diff options
Diffstat (limited to 'firmware/target/mips')
29 files changed, 6070 insertions, 71 deletions
diff --git a/firmware/target/mips/ingenic_jz47xx/app.lds b/firmware/target/mips/ingenic_jz47xx/app.lds index a8ac6ff0bf..85c332b182 100644 --- a/firmware/target/mips/ingenic_jz47xx/app.lds +++ b/firmware/target/mips/ingenic_jz47xx/app.lds | |||
@@ -5,8 +5,10 @@ OUTPUT_ARCH(MIPS) | |||
5 | ENTRY(_start) | 5 | ENTRY(_start) |
6 | STARTUP(target/mips/ingenic_jz47xx/crt0.o) | 6 | STARTUP(target/mips/ingenic_jz47xx/crt0.o) |
7 | 7 | ||
8 | #define DRAMORIG 0x80004000 | 8 | #define STUBOFFSET 0x4000 |
9 | #define DRAMSIZE (MEMORYSIZE * 0x100000) | 9 | |
10 | #define DRAMORIG (0x80000000 + STUBOFFSET) | ||
11 | #define DRAMSIZE (MEMORYSIZE * 0x100000 - STUBOFFSET) | ||
10 | #define IRAMORIG 0x80000000 | 12 | #define IRAMORIG 0x80000000 |
11 | #define IRAMSIZE 16K | 13 | #define IRAMSIZE 16K |
12 | 14 | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/ata-nand-jz4740.c b/firmware/target/mips/ingenic_jz47xx/ata-nand-jz4740.c index ac4092f043..0ce0ed1e19 100644 --- a/firmware/target/mips/ingenic_jz47xx/ata-nand-jz4740.c +++ b/firmware/target/mips/ingenic_jz47xx/ata-nand-jz4740.c | |||
@@ -20,7 +20,7 @@ | |||
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | 21 | ||
22 | #include "config.h" | 22 | #include "config.h" |
23 | #include "jz4740.h" | 23 | #include "cpu.h" |
24 | #include "nand.h" | 24 | #include "nand.h" |
25 | #include "nand_id.h" | 25 | #include "nand_id.h" |
26 | #include "system.h" | 26 | #include "system.h" |
diff --git a/firmware/target/mips/ingenic_jz47xx/ata-nand-jz4760.c b/firmware/target/mips/ingenic_jz47xx/ata-nand-jz4760.c new file mode 100644 index 0000000000..b3cc589528 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/ata-nand-jz4760.c | |||
@@ -0,0 +1,692 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | #include "cpu.h" | ||
24 | #include "nand.h" | ||
25 | #include "nand_id.h" | ||
26 | #include "system.h" | ||
27 | #include "panic.h" | ||
28 | #include "kernel.h" | ||
29 | #include "storage.h" | ||
30 | #include "string.h" | ||
31 | /*#define LOGF_ENABLE*/ | ||
32 | #include "logf.h" | ||
33 | |||
34 | //#define USE_DMA | ||
35 | //#define USE_ECC | ||
36 | |||
37 | /* | ||
38 | * Standard NAND flash commands | ||
39 | */ | ||
40 | #define NAND_CMD_READ0 0 | ||
41 | #define NAND_CMD_READ1 1 | ||
42 | #define NAND_CMD_RNDOUT 5 | ||
43 | #define NAND_CMD_PAGEPROG 0x10 | ||
44 | #define NAND_CMD_READOOB 0x50 | ||
45 | #define NAND_CMD_ERASE1 0x60 | ||
46 | #define NAND_CMD_STATUS 0x70 | ||
47 | #define NAND_CMD_STATUS_MULTI 0x71 | ||
48 | #define NAND_CMD_SEQIN 0x80 | ||
49 | #define NAND_CMD_RNDIN 0x85 | ||
50 | #define NAND_CMD_READID 0x90 | ||
51 | #define NAND_CMD_ERASE2 0xd0 | ||
52 | #define NAND_CMD_RESET 0xff | ||
53 | |||
54 | /* Extended commands for large page devices */ | ||
55 | #define NAND_CMD_READSTART 0x30 | ||
56 | #define NAND_CMD_RNDOUTSTART 0xE0 | ||
57 | #define NAND_CMD_CACHEDPROG 0x15 | ||
58 | |||
59 | /* Status bits */ | ||
60 | #define NAND_STATUS_FAIL 0x01 | ||
61 | #define NAND_STATUS_FAIL_N1 0x02 | ||
62 | #define NAND_STATUS_TRUE_READY 0x20 | ||
63 | #define NAND_STATUS_READY 0x40 | ||
64 | #define NAND_STATUS_WP 0x80 | ||
65 | |||
66 | /* | ||
67 | * NAND parameter struct | ||
68 | */ | ||
69 | struct nand_param { | ||
70 | unsigned int bus_width; /* data bus width: 8-bit/16-bit */ | ||
71 | unsigned int row_cycle; /* row address cycles: 2/3 */ | ||
72 | unsigned int page_size; /* page size in bytes: 512/2048/4096 */ | ||
73 | unsigned int oob_size; /* oob size in bytes: 16/64/128 */ | ||
74 | unsigned int page_per_block; /* pages per block: 32/64/128 */ | ||
75 | unsigned int bad_block_pos; /* bad block pos in oob: 0/5 */ | ||
76 | }; | ||
77 | |||
78 | /* | ||
79 | * jz4760_nand.c | ||
80 | * | ||
81 | * NAND read routine for JZ4760 | ||
82 | * | ||
83 | * Copyright (c) 2005-2008 Ingenic Semiconductor Inc. | ||
84 | * | ||
85 | */ | ||
86 | |||
87 | #define CFG_NAND_BASE 0xBA000000 | ||
88 | #define NAND_ADDR_OFFSET 0x00800000 | ||
89 | #define NAND_CMD_OFFSET 0x00400000 | ||
90 | |||
91 | #define CFG_NAND_SMCR1 0x0d444400 | ||
92 | |||
93 | #define NAND_DATAPORT CFG_NAND_BASE | ||
94 | #define NAND_ADDRPORT (CFG_NAND_BASE | NAND_ADDR_OFFSET) | ||
95 | #define NAND_COMMPORT (CFG_NAND_BASE | NAND_CMD_OFFSET) | ||
96 | |||
97 | #define ECC_BLOCK 512 | ||
98 | #define ECC_POS 24 | ||
99 | #define PAR_SIZE 13 | ||
100 | |||
101 | #define __nand_cmd(n) (REG8(NAND_COMMPORT) = (n)) | ||
102 | #define __nand_addr(n) (REG8(NAND_ADDRPORT) = (n)) | ||
103 | #define __nand_data8() (REG8(NAND_DATAPORT)) | ||
104 | #define __nand_data16() (REG16(NAND_DATAPORT)) | ||
105 | |||
106 | #define __nand_select() (REG_NEMC_NFCSR |= NEMC_NFCSR_NFE1 | NEMC_NFCSR_NFCE1) | ||
107 | #define __nand_deselect() (REG_NEMC_NFCSR &= ~(NEMC_NFCSR_NFE1 | NEMC_NFCSR_NFCE1)) | ||
108 | |||
109 | /*--------------------------------------------------------------*/ | ||
110 | |||
111 | static struct nand_info* chip_info = NULL; | ||
112 | static struct nand_info* bank; | ||
113 | static unsigned long nand_size; | ||
114 | static struct nand_param internal_param; | ||
115 | static struct mutex nand_mtx; | ||
116 | #ifdef USE_DMA | ||
117 | static struct mutex nand_dma_mtx; | ||
118 | static struct semaphore nand_dma_complete; | ||
119 | #endif | ||
120 | static unsigned char temp_page[2048]; /* Max page size */ | ||
121 | |||
122 | static inline void jz_nand_wait_ready(void) | ||
123 | { | ||
124 | unsigned int timeout = 1000; | ||
125 | while ((REG_GPIO_PXPIN(0) & 0x00100000) && timeout--); | ||
126 | while (!(REG_GPIO_PXPIN(0) & 0x00100000)); | ||
127 | } | ||
128 | |||
129 | #ifndef USE_DMA | ||
130 | static inline void jz_nand_read_buf16(void *buf, int count) | ||
131 | { | ||
132 | register int i; | ||
133 | register unsigned short *p = (unsigned short *)buf; | ||
134 | |||
135 | for (i = 0; i < count; i += 2) | ||
136 | *p++ = __nand_data16(); | ||
137 | } | ||
138 | |||
139 | static inline void jz_nand_read_buf8(void *buf, int count) | ||
140 | { | ||
141 | register int i; | ||
142 | register unsigned char *p = (unsigned char *)buf; | ||
143 | |||
144 | for (i = 0; i < count; i++) | ||
145 | *p++ = __nand_data8(); | ||
146 | } | ||
147 | #else | ||
148 | static void jz_nand_write_dma(void *source, unsigned int len, int bw) | ||
149 | { | ||
150 | mutex_lock(&nand_dma_mtx); | ||
151 | |||
152 | if(((unsigned int)source < 0xa0000000) && len) | ||
153 | dma_cache_wback_inv((unsigned long)source, len); | ||
154 | |||
155 | dma_enable(); | ||
156 | |||
157 | REG_DMAC_DCCSR(DMA_NAND_CHANNEL) = DMAC_DCCSR_NDES; | ||
158 | REG_DMAC_DSAR(DMA_NAND_CHANNEL) = PHYSADDR((unsigned long)source); | ||
159 | REG_DMAC_DTAR(DMA_NAND_CHANNEL) = PHYSADDR((unsigned long)NAND_DATAPORT); | ||
160 | REG_DMAC_DTCR(DMA_NAND_CHANNEL) = len / 16; | ||
161 | REG_DMAC_DRSR(DMA_NAND_CHANNEL) = DMAC_DRSR_RS_AUTO; | ||
162 | REG_DMAC_DCMD(DMA_NAND_CHANNEL) = (DMAC_DCMD_SAI| DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DS_16BYTE | | ||
163 | (bw == 8 ? DMAC_DCMD_DWDH_8 : DMAC_DCMD_DWDH_16)); | ||
164 | |||
165 | REG_DMAC_DCCSR(DMA_NAND_CHANNEL) |= DMAC_DCCSR_EN; /* Enable DMA channel */ | ||
166 | #if 1 | ||
167 | while( REG_DMAC_DTCR(DMA_NAND_CHANNEL) ) | ||
168 | yield(); | ||
169 | #else | ||
170 | REG_DMAC_DCMD(DMA_NAND_CHANNEL) |= DMAC_DCMD_TIE; /* Enable DMA interrupt */ | ||
171 | semaphore_wait(&nand_dma_complete, TIMEOUT_BLOCK); | ||
172 | #endif | ||
173 | |||
174 | REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_EN; /* Disable DMA channel */ | ||
175 | |||
176 | dma_disable(); | ||
177 | |||
178 | mutex_unlock(&nand_dma_mtx); | ||
179 | } | ||
180 | |||
181 | static void jz_nand_read_dma(void *target, unsigned int len, int bw) | ||
182 | { | ||
183 | mutex_lock(&nand_dma_mtx); | ||
184 | |||
185 | if(((unsigned int)target < 0xa0000000) && len) | ||
186 | dma_cache_wback_inv((unsigned long)target, len); | ||
187 | |||
188 | dma_enable(); | ||
189 | |||
190 | REG_DMAC_DCCSR(DMA_NAND_CHANNEL) = DMAC_DCCSR_NDES ; | ||
191 | REG_DMAC_DSAR(DMA_NAND_CHANNEL) = PHYSADDR((unsigned long)NAND_DATAPORT); | ||
192 | REG_DMAC_DTAR(DMA_NAND_CHANNEL) = PHYSADDR((unsigned long)target); | ||
193 | REG_DMAC_DTCR(DMA_NAND_CHANNEL) = len / 4; | ||
194 | REG_DMAC_DRSR(DMA_NAND_CHANNEL) = DMAC_DRSR_RS_AUTO; | ||
195 | REG_DMAC_DCMD(DMA_NAND_CHANNEL) = (DMAC_DCMD_SAI| DMAC_DCMD_DAI | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | | ||
196 | (bw == 8 ? DMAC_DCMD_SWDH_8 : DMAC_DCMD_SWDH_16)); | ||
197 | REG_DMAC_DCCSR(DMA_NAND_CHANNEL) |= DMAC_DCCSR_EN; /* Enable DMA channel */ | ||
198 | #if 1 | ||
199 | while( REG_DMAC_DTCR(DMA_NAND_CHANNEL) ) | ||
200 | yield(); | ||
201 | #else | ||
202 | REG_DMAC_DCMD(DMA_NAND_CHANNEL) |= DMAC_DCMD_TIE; /* Enable DMA interrupt */ | ||
203 | semaphore_wait(&nand_dma_complete, TIMEOUT_BLOCK); | ||
204 | #endif | ||
205 | |||
206 | //REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_EN; /* Disable DMA channel */ | ||
207 | |||
208 | dma_disable(); | ||
209 | |||
210 | mutex_unlock(&nand_dma_mtx); | ||
211 | } | ||
212 | |||
213 | void DMA_CALLBACK(DMA_NAND_CHANNEL)(void) | ||
214 | { | ||
215 | if (REG_DMAC_DCCSR(DMA_NAND_CHANNEL) & DMAC_DCCSR_HLT) | ||
216 | REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_HLT; | ||
217 | |||
218 | if (REG_DMAC_DCCSR(DMA_NAND_CHANNEL) & DMAC_DCCSR_AR) | ||
219 | REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_AR; | ||
220 | |||
221 | if (REG_DMAC_DCCSR(DMA_NAND_CHANNEL) & DMAC_DCCSR_CT) | ||
222 | REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_CT; | ||
223 | |||
224 | if (REG_DMAC_DCCSR(DMA_NAND_CHANNEL) & DMAC_DCCSR_TT) | ||
225 | REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_TT; | ||
226 | |||
227 | semaphore_release(&nand_dma_complete); | ||
228 | } | ||
229 | #endif /* USE_DMA */ | ||
230 | |||
231 | static inline void jz_nand_read_buf(void *buf, int count, int bw) | ||
232 | { | ||
233 | #ifdef USE_DMA | ||
234 | if (bw == 8) | ||
235 | jz_nand_read_dma(buf, count, 8); | ||
236 | else | ||
237 | jz_nand_read_dma(buf, count, 16); | ||
238 | #else | ||
239 | if (bw == 8) | ||
240 | jz_nand_read_buf8(buf, count); | ||
241 | else | ||
242 | jz_nand_read_buf16(buf, count); | ||
243 | #endif | ||
244 | } | ||
245 | |||
246 | #ifdef USE_ECC | ||
247 | /* | ||
248 | * Correct 1~9-bit errors in 512-bytes data | ||
249 | */ | ||
250 | static void jz_rs_correct(unsigned char *dat, int idx, int mask) | ||
251 | { | ||
252 | int i, j; | ||
253 | unsigned short d, d1, dm; | ||
254 | |||
255 | i = (idx * 9) >> 3; | ||
256 | j = (idx * 9) & 0x7; | ||
257 | |||
258 | i = (j == 0) ? (i - 1) : i; | ||
259 | j = (j == 0) ? 7 : (j - 1); | ||
260 | |||
261 | if (i > 512) | ||
262 | return; | ||
263 | |||
264 | if (i == 512) | ||
265 | d = dat[i - 1]; | ||
266 | else | ||
267 | d = (dat[i] << 8) | dat[i - 1]; | ||
268 | |||
269 | d1 = (d >> j) & 0x1ff; | ||
270 | d1 ^= mask; | ||
271 | |||
272 | dm = ~(0x1ff << j); | ||
273 | d = (d & dm) | (d1 << j); | ||
274 | |||
275 | dat[i - 1] = d & 0xff; | ||
276 | if (i < 512) | ||
277 | dat[i] = (d >> 8) & 0xff; | ||
278 | } | ||
279 | #endif | ||
280 | |||
281 | /* | ||
282 | * Read oob | ||
283 | */ | ||
284 | static int jz_nand_read_oob(unsigned long page_addr, unsigned char *buf, int size) | ||
285 | { | ||
286 | struct nand_param *nandp = &internal_param; | ||
287 | int page_size, row_cycle, bus_width; | ||
288 | int col_addr; | ||
289 | |||
290 | page_size = nandp->page_size; | ||
291 | row_cycle = nandp->row_cycle; | ||
292 | bus_width = nandp->bus_width; | ||
293 | |||
294 | if (page_size >= 2048) | ||
295 | col_addr = page_size; | ||
296 | else | ||
297 | col_addr = 0; | ||
298 | |||
299 | if (page_size >= 2048) | ||
300 | /* Send READ0 command */ | ||
301 | __nand_cmd(NAND_CMD_READ0); | ||
302 | else | ||
303 | /* Send READOOB command */ | ||
304 | __nand_cmd(NAND_CMD_READOOB); | ||
305 | |||
306 | /* Send column address */ | ||
307 | __nand_addr(col_addr & 0xff); | ||
308 | if (page_size >= 2048) | ||
309 | __nand_addr((col_addr >> 8) & 0xff); | ||
310 | |||
311 | /* Send page address */ | ||
312 | __nand_addr(page_addr & 0xff); | ||
313 | __nand_addr((page_addr >> 8) & 0xff); | ||
314 | if (row_cycle == 3) | ||
315 | __nand_addr((page_addr >> 16) & 0xff); | ||
316 | |||
317 | /* Send READSTART command for 2048 ps NAND */ | ||
318 | if (page_size >= 2048) | ||
319 | __nand_cmd(NAND_CMD_READSTART); | ||
320 | |||
321 | /* Wait for device ready */ | ||
322 | jz_nand_wait_ready(); | ||
323 | |||
324 | /* Read oob data */ | ||
325 | jz_nand_read_buf(buf, size, bus_width); | ||
326 | |||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | |||
331 | /* | ||
332 | * nand_read_page() | ||
333 | * | ||
334 | * Input: | ||
335 | * | ||
336 | * block - block number: 0, 1, 2, ... | ||
337 | * page - page number within a block: 0, 1, 2, ... | ||
338 | * dst - pointer to target buffer | ||
339 | */ | ||
340 | static int jz_nand_read_page(unsigned long page_addr, unsigned char *dst) | ||
341 | { | ||
342 | struct nand_param *nandp = &internal_param; | ||
343 | int page_size, oob_size, page_per_block; | ||
344 | int row_cycle, bus_width, ecc_count; | ||
345 | int i; | ||
346 | #ifdef USE_ECC | ||
347 | int j; | ||
348 | #endif | ||
349 | unsigned char *data_buf; | ||
350 | unsigned char oob_buf[nandp->oob_size]; | ||
351 | |||
352 | page_size = nandp->page_size; | ||
353 | oob_size = nandp->oob_size; | ||
354 | page_per_block = nandp->page_per_block; | ||
355 | row_cycle = nandp->row_cycle; | ||
356 | bus_width = nandp->bus_width; | ||
357 | |||
358 | /* | ||
359 | * Read oob data | ||
360 | */ | ||
361 | jz_nand_read_oob(page_addr, oob_buf, oob_size); | ||
362 | |||
363 | /* | ||
364 | * Read page data | ||
365 | */ | ||
366 | |||
367 | /* Send READ0 command */ | ||
368 | __nand_cmd(NAND_CMD_READ0); | ||
369 | |||
370 | /* Send column address */ | ||
371 | __nand_addr(0); | ||
372 | if (page_size >= 2048) | ||
373 | __nand_addr(0); | ||
374 | |||
375 | /* Send page address */ | ||
376 | __nand_addr(page_addr & 0xff); | ||
377 | __nand_addr((page_addr >> 8) & 0xff); | ||
378 | if (row_cycle >= 3) | ||
379 | __nand_addr((page_addr >> 16) & 0xff); | ||
380 | |||
381 | /* Send READSTART command for 2048 ps NAND */ | ||
382 | if (page_size >= 2048) | ||
383 | __nand_cmd(NAND_CMD_READSTART); | ||
384 | |||
385 | /* Wait for device ready */ | ||
386 | jz_nand_wait_ready(); | ||
387 | |||
388 | /* Read page data */ | ||
389 | data_buf = dst; | ||
390 | |||
391 | ecc_count = page_size / ECC_BLOCK; | ||
392 | |||
393 | for (i = 0; i < ecc_count; i++) | ||
394 | { | ||
395 | #ifdef USE_ECC | ||
396 | volatile unsigned char *paraddr = (volatile unsigned char *)EMC_NFPAR0; | ||
397 | unsigned int stat; | ||
398 | |||
399 | /* Enable RS decoding */ | ||
400 | REG_EMC_NFINTS = 0x0; | ||
401 | __ecc_decoding_4bit(); | ||
402 | #endif | ||
403 | |||
404 | /* Read data */ | ||
405 | jz_nand_read_buf((void *)data_buf, ECC_BLOCK, bus_width); | ||
406 | |||
407 | #ifdef USE_ECC | ||
408 | /* Set PAR values */ | ||
409 | for (j = 0; j < PAR_SIZE; j++) | ||
410 | *paraddr++ = oob_buf[ECC_POS + i*PAR_SIZE + j]; | ||
411 | |||
412 | /* Set PRDY */ | ||
413 | REG_EMC_NFECR |= EMC_NFECR_PRDY; | ||
414 | |||
415 | /* Wait for completion */ | ||
416 | __ecc_decode_sync(); | ||
417 | |||
418 | /* Disable decoding */ | ||
419 | __ecc_disable(); | ||
420 | |||
421 | /* Check result of decoding */ | ||
422 | stat = REG_EMC_NFINTS; | ||
423 | if (stat & EMC_NFINTS_ERR) | ||
424 | { | ||
425 | /* Error occurred */ | ||
426 | if (stat & EMC_NFINTS_UNCOR) | ||
427 | { | ||
428 | /* Uncorrectable error occurred */ | ||
429 | logf("Uncorrectable ECC error at NAND page address 0x%lx", page_addr); | ||
430 | return -1; | ||
431 | } | ||
432 | else | ||
433 | { | ||
434 | unsigned int errcnt, index, mask; | ||
435 | |||
436 | errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT; | ||
437 | switch (errcnt) | ||
438 | { | ||
439 | case 4: | ||
440 | index = (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT; | ||
441 | mask = (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT; | ||
442 | jz_rs_correct(data_buf, index, mask); | ||
443 | case 3: | ||
444 | index = (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT; | ||
445 | mask = (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT; | ||
446 | jz_rs_correct(data_buf, index, mask); | ||
447 | case 2: | ||
448 | index = (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT; | ||
449 | mask = (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT; | ||
450 | jz_rs_correct(data_buf, index, mask); | ||
451 | case 1: | ||
452 | index = (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT; | ||
453 | mask = (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT; | ||
454 | jz_rs_correct(data_buf, index, mask); | ||
455 | break; | ||
456 | default: | ||
457 | break; | ||
458 | } | ||
459 | } | ||
460 | } | ||
461 | #endif | ||
462 | |||
463 | data_buf += ECC_BLOCK; | ||
464 | } | ||
465 | |||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | static int jz_nand_init(void) | ||
470 | { | ||
471 | unsigned char cData[5]; | ||
472 | |||
473 | __gpio_as_nand_16bit(1); | ||
474 | |||
475 | REG_NEMC_SMCR1 = CFG_NAND_SMCR1 | 0x40; | ||
476 | |||
477 | __nand_select(); | ||
478 | |||
479 | __nand_cmd(NAND_CMD_READID); | ||
480 | __nand_addr(NAND_CMD_READ0); | ||
481 | cData[0] = __nand_data8(); | ||
482 | cData[1] = __nand_data8(); | ||
483 | cData[2] = __nand_data8(); | ||
484 | cData[3] = __nand_data8(); | ||
485 | cData[4] = __nand_data8(); | ||
486 | |||
487 | __nand_deselect(); | ||
488 | |||
489 | logf("NAND chip %d: 0x%x 0x%x 0x%x 0x%x 0x%x", i+1, cData[0], cData[1], | ||
490 | cData[2], cData[3], cData[4]); | ||
491 | |||
492 | bank = nand_identify(cData); | ||
493 | |||
494 | if(bank == NULL) | ||
495 | { | ||
496 | panicf("Unknown NAND flash chip: 0x%x 0x%x 0x%x 0x%x 0x%x", cData[0], | ||
497 | cData[1], cData[2], cData[3], cData[4]); | ||
498 | return -1; /* panicf() doesn't return though */ | ||
499 | } | ||
500 | |||
501 | chip_info = bank; | ||
502 | |||
503 | internal_param.bus_width = 16; | ||
504 | internal_param.row_cycle = chip_info->row_cycles; | ||
505 | internal_param.page_size = chip_info->page_size; | ||
506 | internal_param.oob_size = chip_info->spare_size; | ||
507 | internal_param.page_per_block = chip_info->pages_per_block; | ||
508 | internal_param.bad_block_pos = 0; | ||
509 | |||
510 | nand_size = ((chip_info->page_size * chip_info->blocks_per_bank * chip_info->pages_per_block) - 0x200000) / 512; | ||
511 | |||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | int nand_init(void) | ||
516 | { | ||
517 | int res = 0; | ||
518 | static bool inited = false; | ||
519 | |||
520 | if(!inited) | ||
521 | { | ||
522 | res = jz_nand_init(); | ||
523 | mutex_init(&nand_mtx); | ||
524 | #ifdef USE_DMA | ||
525 | mutex_init(&nand_dma_mtx); | ||
526 | semaphore_init(&nand_dma_complete, 1, 0); | ||
527 | system_enable_irq(DMA_IRQ(DMA_NAND_CHANNEL)); | ||
528 | #endif | ||
529 | |||
530 | inited = true; | ||
531 | } | ||
532 | |||
533 | return res; | ||
534 | } | ||
535 | |||
536 | static inline int read_sector(unsigned long start, unsigned int count, | ||
537 | void* buf, unsigned int chip_size) | ||
538 | { | ||
539 | register int ret; | ||
540 | |||
541 | if(UNLIKELY(start % chip_size == 0 && count == chip_size)) | ||
542 | ret = jz_nand_read_page(start / chip_size, buf); | ||
543 | else | ||
544 | { | ||
545 | ret = jz_nand_read_page(start / chip_size, temp_page); | ||
546 | memcpy(buf, temp_page + (start % chip_size), count); | ||
547 | } | ||
548 | |||
549 | return ret; | ||
550 | } | ||
551 | |||
552 | static inline int write_sector(unsigned long start, unsigned int count, | ||
553 | const void* buf, unsigned int chip_size) | ||
554 | { | ||
555 | int ret = 0; | ||
556 | |||
557 | (void)start; | ||
558 | (void)count; | ||
559 | (void)buf; | ||
560 | (void)chip_size; | ||
561 | |||
562 | /* TODO */ | ||
563 | |||
564 | return ret; | ||
565 | } | ||
566 | |||
567 | int nand_read_sectors(IF_MV(int drive,) unsigned long start, int count, void* buf) | ||
568 | { | ||
569 | #ifdef HAVE_MULTIVOLUME | ||
570 | (void)drive; | ||
571 | #endif | ||
572 | int ret = 0; | ||
573 | unsigned int _count, chip_size = chip_info->page_size; | ||
574 | unsigned long _start; | ||
575 | |||
576 | logf("start"); | ||
577 | mutex_lock(&nand_mtx); | ||
578 | |||
579 | _start = start << 9; | ||
580 | _start += 0x200000; /* skip BL */ | ||
581 | _count = count << 9; | ||
582 | |||
583 | __nand_select(); | ||
584 | ret = read_sector(_start, _count, buf, chip_size); | ||
585 | __nand_deselect(); | ||
586 | |||
587 | mutex_unlock(&nand_mtx); | ||
588 | |||
589 | logf("nand_read_sectors(%ld, %d, 0x%x): %d", start, count, (int)buf, ret); | ||
590 | |||
591 | return ret; | ||
592 | } | ||
593 | |||
594 | int nand_write_sectors(IF_MV(int drive,) unsigned long start, int count, const void* buf) | ||
595 | { | ||
596 | #ifdef HAVE_MULTIVOLUME | ||
597 | (void)drive; | ||
598 | #endif | ||
599 | int ret = 0; | ||
600 | unsigned int _count, chip_size = chip_info->page_size; | ||
601 | unsigned long _start; | ||
602 | |||
603 | logf("start"); | ||
604 | mutex_lock(&nand_mtx); | ||
605 | |||
606 | _start = start << 9; | ||
607 | _start += chip_info->page_size * chip_info->pages_per_block; /* skip BL */ | ||
608 | _count = count << 9; | ||
609 | |||
610 | __nand_select(); | ||
611 | ret = write_sector(_start, _count, buf, chip_size); | ||
612 | __nand_deselect(); | ||
613 | |||
614 | mutex_unlock(&nand_mtx); | ||
615 | |||
616 | logf("nand_write_sectors(%ld, %d, 0x%x): %d", start, count, (int)buf, ret); | ||
617 | |||
618 | return ret; | ||
619 | } | ||
620 | |||
621 | #ifdef HAVE_STORAGE_FLUSH | ||
622 | int nand_flush(void) | ||
623 | { | ||
624 | return 0; | ||
625 | } | ||
626 | #endif | ||
627 | |||
628 | void nand_spindown(int seconds) | ||
629 | { | ||
630 | /* null */ | ||
631 | (void)seconds; | ||
632 | } | ||
633 | |||
634 | void nand_sleep(void) | ||
635 | { | ||
636 | /* null */ | ||
637 | } | ||
638 | |||
639 | void nand_spin(void) | ||
640 | { | ||
641 | /* null */ | ||
642 | } | ||
643 | |||
644 | void nand_enable(bool on) | ||
645 | { | ||
646 | /* null - flash controller is enabled/disabled as needed. */ | ||
647 | (void)on; | ||
648 | } | ||
649 | |||
650 | /* TODO */ | ||
651 | long nand_last_disk_activity(void) | ||
652 | { | ||
653 | return 0; | ||
654 | } | ||
655 | |||
656 | int nand_spinup_time(void) | ||
657 | { | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | void nand_sleepnow(void) | ||
662 | { | ||
663 | } | ||
664 | |||
665 | #ifdef STORAGE_GET_INFO | ||
666 | void nand_get_info(IF_MV(int drive,) struct storage_info *info) | ||
667 | { | ||
668 | #ifdef HAVE_MULTIVOLUME | ||
669 | (void)drive; | ||
670 | #endif | ||
671 | |||
672 | /* firmware version */ | ||
673 | info->revision="0.00"; | ||
674 | |||
675 | info->vendor="Rockbox"; | ||
676 | info->product="NAND Storage"; | ||
677 | |||
678 | /* blocks count */ | ||
679 | info->num_sectors = nand_size; | ||
680 | info->sector_size = 512; | ||
681 | } | ||
682 | #endif | ||
683 | |||
684 | #ifdef CONFIG_STORAGE_MULTI | ||
685 | int nand_num_drives(int first_drive) | ||
686 | { | ||
687 | /* We don't care which logical drive number(s) we have been assigned */ | ||
688 | (void)first_drive; | ||
689 | |||
690 | return 1; | ||
691 | } | ||
692 | #endif | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4740.c b/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4740.c index 9565551df6..9215d7d08a 100644 --- a/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4740.c +++ b/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4740.c | |||
@@ -21,7 +21,7 @@ | |||
21 | 21 | ||
22 | #include "config.h" | 22 | #include "config.h" |
23 | #include "gcc_extensions.h" | 23 | #include "gcc_extensions.h" |
24 | #include "jz4740.h" | 24 | #include "cpu.h" |
25 | #include "ata-sd-target.h" | 25 | #include "ata-sd-target.h" |
26 | #include "led.h" | 26 | #include "led.h" |
27 | #include "sdmmc.h" | 27 | #include "sdmmc.h" |
diff --git a/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4760.c b/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4760.c new file mode 100644 index 0000000000..c34f74a202 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/ata-sd-jz4760.c | |||
@@ -0,0 +1,1487 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | #include "gcc_extensions.h" | ||
24 | #include "cpu.h" | ||
25 | #include "ata-sd-target.h" | ||
26 | #include "dma-target.h" | ||
27 | #include "led.h" | ||
28 | #include "sdmmc.h" | ||
29 | #include "logf.h" | ||
30 | #include "storage.h" | ||
31 | #include "string.h" | ||
32 | |||
33 | static long last_disk_activity = -1; | ||
34 | static tCardInfo card[NUM_DRIVES]; | ||
35 | |||
36 | static struct mutex sd_mtx; | ||
37 | static struct semaphore sd_wakeup; | ||
38 | |||
39 | static int use_4bit[NUM_DRIVES]; | ||
40 | static int num_6[NUM_DRIVES]; | ||
41 | static int sd2_0[NUM_DRIVES]; | ||
42 | |||
43 | #define SD_DMA_ENABLE 1 | ||
44 | |||
45 | //#define DEBUG(x...) logf(x) | ||
46 | #define DEBUG(x, ...) | ||
47 | |||
48 | /* volumes */ | ||
49 | #define SD_SLOT_1 0 /* SD card 1 */ | ||
50 | #define SD_SLOT_2 1 /* SD card 2 */ | ||
51 | |||
52 | #define MSC_CHN(n) (2-n) | ||
53 | |||
54 | #define SD_IRQ_MASK(n) \ | ||
55 | do { \ | ||
56 | REG_MSC_IMASK(n) = 0xffff; \ | ||
57 | REG_MSC_IREG(n) = 0xffff; \ | ||
58 | } while (0) | ||
59 | |||
60 | /* Error codes */ | ||
61 | enum sd_result_t | ||
62 | { | ||
63 | SD_NO_RESPONSE = -1, | ||
64 | SD_NO_ERROR = 0, | ||
65 | SD_ERROR_OUT_OF_RANGE, | ||
66 | SD_ERROR_ADDRESS, | ||
67 | SD_ERROR_BLOCK_LEN, | ||
68 | SD_ERROR_ERASE_SEQ, | ||
69 | SD_ERROR_ERASE_PARAM, | ||
70 | SD_ERROR_WP_VIOLATION, | ||
71 | SD_ERROR_CARD_IS_LOCKED, | ||
72 | SD_ERROR_LOCK_UNLOCK_FAILED, | ||
73 | SD_ERROR_COM_CRC, | ||
74 | SD_ERROR_ILLEGAL_COMMAND, | ||
75 | SD_ERROR_CARD_ECC_FAILED, | ||
76 | SD_ERROR_CC, | ||
77 | SD_ERROR_GENERAL, | ||
78 | SD_ERROR_UNDERRUN, | ||
79 | SD_ERROR_OVERRUN, | ||
80 | SD_ERROR_CID_CSD_OVERWRITE, | ||
81 | SD_ERROR_STATE_MISMATCH, | ||
82 | SD_ERROR_HEADER_MISMATCH, | ||
83 | SD_ERROR_TIMEOUT, | ||
84 | SD_ERROR_CRC, | ||
85 | SD_ERROR_DRIVER_FAILURE, | ||
86 | }; | ||
87 | |||
88 | /* Standard MMC/SD clock speeds */ | ||
89 | #define MMC_CLOCK_SLOW 400000 /* 400 kHz for initial setup */ | ||
90 | #define SD_CLOCK_FAST 24000000 /* 24 MHz for SD Cards */ | ||
91 | #define SD_CLOCK_HIGH 48000000 /* 48 MHz for SD Cards */ | ||
92 | |||
93 | /* Extra commands for state control */ | ||
94 | /* Use negative numbers to disambiguate */ | ||
95 | #define SD_CIM_RESET -1 | ||
96 | |||
97 | /* Proprietary commands, illegal/reserved according to SD Specification 2.00 */ | ||
98 | /* class 1 */ | ||
99 | #define SD_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ | ||
100 | |||
101 | /* class 3 */ | ||
102 | #define SD_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ | ||
103 | |||
104 | /* class 4 */ | ||
105 | #define SD_PROGRAM_CID 26 /* adtc R1 */ | ||
106 | #define SD_PROGRAM_CSD 27 /* adtc R1 */ | ||
107 | |||
108 | /* class 9 */ | ||
109 | #define SD_GO_IRQ_STATE 40 /* bcr R5 */ | ||
110 | |||
111 | /* Don't change the order of these; they are used in dispatch tables */ | ||
112 | enum sd_rsp_t | ||
113 | { | ||
114 | RESPONSE_NONE = 0, | ||
115 | RESPONSE_R1 = 1, | ||
116 | RESPONSE_R1B = 2, | ||
117 | RESPONSE_R2_CID = 3, | ||
118 | RESPONSE_R2_CSD = 4, | ||
119 | RESPONSE_R3 = 5, | ||
120 | RESPONSE_R4 = 6, | ||
121 | RESPONSE_R5 = 7, | ||
122 | RESPONSE_R6 = 8, | ||
123 | RESPONSE_R7 = 9, | ||
124 | }; | ||
125 | |||
126 | /* | ||
127 | MMC status in R1 | ||
128 | Type | ||
129 | e : error bit | ||
130 | s : status bit | ||
131 | r : detected and set for the actual command response | ||
132 | x : detected and set during command execution. the host must poll | ||
133 | the card by sending status command in order to read these bits. | ||
134 | Clear condition | ||
135 | a : according to the card state | ||
136 | b : always related to the previous command. Reception of | ||
137 | a valid command will clear it (with a delay of one command) | ||
138 | c : clear by read | ||
139 | */ | ||
140 | |||
141 | #define R1_OUT_OF_RANGE (1 << 31) /* er, c */ | ||
142 | #define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ | ||
143 | #define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ | ||
144 | #define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ | ||
145 | #define R1_ERASE_PARAM (1 << 27) /* ex, c */ | ||
146 | #define R1_WP_VIOLATION (1 << 26) /* erx, c */ | ||
147 | #define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ | ||
148 | #define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ | ||
149 | #define R1_COM_CRC_ERROR (1 << 23) /* er, b */ | ||
150 | #define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ | ||
151 | #define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ | ||
152 | #define R1_CC_ERROR (1 << 20) /* erx, c */ | ||
153 | #define R1_ERROR (1 << 19) /* erx, c */ | ||
154 | #define R1_UNDERRUN (1 << 18) /* ex, c */ | ||
155 | #define R1_OVERRUN (1 << 17) /* ex, c */ | ||
156 | #define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ | ||
157 | #define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ | ||
158 | #define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ | ||
159 | #define R1_ERASE_RESET (1 << 13) /* sr, c */ | ||
160 | #define R1_STATUS(x) (x & 0xFFFFE000) | ||
161 | #define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ | ||
162 | #define R1_READY_FOR_DATA (1 << 8) /* sx, a */ | ||
163 | #define R1_APP_CMD (1 << 7) /* sr, c */ | ||
164 | |||
165 | /* These are unpacked versions of the actual responses */ | ||
166 | struct sd_response_r1 | ||
167 | { | ||
168 | unsigned char cmd; | ||
169 | unsigned int status; | ||
170 | }; | ||
171 | |||
172 | struct sd_response_r3 | ||
173 | { | ||
174 | unsigned int ocr; | ||
175 | }; | ||
176 | |||
177 | #define SD_CARD_BUSY 0x80000000 /* Card Power up status bit */ | ||
178 | |||
179 | struct sd_request | ||
180 | { | ||
181 | int index; /* Slot index - used for CS lines */ | ||
182 | int cmd; /* Command to send */ | ||
183 | unsigned int arg; /* Argument to send */ | ||
184 | enum sd_rsp_t rtype; /* Response type expected */ | ||
185 | |||
186 | /* Data transfer (these may be modified at the low level) */ | ||
187 | unsigned short nob; /* Number of blocks to transfer*/ | ||
188 | unsigned short block_len; /* Block length */ | ||
189 | unsigned char *buffer; /* Data buffer */ | ||
190 | unsigned int cnt; /* Data length, for PIO */ | ||
191 | |||
192 | /* Results */ | ||
193 | unsigned char response[18]; /* Buffer to store response - CRC is optional */ | ||
194 | enum sd_result_t result; | ||
195 | }; | ||
196 | |||
197 | #define SD_OCR_ARG 0x00ff8000 /* Argument of OCR */ | ||
198 | |||
199 | /*********************************************************************** | ||
200 | * SD Events | ||
201 | */ | ||
202 | #define SD_EVENT_NONE 0x00 /* No events */ | ||
203 | #define SD_EVENT_RX_DATA_DONE 0x01 /* Rx data done */ | ||
204 | #define SD_EVENT_TX_DATA_DONE 0x02 /* Tx data done */ | ||
205 | #define SD_EVENT_PROG_DONE 0x04 /* Programming is done */ | ||
206 | |||
207 | /************************************************************************** | ||
208 | * Utility functions | ||
209 | **************************************************************************/ | ||
210 | |||
211 | #define PARSE_U32(_buf,_index) \ | ||
212 | (((unsigned int)_buf[_index]) << 24) | (((unsigned int)_buf[_index+1]) << 16) | \ | ||
213 | (((unsigned int)_buf[_index+2]) << 8) | ((unsigned int)_buf[_index+3]); | ||
214 | |||
215 | #define PARSE_U16(_buf,_index) \ | ||
216 | (((unsigned short)_buf[_index]) << 8) | ((unsigned short)_buf[_index+1]); | ||
217 | |||
218 | static int sd_unpack_r1(struct sd_request *request, struct sd_response_r1 *r1) | ||
219 | { | ||
220 | unsigned char *buf = request->response; | ||
221 | |||
222 | if (request->result) | ||
223 | return request->result; | ||
224 | |||
225 | r1->cmd = buf[0]; | ||
226 | r1->status = PARSE_U32(buf,1); | ||
227 | |||
228 | DEBUG("sd_unpack_r1: cmd=%d status=%08x", r1->cmd, r1->status); | ||
229 | |||
230 | if (R1_STATUS(r1->status)) { | ||
231 | if (r1->status & R1_OUT_OF_RANGE) return SD_ERROR_OUT_OF_RANGE; | ||
232 | if (r1->status & R1_ADDRESS_ERROR) return SD_ERROR_ADDRESS; | ||
233 | if (r1->status & R1_BLOCK_LEN_ERROR) return SD_ERROR_BLOCK_LEN; | ||
234 | if (r1->status & R1_ERASE_SEQ_ERROR) return SD_ERROR_ERASE_SEQ; | ||
235 | if (r1->status & R1_ERASE_PARAM) return SD_ERROR_ERASE_PARAM; | ||
236 | if (r1->status & R1_WP_VIOLATION) return SD_ERROR_WP_VIOLATION; | ||
237 | //if (r1->status & R1_CARD_IS_LOCKED) return SD_ERROR_CARD_IS_LOCKED; | ||
238 | if (r1->status & R1_LOCK_UNLOCK_FAILED) return SD_ERROR_LOCK_UNLOCK_FAILED; | ||
239 | if (r1->status & R1_COM_CRC_ERROR) return SD_ERROR_COM_CRC; | ||
240 | if (r1->status & R1_ILLEGAL_COMMAND) return SD_ERROR_ILLEGAL_COMMAND; | ||
241 | if (r1->status & R1_CARD_ECC_FAILED) return SD_ERROR_CARD_ECC_FAILED; | ||
242 | if (r1->status & R1_CC_ERROR) return SD_ERROR_CC; | ||
243 | if (r1->status & R1_ERROR) return SD_ERROR_GENERAL; | ||
244 | if (r1->status & R1_UNDERRUN) return SD_ERROR_UNDERRUN; | ||
245 | if (r1->status & R1_OVERRUN) return SD_ERROR_OVERRUN; | ||
246 | if (r1->status & R1_CID_CSD_OVERWRITE) return SD_ERROR_CID_CSD_OVERWRITE; | ||
247 | } | ||
248 | |||
249 | if (buf[0] != request->cmd) | ||
250 | return SD_ERROR_HEADER_MISMATCH; | ||
251 | |||
252 | /* This should be last - it's the least dangerous error */ | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static int sd_unpack_r6(struct sd_request *request, struct sd_response_r1 *r1, unsigned long *rca) | ||
258 | { | ||
259 | unsigned char *buf = request->response; | ||
260 | |||
261 | if (request->result) | ||
262 | return request->result; | ||
263 | |||
264 | *rca = PARSE_U16(buf,1); /* Save RCA returned by the SD Card */ | ||
265 | |||
266 | *(buf+1) = 0; | ||
267 | *(buf+2) = 0; | ||
268 | |||
269 | return sd_unpack_r1(request, r1); | ||
270 | } | ||
271 | |||
272 | static int sd_unpack_r3(struct sd_request *request, struct sd_response_r3 *r3) | ||
273 | { | ||
274 | unsigned char *buf = request->response; | ||
275 | |||
276 | if (request->result) return request->result; | ||
277 | |||
278 | r3->ocr = PARSE_U32(buf,1); | ||
279 | DEBUG("sd_unpack_r3: ocr=%08x", r3->ocr); | ||
280 | |||
281 | if (buf[0] != 0x3f) | ||
282 | return SD_ERROR_HEADER_MISMATCH; | ||
283 | |||
284 | return 0; | ||
285 | } | ||
286 | |||
287 | /* Stop the MMC clock and wait while it happens */ | ||
288 | static inline int jz_sd_stop_clock(const int drive) | ||
289 | { | ||
290 | register int timeout = 1000; | ||
291 | |||
292 | //DEBUG("stop MMC clock"); | ||
293 | REG_MSC_STRPCL(MSC_CHN(drive)) = MSC_STRPCL_CLOCK_CONTROL_STOP; | ||
294 | |||
295 | while (timeout && (REG_MSC_STAT(MSC_CHN(drive)) & MSC_STAT_CLK_EN)) | ||
296 | { | ||
297 | timeout--; | ||
298 | if (timeout == 0) | ||
299 | { | ||
300 | DEBUG("Timeout on stop clock waiting"); | ||
301 | return SD_ERROR_TIMEOUT; | ||
302 | } | ||
303 | udelay(1); | ||
304 | } | ||
305 | //DEBUG("clock off time is %d microsec", timeout); | ||
306 | return SD_NO_ERROR; | ||
307 | } | ||
308 | |||
309 | /* Start the MMC clock and operation */ | ||
310 | static inline int jz_sd_start_clock(const int drive) | ||
311 | { | ||
312 | REG_MSC_STRPCL(MSC_CHN(drive)) = MSC_STRPCL_CLOCK_CONTROL_START | MSC_STRPCL_START_OP; | ||
313 | return SD_NO_ERROR; | ||
314 | } | ||
315 | |||
316 | static int jz_sd_check_status(const int drive, struct sd_request *request) | ||
317 | { | ||
318 | (void)request; | ||
319 | unsigned int status = REG_MSC_STAT(MSC_CHN(drive)); | ||
320 | |||
321 | /* Checking for response or data timeout */ | ||
322 | if (status & (MSC_STAT_TIME_OUT_RES | MSC_STAT_TIME_OUT_READ)) | ||
323 | { | ||
324 | DEBUG("SD timeout, MSC_STAT 0x%x CMD %d", status, | ||
325 | request->cmd); | ||
326 | return SD_ERROR_TIMEOUT; | ||
327 | } | ||
328 | |||
329 | /* Checking for CRC error */ | ||
330 | if (status & | ||
331 | (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR | | ||
332 | MSC_STAT_CRC_RES_ERR)) | ||
333 | { | ||
334 | DEBUG("SD CRC error, MSC_STAT 0x%x", status); | ||
335 | return SD_ERROR_CRC; | ||
336 | |||
337 | } | ||
338 | |||
339 | |||
340 | /* Checking for FIFO empty */ | ||
341 | /*if(status & MSC_STAT_DATA_FIFO_EMPTY && request->rtype != RESPONSE_NONE) | ||
342 | { | ||
343 | DEBUG("SD FIFO empty, MSC_STAT 0x%x", status); | ||
344 | return SD_ERROR_UNDERRUN; | ||
345 | }*/ | ||
346 | |||
347 | return SD_NO_ERROR; | ||
348 | } | ||
349 | |||
350 | /* Obtain response to the command and store it to response buffer */ | ||
351 | static void jz_sd_get_response(const int drive, struct sd_request *request) | ||
352 | { | ||
353 | int i; | ||
354 | unsigned char *buf; | ||
355 | unsigned int data; | ||
356 | |||
357 | DEBUG("fetch response for request %d, cmd %d", request->rtype, | ||
358 | request->cmd); | ||
359 | buf = request->response; | ||
360 | request->result = SD_NO_ERROR; | ||
361 | |||
362 | switch (request->rtype) | ||
363 | { | ||
364 | case RESPONSE_R1: | ||
365 | case RESPONSE_R1B: | ||
366 | case RESPONSE_R7: | ||
367 | case RESPONSE_R6: | ||
368 | case RESPONSE_R3: | ||
369 | case RESPONSE_R4: | ||
370 | case RESPONSE_R5: | ||
371 | { | ||
372 | data = REG_MSC_RES(MSC_CHN(drive)); | ||
373 | buf[0] = (data >> 8) & 0xff; | ||
374 | buf[1] = data & 0xff; | ||
375 | data = REG_MSC_RES(MSC_CHN(drive)); | ||
376 | buf[2] = (data >> 8) & 0xff; | ||
377 | buf[3] = data & 0xff; | ||
378 | data = REG_MSC_RES(MSC_CHN(drive)); | ||
379 | buf[4] = data & 0xff; | ||
380 | |||
381 | DEBUG("request %d, response [%02x %02x %02x %02x %02x]", | ||
382 | request->rtype, buf[0], buf[1], buf[2], | ||
383 | buf[3], buf[4]); | ||
384 | break; | ||
385 | } | ||
386 | case RESPONSE_R2_CID: | ||
387 | case RESPONSE_R2_CSD: | ||
388 | { | ||
389 | for (i = 0; i < 16; i += 2) | ||
390 | { | ||
391 | data = REG_MSC_RES(MSC_CHN(drive)); | ||
392 | buf[i] = (data >> 8) & 0xff; | ||
393 | buf[i + 1] = data & 0xff; | ||
394 | } | ||
395 | DEBUG("request %d, response []", request->rtype); | ||
396 | break; | ||
397 | } | ||
398 | case RESPONSE_NONE: | ||
399 | DEBUG("No response"); | ||
400 | break; | ||
401 | |||
402 | default: | ||
403 | DEBUG("unhandled response type for request %d", | ||
404 | request->rtype); | ||
405 | break; | ||
406 | } | ||
407 | } | ||
408 | |||
409 | static int jz_sd_receive_data(const int drive, struct sd_request *req) | ||
410 | { | ||
411 | unsigned int nob = req->nob; | ||
412 | unsigned int wblocklen = (unsigned int) (req->block_len + 3) >> 2; /* length in word */ | ||
413 | unsigned char *buf = req->buffer; | ||
414 | unsigned int *wbuf = (unsigned int *) buf; | ||
415 | unsigned int waligned = (((unsigned int) buf & 0x3) == 0); /* word aligned ? */ | ||
416 | unsigned int stat, timeout, data, cnt; | ||
417 | |||
418 | for (; nob >= 1; nob--) | ||
419 | { | ||
420 | timeout = 0x3FFFFFF; | ||
421 | |||
422 | while (timeout) | ||
423 | { | ||
424 | timeout--; | ||
425 | stat = REG_MSC_STAT(MSC_CHN(drive)); | ||
426 | |||
427 | if (stat & MSC_STAT_TIME_OUT_READ) | ||
428 | return SD_ERROR_TIMEOUT; | ||
429 | else if (stat & MSC_STAT_CRC_READ_ERROR) | ||
430 | return SD_ERROR_CRC; | ||
431 | else if (!(stat & MSC_STAT_DATA_FIFO_EMPTY) | ||
432 | || (stat & MSC_STAT_DATA_FIFO_AFULL)) | ||
433 | /* Ready to read data */ | ||
434 | break; | ||
435 | |||
436 | udelay(1); | ||
437 | } | ||
438 | |||
439 | if (!timeout) | ||
440 | return SD_ERROR_TIMEOUT; | ||
441 | |||
442 | /* Read data from RXFIFO. It could be FULL or PARTIAL FULL */ | ||
443 | DEBUG("Receive Data = %d", wblocklen); | ||
444 | cnt = wblocklen; | ||
445 | while (cnt) | ||
446 | { | ||
447 | data = REG_MSC_RXFIFO(MSC_CHN(drive)); | ||
448 | if (waligned) | ||
449 | *wbuf++ = data; | ||
450 | else | ||
451 | { | ||
452 | *buf++ = (unsigned char) (data >> 0); | ||
453 | *buf++ = (unsigned char) (data >> 8); | ||
454 | *buf++ = (unsigned char) (data >> 16); | ||
455 | *buf++ = (unsigned char) (data >> 24); | ||
456 | } | ||
457 | cnt--; | ||
458 | while (cnt | ||
459 | && (REG_MSC_STAT(MSC_CHN(drive)) & | ||
460 | MSC_STAT_DATA_FIFO_EMPTY)); | ||
461 | } | ||
462 | } | ||
463 | |||
464 | return SD_NO_ERROR; | ||
465 | } | ||
466 | |||
467 | static int jz_sd_transmit_data(const int drive, struct sd_request *req) | ||
468 | { | ||
469 | unsigned int nob = req->nob; | ||
470 | unsigned int wblocklen = (unsigned int) (req->block_len + 3) >> 2; /* length in word */ | ||
471 | unsigned char *buf = req->buffer; | ||
472 | unsigned int *wbuf = (unsigned int *) buf; | ||
473 | unsigned int waligned = (((unsigned int) buf & 0x3) == 0); /* word aligned ? */ | ||
474 | unsigned int stat, timeout, data, cnt; | ||
475 | |||
476 | for (; nob >= 1; nob--) | ||
477 | { | ||
478 | timeout = 0x3FFFFFF; | ||
479 | |||
480 | while (timeout) | ||
481 | { | ||
482 | timeout--; | ||
483 | stat = REG_MSC_STAT(MSC_CHN(drive)); | ||
484 | |||
485 | if (stat & | ||
486 | (MSC_STAT_CRC_WRITE_ERROR | | ||
487 | MSC_STAT_CRC_WRITE_ERROR_NOSTS)) | ||
488 | return SD_ERROR_CRC; | ||
489 | else if (!(stat & MSC_STAT_DATA_FIFO_FULL)) | ||
490 | /* Ready to write data */ | ||
491 | break; | ||
492 | |||
493 | udelay(1); | ||
494 | } | ||
495 | |||
496 | if (!timeout) | ||
497 | return SD_ERROR_TIMEOUT; | ||
498 | |||
499 | /* Write data to TXFIFO */ | ||
500 | cnt = wblocklen; | ||
501 | while (cnt) | ||
502 | { | ||
503 | while (REG_MSC_STAT(MSC_CHN(drive)) & MSC_STAT_DATA_FIFO_FULL); | ||
504 | |||
505 | if (waligned) | ||
506 | REG_MSC_TXFIFO(MSC_CHN(drive)) = *wbuf++; | ||
507 | else | ||
508 | { | ||
509 | data = *buf++; | ||
510 | data |= *buf++ << 8; | ||
511 | data |= *buf++ << 16; | ||
512 | data |= *buf++ << 24; | ||
513 | REG_MSC_TXFIFO(MSC_CHN(drive)) = data; | ||
514 | } | ||
515 | |||
516 | cnt--; | ||
517 | } | ||
518 | } | ||
519 | |||
520 | return SD_NO_ERROR; | ||
521 | } | ||
522 | |||
523 | #if SD_DMA_ENABLE | ||
524 | static void jz_sd_receive_data_dma(const int drive, struct sd_request *req) | ||
525 | { | ||
526 | unsigned int waligned = (((unsigned int)req->buffer & 0x3) == 0); /* word aligned ? */ | ||
527 | unsigned int size = req->block_len * req->nob; | ||
528 | |||
529 | if (!waligned) | ||
530 | { | ||
531 | jz_sd_receive_data(drive, req); | ||
532 | return; | ||
533 | } | ||
534 | |||
535 | /* flush dcache */ | ||
536 | dma_cache_wback_inv((unsigned long) req->buffer, size); | ||
537 | |||
538 | /* setup dma channel */ | ||
539 | REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) = 0; | ||
540 | REG_DMAC_DSAR(DMA_SD_RX_CHANNEL) = PHYSADDR(MSC_RXFIFO(MSC_CHN(drive))); /* DMA source addr */ | ||
541 | REG_DMAC_DTAR(DMA_SD_RX_CHANNEL) = PHYSADDR((unsigned long)req->buffer); /* DMA dest addr */ | ||
542 | REG_DMAC_DTCR(DMA_SD_RX_CHANNEL) = (size + 3) >> 2; /* DMA transfer count */ | ||
543 | REG_DMAC_DRSR(DMA_SD_RX_CHANNEL) = (drive == SD_SLOT_1) ? DMAC_DRSR_RS_MSC2IN : DMAC_DRSR_RS_MSC1IN; /* DMA request type */ | ||
544 | |||
545 | REG_DMAC_DCMD(DMA_SD_RX_CHANNEL) = | ||
546 | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | | ||
547 | DMAC_DCMD_DS_32BIT; | ||
548 | REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; | ||
549 | |||
550 | /* wait for dma completion */ | ||
551 | while (REG_DMAC_DTCR(DMA_SD_RX_CHANNEL)); | ||
552 | |||
553 | /* clear status and disable channel */ | ||
554 | REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) = 0; | ||
555 | } | ||
556 | |||
557 | static void jz_sd_transmit_data_dma(const int drive, struct sd_request *req) | ||
558 | { | ||
559 | unsigned int waligned = (((unsigned int)req->buffer & 0x3) == 0); /* word aligned ? */ | ||
560 | unsigned int size = req->block_len * req->nob; | ||
561 | |||
562 | if (!waligned) | ||
563 | { | ||
564 | jz_sd_transmit_data(drive, req); | ||
565 | return; | ||
566 | } | ||
567 | |||
568 | /* flush dcache */ | ||
569 | dma_cache_wback_inv((unsigned long) req->buffer, size); | ||
570 | |||
571 | /* setup dma channel */ | ||
572 | REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) = 0; | ||
573 | REG_DMAC_DSAR(DMA_SD_TX_CHANNEL) = PHYSADDR((unsigned long) req->buffer); /* DMA source addr */ | ||
574 | REG_DMAC_DTAR(DMA_SD_TX_CHANNEL) = PHYSADDR(MSC_TXFIFO(MSC_CHN(drive))); /* DMA dest addr */ | ||
575 | REG_DMAC_DTCR(DMA_SD_TX_CHANNEL) = (size + 3) >> 2; /* DMA transfer count */ | ||
576 | REG_DMAC_DRSR(DMA_SD_TX_CHANNEL) = (drive == SD_SLOT_1) ? DMAC_DRSR_RS_MSC2OUT : DMAC_DRSR_RS_MSC1OUT; /* DMA request type */ | ||
577 | |||
578 | REG_DMAC_DCMD(DMA_SD_TX_CHANNEL) = | ||
579 | DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | | ||
580 | DMAC_DCMD_DS_32BIT; | ||
581 | REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; | ||
582 | |||
583 | /* wait for dma completion */ | ||
584 | while (REG_DMAC_DTCR(DMA_SD_TX_CHANNEL)); | ||
585 | |||
586 | /* clear status and disable channel */ | ||
587 | REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) = 0; | ||
588 | } | ||
589 | |||
590 | void DMA_CALLBACK(DMA_SD_RX_CHANNEL)(void) | ||
591 | { | ||
592 | if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) & DMAC_DCCSR_AR) | ||
593 | { | ||
594 | logf("SD RX DMA address error"); | ||
595 | REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) &= ~DMAC_DCCSR_AR; | ||
596 | } | ||
597 | |||
598 | if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) & DMAC_DCCSR_HLT) | ||
599 | { | ||
600 | logf("SD RX DMA halt"); | ||
601 | REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) &= ~DMAC_DCCSR_HLT; | ||
602 | } | ||
603 | |||
604 | if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) & DMAC_DCCSR_TT) | ||
605 | { | ||
606 | REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) &= ~DMAC_DCCSR_TT; | ||
607 | //sd_rx_dma_callback(); | ||
608 | } | ||
609 | } | ||
610 | |||
611 | void DMA_CALLBACK(DMA_SD_TX_CHANNEL)(void) | ||
612 | { | ||
613 | if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) & DMAC_DCCSR_AR) | ||
614 | { | ||
615 | logf("SD TX DMA address error: %x, %x, %x", var1, var2, var3); | ||
616 | REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) &= ~DMAC_DCCSR_AR; | ||
617 | } | ||
618 | |||
619 | if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) & DMAC_DCCSR_HLT) | ||
620 | { | ||
621 | logf("SD TX DMA halt"); | ||
622 | REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) &= ~DMAC_DCCSR_HLT; | ||
623 | } | ||
624 | |||
625 | if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) & DMAC_DCCSR_TT) | ||
626 | { | ||
627 | REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) &= ~DMAC_DCCSR_TT; | ||
628 | //sd_tx_dma_callback(); | ||
629 | } | ||
630 | } | ||
631 | #endif /* SD_DMA_ENABLE */ | ||
632 | |||
633 | static inline unsigned int jz_sd_calc_clkrt(const int drive, unsigned int rate) | ||
634 | { | ||
635 | unsigned int clkrt; | ||
636 | unsigned int clk_src = sd2_0[drive] ? SD_CLOCK_HIGH : SD_CLOCK_FAST; | ||
637 | |||
638 | clkrt = 0; | ||
639 | while (rate < clk_src) | ||
640 | { | ||
641 | clkrt++; | ||
642 | clk_src >>= 1; | ||
643 | } | ||
644 | return clkrt; | ||
645 | } | ||
646 | |||
647 | static inline void cpm_select_msc_clk(unsigned int rate) | ||
648 | { | ||
649 | unsigned int div = __cpm_get_pllout2() / rate; | ||
650 | |||
651 | REG_CPM_MSCCDR = div - 1; | ||
652 | } | ||
653 | |||
654 | /* Set the MMC clock frequency */ | ||
655 | static void jz_sd_set_clock(const int drive, unsigned int rate) | ||
656 | { | ||
657 | int clkrt; | ||
658 | |||
659 | jz_sd_stop_clock(drive); | ||
660 | |||
661 | /* select clock source from CPM */ | ||
662 | cpm_select_msc_clk(rate); | ||
663 | |||
664 | __cpm_enable_pll_change(); | ||
665 | clkrt = jz_sd_calc_clkrt(drive, rate); | ||
666 | REG_MSC_CLKRT(MSC_CHN(drive)) = clkrt; | ||
667 | |||
668 | DEBUG("set clock to %u Hz clkrt=%d", rate, clkrt); | ||
669 | } | ||
670 | |||
671 | /******************************************************************************************************************** | ||
672 | ** Name: int jz_sd_exec_cmd() | ||
673 | ** Function: send command to the card, and get a response | ||
674 | ** Input: struct sd_request *req: SD request | ||
675 | ** Output: 0: right >0: error code | ||
676 | ********************************************************************************************************************/ | ||
677 | static int jz_sd_exec_cmd(const int drive, struct sd_request *request) | ||
678 | { | ||
679 | unsigned int cmdat = 0, events = 0; | ||
680 | int retval, timeout = 0x3fffff; | ||
681 | |||
682 | /* Indicate we have no result yet */ | ||
683 | request->result = SD_NO_RESPONSE; | ||
684 | |||
685 | if (request->cmd == SD_CIM_RESET) { | ||
686 | /* On reset, 1-bit bus width */ | ||
687 | use_4bit[drive] = 0; | ||
688 | |||
689 | /* Reset MMC/SD controller */ | ||
690 | __msc_reset(MSC_CHN(drive)); | ||
691 | |||
692 | /* On reset, drop SD clock down */ | ||
693 | jz_sd_set_clock(drive, MMC_CLOCK_SLOW); | ||
694 | |||
695 | /* On reset, stop SD clock */ | ||
696 | jz_sd_stop_clock(drive); | ||
697 | } | ||
698 | if (request->cmd == SD_SET_BUS_WIDTH) | ||
699 | { | ||
700 | if (request->arg == 0x2) | ||
701 | { | ||
702 | DEBUG("Use 4-bit bus width"); | ||
703 | use_4bit[drive] = 1; | ||
704 | } | ||
705 | else | ||
706 | { | ||
707 | DEBUG("Use 1-bit bus width"); | ||
708 | use_4bit[drive] = 0; | ||
709 | } | ||
710 | } | ||
711 | |||
712 | /* stop clock */ | ||
713 | jz_sd_stop_clock(drive); | ||
714 | |||
715 | /* mask all interrupts */ | ||
716 | //REG_MSC_IMASK(MSC_CHN(drive)) = 0xffff; | ||
717 | /* clear status */ | ||
718 | REG_MSC_IREG(MSC_CHN(drive)) = 0xffff; | ||
719 | /*open interrupt */ | ||
720 | REG_MSC_IMASK(MSC_CHN(drive)) = (~7); | ||
721 | /* use 4-bit bus width when possible */ | ||
722 | if (use_4bit[drive]) | ||
723 | cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT; | ||
724 | |||
725 | /* Set command type and events */ | ||
726 | switch (request->cmd) | ||
727 | { | ||
728 | /* SD core extra command */ | ||
729 | case SD_CIM_RESET: | ||
730 | cmdat |= MSC_CMDAT_INIT; /* Initialization sequence sent prior to command */ | ||
731 | break; | ||
732 | /* bc - broadcast - no response */ | ||
733 | case SD_GO_IDLE_STATE: | ||
734 | case SD_SET_DSR: | ||
735 | break; | ||
736 | |||
737 | /* bcr - broadcast with response */ | ||
738 | case SD_APP_OP_COND: | ||
739 | case SD_ALL_SEND_CID: | ||
740 | case SD_GO_IRQ_STATE: | ||
741 | break; | ||
742 | |||
743 | /* adtc - addressed with data transfer */ | ||
744 | case SD_READ_DAT_UNTIL_STOP: | ||
745 | case SD_READ_SINGLE_BLOCK: | ||
746 | case SD_READ_MULTIPLE_BLOCK: | ||
747 | case SD_SEND_SCR: | ||
748 | #if SD_DMA_ENABLE | ||
749 | cmdat |= | ||
750 | MSC_CMDAT_DATA_EN | MSC_CMDAT_READ | MSC_CMDAT_DMA_EN; | ||
751 | #else | ||
752 | cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_READ; | ||
753 | #endif | ||
754 | events = SD_EVENT_RX_DATA_DONE; | ||
755 | break; | ||
756 | |||
757 | case 6: | ||
758 | if (num_6[drive] < 2) | ||
759 | { | ||
760 | #if SD_DMA_ENABLE | ||
761 | cmdat |= | ||
762 | MSC_CMDAT_DATA_EN | MSC_CMDAT_READ | | ||
763 | MSC_CMDAT_DMA_EN; | ||
764 | #else | ||
765 | cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_READ; | ||
766 | #endif | ||
767 | events = SD_EVENT_RX_DATA_DONE; | ||
768 | } | ||
769 | break; | ||
770 | |||
771 | case SD_WRITE_DAT_UNTIL_STOP: | ||
772 | case SD_WRITE_BLOCK: | ||
773 | case SD_WRITE_MULTIPLE_BLOCK: | ||
774 | case SD_PROGRAM_CID: | ||
775 | case SD_PROGRAM_CSD: | ||
776 | case SD_LOCK_UNLOCK: | ||
777 | #if SD_DMA_ENABLE | ||
778 | cmdat |= | ||
779 | MSC_CMDAT_DATA_EN | MSC_CMDAT_WRITE | MSC_CMDAT_DMA_EN; | ||
780 | #else | ||
781 | cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_WRITE; | ||
782 | #endif | ||
783 | events = SD_EVENT_TX_DATA_DONE | SD_EVENT_PROG_DONE; | ||
784 | break; | ||
785 | |||
786 | case SD_STOP_TRANSMISSION: | ||
787 | events = SD_EVENT_PROG_DONE; | ||
788 | break; | ||
789 | |||
790 | /* ac - no data transfer */ | ||
791 | default: | ||
792 | break; | ||
793 | } | ||
794 | |||
795 | /* Set response type */ | ||
796 | switch (request->rtype) | ||
797 | { | ||
798 | case RESPONSE_NONE: | ||
799 | break; | ||
800 | case RESPONSE_R1B: | ||
801 | cmdat |= MSC_CMDAT_BUSY; | ||
802 | /* FALLTHRU */ | ||
803 | case RESPONSE_R1: | ||
804 | case RESPONSE_R7: | ||
805 | cmdat |= MSC_CMDAT_RESPONSE_R1; | ||
806 | break; | ||
807 | case RESPONSE_R2_CID: | ||
808 | case RESPONSE_R2_CSD: | ||
809 | cmdat |= MSC_CMDAT_RESPONSE_R2; | ||
810 | break; | ||
811 | case RESPONSE_R3: | ||
812 | cmdat |= MSC_CMDAT_RESPONSE_R3; | ||
813 | break; | ||
814 | case RESPONSE_R4: | ||
815 | cmdat |= MSC_CMDAT_RESPONSE_R4; | ||
816 | break; | ||
817 | case RESPONSE_R5: | ||
818 | cmdat |= MSC_CMDAT_RESPONSE_R5; | ||
819 | break; | ||
820 | case RESPONSE_R6: | ||
821 | cmdat |= MSC_CMDAT_RESPONSE_R6; | ||
822 | break; | ||
823 | default: | ||
824 | break; | ||
825 | } | ||
826 | |||
827 | /* Set command index */ | ||
828 | if (request->cmd == SD_CIM_RESET) | ||
829 | REG_MSC_CMD(MSC_CHN(drive)) = SD_GO_IDLE_STATE; | ||
830 | else | ||
831 | REG_MSC_CMD(MSC_CHN(drive)) = request->cmd; | ||
832 | |||
833 | /* Set argument */ | ||
834 | REG_MSC_ARG(MSC_CHN(drive)) = request->arg; | ||
835 | |||
836 | /* Set block length and nob */ | ||
837 | if (request->cmd == SD_SEND_SCR) | ||
838 | { /* get SCR from DataFIFO */ | ||
839 | REG_MSC_BLKLEN(MSC_CHN(drive)) = 8; | ||
840 | REG_MSC_NOB(MSC_CHN(drive)) = 1; | ||
841 | } | ||
842 | else | ||
843 | { | ||
844 | REG_MSC_BLKLEN(MSC_CHN(drive)) = request->block_len; | ||
845 | REG_MSC_NOB(MSC_CHN(drive)) = request->nob; | ||
846 | } | ||
847 | |||
848 | /* Set command */ | ||
849 | REG_MSC_CMDAT(MSC_CHN(drive)) = cmdat; | ||
850 | |||
851 | DEBUG("Send cmd %d cmdat: %x arg: %x resp %d", request->cmd, | ||
852 | cmdat, request->arg, request->rtype); | ||
853 | |||
854 | /* Start SD clock and send command to card */ | ||
855 | jz_sd_start_clock(drive); | ||
856 | |||
857 | /* Wait for command completion */ | ||
858 | //__intc_unmask_irq(IRQ_MSC); | ||
859 | //semaphore_wait(&sd_wakeup, 100); | ||
860 | while (timeout-- && !(REG_MSC_STAT(MSC_CHN(drive)) & MSC_STAT_END_CMD_RES)); | ||
861 | |||
862 | |||
863 | if (timeout == 0) | ||
864 | return SD_ERROR_TIMEOUT; | ||
865 | |||
866 | REG_MSC_IREG(MSC_CHN(drive)) = MSC_IREG_END_CMD_RES; /* clear flag */ | ||
867 | |||
868 | /* Check for status */ | ||
869 | retval = jz_sd_check_status(drive, request); | ||
870 | if (retval) | ||
871 | return retval; | ||
872 | |||
873 | /* Complete command with no response */ | ||
874 | if (request->rtype == RESPONSE_NONE) | ||
875 | return SD_NO_ERROR; | ||
876 | |||
877 | /* Get response */ | ||
878 | jz_sd_get_response(drive, request); | ||
879 | |||
880 | /* Start data operation */ | ||
881 | if (events & (SD_EVENT_RX_DATA_DONE | SD_EVENT_TX_DATA_DONE)) | ||
882 | { | ||
883 | if (events & SD_EVENT_RX_DATA_DONE) | ||
884 | { | ||
885 | if (request->cmd == SD_SEND_SCR) | ||
886 | { | ||
887 | /* SD card returns SCR register as data. | ||
888 | SD core expect it in the response buffer, | ||
889 | after normal response. */ | ||
890 | request->buffer = | ||
891 | (unsigned char *) ((unsigned int) request->response + 5); | ||
892 | } | ||
893 | #if SD_DMA_ENABLE | ||
894 | jz_sd_receive_data_dma(drive, request); | ||
895 | #else | ||
896 | jz_sd_receive_data(drive, request); | ||
897 | #endif | ||
898 | } | ||
899 | |||
900 | if (events & SD_EVENT_TX_DATA_DONE) | ||
901 | { | ||
902 | #if SD_DMA_ENABLE | ||
903 | jz_sd_transmit_data_dma(drive, request); | ||
904 | #else | ||
905 | jz_sd_transmit_data(drive, request); | ||
906 | #endif | ||
907 | } | ||
908 | //__intc_unmask_irq(IRQ_MSC); | ||
909 | //semaphore_wait(&sd_wakeup, 100); | ||
910 | /* Wait for Data Done */ | ||
911 | while (!(REG_MSC_IREG(MSC_CHN(drive)) & MSC_IREG_DATA_TRAN_DONE)); | ||
912 | REG_MSC_IREG(MSC_CHN(drive)) = MSC_IREG_DATA_TRAN_DONE; /* clear status */ | ||
913 | } | ||
914 | |||
915 | /* Wait for Prog Done event */ | ||
916 | if (events & SD_EVENT_PROG_DONE) | ||
917 | { | ||
918 | //__intc_unmask_irq(IRQ_MSC); | ||
919 | //semaphore_wait(&sd_wakeup, 100); | ||
920 | while (!(REG_MSC_IREG(MSC_CHN(drive)) & MSC_IREG_PRG_DONE)); | ||
921 | REG_MSC_IREG(MSC_CHN(drive)) = MSC_IREG_PRG_DONE; /* clear status */ | ||
922 | } | ||
923 | |||
924 | /* Command completed */ | ||
925 | |||
926 | return SD_NO_ERROR; /* return successfully */ | ||
927 | } | ||
928 | |||
929 | /******************************************************************************************************************* | ||
930 | ** Name: int sd_chkcard() | ||
931 | ** Function: check whether card is insert entirely | ||
932 | ** Input: NULL | ||
933 | ** Output: 1: insert entirely 0: not insert entirely | ||
934 | ********************************************************************************************************************/ | ||
935 | static int jz_sd_chkcard(const int drive) | ||
936 | { | ||
937 | return (__gpio_get_pin((drive == SD_SLOT_1) ? PIN_SD1_CD : PIN_SD2_CD) == 0 ? 1 : 0); | ||
938 | } | ||
939 | |||
940 | /* MSC interrupt handler */ | ||
941 | void MSC(void) | ||
942 | { | ||
943 | //semaphore_release(&sd_wakeup); | ||
944 | logf("MSC interrupt"); | ||
945 | } | ||
946 | |||
947 | #ifdef HAVE_HOTSWAP | ||
948 | static void sd_gpio_setup_irq(const int drive, bool inserted) | ||
949 | { | ||
950 | int pin = (drive == SD_SLOT_1) ? PIN_SD1_CD : PIN_SD2_CD; | ||
951 | int irq = (drive == SD_SLOT_1) ? IRQ_SD1_CD : IRQ_SD2_CD; | ||
952 | if(inserted) | ||
953 | __gpio_as_irq_rise_edge(pin); | ||
954 | else | ||
955 | __gpio_as_irq_fall_edge(pin); | ||
956 | system_enable_irq(irq); | ||
957 | } | ||
958 | #endif | ||
959 | |||
960 | /******************************************************************************************************************* | ||
961 | ** Name: void sd_hardware_init() | ||
962 | ** Function: initialize the hardware condiction that access sd card | ||
963 | ** Input: NULL | ||
964 | ** Output: NULL | ||
965 | ********************************************************************************************************************/ | ||
966 | static void jz_sd_hardware_init(const int drive) | ||
967 | { | ||
968 | if (drive == SD_SLOT_1) | ||
969 | __cpm_start_msc2(); /* enable mmc2 clock */ | ||
970 | else | ||
971 | __cpm_start_msc1(); /* enable mmc1 clock */ | ||
972 | #ifdef HAVE_HOTSWAP | ||
973 | sd_gpio_setup_irq(drive, jz_sd_chkcard(drive)); | ||
974 | #endif | ||
975 | __msc_reset(MSC_CHN(drive)); /* reset mmc/sd controller */ | ||
976 | SD_IRQ_MASK(MSC_CHN(drive)); /* mask all IRQs */ | ||
977 | jz_sd_stop_clock(drive); /* stop SD clock */ | ||
978 | } | ||
979 | |||
980 | static int sd_send_cmd(const int drive, struct sd_request *request, int cmd, unsigned int arg, | ||
981 | unsigned short nob, unsigned short block_len, | ||
982 | enum sd_rsp_t rtype, unsigned char* buffer) | ||
983 | { | ||
984 | request->cmd = cmd; | ||
985 | request->arg = arg; | ||
986 | request->rtype = rtype; | ||
987 | request->nob = nob; | ||
988 | request->block_len = block_len; | ||
989 | request->buffer = buffer; | ||
990 | request->cnt = nob * block_len; | ||
991 | |||
992 | return jz_sd_exec_cmd(drive, request); | ||
993 | } | ||
994 | |||
995 | static void sd_simple_cmd(const int drive, struct sd_request *request, int cmd, unsigned int arg, | ||
996 | enum sd_rsp_t rtype) | ||
997 | { | ||
998 | sd_send_cmd(drive, request, cmd, arg, 0, 0, rtype, NULL); | ||
999 | } | ||
1000 | |||
1001 | #define SD_INIT_DOING 0 | ||
1002 | #define SD_INIT_PASSED 1 | ||
1003 | #define SD_INIT_FAILED 2 | ||
1004 | static int sd_init_card_state(const int drive, struct sd_request *request) | ||
1005 | { | ||
1006 | struct sd_response_r1 r1; | ||
1007 | struct sd_response_r3 r3; | ||
1008 | int retval, i, ocr = 0x40300000, limit_41 = 0; | ||
1009 | |||
1010 | switch (request->cmd) | ||
1011 | { | ||
1012 | case SD_GO_IDLE_STATE: /* No response to parse */ | ||
1013 | sd_simple_cmd(drive, request, SD_SEND_IF_COND, 0x1AA, RESPONSE_R1); | ||
1014 | break; | ||
1015 | |||
1016 | case SD_SEND_IF_COND: | ||
1017 | retval = sd_unpack_r1(request, &r1); | ||
1018 | sd_simple_cmd(drive, request, SD_APP_CMD, 0, RESPONSE_R1); | ||
1019 | break; | ||
1020 | |||
1021 | case SD_APP_CMD: | ||
1022 | retval = sd_unpack_r1(request, &r1); | ||
1023 | if (retval & (limit_41 < 100)) | ||
1024 | { | ||
1025 | DEBUG("sd_init_card_state: unable to SD_APP_CMD error=%d", | ||
1026 | retval); | ||
1027 | limit_41++; | ||
1028 | sd_simple_cmd(drive, request, SD_APP_OP_COND, ocr, RESPONSE_R3); | ||
1029 | } | ||
1030 | else if (limit_41 < 100) | ||
1031 | { | ||
1032 | limit_41++; | ||
1033 | sd_simple_cmd(drive, request, SD_APP_OP_COND, ocr, RESPONSE_R3); | ||
1034 | } | ||
1035 | else | ||
1036 | /* reset the card to idle*/ | ||
1037 | sd_simple_cmd(drive, request, SD_GO_IDLE_STATE, 0, RESPONSE_NONE); | ||
1038 | break; | ||
1039 | |||
1040 | case SD_APP_OP_COND: | ||
1041 | retval = sd_unpack_r3(request, &r3); | ||
1042 | if (retval) | ||
1043 | break; | ||
1044 | |||
1045 | DEBUG("sd_init_card_state: read ocr value = 0x%08x", r3.ocr); | ||
1046 | card[drive].ocr = r3.ocr; | ||
1047 | |||
1048 | if(!(r3.ocr & SD_CARD_BUSY || ocr == 0)) | ||
1049 | { | ||
1050 | sleep(HZ / 100); | ||
1051 | sd_simple_cmd(drive, request, SD_APP_CMD, 0, RESPONSE_R1); | ||
1052 | } | ||
1053 | else | ||
1054 | { | ||
1055 | /* Set the data bus width to 4 bits */ | ||
1056 | use_4bit[drive] = 1; | ||
1057 | sd_simple_cmd(drive, request, SD_ALL_SEND_CID, 0, RESPONSE_R2_CID); | ||
1058 | } | ||
1059 | break; | ||
1060 | |||
1061 | case SD_ALL_SEND_CID: | ||
1062 | for(i=0; i<4; i++) | ||
1063 | card[drive].cid[i] = ((request->response[1+i*4]<<24) | (request->response[2+i*4]<<16) | | ||
1064 | (request->response[3+i*4]<< 8) | request->response[4+i*4]); | ||
1065 | |||
1066 | logf("CID: %08lx%08lx%08lx%08lx", card[drive].cid[0], card[drive].cid[1], card[drive].cid[2], card[drive].cid[3]); | ||
1067 | sd_simple_cmd(drive, request, SD_SEND_RELATIVE_ADDR, 0, RESPONSE_R6); | ||
1068 | break; | ||
1069 | case SD_SEND_RELATIVE_ADDR: | ||
1070 | retval = sd_unpack_r6(request, &r1, &card[drive].rca); | ||
1071 | card[drive].rca = card[drive].rca << 16; | ||
1072 | DEBUG("sd_init_card_state: Get RCA from SD: 0x%04lx Status: %x", card[drive].rca, r1.status); | ||
1073 | if (retval) | ||
1074 | { | ||
1075 | DEBUG("sd_init_card_state: unable to SET_RELATIVE_ADDR error=%d", | ||
1076 | retval); | ||
1077 | return SD_INIT_FAILED; | ||
1078 | } | ||
1079 | |||
1080 | sd_simple_cmd(drive, request, SD_SEND_CSD, card[drive].rca, RESPONSE_R2_CSD); | ||
1081 | break; | ||
1082 | |||
1083 | case SD_SEND_CSD: | ||
1084 | for(i=0; i<4; i++) | ||
1085 | card[drive].csd[i] = ((request->response[1+i*4]<<24) | (request->response[2+i*4]<<16) | | ||
1086 | (request->response[3+i*4]<< 8) | request->response[4+i*4]); | ||
1087 | |||
1088 | sd_parse_csd(&card[drive]); | ||
1089 | sd2_0[drive] = (card_extract_bits(card[drive].csd, 127, 2) == 1); | ||
1090 | |||
1091 | logf("CSD: %08lx%08lx%08lx%08lx", card[drive].csd[0], card[drive].csd[1], card[drive].csd[2], card[drive].csd[3]); | ||
1092 | DEBUG("SD card is ready"); | ||
1093 | jz_sd_set_clock(drive, SD_CLOCK_FAST); | ||
1094 | return SD_INIT_PASSED; | ||
1095 | |||
1096 | default: | ||
1097 | DEBUG("sd_init_card_state: error! Illegal last cmd %d", request->cmd); | ||
1098 | return SD_INIT_FAILED; | ||
1099 | } | ||
1100 | |||
1101 | return SD_INIT_DOING; | ||
1102 | } | ||
1103 | |||
1104 | static int sd_switch(const int drive, struct sd_request *request, int mode, int group, | ||
1105 | unsigned char value, unsigned char * resp) | ||
1106 | { | ||
1107 | unsigned int arg; | ||
1108 | |||
1109 | mode = !!mode; | ||
1110 | value &= 0xF; | ||
1111 | arg = (mode << 31 | 0x00FFFFFF); | ||
1112 | arg &= ~(0xF << (group * 4)); | ||
1113 | arg |= value << (group * 4); | ||
1114 | sd_send_cmd(drive, request, 6, arg, 1, 64, RESPONSE_R1, resp); | ||
1115 | |||
1116 | return 0; | ||
1117 | } | ||
1118 | |||
1119 | /* | ||
1120 | * Fetches and decodes switch information | ||
1121 | */ | ||
1122 | static int sd_read_switch(const int drive, struct sd_request *request) | ||
1123 | { | ||
1124 | unsigned int status[64 / 4]; | ||
1125 | |||
1126 | memset((unsigned char *)status, 0, 64); | ||
1127 | sd_switch(drive, request, 0, 0, 1, (unsigned char*) status); | ||
1128 | |||
1129 | if (((unsigned char *)status)[13] & 0x02) | ||
1130 | return 0; | ||
1131 | else | ||
1132 | return 1; | ||
1133 | } | ||
1134 | |||
1135 | /* | ||
1136 | * Test if the card supports high-speed mode and, if so, switch to it. | ||
1137 | */ | ||
1138 | static int sd_switch_hs(const int drive, struct sd_request *request) | ||
1139 | { | ||
1140 | unsigned int status[64 / 4]; | ||
1141 | |||
1142 | sd_switch(drive, request, 1, 0, 1, (unsigned char*) status); | ||
1143 | return 0; | ||
1144 | } | ||
1145 | |||
1146 | static int sd_select_card(const int drive) | ||
1147 | { | ||
1148 | struct sd_request request; | ||
1149 | struct sd_response_r1 r1; | ||
1150 | int retval; | ||
1151 | |||
1152 | sd_simple_cmd(drive, &request, SD_SELECT_CARD, card[drive].rca, | ||
1153 | RESPONSE_R1B); | ||
1154 | retval = sd_unpack_r1(&request, &r1); | ||
1155 | if (retval) | ||
1156 | return retval; | ||
1157 | |||
1158 | if (sd2_0[drive]) | ||
1159 | { | ||
1160 | retval = sd_read_switch(drive, &request); | ||
1161 | if (!retval) | ||
1162 | { | ||
1163 | sd_switch_hs(drive, &request); | ||
1164 | jz_sd_set_clock(drive, SD_CLOCK_HIGH); | ||
1165 | } | ||
1166 | } | ||
1167 | num_6[drive] = 3; | ||
1168 | sd_simple_cmd(drive, &request, SD_APP_CMD, card[drive].rca, | ||
1169 | RESPONSE_R1); | ||
1170 | retval = sd_unpack_r1(&request, &r1); | ||
1171 | if (retval) | ||
1172 | return retval; | ||
1173 | sd_simple_cmd(drive, &request, SD_SET_BUS_WIDTH, 2, RESPONSE_R1); | ||
1174 | retval = sd_unpack_r1(&request, &r1); | ||
1175 | if (retval) | ||
1176 | return retval; | ||
1177 | |||
1178 | card[drive].initialized = 1; | ||
1179 | |||
1180 | return 0; | ||
1181 | } | ||
1182 | |||
1183 | static int sd_init_device(const int drive) | ||
1184 | { | ||
1185 | int retval = 0; | ||
1186 | struct sd_request init_req; | ||
1187 | register int timeout = 1000; | ||
1188 | |||
1189 | mutex_lock(&sd_mtx); | ||
1190 | |||
1191 | /* Initialise card data as blank */ | ||
1192 | memset(&card[drive], 0, sizeof(tCardInfo)); | ||
1193 | |||
1194 | sd2_0[drive] = 0; | ||
1195 | num_6[drive] = 0; | ||
1196 | use_4bit[drive] = 0; | ||
1197 | |||
1198 | /* reset mmc/sd controller */ | ||
1199 | jz_sd_hardware_init(drive); | ||
1200 | |||
1201 | sd_simple_cmd(drive, &init_req, SD_CIM_RESET, 0, RESPONSE_NONE); | ||
1202 | sd_simple_cmd(drive, &init_req, SD_GO_IDLE_STATE, 0, RESPONSE_NONE); | ||
1203 | |||
1204 | sleep(HZ/2); /* Give the card/controller some rest */ | ||
1205 | |||
1206 | while(timeout-- && ((retval = sd_init_card_state(drive, &init_req)) == SD_INIT_DOING)); | ||
1207 | retval = (retval == SD_INIT_PASSED ? sd_select_card(drive) : -1); | ||
1208 | |||
1209 | if (drive == SD_SLOT_1) | ||
1210 | __cpm_stop_msc2(); /* disable SD1 clock */ | ||
1211 | else | ||
1212 | __cpm_stop_msc1(); /* disable SD2 clock */ | ||
1213 | |||
1214 | mutex_unlock(&sd_mtx); | ||
1215 | |||
1216 | return retval; | ||
1217 | } | ||
1218 | |||
1219 | int sd_init(void) | ||
1220 | { | ||
1221 | static bool inited = false; | ||
1222 | |||
1223 | sd_init_gpio(); /* init GPIO */ | ||
1224 | |||
1225 | #if SD_DMA_ENABLE | ||
1226 | __dmac_channel_enable_clk(DMA_SD_RX_CHANNEL); | ||
1227 | __dmac_channel_enable_clk(DMA_SD_TX_CHANNEL); | ||
1228 | #endif | ||
1229 | |||
1230 | if(!inited) | ||
1231 | { | ||
1232 | semaphore_init(&sd_wakeup, 1, 0); | ||
1233 | mutex_init(&sd_mtx); | ||
1234 | inited = true; | ||
1235 | } | ||
1236 | |||
1237 | for (int drive = 0; drive < NUM_DRIVES; drive++) | ||
1238 | sd_init_device(drive); | ||
1239 | |||
1240 | return 0; | ||
1241 | } | ||
1242 | |||
1243 | static inline bool card_detect_target(const int drive) | ||
1244 | { | ||
1245 | return (jz_sd_chkcard(drive) == 1); | ||
1246 | } | ||
1247 | |||
1248 | tCardInfo* card_get_info_target(const int drive) | ||
1249 | { | ||
1250 | return &card[drive]; | ||
1251 | } | ||
1252 | |||
1253 | static inline void sd_start_transfer(const int drive) | ||
1254 | { | ||
1255 | mutex_lock(&sd_mtx); | ||
1256 | if (drive == SD_SLOT_1) | ||
1257 | __cpm_start_msc2(); | ||
1258 | else | ||
1259 | __cpm_start_msc1(); | ||
1260 | led(true); | ||
1261 | } | ||
1262 | |||
1263 | static inline void sd_stop_transfer(const int drive) | ||
1264 | { | ||
1265 | led(false); | ||
1266 | if (drive == SD_SLOT_1) | ||
1267 | __cpm_stop_msc2(); | ||
1268 | else | ||
1269 | __cpm_stop_msc1(); | ||
1270 | mutex_unlock(&sd_mtx); | ||
1271 | } | ||
1272 | |||
1273 | int sd_read_sectors(const int drive, unsigned long start, int count, void* buf) | ||
1274 | { | ||
1275 | sd_start_transfer(drive); | ||
1276 | |||
1277 | struct sd_request request; | ||
1278 | struct sd_response_r1 r1; | ||
1279 | int retval = -1; | ||
1280 | |||
1281 | if (!card_detect_target(drive) || count == 0 || start > card[drive].numblocks) | ||
1282 | goto err; | ||
1283 | |||
1284 | if(card[drive].initialized == 0 && !sd_init_device(drive)) | ||
1285 | goto err; | ||
1286 | |||
1287 | sd_simple_cmd(drive, &request, SD_SEND_STATUS, card[drive].rca, RESPONSE_R1); | ||
1288 | retval = sd_unpack_r1(&request, &r1); | ||
1289 | if (retval && (retval != SD_ERROR_STATE_MISMATCH)) | ||
1290 | goto err; | ||
1291 | |||
1292 | sd_simple_cmd(drive, &request, SD_SET_BLOCKLEN, SD_BLOCK_SIZE, RESPONSE_R1); | ||
1293 | if ((retval = sd_unpack_r1(&request, &r1))) | ||
1294 | goto err; | ||
1295 | |||
1296 | if (sd2_0[drive]) | ||
1297 | { | ||
1298 | sd_send_cmd(drive, &request, SD_READ_MULTIPLE_BLOCK, start, | ||
1299 | count, SD_BLOCK_SIZE, RESPONSE_R1, buf); | ||
1300 | if ((retval = sd_unpack_r1(&request, &r1))) | ||
1301 | goto err; | ||
1302 | } | ||
1303 | else | ||
1304 | { | ||
1305 | sd_send_cmd(drive, &request, SD_READ_MULTIPLE_BLOCK, | ||
1306 | start * SD_BLOCK_SIZE, count, | ||
1307 | SD_BLOCK_SIZE, RESPONSE_R1, buf); | ||
1308 | if ((retval = sd_unpack_r1(&request, &r1))) | ||
1309 | goto err; | ||
1310 | } | ||
1311 | |||
1312 | last_disk_activity = current_tick; | ||
1313 | |||
1314 | sd_simple_cmd(drive, &request, SD_STOP_TRANSMISSION, 0, RESPONSE_R1B); | ||
1315 | if ((retval = sd_unpack_r1(&request, &r1))) | ||
1316 | goto err; | ||
1317 | |||
1318 | err: | ||
1319 | sd_stop_transfer(drive); | ||
1320 | |||
1321 | return retval; | ||
1322 | } | ||
1323 | |||
1324 | int sd_write_sectors(const int drive, unsigned long start, int count, const void* buf) | ||
1325 | { | ||
1326 | sd_start_transfer(drive); | ||
1327 | |||
1328 | struct sd_request request; | ||
1329 | struct sd_response_r1 r1; | ||
1330 | int retval = -1; | ||
1331 | |||
1332 | if (!card_detect_target(drive) || count == 0 || start > card[drive].numblocks) | ||
1333 | goto err; | ||
1334 | |||
1335 | if(card[drive].initialized == 0 && !sd_init_device(drive)) | ||
1336 | goto err; | ||
1337 | |||
1338 | sd_simple_cmd(drive, &request, SD_SEND_STATUS, card[drive].rca, RESPONSE_R1); | ||
1339 | retval = sd_unpack_r1(&request, &r1); | ||
1340 | if (retval && (retval != SD_ERROR_STATE_MISMATCH)) | ||
1341 | goto err; | ||
1342 | |||
1343 | sd_simple_cmd(drive, &request, SD_SET_BLOCKLEN, SD_BLOCK_SIZE, RESPONSE_R1); | ||
1344 | if ((retval = sd_unpack_r1(&request, &r1))) | ||
1345 | goto err; | ||
1346 | |||
1347 | if (sd2_0[drive]) | ||
1348 | { | ||
1349 | sd_send_cmd(drive, &request, SD_WRITE_MULTIPLE_BLOCK, start, | ||
1350 | count, SD_BLOCK_SIZE, RESPONSE_R1, | ||
1351 | (void*)buf); | ||
1352 | if ((retval = sd_unpack_r1(&request, &r1))) | ||
1353 | goto err; | ||
1354 | } | ||
1355 | else | ||
1356 | { | ||
1357 | sd_send_cmd(drive, &request, SD_WRITE_MULTIPLE_BLOCK, | ||
1358 | start * SD_BLOCK_SIZE, count, | ||
1359 | SD_BLOCK_SIZE, RESPONSE_R1, (void*)buf); | ||
1360 | if ((retval = sd_unpack_r1(&request, &r1))) | ||
1361 | goto err; | ||
1362 | } | ||
1363 | |||
1364 | last_disk_activity = current_tick; | ||
1365 | |||
1366 | sd_simple_cmd(drive, &request, SD_STOP_TRANSMISSION, 0, RESPONSE_R1B); | ||
1367 | if ((retval = sd_unpack_r1(&request, &r1))) | ||
1368 | goto err; | ||
1369 | |||
1370 | err: | ||
1371 | sd_stop_transfer(drive); | ||
1372 | |||
1373 | return retval; | ||
1374 | } | ||
1375 | |||
1376 | long sd_last_disk_activity(void) | ||
1377 | { | ||
1378 | return last_disk_activity; | ||
1379 | } | ||
1380 | |||
1381 | int sd_spinup_time(void) | ||
1382 | { | ||
1383 | return 0; | ||
1384 | } | ||
1385 | |||
1386 | void sd_enable(bool on) | ||
1387 | { | ||
1388 | (void)on; | ||
1389 | } | ||
1390 | |||
1391 | bool sd_disk_is_active(void) | ||
1392 | { | ||
1393 | return false; | ||
1394 | } | ||
1395 | |||
1396 | int sd_soft_reset(void) | ||
1397 | { | ||
1398 | return 0; | ||
1399 | } | ||
1400 | |||
1401 | #ifdef HAVE_HOTSWAP | ||
1402 | bool sd_removable(const int drive) | ||
1403 | { | ||
1404 | (void)drive; | ||
1405 | return true; | ||
1406 | } | ||
1407 | |||
1408 | static int sd1_oneshot_callback(struct timeout *tmo) | ||
1409 | { | ||
1410 | int state = card_detect_target(SD_SLOT_1); | ||
1411 | |||
1412 | /* This is called only if the state was stable for 300ms - check state | ||
1413 | * and post appropriate event. */ | ||
1414 | queue_broadcast(state ? SYS_HOTSWAP_INSERTED : SYS_HOTSWAP_EXTRACTED, | ||
1415 | 0); | ||
1416 | |||
1417 | sd_gpio_setup_irq(SD_SLOT_1, state); | ||
1418 | |||
1419 | return 0; | ||
1420 | (void)tmo; | ||
1421 | } | ||
1422 | |||
1423 | static int sd2_oneshot_callback(struct timeout *tmo) | ||
1424 | { | ||
1425 | int state = card_detect_target(SD_SLOT_2); | ||
1426 | |||
1427 | /* This is called only if the state was stable for 300ms - check state | ||
1428 | * and post appropriate event. */ | ||
1429 | queue_broadcast(state ? SYS_HOTSWAP_INSERTED : SYS_HOTSWAP_EXTRACTED, | ||
1430 | 1); | ||
1431 | |||
1432 | sd_gpio_setup_irq(SD_SLOT_2, state); | ||
1433 | |||
1434 | return 0; | ||
1435 | (void)tmo; | ||
1436 | } | ||
1437 | |||
1438 | /* called on insertion/removal interrupt */ | ||
1439 | void GPIO_SD1_CD(void) | ||
1440 | { | ||
1441 | static struct timeout sd1_oneshot; | ||
1442 | timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0); | ||
1443 | } | ||
1444 | |||
1445 | void GPIO_SD2_CD(void) | ||
1446 | { | ||
1447 | static struct timeout sd2_oneshot; | ||
1448 | timeout_register(&sd2_oneshot, sd2_oneshot_callback, (3*HZ/10), 0); | ||
1449 | } | ||
1450 | #endif | ||
1451 | |||
1452 | bool sd_present(const int drive) | ||
1453 | { | ||
1454 | return card_detect_target(drive); | ||
1455 | } | ||
1456 | |||
1457 | #ifdef CONFIG_STORAGE_MULTI | ||
1458 | int sd_num_drives(int first_drive) | ||
1459 | { | ||
1460 | return NUM_DRIVES; | ||
1461 | } | ||
1462 | #endif /* CONFIG_STORAGE_MULTI */ | ||
1463 | |||
1464 | int sd_event(long id, intptr_t data) | ||
1465 | { | ||
1466 | int rc = 0; | ||
1467 | |||
1468 | switch (id) | ||
1469 | { | ||
1470 | #ifdef HAVE_HOTSWAP | ||
1471 | case SYS_HOTSWAP_INSERTED: | ||
1472 | case SYS_HOTSWAP_EXTRACTED: | ||
1473 | /* Force card init for new card, re-init for re-inserted one or | ||
1474 | * clear if the last attempt to init failed with an error. */ | ||
1475 | mutex_lock(&sd_mtx); /* lock-out card activity */ | ||
1476 | card[data].initialized = 0; | ||
1477 | mutex_unlock(&sd_mtx); | ||
1478 | break; | ||
1479 | #endif /* HAVE_HOTSWAP */ | ||
1480 | default: | ||
1481 | rc = storage_event_default_handler(id, data, last_disk_activity, | ||
1482 | STORAGE_SD); | ||
1483 | break; | ||
1484 | } | ||
1485 | |||
1486 | return rc; | ||
1487 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/backlight-target.h b/firmware/target/mips/ingenic_jz47xx/backlight-target.h index 0dc7ce387a..1b61d13e4c 100644 --- a/firmware/target/mips/ingenic_jz47xx/backlight-target.h +++ b/firmware/target/mips/ingenic_jz47xx/backlight-target.h | |||
@@ -26,6 +26,8 @@ | |||
26 | bool backlight_hw_init(void); | 26 | bool backlight_hw_init(void); |
27 | void backlight_hw_on(void); | 27 | void backlight_hw_on(void); |
28 | void backlight_hw_off(void); | 28 | void backlight_hw_off(void); |
29 | #ifdef HAVE_BACKLIGHT_BRIGHTNESS | ||
29 | void backlight_hw_brightness(int brightness); | 30 | void backlight_hw_brightness(int brightness); |
31 | #endif /* HAVE_BACKLIGHT_BRIGHTNESS */ | ||
30 | 32 | ||
31 | #endif /* BACKLIGHT_TARGET_H */ | 33 | #endif /* BACKLIGHT_TARGET_H */ |
diff --git a/firmware/target/mips/ingenic_jz47xx/codec-jz4760.c b/firmware/target/mips/ingenic_jz47xx/codec-jz4760.c new file mode 100644 index 0000000000..f25dc70eb4 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/codec-jz4760.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * | ||
9 | * Copyright (C) 2016 by Roman Stolyarov | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version 2 | ||
14 | * of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
17 | * KIND, either express or implied. | ||
18 | * | ||
19 | ****************************************************************************/ | ||
20 | |||
21 | #include "config.h" | ||
22 | #include "audio.h" | ||
23 | #include "sound.h" | ||
24 | #include "cpu.h" | ||
25 | #include "system.h" | ||
26 | #include "pcm_sw_volume.h" | ||
27 | #include "cs4398.h" | ||
28 | #include "kernel.h" | ||
29 | |||
30 | #define PIN_CS_RST (32*1+10) | ||
31 | #define PIN_CODEC_PWRON (32*1+13) | ||
32 | #define PIN_AP_MUTE (32*1+14) | ||
33 | #define PIN_JD_CON (32*1+16) | ||
34 | |||
35 | static void pop_ctrl(const int val) | ||
36 | { | ||
37 | if(val) | ||
38 | __gpio_clear_pin(PIN_JD_CON); | ||
39 | else | ||
40 | __gpio_set_pin(PIN_JD_CON); | ||
41 | } | ||
42 | |||
43 | static void amp_enable(const int val) | ||
44 | { | ||
45 | if(val) | ||
46 | __gpio_set_pin(PIN_CODEC_PWRON); | ||
47 | else | ||
48 | __gpio_clear_pin(PIN_CODEC_PWRON); | ||
49 | } | ||
50 | |||
51 | static void dac_enable(const int val) | ||
52 | { | ||
53 | if(val) | ||
54 | __gpio_set_pin(PIN_CS_RST); | ||
55 | else | ||
56 | __gpio_clear_pin(PIN_CS_RST); | ||
57 | } | ||
58 | |||
59 | static void ap_mute(bool mute) | ||
60 | { | ||
61 | if(mute) | ||
62 | __gpio_clear_pin(PIN_AP_MUTE); | ||
63 | else | ||
64 | __gpio_set_pin(PIN_AP_MUTE); | ||
65 | } | ||
66 | |||
67 | static void audiohw_mute(bool mute) | ||
68 | { | ||
69 | if(mute) | ||
70 | cs4398_write_reg(CS4398_REG_MUTE, cs4398_read_reg(CS4398_REG_MUTE) | CS4398_MUTE_A | CS4398_MUTE_B); | ||
71 | else | ||
72 | cs4398_write_reg(CS4398_REG_MUTE, cs4398_read_reg(CS4398_REG_MUTE) & ~(CS4398_MUTE_A | CS4398_MUTE_B)); | ||
73 | } | ||
74 | |||
75 | void audiohw_preinit(void) | ||
76 | { | ||
77 | cs4398_write_reg(CS4398_REG_MISC, CS4398_CPEN | CS4398_PDN); | ||
78 | cs4398_write_reg(CS4398_REG_MODECTL, CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST); | ||
79 | cs4398_write_reg(CS4398_REG_VOLMIX, CS4398_ATAPI_A_L | CS4398_ATAPI_B_R); | ||
80 | cs4398_write_reg(CS4398_REG_MUTE, CS4398_MUTEP_LOW); | ||
81 | cs4398_write_reg(CS4398_REG_VOL_A, 0xff); | ||
82 | cs4398_write_reg(CS4398_REG_VOL_B, 0xff); | ||
83 | cs4398_write_reg(CS4398_REG_RAMPFILT, CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); | ||
84 | cs4398_write_reg(CS4398_REG_MISC, CS4398_CPEN); | ||
85 | } | ||
86 | |||
87 | void audiohw_init(void) | ||
88 | { | ||
89 | __gpio_as_func1(3*32+12); // BCK | ||
90 | __gpio_as_func0(3*32+13); // LRCK | ||
91 | __gpio_as_func2(4*32+5); // MCLK | ||
92 | __gpio_as_func0(4*32+7); // DO | ||
93 | |||
94 | pop_ctrl(0); | ||
95 | ap_mute(true); | ||
96 | amp_enable(0); | ||
97 | dac_enable(0); | ||
98 | |||
99 | __gpio_as_output(PIN_JD_CON); | ||
100 | __gpio_as_output(PIN_AP_MUTE); | ||
101 | __gpio_as_output(PIN_CODEC_PWRON); | ||
102 | __gpio_as_output(PIN_CS_RST); | ||
103 | |||
104 | mdelay(100); | ||
105 | amp_enable(1); | ||
106 | |||
107 | /* set AIC clk PLL1 */ | ||
108 | __cpm_select_i2sclk_pll(); | ||
109 | __cpm_select_i2sclk_pll1(); | ||
110 | |||
111 | __cpm_enable_pll_change(); | ||
112 | __cpm_set_i2sdiv(43-1); | ||
113 | |||
114 | __cpm_start_aic(); | ||
115 | |||
116 | /* Init AIC */ | ||
117 | __i2s_enable_sclk(); | ||
118 | __i2s_external_codec(); | ||
119 | __i2s_select_msbjustified(); | ||
120 | __i2s_as_master(); | ||
121 | __i2s_enable_transmit_dma(); | ||
122 | __i2s_set_transmit_trigger(24); | ||
123 | __i2s_set_oss_sample_size(16); | ||
124 | __i2s_enable(); | ||
125 | |||
126 | /* Init DAC */ | ||
127 | dac_enable(1); | ||
128 | udelay(1); | ||
129 | audiohw_preinit(); | ||
130 | } | ||
131 | |||
132 | static int vol_tenthdb2hw(const int tdb) | ||
133 | { | ||
134 | if (tdb < CS4398_VOLUME_MIN) { | ||
135 | return 0xff; | ||
136 | } else if (tdb > CS4398_VOLUME_MAX) { | ||
137 | return 0x00; | ||
138 | } else { | ||
139 | return (-tdb/5); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | void audiohw_set_volume(int vol_l, int vol_r) | ||
144 | { | ||
145 | cs4398_write_reg(CS4398_REG_VOL_A, vol_tenthdb2hw(vol_l)); | ||
146 | cs4398_write_reg(CS4398_REG_VOL_B, vol_tenthdb2hw(vol_r)); | ||
147 | } | ||
148 | |||
149 | void audiohw_set_lineout_volume(int vol_l, int vol_r) | ||
150 | { | ||
151 | #if 0 /* unused */ | ||
152 | cs4398_write_reg(CS4398_REG_VOL_A, vol_tenthdb2hw(vol_l)); | ||
153 | cs4398_write_reg(CS4398_REG_VOL_B, vol_tenthdb2hw(vol_r)); | ||
154 | #else | ||
155 | (void)vol_l; | ||
156 | (void)vol_r; | ||
157 | #endif | ||
158 | } | ||
159 | |||
160 | void audiohw_set_filter_roll_off(int value) | ||
161 | { | ||
162 | /* 0 = fast (sharp); | ||
163 | 1 = slow */ | ||
164 | if (value == 0) { | ||
165 | cs4398_write_reg(CS4398_REG_RAMPFILT, cs4398_read_reg(CS4398_REG_RAMPFILT) & ~CS4398_FILT_SEL); | ||
166 | } else { | ||
167 | cs4398_write_reg(CS4398_REG_RAMPFILT, cs4398_read_reg(CS4398_REG_RAMPFILT) | CS4398_FILT_SEL); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | void pll1_init(unsigned int freq); | ||
172 | void audiohw_set_frequency(int fsel) | ||
173 | { | ||
174 | unsigned int pll1_speed; | ||
175 | unsigned char mclk_div, bclk_div, func_mode; | ||
176 | |||
177 | switch(fsel) | ||
178 | { | ||
179 | case HW_FREQ_8: | ||
180 | pll1_speed = 426000000; | ||
181 | mclk_div = 52; | ||
182 | bclk_div = 16; | ||
183 | func_mode = 0; | ||
184 | break; | ||
185 | case HW_FREQ_11: | ||
186 | pll1_speed = 508000000; | ||
187 | mclk_div = 45; | ||
188 | bclk_div = 16; | ||
189 | func_mode = 0; | ||
190 | break; | ||
191 | case HW_FREQ_12: | ||
192 | pll1_speed = 516000000; | ||
193 | mclk_div = 42; | ||
194 | bclk_div = 16; | ||
195 | func_mode = 0; | ||
196 | break; | ||
197 | case HW_FREQ_16: | ||
198 | pll1_speed = 426000000; | ||
199 | mclk_div = 52; | ||
200 | bclk_div = 8; | ||
201 | func_mode = 0; | ||
202 | break; | ||
203 | case HW_FREQ_22: | ||
204 | pll1_speed = 508000000; | ||
205 | mclk_div = 45; | ||
206 | bclk_div = 8; | ||
207 | func_mode = 0; | ||
208 | break; | ||
209 | case HW_FREQ_24: | ||
210 | pll1_speed = 516000000; | ||
211 | mclk_div = 42; | ||
212 | bclk_div = 8; | ||
213 | func_mode = 0; | ||
214 | break; | ||
215 | case HW_FREQ_32: | ||
216 | pll1_speed = 426000000; | ||
217 | mclk_div = 52; | ||
218 | bclk_div = 4; | ||
219 | func_mode = 0; | ||
220 | break; | ||
221 | case HW_FREQ_44: | ||
222 | pll1_speed = 508000000; | ||
223 | mclk_div = 45; | ||
224 | bclk_div = 4; | ||
225 | func_mode = 0; | ||
226 | break; | ||
227 | case HW_FREQ_48: | ||
228 | pll1_speed = 516000000; | ||
229 | mclk_div = 42; | ||
230 | bclk_div = 4; | ||
231 | func_mode = 0; | ||
232 | break; | ||
233 | case HW_FREQ_64: | ||
234 | pll1_speed = 426000000; | ||
235 | mclk_div = 52; | ||
236 | bclk_div = 2; | ||
237 | func_mode = 1; | ||
238 | break; | ||
239 | case HW_FREQ_88: | ||
240 | pll1_speed = 508000000; | ||
241 | mclk_div = 45; | ||
242 | bclk_div = 2; | ||
243 | func_mode = 1; | ||
244 | break; | ||
245 | case HW_FREQ_96: | ||
246 | pll1_speed = 516000000; | ||
247 | mclk_div = 42; | ||
248 | bclk_div = 2; | ||
249 | func_mode = 1; | ||
250 | break; | ||
251 | default: | ||
252 | return; | ||
253 | } | ||
254 | |||
255 | __i2s_stop_bitclk(); | ||
256 | |||
257 | /* 0 = Single-Speed Mode (<50KHz); | ||
258 | 1 = Double-Speed Mode (50-100KHz); | ||
259 | 2 = Quad-Speed Mode; (100-200KHz) */ | ||
260 | cs4398_write_reg(CS4398_REG_MODECTL, (cs4398_read_reg(CS4398_REG_MODECTL) & ~CS4398_FM_MASK) | func_mode); | ||
261 | if (func_mode == 2) | ||
262 | cs4398_write_reg(CS4398_REG_MISC, cs4398_read_reg(CS4398_REG_MISC) | CS4398_MCLKDIV2); | ||
263 | else | ||
264 | cs4398_write_reg(CS4398_REG_MISC, cs4398_read_reg(CS4398_REG_MISC) & ~CS4398_MCLKDIV2); | ||
265 | |||
266 | pll1_init(pll1_speed); | ||
267 | __cpm_enable_pll_change(); | ||
268 | __cpm_set_i2sdiv(mclk_div-1); | ||
269 | __i2s_set_i2sdiv(bclk_div-1); | ||
270 | __i2s_start_bitclk(); | ||
271 | } | ||
272 | |||
273 | void audiohw_postinit(void) | ||
274 | { | ||
275 | sleep(HZ); | ||
276 | audiohw_mute(false); | ||
277 | ap_mute(false); | ||
278 | pop_ctrl(1); | ||
279 | } | ||
280 | |||
281 | void audiohw_close(void) | ||
282 | { | ||
283 | pop_ctrl(0); | ||
284 | sleep(HZ/10); | ||
285 | ap_mute(true); | ||
286 | audiohw_mute(true); | ||
287 | amp_enable(0); | ||
288 | dac_enable(0); | ||
289 | __i2s_disable(); | ||
290 | __cpm_stop_aic(); | ||
291 | sleep(HZ); | ||
292 | pop_ctrl(1); | ||
293 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/crt0.S b/firmware/target/mips/ingenic_jz47xx/crt0.S index 0ae365022a..49de3e6a01 100644 --- a/firmware/target/mips/ingenic_jz47xx/crt0.S +++ b/firmware/target/mips/ingenic_jz47xx/crt0.S | |||
@@ -50,6 +50,7 @@ | |||
50 | .set noat | 50 | .set noat |
51 | 51 | ||
52 | #ifdef BOOTLOADER | 52 | #ifdef BOOTLOADER |
53 | #ifndef XDUOO_X3 | ||
53 | /* These will get filled in by scramble */ | 54 | /* These will get filled in by scramble */ |
54 | .word 0 /* Empty */ | 55 | .word 0 /* Empty */ |
55 | .word 0 /* Filesize */ | 56 | .word 0 /* Filesize */ |
@@ -65,6 +66,7 @@ _relocate_loop: | |||
65 | bne t1, t2, _relocate_loop | 66 | bne t1, t2, _relocate_loop |
66 | sw t3, -4(t1) | 67 | sw t3, -4(t1) |
67 | #endif | 68 | #endif |
69 | #endif | ||
68 | 70 | ||
69 | _start: | 71 | _start: |
70 | la ra, _start | 72 | la ra, _start |
diff --git a/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c b/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c new file mode 100644 index 0000000000..88fc351946 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/debug-jz4760.c | |||
@@ -0,0 +1,146 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | #include "system.h" | ||
24 | #include "cpu.h" | ||
25 | #include <stdarg.h> | ||
26 | #include <stdio.h> | ||
27 | #include "lcd.h" | ||
28 | #include "kernel.h" | ||
29 | #include "font.h" | ||
30 | #include "button.h" | ||
31 | #include "timefuncs.h" | ||
32 | |||
33 | #define CFG_UART_BASE UART1_BASE /* Base of the UART channel */ | ||
34 | |||
35 | void serial_putc (const char c) | ||
36 | { | ||
37 | volatile u8 *uart_lsr = (volatile u8 *)(CFG_UART_BASE + OFF_LSR); | ||
38 | volatile u8 *uart_tdr = (volatile u8 *)(CFG_UART_BASE + OFF_TDR); | ||
39 | |||
40 | if (c == '\n') serial_putc ('\r'); | ||
41 | |||
42 | /* Wait for fifo to shift out some bytes */ | ||
43 | while ( !((*uart_lsr & (UARTLSR_TDRQ | UARTLSR_TEMT)) == 0x60) ); | ||
44 | |||
45 | *uart_tdr = (u8)c; | ||
46 | } | ||
47 | |||
48 | void serial_puts (const char *s) | ||
49 | { | ||
50 | while (*s) { | ||
51 | serial_putc (*s++); | ||
52 | } | ||
53 | } | ||
54 | |||
55 | void serial_putsf(const char *format, ...) | ||
56 | { | ||
57 | static char printfbuf[256]; | ||
58 | int len; | ||
59 | unsigned char *ptr; | ||
60 | va_list ap; | ||
61 | va_start(ap, format); | ||
62 | |||
63 | ptr = printfbuf; | ||
64 | len = vsnprintf(ptr, sizeof(printfbuf), format, ap); | ||
65 | va_end(ap); | ||
66 | |||
67 | serial_puts(ptr); | ||
68 | serial_putc('\n'); | ||
69 | } | ||
70 | |||
71 | void serial_put_hex(unsigned int d) | ||
72 | { | ||
73 | char c[12]; | ||
74 | int i; | ||
75 | for(i = 0; i < 8;i++) | ||
76 | { | ||
77 | c[i] = (d >> ((7 - i) * 4)) & 0xf; | ||
78 | if(c[i] < 10) | ||
79 | c[i] += 0x30; | ||
80 | else | ||
81 | c[i] += (0x41 - 10); | ||
82 | } | ||
83 | c[8] = '\n'; | ||
84 | c[9] = 0; | ||
85 | serial_puts(c); | ||
86 | |||
87 | } | ||
88 | |||
89 | void serial_put_dec(unsigned int d) | ||
90 | { | ||
91 | char c[16]; | ||
92 | int i; | ||
93 | int j = 0; | ||
94 | int x = d; | ||
95 | |||
96 | while (x /= 10) | ||
97 | j++; | ||
98 | |||
99 | for (i = j; i >= 0; i--) { | ||
100 | c[i] = d % 10; | ||
101 | c[i] += 0x30; | ||
102 | d /= 10; | ||
103 | } | ||
104 | c[j + 1] = '\n'; | ||
105 | c[j + 2] = 0; | ||
106 | serial_puts(c); | ||
107 | } | ||
108 | |||
109 | void serial_dump_data(unsigned char* data, int len) | ||
110 | { | ||
111 | int i; | ||
112 | for(i=0; i<len; i++) | ||
113 | { | ||
114 | unsigned char a = ((*data)>>4) & 0xf; | ||
115 | if(a < 10) | ||
116 | a += 0x30; | ||
117 | else | ||
118 | a += (0x41 - 10); | ||
119 | serial_putc( a ); | ||
120 | |||
121 | a = (*data) & 0xf; | ||
122 | if(a < 10) | ||
123 | a += 0x30; | ||
124 | else | ||
125 | a += (0x41 - 10); | ||
126 | serial_putc( a ); | ||
127 | |||
128 | serial_putc( ' ' ); | ||
129 | |||
130 | data++; | ||
131 | } | ||
132 | |||
133 | serial_putc( '\n' ); | ||
134 | } | ||
135 | |||
136 | bool dbg_ports(void) | ||
137 | { | ||
138 | serial_puts("dbg_ports\n"); | ||
139 | return false; | ||
140 | } | ||
141 | |||
142 | bool dbg_hw_info(void) | ||
143 | { | ||
144 | serial_puts("dbg_hw_info\n"); | ||
145 | return false; | ||
146 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/dma-target.h b/firmware/target/mips/ingenic_jz47xx/dma-target.h new file mode 100644 index 0000000000..792d8fe87c --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/dma-target.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 | #ifndef __DMA_TARGET_H_ | ||
23 | #define __DMA_TARGET_H_ | ||
24 | |||
25 | #include "system.h" | ||
26 | #include <string.h> | ||
27 | |||
28 | void memset_dma(void *target, int c, size_t len, unsigned int bits); | ||
29 | void memcpy_dma(void *target, const void *source, size_t len, unsigned int bits); | ||
30 | |||
31 | #endif /* __DMA_TARGET_H_ */ | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/dma_acc-jz4760.c b/firmware/target/mips/ingenic_jz47xx/dma_acc-jz4760.c new file mode 100644 index 0000000000..4cdea2ad08 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/dma_acc-jz4760.c | |||
@@ -0,0 +1,102 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "dma-target.h" | ||
23 | |||
24 | #define MDMA_CHANNEL 0 | ||
25 | |||
26 | void memset_dma(void *target, int c, size_t len, unsigned int bits) | ||
27 | { | ||
28 | unsigned int d; | ||
29 | unsigned char *dp; | ||
30 | |||
31 | if(((unsigned int)target < 0xa0000000) && len) | ||
32 | dma_cache_wback_inv((unsigned long)target, len); | ||
33 | |||
34 | dp = (unsigned char *)((unsigned int)(&d) | 0xa0000000); | ||
35 | *(dp + 0) = c; | ||
36 | *(dp + 1) = c; | ||
37 | *(dp + 2) = c; | ||
38 | *(dp + 3) = c; | ||
39 | |||
40 | REG_MDMAC_DCCSR(MDMA_CHANNEL) = 0; | ||
41 | REG_MDMAC_DSAR(MDMA_CHANNEL) = PHYSADDR((unsigned long)dp); | ||
42 | REG_MDMAC_DTAR(MDMA_CHANNEL) = PHYSADDR((unsigned long)target); | ||
43 | REG_MDMAC_DRSR(MDMA_CHANNEL) = DMAC_DRSR_RS_AUTO; | ||
44 | switch (bits) | ||
45 | { | ||
46 | case 8: | ||
47 | REG_MDMAC_DTCR(MDMA_CHANNEL) = len; | ||
48 | REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_8BIT; | ||
49 | break; | ||
50 | case 16: | ||
51 | REG_MDMAC_DTCR(MDMA_CHANNEL) = len / 2; | ||
52 | REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BIT; | ||
53 | break; | ||
54 | case 32: | ||
55 | REG_MDMAC_DTCR(MDMA_CHANNEL) = len / 4; | ||
56 | REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; | ||
57 | break; | ||
58 | default: | ||
59 | return; | ||
60 | } | ||
61 | REG_MDMAC_DCCSR(MDMA_CHANNEL) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; | ||
62 | |||
63 | while (REG_MDMAC_DTCR(MDMA_CHANNEL)); | ||
64 | |||
65 | REG_MDMAC_DCCSR(MDMA_CHANNEL) = 0; | ||
66 | } | ||
67 | |||
68 | void memcpy_dma(void *target, const void *source, size_t len, unsigned int bits) | ||
69 | { | ||
70 | if(((unsigned int)source < 0xa0000000) && len) | ||
71 | dma_cache_wback_inv((unsigned long)source, len); | ||
72 | |||
73 | if(((unsigned int)target < 0xa0000000) && len) | ||
74 | dma_cache_wback_inv((unsigned long)target, len); | ||
75 | |||
76 | REG_MDMAC_DCCSR(MDMA_CHANNEL) = 0; | ||
77 | REG_MDMAC_DSAR(MDMA_CHANNEL) = PHYSADDR((unsigned long)source); | ||
78 | REG_MDMAC_DTAR(MDMA_CHANNEL) = PHYSADDR((unsigned long)target); | ||
79 | REG_MDMAC_DRSR(MDMA_CHANNEL) = DMAC_DRSR_RS_AUTO; | ||
80 | switch (bits) | ||
81 | { | ||
82 | case 8: | ||
83 | REG_MDMAC_DTCR(MDMA_CHANNEL) = len; | ||
84 | REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_8BIT; | ||
85 | break; | ||
86 | case 16: | ||
87 | REG_MDMAC_DTCR(MDMA_CHANNEL) = len / 2; | ||
88 | REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BIT; | ||
89 | break; | ||
90 | case 32: | ||
91 | REG_MDMAC_DTCR(MDMA_CHANNEL) = len / 4; | ||
92 | REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; | ||
93 | break; | ||
94 | default: | ||
95 | return; | ||
96 | } | ||
97 | REG_MDMAC_DCCSR(MDMA_CHANNEL) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; | ||
98 | |||
99 | while (REG_MDMAC_DTCR(MDMA_CHANNEL)); | ||
100 | |||
101 | REG_MDMAC_DCCSR(MDMA_CHANNEL) = 0; | ||
102 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c b/firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c new file mode 100644 index 0000000000..e35fe7a091 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c | |||
@@ -0,0 +1,355 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 | #include "config.h" | ||
22 | #include "system.h" | ||
23 | #include "cpu.h" | ||
24 | #include "logf.h" | ||
25 | #include "i2c.h" | ||
26 | |||
27 | #define I2C_CHN 1 | ||
28 | #define I2C_CLK 100000 | ||
29 | |||
30 | #define I2C_READ 1 | ||
31 | #define I2C_WRITE 0 | ||
32 | |||
33 | #define I2C_M_RD 1 | ||
34 | #define I2C_M_WR 2 | ||
35 | |||
36 | #define TIMEOUT 100000 | ||
37 | |||
38 | static char i2c_rwflags; | ||
39 | static int i2c_ctrl_rest = 0; | ||
40 | static unsigned char *msg_buf; | ||
41 | static int cmd_cnt; | ||
42 | static volatile int cmd_flag; | ||
43 | static int r_cnt; | ||
44 | static unsigned char i2c_subaddr = 0; | ||
45 | |||
46 | /* | ||
47 | * I2C bus protocol basic routines | ||
48 | */ | ||
49 | |||
50 | /* Interrupt handler */ | ||
51 | void I2C1(void) | ||
52 | { | ||
53 | int timeout = TIMEOUT; | ||
54 | |||
55 | if (__i2c_abrt_7b_addr_nack(I2C_CHN)) { | ||
56 | int ret; | ||
57 | cmd_flag = -1; | ||
58 | __i2c_clear_interrupts(ret,I2C_CHN); | ||
59 | REG_I2C_INTM(I2C_CHN) = 0x0; | ||
60 | return; | ||
61 | } | ||
62 | |||
63 | /* first byte,when length > 1 */ | ||
64 | if (cmd_flag == 0 && cmd_cnt > 1) { | ||
65 | cmd_flag = 1; | ||
66 | if (i2c_rwflags == I2C_M_RD) { | ||
67 | REG_I2C_DC(I2C_CHN) = I2C_READ << 8; | ||
68 | } else { | ||
69 | REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | *msg_buf++; | ||
70 | } | ||
71 | cmd_cnt--; | ||
72 | } | ||
73 | |||
74 | if (i2c_rwflags == I2C_M_RD) { | ||
75 | if (REG_I2C_STA(I2C_CHN) & I2C_STA_RFNE) { | ||
76 | *msg_buf++ = REG_I2C_DC(I2C_CHN) & 0xff; | ||
77 | r_cnt--; | ||
78 | } | ||
79 | |||
80 | REG_I2C_DC(I2C_CHN) = I2C_READ << 8; | ||
81 | } else { | ||
82 | REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | *msg_buf++; | ||
83 | } | ||
84 | |||
85 | cmd_cnt--; | ||
86 | |||
87 | if (!(cmd_cnt)) { | ||
88 | REG_I2C_INTM(I2C_CHN) = 0x0; | ||
89 | cmd_flag = 2; | ||
90 | if (i2c_rwflags == I2C_M_RD){ | ||
91 | while (r_cnt > 2) { | ||
92 | if ((REG_I2C_STA(I2C_CHN) & I2C_STA_RFNE) && timeout) { | ||
93 | *msg_buf++ = REG_I2C_DC(I2C_CHN) & 0xff; | ||
94 | r_cnt--; | ||
95 | } | ||
96 | if (!(timeout--)) { | ||
97 | cmd_flag = -1; | ||
98 | return; | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | return; | ||
105 | } | ||
106 | |||
107 | static int i2c_set_clk(int i2c_clk) | ||
108 | { | ||
109 | int dev_clk = __cpm_get_pclk(); | ||
110 | int count = 0; | ||
111 | |||
112 | if (i2c_clk < 0 || i2c_clk > 400000) | ||
113 | goto Set_clk_err; | ||
114 | |||
115 | count = dev_clk/i2c_clk - 23; | ||
116 | if (count < 0) | ||
117 | goto Set_clk_err; | ||
118 | |||
119 | if (i2c_clk <= 100000) { | ||
120 | REG_I2C_CTRL(I2C_CHN) = 0x43 | i2c_ctrl_rest; /* standard speed mode*/ | ||
121 | if (count%2 == 0) { | ||
122 | REG_I2C_SHCNT(I2C_CHN) = count/2 + 6 - 5; | ||
123 | REG_I2C_SLCNT(I2C_CHN) = count/2 + 8 + 5; | ||
124 | } else { | ||
125 | REG_I2C_SHCNT(I2C_CHN) = count/2 + 6 -5; | ||
126 | REG_I2C_SLCNT(I2C_CHN) = count/2 + 8 +5 + 1; | ||
127 | } | ||
128 | } else { | ||
129 | REG_I2C_CTRL(I2C_CHN) = 0x45 | i2c_ctrl_rest; /* high speed mode*/ | ||
130 | if (count%2 == 0) { | ||
131 | REG_I2C_FHCNT(I2C_CHN) = count/2 + 6; | ||
132 | REG_I2C_FLCNT(I2C_CHN) = count/2 + 8; | ||
133 | } else { | ||
134 | REG_I2C_FHCNT(I2C_CHN) = count/2 + 6; | ||
135 | REG_I2C_FLCNT(I2C_CHN) = count/2 + 8 + 1; | ||
136 | } | ||
137 | } | ||
138 | return 0; | ||
139 | |||
140 | Set_clk_err: | ||
141 | |||
142 | logf("i2c set sclk faild,i2c_clk=%d,dev_clk=%d.\n",i2c_clk,dev_clk); | ||
143 | return -1; | ||
144 | } | ||
145 | |||
146 | static int i2c_disable(void) | ||
147 | { | ||
148 | int timeout = TIMEOUT; | ||
149 | |||
150 | __i2c_disable(I2C_CHN); | ||
151 | while(__i2c_is_enable(I2C_CHN) && (timeout > 0)) { | ||
152 | udelay(1); | ||
153 | timeout--; | ||
154 | } | ||
155 | if(timeout) | ||
156 | return 0; | ||
157 | else | ||
158 | return 1; | ||
159 | } | ||
160 | |||
161 | static int i2c_enable(void) | ||
162 | { | ||
163 | int timeout = TIMEOUT; | ||
164 | |||
165 | __i2c_enable(I2C_CHN); | ||
166 | while(__i2c_is_disable(I2C_CHN) && (timeout > 0)) { | ||
167 | udelay(1); | ||
168 | timeout--; | ||
169 | } | ||
170 | if(timeout) | ||
171 | return 0; | ||
172 | else | ||
173 | return 1; | ||
174 | } | ||
175 | |||
176 | static void i2c_init_as_master(unsigned char address) | ||
177 | { | ||
178 | if(i2c_disable()) | ||
179 | logf("i2c not disable\n"); | ||
180 | |||
181 | i2c_set_clk(I2C_CLK); | ||
182 | |||
183 | REG_I2C_TAR(I2C_CHN) = address; /* slave id needed write only once */ | ||
184 | REG_I2C_INTM(I2C_CHN) = 0x0; /* unmask all interrupts */ | ||
185 | REG_I2C_TXTL(I2C_CHN) = 0x1; | ||
186 | |||
187 | if(i2c_enable()) | ||
188 | logf("i2c not enable\n"); | ||
189 | } | ||
190 | |||
191 | int xfer_read_subaddr(unsigned char subaddr, unsigned char device, unsigned char *buf, int length) | ||
192 | { | ||
193 | int timeout,r_i = 0; | ||
194 | |||
195 | cmd_cnt = length; | ||
196 | r_cnt = length; | ||
197 | msg_buf = buf; | ||
198 | i2c_rwflags = I2C_M_RD; | ||
199 | i2c_ctrl_rest = I2C_CTRL_REST; | ||
200 | i2c_init_as_master(device); | ||
201 | |||
202 | REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | subaddr; | ||
203 | |||
204 | cmd_flag = 0; | ||
205 | REG_I2C_INTM(I2C_CHN) = 0x10; | ||
206 | timeout = TIMEOUT; | ||
207 | while (cmd_flag != 2 && --timeout) { | ||
208 | if (cmd_flag == -1) { | ||
209 | r_i = 1; | ||
210 | goto R_dev_err; | ||
211 | } | ||
212 | udelay(10); | ||
213 | } | ||
214 | if (!timeout) { | ||
215 | r_i = 4; | ||
216 | goto R_timeout; | ||
217 | } | ||
218 | |||
219 | while (r_cnt) { | ||
220 | while (!(REG_I2C_STA(I2C_CHN) & I2C_STA_RFNE)) { | ||
221 | if ((cmd_flag == -1) || | ||
222 | (REG_I2C_INTST(I2C_CHN) & I2C_INTST_TXABT) || | ||
223 | REG_I2C_TXABRT(I2C_CHN)) { | ||
224 | int ret; | ||
225 | r_i = 2; | ||
226 | __i2c_clear_interrupts(ret,I2C_CHN); | ||
227 | goto R_dev_err; | ||
228 | } | ||
229 | } | ||
230 | *msg_buf++ = REG_I2C_DC(I2C_CHN) & 0xff; | ||
231 | r_cnt--; | ||
232 | } | ||
233 | |||
234 | timeout = TIMEOUT; | ||
235 | while ((REG_I2C_STA(I2C_CHN) & I2C_STA_MSTACT) && --timeout) | ||
236 | udelay(10); | ||
237 | if (!timeout){ | ||
238 | r_i = 3; | ||
239 | goto R_timeout; | ||
240 | } | ||
241 | |||
242 | return 0; | ||
243 | |||
244 | R_dev_err: | ||
245 | R_timeout: | ||
246 | |||
247 | i2c_init_as_master(device); | ||
248 | if (r_i == 1) { | ||
249 | logf("Read i2c device 0x%2x failed in r_i = %d :device no ack.\n",device,r_i); | ||
250 | } else if (r_i == 2) { | ||
251 | logf("Read i2c device 0x%2x failed in r_i = %d :i2c abort.\n",device,r_i); | ||
252 | } else if (r_i == 3) { | ||
253 | logf("Read i2c device 0x%2x failed in r_i = %d :waite master inactive timeout.\n",device,r_i); | ||
254 | } else if (r_i == 4) { | ||
255 | logf("Read i2c device 0x%2x failed in r_i = %d.\n",device,r_i); | ||
256 | } else { | ||
257 | logf("Read i2c device 0x%2x failed in r_i = %d.\n",device,r_i); | ||
258 | } | ||
259 | return -1; | ||
260 | } | ||
261 | |||
262 | int xfer_write_subaddr(unsigned char subaddr, unsigned char device, const unsigned char *buf, int length) | ||
263 | { | ||
264 | int timeout,w_i = 0; | ||
265 | |||
266 | cmd_cnt = length; | ||
267 | r_cnt = length; | ||
268 | msg_buf = (unsigned char *)buf; | ||
269 | i2c_rwflags = I2C_M_WR; | ||
270 | i2c_ctrl_rest = I2C_CTRL_REST; | ||
271 | i2c_init_as_master(device); | ||
272 | |||
273 | REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | subaddr; | ||
274 | |||
275 | cmd_flag = 0; | ||
276 | REG_I2C_INTM(I2C_CHN) = 0x10; | ||
277 | |||
278 | timeout = TIMEOUT; | ||
279 | while ((cmd_flag != 2) && (--timeout)) | ||
280 | { | ||
281 | if (cmd_flag == -1){ | ||
282 | w_i = 1; | ||
283 | goto W_dev_err; | ||
284 | } | ||
285 | udelay(10); | ||
286 | } | ||
287 | |||
288 | timeout = TIMEOUT; | ||
289 | while((!(REG_I2C_STA(I2C_CHN) & I2C_STA_TFE)) && --timeout){ | ||
290 | udelay(10); | ||
291 | } | ||
292 | if (!timeout){ | ||
293 | w_i = 2; | ||
294 | goto W_timeout; | ||
295 | } | ||
296 | |||
297 | timeout = TIMEOUT; | ||
298 | while (__i2c_master_active(I2C_CHN) && --timeout); | ||
299 | if (!timeout){ | ||
300 | w_i = 3; | ||
301 | goto W_timeout; | ||
302 | } | ||
303 | |||
304 | if ((length == 1)&& | ||
305 | ((cmd_flag == -1) || | ||
306 | (REG_I2C_INTST(I2C_CHN) & I2C_INTST_TXABT) || | ||
307 | REG_I2C_TXABRT(I2C_CHN))) { | ||
308 | int ret; | ||
309 | w_i = 5; | ||
310 | __i2c_clear_interrupts(ret,I2C_CHN); | ||
311 | goto W_dev_err; | ||
312 | } | ||
313 | |||
314 | return 0; | ||
315 | |||
316 | W_dev_err: | ||
317 | W_timeout: | ||
318 | |||
319 | i2c_init_as_master(device); | ||
320 | if (w_i == 1) { | ||
321 | logf("Write i2c device 0x%2x failed in w_i=%d:device no ack. I2C_CHN:[%d]sxyzhang\n",device,w_i,I2C_CHN); | ||
322 | } else if (w_i == 2) { | ||
323 | logf("Write i2c device 0x%2x failed in w_i=%d:waite TF buff empty timeout.\n",device,w_i); | ||
324 | } else if (w_i == 3) { | ||
325 | logf("Write i2c device 0x%2x failed in w_i=%d:waite master inactive timeout.\n",device,w_i); | ||
326 | } else if (w_i == 5) { | ||
327 | logf("Write i2c device 0x%2x failed in w_i=%d:device no ack or abort.I2C_CHN:[%d]sxyzhang \n",device,w_i,I2C_CHN); | ||
328 | } else { | ||
329 | logf("Write i2c device 0x%2x failed in w_i=%d.\n",device,w_i); | ||
330 | } | ||
331 | |||
332 | return -1; | ||
333 | } | ||
334 | |||
335 | int i2c_read(int device, unsigned char* buf, int count) | ||
336 | { | ||
337 | return xfer_read_subaddr(i2c_subaddr, device, &buf[0], count); | ||
338 | } | ||
339 | |||
340 | int i2c_write(int device, const unsigned char* buf, int count) | ||
341 | { | ||
342 | if (count < 2) | ||
343 | { | ||
344 | i2c_subaddr = buf[0]; | ||
345 | return 0; | ||
346 | } | ||
347 | return xfer_write_subaddr(buf[0], device, &buf[1], count-1); | ||
348 | } | ||
349 | |||
350 | void i2c_init(void) | ||
351 | { | ||
352 | __gpio_as_i2c(I2C_CHN); | ||
353 | __cpm_start_i2c1(); | ||
354 | system_enable_irq(IRQ_I2C1); | ||
355 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/kernel-jz4760.c b/firmware/target/mips/ingenic_jz47xx/kernel-jz4760.c new file mode 100644 index 0000000000..ab0f152669 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/kernel-jz4760.c | |||
@@ -0,0 +1,53 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | #include "system.h" | ||
24 | #include "kernel.h" | ||
25 | #include "cpu.h" | ||
26 | |||
27 | void tick_start(unsigned int interval_in_ms) | ||
28 | { | ||
29 | unsigned int latch; | ||
30 | |||
31 | /* 12Mhz / 4 = 3Mhz */ | ||
32 | latch = interval_in_ms*1000 * 3; | ||
33 | |||
34 | REG_OST_OSTCSR = OSTCSR_PRESCALE4 | OSTCSR_EXT_EN; | ||
35 | REG_OST_OSTDR = latch; | ||
36 | REG_OST_OSTCNTL = 0; | ||
37 | REG_OST_OSTCNTH = 0; | ||
38 | |||
39 | system_enable_irq(IRQ_TCU0); | ||
40 | |||
41 | REG_TCU_TMCR = TMCR_OSTMASK; /* unmask match irq */ | ||
42 | REG_TCU_TSCR = TSCR_OST; /* enable timer clock */ | ||
43 | REG_TCU_TESR = TESR_OST; /* start counting up */ | ||
44 | } | ||
45 | |||
46 | /* Interrupt handler */ | ||
47 | void TCU0(void) | ||
48 | { | ||
49 | REG_TCU_TFCR = TFCR_OSTFLAG; /* ACK timer */ | ||
50 | |||
51 | /* Run through the list of tick tasks */ | ||
52 | call_tick_tasks(); | ||
53 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/lcd-jz4760.c b/firmware/target/mips/ingenic_jz47xx/lcd-jz4760.c new file mode 100644 index 0000000000..03fb937d0a --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/lcd-jz4760.c | |||
@@ -0,0 +1,28 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 | #include <sys/types.h> /* off_t */ | ||
22 | |||
23 | #include "config.h" | ||
24 | #include "cpu.h" | ||
25 | #include "lcd.h" | ||
26 | #include "lcd-target.h" | ||
27 | #include "system.h" | ||
28 | #include "kernel.h" | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c new file mode 100644 index 0000000000..39df037f76 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c | |||
@@ -0,0 +1,240 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "logf.h" | ||
25 | #include "audio.h" | ||
26 | #include "sound.h" | ||
27 | #include "pcm.h" | ||
28 | #include "pcm-internal.h" | ||
29 | #include "cpu.h" | ||
30 | |||
31 | |||
32 | /**************************************************************************** | ||
33 | ** Playback DMA transfer | ||
34 | **/ | ||
35 | |||
36 | void pcm_play_dma_postinit(void) | ||
37 | { | ||
38 | audiohw_postinit(); | ||
39 | |||
40 | /* Flush FIFO */ | ||
41 | __aic_flush_tfifo(); | ||
42 | } | ||
43 | |||
44 | void pcm_play_dma_init(void) | ||
45 | { | ||
46 | system_enable_irq(DMA_IRQ(DMA_AIC_TX_CHANNEL)); | ||
47 | |||
48 | /* Initialize default register values. */ | ||
49 | audiohw_init(); | ||
50 | } | ||
51 | |||
52 | void pcm_dma_apply_settings(void) | ||
53 | { | ||
54 | audiohw_set_frequency(pcm_fsel); | ||
55 | } | ||
56 | |||
57 | static const void* playback_address; | ||
58 | static inline void set_dma(const void *addr, size_t size) | ||
59 | { | ||
60 | int burst_size; | ||
61 | logf("%x %d %x", (unsigned int)addr, size, REG_AIC_SR); | ||
62 | |||
63 | dma_cache_wback_inv((unsigned long)addr, size); | ||
64 | |||
65 | if(size % 16) | ||
66 | { | ||
67 | if(size % 4) | ||
68 | { | ||
69 | size /= 2; | ||
70 | burst_size = DMAC_DCMD_DS_16BIT; | ||
71 | } | ||
72 | else | ||
73 | { | ||
74 | size /= 4; | ||
75 | burst_size = DMAC_DCMD_DS_32BIT; | ||
76 | } | ||
77 | } | ||
78 | else | ||
79 | { | ||
80 | size /= 16; | ||
81 | burst_size = DMAC_DCMD_DS_16BYTE; | ||
82 | } | ||
83 | |||
84 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = 0; | ||
85 | REG_DMAC_DSAR(DMA_AIC_TX_CHANNEL) = PHYSADDR((unsigned long)addr); | ||
86 | REG_DMAC_DTAR(DMA_AIC_TX_CHANNEL) = PHYSADDR((unsigned long)AIC_DR); | ||
87 | REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL) = size; | ||
88 | REG_DMAC_DRSR(DMA_AIC_TX_CHANNEL) = DMAC_DRSR_RS_AICOUT; | ||
89 | REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) = (DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | burst_size | DMAC_DCMD_DWDH_16 | DMAC_DCMD_TIE); | ||
90 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; | ||
91 | |||
92 | playback_address = addr; | ||
93 | } | ||
94 | |||
95 | static inline void play_dma_callback(void) | ||
96 | { | ||
97 | const void *start; | ||
98 | size_t size; | ||
99 | |||
100 | if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) | ||
101 | { | ||
102 | set_dma(start, size); | ||
103 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; | ||
104 | pcm_play_dma_status_callback(PCM_DMAST_STARTED); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void) __attribute__ ((section(".icode"))); | ||
109 | void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void) | ||
110 | { | ||
111 | if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_AR) | ||
112 | { | ||
113 | logf("PCM DMA address error"); | ||
114 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_AR; | ||
115 | } | ||
116 | |||
117 | if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_HLT) | ||
118 | { | ||
119 | logf("PCM DMA halt"); | ||
120 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_HLT; | ||
121 | } | ||
122 | |||
123 | if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_TT) | ||
124 | { | ||
125 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_TT; | ||
126 | play_dma_callback(); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | void pcm_play_dma_start(const void *addr, size_t size) | ||
131 | { | ||
132 | __dmac_channel_enable_clk(DMA_AIC_TX_CHANNEL); | ||
133 | |||
134 | set_dma(addr, size); | ||
135 | |||
136 | __aic_enable_replay(); | ||
137 | |||
138 | __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL); | ||
139 | } | ||
140 | |||
141 | void pcm_play_dma_stop(void) | ||
142 | { | ||
143 | int flags = disable_irq_save(); | ||
144 | |||
145 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) | DMAC_DCCSR_HLT) & ~DMAC_DCCSR_EN; | ||
146 | |||
147 | __dmac_channel_disable_clk(DMA_AIC_TX_CHANNEL); | ||
148 | |||
149 | __aic_disable_replay(); | ||
150 | |||
151 | restore_irq(flags); | ||
152 | } | ||
153 | |||
154 | static unsigned int play_lock = 0; | ||
155 | void pcm_play_lock(void) | ||
156 | { | ||
157 | int flags = disable_irq_save(); | ||
158 | |||
159 | if (++play_lock == 1) | ||
160 | __dmac_channel_disable_irq(DMA_AIC_TX_CHANNEL); | ||
161 | |||
162 | restore_irq(flags); | ||
163 | } | ||
164 | |||
165 | void pcm_play_unlock(void) | ||
166 | { | ||
167 | int flags = disable_irq_save(); | ||
168 | |||
169 | if (--play_lock == 0) | ||
170 | __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL); | ||
171 | |||
172 | restore_irq(flags); | ||
173 | } | ||
174 | |||
175 | void pcm_play_dma_pause(bool pause) | ||
176 | { | ||
177 | int flags = disable_irq_save(); | ||
178 | |||
179 | if(pause) | ||
180 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_EN; | ||
181 | else | ||
182 | REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; | ||
183 | |||
184 | restore_irq(flags); | ||
185 | } | ||
186 | |||
187 | static int get_dma_count(void) | ||
188 | { | ||
189 | int count = REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL); | ||
190 | switch(REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) & DMAC_DCMD_DS_MASK) | ||
191 | { | ||
192 | case DMAC_DCMD_DS_16BIT: | ||
193 | count *= 2; | ||
194 | break; | ||
195 | case DMAC_DCMD_DS_32BIT: | ||
196 | count *= 4; | ||
197 | break; | ||
198 | case DMAC_DCMD_DS_16BYTE: | ||
199 | count *= 16; | ||
200 | break; | ||
201 | } | ||
202 | |||
203 | return count; | ||
204 | } | ||
205 | |||
206 | size_t pcm_get_bytes_waiting(void) | ||
207 | { | ||
208 | int bytes, flags = disable_irq_save(); | ||
209 | |||
210 | if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN) | ||
211 | bytes = get_dma_count() & ~3; | ||
212 | else | ||
213 | bytes = 0; | ||
214 | |||
215 | restore_irq(flags); | ||
216 | |||
217 | return bytes; | ||
218 | } | ||
219 | |||
220 | const void * pcm_play_dma_get_peak_buffer(int *count) | ||
221 | { | ||
222 | int flags = disable_irq_save(); | ||
223 | |||
224 | const void* addr; | ||
225 | if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN) | ||
226 | { | ||
227 | int bytes = get_dma_count(); | ||
228 | *count = bytes >> 2; | ||
229 | addr = (const void*)((int)(playback_address + bytes + 2) & ~3); | ||
230 | } | ||
231 | else | ||
232 | { | ||
233 | *count = 0; | ||
234 | addr = NULL; | ||
235 | } | ||
236 | |||
237 | restore_irq(flags); | ||
238 | |||
239 | return addr; | ||
240 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/system-jz4760.c b/firmware/target/mips/ingenic_jz47xx/system-jz4760.c new file mode 100644 index 0000000000..8472a7378f --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/system-jz4760.c | |||
@@ -0,0 +1,710 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | #include "cpu.h" | ||
24 | #include "mips.h" | ||
25 | #include "mmu-mips.h" | ||
26 | #include "panic.h" | ||
27 | #include "system.h" | ||
28 | #include "kernel.h" | ||
29 | #include "power.h" | ||
30 | |||
31 | static int irq; | ||
32 | static void UIRQ(void) | ||
33 | { | ||
34 | panicf("Unhandled interrupt occurred: %d", irq); | ||
35 | } | ||
36 | |||
37 | #define intr(name) extern __attribute__((weak,alias("UIRQ"))) void name (void) | ||
38 | |||
39 | intr(I2C1);intr(I2C0);intr(UART3);intr(UART2);intr(UART1);intr(UART0);intr(GPU); | ||
40 | intr(SSI1);intr(SSI0);intr(TSSI);intr(KBC);intr(SADC);intr(ETH);intr(UHC); | ||
41 | intr(OTG);intr(TCU2);intr(TCU1);intr(TCU0);intr(GPS);intr(IPU);intr(CIM); | ||
42 | intr(LCD);intr(RTC);intr(OWI);intr(AIC);intr(MSC2);intr(MSC1);intr(MSC0); | ||
43 | intr(SCC);intr(BCH);intr(PCM);intr(HARB0);intr(HARB2);intr(AOSD);intr(CPM); | ||
44 | |||
45 | intr(DMA0);intr(DMA1);intr(DMA2);intr(DMA3);intr(DMA4);intr(DMA5); | ||
46 | intr(DMA6);intr(DMA7);intr(DMA8);intr(DMA9);intr(DMA10);intr(DMA11); | ||
47 | intr(MDMA0);intr(MDMA1);intr(MDMA2); | ||
48 | intr(BDMA0);intr(BDMA1);intr(BDMA2); | ||
49 | |||
50 | intr(GPIO0);intr(GPIO1);intr(GPIO2);intr(GPIO3);intr(GPIO4);intr(GPIO5); | ||
51 | intr(GPIO6);intr(GPIO7);intr(GPIO8);intr(GPIO9);intr(GPIO10);intr(GPIO11); | ||
52 | intr(GPIO12);intr(GPIO13);intr(GPIO14);intr(GPIO15);intr(GPIO16);intr(GPIO17); | ||
53 | intr(GPIO18);intr(GPIO19);intr(GPIO20);intr(GPIO21);intr(GPIO22);intr(GPIO23); | ||
54 | intr(GPIO24);intr(GPIO25);intr(GPIO26);intr(GPIO27);intr(GPIO28);intr(GPIO29); | ||
55 | intr(GPIO30);intr(GPIO31);intr(GPIO32);intr(GPIO33);intr(GPIO34);intr(GPIO35); | ||
56 | intr(GPIO36);intr(GPIO37);intr(GPIO38);intr(GPIO39);intr(GPIO40);intr(GPIO41); | ||
57 | intr(GPIO42);intr(GPIO43);intr(GPIO44);intr(GPIO45);intr(GPIO46);intr(GPIO47); | ||
58 | intr(GPIO48);intr(GPIO49);intr(GPIO50);intr(GPIO51);intr(GPIO52);intr(GPIO53); | ||
59 | intr(GPIO54);intr(GPIO55);intr(GPIO56);intr(GPIO57);intr(GPIO58);intr(GPIO59); | ||
60 | intr(GPIO60);intr(GPIO61);intr(GPIO62);intr(GPIO63);intr(GPIO64);intr(GPIO65); | ||
61 | intr(GPIO66);intr(GPIO67);intr(GPIO68);intr(GPIO69);intr(GPIO70);intr(GPIO71); | ||
62 | intr(GPIO72);intr(GPIO73);intr(GPIO74);intr(GPIO75);intr(GPIO76);intr(GPIO77); | ||
63 | intr(GPIO78);intr(GPIO79);intr(GPIO80);intr(GPIO81);intr(GPIO82);intr(GPIO83); | ||
64 | intr(GPIO84);intr(GPIO85);intr(GPIO86);intr(GPIO87);intr(GPIO88);intr(GPIO89); | ||
65 | intr(GPIO90);intr(GPIO91);intr(GPIO92);intr(GPIO93);intr(GPIO94);intr(GPIO95); | ||
66 | intr(GPIO96);intr(GPIO97);intr(GPIO98);intr(GPIO99);intr(GPIO100);intr(GPIO101); | ||
67 | intr(GPIO102);intr(GPIO103);intr(GPIO104);intr(GPIO105);intr(GPIO106); | ||
68 | intr(GPIO107);intr(GPIO108);intr(GPIO109);intr(GPIO110);intr(GPIO111); | ||
69 | intr(GPIO112);intr(GPIO113);intr(GPIO114);intr(GPIO115);intr(GPIO116); | ||
70 | intr(GPIO117);intr(GPIO118);intr(GPIO119);intr(GPIO120);intr(GPIO121); | ||
71 | intr(GPIO122);intr(GPIO123);intr(GPIO124);intr(GPIO125);intr(GPIO126); | ||
72 | intr(GPIO127);intr(GPIO128);intr(GPIO129);intr(GPIO130);intr(GPIO131); | ||
73 | intr(GPIO132);intr(GPIO133);intr(GPIO134);intr(GPIO135);intr(GPIO136); | ||
74 | intr(GPIO137);intr(GPIO138);intr(GPIO139);intr(GPIO140);intr(GPIO141); | ||
75 | intr(GPIO142);intr(GPIO143);intr(GPIO144);intr(GPIO145);intr(GPIO146); | ||
76 | intr(GPIO147);intr(GPIO148);intr(GPIO149);intr(GPIO150);intr(GPIO151); | ||
77 | intr(GPIO152);intr(GPIO153);intr(GPIO154);intr(GPIO155);intr(GPIO156); | ||
78 | intr(GPIO157);intr(GPIO158);intr(GPIO159);intr(GPIO160);intr(GPIO161); | ||
79 | intr(GPIO162);intr(GPIO163);intr(GPIO164);intr(GPIO165);intr(GPIO166); | ||
80 | intr(GPIO167);intr(GPIO168);intr(GPIO169);intr(GPIO170);intr(GPIO171); | ||
81 | intr(GPIO172);intr(GPIO173);intr(GPIO174);intr(GPIO175);intr(GPIO176); | ||
82 | intr(GPIO177);intr(GPIO178);intr(GPIO179);intr(GPIO180);intr(GPIO181); | ||
83 | intr(GPIO182);intr(GPIO183);intr(GPIO184);intr(GPIO185);intr(GPIO186); | ||
84 | intr(GPIO187);intr(GPIO188);intr(GPIO189);intr(GPIO190);intr(GPIO191); | ||
85 | |||
86 | static void (* const irqvector[])(void) = | ||
87 | { | ||
88 | I2C1,I2C0,UART3,UART2,UART1,UART0,GPU,SSI1, | ||
89 | SSI0,TSSI,UIRQ,KBC,UIRQ,UIRQ,UIRQ,UIRQ, | ||
90 | UIRQ,UIRQ,SADC,ETH,UHC,OTG,UIRQ,UIRQ, | ||
91 | UIRQ,TCU2,TCU1,TCU0,GPS,IPU,CIM,LCD, | ||
92 | |||
93 | RTC,OWI,AIC,MSC2,MSC1,MSC0,SCC,BCH, // 32 | ||
94 | PCM,HARB0,HARB2,AOSD,CPM,UIRQ, | ||
95 | |||
96 | DMA0,DMA1,DMA2,DMA3,DMA4,DMA5,DMA6,DMA7, // 46 | ||
97 | DMA8,DMA9,DMA10,DMA11,MDMA0,MDMA1,MDMA2,BDMA0, | ||
98 | BDMA1,BDMA2, | ||
99 | |||
100 | GPIO0,GPIO1,GPIO2,GPIO3,GPIO4,GPIO5,GPIO6,GPIO7, // 64 | ||
101 | GPIO8,GPIO9,GPIO10,GPIO11,GPIO12,GPIO13,GPIO14,GPIO15, | ||
102 | GPIO16,GPIO17,GPIO18,GPIO19,GPIO20,GPIO21,GPIO22,GPIO23, | ||
103 | GPIO24,GPIO25,GPIO26,GPIO27,GPIO28,GPIO29,GPIO30,GPIO31, | ||
104 | GPIO32,GPIO33,GPIO34,GPIO35,GPIO36,GPIO37,GPIO38,GPIO39, | ||
105 | GPIO40,GPIO41,GPIO42,GPIO43,GPIO44,GPIO45,GPIO46,GPIO47, | ||
106 | GPIO48,GPIO49,GPIO50,GPIO51,GPIO52,GPIO53,GPIO54,GPIO55, | ||
107 | GPIO56,GPIO57,GPIO58,GPIO59,GPIO60,GPIO61,GPIO62,GPIO63, | ||
108 | GPIO64,GPIO65,GPIO66,GPIO67,GPIO68,GPIO69,GPIO70,GPIO71, | ||
109 | GPIO72,GPIO73,GPIO74,GPIO75,GPIO76,GPIO77,GPIO78,GPIO79, | ||
110 | GPIO80,GPIO81,GPIO82,GPIO83,GPIO84,GPIO85,GPIO86,GPIO87, | ||
111 | GPIO88,GPIO89,GPIO90,GPIO91,GPIO92,GPIO93,GPIO94,GPIO95, | ||
112 | GPIO96,GPIO97,GPIO98,GPIO99,GPIO100,GPIO101,GPIO102,GPIO103, | ||
113 | GPIO104,GPIO105,GPIO106,GPIO107,GPIO108,GPIO109,GPIO110,GPIO111, | ||
114 | GPIO112,GPIO113,GPIO114,GPIO115,GPIO116,GPIO117,GPIO118,GPIO119, | ||
115 | GPIO120,GPIO121,GPIO122,GPIO123,GPIO124,GPIO125,GPIO126,GPIO127, | ||
116 | GPIO128,GPIO129,GPIO130,GPIO131,GPIO132,GPIO133,GPIO134,GPIO135, | ||
117 | GPIO136,GPIO137,GPIO138,GPIO139,GPIO140,GPIO141,GPIO142,GPIO143, | ||
118 | GPIO144,GPIO145,GPIO146,GPIO147,GPIO148,GPIO149,GPIO150,GPIO151, | ||
119 | GPIO152,GPIO153,GPIO154,GPIO155,GPIO156,GPIO157,GPIO158,GPIO159, | ||
120 | GPIO160,GPIO161,GPIO162,GPIO163,GPIO164,GPIO165,GPIO166,GPIO167, | ||
121 | GPIO168,GPIO169,GPIO170,GPIO171,GPIO172,GPIO173,GPIO174,GPIO175, | ||
122 | GPIO176,GPIO177,GPIO178,GPIO179,GPIO180,GPIO181,GPIO182,GPIO183, | ||
123 | GPIO184,GPIO185,GPIO186,GPIO187,GPIO188,GPIO189,GPIO190,GPIO191 | ||
124 | }; | ||
125 | |||
126 | static unsigned int dma_irq_mask = 0; | ||
127 | static unsigned char mdma_irq_mask = 0; | ||
128 | static unsigned char bdma_irq_mask = 0; | ||
129 | static unsigned int gpio_irq_mask[6] = {0}; | ||
130 | |||
131 | void system_enable_irq(unsigned int irq) | ||
132 | { | ||
133 | register unsigned int t; | ||
134 | if ((irq >= IRQ_GPIO_0) && (irq <= IRQ_GPIO_0 + NUM_GPIO)) | ||
135 | { | ||
136 | __gpio_unmask_irq(irq - IRQ_GPIO_0); | ||
137 | t = (irq - IRQ_GPIO_0) >> 5; | ||
138 | gpio_irq_mask[t] |= (1 << ((irq - IRQ_GPIO_0) & 0x1f)); | ||
139 | __intc_unmask_irq(IRQ_GPIO0 - t); | ||
140 | } | ||
141 | else if ((irq >= IRQ_DMA_0) && (irq <= IRQ_DMA_0 + NUM_DMA)) | ||
142 | { | ||
143 | __dmac_channel_enable_irq(irq - IRQ_DMA_0); | ||
144 | t = (irq - IRQ_DMA_0) / HALF_DMA_NUM; | ||
145 | dma_irq_mask |= (1 << (irq - IRQ_DMA_0)); | ||
146 | __intc_unmask_irq(IRQ_DMAC0 - t); | ||
147 | } | ||
148 | else if ((irq >= IRQ_MDMA_0) && (irq <= IRQ_MDMA_0 + NUM_MDMA)) | ||
149 | { | ||
150 | __mdmac_channel_enable_irq(irq - IRQ_MDMA_0); | ||
151 | mdma_irq_mask |= (1 << (irq - IRQ_MDMA_0)); | ||
152 | __intc_unmask_irq(IRQ_MDMA); | ||
153 | } | ||
154 | else if ((irq >= IRQ_BDMA_0) && (irq <= IRQ_BDMA_0 + NUM_BDMA)) | ||
155 | { | ||
156 | __bdmac_channel_enable_irq(irq - IRQ_BDMA_0); | ||
157 | bdma_irq_mask |= (1 << (irq - IRQ_BDMA_0)); | ||
158 | __intc_unmask_irq(IRQ_BDMA); | ||
159 | } | ||
160 | else if (irq < IRQ_INTC_MAX) | ||
161 | __intc_unmask_irq(irq); | ||
162 | } | ||
163 | |||
164 | static void dis_irq(unsigned int irq) | ||
165 | { | ||
166 | register unsigned int t; | ||
167 | if ((irq >= IRQ_GPIO_0) && (irq <= IRQ_GPIO_0 + NUM_GPIO)) | ||
168 | { | ||
169 | __gpio_mask_irq(irq - IRQ_GPIO_0); | ||
170 | t = (irq - IRQ_GPIO_0) >> 5; | ||
171 | gpio_irq_mask[t] &= ~(1 << ((irq - IRQ_GPIO_0) & 0x1f)); | ||
172 | if (!gpio_irq_mask[t]) | ||
173 | __intc_mask_irq(IRQ_GPIO0 - t); | ||
174 | } | ||
175 | else if ((irq >= IRQ_DMA_0) && (irq < IRQ_DMA_0 + NUM_DMA)) | ||
176 | { | ||
177 | __dmac_channel_disable_irq(irq - IRQ_DMA_0); | ||
178 | dma_irq_mask &= ~(1 << (irq - IRQ_DMA_0)); | ||
179 | if (!(dma_irq_mask & 0x003F)) | ||
180 | __intc_mask_irq(IRQ_DMAC0); | ||
181 | if (!(dma_irq_mask & 0x0FC0)) | ||
182 | __intc_mask_irq(IRQ_DMAC1); | ||
183 | } | ||
184 | else if ((irq >= IRQ_MDMA_0) && (irq < IRQ_MDMA_0 + NUM_MDMA)) | ||
185 | { | ||
186 | __mdmac_channel_disable_irq(irq - IRQ_MDMA_0); | ||
187 | mdma_irq_mask &= ~(1 << (irq - IRQ_MDMA_0)); | ||
188 | if (!mdma_irq_mask) | ||
189 | __intc_mask_irq(IRQ_MDMA); | ||
190 | } | ||
191 | else if ((irq >= IRQ_BDMA_0) && (irq < IRQ_BDMA_0 + NUM_BDMA)) | ||
192 | { | ||
193 | __bdmac_channel_disable_irq(irq - IRQ_BDMA_0); | ||
194 | bdma_irq_mask &= ~(1 << (irq - IRQ_BDMA_0)); | ||
195 | if (!bdma_irq_mask) | ||
196 | __intc_mask_irq(IRQ_BDMA); | ||
197 | } | ||
198 | else if (irq < IRQ_INTC_MAX) | ||
199 | __intc_mask_irq(irq); | ||
200 | } | ||
201 | |||
202 | static void ack_irq(unsigned int irq) | ||
203 | { | ||
204 | if ((irq >= IRQ_GPIO_0) && (irq <= IRQ_GPIO_0 + NUM_GPIO)) | ||
205 | { | ||
206 | __gpio_ack_irq(irq - IRQ_GPIO_0); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | static int get_irq_number(void) | ||
211 | { | ||
212 | static unsigned long ipl0, ipl1; | ||
213 | register int irq0, irq1; | ||
214 | |||
215 | ipl0 |= REG_INTC_ICPR(0); | ||
216 | ipl1 |= REG_INTC_ICPR(1); | ||
217 | |||
218 | if (!(ipl0 || ipl1)) | ||
219 | return -1; | ||
220 | |||
221 | __asm__ __volatile__("negu $8, %0 \n" | ||
222 | "and $8, %0, $8 \n" | ||
223 | "clz %0, %1 \n" | ||
224 | "li $8, 31 \n" | ||
225 | "subu %0, $8, %0 \n" | ||
226 | : "=r" (irq0) | ||
227 | : "r" (ipl0) | ||
228 | : "t0" | ||
229 | ); | ||
230 | |||
231 | __asm__ __volatile__("negu $8, %0 \n" | ||
232 | "and $8, %0, $8 \n" | ||
233 | "clz %0, %1 \n" | ||
234 | "li $8, 31 \n" | ||
235 | "subu %0, $8, %0 \n" | ||
236 | : "=r" (irq1) | ||
237 | : "r" (ipl1) | ||
238 | : "t0" | ||
239 | ); | ||
240 | |||
241 | if (UNLIKELY(irq0 < 0) && UNLIKELY(irq1 < 0)) | ||
242 | return -1; | ||
243 | |||
244 | if (!(ipl0 & 3)) { | ||
245 | if (ipl0) { | ||
246 | irq = irq0; | ||
247 | ipl0 &= ~(1<<irq0); | ||
248 | } else { | ||
249 | irq = irq1 + 32; | ||
250 | ipl1 &= ~(1<<irq1); | ||
251 | } | ||
252 | } else { | ||
253 | if (ipl0 & 2) { | ||
254 | irq = 1; | ||
255 | ipl0 &= ~(1<<irq); | ||
256 | } else { | ||
257 | irq = 0; | ||
258 | ipl0 &= ~(1<<irq); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | switch (irq) | ||
263 | { | ||
264 | case IRQ_GPIO0: | ||
265 | case IRQ_GPIO1: | ||
266 | case IRQ_GPIO2: | ||
267 | case IRQ_GPIO3: | ||
268 | case IRQ_GPIO4: | ||
269 | case IRQ_GPIO5: | ||
270 | irq = __gpio_get_irq() + IRQ_GPIO_0; | ||
271 | break; | ||
272 | case IRQ_DMAC0: | ||
273 | case IRQ_DMAC1: | ||
274 | irq = __dmac_get_irq() + IRQ_DMA_0; | ||
275 | break; | ||
276 | case IRQ_MDMA: | ||
277 | irq = __mdmac_get_irq() + IRQ_MDMA_0; | ||
278 | break; | ||
279 | case IRQ_BDMA: | ||
280 | irq = __bdmac_get_irq() + IRQ_BDMA_0; | ||
281 | break; | ||
282 | } | ||
283 | |||
284 | return irq; | ||
285 | } | ||
286 | |||
287 | void intr_handler(void) | ||
288 | { | ||
289 | register int irq = get_irq_number(); | ||
290 | if(UNLIKELY(irq < 0)) | ||
291 | return; | ||
292 | |||
293 | ack_irq(irq); | ||
294 | if(LIKELY(irq >= 0)) | ||
295 | irqvector[irq](); | ||
296 | } | ||
297 | |||
298 | #define EXC(x,y) case (x): return (y); | ||
299 | static char* parse_exception(unsigned int cause) | ||
300 | { | ||
301 | switch(cause & M_CauseExcCode) | ||
302 | { | ||
303 | EXC(EXC_INT, "Interrupt"); | ||
304 | EXC(EXC_MOD, "TLB Modified"); | ||
305 | EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)"); | ||
306 | EXC(EXC_ADEL, "Address Error (Load or Ifetch)"); | ||
307 | EXC(EXC_ADES, "Address Error (Store)"); | ||
308 | EXC(EXC_TLBS, "TLB Exception (Store)"); | ||
309 | EXC(EXC_IBE, "Instruction Bus Error"); | ||
310 | EXC(EXC_DBE, "Data Bus Error"); | ||
311 | EXC(EXC_SYS, "Syscall"); | ||
312 | EXC(EXC_BP, "Breakpoint"); | ||
313 | EXC(EXC_RI, "Reserved Instruction"); | ||
314 | EXC(EXC_CPU, "Coprocessor Unusable"); | ||
315 | EXC(EXC_OV, "Overflow"); | ||
316 | EXC(EXC_TR, "Trap Instruction"); | ||
317 | EXC(EXC_FPE, "Floating Point Exception"); | ||
318 | EXC(EXC_C2E, "COP2 Exception"); | ||
319 | EXC(EXC_MDMX, "MDMX Exception"); | ||
320 | EXC(EXC_WATCH, "Watch Exception"); | ||
321 | EXC(EXC_MCHECK, "Machine Check Exception"); | ||
322 | EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode"); | ||
323 | default: | ||
324 | return NULL; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | void exception_handler(void* stack_ptr, unsigned int cause, unsigned int epc) | ||
329 | { | ||
330 | panicf("Exception occurred: %s [0x%08x] at 0x%08x (stack at 0x%08x)", parse_exception(cause), read_c0_badvaddr(), epc, (unsigned int)stack_ptr); | ||
331 | } | ||
332 | |||
333 | void tlb_refill_handler(void) | ||
334 | { | ||
335 | panicf("TLB refill handler at 0x%08lx! [0x%x]", read_c0_epc(), read_c0_badvaddr()); | ||
336 | } | ||
337 | |||
338 | void udelay(unsigned int usec) | ||
339 | { | ||
340 | unsigned int i = usec * (__cpm_get_cclk() / 2000000); | ||
341 | __asm__ __volatile__ ( | ||
342 | ".set noreorder \n" | ||
343 | "1: \n" | ||
344 | "bne %0, $0, 1b \n" | ||
345 | "addi %0, %0, -1 \n" | ||
346 | ".set reorder \n" | ||
347 | : "=r" (i) | ||
348 | : "0" (i) | ||
349 | ); | ||
350 | } | ||
351 | |||
352 | void mdelay(unsigned int msec) | ||
353 | { | ||
354 | unsigned int i; | ||
355 | for(i=0; i<msec; i++) | ||
356 | udelay(1000); | ||
357 | } | ||
358 | |||
359 | #define MHZ (1000 * 1000) | ||
360 | static inline unsigned int pll_calc_m_n_od(unsigned int speed, unsigned int xtal) | ||
361 | { | ||
362 | const int pll_m_max = 0x7f, pll_m_min = 4; | ||
363 | const int pll_n_max = 0x0f, pll_n_min = 2; | ||
364 | |||
365 | int od[] = {1, 2, 4, 8}; | ||
366 | |||
367 | unsigned int plcr_m_n_od = 0; | ||
368 | unsigned int distance; | ||
369 | unsigned int tmp, raw; | ||
370 | |||
371 | int i, j, k; | ||
372 | int m, n; | ||
373 | |||
374 | distance = 0xFFFFFFFF; | ||
375 | |||
376 | for (i = 0; i < (int)sizeof (od) / (int)sizeof(int); i++) { | ||
377 | /* Limit: 500MHZ <= CLK_OUT * OD <= 1500MHZ */ | ||
378 | if ((speed * od[i]) < 500 * MHZ || (speed * od[i]) > 1500 * MHZ) | ||
379 | continue; | ||
380 | for (k = pll_n_min; k <= pll_n_max; k++) { | ||
381 | n = k; | ||
382 | |||
383 | /* Limit: 1MHZ <= XIN/N <= 50MHZ */ | ||
384 | if ((xtal / n) < (1 * MHZ)) | ||
385 | break; | ||
386 | if ((xtal / n) > (15 * MHZ)) | ||
387 | continue; | ||
388 | |||
389 | for (j = pll_m_min; j <= pll_m_max; j++) { | ||
390 | m = j*2; | ||
391 | |||
392 | raw = xtal * m / n; | ||
393 | tmp = raw / od[i]; | ||
394 | |||
395 | tmp = (tmp > speed) ? (tmp - speed) : (speed - tmp); | ||
396 | |||
397 | if (tmp < distance) { | ||
398 | distance = tmp; | ||
399 | |||
400 | plcr_m_n_od = (j << CPPCR0_PLLM_LSB) | ||
401 | | (k << CPPCR0_PLLN_LSB) | ||
402 | | (i << CPPCR0_PLLOD_LSB); | ||
403 | |||
404 | if (!distance) { /* Match. */ | ||
405 | return plcr_m_n_od; | ||
406 | } | ||
407 | } | ||
408 | } | ||
409 | } | ||
410 | } | ||
411 | return plcr_m_n_od; | ||
412 | } | ||
413 | |||
414 | /* PLL output clock = EXTAL * NF / (NR * NO) | ||
415 | * | ||
416 | * NF = FD + 2, NR = RD + 2 | ||
417 | * NO = 1 (if OD = 0), NO = 2 (if OD = 1 or 2), NO = 4 (if OD = 3) | ||
418 | */ | ||
419 | static void pll0_init(unsigned int freq) | ||
420 | { | ||
421 | register unsigned int cfcr, plcr1; | ||
422 | int n2FR[9] = { | ||
423 | 0, 0, 1, 2, 3, 0, 4, 0, 5 | ||
424 | }; | ||
425 | |||
426 | /** divisors, | ||
427 | * for jz4760b,I:H:H2:P:M:S. | ||
428 | * DIV should be one of [1, 2, 3, 4, 6, 8] | ||
429 | */ | ||
430 | int div[6] = {1, 4, 4, 4, 4, 4}; | ||
431 | int usbdiv; | ||
432 | |||
433 | /* set ahb **/ | ||
434 | REG32(HARB0_BASE) = 0x00300000; | ||
435 | REG32(0xb3070048) = 0x00000000; | ||
436 | REG32(HARB2_BASE) = 0x00FFFFFF; | ||
437 | |||
438 | cfcr = CPCCR_PCS | | ||
439 | (n2FR[div[0]] << CPCCR_CDIV_LSB) | | ||
440 | (n2FR[div[1]] << CPCCR_HDIV_LSB) | | ||
441 | (n2FR[div[2]] << CPCCR_H2DIV_LSB) | | ||
442 | (n2FR[div[3]] << CPCCR_PDIV_LSB) | | ||
443 | (n2FR[div[4]] << CPCCR_MDIV_LSB) | | ||
444 | (n2FR[div[5]] << CPCCR_SDIV_LSB); | ||
445 | |||
446 | // write REG_DDRC_CTRL 8 times to clear ddr fifo | ||
447 | REG_DDRC_CTRL = 0; | ||
448 | REG_DDRC_CTRL = 0; | ||
449 | REG_DDRC_CTRL = 0; | ||
450 | REG_DDRC_CTRL = 0; | ||
451 | REG_DDRC_CTRL = 0; | ||
452 | REG_DDRC_CTRL = 0; | ||
453 | REG_DDRC_CTRL = 0; | ||
454 | REG_DDRC_CTRL = 0; | ||
455 | |||
456 | if (CFG_EXTAL > 16000000) | ||
457 | cfcr |= CPCCR_ECS; | ||
458 | else | ||
459 | cfcr &= ~CPCCR_ECS; | ||
460 | |||
461 | cfcr &= ~CPCCR_MEM; /* mddr */ | ||
462 | cfcr |= CPCCR_CE; | ||
463 | |||
464 | plcr1 = pll_calc_m_n_od(freq, CFG_EXTAL); | ||
465 | plcr1 |= (0x20 << CPPCR0_PLLST_LSB) /* PLL stable time */ | ||
466 | | CPPCR0_PLLEN; /* enable PLL */ | ||
467 | |||
468 | /* | ||
469 | * Init USB Host clock, pllout2 must be n*48MHz | ||
470 | * For JZ4760b UHC - River. | ||
471 | */ | ||
472 | usbdiv = (cfcr & CPCCR_PCS) ? CPU_FREQ : (CPU_FREQ / 2); | ||
473 | REG_CPM_UHCCDR = usbdiv / 48000000 - 1; | ||
474 | |||
475 | /* init PLL */ | ||
476 | REG_CPM_CPCCR = cfcr; | ||
477 | REG_CPM_CPPCR0 = plcr1; | ||
478 | |||
479 | __cpm_enable_pll_change(); | ||
480 | |||
481 | /*wait for pll output stable ...*/ | ||
482 | while (!(REG_CPM_CPPCR0 & CPPCR0_PLLS)); | ||
483 | |||
484 | REG_CPM_CPPCR0 &= ~CPPCR0_LOCK; | ||
485 | } | ||
486 | |||
487 | void pll1_init(unsigned int freq) | ||
488 | { | ||
489 | register unsigned int plcr2; | ||
490 | |||
491 | /* set CPM_CPCCR_MEM only for ddr1 or ddr2 */ | ||
492 | plcr2 = pll_calc_m_n_od(freq, CFG_EXTAL) | ||
493 | | CPPCR1_PLL1EN; /* enable PLL1 */ | ||
494 | |||
495 | /* init PLL_1 , source clock is extal clock */ | ||
496 | REG_CPM_CPPCR1 = plcr2; | ||
497 | |||
498 | __cpm_enable_pll_change(); | ||
499 | |||
500 | /*wait for pll_1 output stable ...*/ | ||
501 | while (!(REG_CPM_CPPCR1 & CPPCR1_PLL1S)); | ||
502 | |||
503 | REG_CPM_CPPCR1 &= ~CPPCR1_LOCK; | ||
504 | } | ||
505 | |||
506 | static void serial_setbrg(void) | ||
507 | { | ||
508 | volatile u8 *uart_lcr = (volatile u8 *)(CFG_UART_BASE + OFF_LCR); | ||
509 | volatile u8 *uart_dlhr = (volatile u8 *)(CFG_UART_BASE + OFF_DLHR); | ||
510 | volatile u8 *uart_dllr = (volatile u8 *)(CFG_UART_BASE + OFF_DLLR); | ||
511 | volatile u8 *uart_umr = (volatile u8 *)(CFG_UART_BASE + OFF_UMR); | ||
512 | volatile u8 *uart_uacr = (volatile u8 *)(CFG_UART_BASE + OFF_UACR); | ||
513 | u16 baud_div, tmp; | ||
514 | |||
515 | *uart_umr = 16; | ||
516 | *uart_uacr = 0; | ||
517 | baud_div = 13; /* 57600 */ | ||
518 | |||
519 | tmp = *uart_lcr; | ||
520 | tmp |= UARTLCR_DLAB; | ||
521 | *uart_lcr = tmp; | ||
522 | |||
523 | *uart_dlhr = (baud_div >> 8) & 0xff; | ||
524 | *uart_dllr = baud_div & 0xff; | ||
525 | |||
526 | tmp &= ~UARTLCR_DLAB; | ||
527 | *uart_lcr = tmp; | ||
528 | } | ||
529 | |||
530 | int serial_preinit(void) | ||
531 | { | ||
532 | volatile u8 *uart_fcr = (volatile u8 *)(CFG_UART_BASE + OFF_FCR); | ||
533 | volatile u8 *uart_lcr = (volatile u8 *)(CFG_UART_BASE + OFF_LCR); | ||
534 | volatile u8 *uart_ier = (volatile u8 *)(CFG_UART_BASE + OFF_IER); | ||
535 | volatile u8 *uart_sircr = (volatile u8 *)(CFG_UART_BASE + OFF_SIRCR); | ||
536 | |||
537 | __gpio_as_uart1(); | ||
538 | __cpm_start_uart1(); | ||
539 | |||
540 | /* Disable port interrupts while changing hardware */ | ||
541 | *uart_ier = 0; | ||
542 | |||
543 | /* Disable UART unit function */ | ||
544 | *uart_fcr = ~UARTFCR_UUE; | ||
545 | |||
546 | /* Set both receiver and transmitter in UART mode (not SIR) */ | ||
547 | *uart_sircr = ~(SIRCR_RSIRE | SIRCR_TSIRE); | ||
548 | |||
549 | /* Set databits, stopbits and parity. (8-bit data, 1 stopbit, no parity) */ | ||
550 | *uart_lcr = UARTLCR_WLEN_8 | UARTLCR_STOP1; | ||
551 | |||
552 | /* Set baud rate */ | ||
553 | serial_setbrg(); | ||
554 | |||
555 | /* Enable UART unit, enable and clear FIFO */ | ||
556 | *uart_fcr = UARTFCR_UUE | UARTFCR_FE | UARTFCR_TFLS | UARTFCR_RFLS; | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | void usb_preinit(void) | ||
562 | { | ||
563 | /* Clear ECS bit of CPCCR, 0:clock source is EXCLK, 1:clock source is EXCLK/2 */ | ||
564 | REG_CPM_CPCCR &= ~CPCCR_ECS; | ||
565 | |||
566 | /* Clear all bits of USBCDR, 0:OTG clock source is pin EXCLK, PLL0 output, divider = 1:12MHZ */ | ||
567 | REG_CPM_USBCDR = 0; | ||
568 | |||
569 | /* Set CE bit of CPCCR, it means frequence is changed immediately */ | ||
570 | REG_CPM_CPCCR |= CPCCR_CE; | ||
571 | |||
572 | udelay(3); | ||
573 | |||
574 | /* Clear OTG bit of CLKGR0, 0:device can be accessed */ | ||
575 | REG_CPM_CLKGR0 &= ~CLKGR0_OTG; | ||
576 | |||
577 | /* fil */ | ||
578 | REG_CPM_USBVBFIL = 0x80; | ||
579 | |||
580 | /* rdt */ | ||
581 | REG_CPM_USBRDT = (600 * (CPU_FREQ / 1000000)) / 1000; | ||
582 | |||
583 | /* rdt - filload_en */ | ||
584 | REG_CPM_USBRDT |= (1 << 25); | ||
585 | |||
586 | /* TXRISETUNE & TXVREFTUNE. */ | ||
587 | REG_CPM_USBPCR &= ~0x3f; | ||
588 | REG_CPM_USBPCR |= 0x35; | ||
589 | |||
590 | /* enable tx pre-emphasis */ | ||
591 | REG_CPM_USBPCR |= 0x40; | ||
592 | |||
593 | /* most DC leave of tx */ | ||
594 | REG_CPM_USBPCR |= 0xf; | ||
595 | |||
596 | /* Device Mode. */ | ||
597 | REG_CPM_USBPCR &= ~(1 << 31); | ||
598 | REG_CPM_USBPCR |= USBPCR_VBUSVLDEXT; | ||
599 | |||
600 | /* phy reset */ | ||
601 | REG_CPM_USBPCR |= USBPCR_POR; | ||
602 | udelay(30); | ||
603 | REG_CPM_USBPCR &= ~USBPCR_POR; | ||
604 | udelay(300); | ||
605 | |||
606 | /* Enable the USB PHY */ | ||
607 | REG_CPM_OPCR |= OPCR_OTGPHY_ENABLE; | ||
608 | |||
609 | /* Wait PHY Clock Stable. */ | ||
610 | udelay(300); | ||
611 | } | ||
612 | |||
613 | void dma_preinit(void) | ||
614 | { | ||
615 | __cpm_start_mdma(); | ||
616 | __cpm_start_dmac(); | ||
617 | |||
618 | REG_MDMAC_DMACKES = 0x1; | ||
619 | |||
620 | REG_DMAC_DMACR(DMA_AIC_TX_CHANNEL) = DMAC_DMACR_DMAE | DMAC_DMACR_FAIC; | ||
621 | REG_DMAC_DMACR(DMA_SD_RX_CHANNEL) = DMAC_DMACR_DMAE | DMAC_DMACR_FMSC; | ||
622 | REG_DMAC_DMACR(DMA_SD_TX_CHANNEL) = DMAC_DMACR_DMAE | DMAC_DMACR_FMSC; | ||
623 | } | ||
624 | |||
625 | /* Gets called *before* main */ | ||
626 | void ICODE_ATTR system_main(void) | ||
627 | { | ||
628 | int i; | ||
629 | |||
630 | __dcache_writeback_all(); | ||
631 | __icache_invalidate_all(); | ||
632 | |||
633 | write_c0_status(1 << 28 | 1 << 10 ); /* Enable CP | Mask interrupt 2 */ | ||
634 | |||
635 | /* Disable all interrupts */ | ||
636 | for(i=0; i<IRQ_INTC_MAX; i++) | ||
637 | dis_irq(i); | ||
638 | |||
639 | mmu_init(); | ||
640 | |||
641 | pll0_init(CPU_FREQ); | ||
642 | pll1_init(CPU_FREQ); | ||
643 | |||
644 | serial_preinit(); | ||
645 | usb_preinit(); | ||
646 | dma_preinit(); | ||
647 | |||
648 | /* Enable interrupts at core level */ | ||
649 | enable_interrupt(); | ||
650 | } | ||
651 | |||
652 | void system_reboot(void) | ||
653 | { | ||
654 | REG_WDT_WCSR = WCSR_PRESCALE4 | WCSR_CLKIN_EXT; | ||
655 | REG_WDT_WCNT = 0; | ||
656 | REG_WDT_WDR = JZ_EXTAL/1000; /* reset after 4ms */ | ||
657 | REG_TCU_TSCR = TSCR_WDT; /* enable wdt clock */ | ||
658 | REG_WDT_WCER = WCER_TCEN; /* wdt start */ | ||
659 | while (1); | ||
660 | } | ||
661 | |||
662 | void system_exception_wait(void) | ||
663 | { | ||
664 | /* check for power button without including any .h file */ | ||
665 | while(1) | ||
666 | { | ||
667 | if( (~REG_GPIO_PXPIN(0)) & (1 << 30) ) | ||
668 | return; | ||
669 | asm volatile("nop"); | ||
670 | } | ||
671 | } | ||
672 | |||
673 | void power_off(void) | ||
674 | { | ||
675 | REG_CPM_RSR = 0x0; | ||
676 | |||
677 | /* Set minimum wakeup_n pin low-level assertion time for wakeup: 100ms */ | ||
678 | rtc_write_reg(RTC_HWFCR, HWFCR_WAIT_TIME(1000)); | ||
679 | |||
680 | /* Set reset pin low-level assertion time after wakeup: must > 60ms */ | ||
681 | rtc_write_reg(RTC_HRCR, HRCR_WAIT_TIME(60)); | ||
682 | |||
683 | /* clear wakeup status register */ | ||
684 | rtc_write_reg(RTC_HWRSR, 0x0); | ||
685 | |||
686 | /* set wake up valid level as low */ | ||
687 | rtc_write_reg(RTC_HWCR,0x8); | ||
688 | |||
689 | /* Put CPU to hibernate mode */ | ||
690 | rtc_write_reg(RTC_HCR, HCR_PD); | ||
691 | |||
692 | while (1); | ||
693 | } | ||
694 | |||
695 | void system_init(void) | ||
696 | { | ||
697 | } | ||
698 | |||
699 | int system_memory_guard(int newmode) | ||
700 | { | ||
701 | (void)newmode; | ||
702 | return 0; | ||
703 | } | ||
704 | |||
705 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
706 | void set_cpu_frequency(long frequency) | ||
707 | { | ||
708 | serial_putsf("set_cpu_frequency: %d\n", frequency); | ||
709 | } | ||
710 | #endif | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/system-target.h b/firmware/target/mips/ingenic_jz47xx/system-target.h index 1c2e7d7173..9720d3a80c 100644 --- a/firmware/target/mips/ingenic_jz47xx/system-target.h +++ b/firmware/target/mips/ingenic_jz47xx/system-target.h | |||
@@ -25,7 +25,7 @@ | |||
25 | #include <inttypes.h> | 25 | #include <inttypes.h> |
26 | 26 | ||
27 | #include "config.h" | 27 | #include "config.h" |
28 | #include "jz4740.h" | 28 | #include "cpu.h" |
29 | #include "mipsregs.h" | 29 | #include "mipsregs.h" |
30 | 30 | ||
31 | #define CACHE_SIZE 16*1024 | 31 | #define CACHE_SIZE 16*1024 |
@@ -35,6 +35,8 @@ | |||
35 | /* no optimized byteswap functions implemented for mips, yet */ | 35 | /* no optimized byteswap functions implemented for mips, yet */ |
36 | #define NEED_GENERIC_BYTESWAPS | 36 | #define NEED_GENERIC_BYTESWAPS |
37 | 37 | ||
38 | #define STORAGE_WANTS_ALIGN | ||
39 | |||
38 | /* This one returns the old status */ | 40 | /* This one returns the old status */ |
39 | static inline int set_interrupt_status(int status, int mask) | 41 | static inline int set_interrupt_status(int status, int mask) |
40 | { | 42 | { |
@@ -86,10 +88,18 @@ void mdelay(unsigned int msec); | |||
86 | void dma_enable(void); | 88 | void dma_enable(void); |
87 | void dma_disable(void); | 89 | void dma_disable(void); |
88 | 90 | ||
91 | #if CONFIG_CPU == JZ4732 | ||
89 | #define DMA_AIC_TX_CHANNEL 0 | 92 | #define DMA_AIC_TX_CHANNEL 0 |
90 | #define DMA_NAND_CHANNEL 1 | 93 | #define DMA_NAND_CHANNEL 1 |
91 | #define DMA_USB_CHANNEL 2 | 94 | #define DMA_USB_CHANNEL 2 |
92 | #define DMA_LCD_CHANNEL 3 | 95 | #define DMA_LCD_CHANNEL 3 |
96 | #elif CONFIG_CPU == JZ4760B | ||
97 | #define DMA_AIC_TX_CHANNEL 0 | ||
98 | #define DMA_NAND_CHANNEL 1 | ||
99 | #define DMA_USB_CHANNEL 2 | ||
100 | #define DMA_SD_RX_CHANNEL 3 | ||
101 | #define DMA_SD_TX_CHANNEL 4 | ||
102 | #endif | ||
93 | 103 | ||
94 | #define XDMA_CALLBACK(n) DMA ## n | 104 | #define XDMA_CALLBACK(n) DMA ## n |
95 | #define DMA_CALLBACK(n) XDMA_CALLBACK(n) | 105 | #define DMA_CALLBACK(n) XDMA_CALLBACK(n) |
@@ -103,7 +113,7 @@ void dma_disable(void); | |||
103 | */ | 113 | */ |
104 | static inline void core_sleep(void) | 114 | static inline void core_sleep(void) |
105 | { | 115 | { |
106 | #if CONFIG_CPU == JZ4732 | 116 | #if CONFIG_CPU == JZ4732 || CONFIG_CPU == JZ4760B |
107 | __cpm_idle_mode(); | 117 | __cpm_idle_mode(); |
108 | #endif | 118 | #endif |
109 | asm volatile(".set mips32r2 \n" | 119 | asm volatile(".set mips32r2 \n" |
diff --git a/firmware/target/mips/ingenic_jz47xx/timer-jz4760.c b/firmware/target/mips/ingenic_jz47xx/timer-jz4760.c new file mode 100644 index 0000000000..0f16825b34 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/timer-jz4760.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | #include "cpu.h" | ||
24 | #include "system.h" | ||
25 | #include "timer.h" | ||
26 | |||
27 | /* Interrupt handler */ | ||
28 | void TCU1(void) | ||
29 | { | ||
30 | __tcu_clear_full_match_flag(5); | ||
31 | |||
32 | if (pfn_timer != NULL) | ||
33 | pfn_timer(); | ||
34 | } | ||
35 | |||
36 | bool timer_set(long cycles, bool start) | ||
37 | { | ||
38 | unsigned int divider = cycles, prescaler_bit = 0, prescaler = 1, old_irq; | ||
39 | |||
40 | if(cycles < 1) | ||
41 | return false; | ||
42 | |||
43 | if(start && pfn_unregister != NULL) | ||
44 | { | ||
45 | pfn_unregister(); | ||
46 | pfn_unregister = NULL; | ||
47 | } | ||
48 | |||
49 | /* Increase prescale values starting from 0 to make the cycle count fit */ | ||
50 | while(divider > 65535 && prescaler <= 1024) | ||
51 | { | ||
52 | prescaler <<= 2; /* 1, 4, 16, 64, 256, 1024 */ | ||
53 | prescaler_bit++; | ||
54 | divider = cycles / prescaler; | ||
55 | } | ||
56 | |||
57 | old_irq = disable_irq_save(); | ||
58 | |||
59 | __tcu_stop_counter(5); | ||
60 | if(start) | ||
61 | { | ||
62 | __tcu_disable_pwm_output(5); | ||
63 | |||
64 | __tcu_mask_half_match_irq(5); | ||
65 | __tcu_unmask_full_match_irq(5); | ||
66 | |||
67 | /* EXTAL clock = CFG_EXTAL (12Mhz in most targets) */ | ||
68 | __tcu_select_extalclk(5); | ||
69 | } | ||
70 | |||
71 | REG_TCU_TCSR(5) = (REG_TCU_TCSR(5) & ~TCSR_PRESCALE_MASK) | (prescaler_bit << TCSR_PRESCALE_LSB); | ||
72 | REG_TCU_TCNT(5) = 0; | ||
73 | REG_TCU_TDHR(5) = 0; | ||
74 | REG_TCU_TDFR(5) = divider; | ||
75 | |||
76 | __tcu_clear_full_match_flag(5); | ||
77 | |||
78 | if(start) | ||
79 | { | ||
80 | system_enable_irq(IRQ_TCU1); | ||
81 | __tcu_start_counter(5); | ||
82 | } | ||
83 | |||
84 | restore_irq(old_irq); | ||
85 | |||
86 | return true; | ||
87 | } | ||
88 | |||
89 | bool timer_start(void) | ||
90 | { | ||
91 | __tcu_start_counter(5); | ||
92 | |||
93 | return true; | ||
94 | } | ||
95 | |||
96 | void timer_stop(void) | ||
97 | { | ||
98 | unsigned int old_irq = disable_irq_save(); | ||
99 | __tcu_stop_counter(5); | ||
100 | restore_irq(old_irq); | ||
101 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c new file mode 100644 index 0000000000..bc2158fb6f --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/usb-jz4760.c | |||
@@ -0,0 +1,870 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | /*#define LOGF_ENABLE*/ | ||
24 | #include "logf.h" | ||
25 | #include "system.h" | ||
26 | #include "usb_ch9.h" | ||
27 | #include "usb_drv.h" | ||
28 | #include "usb_core.h" | ||
29 | #include "cpu.h" | ||
30 | #include "thread.h" | ||
31 | |||
32 | #define PIN_USB_DET (32*4+19) | ||
33 | #define IRQ_USB_DET GPIO_IRQ(PIN_USB_DET) | ||
34 | #define GPIO_USB_DET GPIO147 | ||
35 | |||
36 | #define PIN_USB_DRVVBUS (32*4+10) | ||
37 | #define PIN_USB_OTG_ID (32*3+7) | ||
38 | |||
39 | #define EP_BUF_LEFT(ep) ((ep)->length - (ep)->sent) | ||
40 | #define EP_PTR(ep) ((void*)((unsigned int)(ep)->buf + (ep)->sent)) | ||
41 | #define EP_NUMBER(ep) (((int)(ep) - (int)&endpoints[0])/sizeof(struct usb_endpoint)) | ||
42 | #define EP_NUMBER2(ep) (EP_NUMBER((ep))/2) | ||
43 | #define TOTAL_EP() (sizeof(endpoints)/sizeof(struct usb_endpoint)) | ||
44 | #define EP_IS_IN(ep) (EP_NUMBER((ep))%2) | ||
45 | |||
46 | enum ep_type | ||
47 | { | ||
48 | ep_control, | ||
49 | ep_bulk, | ||
50 | ep_interrupt, | ||
51 | ep_isochronous | ||
52 | }; | ||
53 | |||
54 | struct usb_endpoint | ||
55 | { | ||
56 | void *buf; | ||
57 | size_t length; | ||
58 | union | ||
59 | { | ||
60 | size_t sent; | ||
61 | size_t received; | ||
62 | }; | ||
63 | bool busy; | ||
64 | |||
65 | const enum ep_type type; | ||
66 | const bool use_dma; | ||
67 | |||
68 | const long fifo_addr; | ||
69 | unsigned short fifo_size; | ||
70 | |||
71 | bool wait; | ||
72 | struct semaphore complete; | ||
73 | }; | ||
74 | |||
75 | #define EP_INIT(_type, _fifo_addr, _fifo_size, _buf, _use_dma) \ | ||
76 | { .type = (_type), .fifo_addr = (_fifo_addr), .fifo_size = (_fifo_size), \ | ||
77 | .buf = (_buf), .use_dma = (_use_dma), .length = 0, .busy = false, .wait = false } | ||
78 | |||
79 | static unsigned char ep0_rx_buf[64]; | ||
80 | static struct usb_endpoint endpoints[] = | ||
81 | { | ||
82 | EP_INIT(ep_control, USB_FIFO_EP(0), 64, NULL, false), | ||
83 | EP_INIT(ep_control, USB_FIFO_EP(0), 64, &ep0_rx_buf, false), | ||
84 | EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL, false), | ||
85 | EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL, false), | ||
86 | EP_INIT(ep_interrupt, USB_FIFO_EP(2), 64, NULL, false), | ||
87 | EP_INIT(ep_interrupt, USB_FIFO_EP(2), 64, NULL, false), | ||
88 | }; | ||
89 | |||
90 | static inline void select_endpoint(int ep) | ||
91 | { | ||
92 | REG_USB_INDEX = ep; | ||
93 | } | ||
94 | |||
95 | static void readFIFO(struct usb_endpoint *ep, unsigned int size) | ||
96 | { | ||
97 | logf("%s(EP%d, %d)", __func__, EP_NUMBER2(ep), size); | ||
98 | |||
99 | register unsigned char *ptr = (unsigned char*)EP_PTR(ep); | ||
100 | register unsigned int *ptr32 = (unsigned int*)ptr; | ||
101 | register unsigned int s = size >> 2; | ||
102 | register unsigned int x; | ||
103 | |||
104 | if(size > 0) | ||
105 | { | ||
106 | if( ((unsigned int)ptr & 3) == 0 ) | ||
107 | { | ||
108 | while(s--) | ||
109 | *ptr32++ = REG32(ep->fifo_addr); | ||
110 | |||
111 | ptr = (unsigned char*)ptr32; | ||
112 | } | ||
113 | else | ||
114 | { | ||
115 | while(s--) | ||
116 | { | ||
117 | x = REG32(ep->fifo_addr); | ||
118 | *ptr++ = x & 0xFF; x >>= 8; | ||
119 | *ptr++ = x & 0xFF; x >>= 8; | ||
120 | *ptr++ = x & 0xFF; x >>= 8; | ||
121 | *ptr++ = x; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | s = size & 3; | ||
126 | while(s--) | ||
127 | *ptr++ = REG8(ep->fifo_addr); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | static void writeFIFO(struct usb_endpoint *ep, size_t size) | ||
132 | { | ||
133 | logf("%s(EP%d, %d)", __func__, EP_NUMBER2(ep), size); | ||
134 | |||
135 | register unsigned int *d32 = (unsigned int *)EP_PTR(ep); | ||
136 | register size_t s = size >> 2; | ||
137 | |||
138 | if(size > 0) | ||
139 | { | ||
140 | while (s--) | ||
141 | REG32(ep->fifo_addr) = *d32++; | ||
142 | |||
143 | if( (s = size & 3) ) | ||
144 | { | ||
145 | register unsigned char *d8 = (unsigned char *)d32; | ||
146 | while (s--) | ||
147 | REG8(ep->fifo_addr) = *d8++; | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | |||
152 | static void flushFIFO(struct usb_endpoint *ep) | ||
153 | { | ||
154 | logf("%s(%d)", __func__, EP_NUMBER(ep)); | ||
155 | |||
156 | switch (ep->type) | ||
157 | { | ||
158 | case ep_control: | ||
159 | break; | ||
160 | |||
161 | case ep_bulk: | ||
162 | case ep_interrupt: | ||
163 | case ep_isochronous: | ||
164 | if(EP_IS_IN(ep)) | ||
165 | REG_USB_INCSR |= (USB_INCSR_FF | USB_INCSR_CDT); | ||
166 | else | ||
167 | REG_USB_OUTCSR |= (USB_OUTCSR_FF | USB_OUTCSR_CDT); | ||
168 | break; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static inline void ep_transfer_completed(struct usb_endpoint* ep) | ||
173 | { | ||
174 | ep->sent = 0; | ||
175 | ep->length = 0; | ||
176 | ep->buf = NULL; | ||
177 | ep->busy = false; | ||
178 | if(ep->wait) | ||
179 | semaphore_release(&ep->complete); | ||
180 | } | ||
181 | |||
182 | static void EP0_send(void) | ||
183 | { | ||
184 | struct usb_endpoint* ep = &endpoints[0]; | ||
185 | unsigned int length; | ||
186 | unsigned char csr0; | ||
187 | |||
188 | select_endpoint(0); | ||
189 | csr0 = REG_USB_CSR0; | ||
190 | |||
191 | if(ep->sent == 0) | ||
192 | length = MIN(ep->length, ep->fifo_size); | ||
193 | else | ||
194 | length = MIN(EP_BUF_LEFT(ep), ep->fifo_size); | ||
195 | |||
196 | writeFIFO(ep, length); | ||
197 | ep->sent += length; | ||
198 | |||
199 | if(ep->sent >= ep->length) | ||
200 | { | ||
201 | REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY | USB_CSR0_DATAEND); /* Set data end! */ | ||
202 | usb_core_transfer_complete(0, USB_DIR_IN, 0, ep->sent); | ||
203 | ep_transfer_completed(ep); | ||
204 | } | ||
205 | else | ||
206 | REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY); | ||
207 | } | ||
208 | |||
209 | static void EP0_handler(void) | ||
210 | { | ||
211 | logf("%s()", __func__); | ||
212 | |||
213 | unsigned char csr0; | ||
214 | struct usb_endpoint *ep_send = &endpoints[0]; | ||
215 | struct usb_endpoint *ep_recv = &endpoints[1]; | ||
216 | |||
217 | /* Read CSR0 */ | ||
218 | select_endpoint(0); | ||
219 | csr0 = REG_USB_CSR0; | ||
220 | |||
221 | /* Check for SentStall: | ||
222 | This bit is set when a STALL handshake is transmitted. The CPU should clear this bit. | ||
223 | */ | ||
224 | if(csr0 & USB_CSR0_SENTSTALL) | ||
225 | { | ||
226 | REG_USB_CSR0 = csr0 & ~USB_CSR0_SENTSTALL; | ||
227 | return; | ||
228 | } | ||
229 | |||
230 | /* Check for SetupEnd: | ||
231 | This bit will be set when a control transaction ends before the DataEnd bit has been set. | ||
232 | An interrupt will be generated and the FIFO flushed at this time. | ||
233 | The bit is cleared by the CPU writing a 1 to the ServicedSetupEnd bit. | ||
234 | */ | ||
235 | if(csr0 & USB_CSR0_SETUPEND) | ||
236 | { | ||
237 | REG_USB_CSR0 = csr0 | USB_CSR0_SVDSETUPEND; | ||
238 | return; | ||
239 | } | ||
240 | |||
241 | /* Call relevant routines for endpoint 0 state */ | ||
242 | if(ep_send->busy) | ||
243 | EP0_send(); | ||
244 | else if(csr0 & USB_CSR0_OUTPKTRDY) /* There is a packet in the fifo */ | ||
245 | { | ||
246 | readFIFO(ep_recv, REG_USB_COUNT0); | ||
247 | REG_USB_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */ | ||
248 | usb_core_control_request((struct usb_ctrlrequest*)ep_recv->buf); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | static void EPIN_handler(unsigned int endpoint) | ||
253 | { | ||
254 | struct usb_endpoint* ep = &endpoints[endpoint*2]; | ||
255 | unsigned int length, csr; | ||
256 | |||
257 | select_endpoint(endpoint); | ||
258 | csr = REG_USB_INCSR; | ||
259 | logf("%s(%d): 0x%x", __func__, endpoint, csr); | ||
260 | |||
261 | if(!ep->busy) | ||
262 | { | ||
263 | logf("Entered EPIN handler without work!"); | ||
264 | return; | ||
265 | } | ||
266 | |||
267 | if(csr & USB_INCSR_SENTSTALL) | ||
268 | { | ||
269 | REG_USB_INCSR = csr & ~USB_INCSR_SENTSTALL; | ||
270 | return; | ||
271 | } | ||
272 | |||
273 | if(ep->use_dma) | ||
274 | return; | ||
275 | |||
276 | if(csr & USB_INCSR_FFNOTEMPT) | ||
277 | { | ||
278 | logf("FIFO is not empty! 0x%x", csr); | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | logf("EP%d: %d -> %d", endpoint, ep->sent, ep->length); | ||
283 | |||
284 | if(ep->sent == 0) | ||
285 | length = MIN(ep->length, ep->fifo_size); | ||
286 | else | ||
287 | length = MIN(EP_BUF_LEFT(ep), ep->fifo_size); | ||
288 | |||
289 | writeFIFO(ep, length); | ||
290 | REG_USB_INCSR = csr | USB_INCSR_INPKTRDY; | ||
291 | ep->sent += length; | ||
292 | |||
293 | if(ep->sent >= ep->length) | ||
294 | { | ||
295 | usb_core_transfer_complete(endpoint, USB_DIR_IN, 0, ep->sent); | ||
296 | ep_transfer_completed(ep); | ||
297 | logf("sent complete"); | ||
298 | } | ||
299 | } | ||
300 | |||
301 | static void EPOUT_handler(unsigned int endpoint) | ||
302 | { | ||
303 | struct usb_endpoint* ep = &endpoints[endpoint*2+1]; | ||
304 | unsigned int size, csr; | ||
305 | |||
306 | if(!ep->busy) | ||
307 | { | ||
308 | logf("Entered EPOUT handler without work!"); | ||
309 | return; | ||
310 | } | ||
311 | |||
312 | select_endpoint(endpoint); | ||
313 | while((csr = REG_USB_OUTCSR) & (USB_OUTCSR_SENTSTALL|USB_OUTCSR_OUTPKTRDY)) | ||
314 | { | ||
315 | logf("%s(%d): 0x%x", __func__, endpoint, csr); | ||
316 | if(csr & USB_OUTCSR_SENTSTALL) | ||
317 | { | ||
318 | logf("stall sent, flushing fifo.."); | ||
319 | flushFIFO(ep); | ||
320 | REG_USB_OUTCSR = csr & ~USB_OUTCSR_SENTSTALL; | ||
321 | return; | ||
322 | } | ||
323 | |||
324 | if(ep->use_dma) | ||
325 | return; | ||
326 | |||
327 | if(csr & USB_OUTCSR_OUTPKTRDY) /* There is a packet in the fifo */ | ||
328 | { | ||
329 | size = REG_USB_OUTCOUNT; | ||
330 | |||
331 | readFIFO(ep, size); | ||
332 | ep->received += size; | ||
333 | |||
334 | /*if(csr & USB_OUTCSR_FFFULL) | ||
335 | csr &= ~USB_OUTCSR_FFFULL;*/ | ||
336 | |||
337 | REG_USB_OUTCSR = csr & ~USB_OUTCSR_OUTPKTRDY; | ||
338 | |||
339 | logf("received: %d max length: %d", ep->received, ep->length); | ||
340 | |||
341 | if(size < ep->fifo_size || ep->received >= ep->length) | ||
342 | { | ||
343 | usb_core_transfer_complete(endpoint, USB_DIR_OUT, 0, ep->received); | ||
344 | ep_transfer_completed(ep); | ||
345 | logf("receive transfer_complete"); | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | |||
351 | static void EPDMA_handler(int number) | ||
352 | { | ||
353 | int endpoint = -1; | ||
354 | unsigned int size = 0; | ||
355 | |||
356 | if(number == USB_INTR_DMA_BULKIN) | ||
357 | endpoint = (REG_USB_CNTL(0) >> 4) & 0xF; | ||
358 | else if(number == USB_INTR_DMA_BULKOUT) | ||
359 | endpoint = (REG_USB_CNTL(1) >> 4) & 0xF; | ||
360 | |||
361 | struct usb_endpoint* ep = &endpoints[endpoint]; | ||
362 | logf("DMA_BULK%d %d", number, endpoint); | ||
363 | |||
364 | if(number == USB_INTR_DMA_BULKIN) | ||
365 | size = (unsigned int)ep->buf - REG_USB_ADDR(0); | ||
366 | else if(number == USB_INTR_DMA_BULKOUT) | ||
367 | size = (unsigned int)ep->buf - REG_USB_ADDR(1); | ||
368 | |||
369 | if(number == USB_INTR_DMA_BULKOUT) | ||
370 | { | ||
371 | /* Disable DMA */ | ||
372 | REG_USB_CNTL(1) = 0; | ||
373 | |||
374 | __dcache_invalidate_all(); | ||
375 | |||
376 | select_endpoint(endpoint); | ||
377 | /* Read out last packet manually */ | ||
378 | unsigned int lpack_size = REG_USB_OUTCOUNT; | ||
379 | if(lpack_size > 0) | ||
380 | { | ||
381 | ep->buf += ep->length - lpack_size; | ||
382 | readFIFO(ep, lpack_size); | ||
383 | REG_USB_OUTCSR &= ~USB_OUTCSR_OUTPKTRDY; | ||
384 | } | ||
385 | } | ||
386 | else if(number == USB_INTR_DMA_BULKIN && size % ep->fifo_size) | ||
387 | { | ||
388 | /* If the last packet is less than MAXP, set INPKTRDY manually */ | ||
389 | REG_USB_INCSR |= USB_INCSR_INPKTRDY; | ||
390 | } | ||
391 | |||
392 | usb_core_transfer_complete(endpoint, EP_IS_IN(ep) ? USB_DIR_IN : USB_DIR_OUT, | ||
393 | 0, ep->length); | ||
394 | ep_transfer_completed(ep); | ||
395 | } | ||
396 | |||
397 | static void setup_endpoint(struct usb_endpoint *ep) | ||
398 | { | ||
399 | int csr, csrh; | ||
400 | |||
401 | select_endpoint(EP_NUMBER2(ep)); | ||
402 | |||
403 | ep->busy = false; | ||
404 | ep->wait = false; | ||
405 | ep->sent = 0; | ||
406 | ep->length = 0; | ||
407 | |||
408 | if(ep->type == ep_bulk) | ||
409 | { | ||
410 | if(REG_USB_POWER & USB_POWER_HSMODE) | ||
411 | ep->fifo_size = 512; | ||
412 | else | ||
413 | ep->fifo_size = 64; | ||
414 | } | ||
415 | |||
416 | if(EP_IS_IN(ep)) | ||
417 | { | ||
418 | csr = (USB_INCSR_FF | USB_INCSR_CDT); | ||
419 | csrh = USB_INCSRH_MODE; | ||
420 | |||
421 | if(ep->use_dma) | ||
422 | csrh |= (USB_INCSRH_DMAREQENAB | USB_INCSRH_AUTOSET | USB_INCSRH_DMAREQMODE); | ||
423 | |||
424 | if(ep->type == ep_interrupt) | ||
425 | csrh |= USB_INCSRH_FRCDATATOG; | ||
426 | |||
427 | REG_USB_INMAXP = ep->fifo_size; | ||
428 | REG_USB_INCSR = csr; | ||
429 | REG_USB_INCSRH = csrh; | ||
430 | REG_USB_INTRINE |= USB_INTR_EP(EP_NUMBER2(ep)); | ||
431 | } | ||
432 | else | ||
433 | { | ||
434 | csr = (USB_OUTCSR_FF | USB_OUTCSR_CDT); | ||
435 | csrh = 0; | ||
436 | |||
437 | if(ep->type == ep_interrupt) | ||
438 | csrh |= USB_OUTCSRH_DNYT; | ||
439 | |||
440 | if(ep->use_dma) | ||
441 | csrh |= (USB_OUTCSRH_DMAREQENAB | USB_OUTCSRH_AUTOCLR | USB_OUTCSRH_DMAREQMODE); | ||
442 | |||
443 | REG_USB_OUTMAXP = ep->fifo_size; | ||
444 | REG_USB_OUTCSR = csr; | ||
445 | REG_USB_OUTCSRH = csrh; | ||
446 | REG_USB_INTROUTE |= USB_INTR_EP(EP_NUMBER2(ep)); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | static void udc_reset(void) | ||
451 | { | ||
452 | /* From the datasheet: | ||
453 | |||
454 | When a reset condition is detected on the USB, the controller performs the following actions: | ||
455 | * Sets FAddr to 0. | ||
456 | * Sets Index to 0. | ||
457 | * Flushes all endpoint FIFOs. | ||
458 | * Clears all control/status registers. | ||
459 | * Enables all endpoint interrupts. | ||
460 | * Generates a Reset interrupt. | ||
461 | */ | ||
462 | |||
463 | logf("%s()", __func__); | ||
464 | |||
465 | unsigned int i; | ||
466 | |||
467 | REG_USB_FADDR = 0; | ||
468 | REG_USB_INDEX = 0; | ||
469 | |||
470 | /* Disable interrupts */ | ||
471 | REG_USB_INTRINE = 0; | ||
472 | REG_USB_INTROUTE = 0; | ||
473 | REG_USB_INTRUSBE = 0; | ||
474 | |||
475 | /* Disable DMA */ | ||
476 | REG_USB_CNTL(0) = 0; | ||
477 | REG_USB_CNTL(1) = 0; | ||
478 | |||
479 | /* High speed, softconnect */ | ||
480 | REG_USB_POWER = (USB_POWER_SOFTCONN | USB_POWER_HSENAB); | ||
481 | |||
482 | /* Reset EP0 */ | ||
483 | select_endpoint(0); | ||
484 | REG_USB_CSR0 = (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_SVDSETUPEND | USB_CSR0_FLUSHFIFO); | ||
485 | |||
486 | /* Reset other endpoints */ | ||
487 | for(i=2; i<TOTAL_EP(); i++) | ||
488 | setup_endpoint(&endpoints[i]); | ||
489 | |||
490 | /* Enable interrupts */ | ||
491 | REG_USB_INTRINE |= USB_INTR_EP(0); | ||
492 | REG_USB_INTRUSBE |= USB_INTR_RESET; | ||
493 | |||
494 | usb_core_bus_reset(); | ||
495 | } | ||
496 | |||
497 | /* Interrupt handler */ | ||
498 | void OTG(void) | ||
499 | { | ||
500 | /* Read interrupt registers */ | ||
501 | unsigned char intrUSB = REG_USB_INTRUSB & 0x07; /* Mask SOF */ | ||
502 | unsigned short intrIn = REG_USB_INTRIN; | ||
503 | unsigned short intrOut = REG_USB_INTROUT; | ||
504 | unsigned char intrDMA = REG_USB_INTR; | ||
505 | |||
506 | logf("%x %x %x %x", intrUSB, intrIn, intrOut, intrDMA); | ||
507 | |||
508 | /* EPIN & EPOUT are all handled in DMA */ | ||
509 | if(intrIn & USB_INTR_EP(0)) | ||
510 | EP0_handler(); | ||
511 | if(intrIn & USB_INTR_EP(1)) | ||
512 | EPIN_handler(1); | ||
513 | if(intrIn & USB_INTR_EP(2)) | ||
514 | EPIN_handler(2); | ||
515 | if(intrOut & USB_INTR_EP(1)) | ||
516 | EPOUT_handler(1); | ||
517 | if(intrOut & USB_INTR_EP(2)) | ||
518 | EPOUT_handler(2); | ||
519 | if(intrUSB & USB_INTR_RESET) | ||
520 | udc_reset(); | ||
521 | if(intrUSB & USB_INTR_SUSPEND) | ||
522 | logf("USB suspend"); | ||
523 | if(intrUSB & USB_INTR_RESUME) | ||
524 | logf("USB resume"); | ||
525 | if(intrDMA & USB_INTR_DMA_BULKIN) | ||
526 | EPDMA_handler(USB_INTR_DMA_BULKIN); | ||
527 | if(intrDMA & USB_INTR_DMA_BULKOUT) | ||
528 | EPDMA_handler(USB_INTR_DMA_BULKOUT); | ||
529 | } | ||
530 | |||
531 | bool usb_drv_stalled(int endpoint, bool in) | ||
532 | { | ||
533 | endpoint &= 0x7F; | ||
534 | |||
535 | logf("%s(%d, %s)", __func__, endpoint, in?"IN":"OUT"); | ||
536 | |||
537 | select_endpoint(endpoint); | ||
538 | |||
539 | if(endpoint == EP_CONTROL) | ||
540 | return (REG_USB_CSR0 & USB_CSR0_SENDSTALL) != 0; | ||
541 | else | ||
542 | { | ||
543 | if(in) | ||
544 | return (REG_USB_INCSR & USB_INCSR_SENDSTALL) != 0; | ||
545 | else | ||
546 | return (REG_USB_OUTCSR & USB_OUTCSR_SENDSTALL) != 0; | ||
547 | } | ||
548 | } | ||
549 | |||
550 | void usb_drv_stall(int endpoint, bool stall, bool in) | ||
551 | { | ||
552 | endpoint &= 0x7F; | ||
553 | |||
554 | logf("%s(%d,%s,%s)", __func__, endpoint, stall?"Y":"N", in?"IN":"OUT"); | ||
555 | |||
556 | select_endpoint(endpoint); | ||
557 | |||
558 | if(endpoint == EP_CONTROL) | ||
559 | { | ||
560 | if(stall) | ||
561 | REG_USB_CSR0 |= USB_CSR0_SENDSTALL; | ||
562 | else | ||
563 | REG_USB_CSR0 &= ~USB_CSR0_SENDSTALL; | ||
564 | } | ||
565 | else | ||
566 | { | ||
567 | if(in) | ||
568 | { | ||
569 | if(stall) | ||
570 | REG_USB_INCSR |= USB_INCSR_SENDSTALL; | ||
571 | else | ||
572 | REG_USB_INCSR = (REG_USB_INCSR & ~USB_INCSR_SENDSTALL) | USB_INCSR_CDT; | ||
573 | } | ||
574 | else | ||
575 | { | ||
576 | if(stall) | ||
577 | REG_USB_OUTCSR |= USB_OUTCSR_SENDSTALL; | ||
578 | else | ||
579 | REG_USB_OUTCSR = (REG_USB_OUTCSR & ~USB_OUTCSR_SENDSTALL) | USB_OUTCSR_CDT; | ||
580 | } | ||
581 | } | ||
582 | } | ||
583 | |||
584 | int usb_detect(void) | ||
585 | { | ||
586 | return (__gpio_get_pin(PIN_USB_DET) == 1) | ||
587 | ? USB_INSERTED : USB_EXTRACTED; | ||
588 | } | ||
589 | |||
590 | void usb_init_device(void) | ||
591 | { | ||
592 | __gpio_clear_pin(PIN_USB_DRVVBUS); | ||
593 | __gpio_as_output(PIN_USB_DRVVBUS); | ||
594 | |||
595 | __gpio_as_input(PIN_USB_OTG_ID); | ||
596 | __gpio_as_input(PIN_USB_DET); | ||
597 | |||
598 | __gpio_disable_pull(PIN_USB_OTG_ID); | ||
599 | __gpio_disable_pull(PIN_USB_DET); | ||
600 | |||
601 | #ifdef USB_STATUS_BY_EVENT | ||
602 | __gpio_as_irq_rise_edge(PIN_USB_DET); | ||
603 | system_enable_irq(IRQ_USB_DET); | ||
604 | #endif | ||
605 | |||
606 | system_enable_irq(IRQ_OTG); | ||
607 | |||
608 | for(unsigned i=0; i<TOTAL_EP(); i++) | ||
609 | semaphore_init(&endpoints[i].complete, 1, 0); | ||
610 | } | ||
611 | |||
612 | #ifdef USB_STATUS_BY_EVENT | ||
613 | static int usb_oneshot_callback(struct timeout *tmo) | ||
614 | { | ||
615 | (void)tmo; | ||
616 | int state = usb_detect(); | ||
617 | |||
618 | /* This is called only if the state was stable for HZ/16 - check state | ||
619 | * and post appropriate event. */ | ||
620 | usb_status_event(state); | ||
621 | |||
622 | if(state == USB_EXTRACTED) | ||
623 | __gpio_as_irq_rise_edge(PIN_USB_DET); | ||
624 | else | ||
625 | __gpio_as_irq_fall_edge(PIN_USB_DET); | ||
626 | |||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | void GPIO_USB_DET(void) | ||
631 | { | ||
632 | static struct timeout usb_oneshot; | ||
633 | timeout_register(&usb_oneshot, usb_oneshot_callback, (HZ/16), 0); | ||
634 | } | ||
635 | #endif | ||
636 | |||
637 | void usb_enable(bool on) | ||
638 | { | ||
639 | if(on) | ||
640 | usb_core_init(); | ||
641 | else | ||
642 | usb_core_exit(); | ||
643 | } | ||
644 | |||
645 | void usb_attach(void) | ||
646 | { | ||
647 | usb_enable(true); | ||
648 | } | ||
649 | |||
650 | void usb_drv_init(void) | ||
651 | { | ||
652 | logf("%s()", __func__); | ||
653 | |||
654 | /* Dis- and reconnect from USB */ | ||
655 | REG_USB_POWER &= ~USB_POWER_SOFTCONN; | ||
656 | mdelay(20); | ||
657 | REG_USB_POWER |= USB_POWER_SOFTCONN; | ||
658 | mdelay(20); | ||
659 | |||
660 | udc_reset(); | ||
661 | } | ||
662 | |||
663 | void usb_drv_exit(void) | ||
664 | { | ||
665 | logf("%s()", __func__); | ||
666 | |||
667 | REG_USB_FADDR = 0; | ||
668 | REG_USB_INDEX = 0; | ||
669 | |||
670 | /* Disable interrupts */ | ||
671 | REG_USB_INTRINE = 0; | ||
672 | REG_USB_INTROUTE = 0; | ||
673 | REG_USB_INTRUSBE = 0; | ||
674 | |||
675 | /* Disable DMA */ | ||
676 | REG_USB_CNTL(0) = 0; | ||
677 | REG_USB_CNTL(1) = 0; | ||
678 | |||
679 | /* Disconnect from USB */ | ||
680 | REG_USB_POWER &= ~USB_POWER_SOFTCONN; | ||
681 | } | ||
682 | |||
683 | void usb_drv_set_address(int address) | ||
684 | { | ||
685 | logf("%s(%d)", __func__, address); | ||
686 | |||
687 | REG_USB_FADDR = address; | ||
688 | } | ||
689 | |||
690 | static void usb_drv_send_internal(struct usb_endpoint* ep, void* ptr, int length, bool blocking) | ||
691 | { | ||
692 | if(ep->type == ep_control && ptr == NULL && length == 0) | ||
693 | return; /* ACK request, handled in the ISR */ | ||
694 | |||
695 | int flags = disable_irq_save(); | ||
696 | |||
697 | ep->buf = ptr; | ||
698 | ep->sent = 0; | ||
699 | ep->length = length; | ||
700 | ep->busy = true; | ||
701 | if(blocking) | ||
702 | ep->wait = true; | ||
703 | |||
704 | if(ep->type == ep_control) | ||
705 | { | ||
706 | EP0_send(); | ||
707 | } | ||
708 | else | ||
709 | { | ||
710 | if(ep->use_dma) | ||
711 | { | ||
712 | //dma_cache_wback_inv((unsigned long)ptr, length); | ||
713 | __dcache_writeback_all(); | ||
714 | REG_USB_ADDR(0) = PHYSADDR((unsigned long)ptr); | ||
715 | REG_USB_COUNT(0) = length; | ||
716 | REG_USB_CNTL(0) = (USB_CNTL_INTR_EN | USB_CNTL_MODE_1 | | ||
717 | USB_CNTL_DIR_IN | USB_CNTL_ENA | | ||
718 | USB_CNTL_EP(EP_NUMBER2(ep)) | USB_CNTL_BURST_16); | ||
719 | } | ||
720 | else | ||
721 | EPIN_handler(EP_NUMBER2(ep)); | ||
722 | } | ||
723 | |||
724 | restore_irq(flags); | ||
725 | |||
726 | if(blocking) | ||
727 | { | ||
728 | semaphore_wait(&ep->complete, TIMEOUT_BLOCK); | ||
729 | ep->wait = false; | ||
730 | } | ||
731 | } | ||
732 | |||
733 | int usb_drv_send_nonblocking(int endpoint, void* ptr, int length) | ||
734 | { | ||
735 | logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length); | ||
736 | |||
737 | usb_drv_send_internal(&endpoints[(endpoint & 0x7F)*2], ptr, length, false); | ||
738 | |||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | int usb_drv_send(int endpoint, void* ptr, int length) | ||
743 | { | ||
744 | logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length); | ||
745 | |||
746 | usb_drv_send_internal(&endpoints[(endpoint & 0x7F)*2], ptr, length, true); | ||
747 | |||
748 | return 0; | ||
749 | } | ||
750 | |||
751 | int usb_drv_recv(int endpoint, void* ptr, int length) | ||
752 | { | ||
753 | int flags; | ||
754 | struct usb_endpoint *ep; | ||
755 | endpoint &= 0x7F; | ||
756 | |||
757 | logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length); | ||
758 | |||
759 | if(endpoint == EP_CONTROL) | ||
760 | return 0; /* all EP0 OUT transactions are handled within the ISR */ | ||
761 | else | ||
762 | { | ||
763 | flags = disable_irq_save(); | ||
764 | ep = &endpoints[endpoint*2+1]; | ||
765 | |||
766 | ep->buf = ptr; | ||
767 | ep->received = 0; | ||
768 | ep->length = length; | ||
769 | ep->busy = true; | ||
770 | if(ep->use_dma) | ||
771 | { | ||
772 | //dma_cache_wback_inv((unsigned long)ptr, length); | ||
773 | __dcache_writeback_all(); | ||
774 | REG_USB_ADDR(1) = PHYSADDR((unsigned long)ptr); | ||
775 | REG_USB_COUNT(1) = length; | ||
776 | REG_USB_CNTL(1) = (USB_CNTL_INTR_EN | USB_CNTL_MODE_1 | | ||
777 | USB_CNTL_ENA | USB_CNTL_EP(endpoint) | | ||
778 | USB_CNTL_BURST_16); | ||
779 | } | ||
780 | else | ||
781 | EPOUT_handler(endpoint); | ||
782 | |||
783 | restore_irq(flags); | ||
784 | return 0; | ||
785 | } | ||
786 | } | ||
787 | |||
788 | void usb_drv_set_test_mode(int mode) | ||
789 | { | ||
790 | logf("%s(%d)", __func__, mode); | ||
791 | |||
792 | switch(mode) | ||
793 | { | ||
794 | case 0: | ||
795 | REG_USB_TESTMODE &= ~USB_TEST_ALL; | ||
796 | break; | ||
797 | case 1: | ||
798 | REG_USB_TESTMODE |= USB_TEST_J; | ||
799 | break; | ||
800 | case 2: | ||
801 | REG_USB_TESTMODE |= USB_TEST_K; | ||
802 | break; | ||
803 | case 3: | ||
804 | REG_USB_TESTMODE |= USB_TEST_SE0NAK; | ||
805 | break; | ||
806 | case 4: | ||
807 | REG_USB_TESTMODE |= USB_TEST_PACKET; | ||
808 | break; | ||
809 | } | ||
810 | } | ||
811 | |||
812 | int usb_drv_port_speed(void) | ||
813 | { | ||
814 | return (REG_USB_POWER & USB_POWER_HSMODE) ? 1 : 0; | ||
815 | } | ||
816 | |||
817 | void usb_drv_cancel_all_transfers(void) | ||
818 | { | ||
819 | logf("%s()", __func__); | ||
820 | |||
821 | unsigned int i, flags; | ||
822 | flags = disable_irq_save(); | ||
823 | |||
824 | for(i=0; i<TOTAL_EP(); i++) | ||
825 | { | ||
826 | if(i != 1) /* ep0 out needs special handling */ | ||
827 | endpoints[i].buf = NULL; | ||
828 | |||
829 | endpoints[i].sent = 0; | ||
830 | endpoints[i].length = 0; | ||
831 | |||
832 | select_endpoint(i/2); | ||
833 | flushFIFO(&endpoints[i]); | ||
834 | } | ||
835 | restore_irq(flags); | ||
836 | } | ||
837 | |||
838 | |||
839 | void usb_drv_release_endpoint(int ep) | ||
840 | { | ||
841 | (void)ep; | ||
842 | logf("%s(%d, %s)", __func__, (ep & 0x7F), (ep >> 7) ? "IN" : "OUT"); | ||
843 | } | ||
844 | |||
845 | int usb_drv_request_endpoint(int type, int dir) | ||
846 | { | ||
847 | logf("%s(%d, %s)", __func__, type, (dir == USB_DIR_IN) ? "IN" : "OUT"); | ||
848 | |||
849 | dir &= USB_ENDPOINT_DIR_MASK; | ||
850 | type &= USB_ENDPOINT_XFERTYPE_MASK; | ||
851 | |||
852 | /* There are only 3+2 endpoints, so hardcode this ... */ | ||
853 | switch(type) | ||
854 | { | ||
855 | case USB_ENDPOINT_XFER_BULK: | ||
856 | if(dir == USB_DIR_IN) | ||
857 | return (1 | USB_DIR_IN); | ||
858 | else | ||
859 | return (1 | USB_DIR_OUT); | ||
860 | |||
861 | case USB_ENDPOINT_XFER_INT: | ||
862 | if(dir == USB_DIR_IN) | ||
863 | return (2 | USB_DIR_IN); | ||
864 | else | ||
865 | return (2 | USB_DIR_OUT); | ||
866 | |||
867 | default: | ||
868 | return -1; | ||
869 | } | ||
870 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/xdebug.h b/firmware/target/mips/ingenic_jz47xx/xdebug.h new file mode 100644 index 0000000000..2f348e266b --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/xdebug.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 | #ifndef __XDEBUG_H_ | ||
23 | #define __XDEBUG_H_ | ||
24 | |||
25 | void serial_puts(const char *s); | ||
26 | void serial_putsf(const char *format, ...); | ||
27 | void serial_put_hex(unsigned int d); | ||
28 | void serial_put_dec(unsigned int d); | ||
29 | void serial_dump_data(unsigned char* data, int len); | ||
30 | |||
31 | #endif /* __XDEBUG_H_ */ | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/adc-target.h b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/adc-target.h new file mode 100644 index 0000000000..c47406ca21 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/adc-target.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 | #ifndef _ADC_TARGET_H_ | ||
22 | #define _ADC_TARGET_H_ | ||
23 | |||
24 | #define NUM_ADC_CHANNELS 4 | ||
25 | |||
26 | #define ADC_BUTTONS 0 | ||
27 | |||
28 | #endif /* _ADC_TARGET_H_ */ | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/ata-sd-target.h b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/ata-sd-target.h new file mode 100644 index 0000000000..bb2cced15b --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/ata-sd-target.h | |||
@@ -0,0 +1,48 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 | #ifndef ATA_SD_TARGET_H | ||
23 | #define ATA_SD_TARGET_H | ||
24 | |||
25 | #include "cpu.h" | ||
26 | #include "system.h" | ||
27 | |||
28 | #define PIN_SD1_CD (32*0+29) /* Pin to check card insertion */ | ||
29 | #define IRQ_SD1_CD GPIO_IRQ(PIN_SD1_CD) | ||
30 | #define GPIO_SD1_CD GPIO29 | ||
31 | |||
32 | #define PIN_SD2_CD (32*0+28) /* Pin to check card insertion */ | ||
33 | #define IRQ_SD2_CD GPIO_IRQ(PIN_SD2_CD) | ||
34 | #define GPIO_SD2_CD GPIO28 | ||
35 | |||
36 | static inline void sd_init_gpio(void) | ||
37 | { | ||
38 | __gpio_as_msc1_pd_4bit(); | ||
39 | __gpio_as_msc2_pb_4bit(); | ||
40 | |||
41 | __gpio_as_input(PIN_SD1_CD); | ||
42 | __gpio_as_input(PIN_SD2_CD); | ||
43 | |||
44 | __gpio_disable_pull(PIN_SD1_CD); | ||
45 | __gpio_disable_pull(PIN_SD2_CD); | ||
46 | } | ||
47 | |||
48 | #endif | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/backlight-xduoo_x3.c b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/backlight-xduoo_x3.c new file mode 100644 index 0000000000..3f00b0b67d --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/backlight-xduoo_x3.c | |||
@@ -0,0 +1,50 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | #include "cpu.h" | ||
24 | #include "backlight-target.h" | ||
25 | #include "lcd.h" | ||
26 | |||
27 | bool backlight_hw_init(void) | ||
28 | { | ||
29 | return true; | ||
30 | } | ||
31 | |||
32 | void backlight_hw_on(void) | ||
33 | { | ||
34 | lcd_enable(true); | ||
35 | } | ||
36 | |||
37 | void backlight_hw_off(void) | ||
38 | { | ||
39 | lcd_enable(false); | ||
40 | } | ||
41 | |||
42 | void backlight_hw_brightness(int brightness) | ||
43 | { | ||
44 | lcd_set_contrast(brightness*16-1); | ||
45 | } | ||
46 | |||
47 | void lcd_sleep(void) | ||
48 | { | ||
49 | backlight_hw_off(); | ||
50 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/button-target.h b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/button-target.h new file mode 100644 index 0000000000..2dd94b14bc --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/button-target.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 | #ifndef BUTTON_TARGET_H | ||
22 | #define BUTTON_TARGET_H | ||
23 | |||
24 | #define HAS_BUTTON_HOLD | ||
25 | |||
26 | /* Main unit's buttons */ | ||
27 | #define BUTTON_POWER 0x00000001 | ||
28 | #define BUTTON_HOME 0x00000002 | ||
29 | #define BUTTON_OPTION 0x00000004 | ||
30 | #define BUTTON_PREV 0x00000008 | ||
31 | #define BUTTON_NEXT 0x00000010 | ||
32 | #define BUTTON_PLAY 0x00000020 | ||
33 | #define BUTTON_VOL_UP 0x00000040 | ||
34 | #define BUTTON_VOL_DOWN 0x00000080 | ||
35 | |||
36 | #define BUTTON_LEFT 0 | ||
37 | #define BUTTON_RIGHT 0 | ||
38 | |||
39 | #define BUTTON_MAIN (BUTTON_POWER | BUTTON_HOME | BUTTON_OPTION | BUTTON_PREV | \ | ||
40 | BUTTON_NEXT | BUTTON_PLAY | BUTTON_VOL_UP | BUTTON_VOL_DOWN) | ||
41 | |||
42 | /* Software power-off */ | ||
43 | #define POWEROFF_BUTTON BUTTON_POWER | ||
44 | #define POWEROFF_COUNT 10 | ||
45 | |||
46 | #endif /* BUTTON_TARGET_H */ | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c new file mode 100644 index 0000000000..89251b727d --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c | |||
@@ -0,0 +1,420 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 | #include "config.h" | ||
22 | |||
23 | #include "lcd.h" | ||
24 | #include "system.h" | ||
25 | #include "cpu.h" | ||
26 | #include "string.h" | ||
27 | |||
28 | /* LCD pins */ | ||
29 | #define PIN_BL_EN (32*4+0) | ||
30 | |||
31 | #define PIN_LCD_D0 (32*2+2) | ||
32 | #define PIN_LCD_D1 (32*2+3) | ||
33 | #define PIN_LCD_D2 (32*2+4) | ||
34 | #define PIN_LCD_D3 (32*2+5) | ||
35 | #define PIN_LCD_D4 (32*2+6) | ||
36 | #define PIN_LCD_D5 (32*2+7) | ||
37 | #define PIN_LCD_D6 (32*2+12) | ||
38 | #define PIN_LCD_D7 (32*2+13) | ||
39 | |||
40 | #define PIN_LCD_RD (32*2+8) | ||
41 | #define PIN_LCD_DC (32*2+9) | ||
42 | #define PIN_LCD_CS (32*2+14) | ||
43 | #define PIN_LCD_RES (32*2+18) | ||
44 | #define PIN_LCD_WR (32*2+19) | ||
45 | |||
46 | /* LCD setup codes */ | ||
47 | #define LCD_SET_LOWER_COLUMN_ADDRESS ((char)0x00) | ||
48 | #define LCD_SET_HIGHER_COLUMN_ADDRESS ((char)0x10) | ||
49 | #define LCD_SET_DISPLAY_START_LINE ((char)0x40) | ||
50 | #define LCD_SET_CONTRAST_CONTROL_REGISTER ((char)0x81) | ||
51 | #define LCD_SET_CHARGE_PUMP ((char)0x8D) | ||
52 | #define LCD_SET_SEGMENT_REMAP ((char)0xA0) | ||
53 | #define LCD_SET_SEGMENT_REMAP_INV ((char)0xA1) | ||
54 | #define LCD_SET_ENTIRE_DISPLAY_OFF ((char)0xA4) | ||
55 | #define LCD_SET_ENTIRE_DISPLAY_ON ((char)0xA5) | ||
56 | #define LCD_SET_NORMAL_DISPLAY ((char)0xA6) | ||
57 | #define LCD_SET_REVERSE_DISPLAY ((char)0xA7) | ||
58 | #define LCD_SET_MULTIPLEX_RATIO ((char)0xA8) | ||
59 | #define LCD_SET_DC_DC ((char)0xAD) | ||
60 | #define LCD_SET_DISPLAY_OFF ((char)0xAE) | ||
61 | #define LCD_SET_DISPLAY_ON ((char)0xAF) | ||
62 | #define LCD_SET_PAGE_ADDRESS ((char)0xB0) | ||
63 | #define LCD_SET_COM_OUTPUT_SCAN_DIRECTION ((char)0xC0) | ||
64 | #define LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV ((char)0xC8) | ||
65 | #define LCD_SET_DISPLAY_OFFSET ((char)0xD3) | ||
66 | #define LCD_SET_DISPLAY_CLOCK_AND_OSC_FREQ ((char)0xD5) | ||
67 | #define LCD_SET_VCOM_HW_CONFIGURATION ((char)0xDA) | ||
68 | #define LCD_SET_VCOM_DESELECT_LEVEL ((char)0xDB) | ||
69 | #define LCD_SET_PRECHARGE_PERIOD ((char)0xD9) | ||
70 | #define LCD_NOP ((char)0xE3) | ||
71 | |||
72 | /* LCD command codes */ | ||
73 | #define LCD_CNTL_CONTRAST 0x81 /* Contrast */ | ||
74 | #define LCD_CNTL_OUTSCAN 0xc8 /* Output scan direction */ | ||
75 | #define LCD_CNTL_SEGREMAP 0xa1 /* Segment remap */ | ||
76 | #define LCD_CNTL_DISPON 0xaf /* Display on */ | ||
77 | |||
78 | #define LCD_CNTL_PAGE 0xb0 /* Page address */ | ||
79 | #define LCD_CNTL_HIGHCOL 0x10 /* Upper column address */ | ||
80 | #define LCD_CNTL_LOWCOL 0x00 /* Lower column address */ | ||
81 | |||
82 | #define LCD_COL_OFFSET 2 /* column offset */ | ||
83 | |||
84 | static inline void bitdelay(void) | ||
85 | { | ||
86 | unsigned int i = 15; | ||
87 | __asm__ __volatile__ ( | ||
88 | ".set noreorder \n" | ||
89 | "1: \n" | ||
90 | "bne %0, $0, 1b \n" | ||
91 | "addi %0, %0, -1 \n" | ||
92 | ".set reorder \n" | ||
93 | : "=r" (i) | ||
94 | : "0" (i) | ||
95 | ); | ||
96 | } | ||
97 | |||
98 | void lcd_hw_init(void) | ||
99 | { | ||
100 | REG_GPIO_PXFUNC(2) = 0x000C73FC; /* D0-D7 RD DC CS RES WR */ | ||
101 | REG_GPIO_PXSELC(2) = 0x000C73FC; | ||
102 | REG_GPIO_PXDIRS(2) = 0x000C73FC; | ||
103 | REG_GPIO_PXDATS(2) = 0x000C73FC; | ||
104 | __gpio_clear_pin(PIN_BL_EN); | ||
105 | __gpio_as_output(PIN_BL_EN); | ||
106 | __gpio_clear_pin(PIN_LCD_RES); | ||
107 | udelay(1); | ||
108 | __gpio_set_pin(PIN_LCD_RES); | ||
109 | __gpio_clear_pin(PIN_LCD_CS); | ||
110 | } | ||
111 | |||
112 | void lcd_write_command(int byte) | ||
113 | { | ||
114 | __gpio_clear_pin(PIN_LCD_DC); | ||
115 | REG_GPIO_PXDATC(2) = 0x000030FC; | ||
116 | REG_GPIO_PXDATS(2) = ((byte & 0xC0) << 6) | ((byte & 0x3F) << 2); | ||
117 | __gpio_clear_pin(PIN_LCD_WR); | ||
118 | bitdelay(); | ||
119 | __gpio_set_pin(PIN_LCD_WR); | ||
120 | bitdelay(); | ||
121 | } | ||
122 | |||
123 | void lcd_write_data(const fb_data* p_bytes, int count) | ||
124 | { | ||
125 | __gpio_set_pin(PIN_LCD_DC); | ||
126 | while (count--) | ||
127 | { | ||
128 | REG_GPIO_PXDATC(2) = 0x000030FC; | ||
129 | REG_GPIO_PXDATS(2) = ((*p_bytes & 0xC0) << 6) | ((*p_bytes & 0x3F) << 2); | ||
130 | p_bytes++; | ||
131 | __gpio_clear_pin(PIN_LCD_WR); | ||
132 | bitdelay(); | ||
133 | __gpio_set_pin(PIN_LCD_WR); | ||
134 | bitdelay(); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | void lcd_enable_power(bool onoff) | ||
139 | { | ||
140 | if (onoff) | ||
141 | __gpio_set_pin(PIN_BL_EN); | ||
142 | else | ||
143 | __gpio_clear_pin(PIN_BL_EN); | ||
144 | } | ||
145 | |||
146 | /** globals **/ | ||
147 | |||
148 | static bool display_on = false; /* used by lcd_enable */ | ||
149 | |||
150 | /*** hardware configuration ***/ | ||
151 | |||
152 | void lcd_set_contrast(int val) | ||
153 | { | ||
154 | lcd_write_command(LCD_CNTL_CONTRAST); | ||
155 | lcd_write_command(val); | ||
156 | } | ||
157 | |||
158 | void lcd_set_invert_display(bool yesno) | ||
159 | { | ||
160 | if (yesno) | ||
161 | lcd_write_command(LCD_SET_REVERSE_DISPLAY); | ||
162 | else | ||
163 | lcd_write_command(LCD_SET_NORMAL_DISPLAY); | ||
164 | } | ||
165 | |||
166 | /* turn the display upside down (call lcd_update() afterwards) */ | ||
167 | void lcd_set_flip(bool yesno) | ||
168 | { | ||
169 | if (yesno) | ||
170 | { | ||
171 | lcd_write_command(LCD_SET_SEGMENT_REMAP); | ||
172 | lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION); | ||
173 | } | ||
174 | else | ||
175 | { | ||
176 | lcd_write_command(LCD_SET_SEGMENT_REMAP_INV); | ||
177 | lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | #ifdef HAVE_LCD_ENABLE | ||
182 | void lcd_enable(bool enable) | ||
183 | { | ||
184 | if(display_on == enable) | ||
185 | return; | ||
186 | |||
187 | if( (display_on = enable) ) /* simple '=' is not a typo ! */ | ||
188 | { | ||
189 | lcd_enable_power(enable); | ||
190 | lcd_write_command(LCD_SET_DISPLAY_ON); | ||
191 | send_event(LCD_EVENT_ACTIVATION, NULL); | ||
192 | } | ||
193 | else | ||
194 | { | ||
195 | lcd_write_command(LCD_SET_DISPLAY_OFF); | ||
196 | lcd_enable_power(enable); | ||
197 | } | ||
198 | } | ||
199 | |||
200 | bool lcd_active(void) | ||
201 | { | ||
202 | return display_on; | ||
203 | } | ||
204 | #endif | ||
205 | |||
206 | /* LCD init, largely based on what OF does */ | ||
207 | void lcd_init_device(void) | ||
208 | { | ||
209 | int i; | ||
210 | |||
211 | lcd_hw_init(); | ||
212 | |||
213 | /* Set display off */ | ||
214 | lcd_write_command(LCD_SET_DISPLAY_OFF); | ||
215 | |||
216 | /* Set display clock and oscillator frequency */ | ||
217 | lcd_write_command(LCD_SET_DISPLAY_CLOCK_AND_OSC_FREQ); | ||
218 | lcd_write_command(0x80); | ||
219 | |||
220 | /* Set multiplex ratio*/ | ||
221 | lcd_write_command(LCD_SET_MULTIPLEX_RATIO); | ||
222 | lcd_write_command(0x3F); | ||
223 | |||
224 | /* Set display offset */ | ||
225 | lcd_write_command(LCD_SET_DISPLAY_OFFSET); | ||
226 | lcd_write_command(0x00); | ||
227 | |||
228 | /* Set starting line as 0 */ | ||
229 | lcd_write_command(LCD_SET_DISPLAY_START_LINE); | ||
230 | |||
231 | /* Set charge pump */ | ||
232 | lcd_write_command(LCD_SET_CHARGE_PUMP); | ||
233 | lcd_write_command(0x14); /* VCC Generated by Internal DC/DC Circuit */ | ||
234 | |||
235 | /* Column 131 is remapped to SEG0 */ | ||
236 | lcd_write_command(LCD_SET_SEGMENT_REMAP_INV); | ||
237 | |||
238 | /* Invert COM scan direction (N-1 to 0) */ | ||
239 | lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV); | ||
240 | |||
241 | /* Set COM hardware configuration */ | ||
242 | lcd_write_command(LCD_SET_VCOM_HW_CONFIGURATION); | ||
243 | lcd_write_command(0x12); | ||
244 | |||
245 | /* Set contrast control */ | ||
246 | lcd_write_command(LCD_SET_CONTRAST_CONTROL_REGISTER); | ||
247 | lcd_write_command(0xCF); /* VCC Generated by Internal DC/DC Circuit */ | ||
248 | |||
249 | /* Set pre-charge period */ | ||
250 | lcd_write_command(LCD_SET_PRECHARGE_PERIOD); | ||
251 | lcd_write_command(0xF1); /* VCC Generated by Internal DC/DC Circuit */ | ||
252 | |||
253 | /* Set VCOM deselect level */ | ||
254 | lcd_write_command(LCD_SET_VCOM_DESELECT_LEVEL); | ||
255 | lcd_write_command(0x40); | ||
256 | |||
257 | /* Set normal display mode (not every pixel ON) */ | ||
258 | lcd_write_command(LCD_SET_ENTIRE_DISPLAY_OFF); | ||
259 | |||
260 | /* Set normal display mode (not inverted) */ | ||
261 | lcd_write_command(LCD_SET_NORMAL_DISPLAY); | ||
262 | |||
263 | fb_data p_bytes[LCD_WIDTH + 2 * LCD_COL_OFFSET]; | ||
264 | memset(p_bytes, 0, sizeof(p_bytes)); /* fills with 0 : pixel off */ | ||
265 | for(i = 0; i < 8; i++) | ||
266 | { | ||
267 | lcd_write_command (LCD_SET_PAGE_ADDRESS | (i /*& 0xf*/)); | ||
268 | lcd_write_data(p_bytes, LCD_WIDTH + 2 * LCD_COL_OFFSET); | ||
269 | } | ||
270 | |||
271 | lcd_enable(true); | ||
272 | |||
273 | lcd_update(); | ||
274 | } | ||
275 | |||
276 | /*** Update functions ***/ | ||
277 | |||
278 | /* Performance function that works with an external buffer | ||
279 | note that by and bheight are in 8-pixel units! */ | ||
280 | void lcd_blit_mono(const unsigned char *data, int x, int by, int width, | ||
281 | int bheight, int stride) | ||
282 | { | ||
283 | if(!display_on) | ||
284 | return; | ||
285 | |||
286 | /* Copy display bitmap to hardware */ | ||
287 | while (bheight--) | ||
288 | { | ||
289 | lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf)); | ||
290 | lcd_write_command (LCD_CNTL_HIGHCOL | (((x+LCD_COL_OFFSET)>>4) & 0xf)); | ||
291 | lcd_write_command (LCD_CNTL_LOWCOL | ((x+LCD_COL_OFFSET) & 0xf)); | ||
292 | |||
293 | lcd_write_data(data, width); | ||
294 | data += stride; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | |||
299 | #ifndef BOOTLOADER | ||
300 | /* Helper function for lcd_grey_phase_blit(). */ | ||
301 | void lcd_grey_data(unsigned char *values, unsigned char *phases, int count) ICODE_ATTR; | ||
302 | void lcd_grey_data(unsigned char *values, unsigned char *phases, int count) | ||
303 | { | ||
304 | unsigned char a, b, c, d; | ||
305 | |||
306 | __gpio_set_pin(PIN_LCD_DC); | ||
307 | while(count--) | ||
308 | { | ||
309 | c = 0; | ||
310 | d = 8; | ||
311 | while(d--) | ||
312 | { | ||
313 | a = *phases; | ||
314 | b = *values++; | ||
315 | b += a & ~0x80; | ||
316 | *phases++ = b; | ||
317 | c <<= 1; | ||
318 | c |= a >> 7; | ||
319 | } | ||
320 | REG_GPIO_PXDATC(2) = 0x000030FC; | ||
321 | REG_GPIO_PXDATS(2) = ((c & 0xC0) << 6) | ((c & 0x3F) << 2); | ||
322 | __gpio_clear_pin(PIN_LCD_WR); | ||
323 | bitdelay(); | ||
324 | __gpio_set_pin(PIN_LCD_WR); | ||
325 | bitdelay(); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | /* Performance function that works with an external buffer | ||
330 | note that by and bheight are in 8-pixel units! */ | ||
331 | void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases, | ||
332 | int x, int by, int width, int bheight, int stride) | ||
333 | { | ||
334 | if(!display_on) | ||
335 | return; | ||
336 | |||
337 | stride <<= 3; /* 8 pixels per block */ | ||
338 | /* Copy display bitmap to hardware */ | ||
339 | while (bheight--) | ||
340 | { | ||
341 | lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf)); | ||
342 | lcd_write_command (LCD_CNTL_HIGHCOL | (((x+LCD_COL_OFFSET)>>4) & 0xf)); | ||
343 | lcd_write_command (LCD_CNTL_LOWCOL | ((x+LCD_COL_OFFSET) & 0xf)); | ||
344 | |||
345 | lcd_grey_data(values, phases, width); | ||
346 | |||
347 | values += stride; | ||
348 | phases += stride; | ||
349 | } | ||
350 | } | ||
351 | #endif | ||
352 | |||
353 | /* Update the display. | ||
354 | This must be called after all other LCD functions that change the display. */ | ||
355 | void lcd_update(void) ICODE_ATTR; | ||
356 | void lcd_update(void) | ||
357 | { | ||
358 | int y; | ||
359 | |||
360 | if(!display_on) | ||
361 | return; | ||
362 | |||
363 | /* Copy display bitmap to hardware */ | ||
364 | for (y = 0; y < LCD_FBHEIGHT; y++) | ||
365 | { | ||
366 | lcd_write_command (LCD_CNTL_PAGE | (y & 0xf)); | ||
367 | lcd_write_command (LCD_CNTL_HIGHCOL | ((LCD_COL_OFFSET >> 4) & 0xf)); | ||
368 | lcd_write_command (LCD_CNTL_LOWCOL | (LCD_COL_OFFSET & 0xf)); | ||
369 | |||
370 | lcd_write_data (FBADDR(0, y), LCD_WIDTH); | ||
371 | } | ||
372 | } | ||
373 | |||
374 | /* Update a fraction of the display. */ | ||
375 | void lcd_update_rect(int, int, int, int) ICODE_ATTR; | ||
376 | void lcd_update_rect(int x, int y, int width, int height) | ||
377 | { | ||
378 | int ymax; | ||
379 | |||
380 | if(!display_on) | ||
381 | return; | ||
382 | |||
383 | /* The Y coordinates have to work on even 8 pixel rows */ | ||
384 | if (x < 0) | ||
385 | { | ||
386 | width += x; | ||
387 | x = 0; | ||
388 | } | ||
389 | |||
390 | if (x + width > LCD_WIDTH) | ||
391 | width = LCD_WIDTH - x; | ||
392 | |||
393 | if (width <= 0) | ||
394 | return; /* nothing left to do, 0 is harmful to lcd_write_data() */ | ||
395 | |||
396 | if (y < 0) | ||
397 | { | ||
398 | height += y; | ||
399 | y = 0; | ||
400 | } | ||
401 | |||
402 | if (y + height > LCD_HEIGHT) | ||
403 | height = LCD_HEIGHT - y; | ||
404 | |||
405 | if (height <= 0) | ||
406 | return; /* nothing left to do */ | ||
407 | |||
408 | ymax = (y + height-1) >> 3; | ||
409 | y >>= 3; | ||
410 | |||
411 | /* Copy specified rectange bitmap to hardware */ | ||
412 | for (; y <= ymax; y++) | ||
413 | { | ||
414 | lcd_write_command (LCD_CNTL_PAGE | (y & 0xf)); | ||
415 | lcd_write_command (LCD_CNTL_HIGHCOL | (((x+LCD_COL_OFFSET) >> 4) & 0xf)); | ||
416 | lcd_write_command (LCD_CNTL_LOWCOL | ((x+LCD_COL_OFFSET) & 0xf)); | ||
417 | |||
418 | lcd_write_data (FBADDR(x,y), width); | ||
419 | } | ||
420 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/power-xduoo_x3.c b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/power-xduoo_x3.c new file mode 100644 index 0000000000..9ae602ba56 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/power-xduoo_x3.c | |||
@@ -0,0 +1,46 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | #include "power.h" | ||
24 | #include "cpu.h" | ||
25 | |||
26 | #define CHARGE_STAT_GPIO (32*1+6) /* STAT port */ | ||
27 | |||
28 | /* Detect which power sources are present. */ | ||
29 | unsigned int power_input_status(void) | ||
30 | { | ||
31 | if(!__gpio_get_pin(CHARGE_STAT_GPIO)) | ||
32 | return POWER_INPUT_USB_CHARGER; | ||
33 | |||
34 | return POWER_INPUT_NONE; | ||
35 | } | ||
36 | |||
37 | void power_init(void) | ||
38 | { | ||
39 | __gpio_as_input(CHARGE_STAT_GPIO); | ||
40 | __gpio_disable_pull(CHARGE_STAT_GPIO); | ||
41 | } | ||
42 | |||
43 | bool charging_state(void) | ||
44 | { | ||
45 | return (power_input_status() == POWER_INPUT_USB_CHARGER); | ||
46 | } | ||
diff --git a/firmware/target/mips/ingenic_jz47xx/xduoo_x3/sadc-xduoo_x3.c b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/sadc-xduoo_x3.c new file mode 100644 index 0000000000..d4a3548305 --- /dev/null +++ b/firmware/target/mips/ingenic_jz47xx/xduoo_x3/sadc-xduoo_x3.c | |||
@@ -0,0 +1,214 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Roman Stolyarov | ||
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 "config.h" | ||
23 | #include "system.h" | ||
24 | #include "cpu.h" | ||
25 | #include "button.h" | ||
26 | #include "button-target.h" | ||
27 | #include "powermgmt.h" | ||
28 | #include "kernel.h" | ||
29 | #include "backlight.h" | ||
30 | #include "logf.h" | ||
31 | #include "adc.h" | ||
32 | |||
33 | #define PIN_BTN_POWER (32*0+30) | ||
34 | #define PIN_BTN_HOLD (32*1+15) | ||
35 | |||
36 | #define PIN_KEY_INT (32*4+13) | ||
37 | #define KEY_INT_IRQ GPIO141 | ||
38 | |||
39 | #define PIN_CHARGE_CON (32*1+7) | ||
40 | |||
41 | #define PIN_PH_DECT (32*1+11) | ||
42 | #define IRQ_PH_DECT GPIO_IRQ(PIN_PH_DECT) | ||
43 | #define GPIO_PH_DECT GPIO43 | ||
44 | |||
45 | #define PIN_LO_DECT (32*1+12) | ||
46 | #define IRQ_LO_DECT GPIO_IRQ(PIN_LO_DECT) | ||
47 | #define GPIO_LO_DECT GPIO44 | ||
48 | |||
49 | static volatile unsigned short bat_val,key_val; | ||
50 | |||
51 | bool headphones_inserted(void) | ||
52 | { | ||
53 | return (__gpio_get_pin(PIN_PH_DECT) != 0); | ||
54 | } | ||
55 | |||
56 | void button_init_device(void) | ||
57 | { | ||
58 | key_val = 0xfff; | ||
59 | |||
60 | __gpio_as_input(PIN_BTN_POWER); | ||
61 | __gpio_as_input(PIN_BTN_HOLD); | ||
62 | |||
63 | __gpio_disable_pull(PIN_BTN_POWER); | ||
64 | __gpio_disable_pull(PIN_BTN_HOLD); | ||
65 | |||
66 | __gpio_as_irq_fall_edge(PIN_KEY_INT); | ||
67 | system_enable_irq(GPIO_IRQ(PIN_KEY_INT)); | ||
68 | |||
69 | __gpio_set_pin(PIN_CHARGE_CON); /* 0.7 A */ | ||
70 | __gpio_as_output(PIN_CHARGE_CON); | ||
71 | |||
72 | __gpio_as_input(PIN_LO_DECT); | ||
73 | __gpio_as_input(PIN_PH_DECT); | ||
74 | |||
75 | __gpio_disable_pull(PIN_LO_DECT); | ||
76 | __gpio_disable_pull(PIN_PH_DECT); | ||
77 | } | ||
78 | |||
79 | bool button_hold(void) | ||
80 | { | ||
81 | return (__gpio_get_pin(PIN_BTN_HOLD) ? true : false); | ||
82 | } | ||
83 | |||
84 | int button_read_device(void) | ||
85 | { | ||
86 | static bool hold_button = false; | ||
87 | bool hold_button_old; | ||
88 | |||
89 | hold_button_old = hold_button; | ||
90 | hold_button = (__gpio_get_pin(PIN_BTN_HOLD) ? true : false); | ||
91 | |||
92 | int btn = BUTTON_NONE; | ||
93 | bool gpio_btn = (__gpio_get_pin(PIN_BTN_POWER) ? false : true); | ||
94 | |||
95 | REG_SADC_ADCFG = ADCFG_VBAT_SEL + ADCFG_CMD_AUX(1); | ||
96 | REG_SADC_ADENA = ADENA_VBATEN + ADENA_AUXEN; | ||
97 | |||
98 | #ifndef BOOTLOADER | ||
99 | if (hold_button != hold_button_old) { | ||
100 | backlight_hold_changed(hold_button); | ||
101 | } | ||
102 | if (hold_button) { | ||
103 | return BUTTON_NONE; | ||
104 | } | ||
105 | #endif | ||
106 | |||
107 | if (gpio_btn) | ||
108 | btn |= BUTTON_POWER; | ||
109 | |||
110 | if (key_val < 261) | ||
111 | btn |= BUTTON_VOL_UP; | ||
112 | else | ||
113 | if (key_val < 653) | ||
114 | btn |= BUTTON_VOL_DOWN; | ||
115 | else | ||
116 | if (key_val < 1101) | ||
117 | btn |= BUTTON_PREV; | ||
118 | else | ||
119 | if (key_val < 1498) | ||
120 | btn |= BUTTON_NEXT; | ||
121 | else | ||
122 | if (key_val < 1839) | ||
123 | btn |= BUTTON_PLAY; | ||
124 | else | ||
125 | if (key_val < 2213) | ||
126 | btn |= BUTTON_OPTION; | ||
127 | else | ||
128 | if (key_val < 2600) | ||
129 | btn |= BUTTON_HOME; | ||
130 | |||
131 | return btn; | ||
132 | } | ||
133 | |||
134 | /* called on button press interrupt */ | ||
135 | void KEY_INT_IRQ(void) | ||
136 | { | ||
137 | } | ||
138 | |||
139 | const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = | ||
140 | { | ||
141 | /* 5% */ | ||
142 | 3634 | ||
143 | }; | ||
144 | |||
145 | const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = | ||
146 | { | ||
147 | /* 0% */ | ||
148 | 3300 | ||
149 | }; | ||
150 | |||
151 | |||
152 | /* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ | ||
153 | const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = | ||
154 | { | ||
155 | { 3300, 3652, 3704, 3730, 3753, 3786, 3836, 3906, 3973, 4061, 4160 } | ||
156 | }; | ||
157 | |||
158 | #if CONFIG_CHARGING | ||
159 | /* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ | ||
160 | const unsigned short percent_to_volt_charge[11] = | ||
161 | { 3300, 3652, 3704, 3730, 3753, 3786, 3836, 3906, 3973, 4061, 4160 }; | ||
162 | #endif /* CONFIG_CHARGING */ | ||
163 | |||
164 | /* VBAT = (BDATA/1024) * 2.5V */ | ||
165 | #define BATTERY_SCALE_FACTOR 2460 | ||
166 | |||
167 | /* Returns battery voltage from ADC [millivolts] */ | ||
168 | int _battery_voltage(void) | ||
169 | { | ||
170 | return (bat_val*BATTERY_SCALE_FACTOR)>>10; | ||
171 | } | ||
172 | |||
173 | void adc_init(void) | ||
174 | { | ||
175 | bat_val = 0xfff; | ||
176 | |||
177 | __cpm_start_sadc(); | ||
178 | mdelay(10); | ||
179 | REG_SADC_ADENA = 0; /* Power Up */ | ||
180 | mdelay(70); | ||
181 | REG_SADC_ADSTATE = 0; | ||
182 | REG_SADC_ADCTRL = ADCTRL_MASK_ALL - ADCTRL_ARDYM - ADCTRL_VRDYM; | ||
183 | REG_SADC_ADCFG = ADCFG_VBAT_SEL + ADCFG_CMD_AUX(1); | ||
184 | REG_SADC_ADCLK = (4 << 16) | (1 << 8) | 59; /* 200KHz */ | ||
185 | system_enable_irq(IRQ_SADC); | ||
186 | } | ||
187 | |||
188 | void adc_close(void) | ||
189 | { | ||
190 | REG_SADC_ADENA = ADENA_POWER; /* Power Down */ | ||
191 | __intc_mask_irq(IRQ_SADC); | ||
192 | mdelay(20); | ||
193 | __cpm_stop_sadc(); | ||
194 | } | ||
195 | |||
196 | /* Interrupt handler */ | ||
197 | void SADC(void) | ||
198 | { | ||
199 | unsigned char state; | ||
200 | unsigned char sadcstate; | ||
201 | |||
202 | sadcstate = REG_SADC_ADSTATE; | ||
203 | state = REG_SADC_ADSTATE & (~REG_SADC_ADCTRL); | ||
204 | REG_SADC_ADSTATE &= sadcstate; | ||
205 | |||
206 | if(state & ADCTRL_ARDYM) | ||
207 | { | ||
208 | key_val = REG_SADC_ADADAT; | ||
209 | } | ||
210 | if(state & ADCTRL_VRDYM) | ||
211 | { | ||
212 | bat_val = REG_SADC_ADVDAT; | ||
213 | } | ||
214 | } | ||
diff --git a/firmware/target/mips/mmu-mips.c b/firmware/target/mips/mmu-mips.c index 9dcec43321..b519bf9331 100644 --- a/firmware/target/mips/mmu-mips.c +++ b/firmware/target/mips/mmu-mips.c | |||
@@ -127,86 +127,76 @@ void mmu_init(void) | |||
127 | 127 | ||
128 | #define SYNC_WB() __asm__ __volatile__ ("sync") | 128 | #define SYNC_WB() __asm__ __volatile__ ("sync") |
129 | 129 | ||
130 | #define __CACHE_OP(op, addr) \ | 130 | #define cache_op(base,op) \ |
131 | __asm__ __volatile__( \ | 131 | __asm__ __volatile__(" \ |
132 | " .set noreorder \n" \ | 132 | .set noreorder; \ |
133 | " .set mips32\n\t \n" \ | 133 | .set mips3; \ |
134 | " cache %0, %1 \n" \ | 134 | cache %1, (%0); \ |
135 | " .set mips0 \n" \ | 135 | .set mips0; \ |
136 | " .set reorder \n" \ | 136 | .set reorder" \ |
137 | : \ | 137 | : \ |
138 | : "i" (op), "m" (*(unsigned char *)(addr))) | 138 | : "r" (base), \ |
139 | 139 | "i" (op)); | |
140 | void __flush_dcache_line(unsigned long addr) | 140 | |
141 | void __icache_invalidate_all(void) | ||
141 | { | 142 | { |
142 | __CACHE_OP(DCHitWBInv, addr); | 143 | unsigned long start; |
144 | unsigned long end; | ||
145 | |||
146 | start = A_K0BASE; | ||
147 | end = start + CACHE_SIZE; | ||
148 | while(start < end) | ||
149 | { | ||
150 | cache_op(start,ICIndexInv); | ||
151 | start += CACHE_LINE_SIZE; | ||
152 | } | ||
143 | SYNC_WB(); | 153 | SYNC_WB(); |
144 | } | 154 | } |
145 | 155 | ||
146 | void __icache_invalidate_all(void) | 156 | void __dcache_invalidate_all(void) |
147 | { | 157 | { |
148 | unsigned int i; | 158 | unsigned long start; |
149 | 159 | unsigned long end; | |
150 | asm volatile (".set noreorder \n" | 160 | |
151 | ".set mips32 \n" | 161 | start = A_K0BASE; |
152 | "mtc0 $0, $28 \n" /* TagLo */ | 162 | end = start + CACHE_SIZE; |
153 | "mtc0 $0, $29 \n" /* TagHi */ | 163 | while (start < end) |
154 | ".set mips0 \n" | 164 | { |
155 | ".set reorder \n" | 165 | cache_op(start,DCIndexWBInv); |
156 | ); | 166 | start += CACHE_LINE_SIZE; |
157 | for(i=A_K0BASE; i<A_K0BASE+CACHE_SIZE; i+=CACHE_LINE_SIZE) | 167 | } |
158 | __CACHE_OP(ICIndexStTag, i); | 168 | SYNC_WB(); |
159 | |||
160 | /* invalidate btb */ | ||
161 | asm volatile ( | ||
162 | ".set mips32 \n" | ||
163 | "mfc0 %0, $16, 7 \n" | ||
164 | "nop \n" | ||
165 | "ori %0, 2 \n" | ||
166 | "mtc0 %0, $16, 7 \n" | ||
167 | ".set mips0 \n" | ||
168 | : | ||
169 | : "r" (i)); | ||
170 | } | 169 | } |
171 | 170 | ||
172 | void __dcache_invalidate_all(void) | 171 | void __idcache_invalidate_all(void) |
173 | { | 172 | { |
174 | unsigned int i; | 173 | __dcache_invalidate_all(); |
175 | 174 | __icache_invalidate_all(); | |
176 | asm volatile (".set noreorder \n" | ||
177 | ".set mips32 \n" | ||
178 | "mtc0 $0, $28 \n" | ||
179 | "mtc0 $0, $29 \n" | ||
180 | ".set mips0 \n" | ||
181 | ".set reorder \n" | ||
182 | ); | ||
183 | for (i=A_K0BASE; i<A_K0BASE+CACHE_SIZE; i+=CACHE_LINE_SIZE) | ||
184 | __CACHE_OP(DCIndexStTag, i); | ||
185 | } | 175 | } |
186 | 176 | ||
187 | void __dcache_writeback_all(void) __attribute__ ((section(".icode"))); | ||
188 | void __dcache_writeback_all(void) | 177 | void __dcache_writeback_all(void) |
189 | { | 178 | { |
190 | unsigned int i; | 179 | __dcache_invalidate_all(); |
191 | for(i=A_K0BASE; i<A_K0BASE+CACHE_SIZE; i+=CACHE_LINE_SIZE) | ||
192 | __CACHE_OP(DCIndexWBInv, i); | ||
193 | |||
194 | SYNC_WB(); | ||
195 | } | 180 | } |
196 | 181 | ||
197 | void dma_cache_wback_inv(unsigned long addr, unsigned long size) | 182 | void dma_cache_wback_inv(unsigned long addr, unsigned long size) |
198 | { | 183 | { |
199 | unsigned long end, a; | 184 | unsigned long end, a; |
200 | 185 | ||
201 | if (size >= CACHE_SIZE) | 186 | if (size >= CACHE_SIZE*2) { |
202 | __dcache_writeback_all(); | 187 | __dcache_writeback_all(); |
203 | else | 188 | } |
204 | { | 189 | else { |
205 | unsigned long dc_lsize = CACHE_LINE_SIZE; | 190 | unsigned long dc_lsize = CACHE_LINE_SIZE; |
206 | 191 | ||
207 | a = addr & ~(dc_lsize - 1); | 192 | a = addr & ~(dc_lsize - 1); |
208 | end = (addr + size - 1) & ~(dc_lsize - 1); | 193 | end = (addr + size - 1) & ~(dc_lsize - 1); |
209 | for(; a < end; a += dc_lsize) | 194 | while (1) { |
210 | __flush_dcache_line(a); | 195 | cache_op(a,DCHitWBInv); |
196 | if (a == end) | ||
197 | break; | ||
198 | a += dc_lsize; | ||
199 | } | ||
211 | } | 200 | } |
201 | SYNC_WB(); | ||
212 | } | 202 | } |
diff --git a/firmware/target/mips/mmu-mips.h b/firmware/target/mips/mmu-mips.h index 47aea807cc..7e1e36d3f4 100644 --- a/firmware/target/mips/mmu-mips.h +++ b/firmware/target/mips/mmu-mips.h | |||
@@ -31,14 +31,16 @@ void mmu_init(void); | |||
31 | #define HAVE_CPUCACHE_INVALIDATE | 31 | #define HAVE_CPUCACHE_INVALIDATE |
32 | //#define HAVE_CPUCACHE_FLUSH | 32 | //#define HAVE_CPUCACHE_FLUSH |
33 | 33 | ||
34 | void __dcache_writeback_all(void); | 34 | void __idcache_invalidate_all(void); |
35 | void __dcache_invalidate_all(void); | ||
36 | void __icache_invalidate_all(void); | 35 | void __icache_invalidate_all(void); |
37 | void __flush_dcache_line(unsigned long addr); | 36 | void __dcache_invalidate_all(void); |
37 | void __dcache_writeback_all(void); | ||
38 | |||
38 | void dma_cache_wback_inv(unsigned long addr, unsigned long size); | 39 | void dma_cache_wback_inv(unsigned long addr, unsigned long size); |
39 | 40 | ||
40 | #define commit_discard_idcache __icache_invalidate_all | 41 | #define commit_discard_idcache __idcache_invalidate_all |
41 | #define commit_discard_dcache __dcache_invalidate_all | 42 | #define commit_discard_icache __icache_invalidate_all |
42 | #define commit_dcache __dcache_writeback_all | 43 | #define commit_discard_dcache __dcache_invalidate_all |
44 | #define commit_dcache __dcache_writeback_all | ||
43 | 45 | ||
44 | #endif /* __MMU_MIPS_INCLUDE_H */ | 46 | #endif /* __MMU_MIPS_INCLUDE_H */ |