1602001195
The Aspeed AST2400 integrates a set of 14 I2C/SMBus bus controllers directly connected to the APB bus. They can be programmed as master or slave but the propopsed model only supports the master mode. On the TODO list, we also have : - improve and harden the state machine. - bus recovery support (used by the Linux driver). - transfer mode state machine bits. this is not strictly necessary as it is mostly used for debug. The bus busy bit is deducted from the I2C core engine of qemu. - support of the pool buffer: 2048 bytes of internal SRAM (not used by the Linux driver). Signed-off-by: Cédric Le Goater <clg@kaod.org> Reviewed-by: Andrew Jeffery <andrew@aj.id.au> Message-id: 1464704307-25178-1-git-send-email-clg@kaod.org [PMM: removed unused functions aspeed_i2c_bus_get_state() and aspeed_i2c_bus_set_state()] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
158 lines
4.8 KiB
C
158 lines
4.8 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_VIC_BASE 0x1E6C0000
|
|
#define AST2400_TIMER_BASE 0x1E782000
|
|
#define AST2400_I2C_BASE 0x1E78A000
|
|
|
|
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());
|
|
}
|
|
|
|
static void ast2400_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
int i;
|
|
AST2400State *s = AST2400(dev);
|
|
Error *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);
|
|
}
|
|
|
|
/* 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));
|
|
}
|
|
|
|
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)
|