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.c294
1 files changed, 104 insertions, 190 deletions
diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.c b/firmware/target/mips/ingenic_x1000/sfc-x1000.c
index c1fde89b70..5ade6bcc64 100644
--- a/firmware/target/mips/ingenic_x1000/sfc-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.c
@@ -21,86 +21,71 @@
21 21
22#include "system.h" 22#include "system.h"
23#include "kernel.h" 23#include "kernel.h"
24#include "panic.h"
25#include "sfc-x1000.h" 24#include "sfc-x1000.h"
25#include "clk-x1000.h"
26#include "irq-x1000.h" 26#include "irq-x1000.h"
27#include "x1000/sfc.h"
28#include "x1000/cpm.h"
29
30/* DMA works, but not in the SPL due to some hardware not being set up right.
31 * Only the SPL and bootloader actually require flash access, so to keep it
32 * simple, DMA is unconditionally disabled. */
33//#define NEED_SFC_DMA
34 27
28/* #define USE_DMA */
35#define FIFO_THRESH 31 29#define FIFO_THRESH 31
36 30
37#define SFC_STATUS_PENDING (-1) 31static void sfc_poll_wait(void);
32#ifdef USE_DMA
33static void sfc_irq_wait(void);
38 34
39#ifdef NEED_SFC_DMA
40static struct mutex sfc_mutex;
41static struct semaphore sfc_sema; 35static struct semaphore sfc_sema;
42static struct timeout sfc_lockup_tmo;
43static bool sfc_inited = false;
44static volatile int sfc_status;
45#else
46# define sfc_status SFC_STATUS_OK
47#endif
48
49void sfc_init(void)
50{
51#ifdef NEED_SFC_DMA
52 if(sfc_inited)
53 return;
54
55 mutex_init(&sfc_mutex);
56 semaphore_init(&sfc_sema, 1, 0);
57 sfc_inited = true;
58#endif
59}
60
61void sfc_lock(void)
62{
63#ifdef NEED_SFC_DMA
64 mutex_lock(&sfc_mutex);
65#endif
66}
67 36
68void sfc_unlock(void) 37/* This function pointer thing is a hack for the SPL, since it has to use
69{ 38 * the NAND driver directly and we can't afford to drag in the whole kernel
70#ifdef NEED_SFC_DMA 39 * just to wait on a semaphore. */
71 mutex_unlock(&sfc_mutex); 40static void(*sfc_wait)(void) = sfc_poll_wait;
72#endif 41#endif
73}
74 42
75void sfc_open(void) 43void sfc_open(void)
76{ 44{
77 jz_writef(CPM_CLKGR, SFC(0)); 45 jz_writef(CPM_CLKGR, SFC(0));
46#ifdef USE_DMA
47 jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32),
48 PHASE_NUM(1), THRESHOLD(FIFO_THRESH), WP_EN(1));
49#else
78 jz_writef(SFC_GLB, OP_MODE_V(SLAVE), PHASE_NUM(1), 50 jz_writef(SFC_GLB, OP_MODE_V(SLAVE), PHASE_NUM(1),
79 THRESHOLD(FIFO_THRESH), WP_EN(1)); 51 THRESHOLD(FIFO_THRESH), WP_EN(1));
52#endif
80 REG_SFC_CGE = 0; 53 REG_SFC_CGE = 0;
81 REG_SFC_INTC = 0x1f; 54 REG_SFC_INTC = 0x1f;
82 REG_SFC_MEM_ADDR = 0; 55 REG_SFC_MEM_ADDR = 0;
56}
57
58void sfc_close(void)
59{
60 REG_SFC_CGE = 0x1f;
61 jz_writef(CPM_CLKGR, SFC(1));
62}
63
64void sfc_irq_begin(void)
65{
66#ifdef USE_DMA
67 static bool inited = false;
68 if(!inited) {
69 semaphore_init(&sfc_sema, 1, 0);
70 inited = true;
71 }
83 72
84#ifdef NEED_SFC_DMA
85 jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32));
86 system_enable_irq(IRQ_SFC); 73 system_enable_irq(IRQ_SFC);
74 sfc_wait = sfc_irq_wait;
87#endif 75#endif
88} 76}
89 77
90void sfc_close(void) 78void sfc_irq_end(void)
91{ 79{
92#ifdef NEED_SFC_DMA 80#ifdef USE_DMA
93 system_disable_irq(IRQ_SFC); 81 system_disable_irq(IRQ_SFC);
82 sfc_wait = sfc_poll_wait;
94#endif 83#endif
95
96 REG_SFC_CGE = 0x1f;
97 jz_writef(CPM_CLKGR, SFC(1));
98} 84}
99 85
100void sfc_set_clock(uint32_t freq) 86void sfc_set_clock(uint32_t freq)
101{ 87{
102 /* TODO: This is a hack so we can use MPLL in the SPL. 88 /* FIXME: Get rid of this hack & allow defining a real clock tree... */
103 * There must be a better way to do this... */
104 x1000_clk_t clksrc = X1000_CLK_MPLL; 89 x1000_clk_t clksrc = X1000_CLK_MPLL;
105 uint32_t in_freq = clk_get(clksrc); 90 uint32_t in_freq = clk_get(clksrc);
106 if(in_freq < freq) { 91 if(in_freq < freq) {
@@ -115,170 +100,99 @@ void sfc_set_clock(uint32_t freq)
115 jz_writef(CPM_SSICDR, CE(0)); 100 jz_writef(CPM_SSICDR, CE(0));
116} 101}
117 102
118#ifdef NEED_SFC_DMA 103#ifndef USE_DMA
119static int sfc_lockup_tmo_cb(struct timeout* tmo) 104static void sfc_fifo_rdwr(bool write, void* buffer, uint32_t data_bytes)
120{
121 (void)tmo;
122
123 int irq = disable_irq_save();
124 if(sfc_status == SFC_STATUS_PENDING) {
125 sfc_status = SFC_STATUS_LOCKUP;
126 jz_overwritef(SFC_TRIG, STOP(1));
127 semaphore_release(&sfc_sema);
128 }
129
130 restore_irq(irq);
131 return 0;
132}
133
134static void sfc_wait_end(void)
135{ 105{
136 semaphore_wait(&sfc_sema, TIMEOUT_BLOCK); 106 uint32_t* word_buf = (uint32_t*)buffer;
137} 107 uint32_t sr_bit = write ? BM_SFC_SR_TREQ : BM_SFC_SR_RREQ;
138 108 uint32_t clr_bit = write ? BM_SFC_SCR_CLR_TREQ : BM_SFC_SCR_CLR_RREQ;
139void SFC(void) 109 uint32_t data_words = (data_bytes + 3) / 4;
140{
141 unsigned sr = REG_SFC_SR & ~REG_SFC_INTC;
142
143 if(jz_vreadf(sr, SFC_SR, OVER)) {
144 jz_overwritef(SFC_SCR, CLR_OVER(1));
145 sfc_status = SFC_STATUS_OVERFLOW;
146 } else if(jz_vreadf(sr, SFC_SR, UNDER)) {
147 jz_overwritef(SFC_SCR, CLR_UNDER(1));
148 sfc_status = SFC_STATUS_UNDERFLOW;
149 } else if(jz_vreadf(sr, SFC_SR, END)) {
150 jz_overwritef(SFC_SCR, CLR_END(1));
151 sfc_status = SFC_STATUS_OK;
152 } else {
153 panicf("SFC IRQ bug");
154 return;
155 }
156
157 /* Not sure this is wholly correct */
158 if(sfc_status != SFC_STATUS_OK)
159 jz_overwritef(SFC_TRIG, STOP(1));
160
161 REG_SFC_INTC = 0x1f;
162 semaphore_release(&sfc_sema);
163}
164#else
165/* Note the X1000 is *very* picky about how the SFC FIFOs are accessed
166 * so please do NOT try to rearrange the code without testing it first!
167 */
168
169static void sfc_fifo_read(unsigned* buffer, int data_bytes)
170{
171 int data_words = (data_bytes + 3) / 4;
172 while(data_words > 0) { 110 while(data_words > 0) {
173 if(jz_readf(SFC_SR, RREQ)) { 111 if(REG_SFC_SR & sr_bit) {
174 jz_overwritef(SFC_SCR, CLR_RREQ(1)); 112 REG_SFC_SCR = clr_bit;
175 113
176 int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words; 114 /* We need to read/write in bursts equal to FIFO threshold amount
115 * X1000 PM, 10.8.5, SFC > software guidelines > slave mode */
116 uint32_t amount = MIN(data_words, FIFO_THRESH);
177 data_words -= amount; 117 data_words -= amount;
178 while(amount > 0) {
179 *buffer++ = REG_SFC_DATA;
180 amount -= 1;
181 }
182 }
183 }
184}
185 118
186static void sfc_fifo_write(const unsigned* buffer, int data_bytes) 119 uint32_t* endptr = word_buf + amount;
187{ 120 for(; word_buf != endptr; ++word_buf) {
188 int data_words = (data_bytes + 3) / 4; 121 if(write)
189 while(data_words > 0) { 122 REG_SFC_DATA = *word_buf;
190 if(jz_readf(SFC_SR, TREQ)) { 123 else
191 jz_overwritef(SFC_SCR, CLR_TREQ(1)); 124 *word_buf = REG_SFC_DATA;
192
193 int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words;
194 data_words -= amount;
195 while(amount > 0) {
196 REG_SFC_DATA = *buffer++;
197 amount -= 1;
198 } 125 }
199 } 126 }
200 } 127 }
201} 128}
202
203static void sfc_wait_end(void)
204{
205 while(jz_readf(SFC_SR, END) == 0);
206 jz_overwritef(SFC_SCR, CLR_TREQ(1));
207}
208
209#endif /* NEED_SFC_DMA */
210
211int sfc_exec(const sfc_op* op)
212{
213#ifdef NEED_SFC_DMA
214 uint32_t intc_clear = jz_orm(SFC_INTC, MSK_END);
215#endif 129#endif
216 130
217 if(op->flags & (SFC_FLAG_READ|SFC_FLAG_WRITE)) { 131void sfc_exec(uint32_t cmd, uint32_t addr, void* data, uint32_t size)
218 jz_writef(SFC_TRAN_CONF(0), DATA_EN(1)); 132{
219 REG_SFC_TRAN_LENGTH = op->data_bytes; 133 /* Deal with transfer direction */
220#ifdef NEED_SFC_DMA 134 bool write = (size & SFC_WRITE) != 0;
221 REG_SFC_MEM_ADDR = PHYSADDR(op->buffer); 135 uint32_t glb = REG_SFC_GLB;
222#endif 136 if(data) {
223 137 if(write) {
224 if(op->flags & SFC_FLAG_READ) 138 jz_vwritef(glb, SFC_GLB, TRAN_DIR_V(WRITE));
225 { 139 size &= ~SFC_WRITE;
226 jz_writef(SFC_GLB, TRAN_DIR_V(READ)); 140#ifdef USE_DMA
227#ifdef NEED_SFC_DMA 141 commit_dcache_range(data, size);
228 discard_dcache_range(op->buffer, op->data_bytes);
229 intc_clear |= jz_orm(SFC_INTC, MSK_OVER);
230#endif 142#endif
231 } 143 } else {
232 else 144 jz_vwritef(glb, SFC_GLB, TRAN_DIR_V(READ));
233 { 145#ifdef USE_DMA
234 jz_writef(SFC_GLB, TRAN_DIR_V(WRITE)); 146 discard_dcache_range(data, size);
235#ifdef NEED_SFC_DMA
236 commit_dcache_range(op->buffer, op->data_bytes);
237 intc_clear |= jz_orm(SFC_INTC, MSK_UNDER);
238#endif 147#endif
239 } 148 }
240 } else {
241 jz_writef(SFC_TRAN_CONF(0), DATA_EN(0));
242 REG_SFC_TRAN_LENGTH = 0;
243#ifdef NEED_SFC_DMA
244 REG_SFC_MEM_ADDR = 0;
245#endif
246 } 149 }
247 150
248 bool dummy_first = (op->flags & SFC_FLAG_DUMMYFIRST) != 0; 151 /* Program transfer configuration */
249 jz_writef(SFC_TRAN_CONF(0), 152 REG_SFC_GLB = glb;
250 MODE(op->mode), POLL_EN(0), 153 REG_SFC_TRAN_LENGTH = size;
251 ADDR_WIDTH(op->addr_bytes), 154#ifdef USE_DMA
252 PHASE_FMT(dummy_first ? 1 : 0), 155 REG_SFC_MEM_ADDR = PHYSADDR(data);
253 DUMMY_BITS(op->dummy_bits), 156#endif
254 COMMAND(op->command), CMD_EN(1)); 157 REG_SFC_TRAN_CONF(0) = cmd;
255 158 REG_SFC_DEV_ADDR(0) = addr;
256 REG_SFC_DEV_ADDR(0) = op->addr_lo; 159 REG_SFC_DEV_PLUS(0) = 0;
257 REG_SFC_DEV_PLUS(0) = op->addr_hi;
258 160
259#ifdef NEED_SFC_DMA 161 /* Clear old interrupts */
260 sfc_status = SFC_STATUS_PENDING;
261 timeout_register(&sfc_lockup_tmo, sfc_lockup_tmo_cb, 10*HZ, 0);
262 REG_SFC_SCR = 0x1f; 162 REG_SFC_SCR = 0x1f;
263 REG_SFC_INTC &= ~intc_clear; 163 jz_writef(SFC_INTC, MSK_END(0));
264#endif
265 164
165 /* Start the command */
266 jz_overwritef(SFC_TRIG, FLUSH(1)); 166 jz_overwritef(SFC_TRIG, FLUSH(1));
267 jz_overwritef(SFC_TRIG, START(1)); 167 jz_overwritef(SFC_TRIG, START(1));
268 168
269#ifndef NEED_SFC_DMA 169 /* Data transfer by PIO or DMA, and wait for completion */
270 if(op->flags & SFC_FLAG_READ) 170#ifndef USE_DMA
271 sfc_fifo_read((unsigned*)op->buffer, op->data_bytes); 171 sfc_fifo_rdwr(write, data, size);
272 if(op->flags & SFC_FLAG_WRITE) 172 sfc_poll_wait();
273 sfc_fifo_write((const unsigned*)op->buffer, op->data_bytes); 173#else
174 sfc_wait();
274#endif 175#endif
176}
275 177
276 sfc_wait_end(); 178static void sfc_poll_wait(void)
179{
180 while(jz_readf(SFC_SR, END) == 0);
181 jz_overwritef(SFC_SCR, CLR_END(1));
182}
277 183
278#ifdef NEED_SFC_DMA 184#ifdef USE_DMA
279 if(op->flags & SFC_FLAG_READ) 185static void sfc_irq_wait(void)
280 discard_dcache_range(op->buffer, op->data_bytes); 186{
281#endif 187 semaphore_wait(&sfc_sema, TIMEOUT_BLOCK);
188}
282 189
283 return sfc_status; 190void SFC(void)
191{
192 /* the only interrupt we use is END; errors are basically not
193 * possible with the SPI interface... */
194 semaphore_release(&sfc_sema);
195 jz_overwritef(SFC_SCR, CLR_END(1));
196 jz_writef(SFC_INTC, MSK_END(1));
284} 197}
198#endif