From 0662793ca0050e823cd1207cc4689a1cba5068bd Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Thu, 28 Jun 2018 06:24:26 -0400 Subject: Add cleaned-up xDuoo X3 support Cleaned up, rebased, and forward-ported from the xvortex fork. (original credit to vsoftster@gmail.com) Change-Id: Ibcc023a0271ea81e901450a88317708c2683236d Signed-off-by: Solomon Peachy --- firmware/target/hosted/sdl/sim-ui-defines.h | 7 + firmware/target/mips/ingenic_jz47xx/app.lds | 6 +- .../target/mips/ingenic_jz47xx/ata-nand-jz4740.c | 2 +- .../target/mips/ingenic_jz47xx/ata-nand-jz4760.c | 692 +++++++++ .../target/mips/ingenic_jz47xx/ata-sd-jz4740.c | 2 +- .../target/mips/ingenic_jz47xx/ata-sd-jz4760.c | 1487 ++++++++++++++++++++ .../target/mips/ingenic_jz47xx/backlight-target.h | 2 + firmware/target/mips/ingenic_jz47xx/codec-jz4760.c | 293 ++++ firmware/target/mips/ingenic_jz47xx/crt0.S | 2 + firmware/target/mips/ingenic_jz47xx/debug-jz4760.c | 146 ++ firmware/target/mips/ingenic_jz47xx/dma-target.h | 31 + .../target/mips/ingenic_jz47xx/dma_acc-jz4760.c | 102 ++ firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c | 355 +++++ .../target/mips/ingenic_jz47xx/kernel-jz4760.c | 53 + firmware/target/mips/ingenic_jz47xx/lcd-jz4760.c | 28 + firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c | 240 ++++ .../target/mips/ingenic_jz47xx/system-jz4760.c | 710 ++++++++++ .../target/mips/ingenic_jz47xx/system-target.h | 14 +- firmware/target/mips/ingenic_jz47xx/timer-jz4760.c | 101 ++ firmware/target/mips/ingenic_jz47xx/usb-jz4760.c | 870 ++++++++++++ firmware/target/mips/ingenic_jz47xx/xdebug.h | 31 + .../mips/ingenic_jz47xx/xduoo_x3/adc-target.h | 28 + .../mips/ingenic_jz47xx/xduoo_x3/ata-sd-target.h | 48 + .../ingenic_jz47xx/xduoo_x3/backlight-xduoo_x3.c | 50 + .../mips/ingenic_jz47xx/xduoo_x3/button-target.h | 46 + .../mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c | 420 ++++++ .../mips/ingenic_jz47xx/xduoo_x3/power-xduoo_x3.c | 46 + .../mips/ingenic_jz47xx/xduoo_x3/sadc-xduoo_x3.c | 214 +++ firmware/target/mips/mmu-mips.c | 108 +- firmware/target/mips/mmu-mips.h | 14 +- 30 files changed, 6077 insertions(+), 71 deletions(-) create mode 100644 firmware/target/mips/ingenic_jz47xx/ata-nand-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/ata-sd-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/codec-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/debug-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/dma-target.h create mode 100644 firmware/target/mips/ingenic_jz47xx/dma_acc-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/i2c-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/kernel-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/lcd-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/pcm-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/system-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/timer-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/usb-jz4760.c create mode 100644 firmware/target/mips/ingenic_jz47xx/xdebug.h create mode 100644 firmware/target/mips/ingenic_jz47xx/xduoo_x3/adc-target.h create mode 100644 firmware/target/mips/ingenic_jz47xx/xduoo_x3/ata-sd-target.h create mode 100644 firmware/target/mips/ingenic_jz47xx/xduoo_x3/backlight-xduoo_x3.c create mode 100644 firmware/target/mips/ingenic_jz47xx/xduoo_x3/button-target.h create mode 100644 firmware/target/mips/ingenic_jz47xx/xduoo_x3/lcd-xduoo_x3.c create mode 100644 firmware/target/mips/ingenic_jz47xx/xduoo_x3/power-xduoo_x3.c create mode 100644 firmware/target/mips/ingenic_jz47xx/xduoo_x3/sadc-xduoo_x3.c (limited to 'firmware/target') diff --git a/firmware/target/hosted/sdl/sim-ui-defines.h b/firmware/target/hosted/sdl/sim-ui-defines.h index 1ac124c881..b237842711 100644 --- a/firmware/target/hosted/sdl/sim-ui-defines.h +++ b/firmware/target/hosted/sdl/sim-ui-defines.h @@ -508,6 +508,7 @@ #define UI_LCD_POSX 46 #define UI_LCD_POSY 40 +<<<<<<< HEAD #elif defined(SONY_NWZA860) #define UI_TITLE "Sony NWZ-A860 Series" #define UI_WIDTH 390 /* width of GUI window */ @@ -521,6 +522,12 @@ #define UI_HEIGHT 380 #define UI_LCD_POSX 29 #define UI_LCD_POSY 25 +#elif defined(XDUOO_X3) +#define UI_TITLE "xDuoo X3" +#define UI_WIDTH 192 /* width of GUI window */ +#define UI_HEIGHT 457 /* height of GUI window */ +#define UI_LCD_POSX 34 +#define UI_LCD_POSY 73 #elif defined(SIMULATOR) #error no UI defines #endif 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) ENTRY(_start) STARTUP(target/mips/ingenic_jz47xx/crt0.o) -#define DRAMORIG 0x80004000 -#define DRAMSIZE (MEMORYSIZE * 0x100000) +#define STUBOFFSET 0x4000 + +#define DRAMORIG (0x80000000 + STUBOFFSET) +#define DRAMSIZE (MEMORYSIZE * 0x100000 - STUBOFFSET) #define IRAMORIG 0x80000000 #define IRAMSIZE 16K 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 @@ ****************************************************************************/ #include "config.h" -#include "jz4740.h" +#include "cpu.h" #include "nand.h" #include "nand_id.h" #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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "cpu.h" +#include "nand.h" +#include "nand_id.h" +#include "system.h" +#include "panic.h" +#include "kernel.h" +#include "storage.h" +#include "string.h" +/*#define LOGF_ENABLE*/ +#include "logf.h" + +//#define USE_DMA +//#define USE_ECC + +/* + * Standard NAND flash commands + */ +#define NAND_CMD_READ0 0 +#define NAND_CMD_READ1 1 +#define NAND_CMD_RNDOUT 5 +#define NAND_CMD_PAGEPROG 0x10 +#define NAND_CMD_READOOB 0x50 +#define NAND_CMD_ERASE1 0x60 +#define NAND_CMD_STATUS 0x70 +#define NAND_CMD_STATUS_MULTI 0x71 +#define NAND_CMD_SEQIN 0x80 +#define NAND_CMD_RNDIN 0x85 +#define NAND_CMD_READID 0x90 +#define NAND_CMD_ERASE2 0xd0 +#define NAND_CMD_RESET 0xff + +/* Extended commands for large page devices */ +#define NAND_CMD_READSTART 0x30 +#define NAND_CMD_RNDOUTSTART 0xE0 +#define NAND_CMD_CACHEDPROG 0x15 + +/* Status bits */ +#define NAND_STATUS_FAIL 0x01 +#define NAND_STATUS_FAIL_N1 0x02 +#define NAND_STATUS_TRUE_READY 0x20 +#define NAND_STATUS_READY 0x40 +#define NAND_STATUS_WP 0x80 + +/* + * NAND parameter struct + */ +struct nand_param { + unsigned int bus_width; /* data bus width: 8-bit/16-bit */ + unsigned int row_cycle; /* row address cycles: 2/3 */ + unsigned int page_size; /* page size in bytes: 512/2048/4096 */ + unsigned int oob_size; /* oob size in bytes: 16/64/128 */ + unsigned int page_per_block; /* pages per block: 32/64/128 */ + unsigned int bad_block_pos; /* bad block pos in oob: 0/5 */ +}; + +/* + * jz4760_nand.c + * + * NAND read routine for JZ4760 + * + * Copyright (c) 2005-2008 Ingenic Semiconductor Inc. + * + */ + +#define CFG_NAND_BASE 0xBA000000 +#define NAND_ADDR_OFFSET 0x00800000 +#define NAND_CMD_OFFSET 0x00400000 + +#define CFG_NAND_SMCR1 0x0d444400 + +#define NAND_DATAPORT CFG_NAND_BASE +#define NAND_ADDRPORT (CFG_NAND_BASE | NAND_ADDR_OFFSET) +#define NAND_COMMPORT (CFG_NAND_BASE | NAND_CMD_OFFSET) + +#define ECC_BLOCK 512 +#define ECC_POS 24 +#define PAR_SIZE 13 + +#define __nand_cmd(n) (REG8(NAND_COMMPORT) = (n)) +#define __nand_addr(n) (REG8(NAND_ADDRPORT) = (n)) +#define __nand_data8() (REG8(NAND_DATAPORT)) +#define __nand_data16() (REG16(NAND_DATAPORT)) + +#define __nand_select() (REG_NEMC_NFCSR |= NEMC_NFCSR_NFE1 | NEMC_NFCSR_NFCE1) +#define __nand_deselect() (REG_NEMC_NFCSR &= ~(NEMC_NFCSR_NFE1 | NEMC_NFCSR_NFCE1)) + +/*--------------------------------------------------------------*/ + +static struct nand_info* chip_info = NULL; +static struct nand_info* bank; +static unsigned long nand_size; +static struct nand_param internal_param; +static struct mutex nand_mtx; +#ifdef USE_DMA +static struct mutex nand_dma_mtx; +static struct semaphore nand_dma_complete; +#endif +static unsigned char temp_page[2048]; /* Max page size */ + +static inline void jz_nand_wait_ready(void) +{ + unsigned int timeout = 1000; + while ((REG_GPIO_PXPIN(0) & 0x00100000) && timeout--); + while (!(REG_GPIO_PXPIN(0) & 0x00100000)); +} + +#ifndef USE_DMA +static inline void jz_nand_read_buf16(void *buf, int count) +{ + register int i; + register unsigned short *p = (unsigned short *)buf; + + for (i = 0; i < count; i += 2) + *p++ = __nand_data16(); +} + +static inline void jz_nand_read_buf8(void *buf, int count) +{ + register int i; + register unsigned char *p = (unsigned char *)buf; + + for (i = 0; i < count; i++) + *p++ = __nand_data8(); +} +#else +static void jz_nand_write_dma(void *source, unsigned int len, int bw) +{ + mutex_lock(&nand_dma_mtx); + + if(((unsigned int)source < 0xa0000000) && len) + dma_cache_wback_inv((unsigned long)source, len); + + dma_enable(); + + REG_DMAC_DCCSR(DMA_NAND_CHANNEL) = DMAC_DCCSR_NDES; + REG_DMAC_DSAR(DMA_NAND_CHANNEL) = PHYSADDR((unsigned long)source); + REG_DMAC_DTAR(DMA_NAND_CHANNEL) = PHYSADDR((unsigned long)NAND_DATAPORT); + REG_DMAC_DTCR(DMA_NAND_CHANNEL) = len / 16; + REG_DMAC_DRSR(DMA_NAND_CHANNEL) = DMAC_DRSR_RS_AUTO; + REG_DMAC_DCMD(DMA_NAND_CHANNEL) = (DMAC_DCMD_SAI| DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DS_16BYTE | + (bw == 8 ? DMAC_DCMD_DWDH_8 : DMAC_DCMD_DWDH_16)); + + REG_DMAC_DCCSR(DMA_NAND_CHANNEL) |= DMAC_DCCSR_EN; /* Enable DMA channel */ +#if 1 + while( REG_DMAC_DTCR(DMA_NAND_CHANNEL) ) + yield(); +#else + REG_DMAC_DCMD(DMA_NAND_CHANNEL) |= DMAC_DCMD_TIE; /* Enable DMA interrupt */ + semaphore_wait(&nand_dma_complete, TIMEOUT_BLOCK); +#endif + + REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_EN; /* Disable DMA channel */ + + dma_disable(); + + mutex_unlock(&nand_dma_mtx); +} + +static void jz_nand_read_dma(void *target, unsigned int len, int bw) +{ + mutex_lock(&nand_dma_mtx); + + if(((unsigned int)target < 0xa0000000) && len) + dma_cache_wback_inv((unsigned long)target, len); + + dma_enable(); + + REG_DMAC_DCCSR(DMA_NAND_CHANNEL) = DMAC_DCCSR_NDES ; + REG_DMAC_DSAR(DMA_NAND_CHANNEL) = PHYSADDR((unsigned long)NAND_DATAPORT); + REG_DMAC_DTAR(DMA_NAND_CHANNEL) = PHYSADDR((unsigned long)target); + REG_DMAC_DTCR(DMA_NAND_CHANNEL) = len / 4; + REG_DMAC_DRSR(DMA_NAND_CHANNEL) = DMAC_DRSR_RS_AUTO; + REG_DMAC_DCMD(DMA_NAND_CHANNEL) = (DMAC_DCMD_SAI| DMAC_DCMD_DAI | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT | + (bw == 8 ? DMAC_DCMD_SWDH_8 : DMAC_DCMD_SWDH_16)); + REG_DMAC_DCCSR(DMA_NAND_CHANNEL) |= DMAC_DCCSR_EN; /* Enable DMA channel */ +#if 1 + while( REG_DMAC_DTCR(DMA_NAND_CHANNEL) ) + yield(); +#else + REG_DMAC_DCMD(DMA_NAND_CHANNEL) |= DMAC_DCMD_TIE; /* Enable DMA interrupt */ + semaphore_wait(&nand_dma_complete, TIMEOUT_BLOCK); +#endif + + //REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_EN; /* Disable DMA channel */ + + dma_disable(); + + mutex_unlock(&nand_dma_mtx); +} + +void DMA_CALLBACK(DMA_NAND_CHANNEL)(void) +{ + if (REG_DMAC_DCCSR(DMA_NAND_CHANNEL) & DMAC_DCCSR_HLT) + REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_HLT; + + if (REG_DMAC_DCCSR(DMA_NAND_CHANNEL) & DMAC_DCCSR_AR) + REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_AR; + + if (REG_DMAC_DCCSR(DMA_NAND_CHANNEL) & DMAC_DCCSR_CT) + REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_CT; + + if (REG_DMAC_DCCSR(DMA_NAND_CHANNEL) & DMAC_DCCSR_TT) + REG_DMAC_DCCSR(DMA_NAND_CHANNEL) &= ~DMAC_DCCSR_TT; + + semaphore_release(&nand_dma_complete); +} +#endif /* USE_DMA */ + +static inline void jz_nand_read_buf(void *buf, int count, int bw) +{ +#ifdef USE_DMA + if (bw == 8) + jz_nand_read_dma(buf, count, 8); + else + jz_nand_read_dma(buf, count, 16); +#else + if (bw == 8) + jz_nand_read_buf8(buf, count); + else + jz_nand_read_buf16(buf, count); +#endif +} + +#ifdef USE_ECC +/* + * Correct 1~9-bit errors in 512-bytes data + */ +static void jz_rs_correct(unsigned char *dat, int idx, int mask) +{ + int i, j; + unsigned short d, d1, dm; + + i = (idx * 9) >> 3; + j = (idx * 9) & 0x7; + + i = (j == 0) ? (i - 1) : i; + j = (j == 0) ? 7 : (j - 1); + + if (i > 512) + return; + + if (i == 512) + d = dat[i - 1]; + else + d = (dat[i] << 8) | dat[i - 1]; + + d1 = (d >> j) & 0x1ff; + d1 ^= mask; + + dm = ~(0x1ff << j); + d = (d & dm) | (d1 << j); + + dat[i - 1] = d & 0xff; + if (i < 512) + dat[i] = (d >> 8) & 0xff; +} +#endif + +/* + * Read oob + */ +static int jz_nand_read_oob(unsigned long page_addr, unsigned char *buf, int size) +{ + struct nand_param *nandp = &internal_param; + int page_size, row_cycle, bus_width; + int col_addr; + + page_size = nandp->page_size; + row_cycle = nandp->row_cycle; + bus_width = nandp->bus_width; + + if (page_size >= 2048) + col_addr = page_size; + else + col_addr = 0; + + if (page_size >= 2048) + /* Send READ0 command */ + __nand_cmd(NAND_CMD_READ0); + else + /* Send READOOB command */ + __nand_cmd(NAND_CMD_READOOB); + + /* Send column address */ + __nand_addr(col_addr & 0xff); + if (page_size >= 2048) + __nand_addr((col_addr >> 8) & 0xff); + + /* Send page address */ + __nand_addr(page_addr & 0xff); + __nand_addr((page_addr >> 8) & 0xff); + if (row_cycle == 3) + __nand_addr((page_addr >> 16) & 0xff); + + /* Send READSTART command for 2048 ps NAND */ + if (page_size >= 2048) + __nand_cmd(NAND_CMD_READSTART); + + /* Wait for device ready */ + jz_nand_wait_ready(); + + /* Read oob data */ + jz_nand_read_buf(buf, size, bus_width); + + return 0; +} + + +/* + * nand_read_page() + * + * Input: + * + * block - block number: 0, 1, 2, ... + * page - page number within a block: 0, 1, 2, ... + * dst - pointer to target buffer + */ +static int jz_nand_read_page(unsigned long page_addr, unsigned char *dst) +{ + struct nand_param *nandp = &internal_param; + int page_size, oob_size, page_per_block; + int row_cycle, bus_width, ecc_count; + int i; +#ifdef USE_ECC + int j; +#endif + unsigned char *data_buf; + unsigned char oob_buf[nandp->oob_size]; + + page_size = nandp->page_size; + oob_size = nandp->oob_size; + page_per_block = nandp->page_per_block; + row_cycle = nandp->row_cycle; + bus_width = nandp->bus_width; + + /* + * Read oob data + */ + jz_nand_read_oob(page_addr, oob_buf, oob_size); + + /* + * Read page data + */ + + /* Send READ0 command */ + __nand_cmd(NAND_CMD_READ0); + + /* Send column address */ + __nand_addr(0); + if (page_size >= 2048) + __nand_addr(0); + + /* Send page address */ + __nand_addr(page_addr & 0xff); + __nand_addr((page_addr >> 8) & 0xff); + if (row_cycle >= 3) + __nand_addr((page_addr >> 16) & 0xff); + + /* Send READSTART command for 2048 ps NAND */ + if (page_size >= 2048) + __nand_cmd(NAND_CMD_READSTART); + + /* Wait for device ready */ + jz_nand_wait_ready(); + + /* Read page data */ + data_buf = dst; + + ecc_count = page_size / ECC_BLOCK; + + for (i = 0; i < ecc_count; i++) + { +#ifdef USE_ECC + volatile unsigned char *paraddr = (volatile unsigned char *)EMC_NFPAR0; + unsigned int stat; + + /* Enable RS decoding */ + REG_EMC_NFINTS = 0x0; + __ecc_decoding_4bit(); +#endif + + /* Read data */ + jz_nand_read_buf((void *)data_buf, ECC_BLOCK, bus_width); + +#ifdef USE_ECC + /* Set PAR values */ + for (j = 0; j < PAR_SIZE; j++) + *paraddr++ = oob_buf[ECC_POS + i*PAR_SIZE + j]; + + /* Set PRDY */ + REG_EMC_NFECR |= EMC_NFECR_PRDY; + + /* Wait for completion */ + __ecc_decode_sync(); + + /* Disable decoding */ + __ecc_disable(); + + /* Check result of decoding */ + stat = REG_EMC_NFINTS; + if (stat & EMC_NFINTS_ERR) + { + /* Error occurred */ + if (stat & EMC_NFINTS_UNCOR) + { + /* Uncorrectable error occurred */ + logf("Uncorrectable ECC error at NAND page address 0x%lx", page_addr); + return -1; + } + else + { + unsigned int errcnt, index, mask; + + errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT; + switch (errcnt) + { + case 4: + index = (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT; + mask = (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT; + jz_rs_correct(data_buf, index, mask); + case 3: + index = (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT; + mask = (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT; + jz_rs_correct(data_buf, index, mask); + case 2: + index = (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT; + mask = (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT; + jz_rs_correct(data_buf, index, mask); + case 1: + index = (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT; + mask = (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT; + jz_rs_correct(data_buf, index, mask); + break; + default: + break; + } + } + } +#endif + + data_buf += ECC_BLOCK; + } + + return 0; +} + +static int jz_nand_init(void) +{ + unsigned char cData[5]; + + __gpio_as_nand_16bit(1); + + REG_NEMC_SMCR1 = CFG_NAND_SMCR1 | 0x40; + + __nand_select(); + + __nand_cmd(NAND_CMD_READID); + __nand_addr(NAND_CMD_READ0); + cData[0] = __nand_data8(); + cData[1] = __nand_data8(); + cData[2] = __nand_data8(); + cData[3] = __nand_data8(); + cData[4] = __nand_data8(); + + __nand_deselect(); + + logf("NAND chip %d: 0x%x 0x%x 0x%x 0x%x 0x%x", i+1, cData[0], cData[1], + cData[2], cData[3], cData[4]); + + bank = nand_identify(cData); + + if(bank == NULL) + { + panicf("Unknown NAND flash chip: 0x%x 0x%x 0x%x 0x%x 0x%x", cData[0], + cData[1], cData[2], cData[3], cData[4]); + return -1; /* panicf() doesn't return though */ + } + + chip_info = bank; + + internal_param.bus_width = 16; + internal_param.row_cycle = chip_info->row_cycles; + internal_param.page_size = chip_info->page_size; + internal_param.oob_size = chip_info->spare_size; + internal_param.page_per_block = chip_info->pages_per_block; + internal_param.bad_block_pos = 0; + + nand_size = ((chip_info->page_size * chip_info->blocks_per_bank * chip_info->pages_per_block) - 0x200000) / 512; + + return 0; +} + +int nand_init(void) +{ + int res = 0; + static bool inited = false; + + if(!inited) + { + res = jz_nand_init(); + mutex_init(&nand_mtx); +#ifdef USE_DMA + mutex_init(&nand_dma_mtx); + semaphore_init(&nand_dma_complete, 1, 0); + system_enable_irq(DMA_IRQ(DMA_NAND_CHANNEL)); +#endif + + inited = true; + } + + return res; +} + +static inline int read_sector(unsigned long start, unsigned int count, + void* buf, unsigned int chip_size) +{ + register int ret; + + if(UNLIKELY(start % chip_size == 0 && count == chip_size)) + ret = jz_nand_read_page(start / chip_size, buf); + else + { + ret = jz_nand_read_page(start / chip_size, temp_page); + memcpy(buf, temp_page + (start % chip_size), count); + } + + return ret; +} + +static inline int write_sector(unsigned long start, unsigned int count, + const void* buf, unsigned int chip_size) +{ + int ret = 0; + + (void)start; + (void)count; + (void)buf; + (void)chip_size; + + /* TODO */ + + return ret; +} + +int nand_read_sectors(IF_MV(int drive,) unsigned long start, int count, void* buf) +{ +#ifdef HAVE_MULTIVOLUME + (void)drive; +#endif + int ret = 0; + unsigned int _count, chip_size = chip_info->page_size; + unsigned long _start; + + logf("start"); + mutex_lock(&nand_mtx); + + _start = start << 9; + _start += 0x200000; /* skip BL */ + _count = count << 9; + + __nand_select(); + ret = read_sector(_start, _count, buf, chip_size); + __nand_deselect(); + + mutex_unlock(&nand_mtx); + + logf("nand_read_sectors(%ld, %d, 0x%x): %d", start, count, (int)buf, ret); + + return ret; +} + +int nand_write_sectors(IF_MV(int drive,) unsigned long start, int count, const void* buf) +{ +#ifdef HAVE_MULTIVOLUME + (void)drive; +#endif + int ret = 0; + unsigned int _count, chip_size = chip_info->page_size; + unsigned long _start; + + logf("start"); + mutex_lock(&nand_mtx); + + _start = start << 9; + _start += chip_info->page_size * chip_info->pages_per_block; /* skip BL */ + _count = count << 9; + + __nand_select(); + ret = write_sector(_start, _count, buf, chip_size); + __nand_deselect(); + + mutex_unlock(&nand_mtx); + + logf("nand_write_sectors(%ld, %d, 0x%x): %d", start, count, (int)buf, ret); + + return ret; +} + +#ifdef HAVE_STORAGE_FLUSH +int nand_flush(void) +{ + return 0; +} +#endif + +void nand_spindown(int seconds) +{ + /* null */ + (void)seconds; +} + +void nand_sleep(void) +{ + /* null */ +} + +void nand_spin(void) +{ + /* null */ +} + +void nand_enable(bool on) +{ + /* null - flash controller is enabled/disabled as needed. */ + (void)on; +} + +/* TODO */ +long nand_last_disk_activity(void) +{ + return 0; +} + +int nand_spinup_time(void) +{ + return 0; +} + +void nand_sleepnow(void) +{ +} + +#ifdef STORAGE_GET_INFO +void nand_get_info(IF_MV(int drive,) struct storage_info *info) +{ +#ifdef HAVE_MULTIVOLUME + (void)drive; +#endif + + /* firmware version */ + info->revision="0.00"; + + info->vendor="Rockbox"; + info->product="NAND Storage"; + + /* blocks count */ + info->num_sectors = nand_size; + info->sector_size = 512; +} +#endif + +#ifdef CONFIG_STORAGE_MULTI +int nand_num_drives(int first_drive) +{ + /* We don't care which logical drive number(s) we have been assigned */ + (void)first_drive; + + return 1; +} +#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 @@ #include "config.h" #include "gcc_extensions.h" -#include "jz4740.h" +#include "cpu.h" #include "ata-sd-target.h" #include "led.h" #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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "gcc_extensions.h" +#include "cpu.h" +#include "ata-sd-target.h" +#include "dma-target.h" +#include "led.h" +#include "sdmmc.h" +#include "logf.h" +#include "storage.h" +#include "string.h" + +static long last_disk_activity = -1; +static tCardInfo card[NUM_DRIVES]; + +static struct mutex sd_mtx; +static struct semaphore sd_wakeup; + +static int use_4bit[NUM_DRIVES]; +static int num_6[NUM_DRIVES]; +static int sd2_0[NUM_DRIVES]; + +#define SD_DMA_ENABLE 1 + +//#define DEBUG(x...) logf(x) +#define DEBUG(x, ...) + +/* volumes */ +#define SD_SLOT_1 0 /* SD card 1 */ +#define SD_SLOT_2 1 /* SD card 2 */ + +#define MSC_CHN(n) (2-n) + +#define SD_IRQ_MASK(n) \ +do { \ + REG_MSC_IMASK(n) = 0xffff; \ + REG_MSC_IREG(n) = 0xffff; \ +} while (0) + +/* Error codes */ +enum sd_result_t +{ + SD_NO_RESPONSE = -1, + SD_NO_ERROR = 0, + SD_ERROR_OUT_OF_RANGE, + SD_ERROR_ADDRESS, + SD_ERROR_BLOCK_LEN, + SD_ERROR_ERASE_SEQ, + SD_ERROR_ERASE_PARAM, + SD_ERROR_WP_VIOLATION, + SD_ERROR_CARD_IS_LOCKED, + SD_ERROR_LOCK_UNLOCK_FAILED, + SD_ERROR_COM_CRC, + SD_ERROR_ILLEGAL_COMMAND, + SD_ERROR_CARD_ECC_FAILED, + SD_ERROR_CC, + SD_ERROR_GENERAL, + SD_ERROR_UNDERRUN, + SD_ERROR_OVERRUN, + SD_ERROR_CID_CSD_OVERWRITE, + SD_ERROR_STATE_MISMATCH, + SD_ERROR_HEADER_MISMATCH, + SD_ERROR_TIMEOUT, + SD_ERROR_CRC, + SD_ERROR_DRIVER_FAILURE, +}; + +/* Standard MMC/SD clock speeds */ +#define MMC_CLOCK_SLOW 400000 /* 400 kHz for initial setup */ +#define SD_CLOCK_FAST 24000000 /* 24 MHz for SD Cards */ +#define SD_CLOCK_HIGH 48000000 /* 48 MHz for SD Cards */ + +/* Extra commands for state control */ +/* Use negative numbers to disambiguate */ +#define SD_CIM_RESET -1 + +/* Proprietary commands, illegal/reserved according to SD Specification 2.00 */ + /* class 1 */ +#define SD_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ + + /* class 3 */ +#define SD_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ + + /* class 4 */ +#define SD_PROGRAM_CID 26 /* adtc R1 */ +#define SD_PROGRAM_CSD 27 /* adtc R1 */ + + /* class 9 */ +#define SD_GO_IRQ_STATE 40 /* bcr R5 */ + +/* Don't change the order of these; they are used in dispatch tables */ +enum sd_rsp_t +{ + RESPONSE_NONE = 0, + RESPONSE_R1 = 1, + RESPONSE_R1B = 2, + RESPONSE_R2_CID = 3, + RESPONSE_R2_CSD = 4, + RESPONSE_R3 = 5, + RESPONSE_R4 = 6, + RESPONSE_R5 = 7, + RESPONSE_R6 = 8, + RESPONSE_R7 = 9, +}; + +/* + MMC status in R1 + Type + e : error bit + s : status bit + r : detected and set for the actual command response + x : detected and set during command execution. the host must poll + the card by sending status command in order to read these bits. + Clear condition + a : according to the card state + b : always related to the previous command. Reception of + a valid command will clear it (with a delay of one command) + c : clear by read + */ + +#define R1_OUT_OF_RANGE (1 << 31) /* er, c */ +#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ +#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ +#define R1_ERASE_PARAM (1 << 27) /* ex, c */ +#define R1_WP_VIOLATION (1 << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1 << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ +#define R1_CC_ERROR (1 << 20) /* erx, c */ +#define R1_ERROR (1 << 19) /* erx, c */ +#define R1_UNDERRUN (1 << 18) /* ex, c */ +#define R1_OVERRUN (1 << 17) /* ex, c */ +#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ +#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ +#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ +#define R1_ERASE_RESET (1 << 13) /* sr, c */ +#define R1_STATUS(x) (x & 0xFFFFE000) +#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ +#define R1_APP_CMD (1 << 7) /* sr, c */ + +/* These are unpacked versions of the actual responses */ +struct sd_response_r1 +{ + unsigned char cmd; + unsigned int status; +}; + +struct sd_response_r3 +{ + unsigned int ocr; +}; + +#define SD_CARD_BUSY 0x80000000 /* Card Power up status bit */ + +struct sd_request +{ + int index; /* Slot index - used for CS lines */ + int cmd; /* Command to send */ + unsigned int arg; /* Argument to send */ + enum sd_rsp_t rtype; /* Response type expected */ + + /* Data transfer (these may be modified at the low level) */ + unsigned short nob; /* Number of blocks to transfer*/ + unsigned short block_len; /* Block length */ + unsigned char *buffer; /* Data buffer */ + unsigned int cnt; /* Data length, for PIO */ + + /* Results */ + unsigned char response[18]; /* Buffer to store response - CRC is optional */ + enum sd_result_t result; +}; + +#define SD_OCR_ARG 0x00ff8000 /* Argument of OCR */ + +/*********************************************************************** + * SD Events + */ +#define SD_EVENT_NONE 0x00 /* No events */ +#define SD_EVENT_RX_DATA_DONE 0x01 /* Rx data done */ +#define SD_EVENT_TX_DATA_DONE 0x02 /* Tx data done */ +#define SD_EVENT_PROG_DONE 0x04 /* Programming is done */ + +/************************************************************************** + * Utility functions + **************************************************************************/ + +#define PARSE_U32(_buf,_index) \ + (((unsigned int)_buf[_index]) << 24) | (((unsigned int)_buf[_index+1]) << 16) | \ + (((unsigned int)_buf[_index+2]) << 8) | ((unsigned int)_buf[_index+3]); + +#define PARSE_U16(_buf,_index) \ + (((unsigned short)_buf[_index]) << 8) | ((unsigned short)_buf[_index+1]); + +static int sd_unpack_r1(struct sd_request *request, struct sd_response_r1 *r1) +{ + unsigned char *buf = request->response; + + if (request->result) + return request->result; + + r1->cmd = buf[0]; + r1->status = PARSE_U32(buf,1); + + DEBUG("sd_unpack_r1: cmd=%d status=%08x", r1->cmd, r1->status); + + if (R1_STATUS(r1->status)) { + if (r1->status & R1_OUT_OF_RANGE) return SD_ERROR_OUT_OF_RANGE; + if (r1->status & R1_ADDRESS_ERROR) return SD_ERROR_ADDRESS; + if (r1->status & R1_BLOCK_LEN_ERROR) return SD_ERROR_BLOCK_LEN; + if (r1->status & R1_ERASE_SEQ_ERROR) return SD_ERROR_ERASE_SEQ; + if (r1->status & R1_ERASE_PARAM) return SD_ERROR_ERASE_PARAM; + if (r1->status & R1_WP_VIOLATION) return SD_ERROR_WP_VIOLATION; + //if (r1->status & R1_CARD_IS_LOCKED) return SD_ERROR_CARD_IS_LOCKED; + if (r1->status & R1_LOCK_UNLOCK_FAILED) return SD_ERROR_LOCK_UNLOCK_FAILED; + if (r1->status & R1_COM_CRC_ERROR) return SD_ERROR_COM_CRC; + if (r1->status & R1_ILLEGAL_COMMAND) return SD_ERROR_ILLEGAL_COMMAND; + if (r1->status & R1_CARD_ECC_FAILED) return SD_ERROR_CARD_ECC_FAILED; + if (r1->status & R1_CC_ERROR) return SD_ERROR_CC; + if (r1->status & R1_ERROR) return SD_ERROR_GENERAL; + if (r1->status & R1_UNDERRUN) return SD_ERROR_UNDERRUN; + if (r1->status & R1_OVERRUN) return SD_ERROR_OVERRUN; + if (r1->status & R1_CID_CSD_OVERWRITE) return SD_ERROR_CID_CSD_OVERWRITE; + } + + if (buf[0] != request->cmd) + return SD_ERROR_HEADER_MISMATCH; + + /* This should be last - it's the least dangerous error */ + + return 0; +} + +static int sd_unpack_r6(struct sd_request *request, struct sd_response_r1 *r1, unsigned long *rca) +{ + unsigned char *buf = request->response; + + if (request->result) + return request->result; + + *rca = PARSE_U16(buf,1); /* Save RCA returned by the SD Card */ + + *(buf+1) = 0; + *(buf+2) = 0; + + return sd_unpack_r1(request, r1); +} + +static int sd_unpack_r3(struct sd_request *request, struct sd_response_r3 *r3) +{ + unsigned char *buf = request->response; + + if (request->result) return request->result; + + r3->ocr = PARSE_U32(buf,1); + DEBUG("sd_unpack_r3: ocr=%08x", r3->ocr); + + if (buf[0] != 0x3f) + return SD_ERROR_HEADER_MISMATCH; + + return 0; +} + +/* Stop the MMC clock and wait while it happens */ +static inline int jz_sd_stop_clock(const int drive) +{ + register int timeout = 1000; + + //DEBUG("stop MMC clock"); + REG_MSC_STRPCL(MSC_CHN(drive)) = MSC_STRPCL_CLOCK_CONTROL_STOP; + + while (timeout && (REG_MSC_STAT(MSC_CHN(drive)) & MSC_STAT_CLK_EN)) + { + timeout--; + if (timeout == 0) + { + DEBUG("Timeout on stop clock waiting"); + return SD_ERROR_TIMEOUT; + } + udelay(1); + } + //DEBUG("clock off time is %d microsec", timeout); + return SD_NO_ERROR; +} + +/* Start the MMC clock and operation */ +static inline int jz_sd_start_clock(const int drive) +{ + REG_MSC_STRPCL(MSC_CHN(drive)) = MSC_STRPCL_CLOCK_CONTROL_START | MSC_STRPCL_START_OP; + return SD_NO_ERROR; +} + +static int jz_sd_check_status(const int drive, struct sd_request *request) +{ + (void)request; + unsigned int status = REG_MSC_STAT(MSC_CHN(drive)); + + /* Checking for response or data timeout */ + if (status & (MSC_STAT_TIME_OUT_RES | MSC_STAT_TIME_OUT_READ)) + { + DEBUG("SD timeout, MSC_STAT 0x%x CMD %d", status, + request->cmd); + return SD_ERROR_TIMEOUT; + } + + /* Checking for CRC error */ + if (status & + (MSC_STAT_CRC_READ_ERROR | MSC_STAT_CRC_WRITE_ERROR | + MSC_STAT_CRC_RES_ERR)) + { + DEBUG("SD CRC error, MSC_STAT 0x%x", status); + return SD_ERROR_CRC; + + } + + + /* Checking for FIFO empty */ + /*if(status & MSC_STAT_DATA_FIFO_EMPTY && request->rtype != RESPONSE_NONE) + { + DEBUG("SD FIFO empty, MSC_STAT 0x%x", status); + return SD_ERROR_UNDERRUN; + }*/ + + return SD_NO_ERROR; +} + +/* Obtain response to the command and store it to response buffer */ +static void jz_sd_get_response(const int drive, struct sd_request *request) +{ + int i; + unsigned char *buf; + unsigned int data; + + DEBUG("fetch response for request %d, cmd %d", request->rtype, + request->cmd); + buf = request->response; + request->result = SD_NO_ERROR; + + switch (request->rtype) + { + case RESPONSE_R1: + case RESPONSE_R1B: + case RESPONSE_R7: + case RESPONSE_R6: + case RESPONSE_R3: + case RESPONSE_R4: + case RESPONSE_R5: + { + data = REG_MSC_RES(MSC_CHN(drive)); + buf[0] = (data >> 8) & 0xff; + buf[1] = data & 0xff; + data = REG_MSC_RES(MSC_CHN(drive)); + buf[2] = (data >> 8) & 0xff; + buf[3] = data & 0xff; + data = REG_MSC_RES(MSC_CHN(drive)); + buf[4] = data & 0xff; + + DEBUG("request %d, response [%02x %02x %02x %02x %02x]", + request->rtype, buf[0], buf[1], buf[2], + buf[3], buf[4]); + break; + } + case RESPONSE_R2_CID: + case RESPONSE_R2_CSD: + { + for (i = 0; i < 16; i += 2) + { + data = REG_MSC_RES(MSC_CHN(drive)); + buf[i] = (data >> 8) & 0xff; + buf[i + 1] = data & 0xff; + } + DEBUG("request %d, response []", request->rtype); + break; + } + case RESPONSE_NONE: + DEBUG("No response"); + break; + + default: + DEBUG("unhandled response type for request %d", + request->rtype); + break; + } +} + +static int jz_sd_receive_data(const int drive, struct sd_request *req) +{ + unsigned int nob = req->nob; + unsigned int wblocklen = (unsigned int) (req->block_len + 3) >> 2; /* length in word */ + unsigned char *buf = req->buffer; + unsigned int *wbuf = (unsigned int *) buf; + unsigned int waligned = (((unsigned int) buf & 0x3) == 0); /* word aligned ? */ + unsigned int stat, timeout, data, cnt; + + for (; nob >= 1; nob--) + { + timeout = 0x3FFFFFF; + + while (timeout) + { + timeout--; + stat = REG_MSC_STAT(MSC_CHN(drive)); + + if (stat & MSC_STAT_TIME_OUT_READ) + return SD_ERROR_TIMEOUT; + else if (stat & MSC_STAT_CRC_READ_ERROR) + return SD_ERROR_CRC; + else if (!(stat & MSC_STAT_DATA_FIFO_EMPTY) + || (stat & MSC_STAT_DATA_FIFO_AFULL)) + /* Ready to read data */ + break; + + udelay(1); + } + + if (!timeout) + return SD_ERROR_TIMEOUT; + + /* Read data from RXFIFO. It could be FULL or PARTIAL FULL */ + DEBUG("Receive Data = %d", wblocklen); + cnt = wblocklen; + while (cnt) + { + data = REG_MSC_RXFIFO(MSC_CHN(drive)); + if (waligned) + *wbuf++ = data; + else + { + *buf++ = (unsigned char) (data >> 0); + *buf++ = (unsigned char) (data >> 8); + *buf++ = (unsigned char) (data >> 16); + *buf++ = (unsigned char) (data >> 24); + } + cnt--; + while (cnt + && (REG_MSC_STAT(MSC_CHN(drive)) & + MSC_STAT_DATA_FIFO_EMPTY)); + } + } + + return SD_NO_ERROR; +} + +static int jz_sd_transmit_data(const int drive, struct sd_request *req) +{ + unsigned int nob = req->nob; + unsigned int wblocklen = (unsigned int) (req->block_len + 3) >> 2; /* length in word */ + unsigned char *buf = req->buffer; + unsigned int *wbuf = (unsigned int *) buf; + unsigned int waligned = (((unsigned int) buf & 0x3) == 0); /* word aligned ? */ + unsigned int stat, timeout, data, cnt; + + for (; nob >= 1; nob--) + { + timeout = 0x3FFFFFF; + + while (timeout) + { + timeout--; + stat = REG_MSC_STAT(MSC_CHN(drive)); + + if (stat & + (MSC_STAT_CRC_WRITE_ERROR | + MSC_STAT_CRC_WRITE_ERROR_NOSTS)) + return SD_ERROR_CRC; + else if (!(stat & MSC_STAT_DATA_FIFO_FULL)) + /* Ready to write data */ + break; + + udelay(1); + } + + if (!timeout) + return SD_ERROR_TIMEOUT; + + /* Write data to TXFIFO */ + cnt = wblocklen; + while (cnt) + { + while (REG_MSC_STAT(MSC_CHN(drive)) & MSC_STAT_DATA_FIFO_FULL); + + if (waligned) + REG_MSC_TXFIFO(MSC_CHN(drive)) = *wbuf++; + else + { + data = *buf++; + data |= *buf++ << 8; + data |= *buf++ << 16; + data |= *buf++ << 24; + REG_MSC_TXFIFO(MSC_CHN(drive)) = data; + } + + cnt--; + } + } + + return SD_NO_ERROR; +} + +#if SD_DMA_ENABLE +static void jz_sd_receive_data_dma(const int drive, struct sd_request *req) +{ + unsigned int waligned = (((unsigned int)req->buffer & 0x3) == 0); /* word aligned ? */ + unsigned int size = req->block_len * req->nob; + + if (!waligned) + { + jz_sd_receive_data(drive, req); + return; + } + + /* flush dcache */ + dma_cache_wback_inv((unsigned long) req->buffer, size); + + /* setup dma channel */ + REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) = 0; + REG_DMAC_DSAR(DMA_SD_RX_CHANNEL) = PHYSADDR(MSC_RXFIFO(MSC_CHN(drive))); /* DMA source addr */ + REG_DMAC_DTAR(DMA_SD_RX_CHANNEL) = PHYSADDR((unsigned long)req->buffer); /* DMA dest addr */ + REG_DMAC_DTCR(DMA_SD_RX_CHANNEL) = (size + 3) >> 2; /* DMA transfer count */ + REG_DMAC_DRSR(DMA_SD_RX_CHANNEL) = (drive == SD_SLOT_1) ? DMAC_DRSR_RS_MSC2IN : DMAC_DRSR_RS_MSC1IN; /* DMA request type */ + + REG_DMAC_DCMD(DMA_SD_RX_CHANNEL) = + DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_32BIT; + REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; + + /* wait for dma completion */ + while (REG_DMAC_DTCR(DMA_SD_RX_CHANNEL)); + + /* clear status and disable channel */ + REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) = 0; +} + +static void jz_sd_transmit_data_dma(const int drive, struct sd_request *req) +{ + unsigned int waligned = (((unsigned int)req->buffer & 0x3) == 0); /* word aligned ? */ + unsigned int size = req->block_len * req->nob; + + if (!waligned) + { + jz_sd_transmit_data(drive, req); + return; + } + + /* flush dcache */ + dma_cache_wback_inv((unsigned long) req->buffer, size); + + /* setup dma channel */ + REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) = 0; + REG_DMAC_DSAR(DMA_SD_TX_CHANNEL) = PHYSADDR((unsigned long) req->buffer); /* DMA source addr */ + REG_DMAC_DTAR(DMA_SD_TX_CHANNEL) = PHYSADDR(MSC_TXFIFO(MSC_CHN(drive))); /* DMA dest addr */ + REG_DMAC_DTCR(DMA_SD_TX_CHANNEL) = (size + 3) >> 2; /* DMA transfer count */ + REG_DMAC_DRSR(DMA_SD_TX_CHANNEL) = (drive == SD_SLOT_1) ? DMAC_DRSR_RS_MSC2OUT : DMAC_DRSR_RS_MSC1OUT; /* DMA request type */ + + REG_DMAC_DCMD(DMA_SD_TX_CHANNEL) = + DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | + DMAC_DCMD_DS_32BIT; + REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; + + /* wait for dma completion */ + while (REG_DMAC_DTCR(DMA_SD_TX_CHANNEL)); + + /* clear status and disable channel */ + REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) = 0; +} + +void DMA_CALLBACK(DMA_SD_RX_CHANNEL)(void) +{ + if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) & DMAC_DCCSR_AR) + { + logf("SD RX DMA address error"); + REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) &= ~DMAC_DCCSR_AR; + } + + if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) & DMAC_DCCSR_HLT) + { + logf("SD RX DMA halt"); + REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) &= ~DMAC_DCCSR_HLT; + } + + if (REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) & DMAC_DCCSR_TT) + { + REG_DMAC_DCCSR(DMA_SD_RX_CHANNEL) &= ~DMAC_DCCSR_TT; + //sd_rx_dma_callback(); + } +} + +void DMA_CALLBACK(DMA_SD_TX_CHANNEL)(void) +{ + if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) & DMAC_DCCSR_AR) + { + logf("SD TX DMA address error: %x, %x, %x", var1, var2, var3); + REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) &= ~DMAC_DCCSR_AR; + } + + if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) & DMAC_DCCSR_HLT) + { + logf("SD TX DMA halt"); + REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) &= ~DMAC_DCCSR_HLT; + } + + if (REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) & DMAC_DCCSR_TT) + { + REG_DMAC_DCCSR(DMA_SD_TX_CHANNEL) &= ~DMAC_DCCSR_TT; + //sd_tx_dma_callback(); + } +} +#endif /* SD_DMA_ENABLE */ + +static inline unsigned int jz_sd_calc_clkrt(const int drive, unsigned int rate) +{ + unsigned int clkrt; + unsigned int clk_src = sd2_0[drive] ? SD_CLOCK_HIGH : SD_CLOCK_FAST; + + clkrt = 0; + while (rate < clk_src) + { + clkrt++; + clk_src >>= 1; + } + return clkrt; +} + +static inline void cpm_select_msc_clk(unsigned int rate) +{ + unsigned int div = __cpm_get_pllout2() / rate; + + REG_CPM_MSCCDR = div - 1; +} + +/* Set the MMC clock frequency */ +static void jz_sd_set_clock(const int drive, unsigned int rate) +{ + int clkrt; + + jz_sd_stop_clock(drive); + + /* select clock source from CPM */ + cpm_select_msc_clk(rate); + + __cpm_enable_pll_change(); + clkrt = jz_sd_calc_clkrt(drive, rate); + REG_MSC_CLKRT(MSC_CHN(drive)) = clkrt; + + DEBUG("set clock to %u Hz clkrt=%d", rate, clkrt); +} + +/******************************************************************************************************************** +** Name: int jz_sd_exec_cmd() +** Function: send command to the card, and get a response +** Input: struct sd_request *req: SD request +** Output: 0: right >0: error code +********************************************************************************************************************/ +static int jz_sd_exec_cmd(const int drive, struct sd_request *request) +{ + unsigned int cmdat = 0, events = 0; + int retval, timeout = 0x3fffff; + + /* Indicate we have no result yet */ + request->result = SD_NO_RESPONSE; + + if (request->cmd == SD_CIM_RESET) { + /* On reset, 1-bit bus width */ + use_4bit[drive] = 0; + + /* Reset MMC/SD controller */ + __msc_reset(MSC_CHN(drive)); + + /* On reset, drop SD clock down */ + jz_sd_set_clock(drive, MMC_CLOCK_SLOW); + + /* On reset, stop SD clock */ + jz_sd_stop_clock(drive); + } + if (request->cmd == SD_SET_BUS_WIDTH) + { + if (request->arg == 0x2) + { + DEBUG("Use 4-bit bus width"); + use_4bit[drive] = 1; + } + else + { + DEBUG("Use 1-bit bus width"); + use_4bit[drive] = 0; + } + } + + /* stop clock */ + jz_sd_stop_clock(drive); + + /* mask all interrupts */ + //REG_MSC_IMASK(MSC_CHN(drive)) = 0xffff; + /* clear status */ + REG_MSC_IREG(MSC_CHN(drive)) = 0xffff; + /*open interrupt */ + REG_MSC_IMASK(MSC_CHN(drive)) = (~7); + /* use 4-bit bus width when possible */ + if (use_4bit[drive]) + cmdat |= MSC_CMDAT_BUS_WIDTH_4BIT; + + /* Set command type and events */ + switch (request->cmd) + { + /* SD core extra command */ + case SD_CIM_RESET: + cmdat |= MSC_CMDAT_INIT; /* Initialization sequence sent prior to command */ + break; + /* bc - broadcast - no response */ + case SD_GO_IDLE_STATE: + case SD_SET_DSR: + break; + + /* bcr - broadcast with response */ + case SD_APP_OP_COND: + case SD_ALL_SEND_CID: + case SD_GO_IRQ_STATE: + break; + + /* adtc - addressed with data transfer */ + case SD_READ_DAT_UNTIL_STOP: + case SD_READ_SINGLE_BLOCK: + case SD_READ_MULTIPLE_BLOCK: + case SD_SEND_SCR: +#if SD_DMA_ENABLE + cmdat |= + MSC_CMDAT_DATA_EN | MSC_CMDAT_READ | MSC_CMDAT_DMA_EN; +#else + cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_READ; +#endif + events = SD_EVENT_RX_DATA_DONE; + break; + + case 6: + if (num_6[drive] < 2) + { +#if SD_DMA_ENABLE + cmdat |= + MSC_CMDAT_DATA_EN | MSC_CMDAT_READ | + MSC_CMDAT_DMA_EN; +#else + cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_READ; +#endif + events = SD_EVENT_RX_DATA_DONE; + } + break; + + case SD_WRITE_DAT_UNTIL_STOP: + case SD_WRITE_BLOCK: + case SD_WRITE_MULTIPLE_BLOCK: + case SD_PROGRAM_CID: + case SD_PROGRAM_CSD: + case SD_LOCK_UNLOCK: +#if SD_DMA_ENABLE + cmdat |= + MSC_CMDAT_DATA_EN | MSC_CMDAT_WRITE | MSC_CMDAT_DMA_EN; +#else + cmdat |= MSC_CMDAT_DATA_EN | MSC_CMDAT_WRITE; +#endif + events = SD_EVENT_TX_DATA_DONE | SD_EVENT_PROG_DONE; + break; + + case SD_STOP_TRANSMISSION: + events = SD_EVENT_PROG_DONE; + break; + + /* ac - no data transfer */ + default: + break; + } + + /* Set response type */ + switch (request->rtype) + { + case RESPONSE_NONE: + break; + case RESPONSE_R1B: + cmdat |= MSC_CMDAT_BUSY; + /* FALLTHRU */ + case RESPONSE_R1: + case RESPONSE_R7: + cmdat |= MSC_CMDAT_RESPONSE_R1; + break; + case RESPONSE_R2_CID: + case RESPONSE_R2_CSD: + cmdat |= MSC_CMDAT_RESPONSE_R2; + break; + case RESPONSE_R3: + cmdat |= MSC_CMDAT_RESPONSE_R3; + break; + case RESPONSE_R4: + cmdat |= MSC_CMDAT_RESPONSE_R4; + break; + case RESPONSE_R5: + cmdat |= MSC_CMDAT_RESPONSE_R5; + break; + case RESPONSE_R6: + cmdat |= MSC_CMDAT_RESPONSE_R6; + break; + default: + break; + } + + /* Set command index */ + if (request->cmd == SD_CIM_RESET) + REG_MSC_CMD(MSC_CHN(drive)) = SD_GO_IDLE_STATE; + else + REG_MSC_CMD(MSC_CHN(drive)) = request->cmd; + + /* Set argument */ + REG_MSC_ARG(MSC_CHN(drive)) = request->arg; + + /* Set block length and nob */ + if (request->cmd == SD_SEND_SCR) + { /* get SCR from DataFIFO */ + REG_MSC_BLKLEN(MSC_CHN(drive)) = 8; + REG_MSC_NOB(MSC_CHN(drive)) = 1; + } + else + { + REG_MSC_BLKLEN(MSC_CHN(drive)) = request->block_len; + REG_MSC_NOB(MSC_CHN(drive)) = request->nob; + } + + /* Set command */ + REG_MSC_CMDAT(MSC_CHN(drive)) = cmdat; + + DEBUG("Send cmd %d cmdat: %x arg: %x resp %d", request->cmd, + cmdat, request->arg, request->rtype); + + /* Start SD clock and send command to card */ + jz_sd_start_clock(drive); + + /* Wait for command completion */ + //__intc_unmask_irq(IRQ_MSC); + //semaphore_wait(&sd_wakeup, 100); + while (timeout-- && !(REG_MSC_STAT(MSC_CHN(drive)) & MSC_STAT_END_CMD_RES)); + + + if (timeout == 0) + return SD_ERROR_TIMEOUT; + + REG_MSC_IREG(MSC_CHN(drive)) = MSC_IREG_END_CMD_RES; /* clear flag */ + + /* Check for status */ + retval = jz_sd_check_status(drive, request); + if (retval) + return retval; + + /* Complete command with no response */ + if (request->rtype == RESPONSE_NONE) + return SD_NO_ERROR; + + /* Get response */ + jz_sd_get_response(drive, request); + + /* Start data operation */ + if (events & (SD_EVENT_RX_DATA_DONE | SD_EVENT_TX_DATA_DONE)) + { + if (events & SD_EVENT_RX_DATA_DONE) + { + if (request->cmd == SD_SEND_SCR) + { + /* SD card returns SCR register as data. + SD core expect it in the response buffer, + after normal response. */ + request->buffer = + (unsigned char *) ((unsigned int) request->response + 5); + } +#if SD_DMA_ENABLE + jz_sd_receive_data_dma(drive, request); +#else + jz_sd_receive_data(drive, request); +#endif + } + + if (events & SD_EVENT_TX_DATA_DONE) + { +#if SD_DMA_ENABLE + jz_sd_transmit_data_dma(drive, request); +#else + jz_sd_transmit_data(drive, request); +#endif + } + //__intc_unmask_irq(IRQ_MSC); + //semaphore_wait(&sd_wakeup, 100); + /* Wait for Data Done */ + while (!(REG_MSC_IREG(MSC_CHN(drive)) & MSC_IREG_DATA_TRAN_DONE)); + REG_MSC_IREG(MSC_CHN(drive)) = MSC_IREG_DATA_TRAN_DONE; /* clear status */ + } + + /* Wait for Prog Done event */ + if (events & SD_EVENT_PROG_DONE) + { + //__intc_unmask_irq(IRQ_MSC); + //semaphore_wait(&sd_wakeup, 100); + while (!(REG_MSC_IREG(MSC_CHN(drive)) & MSC_IREG_PRG_DONE)); + REG_MSC_IREG(MSC_CHN(drive)) = MSC_IREG_PRG_DONE; /* clear status */ + } + + /* Command completed */ + + return SD_NO_ERROR; /* return successfully */ +} + +/******************************************************************************************************************* +** Name: int sd_chkcard() +** Function: check whether card is insert entirely +** Input: NULL +** Output: 1: insert entirely 0: not insert entirely +********************************************************************************************************************/ +static int jz_sd_chkcard(const int drive) +{ + return (__gpio_get_pin((drive == SD_SLOT_1) ? PIN_SD1_CD : PIN_SD2_CD) == 0 ? 1 : 0); +} + +/* MSC interrupt handler */ +void MSC(void) +{ + //semaphore_release(&sd_wakeup); + logf("MSC interrupt"); +} + +#ifdef HAVE_HOTSWAP +static void sd_gpio_setup_irq(const int drive, bool inserted) +{ + int pin = (drive == SD_SLOT_1) ? PIN_SD1_CD : PIN_SD2_CD; + int irq = (drive == SD_SLOT_1) ? IRQ_SD1_CD : IRQ_SD2_CD; + if(inserted) + __gpio_as_irq_rise_edge(pin); + else + __gpio_as_irq_fall_edge(pin); + system_enable_irq(irq); +} +#endif + +/******************************************************************************************************************* +** Name: void sd_hardware_init() +** Function: initialize the hardware condiction that access sd card +** Input: NULL +** Output: NULL +********************************************************************************************************************/ +static void jz_sd_hardware_init(const int drive) +{ + if (drive == SD_SLOT_1) + __cpm_start_msc2(); /* enable mmc2 clock */ + else + __cpm_start_msc1(); /* enable mmc1 clock */ +#ifdef HAVE_HOTSWAP + sd_gpio_setup_irq(drive, jz_sd_chkcard(drive)); +#endif + __msc_reset(MSC_CHN(drive)); /* reset mmc/sd controller */ + SD_IRQ_MASK(MSC_CHN(drive)); /* mask all IRQs */ + jz_sd_stop_clock(drive); /* stop SD clock */ +} + +static int sd_send_cmd(const int drive, struct sd_request *request, int cmd, unsigned int arg, + unsigned short nob, unsigned short block_len, + enum sd_rsp_t rtype, unsigned char* buffer) +{ + request->cmd = cmd; + request->arg = arg; + request->rtype = rtype; + request->nob = nob; + request->block_len = block_len; + request->buffer = buffer; + request->cnt = nob * block_len; + + return jz_sd_exec_cmd(drive, request); +} + +static void sd_simple_cmd(const int drive, struct sd_request *request, int cmd, unsigned int arg, + enum sd_rsp_t rtype) +{ + sd_send_cmd(drive, request, cmd, arg, 0, 0, rtype, NULL); +} + +#define SD_INIT_DOING 0 +#define SD_INIT_PASSED 1 +#define SD_INIT_FAILED 2 +static int sd_init_card_state(const int drive, struct sd_request *request) +{ + struct sd_response_r1 r1; + struct sd_response_r3 r3; + int retval, i, ocr = 0x40300000, limit_41 = 0; + + switch (request->cmd) + { + case SD_GO_IDLE_STATE: /* No response to parse */ + sd_simple_cmd(drive, request, SD_SEND_IF_COND, 0x1AA, RESPONSE_R1); + break; + + case SD_SEND_IF_COND: + retval = sd_unpack_r1(request, &r1); + sd_simple_cmd(drive, request, SD_APP_CMD, 0, RESPONSE_R1); + break; + + case SD_APP_CMD: + retval = sd_unpack_r1(request, &r1); + if (retval & (limit_41 < 100)) + { + DEBUG("sd_init_card_state: unable to SD_APP_CMD error=%d", + retval); + limit_41++; + sd_simple_cmd(drive, request, SD_APP_OP_COND, ocr, RESPONSE_R3); + } + else if (limit_41 < 100) + { + limit_41++; + sd_simple_cmd(drive, request, SD_APP_OP_COND, ocr, RESPONSE_R3); + } + else + /* reset the card to idle*/ + sd_simple_cmd(drive, request, SD_GO_IDLE_STATE, 0, RESPONSE_NONE); + break; + + case SD_APP_OP_COND: + retval = sd_unpack_r3(request, &r3); + if (retval) + break; + + DEBUG("sd_init_card_state: read ocr value = 0x%08x", r3.ocr); + card[drive].ocr = r3.ocr; + + if(!(r3.ocr & SD_CARD_BUSY || ocr == 0)) + { + sleep(HZ / 100); + sd_simple_cmd(drive, request, SD_APP_CMD, 0, RESPONSE_R1); + } + else + { + /* Set the data bus width to 4 bits */ + use_4bit[drive] = 1; + sd_simple_cmd(drive, request, SD_ALL_SEND_CID, 0, RESPONSE_R2_CID); + } + break; + + case SD_ALL_SEND_CID: + for(i=0; i<4; i++) + card[drive].cid[i] = ((request->response[1+i*4]<<24) | (request->response[2+i*4]<<16) | + (request->response[3+i*4]<< 8) | request->response[4+i*4]); + + logf("CID: %08lx%08lx%08lx%08lx", card[drive].cid[0], card[drive].cid[1], card[drive].cid[2], card[drive].cid[3]); + sd_simple_cmd(drive, request, SD_SEND_RELATIVE_ADDR, 0, RESPONSE_R6); + break; + case SD_SEND_RELATIVE_ADDR: + retval = sd_unpack_r6(request, &r1, &card[drive].rca); + card[drive].rca = card[drive].rca << 16; + DEBUG("sd_init_card_state: Get RCA from SD: 0x%04lx Status: %x", card[drive].rca, r1.status); + if (retval) + { + DEBUG("sd_init_card_state: unable to SET_RELATIVE_ADDR error=%d", + retval); + return SD_INIT_FAILED; + } + + sd_simple_cmd(drive, request, SD_SEND_CSD, card[drive].rca, RESPONSE_R2_CSD); + break; + + case SD_SEND_CSD: + for(i=0; i<4; i++) + card[drive].csd[i] = ((request->response[1+i*4]<<24) | (request->response[2+i*4]<<16) | + (request->response[3+i*4]<< 8) | request->response[4+i*4]); + + sd_parse_csd(&card[drive]); + sd2_0[drive] = (card_extract_bits(card[drive].csd, 127, 2) == 1); + + logf("CSD: %08lx%08lx%08lx%08lx", card[drive].csd[0], card[drive].csd[1], card[drive].csd[2], card[drive].csd[3]); + DEBUG("SD card is ready"); + jz_sd_set_clock(drive, SD_CLOCK_FAST); + return SD_INIT_PASSED; + + default: + DEBUG("sd_init_card_state: error! Illegal last cmd %d", request->cmd); + return SD_INIT_FAILED; + } + + return SD_INIT_DOING; +} + +static int sd_switch(const int drive, struct sd_request *request, int mode, int group, + unsigned char value, unsigned char * resp) +{ + unsigned int arg; + + mode = !!mode; + value &= 0xF; + arg = (mode << 31 | 0x00FFFFFF); + arg &= ~(0xF << (group * 4)); + arg |= value << (group * 4); + sd_send_cmd(drive, request, 6, arg, 1, 64, RESPONSE_R1, resp); + + return 0; +} + +/* + * Fetches and decodes switch information + */ +static int sd_read_switch(const int drive, struct sd_request *request) +{ + unsigned int status[64 / 4]; + + memset((unsigned char *)status, 0, 64); + sd_switch(drive, request, 0, 0, 1, (unsigned char*) status); + + if (((unsigned char *)status)[13] & 0x02) + return 0; + else + return 1; +} + +/* + * Test if the card supports high-speed mode and, if so, switch to it. + */ +static int sd_switch_hs(const int drive, struct sd_request *request) +{ + unsigned int status[64 / 4]; + + sd_switch(drive, request, 1, 0, 1, (unsigned char*) status); + return 0; +} + +static int sd_select_card(const int drive) +{ + struct sd_request request; + struct sd_response_r1 r1; + int retval; + + sd_simple_cmd(drive, &request, SD_SELECT_CARD, card[drive].rca, + RESPONSE_R1B); + retval = sd_unpack_r1(&request, &r1); + if (retval) + return retval; + + if (sd2_0[drive]) + { + retval = sd_read_switch(drive, &request); + if (!retval) + { + sd_switch_hs(drive, &request); + jz_sd_set_clock(drive, SD_CLOCK_HIGH); + } + } + num_6[drive] = 3; + sd_simple_cmd(drive, &request, SD_APP_CMD, card[drive].rca, + RESPONSE_R1); + retval = sd_unpack_r1(&request, &r1); + if (retval) + return retval; + sd_simple_cmd(drive, &request, SD_SET_BUS_WIDTH, 2, RESPONSE_R1); + retval = sd_unpack_r1(&request, &r1); + if (retval) + return retval; + + card[drive].initialized = 1; + + return 0; +} + +static int sd_init_device(const int drive) +{ + int retval = 0; + struct sd_request init_req; + register int timeout = 1000; + + mutex_lock(&sd_mtx); + + /* Initialise card data as blank */ + memset(&card[drive], 0, sizeof(tCardInfo)); + + sd2_0[drive] = 0; + num_6[drive] = 0; + use_4bit[drive] = 0; + + /* reset mmc/sd controller */ + jz_sd_hardware_init(drive); + + sd_simple_cmd(drive, &init_req, SD_CIM_RESET, 0, RESPONSE_NONE); + sd_simple_cmd(drive, &init_req, SD_GO_IDLE_STATE, 0, RESPONSE_NONE); + + sleep(HZ/2); /* Give the card/controller some rest */ + + while(timeout-- && ((retval = sd_init_card_state(drive, &init_req)) == SD_INIT_DOING)); + retval = (retval == SD_INIT_PASSED ? sd_select_card(drive) : -1); + + if (drive == SD_SLOT_1) + __cpm_stop_msc2(); /* disable SD1 clock */ + else + __cpm_stop_msc1(); /* disable SD2 clock */ + + mutex_unlock(&sd_mtx); + + return retval; +} + +int sd_init(void) +{ + static bool inited = false; + + sd_init_gpio(); /* init GPIO */ + +#if SD_DMA_ENABLE + __dmac_channel_enable_clk(DMA_SD_RX_CHANNEL); + __dmac_channel_enable_clk(DMA_SD_TX_CHANNEL); +#endif + + if(!inited) + { + semaphore_init(&sd_wakeup, 1, 0); + mutex_init(&sd_mtx); + inited = true; + } + + for (int drive = 0; drive < NUM_DRIVES; drive++) + sd_init_device(drive); + + return 0; +} + +static inline bool card_detect_target(const int drive) +{ + return (jz_sd_chkcard(drive) == 1); +} + +tCardInfo* card_get_info_target(const int drive) +{ + return &card[drive]; +} + +static inline void sd_start_transfer(const int drive) +{ + mutex_lock(&sd_mtx); + if (drive == SD_SLOT_1) + __cpm_start_msc2(); + else + __cpm_start_msc1(); + led(true); +} + +static inline void sd_stop_transfer(const int drive) +{ + led(false); + if (drive == SD_SLOT_1) + __cpm_stop_msc2(); + else + __cpm_stop_msc1(); + mutex_unlock(&sd_mtx); +} + +int sd_read_sectors(const int drive, unsigned long start, int count, void* buf) +{ + sd_start_transfer(drive); + + struct sd_request request; + struct sd_response_r1 r1; + int retval = -1; + + if (!card_detect_target(drive) || count == 0 || start > card[drive].numblocks) + goto err; + + if(card[drive].initialized == 0 && !sd_init_device(drive)) + goto err; + + sd_simple_cmd(drive, &request, SD_SEND_STATUS, card[drive].rca, RESPONSE_R1); + retval = sd_unpack_r1(&request, &r1); + if (retval && (retval != SD_ERROR_STATE_MISMATCH)) + goto err; + + sd_simple_cmd(drive, &request, SD_SET_BLOCKLEN, SD_BLOCK_SIZE, RESPONSE_R1); + if ((retval = sd_unpack_r1(&request, &r1))) + goto err; + + if (sd2_0[drive]) + { + sd_send_cmd(drive, &request, SD_READ_MULTIPLE_BLOCK, start, + count, SD_BLOCK_SIZE, RESPONSE_R1, buf); + if ((retval = sd_unpack_r1(&request, &r1))) + goto err; + } + else + { + sd_send_cmd(drive, &request, SD_READ_MULTIPLE_BLOCK, + start * SD_BLOCK_SIZE, count, + SD_BLOCK_SIZE, RESPONSE_R1, buf); + if ((retval = sd_unpack_r1(&request, &r1))) + goto err; + } + + last_disk_activity = current_tick; + + sd_simple_cmd(drive, &request, SD_STOP_TRANSMISSION, 0, RESPONSE_R1B); + if ((retval = sd_unpack_r1(&request, &r1))) + goto err; + +err: + sd_stop_transfer(drive); + + return retval; +} + +int sd_write_sectors(const int drive, unsigned long start, int count, const void* buf) +{ + sd_start_transfer(drive); + + struct sd_request request; + struct sd_response_r1 r1; + int retval = -1; + + if (!card_detect_target(drive) || count == 0 || start > card[drive].numblocks) + goto err; + + if(card[drive].initialized == 0 && !sd_init_device(drive)) + goto err; + + sd_simple_cmd(drive, &request, SD_SEND_STATUS, card[drive].rca, RESPONSE_R1); + retval = sd_unpack_r1(&request, &r1); + if (retval && (retval != SD_ERROR_STATE_MISMATCH)) + goto err; + + sd_simple_cmd(drive, &request, SD_SET_BLOCKLEN, SD_BLOCK_SIZE, RESPONSE_R1); + if ((retval = sd_unpack_r1(&request, &r1))) + goto err; + + if (sd2_0[drive]) + { + sd_send_cmd(drive, &request, SD_WRITE_MULTIPLE_BLOCK, start, + count, SD_BLOCK_SIZE, RESPONSE_R1, + (void*)buf); + if ((retval = sd_unpack_r1(&request, &r1))) + goto err; + } + else + { + sd_send_cmd(drive, &request, SD_WRITE_MULTIPLE_BLOCK, + start * SD_BLOCK_SIZE, count, + SD_BLOCK_SIZE, RESPONSE_R1, (void*)buf); + if ((retval = sd_unpack_r1(&request, &r1))) + goto err; + } + + last_disk_activity = current_tick; + + sd_simple_cmd(drive, &request, SD_STOP_TRANSMISSION, 0, RESPONSE_R1B); + if ((retval = sd_unpack_r1(&request, &r1))) + goto err; + +err: + sd_stop_transfer(drive); + + return retval; +} + +long sd_last_disk_activity(void) +{ + return last_disk_activity; +} + +int sd_spinup_time(void) +{ + return 0; +} + +void sd_enable(bool on) +{ + (void)on; +} + +bool sd_disk_is_active(void) +{ + return false; +} + +int sd_soft_reset(void) +{ + return 0; +} + +#ifdef HAVE_HOTSWAP +bool sd_removable(const int drive) +{ + (void)drive; + return true; +} + +static int sd1_oneshot_callback(struct timeout *tmo) +{ + int state = card_detect_target(SD_SLOT_1); + + /* This is called only if the state was stable for 300ms - check state + * and post appropriate event. */ + queue_broadcast(state ? SYS_HOTSWAP_INSERTED : SYS_HOTSWAP_EXTRACTED, + 0); + + sd_gpio_setup_irq(SD_SLOT_1, state); + + return 0; + (void)tmo; +} + +static int sd2_oneshot_callback(struct timeout *tmo) +{ + int state = card_detect_target(SD_SLOT_2); + + /* This is called only if the state was stable for 300ms - check state + * and post appropriate event. */ + queue_broadcast(state ? SYS_HOTSWAP_INSERTED : SYS_HOTSWAP_EXTRACTED, + 1); + + sd_gpio_setup_irq(SD_SLOT_2, state); + + return 0; + (void)tmo; +} + +/* called on insertion/removal interrupt */ +void GPIO_SD1_CD(void) +{ + static struct timeout sd1_oneshot; + timeout_register(&sd1_oneshot, sd1_oneshot_callback, (3*HZ/10), 0); +} + +void GPIO_SD2_CD(void) +{ + static struct timeout sd2_oneshot; + timeout_register(&sd2_oneshot, sd2_oneshot_callback, (3*HZ/10), 0); +} +#endif + +bool sd_present(const int drive) +{ + return card_detect_target(drive); +} + +#ifdef CONFIG_STORAGE_MULTI +int sd_num_drives(int first_drive) +{ + return NUM_DRIVES; +} +#endif /* CONFIG_STORAGE_MULTI */ + +int sd_event(long id, intptr_t data) +{ + int rc = 0; + + switch (id) + { +#ifdef HAVE_HOTSWAP + case SYS_HOTSWAP_INSERTED: + case SYS_HOTSWAP_EXTRACTED: + /* Force card init for new card, re-init for re-inserted one or + * clear if the last attempt to init failed with an error. */ + mutex_lock(&sd_mtx); /* lock-out card activity */ + card[data].initialized = 0; + mutex_unlock(&sd_mtx); + break; +#endif /* HAVE_HOTSWAP */ + default: + rc = storage_event_default_handler(id, data, last_disk_activity, + STORAGE_SD); + break; + } + + return rc; +} 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 @@ bool backlight_hw_init(void); void backlight_hw_on(void); void backlight_hw_off(void); +#ifdef HAVE_BACKLIGHT_BRIGHTNESS void backlight_hw_brightness(int brightness); +#endif /* HAVE_BACKLIGHT_BRIGHTNESS */ #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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "audio.h" +#include "sound.h" +#include "cpu.h" +#include "system.h" +#include "pcm_sw_volume.h" +#include "cs4398.h" +#include "kernel.h" + +#define PIN_CS_RST (32*1+10) +#define PIN_CODEC_PWRON (32*1+13) +#define PIN_AP_MUTE (32*1+14) +#define PIN_JD_CON (32*1+16) + +static void pop_ctrl(const int val) +{ + if(val) + __gpio_clear_pin(PIN_JD_CON); + else + __gpio_set_pin(PIN_JD_CON); +} + +static void amp_enable(const int val) +{ + if(val) + __gpio_set_pin(PIN_CODEC_PWRON); + else + __gpio_clear_pin(PIN_CODEC_PWRON); +} + +static void dac_enable(const int val) +{ + if(val) + __gpio_set_pin(PIN_CS_RST); + else + __gpio_clear_pin(PIN_CS_RST); +} + +static void ap_mute(bool mute) +{ + if(mute) + __gpio_clear_pin(PIN_AP_MUTE); + else + __gpio_set_pin(PIN_AP_MUTE); +} + +static void audiohw_mute(bool mute) +{ + if(mute) + cs4398_write_reg(CS4398_REG_MUTE, cs4398_read_reg(CS4398_REG_MUTE) | CS4398_MUTE_A | CS4398_MUTE_B); + else + cs4398_write_reg(CS4398_REG_MUTE, cs4398_read_reg(CS4398_REG_MUTE) & ~(CS4398_MUTE_A | CS4398_MUTE_B)); +} + +void audiohw_preinit(void) +{ + cs4398_write_reg(CS4398_REG_MISC, CS4398_CPEN | CS4398_PDN); + cs4398_write_reg(CS4398_REG_MODECTL, CS4398_FM_SINGLE | CS4398_DEM_NONE | CS4398_DIF_LJUST); + cs4398_write_reg(CS4398_REG_VOLMIX, CS4398_ATAPI_A_L | CS4398_ATAPI_B_R); + cs4398_write_reg(CS4398_REG_MUTE, CS4398_MUTEP_LOW); + cs4398_write_reg(CS4398_REG_VOL_A, 0xff); + cs4398_write_reg(CS4398_REG_VOL_B, 0xff); + cs4398_write_reg(CS4398_REG_RAMPFILT, CS4398_ZERO_CROSS | CS4398_SOFT_RAMP); + cs4398_write_reg(CS4398_REG_MISC, CS4398_CPEN); +} + +void audiohw_init(void) +{ + __gpio_as_func1(3*32+12); // BCK + __gpio_as_func0(3*32+13); // LRCK + __gpio_as_func2(4*32+5); // MCLK + __gpio_as_func0(4*32+7); // DO + + pop_ctrl(0); + ap_mute(true); + amp_enable(0); + dac_enable(0); + + __gpio_as_output(PIN_JD_CON); + __gpio_as_output(PIN_AP_MUTE); + __gpio_as_output(PIN_CODEC_PWRON); + __gpio_as_output(PIN_CS_RST); + + mdelay(100); + amp_enable(1); + + /* set AIC clk PLL1 */ + __cpm_select_i2sclk_pll(); + __cpm_select_i2sclk_pll1(); + + __cpm_enable_pll_change(); + __cpm_set_i2sdiv(43-1); + + __cpm_start_aic(); + + /* Init AIC */ + __i2s_enable_sclk(); + __i2s_external_codec(); + __i2s_select_msbjustified(); + __i2s_as_master(); + __i2s_enable_transmit_dma(); + __i2s_set_transmit_trigger(24); + __i2s_set_oss_sample_size(16); + __i2s_enable(); + + /* Init DAC */ + dac_enable(1); + udelay(1); + audiohw_preinit(); +} + +static int vol_tenthdb2hw(const int tdb) +{ + if (tdb < CS4398_VOLUME_MIN) { + return 0xff; + } else if (tdb > CS4398_VOLUME_MAX) { + return 0x00; + } else { + return (-tdb/5); + } +} + +void audiohw_set_volume(int vol_l, int vol_r) +{ + cs4398_write_reg(CS4398_REG_VOL_A, vol_tenthdb2hw(vol_l)); + cs4398_write_reg(CS4398_REG_VOL_B, vol_tenthdb2hw(vol_r)); +} + +void audiohw_set_lineout_volume(int vol_l, int vol_r) +{ +#if 0 /* unused */ + cs4398_write_reg(CS4398_REG_VOL_A, vol_tenthdb2hw(vol_l)); + cs4398_write_reg(CS4398_REG_VOL_B, vol_tenthdb2hw(vol_r)); +#else + (void)vol_l; + (void)vol_r; +#endif +} + +void audiohw_set_filter_roll_off(int value) +{ + /* 0 = fast (sharp); + 1 = slow */ + if (value == 0) { + cs4398_write_reg(CS4398_REG_RAMPFILT, cs4398_read_reg(CS4398_REG_RAMPFILT) & ~CS4398_FILT_SEL); + } else { + cs4398_write_reg(CS4398_REG_RAMPFILT, cs4398_read_reg(CS4398_REG_RAMPFILT) | CS4398_FILT_SEL); + } +} + +void pll1_init(unsigned int freq); +void audiohw_set_frequency(int fsel) +{ + unsigned int pll1_speed; + unsigned char mclk_div, bclk_div, func_mode; + + switch(fsel) + { + case HW_FREQ_8: + pll1_speed = 426000000; + mclk_div = 52; + bclk_div = 16; + func_mode = 0; + break; + case HW_FREQ_11: + pll1_speed = 508000000; + mclk_div = 45; + bclk_div = 16; + func_mode = 0; + break; + case HW_FREQ_12: + pll1_speed = 516000000; + mclk_div = 42; + bclk_div = 16; + func_mode = 0; + break; + case HW_FREQ_16: + pll1_speed = 426000000; + mclk_div = 52; + bclk_div = 8; + func_mode = 0; + break; + case HW_FREQ_22: + pll1_speed = 508000000; + mclk_div = 45; + bclk_div = 8; + func_mode = 0; + break; + case HW_FREQ_24: + pll1_speed = 516000000; + mclk_div = 42; + bclk_div = 8; + func_mode = 0; + break; + case HW_FREQ_32: + pll1_speed = 426000000; + mclk_div = 52; + bclk_div = 4; + func_mode = 0; + break; + case HW_FREQ_44: + pll1_speed = 508000000; + mclk_div = 45; + bclk_div = 4; + func_mode = 0; + break; + case HW_FREQ_48: + pll1_speed = 516000000; + mclk_div = 42; + bclk_div = 4; + func_mode = 0; + break; + case HW_FREQ_64: + pll1_speed = 426000000; + mclk_div = 52; + bclk_div = 2; + func_mode = 1; + break; + case HW_FREQ_88: + pll1_speed = 508000000; + mclk_div = 45; + bclk_div = 2; + func_mode = 1; + break; + case HW_FREQ_96: + pll1_speed = 516000000; + mclk_div = 42; + bclk_div = 2; + func_mode = 1; + break; + default: + return; + } + + __i2s_stop_bitclk(); + + /* 0 = Single-Speed Mode (<50KHz); + 1 = Double-Speed Mode (50-100KHz); + 2 = Quad-Speed Mode; (100-200KHz) */ + cs4398_write_reg(CS4398_REG_MODECTL, (cs4398_read_reg(CS4398_REG_MODECTL) & ~CS4398_FM_MASK) | func_mode); + if (func_mode == 2) + cs4398_write_reg(CS4398_REG_MISC, cs4398_read_reg(CS4398_REG_MISC) | CS4398_MCLKDIV2); + else + cs4398_write_reg(CS4398_REG_MISC, cs4398_read_reg(CS4398_REG_MISC) & ~CS4398_MCLKDIV2); + + pll1_init(pll1_speed); + __cpm_enable_pll_change(); + __cpm_set_i2sdiv(mclk_div-1); + __i2s_set_i2sdiv(bclk_div-1); + __i2s_start_bitclk(); +} + +void audiohw_postinit(void) +{ + sleep(HZ); + audiohw_mute(false); + ap_mute(false); + pop_ctrl(1); +} + +void audiohw_close(void) +{ + pop_ctrl(0); + sleep(HZ/10); + ap_mute(true); + audiohw_mute(true); + amp_enable(0); + dac_enable(0); + __i2s_disable(); + __cpm_stop_aic(); + sleep(HZ); + pop_ctrl(1); +} 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 @@ .set noat #ifdef BOOTLOADER +#ifndef XDUOO_X3 /* These will get filled in by scramble */ .word 0 /* Empty */ .word 0 /* Filesize */ @@ -65,6 +66,7 @@ _relocate_loop: bne t1, t2, _relocate_loop sw t3, -4(t1) #endif +#endif _start: 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "system.h" +#include "cpu.h" +#include +#include +#include "lcd.h" +#include "kernel.h" +#include "font.h" +#include "button.h" +#include "timefuncs.h" + +#define CFG_UART_BASE UART1_BASE /* Base of the UART channel */ + +void serial_putc (const char c) +{ + volatile u8 *uart_lsr = (volatile u8 *)(CFG_UART_BASE + OFF_LSR); + volatile u8 *uart_tdr = (volatile u8 *)(CFG_UART_BASE + OFF_TDR); + + if (c == '\n') serial_putc ('\r'); + + /* Wait for fifo to shift out some bytes */ + while ( !((*uart_lsr & (UARTLSR_TDRQ | UARTLSR_TEMT)) == 0x60) ); + + *uart_tdr = (u8)c; +} + +void serial_puts (const char *s) +{ + while (*s) { + serial_putc (*s++); + } +} + +void serial_putsf(const char *format, ...) +{ + static char printfbuf[256]; + int len; + unsigned char *ptr; + va_list ap; + va_start(ap, format); + + ptr = printfbuf; + len = vsnprintf(ptr, sizeof(printfbuf), format, ap); + va_end(ap); + + serial_puts(ptr); + serial_putc('\n'); +} + +void serial_put_hex(unsigned int d) +{ + char c[12]; + int i; + for(i = 0; i < 8;i++) + { + c[i] = (d >> ((7 - i) * 4)) & 0xf; + if(c[i] < 10) + c[i] += 0x30; + else + c[i] += (0x41 - 10); + } + c[8] = '\n'; + c[9] = 0; + serial_puts(c); + +} + +void serial_put_dec(unsigned int d) +{ + char c[16]; + int i; + int j = 0; + int x = d; + + while (x /= 10) + j++; + + for (i = j; i >= 0; i--) { + c[i] = d % 10; + c[i] += 0x30; + d /= 10; + } + c[j + 1] = '\n'; + c[j + 2] = 0; + serial_puts(c); +} + +void serial_dump_data(unsigned char* data, int len) +{ + int i; + for(i=0; i>4) & 0xf; + if(a < 10) + a += 0x30; + else + a += (0x41 - 10); + serial_putc( a ); + + a = (*data) & 0xf; + if(a < 10) + a += 0x30; + else + a += (0x41 - 10); + serial_putc( a ); + + serial_putc( ' ' ); + + data++; + } + + serial_putc( '\n' ); +} + +bool dbg_ports(void) +{ + serial_puts("dbg_ports\n"); + return false; +} + +bool dbg_hw_info(void) +{ + serial_puts("dbg_hw_info\n"); + return false; +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __DMA_TARGET_H_ +#define __DMA_TARGET_H_ + +#include "system.h" +#include + +void memset_dma(void *target, int c, size_t len, unsigned int bits); +void memcpy_dma(void *target, const void *source, size_t len, unsigned int bits); + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "dma-target.h" + +#define MDMA_CHANNEL 0 + +void memset_dma(void *target, int c, size_t len, unsigned int bits) +{ + unsigned int d; + unsigned char *dp; + + if(((unsigned int)target < 0xa0000000) && len) + dma_cache_wback_inv((unsigned long)target, len); + + dp = (unsigned char *)((unsigned int)(&d) | 0xa0000000); + *(dp + 0) = c; + *(dp + 1) = c; + *(dp + 2) = c; + *(dp + 3) = c; + + REG_MDMAC_DCCSR(MDMA_CHANNEL) = 0; + REG_MDMAC_DSAR(MDMA_CHANNEL) = PHYSADDR((unsigned long)dp); + REG_MDMAC_DTAR(MDMA_CHANNEL) = PHYSADDR((unsigned long)target); + REG_MDMAC_DRSR(MDMA_CHANNEL) = DMAC_DRSR_RS_AUTO; + switch (bits) + { + case 8: + REG_MDMAC_DTCR(MDMA_CHANNEL) = len; + REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_8BIT; + break; + case 16: + REG_MDMAC_DTCR(MDMA_CHANNEL) = len / 2; + REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BIT; + break; + case 32: + REG_MDMAC_DTCR(MDMA_CHANNEL) = len / 4; + REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; + break; + default: + return; + } + REG_MDMAC_DCCSR(MDMA_CHANNEL) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; + + while (REG_MDMAC_DTCR(MDMA_CHANNEL)); + + REG_MDMAC_DCCSR(MDMA_CHANNEL) = 0; +} + +void memcpy_dma(void *target, const void *source, size_t len, unsigned int bits) +{ + if(((unsigned int)source < 0xa0000000) && len) + dma_cache_wback_inv((unsigned long)source, len); + + if(((unsigned int)target < 0xa0000000) && len) + dma_cache_wback_inv((unsigned long)target, len); + + REG_MDMAC_DCCSR(MDMA_CHANNEL) = 0; + REG_MDMAC_DSAR(MDMA_CHANNEL) = PHYSADDR((unsigned long)source); + REG_MDMAC_DTAR(MDMA_CHANNEL) = PHYSADDR((unsigned long)target); + REG_MDMAC_DRSR(MDMA_CHANNEL) = DMAC_DRSR_RS_AUTO; + switch (bits) + { + case 8: + REG_MDMAC_DTCR(MDMA_CHANNEL) = len; + REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_8 | DMAC_DCMD_DWDH_8 | DMAC_DCMD_DS_8BIT; + break; + case 16: + REG_MDMAC_DTCR(MDMA_CHANNEL) = len / 2; + REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_16 | DMAC_DCMD_DWDH_16 | DMAC_DCMD_DS_16BIT; + break; + case 32: + REG_MDMAC_DTCR(MDMA_CHANNEL) = len / 4; + REG_MDMAC_DCMD(MDMA_CHANNEL) = DMAC_DCMD_SAI | DMAC_DCMD_DAI | DMAC_DCMD_SWDH_32 | DMAC_DCMD_DWDH_32 | DMAC_DCMD_DS_32BIT; + break; + default: + return; + } + REG_MDMAC_DCCSR(MDMA_CHANNEL) = DMAC_DCCSR_EN | DMAC_DCCSR_NDES; + + while (REG_MDMAC_DTCR(MDMA_CHANNEL)); + + REG_MDMAC_DCCSR(MDMA_CHANNEL) = 0; +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" +#include "system.h" +#include "cpu.h" +#include "logf.h" +#include "i2c.h" + +#define I2C_CHN 1 +#define I2C_CLK 100000 + +#define I2C_READ 1 +#define I2C_WRITE 0 + +#define I2C_M_RD 1 +#define I2C_M_WR 2 + +#define TIMEOUT 100000 + +static char i2c_rwflags; +static int i2c_ctrl_rest = 0; +static unsigned char *msg_buf; +static int cmd_cnt; +static volatile int cmd_flag; +static int r_cnt; +static unsigned char i2c_subaddr = 0; + +/* + * I2C bus protocol basic routines + */ + +/* Interrupt handler */ +void I2C1(void) +{ + int timeout = TIMEOUT; + + if (__i2c_abrt_7b_addr_nack(I2C_CHN)) { + int ret; + cmd_flag = -1; + __i2c_clear_interrupts(ret,I2C_CHN); + REG_I2C_INTM(I2C_CHN) = 0x0; + return; + } + + /* first byte,when length > 1 */ + if (cmd_flag == 0 && cmd_cnt > 1) { + cmd_flag = 1; + if (i2c_rwflags == I2C_M_RD) { + REG_I2C_DC(I2C_CHN) = I2C_READ << 8; + } else { + REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | *msg_buf++; + } + cmd_cnt--; + } + + if (i2c_rwflags == I2C_M_RD) { + if (REG_I2C_STA(I2C_CHN) & I2C_STA_RFNE) { + *msg_buf++ = REG_I2C_DC(I2C_CHN) & 0xff; + r_cnt--; + } + + REG_I2C_DC(I2C_CHN) = I2C_READ << 8; + } else { + REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | *msg_buf++; + } + + cmd_cnt--; + + if (!(cmd_cnt)) { + REG_I2C_INTM(I2C_CHN) = 0x0; + cmd_flag = 2; + if (i2c_rwflags == I2C_M_RD){ + while (r_cnt > 2) { + if ((REG_I2C_STA(I2C_CHN) & I2C_STA_RFNE) && timeout) { + *msg_buf++ = REG_I2C_DC(I2C_CHN) & 0xff; + r_cnt--; + } + if (!(timeout--)) { + cmd_flag = -1; + return; + } + } + } + } + + return; +} + +static int i2c_set_clk(int i2c_clk) +{ + int dev_clk = __cpm_get_pclk(); + int count = 0; + + if (i2c_clk < 0 || i2c_clk > 400000) + goto Set_clk_err; + + count = dev_clk/i2c_clk - 23; + if (count < 0) + goto Set_clk_err; + + if (i2c_clk <= 100000) { + REG_I2C_CTRL(I2C_CHN) = 0x43 | i2c_ctrl_rest; /* standard speed mode*/ + if (count%2 == 0) { + REG_I2C_SHCNT(I2C_CHN) = count/2 + 6 - 5; + REG_I2C_SLCNT(I2C_CHN) = count/2 + 8 + 5; + } else { + REG_I2C_SHCNT(I2C_CHN) = count/2 + 6 -5; + REG_I2C_SLCNT(I2C_CHN) = count/2 + 8 +5 + 1; + } + } else { + REG_I2C_CTRL(I2C_CHN) = 0x45 | i2c_ctrl_rest; /* high speed mode*/ + if (count%2 == 0) { + REG_I2C_FHCNT(I2C_CHN) = count/2 + 6; + REG_I2C_FLCNT(I2C_CHN) = count/2 + 8; + } else { + REG_I2C_FHCNT(I2C_CHN) = count/2 + 6; + REG_I2C_FLCNT(I2C_CHN) = count/2 + 8 + 1; + } + } + return 0; + +Set_clk_err: + + logf("i2c set sclk faild,i2c_clk=%d,dev_clk=%d.\n",i2c_clk,dev_clk); + return -1; +} + +static int i2c_disable(void) +{ + int timeout = TIMEOUT; + + __i2c_disable(I2C_CHN); + while(__i2c_is_enable(I2C_CHN) && (timeout > 0)) { + udelay(1); + timeout--; + } + if(timeout) + return 0; + else + return 1; +} + +static int i2c_enable(void) +{ + int timeout = TIMEOUT; + + __i2c_enable(I2C_CHN); + while(__i2c_is_disable(I2C_CHN) && (timeout > 0)) { + udelay(1); + timeout--; + } + if(timeout) + return 0; + else + return 1; +} + +static void i2c_init_as_master(unsigned char address) +{ + if(i2c_disable()) + logf("i2c not disable\n"); + + i2c_set_clk(I2C_CLK); + + REG_I2C_TAR(I2C_CHN) = address; /* slave id needed write only once */ + REG_I2C_INTM(I2C_CHN) = 0x0; /* unmask all interrupts */ + REG_I2C_TXTL(I2C_CHN) = 0x1; + + if(i2c_enable()) + logf("i2c not enable\n"); +} + +int xfer_read_subaddr(unsigned char subaddr, unsigned char device, unsigned char *buf, int length) +{ + int timeout,r_i = 0; + + cmd_cnt = length; + r_cnt = length; + msg_buf = buf; + i2c_rwflags = I2C_M_RD; + i2c_ctrl_rest = I2C_CTRL_REST; + i2c_init_as_master(device); + + REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | subaddr; + + cmd_flag = 0; + REG_I2C_INTM(I2C_CHN) = 0x10; + timeout = TIMEOUT; + while (cmd_flag != 2 && --timeout) { + if (cmd_flag == -1) { + r_i = 1; + goto R_dev_err; + } + udelay(10); + } + if (!timeout) { + r_i = 4; + goto R_timeout; + } + + while (r_cnt) { + while (!(REG_I2C_STA(I2C_CHN) & I2C_STA_RFNE)) { + if ((cmd_flag == -1) || + (REG_I2C_INTST(I2C_CHN) & I2C_INTST_TXABT) || + REG_I2C_TXABRT(I2C_CHN)) { + int ret; + r_i = 2; + __i2c_clear_interrupts(ret,I2C_CHN); + goto R_dev_err; + } + } + *msg_buf++ = REG_I2C_DC(I2C_CHN) & 0xff; + r_cnt--; + } + + timeout = TIMEOUT; + while ((REG_I2C_STA(I2C_CHN) & I2C_STA_MSTACT) && --timeout) + udelay(10); + if (!timeout){ + r_i = 3; + goto R_timeout; + } + + return 0; + +R_dev_err: +R_timeout: + + i2c_init_as_master(device); + if (r_i == 1) { + logf("Read i2c device 0x%2x failed in r_i = %d :device no ack.\n",device,r_i); + } else if (r_i == 2) { + logf("Read i2c device 0x%2x failed in r_i = %d :i2c abort.\n",device,r_i); + } else if (r_i == 3) { + logf("Read i2c device 0x%2x failed in r_i = %d :waite master inactive timeout.\n",device,r_i); + } else if (r_i == 4) { + logf("Read i2c device 0x%2x failed in r_i = %d.\n",device,r_i); + } else { + logf("Read i2c device 0x%2x failed in r_i = %d.\n",device,r_i); + } + return -1; +} + +int xfer_write_subaddr(unsigned char subaddr, unsigned char device, const unsigned char *buf, int length) +{ + int timeout,w_i = 0; + + cmd_cnt = length; + r_cnt = length; + msg_buf = (unsigned char *)buf; + i2c_rwflags = I2C_M_WR; + i2c_ctrl_rest = I2C_CTRL_REST; + i2c_init_as_master(device); + + REG_I2C_DC(I2C_CHN) = (I2C_WRITE << 8) | subaddr; + + cmd_flag = 0; + REG_I2C_INTM(I2C_CHN) = 0x10; + + timeout = TIMEOUT; + while ((cmd_flag != 2) && (--timeout)) + { + if (cmd_flag == -1){ + w_i = 1; + goto W_dev_err; + } + udelay(10); + } + + timeout = TIMEOUT; + while((!(REG_I2C_STA(I2C_CHN) & I2C_STA_TFE)) && --timeout){ + udelay(10); + } + if (!timeout){ + w_i = 2; + goto W_timeout; + } + + timeout = TIMEOUT; + while (__i2c_master_active(I2C_CHN) && --timeout); + if (!timeout){ + w_i = 3; + goto W_timeout; + } + + if ((length == 1)&& + ((cmd_flag == -1) || + (REG_I2C_INTST(I2C_CHN) & I2C_INTST_TXABT) || + REG_I2C_TXABRT(I2C_CHN))) { + int ret; + w_i = 5; + __i2c_clear_interrupts(ret,I2C_CHN); + goto W_dev_err; + } + + return 0; + +W_dev_err: +W_timeout: + + i2c_init_as_master(device); + if (w_i == 1) { + logf("Write i2c device 0x%2x failed in w_i=%d:device no ack. I2C_CHN:[%d]sxyzhang\n",device,w_i,I2C_CHN); + } else if (w_i == 2) { + logf("Write i2c device 0x%2x failed in w_i=%d:waite TF buff empty timeout.\n",device,w_i); + } else if (w_i == 3) { + logf("Write i2c device 0x%2x failed in w_i=%d:waite master inactive timeout.\n",device,w_i); + } else if (w_i == 5) { + 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); + } else { + logf("Write i2c device 0x%2x failed in w_i=%d.\n",device,w_i); + } + + return -1; +} + +int i2c_read(int device, unsigned char* buf, int count) +{ + return xfer_read_subaddr(i2c_subaddr, device, &buf[0], count); +} + +int i2c_write(int device, const unsigned char* buf, int count) +{ + if (count < 2) + { + i2c_subaddr = buf[0]; + return 0; + } + return xfer_write_subaddr(buf[0], device, &buf[1], count-1); +} + +void i2c_init(void) +{ + __gpio_as_i2c(I2C_CHN); + __cpm_start_i2c1(); + system_enable_irq(IRQ_I2C1); +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "system.h" +#include "kernel.h" +#include "cpu.h" + +void tick_start(unsigned int interval_in_ms) +{ + unsigned int latch; + + /* 12Mhz / 4 = 3Mhz */ + latch = interval_in_ms*1000 * 3; + + REG_OST_OSTCSR = OSTCSR_PRESCALE4 | OSTCSR_EXT_EN; + REG_OST_OSTDR = latch; + REG_OST_OSTCNTL = 0; + REG_OST_OSTCNTH = 0; + + system_enable_irq(IRQ_TCU0); + + REG_TCU_TMCR = TMCR_OSTMASK; /* unmask match irq */ + REG_TCU_TSCR = TSCR_OST; /* enable timer clock */ + REG_TCU_TESR = TESR_OST; /* start counting up */ +} + +/* Interrupt handler */ +void TCU0(void) +{ + REG_TCU_TFCR = TFCR_OSTFLAG; /* ACK timer */ + + /* Run through the list of tick tasks */ + call_tick_tasks(); +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include /* off_t */ + +#include "config.h" +#include "cpu.h" +#include "lcd.h" +#include "lcd-target.h" +#include "system.h" +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "system.h" +#include "kernel.h" +#include "logf.h" +#include "audio.h" +#include "sound.h" +#include "pcm.h" +#include "pcm-internal.h" +#include "cpu.h" + + +/**************************************************************************** + ** Playback DMA transfer + **/ + +void pcm_play_dma_postinit(void) +{ + audiohw_postinit(); + + /* Flush FIFO */ + __aic_flush_tfifo(); +} + +void pcm_play_dma_init(void) +{ + system_enable_irq(DMA_IRQ(DMA_AIC_TX_CHANNEL)); + + /* Initialize default register values. */ + audiohw_init(); +} + +void pcm_dma_apply_settings(void) +{ + audiohw_set_frequency(pcm_fsel); +} + +static const void* playback_address; +static inline void set_dma(const void *addr, size_t size) +{ + int burst_size; + logf("%x %d %x", (unsigned int)addr, size, REG_AIC_SR); + + dma_cache_wback_inv((unsigned long)addr, size); + + if(size % 16) + { + if(size % 4) + { + size /= 2; + burst_size = DMAC_DCMD_DS_16BIT; + } + else + { + size /= 4; + burst_size = DMAC_DCMD_DS_32BIT; + } + } + else + { + size /= 16; + burst_size = DMAC_DCMD_DS_16BYTE; + } + + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = 0; + REG_DMAC_DSAR(DMA_AIC_TX_CHANNEL) = PHYSADDR((unsigned long)addr); + REG_DMAC_DTAR(DMA_AIC_TX_CHANNEL) = PHYSADDR((unsigned long)AIC_DR); + REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL) = size; + REG_DMAC_DRSR(DMA_AIC_TX_CHANNEL) = DMAC_DRSR_RS_AICOUT; + REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) = (DMAC_DCMD_SAI | DMAC_DCMD_SWDH_32 | burst_size | DMAC_DCMD_DWDH_16 | DMAC_DCMD_TIE); + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = DMAC_DCCSR_NDES | DMAC_DCCSR_EN; + + playback_address = addr; +} + +static inline void play_dma_callback(void) +{ + const void *start; + size_t size; + + if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &start, &size)) + { + set_dma(start, size); + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; + pcm_play_dma_status_callback(PCM_DMAST_STARTED); + } +} + +void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void) __attribute__ ((section(".icode"))); +void DMA_CALLBACK(DMA_AIC_TX_CHANNEL)(void) +{ + if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_AR) + { + logf("PCM DMA address error"); + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_AR; + } + + if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_HLT) + { + logf("PCM DMA halt"); + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_HLT; + } + + if (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_TT) + { + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_TT; + play_dma_callback(); + } +} + +void pcm_play_dma_start(const void *addr, size_t size) +{ + __dmac_channel_enable_clk(DMA_AIC_TX_CHANNEL); + + set_dma(addr, size); + + __aic_enable_replay(); + + __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL); +} + +void pcm_play_dma_stop(void) +{ + int flags = disable_irq_save(); + + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) = (REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) | DMAC_DCCSR_HLT) & ~DMAC_DCCSR_EN; + + __dmac_channel_disable_clk(DMA_AIC_TX_CHANNEL); + + __aic_disable_replay(); + + restore_irq(flags); +} + +static unsigned int play_lock = 0; +void pcm_play_lock(void) +{ + int flags = disable_irq_save(); + + if (++play_lock == 1) + __dmac_channel_disable_irq(DMA_AIC_TX_CHANNEL); + + restore_irq(flags); +} + +void pcm_play_unlock(void) +{ + int flags = disable_irq_save(); + + if (--play_lock == 0) + __dmac_channel_enable_irq(DMA_AIC_TX_CHANNEL); + + restore_irq(flags); +} + +void pcm_play_dma_pause(bool pause) +{ + int flags = disable_irq_save(); + + if(pause) + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) &= ~DMAC_DCCSR_EN; + else + REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) |= DMAC_DCCSR_EN; + + restore_irq(flags); +} + +static int get_dma_count(void) +{ + int count = REG_DMAC_DTCR(DMA_AIC_TX_CHANNEL); + switch(REG_DMAC_DCMD(DMA_AIC_TX_CHANNEL) & DMAC_DCMD_DS_MASK) + { + case DMAC_DCMD_DS_16BIT: + count *= 2; + break; + case DMAC_DCMD_DS_32BIT: + count *= 4; + break; + case DMAC_DCMD_DS_16BYTE: + count *= 16; + break; + } + + return count; +} + +size_t pcm_get_bytes_waiting(void) +{ + int bytes, flags = disable_irq_save(); + + if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN) + bytes = get_dma_count() & ~3; + else + bytes = 0; + + restore_irq(flags); + + return bytes; +} + +const void * pcm_play_dma_get_peak_buffer(int *count) +{ + int flags = disable_irq_save(); + + const void* addr; + if(REG_DMAC_DCCSR(DMA_AIC_TX_CHANNEL) & DMAC_DCCSR_EN) + { + int bytes = get_dma_count(); + *count = bytes >> 2; + addr = (const void*)((int)(playback_address + bytes + 2) & ~3); + } + else + { + *count = 0; + addr = NULL; + } + + restore_irq(flags); + + return addr; +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "cpu.h" +#include "mips.h" +#include "mmu-mips.h" +#include "panic.h" +#include "system.h" +#include "kernel.h" +#include "power.h" + +static int irq; +static void UIRQ(void) +{ + panicf("Unhandled interrupt occurred: %d", irq); +} + +#define intr(name) extern __attribute__((weak,alias("UIRQ"))) void name (void) + +intr(I2C1);intr(I2C0);intr(UART3);intr(UART2);intr(UART1);intr(UART0);intr(GPU); +intr(SSI1);intr(SSI0);intr(TSSI);intr(KBC);intr(SADC);intr(ETH);intr(UHC); +intr(OTG);intr(TCU2);intr(TCU1);intr(TCU0);intr(GPS);intr(IPU);intr(CIM); +intr(LCD);intr(RTC);intr(OWI);intr(AIC);intr(MSC2);intr(MSC1);intr(MSC0); +intr(SCC);intr(BCH);intr(PCM);intr(HARB0);intr(HARB2);intr(AOSD);intr(CPM); + +intr(DMA0);intr(DMA1);intr(DMA2);intr(DMA3);intr(DMA4);intr(DMA5); +intr(DMA6);intr(DMA7);intr(DMA8);intr(DMA9);intr(DMA10);intr(DMA11); +intr(MDMA0);intr(MDMA1);intr(MDMA2); +intr(BDMA0);intr(BDMA1);intr(BDMA2); + +intr(GPIO0);intr(GPIO1);intr(GPIO2);intr(GPIO3);intr(GPIO4);intr(GPIO5); +intr(GPIO6);intr(GPIO7);intr(GPIO8);intr(GPIO9);intr(GPIO10);intr(GPIO11); +intr(GPIO12);intr(GPIO13);intr(GPIO14);intr(GPIO15);intr(GPIO16);intr(GPIO17); +intr(GPIO18);intr(GPIO19);intr(GPIO20);intr(GPIO21);intr(GPIO22);intr(GPIO23); +intr(GPIO24);intr(GPIO25);intr(GPIO26);intr(GPIO27);intr(GPIO28);intr(GPIO29); +intr(GPIO30);intr(GPIO31);intr(GPIO32);intr(GPIO33);intr(GPIO34);intr(GPIO35); +intr(GPIO36);intr(GPIO37);intr(GPIO38);intr(GPIO39);intr(GPIO40);intr(GPIO41); +intr(GPIO42);intr(GPIO43);intr(GPIO44);intr(GPIO45);intr(GPIO46);intr(GPIO47); +intr(GPIO48);intr(GPIO49);intr(GPIO50);intr(GPIO51);intr(GPIO52);intr(GPIO53); +intr(GPIO54);intr(GPIO55);intr(GPIO56);intr(GPIO57);intr(GPIO58);intr(GPIO59); +intr(GPIO60);intr(GPIO61);intr(GPIO62);intr(GPIO63);intr(GPIO64);intr(GPIO65); +intr(GPIO66);intr(GPIO67);intr(GPIO68);intr(GPIO69);intr(GPIO70);intr(GPIO71); +intr(GPIO72);intr(GPIO73);intr(GPIO74);intr(GPIO75);intr(GPIO76);intr(GPIO77); +intr(GPIO78);intr(GPIO79);intr(GPIO80);intr(GPIO81);intr(GPIO82);intr(GPIO83); +intr(GPIO84);intr(GPIO85);intr(GPIO86);intr(GPIO87);intr(GPIO88);intr(GPIO89); +intr(GPIO90);intr(GPIO91);intr(GPIO92);intr(GPIO93);intr(GPIO94);intr(GPIO95); +intr(GPIO96);intr(GPIO97);intr(GPIO98);intr(GPIO99);intr(GPIO100);intr(GPIO101); +intr(GPIO102);intr(GPIO103);intr(GPIO104);intr(GPIO105);intr(GPIO106); +intr(GPIO107);intr(GPIO108);intr(GPIO109);intr(GPIO110);intr(GPIO111); +intr(GPIO112);intr(GPIO113);intr(GPIO114);intr(GPIO115);intr(GPIO116); +intr(GPIO117);intr(GPIO118);intr(GPIO119);intr(GPIO120);intr(GPIO121); +intr(GPIO122);intr(GPIO123);intr(GPIO124);intr(GPIO125);intr(GPIO126); +intr(GPIO127);intr(GPIO128);intr(GPIO129);intr(GPIO130);intr(GPIO131); +intr(GPIO132);intr(GPIO133);intr(GPIO134);intr(GPIO135);intr(GPIO136); +intr(GPIO137);intr(GPIO138);intr(GPIO139);intr(GPIO140);intr(GPIO141); +intr(GPIO142);intr(GPIO143);intr(GPIO144);intr(GPIO145);intr(GPIO146); +intr(GPIO147);intr(GPIO148);intr(GPIO149);intr(GPIO150);intr(GPIO151); +intr(GPIO152);intr(GPIO153);intr(GPIO154);intr(GPIO155);intr(GPIO156); +intr(GPIO157);intr(GPIO158);intr(GPIO159);intr(GPIO160);intr(GPIO161); +intr(GPIO162);intr(GPIO163);intr(GPIO164);intr(GPIO165);intr(GPIO166); +intr(GPIO167);intr(GPIO168);intr(GPIO169);intr(GPIO170);intr(GPIO171); +intr(GPIO172);intr(GPIO173);intr(GPIO174);intr(GPIO175);intr(GPIO176); +intr(GPIO177);intr(GPIO178);intr(GPIO179);intr(GPIO180);intr(GPIO181); +intr(GPIO182);intr(GPIO183);intr(GPIO184);intr(GPIO185);intr(GPIO186); +intr(GPIO187);intr(GPIO188);intr(GPIO189);intr(GPIO190);intr(GPIO191); + +static void (* const irqvector[])(void) = +{ + I2C1,I2C0,UART3,UART2,UART1,UART0,GPU,SSI1, + SSI0,TSSI,UIRQ,KBC,UIRQ,UIRQ,UIRQ,UIRQ, + UIRQ,UIRQ,SADC,ETH,UHC,OTG,UIRQ,UIRQ, + UIRQ,TCU2,TCU1,TCU0,GPS,IPU,CIM,LCD, + + RTC,OWI,AIC,MSC2,MSC1,MSC0,SCC,BCH, // 32 + PCM,HARB0,HARB2,AOSD,CPM,UIRQ, + + DMA0,DMA1,DMA2,DMA3,DMA4,DMA5,DMA6,DMA7, // 46 + DMA8,DMA9,DMA10,DMA11,MDMA0,MDMA1,MDMA2,BDMA0, + BDMA1,BDMA2, + + GPIO0,GPIO1,GPIO2,GPIO3,GPIO4,GPIO5,GPIO6,GPIO7, // 64 + GPIO8,GPIO9,GPIO10,GPIO11,GPIO12,GPIO13,GPIO14,GPIO15, + GPIO16,GPIO17,GPIO18,GPIO19,GPIO20,GPIO21,GPIO22,GPIO23, + GPIO24,GPIO25,GPIO26,GPIO27,GPIO28,GPIO29,GPIO30,GPIO31, + GPIO32,GPIO33,GPIO34,GPIO35,GPIO36,GPIO37,GPIO38,GPIO39, + GPIO40,GPIO41,GPIO42,GPIO43,GPIO44,GPIO45,GPIO46,GPIO47, + GPIO48,GPIO49,GPIO50,GPIO51,GPIO52,GPIO53,GPIO54,GPIO55, + GPIO56,GPIO57,GPIO58,GPIO59,GPIO60,GPIO61,GPIO62,GPIO63, + GPIO64,GPIO65,GPIO66,GPIO67,GPIO68,GPIO69,GPIO70,GPIO71, + GPIO72,GPIO73,GPIO74,GPIO75,GPIO76,GPIO77,GPIO78,GPIO79, + GPIO80,GPIO81,GPIO82,GPIO83,GPIO84,GPIO85,GPIO86,GPIO87, + GPIO88,GPIO89,GPIO90,GPIO91,GPIO92,GPIO93,GPIO94,GPIO95, + GPIO96,GPIO97,GPIO98,GPIO99,GPIO100,GPIO101,GPIO102,GPIO103, + GPIO104,GPIO105,GPIO106,GPIO107,GPIO108,GPIO109,GPIO110,GPIO111, + GPIO112,GPIO113,GPIO114,GPIO115,GPIO116,GPIO117,GPIO118,GPIO119, + GPIO120,GPIO121,GPIO122,GPIO123,GPIO124,GPIO125,GPIO126,GPIO127, + GPIO128,GPIO129,GPIO130,GPIO131,GPIO132,GPIO133,GPIO134,GPIO135, + GPIO136,GPIO137,GPIO138,GPIO139,GPIO140,GPIO141,GPIO142,GPIO143, + GPIO144,GPIO145,GPIO146,GPIO147,GPIO148,GPIO149,GPIO150,GPIO151, + GPIO152,GPIO153,GPIO154,GPIO155,GPIO156,GPIO157,GPIO158,GPIO159, + GPIO160,GPIO161,GPIO162,GPIO163,GPIO164,GPIO165,GPIO166,GPIO167, + GPIO168,GPIO169,GPIO170,GPIO171,GPIO172,GPIO173,GPIO174,GPIO175, + GPIO176,GPIO177,GPIO178,GPIO179,GPIO180,GPIO181,GPIO182,GPIO183, + GPIO184,GPIO185,GPIO186,GPIO187,GPIO188,GPIO189,GPIO190,GPIO191 +}; + +static unsigned int dma_irq_mask = 0; +static unsigned char mdma_irq_mask = 0; +static unsigned char bdma_irq_mask = 0; +static unsigned int gpio_irq_mask[6] = {0}; + +void system_enable_irq(unsigned int irq) +{ + register unsigned int t; + if ((irq >= IRQ_GPIO_0) && (irq <= IRQ_GPIO_0 + NUM_GPIO)) + { + __gpio_unmask_irq(irq - IRQ_GPIO_0); + t = (irq - IRQ_GPIO_0) >> 5; + gpio_irq_mask[t] |= (1 << ((irq - IRQ_GPIO_0) & 0x1f)); + __intc_unmask_irq(IRQ_GPIO0 - t); + } + else if ((irq >= IRQ_DMA_0) && (irq <= IRQ_DMA_0 + NUM_DMA)) + { + __dmac_channel_enable_irq(irq - IRQ_DMA_0); + t = (irq - IRQ_DMA_0) / HALF_DMA_NUM; + dma_irq_mask |= (1 << (irq - IRQ_DMA_0)); + __intc_unmask_irq(IRQ_DMAC0 - t); + } + else if ((irq >= IRQ_MDMA_0) && (irq <= IRQ_MDMA_0 + NUM_MDMA)) + { + __mdmac_channel_enable_irq(irq - IRQ_MDMA_0); + mdma_irq_mask |= (1 << (irq - IRQ_MDMA_0)); + __intc_unmask_irq(IRQ_MDMA); + } + else if ((irq >= IRQ_BDMA_0) && (irq <= IRQ_BDMA_0 + NUM_BDMA)) + { + __bdmac_channel_enable_irq(irq - IRQ_BDMA_0); + bdma_irq_mask |= (1 << (irq - IRQ_BDMA_0)); + __intc_unmask_irq(IRQ_BDMA); + } + else if (irq < IRQ_INTC_MAX) + __intc_unmask_irq(irq); +} + +static void dis_irq(unsigned int irq) +{ + register unsigned int t; + if ((irq >= IRQ_GPIO_0) && (irq <= IRQ_GPIO_0 + NUM_GPIO)) + { + __gpio_mask_irq(irq - IRQ_GPIO_0); + t = (irq - IRQ_GPIO_0) >> 5; + gpio_irq_mask[t] &= ~(1 << ((irq - IRQ_GPIO_0) & 0x1f)); + if (!gpio_irq_mask[t]) + __intc_mask_irq(IRQ_GPIO0 - t); + } + else if ((irq >= IRQ_DMA_0) && (irq < IRQ_DMA_0 + NUM_DMA)) + { + __dmac_channel_disable_irq(irq - IRQ_DMA_0); + dma_irq_mask &= ~(1 << (irq - IRQ_DMA_0)); + if (!(dma_irq_mask & 0x003F)) + __intc_mask_irq(IRQ_DMAC0); + if (!(dma_irq_mask & 0x0FC0)) + __intc_mask_irq(IRQ_DMAC1); + } + else if ((irq >= IRQ_MDMA_0) && (irq < IRQ_MDMA_0 + NUM_MDMA)) + { + __mdmac_channel_disable_irq(irq - IRQ_MDMA_0); + mdma_irq_mask &= ~(1 << (irq - IRQ_MDMA_0)); + if (!mdma_irq_mask) + __intc_mask_irq(IRQ_MDMA); + } + else if ((irq >= IRQ_BDMA_0) && (irq < IRQ_BDMA_0 + NUM_BDMA)) + { + __bdmac_channel_disable_irq(irq - IRQ_BDMA_0); + bdma_irq_mask &= ~(1 << (irq - IRQ_BDMA_0)); + if (!bdma_irq_mask) + __intc_mask_irq(IRQ_BDMA); + } + else if (irq < IRQ_INTC_MAX) + __intc_mask_irq(irq); +} + +static void ack_irq(unsigned int irq) +{ + if ((irq >= IRQ_GPIO_0) && (irq <= IRQ_GPIO_0 + NUM_GPIO)) + { + __gpio_ack_irq(irq - IRQ_GPIO_0); + } +} + +static int get_irq_number(void) +{ + static unsigned long ipl0, ipl1; + register int irq0, irq1; + + ipl0 |= REG_INTC_ICPR(0); + ipl1 |= REG_INTC_ICPR(1); + + if (!(ipl0 || ipl1)) + return -1; + + __asm__ __volatile__("negu $8, %0 \n" + "and $8, %0, $8 \n" + "clz %0, %1 \n" + "li $8, 31 \n" + "subu %0, $8, %0 \n" + : "=r" (irq0) + : "r" (ipl0) + : "t0" + ); + + __asm__ __volatile__("negu $8, %0 \n" + "and $8, %0, $8 \n" + "clz %0, %1 \n" + "li $8, 31 \n" + "subu %0, $8, %0 \n" + : "=r" (irq1) + : "r" (ipl1) + : "t0" + ); + + if (UNLIKELY(irq0 < 0) && UNLIKELY(irq1 < 0)) + return -1; + + if (!(ipl0 & 3)) { + if (ipl0) { + irq = irq0; + ipl0 &= ~(1<= 0)) + irqvector[irq](); +} + +#define EXC(x,y) case (x): return (y); +static char* parse_exception(unsigned int cause) +{ + switch(cause & M_CauseExcCode) + { + EXC(EXC_INT, "Interrupt"); + EXC(EXC_MOD, "TLB Modified"); + EXC(EXC_TLBL, "TLB Exception (Load or Ifetch)"); + EXC(EXC_ADEL, "Address Error (Load or Ifetch)"); + EXC(EXC_ADES, "Address Error (Store)"); + EXC(EXC_TLBS, "TLB Exception (Store)"); + EXC(EXC_IBE, "Instruction Bus Error"); + EXC(EXC_DBE, "Data Bus Error"); + EXC(EXC_SYS, "Syscall"); + EXC(EXC_BP, "Breakpoint"); + EXC(EXC_RI, "Reserved Instruction"); + EXC(EXC_CPU, "Coprocessor Unusable"); + EXC(EXC_OV, "Overflow"); + EXC(EXC_TR, "Trap Instruction"); + EXC(EXC_FPE, "Floating Point Exception"); + EXC(EXC_C2E, "COP2 Exception"); + EXC(EXC_MDMX, "MDMX Exception"); + EXC(EXC_WATCH, "Watch Exception"); + EXC(EXC_MCHECK, "Machine Check Exception"); + EXC(EXC_CacheErr, "Cache error caused re-entry to Debug Mode"); + default: + return NULL; + } +} + +void exception_handler(void* stack_ptr, unsigned int cause, unsigned int epc) +{ + panicf("Exception occurred: %s [0x%08x] at 0x%08x (stack at 0x%08x)", parse_exception(cause), read_c0_badvaddr(), epc, (unsigned int)stack_ptr); +} + +void tlb_refill_handler(void) +{ + panicf("TLB refill handler at 0x%08lx! [0x%x]", read_c0_epc(), read_c0_badvaddr()); +} + +void udelay(unsigned int usec) +{ + unsigned int i = usec * (__cpm_get_cclk() / 2000000); + __asm__ __volatile__ ( + ".set noreorder \n" + "1: \n" + "bne %0, $0, 1b \n" + "addi %0, %0, -1 \n" + ".set reorder \n" + : "=r" (i) + : "0" (i) + ); +} + +void mdelay(unsigned int msec) +{ + unsigned int i; + for(i=0; i 1500 * MHZ) + continue; + for (k = pll_n_min; k <= pll_n_max; k++) { + n = k; + + /* Limit: 1MHZ <= XIN/N <= 50MHZ */ + if ((xtal / n) < (1 * MHZ)) + break; + if ((xtal / n) > (15 * MHZ)) + continue; + + for (j = pll_m_min; j <= pll_m_max; j++) { + m = j*2; + + raw = xtal * m / n; + tmp = raw / od[i]; + + tmp = (tmp > speed) ? (tmp - speed) : (speed - tmp); + + if (tmp < distance) { + distance = tmp; + + plcr_m_n_od = (j << CPPCR0_PLLM_LSB) + | (k << CPPCR0_PLLN_LSB) + | (i << CPPCR0_PLLOD_LSB); + + if (!distance) { /* Match. */ + return plcr_m_n_od; + } + } + } + } + } + return plcr_m_n_od; +} + +/* PLL output clock = EXTAL * NF / (NR * NO) + * + * NF = FD + 2, NR = RD + 2 + * NO = 1 (if OD = 0), NO = 2 (if OD = 1 or 2), NO = 4 (if OD = 3) + */ +static void pll0_init(unsigned int freq) +{ + register unsigned int cfcr, plcr1; + int n2FR[9] = { + 0, 0, 1, 2, 3, 0, 4, 0, 5 + }; + + /** divisors, + * for jz4760b,I:H:H2:P:M:S. + * DIV should be one of [1, 2, 3, 4, 6, 8] + */ + int div[6] = {1, 4, 4, 4, 4, 4}; + int usbdiv; + + /* set ahb **/ + REG32(HARB0_BASE) = 0x00300000; + REG32(0xb3070048) = 0x00000000; + REG32(HARB2_BASE) = 0x00FFFFFF; + + cfcr = CPCCR_PCS | + (n2FR[div[0]] << CPCCR_CDIV_LSB) | + (n2FR[div[1]] << CPCCR_HDIV_LSB) | + (n2FR[div[2]] << CPCCR_H2DIV_LSB) | + (n2FR[div[3]] << CPCCR_PDIV_LSB) | + (n2FR[div[4]] << CPCCR_MDIV_LSB) | + (n2FR[div[5]] << CPCCR_SDIV_LSB); + + // write REG_DDRC_CTRL 8 times to clear ddr fifo + REG_DDRC_CTRL = 0; + REG_DDRC_CTRL = 0; + REG_DDRC_CTRL = 0; + REG_DDRC_CTRL = 0; + REG_DDRC_CTRL = 0; + REG_DDRC_CTRL = 0; + REG_DDRC_CTRL = 0; + REG_DDRC_CTRL = 0; + + if (CFG_EXTAL > 16000000) + cfcr |= CPCCR_ECS; + else + cfcr &= ~CPCCR_ECS; + + cfcr &= ~CPCCR_MEM; /* mddr */ + cfcr |= CPCCR_CE; + + plcr1 = pll_calc_m_n_od(freq, CFG_EXTAL); + plcr1 |= (0x20 << CPPCR0_PLLST_LSB) /* PLL stable time */ + | CPPCR0_PLLEN; /* enable PLL */ + + /* + * Init USB Host clock, pllout2 must be n*48MHz + * For JZ4760b UHC - River. + */ + usbdiv = (cfcr & CPCCR_PCS) ? CPU_FREQ : (CPU_FREQ / 2); + REG_CPM_UHCCDR = usbdiv / 48000000 - 1; + + /* init PLL */ + REG_CPM_CPCCR = cfcr; + REG_CPM_CPPCR0 = plcr1; + + __cpm_enable_pll_change(); + + /*wait for pll output stable ...*/ + while (!(REG_CPM_CPPCR0 & CPPCR0_PLLS)); + + REG_CPM_CPPCR0 &= ~CPPCR0_LOCK; +} + +void pll1_init(unsigned int freq) +{ + register unsigned int plcr2; + + /* set CPM_CPCCR_MEM only for ddr1 or ddr2 */ + plcr2 = pll_calc_m_n_od(freq, CFG_EXTAL) + | CPPCR1_PLL1EN; /* enable PLL1 */ + + /* init PLL_1 , source clock is extal clock */ + REG_CPM_CPPCR1 = plcr2; + + __cpm_enable_pll_change(); + + /*wait for pll_1 output stable ...*/ + while (!(REG_CPM_CPPCR1 & CPPCR1_PLL1S)); + + REG_CPM_CPPCR1 &= ~CPPCR1_LOCK; +} + +static void serial_setbrg(void) +{ + volatile u8 *uart_lcr = (volatile u8 *)(CFG_UART_BASE + OFF_LCR); + volatile u8 *uart_dlhr = (volatile u8 *)(CFG_UART_BASE + OFF_DLHR); + volatile u8 *uart_dllr = (volatile u8 *)(CFG_UART_BASE + OFF_DLLR); + volatile u8 *uart_umr = (volatile u8 *)(CFG_UART_BASE + OFF_UMR); + volatile u8 *uart_uacr = (volatile u8 *)(CFG_UART_BASE + OFF_UACR); + u16 baud_div, tmp; + + *uart_umr = 16; + *uart_uacr = 0; + baud_div = 13; /* 57600 */ + + tmp = *uart_lcr; + tmp |= UARTLCR_DLAB; + *uart_lcr = tmp; + + *uart_dlhr = (baud_div >> 8) & 0xff; + *uart_dllr = baud_div & 0xff; + + tmp &= ~UARTLCR_DLAB; + *uart_lcr = tmp; +} + +int serial_preinit(void) +{ + volatile u8 *uart_fcr = (volatile u8 *)(CFG_UART_BASE + OFF_FCR); + volatile u8 *uart_lcr = (volatile u8 *)(CFG_UART_BASE + OFF_LCR); + volatile u8 *uart_ier = (volatile u8 *)(CFG_UART_BASE + OFF_IER); + volatile u8 *uart_sircr = (volatile u8 *)(CFG_UART_BASE + OFF_SIRCR); + + __gpio_as_uart1(); + __cpm_start_uart1(); + + /* Disable port interrupts while changing hardware */ + *uart_ier = 0; + + /* Disable UART unit function */ + *uart_fcr = ~UARTFCR_UUE; + + /* Set both receiver and transmitter in UART mode (not SIR) */ + *uart_sircr = ~(SIRCR_RSIRE | SIRCR_TSIRE); + + /* Set databits, stopbits and parity. (8-bit data, 1 stopbit, no parity) */ + *uart_lcr = UARTLCR_WLEN_8 | UARTLCR_STOP1; + + /* Set baud rate */ + serial_setbrg(); + + /* Enable UART unit, enable and clear FIFO */ + *uart_fcr = UARTFCR_UUE | UARTFCR_FE | UARTFCR_TFLS | UARTFCR_RFLS; + + return 0; +} + +void usb_preinit(void) +{ + /* Clear ECS bit of CPCCR, 0:clock source is EXCLK, 1:clock source is EXCLK/2 */ + REG_CPM_CPCCR &= ~CPCCR_ECS; + + /* Clear all bits of USBCDR, 0:OTG clock source is pin EXCLK, PLL0 output, divider = 1:12MHZ */ + REG_CPM_USBCDR = 0; + + /* Set CE bit of CPCCR, it means frequence is changed immediately */ + REG_CPM_CPCCR |= CPCCR_CE; + + udelay(3); + + /* Clear OTG bit of CLKGR0, 0:device can be accessed */ + REG_CPM_CLKGR0 &= ~CLKGR0_OTG; + + /* fil */ + REG_CPM_USBVBFIL = 0x80; + + /* rdt */ + REG_CPM_USBRDT = (600 * (CPU_FREQ / 1000000)) / 1000; + + /* rdt - filload_en */ + REG_CPM_USBRDT |= (1 << 25); + + /* TXRISETUNE & TXVREFTUNE. */ + REG_CPM_USBPCR &= ~0x3f; + REG_CPM_USBPCR |= 0x35; + + /* enable tx pre-emphasis */ + REG_CPM_USBPCR |= 0x40; + + /* most DC leave of tx */ + REG_CPM_USBPCR |= 0xf; + + /* Device Mode. */ + REG_CPM_USBPCR &= ~(1 << 31); + REG_CPM_USBPCR |= USBPCR_VBUSVLDEXT; + + /* phy reset */ + REG_CPM_USBPCR |= USBPCR_POR; + udelay(30); + REG_CPM_USBPCR &= ~USBPCR_POR; + udelay(300); + + /* Enable the USB PHY */ + REG_CPM_OPCR |= OPCR_OTGPHY_ENABLE; + + /* Wait PHY Clock Stable. */ + udelay(300); +} + +void dma_preinit(void) +{ + __cpm_start_mdma(); + __cpm_start_dmac(); + + REG_MDMAC_DMACKES = 0x1; + + REG_DMAC_DMACR(DMA_AIC_TX_CHANNEL) = DMAC_DMACR_DMAE | DMAC_DMACR_FAIC; + REG_DMAC_DMACR(DMA_SD_RX_CHANNEL) = DMAC_DMACR_DMAE | DMAC_DMACR_FMSC; + REG_DMAC_DMACR(DMA_SD_TX_CHANNEL) = DMAC_DMACR_DMAE | DMAC_DMACR_FMSC; +} + +/* Gets called *before* main */ +void ICODE_ATTR system_main(void) +{ + int i; + + __dcache_writeback_all(); + __icache_invalidate_all(); + + write_c0_status(1 << 28 | 1 << 10 ); /* Enable CP | Mask interrupt 2 */ + + /* Disable all interrupts */ + for(i=0; i 60ms */ + rtc_write_reg(RTC_HRCR, HRCR_WAIT_TIME(60)); + + /* clear wakeup status register */ + rtc_write_reg(RTC_HWRSR, 0x0); + + /* set wake up valid level as low */ + rtc_write_reg(RTC_HWCR,0x8); + + /* Put CPU to hibernate mode */ + rtc_write_reg(RTC_HCR, HCR_PD); + + while (1); +} + +void system_init(void) +{ +} + +int system_memory_guard(int newmode) +{ + (void)newmode; + return 0; +} + +#ifdef HAVE_ADJUSTABLE_CPU_FREQ +void set_cpu_frequency(long frequency) +{ + serial_putsf("set_cpu_frequency: %d\n", frequency); +} +#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 @@ #include #include "config.h" -#include "jz4740.h" +#include "cpu.h" #include "mipsregs.h" #define CACHE_SIZE 16*1024 @@ -35,6 +35,8 @@ /* no optimized byteswap functions implemented for mips, yet */ #define NEED_GENERIC_BYTESWAPS +#define STORAGE_WANTS_ALIGN + /* This one returns the old status */ static inline int set_interrupt_status(int status, int mask) { @@ -86,10 +88,18 @@ void mdelay(unsigned int msec); void dma_enable(void); void dma_disable(void); +#if CONFIG_CPU == JZ4732 #define DMA_AIC_TX_CHANNEL 0 #define DMA_NAND_CHANNEL 1 #define DMA_USB_CHANNEL 2 #define DMA_LCD_CHANNEL 3 +#elif CONFIG_CPU == JZ4760B +#define DMA_AIC_TX_CHANNEL 0 +#define DMA_NAND_CHANNEL 1 +#define DMA_USB_CHANNEL 2 +#define DMA_SD_RX_CHANNEL 3 +#define DMA_SD_TX_CHANNEL 4 +#endif #define XDMA_CALLBACK(n) DMA ## n #define DMA_CALLBACK(n) XDMA_CALLBACK(n) @@ -103,7 +113,7 @@ void dma_disable(void); */ static inline void core_sleep(void) { -#if CONFIG_CPU == JZ4732 +#if CONFIG_CPU == JZ4732 || CONFIG_CPU == JZ4760B __cpm_idle_mode(); #endif 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "cpu.h" +#include "system.h" +#include "timer.h" + +/* Interrupt handler */ +void TCU1(void) +{ + __tcu_clear_full_match_flag(5); + + if (pfn_timer != NULL) + pfn_timer(); +} + +bool timer_set(long cycles, bool start) +{ + unsigned int divider = cycles, prescaler_bit = 0, prescaler = 1, old_irq; + + if(cycles < 1) + return false; + + if(start && pfn_unregister != NULL) + { + pfn_unregister(); + pfn_unregister = NULL; + } + + /* Increase prescale values starting from 0 to make the cycle count fit */ + while(divider > 65535 && prescaler <= 1024) + { + prescaler <<= 2; /* 1, 4, 16, 64, 256, 1024 */ + prescaler_bit++; + divider = cycles / prescaler; + } + + old_irq = disable_irq_save(); + + __tcu_stop_counter(5); + if(start) + { + __tcu_disable_pwm_output(5); + + __tcu_mask_half_match_irq(5); + __tcu_unmask_full_match_irq(5); + + /* EXTAL clock = CFG_EXTAL (12Mhz in most targets) */ + __tcu_select_extalclk(5); + } + + REG_TCU_TCSR(5) = (REG_TCU_TCSR(5) & ~TCSR_PRESCALE_MASK) | (prescaler_bit << TCSR_PRESCALE_LSB); + REG_TCU_TCNT(5) = 0; + REG_TCU_TDHR(5) = 0; + REG_TCU_TDFR(5) = divider; + + __tcu_clear_full_match_flag(5); + + if(start) + { + system_enable_irq(IRQ_TCU1); + __tcu_start_counter(5); + } + + restore_irq(old_irq); + + return true; +} + +bool timer_start(void) +{ + __tcu_start_counter(5); + + return true; +} + +void timer_stop(void) +{ + unsigned int old_irq = disable_irq_save(); + __tcu_stop_counter(5); + restore_irq(old_irq); +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +/*#define LOGF_ENABLE*/ +#include "logf.h" +#include "system.h" +#include "usb_ch9.h" +#include "usb_drv.h" +#include "usb_core.h" +#include "cpu.h" +#include "thread.h" + +#define PIN_USB_DET (32*4+19) +#define IRQ_USB_DET GPIO_IRQ(PIN_USB_DET) +#define GPIO_USB_DET GPIO147 + +#define PIN_USB_DRVVBUS (32*4+10) +#define PIN_USB_OTG_ID (32*3+7) + +#define EP_BUF_LEFT(ep) ((ep)->length - (ep)->sent) +#define EP_PTR(ep) ((void*)((unsigned int)(ep)->buf + (ep)->sent)) +#define EP_NUMBER(ep) (((int)(ep) - (int)&endpoints[0])/sizeof(struct usb_endpoint)) +#define EP_NUMBER2(ep) (EP_NUMBER((ep))/2) +#define TOTAL_EP() (sizeof(endpoints)/sizeof(struct usb_endpoint)) +#define EP_IS_IN(ep) (EP_NUMBER((ep))%2) + +enum ep_type +{ + ep_control, + ep_bulk, + ep_interrupt, + ep_isochronous +}; + +struct usb_endpoint +{ + void *buf; + size_t length; + union + { + size_t sent; + size_t received; + }; + bool busy; + + const enum ep_type type; + const bool use_dma; + + const long fifo_addr; + unsigned short fifo_size; + + bool wait; + struct semaphore complete; +}; + +#define EP_INIT(_type, _fifo_addr, _fifo_size, _buf, _use_dma) \ + { .type = (_type), .fifo_addr = (_fifo_addr), .fifo_size = (_fifo_size), \ + .buf = (_buf), .use_dma = (_use_dma), .length = 0, .busy = false, .wait = false } + +static unsigned char ep0_rx_buf[64]; +static struct usb_endpoint endpoints[] = +{ + EP_INIT(ep_control, USB_FIFO_EP(0), 64, NULL, false), + EP_INIT(ep_control, USB_FIFO_EP(0), 64, &ep0_rx_buf, false), + EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL, false), + EP_INIT(ep_bulk, USB_FIFO_EP(1), 512, NULL, false), + EP_INIT(ep_interrupt, USB_FIFO_EP(2), 64, NULL, false), + EP_INIT(ep_interrupt, USB_FIFO_EP(2), 64, NULL, false), +}; + +static inline void select_endpoint(int ep) +{ + REG_USB_INDEX = ep; +} + +static void readFIFO(struct usb_endpoint *ep, unsigned int size) +{ + logf("%s(EP%d, %d)", __func__, EP_NUMBER2(ep), size); + + register unsigned char *ptr = (unsigned char*)EP_PTR(ep); + register unsigned int *ptr32 = (unsigned int*)ptr; + register unsigned int s = size >> 2; + register unsigned int x; + + if(size > 0) + { + if( ((unsigned int)ptr & 3) == 0 ) + { + while(s--) + *ptr32++ = REG32(ep->fifo_addr); + + ptr = (unsigned char*)ptr32; + } + else + { + while(s--) + { + x = REG32(ep->fifo_addr); + *ptr++ = x & 0xFF; x >>= 8; + *ptr++ = x & 0xFF; x >>= 8; + *ptr++ = x & 0xFF; x >>= 8; + *ptr++ = x; + } + } + + s = size & 3; + while(s--) + *ptr++ = REG8(ep->fifo_addr); + } +} + +static void writeFIFO(struct usb_endpoint *ep, size_t size) +{ + logf("%s(EP%d, %d)", __func__, EP_NUMBER2(ep), size); + + register unsigned int *d32 = (unsigned int *)EP_PTR(ep); + register size_t s = size >> 2; + + if(size > 0) + { + while (s--) + REG32(ep->fifo_addr) = *d32++; + + if( (s = size & 3) ) + { + register unsigned char *d8 = (unsigned char *)d32; + while (s--) + REG8(ep->fifo_addr) = *d8++; + } + } +} + +static void flushFIFO(struct usb_endpoint *ep) +{ + logf("%s(%d)", __func__, EP_NUMBER(ep)); + + switch (ep->type) + { + case ep_control: + break; + + case ep_bulk: + case ep_interrupt: + case ep_isochronous: + if(EP_IS_IN(ep)) + REG_USB_INCSR |= (USB_INCSR_FF | USB_INCSR_CDT); + else + REG_USB_OUTCSR |= (USB_OUTCSR_FF | USB_OUTCSR_CDT); + break; + } +} + +static inline void ep_transfer_completed(struct usb_endpoint* ep) +{ + ep->sent = 0; + ep->length = 0; + ep->buf = NULL; + ep->busy = false; + if(ep->wait) + semaphore_release(&ep->complete); +} + +static void EP0_send(void) +{ + struct usb_endpoint* ep = &endpoints[0]; + unsigned int length; + unsigned char csr0; + + select_endpoint(0); + csr0 = REG_USB_CSR0; + + if(ep->sent == 0) + length = MIN(ep->length, ep->fifo_size); + else + length = MIN(EP_BUF_LEFT(ep), ep->fifo_size); + + writeFIFO(ep, length); + ep->sent += length; + + if(ep->sent >= ep->length) + { + REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY | USB_CSR0_DATAEND); /* Set data end! */ + usb_core_transfer_complete(0, USB_DIR_IN, 0, ep->sent); + ep_transfer_completed(ep); + } + else + REG_USB_CSR0 = (csr0 | USB_CSR0_INPKTRDY); +} + +static void EP0_handler(void) +{ + logf("%s()", __func__); + + unsigned char csr0; + struct usb_endpoint *ep_send = &endpoints[0]; + struct usb_endpoint *ep_recv = &endpoints[1]; + + /* Read CSR0 */ + select_endpoint(0); + csr0 = REG_USB_CSR0; + + /* Check for SentStall: + This bit is set when a STALL handshake is transmitted. The CPU should clear this bit. + */ + if(csr0 & USB_CSR0_SENTSTALL) + { + REG_USB_CSR0 = csr0 & ~USB_CSR0_SENTSTALL; + return; + } + + /* Check for SetupEnd: + This bit will be set when a control transaction ends before the DataEnd bit has been set. + An interrupt will be generated and the FIFO flushed at this time. + The bit is cleared by the CPU writing a 1 to the ServicedSetupEnd bit. + */ + if(csr0 & USB_CSR0_SETUPEND) + { + REG_USB_CSR0 = csr0 | USB_CSR0_SVDSETUPEND; + return; + } + + /* Call relevant routines for endpoint 0 state */ + if(ep_send->busy) + EP0_send(); + else if(csr0 & USB_CSR0_OUTPKTRDY) /* There is a packet in the fifo */ + { + readFIFO(ep_recv, REG_USB_COUNT0); + REG_USB_CSR0 = csr0 | USB_CSR0_SVDOUTPKTRDY; /* clear OUTPKTRDY bit */ + usb_core_control_request((struct usb_ctrlrequest*)ep_recv->buf); + } +} + +static void EPIN_handler(unsigned int endpoint) +{ + struct usb_endpoint* ep = &endpoints[endpoint*2]; + unsigned int length, csr; + + select_endpoint(endpoint); + csr = REG_USB_INCSR; + logf("%s(%d): 0x%x", __func__, endpoint, csr); + + if(!ep->busy) + { + logf("Entered EPIN handler without work!"); + return; + } + + if(csr & USB_INCSR_SENTSTALL) + { + REG_USB_INCSR = csr & ~USB_INCSR_SENTSTALL; + return; + } + + if(ep->use_dma) + return; + + if(csr & USB_INCSR_FFNOTEMPT) + { + logf("FIFO is not empty! 0x%x", csr); + return; + } + + logf("EP%d: %d -> %d", endpoint, ep->sent, ep->length); + + if(ep->sent == 0) + length = MIN(ep->length, ep->fifo_size); + else + length = MIN(EP_BUF_LEFT(ep), ep->fifo_size); + + writeFIFO(ep, length); + REG_USB_INCSR = csr | USB_INCSR_INPKTRDY; + ep->sent += length; + + if(ep->sent >= ep->length) + { + usb_core_transfer_complete(endpoint, USB_DIR_IN, 0, ep->sent); + ep_transfer_completed(ep); + logf("sent complete"); + } +} + +static void EPOUT_handler(unsigned int endpoint) +{ + struct usb_endpoint* ep = &endpoints[endpoint*2+1]; + unsigned int size, csr; + + if(!ep->busy) + { + logf("Entered EPOUT handler without work!"); + return; + } + + select_endpoint(endpoint); + while((csr = REG_USB_OUTCSR) & (USB_OUTCSR_SENTSTALL|USB_OUTCSR_OUTPKTRDY)) + { + logf("%s(%d): 0x%x", __func__, endpoint, csr); + if(csr & USB_OUTCSR_SENTSTALL) + { + logf("stall sent, flushing fifo.."); + flushFIFO(ep); + REG_USB_OUTCSR = csr & ~USB_OUTCSR_SENTSTALL; + return; + } + + if(ep->use_dma) + return; + + if(csr & USB_OUTCSR_OUTPKTRDY) /* There is a packet in the fifo */ + { + size = REG_USB_OUTCOUNT; + + readFIFO(ep, size); + ep->received += size; + + /*if(csr & USB_OUTCSR_FFFULL) + csr &= ~USB_OUTCSR_FFFULL;*/ + + REG_USB_OUTCSR = csr & ~USB_OUTCSR_OUTPKTRDY; + + logf("received: %d max length: %d", ep->received, ep->length); + + if(size < ep->fifo_size || ep->received >= ep->length) + { + usb_core_transfer_complete(endpoint, USB_DIR_OUT, 0, ep->received); + ep_transfer_completed(ep); + logf("receive transfer_complete"); + } + } + } +} + +static void EPDMA_handler(int number) +{ + int endpoint = -1; + unsigned int size = 0; + + if(number == USB_INTR_DMA_BULKIN) + endpoint = (REG_USB_CNTL(0) >> 4) & 0xF; + else if(number == USB_INTR_DMA_BULKOUT) + endpoint = (REG_USB_CNTL(1) >> 4) & 0xF; + + struct usb_endpoint* ep = &endpoints[endpoint]; + logf("DMA_BULK%d %d", number, endpoint); + + if(number == USB_INTR_DMA_BULKIN) + size = (unsigned int)ep->buf - REG_USB_ADDR(0); + else if(number == USB_INTR_DMA_BULKOUT) + size = (unsigned int)ep->buf - REG_USB_ADDR(1); + + if(number == USB_INTR_DMA_BULKOUT) + { + /* Disable DMA */ + REG_USB_CNTL(1) = 0; + + __dcache_invalidate_all(); + + select_endpoint(endpoint); + /* Read out last packet manually */ + unsigned int lpack_size = REG_USB_OUTCOUNT; + if(lpack_size > 0) + { + ep->buf += ep->length - lpack_size; + readFIFO(ep, lpack_size); + REG_USB_OUTCSR &= ~USB_OUTCSR_OUTPKTRDY; + } + } + else if(number == USB_INTR_DMA_BULKIN && size % ep->fifo_size) + { + /* If the last packet is less than MAXP, set INPKTRDY manually */ + REG_USB_INCSR |= USB_INCSR_INPKTRDY; + } + + usb_core_transfer_complete(endpoint, EP_IS_IN(ep) ? USB_DIR_IN : USB_DIR_OUT, + 0, ep->length); + ep_transfer_completed(ep); +} + +static void setup_endpoint(struct usb_endpoint *ep) +{ + int csr, csrh; + + select_endpoint(EP_NUMBER2(ep)); + + ep->busy = false; + ep->wait = false; + ep->sent = 0; + ep->length = 0; + + if(ep->type == ep_bulk) + { + if(REG_USB_POWER & USB_POWER_HSMODE) + ep->fifo_size = 512; + else + ep->fifo_size = 64; + } + + if(EP_IS_IN(ep)) + { + csr = (USB_INCSR_FF | USB_INCSR_CDT); + csrh = USB_INCSRH_MODE; + + if(ep->use_dma) + csrh |= (USB_INCSRH_DMAREQENAB | USB_INCSRH_AUTOSET | USB_INCSRH_DMAREQMODE); + + if(ep->type == ep_interrupt) + csrh |= USB_INCSRH_FRCDATATOG; + + REG_USB_INMAXP = ep->fifo_size; + REG_USB_INCSR = csr; + REG_USB_INCSRH = csrh; + REG_USB_INTRINE |= USB_INTR_EP(EP_NUMBER2(ep)); + } + else + { + csr = (USB_OUTCSR_FF | USB_OUTCSR_CDT); + csrh = 0; + + if(ep->type == ep_interrupt) + csrh |= USB_OUTCSRH_DNYT; + + if(ep->use_dma) + csrh |= (USB_OUTCSRH_DMAREQENAB | USB_OUTCSRH_AUTOCLR | USB_OUTCSRH_DMAREQMODE); + + REG_USB_OUTMAXP = ep->fifo_size; + REG_USB_OUTCSR = csr; + REG_USB_OUTCSRH = csrh; + REG_USB_INTROUTE |= USB_INTR_EP(EP_NUMBER2(ep)); + } +} + +static void udc_reset(void) +{ + /* From the datasheet: + + When a reset condition is detected on the USB, the controller performs the following actions: + * Sets FAddr to 0. + * Sets Index to 0. + * Flushes all endpoint FIFOs. + * Clears all control/status registers. + * Enables all endpoint interrupts. + * Generates a Reset interrupt. + */ + + logf("%s()", __func__); + + unsigned int i; + + REG_USB_FADDR = 0; + REG_USB_INDEX = 0; + + /* Disable interrupts */ + REG_USB_INTRINE = 0; + REG_USB_INTROUTE = 0; + REG_USB_INTRUSBE = 0; + + /* Disable DMA */ + REG_USB_CNTL(0) = 0; + REG_USB_CNTL(1) = 0; + + /* High speed, softconnect */ + REG_USB_POWER = (USB_POWER_SOFTCONN | USB_POWER_HSENAB); + + /* Reset EP0 */ + select_endpoint(0); + REG_USB_CSR0 = (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_SVDSETUPEND | USB_CSR0_FLUSHFIFO); + + /* Reset other endpoints */ + for(i=2; itype == ep_control && ptr == NULL && length == 0) + return; /* ACK request, handled in the ISR */ + + int flags = disable_irq_save(); + + ep->buf = ptr; + ep->sent = 0; + ep->length = length; + ep->busy = true; + if(blocking) + ep->wait = true; + + if(ep->type == ep_control) + { + EP0_send(); + } + else + { + if(ep->use_dma) + { + //dma_cache_wback_inv((unsigned long)ptr, length); + __dcache_writeback_all(); + REG_USB_ADDR(0) = PHYSADDR((unsigned long)ptr); + REG_USB_COUNT(0) = length; + REG_USB_CNTL(0) = (USB_CNTL_INTR_EN | USB_CNTL_MODE_1 | + USB_CNTL_DIR_IN | USB_CNTL_ENA | + USB_CNTL_EP(EP_NUMBER2(ep)) | USB_CNTL_BURST_16); + } + else + EPIN_handler(EP_NUMBER2(ep)); + } + + restore_irq(flags); + + if(blocking) + { + semaphore_wait(&ep->complete, TIMEOUT_BLOCK); + ep->wait = false; + } +} + +int usb_drv_send_nonblocking(int endpoint, void* ptr, int length) +{ + logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length); + + usb_drv_send_internal(&endpoints[(endpoint & 0x7F)*2], ptr, length, false); + + return 0; +} + +int usb_drv_send(int endpoint, void* ptr, int length) +{ + logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length); + + usb_drv_send_internal(&endpoints[(endpoint & 0x7F)*2], ptr, length, true); + + return 0; +} + +int usb_drv_recv(int endpoint, void* ptr, int length) +{ + int flags; + struct usb_endpoint *ep; + endpoint &= 0x7F; + + logf("%s(%d, 0x%x, %d)", __func__, endpoint, (int)ptr, length); + + if(endpoint == EP_CONTROL) + return 0; /* all EP0 OUT transactions are handled within the ISR */ + else + { + flags = disable_irq_save(); + ep = &endpoints[endpoint*2+1]; + + ep->buf = ptr; + ep->received = 0; + ep->length = length; + ep->busy = true; + if(ep->use_dma) + { + //dma_cache_wback_inv((unsigned long)ptr, length); + __dcache_writeback_all(); + REG_USB_ADDR(1) = PHYSADDR((unsigned long)ptr); + REG_USB_COUNT(1) = length; + REG_USB_CNTL(1) = (USB_CNTL_INTR_EN | USB_CNTL_MODE_1 | + USB_CNTL_ENA | USB_CNTL_EP(endpoint) | + USB_CNTL_BURST_16); + } + else + EPOUT_handler(endpoint); + + restore_irq(flags); + return 0; + } +} + +void usb_drv_set_test_mode(int mode) +{ + logf("%s(%d)", __func__, mode); + + switch(mode) + { + case 0: + REG_USB_TESTMODE &= ~USB_TEST_ALL; + break; + case 1: + REG_USB_TESTMODE |= USB_TEST_J; + break; + case 2: + REG_USB_TESTMODE |= USB_TEST_K; + break; + case 3: + REG_USB_TESTMODE |= USB_TEST_SE0NAK; + break; + case 4: + REG_USB_TESTMODE |= USB_TEST_PACKET; + break; + } +} + +int usb_drv_port_speed(void) +{ + return (REG_USB_POWER & USB_POWER_HSMODE) ? 1 : 0; +} + +void usb_drv_cancel_all_transfers(void) +{ + logf("%s()", __func__); + + unsigned int i, flags; + flags = disable_irq_save(); + + for(i=0; i> 7) ? "IN" : "OUT"); +} + +int usb_drv_request_endpoint(int type, int dir) +{ + logf("%s(%d, %s)", __func__, type, (dir == USB_DIR_IN) ? "IN" : "OUT"); + + dir &= USB_ENDPOINT_DIR_MASK; + type &= USB_ENDPOINT_XFERTYPE_MASK; + + /* There are only 3+2 endpoints, so hardcode this ... */ + switch(type) + { + case USB_ENDPOINT_XFER_BULK: + if(dir == USB_DIR_IN) + return (1 | USB_DIR_IN); + else + return (1 | USB_DIR_OUT); + + case USB_ENDPOINT_XFER_INT: + if(dir == USB_DIR_IN) + return (2 | USB_DIR_IN); + else + return (2 | USB_DIR_OUT); + + default: + return -1; + } +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __XDEBUG_H_ +#define __XDEBUG_H_ + +void serial_puts(const char *s); +void serial_putsf(const char *format, ...); +void serial_put_hex(unsigned int d); +void serial_put_dec(unsigned int d); +void serial_dump_data(unsigned char* data, int len); + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _ADC_TARGET_H_ +#define _ADC_TARGET_H_ + +#define NUM_ADC_CHANNELS 4 + +#define ADC_BUTTONS 0 + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef ATA_SD_TARGET_H +#define ATA_SD_TARGET_H + +#include "cpu.h" +#include "system.h" + +#define PIN_SD1_CD (32*0+29) /* Pin to check card insertion */ +#define IRQ_SD1_CD GPIO_IRQ(PIN_SD1_CD) +#define GPIO_SD1_CD GPIO29 + +#define PIN_SD2_CD (32*0+28) /* Pin to check card insertion */ +#define IRQ_SD2_CD GPIO_IRQ(PIN_SD2_CD) +#define GPIO_SD2_CD GPIO28 + +static inline void sd_init_gpio(void) +{ + __gpio_as_msc1_pd_4bit(); + __gpio_as_msc2_pb_4bit(); + + __gpio_as_input(PIN_SD1_CD); + __gpio_as_input(PIN_SD2_CD); + + __gpio_disable_pull(PIN_SD1_CD); + __gpio_disable_pull(PIN_SD2_CD); +} + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "cpu.h" +#include "backlight-target.h" +#include "lcd.h" + +bool backlight_hw_init(void) +{ + return true; +} + +void backlight_hw_on(void) +{ + lcd_enable(true); +} + +void backlight_hw_off(void) +{ + lcd_enable(false); +} + +void backlight_hw_brightness(int brightness) +{ + lcd_set_contrast(brightness*16-1); +} + +void lcd_sleep(void) +{ + backlight_hw_off(); +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef BUTTON_TARGET_H +#define BUTTON_TARGET_H + +#define HAS_BUTTON_HOLD + +/* Main unit's buttons */ +#define BUTTON_POWER 0x00000001 +#define BUTTON_HOME 0x00000002 +#define BUTTON_OPTION 0x00000004 +#define BUTTON_PREV 0x00000008 +#define BUTTON_NEXT 0x00000010 +#define BUTTON_PLAY 0x00000020 +#define BUTTON_VOL_UP 0x00000040 +#define BUTTON_VOL_DOWN 0x00000080 + +#define BUTTON_LEFT 0 +#define BUTTON_RIGHT 0 + +#define BUTTON_MAIN (BUTTON_POWER | BUTTON_HOME | BUTTON_OPTION | BUTTON_PREV | \ + BUTTON_NEXT | BUTTON_PLAY | BUTTON_VOL_UP | BUTTON_VOL_DOWN) + +/* Software power-off */ +#define POWEROFF_BUTTON BUTTON_POWER +#define POWEROFF_COUNT 10 + +#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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "config.h" + +#include "lcd.h" +#include "system.h" +#include "cpu.h" +#include "string.h" + +/* LCD pins */ +#define PIN_BL_EN (32*4+0) + +#define PIN_LCD_D0 (32*2+2) +#define PIN_LCD_D1 (32*2+3) +#define PIN_LCD_D2 (32*2+4) +#define PIN_LCD_D3 (32*2+5) +#define PIN_LCD_D4 (32*2+6) +#define PIN_LCD_D5 (32*2+7) +#define PIN_LCD_D6 (32*2+12) +#define PIN_LCD_D7 (32*2+13) + +#define PIN_LCD_RD (32*2+8) +#define PIN_LCD_DC (32*2+9) +#define PIN_LCD_CS (32*2+14) +#define PIN_LCD_RES (32*2+18) +#define PIN_LCD_WR (32*2+19) + +/* LCD setup codes */ +#define LCD_SET_LOWER_COLUMN_ADDRESS ((char)0x00) +#define LCD_SET_HIGHER_COLUMN_ADDRESS ((char)0x10) +#define LCD_SET_DISPLAY_START_LINE ((char)0x40) +#define LCD_SET_CONTRAST_CONTROL_REGISTER ((char)0x81) +#define LCD_SET_CHARGE_PUMP ((char)0x8D) +#define LCD_SET_SEGMENT_REMAP ((char)0xA0) +#define LCD_SET_SEGMENT_REMAP_INV ((char)0xA1) +#define LCD_SET_ENTIRE_DISPLAY_OFF ((char)0xA4) +#define LCD_SET_ENTIRE_DISPLAY_ON ((char)0xA5) +#define LCD_SET_NORMAL_DISPLAY ((char)0xA6) +#define LCD_SET_REVERSE_DISPLAY ((char)0xA7) +#define LCD_SET_MULTIPLEX_RATIO ((char)0xA8) +#define LCD_SET_DC_DC ((char)0xAD) +#define LCD_SET_DISPLAY_OFF ((char)0xAE) +#define LCD_SET_DISPLAY_ON ((char)0xAF) +#define LCD_SET_PAGE_ADDRESS ((char)0xB0) +#define LCD_SET_COM_OUTPUT_SCAN_DIRECTION ((char)0xC0) +#define LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV ((char)0xC8) +#define LCD_SET_DISPLAY_OFFSET ((char)0xD3) +#define LCD_SET_DISPLAY_CLOCK_AND_OSC_FREQ ((char)0xD5) +#define LCD_SET_VCOM_HW_CONFIGURATION ((char)0xDA) +#define LCD_SET_VCOM_DESELECT_LEVEL ((char)0xDB) +#define LCD_SET_PRECHARGE_PERIOD ((char)0xD9) +#define LCD_NOP ((char)0xE3) + +/* LCD command codes */ +#define LCD_CNTL_CONTRAST 0x81 /* Contrast */ +#define LCD_CNTL_OUTSCAN 0xc8 /* Output scan direction */ +#define LCD_CNTL_SEGREMAP 0xa1 /* Segment remap */ +#define LCD_CNTL_DISPON 0xaf /* Display on */ + +#define LCD_CNTL_PAGE 0xb0 /* Page address */ +#define LCD_CNTL_HIGHCOL 0x10 /* Upper column address */ +#define LCD_CNTL_LOWCOL 0x00 /* Lower column address */ + +#define LCD_COL_OFFSET 2 /* column offset */ + +static inline void bitdelay(void) +{ + unsigned int i = 15; + __asm__ __volatile__ ( + ".set noreorder \n" + "1: \n" + "bne %0, $0, 1b \n" + "addi %0, %0, -1 \n" + ".set reorder \n" + : "=r" (i) + : "0" (i) + ); +} + +void lcd_hw_init(void) +{ + REG_GPIO_PXFUNC(2) = 0x000C73FC; /* D0-D7 RD DC CS RES WR */ + REG_GPIO_PXSELC(2) = 0x000C73FC; + REG_GPIO_PXDIRS(2) = 0x000C73FC; + REG_GPIO_PXDATS(2) = 0x000C73FC; + __gpio_clear_pin(PIN_BL_EN); + __gpio_as_output(PIN_BL_EN); + __gpio_clear_pin(PIN_LCD_RES); + udelay(1); + __gpio_set_pin(PIN_LCD_RES); + __gpio_clear_pin(PIN_LCD_CS); +} + +void lcd_write_command(int byte) +{ + __gpio_clear_pin(PIN_LCD_DC); + REG_GPIO_PXDATC(2) = 0x000030FC; + REG_GPIO_PXDATS(2) = ((byte & 0xC0) << 6) | ((byte & 0x3F) << 2); + __gpio_clear_pin(PIN_LCD_WR); + bitdelay(); + __gpio_set_pin(PIN_LCD_WR); + bitdelay(); +} + +void lcd_write_data(const fb_data* p_bytes, int count) +{ + __gpio_set_pin(PIN_LCD_DC); + while (count--) + { + REG_GPIO_PXDATC(2) = 0x000030FC; + REG_GPIO_PXDATS(2) = ((*p_bytes & 0xC0) << 6) | ((*p_bytes & 0x3F) << 2); + p_bytes++; + __gpio_clear_pin(PIN_LCD_WR); + bitdelay(); + __gpio_set_pin(PIN_LCD_WR); + bitdelay(); + } +} + +void lcd_enable_power(bool onoff) +{ + if (onoff) + __gpio_set_pin(PIN_BL_EN); + else + __gpio_clear_pin(PIN_BL_EN); +} + +/** globals **/ + +static bool display_on = false; /* used by lcd_enable */ + +/*** hardware configuration ***/ + +void lcd_set_contrast(int val) +{ + lcd_write_command(LCD_CNTL_CONTRAST); + lcd_write_command(val); +} + +void lcd_set_invert_display(bool yesno) +{ + if (yesno) + lcd_write_command(LCD_SET_REVERSE_DISPLAY); + else + lcd_write_command(LCD_SET_NORMAL_DISPLAY); +} + +/* turn the display upside down (call lcd_update() afterwards) */ +void lcd_set_flip(bool yesno) +{ + if (yesno) + { + lcd_write_command(LCD_SET_SEGMENT_REMAP); + lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION); + } + else + { + lcd_write_command(LCD_SET_SEGMENT_REMAP_INV); + lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV); + } +} + +#ifdef HAVE_LCD_ENABLE +void lcd_enable(bool enable) +{ + if(display_on == enable) + return; + + if( (display_on = enable) ) /* simple '=' is not a typo ! */ + { + lcd_enable_power(enable); + lcd_write_command(LCD_SET_DISPLAY_ON); + send_event(LCD_EVENT_ACTIVATION, NULL); + } + else + { + lcd_write_command(LCD_SET_DISPLAY_OFF); + lcd_enable_power(enable); + } +} + +bool lcd_active(void) +{ + return display_on; +} +#endif + +/* LCD init, largely based on what OF does */ +void lcd_init_device(void) +{ + int i; + + lcd_hw_init(); + + /* Set display off */ + lcd_write_command(LCD_SET_DISPLAY_OFF); + + /* Set display clock and oscillator frequency */ + lcd_write_command(LCD_SET_DISPLAY_CLOCK_AND_OSC_FREQ); + lcd_write_command(0x80); + + /* Set multiplex ratio*/ + lcd_write_command(LCD_SET_MULTIPLEX_RATIO); + lcd_write_command(0x3F); + + /* Set display offset */ + lcd_write_command(LCD_SET_DISPLAY_OFFSET); + lcd_write_command(0x00); + + /* Set starting line as 0 */ + lcd_write_command(LCD_SET_DISPLAY_START_LINE); + + /* Set charge pump */ + lcd_write_command(LCD_SET_CHARGE_PUMP); + lcd_write_command(0x14); /* VCC Generated by Internal DC/DC Circuit */ + + /* Column 131 is remapped to SEG0 */ + lcd_write_command(LCD_SET_SEGMENT_REMAP_INV); + + /* Invert COM scan direction (N-1 to 0) */ + lcd_write_command(LCD_SET_COM_OUTPUT_SCAN_DIRECTION_INV); + + /* Set COM hardware configuration */ + lcd_write_command(LCD_SET_VCOM_HW_CONFIGURATION); + lcd_write_command(0x12); + + /* Set contrast control */ + lcd_write_command(LCD_SET_CONTRAST_CONTROL_REGISTER); + lcd_write_command(0xCF); /* VCC Generated by Internal DC/DC Circuit */ + + /* Set pre-charge period */ + lcd_write_command(LCD_SET_PRECHARGE_PERIOD); + lcd_write_command(0xF1); /* VCC Generated by Internal DC/DC Circuit */ + + /* Set VCOM deselect level */ + lcd_write_command(LCD_SET_VCOM_DESELECT_LEVEL); + lcd_write_command(0x40); + + /* Set normal display mode (not every pixel ON) */ + lcd_write_command(LCD_SET_ENTIRE_DISPLAY_OFF); + + /* Set normal display mode (not inverted) */ + lcd_write_command(LCD_SET_NORMAL_DISPLAY); + + fb_data p_bytes[LCD_WIDTH + 2 * LCD_COL_OFFSET]; + memset(p_bytes, 0, sizeof(p_bytes)); /* fills with 0 : pixel off */ + for(i = 0; i < 8; i++) + { + lcd_write_command (LCD_SET_PAGE_ADDRESS | (i /*& 0xf*/)); + lcd_write_data(p_bytes, LCD_WIDTH + 2 * LCD_COL_OFFSET); + } + + lcd_enable(true); + + lcd_update(); +} + +/*** Update functions ***/ + +/* Performance function that works with an external buffer + note that by and bheight are in 8-pixel units! */ +void lcd_blit_mono(const unsigned char *data, int x, int by, int width, + int bheight, int stride) +{ + if(!display_on) + return; + + /* Copy display bitmap to hardware */ + while (bheight--) + { + lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf)); + lcd_write_command (LCD_CNTL_HIGHCOL | (((x+LCD_COL_OFFSET)>>4) & 0xf)); + lcd_write_command (LCD_CNTL_LOWCOL | ((x+LCD_COL_OFFSET) & 0xf)); + + lcd_write_data(data, width); + data += stride; + } +} + + +#ifndef BOOTLOADER +/* Helper function for lcd_grey_phase_blit(). */ +void lcd_grey_data(unsigned char *values, unsigned char *phases, int count) ICODE_ATTR; +void lcd_grey_data(unsigned char *values, unsigned char *phases, int count) +{ + unsigned char a, b, c, d; + + __gpio_set_pin(PIN_LCD_DC); + while(count--) + { + c = 0; + d = 8; + while(d--) + { + a = *phases; + b = *values++; + b += a & ~0x80; + *phases++ = b; + c <<= 1; + c |= a >> 7; + } + REG_GPIO_PXDATC(2) = 0x000030FC; + REG_GPIO_PXDATS(2) = ((c & 0xC0) << 6) | ((c & 0x3F) << 2); + __gpio_clear_pin(PIN_LCD_WR); + bitdelay(); + __gpio_set_pin(PIN_LCD_WR); + bitdelay(); + } +} + +/* Performance function that works with an external buffer + note that by and bheight are in 8-pixel units! */ +void lcd_blit_grey_phase(unsigned char *values, unsigned char *phases, + int x, int by, int width, int bheight, int stride) +{ + if(!display_on) + return; + + stride <<= 3; /* 8 pixels per block */ + /* Copy display bitmap to hardware */ + while (bheight--) + { + lcd_write_command (LCD_CNTL_PAGE | (by++ & 0xf)); + lcd_write_command (LCD_CNTL_HIGHCOL | (((x+LCD_COL_OFFSET)>>4) & 0xf)); + lcd_write_command (LCD_CNTL_LOWCOL | ((x+LCD_COL_OFFSET) & 0xf)); + + lcd_grey_data(values, phases, width); + + values += stride; + phases += stride; + } +} +#endif + +/* Update the display. + This must be called after all other LCD functions that change the display. */ +void lcd_update(void) ICODE_ATTR; +void lcd_update(void) +{ + int y; + + if(!display_on) + return; + + /* Copy display bitmap to hardware */ + for (y = 0; y < LCD_FBHEIGHT; y++) + { + lcd_write_command (LCD_CNTL_PAGE | (y & 0xf)); + lcd_write_command (LCD_CNTL_HIGHCOL | ((LCD_COL_OFFSET >> 4) & 0xf)); + lcd_write_command (LCD_CNTL_LOWCOL | (LCD_COL_OFFSET & 0xf)); + + lcd_write_data (FBADDR(0, y), LCD_WIDTH); + } +} + +/* Update a fraction of the display. */ +void lcd_update_rect(int, int, int, int) ICODE_ATTR; +void lcd_update_rect(int x, int y, int width, int height) +{ + int ymax; + + if(!display_on) + return; + + /* The Y coordinates have to work on even 8 pixel rows */ + if (x < 0) + { + width += x; + x = 0; + } + + if (x + width > LCD_WIDTH) + width = LCD_WIDTH - x; + + if (width <= 0) + return; /* nothing left to do, 0 is harmful to lcd_write_data() */ + + if (y < 0) + { + height += y; + y = 0; + } + + if (y + height > LCD_HEIGHT) + height = LCD_HEIGHT - y; + + if (height <= 0) + return; /* nothing left to do */ + + ymax = (y + height-1) >> 3; + y >>= 3; + + /* Copy specified rectange bitmap to hardware */ + for (; y <= ymax; y++) + { + lcd_write_command (LCD_CNTL_PAGE | (y & 0xf)); + lcd_write_command (LCD_CNTL_HIGHCOL | (((x+LCD_COL_OFFSET) >> 4) & 0xf)); + lcd_write_command (LCD_CNTL_LOWCOL | ((x+LCD_COL_OFFSET) & 0xf)); + + lcd_write_data (FBADDR(x,y), width); + } +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "power.h" +#include "cpu.h" + +#define CHARGE_STAT_GPIO (32*1+6) /* STAT port */ + +/* Detect which power sources are present. */ +unsigned int power_input_status(void) +{ + if(!__gpio_get_pin(CHARGE_STAT_GPIO)) + return POWER_INPUT_USB_CHARGER; + + return POWER_INPUT_NONE; +} + +void power_init(void) +{ + __gpio_as_input(CHARGE_STAT_GPIO); + __gpio_disable_pull(CHARGE_STAT_GPIO); +} + +bool charging_state(void) +{ + return (power_input_status() == POWER_INPUT_USB_CHARGER); +} 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 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2016 by Roman Stolyarov + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include "system.h" +#include "cpu.h" +#include "button.h" +#include "button-target.h" +#include "powermgmt.h" +#include "kernel.h" +#include "backlight.h" +#include "logf.h" +#include "adc.h" + +#define PIN_BTN_POWER (32*0+30) +#define PIN_BTN_HOLD (32*1+15) + +#define PIN_KEY_INT (32*4+13) +#define KEY_INT_IRQ GPIO141 + +#define PIN_CHARGE_CON (32*1+7) + +#define PIN_PH_DECT (32*1+11) +#define IRQ_PH_DECT GPIO_IRQ(PIN_PH_DECT) +#define GPIO_PH_DECT GPIO43 + +#define PIN_LO_DECT (32*1+12) +#define IRQ_LO_DECT GPIO_IRQ(PIN_LO_DECT) +#define GPIO_LO_DECT GPIO44 + +static volatile unsigned short bat_val,key_val; + +bool headphones_inserted(void) +{ + return (__gpio_get_pin(PIN_PH_DECT) != 0); +} + +void button_init_device(void) +{ + key_val = 0xfff; + + __gpio_as_input(PIN_BTN_POWER); + __gpio_as_input(PIN_BTN_HOLD); + + __gpio_disable_pull(PIN_BTN_POWER); + __gpio_disable_pull(PIN_BTN_HOLD); + + __gpio_as_irq_fall_edge(PIN_KEY_INT); + system_enable_irq(GPIO_IRQ(PIN_KEY_INT)); + + __gpio_set_pin(PIN_CHARGE_CON); /* 0.7 A */ + __gpio_as_output(PIN_CHARGE_CON); + + __gpio_as_input(PIN_LO_DECT); + __gpio_as_input(PIN_PH_DECT); + + __gpio_disable_pull(PIN_LO_DECT); + __gpio_disable_pull(PIN_PH_DECT); +} + +bool button_hold(void) +{ + return (__gpio_get_pin(PIN_BTN_HOLD) ? true : false); +} + +int button_read_device(void) +{ + static bool hold_button = false; + bool hold_button_old; + + hold_button_old = hold_button; + hold_button = (__gpio_get_pin(PIN_BTN_HOLD) ? true : false); + + int btn = BUTTON_NONE; + bool gpio_btn = (__gpio_get_pin(PIN_BTN_POWER) ? false : true); + + REG_SADC_ADCFG = ADCFG_VBAT_SEL + ADCFG_CMD_AUX(1); + REG_SADC_ADENA = ADENA_VBATEN + ADENA_AUXEN; + +#ifndef BOOTLOADER + if (hold_button != hold_button_old) { + backlight_hold_changed(hold_button); + } + if (hold_button) { + return BUTTON_NONE; + } +#endif + + if (gpio_btn) + btn |= BUTTON_POWER; + + if (key_val < 261) + btn |= BUTTON_VOL_UP; + else + if (key_val < 653) + btn |= BUTTON_VOL_DOWN; + else + if (key_val < 1101) + btn |= BUTTON_PREV; + else + if (key_val < 1498) + btn |= BUTTON_NEXT; + else + if (key_val < 1839) + btn |= BUTTON_PLAY; + else + if (key_val < 2213) + btn |= BUTTON_OPTION; + else + if (key_val < 2600) + btn |= BUTTON_HOME; + + return btn; +} + +/* called on button press interrupt */ +void KEY_INT_IRQ(void) +{ +} + +const unsigned short battery_level_dangerous[BATTERY_TYPES_COUNT] = +{ + /* 5% */ + 3634 +}; + +const unsigned short battery_level_shutoff[BATTERY_TYPES_COUNT] = +{ + /* 0% */ + 3300 +}; + + +/* voltages (millivolt) of 0%, 10%, ... 100% when charging disabled */ +const unsigned short percent_to_volt_discharge[BATTERY_TYPES_COUNT][11] = +{ + { 3300, 3652, 3704, 3730, 3753, 3786, 3836, 3906, 3973, 4061, 4160 } +}; + +#if CONFIG_CHARGING +/* voltages (millivolt) of 0%, 10%, ... 100% when charging enabled */ +const unsigned short percent_to_volt_charge[11] = + { 3300, 3652, 3704, 3730, 3753, 3786, 3836, 3906, 3973, 4061, 4160 }; +#endif /* CONFIG_CHARGING */ + +/* VBAT = (BDATA/1024) * 2.5V */ +#define BATTERY_SCALE_FACTOR 2460 + +/* Returns battery voltage from ADC [millivolts] */ +int _battery_voltage(void) +{ + return (bat_val*BATTERY_SCALE_FACTOR)>>10; +} + +void adc_init(void) +{ + bat_val = 0xfff; + + __cpm_start_sadc(); + mdelay(10); + REG_SADC_ADENA = 0; /* Power Up */ + mdelay(70); + REG_SADC_ADSTATE = 0; + REG_SADC_ADCTRL = ADCTRL_MASK_ALL - ADCTRL_ARDYM - ADCTRL_VRDYM; + REG_SADC_ADCFG = ADCFG_VBAT_SEL + ADCFG_CMD_AUX(1); + REG_SADC_ADCLK = (4 << 16) | (1 << 8) | 59; /* 200KHz */ + system_enable_irq(IRQ_SADC); +} + +void adc_close(void) +{ + REG_SADC_ADENA = ADENA_POWER; /* Power Down */ + __intc_mask_irq(IRQ_SADC); + mdelay(20); + __cpm_stop_sadc(); +} + +/* Interrupt handler */ +void SADC(void) +{ + unsigned char state; + unsigned char sadcstate; + + sadcstate = REG_SADC_ADSTATE; + state = REG_SADC_ADSTATE & (~REG_SADC_ADCTRL); + REG_SADC_ADSTATE &= sadcstate; + + if(state & ADCTRL_ARDYM) + { + key_val = REG_SADC_ADADAT; + } + if(state & ADCTRL_VRDYM) + { + bat_val = REG_SADC_ADVDAT; + } +} 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) #define SYNC_WB() __asm__ __volatile__ ("sync") -#define __CACHE_OP(op, addr) \ - __asm__ __volatile__( \ - " .set noreorder \n" \ - " .set mips32\n\t \n" \ - " cache %0, %1 \n" \ - " .set mips0 \n" \ - " .set reorder \n" \ - : \ - : "i" (op), "m" (*(unsigned char *)(addr))) - -void __flush_dcache_line(unsigned long addr) +#define cache_op(base,op) \ + __asm__ __volatile__(" \ + .set noreorder; \ + .set mips3; \ + cache %1, (%0); \ + .set mips0; \ + .set reorder" \ + : \ + : "r" (base), \ + "i" (op)); + +void __icache_invalidate_all(void) { - __CACHE_OP(DCHitWBInv, addr); + unsigned long start; + unsigned long end; + + start = A_K0BASE; + end = start + CACHE_SIZE; + while(start < end) + { + cache_op(start,ICIndexInv); + start += CACHE_LINE_SIZE; + } SYNC_WB(); } -void __icache_invalidate_all(void) +void __dcache_invalidate_all(void) { - unsigned int i; - - asm volatile (".set noreorder \n" - ".set mips32 \n" - "mtc0 $0, $28 \n" /* TagLo */ - "mtc0 $0, $29 \n" /* TagHi */ - ".set mips0 \n" - ".set reorder \n" - ); - for(i=A_K0BASE; i= CACHE_SIZE) + if (size >= CACHE_SIZE*2) { __dcache_writeback_all(); - else - { + } + else { unsigned long dc_lsize = CACHE_LINE_SIZE; - + a = addr & ~(dc_lsize - 1); end = (addr + size - 1) & ~(dc_lsize - 1); - for(; a < end; a += dc_lsize) - __flush_dcache_line(a); + while (1) { + cache_op(a,DCHitWBInv); + if (a == end) + break; + a += dc_lsize; + } } + SYNC_WB(); } 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); #define HAVE_CPUCACHE_INVALIDATE //#define HAVE_CPUCACHE_FLUSH -void __dcache_writeback_all(void); -void __dcache_invalidate_all(void); +void __idcache_invalidate_all(void); void __icache_invalidate_all(void); -void __flush_dcache_line(unsigned long addr); +void __dcache_invalidate_all(void); +void __dcache_writeback_all(void); + void dma_cache_wback_inv(unsigned long addr, unsigned long size); -#define commit_discard_idcache __icache_invalidate_all -#define commit_discard_dcache __dcache_invalidate_all -#define commit_dcache __dcache_writeback_all +#define commit_discard_idcache __idcache_invalidate_all +#define commit_discard_icache __icache_invalidate_all +#define commit_discard_dcache __dcache_invalidate_all +#define commit_dcache __dcache_writeback_all #endif /* __MMU_MIPS_INCLUDE_H */ -- cgit v1.2.3