xref: /openbmc/linux/drivers/spi/spi-lantiq-ssc.c (revision ea11a8bb)
17876981aSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
217f84b79SHauke Mehrtens /*
317f84b79SHauke Mehrtens  * Copyright (C) 2011-2015 Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
417f84b79SHauke Mehrtens  * Copyright (C) 2016 Hauke Mehrtens <hauke@hauke-m.de>
517f84b79SHauke Mehrtens  */
617f84b79SHauke Mehrtens 
717f84b79SHauke Mehrtens #include <linux/kernel.h>
817f84b79SHauke Mehrtens #include <linux/module.h>
9749396cbSRob Herring #include <linux/of.h>
10749396cbSRob Herring #include <linux/platform_device.h>
1117f84b79SHauke Mehrtens #include <linux/clk.h>
1217f84b79SHauke Mehrtens #include <linux/io.h>
1317f84b79SHauke Mehrtens #include <linux/delay.h>
1417f84b79SHauke Mehrtens #include <linux/interrupt.h>
1517f84b79SHauke Mehrtens #include <linux/sched.h>
1617f84b79SHauke Mehrtens #include <linux/completion.h>
1717f84b79SHauke Mehrtens #include <linux/spinlock.h>
1817f84b79SHauke Mehrtens #include <linux/err.h>
1917f84b79SHauke Mehrtens #include <linux/pm_runtime.h>
2017f84b79SHauke Mehrtens #include <linux/spi/spi.h>
2117f84b79SHauke Mehrtens 
2217f84b79SHauke Mehrtens #ifdef CONFIG_LANTIQ
2317f84b79SHauke Mehrtens #include <lantiq_soc.h>
2417f84b79SHauke Mehrtens #endif
2517f84b79SHauke Mehrtens 
26ad2fca07SHauke Mehrtens #define LTQ_SPI_RX_IRQ_NAME	"spi_rx"
27ad2fca07SHauke Mehrtens #define LTQ_SPI_TX_IRQ_NAME	"spi_tx"
28ad2fca07SHauke Mehrtens #define LTQ_SPI_ERR_IRQ_NAME	"spi_err"
29ad2fca07SHauke Mehrtens #define LTQ_SPI_FRM_IRQ_NAME	"spi_frm"
3017f84b79SHauke Mehrtens 
31ad2fca07SHauke Mehrtens #define LTQ_SPI_CLC		0x00
32ad2fca07SHauke Mehrtens #define LTQ_SPI_PISEL		0x04
33ad2fca07SHauke Mehrtens #define LTQ_SPI_ID		0x08
34ad2fca07SHauke Mehrtens #define LTQ_SPI_CON		0x10
35ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT		0x14
36ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE	0x18
37ad2fca07SHauke Mehrtens #define LTQ_SPI_TB		0x20
38ad2fca07SHauke Mehrtens #define LTQ_SPI_RB		0x24
39ad2fca07SHauke Mehrtens #define LTQ_SPI_RXFCON		0x30
40ad2fca07SHauke Mehrtens #define LTQ_SPI_TXFCON		0x34
41ad2fca07SHauke Mehrtens #define LTQ_SPI_FSTAT		0x38
42ad2fca07SHauke Mehrtens #define LTQ_SPI_BRT		0x40
43ad2fca07SHauke Mehrtens #define LTQ_SPI_BRSTAT		0x44
44ad2fca07SHauke Mehrtens #define LTQ_SPI_SFCON		0x60
45ad2fca07SHauke Mehrtens #define LTQ_SPI_SFSTAT		0x64
46ad2fca07SHauke Mehrtens #define LTQ_SPI_GPOCON		0x70
47ad2fca07SHauke Mehrtens #define LTQ_SPI_GPOSTAT		0x74
48ad2fca07SHauke Mehrtens #define LTQ_SPI_FPGO		0x78
49ad2fca07SHauke Mehrtens #define LTQ_SPI_RXREQ		0x80
50ad2fca07SHauke Mehrtens #define LTQ_SPI_RXCNT		0x84
51ad2fca07SHauke Mehrtens #define LTQ_SPI_DMACON		0xec
52ad2fca07SHauke Mehrtens #define LTQ_SPI_IRNEN		0xf4
5317f84b79SHauke Mehrtens 
54ad2fca07SHauke Mehrtens #define LTQ_SPI_CLC_SMC_S	16	/* Clock divider for sleep mode */
55ad2fca07SHauke Mehrtens #define LTQ_SPI_CLC_SMC_M	(0xFF << LTQ_SPI_CLC_SMC_S)
56ad2fca07SHauke Mehrtens #define LTQ_SPI_CLC_RMC_S	8	/* Clock divider for normal run mode */
57ad2fca07SHauke Mehrtens #define LTQ_SPI_CLC_RMC_M	(0xFF << LTQ_SPI_CLC_RMC_S)
58ad2fca07SHauke Mehrtens #define LTQ_SPI_CLC_DISS	BIT(1)	/* Disable status bit */
59ad2fca07SHauke Mehrtens #define LTQ_SPI_CLC_DISR	BIT(0)	/* Disable request bit */
6017f84b79SHauke Mehrtens 
61ad2fca07SHauke Mehrtens #define LTQ_SPI_ID_TXFS_S	24	/* Implemented TX FIFO size */
62ad2fca07SHauke Mehrtens #define LTQ_SPI_ID_RXFS_S	16	/* Implemented RX FIFO size */
63ad2fca07SHauke Mehrtens #define LTQ_SPI_ID_MOD_S	8	/* Module ID */
64ad2fca07SHauke Mehrtens #define LTQ_SPI_ID_MOD_M	(0xff << LTQ_SPI_ID_MOD_S)
65ad2fca07SHauke Mehrtens #define LTQ_SPI_ID_CFG_S	5	/* DMA interface support */
66ad2fca07SHauke Mehrtens #define LTQ_SPI_ID_CFG_M	(1 << LTQ_SPI_ID_CFG_S)
67ad2fca07SHauke Mehrtens #define LTQ_SPI_ID_REV_M	0x1F	/* Hardware revision number */
6817f84b79SHauke Mehrtens 
69ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_BM_S	16	/* Data width selection */
70ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_BM_M	(0x1F << LTQ_SPI_CON_BM_S)
71ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_EM		BIT(24)	/* Echo mode */
72ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_IDLE	BIT(23)	/* Idle bit value */
73ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_ENBV	BIT(22)	/* Enable byte valid control */
74ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_RUEN	BIT(12)	/* Receive underflow error enable */
75ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_TUEN	BIT(11)	/* Transmit underflow error enable */
76ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_AEN		BIT(10)	/* Abort error enable */
77ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_REN		BIT(9)	/* Receive overflow error enable */
78ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_TEN		BIT(8)	/* Transmit overflow error enable */
79ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_LB		BIT(7)	/* Loopback control */
80ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_PO		BIT(6)	/* Clock polarity control */
81ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_PH		BIT(5)	/* Clock phase control */
82ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_HB		BIT(4)	/* Heading control */
83ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_RXOFF	BIT(1)	/* Switch receiver off */
84ad2fca07SHauke Mehrtens #define LTQ_SPI_CON_TXOFF	BIT(0)	/* Switch transmitter off */
8517f84b79SHauke Mehrtens 
86ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_RXBV_S	28
87ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_RXBV_M	(0x7 << LTQ_SPI_STAT_RXBV_S)
88ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_BSY	BIT(13)	/* Busy flag */
89ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_RUE	BIT(12)	/* Receive underflow error flag */
90ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_TUE	BIT(11)	/* Transmit underflow error flag */
91ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_AE		BIT(10)	/* Abort error flag */
92ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_RE		BIT(9)	/* Receive error flag */
93ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_TE		BIT(8)	/* Transmit error flag */
94ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_ME		BIT(7)	/* Mode error flag */
95*ea11a8bbSYang Yingliang #define LTQ_SPI_STAT_MS		BIT(1)	/* Host/target select bit */
96ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_EN		BIT(0)	/* Enable bit */
97ad2fca07SHauke Mehrtens #define LTQ_SPI_STAT_ERRORS	(LTQ_SPI_STAT_ME | LTQ_SPI_STAT_TE | \
98ad2fca07SHauke Mehrtens 				 LTQ_SPI_STAT_RE | LTQ_SPI_STAT_AE | \
99ad2fca07SHauke Mehrtens 				 LTQ_SPI_STAT_TUE | LTQ_SPI_STAT_RUE)
10017f84b79SHauke Mehrtens 
101ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_SETTUE	BIT(15)	/* Set transmit underflow error flag */
102ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_SETAE	BIT(14)	/* Set abort error flag */
103ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_SETRE	BIT(13)	/* Set receive error flag */
104ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_SETTE	BIT(12)	/* Set transmit error flag */
105ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_CLRTUE	BIT(11)	/* Clear transmit underflow error flag */
106ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_CLRAE	BIT(10)	/* Clear abort error flag */
107ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_CLRRE	BIT(9)	/* Clear receive error flag */
108ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_CLRTE	BIT(8)	/* Clear transmit error flag */
109ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_SETME	BIT(7)	/* Set mode error flag */
110ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_CLRME	BIT(6)	/* Clear mode error flag */
111ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_SETRUE	BIT(5)	/* Set receive underflow error flag */
112ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_CLRRUE	BIT(4)	/* Clear receive underflow error flag */
113*ea11a8bbSYang Yingliang #define LTQ_SPI_WHBSTATE_SETMS	BIT(3)	/* Set host select bit */
114*ea11a8bbSYang Yingliang #define LTQ_SPI_WHBSTATE_CLRMS	BIT(2)	/* Clear host select bit */
115ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_SETEN	BIT(1)	/* Set enable bit (operational mode) */
116ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_CLREN	BIT(0)	/* Clear enable bit (config mode */
117ad2fca07SHauke Mehrtens #define LTQ_SPI_WHBSTATE_CLR_ERRORS	(LTQ_SPI_WHBSTATE_CLRRUE | \
118ad2fca07SHauke Mehrtens 					 LTQ_SPI_WHBSTATE_CLRME | \
119ad2fca07SHauke Mehrtens 					 LTQ_SPI_WHBSTATE_CLRTE | \
120ad2fca07SHauke Mehrtens 					 LTQ_SPI_WHBSTATE_CLRRE | \
121ad2fca07SHauke Mehrtens 					 LTQ_SPI_WHBSTATE_CLRAE | \
122ad2fca07SHauke Mehrtens 					 LTQ_SPI_WHBSTATE_CLRTUE)
12317f84b79SHauke Mehrtens 
124ad2fca07SHauke Mehrtens #define LTQ_SPI_RXFCON_RXFITL_S	8	/* FIFO interrupt trigger level */
125ad2fca07SHauke Mehrtens #define LTQ_SPI_RXFCON_RXFLU	BIT(1)	/* FIFO flush */
126ad2fca07SHauke Mehrtens #define LTQ_SPI_RXFCON_RXFEN	BIT(0)	/* FIFO enable */
12717f84b79SHauke Mehrtens 
128ad2fca07SHauke Mehrtens #define LTQ_SPI_TXFCON_TXFITL_S	8	/* FIFO interrupt trigger level */
129ad2fca07SHauke Mehrtens #define LTQ_SPI_TXFCON_TXFLU	BIT(1)	/* FIFO flush */
130ad2fca07SHauke Mehrtens #define LTQ_SPI_TXFCON_TXFEN	BIT(0)	/* FIFO enable */
13117f84b79SHauke Mehrtens 
132ad2fca07SHauke Mehrtens #define LTQ_SPI_FSTAT_RXFFL_S	0
133ad2fca07SHauke Mehrtens #define LTQ_SPI_FSTAT_TXFFL_S	8
13417f84b79SHauke Mehrtens 
135ad2fca07SHauke Mehrtens #define LTQ_SPI_GPOCON_ISCSBN_S	8
136ad2fca07SHauke Mehrtens #define LTQ_SPI_GPOCON_INVOUTN_S	0
13717f84b79SHauke Mehrtens 
138ad2fca07SHauke Mehrtens #define LTQ_SPI_FGPO_SETOUTN_S	8
139ad2fca07SHauke Mehrtens #define LTQ_SPI_FGPO_CLROUTN_S	0
14017f84b79SHauke Mehrtens 
141ad2fca07SHauke Mehrtens #define LTQ_SPI_RXREQ_RXCNT_M	0xFFFF	/* Receive count value */
142ad2fca07SHauke Mehrtens #define LTQ_SPI_RXCNT_TODO_M	0xFFFF	/* Recevie to-do value */
14317f84b79SHauke Mehrtens 
144ad2fca07SHauke Mehrtens #define LTQ_SPI_IRNEN_TFI	BIT(4)	/* TX finished interrupt */
145ad2fca07SHauke Mehrtens #define LTQ_SPI_IRNEN_F		BIT(3)	/* Frame end interrupt request */
146ad2fca07SHauke Mehrtens #define LTQ_SPI_IRNEN_E		BIT(2)	/* Error end interrupt request */
147ad2fca07SHauke Mehrtens #define LTQ_SPI_IRNEN_T_XWAY	BIT(1)	/* Transmit end interrupt request */
148ad2fca07SHauke Mehrtens #define LTQ_SPI_IRNEN_R_XWAY	BIT(0)	/* Receive end interrupt request */
149ad2fca07SHauke Mehrtens #define LTQ_SPI_IRNEN_R_XRX	BIT(1)	/* Transmit end interrupt request */
150ad2fca07SHauke Mehrtens #define LTQ_SPI_IRNEN_T_XRX	BIT(0)	/* Receive end interrupt request */
151ad2fca07SHauke Mehrtens #define LTQ_SPI_IRNEN_ALL	0x1F
15217f84b79SHauke Mehrtens 
153744cd0f2SDilip Kota struct lantiq_ssc_spi;
154744cd0f2SDilip Kota 
15517f84b79SHauke Mehrtens struct lantiq_ssc_hwcfg {
156744cd0f2SDilip Kota 	int (*cfg_irq)(struct platform_device *pdev, struct lantiq_ssc_spi *spi);
15717f84b79SHauke Mehrtens 	unsigned int	irnen_r;
15817f84b79SHauke Mehrtens 	unsigned int	irnen_t;
1598d19d665SDilip Kota 	unsigned int	irncr;
1608d19d665SDilip Kota 	unsigned int	irnicr;
16194eca904SDilip Kota 	bool		irq_ack;
1628743d215SDilip Kota 	u32		fifo_size_mask;
16317f84b79SHauke Mehrtens };
16417f84b79SHauke Mehrtens 
16517f84b79SHauke Mehrtens struct lantiq_ssc_spi {
166*ea11a8bbSYang Yingliang 	struct spi_controller		*host;
16717f84b79SHauke Mehrtens 	struct device			*dev;
16817f84b79SHauke Mehrtens 	void __iomem			*regbase;
16917f84b79SHauke Mehrtens 	struct clk			*spi_clk;
17017f84b79SHauke Mehrtens 	struct clk			*fpi_clk;
17117f84b79SHauke Mehrtens 	const struct lantiq_ssc_hwcfg	*hwcfg;
17217f84b79SHauke Mehrtens 
17317f84b79SHauke Mehrtens 	spinlock_t			lock;
17417f84b79SHauke Mehrtens 	struct workqueue_struct		*wq;
17517f84b79SHauke Mehrtens 	struct work_struct		work;
17617f84b79SHauke Mehrtens 
17717f84b79SHauke Mehrtens 	const u8			*tx;
17817f84b79SHauke Mehrtens 	u8				*rx;
17917f84b79SHauke Mehrtens 	unsigned int			tx_todo;
18017f84b79SHauke Mehrtens 	unsigned int			rx_todo;
18117f84b79SHauke Mehrtens 	unsigned int			bits_per_word;
18217f84b79SHauke Mehrtens 	unsigned int			speed_hz;
18317f84b79SHauke Mehrtens 	unsigned int			tx_fifo_size;
18417f84b79SHauke Mehrtens 	unsigned int			rx_fifo_size;
18517f84b79SHauke Mehrtens 	unsigned int			base_cs;
186661ccf2bSDilip Kota 	unsigned int			fdx_tx_level;
18717f84b79SHauke Mehrtens };
18817f84b79SHauke Mehrtens 
lantiq_ssc_readl(const struct lantiq_ssc_spi * spi,u32 reg)18917f84b79SHauke Mehrtens static u32 lantiq_ssc_readl(const struct lantiq_ssc_spi *spi, u32 reg)
19017f84b79SHauke Mehrtens {
19117f84b79SHauke Mehrtens 	return __raw_readl(spi->regbase + reg);
19217f84b79SHauke Mehrtens }
19317f84b79SHauke Mehrtens 
lantiq_ssc_writel(const struct lantiq_ssc_spi * spi,u32 val,u32 reg)19417f84b79SHauke Mehrtens static void lantiq_ssc_writel(const struct lantiq_ssc_spi *spi, u32 val,
19517f84b79SHauke Mehrtens 			      u32 reg)
19617f84b79SHauke Mehrtens {
19717f84b79SHauke Mehrtens 	__raw_writel(val, spi->regbase + reg);
19817f84b79SHauke Mehrtens }
19917f84b79SHauke Mehrtens 
lantiq_ssc_maskl(const struct lantiq_ssc_spi * spi,u32 clr,u32 set,u32 reg)20017f84b79SHauke Mehrtens static void lantiq_ssc_maskl(const struct lantiq_ssc_spi *spi, u32 clr,
20117f84b79SHauke Mehrtens 			     u32 set, u32 reg)
20217f84b79SHauke Mehrtens {
20317f84b79SHauke Mehrtens 	u32 val = __raw_readl(spi->regbase + reg);
20417f84b79SHauke Mehrtens 
20517f84b79SHauke Mehrtens 	val &= ~clr;
20617f84b79SHauke Mehrtens 	val |= set;
20717f84b79SHauke Mehrtens 	__raw_writel(val, spi->regbase + reg);
20817f84b79SHauke Mehrtens }
20917f84b79SHauke Mehrtens 
tx_fifo_level(const struct lantiq_ssc_spi * spi)21017f84b79SHauke Mehrtens static unsigned int tx_fifo_level(const struct lantiq_ssc_spi *spi)
21117f84b79SHauke Mehrtens {
2128743d215SDilip Kota 	const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
213ad2fca07SHauke Mehrtens 	u32 fstat = lantiq_ssc_readl(spi, LTQ_SPI_FSTAT);
21417f84b79SHauke Mehrtens 
2158743d215SDilip Kota 	return (fstat >> LTQ_SPI_FSTAT_TXFFL_S) & hwcfg->fifo_size_mask;
21617f84b79SHauke Mehrtens }
21717f84b79SHauke Mehrtens 
rx_fifo_level(const struct lantiq_ssc_spi * spi)21817f84b79SHauke Mehrtens static unsigned int rx_fifo_level(const struct lantiq_ssc_spi *spi)
21917f84b79SHauke Mehrtens {
2208743d215SDilip Kota 	const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
221ad2fca07SHauke Mehrtens 	u32 fstat = lantiq_ssc_readl(spi, LTQ_SPI_FSTAT);
22217f84b79SHauke Mehrtens 
2238743d215SDilip Kota 	return (fstat >> LTQ_SPI_FSTAT_RXFFL_S) & hwcfg->fifo_size_mask;
22417f84b79SHauke Mehrtens }
22517f84b79SHauke Mehrtens 
tx_fifo_free(const struct lantiq_ssc_spi * spi)22617f84b79SHauke Mehrtens static unsigned int tx_fifo_free(const struct lantiq_ssc_spi *spi)
22717f84b79SHauke Mehrtens {
22817f84b79SHauke Mehrtens 	return spi->tx_fifo_size - tx_fifo_level(spi);
22917f84b79SHauke Mehrtens }
23017f84b79SHauke Mehrtens 
rx_fifo_reset(const struct lantiq_ssc_spi * spi)23117f84b79SHauke Mehrtens static void rx_fifo_reset(const struct lantiq_ssc_spi *spi)
23217f84b79SHauke Mehrtens {
233ad2fca07SHauke Mehrtens 	u32 val = spi->rx_fifo_size << LTQ_SPI_RXFCON_RXFITL_S;
23417f84b79SHauke Mehrtens 
235ad2fca07SHauke Mehrtens 	val |= LTQ_SPI_RXFCON_RXFEN | LTQ_SPI_RXFCON_RXFLU;
236ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, val, LTQ_SPI_RXFCON);
23717f84b79SHauke Mehrtens }
23817f84b79SHauke Mehrtens 
tx_fifo_reset(const struct lantiq_ssc_spi * spi)23917f84b79SHauke Mehrtens static void tx_fifo_reset(const struct lantiq_ssc_spi *spi)
24017f84b79SHauke Mehrtens {
241ad2fca07SHauke Mehrtens 	u32 val = 1 << LTQ_SPI_TXFCON_TXFITL_S;
24217f84b79SHauke Mehrtens 
243ad2fca07SHauke Mehrtens 	val |= LTQ_SPI_TXFCON_TXFEN | LTQ_SPI_TXFCON_TXFLU;
244ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, val, LTQ_SPI_TXFCON);
24517f84b79SHauke Mehrtens }
24617f84b79SHauke Mehrtens 
rx_fifo_flush(const struct lantiq_ssc_spi * spi)24717f84b79SHauke Mehrtens static void rx_fifo_flush(const struct lantiq_ssc_spi *spi)
24817f84b79SHauke Mehrtens {
249ad2fca07SHauke Mehrtens 	lantiq_ssc_maskl(spi, 0, LTQ_SPI_RXFCON_RXFLU, LTQ_SPI_RXFCON);
25017f84b79SHauke Mehrtens }
25117f84b79SHauke Mehrtens 
tx_fifo_flush(const struct lantiq_ssc_spi * spi)25217f84b79SHauke Mehrtens static void tx_fifo_flush(const struct lantiq_ssc_spi *spi)
25317f84b79SHauke Mehrtens {
254ad2fca07SHauke Mehrtens 	lantiq_ssc_maskl(spi, 0, LTQ_SPI_TXFCON_TXFLU, LTQ_SPI_TXFCON);
25517f84b79SHauke Mehrtens }
25617f84b79SHauke Mehrtens 
hw_enter_config_mode(const struct lantiq_ssc_spi * spi)25717f84b79SHauke Mehrtens static void hw_enter_config_mode(const struct lantiq_ssc_spi *spi)
25817f84b79SHauke Mehrtens {
259ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, LTQ_SPI_WHBSTATE_CLREN, LTQ_SPI_WHBSTATE);
26017f84b79SHauke Mehrtens }
26117f84b79SHauke Mehrtens 
hw_enter_active_mode(const struct lantiq_ssc_spi * spi)26217f84b79SHauke Mehrtens static void hw_enter_active_mode(const struct lantiq_ssc_spi *spi)
26317f84b79SHauke Mehrtens {
264ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, LTQ_SPI_WHBSTATE_SETEN, LTQ_SPI_WHBSTATE);
26517f84b79SHauke Mehrtens }
26617f84b79SHauke Mehrtens 
hw_setup_speed_hz(const struct lantiq_ssc_spi * spi,unsigned int max_speed_hz)26717f84b79SHauke Mehrtens static void hw_setup_speed_hz(const struct lantiq_ssc_spi *spi,
26817f84b79SHauke Mehrtens 			      unsigned int max_speed_hz)
26917f84b79SHauke Mehrtens {
27017f84b79SHauke Mehrtens 	u32 spi_clk, brt;
27117f84b79SHauke Mehrtens 
27217f84b79SHauke Mehrtens 	/*
27317f84b79SHauke Mehrtens 	 * SPI module clock is derived from FPI bus clock dependent on
27417f84b79SHauke Mehrtens 	 * divider value in CLC.RMS which is always set to 1.
27517f84b79SHauke Mehrtens 	 *
27617f84b79SHauke Mehrtens 	 *                 f_SPI
27717f84b79SHauke Mehrtens 	 * baudrate = --------------
27817f84b79SHauke Mehrtens 	 *             2 * (BR + 1)
27917f84b79SHauke Mehrtens 	 */
28017f84b79SHauke Mehrtens 	spi_clk = clk_get_rate(spi->fpi_clk) / 2;
28117f84b79SHauke Mehrtens 
28217f84b79SHauke Mehrtens 	if (max_speed_hz > spi_clk)
28317f84b79SHauke Mehrtens 		brt = 0;
28417f84b79SHauke Mehrtens 	else
28517f84b79SHauke Mehrtens 		brt = spi_clk / max_speed_hz - 1;
28617f84b79SHauke Mehrtens 
28717f84b79SHauke Mehrtens 	if (brt > 0xFFFF)
28817f84b79SHauke Mehrtens 		brt = 0xFFFF;
28917f84b79SHauke Mehrtens 
29017f84b79SHauke Mehrtens 	dev_dbg(spi->dev, "spi_clk %u, max_speed_hz %u, brt %u\n",
29117f84b79SHauke Mehrtens 		spi_clk, max_speed_hz, brt);
29217f84b79SHauke Mehrtens 
293ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, brt, LTQ_SPI_BRT);
29417f84b79SHauke Mehrtens }
29517f84b79SHauke Mehrtens 
hw_setup_bits_per_word(const struct lantiq_ssc_spi * spi,unsigned int bits_per_word)29617f84b79SHauke Mehrtens static void hw_setup_bits_per_word(const struct lantiq_ssc_spi *spi,
29717f84b79SHauke Mehrtens 				   unsigned int bits_per_word)
29817f84b79SHauke Mehrtens {
29917f84b79SHauke Mehrtens 	u32 bm;
30017f84b79SHauke Mehrtens 
30117f84b79SHauke Mehrtens 	/* CON.BM value = bits_per_word - 1 */
302ad2fca07SHauke Mehrtens 	bm = (bits_per_word - 1) << LTQ_SPI_CON_BM_S;
30317f84b79SHauke Mehrtens 
304ad2fca07SHauke Mehrtens 	lantiq_ssc_maskl(spi, LTQ_SPI_CON_BM_M, bm, LTQ_SPI_CON);
30517f84b79SHauke Mehrtens }
30617f84b79SHauke Mehrtens 
hw_setup_clock_mode(const struct lantiq_ssc_spi * spi,unsigned int mode)30717f84b79SHauke Mehrtens static void hw_setup_clock_mode(const struct lantiq_ssc_spi *spi,
30817f84b79SHauke Mehrtens 				unsigned int mode)
30917f84b79SHauke Mehrtens {
31017f84b79SHauke Mehrtens 	u32 con_set = 0, con_clr = 0;
31117f84b79SHauke Mehrtens 
31217f84b79SHauke Mehrtens 	/*
31317f84b79SHauke Mehrtens 	 * SPI mode mapping in CON register:
31417f84b79SHauke Mehrtens 	 * Mode CPOL CPHA CON.PO CON.PH
31517f84b79SHauke Mehrtens 	 *  0    0    0      0      1
31617f84b79SHauke Mehrtens 	 *  1    0    1      0      0
31717f84b79SHauke Mehrtens 	 *  2    1    0      1      1
31817f84b79SHauke Mehrtens 	 *  3    1    1      1      0
31917f84b79SHauke Mehrtens 	 */
32017f84b79SHauke Mehrtens 	if (mode & SPI_CPHA)
321ad2fca07SHauke Mehrtens 		con_clr |= LTQ_SPI_CON_PH;
32217f84b79SHauke Mehrtens 	else
323ad2fca07SHauke Mehrtens 		con_set |= LTQ_SPI_CON_PH;
32417f84b79SHauke Mehrtens 
32517f84b79SHauke Mehrtens 	if (mode & SPI_CPOL)
326ad2fca07SHauke Mehrtens 		con_set |= LTQ_SPI_CON_PO | LTQ_SPI_CON_IDLE;
32717f84b79SHauke Mehrtens 	else
328ad2fca07SHauke Mehrtens 		con_clr |= LTQ_SPI_CON_PO | LTQ_SPI_CON_IDLE;
32917f84b79SHauke Mehrtens 
33017f84b79SHauke Mehrtens 	/* Set heading control */
33117f84b79SHauke Mehrtens 	if (mode & SPI_LSB_FIRST)
332ad2fca07SHauke Mehrtens 		con_clr |= LTQ_SPI_CON_HB;
33317f84b79SHauke Mehrtens 	else
334ad2fca07SHauke Mehrtens 		con_set |= LTQ_SPI_CON_HB;
33517f84b79SHauke Mehrtens 
33617f84b79SHauke Mehrtens 	/* Set loopback mode */
33717f84b79SHauke Mehrtens 	if (mode & SPI_LOOP)
338ad2fca07SHauke Mehrtens 		con_set |= LTQ_SPI_CON_LB;
33917f84b79SHauke Mehrtens 	else
340ad2fca07SHauke Mehrtens 		con_clr |= LTQ_SPI_CON_LB;
34117f84b79SHauke Mehrtens 
342ad2fca07SHauke Mehrtens 	lantiq_ssc_maskl(spi, con_clr, con_set, LTQ_SPI_CON);
34317f84b79SHauke Mehrtens }
34417f84b79SHauke Mehrtens 
lantiq_ssc_hw_init(const struct lantiq_ssc_spi * spi)34517f84b79SHauke Mehrtens static void lantiq_ssc_hw_init(const struct lantiq_ssc_spi *spi)
34617f84b79SHauke Mehrtens {
34717f84b79SHauke Mehrtens 	const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
34817f84b79SHauke Mehrtens 
34917f84b79SHauke Mehrtens 	/*
35017f84b79SHauke Mehrtens 	 * Set clock divider for run mode to 1 to
35117f84b79SHauke Mehrtens 	 * run at same frequency as FPI bus
35217f84b79SHauke Mehrtens 	 */
353ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, 1 << LTQ_SPI_CLC_RMC_S, LTQ_SPI_CLC);
35417f84b79SHauke Mehrtens 
35517f84b79SHauke Mehrtens 	/* Put controller into config mode */
35617f84b79SHauke Mehrtens 	hw_enter_config_mode(spi);
35717f84b79SHauke Mehrtens 
35817f84b79SHauke Mehrtens 	/* Clear error flags */
359ad2fca07SHauke Mehrtens 	lantiq_ssc_maskl(spi, 0, LTQ_SPI_WHBSTATE_CLR_ERRORS, LTQ_SPI_WHBSTATE);
36017f84b79SHauke Mehrtens 
36117f84b79SHauke Mehrtens 	/* Enable error checking, disable TX/RX */
362ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, LTQ_SPI_CON_RUEN | LTQ_SPI_CON_AEN |
363ad2fca07SHauke Mehrtens 		LTQ_SPI_CON_TEN | LTQ_SPI_CON_REN | LTQ_SPI_CON_TXOFF |
364ad2fca07SHauke Mehrtens 		LTQ_SPI_CON_RXOFF, LTQ_SPI_CON);
36517f84b79SHauke Mehrtens 
36617f84b79SHauke Mehrtens 	/* Setup default SPI mode */
36717f84b79SHauke Mehrtens 	hw_setup_bits_per_word(spi, spi->bits_per_word);
36817f84b79SHauke Mehrtens 	hw_setup_clock_mode(spi, SPI_MODE_0);
36917f84b79SHauke Mehrtens 
370*ea11a8bbSYang Yingliang 	/* Enable host mode and clear error flags */
371ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, LTQ_SPI_WHBSTATE_SETMS |
372ad2fca07SHauke Mehrtens 			       LTQ_SPI_WHBSTATE_CLR_ERRORS,
373ad2fca07SHauke Mehrtens 			       LTQ_SPI_WHBSTATE);
37417f84b79SHauke Mehrtens 
37517f84b79SHauke Mehrtens 	/* Reset GPIO/CS registers */
376ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, 0, LTQ_SPI_GPOCON);
377ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, 0xFF00, LTQ_SPI_FPGO);
37817f84b79SHauke Mehrtens 
37917f84b79SHauke Mehrtens 	/* Enable and flush FIFOs */
38017f84b79SHauke Mehrtens 	rx_fifo_reset(spi);
38117f84b79SHauke Mehrtens 	tx_fifo_reset(spi);
38217f84b79SHauke Mehrtens 
38317f84b79SHauke Mehrtens 	/* Enable interrupts */
384ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, hwcfg->irnen_t | hwcfg->irnen_r |
385ad2fca07SHauke Mehrtens 			  LTQ_SPI_IRNEN_E, LTQ_SPI_IRNEN);
38617f84b79SHauke Mehrtens }
38717f84b79SHauke Mehrtens 
lantiq_ssc_setup(struct spi_device * spidev)38817f84b79SHauke Mehrtens static int lantiq_ssc_setup(struct spi_device *spidev)
38917f84b79SHauke Mehrtens {
390*ea11a8bbSYang Yingliang 	struct spi_controller *host = spidev->controller;
391*ea11a8bbSYang Yingliang 	struct lantiq_ssc_spi *spi = spi_controller_get_devdata(host);
3929e264f3fSAmit Kumar Mahapatra via Alsa-devel 	unsigned int cs = spi_get_chipselect(spidev, 0);
39317f84b79SHauke Mehrtens 	u32 gpocon;
39417f84b79SHauke Mehrtens 
39517f84b79SHauke Mehrtens 	/* GPIOs are used for CS */
3969e264f3fSAmit Kumar Mahapatra via Alsa-devel 	if (spi_get_csgpiod(spidev, 0))
39717f84b79SHauke Mehrtens 		return 0;
39817f84b79SHauke Mehrtens 
39917f84b79SHauke Mehrtens 	dev_dbg(spi->dev, "using internal chipselect %u\n", cs);
40017f84b79SHauke Mehrtens 
40117f84b79SHauke Mehrtens 	if (cs < spi->base_cs) {
40217f84b79SHauke Mehrtens 		dev_err(spi->dev,
40317f84b79SHauke Mehrtens 			"chipselect %i too small (min %i)\n", cs, spi->base_cs);
40417f84b79SHauke Mehrtens 		return -EINVAL;
40517f84b79SHauke Mehrtens 	}
40617f84b79SHauke Mehrtens 
40717f84b79SHauke Mehrtens 	/* set GPO pin to CS mode */
408ad2fca07SHauke Mehrtens 	gpocon = 1 << ((cs - spi->base_cs) + LTQ_SPI_GPOCON_ISCSBN_S);
40917f84b79SHauke Mehrtens 
41017f84b79SHauke Mehrtens 	/* invert GPO pin */
41117f84b79SHauke Mehrtens 	if (spidev->mode & SPI_CS_HIGH)
41217f84b79SHauke Mehrtens 		gpocon |= 1 << (cs - spi->base_cs);
41317f84b79SHauke Mehrtens 
414ad2fca07SHauke Mehrtens 	lantiq_ssc_maskl(spi, 0, gpocon, LTQ_SPI_GPOCON);
41517f84b79SHauke Mehrtens 
41617f84b79SHauke Mehrtens 	return 0;
41717f84b79SHauke Mehrtens }
41817f84b79SHauke Mehrtens 
lantiq_ssc_prepare_message(struct spi_controller * host,struct spi_message * message)419*ea11a8bbSYang Yingliang static int lantiq_ssc_prepare_message(struct spi_controller *host,
42017f84b79SHauke Mehrtens 				      struct spi_message *message)
42117f84b79SHauke Mehrtens {
422*ea11a8bbSYang Yingliang 	struct lantiq_ssc_spi *spi = spi_controller_get_devdata(host);
42317f84b79SHauke Mehrtens 
42417f84b79SHauke Mehrtens 	hw_enter_config_mode(spi);
42517f84b79SHauke Mehrtens 	hw_setup_clock_mode(spi, message->spi->mode);
42617f84b79SHauke Mehrtens 	hw_enter_active_mode(spi);
42717f84b79SHauke Mehrtens 
42817f84b79SHauke Mehrtens 	return 0;
42917f84b79SHauke Mehrtens }
43017f84b79SHauke Mehrtens 
hw_setup_transfer(struct lantiq_ssc_spi * spi,struct spi_device * spidev,struct spi_transfer * t)43117f84b79SHauke Mehrtens static void hw_setup_transfer(struct lantiq_ssc_spi *spi,
43217f84b79SHauke Mehrtens 			      struct spi_device *spidev, struct spi_transfer *t)
43317f84b79SHauke Mehrtens {
43417f84b79SHauke Mehrtens 	unsigned int speed_hz = t->speed_hz;
43517f84b79SHauke Mehrtens 	unsigned int bits_per_word = t->bits_per_word;
43617f84b79SHauke Mehrtens 	u32 con;
43717f84b79SHauke Mehrtens 
43817f84b79SHauke Mehrtens 	if (bits_per_word != spi->bits_per_word ||
43917f84b79SHauke Mehrtens 		speed_hz != spi->speed_hz) {
44017f84b79SHauke Mehrtens 		hw_enter_config_mode(spi);
44117f84b79SHauke Mehrtens 		hw_setup_speed_hz(spi, speed_hz);
44217f84b79SHauke Mehrtens 		hw_setup_bits_per_word(spi, bits_per_word);
44317f84b79SHauke Mehrtens 		hw_enter_active_mode(spi);
44417f84b79SHauke Mehrtens 
44517f84b79SHauke Mehrtens 		spi->speed_hz = speed_hz;
44617f84b79SHauke Mehrtens 		spi->bits_per_word = bits_per_word;
44717f84b79SHauke Mehrtens 	}
44817f84b79SHauke Mehrtens 
44917f84b79SHauke Mehrtens 	/* Configure transmitter and receiver */
450ad2fca07SHauke Mehrtens 	con = lantiq_ssc_readl(spi, LTQ_SPI_CON);
45117f84b79SHauke Mehrtens 	if (t->tx_buf)
452ad2fca07SHauke Mehrtens 		con &= ~LTQ_SPI_CON_TXOFF;
45317f84b79SHauke Mehrtens 	else
454ad2fca07SHauke Mehrtens 		con |= LTQ_SPI_CON_TXOFF;
45517f84b79SHauke Mehrtens 
45617f84b79SHauke Mehrtens 	if (t->rx_buf)
457ad2fca07SHauke Mehrtens 		con &= ~LTQ_SPI_CON_RXOFF;
45817f84b79SHauke Mehrtens 	else
459ad2fca07SHauke Mehrtens 		con |= LTQ_SPI_CON_RXOFF;
46017f84b79SHauke Mehrtens 
461ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, con, LTQ_SPI_CON);
46217f84b79SHauke Mehrtens }
46317f84b79SHauke Mehrtens 
lantiq_ssc_unprepare_message(struct spi_controller * host,struct spi_message * message)464*ea11a8bbSYang Yingliang static int lantiq_ssc_unprepare_message(struct spi_controller *host,
46517f84b79SHauke Mehrtens 					struct spi_message *message)
46617f84b79SHauke Mehrtens {
467*ea11a8bbSYang Yingliang 	struct lantiq_ssc_spi *spi = spi_controller_get_devdata(host);
46817f84b79SHauke Mehrtens 
46917f84b79SHauke Mehrtens 	flush_workqueue(spi->wq);
47017f84b79SHauke Mehrtens 
47117f84b79SHauke Mehrtens 	/* Disable transmitter and receiver while idle */
472ad2fca07SHauke Mehrtens 	lantiq_ssc_maskl(spi, 0, LTQ_SPI_CON_TXOFF | LTQ_SPI_CON_RXOFF,
473ad2fca07SHauke Mehrtens 			 LTQ_SPI_CON);
47417f84b79SHauke Mehrtens 
47517f84b79SHauke Mehrtens 	return 0;
47617f84b79SHauke Mehrtens }
47717f84b79SHauke Mehrtens 
tx_fifo_write(struct lantiq_ssc_spi * spi)47817f84b79SHauke Mehrtens static void tx_fifo_write(struct lantiq_ssc_spi *spi)
47917f84b79SHauke Mehrtens {
48017f84b79SHauke Mehrtens 	const u8 *tx8;
48117f84b79SHauke Mehrtens 	const u16 *tx16;
48217f84b79SHauke Mehrtens 	const u32 *tx32;
48317f84b79SHauke Mehrtens 	u32 data;
48417f84b79SHauke Mehrtens 	unsigned int tx_free = tx_fifo_free(spi);
48517f84b79SHauke Mehrtens 
486661ccf2bSDilip Kota 	spi->fdx_tx_level = 0;
48717f84b79SHauke Mehrtens 	while (spi->tx_todo && tx_free) {
48817f84b79SHauke Mehrtens 		switch (spi->bits_per_word) {
48917f84b79SHauke Mehrtens 		case 2 ... 8:
49017f84b79SHauke Mehrtens 			tx8 = spi->tx;
49117f84b79SHauke Mehrtens 			data = *tx8;
49217f84b79SHauke Mehrtens 			spi->tx_todo--;
49317f84b79SHauke Mehrtens 			spi->tx++;
49417f84b79SHauke Mehrtens 			break;
49517f84b79SHauke Mehrtens 		case 16:
49617f84b79SHauke Mehrtens 			tx16 = (u16 *) spi->tx;
49717f84b79SHauke Mehrtens 			data = *tx16;
49817f84b79SHauke Mehrtens 			spi->tx_todo -= 2;
49917f84b79SHauke Mehrtens 			spi->tx += 2;
50017f84b79SHauke Mehrtens 			break;
50117f84b79SHauke Mehrtens 		case 32:
50217f84b79SHauke Mehrtens 			tx32 = (u32 *) spi->tx;
50317f84b79SHauke Mehrtens 			data = *tx32;
50417f84b79SHauke Mehrtens 			spi->tx_todo -= 4;
50517f84b79SHauke Mehrtens 			spi->tx += 4;
50617f84b79SHauke Mehrtens 			break;
50717f84b79SHauke Mehrtens 		default:
50817f84b79SHauke Mehrtens 			WARN_ON(1);
50917f84b79SHauke Mehrtens 			data = 0;
51017f84b79SHauke Mehrtens 			break;
51117f84b79SHauke Mehrtens 		}
51217f84b79SHauke Mehrtens 
513ad2fca07SHauke Mehrtens 		lantiq_ssc_writel(spi, data, LTQ_SPI_TB);
51417f84b79SHauke Mehrtens 		tx_free--;
515661ccf2bSDilip Kota 		spi->fdx_tx_level++;
51617f84b79SHauke Mehrtens 	}
51717f84b79SHauke Mehrtens }
51817f84b79SHauke Mehrtens 
rx_fifo_read_full_duplex(struct lantiq_ssc_spi * spi)51917f84b79SHauke Mehrtens static void rx_fifo_read_full_duplex(struct lantiq_ssc_spi *spi)
52017f84b79SHauke Mehrtens {
52117f84b79SHauke Mehrtens 	u8 *rx8;
52217f84b79SHauke Mehrtens 	u16 *rx16;
52317f84b79SHauke Mehrtens 	u32 *rx32;
52417f84b79SHauke Mehrtens 	u32 data;
52517f84b79SHauke Mehrtens 	unsigned int rx_fill = rx_fifo_level(spi);
52617f84b79SHauke Mehrtens 
527661ccf2bSDilip Kota 	/*
528661ccf2bSDilip Kota 	 * Wait until all expected data to be shifted in.
529661ccf2bSDilip Kota 	 * Otherwise, rx overrun may occur.
530661ccf2bSDilip Kota 	 */
531661ccf2bSDilip Kota 	while (rx_fill != spi->fdx_tx_level)
532661ccf2bSDilip Kota 		rx_fill = rx_fifo_level(spi);
533661ccf2bSDilip Kota 
53417f84b79SHauke Mehrtens 	while (rx_fill) {
535ad2fca07SHauke Mehrtens 		data = lantiq_ssc_readl(spi, LTQ_SPI_RB);
53617f84b79SHauke Mehrtens 
53717f84b79SHauke Mehrtens 		switch (spi->bits_per_word) {
53817f84b79SHauke Mehrtens 		case 2 ... 8:
53917f84b79SHauke Mehrtens 			rx8 = spi->rx;
54017f84b79SHauke Mehrtens 			*rx8 = data;
54117f84b79SHauke Mehrtens 			spi->rx_todo--;
54217f84b79SHauke Mehrtens 			spi->rx++;
54317f84b79SHauke Mehrtens 			break;
54417f84b79SHauke Mehrtens 		case 16:
54517f84b79SHauke Mehrtens 			rx16 = (u16 *) spi->rx;
54617f84b79SHauke Mehrtens 			*rx16 = data;
54717f84b79SHauke Mehrtens 			spi->rx_todo -= 2;
54817f84b79SHauke Mehrtens 			spi->rx += 2;
54917f84b79SHauke Mehrtens 			break;
55017f84b79SHauke Mehrtens 		case 32:
55117f84b79SHauke Mehrtens 			rx32 = (u32 *) spi->rx;
55217f84b79SHauke Mehrtens 			*rx32 = data;
55317f84b79SHauke Mehrtens 			spi->rx_todo -= 4;
55417f84b79SHauke Mehrtens 			spi->rx += 4;
55517f84b79SHauke Mehrtens 			break;
55617f84b79SHauke Mehrtens 		default:
55717f84b79SHauke Mehrtens 			WARN_ON(1);
55817f84b79SHauke Mehrtens 			break;
55917f84b79SHauke Mehrtens 		}
56017f84b79SHauke Mehrtens 
56117f84b79SHauke Mehrtens 		rx_fill--;
56217f84b79SHauke Mehrtens 	}
56317f84b79SHauke Mehrtens }
56417f84b79SHauke Mehrtens 
rx_fifo_read_half_duplex(struct lantiq_ssc_spi * spi)56517f84b79SHauke Mehrtens static void rx_fifo_read_half_duplex(struct lantiq_ssc_spi *spi)
56617f84b79SHauke Mehrtens {
56717f84b79SHauke Mehrtens 	u32 data, *rx32;
56817f84b79SHauke Mehrtens 	u8 *rx8;
56917f84b79SHauke Mehrtens 	unsigned int rxbv, shift;
57017f84b79SHauke Mehrtens 	unsigned int rx_fill = rx_fifo_level(spi);
57117f84b79SHauke Mehrtens 
57217f84b79SHauke Mehrtens 	/*
57317f84b79SHauke Mehrtens 	 * In RX-only mode the bits per word value is ignored by HW. A value
57417f84b79SHauke Mehrtens 	 * of 32 is used instead. Thus all 4 bytes per FIFO must be read.
57517f84b79SHauke Mehrtens 	 * If remaining RX bytes are less than 4, the FIFO must be read
57617f84b79SHauke Mehrtens 	 * differently. The amount of received and valid bytes is indicated
57717f84b79SHauke Mehrtens 	 * by STAT.RXBV register value.
57817f84b79SHauke Mehrtens 	 */
57917f84b79SHauke Mehrtens 	while (rx_fill) {
58017f84b79SHauke Mehrtens 		if (spi->rx_todo < 4)  {
581ad2fca07SHauke Mehrtens 			rxbv = (lantiq_ssc_readl(spi, LTQ_SPI_STAT) &
582ad2fca07SHauke Mehrtens 				LTQ_SPI_STAT_RXBV_M) >> LTQ_SPI_STAT_RXBV_S;
583ad2fca07SHauke Mehrtens 			data = lantiq_ssc_readl(spi, LTQ_SPI_RB);
58417f84b79SHauke Mehrtens 
58517f84b79SHauke Mehrtens 			shift = (rxbv - 1) * 8;
58617f84b79SHauke Mehrtens 			rx8 = spi->rx;
58717f84b79SHauke Mehrtens 
58817f84b79SHauke Mehrtens 			while (rxbv) {
58917f84b79SHauke Mehrtens 				*rx8++ = (data >> shift) & 0xFF;
59017f84b79SHauke Mehrtens 				rxbv--;
59117f84b79SHauke Mehrtens 				shift -= 8;
59217f84b79SHauke Mehrtens 				spi->rx_todo--;
59317f84b79SHauke Mehrtens 				spi->rx++;
59417f84b79SHauke Mehrtens 			}
59517f84b79SHauke Mehrtens 		} else {
596ad2fca07SHauke Mehrtens 			data = lantiq_ssc_readl(spi, LTQ_SPI_RB);
59717f84b79SHauke Mehrtens 			rx32 = (u32 *) spi->rx;
59817f84b79SHauke Mehrtens 
59917f84b79SHauke Mehrtens 			*rx32++ = data;
60017f84b79SHauke Mehrtens 			spi->rx_todo -= 4;
60117f84b79SHauke Mehrtens 			spi->rx += 4;
60217f84b79SHauke Mehrtens 		}
60317f84b79SHauke Mehrtens 		rx_fill--;
60417f84b79SHauke Mehrtens 	}
60517f84b79SHauke Mehrtens }
60617f84b79SHauke Mehrtens 
rx_request(struct lantiq_ssc_spi * spi)60717f84b79SHauke Mehrtens static void rx_request(struct lantiq_ssc_spi *spi)
60817f84b79SHauke Mehrtens {
60917f84b79SHauke Mehrtens 	unsigned int rxreq, rxreq_max;
61017f84b79SHauke Mehrtens 
61117f84b79SHauke Mehrtens 	/*
61217f84b79SHauke Mehrtens 	 * To avoid receive overflows at high clocks it is better to request
61317f84b79SHauke Mehrtens 	 * only the amount of bytes that fits into all FIFOs. This value
61417f84b79SHauke Mehrtens 	 * depends on the FIFO size implemented in hardware.
61517f84b79SHauke Mehrtens 	 */
61617f84b79SHauke Mehrtens 	rxreq = spi->rx_todo;
61717f84b79SHauke Mehrtens 	rxreq_max = spi->rx_fifo_size * 4;
61817f84b79SHauke Mehrtens 	if (rxreq > rxreq_max)
61917f84b79SHauke Mehrtens 		rxreq = rxreq_max;
62017f84b79SHauke Mehrtens 
621ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, rxreq, LTQ_SPI_RXREQ);
62217f84b79SHauke Mehrtens }
62317f84b79SHauke Mehrtens 
lantiq_ssc_xmit_interrupt(int irq,void * data)62417f84b79SHauke Mehrtens static irqreturn_t lantiq_ssc_xmit_interrupt(int irq, void *data)
62517f84b79SHauke Mehrtens {
62617f84b79SHauke Mehrtens 	struct lantiq_ssc_spi *spi = data;
62794eca904SDilip Kota 	const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
62894eca904SDilip Kota 	u32 val = lantiq_ssc_readl(spi, hwcfg->irncr);
62917f84b79SHauke Mehrtens 
6307349201dSBarry Song 	spin_lock(&spi->lock);
63194eca904SDilip Kota 	if (hwcfg->irq_ack)
63294eca904SDilip Kota 		lantiq_ssc_writel(spi, val, hwcfg->irncr);
63394eca904SDilip Kota 
63417f84b79SHauke Mehrtens 	if (spi->tx) {
63517f84b79SHauke Mehrtens 		if (spi->rx && spi->rx_todo)
63617f84b79SHauke Mehrtens 			rx_fifo_read_full_duplex(spi);
63717f84b79SHauke Mehrtens 
63817f84b79SHauke Mehrtens 		if (spi->tx_todo)
63917f84b79SHauke Mehrtens 			tx_fifo_write(spi);
64017f84b79SHauke Mehrtens 		else if (!tx_fifo_level(spi))
64117f84b79SHauke Mehrtens 			goto completed;
64217f84b79SHauke Mehrtens 	} else if (spi->rx) {
64317f84b79SHauke Mehrtens 		if (spi->rx_todo) {
64417f84b79SHauke Mehrtens 			rx_fifo_read_half_duplex(spi);
64517f84b79SHauke Mehrtens 
64617f84b79SHauke Mehrtens 			if (spi->rx_todo)
64717f84b79SHauke Mehrtens 				rx_request(spi);
64817f84b79SHauke Mehrtens 			else
64917f84b79SHauke Mehrtens 				goto completed;
65017f84b79SHauke Mehrtens 		} else {
65117f84b79SHauke Mehrtens 			goto completed;
65217f84b79SHauke Mehrtens 		}
65317f84b79SHauke Mehrtens 	}
65417f84b79SHauke Mehrtens 
6557349201dSBarry Song 	spin_unlock(&spi->lock);
65617f84b79SHauke Mehrtens 	return IRQ_HANDLED;
65717f84b79SHauke Mehrtens 
65817f84b79SHauke Mehrtens completed:
65917f84b79SHauke Mehrtens 	queue_work(spi->wq, &spi->work);
6607349201dSBarry Song 	spin_unlock(&spi->lock);
66117f84b79SHauke Mehrtens 
66217f84b79SHauke Mehrtens 	return IRQ_HANDLED;
66317f84b79SHauke Mehrtens }
66417f84b79SHauke Mehrtens 
lantiq_ssc_err_interrupt(int irq,void * data)66517f84b79SHauke Mehrtens static irqreturn_t lantiq_ssc_err_interrupt(int irq, void *data)
66617f84b79SHauke Mehrtens {
66717f84b79SHauke Mehrtens 	struct lantiq_ssc_spi *spi = data;
66894eca904SDilip Kota 	const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
669ad2fca07SHauke Mehrtens 	u32 stat = lantiq_ssc_readl(spi, LTQ_SPI_STAT);
67094eca904SDilip Kota 	u32 val = lantiq_ssc_readl(spi, hwcfg->irncr);
67117f84b79SHauke Mehrtens 
672ad2fca07SHauke Mehrtens 	if (!(stat & LTQ_SPI_STAT_ERRORS))
67317f84b79SHauke Mehrtens 		return IRQ_NONE;
67417f84b79SHauke Mehrtens 
6757349201dSBarry Song 	spin_lock(&spi->lock);
67694eca904SDilip Kota 	if (hwcfg->irq_ack)
67794eca904SDilip Kota 		lantiq_ssc_writel(spi, val, hwcfg->irncr);
67894eca904SDilip Kota 
679ad2fca07SHauke Mehrtens 	if (stat & LTQ_SPI_STAT_RUE)
68017f84b79SHauke Mehrtens 		dev_err(spi->dev, "receive underflow error\n");
681ad2fca07SHauke Mehrtens 	if (stat & LTQ_SPI_STAT_TUE)
68217f84b79SHauke Mehrtens 		dev_err(spi->dev, "transmit underflow error\n");
683ad2fca07SHauke Mehrtens 	if (stat & LTQ_SPI_STAT_AE)
68417f84b79SHauke Mehrtens 		dev_err(spi->dev, "abort error\n");
685ad2fca07SHauke Mehrtens 	if (stat & LTQ_SPI_STAT_RE)
68617f84b79SHauke Mehrtens 		dev_err(spi->dev, "receive overflow error\n");
687ad2fca07SHauke Mehrtens 	if (stat & LTQ_SPI_STAT_TE)
68817f84b79SHauke Mehrtens 		dev_err(spi->dev, "transmit overflow error\n");
689ad2fca07SHauke Mehrtens 	if (stat & LTQ_SPI_STAT_ME)
69017f84b79SHauke Mehrtens 		dev_err(spi->dev, "mode error\n");
69117f84b79SHauke Mehrtens 
69217f84b79SHauke Mehrtens 	/* Clear error flags */
693ad2fca07SHauke Mehrtens 	lantiq_ssc_maskl(spi, 0, LTQ_SPI_WHBSTATE_CLR_ERRORS, LTQ_SPI_WHBSTATE);
69417f84b79SHauke Mehrtens 
69517f84b79SHauke Mehrtens 	/* set bad status so it can be retried */
696*ea11a8bbSYang Yingliang 	if (spi->host->cur_msg)
697*ea11a8bbSYang Yingliang 		spi->host->cur_msg->status = -EIO;
69817f84b79SHauke Mehrtens 	queue_work(spi->wq, &spi->work);
6997349201dSBarry Song 	spin_unlock(&spi->lock);
70017f84b79SHauke Mehrtens 
70117f84b79SHauke Mehrtens 	return IRQ_HANDLED;
70217f84b79SHauke Mehrtens }
70317f84b79SHauke Mehrtens 
intel_lgm_ssc_isr(int irq,void * data)704040f7f97SDilip Kota static irqreturn_t intel_lgm_ssc_isr(int irq, void *data)
705040f7f97SDilip Kota {
706040f7f97SDilip Kota 	struct lantiq_ssc_spi *spi = data;
707040f7f97SDilip Kota 	const struct lantiq_ssc_hwcfg *hwcfg = spi->hwcfg;
708040f7f97SDilip Kota 	u32 val = lantiq_ssc_readl(spi, hwcfg->irncr);
709040f7f97SDilip Kota 
710040f7f97SDilip Kota 	if (!(val & LTQ_SPI_IRNEN_ALL))
711040f7f97SDilip Kota 		return IRQ_NONE;
712040f7f97SDilip Kota 
713040f7f97SDilip Kota 	if (val & LTQ_SPI_IRNEN_E)
714040f7f97SDilip Kota 		return lantiq_ssc_err_interrupt(irq, data);
715040f7f97SDilip Kota 
716040f7f97SDilip Kota 	if ((val & hwcfg->irnen_t) || (val & hwcfg->irnen_r))
717040f7f97SDilip Kota 		return lantiq_ssc_xmit_interrupt(irq, data);
718040f7f97SDilip Kota 
719040f7f97SDilip Kota 	return IRQ_HANDLED;
720040f7f97SDilip Kota }
721040f7f97SDilip Kota 
transfer_start(struct lantiq_ssc_spi * spi,struct spi_device * spidev,struct spi_transfer * t)72217f84b79SHauke Mehrtens static int transfer_start(struct lantiq_ssc_spi *spi, struct spi_device *spidev,
72317f84b79SHauke Mehrtens 			  struct spi_transfer *t)
72417f84b79SHauke Mehrtens {
72517f84b79SHauke Mehrtens 	unsigned long flags;
72617f84b79SHauke Mehrtens 
72717f84b79SHauke Mehrtens 	spin_lock_irqsave(&spi->lock, flags);
72817f84b79SHauke Mehrtens 
72917f84b79SHauke Mehrtens 	spi->tx = t->tx_buf;
73017f84b79SHauke Mehrtens 	spi->rx = t->rx_buf;
73117f84b79SHauke Mehrtens 
73217f84b79SHauke Mehrtens 	if (t->tx_buf) {
73317f84b79SHauke Mehrtens 		spi->tx_todo = t->len;
73417f84b79SHauke Mehrtens 
73517f84b79SHauke Mehrtens 		/* initially fill TX FIFO */
73617f84b79SHauke Mehrtens 		tx_fifo_write(spi);
73717f84b79SHauke Mehrtens 	}
73817f84b79SHauke Mehrtens 
73917f84b79SHauke Mehrtens 	if (spi->rx) {
74017f84b79SHauke Mehrtens 		spi->rx_todo = t->len;
74117f84b79SHauke Mehrtens 
74217f84b79SHauke Mehrtens 		/* start shift clock in RX-only mode */
74317f84b79SHauke Mehrtens 		if (!spi->tx)
74417f84b79SHauke Mehrtens 			rx_request(spi);
74517f84b79SHauke Mehrtens 	}
74617f84b79SHauke Mehrtens 
74717f84b79SHauke Mehrtens 	spin_unlock_irqrestore(&spi->lock, flags);
74817f84b79SHauke Mehrtens 
74917f84b79SHauke Mehrtens 	return t->len;
75017f84b79SHauke Mehrtens }
75117f84b79SHauke Mehrtens 
75217f84b79SHauke Mehrtens /*
75317f84b79SHauke Mehrtens  * The driver only gets an interrupt when the FIFO is empty, but there
75417f84b79SHauke Mehrtens  * is an additional shift register from which the data is written to
75517f84b79SHauke Mehrtens  * the wire. We get the last interrupt when the controller starts to
75617f84b79SHauke Mehrtens  * write the last word to the wire, not when it is finished. Do busy
75717f84b79SHauke Mehrtens  * waiting till it finishes.
75817f84b79SHauke Mehrtens  */
lantiq_ssc_bussy_work(struct work_struct * work)75917f84b79SHauke Mehrtens static void lantiq_ssc_bussy_work(struct work_struct *work)
76017f84b79SHauke Mehrtens {
76117f84b79SHauke Mehrtens 	struct lantiq_ssc_spi *spi;
76217f84b79SHauke Mehrtens 	unsigned long long timeout = 8LL * 1000LL;
76317f84b79SHauke Mehrtens 	unsigned long end;
76417f84b79SHauke Mehrtens 
76517f84b79SHauke Mehrtens 	spi = container_of(work, typeof(*spi), work);
76617f84b79SHauke Mehrtens 
76717f84b79SHauke Mehrtens 	do_div(timeout, spi->speed_hz);
76817f84b79SHauke Mehrtens 	timeout += timeout + 100; /* some tolerance */
76917f84b79SHauke Mehrtens 
77017f84b79SHauke Mehrtens 	end = jiffies + msecs_to_jiffies(timeout);
77117f84b79SHauke Mehrtens 	do {
772ad2fca07SHauke Mehrtens 		u32 stat = lantiq_ssc_readl(spi, LTQ_SPI_STAT);
77317f84b79SHauke Mehrtens 
774ad2fca07SHauke Mehrtens 		if (!(stat & LTQ_SPI_STAT_BSY)) {
775*ea11a8bbSYang Yingliang 			spi_finalize_current_transfer(spi->host);
77617f84b79SHauke Mehrtens 			return;
77717f84b79SHauke Mehrtens 		}
77817f84b79SHauke Mehrtens 
77917f84b79SHauke Mehrtens 		cond_resched();
78017f84b79SHauke Mehrtens 	} while (!time_after_eq(jiffies, end));
78117f84b79SHauke Mehrtens 
782*ea11a8bbSYang Yingliang 	if (spi->host->cur_msg)
783*ea11a8bbSYang Yingliang 		spi->host->cur_msg->status = -EIO;
784*ea11a8bbSYang Yingliang 	spi_finalize_current_transfer(spi->host);
78517f84b79SHauke Mehrtens }
78617f84b79SHauke Mehrtens 
lantiq_ssc_handle_err(struct spi_controller * host,struct spi_message * message)787*ea11a8bbSYang Yingliang static void lantiq_ssc_handle_err(struct spi_controller *host,
78817f84b79SHauke Mehrtens 				  struct spi_message *message)
78917f84b79SHauke Mehrtens {
790*ea11a8bbSYang Yingliang 	struct lantiq_ssc_spi *spi = spi_controller_get_devdata(host);
79117f84b79SHauke Mehrtens 
79217f84b79SHauke Mehrtens 	/* flush FIFOs on timeout */
79317f84b79SHauke Mehrtens 	rx_fifo_flush(spi);
79417f84b79SHauke Mehrtens 	tx_fifo_flush(spi);
79517f84b79SHauke Mehrtens }
79617f84b79SHauke Mehrtens 
lantiq_ssc_set_cs(struct spi_device * spidev,bool enable)79717f84b79SHauke Mehrtens static void lantiq_ssc_set_cs(struct spi_device *spidev, bool enable)
79817f84b79SHauke Mehrtens {
799*ea11a8bbSYang Yingliang 	struct lantiq_ssc_spi *spi = spi_controller_get_devdata(spidev->controller);
8009e264f3fSAmit Kumar Mahapatra via Alsa-devel 	unsigned int cs = spi_get_chipselect(spidev, 0);
80117f84b79SHauke Mehrtens 	u32 fgpo;
80217f84b79SHauke Mehrtens 
80317f84b79SHauke Mehrtens 	if (!!(spidev->mode & SPI_CS_HIGH) == enable)
80417f84b79SHauke Mehrtens 		fgpo = (1 << (cs - spi->base_cs));
80517f84b79SHauke Mehrtens 	else
806ad2fca07SHauke Mehrtens 		fgpo = (1 << (cs - spi->base_cs + LTQ_SPI_FGPO_SETOUTN_S));
80717f84b79SHauke Mehrtens 
808ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, fgpo, LTQ_SPI_FPGO);
80917f84b79SHauke Mehrtens }
81017f84b79SHauke Mehrtens 
lantiq_ssc_transfer_one(struct spi_controller * host,struct spi_device * spidev,struct spi_transfer * t)811*ea11a8bbSYang Yingliang static int lantiq_ssc_transfer_one(struct spi_controller *host,
81217f84b79SHauke Mehrtens 				   struct spi_device *spidev,
81317f84b79SHauke Mehrtens 				   struct spi_transfer *t)
81417f84b79SHauke Mehrtens {
815*ea11a8bbSYang Yingliang 	struct lantiq_ssc_spi *spi = spi_controller_get_devdata(host);
81617f84b79SHauke Mehrtens 
81717f84b79SHauke Mehrtens 	hw_setup_transfer(spi, spidev, t);
81817f84b79SHauke Mehrtens 
81917f84b79SHauke Mehrtens 	return transfer_start(spi, spidev, t);
82017f84b79SHauke Mehrtens }
82117f84b79SHauke Mehrtens 
intel_lgm_cfg_irq(struct platform_device * pdev,struct lantiq_ssc_spi * spi)822040f7f97SDilip Kota static int intel_lgm_cfg_irq(struct platform_device *pdev, struct lantiq_ssc_spi *spi)
823040f7f97SDilip Kota {
824040f7f97SDilip Kota 	int irq;
825040f7f97SDilip Kota 
826040f7f97SDilip Kota 	irq = platform_get_irq(pdev, 0);
827040f7f97SDilip Kota 	if (irq < 0)
828040f7f97SDilip Kota 		return irq;
829040f7f97SDilip Kota 
830040f7f97SDilip Kota 	return devm_request_irq(&pdev->dev, irq, intel_lgm_ssc_isr, 0, "spi", spi);
831040f7f97SDilip Kota }
832040f7f97SDilip Kota 
lantiq_cfg_irq(struct platform_device * pdev,struct lantiq_ssc_spi * spi)833744cd0f2SDilip Kota static int lantiq_cfg_irq(struct platform_device *pdev, struct lantiq_ssc_spi *spi)
834744cd0f2SDilip Kota {
835744cd0f2SDilip Kota 	int irq, err;
836744cd0f2SDilip Kota 
837744cd0f2SDilip Kota 	irq = platform_get_irq_byname(pdev, LTQ_SPI_RX_IRQ_NAME);
838744cd0f2SDilip Kota 	if (irq < 0)
839744cd0f2SDilip Kota 		return irq;
840744cd0f2SDilip Kota 
841744cd0f2SDilip Kota 	err = devm_request_irq(&pdev->dev, irq, lantiq_ssc_xmit_interrupt,
842744cd0f2SDilip Kota 			       0, LTQ_SPI_RX_IRQ_NAME, spi);
843744cd0f2SDilip Kota 	if (err)
844744cd0f2SDilip Kota 		return err;
845744cd0f2SDilip Kota 
846744cd0f2SDilip Kota 	irq = platform_get_irq_byname(pdev, LTQ_SPI_TX_IRQ_NAME);
847744cd0f2SDilip Kota 	if (irq < 0)
848744cd0f2SDilip Kota 		return irq;
849744cd0f2SDilip Kota 
850744cd0f2SDilip Kota 	err = devm_request_irq(&pdev->dev, irq, lantiq_ssc_xmit_interrupt,
851744cd0f2SDilip Kota 			       0, LTQ_SPI_TX_IRQ_NAME, spi);
852744cd0f2SDilip Kota 
853744cd0f2SDilip Kota 	if (err)
854744cd0f2SDilip Kota 		return err;
855744cd0f2SDilip Kota 
856744cd0f2SDilip Kota 	irq = platform_get_irq_byname(pdev, LTQ_SPI_ERR_IRQ_NAME);
857744cd0f2SDilip Kota 	if (irq < 0)
858744cd0f2SDilip Kota 		return irq;
859744cd0f2SDilip Kota 
860744cd0f2SDilip Kota 	err = devm_request_irq(&pdev->dev, irq, lantiq_ssc_err_interrupt,
861744cd0f2SDilip Kota 			       0, LTQ_SPI_ERR_IRQ_NAME, spi);
862744cd0f2SDilip Kota 	return err;
863744cd0f2SDilip Kota }
864744cd0f2SDilip Kota 
86517f84b79SHauke Mehrtens static const struct lantiq_ssc_hwcfg lantiq_ssc_xway = {
866744cd0f2SDilip Kota 	.cfg_irq	= lantiq_cfg_irq,
867ad2fca07SHauke Mehrtens 	.irnen_r	= LTQ_SPI_IRNEN_R_XWAY,
868ad2fca07SHauke Mehrtens 	.irnen_t	= LTQ_SPI_IRNEN_T_XWAY,
8698d19d665SDilip Kota 	.irnicr		= 0xF8,
8708d19d665SDilip Kota 	.irncr		= 0xFC,
8718743d215SDilip Kota 	.fifo_size_mask	= GENMASK(5, 0),
87294eca904SDilip Kota 	.irq_ack	= false,
87317f84b79SHauke Mehrtens };
87417f84b79SHauke Mehrtens 
87517f84b79SHauke Mehrtens static const struct lantiq_ssc_hwcfg lantiq_ssc_xrx = {
876744cd0f2SDilip Kota 	.cfg_irq	= lantiq_cfg_irq,
877ad2fca07SHauke Mehrtens 	.irnen_r	= LTQ_SPI_IRNEN_R_XRX,
878ad2fca07SHauke Mehrtens 	.irnen_t	= LTQ_SPI_IRNEN_T_XRX,
8798d19d665SDilip Kota 	.irnicr		= 0xF8,
8808d19d665SDilip Kota 	.irncr		= 0xFC,
8818743d215SDilip Kota 	.fifo_size_mask	= GENMASK(5, 0),
88294eca904SDilip Kota 	.irq_ack	= false,
88317f84b79SHauke Mehrtens };
88417f84b79SHauke Mehrtens 
885040f7f97SDilip Kota static const struct lantiq_ssc_hwcfg intel_ssc_lgm = {
886040f7f97SDilip Kota 	.cfg_irq	= intel_lgm_cfg_irq,
887040f7f97SDilip Kota 	.irnen_r	= LTQ_SPI_IRNEN_R_XRX,
888040f7f97SDilip Kota 	.irnen_t	= LTQ_SPI_IRNEN_T_XRX,
889040f7f97SDilip Kota 	.irnicr		= 0xFC,
890040f7f97SDilip Kota 	.irncr		= 0xF8,
891040f7f97SDilip Kota 	.fifo_size_mask	= GENMASK(7, 0),
892040f7f97SDilip Kota 	.irq_ack	= true,
893040f7f97SDilip Kota };
894040f7f97SDilip Kota 
89517f84b79SHauke Mehrtens static const struct of_device_id lantiq_ssc_match[] = {
89617f84b79SHauke Mehrtens 	{ .compatible = "lantiq,ase-spi", .data = &lantiq_ssc_xway, },
89717f84b79SHauke Mehrtens 	{ .compatible = "lantiq,falcon-spi", .data = &lantiq_ssc_xrx, },
89817f84b79SHauke Mehrtens 	{ .compatible = "lantiq,xrx100-spi", .data = &lantiq_ssc_xrx, },
899040f7f97SDilip Kota 	{ .compatible = "intel,lgm-spi", .data = &intel_ssc_lgm, },
90017f84b79SHauke Mehrtens 	{},
90117f84b79SHauke Mehrtens };
90217f84b79SHauke Mehrtens MODULE_DEVICE_TABLE(of, lantiq_ssc_match);
90317f84b79SHauke Mehrtens 
lantiq_ssc_probe(struct platform_device * pdev)90417f84b79SHauke Mehrtens static int lantiq_ssc_probe(struct platform_device *pdev)
90517f84b79SHauke Mehrtens {
90617f84b79SHauke Mehrtens 	struct device *dev = &pdev->dev;
907*ea11a8bbSYang Yingliang 	struct spi_controller *host;
90817f84b79SHauke Mehrtens 	struct lantiq_ssc_spi *spi;
90917f84b79SHauke Mehrtens 	const struct lantiq_ssc_hwcfg *hwcfg;
91017f84b79SHauke Mehrtens 	u32 id, supports_dma, revision;
91117f84b79SHauke Mehrtens 	unsigned int num_cs;
912744cd0f2SDilip Kota 	int err;
91317f84b79SHauke Mehrtens 
914c5a3106aSMinghao Chi (CGEL ZTE) 	hwcfg = of_device_get_match_data(dev);
91517f84b79SHauke Mehrtens 
916*ea11a8bbSYang Yingliang 	host = spi_alloc_host(dev, sizeof(struct lantiq_ssc_spi));
917*ea11a8bbSYang Yingliang 	if (!host)
91817f84b79SHauke Mehrtens 		return -ENOMEM;
91917f84b79SHauke Mehrtens 
920*ea11a8bbSYang Yingliang 	spi = spi_controller_get_devdata(host);
921*ea11a8bbSYang Yingliang 	spi->host = host;
92217f84b79SHauke Mehrtens 	spi->dev = dev;
92317f84b79SHauke Mehrtens 	spi->hwcfg = hwcfg;
92417f84b79SHauke Mehrtens 	platform_set_drvdata(pdev, spi);
92522262695SMarkus Elfring 	spi->regbase = devm_platform_ioremap_resource(pdev, 0);
92617f84b79SHauke Mehrtens 	if (IS_ERR(spi->regbase)) {
92717f84b79SHauke Mehrtens 		err = PTR_ERR(spi->regbase);
928*ea11a8bbSYang Yingliang 		goto err_host_put;
92917f84b79SHauke Mehrtens 	}
93017f84b79SHauke Mehrtens 
931744cd0f2SDilip Kota 	err = hwcfg->cfg_irq(pdev, spi);
93217f84b79SHauke Mehrtens 	if (err)
933*ea11a8bbSYang Yingliang 		goto err_host_put;
93417f84b79SHauke Mehrtens 
93517f84b79SHauke Mehrtens 	spi->spi_clk = devm_clk_get(dev, "gate");
93617f84b79SHauke Mehrtens 	if (IS_ERR(spi->spi_clk)) {
93717f84b79SHauke Mehrtens 		err = PTR_ERR(spi->spi_clk);
938*ea11a8bbSYang Yingliang 		goto err_host_put;
93917f84b79SHauke Mehrtens 	}
94017f84b79SHauke Mehrtens 	err = clk_prepare_enable(spi->spi_clk);
94117f84b79SHauke Mehrtens 	if (err)
942*ea11a8bbSYang Yingliang 		goto err_host_put;
94317f84b79SHauke Mehrtens 
94417f84b79SHauke Mehrtens 	/*
94517f84b79SHauke Mehrtens 	 * Use the old clk_get_fpi() function on Lantiq platform, till it
94617f84b79SHauke Mehrtens 	 * supports common clk.
94717f84b79SHauke Mehrtens 	 */
94817f84b79SHauke Mehrtens #if defined(CONFIG_LANTIQ) && !defined(CONFIG_COMMON_CLK)
94917f84b79SHauke Mehrtens 	spi->fpi_clk = clk_get_fpi();
95017f84b79SHauke Mehrtens #else
95117f84b79SHauke Mehrtens 	spi->fpi_clk = clk_get(dev, "freq");
95217f84b79SHauke Mehrtens #endif
95317f84b79SHauke Mehrtens 	if (IS_ERR(spi->fpi_clk)) {
95417f84b79SHauke Mehrtens 		err = PTR_ERR(spi->fpi_clk);
95517f84b79SHauke Mehrtens 		goto err_clk_disable;
95617f84b79SHauke Mehrtens 	}
95717f84b79SHauke Mehrtens 
95817f84b79SHauke Mehrtens 	num_cs = 8;
95917f84b79SHauke Mehrtens 	of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs);
96017f84b79SHauke Mehrtens 
96117f84b79SHauke Mehrtens 	spi->base_cs = 1;
96217f84b79SHauke Mehrtens 	of_property_read_u32(pdev->dev.of_node, "base-cs", &spi->base_cs);
96317f84b79SHauke Mehrtens 
96417f84b79SHauke Mehrtens 	spin_lock_init(&spi->lock);
96517f84b79SHauke Mehrtens 	spi->bits_per_word = 8;
96617f84b79SHauke Mehrtens 	spi->speed_hz = 0;
96717f84b79SHauke Mehrtens 
968*ea11a8bbSYang Yingliang 	host->dev.of_node = pdev->dev.of_node;
969*ea11a8bbSYang Yingliang 	host->num_chipselect = num_cs;
970*ea11a8bbSYang Yingliang 	host->use_gpio_descriptors = true;
971*ea11a8bbSYang Yingliang 	host->setup = lantiq_ssc_setup;
972*ea11a8bbSYang Yingliang 	host->set_cs = lantiq_ssc_set_cs;
973*ea11a8bbSYang Yingliang 	host->handle_err = lantiq_ssc_handle_err;
974*ea11a8bbSYang Yingliang 	host->prepare_message = lantiq_ssc_prepare_message;
975*ea11a8bbSYang Yingliang 	host->unprepare_message = lantiq_ssc_unprepare_message;
976*ea11a8bbSYang Yingliang 	host->transfer_one = lantiq_ssc_transfer_one;
977*ea11a8bbSYang Yingliang 	host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH |
97817f84b79SHauke Mehrtens 			  SPI_LOOP;
979*ea11a8bbSYang Yingliang 	host->bits_per_word_mask = SPI_BPW_RANGE_MASK(2, 8) |
98017f84b79SHauke Mehrtens 				   SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
98117f84b79SHauke Mehrtens 
982ba3548cfSHauke Mehrtens 	spi->wq = alloc_ordered_workqueue(dev_name(dev), WQ_MEM_RECLAIM);
98317f84b79SHauke Mehrtens 	if (!spi->wq) {
98417f84b79SHauke Mehrtens 		err = -ENOMEM;
98517f84b79SHauke Mehrtens 		goto err_clk_put;
98617f84b79SHauke Mehrtens 	}
98717f84b79SHauke Mehrtens 	INIT_WORK(&spi->work, lantiq_ssc_bussy_work);
98817f84b79SHauke Mehrtens 
989ad2fca07SHauke Mehrtens 	id = lantiq_ssc_readl(spi, LTQ_SPI_ID);
9908743d215SDilip Kota 	spi->tx_fifo_size = (id >> LTQ_SPI_ID_TXFS_S) & hwcfg->fifo_size_mask;
9918743d215SDilip Kota 	spi->rx_fifo_size = (id >> LTQ_SPI_ID_RXFS_S) & hwcfg->fifo_size_mask;
992ad2fca07SHauke Mehrtens 	supports_dma = (id & LTQ_SPI_ID_CFG_M) >> LTQ_SPI_ID_CFG_S;
993ad2fca07SHauke Mehrtens 	revision = id & LTQ_SPI_ID_REV_M;
99417f84b79SHauke Mehrtens 
99517f84b79SHauke Mehrtens 	lantiq_ssc_hw_init(spi);
99617f84b79SHauke Mehrtens 
99717f84b79SHauke Mehrtens 	dev_info(dev,
99817f84b79SHauke Mehrtens 		"Lantiq SSC SPI controller (Rev %i, TXFS %u, RXFS %u, DMA %u)\n",
99917f84b79SHauke Mehrtens 		revision, spi->tx_fifo_size, spi->rx_fifo_size, supports_dma);
100017f84b79SHauke Mehrtens 
1001*ea11a8bbSYang Yingliang 	err = devm_spi_register_controller(dev, host);
100217f84b79SHauke Mehrtens 	if (err) {
1003*ea11a8bbSYang Yingliang 		dev_err(dev, "failed to register spi host\n");
100417f84b79SHauke Mehrtens 		goto err_wq_destroy;
100517f84b79SHauke Mehrtens 	}
100617f84b79SHauke Mehrtens 
100717f84b79SHauke Mehrtens 	return 0;
100817f84b79SHauke Mehrtens 
100917f84b79SHauke Mehrtens err_wq_destroy:
101017f84b79SHauke Mehrtens 	destroy_workqueue(spi->wq);
101117f84b79SHauke Mehrtens err_clk_put:
101217f84b79SHauke Mehrtens 	clk_put(spi->fpi_clk);
101317f84b79SHauke Mehrtens err_clk_disable:
101417f84b79SHauke Mehrtens 	clk_disable_unprepare(spi->spi_clk);
1015*ea11a8bbSYang Yingliang err_host_put:
1016*ea11a8bbSYang Yingliang 	spi_controller_put(host);
101717f84b79SHauke Mehrtens 
101817f84b79SHauke Mehrtens 	return err;
101917f84b79SHauke Mehrtens }
102017f84b79SHauke Mehrtens 
lantiq_ssc_remove(struct platform_device * pdev)10211f85ed7dSUwe Kleine-König static void lantiq_ssc_remove(struct platform_device *pdev)
102217f84b79SHauke Mehrtens {
102317f84b79SHauke Mehrtens 	struct lantiq_ssc_spi *spi = platform_get_drvdata(pdev);
102417f84b79SHauke Mehrtens 
1025ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, 0, LTQ_SPI_IRNEN);
1026ad2fca07SHauke Mehrtens 	lantiq_ssc_writel(spi, 0, LTQ_SPI_CLC);
102717f84b79SHauke Mehrtens 	rx_fifo_flush(spi);
102817f84b79SHauke Mehrtens 	tx_fifo_flush(spi);
102917f84b79SHauke Mehrtens 	hw_enter_config_mode(spi);
103017f84b79SHauke Mehrtens 
103117f84b79SHauke Mehrtens 	destroy_workqueue(spi->wq);
103217f84b79SHauke Mehrtens 	clk_disable_unprepare(spi->spi_clk);
103317f84b79SHauke Mehrtens 	clk_put(spi->fpi_clk);
103417f84b79SHauke Mehrtens }
103517f84b79SHauke Mehrtens 
103617f84b79SHauke Mehrtens static struct platform_driver lantiq_ssc_driver = {
103717f84b79SHauke Mehrtens 	.probe = lantiq_ssc_probe,
10381f85ed7dSUwe Kleine-König 	.remove_new = lantiq_ssc_remove,
103917f84b79SHauke Mehrtens 	.driver = {
104017f84b79SHauke Mehrtens 		.name = "spi-lantiq-ssc",
104117f84b79SHauke Mehrtens 		.of_match_table = lantiq_ssc_match,
104217f84b79SHauke Mehrtens 	},
104317f84b79SHauke Mehrtens };
104417f84b79SHauke Mehrtens module_platform_driver(lantiq_ssc_driver);
104517f84b79SHauke Mehrtens 
104617f84b79SHauke Mehrtens MODULE_DESCRIPTION("Lantiq SSC SPI controller driver");
104717f84b79SHauke Mehrtens MODULE_AUTHOR("Daniel Schwierzeck <daniel.schwierzeck@gmail.com>");
104817f84b79SHauke Mehrtens MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
104917f84b79SHauke Mehrtens MODULE_LICENSE("GPL");
105017f84b79SHauke Mehrtens MODULE_ALIAS("platform:spi-lantiq-ssc");
1051