162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Copyright (c) 2009 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci//      Jaswinder Singh <jassi.brar@samsung.com>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/init.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/interrupt.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1262306a36Sopenharmony_ci#include <linux/dmaengine.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1562306a36Sopenharmony_ci#include <linux/spi/spi.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/platform_data/spi-s3c64xx.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define MAX_SPI_PORTS		12
2162306a36Sopenharmony_ci#define S3C64XX_SPI_QUIRK_CS_AUTO	(1 << 1)
2262306a36Sopenharmony_ci#define AUTOSUSPEND_TIMEOUT	2000
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Registers and bit-fields */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define S3C64XX_SPI_CH_CFG		0x00
2762306a36Sopenharmony_ci#define S3C64XX_SPI_CLK_CFG		0x04
2862306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CFG		0x08
2962306a36Sopenharmony_ci#define S3C64XX_SPI_CS_REG		0x0C
3062306a36Sopenharmony_ci#define S3C64XX_SPI_INT_EN		0x10
3162306a36Sopenharmony_ci#define S3C64XX_SPI_STATUS		0x14
3262306a36Sopenharmony_ci#define S3C64XX_SPI_TX_DATA		0x18
3362306a36Sopenharmony_ci#define S3C64XX_SPI_RX_DATA		0x1C
3462306a36Sopenharmony_ci#define S3C64XX_SPI_PACKET_CNT		0x20
3562306a36Sopenharmony_ci#define S3C64XX_SPI_PENDING_CLR		0x24
3662306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_CFG		0x28
3762306a36Sopenharmony_ci#define S3C64XX_SPI_FB_CLK		0x2C
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#define S3C64XX_SPI_CH_HS_EN		(1<<6)	/* High Speed Enable */
4062306a36Sopenharmony_ci#define S3C64XX_SPI_CH_SW_RST		(1<<5)
4162306a36Sopenharmony_ci#define S3C64XX_SPI_CH_SLAVE		(1<<4)
4262306a36Sopenharmony_ci#define S3C64XX_SPI_CPOL_L		(1<<3)
4362306a36Sopenharmony_ci#define S3C64XX_SPI_CPHA_B		(1<<2)
4462306a36Sopenharmony_ci#define S3C64XX_SPI_CH_RXCH_ON		(1<<1)
4562306a36Sopenharmony_ci#define S3C64XX_SPI_CH_TXCH_ON		(1<<0)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define S3C64XX_SPI_CLKSEL_SRCMSK	(3<<9)
4862306a36Sopenharmony_ci#define S3C64XX_SPI_CLKSEL_SRCSHFT	9
4962306a36Sopenharmony_ci#define S3C64XX_SPI_ENCLK_ENABLE	(1<<8)
5062306a36Sopenharmony_ci#define S3C64XX_SPI_PSR_MASK		0xff
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_BYTE		(0<<29)
5362306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_HALFWORD	(1<<29)
5462306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_WORD		(2<<29)
5562306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_CH_TSZ_MASK		(3<<29)
5662306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_BYTE		(0<<17)
5762306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD	(1<<17)
5862306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_WORD		(2<<17)
5962306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_BUS_TSZ_MASK		(3<<17)
6062306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_RX_RDY_LVL		GENMASK(16, 11)
6162306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_RX_RDY_LVL_SHIFT	11
6262306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_SELF_LOOPBACK		(1<<3)
6362306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_RXDMA_ON		(1<<2)
6462306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_TXDMA_ON		(1<<1)
6562306a36Sopenharmony_ci#define S3C64XX_SPI_MODE_4BURST			(1<<0)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define S3C64XX_SPI_CS_NSC_CNT_2		(2<<4)
6862306a36Sopenharmony_ci#define S3C64XX_SPI_CS_AUTO			(1<<1)
6962306a36Sopenharmony_ci#define S3C64XX_SPI_CS_SIG_INACT		(1<<0)
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci#define S3C64XX_SPI_INT_TRAILING_EN		(1<<6)
7262306a36Sopenharmony_ci#define S3C64XX_SPI_INT_RX_OVERRUN_EN		(1<<5)
7362306a36Sopenharmony_ci#define S3C64XX_SPI_INT_RX_UNDERRUN_EN		(1<<4)
7462306a36Sopenharmony_ci#define S3C64XX_SPI_INT_TX_OVERRUN_EN		(1<<3)
7562306a36Sopenharmony_ci#define S3C64XX_SPI_INT_TX_UNDERRUN_EN		(1<<2)
7662306a36Sopenharmony_ci#define S3C64XX_SPI_INT_RX_FIFORDY_EN		(1<<1)
7762306a36Sopenharmony_ci#define S3C64XX_SPI_INT_TX_FIFORDY_EN		(1<<0)
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define S3C64XX_SPI_ST_RX_OVERRUN_ERR		(1<<5)
8062306a36Sopenharmony_ci#define S3C64XX_SPI_ST_RX_UNDERRUN_ERR		(1<<4)
8162306a36Sopenharmony_ci#define S3C64XX_SPI_ST_TX_OVERRUN_ERR		(1<<3)
8262306a36Sopenharmony_ci#define S3C64XX_SPI_ST_TX_UNDERRUN_ERR		(1<<2)
8362306a36Sopenharmony_ci#define S3C64XX_SPI_ST_RX_FIFORDY		(1<<1)
8462306a36Sopenharmony_ci#define S3C64XX_SPI_ST_TX_FIFORDY		(1<<0)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define S3C64XX_SPI_PACKET_CNT_EN		(1<<16)
8762306a36Sopenharmony_ci#define S3C64XX_SPI_PACKET_CNT_MASK		GENMASK(15, 0)
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define S3C64XX_SPI_PND_TX_UNDERRUN_CLR		(1<<4)
9062306a36Sopenharmony_ci#define S3C64XX_SPI_PND_TX_OVERRUN_CLR		(1<<3)
9162306a36Sopenharmony_ci#define S3C64XX_SPI_PND_RX_UNDERRUN_CLR		(1<<2)
9262306a36Sopenharmony_ci#define S3C64XX_SPI_PND_RX_OVERRUN_CLR		(1<<1)
9362306a36Sopenharmony_ci#define S3C64XX_SPI_PND_TRAILING_CLR		(1<<0)
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_HALF_WORD		(1<<7)
9662306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_BYTE		(1<<6)
9762306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_BIT			(1<<5)
9862306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_RX_EN			(1<<4)
9962306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_HALF_WORD		(1<<3)
10062306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_BYTE		(1<<2)
10162306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_BIT			(1<<1)
10262306a36Sopenharmony_ci#define S3C64XX_SPI_SWAP_TX_EN			(1<<0)
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define S3C64XX_SPI_FBCLK_MSK			(3<<0)
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define FIFO_LVL_MASK(i) ((i)->port_conf->fifo_lvl_mask[i->port_id])
10762306a36Sopenharmony_ci#define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & \
10862306a36Sopenharmony_ci				(1 << (i)->port_conf->tx_st_done)) ? 1 : 0)
10962306a36Sopenharmony_ci#define TX_FIFO_LVL(v, i) (((v) >> 6) & FIFO_LVL_MASK(i))
11062306a36Sopenharmony_ci#define RX_FIFO_LVL(v, i) (((v) >> (i)->port_conf->rx_lvl_offset) & \
11162306a36Sopenharmony_ci					FIFO_LVL_MASK(i))
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci#define S3C64XX_SPI_MAX_TRAILCNT	0x3ff
11462306a36Sopenharmony_ci#define S3C64XX_SPI_TRAILCNT_OFF	19
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define S3C64XX_SPI_TRAILCNT		S3C64XX_SPI_MAX_TRAILCNT
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#define S3C64XX_SPI_POLLING_SIZE	32
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
12162306a36Sopenharmony_ci#define is_polling(x)	(x->cntrlr_info->polling)
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define RXBUSY    (1<<2)
12462306a36Sopenharmony_ci#define TXBUSY    (1<<3)
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistruct s3c64xx_spi_dma_data {
12762306a36Sopenharmony_ci	struct dma_chan *ch;
12862306a36Sopenharmony_ci	dma_cookie_t cookie;
12962306a36Sopenharmony_ci	enum dma_transfer_direction direction;
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci/**
13362306a36Sopenharmony_ci * struct s3c64xx_spi_port_config - SPI Controller hardware info
13462306a36Sopenharmony_ci * @fifo_lvl_mask: Bit-mask for {TX|RX}_FIFO_LVL bits in SPI_STATUS register.
13562306a36Sopenharmony_ci * @rx_lvl_offset: Bit offset of RX_FIFO_LVL bits in SPI_STATUS regiter.
13662306a36Sopenharmony_ci * @tx_st_done: Bit offset of TX_DONE bit in SPI_STATUS regiter.
13762306a36Sopenharmony_ci * @clk_div: Internal clock divider
13862306a36Sopenharmony_ci * @quirks: Bitmask of known quirks
13962306a36Sopenharmony_ci * @high_speed: True, if the controller supports HIGH_SPEED_EN bit.
14062306a36Sopenharmony_ci * @clk_from_cmu: True, if the controller does not include a clock mux and
14162306a36Sopenharmony_ci *	prescaler unit.
14262306a36Sopenharmony_ci * @clk_ioclk: True if clock is present on this device
14362306a36Sopenharmony_ci * @has_loopback: True if loopback mode can be supported
14462306a36Sopenharmony_ci *
14562306a36Sopenharmony_ci * The Samsung s3c64xx SPI controller are used on various Samsung SoC's but
14662306a36Sopenharmony_ci * differ in some aspects such as the size of the fifo and spi bus clock
14762306a36Sopenharmony_ci * setup. Such differences are specified to the driver using this structure
14862306a36Sopenharmony_ci * which is provided as driver data to the driver.
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_cistruct s3c64xx_spi_port_config {
15162306a36Sopenharmony_ci	int	fifo_lvl_mask[MAX_SPI_PORTS];
15262306a36Sopenharmony_ci	int	rx_lvl_offset;
15362306a36Sopenharmony_ci	int	tx_st_done;
15462306a36Sopenharmony_ci	int	quirks;
15562306a36Sopenharmony_ci	int	clk_div;
15662306a36Sopenharmony_ci	bool	high_speed;
15762306a36Sopenharmony_ci	bool	clk_from_cmu;
15862306a36Sopenharmony_ci	bool	clk_ioclk;
15962306a36Sopenharmony_ci	bool	has_loopback;
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/**
16362306a36Sopenharmony_ci * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver.
16462306a36Sopenharmony_ci * @clk: Pointer to the spi clock.
16562306a36Sopenharmony_ci * @src_clk: Pointer to the clock used to generate SPI signals.
16662306a36Sopenharmony_ci * @ioclk: Pointer to the i/o clock between host and target
16762306a36Sopenharmony_ci * @pdev: Pointer to device's platform device data
16862306a36Sopenharmony_ci * @host: Pointer to the SPI Protocol host.
16962306a36Sopenharmony_ci * @cntrlr_info: Platform specific data for the controller this driver manages.
17062306a36Sopenharmony_ci * @lock: Controller specific lock.
17162306a36Sopenharmony_ci * @state: Set of FLAGS to indicate status.
17262306a36Sopenharmony_ci * @sfr_start: BUS address of SPI controller regs.
17362306a36Sopenharmony_ci * @regs: Pointer to ioremap'ed controller registers.
17462306a36Sopenharmony_ci * @xfer_completion: To indicate completion of xfer task.
17562306a36Sopenharmony_ci * @cur_mode: Stores the active configuration of the controller.
17662306a36Sopenharmony_ci * @cur_bpw: Stores the active bits per word settings.
17762306a36Sopenharmony_ci * @cur_speed: Current clock speed
17862306a36Sopenharmony_ci * @rx_dma: Local receive DMA data (e.g. chan and direction)
17962306a36Sopenharmony_ci * @tx_dma: Local transmit DMA data (e.g. chan and direction)
18062306a36Sopenharmony_ci * @port_conf: Local SPI port configuartion data
18162306a36Sopenharmony_ci * @port_id: Port identification number
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_cistruct s3c64xx_spi_driver_data {
18462306a36Sopenharmony_ci	void __iomem                    *regs;
18562306a36Sopenharmony_ci	struct clk                      *clk;
18662306a36Sopenharmony_ci	struct clk                      *src_clk;
18762306a36Sopenharmony_ci	struct clk                      *ioclk;
18862306a36Sopenharmony_ci	struct platform_device          *pdev;
18962306a36Sopenharmony_ci	struct spi_controller           *host;
19062306a36Sopenharmony_ci	struct s3c64xx_spi_info         *cntrlr_info;
19162306a36Sopenharmony_ci	spinlock_t                      lock;
19262306a36Sopenharmony_ci	unsigned long                   sfr_start;
19362306a36Sopenharmony_ci	struct completion               xfer_completion;
19462306a36Sopenharmony_ci	unsigned                        state;
19562306a36Sopenharmony_ci	unsigned                        cur_mode, cur_bpw;
19662306a36Sopenharmony_ci	unsigned                        cur_speed;
19762306a36Sopenharmony_ci	struct s3c64xx_spi_dma_data	rx_dma;
19862306a36Sopenharmony_ci	struct s3c64xx_spi_dma_data	tx_dma;
19962306a36Sopenharmony_ci	const struct s3c64xx_spi_port_config	*port_conf;
20062306a36Sopenharmony_ci	unsigned int			port_id;
20162306a36Sopenharmony_ci};
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void s3c64xx_flush_fifo(struct s3c64xx_spi_driver_data *sdd)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	void __iomem *regs = sdd->regs;
20662306a36Sopenharmony_ci	unsigned long loops;
20762306a36Sopenharmony_ci	u32 val;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	writel(0, regs + S3C64XX_SPI_PACKET_CNT);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	val = readl(regs + S3C64XX_SPI_CH_CFG);
21262306a36Sopenharmony_ci	val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON);
21362306a36Sopenharmony_ci	writel(val, regs + S3C64XX_SPI_CH_CFG);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	val = readl(regs + S3C64XX_SPI_CH_CFG);
21662306a36Sopenharmony_ci	val |= S3C64XX_SPI_CH_SW_RST;
21762306a36Sopenharmony_ci	val &= ~S3C64XX_SPI_CH_HS_EN;
21862306a36Sopenharmony_ci	writel(val, regs + S3C64XX_SPI_CH_CFG);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Flush TxFIFO*/
22162306a36Sopenharmony_ci	loops = msecs_to_loops(1);
22262306a36Sopenharmony_ci	do {
22362306a36Sopenharmony_ci		val = readl(regs + S3C64XX_SPI_STATUS);
22462306a36Sopenharmony_ci	} while (TX_FIFO_LVL(val, sdd) && loops--);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (loops == 0)
22762306a36Sopenharmony_ci		dev_warn(&sdd->pdev->dev, "Timed out flushing TX FIFO\n");
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	/* Flush RxFIFO*/
23062306a36Sopenharmony_ci	loops = msecs_to_loops(1);
23162306a36Sopenharmony_ci	do {
23262306a36Sopenharmony_ci		val = readl(regs + S3C64XX_SPI_STATUS);
23362306a36Sopenharmony_ci		if (RX_FIFO_LVL(val, sdd))
23462306a36Sopenharmony_ci			readl(regs + S3C64XX_SPI_RX_DATA);
23562306a36Sopenharmony_ci		else
23662306a36Sopenharmony_ci			break;
23762306a36Sopenharmony_ci	} while (loops--);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (loops == 0)
24062306a36Sopenharmony_ci		dev_warn(&sdd->pdev->dev, "Timed out flushing RX FIFO\n");
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	val = readl(regs + S3C64XX_SPI_CH_CFG);
24362306a36Sopenharmony_ci	val &= ~S3C64XX_SPI_CH_SW_RST;
24462306a36Sopenharmony_ci	writel(val, regs + S3C64XX_SPI_CH_CFG);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	val = readl(regs + S3C64XX_SPI_MODE_CFG);
24762306a36Sopenharmony_ci	val &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON);
24862306a36Sopenharmony_ci	writel(val, regs + S3C64XX_SPI_MODE_CFG);
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void s3c64xx_spi_dmacb(void *data)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd;
25462306a36Sopenharmony_ci	struct s3c64xx_spi_dma_data *dma = data;
25562306a36Sopenharmony_ci	unsigned long flags;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	if (dma->direction == DMA_DEV_TO_MEM)
25862306a36Sopenharmony_ci		sdd = container_of(data,
25962306a36Sopenharmony_ci			struct s3c64xx_spi_driver_data, rx_dma);
26062306a36Sopenharmony_ci	else
26162306a36Sopenharmony_ci		sdd = container_of(data,
26262306a36Sopenharmony_ci			struct s3c64xx_spi_driver_data, tx_dma);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	spin_lock_irqsave(&sdd->lock, flags);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	if (dma->direction == DMA_DEV_TO_MEM) {
26762306a36Sopenharmony_ci		sdd->state &= ~RXBUSY;
26862306a36Sopenharmony_ci		if (!(sdd->state & TXBUSY))
26962306a36Sopenharmony_ci			complete(&sdd->xfer_completion);
27062306a36Sopenharmony_ci	} else {
27162306a36Sopenharmony_ci		sdd->state &= ~TXBUSY;
27262306a36Sopenharmony_ci		if (!(sdd->state & RXBUSY))
27362306a36Sopenharmony_ci			complete(&sdd->xfer_completion);
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	spin_unlock_irqrestore(&sdd->lock, flags);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int prepare_dma(struct s3c64xx_spi_dma_data *dma,
28062306a36Sopenharmony_ci			struct sg_table *sgt)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd;
28362306a36Sopenharmony_ci	struct dma_slave_config config;
28462306a36Sopenharmony_ci	struct dma_async_tx_descriptor *desc;
28562306a36Sopenharmony_ci	int ret;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	memset(&config, 0, sizeof(config));
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (dma->direction == DMA_DEV_TO_MEM) {
29062306a36Sopenharmony_ci		sdd = container_of((void *)dma,
29162306a36Sopenharmony_ci			struct s3c64xx_spi_driver_data, rx_dma);
29262306a36Sopenharmony_ci		config.direction = dma->direction;
29362306a36Sopenharmony_ci		config.src_addr = sdd->sfr_start + S3C64XX_SPI_RX_DATA;
29462306a36Sopenharmony_ci		config.src_addr_width = sdd->cur_bpw / 8;
29562306a36Sopenharmony_ci		config.src_maxburst = 1;
29662306a36Sopenharmony_ci		dmaengine_slave_config(dma->ch, &config);
29762306a36Sopenharmony_ci	} else {
29862306a36Sopenharmony_ci		sdd = container_of((void *)dma,
29962306a36Sopenharmony_ci			struct s3c64xx_spi_driver_data, tx_dma);
30062306a36Sopenharmony_ci		config.direction = dma->direction;
30162306a36Sopenharmony_ci		config.dst_addr = sdd->sfr_start + S3C64XX_SPI_TX_DATA;
30262306a36Sopenharmony_ci		config.dst_addr_width = sdd->cur_bpw / 8;
30362306a36Sopenharmony_ci		config.dst_maxburst = 1;
30462306a36Sopenharmony_ci		dmaengine_slave_config(dma->ch, &config);
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	desc = dmaengine_prep_slave_sg(dma->ch, sgt->sgl, sgt->nents,
30862306a36Sopenharmony_ci				       dma->direction, DMA_PREP_INTERRUPT);
30962306a36Sopenharmony_ci	if (!desc) {
31062306a36Sopenharmony_ci		dev_err(&sdd->pdev->dev, "unable to prepare %s scatterlist",
31162306a36Sopenharmony_ci			dma->direction == DMA_DEV_TO_MEM ? "rx" : "tx");
31262306a36Sopenharmony_ci		return -ENOMEM;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	desc->callback = s3c64xx_spi_dmacb;
31662306a36Sopenharmony_ci	desc->callback_param = dma;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	dma->cookie = dmaengine_submit(desc);
31962306a36Sopenharmony_ci	ret = dma_submit_error(dma->cookie);
32062306a36Sopenharmony_ci	if (ret) {
32162306a36Sopenharmony_ci		dev_err(&sdd->pdev->dev, "DMA submission failed");
32262306a36Sopenharmony_ci		return -EIO;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	dma_async_issue_pending(dma->ch);
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd =
33262306a36Sopenharmony_ci					spi_controller_get_devdata(spi->controller);
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (sdd->cntrlr_info->no_cs)
33562306a36Sopenharmony_ci		return;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (enable) {
33862306a36Sopenharmony_ci		if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) {
33962306a36Sopenharmony_ci			writel(0, sdd->regs + S3C64XX_SPI_CS_REG);
34062306a36Sopenharmony_ci		} else {
34162306a36Sopenharmony_ci			u32 ssel = readl(sdd->regs + S3C64XX_SPI_CS_REG);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci			ssel |= (S3C64XX_SPI_CS_AUTO |
34462306a36Sopenharmony_ci						S3C64XX_SPI_CS_NSC_CNT_2);
34562306a36Sopenharmony_ci			writel(ssel, sdd->regs + S3C64XX_SPI_CS_REG);
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci	} else {
34862306a36Sopenharmony_ci		if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO))
34962306a36Sopenharmony_ci			writel(S3C64XX_SPI_CS_SIG_INACT,
35062306a36Sopenharmony_ci			       sdd->regs + S3C64XX_SPI_CS_REG);
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic int s3c64xx_spi_prepare_transfer(struct spi_controller *spi)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(spi);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (is_polling(sdd))
35962306a36Sopenharmony_ci		return 0;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* Requests DMA channels */
36262306a36Sopenharmony_ci	sdd->rx_dma.ch = dma_request_chan(&sdd->pdev->dev, "rx");
36362306a36Sopenharmony_ci	if (IS_ERR(sdd->rx_dma.ch)) {
36462306a36Sopenharmony_ci		dev_err(&sdd->pdev->dev, "Failed to get RX DMA channel\n");
36562306a36Sopenharmony_ci		sdd->rx_dma.ch = NULL;
36662306a36Sopenharmony_ci		return 0;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	sdd->tx_dma.ch = dma_request_chan(&sdd->pdev->dev, "tx");
37062306a36Sopenharmony_ci	if (IS_ERR(sdd->tx_dma.ch)) {
37162306a36Sopenharmony_ci		dev_err(&sdd->pdev->dev, "Failed to get TX DMA channel\n");
37262306a36Sopenharmony_ci		dma_release_channel(sdd->rx_dma.ch);
37362306a36Sopenharmony_ci		sdd->tx_dma.ch = NULL;
37462306a36Sopenharmony_ci		sdd->rx_dma.ch = NULL;
37562306a36Sopenharmony_ci		return 0;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	spi->dma_rx = sdd->rx_dma.ch;
37962306a36Sopenharmony_ci	spi->dma_tx = sdd->tx_dma.ch;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	return 0;
38262306a36Sopenharmony_ci}
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_cistatic int s3c64xx_spi_unprepare_transfer(struct spi_controller *spi)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(spi);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (is_polling(sdd))
38962306a36Sopenharmony_ci		return 0;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* Releases DMA channels if they are allocated */
39262306a36Sopenharmony_ci	if (sdd->rx_dma.ch && sdd->tx_dma.ch) {
39362306a36Sopenharmony_ci		dma_release_channel(sdd->rx_dma.ch);
39462306a36Sopenharmony_ci		dma_release_channel(sdd->tx_dma.ch);
39562306a36Sopenharmony_ci		sdd->rx_dma.ch = NULL;
39662306a36Sopenharmony_ci		sdd->tx_dma.ch = NULL;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic bool s3c64xx_spi_can_dma(struct spi_controller *host,
40362306a36Sopenharmony_ci				struct spi_device *spi,
40462306a36Sopenharmony_ci				struct spi_transfer *xfer)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (sdd->rx_dma.ch && sdd->tx_dma.ch) {
40962306a36Sopenharmony_ci		return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1;
41062306a36Sopenharmony_ci	} else {
41162306a36Sopenharmony_ci		return false;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_cistatic int s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd,
41762306a36Sopenharmony_ci				    struct spi_transfer *xfer, int dma_mode)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	void __iomem *regs = sdd->regs;
42062306a36Sopenharmony_ci	u32 modecfg, chcfg;
42162306a36Sopenharmony_ci	int ret = 0;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	modecfg = readl(regs + S3C64XX_SPI_MODE_CFG);
42462306a36Sopenharmony_ci	modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	chcfg = readl(regs + S3C64XX_SPI_CH_CFG);
42762306a36Sopenharmony_ci	chcfg &= ~S3C64XX_SPI_CH_TXCH_ON;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (dma_mode) {
43062306a36Sopenharmony_ci		chcfg &= ~S3C64XX_SPI_CH_RXCH_ON;
43162306a36Sopenharmony_ci	} else {
43262306a36Sopenharmony_ci		/* Always shift in data in FIFO, even if xfer is Tx only,
43362306a36Sopenharmony_ci		 * this helps setting PCKT_CNT value for generating clocks
43462306a36Sopenharmony_ci		 * as exactly needed.
43562306a36Sopenharmony_ci		 */
43662306a36Sopenharmony_ci		chcfg |= S3C64XX_SPI_CH_RXCH_ON;
43762306a36Sopenharmony_ci		writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
43862306a36Sopenharmony_ci					| S3C64XX_SPI_PACKET_CNT_EN,
43962306a36Sopenharmony_ci					regs + S3C64XX_SPI_PACKET_CNT);
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (xfer->tx_buf != NULL) {
44362306a36Sopenharmony_ci		sdd->state |= TXBUSY;
44462306a36Sopenharmony_ci		chcfg |= S3C64XX_SPI_CH_TXCH_ON;
44562306a36Sopenharmony_ci		if (dma_mode) {
44662306a36Sopenharmony_ci			modecfg |= S3C64XX_SPI_MODE_TXDMA_ON;
44762306a36Sopenharmony_ci			ret = prepare_dma(&sdd->tx_dma, &xfer->tx_sg);
44862306a36Sopenharmony_ci		} else {
44962306a36Sopenharmony_ci			switch (sdd->cur_bpw) {
45062306a36Sopenharmony_ci			case 32:
45162306a36Sopenharmony_ci				iowrite32_rep(regs + S3C64XX_SPI_TX_DATA,
45262306a36Sopenharmony_ci					xfer->tx_buf, xfer->len / 4);
45362306a36Sopenharmony_ci				break;
45462306a36Sopenharmony_ci			case 16:
45562306a36Sopenharmony_ci				iowrite16_rep(regs + S3C64XX_SPI_TX_DATA,
45662306a36Sopenharmony_ci					xfer->tx_buf, xfer->len / 2);
45762306a36Sopenharmony_ci				break;
45862306a36Sopenharmony_ci			default:
45962306a36Sopenharmony_ci				iowrite8_rep(regs + S3C64XX_SPI_TX_DATA,
46062306a36Sopenharmony_ci					xfer->tx_buf, xfer->len);
46162306a36Sopenharmony_ci				break;
46262306a36Sopenharmony_ci			}
46362306a36Sopenharmony_ci		}
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	if (xfer->rx_buf != NULL) {
46762306a36Sopenharmony_ci		sdd->state |= RXBUSY;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci		if (sdd->port_conf->high_speed && sdd->cur_speed >= 30000000UL
47062306a36Sopenharmony_ci					&& !(sdd->cur_mode & SPI_CPHA))
47162306a36Sopenharmony_ci			chcfg |= S3C64XX_SPI_CH_HS_EN;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		if (dma_mode) {
47462306a36Sopenharmony_ci			modecfg |= S3C64XX_SPI_MODE_RXDMA_ON;
47562306a36Sopenharmony_ci			chcfg |= S3C64XX_SPI_CH_RXCH_ON;
47662306a36Sopenharmony_ci			writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff)
47762306a36Sopenharmony_ci					| S3C64XX_SPI_PACKET_CNT_EN,
47862306a36Sopenharmony_ci					regs + S3C64XX_SPI_PACKET_CNT);
47962306a36Sopenharmony_ci			ret = prepare_dma(&sdd->rx_dma, &xfer->rx_sg);
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	if (ret)
48462306a36Sopenharmony_ci		return ret;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	writel(modecfg, regs + S3C64XX_SPI_MODE_CFG);
48762306a36Sopenharmony_ci	writel(chcfg, regs + S3C64XX_SPI_CH_CFG);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	return 0;
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd,
49362306a36Sopenharmony_ci					int timeout_ms)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	void __iomem *regs = sdd->regs;
49662306a36Sopenharmony_ci	unsigned long val = 1;
49762306a36Sopenharmony_ci	u32 status;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	/* max fifo depth available */
50062306a36Sopenharmony_ci	u32 max_fifo = (FIFO_LVL_MASK(sdd) >> 1) + 1;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (timeout_ms)
50362306a36Sopenharmony_ci		val = msecs_to_loops(timeout_ms);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	do {
50662306a36Sopenharmony_ci		status = readl(regs + S3C64XX_SPI_STATUS);
50762306a36Sopenharmony_ci	} while (RX_FIFO_LVL(status, sdd) < max_fifo && --val);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* return the actual received data length */
51062306a36Sopenharmony_ci	return RX_FIFO_LVL(status, sdd);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic int s3c64xx_wait_for_dma(struct s3c64xx_spi_driver_data *sdd,
51462306a36Sopenharmony_ci				struct spi_transfer *xfer)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	void __iomem *regs = sdd->regs;
51762306a36Sopenharmony_ci	unsigned long val;
51862306a36Sopenharmony_ci	u32 status;
51962306a36Sopenharmony_ci	int ms;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/* millisecs to xfer 'len' bytes @ 'cur_speed' */
52262306a36Sopenharmony_ci	ms = xfer->len * 8 * 1000 / sdd->cur_speed;
52362306a36Sopenharmony_ci	ms += 30;               /* some tolerance */
52462306a36Sopenharmony_ci	ms = max(ms, 100);      /* minimum timeout */
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	val = msecs_to_jiffies(ms) + 10;
52762306a36Sopenharmony_ci	val = wait_for_completion_timeout(&sdd->xfer_completion, val);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	/*
53062306a36Sopenharmony_ci	 * If the previous xfer was completed within timeout, then
53162306a36Sopenharmony_ci	 * proceed further else return -EIO.
53262306a36Sopenharmony_ci	 * DmaTx returns after simply writing data in the FIFO,
53362306a36Sopenharmony_ci	 * w/o waiting for real transmission on the bus to finish.
53462306a36Sopenharmony_ci	 * DmaRx returns only after Dma read data from FIFO which
53562306a36Sopenharmony_ci	 * needs bus transmission to finish, so we don't worry if
53662306a36Sopenharmony_ci	 * Xfer involved Rx(with or without Tx).
53762306a36Sopenharmony_ci	 */
53862306a36Sopenharmony_ci	if (val && !xfer->rx_buf) {
53962306a36Sopenharmony_ci		val = msecs_to_loops(10);
54062306a36Sopenharmony_ci		status = readl(regs + S3C64XX_SPI_STATUS);
54162306a36Sopenharmony_ci		while ((TX_FIFO_LVL(status, sdd)
54262306a36Sopenharmony_ci			|| !S3C64XX_SPI_ST_TX_DONE(status, sdd))
54362306a36Sopenharmony_ci		       && --val) {
54462306a36Sopenharmony_ci			cpu_relax();
54562306a36Sopenharmony_ci			status = readl(regs + S3C64XX_SPI_STATUS);
54662306a36Sopenharmony_ci		}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* If timed out while checking rx/tx status return error */
55162306a36Sopenharmony_ci	if (!val)
55262306a36Sopenharmony_ci		return -EIO;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return 0;
55562306a36Sopenharmony_ci}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
55862306a36Sopenharmony_ci				struct spi_transfer *xfer, bool use_irq)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	void __iomem *regs = sdd->regs;
56162306a36Sopenharmony_ci	unsigned long val;
56262306a36Sopenharmony_ci	u32 status;
56362306a36Sopenharmony_ci	int loops;
56462306a36Sopenharmony_ci	u32 cpy_len;
56562306a36Sopenharmony_ci	u8 *buf;
56662306a36Sopenharmony_ci	int ms;
56762306a36Sopenharmony_ci	unsigned long time_us;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* microsecs to xfer 'len' bytes @ 'cur_speed' */
57062306a36Sopenharmony_ci	time_us = (xfer->len * 8 * 1000 * 1000) / sdd->cur_speed;
57162306a36Sopenharmony_ci	ms = (time_us / 1000);
57262306a36Sopenharmony_ci	ms += 10; /* some tolerance */
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* sleep during signal transfer time */
57562306a36Sopenharmony_ci	status = readl(regs + S3C64XX_SPI_STATUS);
57662306a36Sopenharmony_ci	if (RX_FIFO_LVL(status, sdd) < xfer->len)
57762306a36Sopenharmony_ci		usleep_range(time_us / 2, time_us);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (use_irq) {
58062306a36Sopenharmony_ci		val = msecs_to_jiffies(ms);
58162306a36Sopenharmony_ci		if (!wait_for_completion_timeout(&sdd->xfer_completion, val))
58262306a36Sopenharmony_ci			return -EIO;
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	val = msecs_to_loops(ms);
58662306a36Sopenharmony_ci	do {
58762306a36Sopenharmony_ci		status = readl(regs + S3C64XX_SPI_STATUS);
58862306a36Sopenharmony_ci	} while (RX_FIFO_LVL(status, sdd) < xfer->len && --val);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (!val)
59162306a36Sopenharmony_ci		return -EIO;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	/* If it was only Tx */
59462306a36Sopenharmony_ci	if (!xfer->rx_buf) {
59562306a36Sopenharmony_ci		sdd->state &= ~TXBUSY;
59662306a36Sopenharmony_ci		return 0;
59762306a36Sopenharmony_ci	}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	/*
60062306a36Sopenharmony_ci	 * If the receive length is bigger than the controller fifo
60162306a36Sopenharmony_ci	 * size, calculate the loops and read the fifo as many times.
60262306a36Sopenharmony_ci	 * loops = length / max fifo size (calculated by using the
60362306a36Sopenharmony_ci	 * fifo mask).
60462306a36Sopenharmony_ci	 * For any size less than the fifo size the below code is
60562306a36Sopenharmony_ci	 * executed atleast once.
60662306a36Sopenharmony_ci	 */
60762306a36Sopenharmony_ci	loops = xfer->len / ((FIFO_LVL_MASK(sdd) >> 1) + 1);
60862306a36Sopenharmony_ci	buf = xfer->rx_buf;
60962306a36Sopenharmony_ci	do {
61062306a36Sopenharmony_ci		/* wait for data to be received in the fifo */
61162306a36Sopenharmony_ci		cpy_len = s3c64xx_spi_wait_for_timeout(sdd,
61262306a36Sopenharmony_ci						       (loops ? ms : 0));
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci		switch (sdd->cur_bpw) {
61562306a36Sopenharmony_ci		case 32:
61662306a36Sopenharmony_ci			ioread32_rep(regs + S3C64XX_SPI_RX_DATA,
61762306a36Sopenharmony_ci				     buf, cpy_len / 4);
61862306a36Sopenharmony_ci			break;
61962306a36Sopenharmony_ci		case 16:
62062306a36Sopenharmony_ci			ioread16_rep(regs + S3C64XX_SPI_RX_DATA,
62162306a36Sopenharmony_ci				     buf, cpy_len / 2);
62262306a36Sopenharmony_ci			break;
62362306a36Sopenharmony_ci		default:
62462306a36Sopenharmony_ci			ioread8_rep(regs + S3C64XX_SPI_RX_DATA,
62562306a36Sopenharmony_ci				    buf, cpy_len);
62662306a36Sopenharmony_ci			break;
62762306a36Sopenharmony_ci		}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		buf = buf + cpy_len;
63062306a36Sopenharmony_ci	} while (loops--);
63162306a36Sopenharmony_ci	sdd->state &= ~RXBUSY;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	return 0;
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_cistatic int s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	void __iomem *regs = sdd->regs;
63962306a36Sopenharmony_ci	int ret;
64062306a36Sopenharmony_ci	u32 val;
64162306a36Sopenharmony_ci	int div = sdd->port_conf->clk_div;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/* Disable Clock */
64462306a36Sopenharmony_ci	if (!sdd->port_conf->clk_from_cmu) {
64562306a36Sopenharmony_ci		val = readl(regs + S3C64XX_SPI_CLK_CFG);
64662306a36Sopenharmony_ci		val &= ~S3C64XX_SPI_ENCLK_ENABLE;
64762306a36Sopenharmony_ci		writel(val, regs + S3C64XX_SPI_CLK_CFG);
64862306a36Sopenharmony_ci	}
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci	/* Set Polarity and Phase */
65162306a36Sopenharmony_ci	val = readl(regs + S3C64XX_SPI_CH_CFG);
65262306a36Sopenharmony_ci	val &= ~(S3C64XX_SPI_CH_SLAVE |
65362306a36Sopenharmony_ci			S3C64XX_SPI_CPOL_L |
65462306a36Sopenharmony_ci			S3C64XX_SPI_CPHA_B);
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	if (sdd->cur_mode & SPI_CPOL)
65762306a36Sopenharmony_ci		val |= S3C64XX_SPI_CPOL_L;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	if (sdd->cur_mode & SPI_CPHA)
66062306a36Sopenharmony_ci		val |= S3C64XX_SPI_CPHA_B;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	writel(val, regs + S3C64XX_SPI_CH_CFG);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	/* Set Channel & DMA Mode */
66562306a36Sopenharmony_ci	val = readl(regs + S3C64XX_SPI_MODE_CFG);
66662306a36Sopenharmony_ci	val &= ~(S3C64XX_SPI_MODE_BUS_TSZ_MASK
66762306a36Sopenharmony_ci			| S3C64XX_SPI_MODE_CH_TSZ_MASK);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	switch (sdd->cur_bpw) {
67062306a36Sopenharmony_ci	case 32:
67162306a36Sopenharmony_ci		val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD;
67262306a36Sopenharmony_ci		val |= S3C64XX_SPI_MODE_CH_TSZ_WORD;
67362306a36Sopenharmony_ci		break;
67462306a36Sopenharmony_ci	case 16:
67562306a36Sopenharmony_ci		val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD;
67662306a36Sopenharmony_ci		val |= S3C64XX_SPI_MODE_CH_TSZ_HALFWORD;
67762306a36Sopenharmony_ci		break;
67862306a36Sopenharmony_ci	default:
67962306a36Sopenharmony_ci		val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE;
68062306a36Sopenharmony_ci		val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE;
68162306a36Sopenharmony_ci		break;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if ((sdd->cur_mode & SPI_LOOP) && sdd->port_conf->has_loopback)
68562306a36Sopenharmony_ci		val |= S3C64XX_SPI_MODE_SELF_LOOPBACK;
68662306a36Sopenharmony_ci	else
68762306a36Sopenharmony_ci		val &= ~S3C64XX_SPI_MODE_SELF_LOOPBACK;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	writel(val, regs + S3C64XX_SPI_MODE_CFG);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (sdd->port_conf->clk_from_cmu) {
69262306a36Sopenharmony_ci		ret = clk_set_rate(sdd->src_clk, sdd->cur_speed * div);
69362306a36Sopenharmony_ci		if (ret)
69462306a36Sopenharmony_ci			return ret;
69562306a36Sopenharmony_ci		sdd->cur_speed = clk_get_rate(sdd->src_clk) / div;
69662306a36Sopenharmony_ci	} else {
69762306a36Sopenharmony_ci		/* Configure Clock */
69862306a36Sopenharmony_ci		val = readl(regs + S3C64XX_SPI_CLK_CFG);
69962306a36Sopenharmony_ci		val &= ~S3C64XX_SPI_PSR_MASK;
70062306a36Sopenharmony_ci		val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / div - 1)
70162306a36Sopenharmony_ci				& S3C64XX_SPI_PSR_MASK);
70262306a36Sopenharmony_ci		writel(val, regs + S3C64XX_SPI_CLK_CFG);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		/* Enable Clock */
70562306a36Sopenharmony_ci		val = readl(regs + S3C64XX_SPI_CLK_CFG);
70662306a36Sopenharmony_ci		val |= S3C64XX_SPI_ENCLK_ENABLE;
70762306a36Sopenharmony_ci		writel(val, regs + S3C64XX_SPI_CLK_CFG);
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	return 0;
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci#define XFER_DMAADDR_INVALID DMA_BIT_MASK(32)
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic int s3c64xx_spi_prepare_message(struct spi_controller *host,
71662306a36Sopenharmony_ci				       struct spi_message *msg)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host);
71962306a36Sopenharmony_ci	struct spi_device *spi = msg->spi;
72062306a36Sopenharmony_ci	struct s3c64xx_spi_csinfo *cs = spi->controller_data;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* Configure feedback delay */
72362306a36Sopenharmony_ci	if (!cs)
72462306a36Sopenharmony_ci		/* No delay if not defined */
72562306a36Sopenharmony_ci		writel(0, sdd->regs + S3C64XX_SPI_FB_CLK);
72662306a36Sopenharmony_ci	else
72762306a36Sopenharmony_ci		writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	return 0;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_cistatic size_t s3c64xx_spi_max_transfer_size(struct spi_device *spi)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	struct spi_controller *ctlr = spi->controller;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	return ctlr->can_dma ? S3C64XX_SPI_PACKET_CNT_MASK : SIZE_MAX;
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_cistatic int s3c64xx_spi_transfer_one(struct spi_controller *host,
74062306a36Sopenharmony_ci				    struct spi_device *spi,
74162306a36Sopenharmony_ci				    struct spi_transfer *xfer)
74262306a36Sopenharmony_ci{
74362306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host);
74462306a36Sopenharmony_ci	const unsigned int fifo_len = (FIFO_LVL_MASK(sdd) >> 1) + 1;
74562306a36Sopenharmony_ci	const void *tx_buf = NULL;
74662306a36Sopenharmony_ci	void *rx_buf = NULL;
74762306a36Sopenharmony_ci	int target_len = 0, origin_len = 0;
74862306a36Sopenharmony_ci	int use_dma = 0;
74962306a36Sopenharmony_ci	bool use_irq = false;
75062306a36Sopenharmony_ci	int status;
75162306a36Sopenharmony_ci	u32 speed;
75262306a36Sopenharmony_ci	u8 bpw;
75362306a36Sopenharmony_ci	unsigned long flags;
75462306a36Sopenharmony_ci	u32 rdy_lv;
75562306a36Sopenharmony_ci	u32 val;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	reinit_completion(&sdd->xfer_completion);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* Only BPW and Speed may change across transfers */
76062306a36Sopenharmony_ci	bpw = xfer->bits_per_word;
76162306a36Sopenharmony_ci	speed = xfer->speed_hz;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) {
76462306a36Sopenharmony_ci		sdd->cur_bpw = bpw;
76562306a36Sopenharmony_ci		sdd->cur_speed = speed;
76662306a36Sopenharmony_ci		sdd->cur_mode = spi->mode;
76762306a36Sopenharmony_ci		status = s3c64xx_spi_config(sdd);
76862306a36Sopenharmony_ci		if (status)
76962306a36Sopenharmony_ci			return status;
77062306a36Sopenharmony_ci	}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	if (!is_polling(sdd) && (xfer->len > fifo_len) &&
77362306a36Sopenharmony_ci	    sdd->rx_dma.ch && sdd->tx_dma.ch) {
77462306a36Sopenharmony_ci		use_dma = 1;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	} else if (xfer->len >= fifo_len) {
77762306a36Sopenharmony_ci		tx_buf = xfer->tx_buf;
77862306a36Sopenharmony_ci		rx_buf = xfer->rx_buf;
77962306a36Sopenharmony_ci		origin_len = xfer->len;
78062306a36Sopenharmony_ci		target_len = xfer->len;
78162306a36Sopenharmony_ci		xfer->len = fifo_len - 1;
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	do {
78562306a36Sopenharmony_ci		/* transfer size is greater than 32, change to IRQ mode */
78662306a36Sopenharmony_ci		if (!use_dma && xfer->len > S3C64XX_SPI_POLLING_SIZE)
78762306a36Sopenharmony_ci			use_irq = true;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci		if (use_irq) {
79062306a36Sopenharmony_ci			reinit_completion(&sdd->xfer_completion);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci			rdy_lv = xfer->len;
79362306a36Sopenharmony_ci			/* Setup RDY_FIFO trigger Level
79462306a36Sopenharmony_ci			 * RDY_LVL =
79562306a36Sopenharmony_ci			 * fifo_lvl up to 64 byte -> N bytes
79662306a36Sopenharmony_ci			 *               128 byte -> RDY_LVL * 2 bytes
79762306a36Sopenharmony_ci			 *               256 byte -> RDY_LVL * 4 bytes
79862306a36Sopenharmony_ci			 */
79962306a36Sopenharmony_ci			if (fifo_len == 128)
80062306a36Sopenharmony_ci				rdy_lv /= 2;
80162306a36Sopenharmony_ci			else if (fifo_len == 256)
80262306a36Sopenharmony_ci				rdy_lv /= 4;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci			val = readl(sdd->regs + S3C64XX_SPI_MODE_CFG);
80562306a36Sopenharmony_ci			val &= ~S3C64XX_SPI_MODE_RX_RDY_LVL;
80662306a36Sopenharmony_ci			val |= (rdy_lv << S3C64XX_SPI_MODE_RX_RDY_LVL_SHIFT);
80762306a36Sopenharmony_ci			writel(val, sdd->regs + S3C64XX_SPI_MODE_CFG);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci			/* Enable FIFO_RDY_EN IRQ */
81062306a36Sopenharmony_ci			val = readl(sdd->regs + S3C64XX_SPI_INT_EN);
81162306a36Sopenharmony_ci			writel((val | S3C64XX_SPI_INT_RX_FIFORDY_EN),
81262306a36Sopenharmony_ci					sdd->regs + S3C64XX_SPI_INT_EN);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci		}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci		spin_lock_irqsave(&sdd->lock, flags);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci		/* Pending only which is to be done */
81962306a36Sopenharmony_ci		sdd->state &= ~RXBUSY;
82062306a36Sopenharmony_ci		sdd->state &= ~TXBUSY;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci		/* Start the signals */
82362306a36Sopenharmony_ci		s3c64xx_spi_set_cs(spi, true);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci		status = s3c64xx_enable_datapath(sdd, xfer, use_dma);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci		spin_unlock_irqrestore(&sdd->lock, flags);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci		if (status) {
83062306a36Sopenharmony_ci			dev_err(&spi->dev, "failed to enable data path for transfer: %d\n", status);
83162306a36Sopenharmony_ci			break;
83262306a36Sopenharmony_ci		}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci		if (use_dma)
83562306a36Sopenharmony_ci			status = s3c64xx_wait_for_dma(sdd, xfer);
83662306a36Sopenharmony_ci		else
83762306a36Sopenharmony_ci			status = s3c64xx_wait_for_pio(sdd, xfer, use_irq);
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci		if (status) {
84062306a36Sopenharmony_ci			dev_err(&spi->dev,
84162306a36Sopenharmony_ci				"I/O Error: rx-%d tx-%d rx-%c tx-%c len-%d dma-%d res-(%d)\n",
84262306a36Sopenharmony_ci				xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0,
84362306a36Sopenharmony_ci				(sdd->state & RXBUSY) ? 'f' : 'p',
84462306a36Sopenharmony_ci				(sdd->state & TXBUSY) ? 'f' : 'p',
84562306a36Sopenharmony_ci				xfer->len, use_dma ? 1 : 0, status);
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci			if (use_dma) {
84862306a36Sopenharmony_ci				struct dma_tx_state s;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci				if (xfer->tx_buf && (sdd->state & TXBUSY)) {
85162306a36Sopenharmony_ci					dmaengine_pause(sdd->tx_dma.ch);
85262306a36Sopenharmony_ci					dmaengine_tx_status(sdd->tx_dma.ch, sdd->tx_dma.cookie, &s);
85362306a36Sopenharmony_ci					dmaengine_terminate_all(sdd->tx_dma.ch);
85462306a36Sopenharmony_ci					dev_err(&spi->dev, "TX residue: %d\n", s.residue);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci				}
85762306a36Sopenharmony_ci				if (xfer->rx_buf && (sdd->state & RXBUSY)) {
85862306a36Sopenharmony_ci					dmaengine_pause(sdd->rx_dma.ch);
85962306a36Sopenharmony_ci					dmaengine_tx_status(sdd->rx_dma.ch, sdd->rx_dma.cookie, &s);
86062306a36Sopenharmony_ci					dmaengine_terminate_all(sdd->rx_dma.ch);
86162306a36Sopenharmony_ci					dev_err(&spi->dev, "RX residue: %d\n", s.residue);
86262306a36Sopenharmony_ci				}
86362306a36Sopenharmony_ci			}
86462306a36Sopenharmony_ci		} else {
86562306a36Sopenharmony_ci			s3c64xx_flush_fifo(sdd);
86662306a36Sopenharmony_ci		}
86762306a36Sopenharmony_ci		if (target_len > 0) {
86862306a36Sopenharmony_ci			target_len -= xfer->len;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci			if (xfer->tx_buf)
87162306a36Sopenharmony_ci				xfer->tx_buf += xfer->len;
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci			if (xfer->rx_buf)
87462306a36Sopenharmony_ci				xfer->rx_buf += xfer->len;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci			if (target_len >= fifo_len)
87762306a36Sopenharmony_ci				xfer->len = fifo_len - 1;
87862306a36Sopenharmony_ci			else
87962306a36Sopenharmony_ci				xfer->len = target_len;
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci	} while (target_len > 0);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (origin_len) {
88462306a36Sopenharmony_ci		/* Restore original xfer buffers and length */
88562306a36Sopenharmony_ci		xfer->tx_buf = tx_buf;
88662306a36Sopenharmony_ci		xfer->rx_buf = rx_buf;
88762306a36Sopenharmony_ci		xfer->len = origin_len;
88862306a36Sopenharmony_ci	}
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	return status;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic struct s3c64xx_spi_csinfo *s3c64xx_get_target_ctrldata(
89462306a36Sopenharmony_ci				struct spi_device *spi)
89562306a36Sopenharmony_ci{
89662306a36Sopenharmony_ci	struct s3c64xx_spi_csinfo *cs;
89762306a36Sopenharmony_ci	struct device_node *target_np, *data_np = NULL;
89862306a36Sopenharmony_ci	u32 fb_delay = 0;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	target_np = spi->dev.of_node;
90162306a36Sopenharmony_ci	if (!target_np) {
90262306a36Sopenharmony_ci		dev_err(&spi->dev, "device node not found\n");
90362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	cs = kzalloc(sizeof(*cs), GFP_KERNEL);
90762306a36Sopenharmony_ci	if (!cs)
90862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	data_np = of_get_child_by_name(target_np, "controller-data");
91162306a36Sopenharmony_ci	if (!data_np) {
91262306a36Sopenharmony_ci		dev_info(&spi->dev, "feedback delay set to default (0)\n");
91362306a36Sopenharmony_ci		return cs;
91462306a36Sopenharmony_ci	}
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay);
91762306a36Sopenharmony_ci	cs->fb_delay = fb_delay;
91862306a36Sopenharmony_ci	of_node_put(data_np);
91962306a36Sopenharmony_ci	return cs;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci/*
92362306a36Sopenharmony_ci * Here we only check the validity of requested configuration
92462306a36Sopenharmony_ci * and save the configuration in a local data-structure.
92562306a36Sopenharmony_ci * The controller is actually configured only just before we
92662306a36Sopenharmony_ci * get a message to transfer.
92762306a36Sopenharmony_ci */
92862306a36Sopenharmony_cistatic int s3c64xx_spi_setup(struct spi_device *spi)
92962306a36Sopenharmony_ci{
93062306a36Sopenharmony_ci	struct s3c64xx_spi_csinfo *cs = spi->controller_data;
93162306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd;
93262306a36Sopenharmony_ci	int err;
93362306a36Sopenharmony_ci	int div;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	sdd = spi_controller_get_devdata(spi->controller);
93662306a36Sopenharmony_ci	if (spi->dev.of_node) {
93762306a36Sopenharmony_ci		cs = s3c64xx_get_target_ctrldata(spi);
93862306a36Sopenharmony_ci		spi->controller_data = cs;
93962306a36Sopenharmony_ci	}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/* NULL is fine, we just avoid using the FB delay (=0) */
94262306a36Sopenharmony_ci	if (IS_ERR(cs)) {
94362306a36Sopenharmony_ci		dev_err(&spi->dev, "No CS for SPI(%d)\n", spi_get_chipselect(spi, 0));
94462306a36Sopenharmony_ci		return -ENODEV;
94562306a36Sopenharmony_ci	}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (!spi_get_ctldata(spi))
94862306a36Sopenharmony_ci		spi_set_ctldata(spi, cs);
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	pm_runtime_get_sync(&sdd->pdev->dev);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	div = sdd->port_conf->clk_div;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	/* Check if we can provide the requested rate */
95562306a36Sopenharmony_ci	if (!sdd->port_conf->clk_from_cmu) {
95662306a36Sopenharmony_ci		u32 psr, speed;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci		/* Max possible */
95962306a36Sopenharmony_ci		speed = clk_get_rate(sdd->src_clk) / div / (0 + 1);
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci		if (spi->max_speed_hz > speed)
96262306a36Sopenharmony_ci			spi->max_speed_hz = speed;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci		psr = clk_get_rate(sdd->src_clk) / div / spi->max_speed_hz - 1;
96562306a36Sopenharmony_ci		psr &= S3C64XX_SPI_PSR_MASK;
96662306a36Sopenharmony_ci		if (psr == S3C64XX_SPI_PSR_MASK)
96762306a36Sopenharmony_ci			psr--;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci		speed = clk_get_rate(sdd->src_clk) / div / (psr + 1);
97062306a36Sopenharmony_ci		if (spi->max_speed_hz < speed) {
97162306a36Sopenharmony_ci			if (psr+1 < S3C64XX_SPI_PSR_MASK) {
97262306a36Sopenharmony_ci				psr++;
97362306a36Sopenharmony_ci			} else {
97462306a36Sopenharmony_ci				err = -EINVAL;
97562306a36Sopenharmony_ci				goto setup_exit;
97662306a36Sopenharmony_ci			}
97762306a36Sopenharmony_ci		}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci		speed = clk_get_rate(sdd->src_clk) / div / (psr + 1);
98062306a36Sopenharmony_ci		if (spi->max_speed_hz >= speed) {
98162306a36Sopenharmony_ci			spi->max_speed_hz = speed;
98262306a36Sopenharmony_ci		} else {
98362306a36Sopenharmony_ci			dev_err(&spi->dev, "Can't set %dHz transfer speed\n",
98462306a36Sopenharmony_ci				spi->max_speed_hz);
98562306a36Sopenharmony_ci			err = -EINVAL;
98662306a36Sopenharmony_ci			goto setup_exit;
98762306a36Sopenharmony_ci		}
98862306a36Sopenharmony_ci	}
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&sdd->pdev->dev);
99162306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&sdd->pdev->dev);
99262306a36Sopenharmony_ci	s3c64xx_spi_set_cs(spi, false);
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	return 0;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cisetup_exit:
99762306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&sdd->pdev->dev);
99862306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&sdd->pdev->dev);
99962306a36Sopenharmony_ci	/* setup() returns with device de-selected */
100062306a36Sopenharmony_ci	s3c64xx_spi_set_cs(spi, false);
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	spi_set_ctldata(spi, NULL);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	/* This was dynamically allocated on the DT path */
100562306a36Sopenharmony_ci	if (spi->dev.of_node)
100662306a36Sopenharmony_ci		kfree(cs);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	return err;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_cistatic void s3c64xx_spi_cleanup(struct spi_device *spi)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	/* This was dynamically allocated on the DT path */
101662306a36Sopenharmony_ci	if (spi->dev.of_node)
101762306a36Sopenharmony_ci		kfree(cs);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	spi_set_ctldata(spi, NULL);
102062306a36Sopenharmony_ci}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_cistatic irqreturn_t s3c64xx_spi_irq(int irq, void *data)
102362306a36Sopenharmony_ci{
102462306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = data;
102562306a36Sopenharmony_ci	struct spi_controller *spi = sdd->host;
102662306a36Sopenharmony_ci	unsigned int val, clr = 0;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	val = readl(sdd->regs + S3C64XX_SPI_STATUS);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (val & S3C64XX_SPI_ST_RX_OVERRUN_ERR) {
103162306a36Sopenharmony_ci		clr = S3C64XX_SPI_PND_RX_OVERRUN_CLR;
103262306a36Sopenharmony_ci		dev_err(&spi->dev, "RX overrun\n");
103362306a36Sopenharmony_ci	}
103462306a36Sopenharmony_ci	if (val & S3C64XX_SPI_ST_RX_UNDERRUN_ERR) {
103562306a36Sopenharmony_ci		clr |= S3C64XX_SPI_PND_RX_UNDERRUN_CLR;
103662306a36Sopenharmony_ci		dev_err(&spi->dev, "RX underrun\n");
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci	if (val & S3C64XX_SPI_ST_TX_OVERRUN_ERR) {
103962306a36Sopenharmony_ci		clr |= S3C64XX_SPI_PND_TX_OVERRUN_CLR;
104062306a36Sopenharmony_ci		dev_err(&spi->dev, "TX overrun\n");
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci	if (val & S3C64XX_SPI_ST_TX_UNDERRUN_ERR) {
104362306a36Sopenharmony_ci		clr |= S3C64XX_SPI_PND_TX_UNDERRUN_CLR;
104462306a36Sopenharmony_ci		dev_err(&spi->dev, "TX underrun\n");
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	if (val & S3C64XX_SPI_ST_RX_FIFORDY) {
104862306a36Sopenharmony_ci		complete(&sdd->xfer_completion);
104962306a36Sopenharmony_ci		/* No pending clear irq, turn-off INT_EN_RX_FIFO_RDY */
105062306a36Sopenharmony_ci		val = readl(sdd->regs + S3C64XX_SPI_INT_EN);
105162306a36Sopenharmony_ci		writel((val & ~S3C64XX_SPI_INT_RX_FIFORDY_EN),
105262306a36Sopenharmony_ci				sdd->regs + S3C64XX_SPI_INT_EN);
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	/* Clear the pending irq by setting and then clearing it */
105662306a36Sopenharmony_ci	writel(clr, sdd->regs + S3C64XX_SPI_PENDING_CLR);
105762306a36Sopenharmony_ci	writel(0, sdd->regs + S3C64XX_SPI_PENDING_CLR);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	return IRQ_HANDLED;
106062306a36Sopenharmony_ci}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_cistatic void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
106562306a36Sopenharmony_ci	void __iomem *regs = sdd->regs;
106662306a36Sopenharmony_ci	unsigned int val;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	sdd->cur_speed = 0;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	if (sci->no_cs)
107162306a36Sopenharmony_ci		writel(0, sdd->regs + S3C64XX_SPI_CS_REG);
107262306a36Sopenharmony_ci	else if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO))
107362306a36Sopenharmony_ci		writel(S3C64XX_SPI_CS_SIG_INACT, sdd->regs + S3C64XX_SPI_CS_REG);
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	/* Disable Interrupts - we use Polling if not DMA mode */
107662306a36Sopenharmony_ci	writel(0, regs + S3C64XX_SPI_INT_EN);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	if (!sdd->port_conf->clk_from_cmu)
107962306a36Sopenharmony_ci		writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT,
108062306a36Sopenharmony_ci				regs + S3C64XX_SPI_CLK_CFG);
108162306a36Sopenharmony_ci	writel(0, regs + S3C64XX_SPI_MODE_CFG);
108262306a36Sopenharmony_ci	writel(0, regs + S3C64XX_SPI_PACKET_CNT);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	/* Clear any irq pending bits, should set and clear the bits */
108562306a36Sopenharmony_ci	val = S3C64XX_SPI_PND_RX_OVERRUN_CLR |
108662306a36Sopenharmony_ci		S3C64XX_SPI_PND_RX_UNDERRUN_CLR |
108762306a36Sopenharmony_ci		S3C64XX_SPI_PND_TX_OVERRUN_CLR |
108862306a36Sopenharmony_ci		S3C64XX_SPI_PND_TX_UNDERRUN_CLR;
108962306a36Sopenharmony_ci	writel(val, regs + S3C64XX_SPI_PENDING_CLR);
109062306a36Sopenharmony_ci	writel(0, regs + S3C64XX_SPI_PENDING_CLR);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	writel(0, regs + S3C64XX_SPI_SWAP_CFG);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	val = readl(regs + S3C64XX_SPI_MODE_CFG);
109562306a36Sopenharmony_ci	val &= ~S3C64XX_SPI_MODE_4BURST;
109662306a36Sopenharmony_ci	val &= ~(S3C64XX_SPI_MAX_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF);
109762306a36Sopenharmony_ci	val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF);
109862306a36Sopenharmony_ci	writel(val, regs + S3C64XX_SPI_MODE_CFG);
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci	s3c64xx_flush_fifo(sdd);
110162306a36Sopenharmony_ci}
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci#ifdef CONFIG_OF
110462306a36Sopenharmony_cistatic struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	struct s3c64xx_spi_info *sci;
110762306a36Sopenharmony_ci	u32 temp;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	sci = devm_kzalloc(dev, sizeof(*sci), GFP_KERNEL);
111062306a36Sopenharmony_ci	if (!sci)
111162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	if (of_property_read_u32(dev->of_node, "samsung,spi-src-clk", &temp)) {
111462306a36Sopenharmony_ci		dev_warn(dev, "spi bus clock parent not specified, using clock at index 0 as parent\n");
111562306a36Sopenharmony_ci		sci->src_clk_nr = 0;
111662306a36Sopenharmony_ci	} else {
111762306a36Sopenharmony_ci		sci->src_clk_nr = temp;
111862306a36Sopenharmony_ci	}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	if (of_property_read_u32(dev->of_node, "num-cs", &temp)) {
112162306a36Sopenharmony_ci		dev_warn(dev, "number of chip select lines not specified, assuming 1 chip select line\n");
112262306a36Sopenharmony_ci		sci->num_cs = 1;
112362306a36Sopenharmony_ci	} else {
112462306a36Sopenharmony_ci		sci->num_cs = temp;
112562306a36Sopenharmony_ci	}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	sci->no_cs = of_property_read_bool(dev->of_node, "no-cs-readback");
112862306a36Sopenharmony_ci	sci->polling = !of_property_present(dev->of_node, "dmas");
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	return sci;
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci#else
113362306a36Sopenharmony_cistatic struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev)
113462306a36Sopenharmony_ci{
113562306a36Sopenharmony_ci	return dev_get_platdata(dev);
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci#endif
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic inline const struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config(
114062306a36Sopenharmony_ci						struct platform_device *pdev)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci#ifdef CONFIG_OF
114362306a36Sopenharmony_ci	if (pdev->dev.of_node)
114462306a36Sopenharmony_ci		return of_device_get_match_data(&pdev->dev);
114562306a36Sopenharmony_ci#endif
114662306a36Sopenharmony_ci	return (const struct s3c64xx_spi_port_config *)platform_get_device_id(pdev)->driver_data;
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic int s3c64xx_spi_probe(struct platform_device *pdev)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	struct resource	*mem_res;
115262306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd;
115362306a36Sopenharmony_ci	struct s3c64xx_spi_info *sci = dev_get_platdata(&pdev->dev);
115462306a36Sopenharmony_ci	struct spi_controller *host;
115562306a36Sopenharmony_ci	int ret, irq;
115662306a36Sopenharmony_ci	char clk_name[16];
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (!sci && pdev->dev.of_node) {
115962306a36Sopenharmony_ci		sci = s3c64xx_spi_parse_dt(&pdev->dev);
116062306a36Sopenharmony_ci		if (IS_ERR(sci))
116162306a36Sopenharmony_ci			return PTR_ERR(sci);
116262306a36Sopenharmony_ci	}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	if (!sci)
116562306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, -ENODEV,
116662306a36Sopenharmony_ci				     "Platform_data missing!\n");
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
116962306a36Sopenharmony_ci	if (irq < 0)
117062306a36Sopenharmony_ci		return irq;
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_ci	host = devm_spi_alloc_host(&pdev->dev, sizeof(*sdd));
117362306a36Sopenharmony_ci	if (!host)
117462306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, -ENOMEM,
117562306a36Sopenharmony_ci				     "Unable to allocate SPI Host\n");
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	platform_set_drvdata(pdev, host);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	sdd = spi_controller_get_devdata(host);
118062306a36Sopenharmony_ci	sdd->port_conf = s3c64xx_spi_get_port_config(pdev);
118162306a36Sopenharmony_ci	sdd->host = host;
118262306a36Sopenharmony_ci	sdd->cntrlr_info = sci;
118362306a36Sopenharmony_ci	sdd->pdev = pdev;
118462306a36Sopenharmony_ci	if (pdev->dev.of_node) {
118562306a36Sopenharmony_ci		ret = of_alias_get_id(pdev->dev.of_node, "spi");
118662306a36Sopenharmony_ci		if (ret < 0)
118762306a36Sopenharmony_ci			return dev_err_probe(&pdev->dev, ret,
118862306a36Sopenharmony_ci					     "Failed to get alias id\n");
118962306a36Sopenharmony_ci		sdd->port_id = ret;
119062306a36Sopenharmony_ci	} else {
119162306a36Sopenharmony_ci		sdd->port_id = pdev->id;
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	sdd->cur_bpw = 8;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci	sdd->tx_dma.direction = DMA_MEM_TO_DEV;
119762306a36Sopenharmony_ci	sdd->rx_dma.direction = DMA_DEV_TO_MEM;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	host->dev.of_node = pdev->dev.of_node;
120062306a36Sopenharmony_ci	host->bus_num = sdd->port_id;
120162306a36Sopenharmony_ci	host->setup = s3c64xx_spi_setup;
120262306a36Sopenharmony_ci	host->cleanup = s3c64xx_spi_cleanup;
120362306a36Sopenharmony_ci	host->prepare_transfer_hardware = s3c64xx_spi_prepare_transfer;
120462306a36Sopenharmony_ci	host->unprepare_transfer_hardware = s3c64xx_spi_unprepare_transfer;
120562306a36Sopenharmony_ci	host->prepare_message = s3c64xx_spi_prepare_message;
120662306a36Sopenharmony_ci	host->transfer_one = s3c64xx_spi_transfer_one;
120762306a36Sopenharmony_ci	host->max_transfer_size = s3c64xx_spi_max_transfer_size;
120862306a36Sopenharmony_ci	host->num_chipselect = sci->num_cs;
120962306a36Sopenharmony_ci	host->use_gpio_descriptors = true;
121062306a36Sopenharmony_ci	host->dma_alignment = 8;
121162306a36Sopenharmony_ci	host->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
121262306a36Sopenharmony_ci				   SPI_BPW_MASK(8);
121362306a36Sopenharmony_ci	/* the spi->mode bits understood by this driver: */
121462306a36Sopenharmony_ci	host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
121562306a36Sopenharmony_ci	if (sdd->port_conf->has_loopback)
121662306a36Sopenharmony_ci		host->mode_bits |= SPI_LOOP;
121762306a36Sopenharmony_ci	host->auto_runtime_pm = true;
121862306a36Sopenharmony_ci	if (!is_polling(sdd))
121962306a36Sopenharmony_ci		host->can_dma = s3c64xx_spi_can_dma;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	sdd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res);
122262306a36Sopenharmony_ci	if (IS_ERR(sdd->regs))
122362306a36Sopenharmony_ci		return PTR_ERR(sdd->regs);
122462306a36Sopenharmony_ci	sdd->sfr_start = mem_res->start;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	if (sci->cfg_gpio && sci->cfg_gpio())
122762306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, -EBUSY,
122862306a36Sopenharmony_ci				     "Unable to config gpio\n");
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	/* Setup clocks */
123162306a36Sopenharmony_ci	sdd->clk = devm_clk_get_enabled(&pdev->dev, "spi");
123262306a36Sopenharmony_ci	if (IS_ERR(sdd->clk))
123362306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(sdd->clk),
123462306a36Sopenharmony_ci				     "Unable to acquire clock 'spi'\n");
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	sprintf(clk_name, "spi_busclk%d", sci->src_clk_nr);
123762306a36Sopenharmony_ci	sdd->src_clk = devm_clk_get_enabled(&pdev->dev, clk_name);
123862306a36Sopenharmony_ci	if (IS_ERR(sdd->src_clk))
123962306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, PTR_ERR(sdd->src_clk),
124062306a36Sopenharmony_ci				     "Unable to acquire clock '%s'\n",
124162306a36Sopenharmony_ci				     clk_name);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	if (sdd->port_conf->clk_ioclk) {
124462306a36Sopenharmony_ci		sdd->ioclk = devm_clk_get_enabled(&pdev->dev, "spi_ioclk");
124562306a36Sopenharmony_ci		if (IS_ERR(sdd->ioclk))
124662306a36Sopenharmony_ci			return dev_err_probe(&pdev->dev, PTR_ERR(sdd->ioclk),
124762306a36Sopenharmony_ci					     "Unable to acquire 'ioclk'\n");
124862306a36Sopenharmony_ci	}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&pdev->dev, AUTOSUSPEND_TIMEOUT);
125162306a36Sopenharmony_ci	pm_runtime_use_autosuspend(&pdev->dev);
125262306a36Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
125362306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
125462306a36Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	/* Setup Deufult Mode */
125762306a36Sopenharmony_ci	s3c64xx_spi_hwinit(sdd);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	spin_lock_init(&sdd->lock);
126062306a36Sopenharmony_ci	init_completion(&sdd->xfer_completion);
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, irq, s3c64xx_spi_irq, 0,
126362306a36Sopenharmony_ci				"spi-s3c64xx", sdd);
126462306a36Sopenharmony_ci	if (ret != 0) {
126562306a36Sopenharmony_ci		dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n",
126662306a36Sopenharmony_ci			irq, ret);
126762306a36Sopenharmony_ci		goto err_pm_put;
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN |
127162306a36Sopenharmony_ci	       S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN,
127262306a36Sopenharmony_ci	       sdd->regs + S3C64XX_SPI_INT_EN);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	ret = devm_spi_register_controller(&pdev->dev, host);
127562306a36Sopenharmony_ci	if (ret != 0) {
127662306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot register SPI host: %d\n", ret);
127762306a36Sopenharmony_ci		goto err_pm_put;
127862306a36Sopenharmony_ci	}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d with %d Targets attached\n",
128162306a36Sopenharmony_ci					sdd->port_id, host->num_chipselect);
128262306a36Sopenharmony_ci	dev_dbg(&pdev->dev, "\tIOmem=[%pR]\tFIFO %dbytes\n",
128362306a36Sopenharmony_ci					mem_res, (FIFO_LVL_MASK(sdd) >> 1) + 1);
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	pm_runtime_mark_last_busy(&pdev->dev);
128662306a36Sopenharmony_ci	pm_runtime_put_autosuspend(&pdev->dev);
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	return 0;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cierr_pm_put:
129162306a36Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
129262306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
129362306a36Sopenharmony_ci	pm_runtime_set_suspended(&pdev->dev);
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	return ret;
129662306a36Sopenharmony_ci}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_cistatic void s3c64xx_spi_remove(struct platform_device *pdev)
129962306a36Sopenharmony_ci{
130062306a36Sopenharmony_ci	struct spi_controller *host = platform_get_drvdata(pdev);
130162306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	writel(0, sdd->regs + S3C64XX_SPI_INT_EN);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	if (!is_polling(sdd)) {
130862306a36Sopenharmony_ci		dma_release_channel(sdd->rx_dma.ch);
130962306a36Sopenharmony_ci		dma_release_channel(sdd->tx_dma.ch);
131062306a36Sopenharmony_ci	}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	pm_runtime_put_noidle(&pdev->dev);
131362306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
131462306a36Sopenharmony_ci	pm_runtime_set_suspended(&pdev->dev);
131562306a36Sopenharmony_ci}
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
131862306a36Sopenharmony_cistatic int s3c64xx_spi_suspend(struct device *dev)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	struct spi_controller *host = dev_get_drvdata(dev);
132162306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	int ret = spi_controller_suspend(host);
132462306a36Sopenharmony_ci	if (ret)
132562306a36Sopenharmony_ci		return ret;
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	ret = pm_runtime_force_suspend(dev);
132862306a36Sopenharmony_ci	if (ret < 0)
132962306a36Sopenharmony_ci		return ret;
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	sdd->cur_speed = 0; /* Output Clock is stopped */
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	return 0;
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic int s3c64xx_spi_resume(struct device *dev)
133762306a36Sopenharmony_ci{
133862306a36Sopenharmony_ci	struct spi_controller *host = dev_get_drvdata(dev);
133962306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host);
134062306a36Sopenharmony_ci	struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
134162306a36Sopenharmony_ci	int ret;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (sci->cfg_gpio)
134462306a36Sopenharmony_ci		sci->cfg_gpio();
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	ret = pm_runtime_force_resume(dev);
134762306a36Sopenharmony_ci	if (ret < 0)
134862306a36Sopenharmony_ci		return ret;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	return spi_controller_resume(host);
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci#endif /* CONFIG_PM_SLEEP */
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci#ifdef CONFIG_PM
135562306a36Sopenharmony_cistatic int s3c64xx_spi_runtime_suspend(struct device *dev)
135662306a36Sopenharmony_ci{
135762306a36Sopenharmony_ci	struct spi_controller *host = dev_get_drvdata(dev);
135862306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	clk_disable_unprepare(sdd->clk);
136162306a36Sopenharmony_ci	clk_disable_unprepare(sdd->src_clk);
136262306a36Sopenharmony_ci	clk_disable_unprepare(sdd->ioclk);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	return 0;
136562306a36Sopenharmony_ci}
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_cistatic int s3c64xx_spi_runtime_resume(struct device *dev)
136862306a36Sopenharmony_ci{
136962306a36Sopenharmony_ci	struct spi_controller *host = dev_get_drvdata(dev);
137062306a36Sopenharmony_ci	struct s3c64xx_spi_driver_data *sdd = spi_controller_get_devdata(host);
137162306a36Sopenharmony_ci	int ret;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (sdd->port_conf->clk_ioclk) {
137462306a36Sopenharmony_ci		ret = clk_prepare_enable(sdd->ioclk);
137562306a36Sopenharmony_ci		if (ret != 0)
137662306a36Sopenharmony_ci			return ret;
137762306a36Sopenharmony_ci	}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	ret = clk_prepare_enable(sdd->src_clk);
138062306a36Sopenharmony_ci	if (ret != 0)
138162306a36Sopenharmony_ci		goto err_disable_ioclk;
138262306a36Sopenharmony_ci
138362306a36Sopenharmony_ci	ret = clk_prepare_enable(sdd->clk);
138462306a36Sopenharmony_ci	if (ret != 0)
138562306a36Sopenharmony_ci		goto err_disable_src_clk;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	s3c64xx_spi_hwinit(sdd);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN |
139062306a36Sopenharmony_ci	       S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN,
139162306a36Sopenharmony_ci	       sdd->regs + S3C64XX_SPI_INT_EN);
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	return 0;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_cierr_disable_src_clk:
139662306a36Sopenharmony_ci	clk_disable_unprepare(sdd->src_clk);
139762306a36Sopenharmony_cierr_disable_ioclk:
139862306a36Sopenharmony_ci	clk_disable_unprepare(sdd->ioclk);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	return ret;
140162306a36Sopenharmony_ci}
140262306a36Sopenharmony_ci#endif /* CONFIG_PM */
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_cistatic const struct dev_pm_ops s3c64xx_spi_pm = {
140562306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume)
140662306a36Sopenharmony_ci	SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend,
140762306a36Sopenharmony_ci			   s3c64xx_spi_runtime_resume, NULL)
140862306a36Sopenharmony_ci};
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
141162306a36Sopenharmony_ci	.fifo_lvl_mask	= { 0x7f },
141262306a36Sopenharmony_ci	.rx_lvl_offset	= 13,
141362306a36Sopenharmony_ci	.tx_st_done	= 21,
141462306a36Sopenharmony_ci	.clk_div	= 2,
141562306a36Sopenharmony_ci	.high_speed	= true,
141662306a36Sopenharmony_ci};
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
141962306a36Sopenharmony_ci	.fifo_lvl_mask	= { 0x7f, 0x7F },
142062306a36Sopenharmony_ci	.rx_lvl_offset	= 13,
142162306a36Sopenharmony_ci	.tx_st_done	= 21,
142262306a36Sopenharmony_ci	.clk_div	= 2,
142362306a36Sopenharmony_ci};
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
142662306a36Sopenharmony_ci	.fifo_lvl_mask	= { 0x1ff, 0x7F },
142762306a36Sopenharmony_ci	.rx_lvl_offset	= 15,
142862306a36Sopenharmony_ci	.tx_st_done	= 25,
142962306a36Sopenharmony_ci	.clk_div	= 2,
143062306a36Sopenharmony_ci	.high_speed	= true,
143162306a36Sopenharmony_ci};
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config exynos4_spi_port_config = {
143462306a36Sopenharmony_ci	.fifo_lvl_mask	= { 0x1ff, 0x7F, 0x7F },
143562306a36Sopenharmony_ci	.rx_lvl_offset	= 15,
143662306a36Sopenharmony_ci	.tx_st_done	= 25,
143762306a36Sopenharmony_ci	.clk_div	= 2,
143862306a36Sopenharmony_ci	.high_speed	= true,
143962306a36Sopenharmony_ci	.clk_from_cmu	= true,
144062306a36Sopenharmony_ci	.quirks		= S3C64XX_SPI_QUIRK_CS_AUTO,
144162306a36Sopenharmony_ci};
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config exynos7_spi_port_config = {
144462306a36Sopenharmony_ci	.fifo_lvl_mask	= { 0x1ff, 0x7F, 0x7F, 0x7F, 0x7F, 0x1ff},
144562306a36Sopenharmony_ci	.rx_lvl_offset	= 15,
144662306a36Sopenharmony_ci	.tx_st_done	= 25,
144762306a36Sopenharmony_ci	.clk_div	= 2,
144862306a36Sopenharmony_ci	.high_speed	= true,
144962306a36Sopenharmony_ci	.clk_from_cmu	= true,
145062306a36Sopenharmony_ci	.quirks		= S3C64XX_SPI_QUIRK_CS_AUTO,
145162306a36Sopenharmony_ci};
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config exynos5433_spi_port_config = {
145462306a36Sopenharmony_ci	.fifo_lvl_mask	= { 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff},
145562306a36Sopenharmony_ci	.rx_lvl_offset	= 15,
145662306a36Sopenharmony_ci	.tx_st_done	= 25,
145762306a36Sopenharmony_ci	.clk_div	= 2,
145862306a36Sopenharmony_ci	.high_speed	= true,
145962306a36Sopenharmony_ci	.clk_from_cmu	= true,
146062306a36Sopenharmony_ci	.clk_ioclk	= true,
146162306a36Sopenharmony_ci	.quirks		= S3C64XX_SPI_QUIRK_CS_AUTO,
146262306a36Sopenharmony_ci};
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config exynosautov9_spi_port_config = {
146562306a36Sopenharmony_ci	.fifo_lvl_mask	= { 0x1ff, 0x1ff, 0x7f, 0x7f, 0x7f, 0x7f, 0x1ff, 0x7f,
146662306a36Sopenharmony_ci			    0x7f, 0x7f, 0x7f, 0x7f},
146762306a36Sopenharmony_ci	.rx_lvl_offset	= 15,
146862306a36Sopenharmony_ci	.tx_st_done	= 25,
146962306a36Sopenharmony_ci	.clk_div	= 4,
147062306a36Sopenharmony_ci	.high_speed	= true,
147162306a36Sopenharmony_ci	.clk_from_cmu	= true,
147262306a36Sopenharmony_ci	.clk_ioclk	= true,
147362306a36Sopenharmony_ci	.has_loopback	= true,
147462306a36Sopenharmony_ci	.quirks		= S3C64XX_SPI_QUIRK_CS_AUTO,
147562306a36Sopenharmony_ci};
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_cistatic const struct s3c64xx_spi_port_config fsd_spi_port_config = {
147862306a36Sopenharmony_ci	.fifo_lvl_mask	= { 0x7f, 0x7f, 0x7f, 0x7f, 0x7f},
147962306a36Sopenharmony_ci	.rx_lvl_offset	= 15,
148062306a36Sopenharmony_ci	.tx_st_done	= 25,
148162306a36Sopenharmony_ci	.clk_div	= 2,
148262306a36Sopenharmony_ci	.high_speed	= true,
148362306a36Sopenharmony_ci	.clk_from_cmu	= true,
148462306a36Sopenharmony_ci	.clk_ioclk	= false,
148562306a36Sopenharmony_ci	.quirks		= S3C64XX_SPI_QUIRK_CS_AUTO,
148662306a36Sopenharmony_ci};
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_cistatic const struct platform_device_id s3c64xx_spi_driver_ids[] = {
148962306a36Sopenharmony_ci	{
149062306a36Sopenharmony_ci		.name		= "s3c2443-spi",
149162306a36Sopenharmony_ci		.driver_data	= (kernel_ulong_t)&s3c2443_spi_port_config,
149262306a36Sopenharmony_ci	}, {
149362306a36Sopenharmony_ci		.name		= "s3c6410-spi",
149462306a36Sopenharmony_ci		.driver_data	= (kernel_ulong_t)&s3c6410_spi_port_config,
149562306a36Sopenharmony_ci	},
149662306a36Sopenharmony_ci	{ },
149762306a36Sopenharmony_ci};
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_cistatic const struct of_device_id s3c64xx_spi_dt_match[] = {
150062306a36Sopenharmony_ci	{ .compatible = "samsung,s3c2443-spi",
150162306a36Sopenharmony_ci			.data = (void *)&s3c2443_spi_port_config,
150262306a36Sopenharmony_ci	},
150362306a36Sopenharmony_ci	{ .compatible = "samsung,s3c6410-spi",
150462306a36Sopenharmony_ci			.data = (void *)&s3c6410_spi_port_config,
150562306a36Sopenharmony_ci	},
150662306a36Sopenharmony_ci	{ .compatible = "samsung,s5pv210-spi",
150762306a36Sopenharmony_ci			.data = (void *)&s5pv210_spi_port_config,
150862306a36Sopenharmony_ci	},
150962306a36Sopenharmony_ci	{ .compatible = "samsung,exynos4210-spi",
151062306a36Sopenharmony_ci			.data = (void *)&exynos4_spi_port_config,
151162306a36Sopenharmony_ci	},
151262306a36Sopenharmony_ci	{ .compatible = "samsung,exynos7-spi",
151362306a36Sopenharmony_ci			.data = (void *)&exynos7_spi_port_config,
151462306a36Sopenharmony_ci	},
151562306a36Sopenharmony_ci	{ .compatible = "samsung,exynos5433-spi",
151662306a36Sopenharmony_ci			.data = (void *)&exynos5433_spi_port_config,
151762306a36Sopenharmony_ci	},
151862306a36Sopenharmony_ci	{ .compatible = "samsung,exynosautov9-spi",
151962306a36Sopenharmony_ci			.data = (void *)&exynosautov9_spi_port_config,
152062306a36Sopenharmony_ci	},
152162306a36Sopenharmony_ci	{ .compatible = "tesla,fsd-spi",
152262306a36Sopenharmony_ci			.data = (void *)&fsd_spi_port_config,
152362306a36Sopenharmony_ci	},
152462306a36Sopenharmony_ci	{ },
152562306a36Sopenharmony_ci};
152662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match);
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_cistatic struct platform_driver s3c64xx_spi_driver = {
152962306a36Sopenharmony_ci	.driver = {
153062306a36Sopenharmony_ci		.name	= "s3c64xx-spi",
153162306a36Sopenharmony_ci		.pm = &s3c64xx_spi_pm,
153262306a36Sopenharmony_ci		.of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
153362306a36Sopenharmony_ci	},
153462306a36Sopenharmony_ci	.probe = s3c64xx_spi_probe,
153562306a36Sopenharmony_ci	.remove_new = s3c64xx_spi_remove,
153662306a36Sopenharmony_ci	.id_table = s3c64xx_spi_driver_ids,
153762306a36Sopenharmony_ci};
153862306a36Sopenharmony_ciMODULE_ALIAS("platform:s3c64xx-spi");
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_cimodule_platform_driver(s3c64xx_spi_driver);
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ciMODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
154362306a36Sopenharmony_ciMODULE_DESCRIPTION("S3C64XX SPI Controller Driver");
154462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1545