aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-spear
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-spear')
-rw-r--r--arch/arm/plat-spear/Kconfig31
-rw-r--r--arch/arm/plat-spear/Makefile8
-rw-r--r--arch/arm/plat-spear/clock.c1008
-rw-r--r--arch/arm/plat-spear/include/plat/clkdev.h20
-rw-r--r--arch/arm/plat-spear/include/plat/clock.h249
-rw-r--r--arch/arm/plat-spear/include/plat/debug-macro.S36
-rw-r--r--arch/arm/plat-spear/include/plat/gpio.h24
-rw-r--r--arch/arm/plat-spear/include/plat/hardware.h23
-rw-r--r--arch/arm/plat-spear/include/plat/io.h22
-rw-r--r--arch/arm/plat-spear/include/plat/keyboard.h141
-rw-r--r--arch/arm/plat-spear/include/plat/memory.h20
-rw-r--r--arch/arm/plat-spear/include/plat/padmux.h92
-rw-r--r--arch/arm/plat-spear/include/plat/shirq.h73
-rw-r--r--arch/arm/plat-spear/include/plat/system.h41
-rw-r--r--arch/arm/plat-spear/include/plat/timex.h19
-rw-r--r--arch/arm/plat-spear/include/plat/uncompress.h43
-rw-r--r--arch/arm/plat-spear/include/plat/vmalloc.h19
-rw-r--r--arch/arm/plat-spear/padmux.c164
-rw-r--r--arch/arm/plat-spear/shirq.c118
-rw-r--r--arch/arm/plat-spear/time.c237
20 files changed, 2388 insertions, 0 deletions
diff --git a/arch/arm/plat-spear/Kconfig b/arch/arm/plat-spear/Kconfig
new file mode 100644
index 00000000..1bb3dbce
--- /dev/null
+++ b/arch/arm/plat-spear/Kconfig
@@ -0,0 +1,31 @@
+#
+# SPEAr Platform configuration file
+#
+
+if PLAT_SPEAR
+
+choice
+ prompt "ST SPEAr Family"
+ default ARCH_SPEAR3XX
+
+config ARCH_SPEAR3XX
+ bool "SPEAr3XX"
+ select ARM_VIC
+ select CPU_ARM926T
+ help
+ Supports for ARM's SPEAR3XX family
+
+config ARCH_SPEAR6XX
+ bool "SPEAr6XX"
+ select ARM_VIC
+ select CPU_ARM926T
+ help
+ Supports for ARM's SPEAR6XX family
+
+endchoice
+
+# Adding SPEAr machine specific configuration files
+source "arch/arm/mach-spear3xx/Kconfig"
+source "arch/arm/mach-spear6xx/Kconfig"
+
+endif
diff --git a/arch/arm/plat-spear/Makefile b/arch/arm/plat-spear/Makefile
new file mode 100644
index 00000000..b4f340b8
--- /dev/null
+++ b/arch/arm/plat-spear/Makefile
@@ -0,0 +1,8 @@
+#
+# SPEAr Platform specific Makefile
+#
+
+# Common support
+obj-y := clock.o time.o
+
+obj-$(CONFIG_ARCH_SPEAR3XX) += shirq.o padmux.o
diff --git a/arch/arm/plat-spear/clock.c b/arch/arm/plat-spear/clock.c
new file mode 100644
index 00000000..6fa474cb
--- /dev/null
+++ b/arch/arm/plat-spear/clock.c
@@ -0,0 +1,1008 @@
+/*
+ * arch/arm/plat-spear/clock.c
+ *
+ * Clock framework for SPEAr platform
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <plat/clock.h>
+
+static DEFINE_SPINLOCK(clocks_lock);
+static LIST_HEAD(root_clks);
+#ifdef CONFIG_DEBUG_FS
+static LIST_HEAD(clocks);
+#endif
+
+static void propagate_rate(struct clk *, int on_init);
+#ifdef CONFIG_DEBUG_FS
+static int clk_debugfs_reparent(struct clk *);
+#endif
+
+static int generic_clk_enable(struct clk *clk)
+{
+ unsigned int val;
+
+ if (!clk->en_reg)
+ return -EFAULT;
+
+ val = readl(clk->en_reg);
+ if (unlikely(clk->flags & RESET_TO_ENABLE))
+ val &= ~(1 << clk->en_reg_bit);
+ else
+ val |= 1 << clk->en_reg_bit;
+
+ writel(val, clk->en_reg);
+
+ return 0;
+}
+
+static void generic_clk_disable(struct clk *clk)
+{
+ unsigned int val;
+
+ if (!clk->en_reg)
+ return;
+
+ val = readl(clk->en_reg);
+ if (unlikely(clk->flags & RESET_TO_ENABLE))
+ val |= 1 << clk->en_reg_bit;
+ else
+ val &= ~(1 << clk->en_reg_bit);
+
+ writel(val, clk->en_reg);
+}
+
+/* generic clk ops */
+static struct clkops generic_clkops = {
+ .enable = generic_clk_enable,
+ .disable = generic_clk_disable,
+};
+
+/* returns current programmed clocks clock info structure */
+static struct pclk_info *pclk_info_get(struct clk *clk)
+{
+ unsigned int val, i;
+ struct pclk_info *info = NULL;
+
+ val = (readl(clk->pclk_sel->pclk_sel_reg) >> clk->pclk_sel_shift)
+ & clk->pclk_sel->pclk_sel_mask;
+
+ for (i = 0; i < clk->pclk_sel->pclk_count; i++) {
+ if (clk->pclk_sel->pclk_info[i].pclk_val == val)
+ info = &clk->pclk_sel->pclk_info[i];
+ }
+
+ return info;
+}
+
+/*
+ * Set Update pclk, and pclk_info of clk and add clock sibling node to current
+ * parents children list
+ */
+static void clk_reparent(struct clk *clk, struct pclk_info *pclk_info)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ list_del(&clk->sibling);
+ list_add(&clk->sibling, &pclk_info->pclk->children);
+
+ clk->pclk = pclk_info->pclk;
+ spin_unlock_irqrestore(&clocks_lock, flags);
+
+#ifdef CONFIG_DEBUG_FS
+ clk_debugfs_reparent(clk);
+#endif
+}
+
+static void do_clk_disable(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ if (!clk->usage_count) {
+ WARN_ON(1);
+ return;
+ }
+
+ clk->usage_count--;
+
+ if (clk->usage_count == 0) {
+ /*
+ * Surely, there are no active childrens or direct users
+ * of this clock
+ */
+ if (clk->pclk)
+ do_clk_disable(clk->pclk);
+
+ if (clk->ops && clk->ops->disable)
+ clk->ops->disable(clk);
+ }
+}
+
+static int do_clk_enable(struct clk *clk)
+{
+ int ret = 0;
+
+ if (!clk)
+ return -EFAULT;
+
+ if (clk->usage_count == 0) {
+ if (clk->pclk) {
+ ret = do_clk_enable(clk->pclk);
+ if (ret)
+ goto err;
+ }
+ if (clk->ops && clk->ops->enable) {
+ ret = clk->ops->enable(clk);
+ if (ret) {
+ if (clk->pclk)
+ do_clk_disable(clk->pclk);
+ goto err;
+ }
+ }
+ /*
+ * Since the clock is going to be used for the first
+ * time please reclac
+ */
+ if (clk->recalc) {
+ ret = clk->recalc(clk);
+ if (ret)
+ goto err;
+ }
+ }
+ clk->usage_count++;
+err:
+ return ret;
+}
+
+/*
+ * clk_enable - inform the system when the clock source should be running.
+ * @clk: clock source
+ *
+ * If the clock can not be enabled/disabled, this should return success.
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ ret = do_clk_enable(clk);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+
+/*
+ * clk_disable - inform the system when the clock source is no longer required.
+ * @clk: clock source
+ *
+ * Inform the system that a clock source is no longer required by
+ * a driver and may be shut down.
+ *
+ * Implementation detail: if the clock source is shared between
+ * multiple drivers, clk_enable() calls must be balanced by the
+ * same number of clk_disable() calls for the clock source to be
+ * disabled.
+ */
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ do_clk_disable(clk);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+/**
+ * clk_get_rate - obtain the current clock rate (in Hz) for a clock source.
+ * This is only valid once the clock source has been enabled.
+ * @clk: clock source
+ */
+unsigned long clk_get_rate(struct clk *clk)
+{
+ unsigned long flags, rate;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ rate = clk->rate;
+ spin_unlock_irqrestore(&clocks_lock, flags);
+
+ return rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+/**
+ * clk_set_parent - set the parent clock source for this clock
+ * @clk: clock source
+ * @parent: parent clock source
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int i, found = 0, val = 0;
+ unsigned long flags;
+
+ if (!clk || !parent)
+ return -EFAULT;
+ if (clk->pclk == parent)
+ return 0;
+ if (!clk->pclk_sel)
+ return -EPERM;
+
+ /* check if requested parent is in clk parent list */
+ for (i = 0; i < clk->pclk_sel->pclk_count; i++) {
+ if (clk->pclk_sel->pclk_info[i].pclk == parent) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ /* reflect parent change in hardware */
+ val = readl(clk->pclk_sel->pclk_sel_reg);
+ val &= ~(clk->pclk_sel->pclk_sel_mask << clk->pclk_sel_shift);
+ val |= clk->pclk_sel->pclk_info[i].pclk_val << clk->pclk_sel_shift;
+ writel(val, clk->pclk_sel->pclk_sel_reg);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+
+ /* reflect parent change in software */
+ clk_reparent(clk, &clk->pclk_sel->pclk_info[i]);
+
+ propagate_rate(clk, 0);
+ return 0;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+/**
+ * clk_set_rate - set the clock rate for a clock source
+ * @clk: clock source
+ * @rate: desired clock rate in Hz
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ if (!clk || !rate)
+ return -EFAULT;
+
+ if (clk->set_rate) {
+ spin_lock_irqsave(&clocks_lock, flags);
+ ret = clk->set_rate(clk, rate);
+ if (!ret)
+ /* if successful -> propagate */
+ propagate_rate(clk, 0);
+ spin_unlock_irqrestore(&clocks_lock, flags);
+ } else if (clk->pclk) {
+ u32 mult = clk->div_factor ? clk->div_factor : 1;
+ ret = clk_set_rate(clk->pclk, mult * rate);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+/* registers clock in platform clock framework */
+void clk_register(struct clk_lookup *cl)
+{
+ struct clk *clk;
+ unsigned long flags;
+
+ if (!cl || !cl->clk)
+ return;
+ clk = cl->clk;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+
+ INIT_LIST_HEAD(&clk->children);
+ if (clk->flags & ALWAYS_ENABLED)
+ clk->ops = NULL;
+ else if (!clk->ops)
+ clk->ops = &generic_clkops;
+
+ /* root clock don't have any parents */
+ if (!clk->pclk && !clk->pclk_sel) {
+ list_add(&clk->sibling, &root_clks);
+ } else if (clk->pclk && !clk->pclk_sel) {
+ /* add clocks with only one parent to parent's children list */
+ list_add(&clk->sibling, &clk->pclk->children);
+ } else {
+ /* clocks with more than one parent */
+ struct pclk_info *pclk_info;
+
+ pclk_info = pclk_info_get(clk);
+ if (!pclk_info) {
+ pr_err("CLKDEV: invalid pclk info of clk with"
+ " %s dev_id and %s con_id\n",
+ cl->dev_id, cl->con_id);
+ } else {
+ clk->pclk = pclk_info->pclk;
+ list_add(&clk->sibling, &pclk_info->pclk->children);
+ }
+ }
+
+ spin_unlock_irqrestore(&clocks_lock, flags);
+
+ /* debugfs specific */
+#ifdef CONFIG_DEBUG_FS
+ list_add(&clk->node, &clocks);
+ clk->cl = cl;
+#endif
+
+ /* add clock to arm clockdev framework */
+ clkdev_add(cl);
+}
+
+/**
+ * propagate_rate - recalculate and propagate all clocks to children
+ * @pclk: parent clock required to be propogated
+ * @on_init: flag for enabling clocks which are ENABLED_ON_INIT.
+ *
+ * Recalculates all children clocks
+ */
+void propagate_rate(struct clk *pclk, int on_init)
+{
+ struct clk *clk, *_temp;
+ int ret = 0;
+
+ list_for_each_entry_safe(clk, _temp, &pclk->children, sibling) {
+ if (clk->recalc) {
+ ret = clk->recalc(clk);
+ /*
+ * recalc will return error if clk out is not programmed
+ * In this case configure default rate.
+ */
+ if (ret && clk->set_rate)
+ clk->set_rate(clk, 0);
+ }
+ propagate_rate(clk, on_init);
+
+ if (!on_init)
+ continue;
+
+ /* Enable clks enabled on init, in software view */
+ if (clk->flags & ENABLED_ON_INIT)
+ do_clk_enable(clk);
+ }
+}
+
+/**
+ * round_rate_index - return closest programmable rate index in rate_config tbl
+ * @clk: ptr to clock structure
+ * @drate: desired rate
+ * @rate: final rate will be returned in this variable only.
+ *
+ * Finds index in rate_config for highest clk rate which is less than
+ * requested rate. If there is no clk rate lesser than requested rate then
+ * -EINVAL is returned. This routine assumes that rate_config is written
+ * in incrementing order of clk rates.
+ * If drate passed is zero then default rate is programmed.
+ */
+static int
+round_rate_index(struct clk *clk, unsigned long drate, unsigned long *rate)
+{
+ unsigned long tmp = 0, prev_rate = 0;
+ int index;
+
+ if (!clk->calc_rate)
+ return -EFAULT;
+
+ if (!drate)
+ return -EINVAL;
+
+ /*
+ * This loops ends on two conditions:
+ * - as soon as clk is found with rate greater than requested rate.
+ * - if all clks in rate_config are smaller than requested rate.
+ */
+ for (index = 0; index < clk->rate_config.count; index++) {
+ prev_rate = tmp;
+ tmp = clk->calc_rate(clk, index);
+ if (drate < tmp) {
+ index--;
+ break;
+ }
+ }
+ /* return if can't find suitable clock */
+ if (index < 0) {
+ index = -EINVAL;
+ *rate = 0;
+ } else if (index == clk->rate_config.count) {
+ /* program with highest clk rate possible */
+ index = clk->rate_config.count - 1;
+ *rate = tmp;
+ } else
+ *rate = prev_rate;
+
+ return index;
+}
+
+/**
+ * clk_round_rate - adjust a rate to the exact rate a clock can provide
+ * @clk: clock source
+ * @rate: desired clock rate in Hz
+ *
+ * Returns rounded clock rate in Hz, or negative errno.
+ */
+long clk_round_rate(struct clk *clk, unsigned long drate)
+{
+ long rate = 0;
+ int index;
+
+ /*
+ * propagate call to parent who supports calc_rate. Similar approach is
+ * used in clk_set_rate.
+ */
+ if (!clk->calc_rate) {
+ u32 mult;
+ if (!clk->pclk)
+ return clk->rate;
+
+ mult = clk->div_factor ? clk->div_factor : 1;
+ return clk_round_rate(clk->pclk, mult * drate) / mult;
+ }
+
+ index = round_rate_index(clk, drate, &rate);
+ if (index >= 0)
+ return rate;
+ else
+ return index;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+/*All below functions are called with lock held */
+
+/*
+ * Calculates pll clk rate for specific value of mode, m, n and p
+ *
+ * In normal mode
+ * rate = (2 * M[15:8] * Fin)/(N * 2^P)
+ *
+ * In Dithered mode
+ * rate = (2 * M[15:0] * Fin)/(256 * N * 2^P)
+ */
+unsigned long pll_calc_rate(struct clk *clk, int index)
+{
+ unsigned long rate = clk->pclk->rate;
+ struct pll_rate_tbl *tbls = clk->rate_config.tbls;
+ unsigned int mode;
+
+ mode = tbls[index].mode ? 256 : 1;
+ return (((2 * rate / 10000) * tbls[index].m) /
+ (mode * tbls[index].n * (1 << tbls[index].p))) * 10000;
+}
+
+/*
+ * calculates current programmed rate of pll1
+ *
+ * In normal mode
+ * rate = (2 * M[15:8] * Fin)/(N * 2^P)
+ *
+ * In Dithered mode
+ * rate = (2 * M[15:0] * Fin)/(256 * N * 2^P)
+ */
+int pll_clk_recalc(struct clk *clk)
+{
+ struct pll_clk_config *config = clk->private_data;
+ unsigned int num = 2, den = 0, val, mode = 0;
+
+ mode = (readl(config->mode_reg) >> config->masks->mode_shift) &
+ config->masks->mode_mask;
+
+ val = readl(config->cfg_reg);
+ /* calculate denominator */
+ den = (val >> config->masks->div_p_shift) & config->masks->div_p_mask;
+ den = 1 << den;
+ den *= (val >> config->masks->div_n_shift) & config->masks->div_n_mask;
+
+ /* calculate numerator & denominator */
+ if (!mode) {
+ /* Normal mode */
+ num *= (val >> config->masks->norm_fdbk_m_shift) &
+ config->masks->norm_fdbk_m_mask;
+ } else {
+ /* Dithered mode */
+ num *= (val >> config->masks->dith_fdbk_m_shift) &
+ config->masks->dith_fdbk_m_mask;
+ den *= 256;
+ }
+
+ if (!den)
+ return -EINVAL;
+
+ clk->rate = (((clk->pclk->rate/10000) * num) / den) * 10000;
+ return 0;
+}
+
+/*
+ * Configures new clock rate of pll
+ */
+int pll_clk_set_rate(struct clk *clk, unsigned long desired_rate)
+{
+ struct pll_rate_tbl *tbls = clk->rate_config.tbls;
+ struct pll_clk_config *config = clk->private_data;
+ unsigned long val, rate;
+ int i;
+
+ i = round_rate_index(clk, desired_rate, &rate);
+ if (i < 0)
+ return i;
+
+ val = readl(config->mode_reg) &
+ ~(config->masks->mode_mask << config->masks->mode_shift);
+ val |= (tbls[i].mode & config->masks->mode_mask) <<
+ config->masks->mode_shift;
+ writel(val, config->mode_reg);
+
+ val = readl(config->cfg_reg) &
+ ~(config->masks->div_p_mask << config->masks->div_p_shift);
+ val |= (tbls[i].p & config->masks->div_p_mask) <<
+ config->masks->div_p_shift;
+ val &= ~(config->masks->div_n_mask << config->masks->div_n_shift);
+ val |= (tbls[i].n & config->masks->div_n_mask) <<
+ config->masks->div_n_shift;
+ val &= ~(config->masks->dith_fdbk_m_mask <<
+ config->masks->dith_fdbk_m_shift);
+ if (tbls[i].mode)
+ val |= (tbls[i].m & config->masks->dith_fdbk_m_mask) <<
+ config->masks->dith_fdbk_m_shift;
+ else
+ val |= (tbls[i].m & config->masks->norm_fdbk_m_mask) <<
+ config->masks->norm_fdbk_m_shift;
+
+ writel(val, config->cfg_reg);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+/*
+ * Calculates ahb, apb clk rate for specific value of div
+ */
+unsigned long bus_calc_rate(struct clk *clk, int index)
+{
+ unsigned long rate = clk->pclk->rate;
+ struct bus_rate_tbl *tbls = clk->rate_config.tbls;
+
+ return rate / (tbls[index].div + 1);
+}
+
+/* calculates current programmed rate of ahb or apb bus */
+int bus_clk_recalc(struct clk *clk)
+{
+ struct bus_clk_config *config = clk->private_data;
+ unsigned int div;
+
+ div = ((readl(config->reg) >> config->masks->shift) &
+ config->masks->mask) + 1;
+
+ if (!div)
+ return -EINVAL;
+
+ clk->rate = (unsigned long)clk->pclk->rate / div;
+ return 0;
+}
+
+/* Configures new clock rate of AHB OR APB bus */
+int bus_clk_set_rate(struct clk *clk, unsigned long desired_rate)
+{
+ struct bus_rate_tbl *tbls = clk->rate_config.tbls;
+ struct bus_clk_config *config = clk->private_data;
+ unsigned long val, rate;
+ int i;
+
+ i = round_rate_index(clk, desired_rate, &rate);
+ if (i < 0)
+ return i;
+
+ val = readl(config->reg) &
+ ~(config->masks->mask << config->masks->shift);
+ val |= (tbls[i].div & config->masks->mask) << config->masks->shift;
+ writel(val, config->reg);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+/*
+ * gives rate for different values of eq, x and y
+ *
+ * Fout from synthesizer can be given from two equations:
+ * Fout1 = (Fin * X/Y)/2 EQ1
+ * Fout2 = Fin * X/Y EQ2
+ */
+unsigned long aux_calc_rate(struct clk *clk, int index)
+{
+ unsigned long rate = clk->pclk->rate;
+ struct aux_rate_tbl *tbls = clk->rate_config.tbls;
+ u8 eq = tbls[index].eq ? 1 : 2;
+
+ return (((rate/10000) * tbls[index].xscale) /
+ (tbls[index].yscale * eq)) * 10000;
+}
+
+/*
+ * calculates current programmed rate of auxiliary synthesizers
+ * used by: UART, FIRDA
+ *
+ * Fout from synthesizer can be given from two equations:
+ * Fout1 = (Fin * X/Y)/2
+ * Fout2 = Fin * X/Y
+ *
+ * Selection of eqn 1 or 2 is programmed in register
+ */
+int aux_clk_recalc(struct clk *clk)
+{
+ struct aux_clk_config *config = clk->private_data;
+ unsigned int num = 1, den = 1, val, eqn;
+
+ val = readl(config->synth_reg);
+
+ eqn = (val >> config->masks->eq_sel_shift) &
+ config->masks->eq_sel_mask;
+ if (eqn == config->masks->eq1_mask)
+ den *= 2;
+
+ /* calculate numerator */
+ num = (val >> config->masks->xscale_sel_shift) &
+ config->masks->xscale_sel_mask;
+
+ /* calculate denominator */
+ den *= (val >> config->masks->yscale_sel_shift) &
+ config->masks->yscale_sel_mask;
+
+ if (!den)
+ return -EINVAL;
+
+ clk->rate = (((clk->pclk->rate/10000) * num) / den) * 10000;
+ return 0;
+}
+
+/* Configures new clock rate of auxiliary synthesizers used by: UART, FIRDA*/
+int aux_clk_set_rate(struct clk *clk, unsigned long desired_rate)
+{
+ struct aux_rate_tbl *tbls = clk->rate_config.tbls;
+ struct aux_clk_config *config = clk->private_data;
+ unsigned long val, rate;
+ int i;
+
+ i = round_rate_index(clk, desired_rate, &rate);
+ if (i < 0)
+ return i;
+
+ val = readl(config->synth_reg) &
+ ~(config->masks->eq_sel_mask << config->masks->eq_sel_shift);
+ val |= (tbls[i].eq & config->masks->eq_sel_mask) <<
+ config->masks->eq_sel_shift;
+ val &= ~(config->masks->xscale_sel_mask <<
+ config->masks->xscale_sel_shift);
+ val |= (tbls[i].xscale & config->masks->xscale_sel_mask) <<
+ config->masks->xscale_sel_shift;
+ val &= ~(config->masks->yscale_sel_mask <<
+ config->masks->yscale_sel_shift);
+ val |= (tbls[i].yscale & config->masks->yscale_sel_mask) <<
+ config->masks->yscale_sel_shift;
+ writel(val, config->synth_reg);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+/*
+ * Calculates gpt clk rate for different values of mscale and nscale
+ *
+ * Fout= Fin/((2 ^ (N+1)) * (M+1))
+ */
+unsigned long gpt_calc_rate(struct clk *clk, int index)
+{
+ unsigned long rate = clk->pclk->rate;
+ struct gpt_rate_tbl *tbls = clk->rate_config.tbls;
+
+ return rate / ((1 << (tbls[index].nscale + 1)) *
+ (tbls[index].mscale + 1));
+}
+
+/*
+ * calculates current programmed rate of gpt synthesizers
+ * Fout from synthesizer can be given from below equations:
+ * Fout= Fin/((2 ^ (N+1)) * (M+1))
+ */
+int gpt_clk_recalc(struct clk *clk)
+{
+ struct gpt_clk_config *config = clk->private_data;
+ unsigned int div = 1, val;
+
+ val = readl(config->synth_reg);
+ div += (val >> config->masks->mscale_sel_shift) &
+ config->masks->mscale_sel_mask;
+ div *= 1 << (((val >> config->masks->nscale_sel_shift) &
+ config->masks->nscale_sel_mask) + 1);
+
+ if (!div)
+ return -EINVAL;
+
+ clk->rate = (unsigned long)clk->pclk->rate / div;
+ return 0;
+}
+
+/* Configures new clock rate of gptiliary synthesizers used by: UART, FIRDA*/
+int gpt_clk_set_rate(struct clk *clk, unsigned long desired_rate)
+{
+ struct gpt_rate_tbl *tbls = clk->rate_config.tbls;
+ struct gpt_clk_config *config = clk->private_data;
+ unsigned long val, rate;
+ int i;
+
+ i = round_rate_index(clk, desired_rate, &rate);
+ if (i < 0)
+ return i;
+
+ val = readl(config->synth_reg) & ~(config->masks->mscale_sel_mask <<
+ config->masks->mscale_sel_shift);
+ val |= (tbls[i].mscale & config->masks->mscale_sel_mask) <<
+ config->masks->mscale_sel_shift;
+ val &= ~(config->masks->nscale_sel_mask <<
+ config->masks->nscale_sel_shift);
+ val |= (tbls[i].nscale & config->masks->nscale_sel_mask) <<
+ config->masks->nscale_sel_shift;
+ writel(val, config->synth_reg);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+/*
+ * Calculates clcd clk rate for different values of div
+ *
+ * Fout from synthesizer can be given from below equation:
+ * Fout= Fin/2*div (division factor)
+ * div is 17 bits:-
+ * 0-13 (fractional part)
+ * 14-16 (integer part)
+ * To calculate Fout we left shift val by 14 bits and divide Fin by
+ * complete div (including fractional part) and then right shift the
+ * result by 14 places.
+ */
+unsigned long clcd_calc_rate(struct clk *clk, int index)
+{
+ unsigned long rate = clk->pclk->rate;
+ struct clcd_rate_tbl *tbls = clk->rate_config.tbls;
+
+ rate /= 1000;
+ rate <<= 12;
+ rate /= (2 * tbls[index].div);
+ rate >>= 12;
+ rate *= 1000;
+
+ return rate;
+}
+
+/*
+ * calculates current programmed rate of clcd synthesizer
+ * Fout from synthesizer can be given from below equation:
+ * Fout= Fin/2*div (division factor)
+ * div is 17 bits:-
+ * 0-13 (fractional part)
+ * 14-16 (integer part)
+ * To calculate Fout we left shift val by 14 bits and divide Fin by
+ * complete div (including fractional part) and then right shift the
+ * result by 14 places.
+ */
+int clcd_clk_recalc(struct clk *clk)
+{
+ struct clcd_clk_config *config = clk->private_data;
+ unsigned int div = 1;
+ unsigned long prate;
+ unsigned int val;
+
+ val = readl(config->synth_reg);
+ div = (val >> config->masks->div_factor_shift) &
+ config->masks->div_factor_mask;
+
+ if (!div)
+ return -EINVAL;
+
+ prate = clk->pclk->rate / 1000; /* first level division, make it KHz */
+
+ clk->rate = (((unsigned long)prate << 12) / (2 * div)) >> 12;
+ clk->rate *= 1000;
+ return 0;
+}
+
+/* Configures new clock rate of auxiliary synthesizers used by: UART, FIRDA*/
+int clcd_clk_set_rate(struct clk *clk, unsigned long desired_rate)
+{
+ struct clcd_rate_tbl *tbls = clk->rate_config.tbls;
+ struct clcd_clk_config *config = clk->private_data;
+ unsigned long val, rate;
+ int i;
+
+ i = round_rate_index(clk, desired_rate, &rate);
+ if (i < 0)
+ return i;
+
+ val = readl(config->synth_reg) & ~(config->masks->div_factor_mask <<
+ config->masks->div_factor_shift);
+ val |= (tbls[i].div & config->masks->div_factor_mask) <<
+ config->masks->div_factor_shift;
+ writel(val, config->synth_reg);
+
+ clk->rate = rate;
+
+ return 0;
+}
+
+/*
+ * Used for clocks that always have value as the parent clock divided by a
+ * fixed divisor
+ */
+int follow_parent(struct clk *clk)
+{
+ unsigned int div_factor = (clk->div_factor < 1) ? 1 : clk->div_factor;
+
+ clk->rate = clk->pclk->rate/div_factor;
+ return 0;
+}
+
+/**
+ * recalc_root_clocks - recalculate and propagate all root clocks
+ *
+ * Recalculates all root clocks (clocks with no parent), which if the
+ * clock's .recalc is set correctly, should also propagate their rates.
+ */
+void recalc_root_clocks(void)
+{
+ struct clk *pclk;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&clocks_lock, flags);
+ list_for_each_entry(pclk, &root_clks, sibling) {
+ if (pclk->recalc) {
+ ret = pclk->recalc(pclk);
+ /*
+ * recalc will return error if clk out is not programmed
+ * In this case configure default clock.
+ */
+ if (ret && pclk->set_rate)
+ pclk->set_rate(pclk, 0);
+ }
+ propagate_rate(pclk, 1);
+ /* Enable clks enabled on init, in software view */
+ if (pclk->flags & ENABLED_ON_INIT)
+ do_clk_enable(pclk);
+ }
+ spin_unlock_irqrestore(&clocks_lock, flags);
+}
+
+void __init clk_init(void)
+{
+ recalc_root_clocks();
+}
+
+#ifdef CONFIG_DEBUG_FS
+/*
+ * debugfs support to trace clock tree hierarchy and attributes
+ */
+static struct dentry *clk_debugfs_root;
+static int clk_debugfs_register_one(struct clk *c)
+{
+ int err;
+ struct dentry *d, *child;
+ struct clk *pa = c->pclk;
+ char s[255];
+ char *p = s;
+
+ if (c) {
+ if (c->cl->con_id)
+ p += sprintf(p, "%s", c->cl->con_id);
+ if (c->cl->dev_id)
+ p += sprintf(p, "%s", c->cl->dev_id);
+ }
+ d = debugfs_create_dir(s, pa ? pa->dent : clk_debugfs_root);
+ if (!d)
+ return -ENOMEM;
+ c->dent = d;
+
+ d = debugfs_create_u32("usage_count", S_IRUGO, c->dent,
+ (u32 *)&c->usage_count);
+ if (!d) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
+ if (!d) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
+ if (!d) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+ return 0;
+
+err_out:
+ d = c->dent;
+ list_for_each_entry(child, &d->d_subdirs, d_u.d_child)
+ debugfs_remove(child);
+ debugfs_remove(c->dent);
+ return err;
+}
+
+static int clk_debugfs_register(struct clk *c)
+{
+ int err;
+ struct clk *pa = c->pclk;
+
+ if (pa && !pa->dent) {
+ err = clk_debugfs_register(pa);
+ if (err)
+ return err;
+ }
+
+ if (!c->dent) {
+ err = clk_debugfs_register_one(c);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static int __init clk_debugfs_init(void)
+{
+ struct clk *c;
+ struct dentry *d;
+ int err;
+
+ d = debugfs_create_dir("clock", NULL);
+ if (!d)
+ return -ENOMEM;
+ clk_debugfs_root = d;
+
+ list_for_each_entry(c, &clocks, node) {
+ err = clk_debugfs_register(c);
+ if (err)
+ goto err_out;
+ }
+ return 0;
+err_out:
+ debugfs_remove_recursive(clk_debugfs_root);
+ return err;
+}
+late_initcall(clk_debugfs_init);
+
+static int clk_debugfs_reparent(struct clk *c)
+{
+ debugfs_remove(c->dent);
+ return clk_debugfs_register_one(c);
+}
+#endif /* CONFIG_DEBUG_FS */
diff --git a/arch/arm/plat-spear/include/plat/clkdev.h b/arch/arm/plat-spear/include/plat/clkdev.h
new file mode 100644
index 00000000..a2d0112f
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/clkdev.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/plat-spear/include/plat/clkdev.h
+ *
+ * Clock Dev framework definitions for SPEAr platform
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_CLKDEV_H
+#define __PLAT_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif /* __PLAT_CLKDEV_H */
diff --git a/arch/arm/plat-spear/include/plat/clock.h b/arch/arm/plat-spear/include/plat/clock.h
new file mode 100644
index 00000000..0062bafe
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/clock.h
@@ -0,0 +1,249 @@
+/*
+ * arch/arm/plat-spear/include/plat/clock.h
+ *
+ * Clock framework definitions for SPEAr platform
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_CLOCK_H
+#define __PLAT_CLOCK_H
+
+#include <linux/list.h>
+#include <linux/clkdev.h>
+#include <linux/types.h>
+
+/* clk structure flags */
+#define ALWAYS_ENABLED (1 << 0) /* clock always enabled */
+#define RESET_TO_ENABLE (1 << 1) /* reset register bit to enable clk */
+#define ENABLED_ON_INIT (1 << 2) /* clocks enabled at init */
+
+/**
+ * struct clkops - clock operations
+ * @enable: pointer to clock enable function
+ * @disable: pointer to clock disable function
+ */
+struct clkops {
+ int (*enable) (struct clk *);
+ void (*disable) (struct clk *);
+};
+
+/**
+ * struct pclk_info - parents info
+ * @pclk: pointer to parent clk
+ * @pclk_val: value to be written for selecting this parent
+ */
+struct pclk_info {
+ struct clk *pclk;
+ u8 pclk_val;
+};
+
+/**
+ * struct pclk_sel - parents selection configuration
+ * @pclk_info: pointer to array of parent clock info
+ * @pclk_count: number of parents
+ * @pclk_sel_reg: register for selecting a parent
+ * @pclk_sel_mask: mask for selecting parent (can be used to clear bits also)
+ */
+struct pclk_sel {
+ struct pclk_info *pclk_info;
+ u8 pclk_count;
+ void __iomem *pclk_sel_reg;
+ unsigned int pclk_sel_mask;
+};
+
+/**
+ * struct rate_config - clk rate configurations
+ * @tbls: array of device specific clk rate tables, in ascending order of rates
+ * @count: size of tbls array
+ * @default_index: default setting when originally disabled
+ */
+struct rate_config {
+ void *tbls;
+ u8 count;
+ u8 default_index;
+};
+
+/**
+ * struct clk - clock structure
+ * @usage_count: num of users who enabled this clock
+ * @flags: flags for clock properties
+ * @rate: programmed clock rate in Hz
+ * @en_reg: clk enable/disable reg
+ * @en_reg_bit: clk enable/disable bit
+ * @ops: clk enable/disable ops - generic_clkops selected if NULL
+ * @recalc: pointer to clock rate recalculate function
+ * @set_rate: pointer to clock set rate function
+ * @calc_rate: pointer to clock get rate function for index
+ * @rate_config: rate configuration information, used by set_rate
+ * @div_factor: division factor to parent clock.
+ * @pclk: current parent clk
+ * @pclk_sel: pointer to parent selection structure
+ * @pclk_sel_shift: register shift for selecting parent of this clock
+ * @children: list for childrens or this clock
+ * @sibling: node for list of clocks having same parents
+ * @private_data: clock specific private data
+ * @node: list to maintain clocks linearly
+ * @cl: clocklook up associated with this clock
+ * @dent: object for debugfs
+ */
+struct clk {
+ unsigned int usage_count;
+ unsigned int flags;
+ unsigned long rate;
+ void __iomem *en_reg;
+ u8 en_reg_bit;
+ const struct clkops *ops;
+ int (*recalc) (struct clk *);
+ int (*set_rate) (struct clk *, unsigned long rate);
+ unsigned long (*calc_rate)(struct clk *, int index);
+ struct rate_config rate_config;
+ unsigned int div_factor;
+
+ struct clk *pclk;
+ struct pclk_sel *pclk_sel;
+ unsigned int pclk_sel_shift;
+
+ struct list_head children;
+ struct list_head sibling;
+ void *private_data;
+#ifdef CONFIG_DEBUG_FS
+ struct list_head node;
+ struct clk_lookup *cl;
+ struct dentry *dent;
+#endif
+};
+
+/* pll configuration structure */
+struct pll_clk_masks {
+ u32 mode_mask;
+ u32 mode_shift;
+
+ u32 norm_fdbk_m_mask;
+ u32 norm_fdbk_m_shift;
+ u32 dith_fdbk_m_mask;
+ u32 dith_fdbk_m_shift;
+ u32 div_p_mask;
+ u32 div_p_shift;
+ u32 div_n_mask;
+ u32 div_n_shift;
+};
+
+struct pll_clk_config {
+ void __iomem *mode_reg;
+ void __iomem *cfg_reg;
+ struct pll_clk_masks *masks;
+};
+
+/* pll clk rate config structure */
+struct pll_rate_tbl {
+ u8 mode;
+ u16 m;
+ u8 n;
+ u8 p;
+};
+
+/* ahb and apb bus configuration structure */
+struct bus_clk_masks {
+ u32 mask;
+ u32 shift;
+};
+
+struct bus_clk_config {
+ void __iomem *reg;
+ struct bus_clk_masks *masks;
+};
+
+/* ahb and apb clk bus rate config structure */
+struct bus_rate_tbl {
+ u8 div;
+};
+
+/* Aux clk configuration structure: applicable to UART and FIRDA */
+struct aux_clk_masks {
+ u32 eq_sel_mask;
+ u32 eq_sel_shift;
+ u32 eq1_mask;
+ u32 eq2_mask;
+ u32 xscale_sel_mask;
+ u32 xscale_sel_shift;
+ u32 yscale_sel_mask;
+ u32 yscale_sel_shift;
+};
+
+struct aux_clk_config {
+ void __iomem *synth_reg;
+ struct aux_clk_masks *masks;
+};
+
+/* aux clk rate config structure */
+struct aux_rate_tbl {
+ u16 xscale;
+ u16 yscale;
+ u8 eq;
+};
+
+/* GPT clk configuration structure */
+struct gpt_clk_masks {
+ u32 mscale_sel_mask;
+ u32 mscale_sel_shift;
+ u32 nscale_sel_mask;
+ u32 nscale_sel_shift;
+};
+
+struct gpt_clk_config {
+ void __iomem *synth_reg;
+ struct gpt_clk_masks *masks;
+};
+
+/* gpt clk rate config structure */
+struct gpt_rate_tbl {
+ u16 mscale;
+ u16 nscale;
+};
+
+/* clcd clk configuration structure */
+struct clcd_synth_masks {
+ u32 div_factor_mask;
+ u32 div_factor_shift;
+};
+
+struct clcd_clk_config {
+ void __iomem *synth_reg;
+ struct clcd_synth_masks *masks;
+};
+
+/* clcd clk rate config structure */
+struct clcd_rate_tbl {
+ u16 div;
+};
+
+/* platform specific clock functions */
+void __init clk_init(void);
+void clk_register(struct clk_lookup *cl);
+void recalc_root_clocks(void);
+
+/* clock recalc & set rate functions */
+int follow_parent(struct clk *clk);
+unsigned long pll_calc_rate(struct clk *clk, int index);
+int pll_clk_recalc(struct clk *clk);
+int pll_clk_set_rate(struct clk *clk, unsigned long desired_rate);
+unsigned long bus_calc_rate(struct clk *clk, int index);
+int bus_clk_recalc(struct clk *clk);
+int bus_clk_set_rate(struct clk *clk, unsigned long desired_rate);
+unsigned long gpt_calc_rate(struct clk *clk, int index);
+int gpt_clk_recalc(struct clk *clk);
+int gpt_clk_set_rate(struct clk *clk, unsigned long desired_rate);
+unsigned long aux_calc_rate(struct clk *clk, int index);
+int aux_clk_recalc(struct clk *clk);
+int aux_clk_set_rate(struct clk *clk, unsigned long desired_rate);
+unsigned long clcd_calc_rate(struct clk *clk, int index);
+int clcd_clk_recalc(struct clk *clk);
+int clcd_clk_set_rate(struct clk *clk, unsigned long desired_rate);
+
+#endif /* __PLAT_CLOCK_H */
diff --git a/arch/arm/plat-spear/include/plat/debug-macro.S b/arch/arm/plat-spear/include/plat/debug-macro.S
new file mode 100644
index 00000000..8501bbf2
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/debug-macro.S
@@ -0,0 +1,36 @@
+/*
+ * arch/arm/plat-spear/include/plat/debug-macro.S
+ *
+ * Debugging macro include header for spear platform
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/amba/serial.h>
+#include <mach/hardware.h>
+
+ .macro addruart, rp, rv
+ mov \rp, #SPEAR_DBG_UART_BASE @ Physical base
+ mov \rv, #VA_SPEAR_DBG_UART_BASE @ Virtual base
+ .endm
+
+ .macro senduart, rd, rx
+ strb \rd, [\rx, #UART01x_DR] @ ASC_TX_BUFFER
+ .endm
+
+ .macro waituart, rd, rx
+1001: ldr \rd, [\rx, #UART01x_FR] @ FLAG REGISTER
+ tst \rd, #UART01x_FR_TXFF @ TX_FULL
+ bne 1001b
+ .endm
+
+ .macro busyuart, rd, rx
+1002: ldr \rd, [\rx, #UART01x_FR] @ FLAG REGISTER
+ tst \rd, #UART011_FR_TXFE @ TX_EMPTY
+ beq 1002b
+ .endm
diff --git a/arch/arm/plat-spear/include/plat/gpio.h b/arch/arm/plat-spear/include/plat/gpio.h
new file mode 100644
index 00000000..b857c912
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/gpio.h
@@ -0,0 +1,24 @@
+/*
+ * arch/arm/plat-spear/include/plat/gpio.h
+ *
+ * GPIO macros for SPEAr platform
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_GPIO_H
+#define __PLAT_GPIO_H
+
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
+
+#endif /* __PLAT_GPIO_H */
diff --git a/arch/arm/plat-spear/include/plat/hardware.h b/arch/arm/plat-spear/include/plat/hardware.h
new file mode 100644
index 00000000..66d67722
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/hardware.h
@@ -0,0 +1,23 @@
+/*
+ * arch/arm/plat-spear/include/plat/hardware.h
+ *
+ * Hardware definitions for SPEAr
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_HARDWARE_H
+#define __PLAT_HARDWARE_H
+
+#ifndef __ASSEMBLY__
+#define IOMEM(x) ((void __iomem __force *)(x))
+#else
+#define IOMEM(x) (x)
+#endif
+
+#endif /* __PLAT_HARDWARE_H */
diff --git a/arch/arm/plat-spear/include/plat/io.h b/arch/arm/plat-spear/include/plat/io.h
new file mode 100644
index 00000000..4d4ba822
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/io.h
@@ -0,0 +1,22 @@
+/*
+ * arch/arm/plat-spear/include/plat/io.h
+ *
+ * IO definitions for SPEAr platform
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_IO_H
+#define __PLAT_IO_H
+
+#define IO_SPACE_LIMIT 0xFFFFFFFF
+
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
+
+#endif /* __PLAT_IO_H */
diff --git a/arch/arm/plat-spear/include/plat/keyboard.h b/arch/arm/plat-spear/include/plat/keyboard.h
new file mode 100644
index 00000000..68b5394f
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/keyboard.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2010 ST Microelectronics
+ * Rajeev Kumar<rajeev-dlh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_KEYBOARD_H
+#define __PLAT_KEYBOARD_H
+
+#include <linux/bitops.h>
+#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/types.h>
+
+#define DECLARE_KEYMAP(_name) \
+int _name[] = { \
+ KEY(0, 0, KEY_ESC), \
+ KEY(0, 1, KEY_1), \
+ KEY(0, 2, KEY_2), \
+ KEY(0, 3, KEY_3), \
+ KEY(0, 4, KEY_4), \
+ KEY(0, 5, KEY_5), \
+ KEY(0, 6, KEY_6), \
+ KEY(0, 7, KEY_7), \
+ KEY(0, 8, KEY_8), \
+ KEY(1, 0, KEY_9), \
+ KEY(1, 1, KEY_MINUS), \
+ KEY(1, 2, KEY_EQUAL), \
+ KEY(1, 3, KEY_BACKSPACE), \
+ KEY(1, 4, KEY_TAB), \
+ KEY(1, 5, KEY_Q), \
+ KEY(1, 6, KEY_W), \
+ KEY(1, 7, KEY_E), \
+ KEY(1, 8, KEY_R), \
+ KEY(2, 0, KEY_T), \
+ KEY(2, 1, KEY_Y), \
+ KEY(2, 2, KEY_U), \
+ KEY(2, 3, KEY_I), \
+ KEY(2, 4, KEY_O), \
+ KEY(2, 5, KEY_P), \
+ KEY(2, 6, KEY_LEFTBRACE), \
+ KEY(2, 7, KEY_RIGHTBRACE), \
+ KEY(2, 8, KEY_ENTER), \
+ KEY(3, 0, KEY_LEFTCTRL), \
+ KEY(3, 1, KEY_A), \
+ KEY(3, 2, KEY_S), \
+ KEY(3, 3, KEY_D), \
+ KEY(3, 4, KEY_F), \
+ KEY(3, 5, KEY_G), \
+ KEY(3, 6, KEY_H), \
+ KEY(3, 7, KEY_J), \
+ KEY(3, 8, KEY_K), \
+ KEY(4, 0, KEY_L), \
+ KEY(4, 1, KEY_SEMICOLON), \
+ KEY(4, 2, KEY_APOSTROPHE), \
+ KEY(4, 3, KEY_GRAVE), \
+ KEY(4, 4, KEY_LEFTSHIFT), \
+ KEY(4, 5, KEY_BACKSLASH), \
+ KEY(4, 6, KEY_Z), \
+ KEY(4, 7, KEY_X), \
+ KEY(4, 8, KEY_C), \
+ KEY(4, 0, KEY_L), \
+ KEY(4, 1, KEY_SEMICOLON), \
+ KEY(4, 2, KEY_APOSTROPHE), \
+ KEY(4, 3, KEY_GRAVE), \
+ KEY(4, 4, KEY_LEFTSHIFT), \
+ KEY(4, 5, KEY_BACKSLASH), \
+ KEY(4, 6, KEY_Z), \
+ KEY(4, 7, KEY_X), \
+ KEY(4, 8, KEY_C), \
+ KEY(4, 0, KEY_L), \
+ KEY(4, 1, KEY_SEMICOLON), \
+ KEY(4, 2, KEY_APOSTROPHE), \
+ KEY(4, 3, KEY_GRAVE), \
+ KEY(4, 4, KEY_LEFTSHIFT), \
+ KEY(4, 5, KEY_BACKSLASH), \
+ KEY(4, 6, KEY_Z), \
+ KEY(4, 7, KEY_X), \
+ KEY(4, 8, KEY_C), \
+ KEY(5, 0, KEY_V), \
+ KEY(5, 1, KEY_B), \
+ KEY(5, 2, KEY_N), \
+ KEY(5, 3, KEY_M), \
+ KEY(5, 4, KEY_COMMA), \
+ KEY(5, 5, KEY_DOT), \
+ KEY(5, 6, KEY_SLASH), \
+ KEY(5, 7, KEY_RIGHTSHIFT), \
+ KEY(5, 8, KEY_KPASTERISK), \
+ KEY(6, 0, KEY_LEFTALT), \
+ KEY(6, 1, KEY_SPACE), \
+ KEY(6, 2, KEY_CAPSLOCK), \
+ KEY(6, 3, KEY_F1), \
+ KEY(6, 4, KEY_F2), \
+ KEY(6, 5, KEY_F3), \
+ KEY(6, 6, KEY_F4), \
+ KEY(6, 7, KEY_F5), \
+ KEY(6, 8, KEY_F6), \
+ KEY(7, 0, KEY_F7), \
+ KEY(7, 1, KEY_F8), \
+ KEY(7, 2, KEY_F9), \
+ KEY(7, 3, KEY_F10), \
+ KEY(7, 4, KEY_NUMLOCK), \
+ KEY(7, 5, KEY_SCROLLLOCK), \
+ KEY(7, 6, KEY_KP7), \
+ KEY(7, 7, KEY_KP8), \
+ KEY(7, 8, KEY_KP9), \
+ KEY(8, 0, KEY_KPMINUS), \
+ KEY(8, 1, KEY_KP4), \
+ KEY(8, 2, KEY_KP5), \
+ KEY(8, 3, KEY_KP6), \
+ KEY(8, 4, KEY_KPPLUS), \
+ KEY(8, 5, KEY_KP1), \
+ KEY(8, 6, KEY_KP2), \
+ KEY(8, 7, KEY_KP3), \
+ KEY(8, 8, KEY_KP0), \
+}
+
+/**
+ * struct kbd_platform_data - spear keyboard platform data
+ * keymap: pointer to keymap data (table and size)
+ * rep: enables key autorepeat
+ *
+ * This structure is supposed to be used by platform code to supply
+ * keymaps to drivers that implement keyboards.
+ */
+struct kbd_platform_data {
+ const struct matrix_keymap_data *keymap;
+ bool rep;
+};
+
+/* This function is used to set platform data field of pdev->dev */
+static inline void
+kbd_set_plat_data(struct platform_device *pdev, struct kbd_platform_data *data)
+{
+ pdev->dev.platform_data = data;
+}
+
+#endif /* __PLAT_KEYBOARD_H */
diff --git a/arch/arm/plat-spear/include/plat/memory.h b/arch/arm/plat-spear/include/plat/memory.h
new file mode 100644
index 00000000..7e3599e1
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/memory.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/plat-spear/include/plat/memory.h
+ *
+ * Memory map for SPEAr platform
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_MEMORY_H
+#define __PLAT_MEMORY_H
+
+/* Physical DRAM offset */
+#define PLAT_PHYS_OFFSET UL(0x00000000)
+
+#endif /* __PLAT_MEMORY_H */
diff --git a/arch/arm/plat-spear/include/plat/padmux.h b/arch/arm/plat-spear/include/plat/padmux.h
new file mode 100644
index 00000000..877f3adc
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/padmux.h
@@ -0,0 +1,92 @@
+/*
+ * arch/arm/plat-spear/include/plat/padmux.h
+ *
+ * SPEAr platform specific gpio pads muxing file
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_PADMUX_H
+#define __PLAT_PADMUX_H
+
+#include <linux/types.h>
+
+/*
+ * struct pmx_reg: configuration structure for mode reg and mux reg
+ *
+ * offset: offset of mode reg
+ * mask: mask of mode reg
+ */
+struct pmx_reg {
+ u32 offset;
+ u32 mask;
+};
+
+/*
+ * struct pmx_dev_mode: configuration structure every group of modes of a device
+ *
+ * ids: all modes for this configuration
+ * mask: mask for supported mode
+ */
+struct pmx_dev_mode {
+ u32 ids;
+ u32 mask;
+};
+
+/*
+ * struct pmx_mode: mode definition structure
+ *
+ * name: mode name
+ * mask: mode mask
+ */
+struct pmx_mode {
+ char *name;
+ u32 id;
+ u32 mask;
+};
+
+/*
+ * struct pmx_dev: device definition structure
+ *
+ * name: device name
+ * modes: device configuration array for different modes supported
+ * mode_count: size of modes array
+ * is_active: is peripheral active/enabled
+ * enb_on_reset: if 1, mask bits to be cleared in reg otherwise to be set in reg
+ */
+struct pmx_dev {
+ char *name;
+ struct pmx_dev_mode *modes;
+ u8 mode_count;
+ bool is_active;
+ bool enb_on_reset;
+};
+
+/*
+ * struct pmx_driver: driver definition structure
+ *
+ * mode: mode to be set
+ * devs: array of pointer to pmx devices
+ * devs_count: ARRAY_SIZE of devs
+ * base: base address of soc config registers
+ * mode_reg: structure of mode config register
+ * mux_reg: structure of device mux config register
+ */
+struct pmx_driver {
+ struct pmx_mode *mode;
+ struct pmx_dev **devs;
+ u8 devs_count;
+ u32 *base;
+ struct pmx_reg mode_reg;
+ struct pmx_reg mux_reg;
+};
+
+/* pmx functions */
+int pmx_register(struct pmx_driver *driver);
+
+#endif /* __PLAT_PADMUX_H */
diff --git a/arch/arm/plat-spear/include/plat/shirq.h b/arch/arm/plat-spear/include/plat/shirq.h
new file mode 100644
index 00000000..03ed8b58
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/shirq.h
@@ -0,0 +1,73 @@
+/*
+ * arch/arm/plat-spear/include/plat/shirq.h
+ *
+ * SPEAr platform shared irq layer header file
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_SHIRQ_H
+#define __PLAT_SHIRQ_H
+
+#include <linux/irq.h>
+#include <linux/types.h>
+
+/*
+ * struct shirq_dev_config: shared irq device configuration
+ *
+ * virq: virtual irq number of device
+ * enb_mask: enable mask of device
+ * status_mask: status mask of device
+ * clear_mask: clear mask of device
+ */
+struct shirq_dev_config {
+ u32 virq;
+ u32 enb_mask;
+ u32 status_mask;
+ u32 clear_mask;
+};
+
+/*
+ * struct shirq_regs: shared irq register configuration
+ *
+ * base: base address of shared irq register
+ * enb_reg: enable register offset
+ * reset_to_enb: val 1 indicates, we need to clear bit for enabling interrupt
+ * status_reg: status register offset
+ * status_reg_mask: status register valid mask
+ * clear_reg: clear register offset
+ * reset_to_clear: val 1 indicates, we need to clear bit for clearing interrupt
+ */
+struct shirq_regs {
+ void __iomem *base;
+ u32 enb_reg;
+ u32 reset_to_enb;
+ u32 status_reg;
+ u32 status_reg_mask;
+ u32 clear_reg;
+ u32 reset_to_clear;
+};
+
+/*
+ * struct spear_shirq: shared irq structure
+ *
+ * irq: hardware irq number
+ * dev_config: array of device config structures which are using "irq" line
+ * dev_count: size of dev_config array
+ * regs: register configuration for shared irq block
+ */
+struct spear_shirq {
+ u32 irq;
+ struct shirq_dev_config *dev_config;
+ u32 dev_count;
+ struct shirq_regs regs;
+};
+
+int spear_shirq_register(struct spear_shirq *shirq);
+
+#endif /* __PLAT_SHIRQ_H */
diff --git a/arch/arm/plat-spear/include/plat/system.h b/arch/arm/plat-spear/include/plat/system.h
new file mode 100644
index 00000000..a235fa0c
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/system.h
@@ -0,0 +1,41 @@
+/*
+ * arch/arm/plat-spear/include/plat/system.h
+ *
+ * SPEAr platform specific architecture functions
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_SYSTEM_H
+#define __PLAT_SYSTEM_H
+
+#include <linux/io.h>
+#include <asm/hardware/sp810.h>
+#include <mach/hardware.h>
+
+static inline void arch_idle(void)
+{
+ /*
+ * This should do all the clock switching
+ * and wait for interrupt tricks
+ */
+ cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+ if (mode == 's') {
+ /* software reset, Jump into ROM at address 0 */
+ cpu_reset(0);
+ } else {
+ /* hardware reset, Use on-chip reset capability */
+ sysctl_soft_reset((void __iomem *)VA_SPEAR_SYS_CTRL_BASE);
+ }
+}
+
+#endif /* __PLAT_SYSTEM_H */
diff --git a/arch/arm/plat-spear/include/plat/timex.h b/arch/arm/plat-spear/include/plat/timex.h
new file mode 100644
index 00000000..914d09dd
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/timex.h
@@ -0,0 +1,19 @@
+/*
+ * arch/arm/plat-spear/include/plat/timex.h
+ *
+ * SPEAr platform specific timex definitions
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_TIMEX_H
+#define __PLAT_TIMEX_H
+
+#define CLOCK_TICK_RATE 48000000
+
+#endif /* __PLAT_TIMEX_H */
diff --git a/arch/arm/plat-spear/include/plat/uncompress.h b/arch/arm/plat-spear/include/plat/uncompress.h
new file mode 100644
index 00000000..1bf84527
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/uncompress.h
@@ -0,0 +1,43 @@
+/*
+ * arch/arm/plat-spear/include/plat/uncompress.h
+ *
+ * Serial port stubs for kernel decompress status messages
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/amba/serial.h>
+#include <mach/hardware.h>
+
+#ifndef __PLAT_UNCOMPRESS_H
+#define __PLAT_UNCOMPRESS_H
+/*
+ * This does not append a newline
+ */
+static inline void putc(int c)
+{
+ void __iomem *base = (void __iomem *)SPEAR_DBG_UART_BASE;
+
+ while (readl_relaxed(base + UART01x_FR) & UART01x_FR_TXFF)
+ barrier();
+
+ writel_relaxed(c, base + UART01x_DR);
+}
+
+static inline void flush(void)
+{
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
+
+#endif /* __PLAT_UNCOMPRESS_H */
diff --git a/arch/arm/plat-spear/include/plat/vmalloc.h b/arch/arm/plat-spear/include/plat/vmalloc.h
new file mode 100644
index 00000000..8c8b24d0
--- /dev/null
+++ b/arch/arm/plat-spear/include/plat/vmalloc.h
@@ -0,0 +1,19 @@
+/*
+ * arch/arm/plat-spear/include/plat/vmalloc.h
+ *
+ * Defining Vmalloc area for SPEAr platform
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __PLAT_VMALLOC_H
+#define __PLAT_VMALLOC_H
+
+#define VMALLOC_END 0xF0000000UL
+
+#endif /* __PLAT_VMALLOC_H */
diff --git a/arch/arm/plat-spear/padmux.c b/arch/arm/plat-spear/padmux.c
new file mode 100644
index 00000000..555eec6d
--- /dev/null
+++ b/arch/arm/plat-spear/padmux.c
@@ -0,0 +1,164 @@
+/*
+ * arch/arm/plat-spear/include/plat/padmux.c
+ *
+ * SPEAr platform specific gpio pads muxing source file
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <plat/padmux.h>
+
+/*
+ * struct pmx: pmx definition structure
+ *
+ * base: base address of configuration registers
+ * mode_reg: mode configurations
+ * mux_reg: muxing configurations
+ * active_mode: pointer to current active mode
+ */
+struct pmx {
+ u32 base;
+ struct pmx_reg mode_reg;
+ struct pmx_reg mux_reg;
+ struct pmx_mode *active_mode;
+};
+
+static struct pmx *pmx;
+
+/**
+ * pmx_mode_set - Enables an multiplexing mode
+ * @mode - pointer to pmx mode
+ *
+ * It will set mode of operation in hardware.
+ * Returns -ve on Err otherwise 0
+ */
+static int pmx_mode_set(struct pmx_mode *mode)
+{
+ u32 val;
+
+ if (!mode->name)
+ return -EFAULT;
+
+ pmx->active_mode = mode;
+
+ val = readl(pmx->base + pmx->mode_reg.offset);
+ val &= ~pmx->mode_reg.mask;
+ val |= mode->mask & pmx->mode_reg.mask;
+ writel(val, pmx->base + pmx->mode_reg.offset);
+
+ return 0;
+}
+
+/**
+ * pmx_devs_enable - Enables list of devices
+ * @devs - pointer to pmx device array
+ * @count - number of devices to enable
+ *
+ * It will enable pads for all required peripherals once and only once.
+ * If peripheral is not supported by current mode then request is rejected.
+ * Conflicts between peripherals are not handled and peripherals will be
+ * enabled in the order they are present in pmx_dev array.
+ * In case of conflicts last peripheral enabled will be present.
+ * Returns -ve on Err otherwise 0
+ */
+static int pmx_devs_enable(struct pmx_dev **devs, u8 count)
+{
+ u32 val, i, mask;
+
+ if (!count)
+ return -EINVAL;
+
+ val = readl(pmx->base + pmx->mux_reg.offset);
+ for (i = 0; i < count; i++) {
+ u8 j = 0;
+
+ if (!devs[i]->name || !devs[i]->modes) {
+ printk(KERN_ERR "padmux: dev name or modes is null\n");
+ continue;
+ }
+ /* check if peripheral exists in active mode */
+ if (pmx->active_mode) {
+ bool found = false;
+ for (j = 0; j < devs[i]->mode_count; j++) {
+ if (devs[i]->modes[j].ids &
+ pmx->active_mode->id) {
+ found = true;
+ break;
+ }
+ }
+ if (found == false) {
+ printk(KERN_ERR "%s device not available in %s"\
+ "mode\n", devs[i]->name,
+ pmx->active_mode->name);
+ continue;
+ }
+ }
+
+ /* enable peripheral */
+ mask = devs[i]->modes[j].mask & pmx->mux_reg.mask;
+ if (devs[i]->enb_on_reset)
+ val &= ~mask;
+ else
+ val |= mask;
+
+ devs[i]->is_active = true;
+ }
+ writel(val, pmx->base + pmx->mux_reg.offset);
+ kfree(pmx);
+
+ /* this will ensure that multiplexing can't be changed now */
+ pmx = (struct pmx *)-1;
+
+ return 0;
+}
+
+/**
+ * pmx_register - registers a platform requesting pad mux feature
+ * @driver - pointer to driver structure containing driver specific parameters
+ *
+ * Also this must be called only once. This will allocate memory for pmx
+ * structure, will call pmx_mode_set, will call pmx_devs_enable.
+ * Returns -ve on Err otherwise 0
+ */
+int pmx_register(struct pmx_driver *driver)
+{
+ int ret = 0;
+
+ if (pmx)
+ return -EPERM;
+ if (!driver->base || !driver->devs)
+ return -EFAULT;
+
+ pmx = kzalloc(sizeof(*pmx), GFP_KERNEL);
+ if (!pmx)
+ return -ENOMEM;
+
+ pmx->base = (u32)driver->base;
+ pmx->mode_reg.offset = driver->mode_reg.offset;
+ pmx->mode_reg.mask = driver->mode_reg.mask;
+ pmx->mux_reg.offset = driver->mux_reg.offset;
+ pmx->mux_reg.mask = driver->mux_reg.mask;
+
+ /* choose mode to enable */
+ if (driver->mode) {
+ ret = pmx_mode_set(driver->mode);
+ if (ret)
+ goto pmx_fail;
+ }
+ ret = pmx_devs_enable(driver->devs, driver->devs_count);
+ if (ret)
+ goto pmx_fail;
+
+ return 0;
+
+pmx_fail:
+ return ret;
+}
diff --git a/arch/arm/plat-spear/shirq.c b/arch/arm/plat-spear/shirq.c
new file mode 100644
index 00000000..961fb726
--- /dev/null
+++ b/arch/arm/plat-spear/shirq.c
@@ -0,0 +1,118 @@
+/*
+ * arch/arm/plat-spear/shirq.c
+ *
+ * SPEAr platform shared irq layer source file
+ *
+ * Copyright (C) 2009 ST Microelectronics
+ * Viresh Kumar<viresh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <plat/shirq.h>
+
+struct spear_shirq *shirq;
+static DEFINE_SPINLOCK(lock);
+
+static void shirq_irq_mask(struct irq_data *d)
+{
+ struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
+ u32 val, id = d->irq - shirq->dev_config[0].virq;
+ unsigned long flags;
+
+ if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1)
+ return;
+
+ spin_lock_irqsave(&lock, flags);
+ val = readl(shirq->regs.base + shirq->regs.enb_reg);
+ if (shirq->regs.reset_to_enb)
+ val |= shirq->dev_config[id].enb_mask;
+ else
+ val &= ~(shirq->dev_config[id].enb_mask);
+ writel(val, shirq->regs.base + shirq->regs.enb_reg);
+ spin_unlock_irqrestore(&lock, flags);
+}
+
+static void shirq_irq_unmask(struct irq_data *d)
+{
+ struct spear_shirq *shirq = irq_data_get_irq_chip_data(d);
+ u32 val, id = d->irq - shirq->dev_config[0].virq;
+ unsigned long flags;
+
+ if ((shirq->regs.enb_reg == -1) || shirq->dev_config[id].enb_mask == -1)
+ return;
+
+ spin_lock_irqsave(&lock, flags);
+ val = readl(shirq->regs.base + shirq->regs.enb_reg);
+ if (shirq->regs.reset_to_enb)
+ val &= ~(shirq->dev_config[id].enb_mask);
+ else
+ val |= shirq->dev_config[id].enb_mask;
+ writel(val, shirq->regs.base + shirq->regs.enb_reg);
+ spin_unlock_irqrestore(&lock, flags);
+}
+
+static struct irq_chip shirq_chip = {
+ .name = "spear_shirq",
+ .irq_ack = shirq_irq_mask,
+ .irq_mask = shirq_irq_mask,
+ .irq_unmask = shirq_irq_unmask,
+};
+
+static void shirq_handler(unsigned irq, struct irq_desc *desc)
+{
+ u32 i, val, mask;
+ struct spear_shirq *shirq = irq_get_handler_data(irq);
+
+ desc->irq_data.chip->irq_ack(&desc->irq_data);
+ while ((val = readl(shirq->regs.base + shirq->regs.status_reg) &
+ shirq->regs.status_reg_mask)) {
+ for (i = 0; (i < shirq->dev_count) && val; i++) {
+ if (!(shirq->dev_config[i].status_mask & val))
+ continue;
+
+ generic_handle_irq(shirq->dev_config[i].virq);
+
+ /* clear interrupt */
+ val &= ~shirq->dev_config[i].status_mask;
+ if ((shirq->regs.clear_reg == -1) ||
+ shirq->dev_config[i].clear_mask == -1)
+ continue;
+ mask = readl(shirq->regs.base + shirq->regs.clear_reg);
+ if (shirq->regs.reset_to_clear)
+ mask &= ~shirq->dev_config[i].clear_mask;
+ else
+ mask |= shirq->dev_config[i].clear_mask;
+ writel(mask, shirq->regs.base + shirq->regs.clear_reg);
+ }
+ }
+ desc->irq_data.chip->irq_unmask(&desc->irq_data);
+}
+
+int spear_shirq_register(struct spear_shirq *shirq)
+{
+ int i;
+
+ if (!shirq || !shirq->dev_config || !shirq->regs.base)
+ return -EFAULT;
+
+ if (!shirq->dev_count)
+ return -EINVAL;
+
+ irq_set_chained_handler(shirq->irq, shirq_handler);
+ for (i = 0; i < shirq->dev_count; i++) {
+ irq_set_chip_and_handler(shirq->dev_config[i].virq,
+ &shirq_chip, handle_simple_irq);
+ set_irq_flags(shirq->dev_config[i].virq, IRQF_VALID);
+ irq_set_chip_data(shirq->dev_config[i].virq, shirq);
+ }
+
+ irq_set_handler_data(shirq->irq, shirq);
+ return 0;
+}
diff --git a/arch/arm/plat-spear/time.c b/arch/arm/plat-spear/time.c
new file mode 100644
index 00000000..0c77e429
--- /dev/null
+++ b/arch/arm/plat-spear/time.c
@@ -0,0 +1,237 @@
+/*
+ * arch/arm/plat-spear/time.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Shiraz Hashim<shiraz.hashim@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/irq.h>
+#include <asm/mach/time.h>
+#include <mach/generic.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+/*
+ * We would use TIMER0 and TIMER1 as clockevent and clocksource.
+ * Timer0 and Timer1 both belong to same gpt block in cpu subbsystem. Further
+ * they share same functional clock. Any change in one's functional clock will
+ * also affect other timer.
+ */
+
+#define CLKEVT 0 /* gpt0, channel0 as clockevent */
+#define CLKSRC 1 /* gpt0, channel1 as clocksource */
+
+/* Register offsets, x is channel number */
+#define CR(x) ((x) * 0x80 + 0x80)
+#define IR(x) ((x) * 0x80 + 0x84)
+#define LOAD(x) ((x) * 0x80 + 0x88)
+#define COUNT(x) ((x) * 0x80 + 0x8C)
+
+/* Reg bit definitions */
+#define CTRL_INT_ENABLE 0x0100
+#define CTRL_ENABLE 0x0020
+#define CTRL_ONE_SHOT 0x0010
+
+#define CTRL_PRESCALER1 0x0
+#define CTRL_PRESCALER2 0x1
+#define CTRL_PRESCALER4 0x2
+#define CTRL_PRESCALER8 0x3
+#define CTRL_PRESCALER16 0x4
+#define CTRL_PRESCALER32 0x5
+#define CTRL_PRESCALER64 0x6
+#define CTRL_PRESCALER128 0x7
+#define CTRL_PRESCALER256 0x8
+
+#define INT_STATUS 0x1
+
+/*
+ * Minimum clocksource/clockevent timer range in seconds
+ */
+#define SPEAR_MIN_RANGE 4
+
+static __iomem void *gpt_base;
+static struct clk *gpt_clk;
+
+static void clockevent_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk_event_dev);
+static int clockevent_next_event(unsigned long evt,
+ struct clock_event_device *clk_event_dev);
+
+static void spear_clocksource_init(void)
+{
+ u32 tick_rate;
+ u16 val;
+
+ /* program the prescaler (/256)*/
+ writew(CTRL_PRESCALER256, gpt_base + CR(CLKSRC));
+
+ /* find out actual clock driving Timer */
+ tick_rate = clk_get_rate(gpt_clk);
+ tick_rate >>= CTRL_PRESCALER256;
+
+ writew(0xFFFF, gpt_base + LOAD(CLKSRC));
+
+ val = readw(gpt_base + CR(CLKSRC));
+ val &= ~CTRL_ONE_SHOT; /* autoreload mode */
+ val |= CTRL_ENABLE ;
+ writew(val, gpt_base + CR(CLKSRC));
+
+ /* register the clocksource */
+ clocksource_mmio_init(gpt_base + COUNT(CLKSRC), "tmr1", tick_rate,
+ 200, 16, clocksource_mmio_readw_up);
+}
+
+static struct clock_event_device clkevt = {
+ .name = "tmr0",
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = clockevent_set_mode,
+ .set_next_event = clockevent_next_event,
+ .shift = 0, /* to be computed */
+};
+
+static void clockevent_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk_event_dev)
+{
+ u32 period;
+ u16 val;
+
+ /* stop the timer */
+ val = readw(gpt_base + CR(CLKEVT));
+ val &= ~CTRL_ENABLE;
+ writew(val, gpt_base + CR(CLKEVT));
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ period = clk_get_rate(gpt_clk) / HZ;
+ period >>= CTRL_PRESCALER16;
+ writew(period, gpt_base + LOAD(CLKEVT));
+
+ val = readw(gpt_base + CR(CLKEVT));
+ val &= ~CTRL_ONE_SHOT;
+ val |= CTRL_ENABLE | CTRL_INT_ENABLE;
+ writew(val, gpt_base + CR(CLKEVT));
+
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ val = readw(gpt_base + CR(CLKEVT));
+ val |= CTRL_ONE_SHOT;
+ writew(val, gpt_base + CR(CLKEVT));
+
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_RESUME:
+
+ break;
+ default:
+ pr_err("Invalid mode requested\n");
+ break;
+ }
+}
+
+static int clockevent_next_event(unsigned long cycles,
+ struct clock_event_device *clk_event_dev)
+{
+ u16 val;
+
+ writew(cycles, gpt_base + LOAD(CLKEVT));
+
+ val = readw(gpt_base + CR(CLKEVT));
+ val |= CTRL_ENABLE | CTRL_INT_ENABLE;
+ writew(val, gpt_base + CR(CLKEVT));
+
+ return 0;
+}
+
+static irqreturn_t spear_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = &clkevt;
+
+ writew(INT_STATUS, gpt_base + IR(CLKEVT));
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction spear_timer_irq = {
+ .name = "timer",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = spear_timer_interrupt
+};
+
+static void __init spear_clockevent_init(void)
+{
+ u32 tick_rate;
+
+ /* program the prescaler */
+ writew(CTRL_PRESCALER16, gpt_base + CR(CLKEVT));
+
+ tick_rate = clk_get_rate(gpt_clk);
+ tick_rate >>= CTRL_PRESCALER16;
+
+ clockevents_calc_mult_shift(&clkevt, tick_rate, SPEAR_MIN_RANGE);
+
+ clkevt.max_delta_ns = clockevent_delta2ns(0xfff0,
+ &clkevt);
+ clkevt.min_delta_ns = clockevent_delta2ns(3, &clkevt);
+
+ clkevt.cpumask = cpumask_of(0);
+
+ clockevents_register_device(&clkevt);
+
+ setup_irq(SPEAR_GPT0_CHAN0_IRQ, &spear_timer_irq);
+}
+
+void __init spear_setup_timer(void)
+{
+ int ret;
+
+ if (!request_mem_region(SPEAR_GPT0_BASE, SZ_1K, "gpt0")) {
+ pr_err("%s:cannot get IO addr\n", __func__);
+ return;
+ }
+
+ gpt_base = (void __iomem *)ioremap(SPEAR_GPT0_BASE, SZ_1K);
+ if (!gpt_base) {
+ pr_err("%s:ioremap failed for gpt\n", __func__);
+ goto err_mem;
+ }
+
+ gpt_clk = clk_get_sys("gpt0", NULL);
+ if (!gpt_clk) {
+ pr_err("%s:couldn't get clk for gpt\n", __func__);
+ goto err_iomap;
+ }
+
+ ret = clk_enable(gpt_clk);
+ if (ret < 0) {
+ pr_err("%s:couldn't enable gpt clock\n", __func__);
+ goto err_clk;
+ }
+
+ spear_clockevent_init();
+ spear_clocksource_init();
+
+ return;
+
+err_clk:
+ clk_put(gpt_clk);
+err_iomap:
+ iounmap(gpt_base);
+err_mem:
+ release_mem_region(SPEAR_GPT0_BASE, SZ_1K);
+}