]>
Commit | Line | Data |
---|---|---|
4ce7ff6e AJ |
1 | /* |
2 | * QEMU JAZZ RC4030 chipset | |
3 | * | |
4 | * Copyright (c) 2007-2008 Hervé Poussineau | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | ||
25 | #include "hw.h" | |
26 | #include "qemu-timer.h" | |
27 | ||
28 | //#define DEBUG_RC4030 | |
29 | ||
30 | #ifdef DEBUG_RC4030 | |
31 | static const char* irq_names[] = { "parallel", "floppy", "sound", "video", | |
32 | "network", "scsi", "keyboard", "mouse", "serial0", "serial1" }; | |
33 | #endif | |
34 | ||
35 | typedef struct rc4030State | |
36 | { | |
37 | uint32_t config; /* 0x0000: RC4030 config register */ | |
38 | uint32_t invalid_address_register; /* 0x0010: Invalid Address register */ | |
39 | ||
40 | /* DMA */ | |
41 | uint32_t dma_regs[8][4]; | |
42 | uint32_t dma_tl_base; /* 0x0018: DMA transl. table base */ | |
43 | uint32_t dma_tl_limit; /* 0x0020: DMA transl. table limit */ | |
44 | ||
45 | /* cache */ | |
46 | uint32_t remote_failed_address; /* 0x0038: Remote Failed Address */ | |
47 | uint32_t memory_failed_address; /* 0x0040: Memory Failed Address */ | |
48 | uint32_t cache_ptag; /* 0x0048: I/O Cache Physical Tag */ | |
49 | uint32_t cache_ltag; /* 0x0050: I/O Cache Logical Tag */ | |
50 | uint32_t cache_bmask; /* 0x0058: I/O Cache Byte Mask */ | |
51 | uint32_t cache_bwin; /* 0x0060: I/O Cache Buffer Window */ | |
52 | ||
53 | uint32_t offset208; | |
54 | uint32_t offset210; | |
55 | uint32_t nvram_protect; /* 0x0220: NV ram protect register */ | |
56 | uint32_t offset238; | |
57 | uint32_t rem_speed[15]; | |
58 | uint32_t imr_jazz; /* Local bus int enable mask */ | |
59 | uint32_t isr_jazz; /* Local bus int source */ | |
60 | ||
61 | /* timer */ | |
62 | QEMUTimer *periodic_timer; | |
63 | uint32_t itr; /* Interval timer reload */ | |
64 | ||
65 | uint32_t dummy32; | |
66 | qemu_irq timer_irq; | |
67 | qemu_irq jazz_bus_irq; | |
68 | } rc4030State; | |
69 | ||
70 | static void set_next_tick(rc4030State *s) | |
71 | { | |
72 | qemu_irq_lower(s->timer_irq); | |
73 | uint32_t hz; | |
74 | ||
75 | hz = 1000 / (s->itr + 1); | |
76 | ||
77 | qemu_mod_timer(s->periodic_timer, qemu_get_clock(vm_clock) + ticks_per_sec / hz); | |
78 | } | |
79 | ||
80 | /* called for accesses to rc4030 */ | |
81 | static uint32_t rc4030_readl(void *opaque, target_phys_addr_t addr) | |
82 | { | |
83 | rc4030State *s = opaque; | |
84 | uint32_t val; | |
85 | ||
86 | addr &= 0x3fff; | |
87 | switch (addr & ~0x3) { | |
88 | /* Global config register */ | |
89 | case 0x0000: | |
90 | val = s->config; | |
91 | break; | |
92 | /* Invalid Address register */ | |
93 | case 0x0010: | |
94 | val = s->invalid_address_register; | |
95 | break; | |
96 | /* DMA transl. table base */ | |
97 | case 0x0018: | |
98 | val = s->dma_tl_base; | |
99 | break; | |
100 | /* DMA transl. table limit */ | |
101 | case 0x0020: | |
102 | val = s->dma_tl_limit; | |
103 | break; | |
104 | /* Remote Failed Address */ | |
105 | case 0x0038: | |
106 | val = s->remote_failed_address; | |
107 | break; | |
108 | /* Memory Failed Address */ | |
109 | case 0x0040: | |
110 | val = s->memory_failed_address; | |
111 | break; | |
112 | /* I/O Cache Byte Mask */ | |
113 | case 0x0058: | |
114 | val = s->cache_bmask; | |
115 | /* HACK */ | |
116 | if (s->cache_bmask == (uint32_t)-1) | |
117 | s->cache_bmask = 0; | |
118 | break; | |
119 | /* Remote Speed Registers */ | |
120 | case 0x0070: | |
121 | case 0x0078: | |
122 | case 0x0080: | |
123 | case 0x0088: | |
124 | case 0x0090: | |
125 | case 0x0098: | |
126 | case 0x00a0: | |
127 | case 0x00a8: | |
128 | case 0x00b0: | |
129 | case 0x00b8: | |
130 | case 0x00c0: | |
131 | case 0x00c8: | |
132 | case 0x00d0: | |
133 | case 0x00d8: | |
134 | case 0x00e0: | |
135 | val = s->rem_speed[(addr - 0x0070) >> 3]; | |
136 | break; | |
137 | /* DMA channel base address */ | |
138 | case 0x0100: | |
139 | case 0x0108: | |
140 | case 0x0110: | |
141 | case 0x0118: | |
142 | case 0x0120: | |
143 | case 0x0128: | |
144 | case 0x0130: | |
145 | case 0x0138: | |
146 | case 0x0140: | |
147 | case 0x0148: | |
148 | case 0x0150: | |
149 | case 0x0158: | |
150 | case 0x0160: | |
151 | case 0x0168: | |
152 | case 0x0170: | |
153 | case 0x0178: | |
154 | case 0x0180: | |
155 | case 0x0188: | |
156 | case 0x0190: | |
157 | case 0x0198: | |
158 | case 0x01a0: | |
159 | case 0x01a8: | |
160 | case 0x01b0: | |
161 | case 0x01b8: | |
162 | case 0x01c0: | |
163 | case 0x01c8: | |
164 | case 0x01d0: | |
165 | case 0x01d8: | |
166 | case 0x01e0: | |
167 | case 0x1e8: | |
168 | case 0x01f0: | |
169 | case 0x01f8: | |
170 | { | |
171 | int entry = (addr - 0x0100) >> 5; | |
172 | int idx = (addr & 0x1f) >> 3; | |
173 | val = s->dma_regs[entry][idx]; | |
174 | } | |
175 | break; | |
176 | /* Offset 0x0208 */ | |
177 | case 0x0208: | |
178 | val = s->offset208; | |
179 | break; | |
180 | /* Offset 0x0210 */ | |
181 | case 0x0210: | |
182 | val = s->offset210; | |
183 | break; | |
184 | /* NV ram protect register */ | |
185 | case 0x0220: | |
186 | val = s->nvram_protect; | |
187 | break; | |
188 | /* Interval timer count */ | |
189 | case 0x0230: | |
190 | val = s->dummy32; | |
191 | qemu_irq_lower(s->timer_irq); | |
192 | break; | |
193 | /* Offset 0x0238 */ | |
194 | case 0x0238: | |
195 | val = s->offset238; | |
196 | break; | |
197 | default: | |
198 | #ifdef DEBUG_RC4030 | |
199 | printf("rc4030: invalid read [" TARGET_FMT_lx "]\n", addr); | |
200 | #endif | |
201 | val = 0; | |
202 | break; | |
203 | } | |
204 | ||
205 | #ifdef DEBUG_RC4030 | |
206 | if ((addr & ~3) != 0x230) | |
207 | printf("rc4030: read 0x%02x at " TARGET_FMT_lx "\n", val, addr); | |
208 | #endif | |
209 | ||
210 | return val; | |
211 | } | |
212 | ||
213 | static uint32_t rc4030_readw(void *opaque, target_phys_addr_t addr) | |
214 | { | |
215 | uint32_t v = rc4030_readl(opaque, addr & ~0x3); | |
216 | if (addr & 0x2) | |
217 | return v >> 16; | |
218 | else | |
219 | return v & 0xffff; | |
220 | } | |
221 | ||
222 | static uint32_t rc4030_readb(void *opaque, target_phys_addr_t addr) | |
223 | { | |
224 | uint32_t v = rc4030_readl(opaque, addr & ~0x3); | |
225 | return (v >> (8 * (addr & 0x3))) & 0xff; | |
226 | } | |
227 | ||
228 | static void rc4030_writel(void *opaque, target_phys_addr_t addr, uint32_t val) | |
229 | { | |
230 | rc4030State *s = opaque; | |
231 | addr &= 0x3fff; | |
232 | ||
233 | #ifdef DEBUG_RC4030 | |
234 | printf("rc4030: write 0x%02x at " TARGET_FMT_lx "\n", val, addr); | |
235 | #endif | |
236 | ||
237 | switch (addr & ~0x3) { | |
238 | /* Global config register */ | |
239 | case 0x0000: | |
240 | s->config = val; | |
241 | break; | |
242 | /* DMA transl. table base */ | |
243 | case 0x0018: | |
244 | s->dma_tl_base = val; | |
245 | break; | |
246 | /* DMA transl. table limit */ | |
247 | case 0x0020: | |
248 | s->dma_tl_limit = val; | |
249 | break; | |
250 | /* I/O Cache Physical Tag */ | |
251 | case 0x0048: | |
252 | s->cache_ptag = val; | |
253 | break; | |
254 | /* I/O Cache Logical Tag */ | |
255 | case 0x0050: | |
256 | s->cache_ltag = val; | |
257 | break; | |
258 | /* I/O Cache Byte Mask */ | |
259 | case 0x0058: | |
260 | s->cache_bmask |= val; /* HACK */ | |
261 | break; | |
262 | /* I/O Cache Buffer Window */ | |
263 | case 0x0060: | |
264 | s->cache_bwin = val; | |
265 | /* HACK */ | |
266 | if (s->cache_ltag == 0x80000001 && s->cache_bmask == 0xf0f0f0f) { | |
267 | target_phys_addr_t dests[] = { 4, 0, 8, 0x10 }; | |
268 | static int current = 0; | |
269 | target_phys_addr_t dest = 0 + dests[current]; | |
270 | uint8_t buf; | |
271 | current = (current + 1) % (sizeof(dests)/sizeof(dests[0])); | |
272 | buf = s->cache_bwin - 1; | |
273 | cpu_physical_memory_rw(dest, &buf, 1, 1); | |
274 | } | |
275 | break; | |
276 | /* Remote Speed Registers */ | |
277 | case 0x0070: | |
278 | case 0x0078: | |
279 | case 0x0080: | |
280 | case 0x0088: | |
281 | case 0x0090: | |
282 | case 0x0098: | |
283 | case 0x00a0: | |
284 | case 0x00a8: | |
285 | case 0x00b0: | |
286 | case 0x00b8: | |
287 | case 0x00c0: | |
288 | case 0x00c8: | |
289 | case 0x00d0: | |
290 | case 0x00d8: | |
291 | case 0x00e0: | |
292 | s->rem_speed[(addr - 0x0070) >> 3] = val; | |
293 | break; | |
294 | /* DMA channel base address */ | |
295 | case 0x0100: | |
296 | case 0x0108: | |
297 | case 0x0110: | |
298 | case 0x0118: | |
299 | case 0x0120: | |
300 | case 0x0128: | |
301 | case 0x0130: | |
302 | case 0x0138: | |
303 | case 0x0140: | |
304 | case 0x0148: | |
305 | case 0x0150: | |
306 | case 0x0158: | |
307 | case 0x0160: | |
308 | case 0x0168: | |
309 | case 0x0170: | |
310 | case 0x0178: | |
311 | case 0x0180: | |
312 | case 0x0188: | |
313 | case 0x0190: | |
314 | case 0x0198: | |
315 | case 0x01a0: | |
316 | case 0x01a8: | |
317 | case 0x01b0: | |
318 | case 0x01b8: | |
319 | case 0x01c0: | |
320 | case 0x01c8: | |
321 | case 0x01d0: | |
322 | case 0x01d8: | |
323 | case 0x01e0: | |
324 | case 0x1e8: | |
325 | case 0x01f0: | |
326 | case 0x01f8: | |
327 | { | |
328 | int entry = (addr - 0x0100) >> 5; | |
329 | int idx = (addr & 0x1f) >> 3; | |
330 | s->dma_regs[entry][idx] = val; | |
331 | } | |
332 | break; | |
333 | /* Offset 0x0210 */ | |
334 | case 0x0210: | |
335 | s->offset210 = val; | |
336 | break; | |
337 | /* Interval timer reload */ | |
338 | case 0x0228: | |
339 | s->itr = val; | |
340 | qemu_irq_lower(s->timer_irq); | |
341 | set_next_tick(s); | |
342 | break; | |
343 | default: | |
344 | #ifdef DEBUG_RC4030 | |
345 | printf("rc4030: invalid write of 0x%02x at [" TARGET_FMT_lx "]\n", val, addr); | |
346 | #endif | |
347 | break; | |
348 | } | |
349 | } | |
350 | ||
351 | static void rc4030_writew(void *opaque, target_phys_addr_t addr, uint32_t val) | |
352 | { | |
353 | uint32_t old_val = rc4030_readl(opaque, addr & ~0x3); | |
354 | ||
355 | if (addr & 0x2) | |
356 | val = (val << 16) | (old_val & 0x0000ffff); | |
357 | else | |
358 | val = val | (old_val & 0xffff0000); | |
359 | rc4030_writel(opaque, addr & ~0x3, val); | |
360 | } | |
361 | ||
362 | static void rc4030_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) | |
363 | { | |
364 | uint32_t old_val = rc4030_readl(opaque, addr & ~0x3); | |
365 | ||
366 | switch (addr & 3) { | |
367 | case 0: | |
368 | val = val | (old_val & 0xffffff00); | |
369 | break; | |
370 | case 1: | |
371 | val = (val << 8) | (old_val & 0xffff00ff); | |
372 | break; | |
373 | case 2: | |
374 | val = (val << 16) | (old_val & 0xff00ffff); | |
375 | break; | |
376 | case 3: | |
377 | val = (val << 24) | (old_val & 0x00ffffff); | |
378 | break; | |
379 | } | |
380 | rc4030_writel(opaque, addr & ~0x3, val); | |
381 | } | |
382 | ||
383 | static CPUReadMemoryFunc *rc4030_read[3] = { | |
384 | rc4030_readb, | |
385 | rc4030_readw, | |
386 | rc4030_readl, | |
387 | }; | |
388 | ||
389 | static CPUWriteMemoryFunc *rc4030_write[3] = { | |
390 | rc4030_writeb, | |
391 | rc4030_writew, | |
392 | rc4030_writel, | |
393 | }; | |
394 | ||
395 | static void update_jazz_irq(rc4030State *s) | |
396 | { | |
397 | uint16_t pending; | |
398 | ||
399 | pending = s->isr_jazz & s->imr_jazz; | |
400 | ||
401 | #ifdef DEBUG_RC4030 | |
402 | if (s->isr_jazz != 0) { | |
403 | uint32_t irq = 0; | |
404 | printf("jazz pending:"); | |
405 | for (irq = 0; irq < sizeof(irq_names)/sizeof(irq_names[0]); irq++) { | |
406 | if (s->isr_jazz & (1 << irq)) { | |
407 | printf(" %s", irq_names[irq]); | |
408 | if (!(s->imr_jazz & (1 << irq))) { | |
409 | printf("(ignored)"); | |
410 | } | |
411 | } | |
412 | } | |
413 | printf("\n"); | |
414 | } | |
415 | #endif | |
416 | ||
417 | if (pending != 0) | |
418 | qemu_irq_raise(s->jazz_bus_irq); | |
419 | else | |
420 | qemu_irq_lower(s->jazz_bus_irq); | |
421 | } | |
422 | ||
423 | static void rc4030_irq_jazz_request(void *opaque, int irq, int level) | |
424 | { | |
425 | rc4030State *s = opaque; | |
426 | ||
427 | if (level) { | |
428 | s->isr_jazz |= 1 << irq; | |
429 | } else { | |
430 | s->isr_jazz &= ~(1 << irq); | |
431 | } | |
432 | ||
433 | update_jazz_irq(s); | |
434 | } | |
435 | ||
436 | static void rc4030_periodic_timer(void *opaque) | |
437 | { | |
438 | rc4030State *s = opaque; | |
439 | ||
440 | set_next_tick(s); | |
441 | qemu_irq_raise(s->timer_irq); | |
442 | } | |
443 | ||
444 | static uint32_t int_readb(void *opaque, target_phys_addr_t addr) | |
445 | { | |
446 | rc4030State *s = opaque; | |
447 | uint32_t val; | |
448 | uint32_t irq; | |
449 | addr &= 0xfff; | |
450 | ||
451 | switch (addr) { | |
452 | case 0x00: { | |
453 | /* Local bus int source */ | |
454 | uint32_t pending = s->isr_jazz & s->imr_jazz; | |
455 | val = 0; | |
456 | irq = 0; | |
457 | while (pending) { | |
458 | if (pending & 1) { | |
459 | //printf("returning irq %s\n", irq_names[irq]); | |
460 | val = (irq + 1) << 2; | |
461 | break; | |
462 | } | |
463 | irq++; | |
464 | pending >>= 1; | |
465 | } | |
466 | break; | |
467 | } | |
468 | default: | |
469 | #ifdef DEBUG_RC4030 | |
470 | printf("rc4030: (interrupt controller) invalid read [" TARGET_FMT_lx "]\n", addr); | |
471 | #endif | |
472 | val = 0; | |
473 | } | |
474 | ||
475 | #ifdef DEBUG_RC4030 | |
476 | printf("rc4030: (interrupt controller) read 0x%02x at " TARGET_FMT_lx "\n", val, addr); | |
477 | #endif | |
478 | ||
479 | return val; | |
480 | } | |
481 | ||
482 | static uint32_t int_readw(void *opaque, target_phys_addr_t addr) | |
483 | { | |
484 | uint32_t v; | |
485 | v = int_readb(opaque, addr); | |
486 | v |= int_readb(opaque, addr + 1) << 8; | |
487 | return v; | |
488 | } | |
489 | ||
490 | static uint32_t int_readl(void *opaque, target_phys_addr_t addr) | |
491 | { | |
492 | uint32_t v; | |
493 | v = int_readb(opaque, addr); | |
494 | v |= int_readb(opaque, addr + 1) << 8; | |
495 | v |= int_readb(opaque, addr + 2) << 16; | |
496 | v |= int_readb(opaque, addr + 3) << 24; | |
497 | return v; | |
498 | } | |
499 | ||
500 | static void int_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) | |
501 | { | |
502 | rc4030State *s = opaque; | |
503 | addr &= 0xfff; | |
504 | ||
505 | #ifdef DEBUG_RC4030 | |
506 | printf("rc4030: (interrupt controller) write 0x%02x at " TARGET_FMT_lx "\n", val, addr); | |
507 | #endif | |
508 | ||
509 | switch (addr) { | |
510 | /* Local bus int enable mask */ | |
511 | case 0x02: | |
512 | s->imr_jazz = (s->imr_jazz & 0xff00) | (val << 0); update_jazz_irq(s); | |
513 | break; | |
514 | case 0x03: | |
515 | s->imr_jazz = (s->imr_jazz & 0x00ff) | (val << 8); update_jazz_irq(s); | |
516 | break; | |
517 | default: | |
518 | #ifdef DEBUG_RC4030 | |
519 | printf("rc4030: (interrupt controller) invalid write of 0x%02x at [" TARGET_FMT_lx "]\n", val, addr); | |
520 | #endif | |
521 | break; | |
522 | } | |
523 | } | |
524 | ||
525 | static void int_writew(void *opaque, target_phys_addr_t addr, uint32_t val) | |
526 | { | |
527 | int_writeb(opaque, addr, val & 0xff); | |
528 | int_writeb(opaque, addr + 1, (val >> 8) & 0xff); | |
529 | } | |
530 | ||
531 | static void int_writel(void *opaque, target_phys_addr_t addr, uint32_t val) | |
532 | { | |
533 | int_writeb(opaque, addr, val & 0xff); | |
534 | int_writeb(opaque, addr + 1, (val >> 8) & 0xff); | |
535 | int_writeb(opaque, addr + 2, (val >> 16) & 0xff); | |
536 | int_writeb(opaque, addr + 3, (val >> 24) & 0xff); | |
537 | } | |
538 | ||
539 | static CPUReadMemoryFunc *int_read[3] = { | |
540 | int_readb, | |
541 | int_readw, | |
542 | int_readl, | |
543 | }; | |
544 | ||
545 | static CPUWriteMemoryFunc *int_write[3] = { | |
546 | int_writeb, | |
547 | int_writew, | |
548 | int_writel, | |
549 | }; | |
550 | ||
551 | #define G364_512KB_RAM (0x0) | |
552 | #define G364_2MB_RAM (0x1) | |
553 | #define G364_8MB_RAM (0x2) | |
554 | #define G364_32MB_RAM (0x3) | |
555 | ||
556 | static void rc4030_reset(void *opaque) | |
557 | { | |
558 | rc4030State *s = opaque; | |
559 | int i; | |
560 | ||
561 | s->config = (G364_2MB_RAM << 8) | 0x04; | |
562 | s->invalid_address_register = 0; | |
563 | ||
564 | memset(s->dma_regs, 0, sizeof(s->dma_regs)); | |
565 | s->dma_tl_base = s->dma_tl_limit = 0; | |
566 | ||
567 | s->remote_failed_address = s->memory_failed_address = 0; | |
568 | s->cache_ptag = s->cache_ltag = 0; | |
569 | s->cache_bmask = s->cache_bwin = 0; | |
570 | ||
571 | s->offset208 = 0; | |
572 | s->offset210 = 0x18186; | |
573 | s->nvram_protect = 7; | |
574 | s->offset238 = 7; | |
575 | for (i = 0; i < 15; i++) | |
576 | s->rem_speed[i] = 7; | |
577 | s->imr_jazz = s->isr_jazz = 0; | |
578 | ||
579 | s->itr = 0; | |
580 | s->dummy32 = 0; | |
581 | ||
582 | qemu_irq_lower(s->timer_irq); | |
583 | qemu_irq_lower(s->jazz_bus_irq); | |
584 | } | |
585 | ||
586 | qemu_irq *rc4030_init(qemu_irq timer, qemu_irq jazz_bus) | |
587 | { | |
588 | rc4030State *s; | |
589 | int s_chipset, s_int; | |
590 | ||
591 | s = qemu_mallocz(sizeof(rc4030State)); | |
592 | if (!s) | |
593 | return NULL; | |
594 | ||
595 | s->periodic_timer = qemu_new_timer(vm_clock, rc4030_periodic_timer, s); | |
596 | s->timer_irq = timer; | |
597 | s->jazz_bus_irq = jazz_bus; | |
598 | ||
599 | qemu_register_reset(rc4030_reset, s); | |
600 | rc4030_reset(s); | |
601 | ||
602 | s_chipset = cpu_register_io_memory(0, rc4030_read, rc4030_write, s); | |
603 | cpu_register_physical_memory(0x80000000, 0x300, s_chipset); | |
604 | s_int = cpu_register_io_memory(0, int_read, int_write, s); | |
605 | cpu_register_physical_memory(0xf0000000, 0x00001000, s_int); | |
606 | ||
607 | return qemu_allocate_irqs(rc4030_irq_jazz_request, s, 16); | |
608 | } |