/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include /* SE_SPI_LOOPBACK register fields */ #define LOOPBACK_ENABLE 0x1 /* SE_SPI_WORD_LEN register fields */ #define WORD_LEN_MSK QC_GENMASK(9, 0) #define MIN_WORD_LEN 4 /* SPI_TX/SPI_RX_TRANS_LEN fields */ #define TRANS_LEN_MSK QC_GENMASK(23, 0) /* M_CMD OP codes for SPI */ #define SPI_TX_ONLY 1 #define SPI_RX_ONLY 2 #define SPI_FULL_DUPLEX 3 #define SPI_TX_RX 7 #define SPI_CS_ASSERT 8 #define SPI_CS_DEASSERT 9 #define SPI_SCK_ONLY 10 /* M_CMD params for SPI */ /* If fragmentation bit is set then CS will not toggle after each transfer */ #define M_CMD_FRAGMENTATION BIT(2) #define BITS_PER_BYTE 8 #define BITS_PER_WORD 8 #define TX_WATERMARK 1 #define IRQ_TRIGGER (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN | \ M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN | \ M_CMD_CANCEL_EN | M_CMD_ABORT_EN) static void setup_fifo_params(const struct spi_slave *slave) { unsigned int se_bus = slave->bus; struct qup_regs *regs = qup[se_bus].regs; u32 word_len = 0; /* Disable loopback mode */ write32(®s->proto_loopback_cfg, 0); write32(®s->spi_demux_sel, slave->cs); word_len = ((BITS_PER_WORD - MIN_WORD_LEN) & WORD_LEN_MSK); write32(®s->spi_word_len, word_len); /* FIFO PACKING CONFIGURATION */ write32(®s->geni_tx_packing_cfg0, PACK_VECTOR0 | (PACK_VECTOR1 << 10)); write32(®s->geni_tx_packing_cfg1, PACK_VECTOR2 | (PACK_VECTOR3 << 10)); write32(®s->geni_rx_packing_cfg0, PACK_VECTOR0 | (PACK_VECTOR1 << 10)); write32(®s->geni_rx_packing_cfg1, PACK_VECTOR2 | (PACK_VECTOR3 << 10)); write32(®s->geni_byte_granularity, (log2(BITS_PER_WORD) - 3)); } static void qup_setup_m_cmd(unsigned int se_bus, u32 cmd, u32 params) { struct qup_regs *regs = qup[se_bus].regs; u32 m_cmd = (cmd << M_OPCODE_SHFT); m_cmd |= (params & M_PARAMS_MSK); write32(®s->geni_m_cmd0, m_cmd); } int qup_spi_xfer(const struct spi_slave *slave, const void *dout, size_t bytes_out, void *din, size_t bytes_in) { u32 m_cmd = 0; u32 m_param = M_CMD_FRAGMENTATION; int size; unsigned int se_bus = slave->bus; struct qup_regs *regs = qup[se_bus].regs; if ((bytes_in == 0) && (bytes_out == 0)) return 0; setup_fifo_params(slave); if (!bytes_out) { size = bytes_in; m_cmd = SPI_RX_ONLY; dout = NULL; } else if (!bytes_in) { size = bytes_out; m_cmd = SPI_TX_ONLY; din = NULL; } else { size = MIN(bytes_in, bytes_out); m_cmd = SPI_FULL_DUPLEX; } /* Check for maximum permissible transfer length */ assert(!(size & ~TRANS_LEN_MSK)); if (bytes_out) { write32(®s->spi_tx_trans_len, size); write32(®s->geni_tx_watermark_reg, TX_WATERMARK); } if (bytes_in) write32(®s->spi_rx_trans_len, size); qup_setup_m_cmd(se_bus, m_cmd, m_param); if (qup_handle_transfer(se_bus, dout, din, size)) return -1; qup_spi_xfer(slave, dout + size, MAX((int)bytes_out - size, 0), din + size, MAX((int)bytes_in - size, 0)); return 0; } static int spi_qup_set_cs(const struct spi_slave *slave, bool enable) { u32 m_cmd = 0; u32 m_irq = 0; unsigned int se_bus = slave->bus; struct stopwatch sw; m_cmd = (enable) ? SPI_CS_ASSERT : SPI_CS_DEASSERT; qup_setup_m_cmd(se_bus, m_cmd, 0); stopwatch_init_usecs_expire(&sw, 100); do { m_irq = qup_wait_for_m_irq(se_bus); if (m_irq & M_CMD_DONE_EN) { write32(&qup[se_bus].regs->geni_m_irq_clear, m_irq); break; } write32(&qup[se_bus].regs->geni_m_irq_clear, m_irq); } while (!stopwatch_expired(&sw)); if (!(m_irq & M_CMD_DONE_EN)) { printk(BIOS_INFO, "%s:Failed to %s chip\n", __func__, (enable) ? "Assert" : "Deassert"); qup_m_cancel_and_abort(se_bus); return -1; } return 0; } void qup_spi_init(unsigned int bus, unsigned int speed_hz) { u32 m_clk_cfg = 0, div = DEFAULT_SE_CLK / speed_hz; struct qup_regs *regs = qup[bus].regs; /* Make sure div can hit target frequency within +/- 1KHz range */ assert(((DEFAULT_SE_CLK - speed_hz * div) <= div * KHz) && (div > 0)); qupv3_se_fw_load_and_init(bus, SE_PROTOCOL_SPI, MIXED); clock_enable_qup(bus); m_clk_cfg |= ((div << CLK_DIV_SHFT) | SER_CLK_EN); write32(®s->geni_ser_m_clk_cfg, m_clk_cfg); /* Mode:0, cpha=0, cpol=0 */ write32(®s->spi_cpha, 0); write32(®s->spi_cpol, 0); /* Serial engine IO initialization */ write32(®s->geni_cgc_ctrl, DEFAULT_CGC_EN); write32(®s->dma_general_cfg, (AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON | DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON)); write32(®s->geni_output_ctrl, DEFAULT_IO_OUTPUT_CTRL_MSK); write32(®s->geni_force_default_reg, FORCE_DEFAULT); /* Serial engine IO set mode */ write32(®s->se_irq_en, (GENI_M_IRQ_EN | GENI_S_IRQ_EN | DMA_TX_IRQ_EN | DMA_RX_IRQ_EN)); write32(®s->se_gsi_event_en, 0); /* Set RX and RFR watermark */ write32(®s->geni_rx_watermark_reg, 0); write32(®s->geni_rx_rfr_watermark_reg, FIFO_DEPTH - 2); /* GPIO Configuration */ gpio_configure(qup[bus].pin[0], qup[bus].func[0], GPIO_NO_PULL, GPIO_6MA, GPIO_INPUT); /* MISO */ gpio_configure(qup[bus].pin[1], qup[bus].func[1], GPIO_NO_PULL, GPIO_6MA, GPIO_OUTPUT); /* MOSI */ gpio_configure(qup[bus].pin[2], qup[bus].func[2], GPIO_NO_PULL, GPIO_6MA, GPIO_OUTPUT); /* CLK */ gpio_configure(qup[bus].pin[3], qup[bus].func[3], GPIO_NO_PULL, GPIO_6MA, GPIO_OUTPUT); /* CS */ /* Select and setup FIFO mode */ write32(®s->geni_m_irq_clear, 0xFFFFFFFF); write32(®s->geni_s_irq_clear, 0xFFFFFFFF); write32(®s->dma_tx_irq_clr, 0xFFFFFFFF); write32(®s->dma_rx_irq_clr, 0xFFFFFFFF); write32(®s->geni_m_irq_enable, (M_COMMON_GENI_M_IRQ_EN | M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN | M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN)); write32(®s->geni_s_irq_enable, (S_COMMON_GENI_S_IRQ_EN | S_CMD_DONE_EN)); clrbits32(®s->geni_dma_mode_en, GENI_DMA_MODE_EN); } int qup_spi_claim_bus(const struct spi_slave *slave) { return spi_qup_set_cs(slave, 1); } void qup_spi_release_bus(const struct spi_slave *slave) { spi_qup_set_cs(slave, 0); }