aboutsummaryrefslogtreecommitdiff
path: root/drivers/sh
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/sh')
-rw-r--r--drivers/sh/Makefile13
-rw-r--r--drivers/sh/clk/core.c243
-rw-r--r--drivers/sh/clk/cpg.c525
-rw-r--r--drivers/sh/intc/Kconfig6
-rw-r--r--drivers/sh/intc/Makefile2
-rw-r--r--drivers/sh/intc/access.c45
-rw-r--r--drivers/sh/intc/balancing.c2
-rw-r--r--drivers/sh/intc/chip.c53
-rw-r--r--drivers/sh/intc/core.c193
-rw-r--r--drivers/sh/intc/dynamic.c64
-rw-r--r--drivers/sh/intc/handle.c7
-rw-r--r--drivers/sh/intc/internals.h26
-rw-r--r--drivers/sh/intc/irqdomain.c68
-rw-r--r--drivers/sh/intc/userimask.c17
-rw-r--r--drivers/sh/intc/virq.c26
-rw-r--r--drivers/sh/maple/maple.c1
-rw-r--r--drivers/sh/pfc.c611
-rw-r--r--drivers/sh/pm_runtime.c111
18 files changed, 809 insertions, 1204 deletions
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile
index 24e6cec0ae8..788ed9b59b4 100644
--- a/drivers/sh/Makefile
+++ b/drivers/sh/Makefile
@@ -1,9 +1,12 @@
#
# Makefile for the SuperH specific drivers.
#
-obj-y := intc/
+obj-$(CONFIG_SUPERH) += intc/
+obj-$(CONFIG_ARCH_SHMOBILE_LEGACY) += intc/
+ifneq ($(CONFIG_COMMON_CLK),y)
+obj-$(CONFIG_HAVE_CLK) += clk/
+endif
+obj-$(CONFIG_MAPLE) += maple/
+obj-$(CONFIG_SUPERHYWAY) += superhyway/
-obj-$(CONFIG_HAVE_CLK) += clk/
-obj-$(CONFIG_MAPLE) += maple/
-obj-$(CONFIG_SUPERHYWAY) += superhyway/
-obj-$(CONFIG_GENERIC_GPIO) += pfc.o
+obj-y += pm_runtime.o
diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c
index 09615b51d59..be56b22ca94 100644
--- a/drivers/sh/clk/core.c
+++ b/drivers/sh/clk/core.c
@@ -21,12 +21,10 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/list.h>
-#include <linux/kobject.h>
-#include <linux/sysdev.h>
+#include <linux/syscore_ops.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/io.h>
-#include <linux/debugfs.h>
#include <linux/cpufreq.h>
#include <linux/clk.h>
#include <linux/sh_clk.h>
@@ -35,6 +33,9 @@ static LIST_HEAD(clock_list);
static DEFINE_SPINLOCK(clock_lock);
static DEFINE_MUTEX(clock_list_sem);
+/* clock disable operations are not passed on to hardware during boot */
+static int allow_disable;
+
void clk_rate_table_build(struct clk *clk,
struct cpufreq_frequency_table *freq_table,
int nr_freqs,
@@ -62,12 +63,12 @@ void clk_rate_table_build(struct clk *clk,
else
freq = clk->parent->rate * mult / div;
- freq_table[i].index = i;
+ freq_table[i].driver_data = i;
freq_table[i].frequency = freq;
}
/* Termination entry */
- freq_table[i].index = i;
+ freq_table[i].driver_data = i;
freq_table[i].frequency = CPUFREQ_TABLE_END;
}
@@ -171,21 +172,35 @@ long clk_rate_div_range_round(struct clk *clk, unsigned int div_min,
return clk_rate_round_helper(&div_range_round);
}
+static long clk_rate_mult_range_iter(unsigned int pos,
+ struct clk_rate_round_data *rounder)
+{
+ return clk_get_rate(rounder->arg) * pos;
+}
+
+long clk_rate_mult_range_round(struct clk *clk, unsigned int mult_min,
+ unsigned int mult_max, unsigned long rate)
+{
+ struct clk_rate_round_data mult_range_round = {
+ .min = mult_min,
+ .max = mult_max,
+ .func = clk_rate_mult_range_iter,
+ .arg = clk_get_parent(clk),
+ .rate = rate,
+ };
+
+ return clk_rate_round_helper(&mult_range_round);
+}
+
int clk_rate_table_find(struct clk *clk,
struct cpufreq_frequency_table *freq_table,
unsigned long rate)
{
- int i;
+ struct cpufreq_frequency_table *pos;
- for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
- unsigned long freq = freq_table[i].frequency;
-
- if (freq == CPUFREQ_ENTRY_INVALID)
- continue;
-
- if (freq == rate)
- return i;
- }
+ cpufreq_for_each_valid_entry(pos, freq_table)
+ if (pos->frequency == rate)
+ return pos - freq_table;
return -ENOENT;
}
@@ -203,9 +218,6 @@ int clk_reparent(struct clk *child, struct clk *parent)
list_add(&child->sibling, &parent->children);
child->parent = parent;
- /* now do the debugfs renaming to reattach the child
- to the proper parent */
-
return 0;
}
@@ -229,7 +241,7 @@ static void __clk_disable(struct clk *clk)
return;
if (!(--clk->usecount)) {
- if (likely(clk->ops && clk->ops->disable))
+ if (likely(allow_disable && clk->ops && clk->ops->disable))
clk->ops->disable(clk);
if (likely(clk->parent))
__clk_disable(clk->parent);
@@ -337,7 +349,7 @@ static int clk_establish_mapping(struct clk *clk)
*/
if (!clk->parent) {
clk->mapping = &dummy_mapping;
- return 0;
+ goto out;
}
/*
@@ -366,6 +378,9 @@ static int clk_establish_mapping(struct clk *clk)
}
clk->mapping = mapping;
+out:
+ clk->mapped_reg = clk->mapping->base;
+ clk->mapped_reg += (phys_addr_t)clk->enable_reg - clk->mapping->phys;
return 0;
}
@@ -384,17 +399,19 @@ static void clk_teardown_mapping(struct clk *clk)
/* Nothing to do */
if (mapping == &dummy_mapping)
- return;
+ goto out;
kref_put(&mapping->ref, clk_destroy_mapping);
clk->mapping = NULL;
+out:
+ clk->mapped_reg = NULL;
}
int clk_register(struct clk *clk)
{
int ret;
- if (clk == NULL || IS_ERR(clk))
+ if (IS_ERR_OR_NULL(clk))
return -EINVAL;
/*
@@ -418,8 +435,11 @@ int clk_register(struct clk *clk)
list_add(&clk->sibling, &root_clks);
list_add(&clk->node, &clock_list);
+
+#ifdef CONFIG_SH_CLK_CPG_LEGACY
if (clk->ops && clk->ops->init)
clk->ops->init(clk);
+#endif
out_unlock:
mutex_unlock(&clock_list_sem);
@@ -455,19 +475,13 @@ EXPORT_SYMBOL_GPL(clk_get_rate);
int clk_set_rate(struct clk *clk, unsigned long rate)
{
- return clk_set_rate_ex(clk, rate, 0);
-}
-EXPORT_SYMBOL_GPL(clk_set_rate);
-
-int clk_set_rate_ex(struct clk *clk, unsigned long rate, int algo_id)
-{
int ret = -EOPNOTSUPP;
unsigned long flags;
spin_lock_irqsave(&clock_lock, flags);
if (likely(clk->ops && clk->ops->set_rate)) {
- ret = clk->ops->set_rate(clk, rate, algo_id);
+ ret = clk->ops->set_rate(clk, rate);
if (ret != 0)
goto out_unlock;
} else {
@@ -485,7 +499,7 @@ out_unlock:
return ret;
}
-EXPORT_SYMBOL_GPL(clk_set_rate_ex);
+EXPORT_SYMBOL_GPL(clk_set_rate);
int clk_set_parent(struct clk *clk, struct clk *parent)
{
@@ -555,11 +569,7 @@ long clk_round_parent(struct clk *clk, unsigned long target,
return abs(target - *best_freq);
}
- for (freq = parent->freq_table; freq->frequency != CPUFREQ_TABLE_END;
- freq++) {
- if (freq->frequency == CPUFREQ_ENTRY_INVALID)
- continue;
-
+ cpufreq_for_each_valid_entry(freq, parent->freq_table) {
if (unlikely(freq->frequency / target <= div_min - 1)) {
unsigned long freq_max;
@@ -571,7 +581,7 @@ long clk_round_parent(struct clk *clk, unsigned long target,
*best_freq = freq_max;
}
- pr_debug("too low freq %lu, error %lu\n", freq->frequency,
+ pr_debug("too low freq %u, error %lu\n", freq->frequency,
target - freq_max);
if (!error)
@@ -591,7 +601,7 @@ long clk_round_parent(struct clk *clk, unsigned long target,
*best_freq = freq_min;
}
- pr_debug("too high freq %lu, error %lu\n", freq->frequency,
+ pr_debug("too high freq %u, error %lu\n", freq->frequency,
freq_min - target);
if (!error)
@@ -634,153 +644,56 @@ long clk_round_parent(struct clk *clk, unsigned long target,
EXPORT_SYMBOL_GPL(clk_round_parent);
#ifdef CONFIG_PM
-static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state)
+static void clks_core_resume(void)
{
- static pm_message_t prev_state;
struct clk *clkp;
- switch (state.event) {
- case PM_EVENT_ON:
- /* Resumeing from hibernation */
- if (prev_state.event != PM_EVENT_FREEZE)
- break;
-
- list_for_each_entry(clkp, &clock_list, node) {
- if (likely(clkp->ops)) {
- unsigned long rate = clkp->rate;
-
- if (likely(clkp->ops->set_parent))
- clkp->ops->set_parent(clkp,
- clkp->parent);
- if (likely(clkp->ops->set_rate))
- clkp->ops->set_rate(clkp,
- rate, NO_CHANGE);
- else if (likely(clkp->ops->recalc))
- clkp->rate = clkp->ops->recalc(clkp);
- }
+ list_for_each_entry(clkp, &clock_list, node) {
+ if (likely(clkp->usecount && clkp->ops)) {
+ unsigned long rate = clkp->rate;
+
+ if (likely(clkp->ops->set_parent))
+ clkp->ops->set_parent(clkp,
+ clkp->parent);
+ if (likely(clkp->ops->set_rate))
+ clkp->ops->set_rate(clkp, rate);
+ else if (likely(clkp->ops->recalc))
+ clkp->rate = clkp->ops->recalc(clkp);
}
- break;
- case PM_EVENT_FREEZE:
- break;
- case PM_EVENT_SUSPEND:
- break;
}
-
- prev_state = state;
- return 0;
-}
-
-static int clks_sysdev_resume(struct sys_device *dev)
-{
- return clks_sysdev_suspend(dev, PMSG_ON);
}
-static struct sysdev_class clks_sysdev_class = {
- .name = "clks",
-};
-
-static struct sysdev_driver clks_sysdev_driver = {
- .suspend = clks_sysdev_suspend,
- .resume = clks_sysdev_resume,
-};
-
-static struct sys_device clks_sysdev_dev = {
- .cls = &clks_sysdev_class,
+static struct syscore_ops clks_syscore_ops = {
+ .resume = clks_core_resume,
};
-static int __init clk_sysdev_init(void)
+static int __init clk_syscore_init(void)
{
- sysdev_class_register(&clks_sysdev_class);
- sysdev_driver_register(&clks_sysdev_class, &clks_sysdev_driver);
- sysdev_register(&clks_sysdev_dev);
+ register_syscore_ops(&clks_syscore_ops);
return 0;
}
-subsys_initcall(clk_sysdev_init);
+subsys_initcall(clk_syscore_init);
#endif
-/*
- * 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, *child_tmp;
- struct clk *pa = c->parent;
- char s[255];
- char *p = s;
-
- p += sprintf(p, "%p", c);
- d = debugfs_create_dir(s, pa ? pa->dentry : clk_debugfs_root);
- if (!d)
- return -ENOMEM;
- c->dentry = d;
-
- d = debugfs_create_u8("usecount", S_IRUGO, c->dentry, (u8 *)&c->usecount);
- if (!d) {
- err = -ENOMEM;
- goto err_out;
- }
- d = debugfs_create_u32("rate", S_IRUGO, c->dentry, (u32 *)&c->rate);
- if (!d) {
- err = -ENOMEM;
- goto err_out;
- }
- d = debugfs_create_x32("flags", S_IRUGO, c->dentry, (u32 *)&c->flags);
- if (!d) {
- err = -ENOMEM;
- goto err_out;
- }
- return 0;
-
-err_out:
- d = c->dentry;
- list_for_each_entry_safe(child, child_tmp, &d->d_subdirs, d_u.d_child)
- debugfs_remove(child);
- debugfs_remove(c->dentry);
- return err;
-}
-
-static int clk_debugfs_register(struct clk *c)
+static int __init clk_late_init(void)
{
- int err;
- struct clk *pa = c->parent;
-
- if (pa && !pa->dentry) {
- err = clk_debugfs_register(pa);
- if (err)
- return err;
- }
+ unsigned long flags;
+ struct clk *clk;
- if (!c->dentry) {
- err = clk_debugfs_register_one(c);
- if (err)
- return err;
- }
- return 0;
-}
+ /* disable all clocks with zero use count */
+ mutex_lock(&clock_list_sem);
+ spin_lock_irqsave(&clock_lock, flags);
-static int __init clk_debugfs_init(void)
-{
- struct clk *c;
- struct dentry *d;
- int err;
+ list_for_each_entry(clk, &clock_list, node)
+ if (!clk->usecount && clk->ops && clk->ops->disable)
+ clk->ops->disable(clk);
- d = debugfs_create_dir("clock", NULL);
- if (!d)
- return -ENOMEM;
- clk_debugfs_root = d;
+ /* from now on allow clock disable operations */
+ allow_disable = 1;
- list_for_each_entry(c, &clock_list, node) {
- err = clk_debugfs_register(c);
- if (err)
- goto err_out;
- }
+ spin_unlock_irqrestore(&clock_lock, flags);
+ mutex_unlock(&clock_list_sem);
return 0;
-err_out:
- debugfs_remove_recursive(clk_debugfs_root);
- return err;
}
-late_initcall(clk_debugfs_init);
+late_initcall(clk_late_init);
diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c
index 3aea5f0ceb0..7442bc13005 100644
--- a/drivers/sh/clk/cpg.c
+++ b/drivers/sh/clk/cpg.c
@@ -2,6 +2,7 @@
* Helper routines for SuperH Clock Pulse Generator blocks (CPG).
*
* Copyright (C) 2010 Magnus Damm
+ * Copyright (C) 2010 - 2012 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -13,26 +14,84 @@
#include <linux/io.h>
#include <linux/sh_clk.h>
-static int sh_clk_mstp32_enable(struct clk *clk)
+#define CPG_CKSTP_BIT BIT(8)
+
+static unsigned int sh_clk_read(struct clk *clk)
+{
+ if (clk->flags & CLK_ENABLE_REG_8BIT)
+ return ioread8(clk->mapped_reg);
+ else if (clk->flags & CLK_ENABLE_REG_16BIT)
+ return ioread16(clk->mapped_reg);
+
+ return ioread32(clk->mapped_reg);
+}
+
+static void sh_clk_write(int value, struct clk *clk)
+{
+ if (clk->flags & CLK_ENABLE_REG_8BIT)
+ iowrite8(value, clk->mapped_reg);
+ else if (clk->flags & CLK_ENABLE_REG_16BIT)
+ iowrite16(value, clk->mapped_reg);
+ else
+ iowrite32(value, clk->mapped_reg);
+}
+
+static unsigned int r8(const void __iomem *addr)
+{
+ return ioread8(addr);
+}
+
+static unsigned int r16(const void __iomem *addr)
{
- __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit),
- clk->enable_reg);
+ return ioread16(addr);
+}
+
+static unsigned int r32(const void __iomem *addr)
+{
+ return ioread32(addr);
+}
+
+static int sh_clk_mstp_enable(struct clk *clk)
+{
+ sh_clk_write(sh_clk_read(clk) & ~(1 << clk->enable_bit), clk);
+ if (clk->status_reg) {
+ unsigned int (*read)(const void __iomem *addr);
+ int i;
+ void __iomem *mapped_status = (phys_addr_t)clk->status_reg -
+ (phys_addr_t)clk->enable_reg + clk->mapped_reg;
+
+ if (clk->flags & CLK_ENABLE_REG_8BIT)
+ read = r8;
+ else if (clk->flags & CLK_ENABLE_REG_16BIT)
+ read = r16;
+ else
+ read = r32;
+
+ for (i = 1000;
+ (read(mapped_status) & (1 << clk->enable_bit)) && i;
+ i--)
+ cpu_relax();
+ if (!i) {
+ pr_err("cpg: failed to enable %p[%d]\n",
+ clk->enable_reg, clk->enable_bit);
+ return -ETIMEDOUT;
+ }
+ }
return 0;
}
-static void sh_clk_mstp32_disable(struct clk *clk)
+static void sh_clk_mstp_disable(struct clk *clk)
{
- __raw_writel(__raw_readl(clk->enable_reg) | (1 << clk->enable_bit),
- clk->enable_reg);
+ sh_clk_write(sh_clk_read(clk) | (1 << clk->enable_bit), clk);
}
-static struct clk_ops sh_clk_mstp32_clk_ops = {
- .enable = sh_clk_mstp32_enable,
- .disable = sh_clk_mstp32_disable,
+static struct sh_clk_ops sh_clk_mstp_clk_ops = {
+ .enable = sh_clk_mstp_enable,
+ .disable = sh_clk_mstp_disable,
.recalc = followparent_recalc,
};
-int __init sh_clk_mstp32_register(struct clk *clks, int nr)
+int __init sh_clk_mstp_register(struct clk *clks, int nr)
{
struct clk *clkp;
int ret = 0;
@@ -40,79 +99,50 @@ int __init sh_clk_mstp32_register(struct clk *clks, int nr)
for (k = 0; !ret && (k < nr); k++) {
clkp = clks + k;
- clkp->ops = &sh_clk_mstp32_clk_ops;
+ clkp->ops = &sh_clk_mstp_clk_ops;
ret |= clk_register(clkp);
}
return ret;
}
-static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate)
+/*
+ * Div/mult table lookup helpers
+ */
+static inline struct clk_div_table *clk_to_div_table(struct clk *clk)
{
- return clk_rate_table_round(clk, clk->freq_table, rate);
+ return clk->priv;
}
-static int sh_clk_div6_divisors[64] = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
- 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
- 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
-};
+static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk)
+{
+ return clk_to_div_table(clk)->div_mult_table;
+}
-static struct clk_div_mult_table sh_clk_div6_table = {
- .divisors = sh_clk_div6_divisors,
- .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors),
-};
+/*
+ * Common div ops
+ */
+static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate)
+{
+ return clk_rate_table_round(clk, clk->freq_table, rate);
+}
-static unsigned long sh_clk_div6_recalc(struct clk *clk)
+static unsigned long sh_clk_div_recalc(struct clk *clk)
{
- struct clk_div_mult_table *table = &sh_clk_div6_table;
+ struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
unsigned int idx;
clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
- table, NULL);
+ table, clk->arch_flags ? &clk->arch_flags : NULL);
- idx = __raw_readl(clk->enable_reg) & 0x003f;
+ idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask;
return clk->freq_table[idx].frequency;
}
-static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
-{
- struct clk_div_mult_table *table = &sh_clk_div6_table;
- u32 value;
- int ret, i;
-
- if (!clk->parent_table || !clk->parent_num)
- return -EINVAL;
-
- /* Search the parent */
- for (i = 0; i < clk->parent_num; i++)
- if (clk->parent_table[i] == parent)
- break;
-
- if (i == clk->parent_num)
- return -ENODEV;
-
- ret = clk_reparent(clk, parent);
- if (ret < 0)
- return ret;
-
- value = __raw_readl(clk->enable_reg) &
- ~(((1 << clk->src_width) - 1) << clk->src_shift);
-
- __raw_writel(value | (i << clk->src_shift), clk->enable_reg);
-
- /* Rebuild the frequency table */
- clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
- table, &clk->arch_flags);
-
- return 0;
-}
-
-static int sh_clk_div6_set_rate(struct clk *clk,
- unsigned long rate, int algo_id)
+static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate)
{
+ struct clk_div_table *dt = clk_to_div_table(clk);
unsigned long value;
int idx;
@@ -120,60 +150,100 @@ static int sh_clk_div6_set_rate(struct clk *clk,
if (idx < 0)
return idx;
- value = __raw_readl(clk->enable_reg);
- value &= ~0x3f;
- value |= idx;
- __raw_writel(value, clk->enable_reg);
+ value = sh_clk_read(clk);
+ value &= ~(clk->div_mask << clk->enable_bit);
+ value |= (idx << clk->enable_bit);
+ sh_clk_write(value, clk);
+
+ /* XXX: Should use a post-change notifier */
+ if (dt->kick)
+ dt->kick(clk);
+
return 0;
}
-static int sh_clk_div6_enable(struct clk *clk)
+static int sh_clk_div_enable(struct clk *clk)
{
- unsigned long value;
- int ret;
-
- ret = sh_clk_div6_set_rate(clk, clk->rate, 0);
- if (ret == 0) {
- value = __raw_readl(clk->enable_reg);
- value &= ~0x100; /* clear stop bit to enable clock */
- __raw_writel(value, clk->enable_reg);
+ if (clk->div_mask == SH_CLK_DIV6_MSK) {
+ int ret = sh_clk_div_set_rate(clk, clk->rate);
+ if (ret < 0)
+ return ret;
}
- return ret;
+
+ sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk);
+ return 0;
}
-static void sh_clk_div6_disable(struct clk *clk)
+static void sh_clk_div_disable(struct clk *clk)
{
- unsigned long value;
+ unsigned int val;
- value = __raw_readl(clk->enable_reg);
- value |= 0x100; /* stop clock */
- value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */
- __raw_writel(value, clk->enable_reg);
+ val = sh_clk_read(clk);
+ val |= CPG_CKSTP_BIT;
+
+ /*
+ * div6 clocks require the divisor field to be non-zero or the
+ * above CKSTP toggle silently fails. Ensure that the divisor
+ * array is reset to its initial state on disable.
+ */
+ if (clk->flags & CLK_MASK_DIV_ON_DISABLE)
+ val |= clk->div_mask;
+
+ sh_clk_write(val, clk);
}
-static struct clk_ops sh_clk_div6_clk_ops = {
- .recalc = sh_clk_div6_recalc,
+static struct sh_clk_ops sh_clk_div_clk_ops = {
+ .recalc = sh_clk_div_recalc,
+ .set_rate = sh_clk_div_set_rate,
.round_rate = sh_clk_div_round_rate,
- .set_rate = sh_clk_div6_set_rate,
- .enable = sh_clk_div6_enable,
- .disable = sh_clk_div6_disable,
};
-static struct clk_ops sh_clk_div6_reparent_clk_ops = {
- .recalc = sh_clk_div6_recalc,
+static struct sh_clk_ops sh_clk_div_enable_clk_ops = {
+ .recalc = sh_clk_div_recalc,
+ .set_rate = sh_clk_div_set_rate,
.round_rate = sh_clk_div_round_rate,
- .set_rate = sh_clk_div6_set_rate,
- .enable = sh_clk_div6_enable,
- .disable = sh_clk_div6_disable,
- .set_parent = sh_clk_div6_set_parent,
+ .enable = sh_clk_div_enable,
+ .disable = sh_clk_div_disable,
};
-static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
- struct clk_ops *ops)
+static int __init sh_clk_init_parent(struct clk *clk)
+{
+ u32 val;
+
+ if (clk->parent)
+ return 0;
+
+ if (!clk->parent_table || !clk->parent_num)
+ return 0;
+
+ if (!clk->src_width) {
+ pr_err("sh_clk_init_parent: cannot select parent clock\n");
+ return -EINVAL;
+ }
+
+ val = (sh_clk_read(clk) >> clk->src_shift);
+ val &= (1 << clk->src_width) - 1;
+
+ if (val >= clk->parent_num) {
+ pr_err("sh_clk_init_parent: parent table size failed\n");
+ return -EINVAL;
+ }
+
+ clk_reparent(clk, clk->parent_table[val]);
+ if (!clk->parent) {
+ pr_err("sh_clk_init_parent: unable to set parent");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init sh_clk_div_register_ops(struct clk *clks, int nr,
+ struct clk_div_table *table, struct sh_clk_ops *ops)
{
struct clk *clkp;
void *freq_table;
- int nr_divs = sh_clk_div6_table.nr_divisors;
+ int nr_divs = table->div_mult_table->nr_divisors;
int freq_table_size = sizeof(struct cpufreq_frequency_table);
int ret = 0;
int k;
@@ -181,7 +251,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
freq_table_size *= (nr_divs + 1);
freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL);
if (!freq_table) {
- pr_err("sh_clk_div6_register: unable to alloc memory\n");
+ pr_err("%s: unable to alloc memory\n", __func__);
return -ENOMEM;
}
@@ -189,44 +259,98 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
clkp = clks + k;
clkp->ops = ops;
+ clkp->priv = table;
+
clkp->freq_table = freq_table + (k * freq_table_size);
clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
ret = clk_register(clkp);
+ if (ret == 0)
+ ret = sh_clk_init_parent(clkp);
}
return ret;
}
-int __init sh_clk_div6_register(struct clk *clks, int nr)
-{
- return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops);
-}
+/*
+ * div6 support
+ */
+static int sh_clk_div6_divisors[64] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
+};
-int __init sh_clk_div6_reparent_register(struct clk *clks, int nr)
-{
- return sh_clk_div6_register_ops(clks, nr,
- &sh_clk_div6_reparent_clk_ops);
-}
+static struct clk_div_mult_table div6_div_mult_table = {
+ .divisors = sh_clk_div6_divisors,
+ .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors),
+};
+
+static struct clk_div_table sh_clk_div6_table = {
+ .div_mult_table = &div6_div_mult_table,
+};
-static unsigned long sh_clk_div4_recalc(struct clk *clk)
+static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
{
- struct clk_div4_table *d4t = clk->priv;
- struct clk_div_mult_table *table = d4t->div_mult_table;
- unsigned int idx;
+ struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
+ u32 value;
+ int ret, i;
+
+ if (!clk->parent_table || !clk->parent_num)
+ return -EINVAL;
+
+ /* Search the parent */
+ for (i = 0; i < clk->parent_num; i++)
+ if (clk->parent_table[i] == parent)
+ break;
+ if (i == clk->parent_num)
+ return -ENODEV;
+
+ ret = clk_reparent(clk, parent);
+ if (ret < 0)
+ return ret;
+
+ value = sh_clk_read(clk) &
+ ~(((1 << clk->src_width) - 1) << clk->src_shift);
+
+ sh_clk_write(value | (i << clk->src_shift), clk);
+
+ /* Rebuild the frequency table */
clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
- table, &clk->arch_flags);
+ table, NULL);
- idx = (__raw_readl(clk->enable_reg) >> clk->enable_bit) & 0x000f;
+ return 0;
+}
- return clk->freq_table[idx].frequency;
+static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = {
+ .recalc = sh_clk_div_recalc,
+ .round_rate = sh_clk_div_round_rate,
+ .set_rate = sh_clk_div_set_rate,
+ .enable = sh_clk_div_enable,
+ .disable = sh_clk_div_disable,
+ .set_parent = sh_clk_div6_set_parent,
+};
+
+int __init sh_clk_div6_register(struct clk *clks, int nr)
+{
+ return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table,
+ &sh_clk_div_enable_clk_ops);
}
+int __init sh_clk_div6_reparent_register(struct clk *clks, int nr)
+{
+ return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table,
+ &sh_clk_div6_reparent_clk_ops);
+}
+
+/*
+ * div4 support
+ */
static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
{
- struct clk_div4_table *d4t = clk->priv;
- struct clk_div_mult_table *table = d4t->div_mult_table;
+ struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
u32 value;
int ret;
@@ -236,15 +360,15 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
*/
if (parent->flags & CLK_ENABLE_ON_INIT)
- value = __raw_readl(clk->enable_reg) & ~(1 << 7);
+ value = sh_clk_read(clk) & ~(1 << 7);
else
- value = __raw_readl(clk->enable_reg) | (1 << 7);
+ value = sh_clk_read(clk) | (1 << 7);
ret = clk_reparent(clk, parent);
if (ret < 0)
return ret;
- __raw_writel(value, clk->enable_reg);
+ sh_clk_write(value, clk);
/* Rebiuld the frequency table */
clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
@@ -253,107 +377,116 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
return 0;
}
-static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate, int algo_id)
-{
- struct clk_div4_table *d4t = clk->priv;
- unsigned long value;
- int idx = clk_rate_table_find(clk, clk->freq_table, rate);
- if (idx < 0)
- return idx;
-
- value = __raw_readl(clk->enable_reg);
- value &= ~(0xf << clk->enable_bit);
- value |= (idx << clk->enable_bit);
- __raw_writel(value, clk->enable_reg);
-
- if (d4t->kick)
- d4t->kick(clk);
+static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = {
+ .recalc = sh_clk_div_recalc,
+ .set_rate = sh_clk_div_set_rate,
+ .round_rate = sh_clk_div_round_rate,
+ .enable = sh_clk_div_enable,
+ .disable = sh_clk_div_disable,
+ .set_parent = sh_clk_div4_set_parent,
+};
- return 0;
+int __init sh_clk_div4_register(struct clk *clks, int nr,
+ struct clk_div4_table *table)
+{
+ return sh_clk_div_register_ops(clks, nr, table, &sh_clk_div_clk_ops);
}
-static int sh_clk_div4_enable(struct clk *clk)
+int __init sh_clk_div4_enable_register(struct clk *clks, int nr,
+ struct clk_div4_table *table)
{
- __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << 8), clk->enable_reg);
- return 0;
+ return sh_clk_div_register_ops(clks, nr, table,
+ &sh_clk_div_enable_clk_ops);
}
-static void sh_clk_div4_disable(struct clk *clk)
+int __init sh_clk_div4_reparent_register(struct clk *clks, int nr,
+ struct clk_div4_table *table)
{
- __raw_writel(__raw_readl(clk->enable_reg) | (1 << 8), clk->enable_reg);
+ return sh_clk_div_register_ops(clks, nr, table,
+ &sh_clk_div4_reparent_clk_ops);
}
-static struct clk_ops sh_clk_div4_clk_ops = {
- .recalc = sh_clk_div4_recalc,
- .set_rate = sh_clk_div4_set_rate,
- .round_rate = sh_clk_div_round_rate,
-};
+/* FSI-DIV */
+static unsigned long fsidiv_recalc(struct clk *clk)
+{
+ u32 value;
-static struct clk_ops sh_clk_div4_enable_clk_ops = {
- .recalc = sh_clk_div4_recalc,
- .set_rate = sh_clk_div4_set_rate,
- .round_rate = sh_clk_div_round_rate,
- .enable = sh_clk_div4_enable,
- .disable = sh_clk_div4_disable,
-};
+ value = __raw_readl(clk->mapping->base);
-static struct clk_ops sh_clk_div4_reparent_clk_ops = {
- .recalc = sh_clk_div4_recalc,
- .set_rate = sh_clk_div4_set_rate,
- .round_rate = sh_clk_div_round_rate,
- .enable = sh_clk_div4_enable,
- .disable = sh_clk_div4_disable,
- .set_parent = sh_clk_div4_set_parent,
-};
+ value >>= 16;
+ if (value < 2)
+ return clk->parent->rate;
-static int __init sh_clk_div4_register_ops(struct clk *clks, int nr,
- struct clk_div4_table *table, struct clk_ops *ops)
-{
- struct clk *clkp;
- void *freq_table;
- int nr_divs = table->div_mult_table->nr_divisors;
- int freq_table_size = sizeof(struct cpufreq_frequency_table);
- int ret = 0;
- int k;
+ return clk->parent->rate / value;
+}
- freq_table_size *= (nr_divs + 1);
- freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL);
- if (!freq_table) {
- pr_err("sh_clk_div4_register: unable to alloc memory\n");
- return -ENOMEM;
- }
+static long fsidiv_round_rate(struct clk *clk, unsigned long rate)
+{
+ return clk_rate_div_range_round(clk, 1, 0xffff, rate);
+}
- for (k = 0; !ret && (k < nr); k++) {
- clkp = clks + k;
+static void fsidiv_disable(struct clk *clk)
+{
+ __raw_writel(0, clk->mapping->base);
+}
- clkp->ops = ops;
- clkp->priv = table;
+static int fsidiv_enable(struct clk *clk)
+{
+ u32 value;
- clkp->freq_table = freq_table + (k * freq_table_size);
- clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
+ value = __raw_readl(clk->mapping->base) >> 16;
+ if (value < 2)
+ return 0;
- ret = clk_register(clkp);
- }
+ __raw_writel((value << 16) | 0x3, clk->mapping->base);
- return ret;
+ return 0;
}
-int __init sh_clk_div4_register(struct clk *clks, int nr,
- struct clk_div4_table *table)
+static int fsidiv_set_rate(struct clk *clk, unsigned long rate)
{
- return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div4_clk_ops);
-}
+ int idx;
-int __init sh_clk_div4_enable_register(struct clk *clks, int nr,
- struct clk_div4_table *table)
-{
- return sh_clk_div4_register_ops(clks, nr, table,
- &sh_clk_div4_enable_clk_ops);
+ idx = (clk->parent->rate / rate) & 0xffff;
+ if (idx < 2)
+ __raw_writel(0, clk->mapping->base);
+ else
+ __raw_writel(idx << 16, clk->mapping->base);
+
+ return 0;
}
-int __init sh_clk_div4_reparent_register(struct clk *clks, int nr,
- struct clk_div4_table *table)
+static struct sh_clk_ops fsidiv_clk_ops = {
+ .recalc = fsidiv_recalc,
+ .round_rate = fsidiv_round_rate,
+ .set_rate = fsidiv_set_rate,
+ .enable = fsidiv_enable,
+ .disable = fsidiv_disable,
+};
+
+int __init sh_clk_fsidiv_register(struct clk *clks, int nr)
{
- return sh_clk_div4_register_ops(clks, nr, table,
- &sh_clk_div4_reparent_clk_ops);
+ struct clk_mapping *map;
+ int i;
+
+ for (i = 0; i < nr; i++) {
+
+ map = kzalloc(sizeof(struct clk_mapping), GFP_KERNEL);
+ if (!map) {
+ pr_err("%s: unable to alloc memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* clks[i].enable_reg came from SH_CLK_FSIDIV() */
+ map->phys = (phys_addr_t)clks[i].enable_reg;
+ map->len = 8;
+
+ clks[i].enable_reg = 0; /* remove .enable_reg */
+ clks[i].ops = &fsidiv_clk_ops;
+ clks[i].mapping = map;
+
+ clk_register(&clks[i]);
+ }
+
+ return 0;
}
diff --git a/drivers/sh/intc/Kconfig b/drivers/sh/intc/Kconfig
index c88cbccc62b..60228fae943 100644
--- a/drivers/sh/intc/Kconfig
+++ b/drivers/sh/intc/Kconfig
@@ -1,8 +1,12 @@
+config SH_INTC
+ def_bool y
+ select IRQ_DOMAIN
+
comment "Interrupt controller options"
config INTC_USERIMASK
bool "Userspace interrupt masking support"
- depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A)
+ depends on (SUPERH && CPU_SH4A) || COMPILE_TEST
help
This enables support for hardware-assisted userspace hardirq
masking.
diff --git a/drivers/sh/intc/Makefile b/drivers/sh/intc/Makefile
index bb5df868d77..54ec2a0643d 100644
--- a/drivers/sh/intc/Makefile
+++ b/drivers/sh/intc/Makefile
@@ -1,4 +1,4 @@
-obj-y := access.o chip.o core.o dynamic.o handle.o virq.o
+obj-y := access.o chip.o core.o handle.o irqdomain.o virq.o
obj-$(CONFIG_INTC_BALANCING) += balancing.o
obj-$(CONFIG_INTC_USERIMASK) += userimask.o
diff --git a/drivers/sh/intc/access.c b/drivers/sh/intc/access.c
index f892ae1d212..114390f967d 100644
--- a/drivers/sh/intc/access.c
+++ b/drivers/sh/intc/access.c
@@ -75,54 +75,61 @@ unsigned long intc_get_field_from_handle(unsigned int value, unsigned int handle
static unsigned long test_8(unsigned long addr, unsigned long h,
unsigned long ignore)
{
- return intc_get_field_from_handle(__raw_readb(addr), h);
+ void __iomem *ptr = (void __iomem *)addr;
+ return intc_get_field_from_handle(__raw_readb(ptr), h);
}
static unsigned long test_16(unsigned long addr, unsigned long h,
unsigned long ignore)
{
- return intc_get_field_from_handle(__raw_readw(addr), h);
+ void __iomem *ptr = (void __iomem *)addr;
+ return intc_get_field_from_handle(__raw_readw(ptr), h);
}
static unsigned long test_32(unsigned long addr, unsigned long h,
unsigned long ignore)
{
- return intc_get_field_from_handle(__raw_readl(addr), h);
+ void __iomem *ptr = (void __iomem *)addr;
+ return intc_get_field_from_handle(__raw_readl(ptr), h);
}
static unsigned long write_8(unsigned long addr, unsigned long h,
unsigned long data)
{
- __raw_writeb(intc_set_field_from_handle(0, data, h), addr);
- (void)__raw_readb(addr); /* Defeat write posting */
+ void __iomem *ptr = (void __iomem *)addr;
+ __raw_writeb(intc_set_field_from_handle(0, data, h), ptr);
+ (void)__raw_readb(ptr); /* Defeat write posting */
return 0;
}
static unsigned long write_16(unsigned long addr, unsigned long h,
unsigned long data)
{
- __raw_writew(intc_set_field_from_handle(0, data, h), addr);
- (void)__raw_readw(addr); /* Defeat write posting */
+ void __iomem *ptr = (void __iomem *)addr;
+ __raw_writew(intc_set_field_from_handle(0, data, h), ptr);
+ (void)__raw_readw(ptr); /* Defeat write posting */
return 0;
}
static unsigned long write_32(unsigned long addr, unsigned long h,
unsigned long data)
{
- __raw_writel(intc_set_field_from_handle(0, data, h), addr);
- (void)__raw_readl(addr); /* Defeat write posting */
+ void __iomem *ptr = (void __iomem *)addr;
+ __raw_writel(intc_set_field_from_handle(0, data, h), ptr);
+ (void)__raw_readl(ptr); /* Defeat write posting */
return 0;
}
static unsigned long modify_8(unsigned long addr, unsigned long h,
unsigned long data)
{
+ void __iomem *ptr = (void __iomem *)addr;
unsigned long flags;
unsigned int value;
local_irq_save(flags);
- value = intc_set_field_from_handle(__raw_readb(addr), data, h);
- __raw_writeb(value, addr);
- (void)__raw_readb(addr); /* Defeat write posting */
+ value = intc_set_field_from_handle(__raw_readb(ptr), data, h);
+ __raw_writeb(value, ptr);
+ (void)__raw_readb(ptr); /* Defeat write posting */
local_irq_restore(flags);
return 0;
}
@@ -130,12 +137,13 @@ static unsigned long modify_8(unsigned long addr, unsigned long h,
static unsigned long modify_16(unsigned long addr, unsigned long h,
unsigned long data)
{
+ void __iomem *ptr = (void __iomem *)addr;
unsigned long flags;
unsigned int value;
local_irq_save(flags);
- value = intc_set_field_from_handle(__raw_readw(addr), data, h);
- __raw_writew(value, addr);
- (void)__raw_readw(addr); /* Defeat write posting */
+ value = intc_set_field_from_handle(__raw_readw(ptr), data, h);
+ __raw_writew(value, ptr);
+ (void)__raw_readw(ptr); /* Defeat write posting */
local_irq_restore(flags);
return 0;
}
@@ -143,12 +151,13 @@ static unsigned long modify_16(unsigned long addr, unsigned long h,
static unsigned long modify_32(unsigned long addr, unsigned long h,
unsigned long data)
{
+ void __iomem *ptr = (void __iomem *)addr;
unsigned long flags;
unsigned int value;
local_irq_save(flags);
- value = intc_set_field_from_handle(__raw_readl(addr), data, h);
- __raw_writel(value, addr);
- (void)__raw_readl(addr); /* Defeat write posting */
+ value = intc_set_field_from_handle(__raw_readl(ptr), data, h);
+ __raw_writel(value, ptr);
+ (void)__raw_readl(ptr); /* Defeat write posting */
local_irq_restore(flags);
return 0;
}
diff --git a/drivers/sh/intc/balancing.c b/drivers/sh/intc/balancing.c
index cec7a96f2c0..bc780807ccb 100644
--- a/drivers/sh/intc/balancing.c
+++ b/drivers/sh/intc/balancing.c
@@ -9,7 +9,7 @@
*/
#include "internals.h"
-static unsigned long dist_handle[NR_IRQS];
+static unsigned long dist_handle[INTC_NR_IRQS];
void intc_balancing_enable(unsigned int irq)
{
diff --git a/drivers/sh/intc/chip.c b/drivers/sh/intc/chip.c
index de885a0f917..46427b48e2f 100644
--- a/drivers/sh/intc/chip.c
+++ b/drivers/sh/intc/chip.c
@@ -2,13 +2,14 @@
* IRQ chip definitions for INTC IRQs.
*
* Copyright (C) 2007, 2008 Magnus Damm
- * Copyright (C) 2009, 2010 Paul Mundt
+ * Copyright (C) 2009 - 2012 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/cpumask.h>
+#include <linux/bsearch.h>
#include <linux/io.h>
#include "internals.h"
@@ -58,11 +59,6 @@ static void intc_disable(struct irq_data *data)
}
}
-static int intc_set_wake(struct irq_data *data, unsigned int on)
-{
- return 0; /* allow wakeup, but setup hardware in intc_suspend() */
-}
-
#ifdef CONFIG_SMP
/*
* This is held with the irq desc lock held, so we don't require any
@@ -78,7 +74,7 @@ static int intc_set_affinity(struct irq_data *data,
cpumask_copy(data->affinity, cpumask);
- return 0;
+ return IRQ_SET_MASK_OK_NOCOPY;
}
#endif
@@ -87,7 +83,7 @@ static void intc_mask_ack(struct irq_data *data)
unsigned int irq = data->irq;
struct intc_desc_int *d = get_intc_desc(irq);
unsigned long handle = intc_get_ack_handle(irq);
- unsigned long addr;
+ void __iomem *addr;
intc_disable(data);
@@ -95,7 +91,7 @@ static void intc_mask_ack(struct irq_data *data)
if (handle) {
unsigned int value;
- addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
+ addr = (void __iomem *)INTC_REG(d, _INTC_ADDR_D(handle), 0);
value = intc_set_field_from_handle(0, 1, handle);
switch (_INTC_FN(handle)) {
@@ -122,28 +118,12 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
unsigned int nr_hp,
unsigned int irq)
{
- int i;
-
- /*
- * this doesn't scale well, but...
- *
- * this function should only be used for cerain uncommon
- * operations such as intc_set_priority() and intc_set_type()
- * and in those rare cases performance doesn't matter that much.
- * keeping the memory footprint low is more important.
- *
- * one rather simple way to speed this up and still keep the
- * memory footprint down is to make sure the array is sorted
- * and then perform a bisect to lookup the irq.
- */
- for (i = 0; i < nr_hp; i++) {
- if ((hp + i)->irq != irq)
- continue;
+ struct intc_handle_int key;
- return hp + i;
- }
+ key.irq = irq;
+ key.handle = 0;
- return NULL;
+ return bsearch(&key, hp, nr_hp, sizeof(*hp), intc_handle_int_cmp);
}
int intc_set_priority(unsigned int irq, unsigned int prio)
@@ -173,7 +153,8 @@ int intc_set_priority(unsigned int irq, unsigned int prio)
return 0;
}
-#define VALID(x) (x | 0x80)
+#define SENSE_VALID_FLAG 0x80
+#define VALID(x) (x | SENSE_VALID_FLAG)
static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
[IRQ_TYPE_EDGE_FALLING] = VALID(0),
@@ -185,6 +166,9 @@ static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
!defined(CONFIG_CPU_SUBTYPE_SH7709)
[IRQ_TYPE_LEVEL_HIGH] = VALID(3),
#endif
+#if defined(CONFIG_ARM) /* all recent SH-Mobile / R-Mobile ARM support this */
+ [IRQ_TYPE_EDGE_BOTH] = VALID(4),
+#endif
};
static int intc_set_type(struct irq_data *data, unsigned int type)
@@ -198,8 +182,14 @@ static int intc_set_type(struct irq_data *data, unsigned int type)
if (!value)
return -EINVAL;
+ value &= ~SENSE_VALID_FLAG;
+
ihp = intc_find_irq(d->sense, d->nr_sense, irq);
if (ihp) {
+ /* PINT has 2-bit sense registers, should fail on EDGE_BOTH */
+ if (value >= (1 << _INTC_WIDTH(ihp->handle)))
+ return -EINVAL;
+
addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
}
@@ -213,10 +203,9 @@ struct irq_chip intc_irq_chip = {
.irq_mask_ack = intc_mask_ack,
.irq_enable = intc_enable,
.irq_disable = intc_disable,
- .irq_shutdown = intc_disable,
.irq_set_type = intc_set_type,
- .irq_set_wake = intc_set_wake,
#ifdef CONFIG_SMP
.irq_set_affinity = intc_set_affinity,
#endif
+ .flags = IRQCHIP_SKIP_SET_WAKE,
};
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
index e5e9e6735f7..81f22980b2d 100644
--- a/drivers/sh/intc/core.c
+++ b/drivers/sh/intc/core.c
@@ -2,7 +2,7 @@
* Shared interrupt handling code for IPR and INTC2 types of IRQs.
*
* Copyright (C) 2007, 2008 Magnus Damm
- * Copyright (C) 2009, 2010 Paul Mundt
+ * Copyright (C) 2009 - 2012 Paul Mundt
*
* Based on intc2.c and ipr.c
*
@@ -22,24 +22,29 @@
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/interrupt.h>
#include <linux/sh_intc.h>
-#include <linux/sysdev.h>
+#include <linux/irqdomain.h>
+#include <linux/device.h>
+#include <linux/syscore_ops.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/radix-tree.h>
+#include <linux/export.h>
+#include <linux/sort.h>
#include "internals.h"
LIST_HEAD(intc_list);
DEFINE_RAW_SPINLOCK(intc_big_lock);
-unsigned int nr_intc_controllers;
+static unsigned int nr_intc_controllers;
/*
* Default priority level
* - this needs to be at least 2 for 5-bit priorities on 7780
*/
static unsigned int default_prio_level = 2; /* 2 - 16 */
-static unsigned int intc_prio_level[NR_IRQS]; /* for now */
+static unsigned int intc_prio_level[INTC_NR_IRQS]; /* for now */
unsigned int intc_get_dfl_prio_level(void)
{
@@ -62,7 +67,7 @@ void intc_set_prio_level(unsigned int irq, unsigned int level)
static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
{
- generic_handle_irq((unsigned int)get_irq_data(irq));
+ generic_handle_irq((unsigned int)irq_get_handler_data(irq));
}
static void __init intc_register_irq(struct intc_desc *desc,
@@ -75,12 +80,6 @@ static void __init intc_register_irq(struct intc_desc *desc,
unsigned int data[2], primary;
unsigned long flags;
- /*
- * Register the IRQ position with the global IRQ map, then insert
- * it in to the radix tree.
- */
- irq_reserve_irq(irq);
-
raw_spin_lock_irqsave(&intc_big_lock, flags);
radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq));
raw_spin_unlock_irqrestore(&intc_big_lock, flags);
@@ -115,9 +114,9 @@ static void __init intc_register_irq(struct intc_desc *desc,
irq_data = irq_get_irq_data(irq);
disable_irq_nosync(irq);
- set_irq_chip_and_handler_name(irq, &d->chip,
- handle_level_irq, "level");
- set_irq_chip_data(irq, (void *)data[primary]);
+ irq_set_chip_and_handler_name(irq, &d->chip, handle_level_irq,
+ "level");
+ irq_set_chip_data(irq, (void *)data[primary]);
/*
* set priority level
@@ -198,6 +197,7 @@ int __init register_intc_controller(struct intc_desc *desc)
list_add_tail(&d->list, &intc_list);
raw_spin_lock_init(&d->lock);
+ INIT_RADIX_TREE(&d->tree, GFP_ATOMIC);
d->index = nr_intc_controllers;
@@ -263,6 +263,9 @@ int __init register_intc_controller(struct intc_desc *desc)
k += save_reg(d, k, hw->prio_regs[i].set_reg, smp);
k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp);
}
+
+ sort(d->prio, hw->nr_prio_regs, sizeof(*d->prio),
+ intc_handle_int_cmp, NULL);
}
if (hw->sense_regs) {
@@ -273,6 +276,9 @@ int __init register_intc_controller(struct intc_desc *desc)
for (i = 0; i < hw->nr_sense_regs; i++)
k += save_reg(d, k, hw->sense_regs[i].reg, 0);
+
+ sort(d->sense, hw->nr_sense_regs, sizeof(*d->sense),
+ intc_handle_int_cmp, NULL);
}
if (hw->subgroups)
@@ -299,6 +305,8 @@ int __init register_intc_controller(struct intc_desc *desc)
BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */
+ intc_irq_domain_init(d, hw);
+
/* register the vectors one by one */
for (i = 0; i < hw->nr_vectors; i++) {
struct intc_vect *vect = hw->vectors + i;
@@ -308,10 +316,18 @@ int __init register_intc_controller(struct intc_desc *desc)
if (!vect->enum_id)
continue;
- res = irq_alloc_desc_at(irq, numa_node_id());
- if (res != irq && res != -EEXIST) {
- pr_err("can't get irq_desc for %d\n", irq);
- continue;
+ res = irq_create_identity_mapping(d->domain, irq);
+ if (unlikely(res)) {
+ if (res == -EEXIST) {
+ res = irq_domain_associate(d->domain, irq, irq);
+ if (unlikely(res)) {
+ pr_err("domain association failure\n");
+ continue;
+ }
+ } else {
+ pr_err("can't identity map IRQ %d\n", irq);
+ continue;
+ }
}
intc_irq_xlate_set(irq, vect->enum_id, d);
@@ -329,18 +345,29 @@ int __init register_intc_controller(struct intc_desc *desc)
* IRQ support, each vector still needs to have
* its own backing irq_desc.
*/
- res = irq_alloc_desc_at(irq2, numa_node_id());
- if (res != irq2 && res != -EEXIST) {
- pr_err("can't get irq_desc for %d\n", irq2);
- continue;
+ res = irq_create_identity_mapping(d->domain, irq2);
+ if (unlikely(res)) {
+ if (res == -EEXIST) {
+ res = irq_domain_associate(d->domain,
+ irq2, irq2);
+ if (unlikely(res)) {
+ pr_err("domain association "
+ "failure\n");
+ continue;
+ }
+ } else {
+ pr_err("can't identity map IRQ %d\n",
+ irq);
+ continue;
+ }
}
vect2->enum_id = 0;
/* redirect this interrupts to the first one */
- set_irq_chip(irq2, &dummy_irq_chip);
- set_irq_chained_handler(irq2, intc_redirect_irq);
- set_irq_data(irq2, (void *)irq);
+ irq_set_chip(irq2, &dummy_irq_chip);
+ irq_set_chained_handler(irq2, intc_redirect_irq);
+ irq_set_handler_data(irq2, (void *)irq);
}
}
@@ -350,6 +377,8 @@ int __init register_intc_controller(struct intc_desc *desc)
if (desc->force_enable)
intc_enable_disable_enum(desc, d, desc->force_enable, 1);
+ d->skip_suspend = desc->skip_syscore_suspend;
+
nr_intc_controllers++;
return 0;
@@ -375,108 +404,108 @@ err0:
return -ENOMEM;
}
-static ssize_t
-show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf)
+static int intc_suspend(void)
{
struct intc_desc_int *d;
- d = container_of(dev, struct intc_desc_int, sysdev);
+ list_for_each_entry(d, &intc_list, list) {
+ int irq;
- return sprintf(buf, "%s\n", d->chip.name);
-}
+ if (d->skip_suspend)
+ continue;
-static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL);
+ /* enable wakeup irqs belonging to this intc controller */
+ for_each_active_irq(irq) {
+ struct irq_data *data;
+ struct irq_chip *chip;
+
+ data = irq_get_irq_data(irq);
+ chip = irq_data_get_irq_chip(data);
+ if (chip != &d->chip)
+ continue;
+ if (irqd_is_wakeup_set(data))
+ chip->irq_enable(data);
+ }
+ }
+ return 0;
+}
-static int intc_suspend(struct sys_device *dev, pm_message_t state)
+static void intc_resume(void)
{
struct intc_desc_int *d;
- struct irq_data *data;
- struct irq_desc *desc;
- struct irq_chip *chip;
- int irq;
- /* get intc controller associated with this sysdev */
- d = container_of(dev, struct intc_desc_int, sysdev);
+ list_for_each_entry(d, &intc_list, list) {
+ int irq;
- switch (state.event) {
- case PM_EVENT_ON:
- if (d->state.event != PM_EVENT_FREEZE)
- break;
+ if (d->skip_suspend)
+ continue;
for_each_active_irq(irq) {
- desc = irq_to_desc(irq);
+ struct irq_data *data;
+ struct irq_chip *chip;
+
data = irq_get_irq_data(irq);
chip = irq_data_get_irq_chip(data);
-
/*
* This will catch the redirect and VIRQ cases
* due to the dummy_irq_chip being inserted.
*/
if (chip != &d->chip)
continue;
- if (desc->status & IRQ_DISABLED)
+ if (irqd_irq_disabled(data))
chip->irq_disable(data);
else
chip->irq_enable(data);
}
- break;
- case PM_EVENT_FREEZE:
- /* nothing has to be done */
- break;
- case PM_EVENT_SUSPEND:
- /* enable wakeup irqs belonging to this intc controller */
- for_each_active_irq(irq) {
- desc = irq_to_desc(irq);
- data = irq_get_irq_data(irq);
- chip = irq_data_get_irq_chip(data);
-
- if (chip != &d->chip)
- continue;
- if ((desc->status & IRQ_WAKEUP))
- chip->irq_enable(data);
- }
- break;
}
+}
- d->state = state;
+struct syscore_ops intc_syscore_ops = {
+ .suspend = intc_suspend,
+ .resume = intc_resume,
+};
- return 0;
-}
+struct bus_type intc_subsys = {
+ .name = "intc",
+ .dev_name = "intc",
+};
-static int intc_resume(struct sys_device *dev)
+static ssize_t
+show_intc_name(struct device *dev, struct device_attribute *attr, char *buf)
{
- return intc_suspend(dev, PMSG_ON);
+ struct intc_desc_int *d;
+
+ d = container_of(dev, struct intc_desc_int, dev);
+
+ return sprintf(buf, "%s\n", d->chip.name);
}
-struct sysdev_class intc_sysdev_class = {
- .name = "intc",
- .suspend = intc_suspend,
- .resume = intc_resume,
-};
+static DEVICE_ATTR(name, S_IRUGO, show_intc_name, NULL);
-/* register this intc as sysdev to allow suspend/resume */
-static int __init register_intc_sysdevs(void)
+static int __init register_intc_devs(void)
{
struct intc_desc_int *d;
int error;
- error = sysdev_class_register(&intc_sysdev_class);
+ register_syscore_ops(&intc_syscore_ops);
+
+ error = subsys_system_register(&intc_subsys, NULL);
if (!error) {
list_for_each_entry(d, &intc_list, list) {
- d->sysdev.id = d->index;
- d->sysdev.cls = &intc_sysdev_class;
- error = sysdev_register(&d->sysdev);
+ d->dev.id = d->index;
+ d->dev.bus = &intc_subsys;
+ error = device_register(&d->dev);
if (error == 0)
- error = sysdev_create_file(&d->sysdev,
- &attr_name);
+ error = device_create_file(&d->dev,
+ &dev_attr_name);
if (error)
break;
}
}
if (error)
- pr_err("sysdev registration error\n");
+ pr_err("device registration error\n");
return error;
}
-device_initcall(register_intc_sysdevs);
+device_initcall(register_intc_devs);
diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c
deleted file mode 100644
index a3677c9dfe3..00000000000
--- a/drivers/sh/intc/dynamic.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Dynamic IRQ management
- *
- * Copyright (C) 2010 Paul Mundt
- *
- * Modelled after arch/x86/kernel/apic/io_apic.c
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#define pr_fmt(fmt) "intc: " fmt
-
-#include <linux/irq.h>
-#include <linux/bitmap.h>
-#include <linux/spinlock.h>
-#include "internals.h" /* only for activate_irq() damage.. */
-
-/*
- * The IRQ bitmap provides a global map of bound IRQ vectors for a
- * given platform. Allocation of IRQs are either static through the CPU
- * vector map, or dynamic in the case of board mux vectors or MSI.
- *
- * As this is a central point for all IRQ controllers on the system,
- * each of the available sources are mapped out here. This combined with
- * sparseirq makes it quite trivial to keep the vector map tightly packed
- * when dynamically creating IRQs, as well as tying in to otherwise
- * unused irq_desc positions in the sparse array.
- */
-
-/*
- * Dynamic IRQ allocation and deallocation
- */
-unsigned int create_irq_nr(unsigned int irq_want, int node)
-{
- int irq = irq_alloc_desc_at(irq_want, node);
- if (irq < 0)
- return 0;
-
- activate_irq(irq);
- return irq;
-}
-
-int create_irq(void)
-{
- int irq = irq_alloc_desc(numa_node_id());
- if (irq >= 0)
- activate_irq(irq);
-
- return irq;
-}
-
-void destroy_irq(unsigned int irq)
-{
- irq_free_desc(irq);
-}
-
-void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs)
-{
- int i;
-
- for (i = 0; i < nr_vecs; i++)
- irq_reserve_irq(evt2irq(vectors[i].vect));
-}
diff --git a/drivers/sh/intc/handle.c b/drivers/sh/intc/handle.c
index 057ce56829b..7863a44918a 100644
--- a/drivers/sh/intc/handle.c
+++ b/drivers/sh/intc/handle.c
@@ -13,7 +13,7 @@
#include <linux/spinlock.h>
#include "internals.h"
-static unsigned long ack_handle[NR_IRQS];
+static unsigned long ack_handle[INTC_NR_IRQS];
static intc_enum __init intc_grp_id(struct intc_desc *desc,
intc_enum enum_id)
@@ -172,9 +172,8 @@ intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d,
return 0;
}
-static unsigned int __init intc_ack_data(struct intc_desc *desc,
- struct intc_desc_int *d,
- intc_enum enum_id)
+static unsigned int intc_ack_data(struct intc_desc *desc,
+ struct intc_desc_int *d, intc_enum enum_id)
{
struct intc_mask_reg *mr = desc->hw.ack_regs;
unsigned int i, j, fn, mode;
diff --git a/drivers/sh/intc/internals.h b/drivers/sh/intc/internals.h
index 0cf8260971d..7dff08e2a07 100644
--- a/drivers/sh/intc/internals.h
+++ b/drivers/sh/intc/internals.h
@@ -1,10 +1,11 @@
#include <linux/sh_intc.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/radix-tree.h>
-#include <linux/sysdev.h>
+#include <linux/device.h>
#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
@@ -51,9 +52,8 @@ struct intc_subgroup_entry {
struct intc_desc_int {
struct list_head list;
- struct sys_device sysdev;
+ struct device dev;
struct radix_tree_root tree;
- pm_message_t state;
raw_spinlock_t lock;
unsigned int index;
unsigned long *reg;
@@ -67,7 +67,9 @@ struct intc_desc_int {
unsigned int nr_sense;
struct intc_window *window;
unsigned int nr_windows;
+ struct irq_domain *domain;
struct irq_chip chip;
+ bool skip_suspend;
};
@@ -87,7 +89,7 @@ enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */
static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
{
- struct irq_chip *chip = get_irq_chip(irq);
+ struct irq_chip *chip = irq_get_chip(irq);
return container_of(chip, struct intc_desc_int, chip);
}
@@ -104,10 +106,18 @@ static inline void activate_irq(int irq)
set_irq_flags(irq, IRQF_VALID);
#else
/* same effect on other architectures */
- set_irq_noprobe(irq);
+ irq_set_noprobe(irq);
#endif
}
+static inline int intc_handle_int_cmp(const void *a, const void *b)
+{
+ const struct intc_handle_int *_a = a;
+ const struct intc_handle_int *_b = b;
+
+ return _a->irq - _b->irq;
+}
+
/* access.c */
extern unsigned long
(*intc_reg_fns[])(unsigned long addr, unsigned long h, unsigned long data);
@@ -157,8 +167,7 @@ void _intc_enable(struct irq_data *data, unsigned long handle);
/* core.c */
extern struct list_head intc_list;
extern raw_spinlock_t intc_big_lock;
-extern unsigned int nr_intc_controllers;
-extern struct sysdev_class intc_sysdev_class;
+extern struct bus_type intc_subsys;
unsigned int intc_get_dfl_prio_level(void);
unsigned int intc_get_prio_level(unsigned int irq);
@@ -180,6 +189,9 @@ unsigned long intc_get_ack_handle(unsigned int irq);
void intc_enable_disable_enum(struct intc_desc *desc, struct intc_desc_int *d,
intc_enum enum_id, int enable);
+/* irqdomain.c */
+void intc_irq_domain_init(struct intc_desc_int *d, struct intc_hw_desc *hw);
+
/* virq.c */
void intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d);
void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d);
diff --git a/drivers/sh/intc/irqdomain.c b/drivers/sh/intc/irqdomain.c
new file mode 100644
index 00000000000..3968f1c3c5c
--- /dev/null
+++ b/drivers/sh/intc/irqdomain.c
@@ -0,0 +1,68 @@
+/*
+ * IRQ domain support for SH INTC subsystem
+ *
+ * Copyright (C) 2012 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#define pr_fmt(fmt) "intc: " fmt
+
+#include <linux/irqdomain.h>
+#include <linux/sh_intc.h>
+#include <linux/export.h>
+#include "internals.h"
+
+/**
+ * intc_irq_domain_evt_xlate() - Generic xlate for vectored IRQs.
+ *
+ * This takes care of exception vector to hwirq translation through
+ * by way of evt2irq() translation.
+ *
+ * Note: For platforms that use a flat vector space without INTEVT this
+ * basically just mimics irq_domain_xlate_onecell() by way of a nopped
+ * out evt2irq() implementation.
+ */
+static int intc_evt_xlate(struct irq_domain *d, struct device_node *ctrlr,
+ const u32 *intspec, unsigned int intsize,
+ unsigned long *out_hwirq, unsigned int *out_type)
+{
+ if (WARN_ON(intsize < 1))
+ return -EINVAL;
+
+ *out_hwirq = evt2irq(intspec[0]);
+ *out_type = IRQ_TYPE_NONE;
+
+ return 0;
+}
+
+static const struct irq_domain_ops intc_evt_ops = {
+ .xlate = intc_evt_xlate,
+};
+
+void __init intc_irq_domain_init(struct intc_desc_int *d,
+ struct intc_hw_desc *hw)
+{
+ unsigned int irq_base, irq_end;
+
+ /*
+ * Quick linear revmap check
+ */
+ irq_base = evt2irq(hw->vectors[0].vect);
+ irq_end = evt2irq(hw->vectors[hw->nr_vectors - 1].vect);
+
+ /*
+ * Linear domains have a hard-wired assertion that IRQs start at
+ * 0 in order to make some performance optimizations. Lamely
+ * restrict the linear case to these conditions here, taking the
+ * tree penalty for linear cases with non-zero hwirq bases.
+ */
+ if (irq_base == 0 && irq_end == (irq_base + hw->nr_vectors - 1))
+ d->domain = irq_domain_add_linear(NULL, hw->nr_vectors,
+ &intc_evt_ops, NULL);
+ else
+ d->domain = irq_domain_add_tree(NULL, &intc_evt_ops, NULL);
+
+ BUG_ON(!d->domain);
+}
diff --git a/drivers/sh/intc/userimask.c b/drivers/sh/intc/userimask.c
index e32304b66cf..e649ceaaa41 100644
--- a/drivers/sh/intc/userimask.c
+++ b/drivers/sh/intc/userimask.c
@@ -10,24 +10,25 @@
#define pr_fmt(fmt) "intc: " fmt
#include <linux/errno.h>
-#include <linux/sysdev.h>
+#include <linux/device.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/stat.h>
#include <asm/sizes.h>
#include "internals.h"
static void __iomem *uimask;
static ssize_t
-show_intc_userimask(struct sysdev_class *cls,
- struct sysdev_class_attribute *attr, char *buf)
+show_intc_userimask(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf);
}
static ssize_t
-store_intc_userimask(struct sysdev_class *cls,
- struct sysdev_class_attribute *attr,
+store_intc_userimask(struct device *dev,
+ struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long level;
@@ -54,8 +55,8 @@ store_intc_userimask(struct sysdev_class *cls,
return count;
}
-static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR,
- show_intc_userimask, store_intc_userimask);
+static DEVICE_ATTR(userimask, S_IRUSR | S_IWUSR,
+ show_intc_userimask, store_intc_userimask);
static int __init userimask_sysdev_init(void)
@@ -63,7 +64,7 @@ static int __init userimask_sysdev_init(void)
if (unlikely(!uimask))
return -ENXIO;
- return sysdev_class_create_file(&intc_sysdev_class, &attr_userimask);
+ return device_create_file(intc_subsys.dev_root, &dev_attr_userimask);
}
late_initcall(userimask_sysdev_init);
diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c
index e5bf5d3c698..f30ac9354ff 100644
--- a/drivers/sh/intc/virq.c
+++ b/drivers/sh/intc/virq.c
@@ -14,9 +14,10 @@
#include <linux/list.h>
#include <linux/radix-tree.h>
#include <linux/spinlock.h>
+#include <linux/export.h>
#include "internals.h"
-static struct intc_map_entry intc_irq_xlate[NR_IRQS];
+static struct intc_map_entry intc_irq_xlate[INTC_NR_IRQS];
struct intc_virq_list {
unsigned int irq;
@@ -110,7 +111,7 @@ static void intc_virq_handler(unsigned int irq, struct irq_desc *desc)
{
struct irq_data *data = irq_get_irq_data(irq);
struct irq_chip *chip = irq_data_get_irq_chip(data);
- struct intc_virq_list *entry, *vlist = irq_data_get_irq_data(data);
+ struct intc_virq_list *entry, *vlist = irq_data_get_irq_handler_data(data);
struct intc_desc_int *d = get_intc_desc(irq);
chip->irq_mask_ack(data);
@@ -118,7 +119,7 @@ static void intc_virq_handler(unsigned int irq, struct irq_desc *desc)
for_each_virq(entry, vlist) {
unsigned long addr, handle;
- handle = (unsigned long)get_irq_data(entry->irq);
+ handle = (unsigned long)irq_get_handler_data(entry->irq);
addr = INTC_REG(d, _INTC_ADDR_E(handle), 0);
if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0))
@@ -215,27 +216,34 @@ restart:
entry = radix_tree_deref_slot((void **)entries[i]);
if (unlikely(!entry))
continue;
- if (unlikely(entry == RADIX_TREE_RETRY))
+ if (radix_tree_deref_retry(entry))
goto restart;
- irq = create_irq();
+ irq = irq_alloc_desc(numa_node_id());
if (unlikely(irq < 0)) {
pr_err("no more free IRQs, bailing..\n");
break;
}
+ activate_irq(irq);
+
pr_info("Setting up a chained VIRQ from %d -> %d\n",
irq, entry->pirq);
intc_irq_xlate_set(irq, entry->enum_id, d);
- set_irq_chip_and_handler_name(irq, get_irq_chip(entry->pirq),
+ irq_set_chip_and_handler_name(irq, irq_get_chip(entry->pirq),
handle_simple_irq, "virq");
- set_irq_chip_data(irq, get_irq_chip_data(entry->pirq));
+ irq_set_chip_data(irq, irq_get_chip_data(entry->pirq));
+
+ irq_set_handler_data(irq, (void *)entry->handle);
- set_irq_data(irq, (void *)entry->handle);
+ /*
+ * Set the virtual IRQ as non-threadable.
+ */
+ irq_set_nothread(irq);
- set_irq_chained_handler(entry->pirq, intc_virq_handler);
+ irq_set_chained_handler(entry->pirq, intc_virq_handler);
add_virq_to_pirq(entry->pirq, irq);
radix_tree_tag_clear(&d->tree, entry->enum_id,
diff --git a/drivers/sh/maple/maple.c b/drivers/sh/maple/maple.c
index 1e20604257a..bec81c2404f 100644
--- a/drivers/sh/maple/maple.c
+++ b/drivers/sh/maple/maple.c
@@ -20,6 +20,7 @@
#include <linux/maple.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
+#include <linux/module.h>
#include <asm/cacheflush.h>
#include <asm/dma.h>
#include <asm/io.h>
diff --git a/drivers/sh/pfc.c b/drivers/sh/pfc.c
deleted file mode 100644
index 75934e3ea34..00000000000
--- a/drivers/sh/pfc.c
+++ /dev/null
@@ -1,611 +0,0 @@
-/*
- * Pinmuxed GPIO support for SuperH.
- *
- * Copyright (C) 2008 Magnus Damm
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/bitops.h>
-#include <linux/gpio.h>
-
-static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r)
-{
- if (enum_id < r->begin)
- return 0;
-
- if (enum_id > r->end)
- return 0;
-
- return 1;
-}
-
-static unsigned long gpio_read_raw_reg(unsigned long reg,
- unsigned long reg_width)
-{
- switch (reg_width) {
- case 8:
- return __raw_readb(reg);
- case 16:
- return __raw_readw(reg);
- case 32:
- return __raw_readl(reg);
- }
-
- BUG();
- return 0;
-}
-
-static void gpio_write_raw_reg(unsigned long reg,
- unsigned long reg_width,
- unsigned long data)
-{
- switch (reg_width) {
- case 8:
- __raw_writeb(data, reg);
- return;
- case 16:
- __raw_writew(data, reg);
- return;
- case 32:
- __raw_writel(data, reg);
- return;
- }
-
- BUG();
-}
-
-static void gpio_write_bit(struct pinmux_data_reg *dr,
- unsigned long in_pos, unsigned long value)
-{
- unsigned long pos;
-
- pos = dr->reg_width - (in_pos + 1);
-
- pr_debug("write_bit addr = %lx, value = %d, pos = %ld, "
- "r_width = %ld\n",
- dr->reg, !!value, pos, dr->reg_width);
-
- if (value)
- set_bit(pos, &dr->reg_shadow);
- else
- clear_bit(pos, &dr->reg_shadow);
-
- gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow);
-}
-
-static int gpio_read_reg(unsigned long reg, unsigned long reg_width,
- unsigned long field_width, unsigned long in_pos)
-{
- unsigned long data, mask, pos;
-
- data = 0;
- mask = (1 << field_width) - 1;
- pos = reg_width - ((in_pos + 1) * field_width);
-
- pr_debug("read_reg: addr = %lx, pos = %ld, "
- "r_width = %ld, f_width = %ld\n",
- reg, pos, reg_width, field_width);
-
- data = gpio_read_raw_reg(reg, reg_width);
- return (data >> pos) & mask;
-}
-
-static void gpio_write_reg(unsigned long reg, unsigned long reg_width,
- unsigned long field_width, unsigned long in_pos,
- unsigned long value)
-{
- unsigned long mask, pos;
-
- mask = (1 << field_width) - 1;
- pos = reg_width - ((in_pos + 1) * field_width);
-
- pr_debug("write_reg addr = %lx, value = %ld, pos = %ld, "
- "r_width = %ld, f_width = %ld\n",
- reg, value, pos, reg_width, field_width);
-
- mask = ~(mask << pos);
- value = value << pos;
-
- switch (reg_width) {
- case 8:
- __raw_writeb((__raw_readb(reg) & mask) | value, reg);
- break;
- case 16:
- __raw_writew((__raw_readw(reg) & mask) | value, reg);
- break;
- case 32:
- __raw_writel((__raw_readl(reg) & mask) | value, reg);
- break;
- }
-}
-
-static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio)
-{
- struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
- struct pinmux_data_reg *data_reg;
- int k, n;
-
- if (!enum_in_range(gpiop->enum_id, &gpioc->data))
- return -1;
-
- k = 0;
- while (1) {
- data_reg = gpioc->data_regs + k;
-
- if (!data_reg->reg_width)
- break;
-
- for (n = 0; n < data_reg->reg_width; n++) {
- if (data_reg->enum_ids[n] == gpiop->enum_id) {
- gpiop->flags &= ~PINMUX_FLAG_DREG;
- gpiop->flags |= (k << PINMUX_FLAG_DREG_SHIFT);
- gpiop->flags &= ~PINMUX_FLAG_DBIT;
- gpiop->flags |= (n << PINMUX_FLAG_DBIT_SHIFT);
- return 0;
- }
- }
- k++;
- }
-
- BUG();
-
- return -1;
-}
-
-static void setup_data_regs(struct pinmux_info *gpioc)
-{
- struct pinmux_data_reg *drp;
- int k;
-
- for (k = gpioc->first_gpio; k <= gpioc->last_gpio; k++)
- setup_data_reg(gpioc, k);
-
- k = 0;
- while (1) {
- drp = gpioc->data_regs + k;
-
- if (!drp->reg_width)
- break;
-
- drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width);
- k++;
- }
-}
-
-static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio,
- struct pinmux_data_reg **drp, int *bitp)
-{
- struct pinmux_gpio *gpiop = &gpioc->gpios[gpio];
- int k, n;
-
- if (!enum_in_range(gpiop->enum_id, &gpioc->data))
- return -1;
-
- k = (gpiop->flags & PINMUX_FLAG_DREG) >> PINMUX_FLAG_DREG_SHIFT;
- n = (gpiop->flags & PINMUX_FLAG_DBIT) >> PINMUX_FLAG_DBIT_SHIFT;
- *drp = gpioc->data_regs + k;
- *bitp = n;
- return 0;
-}
-
-static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id,
- struct pinmux_cfg_reg **crp, int *indexp,
- unsigned long **cntp)
-{
- struct pinmux_cfg_reg *config_reg;
- unsigned long r_width, f_width;
- int k, n;
-
- k = 0;
- while (1) {
- config_reg = gpioc->cfg_regs + k;
-
- r_width = config_reg->reg_width;
- f_width = config_reg->field_width;
-
- if (!r_width)
- break;
- for (n = 0; n < (r_width / f_width) * 1 << f_width; n++) {
- if (config_reg->enum_ids[n] == enum_id) {
- *crp = config_reg;
- *indexp = n;
- *cntp = &config_reg->cnt[n / (1 << f_width)];
- return 0;
- }
- }
- k++;
- }
-
- return -1;
-}
-
-static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned gpio,
- int pos, pinmux_enum_t *enum_idp)
-{
- pinmux_enum_t enum_id = gpioc->gpios[gpio].enum_id;
- pinmux_enum_t *data = gpioc->gpio_data;
- int k;
-
- if (!enum_in_range(enum_id, &gpioc->data)) {
- if (!enum_in_range(enum_id, &gpioc->mark)) {
- pr_err("non data/mark enum_id for gpio %d\n", gpio);
- return -1;
- }
- }
-
- if (pos) {
- *enum_idp = data[pos + 1];
- return pos + 1;
- }
-
- for (k = 0; k < gpioc->gpio_data_size; k++) {
- if (data[k] == enum_id) {
- *enum_idp = data[k + 1];
- return k + 1;
- }
- }
-
- pr_err("cannot locate data/mark enum_id for gpio %d\n", gpio);
- return -1;
-}
-
-static void write_config_reg(struct pinmux_info *gpioc,
- struct pinmux_cfg_reg *crp,
- int index)
-{
- unsigned long ncomb, pos, value;
-
- ncomb = 1 << crp->field_width;
- pos = index / ncomb;
- value = index % ncomb;
-
- gpio_write_reg(crp->reg, crp->reg_width, crp->field_width, pos, value);
-}
-
-static int check_config_reg(struct pinmux_info *gpioc,
- struct pinmux_cfg_reg *crp,
- int index)
-{
- unsigned long ncomb, pos, value;
-
- ncomb = 1 << crp->field_width;
- pos = index / ncomb;
- value = index % ncomb;
-
- if (gpio_read_reg(crp->reg, crp->reg_width,
- crp->field_width, pos) == value)
- return 0;
-
- return -1;
-}
-
-enum { GPIO_CFG_DRYRUN, GPIO_CFG_REQ, GPIO_CFG_FREE };
-
-static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio,
- int pinmux_type, int cfg_mode)
-{
- struct pinmux_cfg_reg *cr = NULL;
- pinmux_enum_t enum_id;
- struct pinmux_range *range;
- int in_range, pos, index;
- unsigned long *cntp;
-
- switch (pinmux_type) {
-
- case PINMUX_TYPE_FUNCTION:
- range = NULL;
- break;
-
- case PINMUX_TYPE_OUTPUT:
- range = &gpioc->output;
- break;
-
- case PINMUX_TYPE_INPUT:
- range = &gpioc->input;
- break;
-
- case PINMUX_TYPE_INPUT_PULLUP:
- range = &gpioc->input_pu;
- break;
-
- case PINMUX_TYPE_INPUT_PULLDOWN:
- range = &gpioc->input_pd;
- break;
-
- default:
- goto out_err;
- }
-
- pos = 0;
- enum_id = 0;
- index = 0;
- while (1) {
- pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id);
- if (pos <= 0)
- goto out_err;
-
- if (!enum_id)
- break;
-
- /* first check if this is a function enum */
- in_range = enum_in_range(enum_id, &gpioc->function);
- if (!in_range) {
- /* not a function enum */
- if (range) {
- /*
- * other range exists, so this pin is
- * a regular GPIO pin that now is being
- * bound to a specific direction.
- *
- * for this case we only allow function enums
- * and the enums that match the other range.
- */
- in_range = enum_in_range(enum_id, range);
-
- /*
- * special case pass through for fixed
- * input-only or output-only pins without
- * function enum register association.
- */
- if (in_range && enum_id == range->force)
- continue;
- } else {
- /*
- * no other range exists, so this pin
- * must then be of the function type.
- *
- * allow function type pins to select
- * any combination of function/in/out
- * in their MARK lists.
- */
- in_range = 1;
- }
- }
-
- if (!in_range)
- continue;
-
- if (get_config_reg(gpioc, enum_id, &cr, &index, &cntp) != 0)
- goto out_err;
-
- switch (cfg_mode) {
- case GPIO_CFG_DRYRUN:
- if (!*cntp || !check_config_reg(gpioc, cr, index))
- continue;
- break;
-
- case GPIO_CFG_REQ:
- write_config_reg(gpioc, cr, index);
- *cntp = *cntp + 1;
- break;
-
- case GPIO_CFG_FREE:
- *cntp = *cntp - 1;
- break;
- }
- }
-
- return 0;
- out_err:
- return -1;
-}
-
-static DEFINE_SPINLOCK(gpio_lock);
-
-static struct pinmux_info *chip_to_pinmux(struct gpio_chip *chip)
-{
- return container_of(chip, struct pinmux_info, chip);
-}
-
-static int sh_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
- struct pinmux_info *gpioc = chip_to_pinmux(chip);
- struct pinmux_data_reg *dummy;
- unsigned long flags;
- int i, ret, pinmux_type;
-
- ret = -EINVAL;
-
- if (!gpioc)
- goto err_out;
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- if ((gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE) != PINMUX_TYPE_NONE)
- goto err_unlock;
-
- /* setup pin function here if no data is associated with pin */
-
- if (get_data_reg(gpioc, offset, &dummy, &i) != 0)
- pinmux_type = PINMUX_TYPE_FUNCTION;
- else
- pinmux_type = PINMUX_TYPE_GPIO;
-
- if (pinmux_type == PINMUX_TYPE_FUNCTION) {
- if (pinmux_config_gpio(gpioc, offset,
- pinmux_type,
- GPIO_CFG_DRYRUN) != 0)
- goto err_unlock;
-
- if (pinmux_config_gpio(gpioc, offset,
- pinmux_type,
- GPIO_CFG_REQ) != 0)
- BUG();
- }
-
- gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
- gpioc->gpios[offset].flags |= pinmux_type;
-
- ret = 0;
- err_unlock:
- spin_unlock_irqrestore(&gpio_lock, flags);
- err_out:
- return ret;
-}
-
-static void sh_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
- struct pinmux_info *gpioc = chip_to_pinmux(chip);
- unsigned long flags;
- int pinmux_type;
-
- if (!gpioc)
- return;
-
- spin_lock_irqsave(&gpio_lock, flags);
-
- pinmux_type = gpioc->gpios[offset].flags & PINMUX_FLAG_TYPE;
- pinmux_config_gpio(gpioc, offset, pinmux_type, GPIO_CFG_FREE);
- gpioc->gpios[offset].flags &= ~PINMUX_FLAG_TYPE;
- gpioc->gpios[offset].flags |= PINMUX_TYPE_NONE;
-
- spin_unlock_irqrestore(&gpio_lock, flags);
-}
-
-static int pinmux_direction(struct pinmux_info *gpioc,
- unsigned gpio, int new_pinmux_type)
-{
- int pinmux_type;
- int ret = -EINVAL;
-
- if (!gpioc)
- goto err_out;
-
- pinmux_type = gpioc->gpios[gpio].flags & PINMUX_FLAG_TYPE;
-
- switch (pinmux_type) {
- case PINMUX_TYPE_GPIO:
- break;
- case PINMUX_TYPE_OUTPUT:
- case PINMUX_TYPE_INPUT:
- case PINMUX_TYPE_INPUT_PULLUP:
- case PINMUX_TYPE_INPUT_PULLDOWN:
- pinmux_config_gpio(gpioc, gpio, pinmux_type, GPIO_CFG_FREE);
- break;
- default:
- goto err_out;
- }
-
- if (pinmux_config_gpio(gpioc, gpio,
- new_pinmux_type,
- GPIO_CFG_DRYRUN) != 0)
- goto err_out;
-
- if (pinmux_config_gpio(gpioc, gpio,
- new_pinmux_type,
- GPIO_CFG_REQ) != 0)
- BUG();
-
- gpioc->gpios[gpio].flags &= ~PINMUX_FLAG_TYPE;
- gpioc->gpios[gpio].flags |= new_pinmux_type;
-
- ret = 0;
- err_out:
- return ret;
-}
-
-static int sh_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- struct pinmux_info *gpioc = chip_to_pinmux(chip);
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&gpio_lock, flags);
- ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_INPUT);
- spin_unlock_irqrestore(&gpio_lock, flags);
-
- return ret;
-}
-
-static void sh_gpio_set_value(struct pinmux_info *gpioc,
- unsigned gpio, int value)
-{
- struct pinmux_data_reg *dr = NULL;
- int bit = 0;
-
- if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0)
- BUG();
- else
- gpio_write_bit(dr, bit, value);
-}
-
-static int sh_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
- int value)
-{
- struct pinmux_info *gpioc = chip_to_pinmux(chip);
- unsigned long flags;
- int ret;
-
- sh_gpio_set_value(gpioc, offset, value);
- spin_lock_irqsave(&gpio_lock, flags);
- ret = pinmux_direction(gpioc, offset, PINMUX_TYPE_OUTPUT);
- spin_unlock_irqrestore(&gpio_lock, flags);
-
- return ret;
-}
-
-static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio)
-{
- struct pinmux_data_reg *dr = NULL;
- int bit = 0;
-
- if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0)
- return -EINVAL;
-
- return gpio_read_reg(dr->reg, dr->reg_width, 1, bit);
-}
-
-static int sh_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- return sh_gpio_get_value(chip_to_pinmux(chip), offset);
-}
-
-static void sh_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- sh_gpio_set_value(chip_to_pinmux(chip), offset, value);
-}
-
-int register_pinmux(struct pinmux_info *pip)
-{
- struct gpio_chip *chip = &pip->chip;
-
- pr_info("%s handling gpio %d -> %d\n",
- pip->name, pip->first_gpio, pip->last_gpio);
-
- setup_data_regs(pip);
-
- chip->request = sh_gpio_request;
- chip->free = sh_gpio_free;
- chip->direction_input = sh_gpio_direction_input;
- chip->get = sh_gpio_get;
- chip->direction_output = sh_gpio_direction_output;
- chip->set = sh_gpio_set;
-
- WARN_ON(pip->first_gpio != 0); /* needs testing */
-
- chip->label = pip->name;
- chip->owner = THIS_MODULE;
- chip->base = pip->first_gpio;
- chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1;
-
- return gpiochip_add(chip);
-}
-
-int unregister_pinmux(struct pinmux_info *pip)
-{
- pr_info("%s deregistering\n", pip->name);
-
- return gpiochip_remove(&pip->chip);
-}
diff --git a/drivers/sh/pm_runtime.c b/drivers/sh/pm_runtime.c
new file mode 100644
index 00000000000..72f63817a1a
--- /dev/null
+++ b/drivers/sh/pm_runtime.c
@@ -0,0 +1,111 @@
+/*
+ * Runtime PM support code
+ *
+ * Copyright (C) 2009-2010 Magnus Damm
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_clock.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/sh_clk.h>
+#include <linux/bitmap.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_PM_RUNTIME
+static int sh_pm_runtime_suspend(struct device *dev)
+{
+ int ret;
+
+ ret = pm_generic_runtime_suspend(dev);
+ if (ret) {
+ dev_err(dev, "failed to suspend device\n");
+ return ret;
+ }
+
+ ret = pm_clk_suspend(dev);
+ if (ret) {
+ dev_err(dev, "failed to suspend clock\n");
+ pm_generic_runtime_resume(dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sh_pm_runtime_resume(struct device *dev)
+{
+ int ret;
+
+ ret = pm_clk_resume(dev);
+ if (ret) {
+ dev_err(dev, "failed to resume clock\n");
+ return ret;
+ }
+
+ return pm_generic_runtime_resume(dev);
+}
+
+static struct dev_pm_domain default_pm_domain = {
+ .ops = {
+ .runtime_suspend = sh_pm_runtime_suspend,
+ .runtime_resume = sh_pm_runtime_resume,
+ USE_PLATFORM_PM_SLEEP_OPS
+ },
+};
+
+#define DEFAULT_PM_DOMAIN_PTR (&default_pm_domain)
+
+#else
+
+#define DEFAULT_PM_DOMAIN_PTR NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+static struct pm_clk_notifier_block platform_bus_notifier = {
+ .pm_domain = DEFAULT_PM_DOMAIN_PTR,
+ .con_ids = { NULL, },
+};
+
+static bool default_pm_on;
+
+static int __init sh_pm_runtime_init(void)
+{
+ if (IS_ENABLED(CONFIG_ARCH_SHMOBILE_MULTI)) {
+ if (!of_machine_is_compatible("renesas,emev2") &&
+ !of_machine_is_compatible("renesas,r7s72100") &&
+ !of_machine_is_compatible("renesas,r8a73a4") &&
+ !of_machine_is_compatible("renesas,r8a7740") &&
+ !of_machine_is_compatible("renesas,r8a7778") &&
+ !of_machine_is_compatible("renesas,r8a7779") &&
+ !of_machine_is_compatible("renesas,r8a7790") &&
+ !of_machine_is_compatible("renesas,r8a7791") &&
+ !of_machine_is_compatible("renesas,r8a7792") &&
+ !of_machine_is_compatible("renesas,r8a7793") &&
+ !of_machine_is_compatible("renesas,r8a7794") &&
+ !of_machine_is_compatible("renesas,sh7372") &&
+ !of_machine_is_compatible("renesas,sh73a0"))
+ return 0;
+ }
+
+ default_pm_on = true;
+ pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
+ return 0;
+}
+core_initcall(sh_pm_runtime_init);
+
+static int __init sh_pm_runtime_late_init(void)
+{
+ if (default_pm_on)
+ pm_genpd_poweroff_unused();
+ return 0;
+}
+late_initcall(sh_pm_runtime_late_init);