ARM Versatile Platform Baseboard emulation.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1804 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
		
							parent
							
								
									95219897ff
								
							
						
					
					
						commit
						cdbdb648b7
					
				|  | @ -339,7 +339,8 @@ VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o | |||
| endif | ||||
| endif | ||||
| ifeq ($(TARGET_BASE_ARCH), arm) | ||||
| VL_OBJS+= integratorcp.o ps2.o smc91c111.o pl110.o | ||||
| VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o | ||||
| VL_OBJS+= pl011.o pl050.o pl080.o pl110.o pl190.o | ||||
| endif | ||||
| ifdef CONFIG_GDBSTUB | ||||
| VL_OBJS+=gdbstub.o  | ||||
|  |  | |||
|  | @ -0,0 +1,73 @@ | |||
| /* 
 | ||||
|  * Generic ARM Programmable Interrupt Controller support. | ||||
|  * | ||||
|  * Copyright (c) 2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the LGPL | ||||
|  */ | ||||
| 
 | ||||
| #include "vl.h" | ||||
| #include "arm_pic.h" | ||||
| 
 | ||||
| /* Stub functions for hardware that doesn't exist.  */ | ||||
| void pic_set_irq(int irq, int level) | ||||
| { | ||||
|     cpu_abort(cpu_single_env, "pic_set_irq"); | ||||
| } | ||||
| 
 | ||||
