diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/sfc-x1000.c')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/sfc-x1000.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.c b/firmware/target/mips/ingenic_x1000/sfc-x1000.c new file mode 100644 index 0000000000..9537cdc035 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.c | |||
@@ -0,0 +1,298 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "system.h" | ||
23 | #include "kernel.h" | ||
24 | #include "panic.h" | ||
25 | #include "sfc-x1000.h" | ||
26 | #include "gpio-x1000.h" | ||
27 | #include "irq-x1000.h" | ||
28 | #include "x1000/sfc.h" | ||
29 | #include "x1000/cpm.h" | ||
30 | |||
31 | #ifndef BOOTLOADER_SPL | ||
32 | /* DMA only works once the system is properly booted */ | ||
33 | # define NEED_SFC_DMA | ||
34 | #endif | ||
35 | |||
36 | #if defined(BOOTLOADER_SPL) | ||
37 | # if X1000_EXCLK_FREQ == 24000000 | ||
38 | # define FIXED_CLK_FREQ 600000000 | ||
39 | # define FIXED_CLK_SRC X1000_CLK_MPLL | ||
40 | # elif X1000_EXCLK_FREQ == 26000000 | ||
41 | # define FIXED_CLK_FREQ 598000000 | ||
42 | # define FIXED_CLK_SRC X1000_CLK_MPLL | ||
43 | # else | ||
44 | # error "bad EXCLK freq" | ||
45 | # endif | ||
46 | #endif | ||
47 | |||
48 | #define FIFO_THRESH 31 | ||
49 | |||
50 | #define SFC_STATUS_PENDING (-1) | ||
51 | |||
52 | #ifdef NEED_SFC_DMA | ||
53 | static struct mutex sfc_mutex; | ||
54 | static struct semaphore sfc_sema; | ||
55 | static struct timeout sfc_lockup_tmo; | ||
56 | static bool sfc_inited = false; | ||
57 | static volatile int sfc_status; | ||
58 | #else | ||
59 | # define sfc_status SFC_STATUS_OK | ||
60 | #endif | ||
61 | |||
62 | void sfc_init(void) | ||
63 | { | ||
64 | #ifdef NEED_SFC_DMA | ||
65 | if(sfc_inited) | ||
66 | return; | ||
67 | |||
68 | mutex_init(&sfc_mutex); | ||
69 | semaphore_init(&sfc_sema, 1, 0); | ||
70 | sfc_inited = true; | ||
71 | #endif | ||
72 | } | ||
73 | |||
74 | void sfc_lock(void) | ||
75 | { | ||
76 | #ifdef NEED_SFC_DMA | ||
77 | mutex_lock(&sfc_mutex); | ||
78 | #endif | ||
79 | } | ||
80 | |||
81 | void sfc_unlock(void) | ||
82 | { | ||
83 | #ifdef NEED_SFC_DMA | ||
84 | mutex_unlock(&sfc_mutex); | ||
85 | #endif | ||
86 | } | ||
87 | |||
88 | void sfc_open(void) | ||
89 | { | ||
90 | gpio_config(GPIO_A, 0x3f << 26, GPIO_DEVICE(1)); | ||
91 | jz_writef(CPM_CLKGR, SFC(0)); | ||
92 | jz_writef(SFC_GLB, OP_MODE_V(SLAVE), PHASE_NUM(1), | ||
93 | THRESHOLD(FIFO_THRESH), WP_EN(1)); | ||
94 | REG_SFC_CGE = 0; | ||
95 | REG_SFC_INTC = 0x1f; | ||
96 | REG_SFC_MEM_ADDR = 0; | ||
97 | |||
98 | #ifdef NEED_SFC_DMA | ||
99 | jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32)); | ||
100 | system_enable_irq(IRQ_SFC); | ||
101 | #endif | ||
102 | } | ||
103 | |||
104 | void sfc_close(void) | ||
105 | { | ||
106 | #ifdef NEED_SFC_DMA | ||
107 | system_disable_irq(IRQ_SFC); | ||
108 | #endif | ||
109 | |||
110 | REG_SFC_CGE = 0x1f; | ||
111 | jz_writef(CPM_CLKGR, SFC(1)); | ||
112 | } | ||
113 | |||
114 | void sfc_set_clock(x1000_clk_t clksrc, uint32_t freq) | ||
115 | { | ||
116 | uint32_t in_freq; | ||
117 | #ifdef FIXED_CLK_FREQ | ||
118 | /* Small optimization to save code space in SPL by not polling clock */ | ||
119 | clksrc = FIXED_CLK_SRC; | ||
120 | in_freq = FIXED_CLK_FREQ; | ||
121 | #else | ||
122 | in_freq = clk_get(clksrc); | ||
123 | #endif | ||
124 | |||
125 | uint32_t div = clk_calc_div(in_freq, freq); | ||
126 | jz_writef(CPM_SSICDR, CE(1), CLKDIV(div - 1), | ||
127 | SFC_CS(clksrc == X1000_CLK_MPLL ? 1 : 0)); | ||
128 | while(jz_readf(CPM_SSICDR, BUSY)); | ||
129 | jz_writef(CPM_SSICDR, CE(0)); | ||
130 | } | ||
131 | |||
132 | #ifdef NEED_SFC_DMA | ||
133 | static int sfc_lockup_tmo_cb(struct timeout* tmo) | ||
134 | { | ||
135 | (void)tmo; | ||
136 | |||
137 | int irq = disable_irq_save(); | ||
138 | if(sfc_status == SFC_STATUS_PENDING) { | ||
139 | sfc_status = SFC_STATUS_LOCKUP; | ||
140 | jz_overwritef(SFC_TRIG, STOP(1)); | ||
141 | semaphore_release(&sfc_sema); | ||
142 | } | ||
143 | |||
144 | restore_irq(irq); | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static void sfc_wait_end(void) | ||
149 | { | ||
150 | semaphore_wait(&sfc_sema, TIMEOUT_BLOCK); | ||
151 | } | ||
152 | |||
153 | void SFC(void) | ||
154 | { | ||
155 | unsigned sr = REG_SFC_SR & ~REG_SFC_INTC; | ||
156 | |||
157 | if(jz_vreadf(sr, SFC_SR, OVER)) { | ||
158 | jz_overwritef(SFC_SCR, CLR_OVER(1)); | ||
159 | sfc_status = SFC_STATUS_OVERFLOW; | ||
160 | } else if(jz_vreadf(sr, SFC_SR, UNDER)) { | ||
161 | jz_overwritef(SFC_SCR, CLR_UNDER(1)); | ||
162 | sfc_status = SFC_STATUS_UNDERFLOW; | ||
163 | } else if(jz_vreadf(sr, SFC_SR, END)) { | ||
164 | jz_overwritef(SFC_SCR, CLR_END(1)); | ||
165 | sfc_status = SFC_STATUS_OK; | ||
166 | } else { | ||
167 | panicf("SFC IRQ bug"); | ||
168 | return; | ||
169 | } | ||
170 | |||
171 | /* Not sure this is wholly correct */ | ||
172 | if(sfc_status != SFC_STATUS_OK) | ||
173 | jz_overwritef(SFC_TRIG, STOP(1)); | ||
174 | |||
175 | REG_SFC_INTC = 0x1f; | ||
176 | semaphore_release(&sfc_sema); | ||
177 | } | ||
178 | #else | ||
179 | /* Note the X1000 is *very* picky about how the SFC FIFOs are accessed | ||
180 | * so please do NOT try to rearrange the code without testing it first! | ||
181 | */ | ||
182 | |||
183 | void sfc_fifo_read(unsigned* buffer, int data_bytes) | ||
184 | { | ||
185 | int data_words = (data_bytes + 3) / 4; | ||
186 | while(data_words > 0) { | ||
187 | if(jz_readf(SFC_SR, RREQ)) { | ||
188 | jz_overwritef(SFC_SCR, CLR_RREQ(1)); | ||
189 | |||
190 | int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words; | ||
191 | data_words -= amount; | ||
192 | while(amount > 0) { | ||
193 | *buffer++ = REG_SFC_DATA; | ||
194 | amount -= 1; | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | void sfc_fifo_write(const unsigned* buffer, int data_bytes) | ||
201 | { | ||
202 | int data_words = (data_bytes + 3) / 4; | ||
203 | while(data_words > 0) { | ||
204 | if(jz_readf(SFC_SR, TREQ)) { | ||
205 | jz_overwritef(SFC_SCR, CLR_TREQ(1)); | ||
206 | |||
207 | int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words; | ||
208 | data_words -= amount; | ||
209 | while(amount > 0) { | ||
210 | REG_SFC_DATA = *buffer++; | ||
211 | amount -= 1; | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | |||
217 | static void sfc_wait_end(void) | ||
218 | { | ||
219 | while(jz_readf(SFC_SR, END) == 0); | ||
220 | jz_overwritef(SFC_SCR, CLR_TREQ(1)); | ||
221 | } | ||
222 | |||
223 | #endif /* NEED_SFC_DMA */ | ||
224 | |||
225 | int sfc_exec(const sfc_op* op) | ||
226 | { | ||
227 | #ifdef NEED_SFC_DMA | ||
228 | uint32_t intc_clear = jz_orm(SFC_INTC, MSK_END); | ||
229 | #endif | ||
230 | |||
231 | if(op->flags & (SFC_FLAG_READ|SFC_FLAG_WRITE)) { | ||
232 | jz_writef(SFC_TRAN_CONF(0), DATA_EN(1)); | ||
233 | REG_SFC_TRAN_LENGTH = op->data_bytes; | ||
234 | #ifdef NEED_SFC_DMA | ||
235 | REG_SFC_MEM_ADDR = PHYSADDR(op->buffer); | ||
236 | #endif | ||
237 | |||
238 | if(op->flags & SFC_FLAG_READ) | ||
239 | { | ||
240 | jz_writef(SFC_GLB, TRAN_DIR_V(READ)); | ||
241 | #ifdef NEED_SFC_DMA | ||
242 | discard_dcache_range(op->buffer, op->data_bytes); | ||
243 | intc_clear |= jz_orm(SFC_INTC, MSK_OVER); | ||
244 | #endif | ||
245 | } | ||
246 | else | ||
247 | { | ||
248 | jz_writef(SFC_GLB, TRAN_DIR_V(WRITE)); | ||
249 | #ifdef NEED_SFC_DMA | ||
250 | commit_dcache_range(op->buffer, op->data_bytes); | ||
251 | intc_clear |= jz_orm(SFC_INTC, MSK_UNDER); | ||
252 | #endif | ||
253 | } | ||
254 | } else { | ||
255 | jz_writef(SFC_TRAN_CONF(0), DATA_EN(0)); | ||
256 | REG_SFC_TRAN_LENGTH = 0; | ||
257 | #ifdef NEED_SFC_DMA | ||
258 | REG_SFC_MEM_ADDR = 0; | ||
259 | #endif | ||
260 | } | ||
261 | |||
262 | bool dummy_first = (op->flags & SFC_FLAG_DUMMYFIRST) != 0; | ||
263 | jz_writef(SFC_TRAN_CONF(0), | ||
264 | MODE(op->mode), POLL_EN(0), | ||
265 | ADDR_WIDTH(op->addr_bytes), | ||
266 | PHASE_FMT(dummy_first ? 1 : 0), | ||
267 | DUMMY_BITS(op->dummy_bits), | ||
268 | COMMAND(op->command), CMD_EN(1)); | ||
269 | |||
270 | REG_SFC_DEV_ADDR(0) = op->addr_lo; | ||
271 | REG_SFC_DEV_PLUS(0) = op->addr_hi; | ||
272 | |||
273 | #ifdef NEED_SFC_DMA | ||
274 | sfc_status = SFC_STATUS_PENDING; | ||
275 | timeout_register(&sfc_lockup_tmo, sfc_lockup_tmo_cb, 10*HZ, 0); | ||
276 | REG_SFC_SCR = 0x1f; | ||
277 | REG_SFC_INTC &= ~intc_clear; | ||
278 | #endif | ||
279 | |||
280 | jz_overwritef(SFC_TRIG, FLUSH(1)); | ||
281 | jz_overwritef(SFC_TRIG, START(1)); | ||
282 | |||
283 | #ifndef NEED_SFC_DMA | ||
284 | if(op->flags & SFC_FLAG_READ) | ||
285 | sfc_fifo_read((unsigned*)op->buffer, op->data_bytes); | ||
286 | if(op->flags & SFC_FLAG_WRITE) | ||
287 | sfc_fifo_write((const unsigned*)op->buffer, op->data_bytes); | ||
288 | #endif | ||
289 | |||
290 | sfc_wait_end(); | ||
291 | |||
292 | #ifdef NEED_SFC_DMA | ||
293 | if(op->flags & SFC_FLAG_READ) | ||
294 | discard_dcache_range(op->buffer, op->data_bytes); | ||
295 | #endif | ||
296 | |||
297 | return sfc_status; | ||
298 | } | ||