qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Cédric Le Goater" <clg@kaod.org>
To: Chalapathi V <chalapathi.v@linux.ibm.com>, qemu-devel@nongnu.org
Cc: qemu-ppc@nongnu.org, fbarrat@linux.ibm.com, npiggin@gmail.com,
	calebs@us.ibm.com, chalapathi.v@ibm.com,
	saif.abrar@linux.vnet.ibm.com, dantan@us.ibm.com
Subject: Re: [PATCH v2 3/6] hw/ppc: SPI controller model - sequencer and shifter
Date: Tue, 16 Apr 2024 11:39:20 +0200	[thread overview]
Message-ID: <d41c21ae-cf32-415d-94bb-694c8e47d0f4@kaod.org> (raw)
In-Reply-To: <20240409175700.27535-4-chalapathi.v@linux.ibm.com>

Hello,

Please rephrase the subject to something like:

   "ppc/pnv: Extend SPI model ..."

Using a verb is preferable.

On 4/9/24 19:56, Chalapathi V wrote:
> In this commit SPI shift engine and sequencer logic is implemented.
> Shift engine performs serialization and de-serialization according to the
> control by the sequencer and according to the setup defined in the
> configuration registers. Sequencer implements the main control logic and
> FSM to handle data transmit and data receive control of the shift engine.
>
> Signed-off-by: Chalapathi V <chalapathi.v@linux.ibm.com>
> ---
>   include/hw/ppc/pnv_spi_controller.h |   72 ++
>   hw/ppc/pnv_spi_controller.c         | 1311 ++++++++++++++++++++++++++-
>   2 files changed, 1382 insertions(+), 1 deletion(-)
> 
> diff --git a/include/hw/ppc/pnv_spi_controller.h b/include/hw/ppc/pnv_spi_controller.h
> index 5ec50fb14c..ee8e7a17da 100644
> --- a/include/hw/ppc/pnv_spi_controller.h
> +++ b/include/hw/ppc/pnv_spi_controller.h
> @@ -8,6 +8,14 @@
>    * This model Supports a connection to a single SPI responder.
>    * Introduced for P10 to provide access to SPI seeproms, TPM, flash device
>    * and an ADC controller.
> + *
> + * All SPI function control is mapped into the SPI register space to enable
> + * full control by firmware.
> + *
> + * SPI Controller has sequencer and shift engine. The SPI shift engine
> + * performs serialization and de-serialization according to the control by
> + * the sequencer and according to the setup defined in the configuration
> + * registers and the SPI sequencer implements the main control logic.
>    */
>   #include "hw/ssi/ssi.h"
>   
> @@ -21,6 +29,7 @@
>   #define SPI_CONTROLLER_REG_SIZE 8
>   
>   typedef struct SSIBus SSIBus;
> +typedef struct xfer_buffer xfer_buffer;

Please use CamelCase names for typedef. The forward declaration doesn't
seem useful.

>   #define TYPE_PNV_SPI_BUS "pnv-spi-bus"
>   OBJECT_DECLARE_SIMPLE_TYPE(PnvSPIBus, PNV_SPI_BUS)
> @@ -33,6 +42,21 @@ typedef struct PnvSPIBus {
>       uint32_t id;
>   } PnvSPIBus;
>   
> +/* xfer_buffer */
> +typedef struct xfer_buffer {
> +
> +    uint32_t    len;
> +    uint8_t    *data;
> +
> +} xfer_buffer;
> +
> +uint8_t *xfer_buffer_write_ptr(xfer_buffer *payload, uint32_t offset,
> +                uint32_t length);
> +void xfer_buffer_read_ptr(xfer_buffer *payload, uint8_t **read_buf,
> +                uint32_t offset, uint32_t length);
> +xfer_buffer *xfer_buffer_new(void);
> +void xfer_buffer_free(xfer_buffer *payload);
> +

I don't think these helper routines need to be defined in the header file
of the PnvPsi model. They look internal to me.

