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_MMIO 31aae9366aSj_mayer //#define DEBUG_UNASSIGNED 32008ff9d7Sj_mayer #define DEBUG_UIC 33008ff9d7Sj_mayer 34d12d51d5Saliguori 35d12d51d5Saliguori #ifdef DEBUG_UIC 3693fcfe39Saliguori # define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) 37d12d51d5Saliguori #else 38d12d51d5Saliguori # define LOG_UIC(...) do { } while (0) 39d12d51d5Saliguori #endif 40d12d51d5Saliguori 411bba0dc9SAndreas Färber static void ppc4xx_reset(void *opaque) 421bba0dc9SAndreas Färber { 4390cb09d9SAndreas Färber PowerPCCPU *cpu = opaque; 441bba0dc9SAndreas Färber 4590cb09d9SAndreas Färber cpu_reset(CPU(cpu)); 461bba0dc9SAndreas Färber } 471bba0dc9SAndreas Färber 48008ff9d7Sj_mayer /*****************************************************************************/ 4960b14d95SStefan Weil /* Generic PowerPC 4xx processor instantiation */ 502f9859fbSAndreas Färber PowerPCCPU *ppc4xx_init(const char *cpu_model, 51c227f099SAnthony Liguori clk_setup_t *cpu_clk, clk_setup_t *tb_clk, 52008ff9d7Sj_mayer uint32_t sysclk) 53008ff9d7Sj_mayer { 5457274713SAndreas Färber PowerPCCPU *cpu; 55e2684c0bSAndreas Färber CPUPPCState *env; 56008ff9d7Sj_mayer 57008ff9d7Sj_mayer /* init CPUs */ 5857274713SAndreas Färber cpu = cpu_ppc_init(cpu_model); 5957274713SAndreas Färber if (cpu == NULL) { 60aaed909aSbellard fprintf(stderr, "Unable to find PowerPC %s CPU definition\n", 61008ff9d7Sj_mayer cpu_model); 62aaed909aSbellard exit(1); 63008ff9d7Sj_mayer } 6457274713SAndreas Färber env = &cpu->env; 6557274713SAndreas Färber 66008ff9d7Sj_mayer cpu_clk->cb = NULL; /* We don't care about CPU clock frequency changes */ 67008ff9d7Sj_mayer cpu_clk->opaque = env; 68008ff9d7Sj_mayer /* Set time-base frequency to sysclk */ 69ddd1055bSFabien Chouteau tb_clk->cb = ppc_40x_timers_init(env, sysclk, PPC_INTERRUPT_PIT); 70008ff9d7Sj_mayer tb_clk->opaque = env; 71008ff9d7Sj_mayer ppc_dcr_init(env, NULL, NULL); 72008ff9d7Sj_mayer /* Register qemu callbacks */ 7390cb09d9SAndreas Färber qemu_register_reset(ppc4xx_reset, cpu); 74008ff9d7Sj_mayer 752f9859fbSAndreas Färber return cpu; 76008ff9d7Sj_mayer } 77008ff9d7Sj_mayer 78008ff9d7Sj_mayer /*****************************************************************************/ 79008ff9d7Sj_mayer /* "Universal" Interrupt controller */ 80008ff9d7Sj_mayer enum { 81008ff9d7Sj_mayer DCR_UICSR = 0x000, 82008ff9d7Sj_mayer DCR_UICSRS = 0x001, 83008ff9d7Sj_mayer DCR_UICER = 0x002, 84008ff9d7Sj_mayer DCR_UICCR = 0x003, 85008ff9d7Sj_mayer DCR_UICPR = 0x004, 86008ff9d7Sj_mayer DCR_UICTR = 0x005, 87008ff9d7Sj_mayer DCR_UICMSR = 0x006, 88008ff9d7Sj_mayer DCR_UICVR = 0x007, 89008ff9d7Sj_mayer DCR_UICVCR = 0x008, 90008ff9d7Sj_mayer DCR_UICMAX = 0x009, 91008ff9d7Sj_mayer }; 92008ff9d7Sj_mayer 93008ff9d7Sj_mayer #define UIC_MAX_IRQ 32 94c227f099SAnthony Liguori typedef struct ppcuic_t ppcuic_t; 95c227f099SAnthony Liguori struct ppcuic_t { 96008ff9d7Sj_mayer uint32_t dcr_base; 97008ff9d7Sj_mayer int use_vectors; 984c54e875Saurel32 uint32_t level; /* Remembers the state of level-triggered interrupts. */ 99008ff9d7Sj_mayer uint32_t uicsr; /* Status register */ 100008ff9d7Sj_mayer uint32_t uicer; /* Enable register */ 101008ff9d7Sj_mayer uint32_t uiccr; /* Critical register */ 102008ff9d7Sj_mayer uint32_t uicpr; /* Polarity register */ 103008ff9d7Sj_mayer uint32_t uictr; /* Triggering register */ 104008ff9d7Sj_mayer uint32_t uicvcr; /* Vector configuration register */ 105008ff9d7Sj_mayer uint32_t uicvr; 106008ff9d7Sj_mayer qemu_irq *irqs; 107008ff9d7Sj_mayer }; 108008ff9d7Sj_mayer 109c227f099SAnthony Liguori static void ppcuic_trigger_irq (ppcuic_t *uic) 110008ff9d7Sj_mayer { 111008ff9d7Sj_mayer uint32_t ir, cr; 112008ff9d7Sj_mayer int start, end, inc, i; 113008ff9d7Sj_mayer 114008ff9d7Sj_mayer /* Trigger interrupt if any is pending */ 115008ff9d7Sj_mayer ir = uic->uicsr & uic->uicer & (~uic->uiccr); 116008ff9d7Sj_mayer cr = uic->uicsr & uic->uicer & uic->uiccr; 117d12d51d5Saliguori LOG_UIC("%s: uicsr %08" PRIx32 " uicer %08" PRIx32 118aae9366aSj_mayer " uiccr %08" PRIx32 "\n" 119aae9366aSj_mayer " %08" PRIx32 " ir %08" PRIx32 " cr %08" PRIx32 "\n", 120aae9366aSj_mayer __func__, uic->uicsr, uic->uicer, uic->uiccr, 121008ff9d7Sj_mayer uic->uicsr & uic->uicer, ir, cr); 122008ff9d7Sj_mayer if (ir != 0x0000000) { 123d12d51d5Saliguori LOG_UIC("Raise UIC interrupt\n"); 124008ff9d7Sj_mayer qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_INT]); 125008ff9d7Sj_mayer } else { 126d12d51d5Saliguori LOG_UIC("Lower UIC interrupt\n"); 127008ff9d7Sj_mayer qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_INT]); 128008ff9d7Sj_mayer } 129008ff9d7Sj_mayer /* Trigger critical interrupt if any is pending and update vector */ 130008ff9d7Sj_mayer if (cr != 0x0000000) { 131008ff9d7Sj_mayer qemu_irq_raise(uic->irqs[PPCUIC_OUTPUT_CINT]); 132008ff9d7Sj_mayer if (uic->use_vectors) { 133008ff9d7Sj_mayer /* Compute critical IRQ vector */ 134008ff9d7Sj_mayer if (uic->uicvcr & 1) { 135008ff9d7Sj_mayer start = 31; 136008ff9d7Sj_mayer end = 0; 137008ff9d7Sj_mayer inc = -1; 138008ff9d7Sj_mayer } else { 139008ff9d7Sj_mayer start = 0; 140008ff9d7Sj_mayer end = 31; 141008ff9d7Sj_mayer inc = 1; 142008ff9d7Sj_mayer } 143008ff9d7Sj_mayer uic->uicvr = uic->uicvcr & 0xFFFFFFFC; 144008ff9d7Sj_mayer for (i = start; i <= end; i += inc) { 145008ff9d7Sj_mayer if (cr & (1 << i)) { 146008ff9d7Sj_mayer uic->uicvr += (i - start) * 512 * inc; 147008ff9d7Sj_mayer break; 148008ff9d7Sj_mayer } 149008ff9d7Sj_mayer } 150008ff9d7Sj_mayer } 151d12d51d5Saliguori LOG_UIC("Raise UIC critical interrupt - " 152aae9366aSj_mayer "vector %08" PRIx32 "\n", uic->uicvr); 153008ff9d7Sj_mayer } else { 154d12d51d5Saliguori LOG_UIC("Lower UIC critical interrupt\n"); 155008ff9d7Sj_mayer qemu_irq_lower(uic->irqs[PPCUIC_OUTPUT_CINT]); 156008ff9d7Sj_mayer uic->uicvr = 0x00000000; 157008ff9d7Sj_mayer } 158008ff9d7Sj_mayer } 159008ff9d7Sj_mayer 160008ff9d7Sj_mayer static void ppcuic_set_irq (void *opaque, int irq_num, int level) 161008ff9d7Sj_mayer { 162c227f099SAnthony Liguori ppcuic_t *uic; 163008ff9d7Sj_mayer uint32_t mask, sr; 164008ff9d7Sj_mayer 165008ff9d7Sj_mayer uic = opaque; 166923e5e33Saurel32 mask = 1 << (31-irq_num); 167d12d51d5Saliguori LOG_UIC("%s: irq %d level %d uicsr %08" PRIx32 168aae9366aSj_mayer " mask %08" PRIx32 " => %08" PRIx32 " %08" PRIx32 "\n", 169aae9366aSj_mayer __func__, irq_num, level, 170008ff9d7Sj_mayer uic->uicsr, mask, uic->uicsr & mask, level << irq_num); 171008ff9d7Sj_mayer if (irq_num < 0 || irq_num > 31) 172008ff9d7Sj_mayer return; 173008ff9d7Sj_mayer sr = uic->uicsr; 17450bf72b3Saurel32 175008ff9d7Sj_mayer /* Update status register */ 176008ff9d7Sj_mayer if (uic->uictr & mask) { 177008ff9d7Sj_mayer /* Edge sensitive interrupt */ 178008ff9d7Sj_mayer if (level == 1) 179008ff9d7Sj_mayer uic->uicsr |= mask; 180008ff9d7Sj_mayer } else { 181008ff9d7Sj_mayer /* Level sensitive interrupt */ 1824c54e875Saurel32 if (level == 1) { 183008ff9d7Sj_mayer uic->uicsr |= mask; 1844c54e875Saurel32 uic->level |= mask; 1854c54e875Saurel32 } else { 186008ff9d7Sj_mayer uic->uicsr &= ~mask; 1874c54e875Saurel32 uic->level &= ~mask; 1884c54e875Saurel32 } 189008ff9d7Sj_mayer } 190d12d51d5Saliguori LOG_UIC("%s: irq %d level %d sr %" PRIx32 " => " 191aae9366aSj_mayer "%08" PRIx32 "\n", __func__, irq_num, level, uic->uicsr, sr); 192008ff9d7Sj_mayer if (sr != uic->uicsr) 193008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 194008ff9d7Sj_mayer } 195008ff9d7Sj_mayer 19673b01960SAlexander Graf static uint32_t dcr_read_uic (void *opaque, int dcrn) 197008ff9d7Sj_mayer { 198c227f099SAnthony Liguori ppcuic_t *uic; 19973b01960SAlexander Graf uint32_t ret; 200008ff9d7Sj_mayer 201008ff9d7Sj_mayer uic = opaque; 202008ff9d7Sj_mayer dcrn -= uic->dcr_base; 203008ff9d7Sj_mayer switch (dcrn) { 204008ff9d7Sj_mayer case DCR_UICSR: 205008ff9d7Sj_mayer case DCR_UICSRS: 206008ff9d7Sj_mayer ret = uic->uicsr; 207008ff9d7Sj_mayer break; 208008ff9d7Sj_mayer case DCR_UICER: 209008ff9d7Sj_mayer ret = uic->uicer; 210008ff9d7Sj_mayer break; 211008ff9d7Sj_mayer case DCR_UICCR: 212008ff9d7Sj_mayer ret = uic->uiccr; 213008ff9d7Sj_mayer break; 214008ff9d7Sj_mayer case DCR_UICPR: 215008ff9d7Sj_mayer ret = uic->uicpr; 216008ff9d7Sj_mayer break; 217008ff9d7Sj_mayer case DCR_UICTR: 218008ff9d7Sj_mayer ret = uic->uictr; 219008ff9d7Sj_mayer break; 220008ff9d7Sj_mayer case DCR_UICMSR: 221008ff9d7Sj_mayer ret = uic->uicsr & uic->uicer; 222008ff9d7Sj_mayer break; 223008ff9d7Sj_mayer case DCR_UICVR: 224008ff9d7Sj_mayer if (!uic->use_vectors) 225008ff9d7Sj_mayer goto no_read; 226008ff9d7Sj_mayer ret = uic->uicvr; 227008ff9d7Sj_mayer break; 228008ff9d7Sj_mayer case DCR_UICVCR: 229008ff9d7Sj_mayer if (!uic->use_vectors) 230008ff9d7Sj_mayer goto no_read; 231008ff9d7Sj_mayer ret = uic->uicvcr; 232008ff9d7Sj_mayer break; 233008ff9d7Sj_mayer default: 234008ff9d7Sj_mayer no_read: 235008ff9d7Sj_mayer ret = 0x00000000; 236008ff9d7Sj_mayer break; 237008ff9d7Sj_mayer } 238008ff9d7Sj_mayer 239008ff9d7Sj_mayer return ret; 240008ff9d7Sj_mayer } 241008ff9d7Sj_mayer 24273b01960SAlexander Graf static void dcr_write_uic (void *opaque, int dcrn, uint32_t val) 243008ff9d7Sj_mayer { 244c227f099SAnthony Liguori ppcuic_t *uic; 245008ff9d7Sj_mayer 246008ff9d7Sj_mayer uic = opaque; 247008ff9d7Sj_mayer dcrn -= uic->dcr_base; 24873b01960SAlexander Graf LOG_UIC("%s: dcr %d val 0x%x\n", __func__, dcrn, val); 249008ff9d7Sj_mayer switch (dcrn) { 250008ff9d7Sj_mayer case DCR_UICSR: 251008ff9d7Sj_mayer uic->uicsr &= ~val; 2524c54e875Saurel32 uic->uicsr |= uic->level; 253008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 254008ff9d7Sj_mayer break; 255008ff9d7Sj_mayer case DCR_UICSRS: 256008ff9d7Sj_mayer uic->uicsr |= val; 257008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 258008ff9d7Sj_mayer break; 259008ff9d7Sj_mayer case DCR_UICER: 260008ff9d7Sj_mayer uic->uicer = val; 261008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 262008ff9d7Sj_mayer break; 263008ff9d7Sj_mayer case DCR_UICCR: 264008ff9d7Sj_mayer uic->uiccr = val; 265008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 266008ff9d7Sj_mayer break; 267008ff9d7Sj_mayer case DCR_UICPR: 268008ff9d7Sj_mayer uic->uicpr = val; 269008ff9d7Sj_mayer break; 270008ff9d7Sj_mayer case DCR_UICTR: 271008ff9d7Sj_mayer uic->uictr = val; 272008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 273008ff9d7Sj_mayer break; 274008ff9d7Sj_mayer case DCR_UICMSR: 275008ff9d7Sj_mayer break; 276008ff9d7Sj_mayer case DCR_UICVR: 277008ff9d7Sj_mayer break; 278008ff9d7Sj_mayer case DCR_UICVCR: 279008ff9d7Sj_mayer uic->uicvcr = val & 0xFFFFFFFD; 280008ff9d7Sj_mayer ppcuic_trigger_irq(uic); 281008ff9d7Sj_mayer break; 282008ff9d7Sj_mayer } 283008ff9d7Sj_mayer } 284008ff9d7Sj_mayer 285008ff9d7Sj_mayer static void ppcuic_reset (void *opaque) 286008ff9d7Sj_mayer { 287c227f099SAnthony Liguori ppcuic_t *uic; 288008ff9d7Sj_mayer 289008ff9d7Sj_mayer uic = opaque; 290008ff9d7Sj_mayer uic->uiccr = 0x00000000; 291008ff9d7Sj_mayer uic->uicer = 0x00000000; 292008ff9d7Sj_mayer uic->uicpr = 0x00000000; 293008ff9d7Sj_mayer uic->uicsr = 0x00000000; 294008ff9d7Sj_mayer uic->uictr = 0x00000000; 295008ff9d7Sj_mayer if (uic->use_vectors) { 296008ff9d7Sj_mayer uic->uicvcr = 0x00000000; 297008ff9d7Sj_mayer uic->uicvr = 0x0000000; 298008ff9d7Sj_mayer } 299008ff9d7Sj_mayer } 300008ff9d7Sj_mayer 301e2684c0bSAndreas Färber qemu_irq *ppcuic_init (CPUPPCState *env, qemu_irq *irqs, 302008ff9d7Sj_mayer uint32_t dcr_base, int has_ssr, int has_vr) 303008ff9d7Sj_mayer { 304c227f099SAnthony Liguori ppcuic_t *uic; 305008ff9d7Sj_mayer int i; 306008ff9d7Sj_mayer 3077267c094SAnthony Liguori uic = g_malloc0(sizeof(ppcuic_t)); 308008ff9d7Sj_mayer uic->dcr_base = dcr_base; 309008ff9d7Sj_mayer uic->irqs = irqs; 310008ff9d7Sj_mayer if (has_vr) 311008ff9d7Sj_mayer uic->use_vectors = 1; 312008ff9d7Sj_mayer for (i = 0; i < DCR_UICMAX; i++) { 313008ff9d7Sj_mayer ppc_dcr_register(env, dcr_base + i, uic, 314008ff9d7Sj_mayer &dcr_read_uic, &dcr_write_uic); 315008ff9d7Sj_mayer } 316a08d4367SJan Kiszka qemu_register_reset(ppcuic_reset, uic); 317008ff9d7Sj_mayer 318008ff9d7Sj_mayer return qemu_allocate_irqs(&ppcuic_set_irq, uic, UIC_MAX_IRQ); 319008ff9d7Sj_mayer } 32061b24405Saurel32 32161b24405Saurel32 /*****************************************************************************/ 32261b24405Saurel32 /* SDRAM controller */ 323c227f099SAnthony Liguori typedef struct ppc4xx_sdram_t ppc4xx_sdram_t; 324c227f099SAnthony Liguori struct ppc4xx_sdram_t { 32561b24405Saurel32 uint32_t addr; 32661b24405Saurel32 int nbanks; 327b6dcbe08SAvi Kivity MemoryRegion containers[4]; /* used for clipping */ 328b6dcbe08SAvi Kivity MemoryRegion *ram_memories; 329a8170e5eSAvi Kivity hwaddr ram_bases[4]; 330a8170e5eSAvi Kivity hwaddr ram_sizes[4]; 33161b24405Saurel32 uint32_t besr0; 33261b24405Saurel32 uint32_t besr1; 33361b24405Saurel32 uint32_t bear; 33461b24405Saurel32 uint32_t cfg; 33561b24405Saurel32 uint32_t status; 33661b24405Saurel32 uint32_t rtr; 33761b24405Saurel32 uint32_t pmit; 33861b24405Saurel32 uint32_t bcr[4]; 33961b24405Saurel32 uint32_t tr; 34061b24405Saurel32 uint32_t ecccfg; 34161b24405Saurel32 uint32_t eccesr; 34261b24405Saurel32 qemu_irq irq; 34361b24405Saurel32 }; 34461b24405Saurel32 34561b24405Saurel32 enum { 34661b24405Saurel32 SDRAM0_CFGADDR = 0x010, 34761b24405Saurel32 SDRAM0_CFGDATA = 0x011, 34861b24405Saurel32 }; 34961b24405Saurel32 35061b24405Saurel32 /* XXX: TOFIX: some patches have made this code become inconsistent: 351a8170e5eSAvi Kivity * there are type inconsistencies, mixing hwaddr, target_ulong 35261b24405Saurel32 * and uint32_t 35361b24405Saurel32 */ 354a8170e5eSAvi Kivity static uint32_t sdram_bcr (hwaddr ram_base, 355a8170e5eSAvi Kivity hwaddr ram_size) 35661b24405Saurel32 { 35761b24405Saurel32 uint32_t bcr; 35861b24405Saurel32 35961b24405Saurel32 switch (ram_size) { 36061b24405Saurel32 case (4 * 1024 * 1024): 36161b24405Saurel32 bcr = 0x00000000; 36261b24405Saurel32 break; 36361b24405Saurel32 case (8 * 1024 * 1024): 36461b24405Saurel32 bcr = 0x00020000; 36561b24405Saurel32 break; 36661b24405Saurel32 case (16 * 1024 * 1024): 36761b24405Saurel32 bcr = 0x00040000; 36861b24405Saurel32 break; 36961b24405Saurel32 case (32 * 1024 * 1024): 37061b24405Saurel32 bcr = 0x00060000; 37161b24405Saurel32 break; 37261b24405Saurel32 case (64 * 1024 * 1024): 37361b24405Saurel32 bcr = 0x00080000; 37461b24405Saurel32 break; 37561b24405Saurel32 case (128 * 1024 * 1024): 37661b24405Saurel32 bcr = 0x000A0000; 37761b24405Saurel32 break; 37861b24405Saurel32 case (256 * 1024 * 1024): 37961b24405Saurel32 bcr = 0x000C0000; 38061b24405Saurel32 break; 38161b24405Saurel32 default: 38290e189ecSBlue Swirl printf("%s: invalid RAM size " TARGET_FMT_plx "\n", __func__, 38390e189ecSBlue Swirl ram_size); 38461b24405Saurel32 return 0x00000000; 38561b24405Saurel32 } 38661b24405Saurel32 bcr |= ram_base & 0xFF800000; 38761b24405Saurel32 bcr |= 1; 38861b24405Saurel32 38961b24405Saurel32 return bcr; 39061b24405Saurel32 } 39161b24405Saurel32 392a8170e5eSAvi Kivity static inline hwaddr sdram_base(uint32_t bcr) 39361b24405Saurel32 { 39461b24405Saurel32 return bcr & 0xFF800000; 39561b24405Saurel32 } 39661b24405Saurel32 39761b24405Saurel32 static target_ulong sdram_size (uint32_t bcr) 39861b24405Saurel32 { 39961b24405Saurel32 target_ulong size; 40061b24405Saurel32 int sh; 40161b24405Saurel32 40261b24405Saurel32 sh = (bcr >> 17) & 0x7; 40361b24405Saurel32 if (sh == 7) 40461b24405Saurel32 size = -1; 40561b24405Saurel32 else 40661b24405Saurel32 size = (4 * 1024 * 1024) << sh; 40761b24405Saurel32 40861b24405Saurel32 return size; 40961b24405Saurel32 } 41061b24405Saurel32 411b6dcbe08SAvi Kivity static void sdram_set_bcr(ppc4xx_sdram_t *sdram, 412b6dcbe08SAvi Kivity uint32_t *bcrp, uint32_t bcr, int enabled) 41361b24405Saurel32 { 414b6dcbe08SAvi Kivity unsigned n = bcrp - sdram->bcr; 415b6dcbe08SAvi Kivity 41661b24405Saurel32 if (*bcrp & 0x00000001) { 41761b24405Saurel32 /* Unmap RAM */ 41861b24405Saurel32 #ifdef DEBUG_SDRAM 41990e189ecSBlue Swirl printf("%s: unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", 42061b24405Saurel32 __func__, sdram_base(*bcrp), sdram_size(*bcrp)); 42161b24405Saurel32 #endif 422b6dcbe08SAvi Kivity memory_region_del_subregion(get_system_memory(), 423b6dcbe08SAvi Kivity &sdram->containers[n]); 424b6dcbe08SAvi Kivity memory_region_del_subregion(&sdram->containers[n], 425b6dcbe08SAvi Kivity &sdram->ram_memories[n]); 426b6dcbe08SAvi Kivity memory_region_destroy(&sdram->containers[n]); 42761b24405Saurel32 } 42861b24405Saurel32 *bcrp = bcr & 0xFFDEE001; 42961b24405Saurel32 if (enabled && (bcr & 0x00000001)) { 43061b24405Saurel32 #ifdef DEBUG_SDRAM 43190e189ecSBlue Swirl printf("%s: Map RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", 43261b24405Saurel32 __func__, sdram_base(bcr), sdram_size(bcr)); 43361b24405Saurel32 #endif 434*2c9b15caSPaolo Bonzini memory_region_init(&sdram->containers[n], NULL, "sdram-containers", 435b6dcbe08SAvi Kivity sdram_size(bcr)); 436b6dcbe08SAvi Kivity memory_region_add_subregion(&sdram->containers[n], 0, 437b6dcbe08SAvi Kivity &sdram->ram_memories[n]); 438b6dcbe08SAvi Kivity memory_region_add_subregion(get_system_memory(), 439b6dcbe08SAvi Kivity sdram_base(bcr), 440b6dcbe08SAvi Kivity &sdram->containers[n]); 44161b24405Saurel32 } 44261b24405Saurel32 } 44361b24405Saurel32 444c227f099SAnthony Liguori static void sdram_map_bcr (ppc4xx_sdram_t *sdram) 44561b24405Saurel32 { 44661b24405Saurel32 int i; 44761b24405Saurel32 44861b24405Saurel32 for (i = 0; i < sdram->nbanks; i++) { 44961b24405Saurel32 if (sdram->ram_sizes[i] != 0) { 450b6dcbe08SAvi Kivity sdram_set_bcr(sdram, 451b6dcbe08SAvi Kivity &sdram->bcr[i], 45261b24405Saurel32 sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]), 45361b24405Saurel32 1); 45461b24405Saurel32 } else { 455b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[i], 0x00000000, 0); 45661b24405Saurel32 } 45761b24405Saurel32 } 45861b24405Saurel32 } 45961b24405Saurel32 460c227f099SAnthony Liguori static void sdram_unmap_bcr (ppc4xx_sdram_t *sdram) 46161b24405Saurel32 { 46261b24405Saurel32 int i; 46361b24405Saurel32 46461b24405Saurel32 for (i = 0; i < sdram->nbanks; i++) { 46561b24405Saurel32 #ifdef DEBUG_SDRAM 46690e189ecSBlue Swirl printf("%s: Unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", 46761b24405Saurel32 __func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i])); 46861b24405Saurel32 #endif 469b6dcbe08SAvi Kivity memory_region_del_subregion(get_system_memory(), 470b6dcbe08SAvi Kivity &sdram->ram_memories[i]); 47161b24405Saurel32 } 47261b24405Saurel32 } 47361b24405Saurel32 47473b01960SAlexander Graf static uint32_t dcr_read_sdram (void *opaque, int dcrn) 47561b24405Saurel32 { 476c227f099SAnthony Liguori ppc4xx_sdram_t *sdram; 47773b01960SAlexander Graf uint32_t ret; 47861b24405Saurel32 47961b24405Saurel32 sdram = opaque; 48061b24405Saurel32 switch (dcrn) { 48161b24405Saurel32 case SDRAM0_CFGADDR: 48261b24405Saurel32 ret = sdram->addr; 48361b24405Saurel32 break; 48461b24405Saurel32 case SDRAM0_CFGDATA: 48561b24405Saurel32 switch (sdram->addr) { 48661b24405Saurel32 case 0x00: /* SDRAM_BESR0 */ 48761b24405Saurel32 ret = sdram->besr0; 48861b24405Saurel32 break; 48961b24405Saurel32 case 0x08: /* SDRAM_BESR1 */ 49061b24405Saurel32 ret = sdram->besr1; 49161b24405Saurel32 break; 49261b24405Saurel32 case 0x10: /* SDRAM_BEAR */ 49361b24405Saurel32 ret = sdram->bear; 49461b24405Saurel32 break; 49561b24405Saurel32 case 0x20: /* SDRAM_CFG */ 49661b24405Saurel32 ret = sdram->cfg; 49761b24405Saurel32 break; 49861b24405Saurel32 case 0x24: /* SDRAM_STATUS */ 49961b24405Saurel32 ret = sdram->status; 50061b24405Saurel32 break; 50161b24405Saurel32 case 0x30: /* SDRAM_RTR */ 50261b24405Saurel32 ret = sdram->rtr; 50361b24405Saurel32 break; 50461b24405Saurel32 case 0x34: /* SDRAM_PMIT */ 50561b24405Saurel32 ret = sdram->pmit; 50661b24405Saurel32 break; 50761b24405Saurel32 case 0x40: /* SDRAM_B0CR */ 50861b24405Saurel32 ret = sdram->bcr[0]; 50961b24405Saurel32 break; 51061b24405Saurel32 case 0x44: /* SDRAM_B1CR */ 51161b24405Saurel32 ret = sdram->bcr[1]; 51261b24405Saurel32 break; 51361b24405Saurel32 case 0x48: /* SDRAM_B2CR */ 51461b24405Saurel32 ret = sdram->bcr[2]; 51561b24405Saurel32 break; 51661b24405Saurel32 case 0x4C: /* SDRAM_B3CR */ 51761b24405Saurel32 ret = sdram->bcr[3]; 51861b24405Saurel32 break; 51961b24405Saurel32 case 0x80: /* SDRAM_TR */ 52061b24405Saurel32 ret = -1; /* ? */ 52161b24405Saurel32 break; 52261b24405Saurel32 case 0x94: /* SDRAM_ECCCFG */ 52361b24405Saurel32 ret = sdram->ecccfg; 52461b24405Saurel32 break; 52561b24405Saurel32 case 0x98: /* SDRAM_ECCESR */ 52661b24405Saurel32 ret = sdram->eccesr; 52761b24405Saurel32 break; 52861b24405Saurel32 default: /* Error */ 52961b24405Saurel32 ret = -1; 53061b24405Saurel32 break; 53161b24405Saurel32 } 53261b24405Saurel32 break; 53361b24405Saurel32 default: 53461b24405Saurel32 /* Avoid gcc warning */ 53561b24405Saurel32 ret = 0x00000000; 53661b24405Saurel32 break; 53761b24405Saurel32 } 53861b24405Saurel32 53961b24405Saurel32 return ret; 54061b24405Saurel32 } 54161b24405Saurel32 54273b01960SAlexander Graf static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val) 54361b24405Saurel32 { 544c227f099SAnthony Liguori ppc4xx_sdram_t *sdram; 54561b24405Saurel32 54661b24405Saurel32 sdram = opaque; 54761b24405Saurel32 switch (dcrn) { 54861b24405Saurel32 case SDRAM0_CFGADDR: 54961b24405Saurel32 sdram->addr = val; 55061b24405Saurel32 break; 55161b24405Saurel32 case SDRAM0_CFGDATA: 55261b24405Saurel32 switch (sdram->addr) { 55361b24405Saurel32 case 0x00: /* SDRAM_BESR0 */ 55461b24405Saurel32 sdram->besr0 &= ~val; 55561b24405Saurel32 break; 55661b24405Saurel32 case 0x08: /* SDRAM_BESR1 */ 55761b24405Saurel32 sdram->besr1 &= ~val; 55861b24405Saurel32 break; 55961b24405Saurel32 case 0x10: /* SDRAM_BEAR */ 56061b24405Saurel32 sdram->bear = val; 56161b24405Saurel32 break; 56261b24405Saurel32 case 0x20: /* SDRAM_CFG */ 56361b24405Saurel32 val &= 0xFFE00000; 56461b24405Saurel32 if (!(sdram->cfg & 0x80000000) && (val & 0x80000000)) { 56561b24405Saurel32 #ifdef DEBUG_SDRAM 56661b24405Saurel32 printf("%s: enable SDRAM controller\n", __func__); 56761b24405Saurel32 #endif 56861b24405Saurel32 /* validate all RAM mappings */ 56961b24405Saurel32 sdram_map_bcr(sdram); 57061b24405Saurel32 sdram->status &= ~0x80000000; 57161b24405Saurel32 } else if ((sdram->cfg & 0x80000000) && !(val & 0x80000000)) { 57261b24405Saurel32 #ifdef DEBUG_SDRAM 57361b24405Saurel32 printf("%s: disable SDRAM controller\n", __func__); 57461b24405Saurel32 #endif 57561b24405Saurel32 /* invalidate all RAM mappings */ 57661b24405Saurel32 sdram_unmap_bcr(sdram); 57761b24405Saurel32 sdram->status |= 0x80000000; 57861b24405Saurel32 } 57961b24405Saurel32 if (!(sdram->cfg & 0x40000000) && (val & 0x40000000)) 58061b24405Saurel32 sdram->status |= 0x40000000; 58161b24405Saurel32 else if ((sdram->cfg & 0x40000000) && !(val & 0x40000000)) 58261b24405Saurel32 sdram->status &= ~0x40000000; 58361b24405Saurel32 sdram->cfg = val; 58461b24405Saurel32 break; 58561b24405Saurel32 case 0x24: /* SDRAM_STATUS */ 58661b24405Saurel32 /* Read-only register */ 58761b24405Saurel32 break; 58861b24405Saurel32 case 0x30: /* SDRAM_RTR */ 58961b24405Saurel32 sdram->rtr = val & 0x3FF80000; 59061b24405Saurel32 break; 59161b24405Saurel32 case 0x34: /* SDRAM_PMIT */ 59261b24405Saurel32 sdram->pmit = (val & 0xF8000000) | 0x07C00000; 59361b24405Saurel32 break; 59461b24405Saurel32 case 0x40: /* SDRAM_B0CR */ 595b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[0], val, sdram->cfg & 0x80000000); 59661b24405Saurel32 break; 59761b24405Saurel32 case 0x44: /* SDRAM_B1CR */ 598b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[1], val, sdram->cfg & 0x80000000); 59961b24405Saurel32 break; 60061b24405Saurel32 case 0x48: /* SDRAM_B2CR */ 601b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[2], val, sdram->cfg & 0x80000000); 60261b24405Saurel32 break; 60361b24405Saurel32 case 0x4C: /* SDRAM_B3CR */ 604b6dcbe08SAvi Kivity sdram_set_bcr(sdram, &sdram->bcr[3], val, sdram->cfg & 0x80000000); 60561b24405Saurel32 break; 60661b24405Saurel32 case 0x80: /* SDRAM_TR */ 60761b24405Saurel32 sdram->tr = val & 0x018FC01F; 60861b24405Saurel32 break; 60961b24405Saurel32 case 0x94: /* SDRAM_ECCCFG */ 61061b24405Saurel32 sdram->ecccfg = val & 0x00F00000; 61161b24405Saurel32 break; 61261b24405Saurel32 case 0x98: /* SDRAM_ECCESR */ 61361b24405Saurel32 val &= 0xFFF0F000; 61461b24405Saurel32 if (sdram->eccesr == 0 && val != 0) 61561b24405Saurel32 qemu_irq_raise(sdram->irq); 61661b24405Saurel32 else if (sdram->eccesr != 0 && val == 0) 61761b24405Saurel32 qemu_irq_lower(sdram->irq); 61861b24405Saurel32 sdram->eccesr = val; 61961b24405Saurel32 break; 62061b24405Saurel32 default: /* Error */ 62161b24405Saurel32 break; 62261b24405Saurel32 } 62361b24405Saurel32 break; 62461b24405Saurel32 } 62561b24405Saurel32 } 62661b24405Saurel32 62761b24405Saurel32 static void sdram_reset (void *opaque) 62861b24405Saurel32 { 629c227f099SAnthony Liguori ppc4xx_sdram_t *sdram; 63061b24405Saurel32 63161b24405Saurel32 sdram = opaque; 63261b24405Saurel32 sdram->addr = 0x00000000; 63361b24405Saurel32 sdram->bear = 0x00000000; 63461b24405Saurel32 sdram->besr0 = 0x00000000; /* No error */ 63561b24405Saurel32 sdram->besr1 = 0x00000000; /* No error */ 63661b24405Saurel32 sdram->cfg = 0x00000000; 63761b24405Saurel32 sdram->ecccfg = 0x00000000; /* No ECC */ 63861b24405Saurel32 sdram->eccesr = 0x00000000; /* No error */ 63961b24405Saurel32 sdram->pmit = 0x07C00000; 64061b24405Saurel32 sdram->rtr = 0x05F00000; 64161b24405Saurel32 sdram->tr = 0x00854009; 64261b24405Saurel32 /* We pre-initialize RAM banks */ 64361b24405Saurel32 sdram->status = 0x00000000; 64461b24405Saurel32 sdram->cfg = 0x00800000; 64561b24405Saurel32 } 64661b24405Saurel32 647e2684c0bSAndreas Färber void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, 648b6dcbe08SAvi Kivity MemoryRegion *ram_memories, 649a8170e5eSAvi Kivity hwaddr *ram_bases, 650a8170e5eSAvi Kivity hwaddr *ram_sizes, 65161b24405Saurel32 int do_init) 65261b24405Saurel32 { 653c227f099SAnthony Liguori ppc4xx_sdram_t *sdram; 65461b24405Saurel32 6557267c094SAnthony Liguori sdram = g_malloc0(sizeof(ppc4xx_sdram_t)); 65661b24405Saurel32 sdram->irq = irq; 65761b24405Saurel32 sdram->nbanks = nbanks; 658b6dcbe08SAvi Kivity sdram->ram_memories = ram_memories; 659a8170e5eSAvi Kivity memset(sdram->ram_bases, 0, 4 * sizeof(hwaddr)); 66061b24405Saurel32 memcpy(sdram->ram_bases, ram_bases, 661a8170e5eSAvi Kivity nbanks * sizeof(hwaddr)); 662a8170e5eSAvi Kivity memset(sdram->ram_sizes, 0, 4 * sizeof(hwaddr)); 66361b24405Saurel32 memcpy(sdram->ram_sizes, ram_sizes, 664a8170e5eSAvi Kivity nbanks * sizeof(hwaddr)); 665a08d4367SJan Kiszka qemu_register_reset(&sdram_reset, sdram); 66661b24405Saurel32 ppc_dcr_register(env, SDRAM0_CFGADDR, 66761b24405Saurel32 sdram, &dcr_read_sdram, &dcr_write_sdram); 66861b24405Saurel32 ppc_dcr_register(env, SDRAM0_CFGDATA, 66961b24405Saurel32 sdram, &dcr_read_sdram, &dcr_write_sdram); 67061b24405Saurel32 if (do_init) 67161b24405Saurel32 sdram_map_bcr(sdram); 67261b24405Saurel32 } 673b7da58fdSaurel32 674b7da58fdSaurel32 /* Fill in consecutive SDRAM banks with 'ram_size' bytes of memory. 675b7da58fdSaurel32 * 676b7da58fdSaurel32 * sdram_bank_sizes[] must be 0-terminated. 677b7da58fdSaurel32 * 678b7da58fdSaurel32 * The 4xx SDRAM controller supports a small number of banks, and each bank 679b7da58fdSaurel32 * must be one of a small set of sizes. The number of banks and the supported 680b7da58fdSaurel32 * sizes varies by SoC. */ 681c227f099SAnthony Liguori ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, 682b6dcbe08SAvi Kivity MemoryRegion ram_memories[], 683a8170e5eSAvi Kivity hwaddr ram_bases[], 684a8170e5eSAvi Kivity hwaddr ram_sizes[], 685b7da58fdSaurel32 const unsigned int sdram_bank_sizes[]) 686b7da58fdSaurel32 { 687c227f099SAnthony Liguori ram_addr_t size_left = ram_size; 688b6dcbe08SAvi Kivity ram_addr_t base = 0; 689b7da58fdSaurel32 int i; 690b7da58fdSaurel32 int j; 691b7da58fdSaurel32 692b7da58fdSaurel32 for (i = 0; i < nr_banks; i++) { 693b7da58fdSaurel32 for (j = 0; sdram_bank_sizes[j] != 0; j++) { 694b7da58fdSaurel32 unsigned int bank_size = sdram_bank_sizes[j]; 695b7da58fdSaurel32 6965c130f65Spbrook if (bank_size <= size_left) { 6971724f049SAlex Williamson char name[32]; 6981724f049SAlex Williamson snprintf(name, sizeof(name), "ppc4xx.sdram%d", i); 699*2c9b15caSPaolo Bonzini memory_region_init_ram(&ram_memories[i], NULL, name, bank_size); 700c5705a77SAvi Kivity vmstate_register_ram_global(&ram_memories[i]); 701b6dcbe08SAvi Kivity ram_bases[i] = base; 702b7da58fdSaurel32 ram_sizes[i] = bank_size; 70311e5d738SAlin Tomescu base += bank_size; 7045c130f65Spbrook size_left -= bank_size; 705b7da58fdSaurel32 break; 706b7da58fdSaurel32 } 707b7da58fdSaurel32 } 708b7da58fdSaurel32 7095c130f65Spbrook if (!size_left) { 710b7da58fdSaurel32 /* No need to use the remaining banks. */ 711b7da58fdSaurel32 break; 712b7da58fdSaurel32 } 713b7da58fdSaurel32 } 714b7da58fdSaurel32 7155c130f65Spbrook ram_size -= size_left; 716d23ab920SHollis Blanchard if (size_left) 717b7da58fdSaurel32 printf("Truncating memory to %d MiB to fit SDRAM controller limits.\n", 7185c130f65Spbrook (int)(ram_size >> 20)); 719b7da58fdSaurel32 7205c130f65Spbrook return ram_size; 721b7da58fdSaurel32 } 722