[PATCH qemu 15/38] aspeed/smc: handle SPI flash Command mode

Andrew Jeffery andrew at aj.id.au
Fri Nov 25 15:23:40 AEDT 2016


On Fri, 2016-11-18 at 15:21 +0100, Cédric Le Goater wrote:
> The Aspeed SMC controllers have a mode (Command mode) in which
> accesses to the flash content are no different than doing MMIOs. The
> controller generates all the necessary commands to load (or store)
> data in memory.
> 
> However, accesses are restricted to the segment window assigned the
> the flash module by the controller. This window is defined by the
> Segment Address Register.
> 
> Signed-off-by: Cédric Le Goater <clg at kaod.org>

Reviewed-by: Andrew Jeffery <andrew at aj.id.au>

> ---
>  hw/ssi/aspeed_smc.c | 174
> ++++++++++++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 162 insertions(+), 12 deletions(-)
> 
> diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c
> index 72a44150b0a1..eec087199a22 100644
> --- a/hw/ssi/aspeed_smc.c
> +++ b/hw/ssi/aspeed_smc.c
> @@ -69,6 +69,7 @@
>  #define R_CTRL0           (0x10 / 4)
>  #define   CTRL_CMD_SHIFT           16
>  #define   CTRL_CMD_MASK            0xff
> +#define   CTRL_AST2400_SPI_4BYTE   (1 << 13)
>  #define   CTRL_CE_STOP_ACTIVE      (1 << 2)
>  #define   CTRL_CMD_MODE_MASK       0x3
>  #define     CTRL_READMODE          0x0
> @@ -135,6 +136,16 @@
>  #define ASPEED_SOC_SPI_FLASH_BASE   0x30000000
>  #define ASPEED_SOC_SPI2_FLASH_BASE  0x38000000
>  
> +/* Flash opcodes. */
> +#define SPI_OP_READ       0x03    /* Read data bytes (low frequency)
> */
> +#define SPI_OP_WRDI       0x04    /* Write disable */
> +#define SPI_OP_RDSR       0x05    /* Read status register */
> +#define SPI_OP_WREN       0x06    /* Write enable */
> +
> +/* Used for Macronix and Winbond flashes. */
> +#define SPI_OP_EN4B       0xb7    /* Enter 4-byte mode */
> +#define SPI_OP_EX4B       0xe9    /* Exit 4-byte mode */
> +
>  /*
>   * Default segments mapping addresses and size for each slave per
>   * controller. These can be changed when board is initialized with
> the
> @@ -357,6 +368,98 @@ static inline bool aspeed_smc_is_writable(const
> AspeedSMCFlash *fl)
>      return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + fl->id));
>  }
>  
> +static inline int aspeed_smc_flash_cmd(const AspeedSMCFlash *fl)
> +{
> +    AspeedSMCState *s = fl->controller;
> +    int cmd = (s->regs[s->r_ctrl0 + fl->id] >> CTRL_CMD_SHIFT) &
> CTRL_CMD_MASK;
> +
> +    /* This is the default value for read mode. In other modes, the
> +     * command should be defined */
> +    if (aspeed_smc_flash_mode(fl) == CTRL_READMODE) {
> +        cmd = SPI_OP_READ;
> +    }
> +
> +    if (!cmd) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: no command defined for
> mode %d\n",
> +                      __func__, aspeed_smc_flash_mode(fl));
> +    }
> +
> +    return cmd;
> +}
> +
> +static inline int aspeed_smc_flash_is_4byte(const AspeedSMCFlash
> *fl)
> +{
> +    AspeedSMCState *s = fl->controller;
> +
> +    if (s->ctrl->segments == aspeed_segments_spi) {
> +        return s->regs[s->r_ctrl0] & CTRL_AST2400_SPI_4BYTE;
> +    } else {
> +        return s->regs[s->r_ce_ctrl] & (1 << (CTRL_EXTENDED0 + fl-
> >id));
> +    }
> +}
> +
> +static void aspeed_smc_flash_select(const AspeedSMCFlash *fl)
> +{
> +    AspeedSMCState *s = fl->controller;
> +
> +    s->regs[s->r_ctrl0 + fl->id] &= ~CTRL_CE_STOP_ACTIVE;
> +    qemu_set_irq(s->cs_lines[fl->id],
> aspeed_smc_is_ce_stop_active(fl));
> +}
> +
> +static void aspeed_smc_flash_unselect(const AspeedSMCFlash *fl)
> +{
> +    AspeedSMCState *s = fl->controller;
> +
> +    s->regs[s->r_ctrl0 + fl->id] |= CTRL_CE_STOP_ACTIVE;
> +    qemu_set_irq(s->cs_lines[fl->id],
> aspeed_smc_is_ce_stop_active(fl));
> +}
> +
> +static uint32_t aspeed_smc_check_segment_addr(AspeedSMCFlash *fl,
> uint32_t addr)
> +{
> +    AspeedSMCState *s = fl->controller;
> +    AspeedSegments seg;
> +
> +    aspeed_smc_reg_to_segment(s->regs[R_SEG_ADDR0 + fl->id], &seg);
> +    if ((addr & (seg.size - 1)) != addr) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: invalid address 0x%08x for CS%d segment :
> "
> +                      "[ 0x%"HWADDR_PRIx" - 0x%"HWADDR_PRIx" ]\n",
> +                      s->ctrl->name, addr, fl->id, seg.addr,
> +                      seg.addr + seg.size);
> +    }
> +
> +    addr &= seg.size - 1;
> +    return addr;
> +}
> +
> +static void aspeed_smc_flash_setup_read(AspeedSMCFlash *fl, uint32_t
> addr)
> +{
> +    AspeedSMCState *s = fl->controller;
> +    uint8_t cmd = aspeed_smc_flash_cmd(fl);
> +
> +    /*
> +     * To be checked: I am not sure the Aspeed SPI controller needs
> to
> +     * enable writes when running in READ/FREAD command mode
> +     */
> +
> +    /* access can not exceed CS segment */
> +    addr = aspeed_smc_check_segment_addr(fl, addr);
> +
> +    /* TODO: do we have to send 4BYTE each time ? */
> +    if (aspeed_smc_flash_is_4byte(fl)) {
> +        ssi_transfer(s->spi, SPI_OP_EN4B);
> +    }
> +
> +    ssi_transfer(s->spi, cmd);
> +
> +    if (aspeed_smc_flash_is_4byte(fl)) {
> +        ssi_transfer(s->spi, (addr >> 24) & 0xff);
> +    }
> +    ssi_transfer(s->spi, (addr >> 16) & 0xff);
> +    ssi_transfer(s->spi, (addr >> 8) & 0xff);
> +    ssi_transfer(s->spi, (addr & 0xff));
> +}
> +
>  static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr,
> unsigned size)
>  {
>      AspeedSMCFlash *fl = opaque;
> @@ -364,19 +467,55 @@ static uint64_t aspeed_smc_flash_read(void
> *opaque, hwaddr addr, unsigned size)
>      uint64_t ret = 0;
>      int i;
>  
> -    if (aspeed_smc_is_usermode(fl)) {
> +    switch (aspeed_smc_flash_mode(fl)) {
> +    case CTRL_USERMODE:
>          for (i = 0; i < size; i++) {
>              ret |= ssi_transfer(s->spi, 0x0) << (8 * i);
>          }
> -    } else {
> -        qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n",
> -                      __func__);
> -        ret = -1;
> +        break;
> +    case CTRL_READMODE:
> +    case CTRL_FREADMODE:
> +        aspeed_smc_flash_select(fl);
> +        aspeed_smc_flash_setup_read(fl, addr);
> +
> +        for (i = 0; i < size; i++) {
> +            ret |= ssi_transfer(s->spi, 0x0) << (8 * i);
> +        }
> +
> +        aspeed_smc_flash_unselect(fl);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid flash mode
> %d\n",
> +                      __func__, aspeed_smc_flash_mode(fl));
>      }
>  
>      return ret;
>  }
>  
> +static void aspeed_smc_flash_setup_write(AspeedSMCFlash *fl,
> uint32_t addr)
> +{
> +    AspeedSMCState *s = fl->controller;
> +    uint8_t cmd = aspeed_smc_flash_cmd(fl);
> +
> +    /* Flash access can not exceed CS segment */
> +    addr = aspeed_smc_check_segment_addr(fl, addr);
> +
> +    /* TODO: do we have to send 4BYTE each time ? */
> +    if (aspeed_smc_flash_is_4byte(fl)) {
> +        ssi_transfer(s->spi, SPI_OP_EN4B);
> +    }
> +
> +    ssi_transfer(s->spi, SPI_OP_WREN);
> +    ssi_transfer(s->spi, cmd);
> +
> +    if (aspeed_smc_flash_is_4byte(fl)) {
> +        ssi_transfer(s->spi, (addr >> 24) & 0xff);
> +    }
> +    ssi_transfer(s->spi, (addr >> 16) & 0xff);
> +    ssi_transfer(s->spi, (addr >> 8) & 0xff);
> +    ssi_transfer(s->spi, (addr & 0xff));
> +}
> +
>  static void aspeed_smc_flash_write(void *opaque, hwaddr addr,
> uint64_t data,
>                             unsigned size)
>  {
> @@ -390,14 +529,25 @@ static void aspeed_smc_flash_write(void
> *opaque, hwaddr addr, uint64_t data,
>          return;
>      }
>  
> -    if (!aspeed_smc_is_usermode(fl)) {
> -        qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n",
> -                      __func__);
> -        return;
> -    }
> +    switch (aspeed_smc_flash_mode(fl)) {
> +    case CTRL_USERMODE:
> +        for (i = 0; i < size; i++) {
> +            ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
> +        }
> +        break;
> +    case CTRL_WRITEMODE:
> +        aspeed_smc_flash_select(fl);
> +        aspeed_smc_flash_setup_write(fl, addr);
> +
> +        for (i = 0; i < size; i++) {
> +            ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
> +        }
>  
> -    for (i = 0; i < size; i++) {
> -        ssi_transfer(s->spi, (data >> (8 * i)) & 0xff);
> +        aspeed_smc_flash_unselect(fl);
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid flash mode
> %d\n",
> +                      __func__, aspeed_smc_flash_mode(fl));
>      }
>  }
>  
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: This is a digitally signed message part
URL: <http://lists.ozlabs.org/pipermail/openbmc/attachments/20161125/b1854081/attachment.sig>


More information about the openbmc mailing list