#include #include #include #include #include #include #include #include static DEFINE_SPINLOCK(hwblk_lock); static void hwblk_area_mod_cnt(struct hwblk_info *info, int area, int counter, int value, int goal) { struct hwblk_area *hap = info->areas + area; hap->cnt[counter] += value; if (hap->cnt[counter] != goal) return; if (hap->flags & HWBLK_AREA_FLAG_PARENT) hwblk_area_mod_cnt(info, hap->parent, counter, value, goal); } static int __hwblk_mod_cnt(struct hwblk_info *info, int hwblk, int counter, int value, int goal) { struct hwblk *hp = info->hwblks + hwblk; hp->cnt[counter] += value; if (hp->cnt[counter] == goal) hwblk_area_mod_cnt(info, hp->area, counter, value, goal); return hp->cnt[counter]; } static void hwblk_mod_cnt(struct hwblk_info *info, int hwblk, int counter, int value, int goal) { unsigned long flags; spin_lock_irqsave(&hwblk_lock, flags); __hwblk_mod_cnt(info, hwblk, counter, value, goal); spin_unlock_irqrestore(&hwblk_lock, flags); } void hwblk_cnt_inc(struct hwblk_info *info, int hwblk, int counter) { hwblk_mod_cnt(info, hwblk, counter, 1, 1); } void hwblk_cnt_dec(struct hwblk_info *info, int hwblk, int counter) { hwblk_mod_cnt(info, hwblk, counter, -1, 0); } void hwblk_enable(struct hwblk_info *info, int hwblk) { struct hwblk *hp = info->hwblks + hwblk; unsigned long tmp; unsigned long flags; int ret; spin_lock_irqsave(&hwblk_lock, flags); ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, 1, 1); if (ret == 1) { tmp = __raw_readl(hp->mstp); tmp &= ~(1 << hp->bit); __raw_writel(tmp, hp->mstp); } spin_unlock_irqrestore(&hwblk_lock, flags); } void hwblk_disable(struct hwblk_info *info, int hwblk) { struct hwblk *hp = info->hwblks + hwblk; unsigned long tmp; unsigned long flags; int ret; spin_lock_irqsave(&hwblk_lock, flags); ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, -1, 0); if (ret == 0) { tmp = __raw_readl(hp->mstp); tmp |= 1 << hp->bit; __raw_writel(tmp, hp->mstp); } spin_unlock_irqrestore(&hwblk_lock, flags); } struct hwblk_info *hwblk_info; int __init hwblk_register(struct hwblk_info *info) { hwblk_info = info; return 0; } int __init __weak arch_hwblk_init(void) { return 0; } int __weak arch_hwblk_sleep_mode(void) { return SUSP_SH_SLEEP; } int __init hwblk_init(void) { return arch_hwblk_init(); } /* allow clocks to enable and disable hardware blocks */ static int sh_hwblk_clk_enable(struct clk *clk) { if (!hwblk_info) return -ENOENT; hwblk_enable(hwblk_info, clk->arch_flags); return 0; } static void sh_hwblk_clk_disable(struct clk *clk) { if (hwblk_info) hwblk_disable(hwblk_info, clk->arch_flags); } static struct clk_ops sh_hwblk_clk_ops = { .enable = sh_hwblk_clk_enable, .disable = sh_hwblk_clk_disable, .recalc = followparent_recalc, }; int __init sh_hwblk_clk_register(struct clk *clks, int nr) { struct clk *clkp; int ret = 0; int k; for (k = 0; !ret && (k < nr); k++) { clkp = clks + k; clkp->ops = &sh_hwblk_clk_ops; ret |= clk_register(clkp); } return ret; }