| 1 | /* |
| 2 | * Axiado SoC AX3000 |
| 3 | * |
| 4 | * Author: Kuan-Jui Chiu <kchiu@axiado.com> |
| 5 | * |
| 6 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 7 | */ |
| 8 | |
| 9 | #include "qemu/osdep.h" |
| 10 | #include "system/address-spaces.h" |
| 11 | #include "hw/arm/bsa.h" |
| 12 | #include "hw/arm/ax3000-soc.h" |
| 13 | #include "hw/misc/unimp.h" |
| 14 | #include "system/system.h" |
| 15 | #include "qobject/qlist.h" |
| 16 | #include "qom/object.h" |
| 17 | #include "hw/core/boards.h" |
| 18 | |
| 19 | static void ax3000_init(Object *obj) |
| 20 | { |
| 21 | Ax3000SoCState *s = AX3000_SOC(obj); |
| 22 | Ax3000SoCClass *sc = AX3000_SOC_GET_CLASS(s); |
| 23 | |
| 24 | for (int i = 0; i < sc->num_cpus; i++) { |
| 25 | g_autofree char *name = g_strdup_printf("cpu%d", i); |
| 26 | object_initialize_child(obj, name, &s->cpu[i], |
| 27 | ARM_CPU_TYPE_NAME("cortex-a53")); |
| 28 | } |
| 29 | |
| 30 | object_initialize_child(obj, "gic", &s->gic, gicv3_class_name()); |
| 31 | |
| 32 | for (int i = 0; i < AX3000_NUM_UARTS; i++) { |
| 33 | g_autofree char *name = g_strdup_printf("uart%d", i); |
| 34 | object_initialize_child(obj, name, &s->uart[i], TYPE_CADENCE_UART); |
| 35 | } |
| 36 | |
| 37 | object_initialize_child(obj, "clk", &s->ax3000_clk, TYPE_AX3000_CLK); |
| 38 | object_initialize_child(obj, "sdhci0", &s->sdhci0, TYPE_AXIADO_SDHCI); |
| 39 | |
| 40 | for (int i = 0; i < AX3000_NUM_GPIOS; i++) { |
| 41 | g_autofree char *name = g_strdup_printf("gpio%d", i); |
| 42 | object_initialize_child(obj, name, &s->gpio[i], TYPE_CADENCE_GPIO); |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | static void ax3000_realize(DeviceState *dev, Error **errp) |
| 47 | { |
| 48 | Ax3000SoCState *s = AX3000_SOC(dev); |
| 49 | Ax3000SoCClass *sc = AX3000_SOC_GET_CLASS(s); |
| 50 | SysBusDevice *gic_sbd = SYS_BUS_DEVICE(&s->gic); |
| 51 | DeviceState *gic_dev = DEVICE(&s->gic); |
| 52 | QList *redist_region_count; |
| 53 | SysBusDevice *sdhci0_sbd; |
| 54 | DeviceState *card; |
| 55 | DriveInfo *dinfo; |
| 56 | |
| 57 | /* CPUs */ |
| 58 | for (int i = 0; i < sc->num_cpus; i++) { |
| 59 | object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000, |
| 60 | &error_abort); |
| 61 | |
| 62 | if (object_property_find(OBJECT(&s->cpu[i]), "has_el3")) { |
| 63 | object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", |
| 64 | false, &error_abort); |
| 65 | } |
| 66 | |
| 67 | if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) { |
| 68 | return; |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | /* GIC */ |
| 73 | qdev_prop_set_uint32(gic_dev, "num-cpu", sc->num_cpus); |
| 74 | qdev_prop_set_uint32(gic_dev, "num-irq", |
| 75 | AX3000_NUM_IRQS + GIC_INTERNAL); |
| 76 | |
| 77 | redist_region_count = qlist_new(); |
| 78 | qlist_append_int(redist_region_count, sc->num_cpus); |
| 79 | qdev_prop_set_array(gic_dev, "redist-region-count", redist_region_count); |
| 80 | |
| 81 | if (!sysbus_realize(gic_sbd, errp)) { |
| 82 | return; |
| 83 | } |
| 84 | |
| 85 | sysbus_mmio_map(gic_sbd, 0, AX3000_GIC_DIST_BASE); |
| 86 | sysbus_mmio_map(gic_sbd, 1, AX3000_GIC_REDIST_BASE); |
| 87 | |
| 88 | /* |
| 89 | * Mapping from the output timer irq lines from the CPU to the |
| 90 | * GIC PPI inputs. |
| 91 | */ |
| 92 | const int timer_irqs[] = { |
| 93 | [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, |
| 94 | [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, |
| 95 | [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, |
| 96 | [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ |
| 97 | }; |
| 98 | |
| 99 | /* |
| 100 | * Wire the outputs from each CPU's generic timer and the GICv3 |
| 101 | * maintenance interrupt signal to the appropriate GIC PPI inputs, and |
| 102 | * the GIC's IRQ/FIQ interrupt outputs to the CPU's inputs. |
| 103 | */ |
| 104 | for (int i = 0; i < sc->num_cpus; i++) { |
| 105 | DeviceState *cpu_dev = DEVICE(&s->cpu[i]); |
| 106 | int intidbase = AX3000_NUM_IRQS + i * GIC_INTERNAL; |
| 107 | qemu_irq irq; |
| 108 | |
| 109 | for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) { |
| 110 | irq = qdev_get_gpio_in(gic_dev, intidbase + timer_irqs[j]); |
| 111 | qdev_connect_gpio_out(cpu_dev, j, irq); |
| 112 | } |
| 113 | |
| 114 | irq = qdev_get_gpio_in(gic_dev, intidbase + ARCH_GIC_MAINT_IRQ); |
| 115 | qdev_connect_gpio_out_named(cpu_dev, "gicv3-maintenance-interrupt", |
| 116 | 0, irq); |
| 117 | |
| 118 | sysbus_connect_irq(gic_sbd, i, |
| 119 | qdev_get_gpio_in(cpu_dev, ARM_CPU_IRQ)); |
| 120 | sysbus_connect_irq(gic_sbd, i + sc->num_cpus, |
| 121 | qdev_get_gpio_in(cpu_dev, ARM_CPU_FIQ)); |
| 122 | sysbus_connect_irq(gic_sbd, i + 2 * sc->num_cpus, |
| 123 | qdev_get_gpio_in(cpu_dev, ARM_CPU_VIRQ)); |
| 124 | sysbus_connect_irq(gic_sbd, i + 3 * sc->num_cpus, |
| 125 | qdev_get_gpio_in(cpu_dev, ARM_CPU_VFIQ)); |
| 126 | } |
| 127 | |
| 128 | /* DRAM */ |
| 129 | const struct { |
| 130 | hwaddr addr; |
| 131 | size_t size; |
| 132 | const char *name; |
| 133 | } dram_table[] = { |
| 134 | { AX3000_DRAM0_BASE, AX3000_DRAM0_SIZE, "dram0" }, |
| 135 | { AX3000_DRAM1_BASE, AX3000_DRAM1_SIZE, "dram1" } |
| 136 | }; |
| 137 | |
| 138 | for (int i = 0; i < AX3000_NUM_BANKS; i++) { |
| 139 | memory_region_init_ram(&s->dram[i], OBJECT(s), dram_table[i].name, |
| 140 | dram_table[i].size, &error_fatal); |
| 141 | memory_region_add_subregion(get_system_memory(), dram_table[i].addr, |
| 142 | &s->dram[i]); |
| 143 | } |
| 144 | |
| 145 | /* UARTs */ |
| 146 | const struct { |
| 147 | hwaddr addr; |
| 148 | unsigned int irq; |
| 149 | } serial_table[] = { |
| 150 | { AX3000_UART0_BASE, AX3000_UART0_IRQ }, |
| 151 | { AX3000_UART1_BASE, AX3000_UART1_IRQ }, |
| 152 | { AX3000_UART2_BASE, AX3000_UART2_IRQ }, |
| 153 | { AX3000_UART3_BASE, AX3000_UART3_IRQ } |
| 154 | }; |
| 155 | |
| 156 | for (int i = 0; i < AX3000_NUM_UARTS; i++) { |
| 157 | qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); |
| 158 | if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { |
| 159 | return; |
| 160 | } |
| 161 | |
| 162 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr); |
| 163 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, |
| 164 | qdev_get_gpio_in(gic_dev, serial_table[i].irq)); |
| 165 | } |
| 166 | |
| 167 | /* Timer control */ |
| 168 | create_unimplemented_device("ax3000.timerctrl", AX3000_TIMER_CTRL, 32); |
| 169 | |
| 170 | /* Clock control */ |
| 171 | if (!sysbus_realize(SYS_BUS_DEVICE(&s->ax3000_clk), errp)) { |
| 172 | return; |
| 173 | } |
| 174 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->ax3000_clk), 0, AX3000_PLL_BASE); |
| 175 | |
| 176 | /* SDHCI */ |
| 177 | sdhci0_sbd = SYS_BUS_DEVICE(&s->sdhci0); |
| 178 | if (!sysbus_realize(sdhci0_sbd, errp)) { |
| 179 | return; |
| 180 | } |
| 181 | |
| 182 | sysbus_mmio_map(sdhci0_sbd, 0, AX3000_SDHCI0_BASE); |
| 183 | sysbus_mmio_map(sdhci0_sbd, 1, AX3000_EMMC_PHY_BASE); |
| 184 | sysbus_connect_irq(sdhci0_sbd, 0, |
| 185 | qdev_get_gpio_in(gic_dev, AX3000_SDHCI0_IRQ)); |
| 186 | |
| 187 | dinfo = drive_get(IF_SD, 0, 0); |
| 188 | if (dinfo) { |
| 189 | card = qdev_new(TYPE_SD_CARD); |
| 190 | qdev_prop_set_drive_err(card, "drive", |
| 191 | blk_by_legacy_dinfo(dinfo), |
| 192 | &error_fatal); |
| 193 | qdev_realize_and_unref(card, s->sdhci0.sd_bus, &error_fatal); |
| 194 | } |
| 195 | |
| 196 | /* GPIOs */ |
| 197 | const struct { |
| 198 | hwaddr addr; |
| 199 | unsigned int irq; |
| 200 | } gpio_table[] = { |
| 201 | { AX3000_GPIO0_BASE, AX3000_GPIO0_IRQ }, |
| 202 | { AX3000_GPIO1_BASE, AX3000_GPIO1_IRQ }, |
| 203 | { AX3000_GPIO2_BASE, AX3000_GPIO2_IRQ }, |
| 204 | { AX3000_GPIO3_BASE, AX3000_GPIO3_IRQ }, |
| 205 | { AX3000_GPIO4_BASE, AX3000_GPIO4_IRQ }, |
| 206 | { AX3000_GPIO5_BASE, AX3000_GPIO5_IRQ }, |
| 207 | { AX3000_GPIO6_BASE, AX3000_GPIO6_IRQ }, |
| 208 | { AX3000_GPIO7_BASE, AX3000_GPIO7_IRQ } |
| 209 | }; |
| 210 | |
| 211 | for (int i = 0; i < AX3000_NUM_GPIOS; i++) { |
| 212 | if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) { |
| 213 | return; |
| 214 | } |
| 215 | |
| 216 | sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr); |
| 217 | sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, |
| 218 | qdev_get_gpio_in(gic_dev, gpio_table[i].irq)); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | static void ax3000_class_init(ObjectClass *oc, const void *data) |
| 223 | { |
| 224 | DeviceClass *dc = DEVICE_CLASS(oc); |
| 225 | Ax3000SoCClass *sc = AX3000_SOC_CLASS(oc); |
| 226 | |
| 227 | dc->desc = "Axiado SoC AX3000"; |
| 228 | dc->realize = ax3000_realize; |
| 229 | sc->num_cpus = AX3000_NUM_CPUS; |
| 230 | } |
| 231 | |
| 232 | static const TypeInfo axiado_soc_types[] = { |
| 233 | { |
| 234 | .name = TYPE_AX3000_SOC, |
| 235 | .parent = TYPE_SYS_BUS_DEVICE, |
| 236 | .instance_size = sizeof(Ax3000SoCState), |
| 237 | .instance_init = ax3000_init, |
| 238 | .class_init = ax3000_class_init, |
| 239 | .class_size = sizeof(Ax3000SoCClass), |
| 240 | } |
| 241 | }; |
| 242 | |
| 243 | DEFINE_TYPES(axiado_soc_types) |