symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/slavio_intctl.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * QEMU Sparc SLAVIO interrupt controller emulation
       
     3  *
       
     4  * Copyright (c) 2003-2005 Fabrice Bellard
       
     5  *
       
     6  * Permission is hereby granted, free of charge, to any person obtaining a copy
       
     7  * of this software and associated documentation files (the "Software"), to deal
       
     8  * in the Software without restriction, including without limitation the rights
       
     9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    10  * copies of the Software, and to permit persons to whom the Software is
       
    11  * furnished to do so, subject to the following conditions:
       
    12  *
       
    13  * The above copyright notice and this permission notice shall be included in
       
    14  * all copies or substantial portions of the Software.
       
    15  *
       
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
       
    19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    22  * THE SOFTWARE.
       
    23  */
       
    24 #include "hw.h"
       
    25 #include "sun4m.h"
       
    26 #include "console.h"
       
    27 
       
    28 //#define DEBUG_IRQ_COUNT
       
    29 //#define DEBUG_IRQ
       
    30 
       
    31 #ifdef DEBUG_IRQ
       
    32 #define DPRINTF(fmt, args...) \
       
    33 do { printf("IRQ: " fmt , ##args); } while (0)
       
    34 #else
       
    35 #define DPRINTF(fmt, args...)
       
    36 #endif
       
    37 
       
    38 /*
       
    39  * Registers of interrupt controller in sun4m.
       
    40  *
       
    41  * This is the interrupt controller part of chip STP2001 (Slave I/O), also
       
    42  * produced as NCR89C105. See
       
    43  * http://www.ibiblio.org/pub/historic-linux/early-ports/Sparc/NCR/NCR89C105.txt
       
    44  *
       
    45  * There is a system master controller and one for each cpu.
       
    46  *
       
    47  */
       
    48 
       
    49 #define MAX_CPUS 16
       
    50 #define MAX_PILS 16
       
    51 
       
    52 struct SLAVIO_CPUINTCTLState;
       
    53 
       
    54 typedef struct SLAVIO_INTCTLState {
       
    55     uint32_t intregm_pending;
       
    56     uint32_t intregm_disabled;
       
    57     uint32_t target_cpu;
       
    58 #ifdef DEBUG_IRQ_COUNT
       
    59     uint64_t irq_count[32];
       
    60 #endif
       
    61     qemu_irq *cpu_irqs[MAX_CPUS];
       
    62     const uint32_t *intbit_to_level;
       
    63     uint32_t cputimer_lbit, cputimer_mbit;
       
    64     uint32_t pil_out[MAX_CPUS];
       
    65     struct SLAVIO_CPUINTCTLState *slaves[MAX_CPUS];
       
    66 } SLAVIO_INTCTLState;
       
    67 
       
    68 typedef struct SLAVIO_CPUINTCTLState {
       
    69     uint32_t intreg_pending;
       
    70     SLAVIO_INTCTLState *master;
       
    71     uint32_t cpu;
       
    72 } SLAVIO_CPUINTCTLState;
       
    73 
       
    74 #define INTCTL_MAXADDR 0xf
       
    75 #define INTCTL_SIZE (INTCTL_MAXADDR + 1)
       
    76 #define INTCTLM_SIZE 0x14
       
    77 #define MASTER_IRQ_MASK ~0x0fa2007f
       
    78 #define MASTER_DISABLE 0x80000000
       
    79 #define CPU_SOFTIRQ_MASK 0xfffe0000
       
    80 #define CPU_HARDIRQ_MASK 0x0000fffe
       
    81 #define CPU_IRQ_INT15_IN 0x0004000
       
    82 #define CPU_IRQ_INT15_MASK 0x80000000
       
    83 
       
    84 static void slavio_check_interrupts(SLAVIO_INTCTLState *s);
       
    85 
       
    86 // per-cpu interrupt controller
       
    87 static uint32_t slavio_intctl_mem_readl(void *opaque, target_phys_addr_t addr)
       
    88 {
       
    89     SLAVIO_CPUINTCTLState *s = opaque;
       
    90     uint32_t saddr, ret;
       
    91 
       
    92     saddr = addr >> 2;
       
    93     switch (saddr) {
       
    94     case 0:
       
    95         ret = s->intreg_pending;
       
    96         break;
       
    97     default:
       
    98         ret = 0;
       
    99         break;
       
   100     }
       
   101     DPRINTF("read cpu %d reg 0x" TARGET_FMT_plx " = %x\n", cpu, addr, ret);
       
   102 
       
   103     return ret;
       
   104 }
       
   105 
       
   106 static void slavio_intctl_mem_writel(void *opaque, target_phys_addr_t addr,
       
   107                                      uint32_t val)
       
   108 {
       
   109     SLAVIO_CPUINTCTLState *s = opaque;
       
   110     uint32_t saddr;
       
   111 
       
   112     saddr = addr >> 2;
       
   113     DPRINTF("write cpu %d reg 0x" TARGET_FMT_plx " = %x\n", cpu, addr, val);
       
   114     switch (saddr) {
       
   115     case 1: // clear pending softints
       
   116         if (val & CPU_IRQ_INT15_IN)
       
   117             val |= CPU_IRQ_INT15_MASK;
       
   118         val &= CPU_SOFTIRQ_MASK;
       
   119         s->intreg_pending &= ~val;
       
   120         slavio_check_interrupts(s->master);
       
   121         DPRINTF("Cleared cpu %d irq mask %x, curmask %x\n", s->cpu, val,
       
   122                 s->intreg_pending);
       
   123         break;
       
   124     case 2: // set softint
       
   125         val &= CPU_SOFTIRQ_MASK;
       
   126         s->intreg_pending |= val;
       
   127         slavio_check_interrupts(s->master);
       
   128         DPRINTF("Set cpu %d irq mask %x, curmask %x\n", s->cpu, val,
       
   129                 s->intreg_pending);
       
   130         break;
       
   131     default:
       
   132         break;
       
   133     }
       
   134 }
       
   135 
       
   136 static CPUReadMemoryFunc *slavio_intctl_mem_read[3] = {
       
   137     NULL,
       
   138     NULL,
       
   139     slavio_intctl_mem_readl,
       
   140 };
       
   141 
       
   142 static CPUWriteMemoryFunc *slavio_intctl_mem_write[3] = {
       
   143     NULL,
       
   144     NULL,
       
   145     slavio_intctl_mem_writel,
       
   146 };
       
   147 
       
   148 // master system interrupt controller
       
   149 static uint32_t slavio_intctlm_mem_readl(void *opaque, target_phys_addr_t addr)
       
   150 {
       
   151     SLAVIO_INTCTLState *s = opaque;
       
   152     uint32_t saddr, ret;
       
   153 
       
   154     saddr = addr >> 2;
       
   155     switch (saddr) {
       
   156     case 0:
       
   157         ret = s->intregm_pending & ~MASTER_DISABLE;
       
   158         break;
       
   159     case 1:
       
   160         ret = s->intregm_disabled & MASTER_IRQ_MASK;
       
   161         break;
       
   162     case 4:
       
   163         ret = s->target_cpu;
       
   164         break;
       
   165     default:
       
   166         ret = 0;
       
   167         break;
       
   168     }
       
   169     DPRINTF("read system reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
       
   170 
       
   171     return ret;
       
   172 }
       
   173 
       
   174 static void slavio_intctlm_mem_writel(void *opaque, target_phys_addr_t addr,
       
   175                                       uint32_t val)
       
   176 {
       
   177     SLAVIO_INTCTLState *s = opaque;
       
   178     uint32_t saddr;
       
   179 
       
   180     saddr = addr >> 2;
       
   181     DPRINTF("write system reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
       
   182     switch (saddr) {
       
   183     case 2: // clear (enable)
       
   184         // Force clear unused bits
       
   185         val &= MASTER_IRQ_MASK;
       
   186         s->intregm_disabled &= ~val;
       
   187         DPRINTF("Enabled master irq mask %x, curmask %x\n", val,
       
   188                 s->intregm_disabled);
       
   189         slavio_check_interrupts(s);
       
   190         break;
       
   191     case 3: // set (disable, clear pending)
       
   192         // Force clear unused bits
       
   193         val &= MASTER_IRQ_MASK;
       
   194         s->intregm_disabled |= val;
       
   195         s->intregm_pending &= ~val;
       
   196         slavio_check_interrupts(s);
       
   197         DPRINTF("Disabled master irq mask %x, curmask %x\n", val,
       
   198                 s->intregm_disabled);
       
   199         break;
       
   200     case 4:
       
   201         s->target_cpu = val & (MAX_CPUS - 1);
       
   202         slavio_check_interrupts(s);
       
   203         DPRINTF("Set master irq cpu %d\n", s->target_cpu);
       
   204         break;
       
   205     default:
       
   206         break;
       
   207     }
       
   208 }
       
   209 
       
   210 static CPUReadMemoryFunc *slavio_intctlm_mem_read[3] = {
       
   211     NULL,
       
   212     NULL,
       
   213     slavio_intctlm_mem_readl,
       
   214 };
       
   215 
       
   216 static CPUWriteMemoryFunc *slavio_intctlm_mem_write[3] = {
       
   217     NULL,
       
   218     NULL,
       
   219     slavio_intctlm_mem_writel,
       
   220 };
       
   221 
       
   222 void slavio_pic_info(void *opaque)
       
   223 {
       
   224     SLAVIO_INTCTLState *s = opaque;
       
   225     int i;
       
   226 
       
   227     for (i = 0; i < MAX_CPUS; i++) {
       
   228         term_printf("per-cpu %d: pending 0x%08x\n", i,
       
   229                     s->slaves[i]->intreg_pending);
       
   230     }
       
   231     term_printf("master: pending 0x%08x, disabled 0x%08x\n",
       
   232                 s->intregm_pending, s->intregm_disabled);
       
   233 }
       
   234 
       
   235 void slavio_irq_info(void *opaque)
       
   236 {
       
   237 #ifndef DEBUG_IRQ_COUNT
       
   238     term_printf("irq statistic code not compiled.\n");
       
   239 #else
       
   240     SLAVIO_INTCTLState *s = opaque;
       
   241     int i;
       
   242     int64_t count;
       
   243 
       
   244     term_printf("IRQ statistics:\n");
       
   245     for (i = 0; i < 32; i++) {
       
   246         count = s->irq_count[i];
       
   247         if (count > 0)
       
   248             term_printf("%2d: %" PRId64 "\n", i, count);
       
   249     }
       
   250 #endif
       
   251 }
       
   252 
       
   253 static void slavio_check_interrupts(SLAVIO_INTCTLState *s)
       
   254 {
       
   255     uint32_t pending = s->intregm_pending, pil_pending;
       
   256     unsigned int i, j;
       
   257 
       
   258     pending &= ~s->intregm_disabled;
       
   259 
       
   260     DPRINTF("pending %x disabled %x\n", pending, s->intregm_disabled);
       
   261     for (i = 0; i < MAX_CPUS; i++) {
       
   262         pil_pending = 0;
       
   263         if (pending && !(s->intregm_disabled & MASTER_DISABLE) &&
       
   264             (i == s->target_cpu)) {
       
   265             for (j = 0; j < 32; j++) {
       
   266                 if (pending & (1 << j))
       
   267                     pil_pending |= 1 << s->intbit_to_level[j];
       
   268             }
       
   269         }
       
   270         pil_pending |= (s->slaves[i]->intreg_pending & CPU_SOFTIRQ_MASK) >> 16;
       
   271 
       
   272         for (j = 0; j < MAX_PILS; j++) {
       
   273             if (pil_pending & (1 << j)) {
       
   274                 if (!(s->pil_out[i] & (1 << j)))
       
   275                     qemu_irq_raise(s->cpu_irqs[i][j]);
       
   276             } else {
       
   277                 if (s->pil_out[i] & (1 << j))
       
   278                     qemu_irq_lower(s->cpu_irqs[i][j]);
       
   279             }
       
   280         }
       
   281         s->pil_out[i] = pil_pending;
       
   282     }
       
   283 }
       
   284 
       
   285 /*
       
   286  * "irq" here is the bit number in the system interrupt register to
       
   287  * separate serial and keyboard interrupts sharing a level.
       
   288  */
       
   289 static void slavio_set_irq(void *opaque, int irq, int level)
       
   290 {
       
   291     SLAVIO_INTCTLState *s = opaque;
       
   292     uint32_t mask = 1 << irq;
       
   293     uint32_t pil = s->intbit_to_level[irq];
       
   294 
       
   295     DPRINTF("Set cpu %d irq %d -> pil %d level %d\n", s->target_cpu, irq, pil,
       
   296             level);
       
   297     if (pil > 0) {
       
   298         if (level) {
       
   299 #ifdef DEBUG_IRQ_COUNT
       
   300             s->irq_count[pil]++;
       
   301 #endif
       
   302             s->intregm_pending |= mask;
       
   303             s->slaves[s->target_cpu]->intreg_pending |= 1 << pil;
       
   304         } else {
       
   305             s->intregm_pending &= ~mask;
       
   306             s->slaves[s->target_cpu]->intreg_pending &= ~(1 << pil);
       
   307         }
       
   308         slavio_check_interrupts(s);
       
   309     }
       
   310 }
       
   311 
       
   312 static void slavio_set_timer_irq_cpu(void *opaque, int cpu, int level)
       
   313 {
       
   314     SLAVIO_INTCTLState *s = opaque;
       
   315 
       
   316     DPRINTF("Set cpu %d local timer level %d\n", cpu, level);
       
   317 
       
   318     if (level) {
       
   319         s->intregm_pending |= s->cputimer_mbit;
       
   320         s->slaves[cpu]->intreg_pending |= s->cputimer_lbit;
       
   321     } else {
       
   322         s->intregm_pending &= ~s->cputimer_mbit;
       
   323         s->slaves[cpu]->intreg_pending &= ~s->cputimer_lbit;
       
   324     }
       
   325 
       
   326     slavio_check_interrupts(s);
       
   327 }
       
   328 
       
   329 static void slavio_intctl_save(QEMUFile *f, void *opaque)
       
   330 {
       
   331     SLAVIO_INTCTLState *s = opaque;
       
   332     int i;
       
   333 
       
   334     for (i = 0; i < MAX_CPUS; i++) {
       
   335         qemu_put_be32s(f, &s->slaves[i]->intreg_pending);
       
   336     }
       
   337     qemu_put_be32s(f, &s->intregm_pending);
       
   338     qemu_put_be32s(f, &s->intregm_disabled);
       
   339     qemu_put_be32s(f, &s->target_cpu);
       
   340 }
       
   341 
       
   342 static int slavio_intctl_load(QEMUFile *f, void *opaque, int version_id)
       
   343 {
       
   344     SLAVIO_INTCTLState *s = opaque;
       
   345     int i;
       
   346 
       
   347     if (version_id != 1)
       
   348         return -EINVAL;
       
   349 
       
   350     for (i = 0; i < MAX_CPUS; i++) {
       
   351         qemu_get_be32s(f, &s->slaves[i]->intreg_pending);
       
   352     }
       
   353     qemu_get_be32s(f, &s->intregm_pending);
       
   354     qemu_get_be32s(f, &s->intregm_disabled);
       
   355     qemu_get_be32s(f, &s->target_cpu);
       
   356     slavio_check_interrupts(s);
       
   357     return 0;
       
   358 }
       
   359 
       
   360 static void slavio_intctl_reset(void *opaque)
       
   361 {
       
   362     SLAVIO_INTCTLState *s = opaque;
       
   363     int i;
       
   364 
       
   365     for (i = 0; i < MAX_CPUS; i++) {
       
   366         s->slaves[i]->intreg_pending = 0;
       
   367     }
       
   368     s->intregm_disabled = ~MASTER_IRQ_MASK;
       
   369     s->intregm_pending = 0;
       
   370     s->target_cpu = 0;
       
   371     slavio_check_interrupts(s);
       
   372 }
       
   373 
       
   374 void *slavio_intctl_init(target_phys_addr_t addr, target_phys_addr_t addrg,
       
   375                          const uint32_t *intbit_to_level,
       
   376                          qemu_irq **irq, qemu_irq **cpu_irq,
       
   377                          qemu_irq **parent_irq, unsigned int cputimer)
       
   378 {
       
   379     int slavio_intctl_io_memory, slavio_intctlm_io_memory, i;
       
   380     SLAVIO_INTCTLState *s;
       
   381     SLAVIO_CPUINTCTLState *slave;
       
   382 
       
   383     s = qemu_mallocz(sizeof(SLAVIO_INTCTLState));
       
   384     if (!s)
       
   385         return NULL;
       
   386 
       
   387     s->intbit_to_level = intbit_to_level;
       
   388     for (i = 0; i < MAX_CPUS; i++) {
       
   389         slave = qemu_mallocz(sizeof(SLAVIO_CPUINTCTLState));
       
   390         if (!slave)
       
   391             return NULL;
       
   392 
       
   393         slave->cpu = i;
       
   394         slave->master = s;
       
   395 
       
   396         slavio_intctl_io_memory = cpu_register_io_memory(0,
       
   397                                                          slavio_intctl_mem_read,
       
   398                                                          slavio_intctl_mem_write,
       
   399                                                          slave);
       
   400         cpu_register_physical_memory(addr + i * TARGET_PAGE_SIZE, INTCTL_SIZE,
       
   401                                      slavio_intctl_io_memory);
       
   402 
       
   403         s->slaves[i] = slave;
       
   404         s->cpu_irqs[i] = parent_irq[i];
       
   405     }
       
   406 
       
   407     slavio_intctlm_io_memory = cpu_register_io_memory(0,
       
   408                                                       slavio_intctlm_mem_read,
       
   409                                                       slavio_intctlm_mem_write,
       
   410                                                       s);
       
   411     cpu_register_physical_memory(addrg, INTCTLM_SIZE, slavio_intctlm_io_memory);
       
   412 
       
   413     register_savevm("slavio_intctl", addr, 1, slavio_intctl_save,
       
   414                     slavio_intctl_load, s);
       
   415     qemu_register_reset(slavio_intctl_reset, s);
       
   416     *irq = qemu_allocate_irqs(slavio_set_irq, s, 32);
       
   417 
       
   418     *cpu_irq = qemu_allocate_irqs(slavio_set_timer_irq_cpu, s, MAX_CPUS);
       
   419     s->cputimer_mbit = 1 << cputimer;
       
   420     s->cputimer_lbit = 1 << intbit_to_level[cputimer];
       
   421     slavio_intctl_reset(s);
       
   422     return s;
       
   423 }