Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  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}