162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2008-2014, The Linux foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/err.h> 962306a36Sopenharmony_ci#include <linux/interrupt.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/list.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/platform_device.h> 1562306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1662306a36Sopenharmony_ci#include <linux/spi/spi.h> 1762306a36Sopenharmony_ci#include <linux/dmaengine.h> 1862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define QUP_CONFIG 0x0000 2162306a36Sopenharmony_ci#define QUP_STATE 0x0004 2262306a36Sopenharmony_ci#define QUP_IO_M_MODES 0x0008 2362306a36Sopenharmony_ci#define QUP_SW_RESET 0x000c 2462306a36Sopenharmony_ci#define QUP_OPERATIONAL 0x0018 2562306a36Sopenharmony_ci#define QUP_ERROR_FLAGS 0x001c 2662306a36Sopenharmony_ci#define QUP_ERROR_FLAGS_EN 0x0020 2762306a36Sopenharmony_ci#define QUP_OPERATIONAL_MASK 0x0028 2862306a36Sopenharmony_ci#define QUP_HW_VERSION 0x0030 2962306a36Sopenharmony_ci#define QUP_MX_OUTPUT_CNT 0x0100 3062306a36Sopenharmony_ci#define QUP_OUTPUT_FIFO 0x0110 3162306a36Sopenharmony_ci#define QUP_MX_WRITE_CNT 0x0150 3262306a36Sopenharmony_ci#define QUP_MX_INPUT_CNT 0x0200 3362306a36Sopenharmony_ci#define QUP_MX_READ_CNT 0x0208 3462306a36Sopenharmony_ci#define QUP_INPUT_FIFO 0x0218 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define SPI_CONFIG 0x0300 3762306a36Sopenharmony_ci#define SPI_IO_CONTROL 0x0304 3862306a36Sopenharmony_ci#define SPI_ERROR_FLAGS 0x0308 3962306a36Sopenharmony_ci#define SPI_ERROR_FLAGS_EN 0x030c 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* QUP_CONFIG fields */ 4262306a36Sopenharmony_ci#define QUP_CONFIG_SPI_MODE (1 << 8) 4362306a36Sopenharmony_ci#define QUP_CONFIG_CLOCK_AUTO_GATE BIT(13) 4462306a36Sopenharmony_ci#define QUP_CONFIG_NO_INPUT BIT(7) 4562306a36Sopenharmony_ci#define QUP_CONFIG_NO_OUTPUT BIT(6) 4662306a36Sopenharmony_ci#define QUP_CONFIG_N 0x001f 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* QUP_STATE fields */ 4962306a36Sopenharmony_ci#define QUP_STATE_VALID BIT(2) 5062306a36Sopenharmony_ci#define QUP_STATE_RESET 0 5162306a36Sopenharmony_ci#define QUP_STATE_RUN 1 5262306a36Sopenharmony_ci#define QUP_STATE_PAUSE 3 5362306a36Sopenharmony_ci#define QUP_STATE_MASK 3 5462306a36Sopenharmony_ci#define QUP_STATE_CLEAR 2 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define QUP_HW_VERSION_2_1_1 0x20010001 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* QUP_IO_M_MODES fields */ 5962306a36Sopenharmony_ci#define QUP_IO_M_PACK_EN BIT(15) 6062306a36Sopenharmony_ci#define QUP_IO_M_UNPACK_EN BIT(14) 6162306a36Sopenharmony_ci#define QUP_IO_M_INPUT_MODE_MASK_SHIFT 12 6262306a36Sopenharmony_ci#define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT 10 6362306a36Sopenharmony_ci#define QUP_IO_M_INPUT_MODE_MASK (3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT) 6462306a36Sopenharmony_ci#define QUP_IO_M_OUTPUT_MODE_MASK (3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT) 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define QUP_IO_M_OUTPUT_BLOCK_SIZE(x) (((x) & (0x03 << 0)) >> 0) 6762306a36Sopenharmony_ci#define QUP_IO_M_OUTPUT_FIFO_SIZE(x) (((x) & (0x07 << 2)) >> 2) 6862306a36Sopenharmony_ci#define QUP_IO_M_INPUT_BLOCK_SIZE(x) (((x) & (0x03 << 5)) >> 5) 6962306a36Sopenharmony_ci#define QUP_IO_M_INPUT_FIFO_SIZE(x) (((x) & (0x07 << 7)) >> 7) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define QUP_IO_M_MODE_FIFO 0 7262306a36Sopenharmony_ci#define QUP_IO_M_MODE_BLOCK 1 7362306a36Sopenharmony_ci#define QUP_IO_M_MODE_DMOV 2 7462306a36Sopenharmony_ci#define QUP_IO_M_MODE_BAM 3 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* QUP_OPERATIONAL fields */ 7762306a36Sopenharmony_ci#define QUP_OP_IN_BLOCK_READ_REQ BIT(13) 7862306a36Sopenharmony_ci#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12) 7962306a36Sopenharmony_ci#define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) 8062306a36Sopenharmony_ci#define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) 8162306a36Sopenharmony_ci#define QUP_OP_IN_SERVICE_FLAG BIT(9) 8262306a36Sopenharmony_ci#define QUP_OP_OUT_SERVICE_FLAG BIT(8) 8362306a36Sopenharmony_ci#define QUP_OP_IN_FIFO_FULL BIT(7) 8462306a36Sopenharmony_ci#define QUP_OP_OUT_FIFO_FULL BIT(6) 8562306a36Sopenharmony_ci#define QUP_OP_IN_FIFO_NOT_EMPTY BIT(5) 8662306a36Sopenharmony_ci#define QUP_OP_OUT_FIFO_NOT_EMPTY BIT(4) 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */ 8962306a36Sopenharmony_ci#define QUP_ERROR_OUTPUT_OVER_RUN BIT(5) 9062306a36Sopenharmony_ci#define QUP_ERROR_INPUT_UNDER_RUN BIT(4) 9162306a36Sopenharmony_ci#define QUP_ERROR_OUTPUT_UNDER_RUN BIT(3) 9262306a36Sopenharmony_ci#define QUP_ERROR_INPUT_OVER_RUN BIT(2) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* SPI_CONFIG fields */ 9562306a36Sopenharmony_ci#define SPI_CONFIG_HS_MODE BIT(10) 9662306a36Sopenharmony_ci#define SPI_CONFIG_INPUT_FIRST BIT(9) 9762306a36Sopenharmony_ci#define SPI_CONFIG_LOOPBACK BIT(8) 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci/* SPI_IO_CONTROL fields */ 10062306a36Sopenharmony_ci#define SPI_IO_C_FORCE_CS BIT(11) 10162306a36Sopenharmony_ci#define SPI_IO_C_CLK_IDLE_HIGH BIT(10) 10262306a36Sopenharmony_ci#define SPI_IO_C_MX_CS_MODE BIT(8) 10362306a36Sopenharmony_ci#define SPI_IO_C_CS_N_POLARITY_0 BIT(4) 10462306a36Sopenharmony_ci#define SPI_IO_C_CS_SELECT(x) (((x) & 3) << 2) 10562306a36Sopenharmony_ci#define SPI_IO_C_CS_SELECT_MASK 0x000c 10662306a36Sopenharmony_ci#define SPI_IO_C_TRISTATE_CS BIT(1) 10762306a36Sopenharmony_ci#define SPI_IO_C_NO_TRI_STATE BIT(0) 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */ 11062306a36Sopenharmony_ci#define SPI_ERROR_CLK_OVER_RUN BIT(1) 11162306a36Sopenharmony_ci#define SPI_ERROR_CLK_UNDER_RUN BIT(0) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define SPI_NUM_CHIPSELECTS 4 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define SPI_MAX_XFER (SZ_64K - 64) 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* high speed mode is when bus rate is greater then 26MHz */ 11862306a36Sopenharmony_ci#define SPI_HS_MIN_RATE 26000000 11962306a36Sopenharmony_ci#define SPI_MAX_RATE 50000000 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define SPI_DELAY_THRESHOLD 1 12262306a36Sopenharmony_ci#define SPI_DELAY_RETRY 10 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct spi_qup { 12562306a36Sopenharmony_ci void __iomem *base; 12662306a36Sopenharmony_ci struct device *dev; 12762306a36Sopenharmony_ci struct clk *cclk; /* core clock */ 12862306a36Sopenharmony_ci struct clk *iclk; /* interface clock */ 12962306a36Sopenharmony_ci int irq; 13062306a36Sopenharmony_ci spinlock_t lock; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci int in_fifo_sz; 13362306a36Sopenharmony_ci int out_fifo_sz; 13462306a36Sopenharmony_ci int in_blk_sz; 13562306a36Sopenharmony_ci int out_blk_sz; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci struct spi_transfer *xfer; 13862306a36Sopenharmony_ci struct completion done; 13962306a36Sopenharmony_ci int error; 14062306a36Sopenharmony_ci int w_size; /* bytes per SPI word */ 14162306a36Sopenharmony_ci int n_words; 14262306a36Sopenharmony_ci int tx_bytes; 14362306a36Sopenharmony_ci int rx_bytes; 14462306a36Sopenharmony_ci const u8 *tx_buf; 14562306a36Sopenharmony_ci u8 *rx_buf; 14662306a36Sopenharmony_ci int qup_v1; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci int mode; 14962306a36Sopenharmony_ci struct dma_slave_config rx_conf; 15062306a36Sopenharmony_ci struct dma_slave_config tx_conf; 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci return (opflag & flag) != 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic inline bool spi_qup_is_dma_xfer(int mode) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM) 16562306a36Sopenharmony_ci return true; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return false; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci/* get's the transaction size length */ 17162306a36Sopenharmony_cistatic inline unsigned int spi_qup_len(struct spi_qup *controller) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci return controller->n_words * controller->w_size; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic inline bool spi_qup_is_valid_state(struct spi_qup *controller) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci u32 opstate = readl_relaxed(controller->base + QUP_STATE); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return opstate & QUP_STATE_VALID; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int spi_qup_set_state(struct spi_qup *controller, u32 state) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci unsigned long loop; 18662306a36Sopenharmony_ci u32 cur_state; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci loop = 0; 18962306a36Sopenharmony_ci while (!spi_qup_is_valid_state(controller)) { 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (++loop > SPI_DELAY_RETRY) 19462306a36Sopenharmony_ci return -EIO; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (loop) 19862306a36Sopenharmony_ci dev_dbg(controller->dev, "invalid state for %ld,us %d\n", 19962306a36Sopenharmony_ci loop, state); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci cur_state = readl_relaxed(controller->base + QUP_STATE); 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * Per spec: for PAUSE_STATE to RESET_STATE, two writes 20462306a36Sopenharmony_ci * of (b10) are required 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) && 20762306a36Sopenharmony_ci (state == QUP_STATE_RESET)) { 20862306a36Sopenharmony_ci writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); 20962306a36Sopenharmony_ci writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE); 21062306a36Sopenharmony_ci } else { 21162306a36Sopenharmony_ci cur_state &= ~QUP_STATE_MASK; 21262306a36Sopenharmony_ci cur_state |= state; 21362306a36Sopenharmony_ci writel_relaxed(cur_state, controller->base + QUP_STATE); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci loop = 0; 21762306a36Sopenharmony_ci while (!spi_qup_is_valid_state(controller)) { 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci if (++loop > SPI_DELAY_RETRY) 22262306a36Sopenharmony_ci return -EIO; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void spi_qup_read_from_fifo(struct spi_qup *controller, u32 num_words) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci u8 *rx_buf = controller->rx_buf; 23162306a36Sopenharmony_ci int i, shift, num_bytes; 23262306a36Sopenharmony_ci u32 word; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci for (; num_words; num_words--) { 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci word = readl_relaxed(controller->base + QUP_INPUT_FIFO); 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci num_bytes = min_t(int, spi_qup_len(controller) - 23962306a36Sopenharmony_ci controller->rx_bytes, 24062306a36Sopenharmony_ci controller->w_size); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!rx_buf) { 24362306a36Sopenharmony_ci controller->rx_bytes += num_bytes; 24462306a36Sopenharmony_ci continue; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci for (i = 0; i < num_bytes; i++, controller->rx_bytes++) { 24862306a36Sopenharmony_ci /* 24962306a36Sopenharmony_ci * The data format depends on bytes per SPI word: 25062306a36Sopenharmony_ci * 4 bytes: 0x12345678 25162306a36Sopenharmony_ci * 2 bytes: 0x00001234 25262306a36Sopenharmony_ci * 1 byte : 0x00000012 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci shift = BITS_PER_BYTE; 25562306a36Sopenharmony_ci shift *= (controller->w_size - i - 1); 25662306a36Sopenharmony_ci rx_buf[controller->rx_bytes] = word >> shift; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistatic void spi_qup_read(struct spi_qup *controller, u32 *opflags) 26262306a36Sopenharmony_ci{ 26362306a36Sopenharmony_ci u32 remainder, words_per_block, num_words; 26462306a36Sopenharmony_ci bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->rx_bytes, 26762306a36Sopenharmony_ci controller->w_size); 26862306a36Sopenharmony_ci words_per_block = controller->in_blk_sz >> 2; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci do { 27162306a36Sopenharmony_ci /* ACK by clearing service flag */ 27262306a36Sopenharmony_ci writel_relaxed(QUP_OP_IN_SERVICE_FLAG, 27362306a36Sopenharmony_ci controller->base + QUP_OPERATIONAL); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (!remainder) 27662306a36Sopenharmony_ci goto exit; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (is_block_mode) { 27962306a36Sopenharmony_ci num_words = (remainder > words_per_block) ? 28062306a36Sopenharmony_ci words_per_block : remainder; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci if (!spi_qup_is_flag_set(controller, 28362306a36Sopenharmony_ci QUP_OP_IN_FIFO_NOT_EMPTY)) 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci num_words = 1; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* read up to the maximum transfer size available */ 29062306a36Sopenharmony_ci spi_qup_read_from_fifo(controller, num_words); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci remainder -= num_words; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* if block mode, check to see if next block is available */ 29562306a36Sopenharmony_ci if (is_block_mode && !spi_qup_is_flag_set(controller, 29662306a36Sopenharmony_ci QUP_OP_IN_BLOCK_READ_REQ)) 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci } while (remainder); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block 30362306a36Sopenharmony_ci * reads, it has to be cleared again at the very end. However, be sure 30462306a36Sopenharmony_ci * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be 30562306a36Sopenharmony_ci * present and this is used to determine if transaction is complete 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ciexit: 30862306a36Sopenharmony_ci if (!remainder) { 30962306a36Sopenharmony_ci *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); 31062306a36Sopenharmony_ci if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG) 31162306a36Sopenharmony_ci writel_relaxed(QUP_OP_IN_SERVICE_FLAG, 31262306a36Sopenharmony_ci controller->base + QUP_OPERATIONAL); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci const u8 *tx_buf = controller->tx_buf; 31962306a36Sopenharmony_ci int i, num_bytes; 32062306a36Sopenharmony_ci u32 word, data; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci for (; num_words; num_words--) { 32362306a36Sopenharmony_ci word = 0; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci num_bytes = min_t(int, spi_qup_len(controller) - 32662306a36Sopenharmony_ci controller->tx_bytes, 32762306a36Sopenharmony_ci controller->w_size); 32862306a36Sopenharmony_ci if (tx_buf) 32962306a36Sopenharmony_ci for (i = 0; i < num_bytes; i++) { 33062306a36Sopenharmony_ci data = tx_buf[controller->tx_bytes + i]; 33162306a36Sopenharmony_ci word |= data << (BITS_PER_BYTE * (3 - i)); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci controller->tx_bytes += num_bytes; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic void spi_qup_dma_done(void *data) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct spi_qup *qup = data; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci complete(&qup->done); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void spi_qup_write(struct spi_qup *controller) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK; 35062306a36Sopenharmony_ci u32 remainder, words_per_block, num_words; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->tx_bytes, 35362306a36Sopenharmony_ci controller->w_size); 35462306a36Sopenharmony_ci words_per_block = controller->out_blk_sz >> 2; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci do { 35762306a36Sopenharmony_ci /* ACK by clearing service flag */ 35862306a36Sopenharmony_ci writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, 35962306a36Sopenharmony_ci controller->base + QUP_OPERATIONAL); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* make sure the interrupt is valid */ 36262306a36Sopenharmony_ci if (!remainder) 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (is_block_mode) { 36662306a36Sopenharmony_ci num_words = (remainder > words_per_block) ? 36762306a36Sopenharmony_ci words_per_block : remainder; 36862306a36Sopenharmony_ci } else { 36962306a36Sopenharmony_ci if (spi_qup_is_flag_set(controller, 37062306a36Sopenharmony_ci QUP_OP_OUT_FIFO_FULL)) 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci num_words = 1; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci spi_qup_write_to_fifo(controller, num_words); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci remainder -= num_words; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* if block mode, check to see if next block is available */ 38162306a36Sopenharmony_ci if (is_block_mode && !spi_qup_is_flag_set(controller, 38262306a36Sopenharmony_ci QUP_OP_OUT_BLOCK_WRITE_REQ)) 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci } while (remainder); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int spi_qup_prep_sg(struct spi_controller *host, struct scatterlist *sgl, 38962306a36Sopenharmony_ci unsigned int nents, enum dma_transfer_direction dir, 39062306a36Sopenharmony_ci dma_async_tx_callback callback) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct spi_qup *qup = spi_controller_get_devdata(host); 39362306a36Sopenharmony_ci unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE; 39462306a36Sopenharmony_ci struct dma_async_tx_descriptor *desc; 39562306a36Sopenharmony_ci struct dma_chan *chan; 39662306a36Sopenharmony_ci dma_cookie_t cookie; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (dir == DMA_MEM_TO_DEV) 39962306a36Sopenharmony_ci chan = host->dma_tx; 40062306a36Sopenharmony_ci else 40162306a36Sopenharmony_ci chan = host->dma_rx; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags); 40462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(desc)) 40562306a36Sopenharmony_ci return desc ? PTR_ERR(desc) : -EINVAL; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci desc->callback = callback; 40862306a36Sopenharmony_ci desc->callback_param = qup; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci cookie = dmaengine_submit(desc); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return dma_submit_error(cookie); 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void spi_qup_dma_terminate(struct spi_controller *host, 41662306a36Sopenharmony_ci struct spi_transfer *xfer) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci if (xfer->tx_buf) 41962306a36Sopenharmony_ci dmaengine_terminate_all(host->dma_tx); 42062306a36Sopenharmony_ci if (xfer->rx_buf) 42162306a36Sopenharmony_ci dmaengine_terminate_all(host->dma_rx); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic u32 spi_qup_sgl_get_nents_len(struct scatterlist *sgl, u32 max, 42562306a36Sopenharmony_ci u32 *nents) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct scatterlist *sg; 42862306a36Sopenharmony_ci u32 total = 0; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci for (sg = sgl; sg; sg = sg_next(sg)) { 43162306a36Sopenharmony_ci unsigned int len = sg_dma_len(sg); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* check for overflow as well as limit */ 43462306a36Sopenharmony_ci if (((total + len) < total) || ((total + len) > max)) 43562306a36Sopenharmony_ci break; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci total += len; 43862306a36Sopenharmony_ci (*nents)++; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return total; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer, 44562306a36Sopenharmony_ci unsigned long timeout) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci dma_async_tx_callback rx_done = NULL, tx_done = NULL; 44862306a36Sopenharmony_ci struct spi_controller *host = spi->controller; 44962306a36Sopenharmony_ci struct spi_qup *qup = spi_controller_get_devdata(host); 45062306a36Sopenharmony_ci struct scatterlist *tx_sgl, *rx_sgl; 45162306a36Sopenharmony_ci int ret; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (xfer->rx_buf) 45462306a36Sopenharmony_ci rx_done = spi_qup_dma_done; 45562306a36Sopenharmony_ci else if (xfer->tx_buf) 45662306a36Sopenharmony_ci tx_done = spi_qup_dma_done; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci rx_sgl = xfer->rx_sg.sgl; 45962306a36Sopenharmony_ci tx_sgl = xfer->tx_sg.sgl; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci do { 46262306a36Sopenharmony_ci u32 rx_nents = 0, tx_nents = 0; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (rx_sgl) 46562306a36Sopenharmony_ci qup->n_words = spi_qup_sgl_get_nents_len(rx_sgl, 46662306a36Sopenharmony_ci SPI_MAX_XFER, &rx_nents) / qup->w_size; 46762306a36Sopenharmony_ci if (tx_sgl) 46862306a36Sopenharmony_ci qup->n_words = spi_qup_sgl_get_nents_len(tx_sgl, 46962306a36Sopenharmony_ci SPI_MAX_XFER, &tx_nents) / qup->w_size; 47062306a36Sopenharmony_ci if (!qup->n_words) 47162306a36Sopenharmony_ci return -EIO; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci ret = spi_qup_io_config(spi, xfer); 47462306a36Sopenharmony_ci if (ret) 47562306a36Sopenharmony_ci return ret; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* before issuing the descriptors, set the QUP to run */ 47862306a36Sopenharmony_ci ret = spi_qup_set_state(qup, QUP_STATE_RUN); 47962306a36Sopenharmony_ci if (ret) { 48062306a36Sopenharmony_ci dev_warn(qup->dev, "cannot set RUN state\n"); 48162306a36Sopenharmony_ci return ret; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci if (rx_sgl) { 48462306a36Sopenharmony_ci ret = spi_qup_prep_sg(host, rx_sgl, rx_nents, 48562306a36Sopenharmony_ci DMA_DEV_TO_MEM, rx_done); 48662306a36Sopenharmony_ci if (ret) 48762306a36Sopenharmony_ci return ret; 48862306a36Sopenharmony_ci dma_async_issue_pending(host->dma_rx); 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (tx_sgl) { 49262306a36Sopenharmony_ci ret = spi_qup_prep_sg(host, tx_sgl, tx_nents, 49362306a36Sopenharmony_ci DMA_MEM_TO_DEV, tx_done); 49462306a36Sopenharmony_ci if (ret) 49562306a36Sopenharmony_ci return ret; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci dma_async_issue_pending(host->dma_tx); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (!wait_for_completion_timeout(&qup->done, timeout)) 50162306a36Sopenharmony_ci return -ETIMEDOUT; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci for (; rx_sgl && rx_nents--; rx_sgl = sg_next(rx_sgl)) 50462306a36Sopenharmony_ci ; 50562306a36Sopenharmony_ci for (; tx_sgl && tx_nents--; tx_sgl = sg_next(tx_sgl)) 50662306a36Sopenharmony_ci ; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci } while (rx_sgl || tx_sgl); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci} 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_cistatic int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer, 51462306a36Sopenharmony_ci unsigned long timeout) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct spi_controller *host = spi->controller; 51762306a36Sopenharmony_ci struct spi_qup *qup = spi_controller_get_devdata(host); 51862306a36Sopenharmony_ci int ret, n_words, iterations, offset = 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci n_words = qup->n_words; 52162306a36Sopenharmony_ci iterations = n_words / SPI_MAX_XFER; /* round down */ 52262306a36Sopenharmony_ci qup->rx_buf = xfer->rx_buf; 52362306a36Sopenharmony_ci qup->tx_buf = xfer->tx_buf; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci do { 52662306a36Sopenharmony_ci if (iterations) 52762306a36Sopenharmony_ci qup->n_words = SPI_MAX_XFER; 52862306a36Sopenharmony_ci else 52962306a36Sopenharmony_ci qup->n_words = n_words % SPI_MAX_XFER; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (qup->tx_buf && offset) 53262306a36Sopenharmony_ci qup->tx_buf = xfer->tx_buf + offset * SPI_MAX_XFER; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (qup->rx_buf && offset) 53562306a36Sopenharmony_ci qup->rx_buf = xfer->rx_buf + offset * SPI_MAX_XFER; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* 53862306a36Sopenharmony_ci * if the transaction is small enough, we need 53962306a36Sopenharmony_ci * to fallback to FIFO mode 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32))) 54262306a36Sopenharmony_ci qup->mode = QUP_IO_M_MODE_FIFO; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = spi_qup_io_config(spi, xfer); 54562306a36Sopenharmony_ci if (ret) 54662306a36Sopenharmony_ci return ret; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ret = spi_qup_set_state(qup, QUP_STATE_RUN); 54962306a36Sopenharmony_ci if (ret) { 55062306a36Sopenharmony_ci dev_warn(qup->dev, "cannot set RUN state\n"); 55162306a36Sopenharmony_ci return ret; 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci ret = spi_qup_set_state(qup, QUP_STATE_PAUSE); 55562306a36Sopenharmony_ci if (ret) { 55662306a36Sopenharmony_ci dev_warn(qup->dev, "cannot set PAUSE state\n"); 55762306a36Sopenharmony_ci return ret; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (qup->mode == QUP_IO_M_MODE_FIFO) 56162306a36Sopenharmony_ci spi_qup_write(qup); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ret = spi_qup_set_state(qup, QUP_STATE_RUN); 56462306a36Sopenharmony_ci if (ret) { 56562306a36Sopenharmony_ci dev_warn(qup->dev, "cannot set RUN state\n"); 56662306a36Sopenharmony_ci return ret; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (!wait_for_completion_timeout(&qup->done, timeout)) 57062306a36Sopenharmony_ci return -ETIMEDOUT; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci offset++; 57362306a36Sopenharmony_ci } while (iterations--); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return 0; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic bool spi_qup_data_pending(struct spi_qup *controller) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci unsigned int remainder_tx, remainder_rx; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci remainder_tx = DIV_ROUND_UP(spi_qup_len(controller) - 58362306a36Sopenharmony_ci controller->tx_bytes, controller->w_size); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci remainder_rx = DIV_ROUND_UP(spi_qup_len(controller) - 58662306a36Sopenharmony_ci controller->rx_bytes, controller->w_size); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return remainder_tx || remainder_rx; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct spi_qup *controller = dev_id; 59462306a36Sopenharmony_ci u32 opflags, qup_err, spi_err; 59562306a36Sopenharmony_ci int error = 0; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS); 59862306a36Sopenharmony_ci spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS); 59962306a36Sopenharmony_ci opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); 60262306a36Sopenharmony_ci writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci if (qup_err) { 60562306a36Sopenharmony_ci if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN) 60662306a36Sopenharmony_ci dev_warn(controller->dev, "OUTPUT_OVER_RUN\n"); 60762306a36Sopenharmony_ci if (qup_err & QUP_ERROR_INPUT_UNDER_RUN) 60862306a36Sopenharmony_ci dev_warn(controller->dev, "INPUT_UNDER_RUN\n"); 60962306a36Sopenharmony_ci if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN) 61062306a36Sopenharmony_ci dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n"); 61162306a36Sopenharmony_ci if (qup_err & QUP_ERROR_INPUT_OVER_RUN) 61262306a36Sopenharmony_ci dev_warn(controller->dev, "INPUT_OVER_RUN\n"); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci error = -EIO; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (spi_err) { 61862306a36Sopenharmony_ci if (spi_err & SPI_ERROR_CLK_OVER_RUN) 61962306a36Sopenharmony_ci dev_warn(controller->dev, "CLK_OVER_RUN\n"); 62062306a36Sopenharmony_ci if (spi_err & SPI_ERROR_CLK_UNDER_RUN) 62162306a36Sopenharmony_ci dev_warn(controller->dev, "CLK_UNDER_RUN\n"); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci error = -EIO; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci spin_lock(&controller->lock); 62762306a36Sopenharmony_ci if (!controller->error) 62862306a36Sopenharmony_ci controller->error = error; 62962306a36Sopenharmony_ci spin_unlock(&controller->lock); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (spi_qup_is_dma_xfer(controller->mode)) { 63262306a36Sopenharmony_ci writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); 63362306a36Sopenharmony_ci } else { 63462306a36Sopenharmony_ci if (opflags & QUP_OP_IN_SERVICE_FLAG) 63562306a36Sopenharmony_ci spi_qup_read(controller, &opflags); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (opflags & QUP_OP_OUT_SERVICE_FLAG) 63862306a36Sopenharmony_ci spi_qup_write(controller); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (!spi_qup_data_pending(controller)) 64162306a36Sopenharmony_ci complete(&controller->done); 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (error) 64562306a36Sopenharmony_ci complete(&controller->done); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci if (opflags & QUP_OP_MAX_INPUT_DONE_FLAG) { 64862306a36Sopenharmony_ci if (!spi_qup_is_dma_xfer(controller->mode)) { 64962306a36Sopenharmony_ci if (spi_qup_data_pending(controller)) 65062306a36Sopenharmony_ci return IRQ_HANDLED; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci complete(&controller->done); 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return IRQ_HANDLED; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci/* set clock freq ... bits per word, determine mode */ 65962306a36Sopenharmony_cistatic int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct spi_qup *controller = spi_controller_get_devdata(spi->controller); 66262306a36Sopenharmony_ci int ret; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { 66562306a36Sopenharmony_ci dev_err(controller->dev, "too big size for loopback %d > %d\n", 66662306a36Sopenharmony_ci xfer->len, controller->in_fifo_sz); 66762306a36Sopenharmony_ci return -EIO; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci ret = clk_set_rate(controller->cclk, xfer->speed_hz); 67162306a36Sopenharmony_ci if (ret) { 67262306a36Sopenharmony_ci dev_err(controller->dev, "fail to set frequency %d", 67362306a36Sopenharmony_ci xfer->speed_hz); 67462306a36Sopenharmony_ci return -EIO; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8); 67862306a36Sopenharmony_ci controller->n_words = xfer->len / controller->w_size; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci if (controller->n_words <= (controller->in_fifo_sz / sizeof(u32))) 68162306a36Sopenharmony_ci controller->mode = QUP_IO_M_MODE_FIFO; 68262306a36Sopenharmony_ci else if (spi->controller->can_dma && 68362306a36Sopenharmony_ci spi->controller->can_dma(spi->controller, spi, xfer) && 68462306a36Sopenharmony_ci spi->controller->cur_msg_mapped) 68562306a36Sopenharmony_ci controller->mode = QUP_IO_M_MODE_BAM; 68662306a36Sopenharmony_ci else 68762306a36Sopenharmony_ci controller->mode = QUP_IO_M_MODE_BLOCK; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/* prep qup for another spi transaction of specific type */ 69362306a36Sopenharmony_cistatic int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci struct spi_qup *controller = spi_controller_get_devdata(spi->controller); 69662306a36Sopenharmony_ci u32 config, iomode, control; 69762306a36Sopenharmony_ci unsigned long flags; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci spin_lock_irqsave(&controller->lock, flags); 70062306a36Sopenharmony_ci controller->xfer = xfer; 70162306a36Sopenharmony_ci controller->error = 0; 70262306a36Sopenharmony_ci controller->rx_bytes = 0; 70362306a36Sopenharmony_ci controller->tx_bytes = 0; 70462306a36Sopenharmony_ci spin_unlock_irqrestore(&controller->lock, flags); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (spi_qup_set_state(controller, QUP_STATE_RESET)) { 70862306a36Sopenharmony_ci dev_err(controller->dev, "cannot set RESET state\n"); 70962306a36Sopenharmony_ci return -EIO; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci switch (controller->mode) { 71362306a36Sopenharmony_ci case QUP_IO_M_MODE_FIFO: 71462306a36Sopenharmony_ci writel_relaxed(controller->n_words, 71562306a36Sopenharmony_ci controller->base + QUP_MX_READ_CNT); 71662306a36Sopenharmony_ci writel_relaxed(controller->n_words, 71762306a36Sopenharmony_ci controller->base + QUP_MX_WRITE_CNT); 71862306a36Sopenharmony_ci /* must be zero for FIFO */ 71962306a36Sopenharmony_ci writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); 72062306a36Sopenharmony_ci writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); 72162306a36Sopenharmony_ci break; 72262306a36Sopenharmony_ci case QUP_IO_M_MODE_BAM: 72362306a36Sopenharmony_ci writel_relaxed(controller->n_words, 72462306a36Sopenharmony_ci controller->base + QUP_MX_INPUT_CNT); 72562306a36Sopenharmony_ci writel_relaxed(controller->n_words, 72662306a36Sopenharmony_ci controller->base + QUP_MX_OUTPUT_CNT); 72762306a36Sopenharmony_ci /* must be zero for BLOCK and BAM */ 72862306a36Sopenharmony_ci writel_relaxed(0, controller->base + QUP_MX_READ_CNT); 72962306a36Sopenharmony_ci writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (!controller->qup_v1) { 73262306a36Sopenharmony_ci void __iomem *input_cnt; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci input_cnt = controller->base + QUP_MX_INPUT_CNT; 73562306a36Sopenharmony_ci /* 73662306a36Sopenharmony_ci * for DMA transfers, both QUP_MX_INPUT_CNT and 73762306a36Sopenharmony_ci * QUP_MX_OUTPUT_CNT must be zero to all cases but one. 73862306a36Sopenharmony_ci * That case is a non-balanced transfer when there is 73962306a36Sopenharmony_ci * only a rx_buf. 74062306a36Sopenharmony_ci */ 74162306a36Sopenharmony_ci if (xfer->tx_buf) 74262306a36Sopenharmony_ci writel_relaxed(0, input_cnt); 74362306a36Sopenharmony_ci else 74462306a36Sopenharmony_ci writel_relaxed(controller->n_words, input_cnt); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci case QUP_IO_M_MODE_BLOCK: 75062306a36Sopenharmony_ci reinit_completion(&controller->done); 75162306a36Sopenharmony_ci writel_relaxed(controller->n_words, 75262306a36Sopenharmony_ci controller->base + QUP_MX_INPUT_CNT); 75362306a36Sopenharmony_ci writel_relaxed(controller->n_words, 75462306a36Sopenharmony_ci controller->base + QUP_MX_OUTPUT_CNT); 75562306a36Sopenharmony_ci /* must be zero for BLOCK and BAM */ 75662306a36Sopenharmony_ci writel_relaxed(0, controller->base + QUP_MX_READ_CNT); 75762306a36Sopenharmony_ci writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT); 75862306a36Sopenharmony_ci break; 75962306a36Sopenharmony_ci default: 76062306a36Sopenharmony_ci dev_err(controller->dev, "unknown mode = %d\n", 76162306a36Sopenharmony_ci controller->mode); 76262306a36Sopenharmony_ci return -EIO; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci iomode = readl_relaxed(controller->base + QUP_IO_M_MODES); 76662306a36Sopenharmony_ci /* Set input and output transfer mode */ 76762306a36Sopenharmony_ci iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (!spi_qup_is_dma_xfer(controller->mode)) 77062306a36Sopenharmony_ci iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN); 77162306a36Sopenharmony_ci else 77262306a36Sopenharmony_ci iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT); 77562306a36Sopenharmony_ci iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci control = readl_relaxed(controller->base + SPI_IO_CONTROL); 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci if (spi->mode & SPI_CPOL) 78262306a36Sopenharmony_ci control |= SPI_IO_C_CLK_IDLE_HIGH; 78362306a36Sopenharmony_ci else 78462306a36Sopenharmony_ci control &= ~SPI_IO_C_CLK_IDLE_HIGH; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci writel_relaxed(control, controller->base + SPI_IO_CONTROL); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci config = readl_relaxed(controller->base + SPI_CONFIG); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (spi->mode & SPI_LOOP) 79162306a36Sopenharmony_ci config |= SPI_CONFIG_LOOPBACK; 79262306a36Sopenharmony_ci else 79362306a36Sopenharmony_ci config &= ~SPI_CONFIG_LOOPBACK; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (spi->mode & SPI_CPHA) 79662306a36Sopenharmony_ci config &= ~SPI_CONFIG_INPUT_FIRST; 79762306a36Sopenharmony_ci else 79862306a36Sopenharmony_ci config |= SPI_CONFIG_INPUT_FIRST; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci /* 80162306a36Sopenharmony_ci * HS_MODE improves signal stability for spi-clk high rates, 80262306a36Sopenharmony_ci * but is invalid in loop back mode. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP)) 80562306a36Sopenharmony_ci config |= SPI_CONFIG_HS_MODE; 80662306a36Sopenharmony_ci else 80762306a36Sopenharmony_ci config &= ~SPI_CONFIG_HS_MODE; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci writel_relaxed(config, controller->base + SPI_CONFIG); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci config = readl_relaxed(controller->base + QUP_CONFIG); 81262306a36Sopenharmony_ci config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N); 81362306a36Sopenharmony_ci config |= xfer->bits_per_word - 1; 81462306a36Sopenharmony_ci config |= QUP_CONFIG_SPI_MODE; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (spi_qup_is_dma_xfer(controller->mode)) { 81762306a36Sopenharmony_ci if (!xfer->tx_buf) 81862306a36Sopenharmony_ci config |= QUP_CONFIG_NO_OUTPUT; 81962306a36Sopenharmony_ci if (!xfer->rx_buf) 82062306a36Sopenharmony_ci config |= QUP_CONFIG_NO_INPUT; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci writel_relaxed(config, controller->base + QUP_CONFIG); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci /* only write to OPERATIONAL_MASK when register is present */ 82662306a36Sopenharmony_ci if (!controller->qup_v1) { 82762306a36Sopenharmony_ci u32 mask = 0; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci /* 83062306a36Sopenharmony_ci * mask INPUT and OUTPUT service flags to prevent IRQs on FIFO 83162306a36Sopenharmony_ci * status change in BAM mode 83262306a36Sopenharmony_ci */ 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci if (spi_qup_is_dma_xfer(controller->mode)) 83562306a36Sopenharmony_ci mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK); 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic int spi_qup_transfer_one(struct spi_controller *host, 84462306a36Sopenharmony_ci struct spi_device *spi, 84562306a36Sopenharmony_ci struct spi_transfer *xfer) 84662306a36Sopenharmony_ci{ 84762306a36Sopenharmony_ci struct spi_qup *controller = spi_controller_get_devdata(host); 84862306a36Sopenharmony_ci unsigned long timeout, flags; 84962306a36Sopenharmony_ci int ret; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci ret = spi_qup_io_prep(spi, xfer); 85262306a36Sopenharmony_ci if (ret) 85362306a36Sopenharmony_ci return ret; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC); 85662306a36Sopenharmony_ci timeout = DIV_ROUND_UP(min_t(unsigned long, SPI_MAX_XFER, 85762306a36Sopenharmony_ci xfer->len) * 8, timeout); 85862306a36Sopenharmony_ci timeout = 100 * msecs_to_jiffies(timeout); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci reinit_completion(&controller->done); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci spin_lock_irqsave(&controller->lock, flags); 86362306a36Sopenharmony_ci controller->xfer = xfer; 86462306a36Sopenharmony_ci controller->error = 0; 86562306a36Sopenharmony_ci controller->rx_bytes = 0; 86662306a36Sopenharmony_ci controller->tx_bytes = 0; 86762306a36Sopenharmony_ci spin_unlock_irqrestore(&controller->lock, flags); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (spi_qup_is_dma_xfer(controller->mode)) 87062306a36Sopenharmony_ci ret = spi_qup_do_dma(spi, xfer, timeout); 87162306a36Sopenharmony_ci else 87262306a36Sopenharmony_ci ret = spi_qup_do_pio(spi, xfer, timeout); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci spi_qup_set_state(controller, QUP_STATE_RESET); 87562306a36Sopenharmony_ci spin_lock_irqsave(&controller->lock, flags); 87662306a36Sopenharmony_ci if (!ret) 87762306a36Sopenharmony_ci ret = controller->error; 87862306a36Sopenharmony_ci spin_unlock_irqrestore(&controller->lock, flags); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (ret && spi_qup_is_dma_xfer(controller->mode)) 88162306a36Sopenharmony_ci spi_qup_dma_terminate(host, xfer); 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci return ret; 88462306a36Sopenharmony_ci} 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cistatic bool spi_qup_can_dma(struct spi_controller *host, struct spi_device *spi, 88762306a36Sopenharmony_ci struct spi_transfer *xfer) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct spi_qup *qup = spi_controller_get_devdata(host); 89062306a36Sopenharmony_ci size_t dma_align = dma_get_cache_alignment(); 89162306a36Sopenharmony_ci int n_words; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (xfer->rx_buf) { 89462306a36Sopenharmony_ci if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) || 89562306a36Sopenharmony_ci IS_ERR_OR_NULL(host->dma_rx)) 89662306a36Sopenharmony_ci return false; 89762306a36Sopenharmony_ci if (qup->qup_v1 && (xfer->len % qup->in_blk_sz)) 89862306a36Sopenharmony_ci return false; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (xfer->tx_buf) { 90262306a36Sopenharmony_ci if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) || 90362306a36Sopenharmony_ci IS_ERR_OR_NULL(host->dma_tx)) 90462306a36Sopenharmony_ci return false; 90562306a36Sopenharmony_ci if (qup->qup_v1 && (xfer->len % qup->out_blk_sz)) 90662306a36Sopenharmony_ci return false; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8); 91062306a36Sopenharmony_ci if (n_words <= (qup->in_fifo_sz / sizeof(u32))) 91162306a36Sopenharmony_ci return false; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci return true; 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic void spi_qup_release_dma(struct spi_controller *host) 91762306a36Sopenharmony_ci{ 91862306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(host->dma_rx)) 91962306a36Sopenharmony_ci dma_release_channel(host->dma_rx); 92062306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(host->dma_tx)) 92162306a36Sopenharmony_ci dma_release_channel(host->dma_tx); 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int spi_qup_init_dma(struct spi_controller *host, resource_size_t base) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct spi_qup *spi = spi_controller_get_devdata(host); 92762306a36Sopenharmony_ci struct dma_slave_config *rx_conf = &spi->rx_conf, 92862306a36Sopenharmony_ci *tx_conf = &spi->tx_conf; 92962306a36Sopenharmony_ci struct device *dev = spi->dev; 93062306a36Sopenharmony_ci int ret; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* allocate dma resources, if available */ 93362306a36Sopenharmony_ci host->dma_rx = dma_request_chan(dev, "rx"); 93462306a36Sopenharmony_ci if (IS_ERR(host->dma_rx)) 93562306a36Sopenharmony_ci return PTR_ERR(host->dma_rx); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci host->dma_tx = dma_request_chan(dev, "tx"); 93862306a36Sopenharmony_ci if (IS_ERR(host->dma_tx)) { 93962306a36Sopenharmony_ci ret = PTR_ERR(host->dma_tx); 94062306a36Sopenharmony_ci goto err_tx; 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* set DMA parameters */ 94462306a36Sopenharmony_ci rx_conf->direction = DMA_DEV_TO_MEM; 94562306a36Sopenharmony_ci rx_conf->device_fc = 1; 94662306a36Sopenharmony_ci rx_conf->src_addr = base + QUP_INPUT_FIFO; 94762306a36Sopenharmony_ci rx_conf->src_maxburst = spi->in_blk_sz; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci tx_conf->direction = DMA_MEM_TO_DEV; 95062306a36Sopenharmony_ci tx_conf->device_fc = 1; 95162306a36Sopenharmony_ci tx_conf->dst_addr = base + QUP_OUTPUT_FIFO; 95262306a36Sopenharmony_ci tx_conf->dst_maxburst = spi->out_blk_sz; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci ret = dmaengine_slave_config(host->dma_rx, rx_conf); 95562306a36Sopenharmony_ci if (ret) { 95662306a36Sopenharmony_ci dev_err(dev, "failed to configure RX channel\n"); 95762306a36Sopenharmony_ci goto err; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci ret = dmaengine_slave_config(host->dma_tx, tx_conf); 96162306a36Sopenharmony_ci if (ret) { 96262306a36Sopenharmony_ci dev_err(dev, "failed to configure TX channel\n"); 96362306a36Sopenharmony_ci goto err; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci return 0; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cierr: 96962306a36Sopenharmony_ci dma_release_channel(host->dma_tx); 97062306a36Sopenharmony_cierr_tx: 97162306a36Sopenharmony_ci dma_release_channel(host->dma_rx); 97262306a36Sopenharmony_ci return ret; 97362306a36Sopenharmony_ci} 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_cistatic void spi_qup_set_cs(struct spi_device *spi, bool val) 97662306a36Sopenharmony_ci{ 97762306a36Sopenharmony_ci struct spi_qup *controller; 97862306a36Sopenharmony_ci u32 spi_ioc; 97962306a36Sopenharmony_ci u32 spi_ioc_orig; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci controller = spi_controller_get_devdata(spi->controller); 98262306a36Sopenharmony_ci spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL); 98362306a36Sopenharmony_ci spi_ioc_orig = spi_ioc; 98462306a36Sopenharmony_ci if (!val) 98562306a36Sopenharmony_ci spi_ioc |= SPI_IO_C_FORCE_CS; 98662306a36Sopenharmony_ci else 98762306a36Sopenharmony_ci spi_ioc &= ~SPI_IO_C_FORCE_CS; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (spi_ioc != spi_ioc_orig) 99062306a36Sopenharmony_ci writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL); 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic int spi_qup_probe(struct platform_device *pdev) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci struct spi_controller *host; 99662306a36Sopenharmony_ci struct clk *iclk, *cclk; 99762306a36Sopenharmony_ci struct spi_qup *controller; 99862306a36Sopenharmony_ci struct resource *res; 99962306a36Sopenharmony_ci struct device *dev; 100062306a36Sopenharmony_ci void __iomem *base; 100162306a36Sopenharmony_ci u32 max_freq, iomode, num_cs; 100262306a36Sopenharmony_ci int ret, irq, size; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci dev = &pdev->dev; 100562306a36Sopenharmony_ci base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 100662306a36Sopenharmony_ci if (IS_ERR(base)) 100762306a36Sopenharmony_ci return PTR_ERR(base); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 101062306a36Sopenharmony_ci if (irq < 0) 101162306a36Sopenharmony_ci return irq; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci cclk = devm_clk_get(dev, "core"); 101462306a36Sopenharmony_ci if (IS_ERR(cclk)) 101562306a36Sopenharmony_ci return PTR_ERR(cclk); 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci iclk = devm_clk_get(dev, "iface"); 101862306a36Sopenharmony_ci if (IS_ERR(iclk)) 101962306a36Sopenharmony_ci return PTR_ERR(iclk); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci /* This is optional parameter */ 102262306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq)) 102362306a36Sopenharmony_ci max_freq = SPI_MAX_RATE; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (!max_freq || max_freq > SPI_MAX_RATE) { 102662306a36Sopenharmony_ci dev_err(dev, "invalid clock frequency %d\n", max_freq); 102762306a36Sopenharmony_ci return -ENXIO; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci host = spi_alloc_host(dev, sizeof(struct spi_qup)); 103162306a36Sopenharmony_ci if (!host) { 103262306a36Sopenharmony_ci dev_err(dev, "cannot allocate host\n"); 103362306a36Sopenharmony_ci return -ENOMEM; 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci /* use num-cs unless not present or out of range */ 103762306a36Sopenharmony_ci if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) || 103862306a36Sopenharmony_ci num_cs > SPI_NUM_CHIPSELECTS) 103962306a36Sopenharmony_ci host->num_chipselect = SPI_NUM_CHIPSELECTS; 104062306a36Sopenharmony_ci else 104162306a36Sopenharmony_ci host->num_chipselect = num_cs; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci host->use_gpio_descriptors = true; 104462306a36Sopenharmony_ci host->max_native_cs = SPI_NUM_CHIPSELECTS; 104562306a36Sopenharmony_ci host->bus_num = pdev->id; 104662306a36Sopenharmony_ci host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP; 104762306a36Sopenharmony_ci host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); 104862306a36Sopenharmony_ci host->max_speed_hz = max_freq; 104962306a36Sopenharmony_ci host->transfer_one = spi_qup_transfer_one; 105062306a36Sopenharmony_ci host->dev.of_node = pdev->dev.of_node; 105162306a36Sopenharmony_ci host->auto_runtime_pm = true; 105262306a36Sopenharmony_ci host->dma_alignment = dma_get_cache_alignment(); 105362306a36Sopenharmony_ci host->max_dma_len = SPI_MAX_XFER; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci platform_set_drvdata(pdev, host); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci controller = spi_controller_get_devdata(host); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci controller->dev = dev; 106062306a36Sopenharmony_ci controller->base = base; 106162306a36Sopenharmony_ci controller->iclk = iclk; 106262306a36Sopenharmony_ci controller->cclk = cclk; 106362306a36Sopenharmony_ci controller->irq = irq; 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci ret = spi_qup_init_dma(host, res->start); 106662306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 106762306a36Sopenharmony_ci goto error; 106862306a36Sopenharmony_ci else if (!ret) 106962306a36Sopenharmony_ci host->can_dma = spi_qup_can_dma; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci controller->qup_v1 = (uintptr_t)of_device_get_match_data(dev); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci if (!controller->qup_v1) 107462306a36Sopenharmony_ci host->set_cs = spi_qup_set_cs; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci spin_lock_init(&controller->lock); 107762306a36Sopenharmony_ci init_completion(&controller->done); 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci ret = clk_prepare_enable(cclk); 108062306a36Sopenharmony_ci if (ret) { 108162306a36Sopenharmony_ci dev_err(dev, "cannot enable core clock\n"); 108262306a36Sopenharmony_ci goto error_dma; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci ret = clk_prepare_enable(iclk); 108662306a36Sopenharmony_ci if (ret) { 108762306a36Sopenharmony_ci clk_disable_unprepare(cclk); 108862306a36Sopenharmony_ci dev_err(dev, "cannot enable iface clock\n"); 108962306a36Sopenharmony_ci goto error_dma; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci iomode = readl_relaxed(base + QUP_IO_M_MODES); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode); 109562306a36Sopenharmony_ci if (size) 109662306a36Sopenharmony_ci controller->out_blk_sz = size * 16; 109762306a36Sopenharmony_ci else 109862306a36Sopenharmony_ci controller->out_blk_sz = 4; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode); 110162306a36Sopenharmony_ci if (size) 110262306a36Sopenharmony_ci controller->in_blk_sz = size * 16; 110362306a36Sopenharmony_ci else 110462306a36Sopenharmony_ci controller->in_blk_sz = 4; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode); 110762306a36Sopenharmony_ci controller->out_fifo_sz = controller->out_blk_sz * (2 << size); 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci size = QUP_IO_M_INPUT_FIFO_SIZE(iomode); 111062306a36Sopenharmony_ci controller->in_fifo_sz = controller->in_blk_sz * (2 << size); 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci dev_info(dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n", 111362306a36Sopenharmony_ci controller->in_blk_sz, controller->in_fifo_sz, 111462306a36Sopenharmony_ci controller->out_blk_sz, controller->out_fifo_sz); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci writel_relaxed(1, base + QUP_SW_RESET); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci ret = spi_qup_set_state(controller, QUP_STATE_RESET); 111962306a36Sopenharmony_ci if (ret) { 112062306a36Sopenharmony_ci dev_err(dev, "cannot set RESET state\n"); 112162306a36Sopenharmony_ci goto error_clk; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci writel_relaxed(0, base + QUP_OPERATIONAL); 112562306a36Sopenharmony_ci writel_relaxed(0, base + QUP_IO_M_MODES); 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci if (!controller->qup_v1) 112862306a36Sopenharmony_ci writel_relaxed(0, base + QUP_OPERATIONAL_MASK); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN, 113162306a36Sopenharmony_ci base + SPI_ERROR_FLAGS_EN); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci /* if earlier version of the QUP, disable INPUT_OVERRUN */ 113462306a36Sopenharmony_ci if (controller->qup_v1) 113562306a36Sopenharmony_ci writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN | 113662306a36Sopenharmony_ci QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN, 113762306a36Sopenharmony_ci base + QUP_ERROR_FLAGS_EN); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci writel_relaxed(0, base + SPI_CONFIG); 114062306a36Sopenharmony_ci writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, spi_qup_qup_irq, 114362306a36Sopenharmony_ci IRQF_TRIGGER_HIGH, pdev->name, controller); 114462306a36Sopenharmony_ci if (ret) 114562306a36Sopenharmony_ci goto error_clk; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); 114862306a36Sopenharmony_ci pm_runtime_use_autosuspend(dev); 114962306a36Sopenharmony_ci pm_runtime_set_active(dev); 115062306a36Sopenharmony_ci pm_runtime_enable(dev); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci ret = devm_spi_register_controller(dev, host); 115362306a36Sopenharmony_ci if (ret) 115462306a36Sopenharmony_ci goto disable_pm; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci return 0; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cidisable_pm: 115962306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 116062306a36Sopenharmony_cierror_clk: 116162306a36Sopenharmony_ci clk_disable_unprepare(cclk); 116262306a36Sopenharmony_ci clk_disable_unprepare(iclk); 116362306a36Sopenharmony_cierror_dma: 116462306a36Sopenharmony_ci spi_qup_release_dma(host); 116562306a36Sopenharmony_cierror: 116662306a36Sopenharmony_ci spi_controller_put(host); 116762306a36Sopenharmony_ci return ret; 116862306a36Sopenharmony_ci} 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci#ifdef CONFIG_PM 117162306a36Sopenharmony_cistatic int spi_qup_pm_suspend_runtime(struct device *device) 117262306a36Sopenharmony_ci{ 117362306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(device); 117462306a36Sopenharmony_ci struct spi_qup *controller = spi_controller_get_devdata(host); 117562306a36Sopenharmony_ci u32 config; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* Enable clocks auto gaiting */ 117862306a36Sopenharmony_ci config = readl(controller->base + QUP_CONFIG); 117962306a36Sopenharmony_ci config |= QUP_CONFIG_CLOCK_AUTO_GATE; 118062306a36Sopenharmony_ci writel_relaxed(config, controller->base + QUP_CONFIG); 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci clk_disable_unprepare(controller->cclk); 118362306a36Sopenharmony_ci clk_disable_unprepare(controller->iclk); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci return 0; 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic int spi_qup_pm_resume_runtime(struct device *device) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(device); 119162306a36Sopenharmony_ci struct spi_qup *controller = spi_controller_get_devdata(host); 119262306a36Sopenharmony_ci u32 config; 119362306a36Sopenharmony_ci int ret; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci ret = clk_prepare_enable(controller->iclk); 119662306a36Sopenharmony_ci if (ret) 119762306a36Sopenharmony_ci return ret; 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci ret = clk_prepare_enable(controller->cclk); 120062306a36Sopenharmony_ci if (ret) { 120162306a36Sopenharmony_ci clk_disable_unprepare(controller->iclk); 120262306a36Sopenharmony_ci return ret; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* Disable clocks auto gaiting */ 120662306a36Sopenharmony_ci config = readl_relaxed(controller->base + QUP_CONFIG); 120762306a36Sopenharmony_ci config &= ~QUP_CONFIG_CLOCK_AUTO_GATE; 120862306a36Sopenharmony_ci writel_relaxed(config, controller->base + QUP_CONFIG); 120962306a36Sopenharmony_ci return 0; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci#endif /* CONFIG_PM */ 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 121462306a36Sopenharmony_cistatic int spi_qup_suspend(struct device *device) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(device); 121762306a36Sopenharmony_ci struct spi_qup *controller = spi_controller_get_devdata(host); 121862306a36Sopenharmony_ci int ret; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci if (pm_runtime_suspended(device)) { 122162306a36Sopenharmony_ci ret = spi_qup_pm_resume_runtime(device); 122262306a36Sopenharmony_ci if (ret) 122362306a36Sopenharmony_ci return ret; 122462306a36Sopenharmony_ci } 122562306a36Sopenharmony_ci ret = spi_controller_suspend(host); 122662306a36Sopenharmony_ci if (ret) 122762306a36Sopenharmony_ci return ret; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci ret = spi_qup_set_state(controller, QUP_STATE_RESET); 123062306a36Sopenharmony_ci if (ret) 123162306a36Sopenharmony_ci return ret; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci clk_disable_unprepare(controller->cclk); 123462306a36Sopenharmony_ci clk_disable_unprepare(controller->iclk); 123562306a36Sopenharmony_ci return 0; 123662306a36Sopenharmony_ci} 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_cistatic int spi_qup_resume(struct device *device) 123962306a36Sopenharmony_ci{ 124062306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(device); 124162306a36Sopenharmony_ci struct spi_qup *controller = spi_controller_get_devdata(host); 124262306a36Sopenharmony_ci int ret; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci ret = clk_prepare_enable(controller->iclk); 124562306a36Sopenharmony_ci if (ret) 124662306a36Sopenharmony_ci return ret; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci ret = clk_prepare_enable(controller->cclk); 124962306a36Sopenharmony_ci if (ret) { 125062306a36Sopenharmony_ci clk_disable_unprepare(controller->iclk); 125162306a36Sopenharmony_ci return ret; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci ret = spi_qup_set_state(controller, QUP_STATE_RESET); 125562306a36Sopenharmony_ci if (ret) 125662306a36Sopenharmony_ci goto disable_clk; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci ret = spi_controller_resume(host); 125962306a36Sopenharmony_ci if (ret) 126062306a36Sopenharmony_ci goto disable_clk; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci return 0; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_cidisable_clk: 126562306a36Sopenharmony_ci clk_disable_unprepare(controller->cclk); 126662306a36Sopenharmony_ci clk_disable_unprepare(controller->iclk); 126762306a36Sopenharmony_ci return ret; 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */ 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_cistatic void spi_qup_remove(struct platform_device *pdev) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci struct spi_controller *host = dev_get_drvdata(&pdev->dev); 127462306a36Sopenharmony_ci struct spi_qup *controller = spi_controller_get_devdata(host); 127562306a36Sopenharmony_ci int ret; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci ret = pm_runtime_get_sync(&pdev->dev); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci if (ret >= 0) { 128062306a36Sopenharmony_ci ret = spi_qup_set_state(controller, QUP_STATE_RESET); 128162306a36Sopenharmony_ci if (ret) 128262306a36Sopenharmony_ci dev_warn(&pdev->dev, "failed to reset controller (%pe)\n", 128362306a36Sopenharmony_ci ERR_PTR(ret)); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci clk_disable_unprepare(controller->cclk); 128662306a36Sopenharmony_ci clk_disable_unprepare(controller->iclk); 128762306a36Sopenharmony_ci } else { 128862306a36Sopenharmony_ci dev_warn(&pdev->dev, "failed to resume, skip hw disable (%pe)\n", 128962306a36Sopenharmony_ci ERR_PTR(ret)); 129062306a36Sopenharmony_ci } 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci spi_qup_release_dma(host); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci pm_runtime_put_noidle(&pdev->dev); 129562306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic const struct of_device_id spi_qup_dt_match[] = { 129962306a36Sopenharmony_ci { .compatible = "qcom,spi-qup-v1.1.1", .data = (void *)1, }, 130062306a36Sopenharmony_ci { .compatible = "qcom,spi-qup-v2.1.1", }, 130162306a36Sopenharmony_ci { .compatible = "qcom,spi-qup-v2.2.1", }, 130262306a36Sopenharmony_ci { } 130362306a36Sopenharmony_ci}; 130462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, spi_qup_dt_match); 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic const struct dev_pm_ops spi_qup_dev_pm_ops = { 130762306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume) 130862306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime, 130962306a36Sopenharmony_ci spi_qup_pm_resume_runtime, 131062306a36Sopenharmony_ci NULL) 131162306a36Sopenharmony_ci}; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_cistatic struct platform_driver spi_qup_driver = { 131462306a36Sopenharmony_ci .driver = { 131562306a36Sopenharmony_ci .name = "spi_qup", 131662306a36Sopenharmony_ci .pm = &spi_qup_dev_pm_ops, 131762306a36Sopenharmony_ci .of_match_table = spi_qup_dt_match, 131862306a36Sopenharmony_ci }, 131962306a36Sopenharmony_ci .probe = spi_qup_probe, 132062306a36Sopenharmony_ci .remove_new = spi_qup_remove, 132162306a36Sopenharmony_ci}; 132262306a36Sopenharmony_cimodule_platform_driver(spi_qup_driver); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 132562306a36Sopenharmony_ciMODULE_ALIAS("platform:spi_qup"); 1326