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