xref: /openbmc/qemu/hw/arm/npcm7xx.c (revision 77586436)
12d8f048cSHavard Skinnemoen /*
22d8f048cSHavard Skinnemoen  * Nuvoton NPCM7xx SoC family.
32d8f048cSHavard Skinnemoen  *
42d8f048cSHavard Skinnemoen  * Copyright 2020 Google LLC
52d8f048cSHavard Skinnemoen  *
62d8f048cSHavard Skinnemoen  * This program is free software; you can redistribute it and/or modify it
72d8f048cSHavard Skinnemoen  * under the terms of the GNU General Public License as published by the
82d8f048cSHavard Skinnemoen  * Free Software Foundation; either version 2 of the License, or
92d8f048cSHavard Skinnemoen  * (at your option) any later version.
102d8f048cSHavard Skinnemoen  *
112d8f048cSHavard Skinnemoen  * This program is distributed in the hope that it will be useful, but WITHOUT
122d8f048cSHavard Skinnemoen  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
132d8f048cSHavard Skinnemoen  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
142d8f048cSHavard Skinnemoen  * for more details.
152d8f048cSHavard Skinnemoen  */
162d8f048cSHavard Skinnemoen 
172d8f048cSHavard Skinnemoen #include "qemu/osdep.h"
182d8f048cSHavard Skinnemoen 
192d8f048cSHavard Skinnemoen #include "exec/address-spaces.h"
202d8f048cSHavard Skinnemoen #include "hw/arm/boot.h"
212d8f048cSHavard Skinnemoen #include "hw/arm/npcm7xx.h"
222d8f048cSHavard Skinnemoen #include "hw/char/serial.h"
232d8f048cSHavard Skinnemoen #include "hw/loader.h"
242d8f048cSHavard Skinnemoen #include "hw/misc/unimp.h"
250be12dc7SHao Wu #include "hw/qdev-clock.h"
262d8f048cSHavard Skinnemoen #include "hw/qdev-properties.h"
272d8f048cSHavard Skinnemoen #include "qapi/error.h"
282d8f048cSHavard Skinnemoen #include "qemu/units.h"
292d8f048cSHavard Skinnemoen #include "sysemu/sysemu.h"
302d8f048cSHavard Skinnemoen 
312d8f048cSHavard Skinnemoen /*
322d8f048cSHavard Skinnemoen  * This covers the whole MMIO space. We'll use this to catch any MMIO accesses
332d8f048cSHavard Skinnemoen  * that aren't handled by any device.
342d8f048cSHavard Skinnemoen  */
352d8f048cSHavard Skinnemoen #define NPCM7XX_MMIO_BA         (0x80000000)
362d8f048cSHavard Skinnemoen #define NPCM7XX_MMIO_SZ         (0x7ffd0000)
372d8f048cSHavard Skinnemoen 
38c752bb07SHavard Skinnemoen /* OTP key storage and fuse strap array */
39c752bb07SHavard Skinnemoen #define NPCM7XX_OTP1_BA         (0xf0189000)
40c752bb07SHavard Skinnemoen #define NPCM7XX_OTP2_BA         (0xf018a000)
41c752bb07SHavard Skinnemoen 
422d8f048cSHavard Skinnemoen /* Core system modules. */
432d8f048cSHavard Skinnemoen #define NPCM7XX_L2C_BA          (0xf03fc000)
442d8f048cSHavard Skinnemoen #define NPCM7XX_CPUP_BA         (0xf03fe000)
452d8f048cSHavard Skinnemoen #define NPCM7XX_GCR_BA          (0xf0800000)
462d8f048cSHavard Skinnemoen #define NPCM7XX_CLK_BA          (0xf0801000)
471351f892SHavard Skinnemoen #define NPCM7XX_MC_BA           (0xf0824000)
48326ccfe2SHavard Skinnemoen #define NPCM7XX_RNG_BA          (0xf000b000)
492d8f048cSHavard Skinnemoen 
50e23e7b12SHavard Skinnemoen /* USB Host modules */
51e23e7b12SHavard Skinnemoen #define NPCM7XX_EHCI_BA         (0xf0806000)
52e23e7b12SHavard Skinnemoen #define NPCM7XX_OHCI_BA         (0xf0807000)
53e23e7b12SHavard Skinnemoen 
5477c05b0bSHao Wu /* ADC Module */
5577c05b0bSHao Wu #define NPCM7XX_ADC_BA          (0xf000c000)
5677c05b0bSHao Wu 
572d8f048cSHavard Skinnemoen /* Internal AHB SRAM */
582d8f048cSHavard Skinnemoen #define NPCM7XX_RAM3_BA         (0xc0008000)
592d8f048cSHavard Skinnemoen #define NPCM7XX_RAM3_SZ         (4 * KiB)
602d8f048cSHavard Skinnemoen 
612d8f048cSHavard Skinnemoen /* Memory blocks at the end of the address space */
622d8f048cSHavard Skinnemoen #define NPCM7XX_RAM2_BA         (0xfffd0000)
632d8f048cSHavard Skinnemoen #define NPCM7XX_RAM2_SZ         (128 * KiB)
642d8f048cSHavard Skinnemoen #define NPCM7XX_ROM_BA          (0xffff0000)
652d8f048cSHavard Skinnemoen #define NPCM7XX_ROM_SZ          (64 * KiB)
662d8f048cSHavard Skinnemoen 
6777c05b0bSHao Wu 
682ddae9ccSHavard Skinnemoen /* Clock configuration values to be fixed up when bypassing bootloader */
692ddae9ccSHavard Skinnemoen 
702ddae9ccSHavard Skinnemoen /* Run PLL1 at 1600 MHz */
712ddae9ccSHavard Skinnemoen #define NPCM7XX_PLLCON1_FIXUP_VAL   (0x00402101)
722ddae9ccSHavard Skinnemoen /* Run the CPU from PLL1 and UART from PLL2 */
732ddae9ccSHavard Skinnemoen #define NPCM7XX_CLKSEL_FIXUP_VAL    (0x004aaba9)
742ddae9ccSHavard Skinnemoen 
752d8f048cSHavard Skinnemoen /*
762d8f048cSHavard Skinnemoen  * Interrupt lines going into the GIC. This does not include internal Cortex-A9
772d8f048cSHavard Skinnemoen  * interrupts.
782d8f048cSHavard Skinnemoen  */
792d8f048cSHavard Skinnemoen enum NPCM7xxInterrupt {
8077c05b0bSHao Wu     NPCM7XX_ADC_IRQ             = 0,
812d8f048cSHavard Skinnemoen     NPCM7XX_UART0_IRQ           = 2,
822d8f048cSHavard Skinnemoen     NPCM7XX_UART1_IRQ,
832d8f048cSHavard Skinnemoen     NPCM7XX_UART2_IRQ,
842d8f048cSHavard Skinnemoen     NPCM7XX_UART3_IRQ,
85*77586436SDoug Evans     NPCM7XX_EMC1RX_IRQ          = 15,
86*77586436SDoug Evans     NPCM7XX_EMC1TX_IRQ,
872d8f048cSHavard Skinnemoen     NPCM7XX_TIMER0_IRQ          = 32,   /* Timer Module 0 */
882d8f048cSHavard Skinnemoen     NPCM7XX_TIMER1_IRQ,
892d8f048cSHavard Skinnemoen     NPCM7XX_TIMER2_IRQ,
902d8f048cSHavard Skinnemoen     NPCM7XX_TIMER3_IRQ,
912d8f048cSHavard Skinnemoen     NPCM7XX_TIMER4_IRQ,
922d8f048cSHavard Skinnemoen     NPCM7XX_TIMER5_IRQ,                 /* Timer Module 1 */
932d8f048cSHavard Skinnemoen     NPCM7XX_TIMER6_IRQ,
942d8f048cSHavard Skinnemoen     NPCM7XX_TIMER7_IRQ,
952d8f048cSHavard Skinnemoen     NPCM7XX_TIMER8_IRQ,
962d8f048cSHavard Skinnemoen     NPCM7XX_TIMER9_IRQ,
972d8f048cSHavard Skinnemoen     NPCM7XX_TIMER10_IRQ,                /* Timer Module 2 */
982d8f048cSHavard Skinnemoen     NPCM7XX_TIMER11_IRQ,
992d8f048cSHavard Skinnemoen     NPCM7XX_TIMER12_IRQ,
1002d8f048cSHavard Skinnemoen     NPCM7XX_TIMER13_IRQ,
1012d8f048cSHavard Skinnemoen     NPCM7XX_TIMER14_IRQ,
1027d378ed6SHao Wu     NPCM7XX_WDG0_IRQ            = 47,   /* Timer Module 0 Watchdog */
1037d378ed6SHao Wu     NPCM7XX_WDG1_IRQ,                   /* Timer Module 1 Watchdog */
1047d378ed6SHao Wu     NPCM7XX_WDG2_IRQ,                   /* Timer Module 2 Watchdog */
105e23e7b12SHavard Skinnemoen     NPCM7XX_EHCI_IRQ            = 61,
106e23e7b12SHavard Skinnemoen     NPCM7XX_OHCI_IRQ            = 62,
10794e77879SHao Wu     NPCM7XX_SMBUS0_IRQ          = 64,
10894e77879SHao Wu     NPCM7XX_SMBUS1_IRQ,
10994e77879SHao Wu     NPCM7XX_SMBUS2_IRQ,
11094e77879SHao Wu     NPCM7XX_SMBUS3_IRQ,
11194e77879SHao Wu     NPCM7XX_SMBUS4_IRQ,
11294e77879SHao Wu     NPCM7XX_SMBUS5_IRQ,
11394e77879SHao Wu     NPCM7XX_SMBUS6_IRQ,
11494e77879SHao Wu     NPCM7XX_SMBUS7_IRQ,
11594e77879SHao Wu     NPCM7XX_SMBUS8_IRQ,
11694e77879SHao Wu     NPCM7XX_SMBUS9_IRQ,
11794e77879SHao Wu     NPCM7XX_SMBUS10_IRQ,
11894e77879SHao Wu     NPCM7XX_SMBUS11_IRQ,
11994e77879SHao Wu     NPCM7XX_SMBUS12_IRQ,
12094e77879SHao Wu     NPCM7XX_SMBUS13_IRQ,
12194e77879SHao Wu     NPCM7XX_SMBUS14_IRQ,
12294e77879SHao Wu     NPCM7XX_SMBUS15_IRQ,
1231e943c58SHao Wu     NPCM7XX_PWM0_IRQ            = 93,   /* PWM module 0 */
1241e943c58SHao Wu     NPCM7XX_PWM1_IRQ,                   /* PWM module 1 */
125*77586436SDoug Evans     NPCM7XX_EMC2RX_IRQ          = 114,
126*77586436SDoug Evans     NPCM7XX_EMC2TX_IRQ,
127526dbbe0SHavard Skinnemoen     NPCM7XX_GPIO0_IRQ           = 116,
128526dbbe0SHavard Skinnemoen     NPCM7XX_GPIO1_IRQ,
129526dbbe0SHavard Skinnemoen     NPCM7XX_GPIO2_IRQ,
130526dbbe0SHavard Skinnemoen     NPCM7XX_GPIO3_IRQ,
131526dbbe0SHavard Skinnemoen     NPCM7XX_GPIO4_IRQ,
132526dbbe0SHavard Skinnemoen     NPCM7XX_GPIO5_IRQ,
133526dbbe0SHavard Skinnemoen     NPCM7XX_GPIO6_IRQ,
134526dbbe0SHavard Skinnemoen     NPCM7XX_GPIO7_IRQ,
1352d8f048cSHavard Skinnemoen };
1362d8f048cSHavard Skinnemoen 
1372d8f048cSHavard Skinnemoen /* Total number of GIC interrupts, including internal Cortex-A9 interrupts. */
1382d8f048cSHavard Skinnemoen #define NPCM7XX_NUM_IRQ         (160)
1392d8f048cSHavard Skinnemoen 
1402d8f048cSHavard Skinnemoen /* Register base address for each Timer Module */
1412d8f048cSHavard Skinnemoen static const hwaddr npcm7xx_tim_addr[] = {
1422d8f048cSHavard Skinnemoen     0xf0008000,
1432d8f048cSHavard Skinnemoen     0xf0009000,
1442d8f048cSHavard Skinnemoen     0xf000a000,
1452d8f048cSHavard Skinnemoen };
1462d8f048cSHavard Skinnemoen 
1472d8f048cSHavard Skinnemoen /* Register base address for each 16550 UART */
1482d8f048cSHavard Skinnemoen static const hwaddr npcm7xx_uart_addr[] = {
1492d8f048cSHavard Skinnemoen     0xf0001000,
1502d8f048cSHavard Skinnemoen     0xf0002000,
1512d8f048cSHavard Skinnemoen     0xf0003000,
1522d8f048cSHavard Skinnemoen     0xf0004000,
1532d8f048cSHavard Skinnemoen };
1542d8f048cSHavard Skinnemoen 
155b821242cSHavard Skinnemoen /* Direct memory-mapped access to SPI0 CS0-1. */
156b821242cSHavard Skinnemoen static const hwaddr npcm7xx_fiu0_flash_addr[] = {
157b821242cSHavard Skinnemoen     0x80000000, /* CS0 */
158b821242cSHavard Skinnemoen     0x88000000, /* CS1 */
159b821242cSHavard Skinnemoen };
160b821242cSHavard Skinnemoen 
161b821242cSHavard Skinnemoen /* Direct memory-mapped access to SPI3 CS0-3. */
162b821242cSHavard Skinnemoen static const hwaddr npcm7xx_fiu3_flash_addr[] = {
163b821242cSHavard Skinnemoen     0xa0000000, /* CS0 */
164b821242cSHavard Skinnemoen     0xa8000000, /* CS1 */
165b821242cSHavard Skinnemoen     0xb0000000, /* CS2 */
166b821242cSHavard Skinnemoen     0xb8000000, /* CS3 */
167b821242cSHavard Skinnemoen };
168b821242cSHavard Skinnemoen 
1691e943c58SHao Wu /* Register base address for each PWM Module */
1701e943c58SHao Wu static const hwaddr npcm7xx_pwm_addr[] = {
1711e943c58SHao Wu     0xf0103000,
1721e943c58SHao Wu     0xf0104000,
1731e943c58SHao Wu };
1741e943c58SHao Wu 
17594e77879SHao Wu /* Direct memory-mapped access to each SMBus Module. */
17694e77879SHao Wu static const hwaddr npcm7xx_smbus_addr[] = {
17794e77879SHao Wu     0xf0080000,
17894e77879SHao Wu     0xf0081000,
17994e77879SHao Wu     0xf0082000,
18094e77879SHao Wu     0xf0083000,
18194e77879SHao Wu     0xf0084000,
18294e77879SHao Wu     0xf0085000,
18394e77879SHao Wu     0xf0086000,
18494e77879SHao Wu     0xf0087000,
18594e77879SHao Wu     0xf0088000,
18694e77879SHao Wu     0xf0089000,
18794e77879SHao Wu     0xf008a000,
18894e77879SHao Wu     0xf008b000,
18994e77879SHao Wu     0xf008c000,
19094e77879SHao Wu     0xf008d000,
19194e77879SHao Wu     0xf008e000,
19294e77879SHao Wu     0xf008f000,
19394e77879SHao Wu };
19494e77879SHao Wu 
195*77586436SDoug Evans /* Register base address for each EMC Module */
196*77586436SDoug Evans static const hwaddr npcm7xx_emc_addr[] = {
197*77586436SDoug Evans     0xf0825000,
198*77586436SDoug Evans     0xf0826000,
199*77586436SDoug Evans };
200*77586436SDoug Evans 
201b821242cSHavard Skinnemoen static const struct {
202526dbbe0SHavard Skinnemoen     hwaddr regs_addr;
203526dbbe0SHavard Skinnemoen     uint32_t unconnected_pins;
204526dbbe0SHavard Skinnemoen     uint32_t reset_pu;
205526dbbe0SHavard Skinnemoen     uint32_t reset_pd;
206526dbbe0SHavard Skinnemoen     uint32_t reset_osrc;
207526dbbe0SHavard Skinnemoen     uint32_t reset_odsc;
208526dbbe0SHavard Skinnemoen } npcm7xx_gpio[] = {
209526dbbe0SHavard Skinnemoen     {
210526dbbe0SHavard Skinnemoen         .regs_addr = 0xf0010000,
211526dbbe0SHavard Skinnemoen         .reset_pu = 0xff03ffff,
212526dbbe0SHavard Skinnemoen         .reset_pd = 0x00fc0000,
213526dbbe0SHavard Skinnemoen     }, {
214526dbbe0SHavard Skinnemoen         .regs_addr = 0xf0011000,
215526dbbe0SHavard Skinnemoen         .unconnected_pins = 0x0000001e,
216526dbbe0SHavard Skinnemoen         .reset_pu = 0xfefffe07,
217526dbbe0SHavard Skinnemoen         .reset_pd = 0x010001e0,
218526dbbe0SHavard Skinnemoen     }, {
219526dbbe0SHavard Skinnemoen         .regs_addr = 0xf0012000,
220526dbbe0SHavard Skinnemoen         .reset_pu = 0x780fffff,
221526dbbe0SHavard Skinnemoen         .reset_pd = 0x07f00000,
222526dbbe0SHavard Skinnemoen         .reset_odsc = 0x00700000,
223526dbbe0SHavard Skinnemoen     }, {
224526dbbe0SHavard Skinnemoen         .regs_addr = 0xf0013000,
225526dbbe0SHavard Skinnemoen         .reset_pu = 0x00fc0000,
226526dbbe0SHavard Skinnemoen         .reset_pd = 0xff000000,
227526dbbe0SHavard Skinnemoen     }, {
228526dbbe0SHavard Skinnemoen         .regs_addr = 0xf0014000,
229526dbbe0SHavard Skinnemoen         .reset_pu = 0xffffffff,
230526dbbe0SHavard Skinnemoen     }, {
231526dbbe0SHavard Skinnemoen         .regs_addr = 0xf0015000,
232526dbbe0SHavard Skinnemoen         .reset_pu = 0xbf83f801,
233526dbbe0SHavard Skinnemoen         .reset_pd = 0x007c0000,
234526dbbe0SHavard Skinnemoen         .reset_osrc = 0x000000f1,
235526dbbe0SHavard Skinnemoen         .reset_odsc = 0x3f9f80f1,
236526dbbe0SHavard Skinnemoen     }, {
237526dbbe0SHavard Skinnemoen         .regs_addr = 0xf0016000,
238526dbbe0SHavard Skinnemoen         .reset_pu = 0xfc00f801,
239526dbbe0SHavard Skinnemoen         .reset_pd = 0x000007fe,
240526dbbe0SHavard Skinnemoen         .reset_odsc = 0x00000800,
241526dbbe0SHavard Skinnemoen     }, {
242526dbbe0SHavard Skinnemoen         .regs_addr = 0xf0017000,
243526dbbe0SHavard Skinnemoen         .unconnected_pins = 0xffffff00,
244526dbbe0SHavard Skinnemoen         .reset_pu = 0x0000007f,
245526dbbe0SHavard Skinnemoen         .reset_osrc = 0x0000007f,
246526dbbe0SHavard Skinnemoen         .reset_odsc = 0x0000007f,
247526dbbe0SHavard Skinnemoen     },
248526dbbe0SHavard Skinnemoen };
249526dbbe0SHavard Skinnemoen 
250526dbbe0SHavard Skinnemoen static const struct {
251b821242cSHavard Skinnemoen     const char *name;
252b821242cSHavard Skinnemoen     hwaddr regs_addr;
253b821242cSHavard Skinnemoen     int cs_count;
254b821242cSHavard Skinnemoen     const hwaddr *flash_addr;
255b821242cSHavard Skinnemoen } npcm7xx_fiu[] = {
256b821242cSHavard Skinnemoen     {
257b821242cSHavard Skinnemoen         .name = "fiu0",
258b821242cSHavard Skinnemoen         .regs_addr = 0xfb000000,
259b821242cSHavard Skinnemoen         .cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),
260b821242cSHavard Skinnemoen         .flash_addr = npcm7xx_fiu0_flash_addr,
261b821242cSHavard Skinnemoen     }, {
262b821242cSHavard Skinnemoen         .name = "fiu3",
263b821242cSHavard Skinnemoen         .regs_addr = 0xc0000000,
264b821242cSHavard Skinnemoen         .cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
265b821242cSHavard Skinnemoen         .flash_addr = npcm7xx_fiu3_flash_addr,
266b821242cSHavard Skinnemoen     },
267b821242cSHavard Skinnemoen };
268b821242cSHavard Skinnemoen 
2692ddae9ccSHavard Skinnemoen static void npcm7xx_write_board_setup(ARMCPU *cpu,
2702ddae9ccSHavard Skinnemoen                                       const struct arm_boot_info *info)
2712ddae9ccSHavard Skinnemoen {
2722ddae9ccSHavard Skinnemoen     uint32_t board_setup[] = {
2732ddae9ccSHavard Skinnemoen         0xe59f0010,     /* ldr r0, clk_base_addr */
2742ddae9ccSHavard Skinnemoen         0xe59f1010,     /* ldr r1, pllcon1_value */
2752ddae9ccSHavard Skinnemoen         0xe5801010,     /* str r1, [r0, #16] */
2762ddae9ccSHavard Skinnemoen         0xe59f100c,     /* ldr r1, clksel_value */
2772ddae9ccSHavard Skinnemoen         0xe5801004,     /* str r1, [r0, #4] */
2782ddae9ccSHavard Skinnemoen         0xe12fff1e,     /* bx lr */
2792ddae9ccSHavard Skinnemoen         NPCM7XX_CLK_BA,
2802ddae9ccSHavard Skinnemoen         NPCM7XX_PLLCON1_FIXUP_VAL,
2812ddae9ccSHavard Skinnemoen         NPCM7XX_CLKSEL_FIXUP_VAL,
2822ddae9ccSHavard Skinnemoen     };
2832ddae9ccSHavard Skinnemoen     int i;
2842ddae9ccSHavard Skinnemoen 
2852ddae9ccSHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(board_setup); i++) {
2862ddae9ccSHavard Skinnemoen         board_setup[i] = tswap32(board_setup[i]);
2872ddae9ccSHavard Skinnemoen     }
2882ddae9ccSHavard Skinnemoen     rom_add_blob_fixed("board-setup", board_setup, sizeof(board_setup),
2892ddae9ccSHavard Skinnemoen                        info->board_setup_addr);
2902ddae9ccSHavard Skinnemoen }
2912ddae9ccSHavard Skinnemoen 
2922d8f048cSHavard Skinnemoen static void npcm7xx_write_secondary_boot(ARMCPU *cpu,
2932d8f048cSHavard Skinnemoen                                          const struct arm_boot_info *info)
2942d8f048cSHavard Skinnemoen {
2952d8f048cSHavard Skinnemoen     /*
2962d8f048cSHavard Skinnemoen      * The default smpboot stub halts the secondary CPU with a 'wfi'
2972d8f048cSHavard Skinnemoen      * instruction, but the arch/arm/mach-npcm/platsmp.c in the Linux kernel
2982d8f048cSHavard Skinnemoen      * does not send an IPI to wake it up, so the second CPU fails to boot. So
2992d8f048cSHavard Skinnemoen      * we need to provide our own smpboot stub that can not use 'wfi', it has
3002d8f048cSHavard Skinnemoen      * to spin the secondary CPU until the first CPU writes to the SCRPAD reg.
3012d8f048cSHavard Skinnemoen      */
3022d8f048cSHavard Skinnemoen     uint32_t smpboot[] = {
3032d8f048cSHavard Skinnemoen         0xe59f2018,     /* ldr r2, bootreg_addr */
3042d8f048cSHavard Skinnemoen         0xe3a00000,     /* mov r0, #0 */
3052d8f048cSHavard Skinnemoen         0xe5820000,     /* str r0, [r2] */
3062d8f048cSHavard Skinnemoen         0xe320f002,     /* wfe */
3072d8f048cSHavard Skinnemoen         0xe5921000,     /* ldr r1, [r2] */
3082d8f048cSHavard Skinnemoen         0xe1110001,     /* tst r1, r1 */
3092d8f048cSHavard Skinnemoen         0x0afffffb,     /* beq <wfe> */
3102d8f048cSHavard Skinnemoen         0xe12fff11,     /* bx r1 */
3112d8f048cSHavard Skinnemoen         NPCM7XX_SMP_BOOTREG_ADDR,
3122d8f048cSHavard Skinnemoen     };
3132d8f048cSHavard Skinnemoen     int i;
3142d8f048cSHavard Skinnemoen 
3152d8f048cSHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(smpboot); i++) {
3162d8f048cSHavard Skinnemoen         smpboot[i] = tswap32(smpboot[i]);
3172d8f048cSHavard Skinnemoen     }
3182d8f048cSHavard Skinnemoen 
3192d8f048cSHavard Skinnemoen     rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
3202d8f048cSHavard Skinnemoen                        NPCM7XX_SMP_LOADER_START);
3212d8f048cSHavard Skinnemoen }
3222d8f048cSHavard Skinnemoen 
3232d8f048cSHavard Skinnemoen static struct arm_boot_info npcm7xx_binfo = {
3242d8f048cSHavard Skinnemoen     .loader_start           = NPCM7XX_LOADER_START,
3252d8f048cSHavard Skinnemoen     .smp_loader_start       = NPCM7XX_SMP_LOADER_START,
3262d8f048cSHavard Skinnemoen     .smp_bootreg_addr       = NPCM7XX_SMP_BOOTREG_ADDR,
3272d8f048cSHavard Skinnemoen     .gic_cpu_if_addr        = NPCM7XX_GIC_CPU_IF_ADDR,
3282d8f048cSHavard Skinnemoen     .write_secondary_boot   = npcm7xx_write_secondary_boot,
3292d8f048cSHavard Skinnemoen     .board_id               = -1,
3302ddae9ccSHavard Skinnemoen     .board_setup_addr       = NPCM7XX_BOARD_SETUP_ADDR,
3312ddae9ccSHavard Skinnemoen     .write_board_setup      = npcm7xx_write_board_setup,
3322d8f048cSHavard Skinnemoen };
3332d8f048cSHavard Skinnemoen 
3342d8f048cSHavard Skinnemoen void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
3352d8f048cSHavard Skinnemoen {
3362d8f048cSHavard Skinnemoen     NPCM7xxClass *sc = NPCM7XX_GET_CLASS(soc);
3372d8f048cSHavard Skinnemoen 
3382d8f048cSHavard Skinnemoen     npcm7xx_binfo.ram_size = machine->ram_size;
3392d8f048cSHavard Skinnemoen     npcm7xx_binfo.nb_cpus = sc->num_cpus;
3402d8f048cSHavard Skinnemoen 
3412d8f048cSHavard Skinnemoen     arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
3422d8f048cSHavard Skinnemoen }
3432d8f048cSHavard Skinnemoen 
344c752bb07SHavard Skinnemoen static void npcm7xx_init_fuses(NPCM7xxState *s)
345c752bb07SHavard Skinnemoen {
346c752bb07SHavard Skinnemoen     NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
347c752bb07SHavard Skinnemoen     uint32_t value;
348c752bb07SHavard Skinnemoen 
349c752bb07SHavard Skinnemoen     /*
350c752bb07SHavard Skinnemoen      * The initial mask of disabled modules indicates the chip derivative (e.g.
351c752bb07SHavard Skinnemoen      * NPCM750 or NPCM730).
352c752bb07SHavard Skinnemoen      */
353c752bb07SHavard Skinnemoen     value = tswap32(nc->disabled_modules);
354c752bb07SHavard Skinnemoen     npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
355c752bb07SHavard Skinnemoen                             sizeof(value));
356c752bb07SHavard Skinnemoen }
357c752bb07SHavard Skinnemoen 
35877c05b0bSHao Wu static void npcm7xx_write_adc_calibration(NPCM7xxState *s)
35977c05b0bSHao Wu {
36077c05b0bSHao Wu     /* Both ADC and the fuse array must have realized. */
36177c05b0bSHao Wu     QEMU_BUILD_BUG_ON(sizeof(s->adc.calibration_r_values) != 4);
36277c05b0bSHao Wu     npcm7xx_otp_array_write(&s->fuse_array, s->adc.calibration_r_values,
36377c05b0bSHao Wu             NPCM7XX_FUSE_ADC_CALIB, sizeof(s->adc.calibration_r_values));
36477c05b0bSHao Wu }
36577c05b0bSHao Wu 
3662d8f048cSHavard Skinnemoen static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
3672d8f048cSHavard Skinnemoen {
3682d8f048cSHavard Skinnemoen     return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
3692d8f048cSHavard Skinnemoen }
3702d8f048cSHavard Skinnemoen 
3712d8f048cSHavard Skinnemoen static void npcm7xx_init(Object *obj)
3722d8f048cSHavard Skinnemoen {
3732d8f048cSHavard Skinnemoen     NPCM7xxState *s = NPCM7XX(obj);
3742d8f048cSHavard Skinnemoen     int i;
3752d8f048cSHavard Skinnemoen 
3762d8f048cSHavard Skinnemoen     for (i = 0; i < NPCM7XX_MAX_NUM_CPUS; i++) {
3772d8f048cSHavard Skinnemoen         object_initialize_child(obj, "cpu[*]", &s->cpu[i],
3782d8f048cSHavard Skinnemoen                                 ARM_CPU_TYPE_NAME("cortex-a9"));
3792d8f048cSHavard Skinnemoen     }
3802d8f048cSHavard Skinnemoen 
3812d8f048cSHavard Skinnemoen     object_initialize_child(obj, "a9mpcore", &s->a9mpcore, TYPE_A9MPCORE_PRIV);
3822d8f048cSHavard Skinnemoen     object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM7XX_GCR);
3832d8f048cSHavard Skinnemoen     object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
3842d8f048cSHavard Skinnemoen                               "power-on-straps");
3852d8f048cSHavard Skinnemoen     object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
386c752bb07SHavard Skinnemoen     object_initialize_child(obj, "otp1", &s->key_storage,
387c752bb07SHavard Skinnemoen                             TYPE_NPCM7XX_KEY_STORAGE);
388c752bb07SHavard Skinnemoen     object_initialize_child(obj, "otp2", &s->fuse_array,
389c752bb07SHavard Skinnemoen                             TYPE_NPCM7XX_FUSE_ARRAY);
3901351f892SHavard Skinnemoen     object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC);
391326ccfe2SHavard Skinnemoen     object_initialize_child(obj, "rng", &s->rng, TYPE_NPCM7XX_RNG);
39277c05b0bSHao Wu     object_initialize_child(obj, "adc", &s->adc, TYPE_NPCM7XX_ADC);
3932d8f048cSHavard Skinnemoen 
3942d8f048cSHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
3952d8f048cSHavard Skinnemoen         object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
3962d8f048cSHavard Skinnemoen     }
397b821242cSHavard Skinnemoen 
398526dbbe0SHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(s->gpio); i++) {
399526dbbe0SHavard Skinnemoen         object_initialize_child(obj, "gpio[*]", &s->gpio[i], TYPE_NPCM7XX_GPIO);
400526dbbe0SHavard Skinnemoen     }
401526dbbe0SHavard Skinnemoen 
40294e77879SHao Wu     for (i = 0; i < ARRAY_SIZE(s->smbus); i++) {
40394e77879SHao Wu         object_initialize_child(obj, "smbus[*]", &s->smbus[i],
40494e77879SHao Wu                                 TYPE_NPCM7XX_SMBUS);
40594e77879SHao Wu     }
40694e77879SHao Wu 
407e23e7b12SHavard Skinnemoen     object_initialize_child(obj, "ehci", &s->ehci, TYPE_NPCM7XX_EHCI);
408e23e7b12SHavard Skinnemoen     object_initialize_child(obj, "ohci", &s->ohci, TYPE_SYSBUS_OHCI);
409e23e7b12SHavard Skinnemoen 
410b821242cSHavard Skinnemoen     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
411b821242cSHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
412b821242cSHavard Skinnemoen         object_initialize_child(obj, npcm7xx_fiu[i].name, &s->fiu[i],
413b821242cSHavard Skinnemoen                                 TYPE_NPCM7XX_FIU);
414b821242cSHavard Skinnemoen     }
4151e943c58SHao Wu 
4161e943c58SHao Wu     for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
4171e943c58SHao Wu         object_initialize_child(obj, "pwm[*]", &s->pwm[i], TYPE_NPCM7XX_PWM);
4181e943c58SHao Wu     }
419*77586436SDoug Evans 
420*77586436SDoug Evans     for (i = 0; i < ARRAY_SIZE(s->emc); i++) {
421*77586436SDoug Evans         object_initialize_child(obj, "emc[*]", &s->emc[i], TYPE_NPCM7XX_EMC);
422*77586436SDoug Evans     }
4232d8f048cSHavard Skinnemoen }
4242d8f048cSHavard Skinnemoen 
4252d8f048cSHavard Skinnemoen static void npcm7xx_realize(DeviceState *dev, Error **errp)
4262d8f048cSHavard Skinnemoen {
4272d8f048cSHavard Skinnemoen     NPCM7xxState *s = NPCM7XX(dev);
4282d8f048cSHavard Skinnemoen     NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
4292d8f048cSHavard Skinnemoen     int i;
4302d8f048cSHavard Skinnemoen 
4312d8f048cSHavard Skinnemoen     if (memory_region_size(s->dram) > NPCM7XX_DRAM_SZ) {
4322d8f048cSHavard Skinnemoen         error_setg(errp, "%s: NPCM7xx cannot address more than %" PRIu64
4332d8f048cSHavard Skinnemoen                    " MiB of DRAM", __func__, NPCM7XX_DRAM_SZ / MiB);
4342d8f048cSHavard Skinnemoen         return;
4352d8f048cSHavard Skinnemoen     }
4362d8f048cSHavard Skinnemoen 
4372d8f048cSHavard Skinnemoen     /* CPUs */
4382d8f048cSHavard Skinnemoen     for (i = 0; i < nc->num_cpus; i++) {
4392d8f048cSHavard Skinnemoen         object_property_set_int(OBJECT(&s->cpu[i]), "mp-affinity",
4402d8f048cSHavard Skinnemoen                                 arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
4412d8f048cSHavard Skinnemoen                                 &error_abort);
4422d8f048cSHavard Skinnemoen         object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar",
4432d8f048cSHavard Skinnemoen                                 NPCM7XX_GIC_CPU_IF_ADDR, &error_abort);
4442d8f048cSHavard Skinnemoen         object_property_set_bool(OBJECT(&s->cpu[i]), "reset-hivecs", true,
4452d8f048cSHavard Skinnemoen                                  &error_abort);
4462d8f048cSHavard Skinnemoen 
4472d8f048cSHavard Skinnemoen         /* Disable security extensions. */
4482d8f048cSHavard Skinnemoen         object_property_set_bool(OBJECT(&s->cpu[i]), "has_el3", false,
4492d8f048cSHavard Skinnemoen                                  &error_abort);
4502d8f048cSHavard Skinnemoen 
4512d8f048cSHavard Skinnemoen         if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
4522d8f048cSHavard Skinnemoen             return;
4532d8f048cSHavard Skinnemoen         }
4542d8f048cSHavard Skinnemoen     }
4552d8f048cSHavard Skinnemoen 
4562d8f048cSHavard Skinnemoen     /* A9MPCORE peripherals. Can only fail if we pass bad parameters here. */
4572d8f048cSHavard Skinnemoen     object_property_set_int(OBJECT(&s->a9mpcore), "num-cpu", nc->num_cpus,
4582d8f048cSHavard Skinnemoen                             &error_abort);
4592d8f048cSHavard Skinnemoen     object_property_set_int(OBJECT(&s->a9mpcore), "num-irq", NPCM7XX_NUM_IRQ,
4602d8f048cSHavard Skinnemoen                             &error_abort);
4612d8f048cSHavard Skinnemoen     sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), &error_abort);
4622d8f048cSHavard Skinnemoen     sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, NPCM7XX_CPUP_BA);
4632d8f048cSHavard Skinnemoen 
4642d8f048cSHavard Skinnemoen     for (i = 0; i < nc->num_cpus; i++) {
4652d8f048cSHavard Skinnemoen         sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i,
4662d8f048cSHavard Skinnemoen                            qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
4672d8f048cSHavard Skinnemoen         sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + nc->num_cpus,
4682d8f048cSHavard Skinnemoen                            qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
4692d8f048cSHavard Skinnemoen     }
4702d8f048cSHavard Skinnemoen 
4712d8f048cSHavard Skinnemoen     /* L2 cache controller */
4722d8f048cSHavard Skinnemoen     sysbus_create_simple("l2x0", NPCM7XX_L2C_BA, NULL);
4732d8f048cSHavard Skinnemoen 
4742d8f048cSHavard Skinnemoen     /* System Global Control Registers (GCR). Can fail due to user input. */
4752d8f048cSHavard Skinnemoen     object_property_set_int(OBJECT(&s->gcr), "disabled-modules",
4762d8f048cSHavard Skinnemoen                             nc->disabled_modules, &error_abort);
4772d8f048cSHavard Skinnemoen     object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram));
4782d8f048cSHavard Skinnemoen     if (!sysbus_realize(SYS_BUS_DEVICE(&s->gcr), errp)) {
4792d8f048cSHavard Skinnemoen         return;
4802d8f048cSHavard Skinnemoen     }
4812d8f048cSHavard Skinnemoen     sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM7XX_GCR_BA);
4822d8f048cSHavard Skinnemoen 
4832d8f048cSHavard Skinnemoen     /* Clock Control Registers (CLK). Cannot fail. */
4842d8f048cSHavard Skinnemoen     sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
4852d8f048cSHavard Skinnemoen     sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
4862d8f048cSHavard Skinnemoen 
487c752bb07SHavard Skinnemoen     /* OTP key storage and fuse strap array. Cannot fail. */
488c752bb07SHavard Skinnemoen     sysbus_realize(SYS_BUS_DEVICE(&s->key_storage), &error_abort);
489c752bb07SHavard Skinnemoen     sysbus_mmio_map(SYS_BUS_DEVICE(&s->key_storage), 0, NPCM7XX_OTP1_BA);
490c752bb07SHavard Skinnemoen     sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort);
491c752bb07SHavard Skinnemoen     sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
492c752bb07SHavard Skinnemoen     npcm7xx_init_fuses(s);
493c752bb07SHavard Skinnemoen 
4941351f892SHavard Skinnemoen     /* Fake Memory Controller (MC). Cannot fail. */
4951351f892SHavard Skinnemoen     sysbus_realize(SYS_BUS_DEVICE(&s->mc), &error_abort);
4961351f892SHavard Skinnemoen     sysbus_mmio_map(SYS_BUS_DEVICE(&s->mc), 0, NPCM7XX_MC_BA);
4971351f892SHavard Skinnemoen 
49877c05b0bSHao Wu     /* ADC Modules. Cannot fail. */
49977c05b0bSHao Wu     qdev_connect_clock_in(DEVICE(&s->adc), "clock", qdev_get_clock_out(
50077c05b0bSHao Wu                           DEVICE(&s->clk), "adc-clock"));
50177c05b0bSHao Wu     sysbus_realize(SYS_BUS_DEVICE(&s->adc), &error_abort);
50277c05b0bSHao Wu     sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, NPCM7XX_ADC_BA);
50377c05b0bSHao Wu     sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0,
50477c05b0bSHao Wu             npcm7xx_irq(s, NPCM7XX_ADC_IRQ));
50577c05b0bSHao Wu     npcm7xx_write_adc_calibration(s);
50677c05b0bSHao Wu 
5072d8f048cSHavard Skinnemoen     /* Timer Modules (TIM). Cannot fail. */
5082d8f048cSHavard Skinnemoen     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
5092d8f048cSHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
5102d8f048cSHavard Skinnemoen         SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]);
5112d8f048cSHavard Skinnemoen         int first_irq;
5122d8f048cSHavard Skinnemoen         int j;
5132d8f048cSHavard Skinnemoen 
5140be12dc7SHao Wu         /* Connect the timer clock. */
5150be12dc7SHao Wu         qdev_connect_clock_in(DEVICE(&s->tim[i]), "clock", qdev_get_clock_out(
5160be12dc7SHao Wu                     DEVICE(&s->clk), "timer-clock"));
5170be12dc7SHao Wu 
5182d8f048cSHavard Skinnemoen         sysbus_realize(sbd, &error_abort);
5192d8f048cSHavard Skinnemoen         sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
5202d8f048cSHavard Skinnemoen 
5212d8f048cSHavard Skinnemoen         first_irq = NPCM7XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL;
5222d8f048cSHavard Skinnemoen         for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) {
5232d8f048cSHavard Skinnemoen             qemu_irq irq = npcm7xx_irq(s, first_irq + j);
5242d8f048cSHavard Skinnemoen             sysbus_connect_irq(sbd, j, irq);
5252d8f048cSHavard Skinnemoen         }
5267d378ed6SHao Wu 
5277d378ed6SHao Wu         /* IRQ for watchdogs */
5287d378ed6SHao Wu         sysbus_connect_irq(sbd, NPCM7XX_TIMERS_PER_CTRL,
5297d378ed6SHao Wu                 npcm7xx_irq(s, NPCM7XX_WDG0_IRQ + i));
5307d378ed6SHao Wu         /* GPIO that connects clk module with watchdog */
5317d378ed6SHao Wu         qdev_connect_gpio_out_named(DEVICE(&s->tim[i]),
5327d378ed6SHao Wu                 NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 0,
5337d378ed6SHao Wu                 qdev_get_gpio_in_named(DEVICE(&s->clk),
5347d378ed6SHao Wu                         NPCM7XX_WATCHDOG_RESET_GPIO_IN, i));
5352d8f048cSHavard Skinnemoen     }
5362d8f048cSHavard Skinnemoen 
5372d8f048cSHavard Skinnemoen     /* UART0..3 (16550 compatible) */
5382d8f048cSHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(npcm7xx_uart_addr); i++) {
5392d8f048cSHavard Skinnemoen         serial_mm_init(get_system_memory(), npcm7xx_uart_addr[i], 2,
5402d8f048cSHavard Skinnemoen                        npcm7xx_irq(s, NPCM7XX_UART0_IRQ + i), 115200,
5412d8f048cSHavard Skinnemoen                        serial_hd(i), DEVICE_LITTLE_ENDIAN);
5422d8f048cSHavard Skinnemoen     }
5432d8f048cSHavard Skinnemoen 
544326ccfe2SHavard Skinnemoen     /* Random Number Generator. Cannot fail. */
545326ccfe2SHavard Skinnemoen     sysbus_realize(SYS_BUS_DEVICE(&s->rng), &error_abort);
546326ccfe2SHavard Skinnemoen     sysbus_mmio_map(SYS_BUS_DEVICE(&s->rng), 0, NPCM7XX_RNG_BA);
547326ccfe2SHavard Skinnemoen 
548526dbbe0SHavard Skinnemoen     /* GPIO modules. Cannot fail. */
549526dbbe0SHavard Skinnemoen     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_gpio) != ARRAY_SIZE(s->gpio));
550526dbbe0SHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(s->gpio); i++) {
551526dbbe0SHavard Skinnemoen         Object *obj = OBJECT(&s->gpio[i]);
552526dbbe0SHavard Skinnemoen 
553526dbbe0SHavard Skinnemoen         object_property_set_uint(obj, "reset-pullup",
554526dbbe0SHavard Skinnemoen                                  npcm7xx_gpio[i].reset_pu, &error_abort);
555526dbbe0SHavard Skinnemoen         object_property_set_uint(obj, "reset-pulldown",
556526dbbe0SHavard Skinnemoen                                  npcm7xx_gpio[i].reset_pd, &error_abort);
557526dbbe0SHavard Skinnemoen         object_property_set_uint(obj, "reset-osrc",
558526dbbe0SHavard Skinnemoen                                  npcm7xx_gpio[i].reset_osrc, &error_abort);
559526dbbe0SHavard Skinnemoen         object_property_set_uint(obj, "reset-odsc",
560526dbbe0SHavard Skinnemoen                                  npcm7xx_gpio[i].reset_odsc, &error_abort);
561526dbbe0SHavard Skinnemoen         sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort);
562526dbbe0SHavard Skinnemoen         sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm7xx_gpio[i].regs_addr);
563526dbbe0SHavard Skinnemoen         sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0,
564526dbbe0SHavard Skinnemoen                            npcm7xx_irq(s, NPCM7XX_GPIO0_IRQ + i));
565526dbbe0SHavard Skinnemoen     }
566526dbbe0SHavard Skinnemoen 
56794e77879SHao Wu     /* SMBus modules. Cannot fail. */
56894e77879SHao Wu     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_smbus_addr) != ARRAY_SIZE(s->smbus));
56994e77879SHao Wu     for (i = 0; i < ARRAY_SIZE(s->smbus); i++) {
57094e77879SHao Wu         Object *obj = OBJECT(&s->smbus[i]);
57194e77879SHao Wu 
57294e77879SHao Wu         sysbus_realize(SYS_BUS_DEVICE(obj), &error_abort);
57394e77879SHao Wu         sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm7xx_smbus_addr[i]);
57494e77879SHao Wu         sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0,
57594e77879SHao Wu                            npcm7xx_irq(s, NPCM7XX_SMBUS0_IRQ + i));
57694e77879SHao Wu     }
57794e77879SHao Wu 
578e23e7b12SHavard Skinnemoen     /* USB Host */
579e23e7b12SHavard Skinnemoen     object_property_set_bool(OBJECT(&s->ehci), "companion-enable", true,
580e23e7b12SHavard Skinnemoen                              &error_abort);
581e23e7b12SHavard Skinnemoen     sysbus_realize(SYS_BUS_DEVICE(&s->ehci), &error_abort);
582e23e7b12SHavard Skinnemoen     sysbus_mmio_map(SYS_BUS_DEVICE(&s->ehci), 0, NPCM7XX_EHCI_BA);
583e23e7b12SHavard Skinnemoen     sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci), 0,
584e23e7b12SHavard Skinnemoen                        npcm7xx_irq(s, NPCM7XX_EHCI_IRQ));
585e23e7b12SHavard Skinnemoen 
586e23e7b12SHavard Skinnemoen     object_property_set_str(OBJECT(&s->ohci), "masterbus", "usb-bus.0",
587e23e7b12SHavard Skinnemoen                             &error_abort);
588e23e7b12SHavard Skinnemoen     object_property_set_uint(OBJECT(&s->ohci), "num-ports", 1, &error_abort);
589e23e7b12SHavard Skinnemoen     sysbus_realize(SYS_BUS_DEVICE(&s->ohci), &error_abort);
590e23e7b12SHavard Skinnemoen     sysbus_mmio_map(SYS_BUS_DEVICE(&s->ohci), 0, NPCM7XX_OHCI_BA);
591e23e7b12SHavard Skinnemoen     sysbus_connect_irq(SYS_BUS_DEVICE(&s->ohci), 0,
592e23e7b12SHavard Skinnemoen                        npcm7xx_irq(s, NPCM7XX_OHCI_IRQ));
593e23e7b12SHavard Skinnemoen 
5941e943c58SHao Wu     /* PWM Modules. Cannot fail. */
5951e943c58SHao Wu     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_pwm_addr) != ARRAY_SIZE(s->pwm));
5961e943c58SHao Wu     for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
5971e943c58SHao Wu         SysBusDevice *sbd = SYS_BUS_DEVICE(&s->pwm[i]);
5981e943c58SHao Wu 
5991e943c58SHao Wu         qdev_connect_clock_in(DEVICE(&s->pwm[i]), "clock", qdev_get_clock_out(
6001e943c58SHao Wu                     DEVICE(&s->clk), "apb3-clock"));
6011e943c58SHao Wu         sysbus_realize(sbd, &error_abort);
6021e943c58SHao Wu         sysbus_mmio_map(sbd, 0, npcm7xx_pwm_addr[i]);
6031e943c58SHao Wu         sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
6041e943c58SHao Wu     }
6051e943c58SHao Wu 
606b821242cSHavard Skinnemoen     /*
607*77586436SDoug Evans      * EMC Modules. Cannot fail.
608*77586436SDoug Evans      * The mapping of the device to its netdev backend works as follows:
609*77586436SDoug Evans      * emc[i] = nd_table[i]
610*77586436SDoug Evans      * This works around the inability to specify the netdev property for the
611*77586436SDoug Evans      * emc device: it's not pluggable and thus the -device option can't be
612*77586436SDoug Evans      * used.
613*77586436SDoug Evans      */
614*77586436SDoug Evans     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_emc_addr) != ARRAY_SIZE(s->emc));
615*77586436SDoug Evans     QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->emc) != 2);
616*77586436SDoug Evans     for (i = 0; i < ARRAY_SIZE(s->emc); i++) {
617*77586436SDoug Evans         s->emc[i].emc_num = i;
618*77586436SDoug Evans         SysBusDevice *sbd = SYS_BUS_DEVICE(&s->emc[i]);
619*77586436SDoug Evans         if (nd_table[i].used) {
620*77586436SDoug Evans             qemu_check_nic_model(&nd_table[i], TYPE_NPCM7XX_EMC);
621*77586436SDoug Evans             qdev_set_nic_properties(DEVICE(sbd), &nd_table[i]);
622*77586436SDoug Evans         }
623*77586436SDoug Evans         /*
624*77586436SDoug Evans          * The device exists regardless of whether it's connected to a QEMU
625*77586436SDoug Evans          * netdev backend. So always instantiate it even if there is no
626*77586436SDoug Evans          * backend.
627*77586436SDoug Evans          */
628*77586436SDoug Evans         sysbus_realize(sbd, &error_abort);
629*77586436SDoug Evans         sysbus_mmio_map(sbd, 0, npcm7xx_emc_addr[i]);
630*77586436SDoug Evans         int tx_irq = i == 0 ? NPCM7XX_EMC1TX_IRQ : NPCM7XX_EMC2TX_IRQ;
631*77586436SDoug Evans         int rx_irq = i == 0 ? NPCM7XX_EMC1RX_IRQ : NPCM7XX_EMC2RX_IRQ;
632*77586436SDoug Evans         /*
633*77586436SDoug Evans          * N.B. The values for the second argument sysbus_connect_irq are
634*77586436SDoug Evans          * chosen to match the registration order in npcm7xx_emc_realize.
635*77586436SDoug Evans          */
636*77586436SDoug Evans         sysbus_connect_irq(sbd, 0, npcm7xx_irq(s, tx_irq));
637*77586436SDoug Evans         sysbus_connect_irq(sbd, 1, npcm7xx_irq(s, rx_irq));
638*77586436SDoug Evans     }
639*77586436SDoug Evans 
640*77586436SDoug Evans     /*
641b821242cSHavard Skinnemoen      * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
642b821242cSHavard Skinnemoen      * specified, but this is a programming error.
643b821242cSHavard Skinnemoen      */
644b821242cSHavard Skinnemoen     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
645b821242cSHavard Skinnemoen     for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
646b821242cSHavard Skinnemoen         SysBusDevice *sbd = SYS_BUS_DEVICE(&s->fiu[i]);
647b821242cSHavard Skinnemoen         int j;
648b821242cSHavard Skinnemoen 
649b821242cSHavard Skinnemoen         object_property_set_int(OBJECT(sbd), "cs-count",
650b821242cSHavard Skinnemoen                                 npcm7xx_fiu[i].cs_count, &error_abort);
651b821242cSHavard Skinnemoen         sysbus_realize(sbd, &error_abort);
652b821242cSHavard Skinnemoen 
653b821242cSHavard Skinnemoen         sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr);
654b821242cSHavard Skinnemoen         for (j = 0; j < npcm7xx_fiu[i].cs_count; j++) {
655b821242cSHavard Skinnemoen             sysbus_mmio_map(sbd, j + 1, npcm7xx_fiu[i].flash_addr[j]);
656b821242cSHavard Skinnemoen         }
657b821242cSHavard Skinnemoen     }
658b821242cSHavard Skinnemoen 
6592d8f048cSHavard Skinnemoen     /* RAM2 (SRAM) */
6602d8f048cSHavard Skinnemoen     memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
6612d8f048cSHavard Skinnemoen                            NPCM7XX_RAM2_SZ, &error_abort);
6622d8f048cSHavard Skinnemoen     memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM2_BA, &s->sram);
6632d8f048cSHavard Skinnemoen 
6642d8f048cSHavard Skinnemoen     /* RAM3 (SRAM) */
6652d8f048cSHavard Skinnemoen     memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3",
6662d8f048cSHavard Skinnemoen                            NPCM7XX_RAM3_SZ, &error_abort);
6672d8f048cSHavard Skinnemoen     memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM3_BA, &s->ram3);
6682d8f048cSHavard Skinnemoen 
6692d8f048cSHavard Skinnemoen     /* Internal ROM */
6702d8f048cSHavard Skinnemoen     memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM7XX_ROM_SZ,
6712d8f048cSHavard Skinnemoen                            &error_abort);
6722d8f048cSHavard Skinnemoen     memory_region_add_subregion(get_system_memory(), NPCM7XX_ROM_BA, &s->irom);
6732d8f048cSHavard Skinnemoen 
6742d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.shm",          0xc0001000,   4 * KiB);
6752d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.vdmx",         0xe0800000,   4 * KiB);
6762d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.pcierc",       0xe1000000,  64 * KiB);
6772d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.kcs",          0xf0007000,   4 * KiB);
6782d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.gfxi",         0xf000e000,   4 * KiB);
6792d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.espi",         0xf009f000,   4 * KiB);
6802d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.peci",         0xf0100000,   4 * KiB);
6812d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.siox[1]",      0xf0101000,   4 * KiB);
6822d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.siox[2]",      0xf0102000,   4 * KiB);
6832d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mft[0]",       0xf0180000,   4 * KiB);
6842d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mft[1]",       0xf0181000,   4 * KiB);
6852d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mft[2]",       0xf0182000,   4 * KiB);
6862d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mft[3]",       0xf0183000,   4 * KiB);
6872d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mft[4]",       0xf0184000,   4 * KiB);
6882d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mft[5]",       0xf0185000,   4 * KiB);
6892d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mft[6]",       0xf0186000,   4 * KiB);
6902d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mft[7]",       0xf0187000,   4 * KiB);
6912d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.pspi1",        0xf0200000,   4 * KiB);
6922d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.pspi2",        0xf0201000,   4 * KiB);
6932d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.ahbpci",       0xf0400000,   1 * MiB);
6942d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mcphy",        0xf05f0000,  64 * KiB);
6952d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.gmac1",        0xf0802000,   8 * KiB);
6962d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.gmac2",        0xf0804000,   8 * KiB);
6972d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.vcd",          0xf0810000,  64 * KiB);
6982d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.ece",          0xf0820000,   8 * KiB);
6992d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.vdma",         0xf0822000,   8 * KiB);
7002d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[0]",      0xf0830000,   4 * KiB);
7012d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[1]",      0xf0831000,   4 * KiB);
7022d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[2]",      0xf0832000,   4 * KiB);
7032d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[3]",      0xf0833000,   4 * KiB);
7042d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[4]",      0xf0834000,   4 * KiB);
7052d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[5]",      0xf0835000,   4 * KiB);
7062d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[6]",      0xf0836000,   4 * KiB);
7072d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[7]",      0xf0837000,   4 * KiB);
7082d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[8]",      0xf0838000,   4 * KiB);
7092d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.usbd[9]",      0xf0839000,   4 * KiB);
7102d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.sd",           0xf0840000,   8 * KiB);
7112d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.mmc",          0xf0842000,   8 * KiB);
7122d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.pcimbx",       0xf0848000, 512 * KiB);
7132d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.aes",          0xf0858000,   4 * KiB);
7142d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.des",          0xf0859000,   4 * KiB);
7152d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.sha",          0xf085a000,   4 * KiB);
7162d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.secacc",       0xf085b000,   4 * KiB);
7172d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.spixcs0",      0xf8000000,  16 * MiB);
7182d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.spixcs1",      0xf9000000,  16 * MiB);
7192d8f048cSHavard Skinnemoen     create_unimplemented_device("npcm7xx.spix",         0xfb001000,   4 * KiB);
7202d8f048cSHavard Skinnemoen }
7212d8f048cSHavard Skinnemoen 
7222d8f048cSHavard Skinnemoen static Property npcm7xx_properties[] = {
7232d8f048cSHavard Skinnemoen     DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION,
7242d8f048cSHavard Skinnemoen                      MemoryRegion *),
7252d8f048cSHavard Skinnemoen     DEFINE_PROP_END_OF_LIST(),
7262d8f048cSHavard Skinnemoen };
7272d8f048cSHavard Skinnemoen 
7282d8f048cSHavard Skinnemoen static void npcm7xx_class_init(ObjectClass *oc, void *data)
7292d8f048cSHavard Skinnemoen {
7302d8f048cSHavard Skinnemoen     DeviceClass *dc = DEVICE_CLASS(oc);
7312d8f048cSHavard Skinnemoen 
7322d8f048cSHavard Skinnemoen     dc->realize = npcm7xx_realize;
7332d8f048cSHavard Skinnemoen     dc->user_creatable = false;
7342d8f048cSHavard Skinnemoen     device_class_set_props(dc, npcm7xx_properties);
7352d8f048cSHavard Skinnemoen }
7362d8f048cSHavard Skinnemoen 
7372d8f048cSHavard Skinnemoen static void npcm730_class_init(ObjectClass *oc, void *data)
7382d8f048cSHavard Skinnemoen {
7392d8f048cSHavard Skinnemoen     NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
7402d8f048cSHavard Skinnemoen 
7412d8f048cSHavard Skinnemoen     /* NPCM730 is optimized for data center use, so no graphics, etc. */
7422d8f048cSHavard Skinnemoen     nc->disabled_modules = 0x00300395;
7432d8f048cSHavard Skinnemoen     nc->num_cpus = 2;
7442d8f048cSHavard Skinnemoen }
7452d8f048cSHavard Skinnemoen 
7462d8f048cSHavard Skinnemoen static void npcm750_class_init(ObjectClass *oc, void *data)
7472d8f048cSHavard Skinnemoen {
7482d8f048cSHavard Skinnemoen     NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
7492d8f048cSHavard Skinnemoen 
7502d8f048cSHavard Skinnemoen     /* NPCM750 has 2 cores and a full set of peripherals */
7512d8f048cSHavard Skinnemoen     nc->disabled_modules = 0x00000000;
7522d8f048cSHavard Skinnemoen     nc->num_cpus = 2;
7532d8f048cSHavard Skinnemoen }
7542d8f048cSHavard Skinnemoen 
7552d8f048cSHavard Skinnemoen static const TypeInfo npcm7xx_soc_types[] = {
7562d8f048cSHavard Skinnemoen     {
7572d8f048cSHavard Skinnemoen         .name           = TYPE_NPCM7XX,
7582d8f048cSHavard Skinnemoen         .parent         = TYPE_DEVICE,
7592d8f048cSHavard Skinnemoen         .instance_size  = sizeof(NPCM7xxState),
7602d8f048cSHavard Skinnemoen         .instance_init  = npcm7xx_init,
7612d8f048cSHavard Skinnemoen         .class_size     = sizeof(NPCM7xxClass),
7622d8f048cSHavard Skinnemoen         .class_init     = npcm7xx_class_init,
7632d8f048cSHavard Skinnemoen         .abstract       = true,
7642d8f048cSHavard Skinnemoen     }, {
7652d8f048cSHavard Skinnemoen         .name           = TYPE_NPCM730,
7662d8f048cSHavard Skinnemoen         .parent         = TYPE_NPCM7XX,
7672d8f048cSHavard Skinnemoen         .class_init     = npcm730_class_init,
7682d8f048cSHavard Skinnemoen     }, {
7692d8f048cSHavard Skinnemoen         .name           = TYPE_NPCM750,
7702d8f048cSHavard Skinnemoen         .parent         = TYPE_NPCM7XX,
7712d8f048cSHavard Skinnemoen         .class_init     = npcm750_class_init,
7722d8f048cSHavard Skinnemoen     },
7732d8f048cSHavard Skinnemoen };
7742d8f048cSHavard Skinnemoen 
7752d8f048cSHavard Skinnemoen DEFINE_TYPES(npcm7xx_soc_types);
776