1008ff9d7Sj_mayer /* 2008ff9d7Sj_mayer * QEMU PowerPC 4xx embedded processors shared devices emulation 3008ff9d7Sj_mayer * 4008ff9d7Sj_mayer * Copyright (c) 2007 Jocelyn Mayer 5008ff9d7Sj_mayer * 6008ff9d7Sj_mayer * Permission is hereby granted, free of charge, to any person obtaining a copy 7008ff9d7Sj_mayer * of this software and associated documentation files (the "Software"), to deal 8008ff9d7Sj_mayer * in the Software without restriction, including without limitation the rights 9008ff9d7Sj_mayer * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10008ff9d7Sj_mayer * copies of the Software, and to permit persons to whom the Software is 11008ff9d7Sj_mayer * furnished to do so, subject to the following conditions: 12008ff9d7Sj_mayer * 13008ff9d7Sj_mayer * The above copyright notice and this permission notice shall be included in 14008ff9d7Sj_mayer * all copies or substantial portions of the Software. 15008ff9d7Sj_mayer * 16008ff9d7Sj_mayer * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17008ff9d7Sj_mayer * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18008ff9d7Sj_mayer * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19008ff9d7Sj_mayer * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20008ff9d7Sj_mayer * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21008ff9d7Sj_mayer * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22008ff9d7Sj_mayer * THE SOFTWARE. 23008ff9d7Sj_mayer */ 2483c9f4caSPaolo Bonzini #include "hw/hw.h" 250d09e41aSPaolo Bonzini #include "hw/ppc/ppc.h" 260d09e41aSPaolo Bonzini #include "hw/ppc/ppc4xx.h" 271de7afc9SPaolo Bonzini #include "qemu/log.h" 28022c62cbSPaolo Bonzini #include "exec/address-spaces.h" 29008ff9d7Sj_mayer 30008ff9d7Sj_mayer #define DEBUG_UIC 31008ff9d7Sj_mayer 32d12d51d5Saliguori 33d12d51d5Saliguori #ifdef DEBUG_UIC 3493fcfe39Saliguori # define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) 35d12d51d5Saliguori #else 36d12d51d5Saliguori # define LOG_UIC(...) do { } while (0) 37d12d51d5Saliguori #endif 38d12d51d5Saliguori 391bba0dc9SAndreas Färber static void ppc4xx_reset(void *opaque) 401bba0dc9SAndreas Färber { 4190cb09d9SAndreas Färber PowerPCCPU *cpu = opaque; 421bba0dc9SAndreas Färber 4390cb09d9SAndreas Färber cpu_reset(CPU(cpu)); 441bba0dc9SAndreas Färber } 451bba0dc9SAndreas Färber 46008ff9d7Sj_mayer /*****************************************************************************/ 4760b14d95SStefan Weil /* Generic PowerPC 4xx processor instantiation */ 482f9859fbSAndreas Färber PowerPCCPU *ppc4xx_init(const char *cpu_model, 49c227f099SAnthony Liguori clk_setup_t *cpu_clk, clk_setup_t *tb_clk, 50008ff9d7Sj_mayer uint32_t sysclk) 51008ff9d7Sj_mayer { 5257274713SAndreas Färber PowerPCCPU *cpu; 53e2684c0bSAndreas Färber CPUPPCState *env; 54008ff9d7Sj_mayer 55008ff9d7Sj_mayer /* init CPUs */ 5657274713SAndreas Färber cpu = cpu_ppc_init(cpu_model); 5757274713SAndreas Färber if (cpu == NULL) { 58aaed909aSbellard fprintf(stderr, "Unable to find PowerPC %s CPU definition\n", 59008ff9d7Sj_mayer cpu_model); 60aaed909aSbellard exit(1); 61008ff9d7Sj_mayer } 6257274713SAndreas Färber env = &cpu->env; 6357274713SAndreas Färber 64008ff9d7Sj_mayer cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ 65008ff9d7Sj_mayer cpu_clk->opaque = env; 66008ff9d7Sj_mayer /* Set time-base frequency to sysclk */ 67ddd1055bSFabien Chouteau tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT); 68008ff9d7Sj_mayer tb_clk->opaque = env; 69008ff9d7Sj_mayer ppc_dcr_init(env, NULL, NULL); 70008ff9d7Sj_mayer /* Register qemu callbacks */ 7190cb09d9SAndreas Färber qemu_register_reset(ppc4xx_reset, cpu); 72008ff9d7Sj_mayer 732f9859fbSAndreas Färber return cpu; 74008ff9d7Sj_mayer } 75008ff9d7Sj_mayer 76008ff9d7Sj_mayer /*****************************************************************************/ 77008ff9d7Sj_mayer /* "Universal" Interrupt controller */ 78008ff9d7Sj_mayer enum { 79008ff9d7Sj_mayer DCR_UICSR = 0x000, 80008ff9d7Sj_mayer DCR_UICSRS = 0x001, 81008ff9d7Sj_mayer DCR_UICER = 0x002, 82008ff9d7Sj_mayer DCR_UICCR = 0x003, 83008ff9d7Sj_mayer DCR_UICPR = 0x004, 84008ff9d7Sj_mayer DCR_UICTR = 0x005, 85008ff9d7Sj_mayer DCR_UICMSR = 0x006, 86008ff9d7Sj_mayer DCR_UICVR = 0x007, 87008ff9d7Sj_mayer DCR_UICVCR = 0x008, 88008ff9d7Sj_mayer DCR_UICMAX = 0x009, 89008ff9d7Sj_mayer }; 90008ff9d7Sj_mayer 91008ff9d7Sj_mayer #define UIC_MAX_IRQ 32 92c227f099SAnthony Liguori typedef struct ppcuic_t ppcuic_t; 93c227f099SAnthony Liguori struct ppcuic_t { 94008ff9d7Sj_mayer uint32_t dcr_base; 95008ff9d7Sj_mayer int use_vectors; 964c54e875Saurel32 uint32_t level; /* Remembers the state of level-triggered interrupts. */ 97008ff9d7Sj_mayer uint32_t uicsr; /* Status register */ 98008ff9d7Sj_mayer uint32_t uicer; /* Enable register */ 99008ff9d7Sj_mayer uint32_t uiccr; /* Critical register */ 100008ff9d7Sj_mayer uint32_t uicpr; /* Polarity register */ 101008ff9d7Sj_mayer uint32_t uictr; /* Triggering register */ 102008ff9d7Sj_mayer uint32_t uicvcr; /* Vector configuration register */ 103008ff9d7Sj_mayer uint32_t uicvr; 104008ff9d7Sj_mayer qemu_irq *irqs; 105008ff9d7Sj_mayer }; 106008ff9d7Sj_mayer 107c227f099SAnthony Liguori static void ppcuic_trigger_irq (ppcuic_t *uic) 108008ff9d7Sj_mayer { 109008ff9d7Sj_mayer uint32_t ir, cr; 110008ff9d7Sj_mayer int start, end, inc, i; 111008ff9d7Sj_mayer 112008ff9d7Sj_mayer /* Trigger interrupt if any is pending */ 113008ff9d7Sj_mayer ir = uic->uicsr & uic->uicer & (~uic->uiccr); 114008ff9d7Sj_mayer cr = uic->uicsr & uic->uicer & uic->uiccr; 115d12d51d5Saliguori LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32 116aae9366aSj_mayer " uiccr %08" PRIx32 "\n" 117aae9366aSj_mayer " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", 118aae9366aSj_mayer __func__, uic->uicsr, uic->uicer, uic->uiccr, 119008ff9d7Sj_mayer uic->uicsr & uic->uicer, ir, cr); 120008ff9d7Sj_mayer if (ir != 0x0000000) { 121d12d51d5Saliguori LOG_UIC("Raise UIC interrupt\n"); 122008ff9d7Sj_mayer qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]); 123008ff9d7Sj_mayer } else { 124d12d51d5Saliguori LOG_UIC("Lower UIC interrupt\n"); 125008ff9d7Sj_mayer qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]); 126008ff9d7Sj_mayer } 127008ff9d7Sj_mayer /* Trigger critical interrupt if any is pending and update vector */ 128008ff9d7Sj_mayer if (cr != 0x0000000) { 129008ff9d7Sj_mayer qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]); 130008ff9d7Sj_mayer if (uic->use_vectors) { 131008ff9d7Sj_mayer /* Compute critical IRQ vector */ 132008ff9d7Sj_mayer if (uic->uicvcr & 1) { 133008ff9d7Sj_mayer start = 31; 134008ff9d7Sj_mayer end = 0; 135008ff9d7Sj_mayer inc = -1; 136008ff9d7Sj_mayer } else { 137008ff9d7Sj_mayer start = 0; 138008ff9d7Sj_mayer end = 31; 139008ff9d7Sj_mayer inc = 1; 140008ff9d7Sj_mayer } 141008ff9d7Sj_mayer uic->uicvr = uic->uicvcr & 0xFFFFFFFC; 142008ff9d7Sj_mayer for (i = start; i <= end; i += inc) { 143008ff9d7Sj_mayer if (cr & (1 << i)) { 144008ff9d7Sj_mayer uic->uicvr += (i - start) * 512 * inc; 145008ff9d7Sj_mayer break; 146008ff9d7Sj_mayer } 147008ff9d7Sj_mayer } 148008ff9d7Sj_mayer } 149d12d51d5Saliguori LOG_UIC("Raise UIC critical interrupt - " 150aae9366aSj_mayer "vector %08" PRIx32 "\n", uic->uicvr); 151008ff9d7Sj_mayer } else { 152d12d51d5Saliguori LOG_UIC("Lower UIC critical interrupt\n"); 153008ff9d7Sj_mayer qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]); 154008ff9d7Sj_mayer uic->uicvr = 0x00000000; 155008ff9d7Sj_mayer } 156008ff9d7Sj_mayer } 157008ff9d7Sj_mayer 158008ff9d7Sj_mayer static void ppcuic_set_irq (void *opaque, int irq_num, int level) 159008ff9d7Sj_mayer { 160c227f099SAnthony Liguori ppcuic_t *uic; 161008ff9d7Sj_mayer uint32_t mask, sr; 162008ff9d7Sj_mayer 163008ff9d7Sj_mayer uic = opaque; 164*a1f7f97bSPeter Maydell mask = 1U << (31-irq_num); 165d12d51d5Saliguori LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 166aae9366aSj_mayer " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", 167aae9366aSj_mayer __func__, irq_num, level, 168008ff9d7Sj_mayer uic->uicsr, mask, uic->uicsr & mask, level << irq_num); 169008ff9d7Sj_mayer if (irq_num < 0 || irq_num > 31) 170008ff9d7Sj_mayer return; 171008ff9d7Sj_mayer sr = uic->uicsr; 17250bf72b3Saurel32 173008ff9d7Sj_mayer /* Update status register */ 174008ff9d7Sj_mayer if (uic->uictr & mask) { 175008ff9d7Sj_mayer /* Edge sensitive interrupt */ 176008ff9d7Sj_mayer if (level == 1) 177008ff9d7Sj_mayer uic->uicsr |= mask; 178008ff9d7Sj_mayer } else { 179008ff9d7Sj_mayer /* Level sensitive interrupt */ 1804c54e875Saurel32 if (level == 1) { 181008ff9d7Sj_mayer uic->uicsr |= mask; 1824c54e875Saurel32 uic->level |= mask; 1834c54e875Saurel32 } else { 184008ff9d7Sj_mayer uic->uicsr &= ~mask; 1854c54e875Saurel32 uic->level &= ~mask; 1864c54e875Saurel32 } 187008ff9d7Sj_mayer } 188d12d51d5Saliguori LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => " 189aae9366aSj_mayer "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); 190008ff9d7Sj_mayer if (sr != uic->uicsr) 191008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 192008ff9d7Sj_mayer } 193008ff9d7Sj_mayer 19473b01960SAlexander Graf static uint32_t dcr_read_uic (void *opaque, int dcrn) 195008ff9d7Sj_mayer { 196c227f099SAnthony Liguori ppcuic_t *uic; 19773b01960SAlexander Graf uint32_t ret; 198008ff9d7Sj_mayer 199008ff9d7Sj_mayer uic = opaque; 200008ff9d7Sj_mayer dcrn -= uic->dcr_base; 201008ff9d7Sj_mayer switch (dcrn) { 202008ff9d7Sj_mayer case DCR_UICSR: 203008ff9d7Sj_mayer case DCR_UICSRS: 204008ff9d7Sj_mayer ret = uic->uicsr; 205008ff9d7Sj_mayer break; 206008ff9d7Sj_mayer case DCR_UICER: 207008ff9d7Sj_mayer ret = uic->uicer; 208008ff9d7Sj_mayer break; 209008ff9d7Sj_mayer case DCR_UICCR: 210008ff9d7Sj_mayer ret = uic->uiccr; 211008ff9d7Sj_mayer break; 212008ff9d7Sj_mayer case DCR_UICPR: 213008ff9d7Sj_mayer ret = uic->uicpr; 214008ff9d7Sj_mayer break; 215008ff9d7Sj_mayer case DCR_UICTR: 216008ff9d7Sj_mayer ret = uic->uictr; 217008ff9d7Sj_mayer break; 218008ff9d7Sj_mayer case DCR_UICMSR: 219008ff9d7Sj_mayer ret = uic->uicsr & uic->uicer; 220008ff9d7Sj_mayer break; 221008ff9d7Sj_mayer case DCR_UICVR: 222008ff9d7Sj_mayer if (!uic->use_vectors) 223008ff9d7Sj_mayer goto no_read; 224008ff9d7Sj_mayer ret = uic->uicvr; 225008ff9d7Sj_mayer break; 226008ff9d7Sj_mayer case DCR_UICVCR: 227008ff9d7Sj_mayer if (!uic->use_vectors) 228008ff9d7Sj_mayer goto no_read; 229008ff9d7Sj_mayer ret = uic->uicvcr; 230008ff9d7Sj_mayer break; 231008ff9d7Sj_mayer default: 232008ff9d7Sj_mayer no_read: 233008ff9d7Sj_mayer ret = 0x00000000; 234008ff9d7Sj_mayer break; 235008ff9d7Sj_mayer } 236008ff9d7Sj_mayer 237008ff9d7Sj_mayer return ret; 238008ff9d7Sj_mayer } 239008ff9d7Sj_mayer 24073b01960SAlexander Graf static void dcr_write_uic (void *opaque, int dcrn, uint32_t val) 241008ff9d7Sj_mayer { 242c227f099SAnthony Liguori ppcuic_t *uic; 243008ff9d7Sj_mayer 244008ff9d7Sj_mayer uic = opaque; 245008ff9d7Sj_mayer dcrn -= uic->dcr_base; 24673b01960SAlexander Graf LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); 247008ff9d7Sj_mayer switch (dcrn) { 248008ff9d7Sj_mayer case DCR_UICSR: 249008ff9d7Sj_mayer uic->uicsr &= ~val; 2504c54e875Saurel32 uic->uicsr |= uic->level; 251008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 252008ff9d7Sj_mayer break; 253008ff9d7Sj_mayer case DCR_UICSRS: 254008ff9d7Sj_mayer uic->uicsr |= val; 255008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 256008ff9d7Sj_mayer break; 257008ff9d7Sj_mayer case DCR_UICER: 258008ff9d7Sj_mayer uic->uicer = val; 259008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 260008ff9d7Sj_mayer break; 261008ff9d7Sj_mayer case DCR_UICCR: 262008ff9d7Sj_mayer uic->uiccr = val; 263008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 264008ff9d7Sj_mayer break; 265008ff9d7Sj_mayer case DCR_UICPR: 266008ff9d7Sj_mayer uic->uicpr = val; 267008ff9d7Sj_mayer break; 268008ff9d7Sj_mayer case DCR_UICTR: 269008ff9d7Sj_mayer uic->uictr = val; 270008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 271008ff9d7Sj_mayer break; 272008ff9d7Sj_mayer case DCR_UICMSR: 273008ff9d7Sj_mayer break; 274008ff9d7Sj_mayer case DCR_UICVR: 275008ff9d7Sj_mayer break; 276008ff9d7Sj_mayer case DCR_UICVCR: 277008ff9d7Sj_mayer uic->uicvcr = val & 0xFFFFFFFD; 278008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 279008ff9d7Sj_mayer break; 280008ff9d7Sj_mayer } 281008ff9d7Sj_mayer } 282008ff9d7Sj_mayer 283008ff9d7Sj_mayer static void ppcuic_reset (void *opaque) 284008ff9d7Sj_mayer { 285c227f099SAnthony Liguori ppcuic_t *uic; 286008ff9d7Sj_mayer 287008ff9d7Sj_mayer uic = opaque; 288008ff9d7Sj_mayer uic->uiccr = 0x00000000; 289008ff9d7Sj_mayer uic->uicer = 0x00000000; 290008ff9d7Sj_mayer uic->uicpr = 0x00000000; 291008ff9d7Sj_mayer uic->uicsr = 0x00000000; 292008ff9d7Sj_mayer uic->uictr = 0x00000000; 293008ff9d7Sj_mayer if (uic->use_vectors) { 294008ff9d7Sj_mayer uic->uicvcr = 0x00000000; 295008ff9d7Sj_mayer uic->uicvr = 0x0000000; 296008ff9d7Sj_mayer } 297008ff9d7Sj_mayer } 298008ff9d7Sj_mayer 299e2684c0bSAndreas Färber qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs, 300008ff9d7Sj_mayer uint32_t dcr_base, int has_ssr, int has_vr) 301008ff9d7Sj_mayer { 302c227f099SAnthony Liguori ppcuic_t *uic; 303008ff9d7Sj_mayer int i; 304008ff9d7Sj_mayer 3057267c094SAnthony Liguori uic = g_malloc0(sizeof(ppcuic_t)); 306008ff9d7Sj_mayer uic->dcr_base = dcr_base; 307008ff9d7Sj_mayer uic->irqs = irqs; 308008ff9d7Sj_mayer if (has_vr) 309008ff9d7Sj_mayer uic->use_vectors = 1; 310008ff9d7Sj_mayer for (i = 0; i < DCR_UICMAX; i++) { 311008ff9d7Sj_mayer ppc_dcr_register(env, dcr_base + i, uic, 312008ff9d7Sj_mayer &dcr_read_uic, &dcr_write_uic); 313008ff9d7Sj_mayer } 314a08d4367SJan Kiszka qemu_register_reset(ppcuic_reset, uic); 315008ff9d7Sj_mayer 316008ff9d7Sj_mayer return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ); 317008ff9d7Sj_mayer } 31861b24405Saurel32 31961b24405Saurel32 /*****************************************************************************/ 32061b24405Saurel32 /* SDRAM controller */ 321c227f099SAnthony Liguori typedef struct ppc4xx_sdram_t ppc4xx_sdram_t; 322c227f099SAnthony Liguori struct ppc4xx_sdram_t { 32361b24405Saurel32 uint32_t addr; 32461b24405Saurel32 int nbanks; 325b6dcbe08SAvi Kivity MemoryRegion containers[4]; /* used for clipping */ 326b6dcbe08SAvi Kivity MemoryRegion *ram_memories; 327a8170e5eSAvi Kivity hwaddr ram_bases[4]; 328a8170e5eSAvi Kivity hwaddr ram_sizes[4]; 32961b24405Saurel32 uint32_t besr0; 33061b24405Saurel32 uint32_t besr1; 33161b24405Saurel32 uint32_t bear; 33261b24405Saurel32 uint32_t cfg; 33361b24405Saurel32 uint32_t status; 33461b24405Saurel32 uint32_t rtr; 33561b24405Saurel32 uint32_t pmit; 33661b24405Saurel32 uint32_t bcr[4]; 33761b24405Saurel32 uint32_t tr; 33861b24405Saurel32 uint32_t ecccfg; 33961b24405Saurel32 uint32_t eccesr; 34061b24405Saurel32 qemu_irq irq; 34161b24405Saurel32 }; 34261b24405Saurel32 34361b24405Saurel32 enum { 34461b24405Saurel32 SDRAM0_CFGADDR = 0x010, 34561b24405Saurel32 SDRAM0_CFGDATA = 0x011, 34661b24405Saurel32 }; 34761b24405Saurel32 34861b24405Saurel32 /* XXX: TOFIX: some patches have made this code become inconsistent: 349a8170e5eSAvi Kivity * there are type inconsistencies, mixing hwaddr, target_ulong 35061b24405Saurel32 * and uint32_t 35161b24405Saurel32 */ 352a8170e5eSAvi Kivity static uint32_t sdram_bcr (hwaddr ram_base, 353a8170e5eSAvi Kivity hwaddr ram_size) 35461b24405Saurel32 { 35561b24405Saurel32 uint32_t bcr; 35661b24405Saurel32 35761b24405Saurel32 switch (ram_size) { 35861b24405Saurel32 case (4 * 1024 * 1024): 35961b24405Saurel32 bcr = 0x00000000; 36061b24405Saurel32 break; 36161b24405Saurel32 case (8 * 1024 * 1024): 36261b24405Saurel32 bcr = 0x00020000; 36361b24405Saurel32 break; 36461b24405Saurel32 case (16 * 1024 * 1024): 36561b24405Saurel32 bcr = 0x00040000; 36661b24405Saurel32 break; 36761b24405Saurel32 case (32 * 1024 * 1024): 36861b24405Saurel32 bcr = 0x00060000; 36961b24405Saurel32 break; 37061b24405Saurel32 case (64 * 1024 * 1024): 37161b24405Saurel32 bcr = 0x00080000; 37261b24405Saurel32 break; 37361b24405Saurel32 case (128 * 1024 * 1024): 37461b24405Saurel32 bcr = 0x000A0000; 37561b24405Saurel32 break; 37661b24405Saurel32 case (256 * 1024 * 1024): 37761b24405Saurel32 bcr = 0x000C0000; 37861b24405Saurel32 break; 37961b24405Saurel32 default: 38090e189ecSBlue Swirl printf("%s: invalid RAM size " TARGET_FMT_plx "\n", __func__, 38190e189ecSBlue Swirl ram_size); 38261b24405Saurel32 return 0x00000000; 38361b24405Saurel32 } 38461b24405Saurel32 bcr |= ram_base & 0xFF800000; 38561b24405Saurel32 bcr |= 1; 38661b24405Saurel32 38761b24405Saurel32 return bcr; 38861b24405Saurel32 } 38961b24405Saurel32 390a8170e5eSAvi Kivity static inline hwaddr sdram_base(uint32_t bcr) 39161b24405Saurel32 { 39261b24405Saurel32 return bcr & 0xFF800000; 39361b24405Saurel32 } 39461b24405Saurel32 39561b24405Saurel32 static target_ulong sdram_size (uint32_t bcr) 39661b24405Saurel32 { 39761b24405Saurel32 target_ulong size; 39861b24405Saurel32 int sh; 39961b24405Saurel32 40061b24405Saurel32 sh = (bcr >> 17) & 0x7; 40161b24405Saurel32 if (sh == 7) 40261b24405Saurel32 size = -1; 40361b24405Saurel32 else 40461b24405Saurel32 size = (4 * 1024 * 1024) << sh; 40561b24405Saurel32 40661b24405Saurel32 return size; 40761b24405Saurel32 } 40861b24405Saurel32 409b6dcbe08SAvi Kivity static void sdram_set_bcr(ppc4xx_sdram_t *sdram, 410b6dcbe08SAvi Kivity uint32_t *bcrp, uint32_t bcr, int enabled) 41161b24405Saurel32 { 412b6dcbe08SAvi Kivity unsigned n = bcrp - sdram->bcr; 413b6dcbe08SAvi Kivity 41461b24405Saurel32 if (*bcrp & 0x00000001) { 41561b24405Saurel32 /* Unmap RAM */ 41661b24405Saurel32 #ifdef DEBUG_SDRAM 41790e189ecSBlue Swirl printf("%s: unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", 41861b24405Saurel32 __func__, sdram_base(*bcrp), sdram_size(*bcrp)); 41961b24405Saurel32 #endif 420b6dcbe08SAvi Kivity memory_region_del_subregion(get_system_memory(), 421b6dcbe08SAvi Kivity &sdram->containers[n]); 422b6dcbe08SAvi Kivity memory_region_del_subregion(&sdram->containers[n], 423b6dcbe08SAvi Kivity &sdram->ram_memories[n]); 424b6dcbe08SAvi Kivity memory_region_destroy(&sdram->containers[n]); 42561b24405Saurel32 } 42661b24405Saurel32 *bcrp = bcr & 0xFFDEE001; 42761b24405Saurel32 if (enabled && (bcr & 0x00000001)) { 42861b24405Saurel32 #ifdef DEBUG_SDRAM 42990e189ecSBlue Swirl printf("%s: Map RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", 43061b24405Saurel32 __func__, sdram_base(bcr), sdram_size(bcr)); 43161b24405Saurel32 #endif 4322c9b15caSPaolo Bonzini memory_region_init(&sdram->containers[n], NULL, "sdram-containers", 433b6dcbe08SAvi Kivity sdram_size(bcr)); 434b6dcbe08SAvi Kivity memory_region_add_subregion(&sdram->containers[n], 0, 435b6dcbe08SAvi Kivity &sdram->ram_memories[n]); 436b6dcbe08SAvi Kivity memory_region_add_subregion(get_system_memory(), 437b6dcbe08SAvi Kivity sdram_base(bcr), 438b6dcbe08SAvi Kivity &sdram->containers[n]); 43961b24405Saurel32 } 44061b24405Saurel32 } 44161b24405Saurel32 442c227f099SAnthony Liguori static void sdram_map_bcr (ppc4xx_sdram_t *sdram) 44361b24405Saurel32 { 44461b24405Saurel32 int i; 44561b24405Saurel32 44661b24405Saurel32 for (i = 0; i < sdram->nbanks; i++) { 44761b24405Saurel32 if (sdram->ram_sizes[i] != 0) { 448b6dcbe08SAvi Kivity sdram_set_bcr(sdram, 449b6dcbe08SAvi Kivity &sdram->bcr[i], 45061b24405Saurel32 sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]), 45161b24405Saurel32 1); 45261b24405Saurel32 } else { 453b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[i], 0x00000000, 0); 45461b24405Saurel32 } 45561b24405Saurel32 } 45661b24405Saurel32 } 45761b24405Saurel32 458c227f099SAnthony Liguori static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram) 45961b24405Saurel32 { 46061b24405Saurel32 int i; 46161b24405Saurel32 46261b24405Saurel32 for (i = 0; i < sdram->nbanks; i++) { 46361b24405Saurel32 #ifdef DEBUG_SDRAM 46490e189ecSBlue Swirl printf("%s: Unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", 46561b24405Saurel32 __func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i])); 46661b24405Saurel32 #endif 467b6dcbe08SAvi Kivity memory_region_del_subregion(get_system_memory(), 468b6dcbe08SAvi Kivity &sdram->ram_memories[i]); 46961b24405Saurel32 } 47061b24405Saurel32 } 47161b24405Saurel32 47273b01960SAlexander Graf static uint32_t dcr_read_sdram (void *opaque, int dcrn) 47361b24405Saurel32 { 474c227f099SAnthony Liguori ppc4xx_sdram_t *sdram; 47573b01960SAlexander Graf uint32_t ret; 47661b24405Saurel32 47761b24405Saurel32 sdram = opaque; 47861b24405Saurel32 switch (dcrn) { 47961b24405Saurel32 case SDRAM0_CFGADDR: 48061b24405Saurel32 ret = sdram->addr; 48161b24405Saurel32 break; 48261b24405Saurel32 case SDRAM0_CFGDATA: 48361b24405Saurel32 switch (sdram->addr) { 48461b24405Saurel32 case 0x00: /* SDRAM_BESR0 */ 48561b24405Saurel32 ret = sdram->besr0; 48661b24405Saurel32 break; 48761b24405Saurel32 case 0x08: /* SDRAM_BESR1 */ 48861b24405Saurel32 ret = sdram->besr1; 48961b24405Saurel32 break; 49061b24405Saurel32 case 0x10: /* SDRAM_BEAR */ 49161b24405Saurel32 ret = sdram->bear; 49261b24405Saurel32 break; 49361b24405Saurel32 case 0x20: /* SDRAM_CFG */ 49461b24405Saurel32 ret = sdram->cfg; 49561b24405Saurel32 break; 49661b24405Saurel32 case 0x24: /* SDRAM_STATUS */ 49761b24405Saurel32 ret = sdram->status; 49861b24405Saurel32 break; 49961b24405Saurel32 case 0x30: /* SDRAM_RTR */ 50061b24405Saurel32 ret = sdram->rtr; 50161b24405Saurel32 break; 50261b24405Saurel32 case 0x34: /* SDRAM_PMIT */ 50361b24405Saurel32 ret = sdram->pmit; 50461b24405Saurel32 break; 50561b24405Saurel32 case 0x40: /* SDRAM_B0CR */ 50661b24405Saurel32 ret = sdram->bcr[0]; 50761b24405Saurel32 break; 50861b24405Saurel32 case 0x44: /* SDRAM_B1CR */ 50961b24405Saurel32 ret = sdram->bcr[1]; 51061b24405Saurel32 break; 51161b24405Saurel32 case 0x48: /* SDRAM_B2CR */ 51261b24405Saurel32 ret = sdram->bcr[2]; 51361b24405Saurel32 break; 51461b24405Saurel32 case 0x4C: /* SDRAM_B3CR */ 51561b24405Saurel32 ret = sdram->bcr[3]; 51661b24405Saurel32 break; 51761b24405Saurel32 case 0x80: /* SDRAM_TR */ 51861b24405Saurel32 ret = -1; /* ? */ 51961b24405Saurel32 break; 52061b24405Saurel32 case 0x94: /* SDRAM_ECCCFG */ 52161b24405Saurel32 ret = sdram->ecccfg; 52261b24405Saurel32 break; 52361b24405Saurel32 case 0x98: /* SDRAM_ECCESR */ 52461b24405Saurel32 ret = sdram->eccesr; 52561b24405Saurel32 break; 52661b24405Saurel32 default: /* Error */ 52761b24405Saurel32 ret = -1; 52861b24405Saurel32 break; 52961b24405Saurel32 } 53061b24405Saurel32 break; 53161b24405Saurel32 default: 53261b24405Saurel32 /* Avoid gcc warning */ 53361b24405Saurel32 ret = 0x00000000; 53461b24405Saurel32 break; 53561b24405Saurel32 } 53661b24405Saurel32 53761b24405Saurel32 return ret; 53861b24405Saurel32 } 53961b24405Saurel32 54073b01960SAlexander Graf static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val) 54161b24405Saurel32 { 542c227f099SAnthony Liguori ppc4xx_sdram_t *sdram; 54361b24405Saurel32 54461b24405Saurel32 sdram = opaque; 54561b24405Saurel32 switch (dcrn) { 54661b24405Saurel32 case SDRAM0_CFGADDR: 54761b24405Saurel32 sdram->addr = val; 54861b24405Saurel32 break; 54961b24405Saurel32 case SDRAM0_CFGDATA: 55061b24405Saurel32 switch (sdram->addr) { 55161b24405Saurel32 case 0x00: /* SDRAM_BESR0 */ 55261b24405Saurel32 sdram->besr0 &= ~val; 55361b24405Saurel32 break; 55461b24405Saurel32 case 0x08: /* SDRAM_BESR1 */ 55561b24405Saurel32 sdram->besr1 &= ~val; 55661b24405Saurel32 break; 55761b24405Saurel32 case 0x10: /* SDRAM_BEAR */ 55861b24405Saurel32 sdram->bear = val; 55961b24405Saurel32 break; 56061b24405Saurel32 case 0x20: /* SDRAM_CFG */ 56161b24405Saurel32 val &= 0xFFE00000; 56261b24405Saurel32 if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) { 56361b24405Saurel32 #ifdef DEBUG_SDRAM 56461b24405Saurel32 printf("%s: enable SDRAM controller\n", __func__); 56561b24405Saurel32 #endif 56661b24405Saurel32 /* validate all RAM mappings */ 56761b24405Saurel32 sdram_map_bcr(sdram); 56861b24405Saurel32 sdram->status &= ~0x80000000; 56961b24405Saurel32 } else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) { 57061b24405Saurel32 #ifdef DEBUG_SDRAM 57161b24405Saurel32 printf("%s: disable SDRAM controller\n", __func__); 57261b24405Saurel32 #endif 57361b24405Saurel32 /* invalidate all RAM mappings */ 57461b24405Saurel32 sdram_unmap_bcr(sdram); 57561b24405Saurel32 sdram->status |= 0x80000000; 57661b24405Saurel32 } 57761b24405Saurel32 if (!(sdram->cfg & 0x40000000) && (val & 0x40000000)) 57861b24405Saurel32 sdram->status |= 0x40000000; 57961b24405Saurel32 else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000)) 58061b24405Saurel32 sdram->status &= ~0x40000000; 58161b24405Saurel32 sdram->cfg = val; 58261b24405Saurel32 break; 58361b24405Saurel32 case 0x24: /* SDRAM_STATUS */ 58461b24405Saurel32 /* Read-only register */ 58561b24405Saurel32 break; 58661b24405Saurel32 case 0x30: /* SDRAM_RTR */ 58761b24405Saurel32 sdram->rtr = val & 0x3FF80000; 58861b24405Saurel32 break; 58961b24405Saurel32 case 0x34: /* SDRAM_PMIT */ 59061b24405Saurel32 sdram->pmit = (val & 0xF8000000) | 0x07C00000; 59161b24405Saurel32 break; 59261b24405Saurel32 case 0x40: /* SDRAM_B0CR */ 593b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[0], val, sdram->cfg & 0x80000000); 59461b24405Saurel32 break; 59561b24405Saurel32 case 0x44: /* SDRAM_B1CR */ 596b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[1], val, sdram->cfg & 0x80000000); 59761b24405Saurel32 break; 59861b24405Saurel32 case 0x48: /* SDRAM_B2CR */ 599b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[2], val, sdram->cfg & 0x80000000); 60061b24405Saurel32 break; 60161b24405Saurel32 case 0x4C: /* SDRAM_B3CR */ 602b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[3], val, sdram->cfg & 0x80000000); 60361b24405Saurel32 break; 60461b24405Saurel32 case 0x80: /* SDRAM_TR */ 60561b24405Saurel32 sdram->tr = val & 0x018FC01F; 60661b24405Saurel32 break; 60761b24405Saurel32 case 0x94: /* SDRAM_ECCCFG */ 60861b24405Saurel32 sdram->ecccfg = val & 0x00F00000; 60961b24405Saurel32 break; 61061b24405Saurel32 case 0x98: /* SDRAM_ECCESR */ 61161b24405Saurel32 val &= 0xFFF0F000; 61261b24405Saurel32 if (sdram->eccesr == 0 && val != 0) 61361b24405Saurel32 qemu_irq_raise(sdram->irq); 61461b24405Saurel32 else if (sdram->eccesr != 0 && val == 0) 61561b24405Saurel32 qemu_irq_lower(sdram->irq); 61661b24405Saurel32 sdram->eccesr = val; 61761b24405Saurel32 break; 61861b24405Saurel32 default: /* Error */ 61961b24405Saurel32 break; 62061b24405Saurel32 } 62161b24405Saurel32 break; 62261b24405Saurel32 } 62361b24405Saurel32 } 62461b24405Saurel32 62561b24405Saurel32 static void sdram_reset (void *opaque) 62661b24405Saurel32 { 627c227f099SAnthony Liguori ppc4xx_sdram_t *sdram; 62861b24405Saurel32 62961b24405Saurel32 sdram = opaque; 63061b24405Saurel32 sdram->addr = 0x00000000; 63161b24405Saurel32 sdram->bear = 0x00000000; 63261b24405Saurel32 sdram->besr0 = 0x00000000; /* No error */ 63361b24405Saurel32 sdram->besr1 = 0x00000000; /* No error */ 63461b24405Saurel32 sdram->cfg = 0x00000000; 63561b24405Saurel32 sdram->ecccfg = 0x00000000; /* No ECC */ 63661b24405Saurel32 sdram->eccesr = 0x00000000; /* No error */ 63761b24405Saurel32 sdram->pmit = 0x07C00000; 63861b24405Saurel32 sdram->rtr = 0x05F00000; 63961b24405Saurel32 sdram->tr = 0x00854009; 64061b24405Saurel32 /* We pre-initialize RAM banks */ 64161b24405Saurel32 sdram->status = 0x00000000; 64261b24405Saurel32 sdram->cfg = 0x00800000; 64361b24405Saurel32 } 64461b24405Saurel32 645e2684c0bSAndreas Färber void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, 646b6dcbe08SAvi Kivity MemoryRegion *ram_memories, 647a8170e5eSAvi Kivity hwaddr *ram_bases, 648a8170e5eSAvi Kivity hwaddr *ram_sizes, 64961b24405Saurel32 int do_init) 65061b24405Saurel32 { 651c227f099SAnthony Liguori ppc4xx_sdram_t *sdram; 65261b24405Saurel32 6537267c094SAnthony Liguori sdram = g_malloc0(sizeof(ppc4xx_sdram_t)); 65461b24405Saurel32 sdram->irq = irq; 65561b24405Saurel32 sdram->nbanks = nbanks; 656b6dcbe08SAvi Kivity sdram->ram_memories = ram_memories; 657a8170e5eSAvi Kivity memset(sdram->ram_bases, 0, 4 * sizeof(hwaddr)); 65861b24405Saurel32 memcpy(sdram->ram_bases, ram_bases, 659a8170e5eSAvi Kivity nbanks * sizeof(hwaddr)); 660a8170e5eSAvi Kivity memset(sdram->ram_sizes, 0, 4 * sizeof(hwaddr)); 66161b24405Saurel32 memcpy(sdram->ram_sizes, ram_sizes, 662a8170e5eSAvi Kivity nbanks * sizeof(hwaddr)); 663a08d4367SJan Kiszka qemu_register_reset(&sdram_reset, sdram); 66461b24405Saurel32 ppc_dcr_register(env, SDRAM0_CFGADDR, 66561b24405Saurel32 sdram, &dcr_read_sdram, &dcr_write_sdram); 66661b24405Saurel32 ppc_dcr_register(env, SDRAM0_CFGDATA, 66761b24405Saurel32 sdram, &dcr_read_sdram, &dcr_write_sdram); 66861b24405Saurel32 if (do_init) 66961b24405Saurel32 sdram_map_bcr(sdram); 67061b24405Saurel32 } 671b7da58fdSaurel32 672b7da58fdSaurel32 /* Fill in consecutive SDRAM banks with 'ram_size' bytes of memory. 673b7da58fdSaurel32 * 674b7da58fdSaurel32 * sdram_bank_sizes[] must be 0-terminated. 675b7da58fdSaurel32 * 676b7da58fdSaurel32 * The 4xx SDRAM controller supports a small number of banks, and each bank 677b7da58fdSaurel32 * must be one of a small set of sizes. The number of banks and the supported 678b7da58fdSaurel32 * sizes varies by SoC. */ 679c227f099SAnthony Liguori ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, 680b6dcbe08SAvi Kivity MemoryRegion ram_memories[], 681a8170e5eSAvi Kivity hwaddr ram_bases[], 682a8170e5eSAvi Kivity hwaddr ram_sizes[], 683b7da58fdSaurel32 const unsigned int sdram_bank_sizes[]) 684b7da58fdSaurel32 { 685c227f099SAnthony Liguori ram_addr_t size_left = ram_size; 686b6dcbe08SAvi Kivity ram_addr_t base = 0; 687b7da58fdSaurel32 int i; 688b7da58fdSaurel32 int j; 689b7da58fdSaurel32 690b7da58fdSaurel32 for (i = 0; i < nr_banks; i++) { 691b7da58fdSaurel32 for (j = 0; sdram_bank_sizes[j] != 0; j++) { 692b7da58fdSaurel32 unsigned int bank_size = sdram_bank_sizes[j]; 693b7da58fdSaurel32 6945c130f65Spbrook if (bank_size <= size_left) { 6951724f049SAlex Williamson char name[32]; 6961724f049SAlex Williamson snprintf(name, sizeof(name), "ppc4xx.sdram%d", i); 6972c9b15caSPaolo Bonzini memory_region_init_ram(&ram_memories[i], NULL, name, bank_size); 698c5705a77SAvi Kivity vmstate_register_ram_global(&ram_memories[i]); 699b6dcbe08SAvi Kivity ram_bases[i] = base; 700b7da58fdSaurel32 ram_sizes[i] = bank_size; 70111e5d738SAlin Tomescu base += bank_size; 7025c130f65Spbrook size_left -= bank_size; 703b7da58fdSaurel32 break; 704b7da58fdSaurel32 } 705b7da58fdSaurel32 } 706b7da58fdSaurel32 7075c130f65Spbrook if (!size_left) { 708b7da58fdSaurel32 /* No need to use the remaining banks. */ 709b7da58fdSaurel32 break; 710b7da58fdSaurel32 } 711b7da58fdSaurel32 } 712b7da58fdSaurel32 7135c130f65Spbrook ram_size -= size_left; 714d23ab920SHollis Blanchard if (size_left) 715b7da58fdSaurel32 printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n", 7165c130f65Spbrook (int)(ram_size >> 20)); 717b7da58fdSaurel32 7185c130f65Spbrook return ram_size; 719b7da58fdSaurel32 } 720