/[gxemul]/trunk/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

Diff of /trunk/src/devices/dev_footbridge.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 24 by dpavlin, Mon Oct 8 16:19:56 2007 UTC revision 34 by dpavlin, Mon Oct 8 16:21:17 2007 UTC
# Line 1  Line 1 
1  /*  /*
2   *  Copyright (C) 2005-2006  Anders Gavare.  All rights reserved.   *  Copyright (C) 2005-2007  Anders Gavare.  All rights reserved.
3   *   *
4   *  Redistribution and use in source and binary forms, with or without   *  Redistribution and use in source and binary forms, with or without
5   *  modification, are permitted provided that the following conditions are met:   *  modification, are permitted provided that the following conditions are met:
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *   *
27   *   *
28   *  $Id: dev_footbridge.c,v 1.43 2006/03/04 12:38:47 debug Exp $   *  $Id: dev_footbridge.c,v 1.55 2007/02/03 16:18:56 debug Exp $
29   *   *
30   *  Footbridge. Used in Netwinder and Cats.   *  Footbridge. Used in Netwinder and Cats.
31   *   *
# Line 34  Line 34 
34   *      o)  FIQs.   *      o)  FIQs.
35   *      o)  Pretty much everything else as well :)  (This entire thing   *      o)  Pretty much everything else as well :)  (This entire thing
36   *          is a quick hack to work primarily with NetBSD and OpenBSD   *          is a quick hack to work primarily with NetBSD and OpenBSD
37   *          as a guest OS.)   *          as guest OSes.)
38   */   */
39    
40  #include <stdio.h>  #include <stdio.h>
# Line 49  Line 49 
49  #include "machine.h"  #include "machine.h"
50  #include "memory.h"  #include "memory.h"
51  #include "misc.h"  #include "misc.h"
52    #include "timer.h"
53    
54    
55  #include "dc21285reg.h"  #include "dc21285reg.h"
56    
57  #define DEV_FOOTBRIDGE_TICK_SHIFT       14  #define DEV_FOOTBRIDGE_TICK_SHIFT       14
58  #define DEV_FOOTBRIDGE_LENGTH           0x400  #define DEV_FOOTBRIDGE_LENGTH           0x400
59  #define TIMER_POLL_THRESHOLD            15  
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():   *  dev_footbridge_tick():
129   *   *
130   *  The 4 footbridge timers should decrease every now and then, and cause   *  The 4 footbridge timers should decrease and cause interrupts. Periodic
131   *  interrupts. Periodic interrupts restart as soon as they are acknowledged,   *  interrupts restart as soon as they are acknowledged, non-periodic
132   *  non-periodic interrupts need to be "reloaded" to restart.   *  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)  void dev_footbridge_tick(struct cpu *cpu, void *extra)
138  {  {
139          int i;          int i;
140          struct footbridge_data *d = (struct footbridge_data *) extra;          struct footbridge_data *d = (struct footbridge_data *) extra;
141    
         if (!d->timer_being_read)  
                 d->timer_poll_mode = 0;  
   
142          for (i=0; i<N_FOOTBRIDGE_TIMERS; i++) {          for (i=0; i<N_FOOTBRIDGE_TIMERS; i++) {
143                  unsigned int amount = 1 << DEV_FOOTBRIDGE_TICK_SHIFT;                  if (d->timer_control[i] & TIMER_ENABLE) {
144                  if (d->timer_control[i] & TIMER_FCLK_16)                          if (d->pending_timer_interrupts[i] > 0) {
145                          amount >>= 4;                                  d->timer_value[i] = random() % d->timer_load[i];
146                  else if (d->timer_control[i] & TIMER_FCLK_256)                                  INTERRUPT_ASSERT(d->timer_irq[i]);
147                          amount >>= 8;                          }
148                    }
149            }
150    }
151    
                 if (d->timer_tick_countdown[i] == 0)  
                         continue;  
152    
153                  if (d->timer_value[i] > amount)  /*
154                          d->timer_value[i] -= amount;   *  footbridge_interrupt_assert():
155                  else   */
156                          d->timer_value[i] = 0;  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->timer_value[i] == 0) {          if ((d->irq_status & d->irq_enable) && !d->irq_asserted) {
162                          d->timer_tick_countdown[i] --;                  d->irq_asserted = 1;
163                          if (d->timer_tick_countdown[i] > 0)                  INTERRUPT_ASSERT(d->irq);
164                                  continue;          }
165    }
166                          if (d->timer_control[i] & TIMER_ENABLE)  
167                                  cpu_interrupt(cpu, IRQ_TIMER_1 + i);  
168                          d->timer_tick_countdown[i] = 0;  /*
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    
# Line 120  DEVICE_ACCESS(footbridge_isa) Line 199  DEVICE_ACCESS(footbridge_isa)
199          }          }
200    
201          x = cpu->machine->isa_pic_data.last_int;          x = cpu->machine->isa_pic_data.last_int;
         if (x == 0)  
                 cpu_interrupt_ack(cpu, 32 + x);  
   
202          if (x < 8)          if (x < 8)
203                  odata = cpu->machine->isa_pic_data.pic1->irq_base + x;                  odata = cpu->machine->isa_pic_data.pic1->irq_base + x;
204          else          else
# Line 136  DEVICE_ACCESS(footbridge_isa) Line 212  DEVICE_ACCESS(footbridge_isa)
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():   *  dev_footbridge_pci_access():
238   *   *
239   *  The Footbridge PCI configuration space is implemented as a direct memory   *  The Footbridge PCI configuration space is implemented as a direct memory
# Line 263  DEVICE_ACCESS(footbridge) Line 361  DEVICE_ACCESS(footbridge)
361          case IRQ_ENABLE_SET:          case IRQ_ENABLE_SET:
362                  if (writeflag == MEM_WRITE) {                  if (writeflag == MEM_WRITE) {
363                          d->irq_enable |= idata;                          d->irq_enable |= idata;
364                          cpu_interrupt(cpu, 64);                          if (d->irq_status & d->irq_enable)
365                                    INTERRUPT_ASSERT(d->irq);
366                            else
367                                    INTERRUPT_DEASSERT(d->irq);
368                  } else {                  } else {
369                          odata = d->irq_enable;                          odata = d->irq_enable;
370                          fatal("[ WARNING: footbridge read from "                          fatal("[ WARNING: footbridge read from "
# Line 275  DEVICE_ACCESS(footbridge) Line 376  DEVICE_ACCESS(footbridge)
376          case IRQ_ENABLE_CLEAR:          case IRQ_ENABLE_CLEAR:
377                  if (writeflag == MEM_WRITE) {                  if (writeflag == MEM_WRITE) {
378                          d->irq_enable &= ~idata;                          d->irq_enable &= ~idata;
379                          cpu_interrupt(cpu, 64);                          if (d->irq_status & d->irq_enable)
380                                    INTERRUPT_ASSERT(d->irq);
381                            else
382                                    INTERRUPT_DEASSERT(d->irq);
383                  } else {                  } else {
384                          odata = d->irq_enable;                          odata = d->irq_enable;
385                          fatal("[ WARNING: footbridge read from "                          fatal("[ WARNING: footbridge read from "
# Line 316  DEVICE_ACCESS(footbridge) Line 420  DEVICE_ACCESS(footbridge)
420                  if (writeflag == MEM_READ)                  if (writeflag == MEM_READ)
421                          odata = d->timer_load[timer_nr];                          odata = d->timer_load[timer_nr];
422                  else {                  else {
423                          d->timer_value[timer_nr] =                          d->timer_load[timer_nr] = idata & TIMER_MAX_VAL;
424                              d->timer_load[timer_nr] = idata & TIMER_MAX_VAL;                          reload_timer_value(cpu, d, timer_nr);
425                          debug("[ footbridge: timer %i (1-based), value %i ]\n",                          /*  debug("[ footbridge: timer %i (1-based), "
426                              timer_nr + 1, (int)d->timer_value[timer_nr]);                              "value %i ]\n", timer_nr + 1,
427                          d->timer_tick_countdown[timer_nr] = 1;                              (int)d->timer_value[timer_nr]);  */
428                          cpu_interrupt_ack(cpu, IRQ_TIMER_1 + timer_nr);                          INTERRUPT_DEASSERT(d->timer_irq[timer_nr]);
429                  }                  }
430                  break;                  break;
431    
432          case TIMER_1_VALUE:          case TIMER_1_VALUE:
433                  if (writeflag == MEM_READ) {                  if (writeflag == MEM_READ)
                         /*  
                          *  NOTE/TODO: This is INCORRECT but speeds up NetBSD  
                          *  and OpenBSD boot sequences: if the timer is polled  
                          *  "very often" (such as during bootup), then this  
                          *  causes the timers to expire quickly.  
                          */  
                         d->timer_being_read = 1;  
                         d->timer_poll_mode ++;  
                         if (d->timer_poll_mode >= TIMER_POLL_THRESHOLD) {  
                                 d->timer_poll_mode = TIMER_POLL_THRESHOLD;  
                                 dev_footbridge_tick(cpu, d);  
                                 dev_footbridge_tick(cpu, d);  
                                 dev_footbridge_tick(cpu, d);  
                         }  
434                          odata = d->timer_value[timer_nr];                          odata = d->timer_value[timer_nr];
435                          d->timer_being_read = 0;                  else
                 } else  
