[PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI)
Andrew Jeffery
andrew at aj.id.au
Tue Jun 7 12:08:46 AEST 2016
On Sun, 2016-05-29 at 23:19 +0200, Cédric Le Goater wrote:
> This patch provides a simple device model for the SMC controllers
> available on the Aspeed AST2400 soc. Support is limited to the FMC and
> the SPI controllers, and to SPI flash slaves.
>
> Below is the initial framework : the sysbus object, MMIO for registers
> configuration and controls. Each controller has a SPI bus and a
> configurable number of CS lines for SPI flash slaves.
>
> Signed-off-by: Cédric Le Goater <clg at kaod.org>
> ---
> hw/arm/ast2400.c | 33 ++++++
> hw/ssi/Makefile.objs | 1 +
> hw/ssi/aspeed_smc.c | 260 ++++++++++++++++++++++++++++++++++++++++++++
> include/hw/arm/ast2400.h | 3 +
> include/hw/ssi/aspeed_smc.h | 68 ++++++++++++
> 5 files changed, 365 insertions(+)
> create mode 100644 hw/ssi/aspeed_smc.c
> create mode 100644 include/hw/ssi/aspeed_smc.h
>
> diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c
> index 23d52bffcaa7..8f678983cee5 100644
> --- a/hw/arm/ast2400.c
> +++ b/hw/arm/ast2400.c
> @@ -23,6 +23,9 @@
> #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
> @@ -77,6 +80,14 @@ static void ast2400_init(Object *obj)
> 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->smc, sizeof(s->smc), TYPE_ASPEED_SMC);
> + 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), TYPE_ASPEED_SMC);
> + 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)
> @@ -171,6 +182,28 @@ static void ast2400_realize(DeviceState *dev, Error **errp)
> /* The palmetto platform expects a ds3231 RTC but a ds1338 is
> * enough to provide basic RTC features. Alarms will be missing */
> i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&s->i2c), 0), "ds1338", 0x68);
> +
> + /* SMC */
> + object_property_set_int(OBJECT(&s->smc), 1, "num-cs", &err);
> + object_property_set_int(OBJECT(&s->spi), AspeedSMCFMC, "smc-type", &err);
> + object_property_set_bool(OBJECT(&s->smc), true, "realized", &err);
> + if (err) {
> + error_propagate(errp, err);
> + return;
> + }
> + sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_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_int(OBJECT(&s->spi), AspeedSMCSPI, "smc-type", &err);
> + object_property_set_bool(OBJECT(&s->spi), true, "realized", &err);
> + if (err) {
> + error_propagate(errp, err);
> + return;
> + }
> + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE);
> }
>
> static void ast2400_class_init(ObjectClass *oc, void *data)
> diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
> index 9555825acad1..6b32bf22ce3b 100644
> --- a/hw/ssi/Makefile.objs
> +++ b/hw/ssi/Makefile.objs
> @@ -2,5 +2,6 @@ common-obj-$(CONFIG_PL022) += pl022.o
> common-obj-$(CONFIG_SSI) += ssi.o
> common-obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
> common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
> +common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
>
> obj-$(CONFIG_OMAP) += omap_spi.o
> diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
> new file mode 100644
> index 000000000000..780fcbbc9e55
> --- /dev/null
> +++ b/hw/ssi/aspeed_smc.c
> @@ -0,0 +1,260 @@
> +/*
> + * ASPEED AST2400 SMC Controller (SPI Flash Only)
> + *
> + * Copyright (C) 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/sysbus.h"
> +#include "sysemu/sysemu.h"
> +#include "qemu/log.h"
> +#include "include/qemu/error-report.h"
> +#include "exec/address-spaces.h"
> +
> +#include "hw/ssi/aspeed_smc.h"
> +
> +/* CE Type Setting Register */
> +#define R_CONF (0x00 / 4)
> +#define CONF_LEGACY_DISABLE (1 << 31)
> +#define CONF_ENABLE_W4 20
> +#define CONF_ENABLE_W3 19
> +#define CONF_ENABLE_W2 18
> +#define CONF_ENABLE_W1 17
> +#define CONF_ENABLE_W0 16
> +#define CONF_FLASH_TYPE4 9
> +#define CONF_FLASH_TYPE3 7
> +#define CONF_FLASH_TYPE2 5
> +#define CONF_FLASH_TYPE1 3
> +#define CONF_FLASH_TYPE0 1
> +
> +/* CE Control Register */
> +#define R_CTRL (0x04 / 4)
> +#define CRTL_EXTENDED4 4 /* 32 bit addressing for SPI */
> +#define CRTL_EXTENDED3 3 /* 32 bit addressing for SPI */
> +#define CRTL_EXTENDED2 2 /* 32 bit addressing for SPI */
> +#define CRTL_EXTENDED1 1 /* 32 bit addressing for SPI */
> +#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */
> +
> +/* Interrupt Control and Status Register */
> +#define R_INTR_CTRL (0x08 / 4)
> +
> +/* CEx Control Register */
> +#define R_CTRL0 (0x10 / 4)
> +#define CTRL_CE_STOP_ACTIVE (1 << 2)
> +#define CTRL_USERMODE 0x3
> +#define R_CTRL1 (0x14 / 4)
> +#define R_CTRL2 (0x18 / 4)
> +#define R_CTRL3 (0x1C / 4)
> +#define R_CTRL4 (0x20 / 4)
> +
> +/* CEx Segment Address Register */
> +#define R_SEG_ADDR0 (0x30 / 4)
> +#define SEG_SIZE_SHIFT 24 /* 8MB units */
> +#define SEG_SIZE_MASK 0x7f
> +#define SEG_START_SHIFT 16 /* address bit [A29-A23] */
> +#define SEG_START_MASK 0x7f
> +#define R_SEG_ADDR1 (0x34 / 4)
> +#define R_SEG_ADDR2 (0x38 / 4)
> +#define R_SEG_ADDR3 (0x3C / 4)
> +#define R_SEG_ADDR4 (0x40 / 4)
> +
> +/* Misc Control Register #1 */
> +#define R_MISC_CRTL1 (0x50 / 4)
> +
> +/* Misc Control Register #2 */
> +#define R_MISC_CRTL2 (0x54 / 4)
> +
> +
> +/* SPI controller registers and bits (that we care about) */
> +#define R_SPI_CONF (0x00 / 4)
> +#define SPI_CONF_ENABLE_W0 0
> +#define R_SPI_CTRL0 (0x4 / 4)
> +#define R_SPI_MISC_CRTL (0x10 / 4)
> +#define R_SPI_TIMINGS (0x14 / 4)
> +
> +static const int aspeed_smc_r_ctrl0[] = { R_CTRL0, R_CTRL0, R_SPI_CTRL0 };
> +static const int aspeed_smc_r_conf[] = { R_CONF, R_CONF, R_SPI_CONF };
> +static const int aspeed_smc_conf_enable_w0[] = {
> + CONF_ENABLE_W0, CONF_ENABLE_W0, SPI_CONF_ENABLE_W0 };
> +
> +
> +static bool aspeed_smc_is_ce_stop_active(AspeedSMCState *s, int cs)
> +{
> + return s->regs[s->r_ctrl0 + cs] & CTRL_CE_STOP_ACTIVE;
> +}
> +
> +static void aspeed_smc_update_cs(AspeedSMCState *s)
> +{
> + int i;
> +
> + for (i = 0; i < s->num_cs; ++i) {
> + if (aspeed_smc_is_ce_stop_active(s, i)) {
> + /* should reset command byte in control reg */
Can you expand on why this hasn't been implemented? I assume there are
no side-effects?
> + }
> +
> + qemu_set_irq(s->cs_lines[i], aspeed_smc_is_ce_stop_active(s, i));
> + }
> +}
> +
> +static void aspeed_smc_reset(DeviceState *d)
> +{
> + AspeedSMCState *s = ASPEED_SMC(d);
> + int i;
> +
> + memset(s->regs, 0, sizeof s->regs);
> +
> + for (i = 0; i < s->num_cs; ++i) {
> + s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
> + }
> +
> + aspeed_smc_update_cs(s);
> +}
> +
> +static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> + AspeedSMCState *s = ASPEED_SMC(opaque);
> + uint32_t r = 0;
> +
> + addr >>= 2;
> + switch (addr) {
> + default:
> + if (addr < ARRAY_SIZE(s->regs)) {
> + r = s->regs[addr];
> + }
> + break;
> + }
Is there a good reason to use the switch here if there's only a default
case?
> +
> + return r;
> +}
> +
> +static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
> + unsigned int size)
> +{
> + AspeedSMCState *s = ASPEED_SMC(opaque);
> + uint32_t value = data;
> +
> + addr >>= 2;
> + switch (addr) {
> + default:
> + if (addr < ARRAY_SIZE(s->regs)) {
> + s->regs[addr] = value;
> + }
> + break;
> + }
Similar to above, though with ranges maybe we could accommodate the
condition below?
> +
> + if (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs) {
> + aspeed_smc_update_cs(s);
> + }
> +}
> +
> +static const MemoryRegionOps aspeed_smc_ops = {
> + .read = aspeed_smc_read,
> + .write = aspeed_smc_write,
> + .endianness = DEVICE_NATIVE_ENDIAN,
I think I asked about this previously - should it be
DEVICE_LITTLE_ENDIAN?
> + .valid.unaligned = true,
> +};
> +
> +static const char * const aspeed_smc_types[] = { "smc", "fmc", "spi" };
> +static const int aspeed_smc_max_slaves[] = { 5, 5, 1 };
> +
> +static int aspeed_smc_init(SysBusDevice *sbd)
> +{
> + DeviceState *dev = DEVICE(sbd);
> + AspeedSMCState *s = ASPEED_SMC(dev);
> + int i;
> + char name[16];
> +
> + s->spi = ssi_create_bus(dev, "spi");
> +
> + /* Enforce some real HW limits */
> + if (s->num_cs > aspeed_smc_max_slaves[s->smc_type]) {
> + s->num_cs = aspeed_smc_max_slaves[s->smc_type];
> + }
> +
> + /* Setup cs_lines for slaves */
> + sysbus_init_irq(sbd, &s->irq);
> + s->cs_lines = g_new0(qemu_irq, s->num_cs);
> + ssi_auto_connect_slaves(dev, s->cs_lines, s->spi);
> +
> + for (i = 0; i < s->num_cs; ++i) {
> + sysbus_init_irq(sbd, &s->cs_lines[i]);
> + }
> +
> + /* There are some differences in the register numbers and bits
> + * between the FMC and SPI controller. Let's abstract these.
> + */
> + s->r_ctrl0 = aspeed_smc_r_ctrl0[s->smc_type];
> + s->r_conf = aspeed_smc_r_conf[s->smc_type];
> + s->conf_enable_w0 = aspeed_smc_conf_enable_w0[s->smc_type];
A little hairy, but understandable in that we avoid two mostly similar
implementations.
> +
> + /* Unselect all slaves */
> + for (i = 0; i < s->num_cs; ++i) {
> + s->regs[s->r_ctrl0 + i] |= CTRL_CE_STOP_ACTIVE;
> + }
> +
> + snprintf(name, sizeof(name), "aspeed.%s", aspeed_smc_types[s->smc_type]);
> + memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s,
> + name, ASPEED_SMC_R_MAX * 4);
> + sysbus_init_mmio(sbd, &s->mmio);
> +
> + return 0;
> +}
> +
> +static const VMStateDescription vmstate_aspeed_smc = {
> + .name = "aspeed.smc",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> +static Property aspeed_smc_properties[] = {
> + DEFINE_PROP_UINT8("num-cs", AspeedSMCState, num_cs, 1),
> + DEFINE_PROP_UINT8("smc-type", AspeedSMCState, smc_type, AspeedSMCFMC),
> + DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void aspeed_smc_class_init(ObjectClass *klass, void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
> +
> + k->init = aspeed_smc_init;
> + dc->reset = aspeed_smc_reset;
> + dc->props = aspeed_smc_properties;
> + dc->vmsd = &vmstate_aspeed_smc;
> +}
> +
> +static const TypeInfo aspeed_smc_info = {
> + .name = TYPE_ASPEED_SMC,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(AspeedSMCState),
> + .class_init = aspeed_smc_class_init,
> +};
> +
> +static void aspeed_smc_register_types(void)
> +{
> + type_register_static(&aspeed_smc_info);
> +}
> +
> +type_init(aspeed_smc_register_types)
> diff --git a/include/hw/arm/ast2400.h b/include/hw/arm/ast2400.h
> index e96e3db3fbea..9ba4245619c1 100644
> --- a/include/hw/arm/ast2400.h
> +++ b/include/hw/arm/ast2400.h
> @@ -17,6 +17,7 @@
> #include "hw/misc/aspeed_scu.h"
> #include "hw/timer/aspeed_timer.h"
> #include "hw/i2c/aspeed_i2c.h"
> +#include "hw/ssi/aspeed_smc.h"
>
> typedef struct AST2400State {
> /*< private >*/
> @@ -30,6 +31,8 @@ typedef struct AST2400State {
> AspeedTimerCtrlState timerctrl;
> AspeedSCUState scu;
> AspeedI2CState i2c;
> + AspeedSMCState smc;
> + AspeedSMCState spi;
> } AST2400State;
>
> #define TYPE_AST2400 "ast2400"
> diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h
> new file mode 100644
> index 000000000000..9b95fcee5da7
> --- /dev/null
> +++ b/include/hw/ssi/aspeed_smc.h
> @@ -0,0 +1,68 @@
> +/*
> + * ASPEED AST2400 SMC Controller (SPI Flash Only)
> + *
> + * Copyright (C) 2016 IBM Corp.
> + *
> + * 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.
> + */
> +
> +#ifndef ASPEED_SMC_H
> +#define ASPEED_SMC_H
> +
> +#include "hw/ssi/ssi.h"
> +
> +enum AspeedSMCType {
> + AspeedSMCLegacy,
> + AspeedSMCFMC,
> + AspeedSMCSPI,
> +};
> +
> +#define TYPE_ASPEED_SMC "aspeed.smc"
> +#define ASPEED_SMC(obj) OBJECT_CHECK(AspeedSMCState, (obj), TYPE_ASPEED_SMC)
> +
> +#define ASPEED_SMC_R_MAX (0x100 / 4)
> +
> +typedef struct AspeedSMCState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion mmio;
> +
> + qemu_irq irq;
> + int irqline;
> +
> + uint8_t num_cs;
> + qemu_irq *cs_lines;
> +
> + SSIBus *spi;
> +
> + uint32_t regs[ASPEED_SMC_R_MAX];
> +
> + uint8_t smc_type;
> +
> + /* depends on the controller type */
> + uint8_t r_conf;
> + uint8_t r_ctrl0;
> + uint8_t conf_enable_w0;
> +} AspeedSMCState;
> +
> +#define TYPE_ASPEED_SPI "aspeed.spi"
> +#define ASPEED_SPI(obj) OBJECT_CHECK(AspeedSPIState, (obj), TYPE_ASPEED_SPI)
> +
> +
> +#endif /* ASPEED_SMC_H */
Otherwise, looks good to me.
Andrew
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part
URL: <http://lists.ozlabs.org/pipermail/openbmc/attachments/20160607/ee5c194d/attachment.sig>
More information about the openbmc
mailing list