[ARM] 5477/1: Freescale STMP platform support [6/10]

Sources: common STMP3xxx platform support

Signed-off-by: dmitry pervushin <dpervushin@embeddedalley.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
diff --git a/arch/arm/plat-stmp3xxx/Kconfig b/arch/arm/plat-stmp3xxx/Kconfig
new file mode 100644
index 0000000..2cf37c3
--- /dev/null
+++ b/arch/arm/plat-stmp3xxx/Kconfig
@@ -0,0 +1,37 @@
+if ARCH_STMP3XXX
+
+menu "Freescale STMP3xxx implementations"
+
+choice
+	prompt "Select STMP3xxx chip family"
+
+config ARCH_STMP37XX
+	bool "Freescale SMTP37xx"
+	select CPU_ARM926T
+	---help---
+	 STMP37xx refers to 3700 through 3769 chips
+
+config ARCH_STMP378X
+	bool "Freescale STMP378x"
+	select CPU_ARM926T
+	---help---
+	 STMP378x refers to 3780 through 3789 chips
+
+endchoice
+
+choice
+	prompt "Select STMP3xxx board type"
+
+config MACH_STMP37XX
+	depends on ARCH_STMP37XX
+	bool "Freescale STMP37xx development board"
+
+config MACH_STMP378X
+	depends on ARCH_STMP378X
+	bool "Freescale STMP378x development board"
+
+endchoice
+
+endmenu
+
+endif
diff --git a/arch/arm/plat-stmp3xxx/Makefile b/arch/arm/plat-stmp3xxx/Makefile
new file mode 100644
index 0000000..b634800
--- /dev/null
+++ b/arch/arm/plat-stmp3xxx/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the linux kernel.
+#
+# Object file lists.
+obj-y += core.o timer.o irq.o dma.o clock.o pinmux.o
diff --git a/arch/arm/plat-stmp3xxx/clock.c b/arch/arm/plat-stmp3xxx/clock.c
new file mode 100644
index 0000000..9a1d46b
--- /dev/null
+++ b/arch/arm/plat-stmp3xxx/clock.c
@@ -0,0 +1,1112 @@
+/*
+ * Clock manipulation routines for Freescale STMP37XX/STMP378X
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/mach-types.h>
+#include <asm/clkdev.h>
+#include <mach/regs-clkctrl.h>
+
+#include "clock.h"
+
+static DEFINE_SPINLOCK(clocks_lock);
+
+static struct clk osc_24M;
+static struct clk pll_clk;
+static struct clk cpu_clk;
+static struct clk hclk;
+
+static int propagate_rate(struct clk *);
+
+static inline int clk_is_busy(struct clk *clk)
+{
+	return __raw_readl(clk->busy_reg) & (1 << clk->busy_bit);
+}
+
+static inline int clk_good(struct clk *clk)
+{
+	return clk && !IS_ERR(clk) && clk->ops;
+}
+
+static int std_clk_enable(struct clk *clk)
+{
+	if (clk->enable_reg) {
+		u32 clk_reg = __raw_readl(clk->enable_reg);
+		if (clk->enable_negate)
+			clk_reg &= ~(1 << clk->enable_shift);
+		else
+			clk_reg |= (1 << clk->enable_shift);
+		__raw_writel(clk_reg, clk->enable_reg);
+		if (clk->enable_wait)
+			udelay(clk->enable_wait);
+		return 0;
+	} else
+		return -EINVAL;
+}
+
+static int std_clk_disable(struct clk *clk)
+{
+	if (clk->enable_reg) {
+		u32 clk_reg = __raw_readl(clk->enable_reg);
+		if (clk->enable_negate)
+			clk_reg |= (1 << clk->enable_shift);
+		else
+			clk_reg &= ~(1 << clk->enable_shift);
+		__raw_writel(clk_reg, clk->enable_reg);
+		return 0;
+	} else
+		return -EINVAL;
+}
+
+static int io_set_rate(struct clk *clk, u32 rate)
+{
+	u32 reg_frac, clkctrl_frac;
+	int i, ret = 0, mask = 0x1f;
+
+	clkctrl_frac = (clk->parent->rate * 18 + rate - 1) / rate;
+
+	if (clkctrl_frac < 18 || clkctrl_frac > 35) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	reg_frac = __raw_readl(clk->scale_reg);
+	reg_frac &= ~(mask << clk->scale_shift);
+	__raw_writel(reg_frac | (clkctrl_frac << clk->scale_shift),
+				clk->scale_reg);
+	if (clk->busy_reg) {
+		for (i = 10000; i; i--)
+			if (!clk_is_busy(clk))
+				break;
+		if (!i)
+			ret = -ETIMEDOUT;
+		else
+			ret = 0;
+	}
+out:
+	return ret;
+}
+
+static long io_get_rate(struct clk *clk)
+{
+	long rate = clk->parent->rate * 18;
+	int mask = 0x1f;
+
+	rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & mask;
+	clk->rate = rate;
+
+	return rate;
+}
+
+static long per_get_rate(struct clk *clk)
+{
+	long rate = clk->parent->rate;
+	long div;
+	const int mask = 0xff;
+
+	if (clk->enable_reg &&
+			!(__raw_readl(clk->enable_reg) & clk->enable_shift))
+		clk->rate = 0;
+	else {
+		div = (__raw_readl(clk->scale_reg) >> clk->scale_shift) & mask;
+		if (div)
+			rate /= div;
+		clk->rate = rate;
+	}
+
+	return clk->rate;
+}
+
+static int per_set_rate(struct clk *clk, u32 rate)
+{
+	int ret = -EINVAL;
+	int div = (clk->parent->rate + rate - 1) / rate;
+	u32 reg_frac;
+	const int mask = 0xff;
+	int try = 10;
+	int i = -1;
+
+	if (div == 0 || div > mask)
+		goto out;
+
+	reg_frac = __raw_readl(clk->scale_reg);
+	reg_frac &= ~(mask << clk->scale_shift);
+
+	while (try--) {
+		__raw_writel(reg_frac | (div << clk->scale_shift),
+				clk->scale_reg);
+
+		if (clk->busy_reg) {
+			for (i = 10000; i; i--)
+				if (!clk_is_busy(clk))
+					break;
+		}
+		if (i)
+			break;
+	}
+
+	if (!i)
+		ret = -ETIMEDOUT;
+	else
+		ret = 0;
+
+out:
+	if (ret != 0)
+		printk(KERN_ERR "%s: error %d\n", __func__, ret);
+	return ret;
+}
+
+static long lcdif_get_rate(struct clk *clk)
+{
+	long rate = clk->parent->rate;
+	long div;
+	const int mask = 0xff;
+
+	div = (__raw_readl(clk->scale_reg) >> clk->scale_shift) & mask;
+	if (div) {
+		rate /= div;
+		div = (HW_CLKCTRL_FRAC_RD() & BM_CLKCTRL_FRAC_PIXFRAC) >>
+				BP_CLKCTRL_FRAC_PIXFRAC;
+		rate /= div;
+	}
+	clk->rate = rate;
+
+	return rate;
+}
+
+static int lcdif_set_rate(struct clk *clk, u32 rate)
+{
+	int ret = 0;
+	/*
+	 * On 3700, we can get most timings exact by modifying ref_pix
+	 * and the divider, but keeping the phase timings at 1 (2
+	 * phases per cycle).
+	 *
+	 * ref_pix can be between 480e6*18/35=246.9MHz and 480e6*18/18=480MHz,
+	 * which is between 18/(18*480e6)=2.084ns and 35/(18*480e6)=4.050ns.
+	 *
+	 * ns_cycle >= 2*18e3/(18*480) = 25/6
+	 * ns_cycle <= 2*35e3/(18*480) = 875/108
+	 *
+	 * Multiply the ns_cycle by 'div' to lengthen it until it fits the
+	 * bounds. This is the divider we'll use after ref_pix.
+	 *
+	 * 6 * ns_cycle >= 25 * div
+	 * 108 * ns_cycle <= 875 * div
+	 */
+	u32 ns_cycle = 1000000 / rate;
+	u32 div, reg_val;
+	u32 lowest_result = (u32) -1;
+	u32 lowest_div = 0, lowest_fracdiv = 0;
+
+	for (div = 1; div < 256; ++div) {
+		u32 fracdiv;
+		u32 ps_result;
+		int lower_bound = 6 * ns_cycle >= 25 * div;
+		int upper_bound = 108 * ns_cycle <= 875 * div;
+		if (!lower_bound)
+			break;
+		if (!upper_bound)
+			continue;
+		/*
+		 * Found a matching div. Calculate fractional divider needed,
+		 * rounded up.
+		 */
+		fracdiv = ((clk->parent->rate / 1000 * 18 / 2) *
+				ns_cycle + 1000 * div - 1) /
+				(1000 * div);
+		if (fracdiv < 18 || fracdiv > 35) {
+			ret = -EINVAL;
+			goto out;
+		}
+		/* Calculate the actual cycle time this results in */
+		ps_result = 6250 * div * fracdiv / 27;
+
+		/* Use the fastest result that doesn't break ns_cycle */
+		if (ps_result <= lowest_result) {
+			lowest_result = ps_result;
+			lowest_div = div;
+			lowest_fracdiv = fracdiv;
+		}
+	}
+
+	if (div >= 256 || lowest_result == (u32) -1) {
+		ret = -EINVAL;
+		goto out;
+	}
+	pr_debug("Programming PFD=%u,DIV=%u ref_pix=%uMHz "
+			"PIXCLK=%uMHz cycle=%u.%03uns\n",
+			lowest_fracdiv, lowest_div,
+			480*18/lowest_fracdiv, 480*18/lowest_fracdiv/lowest_div,
+			lowest_result / 1000, lowest_result % 1000);
+
+	/* Program ref_pix phase fractional divider */
+	HW_CLKCTRL_FRAC_WR((HW_CLKCTRL_FRAC_RD() & ~BM_CLKCTRL_FRAC_PIXFRAC) |
+			   BF_CLKCTRL_FRAC_PIXFRAC(lowest_fracdiv));
+	/* Ungate PFD */
+	HW_CLKCTRL_FRAC_CLR(BM_CLKCTRL_FRAC_CLKGATEPIX);
+
+	/* Program pix divider */
+	reg_val = __raw_readl(clk->scale_reg);
+	reg_val &= ~(BM_CLKCTRL_PIX_DIV | BM_CLKCTRL_PIX_CLKGATE);
+	reg_val |= BF_CLKCTRL_PIX_DIV(lowest_div);
+	__raw_writel(reg_val, clk->scale_reg);
+
+	/* Wait for divider update */
+	if (clk->busy_reg) {
+		int i;
+		for (i = 10000; i; i--)
+			if (!clk_is_busy(clk))
+				break;
+		if (!i) {
+			ret = -ETIMEDOUT;
+			goto out;
+		}
+	}
+
+	/* Switch to ref_pix source */
+	HW_CLKCTRL_CLKSEQ_CLR(BM_CLKCTRL_CLKSEQ_BYPASS_PIX);
+
+out:
+	return ret;
+}
+
+
+static int cpu_set_rate(struct clk *clk, u32 rate)
+{
+	if (rate < 24000)
+		return -EINVAL;
+	else if (rate == 24000) {
+		/* switch to the 24M source */
+		clk_set_parent(clk, &osc_24M);
+	} else {
+		int i;
+		u32 clkctrl_cpu = 1;
+		u32 c = clkctrl_cpu;
+		u32 clkctrl_frac = 1;
+		u32 val;
+		for ( ; c < 0x40; c++) {
+			u32 f = (pll_clk.rate*18/c + rate/2) / rate;
+			int s1, s2;
+
+			if (f < 18 || f > 35)
+				continue;
+			s1 = pll_clk.rate*18/clkctrl_frac/clkctrl_cpu - rate;
+			s2 = pll_clk.rate*18/c/f - rate;
+			pr_debug("%s: s1 %d, s2 %d\n", __func__, s1, s2);
+			if (abs(s1) > abs(s2)) {
+				clkctrl_cpu = c;
+				clkctrl_frac = f;
+			}
+			if (s2 == 0)
+				break;
+		};
+		pr_debug("%s: clkctrl_cpu %d, clkctrl_frac %d\n", __func__,
+				clkctrl_cpu, clkctrl_frac);
+		if (c == 0x40) {
+			int  d = pll_clk.rate*18/clkctrl_frac/clkctrl_cpu -
+				rate;
+			if (abs(d) > 100 ||
+			    clkctrl_frac < 18 || clkctrl_frac > 35)
+				return -EINVAL;
+		}
+
+		/* 4.6.2 */
+		val = __raw_readl(clk->scale_reg);
+		val &= ~(0x3f << clk->scale_shift);
+		val |= clkctrl_frac;
+		clk_set_parent(clk, &osc_24M);
+		udelay(10);
+		__raw_writel(val, clk->scale_reg);
+		/* ungate */
+		__raw_writel(1<<7, clk->scale_reg + 8);
+		/* write clkctrl_cpu */
+		clk->saved_div = clkctrl_cpu;
+		HW_CLKCTRL_CPU_WR((HW_CLKCTRL_CPU_RD() & ~0x3f) | clkctrl_cpu);
+		for (i = 10000; i; i--)
+			if (!clk_is_busy(clk))
+				break;
+		if (!i) {
+			printk(KERN_ERR "couldn't set up CPU divisor\n");
+			return -ETIMEDOUT;
+		}
+		clk_set_parent(clk, &pll_clk);
+		clk->saved_div = 0;
+		udelay(10);
+	}
+	return 0;
+}
+
+static long cpu_get_rate(struct clk *clk)
+{
+	long rate = clk->parent->rate * 18;
+
+	rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & 0x3f;
+	rate /= HW_CLKCTRL_CPU_RD() & 0x3f;
+	rate = ((rate + 9) / 10) * 10;
+	clk->rate = rate;
+
+	return rate;
+}
+
+static long cpu_round_rate(struct clk *clk, u32 rate)
+{
+	unsigned long r = 0;
+
+	if (rate <= 24000)
+		r = 24000;
+	else {
+		u32 clkctrl_cpu = 1;
+		u32 clkctrl_frac;
+		do {
+			clkctrl_frac =
+				(pll_clk.rate*18 / clkctrl_cpu + rate/2) / rate;
+			if (clkctrl_frac > 35)
+				continue;
+			if (pll_clk.rate*18 / clkctrl_frac / clkctrl_cpu/10 ==
+			    rate / 10)
+				break;
+		} while (pll_clk.rate / 2  >= clkctrl_cpu++ * rate);
+		if (pll_clk.rate / 2 < (clkctrl_cpu - 1) * rate)
+			clkctrl_cpu--;
+		pr_debug("%s: clkctrl_cpu %d, clkctrl_frac %d\n", __func__,
+				clkctrl_cpu, clkctrl_frac);
+		if (clkctrl_frac < 18)
+			clkctrl_frac = 18;
+		if (clkctrl_frac > 35)
+			clkctrl_frac = 35;
+
+		r = pll_clk.rate * 18;
+		r /= clkctrl_frac;
+		r /= clkctrl_cpu;
+		r = 10 * ((r + 9) / 10);
+	}
+	return r;
+}
+
+static long emi_get_rate(struct clk *clk)
+{
+	long rate = clk->parent->rate * 18;
+
+	rate /= (__raw_readl(clk->scale_reg) >> clk->scale_shift) & 0x3f;
+	rate /= HW_CLKCTRL_EMI_RD() & 0x3f;
+	clk->rate = rate;
+
+	return rate;
+}
+
+static int clkseq_set_parent(struct clk *clk, struct clk *parent)
+{
+	int ret = -EINVAL;
+	int shift = 8;
+
+	/* bypass? */
+	if (parent == &osc_24M)
+		shift = 4;
+
+	if (clk->bypass_reg) {
+		u32 hbus_mask = BM_CLKCTRL_HBUS_DIV_FRAC_EN |
+				BM_CLKCTRL_HBUS_DIV;
+
+		if (clk == &cpu_clk && shift == 4) {
+			u32 hbus_val = HW_CLKCTRL_HBUS_RD();
+			u32 cpu_val = HW_CLKCTRL_CPU_RD();
+			hbus_val &= ~hbus_mask;
+			hbus_val |= 1;
+			clk->saved_div = cpu_val & BM_CLKCTRL_CPU_DIV_CPU;
+			cpu_val &= ~BM_CLKCTRL_CPU_DIV_CPU;
+			cpu_val |= 1;
+			__raw_writel(1 << clk->bypass_shift,
+					clk->bypass_reg + shift);
+			if (machine_is_stmp378x()) {
+				HW_CLKCTRL_HBUS_WR(hbus_val);
+				HW_CLKCTRL_CPU_WR(cpu_val);
+				hclk.rate = 0;
+			}
+		} else if (clk == &cpu_clk && shift == 8) {
+			u32 hbus_val = HW_CLKCTRL_HBUS_RD();
+			u32 cpu_val = HW_CLKCTRL_CPU_RD();
+			hbus_val &= ~hbus_mask;
+			hbus_val |= 2;
+			cpu_val &= ~BM_CLKCTRL_CPU_DIV_CPU;
+			if (clk->saved_div)
+				cpu_val |= clk->saved_div;
+			else
+				cpu_val |= 2;
+			if (machine_is_stmp378x()) {
+				HW_CLKCTRL_HBUS_WR(hbus_val);
+				HW_CLKCTRL_CPU_WR(cpu_val);
+				hclk.rate = 0;
+			}
+			__raw_writel(1 << clk->bypass_shift,
+					clk->bypass_reg + shift);
+		} else
+			__raw_writel(1 << clk->bypass_shift,
+					clk->bypass_reg + shift);
+
+		ret = 0;
+	}
+
+	return ret;
+}
+
+static int hbus_set_rate(struct clk *clk, u32 rate)
+{
+	u8 div = 0;
+	int is_frac = 0;
+	u32 clkctrl_hbus;
+	struct clk *parent = clk->parent;
+
+	pr_debug("%s: rate %d, parent rate %d\n", __func__, rate,
+			parent->rate);
+
+	if (rate > parent->rate)
+		return -EINVAL;
+
+	if (((parent->rate + rate/2) / rate) * rate != parent->rate &&
+	    parent->rate / rate < 32) {
+		pr_debug("%s: switching to fractional mode\n", __func__);
+		is_frac = 1;
+	}
+
+	if (is_frac)
+		div = (32 * rate + parent->rate / 2) / parent->rate;
+	else
+		div = (parent->rate + rate - 1) / rate;
+	pr_debug("%s: div calculated is %d\n", __func__, div);
+	if (!div || div > 0x1f)
+		return -EINVAL;
+
+	clk_set_parent(&cpu_clk, &osc_24M);
+	udelay(10);
+	clkctrl_hbus = __raw_readl(clk->scale_reg);
+	clkctrl_hbus &= ~0x3f;
+	clkctrl_hbus |= div;
+	clkctrl_hbus |= (is_frac << 5);
+
+	__raw_writel(clkctrl_hbus, clk->scale_reg);
+	if (clk->busy_reg) {
+		int i;
+		for (i = 10000; i; i--)
+			if (!clk_is_busy(clk))
+				break;
+		if (!i) {
+			printk(KERN_ERR "couldn't set up CPU divisor\n");
+			return -ETIMEDOUT;
+		}
+	}
+	clk_set_parent(&cpu_clk, &pll_clk);
+	__raw_writel(clkctrl_hbus, clk->scale_reg);
+	udelay(10);
+	return 0;
+}
+
+static long hbus_get_rate(struct clk *clk)
+{
+	long rate = clk->parent->rate;
+
+	if (__raw_readl(clk->scale_reg) & 0x20) {
+		rate *= __raw_readl(clk->scale_reg) & 0x1f;
+		rate /= 32;
+	} else
+		rate /= __raw_readl(clk->scale_reg) & 0x1f;
+	clk->rate = rate;
+
+	return rate;
+}
+
+static int xbus_set_rate(struct clk *clk, u32 rate)
+{
+	u16 div = 0;
+	u32 clkctrl_xbus;
+
+	pr_debug("%s: rate %d, parent rate %d\n", __func__, rate,
+			clk->parent->rate);
+
+	div = (clk->parent->rate + rate - 1) / rate;
+	pr_debug("%s: div calculated is %d\n", __func__, div);
+	if (!div || div > 0x3ff)
+		return -EINVAL;
+
+	clkctrl_xbus = __raw_readl(clk->scale_reg);
+	clkctrl_xbus &= ~0x3ff;
+	clkctrl_xbus |= div;
+	__raw_writel(clkctrl_xbus, clk->scale_reg);
+	if (clk->busy_reg) {
+		int i;
+		for (i = 10000; i; i--)
+			if (!clk_is_busy(clk))
+				break;
+		if (!i) {
+			printk(KERN_ERR "couldn't set up xbus divisor\n");
+			return -ETIMEDOUT;
+		}
+	}
+	return 0;
+}
+
+static long xbus_get_rate(struct clk *clk)
+{
+	long rate = clk->parent->rate;
+
+	rate /= __raw_readl(clk->scale_reg) & 0x3ff;
+	clk->rate = rate;
+
+	return rate;
+}
+
+
+/* Clock ops */
+
+static struct clk_ops std_ops = {
+	.enable		= std_clk_enable,
+	.disable	= std_clk_disable,
+	.get_rate	= per_get_rate,
+	.set_rate	= per_set_rate,
+	.set_parent	= clkseq_set_parent,
+};
+
+static struct clk_ops min_ops = {
+	.enable		= std_clk_enable,
+	.disable	= std_clk_disable,
+};
+
+static struct clk_ops cpu_ops = {
+	.enable		= std_clk_enable,
+	.disable	= std_clk_disable,
+	.get_rate	= cpu_get_rate,
+	.set_rate	= cpu_set_rate,
+	.round_rate	= cpu_round_rate,
+	.set_parent	= clkseq_set_parent,
+};
+
+static struct clk_ops io_ops = {
+	.enable		= std_clk_enable,
+	.disable	= std_clk_disable,
+	.get_rate	= io_get_rate,
+	.set_rate	= io_set_rate,
+};
+
+static struct clk_ops hbus_ops = {
+	.get_rate	= hbus_get_rate,
+	.set_rate	= hbus_set_rate,
+};
+
+static struct clk_ops xbus_ops = {
+	.get_rate	= xbus_get_rate,
+	.set_rate	= xbus_set_rate,
+};
+
+static struct clk_ops lcdif_ops = {
+	.enable		= std_clk_enable,
+	.disable	= std_clk_disable,
+	.get_rate	= lcdif_get_rate,
+	.set_rate	= lcdif_set_rate,
+	.set_parent	= clkseq_set_parent,
+};
+
+static struct clk_ops emi_ops = {
+	.get_rate	= emi_get_rate,
+};
+
+/* List of on-chip clocks */
+
+static struct clk osc_24M = {
+	.flags		= FIXED_RATE | ENABLED,
+	.rate		= 24000,
+};
+
+static struct clk pll_clk = {
+	.parent		= &osc_24M,
+	.enable_reg	= HW_CLKCTRL_PLLCTRL0_ADDR,
+	.enable_shift	= 16,
+	.enable_wait	= 10,
+	.flags		= FIXED_RATE | ENABLED,
+	.rate		= 480000,
+	.ops		= &min_ops,
+};
+
+static struct clk cpu_clk = {
+	.parent		= &pll_clk,
+	.scale_reg	= HW_CLKCTRL_FRAC_ADDR,
+	.scale_shift	= 0,
+	.bypass_reg	= HW_CLKCTRL_CLKSEQ_ADDR,
+	.bypass_shift	= 7,
+	.busy_reg	= HW_CLKCTRL_CPU_ADDR,
+	.busy_bit	= 28,
+	.flags		= RATE_PROPAGATES | ENABLED,
+	.ops		= &cpu_ops,
+};
+
+static struct clk io_clk = {
+	.parent		= &pll_clk,
+	.enable_reg	= HW_CLKCTRL_FRAC_ADDR,
+	.enable_shift	= 31,
+	.enable_negate	= 1,
+	.scale_reg	= HW_CLKCTRL_FRAC_ADDR,
+	.scale_shift	= 24,
+	.flags		= RATE_PROPAGATES | ENABLED,
+	.ops		= &io_ops,
+};
+
+static struct clk hclk = {
+	.parent		= &cpu_clk,
+	.scale_reg	= HW_CLKCTRL_HBUS_ADDR,
+	.bypass_reg	= HW_CLKCTRL_CLKSEQ_ADDR,
+	.bypass_shift	= 7,
+	.busy_reg	= HW_CLKCTRL_HBUS_ADDR,
+	.busy_bit	= 29,
+	.flags		= RATE_PROPAGATES | ENABLED,
+	.ops		= &hbus_ops,
+};
+
+static struct clk xclk = {
+	.parent		= &osc_24M,
+	.scale_reg	= HW_CLKCTRL_XBUS_ADDR,
+	.busy_reg	= HW_CLKCTRL_XBUS_ADDR,
+	.busy_bit	= 31,
+	.flags		= RATE_PROPAGATES | ENABLED,
+	.ops		= &xbus_ops,
+};
+
+static struct clk uart_clk = {
+	.parent		= &xclk,
+	.enable_reg	= HW_CLKCTRL_XTAL_ADDR,
+	.enable_shift	= 31,
+	.enable_negate	= 1,
+	.flags		= ENABLED,
+	.ops		= &min_ops,
+};
+
+static struct clk audio_clk = {
+	.parent		= &xclk,
+	.enable_reg	= HW_CLKCTRL_XTAL_ADDR,
+	.enable_shift	= 30,
+	.enable_negate	= 1,
+	.ops		= &min_ops,
+};
+
+static struct clk pwm_clk = {
+	.parent		= &xclk,
+	.enable_reg	= HW_CLKCTRL_XTAL_ADDR,
+	.enable_shift	= 29,
+	.enable_negate	= 1,
+	.ops		= &min_ops,
+};
+
+static struct clk dri_clk = {
+	.parent		= &xclk,
+	.enable_reg	= HW_CLKCTRL_XTAL_ADDR,
+	.enable_shift	= 28,
+	.enable_negate	= 1,
+	.ops		= &min_ops,
+};
+
+static struct clk digctl_clk = {
+	.parent		= &xclk,
+	.enable_reg	= HW_CLKCTRL_XTAL_ADDR,
+	.enable_shift	= 27,
+	.enable_negate	= 1,
+	.ops		= &min_ops,
+};
+
+static struct clk timer_clk = {
+	.parent		= &xclk,
+	.enable_reg	= HW_CLKCTRL_XTAL_ADDR,
+	.enable_shift	= 26,
+	.enable_negate	= 1,
+	.flags		= ENABLED,
+	.ops		= &min_ops,
+};
+
+static struct clk lcdif_clk = {
+	.parent		= &pll_clk,
+	.scale_reg	= HW_CLKCTRL_PIX_ADDR,
+	.busy_reg	= HW_CLKCTRL_PIX_ADDR,
+	.busy_bit	= 29,
+	.enable_reg	= HW_CLKCTRL_PIX_ADDR,
+	.enable_shift	= 31,
+	.enable_negate	= 1,
+	.bypass_reg	= HW_CLKCTRL_CLKSEQ_ADDR,
+	.bypass_shift	= 1,
+	.flags		= NEEDS_SET_PARENT,
+	.ops		= &lcdif_ops,
+};
+
+static struct clk ssp_clk = {
+	.parent		= &io_clk,
+	.scale_reg	= HW_CLKCTRL_SSP_ADDR,
+	.busy_reg	= HW_CLKCTRL_SSP_ADDR,
+	.busy_bit	= 29,
+	.enable_reg	= HW_CLKCTRL_SSP_ADDR,
+	.enable_shift	= 31,
+	.bypass_reg	= HW_CLKCTRL_CLKSEQ_ADDR,
+	.bypass_shift	= 5,
+	.enable_negate	= 1,
+	.flags		= NEEDS_SET_PARENT,
+	.ops		= &std_ops,
+};
+
+static struct clk gpmi_clk = {
+	.parent		= &io_clk,
+	.scale_reg	= HW_CLKCTRL_GPMI_ADDR,
+	.busy_reg	= HW_CLKCTRL_GPMI_ADDR,
+	.busy_bit	= 29,
+	.enable_reg	= HW_CLKCTRL_GPMI_ADDR,
+	.enable_shift	= 31,
+	.enable_negate	= 1,
+	.bypass_reg	= HW_CLKCTRL_CLKSEQ_ADDR,
+	.bypass_shift	= 4,
+	.flags		= NEEDS_SET_PARENT,
+	.ops		= &std_ops,
+};
+
+static struct clk spdif_clk = {
+	.parent		= &pll_clk,
+	.enable_reg	= HW_CLKCTRL_SPDIF_ADDR,
+	.enable_shift	= 31,
+	.enable_negate	= 1,
+	.ops		= &min_ops,
+};
+
+static struct clk emi_clk = {
+	.parent		= &pll_clk,
+	.enable_reg	= HW_CLKCTRL_EMI_ADDR,
+	.enable_shift	= 31,
+	.enable_negate	= 1,
+	.scale_reg	= HW_CLKCTRL_FRAC_ADDR,
+	.scale_shift	= 8,
+	.busy_reg	= HW_CLKCTRL_EMI_ADDR,
+	.busy_bit	= 28,
+	.bypass_reg	= HW_CLKCTRL_CLKSEQ_ADDR,
+	.bypass_shift	= 6,
+	.flags		= ENABLED,
+	.ops		= &emi_ops,
+};
+
+static struct clk ir_clk = {
+	.parent		= &io_clk,
+	.enable_reg	= HW_CLKCTRL_IR_ADDR,
+	.enable_shift	= 31,
+	.enable_negate	= 1,
+	.bypass_reg	= HW_CLKCTRL_CLKSEQ_ADDR,
+	.bypass_shift	= 3,
+	.ops		= &min_ops,
+};
+
+static struct clk saif_clk = {
+	.parent		= &pll_clk,
+	.scale_reg	= HW_CLKCTRL_SAIF_ADDR,
+	.busy_reg	= HW_CLKCTRL_SAIF_ADDR,
+	.busy_bit	= 29,
+	.enable_reg	= HW_CLKCTRL_SAIF_ADDR,
+	.enable_shift	= 31,
+	.enable_negate	= 1,
+	.bypass_reg	= HW_CLKCTRL_CLKSEQ_ADDR,
+	.bypass_shift	= 0,
+	.ops		= &std_ops,
+};
+
+static struct clk usb_clk = {
+	.parent		= &pll_clk,
+	.enable_reg	= HW_CLKCTRL_PLLCTRL0_ADDR,
+	.enable_shift	= 18,
+	.enable_negate	= 1,
+	.ops		= &min_ops,
+};
+
+/* list of all the clocks */
+static __initdata struct clk_lookup onchip_clks[] = {
+	{
+		.con_id = "osc_24M",
+		.clk = &osc_24M,
+	}, {
+		.con_id = "pll",
+		.clk = &pll_clk,
+	}, {
+		.con_id = "cpu",
+		.clk = &cpu_clk,
+	}, {
+		.con_id = "hclk",
+		.clk = &hclk,
+	}, {
+		.con_id = "xclk",
+		.clk = &xclk,
+	}, {
+		.con_id = "io",
+		.clk = &io_clk,
+	}, {
+		.con_id = "uart",
+		.clk = &uart_clk,
+	}, {
+		.con_id = "audio",
+		.clk = &audio_clk,
+	}, {
+		.con_id = "pwm",
+		.clk = &pwm_clk,
+	}, {
+		.con_id = "dri",
+		.clk = &dri_clk,
+	}, {
+		.con_id = "digctl",
+		.clk = &digctl_clk,
+	}, {
+		.con_id = "timer",
+		.clk = &timer_clk,
+	}, {
+		.con_id = "lcdif",
+		.clk = &lcdif_clk,
+	}, {
+		.con_id = "ssp",
+		.clk = &ssp_clk,
+	}, {
+		.con_id = "gpmi",
+		.clk = &gpmi_clk,
+	}, {
+		.con_id = "spdif",
+		.clk = &spdif_clk,
+	}, {
+		.con_id = "emi",
+		.clk = &emi_clk,
+	}, {
+		.con_id = "ir",
+		.clk = &ir_clk,
+	}, {
+		.con_id = "saif",
+		.clk = &saif_clk,
+	}, {
+		.con_id = "usb",
+		.clk = &usb_clk,
+	},
+};
+
+static int __init propagate_rate(struct clk *clk)
+{
+	struct clk_lookup *cl;
+
+	for (cl = onchip_clks; cl < onchip_clks + ARRAY_SIZE(onchip_clks);
+	     cl++) {
+		if (unlikely(!clk_good(cl->clk)))
+			continue;
+		if (cl->clk->parent == clk && cl->clk->ops->get_rate) {
+			cl->clk->ops->get_rate(cl->clk);
+			if (cl->clk->flags & RATE_PROPAGATES)
+				propagate_rate(cl->clk);
+		}
+	}
+
+	return 0;
+}
+
+/* Exported API */
+unsigned long clk_get_rate(struct clk *clk)
+{
+	if (unlikely(!clk_good(clk)))
+		return 0;
+
+	if (clk->rate != 0)
+		return clk->rate;
+
+	if (clk->ops->get_rate != NULL)
+		return clk->ops->get_rate(clk);
+
+	return clk_get_rate(clk->parent);
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+	if (unlikely(!clk_good(clk)))
+		return 0;
+
+	if (clk->ops->round_rate)
+		return clk->ops->round_rate(clk, rate);
+
+	return 0;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+static inline int close_enough(long rate1, long rate2)
+{
+	return rate1 && !((rate2 - rate1) * 1000 / rate1);
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	int ret = -EINVAL;
+
+	if (unlikely(!clk_good(clk)))
+		goto out;
+
+	if (clk->flags & FIXED_RATE || !clk->ops->set_rate)
+		goto out;
+
+	else if (!close_enough(clk->rate, rate)) {
+		ret = clk->ops->set_rate(clk, rate);
+		if (ret < 0)
+			goto out;
+		clk->rate = rate;
+		if (clk->flags & RATE_PROPAGATES)
+			propagate_rate(clk);
+	} else
+		ret = 0;
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+int clk_enable(struct clk *clk)
+{
+	unsigned long clocks_flags;
+
+	if (unlikely(!clk_good(clk)))
+		return -EINVAL;
+
+	if (clk->parent)
+		clk_enable(clk->parent);
+
+	spin_lock_irqsave(&clocks_lock, clocks_flags);
+
+	clk->usage++;
+	if (clk->ops && clk->ops->enable)
+		clk->ops->enable(clk);
+
+	spin_unlock_irqrestore(&clocks_lock, clocks_flags);
+	return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+static void local_clk_disable(struct clk *clk)
+{
+	if (unlikely(!clk_good(clk)))
+		return;
+
+	if (clk->usage == 0 && clk->ops->disable)
+		clk->ops->disable(clk);
+
+	if (clk->parent)
+		local_clk_disable(clk->parent);
+}
+
+void clk_disable(struct clk *clk)
+{
+	unsigned long clocks_flags;
+
+	if (unlikely(!clk_good(clk)))
+		return;
+
+	spin_lock_irqsave(&clocks_lock, clocks_flags);
+
+	if ((--clk->usage) == 0 && clk->ops->disable)
+		clk->ops->disable(clk);
+
+	spin_unlock_irqrestore(&clocks_lock, clocks_flags);
+	if (clk->parent)
+		clk_disable(clk->parent);
+}
+EXPORT_SYMBOL(clk_disable);
+
+/* Some additional API */
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+	int ret = -ENODEV;
+	unsigned long clocks_flags;
+
+	if (unlikely(!clk_good(clk)))
+		goto out;
+
+	if (!clk->ops->set_parent)
+		goto out;
+
+	spin_lock_irqsave(&clocks_lock, clocks_flags);
+
+	ret = clk->ops->set_parent(clk, parent);
+	if (!ret) {
+		/* disable if usage count is 0 */
+		local_clk_disable(parent);
+
+		parent->usage += clk->usage;
+		clk->parent->usage -= clk->usage;
+
+		/* disable if new usage count is 0 */
+		local_clk_disable(clk->parent);
+
+		clk->parent = parent;
+	}
+	spin_unlock_irqrestore(&clocks_lock, clocks_flags);
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+	if (unlikely(!clk_good(clk)))
+		return NULL;
+	return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+static int __init clk_init(void)
+{
+	struct clk_lookup *cl;
+	struct clk_ops *ops;
+
+	spin_lock_init(&clocks_lock);
+
+	for (cl = onchip_clks; cl < onchip_clks + ARRAY_SIZE(onchip_clks);
+	     cl++) {
+		if (cl->clk->flags & ENABLED)
+			clk_enable(cl->clk);
+		else
+			local_clk_disable(cl->clk);
+
+		ops = cl->clk->ops;
+
+		if ((cl->clk->flags & NEEDS_INITIALIZATION) &&
+				ops && ops->set_rate)
+			ops->set_rate(cl->clk, cl->clk->rate);
+
+		if (cl->clk->flags & FIXED_RATE) {
+			if (cl->clk->flags & RATE_PROPAGATES)
+				propagate_rate(cl->clk);
+		} else {
+			if (ops && ops->get_rate)
+				ops->get_rate(cl->clk);
+		}
+
+		if (cl->clk->flags & NEEDS_SET_PARENT) {
+			if (ops && ops->set_parent)
+				ops->set_parent(cl->clk, cl->clk->parent);
+		}
+
+		clkdev_add(cl);
+	}
+	return 0;
+}
+
+arch_initcall(clk_init);
diff --git a/arch/arm/plat-stmp3xxx/clock.h b/arch/arm/plat-stmp3xxx/clock.h
new file mode 100644
index 0000000..a6611e1
--- /dev/null
+++ b/arch/arm/plat-stmp3xxx/clock.h
@@ -0,0 +1,61 @@
+/*
+ * Clock control driver for Freescale STMP37XX/STMP378X - internal header file
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __ARCH_ARM_STMX3XXX_CLOCK_H__
+#define __ARCH_ARM_STMX3XXX_CLOCK_H__
+
+#ifndef __ASSEMBLER__
+
+struct clk_ops {
+	int (*enable) (struct clk *);
+	int (*disable) (struct clk *);
+	long (*get_rate) (struct clk *);
+	long (*round_rate) (struct clk *, u32);
+	int (*set_rate) (struct clk *, u32);
+	int (*set_parent) (struct clk *, struct clk *);
+};
+
+struct clk {
+	struct clk *parent;
+	u32 rate;
+	u32 flags;
+	u8 scale_shift;
+	u8 enable_shift;
+	u8 bypass_shift;
+	u8 busy_bit;
+	s8 usage;
+	int enable_wait;
+	int enable_negate;
+	u32 saved_div;
+	void __iomem *enable_reg;
+	void __iomem *scale_reg;
+	void __iomem *bypass_reg;
+	void __iomem *busy_reg;
+	struct clk_ops *ops;
+};
+
+#endif /* __ASSEMBLER__ */
+
+/* Flags */
+#define RATE_PROPAGATES      (1<<0)
+#define NEEDS_INITIALIZATION (1<<1)
+#define PARENT_SET_RATE      (1<<2)
+#define FIXED_RATE           (1<<3)
+#define ENABLED	             (1<<4)
+#define NEEDS_SET_PARENT     (1<<5)
+
+#endif
diff --git a/arch/arm/plat-stmp3xxx/core.c b/arch/arm/plat-stmp3xxx/core.c
new file mode 100644
index 0000000..6e2fef16
--- /dev/null
+++ b/arch/arm/plat-stmp3xxx/core.c
@@ -0,0 +1,127 @@
+/*
+ * Freescale STMP37XX/STMP378X core routines
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <mach/stmp3xxx.h>
+#include <mach/dma.h>
+#include <mach/regs-clkctrl.h>
+
+static int __stmp3xxx_reset_block(void __iomem *hwreg, int just_enable)
+{
+	u32 c;
+	int timeout;
+
+	/* the process of software reset of IP block is done
+	   in several steps:
+
+	   - clear SFTRST and wait for block is enabled;
+	   - clear clock gating (CLKGATE bit);
+	   - set the SFTRST again and wait for block is in reset;
+	   - clear SFTRST and wait for reset completion.
+	*/
+	c = __raw_readl(hwreg);
+	c &= ~(1<<31);		/* clear SFTRST */
+	__raw_writel(c, hwreg);
+	for (timeout = 1000000; timeout > 0; timeout--)
+		/* still in SFTRST state ? */
+		if ((__raw_readl(hwreg) & (1<<31)) == 0)
+			break;
+	if (timeout <= 0) {
+		printk(KERN_ERR"%s(%p): timeout when enabling\n",
+				__func__, hwreg);
+		return -ETIME;
+	}
+
+	c = __raw_readl(hwreg);
+	c &= ~(1<<30);		/* clear CLKGATE */
+	__raw_writel(c, hwreg);
+
+	if (!just_enable) {
+		c = __raw_readl(hwreg);
+		c |= (1<<31);		/* now again set SFTRST */
+		__raw_writel(c, hwreg);
+		for (timeout = 1000000; timeout > 0; timeout--)
+			/* poll until CLKGATE set */
+			if (__raw_readl(hwreg) & (1<<30))
+				break;
+		if (timeout <= 0) {
+			printk(KERN_ERR"%s(%p): timeout when resetting\n",
+					__func__, hwreg);
+			return -ETIME;
+		}
+
+		c = __raw_readl(hwreg);
+		c &= ~(1<<31);		/* clear SFTRST */
+		__raw_writel(c, hwreg);
+		for (timeout = 1000000; timeout > 0; timeout--)
+			/* still in SFTRST state ? */
+			if ((__raw_readl(hwreg) & (1<<31)) == 0)
+				break;
+		if (timeout <= 0) {
+			printk(KERN_ERR"%s(%p): timeout when enabling "
+					"after reset\n", __func__, hwreg);
+			return -ETIME;
+		}
+
+		c = __raw_readl(hwreg);
+		c &= ~(1<<30);		/* clear CLKGATE */
+		__raw_writel(c, hwreg);
+	}
+	for (timeout = 1000000; timeout > 0; timeout--)
+		/* still in SFTRST state ? */
+		if ((__raw_readl(hwreg) & (1<<30)) == 0)
+			break;
+
+	if (timeout <= 0) {
+		printk(KERN_ERR"%s(%p): timeout when unclockgating\n",
+				__func__, hwreg);
+		return -ETIME;
+	}
+
+	return 0;
+}
+
+int stmp3xxx_reset_block(void __iomem *hwreg, int just_enable)
+{
+	int try = 10;
+	int r;
+
+	while (try--) {
+		r = __stmp3xxx_reset_block(hwreg, just_enable);
+		if (!r)
+			break;
+		pr_debug("%s: try %d failed\n", __func__, 10 - try);
+	}
+	return r;
+}
+EXPORT_SYMBOL(stmp3xxx_reset_block);
+
+struct platform_device stmp3xxx_dbguart = {
+	.name = "stmp3xxx-dbguart",
+	.id = -1,
+};
+
+void __init stmp3xxx_init(void)
+{
+	/* Turn off auto-slow and other tricks */
+	HW_CLKCTRL_HBUS_CLR(0x07f00000U);
+
+	stmp3xxx_dma_init();
+}
diff --git a/arch/arm/plat-stmp3xxx/dma.c b/arch/arm/plat-stmp3xxx/dma.c
new file mode 100644
index 0000000..cf42de0
--- /dev/null
+++ b/arch/arm/plat-stmp3xxx/dma.c
@@ -0,0 +1,462 @@
+/*
+ * DMA helper routines for Freescale STMP37XX/STMP378X
+ *
+ * Author: dmitry pervushin <dpervushin@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/sysdev.h>
+#include <linux/cpufreq.h>
+
+#include <asm/page.h>
+
+#include <mach/dma.h>
+#include <mach/regs-apbx.h>
+#include <mach/regs-apbh.h>
+
+static const size_t pool_item_size = sizeof(struct stmp3xxx_dma_command);
+static const size_t pool_alignment = 8;
+static struct stmp3xxx_dma_user {
+	void *pool;
+	int inuse;
+	const char *name;
+} channels[MAX_DMA_CHANNELS];
+
+static inline int dmach(int ch)
+{
+	return ch % 16;
+}
+
+static inline int dmabus(int ch)
+{
+	return ch / 16;
+}
+
+#define IS_VALID_CHANNEL(ch) ((ch) >= 0 && (ch) < MAX_DMA_CHANNELS)
+#define IS_USED(ch) (channels[ch].inuse)
+
+int stmp3xxx_dma_request(int ch, struct device *dev, const char *name)
+{
+	struct stmp3xxx_dma_user *user;
+	int err = 0;
+
+	user = channels + ch;
+	if (!IS_VALID_CHANNEL(ch)) {
+		err = -ENODEV;
+		goto out;
+	}
+	if (IS_USED(ch)) {
+		err = -EBUSY;
+		goto out;
+	}
+	/* Create a pool to allocate dma commands from */
+	user->pool = dma_pool_create(name, dev, pool_item_size,
+				     pool_alignment, PAGE_SIZE);
+	if (user->pool == NULL) {
+		err = -ENOMEM;
+		goto out;
+	}
+	user->name = name;
+	user->inuse++;
+out:
+	return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_request);
+
+int stmp3xxx_dma_release(int ch)
+{
+	struct stmp3xxx_dma_user *user = channels + ch;
+	int err = 0;
+
+	if (!IS_VALID_CHANNEL(ch)) {
+		err = -ENODEV;
+		goto out;
+	}
+	if (!IS_USED(ch)) {
+		err = -EBUSY;
+		goto out;
+	}
+	BUG_ON(user->pool == NULL);
+	dma_pool_destroy(user->pool);
+	user->inuse--;
+out:
+	return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_release);
+
+int stmp3xxx_dma_read_semaphore(int channel)
+{
+	int sem = -1;
+
+	switch (dmabus(channel)) {
+	case STMP3XXX_BUS_APBH:
+		sem =
+		    (HW_APBH_CHn_SEMA_RD(dmach(channel)) &
+		     BM_APBH_CHn_SEMA_PHORE) >> BP_APBH_CHn_SEMA_PHORE;
+		break;
+
+	case STMP3XXX_BUS_APBX:
+		sem =
+		    (HW_APBX_CHn_SEMA_RD(dmach(channel)) &
+		     BM_APBX_CHn_SEMA_PHORE) >> BP_APBX_CHn_SEMA_PHORE;
+		break;
+	default:
+		BUG();
+	}
+	return sem;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_read_semaphore);
+
+int stmp3xxx_dma_allocate_command(int channel,
+				  struct stmp3xxx_dma_descriptor *descriptor)
+{
+	struct stmp3xxx_dma_user *user = channels + channel;
+	int err = 0;
+
+	if (!IS_VALID_CHANNEL(channel)) {
+		err = -ENODEV;
+		goto out;
+	}
+	if (!IS_USED(channel)) {
+		err = -EBUSY;
+		goto out;
+	}
+	if (descriptor == NULL) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	/* Allocate memory for a command from the buffer */
+	descriptor->command =
+	    dma_pool_alloc(user->pool, GFP_KERNEL, &descriptor->handle);
+
+	/* Check it worked */
+	if (!descriptor->command) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	memset(descriptor->command, 0, pool_item_size);
+out:
+	WARN_ON(err);
+	return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_allocate_command);
+
+int stmp3xxx_dma_free_command(int channel,
+			      struct stmp3xxx_dma_descriptor *descriptor)
+{
+	int err = 0;
+
+	if (!IS_VALID_CHANNEL(channel)) {
+		err = -ENODEV;
+		goto out;
+	}
+	if (!IS_USED(channel)) {
+		err = -EBUSY;
+		goto out;
+	}
+
+	/* Return the command memory to the pool */
+	dma_pool_free(channels[channel].pool, descriptor->command,
+		      descriptor->handle);
+
+	/* Initialise descriptor so we're not tempted to use it */
+	descriptor->command = NULL;
+	descriptor->handle = 0;
+	descriptor->virtual_buf_ptr = NULL;
+	descriptor->next_descr = NULL;
+
+	WARN_ON(err);
+out:
+	return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_free_command);
+
+void stmp3xxx_dma_go(int channel,
+		     struct stmp3xxx_dma_descriptor *head, u32 semaphore)
+{
+	int ch = dmach(channel);
+
+	switch (dmabus(channel)) {
+	case STMP3XXX_BUS_APBH:
+		/* Set next command */
+		HW_APBH_CHn_NXTCMDAR_WR(ch, head->handle);
+		/* Set counting semaphore (kicks off transfer). Assumes
+		   peripheral has been set up correctly */
+		HW_APBH_CHn_SEMA_WR(ch, semaphore);
+		break;
+
+	case STMP3XXX_BUS_APBX:
+		/* Set next command */
+		HW_APBX_CHn_NXTCMDAR_WR(ch, head->handle);
+		/* Set counting semaphore (kicks off transfer). Assumes
+		   peripheral has been set up correctly */
+		HW_APBX_CHn_SEMA_WR(ch, semaphore);
+		break;
+	}
+}
+EXPORT_SYMBOL(stmp3xxx_dma_go);
+
+int stmp3xxx_dma_running(int channel)
+{
+	switch (dmabus(channel)) {
+	case STMP3XXX_BUS_APBH:
+		return HW_APBH_CHn_SEMA_RD(dmach(channel)) &
+		    BM_APBH_CHn_SEMA_PHORE;
+
+	case STMP3XXX_BUS_APBX:
+		return HW_APBX_CHn_SEMA_RD(dmach(channel)) &
+		    BM_APBX_CHn_SEMA_PHORE;
+
+	default:
+		BUG();
+		return 0;
+	}
+}
+EXPORT_SYMBOL(stmp3xxx_dma_running);
+
+/*
+ * Circular dma chain management
+ */
+void stmp3xxx_dma_free_chain(struct stmp37xx_circ_dma_chain *chain)
+{
+	int i;
+
+	for (i = 0; i < chain->total_count; i++)
+		stmp3xxx_dma_free_command(
+			STMP3xxx_DMA(chain->channel, chain->bus),
+			&chain->chain[i]);
+}
+EXPORT_SYMBOL(stmp3xxx_dma_free_chain);
+
+int stmp3xxx_dma_make_chain(int ch, struct stmp37xx_circ_dma_chain *chain,
+			    struct stmp3xxx_dma_descriptor descriptors[],
+			    unsigned items)
+{
+	int i;
+	int err = 0;
+
+	if (items == 0)
+		return err;
+
+	for (i = 0; i < items; i++) {
+		err = stmp3xxx_dma_allocate_command(ch, &descriptors[i]);
+		if (err) {
+			WARN_ON(err);
+			/*
+			 * Couldn't allocate the whole chain.
+			 * deallocate what has been allocated
+			 */
+			if (i) {
+				do {
+					stmp3xxx_dma_free_command(ch,
+								  &descriptors
+								  [i]);
+				} while (i-- >= 0);
+			}
+			return err;
+		}
+
+		/* link them! */
+		if (i > 0) {
+			descriptors[i - 1].next_descr = &descriptors[i];
+			descriptors[i - 1].command->next =
+						descriptors[i].handle;
+		}
+	}
+
+	/* make list circular */
+	descriptors[items - 1].next_descr = &descriptors[0];
+	descriptors[items - 1].command->next = descriptors[0].handle;
+
+	chain->total_count = items;
+	chain->chain = descriptors;
+	chain->free_index = 0;
+	chain->active_index = 0;
+	chain->cooked_index = 0;
+	chain->free_count = items;
+	chain->active_count = 0;
+	chain->cooked_count = 0;
+	chain->bus = dmabus(ch);
+	chain->channel = dmach(ch);
+	return err;
+}
+EXPORT_SYMBOL(stmp3xxx_dma_make_chain);
+
+void stmp37xx_circ_clear_chain(struct stmp37xx_circ_dma_chain *chain)
+{
+	BUG_ON(stmp3xxx_dma_running(STMP3xxx_DMA(chain->channel, chain->bus)) >
+	       0);
+	chain->free_index = 0;
+	chain->active_index = 0;
+	chain->cooked_index = 0;
+	chain->free_count = chain->total_count;
+	chain->active_count = 0;
+	chain->cooked_count = 0;
+}
+EXPORT_SYMBOL(stmp37xx_circ_clear_chain);
+
+void stmp37xx_circ_advance_free(struct stmp37xx_circ_dma_chain *chain,
+		unsigned count)
+{
+	BUG_ON(chain->cooked_count < count);
+
+	chain->cooked_count -= count;
+	chain->cooked_index += count;
+	chain->cooked_index %= chain->total_count;
+	chain->free_count += count;
+}
+EXPORT_SYMBOL(stmp37xx_circ_advance_free);
+
+void stmp37xx_circ_advance_active(struct stmp37xx_circ_dma_chain *chain,
+		unsigned count)
+{
+	BUG_ON(chain->free_count < count);
+
+	chain->free_count -= count;
+	chain->free_index += count;
+	chain->free_index %= chain->total_count;
+	chain->active_count += count;
+
+	switch (chain->bus) {
+	case STMP3XXX_BUS_APBH:
+		/* Set counting semaphore (kicks off transfer). Assumes
+		   peripheral has been set up correctly */
+		HW_APBH_CHn_SEMA_CLR(chain->channel,
+				     BM_APBH_CHn_SEMA_INCREMENT_SEMA);
+		HW_APBH_CHn_SEMA_SET(chain->channel,
+				     BF_APBH_CHn_SEMA_INCREMENT_SEMA(count));
+		break;
+
+	case STMP3XXX_BUS_APBX:
+		/* Set counting semaphore (kicks off transfer). Assumes
+		   peripheral has been set up correctly */
+		HW_APBX_CHn_SEMA_CLR(chain->channel,
+				     BM_APBX_CHn_SEMA_INCREMENT_SEMA);
+		HW_APBX_CHn_SEMA_SET(chain->channel,
+				     BF_APBX_CHn_SEMA_INCREMENT_SEMA(count));
+		break;
+
+	default:
+		BUG();
+	}
+}
+EXPORT_SYMBOL(stmp37xx_circ_advance_active);
+
+unsigned stmp37xx_circ_advance_cooked(struct stmp37xx_circ_dma_chain *chain)
+{
+	unsigned cooked;
+
+	cooked = chain->active_count -
+	  stmp3xxx_dma_read_semaphore(STMP3xxx_DMA(chain->channel, chain->bus));
+
+	chain->active_count -= cooked;
+	chain->active_index += cooked;
+	chain->active_index %= chain->total_count;
+
+	chain->cooked_count += cooked;
+
+	return cooked;
+}
+EXPORT_SYMBOL(stmp37xx_circ_advance_cooked);
+
+void stmp3xxx_dma_set_alt_target(int channel, int function)
+{
+#if defined(CONFIG_ARCH_STMP37XX)
+	unsigned bits = 4;
+#elif defined(CONFIG_ARCH_STMP378X)
+	unsigned bits = 2;
+#else
+#error wrong arch
+#endif
+	int shift = dmach(channel) * bits;
+	unsigned mask = (1<<bits) - 1;
+
+	BUG_ON(function < 0 || function >= (1<<bits));
+	pr_debug("%s: channel = %d, using mask %x, "
+		 "shift = %d\n", __func__, channel, mask, shift);
+
+	switch (dmabus(channel)) {
+	case STMP3XXX_BUS_APBH:
+		HW_APBH_DEVSEL_CLR(mask<<shift);
+		HW_APBH_DEVSEL_SET(function<<shift);
+		break;
+	case STMP3XXX_BUS_APBX:
+		HW_APBX_DEVSEL_CLR(mask<<shift);
+		HW_APBX_DEVSEL_SET(function<<shift);
+		break;
+	default:
+		BUG();
+	}
+}
+EXPORT_SYMBOL(stmp3xxx_dma_set_alt_target);
+
+void stmp3xxx_dma_suspend(void)
+{
+	HW_APBH_CTRL0_SET(BM_APBH_CTRL0_CLKGATE);
+	HW_APBX_CTRL0_SET(BM_APBX_CTRL0_CLKGATE);
+}
+
+void stmp3xxx_dma_resume(void)
+{
+	HW_APBH_CTRL0_CLR(BM_APBH_CTRL0_CLKGATE | BM_APBH_CTRL0_SFTRST);
+	HW_APBX_CTRL0_CLR(BM_APBX_CTRL0_CLKGATE | BM_APBX_CTRL0_SFTRST);
+}
+
+#ifdef CONFIG_CPU_FREQ
+
+struct dma_notifier_block {
+	struct notifier_block nb;
+	void *data;
+};
+
+static int dma_cpufreq_notifier(struct notifier_block *self,
+				unsigned long phase, void *p)
+{
+	switch (phase) {
+	case CPUFREQ_POSTCHANGE:
+		stmp3xxx_dma_resume();
+		break;
+
+	case CPUFREQ_PRECHANGE:
+		stmp3xxx_dma_suspend();
+		break;
+
+	default:
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct dma_notifier_block dma_cpufreq_nb = {
+	.nb = {
+		.notifier_call = dma_cpufreq_notifier,
+	},
+};
+#endif /* CONFIG_CPU_FREQ */
+
+void __init stmp3xxx_dma_init(void)
+{
+	HW_APBH_CTRL0_CLR(BM_APBH_CTRL0_CLKGATE | BM_APBH_CTRL0_SFTRST);
+	HW_APBX_CTRL0_CLR(BM_APBX_CTRL0_CLKGATE | BM_APBX_CTRL0_SFTRST);
+#ifdef CONFIG_CPU_FREQ
+	cpufreq_register_notifier(&dma_cpufreq_nb.nb,
+				CPUFREQ_TRANSITION_NOTIFIER);
+#endif /* CONFIG_CPU_FREQ */
+
+}
diff --git a/arch/arm/plat-stmp3xxx/irq.c b/arch/arm/plat-stmp3xxx/irq.c
new file mode 100644
index 0000000..cb36590
--- /dev/null
+++ b/arch/arm/plat-stmp3xxx/irq.c
@@ -0,0 +1,59 @@
+/*
+ * Freescale STMP37XX/STMP378X common interrupt handling code
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/sysdev.h>
+
+#include <mach/stmp3xxx.h>
+#include <mach/regs-icoll.h>
+
+void __init stmp3xxx_init_irq(struct irq_chip *chip)
+{
+	unsigned int i;
+
+	/* Reset the interrupt controller */
+	HW_ICOLL_CTRL_CLR(BM_ICOLL_CTRL_CLKGATE);
+	udelay(10);
+	HW_ICOLL_CTRL_CLR(BM_ICOLL_CTRL_SFTRST);
+	udelay(10);
+	HW_ICOLL_CTRL_SET(BM_ICOLL_CTRL_SFTRST);
+	while (!(HW_ICOLL_CTRL_RD() & BM_ICOLL_CTRL_CLKGATE))
+		continue;
+	HW_ICOLL_CTRL_CLR(BM_ICOLL_CTRL_SFTRST | BM_ICOLL_CTRL_CLKGATE);
+
+	/* Disable all interrupts initially */
+	for (i = 0; i < NR_REAL_IRQS; i++) {
+		chip->mask(i);
+		set_irq_chip(i, chip);
+		set_irq_handler(i, handle_level_irq);
+		set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
+	}
+
+	/* Ensure vector is cleared */
+	HW_ICOLL_LEVELACK_WR(1);
+	HW_ICOLL_LEVELACK_WR(2);
+	HW_ICOLL_LEVELACK_WR(4);
+	HW_ICOLL_LEVELACK_WR(8);
+
+	HW_ICOLL_VECTOR_WR(0);
+	/* Barrier */
+	(void) HW_ICOLL_STAT_RD();
+}
+
diff --git a/arch/arm/plat-stmp3xxx/pinmux.c b/arch/arm/plat-stmp3xxx/pinmux.c
new file mode 100644
index 0000000..9b28cc8
--- /dev/null
+++ b/arch/arm/plat-stmp3xxx/pinmux.c
@@ -0,0 +1,545 @@
+/*
+ * Freescale STMP378X/STMP378X Pin Multiplexing
+ *
+ * Author: Vladislav Buzov <vbuzov@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/sysdev.h>
+#include <linux/string.h>
+#include <linux/bitops.h>
+#include <linux/sysdev.h>
+#include <linux/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-pinctrl.h>
+#include <mach/pins.h>
+#include <mach/pinmux.h>
+
+#define NR_BANKS ARRAY_SIZE(pinmux_banks)
+static struct stmp3xxx_pinmux_bank pinmux_banks[] = {
+	[0] = {
+		.hw_muxsel = {
+			HW_PINCTRL_MUXSEL0_ADDR,
+			HW_PINCTRL_MUXSEL1_ADDR
+		},
+		.hw_drive = {
+			HW_PINCTRL_DRIVE0_ADDR,
+			HW_PINCTRL_DRIVE1_ADDR,
+			HW_PINCTRL_DRIVE2_ADDR,
+			HW_PINCTRL_DRIVE3_ADDR
+		},
+		.hw_pull = HW_PINCTRL_PULL0_ADDR,
+		.functions = { 0x0, 0x1, 0x2, 0x3 },
+		.strengths = { 0x0, 0x1, 0x2, 0x3, 0xff },
+
+		.hw_gpio_read = HW_PINCTRL_DIN0_ADDR,
+		.hw_gpio_set = HW_PINCTRL_DOUT0_ADDR + HW_STMP3xxx_SET,
+		.hw_gpio_clr = HW_PINCTRL_DOUT0_ADDR + HW_STMP3xxx_CLR,
+		.hw_gpio_doe = HW_PINCTRL_DOE0_ADDR,
+		.irq = IRQ_GPIO0,
+
+		.pin2irq = HW_PINCTRL_PIN2IRQ0_ADDR,
+		.irqstat = HW_PINCTRL_IRQSTAT0_ADDR,
+		.irqlevel = HW_PINCTRL_IRQLEVEL0_ADDR,
+		.irqpolarity = HW_PINCTRL_IRQPOL0_ADDR,
+		.irqen = HW_PINCTRL_IRQEN0_ADDR,
+	},
+	[1] = {
+		.hw_muxsel = {
+			HW_PINCTRL_MUXSEL2_ADDR,
+			HW_PINCTRL_MUXSEL3_ADDR
+		},
+		.hw_drive = {
+			HW_PINCTRL_DRIVE4_ADDR,
+			HW_PINCTRL_DRIVE5_ADDR,
+			HW_PINCTRL_DRIVE6_ADDR,
+			HW_PINCTRL_DRIVE7_ADDR
+		},
+		.hw_pull = HW_PINCTRL_PULL1_ADDR,
+		.functions = { 0x0, 0x1, 0x2, 0x3 },
+		.strengths = { 0x0, 0x1, 0x2, 0x3, 0xff },
+
+		.hw_gpio_read = HW_PINCTRL_DIN1_ADDR,
+		.hw_gpio_set = HW_PINCTRL_DOUT1_ADDR + HW_STMP3xxx_SET,
+		.hw_gpio_clr = HW_PINCTRL_DOUT1_ADDR + HW_STMP3xxx_CLR,
+		.hw_gpio_doe = HW_PINCTRL_DOE1_ADDR,
+		.irq = IRQ_GPIO1,
+
+		.pin2irq = HW_PINCTRL_PIN2IRQ1_ADDR,
+		.irqstat = HW_PINCTRL_IRQSTAT1_ADDR,
+		.irqlevel = HW_PINCTRL_IRQLEVEL1_ADDR,
+		.irqpolarity = HW_PINCTRL_IRQPOL1_ADDR,
+		.irqen = HW_PINCTRL_IRQEN1_ADDR,
+	},
+	[2] = {
+	       .hw_muxsel = {
+			HW_PINCTRL_MUXSEL4_ADDR,
+			HW_PINCTRL_MUXSEL5_ADDR,
+		},
+		.hw_drive = {
+			HW_PINCTRL_DRIVE8_ADDR,
+			HW_PINCTRL_DRIVE9_ADDR,
+			HW_PINCTRL_DRIVE10_ADDR,
+			HW_PINCTRL_DRIVE11_ADDR,
+		},
+		.hw_pull = HW_PINCTRL_PULL2_ADDR,
+		.functions = { 0x0, 0x1, 0x2, 0x3 },
+		.strengths = { 0x0, 0x1, 0x2, 0x1, 0x2 },
+
+		.hw_gpio_read = HW_PINCTRL_DIN2_ADDR,
+		.hw_gpio_set = HW_PINCTRL_DOUT2_ADDR + HW_STMP3xxx_SET,
+		.hw_gpio_clr = HW_PINCTRL_DOUT2_ADDR + HW_STMP3xxx_CLR,
+		.hw_gpio_doe = HW_PINCTRL_DOE2_ADDR,
+		.irq = IRQ_GPIO2,
+
+		.pin2irq = HW_PINCTRL_PIN2IRQ2_ADDR,
+		.irqstat = HW_PINCTRL_IRQSTAT2_ADDR,
+		.irqlevel = HW_PINCTRL_IRQLEVEL2_ADDR,
+		.irqpolarity = HW_PINCTRL_IRQPOL2_ADDR,
+		.irqen = HW_PINCTRL_IRQEN2_ADDR,
+	},
+	[3] = {
+	       .hw_muxsel = {
+		       HW_PINCTRL_MUXSEL6_ADDR,
+		       HW_PINCTRL_MUXSEL7_ADDR,
+	       },
+	       .hw_drive = {
+			HW_PINCTRL_DRIVE12_ADDR,
+			HW_PINCTRL_DRIVE13_ADDR,
+			HW_PINCTRL_DRIVE14_ADDR,
+			NULL,
+	       },
+	       .hw_pull = HW_PINCTRL_PULL3_ADDR,
+	       .functions = {0x0, 0x1, 0x2, 0x3},
+	       .strengths = {0x0, 0x1, 0x2, 0x3, 0xff},
+	},
+};
+
+static inline struct stmp3xxx_pinmux_bank *
+stmp3xxx_pinmux_bank(unsigned id, unsigned *bank, unsigned *pin)
+{
+	unsigned b, p;
+
+	b = STMP3XXX_PINID_TO_BANK(id);
+	p = STMP3XXX_PINID_TO_PINNUM(id);
+	BUG_ON(b >= NR_BANKS);
+	if (bank)
+		*bank = b;
+	if (pin)
+		*pin = p;
+	return &pinmux_banks[b];
+}
+
+/* Check if requested pin is owned by caller */
+static int stmp3xxx_check_pin(unsigned id, const char *label)
+{
+	unsigned pin;
+	struct stmp3xxx_pinmux_bank *pm = stmp3xxx_pinmux_bank(id, NULL, &pin);
+
+	if (!test_bit(pin, &pm->pin_map)) {
+		printk(KERN_WARNING
+		       "%s: Accessing free pin %x, caller %s\n",
+		       __func__, id, label);
+
+		return -EINVAL;
+	}
+
+	if (label && pm->pin_labels[pin] &&
+	    strcmp(label, pm->pin_labels[pin])) {
+		printk(KERN_WARNING
+		       "%s: Wrong pin owner %x, caller %s owner %s\n",
+		       __func__, id, label, pm->pin_labels[pin]);
+
+		return -EINVAL;
+	}
+	return 0;
+}
+
+void stmp3xxx_pin_strength(unsigned id, enum pin_strength strength,
+		const char *label)
+{
+	struct stmp3xxx_pinmux_bank *pbank;
+	void __iomem *hwdrive;
+	u32 shift, val;
+	u32 bank, pin;
+
+	pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
+	pr_debug("%s: label %s bank %d pin %d strength %d\n", __func__, label,
+		 bank, pin, strength);
+
+	hwdrive = pbank->hw_drive[pin / HW_DRIVE_PIN_NUM];
+	shift = (pin % HW_DRIVE_PIN_NUM) * HW_DRIVE_PIN_LEN;
+	val = pbank->strengths[strength];
+	if (val == 0xff) {
+		printk(KERN_WARNING
+		       "%s: strength is not supported for bank %d, caller %s",
+		       __func__, bank, label);
+		return;
+	}
+
+	if (stmp3xxx_check_pin(id, label))
+		return;
+
+	pr_debug("%s: writing 0x%x to 0x%p register\n", __func__,
+			val << shift, hwdrive);
+	__raw_writel(HW_DRIVE_PINDRV_MASK << shift, hwdrive + HW_STMP3xxx_CLR);
+	__raw_writel(val << shift, hwdrive + HW_STMP3xxx_SET);
+}
+
+void stmp3xxx_pin_voltage(unsigned id, enum pin_voltage voltage,
+			  const char *label)
+{
+	struct stmp3xxx_pinmux_bank *pbank;
+	void __iomem *hwdrive;
+	u32 shift;
+	u32 bank, pin;
+
+	pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
+	pr_debug("%s: label %s bank %d pin %d voltage %d\n", __func__, label,
+		 bank, pin, voltage);
+
+	hwdrive = pbank->hw_drive[pin / HW_DRIVE_PIN_NUM];
+	shift = (pin % HW_DRIVE_PIN_NUM) * HW_DRIVE_PIN_LEN;
+
+	if (stmp3xxx_check_pin(id, label))
+		return;
+
+	pr_debug("%s: changing 0x%x bit in 0x%p register\n",
+			__func__, HW_DRIVE_PINV_MASK << shift, hwdrive);
+	if (voltage == PIN_1_8V)
+		__raw_writel(HW_DRIVE_PINV_MASK << shift,
+			     hwdrive + HW_STMP3xxx_CLR);
+	else
+		__raw_writel(HW_DRIVE_PINV_MASK << shift,
+			     hwdrive + HW_STMP3xxx_SET);
+}
+
+void stmp3xxx_pin_pullup(unsigned id, int enable, const char *label)
+{
+	struct stmp3xxx_pinmux_bank *pbank;
+	void __iomem *hwpull;
+	u32 bank, pin;
+
+	pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
+	pr_debug("%s: label %s bank %d pin %d enable %d\n", __func__, label,
+		 bank, pin, enable);
+
+	hwpull = pbank->hw_pull;
+
+	if (stmp3xxx_check_pin(id, label))
+		return;
+
+	pr_debug("%s: changing 0x%x bit in 0x%p register\n",
+			__func__, 1 << pin, hwpull);
+	__raw_writel(1 << pin,
+		     hwpull + (enable ? HW_STMP3xxx_SET : HW_STMP3xxx_CLR));
+}
+
+int stmp3xxx_request_pin(unsigned id, enum pin_fun fun, const char *label)
+{
+	struct stmp3xxx_pinmux_bank *pbank;
+	u32 bank, pin;
+	int ret = 0;
+
+	pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
+	pr_debug("%s: label %s bank %d pin %d fun %d\n", __func__, label,
+		 bank, pin, fun);
+
+	if (test_bit(pin, &pbank->pin_map)) {
+		printk(KERN_WARNING
+		       "%s: CONFLICT DETECTED pin %d:%d caller %s owner %s\n",
+		       __func__, bank, pin, label, pbank->pin_labels[pin]);
+		return -EBUSY;
+	}
+
+	set_bit(pin, &pbank->pin_map);
+	pbank->pin_labels[pin] = label;
+
+	stmp3xxx_set_pin_type(id, fun);
+
+	return ret;
+}
+
+void stmp3xxx_set_pin_type(unsigned id, enum pin_fun fun)
+{
+	struct stmp3xxx_pinmux_bank *pbank;
+	void __iomem *hwmux;
+	u32 shift, val;
+	u32 bank, pin;
+
+	pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
+
+	hwmux = pbank->hw_muxsel[pin / HW_MUXSEL_PIN_NUM];
+	shift = (pin % HW_MUXSEL_PIN_NUM) * HW_MUXSEL_PIN_LEN;
+
+	val = pbank->functions[fun];
+	shift = (pin % HW_MUXSEL_PIN_NUM) * HW_MUXSEL_PIN_LEN;
+	pr_debug("%s: writing 0x%x to 0x%p register\n",
+			__func__, val << shift, hwmux);
+	__raw_writel(HW_MUXSEL_PINFUN_MASK << shift, hwmux + HW_STMP3xxx_CLR);
+	__raw_writel(val << shift, hwmux + HW_STMP3xxx_SET);
+}
+
+void stmp3xxx_release_pin(unsigned id, const char *label)
+{
+	struct stmp3xxx_pinmux_bank *pbank;
+	u32 bank, pin;
+
+	pbank = stmp3xxx_pinmux_bank(id, &bank, &pin);
+	pr_debug("%s: label %s bank %d pin %d\n", __func__, label, bank, pin);
+
+	if (stmp3xxx_check_pin(id, label))
+		return;
+
+	clear_bit(pin, &pbank->pin_map);
+	pbank->pin_labels[pin] = NULL;
+}
+
+int stmp3xxx_request_pin_group(struct pin_group *pin_group, const char *label)
+{
+	struct pin_desc *pin;
+	int p;
+	int err = 0;
+
+	/* Allocate and configure pins */
+	for (p = 0; p < pin_group->nr_pins; p++) {
+		pr_debug("%s: #%d\n", __func__, p);
+		pin = &pin_group->pins[p];
+
+		err = stmp3xxx_request_pin(pin->id, pin->fun, label);
+		if (err)
+			goto out_err;
+
+		stmp3xxx_pin_strength(pin->id, pin->strength, label);
+		stmp3xxx_pin_voltage(pin->id, pin->voltage, label);
+		stmp3xxx_pin_pullup(pin->id, pin->pullup, label);
+	}
+
+	return 0;
+
+out_err:
+	/* Release allocated pins in case of error */
+	while (--p >= 0) {
+		pr_debug("%s: releasing #%d\n", __func__, p);
+		stmp3xxx_release_pin(pin_group->pins[p].id, label);
+	}
+	return err;
+}
+EXPORT_SYMBOL(stmp3xxx_request_pin_group);
+
+void stmp3xxx_release_pin_group(struct pin_group *pin_group, const char *label)
+{
+	struct pin_desc *pin;
+	int p;
+
+	for (p = 0; p < pin_group->nr_pins; p++) {
+		pin = &pin_group->pins[p];
+		stmp3xxx_release_pin(pin->id, label);
+	}
+}
+EXPORT_SYMBOL(stmp3xxx_release_pin_group);
+
+static int stmp3xxx_irq_to_gpio(int irq,
+	struct stmp3xxx_pinmux_bank **bank, unsigned *gpio)
+{
+	struct stmp3xxx_pinmux_bank *pm;
+
+	for (pm = pinmux_banks; pm < pinmux_banks + NR_BANKS; pm++)
+		if (pm->virq <= irq && irq < pm->virq + 32) {
+			*bank = pm;
+			*gpio = irq - pm->virq;
+			return 0;
+		}
+	return -ENOENT;
+}
+
+static int stmp3xxx_set_irqtype(unsigned irq, unsigned type)
+{
+	struct stmp3xxx_pinmux_bank *pm;
+	unsigned gpio;
+	int l, p;
+
+	stmp3xxx_irq_to_gpio(irq, &pm, &gpio);
+	switch (type) {
+	case IRQ_TYPE_EDGE_RISING:
+		l = 0; p = 1; break;
+	case IRQ_TYPE_EDGE_FALLING:
+		l = 0; p = 0; break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		l = 1; p = 1; break;
+	case IRQ_TYPE_LEVEL_LOW:
+		l = 1; p = 0; break;
+	default:
+		pr_debug("%s: Incorrect GPIO interrupt type 0x%x\n",
+				__func__, type);
+		return -ENXIO;
+	}
+	__raw_writel(1 << gpio,
+		pm->irqlevel + (l ? HW_STMP3xxx_SET : HW_STMP3xxx_CLR));
+	__raw_writel(1 << gpio,
+		pm->irqpolarity + (p ? HW_STMP3xxx_SET : HW_STMP3xxx_CLR));
+	return 0;
+}
+
+static void stmp3xxx_pin_ack_irq(unsigned irq)
+{
+	u32 stat;
+	struct stmp3xxx_pinmux_bank *pm;
+	unsigned gpio;
+
+	stmp3xxx_irq_to_gpio(irq, &pm, &gpio);
+	stat = __raw_readl(pm->irqstat) & (1<<gpio);
+	__raw_writel(stat, pm->irqstat + HW_STMP3xxx_CLR);
+}
+
+static void stmp3xxx_pin_mask_irq(unsigned irq)
+{
+	struct stmp3xxx_pinmux_bank *pm;
+	unsigned gpio;
+
+	stmp3xxx_irq_to_gpio(irq, &pm, &gpio);
+	__raw_writel(1 << gpio, pm->irqen + HW_STMP3xxx_CLR);
+	__raw_writel(1 << gpio, pm->pin2irq + HW_STMP3xxx_CLR);
+}
+
+static void stmp3xxx_pin_unmask_irq(unsigned irq)
+{
+	struct stmp3xxx_pinmux_bank *pm;
+	unsigned gpio;
+
+	stmp3xxx_irq_to_gpio(irq, &pm, &gpio);
+	__raw_writel(1 << gpio, pm->irqen + HW_STMP3xxx_SET);
+	__raw_writel(1 << gpio, pm->pin2irq + HW_STMP3xxx_SET);
+}
+
+static inline
+struct stmp3xxx_pinmux_bank *to_pinmux_bank(struct gpio_chip *chip)
+{
+	return container_of(chip, struct stmp3xxx_pinmux_bank, chip);
+}
+
+static int stmp3xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
+	return pm->virq + offset;
+}
+
+static int stmp3xxx_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
+	unsigned v;
+
+	v = __raw_readl(pm->hw_gpio_read) & (1 << offset);
+	return v ? 1 : 0;
+}
+
+static void stmp3xxx_gpio_set(struct gpio_chip *chip, unsigned offset, int v)
+{
+	struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
+
+	__raw_writel(1 << offset, v ? pm->hw_gpio_set : pm->hw_gpio_clr);
+}
+
+static int stmp3xxx_gpio_output(struct gpio_chip *chip, unsigned offset, int v)
+{
+	struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
+
+	__raw_writel(1 << offset, pm->hw_gpio_doe + HW_STMP3xxx_SET);
+	stmp3xxx_gpio_set(chip, offset, v);
+	return 0;
+}
+
+static int stmp3xxx_gpio_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct stmp3xxx_pinmux_bank *pm = to_pinmux_bank(chip);
+
+	__raw_writel(1 << offset, pm->hw_gpio_doe + HW_STMP3xxx_CLR);
+	return 0;
+}
+
+static int stmp3xxx_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	return stmp3xxx_request_pin(chip->base + offset, PIN_GPIO, "gpio");
+}
+
+static void stmp3xxx_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+	stmp3xxx_release_pin(chip->base + offset, "gpio");
+}
+
+static void stmp3xxx_gpio_irq(u32 irq, struct irq_desc *desc)
+{
+	struct stmp3xxx_pinmux_bank *pm = get_irq_data(irq);
+	int gpio_irq = pm->virq;
+	u32 stat = __raw_readl(pm->irqstat);
+
+	while (stat) {
+		if (stat & 1)
+			irq_desc[gpio_irq].handle_irq(gpio_irq,
+				&irq_desc[gpio_irq]);
+		gpio_irq++;
+		stat >>= 1;
+	}
+}
+
+static struct irq_chip gpio_irq_chip = {
+	.ack	= stmp3xxx_pin_ack_irq,
+	.mask	= stmp3xxx_pin_mask_irq,
+	.unmask	= stmp3xxx_pin_unmask_irq,
+	.set_type = stmp3xxx_set_irqtype,
+};
+
+int __init stmp3xxx_pinmux_init(int virtual_irq_start)
+{
+	int b, r = 0;
+	struct stmp3xxx_pinmux_bank *pm;
+	int virq;
+
+	for (b = 0; b < 3; b++) {
+		/* only banks 0,1,2 are allowed to GPIO */
+		pm = pinmux_banks + b;
+		pm->chip.base = 32 * b;
+		pm->chip.ngpio = 32;
+		pm->chip.owner = THIS_MODULE;
+		pm->chip.can_sleep = 1;
+		pm->chip.exported = 1;
+		pm->chip.to_irq = stmp3xxx_gpio_to_irq;
+		pm->chip.direction_input = stmp3xxx_gpio_input;
+		pm->chip.direction_output = stmp3xxx_gpio_output;
+		pm->chip.get = stmp3xxx_gpio_get;
+		pm->chip.set = stmp3xxx_gpio_set;
+		pm->chip.request = stmp3xxx_gpio_request;
+		pm->chip.free = stmp3xxx_gpio_free;
+		pm->virq = virtual_irq_start + b * 32;
+
+		for (virq = pm->virq; virq < pm->virq; virq++) {
+			gpio_irq_chip.mask(virq);
+			set_irq_chip(virq, &gpio_irq_chip);
+			set_irq_handler(virq, handle_level_irq);
+			set_irq_flags(virq, IRQF_VALID);
+		}
+		r = gpiochip_add(&pm->chip);
+		if (r < 0)
+			break;
+		set_irq_chained_handler(pm->irq, stmp3xxx_gpio_irq);
+		set_irq_data(pm->irq, pm);
+	}
+	return r;
+}
+
+MODULE_AUTHOR("Vladislav Buzov");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/plat-stmp3xxx/timer.c b/arch/arm/plat-stmp3xxx/timer.c
new file mode 100644
index 0000000..c916068
--- /dev/null
+++ b/arch/arm/plat-stmp3xxx/timer.c
@@ -0,0 +1,172 @@
+/*
+ * System timer for Freescale STMP37XX/STMP378X
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/mach/time.h>
+#include <mach/stmp3xxx.h>
+#include <mach/regs-timrot.h>
+
+static irqreturn_t
+stmp3xxx_timer_interrupt(int irq, void *dev_id)
+{
+	struct clock_event_device *c = dev_id;
+
+	if (HW_TIMROT_TIMCTRLn_RD(0) & (1<<15)) {
+		HW_TIMROT_TIMCTRLn_CLR(0, (1<<15));
+		c->event_handler(c);
+	} else if (HW_TIMROT_TIMCTRLn_RD(1) & (1<<15)) {
+		HW_TIMROT_TIMCTRLn_CLR(1, (1<<15));
+		HW_TIMROT_TIMCTRLn_CLR(1, BM_TIMROT_TIMCTRLn_IRQ_EN);
+		HW_TIMROT_TIMCOUNTn_WR(1, 0xFFFF);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static cycle_t stmp3xxx_clock_read(void)
+{
+	return ~((HW_TIMROT_TIMCOUNTn_RD(1) & 0xFFFF0000) >> 16);
+}
+
+static int
+stmp3xxx_timrot_set_next_event(unsigned long delta,
+		struct clock_event_device *dev)
+{
+	HW_TIMROT_TIMCOUNTn_WR(0, delta); /* reload */
+	return 0;
+}
+
+static void
+stmp3xxx_timrot_set_mode(enum clock_event_mode mode,
+		struct clock_event_device *dev)
+{
+}
+
+static struct clock_event_device ckevt_timrot = {
+	.name		= "timrot",
+	.features	= CLOCK_EVT_FEAT_ONESHOT,
+	.shift		= 32,
+	.set_next_event	= stmp3xxx_timrot_set_next_event,
+	.set_mode	= stmp3xxx_timrot_set_mode,
+};
+
+static struct clocksource cksrc_stmp3xxx = {
+	.name           = "cksrc_stmp3xxx",
+	.rating         = 250,
+	.read           = stmp3xxx_clock_read,
+	.mask           = CLOCKSOURCE_MASK(16),
+	.shift          = 10,
+	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static struct irqaction stmp3xxx_timer_irq = {
+	.name		= "stmp3xxx_timer",
+	.flags		= IRQF_DISABLED | IRQF_TIMER,
+	.handler	= stmp3xxx_timer_interrupt,
+	.dev_id		= &ckevt_timrot,
+};
+
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ */
+static void __init stmp3xxx_init_timer(void)
+{
+	cksrc_stmp3xxx.mult = clocksource_hz2mult(CLOCK_TICK_RATE,
+				cksrc_stmp3xxx.shift);
+	ckevt_timrot.mult = div_sc(CLOCK_TICK_RATE, NSEC_PER_SEC,
+				ckevt_timrot.shift);
+	ckevt_timrot.min_delta_ns = clockevent_delta2ns(2, &ckevt_timrot);
+	ckevt_timrot.max_delta_ns = clockevent_delta2ns(0xFFF, &ckevt_timrot);
+	ckevt_timrot.cpumask = cpumask_of(0);
+
+	HW_TIMROT_ROTCTRL_CLR(BM_TIMROT_ROTCTRL_SFTRST |
+				BM_TIMROT_ROTCTRL_CLKGATE);
+	HW_TIMROT_TIMCOUNTn_WR(0, 0);
+	HW_TIMROT_TIMCOUNTn_WR(1, 0);
+
+	HW_TIMROT_TIMCTRLn_WR(0,
+			      (BF_TIMROT_TIMCTRLn_SELECT(8) |  /* 32 kHz */
+			       BF_TIMROT_TIMCTRLn_PRESCALE(0) |
+			       BM_TIMROT_TIMCTRLn_RELOAD |
+			       BM_TIMROT_TIMCTRLn_UPDATE |
+			       BM_TIMROT_TIMCTRLn_IRQ_EN));
+	HW_TIMROT_TIMCTRLn_WR(1,
+			      (BF_TIMROT_TIMCTRLn_SELECT(8) |  /* 32 kHz */
+			       BF_TIMROT_TIMCTRLn_PRESCALE(0) |
+			       BM_TIMROT_TIMCTRLn_RELOAD |
+			       BM_TIMROT_TIMCTRLn_UPDATE));
+
+	HW_TIMROT_TIMCOUNTn_WR(0, CLOCK_TICK_RATE / HZ - 1);
+	HW_TIMROT_TIMCOUNTn_WR(1, 0xFFFF); /* reload */
+
+	setup_irq(IRQ_TIMER0, &stmp3xxx_timer_irq);
+
+	clocksource_register(&cksrc_stmp3xxx);
+	clockevents_register_device(&ckevt_timrot);
+}
+
+#ifdef CONFIG_PM
+
+void stmp3xxx_suspend_timer(void)
+{
+	HW_TIMROT_TIMCTRLn_CLR(0, BM_TIMROT_TIMCTRLn_IRQ_EN);
+	HW_TIMROT_TIMCTRLn_CLR(0, (1<<15));
+	HW_TIMROT_ROTCTRL_SET(BM_TIMROT_ROTCTRL_CLKGATE);
+}
+
+void stmp3xxx_resume_timer(void)
+{
+	HW_TIMROT_ROTCTRL_CLR(BM_TIMROT_ROTCTRL_SFTRST |
+				BM_TIMROT_ROTCTRL_CLKGATE);
+
+
+	HW_TIMROT_TIMCTRLn_WR(0,
+			      (BF_TIMROT_TIMCTRLn_SELECT(8) |  /* 32 kHz */
+			       BF_TIMROT_TIMCTRLn_PRESCALE(0) |
+			       BM_TIMROT_TIMCTRLn_UPDATE |
+			       BM_TIMROT_TIMCTRLn_IRQ_EN));
+	HW_TIMROT_TIMCTRLn_WR(1,
+			      (BF_TIMROT_TIMCTRLn_SELECT(8) |  /* 32 kHz */
+			       BF_TIMROT_TIMCTRLn_PRESCALE(0) |
+			       BM_TIMROT_TIMCTRLn_RELOAD |
+			       BM_TIMROT_TIMCTRLn_UPDATE));
+
+	HW_TIMROT_TIMCOUNTn_WR(0, CLOCK_TICK_RATE / HZ - 1);
+	HW_TIMROT_TIMCOUNTn_WR(1, 0xFFFF); /* reload */
+}
+
+#else
+
+#define stmp3xxx_suspend_timer	NULL
+#define	stmp3xxx_resume_timer	NULL
+
+#endif	/* CONFIG_PM */
+
+struct sys_timer stmp3xxx_timer = {
+	.init		= stmp3xxx_init_timer,
+	.suspend	= stmp3xxx_suspend_timer,
+	.resume		= stmp3xxx_resume_timer,
+};