436                          d->timer_value[timer_nr] = idata & TIMER_MAX_VAL;                          d->timer_value[timer_nr] = idata & TIMER_MAX_VAL;
437                  break;                  break;
438    
# Line 359  DEVICE_ACCESS(footbridge) Line 448  DEVICE_ACCESS(footbridge)
448                                  exit(1);                                  exit(1);
449                          }                          }
450                          if (idata & TIMER_ENABLE) {                          if (idata & TIMER_ENABLE) {
451                                  d->timer_value[timer_nr] =                                  reload_timer_value(cpu, d, timer_nr);
452                                      d->timer_load[timer_nr];                          } else {
453                                  d->timer_tick_countdown[timer_nr] = 1;                                  d->pending_timer_interrupts[timer_nr] = 0;
454                          }                          }
455                          cpu_interrupt_ack(cpu, IRQ_TIMER_1 + timer_nr);                          INTERRUPT_DEASSERT(d->timer_irq[timer_nr]);
456                  }                  }
457                  break;                  break;
458    
459          case TIMER_1_CLEAR:          case TIMER_1_CLEAR:
460                  if (d->timer_control[timer_nr] & TIMER_MODE_PERIODIC) {                  if (d->timer_control[timer_nr] & TIMER_MODE_PERIODIC) {
461                          d->timer_value[timer_nr] = d->timer_load[timer_nr];                          reload_timer_value(cpu, d, timer_nr);
462                          d->timer_tick_countdown[timer_nr] = 1;                  }
463    
464                    if (d->pending_timer_interrupts[timer_nr] > 0) {
465                            d->pending_timer_interrupts[timer_nr] --;
466                  }                  }
467                  cpu_interrupt_ack(cpu, IRQ_TIMER_1 + timer_nr);  
468                    INTERRUPT_DEASSERT(d->timer_irq[timer_nr]);
469                  break;                  break;
470    
471          default:if (writeflag == MEM_READ) {          default:if (writeflag == MEM_READ) {
# Line 394  DEVICE_ACCESS(footbridge) Line 487  DEVICE_ACCESS(footbridge)
487  DEVINIT(footbridge)  DEVINIT(footbridge)
488  {  {
489          struct footbridge_data *d;          struct footbridge_data *d;
490            char irq_path[300], irq_path_isa[300];
491          uint64_t pci_addr = 0x7b000000;          uint64_t pci_addr = 0x7b000000;
492          int i;          int i;
493    
# Line 404  DEVINIT(footbridge) Line 498  DEVINIT(footbridge)
498          }          }
499          memset(d, 0, sizeof(struct footbridge_data));          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:  */          /*  DC21285 register access:  */
505          memory_device_register(devinit->machine->memory, devinit->name,          memory_device_register(devinit->machine->memory, devinit->name,
506              devinit->addr, DEV_FOOTBRIDGE_LENGTH,              devinit->addr, DEV_FOOTBRIDGE_LENGTH,
# Line 416  DEVINIT(footbridge) Line 513  DEVINIT(footbridge)
513          /*  The "fcom" console:  */          /*  The "fcom" console:  */
514          d->console_handle = console_start_slave(devinit->machine, "fcom", 0);          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:  */          /*  A PCI bus:  */
552          d->pcibus = bus_pci_init(          d->pcibus = bus_pci_init(
553              devinit->machine,              devinit->machine,
554              devinit->irq_nr,    /*  PCI controller irq  */              irq_path,
555              0x7c000000,         /*  PCI device io offset  */              0x7c000000,         /*  PCI device io offset  */
556              0x80000000,         /*  PCI device mem offset  */              0x80000000,         /*  PCI device mem offset  */
557              0x00000000,         /*  PCI port base  */              0x00000000,         /*  PCI port base  */
558              0x00000000,         /*  PCI mem base  */              0x00000000,         /*  PCI mem base  */
559              0,                  /*  PCI irq base: TODO  */              irq_path,           /*  PCI irq base  */
560              0x7c000000,         /*  ISA port base  */              0x7c000000,         /*  ISA port base  */
561              0x80000000,         /*  ISA mem base  */              0x80000000,         /*  ISA mem base  */
562              32);                /*  ISA port base  */              irq_path_isa);      /*  ISA port base  */
563    
564          /*  ... with some default devices for known machine types:  */          /*  ... with some default devices for known machine types:  */
565          switch (devinit->machine->machine_type) {          switch (devinit->machine->machine_type) {
# Line 444  DEVINIT(footbridge) Line 576  DEVINIT(footbridge)
576                      devinit->machine->memory, 0xc0, 11, 0, "symphony_83c553");                      devinit->machine->memory, 0xc0, 11, 0, "symphony_83c553");
577                  bus_pci_add(devinit->machine, d->pcibus,                  bus_pci_add(devinit->machine, d->pcibus,
578                      devinit->machine->memory, 0xc0, 11, 1, "symphony_82c105");                      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;                  break;
583          default:fatal("footbridge: unimplemented machine type.\n");          default:fatal("footbridge: unimplemented machine type.\n");
584                  exit(1);                  exit(1);
# Line 462  DEVINIT(footbridge) Line 597  DEVINIT(footbridge)
597          machine_add_tickfunction(devinit->machine,          machine_add_tickfunction(devinit->machine,
598              dev_footbridge_tick, d, DEV_FOOTBRIDGE_TICK_SHIFT, 0.0);              dev_footbridge_tick, d, DEV_FOOTBRIDGE_TICK_SHIFT, 0.0);
599    
600          devinit->return_ptr = d;          devinit->return_ptr = d->pcibus;
601          return 1;          return 1;
602  }  }
603    

Legend:
Removed from v.24  
changed lines
  Added in v.34

  ViewVC Help
Powered by ViewVC 1.1.26