>   typedef struct PnvSpiController {
>       DeviceState parent;
>   
> @@ -40,6 +64,39 @@ typedef struct PnvSpiController {
>       MemoryRegion    xscom_spic_regs;
>       /* SPI controller object number */
>       uint32_t        spic_num;
> +    uint8_t         responder_select;
> +    /* To verify if shift_n1 happens prior to shift_n2 */
> +    bool            shift_n1_done;
> +    /*
> +     * Internal flags for the first and last indicators for the SPI
> +     * interface methods
> +     */
> +    uint8_t         first;
> +    uint8_t         last;
> +    /* Loop counter for branch operation opcode Ex/Fx */
> +    uint8_t         loop_counter_1;
> +    uint8_t         loop_counter_2;
> +    /* N1/N2_bits specifies the size of the N1/N2 segment of a frame in bits.*/
> +    uint8_t         N1_bits;
> +    uint8_t         N2_bits;
> +    /* Number of bytes in a payload for the N1/N2 frame segment.*/
> +    uint8_t         N1_bytes;
> +    uint8_t         N2_bytes;
> +    /* Number of N1/N2 bytes marked for transmit */
> +    uint8_t         N1_tx;
> +    uint8_t         N2_tx;
> +    /* Number of N1/N2 bytes marked for receive */
> +    uint8_t         N1_rx;
> +    uint8_t         N2_rx;
> +    /*
> +     * Setting this attribute to true will cause the engine to reverse the
> +     * bit order of each byte it appends to a payload before sending the
> +     * payload to a device. There may be cases where an end device expects
> +     * a reversed order, like in the case of the Nuvoton TPM device. The
> +     * order of bytes in the payload is not reversed, only the order of the
> +     * 8 bits in each payload byte.
> +     */
> +    bool            reverse_bits;
>   
>       /* SPI Controller registers */
>       uint64_t        error_reg;
> @@ -52,4 +109,19 @@ typedef struct PnvSpiController {
>       uint8_t         sequencer_operation_reg[SPI_CONTROLLER_REG_SIZE];
>       uint64_t        status_reg;
>   } PnvSpiController;
> +
> +void log_all_N_counts(PnvSpiController *spi_controller);
> +void spi_response(PnvSpiController *spi_controller, int bits,
> +                xfer_buffer *rsp_payload);
> +void operation_sequencer(PnvSpiController *spi_controller);
> +bool operation_shiftn1(PnvSpiController *spi_controller, uint8_t opcode,
> +                       xfer_buffer **payload, bool send_n1_alone);
> +bool operation_shiftn2(PnvSpiController *spi_controller, uint8_t opcode,
> +                       xfer_buffer **payload);
> +bool does_rdr_match(PnvSpiController *spi_controller);
> +uint8_t get_from_offset(PnvSpiController *spi_controller, uint8_t offset);
> +void shift_byte_in(PnvSpiController *spi_controller, uint8_t byte);
> +void calculate_N1(PnvSpiController *spi_controller, uint8_t opcode);
> +void calculate_N2(PnvSpiController *spi_controller, uint8_t opcode);
> +uint8_t reverse_bits8(uint8_t x);

These routines are internal and belong to hw/ppc/pnv_spi_controller.c. Please
don't export them. Also, adding a pnv_psi_ prefix would be a plus to identify
the sympbols.


>   #endif /* PPC_PNV_SPI_CONTROLLER_H */
> diff --git a/hw/ppc/pnv_spi_controller.c b/hw/ppc/pnv_spi_controller.c
> index e2478a47f2..afe7f17565 100644
> --- a/hw/ppc/pnv_spi_controller.c
> +++ b/hw/ppc/pnv_spi_controller.c
> @@ -52,6 +52,11 @@ static uint64_t pnv_spi_controller_read(void *opaque, hwaddr addr,
>                              val));
>           sc->status_reg = SETFIELD(STATUS_REG_RDR_FULL, sc->status_reg, 0);
>           SPI_DEBUG(qemu_log("RDR being read, RDR_full set to 0\n"));
> +        if (GETFIELD(STATUS_REG_SHIFTER_FSM, sc->status_reg) == FSM_WAIT) {
> +            SPI_DEBUG(qemu_log("RDR being read while shifter is waiting, "
> +                           "starting sequencer\n"));

Please use trace events instead.

> +            operation_sequencer(sc);
> +        }
>           break;
>       case SEQUENCER_OPERATION_REG:
>           val = 0;
> @@ -124,13 +129,15 @@ static void pnv_spi_controller_write(void *opaque, hwaddr addr,
>           sc->status_reg = SETFIELD(STATUS_REG_TDR_UNDERRUN, sc->status_reg, 0);
>           SPI_DEBUG(qemu_log("TDR being written, TDR_underrun set to 0\n"));
>           SPI_DEBUG(qemu_log("TDR being written, starting sequencer\n"));
> +        operation_sequencer(sc);
> +
>           break;
>       case RECEIVE_DATA_REG:
>           sc->receive_data_reg = val;
>           break;
>       case SEQUENCER_OPERATION_REG:
>           for (int i = 0; i < SPI_CONTROLLER_REG_SIZE; i++) {
> -	    sc->sequencer_operation_reg[i] = (val >> (56 - i * 8)) & 0xFF;
> +            sc->sequencer_operation_reg[i] = (val >> (56 - i * 8)) & 0xFF;

This change belongs to the previous patch.

>           }
>           break;
>       case STATUS_REG:
> @@ -157,6 +164,1306 @@ static const MemoryRegionOps pnv_spi_controller_xscom_ops = {
>       .endianness = DEVICE_BIG_ENDIAN,
>   };
>   
> +/* xfer_buffer_methods */
> +xfer_buffer *xfer_buffer_new(void)
> +{
> +    xfer_buffer *payload = g_malloc0(sizeof(*payload));
> +
> +    payload->data = g_malloc0(payload->len);

euh. payload->len is 0. This allocation is pointless.

> +    return payload;
> +}
> +
> +void xfer_buffer_free(xfer_buffer *payload)
> +{
> +    free(payload->data);
> +    free(payload);
> +}
> +
> +uint8_t *xfer_buffer_write_ptr(xfer_buffer *payload, uint32_t offset,
> +                            uint32_t length)
> +{
> +    if (payload->len < (offset + length)) {
> +        payload->len = offset + length;
> +        payload->data = g_realloc(payload->data, payload->len);
> +    }
> +    return &payload->data[offset];
> +}
> +
> +void xfer_buffer_read_ptr(xfer_buffer *payload, uint8_t **read_buf,
> +                uint32_t offset, uint32_t length)
> +{
> +    static uint32_t prev_len;
> +    if ((prev_len != length) || (*read_buf == NULL)) {
> +        *read_buf = g_realloc(*read_buf, length);
> +        prev_len = length;
> +    }
> +    if (offset > payload->len) {
> +        /* Reading outside payload, just return */
> +        return;
> +    }
> +    *read_buf = &payload->data[offset];
> +}

Isn't there a maximum buffer size ? It would simplify the implementation
and avoid all these alloc/realloc/free calls.

> +uint8_t reverse_bits8(uint8_t x)
> +{
> +    x = (x << 4) | (x >> 4);
> +    x = ((x & 0x33) << 2) | ((x & 0xcc) >> 2);
> +    x = ((x & 0x55) << 1) | ((x & 0xaa) >> 1);
> +    return x;
> +}

revbit8() in qemu/host-utils.h should do the same.

The rest is difficult to comment on without an intimitate knowledge of
the controller and the datasheet. Can teammates help ?

Thanks,

C.



> +bool does_rdr_match(PnvSpiController *sc)
> +{
> +    /*
> +     * According to spec, the mask bits that are 0 are compared and the
> +     * bits that are 1 are ignored.
> +     */
> +    uint16_t rdr_match_mask = GETFIELD(MEMORY_MAPPING_REG_RDR_MATCH_MASK,
> +                                        sc->memory_mapping_reg);
> +    uint16_t rdr_match_val = GETFIELD(MEMORY_MAPPING_REG_RDR_MATCH_VAL,
> +                                        sc->memory_mapping_reg);
> +    if ((~rdr_match_mask & rdr_match_val) == ((~rdr_match_mask) &
> +            GETFIELD(PPC_BITMASK(48, 63), sc->receive_data_reg))) {
> +        SPI_DEBUG(qemu_log("RDR match successful, match=0x%4.4x, "
> +                                 "mask=0x%4.4x, RDR[48:63]=0x%4.4llx\n",
> +                           rdr_match_val, rdr_match_mask,
> +                                  GETFIELD(PPC_BITMASK(48, 63),
> +                                   sc->receive_data_reg)));
> +        return true;
> +    } else {
> +        SPI_DEBUG(qemu_log("RDR match failed, match=0x%4.4x, mask=0x%4.4x, "
> +              "RDR[48:63]=0x%4.4llx\n", rdr_match_val, rdr_match_mask,
> +               GETFIELD(PPC_BITMASK(48, 63), sc->receive_data_reg)));
> +        return false;
> +    }
> +}
> +
> +uint8_t get_from_offset(PnvSpiController *sc, uint8_t offset)
> +{
> +    uint8_t byte;
> +
> +    /*
> +     * Offset is an index between 0 and SPI_CONTROLLER_REG_SIZE - 1
> +     * Check the offset before using it.
> +     */
> +    if (offset < SPI_CONTROLLER_REG_SIZE) {
> +        byte = (sc->transmit_data_reg >> (56 - offset * 8)) & 0xFF;
> +    } else {
> +        /*
> +         * Log an error and return a 0xFF since we have to assign something
> +         * to byte before returning.
> +         */
> +        qemu_log_mask(LOG_GUEST_ERROR, "Invalid offset = %d used to get byte "
> +                      "from TDR\n", offset);
> +        byte = 0xff;
> +    }
> +    return byte;
> +}
> +
> +void shift_byte_in(PnvSpiController *sc, uint8_t byte)
> +{
> +    sc->receive_data_reg = (sc->receive_data_reg << 8) | byte;
> +    SPI_DEBUG(qemu_log("0x%2.2x shifted in, RDR now = 0x%16.16lx\n", byte,
> +                    sc->receive_data_reg));
> +}
> +
> +void spi_response(PnvSpiController *sc, int bits, xfer_buffer *rsp_payload)
> +{
> +    uint8_t *read_buf = NULL;
> +    uint8_t ecc_count;
> +    uint8_t shift_in_count;
> +
> +    /*
> +     * Processing here must handle:
> +     * - Which bytes in the payload we should move to the RDR
> +     * - Explicit mode counter configuration settings
> +     * - RDR full and RDR overrun status
> +     */
> +
> +    /*
> +     * First check that the response payload is the exact same
> +     * number of bytes as the request payload was
> +     */
> +    if (rsp_payload->len != (sc->N1_bytes + sc->N2_bytes)) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "Invalid response payload size in "
> +                       "bytes, expected %d, got %d\n",
> +                       (sc->N1_bytes + sc->N2_bytes), rsp_payload->len);
> +    } else {
> +        uint8_t ecc_control;
> +        SPI_DEBUG(qemu_log("SPI response received, payload len = %d\n",
> +                       rsp_payload->len));
> +        log_all_N_counts(sc);
> +        /*
> +         * Adding an ECC count let's us know when we have found a payload byte
> +         * that was shifted in but cannot be loaded into RDR.  Bits 29-30
> +         * equal to either 0b00 or 0b10 indicate that we are taking in data
> +         * with ECC and either applying the ECC or discarding it.
> +         */
> +        ecc_count = 0;
> +        ecc_control = GETFIELD(PPC_BITMASK(29, 30),
> +                                    sc->clock_config_reset_control);
> +        if (ecc_control == 0 || ecc_control == 2) {
> +            ecc_count = 1;
> +        }
> +        /*
> +         * Use the N1_rx and N2_rx counts to control shifting data from the
> +         * payload into the RDR.  Keep an overall count of the number of bytes
> +         * shifted into RDR so we can discard every 9th byte when ECC is
> +         * enabled.
> +         */
> +        shift_in_count = 0;
> +        /* Handle the N1 portion of the frame first */
> +        if (sc->N1_rx != 0) {
> +            uint8_t n1_count = 0;
> +            while (n1_count < sc->N1_bytes) {
> +                shift_in_count++;
> +                xfer_buffer_read_ptr(rsp_payload, &read_buf, n1_count, 1);
> +                if ((ecc_count != 0) &&
> +                    (shift_in_count == (SPI_CONTROLLER_REG_SIZE + ecc_count))) {
> +                    SPI_DEBUG(qemu_log("Discarding rx N1 ECC byte = 0x%2.2x at "
> +                           "payload index = %d\n", read_buf[0], n1_count));
> +                    shift_in_count = 0;
> +                } else {
> +                    uint8_t n1_byte = 0x00;
> +                    n1_byte = read_buf[0];
> +                    SPI_DEBUG(qemu_log("Extracting rx n1_byte = 0x%2.2x from "
> +                            "payload at index = %d\n", n1_byte, n1_count));
> +                    if (sc->reverse_bits) {
> +                        SPI_DEBUG(qemu_log("Reversing bit order of rx "
> +                                            "n1_byte\n"));
> +                        n1_byte = reverse_bits8(n1_byte);
> +                    }
> +                    SPI_DEBUG(qemu_log("Shifting rx N1 byte = 0x%2.2x into "
> +                                   "RDR\n", n1_byte));
> +                    shift_byte_in(sc, n1_byte);
> +                }
> +                n1_count++;
> +            } /* end of while */
> +        }
> +        /* Handle the N2 portion of the frame */
> +        if (sc->N2_rx != 0) {
> +            uint8_t n2_count = 0;
> +            while (n2_count < sc->N2_bytes) {
> +                shift_in_count++;
> +                xfer_buffer_read_ptr(rsp_payload, &read_buf,
> +                                (sc->N1_bytes + n2_count), 1);
> +                if ((ecc_count != 0) &&
> +                    (shift_in_count == (SPI_CONTROLLER_REG_SIZE + ecc_count))) {
> +                    SPI_DEBUG(qemu_log("Discarding rx N1 ECC byte = 0x%2.2x at "
> +                           "payload index = %d\n", read_buf[0],
> +                           (sc->N1_bytes + n2_count)));
> +                    shift_in_count = 0;
> +                } else {
> +                    /*
> +                     * The code handles shifting data from the payload received
> +                     * from the responder into the responder's RDR.  Since this
> +                     * is an N2 frame segment it is safe to assume that there
> +                     * was a preceding N1 segment which was combined with an N2
> +                     * segment to create a single frame. The response data will
> +                     * then have N1_bytes of data in the payload representing a
> +                     * responder response to the N1 section of the frame. If N2
> +                     * is set to receive the shifting for N2 data begins after
> +                     * the N1 bytes regardless of whether or not N1 was marked
> +                     * for transmit or receive.
> +                     */
> +                    uint8_t n2_byte = 0x00;
> +                    n2_byte = read_buf[0];
> +                    SPI_DEBUG(qemu_log("Extracting rx n2_byte = 0x%2.2x from "
> +                            "payload at index = %d\n", n2_byte,
> +                            (sc->N1_bytes + n2_count)));
> +                    if (sc->reverse_bits) {
> +                        SPI_DEBUG(qemu_log("Reversing bit order of rx "
> +                                            "n2_byte\n"));
> +                        n2_byte = reverse_bits8(n2_byte);
> +                    }
> +                    SPI_DEBUG(qemu_log("Shifting rx N2 byte = 0x%2.2x into "
> +                              "RDR\n", n2_byte));
> +                    shift_byte_in(sc, n2_byte);
> +                }
> +                n2_count++;
> +            }
> +        }
> +        if ((sc->N1_rx + sc->N2_rx) > 0) {
> +            /*
> +             * Data was received so handle RDR status.
> +             * It is easier to handle RDR_full and RDR_overrun status here
> +             * since the RDR register's shift_byte_in method is called
> +             * multiple times in a row. Controlling RDR status is done here
> +             * instead of in the RDR scoped methods for that reason.
> +             */
> +            if (GETFIELD(STATUS_REG_RDR_FULL, sc->status_reg) == 1) {
> +                /*
> +                 * Data was shifted into the RDR before having been read
> +                 * causing previous data to have been overrun.
> +                 */
> +                sc->status_reg = SETFIELD(STATUS_REG_RDR_OVERRUN,
> +                                sc->status_reg, 1);
> +            } else {
> +                /*
> +                 * Set status to indicate that the received data register is
> +                 * full. This flag is only cleared once the RDR is unloaded.
> +                 */
> +                sc->status_reg = SETFIELD(STATUS_REG_RDR_FULL,
> +                                sc->status_reg, 1);
> +                SPI_DEBUG(qemu_log("RDR_full set to 1\n"));
> +            }
> +        }
> +    } /* end of else */
> +} /* end of spi_response() */
> +
> +void log_all_N_counts(PnvSpiController *sc)
> +{
> +    SPI_DEBUG(qemu_log("N1_bits = %d, N1_bytes = %d, N1_tx = %d, N1_rx = %d, "
> +          "N2_bits = %d, N2_bytes = %d, N2_tx = %d, N2_rx = %d\n",
> +           sc->N1_bits, sc->N1_bytes, sc->N1_tx, sc->N1_rx, sc->N2_bits,
> +           sc->N2_bytes, sc->N2_tx, sc->N2_rx));
> +}
> +
> +static inline void next_sequencer_fsm(PnvSpiController *sc)
> +{
> +    uint8_t seq_index = GETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg);
> +    sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg,
> +                              (seq_index + 1));
> +    sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM, sc->status_reg,
> +                                      SEQ_STATE_INDEX_INCREMENT);
> +}
> +
> +void operation_sequencer(PnvSpiController *sc)
> +{
> +    /*
> +     * Loop through each sequencer operation ID and perform the requested
> +     *  operations.
> +     * Flag for indicating if we should send the N1 frame or wait to combine
> +     * it with a preceding N2 frame.
> +     */
> +    bool send_n1_alone = true;
> +    bool stop = false; /* Flag to stop the sequencer */
> +    uint8_t opcode = 0;
> +    uint8_t masked_opcode = 0;
> +
> +    /*
> +     * xfer_buffer for containing the payload of the SPI frame.
> +     * This is a static because there are cases where a sequence has to stop
> +     * and wait for the target application to unload the RDR.  If this occurs
> +     * during a sequence where N1 is not sent alone and instead combined with
> +     * N2 since the N1 tx length + the N2 tx length is less than the size of
> +     * the TDR.
> +     */
> +    static xfer_buffer *payload;
> +
> +    if (payload == NULL) {
> +        payload = xfer_buffer_new();
> +    }
> +    /*
> +     * Clear the sequencer FSM error bit - general_SPI_status[3]
> +     * before starting a sequence.
> +     */
> +    sc->status_reg = SETFIELD(PPC_BIT(35), sc->status_reg, 0);
> +    /*
> +     * If the FSM is idle set the sequencer index to 0
> +     * (new/restarted sequence)
> +     */
> +    if (GETFIELD(STATUS_REG_SEQUENCER_FSM, sc->status_reg) ==
> +                    SEQ_STATE_IDLE) {
> +        sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                  sc->status_reg, 0);
> +    }
> +    /*
> +     * There are only 8 possible operation IDs to iterate through though
> +     * some operations may cause more than one frame to be sequenced.
> +     */
> +    while (GETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg) < 8) {
> +        opcode = sc->sequencer_operation_reg[GETFIELD(
> +                        STATUS_REG_SEQUENCER_INDEX, sc->status_reg)];
> +        /* Set sequencer state to decode */
> +        sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM, sc->status_reg,
> +                                  SEQ_STATE_DECODE);
> +        /*
> +         * Only the upper nibble of the operation ID is needed to know what
> +         * kind of operation is requested.
> +         */
> +        masked_opcode = opcode & 0xF0;
> +        switch (masked_opcode) {
> +        /*
> +         * Increment the operation index in each case instead of just
> +         * once at the end in case an operation like the branch
> +         * operation needs to change the index.
> +         */
> +        case SEQ_OP_STOP:
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_EXECUTE);
> +            /* A stop operation in any position stops the sequencer */
> +            SPI_DEBUG(qemu_log("Sequencer STOP at index = 0x%llx, sequencer "
> +                               "idling\n", GETFIELD(
> +                               STATUS_REG_SEQUENCER_INDEX,
> +                               sc->status_reg)));
> +            stop = true;
> +            sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM, sc->status_reg,
> +                                          FSM_IDLE);
> +            sc->loop_counter_1 = 0;
> +            sc->loop_counter_2 = 0;
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_IDLE);
> +            break;
> +
> +        case SEQ_OP_SELECT_SLAVE:
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_EXECUTE);
> +            SPI_DEBUG(qemu_log("Sequencer SELECT_SLAVE at index = 0x%llx\n",
> +                GETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg)));
> +            /*
> +             * This device currently only supports a single responder
> +             * connection at position 0.  De-selecting a responder is fine
> +             * and expected at the end of a sequence but selecting any
> +             * responder other than 0 should cause an error.
> +             */
> +            sc->responder_select = opcode & 0x0F;
> +            if (sc->responder_select == 0) {
> +                SPI_DEBUG(qemu_log("Shifter done, pull the CS line high\n"));
> +                qemu_set_irq((sc->bus).cs_line[0], 1);
> +                sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                          sc->status_reg,
> +                                          (GETFIELD(
> +                                          STATUS_REG_SEQUENCER_INDEX,
> +                                          sc->status_reg) + 1));
> +                sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                          sc->status_reg, FSM_DONE);
> +            } else if (sc->responder_select != 1) {
> +                qemu_log_mask(LOG_GUEST_ERROR, "Slave selection other than 1 "
> +                              "not supported, select = 0x%x\n",
> +                               sc->responder_select);
> +                SPI_DEBUG(qemu_log("Sequencer stop requested due to invalid "
> +                                        "responder select at index = 0x%llx, "
> +                                   "shifter idling\n", GETFIELD(
> +                                   STATUS_REG_SEQUENCER_INDEX,
> +                                   sc->status_reg)));
> +                sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                          sc->status_reg, FSM_IDLE);
> +                stop = true;
> +            } else {
> +                /*
> +                 * Only allow an FSM_START state when a responder is
> +                 * selected
> +                 */
> +                sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                          sc->status_reg, FSM_START);
> +                SPI_DEBUG(qemu_log("shifter starting, pull CS line low\n"));
> +                qemu_set_irq((sc->bus).cs_line[0], 0);
> +                sc->first = 1;
> +                sc->last = 0;
> +                /*
> +                 * A Shift_N2 operation is only valid after a Shift_N1
> +                 * according to the spec. The spec doesn't say if that means
> +                 * immediately after or just after at any point. We will track
> +                 * the occurrence of a Shift_N1 to enforce this requirement in
> +                 * the most generic way possible by assuming that the rule
> +                 * applies once a valid responder select has occurred.
> +                 */
> +                sc->shift_n1_done = false;
> +                next_sequencer_fsm(sc);
> +            }
> +            break;
> +
> +        case SEQ_OP_SHIFT_N1:
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_EXECUTE);
> +            SPI_DEBUG(qemu_log("Sequencer SHIFT_N1 at index = 0x%llx\n",
> +                GETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg)));
> +            /*
> +             * Only allow a shift_n1 when the state is not IDLE or DONE.
> +             * In either of those two cases the sequencer is not in a proper
> +             * state to perform shift operations because the sequencer has:
> +             * - processed a responder deselect (DONE)
> +             * - processed a stop opcode (IDLE)
> +             * - encountered an error (IDLE)
> +             */
> +            if ((GETFIELD(STATUS_REG_SHIFTER_FSM,
> +                               sc->status_reg) == FSM_IDLE) ||
> +                (GETFIELD(STATUS_REG_SHIFTER_FSM,
> +                               sc->status_reg) == FSM_DONE)) {
> +                qemu_log_mask(LOG_GUEST_ERROR, "Shift_N1 not allowed in "
> +                              "shifter state = 0x%llx", GETFIELD(
> +                        STATUS_REG_SHIFTER_FSM, sc->status_reg));
> +                /*
> +                 * Set sequencer FSM error bit 3 (general_SPI_status[3])
> +                 * in status reg.
> +                 */
> +                sc->status_reg = SETFIELD(PPC_BIT(35), sc->status_reg, 1);
> +                SPI_DEBUG(qemu_log("Sequencer stop requested due to invalid "
> +                      "shifter state at index = 0x%llx\n", GETFIELD(
> +                         STATUS_REG_SEQUENCER_INDEX, sc->status_reg)));
> +                stop = true;
> +            } else {
> +                /*
> +                 * Look for the special case where there is a shift_n1 set for
> +                 * transmit and it is followed by a shift_n2 set for transmit
> +                 * AND the combined transmit length of the two operations is
> +                 * less than or equal to the size of the TDR register. In this
> +                 * case we want to use both this current shift_n1 opcode and the
> +                 * following shift_n2 opcode to assemble the frame for
> +                 * transmission to the responder without requiring a refill of
> +                 * the TDR between the two operations.
> +                 */
> +                if ((sc->sequencer_operation_reg[GETFIELD(
> +                     STATUS_REG_SEQUENCER_INDEX, sc->status_reg) + 1] & 0xF0)
> +                                == SEQ_OP_SHIFT_N2) {
> +                    SPI_DEBUG(qemu_log("Not sending N1 alone\n"));
> +                    send_n1_alone = false;
> +                }
> +                /*
> +                 * If the next opcode is 0x10, which deselects the SPI device
> +                 * then this is the last shift
> +                 */
> +                if (sc->sequencer_operation_reg[GETFIELD(
> +                    STATUS_REG_SEQUENCER_INDEX, sc->status_reg) + 1] ==
> +                                SEQ_OP_SELECT_SLAVE) {
> +                    sc->last = 1;
> +                }
> +                sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                sc->status_reg, FSM_SHIFT_N1);
> +                stop = operation_shiftn1(sc, opcode, &payload, send_n1_alone);
> +                if (stop) {
> +                    /*
> +                     *  The operation code says to stop, this can occur if:
> +                     * (1) RDR is full and the N1 shift is set for receive
> +                     * (2) TDR was empty at the time of the N1 shift so we need
> +                     * to wait for data.
> +                     * (3) Neither 1 nor 2 are occurring and we aren't sending
> +                     * N1 alone and N2 counter reload is set (bit 0 of the N2
> +                     * counter reload field).  In this case TDR_underrun will
> +                     * will be set and the Payload has been loaded so it is
> +                     * ok to advance the sequencer.
> +                     */
> +                    if (GETFIELD(STATUS_REG_TDR_UNDERRUN, sc->status_reg)) {
> +                        SPI_DEBUG(qemu_log("Sequencer stop requested due to N2 "
> +                              "counter reload active.\n"));
> +                        sc->shift_n1_done = true;
> +                        sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                                  sc->status_reg,
> +                                                  FSM_SHIFT_N2);
> +                        sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                                  sc->status_reg,
> +                                                  (GETFIELD(
> +                                                  STATUS_REG_SEQUENCER_INDEX,
> +                                                  sc->status_reg) + 1));
> +                        SPI_DEBUG(qemu_log("Set new sequencer index to = "
> +                                           "0x%llx\n", GETFIELD(
> +                                           STATUS_REG_SEQUENCER_INDEX,
> +                                           sc->status_reg)));
> +                    } else {
> +                        /*
> +                         * This is case (1) or (2) so the sequencer needs to
> +                         * wait and NOT go to the next sequence yet.
> +                         */
> +                        sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                                  sc->status_reg, FSM_WAIT);
> +                    }
> +                } else {
> +                    /* Ok to move on to the next index */
> +                    sc->shift_n1_done = true;
> +                    next_sequencer_fsm(sc);
> +                }
> +            }
> +            break;
> +
> +        case SEQ_OP_SHIFT_N2:
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_EXECUTE);
> +            SPI_DEBUG(qemu_log("Sequencer SHIFT_N2 at index = %lld\n",
> +                                GETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                         sc->status_reg)));
> +            if (!sc->shift_n1_done) {
> +                qemu_log_mask(LOG_GUEST_ERROR, "Shift_N2 is not allowed if a "
> +                              "Shift_N1 is not done, shifter state = 0x%llx",
> +                              GETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                       sc->status_reg));
> +                /*
> +                 * In case the sequencer actually stops if an N2 shift is
> +                 * requested before any N1 shift is done. Set sequencer FSM
> +                 * error bit 3 (general_SPI_status[3]) in status reg.
> +                 */
> +                sc->status_reg = SETFIELD(PPC_BIT(35), sc->status_reg, 1);
> +                SPI_DEBUG(qemu_log("Sequencer stop requested due to shift_n2 "
> +                                    "w/no shift_n1 done at index = 0x%llx\n",
> +                                   GETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                            sc->status_reg)));
> +                stop = true;
> +            } else {
> +                /*
> +                 * If the next opcode is 0x10, which deselects the SPI device
> +                 * then this is the last shift
> +                 */
> +                if (sc->sequencer_operation_reg[GETFIELD(
> +                    STATUS_REG_SEQUENCER_INDEX,
> +                    sc->status_reg) + 1] == SEQ_OP_SELECT_SLAVE) {
> +                    sc->last = 1;
> +                }
> +                /* Ok to do a Shift_N2 */
> +                sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                          sc->status_reg, FSM_SHIFT_N2);
> +                stop = operation_shiftn2(sc, opcode, &payload);
> +                /*
> +                 * If the operation code says to stop set the shifter state to
> +                 * wait and stop
> +                 */
> +                if (stop) {
> +                    sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                              sc->status_reg, FSM_WAIT);
> +                } else {
> +                    /* Ok to move on to the next index */
> +                    next_sequencer_fsm(sc);
> +                }
> +            }
> +            break;
> +
> +        case SEQ_OP_BRANCH_IFNEQ_RDR:
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_EXECUTE);
> +            SPI_DEBUG(qemu_log("Sequencer BRANCH_IFNEQ_RDR at "
> +                               "index = 0x%llx\n", GETFIELD(
> +                               STATUS_REG_SEQUENCER_INDEX, sc->status_reg)));
> +            /*
> +             * The memory mapping register RDR match value is compared against
> +             * the 16 rightmost bytes of the RDR (potentially with masking).
> +             * Since this comparison is performed against the contents of the
> +             * RDR then a receive must have previously occurred otherwise
> +             * there is no data to compare and the operation cannot be
> +             * completed and will stop the sequencer until RDR full is set to
> +             * 1.
> +             */
> +            if (GETFIELD(STATUS_REG_RDR_FULL, sc->status_reg) == 1) {
> +                bool rdr_matched = false;
> +                rdr_matched = does_rdr_match(sc);
> +                if (rdr_matched) {
> +                    SPI_DEBUG(qemu_log("Proceed to next sequencer index "
> +                                   "(increment on RDR match)\n"));
> +                    /* A match occurred, increment the sequencer index. */
> +                    next_sequencer_fsm(sc);
> +                } else {
> +                    SPI_DEBUG(qemu_log("Proceed to sequencer index=0x%x "
> +                          "(branch on RDR match fail)\n", (opcode & 0x7)));
> +                    /*
> +                     * Branch the sequencer to the index coded into the op
> +                     * code.
> +                     */
> +                    sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                              sc->status_reg, (opcode & 0x7));
> +                }
> +                /*
> +                 * Regardless of where the branch ended up we want the
> +                 * sequencer to continue shifting so we have to clear
> +                 * RDR_full.
> +                 */
> +                sc->status_reg = SETFIELD(STATUS_REG_RDR_FULL,
> +                                sc->status_reg, 0);
> +            } else {
> +                SPI_DEBUG(qemu_log("RDR not full for 0x6x opcode! Stopping "
> +                      "sequencer.\n"));
> +                stop = true;
> +                sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM,
> +                                         sc->status_reg, FSM_WAIT);
> +            }
> +            break;
> +
> +        case SEQ_OP_TRANSFER_TDR:
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_EXECUTE);
> +            qemu_log_mask(LOG_GUEST_ERROR, "Transfer TDR is not supported\n");
> +            next_sequencer_fsm(sc);
> +            break;
> +
> +        case SEQ_OP_BRANCH_IFNEQ_INC_1:
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_EXECUTE);
> +            SPI_DEBUG(qemu_log("Sequencer BRANCH_IFNEQ_INC_1 at index = "
> +                               "0x%llx, next index = %d, count_compare_1 = "
> +                               "0x%llx, loop_counter_1 = %d\n", GETFIELD(
> +                               STATUS_REG_SEQUENCER_INDEX, sc->status_reg),
> +                               (opcode & 0x07),
> +                  GETFIELD(COUNTER_CONFIG_REG_COUNT_COMPARE1,
> +                  sc->status_reg), sc->loop_counter_1));
> +            /*
> +             * The spec says the loop should execute count compare + 1 times.
> +             * However we learned from engineering that we really only loop
> +             * count_compare times, count compare = 0 makes this op code a
> +             * no-op
> +             */
> +            if (sc->loop_counter_1 !=
> +                GETFIELD(COUNTER_CONFIG_REG_COUNT_COMPARE1,
> +                                sc->counter_config_reg)) {
> +                /*
> +                 * If the next opcode is 0x10, which deselects the SPI device
> +                 * and we know that the next opcode is the last one in the
> +                 * loop then the next shift is the last shift
> +                 */
> +                uint8_t condition1 = sc->sequencer_operation_reg[
> +                                     GETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                     sc->status_reg) + 1];
> +                uint8_t condition2 = GETFIELD(COUNTER_CONFIG_REG_COUNT_COMPARE1,
> +                                 sc->counter_config_reg);
> +
> +                if ((condition1 == SEQ_OP_SELECT_SLAVE) &&
> +                    ((sc->loop_counter_1 + 1) == condition2)) {
> +                    sc->last = 1;
> +                }
> +                /*
> +                 * Next index is the lower nibble of the branch operation ID,
> +                 * mask off all but the first three bits so we don't try to
> +                 * access beyond the sequencer_operation_reg boundary.
> +                 */
> +                sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                          sc->status_reg, (opcode & 0x7));
> +                sc->loop_counter_1++;
> +                SPI_DEBUG(qemu_log("Branching to index = %d, loop_counter_1 = "
> +                      "%d\n", (opcode & 0x7), sc->loop_counter_1));
> +            } else {
> +                /* Continue to next index if loop counter is reached */
> +                next_sequencer_fsm(sc);
> +                SPI_DEBUG(qemu_log("loop counter 1 achieved, next sequencer "
> +                      "index = 0x%llx\n", GETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                                  sc->status_reg)));
> +            }
> +            break;
> +
> +        case SEQ_OP_BRANCH_IFNEQ_INC_2:
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_EXECUTE);
> +            SPI_DEBUG(qemu_log("Sequencer BRANCH_IFNEQ_INC_2 at index = "
> +                               "0x%llx, next index = %d, count_compare_2 = "
> +                               "0x%llx, loop_counter_2 = %d\n", GETFIELD(
> +                               STATUS_REG_SEQUENCER_INDEX, sc->status_reg),
> +                               (opcode & 0x07), GETFIELD(
> +                               COUNTER_CONFIG_REG_COUNT_COMPARE2,
> +                               sc->status_reg), sc->loop_counter_2));
> +            /*
> +             * If the next opcode is 0x10, which deselects the SPI device
> +             * and we know that the next opcode is the last one in the
> +             * loop then the next shift is the last shift
> +             */
> +            uint8_t condition1 = sc->sequencer_operation_reg[
> +                                 GETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                          sc->status_reg) + 1];
> +            uint8_t condition2 = GETFIELD(COUNTER_CONFIG_REG_COUNT_COMPARE2,
> +                              sc->counter_config_reg);
> +
> +            if ((condition1 == SEQ_OP_SELECT_SLAVE) &&
> +                ((sc->loop_counter_2 + 1) == condition2)) {
> +                sc->last = 1;
> +            }
> +            /*
> +             * The spec says the loop should execute count compare + 1 times.
> +             * However we learned from engineering that we really only loop
> +             * count_compare times, count compare = 0 makes this op code a
> +             * no-op
> +             */
> +            if (sc->loop_counter_2 != condition2) {
> +                /*
> +                 * Next index is the lower nibble of the branch operation ID,
> +                 * mask off all but the first three bits so we don't try to
> +                 * access beyond the sequencer_operation_reg boundary.
> +                 */
> +                sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                          sc->status_reg,
> +                                          (opcode & 0x7));
> +                sc->loop_counter_2++;
> +                SPI_DEBUG(qemu_log("Branching to index = %d, loop_counter_2 "
> +                                   "= %d", (opcode & 0x7),
> +                                   sc->loop_counter_2));
> +            } else {
> +                /* Continue to next index if loop counter is reached */
> +                next_sequencer_fsm(sc);
> +                SPI_DEBUG(qemu_log("loop counter 2 achieved, next sequencer "
> +                      "index = 0x%llx\n", GETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                                 sc->status_reg)));
> +            }
> +            break;
> +
> +        default:
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_EXECUTE);
> +            qemu_log_mask(LOG_GUEST_ERROR, "Sequencer opcode 0x%x is not "
> +                      "supported\n", opcode);
> +            /* Ignore unsupported operations. */
> +            next_sequencer_fsm(sc);
> +            break;
> +        } /* end of switch */
> +        /*
> +         * If we used all 8 opcodes without seeing a 00 - STOP in the sequence
> +         * we need to go ahead and end things as if there was a STOP at the
> +         * end.
> +         */
> +        if (GETFIELD(STATUS_REG_SEQUENCER_INDEX, sc->status_reg) == 8) {
> +            SPI_DEBUG(qemu_log("All 8 opcodes completed, sequencer "
> +                               "idling\n"));
> +            sc->status_reg = SETFIELD(STATUS_REG_SHIFTER_FSM, sc->status_reg,
> +                                      FSM_IDLE);
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_INDEX,
> +                                      sc->status_reg, 0);
> +            sc->loop_counter_1 = 0;
> +            sc->loop_counter_2 = 0;
> +            sc->status_reg = SETFIELD(STATUS_REG_SEQUENCER_FSM,
> +                                      sc->status_reg, SEQ_STATE_IDLE);
> +            break;
> +        }
> +        /* Break the loop if a stop was requested */
> +        if (stop) {
> +            break;
> +        }
> +    } /* end of while */
> +    return;
> +} /* end of operation_sequencer() */
> +
> +/*
> + * Calculate the N1 counters based on passed in opcode and
> + * internal register values.
> + * The method assumes that the opcode is a Shift_N1 opcode
> + * and doesn't test it.
> + * The counters returned are:
> + * N1 bits: Number of bits in the payload data that are significant
> + * to the responder.
> + * N1_bytes: Total count of payload bytes for the N1 (portion of the) frame.
> + * N1_tx: Total number of bytes taken from TDR for N1
> + * N1_rx: Total number of bytes taken from the payload for N1
> + */
> +void calculate_N1(PnvSpiController *sc, uint8_t opcode)
> +{
> +    /*
> +     * Shift_N1 opcode form: 0x3M
> +     * Implicit mode:
> +     * If M != 0 the shift count is M bytes and M is the number of tx bytes.
> +     * Forced Implicit mode:
> +     * M is the shift count but tx and rx is determined by the count control
> +     * register fields.  Note that we only check for forced Implicit mode when
> +     * M != 0 since the mode doesn't make sense when M = 0.
> +     * Explicit mode:
> +     * If M == 0 then shift count is number of bits defined in the
> +     * Counter Configuration Register's shift_count_N1 field.
> +     */
> +    if (GETFIELD(PPC_BITMASK8(4, 7), opcode) == 0) {
> +        /* Explicit mode */
> +        sc->N1_bits = GETFIELD(COUNTER_CONFIG_REG_SHIFT_COUNT_N1,
> +                                       sc->counter_config_reg);
> +        sc->N1_bytes = ceil(sc->N1_bits / 8);
> +        sc->N1_tx = 0;
> +        sc->N1_rx = 0;
> +        /* If tx count control for N1 is set, load the tx value */
> +        if (GETFIELD(PPC_BIT(50), sc->counter_config_reg) == 1) {
> +            sc->N1_tx = sc->N1_bytes;
> +        }
> +        /* If rx count control for N1 is set, load the rx value */
> +        if (GETFIELD(PPC_BIT(51), sc->counter_config_reg) == 1) {
> +            sc->N1_rx = sc->N1_bytes;
> +        }
> +    } else {
> +        /* Implicit mode/Forced Implicit mode, use M field from opcode */
> +        sc->N1_bytes = GETFIELD(PPC_BITMASK8(4, 7), opcode);
> +        sc->N1_bits = sc->N1_bytes * 8;
> +        /*
> +         * Assume that we are going to transmit the count
> +         * (pure Implicit only)
> +         */
> +        sc->N1_tx = sc->N1_bytes;
> +        sc->N1_rx = 0;
> +        /* Let Forced Implicit mode have an effect on the counts */
> +        if (GETFIELD(PPC_BIT(49), sc->counter_config_reg) == 1) {
> +            /*
> +             * If Forced Implicit mode and count control doesn't
> +             * indicate transmit then reset the tx count to 0
> +             */
> +            if (GETFIELD(PPC_BIT(50), sc->counter_config_reg) == 0) {
> +                sc->N1_tx = 0;
> +            }
> +            /* If rx count control for N1 is set, load the rx value */
> +            if (GETFIELD(PPC_BIT(51), sc->counter_config_reg) == 1) {
> +                sc->N1_rx = sc->N1_bytes;
> +            }
> +        }
> +    }
> +    /*
> +     * Enforce an upper limit on the size of N1 that is equal to the known size
> +     * of the shift register, 64 bits or 72 bits if ECC is enabled.
> +     * If the size exceeds 72 bits it is a user error so log an error,
> +     * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM
> +     * error bit.
> +     */
> +    uint8_t ecc_control = GETFIELD(PPC_BITMASK(29, 30),
> +                                   sc->clock_config_reset_control);
> +    if (ecc_control == 0 || ecc_control == 2) {
> +        if (sc->N1_bytes > (SPI_CONTROLLER_REG_SIZE + 1)) {
> +            qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size when "
> +                          "ECC enabled, bytes = 0x%x, bits = 0x%x\n",
> +                          sc->N1_bytes, sc->N1_bits);
> +            sc->N1_bytes = SPI_CONTROLLER_REG_SIZE + 1;
> +            sc->N1_bits = sc->N1_bytes * 8;
> +        }
> +    } else if (sc->N1_bytes > SPI_CONTROLLER_REG_SIZE) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size, "
> +                      "bytes = 0x%x, bits = 0x%x\n",
> +                      sc->N1_bytes, sc->N1_bits);
> +        sc->N1_bytes = SPI_CONTROLLER_REG_SIZE;
> +        sc->N1_bits = sc->N1_bytes * 8;
> +    }
> +} /* end of calculate_N1 */
> +
> +/*
> + * Shift_N1 operation handler method
> + */
> +bool operation_shiftn1(PnvSpiController *sc, uint8_t opcode,
> +                       xfer_buffer **payload, bool send_n1_alone)
> +{
> +    uint8_t n1_count;
> +    bool stop = false;
> +
> +    /*
> +     * If there isn't a current payload left over from a stopped sequence
> +     * create a new one.
> +     */
> +    if (*payload == NULL) {
> +        SPI_DEBUG(qemu_log("Creating new payload xfer_buffer\n"));
> +        *payload = xfer_buffer_new();
> +    }
> +    /*
> +     * Use a combination of N1 counters to build the N1 portion of the
> +     * transmit payload.
> +     * We only care about transmit at this time since the request payload
> +     * only represents data going out on the controller output line.
> +     * Leave mode specific considerations in the calculate function since
> +     * all we really care about are counters that tell use exactly how
> +     * many bytes are in the payload and how many of those bytes to
> +     * include from the TDR into the payload.
> +     */
> +    calculate_N1(sc, opcode);
> +    SPI_DEBUG(qemu_log("Shift N1 started..\n"));
> +    log_all_N_counts(sc);
> +    /*
> +     * Zero out the N2 counters here in case there is no N2 operation following
> +     * the N1 operation in the sequencer.  This keeps leftover N2 information
> +     * from interfering with spi_response logic.
> +     */
> +    sc->N2_bits = 0;
> +    sc->N2_bytes = 0;
> +    sc->N2_tx = 0;
> +    sc->N2_rx = 0;
> +    /*
> +     * N1_bytes is the overall size of the N1 portion of the frame regardless of
> +     * whether N1 is used for tx, rx or both.  Loop over the size to build a
> +     * payload that is N1_bytes long.
> +     * N1_tx is the count of bytes to take from the TDR and "shift" into the
> +     * frame which means append those bytes to the payload for the N1 portion
> +     * of the frame.
> +     * If N1_tx is 0 or if the count exceeds the size of the TDR append 0xFF to
> +     * the frame until the overall N1 count is reached.
> +     */
> +    n1_count = 0;
> +    while (n1_count < sc->N1_bytes) {
> +        /*
> +         * Assuming that if N1_tx is not equal to 0 then it is the same as
> +         * N1_bytes.
> +         */
> +        if ((sc->N1_tx != 0) && (n1_count < SPI_CONTROLLER_REG_SIZE)) {
> +
> +            if (GETFIELD(STATUS_REG_TDR_FULL, sc->status_reg) == 1) {
> +                /*
> +                 * Note that we are only appending to the payload IF the TDR
> +                 * is full otherwise we don't touch the payload because we are
> +                 * going to NOT send the payload and instead tell the sequencer
> +                 * that called us to stop and wait for a TDR write so we have
> +                 * data to load into the payload.
> +                 */
> +                uint8_t n1_byte = 0x00;
> +                n1_byte = get_from_offset(sc, n1_count);
> +                SPI_DEBUG(qemu_log("Extracting tx n1_byte = 0x%2.2x at index "
> +                                   "%d from TDR\n", n1_byte, n1_count));
> +                if (sc->reverse_bits) {
> +                    SPI_DEBUG(qemu_log("Reversing bit order of tx n1_byte\n"));
> +                    n1_byte = reverse_bits8(n1_byte);
> +                }
> +                SPI_DEBUG(qemu_log("Appending tx n1_byte = 0x%2.2x to "
> +                            "Payload\n", n1_byte));
> +                *(xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) =
> +                        n1_byte;
> +            } else {
> +                /*
> +                 * We hit a shift_n1 opcode TX but the TDR is empty, tell the
> +                 * sequencer to stop and break this loop.
> +                 */
> +                SPI_DEBUG(qemu_log("Shift N1 set for transmit but TDR is empty,"
> +                      " requesting sequencer stop\n"));
> +                stop = true;
> +                break;
> +            }
> +        } else {
> +            /*
> +             * Cases here:
> +             * - we are receiving during the N1 frame segment and the RDR
> +             *   is full so we need to stop until the RDR is read
> +             * - we are transmitting and we don't care about RDR status
> +             *   since we won't be loading RDR during the frame segment.
> +             * - we are receiving and the RDR is empty so we allow the operation
> +             *   to proceed.
> +             */
> +            if ((sc->N1_rx != 0) && (GETFIELD(STATUS_REG_RDR_FULL,
> +                                           sc->status_reg) == 1)) {
> +                SPI_DEBUG(qemu_log("Shift N1 set for receive but RDR is full, "
> +                      "requesting sequencer stop\n"));
> +                stop = true;
> +                break;
> +            } else {
> +                SPI_DEBUG(qemu_log("Appending tx n1_byte = 0xFF to Payload\n"));
> +                *(xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) = 0xff;
> +            }
> +        }
> +        n1_count++;
> +    } /* end of while */
> +    /*
> +     * If we are not stopping due to an empty TDR and we are doing an N1 TX
> +     * and the TDR is full we need to clear the TDR_full status.
> +     * Do this here instead of up in the loop above so we don't log the message
> +     * in every loop iteration.
> +     * Ignore the send_n1_alone flag, all that does is defer the TX until the N2
> +     * operation, which was found immediately after the current opcode.  The TDR
> +     * was unloaded and will be shifted so we have to clear the TDR_full status.
> +     */
> +    if (!stop && (sc->N1_tx != 0) &&
> +        (GETFIELD(STATUS_REG_TDR_FULL, sc->status_reg) == 1)) {
> +
> +        sc->status_reg = SETFIELD(STATUS_REG_TDR_FULL, sc->status_reg, 0);
> +        SPI_DEBUG(qemu_log("TDR_full set to 0\n"));
> +    }
> +    /*
> +     * There are other reasons why the shifter would stop, such as a TDR empty
> +     * or RDR full condition with N1 set to receive.  If we haven't stopped due
> +     * to either one of those conditions then check if the send_n1_alone flag is
> +     * equal to False, indicating the next opcode is an N2 operation, AND if
> +     * the N2 counter reload switch (bit 0 of the N2 count control field) is
> +     * set.  This condition requires a pacing write to "kick" off the N2
> +     * shift which includes the N1 shift as well when send_n1_alone is False.
> +     */
> +    if (!stop && !send_n1_alone &&
> +       (GETFIELD(PPC_BIT(52), sc->counter_config_reg) == 1)) {
> +        SPI_DEBUG(qemu_log("N2 counter reload active, stop N1 shift, "
> +              "TDR_underrun set to 1\n"));
> +        stop = true;
> +        sc->status_reg = SETFIELD(STATUS_REG_TDR_UNDERRUN, sc->status_reg, 1);
> +    }
> +    /*
> +     * If send_n1_alone is set AND we have a full TDR then this is the first and
> +     * last payload to send and we don't have an N2 frame segment to add to the
> +     * payload.
> +     */
> +    if (send_n1_alone && !stop) {
> +        uint32_t tx;
> +        uint32_t rx;
> +        uint8_t *read_buf = NULL;
> +        xfer_buffer *rsp_payload = NULL;
> +
> +        /* We have a TX and a full TDR or an RX and an empty RDR */
> +        SPI_DEBUG(qemu_log("Shifting N1 frame: first = %d, last = %d, "
> +                           "n1 bits = %d\n", sc->first, sc->last,
> +                           sc->N1_bits));
> +        rsp_payload = xfer_buffer_new();
> +        for (int offset = 0; offset < (*payload)->len; offset = offset + 4) {
> +            xfer_buffer_read_ptr(*payload, &read_buf, offset, 4);
> +            tx = 0;
> +            for (int i = 0; i < 4; i++) {
> +                if ((offset + i) >= (*payload)->len) {
> +                    break;
> +                }
> +                tx = (tx << 8) | read_buf[i];
> +            }
> +            rx = ssi_transfer((sc->bus).ssi_bus, tx);
> +            for (int i = 0; i < 4; i++) {
> +                if ((offset + i) >= (*payload)->len) {
> +                    break;
> +                }
> +                *(xfer_buffer_write_ptr(rsp_payload, rsp_payload->len, 1)) =
> +                        (rx >> (24 - i * 8)) & 0xFF;
> +            }
> +        }
> +        if (rsp_payload != NULL) {
> +            spi_response(sc, sc->N1_bits, rsp_payload);
> +        }
> +        sc->first = 0;
> +        sc->last = 0;
> +        /* The N1 frame shift is complete so reset the N1 counters */
> +        sc->N2_bits = 0;
> +        sc->N2_bytes = 0;
> +        sc->N2_tx = 0;
> +        sc->N2_rx = 0;
> +        xfer_buffer_free(*payload);
> +        *payload = NULL;
> +        SPI_DEBUG(qemu_log("Payload buffer freed\n"));
> +    } else {
> +        SPI_DEBUG(qemu_log("Not shifting N1, send_n1_alone = %d, stop = %d\n",
> +              send_n1_alone, stop));
> +    }
> +    return stop;
> +} /* end of operation_shiftn1() */
> +
> +/*
> + * Calculate the N2 counters based on passed in opcode and
> + * internal register values.
> + * The method assumes that the opcode is a Shift_N2 opcode
> + * and doesn't test it.
> + * The counters returned are:
> + * N2 bits: Number of bits in the payload data that are significant
> + * to the responder.
> + * N2_bytes: Total count of payload bytes for the N2 frame.
> + * N2_tx: Total number of bytes taken from TDR for N2
> + * N2_rx: Total number of bytes taken from the payload for N2
> + */
> +void calculate_N2(PnvSpiController *sc, uint8_t opcode)
> +{
> +    /*
> +     * Shift_N2 opcode form: 0x4M
> +     * Implicit mode:
> +     * If M!=0 the shift count is M bytes and M is the number of rx bytes.
> +     * Forced Implicit mode:
> +     * M is the shift count but tx and rx is determined by the count control
> +     * register fields.  Note that we only check for Forced Implicit mode when
> +     * M != 0 since the mode doesn't make sense when M = 0.
> +     * Explicit mode:
> +     * If M==0 then shift count is number of bits defined in the
> +     * Counter Configuration Register's shift_count_N1 field.
> +     */
> +    if (GETFIELD(PPC_BITMASK8(4, 7), opcode) == 0) {
> +        /* Explicit mode */
> +        sc->N2_bits = GETFIELD(COUNTER_CONFIG_REG_SHIFT_COUNT_N2,
> +                               sc->counter_config_reg);
> +        sc->N2_bytes = ceil(sc->N2_bits / 8);
> +        sc->N2_tx = 0;
> +        sc->N2_rx = 0;
> +        /* If tx count control for N2 is set, load the tx value */
> +        if (GETFIELD(PPC_BIT(54), sc->counter_config_reg) == 1) {
> +            sc->N2_tx = sc->N2_bytes;
> +        }
> +        /* If rx count control for N2 is set, load the rx value */
> +        if (GETFIELD(PPC_BIT(55), sc->counter_config_reg) == 1) {
> +            sc->N2_rx = sc->N2_bytes;
> +        }
> +    } else {
> +        /* Implicit mode/Forced Implicit mode, use M field from opcode */
> +        sc->N2_bytes = GETFIELD(PPC_BITMASK8(4, 7), opcode);
> +        sc->N2_bits = sc->N2_bytes * 8;
> +        /* Assume that we are going to receive the count */
> +        sc->N2_rx = sc->N2_bytes;
> +        sc->N2_tx = 0;
> +        /* Let Forced Implicit mode have an effect on the counts */
> +        if (GETFIELD(PPC_BIT(53), sc->counter_config_reg) == 1) {
> +            /*
> +             * If Forced Implicit mode and count control doesn't
> +             * indicate a receive then reset the rx count to 0
> +             */
> +            if (GETFIELD(PPC_BIT(55), sc->counter_config_reg) == 0) {
> +                sc->N2_rx = 0;
> +            }
> +            /* If tx count control for N2 is set, load the tx value */
> +            if (GETFIELD(PPC_BIT(54), sc->counter_config_reg) == 1) {
> +                sc->N2_tx = sc->N2_bytes;
> +            }
> +        }
> +    }
> +    /*
> +     * Enforce an upper limit on the size of N1 that is equal to the
> +     * known size of the shift register, 64 bits or 72 bits if ECC
> +     * is enabled.
> +     * If the size exceeds 72 bits it is a user error so log an error,
> +     * cap the size at a max of 64 bits or 72 bits and set the sequencer FSM
> +     * error bit.
> +     */
> +    uint8_t ecc_control = GETFIELD(PPC_BITMASK(29, 30),
> +                                   sc->clock_config_reset_control);
> +    if (ecc_control == 0 || ecc_control == 2) {
> +        if (sc->N2_bytes > (SPI_CONTROLLER_REG_SIZE + 1)) {
> +            SPI_DEBUG(qemu_log("Unsupported N2 shift size when ECC enabled, "
> +                                     "bytes = 0x%x, bits = 0x%x\n",
> +                               sc->N2_bytes, sc->N2_bits));
> +            sc->N2_bytes = SPI_CONTROLLER_REG_SIZE + 1;
> +            sc->N2_bits = sc->N2_bytes * 8;
> +        }
> +    } else if (sc->N2_bytes > SPI_CONTROLLER_REG_SIZE) {
> +        SPI_DEBUG(qemu_log("Unsupported N2 shift size, bytes = 0x%x, "
> +                           "bits = 0x%x\n", sc->N2_bytes, sc->N2_bits));
> +        sc->N2_bytes = SPI_CONTROLLER_REG_SIZE;
> +        sc->N2_bits = sc->N2_bytes * 8;
> +    }
> +} /* end of calculate_N2 */
> +
> +/*
> + * Shift_N2 operation handler method
> + */
> +
> +bool operation_shiftn2(PnvSpiController *sc, uint8_t opcode,
> +                       xfer_buffer **payload)
> +{
> +    uint8_t n2_count;
> +    bool stop = false;
> +
> +    /*
> +     * If there isn't a current payload left over from a stopped sequence
> +     * create a new one.
> +     */
> +    if (*payload == NULL) {
> +        SPI_DEBUG(qemu_log("Creating new payload xfer_buffer\n"));
> +        *payload = xfer_buffer_new();
> +    }
> +    /*
> +     * Use a combination of N2 counters to build the N2 portion of the
> +     * transmit payload.
> +     */
> +    calculate_N2(sc, opcode);
> +    SPI_DEBUG(qemu_log("Shift N2 started\n"));
> +    log_all_N_counts(sc);
> +    /*
> +     * The only difference between this code and the code for shift N1 is
> +     * that this code has to account for the possible presence of N1 transmit
> +     * bytes already taken from the TDR.
> +     * If there are bytes to be transmitted for the N2 portion of the frame
> +     * and there are still bytes in TDR that have not been copied into the
> +     * TX data of the payload, this code will handle transmitting those
> +     * remaining bytes.
> +     * If for some reason the transmit count(s) add up to more than the size
> +     * of the TDR we will just append 0xFF to the transmit payload data until
> +     * the payload is N1 + N2 bytes long.
> +     */
> +    n2_count = 0;
> +    while (n2_count < sc->N2_bytes) {
> +        /*
> +         * If the RDR is full and we need to RX just bail out, letting the
> +         * code continue will end up building the payload twice in the same
> +         * buffer since RDR full causes a sequence stop and restart.
> +         */
> +        if ((sc->N2_rx != 0) &&
> +            (GETFIELD(STATUS_REG_RDR_FULL, sc->status_reg) == 1)) {
> +            SPI_DEBUG(qemu_log("Shift N2 set for receive but RDR is full, "
> +                  "requesting sequencer stop\n"));
> +            stop = true;
> +            break;
> +        }
> +        if ((sc->N2_tx != 0) && ((sc->N1_tx + n2_count) <
> +                                SPI_CONTROLLER_REG_SIZE)) {
> +            /* Always append data for the N2 segment if it is set for TX */
> +            uint8_t n2_byte = 0x00;
> +            n2_byte = get_from_offset(sc, (sc->N1_tx + n2_count));
> +            SPI_DEBUG(qemu_log("Extracting tx n2_byte = 0x%2.2x at index %d "
> +                  "from TDR\n", n2_byte, (sc->N1_tx + n2_count)));
> +            if (sc->reverse_bits) {
> +                SPI_DEBUG(qemu_log("Reversing bit order of tx n2_byte\n"));
> +                n2_byte = reverse_bits8(n2_byte);
> +            }
> +            SPI_DEBUG(qemu_log("Appending tx n2_byte = 0x%2.2x to Payload\n",
> +                            n2_byte));
> +            *(xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) = n2_byte;
> +        } else {
> +            /*
> +             * Regardless of whether or not N2 is set for TX or RX, we need
> +             * the number of bytes in the payload to match the overall length
> +             * of the operation.
> +             */
> +            SPI_DEBUG(qemu_log("Appending tx n2_byte = 0xFF to Payload\n"));
> +            *(xfer_buffer_write_ptr(*payload, (*payload)->len, 1)) = 0xff;
> +        }
> +        n2_count++;
> +    } /* end of while */
> +    if (!stop) {
> +        uint32_t tx;
> +        uint32_t rx;
> +        uint8_t *read_buf = NULL;
> +        xfer_buffer *rsp_payload = NULL;
> +
> +        /* We have a TX and a full TDR or an RX and an empty RDR */
> +        SPI_DEBUG(qemu_log("Shifting N2 frame: first = %d, last = %d, "
> +              "n1+n2 bits = %d\n", sc->first, sc->last,
> +              (sc->N1_bits + sc->N2_bits)));
> +        rsp_payload = xfer_buffer_new();
> +        for (int offset = 0; offset < (*payload)->len; offset = offset + 4) {
> +            xfer_buffer_read_ptr(*payload, &read_buf, offset, 4);
> +            tx = 0;
> +            for (int i = 0; i < 4; i++) {
> +                if ((offset + i) >= (*payload)->len) {
> +                    break;
> +                }
> +                tx = (tx << 8) | read_buf[i];
> +            }
> +            rx = ssi_transfer((sc->bus).ssi_bus, tx);
> +            for (int i = 0; i < 4; i++) {
> +                if ((offset + i) >= (*payload)->len) {
> +                    break;
> +                }
> +                *(xfer_buffer_write_ptr(rsp_payload, rsp_payload->len, 1)) =
> +                        (rx >> (24 - i * 8)) & 0xFF;
> +            }
> +        }
> +        if (rsp_payload != NULL) {
> +            spi_response(sc, (sc->N1_bits + sc->N2_bits), rsp_payload);
> +        }
> +        sc->first = 0;
> +        sc->last = 0;
> +        /*
> +         * If we are doing an N2 TX and the TDR is full we need to clear the
> +         * TDR_full status. Do this here instead of up in the loop above so we
> +         * don't log the message in every loop iteration.
> +         */
> +        if ((sc->N2_tx != 0) &&
> +            (GETFIELD(STATUS_REG_TDR_FULL, sc->status_reg) == 1)) {
> +            sc->status_reg = SETFIELD(STATUS_REG_TDR_FULL, sc->status_reg, 0);
> +            SPI_DEBUG(qemu_log("TDR_full set to 0\n"));
> +        }
> +        /*
> +         * The N2 frame shift is complete so reset the N2 counters.
> +         * Reset the N1 counters also in case the frame was a combination of
> +         * N1 and N2 segments.
> +         */
> +        sc->N2_bits = 0;
> +        sc->N2_bytes = 0;
> +        sc->N2_tx = 0;
> +        sc->N2_rx = 0;
> +        sc->N1_bits = 0;
> +        sc->N1_bytes = 0;
> +        sc->N1_tx = 0;
> +        sc->N1_rx = 0;
> +        xfer_buffer_free(*payload);
> +        *payload = NULL;
> +        SPI_DEBUG(qemu_log("Payload buffer freed\n"));
> +    } else {
> +        SPI_DEBUG(qemu_log("Not shifting N2, stop = %d\n", stop));
> +    }
> +    return stop;
> +} /*  end of operation_shiftn2()*/
> +
> +/*
> + * The SPIC engine and its internal sequencer can be interrupted and reset by
> + * a hardware signal, the sbe_spicst_hard_reset bits from Pervasive
> + * Miscellaneous Register of sbe_register_bo device.
> + * Reset immediately aborts any SPI transaction in progress and returns the
> + * sequencer and state machines to idle state.
> + * The configuration register values are not changed. The status register is
> + * not reset. The engine registers are not reset.
> + * The SPIC engine reset does not have any affect on the attached devices.
> + * Reset handling of any attached devices is beyond the scope of the engine.
> + */
> +static void do_reset(DeviceState *dev)
> +{
> +    PnvSpiController *sc = PNV_SPICONTROLLER(dev);
> +    SPI_DEBUG(qemu_log("Resetting spic engine sequencer configuration and spi "
> +                           "communication\n"));
> +    /* Reset all N1 and N2 counters, and other constants */
> +    sc->first = 0;
> +    sc->last = 0;
> +    sc->N2_bits = 0;
> +    sc->N2_bytes = 0;
> +    sc->N2_tx = 0;
> +    sc->N2_rx = 0;
> +    sc->N1_bits = 0;
> +    sc->N1_bytes = 0;
> +    sc->N1_tx = 0;
> +    sc->N1_rx = 0;
> +    sc->loop_counter_1 = 0;
> +    sc->loop_counter_2 = 0;
> +    SPI_DEBUG(qemu_log("Disconnected from responder\n"));
> +    qemu_set_irq((sc->bus).cs_line[0], 1);
> +}
> +
>   static void pnv_spi_bus_realize(DeviceState *dev, Error **errp)
>   {
>       PnvSPIBus *s = PNV_SPI_BUS(dev);
> @@ -191,6 +1498,7 @@ static const TypeInfo pnv_spi_bus_info = {
>   
>   static Property pnv_spi_controller_properties[] = {
>       DEFINE_PROP_UINT32("spic_num", PnvSpiController, spic_num, 0),
> +    DEFINE_PROP_BOOL("reverse_bits", PnvSpiController, reverse_bits, false),
>       DEFINE_PROP_END_OF_LIST(),
>   };
>   
> @@ -254,6 +1562,7 @@ static void pnv_spi_controller_class_init(ObjectClass *klass, void *data)
>   
>       dc->desc = "PowerNV SPI Controller";
>       dc->realize = pnv_spi_controller_realize;
> +    dc->reset = do_reset;
>       device_class_set_props(dc, pnv_spi_controller_properties);
>   }
>   



  reply	other threads:[~2024-04-16  9:41 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-09 17:56 [PATCH v2 0/6] hw/ppc: SPI model Chalapathi V
2024-04-09 17:56 ` [PATCH v2 1/6] hw/ppc: remove SPI responder model Chalapathi V
2024-04-15 14:46   ` Cédric Le Goater
2024-04-09 17:56 ` [PATCH v2 2/6] hw/ppc: SPI controller model - registers implementation Chalapathi V
2024-04-15 15:14   ` Cédric Le Goater
2024-04-16 17:02     ` Chalapathi V
2024-04-22 14:06       ` Cédric Le Goater
2024-04-09 17:56 ` [PATCH v2 3/6] hw/ppc: SPI controller model - sequencer and shifter Chalapathi V
2024-04-16  9:39   ` Cédric Le Goater [this message]
2024-04-16 17:08     ` Chalapathi V
2024-04-09 17:56 ` [PATCH v2 4/6] hw/misc: Microchip's 25CSM04 SEEPROM model Chalapathi V
2024-04-22 14:44   ` Cédric Le Goater
2024-04-09 17:56 ` [PATCH v2 5/6] hw/ppc: SPI controller wiring to P10 chip and create seeprom device Chalapathi V
2024-04-22 15:03   ` Cédric Le Goater
2024-04-24 17:12     ` Chalapathi V
2024-04-09 17:57 ` [PATCH v2 6/6] tests/qtest: Add pnv-spi-seeprom qtest Chalapathi V

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d41c21ae-cf32-415d-94bb-694c8e47d0f4@kaod.org \
    --to=clg@kaod.org \
    --cc=calebs@us.ibm.com \
    --cc=chalapathi.v@ibm.com \
    --cc=chalapathi.v@linux.ibm.com \
    --cc=dantan@us.ibm.com \
    --cc=fbarrat@linux.ibm.com \
    --cc=npiggin@gmail.com \
    --cc=qemu-devel@nongnu.org \
    --cc=qemu-ppc@nongnu.org \
    --cc=saif.abrar@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).