[PATCH qemu 01/12] ast2400: add SMC controllers (FMC and SPI)

Cédric Le Goater clg at kaod.org
Wed Jun 8 21:53:08 AEST 2016


On 06/07/2016 04:08 AM, Andrew Jeffery wrote:
> 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?

This comes from an initial implementation in which the command in bits [23:16] 
of the control register were used to generate SPI transfers when the flash was 
accessed in command mode. But it was not efficient.

I would like to keep it around for the sake of the model. I will see what
I can do even it is not really useful. 

>> +        }
>> +
>> +        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?

I can not even say laziness :) I will add some cases.

>> +
>> +    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?

yes. some enforcement on which registers can be handled is better.

>> +
>> +    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? 

yes.

>> +    .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.

yeah. Well, that seem the best approach for the rest of the code. See 
below :
	s->regs[s->r_ctrl0 + i] 

I like an array of registers values: 

	s->regs[s->regs_value[CRTL0] + 1] 

or some macro : 

	#define REG_CRTL0(s) aspeed_smc_r_ctrl0[(s)->smc_type];

	s->regs[RET_CRTL0(s) + i]

I am open to any suggestions.

>> +
>> +    /* 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
> 



More information about the openbmc mailing list