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