1#include <linux/clk.h> 2#include <linux/compiler.h> 3#include <linux/io.h> 4#include <linux/spinlock.h> 5#include <asm/suspend.h> 6#include <asm/hwblk.h> 7#include <asm/clock.h> 8 9static DEFINE_SPINLOCK(hwblk_lock); 10 11static void hwblk_area_mod_cnt(struct hwblk_info *info, 12 int area, int counter, int value, int goal) 13{ 14 struct hwblk_area *hap = info->areas + area; 15 16 hap->cnt[counter] += value; 17 18 if (hap->cnt[counter] != goal) 19 return; 20 21 if (hap->flags & HWBLK_AREA_FLAG_PARENT) 22 hwblk_area_mod_cnt(info, hap->parent, counter, value, goal); 23} 24 25 26static int __hwblk_mod_cnt(struct hwblk_info *info, int hwblk, 27 int counter, int value, int goal) 28{ 29 struct hwblk *hp = info->hwblks + hwblk; 30 31 hp->cnt[counter] += value; 32 if (hp->cnt[counter] == goal) 33 hwblk_area_mod_cnt(info, hp->area, counter, value, goal); 34 35 return hp->cnt[counter]; 36} 37 38static void hwblk_mod_cnt(struct hwblk_info *info, int hwblk, 39 int counter, int value, int goal) 40{ 41 unsigned long flags; 42 43 spin_lock_irqsave(&hwblk_lock, flags); 44 __hwblk_mod_cnt(info, hwblk, counter, value, goal); 45 spin_unlock_irqrestore(&hwblk_lock, flags); 46} 47 48void hwblk_cnt_inc(struct hwblk_info *info, int hwblk, int counter) 49{ 50 hwblk_mod_cnt(info, hwblk, counter, 1, 1); 51} 52 53void hwblk_cnt_dec(struct hwblk_info *info, int hwblk, int counter) 54{ 55 hwblk_mod_cnt(info, hwblk, counter, -1, 0); 56} 57 58void hwblk_enable(struct hwblk_info *info, int hwblk) 59{ 60 struct hwblk *hp = info->hwblks + hwblk; 61 unsigned long tmp; 62 unsigned long flags; 63 int ret; 64 65 spin_lock_irqsave(&hwblk_lock, flags); 66 67 ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, 1, 1); 68 if (ret == 1) { 69 tmp = __raw_readl(hp->mstp); 70 tmp &= ~(1 << hp->bit); 71 __raw_writel(tmp, hp->mstp); 72 } 73 74 spin_unlock_irqrestore(&hwblk_lock, flags); 75} 76 77void hwblk_disable(struct hwblk_info *info, int hwblk) 78{ 79 struct hwblk *hp = info->hwblks + hwblk; 80 unsigned long tmp; 81 unsigned long flags; 82 int ret; 83 84 spin_lock_irqsave(&hwblk_lock, flags); 85 86 ret = __hwblk_mod_cnt(info, hwblk, HWBLK_CNT_USAGE, -1, 0); 87 if (ret == 0) { 88 tmp = __raw_readl(hp->mstp); 89 tmp |= 1 << hp->bit; 90 __raw_writel(tmp, hp->mstp); 91 } 92 93 spin_unlock_irqrestore(&hwblk_lock, flags); 94} 95 96struct hwblk_info *hwblk_info; 97 98int __init hwblk_register(struct hwblk_info *info) 99{ 100 hwblk_info = info; 101 return 0; 102} 103 104int __init __weak arch_hwblk_init(void) 105{ 106 return 0; 107} 108 109int __weak arch_hwblk_sleep_mode(void) 110{ 111 return SUSP_SH_SLEEP; 112} 113 114int __init hwblk_init(void) 115{ 116 return arch_hwblk_init(); 117} 118 119/* allow clocks to enable and disable hardware blocks */ 120static int sh_hwblk_clk_enable(struct clk *clk) 121{ 122 if (!hwblk_info) 123 return -ENOENT; 124 125 hwblk_enable(hwblk_info, clk->arch_flags); 126 return 0; 127} 128 129static void sh_hwblk_clk_disable(struct clk *clk) 130{ 131 if (hwblk_info) 132 hwblk_disable(hwblk_info, clk->arch_flags); 133} 134 135static struct clk_ops sh_hwblk_clk_ops = { 136 .enable = sh_hwblk_clk_enable, 137 .disable = sh_hwblk_clk_disable, 138 .recalc = followparent_recalc, 139}; 140 141int __init sh_hwblk_clk_register(struct clk *clks, int nr) 142{ 143 struct clk *clkp; 144 int ret = 0; 145 int k; 146 147 for (k = 0; !ret && (k < nr); k++) { 148 clkp = clks + k; 149 150 /* skip over clocks using hwblk 0 (HWBLK_UNKNOWN) */ 151 if (!clkp->arch_flags) 152 continue; 153 154 clkp->ops = &sh_hwblk_clk_ops; 155 ret |= clk_register(clkp); 156 } 157 158 return ret; 159} 160