1 /*
2  * Copyright (C) Cvitek Co., Ltd. 2019-2020. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <errno.h>
17 #include <rtthread.h>
18 #include <string.h>
19 #include <stdio.h>
20 
21 #include "mmio.h"
22 #include "dw_spi.h"
23 
24 #include <rthw.h>
25 
26 #ifdef SPI_DEBUG
27 #define SP_DEBUG_LOG(fmt, ...) \
28     do { \
29         rt_kprintf(fmt, ##__VA_ARGS__); \
30     } while(0)
31 #else
32 #define SP_DEBUG_LOG(fmt, ...)
33 #endif
34 
35 /* Restart the controller, disable all interrupts, clean rx fifo */
spi_hw_init(struct dw_spi * dws)36 void spi_hw_init(struct dw_spi *dws)
37 {
38     /*
39      * Try to detect the FIFO depth if not set by interface driver,
40      * the depth could be from 2 to 256 from HW spec
41      */
42     if (!dws->fifo_len) {
43         uint32_t fifo;
44 
45         for (fifo = 1; fifo < 256; fifo++) {
46             dw_writel(dws, CVI_DW_SPI_TXFTLR, fifo);
47             if (fifo != dw_readl(dws, CVI_DW_SPI_TXFTLR))
48                 break;
49         }
50         dw_writel(dws, CVI_DW_SPI_TXFTLR, 0);
51 
52         dws->fifo_len = (fifo == 1) ? 0 : fifo;
53 
54         SP_DEBUG_LOG("Detected FIFO size: %u bytes\n", dws->fifo_len);
55     }
56 }
57 
min3(uint32_t a,uint32_t b,uint32_t c)58 uint32_t min3(uint32_t a, uint32_t b, uint32_t c)
59 {
60     uint32_t tmp;
61 
62     tmp = (a < b) ? a : b;
63     return (tmp < c) ? tmp : c;
64 }
65 
cpu_relax(void)66 static inline void cpu_relax(void)
67 {
68     //asm volatile("" ::: "memory");
69 }
70 
tx_max(struct dw_spi * dws)71 static inline uint32_t tx_max(struct dw_spi *dws)
72 {
73     uint32_t tx_left, tx_room, rxtx_gap, temp;
74     cpu_relax();
75     tx_left = dws->tx_len;
76     tx_room = dws->fifo_len - dw_readl(dws, CVI_DW_SPI_TXFLR);
77 
78     /*
79      * Another concern is about the tx/rx mismatch, we
80      * though to use (dws->fifo_len - rxflr - txflr) as
81      * one maximum value for tx, but it doesn't cover the
82      * data which is out of tx/rx fifo and inside the
83      * shift registers. So a control from sw point of
84      * view is taken.
85      */
86 
87     SP_DEBUG_LOG("tx left: %#x, tx room: %#x\n", tx_left, tx_room);
88     if (dws->rx != NULL && dws->tx != NULL) {
89         cpu_relax();
90         rxtx_gap =  dws->fifo_len - (dws->rx_len - dws->tx_len);
91         temp = min3(tx_left, tx_room, (uint32_t)(rxtx_gap));
92     } else {
93         temp = tx_left < tx_room ? tx_left : tx_room;
94     }
95 
96     SP_DEBUG_LOG("temp: %#x\n", temp);
97     return temp;
98 }
99 
dw_writer(struct dw_spi * dws)100 void dw_writer(struct dw_spi *dws)
101 {
102     uint32_t max;
103     uint16_t txw = 0;
104 
105     max = tx_max(dws);
106     SP_DEBUG_LOG("max: %#x \n", max);
107     while (max--) {
108         if (dws->tx) {
109             if (dws->n_bytes == 1)
110                 txw = *(uint8_t *)(dws->tx);
111             else
112                 txw = *(uint16_t *)(dws->tx);
113         }
114         dw_writel(dws, CVI_DW_SPI_DR, txw);
115         dws->tx += dws->n_bytes;
116         --dws->tx_len;
117     }
118 }
119 
rx_max(struct dw_spi * dws)120 static inline uint32_t rx_max(struct dw_spi *dws)
121 {
122     uint32_t temp;
123     uint32_t rx_left = dws->rx_len;
124     uint32_t data_in_fifo = dw_readl(dws, CVI_DW_SPI_RXFLR);
125 
126     temp = (rx_left < data_in_fifo ? rx_left : data_in_fifo);
127     SP_DEBUG_LOG("data_in_fifo:%u temp: %u\n", data_in_fifo, temp);
128     return temp;
129 }
130 
dw_spi_check_status(struct dw_spi * dws,bool raw)131 int dw_spi_check_status(struct dw_spi *dws, bool raw)
132 {
133     uint32_t irq_status;
134     int ret = 0;
135 
136     if (raw)
137         irq_status = dw_readl(dws, CVI_DW_SPI_RISR);
138     else
139         irq_status = dw_readl(dws, CVI_DW_SPI_ISR);
140 
141     if (irq_status & CVI_SPI_INT_RXOI) {
142         SP_DEBUG_LOG("RX FIFO overflow detected\n");
143         ret = -1;
144     }
145 
146     if (irq_status & CVI_SPI_INT_RXUI) {
147         SP_DEBUG_LOG("RX FIFO underflow detected\n");
148         ret = -1;
149     }
150 
151     if (irq_status & CVI_SPI_INT_TXOI) {
152         SP_DEBUG_LOG("TX FIFO overflow detected\n");
153         ret = -1;
154     }
155 
156     if (ret)
157         spi_reset_chip(dws);
158 
159     return ret;
160 }
161 
dw_reader(struct dw_spi * dws)162 void dw_reader(struct dw_spi *dws)
163 {
164     uint32_t max;
165     uint16_t rxw;
166 
167     max = rx_max(dws);
168     SP_DEBUG_LOG("max: %#x \n", max);
169     while (max--) {
170         rxw = dw_readl(dws, CVI_DW_SPI_DR);
171         if (dws->rx) {
172             if (dws->n_bytes == 1)
173                 *(uint8_t *)(dws->rx) = rxw;
174             else
175                 *(uint16_t *)(dws->rx) = rxw;
176             dws->rx += dws->n_bytes;
177         }
178         --dws->rx_len;
179     }
180 }
181 
spi_delay_to_ns(struct spi_delay * _delay,struct dw_spi * dws)182 int spi_delay_to_ns(struct spi_delay *_delay, struct dw_spi *dws)
183 {
184     uint32_t delay = _delay->value;
185     uint32_t unit = _delay->unit;
186     uint32_t hz;
187 
188     if (!delay)
189         return 0;
190 
191     switch (unit) {
192         case SPI_DELAY_UNIT_USECS:
193             delay *= 1000;
194             break;
195         case SPI_DELAY_UNIT_NSECS: /* nothing to do here */
196             break;
197         case SPI_DELAY_UNIT_SCK:
198             /* clock cycles need to be obtained from spi_transfer */
199             if (!dws)
200                 return -1;
201             /* if there is no effective speed know, then approximate
202              * by underestimating with half the requested hz
203              */
204             hz = dws->speed_hz / 2;
205             if (!hz)
206                 return -1;
207             delay *= DIV_ROUND_UP(1000000000, hz);
208             break;
209         default:
210             return -EINVAL;
211     }
212 
213     return delay;
214 }
215 
_spi_transfer_delay_ns(uint32_t ns)216 static void _spi_transfer_delay_ns(uint32_t ns)
217 {
218         if (!ns)
219                 return;
220         if (ns <= 1000) {
221         rt_hw_us_delay(1);
222         } else {
223                 uint32_t us = DIV_ROUND_UP(ns, 1000);
224                 rt_hw_us_delay(us);
225         }
226 }
227 
spi_delay_exec(struct spi_delay * _delay,struct dw_spi * dws)228 int spi_delay_exec(struct spi_delay *_delay, struct dw_spi *dws)
229 {
230         int delay;
231 
232         if (!_delay)
233                 return -1;
234 
235         delay = spi_delay_to_ns(_delay, dws);
236         if (delay < 0)
237                 return delay;
238 
239         _spi_transfer_delay_ns(delay);
240 
241         return 0;
242 }
243 
poll_transfer(struct dw_spi * dws)244 int poll_transfer(struct dw_spi *dws)
245 {
246     struct spi_delay delay;
247     uint16_t nbits;
248     delay.unit = SPI_DELAY_UNIT_SCK;
249     nbits = dws->n_bytes * BITS_PER_BYTE;
250     int ret = 0;
251 
252     do
253     {
254         dw_writer(dws);
255         cpu_relax();
256 
257         delay.value = nbits * (dws->rx_len - dws->tx_len);
258         spi_delay_exec(&delay, dws);
259         dw_reader(dws);
260         cpu_relax();
261         ret = dw_spi_check_status(dws, true);
262         if (ret)
263             return ret;
264     } while (dws->rx_len && dws->tx_len);
265 
266     return 0;
267 }
268 
set_tran_mode(struct dw_spi * dws)269 void set_tran_mode(struct dw_spi *dws)
270 {
271     uint32_t reg = dw_readl(dws, CVI_DW_SPI_CTRLR0);
272     uint8_t tmode;
273 
274     if (dws->rx && dws->tx) {
275         tmode = CVI_SPI_TMOD_TR;
276     } else if (dws->rx) {
277         tmode = CVI_SPI_TMOD_RO;
278     } else {
279         tmode = CVI_SPI_TMOD_TO;
280     }
281     reg &= ~CVI_SPI_TMOD_MASK;
282     reg |= (tmode << CVI_SPI_TMOD_OFFSET);
283 
284     dw_writel(dws, CVI_DW_SPI_CTRLR0, reg);
285 }
286 
dw_spi_set_controller_mode(struct dw_spi * dws,uint8_t enable_master)287 void dw_spi_set_controller_mode(struct dw_spi *dws, uint8_t enable_master)
288 {
289     /* do not support to switch controller mode, it is default master mode */
290 }
291 
dw_spi_set_cs(struct dw_spi * dws,bool enable,uint32_t index)292 void dw_spi_set_cs(struct dw_spi *dws, bool enable, uint32_t index)
293 {
294     uint32_t reg = dw_readl(dws, CVI_DW_SPI_SER);
295 
296     if (enable)
297         dw_writel(dws, CVI_DW_SPI_SER, reg | BIT(index));
298     else
299         dw_writel(dws, CVI_DW_SPI_SER, reg & ~BIT(index));
300 }
301 
dw_spi_set_polarity_and_phase(struct dw_spi * dws,uint8_t format)302 void dw_spi_set_polarity_and_phase(struct dw_spi *dws, uint8_t format)
303 {
304     uint32_t reg = dw_readl(dws, CVI_DW_SPI_CTRLR0);
305     reg &= ~(0x3 << 6);
306 
307     switch (format) {
308         case SPI_FORMAT_CPOL0_CPHA0:
309             reg |= (SPI_MODE_0 << 6);
310             break;
311 
312         case SPI_FORMAT_CPOL0_CPHA1:
313             reg |= (SPI_MODE_1 << 6);
314             break;
315 
316         case SPI_FORMAT_CPOL1_CPHA0:
317             reg |= (SPI_MODE_2 << 6);
318             break;
319 
320         case SPI_FORMAT_CPOL1_CPHA1:
321             reg |= (SPI_MODE_3 << 6);
322             break;
323 
324         default:
325             reg = dw_readl(dws, CVI_DW_SPI_CTRLR0);
326             break;
327     }
328     SP_DEBUG_LOG("set phase and polarity: %#x\n", reg);
329     dw_writel(dws, CVI_DW_SPI_CTRLR0, reg);
330 }
331 
dw_spi_set_clock(struct dw_spi * dws,uint32_t clock_in,uint32_t clock_out)332 uint32_t dw_spi_set_clock(struct dw_spi *dws, uint32_t clock_in, uint32_t clock_out)
333 {
334     uint16_t div;
335 
336     div = (DIV_ROUND_UP(clock_in, clock_out) + 1) & 0xfffe;
337     dws->speed_hz = clock_in / div;
338     SP_DEBUG_LOG("clk div value is: %u, hz:%u\n", div, dws->speed_hz);
339     spi_set_clk(dws, div);
340     return dws->speed_hz;
341 }
342 
dw_spi_set_data_frame_len(struct dw_spi * dws,uint32_t size)343 int dw_spi_set_data_frame_len(struct dw_spi *dws, uint32_t size)
344 {
345     uint32_t temp = dw_readl(dws, CVI_DW_SPI_CTRLR0);
346     temp &= ~0xf;
347 
348     if (size == 8) {
349         dws->n_bytes = 1;
350     } else if (size == 16) {
351         dws->n_bytes = 2;
352     } else {
353         SP_DEBUG_LOG("do not support %u bit data!\n", size);
354         return -1;
355     }
356     temp |= (size - 1);
357     dw_writel(dws, CVI_DW_SPI_CTRLR0, temp);
358     SP_DEBUG_LOG("set data frame len: %#x\n", temp);
359     return 0;
360 }
361 
dw_spi_show_regs(struct dw_spi * dws)362 void dw_spi_show_regs(struct dw_spi *dws)
363 {
364     SP_DEBUG_LOG("CTRLR0: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_CTRLR0));
365     SP_DEBUG_LOG("CTRLR1: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_CTRLR1));
366     SP_DEBUG_LOG("SSIENR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SSIENR));
367     SP_DEBUG_LOG("SER: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SER));
368     SP_DEBUG_LOG("BAUDR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_BAUDR));
369     SP_DEBUG_LOG("TXFTLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_TXFTLR));
370     SP_DEBUG_LOG("RXFTLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_RXFTLR));
371     SP_DEBUG_LOG("TXFLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_TXFLR));
372     SP_DEBUG_LOG("RXFLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_RXFLR));
373     SP_DEBUG_LOG("SR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_SR));
374     SP_DEBUG_LOG("IMR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_IMR));
375     SP_DEBUG_LOG("ISR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_ISR));
376     SP_DEBUG_LOG("DMACR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMACR));
377     SP_DEBUG_LOG("DMATDLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMATDLR));
378     SP_DEBUG_LOG("DMARDLR: \t0x%08x\n", dw_readl(dws, CVI_DW_SPI_DMARDLR));
379 }
380