xref: /openbmc/linux/drivers/spi/spi-qup.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1ce718dfbSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
264ff247aSIvan T. Ivanov /*
364ff247aSIvan T. Ivanov  * Copyright (c) 2008-2014, The Linux foundation. All rights reserved.
464ff247aSIvan T. Ivanov  */
564ff247aSIvan T. Ivanov 
664ff247aSIvan T. Ivanov #include <linux/clk.h>
764ff247aSIvan T. Ivanov #include <linux/delay.h>
864ff247aSIvan T. Ivanov #include <linux/err.h>
964ff247aSIvan T. Ivanov #include <linux/interrupt.h>
1064ff247aSIvan T. Ivanov #include <linux/io.h>
1164ff247aSIvan T. Ivanov #include <linux/list.h>
1264ff247aSIvan T. Ivanov #include <linux/module.h>
1364ff247aSIvan T. Ivanov #include <linux/of.h>
1464ff247aSIvan T. Ivanov #include <linux/platform_device.h>
1564ff247aSIvan T. Ivanov #include <linux/pm_runtime.h>
1664ff247aSIvan T. Ivanov #include <linux/spi/spi.h>
17612762e8SAndy Gross #include <linux/dmaengine.h>
18612762e8SAndy Gross #include <linux/dma-mapping.h>
1964ff247aSIvan T. Ivanov 
2064ff247aSIvan T. Ivanov #define QUP_CONFIG			0x0000
2164ff247aSIvan T. Ivanov #define QUP_STATE			0x0004
2264ff247aSIvan T. Ivanov #define QUP_IO_M_MODES			0x0008
2364ff247aSIvan T. Ivanov #define QUP_SW_RESET			0x000c
2464ff247aSIvan T. Ivanov #define QUP_OPERATIONAL			0x0018
2564ff247aSIvan T. Ivanov #define QUP_ERROR_FLAGS			0x001c
2664ff247aSIvan T. Ivanov #define QUP_ERROR_FLAGS_EN		0x0020
2764ff247aSIvan T. Ivanov #define QUP_OPERATIONAL_MASK		0x0028
2864ff247aSIvan T. Ivanov #define QUP_HW_VERSION			0x0030
2964ff247aSIvan T. Ivanov #define QUP_MX_OUTPUT_CNT		0x0100
3064ff247aSIvan T. Ivanov #define QUP_OUTPUT_FIFO			0x0110
3164ff247aSIvan T. Ivanov #define QUP_MX_WRITE_CNT		0x0150
3264ff247aSIvan T. Ivanov #define QUP_MX_INPUT_CNT		0x0200
3364ff247aSIvan T. Ivanov #define QUP_MX_READ_CNT			0x0208
3464ff247aSIvan T. Ivanov #define QUP_INPUT_FIFO			0x0218
3564ff247aSIvan T. Ivanov 
3664ff247aSIvan T. Ivanov #define SPI_CONFIG			0x0300
3764ff247aSIvan T. Ivanov #define SPI_IO_CONTROL			0x0304
3864ff247aSIvan T. Ivanov #define SPI_ERROR_FLAGS			0x0308
3964ff247aSIvan T. Ivanov #define SPI_ERROR_FLAGS_EN		0x030c
4064ff247aSIvan T. Ivanov 
4164ff247aSIvan T. Ivanov /* QUP_CONFIG fields */
4264ff247aSIvan T. Ivanov #define QUP_CONFIG_SPI_MODE		(1 << 8)
4364ff247aSIvan T. Ivanov #define QUP_CONFIG_CLOCK_AUTO_GATE	BIT(13)
4464ff247aSIvan T. Ivanov #define QUP_CONFIG_NO_INPUT		BIT(7)
4564ff247aSIvan T. Ivanov #define QUP_CONFIG_NO_OUTPUT		BIT(6)
4664ff247aSIvan T. Ivanov #define QUP_CONFIG_N			0x001f
4764ff247aSIvan T. Ivanov 
4864ff247aSIvan T. Ivanov /* QUP_STATE fields */
4964ff247aSIvan T. Ivanov #define QUP_STATE_VALID			BIT(2)
5064ff247aSIvan T. Ivanov #define QUP_STATE_RESET			0
5164ff247aSIvan T. Ivanov #define QUP_STATE_RUN			1
5264ff247aSIvan T. Ivanov #define QUP_STATE_PAUSE			3
5364ff247aSIvan T. Ivanov #define QUP_STATE_MASK			3
5464ff247aSIvan T. Ivanov #define QUP_STATE_CLEAR			2
5564ff247aSIvan T. Ivanov 
5664ff247aSIvan T. Ivanov #define QUP_HW_VERSION_2_1_1		0x20010001
5764ff247aSIvan T. Ivanov 
5864ff247aSIvan T. Ivanov /* QUP_IO_M_MODES fields */
5964ff247aSIvan T. Ivanov #define QUP_IO_M_PACK_EN		BIT(15)
6064ff247aSIvan T. Ivanov #define QUP_IO_M_UNPACK_EN		BIT(14)
6164ff247aSIvan T. Ivanov #define QUP_IO_M_INPUT_MODE_MASK_SHIFT	12
6264ff247aSIvan T. Ivanov #define QUP_IO_M_OUTPUT_MODE_MASK_SHIFT	10
6364ff247aSIvan T. Ivanov #define QUP_IO_M_INPUT_MODE_MASK	(3 << QUP_IO_M_INPUT_MODE_MASK_SHIFT)
6464ff247aSIvan T. Ivanov #define QUP_IO_M_OUTPUT_MODE_MASK	(3 << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT)
6564ff247aSIvan T. Ivanov 
6664ff247aSIvan T. Ivanov #define QUP_IO_M_OUTPUT_BLOCK_SIZE(x)	(((x) & (0x03 << 0)) >> 0)
6764ff247aSIvan T. Ivanov #define QUP_IO_M_OUTPUT_FIFO_SIZE(x)	(((x) & (0x07 << 2)) >> 2)
6864ff247aSIvan T. Ivanov #define QUP_IO_M_INPUT_BLOCK_SIZE(x)	(((x) & (0x03 << 5)) >> 5)
6964ff247aSIvan T. Ivanov #define QUP_IO_M_INPUT_FIFO_SIZE(x)	(((x) & (0x07 << 7)) >> 7)
7064ff247aSIvan T. Ivanov 
7164ff247aSIvan T. Ivanov #define QUP_IO_M_MODE_FIFO		0
7264ff247aSIvan T. Ivanov #define QUP_IO_M_MODE_BLOCK		1
7364ff247aSIvan T. Ivanov #define QUP_IO_M_MODE_DMOV		2
7464ff247aSIvan T. Ivanov #define QUP_IO_M_MODE_BAM		3
7564ff247aSIvan T. Ivanov 
7664ff247aSIvan T. Ivanov /* QUP_OPERATIONAL fields */
777538726fSVaradarajan Narayanan #define QUP_OP_IN_BLOCK_READ_REQ	BIT(13)
787538726fSVaradarajan Narayanan #define QUP_OP_OUT_BLOCK_WRITE_REQ	BIT(12)
7964ff247aSIvan T. Ivanov #define QUP_OP_MAX_INPUT_DONE_FLAG	BIT(11)
8064ff247aSIvan T. Ivanov #define QUP_OP_MAX_OUTPUT_DONE_FLAG	BIT(10)
8164ff247aSIvan T. Ivanov #define QUP_OP_IN_SERVICE_FLAG		BIT(9)
8264ff247aSIvan T. Ivanov #define QUP_OP_OUT_SERVICE_FLAG		BIT(8)
8364ff247aSIvan T. Ivanov #define QUP_OP_IN_FIFO_FULL		BIT(7)
8464ff247aSIvan T. Ivanov #define QUP_OP_OUT_FIFO_FULL		BIT(6)
8564ff247aSIvan T. Ivanov #define QUP_OP_IN_FIFO_NOT_EMPTY	BIT(5)
8664ff247aSIvan T. Ivanov #define QUP_OP_OUT_FIFO_NOT_EMPTY	BIT(4)
8764ff247aSIvan T. Ivanov 
8864ff247aSIvan T. Ivanov /* QUP_ERROR_FLAGS and QUP_ERROR_FLAGS_EN fields */
8964ff247aSIvan T. Ivanov #define QUP_ERROR_OUTPUT_OVER_RUN	BIT(5)
9064ff247aSIvan T. Ivanov #define QUP_ERROR_INPUT_UNDER_RUN	BIT(4)
9164ff247aSIvan T. Ivanov #define QUP_ERROR_OUTPUT_UNDER_RUN	BIT(3)
9264ff247aSIvan T. Ivanov #define QUP_ERROR_INPUT_OVER_RUN	BIT(2)
9364ff247aSIvan T. Ivanov 
9464ff247aSIvan T. Ivanov /* SPI_CONFIG fields */
9564ff247aSIvan T. Ivanov #define SPI_CONFIG_HS_MODE		BIT(10)
9664ff247aSIvan T. Ivanov #define SPI_CONFIG_INPUT_FIRST		BIT(9)
9764ff247aSIvan T. Ivanov #define SPI_CONFIG_LOOPBACK		BIT(8)
9864ff247aSIvan T. Ivanov 
9964ff247aSIvan T. Ivanov /* SPI_IO_CONTROL fields */
10064ff247aSIvan T. Ivanov #define SPI_IO_C_FORCE_CS		BIT(11)
10164ff247aSIvan T. Ivanov #define SPI_IO_C_CLK_IDLE_HIGH		BIT(10)
10264ff247aSIvan T. Ivanov #define SPI_IO_C_MX_CS_MODE		BIT(8)
10364ff247aSIvan T. Ivanov #define SPI_IO_C_CS_N_POLARITY_0	BIT(4)
10464ff247aSIvan T. Ivanov #define SPI_IO_C_CS_SELECT(x)		(((x) & 3) << 2)
10564ff247aSIvan T. Ivanov #define SPI_IO_C_CS_SELECT_MASK		0x000c
10664ff247aSIvan T. Ivanov #define SPI_IO_C_TRISTATE_CS		BIT(1)
10764ff247aSIvan T. Ivanov #define SPI_IO_C_NO_TRI_STATE		BIT(0)
10864ff247aSIvan T. Ivanov 
10964ff247aSIvan T. Ivanov /* SPI_ERROR_FLAGS and SPI_ERROR_FLAGS_EN fields */
11064ff247aSIvan T. Ivanov #define SPI_ERROR_CLK_OVER_RUN		BIT(1)
11164ff247aSIvan T. Ivanov #define SPI_ERROR_CLK_UNDER_RUN		BIT(0)
11264ff247aSIvan T. Ivanov 
11364ff247aSIvan T. Ivanov #define SPI_NUM_CHIPSELECTS		4
11464ff247aSIvan T. Ivanov 
1155dc47fefSVaradarajan Narayanan #define SPI_MAX_XFER			(SZ_64K - 64)
116612762e8SAndy Gross 
11764ff247aSIvan T. Ivanov /* high speed mode is when bus rate is greater then 26MHz */
11864ff247aSIvan T. Ivanov #define SPI_HS_MIN_RATE			26000000
11964ff247aSIvan T. Ivanov #define SPI_MAX_RATE			50000000
12064ff247aSIvan T. Ivanov 
12164ff247aSIvan T. Ivanov #define SPI_DELAY_THRESHOLD		1
12264ff247aSIvan T. Ivanov #define SPI_DELAY_RETRY			10
12364ff247aSIvan T. Ivanov 
12464ff247aSIvan T. Ivanov struct spi_qup {
12564ff247aSIvan T. Ivanov 	void __iomem		*base;
12664ff247aSIvan T. Ivanov 	struct device		*dev;
12764ff247aSIvan T. Ivanov 	struct clk		*cclk;	/* core clock */
12864ff247aSIvan T. Ivanov 	struct clk		*iclk;	/* interface clock */
12964ff247aSIvan T. Ivanov 	int			irq;
13064ff247aSIvan T. Ivanov 	spinlock_t		lock;
13164ff247aSIvan T. Ivanov 
13264ff247aSIvan T. Ivanov 	int			in_fifo_sz;
13364ff247aSIvan T. Ivanov 	int			out_fifo_sz;
13464ff247aSIvan T. Ivanov 	int			in_blk_sz;
13564ff247aSIvan T. Ivanov 	int			out_blk_sz;
13664ff247aSIvan T. Ivanov 
13764ff247aSIvan T. Ivanov 	struct spi_transfer	*xfer;
13864ff247aSIvan T. Ivanov 	struct completion	done;
13964ff247aSIvan T. Ivanov 	int			error;
14064ff247aSIvan T. Ivanov 	int			w_size;	/* bytes per SPI word */
141612762e8SAndy Gross 	int			n_words;
14264ff247aSIvan T. Ivanov 	int			tx_bytes;
14364ff247aSIvan T. Ivanov 	int			rx_bytes;
1445dc47fefSVaradarajan Narayanan 	const u8		*tx_buf;
1455dc47fefSVaradarajan Narayanan 	u8			*rx_buf;
14670cea0a9SAndy Gross 	int			qup_v1;
147612762e8SAndy Gross 
14832ecab99SVaradarajan Narayanan 	int			mode;
149612762e8SAndy Gross 	struct dma_slave_config	rx_conf;
150612762e8SAndy Gross 	struct dma_slave_config	tx_conf;
15164ff247aSIvan T. Ivanov };
15264ff247aSIvan T. Ivanov 
1533b5ea2c9SVaradarajan Narayanan static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer);
1543b5ea2c9SVaradarajan Narayanan 
spi_qup_is_flag_set(struct spi_qup * controller,u32 flag)1557538726fSVaradarajan Narayanan static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag)
1567538726fSVaradarajan Narayanan {
1577538726fSVaradarajan Narayanan 	u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL);
1587538726fSVaradarajan Narayanan 
1597538726fSVaradarajan Narayanan 	return (opflag & flag) != 0;
1607538726fSVaradarajan Narayanan }
1617538726fSVaradarajan Narayanan 
spi_qup_is_dma_xfer(int mode)16232ecab99SVaradarajan Narayanan static inline bool spi_qup_is_dma_xfer(int mode)
16332ecab99SVaradarajan Narayanan {
16432ecab99SVaradarajan Narayanan 	if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM)
16532ecab99SVaradarajan Narayanan 		return true;
16632ecab99SVaradarajan Narayanan 
16732ecab99SVaradarajan Narayanan 	return false;
16832ecab99SVaradarajan Narayanan }
16964ff247aSIvan T. Ivanov 
1705dc47fefSVaradarajan Narayanan /* get's the transaction size length */
spi_qup_len(struct spi_qup * controller)1715dc47fefSVaradarajan Narayanan static inline unsigned int spi_qup_len(struct spi_qup *controller)
1725dc47fefSVaradarajan Narayanan {
1735dc47fefSVaradarajan Narayanan 	return controller->n_words * controller->w_size;
1745dc47fefSVaradarajan Narayanan }
1755dc47fefSVaradarajan Narayanan 
spi_qup_is_valid_state(struct spi_qup * controller)17664ff247aSIvan T. Ivanov static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
17764ff247aSIvan T. Ivanov {
17864ff247aSIvan T. Ivanov 	u32 opstate = readl_relaxed(controller->base + QUP_STATE);
17964ff247aSIvan T. Ivanov 
18064ff247aSIvan T. Ivanov 	return opstate & QUP_STATE_VALID;
18164ff247aSIvan T. Ivanov }
18264ff247aSIvan T. Ivanov 
spi_qup_set_state(struct spi_qup * controller,u32 state)18364ff247aSIvan T. Ivanov static int spi_qup_set_state(struct spi_qup *controller, u32 state)
18464ff247aSIvan T. Ivanov {
18564ff247aSIvan T. Ivanov 	unsigned long loop;
18664ff247aSIvan T. Ivanov 	u32 cur_state;
18764ff247aSIvan T. Ivanov 
18864ff247aSIvan T. Ivanov 	loop = 0;
18964ff247aSIvan T. Ivanov 	while (!spi_qup_is_valid_state(controller)) {
19064ff247aSIvan T. Ivanov 
19164ff247aSIvan T. Ivanov 		usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2);
19264ff247aSIvan T. Ivanov 
19364ff247aSIvan T. Ivanov 		if (++loop > SPI_DELAY_RETRY)
19464ff247aSIvan T. Ivanov 			return -EIO;
19564ff247aSIvan T. Ivanov 	}
19664ff247aSIvan T. Ivanov 
19764ff247aSIvan T. Ivanov 	if (loop)
19864ff247aSIvan T. Ivanov 		dev_dbg(controller->dev, "invalid state for %ld,us %d\n",
19964ff247aSIvan T. Ivanov 			loop, state);
20064ff247aSIvan T. Ivanov 
20164ff247aSIvan T. Ivanov 	cur_state = readl_relaxed(controller->base + QUP_STATE);
20264ff247aSIvan T. Ivanov 	/*
20364ff247aSIvan T. Ivanov 	 * Per spec: for PAUSE_STATE to RESET_STATE, two writes
20464ff247aSIvan T. Ivanov 	 * of (b10) are required
20564ff247aSIvan T. Ivanov 	 */
20664ff247aSIvan T. Ivanov 	if (((cur_state & QUP_STATE_MASK) == QUP_STATE_PAUSE) &&
20764ff247aSIvan T. Ivanov 	    (state == QUP_STATE_RESET)) {
20864ff247aSIvan T. Ivanov 		writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE);
20964ff247aSIvan T. Ivanov 		writel_relaxed(QUP_STATE_CLEAR, controller->base + QUP_STATE);
21064ff247aSIvan T. Ivanov 	} else {
21164ff247aSIvan T. Ivanov 		cur_state &= ~QUP_STATE_MASK;
21264ff247aSIvan T. Ivanov 		cur_state |= state;
21364ff247aSIvan T. Ivanov 		writel_relaxed(cur_state, controller->base + QUP_STATE);
21464ff247aSIvan T. Ivanov 	}
21564ff247aSIvan T. Ivanov 
21664ff247aSIvan T. Ivanov 	loop = 0;
21764ff247aSIvan T. Ivanov 	while (!spi_qup_is_valid_state(controller)) {
21864ff247aSIvan T. Ivanov 
21964ff247aSIvan T. Ivanov 		usleep_range(SPI_DELAY_THRESHOLD, SPI_DELAY_THRESHOLD * 2);
22064ff247aSIvan T. Ivanov 
22164ff247aSIvan T. Ivanov 		if (++loop > SPI_DELAY_RETRY)
22264ff247aSIvan T. Ivanov 			return -EIO;
22364ff247aSIvan T. Ivanov 	}
22464ff247aSIvan T. Ivanov 
22564ff247aSIvan T. Ivanov 	return 0;
22664ff247aSIvan T. Ivanov }
22764ff247aSIvan T. Ivanov 
spi_qup_read_from_fifo(struct spi_qup * controller,u32 num_words)2285dc47fefSVaradarajan Narayanan static void spi_qup_read_from_fifo(struct spi_qup *controller, u32 num_words)
22964ff247aSIvan T. Ivanov {
2305dc47fefSVaradarajan Narayanan 	u8 *rx_buf = controller->rx_buf;
2317538726fSVaradarajan Narayanan 	int i, shift, num_bytes;
2327538726fSVaradarajan Narayanan 	u32 word;
23364ff247aSIvan T. Ivanov 
2347538726fSVaradarajan Narayanan 	for (; num_words; num_words--) {
23564ff247aSIvan T. Ivanov 
23664ff247aSIvan T. Ivanov 		word = readl_relaxed(controller->base + QUP_INPUT_FIFO);
23764ff247aSIvan T. Ivanov 
2385dc47fefSVaradarajan Narayanan 		num_bytes = min_t(int, spi_qup_len(controller) -
2395dc47fefSVaradarajan Narayanan 				       controller->rx_bytes,
2407538726fSVaradarajan Narayanan 				       controller->w_size);
2417538726fSVaradarajan Narayanan 
24264ff247aSIvan T. Ivanov 		if (!rx_buf) {
2437538726fSVaradarajan Narayanan 			controller->rx_bytes += num_bytes;
24464ff247aSIvan T. Ivanov 			continue;
24564ff247aSIvan T. Ivanov 		}
24664ff247aSIvan T. Ivanov 
2477538726fSVaradarajan Narayanan 		for (i = 0; i < num_bytes; i++, controller->rx_bytes++) {
24864ff247aSIvan T. Ivanov 			/*
24964ff247aSIvan T. Ivanov 			 * The data format depends on bytes per SPI word:
25064ff247aSIvan T. Ivanov 			 *  4 bytes: 0x12345678
25164ff247aSIvan T. Ivanov 			 *  2 bytes: 0x00001234
25264ff247aSIvan T. Ivanov 			 *  1 byte : 0x00000012
25364ff247aSIvan T. Ivanov 			 */
25464ff247aSIvan T. Ivanov 			shift = BITS_PER_BYTE;
2557538726fSVaradarajan Narayanan 			shift *= (controller->w_size - i - 1);
25664ff247aSIvan T. Ivanov 			rx_buf[controller->rx_bytes] = word >> shift;
25764ff247aSIvan T. Ivanov 		}
25864ff247aSIvan T. Ivanov 	}
25964ff247aSIvan T. Ivanov }
26064ff247aSIvan T. Ivanov 
spi_qup_read(struct spi_qup * controller,u32 * opflags)261cd595b99SVaradarajan Narayanan static void spi_qup_read(struct spi_qup *controller, u32 *opflags)
26264ff247aSIvan T. Ivanov {
2637538726fSVaradarajan Narayanan 	u32 remainder, words_per_block, num_words;
2647538726fSVaradarajan Narayanan 	bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
2657538726fSVaradarajan Narayanan 
2665dc47fefSVaradarajan Narayanan 	remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->rx_bytes,
2677538726fSVaradarajan Narayanan 				 controller->w_size);
2687538726fSVaradarajan Narayanan 	words_per_block = controller->in_blk_sz >> 2;
2697538726fSVaradarajan Narayanan 
2707538726fSVaradarajan Narayanan 	do {
2717538726fSVaradarajan Narayanan 		/* ACK by clearing service flag */
2727538726fSVaradarajan Narayanan 		writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
2737538726fSVaradarajan Narayanan 			       controller->base + QUP_OPERATIONAL);
2747538726fSVaradarajan Narayanan 
275a75e91baSJorge Ramirez-Ortiz 		if (!remainder)
276a75e91baSJorge Ramirez-Ortiz 			goto exit;
277a75e91baSJorge Ramirez-Ortiz 
2787538726fSVaradarajan Narayanan 		if (is_block_mode) {
2797538726fSVaradarajan Narayanan 			num_words = (remainder > words_per_block) ?
2807538726fSVaradarajan Narayanan 					words_per_block : remainder;
2817538726fSVaradarajan Narayanan 		} else {
2827538726fSVaradarajan Narayanan 			if (!spi_qup_is_flag_set(controller,
2837538726fSVaradarajan Narayanan 						 QUP_OP_IN_FIFO_NOT_EMPTY))
2847538726fSVaradarajan Narayanan 				break;
2857538726fSVaradarajan Narayanan 
2867538726fSVaradarajan Narayanan 			num_words = 1;
2877538726fSVaradarajan Narayanan 		}
2887538726fSVaradarajan Narayanan 
2897538726fSVaradarajan Narayanan 		/* read up to the maximum transfer size available */
2905dc47fefSVaradarajan Narayanan 		spi_qup_read_from_fifo(controller, num_words);
2917538726fSVaradarajan Narayanan 
2927538726fSVaradarajan Narayanan 		remainder -= num_words;
2937538726fSVaradarajan Narayanan 
2947538726fSVaradarajan Narayanan 		/* if block mode, check to see if next block is available */
2957538726fSVaradarajan Narayanan 		if (is_block_mode && !spi_qup_is_flag_set(controller,
2967538726fSVaradarajan Narayanan 					QUP_OP_IN_BLOCK_READ_REQ))
2977538726fSVaradarajan Narayanan 			break;
2987538726fSVaradarajan Narayanan 
2997538726fSVaradarajan Narayanan 	} while (remainder);
3007538726fSVaradarajan Narayanan 
3017538726fSVaradarajan Narayanan 	/*
3027538726fSVaradarajan Narayanan 	 * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block
303cd595b99SVaradarajan Narayanan 	 * reads, it has to be cleared again at the very end.  However, be sure
304cd595b99SVaradarajan Narayanan 	 * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
305cd595b99SVaradarajan Narayanan 	 * present and this is used to determine if transaction is complete
3067538726fSVaradarajan Narayanan 	 */
307a75e91baSJorge Ramirez-Ortiz exit:
308a75e91baSJorge Ramirez-Ortiz 	if (!remainder) {
309cd595b99SVaradarajan Narayanan 		*opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
310cd595b99SVaradarajan Narayanan 		if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
3117538726fSVaradarajan Narayanan 			writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
3127538726fSVaradarajan Narayanan 				       controller->base + QUP_OPERATIONAL);
313a75e91baSJorge Ramirez-Ortiz 	}
3147538726fSVaradarajan Narayanan }
3157538726fSVaradarajan Narayanan 
spi_qup_write_to_fifo(struct spi_qup * controller,u32 num_words)3165dc47fefSVaradarajan Narayanan static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words)
3177538726fSVaradarajan Narayanan {
3185dc47fefSVaradarajan Narayanan 	const u8 *tx_buf = controller->tx_buf;
3197538726fSVaradarajan Narayanan 	int i, num_bytes;
3207538726fSVaradarajan Narayanan 	u32 word, data;
32164ff247aSIvan T. Ivanov 
3227538726fSVaradarajan Narayanan 	for (; num_words; num_words--) {
32364ff247aSIvan T. Ivanov 		word = 0;
32464ff247aSIvan T. Ivanov 
3255dc47fefSVaradarajan Narayanan 		num_bytes = min_t(int, spi_qup_len(controller) -
3265dc47fefSVaradarajan Narayanan 				       controller->tx_bytes,
3277538726fSVaradarajan Narayanan 				       controller->w_size);
3287538726fSVaradarajan Narayanan 		if (tx_buf)
3297538726fSVaradarajan Narayanan 			for (i = 0; i < num_bytes; i++) {
3307538726fSVaradarajan Narayanan 				data = tx_buf[controller->tx_bytes + i];
3317538726fSVaradarajan Narayanan 				word |= data << (BITS_PER_BYTE * (3 - i));
33264ff247aSIvan T. Ivanov 			}
33364ff247aSIvan T. Ivanov 
3347538726fSVaradarajan Narayanan 		controller->tx_bytes += num_bytes;
33564ff247aSIvan T. Ivanov 
33664ff247aSIvan T. Ivanov 		writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO);
33764ff247aSIvan T. Ivanov 	}
33864ff247aSIvan T. Ivanov }
33964ff247aSIvan T. Ivanov 
spi_qup_dma_done(void * data)340612762e8SAndy Gross static void spi_qup_dma_done(void *data)
341612762e8SAndy Gross {
342612762e8SAndy Gross 	struct spi_qup *qup = data;
343612762e8SAndy Gross 
344612762e8SAndy Gross 	complete(&qup->done);
345612762e8SAndy Gross }
346612762e8SAndy Gross 
spi_qup_write(struct spi_qup * controller)3475dc47fefSVaradarajan Narayanan static void spi_qup_write(struct spi_qup *controller)
3487538726fSVaradarajan Narayanan {
3497538726fSVaradarajan Narayanan 	bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
3507538726fSVaradarajan Narayanan 	u32 remainder, words_per_block, num_words;
3517538726fSVaradarajan Narayanan 
3525dc47fefSVaradarajan Narayanan 	remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->tx_bytes,
3537538726fSVaradarajan Narayanan 				 controller->w_size);
3547538726fSVaradarajan Narayanan 	words_per_block = controller->out_blk_sz >> 2;
3557538726fSVaradarajan Narayanan 
3567538726fSVaradarajan Narayanan 	do {
3577538726fSVaradarajan Narayanan 		/* ACK by clearing service flag */
3587538726fSVaradarajan Narayanan 		writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
3597538726fSVaradarajan Narayanan 			       controller->base + QUP_OPERATIONAL);
3607538726fSVaradarajan Narayanan 
361a75e91baSJorge Ramirez-Ortiz 		/* make sure the interrupt is valid */
362a75e91baSJorge Ramirez-Ortiz 		if (!remainder)
363a75e91baSJorge Ramirez-Ortiz 			return;
364a75e91baSJorge Ramirez-Ortiz 
3657538726fSVaradarajan Narayanan 		if (is_block_mode) {
3667538726fSVaradarajan Narayanan 			num_words = (remainder > words_per_block) ?
3677538726fSVaradarajan Narayanan 				words_per_block : remainder;
3687538726fSVaradarajan Narayanan 		} else {
3697538726fSVaradarajan Narayanan 			if (spi_qup_is_flag_set(controller,
3707538726fSVaradarajan Narayanan 						QUP_OP_OUT_FIFO_FULL))
3717538726fSVaradarajan Narayanan 				break;
3727538726fSVaradarajan Narayanan 
3737538726fSVaradarajan Narayanan 			num_words = 1;
3747538726fSVaradarajan Narayanan 		}
3757538726fSVaradarajan Narayanan 
3765dc47fefSVaradarajan Narayanan 		spi_qup_write_to_fifo(controller, num_words);
3777538726fSVaradarajan Narayanan 
3787538726fSVaradarajan Narayanan 		remainder -= num_words;
3797538726fSVaradarajan Narayanan 
3807538726fSVaradarajan Narayanan 		/* if block mode, check to see if next block is available */
3817538726fSVaradarajan Narayanan 		if (is_block_mode && !spi_qup_is_flag_set(controller,
3827538726fSVaradarajan Narayanan 					QUP_OP_OUT_BLOCK_WRITE_REQ))
3837538726fSVaradarajan Narayanan 			break;
3847538726fSVaradarajan Narayanan 
3857538726fSVaradarajan Narayanan 	} while (remainder);
3867538726fSVaradarajan Narayanan }
3877538726fSVaradarajan Narayanan 
spi_qup_prep_sg(struct spi_controller * host,struct scatterlist * sgl,unsigned int nents,enum dma_transfer_direction dir,dma_async_tx_callback callback)388*597442ffSYang Yingliang static int spi_qup_prep_sg(struct spi_controller *host, struct scatterlist *sgl,
389a841b24eSVaradarajan Narayanan 			   unsigned int nents, enum dma_transfer_direction dir,
390612762e8SAndy Gross 			   dma_async_tx_callback callback)
391612762e8SAndy Gross {
392*597442ffSYang Yingliang 	struct spi_qup *qup = spi_controller_get_devdata(host);
393612762e8SAndy Gross 	unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
394612762e8SAndy Gross 	struct dma_async_tx_descriptor *desc;
395612762e8SAndy Gross 	struct dma_chan *chan;
396612762e8SAndy Gross 	dma_cookie_t cookie;
397612762e8SAndy Gross 
398a841b24eSVaradarajan Narayanan 	if (dir == DMA_MEM_TO_DEV)
399*597442ffSYang Yingliang 		chan = host->dma_tx;
400a841b24eSVaradarajan Narayanan 	else
401*597442ffSYang Yingliang 		chan = host->dma_rx;
402612762e8SAndy Gross 
403612762e8SAndy Gross 	desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
404d9a09a6cSVaradarajan Narayanan 	if (IS_ERR_OR_NULL(desc))
405d9a09a6cSVaradarajan Narayanan 		return desc ? PTR_ERR(desc) : -EINVAL;
406612762e8SAndy Gross 
407612762e8SAndy Gross 	desc->callback = callback;
408612762e8SAndy Gross 	desc->callback_param = qup;
409612762e8SAndy Gross 
410612762e8SAndy Gross 	cookie = dmaengine_submit(desc);
411612762e8SAndy Gross 
412612762e8SAndy Gross 	return dma_submit_error(cookie);
413612762e8SAndy Gross }
414612762e8SAndy Gross 
spi_qup_dma_terminate(struct spi_controller * host,struct spi_transfer * xfer)415*597442ffSYang Yingliang static void spi_qup_dma_terminate(struct spi_controller *host,
416612762e8SAndy Gross 				  struct spi_transfer *xfer)
417612762e8SAndy Gross {
418612762e8SAndy Gross 	if (xfer->tx_buf)
419*597442ffSYang Yingliang 		dmaengine_terminate_all(host->dma_tx);
420612762e8SAndy Gross 	if (xfer->rx_buf)
421*597442ffSYang Yingliang 		dmaengine_terminate_all(host->dma_rx);
422612762e8SAndy Gross }
423612762e8SAndy Gross 
spi_qup_sgl_get_nents_len(struct scatterlist * sgl,u32 max,u32 * nents)4245884e17eSVaradarajan Narayanan static u32 spi_qup_sgl_get_nents_len(struct scatterlist *sgl, u32 max,
4255884e17eSVaradarajan Narayanan 				     u32 *nents)
4265884e17eSVaradarajan Narayanan {
4275884e17eSVaradarajan Narayanan 	struct scatterlist *sg;
4285884e17eSVaradarajan Narayanan 	u32 total = 0;
4295884e17eSVaradarajan Narayanan 
4305884e17eSVaradarajan Narayanan 	for (sg = sgl; sg; sg = sg_next(sg)) {
4315884e17eSVaradarajan Narayanan 		unsigned int len = sg_dma_len(sg);
4325884e17eSVaradarajan Narayanan 
4335884e17eSVaradarajan Narayanan 		/* check for overflow as well as limit */
4345884e17eSVaradarajan Narayanan 		if (((total + len) < total) || ((total + len) > max))
4355884e17eSVaradarajan Narayanan 			break;
4365884e17eSVaradarajan Narayanan 
4375884e17eSVaradarajan Narayanan 		total += len;
4385884e17eSVaradarajan Narayanan 		(*nents)++;
4395884e17eSVaradarajan Narayanan 	}
4405884e17eSVaradarajan Narayanan 
4415884e17eSVaradarajan Narayanan 	return total;
4425884e17eSVaradarajan Narayanan }
4435884e17eSVaradarajan Narayanan 
spi_qup_do_dma(struct spi_device * spi,struct spi_transfer * xfer,unsigned long timeout)4443b5ea2c9SVaradarajan Narayanan static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
4455f13fd60SVaradarajan Narayanan 			  unsigned long timeout)
446612762e8SAndy Gross {
447612762e8SAndy Gross 	dma_async_tx_callback rx_done = NULL, tx_done = NULL;
448*597442ffSYang Yingliang 	struct spi_controller *host = spi->controller;
449*597442ffSYang Yingliang 	struct spi_qup *qup = spi_controller_get_devdata(host);
4505884e17eSVaradarajan Narayanan 	struct scatterlist *tx_sgl, *rx_sgl;
451612762e8SAndy Gross 	int ret;
452612762e8SAndy Gross 
453612762e8SAndy Gross 	if (xfer->rx_buf)
454612762e8SAndy Gross 		rx_done = spi_qup_dma_done;
455612762e8SAndy Gross 	else if (xfer->tx_buf)
456612762e8SAndy Gross 		tx_done = spi_qup_dma_done;
457612762e8SAndy Gross 
4585884e17eSVaradarajan Narayanan 	rx_sgl = xfer->rx_sg.sgl;
4595884e17eSVaradarajan Narayanan 	tx_sgl = xfer->tx_sg.sgl;
4605884e17eSVaradarajan Narayanan 
4615884e17eSVaradarajan Narayanan 	do {
4626f38f125SArnd Bergmann 		u32 rx_nents = 0, tx_nents = 0;
4635884e17eSVaradarajan Narayanan 
4645884e17eSVaradarajan Narayanan 		if (rx_sgl)
4655884e17eSVaradarajan Narayanan 			qup->n_words = spi_qup_sgl_get_nents_len(rx_sgl,
4665884e17eSVaradarajan Narayanan 					SPI_MAX_XFER, &rx_nents) / qup->w_size;
4675884e17eSVaradarajan Narayanan 		if (tx_sgl)
4685884e17eSVaradarajan Narayanan 			qup->n_words = spi_qup_sgl_get_nents_len(tx_sgl,
4695884e17eSVaradarajan Narayanan 					SPI_MAX_XFER, &tx_nents) / qup->w_size;
4705884e17eSVaradarajan Narayanan 		if (!qup->n_words)
4715884e17eSVaradarajan Narayanan 			return -EIO;
4725884e17eSVaradarajan Narayanan 
4733b5ea2c9SVaradarajan Narayanan 		ret = spi_qup_io_config(spi, xfer);
4743b5ea2c9SVaradarajan Narayanan 		if (ret)
4753b5ea2c9SVaradarajan Narayanan 			return ret;
4763b5ea2c9SVaradarajan Narayanan 
477ce00bab3SVaradarajan Narayanan 		/* before issuing the descriptors, set the QUP to run */
478ce00bab3SVaradarajan Narayanan 		ret = spi_qup_set_state(qup, QUP_STATE_RUN);
479ce00bab3SVaradarajan Narayanan 		if (ret) {
4805884e17eSVaradarajan Narayanan 			dev_warn(qup->dev, "cannot set RUN state\n");
481ce00bab3SVaradarajan Narayanan 			return ret;
482ce00bab3SVaradarajan Narayanan 		}
4835884e17eSVaradarajan Narayanan 		if (rx_sgl) {
484*597442ffSYang Yingliang 			ret = spi_qup_prep_sg(host, rx_sgl, rx_nents,
4855884e17eSVaradarajan Narayanan 					      DMA_DEV_TO_MEM, rx_done);
486612762e8SAndy Gross 			if (ret)
487612762e8SAndy Gross 				return ret;
488*597442ffSYang Yingliang 			dma_async_issue_pending(host->dma_rx);
489612762e8SAndy Gross 		}
490612762e8SAndy Gross 
4915884e17eSVaradarajan Narayanan 		if (tx_sgl) {
492*597442ffSYang Yingliang 			ret = spi_qup_prep_sg(host, tx_sgl, tx_nents,
4935884e17eSVaradarajan Narayanan 					      DMA_MEM_TO_DEV, tx_done);
494612762e8SAndy Gross 			if (ret)
495612762e8SAndy Gross 				return ret;
496612762e8SAndy Gross 
497*597442ffSYang Yingliang 			dma_async_issue_pending(host->dma_tx);
498612762e8SAndy Gross 		}
499612762e8SAndy Gross 
5005f13fd60SVaradarajan Narayanan 		if (!wait_for_completion_timeout(&qup->done, timeout))
5015f13fd60SVaradarajan Narayanan 			return -ETIMEDOUT;
5025f13fd60SVaradarajan Narayanan 
5035884e17eSVaradarajan Narayanan 		for (; rx_sgl && rx_nents--; rx_sgl = sg_next(rx_sgl))
5045884e17eSVaradarajan Narayanan 			;
5055884e17eSVaradarajan Narayanan 		for (; tx_sgl && tx_nents--; tx_sgl = sg_next(tx_sgl))
5065884e17eSVaradarajan Narayanan 			;
5075884e17eSVaradarajan Narayanan 
5085884e17eSVaradarajan Narayanan 	} while (rx_sgl || tx_sgl);
5095884e17eSVaradarajan Narayanan 
510612762e8SAndy Gross 	return 0;
511612762e8SAndy Gross }
512612762e8SAndy Gross 
spi_qup_do_pio(struct spi_device * spi,struct spi_transfer * xfer,unsigned long timeout)5133b5ea2c9SVaradarajan Narayanan static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
5145f13fd60SVaradarajan Narayanan 			  unsigned long timeout)
515612762e8SAndy Gross {
516*597442ffSYang Yingliang 	struct spi_controller *host = spi->controller;
517*597442ffSYang Yingliang 	struct spi_qup *qup = spi_controller_get_devdata(host);
5185dc47fefSVaradarajan Narayanan 	int ret, n_words, iterations, offset = 0;
5195dc47fefSVaradarajan Narayanan 
5205dc47fefSVaradarajan Narayanan 	n_words = qup->n_words;
5215dc47fefSVaradarajan Narayanan 	iterations = n_words / SPI_MAX_XFER; /* round down */
5225dc47fefSVaradarajan Narayanan 	qup->rx_buf = xfer->rx_buf;
5235dc47fefSVaradarajan Narayanan 	qup->tx_buf = xfer->tx_buf;
5245dc47fefSVaradarajan Narayanan 
5255dc47fefSVaradarajan Narayanan 	do {
5265dc47fefSVaradarajan Narayanan 		if (iterations)
5275dc47fefSVaradarajan Narayanan 			qup->n_words = SPI_MAX_XFER;
5285dc47fefSVaradarajan Narayanan 		else
5295dc47fefSVaradarajan Narayanan 			qup->n_words = n_words % SPI_MAX_XFER;
5305dc47fefSVaradarajan Narayanan 
5315dc47fefSVaradarajan Narayanan 		if (qup->tx_buf && offset)
5325dc47fefSVaradarajan Narayanan 			qup->tx_buf = xfer->tx_buf + offset * SPI_MAX_XFER;
5335dc47fefSVaradarajan Narayanan 
5345dc47fefSVaradarajan Narayanan 		if (qup->rx_buf && offset)
5355dc47fefSVaradarajan Narayanan 			qup->rx_buf = xfer->rx_buf + offset * SPI_MAX_XFER;
5365dc47fefSVaradarajan Narayanan 
5375dc47fefSVaradarajan Narayanan 		/*
5385dc47fefSVaradarajan Narayanan 		 * if the transaction is small enough, we need
5395dc47fefSVaradarajan Narayanan 		 * to fallback to FIFO mode
5405dc47fefSVaradarajan Narayanan 		 */
5415dc47fefSVaradarajan Narayanan 		if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
5425dc47fefSVaradarajan Narayanan 			qup->mode = QUP_IO_M_MODE_FIFO;
543612762e8SAndy Gross 
5443b5ea2c9SVaradarajan Narayanan 		ret = spi_qup_io_config(spi, xfer);
5453b5ea2c9SVaradarajan Narayanan 		if (ret)
5463b5ea2c9SVaradarajan Narayanan 			return ret;
5473b5ea2c9SVaradarajan Narayanan 
548612762e8SAndy Gross 		ret = spi_qup_set_state(qup, QUP_STATE_RUN);
549612762e8SAndy Gross 		if (ret) {
550612762e8SAndy Gross 			dev_warn(qup->dev, "cannot set RUN state\n");
551612762e8SAndy Gross 			return ret;
552612762e8SAndy Gross 		}
553612762e8SAndy Gross 
554612762e8SAndy Gross 		ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
555612762e8SAndy Gross 		if (ret) {
556612762e8SAndy Gross 			dev_warn(qup->dev, "cannot set PAUSE state\n");
557612762e8SAndy Gross 			return ret;
558612762e8SAndy Gross 		}
559612762e8SAndy Gross 
5607538726fSVaradarajan Narayanan 		if (qup->mode == QUP_IO_M_MODE_FIFO)
5615dc47fefSVaradarajan Narayanan 			spi_qup_write(qup);
562612762e8SAndy Gross 
563ce00bab3SVaradarajan Narayanan 		ret = spi_qup_set_state(qup, QUP_STATE_RUN);
564ce00bab3SVaradarajan Narayanan 		if (ret) {
5655dc47fefSVaradarajan Narayanan 			dev_warn(qup->dev, "cannot set RUN state\n");
566ce00bab3SVaradarajan Narayanan 			return ret;
567ce00bab3SVaradarajan Narayanan 		}
568ce00bab3SVaradarajan Narayanan 
5695f13fd60SVaradarajan Narayanan 		if (!wait_for_completion_timeout(&qup->done, timeout))
5705f13fd60SVaradarajan Narayanan 			return -ETIMEDOUT;
5715f13fd60SVaradarajan Narayanan 
5725dc47fefSVaradarajan Narayanan 		offset++;
5735dc47fefSVaradarajan Narayanan 	} while (iterations--);
5745dc47fefSVaradarajan Narayanan 
575612762e8SAndy Gross 	return 0;
576612762e8SAndy Gross }
577612762e8SAndy Gross 
spi_qup_data_pending(struct spi_qup * controller)578a75e91baSJorge Ramirez-Ortiz static bool spi_qup_data_pending(struct spi_qup *controller)
579a75e91baSJorge Ramirez-Ortiz {
580a75e91baSJorge Ramirez-Ortiz 	unsigned int remainder_tx, remainder_rx;
581a75e91baSJorge Ramirez-Ortiz 
582a75e91baSJorge Ramirez-Ortiz 	remainder_tx = DIV_ROUND_UP(spi_qup_len(controller) -
583a75e91baSJorge Ramirez-Ortiz 				    controller->tx_bytes, controller->w_size);
584a75e91baSJorge Ramirez-Ortiz 
585a75e91baSJorge Ramirez-Ortiz 	remainder_rx = DIV_ROUND_UP(spi_qup_len(controller) -
586a75e91baSJorge Ramirez-Ortiz 				    controller->rx_bytes, controller->w_size);
587a75e91baSJorge Ramirez-Ortiz 
588a75e91baSJorge Ramirez-Ortiz 	return remainder_tx || remainder_rx;
589a75e91baSJorge Ramirez-Ortiz }
590a75e91baSJorge Ramirez-Ortiz 
spi_qup_qup_irq(int irq,void * dev_id)59164ff247aSIvan T. Ivanov static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
59264ff247aSIvan T. Ivanov {
59364ff247aSIvan T. Ivanov 	struct spi_qup *controller = dev_id;
59464ff247aSIvan T. Ivanov 	u32 opflags, qup_err, spi_err;
59564ff247aSIvan T. Ivanov 	int error = 0;
59664ff247aSIvan T. Ivanov 
59764ff247aSIvan T. Ivanov 	qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
59864ff247aSIvan T. Ivanov 	spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS);
59964ff247aSIvan T. Ivanov 	opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
60064ff247aSIvan T. Ivanov 
60164ff247aSIvan T. Ivanov 	writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS);
60264ff247aSIvan T. Ivanov 	writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS);
60364ff247aSIvan T. Ivanov 
60464ff247aSIvan T. Ivanov 	if (qup_err) {
60564ff247aSIvan T. Ivanov 		if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN)
60664ff247aSIvan T. Ivanov 			dev_warn(controller->dev, "OUTPUT_OVER_RUN\n");
60764ff247aSIvan T. Ivanov 		if (qup_err & QUP_ERROR_INPUT_UNDER_RUN)
60864ff247aSIvan T. Ivanov 			dev_warn(controller->dev, "INPUT_UNDER_RUN\n");
60964ff247aSIvan T. Ivanov 		if (qup_err & QUP_ERROR_OUTPUT_UNDER_RUN)
61064ff247aSIvan T. Ivanov 			dev_warn(controller->dev, "OUTPUT_UNDER_RUN\n");
61164ff247aSIvan T. Ivanov 		if (qup_err & QUP_ERROR_INPUT_OVER_RUN)
61264ff247aSIvan T. Ivanov 			dev_warn(controller->dev, "INPUT_OVER_RUN\n");
61364ff247aSIvan T. Ivanov 
61464ff247aSIvan T. Ivanov 		error = -EIO;
61564ff247aSIvan T. Ivanov 	}
61664ff247aSIvan T. Ivanov 
61764ff247aSIvan T. Ivanov 	if (spi_err) {
61864ff247aSIvan T. Ivanov 		if (spi_err & SPI_ERROR_CLK_OVER_RUN)
61964ff247aSIvan T. Ivanov 			dev_warn(controller->dev, "CLK_OVER_RUN\n");
62064ff247aSIvan T. Ivanov 		if (spi_err & SPI_ERROR_CLK_UNDER_RUN)
62164ff247aSIvan T. Ivanov 			dev_warn(controller->dev, "CLK_UNDER_RUN\n");
62264ff247aSIvan T. Ivanov 
62364ff247aSIvan T. Ivanov 		error = -EIO;
62464ff247aSIvan T. Ivanov 	}
62564ff247aSIvan T. Ivanov 
626fa0f3db4SXingbang Liu 	spin_lock(&controller->lock);
627a75e91baSJorge Ramirez-Ortiz 	if (!controller->error)
628a75e91baSJorge Ramirez-Ortiz 		controller->error = error;
629fa0f3db4SXingbang Liu 	spin_unlock(&controller->lock);
630a75e91baSJorge Ramirez-Ortiz 
631ce7dfc71SVaradarajan Narayanan 	if (spi_qup_is_dma_xfer(controller->mode)) {
632ce7dfc71SVaradarajan Narayanan 		writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
633ce7dfc71SVaradarajan Narayanan 	} else {
63464ff247aSIvan T. Ivanov 		if (opflags & QUP_OP_IN_SERVICE_FLAG)
635cd595b99SVaradarajan Narayanan 			spi_qup_read(controller, &opflags);
63664ff247aSIvan T. Ivanov 
63764ff247aSIvan T. Ivanov 		if (opflags & QUP_OP_OUT_SERVICE_FLAG)
6385dc47fefSVaradarajan Narayanan 			spi_qup_write(controller);
639a75e91baSJorge Ramirez-Ortiz 
640a75e91baSJorge Ramirez-Ortiz 		if (!spi_qup_data_pending(controller))
641a75e91baSJorge Ramirez-Ortiz 			complete(&controller->done);
642612762e8SAndy Gross 	}
64364ff247aSIvan T. Ivanov 
644a75e91baSJorge Ramirez-Ortiz 	if (error)
64564ff247aSIvan T. Ivanov 		complete(&controller->done);
64664ff247aSIvan T. Ivanov 
647a75e91baSJorge Ramirez-Ortiz 	if (opflags & QUP_OP_MAX_INPUT_DONE_FLAG) {
648a75e91baSJorge Ramirez-Ortiz 		if (!spi_qup_is_dma_xfer(controller->mode)) {
649a75e91baSJorge Ramirez-Ortiz 			if (spi_qup_data_pending(controller))
650a75e91baSJorge Ramirez-Ortiz 				return IRQ_HANDLED;
651a75e91baSJorge Ramirez-Ortiz 		}
652a75e91baSJorge Ramirez-Ortiz 		complete(&controller->done);
653a75e91baSJorge Ramirez-Ortiz 	}
654a75e91baSJorge Ramirez-Ortiz 
65564ff247aSIvan T. Ivanov 	return IRQ_HANDLED;
65664ff247aSIvan T. Ivanov }
65764ff247aSIvan T. Ivanov 
65894b9149fSVaradarajan Narayanan /* set clock freq ... bits per word, determine mode */
spi_qup_io_prep(struct spi_device * spi,struct spi_transfer * xfer)65994b9149fSVaradarajan Narayanan static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer)
66064ff247aSIvan T. Ivanov {
661*597442ffSYang Yingliang 	struct spi_qup *controller = spi_controller_get_devdata(spi->controller);
66294b9149fSVaradarajan Narayanan 	int ret;
66364ff247aSIvan T. Ivanov 
66400cce74dSAxel Lin 	if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
66564ff247aSIvan T. Ivanov 		dev_err(controller->dev, "too big size for loopback %d > %d\n",
66664ff247aSIvan T. Ivanov 			xfer->len, controller->in_fifo_sz);
66764ff247aSIvan T. Ivanov 		return -EIO;
66864ff247aSIvan T. Ivanov 	}
66964ff247aSIvan T. Ivanov 
67064ff247aSIvan T. Ivanov 	ret = clk_set_rate(controller->cclk, xfer->speed_hz);
67164ff247aSIvan T. Ivanov 	if (ret) {
67264ff247aSIvan T. Ivanov 		dev_err(controller->dev, "fail to set frequency %d",
67364ff247aSIvan T. Ivanov 			xfer->speed_hz);
67464ff247aSIvan T. Ivanov 		return -EIO;
67564ff247aSIvan T. Ivanov 	}
67664ff247aSIvan T. Ivanov 
67794b9149fSVaradarajan Narayanan 	controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
67894b9149fSVaradarajan Narayanan 	controller->n_words = xfer->len / controller->w_size;
67994b9149fSVaradarajan Narayanan 
68094b9149fSVaradarajan Narayanan 	if (controller->n_words <= (controller->in_fifo_sz / sizeof(u32)))
68194b9149fSVaradarajan Narayanan 		controller->mode = QUP_IO_M_MODE_FIFO;
682*597442ffSYang Yingliang 	else if (spi->controller->can_dma &&
683*597442ffSYang Yingliang 		 spi->controller->can_dma(spi->controller, spi, xfer) &&
684*597442ffSYang Yingliang 		 spi->controller->cur_msg_mapped)
68594b9149fSVaradarajan Narayanan 		controller->mode = QUP_IO_M_MODE_BAM;
68694b9149fSVaradarajan Narayanan 	else
68794b9149fSVaradarajan Narayanan 		controller->mode = QUP_IO_M_MODE_BLOCK;
68894b9149fSVaradarajan Narayanan 
68994b9149fSVaradarajan Narayanan 	return 0;
69094b9149fSVaradarajan Narayanan }
69194b9149fSVaradarajan Narayanan 
69294b9149fSVaradarajan Narayanan /* prep qup for another spi transaction of specific type */
spi_qup_io_config(struct spi_device * spi,struct spi_transfer * xfer)69394b9149fSVaradarajan Narayanan static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
69494b9149fSVaradarajan Narayanan {
695*597442ffSYang Yingliang 	struct spi_qup *controller = spi_controller_get_devdata(spi->controller);
69694b9149fSVaradarajan Narayanan 	u32 config, iomode, control;
69794b9149fSVaradarajan Narayanan 	unsigned long flags;
69894b9149fSVaradarajan Narayanan 
69994b9149fSVaradarajan Narayanan 	spin_lock_irqsave(&controller->lock, flags);
70094b9149fSVaradarajan Narayanan 	controller->xfer     = xfer;
70194b9149fSVaradarajan Narayanan 	controller->error    = 0;
70294b9149fSVaradarajan Narayanan 	controller->rx_bytes = 0;
70394b9149fSVaradarajan Narayanan 	controller->tx_bytes = 0;
70494b9149fSVaradarajan Narayanan 	spin_unlock_irqrestore(&controller->lock, flags);
70594b9149fSVaradarajan Narayanan 
70694b9149fSVaradarajan Narayanan 
70764ff247aSIvan T. Ivanov 	if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
70864ff247aSIvan T. Ivanov 		dev_err(controller->dev, "cannot set RESET state\n");
70964ff247aSIvan T. Ivanov 		return -EIO;
71064ff247aSIvan T. Ivanov 	}
71164ff247aSIvan T. Ivanov 
71294b9149fSVaradarajan Narayanan 	switch (controller->mode) {
71394b9149fSVaradarajan Narayanan 	case QUP_IO_M_MODE_FIFO:
71494b9149fSVaradarajan Narayanan 		writel_relaxed(controller->n_words,
71594b9149fSVaradarajan Narayanan 			       controller->base + QUP_MX_READ_CNT);
71694b9149fSVaradarajan Narayanan 		writel_relaxed(controller->n_words,
71794b9149fSVaradarajan Narayanan 			       controller->base + QUP_MX_WRITE_CNT);
71864ff247aSIvan T. Ivanov 		/* must be zero for FIFO */
71964ff247aSIvan T. Ivanov 		writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT);
72064ff247aSIvan T. Ivanov 		writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
72194b9149fSVaradarajan Narayanan 		break;
72294b9149fSVaradarajan Narayanan 	case QUP_IO_M_MODE_BAM:
72394b9149fSVaradarajan Narayanan 		writel_relaxed(controller->n_words,
72494b9149fSVaradarajan Narayanan 			       controller->base + QUP_MX_INPUT_CNT);
72594b9149fSVaradarajan Narayanan 		writel_relaxed(controller->n_words,
72694b9149fSVaradarajan Narayanan 			       controller->base + QUP_MX_OUTPUT_CNT);
72764ff247aSIvan T. Ivanov 		/* must be zero for BLOCK and BAM */
72864ff247aSIvan T. Ivanov 		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
72964ff247aSIvan T. Ivanov 		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
730612762e8SAndy Gross 
731612762e8SAndy Gross 		if (!controller->qup_v1) {
732612762e8SAndy Gross 			void __iomem *input_cnt;
733612762e8SAndy Gross 
734612762e8SAndy Gross 			input_cnt = controller->base + QUP_MX_INPUT_CNT;
735612762e8SAndy Gross 			/*
736612762e8SAndy Gross 			 * for DMA transfers, both QUP_MX_INPUT_CNT and
737612762e8SAndy Gross 			 * QUP_MX_OUTPUT_CNT must be zero to all cases but one.
738612762e8SAndy Gross 			 * That case is a non-balanced transfer when there is
739612762e8SAndy Gross 			 * only a rx_buf.
740612762e8SAndy Gross 			 */
741612762e8SAndy Gross 			if (xfer->tx_buf)
742612762e8SAndy Gross 				writel_relaxed(0, input_cnt);
743612762e8SAndy Gross 			else
74494b9149fSVaradarajan Narayanan 				writel_relaxed(controller->n_words, input_cnt);
745612762e8SAndy Gross 
746612762e8SAndy Gross 			writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
747612762e8SAndy Gross 		}
74894b9149fSVaradarajan Narayanan 		break;
74994b9149fSVaradarajan Narayanan 	case QUP_IO_M_MODE_BLOCK:
75094b9149fSVaradarajan Narayanan 		reinit_completion(&controller->done);
75194b9149fSVaradarajan Narayanan 		writel_relaxed(controller->n_words,
75294b9149fSVaradarajan Narayanan 			       controller->base + QUP_MX_INPUT_CNT);
75394b9149fSVaradarajan Narayanan 		writel_relaxed(controller->n_words,
75494b9149fSVaradarajan Narayanan 			       controller->base + QUP_MX_OUTPUT_CNT);
75532ecab99SVaradarajan Narayanan 		/* must be zero for BLOCK and BAM */
75632ecab99SVaradarajan Narayanan 		writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
75732ecab99SVaradarajan Narayanan 		writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);
75894b9149fSVaradarajan Narayanan 		break;
75994b9149fSVaradarajan Narayanan 	default:
76094b9149fSVaradarajan Narayanan 		dev_err(controller->dev, "unknown mode = %d\n",
76194b9149fSVaradarajan Narayanan 				controller->mode);
76294b9149fSVaradarajan Narayanan 		return -EIO;
76364ff247aSIvan T. Ivanov 	}
76464ff247aSIvan T. Ivanov 
76564ff247aSIvan T. Ivanov 	iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
76664ff247aSIvan T. Ivanov 	/* Set input and output transfer mode */
76764ff247aSIvan T. Ivanov 	iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);
768612762e8SAndy Gross 
76932ecab99SVaradarajan Narayanan 	if (!spi_qup_is_dma_xfer(controller->mode))
77064ff247aSIvan T. Ivanov 		iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
771612762e8SAndy Gross 	else
772612762e8SAndy Gross 		iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;
773612762e8SAndy Gross 
77432ecab99SVaradarajan Narayanan 	iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
77532ecab99SVaradarajan Narayanan 	iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
77664ff247aSIvan T. Ivanov 
77764ff247aSIvan T. Ivanov 	writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);
77864ff247aSIvan T. Ivanov 
7790667dd5fSIvan T. Ivanov 	control = readl_relaxed(controller->base + SPI_IO_CONTROL);
7800667dd5fSIvan T. Ivanov 
7810667dd5fSIvan T. Ivanov 	if (spi->mode & SPI_CPOL)
7820667dd5fSIvan T. Ivanov 		control |= SPI_IO_C_CLK_IDLE_HIGH;
7830667dd5fSIvan T. Ivanov 	else
7840667dd5fSIvan T. Ivanov 		control &= ~SPI_IO_C_CLK_IDLE_HIGH;
7850667dd5fSIvan T. Ivanov 
7860667dd5fSIvan T. Ivanov 	writel_relaxed(control, controller->base + SPI_IO_CONTROL);
7870667dd5fSIvan T. Ivanov 
78864ff247aSIvan T. Ivanov 	config = readl_relaxed(controller->base + SPI_CONFIG);
78964ff247aSIvan T. Ivanov 
79000cce74dSAxel Lin 	if (spi->mode & SPI_LOOP)
79164ff247aSIvan T. Ivanov 		config |= SPI_CONFIG_LOOPBACK;
79264ff247aSIvan T. Ivanov 	else
79364ff247aSIvan T. Ivanov 		config &= ~SPI_CONFIG_LOOPBACK;
79464ff247aSIvan T. Ivanov 
79500cce74dSAxel Lin 	if (spi->mode & SPI_CPHA)
79664ff247aSIvan T. Ivanov 		config &= ~SPI_CONFIG_INPUT_FIRST;
79764ff247aSIvan T. Ivanov 	else
79864ff247aSIvan T. Ivanov 		config |= SPI_CONFIG_INPUT_FIRST;
79964ff247aSIvan T. Ivanov 
80064ff247aSIvan T. Ivanov 	/*
80164ff247aSIvan T. Ivanov 	 * HS_MODE improves signal stability for spi-clk high rates,
80264ff247aSIvan T. Ivanov 	 * but is invalid in loop back mode.
80364ff247aSIvan T. Ivanov 	 */
80400cce74dSAxel Lin 	if ((xfer->speed_hz >= SPI_HS_MIN_RATE) && !(spi->mode & SPI_LOOP))
80564ff247aSIvan T. Ivanov 		config |= SPI_CONFIG_HS_MODE;
80664ff247aSIvan T. Ivanov 	else
80764ff247aSIvan T. Ivanov 		config &= ~SPI_CONFIG_HS_MODE;
80864ff247aSIvan T. Ivanov 
80964ff247aSIvan T. Ivanov 	writel_relaxed(config, controller->base + SPI_CONFIG);
81064ff247aSIvan T. Ivanov 
81164ff247aSIvan T. Ivanov 	config = readl_relaxed(controller->base + QUP_CONFIG);
81264ff247aSIvan T. Ivanov 	config &= ~(QUP_CONFIG_NO_INPUT | QUP_CONFIG_NO_OUTPUT | QUP_CONFIG_N);
81364ff247aSIvan T. Ivanov 	config |= xfer->bits_per_word - 1;
81464ff247aSIvan T. Ivanov 	config |= QUP_CONFIG_SPI_MODE;
815612762e8SAndy Gross 
81632ecab99SVaradarajan Narayanan 	if (spi_qup_is_dma_xfer(controller->mode)) {
817612762e8SAndy Gross 		if (!xfer->tx_buf)
818612762e8SAndy Gross 			config |= QUP_CONFIG_NO_OUTPUT;
819612762e8SAndy Gross 		if (!xfer->rx_buf)
820612762e8SAndy Gross 			config |= QUP_CONFIG_NO_INPUT;
821612762e8SAndy Gross 	}
822612762e8SAndy Gross 
82364ff247aSIvan T. Ivanov 	writel_relaxed(config, controller->base + QUP_CONFIG);
82464ff247aSIvan T. Ivanov 
82570cea0a9SAndy Gross 	/* only write to OPERATIONAL_MASK when register is present */
826612762e8SAndy Gross 	if (!controller->qup_v1) {
827612762e8SAndy Gross 		u32 mask = 0;
828612762e8SAndy Gross 
829612762e8SAndy Gross 		/*
830612762e8SAndy Gross 		 * mask INPUT and OUTPUT service flags to prevent IRQs on FIFO
831612762e8SAndy Gross 		 * status change in BAM mode
832612762e8SAndy Gross 		 */
833612762e8SAndy Gross 
83432ecab99SVaradarajan Narayanan 		if (spi_qup_is_dma_xfer(controller->mode))
835612762e8SAndy Gross 			mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;
836612762e8SAndy Gross 
837612762e8SAndy Gross 		writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
838612762e8SAndy Gross 	}
839612762e8SAndy Gross 
84064ff247aSIvan T. Ivanov 	return 0;
84164ff247aSIvan T. Ivanov }
84264ff247aSIvan T. Ivanov 
spi_qup_transfer_one(struct spi_controller * host,struct spi_device * spi,struct spi_transfer * xfer)843*597442ffSYang Yingliang static int spi_qup_transfer_one(struct spi_controller *host,
84464ff247aSIvan T. Ivanov 			      struct spi_device *spi,
84564ff247aSIvan T. Ivanov 			      struct spi_transfer *xfer)
84664ff247aSIvan T. Ivanov {
847*597442ffSYang Yingliang 	struct spi_qup *controller = spi_controller_get_devdata(host);
84864ff247aSIvan T. Ivanov 	unsigned long timeout, flags;
8494a6c7d6fSColin Ian King 	int ret;
85064ff247aSIvan T. Ivanov 
85194b9149fSVaradarajan Narayanan 	ret = spi_qup_io_prep(spi, xfer);
85294b9149fSVaradarajan Narayanan 	if (ret)
85394b9149fSVaradarajan Narayanan 		return ret;
85494b9149fSVaradarajan Narayanan 
85564ff247aSIvan T. Ivanov 	timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
8565dc47fefSVaradarajan Narayanan 	timeout = DIV_ROUND_UP(min_t(unsigned long, SPI_MAX_XFER,
8575dc47fefSVaradarajan Narayanan 				     xfer->len) * 8, timeout);
85864ff247aSIvan T. Ivanov 	timeout = 100 * msecs_to_jiffies(timeout);
85964ff247aSIvan T. Ivanov 
86064ff247aSIvan T. Ivanov 	reinit_completion(&controller->done);
86164ff247aSIvan T. Ivanov 
86264ff247aSIvan T. Ivanov 	spin_lock_irqsave(&controller->lock, flags);
86364ff247aSIvan T. Ivanov 	controller->xfer     = xfer;
86464ff247aSIvan T. Ivanov 	controller->error    = 0;
86564ff247aSIvan T. Ivanov 	controller->rx_bytes = 0;
86664ff247aSIvan T. Ivanov 	controller->tx_bytes = 0;
86764ff247aSIvan T. Ivanov 	spin_unlock_irqrestore(&controller->lock, flags);
86864ff247aSIvan T. Ivanov 
86932ecab99SVaradarajan Narayanan 	if (spi_qup_is_dma_xfer(controller->mode))
8703b5ea2c9SVaradarajan Narayanan 		ret = spi_qup_do_dma(spi, xfer, timeout);
871612762e8SAndy Gross 	else
8723b5ea2c9SVaradarajan Narayanan 		ret = spi_qup_do_pio(spi, xfer, timeout);
87364ff247aSIvan T. Ivanov 
87464ff247aSIvan T. Ivanov 	spi_qup_set_state(controller, QUP_STATE_RESET);
87564ff247aSIvan T. Ivanov 	spin_lock_irqsave(&controller->lock, flags);
87664ff247aSIvan T. Ivanov 	if (!ret)
87764ff247aSIvan T. Ivanov 		ret = controller->error;
87864ff247aSIvan T. Ivanov 	spin_unlock_irqrestore(&controller->lock, flags);
879612762e8SAndy Gross 
88032ecab99SVaradarajan Narayanan 	if (ret && spi_qup_is_dma_xfer(controller->mode))
881*597442ffSYang Yingliang 		spi_qup_dma_terminate(host, xfer);
882612762e8SAndy Gross 
883612762e8SAndy Gross 	return ret;
884612762e8SAndy Gross }
885612762e8SAndy Gross 
spi_qup_can_dma(struct spi_controller * host,struct spi_device * spi,struct spi_transfer * xfer)886*597442ffSYang Yingliang static bool spi_qup_can_dma(struct spi_controller *host, struct spi_device *spi,
887612762e8SAndy Gross 			    struct spi_transfer *xfer)
888612762e8SAndy Gross {
889*597442ffSYang Yingliang 	struct spi_qup *qup = spi_controller_get_devdata(host);
890612762e8SAndy Gross 	size_t dma_align = dma_get_cache_alignment();
89132ecab99SVaradarajan Narayanan 	int n_words;
892612762e8SAndy Gross 
89332ecab99SVaradarajan Narayanan 	if (xfer->rx_buf) {
89432ecab99SVaradarajan Narayanan 		if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) ||
895*597442ffSYang Yingliang 		    IS_ERR_OR_NULL(host->dma_rx))
896612762e8SAndy Gross 			return false;
89732ecab99SVaradarajan Narayanan 		if (qup->qup_v1 && (xfer->len % qup->in_blk_sz))
898612762e8SAndy Gross 			return false;
89932ecab99SVaradarajan Narayanan 	}
900612762e8SAndy Gross 
90132ecab99SVaradarajan Narayanan 	if (xfer->tx_buf) {
90232ecab99SVaradarajan Narayanan 		if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) ||
903*597442ffSYang Yingliang 		    IS_ERR_OR_NULL(host->dma_tx))
904612762e8SAndy Gross 			return false;
90532ecab99SVaradarajan Narayanan 		if (qup->qup_v1 && (xfer->len % qup->out_blk_sz))
90632ecab99SVaradarajan Narayanan 			return false;
90732ecab99SVaradarajan Narayanan 	}
908612762e8SAndy Gross 
90932ecab99SVaradarajan Narayanan 	n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
91032ecab99SVaradarajan Narayanan 	if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
91132ecab99SVaradarajan Narayanan 		return false;
912612762e8SAndy Gross 
913612762e8SAndy Gross 	return true;
914612762e8SAndy Gross }
915612762e8SAndy Gross 
spi_qup_release_dma(struct spi_controller * host)916*597442ffSYang Yingliang static void spi_qup_release_dma(struct spi_controller *host)
917612762e8SAndy Gross {
918*597442ffSYang Yingliang 	if (!IS_ERR_OR_NULL(host->dma_rx))
919*597442ffSYang Yingliang 		dma_release_channel(host->dma_rx);
920*597442ffSYang Yingliang 	if (!IS_ERR_OR_NULL(host->dma_tx))
921*597442ffSYang Yingliang 		dma_release_channel(host->dma_tx);
922612762e8SAndy Gross }
923612762e8SAndy Gross 
spi_qup_init_dma(struct spi_controller * host,resource_size_t base)924*597442ffSYang Yingliang static int spi_qup_init_dma(struct spi_controller *host, resource_size_t base)
925612762e8SAndy Gross {
926*597442ffSYang Yingliang 	struct spi_qup *spi = spi_controller_get_devdata(host);
927612762e8SAndy Gross 	struct dma_slave_config *rx_conf = &spi->rx_conf,
928612762e8SAndy Gross 				*tx_conf = &spi->tx_conf;
929612762e8SAndy Gross 	struct device *dev = spi->dev;
930612762e8SAndy Gross 	int ret;
931612762e8SAndy Gross 
932612762e8SAndy Gross 	/* allocate dma resources, if available */
933*597442ffSYang Yingliang 	host->dma_rx = dma_request_chan(dev, "rx");
934*597442ffSYang Yingliang 	if (IS_ERR(host->dma_rx))
935*597442ffSYang Yingliang 		return PTR_ERR(host->dma_rx);
936612762e8SAndy Gross 
937*597442ffSYang Yingliang 	host->dma_tx = dma_request_chan(dev, "tx");
938*597442ffSYang Yingliang 	if (IS_ERR(host->dma_tx)) {
939*597442ffSYang Yingliang 		ret = PTR_ERR(host->dma_tx);
940612762e8SAndy Gross 		goto err_tx;
941612762e8SAndy Gross 	}
942612762e8SAndy Gross 
943612762e8SAndy Gross 	/* set DMA parameters */
944612762e8SAndy Gross 	rx_conf->direction = DMA_DEV_TO_MEM;
945612762e8SAndy Gross 	rx_conf->device_fc = 1;
946612762e8SAndy Gross 	rx_conf->src_addr = base + QUP_INPUT_FIFO;
947612762e8SAndy Gross 	rx_conf->src_maxburst = spi->in_blk_sz;
948612762e8SAndy Gross 
949612762e8SAndy Gross 	tx_conf->direction = DMA_MEM_TO_DEV;
950612762e8SAndy Gross 	tx_conf->device_fc = 1;
951612762e8SAndy Gross 	tx_conf->dst_addr = base + QUP_OUTPUT_FIFO;
952612762e8SAndy Gross 	tx_conf->dst_maxburst = spi->out_blk_sz;
953612762e8SAndy Gross 
954*597442ffSYang Yingliang 	ret = dmaengine_slave_config(host->dma_rx, rx_conf);
955612762e8SAndy Gross 	if (ret) {
956612762e8SAndy Gross 		dev_err(dev, "failed to configure RX channel\n");
957612762e8SAndy Gross 		goto err;
958612762e8SAndy Gross 	}
959612762e8SAndy Gross 
960*597442ffSYang Yingliang 	ret = dmaengine_slave_config(host->dma_tx, tx_conf);
961612762e8SAndy Gross 	if (ret) {
962612762e8SAndy Gross 		dev_err(dev, "failed to configure TX channel\n");
963612762e8SAndy Gross 		goto err;
964612762e8SAndy Gross 	}
965612762e8SAndy Gross 
966612762e8SAndy Gross 	return 0;
967612762e8SAndy Gross 
968612762e8SAndy Gross err:
969*597442ffSYang Yingliang 	dma_release_channel(host->dma_tx);
970612762e8SAndy Gross err_tx:
971*597442ffSYang Yingliang 	dma_release_channel(host->dma_rx);
97264ff247aSIvan T. Ivanov 	return ret;
97364ff247aSIvan T. Ivanov }
97464ff247aSIvan T. Ivanov 
spi_qup_set_cs(struct spi_device * spi,bool val)975b702b9fbSVaradarajan Narayanan static void spi_qup_set_cs(struct spi_device *spi, bool val)
976b702b9fbSVaradarajan Narayanan {
977b702b9fbSVaradarajan Narayanan 	struct spi_qup *controller;
978b702b9fbSVaradarajan Narayanan 	u32 spi_ioc;
979b702b9fbSVaradarajan Narayanan 	u32 spi_ioc_orig;
980b702b9fbSVaradarajan Narayanan 
981*597442ffSYang Yingliang 	controller = spi_controller_get_devdata(spi->controller);
982b702b9fbSVaradarajan Narayanan 	spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL);
983b702b9fbSVaradarajan Narayanan 	spi_ioc_orig = spi_ioc;
984b702b9fbSVaradarajan Narayanan 	if (!val)
985b702b9fbSVaradarajan Narayanan 		spi_ioc |= SPI_IO_C_FORCE_CS;
986b702b9fbSVaradarajan Narayanan 	else
987b702b9fbSVaradarajan Narayanan 		spi_ioc &= ~SPI_IO_C_FORCE_CS;
988b702b9fbSVaradarajan Narayanan 
989b702b9fbSVaradarajan Narayanan 	if (spi_ioc != spi_ioc_orig)
990b702b9fbSVaradarajan Narayanan 		writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL);
991b702b9fbSVaradarajan Narayanan }
992b702b9fbSVaradarajan Narayanan 
spi_qup_probe(struct platform_device * pdev)99364ff247aSIvan T. Ivanov static int spi_qup_probe(struct platform_device *pdev)
99464ff247aSIvan T. Ivanov {
995*597442ffSYang Yingliang 	struct spi_controller *host;
99664ff247aSIvan T. Ivanov 	struct clk *iclk, *cclk;
99764ff247aSIvan T. Ivanov 	struct spi_qup *controller;
99864ff247aSIvan T. Ivanov 	struct resource *res;
99964ff247aSIvan T. Ivanov 	struct device *dev;
100064ff247aSIvan T. Ivanov 	void __iomem *base;
100112cb89e3SIvan T. Ivanov 	u32 max_freq, iomode, num_cs;
100264ff247aSIvan T. Ivanov 	int ret, irq, size;
100364ff247aSIvan T. Ivanov 
100464ff247aSIvan T. Ivanov 	dev = &pdev->dev;
1005dc2eb794SMd Sadre Alam 	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
100664ff247aSIvan T. Ivanov 	if (IS_ERR(base))
100764ff247aSIvan T. Ivanov 		return PTR_ERR(base);
100864ff247aSIvan T. Ivanov 
100964ff247aSIvan T. Ivanov 	irq = platform_get_irq(pdev, 0);
101064ff247aSIvan T. Ivanov 	if (irq < 0)
101164ff247aSIvan T. Ivanov 		return irq;
101264ff247aSIvan T. Ivanov 
101364ff247aSIvan T. Ivanov 	cclk = devm_clk_get(dev, "core");
101464ff247aSIvan T. Ivanov 	if (IS_ERR(cclk))
101564ff247aSIvan T. Ivanov 		return PTR_ERR(cclk);
101664ff247aSIvan T. Ivanov 
101764ff247aSIvan T. Ivanov 	iclk = devm_clk_get(dev, "iface");
101864ff247aSIvan T. Ivanov 	if (IS_ERR(iclk))
101964ff247aSIvan T. Ivanov 		return PTR_ERR(iclk);
102064ff247aSIvan T. Ivanov 
102164ff247aSIvan T. Ivanov 	/* This is optional parameter */
102264ff247aSIvan T. Ivanov 	if (of_property_read_u32(dev->of_node, "spi-max-frequency", &max_freq))
102364ff247aSIvan T. Ivanov 		max_freq = SPI_MAX_RATE;
102464ff247aSIvan T. Ivanov 
102564ff247aSIvan T. Ivanov 	if (!max_freq || max_freq > SPI_MAX_RATE) {
102664ff247aSIvan T. Ivanov 		dev_err(dev, "invalid clock frequency %d\n", max_freq);
102764ff247aSIvan T. Ivanov 		return -ENXIO;
102864ff247aSIvan T. Ivanov 	}
102964ff247aSIvan T. Ivanov 
1030*597442ffSYang Yingliang 	host = spi_alloc_host(dev, sizeof(struct spi_qup));
1031*597442ffSYang Yingliang 	if (!host) {
1032*597442ffSYang Yingliang 		dev_err(dev, "cannot allocate host\n");
103364ff247aSIvan T. Ivanov 		return -ENOMEM;
103464ff247aSIvan T. Ivanov 	}
103564ff247aSIvan T. Ivanov 
10364a8573abSAndy Gross 	/* use num-cs unless not present or out of range */
103712cb89e3SIvan T. Ivanov 	if (of_property_read_u32(dev->of_node, "num-cs", &num_cs) ||
103812cb89e3SIvan T. Ivanov 	    num_cs > SPI_NUM_CHIPSELECTS)
1039*597442ffSYang Yingliang 		host->num_chipselect = SPI_NUM_CHIPSELECTS;
104012cb89e3SIvan T. Ivanov 	else
1041*597442ffSYang Yingliang 		host->num_chipselect = num_cs;
10424a8573abSAndy Gross 
1043*597442ffSYang Yingliang 	host->use_gpio_descriptors = true;
1044*597442ffSYang Yingliang 	host->max_native_cs = SPI_NUM_CHIPSELECTS;
1045*597442ffSYang Yingliang 	host->bus_num = pdev->id;
1046*597442ffSYang Yingliang 	host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LOOP;
1047*597442ffSYang Yingliang 	host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32);
1048*597442ffSYang Yingliang 	host->max_speed_hz = max_freq;
1049*597442ffSYang Yingliang 	host->transfer_one = spi_qup_transfer_one;
1050*597442ffSYang Yingliang 	host->dev.of_node = pdev->dev.of_node;
1051*597442ffSYang Yingliang 	host->auto_runtime_pm = true;
1052*597442ffSYang Yingliang 	host->dma_alignment = dma_get_cache_alignment();
1053*597442ffSYang Yingliang 	host->max_dma_len = SPI_MAX_XFER;
105464ff247aSIvan T. Ivanov 
1055*597442ffSYang Yingliang 	platform_set_drvdata(pdev, host);
105664ff247aSIvan T. Ivanov 
1057*597442ffSYang Yingliang 	controller = spi_controller_get_devdata(host);
105864ff247aSIvan T. Ivanov 
105964ff247aSIvan T. Ivanov 	controller->dev = dev;
106064ff247aSIvan T. Ivanov 	controller->base = base;
106164ff247aSIvan T. Ivanov 	controller->iclk = iclk;
106264ff247aSIvan T. Ivanov 	controller->cclk = cclk;
106364ff247aSIvan T. Ivanov 	controller->irq = irq;
106464ff247aSIvan T. Ivanov 
1065*597442ffSYang Yingliang 	ret = spi_qup_init_dma(host, res->start);
1066612762e8SAndy Gross 	if (ret == -EPROBE_DEFER)
1067612762e8SAndy Gross 		goto error;
1068612762e8SAndy Gross 	else if (!ret)
1069*597442ffSYang Yingliang 		host->can_dma = spi_qup_can_dma;
1070612762e8SAndy Gross 
107188a19814SArnd Bergmann 	controller->qup_v1 = (uintptr_t)of_device_get_match_data(dev);
107270cea0a9SAndy Gross 
1073b702b9fbSVaradarajan Narayanan 	if (!controller->qup_v1)
1074*597442ffSYang Yingliang 		host->set_cs = spi_qup_set_cs;
1075b702b9fbSVaradarajan Narayanan 
107664ff247aSIvan T. Ivanov 	spin_lock_init(&controller->lock);
107764ff247aSIvan T. Ivanov 	init_completion(&controller->done);
107864ff247aSIvan T. Ivanov 
10790c331fd1SStephan Gerhold 	ret = clk_prepare_enable(cclk);
10800c331fd1SStephan Gerhold 	if (ret) {
10810c331fd1SStephan Gerhold 		dev_err(dev, "cannot enable core clock\n");
10820c331fd1SStephan Gerhold 		goto error_dma;
10830c331fd1SStephan Gerhold 	}
10840c331fd1SStephan Gerhold 
10850c331fd1SStephan Gerhold 	ret = clk_prepare_enable(iclk);
10860c331fd1SStephan Gerhold 	if (ret) {
10870c331fd1SStephan Gerhold 		clk_disable_unprepare(cclk);
10880c331fd1SStephan Gerhold 		dev_err(dev, "cannot enable iface clock\n");
10890c331fd1SStephan Gerhold 		goto error_dma;
10900c331fd1SStephan Gerhold 	}
10910c331fd1SStephan Gerhold 
109264ff247aSIvan T. Ivanov 	iomode = readl_relaxed(base + QUP_IO_M_MODES);
109364ff247aSIvan T. Ivanov 
109464ff247aSIvan T. Ivanov 	size = QUP_IO_M_OUTPUT_BLOCK_SIZE(iomode);
109564ff247aSIvan T. Ivanov 	if (size)
109664ff247aSIvan T. Ivanov 		controller->out_blk_sz = size * 16;
109764ff247aSIvan T. Ivanov 	else
109864ff247aSIvan T. Ivanov 		controller->out_blk_sz = 4;
109964ff247aSIvan T. Ivanov 
110064ff247aSIvan T. Ivanov 	size = QUP_IO_M_INPUT_BLOCK_SIZE(iomode);
110164ff247aSIvan T. Ivanov 	if (size)
110264ff247aSIvan T. Ivanov 		controller->in_blk_sz = size * 16;
110364ff247aSIvan T. Ivanov 	else
110464ff247aSIvan T. Ivanov 		controller->in_blk_sz = 4;
110564ff247aSIvan T. Ivanov 
110664ff247aSIvan T. Ivanov 	size = QUP_IO_M_OUTPUT_FIFO_SIZE(iomode);
110764ff247aSIvan T. Ivanov 	controller->out_fifo_sz = controller->out_blk_sz * (2 << size);
110864ff247aSIvan T. Ivanov 
110964ff247aSIvan T. Ivanov 	size = QUP_IO_M_INPUT_FIFO_SIZE(iomode);
111064ff247aSIvan T. Ivanov 	controller->in_fifo_sz = controller->in_blk_sz * (2 << size);
111164ff247aSIvan T. Ivanov 
111270cea0a9SAndy Gross 	dev_info(dev, "IN:block:%d, fifo:%d, OUT:block:%d, fifo:%d\n",
111370cea0a9SAndy Gross 		 controller->in_blk_sz, controller->in_fifo_sz,
111464ff247aSIvan T. Ivanov 		 controller->out_blk_sz, controller->out_fifo_sz);
111564ff247aSIvan T. Ivanov 
111664ff247aSIvan T. Ivanov 	writel_relaxed(1, base + QUP_SW_RESET);
111764ff247aSIvan T. Ivanov 
111864ff247aSIvan T. Ivanov 	ret = spi_qup_set_state(controller, QUP_STATE_RESET);
111964ff247aSIvan T. Ivanov 	if (ret) {
112064ff247aSIvan T. Ivanov 		dev_err(dev, "cannot set RESET state\n");
11210c331fd1SStephan Gerhold 		goto error_clk;
112264ff247aSIvan T. Ivanov 	}
112364ff247aSIvan T. Ivanov 
112464ff247aSIvan T. Ivanov 	writel_relaxed(0, base + QUP_OPERATIONAL);
112564ff247aSIvan T. Ivanov 	writel_relaxed(0, base + QUP_IO_M_MODES);
112670cea0a9SAndy Gross 
112770cea0a9SAndy Gross 	if (!controller->qup_v1)
112864ff247aSIvan T. Ivanov 		writel_relaxed(0, base + QUP_OPERATIONAL_MASK);
112970cea0a9SAndy Gross 
113064ff247aSIvan T. Ivanov 	writel_relaxed(SPI_ERROR_CLK_UNDER_RUN | SPI_ERROR_CLK_OVER_RUN,
113164ff247aSIvan T. Ivanov 		       base + SPI_ERROR_FLAGS_EN);
113264ff247aSIvan T. Ivanov 
113370cea0a9SAndy Gross 	/* if earlier version of the QUP, disable INPUT_OVERRUN */
113470cea0a9SAndy Gross 	if (controller->qup_v1)
113570cea0a9SAndy Gross 		writel_relaxed(QUP_ERROR_OUTPUT_OVER_RUN |
113670cea0a9SAndy Gross 			QUP_ERROR_INPUT_UNDER_RUN | QUP_ERROR_OUTPUT_UNDER_RUN,
113770cea0a9SAndy Gross 			base + QUP_ERROR_FLAGS_EN);
113870cea0a9SAndy Gross 
113964ff247aSIvan T. Ivanov 	writel_relaxed(0, base + SPI_CONFIG);
114064ff247aSIvan T. Ivanov 	writel_relaxed(SPI_IO_C_NO_TRI_STATE, base + SPI_IO_CONTROL);
114164ff247aSIvan T. Ivanov 
114264ff247aSIvan T. Ivanov 	ret = devm_request_irq(dev, irq, spi_qup_qup_irq,
114364ff247aSIvan T. Ivanov 			       IRQF_TRIGGER_HIGH, pdev->name, controller);
114464ff247aSIvan T. Ivanov 	if (ret)
11450c331fd1SStephan Gerhold 		goto error_clk;
114664ff247aSIvan T. Ivanov 
114764ff247aSIvan T. Ivanov 	pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
114864ff247aSIvan T. Ivanov 	pm_runtime_use_autosuspend(dev);
114964ff247aSIvan T. Ivanov 	pm_runtime_set_active(dev);
115064ff247aSIvan T. Ivanov 	pm_runtime_enable(dev);
1151045c243aSAndy Gross 
1152*597442ffSYang Yingliang 	ret = devm_spi_register_controller(dev, host);
1153045c243aSAndy Gross 	if (ret)
1154045c243aSAndy Gross 		goto disable_pm;
1155045c243aSAndy Gross 
115664ff247aSIvan T. Ivanov 	return 0;
115764ff247aSIvan T. Ivanov 
1158045c243aSAndy Gross disable_pm:
1159045c243aSAndy Gross 	pm_runtime_disable(&pdev->dev);
11600c331fd1SStephan Gerhold error_clk:
11610c331fd1SStephan Gerhold 	clk_disable_unprepare(cclk);
11620c331fd1SStephan Gerhold 	clk_disable_unprepare(iclk);
1163612762e8SAndy Gross error_dma:
1164*597442ffSYang Yingliang 	spi_qup_release_dma(host);
116564ff247aSIvan T. Ivanov error:
1166*597442ffSYang Yingliang 	spi_controller_put(host);
116764ff247aSIvan T. Ivanov 	return ret;
116864ff247aSIvan T. Ivanov }
116964ff247aSIvan T. Ivanov 
1170ec833050SRafael J. Wysocki #ifdef CONFIG_PM
spi_qup_pm_suspend_runtime(struct device * device)117164ff247aSIvan T. Ivanov static int spi_qup_pm_suspend_runtime(struct device *device)
117264ff247aSIvan T. Ivanov {
1173*597442ffSYang Yingliang 	struct spi_controller *host = dev_get_drvdata(device);
1174*597442ffSYang Yingliang 	struct spi_qup *controller = spi_controller_get_devdata(host);
117564ff247aSIvan T. Ivanov 	u32 config;
117664ff247aSIvan T. Ivanov 
117764ff247aSIvan T. Ivanov 	/* Enable clocks auto gaiting */
117864ff247aSIvan T. Ivanov 	config = readl(controller->base + QUP_CONFIG);
1179f0ceb114SAxel Lin 	config |= QUP_CONFIG_CLOCK_AUTO_GATE;
118064ff247aSIvan T. Ivanov 	writel_relaxed(config, controller->base + QUP_CONFIG);
1181dae1a770SPramod Gurav 
1182dae1a770SPramod Gurav 	clk_disable_unprepare(controller->cclk);
1183dae1a770SPramod Gurav 	clk_disable_unprepare(controller->iclk);
1184dae1a770SPramod Gurav 
118564ff247aSIvan T. Ivanov 	return 0;
118664ff247aSIvan T. Ivanov }
118764ff247aSIvan T. Ivanov 
spi_qup_pm_resume_runtime(struct device * device)118864ff247aSIvan T. Ivanov static int spi_qup_pm_resume_runtime(struct device *device)
118964ff247aSIvan T. Ivanov {
1190*597442ffSYang Yingliang 	struct spi_controller *host = dev_get_drvdata(device);
1191*597442ffSYang Yingliang 	struct spi_qup *controller = spi_controller_get_devdata(host);
119264ff247aSIvan T. Ivanov 	u32 config;
1193dae1a770SPramod Gurav 	int ret;
1194dae1a770SPramod Gurav 
1195dae1a770SPramod Gurav 	ret = clk_prepare_enable(controller->iclk);
1196dae1a770SPramod Gurav 	if (ret)
1197dae1a770SPramod Gurav 		return ret;
1198dae1a770SPramod Gurav 
1199dae1a770SPramod Gurav 	ret = clk_prepare_enable(controller->cclk);
1200494a2276SXu Qiang 	if (ret) {
1201494a2276SXu Qiang 		clk_disable_unprepare(controller->iclk);
1202dae1a770SPramod Gurav 		return ret;
1203494a2276SXu Qiang 	}
120464ff247aSIvan T. Ivanov 
120564ff247aSIvan T. Ivanov 	/* Disable clocks auto gaiting */
120664ff247aSIvan T. Ivanov 	config = readl_relaxed(controller->base + QUP_CONFIG);
1207f0ceb114SAxel Lin 	config &= ~QUP_CONFIG_CLOCK_AUTO_GATE;
120864ff247aSIvan T. Ivanov 	writel_relaxed(config, controller->base + QUP_CONFIG);
120964ff247aSIvan T. Ivanov 	return 0;
121064ff247aSIvan T. Ivanov }
1211ec833050SRafael J. Wysocki #endif /* CONFIG_PM */
121264ff247aSIvan T. Ivanov 
121364ff247aSIvan T. Ivanov #ifdef CONFIG_PM_SLEEP
spi_qup_suspend(struct device * device)121464ff247aSIvan T. Ivanov static int spi_qup_suspend(struct device *device)
121564ff247aSIvan T. Ivanov {
1216*597442ffSYang Yingliang 	struct spi_controller *host = dev_get_drvdata(device);
1217*597442ffSYang Yingliang 	struct spi_qup *controller = spi_controller_get_devdata(host);
121864ff247aSIvan T. Ivanov 	int ret;
121964ff247aSIvan T. Ivanov 
1220136b5cd2SYuji Sasaki 	if (pm_runtime_suspended(device)) {
1221136b5cd2SYuji Sasaki 		ret = spi_qup_pm_resume_runtime(device);
1222136b5cd2SYuji Sasaki 		if (ret)
1223136b5cd2SYuji Sasaki 			return ret;
1224136b5cd2SYuji Sasaki 	}
1225*597442ffSYang Yingliang 	ret = spi_controller_suspend(host);
122664ff247aSIvan T. Ivanov 	if (ret)
122764ff247aSIvan T. Ivanov 		return ret;
122864ff247aSIvan T. Ivanov 
122964ff247aSIvan T. Ivanov 	ret = spi_qup_set_state(controller, QUP_STATE_RESET);
123064ff247aSIvan T. Ivanov 	if (ret)
123164ff247aSIvan T. Ivanov 		return ret;
123264ff247aSIvan T. Ivanov 
123364ff247aSIvan T. Ivanov 	clk_disable_unprepare(controller->cclk);
123464ff247aSIvan T. Ivanov 	clk_disable_unprepare(controller->iclk);
123564ff247aSIvan T. Ivanov 	return 0;
123664ff247aSIvan T. Ivanov }
123764ff247aSIvan T. Ivanov 
spi_qup_resume(struct device * device)123864ff247aSIvan T. Ivanov static int spi_qup_resume(struct device *device)
123964ff247aSIvan T. Ivanov {
1240*597442ffSYang Yingliang 	struct spi_controller *host = dev_get_drvdata(device);
1241*597442ffSYang Yingliang 	struct spi_qup *controller = spi_controller_get_devdata(host);
124264ff247aSIvan T. Ivanov 	int ret;
124364ff247aSIvan T. Ivanov 
124464ff247aSIvan T. Ivanov 	ret = clk_prepare_enable(controller->iclk);
124564ff247aSIvan T. Ivanov 	if (ret)
124664ff247aSIvan T. Ivanov 		return ret;
124764ff247aSIvan T. Ivanov 
124864ff247aSIvan T. Ivanov 	ret = clk_prepare_enable(controller->cclk);
124970034320SXu Qiang 	if (ret) {
125070034320SXu Qiang 		clk_disable_unprepare(controller->iclk);
125164ff247aSIvan T. Ivanov 		return ret;
125270034320SXu Qiang 	}
125364ff247aSIvan T. Ivanov 
125464ff247aSIvan T. Ivanov 	ret = spi_qup_set_state(controller, QUP_STATE_RESET);
125564ff247aSIvan T. Ivanov 	if (ret)
125670034320SXu Qiang 		goto disable_clk;
125764ff247aSIvan T. Ivanov 
1258*597442ffSYang Yingliang 	ret = spi_controller_resume(host);
125970034320SXu Qiang 	if (ret)
126070034320SXu Qiang 		goto disable_clk;
126170034320SXu Qiang 
126270034320SXu Qiang 	return 0;
126370034320SXu Qiang 
126470034320SXu Qiang disable_clk:
126570034320SXu Qiang 	clk_disable_unprepare(controller->cclk);
126670034320SXu Qiang 	clk_disable_unprepare(controller->iclk);
126770034320SXu Qiang 	return ret;
126864ff247aSIvan T. Ivanov }
126964ff247aSIvan T. Ivanov #endif /* CONFIG_PM_SLEEP */
127064ff247aSIvan T. Ivanov 
spi_qup_remove(struct platform_device * pdev)1271dea8e70fSUwe Kleine-König static void spi_qup_remove(struct platform_device *pdev)
127264ff247aSIvan T. Ivanov {
1273*597442ffSYang Yingliang 	struct spi_controller *host = dev_get_drvdata(&pdev->dev);
1274*597442ffSYang Yingliang 	struct spi_qup *controller = spi_controller_get_devdata(host);
127564ff247aSIvan T. Ivanov 	int ret;
127664ff247aSIvan T. Ivanov 
127761f49171SUwe Kleine-König 	ret = pm_runtime_get_sync(&pdev->dev);
127864ff247aSIvan T. Ivanov 
127961f49171SUwe Kleine-König 	if (ret >= 0) {
128064ff247aSIvan T. Ivanov 		ret = spi_qup_set_state(controller, QUP_STATE_RESET);
128164ff247aSIvan T. Ivanov 		if (ret)
128261f49171SUwe Kleine-König 			dev_warn(&pdev->dev, "failed to reset controller (%pe)\n",
128361f49171SUwe Kleine-König 				 ERR_PTR(ret));
1284612762e8SAndy Gross 
128564ff247aSIvan T. Ivanov 		clk_disable_unprepare(controller->cclk);
128664ff247aSIvan T. Ivanov 		clk_disable_unprepare(controller->iclk);
128761f49171SUwe Kleine-König 	} else {
128861f49171SUwe Kleine-König 		dev_warn(&pdev->dev, "failed to resume, skip hw disable (%pe)\n",
128961f49171SUwe Kleine-König 			 ERR_PTR(ret));
129061f49171SUwe Kleine-König 	}
129161f49171SUwe Kleine-König 
1292*597442ffSYang Yingliang 	spi_qup_release_dma(host);
129364ff247aSIvan T. Ivanov 
129464ff247aSIvan T. Ivanov 	pm_runtime_put_noidle(&pdev->dev);
129564ff247aSIvan T. Ivanov 	pm_runtime_disable(&pdev->dev);
129664ff247aSIvan T. Ivanov }
129764ff247aSIvan T. Ivanov 
1298113b1a07SJingoo Han static const struct of_device_id spi_qup_dt_match[] = {
12994d023737SVaradarajan Narayanan 	{ .compatible = "qcom,spi-qup-v1.1.1", .data = (void *)1, },
130064ff247aSIvan T. Ivanov 	{ .compatible = "qcom,spi-qup-v2.1.1", },
130164ff247aSIvan T. Ivanov 	{ .compatible = "qcom,spi-qup-v2.2.1", },
130264ff247aSIvan T. Ivanov 	{ }
130364ff247aSIvan T. Ivanov };
130464ff247aSIvan T. Ivanov MODULE_DEVICE_TABLE(of, spi_qup_dt_match);
130564ff247aSIvan T. Ivanov 
130664ff247aSIvan T. Ivanov static const struct dev_pm_ops spi_qup_dev_pm_ops = {
130764ff247aSIvan T. Ivanov 	SET_SYSTEM_SLEEP_PM_OPS(spi_qup_suspend, spi_qup_resume)
130864ff247aSIvan T. Ivanov 	SET_RUNTIME_PM_OPS(spi_qup_pm_suspend_runtime,
130964ff247aSIvan T. Ivanov 			   spi_qup_pm_resume_runtime,
131064ff247aSIvan T. Ivanov 			   NULL)
131164ff247aSIvan T. Ivanov };
131264ff247aSIvan T. Ivanov 
131364ff247aSIvan T. Ivanov static struct platform_driver spi_qup_driver = {
131464ff247aSIvan T. Ivanov 	.driver = {
131564ff247aSIvan T. Ivanov 		.name		= "spi_qup",
131664ff247aSIvan T. Ivanov 		.pm		= &spi_qup_dev_pm_ops,
131764ff247aSIvan T. Ivanov 		.of_match_table = spi_qup_dt_match,
131864ff247aSIvan T. Ivanov 	},
131964ff247aSIvan T. Ivanov 	.probe = spi_qup_probe,
1320dea8e70fSUwe Kleine-König 	.remove_new = spi_qup_remove,
132164ff247aSIvan T. Ivanov };
132264ff247aSIvan T. Ivanov module_platform_driver(spi_qup_driver);
132364ff247aSIvan T. Ivanov 
132464ff247aSIvan T. Ivanov MODULE_LICENSE("GPL v2");
132564ff247aSIvan T. Ivanov MODULE_ALIAS("platform:spi_qup");
1326