| /* |
| * QEMU PowerPC 4xx embedded processors SDRAM controller emulation |
| * |
| * DDR SDRAM controller: |
| * Copyright (c) 2007 Jocelyn Mayer |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| * |
| * DDR2 SDRAM controller: |
| * Copyright (c) 2012 François Revol |
| * Copyright (c) 2016-2019 BALATON Zoltan |
| * |
| * This work is licensed under the GNU GPL license version 2 or later. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qemu/units.h" |
| #include "qapi/error.h" |
| #include "qemu/log.h" |
| #include "exec/address-spaces.h" /* get_system_memory() */ |
| #include "hw/irq.h" |
| #include "hw/qdev-properties.h" |
| #include "hw/ppc/ppc4xx.h" |
| #include "trace.h" |
| |
| /*****************************************************************************/ |
| /* Shared functions */ |
| |
| /* |
| * Split RAM between SDRAM banks. |
| * |
| * sdram_bank_sizes[] must be in descending order, that is sizes[i] > sizes[i+1] |
| * and must be 0-terminated. |
| * |
| * The 4xx SDRAM controller supports a small number of banks, and each bank |
| * must be one of a small set of sizes. The number of banks and the supported |
| * sizes varies by SoC. |
| */ |
| static bool ppc4xx_sdram_banks(MemoryRegion *ram, int nr_banks, |
| Ppc4xxSdramBank ram_banks[], |
| const ram_addr_t sdram_bank_sizes[], |
| Error **errp) |
| { |
| ERRP_GUARD(); |
| ram_addr_t size_left = memory_region_size(ram); |
| ram_addr_t base = 0; |
| ram_addr_t bank_size; |
| int i; |
| int j; |
| |
| for (i = 0; i < nr_banks; i++) { |
| for (j = 0; sdram_bank_sizes[j] != 0; j++) { |
| bank_size = sdram_bank_sizes[j]; |
| if (bank_size <= size_left) { |
| char name[32]; |
| |
| ram_banks[i].base = base; |
| ram_banks[i].size = bank_size; |
| base += bank_size; |
| size_left -= bank_size; |
| snprintf(name, sizeof(name), "ppc4xx.sdram%d", i); |
| memory_region_init_alias(&ram_banks[i].ram, NULL, name, ram, |
| ram_banks[i].base, ram_banks[i].size); |
| break; |
| } |
| } |
| if (!size_left) { |
| /* No need to use the remaining banks. */ |
| break; |
| } |
| } |
| |
| if (size_left) { |
| ram_addr_t used_size = memory_region_size(ram) - size_left; |
| GString *s = g_string_new(NULL); |
| |
| for (i = 0; sdram_bank_sizes[i]; i++) { |
| g_string_append_printf(s, "%" PRIi64 "%s", |
| sdram_bank_sizes[i] / MiB, |
| sdram_bank_sizes[i + 1] ? ", " : ""); |
| } |
| error_setg(errp, "Invalid SDRAM banks"); |
| error_append_hint(errp, "at most %d bank%s of %s MiB each supported\n", |
| nr_banks, nr_banks == 1 ? "" : "s", s->str); |
| error_append_hint(errp, "Possible valid RAM size: %" PRIi64 " MiB\n", |
| used_size ? used_size / MiB : sdram_bank_sizes[i - 1] / MiB); |
| |
| g_string_free(s, true); |
| return false; |
| } |
| return true; |
| } |
| |
| static void sdram_bank_map(Ppc4xxSdramBank *bank) |
| { |
| trace_ppc4xx_sdram_map(bank->base, bank->size); |
| memory_region_init(&bank->container, NULL, "sdram-container", bank->size); |
| memory_region_add_subregion(&bank->container, 0, &bank->ram); |
| memory_region_add_subregion(get_system_memory(), bank->base, |
| &bank->container); |
| } |
| |
| static void sdram_bank_unmap(Ppc4xxSdramBank *bank) |
| { |
| trace_ppc4xx_sdram_unmap(bank->base, bank->size); |
| memory_region_del_subregion(get_system_memory(), &bank->container); |
| memory_region_del_subregion(&bank->container, &bank->ram); |
| object_unparent(OBJECT(&bank->container)); |
| } |
| |
| static void sdram_bank_set_bcr(Ppc4xxSdramBank *bank, uint32_t bcr, |
| hwaddr base, hwaddr size, int enabled) |
| { |
| if (memory_region_is_mapped(&bank->container)) { |
| sdram_bank_unmap(bank); |
| } |
| bank->bcr = bcr; |
| bank->base = base; |
| bank->size = size; |
| if (enabled && (bcr & 1)) { |
| sdram_bank_map(bank); |
| } |
| } |
| |
| enum { |
| SDRAM0_CFGADDR = 0x010, |
| SDRAM0_CFGDATA = 0x011, |
| }; |
| |
| /*****************************************************************************/ |
| /* DDR SDRAM controller */ |
| #define SDRAM_DDR_BCR_MASK 0xFFDEE001 |
| |
| static uint32_t sdram_ddr_bcr(hwaddr ram_base, hwaddr ram_size) |
| { |
| uint32_t bcr; |
| |
| switch (ram_size) { |
| case 4 * MiB: |
| bcr = 0; |
| break; |
| case 8 * MiB: |
| bcr = 0x20000; |
| break; |
| case 16 * MiB: |
| bcr = 0x40000; |
| break; |
| case 32 * MiB: |
| bcr = 0x60000; |
| break; |
| case 64 * MiB: |
| bcr = 0x80000; |
| break; |
| case 128 * MiB: |
| bcr = 0xA0000; |
| break; |
| case 256 * MiB: |
| bcr = 0xC0000; |
| break; |
| default: |
| qemu_log_mask(LOG_GUEST_ERROR, |
| "%s: invalid RAM size 0x%" HWADDR_PRIx "\n", __func__, |
| ram_size); |
| return 0; |
| } |
| bcr |= ram_base & 0xFF800000; |
| bcr |= 1; |
| |
| return bcr; |
| } |
| |
| static inline hwaddr sdram_ddr_base(uint32_t bcr) |
| { |
| return bcr & 0xFF800000; |
| } |
| |
| static hwaddr sdram_ddr_size(uint32_t bcr) |
| { |
| hwaddr size; |
| int sh; |
| |
| sh = (bcr >> 17) & 0x7; |
| if (sh == 7) { |
| size = -1; |
| } else { |
| size = (4 * MiB) << sh; |
| } |
| |
| return size; |
| } |
| |
| static uint32_t sdram_ddr_dcr_read(void *opaque, int dcrn) |
| { |
| Ppc4xxSdramDdrState *s = opaque; |
| uint32_t ret; |
| |
| switch (dcrn) { |
| case SDRAM0_CFGADDR: |
| ret = s->addr; |
| break; |
| case SDRAM0_CFGDATA: |
| switch (s->addr) { |
| case 0x00: /* SDRAM_BESR0 */ |
| ret = s->besr0; |
| break; |
| case 0x08: /* SDRAM_BESR1 */ |
| ret = s->besr1; |
| break; |
| case 0x10: /* SDRAM_BEAR */ |
| ret = s->bear; |
| break; |
| case 0x20: /* SDRAM_CFG */ |
| ret = s->cfg; |
| break; |
| case 0x24: /* SDRAM_STATUS */ |
| ret = s->status; |
| break; |
| case 0x30: /* SDRAM_RTR */ |
| ret = s->rtr; |
| break; |
| case 0x34: /* SDRAM_PMIT */ |
| ret = s->pmit; |
| break; |
| case 0x40: /* SDRAM_B0CR */ |
| ret = s->bank[0].bcr; |
| break; |
| case 0x44: /* SDRAM_B1CR */ |
| ret = s->bank[1].bcr; |
| break; |
| case 0x48: /* SDRAM_B2CR */ |
| ret = s->bank[2].bcr; |
| break; |
| case 0x4C: /* SDRAM_B3CR */ |
| ret = s->bank[3].bcr; |
| break; |
| case 0x80: /* SDRAM_TR */ |
| ret = -1; /* ? */ |
| break; |
| case 0x94: /* SDRAM_ECCCFG */ |
| ret = s->ecccfg; |
| break; |
| case 0x98: /* SDRAM_ECCESR */ |
| ret = s->eccesr; |
| break; |
| default: /* Error */ |
| ret = -1; |
| break; |
| } |
| break; |
| default: |
| /* Avoid gcc warning */ |
| ret = 0; |
| break; |
| } |
| |
| return ret; |
| } |
| |
| static void sdram_ddr_dcr_write(void *opaque, int dcrn, uint32_t val) |
| { |
| Ppc4xxSdramDdrState *s = opaque; |
| int i; |
| |
| switch (dcrn) { |
| case SDRAM0_CFGADDR: |
| s->addr = val; |
| break; |
| case SDRAM0_CFGDATA: |
| switch (s->addr) { |
| case 0x00: /* SDRAM_BESR0 */ |
| s->besr0 &= ~val; |
| break; |
| case 0x08: /* SDRAM_BESR1 */ |
| s->besr1 &= ~val; |
| break; |
| case 0x10: /* SDRAM_BEAR */ |
| s->bear = val; |
| break; |
| case 0x20: /* SDRAM_CFG */ |
| val &= 0xFFE00000; |
| if (!(s->cfg & 0x80000000) && (val & 0x80000000)) { |
| trace_ppc4xx_sdram_enable("enable"); |
| /* validate all RAM mappings */ |
| for (i = 0; i < s->nbanks; i++) { |
| if (s->bank[i].size) { |
| sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, |
| s->bank[i].base, s->bank[i].size, |
| 1); |
| } |
| } |
| s->status &= ~0x80000000; |
| } else if ((s->cfg & 0x80000000) && !(val & 0x80000000)) { |
| trace_ppc4xx_sdram_enable("disable"); |
| /* invalidate all RAM mappings */ |
| for (i = 0; i < s->nbanks; i++) { |
| if (s->bank[i].size) { |
| sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, |
| s->bank[i].base, s->bank[i].size, |
| 0); |
| } |
| } |
| s->status |= 0x80000000; |
| } |
| if (!(s->cfg & 0x40000000) && (val & 0x40000000)) { |
| s->status |= 0x40000000; |
| } else if ((s->cfg & 0x40000000) && !(val & 0x40000000)) { |
| s->status &= ~0x40000000; |
| } |
| s->cfg = val; |
| break; |
| case 0x24: /* SDRAM_STATUS */ |
| /* Read-only register */ |
| break; |
| case 0x30: /* SDRAM_RTR */ |
| s->rtr = val & 0x3FF80000; |
| break; |
| case 0x34: /* SDRAM_PMIT */ |
| s->pmit = (val & 0xF8000000) | 0x07C00000; |
| break; |
| case 0x40: /* SDRAM_B0CR */ |
| case 0x44: /* SDRAM_B1CR */ |
| case 0x48: /* SDRAM_B2CR */ |
| case 0x4C: /* SDRAM_B3CR */ |
| i = (s->addr - 0x40) / 4; |
| val &= SDRAM_DDR_BCR_MASK; |
| if (s->bank[i].size) { |
| sdram_bank_set_bcr(&s->bank[i], val, |
| sdram_ddr_base(val), sdram_ddr_size(val), |
| s->cfg & 0x80000000); |
| } |
| break; |
| case 0x80: /* SDRAM_TR */ |
| s->tr = val & 0x018FC01F; |
| break; |
| case 0x94: /* SDRAM_ECCCFG */ |
| s->ecccfg = val & 0x00F00000; |
| break; |
| case 0x98: /* SDRAM_ECCESR */ |
| val &= 0xFFF0F000; |
| if (s->eccesr == 0 && val != 0) { |
| qemu_irq_raise(s->irq); |
| } else if (s->eccesr != 0 && val == 0) { |
| qemu_irq_lower(s->irq); |
| } |
| s->eccesr = val; |
| break; |
| default: /* Error */ |
| break; |
| } |
| break; |
| } |
| } |
| |
| static void ppc4xx_sdram_ddr_reset(DeviceState *dev) |
| { |
| Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev); |
| |
| s->addr = 0; |
| s->bear = 0; |
| s->besr0 = 0; /* No error */ |
| s->besr1 = 0; /* No error */ |
| s->cfg = 0; |
| s->ecccfg = 0; /* No ECC */ |
| s->eccesr = 0; /* No error */ |
| s->pmit = 0x07C00000; |
| s->rtr = 0x05F00000; |
| s->tr = 0x00854009; |
| /* We pre-initialize RAM banks */ |
| s->status = 0; |
| s->cfg = 0x00800000; |
| } |
| |
| static void ppc4xx_sdram_ddr_realize(DeviceState *dev, Error **errp) |
| { |
| Ppc4xxSdramDdrState *s = PPC4xx_SDRAM_DDR(dev); |
| Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); |
| const ram_addr_t valid_bank_sizes[] = { |
| 256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 4 * MiB, 0 |
| }; |
| int i; |
| |
| if (s->nbanks < 1 || s->nbanks > 4) { |
| error_setg(errp, "Invalid number of RAM banks"); |
| return; |
| } |
| if (!s->dram_mr) { |
| error_setg(errp, "Missing dram memory region"); |
| return; |
| } |
| if (!ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank, |
| valid_bank_sizes, errp)) { |
| return; |
| } |
| for (i = 0; i < s->nbanks; i++) { |
| if (s->bank[i].size) { |
| s->bank[i].bcr = sdram_ddr_bcr(s->bank[i].base, s->bank[i].size); |
| sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, |
| s->bank[i].base, s->bank[i].size, 0); |
| } else { |
| sdram_bank_set_bcr(&s->bank[i], 0, 0, 0, 0); |
| } |
| trace_ppc4xx_sdram_init(sdram_ddr_base(s->bank[i].bcr), |
| sdram_ddr_size(s->bank[i].bcr), |
| s->bank[i].bcr); |
| } |
| |
| sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); |
| |
| ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR, |
| s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA, |
| s, &sdram_ddr_dcr_read, &sdram_ddr_dcr_write); |
| } |
| |
| static Property ppc4xx_sdram_ddr_props[] = { |
| DEFINE_PROP_LINK("dram", Ppc4xxSdramDdrState, dram_mr, TYPE_MEMORY_REGION, |
| MemoryRegion *), |
| DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4), |
| DEFINE_PROP_END_OF_LIST(), |
| }; |
| |
| static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data) |
| { |
| DeviceClass *dc = DEVICE_CLASS(oc); |
| |
| dc->realize = ppc4xx_sdram_ddr_realize; |
| dc->reset = ppc4xx_sdram_ddr_reset; |
| /* Reason: only works as function of a ppc4xx SoC */ |
| dc->user_creatable = false; |
| device_class_set_props(dc, ppc4xx_sdram_ddr_props); |
| } |
| |
| void ppc4xx_sdram_ddr_enable(Ppc4xxSdramDdrState *s) |
| { |
| sdram_ddr_dcr_write(s, SDRAM0_CFGADDR, 0x20); |
| sdram_ddr_dcr_write(s, SDRAM0_CFGDATA, 0x80000000); |
| } |
| |
| /*****************************************************************************/ |
| /* DDR2 SDRAM controller */ |
| #define SDRAM_DDR2_BCR_MASK 0xffe0ffc1 |
| |
| enum { |
| SDRAM_R0BAS = 0x40, |
| SDRAM_R1BAS, |
| SDRAM_R2BAS, |
| SDRAM_R3BAS, |
| SDRAM_CONF1HB = 0x45, |
| SDRAM_PLBADDULL = 0x4a, |
| SDRAM_CONF1LL = 0x4b, |
| SDRAM_CONFPATHB = 0x4f, |
| SDRAM_PLBADDUHB = 0x50, |
| }; |
| |
| static uint32_t sdram_ddr2_bcr(hwaddr ram_base, hwaddr ram_size) |
| { |
| uint32_t bcr; |
| |
| switch (ram_size) { |
| case 8 * MiB: |
| bcr = 0xffc0; |
| break; |
| case 16 * MiB: |
| bcr = 0xff80; |
| break; |
| case 32 * MiB: |
| bcr = 0xff00; |
| break; |
| case 64 * MiB: |
| bcr = 0xfe00; |
| break; |
| case 128 * MiB: |
| bcr = 0xfc00; |
| break; |
| case 256 * MiB: |
| bcr = 0xf800; |
| break; |
| case 512 * MiB: |
| bcr = 0xf000; |
| break; |
| case 1 * GiB: |
| bcr = 0xe000; |
| break; |
| case 2 * GiB: |
| bcr = 0xc000; |
| break; |
| case 4 * GiB: |
| bcr = 0x8000; |
| break; |
| default: |
| error_report("invalid RAM size " TARGET_FMT_plx, ram_size); |
| return 0; |
| } |
| bcr |= ram_base >> 2 & 0xffe00000; |
| bcr |= 1; |
| |
| return bcr; |
| } |
| |
| static inline hwaddr sdram_ddr2_base(uint32_t bcr) |
| { |
| return (bcr & 0xffe00000) << 2; |
| } |
| |
| static hwaddr sdram_ddr2_size(uint32_t bcr) |
| { |
| hwaddr size; |
| int sh; |
| |
| sh = 1024 - ((bcr >> 6) & 0x3ff); |
| size = 8 * MiB * sh; |
| |
| return size; |
| } |
| |
| static uint32_t sdram_ddr2_dcr_read(void *opaque, int dcrn) |
| { |
| Ppc4xxSdramDdr2State *s = opaque; |
| uint32_t ret = 0; |
| |
| switch (dcrn) { |
| case SDRAM_R0BAS: |
| case SDRAM_R1BAS: |
| case SDRAM_R2BAS: |
| case SDRAM_R3BAS: |
| if (s->bank[dcrn - SDRAM_R0BAS].size) { |
| ret = sdram_ddr2_bcr(s->bank[dcrn - SDRAM_R0BAS].base, |
| s->bank[dcrn - SDRAM_R0BAS].size); |
| } |
| break; |
| case SDRAM_CONF1HB: |
| case SDRAM_CONF1LL: |
| case SDRAM_CONFPATHB: |
| case SDRAM_PLBADDULL: |
| case SDRAM_PLBADDUHB: |
| break; |
| case SDRAM0_CFGADDR: |
| ret = s->addr; |
| break; |
| case SDRAM0_CFGDATA: |
| switch (s->addr) { |
| case 0x14: /* SDRAM_MCSTAT (405EX) */ |
| case 0x1F: |
| ret = 0x80000000; |
| break; |
| case 0x21: /* SDRAM_MCOPT2 */ |
| ret = s->mcopt2; |
| break; |
| case 0x40: /* SDRAM_MB0CF */ |
| ret = 0x00008001; |
| break; |
| case 0x7A: /* SDRAM_DLCR */ |
| ret = 0x02000000; |
| break; |
| case 0xE1: /* SDR0_DDR0 */ |
| ret = SDR0_DDR0_DDRM_ENCODE(1) | SDR0_DDR0_DDRM_DDR1; |
| break; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return ret; |
| } |
| |
| #define SDRAM_DDR2_MCOPT2_DCEN BIT(27) |
| |
| static void sdram_ddr2_dcr_write(void *opaque, int dcrn, uint32_t val) |
| { |
| Ppc4xxSdramDdr2State *s = opaque; |
| int i; |
| |
| switch (dcrn) { |
| case SDRAM_R0BAS: |
| case SDRAM_R1BAS: |
| case SDRAM_R2BAS: |
| case SDRAM_R3BAS: |
| case SDRAM_CONF1HB: |
| case SDRAM_CONF1LL: |
| case SDRAM_CONFPATHB: |
| case SDRAM_PLBADDULL: |
| case SDRAM_PLBADDUHB: |
| break; |
| case SDRAM0_CFGADDR: |
| s->addr = val; |
| break; |
| case SDRAM0_CFGDATA: |
| switch (s->addr) { |
| case 0x00: /* B0CR */ |
| break; |
| case 0x21: /* SDRAM_MCOPT2 */ |
| if (!(s->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) && |
| (val & SDRAM_DDR2_MCOPT2_DCEN)) { |
| trace_ppc4xx_sdram_enable("enable"); |
| /* validate all RAM mappings */ |
| for (i = 0; i < s->nbanks; i++) { |
| if (s->bank[i].size) { |
| sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, |
| s->bank[i].base, s->bank[i].size, |
| 1); |
| } |
| } |
| s->mcopt2 |= SDRAM_DDR2_MCOPT2_DCEN; |
| } else if ((s->mcopt2 & SDRAM_DDR2_MCOPT2_DCEN) && |
| !(val & SDRAM_DDR2_MCOPT2_DCEN)) { |
| trace_ppc4xx_sdram_enable("disable"); |
| /* invalidate all RAM mappings */ |
| for (i = 0; i < s->nbanks; i++) { |
| if (s->bank[i].size) { |
| sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, |
| s->bank[i].base, s->bank[i].size, |
| 0); |
| } |
| } |
| s->mcopt2 &= ~SDRAM_DDR2_MCOPT2_DCEN; |
| } |
| break; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void ppc4xx_sdram_ddr2_reset(DeviceState *dev) |
| { |
| Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev); |
| |
| s->addr = 0; |
| s->mcopt2 = 0; |
| } |
| |
| static void ppc4xx_sdram_ddr2_realize(DeviceState *dev, Error **errp) |
| { |
| Ppc4xxSdramDdr2State *s = PPC4xx_SDRAM_DDR2(dev); |
| Ppc4xxDcrDeviceState *dcr = PPC4xx_DCR_DEVICE(dev); |
| /* |
| * SoC also has 4 GiB but that causes problem with 32 bit |
| * builds (4*GiB overflows the 32 bit ram_addr_t). |
| */ |
| const ram_addr_t valid_bank_sizes[] = { |
| 2 * GiB, 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB, |
| 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0 |
| }; |
| int i; |
| |
| if (s->nbanks < 1 || s->nbanks > 4) { |
| error_setg(errp, "Invalid number of RAM banks"); |
| return; |
| } |
| if (!s->dram_mr) { |
| error_setg(errp, "Missing dram memory region"); |
| return; |
| } |
| if (!ppc4xx_sdram_banks(s->dram_mr, s->nbanks, s->bank, |
| valid_bank_sizes, errp)) { |
| return; |
| } |
| for (i = 0; i < s->nbanks; i++) { |
| if (s->bank[i].size) { |
| s->bank[i].bcr = sdram_ddr2_bcr(s->bank[i].base, s->bank[i].size); |
| s->bank[i].bcr &= SDRAM_DDR2_BCR_MASK; |
| sdram_bank_set_bcr(&s->bank[i], s->bank[i].bcr, |
| s->bank[i].base, s->bank[i].size, 0); |
| } else { |
| sdram_bank_set_bcr(&s->bank[i], 0, 0, 0, 0); |
| } |
| trace_ppc4xx_sdram_init(sdram_ddr2_base(s->bank[i].bcr), |
| sdram_ddr2_size(s->bank[i].bcr), |
| s->bank[i].bcr); |
| } |
| |
| ppc4xx_dcr_register(dcr, SDRAM0_CFGADDR, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM0_CFGDATA, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| |
| ppc4xx_dcr_register(dcr, SDRAM_R0BAS, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM_R1BAS, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM_R2BAS, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM_R3BAS, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM_CONF1HB, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM_PLBADDULL, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM_CONF1LL, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM_CONFPATHB, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| ppc4xx_dcr_register(dcr, SDRAM_PLBADDUHB, |
| s, &sdram_ddr2_dcr_read, &sdram_ddr2_dcr_write); |
| } |
| |
| static Property ppc4xx_sdram_ddr2_props[] = { |
| DEFINE_PROP_LINK("dram", Ppc4xxSdramDdr2State, dram_mr, TYPE_MEMORY_REGION, |
| MemoryRegion *), |
| DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4), |
| DEFINE_PROP_END_OF_LIST(), |
| }; |
| |
| static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data) |
| { |
| DeviceClass *dc = DEVICE_CLASS(oc); |
| |
| dc->realize = ppc4xx_sdram_ddr2_realize; |
| dc->reset = ppc4xx_sdram_ddr2_reset; |
| /* Reason: only works as function of a ppc4xx SoC */ |
| dc->user_creatable = false; |
| device_class_set_props(dc, ppc4xx_sdram_ddr2_props); |
| } |
| |
| void ppc4xx_sdram_ddr2_enable(Ppc4xxSdramDdr2State *s) |
| { |
| sdram_ddr2_dcr_write(s, SDRAM0_CFGADDR, 0x21); |
| sdram_ddr2_dcr_write(s, SDRAM0_CFGDATA, 0x08000000); |
| } |
| |
| static const TypeInfo ppc4xx_sdram_types[] = { |
| { |
| .name = TYPE_PPC4xx_SDRAM_DDR, |
| .parent = TYPE_PPC4xx_DCR_DEVICE, |
| .instance_size = sizeof(Ppc4xxSdramDdrState), |
| .class_init = ppc4xx_sdram_ddr_class_init, |
| }, { |
| .name = TYPE_PPC4xx_SDRAM_DDR2, |
| .parent = TYPE_PPC4xx_DCR_DEVICE, |
| .instance_size = sizeof(Ppc4xxSdramDdr2State), |
| .class_init = ppc4xx_sdram_ddr2_class_init, |
| } |
| }; |
| |
| DEFINE_TYPES(ppc4xx_sdram_types) |