[ARM] 5477/1: Freescale STMP platform support [6/10]
authordmitry pervushin <dpervushin@embeddedalley.com>
Thu, 23 Apr 2009 11:24:13 +0000 (12:24 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 27 Apr 2009 09:28:08 +0000 (10:28 +0100)
Sources: common STMP3xxx platform support

Signed-off-by: dmitry pervushin <dpervushin@embeddedalley.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/plat-stmp3xxx/Kconfig [new file with mode: 0644]
arch/arm/plat-stmp3xxx/Makefile [new file with mode: 0644]
arch/arm/plat-stmp3xxx/clock.c [new file with mode: 0644]
arch/arm/plat-stmp3xxx/clock.h [new file with mode: 0644]
arch/arm/plat-stmp3xxx/core.c [new file with mode: 0644]
arch/arm/plat-stmp3xxx/dma.c [new file with mode: 0644]
arch/arm/plat-stmp3xxx/irq.c [new file with mode: 0644]
arch/arm/plat-stmp3xxx/pinmux.c [new file with mode: 0644]
arch/arm/plat-stmp3xxx/timer.c [new file with mode: 0644]

diff --git a/arch/arm/plat-stmp3xxx/Kconfig b/arch/arm/plat-stmp3xxx/Kconfig
new file mode 100644 (file)
index 0000000..2cf37c3
--- /dev/null
@@ -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 (file)
index 0000000..b634800
--- /dev/null
@@ -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 (file)
index 0000000..9a1d46b
--- /dev/null
@@ -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 (file)
index 0000000..a6611e1
--- /dev/null
@@ -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 (file)
index 0000000..6e2fef1
--- /dev/null
@@ -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 (file)
index 0000000..cf42de0
--- /dev/null
@@ -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 (file)
index 0000000..cb36590
--- /dev/null
@@ -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 (file)
index 0000000..9b28cc8
--- /dev/null
@@ -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 (file)
index 0000000..c916068
--- /dev/null
@@ -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,
+};
This page took 0.049633 seconds and 5 git commands to generate.