diff options
Diffstat (limited to 'drivers/sh')
-rw-r--r-- | drivers/sh/Makefile | 1 | ||||
-rw-r--r-- | drivers/sh/clk/core.c | 116 | ||||
-rw-r--r-- | drivers/sh/clk/cpg.c | 79 | ||||
-rw-r--r-- | drivers/sh/intc/core.c | 37 | ||||
-rw-r--r-- | drivers/sh/intc/internals.h | 7 | ||||
-rw-r--r-- | drivers/sh/intc/userimask.c | 16 | ||||
-rw-r--r-- | drivers/sh/pfc.c | 273 | ||||
-rw-r--r-- | drivers/sh/pm_runtime.c | 65 |
8 files changed, 372 insertions, 222 deletions
diff --git a/drivers/sh/Makefile b/drivers/sh/Makefile index 24e6cec0ae8..7139ad2f208 100644 --- a/drivers/sh/Makefile +++ b/drivers/sh/Makefile @@ -7,3 +7,4 @@ 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 dc8d022c07a..7715de2629c 100644 --- a/drivers/sh/clk/core.c +++ b/drivers/sh/clk/core.c @@ -25,7 +25,6 @@ #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> @@ -173,6 +172,26 @@ 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) @@ -205,9 +224,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; } @@ -339,7 +355,7 @@ static int clk_establish_mapping(struct clk *clk) */ if (!clk->parent) { clk->mapping = &dummy_mapping; - return 0; + goto out; } /* @@ -368,6 +384,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; } @@ -386,10 +405,12 @@ 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) @@ -665,89 +686,6 @@ static int __init clk_syscore_init(void) 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; - 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: - debugfs_remove_recursive(c->dentry); - return err; -} - -static int clk_debugfs_register(struct clk *c) -{ - int err; - struct clk *pa = c->parent; - - if (pa && !pa->dentry) { - err = clk_debugfs_register(pa); - if (err) - return err; - } - - if (!c->dentry) { - 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, &clock_list, 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 __init clk_late_init(void) { unsigned long flags; diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c index 82dd6fb1783..45fee368b09 100644 --- a/drivers/sh/clk/cpg.c +++ b/drivers/sh/clk/cpg.c @@ -15,15 +15,15 @@ static int sh_clk_mstp32_enable(struct clk *clk) { - __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit), - clk->enable_reg); + iowrite32(ioread32(clk->mapped_reg) & ~(1 << clk->enable_bit), + clk->mapped_reg); return 0; } static void sh_clk_mstp32_disable(struct clk *clk) { - __raw_writel(__raw_readl(clk->enable_reg) | (1 << clk->enable_bit), - clk->enable_reg); + iowrite32(ioread32(clk->mapped_reg) | (1 << clk->enable_bit), + clk->mapped_reg); } static struct clk_ops sh_clk_mstp32_clk_ops = { @@ -72,7 +72,7 @@ static unsigned long sh_clk_div6_recalc(struct clk *clk) clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, table, NULL); - idx = __raw_readl(clk->enable_reg) & 0x003f; + idx = ioread32(clk->mapped_reg) & 0x003f; return clk->freq_table[idx].frequency; } @@ -98,10 +98,10 @@ static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) if (ret < 0) return ret; - value = __raw_readl(clk->enable_reg) & + value = ioread32(clk->mapped_reg) & ~(((1 << clk->src_width) - 1) << clk->src_shift); - __raw_writel(value | (i << clk->src_shift), clk->enable_reg); + iowrite32(value | (i << clk->src_shift), clk->mapped_reg); /* Rebuild the frequency table */ clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, @@ -119,10 +119,10 @@ static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate) if (idx < 0) return idx; - value = __raw_readl(clk->enable_reg); + value = ioread32(clk->mapped_reg); value &= ~0x3f; value |= idx; - __raw_writel(value, clk->enable_reg); + iowrite32(value, clk->mapped_reg); return 0; } @@ -133,9 +133,9 @@ static int sh_clk_div6_enable(struct clk *clk) ret = sh_clk_div6_set_rate(clk, clk->rate); if (ret == 0) { - value = __raw_readl(clk->enable_reg); + value = ioread32(clk->mapped_reg); value &= ~0x100; /* clear stop bit to enable clock */ - __raw_writel(value, clk->enable_reg); + iowrite32(value, clk->mapped_reg); } return ret; } @@ -144,10 +144,10 @@ static void sh_clk_div6_disable(struct clk *clk) { unsigned long value; - value = __raw_readl(clk->enable_reg); + value = ioread32(clk->mapped_reg); value |= 0x100; /* stop clock */ value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */ - __raw_writel(value, clk->enable_reg); + iowrite32(value, clk->mapped_reg); } static struct clk_ops sh_clk_div6_clk_ops = { @@ -167,6 +167,38 @@ static struct clk_ops sh_clk_div6_reparent_clk_ops = { .set_parent = sh_clk_div6_set_parent, }; +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 = (ioread32(clk->mapped_reg) >> 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->parent = 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_div6_register_ops(struct clk *clks, int nr, struct clk_ops *ops) { @@ -190,8 +222,11 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, clkp->ops = ops; 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) + break; + + ret = sh_clk_init_parent(clkp); } return ret; @@ -217,7 +252,7 @@ static unsigned long sh_clk_div4_recalc(struct clk *clk) clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, table, &clk->arch_flags); - idx = (__raw_readl(clk->enable_reg) >> clk->enable_bit) & 0x000f; + idx = (ioread32(clk->mapped_reg) >> clk->enable_bit) & 0x000f; return clk->freq_table[idx].frequency; } @@ -235,15 +270,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 = ioread32(clk->mapped_reg) & ~(1 << 7); else - value = __raw_readl(clk->enable_reg) | (1 << 7); + value = ioread32(clk->mapped_reg) | (1 << 7); ret = clk_reparent(clk, parent); if (ret < 0) return ret; - __raw_writel(value, clk->enable_reg); + iowrite32(value, clk->mapped_reg); /* Rebiuld the frequency table */ clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, @@ -260,10 +295,10 @@ static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) if (idx < 0) return idx; - value = __raw_readl(clk->enable_reg); + value = ioread32(clk->mapped_reg); value &= ~(0xf << clk->enable_bit); value |= (idx << clk->enable_bit); - __raw_writel(value, clk->enable_reg); + iowrite32(value, clk->mapped_reg); if (d4t->kick) d4t->kick(clk); @@ -273,13 +308,13 @@ static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate) static int sh_clk_div4_enable(struct clk *clk) { - __raw_writel(__raw_readl(clk->enable_reg) & ~(1 << 8), clk->enable_reg); + iowrite32(ioread32(clk->mapped_reg) & ~(1 << 8), clk->mapped_reg); return 0; } static void sh_clk_div4_disable(struct clk *clk) { - __raw_writel(__raw_readl(clk->enable_reg) | (1 << 8), clk->enable_reg); + iowrite32(ioread32(clk->mapped_reg) | (1 << 8), clk->mapped_reg); } static struct clk_ops sh_clk_div4_clk_ops = { diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c index 8b7a141ff35..e53e449b4ec 100644 --- a/drivers/sh/intc/core.c +++ b/drivers/sh/intc/core.c @@ -25,7 +25,7 @@ #include <linux/stat.h> #include <linux/interrupt.h> #include <linux/sh_intc.h> -#include <linux/sysdev.h> +#include <linux/device.h> #include <linux/syscore_ops.h> #include <linux/list.h> #include <linux/spinlock.h> @@ -354,6 +354,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; @@ -386,6 +388,9 @@ static int intc_suspend(void) list_for_each_entry(d, &intc_list, list) { int irq; + if (d->skip_suspend) + continue; + /* enable wakeup irqs belonging to this intc controller */ for_each_active_irq(irq) { struct irq_data *data; @@ -409,6 +414,9 @@ static void intc_resume(void) list_for_each_entry(d, &intc_list, list) { int irq; + if (d->skip_suspend) + continue; + for_each_active_irq(irq) { struct irq_data *data; struct irq_chip *chip; @@ -434,46 +442,47 @@ struct syscore_ops intc_syscore_ops = { .resume = intc_resume, }; -struct sysdev_class intc_sysdev_class = { +struct bus_type intc_subsys = { .name = "intc", + .dev_name = "intc", }; static ssize_t -show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) +show_intc_name(struct device *dev, struct device_attribute *attr, char *buf) { struct intc_desc_int *d; - d = container_of(dev, struct intc_desc_int, sysdev); + d = container_of(dev, struct intc_desc_int, dev); return sprintf(buf, "%s\n", d->chip.name); } -static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); +static DEVICE_ATTR(name, S_IRUGO, show_intc_name, NULL); -static int __init register_intc_sysdevs(void) +static int __init register_intc_devs(void) { struct intc_desc_int *d; int error; register_syscore_ops(&intc_syscore_ops); - error = sysdev_class_register(&intc_sysdev_class); + 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/internals.h b/drivers/sh/intc/internals.h index 5b934851efa..b0e9155ff73 100644 --- a/drivers/sh/intc/internals.h +++ b/drivers/sh/intc/internals.h @@ -4,7 +4,7 @@ #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,7 +51,7 @@ struct intc_subgroup_entry { struct intc_desc_int { struct list_head list; - struct sys_device sysdev; + struct device dev; struct radix_tree_root tree; raw_spinlock_t lock; unsigned int index; @@ -67,6 +67,7 @@ struct intc_desc_int { struct intc_window *window; unsigned int nr_windows; struct irq_chip chip; + bool skip_suspend; }; @@ -157,7 +158,7 @@ void _intc_enable(struct irq_data *data, unsigned long handle); 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); diff --git a/drivers/sh/intc/userimask.c b/drivers/sh/intc/userimask.c index 56bf9336b92..e649ceaaa41 100644 --- a/drivers/sh/intc/userimask.c +++ b/drivers/sh/intc/userimask.c @@ -10,7 +10,7 @@ #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> @@ -20,15 +20,15 @@ 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; @@ -55,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) @@ -64,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/pfc.c b/drivers/sh/pfc.c index e67fe170d8d..522c6c46d1b 100644 --- a/drivers/sh/pfc.c +++ b/drivers/sh/pfc.c @@ -19,6 +19,75 @@ #include <linux/irq.h> #include <linux/bitops.h> #include <linux/gpio.h> +#include <linux/slab.h> +#include <linux/ioport.h> + +static void pfc_iounmap(struct pinmux_info *pip) +{ + int k; + + for (k = 0; k < pip->num_resources; k++) + if (pip->window[k].virt) + iounmap(pip->window[k].virt); + + kfree(pip->window); + pip->window = NULL; +} + +static int pfc_ioremap(struct pinmux_info *pip) +{ + struct resource *res; + int k; + + if (!pip->num_resources) + return 0; + + pip->window = kzalloc(pip->num_resources * sizeof(*pip->window), + GFP_NOWAIT); + if (!pip->window) + goto err1; + + for (k = 0; k < pip->num_resources; k++) { + res = pip->resource + k; + WARN_ON(resource_type(res) != IORESOURCE_MEM); + pip->window[k].phys = res->start; + pip->window[k].size = resource_size(res); + pip->window[k].virt = ioremap_nocache(res->start, + resource_size(res)); + if (!pip->window[k].virt) + goto err2; + } + + return 0; + +err2: + pfc_iounmap(pip); +err1: + return -1; +} + +static void __iomem *pfc_phys_to_virt(struct pinmux_info *pip, + unsigned long address) +{ + struct pfc_window *window; + int k; + + /* scan through physical windows and convert address */ + for (k = 0; k < pip->num_resources; k++) { + window = pip->window + k; + + if (address < window->phys) + continue; + + if (address >= (window->phys + window->size)) + continue; + + return window->virt + (address - window->phys); + } + + /* no windows defined, register must be 1:1 mapped virt:phys */ + return (void __iomem *)address; +} static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) { @@ -31,41 +100,54 @@ static int enum_in_range(pinmux_enum_t enum_id, struct pinmux_range *r) return 1; } -static unsigned long gpio_read_raw_reg(unsigned long reg, +static unsigned long gpio_read_raw_reg(void __iomem *mapped_reg, unsigned long reg_width) { switch (reg_width) { case 8: - return __raw_readb(reg); + return ioread8(mapped_reg); case 16: - return __raw_readw(reg); + return ioread16(mapped_reg); case 32: - return __raw_readl(reg); + return ioread32(mapped_reg); } BUG(); return 0; } -static void gpio_write_raw_reg(unsigned long reg, +static void gpio_write_raw_reg(void __iomem *mapped_reg, unsigned long reg_width, unsigned long data) { switch (reg_width) { case 8: - __raw_writeb(data, reg); + iowrite8(data, mapped_reg); return; case 16: - __raw_writew(data, reg); + iowrite16(data, mapped_reg); return; case 32: - __raw_writel(data, reg); + iowrite32(data, mapped_reg); return; } BUG(); } +static int gpio_read_bit(struct pinmux_data_reg *dr, + unsigned long in_pos) +{ + unsigned long pos; + + pos = dr->reg_width - (in_pos + 1); + + pr_debug("read_bit: addr = %lx, pos = %ld, " + "r_width = %ld\n", dr->reg, pos, dr->reg_width); + + return (gpio_read_raw_reg(dr->mapped_reg, dr->reg_width) >> pos) & 1; +} + static void gpio_write_bit(struct pinmux_data_reg *dr, unsigned long in_pos, unsigned long value) { @@ -82,53 +164,72 @@ static void gpio_write_bit(struct pinmux_data_reg *dr, else clear_bit(pos, &dr->reg_shadow); - gpio_write_raw_reg(dr->reg, dr->reg_width, dr->reg_shadow); + gpio_write_raw_reg(dr->mapped_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) +static void config_reg_helper(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + unsigned long in_pos, + void __iomem **mapped_regp, + unsigned long *maskp, + unsigned long *posp) { - unsigned long data, mask, pos; + int k; + + *mapped_regp = pfc_phys_to_virt(gpioc, crp->reg); - data = 0; - mask = (1 << field_width) - 1; - pos = reg_width - ((in_pos + 1) * field_width); + if (crp->field_width) { + *maskp = (1 << crp->field_width) - 1; + *posp = crp->reg_width - ((in_pos + 1) * crp->field_width); + } else { + *maskp = (1 << crp->var_field_width[in_pos]) - 1; + *posp = crp->reg_width; + for (k = 0; k <= in_pos; k++) + *posp -= crp->var_field_width[k]; + } +} - pr_debug("read_reg: addr = %lx, pos = %ld, " +static int read_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + unsigned long field) +{ + void __iomem *mapped_reg; + unsigned long mask, pos; + + config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); + + pr_debug("read_reg: addr = %lx, field = %ld, " "r_width = %ld, f_width = %ld\n", - reg, pos, reg_width, field_width); + crp->reg, field, crp->reg_width, crp->field_width); - data = gpio_read_raw_reg(reg, reg_width); - return (data >> pos) & mask; + return (gpio_read_raw_reg(mapped_reg, crp->reg_width) >> 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) +static void write_config_reg(struct pinmux_info *gpioc, + struct pinmux_cfg_reg *crp, + unsigned long field, unsigned long value) { - unsigned long mask, pos; + void __iomem *mapped_reg; + unsigned long mask, pos, data; - mask = (1 << field_width) - 1; - pos = reg_width - ((in_pos + 1) * field_width); + config_reg_helper(gpioc, crp, field, &mapped_reg, &mask, &pos); - pr_debug("write_reg addr = %lx, value = %ld, pos = %ld, " + pr_debug("write_reg addr = %lx, value = %ld, field = %ld, " "r_width = %ld, f_width = %ld\n", - reg, value, pos, reg_width, field_width); + crp->reg, value, field, crp->reg_width, crp->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; - } + data = gpio_read_raw_reg(mapped_reg, crp->reg_width); + data &= mask; + data |= value; + + if (gpioc->unlock_reg) + gpio_write_raw_reg(pfc_phys_to_virt(gpioc, gpioc->unlock_reg), + 32, ~data); + + gpio_write_raw_reg(mapped_reg, crp->reg_width, data); } static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) @@ -147,6 +248,8 @@ static int setup_data_reg(struct pinmux_info *gpioc, unsigned gpio) if (!data_reg->reg_width) break; + data_reg->mapped_reg = pfc_phys_to_virt(gpioc, data_reg->reg); + for (n = 0; n < data_reg->reg_width; n++) { if (data_reg->enum_ids[n] == gpiop->enum_id) { gpiop->flags &= ~PINMUX_FLAG_DREG; @@ -179,7 +282,8 @@ static void setup_data_regs(struct pinmux_info *gpioc) if (!drp->reg_width) break; - drp->reg_shadow = gpio_read_raw_reg(drp->reg, drp->reg_width); + drp->reg_shadow = gpio_read_raw_reg(drp->mapped_reg, + drp->reg_width); k++; } } @@ -201,12 +305,13 @@ static int get_data_reg(struct pinmux_info *gpioc, unsigned gpio, } static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, - struct pinmux_cfg_reg **crp, int *indexp, + struct pinmux_cfg_reg **crp, + int *fieldp, int *valuep, unsigned long **cntp) { struct pinmux_cfg_reg *config_reg; - unsigned long r_width, f_width; - int k, n; + unsigned long r_width, f_width, curr_width, ncomb; + int k, m, n, pos, bit_pos; k = 0; while (1) { @@ -217,13 +322,27 @@ static int get_config_reg(struct pinmux_info *gpioc, pinmux_enum_t enum_id, 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; + + pos = 0; + m = 0; + for (bit_pos = 0; bit_pos < r_width; bit_pos += curr_width) { + if (f_width) + curr_width = f_width; + else + curr_width = config_reg->var_field_width[m]; + + ncomb = 1 << curr_width; + for (n = 0; n < ncomb; n++) { + if (config_reg->enum_ids[pos + n] == enum_id) { + *crp = config_reg; + *fieldp = m; + *valuep = n; + *cntp = &config_reg->cnt[m]; + return 0; + } } + pos += ncomb; + m++; } k++; } @@ -261,36 +380,6 @@ static int get_gpio_enum_id(struct pinmux_info *gpioc, unsigned 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, @@ -299,7 +388,7 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, struct pinmux_cfg_reg *cr = NULL; pinmux_enum_t enum_id; struct pinmux_range *range; - int in_range, pos, index; + int in_range, pos, field, value; unsigned long *cntp; switch (pinmux_type) { @@ -330,7 +419,8 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, pos = 0; enum_id = 0; - index = 0; + field = 0; + value = 0; while (1) { pos = get_gpio_enum_id(gpioc, gpio, pos, &enum_id); if (pos <= 0) @@ -377,17 +467,19 @@ static int pinmux_config_gpio(struct pinmux_info *gpioc, unsigned gpio, if (!in_range) continue; - if (get_config_reg(gpioc, enum_id, &cr, &index, &cntp) != 0) + if (get_config_reg(gpioc, enum_id, &cr, + &field, &value, &cntp) != 0) goto out_err; switch (cfg_mode) { case GPIO_CFG_DRYRUN: - if (!*cntp || !check_config_reg(gpioc, cr, index)) + if (!*cntp || + (read_config_reg(gpioc, cr, field) != value)) continue; break; case GPIO_CFG_REQ: - write_config_reg(gpioc, cr, index); + write_config_reg(gpioc, cr, field, value); *cntp = *cntp + 1; break; @@ -564,7 +656,7 @@ static int sh_gpio_get_value(struct pinmux_info *gpioc, unsigned gpio) if (!gpioc || get_data_reg(gpioc, gpio, &dr, &bit) != 0) return -EINVAL; - return gpio_read_reg(dr->reg, dr->reg_width, 1, bit); + return gpio_read_bit(dr, bit); } static int sh_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -606,10 +698,15 @@ static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset) int register_pinmux(struct pinmux_info *pip) { struct gpio_chip *chip = &pip->chip; + int ret; pr_info("%s handling gpio %d -> %d\n", pip->name, pip->first_gpio, pip->last_gpio); + ret = pfc_ioremap(pip); + if (ret < 0) + return ret; + setup_data_regs(pip); chip->request = sh_gpio_request; @@ -627,12 +724,16 @@ int register_pinmux(struct pinmux_info *pip) chip->base = pip->first_gpio; chip->ngpio = (pip->last_gpio - pip->first_gpio) + 1; - return gpiochip_add(chip); + ret = gpiochip_add(chip); + if (ret < 0) + pfc_iounmap(pip); + + return ret; } int unregister_pinmux(struct pinmux_info *pip) { pr_info("%s deregistering\n", pip->name); - + pfc_iounmap(pip); 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..afe9282629b --- /dev/null +++ b/drivers/sh/pm_runtime.c @@ -0,0 +1,65 @@ +/* + * 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 default_platform_runtime_idle(struct device *dev) +{ + /* suspend synchronously to disable clocks immediately */ + return pm_runtime_suspend(dev); +} + +static struct dev_pm_domain default_pm_domain = { + .ops = { + .runtime_suspend = pm_clk_suspend, + .runtime_resume = pm_clk_resume, + .runtime_idle = default_platform_runtime_idle, + 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 int __init sh_pm_runtime_init(void) +{ + 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) +{ + pm_genpd_poweroff_unused(); + return 0; +} +late_initcall(sh_pm_runtime_late_init); |