From 3ec66893e377b088c1284d2d23adb2aeea6d7965 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sat, 27 Feb 2021 22:08:58 +0000 Subject: New port: FiiO M3K on bare metal Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe --- firmware/target/mips/ingenic_x1000/sfc-x1000.c | 298 +++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 firmware/target/mips/ingenic_x1000/sfc-x1000.c (limited to 'firmware/target/mips/ingenic_x1000/sfc-x1000.c') diff --git a/firmware/target/mips/ingenic_x1000/sfc-x1000.c b/firmware/target/mips/ingenic_x1000/sfc-x1000.c new file mode 100644 index 0000000000..9537cdc035 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/sfc-x1000.c @@ -0,0 +1,298 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2021 Aidan MacDonald + * + * 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 "panic.h" +#include "sfc-x1000.h" +#include "gpio-x1000.h" +#include "irq-x1000.h" +#include "x1000/sfc.h" +#include "x1000/cpm.h" + +#ifndef BOOTLOADER_SPL +/* DMA only works once the system is properly booted */ +# define NEED_SFC_DMA +#endif + +#if defined(BOOTLOADER_SPL) +# if X1000_EXCLK_FREQ == 24000000 +# define FIXED_CLK_FREQ 600000000 +# define FIXED_CLK_SRC X1000_CLK_MPLL +# elif X1000_EXCLK_FREQ == 26000000 +# define FIXED_CLK_FREQ 598000000 +# define FIXED_CLK_SRC X1000_CLK_MPLL +# else +# error "bad EXCLK freq" +# endif +#endif + +#define FIFO_THRESH 31 + +#define SFC_STATUS_PENDING (-1) + +#ifdef NEED_SFC_DMA +static struct mutex sfc_mutex; +static struct semaphore sfc_sema; +static struct timeout sfc_lockup_tmo; +static bool sfc_inited = false; +static volatile int sfc_status; +#else +# define sfc_status SFC_STATUS_OK +#endif + +void sfc_init(void) +{ +#ifdef NEED_SFC_DMA + if(sfc_inited) + return; + + mutex_init(&sfc_mutex); + semaphore_init(&sfc_sema, 1, 0); + sfc_inited = true; +#endif +} + +void sfc_lock(void) +{ +#ifdef NEED_SFC_DMA + mutex_lock(&sfc_mutex); +#endif +} + +void sfc_unlock(void) +{ +#ifdef NEED_SFC_DMA + mutex_unlock(&sfc_mutex); +#endif +} + +void sfc_open(void) +{ + gpio_config(GPIO_A, 0x3f << 26, GPIO_DEVICE(1)); + jz_writef(CPM_CLKGR, SFC(0)); + jz_writef(SFC_GLB, OP_MODE_V(SLAVE), PHASE_NUM(1), + THRESHOLD(FIFO_THRESH), WP_EN(1)); + REG_SFC_CGE = 0; + REG_SFC_INTC = 0x1f; + REG_SFC_MEM_ADDR = 0; + +#ifdef NEED_SFC_DMA + jz_writef(SFC_GLB, OP_MODE_V(DMA), BURST_MD_V(INCR32)); + system_enable_irq(IRQ_SFC); +#endif +} + +void sfc_close(void) +{ +#ifdef NEED_SFC_DMA + system_disable_irq(IRQ_SFC); +#endif + + REG_SFC_CGE = 0x1f; + jz_writef(CPM_CLKGR, SFC(1)); +} + +void sfc_set_clock(x1000_clk_t clksrc, uint32_t freq) +{ + uint32_t in_freq; +#ifdef FIXED_CLK_FREQ + /* Small optimization to save code space in SPL by not polling clock */ + clksrc = FIXED_CLK_SRC; + in_freq = FIXED_CLK_FREQ; +#else + in_freq = clk_get(clksrc); +#endif + + uint32_t div = clk_calc_div(in_freq, freq); + jz_writef(CPM_SSICDR, CE(1), CLKDIV(div - 1), + SFC_CS(clksrc == X1000_CLK_MPLL ? 1 : 0)); + while(jz_readf(CPM_SSICDR, BUSY)); + jz_writef(CPM_SSICDR, CE(0)); +} + +#ifdef NEED_SFC_DMA +static int sfc_lockup_tmo_cb(struct timeout* tmo) +{ + (void)tmo; + + int irq = disable_irq_save(); + if(sfc_status == SFC_STATUS_PENDING) { + sfc_status = SFC_STATUS_LOCKUP; + jz_overwritef(SFC_TRIG, STOP(1)); + semaphore_release(&sfc_sema); + } + + restore_irq(irq); + return 0; +} + +static void sfc_wait_end(void) +{ + semaphore_wait(&sfc_sema, TIMEOUT_BLOCK); +} + +void SFC(void) +{ + unsigned sr = REG_SFC_SR & ~REG_SFC_INTC; + + if(jz_vreadf(sr, SFC_SR, OVER)) { + jz_overwritef(SFC_SCR, CLR_OVER(1)); + sfc_status = SFC_STATUS_OVERFLOW; + } else if(jz_vreadf(sr, SFC_SR, UNDER)) { + jz_overwritef(SFC_SCR, CLR_UNDER(1)); + sfc_status = SFC_STATUS_UNDERFLOW; + } else if(jz_vreadf(sr, SFC_SR, END)) { + jz_overwritef(SFC_SCR, CLR_END(1)); + sfc_status = SFC_STATUS_OK; + } else { + panicf("SFC IRQ bug"); + return; + } + + /* Not sure this is wholly correct */ + if(sfc_status != SFC_STATUS_OK) + jz_overwritef(SFC_TRIG, STOP(1)); + + REG_SFC_INTC = 0x1f; + semaphore_release(&sfc_sema); +} +#else +/* Note the X1000 is *very* picky about how the SFC FIFOs are accessed + * so please do NOT try to rearrange the code without testing it first! + */ + +void sfc_fifo_read(unsigned* buffer, int data_bytes) +{ + int data_words = (data_bytes + 3) / 4; + while(data_words > 0) { + if(jz_readf(SFC_SR, RREQ)) { + jz_overwritef(SFC_SCR, CLR_RREQ(1)); + + int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words; + data_words -= amount; + while(amount > 0) { + *buffer++ = REG_SFC_DATA; + amount -= 1; + } + } + } +} + +void sfc_fifo_write(const unsigned* buffer, int data_bytes) +{ + int data_words = (data_bytes + 3) / 4; + while(data_words > 0) { + if(jz_readf(SFC_SR, TREQ)) { + jz_overwritef(SFC_SCR, CLR_TREQ(1)); + + int amount = data_words > FIFO_THRESH ? FIFO_THRESH : data_words; + data_words -= amount; + while(amount > 0) { + REG_SFC_DATA = *buffer++; + amount -= 1; + } + } + } +} + +static void sfc_wait_end(void) +{ + while(jz_readf(SFC_SR, END) == 0); + jz_overwritef(SFC_SCR, CLR_TREQ(1)); +} + +#endif /* NEED_SFC_DMA */ + +int sfc_exec(const sfc_op* op) +{ +#ifdef NEED_SFC_DMA + uint32_t intc_clear = jz_orm(SFC_INTC, MSK_END); +#endif + + if(op->flags & (SFC_FLAG_READ|SFC_FLAG_WRITE)) { + jz_writef(SFC_TRAN_CONF(0), DATA_EN(1)); + REG_SFC_TRAN_LENGTH = op->data_bytes; +#ifdef NEED_SFC_DMA + REG_SFC_MEM_ADDR = PHYSADDR(op->buffer); +#endif + + if(op->flags & SFC_FLAG_READ) + { + jz_writef(SFC_GLB, TRAN_DIR_V(READ)); +#ifdef NEED_SFC_DMA + discard_dcache_range(op->buffer, op->data_bytes); + intc_clear |= jz_orm(SFC_INTC, MSK_OVER); +#endif + } + else + { + jz_writef(SFC_GLB, TRAN_DIR_V(WRITE)); +#ifdef NEED_SFC_DMA + commit_dcache_range(op->buffer, op->data_bytes); + intc_clear |= jz_orm(SFC_INTC, MSK_UNDER); +#endif + } + } else { + jz_writef(SFC_TRAN_CONF(0), DATA_EN(0)); + REG_SFC_TRAN_LENGTH = 0; +#ifdef NEED_SFC_DMA + REG_SFC_MEM_ADDR = 0; +#endif + } + + bool dummy_first = (op->flags & SFC_FLAG_DUMMYFIRST) != 0; + jz_writef(SFC_TRAN_CONF(0), + MODE(op->mode), POLL_EN(0), + ADDR_WIDTH(op->addr_bytes), + PHASE_FMT(dummy_first ? 1 : 0), + DUMMY_BITS(op->dummy_bits), + COMMAND(op->command), CMD_EN(1)); + + REG_SFC_DEV_ADDR(0) = op->addr_lo; + REG_SFC_DEV_PLUS(0) = op->addr_hi; + +#ifdef NEED_SFC_DMA + sfc_status = SFC_STATUS_PENDING; + timeout_register(&sfc_lockup_tmo, sfc_lockup_tmo_cb, 10*HZ, 0); + REG_SFC_SCR = 0x1f; + REG_SFC_INTC &= ~intc_clear; +#endif + + jz_overwritef(SFC_TRIG, FLUSH(1)); + jz_overwritef(SFC_TRIG, START(1)); + +#ifndef NEED_SFC_DMA + if(op->flags & SFC_FLAG_READ) + sfc_fifo_read((unsigned*)op->buffer, op->data_bytes); + if(op->flags & SFC_FLAG_WRITE) + sfc_fifo_write((const unsigned*)op->buffer, op->data_bytes); +#endif + + sfc_wait_end(); + +#ifdef NEED_SFC_DMA + if(op->flags & SFC_FLAG_READ) + discard_dcache_range(op->buffer, op->data_bytes); +#endif + + return sfc_status; +} -- cgit v1.2.3