@@ -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.
*/
#ifndef PPC_PNV_SPI_H
@@ -31,6 +39,25 @@ typedef struct PnvSpi {
MemoryRegion xscom_spic_regs;
/* SPI object number */
uint32_t spic_num;
+ uint8_t transfer_len;
+ uint8_t responder_select;
+ /* To verify if shift_n1 happens prior to shift_n2 */
+ bool shift_n1_done;
+ /* 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;
/* SPI registers */
uint64_t regs[PNV_SPI_REGS];
@@ -28,6 +28,17 @@
/* counter_config_reg */
#define SPI_CTR_CFG_REG 0x01
+#define SPI_CTR_CFG_N1 PPC_BITMASK(0, 7)
+#define SPI_CTR_CFG_N2 PPC_BITMASK(8, 15)
+#define SPI_CTR_CFG_CMP1 PPC_BITMASK(24, 31)
+#define SPI_CTR_CFG_CMP2 PPC_BITMASK(32, 39)
+#define SPI_CTR_CFG_N1_CTRL_B1 PPC_BIT(49)
+#define SPI_CTR_CFG_N1_CTRL_B2 PPC_BIT(50)
+#define SPI_CTR_CFG_N1_CTRL_B3 PPC_BIT(51)
+#define SPI_CTR_CFG_N2_CTRL_B0 PPC_BIT(52)
+#define SPI_CTR_CFG_N2_CTRL_B1 PPC_BIT(53)
+#define SPI_CTR_CFG_N2_CTRL_B2 PPC_BIT(54)
+#define SPI_CTR_CFG_N2_CTRL_B3 PPC_BIT(55)
/* config_reg */
#define CONFIG_REG1 0x02
@@ -36,9 +47,13 @@
#define SPI_CLK_CFG_REG 0x03
#define SPI_CLK_CFG_HARD_RST 0x0084000000000000;
#define SPI_CLK_CFG_RST_CTRL PPC_BITMASK(24, 27)
+#define SPI_CLK_CFG_ECC_EN PPC_BIT(28)
+#define SPI_CLK_CFG_ECC_CTRL PPC_BITMASK(29, 30)
/* memory_mapping_reg */
#define SPI_MM_REG 0x04
+#define SPI_MM_RDR_MATCH_VAL PPC_BITMASK(32, 47)
+#define SPI_MM_RDR_MATCH_MASK PPC_BITMASK(48, 63)
/* transmit_data_reg */
#define SPI_XMIT_DATA_REG 0x05
@@ -60,8 +75,59 @@
#define SPI_STS_SEQ_FSM PPC_BITMASK(8, 15)
#define SPI_STS_SHIFTER_FSM PPC_BITMASK(16, 27)
#define SPI_STS_SEQ_INDEX PPC_BITMASK(28, 31)
-#define SPI_STS_GEN_STATUS PPC_BITMASK(32, 63)
+#define SPI_STS_GEN_STATUS_B3 PPC_BIT(35)
#define SPI_STS_RDR PPC_BITMASK(1, 3)
#define SPI_STS_TDR PPC_BITMASK(5, 7)
+/*
+ * Shifter states
+ *
+ * These are the same values defined for the Shifter FSM field of the
+ * status register. It's a 12 bit field so we will represent it as three
+ * nibbles in the constants.
+ *
+ * These are shifter_fsm values
+ *
+ * Status reg bits 16-27 -> field bits 0-11
+ * bits 0,1,2,5 unused/reserved
+ * bit 4 crc shift in (unused)
+ * bit 8 crc shift out (unused)
+ */
+
+#define FSM_DONE 0x100 /* bit 3 */
+#define FSM_SHIFT_N2 0x020 /* bit 6 */
+#define FSM_WAIT 0x010 /* bit 7 */
+#define FSM_SHIFT_N1 0x004 /* bit 9 */
+#define FSM_START 0x002 /* bit 10 */
+#define FSM_IDLE 0x001 /* bit 11 */
+
+/*
+ * Sequencer states
+ *
+ * These are sequencer_fsm values
+ *
+ * Status reg bits 8-15 -> field bits 0-7
+ * bits 0-3 unused/reserved
+ *
+ */
+#define SEQ_STATE_INDEX_INCREMENT 0x08 /* bit 4 */
+#define SEQ_STATE_EXECUTE 0x04 /* bit 5 */
+#define SEQ_STATE_DECODE 0x02 /* bit 6 */
+#define SEQ_STATE_IDLE 0x01 /* bit 7 */
+
+/*
+ * These are the supported sequencer operations.
+ * Only the upper nibble is significant because for many operations
+ * the lower nibble is a variable specific to the operation.
+ */
+#define SEQ_OP_STOP 0x00
+#define SEQ_OP_SELECT_SLAVE 0x10
+#define SEQ_OP_SHIFT_N1 0x30
+#define SEQ_OP_SHIFT_N2 0x40
+#define SEQ_OP_BRANCH_IFNEQ_RDR 0x60
+#define SEQ_OP_TRANSFER_TDR 0xC0
+#define SEQ_OP_BRANCH_IFNEQ_INC_1 0xE0
+#define SEQ_OP_BRANCH_IFNEQ_INC_2 0xF0
+#define NUM_SEQ_OPS 8
+
#endif
@@ -17,6 +17,9 @@
#include "hw/irq.h"
#include "trace.h"
+#define PNV_SPI_OPCODE_LO_NIBBLE(x) (x & 0x0F)
+#define PNV_SPI_MASKED_OPCODE(x) (x & 0xF0)
+
/*
* Macro from include/hw/ppc/fdt.h
* fdt.h cannot be included here as it contain ppc target specific dependency.
@@ -31,6 +34,1042 @@
} \
} while (0)
+/* PnvXferBuffer */
+typedef struct PnvXferBuffer {
+
+ uint32_t len;
+ uint8_t *data;
+
+} PnvXferBuffer;
+
+/* pnv_spi_xfer_buffer_methods */
+static PnvXferBuffer *pnv_spi_xfer_buffer_new(void)
+{
+ PnvXferBuffer *payload = g_malloc0(sizeof(*payload));
+
+ return payload;
+}
+
+static void pnv_spi_xfer_buffer_free(PnvXferBuffer *payload)
+{
+ free(payload->data);
+ free(payload);
+}
+
+static uint8_t *pnv_spi_xfer_buffer_write_ptr(PnvXferBuffer *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];
+}
+
+static bool does_rdr_match(PnvSpi *s)
+{
+ /*
+ * 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(SPI_MM_RDR_MATCH_MASK,
+ s->regs[SPI_MM_REG]);
+ uint16_t rdr_match_val = GETFIELD(SPI_MM_RDR_MATCH_VAL,
+ s->regs[SPI_MM_REG]);
+
+ if ((~rdr_match_mask & rdr_match_val) == ((~rdr_match_mask) &
+ GETFIELD(PPC_BITMASK(48, 63), s->regs[SPI_RCV_DATA_REG]))) {
+ return true;
+ }
+ return false;
+}
+
+static uint8_t get_from_offset(PnvSpi *s, uint8_t offset)
+{
+ uint8_t byte;
+
+ /*
+ * Offset is an index between 0 and PNV_SPI_REG_SIZE - 1
+ * Check the offset before using it.
+ */
+ if (offset < PNV_SPI_REG_SIZE) {
+ byte = (s->regs[SPI_XMIT_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;
+}
+
+static uint8_t read_from_frame(PnvSpi *s, uint8_t *read_buf, uint8_t nr_bytes,
+ uint8_t ecc_count, uint8_t shift_in_count)
+{
+ uint8_t byte;
+ int count = 0;
+
+ while (count < nr_bytes) {
+ shift_in_count++;
+ if ((ecc_count != 0) &&
+ (shift_in_count == (PNV_SPI_REG_SIZE + ecc_count))) {
+ shift_in_count = 0;
+ } else {
+ byte = read_buf[count];
+ trace_pnv_spi_shift_rx(byte, count);
+ s->regs[SPI_RCV_DATA_REG] = (s->regs[SPI_RCV_DATA_REG] << 8) | byte;
+ }
+ count++;
+ } /* end of while */
+ return shift_in_count;
+}
+
+static void spi_response(PnvSpi *s, int bits, PnvXferBuffer *rsp_payload)
+{
+ 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 != (s->N1_bytes + s->N2_bytes)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Invalid response payload size in "
+ "bytes, expected %d, got %d\n",
+ (s->N1_bytes + s->N2_bytes), rsp_payload->len);
+ } else {
+ uint8_t ecc_control;
+ trace_pnv_spi_rx_received(rsp_payload->len);
+ trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx,
+ s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx);
+ /*
+ * 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 of
+ * clock_config_reset_control register 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(SPI_CLK_CFG_ECC_CTRL, s->regs[SPI_CLK_CFG_REG]);
+ 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 (s->N1_rx != 0) {
+ trace_pnv_spi_rx_read_N1frame();
+ shift_in_count = read_from_frame(s, &rsp_payload->data[0],
+ s->N1_bytes, ecc_count, shift_in_count);
+ }
+ /* Handle the N2 portion of the frame */
+ if (s->N2_rx != 0) {
+ trace_pnv_spi_rx_read_N2frame();
+ shift_in_count = read_from_frame(s,
+ &rsp_payload->data[s->N1_bytes], s->N2_bytes,
+ ecc_count, shift_in_count);
+ }
+ if ((s->N1_rx + s->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(SPI_STS_RDR_FULL, s->status) == 1) {
+ /*
+ * Data was shifted into the RDR before having been read
+ * causing previous data to have been overrun.
+ */
+ s->status = SETFIELD(SPI_STS_RDR_OVERRUN, s->status, 1);
+ } else {
+ /*
+ * Set status to indicate that the received data register is
+ * full. This flag is only cleared once the RDR is unloaded.
+ */
+ s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 1);
+ }
+ }
+ } /* end of else */
+} /* end of spi_response() */
+
+static void transfer(PnvSpi *s, PnvXferBuffer *payload)
+{
+ uint32_t tx;
+ uint32_t rx;
+ PnvXferBuffer *rsp_payload = NULL;
+
+ rsp_payload = pnv_spi_xfer_buffer_new();
+ for (int offset = 0; offset < payload->len; offset += s->transfer_len) {
+ tx = 0;
+ for (int i = 0; i < s->transfer_len; i++) {
+ if ((offset + i) >= payload->len) {
+ tx <<= 8;
+ } else {
+ tx = (tx << 8) | payload->data[offset + i];
+ }
+ }
+ rx = ssi_transfer(s->ssi_bus, tx);
+ for (int i = 0; i < s->transfer_len; i++) {
+ if ((offset + i) >= payload->len) {
+ break;
+ }
+ *(pnv_spi_xfer_buffer_write_ptr(rsp_payload, rsp_payload->len, 1)) =
+ (rx >> (8 * (s->transfer_len - 1) - i * 8)) & 0xFF;
+ }
+ }
+ if (rsp_payload != NULL) {
+ spi_response(s, s->N1_bits, rsp_payload);
+ pnv_spi_xfer_buffer_free(rsp_payload);
+ }
+}
+
+static inline uint8_t get_seq_index(PnvSpi *s)
+{
+ return GETFIELD(SPI_STS_SEQ_INDEX, s->status);
+}
+
+static inline void next_sequencer_fsm(PnvSpi *s)
+{
+ uint8_t seq_index = get_seq_index(s);
+ s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, (seq_index + 1));
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_INDEX_INCREMENT);
+}
+
+/*
+ * 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
+ */
+static void calculate_N1(PnvSpi *s, 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 (PNV_SPI_OPCODE_LO_NIBBLE(opcode) == 0) {
+ /* Explicit mode */
+ s->N1_bits = GETFIELD(SPI_CTR_CFG_N1, s->regs[SPI_CTR_CFG_REG]);
+ s->N1_bytes = (s->N1_bits + 7) / 8;
+ s->N1_tx = 0;
+ s->N1_rx = 0;
+ /* If tx count control for N1 is set, load the tx value */
+ if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 1) {
+ s->N1_tx = s->N1_bytes;
+ }
+ /* If rx count control for N1 is set, load the rx value */
+ if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 1) {
+ s->N1_rx = s->N1_bytes;
+ }
+ } else {
+ /* Implicit mode/Forced Implicit mode, use M field from opcode */
+ s->N1_bytes = PNV_SPI_OPCODE_LO_NIBBLE(opcode);
+ s->N1_bits = s->N1_bytes * 8;
+ /*
+ * Assume that we are going to transmit the count
+ * (pure Implicit only)
+ */
+ s->N1_tx = s->N1_bytes;
+ s->N1_rx = 0;
+ /* Let Forced Implicit mode have an effect on the counts */
+ if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B1, s->regs[SPI_CTR_CFG_REG]) == 1) {
+ /*
+ * If Forced Implicit mode and count control doesn't
+ * indicate transmit then reset the tx count to 0
+ */
+ if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B2,
+ s->regs[SPI_CTR_CFG_REG]) == 0) {
+ s->N1_tx = 0;
+ }
+ /* If rx count control for N1 is set, load the rx value */
+ if (GETFIELD(SPI_CTR_CFG_N1_CTRL_B3,
+ s->regs[SPI_CTR_CFG_REG]) == 1) {
+ s->N1_rx = s->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(SPI_CLK_CFG_ECC_CTRL,
+ s->regs[SPI_CLK_CFG_REG]);
+ if (ecc_control == 0 || ecc_control == 2) {
+ if (s->N1_bytes > (PNV_SPI_REG_SIZE + 1)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size when "
+ "ECC enabled, bytes = 0x%x, bits = 0x%x\n",
+ s->N1_bytes, s->N1_bits);
+ s->N1_bytes = PNV_SPI_REG_SIZE + 1;
+ s->N1_bits = s->N1_bytes * 8;
+ }
+ } else if (s->N1_bytes > PNV_SPI_REG_SIZE) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Unsupported N1 shift size, "
+ "bytes = 0x%x, bits = 0x%x\n",
+ s->N1_bytes, s->N1_bits);
+ s->N1_bytes = PNV_SPI_REG_SIZE;
+ s->N1_bits = s->N1_bytes * 8;
+ }
+} /* end of calculate_N1 */
+
+/*
+ * Shift_N1 operation handler method
+ */
+static bool operation_shiftn1(PnvSpi *s, uint8_t opcode,
+ PnvXferBuffer **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) {
+ *payload = pnv_spi_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(s, opcode);
+ trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx,
+ s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx);
+ /*
+ * 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.
+ */
+ s->N2_bits = 0;
+ s->N2_bytes = 0;
+ s->N2_tx = 0;
+ s->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 < s->N1_bytes) {
+ /*
+ * Assuming that if N1_tx is not equal to 0 then it is the same as
+ * N1_bytes.
+ */
+ if ((s->N1_tx != 0) && (n1_count < PNV_SPI_REG_SIZE)) {
+
+ if (GETFIELD(SPI_STS_TDR_FULL, s->status) == 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(s, n1_count);
+ trace_pnv_spi_tx_append("n1_byte", n1_byte, n1_count);
+ *(pnv_spi_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.
+ */
+ trace_pnv_spi_sequencer_stop_requested("Shift N1"
+ "set for transmit but TDR is empty");
+ 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 ((s->N1_rx != 0) && (GETFIELD(SPI_STS_RDR_FULL,
+ s->status) == 1)) {
+ trace_pnv_spi_sequencer_stop_requested("shift N1"
+ "set for receive but RDR is full");
+ stop = true;
+ break;
+ } else {
+ trace_pnv_spi_tx_append_FF("n1_byte");
+ *(pnv_spi_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 && (s->N1_tx != 0) &&
+ (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) {
+ s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 0);
+ }
+ /*
+ * 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(SPI_CTR_CFG_N2_CTRL_B0, s->regs[SPI_CTR_CFG_REG]) == 1)) {
+ trace_pnv_spi_sequencer_stop_requested("N2 counter reload "
+ "active, stop N1 shift, TDR_underrun set to 1");
+ stop = true;
+ s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 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) {
+ /* We have a TX and a full TDR or an RX and an empty RDR */
+ trace_pnv_spi_tx_request("Shifting N1 frame", (*payload)->len);
+ transfer(s, *payload);
+ /* The N1 frame shift is complete so reset the N1 counters */
+ s->N2_bits = 0;
+ s->N2_bytes = 0;
+ s->N2_tx = 0;
+ s->N2_rx = 0;
+ pnv_spi_xfer_buffer_free(*payload);
+ *payload = NULL;
+ }
+ 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
+ */
+static void calculate_N2(PnvSpi *s, 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 (PNV_SPI_OPCODE_LO_NIBBLE(opcode) == 0) {
+ /* Explicit mode */
+ s->N2_bits = GETFIELD(SPI_CTR_CFG_N2, s->regs[SPI_CTR_CFG_REG]);
+ s->N2_bytes = (s->N2_bits + 7) / 8;
+ s->N2_tx = 0;
+ s->N2_rx = 0;
+ /* If tx count control for N2 is set, load the tx value */
+ if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2, s->regs[SPI_CTR_CFG_REG]) == 1) {
+ s->N2_tx = s->N2_bytes;
+ }
+ /* If rx count control for N2 is set, load the rx value */
+ if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3, s->regs[SPI_CTR_CFG_REG]) == 1) {
+ s->N2_rx = s->N2_bytes;
+ }
+ } else {
+ /* Implicit mode/Forced Implicit mode, use M field from opcode */
+ s->N2_bytes = PNV_SPI_OPCODE_LO_NIBBLE(opcode);
+ s->N2_bits = s->N2_bytes * 8;
+ /* Assume that we are going to receive the count */
+ s->N2_rx = s->N2_bytes;
+ s->N2_tx = 0;
+ /* Let Forced Implicit mode have an effect on the counts */
+ if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B1, s->regs[SPI_CTR_CFG_REG]) == 1) {
+ /*
+ * If Forced Implicit mode and count control doesn't
+ * indicate a receive then reset the rx count to 0
+ */
+ if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B3,
+ s->regs[SPI_CTR_CFG_REG]) == 0) {
+ s->N2_rx = 0;
+ }
+ /* If tx count control for N2 is set, load the tx value */
+ if (GETFIELD(SPI_CTR_CFG_N2_CTRL_B2,
+ s->regs[SPI_CTR_CFG_REG]) == 1) {
+ s->N2_tx = s->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(SPI_CLK_CFG_ECC_CTRL,
+ s->regs[SPI_CLK_CFG_REG]);
+ if (ecc_control == 0 || ecc_control == 2) {
+ if (s->N2_bytes > (PNV_SPI_REG_SIZE + 1)) {
+ /* Unsupported N2 shift size when ECC enabled */
+ s->N2_bytes = PNV_SPI_REG_SIZE + 1;
+ s->N2_bits = s->N2_bytes * 8;
+ }
+ } else if (s->N2_bytes > PNV_SPI_REG_SIZE) {
+ /* Unsupported N2 shift size */
+ s->N2_bytes = PNV_SPI_REG_SIZE;
+ s->N2_bits = s->N2_bytes * 8;
+ }
+} /* end of calculate_N2 */
+
+/*
+ * Shift_N2 operation handler method
+ */
+
+static bool operation_shiftn2(PnvSpi *s, uint8_t opcode,
+ PnvXferBuffer **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) {
+ *payload = pnv_spi_xfer_buffer_new();
+ }
+ /*
+ * Use a combination of N2 counters to build the N2 portion of the
+ * transmit payload.
+ */
+ calculate_N2(s, opcode);
+ trace_pnv_spi_log_Ncounts(s->N1_bits, s->N1_bytes, s->N1_tx,
+ s->N1_rx, s->N2_bits, s->N2_bytes, s->N2_tx, s->N2_rx);
+ /*
+ * 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 < s->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 ((s->N2_rx != 0) &&
+ (GETFIELD(SPI_STS_RDR_FULL, s->status) == 1)) {
+ trace_pnv_spi_sequencer_stop_requested("shift N2 set"
+ "for receive but RDR is full");
+ stop = true;
+ break;
+ }
+ if ((s->N2_tx != 0) && ((s->N1_tx + n2_count) <
+ PNV_SPI_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(s, (s->N1_tx + n2_count));
+ trace_pnv_spi_tx_append("n2_byte", n2_byte, (s->N1_tx + n2_count));
+ *(pnv_spi_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.
+ */
+ trace_pnv_spi_tx_append_FF("n2_byte");
+ *(pnv_spi_xfer_buffer_write_ptr(*payload, (*payload)->len, 1))
+ = 0xff;
+ }
+ n2_count++;
+ } /* end of while */
+ if (!stop) {
+ /* We have a TX and a full TDR or an RX and an empty RDR */
+ trace_pnv_spi_tx_request("Shifting N2 frame", (*payload)->len);
+ transfer(s, *payload);
+ /*
+ * 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 ((s->N2_tx != 0) &&
+ (GETFIELD(SPI_STS_TDR_FULL, s->status) == 1)) {
+ s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 0);
+ }
+ /*
+ * 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.
+ */
+ s->N2_bits = 0;
+ s->N2_bytes = 0;
+ s->N2_tx = 0;
+ s->N2_rx = 0;
+ s->N1_bits = 0;
+ s->N1_bytes = 0;
+ s->N1_tx = 0;
+ s->N1_rx = 0;
+ pnv_spi_xfer_buffer_free(*payload);
+ *payload = NULL;
+ }
+ return stop;
+} /* end of operation_shiftn2()*/
+
+static void operation_sequencer(PnvSpi *s)
+{
+ /*
+ * 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;
+
+ /*
+ * PnvXferBuffer 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 PnvXferBuffer *payload;
+
+ if (payload == NULL) {
+ payload = pnv_spi_xfer_buffer_new();
+ }
+ /*
+ * Clear the sequencer FSM error bit - general_SPI_status[3]
+ * before starting a sequence.
+ */
+ s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 0);
+ /*
+ * If the FSM is idle set the sequencer index to 0
+ * (new/restarted sequence)
+ */
+ if (GETFIELD(SPI_STS_SEQ_FSM, s->status) == SEQ_STATE_IDLE) {
+ s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0);
+ }
+ /*
+ * There are only 8 possible operation IDs to iterate through though
+ * some operations may cause more than one frame to be sequenced.
+ */
+ while (get_seq_index(s) < NUM_SEQ_OPS) {
+ opcode = s->seq_op[(get_seq_index(s) & 0x7)];
+ /* Set sequencer state to decode */
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_DECODE);
+ /*
+ * Only the upper nibble of the operation ID is needed to know what
+ * kind of operation is requested.
+ */
+ masked_opcode = PNV_SPI_MASKED_OPCODE(opcode);
+ 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:
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE);
+ /* A stop operation in any position stops the sequencer */
+ trace_pnv_spi_sequencer_op("STOP", get_seq_index(s));
+
+ stop = true;
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE);
+ s->loop_counter_1 = 0;
+ s->loop_counter_2 = 0;
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_IDLE);
+ break;
+
+ case SEQ_OP_SELECT_SLAVE:
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE);
+ trace_pnv_spi_sequencer_op("SELECT_SLAVE", get_seq_index(s));
+ /*
+ * 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.
+ */
+ s->responder_select = PNV_SPI_OPCODE_LO_NIBBLE(opcode);
+ if (s->responder_select == 0) {
+ trace_pnv_spi_shifter_done();
+ qemu_set_irq(s->cs_line[0], 1);
+ s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status,
+ (get_seq_index(s) + 1));
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_DONE);
+ } else if (s->responder_select != 1) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Slave selection other than 1 "
+ "not supported, select = 0x%x\n",
+ s->responder_select);
+ trace_pnv_spi_sequencer_stop_requested("invalid "
+ "responder select");
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE);
+ stop = true;
+ } else {
+ /*
+ * Only allow an FSM_START state when a responder is
+ * selected
+ */
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_START);
+ trace_pnv_spi_shifter_stating();
+ qemu_set_irq(s->cs_line[0], 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.
+ */
+ s->shift_n1_done = false;
+ next_sequencer_fsm(s);
+ }
+ break;
+
+ case SEQ_OP_SHIFT_N1:
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE);
+ trace_pnv_spi_sequencer_op("SHIFT_N1", get_seq_index(s));
+ /*
+ * 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(SPI_STS_SHIFTER_FSM, s->status) == FSM_IDLE) ||
+ (GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_DONE)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Shift_N1 not allowed in "
+ "shifter state = 0x%llx", GETFIELD(
+ SPI_STS_SHIFTER_FSM, s->status));
+ /*
+ * Set sequencer FSM error bit 3 (general_SPI_status[3])
+ * in status reg.
+ */
+ s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 1);
+ trace_pnv_spi_sequencer_stop_requested("invalid shifter state");
+ 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 (PNV_SPI_MASKED_OPCODE(s->seq_op[((get_seq_index(s) + 1) &
+ 0x7)])
+ == SEQ_OP_SHIFT_N2) {
+ send_n1_alone = false;
+ }
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status,
+ FSM_SHIFT_N1);
+ stop = operation_shiftn1(s, 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(SPI_STS_TDR_UNDERRUN, s->status)) {
+ s->shift_n1_done = true;
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status,
+ FSM_SHIFT_N2);
+ s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status,
+ (get_seq_index(s) + 1));
+ } else {
+ /*
+ * This is case (1) or (2) so the sequencer needs to
+ * wait and NOT go to the next sequence yet.
+ */
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status,
+ FSM_WAIT);
+ }
+ } else {
+ /* Ok to move on to the next index */
+ s->shift_n1_done = true;
+ next_sequencer_fsm(s);
+ }
+ }
+ break;
+
+ case SEQ_OP_SHIFT_N2:
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE);
+ trace_pnv_spi_sequencer_op("SHIFT_N2", get_seq_index(s));
+ if (!s->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(SPI_STS_SHIFTER_FSM, s->status));
+ /*
+ * 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.
+ */
+ s->status = SETFIELD(SPI_STS_GEN_STATUS_B3, s->status, 1);
+ trace_pnv_spi_sequencer_stop_requested("shift_n2 "
+ "w/no shift_n1 done");
+ stop = true;
+ } else {
+ /* Ok to do a Shift_N2 */
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status,
+ FSM_SHIFT_N2);
+ stop = operation_shiftn2(s, opcode, &payload);
+ /*
+ * If the operation code says to stop set the shifter state to
+ * wait and stop
+ */
+ if (stop) {
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status,
+ FSM_WAIT);
+ } else {
+ /* Ok to move on to the next index */
+ next_sequencer_fsm(s);
+ }
+ }
+ break;
+
+ case SEQ_OP_BRANCH_IFNEQ_RDR:
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE);
+ trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_RDR", get_seq_index(s));
+ /*
+ * 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(SPI_STS_RDR_FULL, s->status) == 1) {
+ bool rdr_matched = false;
+ rdr_matched = does_rdr_match(s);
+ if (rdr_matched) {
+ trace_pnv_spi_RDR_match("success");
+ /* A match occurred, increment the sequencer index. */
+ next_sequencer_fsm(s);
+ } else {
+ trace_pnv_spi_RDR_match("failed");
+ /*
+ * Branch the sequencer to the index coded into the op
+ * code.
+ */
+ s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status,
+ PNV_SPI_OPCODE_LO_NIBBLE(opcode));
+ }
+ /*
+ * Regardless of where the branch ended up we want the
+ * sequencer to continue shifting so we have to clear
+ * RDR_full.
+ */
+ s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0);
+ } else {
+ trace_pnv_spi_sequencer_stop_requested("RDR not"
+ "full for 0x6x opcode");
+ stop = true;
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_WAIT);
+ }
+ break;
+
+ case SEQ_OP_TRANSFER_TDR:
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE);
+ qemu_log_mask(LOG_GUEST_ERROR, "Transfer TDR is not supported\n");
+ next_sequencer_fsm(s);
+ break;
+
+ case SEQ_OP_BRANCH_IFNEQ_INC_1:
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE);
+ trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_1", get_seq_index(s));
+ /*
+ * 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 (s->loop_counter_1 !=
+ GETFIELD(SPI_CTR_CFG_CMP1, s->regs[SPI_CTR_CFG_REG])) {
+ /*
+ * 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.
+ */
+ s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status,
+ PNV_SPI_OPCODE_LO_NIBBLE(opcode));
+ s->loop_counter_1++;
+ } else {
+ /* Continue to next index if loop counter is reached */
+ next_sequencer_fsm(s);
+ }
+ break;
+
+ case SEQ_OP_BRANCH_IFNEQ_INC_2:
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE);
+ trace_pnv_spi_sequencer_op("BRANCH_IFNEQ_INC_2", get_seq_index(s));
+ uint8_t condition2 = GETFIELD(SPI_CTR_CFG_CMP2,
+ s->regs[SPI_CTR_CFG_REG]);
+ /*
+ * 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 (s->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.
+ */
+ s->status = SETFIELD(SPI_STS_SEQ_INDEX,
+ s->status, PNV_SPI_OPCODE_LO_NIBBLE(opcode));
+ s->loop_counter_2++;
+ } else {
+ /* Continue to next index if loop counter is reached */
+ next_sequencer_fsm(s);
+ }
+ break;
+
+ default:
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_EXECUTE);
+ /* Ignore unsupported operations. */
+ next_sequencer_fsm(s);
+ 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 (get_seq_index(s) == NUM_SEQ_OPS) {
+ /* All 8 opcodes completed, sequencer idling */
+ s->status = SETFIELD(SPI_STS_SHIFTER_FSM, s->status, FSM_IDLE);
+ s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, 0);
+ s->loop_counter_1 = 0;
+ s->loop_counter_2 = 0;
+ s->status = SETFIELD(SPI_STS_SEQ_FSM, s->status, SEQ_STATE_IDLE);
+ break;
+ }
+ /* Break the loop if a stop was requested */
+ if (stop) {
+ break;
+ }
+ } /* end of while */
+ return;
+} /* end of operation_sequencer() */
+
+/*
+ * 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)
+{
+ PnvSpi *s = PNV_SPI(dev);
+
+ trace_pnv_spi_reset();
+
+ /* Reset all N1 and N2 counters, and other constants */
+ s->N2_bits = 0;
+ s->N2_bytes = 0;
+ s->N2_tx = 0;
+ s->N2_rx = 0;
+ s->N1_bits = 0;
+ s->N1_bytes = 0;
+ s->N1_tx = 0;
+ s->N1_rx = 0;
+ s->loop_counter_1 = 0;
+ s->loop_counter_2 = 0;
+ /* Disconnected from responder */
+ qemu_set_irq(s->cs_line[0], 1);
+}
+
static uint64_t pnv_spi_xscom_read(void *opaque, hwaddr addr, unsigned size)
{
PnvSpi *s = PNV_SPI(opaque);
@@ -50,6 +1089,10 @@ static uint64_t pnv_spi_xscom_read(void *opaque, hwaddr addr, unsigned size)
val = s->regs[reg];
trace_pnv_spi_read_RDR(val);
s->status = SETFIELD(SPI_STS_RDR_FULL, s->status, 0);
+ if (GETFIELD(SPI_STS_SHIFTER_FSM, s->status) == FSM_WAIT) {
+ trace_pnv_spi_start_sequencer();
+ operation_sequencer(s);
+ }
break;
case SPI_SEQ_OP_REG:
val = 0;
@@ -111,6 +1154,8 @@ static void pnv_spi_xscom_write(void *opaque, hwaddr addr,
trace_pnv_spi_write_TDR(val);
s->status = SETFIELD(SPI_STS_TDR_FULL, s->status, 1);
s->status = SETFIELD(SPI_STS_TDR_UNDERRUN, s->status, 0);
+ trace_pnv_spi_start_sequencer();
+ operation_sequencer(s);
break;
case SPI_SEQ_OP_REG:
for (int i = 0; i < PNV_SPI_REG_SIZE; i++) {
@@ -143,6 +1188,7 @@ static const MemoryRegionOps pnv_spi_xscom_ops = {
static Property pnv_spi_properties[] = {
DEFINE_PROP_UINT32("spic_num", PnvSpi, spic_num, 0),
+ DEFINE_PROP_UINT8("transfer_len", PnvSpi, transfer_len, 4),
DEFINE_PROP_END_OF_LIST(),
};
@@ -192,6 +1238,7 @@ static void pnv_spi_class_init(ObjectClass *klass, void *data)
dc->desc = "PowerNV SPI";
dc->realize = pnv_spi_realize;
+ dc->reset = do_reset;
device_class_set_props(dc, pnv_spi_properties);
}
@@ -38,3 +38,18 @@ pnv_spi_read(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64
pnv_spi_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " val 0x%" PRIx64
pnv_spi_read_RDR(uint64_t val) "data extracted = 0x%" PRIx64
pnv_spi_write_TDR(uint64_t val) "being written, data written = 0x%" PRIx64
+pnv_spi_start_sequencer(void) ""
+pnv_spi_reset(void) "spic engine sequencer configuration and spi communication"
+pnv_spi_sequencer_op(const char* op, uint8_t index) "%s at index = 0x%x"
+pnv_spi_shifter_stating(void) "pull CS line low"
+pnv_spi_shifter_done(void) "pull the CS line high"
+pnv_spi_log_Ncounts(uint8_t N1_bits, uint8_t N1_bytes, uint8_t N1_tx, uint8_t N1_rx, uint8_t N2_bits, uint8_t N2_bytes, uint8_t N2_tx, uint8_t N2_rx) "N1_bits = %d, N1_bytes = %d, N1_tx = %d, N1_rx = %d, N2_bits = %d, N2_bytes = %d, N2_tx = %d, N2_rx = %d"
+pnv_spi_tx_append(const char* frame, uint8_t byte, uint8_t tdr_index) "%s = 0x%2.2x to payload from TDR at index %d"
+pnv_spi_tx_append_FF(const char* frame) "%s to Payload"
+pnv_spi_tx_request(const char* frame, uint32_t payload_len) "%s, payload len = %d"
+pnv_spi_rx_received(uint32_t payload_len) "payload len = %d"
+pnv_spi_rx_read_N1frame(void) ""
+pnv_spi_rx_read_N2frame(void) ""
+pnv_spi_shift_rx(uint8_t byte, uint32_t index) "byte = 0x%2.2x into RDR from payload index %d"
+pnv_spi_sequencer_stop_requested(const char* reason) "due to %s"
+pnv_spi_RDR_match(const char* result) "%s"