| void pic_info(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void irq_info(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void pic_set_irq_new(void *opaque, int irq, int level) | ||||
| { | ||||
|     arm_pic_handler *p = (arm_pic_handler *)opaque; | ||||
|     /* Call the real handler.  */ | ||||
|     (*p)(opaque, irq, level); | ||||
| } | ||||
| 
 | ||||
| /* Model the IRQ/FIQ CPU interrupt lines as a two input interrupt controller.
 | ||||
|    Input 0 is IRQ and input 1 is FIQ.  */ | ||||
| typedef struct | ||||
| { | ||||
|     arm_pic_handler handler; | ||||
|     CPUState *cpu_env; | ||||
| } arm_pic_cpu_state; | ||||
| 
 | ||||
| static void arm_pic_cpu_handler(void *opaque, int irq, int level) | ||||
| { | ||||
|     arm_pic_cpu_state *s = (arm_pic_cpu_state *)opaque; | ||||
|     switch (irq) { | ||||
|     case ARM_PIC_CPU_IRQ: | ||||
|         if (level) | ||||
|             cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); | ||||
|         else | ||||
|             cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); | ||||
|         break; | ||||
|     case ARM_PIC_CPU_FIQ: | ||||
|         if (level) | ||||
|             cpu_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ); | ||||
|         else | ||||
|             cpu_reset_interrupt(s->cpu_env, CPU_INTERRUPT_FIQ); | ||||
|         break; | ||||
|     default: | ||||
|         cpu_abort(s->cpu_env, "arm_pic_cpu_handler: Bad interrput line %d\n", | ||||
|                   irq); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void *arm_pic_init_cpu(CPUState *env) | ||||
| { | ||||
|     arm_pic_cpu_state *s; | ||||
|      | ||||
|     s = (arm_pic_cpu_state *)malloc(sizeof(arm_pic_cpu_state)); | ||||
|     s->handler = arm_pic_cpu_handler; | ||||
|     s->cpu_env = env; | ||||
|     return s; | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| /* 
 | ||||
|  * Generic ARM Programmable Interrupt Controller support. | ||||
|  * | ||||
|  * Copyright (c) 2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the LGPL. | ||||
|  * | ||||
|  * Arm hardware uses a wide variety of interrupt handling hardware. | ||||
|  * This provides a generic framework for connecting interrupt sources and | ||||
|  * inputs. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef ARM_INTERRUPT_H | ||||
| #define ARM_INTERRUPT_H 1 | ||||
| 
 | ||||
| /* The first element of an individual PIC state structures should
 | ||||
|    be a pointer to the handler routine.  */ | ||||
| typedef void (*arm_pic_handler)(void *opaque, int irq, int level); | ||||
| 
 | ||||
| /* The CPU is also modeled as an interrupt controller.  */ | ||||
| #define ARM_PIC_CPU_IRQ 0 | ||||
| #define ARM_PIC_CPU_FIQ 1 | ||||
| void *arm_pic_init_cpu(CPUState *env); | ||||
| 
 | ||||
| #endif /* !ARM_INTERRUPT_H */ | ||||
| 
 | ||||
|  | @ -0,0 +1,383 @@ | |||
| /* 
 | ||||
|  * ARM PrimeCell Timer modules. | ||||
|  * | ||||
|  * Copyright (c) 2005-2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the GPL. | ||||
|  */ | ||||
| 
 | ||||
| #include "vl.h" | ||||
| #include "arm_pic.h" | ||||
| 
 | ||||
| /* Common timer implementation.  */ | ||||
| 
 | ||||
| #define TIMER_CTRL_ONESHOT      (1 << 0) | ||||
| #define TIMER_CTRL_32BIT        (1 << 1) | ||||
| #define TIMER_CTRL_DIV1         (0 << 2) | ||||
| #define TIMER_CTRL_DIV16        (1 << 2) | ||||
| #define TIMER_CTRL_DIV256       (2 << 2) | ||||
| #define TIMER_CTRL_IE           (1 << 5) | ||||
| #define TIMER_CTRL_PERIODIC     (1 << 6) | ||||
| #define TIMER_CTRL_ENABLE       (1 << 7) | ||||
| 
 | ||||
| typedef struct { | ||||
|     int64_t next_time; | ||||
|     int64_t expires; | ||||
|     int64_t loaded; | ||||
|     QEMUTimer *timer; | ||||
|     uint32_t control; | ||||
|     uint32_t count; | ||||
|     uint32_t limit; | ||||
|     int raw_freq; | ||||
|     int freq; | ||||
|     int int_level; | ||||
|     void *pic; | ||||
|     int irq; | ||||
| } arm_timer_state; | ||||
| 
 | ||||
| /* Calculate the new expiry time of the given timer.  */ | ||||
| 
 | ||||
| static void arm_timer_reload(arm_timer_state *s) | ||||
| { | ||||
|     int64_t delay; | ||||
| 
 | ||||
|     s->loaded = s->expires; | ||||
|     delay = muldiv64(s->count, ticks_per_sec, s->freq); | ||||
|     if (delay == 0) | ||||
|         delay = 1; | ||||
|     s->expires += delay; | ||||
| } | ||||
| 
 | ||||
| /* Check all active timers, and schedule the next timer interrupt.  */ | ||||
| 
 | ||||
| static void arm_timer_update(arm_timer_state *s, int64_t now) | ||||
| { | ||||
|     int64_t next; | ||||
| 
 | ||||
|     /* Ignore disabled timers.  */ | ||||
|     if ((s->control & TIMER_CTRL_ENABLE) == 0) | ||||
|         return; | ||||
|     /* Ignore expired one-shot timers.  */ | ||||
|     if (s->count == 0 && (s->control & TIMER_CTRL_ONESHOT)) | ||||
|         return; | ||||
|     if (s->expires - now <= 0) { | ||||
|         /* Timer has expired.  */ | ||||
|         s->int_level = 1; | ||||
|         if (s->control & TIMER_CTRL_ONESHOT) { | ||||
|             /* One-shot.  */ | ||||
|             s->count = 0; | ||||
|         } else { | ||||
|             if ((s->control & TIMER_CTRL_PERIODIC) == 0) { | ||||
|                 /* Free running.  */ | ||||
|                 if (s->control & TIMER_CTRL_32BIT) | ||||
|                     s->count = 0xffffffff; | ||||
|                 else | ||||
|                     s->count = 0xffff; | ||||
|             } else { | ||||
|                   /* Periodic.  */ | ||||
|                   s->count = s->limit; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     while (s->expires - now <= 0) { | ||||
|         arm_timer_reload(s); | ||||
|     } | ||||
|     /* Update interrupts.  */ | ||||
|     if (s->int_level && (s->control & TIMER_CTRL_IE)) { | ||||
|         pic_set_irq_new(s->pic, s->irq, 1); | ||||
|     } else { | ||||
|         pic_set_irq_new(s->pic, s->irq, 0); | ||||
|     } | ||||
| 
 | ||||
|     next = now; | ||||
|     if (next - s->expires < 0) | ||||
|         next = s->expires; | ||||
| 
 | ||||
|     /* Schedule the next timer interrupt.  */ | ||||
|     if (next == now) { | ||||
|         qemu_del_timer(s->timer); | ||||
|         s->next_time = 0; | ||||
|     } else if (next != s->next_time) { | ||||
|         qemu_mod_timer(s->timer, next); | ||||
|         s->next_time = next; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Return the current value of the timer.  */ | ||||
| static uint32_t arm_timer_getcount(arm_timer_state *s, int64_t now) | ||||
| { | ||||
|     int64_t elapsed; | ||||
|     int64_t period; | ||||
| 
 | ||||
|     if (s->count == 0) | ||||
|         return 0; | ||||
|     if ((s->control & TIMER_CTRL_ENABLE) == 0) | ||||
|         return s->count; | ||||
|     elapsed = now - s->loaded; | ||||
|     period = s->expires - s->loaded; | ||||
|     /* If the timer should have expired then return 0.  This can happen
 | ||||
|        when the host timer signal doesnt occur immediately.  It's better to | ||||
|        have a timer appear to sit at zero for a while than have it wrap | ||||
|        around before the guest interrupt is raised.  */ | ||||
|     /* ??? Could we trigger the interrupt here?  */ | ||||
|     if (elapsed > period) | ||||
|         return 0; | ||||
|     /* We need to calculate count * elapsed / period without overfowing.
 | ||||
|        Scale both elapsed and period so they fit in a 32-bit int.  */ | ||||
|     while (period != (int32_t)period) { | ||||
|         period >>= 1; | ||||
|         elapsed >>= 1; | ||||
|     } | ||||
|     return ((uint64_t)s->count * (uint64_t)(int32_t)elapsed) | ||||
|             / (int32_t)period; | ||||
| } | ||||
| 
 | ||||
| uint32_t arm_timer_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     arm_timer_state *s = (arm_timer_state *)opaque; | ||||
| 
 | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* TimerLoad */ | ||||
|     case 6: /* TimerBGLoad */ | ||||
|         return s->limit; | ||||
|     case 1: /* TimerValue */ | ||||
|         return arm_timer_getcount(s, qemu_get_clock(vm_clock)); | ||||
|     case 2: /* TimerControl */ | ||||
|         return s->control; | ||||
|     case 4: /* TimerRIS */ | ||||
|         return s->int_level; | ||||
|     case 5: /* TimerMIS */ | ||||
|         if ((s->control & TIMER_CTRL_IE) == 0) | ||||
|             return 0; | ||||
|         return s->int_level; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "arm_timer_read: Bad offset %x\n", offset); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void arm_timer_write(void *opaque, target_phys_addr_t offset, | ||||
|                             uint32_t value) | ||||
| { | ||||
|     arm_timer_state *s = (arm_timer_state *)opaque; | ||||
|     int64_t now; | ||||
| 
 | ||||
|     now = qemu_get_clock(vm_clock); | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* TimerLoad */ | ||||
|         s->limit = value; | ||||
|         s->count = value; | ||||
|         s->expires = now; | ||||
|         arm_timer_reload(s); | ||||
|         break; | ||||
|     case 1: /* TimerValue */ | ||||
|         /* ??? Linux seems to want to write to this readonly register.
 | ||||
|            Ignore it.  */ | ||||
|         break; | ||||
|     case 2: /* TimerControl */ | ||||
|         if (s->control & TIMER_CTRL_ENABLE) { | ||||
|             /* Pause the timer if it is running.  This may cause some
 | ||||
|                inaccuracy dure to rounding, but avoids a whole lot of other | ||||
|                messyness.  */ | ||||
|             s->count = arm_timer_getcount(s, now); | ||||
|         } | ||||
|         s->control = value; | ||||
|         s->freq = s->raw_freq; | ||||
|         /* ??? Need to recalculate expiry time after changing divisor.  */ | ||||
|         switch ((value >> 2) & 3) { | ||||
|         case 1: s->freq >>= 4; break; | ||||
|         case 2: s->freq >>= 8; break; | ||||
|         } | ||||
|         if (s->control & TIMER_CTRL_ENABLE) { | ||||
|             /* Restart the timer if still enabled.  */ | ||||
|             s->expires = now; | ||||
|             arm_timer_reload(s); | ||||
|         } | ||||
|         break; | ||||
|     case 3: /* TimerIntClr */ | ||||
|         s->int_level = 0; | ||||
|         break; | ||||
|     case 6: /* TimerBGLoad */ | ||||
|         s->limit = value; | ||||
|         break; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "arm_timer_write: Bad offset %x\n", offset); | ||||
|     } | ||||
|     arm_timer_update(s, now); | ||||
| } | ||||
| 
 | ||||
| static void arm_timer_tick(void *opaque) | ||||
| { | ||||
|     int64_t now; | ||||
| 
 | ||||
|     now = qemu_get_clock(vm_clock); | ||||
|     arm_timer_update((arm_timer_state *)opaque, now); | ||||
| } | ||||
| 
 | ||||
| static void *arm_timer_init(uint32_t freq, void *pic, int irq) | ||||
| { | ||||
|     arm_timer_state *s; | ||||
| 
 | ||||
|     s = (arm_timer_state *)qemu_mallocz(sizeof(arm_timer_state)); | ||||
|     s->pic = pic; | ||||
|     s->irq = irq; | ||||
|     s->raw_freq = s->freq = 1000000; | ||||
|     s->control = TIMER_CTRL_IE; | ||||
|     s->count = 0xffffffff; | ||||
| 
 | ||||
|     s->timer = qemu_new_timer(vm_clock, arm_timer_tick, s); | ||||
|     /* ??? Save/restore.  */ | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
| /* ARM PrimeCell SP804 dual timer module.
 | ||||
|    Docs for this device don't seem to be publicly available.  This | ||||
|    implementation is based on gueswork, the linux kernel sources and the | ||||
|    Integrator/CP timer modules.  */ | ||||
| 
 | ||||
| typedef struct { | ||||
|     /* Include a pseudo-PIC device to merge the two interrupt sources.  */ | ||||
|     arm_pic_handler handler; | ||||
|     void *timer[2]; | ||||
|     int level[2]; | ||||
|     uint32_t base; | ||||
|     /* The output PIC device.  */ | ||||
|     void *pic; | ||||
|     int irq; | ||||
| } sp804_state; | ||||
| 
 | ||||
| static void sp804_set_irq(void *opaque, int irq, int level) | ||||
| { | ||||
|     sp804_state *s = (sp804_state *)opaque; | ||||
| 
 | ||||
|     s->level[irq] = level; | ||||
|     pic_set_irq_new(s->pic, s->irq, s->level[0] || s->level[1]); | ||||
| } | ||||
| 
 | ||||
| static uint32_t sp804_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     sp804_state *s = (sp804_state *)opaque; | ||||
| 
 | ||||
|     /* ??? Don't know the PrimeCell ID for this device.  */ | ||||
|     offset -= s->base; | ||||
|     if (offset < 0x20) { | ||||
|         return arm_timer_read(s->timer[0], offset); | ||||
|     } else { | ||||
|         return arm_timer_read(s->timer[1], offset - 0x20); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void sp804_write(void *opaque, target_phys_addr_t offset, | ||||
|                         uint32_t value) | ||||
| { | ||||
|     sp804_state *s = (sp804_state *)opaque; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     if (offset < 0x20) { | ||||
|         arm_timer_write(s->timer[0], offset, value); | ||||
|     } else { | ||||
|         arm_timer_write(s->timer[1], offset - 0x20, value); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static CPUReadMemoryFunc *sp804_readfn[] = { | ||||
|    sp804_read, | ||||
|    sp804_read, | ||||
|    sp804_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *sp804_writefn[] = { | ||||
|    sp804_write, | ||||
|    sp804_write, | ||||
|    sp804_write | ||||
| }; | ||||
| 
 | ||||
| void sp804_init(uint32_t base, void *pic, int irq) | ||||
| { | ||||
|     int iomemtype; | ||||
|     sp804_state *s; | ||||
| 
 | ||||
|     s = (sp804_state *)qemu_mallocz(sizeof(sp804_state)); | ||||
|     s->handler = sp804_set_irq; | ||||
|     s->base = base; | ||||
|     s->pic = pic; | ||||
|     s->irq = irq; | ||||
|     /* ??? The timers are actually configurable between 32kHz and 1MHz, but
 | ||||
|        we don't implement that.  */ | ||||
|     s->timer[0] = arm_timer_init(1000000, s, 0); | ||||
|     s->timer[1] = arm_timer_init(1000000, s, 1); | ||||
|     iomemtype = cpu_register_io_memory(0, sp804_readfn, | ||||
|                                        sp804_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x00000fff, iomemtype); | ||||
|     /* ??? Save/restore.  */ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Integrator/CP timer module.  */ | ||||
| 
 | ||||
| typedef struct { | ||||
|     void *timer[3]; | ||||
|     uint32_t base; | ||||
| } icp_pit_state; | ||||
| 
 | ||||
| static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     icp_pit_state *s = (icp_pit_state *)opaque; | ||||
|     int n; | ||||
| 
 | ||||
|     /* ??? Don't know the PrimeCell ID for this device.  */ | ||||
|     offset -= s->base; | ||||
|     n = offset >> 8; | ||||
|     if (n > 3) | ||||
|         cpu_abort(cpu_single_env, "sp804_read: Bad timer %d\n", n); | ||||
| 
 | ||||
|     return arm_timer_read(s->timer[n], offset & 0xff); | ||||
| } | ||||
| 
 | ||||
| static void icp_pit_write(void *opaque, target_phys_addr_t offset, | ||||
|                           uint32_t value) | ||||
| { | ||||
|     icp_pit_state *s = (icp_pit_state *)opaque; | ||||
|     int n; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     n = offset >> 8; | ||||
|     if (n > 3) | ||||
|         cpu_abort(cpu_single_env, "sp804_write: Bad timer %d\n", n); | ||||
| 
 | ||||
|     arm_timer_write(s->timer[n], offset & 0xff, value); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| static CPUReadMemoryFunc *icp_pit_readfn[] = { | ||||
|    icp_pit_read, | ||||
|    icp_pit_read, | ||||
|    icp_pit_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *icp_pit_writefn[] = { | ||||
|    icp_pit_write, | ||||
|    icp_pit_write, | ||||
|    icp_pit_write | ||||
| }; | ||||
| 
 | ||||
| void icp_pit_init(uint32_t base, void *pic, int irq) | ||||
| { | ||||
|     int iomemtype; | ||||
|     icp_pit_state *s; | ||||
| 
 | ||||
|     s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state)); | ||||
|     s->base = base; | ||||
|     /* Timer 0 runs at the system clock speed (40MHz).  */ | ||||
|     s->timer[0] = arm_timer_init(40000000, pic, irq); | ||||
|     /* The other two timers run at 1MHz.  */ | ||||
|     s->timer[1] = arm_timer_init(1000000, pic, irq + 1); | ||||
|     s->timer[2] = arm_timer_init(1000000, pic, irq + 2); | ||||
| 
 | ||||
|     iomemtype = cpu_register_io_memory(0, icp_pit_readfn, | ||||
|                                        icp_pit_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x00000fff, iomemtype); | ||||
|     /* ??? Save/restore.  */ | ||||
| } | ||||
| 
 | ||||
|  | @ -1,32 +1,19 @@ | |||
| /* 
 | ||||
|  * ARM Integrator CP System emulation. | ||||
|  * | ||||
|  * Copyright (c) 2005 CodeSourcery, LLC. | ||||
|  * Copyright (c) 2005-2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the GPL | ||||
|  */ | ||||
| 
 | ||||
| #include <vl.h> | ||||
| #include "vl.h" | ||||
| #include "arm_pic.h" | ||||
| 
 | ||||
| #define KERNEL_ARGS_ADDR 0x100 | ||||
| #define KERNEL_LOAD_ADDR 0x00010000 | ||||
| #define INITRD_LOAD_ADDR 0x00800000 | ||||
| 
 | ||||
| /* Stub functions for hardware that doesn't exist.  */ | ||||
| void pic_set_irq(int irq, int level) | ||||
| { | ||||
|     cpu_abort (cpu_single_env, "pic_set_irq"); | ||||
| } | ||||
| 
 | ||||
| void pic_info(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void irq_info(void) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void DMA_run (void) | ||||
| { | ||||
| } | ||||
|  | @ -284,41 +271,31 @@ static void integratorcm_init(int memsz, uint32_t flash_offset) | |||
| 
 | ||||
| typedef struct icp_pic_state | ||||
| { | ||||
|   arm_pic_handler handler; | ||||
|   uint32_t base; | ||||
|   uint32_t level; | ||||
|   uint32_t irq_enabled; | ||||
|   uint32_t fiq_enabled; | ||||
|   void *parent; | ||||
|   /* -1 if parent is a cpu, otherwise IRQ number on parent PIC.  */ | ||||
|   int parent_irq; | ||||
|   int parent_fiq; | ||||
| } icp_pic_state; | ||||
| 
 | ||||
| static void icp_pic_update(icp_pic_state *s) | ||||
| { | ||||
|     CPUState *env; | ||||
|     if (s->parent_irq != -1) { | ||||
|         uint32_t flags; | ||||
|     uint32_t flags; | ||||
| 
 | ||||
|     if (s->parent_irq != -1) { | ||||
|         flags = (s->level & s->irq_enabled); | ||||
|         pic_set_irq_new(s->parent, s->parent_irq, | ||||
|                         flags != 0); | ||||
|         return; | ||||
|         pic_set_irq_new(s->parent, s->parent_irq, flags != 0); | ||||
|     } | ||||
|     /* Raise CPU interrupt.  */ | ||||
|     env = (CPUState *)s->parent; | ||||
|     if (s->level & s->fiq_enabled) { | ||||
|         cpu_interrupt (env, CPU_INTERRUPT_FIQ); | ||||
|     } else { | ||||
|         cpu_reset_interrupt (env, CPU_INTERRUPT_FIQ); | ||||
|     } | ||||
|     if (s->level & s->irq_enabled) { | ||||
|       cpu_interrupt (env, CPU_INTERRUPT_HARD); | ||||
|     } else { | ||||
|       cpu_reset_interrupt (env, CPU_INTERRUPT_HARD); | ||||
|     if (s->parent_fiq != -1) { | ||||
|         flags = (s->level & s->fiq_enabled); | ||||
|         pic_set_irq_new(s->parent, s->parent_fiq, flags != 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void pic_set_irq_new(void *opaque, int irq, int level) | ||||
| static void icp_pic_set_irq(void *opaque, int irq, int level) | ||||
| { | ||||
|     icp_pic_state *s = (icp_pic_state *)opaque; | ||||
|     if (level) | ||||
|  | @ -408,7 +385,7 @@ static CPUWriteMemoryFunc *icp_pic_writefn[] = { | |||
| }; | ||||
| 
 | ||||
| static icp_pic_state *icp_pic_init(uint32_t base, void *parent, | ||||
|                                    int parent_irq) | ||||
|                                    int parent_irq, int parent_fiq) | ||||
| { | ||||
|     icp_pic_state *s; | ||||
|     int iomemtype; | ||||
|  | @ -416,10 +393,11 @@ static icp_pic_state *icp_pic_init(uint32_t base, void *parent, | |||
|     s = (icp_pic_state *)qemu_mallocz(sizeof(icp_pic_state)); | ||||
|     if (!s) | ||||
|         return NULL; | ||||
| 
 | ||||
|     s->handler = icp_pic_set_irq; | ||||
|     s->base = base; | ||||
|     s->parent = parent; | ||||
|     s->parent_irq = parent_irq; | ||||
|     s->parent_fiq = parent_fiq; | ||||
|     iomemtype = cpu_register_io_memory(0, icp_pic_readfn, | ||||
|                                        icp_pic_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x007fffff, iomemtype); | ||||
|  | @ -427,499 +405,6 @@ static icp_pic_state *icp_pic_init(uint32_t base, void *parent, | |||
|     return s; | ||||
| } | ||||
| 
 | ||||
| /* Timers.  */ | ||||
| 
 | ||||
| /* System bus clock speed (40MHz) for timer 0.  Not sure about this value.  */ | ||||
| #define ICP_BUS_FREQ 40000000 | ||||
| 
 | ||||
| typedef struct { | ||||
|     int64_t next_time; | ||||
|     int64_t expires[3]; | ||||
|     int64_t loaded[3]; | ||||
|     QEMUTimer *timer; | ||||
|     icp_pic_state *pic; | ||||
|     uint32_t base; | ||||
|     uint32_t control[3]; | ||||
|     uint32_t count[3]; | ||||
|     uint32_t limit[3]; | ||||
|     int freq[3]; | ||||
|     int int_level[3]; | ||||
| } icp_pit_state; | ||||
| 
 | ||||
| /* Calculate the new expiry time of the given timer.  */ | ||||
| 
 | ||||
| static void icp_pit_reload(icp_pit_state *s, int n) | ||||
| { | ||||
|     int64_t delay; | ||||
| 
 | ||||
|     s->loaded[n] = s->expires[n]; | ||||
|     delay = muldiv64(s->count[n], ticks_per_sec, s->freq[n]); | ||||
|     if (delay == 0) | ||||
|         delay = 1; | ||||
|     s->expires[n] += delay; | ||||
| } | ||||
| 
 | ||||
| /* Check all active timers, and schedule the next timer interrupt.  */ | ||||
| 
 | ||||
| static void icp_pit_update(icp_pit_state *s, int64_t now) | ||||
| { | ||||
|     int n; | ||||
|     int64_t next; | ||||
| 
 | ||||
|     next = now; | ||||
|     for (n = 0; n < 3; n++) { | ||||
|         /* Ignore disabled timers.  */ | ||||
|         if ((s->control[n] & 0x80) == 0) | ||||
|             continue; | ||||
|         /* Ignore expired one-shot timers.  */ | ||||
|         if (s->count[n] == 0 && s->control[n] & 1) | ||||
|             continue; | ||||
|         if (s->expires[n] - now <= 0) { | ||||
|             /* Timer has expired.  */ | ||||
|             s->int_level[n] = 1; | ||||
|             if (s->control[n] & 1) { | ||||
|                 /* One-shot.  */ | ||||
|                 s->count[n] = 0; | ||||
|             } else { | ||||
|                 if ((s->control[n] & 0x40) == 0) { | ||||
|                     /* Free running.  */ | ||||
|                     if (s->control[n] & 2) | ||||
|                         s->count[n] = 0xffffffff; | ||||
|                     else | ||||
|                         s->count[n] = 0xffff; | ||||
|                 } else { | ||||
|                       /* Periodic.  */ | ||||
|                       s->count[n] = s->limit[n]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         while (s->expires[n] - now <= 0) { | ||||
|             icp_pit_reload(s, n); | ||||
|         } | ||||
|     } | ||||
|     /* Update interrupts.  */ | ||||
|     for (n = 0; n < 3; n++) { | ||||
|         if (s->int_level[n] && (s->control[n] & 0x20)) { | ||||
|             pic_set_irq_new(s->pic, 5 + n, 1); | ||||
|         } else { | ||||
|             pic_set_irq_new(s->pic, 5 + n, 0); | ||||
|         } | ||||
|         if (next - s->expires[n] < 0) | ||||
|             next = s->expires[n]; | ||||
|     } | ||||
|     /* Schedule the next timer interrupt.  */ | ||||
|     if (next == now) { | ||||
|         qemu_del_timer(s->timer); | ||||
|         s->next_time = 0; | ||||
|     } else if (next != s->next_time) { | ||||
|         qemu_mod_timer(s->timer, next); | ||||
|         s->next_time = next; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Return the current value of the timer.  */ | ||||
| static uint32_t icp_pit_getcount(icp_pit_state *s, int n, int64_t now) | ||||
| { | ||||
|     int64_t elapsed; | ||||
|     int64_t period; | ||||
| 
 | ||||
|     if (s->count[n] == 0) | ||||
|         return 0; | ||||
|     if ((s->control[n] & 0x80) == 0) | ||||
|         return s->count[n]; | ||||
|     elapsed = now - s->loaded[n]; | ||||
|     period = s->expires[n] - s->loaded[n]; | ||||
|     /* If the timer should have expired then return 0.  This can happen
 | ||||
|        when the host timer signal doesnt occur immediately.  It's better to | ||||
|        have a timer appear to sit at zero for a while than have it wrap | ||||
|        around before the guest interrupt is raised.  */ | ||||
|     /* ??? Could we trigger the interrupt here?  */ | ||||
|     if (elapsed > period) | ||||
|         return 0; | ||||
|     /* We need to calculate count * elapsed / period without overfowing.
 | ||||
|        Scale both elapsed and period so they fit in a 32-bit int.  */ | ||||
|     while (period != (int32_t)period) { | ||||
|         period >>= 1; | ||||
|         elapsed >>= 1; | ||||
|     } | ||||
|     return ((uint64_t)s->count[n] * (uint64_t)(int32_t)elapsed) | ||||
|             / (int32_t)period; | ||||
| } | ||||
| 
 | ||||
| static uint32_t icp_pit_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     int n; | ||||
|     icp_pit_state *s = (icp_pit_state *)opaque; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     n = offset >> 8; | ||||
|     if (n > 2) | ||||
|         cpu_abort (cpu_single_env, "icp_pit_read: Bad timer %x\n", offset); | ||||
|     switch ((offset & 0xff) >> 2) { | ||||
|     case 0: /* TimerLoad */ | ||||
|     case 6: /* TimerBGLoad */ | ||||
|         return s->limit[n]; | ||||
|     case 1: /* TimerValue */ | ||||
|         return icp_pit_getcount(s, n, qemu_get_clock(vm_clock)); | ||||
|     case 2: /* TimerControl */ | ||||
|         return s->control[n]; | ||||
|     case 4: /* TimerRIS */ | ||||
|         return s->int_level[n]; | ||||
|     case 5: /* TimerMIS */ | ||||
|         if ((s->control[n] & 0x20) == 0) | ||||
|             return 0; | ||||
|         return s->int_level[n]; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "icp_pit_read: Bad offset %x\n", offset); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void icp_pit_write(void *opaque, target_phys_addr_t offset, | ||||
|                           uint32_t value) | ||||
| { | ||||
|     icp_pit_state *s = (icp_pit_state *)opaque; | ||||
|     int n; | ||||
|     int64_t now; | ||||
| 
 | ||||
|     now = qemu_get_clock(vm_clock); | ||||
|     offset -= s->base; | ||||
|     n = offset >> 8; | ||||
|     if (n > 2) | ||||
|         cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset); | ||||
| 
 | ||||
|     switch ((offset & 0xff) >> 2) { | ||||
|     case 0: /* TimerLoad */ | ||||
|         s->limit[n] = value; | ||||
|         s->count[n] = value; | ||||
|         s->expires[n] = now; | ||||
|         icp_pit_reload(s, n); | ||||
|         break; | ||||
|     case 1: /* TimerValue */ | ||||
|         /* ??? Linux seems to want to write to this readonly register.
 | ||||
|            Ignore it.  */ | ||||
|         break; | ||||
|     case 2: /* TimerControl */ | ||||
|         if (s->control[n] & 0x80) { | ||||
|             /* Pause the timer if it is running.  This may cause some
 | ||||
|                inaccuracy dure to rounding, but avoids a whole lot of other | ||||
|                messyness.  */ | ||||
|             s->count[n] = icp_pit_getcount(s, n, now); | ||||
|         } | ||||
|         s->control[n] = value; | ||||
|         if (n == 0) | ||||
|             s->freq[n] = ICP_BUS_FREQ; | ||||
|         else | ||||
|             s->freq[n] = 1000000; | ||||
|         /* ??? Need to recalculate expiry time after changing divisor.  */ | ||||
|         switch ((value >> 2) & 3) { | ||||
|         case 1: s->freq[n] >>= 4; break; | ||||
|         case 2: s->freq[n] >>= 8; break; | ||||
|         } | ||||
|         if (s->control[n] & 0x80) { | ||||
|             /* Restart the timer if still enabled.  */ | ||||
|             s->expires[n] = now; | ||||
|             icp_pit_reload(s, n); | ||||
|         } | ||||
|         break; | ||||
|     case 3: /* TimerIntClr */ | ||||
|         s->int_level[n] = 0; | ||||
|         break; | ||||
|     case 6: /* TimerBGLoad */ | ||||
|         s->limit[n] = value; | ||||
|         break; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "icp_pit_write: Bad offset %x\n", offset); | ||||
|     } | ||||
|     icp_pit_update(s, now); | ||||
| } | ||||
| 
 | ||||
| static void icp_pit_tick(void *opaque) | ||||
| { | ||||
|     int64_t now; | ||||
| 
 | ||||
|     now = qemu_get_clock(vm_clock); | ||||
|     icp_pit_update((icp_pit_state *)opaque, now); | ||||
| } | ||||
| 
 | ||||
| static CPUReadMemoryFunc *icp_pit_readfn[] = { | ||||
|    icp_pit_read, | ||||
|    icp_pit_read, | ||||
|    icp_pit_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *icp_pit_writefn[] = { | ||||
|    icp_pit_write, | ||||
|    icp_pit_write, | ||||
|    icp_pit_write | ||||
| }; | ||||
| 
 | ||||
| static void icp_pit_init(uint32_t base, icp_pic_state *pic) | ||||
| { | ||||
|     int iomemtype; | ||||
|     icp_pit_state *s; | ||||
|     int n; | ||||
| 
 | ||||
|     s = (icp_pit_state *)qemu_mallocz(sizeof(icp_pit_state)); | ||||
|     s->base = base; | ||||
|     s->pic = pic; | ||||
|     s->freq[0] = ICP_BUS_FREQ; | ||||
|     s->freq[1] = 1000000; | ||||
|     s->freq[2] = 1000000; | ||||
|     for (n = 0; n < 3; n++) { | ||||
|         s->control[n] = 0x20; | ||||
|         s->count[n] = 0xffffffff; | ||||
|     } | ||||
| 
 | ||||
|     iomemtype = cpu_register_io_memory(0, icp_pit_readfn, | ||||
|                                        icp_pit_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x007fffff, iomemtype); | ||||
|     s->timer = qemu_new_timer(vm_clock, icp_pit_tick, s); | ||||
|     /* ??? Save/restore.  */ | ||||
| } | ||||
| 
 | ||||
| /* ARM PrimeCell PL011 UART */ | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t base; | ||||
|     uint32_t readbuff; | ||||
|     uint32_t flags; | ||||
|     uint32_t lcr; | ||||
|     uint32_t cr; | ||||
|     uint32_t dmacr; | ||||
|     uint32_t int_enabled; | ||||
|     uint32_t int_level; | ||||
|     uint32_t read_fifo[16]; | ||||
|     uint32_t ilpr; | ||||
|     uint32_t ibrd; | ||||
|     uint32_t fbrd; | ||||
|     uint32_t ifl; | ||||
|     int read_pos; | ||||
|     int read_count; | ||||
|     int read_trigger; | ||||
|     CharDriverState *chr; | ||||
|     icp_pic_state *pic; | ||||
|     int irq; | ||||
| } pl011_state; | ||||
| 
 | ||||
| #define PL011_INT_TX 0x20 | ||||
| #define PL011_INT_RX 0x10 | ||||
| 
 | ||||
| #define PL011_FLAG_TXFE 0x80 | ||||
| #define PL011_FLAG_RXFF 0x40 | ||||
| #define PL011_FLAG_TXFF 0x20 | ||||
| #define PL011_FLAG_RXFE 0x10 | ||||
| 
 | ||||
| static const unsigned char pl011_id[] = | ||||
| { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | ||||
| 
 | ||||
| static void pl011_update(pl011_state *s) | ||||
| { | ||||
|     uint32_t flags; | ||||
|      | ||||
|     flags = s->int_level & s->int_enabled; | ||||
|     pic_set_irq_new(s->pic, s->irq, flags != 0); | ||||
| } | ||||
| 
 | ||||
| static uint32_t pl011_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     pl011_state *s = (pl011_state *)opaque; | ||||
|     uint32_t c; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     if (offset >= 0xfe0 && offset < 0x1000) { | ||||
|         return pl011_id[(offset - 0xfe0) >> 2]; | ||||
|     } | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* UARTDR */ | ||||
|         s->flags &= ~PL011_FLAG_RXFF; | ||||
|         c = s->read_fifo[s->read_pos]; | ||||
|         if (s->read_count > 0) { | ||||
|             s->read_count--; | ||||
|             if (++s->read_pos == 16) | ||||
|                 s->read_pos = 0; | ||||
|         } | ||||
|         if (s->read_count == 0) { | ||||
|             s->flags |= PL011_FLAG_RXFE; | ||||
|         } | ||||
|         if (s->read_count == s->read_trigger - 1) | ||||
|             s->int_level &= ~ PL011_INT_RX; | ||||
|         pl011_update(s); | ||||
|         return c; | ||||
|     case 1: /* UARTCR */ | ||||
|         return 0; | ||||
|     case 6: /* UARTFR */ | ||||
|         return s->flags; | ||||
|     case 8: /* UARTILPR */ | ||||
|         return s->ilpr; | ||||
|     case 9: /* UARTIBRD */ | ||||
|         return s->ibrd; | ||||
|     case 10: /* UARTFBRD */ | ||||
|         return s->fbrd; | ||||
|     case 11: /* UARTLCR_H */ | ||||
|         return s->lcr; | ||||
|     case 12: /* UARTCR */ | ||||
|         return s->cr; | ||||
|     case 13: /* UARTIFLS */ | ||||
|         return s->ifl; | ||||
|     case 14: /* UARTIMSC */ | ||||
|         return s->int_enabled; | ||||
|     case 15: /* UARTRIS */ | ||||
|         return s->int_level; | ||||
|     case 16: /* UARTMIS */ | ||||
|         return s->int_level & s->int_enabled; | ||||
|     case 18: /* UARTDMACR */ | ||||
|         return s->dmacr; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pl011_set_read_trigger(pl011_state *s) | ||||
| { | ||||
| #if 0 | ||||
|     /* The docs say the RX interrupt is triggered when the FIFO exceeds
 | ||||
|        the threshold.  However linux only reads the FIFO in response to an | ||||
|        interrupt.  Triggering the interrupt when the FIFO is non-empty seems | ||||
|        to make things work.  */ | ||||
|     if (s->lcr & 0x10) | ||||
|         s->read_trigger = (s->ifl >> 1) & 0x1c; | ||||
|     else | ||||
| #endif | ||||
|         s->read_trigger = 1; | ||||
| } | ||||
| 
 | ||||
| static void pl011_write(void *opaque, target_phys_addr_t offset, | ||||
|                           uint32_t value) | ||||
| { | ||||
|     pl011_state *s = (pl011_state *)opaque; | ||||
|     unsigned char ch; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* UARTDR */ | ||||
|         /* ??? Check if transmitter is enabled.  */ | ||||
|         ch = value; | ||||
|         if (s->chr) | ||||
|             qemu_chr_write(s->chr, &ch, 1); | ||||
|         s->int_level |= PL011_INT_TX; | ||||
|         pl011_update(s); | ||||
|         break; | ||||
|     case 1: /* UARTCR */ | ||||
|         s->cr = value; | ||||
|         break; | ||||
|     case 8: /* UARTUARTILPR */ | ||||
|         s->ilpr = value; | ||||
|         break; | ||||
|     case 9: /* UARTIBRD */ | ||||
|         s->ibrd = value; | ||||
|         break; | ||||
|     case 10: /* UARTFBRD */ | ||||
|         s->fbrd = value; | ||||
|         break; | ||||
|     case 11: /* UARTLCR_H */ | ||||
|         s->lcr = value; | ||||
|         pl011_set_read_trigger(s); | ||||
|         break; | ||||
|     case 12: /* UARTCR */ | ||||
|         /* ??? Need to implement the enable and loopback bits.  */ | ||||
|         s->cr = value; | ||||
|         break; | ||||
|     case 13: /* UARTIFS */ | ||||
|         s->ifl = value; | ||||
|         pl011_set_read_trigger(s); | ||||
|         break; | ||||
|     case 14: /* UARTIMSC */ | ||||
|         s->int_enabled = value; | ||||
|         pl011_update(s); | ||||
|         break; | ||||
|     case 17: /* UARTICR */ | ||||
|         s->int_level &= ~value; | ||||
|         pl011_update(s); | ||||
|         break; | ||||
|     case 18: /* UARTDMACR */ | ||||
|         s->dmacr = value; | ||||
|         if (value & 3) | ||||
|             cpu_abort(cpu_single_env, "PL011: DMA not implemented\n"); | ||||
|         break; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static int pl011_can_recieve(void *opaque) | ||||
| { | ||||
|     pl011_state *s = (pl011_state *)opaque; | ||||
| 
 | ||||
|     if (s->lcr & 0x10) | ||||
|         return s->read_count < 16; | ||||
|     else | ||||
|         return s->read_count < 1; | ||||
| } | ||||
| 
 | ||||
| static void pl011_recieve(void *opaque, const uint8_t *buf, int size) | ||||
| { | ||||
|     pl011_state *s = (pl011_state *)opaque; | ||||
|     int slot; | ||||
| 
 | ||||
|     slot = s->read_pos + s->read_count; | ||||
|     if (slot >= 16) | ||||
|         slot -= 16; | ||||
|     s->read_fifo[slot] = *buf; | ||||
|     s->read_count++; | ||||
|     s->flags &= ~PL011_FLAG_RXFE; | ||||
|     if (s->cr & 0x10 || s->read_count == 16) { | ||||
|         s->flags |= PL011_FLAG_RXFF; | ||||
|     } | ||||
|     if (s->read_count == s->read_trigger) { | ||||
|         s->int_level |= PL011_INT_RX; | ||||
|         pl011_update(s); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pl011_event(void *opaque, int event) | ||||
| { | ||||
|     /* ??? Should probably implement break.  */ | ||||
| } | ||||
| 
 | ||||
| static CPUReadMemoryFunc *pl011_readfn[] = { | ||||
|    pl011_read, | ||||
|    pl011_read, | ||||
|    pl011_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *pl011_writefn[] = { | ||||
|    pl011_write, | ||||
|    pl011_write, | ||||
|    pl011_write | ||||
| }; | ||||
| 
 | ||||
| static void pl011_init(uint32_t base, icp_pic_state *pic, int irq, | ||||
|                        CharDriverState *chr) | ||||
| { | ||||
|     int iomemtype; | ||||
|     pl011_state *s; | ||||
| 
 | ||||
|     s = (pl011_state *)qemu_mallocz(sizeof(pl011_state)); | ||||
|     iomemtype = cpu_register_io_memory(0, pl011_readfn, | ||||
|                                        pl011_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x007fffff, iomemtype); | ||||
|     s->base = base; | ||||
|     s->pic = pic; | ||||
|     s->irq = irq; | ||||
|     s->chr = chr; | ||||
|     s->read_trigger = 1; | ||||
|     s->ifl = 0x12; | ||||
|     s->cr = 0x300; | ||||
|     s->flags = 0x90; | ||||
|     if (chr){  | ||||
|         qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s); | ||||
|         qemu_chr_add_event_handler(chr, pl011_event); | ||||
|     } | ||||
|     /* ??? Save/restore.  */ | ||||
| } | ||||
| 
 | ||||
| /* CP control registers.  */ | ||||
| typedef struct { | ||||
|     uint32_t base; | ||||
|  | @ -985,122 +470,6 @@ static void icp_control_init(uint32_t base) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Keyboard/Mouse Interface.  */ | ||||
| 
 | ||||
| typedef struct { | ||||
|     void *dev; | ||||
|     uint32_t base; | ||||
|     uint32_t cr; | ||||
|     uint32_t clk; | ||||
|     uint32_t last; | ||||
|     icp_pic_state *pic; | ||||
|     int pending; | ||||
|     int irq; | ||||
|     int is_mouse; | ||||
| } icp_kmi_state; | ||||
| 
 | ||||
| static void icp_kmi_update(void *opaque, int level) | ||||
| { | ||||
|     icp_kmi_state *s = (icp_kmi_state *)opaque; | ||||
|     int raise; | ||||
| 
 | ||||
|     s->pending = level; | ||||
|     raise = (s->pending && (s->cr & 0x10) != 0) | ||||
|             || (s->cr & 0x08) != 0; | ||||
|     pic_set_irq_new(s->pic, s->irq, raise); | ||||
| } | ||||
| 
 | ||||
| static uint32_t icp_kmi_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     icp_kmi_state *s = (icp_kmi_state *)opaque; | ||||
|     offset -= s->base; | ||||
|     if (offset >= 0xfe0 && offset < 0x1000) | ||||
|         return 0; | ||||
| 
 | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* KMICR */ | ||||
|         return s->cr; | ||||
|     case 1: /* KMISTAT */ | ||||
|         /* KMIC and KMID bits not implemented.  */ | ||||
|         if (s->pending) { | ||||
|             return 0x10; | ||||
|         } else { | ||||
|             return 0; | ||||
|         } | ||||
|     case 2: /* KMIDATA */ | ||||
|         if (s->pending) | ||||
|             s->last = ps2_read_data(s->dev); | ||||
|         return s->last; | ||||
|     case 3: /* KMICLKDIV */ | ||||
|         return s->clk; | ||||
|     case 4: /* KMIIR */ | ||||
|         return s->pending | 2; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "icp_kmi_read: Bad offset %x\n", offset); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void icp_kmi_write(void *opaque, target_phys_addr_t offset, | ||||
|                           uint32_t value) | ||||
| { | ||||
|     icp_kmi_state *s = (icp_kmi_state *)opaque; | ||||
|     offset -= s->base; | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* KMICR */ | ||||
|         s->cr = value; | ||||
|         icp_kmi_update(s, s->pending); | ||||
|         /* ??? Need to implement the enable/disable bit.  */ | ||||
|         break; | ||||
|     case 2: /* KMIDATA */ | ||||
|         /* ??? This should toggle the TX interrupt line.  */ | ||||
|         /* ??? This means kbd/mouse can block each other.  */ | ||||
|         if (s->is_mouse) { | ||||
|             ps2_write_mouse(s->dev, value); | ||||
|         } else { | ||||
|             ps2_write_keyboard(s->dev, value); | ||||
|         } | ||||
|         break; | ||||
|     case 3: /* KMICLKDIV */ | ||||
|         s->clk = value; | ||||
|         return; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "icp_kmi_write: Bad offset %x\n", offset); | ||||
|     } | ||||
| } | ||||
| static CPUReadMemoryFunc *icp_kmi_readfn[] = { | ||||
|    icp_kmi_read, | ||||
|    icp_kmi_read, | ||||
|    icp_kmi_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *icp_kmi_writefn[] = { | ||||
|    icp_kmi_write, | ||||
|    icp_kmi_write, | ||||
|    icp_kmi_write | ||||
| }; | ||||
| 
 | ||||
| static void icp_kmi_init(uint32_t base, icp_pic_state * pic, int irq, | ||||
|                          int is_mouse) | ||||
| { | ||||
|     int iomemtype; | ||||
|     icp_kmi_state *s; | ||||
| 
 | ||||
|     s = (icp_kmi_state *)qemu_mallocz(sizeof(icp_kmi_state)); | ||||
|     iomemtype = cpu_register_io_memory(0, icp_kmi_readfn, | ||||
|                                        icp_kmi_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x007fffff, iomemtype); | ||||
|     s->base = base; | ||||
|     s->pic = pic; | ||||
|     s->irq = irq; | ||||
|     s->is_mouse = is_mouse; | ||||
|     if (is_mouse) | ||||
|         s->dev = ps2_mouse_init(icp_kmi_update, s); | ||||
|     else | ||||
|         s->dev = ps2_kbd_init(icp_kmi_update, s); | ||||
|     /* ??? Save/restore.  */ | ||||
| } | ||||
| 
 | ||||
| /* The worlds second smallest bootloader.  Set r0-r2, then jump to kernel.  */ | ||||
| static uint32_t bootloader[] = { | ||||
|   0xe3a00000, /* mov     r0, #0 */ | ||||
|  | @ -1162,6 +531,7 @@ static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device, | |||
|     CPUState *env; | ||||
|     uint32_t bios_offset; | ||||
|     icp_pic_state *pic; | ||||
|     void *cpu_pic; | ||||
|     int kernel_size; | ||||
|     int initrd_size; | ||||
|     int n; | ||||
|  | @ -1177,14 +547,15 @@ static void integratorcp_init(int ram_size, int vga_ram_size, int boot_device, | |||
|     cpu_register_physical_memory(0x80000000, ram_size, IO_MEM_RAM); | ||||
| 
 | ||||
|     integratorcm_init(ram_size >> 20, bios_offset); | ||||
|     pic = icp_pic_init(0x14000000, env, -1); | ||||
|     icp_pic_init(0xca000000, pic, 26); | ||||
|     icp_pit_init(0x13000000, pic); | ||||
|     cpu_pic = arm_pic_init_cpu(env); | ||||
|     pic = icp_pic_init(0x14000000, cpu_pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ); | ||||
|     icp_pic_init(0xca000000, pic, 26, -1); | ||||
|     icp_pit_init(0x13000000, pic, 5); | ||||
|     pl011_init(0x16000000, pic, 1, serial_hds[0]); | ||||
|     pl011_init(0x17000000, pic, 2, serial_hds[1]); | ||||
|     icp_control_init(0xcb000000); | ||||
|     icp_kmi_init(0x18000000, pic, 3, 0); | ||||
|     icp_kmi_init(0x19000000, pic, 4, 1); | ||||
|     pl050_init(0x18000000, pic, 3, 0); | ||||
|     pl050_init(0x19000000, pic, 4, 1); | ||||
|     if (nd_table[0].vlan) { | ||||
|         if (nd_table[0].model == NULL | ||||
|             || strcmp(nd_table[0].model, "smc91c111") == 0) { | ||||
|  |  | |||
|  | @ -0,0 +1,251 @@ | |||
| /* 
 | ||||
|  * Arm PrimeCell PL011 UART | ||||
|  * | ||||
|  * Copyright (c) 2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the GPL. | ||||
|  */ | ||||
| 
 | ||||
| #include "vl.h" | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t base; | ||||
|     uint32_t readbuff; | ||||
|     uint32_t flags; | ||||
|     uint32_t lcr; | ||||
|     uint32_t cr; | ||||
|     uint32_t dmacr; | ||||
|     uint32_t int_enabled; | ||||
|     uint32_t int_level; | ||||
|     uint32_t read_fifo[16]; | ||||
|     uint32_t ilpr; | ||||
|     uint32_t ibrd; | ||||
|     uint32_t fbrd; | ||||
|     uint32_t ifl; | ||||
|     int read_pos; | ||||
|     int read_count; | ||||
|     int read_trigger; | ||||
|     CharDriverState *chr; | ||||
|     void *pic; | ||||
|     int irq; | ||||
| } pl011_state; | ||||
| 
 | ||||
| #define PL011_INT_TX 0x20 | ||||
| #define PL011_INT_RX 0x10 | ||||
| 
 | ||||
| #define PL011_FLAG_TXFE 0x80 | ||||
| #define PL011_FLAG_RXFF 0x40 | ||||
| #define PL011_FLAG_TXFF 0x20 | ||||
| #define PL011_FLAG_RXFE 0x10 | ||||
| 
 | ||||
| static const unsigned char pl011_id[] = | ||||
| { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | ||||
| 
 | ||||
| static void pl011_update(pl011_state *s) | ||||
| { | ||||
|     uint32_t flags; | ||||
|      | ||||
|     flags = s->int_level & s->int_enabled; | ||||
|     pic_set_irq_new(s->pic, s->irq, flags != 0); | ||||
| } | ||||
| 
 | ||||
| static uint32_t pl011_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     pl011_state *s = (pl011_state *)opaque; | ||||
|     uint32_t c; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     if (offset >= 0xfe0 && offset < 0x1000) { | ||||
|         return pl011_id[(offset - 0xfe0) >> 2]; | ||||
|     } | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* UARTDR */ | ||||
|         s->flags &= ~PL011_FLAG_RXFF; | ||||
|         c = s->read_fifo[s->read_pos]; | ||||
|         if (s->read_count > 0) { | ||||
|             s->read_count--; | ||||
|             if (++s->read_pos == 16) | ||||
|                 s->read_pos = 0; | ||||
|         } | ||||
|         if (s->read_count == 0) { | ||||
|             s->flags |= PL011_FLAG_RXFE; | ||||
|         } | ||||
|         if (s->read_count == s->read_trigger - 1) | ||||
|             s->int_level &= ~ PL011_INT_RX; | ||||
|         pl011_update(s); | ||||
|         return c; | ||||
|     case 1: /* UARTCR */ | ||||
|         return 0; | ||||
|     case 6: /* UARTFR */ | ||||
|         return s->flags; | ||||
|     case 8: /* UARTILPR */ | ||||
|         return s->ilpr; | ||||
|     case 9: /* UARTIBRD */ | ||||
|         return s->ibrd; | ||||
|     case 10: /* UARTFBRD */ | ||||
|         return s->fbrd; | ||||
|     case 11: /* UARTLCR_H */ | ||||
|         return s->lcr; | ||||
|     case 12: /* UARTCR */ | ||||
|         return s->cr; | ||||
|     case 13: /* UARTIFLS */ | ||||
|         return s->ifl; | ||||
|     case 14: /* UARTIMSC */ | ||||
|         return s->int_enabled; | ||||
|     case 15: /* UARTRIS */ | ||||
|         return s->int_level; | ||||
|     case 16: /* UARTMIS */ | ||||
|         return s->int_level & s->int_enabled; | ||||
|     case 18: /* UARTDMACR */ | ||||
|         return s->dmacr; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "pl011_read: Bad offset %x\n", offset); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pl011_set_read_trigger(pl011_state *s) | ||||
| { | ||||
| #if 0 | ||||
|     /* The docs say the RX interrupt is triggered when the FIFO exceeds
 | ||||
|        the threshold.  However linux only reads the FIFO in response to an | ||||
|        interrupt.  Triggering the interrupt when the FIFO is non-empty seems | ||||
|        to make things work.  */ | ||||
|     if (s->lcr & 0x10) | ||||
|         s->read_trigger = (s->ifl >> 1) & 0x1c; | ||||
|     else | ||||
| #endif | ||||
|         s->read_trigger = 1; | ||||
| } | ||||
| 
 | ||||
| static void pl011_write(void *opaque, target_phys_addr_t offset, | ||||
|                           uint32_t value) | ||||
| { | ||||
|     pl011_state *s = (pl011_state *)opaque; | ||||
|     unsigned char ch; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* UARTDR */ | ||||
|         /* ??? Check if transmitter is enabled.  */ | ||||
|         ch = value; | ||||
|         if (s->chr) | ||||
|             qemu_chr_write(s->chr, &ch, 1); | ||||
|         s->int_level |= PL011_INT_TX; | ||||
|         pl011_update(s); | ||||
|         break; | ||||
|     case 1: /* UARTCR */ | ||||
|         s->cr = value; | ||||
|         break; | ||||
|     case 8: /* UARTUARTILPR */ | ||||
|         s->ilpr = value; | ||||
|         break; | ||||
|     case 9: /* UARTIBRD */ | ||||
|         s->ibrd = value; | ||||
|         break; | ||||
|     case 10: /* UARTFBRD */ | ||||
|         s->fbrd = value; | ||||
|         break; | ||||
|     case 11: /* UARTLCR_H */ | ||||
|         s->lcr = value; | ||||
|         pl011_set_read_trigger(s); | ||||
|         break; | ||||
|     case 12: /* UARTCR */ | ||||
|         /* ??? Need to implement the enable and loopback bits.  */ | ||||
|         s->cr = value; | ||||
|         break; | ||||
|     case 13: /* UARTIFS */ | ||||
|         s->ifl = value; | ||||
|         pl011_set_read_trigger(s); | ||||
|         break; | ||||
|     case 14: /* UARTIMSC */ | ||||
|         s->int_enabled = value; | ||||
|         pl011_update(s); | ||||
|         break; | ||||
|     case 17: /* UARTICR */ | ||||
|         s->int_level &= ~value; | ||||
|         pl011_update(s); | ||||
|         break; | ||||
|     case 18: /* UARTDMACR */ | ||||
|         s->dmacr = value; | ||||
|         if (value & 3) | ||||
|             cpu_abort(cpu_single_env, "PL011: DMA not implemented\n"); | ||||
|         break; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "pl011_write: Bad offset %x\n", offset); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static int pl011_can_recieve(void *opaque) | ||||
| { | ||||
|     pl011_state *s = (pl011_state *)opaque; | ||||
| 
 | ||||
|     if (s->lcr & 0x10) | ||||
|         return s->read_count < 16; | ||||
|     else | ||||
|         return s->read_count < 1; | ||||
| } | ||||
| 
 | ||||
| static void pl011_recieve(void *opaque, const uint8_t *buf, int size) | ||||
| { | ||||
|     pl011_state *s = (pl011_state *)opaque; | ||||
|     int slot; | ||||
| 
 | ||||
|     slot = s->read_pos + s->read_count; | ||||
|     if (slot >= 16) | ||||
|         slot -= 16; | ||||
|     s->read_fifo[slot] = *buf; | ||||
|     s->read_count++; | ||||
|     s->flags &= ~PL011_FLAG_RXFE; | ||||
|     if (s->cr & 0x10 || s->read_count == 16) { | ||||
|         s->flags |= PL011_FLAG_RXFF; | ||||
|     } | ||||
|     if (s->read_count == s->read_trigger) { | ||||
|         s->int_level |= PL011_INT_RX; | ||||
|         pl011_update(s); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pl011_event(void *opaque, int event) | ||||
| { | ||||
|     /* ??? Should probably implement break.  */ | ||||
| } | ||||
| 
 | ||||
| static CPUReadMemoryFunc *pl011_readfn[] = { | ||||
|    pl011_read, | ||||
|    pl011_read, | ||||
|    pl011_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *pl011_writefn[] = { | ||||
|    pl011_write, | ||||
|    pl011_write, | ||||
|    pl011_write | ||||
| }; | ||||
| 
 | ||||
| void pl011_init(uint32_t base, void *pic, int irq, | ||||
|                 CharDriverState *chr) | ||||
| { | ||||
|     int iomemtype; | ||||
|     pl011_state *s; | ||||
| 
 | ||||
|     s = (pl011_state *)qemu_mallocz(sizeof(pl011_state)); | ||||
|     iomemtype = cpu_register_io_memory(0, pl011_readfn, | ||||
|                                        pl011_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x00000fff, iomemtype); | ||||
|     s->base = base; | ||||
|     s->pic = pic; | ||||
|     s->irq = irq; | ||||
|     s->chr = chr; | ||||
|     s->read_trigger = 1; | ||||
|     s->ifl = 0x12; | ||||
|     s->cr = 0x300; | ||||
|     s->flags = 0x90; | ||||
|     if (chr){  | ||||
|         qemu_chr_add_read_handler(chr, pl011_can_recieve, pl011_recieve, s); | ||||
|         qemu_chr_add_event_handler(chr, pl011_event); | ||||
|     } | ||||
|     /* ??? Save/restore.  */ | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,127 @@ | |||
| /* 
 | ||||
|  * Arm PrimeCell PL050 Kyeboard / Mouse Interface | ||||
|  * | ||||
|  * Copyright (c) 2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the GPL. | ||||
|  */ | ||||
| 
 | ||||
| #include "vl.h" | ||||
| 
 | ||||
| typedef struct { | ||||
|     void *dev; | ||||
|     uint32_t base; | ||||
|     uint32_t cr; | ||||
|     uint32_t clk; | ||||
|     uint32_t last; | ||||
|     void *pic; | ||||
|     int pending; | ||||
|     int irq; | ||||
|     int is_mouse; | ||||
| } pl050_state; | ||||
| 
 | ||||
| static const unsigned char pl050_id[] = | ||||
| { 0x50, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | ||||
| 
 | ||||
| static void pl050_update(void *opaque, int level) | ||||
| { | ||||
|     pl050_state *s = (pl050_state *)opaque; | ||||
|     int raise; | ||||
| 
 | ||||
|     s->pending = level; | ||||
|     raise = (s->pending && (s->cr & 0x10) != 0) | ||||
|             || (s->cr & 0x08) != 0; | ||||
|     pic_set_irq_new(s->pic, s->irq, raise); | ||||
| } | ||||
| 
 | ||||
| static uint32_t pl050_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     pl050_state *s = (pl050_state *)opaque; | ||||
|     offset -= s->base; | ||||
|     if (offset >= 0xfe0 && offset < 0x1000) | ||||
|         return pl050_id[(offset - 0xfe0) >> 2]; | ||||
| 
 | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* KMICR */ | ||||
|         return s->cr; | ||||
|     case 1: /* KMISTAT */ | ||||
|         /* KMIC and KMID bits not implemented.  */ | ||||
|         if (s->pending) { | ||||
|             return 0x10; | ||||
|         } else { | ||||
|             return 0; | ||||
|         } | ||||
|     case 2: /* KMIDATA */ | ||||
|         if (s->pending) | ||||
|             s->last = ps2_read_data(s->dev); | ||||
|         return s->last; | ||||
|     case 3: /* KMICLKDIV */ | ||||
|         return s->clk; | ||||
|     case 4: /* KMIIR */ | ||||
|         return s->pending | 2; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "pl050_read: Bad offset %x\n", offset); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pl050_write(void *opaque, target_phys_addr_t offset, | ||||
|                           uint32_t value) | ||||
| { | ||||
|     pl050_state *s = (pl050_state *)opaque; | ||||
|     offset -= s->base; | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* KMICR */ | ||||
|         s->cr = value; | ||||
|         pl050_update(s, s->pending); | ||||
|         /* ??? Need to implement the enable/disable bit.  */ | ||||
|         break; | ||||
|     case 2: /* KMIDATA */ | ||||
|         /* ??? This should toggle the TX interrupt line.  */ | ||||
|         /* ??? This means kbd/mouse can block each other.  */ | ||||
|         if (s->is_mouse) { | ||||
|             ps2_write_mouse(s->dev, value); | ||||
|         } else { | ||||
|             ps2_write_keyboard(s->dev, value); | ||||
|         } | ||||
|         break; | ||||
|     case 3: /* KMICLKDIV */ | ||||
|         s->clk = value; | ||||
|         return; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "pl050_write: Bad offset %x\n", offset); | ||||
|     } | ||||
| } | ||||
| static CPUReadMemoryFunc *pl050_readfn[] = { | ||||
|    pl050_read, | ||||
|    pl050_read, | ||||
|    pl050_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *pl050_writefn[] = { | ||||
|    pl050_write, | ||||
|    pl050_write, | ||||
|    pl050_write | ||||
| }; | ||||
| 
 | ||||
| void pl050_init(uint32_t base, void *pic, int irq, int is_mouse) | ||||
| { | ||||
|     int iomemtype; | ||||
|     pl050_state *s; | ||||
| 
 | ||||
|     s = (pl050_state *)qemu_mallocz(sizeof(pl050_state)); | ||||
|     iomemtype = cpu_register_io_memory(0, pl050_readfn, | ||||
|                                        pl050_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x00000fff, iomemtype); | ||||
|     s->base = base; | ||||
|     s->pic = pic; | ||||
|     s->irq = irq; | ||||
|     s->is_mouse = is_mouse; | ||||
|     if (is_mouse) | ||||
|         s->dev = ps2_mouse_init(pl050_update, s); | ||||
|     else | ||||
|         s->dev = ps2_kbd_init(pl050_update, s); | ||||
|     /* ??? Save/restore.  */ | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,328 @@ | |||
| /* 
 | ||||
|  * Arm PrimeCell PL080 DMA controller | ||||
|  * | ||||
|  * Copyright (c) 2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the GPL. | ||||
|  */ | ||||
| 
 | ||||
| #include "vl.h" | ||||
| 
 | ||||
| #define PL080_NUM_CHANNELS 8 | ||||
| #define PL080_CONF_E    0x1 | ||||
| #define PL080_CONF_M1   0x2 | ||||
| #define PL080_CONF_M2   0x4 | ||||
| 
 | ||||
| #define PL080_CCONF_H   0x40000 | ||||
| #define PL080_CCONF_A   0x20000 | ||||
| #define PL080_CCONF_L   0x10000 | ||||
| #define PL080_CCONF_ITC 0x08000 | ||||
| #define PL080_CCONF_IE  0x04000 | ||||
| #define PL080_CCONF_E   0x00001 | ||||
| 
 | ||||
| #define PL080_CCTRL_I   0x80000000 | ||||
| #define PL080_CCTRL_DI  0x08000000 | ||||
| #define PL080_CCTRL_SI  0x04000000 | ||||
| #define PL080_CCTRL_D   0x02000000 | ||||
| #define PL080_CCTRL_S   0x01000000 | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t src; | ||||
|     uint32_t dest; | ||||
|     uint32_t lli; | ||||
|     uint32_t ctrl; | ||||
|     uint32_t conf; | ||||
| } pl080_channel; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint32_t base; | ||||
|     uint8_t tc_int; | ||||
|     uint8_t tc_mask; | ||||
|     uint8_t err_int; | ||||
|     uint8_t err_mask; | ||||
|     uint32_t conf; | ||||
|     uint32_t sync; | ||||
|     uint32_t req_single; | ||||
|     uint32_t req_burst; | ||||
|     pl080_channel chan[PL080_NUM_CHANNELS]; | ||||
|     /* Flag to avoid recursive DMA invocations.  */ | ||||
|     int running; | ||||
|     void *pic; | ||||
|     int irq; | ||||
| } pl080_state; | ||||
| 
 | ||||
| static const unsigned char pl080_id[] = | ||||
| { 0x80, 0x10, 0x04, 0x0a, 0x0d, 0xf0, 0x05, 0xb1 }; | ||||
| 
 | ||||
| static void pl080_update(pl080_state *s) | ||||
| { | ||||
|     if ((s->tc_int & s->tc_mask) | ||||
|             || (s->err_int & s->err_mask)) | ||||
|         pic_set_irq_new(s->pic, s->irq, 1); | ||||
|     else | ||||
|         pic_set_irq_new(s->pic, s->irq, 1); | ||||
| } | ||||
| 
 | ||||
| static void pl080_run(pl080_state *s) | ||||
| { | ||||
|     int c; | ||||
|     int flow; | ||||
|     pl080_channel *ch; | ||||
|     int swidth; | ||||
|     int dwidth; | ||||
|     int xsize; | ||||
|     int n; | ||||
|     int src_id; | ||||
|     int dest_id; | ||||
|     int size; | ||||
|     char buff[4]; | ||||
|     uint32_t req; | ||||
| 
 | ||||
|     s->tc_mask = 0; | ||||
|     for (c = 0; c < PL080_NUM_CHANNELS; c++) { | ||||
|         if (s->chan[c].conf & PL080_CCONF_ITC) | ||||
|             s->tc_mask |= 1 << c; | ||||
|         if (s->chan[c].conf & PL080_CCONF_IE) | ||||
|             s->err_mask |= 1 << c; | ||||
|     } | ||||
| 
 | ||||
|     if ((s->conf & PL080_CONF_E) == 0) | ||||
|         return; | ||||
| 
 | ||||
| cpu_abort(cpu_single_env, "DMA active\n"); | ||||
|     /* If we are already in the middle of a DMA operation then indicate that
 | ||||
|        there may be new DMA requests and return immediately.  */ | ||||
|     if (s->running) { | ||||
|         s->running++; | ||||
|         return; | ||||
|     } | ||||
|     s->running = 1; | ||||
|     while (s->running) { | ||||
|         for (c = 0; c < PL080_NUM_CHANNELS; c++) { | ||||
|             ch = &s->chan[c]; | ||||
| again: | ||||
|             /* Test if thiws channel has any pending DMA requests.  */ | ||||
|             if ((ch->conf & (PL080_CCONF_H | PL080_CCONF_E)) | ||||
|                     != PL080_CCONF_E) | ||||
|                 continue; | ||||
|             flow = (ch->conf >> 11) & 7; | ||||
|             if (flow >= 4) { | ||||
|                 cpu_abort(cpu_single_env,  | ||||
|                     "pl080_run: Peripheral flow control not implemented\n"); | ||||
|             } | ||||
|             src_id = (ch->conf >> 1) & 0x1f; | ||||
|             dest_id = (ch->conf >> 6) & 0x1f; | ||||
|             size = ch->ctrl & 0xfff; | ||||
|             req = s->req_single | s->req_burst; | ||||
|             switch (flow) { | ||||
|             case 0: | ||||
|                 break; | ||||
|             case 1: | ||||
|                 if ((req & (1u << dest_id)) == 0) | ||||
|                     size = 0; | ||||
|                 break; | ||||
|             case 2: | ||||
|                 if ((req & (1u << src_id)) == 0) | ||||
|                     size = 0; | ||||
|                 break; | ||||
|             case 3: | ||||
|                 if ((req & (1u << src_id)) == 0 | ||||
|                         || (req & (1u << dest_id)) == 0) | ||||
|                     size = 0; | ||||
|                 break; | ||||
|             } | ||||
|             if (!size) | ||||
|                 continue; | ||||
| 
 | ||||
|             /* Transfer one element.  */ | ||||
|             /* ??? Should transfer multiple elements for a burst request.  */ | ||||
|             /* ??? Unclear what the proper behavior is when source and
 | ||||
|                destination widths are different.  */ | ||||
|             swidth = 1 << ((ch->ctrl >> 18) & 7); | ||||
|             dwidth = 1 << ((ch->ctrl >> 21) & 7); | ||||
|             for (n = 0; n < dwidth; n+= swidth) { | ||||
|                 cpu_physical_memory_read(ch->src, buff + n, swidth); | ||||
|                 if (ch->ctrl & PL080_CCTRL_SI) | ||||
|                     ch->src += swidth; | ||||
|             } | ||||
|             xsize = (dwidth < swidth) ? swidth : dwidth; | ||||
|             /* ??? This may pad the value incorrectly for dwidth < 32.  */ | ||||
|             for (n = 0; n < xsize; n += dwidth) { | ||||
|                 cpu_physical_memory_write(ch->dest + n, buff + n, dwidth); | ||||
|                 if (ch->ctrl & PL080_CCTRL_DI) | ||||
|                     ch->dest += swidth; | ||||
|             } | ||||
| 
 | ||||
|             size--; | ||||
|             ch->ctrl = (ch->ctrl & 0xfffff000) | size; | ||||
|             if (size == 0) { | ||||
|                 /* Transfer complete.  */ | ||||
|                 if (ch->lli) { | ||||
|                     ch->src = ldl_phys(ch->lli); | ||||
|                     ch->dest = ldl_phys(ch->lli + 4); | ||||
|                     ch->ctrl = ldl_phys(ch->lli + 12); | ||||
|                     ch->lli = ldl_phys(ch->lli + 8); | ||||
|                 } else { | ||||
|                     ch->conf &= ~PL080_CCONF_E; | ||||
|                 } | ||||
|                 if (ch->ctrl & PL080_CCTRL_I) { | ||||
|                     s->tc_int |= 1 << c; | ||||
|                 } | ||||
|             } | ||||
|             goto again; | ||||
|         } | ||||
|         if (--s->running) | ||||
|             s->running = 1; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static uint32_t pl080_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     pl080_state *s = (pl080_state *)opaque; | ||||
|     uint32_t i; | ||||
|     uint32_t mask; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     if (offset >= 0xfe0 && offset < 0x1000) { | ||||
|         return pl080_id[(offset - 0xfe0) >> 2]; | ||||
|     } | ||||
|     if (offset >= 0x100 && offset < 0x200) { | ||||
|         i = (offset & 0xe0) >> 5; | ||||
|         switch (offset >> 2) { | ||||
|         case 0: /* SrcAddr */ | ||||
|             return s->chan[i].src; | ||||
|         case 1: /* DestAddr */ | ||||
|             return s->chan[i].dest; | ||||
|         case 2: /* LLI */ | ||||
|             return s->chan[i].lli; | ||||
|         case 3: /* Control */ | ||||
|             return s->chan[i].ctrl; | ||||
|         case 4: /* Configuration */ | ||||
|             return s->chan[i].conf; | ||||
|         default: | ||||
|             goto bad_offset; | ||||
|         } | ||||
|     } | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* IntStatus */ | ||||
|         return (s->tc_int & s->tc_mask) | (s->err_int & s->err_mask); | ||||
|     case 1: /* IntTCStatus */ | ||||
|         return (s->tc_int & s->tc_mask); | ||||
|     case 3: /* IntErrorStatus */ | ||||
|         return (s->err_int & s->err_mask); | ||||
|     case 5: /* RawIntTCStatus */ | ||||
|         return s->tc_int; | ||||
|     case 6: /* RawIntErrorStatus */ | ||||
|         return s->err_int; | ||||
|     case 7: /* EnbldChns */ | ||||
|         mask = 0; | ||||
|         for (i = 0; i < PL080_NUM_CHANNELS; i++) { | ||||
|             if (s->chan[i].conf & PL080_CCONF_E) | ||||
|                 mask |= 1 << i; | ||||
|         } | ||||
|         return mask; | ||||
|     case 8: /* SoftBReq */ | ||||
|     case 9: /* SoftSReq */ | ||||
|     case 10: /* SoftLBReq */ | ||||
|     case 11: /* SoftLSReq */ | ||||
|         /* ??? Implement these. */ | ||||
|         return 0; | ||||
|     case 12: /* Configuration */ | ||||
|         return s->conf; | ||||
|     case 13: /* Sync */ | ||||
|         return s->sync; | ||||
|     default: | ||||
|     bad_offset: | ||||
|         cpu_abort(cpu_single_env, "pl080_read: Bad offset %x\n", offset); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pl080_write(void *opaque, target_phys_addr_t offset, | ||||
|                           uint32_t value) | ||||
| { | ||||
|     pl080_state *s = (pl080_state *)opaque; | ||||
|     int i; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     if (offset >= 0x100 && offset < 0x200) { | ||||
|         i = (offset & 0xe0) >> 5; | ||||
|         switch (offset >> 2) { | ||||
|         case 0: /* SrcAddr */ | ||||
|             s->chan[i].src = value; | ||||
|             break; | ||||
|         case 1: /* DestAddr */ | ||||
|             s->chan[i].dest = value; | ||||
|             break; | ||||
|         case 2: /* LLI */ | ||||
|             s->chan[i].lli = value; | ||||
|             break; | ||||
|         case 3: /* Control */ | ||||
|             s->chan[i].ctrl = value; | ||||
|             break; | ||||
|         case 4: /* Configuration */ | ||||
|             s->chan[i].conf = value; | ||||
|             pl080_run(s); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     switch (offset >> 2) { | ||||
|     case 2: /* IntTCClear */ | ||||
|         s->tc_int &= ~value; | ||||
|         break; | ||||
|     case 4: /* IntErrorClear */ | ||||
|         s->err_int &= ~value; | ||||
|         break; | ||||
|     case 8: /* SoftBReq */ | ||||
|     case 9: /* SoftSReq */ | ||||
|     case 10: /* SoftLBReq */ | ||||
|     case 11: /* SoftLSReq */ | ||||
|         /* ??? Implement these.  */ | ||||
|         cpu_abort(cpu_single_env, "pl080_write: Soft DMA not implemented\n"); | ||||
|         break; | ||||
|     case 12: /* Configuration */ | ||||
|         s->conf = value; | ||||
|         if (s->conf & (PL080_CONF_M1 | PL080_CONF_M1)) { | ||||
|             cpu_abort(cpu_single_env, | ||||
|                       "pl080_write: Big-endian DMA not implemented\n"); | ||||
|         } | ||||
|         pl080_run(s); | ||||
|         break; | ||||
|     case 13: /* Sync */ | ||||
|         s->sync = value; | ||||
|         break; | ||||
|     default: | ||||
|         cpu_abort(cpu_single_env, "pl080_write: Bad offset %x\n", offset); | ||||
|     } | ||||
|     pl080_update(s); | ||||
| } | ||||
| 
 | ||||
| static CPUReadMemoryFunc *pl080_readfn[] = { | ||||
|    pl080_read, | ||||
|    pl080_read, | ||||
|    pl080_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *pl080_writefn[] = { | ||||
|    pl080_write, | ||||
|    pl080_write, | ||||
|    pl080_write | ||||
| }; | ||||
| 
 | ||||
| void *pl080_init(uint32_t base, void *pic, int irq) | ||||
| { | ||||
|     int iomemtype; | ||||
|     pl080_state *s; | ||||
| 
 | ||||
|     s = (pl080_state *)qemu_mallocz(sizeof(pl080_state)); | ||||
|     iomemtype = cpu_register_io_memory(0, pl080_readfn, | ||||
|                                        pl080_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x00000fff, iomemtype); | ||||
|     s->base = base; | ||||
|     s->pic = pic; | ||||
|     s->irq = irq; | ||||
|     /* ??? Save/restore.  */ | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										27
									
								
								hw/pl110.c
								
								
								
								
							
							
						
						
									
										27
									
								
								hw/pl110.c
								
								
								
								
							|  | @ -1,7 +1,7 @@ | |||
| /* 
 | ||||
|  * Arm PrimeCell PL110 Color LCD Controller | ||||
|  * | ||||
|  * Copyright (c) 2005 CodeSourcery, LLC. | ||||
|  * Copyright (c) 2005-2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the GNU LGPL | ||||
|  | @ -27,6 +27,8 @@ enum pl110_bppmode | |||
| typedef struct { | ||||
|     uint32_t base; | ||||
|     DisplayState *ds; | ||||
|     /* The Versatile/PB uses a slightly modified PL110 controller.  */ | ||||
|     int versatile; | ||||
|     void *pic; | ||||
|     uint32_t timing[4]; | ||||
|     uint32_t cr; | ||||
|  | @ -46,6 +48,15 @@ typedef struct { | |||
| static const unsigned char pl110_id[] = | ||||
| { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | ||||
| 
 | ||||
| /* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
 | ||||
|    has a different ID.  However Linux only looks for the normal ID.  */ | ||||
| #if 0 | ||||
| static const unsigned char pl110_versatile_id[] = | ||||
| { 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; | ||||
| #else | ||||
| #define pl110_versatile_id pl110_id | ||||
| #endif | ||||
| 
 | ||||
| static inline uint32_t rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b) | ||||
| { | ||||
|     return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6); | ||||
|  | @ -101,7 +112,7 @@ static void pl110_update_display(void *opaque) | |||
|     int src_width; | ||||
|     uint8_t *dest; | ||||
|     uint8_t *src; | ||||
|     int first, last; | ||||
|     int first, last = 0; | ||||
|     int dirty, new_dirty; | ||||
|     int i; | ||||
| 
 | ||||
|  | @ -269,7 +280,10 @@ static uint32_t pl110_read(void *opaque, target_phys_addr_t offset) | |||
| 
 | ||||
|     offset -= s->base; | ||||
|     if (offset >= 0xfe0 && offset < 0x1000) { | ||||
|         return pl110_id[(offset - 0xfe0) >> 2]; | ||||
|         if (s->versatile) | ||||
|             return pl110_versatile_id[(offset - 0xfe0) >> 2]; | ||||
|         else | ||||
|             return pl110_id[(offset - 0xfe0) >> 2]; | ||||
|     } | ||||
|     if (offset >= 0x200 && offset < 0x400) { | ||||
|         return s->raw_pallette[(offset - 0x200) >> 2]; | ||||
|  | @ -347,10 +361,16 @@ static void pl110_write(void *opaque, target_phys_addr_t offset, | |||
|         s->lpbase = val; | ||||
|         break; | ||||
|     case 6: /* LCDIMSC */ | ||||
|         if (s->versatile) | ||||
|             goto control; | ||||
|     imsc: | ||||
|         s->int_mask = val; | ||||
|         pl110_update(s); | ||||
|         break; | ||||
|     case 7: /* LCDControl */ | ||||
|         if (s->versatile) | ||||
|             goto imsc; | ||||
|     control: | ||||
|         s->cr = val; | ||||
|         s->bpp = (val >> 1) & 7; | ||||
|         if (pl110_enabled(s)) { | ||||
|  | @ -390,6 +410,7 @@ void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq, | |||
|     cpu_register_physical_memory(base, 0x00000fff, iomemtype); | ||||
|     s->base = base; | ||||
|     s->ds = ds; | ||||
|     s->versatile = versatile; | ||||
|     s->pic = pic; | ||||
|     s->irq = irq; | ||||
|     graphic_console_init(ds, pl110_update_display, pl110_invalidate_display, | ||||
|  |  | |||
|  | @ -0,0 +1,252 @@ | |||
| /* 
 | ||||
|  * Arm PrimeCell PL190 Vector Interrupt Controller | ||||
|  * | ||||
|  * Copyright (c) 2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the GPL. | ||||
|  */ | ||||
| 
 | ||||
| #include "vl.h" | ||||
| #include "arm_pic.h" | ||||
| 
 | ||||
| /* The number of virtual priority levels.  16 user vectors plus the
 | ||||
|    unvectored IRQ.  Chained interrupts would require an additional level | ||||
|    if implemented.  */ | ||||
| 
 | ||||
| #define PL190_NUM_PRIO 17 | ||||
| 
 | ||||
| typedef struct { | ||||
|     arm_pic_handler handler; | ||||
|     uint32_t base; | ||||
|     DisplayState *ds; | ||||
|     uint32_t level; | ||||
|     uint32_t soft_level; | ||||
|     uint32_t irq_enable; | ||||
|     uint32_t fiq_select; | ||||
|     uint32_t default_addr; | ||||
|     uint8_t vect_control[16]; | ||||
|     uint32_t vect_addr[PL190_NUM_PRIO]; | ||||
|     /* Mask containing interrupts with higher priority than this one.  */ | ||||
|     uint32_t prio_mask[PL190_NUM_PRIO + 1]; | ||||
|     int protected; | ||||
|     /* Current priority level.  */ | ||||
|     int priority; | ||||
|     int prev_prio[PL190_NUM_PRIO]; | ||||
|     void *parent; | ||||
|     int irq; | ||||
|     int fiq; | ||||
| } pl190_state; | ||||
| 
 | ||||
| static const unsigned char pl190_id[] = | ||||
| { 0x90, 0x11, 0x04, 0x00, 0x0D, 0xf0, 0x05, 0xb1 }; | ||||
| 
 | ||||
| static inline uint32_t pl190_irq_level(pl190_state *s) | ||||
| { | ||||
|     return (s->level | s->soft_level) & s->irq_enable & ~s->fiq_select; | ||||
| } | ||||
| 
 | ||||
| /* Update interrupts.  */ | ||||
| static void pl190_update(pl190_state *s) | ||||
| { | ||||
|     uint32_t level = pl190_irq_level(s); | ||||
|     int set; | ||||
| 
 | ||||
|     set = (level & s->prio_mask[s->priority]) != 0; | ||||
|     pic_set_irq_new(s->parent, s->irq, set); | ||||
|     set = ((s->level | s->soft_level) & s->fiq_select) != 0; | ||||
|     pic_set_irq_new(s->parent, s->fiq, set); | ||||
| } | ||||
| 
 | ||||
| static void pl190_set_irq(void *opaque, int irq, int level) | ||||
| { | ||||
|     pl190_state *s = (pl190_state *)opaque; | ||||
| 
 | ||||
|     if (level) | ||||
|         s->level |= 1u << irq; | ||||
|     else | ||||
|         s->level &= ~(1u << irq); | ||||
|     pl190_update(s); | ||||
| } | ||||
| 
 | ||||
| static void pl190_update_vectors(pl190_state *s) | ||||
| { | ||||
|     uint32_t mask; | ||||
|     int i; | ||||
|     int n; | ||||
| 
 | ||||
|     mask = 0; | ||||
|     for (i = 0; i < 16; i++) | ||||
|       { | ||||
|         s->prio_mask[i] = mask; | ||||
|         if (s->vect_control[i] & 0x20) | ||||
|           { | ||||
|             n = s->vect_control[i] & 0x1f; | ||||
|             mask |= 1 << n; | ||||
|           } | ||||
|       } | ||||
|     s->prio_mask[16] = mask; | ||||
|     pl190_update(s); | ||||
| } | ||||
| 
 | ||||
| static uint32_t pl190_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     pl190_state *s = (pl190_state *)opaque; | ||||
|     int i; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     if (offset >= 0xfe0 && offset < 0x1000) { | ||||
|         return pl190_id[(offset - 0xfe0) >> 2]; | ||||
|     } | ||||
|     if (offset >= 0x100 && offset < 0x140) { | ||||
|         return s->vect_addr[(offset - 0x100) >> 2]; | ||||
|     } | ||||
|     if (offset >= 0x200 && offset < 0x240) { | ||||
|         return s->vect_control[(offset - 0x200) >> 2]; | ||||
|     } | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* IRQSTATUS */ | ||||
|         return pl190_irq_level(s); | ||||
|     case 1: /* FIQSATUS */ | ||||
|         return (s->level | s->soft_level) & s->fiq_select; | ||||
|     case 2: /* RAWINTR */ | ||||
|         return s->level | s->soft_level; | ||||
|     case 3: /* INTSELECT */ | ||||
|         return s->fiq_select; | ||||
|     case 4: /* INTENABLE */ | ||||
|         return s->irq_enable; | ||||
|     case 6: /* SOFTINT */ | ||||
|         return s->soft_level; | ||||
|     case 8: /* PROTECTION */ | ||||
|         return s->protected; | ||||
|     case 12: /* VECTADDR */ | ||||
|         /* Read vector address at the start of an ISR.  Increases the
 | ||||
|            current priority level to that of the current interrupt.  */ | ||||
|         for (i = 0; i < s->priority; i++) | ||||
|           { | ||||
|             if ((s->level | s->soft_level) & s->prio_mask[i]) | ||||
|               break; | ||||
|           } | ||||
|         /* Reading this value with no pending interrupts is undefined.
 | ||||
|            We return the default address.  */ | ||||
|         if (i == PL190_NUM_PRIO) | ||||
|           return s->vect_addr[16]; | ||||
|         if (i < s->priority) | ||||
|           { | ||||
|             s->prev_prio[i] = s->priority; | ||||
|             s->priority = i; | ||||
|             pl190_update(s); | ||||
|           } | ||||
|         return s->vect_addr[s->priority]; | ||||
|     case 13: /* DEFVECTADDR */ | ||||
|         return s->vect_addr[16]; | ||||
|     default: | ||||
|         cpu_abort (cpu_single_env, "pl190_read: Bad offset %x\n", offset); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pl190_write(void *opaque, target_phys_addr_t offset, uint32_t val) | ||||
| { | ||||
|     pl190_state *s = (pl190_state *)opaque; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     if (offset >= 0x100 && offset < 0x140) { | ||||
|         s->vect_addr[(offset - 0x100) >> 2] = val; | ||||
|         pl190_update_vectors(s); | ||||
|         return; | ||||
|     } | ||||
|     if (offset >= 0x200 && offset < 0x240) { | ||||
|         s->vect_control[(offset - 0x200) >> 2] = val; | ||||
|         pl190_update_vectors(s); | ||||
|         return; | ||||
|     } | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* SELECT */ | ||||
|         /* This is a readonly register, but linux tries to write to it
 | ||||
|            anyway.  Ignore the write.  */ | ||||
|         break; | ||||
|     case 3: /* INTSELECT */ | ||||
|         s->fiq_select = val; | ||||
|         break; | ||||
|     case 4: /* INTENABLE */ | ||||
|         s->irq_enable |= val; | ||||
|         break; | ||||
|     case 5: /* INTENCLEAR */ | ||||
|         s->irq_enable &= ~val; | ||||
|         break; | ||||
|     case 6: /* SOFTINT */ | ||||
|         s->soft_level |= val; | ||||
|         break; | ||||
|     case 7: /* SOFTINTCLEAR */ | ||||
|         s->soft_level &= ~val; | ||||
|         break; | ||||
|     case 8: /* PROTECTION */ | ||||
|         /* TODO: Protection (supervisor only access) is not implemented.  */ | ||||
|         s->protected = val & 1; | ||||
|         break; | ||||
|     case 12: /* VECTADDR */ | ||||
|         /* Restore the previous priority level.  The value written is
 | ||||
|            ignored.  */ | ||||
|         if (s->priority < PL190_NUM_PRIO) | ||||
|             s->priority = s->prev_prio[s->priority]; | ||||
|         break; | ||||
|     case 13: /* DEFVECTADDR */ | ||||
|         s->default_addr = val; | ||||
|         break; | ||||
|     case 0xc0: /* ITCR */ | ||||
|         if (val) | ||||
|             cpu_abort(cpu_single_env, "pl190: Test mode not implemented\n"); | ||||
|         break; | ||||
|     default: | ||||
|         cpu_abort(cpu_single_env, "pl190_write: Bad offset %x\n", offset); | ||||
|         return; | ||||
|     } | ||||
|     pl190_update(s); | ||||
| } | ||||
| 
 | ||||
| static CPUReadMemoryFunc *pl190_readfn[] = { | ||||
|    pl190_read, | ||||
|    pl190_read, | ||||
|    pl190_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *pl190_writefn[] = { | ||||
|    pl190_write, | ||||
|    pl190_write, | ||||
|    pl190_write | ||||
| }; | ||||
| 
 | ||||
| void pl190_reset(pl190_state *s) | ||||
| { | ||||
|   int i; | ||||
| 
 | ||||
|   for (i = 0; i < 16; i++) | ||||
|     { | ||||
|       s->vect_addr[i] = 0; | ||||
|       s->vect_control[i] = 0; | ||||
|     } | ||||
|   s->vect_addr[16] = 0; | ||||
|   s->prio_mask[17] = 0xffffffff; | ||||
|   s->priority = PL190_NUM_PRIO; | ||||
|   pl190_update_vectors(s); | ||||
| } | ||||
| 
 | ||||
| void *pl190_init(uint32_t base, void *parent, int irq, int fiq) | ||||
| { | ||||
|     pl190_state *s; | ||||
|     int iomemtype; | ||||
| 
 | ||||
|     s = (pl190_state *)qemu_mallocz(sizeof(pl190_state)); | ||||
|     iomemtype = cpu_register_io_memory(0, pl190_readfn, | ||||
|                                        pl190_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x00000fff, iomemtype); | ||||
|     s->handler = pl190_set_irq; | ||||
|     s->base = base; | ||||
|     s->parent = parent; | ||||
|     s->irq = irq; | ||||
|     s->fiq = fiq; | ||||
|     pl190_reset(s); | ||||
|     /* ??? Save/restore.  */ | ||||
|     return s; | ||||
| } | ||||
|  | @ -0,0 +1,321 @@ | |||
| /* 
 | ||||
|  * ARM Versatile Platform Baseboard System emulation. | ||||
|  * | ||||
|  * Copyright (c) 2005-2006 CodeSourcery. | ||||
|  * Written by Paul Brook | ||||
|  * | ||||
|  * This code is licenced under the GPL. | ||||
|  */ | ||||
| 
 | ||||
| #include "vl.h" | ||||
| #include "arm_pic.h" | ||||
| 
 | ||||
| #define KERNEL_ARGS_ADDR 0x100 | ||||
| #define KERNEL_LOAD_ADDR 0x00010000 | ||||
| #define INITRD_LOAD_ADDR 0x00800000 | ||||
| 
 | ||||
| /* Primary interrupt controller.  */ | ||||
| 
 | ||||
| typedef struct vpb_sic_state | ||||
| { | ||||
|   arm_pic_handler handler; | ||||
|   uint32_t base; | ||||
|   uint32_t level; | ||||
|   uint32_t mask; | ||||
|   uint32_t pic_enable; | ||||
|   void *parent; | ||||
|   int irq; | ||||
| } vpb_sic_state; | ||||
| 
 | ||||
| static void vpb_sic_update(vpb_sic_state *s) | ||||
| { | ||||
|     uint32_t flags; | ||||
| 
 | ||||
|     flags = s->level & s->mask; | ||||
|     pic_set_irq_new(s->parent, s->irq, flags != 0); | ||||
| } | ||||
| 
 | ||||
| static void vpb_sic_update_pic(vpb_sic_state *s) | ||||
| { | ||||
|     int i; | ||||
|     uint32_t mask; | ||||
| 
 | ||||
|     for (i = 21; i <= 30; i++) { | ||||
|         mask = 1u << i; | ||||
|         if (!(s->pic_enable & mask)) | ||||
|             continue; | ||||
|         pic_set_irq_new(s->parent, i, (s->level & mask) != 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void vpb_sic_set_irq(void *opaque, int irq, int level) | ||||
| { | ||||
|     vpb_sic_state *s = (vpb_sic_state *)opaque; | ||||
|     if (level) | ||||
|         s->level |= 1u << irq; | ||||
|     else | ||||
|         s->level &= ~(1u << irq); | ||||
|     if (s->pic_enable & (1u << irq)) | ||||
|         pic_set_irq_new(s->parent, irq, level); | ||||
|     vpb_sic_update(s); | ||||
| } | ||||
| 
 | ||||
| static uint32_t vpb_sic_read(void *opaque, target_phys_addr_t offset) | ||||
| { | ||||
|     vpb_sic_state *s = (vpb_sic_state *)opaque; | ||||
| 
 | ||||
|     offset -= s->base; | ||||
|     switch (offset >> 2) { | ||||
|     case 0: /* STATUS */ | ||||
|         return s->level & s->mask; | ||||
|     case 1: /* RAWSTAT */ | ||||
|         return s->level; | ||||
|     case 2: /* ENABLE */ | ||||
|         return s->mask; | ||||
|     case 4: /* SOFTINT */ | ||||
|         return s->level & 1; | ||||
|     case 8: /* PICENABLE */ | ||||
|         return s->pic_enable; | ||||
|     default: | ||||
|         printf ("vpb_sic_read: Bad register offset 0x%x\n", offset); | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void vpb_sic_write(void *opaque, target_phys_addr_t offset, | ||||
|                           uint32_t value) | ||||
| { | ||||
|     vpb_sic_state *s = (vpb_sic_state *)opaque; | ||||
|     offset -= s->base; | ||||
| 
 | ||||
|     switch (offset >> 2) { | ||||
|     case 2: /* ENSET */ | ||||
|         s->mask |= value; | ||||
|         break; | ||||
|     case 3: /* ENCLR */ | ||||
|         s->mask &= ~value; | ||||
|         break; | ||||
|     case 4: /* SOFTINTSET */ | ||||
|         if (value) | ||||
|             s->mask |= 1; | ||||
|         break; | ||||
|     case 5: /* SOFTINTCLR */ | ||||
|         if (value) | ||||
|             s->mask &= ~1u; | ||||
|         break; | ||||
|     case 8: /* PICENSET */ | ||||
|         s->pic_enable |= (value & 0x7fe00000); | ||||
|         vpb_sic_update_pic(s); | ||||
|         break; | ||||
|     case 9: /* PICENCLR */ | ||||
|         s->pic_enable &= ~value; | ||||
|         vpb_sic_update_pic(s); | ||||
|         break; | ||||
|     default: | ||||
|         printf ("vpb_sic_write: Bad register offset 0x%x\n", offset); | ||||
|         return; | ||||
|     } | ||||
|     vpb_sic_update(s); | ||||
| } | ||||
| 
 | ||||
| static CPUReadMemoryFunc *vpb_sic_readfn[] = { | ||||
|    vpb_sic_read, | ||||
|    vpb_sic_read, | ||||
|    vpb_sic_read | ||||
| }; | ||||
| 
 | ||||
| static CPUWriteMemoryFunc *vpb_sic_writefn[] = { | ||||
|    vpb_sic_write, | ||||
|    vpb_sic_write, | ||||
|    vpb_sic_write | ||||
| }; | ||||
| 
 | ||||
| static vpb_sic_state *vpb_sic_init(uint32_t base, void *parent, int irq) | ||||
| { | ||||
|     vpb_sic_state *s; | ||||
|     int iomemtype; | ||||
| 
 | ||||
|     s = (vpb_sic_state *)qemu_mallocz(sizeof(vpb_sic_state)); | ||||
|     if (!s) | ||||
|         return NULL; | ||||
|     s->handler = vpb_sic_set_irq; | ||||
|     s->base = base; | ||||
|     s->parent = parent; | ||||
|     s->irq = irq; | ||||
|     iomemtype = cpu_register_io_memory(0, vpb_sic_readfn, | ||||
|                                        vpb_sic_writefn, s); | ||||
|     cpu_register_physical_memory(base, 0x00000fff, iomemtype); | ||||
|     /* ??? Save/restore.  */ | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
| /* Board init.  */ | ||||
| 
 | ||||
| /* The worlds second smallest bootloader.  Set r0-r2, then jump to kernel.  */ | ||||
| static uint32_t bootloader[] = { | ||||
|   0xe3a00000, /* mov     r0, #0 */ | ||||
|   0xe3a01083, /* mov     r1, #0x83 */ | ||||
|   0xe3811c01, /* orr     r1, r1, #0x100 */ | ||||
|   0xe59f2000, /* ldr     r2, [pc, #0] */ | ||||
|   0xe59ff000, /* ldr     pc, [pc, #0] */ | ||||
|   0, /* Address of kernel args.  Set by integratorcp_init.  */ | ||||
|   0  /* Kernel entry point.  Set by integratorcp_init.  */ | ||||
| }; | ||||
| 
 | ||||
| static void set_kernel_args(uint32_t ram_size, int initrd_size, | ||||
|                             const char *kernel_cmdline) | ||||
| { | ||||
|     uint32_t *p; | ||||
| 
 | ||||
|     p = (uint32_t *)(phys_ram_base + KERNEL_ARGS_ADDR); | ||||
|     /* ATAG_CORE */ | ||||
|     stl_raw(p++, 5); | ||||
|     stl_raw(p++, 0x54410001); | ||||
|     stl_raw(p++, 1); | ||||
|     stl_raw(p++, 0x1000); | ||||
|     stl_raw(p++, 0); | ||||
|     /* ATAG_MEM */ | ||||
|     stl_raw(p++, 4); | ||||
|     stl_raw(p++, 0x54410002); | ||||
|     stl_raw(p++, ram_size); | ||||
|     stl_raw(p++, 0); | ||||
|     if (initrd_size) { | ||||
|         /* ATAG_INITRD2 */ | ||||
|         stl_raw(p++, 4); | ||||
|         stl_raw(p++, 0x54420005); | ||||
|         stl_raw(p++, INITRD_LOAD_ADDR); | ||||
|         stl_raw(p++, initrd_size); | ||||
|     } | ||||
|     if (kernel_cmdline && *kernel_cmdline) { | ||||
|         /* ATAG_CMDLINE */ | ||||
|         int cmdline_size; | ||||
| 
 | ||||
|         cmdline_size = strlen(kernel_cmdline); | ||||
|         memcpy (p + 2, kernel_cmdline, cmdline_size + 1); | ||||
|         cmdline_size = (cmdline_size >> 2) + 1; | ||||
|         stl_raw(p++, cmdline_size + 2); | ||||
|         stl_raw(p++, 0x54410009); | ||||
|         p += cmdline_size; | ||||
|     } | ||||
|     /* ATAG_END */ | ||||
|     stl_raw(p++, 0); | ||||
|     stl_raw(p++, 0); | ||||
| } | ||||
| 
 | ||||
| static void vpb_init(int ram_size, int vga_ram_size, int boot_device, | ||||
|                      DisplayState *ds, const char **fd_filename, int snapshot, | ||||
|                      const char *kernel_filename, const char *kernel_cmdline, | ||||
|                      const char *initrd_filename) | ||||
| { | ||||
|     CPUState *env; | ||||
|     int kernel_size; | ||||
|     int initrd_size; | ||||
|     int n; | ||||
|     void *pic; | ||||
|     void *sic; | ||||
| 
 | ||||
|     env = cpu_init(); | ||||
|     cpu_arm_set_model(env, ARM_CPUID_ARM926); | ||||
|     /* ??? RAM shoud repeat to fill physical memory space.  */ | ||||
|     /* SDRAM at address zero.  */ | ||||
|     cpu_register_physical_memory(0, ram_size, IO_MEM_RAM); | ||||
| 
 | ||||
|     pic = arm_pic_init_cpu(env); | ||||
|     pic = pl190_init(0x10140000, pic, ARM_PIC_CPU_IRQ, ARM_PIC_CPU_FIQ); | ||||
|     sic = vpb_sic_init(0x10003000, pic, 31); | ||||
|     pl050_init(0x10006000, sic, 3, 0); | ||||
|     pl050_init(0x10007000, sic, 4, 1); | ||||
| 
 | ||||
|     /* TODO: Init PCI NICs.  */ | ||||
|     if (nd_table[0].vlan) { | ||||
|         if (nd_table[0].model == NULL | ||||
|             || strcmp(nd_table[0].model, "smc91c111") == 0) { | ||||
|             smc91c111_init(&nd_table[0], 0x10010000, sic, 25); | ||||
|         } else { | ||||
|             fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model); | ||||
|             exit (1); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pl011_init(0x101f1000, pic, 12, serial_hds[0]); | ||||
|     pl011_init(0x101f2000, pic, 13, serial_hds[1]); | ||||
|     pl011_init(0x101f3000, pic, 14, serial_hds[2]); | ||||
|     pl011_init(0x10009000, sic, 6, serial_hds[3]); | ||||
| 
 | ||||
|     pl080_init(0x10130000, pic, 17); | ||||
|     sp804_init(0x101e2000, pic, 4); | ||||
|     sp804_init(0x101e3000, pic, 5); | ||||
| 
 | ||||
|     /* The versatile/PB actually has a modified Color LCD controller
 | ||||
|        that includes hardware cursor support from the PL111.  */ | ||||
|     pl110_init(ds, 0x10120000, pic, 16, 1); | ||||
| 
 | ||||
|     /* 0x10000000 System registers.  */ | ||||
|     /* 0x10001000 PCI controller config registers.  */ | ||||
|     /* 0x10002000 Serial bus interface.  */ | ||||
|     /*  0x10003000 Secondary interrupt controller.  */ | ||||
|     /* 0x10004000 AACI (audio).  */ | ||||
|     /* 0x10005000 MMCI0.  */ | ||||
|     /*  0x10006000 KMI0 (keyboard).  */ | ||||
|     /*  0x10007000 KMI1 (mouse).  */ | ||||
|     /* 0x10008000 Character LCD Interface.  */ | ||||
|     /*  0x10009000 UART3.  */ | ||||
|     /* 0x1000a000 Smart card 1.  */ | ||||
|     /* 0x1000b000 MMCI1.  */ | ||||
|     /*  0x10010000 Ethernet.  */ | ||||
|     /* 0x10020000 USB.  */ | ||||
|     /* 0x10100000 SSMC.  */ | ||||
|     /* 0x10110000 MPMC.  */ | ||||
|     /*  0x10120000 CLCD Controller.  */ | ||||
|     /*  0x10130000 DMA Controller.  */ | ||||
|     /*  0x10140000 Vectored interrupt controller.  */ | ||||
|     /* 0x101d0000 AHB Monitor Interface.  */ | ||||
|     /* 0x101e0000 System Controller.  */ | ||||
|     /* 0x101e1000 Watchdog Interface.  */ | ||||
|     /* 0x101e2000 Timer 0/1.  */ | ||||
|     /* 0x101e3000 Timer 2/3.  */ | ||||
|     /* 0x101e4000 GPIO port 0.  */ | ||||
|     /* 0x101e5000 GPIO port 1.  */ | ||||
|     /* 0x101e6000 GPIO port 2.  */ | ||||
|     /* 0x101e7000 GPIO port 3.  */ | ||||
|     /* 0x101e8000 RTC.  */ | ||||
|     /* 0x101f0000 Smart card 0.  */ | ||||
|     /*  0x101f1000 UART0.  */ | ||||
|     /*  0x101f2000 UART1.  */ | ||||
|     /*  0x101f3000 UART2.  */ | ||||
|     /* 0x101f4000 SSPI.  */ | ||||
| 
 | ||||
|     /* Load the kernel.  */ | ||||
|     if (!kernel_filename) { | ||||
|         fprintf(stderr, "Kernel image must be specified\n"); | ||||
|         exit(1); | ||||
|     } | ||||
|     kernel_size = load_image(kernel_filename, | ||||
|                              phys_ram_base + KERNEL_LOAD_ADDR); | ||||
|     if (kernel_size < 0) { | ||||
|         fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); | ||||
|         exit(1); | ||||
|     } | ||||
|     if (initrd_filename) { | ||||
|         initrd_size = load_image(initrd_filename, | ||||
|                                  phys_ram_base + INITRD_LOAD_ADDR); | ||||
|         if (initrd_size < 0) { | ||||
|             fprintf(stderr, "qemu: could not load initrd '%s'\n", | ||||
|                     initrd_filename); | ||||
|             exit(1); | ||||
|         } | ||||
|     } else { | ||||
|         initrd_size = 0; | ||||
|     } | ||||
|     bootloader[5] = KERNEL_ARGS_ADDR; | ||||
|     bootloader[6] = KERNEL_LOAD_ADDR; | ||||
|     for (n = 0; n < sizeof(bootloader) / 4; n++) | ||||
|         stl_raw(phys_ram_base + (n * 4), bootloader[n]); | ||||
|     set_kernel_args(ram_size, initrd_size, kernel_cmdline); | ||||
| } | ||||
| 
 | ||||
| QEMUMachine versatilepb_machine = { | ||||
|     "versatilepb", | ||||
|     "ARM Versatile/PB (ARM926EJ-S)", | ||||
|     vpb_init, | ||||
| }; | ||||
							
								
								
									
										1
									
								
								vl.c
								
								
								
								
							
							
						
						
									
										1
									
								
								vl.c
								
								
								
								
							|  | @ -4436,6 +4436,7 @@ void register_machines(void) | |||
| #elif defined(TARGET_ARM) | ||||
|     qemu_register_machine(&integratorcp926_machine); | ||||
|     qemu_register_machine(&integratorcp1026_machine); | ||||
|     qemu_register_machine(&versatilepb_machine); | ||||
| #else | ||||
| #error unsupported CPU | ||||
| #endif | ||||
|  |  | |||
							
								
								
									
										19
									
								
								vl.h
								
								
								
								
							
							
						
						
									
										19
									
								
								vl.h
								
								
								
								
							|  | @ -966,6 +966,9 @@ void usb_info(void); | |||
| extern QEMUMachine integratorcp926_machine; | ||||
| extern QEMUMachine integratorcp1026_machine; | ||||
| 
 | ||||
| /* versatilepb.c */ | ||||
| extern QEMUMachine versatilepb_machine; | ||||
| 
 | ||||
| /* ps2.c */ | ||||
| void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg); | ||||
| void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); | ||||
|  | @ -981,6 +984,22 @@ void smc91c111_init(NICInfo *, uint32_t, void *, int); | |||
| /* pl110.c */ | ||||
| void *pl110_init(DisplayState *ds, uint32_t base, void *pic, int irq, int); | ||||
| 
 | ||||
| /* pl011.c */ | ||||
| void pl011_init(uint32_t base, void *pic, int irq, CharDriverState *chr); | ||||
| 
 | ||||
| /* pl050.c */ | ||||
| void pl050_init(uint32_t base, void *pic, int irq, int is_mouse); | ||||
| 
 | ||||
| /* pl080.c */ | ||||
| void *pl080_init(uint32_t base, void *pic, int irq); | ||||
| 
 | ||||
| /* pl190.c */ | ||||
| void *pl190_init(uint32_t base, void *parent, int irq, int fiq); | ||||
| 
 | ||||
| /* arm-timer.c */ | ||||
| void sp804_init(uint32_t base, void *pic, int irq); | ||||
| void icp_pit_init(uint32_t base, void *pic, int irq); | ||||
| 
 | ||||
| #endif /* defined(QEMU_TOOL) */ | ||||
| 
 | ||||
| /* monitor.c */ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 pbrook
						pbrook