/[gxemul]/upstream/0.4.5.1/src/devices/dev_footbridge.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.4.5.1/src/devices/dev_footbridge.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 41 - (show annotations)
Mon Oct 8 16:22:20 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 16085 byte(s)
0.4.5.1
1 /*
2 * Copyright (C) 2005-2007 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * $Id: dev_footbridge.c,v 1.55 2007/02/03 16:18:56 debug Exp $
29 *
30 * Footbridge. Used in Netwinder and Cats.
31 *
32 * TODO:
33 * o) Add actual support for the fcom serial port.
34 * o) FIQs.
35 * o) Pretty much everything else as well :) (This entire thing
36 * is a quick hack to work primarily with NetBSD and OpenBSD
37 * as guest OSes.)
38 */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "bus_pci.h"
45 #include "console.h"
46 #include "cpu.h"
47 #include "device.h"
48 #include "devices.h"
49 #include "machine.h"
50 #include "memory.h"
51 #include "misc.h"
52 #include "timer.h"
53
54
55 #include "dc21285reg.h"
56
57 #define DEV_FOOTBRIDGE_TICK_SHIFT 14
58 #define DEV_FOOTBRIDGE_LENGTH 0x400
59
60 #define N_FOOTBRIDGE_TIMERS 4
61
62 struct footbridge_data {
63 struct interrupt irq;
64
65 struct pci_data *pcibus;
66
67 int console_handle;
68
69 uint32_t timer_load[N_FOOTBRIDGE_TIMERS];
70 uint32_t timer_value[N_FOOTBRIDGE_TIMERS];
71 uint32_t timer_control[N_FOOTBRIDGE_TIMERS];
72
73 struct interrupt timer_irq[N_FOOTBRIDGE_TIMERS];
74 struct timer *timer[N_FOOTBRIDGE_TIMERS];
75 int pending_timer_interrupts[N_FOOTBRIDGE_TIMERS];
76
77 int irq_asserted;
78
79 uint32_t irq_status;
80 uint32_t irq_enable;
81
82 uint32_t fiq_status;
83 uint32_t fiq_enable;
84 };
85
86
87 static void timer_tick0(struct timer *t, void *extra)
88 { ((struct footbridge_data *)extra)->pending_timer_interrupts[0] ++; }
89 static void timer_tick1(struct timer *t, void *extra)
90 { ((struct footbridge_data *)extra)->pending_timer_interrupts[1] ++; }
91 static void timer_tick2(struct timer *t, void *extra)
92 { ((struct footbridge_data *)extra)->pending_timer_interrupts[2] ++; }
93 static void timer_tick3(struct timer *t, void *extra)
94 { ((struct footbridge_data *)extra)->pending_timer_interrupts[3] ++; }
95
96
97 static void reload_timer_value(struct cpu *cpu, struct footbridge_data *d,
98 int timer_nr)
99 {
100 double freq = (double)cpu->machine->emulated_hz;
101 int cycles = d->timer_load[timer_nr];
102
103 if (d->timer_control[timer_nr] & TIMER_FCLK_16)
104 cycles <<= 4;
105 else if (d->timer_control[timer_nr] & TIMER_FCLK_256)
106 cycles <<= 8;
107 freq /= (double)cycles;
108
109 d->timer_value[timer_nr] = d->timer_load[timer_nr];
110
111 /* printf("%i: %i -> %f Hz\n", timer_nr,
112 d->timer_load[timer_nr], freq); */
113
114 if (d->timer[timer_nr] == NULL) {
115 switch (timer_nr) {
116 case 0: d->timer[0] = timer_add(freq, timer_tick0, d); break;
117 case 1: d->timer[1] = timer_add(freq, timer_tick1, d); break;
118 case 2: d->timer[2] = timer_add(freq, timer_tick2, d); break;
119 case 3: d->timer[3] = timer_add(freq, timer_tick3, d); break;
120 }
121 } else {
122 timer_update_frequency(d->timer[timer_nr], freq);
123 }
124 }
125
126
127 /*
128 * dev_footbridge_tick():
129 *
130 * The 4 footbridge timers should decrease and cause interrupts. Periodic
131 * interrupts restart as soon as they are acknowledged, non-periodic
132 * interrupts need to be "reloaded" to restart.
133 *
134 * TODO: Hm. I thought I had solved this, but it didn't quite work.
135 * This needs to be re-checked against documentation, sometime.
136 */
137 void dev_footbridge_tick(struct cpu *cpu, void *extra)
138 {
139 int i;
140 struct footbridge_data *d = (struct footbridge_data *) extra;
141
142 for (i=0; i<N_FOOTBRIDGE_TIMERS; i++) {
143 if (d->timer_control[i] & TIMER_ENABLE) {
144 if (d->pending_timer_interrupts[i] > 0) {
145 d->timer_value[i] = random() % d->timer_load[i];
146 INTERRUPT_ASSERT(d->timer_irq[i]);
147 }
148 }
149 }
150 }
151
152
153 /*
154 * footbridge_interrupt_assert():
155 */
156 void footbridge_interrupt_assert(struct interrupt *interrupt)
157 {
158 struct footbridge_data *d = (struct footbridge_data *) interrupt->extra;
159 d->irq_status |= interrupt->line;
160
161 if ((d->irq_status & d->irq_enable) && !d->irq_asserted) {
162 d->irq_asserted = 1;
163 INTERRUPT_ASSERT(d->irq);
164 }
165 }
166
167
168 /*
169 * footbridge_interrupt_deassert():
170 */
171 void footbridge_interrupt_deassert(struct interrupt *interrupt)
172 {
173 struct footbridge_data *d = (struct footbridge_data *) interrupt->extra;
174 d->irq_status &= ~interrupt->line;
175
176 if (!(d->irq_status & d->irq_enable) && d->irq_asserted) {
177 d->irq_asserted = 0;
178 INTERRUPT_DEASSERT(d->irq);
179 }
180 }
181
182
183 /*
184 * dev_footbridge_isa_access():
185 *
186 * Reading the byte at 0x79000000 is a quicker way to figure out which ISA
187 * interrupt has occurred (and acknowledging it at the same time), than
188 * dealing with the legacy 0x20/0xa0 ISA ports.
189 */
190 DEVICE_ACCESS(footbridge_isa)
191 {
192 /* struct footbridge_data *d = extra; */
193 uint64_t idata = 0, odata = 0;
194 int x;
195
196 if (writeflag == MEM_WRITE) {
197 idata = memory_readmax64(cpu, data, len);
198 fatal("[ footbridge_isa: WARNING/TODO: write! ]\n");
199 }
200
201 x = cpu->machine->isa_pic_data.last_int;
202 if (x < 8)
203 odata = cpu->machine->isa_pic_data.pic1->irq_base + x;
204 else
205 odata = cpu->machine->isa_pic_data.pic2->irq_base + x - 8;
206
207 if (writeflag == MEM_READ)
208 memory_writemax64(cpu, data, len, odata);
209
210 return 1;
211 }
212
213
214 /*
215 * Reset pin at ISA port 0x338, at least in the NetWinder:
216 *
217 * TODO: NOT WORKING YET!
218 */
219 DEVICE_ACCESS(footbridge_reset)
220 {
221 uint64_t idata = 0;
222
223 if (writeflag == MEM_WRITE) {
224 idata = memory_readmax64(cpu, data, len);
225 if (idata & 0x40) {
226 debug("[ footbridge_reset: GP16: Halting. ]\n");
227 cpu->running = 0;
228 exit(1);
229 }
230 }
231
232 return 1;
233 }
234
235
236 /*
237 * dev_footbridge_pci_access():
238 *
239 * The Footbridge PCI configuration space is implemented as a direct memory
240 * space (i.e. not one port for addr and one port for data). This function
241 * translates that into bus_pci calls.
242 */
243 DEVICE_ACCESS(footbridge_pci)
244 {
245 struct footbridge_data *d = extra;
246 uint64_t idata = 0, odata = 0;
247 int bus, dev, func, reg;
248
249 if (writeflag == MEM_WRITE)
250 idata = memory_readmax64(cpu, data, len|MEM_PCI_LITTLE_ENDIAN);
251
252 /* Decompose the (direct) address into its components: */
253 bus_pci_decompose_1(relative_addr, &bus, &dev, &func, &reg);
254 bus_pci_setaddr(cpu, d->pcibus, bus, dev, func, reg);
255
256 if (bus == 255) {
257 fatal("[ footbridge DEBUG ERROR: bus 255 unlikely,"
258 " pc (might not be updated) = 0x%08x ]\n", (int)cpu->pc);
259 exit(1);
260 }
261
262 debug("[ footbridge pci: %s bus %i, device %i, function %i, register "
263 "%i ]\n", writeflag == MEM_READ? "read from" : "write to", bus,
264 dev, func, reg);
265
266 bus_pci_data_access(cpu, d->pcibus, writeflag == MEM_READ?
267 &odata : &idata, len, writeflag);
268
269 if (writeflag == MEM_READ)
270 memory_writemax64(cpu, data, len|MEM_PCI_LITTLE_ENDIAN, odata);
271
272 return 1;
273 }
274
275
276 /*
277 * dev_footbridge_access():
278 *
279 * The DC21285 registers.
280 */
281 DEVICE_ACCESS(footbridge)
282 {
283 struct footbridge_data *d = extra;
284 uint64_t idata = 0, odata = 0;
285 int timer_nr = 0;
286
287 if (writeflag == MEM_WRITE)
288 idata = memory_readmax64(cpu, data, len);
289
290 if (relative_addr >= TIMER_1_LOAD && relative_addr <= TIMER_4_CLEAR) {
291 timer_nr = (relative_addr >> 5) & (N_FOOTBRIDGE_TIMERS - 1);
292 relative_addr &= ~0x060;
293 }
294
295 switch (relative_addr) {
296
297 case VENDOR_ID:
298 odata = 0x1011; /* DC21285_VENDOR_ID */
299 break;
300
301 case DEVICE_ID:
302 odata = 0x1065; /* DC21285_DEVICE_ID */
303 break;
304
305 case 0x04:
306 case 0x0c:
307 case 0x10:
308 case 0x14:
309 case 0x18:
310 /* TODO. Written to by Linux. */
311 break;
312
313 case REVISION:
314 odata = 3; /* footbridge revision number */
315 break;
316
317 case PCI_ADDRESS_EXTENSION:
318 /* TODO: Written to by Linux. */
319 if (writeflag == MEM_WRITE && idata != 0)
320 fatal("[ footbridge: TODO: write to PCI_ADDRESS_"
321 "EXTENSION: 0x%llx ]\n", (long long)idata);
322 break;
323
324 case SA_CONTROL:
325 /* Read by Linux: */
326 odata = PCI_CENTRAL_FUNCTION;
327 break;
328
329 case UART_DATA:
330 if (writeflag == MEM_WRITE)
331 console_putchar(d->console_handle, idata);
332 break;
333
334 case UART_RX_STAT:
335 /* TODO */
336 odata = 0;
337 break;
338
339 case UART_FLAGS:
340 odata = UART_TX_EMPTY;
341 break;
342
343 case IRQ_STATUS:
344 if (writeflag == MEM_READ)
345 odata = d->irq_status & d->irq_enable;
346 else {
347 fatal("[ WARNING: footbridge write to irq status? ]\n");
348 exit(1);
349 }
350 break;
351
352 case IRQ_RAW_STATUS:
353 if (writeflag == MEM_READ)
354 odata = d->irq_status;
355 else {
356 fatal("[ footbridge write to irq_raw_status ]\n");
357 exit(1);
358 }
359 break;
360
361 case IRQ_ENABLE_SET:
362 if (writeflag == MEM_WRITE) {
363 d->irq_enable |= idata;
364 if (d->irq_status & d->irq_enable)
365 INTERRUPT_ASSERT(d->irq);
366 else
367 INTERRUPT_DEASSERT(d->irq);
368 } else {
369 odata = d->irq_enable;
370 fatal("[ WARNING: footbridge read from "
371 "ENABLE SET? ]\n");
372 exit(1);
373 }
374 break;
375
376 case IRQ_ENABLE_CLEAR:
377 if (writeflag == MEM_WRITE) {
378 d->irq_enable &= ~idata;
379 if (d->irq_status & d->irq_enable)
380 INTERRUPT_ASSERT(d->irq);
381 else
382 INTERRUPT_DEASSERT(d->irq);
383 } else {
384 odata = d->irq_enable;
385 fatal("[ WARNING: footbridge read from "
386 "ENABLE CLEAR? ]\n");
387 exit(1);
388 }
389 break;
390
391 case FIQ_STATUS:
392 if (writeflag == MEM_READ)
393 odata = d->fiq_status & d->fiq_enable;
394 else {
395 fatal("[ WARNING: footbridge write to fiq status? ]\n");
396 exit(1);
397 }
398 break;
399
400 case FIQ_RAW_STATUS:
401 if (writeflag == MEM_READ)
402 odata = d->fiq_status;
403 else {
404 fatal("[ footbridge write to fiq_raw_status ]\n");
405 exit(1);
406 }
407 break;
408
409 case FIQ_ENABLE_SET:
410 if (writeflag == MEM_WRITE)
411 d->fiq_enable |= idata;
412 break;
413
414 case FIQ_ENABLE_CLEAR:
415 if (writeflag == MEM_WRITE)
416 d->fiq_enable &= ~idata;
417 break;
418
419 case TIMER_1_LOAD:
420 if (writeflag == MEM_READ)
421 odata = d->timer_load[timer_nr];
422 else {
423 d->timer_load[timer_nr] = idata & TIMER_MAX_VAL;
424 reload_timer_value(cpu, d, timer_nr);
425 /* debug("[ footbridge: timer %i (1-based), "
426 "value %i ]\n", timer_nr + 1,
427 (int)d->timer_value[timer_nr]); */
428 INTERRUPT_DEASSERT(d->timer_irq[timer_nr]);
429 }
430 break;
431
432 case TIMER_1_VALUE:
433 if (writeflag == MEM_READ)
434 odata = d->timer_value[timer_nr];
435 else
436 d->timer_value[timer_nr] = idata & TIMER_MAX_VAL;
437 break;
438
439 case TIMER_1_CONTROL:
440 if (writeflag == MEM_READ)
441 odata = d->timer_control[timer_nr];
442 else {
443 d->timer_control[timer_nr] = idata;
444 if (idata & TIMER_FCLK_16 &&
445 idata & TIMER_FCLK_256) {
446 fatal("TODO: footbridge timer: "
447 "both 16 and 256?\n");
448 exit(1);
449 }
450 if (idata & TIMER_ENABLE) {
451 reload_timer_value(cpu, d, timer_nr);
452 } else {
453 d->pending_timer_interrupts[timer_nr] = 0;
454 }
455 INTERRUPT_DEASSERT(d->timer_irq[timer_nr]);
456 }
457 break;
458
459 case TIMER_1_CLEAR:
460 if (d->timer_control[timer_nr] & TIMER_MODE_PERIODIC) {
461 reload_timer_value(cpu, d, timer_nr);
462 }
463
464 if (d->pending_timer_interrupts[timer_nr] > 0) {
465 d->pending_timer_interrupts[timer_nr] --;
466 }
467
468 INTERRUPT_DEASSERT(d->timer_irq[timer_nr]);
469 break;
470
471 default:if (writeflag == MEM_READ) {
472 fatal("[ footbridge: read from 0x%x ]\n",
473 (int)relative_addr);
474 } else {
475 fatal("[ footbridge: write to 0x%x: 0x%llx ]\n",
476 (int)relative_addr, (long long)idata);
477 }
478 }
479
480 if (writeflag == MEM_READ)
481 memory_writemax64(cpu, data, len, odata);
482
483 return 1;
484 }
485
486
487 DEVINIT(footbridge)
488 {
489 struct footbridge_data *d;
490 char irq_path[300], irq_path_isa[300];
491 uint64_t pci_addr = 0x7b000000;
492 int i;
493
494 d = malloc(sizeof(struct footbridge_data));
495 if (d == NULL) {
496 fprintf(stderr, "out of memory\n");
497 exit(1);
498 }
499 memset(d, 0, sizeof(struct footbridge_data));
500
501 /* Connect to the CPU which this footbridge will interrupt: */
502 INTERRUPT_CONNECT(devinit->interrupt_path, d->irq);
503
504 /* DC21285 register access: */
505 memory_device_register(devinit->machine->memory, devinit->name,
506 devinit->addr, DEV_FOOTBRIDGE_LENGTH,
507 dev_footbridge_access, d, DM_DEFAULT, NULL);
508
509 /* ISA interrupt status/acknowledgement: */
510 memory_device_register(devinit->machine->memory, "footbridge_isa",
511 0x79000000, 8, dev_footbridge_isa_access, d, DM_DEFAULT, NULL);
512
513 /* The "fcom" console: */
514 d->console_handle = console_start_slave(devinit->machine, "fcom", 0);
515
516 /* Register 32 footbridge interrupts: */
517 snprintf(irq_path, sizeof(irq_path), "%s.footbridge",
518 devinit->interrupt_path);
519 for (i=0; i<32; i++) {
520 struct interrupt interrupt_template;
521 char tmpstr[200];
522
523 memset(&interrupt_template, 0, sizeof(interrupt_template));
524 interrupt_template.line = 1 << i;
525 snprintf(tmpstr, sizeof(tmpstr), "%s.%i", irq_path, i);
526 interrupt_template.name = tmpstr;
527
528 interrupt_template.extra = d;
529 interrupt_template.interrupt_assert =
530 footbridge_interrupt_assert;
531 interrupt_template.interrupt_deassert =
532 footbridge_interrupt_deassert;
533 interrupt_handler_register(&interrupt_template);
534
535 /* Connect locally to some interrupts: */
536 if (i>=IRQ_TIMER_1 && i<=IRQ_TIMER_4)
537 INTERRUPT_CONNECT(tmpstr, d->timer_irq[i-IRQ_TIMER_1]);
538 }
539
540 switch (devinit->machine->machine_type) {
541 case MACHINE_CATS:
542 snprintf(irq_path_isa, sizeof(irq_path_isa), "%s.10", irq_path);
543 break;
544 case MACHINE_NETWINDER:
545 snprintf(irq_path_isa, sizeof(irq_path_isa), "%s.11", irq_path);
546 break;
547 default:fatal("footbridge unimpl machine type\n");
548 exit(1);
549 }
550
551 /* A PCI bus: */
552 d->pcibus = bus_pci_init(
553 devinit->machine,
554 irq_path,
555 0x7c000000, /* PCI device io offset */
556 0x80000000, /* PCI device mem offset */
557 0x00000000, /* PCI port base */
558 0x00000000, /* PCI mem base */
559 irq_path, /* PCI irq base */
560 0x7c000000, /* ISA port base */
561 0x80000000, /* ISA mem base */
562 irq_path_isa); /* ISA port base */
563
564 /* ... with some default devices for known machine types: */
565 switch (devinit->machine->machine_type) {
566 case MACHINE_CATS:
567 bus_pci_add(devinit->machine, d->pcibus,
568 devinit->machine->memory, 0xc0, 7, 0, "ali_m1543");
569 bus_pci_add(devinit->machine, d->pcibus,
570 devinit->machine->memory, 0xc0, 10, 0, "dec21143");
571 bus_pci_add(devinit->machine, d->pcibus,
572 devinit->machine->memory, 0xc0, 16, 0, "ali_m5229");
573 break;
574 case MACHINE_NETWINDER:
575 bus_pci_add(devinit->machine, d->pcibus,
576 devinit->machine->memory, 0xc0, 11, 0, "symphony_83c553");
577 bus_pci_add(devinit->machine, d->pcibus,
578 devinit->machine->memory, 0xc0, 11, 1, "symphony_82c105");
579 memory_device_register(devinit->machine->memory,
580 "footbridge_reset", 0x7c000338, 1,
581 dev_footbridge_reset_access, d, DM_DEFAULT, NULL);
582 break;
583 default:fatal("footbridge: unimplemented machine type.\n");
584 exit(1);
585 }
586
587 /* PCI configuration space: */
588 memory_device_register(devinit->machine->memory,
589 "footbridge_pci", pci_addr, 0x1000000,
590 dev_footbridge_pci_access, d, DM_DEFAULT, NULL);
591
592 /* Timer ticks: */
593 for (i=0; i<N_FOOTBRIDGE_TIMERS; i++) {
594 d->timer_control[i] = TIMER_MODE_PERIODIC;
595 d->timer_load[i] = TIMER_MAX_VAL;
596 }
597 machine_add_tickfunction(devinit->machine,
598 dev_footbridge_tick, d, DEV_FOOTBRIDGE_TICK_SHIFT, 0.0);
599
600 devinit->return_ptr = d->pcibus;
601 return 1;
602 }
603

  ViewVC Help
Powered by ViewVC 1.1.26