Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/sh/kernel/cpu/hwblk.c
10819 views
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
9
static DEFINE_SPINLOCK(hwblk_lock);
10
11
static 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
26
static 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
38
static 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
48
void hwblk_cnt_inc(struct hwblk_info *info, int hwblk, int counter)
49
{
50
hwblk_mod_cnt(info, hwblk, counter, 1, 1);
51
}
52
53
void hwblk_cnt_dec(struct hwblk_info *info, int hwblk, int counter)
54
{
55
hwblk_mod_cnt(info, hwblk, counter, -1, 0);
56
}
57
58
void 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
77
void 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
96
struct hwblk_info *hwblk_info;
97
98
int __init hwblk_register(struct hwblk_info *info)
99
{
100
hwblk_info = info;
101
return 0;
102
}
103
104
int __init __weak arch_hwblk_init(void)
105
{
106
return 0;
107
}
108
109
int __weak arch_hwblk_sleep_mode(void)
110
{
111
return SUSP_SH_SLEEP;
112
}
113
114
int __init hwblk_init(void)
115
{
116
return arch_hwblk_init();
117
}
118
119
/* allow clocks to enable and disable hardware blocks */
120
static 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
129
static void sh_hwblk_clk_disable(struct clk *clk)
130
{
131
if (hwblk_info)
132
hwblk_disable(hwblk_info, clk->arch_flags);
133
}
134
135
static 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
141
int __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
161