summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/sfc-x1000.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/sfc-x1000.c')
-rw-r--r--firmware/target/mips/ingenic_x1000/sfc-x1000.c298
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
53static struct mutex sfc_mutex;
54static struct semaphore sfc_sema;
55static struct timeout sfc_lockup_tmo;
56static bool sfc_inited = false;
57static volatile int sfc_status;
58#else
59# define sfc_status SFC_STATUS_OK
60#endif
61
62void 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
74void sfc_lock(void)
75{
76#ifdef NEED_SFC_DMA
77 mutex_lock(&sfc_mutex);
78#endif
79}
80
81void sfc_unlock(void)
82{
83#ifdef NEED_SFC_DMA
84 mutex_unlock(&sfc_mutex);
85#endif
86}
87
88void 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
104void 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
114void 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
133static 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
148static void sfc_wait_end(void)
149{
150 semaphore_wait(&sfc_sema, TIMEOUT_BLOCK);
151}
152
153void 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
183void 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
200void 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
217static 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
225int 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}