xref: /openbmc/linux/drivers/spi/spi-meson-spicc.c (revision 749396cb)
1454fa271SNeil Armstrong /*
2454fa271SNeil Armstrong  * Driver for Amlogic Meson SPI communication controller (SPICC)
3454fa271SNeil Armstrong  *
4454fa271SNeil Armstrong  * Copyright (C) BayLibre, SAS
5454fa271SNeil Armstrong  * Author: Neil Armstrong <narmstrong@baylibre.com>
6454fa271SNeil Armstrong  *
7454fa271SNeil Armstrong  * SPDX-License-Identifier: GPL-2.0+
8454fa271SNeil Armstrong  */
9454fa271SNeil Armstrong 
10454fa271SNeil Armstrong #include <linux/bitfield.h>
11454fa271SNeil Armstrong #include <linux/clk.h>
12a6cda1f9SSunny Luo #include <linux/clk-provider.h>
13454fa271SNeil Armstrong #include <linux/device.h>
14454fa271SNeil Armstrong #include <linux/io.h>
15454fa271SNeil Armstrong #include <linux/kernel.h>
16454fa271SNeil Armstrong #include <linux/module.h>
17454fa271SNeil Armstrong #include <linux/of.h>
18454fa271SNeil Armstrong #include <linux/platform_device.h>
19454fa271SNeil Armstrong #include <linux/spi/spi.h>
20454fa271SNeil Armstrong #include <linux/types.h>
21454fa271SNeil Armstrong #include <linux/interrupt.h>
22454fa271SNeil Armstrong #include <linux/reset.h>
23f4567b28SAmjad Ouled-Ameur #include <linux/pinctrl/consumer.h>
24454fa271SNeil Armstrong 
25454fa271SNeil Armstrong /*
26454fa271SNeil Armstrong  * The Meson SPICC controller could support DMA based transfers, but is not
27454fa271SNeil Armstrong  * implemented by the vendor code, and while having the registers documentation
28454fa271SNeil Armstrong  * it has never worked on the GXL Hardware.
29454fa271SNeil Armstrong  * The PIO mode is the only mode implemented, and due to badly designed HW :
30454fa271SNeil Armstrong  * - all transfers are cutted in 16 words burst because the FIFO hangs on
31454fa271SNeil Armstrong  *   TX underflow, and there is no TX "Half-Empty" interrupt, so we go by
32454fa271SNeil Armstrong  *   FIFO max size chunk only
33454fa271SNeil Armstrong  * - CS management is dumb, and goes UP between every burst, so is really a
34454fa271SNeil Armstrong  *   "Data Valid" signal than a Chip Select, GPIO link should be used instead
35454fa271SNeil Armstrong  *   to have a CS go down over the full transfer
36454fa271SNeil Armstrong  */
37454fa271SNeil Armstrong 
38454fa271SNeil Armstrong #define SPICC_MAX_BURST	128
39454fa271SNeil Armstrong 
40454fa271SNeil Armstrong /* Register Map */
41454fa271SNeil Armstrong #define SPICC_RXDATA	0x00
42454fa271SNeil Armstrong 
43454fa271SNeil Armstrong #define SPICC_TXDATA	0x04
44454fa271SNeil Armstrong 
45454fa271SNeil Armstrong #define SPICC_CONREG	0x08
46454fa271SNeil Armstrong #define SPICC_ENABLE		BIT(0)
47454fa271SNeil Armstrong #define SPICC_MODE_MASTER	BIT(1)
48454fa271SNeil Armstrong #define SPICC_XCH		BIT(2)
49454fa271SNeil Armstrong #define SPICC_SMC		BIT(3)
50454fa271SNeil Armstrong #define SPICC_POL		BIT(4)
51454fa271SNeil Armstrong #define SPICC_PHA		BIT(5)
52454fa271SNeil Armstrong #define SPICC_SSCTL		BIT(6)
53454fa271SNeil Armstrong #define SPICC_SSPOL		BIT(7)
54454fa271SNeil Armstrong #define SPICC_DRCTL_MASK	GENMASK(9, 8)
55454fa271SNeil Armstrong #define SPICC_DRCTL_IGNORE	0
56454fa271SNeil Armstrong #define SPICC_DRCTL_FALLING	1
57454fa271SNeil Armstrong #define SPICC_DRCTL_LOWLEVEL	2
58454fa271SNeil Armstrong #define SPICC_CS_MASK		GENMASK(13, 12)
59454fa271SNeil Armstrong #define SPICC_DATARATE_MASK	GENMASK(18, 16)
60454fa271SNeil Armstrong #define SPICC_DATARATE_DIV4	0
61454fa271SNeil Armstrong #define SPICC_DATARATE_DIV8	1
62454fa271SNeil Armstrong #define SPICC_DATARATE_DIV16	2
63454fa271SNeil Armstrong #define SPICC_DATARATE_DIV32	3
64454fa271SNeil Armstrong #define SPICC_BITLENGTH_MASK	GENMASK(24, 19)
65454fa271SNeil Armstrong #define SPICC_BURSTLENGTH_MASK	GENMASK(31, 25)
66454fa271SNeil Armstrong 
67454fa271SNeil Armstrong #define SPICC_INTREG	0x0c
68454fa271SNeil Armstrong #define SPICC_TE_EN	BIT(0) /* TX FIFO Empty Interrupt */
69454fa271SNeil Armstrong #define SPICC_TH_EN	BIT(1) /* TX FIFO Half-Full Interrupt */
70454fa271SNeil Armstrong #define SPICC_TF_EN	BIT(2) /* TX FIFO Full Interrupt */
71454fa271SNeil Armstrong #define SPICC_RR_EN	BIT(3) /* RX FIFO Ready Interrupt */
72454fa271SNeil Armstrong #define SPICC_RH_EN	BIT(4) /* RX FIFO Half-Full Interrupt */
73454fa271SNeil Armstrong #define SPICC_RF_EN	BIT(5) /* RX FIFO Full Interrupt */
74454fa271SNeil Armstrong #define SPICC_RO_EN	BIT(6) /* RX FIFO Overflow Interrupt */
75454fa271SNeil Armstrong #define SPICC_TC_EN	BIT(7) /* Transfert Complete Interrupt */
76454fa271SNeil Armstrong 
77454fa271SNeil Armstrong #define SPICC_DMAREG	0x10
78454fa271SNeil Armstrong #define SPICC_DMA_ENABLE		BIT(0)
79454fa271SNeil Armstrong #define SPICC_TXFIFO_THRESHOLD_MASK	GENMASK(5, 1)
80454fa271SNeil Armstrong #define SPICC_RXFIFO_THRESHOLD_MASK	GENMASK(10, 6)
81454fa271SNeil Armstrong #define SPICC_READ_BURST_MASK		GENMASK(14, 11)
82454fa271SNeil Armstrong #define SPICC_WRITE_BURST_MASK		GENMASK(18, 15)
83454fa271SNeil Armstrong #define SPICC_DMA_URGENT		BIT(19)
84454fa271SNeil Armstrong #define SPICC_DMA_THREADID_MASK		GENMASK(25, 20)
85454fa271SNeil Armstrong #define SPICC_DMA_BURSTNUM_MASK		GENMASK(31, 26)
86454fa271SNeil Armstrong 
87454fa271SNeil Armstrong #define SPICC_STATREG	0x14
88454fa271SNeil Armstrong #define SPICC_TE	BIT(0) /* TX FIFO Empty Interrupt */
89454fa271SNeil Armstrong #define SPICC_TH	BIT(1) /* TX FIFO Half-Full Interrupt */
90454fa271SNeil Armstrong #define SPICC_TF	BIT(2) /* TX FIFO Full Interrupt */
91454fa271SNeil Armstrong #define SPICC_RR	BIT(3) /* RX FIFO Ready Interrupt */
92454fa271SNeil Armstrong #define SPICC_RH	BIT(4) /* RX FIFO Half-Full Interrupt */
93454fa271SNeil Armstrong #define SPICC_RF	BIT(5) /* RX FIFO Full Interrupt */
94454fa271SNeil Armstrong #define SPICC_RO	BIT(6) /* RX FIFO Overflow Interrupt */
95454fa271SNeil Armstrong #define SPICC_TC	BIT(7) /* Transfert Complete Interrupt */
96454fa271SNeil Armstrong 
97454fa271SNeil Armstrong #define SPICC_PERIODREG	0x18
98454fa271SNeil Armstrong #define SPICC_PERIOD	GENMASK(14, 0)	/* Wait cycles */
99454fa271SNeil Armstrong 
100454fa271SNeil Armstrong #define SPICC_TESTREG	0x1c
101454fa271SNeil Armstrong #define SPICC_TXCNT_MASK	GENMASK(4, 0)	/* TX FIFO Counter */
102454fa271SNeil Armstrong #define SPICC_RXCNT_MASK	GENMASK(9, 5)	/* RX FIFO Counter */
103454fa271SNeil Armstrong #define SPICC_SMSTATUS_MASK	GENMASK(12, 10)	/* State Machine Status */
104454fa271SNeil Armstrong #define SPICC_LBC_RO		BIT(13)	/* Loop Back Control Read-Only */
105454fa271SNeil Armstrong #define SPICC_LBC_W1		BIT(14) /* Loop Back Control Write-Only */
106454fa271SNeil Armstrong #define SPICC_SWAP_RO		BIT(14) /* RX FIFO Data Swap Read-Only */
107454fa271SNeil Armstrong #define SPICC_SWAP_W1		BIT(15) /* RX FIFO Data Swap Write-Only */
108454fa271SNeil Armstrong #define SPICC_DLYCTL_RO_MASK	GENMASK(20, 15) /* Delay Control Read-Only */
109f27bff47SNeil Armstrong #define SPICC_MO_DELAY_MASK	GENMASK(17, 16) /* Master Output Delay */
110f27bff47SNeil Armstrong #define SPICC_MO_NO_DELAY	0
111f27bff47SNeil Armstrong #define SPICC_MO_DELAY_1_CYCLE	1
112f27bff47SNeil Armstrong #define SPICC_MO_DELAY_2_CYCLE	2
113f27bff47SNeil Armstrong #define SPICC_MO_DELAY_3_CYCLE	3
114f27bff47SNeil Armstrong #define SPICC_MI_DELAY_MASK	GENMASK(19, 18) /* Master Input Delay */
115f27bff47SNeil Armstrong #define SPICC_MI_NO_DELAY	0
116f27bff47SNeil Armstrong #define SPICC_MI_DELAY_1_CYCLE	1
117f27bff47SNeil Armstrong #define SPICC_MI_DELAY_2_CYCLE	2
118f27bff47SNeil Armstrong #define SPICC_MI_DELAY_3_CYCLE	3
119f27bff47SNeil Armstrong #define SPICC_MI_CAP_DELAY_MASK	GENMASK(21, 20) /* Master Capture Delay */
120f27bff47SNeil Armstrong #define SPICC_CAP_AHEAD_2_CYCLE	0
121f27bff47SNeil Armstrong #define SPICC_CAP_AHEAD_1_CYCLE	1
122f27bff47SNeil Armstrong #define SPICC_CAP_NO_DELAY	2
123f27bff47SNeil Armstrong #define SPICC_CAP_DELAY_1_CYCLE	3
124454fa271SNeil Armstrong #define SPICC_FIFORST_RO_MASK	GENMASK(22, 21) /* FIFO Softreset Read-Only */
125454fa271SNeil Armstrong #define SPICC_FIFORST_W1_MASK	GENMASK(23, 22) /* FIFO Softreset Write-Only */
126454fa271SNeil Armstrong 
127454fa271SNeil Armstrong #define SPICC_DRADDR	0x20	/* Read Address of DMA */
128454fa271SNeil Armstrong 
129454fa271SNeil Armstrong #define SPICC_DWADDR	0x24	/* Write Address of DMA */
130454fa271SNeil Armstrong 
131a6cda1f9SSunny Luo #define SPICC_ENH_CTL0	0x38	/* Enhanced Feature */
1323e0cf4d3SSunny Luo #define SPICC_ENH_CLK_CS_DELAY_MASK	GENMASK(15, 0)
1333e0cf4d3SSunny Luo #define SPICC_ENH_DATARATE_MASK		GENMASK(23, 16)
1343e0cf4d3SSunny Luo #define SPICC_ENH_DATARATE_EN		BIT(24)
135a6cda1f9SSunny Luo #define SPICC_ENH_MOSI_OEN		BIT(25)
136a6cda1f9SSunny Luo #define SPICC_ENH_CLK_OEN		BIT(26)
137a6cda1f9SSunny Luo #define SPICC_ENH_CS_OEN		BIT(27)
138a6cda1f9SSunny Luo #define SPICC_ENH_CLK_CS_DELAY_EN	BIT(28)
139a6cda1f9SSunny Luo #define SPICC_ENH_MAIN_CLK_AO		BIT(29)
140a6cda1f9SSunny Luo 
141454fa271SNeil Armstrong #define writel_bits_relaxed(mask, val, addr) \
142454fa271SNeil Armstrong 	writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)
143454fa271SNeil Armstrong 
144a6cda1f9SSunny Luo struct meson_spicc_data {
1453196816fSNeil Armstrong 	unsigned int			max_speed_hz;
1468791068dSNeil Armstrong 	unsigned int			min_speed_hz;
1470eb707acSNeil Armstrong 	unsigned int			fifo_size;
148a6cda1f9SSunny Luo 	bool				has_oen;
1493e0cf4d3SSunny Luo 	bool				has_enhance_clk_div;
1504e3d3220SNeil Armstrong 	bool				has_pclk;
151a6cda1f9SSunny Luo };
152a6cda1f9SSunny Luo 
153454fa271SNeil Armstrong struct meson_spicc_device {
154454fa271SNeil Armstrong 	struct spi_master		*master;
155454fa271SNeil Armstrong 	struct platform_device		*pdev;
156454fa271SNeil Armstrong 	void __iomem			*base;
157454fa271SNeil Armstrong 	struct clk			*core;
1584e3d3220SNeil Armstrong 	struct clk			*pclk;
15909992025SNeil Armstrong 	struct clk_divider		pow2_div;
1603e0cf4d3SSunny Luo 	struct clk			*clk;
161454fa271SNeil Armstrong 	struct spi_message		*message;
162454fa271SNeil Armstrong 	struct spi_transfer		*xfer;
16304694e50SNeil Armstrong 	struct completion		done;
164a6cda1f9SSunny Luo 	const struct meson_spicc_data	*data;
165454fa271SNeil Armstrong 	u8				*tx_buf;
166454fa271SNeil Armstrong 	u8				*rx_buf;
167454fa271SNeil Armstrong 	unsigned int			bytes_per_word;
168454fa271SNeil Armstrong 	unsigned long			tx_remain;
169454fa271SNeil Armstrong 	unsigned long			rx_remain;
170454fa271SNeil Armstrong 	unsigned long			xfer_remain;
171f4567b28SAmjad Ouled-Ameur 	struct pinctrl			*pinctrl;
172f4567b28SAmjad Ouled-Ameur 	struct pinctrl_state		*pins_idle_high;
173f4567b28SAmjad Ouled-Ameur 	struct pinctrl_state		*pins_idle_low;
174454fa271SNeil Armstrong };
175454fa271SNeil Armstrong 
17609992025SNeil Armstrong #define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
17709992025SNeil Armstrong 
meson_spicc_oen_enable(struct meson_spicc_device * spicc)178a6cda1f9SSunny Luo static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
179a6cda1f9SSunny Luo {
180a6cda1f9SSunny Luo 	u32 conf;
181a6cda1f9SSunny Luo 
182f4567b28SAmjad Ouled-Ameur 	if (!spicc->data->has_oen) {
183f4567b28SAmjad Ouled-Ameur 		/* Try to get pinctrl states for idle high/low */
184f4567b28SAmjad Ouled-Ameur 		spicc->pins_idle_high = pinctrl_lookup_state(spicc->pinctrl,
185f4567b28SAmjad Ouled-Ameur 							     "idle-high");
186f4567b28SAmjad Ouled-Ameur 		if (IS_ERR(spicc->pins_idle_high)) {
187f4567b28SAmjad Ouled-Ameur 			dev_warn(&spicc->pdev->dev, "can't get idle-high pinctrl\n");
188f4567b28SAmjad Ouled-Ameur 			spicc->pins_idle_high = NULL;
189f4567b28SAmjad Ouled-Ameur 		}
190f4567b28SAmjad Ouled-Ameur 		spicc->pins_idle_low = pinctrl_lookup_state(spicc->pinctrl,
191f4567b28SAmjad Ouled-Ameur 							     "idle-low");
192f4567b28SAmjad Ouled-Ameur 		if (IS_ERR(spicc->pins_idle_low)) {
193f4567b28SAmjad Ouled-Ameur 			dev_warn(&spicc->pdev->dev, "can't get idle-low pinctrl\n");
194f4567b28SAmjad Ouled-Ameur 			spicc->pins_idle_low = NULL;
195f4567b28SAmjad Ouled-Ameur 		}
196a6cda1f9SSunny Luo 		return;
197f4567b28SAmjad Ouled-Ameur 	}
198a6cda1f9SSunny Luo 
199a6cda1f9SSunny Luo 	conf = readl_relaxed(spicc->base + SPICC_ENH_CTL0) |
200a6cda1f9SSunny Luo 		SPICC_ENH_MOSI_OEN | SPICC_ENH_CLK_OEN | SPICC_ENH_CS_OEN;
201a6cda1f9SSunny Luo 
202a6cda1f9SSunny Luo 	writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
203a6cda1f9SSunny Luo }
204a6cda1f9SSunny Luo 
meson_spicc_txfull(struct meson_spicc_device * spicc)205454fa271SNeil Armstrong static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
206454fa271SNeil Armstrong {
207454fa271SNeil Armstrong 	return !!FIELD_GET(SPICC_TF,
208454fa271SNeil Armstrong 			   readl_relaxed(spicc->base + SPICC_STATREG));
209454fa271SNeil Armstrong }
210454fa271SNeil Armstrong 
meson_spicc_rxready(struct meson_spicc_device * spicc)211454fa271SNeil Armstrong static inline bool meson_spicc_rxready(struct meson_spicc_device *spicc)
212454fa271SNeil Armstrong {
2130eb707acSNeil Armstrong 	return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF,
214454fa271SNeil Armstrong 			 readl_relaxed(spicc->base + SPICC_STATREG));
215454fa271SNeil Armstrong }
216454fa271SNeil Armstrong 
meson_spicc_pull_data(struct meson_spicc_device * spicc)217454fa271SNeil Armstrong static inline u32 meson_spicc_pull_data(struct meson_spicc_device *spicc)
218454fa271SNeil Armstrong {
219454fa271SNeil Armstrong 	unsigned int bytes = spicc->bytes_per_word;
220454fa271SNeil Armstrong 	unsigned int byte_shift = 0;
221454fa271SNeil Armstrong 	u32 data = 0;
222454fa271SNeil Armstrong 	u8 byte;
223454fa271SNeil Armstrong 
224454fa271SNeil Armstrong 	while (bytes--) {
225454fa271SNeil Armstrong 		byte = *spicc->tx_buf++;
226454fa271SNeil Armstrong 		data |= (byte & 0xff) << byte_shift;
227454fa271SNeil Armstrong 		byte_shift += 8;
228454fa271SNeil Armstrong 	}
229454fa271SNeil Armstrong 
230454fa271SNeil Armstrong 	spicc->tx_remain--;
231454fa271SNeil Armstrong 	return data;
232454fa271SNeil Armstrong }
233454fa271SNeil Armstrong 
meson_spicc_push_data(struct meson_spicc_device * spicc,u32 data)234454fa271SNeil Armstrong static inline void meson_spicc_push_data(struct meson_spicc_device *spicc,
235454fa271SNeil Armstrong 					 u32 data)
236454fa271SNeil Armstrong {
237454fa271SNeil Armstrong 	unsigned int bytes = spicc->bytes_per_word;
238454fa271SNeil Armstrong 	unsigned int byte_shift = 0;
239454fa271SNeil Armstrong 	u8 byte;
240454fa271SNeil Armstrong 
241454fa271SNeil Armstrong 	while (bytes--) {
242454fa271SNeil Armstrong 		byte = (data >> byte_shift) & 0xff;
243454fa271SNeil Armstrong 		*spicc->rx_buf++ = byte;
244454fa271SNeil Armstrong 		byte_shift += 8;
245454fa271SNeil Armstrong 	}
246454fa271SNeil Armstrong 
247454fa271SNeil Armstrong 	spicc->rx_remain--;
248454fa271SNeil Armstrong }
249454fa271SNeil Armstrong 
meson_spicc_rx(struct meson_spicc_device * spicc)250454fa271SNeil Armstrong static inline void meson_spicc_rx(struct meson_spicc_device *spicc)
251454fa271SNeil Armstrong {
252454fa271SNeil Armstrong 	/* Empty RX FIFO */
253454fa271SNeil Armstrong 	while (spicc->rx_remain &&
254454fa271SNeil Armstrong 	       meson_spicc_rxready(spicc))
255454fa271SNeil Armstrong 		meson_spicc_push_data(spicc,
256454fa271SNeil Armstrong 				readl_relaxed(spicc->base + SPICC_RXDATA));
257454fa271SNeil Armstrong }
258454fa271SNeil Armstrong 
meson_spicc_tx(struct meson_spicc_device * spicc)259454fa271SNeil Armstrong static inline void meson_spicc_tx(struct meson_spicc_device *spicc)
260454fa271SNeil Armstrong {
261454fa271SNeil Armstrong 	/* Fill Up TX FIFO */
262454fa271SNeil Armstrong 	while (spicc->tx_remain &&
263454fa271SNeil Armstrong 	       !meson_spicc_txfull(spicc))
264454fa271SNeil Armstrong 		writel_relaxed(meson_spicc_pull_data(spicc),
265454fa271SNeil Armstrong 			       spicc->base + SPICC_TXDATA);
266454fa271SNeil Armstrong }
267454fa271SNeil Armstrong 
meson_spicc_setup_burst(struct meson_spicc_device * spicc)2680eb707acSNeil Armstrong static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc)
269454fa271SNeil Armstrong {
270454fa271SNeil Armstrong 
2710eb707acSNeil Armstrong 	unsigned int burst_len = min_t(unsigned int,
2720eb707acSNeil Armstrong 				       spicc->xfer_remain /
2730eb707acSNeil Armstrong 				       spicc->bytes_per_word,
2740eb707acSNeil Armstrong 				       spicc->data->fifo_size);
275454fa271SNeil Armstrong 	/* Setup Xfer variables */
276454fa271SNeil Armstrong 	spicc->tx_remain = burst_len;
277454fa271SNeil Armstrong 	spicc->rx_remain = burst_len;
278454fa271SNeil Armstrong 	spicc->xfer_remain -= burst_len * spicc->bytes_per_word;
279454fa271SNeil Armstrong 
280454fa271SNeil Armstrong 	/* Setup burst length */
281454fa271SNeil Armstrong 	writel_bits_relaxed(SPICC_BURSTLENGTH_MASK,
282454fa271SNeil Armstrong 			FIELD_PREP(SPICC_BURSTLENGTH_MASK,
2830eb707acSNeil Armstrong 				burst_len - 1),
284454fa271SNeil Armstrong 			spicc->base + SPICC_CONREG);
285454fa271SNeil Armstrong 
286454fa271SNeil Armstrong 	/* Fill TX FIFO */
287454fa271SNeil Armstrong 	meson_spicc_tx(spicc);
288454fa271SNeil Armstrong }
289454fa271SNeil Armstrong 
meson_spicc_irq(int irq,void * data)290454fa271SNeil Armstrong static irqreturn_t meson_spicc_irq(int irq, void *data)
291454fa271SNeil Armstrong {
292454fa271SNeil Armstrong 	struct meson_spicc_device *spicc = (void *) data;
293454fa271SNeil Armstrong 
2940eb707acSNeil Armstrong 	writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
295454fa271SNeil Armstrong 
296454fa271SNeil Armstrong 	/* Empty RX FIFO */
297454fa271SNeil Armstrong 	meson_spicc_rx(spicc);
298454fa271SNeil Armstrong 
2990eb707acSNeil Armstrong 	if (!spicc->xfer_remain) {
300454fa271SNeil Armstrong 		/* Disable all IRQs */
301454fa271SNeil Armstrong 		writel(0, spicc->base + SPICC_INTREG);
302454fa271SNeil Armstrong 
30304694e50SNeil Armstrong 		complete(&spicc->done);
304454fa271SNeil Armstrong 
305454fa271SNeil Armstrong 		return IRQ_HANDLED;
306454fa271SNeil Armstrong 	}
307454fa271SNeil Armstrong 
308454fa271SNeil Armstrong 	/* Setup burst */
3090eb707acSNeil Armstrong 	meson_spicc_setup_burst(spicc);
310454fa271SNeil Armstrong 
3110eb707acSNeil Armstrong 	/* Start burst */
3120eb707acSNeil Armstrong 	writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
313454fa271SNeil Armstrong 
314454fa271SNeil Armstrong 	return IRQ_HANDLED;
315454fa271SNeil Armstrong }
316454fa271SNeil Armstrong 
meson_spicc_auto_io_delay(struct meson_spicc_device * spicc)317f27bff47SNeil Armstrong static void meson_spicc_auto_io_delay(struct meson_spicc_device *spicc)
318f27bff47SNeil Armstrong {
319f27bff47SNeil Armstrong 	u32 div, hz;
320f27bff47SNeil Armstrong 	u32 mi_delay, cap_delay;
321f27bff47SNeil Armstrong 	u32 conf;
322f27bff47SNeil Armstrong 
323f27bff47SNeil Armstrong 	if (spicc->data->has_enhance_clk_div) {
324f27bff47SNeil Armstrong 		div = FIELD_GET(SPICC_ENH_DATARATE_MASK,
325f27bff47SNeil Armstrong 				readl_relaxed(spicc->base + SPICC_ENH_CTL0));
326f27bff47SNeil Armstrong 		div++;
327f27bff47SNeil Armstrong 		div <<= 1;
328f27bff47SNeil Armstrong 	} else {
329f27bff47SNeil Armstrong 		div = FIELD_GET(SPICC_DATARATE_MASK,
330f27bff47SNeil Armstrong 				readl_relaxed(spicc->base + SPICC_CONREG));
331f27bff47SNeil Armstrong 		div += 2;
332f27bff47SNeil Armstrong 		div = 1 << div;
333f27bff47SNeil Armstrong 	}
334f27bff47SNeil Armstrong 
335f27bff47SNeil Armstrong 	mi_delay = SPICC_MI_NO_DELAY;
336f27bff47SNeil Armstrong 	cap_delay = SPICC_CAP_AHEAD_2_CYCLE;
337f27bff47SNeil Armstrong 	hz = clk_get_rate(spicc->clk);
338f27bff47SNeil Armstrong 
339f27bff47SNeil Armstrong 	if (hz >= 100000000)
340f27bff47SNeil Armstrong 		cap_delay = SPICC_CAP_DELAY_1_CYCLE;
341f27bff47SNeil Armstrong 	else if (hz >= 80000000)
342f27bff47SNeil Armstrong 		cap_delay = SPICC_CAP_NO_DELAY;
343f27bff47SNeil Armstrong 	else if (hz >= 40000000)
344f27bff47SNeil Armstrong 		cap_delay = SPICC_CAP_AHEAD_1_CYCLE;
345f27bff47SNeil Armstrong 	else if (div >= 16)
346f27bff47SNeil Armstrong 		mi_delay = SPICC_MI_DELAY_3_CYCLE;
347f27bff47SNeil Armstrong 	else if (div >= 8)
348f27bff47SNeil Armstrong 		mi_delay = SPICC_MI_DELAY_2_CYCLE;
349f27bff47SNeil Armstrong 	else if (div >= 6)
350f27bff47SNeil Armstrong 		mi_delay = SPICC_MI_DELAY_1_CYCLE;
351f27bff47SNeil Armstrong 
352f27bff47SNeil Armstrong 	conf = readl_relaxed(spicc->base + SPICC_TESTREG);
353f27bff47SNeil Armstrong 	conf &= ~(SPICC_MO_DELAY_MASK | SPICC_MI_DELAY_MASK
354f27bff47SNeil Armstrong 		  | SPICC_MI_CAP_DELAY_MASK);
355f27bff47SNeil Armstrong 	conf |= FIELD_PREP(SPICC_MI_DELAY_MASK, mi_delay);
356f27bff47SNeil Armstrong 	conf |= FIELD_PREP(SPICC_MI_CAP_DELAY_MASK, cap_delay);
357f27bff47SNeil Armstrong 	writel_relaxed(conf, spicc->base + SPICC_TESTREG);
358f27bff47SNeil Armstrong }
359f27bff47SNeil Armstrong 
meson_spicc_setup_xfer(struct meson_spicc_device * spicc,struct spi_transfer * xfer)360454fa271SNeil Armstrong static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc,
361454fa271SNeil Armstrong 				   struct spi_transfer *xfer)
362454fa271SNeil Armstrong {
363454fa271SNeil Armstrong 	u32 conf, conf_orig;
364454fa271SNeil Armstrong 
365454fa271SNeil Armstrong 	/* Read original configuration */
366454fa271SNeil Armstrong 	conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG);
367454fa271SNeil Armstrong 
368454fa271SNeil Armstrong 	/* Setup word width */
369454fa271SNeil Armstrong 	conf &= ~SPICC_BITLENGTH_MASK;
370454fa271SNeil Armstrong 	conf |= FIELD_PREP(SPICC_BITLENGTH_MASK,
371454fa271SNeil Armstrong 			   (spicc->bytes_per_word << 3) - 1);
372454fa271SNeil Armstrong 
373454fa271SNeil Armstrong 	/* Ignore if unchanged */
374454fa271SNeil Armstrong 	if (conf != conf_orig)
375454fa271SNeil Armstrong 		writel_relaxed(conf, spicc->base + SPICC_CONREG);
3763e0cf4d3SSunny Luo 
3773e0cf4d3SSunny Luo 	clk_set_rate(spicc->clk, xfer->speed_hz);
378f27bff47SNeil Armstrong 
379f27bff47SNeil Armstrong 	meson_spicc_auto_io_delay(spicc);
3800eb707acSNeil Armstrong 
3810eb707acSNeil Armstrong 	writel_relaxed(0, spicc->base + SPICC_DMAREG);
3820eb707acSNeil Armstrong }
3830eb707acSNeil Armstrong 
meson_spicc_reset_fifo(struct meson_spicc_device * spicc)3840eb707acSNeil Armstrong static void meson_spicc_reset_fifo(struct meson_spicc_device *spicc)
3850eb707acSNeil Armstrong {
3860eb707acSNeil Armstrong 	if (spicc->data->has_oen)
3870eb707acSNeil Armstrong 		writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO,
3880eb707acSNeil Armstrong 				    SPICC_ENH_MAIN_CLK_AO,
3890eb707acSNeil Armstrong 				    spicc->base + SPICC_ENH_CTL0);
3900eb707acSNeil Armstrong 
3910eb707acSNeil Armstrong 	writel_bits_relaxed(SPICC_FIFORST_W1_MASK, SPICC_FIFORST_W1_MASK,
3920eb707acSNeil Armstrong 			    spicc->base + SPICC_TESTREG);
3930eb707acSNeil Armstrong 
3940eb707acSNeil Armstrong 	while (meson_spicc_rxready(spicc))
395d9b883aeSLee Jones 		readl_relaxed(spicc->base + SPICC_RXDATA);
3960eb707acSNeil Armstrong 
3970eb707acSNeil Armstrong 	if (spicc->data->has_oen)
3980eb707acSNeil Armstrong 		writel_bits_relaxed(SPICC_ENH_MAIN_CLK_AO, 0,
3990eb707acSNeil Armstrong 				    spicc->base + SPICC_ENH_CTL0);
400454fa271SNeil Armstrong }
401454fa271SNeil Armstrong 
meson_spicc_transfer_one(struct spi_master * master,struct spi_device * spi,struct spi_transfer * xfer)402454fa271SNeil Armstrong static int meson_spicc_transfer_one(struct spi_master *master,
403454fa271SNeil Armstrong 				    struct spi_device *spi,
404454fa271SNeil Armstrong 				    struct spi_transfer *xfer)
405454fa271SNeil Armstrong {
406454fa271SNeil Armstrong 	struct meson_spicc_device *spicc = spi_master_get_devdata(master);
407134af9aaSNeil Armstrong 	uint64_t timeout;
408454fa271SNeil Armstrong 
409454fa271SNeil Armstrong 	/* Store current transfer */
410454fa271SNeil Armstrong 	spicc->xfer = xfer;
411454fa271SNeil Armstrong 
412454fa271SNeil Armstrong 	/* Setup transfer parameters */
413454fa271SNeil Armstrong 	spicc->tx_buf = (u8 *)xfer->tx_buf;
414454fa271SNeil Armstrong 	spicc->rx_buf = (u8 *)xfer->rx_buf;
415454fa271SNeil Armstrong 	spicc->xfer_remain = xfer->len;
416454fa271SNeil Armstrong 
417454fa271SNeil Armstrong 	/* Pre-calculate word size */
418454fa271SNeil Armstrong 	spicc->bytes_per_word =
419454fa271SNeil Armstrong 	   DIV_ROUND_UP(spicc->xfer->bits_per_word, 8);
420454fa271SNeil Armstrong 
4210eb707acSNeil Armstrong 	if (xfer->len % spicc->bytes_per_word)
4220eb707acSNeil Armstrong 		return -EINVAL;
4230eb707acSNeil Armstrong 
424454fa271SNeil Armstrong 	/* Setup transfer parameters */
425454fa271SNeil Armstrong 	meson_spicc_setup_xfer(spicc, xfer);
426454fa271SNeil Armstrong 
4270eb707acSNeil Armstrong 	meson_spicc_reset_fifo(spicc);
428454fa271SNeil Armstrong 
4290eb707acSNeil Armstrong 	/* Setup burst */
4300eb707acSNeil Armstrong 	meson_spicc_setup_burst(spicc);
431454fa271SNeil Armstrong 
43204694e50SNeil Armstrong 	/* Setup wait for completion */
43304694e50SNeil Armstrong 	reinit_completion(&spicc->done);
43404694e50SNeil Armstrong 
43504694e50SNeil Armstrong 	/* For each byte we wait for 8 cycles of the SPI clock */
43604694e50SNeil Armstrong 	timeout = 8LL * MSEC_PER_SEC * xfer->len;
43704694e50SNeil Armstrong 	do_div(timeout, xfer->speed_hz);
43804694e50SNeil Armstrong 
43904694e50SNeil Armstrong 	/* Add 10us delay between each fifo bursts */
44004694e50SNeil Armstrong 	timeout += ((xfer->len >> 4) * 10) / MSEC_PER_SEC;
44104694e50SNeil Armstrong 
44204694e50SNeil Armstrong 	/* Increase it twice and add 200 ms tolerance */
44304694e50SNeil Armstrong 	timeout += timeout + 200;
44404694e50SNeil Armstrong 
445454fa271SNeil Armstrong 	/* Start burst */
446454fa271SNeil Armstrong 	writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
447454fa271SNeil Armstrong 
448454fa271SNeil Armstrong 	/* Enable interrupts */
4490eb707acSNeil Armstrong 	writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
450454fa271SNeil Armstrong 
45104694e50SNeil Armstrong 	if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout)))
45204694e50SNeil Armstrong 		return -ETIMEDOUT;
45304694e50SNeil Armstrong 
45404694e50SNeil Armstrong 	return 0;
455454fa271SNeil Armstrong }
456454fa271SNeil Armstrong 
meson_spicc_prepare_message(struct spi_master * master,struct spi_message * message)457454fa271SNeil Armstrong static int meson_spicc_prepare_message(struct spi_master *master,
458454fa271SNeil Armstrong 				       struct spi_message *message)
459454fa271SNeil Armstrong {
460454fa271SNeil Armstrong 	struct meson_spicc_device *spicc = spi_master_get_devdata(master);
461454fa271SNeil Armstrong 	struct spi_device *spi = message->spi;
46209992025SNeil Armstrong 	u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK;
463454fa271SNeil Armstrong 
464454fa271SNeil Armstrong 	/* Store current message */
465454fa271SNeil Armstrong 	spicc->message = message;
466454fa271SNeil Armstrong 
467454fa271SNeil Armstrong 	/* Enable Master */
468454fa271SNeil Armstrong 	conf |= SPICC_ENABLE;
469454fa271SNeil Armstrong 	conf |= SPICC_MODE_MASTER;
470454fa271SNeil Armstrong 
471454fa271SNeil Armstrong 	/* SMC = 0 */
472454fa271SNeil Armstrong 
473454fa271SNeil Armstrong 	/* Setup transfer mode */
474454fa271SNeil Armstrong 	if (spi->mode & SPI_CPOL)
475454fa271SNeil Armstrong 		conf |= SPICC_POL;
476454fa271SNeil Armstrong 	else
477454fa271SNeil Armstrong 		conf &= ~SPICC_POL;
478454fa271SNeil Armstrong 
479f4567b28SAmjad Ouled-Ameur 	if (!spicc->data->has_oen) {
480f4567b28SAmjad Ouled-Ameur 		if (spi->mode & SPI_CPOL) {
481f4567b28SAmjad Ouled-Ameur 			if (spicc->pins_idle_high)
482f4567b28SAmjad Ouled-Ameur 				pinctrl_select_state(spicc->pinctrl, spicc->pins_idle_high);
483f4567b28SAmjad Ouled-Ameur 		} else {
484f4567b28SAmjad Ouled-Ameur 			if (spicc->pins_idle_low)
485f4567b28SAmjad Ouled-Ameur 				pinctrl_select_state(spicc->pinctrl, spicc->pins_idle_low);
486f4567b28SAmjad Ouled-Ameur 		}
487f4567b28SAmjad Ouled-Ameur 	}
488f4567b28SAmjad Ouled-Ameur 
489454fa271SNeil Armstrong 	if (spi->mode & SPI_CPHA)
490454fa271SNeil Armstrong 		conf |= SPICC_PHA;
491454fa271SNeil Armstrong 	else
492454fa271SNeil Armstrong 		conf &= ~SPICC_PHA;
493454fa271SNeil Armstrong 
494454fa271SNeil Armstrong 	/* SSCTL = 0 */
495454fa271SNeil Armstrong 
496454fa271SNeil Armstrong 	if (spi->mode & SPI_CS_HIGH)
497454fa271SNeil Armstrong 		conf |= SPICC_SSPOL;
498454fa271SNeil Armstrong 	else
499454fa271SNeil Armstrong 		conf &= ~SPICC_SSPOL;
500454fa271SNeil Armstrong 
501454fa271SNeil Armstrong 	if (spi->mode & SPI_READY)
502454fa271SNeil Armstrong 		conf |= FIELD_PREP(SPICC_DRCTL_MASK, SPICC_DRCTL_LOWLEVEL);
503454fa271SNeil Armstrong 	else
504454fa271SNeil Armstrong 		conf |= FIELD_PREP(SPICC_DRCTL_MASK, SPICC_DRCTL_IGNORE);
505454fa271SNeil Armstrong 
506454fa271SNeil Armstrong 	/* Select CS */
5079e264f3fSAmit Kumar Mahapatra via Alsa-devel 	conf |= FIELD_PREP(SPICC_CS_MASK, spi_get_chipselect(spi, 0));
508454fa271SNeil Armstrong 
509454fa271SNeil Armstrong 	/* Default 8bit word */
510454fa271SNeil Armstrong 	conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, 8 - 1);
511454fa271SNeil Armstrong 
512454fa271SNeil Armstrong 	writel_relaxed(conf, spicc->base + SPICC_CONREG);
513454fa271SNeil Armstrong 
514454fa271SNeil Armstrong 	/* Setup no wait cycles by default */
515454fa271SNeil Armstrong 	writel_relaxed(0, spicc->base + SPICC_PERIODREG);
516454fa271SNeil Armstrong 
5170eb707acSNeil Armstrong 	writel_bits_relaxed(SPICC_LBC_W1, 0, spicc->base + SPICC_TESTREG);
518454fa271SNeil Armstrong 
519454fa271SNeil Armstrong 	return 0;
520454fa271SNeil Armstrong }
521454fa271SNeil Armstrong 
meson_spicc_unprepare_transfer(struct spi_master * master)522454fa271SNeil Armstrong static int meson_spicc_unprepare_transfer(struct spi_master *master)
523454fa271SNeil Armstrong {
524454fa271SNeil Armstrong 	struct meson_spicc_device *spicc = spi_master_get_devdata(master);
52509992025SNeil Armstrong 	u32 conf = readl_relaxed(spicc->base + SPICC_CONREG) & SPICC_DATARATE_MASK;
526454fa271SNeil Armstrong 
527454fa271SNeil Armstrong 	/* Disable all IRQs */
528454fa271SNeil Armstrong 	writel(0, spicc->base + SPICC_INTREG);
529454fa271SNeil Armstrong 
530454fa271SNeil Armstrong 	device_reset_optional(&spicc->pdev->dev);
531454fa271SNeil Armstrong 
53209992025SNeil Armstrong 	/* Set default configuration, keeping datarate field */
53309992025SNeil Armstrong 	writel_relaxed(conf, spicc->base + SPICC_CONREG);
53409992025SNeil Armstrong 
535f4567b28SAmjad Ouled-Ameur 	if (!spicc->data->has_oen)
536f4567b28SAmjad Ouled-Ameur 		pinctrl_select_default_state(&spicc->pdev->dev);
537f4567b28SAmjad Ouled-Ameur 
538454fa271SNeil Armstrong 	return 0;
539454fa271SNeil Armstrong }
540454fa271SNeil Armstrong 
meson_spicc_setup(struct spi_device * spi)541454fa271SNeil Armstrong static int meson_spicc_setup(struct spi_device *spi)
542454fa271SNeil Armstrong {
543454fa271SNeil Armstrong 	if (!spi->controller_state)
544454fa271SNeil Armstrong 		spi->controller_state = spi_master_get_devdata(spi->master);
545cd8fb859SLinus Walleij 
546454fa271SNeil Armstrong 	return 0;
547454fa271SNeil Armstrong }
548454fa271SNeil Armstrong 
meson_spicc_cleanup(struct spi_device * spi)549454fa271SNeil Armstrong static void meson_spicc_cleanup(struct spi_device *spi)
550454fa271SNeil Armstrong {
551454fa271SNeil Armstrong 	spi->controller_state = NULL;
552454fa271SNeil Armstrong }
553454fa271SNeil Armstrong 
5543e0cf4d3SSunny Luo /*
5553e0cf4d3SSunny Luo  * The Clock Mux
5563e0cf4d3SSunny Luo  *            x-----------------x   x------------x    x------\
5573e0cf4d3SSunny Luo  *        |---| pow2 fixed div  |---| pow2 div   |----|      |
5583e0cf4d3SSunny Luo  *        |   x-----------------x   x------------x    |      |
5593e0cf4d3SSunny Luo  * src ---|                                           | mux  |-- out
5603e0cf4d3SSunny Luo  *        |   x-----------------x   x------------x    |      |
5613e0cf4d3SSunny Luo  *        |---| enh fixed div   |---| enh div    |0---|      |
5623e0cf4d3SSunny Luo  *            x-----------------x   x------------x    x------/
5633e0cf4d3SSunny Luo  *
5643e0cf4d3SSunny Luo  * Clk path for GX series:
5653e0cf4d3SSunny Luo  *    src -> pow2 fixed div -> pow2 div -> out
5663e0cf4d3SSunny Luo  *
5673e0cf4d3SSunny Luo  * Clk path for AXG series:
5683e0cf4d3SSunny Luo  *    src -> pow2 fixed div -> pow2 div -> mux -> out
5693e0cf4d3SSunny Luo  *    src -> enh fixed div -> enh div -> mux -> out
5704e3d3220SNeil Armstrong  *
5714e3d3220SNeil Armstrong  * Clk path for G12A series:
5724e3d3220SNeil Armstrong  *    pclk -> pow2 fixed div -> pow2 div -> mux -> out
5734e3d3220SNeil Armstrong  *    pclk -> enh fixed div -> enh div -> mux -> out
57409992025SNeil Armstrong  *
57509992025SNeil Armstrong  * The pow2 divider is tied to the controller HW state, and the
57609992025SNeil Armstrong  * divider is only valid when the controller is initialized.
57709992025SNeil Armstrong  *
57809992025SNeil Armstrong  * A set of clock ops is added to make sure we don't read/set this
57909992025SNeil Armstrong  * clock rate while the controller is in an unknown state.
5803e0cf4d3SSunny Luo  */
5813e0cf4d3SSunny Luo 
meson_spicc_pow2_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)58209992025SNeil Armstrong static unsigned long meson_spicc_pow2_recalc_rate(struct clk_hw *hw,
58309992025SNeil Armstrong 						  unsigned long parent_rate)
58409992025SNeil Armstrong {
58509992025SNeil Armstrong 	struct clk_divider *divider = to_clk_divider(hw);
58609992025SNeil Armstrong 	struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
58709992025SNeil Armstrong 
58836acf80fSNeil Armstrong 	if (!spicc->master->cur_msg)
58909992025SNeil Armstrong 		return 0;
59009992025SNeil Armstrong 
59109992025SNeil Armstrong 	return clk_divider_ops.recalc_rate(hw, parent_rate);
59209992025SNeil Armstrong }
59309992025SNeil Armstrong 
meson_spicc_pow2_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)59409992025SNeil Armstrong static int meson_spicc_pow2_determine_rate(struct clk_hw *hw,
59509992025SNeil Armstrong 					   struct clk_rate_request *req)
59609992025SNeil Armstrong {
59709992025SNeil Armstrong 	struct clk_divider *divider = to_clk_divider(hw);
59809992025SNeil Armstrong 	struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
59909992025SNeil Armstrong 
60036acf80fSNeil Armstrong 	if (!spicc->master->cur_msg)
60109992025SNeil Armstrong 		return -EINVAL;
60209992025SNeil Armstrong 
60309992025SNeil Armstrong 	return clk_divider_ops.determine_rate(hw, req);
60409992025SNeil Armstrong }
60509992025SNeil Armstrong 
meson_spicc_pow2_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)60609992025SNeil Armstrong static int meson_spicc_pow2_set_rate(struct clk_hw *hw, unsigned long rate,
60709992025SNeil Armstrong 				     unsigned long parent_rate)
60809992025SNeil Armstrong {
60909992025SNeil Armstrong 	struct clk_divider *divider = to_clk_divider(hw);
61009992025SNeil Armstrong 	struct meson_spicc_device *spicc = pow2_clk_to_spicc(divider);
61109992025SNeil Armstrong 
61236acf80fSNeil Armstrong 	if (!spicc->master->cur_msg)
61309992025SNeil Armstrong 		return -EINVAL;
61409992025SNeil Armstrong 
61509992025SNeil Armstrong 	return clk_divider_ops.set_rate(hw, rate, parent_rate);
61609992025SNeil Armstrong }
61709992025SNeil Armstrong 
618077dac34SWei Yongjun static const struct clk_ops meson_spicc_pow2_clk_ops = {
61909992025SNeil Armstrong 	.recalc_rate = meson_spicc_pow2_recalc_rate,
62009992025SNeil Armstrong 	.determine_rate = meson_spicc_pow2_determine_rate,
62109992025SNeil Armstrong 	.set_rate = meson_spicc_pow2_set_rate,
62209992025SNeil Armstrong };
62309992025SNeil Armstrong 
meson_spicc_pow2_clk_init(struct meson_spicc_device * spicc)62409992025SNeil Armstrong static int meson_spicc_pow2_clk_init(struct meson_spicc_device *spicc)
6253e0cf4d3SSunny Luo {
6263e0cf4d3SSunny Luo 	struct device *dev = &spicc->pdev->dev;
62709992025SNeil Armstrong 	struct clk_fixed_factor *pow2_fixed_div;
6283e0cf4d3SSunny Luo 	struct clk_init_data init;
6293e0cf4d3SSunny Luo 	struct clk *clk;
6303e0cf4d3SSunny Luo 	struct clk_parent_data parent_data[2];
6313e0cf4d3SSunny Luo 	char name[64];
6323e0cf4d3SSunny Luo 
6333e0cf4d3SSunny Luo 	memset(&init, 0, sizeof(init));
6343e0cf4d3SSunny Luo 	memset(&parent_data, 0, sizeof(parent_data));
6353e0cf4d3SSunny Luo 
6363e0cf4d3SSunny Luo 	init.parent_data = parent_data;
6373e0cf4d3SSunny Luo 
6383e0cf4d3SSunny Luo 	/* algorithm for pow2 div: rate = freq / 4 / (2 ^ N) */
6393e0cf4d3SSunny Luo 
6403e0cf4d3SSunny Luo 	pow2_fixed_div = devm_kzalloc(dev, sizeof(*pow2_fixed_div), GFP_KERNEL);
6413e0cf4d3SSunny Luo 	if (!pow2_fixed_div)
6423e0cf4d3SSunny Luo 		return -ENOMEM;
6433e0cf4d3SSunny Luo 
6443e0cf4d3SSunny Luo 	snprintf(name, sizeof(name), "%s#pow2_fixed_div", dev_name(dev));
6453e0cf4d3SSunny Luo 	init.name = name;
6463e0cf4d3SSunny Luo 	init.ops = &clk_fixed_factor_ops;
6473e0cf4d3SSunny Luo 	init.flags = 0;
6484e3d3220SNeil Armstrong 	if (spicc->data->has_pclk)
6494e3d3220SNeil Armstrong 		parent_data[0].hw = __clk_get_hw(spicc->pclk);
6504e3d3220SNeil Armstrong 	else
6513e0cf4d3SSunny Luo 		parent_data[0].hw = __clk_get_hw(spicc->core);
6523e0cf4d3SSunny Luo 	init.num_parents = 1;
6533e0cf4d3SSunny Luo 
6543e0cf4d3SSunny Luo 	pow2_fixed_div->mult = 1,
6553e0cf4d3SSunny Luo 	pow2_fixed_div->div = 4,
6563e0cf4d3SSunny Luo 	pow2_fixed_div->hw.init = &init;
6573e0cf4d3SSunny Luo 
6583e0cf4d3SSunny Luo 	clk = devm_clk_register(dev, &pow2_fixed_div->hw);
6593e0cf4d3SSunny Luo 	if (WARN_ON(IS_ERR(clk)))
6603e0cf4d3SSunny Luo 		return PTR_ERR(clk);
6613e0cf4d3SSunny Luo 
6623e0cf4d3SSunny Luo 	snprintf(name, sizeof(name), "%s#pow2_div", dev_name(dev));
6633e0cf4d3SSunny Luo 	init.name = name;
66409992025SNeil Armstrong 	init.ops = &meson_spicc_pow2_clk_ops;
66509992025SNeil Armstrong 	/*
66609992025SNeil Armstrong 	 * Set NOCACHE here to make sure we read the actual HW value
66709992025SNeil Armstrong 	 * since we reset the HW after each transfer.
66809992025SNeil Armstrong 	 */
66909992025SNeil Armstrong 	init.flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE;
6703e0cf4d3SSunny Luo 	parent_data[0].hw = &pow2_fixed_div->hw;
6713e0cf4d3SSunny Luo 	init.num_parents = 1;
6723e0cf4d3SSunny Luo 
67309992025SNeil Armstrong 	spicc->pow2_div.shift = 16,
67409992025SNeil Armstrong 	spicc->pow2_div.width = 3,
67509992025SNeil Armstrong 	spicc->pow2_div.flags = CLK_DIVIDER_POWER_OF_TWO,
67609992025SNeil Armstrong 	spicc->pow2_div.reg = spicc->base + SPICC_CONREG;
67709992025SNeil Armstrong 	spicc->pow2_div.hw.init = &init;
6783e0cf4d3SSunny Luo 
67909992025SNeil Armstrong 	spicc->clk = devm_clk_register(dev, &spicc->pow2_div.hw);
68009992025SNeil Armstrong 	if (WARN_ON(IS_ERR(spicc->clk)))
68109992025SNeil Armstrong 		return PTR_ERR(spicc->clk);
6823e0cf4d3SSunny Luo 
6833e0cf4d3SSunny Luo 	return 0;
6843e0cf4d3SSunny Luo }
6853e0cf4d3SSunny Luo 
meson_spicc_enh_clk_init(struct meson_spicc_device * spicc)68609992025SNeil Armstrong static int meson_spicc_enh_clk_init(struct meson_spicc_device *spicc)
68709992025SNeil Armstrong {
68809992025SNeil Armstrong 	struct device *dev = &spicc->pdev->dev;
68909992025SNeil Armstrong 	struct clk_fixed_factor *enh_fixed_div;
69009992025SNeil Armstrong 	struct clk_divider *enh_div;
69109992025SNeil Armstrong 	struct clk_mux *mux;
69209992025SNeil Armstrong 	struct clk_init_data init;
69309992025SNeil Armstrong 	struct clk *clk;
69409992025SNeil Armstrong 	struct clk_parent_data parent_data[2];
69509992025SNeil Armstrong 	char name[64];
69609992025SNeil Armstrong 
69709992025SNeil Armstrong 	memset(&init, 0, sizeof(init));
69809992025SNeil Armstrong 	memset(&parent_data, 0, sizeof(parent_data));
69909992025SNeil Armstrong 
70009992025SNeil Armstrong 	init.parent_data = parent_data;
70109992025SNeil Armstrong 
7023e0cf4d3SSunny Luo 	/* algorithm for enh div: rate = freq / 2 / (N + 1) */
7033e0cf4d3SSunny Luo 
7043e0cf4d3SSunny Luo 	enh_fixed_div = devm_kzalloc(dev, sizeof(*enh_fixed_div), GFP_KERNEL);
7053e0cf4d3SSunny Luo 	if (!enh_fixed_div)
7063e0cf4d3SSunny Luo 		return -ENOMEM;
7073e0cf4d3SSunny Luo 
7083e0cf4d3SSunny Luo 	snprintf(name, sizeof(name), "%s#enh_fixed_div", dev_name(dev));
7093e0cf4d3SSunny Luo 	init.name = name;
7103e0cf4d3SSunny Luo 	init.ops = &clk_fixed_factor_ops;
7113e0cf4d3SSunny Luo 	init.flags = 0;
7124e3d3220SNeil Armstrong 	if (spicc->data->has_pclk)
7134e3d3220SNeil Armstrong 		parent_data[0].hw = __clk_get_hw(spicc->pclk);
7144e3d3220SNeil Armstrong 	else
7153e0cf4d3SSunny Luo 		parent_data[0].hw = __clk_get_hw(spicc->core);
7163e0cf4d3SSunny Luo 	init.num_parents = 1;
7173e0cf4d3SSunny Luo 
7183e0cf4d3SSunny Luo 	enh_fixed_div->mult = 1,
7193e0cf4d3SSunny Luo 	enh_fixed_div->div = 2,
7203e0cf4d3SSunny Luo 	enh_fixed_div->hw.init = &init;
7213e0cf4d3SSunny Luo 
7223e0cf4d3SSunny Luo 	clk = devm_clk_register(dev, &enh_fixed_div->hw);
7233e0cf4d3SSunny Luo 	if (WARN_ON(IS_ERR(clk)))
7243e0cf4d3SSunny Luo 		return PTR_ERR(clk);
7253e0cf4d3SSunny Luo 
7263e0cf4d3SSunny Luo 	enh_div = devm_kzalloc(dev, sizeof(*enh_div), GFP_KERNEL);
7273e0cf4d3SSunny Luo 	if (!enh_div)
7283e0cf4d3SSunny Luo 		return -ENOMEM;
7293e0cf4d3SSunny Luo 
7303e0cf4d3SSunny Luo 	snprintf(name, sizeof(name), "%s#enh_div", dev_name(dev));
7313e0cf4d3SSunny Luo 	init.name = name;
7323e0cf4d3SSunny Luo 	init.ops = &clk_divider_ops;
7333e0cf4d3SSunny Luo 	init.flags = CLK_SET_RATE_PARENT;
7343e0cf4d3SSunny Luo 	parent_data[0].hw = &enh_fixed_div->hw;
7353e0cf4d3SSunny Luo 	init.num_parents = 1;
7363e0cf4d3SSunny Luo 
7373e0cf4d3SSunny Luo 	enh_div->shift	= 16,
7383e0cf4d3SSunny Luo 	enh_div->width	= 8,
7393e0cf4d3SSunny Luo 	enh_div->reg = spicc->base + SPICC_ENH_CTL0;
7403e0cf4d3SSunny Luo 	enh_div->hw.init = &init;
7413e0cf4d3SSunny Luo 
7423e0cf4d3SSunny Luo 	clk = devm_clk_register(dev, &enh_div->hw);
7433e0cf4d3SSunny Luo 	if (WARN_ON(IS_ERR(clk)))
7443e0cf4d3SSunny Luo 		return PTR_ERR(clk);
7453e0cf4d3SSunny Luo 
7463e0cf4d3SSunny Luo 	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
7473e0cf4d3SSunny Luo 	if (!mux)
7483e0cf4d3SSunny Luo 		return -ENOMEM;
7493e0cf4d3SSunny Luo 
7503e0cf4d3SSunny Luo 	snprintf(name, sizeof(name), "%s#sel", dev_name(dev));
7513e0cf4d3SSunny Luo 	init.name = name;
7523e0cf4d3SSunny Luo 	init.ops = &clk_mux_ops;
75309992025SNeil Armstrong 	parent_data[0].hw = &spicc->pow2_div.hw;
7543e0cf4d3SSunny Luo 	parent_data[1].hw = &enh_div->hw;
7553e0cf4d3SSunny Luo 	init.num_parents = 2;
7563e0cf4d3SSunny Luo 	init.flags = CLK_SET_RATE_PARENT;
7573e0cf4d3SSunny Luo 
7583e0cf4d3SSunny Luo 	mux->mask = 0x1,
7593e0cf4d3SSunny Luo 	mux->shift = 24,
7603e0cf4d3SSunny Luo 	mux->reg = spicc->base + SPICC_ENH_CTL0;
7613e0cf4d3SSunny Luo 	mux->hw.init = &init;
7623e0cf4d3SSunny Luo 
7633e0cf4d3SSunny Luo 	spicc->clk = devm_clk_register(dev, &mux->hw);
7643e0cf4d3SSunny Luo 	if (WARN_ON(IS_ERR(spicc->clk)))
7653e0cf4d3SSunny Luo 		return PTR_ERR(spicc->clk);
7663e0cf4d3SSunny Luo 
7673e0cf4d3SSunny Luo 	return 0;
7683e0cf4d3SSunny Luo }
7693e0cf4d3SSunny Luo 
meson_spicc_probe(struct platform_device * pdev)770454fa271SNeil Armstrong static int meson_spicc_probe(struct platform_device *pdev)
771454fa271SNeil Armstrong {
772454fa271SNeil Armstrong 	struct spi_master *master;
773454fa271SNeil Armstrong 	struct meson_spicc_device *spicc;
7744e3d3220SNeil Armstrong 	int ret, irq;
775454fa271SNeil Armstrong 
776454fa271SNeil Armstrong 	master = spi_alloc_master(&pdev->dev, sizeof(*spicc));
777454fa271SNeil Armstrong 	if (!master) {
778454fa271SNeil Armstrong 		dev_err(&pdev->dev, "master allocation failed\n");
779454fa271SNeil Armstrong 		return -ENOMEM;
780454fa271SNeil Armstrong 	}
781454fa271SNeil Armstrong 	spicc = spi_master_get_devdata(master);
782454fa271SNeil Armstrong 	spicc->master = master;
783454fa271SNeil Armstrong 
784a6cda1f9SSunny Luo 	spicc->data = of_device_get_match_data(&pdev->dev);
785a6cda1f9SSunny Luo 	if (!spicc->data) {
786a6cda1f9SSunny Luo 		dev_err(&pdev->dev, "failed to get match data\n");
787a6cda1f9SSunny Luo 		ret = -EINVAL;
788a6cda1f9SSunny Luo 		goto out_master;
789a6cda1f9SSunny Luo 	}
790a6cda1f9SSunny Luo 
791454fa271SNeil Armstrong 	spicc->pdev = pdev;
792454fa271SNeil Armstrong 	platform_set_drvdata(pdev, spicc);
793454fa271SNeil Armstrong 
79404694e50SNeil Armstrong 	init_completion(&spicc->done);
79504694e50SNeil Armstrong 
796362385c0SYueHaibing 	spicc->base = devm_platform_ioremap_resource(pdev, 0);
797454fa271SNeil Armstrong 	if (IS_ERR(spicc->base)) {
798454fa271SNeil Armstrong 		dev_err(&pdev->dev, "io resource mapping failed\n");
799454fa271SNeil Armstrong 		ret = PTR_ERR(spicc->base);
800454fa271SNeil Armstrong 		goto out_master;
801454fa271SNeil Armstrong 	}
802454fa271SNeil Armstrong 
8033e0cf4d3SSunny Luo 	/* Set master mode and enable controller */
8043e0cf4d3SSunny Luo 	writel_relaxed(SPICC_ENABLE | SPICC_MODE_MASTER,
8053e0cf4d3SSunny Luo 		       spicc->base + SPICC_CONREG);
8063e0cf4d3SSunny Luo 
807454fa271SNeil Armstrong 	/* Disable all IRQs */
808454fa271SNeil Armstrong 	writel_relaxed(0, spicc->base + SPICC_INTREG);
809454fa271SNeil Armstrong 
810454fa271SNeil Armstrong 	irq = platform_get_irq(pdev, 0);
811e937440fSMiaoqian Lin 	if (irq < 0) {
812e937440fSMiaoqian Lin 		ret = irq;
813e937440fSMiaoqian Lin 		goto out_master;
814e937440fSMiaoqian Lin 	}
815e937440fSMiaoqian Lin 
816454fa271SNeil Armstrong 	ret = devm_request_irq(&pdev->dev, irq, meson_spicc_irq,
817454fa271SNeil Armstrong 			       0, NULL, spicc);
818454fa271SNeil Armstrong 	if (ret) {
819454fa271SNeil Armstrong 		dev_err(&pdev->dev, "irq request failed\n");
820454fa271SNeil Armstrong 		goto out_master;
821454fa271SNeil Armstrong 	}
822454fa271SNeil Armstrong 
823454fa271SNeil Armstrong 	spicc->core = devm_clk_get(&pdev->dev, "core");
824454fa271SNeil Armstrong 	if (IS_ERR(spicc->core)) {
825454fa271SNeil Armstrong 		dev_err(&pdev->dev, "core clock request failed\n");
826454fa271SNeil Armstrong 		ret = PTR_ERR(spicc->core);
827454fa271SNeil Armstrong 		goto out_master;
828454fa271SNeil Armstrong 	}
829454fa271SNeil Armstrong 
8304e3d3220SNeil Armstrong 	if (spicc->data->has_pclk) {
8314e3d3220SNeil Armstrong 		spicc->pclk = devm_clk_get(&pdev->dev, "pclk");
8324e3d3220SNeil Armstrong 		if (IS_ERR(spicc->pclk)) {
8334e3d3220SNeil Armstrong 			dev_err(&pdev->dev, "pclk clock request failed\n");
8344e3d3220SNeil Armstrong 			ret = PTR_ERR(spicc->pclk);
8354e3d3220SNeil Armstrong 			goto out_master;
8364e3d3220SNeil Armstrong 		}
8374e3d3220SNeil Armstrong 	}
8384e3d3220SNeil Armstrong 
839454fa271SNeil Armstrong 	ret = clk_prepare_enable(spicc->core);
840454fa271SNeil Armstrong 	if (ret) {
841454fa271SNeil Armstrong 		dev_err(&pdev->dev, "core clock enable failed\n");
842454fa271SNeil Armstrong 		goto out_master;
843454fa271SNeil Armstrong 	}
8444e3d3220SNeil Armstrong 
8454e3d3220SNeil Armstrong 	ret = clk_prepare_enable(spicc->pclk);
8464e3d3220SNeil Armstrong 	if (ret) {
8474e3d3220SNeil Armstrong 		dev_err(&pdev->dev, "pclk clock enable failed\n");
84895730d5eSzpershuai 		goto out_core_clk;
8494e3d3220SNeil Armstrong 	}
850454fa271SNeil Armstrong 
851f4567b28SAmjad Ouled-Ameur 	spicc->pinctrl = devm_pinctrl_get(&pdev->dev);
852f4567b28SAmjad Ouled-Ameur 	if (IS_ERR(spicc->pinctrl)) {
853f4567b28SAmjad Ouled-Ameur 		ret = PTR_ERR(spicc->pinctrl);
854f4567b28SAmjad Ouled-Ameur 		goto out_clk;
855f4567b28SAmjad Ouled-Ameur 	}
856f4567b28SAmjad Ouled-Ameur 
857454fa271SNeil Armstrong 	device_reset_optional(&pdev->dev);
858454fa271SNeil Armstrong 
859454fa271SNeil Armstrong 	master->num_chipselect = 4;
860454fa271SNeil Armstrong 	master->dev.of_node = pdev->dev.of_node;
861454fa271SNeil Armstrong 	master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH;
862454fa271SNeil Armstrong 	master->bits_per_word_mask = SPI_BPW_MASK(32) |
863454fa271SNeil Armstrong 				     SPI_BPW_MASK(24) |
864454fa271SNeil Armstrong 				     SPI_BPW_MASK(16) |
865454fa271SNeil Armstrong 				     SPI_BPW_MASK(8);
866*90366cd6SAndy Shevchenko 	master->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX);
8678791068dSNeil Armstrong 	master->min_speed_hz = spicc->data->min_speed_hz;
8684e3d3220SNeil Armstrong 	master->max_speed_hz = spicc->data->max_speed_hz;
869454fa271SNeil Armstrong 	master->setup = meson_spicc_setup;
870454fa271SNeil Armstrong 	master->cleanup = meson_spicc_cleanup;
871454fa271SNeil Armstrong 	master->prepare_message = meson_spicc_prepare_message;
872454fa271SNeil Armstrong 	master->unprepare_transfer_hardware = meson_spicc_unprepare_transfer;
873454fa271SNeil Armstrong 	master->transfer_one = meson_spicc_transfer_one;
874cd8fb859SLinus Walleij 	master->use_gpio_descriptors = true;
875454fa271SNeil Armstrong 
876a6cda1f9SSunny Luo 	meson_spicc_oen_enable(spicc);
877a6cda1f9SSunny Luo 
87809992025SNeil Armstrong 	ret = meson_spicc_pow2_clk_init(spicc);
87909992025SNeil Armstrong 	if (ret) {
88009992025SNeil Armstrong 		dev_err(&pdev->dev, "pow2 clock registration failed\n");
88109992025SNeil Armstrong 		goto out_clk;
88209992025SNeil Armstrong 	}
88309992025SNeil Armstrong 
88409992025SNeil Armstrong 	if (spicc->data->has_enhance_clk_div) {
88509992025SNeil Armstrong 		ret = meson_spicc_enh_clk_init(spicc);
8863e0cf4d3SSunny Luo 		if (ret) {
8873e0cf4d3SSunny Luo 			dev_err(&pdev->dev, "clock registration failed\n");
888b2d501c1Szpershuai 			goto out_clk;
8893e0cf4d3SSunny Luo 		}
89009992025SNeil Armstrong 	}
8913e0cf4d3SSunny Luo 
892454fa271SNeil Armstrong 	ret = devm_spi_register_master(&pdev->dev, master);
893ded5fa4eSAlexey Khoroshilov 	if (ret) {
894ded5fa4eSAlexey Khoroshilov 		dev_err(&pdev->dev, "spi master registration failed\n");
895ded5fa4eSAlexey Khoroshilov 		goto out_clk;
896ded5fa4eSAlexey Khoroshilov 	}
897ded5fa4eSAlexey Khoroshilov 
898454fa271SNeil Armstrong 	return 0;
899454fa271SNeil Armstrong 
900ded5fa4eSAlexey Khoroshilov out_clk:
9014e3d3220SNeil Armstrong 	clk_disable_unprepare(spicc->pclk);
902454fa271SNeil Armstrong 
90395730d5eSzpershuai out_core_clk:
90495730d5eSzpershuai 	clk_disable_unprepare(spicc->core);
90595730d5eSzpershuai 
906454fa271SNeil Armstrong out_master:
907454fa271SNeil Armstrong 	spi_master_put(master);
908454fa271SNeil Armstrong 
909454fa271SNeil Armstrong 	return ret;
910454fa271SNeil Armstrong }
911454fa271SNeil Armstrong 
meson_spicc_remove(struct platform_device * pdev)9128e8355d1SUwe Kleine-König static void meson_spicc_remove(struct platform_device *pdev)
913454fa271SNeil Armstrong {
914454fa271SNeil Armstrong 	struct meson_spicc_device *spicc = platform_get_drvdata(pdev);
915454fa271SNeil Armstrong 
916454fa271SNeil Armstrong 	/* Disable SPI */
917454fa271SNeil Armstrong 	writel(0, spicc->base + SPICC_CONREG);
918454fa271SNeil Armstrong 
919454fa271SNeil Armstrong 	clk_disable_unprepare(spicc->core);
9204e3d3220SNeil Armstrong 	clk_disable_unprepare(spicc->pclk);
921454fa271SNeil Armstrong 
9228311ee21SDongliang Mu 	spi_master_put(spicc->master);
923454fa271SNeil Armstrong }
924454fa271SNeil Armstrong 
925a6cda1f9SSunny Luo static const struct meson_spicc_data meson_spicc_gx_data = {
9263196816fSNeil Armstrong 	.max_speed_hz		= 30000000,
9278791068dSNeil Armstrong 	.min_speed_hz		= 325000,
9280eb707acSNeil Armstrong 	.fifo_size		= 16,
929a6cda1f9SSunny Luo };
930a6cda1f9SSunny Luo 
931a6cda1f9SSunny Luo static const struct meson_spicc_data meson_spicc_axg_data = {
9323196816fSNeil Armstrong 	.max_speed_hz		= 80000000,
9338791068dSNeil Armstrong 	.min_speed_hz		= 325000,
9340eb707acSNeil Armstrong 	.fifo_size		= 16,
935a6cda1f9SSunny Luo 	.has_oen		= true,
9363e0cf4d3SSunny Luo 	.has_enhance_clk_div	= true,
937a6cda1f9SSunny Luo };
938a6cda1f9SSunny Luo 
9394e3d3220SNeil Armstrong static const struct meson_spicc_data meson_spicc_g12a_data = {
9404e3d3220SNeil Armstrong 	.max_speed_hz		= 166666666,
9414e3d3220SNeil Armstrong 	.min_speed_hz		= 50000,
9424e3d3220SNeil Armstrong 	.fifo_size		= 15,
9434e3d3220SNeil Armstrong 	.has_oen		= true,
9444e3d3220SNeil Armstrong 	.has_enhance_clk_div	= true,
9454e3d3220SNeil Armstrong 	.has_pclk		= true,
9464e3d3220SNeil Armstrong };
9474e3d3220SNeil Armstrong 
948454fa271SNeil Armstrong static const struct of_device_id meson_spicc_of_match[] = {
949a6cda1f9SSunny Luo 	{
950a6cda1f9SSunny Luo 		.compatible	= "amlogic,meson-gx-spicc",
951a6cda1f9SSunny Luo 		.data		= &meson_spicc_gx_data,
952a6cda1f9SSunny Luo 	},
953a6cda1f9SSunny Luo 	{
954a6cda1f9SSunny Luo 		.compatible = "amlogic,meson-axg-spicc",
955a6cda1f9SSunny Luo 		.data		= &meson_spicc_axg_data,
956a6cda1f9SSunny Luo 	},
9574e3d3220SNeil Armstrong 	{
9584e3d3220SNeil Armstrong 		.compatible = "amlogic,meson-g12a-spicc",
9594e3d3220SNeil Armstrong 		.data		= &meson_spicc_g12a_data,
9604e3d3220SNeil Armstrong 	},
961454fa271SNeil Armstrong 	{ /* sentinel */ }
962454fa271SNeil Armstrong };
963454fa271SNeil Armstrong MODULE_DEVICE_TABLE(of, meson_spicc_of_match);
964454fa271SNeil Armstrong 
965454fa271SNeil Armstrong static struct platform_driver meson_spicc_driver = {
966454fa271SNeil Armstrong 	.probe   = meson_spicc_probe,
9678e8355d1SUwe Kleine-König 	.remove_new = meson_spicc_remove,
968454fa271SNeil Armstrong 	.driver  = {
969454fa271SNeil Armstrong 		.name = "meson-spicc",
970454fa271SNeil Armstrong 		.of_match_table = of_match_ptr(meson_spicc_of_match),
971454fa271SNeil Armstrong 	},
972454fa271SNeil Armstrong };
973454fa271SNeil Armstrong 
974454fa271SNeil Armstrong module_platform_driver(meson_spicc_driver);
975454fa271SNeil Armstrong 
976454fa271SNeil Armstrong MODULE_DESCRIPTION("Meson SPI Communication Controller driver");
977454fa271SNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
978454fa271SNeil Armstrong MODULE_LICENSE("GPL");
979