From ecee2e9483e39af71df873174df070fdf952c431 Mon Sep 17 00:00:00 2001 From: Caihong Ma Date: Mon, 4 Jul 2016 15:15:54 +0800 Subject: [PATCH] I2S DMA - supplement library and examples --- .../I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino | 99 +++ .../I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino | 69 ++ libraries/CurieI2S/keywords.txt | 106 +-- libraries/CurieI2S/src/CurieI2SDMA.cpp | 275 ++++++ libraries/CurieI2S/src/CurieI2SDMA.h | 81 ++ .../common/scss_registers.h | 32 + .../libarc32_arduino101/drivers/clk_system.c | 41 + .../libarc32_arduino101/drivers/clk_system.h | 64 ++ system/libarc32_arduino101/drivers/i2s_priv.h | 302 +++++++ system/libarc32_arduino101/drivers/soc_dma.c | 793 ++++++++++++++++++ system/libarc32_arduino101/drivers/soc_dma.h | 239 ++++++ .../drivers/soc_dma_priv.h | 339 ++++++++ system/libarc32_arduino101/drivers/soc_i2s.c | 672 +++++++++++++++ system/libarc32_arduino101/drivers/soc_i2s.h | 214 +++++ variants/arduino_101/libarc32drv_arduino101.a | Bin 431312 -> 488918 bytes 15 files changed, 3278 insertions(+), 48 deletions(-) create mode 100644 libraries/CurieI2S/examples/I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino create mode 100644 libraries/CurieI2S/examples/I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino create mode 100644 libraries/CurieI2S/src/CurieI2SDMA.cpp create mode 100644 libraries/CurieI2S/src/CurieI2SDMA.h create mode 100644 system/libarc32_arduino101/drivers/clk_system.c create mode 100644 system/libarc32_arduino101/drivers/clk_system.h create mode 100644 system/libarc32_arduino101/drivers/i2s_priv.h create mode 100644 system/libarc32_arduino101/drivers/soc_dma.c create mode 100644 system/libarc32_arduino101/drivers/soc_dma.h create mode 100644 system/libarc32_arduino101/drivers/soc_dma_priv.h create mode 100644 system/libarc32_arduino101/drivers/soc_i2s.c create mode 100644 system/libarc32_arduino101/drivers/soc_i2s.h diff --git a/libraries/CurieI2S/examples/I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino b/libraries/CurieI2S/examples/I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino new file mode 100644 index 00000000..97ab8163 --- /dev/null +++ b/libraries/CurieI2S/examples/I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/** + * A simple sketch to test the rx channel of the i2s interface. + * A callback function is used to fill up a buffer whenever data is received + * + * To test this sketch you will need a second Arduino/Genuino 101 board with the I2SDMA_TxCallback sketch uploaded + * + * Connection: + * GND -> GND + * I2S_RSCK(pin 8) -> I2S_TSCK(pin 2) + * I2S_RWS(pin 3) -> I2S_TWS(pin 4) + * I2S_RXD(pin 5) -> I2S_TXD(pin 7) + * +**/ +#include + +#define BUFF_SIZE 128 +#define OFFSET 2 +uint32_t dataBuff[BUFF_SIZE+OFFSET]; // extra 2 buffer is for the padding zero + +uint8_t start_flag = 0; +uint8_t done_flag = 0; +uint32_t loop_count = 0; + +void setup() +{ + Serial.begin(115200); + while(!Serial); + Serial.println("CurieI2SDMA Rx Callback"); + + CurieI2SDMA.iniRX(); + /* + * CurieI2SDMA.beginTX(sample_rate, resolution, master,mode) + * mode 1 : PHILIPS_MODE + * 2 : RIGHT_JST_MODE + * 3 : LEFT_JST_MODE + * 4 : DSP_MODE + */ + CurieI2SDMA.beginRX(44100, 32,0,1); + +} + +void loop() +{ + int status = CurieI2SDMA.transRX(dataBuff,sizeof(dataBuff)); + if(status) + return; + + if(start_flag) + { + if((dataBuff[OFFSET]>>16) != loop_count+1) + Serial.println("+++ loop_count jump +++"); + } + else + { + start_flag = 1; + } + loop_count = (dataBuff[OFFSET] >> 16); + + done_flag = 1; + for(uint32_t i = 0 ;i < BUFF_SIZE;++i) + { + //Serial.println(dataBuff[i+OFFSET],HEX); + if ((dataBuff[i+OFFSET] & 0XFFFF0000) == (loop_count <<16) + && (dataBuff[i+OFFSET] & 0XFFFF) == (i+1)) + ; + else + { + done_flag = 0; + Serial.println(dataBuff[i+OFFSET],HEX); + Serial.println("ERROR"); + break; + } + } + + if(done_flag) + Serial.println("RX done"); + delay(100); +} + +/* + Copyright (c) 2016 Intel Corporation. All rights reserved. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110- + 1301 USA +*/ \ No newline at end of file diff --git a/libraries/CurieI2S/examples/I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino b/libraries/CurieI2S/examples/I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino new file mode 100644 index 00000000..fe27ab2e --- /dev/null +++ b/libraries/CurieI2S/examples/I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +//I2S_TX -> Pin 7 +//I2S_TSK -> Pin 4 +//I2S_TSCK -> pin 2 + +#include + +#define BUFF_SIZE 128 +boolean blinkState = true; // state of the LED +uint32_t dataBuff[BUFF_SIZE]; +uint32_t loop_count = 0; +void setup() +{ + Serial.begin(115200); + while(!Serial); + Serial.println("CurieI2SDMA Tx Callback"); + + CurieI2SDMA.iniTX(); + /* + * CurieI2SDMA.beginTX(sample_rate, resolution, master,mode) + * mode 1 : PHILIPS_MODE + * 2 : RIGHT_JST_MODE + * 3 : LEFT_JST_MODE + * 4 : DSP_MODE + */ + CurieI2SDMA.beginTX(44100, 32,1, 1); + digitalWrite(13, blinkState); +} + +void loop() +{ + for(uint32_t i = 0; i + +static void txi2s_done(void* x); +static void rxi2s_done(void* x); +static void txi2s_err(void* x); +static void rxi2s_err(void* x); + +volatile uint8_t txdone_flag = 0; +volatile uint8_t txerror_flag = 0; + +volatile uint8_t rxdone_flag = 0; +volatile uint8_t rxerror_flag = 0; + + +static void txi2s_done(void* x) +{ + txdone_flag = 1; + + return; +} + +static void rxi2s_done(void* x) +{ + rxdone_flag = 1; + + return; +} + +static void txi2s_err(void* x) +{ + txerror_flag = 1; + + return; +} + +static void rxi2s_err(void* x) +{ + rxerror_flag = 1; + + return; +} + +struct soc_i2s_cfg txcfg ; + +struct soc_i2s_cfg rxcfg ; + +Curie_I2SDMA CurieI2SDMA; + +Curie_I2SDMA::Curie_I2SDMA() +{ +} + +int Curie_I2SDMA::iniTX() +{ + muxTX(1); + soc_i2s_init(); + soc_dma_init(); + return I2S_DMA_OK; +} + +int Curie_I2SDMA::iniRX() +{ + muxRX(1); + soc_i2s_init(); + soc_dma_init(); + return I2S_DMA_OK; +} + +void Curie_I2SDMA::muxTX(bool enable) +{ + int mux_mode = GPIO_MUX_MODE; + if(enable) + { + mux_mode = I2S_MUX_MODE; + } + + /* Set SoC pin mux configuration */ + SET_PIN_MODE(g_APinDescription[I2S_TXD].ulSocPin, mux_mode); + SET_PIN_MODE(g_APinDescription[I2S_TWS].ulSocPin, mux_mode); + SET_PIN_MODE(g_APinDescription[I2S_TSCK].ulSocPin, mux_mode); + g_APinDescription[I2S_TXD].ulPinMode = mux_mode; + g_APinDescription[I2S_TWS].ulPinMode = mux_mode; + g_APinDescription[I2S_TSCK].ulPinMode = mux_mode; +} + +void Curie_I2SDMA::muxRX(bool enable) +{ + int mux_mode = GPIO_MUX_MODE; + if(enable) + { + mux_mode = I2S_MUX_MODE; + } + + /* Set SoC pin mux configuration */ + SET_PIN_MODE(49, mux_mode); //I2S_RXD + SET_PIN_MODE(51, mux_mode); //I2S_RWS + SET_PIN_MODE(50, mux_mode); //I2S_RSCK + g_APinDescription[I2S_RXD].ulPinMode = mux_mode; + g_APinDescription[I2S_RWS].ulPinMode = mux_mode; + g_APinDescription[I2S_RSCK].ulPinMode = mux_mode; +} + +int Curie_I2SDMA::beginTX(uint16_t sample_rate,uint8_t resolution,uint8_t master,uint8_t mode) +{ + switch(mode) + { + case 1: + mode = I2S_MODE_PHILLIPS ; + break; + case 2: + mode = I2S_MODE_RJ; + break; + case 3: + mode = I2S_MODE_LJ; + break; + case 4: + mode = I2S_MODE_DSP; + break; + default: + break; + } + + txcfg.sample_rate = sample_rate; + txcfg.resolution = resolution; + txcfg.mode = mode; + txcfg.master = master; + txcfg.cb_done = txi2s_done; + txcfg.cb_err = txi2s_err; + txdone_flag = 0; + txerror_flag = 0; + soc_i2s_config(I2S_CHANNEL_TX, &txcfg); + + return I2S_DMA_OK; +} + +int Curie_I2SDMA::beginRX(uint16_t sample_rate,uint8_t resolution,uint8_t master,uint8_t mode) +{ + switch(mode) + { + case 1: + mode = I2S_MODE_PHILLIPS ; + break; + case 2: + mode = I2S_MODE_RJ; + break; + case 3: + mode = I2S_MODE_LJ; + break; + case 4: + mode = I2S_MODE_DSP; + break; + default: + break; + } + + rxcfg.sample_rate = sample_rate; + rxcfg.resolution = resolution; + rxcfg.mode = mode; + rxcfg.master = master; + + rxcfg.cb_done = rxi2s_done; + rxcfg.cb_err = rxi2s_err; + + rxdone_flag = 0; + rxerror_flag = 0; + + soc_i2s_config(I2S_CHANNEL_RX, &rxcfg); + + return I2S_DMA_OK; +} + +int Curie_I2SDMA::transTX(uint32_t* buf_TX,uint32_t len) +{ + soc_i2s_stream(buf_TX, len,0); + + while (1) + { + // check the DMA and I2S status + if(txdone_flag && !txerror_flag) + { + txdone_flag = 0; + txerror_flag = 0; + return I2S_DMA_OK; + } + if(txerror_flag) + { + return I2S_DMA_FAIL; + } + } +} + +int Curie_I2SDMA::transRX(uint32_t* buf_RX,uint32_t len) +{ + soc_i2s_listen(buf_RX, len ,0); + + while (1) + { + // check the DMA and I2S status + if(rxdone_flag && !rxerror_flag) + { + rxdone_flag = 0; + rxerror_flag = 0; + return I2S_DMA_OK; + } + if(rxerror_flag) + { + return I2S_DMA_FAIL; + } + } + return I2S_DMA_OK; +} + +void Curie_I2SDMA::stopTX() +{ + soc_i2s_stop_stream(); + muxTX(0); +} + +void Curie_I2SDMA::stopRX() +{ + soc_i2s_stop_listen(); + muxRX(0); +} + +int Curie_I2SDMA::mergeData(uint32_t* buf_left,uint32_t* buf_right,uint32_t* buf_TX) +{ + int length = (int)(sizeof(buf_left) / sizeof(buf_left[0])); + for(int i = 0; i < length;++i) + { + buf_TX[2*i] = buf_left[i]; + buf_TX[2*i+1] = buf_right[i]; + } + + return I2S_DMA_OK; +} + +int Curie_I2SDMA::separateData(uint32_t* buf_left,uint32_t* buf_right,uint32_t* buf_RX) +{ + int length1 = (int)( sizeof(buf_RX) / sizeof(buf_RX[0])/2 ); + int length2 = (int)( sizeof(buf_left) / sizeof(buf_left[0]) ); + int length = length1 < length2 ? length1 : length2; + + for(int i = 0; i < length;++i) + { + buf_left[i] = buf_RX[2*i]; + buf_right[i] = buf_RX[2*i+1]; + } + return I2S_DMA_OK; +} diff --git a/libraries/CurieI2S/src/CurieI2SDMA.h b/libraries/CurieI2S/src/CurieI2SDMA.h new file mode 100644 index 00000000..a1707123 --- /dev/null +++ b/libraries/CurieI2S/src/CurieI2SDMA.h @@ -0,0 +1,81 @@ +//*************************************************************** +// +// Copyright (c) 2016 Intel Corporation. All rights reserved. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +//*************************************************************** + +//CurieI2SDMA.h + +#ifndef _CURI2SDMA_H_ +#define _CURI2SDMA_H_ + +#include + +//I2S Arduino Pins +#define I2S_TXD 7 +#define I2S_TWS 4 +#define I2S_TSCK 2 +#define I2S_RXD 5 +#define I2S_RWS 3 +#define I2S_RSCK 8 + +#define I2S_DMA_OK 0 +#define I2S_DMA_FAIL 1 +class Curie_I2SDMA +{ + private: + // mux/demux the i2s rx pins into i2s mode + void muxRX(bool enable); + + // mux/demux the i2s tx pins into i2s mode + void muxTX(bool enable); + + public: + Curie_I2SDMA(); + + // + int beginTX(uint16_t sample_rate,uint8_t resolution,uint8_t master,uint8_t mode); + // + int beginRX(uint16_t sample_rate,uint8_t resolution,uint8_t master,uint8_t mode); + + // initializes i2s interface + int iniTX(); + //void transTX(); + int iniRX(); + + // starts transmission of data to the tx channel + int transTX(uint32_t* buf_TX,uint32_t len); + + // starts listening to the rx channel + int transRX(uint32_t* buf_RX,uint32_t len); + + // merge data of left and right channel into one buffer + int mergeData(uint32_t* buf_left,uint32_t* buf_right,uint32_t* buf_TX); + + // seperate the data to left and right channl + int separateData(uint32_t* buf_left,uint32_t* buf_right,uint32_t* buf_RX); + + // + void stopTX(); + // + void stopRX(); + +}; + +extern Curie_I2SDMA CurieI2SDMA; + +#endif diff --git a/system/libarc32_arduino101/common/scss_registers.h b/system/libarc32_arduino101/common/scss_registers.h index ddf393f4..600459a4 100644 --- a/system/libarc32_arduino101/common/scss_registers.h +++ b/system/libarc32_arduino101/common/scss_registers.h @@ -120,6 +120,8 @@ #define QRK_CLKGATE_CTRL_PWM_ENABLE (1 << 12) #define PERIPH_CLK_GATE_CTRL (SCSS_REGISTER_BASE + 0x018) +#define MLAYER_AHB_CTL (SCSS_REGISTER_BASE + 0x034) +#define SS_PERIPH_CLK_GATE_CTL (SCSS_REGISTER_BASE + 0x028) /* PWM */ #define QRK_PWM_BASE_ADDR 0xB0000800 @@ -216,6 +218,9 @@ #define QRK_RTC_ENABLE (1 << 2) #define QRK_RTC_WRAP_ENABLE (1 << 3) +/* DMA */ +#define SOC_DMA_BASE (0xB0700000) + /* MPR */ #define QRK_MPR_BASE_ADDR 0xB0400000 #define QRK_MPR_REGS_LEN 0x04 @@ -244,6 +249,9 @@ #define SOC_MST_SPI1_REGISTER_BASE (0xB0001400) #define SOC_SLV_SPI_REGISTER_BASE (0xB0001800) +/* I2S */ +#define SOC_I2S_BASE (0xB0003800) + /* Mailbox Interrupt*/ #define IO_REG_MAILBOX_INT_MASK (SCSS_REGISTER_BASE + 0x4A0) @@ -392,11 +400,21 @@ #define SOC_SPIS0_INTERRUPT 0x4 #define SOC_UART0_INTERRUPT 0x5 #define SOC_UART1_INTERRUPT 0x6 +#define SOC_I2S_INTERRUPT 0x7 #define SOC_GPIO_INTERRUPT 0x8 #define SOC_PWM_INTERRUPT 0x9 #define SOC_RTC_INTERRUPT 0xb #define SOC_WDT_INTERRUPT 0xc +#define SOC_DMA_CHANNEL0_INTERRUPT 0xd +#define SOC_DMA_CHANNEL1_INTERRUPT 0xe +#define SOC_DMA_CHANNEL2_INTERRUPT 0xf +#define SOC_DMA_CHANNEL3_INTERRUPT 0x10 +#define SOC_DMA_CHANNEL4_INTERRUPT 0x11 +#define SOC_DMA_CHANNEL5_INTERRUPT 0x12 +#define SOC_DMA_CHANNEL6_INTERRUPT 0x13 +#define SOC_DMA_CHANNEL7_INTERRUPT 0x14 #define SOC_MBOX_INTERRUPT 0x15 +#define SOC_DMA_ERR_INTERRUPT 0x18 #define SOC_MPR_INTERRUPT 0x19 #define SOC_GPIO_AON_INTERRUPT 0x1F @@ -411,9 +429,19 @@ #define SOC_SPIS0_INTERRUPT (40) #define SOC_UART0_INTERRUPT (41) #define SOC_UART1_INTERRUPT (42) +#define SOC_I2S_INTERRUPT (43) #define SOC_GPIO_INTERRUPT (44) #define SOC_PWM_INTERRUPT (45) +#define SOC_DMA_CHANNEL0_INTERRUPT (49) +#define SOC_DMA_CHANNEL1_INTERRUPT (50) +#define SOC_DMA_CHANNEL2_INTERRUPT (51) +#define SOC_DMA_CHANNEL3_INTERRUPT (52) +#define SOC_DMA_CHANNEL4_INTERRUPT (53) +#define SOC_DMA_CHANNEL5_INTERRUPT (54) +#define SOC_DMA_CHANNEL6_INTERRUPT (55) +#define SOC_DMA_CHANNEL7_INTERRUPT (56) #define SOC_MBOX_INTERRUPT (57) +#define SOC_DMA_ERR_INTERRUPT (60) #define SOC_GPIO_AON_INTERRUPT (67) #define DRV_REG(_driver_) (MMIO_REG_VAL_FROM_BASE(SCSS_REGISTER_BASE, _driver_)) @@ -424,4 +452,8 @@ #endif +/* Clock gate mask */ +#define I2C0_CLK_GATE_MASK 0x00080004 +#define I2C1_CLK_GATE_MASK 0x00100008 +#define I2S_CLK_GATE_MASK 0x00200200 #endif /* SCSS_REGISTERS_H_ */ diff --git a/system/libarc32_arduino101/drivers/clk_system.c b/system/libarc32_arduino101/drivers/clk_system.c new file mode 100644 index 00000000..a3b57c79 --- /dev/null +++ b/system/libarc32_arduino101/drivers/clk_system.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "clk_system.h" +#include "scss_registers.h" + +void set_clock_gate(struct clk_gate_info_s* clk_gate_info, uint32_t value) +{ + uint32_t tmp ; + tmp = MMIO_REG_VAL(clk_gate_info->clk_gate_register); + tmp &= ~(clk_gate_info->bits_mask); + tmp |= ((clk_gate_info->bits_mask) & value); + MMIO_REG_VAL(clk_gate_info->clk_gate_register) = tmp; +} diff --git a/system/libarc32_arduino101/drivers/clk_system.h b/system/libarc32_arduino101/drivers/clk_system.h new file mode 100644 index 00000000..99b8e748 --- /dev/null +++ b/system/libarc32_arduino101/drivers/clk_system.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CLK_SYSTEM_H_ +#define CLK_SYSTEM_H_ + +#include + +#define CLK_GATE_OFF (0) +#define CLK_GATE_ON (~CLK_GATE_OFF) + +/** + * @defgroup clk_gate Clock gating driver + * Clk Gate driver API. + * @ingroup common_drivers + * @{ + */ + +/** + * Clock gate data which contain register and bits implicated. + */ +struct clk_gate_info_s { + uint32_t clk_gate_register; /*!< register changed for clock gate */ + uint32_t bits_mask; /*!< mask used for clock gate */ +}; + +/** +* Configure clock gate to specified device +* +* @param clk_gate_info : pointer to a clock gate data structure +* @param value : state of clock gate desired +*/ +void set_clock_gate(struct clk_gate_info_s* clk_gate_info, uint32_t value); + +/** @} */ + +#endif /* CLK_SYSTEM_H_ */ diff --git a/system/libarc32_arduino101/drivers/i2s_priv.h b/system/libarc32_arduino101/drivers/i2s_priv.h new file mode 100644 index 00000000..6b90af1a --- /dev/null +++ b/system/libarc32_arduino101/drivers/i2s_priv.h @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2016, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef I2S_PRIV_H_ +#define I2S_PRIV_H_ + +#include "soc_i2s.h" + +#define I2S_TFIFO_SIZE 4 +#define I2S_RFIFO_SIZE 4 + +#define I2S_RFIFO_THR 1 +#define I2S_TFIFO_THR 1 + +// I2S Registers (from base register) and fields +#define SOC_I2S_CTRL (0x00) +#define SOC_I2S_CTRL_EN_0 (0) +#define SOC_I2S_CTRL_EN_1 (1) +#define SOC_I2S_CTRL_TR_CFG_0 (8) +#define SOC_I2S_CTRL_TR_CFG_1 (9) +#define SOC_I2S_CTRL_LOOP_BACK_0_1 (16) +#define SOC_I2S_CTRL_SFR_RST (20) +#define SOC_I2S_CTRL_T_MS (21) +#define SOC_I2S_CTRL_R_MS (22) +#define SOC_I2S_CTRL_TFIFO_RST (23) +#define SOC_I2S_CTRL_RFIFO_RST (24) +#define SOC_I2S_CTRL_TSYNC_RST (25) +#define SOC_I2S_CTRL_RSYNC_RST (26) +#define SOC_I2S_CTRL_TSYNC_LOOP_BACK (27) +#define SOC_I2S_CTRL_RSYNC_LOOP_BACK (28) + +#define SOC_I2S_STAT (0x04) +#define SOC_I2S_STAT_TDATA_UNDERR (0) +#define SOC_I2S_STAT_UNDERR_CODE (1) +#define SOC_I2S_STAT_RDATA_OVRERR (4) +#define SOC_I2S_STAT_OVRERR_CODE (5) +#define SOC_I2S_STAT_TFIFO_EMPTY (8) +#define SOC_I2S_STAT_TFIFO_AEMPTY (9) +#define SOC_I2S_STAT_TFIFO_FULL (10) +#define SOC_I2S_STAT_TFIFO_AFULL (11) +#define SOC_I2S_STAT_RFIFO_EMPTY (12) +#define SOC_I2S_STAT_RFIFO_AEMPTY (13) +#define SOC_I2S_STAT_RFIFO_FULL (14) +#define SOC_I2S_STAT_RFIFO_AFULL (15) + +#define SOC_I2S_SRR (0x08) +#define SOC_I2S_SRR_TSAMPLE_RATE (0) +#define SOC_I2S_SRR_TRESOLUTION (11) +#define SOC_I2S_SRR_RSAMPLE_RATE (16) +#define SOC_I2S_SRR_RRESOLUTION (27) + +#define SOC_I2S_CID_CTRL (0x0C) +#define SOC_I2S_CID_CTRL_STROBE_0 (0) +#define SOC_I2S_CID_CTRL_STROBE_1 (1) +#define SOC_I2S_CID_CTRL_STROBE_TS (8) +#define SOC_I2S_CID_CTRL_STROBE_RS (9) +#define SOC_I2S_CID_CTRL_INTREQ_MASK (15) +#define SOC_I2S_CID_CTRL_MASK_0 (16) +#define SOC_I2S_CID_CTRL_MASK_1 (17) +#define SOC_I2S_CID_CTRL_TFIFO_EMPTY_MASK (24) +#define SOC_I2S_CID_CTRL_TFIFO_AEMPTY_MASK (25) +#define SOC_I2S_CID_CTRL_TFIFO_FULL_MASK (26) +#define SOC_I2S_CID_CTRL_TFIFO_AFULL_MASK (27) +#define SOC_I2S_CID_CTRL_RFIFO_EMPTY_MASK (28) +#define SOC_I2S_CID_CTRL_RFIFO_AEMPTY_MASK (29) +#define SOC_I2S_CID_CTRL_RFIFO_FULL_MASK (30) +#define SOC_I2S_CID_CTRL_RFIFO_AFULL_MASK (31) + +#define SOC_I2S_TFIFO_STAT (0x10) + +#define SOC_I2S_RFIFO_STAT (0x14) + +#define SOC_I2S_TFIFO_CTRL (0x18) +#define SOC_I2S_TFIFO_CTRL_TAEMPTY_THRS (0) +#define SOC_I2S_TFIFO_CTRL_TAFULL_THRS (16) + +#define SOC_I2S_RFIFO_CTRL (0x1C) +#define SOC_I2S_RFIFO_CTRL_RAEMPTY_THRS (0) +#define SOC_I2S_RFIFO_CTRL_RAFULL_THRS (16) + +#define SOC_I2S_DEV_CONF (0x20) +#define SOC_I2S_DEV_CONF_TRAN_SCK_POLAR (0) +#define SOC_I2S_DEV_CONF_TRAN_WS_POLAR (1) +#define SOC_I2S_DEV_CONF_TRAN_ABP_ALIGN_LR (2) +#define SOC_I2S_DEV_CONF_TRAN_I2S_ALIGN_LR (3) +#define SOC_I2S_DEV_CONF_TRAN_DATA_WS_DEL (4) +#define SOC_I2S_DEV_CONF_TRAN_WS_DSP_MODE (5) +#define SOC_I2S_DEV_CONF_REC_SCK_POLAR (6) +#define SOC_I2S_DEV_CONF_REC_WS_POLAR (7) +#define SOC_I2S_DEV_CONF_REC_ABP_ALIGN_LR (8) +#define SOC_I2S_DEV_CONF_REC_I2S_ALIGN_LR (9) +#define SOC_I2S_DEV_CONF_REC_DATA_WS_DEL (10) +#define SOC_I2S_DEV_CONF_REC_WS_DSP_MODE (11) + +#define SOC_I2S_DATA_REG (0x50) + +struct i2s_channel_regs { + // CRTL Register + uint8_t ctrl; + uint8_t ctrl_en; + uint8_t ctrl_tr_cfg; + uint8_t ctrl_ms; + uint8_t ctrl_fifo_rst; + uint8_t ctrl_sync_rst; + uint8_t ctrl_sync_loopback; + + // STAT Register + uint8_t stat; + uint8_t stat_err; + uint8_t stat_code; + uint8_t stat_fifo_empty; + uint8_t stat_fifo_aempty; + uint8_t stat_fifo_full; + uint8_t stat_fifo_afull; + uint32_t stat_mask; + + // SRR Register + uint8_t srr; + uint8_t srr_sample_rate; + uint8_t srr_resolution; + uint32_t srr_mask; + + // CID CTRL Register + uint8_t cid_ctrl; + uint8_t cid_ctrl_strobe; + uint8_t cid_ctrl_strobe_sync; + uint8_t cid_ctrl_mask; + uint8_t cid_ctrl_fifo_empty_mask; + uint8_t cid_ctrl_fifo_aempty_mask; + uint8_t cid_ctrl_fifo_full_mask; + uint8_t cid_ctrl_fifo_afull_mask; + + // FIFO STAT Register + uint8_t fifo_stat; + + // FIFO CTRL Register + uint8_t fifo_ctrl; + uint8_t fifo_ctrl_aempty_thr; + uint8_t fifo_ctrl_afull_thr; + + // DEV CONF Register + uint8_t dev_conf; + uint8_t dev_conf_sck_polar; + uint8_t dev_conf_ws_polar; + uint8_t dev_conf_abp_align_lr; + uint8_t dev_conf_i2s_align_lr; + uint8_t dev_conf_data_ws_del; + uint8_t dev_conf_ws_dsp_mode; + uint32_t dev_conf_mask; + + // DATA Register + uint8_t data_reg; +}; + +const struct i2s_channel_regs i2s_reg_map[I2S_NUM_CHANNELS] = { + // TX Channel + { + // CRTL Register + .ctrl = SOC_I2S_CTRL, + .ctrl_en = SOC_I2S_CTRL_EN_0, + .ctrl_tr_cfg = SOC_I2S_CTRL_TR_CFG_0, + .ctrl_ms = SOC_I2S_CTRL_T_MS, + .ctrl_fifo_rst = SOC_I2S_CTRL_TFIFO_RST, + .ctrl_sync_rst = SOC_I2S_CTRL_TSYNC_RST, + .ctrl_sync_loopback = SOC_I2S_CTRL_TSYNC_LOOP_BACK, + + // STAT Register + .stat = SOC_I2S_STAT, + .stat_err = SOC_I2S_STAT_TDATA_UNDERR, + .stat_code = SOC_I2S_STAT_UNDERR_CODE, + .stat_fifo_empty = SOC_I2S_STAT_TFIFO_EMPTY, + .stat_fifo_aempty = SOC_I2S_STAT_TFIFO_AEMPTY, + .stat_fifo_full = SOC_I2S_STAT_TFIFO_FULL, + .stat_fifo_afull = SOC_I2S_STAT_TFIFO_AFULL, + .stat_mask = 0x00000F0F, + + // SRR Register + .srr = SOC_I2S_SRR, + .srr_sample_rate = SOC_I2S_SRR_TSAMPLE_RATE, + .srr_resolution = SOC_I2S_SRR_TRESOLUTION, + .srr_mask = 0x0000FFFF, + + // CID CTRL Register + .cid_ctrl = SOC_I2S_CID_CTRL, + .cid_ctrl_strobe = SOC_I2S_CID_CTRL_STROBE_0, + .cid_ctrl_strobe_sync = SOC_I2S_CID_CTRL_STROBE_TS, + .cid_ctrl_mask = SOC_I2S_CID_CTRL_MASK_0, + .cid_ctrl_fifo_empty_mask = SOC_I2S_CID_CTRL_TFIFO_EMPTY_MASK, + .cid_ctrl_fifo_aempty_mask = SOC_I2S_CID_CTRL_TFIFO_AEMPTY_MASK, + .cid_ctrl_fifo_full_mask = SOC_I2S_CID_CTRL_TFIFO_FULL_MASK, + .cid_ctrl_fifo_afull_mask = SOC_I2S_CID_CTRL_TFIFO_AFULL_MASK, + + // FIFO STAT Register + .fifo_stat = SOC_I2S_TFIFO_STAT, + + // FIFO CTRL Register + .fifo_ctrl = SOC_I2S_TFIFO_CTRL, + .fifo_ctrl_aempty_thr = SOC_I2S_TFIFO_CTRL_TAEMPTY_THRS, + .fifo_ctrl_afull_thr = SOC_I2S_TFIFO_CTRL_TAFULL_THRS, + + // DEV CONF Register + .dev_conf = SOC_I2S_DEV_CONF, + .dev_conf_sck_polar = SOC_I2S_DEV_CONF_TRAN_SCK_POLAR, + .dev_conf_ws_polar = SOC_I2S_DEV_CONF_TRAN_WS_POLAR, + .dev_conf_abp_align_lr = SOC_I2S_DEV_CONF_TRAN_ABP_ALIGN_LR, + .dev_conf_i2s_align_lr = SOC_I2S_DEV_CONF_TRAN_I2S_ALIGN_LR, + .dev_conf_data_ws_del = SOC_I2S_DEV_CONF_TRAN_DATA_WS_DEL, + .dev_conf_ws_dsp_mode = SOC_I2S_DEV_CONF_TRAN_WS_DSP_MODE, + .dev_conf_mask = 0x0000003F, + + // DATA Register + .data_reg = SOC_I2S_DATA_REG + }, + + // RX Channel + { + // CRTL Register + .ctrl = SOC_I2S_CTRL, + .ctrl_en = SOC_I2S_CTRL_EN_1, + .ctrl_tr_cfg = SOC_I2S_CTRL_TR_CFG_1, + .ctrl_ms = SOC_I2S_CTRL_R_MS, + .ctrl_fifo_rst = SOC_I2S_CTRL_RFIFO_RST, + .ctrl_sync_rst = SOC_I2S_CTRL_RSYNC_RST, + .ctrl_sync_loopback = SOC_I2S_CTRL_RSYNC_LOOP_BACK, + + // STAT Register + .stat = SOC_I2S_STAT, + .stat_err = SOC_I2S_STAT_RDATA_OVRERR, + .stat_code = SOC_I2S_STAT_OVRERR_CODE, + .stat_fifo_empty = SOC_I2S_STAT_RFIFO_EMPTY, + .stat_fifo_aempty = SOC_I2S_STAT_RFIFO_AEMPTY, + .stat_fifo_full = SOC_I2S_STAT_RFIFO_FULL, + .stat_fifo_afull = SOC_I2S_STAT_RFIFO_AFULL, + .stat_mask = 0x0000F0F0, + + // SRR Register + .srr = SOC_I2S_SRR, + .srr_sample_rate = SOC_I2S_SRR_RSAMPLE_RATE, + .srr_resolution = SOC_I2S_SRR_RRESOLUTION, + .srr_mask = 0xFFFF0000, + + // CID CTRL Register + .cid_ctrl = SOC_I2S_CID_CTRL, + .cid_ctrl_strobe = SOC_I2S_CID_CTRL_STROBE_1, + .cid_ctrl_strobe_sync = SOC_I2S_CID_CTRL_STROBE_RS, + .cid_ctrl_mask = SOC_I2S_CID_CTRL_MASK_1, + .cid_ctrl_fifo_empty_mask = SOC_I2S_CID_CTRL_RFIFO_EMPTY_MASK, + .cid_ctrl_fifo_aempty_mask = SOC_I2S_CID_CTRL_RFIFO_AEMPTY_MASK, + .cid_ctrl_fifo_full_mask = SOC_I2S_CID_CTRL_RFIFO_FULL_MASK, + .cid_ctrl_fifo_afull_mask = SOC_I2S_CID_CTRL_RFIFO_AFULL_MASK, + + // FIFO STAT Register + .fifo_stat = SOC_I2S_RFIFO_STAT, + + // FIFO CTRL Register + .fifo_ctrl = SOC_I2S_RFIFO_CTRL, + .fifo_ctrl_aempty_thr = SOC_I2S_RFIFO_CTRL_RAEMPTY_THRS, + .fifo_ctrl_afull_thr = SOC_I2S_RFIFO_CTRL_RAFULL_THRS, + + // DEV CONF Register + .dev_conf = SOC_I2S_DEV_CONF, + .dev_conf_sck_polar = SOC_I2S_DEV_CONF_REC_SCK_POLAR, + .dev_conf_ws_polar = SOC_I2S_DEV_CONF_REC_WS_POLAR, + .dev_conf_abp_align_lr = SOC_I2S_DEV_CONF_REC_ABP_ALIGN_LR, + .dev_conf_i2s_align_lr = SOC_I2S_DEV_CONF_REC_I2S_ALIGN_LR, + .dev_conf_data_ws_del = SOC_I2S_DEV_CONF_REC_DATA_WS_DEL, + .dev_conf_ws_dsp_mode = SOC_I2S_DEV_CONF_REC_WS_DSP_MODE, + .dev_conf_mask = 0x00000FC0, + + // DATA Register + .data_reg = SOC_I2S_DATA_REG + } +}; + +#endif /* I2S_PRIV_H_ */ diff --git a/system/libarc32_arduino101/drivers/soc_dma.c b/system/libarc32_arduino101/drivers/soc_dma.c new file mode 100644 index 00000000..f0b0120c --- /dev/null +++ b/system/libarc32_arduino101/drivers/soc_dma.c @@ -0,0 +1,793 @@ +/* + * Copyright (c) 2016, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "scss_registers.h" +#include "portable.h" +#include "os/os.h" +#include "data_type.h" +#include "clk_system.h" + +#include "soc_dma.h" +#include "soc_dma_priv.h" + +// Some useful helper macros for setting and clearing bits and values (NR: dont read the value first) +// x - address or variable +// f - bit field to set or clear +#define SETB(x, f) ((x) |= (1 << (f))) +#define CLRB(x, f) ((x) &= ~(1 << (f))) + +#define SETB_NR(x, f) ((x) = (1 << (f))) +#define CLRB_NR(x, f) ((x) = ~(1 << (f))) + +// x - address or variable +// f - starting bit for value +// l - length of value +// v - value to set +#define SETV(x, f, l, v) ((x) &= ~(((1 << (l)) - 1) << (f))); \ + ((x) |= ((((1 << (l)) - 1) & (v)) << (f))) + +#define SETV_NR(x, f, l, v) ((x) = ((((1 << (l)) - 1) & (v)) << (f))) + +// Function Prototypes +static void dma_disable(struct soc_dma_channel *channel); +static void dma_enable(struct soc_dma_channel *channel); +static struct soc_dma_xfer_item *dma_find_list_end(struct soc_dma_xfer_item *head); +static struct dma_lli *dma_find_ll_end(struct dma_lli *head); +static DRIVER_API_RC dma_create_ll(struct soc_dma_channel *channel, struct soc_dma_cfg *cfg); +static void dma_interrupt_handler(void *num); +DRIVER_API_RC soc_dma_config(struct soc_dma_channel *channel, struct soc_dma_cfg *cfg); +DRIVER_API_RC soc_dma_deconfig(struct soc_dma_channel *channel); +DRIVER_API_RC soc_dma_acquire(struct soc_dma_channel *channel); +DRIVER_API_RC soc_dma_release(struct soc_dma_channel *channel); +DRIVER_API_RC soc_dma_start_transfer(struct soc_dma_channel *channel); +DRIVER_API_RC soc_dma_stop_transfer(struct soc_dma_channel *channel); +DRIVER_API_RC soc_dma_alloc_list_item(struct soc_dma_xfer_item **ret, struct soc_dma_xfer_item *base); +DRIVER_API_RC soc_dma_free_list(struct soc_dma_cfg *cfg); +DRIVER_API_RC dma_init(); + +DECLARE_INTERRUPT_HANDLER static void dma_ch0_interrupt_handler() +{ + dma_interrupt_handler((void *)0); +} + +DECLARE_INTERRUPT_HANDLER static void dma_ch1_interrupt_handler() +{ + dma_interrupt_handler((void *)1); +} + +struct soc_dma_info g_dma_info = { + .int_mask = { + INT_DMA_CHANNEL_0_MASK, + INT_DMA_CHANNEL_1_MASK, + INT_DMA_CHANNEL_2_MASK, + INT_DMA_CHANNEL_3_MASK, + INT_DMA_CHANNEL_4_MASK, + INT_DMA_CHANNEL_5_MASK, + INT_DMA_CHANNEL_6_MASK, + INT_DMA_CHANNEL_7_MASK + }, + .int_vector = { + SOC_DMA_CHANNEL0_INTERRUPT, + SOC_DMA_CHANNEL1_INTERRUPT, + SOC_DMA_CHANNEL2_INTERRUPT, + SOC_DMA_CHANNEL3_INTERRUPT, + SOC_DMA_CHANNEL4_INTERRUPT, + SOC_DMA_CHANNEL5_INTERRUPT, + SOC_DMA_CHANNEL6_INTERRUPT, + SOC_DMA_CHANNEL7_INTERRUPT + }, + .int_handler = { + dma_ch0_interrupt_handler, + dma_ch1_interrupt_handler, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + }, + .err_mask = INT_DMA_ERROR_MASK, + .err_vector = SOC_DMA_ERR_INTERRUPT +}; + +static struct soc_dma_info* dma_info = &g_dma_info; + +/* Internal Functions */ +static void dma_disable(struct soc_dma_channel *channel) +{ + uint32_t reg; + + // Turn off the given channel + reg = MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CH_EN_REG); + SETB(reg, SOC_DMA_CH_EN_REG_CH_EN_WE + (channel->id)); + CLRB(reg, SOC_DMA_CH_EN_REG_CH_EN + (channel->id)); + MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CH_EN_REG) = reg; + + // Clear interrupts and disable them + SETB_NR(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CLEAR_TFR), SOC_DMA_CLEAR_CLEAR + (channel->id)); + SETB_NR(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CLEAR_BLOCK), SOC_DMA_CLEAR_CLEAR + (channel->id)); + SETB_NR(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CLEAR_ERR), SOC_DMA_CLEAR_CLEAR + (channel->id)); + + reg = MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_TFR); + SETB(reg, SOC_DMA_MASK_INT_MASK_WE + (channel->id)); + CLRB(reg, SOC_DMA_MASK_INT_MASK + (channel->id)); + MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_TFR) = reg; + + reg = MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_BLOCK); + SETB(reg, SOC_DMA_MASK_INT_MASK_WE + (channel->id)); + CLRB(reg, SOC_DMA_MASK_INT_MASK + (channel->id)); + MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_BLOCK) = reg; + + reg = MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_ERR); + SETB(reg, SOC_DMA_MASK_INT_MASK_WE + (channel->id)); + CLRB(reg, SOC_DMA_MASK_INT_MASK + (channel->id)); + MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_ERR) = reg; + + CLRB(dma_regs[channel->id].CTL_L, SOC_DMA_CTL_L_INT_EN); + + // Deactivate channel + channel->active = 0; + CLRB(dma_info->active, channel->id); + + return; +} + +static void dma_enable(struct soc_dma_channel *channel) +{ + uint32_t reg; + + // Clear interrupts and enable them + SETB_NR(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CLEAR_TFR), SOC_DMA_CLEAR_CLEAR + (channel->id)); + SETB_NR(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CLEAR_BLOCK), SOC_DMA_CLEAR_CLEAR + (channel->id)); + SETB_NR(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CLEAR_ERR), SOC_DMA_CLEAR_CLEAR + (channel->id)); + + reg = MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_TFR); + SETB(reg, SOC_DMA_MASK_INT_MASK_WE + (channel->id)); + SETB(reg, SOC_DMA_MASK_INT_MASK + (channel->id)); + MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_TFR) = reg; + + reg = MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_BLOCK); + SETB(reg, SOC_DMA_MASK_INT_MASK_WE + (channel->id)); + SETB(reg, SOC_DMA_MASK_INT_MASK + (channel->id)); + MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_BLOCK) = reg; + + reg = MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_ERR); + SETB(reg, SOC_DMA_MASK_INT_MASK_WE + (channel->id)); + SETB(reg, SOC_DMA_MASK_INT_MASK + (channel->id)); + MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_MASK_ERR) = reg; + + SETB(dma_regs[channel->id].CTL_L, SOC_DMA_CTL_L_INT_EN); + + // Active channel + channel->active = 1; + SETB(dma_info->active, channel->id); + + // Turn on the given channel + reg = MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CH_EN_REG); + SETB(reg, SOC_DMA_CH_EN_REG_CH_EN_WE + channel->id); + SETB(reg, SOC_DMA_CH_EN_REG_CH_EN + channel->id); + MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CH_EN_REG) = reg; + + return; +} + +static struct soc_dma_xfer_item *dma_find_list_end(struct soc_dma_xfer_item *head) +{ + struct soc_dma_xfer_item *t; + struct soc_dma_xfer_item *h; + + // Get rid of easy cases + if ((head == NULL) || (head->next == NULL) || + (head->next->next == NULL)) + { + return NULL; + } + + // Tortoise and hare check for cyclic ll + t = head->next; + h = head->next->next; + while (t != h) + { + h = h->next; + if (h == NULL) + { + return NULL; + } + h = h->next; + if (h == NULL) + { + return NULL; + } + t = t->next; + } + + // Find the meeting place since a cycle was detected + t = head; + while (t != h) + { + t = t->next; + h = h->next; + } + + return t; +} + +static struct dma_lli *dma_find_ll_end(struct dma_lli *head) +{ + struct dma_lli *t; + struct dma_lli *h; + + // Get rid of easy cases + if ((head == NULL) || (((struct dma_lli *)(head->llp)) == NULL) || + (((struct dma_lli *)(((struct dma_lli *)(head->llp))->llp)) == NULL)) + { + return NULL; + } + + // Tortoise and hare check for cyclic ll + t = (struct dma_lli *)(head->llp); + h = (struct dma_lli *)(((struct dma_lli *)(head->llp))->llp); + while (t != h) + { + h = (struct dma_lli *)(h->llp); + if (h == NULL) + { + return NULL; + } + h = (struct dma_lli *)(h->llp); + if (h == NULL) + { + return NULL; + } + t = (struct dma_lli *)(t->llp); + } + + // Find the meeting place since a cycle was detected + t = head; + while (t != h) + { + t = (struct dma_lli *)(t->llp); + h = (struct dma_lli *)(h->llp); + } + + return t; +} + +static DRIVER_API_RC dma_create_ll(struct soc_dma_channel *channel, struct soc_dma_cfg *cfg) +{ + OS_ERR_TYPE err; + struct dma_lli *lli; + struct dma_lli *last_lli; + struct soc_dma_xfer_item *xfer; + struct soc_dma_xfer_item *last_xfer; + uint8_t list_done; + uint16_t size_left; + uint32_t curr_src; + uint8_t src_delta_amount; + uint32_t curr_dest; + uint8_t dest_delta_amount; + uint32_t reg; + + // Save the LL + channel->ll = balloc(sizeof(struct dma_lli), &err); + lli = (struct dma_lli *)(channel->ll); + last_lli = NULL; + + if (channel->ll == NULL) + { + return DRV_RC_FAIL; + } + + xfer = &(cfg->xfer); + last_xfer = dma_find_list_end(xfer); + + list_done = 0; + + // Set register to default CTL_L register value + reg = 0; + SETB(reg, SOC_DMA_CTL_L_LLP_SRC_EN); + SETB(reg, SOC_DMA_CTL_L_LLP_DST_EN); + SETV(reg, SOC_DMA_CTL_L_TT_FC, SOC_DMA_CTL_L_TT_FC_LEN, cfg->type); + SETV(reg, SOC_DMA_CTL_L_SRC_TR_WIDTH, SOC_DMA_CTL_L_SRC_TR_WIDTH_LEN, SOC_DMA_WIDTH_32); + SETV(reg, SOC_DMA_CTL_L_DST_TR_WIDTH, SOC_DMA_CTL_L_DST_TR_WIDTH_LEN, SOC_DMA_WIDTH_32); + SETB(reg, SOC_DMA_CTL_L_INT_EN); + + while (!list_done) + { + size_left = xfer->size; + curr_src = (uint32_t)(xfer->src.addr); + curr_dest = (uint32_t)(xfer->dest.addr); + + if (xfer == last_xfer) + { + last_lli = lli; + } + + while (size_left) + { + lli->sar = curr_src; + lli->dar = curr_dest; + + // Set the control register for this block + SETV(lli->ctl_u, SOC_DMA_CTL_U_BLOCK_TS, SOC_DMA_CTL_U_BLOCK_TS_LEN, + (size_left > SOC_DMA_BLOCK_SIZE_MAX) ? SOC_DMA_BLOCK_SIZE_MAX : size_left); + + lli->ctl_l = reg; + + if (cfg->src_step_count) + { + SETB(lli->ctl_l, SOC_DMA_CTL_L_SRC_GATHER_EN); + + // Scatter-Gather adds too much complexity for breaking up big blocks + if (size_left > SOC_DMA_BLOCK_SIZE_MAX) + { + soc_dma_deconfig(channel); + return DRV_RC_INVALID_CONFIG; + } + } + + if (cfg->dest_step_count) + { + SETB(lli->ctl_l, SOC_DMA_CTL_L_DST_SCATTER_EN); + + // Scatter-Gather adds too much complexity for breaking up big blocks + if (size_left > SOC_DMA_BLOCK_SIZE_MAX) + { + soc_dma_deconfig(channel); + return DRV_RC_INVALID_CONFIG; + } + } + + SETV(lli->ctl_l, SOC_DMA_CTL_L_SINC, SOC_DMA_CTL_L_SINC_LEN, xfer->src.delta); + SETV(lli->ctl_l, SOC_DMA_CTL_L_DINC, SOC_DMA_CTL_L_DINC_LEN, xfer->dest.delta); + + SETV(lli->ctl_l, SOC_DMA_CTL_L_SRC_TR_WIDTH, SOC_DMA_CTL_L_SRC_TR_WIDTH_LEN, xfer->src.width); + SETV(lli->ctl_l, SOC_DMA_CTL_L_DST_TR_WIDTH, SOC_DMA_CTL_L_DST_TR_WIDTH_LEN, xfer->dest.width); + + if (size_left > SOC_DMA_BLOCK_SIZE_MAX) + { + lli->end_group = 0; + lli->llp = (uint32_t)balloc(sizeof(struct dma_lli), &err); + + if (lli->llp == 0) + { + soc_dma_deconfig(channel); + return DRV_RC_FAIL; + } + + // Calculate new addresses for next block + size_left -= SOC_DMA_BLOCK_SIZE_MAX; + + src_delta_amount = ((xfer->src.width == SOC_DMA_WIDTH_8) ? 1 : + ((xfer->src.width == SOC_DMA_WIDTH_16) ? 2 : 4)); + dest_delta_amount = ((xfer->dest.width == SOC_DMA_WIDTH_8) ? 1 : + ((xfer->dest.width == SOC_DMA_WIDTH_16) ? 2 : 4)); + + switch (xfer->src.delta) + { + case SOC_DMA_DELTA_INCR: + curr_src += (src_delta_amount * SOC_DMA_BLOCK_SIZE_MAX); + break; + case SOC_DMA_DELTA_DECR: + curr_src -= (src_delta_amount * SOC_DMA_BLOCK_SIZE_MAX); + break; + } + + switch (xfer->dest.delta) + { + case SOC_DMA_DELTA_INCR: + curr_dest += (dest_delta_amount * SOC_DMA_BLOCK_SIZE_MAX); + break; + case SOC_DMA_DELTA_DECR: + curr_dest -= (dest_delta_amount * SOC_DMA_BLOCK_SIZE_MAX); + break; + } + + lli = (struct dma_lli *)lli->llp; + } + else + { + // Terminate this group of blocks for this xfer item + lli->end_group = 1; + size_left = 0; + } + } + + // Are we on the last block? If so, complete the list and leave, else setup the next loop + if ((xfer->next) == last_xfer) + { + lli->llp = (uint32_t)last_lli; + list_done = 1; + + if (lli->llp == 0) + { + SETB(lli->ctl_u, SOC_DMA_CTL_U_DONE_BIT); + CLRB(lli->ctl_l, SOC_DMA_CTL_L_LLP_SRC_EN); + CLRB(lli->ctl_l, SOC_DMA_CTL_L_LLP_DST_EN); + } + } + else + { + lli->llp = (uint32_t)balloc(sizeof(struct dma_lli), &err); + + if (lli->llp == 0) + { + soc_dma_deconfig(channel); + return DRV_RC_FAIL; + } + + xfer = xfer->next; + lli = (struct dma_lli *)lli->llp; + } + } + + return DRV_RC_OK; +} + +/* ISR */ +static void dma_interrupt_handler(void *num) +{ + uint32_t id = (uint32_t)num; + struct dma_lli *curr = ((struct dma_lli *)(dma_info->channel[id]->curr)); + + // Figure out whether this was a block or done interrupt + if (MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_STATUS_TFR) & (1 << (SOC_DMA_STATUS_STATUS + id))) + { + if ((dma_info->channel[id]) && (dma_info->channel[id]->cfg.cb_done)) + { + dma_info->channel[id]->cfg.cb_done(dma_info->channel[id]->cfg.cb_done_arg); + } + + // The user's callback might have already released the channel + if (dma_info->channel[id]) + { + dma_disable(dma_info->channel[id]); + } + } + else if (MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_STATUS_BLOCK) & (1 << (SOC_DMA_STATUS_STATUS + id))) + { + if ((dma_info->channel[id]) && (dma_info->channel[id]->cfg.cb_block) && (curr->end_group)) + { + dma_info->channel[id]->cfg.cb_block(dma_info->channel[id]->cfg.cb_block_arg); + } + + if (dma_info->channel[id]) + { + dma_info->channel[id]->curr = ((void *)(curr->llp)); + SETB_NR(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_CLEAR_BLOCK), SOC_DMA_CLEAR_CLEAR + id); + } + } + + return; +} + +DECLARE_INTERRUPT_HANDLER static void dma_interrupt_err_handler() +{ + uint32_t reg = MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_STATUS_ERR); + + // Loop through channels and see which have errors; calling callback and disabling + for (int i = 0; i < SOC_DMA_NUM_CHANNELS; i++) + { + if (reg & (1 << (SOC_DMA_STATUS_STATUS + i))) + { + if ((dma_info->channel[i]) && (dma_info->channel[i]->cfg.cb_err)) + { + dma_info->channel[i]->cfg.cb_err(dma_info->channel[i]->cfg.cb_err_arg); + } + + if (dma_info->channel[i]) + { + dma_disable(dma_info->channel[i]); + } + } + } + + return; +} + +/* External API */ +DRIVER_API_RC soc_dma_config(struct soc_dma_channel *channel, struct soc_dma_cfg *cfg) +{ + DRIVER_API_RC ret; + uint32_t id = channel->id; + + // Check channel to be sure its alright to configure + if (channel == NULL) + { + return DRV_RC_FAIL; + } + else if (channel->active) + { + return DRV_RC_CONTROLLER_IN_USE; + } + + // Setup the config register + CLRB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_L), SOC_DMA_CFG_L_RELOAD_DST); + CLRB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_L), SOC_DMA_CFG_L_RELOAD_SRC); + + CLRB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_L), SOC_DMA_CFG_L_SRC_HS_POL); + CLRB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_L), SOC_DMA_CFG_L_DST_HS_POL); + + CLRB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_L), SOC_DMA_CFG_L_HS_SEL_SRC); + CLRB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_L), SOC_DMA_CFG_L_HS_SEL_DST); + + SETV(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_U), + SOC_DMA_CFG_U_DEST_PER, SOC_DMA_CFG_U_DEST_PER_LEN, cfg->dest_interface); + SETV(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_U), + SOC_DMA_CFG_U_SRC_PER, SOC_DMA_CFG_U_SRC_PER_LEN, cfg->src_interface); + + CLRB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_U), SOC_DMA_CFG_U_SS_UPD_EN); + CLRB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_U), SOC_DMA_CFG_U_DS_UPD_EN); + + SETB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_U), SOC_DMA_CFG_U_FIFO_MODE); + CLRB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].CFG_U), SOC_DMA_CFG_U_FCMODE); + + // Create new LL for DMA transfer + ret = dma_create_ll(channel, cfg); + + if (ret != DRV_RC_OK) + { + return ret; + } + + // Setup scatter-gather registers + SETV(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].SGR), + SOC_DMA_SGR_SGC, SOC_DMA_SGR_SGC_LEN, cfg->src_step_count); + SETV(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].SGR), + SOC_DMA_SGR_SGI, SOC_DMA_SGR_SGI_LEN, cfg->src_step_interval); + + SETV(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].DSR), + SOC_DMA_DSR_DSC, SOC_DMA_DSR_DSC_LEN, cfg->dest_step_count); + SETV(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[id].DSR), + SOC_DMA_DSR_DSI, SOC_DMA_DSR_DSI_LEN, cfg->dest_step_interval); + + // Setup the channel structs cfg stuff + channel->cfgd = 1; + channel->cfg = *cfg; + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_dma_deconfig(struct soc_dma_channel *channel) +{ + struct dma_lli *lli; + struct dma_lli *lli_next; + struct dma_lli *last_lli; + + // Check channel to be sure its alright to deconfigure + if (channel == NULL) + { + return DRV_RC_FAIL; + } + else if (channel->active) + { + return DRV_RC_CONTROLLER_IN_USE; + } + + // Free the link list + lli = (struct dma_lli *)(channel->ll); + last_lli = dma_find_ll_end(lli); + + while (lli) + { + if (((struct dma_lli *)(lli->llp)) == last_lli) + { + bfree(lli); + lli = NULL; + } + else + { + lli_next = ((struct dma_lli *)(lli->llp)); + bfree(lli); + lli = lli_next; + } + } + + channel->ll = NULL; + + channel->cfgd = 0; + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_dma_acquire(struct soc_dma_channel *channel) +{ + uint32_t save; + + // Get a channel from the semaphore if one is free + save = interrupt_lock(); + + // Find the first free channel and set it up in the given struct + for (int i = 0; i < SOC_DMA_NUM_CHANNELS; i++) + { + if (dma_info->channel[i] == NULL) + { + dma_info->channel[i] = channel; + channel->id = i; + channel->active = 0; + soc_dma_deconfig(channel); + interrupt_unlock(save); + return DRV_RC_OK; + } + } + + // Somehow, we got an item from the semaphore but found no free channels... + interrupt_unlock(save); + + return DRV_RC_FAIL; +} + +DRIVER_API_RC soc_dma_release(struct soc_dma_channel *channel) +{ + + if (channel->active) + { + return DRV_RC_CONTROLLER_IN_USE; + } + + // Deconfig the channel if still configured + if (channel->cfgd) + { + soc_dma_deconfig(channel); + } + + // Clear the channel + dma_info->channel[channel->id] = NULL; + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_dma_start_transfer(struct soc_dma_channel *channel) +{ + uint32_t save; + + if (!channel->cfgd) + { + return DRV_RC_FAIL; + } + + // Setup the control register for first "transfer" + SETB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[channel->id].CTL_L), SOC_DMA_CTL_L_LLP_SRC_EN); + SETB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[channel->id].CTL_L), SOC_DMA_CTL_L_LLP_DST_EN); + + SETB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[channel->id].CTL_L), SOC_DMA_CTL_L_INT_EN); + + SETV(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[channel->id].CTL_L), + SOC_DMA_CTL_L_TT_FC, SOC_DMA_CTL_L_TT_FC_LEN, channel->cfg.type); + + // Setup LLP register to point to first block + SETV(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, dma_regs[channel->id].LLP), + SOC_DMA_LLP_LOC, SOC_DMA_LLP_LOC_LEN, ((uint32_t)(channel->ll)) >> SOC_DMA_LLP_LOC); + + // Set the current LLI pointer to point at the head of the list + channel->curr = channel->ll; + + save = interrupt_lock(); + dma_enable(channel); + interrupt_unlock(save); + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_dma_stop_transfer(struct soc_dma_channel *channel) +{ + uint32_t save; + + save = interrupt_lock(); + dma_disable(channel); + interrupt_unlock(save); + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_dma_alloc_list_item(struct soc_dma_xfer_item **ret, struct soc_dma_xfer_item *base) +{ + OS_ERR_TYPE err; + + *ret = balloc(sizeof(struct soc_dma_xfer_item), &err); + + if (*ret == NULL) + { + *ret = NULL; + return DRV_RC_FAIL; + } + + // Copy and connect to the base if one is given + if (base) + { + **ret = *base; + base->next = (*ret); + } + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_dma_free_list(struct soc_dma_cfg *cfg) +{ + struct soc_dma_xfer_item *p; + struct soc_dma_xfer_item *np; + struct soc_dma_xfer_item *lp; + + p = cfg->xfer.next; + lp = dma_find_list_end(&(cfg->xfer)); + + while (p) + { + if (p->next == lp) + { + bfree(p); + p = NULL; + } + else + { + np = p->next; + bfree(p); + p = np; + } + } + + cfg->xfer.next = NULL; + + return DRV_RC_OK; +} + +/* Driver API */ +DRIVER_API_RC soc_dma_init() +{ + struct soc_dma_channel ch; + + dma_info->active = 0; + + // Enable global clock + SETB(MMIO_REG_VAL(MLAYER_AHB_CTL), 6); + + // Setup ISRs (and enable) + for (int i = 0; i < SOC_DMA_NUM_CHANNELS; i++) + { + SET_INTERRUPT_HANDLER(dma_info->int_vector[i], dma_info->int_handler[i]); + SOC_UNMASK_INTERRUPTS(dma_info->int_mask[i]); + } + SET_INTERRUPT_HANDLER(dma_info->err_vector, &dma_interrupt_err_handler); + SOC_UNMASK_INTERRUPTS(dma_info->err_mask); + + // Setup global DMA registers settings + SETB(MMIO_REG_VAL_FROM_BASE(SOC_DMA_BASE, SOC_DMA_DMA_CFG_REG), SOC_DMA_DMA_CFG_REG_DMA_EN); + + // Disable all channels for now and leave the channel array empty + for (int i = 0; i < SOC_DMA_NUM_CHANNELS; i++) + { + ch.id = i; + dma_disable(&ch); + dma_info->channel[i] = NULL; + } + + return DRV_RC_OK; +} + diff --git a/system/libarc32_arduino101/drivers/soc_dma.h b/system/libarc32_arduino101/drivers/soc_dma.h new file mode 100644 index 00000000..e81d071b --- /dev/null +++ b/system/libarc32_arduino101/drivers/soc_dma.h @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2016, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SOC_DMA_H_ +#define SOC_DMA_H_ + +#include "data_type.h" +#include "os/os.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SOC_DMA_NUM_CHANNELS 8 + +// Valid values for type +#define SOC_DMA_TYPE_MEM2MEM 0 +#define SOC_DMA_TYPE_MEM2PER 1 +#define SOC_DMA_TYPE_PER2MEM 2 +#define SOC_DMA_TYPE_PER2PER 3 + +// Valid values for delta +#define SOC_DMA_DELTA_INCR 0 +#define SOC_DMA_DELTA_DECR 1 +#define SOC_DMA_DELTA_NONE 2 + +// Valid values for width +#define SOC_DMA_WIDTH_8 0 +#define SOC_DMA_WIDTH_16 1 +#define SOC_DMA_WIDTH_32 2 + +// Valid values for dest_interface or src_interface +#define SOC_DMA_INTERFACE_UART0_TX 0 +#define SOC_DMA_INTERFACE_UART0_RX 1 +#define SOC_DMA_INTERFACE_UART1_TX 2 +#define SOC_DMA_INTERFACE_UART1_RX 3 +#define SOC_DMA_INTERFACE_SPIM0_TX 4 +#define SOC_DMA_INTERFACE_SPIM0_RX 5 +#define SOC_DMA_INTERFACE_SPIM1_TX 6 +#define SOC_DMA_INTERFACE_SPIM1_RX 7 +#define SOC_DMA_INTERFACE_SPIS_TX 8 +#define SOC_DMA_INTERFACE_SPIS_RX 9 +#define SOC_DMA_INTERFACE_I2S_TX 10 +#define SOC_DMA_INTERFACE_I2S_RX 11 +#define SOC_DMA_INTERFACE_I2C0_TX 12 +#define SOC_DMA_INTERFACE_I2C0_RX 13 +#define SOC_DMA_INTERFACE_I2C1_TX 14 +#define SOC_DMA_INTERFACE_I2C1_RX 15 + +// Part of the list item that is the same from destination and source +struct soc_dma_xfer_part { + uint8_t delta; + uint8_t width; + void *addr; +}; + +// DMa transfer list item +struct soc_dma_xfer_item { + struct soc_dma_xfer_part src; + struct soc_dma_xfer_part dest; + struct soc_dma_xfer_item *next; + uint16_t size; +}; + +// DMA configuration object +struct soc_dma_cfg { + uint8_t type; + + uint8_t dest_interface; + uint8_t src_interface; + uint8_t dest_step_count; + uint8_t src_step_count; + uint32_t dest_step_interval; + uint32_t src_step_interval; + + struct soc_dma_xfer_item xfer; + + void (*cb_done)(void *); + void (*cb_block)(void *); + void (*cb_err)(void *); + void *cb_done_arg; + void *cb_block_arg; + void *cb_err_arg; +}; + +// DMA channel object +struct soc_dma_channel { + uint8_t id; + uint8_t active; + uint8_t cfgd; + struct soc_dma_cfg cfg; + void *ll; + void *curr; +}; + +typedef void (*isr_func)(void); + +// Internal struct for use by the controller (and soc_config) +struct soc_dma_info { + uint32_t int_mask[SOC_DMA_NUM_CHANNELS]; + uint32_t int_vector[SOC_DMA_NUM_CHANNELS]; + isr_func int_handler[SOC_DMA_NUM_CHANNELS]; + uint32_t err_mask; + uint32_t err_vector; + + struct soc_dma_channel* channel[SOC_DMA_NUM_CHANNELS]; + + uint32_t active; +}; + + +/** + * Function to configure a DMA channel (and set it up for transfer) + * + * @param channel : pointer to channel object + * @param cfg : pointer to configuration by which to configure the channel + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_INVALID_CONFIG - if any configuration parameters are not valid + * - DRV_RC_CONTROLLER_IN_USE, - if controller is in use + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_dma_config(struct soc_dma_channel *channel, struct soc_dma_cfg *cfg); + +/** + * Function to deconfigure and disable a DMA channel + * + * @param channel : pointer to channel object + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_CONTROLLER_IN_USE, - if controller is in use + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_dma_deconfig(struct soc_dma_channel *channel); + +/** + * Function to acquire a free DMA channel, must be called before any other DMA functions are used + * + * @param channel : pointer to channel object + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_dma_acquire(struct soc_dma_channel *channel); + +/** + * Function to release a held DMA channel so it can be used by others + * + * @param channel : pointer to channel object + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_CONTROLLER_IN_USE, - if controller is in use + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_dma_release(struct soc_dma_channel *channel); + +/** + * Function to start a DMA transfer, the channel must be configured before this function is called + * + * @param channel : pointer to channel object + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_dma_start_transfer(struct soc_dma_channel *channel); + +/** + * Function to stop an ongoing DMA channel, does nothing if no transfer is in progress + * + * @param channel : pointer to channel object + * + * @return + * - DRV_RC_OK on success + */ +DRIVER_API_RC soc_dma_stop_transfer(struct soc_dma_channel *channel); + +/** + * Function to create a new node for a DMA xfer list. If base is provided, the allocated item will + * inherit all the fields from base and the new item will be linked as the item after base + * + * @param ret : pointer to pointer which will be assigned to the new list item + * @param base : pointer to list item to be used as a base + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_dma_alloc_list_item(struct soc_dma_xfer_item **ret, struct soc_dma_xfer_item *base); + +/** + * Function to free a DMA xfer list (given in the configuration object), should only be used if the + * list was allocated with soc_dma_alloc_list_item + * + * @param cfg : pointer to configuration object + * + * @return + * - DRV_RC_OK on success + */ +DRIVER_API_RC soc_dma_free_list(struct soc_dma_cfg *cfg); + +DRIVER_API_RC soc_dma_init(); + +#ifdef __cplusplus +} +#endif + +#endif /* SOC_DMA_H_ */ diff --git a/system/libarc32_arduino101/drivers/soc_dma_priv.h b/system/libarc32_arduino101/drivers/soc_dma_priv.h new file mode 100644 index 00000000..5497e3a0 --- /dev/null +++ b/system/libarc32_arduino101/drivers/soc_dma_priv.h @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2016, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SOC_DMA_PRIV_H_ +#define SOC_DMA_PRIV_H_ + +#include "soc_dma.h" + +// Timeout on acquiring the DMA semaphore +#define SOC_DMA_SEMAPHORE_DELAY 1000 + +// Largest size a single DMA transfer can be (limited by BLOCK_TS bits in CTL_H) +#define SOC_DMA_BLOCK_SIZE_MAX 4095 + +// DMA Register map and fields +#define SOC_DMA_SAR_0 (0x000) +#define SOC_DMA_SAR_1 (0x058) +#define SOC_DMA_SAR_2 (0x0B0) +#define SOC_DMA_SAR_3 (0x108) +#define SOC_DMA_SAR_4 (0x160) +#define SOC_DMA_SAR_5 (0x1B8) +#define SOC_DMA_SAR_6 (0x210) +#define SOC_DMA_SAR_7 (0x268) +#define SOC_DMA_SAR_SAR (0) +#define SOC_DMA_SAR_SAR_LEN (32) + +#define SOC_DMA_DAR_0 (0x008) +#define SOC_DMA_DAR_1 (0x060) +#define SOC_DMA_DAR_2 (0x0B8) +#define SOC_DMA_DAR_3 (0x110) +#define SOC_DMA_DAR_4 (0x168) +#define SOC_DMA_DAR_5 (0x1C0) +#define SOC_DMA_DAR_6 (0x218) +#define SOC_DMA_DAR_7 (0x270) +#define SOC_DMA_DAR_DAR (0) +#define SOC_DMA_DAR_DAR_LEN (32) + +#define SOC_DMA_LLP_0 (0x010) +#define SOC_DMA_LLP_1 (0x068) +#define SOC_DMA_LLP_2 (0x0C0) +#define SOC_DMA_LLP_3 (0x118) +#define SOC_DMA_LLP_4 (0x170) +#define SOC_DMA_LLP_5 (0x1C8) +#define SOC_DMA_LLP_6 (0x220) +#define SOC_DMA_LLP_7 (0x278) +#define SOC_DMA_LLP_LOC (2) +#define SOC_DMA_LLP_LOC_LEN (30) + +#define SOC_DMA_CTL_L_0 (0x018) +#define SOC_DMA_CTL_L_1 (0x070) +#define SOC_DMA_CTL_L_2 (0x0C8) +#define SOC_DMA_CTL_L_3 (0x120) +#define SOC_DMA_CTL_L_4 (0x178) +#define SOC_DMA_CTL_L_5 (0x1D0) +#define SOC_DMA_CTL_L_6 (0x228) +#define SOC_DMA_CTL_L_7 (0x280) +#define SOC_DMA_CTL_L_LLP_SRC_EN (28) +#define SOC_DMA_CTL_L_LLP_DST_EN (27) +#define SOC_DMA_CTL_L_TT_FC (20) +#define SOC_DMA_CTL_L_TT_FC_LEN (3) +#define SOC_DMA_CTL_L_DST_SCATTER_EN (18) +#define SOC_DMA_CTL_L_SRC_GATHER_EN (17) +#define SOC_DMA_CTL_L_SRC_MSIZE (14) +#define SOC_DMA_CTL_L_SRC_MSIZE_LEN (3) +#define SOC_DMA_CTL_L_DEST_MSIZE (11) +#define SOC_DMA_CTL_L_DEST_MSIZE_LEN (3) +#define SOC_DMA_CTL_L_SINC (9) +#define SOC_DMA_CTL_L_SINC_LEN (2) +#define SOC_DMA_CTL_L_DINC (7) +#define SOC_DMA_CTL_L_DINC_LEN (2) +#define SOC_DMA_CTL_L_SRC_TR_WIDTH (4) +#define SOC_DMA_CTL_L_SRC_TR_WIDTH_LEN (3) +#define SOC_DMA_CTL_L_DST_TR_WIDTH (1) +#define SOC_DMA_CTL_L_DST_TR_WIDTH_LEN (3) +#define SOC_DMA_CTL_L_INT_EN (0) + +#define SOC_DMA_CTL_U_0 (0x01C) +#define SOC_DMA_CTL_U_1 (0x074) +#define SOC_DMA_CTL_U_2 (0x0CC) +#define SOC_DMA_CTL_U_3 (0x124) +#define SOC_DMA_CTL_U_4 (0x17C) +#define SOC_DMA_CTL_U_5 (0x1D4) +#define SOC_DMA_CTL_U_6 (0x22C) +#define SOC_DMA_CTL_U_7 (0x284) +#define SOC_DMA_CTL_U_DONE_BIT (12) +#define SOC_DMA_CTL_U_BLOCK_TS (0) +#define SOC_DMA_CTL_U_BLOCK_TS_LEN (12) + +#define SOC_DMA_CFG_L_0 (0x040) +#define SOC_DMA_CFG_L_1 (0x098) +#define SOC_DMA_CFG_L_2 (0x0F0) +#define SOC_DMA_CFG_L_3 (0x148) +#define SOC_DMA_CFG_L_4 (0x1A0) +#define SOC_DMA_CFG_L_5 (0x1F8) +#define SOC_DMA_CFG_L_6 (0x250) +#define SOC_DMA_CFG_L_7 (0x2A8) +#define SOC_DMA_CFG_L_RELOAD_DST (31) +#define SOC_DMA_CFG_L_RELOAD_SRC (30) +#define SOC_DMA_CFG_L_SRC_HS_POL (19) +#define SOC_DMA_CFG_L_DST_HS_POL (18) +#define SOC_DMA_CFG_L_HS_SEL_SRC (11) +#define SOC_DMA_CFG_L_HS_SEL_DST (10) +#define SOC_DMA_CFG_L_FIFO_EMPTY (9) +#define SOC_DMA_CFG_L_CH_SUSP (8) +#define SOC_DMA_CFG_L_CH_PRIOR (5) +#define SOC_DMA_CFG_L_CH_PRIOR_LEN (3) + +#define SOC_DMA_CFG_U_0 (0x044) +#define SOC_DMA_CFG_U_1 (0x09C) +#define SOC_DMA_CFG_U_2 (0x0F4) +#define SOC_DMA_CFG_U_3 (0x14C) +#define SOC_DMA_CFG_U_4 (0x1A4) +#define SOC_DMA_CFG_U_5 (0x1FC) +#define SOC_DMA_CFG_U_6 (0x254) +#define SOC_DMA_CFG_U_7 (0x2AC) +#define SOC_DMA_CFG_U_DEST_PER (11) +#define SOC_DMA_CFG_U_DEST_PER_LEN (4) +#define SOC_DMA_CFG_U_SRC_PER (7) +#define SOC_DMA_CFG_U_SRC_PER_LEN (4) +#define SOC_DMA_CFG_U_SS_UPD_EN (6) +#define SOC_DMA_CFG_U_DS_UPD_EN (5) +#define SOC_DMA_CFG_U_PROTCTL (2) +#define SOC_DMA_CFG_U_PROTCTL_LEN (3) +#define SOC_DMA_CFG_U_FIFO_MODE (1) +#define SOC_DMA_CFG_U_FCMODE (0) + +#define SOC_DMA_SGR_0 (0x048) +#define SOC_DMA_SGR_1 (0x0A0) +#define SOC_DMA_SGR_2 (0x0F8) +#define SOC_DMA_SGR_3 (0x150) +#define SOC_DMA_SGR_4 (0x1A8) +#define SOC_DMA_SGR_5 (0x200) +#define SOC_DMA_SGR_6 (0x258) +#define SOC_DMA_SGR_7 (0x2B0) +#define SOC_DMA_SGR_SGC (20) +#define SOC_DMA_SGR_SGC_LEN (5) +#define SOC_DMA_SGR_SGI (0) +#define SOC_DMA_SGR_SGI_LEN (20) + +#define SOC_DMA_DSR_0 (0x050) +#define SOC_DMA_DSR_1 (0x0A8) +#define SOC_DMA_DSR_2 (0x100) +#define SOC_DMA_DSR_3 (0x158) +#define SOC_DMA_DSR_4 (0x1B0) +#define SOC_DMA_DSR_5 (0x208) +#define SOC_DMA_DSR_6 (0x260) +#define SOC_DMA_DSR_7 (0x2B8) +#define SOC_DMA_DSR_DSC (20) +#define SOC_DMA_DSR_DSC_LEN (5) +#define SOC_DMA_DSR_DSI (0) +#define SOC_DMA_DSR_DSI_LEN (20) + +#define SOC_DMA_RAW_TFR (0x2C0) +#define SOC_DMA_RAW_BLOCK (0x2C8) +#define SOC_DMA_RAW_SRC_TRAN (0x2D0) +#define SOC_DMA_RAW_DST_TRAN (0x2D8) +#define SOC_DMA_RAW_ERR (0x2E0) +#define SOC_DMA_RAW_RAW (0) + +#define SOC_DMA_STATUS_TFR (0x2E8) +#define SOC_DMA_STATUS_BLOCK (0x2F0) +#define SOC_DMA_STATUS_SRC_TRAN (0x2F8) +#define SOC_DMA_STATUS_DST_TRAN (0x300) +#define SOC_DMA_STATUS_ERR (0x308) +#define SOC_DMA_STATUS_STATUS (0) + +#define SOC_DMA_MASK_TFR (0x310) +#define SOC_DMA_MASK_BLOCK (0x318) +#define SOC_DMA_MASK_SRC_TRAN (0x320) +#define SOC_DMA_MASK_DST_TRAN (0x328) +#define SOC_DMA_MASK_ERR (0x330) +#define SOC_DMA_MASK_INT_MASK_WE (8) +#define SOC_DMA_MASK_INT_MASK (0) + +#define SOC_DMA_CLEAR_TFR (0x338) +#define SOC_DMA_CLEAR_BLOCK (0x340) +#define SOC_DMA_CLEAR_SRC_TRAN (0x348) +#define SOC_DMA_CLEAR_DST_TRAN (0x350) +#define SOC_DMA_CLEAR_ERR (0x358) +#define SOC_DMA_CLEAR_CLEAR (0) + +#define SOC_DMA_DMA_CFG_REG (0x398) +#define SOC_DMA_DMA_CFG_REG_DMA_EN (0) + +#define SOC_DMA_CH_EN_REG (0x3A0) +#define SOC_DMA_CH_EN_REG_CH_EN_WE (8) +#define SOC_DMA_CH_EN_REG_CH_EN (0) + +// DMA Register map struct, used to refer to a channel's specific registers by base name and id number +struct dma_reg_map { + uint16_t SAR; + uint16_t DAR; + + uint16_t LLP; + + uint16_t CTL_L; + + uint16_t CTL_U; + + uint16_t CFG_L; + + uint16_t CFG_U; + + uint16_t SGR; + + uint16_t DSR; +}; + +struct dma_reg_map dma_regs[SOC_DMA_NUM_CHANNELS] = { + { + .SAR = SOC_DMA_SAR_0, + .DAR = SOC_DMA_DAR_0, + .LLP = SOC_DMA_LLP_0, + .CTL_L = SOC_DMA_CTL_L_0, + .CTL_U = SOC_DMA_CTL_U_0, + .CFG_L = SOC_DMA_CFG_L_0, + .CFG_U = SOC_DMA_CFG_U_0, + .SGR = SOC_DMA_SGR_0, + .DSR = SOC_DMA_DSR_0 + }, + { + .SAR = SOC_DMA_SAR_1, + .DAR = SOC_DMA_DAR_1, + .LLP = SOC_DMA_LLP_1, + .CTL_L = SOC_DMA_CTL_L_1, + .CTL_U = SOC_DMA_CTL_U_1, + .CFG_L = SOC_DMA_CFG_L_1, + .CFG_U = SOC_DMA_CFG_U_1, + .SGR = SOC_DMA_SGR_1, + .DSR = SOC_DMA_DSR_1 + }, + { + .SAR = SOC_DMA_SAR_2, + .DAR = SOC_DMA_DAR_2, + .LLP = SOC_DMA_LLP_2, + .CTL_L = SOC_DMA_CTL_L_2, + .CTL_U = SOC_DMA_CTL_U_2, + .CFG_L = SOC_DMA_CFG_L_2, + .CFG_U = SOC_DMA_CFG_U_2, + .SGR = SOC_DMA_SGR_2, + .DSR = SOC_DMA_DSR_2 + }, + { + .SAR = SOC_DMA_SAR_3, + .DAR = SOC_DMA_DAR_3, + .LLP = SOC_DMA_LLP_3, + .CTL_L = SOC_DMA_CTL_L_3, + .CTL_U = SOC_DMA_CTL_U_3, + .CFG_L = SOC_DMA_CFG_L_3, + .CFG_U = SOC_DMA_CFG_U_3, + .SGR = SOC_DMA_SGR_3, + .DSR = SOC_DMA_DSR_3 + }, + { + .SAR = SOC_DMA_SAR_4, + .DAR = SOC_DMA_DAR_4, + .LLP = SOC_DMA_LLP_4, + .CTL_L = SOC_DMA_CTL_L_4, + .CTL_U = SOC_DMA_CTL_U_4, + .CFG_L = SOC_DMA_CFG_L_4, + .CFG_U = SOC_DMA_CFG_U_4, + .SGR = SOC_DMA_SGR_4, + .DSR = SOC_DMA_DSR_4 + }, + { + .SAR = SOC_DMA_SAR_5, + .DAR = SOC_DMA_DAR_5, + .LLP = SOC_DMA_LLP_5, + .CTL_L = SOC_DMA_CTL_L_5, + .CTL_U = SOC_DMA_CTL_U_5, + .CFG_L = SOC_DMA_CFG_L_5, + .CFG_U = SOC_DMA_CFG_U_5, + .SGR = SOC_DMA_SGR_5, + .DSR = SOC_DMA_DSR_5 + }, + { + .SAR = SOC_DMA_SAR_6, + .DAR = SOC_DMA_DAR_6, + .LLP = SOC_DMA_LLP_6, + .CTL_L = SOC_DMA_CTL_L_6, + .CTL_U = SOC_DMA_CTL_U_6, + .CFG_L = SOC_DMA_CFG_L_6, + .CFG_U = SOC_DMA_CFG_U_6, + .SGR = SOC_DMA_SGR_6, + .DSR = SOC_DMA_DSR_6 + }, + { + .SAR = SOC_DMA_SAR_7, + .DAR = SOC_DMA_DAR_7, + .LLP = SOC_DMA_LLP_7, + .CTL_L = SOC_DMA_CTL_L_7, + .CTL_U = SOC_DMA_CTL_U_7, + .CFG_L = SOC_DMA_CFG_L_7, + .CFG_U = SOC_DMA_CFG_U_7, + .SGR = SOC_DMA_SGR_7, + .DSR = SOC_DMA_DSR_7 + } +}; + +// DMA Link List Item as specified by the DW DMAC doc; end_group is special field used by controller +struct dma_lli { + uint32_t sar; + uint32_t dar; + uint32_t llp; + uint32_t ctl_l; + uint32_t ctl_u; + uint32_t dstat; + uint32_t sstat; + uint8_t end_group; +}; + +#endif /* SOC_DMA_PRIV_H_ */ diff --git a/system/libarc32_arduino101/drivers/soc_i2s.c b/system/libarc32_arduino101/drivers/soc_i2s.c new file mode 100644 index 00000000..70b51429 --- /dev/null +++ b/system/libarc32_arduino101/drivers/soc_i2s.c @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2016, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "scss_registers.h" +#include "portable.h" +#include "os/os.h" +#include "i2s_priv.h" +#include "data_type.h" +#include "clk_system.h" +#include "soc_i2s.h" + +/* Info struct */ +struct soc_i2s_info g_i2s_info = { + .reg_base = SOC_I2S_BASE, + .int_vector = SOC_I2S_INTERRUPT, + .int_mask = INT_I2S_MASK, + .clk_speed = 32000000, + .clk_gate_info = &(struct clk_gate_info_s) + { + .clk_gate_register = PERIPH_CLK_GATE_CTRL, + .bits_mask = I2S_CLK_GATE_MASK, + } +}; + +struct soc_i2s_info* i2s_info = &g_i2s_info; + +/* Function Prototypes */ +static void i2s_enable(uint8_t channel); +static void i2s_disable(uint8_t channel); +static void i2s_isr(void); +DRIVER_API_RC soc_i2s_config(uint8_t channel, struct soc_i2s_cfg *cfg); +DRIVER_API_RC soc_i2s_deconfig(uint8_t channel); +DRIVER_API_RC soc_i2s_read(uint32_t *buf, uint32_t len); +DRIVER_API_RC soc_i2s_listen(uint32_t *buf, uint32_t len, uint8_t num_bufs); +DRIVER_API_RC soc_i2s_stop_listen(void); +DRIVER_API_RC soc_i2s_write(uint32_t *buf, uint32_t len); +DRIVER_API_RC soc_i2s_stream(uint32_t *buf, uint32_t len, uint32_t num_bufs); +DRIVER_API_RC soc_i2s_stop_stream(void); +DRIVER_API_RC soc_i2s_init(); + +/* Internal functions */ +static void i2s_enable(uint8_t channel) +{ + uint32_t reg; + + // Enable local clock and interrupts + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].cid_ctrl); + reg &= ~(1 << (i2s_reg_map[channel].cid_ctrl_strobe)); + reg &= ~(1 << (i2s_reg_map[channel].cid_ctrl_strobe_sync)); + reg |= (1 << (i2s_reg_map[channel].cid_ctrl_mask)); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].cid_ctrl) = reg; + + // Clear all interrupts + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].stat); + reg &= ~(i2s_reg_map[channel].stat_mask); + reg |= (1 << SOC_I2S_STAT_TDATA_UNDERR); + reg |= (1 << SOC_I2S_STAT_RDATA_OVRERR); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].stat) = reg; + + // Set enabled flag for channel + i2s_info->en[channel] = 1; + + return; +} + +static void i2s_disable(uint8_t channel) +{ + uint32_t reg; + uint32_t num_active; + int i; + + // Release DMA resources + if (i2s_info->en[channel]) + { + soc_dma_stop_transfer(&(i2s_info->dma_ch[channel])); + soc_dma_release(&(i2s_info->dma_ch[channel])); + soc_dma_free_list(&(i2s_info->dma_cfg[channel])); + } + // Clear enabled flag for channel + i2s_info->en[channel] = 0; + + // Let the processor do whatever power down it wants + num_active = 0; + for (i = 0; i < I2S_NUM_CHANNELS; i++) + { + if (i2s_info->en[i]) + { + num_active++; + } + } + + // Disable channel and hold parts in reset + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl); + reg &= ~(1 << (i2s_reg_map[channel].ctrl_fifo_rst)); + reg &= ~(1 << (i2s_reg_map[channel].ctrl_sync_rst)); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl) = reg; + + // Clear all interrupts + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].stat); + reg &= ~(i2s_reg_map[channel].stat_mask); + reg &= ~(1 << i2s_reg_map[channel].stat_err); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].stat) = reg; + + // Disable local clock and interrupts + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].cid_ctrl); + reg |= (1 << (i2s_reg_map[channel].cid_ctrl_strobe)); + reg |= (1 << (i2s_reg_map[channel].cid_ctrl_strobe_sync)); + reg &= ~(1 << (i2s_reg_map[channel].cid_ctrl_mask)); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].cid_ctrl) = reg; + + return; + +} + +/* ISR */ +static void i2s_isr(void) +{ + uint32_t stat; + + // Determine interrupt source + stat = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_STAT); + + // Check for errors + if (stat & (1 << SOC_I2S_STAT_TDATA_UNDERR)) + { + if (i2s_info->cfg[I2S_CHANNEL_TX].cb_err) + { + i2s_info->cfg[I2S_CHANNEL_TX].cb_err_arg=(void *)stat; + i2s_info->cfg[I2S_CHANNEL_TX].cb_err(i2s_info->cfg[I2S_CHANNEL_TX].cb_err_arg); + } + i2s_disable(I2S_CHANNEL_TX); + } + if (stat & (1 << SOC_I2S_STAT_RDATA_OVRERR)) + { + if (i2s_info->cfg[I2S_CHANNEL_RX].cb_err) + { + i2s_info->cfg[I2S_CHANNEL_RX].cb_err_arg=(void *)stat; + i2s_info->cfg[I2S_CHANNEL_RX].cb_err(i2s_info->cfg[I2S_CHANNEL_RX].cb_err_arg); + } + i2s_disable(I2S_CHANNEL_RX); + } + + return; +} + +DECLARE_INTERRUPT_HANDLER static void i2s_interrupt_handler() +{ + i2s_isr(); +} + +/* DMA Callbacks */ +static void i2s_dma_cb_err(void *num) +{ + uint8_t channel = (uint32_t)num; + + if (i2s_info->cfg[channel].cb_err) + { + i2s_info->cfg[channel].cb_err(i2s_info->cfg[channel].cb_err_arg); + } + i2s_disable(channel); + + return; +} + +static void i2s_dma_cb_done(void *num) +{ + uint8_t channel = (uint32_t)num; + uint32_t reg; + + if(channel == I2S_CHANNEL_TX) + { + if((0x00200000 & MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CTRL)) + && !(0x18000000 & MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CTRL))) + { + for(int i = 0; i < 4; ++i) + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_DATA_REG) = 0x0; + } + + do + { + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].fifo_stat); + } while(reg & 0x000000FF); + } + + if (i2s_info->cfg[channel].cb_done) + { + i2s_info->cfg[channel].cb_done(i2s_info->cfg[channel].cb_done_arg); + } + + i2s_disable(channel); + + return; +} + +static void i2s_dma_cb_block(void *num) +{ + uint8_t channel = (uint32_t)num; + + if (i2s_info->cfg[channel].cb_done) + { + i2s_info->cfg[channel].cb_done(i2s_info->cfg[channel].cb_done_arg); + } + + return; +} + +/* External API */ +DRIVER_API_RC soc_i2s_config(uint8_t channel, struct soc_i2s_cfg *cfg) +{ + uint32_t reg; + uint16_t sample_rate; + + // Check channel no in use + if (channel >= I2S_NUM_CHANNELS) + { + return DRV_RC_FAIL; + } + else if (i2s_info->en[channel]) + { + return DRV_RC_CONTROLLER_IN_USE; + } + + // Set master/slave + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl); + reg &= ~(1 << (i2s_reg_map[channel].ctrl_ms)); + reg |= (cfg->master & 0x1) << i2s_reg_map[channel].ctrl_ms; + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl) = reg; + + // Calculate sample_rate divider (note, acts as if resolution is always 32) + sample_rate = i2s_info->clk_speed / (cfg->sample_rate * cfg->resolution * 2); + + // Setup resolution and sampling rate + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].srr); + reg &= ~(i2s_reg_map[channel].srr_mask); + reg |= (sample_rate & 0x7FF) << i2s_reg_map[channel].srr_sample_rate; + reg |= ((cfg->resolution - 1) & 0x1F) << i2s_reg_map[channel].srr_resolution; + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].srr) = reg; + + // Setup mode + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].dev_conf); + reg &= ~(i2s_reg_map[channel].dev_conf_mask); + // Use sck_polar as shift amount as its the LSb of the DEV_CONF settings + reg |= ((cfg->mode & 0x3F) << i2s_reg_map[channel].dev_conf_sck_polar); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].dev_conf) = reg; + + // Complete configuration (and set flag) + i2s_info->cfg[channel] = *cfg; + i2s_info->cfgd[channel] = 1; + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_i2s_deconfig(uint8_t channel) +{ + // Check channel no in use + if (channel >= I2S_NUM_CHANNELS) + { + return DRV_RC_FAIL; + } + else if (i2s_info->en[channel]) + { + return DRV_RC_CONTROLLER_IN_USE; + } + + i2s_info->cfgd[channel] = 0; + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_i2s_read(uint32_t *buf, uint32_t len) +{ + // Calling listen with 0 buffers is the same as a onetime read of the whole buffer + return soc_i2s_listen(buf, len, 0); +} + +DRIVER_API_RC soc_i2s_listen(uint32_t *buf, uint32_t len, uint8_t num_bufs) +{ + DRIVER_API_RC ret; + uint8_t channel = I2S_CHANNEL_RX; + uint32_t reg; + uint32_t len_per_buf; + int i; + struct soc_dma_xfer_item *dma_list; + + // Check channel no in use and configured + if (channel >= I2S_NUM_CHANNELS) + { + return DRV_RC_FAIL; + } + else if (i2s_info->en[channel] || !(i2s_info->cfgd[channel])) + { + return DRV_RC_FAIL; + } + + // Get a DMA channel + ret = soc_dma_acquire(&(i2s_info->dma_ch[channel])); + + if (ret != DRV_RC_OK) + { + return DRV_RC_FAIL; + } + + // Enable the channel + i2s_enable(channel); + + // Determine the length of a single buffer + if (num_bufs == 0) + { + len_per_buf = len; + } + else + { + len_per_buf = len / num_bufs; + } + + // Prep some configuration + i2s_info->dma_cfg[channel].type = SOC_DMA_TYPE_PER2MEM; + i2s_info->dma_cfg[channel].src_interface = SOC_DMA_INTERFACE_I2S_RX; + i2s_info->dma_cfg[channel].dest_step_count = 0; + i2s_info->dma_cfg[channel].src_step_count = 0; + + i2s_info->dma_cfg[channel].xfer.dest.delta = SOC_DMA_DELTA_INCR; + i2s_info->dma_cfg[channel].xfer.dest.width = SOC_DMA_WIDTH_32; + i2s_info->dma_cfg[channel].xfer.src.delta = SOC_DMA_DELTA_NONE; + i2s_info->dma_cfg[channel].xfer.src.width = SOC_DMA_WIDTH_32; + i2s_info->dma_cfg[channel].xfer.src.addr = (void *)(SOC_I2S_BASE + SOC_I2S_DATA_REG); + + if (num_bufs == 0) + { + i2s_info->dma_cfg[channel].cb_done = i2s_dma_cb_done; + i2s_info->dma_cfg[channel].cb_done_arg = (void *)((uint32_t)channel); + } + else + { + i2s_info->dma_cfg[channel].cb_block = i2s_dma_cb_block; + i2s_info->dma_cfg[channel].cb_block_arg = (void *)((uint32_t)channel); + } + + i2s_info->dma_cfg[channel].cb_err = i2s_dma_cb_err; + i2s_info->dma_cfg[channel].cb_err_arg = (void *)((uint32_t)channel); + + // Setup the linked list + for (i = 0; i < ((num_bufs == 0) ? 1 : num_bufs); i++) + { + if (i == 0) + { + dma_list = &(i2s_info->dma_cfg[channel].xfer); + } + else + { + ret = soc_dma_alloc_list_item(&dma_list, dma_list); + + if (ret != DRV_RC_OK) + { + goto fail; + } + } + + dma_list->dest.addr = (void *)(&(buf[i * (len_per_buf / sizeof(uint32_t))])); + dma_list->size = len_per_buf / sizeof(uint32_t); + } + + // Create a circular list if we are doing circular buffering + if (num_bufs != 0) + { + dma_list->next = &(i2s_info->dma_cfg[channel].xfer); + } + + // Setup and start the DMA engine + ret = soc_dma_config(&(i2s_info->dma_ch[channel]), &(i2s_info->dma_cfg[channel])); + + if (ret != DRV_RC_OK) + { + goto fail; + } + + ret = soc_dma_start_transfer(&(i2s_info->dma_ch[channel])); + + if (ret != DRV_RC_OK) + { + goto fail; + } + + // Enable the channel and let it go! + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl); + reg |= (1 << (i2s_reg_map[channel].ctrl_en)); + reg |= (1 << (i2s_reg_map[channel].ctrl_sync_rst)); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl) = reg; + + return DRV_RC_OK; + +fail: + i2s_disable(channel); + soc_dma_release(&(i2s_info->dma_ch[channel])); + return DRV_RC_FAIL; +} + +DRIVER_API_RC soc_i2s_stop_listen(void) +{ + uint8_t channel = I2S_CHANNEL_RX; + uint32_t save; + + if (channel >= I2S_NUM_CHANNELS) + { + return DRV_RC_FAIL; + } + else if (!(i2s_info->en[channel])) + { + return DRV_RC_FAIL; + } + + save = interrupt_lock(); + i2s_disable(channel); + interrupt_unlock(save); + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_i2s_write(uint32_t *buf, uint32_t len) +{ + // Calling stream with 0 buffers is the same as a onetime write of the whole buffer + return soc_i2s_stream(buf, len, 0); +} + +DRIVER_API_RC soc_i2s_stream(uint32_t *buf, uint32_t len, uint32_t num_bufs) +{ + DRIVER_API_RC ret; + uint8_t channel = I2S_CHANNEL_TX; + uint32_t reg; + uint32_t len_per_buf; + int i; + struct soc_dma_xfer_item *dma_list; + + // Check channel no in use and configured + if (channel >= I2S_NUM_CHANNELS) + { + return DRV_RC_FAIL; + } + else if (i2s_info->en[channel] || !(i2s_info->cfgd[channel])) + { + return DRV_RC_FAIL; + } + + // Get a DMA channel + ret = soc_dma_acquire(&(i2s_info->dma_ch[channel])); + + if (ret != DRV_RC_OK) + { + return DRV_RC_FAIL; + } + + // Enable the channel + i2s_enable(channel); + + // Determine the length of a single buffer + if (num_bufs == 0) + { + len_per_buf = len; + } + else + { + len_per_buf = len / num_bufs; + } + + // Prep some configuration + i2s_info->dma_cfg[channel].type = SOC_DMA_TYPE_MEM2PER; + i2s_info->dma_cfg[channel].dest_interface = SOC_DMA_INTERFACE_I2S_TX; + i2s_info->dma_cfg[channel].dest_step_count = 0; + i2s_info->dma_cfg[channel].src_step_count = 0; + + i2s_info->dma_cfg[channel].xfer.dest.delta = SOC_DMA_DELTA_NONE; + i2s_info->dma_cfg[channel].xfer.dest.width = SOC_DMA_WIDTH_32; + i2s_info->dma_cfg[channel].xfer.dest.addr = (void *)(SOC_I2S_BASE + SOC_I2S_DATA_REG); + i2s_info->dma_cfg[channel].xfer.src.delta = SOC_DMA_DELTA_INCR; + i2s_info->dma_cfg[channel].xfer.src.width = SOC_DMA_WIDTH_32; + + if (num_bufs == 0) + { + i2s_info->dma_cfg[channel].cb_done = i2s_dma_cb_done; + i2s_info->dma_cfg[channel].cb_done_arg = (void *)((uint32_t)channel); + } + else + { + i2s_info->dma_cfg[channel].cb_block = i2s_dma_cb_block; + i2s_info->dma_cfg[channel].cb_block_arg = (void *)((uint32_t)channel); + } + + i2s_info->dma_cfg[channel].cb_err = i2s_dma_cb_err; + i2s_info->dma_cfg[channel].cb_err_arg = (void *)((uint32_t)channel); + + // Setup the linked list + for (i = 0; i < ((num_bufs == 0) ? 1 : num_bufs); i++) + { + if (i == 0) + { + dma_list = &(i2s_info->dma_cfg[channel].xfer); + } + else + { + ret = soc_dma_alloc_list_item(&dma_list, dma_list); + + if (ret != DRV_RC_OK) + { + goto fail; + } + } + + dma_list->src.addr = (void *)(&(buf[i * (len_per_buf / sizeof(uint32_t))])); + dma_list->size = len_per_buf / sizeof(uint32_t); + } + + // Create a circular list if we are doing circular buffering + if (num_bufs != 0) + { + dma_list->next = &(i2s_info->dma_cfg[channel].xfer); + } + + // Setup and start the DMA engine + ret = soc_dma_config(&(i2s_info->dma_ch[channel]), &(i2s_info->dma_cfg[channel])); + + if (ret != DRV_RC_OK) + { + goto fail; + } + + ret = soc_dma_start_transfer(&(i2s_info->dma_ch[channel])); + + if (ret != DRV_RC_OK) + { + goto fail; + } + + // Enable the channel and let it go! + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl); + reg |= (1 << (i2s_reg_map[channel].ctrl_en)); + reg |= (1 << (i2s_reg_map[channel].ctrl_sync_rst)); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl) = reg; + + return DRV_RC_OK; + +fail: + i2s_disable(channel); + soc_dma_release(&(i2s_info->dma_ch[channel])); + return DRV_RC_FAIL; +} + + +DRIVER_API_RC soc_i2s_stop_stream(void) +{ + uint8_t channel = I2S_CHANNEL_TX; + uint32_t save; + + if (channel >= I2S_NUM_CHANNELS) + { + return DRV_RC_FAIL; + } + else if (!(i2s_info->en[channel])) + { + return DRV_RC_FAIL; + } + + save = interrupt_lock(); + i2s_disable(channel); + interrupt_unlock(save); + + return DRV_RC_OK; +} + +/* Driver API */ +DRIVER_API_RC soc_i2s_init() +{ + int i; + uint32_t reg; + + // Prep info struct + for (i = 0; i < I2S_NUM_CHANNELS; i++) + { + i2s_info->en[i] = 0; + i2s_info->cfgd[i] = 0; + i2s_info->cfg[i].cb_done = NULL; + i2s_info->cfg[i].cb_err = NULL; + } + + // Enable global clock, use local clock gating per channel instead + set_clock_gate(i2s_info->clk_gate_info, CLK_GATE_ON); + + // Setup ISR (and enable) + SET_INTERRUPT_HANDLER(i2s_info->int_vector, i2s_interrupt_handler); + SOC_UNMASK_INTERRUPTS(i2s_info->int_mask); + + // Set up control register + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CTRL); + reg |= (1 << SOC_I2S_CTRL_TR_CFG_0); + reg &= ~(1 << SOC_I2S_CTRL_TSYNC_LOOP_BACK); + reg &= ~(1 << SOC_I2S_CTRL_RSYNC_LOOP_BACK); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CTRL) = reg; + + // Set the watermark levels + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_TFIFO_CTRL) &= 0xFFFCFFFF; + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_TFIFO_CTRL) |= (I2S_TFIFO_THR << SOC_I2S_TFIFO_CTRL_TAFULL_THRS); + + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_RFIFO_CTRL) &= 0xFFFCFFFF; + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_RFIFO_CTRL) |= (I2S_RFIFO_THR << SOC_I2S_RFIFO_CTRL_RAFULL_THRS); + + // Enable global interrupt mask + reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CID_CTRL); + reg |= (1 << SOC_I2S_CID_CTRL_INTREQ_MASK); + MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CID_CTRL) = reg; + + // Initially, have all channels disabled + for (i = 0; i < I2S_NUM_CHANNELS; i++) + { + i2s_disable(i); + } + + return DRV_RC_OK; +} + +DRIVER_API_RC soc_i2s_reset(uint8_t channel) +{ + i2s_info->en[channel] = 0; + i2s_info->cfgd[channel] = 0; + i2s_info->cfg[channel].cb_done = NULL; + i2s_info->cfg[channel].cb_err = NULL; + + i2s_info->cfg[channel].cb_done_arg = NULL; + i2s_info->cfg[channel].cb_err_arg = NULL; + + + i2s_info->dma_ch[channel].active = 0; + i2s_info->dma_ch[channel].id = 0; + i2s_info->dma_ch[channel].ll = NULL; + i2s_info->dma_ch[channel].curr = NULL; + + i2s_info->dma_cfg[channel].cb_done_arg = NULL; + i2s_info->dma_cfg[channel].cb_done = NULL; + i2s_info->dma_cfg[channel].cb_block_arg= NULL; + i2s_info->dma_cfg[channel].cb_block = NULL; + i2s_info->dma_cfg[channel].cb_err_arg = NULL; + i2s_info->dma_cfg[channel].cb_err = NULL; + + soc_i2s_config(channel, &(i2s_info->cfg[channel])); + + return DRV_RC_OK; +} diff --git a/system/libarc32_arduino101/drivers/soc_i2s.h b/system/libarc32_arduino101/drivers/soc_i2s.h new file mode 100644 index 00000000..5a14f5f9 --- /dev/null +++ b/system/libarc32_arduino101/drivers/soc_i2s.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2016, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SOC_I2s_H_ +#define SOC_I2s_H_ + +#include "data_type.h" +#include "soc_dma.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @defgroup common_drivers Common Drivers + * Definition of the drivers APIs accessible from any processor. + * @ingroup drivers + */ + +/** + * @defgroup soc_i2s Quark SE SOC I2S + * Quark SE SOC I2S (Audio) drivers API. + * @ingroup common_drivers + * @{ + */ + +// I2S channels +#define I2S_CHANNEL_TX 0 +#define I2S_CHANNEL_RX 1 +#define I2S_NUM_CHANNELS 2 + +// I2S modes +#define I2S_MODE_SCK_POL (0x01) +#define I2S_MODE_WS_POL (0x02) +#define I2S_MODE_LR_ALIGN (0x08) +#define I2S_MODE_SAMPLE_DEL (0x10) +#define I2S_MODE_WS_DSP (0x20) + +#define I2S_MODE_PHILLIPS (I2S_MODE_SCK_POL | I2S_MODE_LR_ALIGN) +#define I2S_MODE_RJ (I2S_MODE_SCK_POL | I2S_MODE_WS_POL | \ + I2S_MODE_SAMPLE_DEL) +#define I2S_MODE_LJ (I2S_MODE_SCK_POL | I2S_MODE_WS_POL | \ + I2S_MODE_LR_ALIGN | I2S_MODE_SAMPLE_DEL) +#define I2S_MODE_DSP (I2S_MODE_SCK_POL | I2S_MODE_LR_ALIGN | \ + I2S_MODE_WS_DSP) + +// I2S configuration object +struct soc_i2s_cfg { + uint16_t sample_rate; // in Hz + uint8_t resolution; + uint8_t mode; + uint8_t master; + + void (*cb_done)(void *); + void *cb_done_arg; + + void (*cb_err)(void *); + void *cb_err_arg; +}; + +// Internal struct for use by the controller (and soc_config) +struct soc_i2s_info { + uint32_t reg_base; + uint32_t int_vector; + uint32_t int_mask; + + uint32_t clk_speed; + + struct soc_i2s_cfg cfg[I2S_NUM_CHANNELS]; + uint8_t en[I2S_NUM_CHANNELS]; + uint8_t cfgd[I2S_NUM_CHANNELS]; + + struct soc_dma_cfg dma_cfg[I2S_NUM_CHANNELS]; + struct soc_dma_channel dma_ch[I2S_NUM_CHANNELS]; + + struct clk_gate_info_s *clk_gate_info; +}; + +extern struct driver soc_i2s_driver; + +/** + * Function to configure specified I2S channel + * + * Configuration parameters must be valid or an error is returned - see return values below. + * + * @param channel : I2S channel number + * @param cfg : pointer to configuration structure + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_INVALID_CONFIG - if any configuration parameters are not valid + * - DRV_RC_CONTROLLER_IN_USE, - if controller is in use + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_i2s_config(uint8_t channel, struct soc_i2s_cfg *cfg); + +/** + * Function to deconfigure specified I2S channel + * + * @param channel : I2S channel number + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_CONTROLLER_IN_USE, - if controller is in use + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_i2s_deconfig(uint8_t channel); + +/** + * Function to transmit a block of audio data + * + * @param buf : pointer to data to write + * @param len : length of data to write (in bytes) + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_i2s_write(uint32_t *buf, uint32_t len); + +/** + * Function to continuously transmit blocks of audio data + * + * @param buf : pointer to buffer to read data from + * @param len : length of buffer (in bytes) + * @param num_bufs : number of parts into which to split the buffer; calling the callback + * after each is sent + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_i2s_stream(uint32_t *buf, uint32_t len, uint32_t num_bufs); + +/** + * Function to stop a continuous audio data write + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_i2s_stop_stream(void); + +/** + * Function to receive a block of audio data + * + * @param buf : pointer to buffer to fill with data + * @param len : length of buffer (in bytes) + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_i2s_read(uint32_t *buf, uint32_t len); + +/** + * Function to continuously receive blocks of audio data + * + * @param buf : pointer to buffer to fill with data + * @param len : length of buffer (in bytes) + * @param num_bufs : number of parts into which to split the buffer; calling the callback + * after each is filled + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_i2s_listen(uint32_t *buf, uint32_t len, uint8_t num_bufs); + +/** + * Function to stop a continuous audio data read + * + * @return + * - DRV_RC_OK on success + * - DRV_RC_FAIL otherwise + */ +DRIVER_API_RC soc_i2s_stop_listen(void); + +DRIVER_API_RC soc_i2s_init(); + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index c7e60d8ae65766a0241c1e77c6dc01dd110b940b..705a4986ce2e5ebf496ccc6a8b5386fb18d5b0ea 100644 GIT binary patch delta 95193 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 G^Zp+++HOz) delta 51482 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