diff options
Diffstat (limited to 'arch/cris/arch-v32/kernel/arbiter.c')
-rw-r--r-- | arch/cris/arch-v32/kernel/arbiter.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/arch/cris/arch-v32/kernel/arbiter.c b/arch/cris/arch-v32/kernel/arbiter.c new file mode 100644 index 000000000000..3870d2fd5160 --- /dev/null +++ b/arch/cris/arch-v32/kernel/arbiter.c | |||
@@ -0,0 +1,297 @@ | |||
1 | /* | ||
2 | * Memory arbiter functions. Allocates bandwith through the | ||
3 | * arbiter and sets up arbiter breakpoints. | ||
4 | * | ||
5 | * The algorithm first assigns slots to the clients that has specified | ||
6 | * bandwith (e.g. ethernet) and then the remaining slots are divided | ||
7 | * on all the active clients. | ||
8 | * | ||
9 | * Copyright (c) 2004, 2005 Axis Communications AB. | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <asm/arch/hwregs/reg_map.h> | ||
14 | #include <asm/arch/hwregs/reg_rdwr.h> | ||
15 | #include <asm/arch/hwregs/marb_defs.h> | ||
16 | #include <asm/arch/arbiter.h> | ||
17 | #include <asm/arch/hwregs/intr_vect.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/signal.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <asm/io.h> | ||
23 | |||
24 | struct crisv32_watch_entry | ||
25 | { | ||
26 | unsigned long instance; | ||
27 | watch_callback* cb; | ||
28 | unsigned long start; | ||
29 | unsigned long end; | ||
30 | int used; | ||
31 | }; | ||
32 | |||
33 | #define NUMBER_OF_BP 4 | ||
34 | #define NBR_OF_CLIENTS 14 | ||
35 | #define NBR_OF_SLOTS 64 | ||
36 | #define SDRAM_BANDWIDTH 100000000 /* Some kind of expected value */ | ||
37 | #define INTMEM_BANDWIDTH 400000000 | ||
38 | #define NBR_OF_REGIONS 2 | ||
39 | |||
40 | static struct crisv32_watch_entry watches[NUMBER_OF_BP] = | ||
41 | { | ||
42 | {regi_marb_bp0}, | ||
43 | {regi_marb_bp1}, | ||
44 | {regi_marb_bp2}, | ||
45 | {regi_marb_bp3} | ||
46 | }; | ||
47 | |||
48 | static int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS]; | ||
49 | static int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS]; | ||
50 | static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH}; | ||
51 | |||
52 | DEFINE_SPINLOCK(arbiter_lock); | ||
53 | |||
54 | static irqreturn_t | ||
55 | crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs); | ||
56 | |||
57 | static void crisv32_arbiter_config(int region) | ||
58 | { | ||
59 | int slot; | ||
60 | int client; | ||
61 | int interval = 0; | ||
62 | int val[NBR_OF_SLOTS]; | ||
63 | |||
64 | for (slot = 0; slot < NBR_OF_SLOTS; slot++) | ||
65 | val[slot] = NBR_OF_CLIENTS + 1; | ||
66 | |||
67 | for (client = 0; client < NBR_OF_CLIENTS; client++) | ||
68 | { | ||
69 | int pos; | ||
70 | if (!requested_slots[region][client]) | ||
71 | continue; | ||
72 | interval = NBR_OF_SLOTS / requested_slots[region][client]; | ||
73 | pos = 0; | ||
74 | while (pos < NBR_OF_SLOTS) | ||
75 | { | ||
76 | if (val[pos] != NBR_OF_CLIENTS + 1) | ||
77 | pos++; | ||
78 | else | ||
79 | { | ||
80 | val[pos] = client; | ||
81 | pos += interval; | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | |||
86 | client = 0; | ||
87 | for (slot = 0; slot < NBR_OF_SLOTS; slot++) | ||
88 | { | ||
89 | if (val[slot] == NBR_OF_CLIENTS + 1) | ||
90 | { | ||
91 | int first = client; | ||
92 | while(!active_clients[region][client]) { | ||
93 | client = (client + 1) % NBR_OF_CLIENTS; | ||
94 | if (client == first) | ||
95 | break; | ||
96 | } | ||
97 | val[slot] = client; | ||
98 | client = (client + 1) % NBR_OF_CLIENTS; | ||
99 | } | ||
100 | if (region == EXT_REGION) | ||
101 | REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, val[slot]); | ||
102 | else if (region == INT_REGION) | ||
103 | REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, val[slot]); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | extern char _stext, _etext; | ||
108 | |||
109 | static void crisv32_arbiter_init(void) | ||
110 | { | ||
111 | static int initialized = 0; | ||
112 | |||
113 | if (initialized) | ||
114 | return; | ||
115 | |||
116 | initialized = 1; | ||
117 | |||
118 | /* CPU caches are active. */ | ||
119 | active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; | ||
120 | crisv32_arbiter_config(EXT_REGION); | ||
121 | crisv32_arbiter_config(INT_REGION); | ||
122 | |||
123 | if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, SA_INTERRUPT, | ||
124 | "arbiter", NULL)) | ||
125 | printk(KERN_ERR "Couldn't allocate arbiter IRQ\n"); | ||
126 | |||
127 | #ifndef CONFIG_ETRAX_KGDB | ||
128 | /* Global watch for writes to kernel text segment. */ | ||
129 | crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext, | ||
130 | arbiter_all_clients, arbiter_all_write, NULL); | ||
131 | #endif | ||
132 | } | ||
133 | |||
134 | |||
135 | |||
136 | int crisv32_arbiter_allocate_bandwith(int client, int region, | ||
137 | unsigned long bandwidth) | ||
138 | { | ||
139 | int i; | ||
140 | int total_assigned = 0; | ||
141 | int total_clients = 0; | ||
142 | int req; | ||
143 | |||
144 | crisv32_arbiter_init(); | ||
145 | |||
146 | for (i = 0; i < NBR_OF_CLIENTS; i++) | ||
147 | { | ||
148 | total_assigned += requested_slots[region][i]; | ||
149 | total_clients += active_clients[region][i]; | ||
150 | } | ||
151 | req = NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth); | ||
152 | |||
153 | if (total_assigned + total_clients + req + 1 > NBR_OF_SLOTS) | ||
154 | return -ENOMEM; | ||
155 | |||
156 | active_clients[region][client] = 1; | ||
157 | requested_slots[region][client] = req; | ||
158 | crisv32_arbiter_config(region); | ||
159 | |||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | int crisv32_arbiter_watch(unsigned long start, unsigned long size, | ||
164 | unsigned long clients, unsigned long accesses, | ||
165 | watch_callback* cb) | ||
166 | { | ||
167 | int i; | ||
168 | |||
169 | crisv32_arbiter_init(); | ||
170 | |||
171 | if (start > 0x80000000) { | ||
172 | printk("Arbiter: %lX doesn't look like a physical address", start); | ||
173 | return -EFAULT; | ||
174 | } | ||
175 | |||
176 | spin_lock(&arbiter_lock); | ||
177 | |||
178 | for (i = 0; i < NUMBER_OF_BP; i++) { | ||
179 | if (!watches[i].used) { | ||
180 | reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); | ||
181 | |||
182 | watches[i].used = 1; | ||
183 | watches[i].start = start; | ||
184 | watches[i].end = start + size; | ||
185 | watches[i].cb = cb; | ||
186 | |||
187 | REG_WR_INT(marb_bp, watches[i].instance, rw_first_addr, watches[i].start); | ||
188 | REG_WR_INT(marb_bp, watches[i].instance, rw_last_addr, watches[i].end); | ||
189 | REG_WR_INT(marb_bp, watches[i].instance, rw_op, accesses); | ||
190 | REG_WR_INT(marb_bp, watches[i].instance, rw_clients, clients); | ||
191 | |||
192 | if (i == 0) | ||
193 | intr_mask.bp0 = regk_marb_yes; | ||
194 | else if (i == 1) | ||
195 | intr_mask.bp1 = regk_marb_yes; | ||
196 | else if (i == 2) | ||
197 | intr_mask.bp2 = regk_marb_yes; | ||
198 | else if (i == 3) | ||
199 | intr_mask.bp3 = regk_marb_yes; | ||
200 | |||
201 | REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); | ||
202 | spin_unlock(&arbiter_lock); | ||
203 | |||
204 | return i; | ||
205 | } | ||
206 | } | ||
207 | spin_unlock(&arbiter_lock); | ||
208 | return -ENOMEM; | ||
209 | } | ||
210 | |||
211 | int crisv32_arbiter_unwatch(int id) | ||
212 | { | ||
213 | reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask); | ||
214 | |||
215 | crisv32_arbiter_init(); | ||
216 | |||
217 | spin_lock(&arbiter_lock); | ||
218 | |||
219 | if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) { | ||
220 | spin_unlock(&arbiter_lock); | ||
221 | return -EINVAL; | ||
222 | } | ||
223 | |||
224 | memset(&watches[id], 0, sizeof(struct crisv32_watch_entry)); | ||
225 | |||
226 | if (id == 0) | ||
227 | intr_mask.bp0 = regk_marb_no; | ||
228 | else if (id == 1) | ||
229 | intr_mask.bp2 = regk_marb_no; | ||
230 | else if (id == 2) | ||
231 | intr_mask.bp2 = regk_marb_no; | ||
232 | else if (id == 3) | ||
233 | intr_mask.bp3 = regk_marb_no; | ||
234 | |||
235 | REG_WR(marb, regi_marb, rw_intr_mask, intr_mask); | ||
236 | |||
237 | spin_unlock(&arbiter_lock); | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | extern void show_registers(struct pt_regs *regs); | ||
242 | |||
243 | static irqreturn_t | ||
244 | crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs) | ||
245 | { | ||
246 | reg_marb_r_masked_intr masked_intr = REG_RD(marb, regi_marb, r_masked_intr); | ||
247 | reg_marb_bp_r_brk_clients r_clients; | ||
248 | reg_marb_bp_r_brk_addr r_addr; | ||
249 | reg_marb_bp_r_brk_op r_op; | ||
250 | reg_marb_bp_r_brk_first_client r_first; | ||
251 | reg_marb_bp_r_brk_size r_size; | ||
252 | reg_marb_bp_rw_ack ack = {0}; | ||
253 | reg_marb_rw_ack_intr ack_intr = {.bp0=1,.bp1=1,.bp2=1,.bp3=1}; | ||
254 | struct crisv32_watch_entry* watch; | ||
255 | |||
256 | if (masked_intr.bp0) { | ||
257 | watch = &watches[0]; | ||
258 | ack_intr.bp0 = regk_marb_yes; | ||
259 | } else if (masked_intr.bp1) { | ||
260 | watch = &watches[1]; | ||
261 | ack_intr.bp1 = regk_marb_yes; | ||
262 | } else if (masked_intr.bp2) { | ||
263 | watch = &watches[2]; | ||
264 | ack_intr.bp2 = regk_marb_yes; | ||
265 | } else if (masked_intr.bp3) { | ||
266 | watch = &watches[3]; | ||
267 | ack_intr.bp3 = regk_marb_yes; | ||
268 | } else { | ||
269 | return IRQ_NONE; | ||
270 | } | ||
271 | |||
272 | /* Retrieve all useful information and print it. */ | ||
273 | r_clients = REG_RD(marb_bp, watch->instance, r_brk_clients); | ||
274 | r_addr = REG_RD(marb_bp, watch->instance, r_brk_addr); | ||
275 | r_op = REG_RD(marb_bp, watch->instance, r_brk_op); | ||
276 | r_first = REG_RD(marb_bp, watch->instance, r_brk_first_client); | ||
277 | r_size = REG_RD(marb_bp, watch->instance, r_brk_size); | ||
278 | |||
279 | printk("Arbiter IRQ\n"); | ||
280 | printk("Clients %X addr %X op %X first %X size %X\n", | ||
281 | REG_TYPE_CONV(int, reg_marb_bp_r_brk_clients, r_clients), | ||
282 | REG_TYPE_CONV(int, reg_marb_bp_r_brk_addr, r_addr), | ||
283 | REG_TYPE_CONV(int, reg_marb_bp_r_brk_op, r_op), | ||
284 | REG_TYPE_CONV(int, reg_marb_bp_r_brk_first_client, r_first), | ||
285 | REG_TYPE_CONV(int, reg_marb_bp_r_brk_size, r_size)); | ||
286 | |||
287 | REG_WR(marb_bp, watch->instance, rw_ack, ack); | ||
288 | REG_WR(marb, regi_marb, rw_ack_intr, ack_intr); | ||
289 | |||
290 | printk("IRQ occured at %lX\n", regs->erp); | ||
291 | |||
292 | if (watch->cb) | ||
293 | watch->cb(); | ||
294 | |||
295 | |||
296 | return IRQ_HANDLED; | ||
297 | } | ||