Each controller on the ast2400 has a memory range on which it maps its flash module slaves. Each slave is assigned a memory segment for its mapping that can be changed at bootime with the Segment Address Register. This is not supported in the current implementation so we are using the defaults provided by the specs. Each SPI flash slave can then be accessed in two modes: Command and User. When in User mode, accesses to the memory segment of the slaves are translated in SPI transfers. When in Command mode, the HW generates the SPI commands automatically and the memory segment is accessed as if doing a MMIO. Other SPI controllers call that mode linear addressing mode. For this purpose, we are adding below each crontoller an array of structs gathering for each SPI flash module, a segment rank, a MemoryRegion to handle the memory accesses and the associated SPI slave device, which should be a m25p80. Only the User mode is supported for now but we are preparing ground for the Command mode. The framework is sufficient to support Linux. Signed-off-by: Cédric Le Goater <clg@kaod.org> Message-id: 1467138270-32481-8-git-send-email-clg@kaod.org [PMM: Use g_new0() rather than g_malloc0()] Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
217 lines
7.2 KiB
C
217 lines
7.2 KiB
C
/*
|
|
* AST2400 SoC
|
|
*
|
|
* Andrew Jeffery <andrew@aj.id.au>
|
|
* Jeremy Kerr <jk@ozlabs.org>
|
|
*
|
|
* Copyright 2016 IBM Corp.
|
|
*
|
|
* This code is licensed under the GPL version 2 or later. See
|
|
* the COPYING file in the top-level directory.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qapi/error.h"
|
|
#include "qemu-common.h"
|
|
#include "cpu.h"
|
|
#include "exec/address-spaces.h"
|
|
#include "hw/arm/ast2400.h"
|
|
#include "hw/char/serial.h"
|
|
#include "qemu/log.h"
|
|
#include "hw/i2c/aspeed_i2c.h"
|
|
|
|
#define AST2400_UART_5_BASE 0x00184000
|
|
#define AST2400_IOMEM_SIZE 0x00200000
|
|
#define AST2400_IOMEM_BASE 0x1E600000
|
|
#define AST2400_SMC_BASE AST2400_IOMEM_BASE /* Legacy SMC */
|
|
#define AST2400_FMC_BASE 0X1E620000
|
|
#define AST2400_SPI_BASE 0X1E630000
|
|
#define AST2400_VIC_BASE 0x1E6C0000
|
|
#define AST2400_SCU_BASE 0x1E6E2000
|
|
#define AST2400_TIMER_BASE 0x1E782000
|
|
#define AST2400_I2C_BASE 0x1E78A000
|
|
|
|
#define AST2400_FMC_FLASH_BASE 0x20000000
|
|
#define AST2400_SPI_FLASH_BASE 0x30000000
|
|
|
|
#define AST2400_A0_SILICON_REV 0x02000303
|
|
|
|
static const int uart_irqs[] = { 9, 32, 33, 34, 10 };
|
|
static const int timer_irqs[] = { 16, 17, 18, 35, 36, 37, 38, 39, };
|
|
|
|
/*
|
|
* IO handlers: simply catch any reads/writes to IO addresses that aren't
|
|
* handled by a device mapping.
|
|
*/
|
|
|
|
static uint64_t ast2400_io_read(void *p, hwaddr offset, unsigned size)
|
|
{
|
|
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n",
|
|
__func__, offset, size);
|
|
return 0;
|
|
}
|
|
|
|
static void ast2400_io_write(void *opaque, hwaddr offset, uint64_t value,
|
|
unsigned size)
|
|
{
|
|
qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n",
|
|
__func__, offset, value, size);
|
|
}
|
|
|
|
static const MemoryRegionOps ast2400_io_ops = {
|
|
.read = ast2400_io_read,
|
|
.write = ast2400_io_write,
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
};
|
|
|
|
static void ast2400_init(Object *obj)
|
|
{
|
|
AST2400State *s = AST2400(obj);
|
|
|
|
s->cpu = cpu_arm_init("arm926");
|
|
|
|
object_initialize(&s->vic, sizeof(s->vic), TYPE_ASPEED_VIC);
|
|
object_property_add_child(obj, "vic", OBJECT(&s->vic), NULL);
|
|
qdev_set_parent_bus(DEVICE(&s->vic), sysbus_get_default());
|
|
|
|
object_initialize(&s->timerctrl, sizeof(s->timerctrl), TYPE_ASPEED_TIMER);
|
|
object_property_add_child(obj, "timerctrl", OBJECT(&s->timerctrl), NULL);
|
|
qdev_set_parent_bus(DEVICE(&s->timerctrl), sysbus_get_default());
|
|
|
|
object_initialize(&s->i2c, sizeof(s->i2c), TYPE_ASPEED_I2C);
|
|
object_property_add_child(obj, "i2c", OBJECT(&s->i2c), NULL);
|
|
qdev_set_parent_bus(DEVICE(&s->i2c), sysbus_get_default());
|
|
|
|
object_initialize(&s->scu, sizeof(s->scu), TYPE_ASPEED_SCU);
|
|
object_property_add_child(obj, "scu", OBJECT(&s->scu), NULL);
|
|
qdev_set_parent_bus(DEVICE(&s->scu), sysbus_get_default());
|
|
qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev",
|
|
AST2400_A0_SILICON_REV);
|
|
object_property_add_alias(obj, "hw-strap1", OBJECT(&s->scu),
|
|
"hw-strap1", &error_abort);
|
|
object_property_add_alias(obj, "hw-strap2", OBJECT(&s->scu),
|
|
"hw-strap2", &error_abort);
|
|
|
|
object_initialize(&s->smc, sizeof(s->smc), "aspeed.smc.fmc");
|
|
object_property_add_child(obj, "smc", OBJECT(&s->smc), NULL);
|
|
qdev_set_parent_bus(DEVICE(&s->smc), sysbus_get_default());
|
|
|
|
object_initialize(&s->spi, sizeof(s->spi), "aspeed.smc.spi");
|
|
object_property_add_child(obj, "spi", OBJECT(&s->spi), NULL);
|
|
qdev_set_parent_bus(DEVICE(&s->spi), sysbus_get_default());
|
|
}
|
|
|
|
static void ast2400_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
int i;
|
|
AST2400State *s = AST2400(dev);
|
|
Error *err = NULL, *local_err = NULL;
|
|
|
|
/* IO space */
|
|
memory_region_init_io(&s->iomem, NULL, &ast2400_io_ops, NULL,
|
|
"ast2400.io", AST2400_IOMEM_SIZE);
|
|
memory_region_add_subregion_overlap(get_system_memory(), AST2400_IOMEM_BASE,
|
|
&s->iomem, -1);
|
|
|
|
/* VIC */
|
|
object_property_set_bool(OBJECT(&s->vic), true, "realized", &err);
|
|
if (err) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->vic), 0, AST2400_VIC_BASE);
|
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 0,
|
|
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_IRQ));
|
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->vic), 1,
|
|
qdev_get_gpio_in(DEVICE(s->cpu), ARM_CPU_FIQ));
|
|
|
|
/* Timer */
|
|
object_property_set_bool(OBJECT(&s->timerctrl), true, "realized", &err);
|
|
if (err) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->timerctrl), 0, AST2400_TIMER_BASE);
|
|
for (i = 0; i < ARRAY_SIZE(timer_irqs); i++) {
|
|
qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->vic), timer_irqs[i]);
|
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->timerctrl), i, irq);
|
|
}
|
|
|
|
/* SCU */
|
|
object_property_set_bool(OBJECT(&s->scu), true, "realized", &err);
|
|
if (err) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->scu), 0, AST2400_SCU_BASE);
|
|
|
|
/* UART - attach an 8250 to the IO space as our UART5 */
|
|
if (serial_hds[0]) {
|
|
qemu_irq uart5 = qdev_get_gpio_in(DEVICE(&s->vic), uart_irqs[4]);
|
|
serial_mm_init(&s->iomem, AST2400_UART_5_BASE, 2,
|
|
uart5, 38400, serial_hds[0], DEVICE_LITTLE_ENDIAN);
|
|
}
|
|
|
|
/* I2C */
|
|
object_property_set_bool(OBJECT(&s->i2c), true, "realized", &err);
|
|
if (err) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c), 0, AST2400_I2C_BASE);
|
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c), 0,
|
|
qdev_get_gpio_in(DEVICE(&s->vic), 12));
|
|
|
|
/* SMC */
|
|
object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err);
|
|
object_property_set_bool(OBJECT(&s->smc), true, "realized", &local_err);
|
|
error_propagate(&err, local_err);
|
|
if (err) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_BASE);
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 1, AST2400_FMC_FLASH_BASE);
|
|
sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0,
|
|
qdev_get_gpio_in(DEVICE(&s->vic), 19));
|
|
|
|
/* SPI */
|
|
object_property_set_int(OBJECT(&s->spi), 1, "num-cs", &err);
|
|
object_property_set_bool(OBJECT(&s->spi), true, "realized", &local_err);
|
|
error_propagate(&err, local_err);
|
|
if (err) {
|
|
error_propagate(errp, err);
|
|
return;
|
|
}
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE);
|
|
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 1, AST2400_SPI_FLASH_BASE);
|
|
}
|
|
|
|
static void ast2400_class_init(ObjectClass *oc, void *data)
|
|
{
|
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
|
|
|
dc->realize = ast2400_realize;
|
|
|
|
/*
|
|
* Reason: creates an ARM CPU, thus use after free(), see
|
|
* arm_cpu_class_init()
|
|
*/
|
|
dc->cannot_destroy_with_object_finalize_yet = true;
|
|
}
|
|
|
|
static const TypeInfo ast2400_type_info = {
|
|
.name = TYPE_AST2400,
|
|
.parent = TYPE_SYS_BUS_DEVICE,
|
|
.instance_size = sizeof(AST2400State),
|
|
.instance_init = ast2400_init,
|
|
.class_init = ast2400_class_init,
|
|
};
|
|
|
|
static void ast2400_register_types(void)
|
|
{
|
|
type_register_static(&ast2400_type_info);
|
|
}
|
|
|
|
type_init(ast2400_register_types)
|