qemu-arm
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-arm] [Qemu-devel] [PATCH for-2.9 17/30] aspeed/smc: handle SPI


From: Cédric Le Goater
Subject: Re: [Qemu-arm] [Qemu-devel] [PATCH for-2.9 17/30] aspeed/smc: handle SPI flash Command mode
Date: Mon, 2 Jan 2017 19:02:42 +0100
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.5.1

On 01/02/2017 06:33 PM, mar.krzeminski wrote:
> Hello Cedric,
> 
> W dniu 02.01.2017 o 16:56, Cédric Le Goater pisze:
>> Hello Marcin,
>>
>> On 12/05/2016 04:33 PM, mar.krzeminski wrote:
>>>
>>> W dniu 05.12.2016 o 15:07, Cédric Le Goater pisze:
>>>> On 12/04/2016 05:31 PM, mar.krzeminski wrote:
>>>>> Hi Cedric,
>>>>>
>>>>> Since there is no public datasheet user guide for SMC I would ask some 
>>>>> question
>>>>> regarding HW itself because I got impression that you are implementing in 
>>>>> this
>>>>> model a part functionality that is done by Bootrom.
>>>>>
>>>>> W dniu 29.11.2016 o 16:43, Cédric Le Goater pisze:
>>>>>> 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 <address@hidden>
>>>>>> Reviewed-by: Andrew Jeffery <address@hidden>
>>>>>> ---
>>>>>>   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 */
>>>>> Are you sure that the controller is aware af all above commands 
>>>>> (especially WD/WE and RDS)?
>>>> HW is aware of SPI_OP_READ which is the default command for the
>>>> "normal" read mode. For other modes, fast and write, the command
>>>> op is configured in the CEx Control Register.
>>>>
>>>> These ops are used in the model :
>>>>
>>>>   * SPI_OP_READ_FAST, for dummies
>>>>   * SPI_OP_EN4B, might be useless if we expect software to send
>>>>     this command before using this mode.
>>>>   * SPI_OP_WREN, same comment.
>>>>
>>>> The rest I should remove as it is unused.
>>> I think only SPI_OP_READ should stay in the model, rest goes to guest.
>> Well, we will need at least one 'EN4B' command to be sent for the qemu
>> flash model to work. If the underlying m25p80 object does not know
>> about the address width, the expected number of bytes will be wrong and
>> the result bogus.
> Hmm, most of the flash I know by default use 3byte address mode.
> What flash are you connecting to model.

chips like n25q256a, mx25l25635e, w25q256. all are > 32MB.

> Do you have same on HW?

No but it is difficult to know what the controller is doing 
in that mode without spying on the bus. 

Anyhow, after some experiments, I think you are right and 
I should  get rid of these command OP in the next version. 

What about the dummy cycles ? the linux driver now has 
support for it and it would be nice to get some support
in qemu also.

Thanks,

C.  

>>
>> Same remark for the WREN, if the m25p80 object is not write enabled,
>> modifying the flash content won't work.
> Same as above.
> 
> Thanks,
> Marcin
>>
>> Thanks,
>>
>> C.
>>
>>>>>> +
>>>>>> +/* Used for Macronix and Winbond flashes. */
>>>>>> +#define SPI_OP_EN4B       0xb7    /* Enter 4-byte mode */
>>>>>> +#define SPI_OP_EX4B       0xe9    /* Exit 4-byte mode */
>>>>>> +
>>>>> Same as above but I think 4byte address mode bit changes onlu nymber of 
>>>>> bytes
>>>>> that is sent after instruction.
>>>>>
>>>>>>   /*
>>>>>>    * 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) {
>>>>> cmd == 0 => NOP command for some flash (eg. mx66l1g45g).
>>>> So I should use another default value, OxFF ?
>>> I believe this check is not needed at all because m25p80c will tell if it 
>>> has
>>> unsupported instruction and HW should accept all register values, isn't it?
>>>>>> +        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 ? */
>>>>> I am not aware of any flash that needs that, this  command should be send 
>>>>> only once.
>>>> yes. That is what I think also.
>>>>
>>>> it also means that a preliminary 4BYTE command should be
>>>> sent before using that mode.
>>> Generally there are two ways to access more than 16MiB in flash:
>>> - 4byte address mode: all commands change to accept 4byte address, not 
>>> supported by all flash devices.
>>> - 4byte opcodes: different opcode is used to signal 4-byte long address 
>>> (eg. 0x3 three bytes, 0x13 four).
>>> Also not all flash support that. If the HW does not use any of those, ones 
>>> should be removed from this model.
>>>
>>>>> I also think that HW does not send this command (see above comment).
>>>>>
>>>>>> +    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:
>>>>> CTRL_FREADMODE should not sent dummy bytes?
>>>> yes it should. this is in a following patch.
>>> Yes, I noticed that after sending this email :)
>>>
>>> Thanks,
>>> Marcin
>>>> Thanks,
>>>>
>>>> C.
>>>>
>>>>> Thanks,
>>>>> Marcin
>>>>>> +        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));
>>>>>>       }
>>>>>>   }
>>>>>>   
>>
> 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]