diff --git a/bsp/fh8620/SConscript b/bsp/fh8620/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..fe0ae941ae9a759ae478de901caec1c961e56af8 --- /dev/null +++ b/bsp/fh8620/SConscript @@ -0,0 +1,14 @@ +# for module compiling +import os +Import('RTT_ROOT') + +cwd = str(Dir('#')) +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/fh8620/SConstruct b/bsp/fh8620/SConstruct new file mode 100644 index 0000000000000000000000000000000000000000..7f9e572807b801903c178572b3cbf510e8e255a6 --- /dev/null +++ b/bsp/fh8620/SConstruct @@ -0,0 +1,33 @@ +import os +import sys +import rtconfig + +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = os.path.normpath(os.getcwd() + '/../..') + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +from building import * + +TARGET = rtconfig.OUTPUT_NAME + rtconfig.TARGET_EXT + +# add rtconfig.h path to the assembler +rtconfig.AFLAGS += ' -I' + str(Dir('#')) +' -I' + RTT_ROOT + '/libcpu/arm/armv6' + +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CCFLAGS = rtconfig.CFLAGS, + CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, + AR = rtconfig.AR, ARFLAGS = '-rc', + LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) + +Export('RTT_ROOT') +Export('rtconfig') + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT) + +# make a building +DoBuilding(TARGET, objs) diff --git a/bsp/fh8620/applications/SConscript b/bsp/fh8620/applications/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..85d211cf16d94cd5f2d9e1c019174fc19df12f38 --- /dev/null +++ b/bsp/fh8620/applications/SConscript @@ -0,0 +1,11 @@ +import rtconfig +Import('RTT_ROOT') +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + +CPPPATH = [cwd, str(Dir('#'))] +group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/fh8620/applications/main.c b/bsp/fh8620/applications/main.c new file mode 100644 index 0000000000000000000000000000000000000000..dd6172051ba69a16550d640584c00eb461853d8a --- /dev/null +++ b/bsp/fh8620/applications/main.c @@ -0,0 +1,45 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ +#include +#include + +void init_thread(void *parameter) +{ + rt_components_init(); + + return ; +} + +int rt_application_init(void) +{ + rt_thread_t tid; + + tid = rt_thread_create("init", init_thread, RT_NULL, + 4096, RT_THREAD_PRIORITY_MAX/3, 20); + if (tid) rt_thread_startup(tid); + + return 0; +} diff --git a/bsp/fh8620/drivers/SConscript b/bsp/fh8620/drivers/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..e9f77159672556505a94a7cd02504b68e400edc1 --- /dev/null +++ b/bsp/fh8620/drivers/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('drivers', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/fh8620/drivers/acw.c b/bsp/fh8620/drivers/acw.c new file mode 100644 index 0000000000000000000000000000000000000000..1d73f1fecae4805acf4a1dce7c41b1c02b0c9ee1 --- /dev/null +++ b/bsp/fh8620/drivers/acw.c @@ -0,0 +1,1386 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "acw.h" +#include "fh_def.h" +#include "fh_dma.h" +#include "dma.h" +#ifdef RT_USING_FH_ACW +#if 1 +typedef struct +{ + unsigned int base; + void *vbase; + unsigned int size; + unsigned int align; +}MEM_DESC; +#define ACW_SELFTEST 0 +int buffer_malloc_withname(MEM_DESC *mem, int size, int align, char* name); +#endif + +#define NR_DESCS_PER_CHANNEL 64 + +#define FIX_SAMPLE_BIT 32 + +#define ACW_HW_NUM_RX 0 +#define ACW_HW_NUM_TX 1 +#define ACW_DMA_CAP_CHANNEL 3 +#define ACW_DMA_PAY_CHANNEL 2 + +#define ACW_CTRL 0x0 +#define ACW_TXFIFO_CTRL 0x4 +#define ACW_RXFIFO_CTRL 0x8 +#define ACW_STATUS 0x0c +#define ACW_DIG_IF_CTRL 0x10 +#define ACW_ADC_PATH_CTRL 0x14 +#define ACW_ADC_ALC_CTRL 0x18 +#define ACW_DAC_PATH_CTRL 0x1c +#define ACW_MISC_CTRL 0x20 +#define ACW_TXFIFO 0xf0a00100 +#define ACW_RXFIFO 0xf0a00200 +typedef char bool; +#define AUDIO_DMA_PREALLOC_SIZE 128*1024 + +#define ACW_INTR_RX_UNDERFLOW 0x10000 +#define ACW_INTR_RX_OVERFLOW 0x20000 +#define ACW_INTR_TX_UNDERFLOW 0x40000 +#define ACW_INTR_TX_OVERFLOW 0x80000 +#define PAGE_SIZE 0x1000 + +enum audio_type +{ + capture = 0, + playback, +}; + +enum audio_state +{ + normal = 0, + xrun, + stopping, + running, +}; + +struct infor_record_t +{ + int record_pid; + int play_pid; +}; // infor_record; + +struct audio_config_t { + int rate; + int volume; + enum io_select io_type; + int frame_bit; + int channels; + int buffer_size; + int period_size; + int buffer_bytes; + int period_bytes; + int start_threshold; + int stop_threshold; +}; + +struct audio_ptr_t +{ + struct audio_config_t cfg; + enum audio_state state; + long size; + int hw_ptr; + int appl_ptr; + struct rt_mutex lock; + struct device_acw dev; + void *area; /*virtual pointer*/ + dma_addr_t addr; /*physical address*/ + unsigned char * mmap_addr; +}; + +struct fh_audio_cfg +{ + struct rt_dma_device *capture_dma; + struct rt_dma_device *playback_dma; + struct dma_transfer *capture_trans; + struct dma_transfer *plauback_trans; + struct audio_ptr_t capture; + struct audio_ptr_t playback; + wait_queue_head_t readqueue; + wait_queue_head_t writequeue; + struct rt_semaphore sem_capture; + struct rt_semaphore sem_playback; +}; +typedef int s32; +typedef s32 dma_cookie_t; +struct fh_dma_chan +{ + struct dma_chan *chan; + void *ch_regs; + unsigned char mask; + unsigned char priority; + bool paused; + bool initialized; + struct rt_mutex lock; + /* these other elements are all protected by lock */ + unsigned long flags; + dma_cookie_t completed; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + struct fh_cyclic_desc *cdesc; + unsigned int descs_allocated; +}; + +struct fh_acw_dma_transfer +{ + struct dma_chan *chan; + struct dma_transfer cfg; + struct scatterlist sgl; + struct dma_async_tx_descriptor *desc; +}; + +struct channel_assign +{ + int capture_channel; + int playback_channel; +}; + +struct audio_dev_mod +{ + int reg_base; + struct channel_assign channel_assign; + struct fh_audio_cfg *audio_config; + +}audio_dev; +static struct work_struct playback_wq; +#define WORK_QUEUE_STACK_SIZE 512 +#if ACW_SELFTEST +#define WORK_QUEUE_PRIORITY (12) +#else +#define WORK_QUEUE_PRIORITY (128+12) +#endif +static struct rt_workqueue* playback_queue; + + +void audio_prealloc_dma_buffer(int aiaotype,struct fh_audio_cfg *audio_config); +#if ACW_SELFTEST +#define BUFF_SIZE 1024*8 +#define TEST_PER_NO 1024 +#endif +static void audio_callback(){ + rt_kprintf("# \n"); +} + +static void audio_callback_capture(){ + rt_kprintf("$ \n"); +} + + +static struct audio_param_store_t +{ + int input_volume; + enum io_select input_io_type; +} audio_param_store; +void reset_dma_buff(enum audio_type type, struct fh_audio_cfg *audio_config); +static void fh_acw_tx_dma_done(void *arg); +static void fh_acw_rx_dma_done(struct fh_audio_cfg *arg); +static bool fh_acw_dma_chan_filter(struct dma_chan *chan, void *filter_param); +#define writel(v,a) SET_REG(a,v) +void fh_acw_stop_playback(struct fh_audio_cfg *audio_config) +{ + if(audio_config->playback.state == stopping) + { + return; + } + + unsigned int rx_status; + rx_status = readl( audio_dev.reg_base + ACW_TXFIFO_CTRL);//clear rx fifo + rx_status = rx_status|(1<<4); + writel(rx_status,audio_dev.reg_base + ACW_TXFIFO_CTRL); + + audio_config->playback.state = stopping; + writel(0, audio_dev.reg_base + ACW_TXFIFO_CTRL);//tx fifo disable + if(audio_config->plauback_trans->channel_number != ACW_PLY_DMA_CHAN) + goto free_mem; + if(!audio_config->plauback_trans->first_lli) + goto free_channel; + audio_config->playback_dma->ops->control(audio_config->playback_dma,RT_DEVICE_CTRL_DMA_CYCLIC_STOP,audio_config->plauback_trans); + audio_config->playback_dma->ops->control(audio_config->playback_dma,RT_DEVICE_CTRL_DMA_CYCLIC_FREE,audio_config->plauback_trans); +free_channel: + audio_config->playback_dma->ops->control(audio_config->playback_dma,RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL,audio_config->plauback_trans); + if(&audio_config->sem_playback) + rt_sem_release(&audio_config->sem_playback); + if(&audio_config->playback.lock) + rt_mutex_release(&audio_config->playback.lock); + if(&playback_wq) + rt_workqueue_cancel_work(playback_queue,&playback_wq); + if(playback_queue) + rt_workqueue_destroy(playback_queue); +free_mem: + if(audio_config->playback.area) + fh_dma_mem_free(audio_config->playback.area); + +} + +void fh_acw_stop_capture(struct fh_audio_cfg *audio_config) +{ + unsigned int rx_status; + + if(audio_config->capture.state == stopping) + { + rt_kprintf(" capture is stopped \n"); + return; + } + rx_status = readl( audio_dev.reg_base + ACW_RXFIFO_CTRL);//clear rx fifo + rx_status = rx_status|(1<<4); + writel(rx_status,audio_dev.reg_base + ACW_RXFIFO_CTRL); + audio_config->capture.state = stopping; + + writel(0, audio_dev.reg_base + 8);//rx fifo disable + if(audio_config->capture_trans->channel_number != ACW_CAP_DMA_CHAN) + goto free_mem; + if(!audio_config->capture_trans->first_lli) + goto free_channel; + audio_config->capture_dma->ops->control(audio_config->capture_dma,RT_DEVICE_CTRL_DMA_CYCLIC_STOP,audio_config->capture_trans); + + audio_config->capture_dma->ops->control(audio_config->capture_dma,RT_DEVICE_CTRL_DMA_CYCLIC_FREE,audio_config->capture_trans); +free_channel: + audio_config->capture_dma->ops->control(audio_config->capture_dma,RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL,audio_config->capture_trans); + if(&audio_config->sem_capture) + rt_sem_release(&audio_config->sem_capture); + if(&audio_config->capture.lock) + rt_mutex_release(&audio_config->capture.lock); +free_mem: + if(audio_config->capture.area) + fh_dma_mem_free( audio_config->capture.area); +} + +void switch_io_type(enum audio_type type, enum io_select io_type) +{ + int reg; + if (capture == type) + { + reg = readl(audio_dev.reg_base + ACW_ADC_PATH_CTRL); + if (mic_in == io_type) + { + rt_kprintf("audio input changed to mic_in\n"); + writel( reg & (~(1<<1)),audio_dev.reg_base + ACW_ADC_PATH_CTRL); + reg = readl(audio_dev.reg_base + ACW_ADC_PATH_CTRL); + reg = reg & (~(1<<3)); + reg |=(0x1<<3); + writel(reg, audio_dev.reg_base + ACW_ADC_PATH_CTRL); + } + else if (line_in == io_type) + { + rt_kprintf("audio input changed to line_in\n"); + writel(reg | (1<<1), audio_dev.reg_base + ACW_ADC_PATH_CTRL); + } + } + else + { + reg = readl(audio_dev.reg_base + ACW_DAC_PATH_CTRL); + if (speaker_out == io_type) + { + rt_kprintf("audio output changed to speaker_out\n"); + reg = reg & (~(3<<21)); + reg = reg & (~(3<<30)); + writel(reg, audio_dev.reg_base + ACW_DAC_PATH_CTRL); + reg = reg | (1<<21); + writel(reg,audio_dev.reg_base + ACW_DAC_PATH_CTRL); + reg = reg | (1<<18); + writel(reg, audio_dev.reg_base + ACW_DAC_PATH_CTRL);/*unmute speaker*/ + reg = reg | (3<<30); + writel(reg,audio_dev.reg_base + ACW_DAC_PATH_CTRL);/*mute line out*/ + } + else if (line_out == io_type) + { + rt_kprintf("audio output changed to line_out\n"); + reg = reg & (~(3<<21)); + writel(reg, audio_dev.reg_base + ACW_DAC_PATH_CTRL);/*mute speaker*/ + reg = reg & (~(3<<30)); + writel(reg, audio_dev.reg_base + ACW_DAC_PATH_CTRL);/*unmute line out*/ + } + } +} + +int get_factor_from_table(int rate) +{ + int factor; + switch(rate) + { + case 8000: + factor = 4; + break; + case 16000: + factor = 1; + break; + case 32000: + factor = 0; + break; + case 44100: + factor = 13; + break; + case 48000: + factor = 6; + break; + default: + factor = -EFAULT; + break; + } + return factor; +} + +void switch_rate(enum audio_type type, int rate) +{ + int reg, factor; + factor = get_factor_from_table(rate); + if (factor < 0) + { + rt_kprintf( "unsupported sample rate\n"); + return; + } + reg = readl(audio_dev.reg_base + ACW_DIG_IF_CTRL); + if (capture == type) + { + rt_kprintf("capture rate set to %d\n", rate); + reg = reg & (~(0xf<<0)); + writel(reg, audio_dev.reg_base + ACW_DIG_IF_CTRL);/*adc and dac sample rate*/ + reg = reg | (factor<<0); + writel(reg,audio_dev.reg_base + ACW_DIG_IF_CTRL); + } + else + { + rt_kprintf("playback rate set to %d\n", rate); + reg = reg & (~(0xf<<8)); + writel(reg, audio_dev.reg_base + ACW_DIG_IF_CTRL);/*adc and dac sample rate*/ + reg = reg | (factor<<8); + writel(reg, audio_dev.reg_base + ACW_DIG_IF_CTRL); + } +} + +int get_param_from_volume(int volume) +{ + if(volume < 0) + volume = 0; + else if(volume > 100) + volume = 100; + + volume = volume * 63 / 100; + return volume; + +} + +void switch_input_volume(int volume) +{ + int reg, param; + param = get_param_from_volume(volume); + if (param < 0) + { + rt_kprintf("capture volume error\n"); + return; + } + + reg = readl(audio_dev.reg_base + ACW_ADC_PATH_CTRL); + reg = reg & (~(0x3f<<8)); + writel(reg, audio_dev.reg_base + ACW_ADC_PATH_CTRL); + reg = reg | (param<<8); + writel(reg,audio_dev.reg_base + ACW_ADC_PATH_CTRL); +} + +void init_audio(enum audio_type type,struct fh_audio_cfg *audio_config) +{ + int reg; + reg = readl(audio_dev.reg_base + ACW_CTRL); + if ((reg & 0x80000000) == 0) + { + writel(0x80000000, audio_dev.reg_base + ACW_CTRL);/*enable audio*/ + } + reg = readl(audio_dev.reg_base + ACW_MISC_CTRL); + if (0x40400 != reg) + { + writel(0x40400,audio_dev.reg_base + ACW_MISC_CTRL);/*misc ctl*/ + } + if (capture == type) + { + writel(0x61141b06,audio_dev.reg_base + ACW_ADC_PATH_CTRL);/*adc cfg*/ + writel(0x167f2307, audio_dev.reg_base + ACW_ADC_ALC_CTRL);/*adc alc*/ + writel(0, audio_dev.reg_base + ACW_RXFIFO_CTRL);/*rx fifo disable*/ + switch_input_volume(audio_config->capture.cfg.volume); + switch_rate(capture, audio_config->capture.cfg.rate); + switch_io_type(capture, audio_config->capture.cfg.io_type); + } + else + { + writel(0x3b403f09, audio_dev.reg_base + ACW_DAC_PATH_CTRL);/*dac cfg*/ + writel(0, audio_dev.reg_base + ACW_TXFIFO_CTRL);/*tx fifo disable*/ + switch_rate(playback, audio_config->playback.cfg.rate); + switch_io_type(playback, audio_config->playback.cfg.io_type); + } + +} + +static inline long bytes_to_frames(int frame_bit, int bytes) +{ + return bytes * 8 /frame_bit; +} + +static inline long frames_to_bytes(int frame_bit, int frames) +{ + return frames * frame_bit / 8; +} + +int avail_data_len(enum audio_type type,struct fh_audio_cfg *stream) +{ + int delta; + if (capture == type) + { + + + delta = stream->capture.hw_ptr - stream->capture.appl_ptr; + + if (delta < 0) + { + delta += stream->capture.size; + } + return delta; + } + else + { + + + delta = stream->playback.appl_ptr - stream->playback.hw_ptr; + + if (delta < 0) + { + delta += stream->playback.size; + } + return stream->playback.size - delta; + } +} + +static rt_err_t fh_audio_close(rt_device_t dev) +{ + struct fh_audio_cfg *audio_config = dev->user_data; + unsigned int reg; + + //disable interrupts + reg = readl(audio_dev.reg_base + ACW_CTRL); + reg &= ~(0x3ff); + writel(reg, audio_dev.reg_base + ACW_CTRL); + + fh_acw_stop_playback(audio_config); + + fh_acw_stop_capture(audio_config); + +} + +int register_tx_dma(struct fh_audio_cfg *audio_config) +{ + int ret; + struct dma_transfer *playback_trans; + playback_trans = audio_config->plauback_trans; + struct rt_dma_device *rt_dma_dev; + rt_dma_dev = audio_config->playback_dma; + if ((audio_config->playback.cfg.buffer_bytes < audio_config->playback.cfg.period_bytes) || + (audio_config->playback.cfg.buffer_bytes <= 0) || (audio_config->playback.cfg.period_bytes <= 0)) + { + rt_kprintf( "buffer_size and period_size are invalid\n"); + return RT_ERROR; + } + + if(playback_trans->channel_number == ACW_PLY_DMA_CHAN){ + + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_CYCLIC_PREPARE,playback_trans); + if(ret){ + rt_kprintf("can't playback cyclic prepare \n"); + return RT_ERROR; + } + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_CYCLIC_START,playback_trans); + if(ret){ + rt_kprintf("can't playback cyclic start \n"); + return RT_ERROR; + } + } + else + return RT_ERROR; + return 0; +} + +int register_rx_dma( struct fh_audio_cfg *audio_config) +{ + int ret; + struct dma_transfer *capture_slave; + capture_slave = audio_config->capture_trans; + struct rt_dma_device *rt_dma_dev; + rt_dma_dev = audio_config->capture_dma; + if (!capture_slave) + { + return -ENOMEM; + } + + if ((audio_config->capture.cfg.buffer_bytes < audio_config->capture.cfg.period_bytes) || + (audio_config->capture.cfg.buffer_bytes <= 0) ||(audio_config->capture.cfg.period_bytes <= 0) ) + { + rt_kprintf( "buffer_size and period_size are invalid\n"); + return RT_ERROR; + } + if(capture_slave->channel_number==ACW_CAP_DMA_CHAN){ + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_CYCLIC_PREPARE,capture_slave); + if(ret){ + rt_kprintf("can't capture cyclic prepare \n"); + return RT_ERROR; + } + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_CYCLIC_START,capture_slave); + if(ret){ + rt_kprintf("can't capture cyclic start \n"); + return RT_ERROR; + } + } + else + return RT_ERROR; + writel(0x11,audio_dev.reg_base + ACW_RXFIFO_CTRL);//clear rx fifo + writel(0x30029,audio_dev.reg_base + ACW_RXFIFO_CTRL);/*enable rx fifo*/ + + return 0; + +} + + +void playback_start_wq_handler(struct work_struct *work) +{ + int avail; + unsigned int rx_status; + while(1) + { + if (stopping == audio_dev.audio_config->playback.state) + { + return; + } + avail = avail_data_len(playback, audio_dev.audio_config); + if (avail < audio_dev.audio_config->playback.cfg.period_bytes) + { + rt_thread_sleep(0); + } + else + { + rx_status = readl( audio_dev.reg_base + ACW_TXFIFO_CTRL);//clear rx fifo + rx_status = rx_status|(1<<4); + writel(rx_status,audio_dev.reg_base + ACW_TXFIFO_CTRL); + writel(0x30029, audio_dev.reg_base + ACW_TXFIFO_CTRL); + break; + } + } +} + +int fh_acw_start_playback(struct fh_audio_cfg *audio_config) +{ + int ret; + + if(audio_config->playback.state == running) + { + rt_kprintf("playback is running \n"); + return 0; + } + + if (audio_config->playback.cfg.buffer_bytes >= AUDIO_DMA_PREALLOC_SIZE) + { + rt_kprintf("DMA prealloc buffer is smaller than audio_config->buffer_bytes %x\n",audio_config->playback.cfg.buffer_bytes); + return -ENOMEM; + } + reset_dma_buff(playback,audio_config); + rt_memset(audio_config->playback.area, 0, audio_config->playback.cfg.buffer_bytes); + audio_config->playback.size = audio_config->playback.cfg.buffer_bytes; + audio_config->playback.state = running; + ret = audio_request_playback_channel(audio_config); + if(ret){ + rt_kprintf("can't request playback channel\n"); + return ret; + } + ret = register_tx_dma(audio_config); + if (ret < 0) + { + rt_kprintf("can't register tx dma\n"); + return ret; + } + rt_list_init(&(playback_wq.list)); + playback_wq.work_func = (void *)playback_start_wq_handler; + playback_wq.work_data = RT_NULL; + playback_queue = rt_workqueue_create("play_workqueue",WORK_QUEUE_STACK_SIZE,WORK_QUEUE_PRIORITY); + if(!playback_queue){ + + rt_kprintf("init work_queue error....\n"); + return -1; + } + rt_workqueue_dowork(playback_queue,&playback_wq); + return 0; +} + +int fh_acw_start_capture(struct fh_audio_cfg *audio_config) +{ + int ret; + if(audio_config->capture.state == running) + { + return 0; + } + if (audio_config->capture.cfg.buffer_bytes >= AUDIO_DMA_PREALLOC_SIZE) + { + rt_kprintf("DMA prealloc buffer is smaller than audio_config->buffer_bytes %x\n",audio_config->capture.cfg.buffer_bytes); + return -ENOMEM; + } + reset_dma_buff(capture,audio_config); + rt_memset(audio_config->capture.area, 0, audio_config->capture.cfg.buffer_bytes); + audio_config->capture.size = audio_config->capture.cfg.buffer_bytes; + audio_config->capture.state = running; + ret = audio_request_capture_channel(audio_config); + if(ret){ + rt_kprintf("can't request capture channel \n"); + return ret; + } + + return register_rx_dma(audio_config); +} + +static void fh_acw_rx_dma_done(struct fh_audio_cfg *arg) +{ +#if 1 + struct fh_audio_cfg *audio_config; + audio_config = arg; + + audio_config->capture.hw_ptr += audio_config->capture.cfg.period_bytes; + + if (audio_config->capture.hw_ptr > audio_config->capture.size ) // TBD_WAIT ... + { + audio_config->capture.hw_ptr = audio_config->capture.hw_ptr - audio_config->capture.size; + + } + + int avail = avail_data_len(capture,audio_config); + if (avail > audio_config->capture.cfg.period_bytes) + { + rt_sem_release(&audio_config->sem_capture); + } +#endif +} + +static void fh_acw_tx_dma_done(void *arg) +{ +#if 1 + struct fh_audio_cfg *audio_config; + audio_config = ( struct fh_audio_cfg *)arg; + + + audio_config->playback.hw_ptr += audio_config->playback.cfg.period_bytes; + + if (audio_config->playback.hw_ptr > audio_config->playback.size ) + { + + audio_config->playback.hw_ptr = audio_config->playback.hw_ptr - audio_config->playback.size; + } + + int avail = avail_data_len(playback,audio_config); + if (avail > audio_config->playback.cfg.period_bytes) + { + + rt_sem_release(&audio_config->sem_playback); + } + +#endif +} + +bool fh_acw_dma_chan_filter(struct dma_chan *chan, void *filter_param) +{ + +} + +int arg_config_support(struct fh_audio_cfg_arg * cfg) +{ + int ret; + + ret = get_param_from_volume(cfg->volume); + if (ret < 0) { + rt_kprintf("invalid volume\n"); + return -EINVAL; + } + ret = get_factor_from_table(cfg->rate); + if (ret < 0) { + rt_kprintf("invalid rate\n"); + return -EINVAL; + } + return 0; +} + +void reset_dma_buff(enum audio_type type, struct fh_audio_cfg *audio_config) +{ + if (capture == type) + { + audio_config->capture.appl_ptr = 0; + audio_config->capture.hw_ptr = 0; + } + else + { + audio_config->playback.appl_ptr = 0; + audio_config->playback.hw_ptr = 0; + } +} + + +static rt_err_t fh_audio_ioctl(rt_device_t dev, rt_uint8_t cmd, void *arg) +{ + struct fh_audio_cfg_arg *cfg; + + struct fh_audio_cfg *audio_config = (struct fh_audio_cfg *)dev->user_data; + int ret; + int reg; + int value,pid; + int *p = (int *)arg; + int rx_status,tx_status; + + switch (cmd) + { + case AC_INIT_CAPTURE_MEM: + + cfg = (struct fh_audio_cfg_arg *)arg; + if (0 == arg_config_support(cfg)) + { + audio_config->capture.cfg.io_type = cfg->io_type; + audio_config->capture.cfg.volume = cfg->volume; + audio_config->capture.cfg.rate = cfg->rate; + audio_config->capture.cfg.channels = cfg->channels; + audio_config->capture.cfg.buffer_size = cfg->buffer_size; + audio_config->capture.cfg.frame_bit = FIX_SAMPLE_BIT; + audio_config->capture.cfg.period_size = cfg->period_size; + audio_config->capture.cfg.buffer_bytes = frames_to_bytes(audio_config->capture.cfg.frame_bit,audio_config->capture.cfg.buffer_size); + audio_config->capture.cfg.period_bytes = frames_to_bytes(audio_config->capture.cfg.frame_bit,audio_config->capture.cfg.period_size); + audio_config->capture.cfg.start_threshold =audio_config->capture.cfg.buffer_bytes; + audio_config->capture.cfg.stop_threshold = audio_config->capture.cfg.buffer_bytes; + audio_prealloc_dma_buffer((int)cfg->io_type,audio_config); + reset_dma_buff(capture, audio_config); + + rt_mutex_init(&audio_config->capture.lock, "audio_c", RT_IPC_FLAG_PRIO); + init_audio(capture, audio_config); + audio_param_store.input_io_type = audio_config->capture.cfg.io_type; + audio_param_store.input_volume = audio_config->capture.cfg.volume; + + } + else + { + return -EINVAL; + } + + break; + case AC_INIT_PLAYBACK_MEM: + cfg = arg; + + if (0 == arg_config_support(cfg)) + { + audio_config->playback.cfg.io_type = cfg->io_type; + audio_config->playback.cfg.volume = cfg->volume; + audio_config->playback.cfg.rate = cfg->rate; + audio_config->playback.cfg.channels = cfg->channels; + audio_config->playback.cfg.buffer_size = cfg->buffer_size; + audio_config->playback.cfg.frame_bit = FIX_SAMPLE_BIT; + audio_config->playback.cfg.period_size = cfg->period_size; + audio_config->playback.cfg.buffer_bytes = frames_to_bytes(audio_config->playback.cfg.frame_bit,audio_config->playback.cfg.buffer_size); + audio_config->playback.cfg.period_bytes = frames_to_bytes(audio_config->playback.cfg.frame_bit,audio_config->playback.cfg.period_size); + audio_config->playback.cfg.start_threshold =audio_config->playback.cfg.buffer_bytes; + audio_config->playback.cfg.stop_threshold = audio_config->playback.cfg.buffer_bytes; + audio_prealloc_dma_buffer((int)cfg->io_type,audio_config); // TBD_WAIT ... + reset_dma_buff(playback, audio_config); + + rt_mutex_init(&audio_config->playback.lock, "audio_p", RT_IPC_FLAG_PRIO); + + init_audio(playback, audio_config); + + } + else + { + return -EINVAL; + } + break; + case AC_AI_EN: + return fh_acw_start_capture(audio_config); + case AC_AO_EN: + rt_kprintf("ao en \n"); + return fh_acw_start_playback(audio_config); + + case AC_SET_VOL: + value = *(rt_uint32_t *)arg; + ret = get_param_from_volume(value); + if (ret < 0) { + return -EINVAL; + } + audio_param_store.input_volume = value; + switch_input_volume(audio_param_store.input_volume); + break; + case AC_SET_INPUT_MODE: + + value = *(rt_uint32_t *)arg; + if (value != mic_in && value != line_in) { + return -EINVAL; + } + audio_param_store.input_io_type = value; + switch_io_type(capture, audio_param_store.input_io_type); + break; + case AC_SET_OUTPUT_MODE: + value = *(rt_uint32_t *)arg; + + if (value != speaker_out && value != line_out) { + return -EINVAL; + } + switch_io_type(playback, value); + break; + case AC_AI_DISABLE: + rt_kprintf(" AC_AI_DISABLE\n"); + + fh_acw_stop_capture(audio_config); + if (audio_config->capture_trans != RT_NULL) + { + rt_free(audio_config->capture_trans); + audio_config->capture_trans = NULL; + } + break; + case AC_AO_DISABLE: + rt_kprintf("[ac_driver]AC_AO_DISABLE\n"); + + fh_acw_stop_playback(audio_config); + if (audio_config->plauback_trans != RT_NULL) + { + rt_free(audio_config->plauback_trans); + audio_config->plauback_trans = NULL; + } + rt_kprintf(" AC_AO_DISABLE\n"); + break; + case AC_AI_PAUSE: + + rt_kprintf( "capture pause\n"); + rx_status = readl(audio_dev.reg_base + ACW_RXFIFO_CTRL);/*rx fifo disable*/ + rx_status = rx_status&(~(1<<0)); + writel(rx_status, audio_dev.reg_base + ACW_RXFIFO_CTRL);/*rx fifo disable*/ + break; + case AC_AI_RESUME: + + rt_kprintf( "capture resume\n"); + rx_status = readl( audio_dev.reg_base + ACW_RXFIFO_CTRL);//clear rx fifo + rx_status = rx_status|(1<<4); + writel(rx_status,audio_dev.reg_base+ ACW_RXFIFO_CTRL);/*enable rx fifo*/ + rx_status = rx_status&(~(1<<4)); + rx_status = rx_status|(1<<0); + writel(rx_status,audio_dev.reg_base + ACW_RXFIFO_CTRL);/*enable rx fifo*/ + break; + case AC_AO_PAUSE: + + rt_kprintf( "playback pause\n"); + tx_status = readl(audio_dev.reg_base + ACW_TXFIFO_CTRL);/*rx fifo disable*/ + tx_status = tx_status&(~(1<<0)); + writel(tx_status, audio_dev.reg_base + ACW_TXFIFO_CTRL);/*tx fifo disable*/ + break; + case AC_AO_RESUME: + + rt_kprintf( "playback resume\n"); + tx_status = readl( audio_dev.reg_base + ACW_TXFIFO_CTRL);//clear rx fifo + tx_status = tx_status|(1<<0); + writel(tx_status,audio_dev.reg_base + ACW_TXFIFO_CTRL); //enable tx fifo read enable + break; + default: + return -ENOTTY; + } + return 0; +} + +static rt_err_t fh_audio_open(rt_device_t dev, rt_uint16_t oflag) +{ + + unsigned int reg; + struct fh_audio_cfg *audio_config = dev->user_data; + //enable interrupts + reg = readl(audio_dev.reg_base + ACW_CTRL); + reg |= 0xa; + writel(reg, audio_dev.reg_base + ACW_CTRL); + + return 0; +} + +static rt_err_t fh_audio_tx_poll(rt_device_t dev, void *buffer){ + struct fh_audio_cfg *audio_config = dev->user_data; + unsigned int mask = 0; + long avail; + + if (running == audio_config->playback.state) + { + + rt_sem_take(&audio_config->sem_playback, RT_WAITING_FOREVER); + avail = avail_data_len(playback, audio_config); + if (avail > audio_config->playback.cfg.period_bytes) + { + mask |= POLLOUT | POLLWRNORM; + } + } + + return mask; +} + +static rt_err_t fh_audio_rx_poll(rt_device_t dev, rt_size_t size){ + + struct fh_audio_cfg *audio_config = dev->user_data; + unsigned int mask = 0; + long avail; + if (running == audio_config->capture.state) + { + rt_sem_take(&audio_config->sem_capture, RT_WAITING_FOREVER); + avail = avail_data_len(capture, audio_config); + if (avail > audio_config->capture.cfg.period_bytes) + { + mask |= POLLIN | POLLRDNORM; + } + } + return mask; +} +static dma_complete_callback mem_complete(void *p){ + + struct rt_completion *completion = (struct rt_completion *)p; + + rt_completion_done(completion); +} + +static rt_size_t fh_audio_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + + int ret; + struct fh_audio_cfg *audio_config = dev->user_data; + int after,left; + int pid,avail; + + + + avail = avail_data_len(capture, audio_config); + if (avail > size) + { + avail = size; + } + after = avail + audio_config->capture.appl_ptr; + if(after > audio_config->capture.size) + { + left = avail - (audio_config->capture.size - audio_config->capture.appl_ptr); + rt_memcpy(buffer, audio_config->capture.area+audio_config->capture.appl_ptr, audio_config->capture.size-audio_config->capture.appl_ptr); + rt_memcpy(buffer+audio_config->capture.size-audio_config->capture.appl_ptr,audio_config->capture.area,left); + rt_mutex_take(&audio_config->capture.lock, RT_WAITING_FOREVER); + audio_config->capture.appl_ptr = left; + rt_mutex_release(&audio_config->capture.lock); + + } + else + { + rt_memcpy(buffer,audio_config->capture.area+audio_config->capture.appl_ptr,avail); + rt_mutex_take(&audio_config->capture.lock, RT_WAITING_FOREVER); + audio_config->capture.appl_ptr += avail; + rt_mutex_release(&audio_config->capture.lock); + + } + return avail; + +} + +static rt_size_t fh_audio_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + + struct fh_audio_cfg *audio_config = dev->user_data; + int ret; + int after,left; + int pid,avail; + + avail = avail_data_len(playback,audio_config); + if (0 == avail) + { + return 0; + } + if (avail > size) + { + avail = size; + } + after = avail+audio_config->playback.appl_ptr; + if(after > audio_config->playback.size) + { + left = avail - (audio_config->playback.size-audio_config->playback.appl_ptr); + rt_memcpy(audio_config->playback.area+audio_config->playback.appl_ptr,buffer,audio_config->playback.size-audio_config->playback.appl_ptr); + rt_memcpy(audio_config->playback.area,buffer+audio_config->playback.size-audio_config->playback.appl_ptr,left); + rt_mutex_take(&audio_config->playback.lock, RT_WAITING_FOREVER); + audio_config->playback.appl_ptr = left; + rt_mutex_release(&audio_config->playback.lock); + + } + else + { + rt_memcpy(audio_config->playback.area+audio_config->playback.appl_ptr,buffer,avail); + rt_mutex_take(&audio_config->playback.lock, RT_WAITING_FOREVER); + audio_config->playback.appl_ptr += avail; + rt_mutex_release(&audio_config->playback.lock); + } + return avail; +} + +static void fh_audio_interrupt(int irq, void *param) +{ + unsigned int interrupts, reg; + struct fh_audio_cfg *audio_config = audio_dev.audio_config; + + interrupts = readl(audio_dev.reg_base + ACW_CTRL); + writel(interrupts, audio_dev.reg_base + ACW_CTRL); + + if(interrupts & ACW_INTR_RX_UNDERFLOW) + { + fh_acw_stop_capture(audio_config); + fh_acw_start_capture(audio_config); + rt_kprintf("ACW_INTR_RX_UNDERFLOW\n"); + } + + if(interrupts & ACW_INTR_RX_OVERFLOW) + { + fh_acw_stop_capture(audio_config); + fh_acw_start_capture(audio_config); + rt_kprintf("ACW_INTR_RX_OVERFLOW\n"); + } + + if(interrupts & ACW_INTR_TX_UNDERFLOW) + { + fh_acw_stop_capture(audio_config); + fh_acw_start_capture(audio_config); + rt_kprintf("ACW_INTR_TX_UNDERFLOW\n"); + } + + if(interrupts & ACW_INTR_TX_OVERFLOW) + { + fh_acw_stop_capture(audio_config); + fh_acw_start_capture(audio_config); + rt_kprintf("ACW_INTR_TX_OVERFLOW\n"); + } + + rt_kprintf("interrupts: 0x%x\n", interrupts); + + +} + + +void audio_prealloc_dma_buffer(int aiaotype,struct fh_audio_cfg *audio_config) +{ + + if(aiaotype == mic_in || aiaotype == line_in){ + audio_config->capture.area = (void *)fh_dma_mem_malloc(audio_config->capture.cfg.buffer_bytes \ + + audio_config->capture.cfg.period_bytes); + + if (!audio_config->capture.area) + { + rt_kprintf("no enough mem for capture buffer alloc\n"); + return ; + } + } + if(aiaotype == speaker_out || aiaotype == line_out){ + audio_config->playback.area = (void *)fh_dma_mem_malloc(audio_config->playback.cfg.buffer_bytes \ + + audio_config->playback.cfg.period_bytes); + + if (!audio_config->playback.area) + { + rt_kprintf("no enough mem for playback buffer alloc\n"); + return ; + }} + +} + +void audio_free_prealloc_dma_buffer(struct fh_audio_cfg *audio_config) +{ + + rt_free( audio_config->capture.area); + rt_free( audio_config->playback.area); +} + +static void init_audio_mutex(struct fh_audio_cfg *audio_config) +{ + rt_sem_init(&audio_config->sem_capture, "sem_capture", 0, RT_IPC_FLAG_FIFO); + rt_sem_init(&audio_config->sem_playback, "sem_playback", 0, RT_IPC_FLAG_FIFO); +} +int audio_request_capture_channel(struct fh_audio_cfg *audio_config){ + struct rt_dma_device *rt_dma_dev; + /*request audio rx dma channel*/ + struct dma_transfer *dma_rx_transfer; + int ret; + dma_rx_transfer = rt_malloc(sizeof(struct dma_transfer)); + + if (!dma_rx_transfer) + { + rt_kprintf("alloc dma_rx_transfer failed\n"); + return RT_ENOMEM; + } + + rt_memset(dma_rx_transfer, 0, sizeof(struct dma_transfer)); + rt_dma_dev = (struct rt_dma_device *)rt_device_find("fh81_dma"); + + if(rt_dma_dev == RT_NULL){ + rt_kprintf("can't find dma dev\n"); + + return -1; + } + audio_config->capture_dma = rt_dma_dev; + audio_config->capture_trans = dma_rx_transfer; + rt_dma_dev->ops->init(rt_dma_dev); + + dma_rx_transfer->channel_number = ACW_CAP_DMA_CHAN; + + dma_rx_transfer->dma_number = 0; + + dma_rx_transfer->dst_add = (rt_uint32_t)audio_config->capture.area;//audio_config->capture.area;//(rt_uint32_t)&tx_buff[0]; + dma_rx_transfer->dst_inc_mode = DW_DMA_SLAVE_INC; + dma_rx_transfer->dst_msize = DW_DMA_SLAVE_MSIZE_32; + + dma_rx_transfer->dst_width = DW_DMA_SLAVE_WIDTH_32BIT; + dma_rx_transfer->fc_mode = DMA_P2M; + + dma_rx_transfer->src_add = (rt_uint32_t)ACW_RXFIFO; + + dma_rx_transfer->src_inc_mode = DW_DMA_SLAVE_FIX; + dma_rx_transfer->src_msize = DW_DMA_SLAVE_MSIZE_32; + dma_rx_transfer->src_hs = DMA_HW_HANDSHAKING; + dma_rx_transfer->src_width = DW_DMA_SLAVE_WIDTH_32BIT; + dma_rx_transfer->trans_len = (audio_config->capture.cfg.buffer_bytes / 4); // DW_DMA_SLAVE_WIDTH_32BIT BUFF_SIZE; + dma_rx_transfer->src_per =ACODEC_RX; + dma_rx_transfer->period_len = audio_config->capture.cfg.period_bytes / 4;// (audio_config->capture.cfg.period_bytes / 4); // TEST_PER_NO; + dma_rx_transfer->complete_callback =(dma_complete_callback)fh_acw_rx_dma_done; + dma_rx_transfer->complete_para = audio_config; + + rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_OPEN,dma_rx_transfer); + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL,dma_rx_transfer); + if(ret){ + rt_kprintf("can't request capture channel\n"); + dma_rx_transfer->channel_number =0xff; + return -ret; + } + +} + +int audio_request_playback_channel(struct fh_audio_cfg *audio_config) +{ + struct rt_dma_device *rt_dma_dev; + int ret; + struct dma_transfer *dma_tx_transfer; + dma_tx_transfer = rt_malloc(sizeof(struct dma_transfer)); + if (!dma_tx_transfer) + { + rt_kprintf("alloc dma_tx_transfer failed\n"); + return RT_ENOMEM; + + } + audio_config->plauback_trans = dma_tx_transfer; + rt_dma_dev = (struct rt_dma_device *)rt_device_find("fh81_dma"); + + if(rt_dma_dev == RT_NULL){ + rt_kprintf("can't find dma dev\n"); + return -1; + } + rt_dma_dev->ops->init(rt_dma_dev); + audio_config->playback_dma = rt_dma_dev; + + rt_memset(dma_tx_transfer, 0, sizeof(struct dma_transfer)); + dma_tx_transfer->channel_number = ACW_PLY_DMA_CHAN; + dma_tx_transfer->dma_number = 0; + dma_tx_transfer->dst_add = (rt_uint32_t)ACW_TXFIFO; + dma_tx_transfer->dst_hs = DMA_HW_HANDSHAKING; + dma_tx_transfer->dst_inc_mode = DW_DMA_SLAVE_FIX; + dma_tx_transfer->dst_msize = DW_DMA_SLAVE_MSIZE_32; + dma_tx_transfer->dst_per = ACODEC_TX; + dma_tx_transfer->dst_width = DW_DMA_SLAVE_WIDTH_32BIT; + dma_tx_transfer->fc_mode = DMA_M2P; + dma_tx_transfer->src_add = (rt_uint32_t)audio_config->playback.area; + dma_tx_transfer->src_inc_mode = DW_DMA_SLAVE_INC; + dma_tx_transfer->src_msize = DW_DMA_SLAVE_MSIZE_32; + dma_tx_transfer->src_width = DW_DMA_SLAVE_WIDTH_32BIT; + dma_tx_transfer->trans_len = (audio_config->playback.cfg.buffer_bytes / 4);// BUFF_SIZE; + dma_tx_transfer->period_len = (audio_config->playback.cfg.period_bytes / 4); // TEST_PER_NO; + dma_tx_transfer->complete_callback =(dma_complete_callback)fh_acw_tx_dma_done; + dma_tx_transfer->complete_para = audio_config; + rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_OPEN,dma_tx_transfer); + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL,dma_tx_transfer); + if(ret){ + rt_kprintf("can't request playbak channel\n"); + dma_tx_transfer->channel_number = 0xff; + return -ret; + } + return 0; + +} + +void audio_release_dma_channel(struct fh_audio_cfg *audio_config) +{ + + if (audio_config->plauback_trans != RT_NULL) + { + audio_config->playback_dma->ops->control(audio_config->playback_dma,RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL,audio_config->plauback_trans); + rt_free(audio_config->plauback_trans); + audio_config->plauback_trans = NULL; + } + + + if (audio_config->capture_trans != RT_NULL) + { + audio_config->capture_dma->ops->control(audio_config->capture_dma,RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL,audio_config->capture_trans); + rt_free(audio_config->capture_trans); + audio_config->capture_trans = NULL; + } + + +} + +void fh_audio_init(void) +{ + + struct fh_audio_cfg *audio_config; + audio_config = rt_malloc(sizeof(struct fh_audio_cfg)); + memset(audio_config,0,sizeof(struct fh_audio_cfg)); // new add + + audio_dev.reg_base = 0xf0a00000; + init_audio_mutex(audio_config); + + rt_device_t audio ; + audio = rt_malloc(sizeof(struct rt_device)); + if (audio == RT_NULL){ + rt_kprintf("%s no mem \n",__func__); + } + + audio->user_data = audio_config; + audio->open =fh_audio_open; + audio->read = fh_audio_read; + audio->write = fh_audio_write; + audio->close = fh_audio_close; + audio->control = fh_audio_ioctl; + audio->rx_indicate =fh_audio_rx_poll; + audio->tx_complete=fh_audio_tx_poll; + audio_dev.audio_config = audio_config; // TBD_WAIT 2015.09.17 add + + rt_device_register(audio, "audio", RT_DEVICE_FLAG_RDWR); + +} + + + +#if ACW_SELFTEST + +#define TEST_FN "/audio.dat" + +static rt_uint32_t rx_buff[BUFF_SIZE] __attribute__((aligned(32))) ; +static const rt_uint32_t tx_buff[BUFF_SIZE*2] __attribute__((aligned(32)))= { + 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa, + 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa, + 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa, + 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa, + 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa, + +}; + + + + + + struct fh_audio_cfg_arg cfg; + +void fh_acw_test(){ + rt_device_t acw_dev ; + int i; + int output=3; + int select; + int select_rx_status =0; + int select_tx_status =0; + int fd; + int index, length; + int mic_boost=1; + int ret; + acw_dev = ( rt_device_t )rt_device_find("audio"); + for(i=0;iopen(acw_dev,0); + cfg.buffer_size = BUFF_SIZE; + cfg.channels =0; + cfg.frame_bit = 16; + + cfg.io_type = mic_in; + + cfg.period_size = BUFF_SIZE/8; + cfg.rate = 8000; + cfg.volume = 80; + +// for(i=0;i<100;i++){ +// acw_dev->control(acw_dev,AC_INIT_CAPTURE_MEM,&cfg); +// +// acw_dev->control(acw_dev,AC_AI_EN,&cfg); +// cfg.io_type = line_out; +// acw_dev->control(acw_dev,AC_INIT_PLAYBACK_MEM,&cfg); +// acw_dev->control(acw_dev,AC_AO_EN,&cfg); +// acw_dev->control(acw_dev,AC_SET_OUTPUT_MODE,&output); +// acw_dev->control(acw_dev,AC_AI_DISABLE,&cfg); +// +// acw_dev->control(acw_dev,AC_AO_DISABLE,&cfg); +// rt_kprintf(" %d \n",i); +// } + + cfg.io_type = mic_in; + acw_dev->control(acw_dev,AC_INIT_CAPTURE_MEM,&cfg); + + ret = acw_dev->control(acw_dev,AC_AI_EN,&cfg); + if(ret) + acw_dev->control(acw_dev,AC_AI_DISABLE,&cfg); + cfg.io_type = line_out; + acw_dev->control(acw_dev,AC_INIT_PLAYBACK_MEM,&cfg); + ret = acw_dev->control(acw_dev,AC_AO_EN,&cfg); + if(ret){ + acw_dev->control(acw_dev,AC_AO_DISABLE,&cfg); + // acw_dev->control(acw_dev,AC_SET_OUTPUT_MODE,&output); + return ; + } + + + for(i=0;i<100;i++) + { + +rx: + select = acw_dev->rx_indicate(acw_dev,RT_NULL); + if(!select) + goto rx; + + + acw_dev->read(acw_dev,0,&rx_buff[0],1024*8); + +tx: + select = acw_dev->tx_complete(acw_dev , RT_NULL); + if(!select) + goto tx; + + acw_dev->write(acw_dev,0,&rx_buff[0],1024*8); + + } + acw_dev->close(acw_dev); + +} +#ifdef RT_USING_FINSH +#include +FINSH_FUNCTION_EXPORT(fh_acw_test, fh_acw_test); +#endif +#endif +#endif + diff --git a/bsp/fh8620/drivers/acw.h b/bsp/fh8620/drivers/acw.h new file mode 100644 index 0000000000000000000000000000000000000000..2156d343f4934aab083c21cb9962015a4b07ab57 --- /dev/null +++ b/bsp/fh8620/drivers/acw.h @@ -0,0 +1,232 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef ACW_H_ +#define ACW_H_ +#include +#include +#ifdef RT_USING_DFS +#include +#endif +#include +#define ACW_CAP_DMA_CHAN 2 +#define ACW_PLY_DMA_CHAN 3 +typedef unsigned long long dma_addr_t; +struct scatterlist { +#ifdef CONFIG_DEBUG_SG + unsigned long sg_magic; +#endif + unsigned long page_link; + unsigned int offset; + unsigned int length; + dma_addr_t dma_address; +#ifdef CONFIG_NEED_SG_DMA_LENGTH + unsigned int dma_length; +#endif +}; +#define readl(a) (*(volatile rt_uint32_t *)(a)) +#define rkqueue_struct rt_workqueue +#define work_struct rt_work +#define INIT_WORK(work,func) rt_work_init(work,func,RT_NULL); +#define queue_work rt_workqueue_dowork + + +//timer +#define timer_list rt_timer +#define wait_queue_head_t struct rt_event +#define init_waitqueue_head(event_t) rt_event_init(event_t, "audio_event", RT_IPC_FLAG_FIFO) +typedef enum{ + AC_SR_8K = 8000, + AC_SR_16K = 16000, + AC_SR_32K = 32000, + AC_SR_441K = 44100, + AC_SR_48K = 48000, +} FH_AC_SAMPLE_RATE_E; + +typedef enum{ + AC_BW_8 = 8, + AC_BW_16 = 16, + AC_BW_24 = 24, +} FH_AC_BIT_WIDTH_E; + +enum io_select{ + mic_in = 0, + line_in = 1, + speaker_out = 2, + line_out = 3, +}; + +struct fh_audio_cfg_arg{ + enum io_select io_type; + int volume; + int rate; + int frame_bit; + int channels; + int buffer_size; + int period_size; +}; +typedef struct{ + unsigned int len; + unsigned char *data; +}FH_AC_FRAME_S; + +typedef enum{ + FH_AC_MIC_IN = 0, + FH_AC_LINE_IN = 1, + FH_AC_SPK_OUT = 2, + FH_AC_LINE_OUT = 3 +}FH_AC_IO_TYPE_E; + + +typedef struct { + FH_AC_IO_TYPE_E io_type; + FH_AC_SAMPLE_RATE_E sample_rate; + FH_AC_BIT_WIDTH_E bit_width; + unsigned int channels; + unsigned int period_size; + unsigned int volume; +} FH_AC_CONFIG; + +struct device_dma_parameters { + /* + * a low level driver may set these to teach IOMMU code about + * sg limitations. + */ + unsigned int max_segment_size; + unsigned long segment_boundary_mask; +}; + +struct list_head { + struct list_head *next; + struct list_head *prev; +}; +struct dma_coherent_mem { + void *virt_base; + dma_addr_t device_base; + int size; + int flags; + unsigned long *bitmap; +}; +struct device_acw{ + unsigned long long *dma_mask; /* dma mask (if dma'able device) */ + unsigned long long coherent_dma_mask;/* Like dma_mask, but for + alloc_coherent mappings as + not all hardware supports + 64 bit addresses for consistent + allocations such descriptors. */ + struct device_dma_parameters *dma_parms; + + struct list_head dma_pools; + + struct dma_coherent_mem *dma_mem; +}; +#define false 0 +#define true 1 + + + +#define AC_INIT_CAPTURE_MEM 0x10 +#define AC_INIT_PLAYBACK_MEM 0x11 + + +#define AC_SET_VOL 0x12 +#define AC_SET_INPUT_MODE 0x13 +#define AC_SET_OUTPUT_MODE 0x14 + + +#define AC_AI_EN 0x15 +#define AC_AO_EN 0x16 +#define AC_AI_DISABLE 0x17 +#define AC_AO_DISABLE 0x18 +#define AC_AI_PAUSE 0x19 +#define AC_AI_RESUME 0x1a +#define AC_AO_PAUSE 0x1b +#define AC_AO_RESUME 0x1c +#define AC_MIC_BOOST 0x1d + +#define POLLIN 0x001 /* There is data to read. */ +#define POLLPRI 0x002 /* There is urgent data to read. */ +#define POLLOUT 0x004 /* Writing now will not block. */ + + +/* These values are defined in XPG4.2. */ +# define POLLRDNORM 0x040 /* Normal data may be read. */ +# define POLLRDBAND 0x080 /* Priority data may be read. */ +# define POLLWRNORM 0x100 /* Writing now will not block. */ +# define POLLWRBAND 0x200 /* Priority data may be written. */ + + + +/* These are extensions for Linux. */ +# define POLLMSG 0x400 +# define POLLREMOVE 0x1000 +# define POLLRDHUP 0x2000 + + +/* Event types always implicitly polled for. These bits need not be set in + `events', but they will appear in `revents' to indicate the status of + the file descriptor. */ +#define POLLERR 0x008 /* Error condition. */ +#define POLLHUP 0x010 /* Hung up. */ +#define POLLNVAL 0x020 /* Invalid polling request. */ + +#define EPERM 1 /* Operation not permitted */ +#define ENOENT 2 /* No such file or directory */ +#define ESRCH 3 /* No such process */ +#define EINTR 4 /* Interrupted system call */ +#define EIO 5 /* I/O error */ +#define ENXIO 6 /* No such device or address */ +#define E2BIG 7 /* Argument list too long */ +#define ENOEXEC 8 /* Exec format error */ +#define EBADF 9 /* Bad file number */ +#define ECHILD 10 /* No child processes */ +#define EAGAIN 11 /* Try again */ +#define ENOMEM 12 /* Out of memory */ +#define EACCES 13 /* Permission denied */ +#define EFAULT 14 /* Bad address */ +#define ENOTBLK 15 /* Block device required */ +#define EBUSY 16 /* Device or resource busy */ +#define EEXIST 17 /* File exists */ +#define EXDEV 18 /* Cross-device link */ +#define ENODEV 19 /* No such device */ +#define ENOTDIR 20 /* Not a directory */ +#define EISDIR 21 /* Is a directory */ +#define EINVAL 22 /* Invalid argument */ +#define ENFILE 23 /* File table overflow */ +#define EMFILE 24 /* Too many open files */ +#define ENOTTY 25 /* Not a typewriter */ +#define ETXTBSY 26 /* Text file busy */ +#define EFBIG 27 /* File too large */ +#define ENOSPC 28 /* No space left on device */ +#define ESPIPE 29 /* Illegal seek */ +#define EROFS 30 /* Read-only file system */ +#define EMLINK 31 /* Too many links */ +#define EPIPE 32 /* Broken pipe */ +#define EDOM 33 /* Math argument out of domain of func */ +#define ERANGE 34 /* Math result not representable */ +extern void fh_audio_init(void); +extern void fh_acw_test(); +#endif diff --git a/bsp/fh8620/drivers/dma.c b/bsp/fh8620/drivers/dma.c new file mode 100644 index 0000000000000000000000000000000000000000..394ac2a6a3d53f8b803228a248ed06a6647bf435 --- /dev/null +++ b/bsp/fh8620/drivers/dma.c @@ -0,0 +1,170 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +/***************************************************************************** + * Include Section + * add all #include here + *****************************************************************************/ +#include "drivers/dma.h" +/***************************************************************************** + * Define section + * add all #define here + *****************************************************************************/ + + +/**************************************************************************** + * ADT section + * add definition of user defined Data Type that only be used in this file here + ***************************************************************************/ + + +/****************************************************************************** + * Function prototype section + * add prototypes for all functions called by this file,execepting those + * declared in header file + *****************************************************************************/ + + +/***************************************************************************** + * Global variables section - Exported + * add declaration of global variables that will be exported here + * e.g. + * int8_t foo; + ****************************************************************************/ + + +/***************************************************************************** + + * static fun; + *****************************************************************************/ +static rt_err_t rt_dma_init(struct rt_device *dev); +static rt_err_t rt_dma_open(struct rt_device *dev, rt_uint16_t oflag); +static rt_err_t rt_dma_close(struct rt_device *dev); +static rt_err_t rt_dma_control(struct rt_device *dev, + rt_uint8_t cmd, + void *args); +/***************************************************************************** + * Global variables section - Local + * define global variables(will be refered only in this file) here, + * static keyword should be used to limit scope of local variable to this file + * e.g. + * static uint8_t ufoo; + *****************************************************************************/ + + + + + /* function body */ +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ +static rt_err_t rt_dma_init(struct rt_device *dev) +{ + struct rt_dma_device *dma; + + RT_ASSERT(dev != RT_NULL); + dma = (struct rt_dma_device *)dev; + if (dma->ops->init) + { + return (dma->ops->init(dma)); + } + + return (-RT_ENOSYS); +} + +static rt_err_t rt_dma_open(struct rt_device *dev, rt_uint16_t oflag) +{ + return (RT_EOK); +} + +static rt_err_t rt_dma_close(struct rt_device *dev) +{ + struct rt_dma_device *dma; + + RT_ASSERT(dev != RT_NULL); + dma = (struct rt_dma_device *)dev; + + if (dma->ops->control(dma, RT_DEVICE_CTRL_DMA_CLOSE, RT_NULL) != RT_EOK) + { + return (-RT_ERROR); + } + + return (RT_EOK); +} + +static rt_err_t rt_dma_control(struct rt_device *dev, + rt_uint8_t cmd, + void *args) +{ + struct rt_dma_device *dma; + + RT_ASSERT(dev != RT_NULL); + dma = (struct rt_dma_device *)dev; + + //args is the private data for the soc!! + return (dma->ops->control(dma, cmd, args)); +} + +/** + * This function register a dma device + */ +rt_err_t rt_hw_dma_register(struct rt_dma_device *dma, + const char *name, + rt_uint32_t flag, + void *data) +{ + rt_uint32_t ret; + struct rt_device *device; + RT_ASSERT(dma != RT_NULL); + + device = &(dma->parent); + + device->type = RT_Device_Class_Miscellaneous; + device->rx_indicate = RT_NULL; + device->tx_complete = RT_NULL; + + device->init = rt_dma_init; + device->open = rt_dma_open; + device->close = rt_dma_close; + device->read = RT_NULL; + device->write = RT_NULL; + device->control = rt_dma_control; + device->user_data = data; + + /* register a character device */ + ret = rt_device_register(device, name, flag); + rt_kprintf("dma ret is :%x\n",ret); + return ret; +} + + + + diff --git a/bsp/fh8620/drivers/dma.h b/bsp/fh8620/drivers/dma.h new file mode 100644 index 0000000000000000000000000000000000000000..c8c3fbf574f8a4ff63239ca1e9709e5af8ecd107 --- /dev/null +++ b/bsp/fh8620/drivers/dma.h @@ -0,0 +1,107 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef DMA_H_ +#define DMA_H_ +#include +/**************************************************************************** +* #include section +* add #include here if any +***************************************************************************/ + + +/**************************************************************************** +* #define section +* add constant #define here if any +***************************************************************************/ +#define RT_DEVICE_CTRL_DMA_OPEN (1) +#define RT_DEVICE_CTRL_DMA_CLOSE (2) +#define RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL (3) +#define RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL (4) +#define RT_DEVICE_CTRL_DMA_SINGLE_TRANSFER (5) + + +//cyclic add func below.... + + +#define RT_DEVICE_CTRL_DMA_CYCLIC_PREPARE (6) +#define RT_DEVICE_CTRL_DMA_CYCLIC_START (7) +#define RT_DEVICE_CTRL_DMA_CYCLIC_STOP (8) +#define RT_DEVICE_CTRL_DMA_CYCLIC_FREE (9) + + +//#define RT_DEVICE_CTRL_ (3) /* get the left time before reboot(in seconds) */ +//#define RT_DEVICE_CTRL_ (4) /* refresh watchdog */ +//#define RT_DEVICE_CTRL_ (5) /* start watchdog */ +//#define RT_DEVICE_CTRL_ (6) /* stop watchdog */ + + + + + +/**************************************************************************** +* ADT section +* add Abstract Data Type definition here +***************************************************************************/ + +struct rt_dma_ops; +struct rt_dma_device +{ + // the parent must be the fitst para.. + struct rt_device parent; + struct rt_dma_ops *ops; +}; + + +struct rt_dma_ops +{ + rt_err_t (*init)(struct rt_dma_device *dma); + rt_err_t (*control)(struct rt_dma_device *dma, int cmd, void *arg); +}; + + + + +/**************************************************************************** +* extern variable declaration section +***************************************************************************/ + +/**************************************************************************** +* section +* add function prototype here if any +***************************************************************************/ +rt_err_t rt_hw_dma_register(struct rt_dma_device *dma, + const char *name, + rt_uint32_t flag, + void *data); + +/********************************End Of File********************************/ + + + + +#endif + diff --git a/bsp/fh8620/drivers/dma_mem.c b/bsp/fh8620/drivers/dma_mem.c new file mode 100644 index 0000000000000000000000000000000000000000..52c9e56b4a8b2ab7b77566a0d7e0365baa2c1ba1 --- /dev/null +++ b/bsp/fh8620/drivers/dma_mem.c @@ -0,0 +1,122 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +/***************************************************************************** + * Include Section + * add all #include here + *****************************************************************************/ + + +#include "dma_mem.h" +#ifdef RT_USING_DMA_MEM + +/***************************************************************************** + * Define section + * add all #define here + *****************************************************************************/ +//#define FH_TEST_DMA_MEM + +/**************************************************************************** + * ADT section + * add definition of user defined Data Type that only be used in this file here + ***************************************************************************/ + + +/****************************************************************************** + * Function prototype section + * add prototypes for all functions called by this file,execepting those + * declared in header file + *****************************************************************************/ + + +/***************************************************************************** + * Global variables section - Exported + * add declaration of global variables that will be exported here + * e.g. + * int8_t foo; + ****************************************************************************/ + + +/***************************************************************************** + + * static fun; + *****************************************************************************/ + +/***************************************************************************** + * Global variables section - Local + * define global variables(will be refered only in this file) here, + * static keyword should be used to limit scope of local variable to this file + * e.g. + * static uint8_t ufoo; + *****************************************************************************/ + +static struct rt_memheap dma_heap = {0}; + + + /* function body */ +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ +rt_err_t fh_dma_mem_init(rt_uint32_t *mem_start,rt_uint32_t size){ + return rt_memheap_init(&dma_heap,"dma_heap",mem_start,size); +} + + +void *fh_dma_mem_malloc(rt_uint32_t size){ + return rt_memheap_alloc(&dma_heap, size); +} + + +void fh_dma_mem_free(void *ptr){ + rt_memheap_free(ptr); +} + +#ifdef FH_TEST_DMA_MEM +int dma_mem_debug(void *ptr){ + //rt_memheap_free(ptr); + rt_kprintf("dma mem start 0x%08x\n",(rt_uint32_t)dma_heap.start_addr); + rt_kprintf("dma mem total size 0x%08x\n",dma_heap.pool_size); + rt_kprintf("dma mem left size 0x%08x\n",dma_heap.available_size); + rt_kprintf("dma mem max use size 0x%08x\n",dma_heap.max_used_size); + return 0; +} +#endif + +#ifdef RT_USING_FINSH +#include +#ifdef FH_TEST_DMA_MEM +FINSH_FUNCTION_EXPORT(dma_mem_debug, dma_start & left size & max_use); +#endif +#endif + + +#endif + diff --git a/bsp/fh8620/drivers/dma_mem.h b/bsp/fh8620/drivers/dma_mem.h new file mode 100644 index 0000000000000000000000000000000000000000..b431703a4aa6cb3de95d9cb4e4f60da453ba9d79 --- /dev/null +++ b/bsp/fh8620/drivers/dma_mem.h @@ -0,0 +1,76 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef DMA_MEM_H_ +#define DMA_MEM_H_ + + + +#ifndef RT_USING_MEMHEAP +#define RT_USING_MEMHEAP +#endif + +#include +/**************************************************************************** +* #include section +* add #include here if any +***************************************************************************/ + + +/**************************************************************************** +* #define section +* add constant #define here if any +***************************************************************************/ + + + +/**************************************************************************** +* ADT section +* add Abstract Data Type definition here +***************************************************************************/ + + + + +/**************************************************************************** +* extern variable declaration section +***************************************************************************/ + +/**************************************************************************** +* section +* add function prototype here if any +***************************************************************************/ +#ifdef RT_USING_DMA_MEM +rt_err_t fh_dma_mem_init(rt_uint32_t *mem_start,rt_uint32_t size); +void *fh_dma_mem_malloc(rt_uint32_t size); +void fh_dma_mem_free(void *ptr); +/********************************End Of File********************************/ + + +#endif + +#endif + diff --git a/bsp/fh8620/drivers/fh_dma.c b/bsp/fh8620/drivers/fh_dma.c new file mode 100644 index 0000000000000000000000000000000000000000..c50ae1e1df7a37f9b7e7dea32ee0fa3cc7d80736 --- /dev/null +++ b/bsp/fh8620/drivers/fh_dma.c @@ -0,0 +1,1618 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +/***************************************************************************** + * Include Section + * add all #include here + *****************************************************************************/ +//#include "drivers/fh_dma.h" +#include "fh_dma.h" +#include "mmu.h" +#include "drivers/dma.h" +#include +#include +#include +#include "fh_arch.h" +#include "mmu.h" +#include "fh_def.h" +/***************************************************************************** + * Define section + * add all #define here + *****************************************************************************/ +//#define DMA_DEBUG +#ifdef DMA_DEBUG + +#define FH_DMA_DEBUG(fmt, args...) \ + rt_kprintf(fmt,##args); +#else +#define FH_DMA_DEBUG(fmt, args...) +#endif + + +#define DMA_REG_BASE (0xEE000000) +#define DMA_CONTROLLER_NUMBER (1) + + + +#define WORK_QUEUE_STACK_SIZE 512 +#define WORK_QUEUE_PRIORITY 12 + + +#define TEST_PER_NO (10) + +#define DESC_MAX_SIZE (20) +/********************************* + * + * copy from the linux core start + * + *********************************/ +//this is the ip reg offset....don't change!!!!!!! +#define DW_DMA_MAX_NR_CHANNELS 8 + +/* + * Redefine this macro to handle differences between 32- and 64-bit + * addressing, big vs. little endian, etc. + */ +#define DW_REG(name) rt_uint32_t name; rt_uint32_t __pad_##name + +/* Hardware register definitions. */ +struct dw_dma_chan_regs { + DW_REG(SAR); /* Source Address Register */ + DW_REG(DAR); /* Destination Address Register */ + DW_REG(LLP); /* Linked List Pointer */ + rt_uint32_t CTL_LO; /* Control Register Low */ + rt_uint32_t CTL_HI; /* Control Register High */ + DW_REG(SSTAT); + DW_REG(DSTAT); + DW_REG(SSTATAR); + DW_REG(DSTATAR); + rt_uint32_t CFG_LO; /* Configuration Register Low */ + rt_uint32_t CFG_HI; /* Configuration Register High */ + DW_REG(SGR); + DW_REG(DSR); +}; + +struct dw_dma_irq_regs { + DW_REG(XFER); + DW_REG(BLOCK); + DW_REG(SRC_TRAN); + DW_REG(DST_TRAN); + DW_REG(ERROR); +}; + +struct dw_dma_regs { + /* per-channel registers */ + struct dw_dma_chan_regs CHAN[DW_DMA_MAX_NR_CHANNELS]; + + /* irq handling */ + struct dw_dma_irq_regs RAW; /* r */ + struct dw_dma_irq_regs STATUS; /* r (raw & mask) */ + struct dw_dma_irq_regs MASK; /* rw (set = irq enabled) */ + struct dw_dma_irq_regs CLEAR; /* w (ack, affects "raw") */ + + DW_REG(STATUS_INT); /* r */ + + /* software handshaking */ + DW_REG(REQ_SRC); + DW_REG(REQ_DST); + DW_REG(SGL_REQ_SRC); + DW_REG(SGL_REQ_DST); + DW_REG(LAST_SRC); + DW_REG(LAST_DST); + + /* miscellaneous */ + DW_REG(CFG); + DW_REG(CH_EN); + DW_REG(ID); + DW_REG(TEST); + + /* optional encoded params, 0x3c8..0x3 */ +}; + + + + +/* Bitfields in CTL_LO */ +#define DWC_CTLL_INT_EN (1 << 0) /* irqs enabled? */ +#define DWC_CTLL_DST_WIDTH(n) ((n)<<1) /* bytes per element */ +#define DWC_CTLL_SRC_WIDTH(n) ((n)<<4) + +#define DWC_CTLL_DST_INC_MODE(n) ((n)<<7) + +#define DWC_CTLL_DST_INC (0<<7) /* DAR update/not */ +#define DWC_CTLL_DST_DEC (1<<7) +#define DWC_CTLL_DST_FIX (2<<7) + + +#define DWC_CTLL_SRC_INC_MODE(n) ((n)<<9) + + +#define DWC_CTLL_SRC_INC (0<<9) /* SAR update/not */ +#define DWC_CTLL_SRC_DEC (1<<9) +#define DWC_CTLL_SRC_FIX (2<<9) +#define DWC_CTLL_DST_MSIZE(n) ((n)<<11) /* burst, #elements */ +#define DWC_CTLL_SRC_MSIZE(n) ((n)<<14) +#define DWC_CTLL_S_GATH_EN (1 << 17) /* src gather, !FIX */ +#define DWC_CTLL_D_SCAT_EN (1 << 18) /* dst scatter, !FIX */ +#define DWC_CTLL_FC(n) ((n) << 20) +#define DWC_CTLL_FC_M2M (0 << 20) /* mem-to-mem */ +#define DWC_CTLL_FC_M2P (1 << 20) /* mem-to-periph */ +#define DWC_CTLL_FC_P2M (2 << 20) /* periph-to-mem */ +#define DWC_CTLL_FC_P2P (3 << 20) /* periph-to-periph */ +/* plus 4 transfer types for peripheral-as-flow-controller */ +#define DWC_CTLL_DMS(n) ((n)<<23) /* dst master select */ +#define DWC_CTLL_SMS(n) ((n)<<25) /* src master select */ +#define DWC_CTLL_LLP_D_EN (1 << 27) /* dest block chain */ +#define DWC_CTLL_LLP_S_EN (1 << 28) /* src block chain */ + +/* Bitfields in CTL_HI */ +#define DWC_CTLH_DONE 0x00001000 +#define DWC_CTLH_BLOCK_TS_MASK 0x00000fff + +/* Bitfields in CFG_LO. Platform-configurable bits are in */ +#define DWC_CFGL_CH_PRIOR_MASK (0x7 << 5) /* priority mask */ +#define DWC_CFGL_CH_PRIOR(x) ((x) << 5) /* priority */ +#define DWC_CFGL_CH_SUSP (1 << 8) /* pause xfer */ +#define DWC_CFGL_FIFO_EMPTY (1 << 9) /* pause xfer */ + + +#define DWC_CFGL_HS_DST (1 << 10) /* handshake w/dst */ +#define DWC_CFGL_HS_SRC (1 << 11) /* handshake w/src */ +#define DWC_CFGL_MAX_BURST(x) ((x) << 20) +#define DWC_CFGL_RELOAD_SAR (1 << 30) +#define DWC_CFGL_RELOAD_DAR (1 << 31) + +/* Bitfields in CFG_HI. Platform-configurable bits are in */ +#define DWC_CFGH_DS_UPD_EN (1 << 5) +#define DWC_CFGH_SS_UPD_EN (1 << 6) + +/* Bitfields in SGR */ +#define DWC_SGR_SGI(x) ((x) << 0) +#define DWC_SGR_SGC(x) ((x) << 20) + +/* Bitfields in DSR */ +#define DWC_DSR_DSI(x) ((x) << 0) +#define DWC_DSR_DSC(x) ((x) << 20) + +/* Bitfields in CFG */ +#define DW_CFG_DMA_EN (1 << 0) + +#define DW_REGLEN 0x400 + + +/* Platform-configurable bits in CFG_HI */ +#define DWC_CFGH_FCMODE (1 << 0) +#define DWC_CFGH_FIFO_MODE (1 << 1) +#define DWC_CFGH_PROTCTL(x) ((x) << 2) +#define DWC_CFGH_SRC_PER(x) ((x) << 7) +#define DWC_CFGH_DST_PER(x) ((x) << 11) + +/* Platform-configurable bits in CFG_LO */ +#define DWC_CFGL_LOCK_CH_XFER (0 << 12) /* scope of LOCK_CH */ +#define DWC_CFGL_LOCK_CH_BLOCK (1 << 12) +#define DWC_CFGL_LOCK_CH_XACT (2 << 12) +#define DWC_CFGL_LOCK_BUS_XFER (0 << 14) /* scope of LOCK_BUS */ +#define DWC_CFGL_LOCK_BUS_BLOCK (1 << 14) +#define DWC_CFGL_LOCK_BUS_XACT (2 << 14) +#define DWC_CFGL_LOCK_CH (1 << 15) /* channel lockout */ +#define DWC_CFGL_LOCK_BUS (1 << 16) /* busmaster lockout */ +#define DWC_CFGL_HS_DST_POL (1 << 18) /* dst handshake active low */ +#define DWC_CFGL_HS_SRC_POL (1 << 19) /* src handshake active low */ + + + + +#define lift_shift_bit_num(bit_num) (1<(b))?(a):(b)) + + + + +#define dw_readl(dw, name) \ + __raw_readl(&(((struct dw_dma_regs *)dw->regs)->name)) +#define dw_writel(dw, name, val) \ + __raw_writel((val), &(((struct dw_dma_regs *)dw->regs)->name)) +#define dw_readw(dw, name) \ + __raw_readw(&(((struct dw_dma_regs *)dw->regs)->name)) +#define dw_writew(dw, name, val) \ + __raw_writew((val), &(((struct dw_dma_regs *)dw->regs)->name)) + + + + + + +#define CHANNEL0 (lift_shift_bit_num(0)) +#define CHANNEL1 (lift_shift_bit_num(1)) +#define CHANNEL2 (lift_shift_bit_num(2)) +#define CHANNEL3 (lift_shift_bit_num(3)) + +#define channel_set_bit(dw, reg, mask) \ + dw_writel(dw, reg, ((mask) << 8) | (mask)) +#define channel_clear_bit(dw, reg, mask) \ + dw_writel(dw, reg, ((mask) << 8) | 0) + + + + +/**************************************************************************** + * ADT section + * add definition of user defined Data Type that only be used in this file here + ***************************************************************************/ + +struct dw_dma{ + //vadd + void *regs; + //padd + rt_uint32_t paddr; + rt_uint32_t irq; + rt_uint32_t channel_max_number; + +#define CONTROLLER_STATUS_CLOSED (0) +#define CONTROLLER_STATUS_OPEN (1) + rt_uint32_t controller_status; +#define FH81_DMA_INIT_NOT_YET (0) +#define FH81_DMA_INIT_ALREADY (1) + rt_uint32_t init; + rt_uint32_t id; + char *name; + rt_uint32_t channel_work_done; +}; + + +struct dma_channel { +#define CHANNEL_STATUS_CLOSED (0) +#define CHANNEL_STATUS_OPEN (1) +#define CHANNEL_STATUS_IDLE (2) +#define CHANNEL_STATUS_BUSY (3) + + rt_uint32_t channel_status; //open, busy ,closed + rt_uint32_t desc_trans_size; + + //isr will set it complete. + struct rt_completion transfer_completion; + //add lock,when set the channel.lock it + struct rt_semaphore channel_lock; + //struct rt_mutex lock; + //rt_enter_critical(); + rt_list_t queue; + //active transfer now!!! + struct dma_transfer *active_trans; + +#define SINGLE_TRANSFER (0) +#define CYCLIC_TRANSFER (1) +#define DEFAULT_TRANSFER SINGLE_TRANSFER + rt_uint32_t open_flag; + // + + + + //new add para... + rt_uint32_t desc_total_no; + rt_uint32_t free_index; + rt_uint32_t used_index; + rt_uint32_t desc_left_cnt; + + rt_uint32_t allign_malloc; + struct dw_lli *base_lli; +}; + + +struct fh81_dma{ + //core use ,this must be the first para!!!! + struct rt_dma_device parent; + //myown + struct dw_dma dwc; + //channel obj + struct dma_channel dma_channel[FH81_MAX_CHANNEL]; + + //struct rt_workqueue* isr_workqueue; + //struct rt_work *isr_work; + +}; + + + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = rt_list_entry((head)->next, typeof(*pos), member), \ + n = rt_list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = rt_list_entry(n->member.next, typeof(*n), member)) + + +/****************************************************************************** + * Function prototype section + * add prototypes for all functions called by this file,execepting those + * declared in header file + *****************************************************************************/ + + +/***************************************************************************** + * Global variables section - Exported + * add declaration of global variables that will be exported here + * e.g. + * int8_t foo; + ****************************************************************************/ + + +/***************************************************************************** + + * static fun; + *****************************************************************************/ +static rt_err_t init (struct rt_dma_device *dma); +static rt_err_t control (struct rt_dma_device *dma, int cmd, void *arg); + + +static void rt_fh_dma_cyclic_stop(struct dma_transfer *p); +static void rt_fh_dma_cyclic_start(struct dma_transfer *p); +static void rt_fh_dma_cyclic_prep(struct fh81_dma * fh81_dma_p,struct dma_transfer *p); +static void rt_fh_dma_cyclic_free(struct dma_transfer *p); + +static struct rt_dma_ops fh81_dma_ops = +{ + init, + control +}; + + + + +/***************************************************************************** + * Global variables section - Local + * define global variables(will be refered only in this file) here, + * static keyword should be used to limit scope of local variable to this file + * e.g. + * static uint8_t ufoo; + *****************************************************************************/ +static struct fh81_dma fh81_dma_controller[DMA_CONTROLLER_NUMBER] = {0}; + +/* function body */ +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + +static rt_uint32_t allign_func(rt_uint32_t in_addr,rt_uint32_t allign_size){ + return (in_addr + allign_size-1) & (~(allign_size - 1)); +} + + +struct dw_lli * get_desc(struct fh81_dma *p_dma,struct dma_transfer *p_transfer,rt_uint32_t lli_size){ + struct dw_lli * ret_lli; + rt_uint32_t free_index; + rt_uint32_t allign_left; + rt_uint32_t totoal_desc; + rt_uint32_t actual_get_desc; + rt_uint32_t totoal_free_desc; + totoal_free_desc = p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt; + free_index = p_dma->dma_channel[p_transfer->channel_number].free_index; + totoal_desc = p_dma->dma_channel[p_transfer->channel_number].desc_total_no; + allign_left = totoal_desc - free_index; + + //check first.. + if(totoal_free_desc < lli_size){ + rt_kprintf("not enough desc to get...\n"); + rt_kprintf("get size is %d,left is %d\n",lli_size,totoal_free_desc); + return RT_NULL; + } + //rt_kprintf("get desc in...\n"); + + //rt_kprintf("lli size is %d\n",lli_size); + if(lli_size > allign_left){ + //if allign desc not enough...just reset null.... + if((totoal_free_desc - allign_left) < lli_size){ + rt_kprintf("not enough desc to get...\n"); + rt_kprintf("app need size is %d, totoal left is %d, allign left is %d\n",lli_size,totoal_free_desc,allign_left); + rt_kprintf("from head to get desc size is %d, actual get is %d\n",(totoal_free_desc - allign_left),(allign_left +lli_size)); + return RT_NULL; + } + else{ + actual_get_desc = allign_left +lli_size; + free_index = 0; + } + } + + + //ret_lli = &p_dma->dma_channel[p_transfer->channel_number].base_lli[free_index]; + + ret_lli = &p_dma->dma_channel[p_transfer->channel_number].base_lli[free_index]; +// rt_kprintf("get desc base index addr:%08x\n",(rt_uint32_t)&p_dma->dma_channel[p_transfer->channel_number].base_lli[0]); +// rt_kprintf("get desc free index addr:%08x\n",(rt_uint32_t)ret_lli); +// rt_kprintf("get desc request size:%08x\n",lli_size); +// rt_kprintf("get desc total size:%08x\n",p_dma->dma_channel[p_transfer->channel_number].desc_total_no); +// rt_kprintf("one desc size is:%08x\n",sizeof( struct dw_lli)); + + p_dma->dma_channel[p_transfer->channel_number].free_index += actual_get_desc; + + //rt_kprintf("get desc free index addr:%08x\n",(rt_uint32_t)&p_dma->dma_channel[p_transfer->channel_number].base_lli[p_dma->dma_channel[p_transfer->channel_number].free_index]); + + p_dma->dma_channel[p_transfer->channel_number].free_index %= p_dma->dma_channel[p_transfer->channel_number].desc_total_no; + p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt -= actual_get_desc; + p_transfer->lli_size = lli_size; + p_transfer->actual_lli_size = actual_get_desc; + return ret_lli; +} + + +rt_uint32_t put_desc(struct fh81_dma *p_dma,struct dma_transfer *p_transfer){ + struct dw_lli * ret_lli; + rt_uint32_t used_index; + rt_uint32_t lli_size; + //rt_kprintf("put desc in...\n"); + used_index = p_dma->dma_channel[p_transfer->channel_number].used_index; + lli_size = p_transfer->actual_lli_size; + p_dma->dma_channel[p_transfer->channel_number].used_index += lli_size; + p_dma->dma_channel[p_transfer->channel_number].used_index %= p_dma->dma_channel[p_transfer->channel_number].desc_total_no; + p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt += lli_size; + p_transfer->lli_size = 0; + p_transfer->actual_lli_size = 0; + return 0; +} + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + +static rt_err_t init (struct rt_dma_device *dma){ + + + //init the clk table + + + struct fh81_dma *my_own = (struct fh81_dma *)dma->parent.user_data; + + FH_DMA_DEBUG("my_own value:0x%x\n",(rt_uint32_t)my_own); + + //check the user data + RT_ASSERT(my_own != RT_NULL); + + return RT_EOK; + +} + + + + + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + +static void handle_dma_open(struct fh81_dma *p_dma){ + + rt_uint32_t i; + struct dw_dma *temp_dwc; + temp_dwc = &p_dma->dwc; + + dw_writel(temp_dwc, CFG, 1); + p_dma->dwc.controller_status = CONTROLLER_STATUS_OPEN; + +} + + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ +static void handle_dma_close(struct fh81_dma *p_dma){ + + + rt_uint32_t i; + struct dw_dma *temp_dwc; + temp_dwc = &p_dma->dwc; + + //take lock + for(i=0;idwc.channel_max_number;i++){ + rt_sem_take(&p_dma->dma_channel[i].channel_lock, RT_WAITING_FOREVER); + + channel_clear_bit(temp_dwc, CH_EN, lift_shift_bit_num(i)); + p_dma->dma_channel[i].channel_status = CHANNEL_STATUS_CLOSED; + } + dw_writel(temp_dwc, CFG, 0); + p_dma->dwc.controller_status = CONTROLLER_STATUS_CLOSED; + + //release lock + for(i=0;idwc.channel_max_number;i++){ + rt_sem_release(&p_dma->dma_channel[i].channel_lock); + } + + //destroy the workqueue.. + //rt_workqueue_destroy(p_dma->isr_workqueue); + + +} + + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + +#define CHANNEL_REAL_FREE (0) +#define CHANNEL_NOT_FREE (1) + +static rt_uint32_t check_channel_real_free(struct fh81_dma *p_dma,rt_uint32_t channel_number){ + + + struct dw_dma *temp_dwc; + temp_dwc = &p_dma->dwc; + rt_uint32_t ret_status; + + + RT_ASSERT(channel_number < p_dma->dwc.channel_max_number); + + ret_status = dw_readl(temp_dwc, CH_EN); + if(ret_status & lift_shift_bit_num(channel_number)){ + //the channel is still busy!!!error here + //FH_DMA_DEBUG("auto request channel error\n"); + return CHANNEL_NOT_FREE; + } + return CHANNEL_REAL_FREE; + +} + + + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + +static rt_err_t handle_request_channel(struct fh81_dma *p_dma,struct dma_transfer *p_transfer){ + + rt_uint32_t i; + struct dw_dma *temp_dwc; + temp_dwc = &p_dma->dwc; + rt_err_t ret_status = RT_EOK; + + //handle if auto check channel... + if(p_transfer->channel_number == AUTO_FIND_CHANNEL){ + //check each channel lock,find a free channel... + for(i=0;idwc.channel_max_number;i++){ + ret_status = rt_sem_trytake(&p_dma->dma_channel[i].channel_lock); + if(ret_status == RT_EOK){ + break; + } + } + + if(i < p_dma->dwc.channel_max_number){ + ret_status = check_channel_real_free(p_dma,i); + if(ret_status!= CHANNEL_REAL_FREE){ + FH_DMA_DEBUG("auto request channel error\n"); + RT_ASSERT(ret_status == CHANNEL_REAL_FREE); + } + //caution : channel is already locked here.... + p_transfer->channel_number = i; + //bind to the controller. + //p_transfer->dma_controller = p_dma; + p_dma->dma_channel[i].channel_status = CHANNEL_STATUS_OPEN; + } + else + return -RT_ENOMEM; + + } + + // request channel by user + else{ + // + + RT_ASSERT(p_transfer->channel_number < p_dma->dwc.channel_max_number); + ret_status = rt_sem_take(&p_dma->dma_channel[p_transfer->channel_number].channel_lock, RT_TICK_PER_SECOND*50); + if(ret_status != RT_EOK) + return -RT_ENOMEM; + //rt_enter_critical(); + ret_status = check_channel_real_free(p_dma,p_transfer->channel_number); + if(ret_status!= CHANNEL_REAL_FREE){ + FH_DMA_DEBUG("user request channel error\n"); + RT_ASSERT(ret_status == CHANNEL_REAL_FREE); + } + + //bind to the controller + //p_transfer->dma_controller = p_dma; + p_dma->dma_channel[p_transfer->channel_number].channel_status = CHANNEL_STATUS_OPEN; + //rt_exit_critical(); + } + + + //malloc desc for this one channel... + //fix me.... + + p_dma->dma_channel[p_transfer->channel_number].allign_malloc = (rt_uint32_t) rt_malloc( + (p_dma->dma_channel[p_transfer->channel_number].desc_total_no + * sizeof(struct dw_lli)) + CACHE_LINE_SIZE); + + + if(!p_dma->dma_channel[p_transfer->channel_number].allign_malloc){ + //release channel + rt_kprintf("[dma]: no mem to malloc channel%d desc..\n",p_transfer->channel_number); + p_dma->dma_channel[p_transfer->channel_number].channel_status = CHANNEL_STATUS_CLOSED; + rt_sem_release(&p_dma->dma_channel[p_transfer->channel_number].channel_lock); + return -RT_ENOMEM; + } + + + p_dma->dma_channel[p_transfer->channel_number].base_lli = + (struct dw_lli *) allign_func( + p_dma->dma_channel[p_transfer->channel_number].allign_malloc, + CACHE_LINE_SIZE); + + FH_DMA_DEBUG("dma desc addr is %x\n",(rt_uint32_t)p_dma->dma_channel[p_transfer->channel_number].base_lli); + //t1 = (UINT32)rt_malloc(GMAC_TX_RING_SIZE * sizeof(Gmac_Tx_DMA_Descriptors) + CACHE_LINE_SIZE); + + + if(!p_dma->dma_channel[p_transfer->channel_number].base_lli){ + FH_DMA_DEBUG("request desc failed..\n"); + RT_ASSERT(p_dma->dma_channel[p_transfer->channel_number].base_lli != RT_NULL); + } + + if((rt_uint32_t)p_dma->dma_channel[p_transfer->channel_number].base_lli % 32){ + rt_kprintf("malloc is not cache allign.."); + + } + + + + //rt_memset((void *)dma_trans_desc->first_lli, 0, lli_size * sizeof(struct dw_lli)); + rt_memset((void *) p_dma->dma_channel[p_transfer->channel_number].base_lli, + 0, + p_dma->dma_channel[p_transfer->channel_number].desc_total_no + * sizeof(struct dw_lli)); + + p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt = p_dma->dma_channel[p_transfer->channel_number].desc_total_no; + p_dma->dma_channel[p_transfer->channel_number].free_index = 0; + p_dma->dma_channel[p_transfer->channel_number].used_index = 0; + + + return RT_EOK; + +} + + + + + + + + + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + +static rt_uint32_t handle_release_channel(struct fh81_dma *p_dma,struct dma_transfer *p_transfer){ + + + rt_uint32_t i; + struct dw_dma *temp_dwc; + temp_dwc = &p_dma->dwc; + rt_uint32_t ret_status; + + //rt_enter_critical(); + ret_status = p_dma->dma_channel[p_transfer->channel_number].channel_status; + + + RT_ASSERT(p_transfer->channel_number < p_dma->dwc.channel_max_number); + + + if(ret_status == CHANNEL_STATUS_CLOSED){ + FH_DMA_DEBUG("release channel error,reason: release a closed channel!!\n"); + RT_ASSERT(ret_status != CHANNEL_STATUS_CLOSED); + } + + channel_clear_bit(temp_dwc, CH_EN, lift_shift_bit_num(p_transfer->channel_number)); + rt_sem_release(&p_dma->dma_channel[p_transfer->channel_number].channel_lock); + //p_transfer->dma_controller = RT_NULL; + p_dma->dma_channel[p_transfer->channel_number].channel_status = CHANNEL_STATUS_CLOSED; + p_dma->dma_channel[p_transfer->channel_number].open_flag = DEFAULT_TRANSFER; + //rt_exit_critical(); + + //release this channel malloc mem... + //fix me..... + rt_free((void *)p_dma->dma_channel[p_transfer->channel_number].allign_malloc); + p_dma->dma_channel[p_transfer->channel_number].allign_malloc = RT_NULL; + p_dma->dma_channel[p_transfer->channel_number].base_lli = RT_NULL; + p_dma->dma_channel[p_transfer->channel_number].desc_left_cnt = p_dma->dma_channel[p_transfer->channel_number].desc_total_no; + p_dma->dma_channel[p_transfer->channel_number].free_index = 0; + p_dma->dma_channel[p_transfer->channel_number].used_index = 0; + + return RT_EOK; + + +} + + + +static rt_uint32_t cal_lli_size(struct dma_transfer *p_transfer){ + RT_ASSERT(p_transfer != RT_NULL); + RT_ASSERT(p_transfer->dma_controller != RT_NULL); + RT_ASSERT(p_transfer->src_width <= DW_DMA_SLAVE_WIDTH_32BIT); + rt_uint32_t lli_number = 0; + rt_uint32_t channel_max_trans_per_lli = 0; + channel_max_trans_per_lli = p_transfer->dma_controller->dma_channel[p_transfer->channel_number].desc_trans_size; + + + lli_number = (p_transfer->trans_len % channel_max_trans_per_lli) ? 1:0; + lli_number += p_transfer->trans_len / channel_max_trans_per_lli; + + return lli_number; + +} + + +static void dump_lli(struct dw_lli *p_lli){ + FH_DMA_DEBUG("link_mem padd:0x%x\n sar:0x%x\n dar:0x%x\n llp:0x%x\n ctllo:0x%x\n ctlhi:0x%x\n sstat:0x%x\n dstat:0x%x\n", + (rt_uint32_t)p_lli,p_lli->sar, p_lli->dar, p_lli->llp, + p_lli->ctllo, p_lli->ctlhi,p_lli->sstat,p_lli->dstat); +} +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ +static void handle_single_transfer(struct fh81_dma *p_dma,struct dma_transfer *p_transfer){ + + + rt_uint32_t i; + struct dw_dma *temp_dwc; + temp_dwc = &p_dma->dwc; + volatile rt_uint32_t ret_status; + rt_list_t *p_controller_list; + rt_uint32_t lli_size,max_trans_size; + struct dw_lli *p_lli = RT_NULL; + struct dma_transfer *dma_trans_desc; + struct dma_transfer *_dma_trans_desc; + + + rt_uint32_t temp_src_add; + rt_uint32_t temp_dst_add; + rt_uint32_t trans_total_len = 0; + rt_uint32_t temp_trans_size = 0; + //rt_uint32_t dma_channl_no = 0; + + RT_ASSERT(p_transfer->channel_number < p_dma->dwc.channel_max_number); + RT_ASSERT(p_transfer->dma_number < DMA_CONTROLLER_NUMBER); + RT_ASSERT(&fh81_dma_controller[p_transfer->dma_number] == p_dma); + //when the dma transfer....the lock should be 0!!!! + //or user may not request the channel... + RT_ASSERT(p_dma->dma_channel[p_transfer->channel_number].channel_lock.value == 0); + + + ret_status = p_dma->dma_channel[p_transfer->channel_number].channel_status; + if(ret_status == CHANNEL_STATUS_CLOSED){ + FH_DMA_DEBUG("transfer error,reason: use a closed channel..\n"); + RT_ASSERT(ret_status != CHANNEL_STATUS_CLOSED); + } + p_transfer->dma_controller = p_dma; + + + + rt_list_init(&p_transfer->transfer_list); + max_trans_size = p_transfer->dma_controller->dma_channel[p_transfer->channel_number].desc_trans_size; + //add transfer to the controller's queue list + //here should insert before and handle after....this could be a fifo... + rt_list_insert_before(&p_dma->dma_channel[p_transfer->channel_number].queue , &p_transfer->transfer_list); + + + p_controller_list = &p_dma->dma_channel[p_transfer->channel_number].queue; + + + //here the driver could make a queue to cache the transfer and kick a thread to handle the queue~~~ + //but now,this is a easy version...,just handle the transfer now!!! + list_for_each_entry_safe(dma_trans_desc, _dma_trans_desc, p_controller_list, transfer_list) { + + //the dma controller could see the active transfer ..... + p_transfer->dma_controller->dma_channel[p_transfer->channel_number].active_trans = dma_trans_desc; + + + trans_total_len = p_transfer->trans_len; + + + //handle desc + //step1:cal lli size... + lli_size = cal_lli_size(dma_trans_desc); + //step2:malloc lli_size mem + //dma_trans_desc->first_lli = (struct dw_lli *)rt_malloc(lli_size * sizeof(struct dw_lli)); + + dma_trans_desc->first_lli = get_desc(p_dma,p_transfer,lli_size); + + //not enough mem.. + if(dma_trans_desc->first_lli == RT_NULL){ + + FH_DMA_DEBUG("transfer error,reason: not enough mem..\n"); + RT_ASSERT(dma_trans_desc->first_lli != RT_NULL); + } + + + //bug here.... + rt_memset((void *)dma_trans_desc->first_lli, 0, lli_size * sizeof(struct dw_lli)); + + p_lli = dma_trans_desc->first_lli; + + //warnning!!!!must check if the add is 32bits ally... + RT_ASSERT(((rt_uint32_t)p_lli & 0x03) == 0); + + RT_ASSERT(dma_trans_desc->dst_inc_mode <=DW_DMA_SLAVE_FIX); + RT_ASSERT(dma_trans_desc->src_inc_mode <=DW_DMA_SLAVE_FIX); + //step3: set the mem.. + for(i=0;idst_inc_mode){ + case DW_DMA_SLAVE_INC: + temp_dst_add = dma_trans_desc->dst_add + i * max_trans_size * (1<dst_width); + break; + case DW_DMA_SLAVE_DEC: + temp_dst_add = dma_trans_desc->dst_add - i * max_trans_size * (1<dst_width); + break; + case DW_DMA_SLAVE_FIX: + temp_dst_add = dma_trans_desc->dst_add; + break; + + } + + + switch(dma_trans_desc->src_inc_mode){ + case DW_DMA_SLAVE_INC: + temp_src_add = dma_trans_desc->src_add + i * max_trans_size * (1<src_width); + break; + case DW_DMA_SLAVE_DEC: + temp_src_add = dma_trans_desc->src_add - i * max_trans_size * (1<src_width); + break; + case DW_DMA_SLAVE_FIX: + temp_src_add = dma_trans_desc->src_add ; + break; + + } + + + p_lli[i].sar = temp_src_add; + p_lli[i].dar = temp_dst_add; + + //para ctl + temp_trans_size = (trans_total_len / max_trans_size)? max_trans_size : (trans_total_len % max_trans_size); + trans_total_len -= temp_trans_size; + + RT_ASSERT(dma_trans_desc->dst_width <=DW_DMA_SLAVE_WIDTH_32BIT); + RT_ASSERT(dma_trans_desc->src_width <=DW_DMA_SLAVE_WIDTH_32BIT); + + RT_ASSERT(dma_trans_desc->dst_msize <=DW_DMA_SLAVE_MSIZE_256); + RT_ASSERT(dma_trans_desc->src_msize <=DW_DMA_SLAVE_MSIZE_256); + RT_ASSERT(dma_trans_desc->fc_mode <=DMA_P2P); + + + + p_lli[i].ctllo = DWC_CTLL_INT_EN|DWC_CTLL_DST_WIDTH(dma_trans_desc->dst_width)|DWC_CTLL_SRC_WIDTH(dma_trans_desc->src_width) + |DWC_CTLL_DST_INC_MODE(dma_trans_desc->dst_inc_mode)|DWC_CTLL_SRC_INC_MODE(dma_trans_desc->src_inc_mode) + |DWC_CTLL_DST_MSIZE(dma_trans_desc->dst_msize)|DWC_CTLL_SRC_MSIZE(dma_trans_desc->src_msize)|DWC_CTLL_FC(dma_trans_desc->fc_mode) + |DWC_CTLL_DMS(0)|DWC_CTLL_SMS(0); + //block size + p_lli[i].ctlhi = temp_trans_size; + + + if(trans_total_len > 0){ + p_lli[i].llp = (rt_uint32_t)&p_lli[i+1]; + p_lli[i].ctllo |= DWC_CTLL_LLP_D_EN|DWC_CTLL_LLP_S_EN; + } + + //flush cache to mem + mmu_clean_invalidated_dcache((rt_uint32_t)&p_lli[i],sizeof(struct dw_lli)); + + dump_lli(&p_lli[i]); + } + + //clear the isr status + + + + + //set the dma config reg + //clear cfg reload reg + //ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); + //ret_status &= ~(DWC_CFGL_RELOAD_SAR|DWC_CFGL_RELOAD_DAR); + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,0); + + //set the first link add + //ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].LLP); + ret_status = 0; + ret_status = (rt_uint32_t)&p_lli[0]; + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].LLP,ret_status); + + //set link enable + //ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CTL_LO); + ret_status = 0; + ret_status =DWC_CTLL_LLP_D_EN|DWC_CTLL_LLP_S_EN; + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CTL_LO,ret_status); + + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CTL_HI,0); + //set handshaking + + RT_ASSERT(dma_trans_desc->dst_hs <= DMA_SW_HANDSHAKING); + RT_ASSERT(dma_trans_desc->src_hs <= DMA_SW_HANDSHAKING); + + if(dma_trans_desc->dst_hs == DMA_SW_HANDSHAKING){ + ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); + ret_status |= DWC_CFGL_HS_DST; + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); + } + else{ + ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); + ret_status &= ~DWC_CFGL_HS_DST; + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); + } + + if(dma_trans_desc->src_hs == DMA_SW_HANDSHAKING){ + ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); + ret_status |= DWC_CFGL_HS_SRC; + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); + } + else{ + ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); + ret_status &= ~DWC_CFGL_HS_SRC; + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); + } + + + //only hw handshaking need this.. + switch(dma_trans_desc->fc_mode){ + case DMA_M2M: + break; + case DMA_M2P: + //set dst per... + RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END); + + ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); + + //clear 43 ~ 46 bit + ret_status &= ~0x7800; + + ret_status |= DWC_CFGH_DST_PER(dma_trans_desc->dst_per); + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); + //DWC_CFGH_SRC_PER + + + break; + case DMA_P2M: + //set src per... + RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END); + + + ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); + + //clear 39 ~ 42 bit + ret_status &= ~0x780; + + + ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per); + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); + + break; + case DMA_P2P: + //set src and dst.. + RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END); + RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END); + + ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); + ret_status &= ~0x7800; + ret_status &= ~0x780; + ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per) | DWC_CFGH_DST_PER(dma_trans_desc->dst_per); + dw_writel(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); + + break; + default: + break; + } + + + dma_trans_desc->dma_controller->dma_channel[dma_trans_desc->channel_number].channel_status = CHANNEL_STATUS_BUSY; + //enable isr... + channel_set_bit(temp_dwc, MASK.XFER, lift_shift_bit_num(dma_trans_desc->channel_number)); + channel_set_bit(temp_dwc, MASK.ERROR, lift_shift_bit_num(dma_trans_desc->channel_number)); + //close + channel_clear_bit(temp_dwc, MASK.BLOCK, lift_shift_bit_num(dma_trans_desc->channel_number)); + + dw_writel(temp_dwc, CLEAR.XFER, 1<<(dma_trans_desc->channel_number)); + dw_writel(temp_dwc, CLEAR.BLOCK, 1<<(dma_trans_desc->channel_number)); + dw_writel(temp_dwc, CLEAR.SRC_TRAN, 1<<(dma_trans_desc->channel_number)); + dw_writel(temp_dwc, CLEAR.DST_TRAN, 1<<(dma_trans_desc->channel_number)); + dw_writel(temp_dwc, CLEAR.ERROR, 1<<(dma_trans_desc->channel_number)); + + + ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); + FH_DMA_DEBUG("cfg_hi value:0x%x\n",ret_status); + + ret_status = dw_readl(temp_dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); + FH_DMA_DEBUG("cfg_low value:0x%x\n",ret_status); + + + ret_status = dw_readl(temp_dwc, MASK.BLOCK); + FH_DMA_DEBUG("mask block value:0x%x\n",ret_status); + + ret_status = dw_readl(temp_dwc, MASK.XFER); + FH_DMA_DEBUG("mask xfer value:0x%x\n",ret_status); + + + + if(dma_trans_desc->prepare_callback){ + dma_trans_desc->prepare_callback(dma_trans_desc->prepare_para); + } + //enable the channle to transfer + channel_set_bit(temp_dwc, CH_EN, lift_shift_bit_num(dma_trans_desc->channel_number)); + + + + } + +} + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ +static rt_err_t control (struct rt_dma_device *dma, int cmd, void *arg){ + + + struct fh81_dma *my_own = (struct fh81_dma *)dma->parent.user_data; + rt_uint32_t i; + struct dw_dma *dwc; + dwc = &my_own->dwc; + + rt_err_t ret = RT_EOK; + + struct dma_transfer *p_dma_transfer = (struct dma_transfer *)arg; + + //FH_DMA_DEBUG("p_dma_transfer value:0x%x\n",(rt_uint32_t)p_dma_transfer); + + + RT_ASSERT(my_own != RT_NULL); + RT_ASSERT(dwc != RT_NULL); + + + + switch(cmd){ + case RT_DEVICE_CTRL_DMA_OPEN: + + //open the controller.. + handle_dma_open(my_own); + break; + case RT_DEVICE_CTRL_DMA_CLOSE: + + //close the controller.. + handle_dma_close(my_own); + break; + case RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL: + //request a channel for the user + RT_ASSERT(p_dma_transfer != RT_NULL); + ret = handle_request_channel(my_own,p_dma_transfer); + + break; + case RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL: + //release a channel + RT_ASSERT(p_dma_transfer != RT_NULL); + + ret = handle_release_channel(my_own,p_dma_transfer); + + + break; + + case RT_DEVICE_CTRL_DMA_SINGLE_TRANSFER: + //make a channel to transfer data. + RT_ASSERT(p_dma_transfer != RT_NULL); + //check if the dma channel is open,or return error. + + my_own->dma_channel[p_dma_transfer->channel_number].open_flag = SINGLE_TRANSFER; + handle_single_transfer(my_own,p_dma_transfer); + //then wait for the channel is complete.. + //caution that::we should be in the "rt_enter_critical()"when set the dma to work. + break; + + + case RT_DEVICE_CTRL_DMA_CYCLIC_PREPARE: + RT_ASSERT(p_dma_transfer != RT_NULL); + my_own->dma_channel[p_dma_transfer->channel_number].open_flag = CYCLIC_TRANSFER; + rt_fh_dma_cyclic_prep(my_own,p_dma_transfer); + break; + + case RT_DEVICE_CTRL_DMA_CYCLIC_START: + rt_fh_dma_cyclic_start(p_dma_transfer); + break; + + case RT_DEVICE_CTRL_DMA_CYCLIC_STOP: + rt_fh_dma_cyclic_stop(p_dma_transfer); + break; + + case RT_DEVICE_CTRL_DMA_CYCLIC_FREE: + rt_fh_dma_cyclic_free(p_dma_transfer); + break; + + default: + break; + + } + + return ret; + + +} + + + +static void rt_fh81_dma_isr(int irq, void *param) +{ + + + RT_ASSERT(irq == DMAC_IRQn); + rt_uint32_t isr_channel_x,i,error,isr_channel_b; + struct fh81_dma *my_own = (struct fh81_dma *)param; + struct dw_dma *dwc; + struct dma_transfer *p_transfer; + dwc = &my_own->dwc; + //p_transfer = + //rt_kprintf("dma isr get in~~~\n"); + error = dw_readl(dwc,STATUS.ERROR); + if(error != 0){ + FH_DMA_DEBUG("dma isr error!!!!\n"); + RT_ASSERT(error == RT_NULL); + } + + isr_channel_x = dw_readl(dwc,STATUS.XFER); + isr_channel_b = dw_readl(dwc,STATUS.BLOCK); + //for single check the transfer status + //check which channel... + + for(i=0;idwc.channel_max_number;i++){ + + if(my_own->dma_channel[i].open_flag == SINGLE_TRANSFER){ + if(isr_channel_x & 1<dma_channel[i].active_trans; + + if(p_transfer->complete_callback){ + p_transfer->complete_callback(p_transfer->complete_para); + } + p_transfer->dma_controller->dma_channel[p_transfer->channel_number].channel_status = CHANNEL_STATUS_IDLE; + //here is a bug...do not free here + //rt_free(p_transfer->first_lli); + put_desc(my_own,p_transfer); + rt_list_remove(&p_transfer->transfer_list); + } + + } + + else if(my_own->dma_channel[i].open_flag == CYCLIC_TRANSFER){ + if(isr_channel_b & 1<dma_channel[i].active_trans; + dw_writel(dwc, CLEAR.BLOCK, 1<<(p_transfer->channel_number)); + if(p_transfer->complete_callback){ + p_transfer->complete_callback(p_transfer->complete_para); + } + } + } + } + + +} + + + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + +const char *channel_lock_name[FH81_MAX_CHANNEL] = { + "channel_0_lock", + "channel_1_lock", + "channel_2_lock", + "channel_3_lock", +}; + +rt_err_t fh81_dma_register(struct fh81_dma * fh81_dma_p, + char * dma_name){ + + rt_uint32_t i; + + RT_ASSERT(fh81_dma_p != RT_NULL); + RT_ASSERT(dma_name != RT_NULL); + //RT_ASSERT(fh81_dma_p->dwc.init != FH81_DMA_INIT_ALREADY); + + + if(fh81_dma_p->dwc.init == FH81_DMA_INIT_ALREADY) + return 0; + + struct rt_dma_device *rt_dma; + rt_dma = &fh81_dma_p->parent; + rt_dma->ops = &fh81_dma_ops; + + + //soc para set + fh81_dma_p->dwc.name = dma_name; + fh81_dma_p->dwc.regs =(void *)DMA_REG_BASE; + fh81_dma_p->dwc.paddr = DMA_REG_BASE; + fh81_dma_p->dwc.irq = DMAC_IRQn; + fh81_dma_p->dwc.channel_max_number = FH81_MAX_CHANNEL; + fh81_dma_p->dwc.controller_status = CONTROLLER_STATUS_CLOSED; + fh81_dma_p->dwc.init = FH81_DMA_INIT_ALREADY; + fh81_dma_p->dwc.id = 0; + //channel set + for(i=0;idma_channel[i].channel_status = CHANNEL_STATUS_CLOSED; + fh81_dma_p->dma_channel[i].desc_total_no = DESC_MAX_SIZE; + //rt_completion_init(&(fh81_dma_p->dma_channel[i].transfer_completion)); + rt_list_init(&(fh81_dma_p->dma_channel[i].queue)); + fh81_dma_p->dma_channel[i].desc_trans_size = FH81_CHANNEL_MAX_TRANSFER_SIZE; + rt_sem_init(&fh81_dma_p->dma_channel[i].channel_lock, channel_lock_name[i], 1, RT_IPC_FLAG_FIFO); + } + + //isr + rt_hw_interrupt_install(fh81_dma_p->dwc.irq, rt_fh81_dma_isr, + (void *)fh81_dma_p, "dma_isr"); + rt_hw_interrupt_umask(fh81_dma_p->dwc.irq); + + return rt_hw_dma_register(rt_dma,dma_name,RT_DEVICE_FLAG_RDWR,fh81_dma_p); + + +} + + +static void rt_fh_dma_cyclic_stop(struct dma_transfer *p){ + + struct fh81_dma *my_own = p->dma_controller; + struct dw_dma *dwc; + dwc = &my_own->dwc; + channel_clear_bit(dwc, CH_EN, 1<<(p->channel_number)); +} + + + + +static void rt_fh_dma_cyclic_start(struct dma_transfer *p){ + + struct fh81_dma *my_own = p->dma_controller; + struct dw_dma *dwc; + dwc = &my_own->dwc; + volatile uint32_t ret_status; + struct dw_lli *p_lli = RT_NULL; + p_lli = p->first_lli; + + //32bit ally + RT_ASSERT(((uint32_t)p_lli & 0x03) == 0); + + dw_writel(dwc, CLEAR.XFER, 1<<(p->channel_number)); + dw_writel(dwc, CLEAR.BLOCK, 1<<(p->channel_number)); + dw_writel(dwc, CLEAR.ERROR, 1<<(p->channel_number)); + + + //enable isr + channel_set_bit(dwc, MASK.BLOCK, lift_shift_bit_num(p->channel_number)); + //disable isr + channel_clear_bit(dwc, MASK.XFER, lift_shift_bit_num(p->channel_number)); + + ret_status = dw_readl(dwc,CHAN[p->channel_number].CFG_LO); + ret_status &= ~(DWC_CFGL_RELOAD_SAR|DWC_CFGL_RELOAD_DAR); + dw_writel(dwc,CHAN[p->channel_number].CFG_LO,ret_status); + + //set the first link add + ret_status = dw_readl(dwc,CHAN[p->channel_number].LLP); + ret_status = (uint32_t)&p_lli[0]; + dw_writel(dwc,CHAN[p->channel_number].LLP,ret_status); + + //set link enable + //ret_status = dw_readl(dwc,CHAN[p->channel_number].CTL_LO); + ret_status =DWC_CTLL_LLP_D_EN|DWC_CTLL_LLP_S_EN; + dw_writel(dwc,CHAN[p->channel_number].CTL_LO,ret_status); + + //clear ctl_hi + dw_writel(dwc,CHAN[p->channel_number].CTL_HI,0); + + //enable channle + channel_set_bit(dwc, CH_EN, 1<<(p->channel_number)); + + +} + + +static void rt_fh_dma_cyclic_prep(struct fh81_dma * fh81_dma_p,struct dma_transfer *p) { + + //bind the controller to the transfer + p->dma_controller = fh81_dma_p; + //bind active transfer + fh81_dma_p->dma_channel[p->channel_number].active_trans = p; + //p_transfer->dma_controller->dma_channel[p_transfer->channel_number].active_trans = dma_trans_desc; + struct fh81_dma *my_own = p->dma_controller; + struct dw_dma *dwc; + dwc = &my_own->dwc; + volatile uint32_t ret_status; + struct dw_lli *p_lli = RT_NULL; + uint32_t periods,i; + uint32_t temp_src_add; + uint32_t temp_dst_add; + uint32_t buf_len = p->trans_len; + uint32_t period_len = p->period_len; + + struct dma_transfer * dma_trans_desc = p; + //check first... + RT_ASSERT(buf_len % period_len == 0); + + //cal the periods... + periods = buf_len / period_len; + + + //get desc.... + //dma_trans_desc->first_lli = (struct dw_lli *)rt_malloc(periods * sizeof(struct dw_lli)); + dma_trans_desc->first_lli = get_desc(fh81_dma_p,dma_trans_desc,periods); + + if(dma_trans_desc->first_lli == RT_NULL){ + + FH_DMA_DEBUG("transfer error,reason: not enough mem..\n"); + RT_ASSERT(dma_trans_desc->first_lli != RT_NULL); + } + + rt_memset((void *)dma_trans_desc->first_lli, 0, periods * sizeof(struct dw_lli)); + p_lli = dma_trans_desc->first_lli; + + RT_ASSERT(((uint32_t)p_lli & 0x03) == 0); + + + RT_ASSERT(dma_trans_desc->dst_inc_mode <=DW_DMA_SLAVE_FIX); + RT_ASSERT(dma_trans_desc->src_inc_mode <=DW_DMA_SLAVE_FIX); + //step3: set the mem.. + for(i=0;idst_inc_mode){ + case DW_DMA_SLAVE_INC: + temp_dst_add = dma_trans_desc->dst_add + i * period_len * (1<dst_width); + break; + case DW_DMA_SLAVE_DEC: + temp_dst_add = dma_trans_desc->dst_add - i * period_len * (1<dst_width); + break; + case DW_DMA_SLAVE_FIX: + temp_dst_add = dma_trans_desc->dst_add; + break; + + } + + + switch(dma_trans_desc->src_inc_mode){ + case DW_DMA_SLAVE_INC: + temp_src_add = dma_trans_desc->src_add + i * period_len * (1<src_width); + break; + case DW_DMA_SLAVE_DEC: + temp_src_add = dma_trans_desc->src_add - i * period_len * (1<src_width); + break; + case DW_DMA_SLAVE_FIX: + temp_src_add = dma_trans_desc->src_add ; + break; + + } + + + p_lli[i].sar = temp_src_add; + p_lli[i].dar = temp_dst_add; + + //para ctl + + + RT_ASSERT(dma_trans_desc->dst_width <=DW_DMA_SLAVE_WIDTH_32BIT); + RT_ASSERT(dma_trans_desc->src_width <=DW_DMA_SLAVE_WIDTH_32BIT); + + RT_ASSERT(dma_trans_desc->dst_msize <=DW_DMA_SLAVE_MSIZE_256); + RT_ASSERT(dma_trans_desc->src_msize <=DW_DMA_SLAVE_MSIZE_256); + RT_ASSERT(dma_trans_desc->fc_mode <=DMA_P2P); + + + + p_lli[i].ctllo = DWC_CTLL_INT_EN|DWC_CTLL_DST_WIDTH(dma_trans_desc->dst_width)|DWC_CTLL_SRC_WIDTH(dma_trans_desc->src_width) + |DWC_CTLL_DST_INC_MODE(dma_trans_desc->dst_inc_mode)|DWC_CTLL_SRC_INC_MODE(dma_trans_desc->src_inc_mode) + |DWC_CTLL_DST_MSIZE(dma_trans_desc->dst_msize)|DWC_CTLL_SRC_MSIZE(dma_trans_desc->src_msize)|DWC_CTLL_FC(dma_trans_desc->fc_mode) + |DWC_CTLL_DMS(0)|DWC_CTLL_SMS(0); + //block size + p_lli[i].ctlhi = period_len; + + + + p_lli[i].llp = (uint32_t)&p_lli[i+1]; + p_lli[i].ctllo |= DWC_CTLL_LLP_D_EN|DWC_CTLL_LLP_S_EN; + + + //flush cache to mem + mmu_clean_invalidated_dcache((uint32_t)&p_lli[i],sizeof(struct dw_lli)); + + dump_lli(&p_lli[i]); + } + //make a ring here + p_lli[periods -1 ].llp = (uint32_t)&p_lli[0]; + + mmu_clean_invalidated_dcache((uint32_t)&p_lli[periods -1 ],sizeof(struct dw_lli)); + + + + //parse the handshake + RT_ASSERT(dma_trans_desc->dst_hs <= DMA_SW_HANDSHAKING); + RT_ASSERT(dma_trans_desc->src_hs <= DMA_SW_HANDSHAKING); + + //dst handshake + dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,0); + ret_status = 0; + if(dma_trans_desc->dst_hs == DMA_SW_HANDSHAKING){ + ret_status |= DWC_CFGL_HS_DST; + } + else{ + ret_status &= ~DWC_CFGL_HS_DST; + } + dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); + + + + //src handshake + ret_status = dw_readl(dwc,CHAN[dma_trans_desc->channel_number].CFG_LO); + if(dma_trans_desc->src_hs == DMA_SW_HANDSHAKING){ + ret_status |= DWC_CFGL_HS_SRC; + } + else{ + ret_status &= ~DWC_CFGL_HS_SRC; + } + dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_LO,ret_status); + + + //only hw handshaking need this.. + switch(dma_trans_desc->fc_mode){ + case DMA_M2M: + break; + case DMA_M2P: + //set dst per... + RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END); + ret_status = dw_readl(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); + //clear 43 ~ 46 bit + ret_status &= ~0x7800; + ret_status |= DWC_CFGH_DST_PER(dma_trans_desc->dst_per); + dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); + //DWC_CFGH_SRC_PER + + break; + case DMA_P2M: + //set src per... + RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END); + ret_status = dw_readl(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); + //clear 39 ~ 42 bit + ret_status &= ~0x780; + ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per); + dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); + + break; + case DMA_P2P: + //set src and dst.. + RT_ASSERT(dma_trans_desc->dst_per < DMA_HW_HS_END); + RT_ASSERT(dma_trans_desc->src_per < DMA_HW_HS_END); + + ret_status = dw_readl(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI); + ret_status &= ~0x7800; + ret_status &= ~0x780; + ret_status |= DWC_CFGH_SRC_PER(dma_trans_desc->src_per) | DWC_CFGH_DST_PER(dma_trans_desc->dst_per); + dw_writel(dwc,CHAN[dma_trans_desc->channel_number].CFG_HI,ret_status); + + break; + default: + break; + } + + dma_trans_desc->dma_controller->dma_channel[dma_trans_desc->channel_number].channel_status = CHANNEL_STATUS_BUSY; + + if(dma_trans_desc->prepare_callback){ + dma_trans_desc->prepare_callback(dma_trans_desc->prepare_para); + } + +} + + +static void rt_fh_dma_cyclic_free(struct dma_transfer *p){ + + struct fh81_dma *my_own = p->dma_controller; + struct dw_dma *dwc; + dwc = &my_own->dwc; + volatile uint32_t ret_status; + struct dw_lli *p_lli = RT_NULL; + p_lli = p->first_lli; + + + //close channel first.. + channel_clear_bit(dwc, CH_EN, 1<<(p->channel_number)); + + //check if close really + while (dw_readl(dwc, CH_EN) & 1<<(p->channel_number)); + + dw_writel(dwc, CLEAR.XFER, 1<<(p->channel_number)); + dw_writel(dwc, CLEAR.BLOCK, 1<<(p->channel_number)); + dw_writel(dwc, CLEAR.ERROR, 1<<(p->channel_number)); + + + + + //rt_free(p->first_lli); + put_desc(my_own,p); + +} + + +void rt_fh_dma_init(void){ + fh81_dma_register(&fh81_dma_controller[0],"fh81_dma"); + +} diff --git a/bsp/fh8620/drivers/fh_dma.h b/bsp/fh8620/drivers/fh_dma.h new file mode 100644 index 0000000000000000000000000000000000000000..faceac2deb249f446b16a79c90858562a784af83 --- /dev/null +++ b/bsp/fh8620/drivers/fh_dma.h @@ -0,0 +1,254 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_DMA_H_ +#define FH_DMA_H_ + + +/**************************************************************************** +* #include section +* add #include here if any +***************************************************************************/ + +#include + + +/********************************* + * + * DMA SOC define start + * + *********************************/ + + +#define FH81_MAX_CHANNEL (4) +#define FH81_CHANNEL_MAX_TRANSFER_SIZE (4095) + + + + +enum DMA_HW_HS_MAP{ + ACODEC_RX = 0, + ACODEC_TX, + SPI0_RX, + SPI0_TX, + SPI1_RX, + SPI1_TX, + UART0_RX, + UART0_TX, + UART1_RX, + UART1_TX, + DMA_HW_HS_END, +}; + +/********************************* + * + * DMA SOC define end + * + *********************************/ + +//user use the dma could use callback function,when the dma make the work done... +typedef void (*dma_complete_callback)(void *complete_para); + +typedef void (*user_prepare)(void *prepare_para); + + + +/**************************** i'm cut-off line ************************************/ + + + + + + +//controller private para... +struct fh81_dma; + + +struct dw_lli { + /* values that are not changed by hardware */ + rt_uint32_t sar; + rt_uint32_t dar; + rt_uint32_t llp; /* chain to next lli */ + rt_uint32_t ctllo; + /* values that may get written back: */ + rt_uint32_t ctlhi; + /* sstat and dstat can snapshot peripheral register state. + * silicon config may discard either or both... + */ + rt_uint32_t sstat; + rt_uint32_t dstat; + rt_uint32_t reserve; + +}; + +//transfer use below +struct dma_transfer{ + //this is private for the dma drive....app don't touch it,the driver will manger it + //link interface for more transfer to the controller... + rt_list_t transfer_list; + struct fh81_dma *dma_controller; + //this the mem add....the dma controller will load the setting to move data .... + //user don't touch it + struct dw_lli *first_lli; + rt_uint32_t lli_size; + //new add for allign get desc... + rt_uint32_t actual_lli_size; + + + //user could set paras below~~~ +#define AUTO_FIND_CHANNEL (0xff) + //transfer with which dma channel...if the data is 0xff, the driver will auto find a free channel. + rt_uint32_t channel_number; + //which dma you want to use...for fh81....only 0!!! + rt_uint32_t dma_number; + + + //user should set the para below +#define DMA_M2M (0) // MEM <=> MEM +#define DMA_M2P (1) // MEM => peripheral A +#define DMA_P2M (2) // MEM <= peripheral A +#define DMA_P2P (3) // peripheral A <=> peripheral B + rt_uint32_t fc_mode;//ip->mem. mem->mem. mem->ip + + + + +#define DMA_HW_HANDSHAKING (0) +#define DMA_SW_HANDSHAKING (1) + rt_uint32_t src_hs; //src + //if use hw handshaking ,you need to set the hw handshaking number, this SOC defined + rt_uint32_t src_per; //src hw handshake number + //rt_uint32_t irq_mode;//for each transfer,irq maybe not same. suggest for the default(transfer isr) + +#define DW_DMA_SLAVE_WIDTH_8BIT (0) +#define DW_DMA_SLAVE_WIDTH_16BIT (1) +#define DW_DMA_SLAVE_WIDTH_32BIT (2) + rt_uint32_t src_width; + + //the user should reference the hw handshaking watermark.. +#define DW_DMA_SLAVE_MSIZE_1 (0) +#define DW_DMA_SLAVE_MSIZE_4 (1) +#define DW_DMA_SLAVE_MSIZE_8 (2) +#define DW_DMA_SLAVE_MSIZE_16 (3) +#define DW_DMA_SLAVE_MSIZE_32 (4) +#define DW_DMA_SLAVE_MSIZE_64 (5) +#define DW_DMA_SLAVE_MSIZE_128 (6) +#define DW_DMA_SLAVE_MSIZE_256 (7) + rt_uint32_t src_msize; + rt_uint32_t src_add; +#define DW_DMA_SLAVE_INC (0) +#define DW_DMA_SLAVE_DEC (1) +#define DW_DMA_SLAVE_FIX (2) + rt_uint32_t src_inc_mode; //increase mode: increase or not change + + +//#define DMA_DST_HW_HANDSHAKING (0) +//#define DMA_DST_SW_HANDSHAKING (1) + rt_uint32_t dst_hs; //src + //if use hw handshaking ,you need to set the hw handshaking number, this SOC defined + rt_uint32_t dst_per; //dst hw handshake number +//#define DW_DMA_SLAVE_WIDTH_8BIT (0) +//#define DW_DMA_SLAVE_WIDTH_16BIT (1) +//#define DW_DMA_SLAVE_WIDTH_32BIT (2) + rt_uint32_t dst_width; +//#define DW_DMA_SLAVE_MSIZE_1 (0) +//#define DW_DMA_SLAVE_MSIZE_4 (1) +//#define DW_DMA_SLAVE_MSIZE_8 (2) +//#define DW_DMA_SLAVE_MSIZE_16 (3) +//#define DW_DMA_SLAVE_MSIZE_32 (4) +//#define DW_DMA_SLAVE_MSIZE_64 (5) +//#define DW_DMA_SLAVE_MSIZE_128 (6) +//#define DW_DMA_SLAVE_MSIZE_256 (7) + rt_uint32_t dst_msize; + rt_uint32_t dst_add; +//#define DW_DMA_SLAVE_INC (0) +//#define DW_DMA_SLAVE_DEC (1) +//#define DW_DMA_SLAVE_FIX (2) + rt_uint32_t dst_inc_mode; //increase mode: increase or not change + + + //total sizes, unit: src_width/DW_DMA_SLAVE_WIDTH_8BIT... + //exg: src_width = DW_DMA_SLAVE_WIDTH_32BIT. trans_len = 2...means that: the dma will transfer 2*4 bytes.. + //exg: src_width = DW_DMA_SLAVE_WIDTH_8BIT. trans_len = 6...means that: the dma will transfer 1*6 bytes.. + rt_uint32_t trans_len; + + + + //this is used when dma finish transfer job + dma_complete_callback complete_callback; + void *complete_para; //for the driver data use the dma driver. + + + //this is used when dma before work..the user maybe need to set his own private para.. + user_prepare prepare_callback; + void *prepare_para; + + + //add cyclic para... + //period len.. + rt_uint32_t period_len; + + +}; + + + + + + + + +/**************************************************************************** +* #define section +* add constant #define here if any +***************************************************************************/ + + +/**************************************************************************** +* ADT section +* add Abstract Data Type definition here +***************************************************************************/ + + + +/**************************************************************************** +* extern variable declaration section +***************************************************************************/ + +/**************************************************************************** +* section +* add function prototype here if any +***************************************************************************/ +rt_err_t fh81_dma_register(struct fh81_dma * fh81_dma_p, + char * dma_name); +void rt_fh_dma_init(void); +/********************************End Of File********************************/ + + + + +#endif /* FH81_DMA_H_ */ + diff --git a/bsp/fh8620/drivers/gpio.c b/bsp/fh8620/drivers/gpio.c new file mode 100644 index 0000000000000000000000000000000000000000..d7d325ad00a4deffd68e458750edf51eeeb35d79 --- /dev/null +++ b/bsp/fh8620/drivers/gpio.c @@ -0,0 +1,450 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "fh_def.h" +#include "gpio.h" +#include "Libraries/inc/fh_gpio.h" +#include "interrupt.h" +#include "board_info.h" +#include +#include "fh_arch.h" +//#define FH_GPIO_DEBUG + +#ifdef FH_GPIO_DEBUG +#define PRINT_GPIO_DBG(fmt, args...) \ + do \ + { \ + rt_kprintf("FH_GPIO_DEBUG: "); \ + rt_kprintf(fmt, ## args); \ + } \ + while(0) +#else +#define PRINT_GPIO_DBG(fmt, args...) do { } while (0) +#endif + +int gpio_available[NUM_OF_GPIO]; +extern struct rt_irq_desc irq_desc[]; + +static inline rt_uint32_t gpio_to_base(rt_uint32_t gpio) +{ + if (gpio >= 32 && gpio < 64) + { + return GPIO1_REG_BASE; + } + else if(gpio < 32) + { + return GPIO0_REG_BASE; + } + else + { + rt_kprintf("ERROR: %s, incorrect GPIO num\n", __func__); + return -RT_ERROR; + } +} + +static inline rt_uint32_t irq_to_base(rt_uint32_t irq) +{ + return (irq-NR_INTERNAL_IRQS > 32) ? GPIO1_REG_BASE : GPIO0_REG_BASE; +} + +static inline rt_uint32_t irq_to_bit(rt_uint32_t irq) +{ + if(irq >= NR_INTERNAL_IRQS && irq < NR_INTERNAL_IRQS + 32) + return 0; + else + return 32; +} + +rt_uint32_t gpio_to_irq(rt_uint32_t gpio) +{ + return (gpio + NR_INTERNAL_IRQS); +} + +void gpio_enable_debounce(rt_uint32_t gpio) +{ + rt_uint32_t tmp, base, offset; + + offset = gpio % 32; + base = gpio_to_base(gpio); + + tmp = GET_REG(base + REG_GPIO_DEBOUNCE); + + tmp |= BIT(offset); + + SET_REG(base + REG_GPIO_DEBOUNCE, tmp); +} + +void gpio_disable_debounce(rt_uint32_t gpio) +{ + rt_uint32_t tmp, base, offset; + + offset = gpio % 32; + base = gpio_to_base(gpio); + + tmp = GET_REG(base + REG_GPIO_DEBOUNCE); + + tmp &= ~BIT(offset); + + SET_REG(base + REG_GPIO_DEBOUNCE, tmp); +} + +int gpio_get_value(rt_uint32_t gpio) +{ + rt_uint32_t tmp, base, offset; + + offset = gpio % 32; + base = gpio_to_base(gpio); + + tmp = GET_REG(base + REG_GPIO_SWPORTA_DDR); + tmp &= BIT(offset); + + if (tmp) { + tmp = GET_REG(base + REG_GPIO_SWPORTA_DR); + } else { + tmp = GET_REG(base + REG_GPIO_EXT_PORTA); + } + tmp &= BIT(offset); + tmp = tmp >> offset; + return tmp; +} + +void gpio_set_value(rt_uint32_t gpio, int val) +{ + rt_uint32_t tmp, base, offset; + + offset = gpio % 32; + base = gpio_to_base(gpio); + + tmp = GET_REG(base + REG_GPIO_SWPORTA_DR); + + if(val) + tmp |= BIT(offset); + else + tmp &= ~BIT(offset); + + SET_REG(base + REG_GPIO_SWPORTA_DR, tmp); + +} + + +int gpio_get_direction(rt_uint32_t gpio) +{ + rt_uint32_t tmp, base, offset; + + offset = gpio % 32; + base = gpio_to_base(gpio); + + tmp = GET_REG(base + REG_GPIO_SWPORTA_DDR); + + tmp &= BIT(offset); + tmp = tmp >> offset; + return tmp; +} + +void gpio_set_direction(rt_uint32_t gpio, rt_uint32_t direction) +{ + rt_uint32_t tmp, base, offset; + + offset = gpio % 32; + base = gpio_to_base(gpio); + + tmp = GET_REG(base + REG_GPIO_SWPORTA_DDR); + + if(direction == GPIO_DIR_OUTPUT) + tmp |= BIT(offset); + else + tmp &= ~BIT(offset); + + SET_REG(base + REG_GPIO_SWPORTA_DDR, tmp); +} + + +int gpio_set_irq_type(rt_uint32_t gpio, rt_uint32_t type) +{ + rt_uint32_t int_type, int_polarity; + rt_uint32_t bit = gpio % 32; + rt_uint32_t base; + base = gpio_to_base(gpio); + + int_type = GET_REG(base + REG_GPIO_INTTYPE_LEVEL); + int_polarity = GET_REG(base + REG_GPIO_INT_POLARITY); + + switch (type & IRQ_TYPE_TRIGGER_MASK) { + case IRQ_TYPE_EDGE_BOTH: + int_type |= BIT(bit); + // toggle trigger + if (gpio_get_value(gpio)) + int_polarity &= ~BIT(bit); + else + int_polarity |= BIT(bit); + break; + case IRQ_TYPE_EDGE_RISING: + int_type |= BIT(bit); + int_polarity |= BIT(bit); + break; + case IRQ_TYPE_EDGE_FALLING: + int_type |= BIT(bit); + int_polarity &= ~BIT(bit); + break; + case IRQ_TYPE_LEVEL_HIGH: + int_type &= ~BIT(bit); + int_polarity |= BIT(bit); + break; + case IRQ_TYPE_LEVEL_LOW: + int_type &= ~BIT(bit); + int_polarity &= ~BIT(bit); + break; + case IRQ_TYPE_NONE: + return 0; + default: + return -RT_ERROR; + } + SET_REG(base + REG_GPIO_INTTYPE_LEVEL, int_type); + SET_REG(base + REG_GPIO_INT_POLARITY, int_polarity); + return 0; +} + +int gpio_irq_mask(rt_uint32_t irq) +{ + rt_uint32_t tmp, base, bit; + + base = irq_to_base(irq); + bit = irq_to_bit(irq); + + tmp = GET_REG(base + REG_GPIO_INTMASK); + tmp |= BIT(irq - NR_INTERNAL_IRQS - bit); + SET_REG(base + REG_GPIO_INTMASK, tmp); + return 0; +} + +int gpio_irq_unmask(rt_uint32_t irq) +{ + rt_uint32_t tmp, base, bit; + + base = irq_to_base(irq); + bit = irq_to_bit(irq); + + tmp = GET_REG(base + REG_GPIO_INTMASK); + tmp &= ~BIT((irq - NR_INTERNAL_IRQS - bit)); + SET_REG(base + REG_GPIO_INTMASK, tmp); + return 0; +} + +void gpio_irq_enable(rt_uint32_t irq) +{ + rt_uint32_t tmp, base, bit; + + base = irq_to_base(irq); + bit = irq_to_bit(irq); + + tmp = GET_REG(base + REG_GPIO_INTEN); + tmp |= BIT(irq - NR_INTERNAL_IRQS - bit); + SET_REG(base + REG_GPIO_INTEN, tmp); +} + +void gpio_irq_disable(rt_uint32_t irq) +{ + rt_uint32_t tmp, base, bit; + + base = irq_to_base(irq); + bit = irq_to_bit(irq); + + tmp = GET_REG(base + REG_GPIO_INTEN); + tmp &= ~BIT((irq - NR_INTERNAL_IRQS - bit)); + SET_REG(base + REG_GPIO_INTEN, tmp); +} + +static void fh_gpio_interrupt(int irq, void *param) +{ + rt_uint32_t irq_status; + int gpio_num, gpio; + rt_uint32_t base; + struct fh_gpio_obj *gpio_obj = (struct fh_gpio_obj *)param; + + //rt_kprintf("fh_gpio_interrupt start\n"); + + //fixme: spin lock??? + base = (irq==40) ? GPIO0_REG_BASE : GPIO1_REG_BASE; + + irq_status = GET_REG(base + REG_GPIO_INTSTATUS); + + if (irq_status == 0) { + rt_kprintf("gpio irq status is zero.\n"); + return; + } + + /* temporarily mask (level sensitive) parent IRQ */ + gpio_irq_mask(irq); + + gpio_num = __rt_ffs(irq_status) - 1; + + SET_REG(base + REG_GPIO_PORTA_EOI, BIT(gpio_num)); + + gpio = gpio_num + ((irq==40) ? 0 : 32); + + //generic_handle_irq(gpio_to_irq(gpio)); + if(irq_desc[gpio_to_irq(gpio)].handler) + irq_desc[gpio_to_irq(gpio)].handler(gpio_to_irq(gpio), irq_desc[gpio_to_irq(gpio)].param); + + gpio_irq_mask(irq); + /* now it may re-trigger */ +} + + +int gpio_direction_input(rt_uint32_t gpio) +{ + rt_uint32_t reg, base; + + if(gpio > NUM_OF_GPIO) + { + rt_kprintf("ERROR: %s, incorrect GPIO num\n", __func__); + return -RT_ERROR; + } + + if(!gpio_available[gpio]) + { + rt_kprintf("ERROR: %s, GPIO %d is not available\n", __func__, gpio); + return -RT_EBUSY; + } + + base = gpio_to_base(gpio); + gpio = gpio % 32; + + + //fixme: lock + //spin_lock_irqsave(&chip->lock, flags); + reg = GET_REG(base + REG_GPIO_SWPORTA_DDR); + reg &= ~(1 << gpio); + SET_REG(base + REG_GPIO_SWPORTA_DDR, reg); + //spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +int gpio_direction_output(rt_uint32_t gpio, rt_uint32_t val) +{ + rt_uint32_t reg, base; + + if(gpio > NUM_OF_GPIO) + { + rt_kprintf("ERROR: %s, incorrect GPIO num\n", __func__); + return -RT_ERROR; + } + + if(!gpio_available[gpio]) + { + rt_kprintf("ERROR: %s, GPIO %d is not available\n", __func__, gpio); + return -RT_EBUSY; + } + + base = gpio_to_base(gpio); + gpio = gpio % 32; + + //fixme: lock + //spin_lock_irqsave(&chip->lock, flags); + reg = GET_REG(base + REG_GPIO_SWPORTA_DDR); + reg |= (1 << gpio); + SET_REG(base + REG_GPIO_SWPORTA_DDR, reg); + + reg = GET_REG(base + REG_GPIO_SWPORTA_DR); + + if(val) + reg |= (1 << gpio); + else + reg &= ~(1 << gpio); + SET_REG(base + REG_GPIO_SWPORTA_DR, reg); + + //spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +int gpio_request(rt_uint32_t gpio) +{ + if(gpio > NUM_OF_GPIO) + { + rt_kprintf("ERROR: %s, incorrect GPIO num\n", __func__); + return -RT_ERROR; + } + gpio_available[gpio] = 1; + return 0; +} + +int gpio_release(rt_uint32_t gpio) +{ + if(gpio > NUM_OF_GPIO) + { + rt_kprintf("ERROR: %s, incorrect GPIO num\n", __func__); + return -RT_ERROR; + } + gpio_available[gpio] = 0; + return 0; +} + +int fh_gpio_probe(void *priv_data) +{ + struct fh_gpio_obj *gpio_obj = (struct fh_gpio_obj *)priv_data; + int i; + + if(gpio_obj->id == 0){ + rt_hw_interrupt_install(gpio_obj->irq, fh_gpio_interrupt, (void *)gpio_obj, "gpio_0"); + } + else if(gpio_obj->id == 1){ + rt_hw_interrupt_install(gpio_obj->irq, fh_gpio_interrupt, (void *)gpio_obj, "gpio_1"); + } + + + + rt_hw_interrupt_umask(gpio_obj->irq); + + for(i=0; i<32; i++) + { + irq_desc[NR_INTERNAL_IRQS + 32 * gpio_obj->id + i].param = gpio_obj; + } + + return 0; +} + +int fh_gpio_exit(void *priv_data) +{ + return 0; +} + +struct fh_board_ops gpio_driver_ops = +{ + .probe = fh_gpio_probe, + .exit = fh_gpio_exit, +}; + +void rt_hw_gpio_init(void) +{ + PRINT_GPIO_DBG("%s start\n", __func__); + rt_memset(gpio_available, 0, sizeof(int) * NUM_OF_GPIO); + fh_board_driver_register("gpio", &gpio_driver_ops); + PRINT_GPIO_DBG("%s end\n", __func__); +} + + diff --git a/bsp/fh8620/drivers/gpio.h b/bsp/fh8620/drivers/gpio.h new file mode 100644 index 0000000000000000000000000000000000000000..7fe4d236b70f27c5c3b879e3ad0f08da22ee0848 --- /dev/null +++ b/bsp/fh8620/drivers/gpio.h @@ -0,0 +1,188 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef GPIO_H_ +#define GPIO_H_ + +#include + +/** + * GPIO interrupt trigger type macro, + * each represent an interrupt trigger mode + * + * @see gpio_set_irq_type(); + */ +enum +{ + IRQ_TYPE_NONE = 0x00000000, /**< none*/ + IRQ_TYPE_EDGE_RISING = 0x00000001, /**< rising edge*/ + IRQ_TYPE_EDGE_FALLING = 0x00000002, /**< falling edge*/ + IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING), + IRQ_TYPE_LEVEL_HIGH = 0x00000004, /**< high level*/ + IRQ_TYPE_LEVEL_LOW = 0x00000008, /**< low level*/ + IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH), + IRQ_TYPE_TRIGGER_MASK = (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW | \ + IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING), +}; + +/** + * GPIO direction macro, + * each represent a direction + * + * @see gpio_get_direction(); + * @see gpio_set_direction(); + */ +#define GPIO_DIR_OUTPUT 1 /**< output*/ +#define GPIO_DIR_INPUT 0 /**< input*/ + +/** + * convert GPIO number to IRQ number + * @param gpio GPIO number to be converted + * @return IRQ number + */ +rt_uint32_t gpio_to_irq(rt_uint32_t gpio); + +/** + * disable GPIO's debounce mode + * controls whether an external signal that is the source + * of an interrupt needs to be debounced to remove any + * spurious glitches. + * @param gpio GPIO number + */ +void gpio_disable_debounce(rt_uint32_t gpio); + +/** + * enable GPIO's debounce mode + * controls whether an external signal that is the source + * of an interrupt needs to be debounced to remove any + * spurious glitches. + * @param gpio GPIO number + */ +void gpio_enable_debounce(rt_uint32_t gpio); + +/** + * allows each GPIO to be configured for interrupts + * it configures the corresponding GPIO to become an interrupt + * @param gpio GPIO number + */ +void gpio_irq_enable(rt_uint32_t irq); + +/** + * GPIO operates as a normal GPIO signal + * interrupts are disabled + * @param gpio GPIO number + */ +void gpio_irq_disable(rt_uint32_t irq); + +/** + * it configures the interrupt type to be + * falling-edge or active-low sensitive + * rising-edge or active-high sensitive. + * @param gpio GPIO number + * @param type interrupt type + * @return 0 if OK + */ +int gpio_set_irq_type(rt_uint32_t gpio, rt_uint32_t type); + +/** + * mask the interrupt + * @param gpio GPIO number + * @return 0 if OK + */ +int gpio_irq_mask(rt_uint32_t irq); + +/** + * unmask the interrupt + * @param gpio GPIO number + * @return 0 if OK + */ +int gpio_irq_unmask(rt_uint32_t irq); + +/** + * get corresponding GPIO's direction + * @param gpio GPIO number + * @return 0 - input + * 1 - output + */ +int gpio_get_direction(rt_uint32_t gpio); + +/** + * set corresponding GPIO's direction + * @param gpio GPIO number + * @return 0 - input + * 1 - output + */ +void gpio_set_direction(rt_uint32_t gpio, rt_uint32_t direction); + +/** + * get corresponding GPIO's value + * @param gpio GPIO number + * @return GPIO value + */ +int gpio_get_value(rt_uint32_t gpio); + +/** + * set corresponding GPIO's value + * @param gpio GPIO number + * @param val GPIO value + */ +void gpio_set_value(rt_uint32_t gpio, int val); + +/** + * set corresponding GPIO's direction to input + * @param gpio GPIO number + * @return 0 if OK + */ +int gpio_direction_input(rt_uint32_t gpio); + +/** + * set corresponding GPIO's value and set direction to output + * @param gpio GPIO number + * @param val GPIO value + * @return 0 if OK + */ +int gpio_direction_output(rt_uint32_t gpio, rt_uint32_t val); + +/** + * request a GPIO + * @param gpio GPIO number + * @return 0 if OK + */ +int gpio_request(rt_uint32_t gpio); + +/** + * release a GPIO + * @param gpio GPIO number + * @return 0 if OK + */ +int gpio_release(rt_uint32_t gpio); + +/** + * initialize GPIO driver + */ +void rt_hw_gpio_init(void); + +#endif /* GPIO_H_ */ diff --git a/bsp/fh8620/drivers/i2c.c b/bsp/fh8620/drivers/i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..41f2c059f96b92d96b0e9787641e5453738fa9c2 --- /dev/null +++ b/bsp/fh8620/drivers/i2c.c @@ -0,0 +1,520 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include "i2c.h" +#include "inc/fh_driverlib.h" +#include "board_info.h" + +//#define FH_I2C_DEBUG + +#ifdef FH_I2C_DEBUG +#define PRINT_I2C_DBG(fmt, args...) \ + do \ + { \ + rt_kprintf("FH_I2C_DEBUG: "); \ + rt_kprintf(fmt, ## args); \ + } \ + while(0) +#else +#define PRINT_I2C_DBG(fmt, args...) do { } while (0) +#endif + + + +static void fh_i2c_xfer_init(struct rt_i2c_bus_device *dev, struct rt_i2c_msg msgs[], rt_uint32_t num) +{ + struct i2c_driver *i2c_drv = (struct i2c_driver *)dev->priv; + struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv; + rt_uint32_t ic_con; + + /* if the slave address is ten bit address, ERROR*/ + if (msgs[i2c_drv->msg_write_idx].flags & I2C_M_TEN) + { + rt_kprintf("ERROR: %s, ten bit address is NOT supported\n", __func__); + return; + } + + /* Disable the adapter */ + I2C_WaitMasterIdle(i2c_obj); + + I2C_Enable(i2c_obj, RT_FALSE); + + /* set the slave (target) address */ + I2C_SetSlaveAddress(i2c_obj, msgs[i2c_drv->msg_write_idx].addr); + + /* Enable interrupts */ + I2C_SetInterruptMask(i2c_obj, DW_IC_INTR_DEFAULT_MASK); + + /* Enable the adapter */ + I2C_Enable(i2c_obj, RT_TRUE); +} + + +static rt_size_t fh_i2c_xfer(struct rt_i2c_bus_device *dev, + struct rt_i2c_msg msgs[], rt_uint32_t num) +{ + struct i2c_driver *i2c_drv = (struct i2c_driver *)dev->priv; + struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv; + int ret; + struct rt_i2c_msg *pmsg = RT_NULL; + + PRINT_I2C_DBG(">>>>>>>>>>>>>%s start\n", __func__); + + rt_completion_init(&i2c_drv->transfer_completion); + + ret = rt_mutex_take(i2c_drv->lock, RT_WAITING_FOREVER ); + + i2c_drv->msgs = msgs; + i2c_drv->msgs_num = num; + i2c_drv->msg_read_idx = 0; + i2c_drv->msg_write_idx = 0; + i2c_drv->cmd_err = 0; + i2c_drv->msg_err = 0; + i2c_drv->status = STATUS_IDLE; + i2c_obj->abort_source = 0; + + ret = I2C_WaitDeviceIdle(i2c_obj); + if (ret < 0) + { + //I2C_SetDataCmd(i2c_obj, 0x200); + //goto done; + } + + fh_i2c_xfer_init(dev, msgs, num); + + ret = rt_completion_wait(&i2c_drv->transfer_completion, RT_TICK_PER_SECOND); + PRINT_I2C_DBG("%s transfer finished\n", "rt_completion_wait"); + if(ret) + { + rt_kprintf("ERROR: %s, transfer timeout\n", __func__); + I2C_SetDataCmd(i2c_obj, 0x200); + I2C_Init(i2c_obj); + ret = -RT_ETIMEOUT; + goto done; + } + + if (i2c_drv->msg_err) + { + rt_kprintf("i2c_priv->msg_err: %d\n", i2c_drv->msg_err); + ret = i2c_drv->msg_err; + goto done; + } + + /* no error */ + if (!i2c_drv->cmd_err) + { + /* Disable the adapter */ + I2C_WaitMasterIdle(i2c_obj); + I2C_Enable(i2c_obj, RT_FALSE); + ret = num; + goto done; + } + + /* We have an error */ + if (i2c_drv->cmd_err == DW_IC_ERR_TX_ABRT) + { + rt_kprintf("ERROR: %s, i2c_priv>cmd_err == DW_IC_ERR_TX_ABRT\n", __func__); + ret = I2C_HandleTxAbort(i2c_obj); + goto done; + } + + ret = 1; + +done: + I2C_Enable(i2c_obj, RT_FALSE); + rt_mutex_release(i2c_drv->lock); + PRINT_I2C_DBG(">>>>>>>>>>>>>%s end\n", __func__); + return ret; + +} + + +/* + * Initiate (and continue) low level master read/write transaction. + * This function is only called from i2c_fh_isr, and pumping i2c_msg + * messages into the tx buffer. Even if the size of i2c_msg data is + * longer than the size of the tx buffer, it handles everything. + */ +static void i2c_fh_xfer_msg(struct rt_i2c_bus_device *dev) +{ + struct i2c_driver *i2c_drv = (struct i2c_driver *)dev->priv; + struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv; + struct rt_i2c_msg *msgs = i2c_drv->msgs; + rt_uint32_t intr_mask, cmd; + int tx_limit, rx_limit; + rt_uint32_t addr = msgs[i2c_drv->msg_write_idx].addr; + rt_uint32_t buf_len = i2c_drv->tx_buf_len; + rt_uint8_t *buf = i2c_drv->tx_buf; + + PRINT_I2C_DBG("%s start, msgs_num: %d, write_idx: %d\n", __func__, i2c_drv->msgs_num, i2c_drv->msg_write_idx); + + intr_mask = DW_IC_INTR_DEFAULT_MASK; + + for (; i2c_drv->msg_write_idx < i2c_drv->msgs_num; i2c_drv->msg_write_idx++) + { + /* + * if target address has changed, we need to + * reprogram the target address in the i2c + * adapter when we are done with this transfer + */ + if (msgs[i2c_drv->msg_write_idx].addr != addr) { + rt_kprintf( + "ERROR: %s, invalid target address\n", __func__); + i2c_drv->msg_err = 1; + break; + } + + if (msgs[i2c_drv->msg_write_idx].len == 0) { + rt_kprintf( + "ERROR: %s, invalid message length\n", __func__); + i2c_drv->msg_err = 1; + break; + } + + if (!(i2c_drv->status & STATUS_WRITE_IN_PROGRESS)) + { + /* new i2c_msg */ + buf = msgs[i2c_drv->msg_write_idx].buf; + buf_len = msgs[i2c_drv->msg_write_idx].len; + + PRINT_I2C_DBG("new msg: len: %d, buf: 0x%x\n", buf_len, buf[0]); + } + + tx_limit = i2c_obj->config.tx_fifo_depth - I2C_GetTransmitFifoLevel(i2c_obj); + rx_limit = i2c_obj->config.rx_fifo_depth - I2C_GetReceiveFifoLevel(i2c_obj); + + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) + { + if (msgs[i2c_drv->msg_write_idx].flags & RT_I2C_RD) + { + cmd = 0x100; + rx_limit--; + } + else + { + cmd = *buf++; + } + + tx_limit--; buf_len--; + + if(!buf_len) + { + //2015-11-8 ar0130 bug fixed + while(I2C_GetTransmitFifoLevel(i2c_obj)); + cmd |= 0x200; + } + + I2C_SetDataCmd(i2c_obj, cmd); + } + + i2c_drv->tx_buf = buf; + i2c_drv->tx_buf_len = buf_len; + + if (buf_len > 0) + { + /* more bytes to be written */ + i2c_drv->status |= STATUS_WRITE_IN_PROGRESS; + break; + } + else + { + i2c_drv->status &= ~STATUS_WRITE_IN_PROGRESS; + } + } + + /* + * If i2c_msg index search is completed, we don't need TX_EMPTY + * interrupt any more. + */ + + if (i2c_drv->msg_write_idx == i2c_drv->msgs_num) + intr_mask &= ~DW_IC_INTR_TX_EMPTY; + + if (i2c_drv->msg_err) + { + rt_kprintf("ERROR: %s, msg_err: %d\n", __func__, i2c_drv->msg_err); + intr_mask = 0; + } + + I2C_SetInterruptMask(i2c_obj, intr_mask); + + PRINT_I2C_DBG("%s end\n", __func__); +} + +static void i2c_fh_read(struct rt_i2c_bus_device *dev) +{ + struct i2c_driver *i2c_drv = (struct i2c_driver *)dev->priv; + struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv; + struct rt_i2c_msg *msgs = i2c_drv->msgs; + int rx_valid; + + PRINT_I2C_DBG("%s start, msgs_num: %d, read_idx: %d\n", __func__, i2c_drv->msgs_num, i2c_drv->msg_read_idx); + + for (; i2c_drv->msg_read_idx < i2c_drv->msgs_num; i2c_drv->msg_read_idx++) + { + rt_uint32_t len; + rt_uint8_t *buf; + + if (!(msgs[i2c_drv->msg_read_idx].flags & RT_I2C_RD)) + continue; + + if (!(i2c_drv->status & STATUS_READ_IN_PROGRESS)) + { + len = msgs[i2c_drv->msg_read_idx].len; + buf = msgs[i2c_drv->msg_read_idx].buf; + } + else + { + PRINT_I2C_DBG("STATUS_READ_IN_PROGRESS\n"); + len = i2c_drv->rx_buf_len; + buf = i2c_drv->rx_buf; + } + + rx_valid = I2C_GetReceiveFifoLevel(i2c_obj); + + if(rx_valid == 0) + { + rt_kprintf("ERROR: %s, rx_valid == 0\n", __func__); + } + PRINT_I2C_DBG("%s, len=%d, rx_valid=%d\n", __func__, len, rx_valid); + for (; len > 0 && rx_valid > 0; len--, rx_valid--) + { + *buf++ = I2C_GetData(i2c_obj); + } + + PRINT_I2C_DBG("i2c_fh_read, len: %d, buf[0]: 0x%x\n", msgs[i2c_drv->msg_read_idx].len, msgs[i2c_drv->msg_read_idx].buf[0]); + + if (len > 0) + { + PRINT_I2C_DBG("len > 0\n"); + i2c_drv->status |= STATUS_READ_IN_PROGRESS; + i2c_drv->rx_buf_len = len; + i2c_drv->rx_buf = buf; + return; + } + else + i2c_drv->status &= ~STATUS_READ_IN_PROGRESS; + } + + PRINT_I2C_DBG("%s end\n", __func__); +} + +/* + * Interrupt service routine. This gets called whenever an I2C interrupt + * occurs. + */ +static void fh_i2c_interrupt(int this_irq, void *dev_id) +{ + struct i2c_driver *i2c_drv = dev_id; + struct rt_i2c_bus_device *i2c_bus_dev = i2c_drv->i2c_bus_dev; + struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)i2c_drv->priv; + rt_uint32_t stat; + + stat = I2C_ClearAndGetInterrupts(i2c_obj); + PRINT_I2C_DBG("status: 0x%x, mask: 0x%x\n", stat, I2C_GetInterruptMask(i2c_obj)); + + if (stat & DW_IC_INTR_TX_ABRT) + { + PRINT_I2C_DBG("DW_IC_INTR_TX_ABRT\n"); + i2c_drv->cmd_err |= DW_IC_ERR_TX_ABRT; + i2c_drv->status = STATUS_IDLE; + + /* + * Anytime TX_ABRT is set, the contents of the tx/rx + * buffers are flushed. Make sure to skip them. + */ + I2C_SetInterruptMask(i2c_obj, 0); + goto tx_aborted; + } + + if (stat & DW_IC_INTR_RX_FULL) + { + i2c_fh_read(i2c_bus_dev); + } + + if (stat & DW_IC_INTR_TX_EMPTY) + { + i2c_fh_xfer_msg(i2c_bus_dev); + } + + /* + * No need to modify or disable the interrupt mask here. + * i2c_fh_xfer_msg() will take care of it according to + * the current transmit status. + */ + +tx_aborted: + if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || i2c_drv->msg_err) + rt_completion_done(&i2c_drv->transfer_completion); + +} + +static const struct rt_i2c_bus_device_ops fh_i2c_ops = +{ + .master_xfer = fh_i2c_xfer, +}; + +int fh_i2c_probe(void *priv_data) +{ + int ret; + struct i2c_driver *i2c_drv; + struct rt_i2c_bus_device *i2c_bus_dev; + struct fh_i2c_obj *i2c_obj = (struct fh_i2c_obj *)priv_data; + char i2c_dev_name[5] = {0}; + + PRINT_I2C_DBG("%s start\n", __func__); + + i2c_bus_dev = (struct rt_i2c_bus_device*)rt_malloc(sizeof(struct rt_i2c_bus_device)); + rt_memset(i2c_bus_dev, 0, sizeof(struct rt_i2c_bus_device)); + i2c_bus_dev->ops = &fh_i2c_ops; + + rt_sprintf(i2c_dev_name, "%s%d", "i2c", i2c_obj->id); + ret = rt_i2c_bus_device_register(i2c_bus_dev, i2c_dev_name); + + if (ret != RT_EOK) + { + rt_kprintf("ERROR:rt_spi_bus_register failed, ret=%d\n", ret); + return -RT_ENOMEM; + } + + //priv struct init + i2c_drv = (struct i2c_driver*)rt_malloc(sizeof(struct i2c_driver)); + rt_memset(i2c_drv, 0, sizeof(struct i2c_driver)); + + i2c_drv->i2c_bus_dev = i2c_bus_dev; + i2c_drv->priv = priv_data; + i2c_bus_dev->priv = i2c_drv; + + i2c_drv->lock = rt_mutex_create("i2c_mux", RT_IPC_FLAG_FIFO); + if(i2c_obj->id == 0){ + rt_hw_interrupt_install(i2c_obj->irq, fh_i2c_interrupt, + (void *)i2c_drv, "i2c_0"); + } + else if(i2c_obj->id == 1){ + rt_hw_interrupt_install(i2c_obj->irq, fh_i2c_interrupt, + (void *)i2c_drv, "i2c_1"); + } + + rt_hw_interrupt_umask(i2c_obj->irq); + + //fixme: get from clk tree + i2c_obj->input_clock = 15000; + + I2C_Init(i2c_obj); + + PRINT_I2C_DBG("%s end\n", __func__); + return ret; + +} + +int fh_i2c_exit(void *priv_data) +{ + return 0; +} + +struct fh_board_ops i2c_driver_ops = +{ + .probe = fh_i2c_probe, + .exit = fh_i2c_exit, +}; + +void rt_hw_i2c_init(void) +{ + int ret; + + PRINT_I2C_DBG("%s start\n", __func__); + fh_board_driver_register("i2c", &i2c_driver_ops); + PRINT_I2C_DBG("%s end\n", __func__); + //fixme: never release? +} + +static rt_err_t fh_i2c_read_reg(struct rt_i2c_bus_device *fh81_i2c, + rt_uint16_t reg, rt_uint8_t *data) { + struct rt_i2c_msg msg[2]; + rt_uint8_t send_buf[2]; + rt_uint8_t recv_buf[1] = {0}; + + PRINT_I2C_DBG("%s start\n", __func__); + + // send_buf[0] = ((reg >> 8) & 0xff); + send_buf[0] = (reg & 0xFF); + + msg[0].addr = 0x51; + msg[0].flags = RT_I2C_WR; + msg[0].len = 1; + msg[0].buf = send_buf; + + msg[1].addr = 0x51; + msg[1].flags = RT_I2C_RD; + msg[1].len = 1; + msg[1].buf = recv_buf; + + rt_i2c_transfer(fh81_i2c, msg, 2); + *data = recv_buf[0]; + return RT_EOK; +} +static rt_err_t fh_i2c_write_reg(struct rt_i2c_bus_device *fh81_i2c, + rt_uint16_t reg, rt_uint8_t data) { + struct rt_i2c_msg msg; + rt_uint8_t send_buf[3]; + + PRINT_I2C_DBG("%s start\n", __func__); + + // send_buf[0] = ((reg >> 8) & 0xff); + send_buf[1] = (reg & 0xFF); + send_buf[2] = data; + + msg.addr = 0x51; + msg.flags = RT_I2C_WR; + msg.len = 2; + msg.buf = send_buf; + + rt_i2c_transfer(fh81_i2c, &msg, 1); + PRINT_I2C_DBG("%s end\n", __func__); + return RT_EOK; +} + +void i2c_test_sensor() { + struct rt_i2c_bus_device *fh81_i2c; + struct rt_i2c_msg msg[2]; + rt_uint8_t data[1] = { 0x00 }; + + fh81_i2c = rt_i2c_bus_device_find("i2c1"); + + fh_i2c_write_reg(fh81_i2c, 0x04, 0x02); + + fh_i2c_read_reg(fh81_i2c, 0x02, data); + + rt_kprintf("data read from 0x3038 is 0x%x\r\n", data[0]); + PRINT_I2C_DBG("%s end\n", __func__); +} +#ifdef RT_USING_FINSH +#include +FINSH_FUNCTION_EXPORT(i2c_test_sensor, sensor i2c test); +#endif + diff --git a/bsp/fh8620/drivers/i2c.h b/bsp/fh8620/drivers/i2c.h new file mode 100644 index 0000000000000000000000000000000000000000..9046bd81cbc4ecfc33736c1cdbd4874323fe95b7 --- /dev/null +++ b/bsp/fh8620/drivers/i2c.h @@ -0,0 +1,56 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __FH81_I2C_H__ +#define __FH81_I2C_H__ + + +#include + +struct i2c_driver +{ + int cmd_err; + int msg_err; + rt_uint32_t status; + + struct rt_i2c_msg *msgs; + int msgs_num; + int msg_write_idx; + rt_uint32_t tx_buf_len; + rt_uint8_t *tx_buf; + int msg_read_idx; + rt_uint32_t rx_buf_len; + rt_uint8_t *rx_buf; + + struct rt_i2c_bus_device *i2c_bus_dev; + struct rt_completion transfer_completion; + rt_mutex_t lock; + void* priv; +}; + +void rt_hw_i2c_init(void); + +#endif diff --git a/bsp/fh8620/drivers/interrupt.c b/bsp/fh8620/drivers/interrupt.c new file mode 100644 index 0000000000000000000000000000000000000000..a865af17b3390c3b9b1f4e64e01a3b8efcd80040 --- /dev/null +++ b/bsp/fh8620/drivers/interrupt.c @@ -0,0 +1,216 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +/***************************************************************************** + * Include Section + * add all #include here + *****************************************************************************/ + +#include "interrupt.h" +#include "fh_def.h" +#include "fh_arch.h" +#include "Libraries/inc/fh_ictl.h" + +/***************************************************************************** + * Define section + * add all #define here + *****************************************************************************/ +#define MAX_HANDLERS (NR_INTERNAL_IRQS+NR_EXTERNAL_IRQS) + + +/**************************************************************************** + * ADT section + * add definition of user defined Data Type that only be used in this file here + ***************************************************************************/ + +/****************************************************************************** + * Function prototype section + * add prototypes for all functions called by this file,execepting those + * declared in header file + *****************************************************************************/ + + + +/***************************************************************************** + * Global variables section - Exported + * add declaration of global variables that will be exported here + * e.g. + * int8_t foo; + ****************************************************************************/ +extern rt_uint32_t rt_interrupt_nest; +struct rt_irq_desc irq_desc[MAX_HANDLERS]; +rt_uint32_t rt_interrupt_from_thread, rt_interrupt_to_thread; +rt_uint32_t rt_thread_switch_interrupt_flag; +/***************************************************************************** + * Global variables section - Local + * define global variables(will be refered only in this file) here, + * static keyword should be used to limit scope of local variable to this file + * e.g. + * static uint8_t ufoo; + *****************************************************************************/ + + + + /* function body */ + + + + + + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ +rt_isr_handler_t rt_hw_interrupt_handle(rt_uint32_t vector, void *param) +{ + rt_kprintf("Unhandled interrupt %d occured!!!\n", vector); + return RT_NULL; +} + + + + +/** + * This function will initialize hardware interrupt + */ +void rt_hw_interrupt_init(void) +{ + rt_int32_t i; + register rt_uint32_t idx; + fh_intc *p = (fh_intc *)INTC_REG_BASE; + + + ictl_close_all_isr(p); + /* init exceptions table */ + for(idx=0; idx < MAX_HANDLERS; idx++) + { + + irq_desc[idx].handler = (rt_isr_handler_t)rt_hw_interrupt_handle; + irq_desc[idx].param = RT_NULL; +#ifdef RT_USING_INTERRUPT_INFO + rt_snprintf(irq_desc[idx].name, RT_NAME_MAX - 1, "default"); + irq_desc[idx].counter = 0; +#endif + + } + + /* init interrupt nest, and context in thread sp */ + rt_interrupt_nest = 0; + rt_interrupt_from_thread = 0; + rt_interrupt_to_thread = 0; + rt_thread_switch_interrupt_flag = 0; + +} + +/** + * This function will mask a interrupt. + * @param vector the interrupt number + */ +void rt_hw_interrupt_mask(int irq) +{ + + fh_intc *p = (fh_intc *)INTC_REG_BASE; + /* Disable irq on AIC */ + ictl_mask_isr(p,irq); + +// if (irq < 32) +// p->IRQ_EN_L &= ~(1 << irq); +// else +// p->IRQ_EN_H &= ~(1 << (irq - 32)); +} + + +void rt_hw_interrupt_umask(int irq) +{ + + fh_intc *p = (fh_intc *)INTC_REG_BASE; + /* Enable irq on AIC */ + ictl_unmask_isr(p,irq); +// if (irq < 32) +// p->IRQ_EN_L |= 1 << irq; +// else +// p->IRQ_EN_H |= 1 << (irq - 32); +} + +/** + * This function will install a interrupt service routine to a interrupt. + * @param vector the interrupt number + * @param handler the interrupt service routine to be installed + * @param param the interrupt service function parameter + * @param name the interrupt name + * @return old handler + */ +rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, + void *param, char *name) +{ + rt_isr_handler_t old_handler = RT_NULL; + + if(vector < MAX_HANDLERS) + { + old_handler = irq_desc[vector].handler; + if (handler != RT_NULL) + { + irq_desc[vector].handler = (rt_isr_handler_t)handler; + irq_desc[vector].param = param; +#ifdef RT_USING_INTERRUPT_INFO + rt_snprintf(irq_desc[vector].name, RT_NAME_MAX - 1, "%s", name); + irq_desc[vector].counter = 0; +#endif + } + } + + return old_handler; +} + +#ifdef RT_USING_FINSH +void list_irq(void) +{ + +#ifdef RT_USING_INTERRUPT_INFO + int irq; + rt_kprintf("number\tcount\tname\n"); + for (irq = 0; irq < MAX_HANDLERS; irq++) + { + if (rt_strncmp(irq_desc[irq].name, "default", sizeof("default"))) + { + rt_kprintf("%02ld: %10ld %s\n", irq, irq_desc[irq].counter, irq_desc[irq].name); + } + } +#else + rt_kprintf("please open 'RT_USING_INTERRUPT_INFO'\n"); + +#endif +} +#include +FINSH_FUNCTION_EXPORT(list_irq, list system irq); + +#endif + diff --git a/bsp/fh8620/drivers/interrupt.h b/bsp/fh8620/drivers/interrupt.h new file mode 100644 index 0000000000000000000000000000000000000000..575b0a5442e1a65d249aea9072bf5b02ba282e64 --- /dev/null +++ b/bsp/fh8620/drivers/interrupt.h @@ -0,0 +1,40 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef INTERRUPT_H_ +#define INTERRUPT_H_ +#include + +#define NR_INTERNAL_IRQS 56 +#define NR_EXTERNAL_IRQS 64 + +void rt_hw_interrupt_init(void); +void rt_hw_interrupt_mask(int irq); +void rt_hw_interrupt_umask(int irq); +rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, + void *param, char *name); + +#endif /* INTERRUPT_H_ */ diff --git a/bsp/fh8620/drivers/mem_process.c b/bsp/fh8620/drivers/mem_process.c new file mode 100644 index 0000000000000000000000000000000000000000..4ef7e2d59976a906a202783afb8401de9a5ce83c --- /dev/null +++ b/bsp/fh8620/drivers/mem_process.c @@ -0,0 +1,87 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include "mmu.h" + +#define CHANGLINE_SIZE (1) + +//#define FH_DBG_MEM_PROCESS + +#ifdef FH_DBG_MEM_PROCESS +void mem_input(rt_uint32_t t_addr, rt_uint32_t t_size, rt_uint8_t t_value) { + + rt_kprintf("mem process add:%x \tsize:%x\tvalue:%x\n", t_addr, t_size, + t_value); + + rt_memset((void *) t_addr, t_value, t_size); + + mmu_clean_invalidated_dcache(t_addr, t_size); + +} + +void mem_output(rt_uint32_t t_addr, rt_uint32_t t_size) { + + rt_uint32_t i; + rt_uint32_t cnt = 0; + rt_uint32_t value; + rt_uint32_t addr, size; + + addr = t_addr; + if (t_size % 4) { + rt_kprintf("mem must be alligned\n"); + } + size = t_size / 4; + rt_int32_t *p = (rt_uint32_t *) t_addr; + + //mmu_clean_invalidated_dcache(addr,t_size); + rt_kprintf("mem process add:0x%x \tsize:0x%x\n", addr, t_size); + rt_kprintf("0x%08x:", addr); + for (i = 0; i < size; i++) { + value = *p++; + if ((cnt / CHANGLINE_SIZE) && (cnt % CHANGLINE_SIZE == 0)) { + rt_kprintf("\n"); + } + if (cnt / CHANGLINE_SIZE && (cnt % CHANGLINE_SIZE) == 0) { + rt_kprintf("0x%08x:", addr + i * 4); + } + rt_kprintf("\t%08x", value); + cnt++; + + } + rt_kprintf("\n"); + +} +#endif + +#ifdef RT_USING_FINSH +#include +#ifdef FH_DBG_MEM_PROCESS +FINSH_FUNCTION_EXPORT(mem_input, mem_input(addr,size,value)); +FINSH_FUNCTION_EXPORT(mem_output, mem_output(add,size)); +#endif +#endif diff --git a/bsp/fh8620/drivers/mmc.c b/bsp/fh8620/drivers/mmc.c new file mode 100644 index 0000000000000000000000000000000000000000..399efd58294d835d90e142d84055c17ef7241ef7 --- /dev/null +++ b/bsp/fh8620/drivers/mmc.c @@ -0,0 +1,710 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "board_info.h" +#include +#include +#include +#include "mmc.h" + +//#define FH_MMC_DEBUG +#define MMC_USE_INTERNAL_BUF + +#ifdef FH_MMC_DEBUG +#define PRINT_MMC_DBG(fmt, args...) \ + do \ + { \ + rt_kprintf("FH_MMC_DEBUG: tick-%d, ", rt_tick_get()); \ + rt_kprintf(fmt, ## args); \ + } \ + while(0) +#else +#define PRINT_MMC_DBG(fmt, args...) do { } while (0) +#endif + +#define PRINT_MMC_REGS(base) \ + do \ + { \ + int i_for_marco; \ + rt_uint32_t addr; \ + for(i_for_marco=0; i_for_marco<20; i_for_marco++) \ + { \ + addr = base + i_for_marco*4*4; \ + rt_kprintf("0x%x: 0x%x, 0x%x, 0x%x, 0x%x\n", addr, \ + GET_REG(addr+0x0), \ + GET_REG(addr+0x4), \ + GET_REG(addr+0x8), \ + GET_REG(addr+0xc)); \ + } \ + } \ + while(0) + + +#define MMC_INTERNAL_DMA_BUF_SIZE (32*1024) +static rt_uint32_t *g_mmc_dma_buf; + +static int fh_mmc_write_pio(struct mmc_driver *mmc_drv) +{ + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + struct rt_mmcsd_cmd *cmd = mmc_drv->cmd; + struct rt_mmcsd_data *data = RT_NULL; + rt_uint32_t size; + + if(cmd) + data = cmd->data; + + if(!data) + { + rt_kprintf("ERROR: %s, data is NULL\n", __func__); + return -RT_EIO; + } + + size = data->blks * data->blksize; + PRINT_MMC_DBG("%s, Send %d bytes\n", __func__, size); + MMC_WriteData(mmc_obj, data->buf, size); + MMC_ResetFifo(mmc_obj); + + return 0; +} + +static int fh_mmc_read_pio(struct mmc_driver *mmc_drv) +{ + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + struct rt_mmcsd_cmd *cmd = mmc_drv->cmd; + struct rt_mmcsd_data *data = RT_NULL; + rt_uint32_t size; + int ret; + + if(cmd) + data = cmd->data; + + if(!data) + { + rt_kprintf("ERROR: %s, data is NULL\n", __func__); + return -RT_EIO; + } + + size = data->blks * data->blksize; + PRINT_MMC_DBG("%s, read %d bytes\n", __func__, size); + ret = MMC_ReadData(mmc_obj, data->buf, size); + if(ret) + { + rt_kprintf("ERROR: %s, fifo IO error, ret: %d\n", __func__, ret); + return -RT_EIO; + } + + MMC_ResetFifo(mmc_obj); + + return 0; +} + + +static void fh_mmc_set_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg) +{ + rt_uint32_t clkdiv; + struct mmc_driver *mmc_drv = host->private_data; + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + + PRINT_MMC_DBG("%s start\n", __func__); + + //fixme: read from PMU + //why io_cfg->clock == 0 ? + if(io_cfg->clock) + { + clkdiv = MMC_CLOCK_IN / io_cfg->clock / 2; + MMC_UpdateClockRegister(mmc_obj, clkdiv); + PRINT_MMC_DBG("io_cfg->clock: %lu, clock in: %lu, clkdiv: %d\n", io_cfg->clock, MMC_CLOCK_IN, clkdiv); + } + + if (io_cfg->bus_width == MMCSD_BUS_WIDTH_4) + { + MMC_SetCardWidth(mmc_obj, MMC_CARD_WIDTH_4BIT); + PRINT_MMC_DBG("set to 4-bit mode\n", MMC_CLOCK_IN, clkdiv); + } + else + { + MMC_SetCardWidth(mmc_obj, MMC_CARD_WIDTH_1BIT); + PRINT_MMC_DBG("set to 1-bit mode\n", MMC_CLOCK_IN, clkdiv); + } + + + /* maybe switch power to the card */ + switch (io_cfg->power_mode) + { + case MMCSD_POWER_OFF: + break; + case MMCSD_POWER_UP: + break; + case MMCSD_POWER_ON: + break; + default: + rt_kprintf("ERROR: %s, unknown power_mode %d\n", __func__, io_cfg->power_mode); + break; + } + PRINT_MMC_DBG("%s end\n", __func__); +} + +static void fh_mmc_enable_sdio_irq(struct rt_mmcsd_host *host, rt_int32_t enable) +{ + struct mmc_driver *mmc_drv = host->private_data; + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + rt_uint32_t reg; + + PRINT_MMC_DBG("%s start\n", __func__); + + if (enable) + { + MMC_ClearRawInterrupt(mmc_obj, MMC_INT_STATUS_SDIO); + reg = MMC_GetInterruptMask(mmc_obj); + reg |= MMC_INT_STATUS_SDIO; + MMC_SetInterruptMask(mmc_obj, reg); + } + else + { + reg = MMC_GetInterruptMask(mmc_obj); + reg &= ~MMC_INT_STATUS_SDIO; + MMC_SetInterruptMask(mmc_obj, reg); + } + +} + +static rt_int32_t fh_mmc_get_card_status(struct rt_mmcsd_host *host) +{ + PRINT_MMC_DBG("%s, start\n", __func__); + PRINT_MMC_DBG("%s, end\n", __func__); + return 0; +} + +static void fh_mmc_send_command(struct mmc_driver *mmc_drv, struct rt_mmcsd_cmd *cmd) +{ + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + struct rt_mmcsd_host *host = mmc_drv->host; + struct rt_mmcsd_req *req = mmc_drv->req; + //fixme: cmd->data or req->data + struct rt_mmcsd_data *data = cmd->data; + int ret; + + rt_uint32_t retries = 0; + rt_uint32_t cmd_flags = 0; + + PRINT_MMC_DBG("%s, start\n", __func__); + + if (!cmd) + { + //fixme: stop dma + rt_kprintf("ERROR: %s, cmd is NULL\n", __func__); + return; + } + + if (data) + { + cmd_flags |= MMC_CMD_FLAG_DATA_EXPECTED; + /* always set data start - also set direction flag for read */ + if (data->flags & DATA_DIR_WRITE) + cmd_flags |= MMC_CMD_FLAG_WRITE_TO_CARD; + + if (data->flags & DATA_STREAM) + cmd_flags |= MMC_CMD_FLAG_DATA_STREAM; + } + + if (cmd == req->stop) + cmd_flags |= MMC_CMD_FLAG_STOP_TRANSFER; + else + cmd_flags |= MMC_CMD_FLAG_WAIT_PREV_DATA; + + switch (resp_type(cmd)) + { + case RESP_NONE: + break; + case RESP_R1: + case RESP_R5: + case RESP_R6: + case RESP_R7: + case RESP_R1B: + cmd_flags |= MMC_CMD_FLAG_RESPONSE_EXPECTED; + cmd_flags |= MMC_CMD_FLAG_CHECK_RESP_CRC; + break; + case RESP_R2: + cmd_flags |= MMC_CMD_FLAG_RESPONSE_EXPECTED; + cmd_flags |= MMC_CMD_FLAG_CHECK_RESP_CRC; + cmd_flags |= MMC_CMD_FLAG_LONG_RESPONSE; + break; + case RESP_R3: + case RESP_R4: + cmd_flags |= MMC_CMD_FLAG_RESPONSE_EXPECTED; + break; + default: + rt_kprintf("ERROR: %s, unknown cmd type %x\n", __func__, resp_type(cmd)); + return; + } + + if (cmd->cmd_code == GO_IDLE_STATE) + cmd_flags |= MMC_CMD_FLAG_SEND_INIT; + + /* CMD 11 check switch voltage */ + if (cmd->cmd_code == READ_DAT_UNTIL_STOP) + cmd_flags |= MMC_CMD_FLAG_SWITCH_VOLTAGE; + + PRINT_MMC_DBG("cmd code: %d, args: 0x%x, resp type: 0x%x, flag: 0x%x\n", cmd->cmd_code, cmd->arg, resp_type(cmd), cmd_flags); + ret = MMC_SendCommand(mmc_obj, cmd->cmd_code, cmd->arg, cmd_flags); + + if(ret) + { + rt_kprintf("ERROR: %s, Send command timeout, cmd: %d, status: 0x%x\n", __func__, cmd->cmd_code, MMC_GetStatus(mmc_obj)); + } + +} +static void fh_mmc_perpare_data(struct mmc_driver *mmc_drv) +{ + struct rt_mmcsd_cmd *cmd = mmc_drv->cmd; + struct rt_mmcsd_data *data = cmd->data; + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + rt_uint32_t data_size; + int i; + + if(!data) + { + MMC_SetBlockSize(mmc_obj, 0); + MMC_SetByteCount(mmc_obj, 0); + return; + } + + PRINT_MMC_DBG("%s, start\n", __func__); + + if(MMC_ResetFifo(mmc_obj)) + { + return; + } + + data_size = data->blks * data->blksize; + + MMC_SetBlockSize(mmc_obj, data->blksize); + + if(data_size % 4) + { + rt_kprintf("ERROR: data_size should be a multiple of 4, but now is %d\n", data_size); + } + MMC_SetByteCount(mmc_obj, data_size); + + PRINT_MMC_DBG("%s, set blk size: 0x%x, byte count: 0x%x\n", __func__, data->blksize, data_size); + + if(data_size > MMC_DMA_DESC_BUFF_SIZE * mmc_drv->max_desc) + { + rt_kprintf("ERROR: %s, given buffer is too big, size: 0x%x, max: 0x%x\n", __func__, data_size, MMC_DMA_DESC_BUFF_SIZE * mmc_drv->max_desc); + return; + } + + if (data_size > MMC_INTERNAL_DMA_BUF_SIZE) + { + rt_kprintf("ERROR: please increase MMC_INTERNAL_DMA_BUF_SIZE.\n"); + return; + } + +#ifdef MMC_USE_DMA +#ifdef MMC_USE_INTERNAL_BUF + if (data->flags & DATA_DIR_WRITE) + { + rt_memcpy(g_mmc_dma_buf, data->buf, data_size); + mmu_clean_invalidated_dcache(g_mmc_dma_buf, data_size); + } + else + { + mmu_invalidate_dcache(g_mmc_dma_buf, data_size); + } + MMC_InitDescriptors(mmc_obj, (rt_uint32_t*)g_mmc_dma_buf, data_size); + mmu_clean_invalidated_dcache(mmc_obj->descriptors, sizeof(MMC_DMA_Descriptors) * mmc_drv->max_desc); + MMC_StartDma(mmc_obj); +#else + MMC_InitDescriptors(mmc_obj, data->buf, data_size); + mmu_clean_invalidated_dcache(mmc_obj->descriptors, sizeof(MMC_DMA_Descriptors) * mmc_drv->max_desc); + mmu_clean_invalidated_dcache(data->buf, data_size); + MMC_StartDma(mmc_obj); +#endif +#endif + PRINT_MMC_DBG("%s, end\n", __func__); +} + +int fh_mmc_wait_card_idle(struct fh_mmc_obj *mmc_obj) +{ + rt_uint32_t tick, timeout; + + tick = rt_tick_get(); + timeout = tick + RT_TICK_PER_SECOND / 2; //500ms + + while(MMC_GetStatus(mmc_obj) & MMC_STATUS_DATA_BUSY) + { + tick = rt_tick_get(); + if(tick > timeout) + { + return -RT_ETIMEOUT; + } + } + + return 0; +} + +static int fh_mmc_get_response(struct mmc_driver *mmc_drv, struct rt_mmcsd_cmd *cmd) +{ + int i; + rt_uint32_t tick, timeout, status; + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + + cmd->resp[0] = 0; + cmd->resp[1] = 0; + cmd->resp[2] = 0; + cmd->resp[3] = 0; + + tick = rt_tick_get(); + timeout = tick + RT_TICK_PER_SECOND / 2; //500ms + + //fixme: spin_lock_irqsave? + do + { + status = MMC_GetRawInterrupt(mmc_obj); + tick = rt_tick_get(); + if(tick > timeout) + { + PRINT_MMC_DBG("ERROR: %s, get response timeout(cmd is not received by card), RINTSTS: 0x%x, cmd: %d\n", __func__, status, cmd->cmd_code); + return -RT_ETIMEOUT; + } + } + while(!(status & MMC_INT_STATUS_CMD_DONE)); + + MMC_ClearRawInterrupt(mmc_obj, MMC_INT_STATUS_CMD_DONE); + + for (i = 0; i < 4; i++) + { + if (resp_type(cmd) == RESP_R2) + { + cmd->resp[i] = MMC_GetResponse(mmc_obj, 3 - i); + //fixme : R2 must delay some time here ,when use UHI card, need check why + //1ms + //rt_thread_sleep(RT_TICK_PER_SECOND / 100); + } + else + { + cmd->resp[i] = MMC_GetResponse(mmc_obj, i); + } + } + + PRINT_MMC_DBG("resp: 0x%x, 0x%x, 0x%x, 0x%x\n", cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); + + if (status & MMC_INT_STATUS_RESPONSE_TIMEOUT) + { + MMC_ClearRawInterrupt(mmc_obj, MMC_INT_STATUS_RESPONSE_TIMEOUT); + PRINT_MMC_DBG("ERROR: %s, get response timeout, RINTSTS: 0x%x\n", __func__, status); + return -RT_ETIMEOUT; + } + + else if (status & (MMC_INT_STATUS_RESP_CRC_ERROR | MMC_INT_STATUS_RESPONSE_ERROR)) + { + MMC_ClearRawInterrupt(mmc_obj, MMC_INT_STATUS_RESP_CRC_ERROR | MMC_INT_STATUS_RESPONSE_ERROR); + rt_kprintf("ERROR: %s, response error or response crc error, RINTSTS: 0x%x\n", __func__, status); + //return -RT_ERROR; + } + + return 0; +} + +static int fh_mmc_start_transfer(struct mmc_driver *mmc_drv) +{ + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + struct rt_mmcsd_host *host = mmc_drv->host; + struct rt_mmcsd_req *req = mmc_drv->req; + struct rt_mmcsd_cmd *cmd = mmc_drv->cmd; + struct rt_mmcsd_data *data = RT_NULL; + int ret; + rt_uint32_t interrupt, status, reg; + + if(cmd) + data = cmd->data; + + if(!data) + { + return 0; + } + + PRINT_MMC_DBG("%s, start\n", __func__); + + //fixme: spin_lock_irqsave(&host->lock, flags); + //open data interrupts + reg = MMC_GetInterruptMask(mmc_obj); + reg |= MMC_INT_STATUS_DATA; + MMC_SetInterruptMask(mmc_obj, reg); + + //fixme: spin_unlock_irqrestore(&host->lock, flags); + ret = rt_completion_wait(&mmc_drv->transfer_completion, RT_TICK_PER_SECOND * 5); + + reg = MMC_GetInterruptMask(mmc_obj); + reg &= ~MMC_INT_STATUS_DATA; + MMC_SetInterruptMask(mmc_obj, reg); + + if(ret) + { + //fixme: error handle + cmd->err = ret; + interrupt = MMC_GetRawInterrupt(mmc_obj); + status = MMC_GetStatus(mmc_obj); + rt_kprintf("ERROR: %s, transfer timeout, ret: %d, RINTSTS: 0x%x, STATUS: 0x%x\n", __func__, ret, interrupt, status); + //PRINT_MMC_REGS(mmc_obj->base); + return -RT_ETIMEOUT; + } + + data->bytes_xfered = data->blks * data->blksize; + +#ifdef MMC_USE_INTERNAL_BUF + if (!(data->flags & DATA_DIR_WRITE)) + { + rt_memcpy(data->buf, g_mmc_dma_buf, data->bytes_xfered); + mmu_invalidate_dcache(g_mmc_dma_buf, data->bytes_xfered); + } +#endif + + return 0; +} + +static void fh_mmc_complete_request(struct mmc_driver *mmc_drv) +{ + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; +#ifdef MMC_USE_DMA + MMC_StopDma(mmc_obj); +#endif + mmc_drv->cmd = RT_NULL; + mmc_drv->req = RT_NULL; + mmc_drv->data = RT_NULL; + + rt_memset(mmc_obj->descriptors, 0, 4096); + + MMC_SetBlockSize(mmc_obj, 0); + MMC_SetByteCount(mmc_obj, 0); + + mmcsd_req_complete(mmc_drv->host); +} + +static void fh_mmc_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) +{ + int ret; + struct mmc_driver *mmc_drv = host->private_data; + struct rt_mmcsd_cmd *cmd = req->cmd; + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + + PRINT_MMC_DBG("%s start\n", __func__); + + mmc_drv->req = req; + mmc_drv->cmd = cmd; + + rt_completion_init(&mmc_drv->transfer_completion); + + ret = fh_mmc_wait_card_idle(mmc_obj); + + if (ret) + { + rt_kprintf("ERROR: %s, data transfer timeout, status: 0x%x\n", __func__, MMC_GetStatus(mmc_obj)); + return; + } + + fh_mmc_perpare_data(mmc_drv); + fh_mmc_send_command(mmc_drv, cmd); + ret = fh_mmc_get_response(mmc_drv, cmd); + if(ret) + { + cmd->err = ret; + rt_kprintf("%s,get response returns %d, cmd: %d\n", __func__, ret, cmd->cmd_code); + goto out; + } + fh_mmc_start_transfer(mmc_drv); + + if(req->stop) + { + /* send stop command */ + PRINT_MMC_DBG("%s send stop\n", __func__); + fh_mmc_send_command(mmc_drv, req->stop); + } + +out: + fh_mmc_complete_request(mmc_drv); + PRINT_MMC_DBG("%s end\n", __func__); +} + +static const struct rt_mmcsd_host_ops fh_mmc_ops = +{ + .request = fh_mmc_request, + .set_iocfg = fh_mmc_set_iocfg, + .enable_sdio_irq = fh_mmc_enable_sdio_irq, + .get_card_status = fh_mmc_get_card_status, +}; + +static void fh_mmc_interrupt(int irq, void *param) +{ + struct mmc_driver *mmc_drv = (struct mmc_driver *)param; + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)mmc_drv->priv; + struct rt_mmcsd_req *req = mmc_drv->req; + struct rt_mmcsd_cmd *cmd = mmc_drv->cmd; + struct rt_mmcsd_data *data; + rt_uint32_t status; + + if (cmd && cmd->data) + { + data = cmd->data; + } + + status = MMC_GetUnmaskedInterrupt(mmc_obj); + PRINT_MMC_DBG("unmasked interrupts: 0x%x\n", status); + + if(status & MMC_INT_STATUS_CARD_DETECT) + { + rt_uint32_t card_status = MMC_GetCardStatus(mmc_obj); + + if (card_status == CARD_UNPLUGED) + { + rt_kprintf("card disconnected\n"); + } + else + { + rt_kprintf("card connected\n"); + } + mmcsd_change(mmc_drv->host); + } + + if (status & MMC_INT_STATUS_SDIO) + { + //fixme: handle sdio + //mmc_signal_sdio_irq ? + } + + if(status & MMC_INIT_STATUS_DATA_ERROR) + { + rt_kprintf("ERROR: %s, data error, status: 0x%x\n", __func__, status); + } + + if (status & MMC_INT_STATUS_TRANSFER_OVER) + { + //MMC_ResetFifo(mmc_obj); + //rt_completion_done(&mmc_drv->transfer_completion); + } + + if (status & MMC_INT_STATUS_TX_REQUEST) + { + fh_mmc_write_pio(mmc_drv); + } + + if (status & MMC_INT_STATUS_RX_REQUEST) + { + fh_mmc_read_pio(mmc_drv); + } + + MMC_ClearRawInterrupt(mmc_obj, MMC_INT_STATUS_ALL); + rt_completion_done(&mmc_drv->transfer_completion); +} + +int fh_mmc_probe(void *priv_data) +{ + struct mmc_driver *mmc_drv; + struct rt_mmcsd_host *host; + struct fh_mmc_obj *mmc_obj = (struct fh_mmc_obj *)priv_data; + + PRINT_MMC_DBG("%s start\n", __func__); + + mmc_drv = (struct mmc_driver*)rt_malloc(sizeof(struct mmc_driver)); + rt_memset(mmc_drv, 0, sizeof(struct mmc_driver)); + mmc_drv->priv = mmc_obj; + + host = mmcsd_alloc_host(); + if (!host) + { + rt_kprintf("ERROR: %s, failed to malloc host\n", __func__); + return -RT_ENOMEM; + } + + mmc_obj->descriptors = (MMC_DMA_Descriptors*)rt_malloc(4096+64); + mmc_obj->descriptors = (MMC_DMA_Descriptors*)(((UINT32)(mmc_obj->descriptors)+31)&(~31)); //cache-line aligned... + + g_mmc_dma_buf = rt_malloc(MMC_INTERNAL_DMA_BUF_SIZE+64); + g_mmc_dma_buf = (rt_uint32_t*)(((rt_uint32_t)g_mmc_dma_buf+31) & (~31)); + + if(!mmc_obj->descriptors) + { + rt_kprintf("ERROR: %s, failed to malloc dma descriptors\n", __func__); + return -RT_ENOMEM; + } + + rt_memset(mmc_obj->descriptors, 0, 4096); + mmc_drv->max_desc = 4096 / (sizeof(MMC_DMA_Descriptors)); + + host->ops = &fh_mmc_ops; + host->freq_min = MMC_FEQ_MIN; + host->freq_max = MMC_FEQ_MAX; + host->valid_ocr = VDD_32_33 | VDD_33_34; + + host->flags = MMCSD_MUTBLKWRITE | \ + MMCSD_SUP_HIGHSPEED | MMCSD_SUP_SDIO_IRQ; + host->max_seg_size = MMC_DMA_DESC_BUFF_SIZE; + host->max_dma_segs = mmc_drv->max_desc; + host->max_blk_size = 512; + //fixme: max_blk_count? + host->max_blk_count = 2048; + host->private_data = mmc_drv; + + mmc_drv->host = host; + gpio_request(mmc_obj->power_pin_gpio); + gpio_direction_output(mmc_obj->power_pin_gpio, 0); + + MMC_Init(mmc_obj); + + if(mmc_obj->id == 0){ + rt_hw_interrupt_install(mmc_obj->irq, fh_mmc_interrupt, (void *)mmc_drv, "mmc_isr_0"); + } + else if(mmc_obj->id == 1){ + rt_hw_interrupt_install(mmc_obj->irq, fh_mmc_interrupt, (void *)mmc_drv, "mmc_isr_1"); + } + + rt_hw_interrupt_umask(mmc_obj->irq); + mmcsd_change(host); + + MMC_SetInterruptMask(mmc_obj, MMC_INT_STATUS_CARD_DETECT); + + PRINT_MMC_DBG("%s end\n", __func__); + + return 0; +} + +int fh_mmc_exit(void *priv_data) +{ + return 0; +} + +struct fh_board_ops mmc_driver_ops = +{ + .probe = fh_mmc_probe, + .exit = fh_mmc_exit, +}; + +void rt_hw_mmc_init(void) +{ + PRINT_MMC_DBG("%s start\n", __func__); + fh_board_driver_register("mmc", &mmc_driver_ops); + PRINT_MMC_DBG("%s end\n", __func__); +} diff --git a/bsp/fh8620/drivers/mmc.h b/bsp/fh8620/drivers/mmc.h new file mode 100644 index 0000000000000000000000000000000000000000..38a404e85740111f49c595166f4db48c04695a5b --- /dev/null +++ b/bsp/fh8620/drivers/mmc.h @@ -0,0 +1,51 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef MMC_H_ +#define MMC_H_ + +#include "Libraries/inc/fh_driverlib.h" +#define MMC_FEQ_MIN 100000 +#define MMC_FEQ_MAX 50000000 + +#define CARD_UNPLUGED 1 +#define CARD_PLUGED 0 + +struct mmc_driver +{ + MMC_DMA_Descriptors* dma_descriptors; + rt_uint32_t max_desc; + struct rt_mmcsd_host *host; + struct rt_mmcsd_req *req; + struct rt_mmcsd_data *data; + struct rt_mmcsd_cmd *cmd; + struct rt_completion transfer_completion; + void* priv; +}; + +void rt_hw_mmc_init(void); + +#endif /* MMC_H_ */ diff --git a/bsp/fh8620/drivers/pwm.c b/bsp/fh8620/drivers/pwm.c new file mode 100644 index 0000000000000000000000000000000000000000..c6c20a259e508d9f62fae391e32ed79c18c4674e --- /dev/null +++ b/bsp/fh8620/drivers/pwm.c @@ -0,0 +1,226 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "fh_def.h" +#include "pwm.h" +#include "interrupt.h" +#include "board_info.h" +#include "inc/fh_driverlib.h" +#include +#ifdef FH_PWM_DEBUG +#define PRINT_PWM_DBG(fmt, args...) \ + do \ + { \ + rt_kprintf("FH_PWM_DEBUG: "); \ + rt_kprintf(fmt, ## args); \ + } \ + while(0) +#else +#define PRINT_PWM_DBG(fmt, args...) do { } while (0) +#endif + + +static struct pwm_driver pwm_drv = +{ + +}; + + + + +static int pwm_get_duty_cycle_ns(struct pwm_device* pwm) +{ + struct fh_pwm_obj *pwm_obj = (struct fh_pwm_obj *)pwm_drv.priv; + rt_uint32_t reg, period, duty; + rt_uint32_t clk_rate = 1000000/*todo: clk_get_rate(fh_pwm_ctrl.clk)*/; + + reg = PWM_GetPwmCmd(pwm_obj, pwm->id); + period = reg & 0x0fff; + duty = (reg >> 16) & 0xfff; + duty = period - duty; //reverse duty cycle + + if(period == 0) + { + period = duty; + } + + pwm->counter_ns = duty * 1000000000 / clk_rate; + pwm->period_ns = period * 1000000000 / clk_rate; + + PRINT_PWM_DBG("get duty: %d, period: %d, reg: 0x%x\n", duty, period, reg); + + return 0; +} + +static int pwm_set_duty_cycle_ns(struct pwm_device* pwm) +{ + struct fh_pwm_obj *pwm_obj = (struct fh_pwm_obj *)pwm_drv.priv; + rt_uint32_t period, duty, reg, clk_rate, duty_revert; + clk_rate = 1000000/*todo: clk_get_rate(fh_pwm_ctrl.clk)*/; + if(!clk_rate) + { + rt_kprintf("PWM: clock rate is 0\n"); + return -RT_EIO; + } + period = pwm->period_ns / (1000000000 / clk_rate); + + if(period < 8) + { + rt_kprintf("PWM: min period is 8\n"); + return -RT_EIO; + } + + duty = pwm->counter_ns / (1000000000 / clk_rate); + + if(period < duty) + { + rt_kprintf("PWM: period < duty\n"); + return -RT_EIO; + } + + duty_revert = period - duty; + + if(duty == period) + { + reg = (duty & 0xfff) << 16 | (0 & 0xfff); + } + else + { + reg = (duty_revert & 0xfff) << 16 | (period & 0xfff); + } + + PRINT_PWM_DBG("set duty_revert: %d, period: %d, reg: 0x%x\n", duty_revert, period, reg); + + PWM_SetPwmCmd(pwm_obj, pwm->id, reg); + return 0; +} + + + + +static rt_err_t fh_pwm_open(rt_device_t dev, rt_uint16_t oflag) +{ + struct fh_pwm_obj *pwm_obj = (struct fh_pwm_obj *)pwm_drv.priv; + PWM_Enable(pwm_obj, RT_TRUE); + return 0; +} + +static rt_err_t fh_pwm_close(rt_device_t dev) +{ + struct fh_pwm_obj *pwm_obj = (struct fh_pwm_obj *)pwm_drv.priv; + PWM_Enable(pwm_obj, RT_FALSE); + return 0; +} + +static rt_err_t fh_pwm_ioctl(rt_device_t dev, rt_uint8_t cmd, void *arg) +{ + int ret = 0; + struct pwm_device *pwm; + struct fh_pwm_obj *pwm_obj = (struct fh_pwm_obj *)pwm_drv.priv; + + switch(cmd) + { + case ENABLE_PWM: + PWM_Enable(pwm_obj, RT_FALSE); + break; + case DISABLE_PWM: + PWM_Enable(pwm_obj, RT_TRUE); + break; + case SET_PWM_DUTY_CYCLE: + pwm = (struct pwm_device *)arg; + PRINT_PWM_DBG("ioctl: pwm addr: %p, pwm->period: %d ns\n", pwm, pwm->period_ns); + pwm_set_duty_cycle_ns(pwm); + break; + case GET_PWM_DUTY_CYCLE: + pwm = (struct pwm_device *)arg; + PRINT_PWM_DBG("ioctl: pwm->id: %d, pwm->counter: %d, pwm->period: %d\n", pwm->id, pwm->counter_ns, pwm->period_ns); + pwm_get_duty_cycle_ns(pwm); + break; + } + + return ret; +} + +int fh_pwm_probe(void *priv_data) +{ + rt_device_t pwm_dev ; + struct fh_pwm_obj *pwm_obj = (struct fh_pwm_obj *)priv_data; + + rt_memset(&pwm_drv, 0, sizeof(struct pwm_driver)); + + pwm_drv.pwm[0].id = 0; + pwm_drv.pwm[1].id = 1; + pwm_drv.pwm[2].id = 2; + + pwm_drv.pwm[0].working = 0; + pwm_drv.pwm[1].working = 0; + pwm_drv.pwm[2].working = 0; + + pwm_drv.priv = pwm_obj; + + //todo: clk + + PWM_Enable(pwm_obj, RT_FALSE); + + pwm_dev = rt_malloc(sizeof(struct rt_device)); + rt_memset(pwm_dev, 0, sizeof(struct rt_device)); + + if (pwm_dev == RT_NULL) + { + rt_kprintf("ERROR: %s rt_device malloc failed\n", __func__); + } + + pwm_dev->user_data = &pwm_drv; + pwm_dev->open =fh_pwm_open; + pwm_dev->close = fh_pwm_close; + pwm_dev->control = fh_pwm_ioctl; + pwm_dev->type = RT_Device_Class_Miscellaneous; + + rt_device_register(pwm_dev, "pwm", RT_DEVICE_FLAG_RDWR); + + + + return 0; +} + +int fh_pwm_exit(void *priv_data) +{ + return 0; +} + +struct fh_board_ops pwm_driver_ops = +{ + .probe = fh_pwm_probe, + .exit = fh_pwm_exit, +}; + +void rt_hw_pwm_init(void) +{ + PRINT_PWM_DBG("%s start\n", __func__); + fh_board_driver_register("pwm", &pwm_driver_ops); + PRINT_PWM_DBG("%s end\n", __func__); +} + diff --git a/bsp/fh8620/drivers/pwm.h b/bsp/fh8620/drivers/pwm.h new file mode 100644 index 0000000000000000000000000000000000000000..3693ab7973d48df20a799c16f41dcec2df7b7bfa --- /dev/null +++ b/bsp/fh8620/drivers/pwm.h @@ -0,0 +1,57 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef PWM_H_ +#define PWM_H_ + + +#include +#define ENABLE_PWM (0x10) +#define DISABLE_PWM (0x11) + +#define SET_PWM_DUTY_CYCLE (0x12) +#define GET_PWM_DUTY_CYCLE (0x13) + +struct pwm_device +{ + int id; + int working; + rt_uint32_t period_ns; + rt_uint32_t counter_ns; +}; + +struct pwm_driver +{ + //struct clk *clk; + struct pwm_device pwm[3]; + struct pwm_device *cur; + void* priv; + +}; + + + +#endif /* PWM_H_ */ diff --git a/bsp/fh8620/drivers/sadc.c b/bsp/fh8620/drivers/sadc.c new file mode 100644 index 0000000000000000000000000000000000000000..bc994e5218fe7d95a5f75e409a3f37c7fb5ce6e0 --- /dev/null +++ b/bsp/fh8620/drivers/sadc.c @@ -0,0 +1,416 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "sadc.h" +#ifdef RT_USING_SADC +#include "inc/fh_driverlib.h" +#include "board_info.h" +#include + +//#define FH_SADC_DEBUG +//#define FH_TEST_SADC + + +#ifdef FH_SADC_DEBUG +#define PRINT_SADC_DBG(fmt, args...) \ + do \ + { \ + rt_kprintf("FH_SADC_DEBUG: "); \ + rt_kprintf(fmt, ## args); \ + } \ + while(0) +#else +#define PRINT_SADC_DBG(fmt, args...) do { } while (0) +#endif + + + + + +#define __raw_writeb(v,a) ( *(volatile unsigned char *)(a) = (v)) +#define __raw_writew(v,a) ( *(volatile unsigned short *)(a) = (v)) +#define __raw_writel(v,a) ( *(volatile unsigned int *)(a) = (v)) + +#define __raw_readb(a) ( *(volatile unsigned char *)(a)) +#define __raw_readw(a) ( *(volatile unsigned short *)(a)) +#define __raw_readl(a) ( *(volatile unsigned int *)(a)) + + +#define wrap_readl(wrap, name) \ + __raw_readl(&(((struct wrap_sadc_reg *)wrap->regs)->name)) + +#define wrap_writel(wrap, name, val) \ + __raw_writel((val), &(((struct wrap_sadc_reg *)wrap->regs)->name)) + +#define wrap_readw(wrap, name) \ + __raw_readw(&(((struct wrap_sadc_reg *)wrap->regs)->name)) + +#define wrap_writew(wrap, name, val) \ + __raw_writew((val), &(((struct wrap_sadc_reg *)wrap->regs)->name)) + +#define wrap_readb(wrap, name) \ + __raw_readb(&(((struct wrap_sadc_reg *)wrap->regs)->name)) + +#define wrap_writeb(wrap, name, val) \ + __raw_writeb((val), &(((struct wrap_sadc_reg *)wrap->regs)->name)) + + + +#define IOCTL_GET_SADC_DATA 1 +#define IOCTL_SADC_POWER_DOWN 0xff +#define SADC_WRAP_BASE (0xf1200000) +#define SADC_IRQn (23) +#define SADC_MAX_CONTROLLER (1) +#define SADC_STATUS_COLESD (0) +#define SADC_STATUS_OPEN (1) + + + +rt_err_t fh_sadc_isr_read_data(struct wrap_sadc_obj *sadc, rt_uint32_t channel, + rt_uint16_t *buf) { + rt_uint32_t xainsel = 1 << channel; + rt_uint32_t xversel = 0; + rt_uint32_t xpwdb = 1; + //cnt + rt_uint32_t sel2sam_pre_cnt = 2; + rt_uint32_t sam_cnt = 2; + rt_uint32_t sam2sel_pos_cnt = 2; + //time out + rt_uint32_t eoc_tos = 0xff; + rt_uint32_t eoc_toe = 0xff; + rt_uint32_t time_out = 0xffff; + //set isr en.. + rt_uint32_t sadc_isr = 0x01; + //start + rt_uint32_t sadc_cmd = 0x01; + //get data + rt_uint32_t temp_data = 0; + rt_err_t ret; + + + //control... + wrap_writel(sadc, sadc_control, xainsel | (xversel << 8) | (xpwdb << 12)); + + + wrap_writel(sadc, sadc_cnt, + sel2sam_pre_cnt | (sam_cnt << 8) | (sam2sel_pos_cnt << 16)); + + wrap_writel(sadc, sadc_timeout, + eoc_tos | (eoc_toe << 8) | (time_out << 16)); + + wrap_writel(sadc, sadc_ier, sadc_isr); + + wrap_writel(sadc, sadc_cmd, sadc_cmd); + + + // ret = rt_completion_wait(&sadc->completion, RT_TICK_PER_SECOND / 2); + + ret = rt_sem_take(&sadc->completion, 5000); + if(ret != RT_EOK) + return ret; + + switch (channel) { + case 0: + case 1: + //read channel 0 1 + temp_data = wrap_readl(sadc, sadc_dout0); + break; + + case 2: + case 3: + //read channel 2 3 + temp_data = wrap_readl(sadc, sadc_dout1); + break; + + case 4: + case 5: + //read channel 4 5 + temp_data = wrap_readl(sadc, sadc_dout2); + break; + + case 6: + case 7: + //read channel 6 7 + temp_data = wrap_readl(sadc, sadc_dout3); + break; + default: + break; + } + if (channel % 2) { + //read low 16bit + *buf = (rt_uint16_t) (temp_data & 0xffff); + } else { + //read high 16bit + *buf = (rt_uint16_t) (temp_data >> 16); + } + return RT_EOK; + +} + + + + + + +static rt_err_t fh_sadc_init(rt_device_t dev) +{ + + // struct fh_pwm_obj *pwm_obj = (struct fh_pwm_obj *)pwm_drv.priv; + PRINT_SADC_DBG("%s\n",__func__); + struct wrap_sadc_obj *sadc_pri =(struct wrap_sadc_obj *)dev->user_data; + return RT_EOK; +} + + + +static rt_err_t fh_sadc_open(rt_device_t dev, rt_uint16_t oflag) +{ + // struct fh_pwm_obj *pwm_obj = (struct fh_pwm_obj *)pwm_drv.priv; + PRINT_SADC_DBG("%s\n",__func__); + struct wrap_sadc_obj *sadc_pri =(struct wrap_sadc_obj *)dev->user_data; + return RT_EOK; +} + +static rt_err_t fh_sadc_close(rt_device_t dev) +{ + PRINT_SADC_DBG("%s\n",__func__); + struct wrap_sadc_obj *sadc_pri =(struct wrap_sadc_obj *)dev->user_data; + return RT_EOK; +} + +static rt_err_t fh_sadc_ioctl(rt_device_t dev, rt_uint8_t cmd, void *arg) +{ + + rt_uint32_t control_reg; + struct wrap_sadc_obj *sadc_pri =(struct wrap_sadc_obj *)dev->user_data; + rt_uint32_t ad_data; + rt_uint16_t ad_raw_data; + + SADC_INFO *sadc_info = (SADC_INFO *)arg; + rt_err_t ret; + switch(cmd){ + case SADC_CMD_READ_RAW_DATA: + ret = fh_sadc_isr_read_data(sadc_pri, sadc_info->channel, &ad_raw_data); + if(ret != RT_EOK) + return ret; + sadc_info->sadc_data = ad_raw_data; + + break; + case SADC_CMD_READ_VOLT: + ret = fh_sadc_isr_read_data(sadc_pri, sadc_info->channel, &ad_raw_data); + if(ret != RT_EOK) + return ret; + + ad_data = ad_raw_data * SADC_REF; + ad_data /= SADC_MAX_AD_VALUE; + sadc_info->sadc_data = ad_data; + + break; + case SADC_CMD_DISABLE: + control_reg = wrap_readl(sadc_pri, sadc_control); + control_reg &= ~(1 << 12); + wrap_writel(sadc_pri, sadc_control, control_reg); + + break; + default : + rt_kprintf("wrong para...\n"); + return RT_EIO; + } + + return RT_EOK; +} + + + +static void fh_sadc_interrupt(int irq, void *param) +{ + + rt_uint32_t isr_status; + struct wrap_sadc_obj *sadc = (struct wrap_sadc_obj *) param; + + isr_status = wrap_readl(sadc, sadc_int_status); + + if (isr_status & 0x01) { + //close isr + rt_uint32_t sadc_isr = 0x00; + + wrap_writel(sadc, sadc_ier, sadc_isr); + //clear status.. + + wrap_writel(sadc, sadc_int_status, isr_status); + + rt_sem_release(&sadc->completion); + // rt_completion_done(&sadc->completion); + } else { + //add error handle process + rt_kprintf("sadc maybe error!\n"); + } + + +} + + +int fh_sadc_probe(void *priv_data) +{ + int ret; + + rt_device_t sadc_dev; + //check if the hw is init already... + //caution this is a read only data...if the driver want to use.malloc and copy it.. + struct wrap_sadc_obj *sadc_obj = (struct wrap_sadc_obj *)priv_data; + if(sadc_obj->init_flag == SADC_INIT_ALREADY) + return RT_EFULL; + + + //malloc a rt device.. + sadc_dev = RT_KERNEL_MALLOC(sizeof(struct rt_device)); + if(!sadc_dev){ + return RT_ENOMEM; + } + rt_memset(sadc_dev, 0, sizeof(struct rt_device)); + PRINT_SADC_DBG("id:%d,\treg:%x,\tirq:%d\n",sadc_obj->id,(rt_uint32_t)sadc_obj->regs,sadc_obj->irq_no); + + //bind rtdev to obj data... + //caution ...this is used to free mem when exit.... + //free step:1:get sadc obj...2:free sadc_obj->rt_dev->user_data..3:free sadc_obj->rt_dev 4:sadc_obj->rt_dev = NULL + sadc_obj->rt_dev = sadc_dev; + + + + //malloc a private data sadc use only...copy data from platform... + struct wrap_sadc_obj *sadc_pri = RT_KERNEL_MALLOC(sizeof(struct wrap_sadc_obj)); + if(!sadc_pri){ + + RT_KERNEL_FREE(sadc_dev); + return RT_ENOMEM; + } + + //copy platform data to pri data.. + rt_memcpy(sadc_pri,sadc_obj,sizeof(struct wrap_sadc_obj)); + + PRINT_SADC_DBG("pri....id:%d,\treg:%x,\tirq:%d\n",sadc_pri->id,(rt_uint32_t)sadc_pri->regs,sadc_pri->irq_no); + + + + //init sem + //rt_completion_init(&sadc_obj->completion); + rt_sem_init(&sadc_pri->completion, "sadc_sem", 0, RT_IPC_FLAG_FIFO); + + //init lock + rt_mutex_init(&sadc_pri->lock,"sadc_lock", RT_IPC_FLAG_FIFO); + + + //bind pri data to rt_sadc_dev... + sadc_dev->user_data = (void *)sadc_pri; + sadc_dev->open =fh_sadc_open; + sadc_dev->close = fh_sadc_close; + sadc_dev->control = fh_sadc_ioctl; + sadc_dev->init = fh_sadc_init; + + if(sadc_pri->id ==0){ + rt_hw_interrupt_install(sadc_pri->irq_no, fh_sadc_interrupt, + (void *)sadc_pri, "sadc_isr_0"); + } + + rt_hw_interrupt_umask(sadc_pri->irq_no); + + rt_device_register(sadc_dev, "sadc", RT_DEVICE_FLAG_RDWR); + + sadc_obj->init_flag = SADC_INIT_ALREADY; + + return RT_EOK; + +} + + +int fh_sadc_exit(void *priv_data) +{ + + PRINT_SADC_DBG("%s\n",__func__); + struct wrap_sadc_obj *sadc_obj = (struct wrap_sadc_obj *)priv_data; + + struct wrap_sadc_obj *sadc_pri = sadc_obj->rt_dev->user_data; + //release sem; + rt_sem_detach(&sadc_pri->completion); + //sadc_pri->completion = RT_NULL; + + //release lock; + rt_mutex_detach(&sadc_pri->lock); + + RT_KERNEL_FREE(sadc_obj->rt_dev->user_data); + + + sadc_obj->rt_dev->user_data = RT_NULL; + RT_KERNEL_FREE(sadc_obj->rt_dev); + sadc_obj->rt_dev = RT_NULL; + + return 0; +} + +struct fh_board_ops sdac_driver_ops = +{ + .probe = fh_sadc_probe, + .exit = fh_sadc_exit, +}; + +void rt_hw_sadc_init(void) +{ + int ret; + fh_board_driver_register("sadc", &sdac_driver_ops); +} + + +#ifdef FH_TEST_SADC +int fh_sadc_test(void){ + + rt_device_t sadc_dev; + SADC_INFO info; + info.channel = 0; + info.sadc_data = 0; + sadc_dev = rt_device_find("sadc"); + if(!sadc_dev){ + rt_kprintf("cann't find the sadc dev\n"); + } + sadc_dev->init(sadc_dev); + sadc_dev->open(sadc_dev,0); + while(1) + { + sadc_dev->control(sadc_dev,SADC_CMD_READ_VOLT,&info); + rt_kprintf("channel:%d,volt:%dmv\n",info.channel,info.sadc_data); + } + + return 0; +} +#endif + +#ifdef RT_USING_FINSH +#include +#ifdef FH_TEST_SADC +FINSH_FUNCTION_EXPORT(fh_sadc_test, fh_sadc_test); +#endif +#endif + +#endif diff --git a/bsp/fh8620/drivers/sadc.h b/bsp/fh8620/drivers/sadc.h new file mode 100644 index 0000000000000000000000000000000000000000..d28ed9ba246598e6431a64cc133df4b6cf088c41 --- /dev/null +++ b/bsp/fh8620/drivers/sadc.h @@ -0,0 +1,109 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef SADC_H_ +#define SADC_H_ + +#include +#ifdef RT_USING_SADC + + +/**************************************************************************** + * #define section + * add constant #define here if any + ***************************************************************************/ +//#define FH_SADC_PROC_FILE "driver/sadc" +#define MAX_CHANNEL_NO (8) +#define SADC_REF (3300) +#define SADC_MAX_AD_VALUE (0x3ff) +#define LOOP_MODE (0x55) +#define ISR_MODE (0xAA) + + +#define SADC_INIT_ALREADY (0x33) +#define SADC_INIT_NOT_YET (0) + + +#define SADC_CMD_READ_RAW_DATA (0x22) +#define SADC_CMD_READ_VOLT (0x33) +#define SADC_CMD_DISABLE (0x44) + +/**************************************************************************** + * ADT section + * add Abstract Data Type definition here + ***************************************************************************/ + +struct wrap_sadc_reg { + rt_uint32_t sadc_cmd; + rt_uint32_t sadc_control; + rt_uint32_t sadc_ier; + rt_uint32_t sadc_int_status; + rt_uint32_t sadc_dout0; + rt_uint32_t sadc_dout1; + rt_uint32_t sadc_dout2; + rt_uint32_t sadc_dout3; + rt_uint32_t sadc_debuge0; + rt_uint32_t sadc_status; + rt_uint32_t sadc_cnt; + rt_uint32_t sadc_timeout; +}; + +struct wrap_sadc_obj { + rt_uint32_t id; + void *regs; + rt_uint32_t irq_no; + rt_uint32_t init_flag; + rt_uint32_t active_channel_no; + rt_uint32_t active_channel_status; + rt_uint16_t channel_data[MAX_CHANNEL_NO]; + rt_uint32_t error_rec; + rt_uint32_t en_isr; + rt_uint32_t sample_mode; + struct rt_mutex lock; + struct rt_semaphore completion; + + //bind to the rtdev.. + rt_device_t rt_dev; + +}; + +typedef struct{ + rt_uint32_t channel; + rt_uint32_t sadc_data; +}SADC_INFO; + + +/**************************************************************************** + * extern variable declaration section + ***************************************************************************/ + +/**************************************************************************** + * section + * add function prototype here if any + ***************************************************************************/ +void rt_hw_sadc_init(void); +#endif +#endif /* SADC_H_ */ diff --git a/bsp/fh8620/drivers/spi_fh_adapt.c b/bsp/fh8620/drivers/spi_fh_adapt.c new file mode 100644 index 0000000000000000000000000000000000000000..291fe9a4f91e8b1690f47c837f71a316e88d040c --- /dev/null +++ b/bsp/fh8620/drivers/spi_fh_adapt.c @@ -0,0 +1,212 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + + /* + * spi_fh_adapt.c + * + * Created on: Mar 2, 2016 + * Author: duobao + */ + + +#include +#include "spi_fh_adapt.h" +#include "board_info.h" + +#ifdef RT_USING_W25QXX +#include "spi_flash_w25qxx.h" +#endif + +#ifdef RT_USING_AT45DBXX +#include "spi_flash_at45dbxx.h" +#endif + +#ifdef RT_USING_SST25VFXX +#include "spi_flash_sst25vfxx.h" +#endif + +#ifdef RT_USING_GD +#include "spi_flash_gd.h" +#endif + + +#ifdef RT_USING_FLASH_DEFAULT +#include "spi_flash_default.h" +#endif + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + + +#define WX_MANU_ID 0xEF +#define AT_MANU_ID 0x1F /* atmel */ +#define SST_MANU_ID 0xBF +#define GD_MANU_ID 0xC8 + + + +#define SPI_ADAPT_DEBUG +#ifdef SPI_ADAPT_DEBUG + +#define CMD_JEDEC_ID 0x9f + + +#define FH_SPI_ADAPT_DEBUG(fmt, args...) \ + rt_kprintf(fmt,##args); +#else +#define FH_SPI_ADAPT_DEBUG(fmt, args...) +#endif +struct fh_flash_id{ + unsigned char id; + rt_err_t (*fh_flash_init)(struct flash_platform_data *plat_flash); + char *name; +}; +const struct fh_flash_id id_map[] = { + +#ifdef RT_USING_W25QXX + WX_MANU_ID,w25qxx_init,"winbond", +#endif + +#ifdef RT_USING_AT45DBXX + AT_MANU_ID,at45dbxx_init,"atmel", +#endif + +#ifdef RT_USING_SST25VFXX + SST_MANU_ID,sst25vfxx_init,"SST", +#endif + +#ifdef RT_USING_GD + GD_MANU_ID,gd_init,"GD", +#endif + +}; + + +struct fh_flash_id * fh_flash_check_id_map(unsigned char id){ + struct fh_flash_id *p_map = RT_NULL; + unsigned int i; + for (i = 0; i < ARRAY_SIZE(id_map); i++) { + p_map = (struct fh_flash_id *)&id_map[i]; + if (p_map->id == id){ + return p_map; + } + } + return RT_NULL; +} + + +int fh_flash_adapt_probe(void *priv_data) +{ + struct flash_platform_data *plat_flash = priv_data; + const char * flash_device_name = plat_flash->flash_name; + const char * spi_device_name = plat_flash->spi_name; + struct rt_spi_device * rt_spi_device; + struct fh_flash_id * flash_model; + + rt_spi_device = (struct rt_spi_device *)rt_device_find(spi_device_name); + if(rt_spi_device == RT_NULL) + { + rt_kprintf("spi device %s not found!\r\n", spi_device_name); + return -RT_ENOSYS; + } + + + /* config spi */ + { + struct rt_spi_configuration cfg; + cfg.data_width = 8; + cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0 and Mode 3 */ + cfg.max_hz = 50 * 1000 * 1000; /* 50M */ + rt_spi_configure(rt_spi_device, &cfg); + } + + /* init flash */ + + rt_uint8_t cmd; + rt_uint8_t id_recv[3]; + uint16_t memory_type_capacity; + rt_err_t ret; + + cmd = 0xFF; /* reset SPI FLASH, cancel all cmd in processing. */ + rt_spi_send(rt_spi_device, &cmd, 1); + /* read flash id */ + cmd = CMD_JEDEC_ID; + rt_spi_send_then_recv(rt_spi_device, &cmd, 1, id_recv, 3); + + //if the flash is already connect. + if(id_recv[0] != 0xff){ + flash_model =fh_flash_check_id_map(id_recv[0]); + if(flash_model){ + ret = flash_model->fh_flash_init(plat_flash); + if(ret != RT_EOK){ + rt_kprintf("flash:%s init error\n",flash_model->name); + rt_kprintf("use default flash ops..\n"); + //flash_model->fh_flash_adapt_init =flash_default_init; + ret = flash_default_init(plat_flash); + } + } + else{ + rt_kprintf( + "use default flash ops...\nunrecognized flash id is :%02X %02X %02X\n", + id_recv[0], id_recv[1], id_recv[2]); + ret = flash_default_init(plat_flash); + + } + + int i; + for(i=0; inr_parts; i++) + { + fh_spi_partition_register(plat_flash->flash_name, &plat_flash->parts[i]); + } + + return ret; + + } + else{ + rt_kprintf("please check if you connect the flash already...\n"); + return RT_ENOSYS; + } + + +} + +int fh_flash_adapt_exit(void *priv_data) +{ + return 0; +} + +struct fh_board_ops flash_driver_ops = +{ + + .probe = fh_flash_adapt_probe, + .exit = fh_flash_adapt_exit, + +}; + +rt_err_t fh_flash_adapt_init(void) +{ + fh_board_driver_register("fh_flash", &flash_driver_ops); + return 0; +} diff --git a/bsp/fh8620/drivers/spi_fh_adapt.h b/bsp/fh8620/drivers/spi_fh_adapt.h new file mode 100644 index 0000000000000000000000000000000000000000..171fdf7184c10e3ab87d31c9e765ff0035bbf48c --- /dev/null +++ b/bsp/fh8620/drivers/spi_fh_adapt.h @@ -0,0 +1,71 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + + /* + * spi_fh_adapt.h + * + * Created on: Mar 2, 2016 + * Author: duobao + */ + +#ifndef SPI_FH_ADAPT_H_ +#define SPI_FH_ADAPT_H_ + +#include +#include + +#define MTD_WRITEABLE 0x400 /* Device is writeable */ +#define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */ +#define MTD_NO_ERASE 0x1000 /* No erase necessary */ +#define MTD_POWERUP_LOCK 0x2000 /* Always locked after reset */ + +struct mtd_partition { + char *name; /* identifier string */ + rt_uint32_t size; /* partition size */ + rt_uint32_t offset; /* offset within the master MTD space */ + rt_uint32_t mask_flags; /* master MTD flags to mask out for this partition */ + struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only) */ +}; + +struct flash_platform_data { + char *flash_name; + char *spi_name; + struct mtd_partition *parts; + unsigned int nr_parts; + + char *type; + + /* we'll likely add more ... use JEDEC IDs, etc */ +}; + + +rt_err_t fh_flash_adapt_init(void); +#endif /* SPI_FH_ADAPT_H_ */ + + + + + diff --git a/bsp/fh8620/drivers/ssi.c b/bsp/fh8620/drivers/ssi.c new file mode 100644 index 0000000000000000000000000000000000000000..e5cb99042e815f3b3508c9ea7cfa88c18872fabd --- /dev/null +++ b/bsp/fh8620/drivers/ssi.c @@ -0,0 +1,878 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include "fh_arch.h" +#include "board_info.h" +#include "ssi.h" +#include "gpio.h" +#include "inc/fh_driverlib.h" +#include "dma.h" +#include "dma_mem.h" +#include "mmu.h" +//#define FH_SPI_DEBUG + +#ifdef FH_SPI_DEBUG +#define PRINT_SPI_DBG(fmt, args...) \ + do \ + { \ + rt_kprintf("FH_SPI_DEBUG: "); \ + rt_kprintf(fmt, ## args); \ + } \ + while(0) +#else +#define PRINT_SPI_DBG(fmt, args...) do { } while (0) +#endif + + +#define RX_DMA_CHANNEL AUTO_FIND_CHANNEL +#define TX_DMA_CHANNEL AUTO_FIND_CHANNEL + +#define DMA_OR_ISR_THRESHOLD 20 +#define MALLOC_DMA_MEM_SIZE 0x1000 + + +//static rt_uint32_t allign_func(rt_uint32_t in_addr,rt_uint32_t allign_size){ +// return (in_addr + allign_size-1) & (~(allign_size - 1)); +//} + + +void * fh_get_spi_dev_pri_data(struct rt_spi_device* device){ + return device->parent.user_data; +} + +static rt_err_t fh_spi_configure(struct rt_spi_device* device, + struct rt_spi_configuration* configuration) +{ + + struct spi_slave_info *spi_slave; + struct spi_controller *spi_control; + struct fh_spi_obj *spi_obj; + struct spi_config *config; + rt_uint32_t status; + rt_uint32_t spi_hz; + + spi_slave = ( struct spi_slave_info *)fh_get_spi_dev_pri_data(device); + spi_control = spi_slave->control; + spi_obj = &spi_control->obj; + config = &spi_obj->config; + + PRINT_SPI_DBG("configuration: \n"); + PRINT_SPI_DBG("\tmode: 0x%x\n", configuration->mode); + PRINT_SPI_DBG("\tdata_width: 0x%x\n", configuration->data_width); + PRINT_SPI_DBG("\tmax_hz: 0x%x\n", configuration->max_hz); + + do{ + status = SPI_ReadStatus(spi_obj); + } + while(status & SPI_STATUS_BUSY); + + + /* data_width */ + if(configuration->data_width <= 8){ + config->data_size = SPI_DATA_SIZE_8BIT; + } + else if(configuration->data_width <= 16){ + config->data_size = SPI_DATA_SIZE_16BIT; + } + else{ + return -RT_ERROR; + } + + if(configuration->max_hz > spi_control->max_hz) + spi_hz = spi_control->max_hz; + else + spi_hz = configuration->max_hz; + + //fixme: div + config->clk_div = spi_control->clk_in/spi_hz; + //config->clk_div = 8; + PRINT_SPI_DBG("config hz:%d spi div:%d\n",spi_hz,config->clk_div); + /* CPOL */ + if(configuration->mode & RT_SPI_CPOL){ + config->clk_polarity = SPI_POLARITY_HIGH; + } + else{ + config->clk_polarity = SPI_POLARITY_LOW; + } + + /* CPHA */ + if(configuration->mode & RT_SPI_CPHA){ + config->clk_phase = SPI_PHASE_TX_FIRST; + } + else{ + config->clk_phase = SPI_PHASE_RX_FIRST; + } + + config->frame_format = SPI_FORMAT_MOTOROLA; + config->transfer_mode = SPI_MODE_TX_RX; + + SPI_Enable(spi_obj, 0); + SPI_SetParameter(spi_obj); + SPI_DisableInterrupt(spi_obj, SPI_IRQ_ALL); + SPI_Enable(spi_obj, 1); + + return RT_EOK; +} + + +static void xfer_dma_done(void *arg) +{ + struct spi_controller *spi_control = (struct spi_controller *)arg; + spi_control->dma_complete_times++; + + struct fh_spi_obj *spi_obj; + int ret; + rt_uint32_t slave_id; + spi_obj = &spi_control->obj; + + + //rt_kprintf("spi dma isr done.....\n"); + if (spi_control->dma_complete_times == 2) { + spi_control->dma_complete_times = 0; + + //add memcpy to user buff + if(spi_control->current_message->recv_buf){ + rt_memcpy((void*)spi_control->current_message->recv_buf,(void*)spi_control->dma.rx_dummy_buff,spi_control->current_message->length); + } + + SPI_Enable(spi_obj,0); + SPI_DisableDma(spi_obj,SPI_TX_DMA|SPI_RX_DMA); + SPI_Enable(spi_obj,1); + + rt_completion_done(&spi_control->transfer_completion); + } + +} + +void dma_set_tx_data(struct spi_controller *spi_control){ + + struct dma_transfer *trans; + rt_uint32_t hs_no; + struct rt_spi_message* current_message = spi_control->current_message; + trans = &spi_control->dma.tx_trans; + hs_no = spi_control->dma.tx_hs; + + struct fh_spi_obj *spi_obj; + spi_obj = &spi_control->obj; + + + + if(current_message->length > MALLOC_DMA_MEM_SIZE){ + rt_kprintf("[spi_dma]message len too large..\n"); + rt_kprintf("[spi_dma] message len is %d,max len is %d\n",current_message->length,MALLOC_DMA_MEM_SIZE); + RT_ASSERT(current_message->length <= MALLOC_DMA_MEM_SIZE); + } + + + rt_memset((void*)spi_control->dma.tx_dummy_buff,0xff,current_message->length); + //copy tx data.... + if(current_message->send_buf){ + rt_memcpy(spi_control->dma.tx_dummy_buff,current_message->send_buf,current_message->length); + } + + + trans->dma_number = 0; + trans->dst_add = (rt_uint32_t)(spi_obj->base + OFFSET_SPI_DR); + + trans->dst_hs = DMA_HW_HANDSHAKING; + trans->dst_inc_mode = DW_DMA_SLAVE_FIX; + trans->dst_msize = DW_DMA_SLAVE_MSIZE_1; + trans->dst_per = hs_no; + trans->dst_width = DW_DMA_SLAVE_WIDTH_8BIT; + trans->fc_mode = DMA_M2P; + + trans->src_add = (rt_uint32_t)spi_control->dma.tx_dummy_buff; + + trans->src_inc_mode = DW_DMA_SLAVE_INC; + trans->src_msize = DW_DMA_SLAVE_MSIZE_1; + + trans->src_width = DW_DMA_SLAVE_WIDTH_8BIT; + trans->trans_len = current_message->length; + + + trans->complete_callback = (void *)xfer_dma_done; + trans->complete_para = (void *)spi_control; + + + + +} + +void dma_set_rx_data(struct spi_controller *spi_control){ + + struct dma_transfer *trans; + rt_uint32_t hs_no; + struct rt_spi_message* current_message = spi_control->current_message; + trans = &spi_control->dma.rx_trans; + hs_no = spi_control->dma.rx_hs; + + struct fh_spi_obj *spi_obj; + spi_obj = &spi_control->obj; + + if(current_message->length > MALLOC_DMA_MEM_SIZE){ + rt_kprintf("[spi_dma]message len too large..len is %d\n",current_message->length); + RT_ASSERT(current_message->length <= MALLOC_DMA_MEM_SIZE); + } + + + //rt_memset((void *)spi_control->dma.rx_dummy_buff,0,MALLOC_DMA_MEM_SIZE); + + trans->dma_number = 0; + trans->fc_mode = DMA_P2M; + + trans->dst_add = (rt_uint32_t)spi_control->dma.rx_dummy_buff; + trans->dst_inc_mode = DW_DMA_SLAVE_INC; + trans->dst_msize = DW_DMA_SLAVE_MSIZE_1; + trans->dst_width = DW_DMA_SLAVE_WIDTH_8BIT; + + + + trans->src_add = (rt_uint32_t)(spi_obj->base + OFFSET_SPI_DR); + trans->src_inc_mode = DW_DMA_SLAVE_FIX; + trans->src_msize = DW_DMA_SLAVE_MSIZE_1; + trans->src_width = DW_DMA_SLAVE_WIDTH_8BIT; + trans->src_hs = DMA_HW_HANDSHAKING; + trans->src_per = hs_no; + trans->trans_len = current_message->length; + + + + trans->complete_callback = (void *)xfer_dma_done; + trans->complete_para = (void *)spi_control; + + +} + + +rt_uint32_t xfer_data_dma(struct spi_controller *spi_control){ + int ret; + + struct fh_spi_obj *spi_obj; + spi_obj = &spi_control->obj; + + struct rt_dma_device *dma_dev = spi_control->dma.dma_dev; + + + //tx data prepare + dma_set_tx_data(spi_control); + //rx data prepare + dma_set_rx_data(spi_control); + //dma go... + + + SPI_Enable(spi_obj,0); + + //SPI_WriteTxDmaLevel(spi_obj,SPI_FIFO_DEPTH / 4); + SPI_WriteTxDmaLevel(spi_obj,1); + //SPI_WriteTxDmaLevel(spi_obj,0); + SPI_WriteRxDmaLevel(spi_obj,0); + SPI_EnableDma(spi_obj,SPI_TX_DMA|SPI_RX_DMA); + SPI_Enable(spi_obj,1); + + dma_dev->ops->control(dma_dev,RT_DEVICE_CTRL_DMA_SINGLE_TRANSFER,(void *)&spi_control->dma.rx_trans); + dma_dev->ops->control(dma_dev,RT_DEVICE_CTRL_DMA_SINGLE_TRANSFER,(void *)&spi_control->dma.tx_trans); + + ret = rt_completion_wait(&spi_control->transfer_completion, RT_TICK_PER_SECOND*50); + //release channel.. + + //dma_dev->ops->control(dma_dev,RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL,(void *)&spi_control->dma.tx_trans); + //dma_dev->ops->control(dma_dev,RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL,(void *)&spi_control->dma.rx_trans); + + if(ret) + { + rt_kprintf("ERROR: %s, transfer timeout\n", __func__); + return -RT_ETIMEOUT; + } + + return RT_EOK; +} + +rt_uint32_t xfer_data_isr(struct spi_controller *spi_control){ + + int ret; + struct fh_spi_obj *spi_obj; + spi_obj = &spi_control->obj; + SPI_SetTxLevel(spi_obj, SPI_FIFO_DEPTH / 2); + SPI_EnableInterrupt(spi_obj, SPI_IRQ_TXEIM); + ret = rt_completion_wait(&spi_control->transfer_completion, RT_TICK_PER_SECOND*50); + if(ret) + { + rt_kprintf("ERROR: %s, transfer timeout\n", __func__); + return -RT_ETIMEOUT; + } + + return RT_EOK; +} + + +void fix_spi_xfer_mode(struct spi_controller *spi_control){ + //switch dma or isr....first check dma ...is error .use isr xfer... + struct rt_dma_device * rt_dma_dev; + struct dma_transfer *tx_trans; + struct dma_transfer *rx_trans; + int ret; + //retry to check if the dma status... + if(spi_control->dma.dma_flag == DMA_BIND_OK){ + //if transfer data too short...use isr.. + if(spi_control->current_message->length < DMA_OR_ISR_THRESHOLD){ + spi_control->xfer_mode = XFER_USE_ISR; + return; + } +#if(0) + rt_dma_dev = spi_control->dma.dma_dev; + //first request channel + + tx_trans = &spi_control->dma.tx_trans; + rx_trans = &spi_control->dma.rx_trans; + tx_trans->channel_number = TX_DMA_CHANNEL; + rx_trans->channel_number = RX_DMA_CHANNEL; + + + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL,(void *)tx_trans); + if(ret != RT_EOK){ + spi_control->xfer_mode = XFER_USE_ISR; + return; + } + + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL,(void *)rx_trans); + if(ret != RT_EOK){ + //release tx channel... + rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL,(void *)&tx_trans); + spi_control->xfer_mode = XFER_USE_ISR; + return; + } +#endif + spi_control->xfer_mode = XFER_USE_DMA; + //if error use isr mode + } + else + spi_control->xfer_mode = XFER_USE_ISR; + + + + +} + +static rt_uint32_t fh_spi_xfer(struct rt_spi_device* device, struct rt_spi_message* message) +{ + + struct spi_slave_info *spi_slave; + struct spi_controller *spi_control; + struct fh_spi_obj *spi_obj; + int ret; + rt_uint32_t slave_id; + spi_slave = ( struct spi_slave_info *)fh_get_spi_dev_pri_data(device); + spi_control = spi_slave->control; + spi_obj = &spi_control->obj; + spi_control->transfered_len = 0; + spi_control->received_len = 0; + + rt_sem_take(&spi_control->xfer_lock, RT_WAITING_FOREVER); + + rt_completion_init(&spi_control->transfer_completion); + + spi_control->current_message = message; + /* take CS */ + if(message->cs_take) + { + if(spi_slave->plat_slave.actice_level == ACTIVE_LOW) + gpio_direction_output(spi_slave->plat_slave.cs_pin, 0); + else + gpio_direction_output(spi_slave->plat_slave.cs_pin, 1); + + //here will always use the slave_0 because that the cs is gpio... + SPI_EnableSlaveen(spi_obj, 0); + } + + //fix transfer mode ..... + fix_spi_xfer_mode(spi_control); + + + switch(spi_control->xfer_mode){ + case XFER_USE_DMA: + PRINT_SPI_DBG("use dma xfer.....###############\n"); + ret = xfer_data_dma(spi_control); + if(ret == RT_EOK){ + break; + } + else{ + //use the isr mode to transfer + spi_control->xfer_mode = XFER_USE_ISR; + rt_kprintf("%s dma transfer error no:%x\n",__func__,ret); + } + case XFER_USE_ISR: + PRINT_SPI_DBG("use isr xfer.....&&&&&&&&&&&&&\n"); + ret = xfer_data_isr(spi_control); + if(ret != RT_EOK) + rt_kprintf("%s isr transfer error no:%x\n",__func__,ret); + break; + + default: + rt_kprintf("%s unknow xfer func...\n",__func__); + while(1) + ; + } + + /* release CS */ + if(message->cs_release) + { + if(spi_slave->plat_slave.actice_level == ACTIVE_LOW) + gpio_direction_output(spi_slave->plat_slave.cs_pin, 1); + else + gpio_direction_output(spi_slave->plat_slave.cs_pin, 0); + SPI_DisableSlaveen(spi_obj, 0); + } + + rt_sem_release(&spi_control->xfer_lock); + PRINT_SPI_DBG("%s end\n", __func__); + + return message->length; +} + +static struct rt_spi_ops fh_spi_ops = +{ + + .configure = fh_spi_configure, + .xfer = fh_spi_xfer, + +}; + + +static void fh_spi_interrupt(int irq, void *param) +{ + + + struct spi_controller *spi_control; + struct fh_spi_obj *spi_obj; + + spi_control = (struct spi_controller *)param; + spi_obj = &spi_control->obj; + rt_uint32_t rx_fifo_capability,tx_fifo_capability; + rt_uint8_t data = 0; + rt_uint8_t *p; + rt_uint32_t status; + +// + + if(spi_control->current_message == RT_NULL){ + rt_kprintf("ERROR: %s, current_message is incorrect\n", __func__); + } + + status = SPI_InterruptStatus(spi_obj); + PRINT_SPI_DBG("status: 0x%x\n", status); + //fixme: ??recv overflow, underflow; tran overflow?? + if(status & SPI_ISR_ERROR){ + rt_kprintf("ERROR: %s, status=%d\n", __func__, status); + SPI_ClearInterrupt(spi_obj); + //fixme: handle error + return; + } + + rx_fifo_capability = SPI_ReadRxFifoLevel(spi_obj); + tx_fifo_capability = MIN( + (SPI_FIFO_DEPTH - SPI_ReadTxFifoLevel(spi_obj)) / 2, + (spi_control->current_message->length - spi_control->transfered_len)); + + + PRINT_SPI_DBG("rx_fifo_capability=%d\n", rx_fifo_capability); + + //rx + spi_control->received_len += rx_fifo_capability; + while(rx_fifo_capability) + { + data = SPI_ReadData(spi_obj); + if(spi_control->current_message->recv_buf){ + *(rt_uint8_t *)spi_control->current_message->recv_buf++ = data; + } + PRINT_SPI_DBG("rx, data: 0x%x\n", data); + //rt_kprintf("rx, data: 0x%x\n", data); + rx_fifo_capability--; + } + + if(spi_control->received_len == spi_control->current_message->length) + { + + //rt_kprintf("asdasdq4902834908dklfkldjsdhgkljshfgljkhsgfkljhsdfkljghklj"); + SPI_DisableInterrupt(spi_obj, SPI_ISR_FLAG); + PRINT_SPI_DBG("finished, length=%d, received_len=%d\n", spi_control->current_message->length, spi_control->received_len); + rt_completion_done(&spi_control->transfer_completion); + + + return; + } + + //tx + + spi_control->transfered_len +=tx_fifo_capability; + if(spi_control->current_message->send_buf){ + p = (rt_uint8_t *)spi_control->current_message->send_buf; + while(tx_fifo_capability){ + PRINT_SPI_DBG("tx, data: 0x%x\n", *p); + //rt_kprintf("tx, data: 0x%x\n", *p); + SPI_WriteData(spi_obj, *p++); + tx_fifo_capability--; + } + spi_control->current_message->send_buf = p; + } + else{ + while(tx_fifo_capability){ + + SPI_WriteData(spi_obj, 0xff); + tx_fifo_capability--; + } + } + + + +} + + +int fh_spi_probe(void *priv_data) +{ + char spi_dev_name[20] = {0}; + char spi_bus_name[20] = {0}; + char spi_isr_name[20] = {0}; + char spi_lock_name[20] = {0}; + + struct spi_slave_info *spi_slave; + struct spi_slave_info *next_slave; + + struct spi_slave_info **control_slave; + + struct spi_controller *spi_control; + struct spi_control_platform_data *plat_data; + int i,ret; + + struct rt_dma_device * rt_dma_dev; + struct dma_transfer *tx_trans; + struct dma_transfer *rx_trans; + + //check data... + plat_data = (struct spi_control_platform_data *)priv_data; + + if(!plat_data){ + rt_kprintf("ERROR:platform data null...\n"); + return -RT_ENOMEM; + } + if(plat_data->slave_no > FH_SPI_SLAVE_MAX_NO){ + rt_kprintf("ERROR:spi controller not support %d slave..\n",plat_data->slave_no); + return -RT_ENOMEM; + } + + + //malloc data + spi_control = (struct spi_controller*)rt_malloc(sizeof(struct spi_controller)); + if(!spi_control){ + rt_kprintf("ERROR:no mem for malloc the spi controller..\n"); + goto error_malloc_bus; + } + + rt_memset(spi_control, 0, sizeof(struct spi_controller)); + //parse platform control data + spi_control->base = plat_data->base; + spi_control->id = plat_data->id; + spi_control->irq = plat_data->irq; + spi_control->max_hz = plat_data->max_hz; + spi_control->slave_no = plat_data->slave_no; + spi_control->obj.base = plat_data->base; + spi_control->clk_in = plat_data->clk_in; + spi_control->plat_data = plat_data; + + rt_sprintf(spi_lock_name, "%s%d", "spi_lock", spi_control->id); + rt_sem_init(&spi_control->xfer_lock, spi_lock_name, 1, RT_IPC_FLAG_FIFO); + + rt_sprintf(spi_bus_name, "%s%d", "spi_bus", spi_control->id); + + ret = rt_spi_bus_register(&spi_control->spi_bus, spi_bus_name, &fh_spi_ops); + + PRINT_SPI_DBG("bus name is :%s\n",spi_bus_name); + + //isr... + rt_sprintf(spi_isr_name, "%s%d", "ssi_isr", spi_control->id); + rt_hw_interrupt_install(spi_control->irq, fh_spi_interrupt, + (void *)spi_control, spi_isr_name); + + rt_hw_interrupt_umask(spi_control->irq); + PRINT_SPI_DBG("isr name is :%s\n",spi_isr_name); + + //check dma .... + if(plat_data->transfer_mode == USE_DMA_TRANSFER){ + + spi_control->dma.dma_dev = (struct rt_dma_device *)rt_device_find(plat_data->dma_name); + if(spi_control->dma.dma_dev == RT_NULL){ + rt_kprintf("can't find dma dev\n"); + //goto error_malloc_slave; + //spi_control->dma_xfer_flag = USE_ISR_TRANSFER; +// spi_control->dma.dma_flag = DMA_BIND_ERROR; +// spi_control->xfer_mode = XFER_USE_ISR; + goto BIND_DMA_ERROR; + } + else{ + + spi_control->dma.control = spi_control; + spi_control->dma.rx_hs = plat_data->rx_hs_no; + spi_control->dma.tx_hs = plat_data->tx_hs_no; + spi_control->dma.dma_name = plat_data->dma_name; + + spi_control->dma.rx_dummy_buff = fh_dma_mem_malloc(MALLOC_DMA_MEM_SIZE); + if(!spi_control->dma.rx_dummy_buff){ + rt_kprintf("malloc rx dma buff failed...\n"); + //spi_control->xfer_mode = XFER_USE_ISR; + goto BIND_DMA_ERROR; + } + + + + spi_control->dma.tx_dummy_buff = fh_dma_mem_malloc(MALLOC_DMA_MEM_SIZE); + if(!spi_control->dma.tx_dummy_buff){ + rt_kprintf("malloc tx dma buff failed...\n"); + fh_dma_mem_free(spi_control->dma.rx_dummy_buff); + //spi_control->xfer_mode = XFER_USE_ISR; + goto BIND_DMA_ERROR; + } + + if(((rt_uint32_t)spi_control->dma.tx_dummy_buff % 4)||((rt_uint32_t)spi_control->dma.rx_dummy_buff % 4)){ + rt_kprintf("dma malloc buff not allign..\n"); + fh_dma_mem_free(spi_control->dma.rx_dummy_buff); + fh_dma_mem_free(spi_control->dma.tx_dummy_buff); + goto BIND_DMA_ERROR; + } + + //open dma dev. + spi_control->dma.dma_dev->ops->control(spi_control->dma.dma_dev,RT_DEVICE_CTRL_DMA_OPEN,RT_NULL); + + //request channel + rt_dma_dev = spi_control->dma.dma_dev; + //first request channel + tx_trans = &spi_control->dma.tx_trans; + rx_trans = &spi_control->dma.rx_trans; + tx_trans->channel_number = TX_DMA_CHANNEL; + rx_trans->channel_number = RX_DMA_CHANNEL; + + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL,(void *)tx_trans); + if(ret != RT_EOK){ + goto BIND_DMA_ERROR; + } + + ret = rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_REQUEST_CHANNEL,(void *)rx_trans); + if(ret != RT_EOK){ + //release tx channel... + rt_dma_dev->ops->control(rt_dma_dev,RT_DEVICE_CTRL_DMA_RELEASE_CHANNEL,(void *)&tx_trans); + goto BIND_DMA_ERROR; + } + + //spi_control->xfer_mode = XFER_USE_DMA; + spi_control->dma.dma_flag = DMA_BIND_OK; + } + } + else{ + +BIND_DMA_ERROR: + spi_control->dma.dma_flag = DMA_BIND_ERROR; + //spi_control->xfer_mode = XFER_USE_ISR; + } + + + + + control_slave = &spi_control->spi_slave; + for(i=0;islave_no;i++){ + spi_slave = (struct spi_slave_info*)rt_malloc(sizeof(struct spi_slave_info)); + if(!spi_slave){ + rt_kprintf("ERROR:no mem for malloc the spi_slave%d..\n",i); + goto error_malloc_slave; + } + rt_memset(spi_slave, 0, sizeof(struct spi_slave_info)); + + //parse platform data... + spi_slave->id = i; + //bind to the spi control....will easy to find all the data... + spi_slave->control = spi_control; + spi_slave->plat_slave.cs_pin = plat_data->plat_slave[i].cs_pin; + spi_slave->plat_slave.actice_level = plat_data->plat_slave[i].actice_level; + rt_sprintf(spi_dev_name, "%s%d%s%d", "ssi", spi_control->id,"_",spi_slave->id); + + *control_slave = spi_slave; + control_slave = &spi_slave->next; + + //register slave dev... + ret = rt_spi_bus_attach_device(&spi_slave->spi_device,spi_dev_name,spi_bus_name,spi_slave); + if(ret != RT_EOK){ + rt_kprintf("register dev to bus failed...\n"); + goto error_malloc_slave; + } + + } + + + + //request gpio... + spi_slave = spi_control->spi_slave; + while(spi_slave != RT_NULL) + { + next_slave = spi_slave->next; + + ret = gpio_request(spi_slave->plat_slave.cs_pin); + if(ret!=0){ + rt_kprintf("request gpio_%d failed...\n",spi_slave->plat_slave.cs_pin); + goto error_malloc_slave; + } + + + PRINT_SPI_DBG("spi_slave info addr:%x,id:%d,cs:%d,active:%d\n",(rt_uint32_t)spi_slave, spi_slave->id, + spi_slave->plat_slave.cs_pin, + spi_slave->plat_slave.actice_level); + spi_slave = next_slave; + } + + //this will be used in platform exit.. + plat_data->control = spi_control; + return RT_EOK; + +error_malloc_slave: + //free the slaveinfo already malloc + spi_slave = spi_control->spi_slave; + while(spi_slave != RT_NULL) + { + next_slave = spi_slave->next; + gpio_release(spi_slave->plat_slave.cs_pin); + rt_free(spi_slave); + spi_slave = next_slave; + } + //mask isr + rt_hw_interrupt_mask(spi_control->irq); + //release sem .. + rt_sem_detach(&spi_control->xfer_lock); + + //free the control malloc . + rt_free(spi_control); + + //fixme:unregister spi_bus... + +error_malloc_bus: + return -RT_ENOMEM; + + + + + +} + +int fh_spi_exit(void *priv_data) +{ + + struct spi_controller *spi_control; + struct spi_control_platform_data *plat_data; + struct spi_slave_info *spi_slave; + struct spi_slave_info *next_slave; + + plat_data = (struct spi_control_platform_data *)priv_data; + spi_control = plat_data->control; + spi_slave = spi_control->spi_slave; + + while(spi_slave != RT_NULL) + { + next_slave = spi_slave->next; + gpio_release(spi_slave->plat_slave.cs_pin); + rt_free(spi_slave); + spi_slave = next_slave; + } + //mask isr + rt_hw_interrupt_mask(spi_control->irq); + //release sem .. + rt_sem_detach(&spi_control->xfer_lock); + + //free the control malloc . + rt_free(spi_control); + //fixme free all the malloc data ... + + return 0; +} + +struct fh_board_ops spi_driver_ops = +{ + + .probe = fh_spi_probe, + .exit = fh_spi_exit, + +}; + +void rt_hw_spi_init(void) +{ + + int ret; + + // rt_kprintf("%s start\n", __func__); + PRINT_SPI_DBG("%s start\n", __func__); + fh_board_driver_register("spi", &spi_driver_ops); + PRINT_SPI_DBG("%s end\n", __func__); + //fixme: never release? + +} +#if(0) +#define TEST_SPI_BUFF_SIZE 0x100 +static rt_uint8_t tx_buf[TEST_SPI_BUFF_SIZE] = {0}; +static rt_uint8_t rx_buf[TEST_SPI_BUFF_SIZE] = {0}; +int ssi_test(void){ + struct rt_spi_device * rt_spi_device; + + int ret; + rt_spi_device = (struct rt_spi_device *)rt_device_find("ssi1_0"); + + if(rt_spi_device == RT_NULL) + { + rt_kprintf("%s spi device %s not found!\r\n",__func__ ,"ssi1_0"); + return -RT_ENOSYS; + } + + /* config spi */ + { + struct rt_spi_configuration cfg; + cfg.data_width = 8; + cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible: Mode 0 and Mode 3 */ + cfg.max_hz = 50 * 1000 * 1000; /* 50M */ + rt_spi_configure(rt_spi_device, &cfg); + } + rt_memset(tx_buf,0x55,TEST_SPI_BUFF_SIZE); + + rt_spi_transfer(rt_spi_device,tx_buf,rx_buf,TEST_SPI_BUFF_SIZE); + + + ret = rt_memcmp(tx_buf,rx_buf,TEST_SPI_BUFF_SIZE); + if(ret != 0){ + rt_kprintf("compare error ..error data %x\n",ret); + } + rt_kprintf("test done \n"); + return 0; +} + +#ifdef RT_USING_FINSH +#include +FINSH_FUNCTION_EXPORT(ssi_test, fh_ssi_test); +#endif +#endif diff --git a/bsp/fh8620/drivers/ssi.h b/bsp/fh8620/drivers/ssi.h new file mode 100644 index 0000000000000000000000000000000000000000..6418de1508f65837f76f39e87bae47c54cdfaa18 --- /dev/null +++ b/bsp/fh8620/drivers/ssi.h @@ -0,0 +1,134 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef SSI_H_ +#define SSI_H_ +#include "Libraries/inc/fh_driverlib.h" +#include +#include +#include "fh_dma.h" +#define SPI_PRIV(drv) ( (struct fh_spi_obj)(drv->priv) ) + +#define FH_SPI_SLAVE_MAX_NO 2 + +struct spi_controller; +//platform use below +struct spi_slave_platform_data{ + + rt_uint32_t cs_pin; +#define ACTIVE_LOW 1 +#define ACTIVE_HIGH 2 + rt_uint32_t actice_level; +}; + + +struct spi_control_platform_data{ + rt_uint32_t id; + rt_uint32_t irq; + rt_uint32_t base; + rt_uint32_t max_hz; + rt_uint32_t slave_no; + rt_uint32_t clk_in; + //handshake no... + rt_uint32_t rx_hs_no; + rt_uint32_t tx_hs_no; + + char *dma_name; + //isr will be the default... +#define USE_ISR_TRANSFER 0 +#define USE_DMA_TRANSFER 1 + rt_uint32_t transfer_mode; + struct spi_controller *control; + struct spi_slave_platform_data plat_slave[FH_SPI_SLAVE_MAX_NO]; + +}; + + +struct spi_controller; +//driver use below....... +struct spi_slave_info +{ + struct rt_spi_device spi_device; + struct spi_controller *control; + struct spi_slave_platform_data plat_slave; + rt_uint32_t id; + //spi control will use to find all the slave info.. + struct spi_slave_info *next; +}; + +struct spi_dma +{ + char *dma_name; +#define DMA_BIND_OK 0 +#define DMA_BIND_ERROR 1 + rt_uint32_t dma_flag; + //bind to the dma dev.. + rt_uint32_t rx_hs; + rt_uint32_t tx_hs; + rt_uint8_t *rx_dummy_buff; + rt_uint8_t *tx_dummy_buff; + struct rt_dma_device *dma_dev; + struct dma_transfer tx_trans; + struct dma_transfer rx_trans; + struct spi_controller *control; +}; + +struct spi_controller +{ + rt_uint32_t id; + rt_uint32_t irq; + rt_uint32_t base; + rt_uint32_t max_hz; + rt_uint32_t slave_no; + rt_uint32_t clk_in; + //bind to the platform data.... + struct spi_control_platform_data *plat_data; + + //rt_uint32_t dma_xfer_flag; + +#define XFER_USE_ISR 0 +#define XFER_USE_DMA 1 + rt_uint32_t xfer_mode; + + struct spi_dma dma; + rt_uint32_t dma_complete_times; + struct rt_spi_bus spi_bus; + struct spi_slave_info *spi_slave; + struct rt_spi_message* current_message; + struct rt_completion transfer_completion; + struct rt_semaphore xfer_lock; + struct fh_spi_obj obj; + rt_uint32_t received_len; + rt_uint32_t transfered_len; + void* priv; +}; + + + + +void rt_hw_spi_init(void); + +#endif /* SPI_H_ */ diff --git a/bsp/fh8620/drivers/trap.c b/bsp/fh8620/drivers/trap.c new file mode 100644 index 0000000000000000000000000000000000000000..7cdf2dd7652f2eaa15561a6133a4cca9fbe32dbd --- /dev/null +++ b/bsp/fh8620/drivers/trap.c @@ -0,0 +1,197 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include "fh_arch.h" +#include "interrupt.h" +#include "Libraries/inc/fh_ictl.h" +/** + * @addtogroup FH81 + */ +/*@{*/ + +extern struct rt_thread *rt_current_thread; +#ifdef RT_USING_FINSH +extern long list_thread(void); +#endif + +/** + * this function will show registers of CPU + * + * @param regs the registers point + */ + +void rt_hw_show_register (struct rt_hw_register *regs) +{ + rt_kprintf("Execption:\n"); + rt_kprintf("r00:0x%08x r01:0x%08x r02:0x%08x r03:0x%08x\n", regs->r0, regs->r1, regs->r2, regs->r3); + rt_kprintf("r04:0x%08x r05:0x%08x r06:0x%08x r07:0x%08x\n", regs->r4, regs->r5, regs->r6, regs->r7); + rt_kprintf("r08:0x%08x r09:0x%08x r10:0x%08x\n", regs->r8, regs->r9, regs->r10); + rt_kprintf("fp :0x%08x ip :0x%08x\n", regs->fp, regs->ip); + rt_kprintf("sp :0x%08x lr :0x%08x pc :0x%08x\n", regs->sp, regs->lr, regs->pc); + rt_kprintf("cpsr:0x%08x\n", regs->cpsr); +} + +/** + * When ARM7TDMI comes across an instruction which it cannot handle, + * it takes the undefined instruction trap. + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_udef(struct rt_hw_register *regs) +{ + rt_hw_show_register(regs); + + rt_kprintf("undefined instruction\n"); + rt_kprintf("thread - %s stack:\n", rt_current_thread->name); + +#ifdef RT_USING_FINSH + list_thread(); +#endif + rt_hw_cpu_shutdown(); +} + +/** + * The software interrupt instruction (SWI) is used for entering + * Supervisor mode, usually to request a particular supervisor + * function. + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_swi(struct rt_hw_register *regs) +{ + rt_hw_show_register(regs); + + rt_kprintf("software interrupt\n"); + rt_hw_cpu_shutdown(); +} + +/** + * An abort indicates that the current memory access cannot be completed, + * which occurs during an instruction prefetch. + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_pabt(struct rt_hw_register *regs) +{ + rt_hw_show_register(regs); + + rt_kprintf("prefetch abort\n"); + rt_kprintf("thread - %s stack:\n", rt_current_thread->name); + +#ifdef RT_USING_FINSH + list_thread(); +#endif + rt_hw_cpu_shutdown(); +} + +/** + * An abort indicates that the current memory access cannot be completed, + * which occurs during a data access. + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_dabt(struct rt_hw_register *regs) +{ + rt_hw_show_register(regs); + + rt_kprintf("data abort\n"); + rt_kprintf("thread - %s stack:\n", rt_current_thread->name); + +#ifdef RT_USING_FINSH + list_thread(); +#endif + rt_hw_cpu_shutdown(); +} + +/** + * Normally, system will never reach here + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_resv(struct rt_hw_register *regs) +{ + rt_kprintf("not used\n"); + rt_hw_show_register(regs); + rt_hw_cpu_shutdown(); +} + +extern struct rt_irq_desc irq_desc[]; + +void rt_hw_trap_irq() +{ + rt_isr_handler_t isr_func; + rt_uint32_t irqstat_l, irqstat_h, irq; + void *param; + + fh_intc *p = (fh_intc *)INTC_REG_BASE; + + irqstat_l = p->IRQ_FINALSTATUS_L; + irqstat_h = p->IRQ_FINALSTATUS_H; + if (irqstat_l) + { + irq = __rt_ffs(irqstat_l) - 1; + } + else if(irqstat_h) + { + irq = __rt_ffs(irqstat_h) - 1 + 32; + } + else + { + rt_kprintf("No interrupt occur\n"); + return; + } + + /* get interrupt service routine */ + isr_func = irq_desc[irq].handler; + param = irq_desc[irq].param; + + /* turn to interrupt service routine */ + if(isr_func){ + isr_func(irq, param); + } +#ifdef RT_USING_INTERRUPT_INFO + irq_desc[irq].counter ++; +#endif +} + +void rt_hw_trap_fiq() +{ + rt_kprintf("fast interrupt request\n"); +} + +/*@}*/ diff --git a/bsp/fh8620/drivers/uart.c b/bsp/fh8620/drivers/uart.c new file mode 100644 index 0000000000000000000000000000000000000000..27d812e16f666ae19599bbf1c94f29a64389c37a --- /dev/null +++ b/bsp/fh8620/drivers/uart.c @@ -0,0 +1,292 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include "fh_arch.h" +#include "Libraries/inc/fh_uart.h" + + +void rt_fh_uart_handler(int vector, void *param) +{ + int status; + unsigned int ret; + struct fh_uart *uart; + unsigned int reg_status; + rt_device_t dev = (rt_device_t)param; + uart = (struct fh_uart *)dev->user_data; + status = uart_get_iir_status(uart->uart_port); + if (status & UART_IIR_NOINT) + { + return; + } + if(status & UART_IIR_THREMPTY){ + //first close tx isr + uart_disable_irq(uart->uart_port,UART_IER_ETBEI); + + rt_hw_serial_isr((struct rt_serial_device *)dev, RT_SERIAL_EVENT_TX_DONE); + } + else if((status & UART_IIR_CHRTOUT)==UART_IIR_CHRTOUT){ + //bug.... + //if no data in rx fifo + reg_status = uart_get_status(uart->uart_port); + if((reg_status & 1<<3) == 0) + ret = uart_getc(uart->uart_port); + } + else{ + rt_interrupt_enter(); + rt_hw_serial_isr((struct rt_serial_device *)dev, RT_SERIAL_EVENT_RX_IND); + rt_interrupt_leave(); + } +} + +/** +* UART device in RT-Thread +*/ +static rt_err_t fh_uart_configure(struct rt_serial_device *serial, + struct serial_configure *cfg) +{ + int div; + enum data_bits data_mode; + enum stop_bits stop_mode; + enum parity parity_mode; + struct fh_uart *uart; + + RT_ASSERT(serial != RT_NULL); + RT_ASSERT(cfg != RT_NULL); + uart = (struct fh_uart *)serial->parent.user_data; + + switch (cfg->data_bits) + { + case DATA_BITS_8: + data_mode = UART_DATA_BIT8; + break; + case DATA_BITS_7: + data_mode = UART_DATA_BIT7; + break; + case DATA_BITS_6: + data_mode = UART_DATA_BIT6; + break; + case DATA_BITS_5: + data_mode = UART_DATA_BIT5; + break; + default: + data_mode = UART_DATA_BIT8; + break; + } + + switch (cfg->stop_bits) + { + case STOP_BITS_2: + stop_mode = UART_STOP_BIT2; + break; + case STOP_BITS_1: + default: + stop_mode = UART_STOP_BIT1; + break; + } + + switch (cfg->parity) + { + case PARITY_ODD: + parity_mode = UART_PARITY_ODD; + break; + case PARITY_EVEN: + parity_mode = UART_PARITY_EVEN; + break; + case PARITY_NONE: + default: + parity_mode = UART_PARITY_NONE; + break; + } + + uart_disable_irq(uart->uart_port, UART_IER_ERBFI); + + uart_configure(uart->uart_port, data_mode, + stop_mode, parity_mode, + cfg->baud_rate, UART_CLOCK_FREQ); + + uart_enable_irq(uart->uart_port, UART_IER_ERBFI); + + return RT_EOK; +} + +static rt_err_t fh_uart_control(struct rt_serial_device *serial, + int cmd, void *arg) +{ + struct fh_uart* uart; + + RT_ASSERT(serial != RT_NULL); + uart = (struct fh_uart *)serial->parent.user_data; + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + /* disable rx irq */ + rt_hw_interrupt_mask(uart->irq); + uart_disable_irq(uart->uart_port,UART_IER_ERBFI); + break; + case RT_DEVICE_CTRL_SET_INT: + /* enable rx irq */ + rt_hw_interrupt_umask(uart->irq); + uart_enable_irq(uart->uart_port,UART_IER_ERBFI); + break; + } + + return RT_EOK; +} + +static int fh_uart_putc(struct rt_serial_device *serial, char c) +{ + struct fh_uart *uart = serial->parent.user_data; + unsigned int ret; + ret = uart_get_status(uart->uart_port); + if(serial->parent.open_flag & RT_DEVICE_FLAG_INT_TX){ + //RT_DEVICE_FLAG_INT_TX + + if(c == '\n'){ + fh_uart_putc(serial,'\r'); + } + if(ret & UART_USR_TFNF){ + uart_putc(uart->uart_port, c); + return 1; + } + //open tx isr here.. + uart_enable_irq(uart->uart_port,UART_IER_ETBEI); + return -1; + } + //poll mode + else{ + + while(!(uart_get_status(uart->uart_port) & UART_USR_TFNF)) + ; + uart_putc(uart->uart_port, c); + return 1; + + + } + + + +} + +static int fh_uart_getc(struct rt_serial_device *serial) +{ + int result; + struct fh_uart *uart = serial->parent.user_data; + + if (uart_is_rx_ready(uart->uart_port)) + { + result = uart_getc(uart->uart_port); + } + else + { + result = -1; + } + + return result; +} + +static const struct rt_uart_ops fh_uart_ops = +{ + fh_uart_configure, + fh_uart_control, + fh_uart_putc, + fh_uart_getc, +}; + + +#if defined(RT_USING_UART0) +static struct rt_serial_device serial0; +struct fh_uart uart0 = { + (uart *)UART0_REG_BASE, + UART0_IRQn +}; + +#endif + +#if defined(RT_USING_UART1) +static struct rt_serial_device serial1; +struct fh_uart uart1 = { + (uart *)UART1_REG_BASE, + UART1_IRQn +}; + +#endif + + + + +/** + * This function will handle init uart + */ +void rt_hw_uart_init(void) +{ + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + +#if defined(RT_USING_UART0) +#if(0) + serial0.ops = &fh_uart_ops; + serial0.config = config; + + /* register vcom device */ + rt_hw_serial_register(&serial0, "uart0", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM | RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_STANDALONE, + &uart0); + rt_hw_interrupt_install(uart0.irq, rt_fh_uart_handler, + (void *)&(serial0.parent), "UART0"); + rt_hw_interrupt_umask(uart0.irq); +#endif + serial0.ops = &fh_uart_ops; + serial0.config = config; + + /* register vcom device */ + rt_hw_serial_register(&serial0, "uart0", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM , + &uart0); + rt_hw_interrupt_install(uart0.irq, rt_fh_uart_handler, + (void *)&(serial0.parent), "UART0"); + rt_hw_interrupt_umask(uart0.irq); + +#endif + +#if defined(RT_USING_UART1) + serial1.ops = &fh_uart_ops; + serial1.config = config; + + /* register vcom device */ + rt_hw_serial_register(&serial1, "uart1", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM , + &uart1); + rt_hw_interrupt_install(uart1.irq, rt_fh_uart_handler, + (void *)&(serial1.parent), "UART1"); + rt_hw_interrupt_umask(uart1.irq); + +#endif + +} + + diff --git a/bsp/fh8620/drivers/uart.h b/bsp/fh8620/drivers/uart.h new file mode 100644 index 0000000000000000000000000000000000000000..d4459f02f895631b9db3ec9d95b32dd84d738426 --- /dev/null +++ b/bsp/fh8620/drivers/uart.h @@ -0,0 +1,33 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef UART_H_ +#define UART_H_ + +void rt_hw_uart_init(void); + + +#endif diff --git a/bsp/fh8620/drivers/wdt.c b/bsp/fh8620/drivers/wdt.c new file mode 100644 index 0000000000000000000000000000000000000000..1d67007a6a1bf49d3282cda1fa72774e9d22d029 --- /dev/null +++ b/bsp/fh8620/drivers/wdt.c @@ -0,0 +1,269 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "fh_def.h" +#include "wdt.h" +#include "interrupt.h" +#include "board_info.h" +#include "inc/fh_driverlib.h" +#include +#include + +#define FH_WDT_DEBUG + +#ifdef FH_WDT_DEBUG +#define PRINT_WDT_DBG(fmt, args...) \ + do \ + { \ + rt_kprintf("FH_WDT_DEBUG: "); \ + rt_kprintf(fmt, ## args); \ + } \ + while(0) +#else +#define PRINT_WDT_DBG(fmt, args...) do { } while (0) +#endif + +static int heartbeat = WDT_HEARTBEAT; + +static rt_uint32_t fh_wdt_time_left(struct wdt_driver *wdt_drv) +{ + struct fh_wdt_obj *wdt_obj = wdt_drv->priv; + //todo: get clk + //return WDT_GetCurrCount(wdt_obj) / WDT_CLOCK; + return WDT_GetCurrCount(wdt_obj) / 1800000; +} + + +static void fh_wdt_keepalive(struct wdt_driver *wdt_drv) +{ + struct fh_wdt_obj *wdt_obj = wdt_drv->priv; + WDT_Kick(wdt_obj); +} + +static inline void fh_wdt_set_next_heartbeat(struct wdt_driver *wdt_drv) +{ + wdt_drv->next_heartbeat = rt_tick_get() + heartbeat * RT_TICK_PER_SECOND; +} + +static inline int fh_wdt_top_in_seconds(struct wdt_driver *wdt_drv, unsigned top) +{ + /* + * There are 16 possible timeout values in 0..15 where the number of + * cycles is 2 ^ (16 + i) and the watchdog counts down. + */ + //todo: get_clk + return (1 << (17 + top)) / 1800000; +} + +static int fh_wdt_set_top(struct wdt_driver *wdt_drv, unsigned top_s) +{ + int i, top_val = FH_WDT_MAX_TOP; + struct fh_wdt_obj *wdt_obj = wdt_drv->priv; + + /* + * Iterate over the timeout values until we find the closest match. We + * always look for >=. + */ + + for (i = 0; i <= FH_WDT_MAX_TOP; ++i) + if (fh_wdt_top_in_seconds(wdt_drv, i) >= top_s) { + top_val = i; + break; + } + + /* Set the new value in the watchdog. */ + PRINT_WDT_DBG("[wdt] set topval: %d\n", top_val); + WDT_SetTopValue(wdt_obj, top_val); + + fh_wdt_set_next_heartbeat(wdt_drv); + + return fh_wdt_top_in_seconds(wdt_drv, top_val); +} + + +rt_err_t fh_watchdog_init(rt_watchdog_t *wdt) +{ + struct wdt_driver *wdt_drv = wdt->parent.user_data; + struct fh_wdt_obj *wdt_obj = wdt_drv->priv; + if (wdt_drv->in_use) + return -RT_EBUSY; + + //todo: spinlock + fh_wdt_set_top(wdt_drv, WDT_HW_TIMEOUT);///3000); + if (!WDT_IsEnable(wdt_obj)) + { + /* + * The watchdog is not currently enabled. Set the timeout to + * the maximum and then start it. + */ + rt_uint32_t value; + value = WDOG_CONTROL_REG_WDT_EN_MASK; + value |= WDOG_CONTROL_REG_RMOD_MASK; + WDT_SetCtrl(wdt_obj, value); + fh_wdt_keepalive(wdt_drv); + } + + fh_wdt_set_next_heartbeat(wdt_drv); + + //todo: unlock + + return RT_EOK; +} + +rt_err_t fh_watchdog_ctrl(rt_watchdog_t *wdt, int cmd, void *arg) +{ + struct wdt_driver *wdt_drv = wdt->parent.user_data; + struct fh_wdt_obj *wdt_obj = wdt_drv->priv; + rt_uint32_t val; + + switch (cmd) + { + case RT_DEVICE_CTRL_WDT_START: + WDT_Enable(wdt_obj, RT_TRUE); + break; + + case RT_DEVICE_CTRL_WDT_STOP: + WDT_Enable(wdt_obj, RT_FALSE); + break; + + case RT_DEVICE_CTRL_WDT_KEEPALIVE: + //fh_wdt_set_next_heartbeat(wdt_drv); + fh_wdt_keepalive(wdt_drv); + break; + + case RT_DEVICE_CTRL_WDT_SET_TIMEOUT: + heartbeat = *((int*)(arg)); + PRINT_WDT_DBG("[wdt] settime value %lu\n", heartbeat); + fh_wdt_set_top(wdt_drv, heartbeat);///3000); + fh_wdt_keepalive(wdt_drv); + fh_wdt_set_next_heartbeat(wdt_drv); + break; + + case RT_DEVICE_CTRL_WDT_GET_TIMEOUT: + arg = &heartbeat; + break; + + case RT_DEVICE_CTRL_WDT_GET_TIMELEFT: + val = fh_wdt_time_left(wdt_drv); + arg = &val; + break; + + default: + return -RT_EIO; + } + return RT_EOK; +} + +static void fh_wdt_interrupt(int irq, void *param) +{ + //todo: stop + //fh81_pmu_stop(); +} + +struct rt_watchdog_ops fh_watchdog_ops = +{ + .init = &fh_watchdog_init, + .control = &fh_watchdog_ctrl, +}; + + +int fh_wdt_probe(void *priv_data) +{ + rt_watchdog_t *wdt_dev; + struct wdt_driver *wdt_drv; + struct fh_wdt_obj *wdt_obj = (struct fh_wdt_obj *)priv_data; + + wdt_drv = (struct wdt_driver *)rt_malloc(sizeof(struct wdt_driver)); + rt_memset(wdt_drv, 0, sizeof(struct wdt_driver)); + + wdt_drv->priv = wdt_obj; + + rt_hw_interrupt_install(wdt_obj->irq, fh_wdt_interrupt, (void *)wdt_drv, "wdt_irq"); + rt_hw_interrupt_umask(wdt_obj->irq); + + //todo: clk + + wdt_dev = (rt_watchdog_t *)rt_malloc(sizeof(rt_watchdog_t)); + + if (wdt_dev == RT_NULL) + { + rt_kprintf("ERROR: %s rt_watchdog_t malloc failed\n", __func__); + } + + wdt_dev->ops = &fh_watchdog_ops; + + rt_hw_watchdog_register(wdt_dev, "fh_wdt", RT_DEVICE_OFLAG_RDWR, wdt_drv); + + return 0; +} + +int fh_wdt_exit(void *priv_data) +{ + return 0; +} + +struct fh_board_ops wdt_driver_ops = +{ + .probe = fh_wdt_probe, + .exit = fh_wdt_exit, +}; + +void rt_hw_wdt_init(void) +{ + PRINT_WDT_DBG("%s start\n", __func__); + fh_board_driver_register("wdt", &wdt_driver_ops); + PRINT_WDT_DBG("%s end\n", __func__); +} + +void wdt_start(int timeout, int kick_times) +{ + rt_device_t wdt_dev; + int ret; + + wdt_dev = rt_device_find("fh_wdt"); + + rt_device_open(wdt_dev, 0); + + ret = rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_SET_TIMEOUT, &timeout); + + int i = 0; + for( ; i < kick_times; i ++ ) + { + rt_thread_sleep(timeout * RT_TICK_PER_SECOND / 2); + ret = rt_device_control(wdt_dev, RT_DEVICE_CTRL_WDT_KEEPALIVE, &timeout); + rt_kprintf( "kicked\n" ); + } + + rt_kprintf( "stop kick the watchdog, it shall reboot for %d seconds.\n", timeout * 2); + +} + +#ifdef RT_USING_WDT +#ifdef RT_USING_FINSH +#include +FINSH_FUNCTION_EXPORT(wdt_start, enable wdt); +#endif +#endif diff --git a/bsp/fh8620/drivers/wdt.h b/bsp/fh8620/drivers/wdt.h new file mode 100644 index 0000000000000000000000000000000000000000..768a2e0739ad1ac4d8b3e1b00b974374e9274124 --- /dev/null +++ b/bsp/fh8620/drivers/wdt.h @@ -0,0 +1,46 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef WDT_H_ +#define WDT_H_ + + + +struct wdt_driver +{ + //struct clk *clk; + unsigned long in_use; + unsigned long next_heartbeat; + //struct timer_list timer; + int expect_close; + + void* priv; +}; + + + + +#endif /* WDT_H_ */ diff --git a/bsp/fh8620/libraries/SConscript b/bsp/fh8620/libraries/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..cc2296bd474acdbd93ea6e7b2fa80bd3c61a8b84 --- /dev/null +++ b/bsp/fh8620/libraries/SConscript @@ -0,0 +1,14 @@ +# for module compiling +import os +from building import * + +objs = [] +cwd = GetCurrentDir() +list = os.listdir(cwd) + +for item in list: + + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/bsp/fh8620/libraries/driverlib/SConscript b/bsp/fh8620/libraries/driverlib/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..3fc86c3730e91226e8b966ca4396f5c04f8a37fe --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +path = [cwd + '/..'] + +group = DefineGroup('Libraries', src, depend = [''], CPPPATH = path) + +Return('group') diff --git a/bsp/fh8620/libraries/driverlib/fh_gpio.c b/bsp/fh8620/libraries/driverlib/fh_gpio.c new file mode 100644 index 0000000000000000000000000000000000000000..e3e58f9a1ffc0e8fa380448d8aab902138fb4bb8 --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_gpio.c @@ -0,0 +1,27 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + + \ No newline at end of file diff --git a/bsp/fh8620/libraries/driverlib/fh_i2c.c b/bsp/fh8620/libraries/driverlib/fh_i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..58aa70206c03f3301c1b2b503d1dcc7a13c57772 --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_i2c.c @@ -0,0 +1,311 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "inc/fh_driverlib.h" + +int I2C_WaitMasterIdle(struct fh_i2c_obj *i2c_obj) +{ + UINT32 reg; + int timeout = 200; //20 ms + + while (GET_REG(i2c_obj->base + OFFSET_I2C_STATUS) & DW_IC_STATUS_MASTER_ACTIVITY) + { + if(timeout < 0) + { + rt_kprintf( "ERROR: %s, timeout waiting for master not active, txflr: 0x%x, rxflr: 0x%x, stat: 0x%x\n", + __func__, I2C_GetReceiveFifoLevel(i2c_obj), I2C_GetTransmitFifoLevel(i2c_obj), GET_REG(i2c_obj->base + OFFSET_I2C_INTR_STAT)); + return -RT_ETIMEOUT; + } + timeout--; + udelay(100); + } + + return 0; +} + +int I2C_WaitDeviceIdle(struct fh_i2c_obj *i2c_obj) +{ + UINT32 reg; + + int timeout = 2000; //200 ms + + while (GET_REG(i2c_obj->base + OFFSET_I2C_STATUS) & DW_IC_STATUS_ACTIVITY) + { + if(timeout < 0) + { + rt_kprintf( "ERROR: %s, timeout waiting for device not active\n", __func__); + return -RT_ETIMEOUT; + } + timeout--; + udelay(100); + } + + return 0; +} + + +static inline UINT32 I2C_CalcSclHcnt(UINT32 ic_clk, UINT32 tSYMBOL, UINT32 tf, int cond, int offset) +{ + /* + * DesignWare I2C core doesn't seem to have solid strategy to meet + * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec + * will result in violation of the tHD;STA spec. + */ + if (cond) + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH + * + * This is based on the DW manuals, and represents an ideal + * configuration. The resulting I2C bus speed will be + * faster than any of the others. + * + * If your hardware is free from tHD;STA issue, try this one. + */ + return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset; + else + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) + * + * This is just experimental rule; the tHD;STA period turned + * out to be proportinal to (_HCNT + 3). With this setting, + * we could meet both tHIGH and tHD;STA timing specs. + * + * If unsure, you'd better to take this alternative. + * + * The reason why we need to take into account "tf" here, + * is the same as described in i2c_fh_scl_lcnt(). + */ + return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset; +} + +static inline UINT32 I2C_CalcSclLcnt(UINT32 ic_clk, UINT32 tLOW, UINT32 tf, int offset) +{ + /* + * Conditional expression: + * + * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) + * + * DW I2C core starts counting the SCL CNTs for the LOW period + * of the SCL clock (tLOW) as soon as it pulls the SCL line. + * In order to meet the tLOW timing spec, we need to take into + * account the fall time of SCL signal (tf). Default tf value + * should be 0.3 us, for safety. + */ + return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset; +} + +static int I2C_SetSpeedCount(struct fh_i2c_obj *i2c_obj) +{ + UINT32 hcnt, lcnt; + + /* set standard and fast speed count for high/low periods */ + + /* Standard-mode */ + hcnt = I2C_CalcSclHcnt(i2c_obj->input_clock, + 40, /* tHD;STA = tHIGH = 4.0 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = I2C_CalcSclLcnt(i2c_obj->input_clock, + 47, /* tLOW = 4.7 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + + SET_REG(i2c_obj->base + OFFSET_I2C_SS_SCL_HCNT, hcnt); + SET_REG(i2c_obj->base + OFFSET_I2C_SS_SCL_LCNT, lcnt); + + /* Fast-mode */ + hcnt = I2C_CalcSclHcnt(i2c_obj->input_clock, + 6, /* tHD;STA = tHIGH = 0.6 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = I2C_CalcSclLcnt(i2c_obj->input_clock, + 13, /* tLOW = 1.3 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + SET_REG(i2c_obj->base + OFFSET_I2C_FS_SCL_HCNT, hcnt); + SET_REG(i2c_obj->base + OFFSET_I2C_FS_SCL_LCNT, lcnt); + + return 0; +} + +inline UINT8 I2C_GetData(struct fh_i2c_obj *i2c_obj) +{ + return GET_REG(i2c_obj->base + OFFSET_I2C_DATA_CMD) & 0xff; +} + +inline void I2C_SetDataCmd(struct fh_i2c_obj *i2c_obj, UINT32 reg) +{ + SET_REG(i2c_obj->base + OFFSET_I2C_DATA_CMD, reg); +} + +inline void I2C_SetInterruptMask(struct fh_i2c_obj *i2c_obj, UINT32 mask) +{ + SET_REG(i2c_obj->base + OFFSET_I2C_INTR_MASK, mask); +} + +inline UINT32 I2C_GetInterruptMask(struct fh_i2c_obj *i2c_obj) +{ + return GET_REG(i2c_obj->base + OFFSET_I2C_INTR_MASK); +} + +UINT32 I2C_ClearAndGetInterrupts(struct fh_i2c_obj *i2c_obj) +{ + UINT32 stat; + /* + * The IC_INTR_STAT register just indicates "enabled" interrupts. + * Ths unmasked raw version of interrupt status bits are available + * in the IC_RAW_INTR_STAT register. + * + * That is, + * stat = readl(IC_INTR_STAT); + * equals to, + * stat = readl(IC_RAW_INTR_STAT) & readl(IC_INTR_MASK); + * + * The raw version might be useful for debugging purposes. + */ + stat = GET_REG(i2c_obj->base + OFFSET_I2C_INTR_STAT); + + /* + * Do not use the IC_CLR_INTR register to clear interrupts, or + * you'll miss some interrupts, triggered during the period from + * readl(IC_INTR_STAT) to readl(IC_CLR_INTR). + * + * Instead, use the separately-prepared IC_CLR_* registers. + */ + if (stat & DW_IC_INTR_RX_UNDER) + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_RX_UNDER); + if (stat & DW_IC_INTR_RX_OVER) + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_RX_OVER); + if (stat & DW_IC_INTR_TX_OVER) + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_TX_OVER); + if (stat & DW_IC_INTR_RD_REQ) + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_RD_REQ); + if (stat & DW_IC_INTR_TX_ABRT) + { + /* + * The IC_TX_ABRT_SOURCE register is cleared whenever + * the IC_CLR_TX_ABRT is read. Preserve it beforehand. + */ + i2c_obj->abort_source = GET_REG(i2c_obj->base + OFFSET_I2C_TX_ABRT_SOURCE); + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_TX_ABRT); + } + if (stat & DW_IC_INTR_RX_DONE) + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_RX_DONE); + if (stat & DW_IC_INTR_ACTIVITY) + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_ACTIVITY); + if (stat & DW_IC_INTR_STOP_DET) + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_STOP_DET); + if (stat & DW_IC_INTR_START_DET) + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_START_DET); + if (stat & DW_IC_INTR_GEN_CALL) + GET_REG(i2c_obj->base + OFFSET_I2C_CLR_GEN_CALL); + + return stat; +} + +int I2C_HandleTxAbort(struct fh_i2c_obj *i2c_obj) +{ + unsigned long abort_source = i2c_obj->abort_source; + int i; + + if (abort_source & DW_IC_TX_ABRT_NOACK) + { + //for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + // rt_kprintf( "%s: %s\n", __func__, abort_sources[i]); + return 0; + } + + //for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) + // rt_kprintf( "%s: %s\n", __func__, abort_sources[i]); + rt_kprintf("%s: abort_sources 0x%x\n", __func__, abort_sources); + + if (abort_source & DW_IC_TX_ARB_LOST) + return 0; + else if (abort_source & DW_IC_TX_ABRT_GCALL_READ) + return 0; /* wrong msgs[] data */ + else + return 0; +} + +inline UINT32 I2C_SetTransmitThreshold(struct fh_i2c_obj *i2c_obj, int txtl) +{ + return SET_REG(i2c_obj->base + OFFSET_I2C_TX_TL, txtl); +} + + +inline UINT32 I2C_GetReceiveFifoLevel(struct fh_i2c_obj *i2c_obj) +{ + return GET_REG(i2c_obj->base + OFFSET_I2C_RXFLR); +} + +inline UINT32 I2C_GetTransmitFifoLevel(struct fh_i2c_obj *i2c_obj) +{ + return GET_REG(i2c_obj->base + OFFSET_I2C_TXFLR); +} + +inline void I2C_SetSlaveAddress(struct fh_i2c_obj *i2c_obj, rt_uint16_t addr) +{ + UINT32 reg; + reg = GET_REG(i2c_obj->base + OFFSET_I2C_TAR); + reg &= ~(0x3ff); + reg |= addr & 0x3ff; + SET_REG(i2c_obj->base + OFFSET_I2C_TAR, reg); +} + +inline void I2C_Enable(struct fh_i2c_obj *i2c_obj, int enable) +{ + SET_REG(i2c_obj->base + OFFSET_I2C_ENABLE, enable); +} + + +void I2C_Init(struct fh_i2c_obj *i2c_obj) +{ + UINT32 ic_con; + UINT32 param0 = GET_REG(i2c_obj->base + OFFSET_I2C_COMP_PARAM1); + + I2C_WaitMasterIdle(i2c_obj); + I2C_Enable(i2c_obj, RT_FALSE); + I2C_SetSpeedCount(i2c_obj); + + i2c_obj->config.tx_fifo_depth = ((param0 >> 16) & 0xff) + 1; + i2c_obj->config.rx_fifo_depth = ((param0 >> 8) & 0xff) + 1; + + /* Configure Tx/Rx FIFO threshold levels */ + SET_REG(i2c_obj->base + OFFSET_I2C_TX_TL, i2c_obj->config.tx_fifo_depth - 1); + SET_REG(i2c_obj->base + OFFSET_I2C_RX_TL, 0); + + /* configure the i2c master */ + ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | + /*OFFSET_I2C_CON_RESTART_EN |*/ DW_IC_CON_SPEED_FAST; //DW_IC_CON_SPEED_STD; + + SET_REG( i2c_obj->base + OFFSET_I2C_CON, ic_con); +} diff --git a/bsp/fh8620/libraries/driverlib/fh_ictl.c b/bsp/fh8620/libraries/driverlib/fh_ictl.c new file mode 100644 index 0000000000000000000000000000000000000000..a53c8dba3412aa35f58ba9d330f4558138fab83e --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_ictl.c @@ -0,0 +1,69 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "inc/fh_driverlib.h" + + + +void ictl_close_all_isr(fh_intc *p){ + if(p){ + //enable all interrupts + p->IRQ_EN_L = 0xffffffff; + p->IRQ_EN_H = 0xffffffff; + //mask all interrupts + p->IRQ_MASK_L = 0xffffffff; + p->IRQ_MASK_H = 0xffffffff; + } + +} + + + + +void ictl_mask_isr(fh_intc *p,int irq){ + if(p){ + + if (irq < 32) + p->IRQ_MASK_L |= (1 << irq); + else + p->IRQ_MASK_H |= (1 << (irq - 32)); + } +} + + + + + +void ictl_unmask_isr(fh_intc *p,int irq){ + if(p){ + if (irq < 32) + p->IRQ_MASK_L &= ~(1 << irq); + else + p->IRQ_MASK_H &= ~(1 << (irq - 32)); + + } + +} diff --git a/bsp/fh8620/libraries/driverlib/fh_mmc.c b/bsp/fh8620/libraries/driverlib/fh_mmc.c new file mode 100644 index 0000000000000000000000000000000000000000..47ac75239af4822df6c7e62ce6fcc436faf62ca1 --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_mmc.c @@ -0,0 +1,413 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "inc/fh_driverlib.h" + +// *1: card off +// *0: card on +inline rt_uint32_t MMC_GetCardStatus(struct fh_mmc_obj *mmc_obj) +{ + rt_uint32_t card_status = GET_REG(mmc_obj->base + OFFSET_SDC_CDETECT); + + return card_status & 0x1; +} + +inline void MMC_StartDma(struct fh_mmc_obj *mmc_obj) +{ + rt_uint32_t reg; + + SET_REG(mmc_obj->base + OFFSET_SDC_DBADDR, (rt_uint32_t)mmc_obj->descriptors); + reg = GET_REG(mmc_obj->base + OFFSET_SDC_BMOD); + reg |= 1 << 7; + SET_REG(mmc_obj->base + OFFSET_SDC_BMOD, reg); +} + +inline void MMC_StopDma(struct fh_mmc_obj *mmc_obj) +{ + rt_uint32_t reg; + + reg = GET_REG(mmc_obj->base + OFFSET_SDC_BMOD); + reg &= ~(1 << 7); + SET_REG(mmc_obj->base + OFFSET_SDC_BMOD, reg); +} + +void MMC_InitDescriptors(struct fh_mmc_obj *mmc_obj, rt_uint32_t *buf, rt_uint32_t size) +{ + MMC_DMA_Descriptors *desc; + rt_uint32_t len = 0; + int i, desc_cnt = 0; + + desc = mmc_obj->descriptors; + + while(size > 0) + { + desc[desc_cnt].desc0.bit.own = 1; + desc[desc_cnt].desc0.bit.sencond_address_chained = 1; + desc[desc_cnt].desc1.bit.buffer1_size = MIN(MMC_DMA_DESC_BUFF_SIZE, size); + desc[desc_cnt].desc2.bit.buffer_addr0 = (rt_uint32_t)buf + len; + desc[desc_cnt].desc3.bit.buffer_addr1 = (rt_uint32_t)mmc_obj->descriptors + (desc_cnt + 1) * sizeof(MMC_DMA_Descriptors); + + size -= desc[desc_cnt].desc1.bit.buffer1_size; + len += desc[desc_cnt].desc1.bit.buffer1_size; + desc_cnt++; + } + + desc[0].desc0.bit.first_descriptor = 1; + desc[desc_cnt-1].desc0.bit.last_descriptor = 1; + desc[desc_cnt-1].desc3.bit.buffer_addr1 = 0; +} + + +inline rt_uint32_t MMC_GetWaterlevel(struct fh_mmc_obj *mmc_obj) +{ + return (GET_REG(mmc_obj->base + OFFSET_SDC_STATUS) >> 17) & 0x1fff; +} + +inline rt_uint32_t MMC_GetStatus(struct fh_mmc_obj *mmc_obj) +{ + return GET_REG(mmc_obj->base + OFFSET_SDC_STATUS); +} + +inline rt_uint32_t MMC_GetRawInterrupt(struct fh_mmc_obj *mmc_obj) +{ + return GET_REG(mmc_obj->base + OFFSET_SDC_RINTSTS); +} + +inline rt_uint32_t MMC_GetUnmaskedInterrupt(struct fh_mmc_obj *mmc_obj) +{ + return GET_REG(mmc_obj->base + OFFSET_SDC_MINTSTS); +} + +inline rt_uint32_t MMC_ClearRawInterrupt(struct fh_mmc_obj *mmc_obj, rt_uint32_t interrupts) +{ + return SET_REG(mmc_obj->base + OFFSET_SDC_RINTSTS, interrupts); +} + +inline rt_uint32_t MMC_GetInterruptMask(struct fh_mmc_obj *mmc_obj) +{ + return GET_REG(mmc_obj->base + OFFSET_SDC_INTMASK); +} + +inline rt_uint32_t MMC_SetInterruptMask(struct fh_mmc_obj *mmc_obj, rt_uint32_t mask) +{ + return SET_REG(mmc_obj->base + OFFSET_SDC_INTMASK, mask); +} + +inline void MMC_SetByteCount(struct fh_mmc_obj *mmc_obj, rt_uint32_t bytes) +{ + SET_REG(mmc_obj->base + OFFSET_SDC_BYTCNT, bytes); +} + +inline void MMC_SetBlockSize(struct fh_mmc_obj *mmc_obj, rt_uint32_t size) +{ + SET_REG(mmc_obj->base + OFFSET_SDC_BLKSIZ, size); +} + +inline rt_uint32_t MMC_GetResponse(struct fh_mmc_obj *mmc_obj, int resp_num) +{ + return GET_REG(mmc_obj->base + OFFSET_SDC_RESP0 + resp_num * 4); +} + +inline rt_uint32_t MMC_IsFifoEmpty(struct fh_mmc_obj *mmc_obj) +{ + return (GET_REG(mmc_obj->base + OFFSET_SDC_STATUS) >> 2) & 0x1; +} + +inline rt_uint32_t MMC_IsDataStateBusy(struct fh_mmc_obj *mmc_obj) +{ + return (GET_REG(mmc_obj->base + OFFSET_SDC_STATUS) >> 10) & 0x1; +} + +int MMC_WriteData(struct fh_mmc_obj *mmc_obj, rt_uint32_t *buf, rt_uint32_t size) +{ + int filled = 0, fifo_available, i, retries; + + for (i=0; i 10000) + { + rt_kprintf("ERROR: %s, get water level timeout\n", __func__); + return -RT_ETIMEOUT; + } + } + while(!fifo_available); + SET_REG(mmc_obj->base + OFFSET_SDC_FIFO, *buf++); + } + + retries = 0; + while(MMC_IsDataStateBusy(mmc_obj)) + { + if(retries++ > 10000) + { + rt_kprintf("ERROR: %s, timeout, data line keep being busy\n", __func__); + return -RT_ETIMEOUT; + } + } + + return 0; +} +int MMC_ReadData(struct fh_mmc_obj *mmc_obj, rt_uint32_t *buf, rt_uint32_t size) +{ + int fifo_available, i, retries; + + for (i=0; i 10000) + { + rt_kprintf("ERROR: %s, get water level timeout\n", __func__); + return -RT_ETIMEOUT; + } + } + while(!fifo_available); + + *buf++ = GET_REG(mmc_obj->base + OFFSET_SDC_FIFO); + } + + retries = 0; + while(MMC_IsDataStateBusy(mmc_obj)) + { + if(retries++ > 10000) + { + rt_kprintf("ERROR: %s, timeout, data line keep being busy\n", __func__); + return -RT_ETIMEOUT; + } + } + + return 0; +} + + +int MMC_UpdateClockRegister(struct fh_mmc_obj *mmc_obj, int div) +{ + rt_uint32_t tick, timeout; + + tick = rt_tick_get(); + timeout = tick + RT_TICK_PER_SECOND / 10; //100ms in total + + /* disable clock */ + SET_REG(mmc_obj->base + OFFSET_SDC_CLKENA, 0); + SET_REG(mmc_obj->base + OFFSET_SDC_CLKSRC, 0); + + /* inform CIU */ + SET_REG(mmc_obj->base + OFFSET_SDC_CMD, 1<<31 | 1<<21); + while(GET_REG(mmc_obj->base + OFFSET_SDC_CMD) & 0x80000000) + { + tick = rt_tick_get(); + if(tick > timeout) + { + rt_kprintf("ERROR: %s, update clock timeout\n", __func__); + return -RT_ETIMEOUT; + } + } + + /* set clock to desired speed */ + SET_REG(mmc_obj->base + OFFSET_SDC_CLKDIV, div); + + /* inform CIU */ + SET_REG(mmc_obj->base + OFFSET_SDC_CMD, 1<<31 | 1<<21); + + while(GET_REG(mmc_obj->base + OFFSET_SDC_CMD) & 0x80000000) + { + tick = rt_tick_get(); + if(tick > timeout) + { + rt_kprintf("ERROR: %s, update clock timeout\n", __func__); + return -RT_ETIMEOUT; + } + } + + /* enable clock */ + SET_REG(mmc_obj->base + OFFSET_SDC_CLKENA, 1); + + /* inform CIU */ + SET_REG(mmc_obj->base + OFFSET_SDC_CMD, 1<<31 | 1<<21); + + while(GET_REG(mmc_obj->base + OFFSET_SDC_CMD) & 0x80000000) + { + tick = rt_tick_get(); + if(tick > timeout) + { + rt_kprintf("ERROR: %s, update clock timeout\n", __func__); + return -RT_ETIMEOUT; + } + } + + return 0; +} + +int MMC_SetCardWidth(struct fh_mmc_obj *mmc_obj, int width) +{ + switch(width) + { + case MMC_CARD_WIDTH_1BIT: + SET_REG(mmc_obj->base + OFFSET_SDC_CTYPE, 0); + break; + case MMC_CARD_WIDTH_4BIT: + SET_REG(mmc_obj->base + OFFSET_SDC_CTYPE, 1); + break; + default: + rt_kprintf("ERROR: %s, card width %d is not supported\n", __func__, width); + return -RT_ERROR; + break; + } + return 0; +} + +int MMC_SendCommand(struct fh_mmc_obj *mmc_obj, rt_uint32_t cmd, rt_uint32_t arg, rt_uint32_t flags) +{ + rt_uint32_t reg, tick, timeout; + + tick = rt_tick_get(); + timeout = tick + RT_TICK_PER_SECOND; //1s + + SET_REG(mmc_obj->base + OFFSET_SDC_CMDARG, arg); + flags |= 1<<31 | 1<<29 | cmd; + + SET_REG(mmc_obj->base + OFFSET_SDC_CMD, flags); + + while(GET_REG(mmc_obj->base + OFFSET_SDC_CMD) & MMC_CMD_START_CMD) + { + tick = rt_tick_get(); + if(tick > timeout) + { + rt_kprintf("ERROR: %s, send cmd timeout\n", __func__); + return -RT_ETIMEOUT; + } + } + + //fixme: check HLE_INT_STATUS + return 0; +} + +int MMC_ResetFifo(struct fh_mmc_obj *mmc_obj) +{ + rt_uint32_t reg, tick, timeout; + + tick = rt_tick_get(); + timeout = tick + RT_TICK_PER_SECOND / 10; //100ms + + reg = GET_REG(mmc_obj->base + OFFSET_SDC_CTRL); + reg |= 1 << 1; + SET_REG(mmc_obj->base + OFFSET_SDC_CTRL, reg); + + //wait until fifo reset finish + while(GET_REG(mmc_obj->base + OFFSET_SDC_CTRL) & MMC_CTRL_FIFO_RESET) + { + tick = rt_tick_get(); + if(tick > timeout) + { + rt_kprintf("ERROR: %s, FIFO reset timeout\n", __func__); + return -RT_ETIMEOUT; + } + } + + return 0; +} + +int MMC_Reset(struct fh_mmc_obj *mmc_obj) +{ + rt_uint32_t reg, tick, timeout; + + tick = rt_tick_get(); + timeout = tick + RT_TICK_PER_SECOND / 10; //100ms + + reg = GET_REG(mmc_obj->base + OFFSET_SDC_BMOD); + reg |= MMC_BMOD_RESET; + SET_REG(mmc_obj->base + OFFSET_SDC_BMOD, reg); + + while(GET_REG(mmc_obj->base + OFFSET_SDC_BMOD) & MMC_BMOD_RESET) + { + tick = rt_tick_get(); + if(tick > timeout) + { + rt_kprintf("ERROR: %s, BMOD Software reset timeout\n", __func__); + return -RT_ETIMEOUT; + } + } + + reg = GET_REG(mmc_obj->base + OFFSET_SDC_CTRL); + reg |= MMC_CTRL_CONTROLLER_RESET | MMC_CTRL_FIFO_RESET | MMC_CTRL_DMA_RESET; + SET_REG(mmc_obj->base + OFFSET_SDC_CTRL, reg); + + tick = rt_tick_get(); + timeout = tick + RT_TICK_PER_SECOND / 10; //100ms + while(GET_REG(mmc_obj->base + OFFSET_SDC_CTRL) & (MMC_CTRL_CONTROLLER_RESET | MMC_CTRL_FIFO_RESET | MMC_CTRL_DMA_RESET)) + { + tick = rt_tick_get(); + if(tick > timeout) + { + rt_kprintf("ERROR: %s, CTRL dma|fifo|ctrl reset timeout\n", __func__); + return -RT_ETIMEOUT; + } + } + return 0; +} + + +void MMC_Init(struct fh_mmc_obj *mmc_obj) +{ + rt_uint32_t reg; + + if(mmc_obj->mmc_reset) + mmc_obj->mmc_reset(mmc_obj); + + MMC_Reset(mmc_obj); + + //fixed burst + reg = GET_REG(mmc_obj->base + OFFSET_SDC_BMOD); + reg |= 1 << 1; + SET_REG(mmc_obj->base + OFFSET_SDC_BMOD, reg); + + //fixme: power on ? ctrl by gpio ? + + MMC_ClearRawInterrupt(mmc_obj, MMC_INT_STATUS_ALL); + MMC_SetInterruptMask(mmc_obj, 0x0); + + //fixme: use_internal_dma + reg = GET_REG(mmc_obj->base + OFFSET_SDC_CTRL); + reg |= MMC_CTRL_INT_ENABLE; +#ifdef MMC_USE_DMA + reg |= MMC_CTRL_USE_DMA; +#endif + SET_REG(mmc_obj->base + OFFSET_SDC_CTRL, reg); + + //set timeout param + SET_REG(mmc_obj->base + OFFSET_SDC_TMOUT, 0xffffffff); + + //set fifo + reg = GET_REG(mmc_obj->base + OFFSET_SDC_FIFOTH); + reg = (reg >> 16) & 0x7ff; + reg = ((0x2 << 28) | ((reg/2) << 16) | ((reg/2 + 1) << 0)); + SET_REG(mmc_obj->base + OFFSET_SDC_FIFOTH, reg); +} diff --git a/bsp/fh8620/libraries/driverlib/fh_pwm.c b/bsp/fh8620/libraries/driverlib/fh_pwm.c new file mode 100644 index 0000000000000000000000000000000000000000..af19110a466c8ee7cbe1c0e36cb0723e711b6f55 --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_pwm.c @@ -0,0 +1,42 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "inc/fh_driverlib.h" + +void PWM_Enable(struct fh_pwm_obj *pwm_obj, int enable) +{ + SET_REG(pwm_obj->base + OFFSET_PWM_CTRL, enable); +} + +unsigned int PWM_GetPwmCmd(struct fh_pwm_obj *pwm_obj, int device_id) +{ + return GET_REG(pwm_obj->base + OFFSET_PWM_CMD(device_id)); +} + +void PWM_SetPwmCmd(struct fh_pwm_obj *pwm_obj, int device_id, unsigned int reg) +{ + SET_REG(pwm_obj->base + OFFSET_PWM_CMD(device_id), reg); +} diff --git a/bsp/fh8620/libraries/driverlib/fh_sdio.c b/bsp/fh8620/libraries/driverlib/fh_sdio.c new file mode 100644 index 0000000000000000000000000000000000000000..8c9e69e07d99f9a65bc8bc68509f2679c9d14ae7 --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_sdio.c @@ -0,0 +1,2085 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "string.h" +#include "inc/fh_sdio.h" +#include "fh_arch.h" +//#include "interrupt.h" + +#define SDIO_PRINTF rt_kprintf + +#define SDC_USE_IDMA + +#define INSTRUCTIONS_PER_USEC 1000 +#define CMD_TIMEOUT_USEC 100000 +#define DATA_READY_TIMEOUT_USEC 200000 +#define DMA_TRANSFER_TIMEOUT_TICKS 300 +#define DATA_TRANSFER_OVER_TIMEOUT_USEC 1000 +#define ACMD41_RETRY_COUNT 1000//100000 + +#define CIU_CLK 50000//25000//25000 //27MHz +#define MMC_FOD_VALUE 125 /* 125 KHz */ +#define NORM_FOD_VALUE 25000//5000//25000 /* 25 MHz */ +#define MMC_FOD_DIVIDER_VALUE (((CIU_CLK+MMC_FOD_VALUE*2-1)/(MMC_FOD_VALUE*2))) +#ifdef SDCARD_CLK_DIVIDER +#define ONE_BIT_BUS_FREQ SDCARD_CLK_DIVIDER +#else +#define ONE_BIT_BUS_FREQ (((CIU_CLK)/(NORM_FOD_VALUE*2))) +#endif + +static unsigned int sdc_clk_divider = ONE_BIT_BUS_FREQ; + +static sdc_t sdc_array[2]; + +static void plat_loop(unsigned int macrosecond) +{ + unsigned int clk; + + while (macrosecond-- > 0) { + for(clk=INSTRUCTIONS_PER_USEC; clk>0; clk--); + } +} + +static int synopmob_execute_command(unsigned int base, unsigned int cmd_register, unsigned int arg_register) +{ + unsigned int retries = CMD_TIMEOUT_USEC; + + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts, FIXME + synopmob_set_register(base+CMDARG, arg_register); + synopmob_set_register(base+CMD, cmd_register | (0x80000000|0x20000000/*fixed to use hold*/)); + + while (retries-- > 0) { + if (!(synopmob_read_register(base+CMD) & 0x80000000/*CMD done bit*/)) + return 0; + plat_loop(1); + } + + return ERRCMDRETRIESOVER; +} + +static int synopmob_wait_command_done(unsigned int base, unsigned int* inst, unsigned int flag) +{ + unsigned int retries = CMD_TIMEOUT_USEC; + unsigned int sts; + + while (retries-- > 0) { + sts = synopmob_read_register(base+RINTSTS); + if (sts && ((sts & flag) == flag) ) { + *inst = sts; + return 0; + } + plat_loop(1); + } + return ERRCMDRETRIESOVER; +} + +static int synopmob_wait_data_ready(unsigned int base) +{ + unsigned int retries = DATA_READY_TIMEOUT_USEC; + + while (retries-- > 0) { + if (!((synopmob_read_register(base+STATUS)) & 0x00000200)) { + return 0; + } + + plat_loop(1); + } + return ERRDATANOTREADY; +} + +static int synopmob_handle_standard_rinsts(unsigned int raw_int_stat) +{ + int error_status = 0; + + if ( raw_int_stat & INTMASK_ERROR) { + if (raw_int_stat & INTMSK_RESP_ERR) { + error_status = ERRRESPRECEP; + } + if (raw_int_stat & INTMSK_RCRC) { + error_status = ERRRESPCRC; + } + if (raw_int_stat & INTMSK_DCRC) { + error_status = ERRDCRC; + } + if (raw_int_stat & INTMSK_RTO) { + error_status = ERRRESPTIMEOUT; + } + if (raw_int_stat & INTMSK_DTO) { + error_status = ERRDRTIMEOUT; + } + if (raw_int_stat & INTMSK_HTO) { + error_status = ERRUNDERWRITE; + } + if (raw_int_stat & INTMSK_FRUN) { + error_status = ERROVERREAD; + } + if (raw_int_stat & INTMSK_HLE) { + error_status = ERRHLE; + } + if (raw_int_stat & INTMSK_SBE) { + error_status = ERRSTARTBIT; + } + if (raw_int_stat & INTMSK_EBE) { + error_status = ERRENDBITERR; + } + } + +//SDIO_PRINTF("------- %s, line %d raw_int_stat = %08x-------\n", __FUNCTION__, __LINE__, raw_int_stat); + return error_status; +} + +static int synopmob_check_r1_resp(unsigned int the_response) +{ + int retval = 0; + + if (the_response & R1CS_ERROR_OCCURED_MAP) { + if (the_response & R1CS_ADDRESS_OUT_OF_RANGE) { + retval = ERRADDRESSRANGE; + } else if (the_response & R1CS_ADDRESS_MISALIGN) { + retval = ERRADDRESSMISALIGN; + } else if (the_response & R1CS_BLOCK_LEN_ERR) { + retval = ERRBLOCKLEN; + } else if (the_response & R1CS_ERASE_SEQ_ERR) { + retval = ERRERASESEQERR; + } else if (the_response & R1CS_ERASE_PARAM) { + retval = ERRERASEPARAM; + } else if (the_response & R1CS_WP_VIOLATION) { + retval = ERRPROT; + } else if (the_response & R1CS_CARD_IS_LOCKED) { + retval = ERRCARDLOCKED; + } else if (the_response & R1CS_LCK_UNLCK_FAILED) { + retval = ERRCARDLOCKED; + } else if (the_response & R1CS_COM_CRC_ERROR) { + retval = ERRCRC; + } else if (the_response & R1CS_ILLEGAL_COMMAND) { + retval = ERRILLEGALCOMMAND; + } else if (the_response & R1CS_CARD_ECC_FAILED) { + retval = ERRECCFAILED; + } else if (the_response & R1CS_CC_ERROR) { + retval = ERRCCERR; + } else if (the_response & R1CS_ERROR) { + retval = ERRUNKNOWN; + } else if (the_response & R1CS_UNDERRUN) { + retval = ERRUNDERRUN; + } else if (the_response & R1CS_OVERRUN) { + retval = ERROVERRUN; + } else if (the_response & R1CS_CSD_OVERWRITE) { + retval = ERRCSDOVERWRITE; + } else if (the_response & R1CS_WP_ERASE_SKIP) { + retval = ERRPROT; + } else if (the_response & R1CS_ERASE_RESET) { + retval = ERRERASERESET; + } else if (the_response & R1CS_SWITCH_ERROR) { + retval = ERRFSMSTATE; + } + } + + return retval; +} + + +static int synopmob_check_r5_resp(unsigned int the_resp) +{ + int ret = 0; + + if (the_resp & R5_IO_ERR_BITS) { + if (the_resp & R5_IO_CRC_ERR) { + ret = ERRDCRC; + } else if (the_resp & R5_IO_BAD_CMD) { + ret = ERRILLEGALCOMMAND; + } else if (the_resp & R5_IO_GEN_ERR) { + ret = ERRUNKNOWN; + } else if (the_resp & R5_IO_FUNC_ERR) { + ret = ERRBADFUNC; + } else if (the_resp & R5_IO_OUT_RANGE) { + ret = ERRADDRESSRANGE; + } + } + + return ret; +} + +static int sd_send_cmd0(sdc_t* sdc) +{ + int ret; + unsigned int intst; + unsigned int base = sdc->ip_base; + + ret = synopmob_execute_command(base, 0x4000, 0); + if (!ret) { + ret = synopmob_wait_command_done(base, &intst, INTMSK_CMD_DONE); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); + return synopmob_handle_standard_rinsts(intst); + } + } + + return ret; +} + +static int sd_send_cmd2(sdc_t* sdc) +{ + int ret; + unsigned int intst; + unsigned int base = sdc->ip_base; + + ret = synopmob_execute_command(base, 0xC2, 0); + if (!ret) { + ret = synopmob_wait_command_done(base, &intst, INTMSK_CMD_DONE); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); + return synopmob_handle_standard_rinsts(intst); + } + } + + return ret; +} + +static int sd_send_cmd3(sdc_t* sdc) +{ + int ret; + unsigned int intst; + unsigned int resp; + unsigned int base = sdc->ip_base; + + ret = synopmob_execute_command(base, 0x43, 0); + if (!ret) { + ret = synopmob_wait_command_done(base, &intst, INTMSK_CMD_DONE); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); + ret = synopmob_handle_standard_rinsts(intst); + if (!ret) { + resp = synopmob_read_register(base+RESP0); + sdc->rca = resp >> 16; + resp = (resp & 0x1fff) | (((resp>>13)&1)<<19) | (((resp>>14)&3)<<22); + return synopmob_check_r1_resp(resp); + } + } + } + + return ret; +} + +static int sd_send_cmd_r1(sdc_t* sdc, unsigned int cmd, unsigned int arg, unsigned int buzy) +{ + int ret; + unsigned int intst; + unsigned int resp; + unsigned int base = sdc->ip_base; + + ret = synopmob_execute_command(base, cmd, arg); + if (!ret) { + ret = synopmob_wait_command_done(base, &intst, INTMSK_CMD_DONE); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); + ret = synopmob_handle_standard_rinsts(intst); + if (!ret) { + resp = synopmob_read_register(base+RESP0); + ret = synopmob_check_r1_resp(resp); + if (buzy && !ret) { + ret = synopmob_wait_data_ready(base); + } + } + } + } + + return ret; +} + +static int sd_send_cmd7(sdc_t* sdc) +{ + return sd_send_cmd_r1(sdc, 0x47, sdc->rca<<16, 1); +} + +static int sd_send_uncmd7(sdc_t* sdc) +{ + int ret; + unsigned int intst; + unsigned int base = sdc->ip_base; + + ret = synopmob_execute_command(base, 0x7, 0); + if (!ret) { + ret = synopmob_wait_command_done(base, &intst, INTMSK_CMD_DONE); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); + ret = synopmob_handle_standard_rinsts(intst); + } + } + + return ret; +} + +static int sd_send_cmd16(sdc_t* sdc) +{ + return sd_send_cmd_r1(sdc, 0x50, 512, 0); +} + +static int sd_send_cmd55(sdc_t* sdc) +{ + return sd_send_cmd_r1(sdc, 0x77, sdc->rca<<16, 0); +} + +static int sd_send_acmd6(sdc_t* sdc, unsigned int bitwidth) +{ + unsigned int cmd_arg; + int ret; + unsigned int base = sdc->ip_base; + + ret = sd_send_cmd55(sdc); + if (!ret) { + cmd_arg = 0; //default to 1bit mode + if (bitwidth == 4) { + cmd_arg = 2; // 4bit mode + } + ret = sd_send_cmd_r1(sdc, 0x2046, cmd_arg, 0); + if (!ret) { + if (bitwidth == 4) { + synopmob_set_register(base+CTYPE, FOUR_BIT_MODE); + } + else { + synopmob_set_register(base+CTYPE, ONE_BIT_MODE); + } + } + } + + return ret; +} + +#ifdef SDC_USE_IDMA +static int sdc_read_write_block(HSDC handle, unsigned int rw, unsigned int blk, unsigned int num, unsigned char* buffer) +{ + sdc_t* sdc = (sdc_t*)handle; + volatile DmaDesc *pDmaDesc = sdc->pDmaDesc; + int ret; + unsigned int intsts = 0; + unsigned int cmd; + unsigned int multi = 0; + unsigned int base = sdc->ip_base; + int loop_for_command_done_check = DATA_TRANSFER_OVER_TIMEOUT_USEC; + rt_err_t err; + + // valid check + if (synopmob_read_register(base+CDETECT) & 1) { + return ERRCARDNOTCONN; + } + if (!num || num > 16) { + return ERRNOTSUPPORTED; + } + if (blk + num > sdc->sectors) { + return ERRADDRESSRANGE; + } + + if ( rw ) { + flush_dcache_range((unsigned long)buffer, num << 9); + } + else { + // to avoid memset bug? + inv_dcache_range((unsigned long)buffer, num << 9); + } + + err = rt_sem_take(sdc->mutex, RT_WAITING_FOREVER); + if (err != RT_EOK) { + return ERRNORES; + } + + // reset + synopmob_set_bits(base+CTRL, FIFO_RESET); //reset FIFO + while (synopmob_read_register(base+CTRL) & FIFO_RESET); + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + + cmd = 0x2658; // write + if ( !rw ) { + cmd = 0x2251; //read + } + //if (num > 1) { + if (num >= 1) { // some card fail on sigle-block mode, so use multi-block instead of sigle-block mode. + cmd++; + multi++; + } + if (sdc->card_type == SD_TYPE) { + blk <<= 9; //SD stadand capability card use 512 unit. + } + num <<= 9; + + pDmaDesc->desc0 |= DescOwnByDma | DescFirstDesc | DescLastDesc; + pDmaDesc->desc1 = ((num << DescBuf1SizeShift) & DescBuf1SizMsk); + pDmaDesc->desc2 = (unsigned int)buffer; + flush_dcache_range((unsigned long)pDmaDesc, sizeof(DmaDesc)); // add SZ_ADJUST + synopmob_set_register(base + DBADDR, (unsigned int)(pDmaDesc)); // add SZ_ADJUST + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, num); + synopmob_set_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_set_bits(base + BMOD,BMOD_DE); + + ret = synopmob_execute_command(base, cmd, blk); + if ( !ret ) { + ret = ERRIDMA; + synopmob_set_bits(base+CTRL, INT_ENABLE); + err = rt_sem_take(sdc->sem, DMA_TRANSFER_TIMEOUT_TICKS); + if ( !err ) { + while (--loop_for_command_done_check > 0) { + intsts = synopmob_read_register(base+RINTSTS); + if ((intsts & (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) == (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) { + break; + } + plat_loop(1); + } + ret = synopmob_handle_standard_rinsts(intsts); + if (!ret ) { + if( !loop_for_command_done_check || !(sdc->idsts & 0x100)) { //normal interrupt + ret = ERRIDMA; + } + } + } + } + + if (ret) { + char* op = "read"; + if (rw) + op = "write"; + + SDIO_PRINTF("sdc_read_write_block(%s) fail:, ret = %d\n", op, ret); + } + + synopmob_clear_bits(base+CTRL, INT_ENABLE); + synopmob_clear_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_clear_bits(base + BMOD,BMOD_DE); + + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, 512); + + synopmob_set_register(base + RINTSTS, 0xfffe); + + if ( !ret && rw) { + ret = synopmob_wait_data_ready(base); + } + + if (!ret && multi ) { //send STOP_TRANSACTION command + ret = sd_send_cmd_r1(sdc, 0x404c, 0, 1); + } + + rt_sem_release(sdc->mutex); + + if ( !rw && !ret ) { //read + inv_dcache_range((unsigned long)buffer, num); + } + + return ret; +} +#else //no IDMA +static int sdc_read_write_block(HSDC handle, unsigned int rw, unsigned int blk, unsigned int num, unsigned char* buffer) +{ + sdc_t* sdc = (sdc_t*)handle; + volatile DmaDesc *pDmaDesc = sdc->pDmaDesc; + int ret; + unsigned int intsts = 0; + unsigned int entries; + unsigned int cmd; + unsigned int multi = 0; + unsigned int base = sdc->ip_base; + int loop_for_command_done_check = DATA_TRANSFER_OVER_TIMEOUT_USEC; + rt_err_t err; + + // valid check + if (synopmob_read_register(base+CDETECT) & 1) { + return ERRCARDNOTCONN; + } + if (!num || num > 16) { + return ERRNOTSUPPORTED; + } + if (blk + num > sdc->sectors) { + return ERRADDRESSRANGE; + } + + if ( rw ) { + flush_dcache_range((unsigned long)buffer, num << 9); + } + else { + // to avoid memset bug? + inv_dcache_range((unsigned long)buffer, num << 9); + } + + err = rt_sem_take(sdc->mutex, RT_WAITING_FOREVER); + if (err != RT_EOK) { + return ERRNORES; + } + + // reset + synopmob_set_bits(base+CTRL, FIFO_RESET); //reset FIFO + while (synopmob_read_register(base+CTRL) & FIFO_RESET); + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + + cmd = 0x2658; // write + if ( !rw ) { + cmd = 0x2251; //read + } + if (num > 1) { + cmd++; + multi++; + } + if (sdc->card_type == SD_TYPE) { + blk <<= 9; //SD stadand capability card use 512 unit. + } + num <<= 9; + + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, num); + + ret = synopmob_execute_command(base, cmd, blk); + if ( !ret ) { + while (1) { + ret = synopmob_wait_command_done(base, &intsts, 0); + if (ret) + break; + + ret = synopmob_handle_standard_rinsts(intsts); + if (ret) + break; + + if (!rw && (intsts & (INTMSK_RXDR|INTMSK_DAT_OVER)) ){ + while (num > 0 ) { + entries = synopmob_read_register(base + STATUS); + if (!GET_FIFO_COUNT(entries)) + break; + *((volatile unsigned int*)buffer) = synopmob_read_register(base + FIFODAT); + buffer += 4; + num -= 4; + } + } + + if (rw && ( intsts & INTMSK_TXDR ) ) { + while (num > 0) { + entries = synopmob_read_register(base+STATUS); + if ( entries & 8 ) { //FIFO is full + break; + } + synopmob_set_register(base+FIFODAT, *((volatile unsigned int*)buffer)); + buffer += 4; + num -= 4; + } + } + + if ( intsts & INTMSK_DAT_OVER ) { + break; + } + + if (intsts & INTMSK_CMD_DONE) { + entries = synopmob_read_register(base+RESP0); + ret = synopmob_check_r1_resp(entries); + if (ret) { + break; + } + } + + synopmob_set_register(base+RINTSTS, intsts); //write to clear + intsts = 0; + } + + if (intsts) { + synopmob_set_register(base+RINTSTS, intsts); //write to clear + } + } + + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, 512); + synopmob_set_register(base + RINTSTS, 0xfffe); + + if ( !ret && rw) { + ret = synopmob_wait_data_ready(base); + } + + if (!ret && multi ) { //send STOP_TRANSACTION command + ret = sd_send_cmd_r1(sdc, 0x404c, 0, 1); + } + + rt_sem_release(sdc->mutex); + + if ( !rw && !ret ) { //read + inv_dcache_range((unsigned long)buffer, num); + } + + return ret; +} +#endif //SDC_USE_IDMA + +int sdc_write_block(HSDC handle, unsigned int blk, unsigned int num, unsigned char* buffer) +{ + return sdc_read_write_block(handle, 1, blk, num, buffer); +} + +int sdc_read_block(HSDC handle, unsigned int blk, unsigned int num, unsigned char* buffer) +{ + return sdc_read_write_block(handle, 0, blk, num, buffer); +} + +int sdc_erase_block(HSDC handle, unsigned int blk, unsigned int num) +{ + int ret; + sdc_t* sdc = (sdc_t*)handle; + + if (sdc->card_type == SD_TYPE) { + blk <<= 9; //SD stadand capability card use 512 unit. + num = ((num-1)<<9) + blk; + } + else { + num = blk + num - 1; + } + + ret = sd_send_cmd_r1(sdc, 0x40|32, blk, 0); // cmd32 + if (!ret) { + ret = sd_send_cmd_r1(sdc, 0x40|33, num, 0); // cmd33 + if (!ret) { + ret = sd_send_cmd_r1(sdc, 0x40|38, 0, 1); // cmd38 + } + } + + return ret; +} + +int sdc_get_sector_num(HSDC handle) +{ + return ((sdc_t*)handle)->sectors; +} + +static int sd_send_cmd9(sdc_t* sdc) +{ + int ret; + unsigned int intst; + unsigned int resp0; + unsigned int resp1; + unsigned int resp2; + unsigned int resp3; + unsigned int base = sdc->ip_base; + unsigned int C_SIZE; + unsigned int C_SIZE_MULT; + unsigned int READ_BL_LEN; + + ret = synopmob_execute_command(base, 0xC9, sdc->rca<<16); + if (!ret) { + ret = synopmob_wait_command_done(base, &intst, INTMSK_CMD_DONE); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); + ret = synopmob_handle_standard_rinsts(intst); + if (!ret) { + sdc->csd[0] = resp0 = synopmob_read_register(base+RESP0); + sdc->csd[1] = resp1 = synopmob_read_register(base+RESP1); + sdc->csd[2] = resp2 = synopmob_read_register(base+RESP2); + sdc->csd[3] = resp3 = synopmob_read_register(base+RESP3); + + if ((resp3>>30) == 0) { //CSD version 1.0 + C_SIZE = (resp1 >> 30) | ((resp2 & 0x3ff)<<2); + C_SIZE_MULT = ((resp1 >> 15) & 0x07); + READ_BL_LEN = ((resp2 >> 16) & 0xf); + sdc->sectors = ((((C_SIZE+1)<<(C_SIZE_MULT+2))<<(READ_BL_LEN))>>9); + } + else { //CSD version 2.0 + sdc->sectors = (((resp1 >> 16)+1)<<10); + } + } + } + } + + return ret; +} + +static int sd_send_cmd5(sdc_t* sdc, unsigned int arg, unsigned int* resp) +{ + unsigned int cmd_reg = 0x45; + unsigned int intst; + int ret; + unsigned int base = sdc->ip_base; + + ret = synopmob_execute_command(base, cmd_reg, arg); + if (!ret) { + ret = synopmob_wait_command_done(base, &intst, INTMSK_CMD_DONE); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); + ret = synopmob_handle_standard_rinsts(intst); + if (!ret) { + *resp = synopmob_read_register(base+RESP0); + } + } + } + + return ret; +} + +static int sd_send_cmd8(sdc_t* sdc) +{ + int ret; + unsigned int cmd_reg = 0x48; + unsigned int intst; + unsigned int err = 0; + unsigned int base = sdc->ip_base; + + ret = synopmob_execute_command(base, cmd_reg, 0x000001A5); + if (!ret) { + while (1) { + ret = synopmob_wait_command_done(base, &intst, 0); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); + err |= synopmob_handle_standard_rinsts(intst); + if (intst & INTMSK_CMD_DONE) { + break; + } + } + } + } + + return err; +} + +static int sd_send_acmd41(sdc_t* sdc, int* hcs) +{ + unsigned int cmd_reg = 0x69; + unsigned int resp; + int ret = 0; + unsigned int count = ACMD41_RETRY_COUNT; + unsigned int cmd_arg = 0xff8000; + unsigned int base = sdc->ip_base; + + if (*hcs) { + cmd_arg |= (1<<30); + } + while ( count > 0) { + SDC_WHERE(); + ret = sd_send_cmd55(sdc); + if (ret) + break; + + SDC_WHERE(); + ret = synopmob_execute_command(base, cmd_reg, cmd_arg); + if (ret) + break; + + SDC_WHERE(); + ret = synopmob_wait_command_done(base, &resp, INTMSK_CMD_DONE); + if ( ret ) + break; + + SDC_WHERE(); + synopmob_set_register(base+RINTSTS, resp); + ret = synopmob_handle_standard_rinsts(resp); + if (!ret) { + SDC_WHERE(); + resp = synopmob_read_register(base+RESP0); + if (resp & 0x80000000) { //card is ready. + SDC_WHERE(); + if ( !(resp & (1<<30)) ) { + SDC_WHERE(); + *hcs = 0; + } + if ( (resp & 0x00ff8000) != 0x00ff8000 ) { //not supported voltage + ret = ERRHARDWARE; + } + break; + } + } + + --count; + plat_loop(1); + } + + if (!count) + ret = ERRACMD41TIMEOUT; + + return ret; +} + +static int sd_send_acmd51(sdc_t* sdc) //Send SCR +{ + unsigned int cmd_reg = 0x2273; + unsigned int resp; + int ret; + unsigned int intst = 0; + unsigned int entries; + int count = 1; + unsigned int base = sdc->ip_base; + + ret = sd_send_cmd55(sdc); + if (!ret) { + synopmob_set_register(base+BLKSIZ, 8); + synopmob_set_register(base+BYTCNT, 8); + ret = synopmob_execute_command(base, cmd_reg, 0); + if (!ret) { + while (1) { + ret = synopmob_wait_command_done(base, &intst, 0); + if (ret) { + break; + } + + ret = synopmob_handle_standard_rinsts(intst); + if (ret) { + break; + } + + if (intst & INTMSK_CMD_DONE) { + resp = synopmob_read_register(base+RESP0); + ret = synopmob_check_r1_resp(resp); + if (ret) + break; + } + + if (intst & INTMSK_DAT_OVER) { + entries = synopmob_read_register(base + STATUS); + if (GET_FIFO_COUNT(entries) == 2) { + while (count >= 0) { + entries = synopmob_read_register(base + FIFODAT); + sdc->scr[count--] = BE32_TO_CPU(entries); + } + } + break; + } + + synopmob_set_register(base+RINTSTS, intst); + intst = 0; + } + + if (intst) { + synopmob_set_register(base+RINTSTS, intst); + } + } + + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, 512); + } + + return ret; +} + + +static int sd_send_cmd6(sdc_t* sdc, unsigned int cmd_arg, unsigned int* data_buff) +{ + unsigned int cmd_reg = 0x2246; + unsigned int resp; + int ret; + unsigned int intst = 0; + unsigned int entries; + int count = 64; + unsigned int base = sdc->ip_base; + + synopmob_set_register(base+BLKSIZ, 64); + synopmob_set_register(base+BYTCNT, 64); + ret = synopmob_execute_command(base, cmd_reg, cmd_arg); + if (!ret) { + while (1) { + ret = synopmob_wait_command_done(base, &intst, 0); + if (ret) { + break; + } + + ret = synopmob_handle_standard_rinsts(intst); + if (ret) { + break; + } + + if (intst & INTMSK_CMD_DONE) { + resp = synopmob_read_register(base+RESP0); + ret = synopmob_check_r1_resp(resp); + if (ret) + break; + } + + while (count > 0) { + entries = synopmob_read_register(base + STATUS); + if ( !GET_FIFO_COUNT(entries) ) { + break; + } + *(data_buff++) = synopmob_read_register(base + FIFODAT); + count -= 4; + } + + if (intst & INTMSK_DAT_OVER) { + break; + } + + synopmob_set_register(base+RINTSTS, intst); //write to clear + intst = 0; + } + + if (intst) { + synopmob_set_register(base+RINTSTS, intst); //write to clear + } + } + + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, 512); + + return ret; +} + +static int synopmob_send_clock_only_cmd(unsigned int base) +{ + return synopmob_execute_command(base, 0x202000, 0); +} + + +static int synopmob_disable_all_clocks(unsigned int base) +{ + synopmob_set_register(base+CLKENA, 0); + return synopmob_send_clock_only_cmd(base); +} + +static int synopmob_enable_clocks_with_val(unsigned int base, unsigned int val) +{ + synopmob_set_register(base+CLKENA, val); + return synopmob_send_clock_only_cmd(base); +} + + +static int synopmob_set_clk_freq(sdc_t* sdc, unsigned int divider) +{ + #define MAX_DIVIDER_VALUE 0xff + + unsigned int orig_clkena; + int retval; + unsigned int base = sdc->ip_base; + + if (divider > MAX_DIVIDER_VALUE) { + return 0xffffffff; + } + + /* To make sure we dont disturb enable/disable settings of the cards*/ + orig_clkena = synopmob_read_register(base+CLKENA); + + /* Disable all clocks before changing frequency the of card clocks */ + if ((retval = synopmob_disable_all_clocks(base)) != 0) { + return retval; + } + /* Program the clock divider in our case it is divider 0 */ + synopmob_clear_bits(base+CLKDIV, MAX_DIVIDER_VALUE); + synopmob_set_bits(base+CLKDIV, divider); + + /*Send the command to CIU using synopmob_send_clock_only_cmd and enable the clocks in CLKENA register */ + if ((retval = synopmob_send_clock_only_cmd(base)) != 0) { + synopmob_enable_clocks_with_val(base, orig_clkena); + return retval; + } + + return synopmob_enable_clocks_with_val(base, orig_clkena); +} + +static int enum_sd_card(sdc_t* sdc) +{ + int ret; + int count = 1000; + int hcs = 0; + unsigned int buffer[16]; + unsigned int base = sdc->ip_base; + + if (synopmob_read_register(base+CDETECT) & 1) { + return ERRCARDNOTCONN; + } + + #if 0 + synopmob_set_bits(0x98500004, (1<<24)); //set to output mode + synopmob_set_bits(0x98500000, (1<<24)); //power off + plat_loop(1000000/5); //Lets give some ramp down period + synopmob_clear_bits(0x98500000, (1<<24)); //power on + plat_loop(1000000/5);//Lets give some ramp down period + #endif + + synopmob_set_register(base+CTYPE, ONE_BIT_MODE); + + synopmob_set_register(base+CLKENA, 0x00000001); /*enable clock, non-low-power mode*/ + ret = synopmob_set_clk_freq(sdc, MMC_FOD_DIVIDER_VALUE); + + if ( !ret ) { + plat_loop(1000); //enough for 74 clock. + SDC_WHERE(); + ret = sd_send_cmd0(sdc); //CMD0 has no response + } + + if ( !ret ) { + SDC_WHERE(); + ret = sd_send_cmd8(sdc); //even if CMD8 get response, it may be V1.0 card. + if (!ret) { + hcs = 1; + } + SDC_WHERE(); + ret = sd_send_acmd41(sdc, &hcs); + } + + if (!ret) { + SDC_WHERE(); + ret = sd_send_cmd2(sdc); //CID + } + + if (!ret) { + SDC_WHERE(); + ret = sd_send_cmd3(sdc); //get RCA + } + + if (!ret) { + SDC_WHERE(); + ret = sd_send_cmd9(sdc); //CSD + } + + if (!ret) { + SDC_WHERE(); + ret = sd_send_cmd7(sdc); //select the card + } + + if (!ret && (sdc->wkmod & SDC_WKMOD_4WIRE) ) { + SDC_WHERE(); + ret = sd_send_acmd51(sdc); //SCR + if (!ret && (sdc->scr[1] & 0x00040000)) { // 4bit mode supported? + ret = sd_send_acmd6(sdc, 4); //switch to 4bit mode + } + } + + if (!ret && (sdc->wkmod & SDC_WKMOD_50M_HI_SPEED) && (sdc->csd[2] & 0x40000000) ) { //judge whether class10 is supported? CMD6 is belonging to class10. + SDC_WHERE(); + ret = sd_send_cmd6(sdc, 0x00fffff1, buffer); //switch to high speed mode. + if ( !ret && (*(((unsigned char*)buffer)+13)&0x02) ) { //the card support high speed mode? + SDC_WHERE(); + ret = sd_send_cmd6(sdc, 0x80fffff1, buffer); //switch to high speed mode. + if (!ret && ((*(((unsigned char*)buffer)+16) & 0xf) == 1) ) { + //switch to high speed mode sucess. + sd_send_uncmd7(sdc); //deselect the card + sd_send_cmd9(sdc); //CSD + ret = sd_send_cmd7(sdc); //select the card + } + } + } + + if (!ret && (sdc->wkmod & (SDC_WKMOD_50M_HI_SPEED|SDC_WKMOD_25M_STAND_SPEED))) { + if ( (sdc->csd[3] & 0xff) == 0x5A ) { //50MHz high speed mode. + SDC_WHERE(); + ret = synopmob_set_clk_freq(sdc, (((CIU_CLK)/(50000*2)))); + } + else if ( (sdc->csd[3] & 0xff) == 0x32 ) { + SDC_WHERE(); //25MHz standard speed mode. + ret = synopmob_set_clk_freq(sdc, sdc_clk_divider/*ONE_BIT_BUS_FREQ*/); + } + } + + if (!ret) { + sdc->card_type = SD_TYPE; + if (hcs) { + sdc->card_type = SD_2_0_TYPE; + } + } + + return ret; +} + +int sdio_drv_creg_read(HSDC handle, int addr, int fn, unsigned int *resp) +{ + sdc_t* sdc = (sdc_t*)handle; + unsigned int arg; + unsigned int cmd_reg = 0x74; + unsigned int intst; + int ret; + unsigned int base = sdc->ip_base; + rt_err_t err; + + if(resp) { + *resp = 0; + } + + err = rt_sem_take(sdc->mutex, RT_WAITING_FOREVER); + if (err != RT_EOK) { + return ERRNORES; + } + + arg = (fn << 28) | (addr << 9); + ret = synopmob_execute_command(base, cmd_reg, arg); + if (!ret) { + ret = synopmob_wait_command_done(base, &intst, INTMSK_CMD_DONE); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); //write to clear + ret = synopmob_handle_standard_rinsts(intst); + if (!ret) { + *resp = synopmob_read_register(base+RESP0); + ret = synopmob_check_r5_resp(*resp); + } + } + } + + rt_sem_release(sdc->mutex); + + if (ret) { + ret++; + ret--; + SDIO_PRINTF("sdio_drv_creg_read fail:, ret = %d\n", ret); + } + + return ret; +} + +int sdio_drv_creg_write(HSDC handle, int addr, int fn, unsigned char data, unsigned int *resp) +{ + sdc_t* sdc = (sdc_t*)handle; + unsigned int arg; + unsigned int cmd_reg = 0x74; + unsigned int intst; + int ret; + unsigned int base = sdc->ip_base; + rt_err_t err; + + err = rt_sem_take(sdc->mutex, RT_WAITING_FOREVER); + if (err != RT_EOK) { + return ERRNORES; + } + + arg = (1 << 31) | (fn << 28) | (1 << 27) | (addr << 9) | data; + ret = synopmob_execute_command(base, cmd_reg, arg); + if (!ret) { + ret = synopmob_wait_command_done(base, &intst, INTMSK_CMD_DONE); + if (!ret) { + synopmob_set_register(base+RINTSTS, intst); //write to clear + ret = synopmob_handle_standard_rinsts(intst); + if (!ret) { + *resp = synopmob_read_register(base+RESP0); + ret = synopmob_check_r5_resp(*resp); + } + } + } + + rt_sem_release(sdc->mutex); + if (ret) { + ret++; + ret--; + SDIO_PRINTF("sdio_drv_creg_write fail:, ret = %d\n", ret); + } + + return ret; +} + +#define ARC_REG_DC_IVDL 0x4A +#define ARC_REG_DC_FLDL 0x4C +#define ARC_REG_DC_CTRL 0x48 +#define FS_FLAG 0x100 +#define _psp_get_aux(aux_reg) \ + (unsigned long)_lr((unsigned)(aux_reg)) + +#define _psp_set_aux(aux_reg,value) \ + _sr((unsigned)(value),(unsigned)(aux_reg)) + +extern void mmu_clean_dcache(rt_uint32_t buffer, rt_uint32_t size); +extern void mmu_invalidate_dcache(rt_uint32_t buffer, rt_uint32_t size); +void inv_dcache_range(unsigned long start, unsigned long len) +{ + mmu_invalidate_dcache(start, len); +} + +void flush_dcache_range(unsigned long start, unsigned long len) +{ + mmu_clean_dcache(start, len); +} + +int g_use_bcm43362 = 0; +static int sdio_drv_read_write(sdc_t* sdc, unsigned int rw, unsigned int addr, unsigned int fn, unsigned int bcnt, + unsigned int bsize, unsigned char *buf) +{ + volatile DmaDesc *pDmaDesc = sdc->pDmaDesc; + int ret; + unsigned int intsts = 0; + unsigned int cmd = 0x2275; + unsigned int base = sdc->ip_base; + unsigned int arg; + unsigned int num; + int loop_for_command_done_check = 10000;//DATA_TRANSFER_OVER_TIMEOUT_USEC; + rt_err_t err; + +//SDIO_PRINTF("------- %s, line %d buf = %08x size = %d -------\n", __FUNCTION__, __LINE__, buf, bsize); + arg = (fn << 28) | (addr << 9); + + if (g_use_bcm43362) { + arg |= (1 << 26); //OPcode = 1............, for AP6181. + } + + if (bcnt == 1 && bsize <= 512) + arg |= (bsize & 0x1ff); + else + arg |= ((1 << 27) | bcnt); + if ( rw ) { + cmd |= 0x400; + arg |= (1 << 31); + } + num = bsize*bcnt; + + if ( rw ) { + flush_dcache_range((unsigned long)buf, num); + } + else { + inv_dcache_range((unsigned long)buf, num); + } + + err = rt_sem_take(sdc->mutex, RT_WAITING_FOREVER); + if (err != RT_EOK) { + return ERRNORES; + } + +//synopmob_set_bits(base+FIFOTH, 0x2 << 28); + // reset + synopmob_set_bits(base+CTRL, FIFO_RESET); //reset FIFO + while (synopmob_read_register(base+CTRL) & FIFO_RESET); + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + + //pDmaDesc->desc0 = 0; + pDmaDesc->desc0 |= DescOwnByDma | DescFirstDesc | DescLastDesc; + pDmaDesc->desc1 = ((num << DescBuf1SizeShift) & DescBuf1SizMsk); + pDmaDesc->desc2 = (unsigned int)buf; + //pDmaDesc->desc3 = 0; + flush_dcache_range((unsigned int)pDmaDesc, sizeof(DmaDesc)); + synopmob_set_register(base+DBADDR, (unsigned int)pDmaDesc); + synopmob_set_register(base+BLKSIZ, bsize); + synopmob_set_register(base+BYTCNT, num); + synopmob_set_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_set_bits(base + BMOD,BMOD_DE); + +//SDIO_PRINTF("pDmaDesc = %08x, %08x / %08x / %08x / %08x\n", pDmaDesc, pDmaDesc->desc0, pDmaDesc->desc1, pDmaDesc->desc2, pDmaDesc->desc3); + ret = synopmob_execute_command(base, cmd, arg); + if ( !ret ) { + ret = ERRIDMA; + err = rt_sem_take(sdc->sem, DMA_TRANSFER_TIMEOUT_TICKS); + if ( !err ) { + while (--loop_for_command_done_check > 0) { + intsts = synopmob_read_register(base+RINTSTS); + if ((intsts & (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) == (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) { + break; + } + plat_loop(1); + } + ret = synopmob_handle_standard_rinsts(intsts); + if (!ret ) { + if( !loop_for_command_done_check || !(sdc->idsts & 0x100)) { //normal interrupt +SDIO_PRINTF("------- %s, line %d idsts = %08x check = %d -------\n", __FUNCTION__, __LINE__, sdc->idsts, loop_for_command_done_check); + ret = ERRIDMA; + } + } + else + SDIO_PRINTF("------- %s, line %d intsts = %08x buf = %08x -------\n", __FUNCTION__, __LINE__, intsts, buf); + } + } + + if (!ret) { + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + synopmob_clear_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_clear_bits(base + BMOD,BMOD_DE); + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, 512); + } + else { + char* op = "read"; + if (rw) + op = "write"; + + SDIO_PRINTF("sdio_drv_read_write1(%s) fail:, ret = %d\n", op, ret); + } + + if ( rw && !ret ) { //write + ret = synopmob_wait_data_ready(base); + } + + rt_sem_release(sdc->mutex); + + if ( !rw && !ret ) { //read + inv_dcache_range((unsigned long)buf, num); + } + + if (ret) { + char* op = "read"; + if (rw) + op = "write"; + + SDIO_PRINTF("sdio_drv_read_write2(%s) fail:, ret = %d\n", op, ret); + } + return ret; + //return ret ? 0/*false*/ : 1/*true*/; +} + +int sdio_drv_read(HSDC handle, unsigned int addr, unsigned int fn, unsigned int bcnt, + unsigned int bsize, unsigned char *buf) +{ + return sdio_drv_read_write((sdc_t*)handle, 0, addr, fn, bcnt, bsize, buf); +} + +int sdio_drv_write(HSDC handle, unsigned int addr, unsigned int fn, unsigned int bcnt, + unsigned int bsize, unsigned char *buf) +{ + return sdio_drv_read_write((sdc_t*)handle, 1, addr, fn, bcnt, bsize, buf); +} + +static void dumpchain(DmaDesc *pChain) +{ + int i = 0; + DmaDesc *tmp_pChain = pChain; + + while(tmp_pChain && i < 10) + { + SDIO_PRINTF("[%d]: chain =%p, buf = %p, size = %d, csi = %08x, next = %p\n", i, tmp_pChain, (DmaDesc *)tmp_pChain->desc2, tmp_pChain->desc1, tmp_pChain->desc0, (DmaDesc *)tmp_pChain->desc3); + tmp_pChain = (DmaDesc *)tmp_pChain->desc3; + i++; + + if(tmp_pChain == pChain) + break; + } +} + +#ifdef DONT_COPY_NET_PAYLOAD_TO_SEND +#if 1 +int sdio_drv_chain_write(sdc_t* sdc, unsigned int addr, unsigned int fn, unsigned int bcnt, unsigned int bsize, buf_chain_t *chain) +{ + int ret; + unsigned int intsts = 0; + unsigned int cmd = 0x2275; + unsigned int base = sdc->ip_base; + unsigned int arg; + unsigned int num; + unsigned int chain_len = 0; + int loop_for_command_done_check = DATA_TRANSFER_OVER_TIMEOUT_USEC; + rt_err_t err; + unsigned int rw = 1; + DmaDesc *tmpDesc = (DmaDesc *)chain; + DmaDesc *lastDesc = (void*)0; + + arg = (fn << 28) | (addr << 9); + if (bcnt == 1 && bsize <= 512) + arg |= (bsize & 0x1ff); + else + arg |= ((1 << 27) | bcnt); + if ( rw ) { + cmd |= 0x400; + arg |= (1 << 31); + } + num = bsize*bcnt; + + err = rt_sem_take(sdc->mutex, RT_WAITING_FOREVER); + if (err != RT_EOK) { + return ERRNORES; + } + + // reset + synopmob_set_bits(base+CTRL, FIFO_RESET); //reset FIFO + while (synopmob_read_register(base+CTRL) & FIFO_RESET); + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + + while(tmpDesc != 0) { + // make sure size is little than DescBuf1SizMsk + if(tmpDesc->desc1 > (DescBuf1SizMsk >> DescBuf1SizeShift)) { + // TBD... fix me + rt_sem_release(sdc->mutex); + return 0; + } + // TBD... fix me, we must align tmpDesc->desc2 to 4 ? + + tmpDesc->desc0 = DescOwnByDma | DescSecAddrChained; + + // is it last node? + if(tmpDesc->desc3 == 0 || tmpDesc->desc3 == (unsigned int)chain) { + tmpDesc->desc0 |= DescLastDesc; + lastDesc = tmpDesc; + } + else { + tmpDesc->desc0 |= DescDisInt; //disable interrupt... + } + + // is it first node? + if((char *)tmpDesc == (char *)chain) { + tmpDesc->desc0 |= DescFirstDesc; + } + + flush_dcache_range(tmpDesc->desc2, tmpDesc->desc1); + + tmpDesc = (DmaDesc *)tmpDesc->desc3; + chain_len += sizeof(buf_chain_t); + + if((char *)tmpDesc == (char *)chain) { + break; + } + } + + lastDesc->desc3 = (unsigned int)chain; + + //FIXME, chain must be continuous arrry. + flush_dcache_range((unsigned long)chain, chain_len); + + synopmob_set_register(base+DBADDR, (unsigned int)(chain)); + + synopmob_set_register(base+BLKSIZ, bsize); + synopmob_set_register(base+BYTCNT, num); + synopmob_set_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_set_bits(base + BMOD,BMOD_DE); + + ret = synopmob_execute_command(base, cmd, arg); + if ( !ret ) { + ret = ERRIDMA; + err = rt_sem_take(sdc->sem, DMA_TRANSFER_TIMEOUT_TICKS); + if ( !err ) { + while (--loop_for_command_done_check > 0) { + intsts = synopmob_read_register(base+RINTSTS); + if ((intsts & (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) == (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) { + break; + } + plat_loop(1); + } + ret = synopmob_handle_standard_rinsts(intsts); + if (!ret ) { + if( !loop_for_command_done_check || !(sdc->idsts & 0x100)) { //normal interrupt + ret = ERRIDMA; + } + } + } + } + + if (!ret) { + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + synopmob_clear_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_clear_bits(base + BMOD,BMOD_DE); + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, 512); + } + else { + char* op = "read"; + if (rw) + op = "write"; + + SDIO_PRINTF("sdio_drv_chain_write1(%s) fail:, ret = %d, bsize = %d * %d\n", op, ret, bsize, bcnt); + dumpchain((DmaDesc *)chain); + } + + if ( rw && !ret ) { + ret = synopmob_wait_data_ready(base); + } + + rt_sem_release(sdc->mutex); + + if (ret) { + ret++; + ret--; + SDIO_PRINTF("sdio_drv_chain_write2, fail:, ret = %d\n", ret); + } + + return ret; + //return ret ? 0/*false*/ : 1/*true*/; +} +#elif 0 +int sdio_drv_chain_write(sdc_t* sdc, unsigned int addr, unsigned int fn, unsigned int bcnt, unsigned int bsize, buf_chain_t *chain) +{ + //static volatile DmaDesc __attribute__ ((aligned(32))) st_pchain[4]; + volatile DmaDesc *st_pchain = (DmaDesc *)0x9a700000; + + int ret; + unsigned int intsts = 0; + unsigned int cmd = 0x2275; + unsigned int base = sdc->ip_base; + unsigned int arg; + unsigned int num; + unsigned int chain_len = 0; + int loop_for_command_done_check = DATA_TRANSFER_OVER_TIMEOUT_USEC; + char err; + unsigned int rw = 1; + + buf_chain_t *usrchain; + unsigned int desc0; + unsigned int length = 0; + + if (!chain) { + return 0; + } + + arg = (fn << 28) | (addr << 9); + if (bcnt == 1 && bsize <= 512) + arg |= (bsize & 0x1ff); + else + arg |= ((1 << 27) | bcnt); + if ( rw ) { + cmd |= 0x400; + arg |= (1 << 31); + } + num = bsize*bcnt; + + OSSemPend(sdc->mutex, 0, &err); + if (err != OS_NO_ERR) { + return ERRNORES; + } + + // reset + synopmob_set_bits(base+CTRL, FIFO_RESET); //reset FIFO + while (synopmob_read_register(base+CTRL) & FIFO_RESET); + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + + usrchain = chain; + while (1) { + if(usrchain->size > (DescBuf1SizMsk >> DescBuf1SizeShift)) { + // TBD... fix me + OSSemPost (sdc->mutex); + return 0; + } + length += usrchain->size; + desc0 = DescOwnByDma | DescSecAddrChained; + if (!usrchain->next || usrchain->next == chain) { + desc0 |= DescLastDesc; + } + else { + desc0 |= DescDisInt; //disable interrupt... + } + + if(usrchain == chain) { + desc0 |= DescFirstDesc; + } + + st_pchain[chain_len].desc0 = desc0; + st_pchain[chain_len].desc1 = (unsigned int)usrchain->size; + st_pchain[chain_len].desc2 = (unsigned int)usrchain->buf; + st_pchain[chain_len].desc3 = (unsigned int)(&st_pchain[chain_len+1]); + flush_dcache_range((unsigned int)usrchain->buf, usrchain->size); + + usrchain = usrchain->next; + if( !usrchain || usrchain == chain) { + break; + } + if (++chain_len >= 4) { + while(1) SDIO_PRINTF("sdio_drv_chain_write:long chain!\n"); + } + } + st_pchain[chain_len].desc3 = (unsigned int)(&st_pchain[0]); + + if (length != num) { + while (1) SDIO_PRINTF("sdio_drv_chain_write:too long packet!\n"); + } + + synopmob_set_register(base+DBADDR, (unsigned int)(st_pchain)); + + synopmob_set_register(base+BLKSIZ, bsize); + synopmob_set_register(base+BYTCNT, num); + synopmob_set_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_set_bits(base + BMOD,BMOD_DE); + + ret = synopmob_execute_command(base, cmd, arg); + if ( !ret ) { + ret = ERRIDMA; + OSSemPend(sdc->sem, DMA_TRANSFER_TIMEOUT_TICKS, &err); + if ( !err ) { + while (--loop_for_command_done_check > 0) { + intsts = synopmob_read_register(base+RINTSTS); + if ((intsts & (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) == (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) { + break; + } + plat_loop(1); + } + ret = synopmob_handle_standard_rinsts(intsts); + if (!ret ) { + if( !loop_for_command_done_check || !(sdc->idsts & 0x100)) { //normal interrupt + ret = ERRIDMA; + } + } + } + } + + if (!ret) { + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + synopmob_clear_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_clear_bits(base + BMOD,BMOD_DE); + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, 512); + } + else { + char* op = "read"; + if (rw) + op = "write"; + + SDIO_PRINTF("sdio_drv_chain_write1(%s) fail:, ret = %d\n", op, ret); + } + + if ( rw && !ret ) { + ret = synopmob_wait_data_ready(base); + } + + OSSemPost (sdc->mutex); + + return ret; + //return ret ? 0/*false*/ : 1/*true*/; +} +#else +static unsigned char __attribute__ ((aligned(32))) st_net_buf[2*1024]; +int sdio_drv_chain_write(sdc_t* sdc, unsigned int addr, unsigned int fn, unsigned int bcnt, unsigned int bsize, buf_chain_t *chain) +{ + static volatile DmaDesc __attribute__ ((aligned(32))) st_pchain; + + int ret; + unsigned int intsts = 0; + unsigned int cmd = 0x2275; + unsigned int base = sdc->ip_base; + unsigned int arg; + unsigned int num; + int loop_for_command_done_check = DATA_TRANSFER_OVER_TIMEOUT_USEC; + rt_err_t err; + unsigned int rw = 1; + + buf_chain_t *usrchain; + unsigned int length = 0; + + if (!chain) { + return 0; + } + + arg = (fn << 28) | (addr << 9); + if (bcnt == 1 && bsize <= 512) + arg |= (bsize & 0x1ff); + else + arg |= ((1 << 27) | bcnt); + if ( rw ) { + cmd |= 0x400; + arg |= (1 << 31); + } + num = bsize*bcnt; + + err = rt_sem_take(sdc->mutex, RT_WAITING_FOREVER); + if (err != RT_EOK) { + return ERRNORES; + } + + + // reset + synopmob_set_bits(base+CTRL, FIFO_RESET); //reset FIFO + while (synopmob_read_register(base+CTRL) & FIFO_RESET); + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + + usrchain = chain; + while (1) { + if (length + usrchain->size >= sizeof(st_net_buf)) { + while(1) SDIO_PRINTF("too long net pkt\n"); + } + + memcpy(st_net_buf + length, usrchain->buf, usrchain->size); + length += usrchain->size; + usrchain = usrchain->next; + if (!usrchain || usrchain->next == chain) { + break; + } + } + + st_pchain.desc0 = DescOwnByDma | DescSecAddrChained | DescLastDesc | DescFirstDesc; + st_pchain.desc1 = length; + st_pchain.desc2 = (unsigned int)st_net_buf; + st_pchain.desc3 = (unsigned int)&st_pchain; + flush_dcache_range((unsigned long)st_net_buf, length); + + if (length != num) { + while (1) SDIO_PRINTF("sdio_drv_chain_write:too long packet!\n"); + } + + synopmob_set_register(base+DBADDR, (unsigned int)(&st_pchain)); + + synopmob_set_register(base+BLKSIZ, bsize); + synopmob_set_register(base+BYTCNT, num); + synopmob_set_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_set_bits(base + BMOD,BMOD_DE); + + ret = synopmob_execute_command(base, cmd, arg); + if ( !ret ) { + ret = ERRIDMA; + err = rt_sem_take(sdc->sem, DMA_TRANSFER_TIMEOUT_TICKS); + if ( !err ) { + while (--loop_for_command_done_check > 0) { + intsts = synopmob_read_register(base+RINTSTS); + if ((intsts & (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) == (INTMSK_CMD_DONE|INTMSK_DAT_OVER)) { + break; + } + plat_loop(1); + } + ret = synopmob_handle_standard_rinsts(intsts); + if (!ret ) { + if( !loop_for_command_done_check || !(sdc->idsts & 0x100)) { //normal interrupt + ret = ERRIDMA; + } + } + } + } + + if (!ret) { + synopmob_set_register(base + RINTSTS, 0xfffe); //clear interrupts + synopmob_clear_bits(base + CTRL, CTRL_USE_IDMAC); + synopmob_clear_bits(base + BMOD,BMOD_DE); + synopmob_set_register(base+BLKSIZ, 512); + synopmob_set_register(base+BYTCNT, 512); + } + else { + char* op = "read"; + if (rw) + op = "write"; + + SDIO_PRINTF("sdio_drv_chain_write1(%s) fail:, ret = %d\n", op, ret); + } + + if ( rw && !ret ) { + ret = synopmob_wait_data_ready(base); + } + + rt_sem_release(sdc->mutex); + + return ret; + //return ret ? 0/*false*/ : 1/*true*/; +} +#endif +#endif + +static int sdio_card_reset(sdc_t* sdc) +{ + unsigned int resp; + int ret; + + /* Soft Reset card */ + sdio_drv_creg_write(sdc, 0x6, 0, 0x8, &resp); + + return 0; +} + +static int enum_sdio_card(sdc_t* sdc) +{ + int ret; + unsigned int resp; + unsigned int base = sdc->ip_base; + + #if 0 + synopmob_set_bits(0x98500004, (1<<24)); //set to output mode + synopmob_set_bits(0x98500000, (1<<24)); //power off + plat_loop(1000000/5); //Lets give some ramp down period + synopmob_clear_bits(0x98500000, (1<<24)); //power on + plat_loop(1000000/5);//Lets give some ramp down period + #endif + + synopmob_set_register(base+CTYPE, ONE_BIT_MODE); + + synopmob_set_register(base+CLKENA, 0x00000001); /*enable clock, non-low-power mode*/ + ret = synopmob_set_clk_freq(sdc, MMC_FOD_DIVIDER_VALUE); + + if ( !ret ) { + plat_loop(100); //enough for 74 clock. + #if 0 + sdio_card_reset(sdc); + plat_loop(100000); + #endif + ret = sd_send_cmd5(sdc, 0, &resp); + if (!ret) { + resp &= 0x00ffffff; + ret = sd_send_cmd5(sdc, resp, &resp); + } + } + + if (!ret) { + ret = sd_send_cmd3(sdc); //get RCA + } + + if (!ret) { + ret = sd_send_cmd7(sdc); //select the card + } + + + if (!g_use_bcm43362) + { + sdio_drv_creg_read(sdc, 0x13, 0, &resp); + if ((resp & 1) && (sdc->wkmod & (SDC_WKMOD_4WIRE|SDC_WKMOD_25M_STAND_SPEED|SDC_WKMOD_50M_HI_SPEED))){ //high speed support? + if (sdc->wkmod & SDC_WKMOD_4WIRE) { + sdio_drv_creg_read(sdc, 0x7, 0, &resp); + resp &= 0xfc; + resp |= (1 << 1); + sdio_drv_creg_write(sdc, 0x7, 0, resp, &resp); //switch to 4bit mode + sdio_drv_creg_read(sdc, 0x7, 0, &resp); + if ((resp & 0x3) != 0x2) { + return ERRCARDINTERNAL; // 4bit mode failed + } + synopmob_set_register(base+CTYPE, FOUR_BIT_MODE); + } + if (sdc->wkmod & (SDC_WKMOD_25M_STAND_SPEED|SDC_WKMOD_50M_HI_SPEED)) { + ret = synopmob_set_clk_freq(sdc, ONE_BIT_BUS_FREQ); + //ret = synopmob_set_clk_freq(sdc, 0); + } + } + + sdio_drv_creg_read(sdc, 0x3, 0, &resp); + if (!ret) { + sdio_drv_creg_read(sdc, 0x0, 0, &resp); //card version + sdio_drv_creg_write(sdc, 0x4, 0, 0x3, &resp); //enable interrupts in card + sdio_drv_creg_write(sdc, 0x2, 0, 0x2, &resp); //Eable IO in card + do { + sdio_drv_creg_read(sdc, 0x3, 0, &resp); + } while (!(resp & 2)); + } + } //g_use_bcm43362 + + sdc->card_type = SDIO_TYPE; + + synopmob_set_bits(base+CTRL, INT_ENABLE); + + return ret; +} + +int sdio_high_speed_mode(HSDC handle, int bitwidth, int freq) +{ + int ret; + sdc_t* sdc = (sdc_t*)handle; + + if (bitwidth == 4) + { + synopmob_set_register(sdc->ip_base+CTYPE, FOUR_BIT_MODE); + } + + ret = synopmob_set_clk_freq(sdc, /*ONE_BIT_BUS_FREQ*/1); + if (ret != 0) + { + SDIO_PRINTF("sdio_high_speed_mode fail:, ret = %d\n", ret); + } + + return ret; +} + +static int common_init(unsigned int which, unsigned int sdio, unsigned int wkmod, unsigned int* dma_desc, HSDC* phandle) +{ + int ret = ERRNORES; + sdc_t* sdc; + unsigned int base; + unsigned int temp; + unsigned int fifo_thresh; + volatile DmaDesc *pDmaDesc; + rt_sem_t sem; + rt_sem_t mutex; + + base = SDC0_REG_BASE; + temp = PMU_SDC0_RST_BIT; + if (which > 0) { + base = SDC1_REG_BASE; + temp = PMU_SDC1_RST_BIT; + } + +#if 0 + //PMU_RST_MODULE(temp); plat_loop(1); + temp = synopmob_read_register(PMU_REG_CLK_DIV3); + temp &= (~(0x0f<<8)); + temp |= (0xf<<8); + synopmob_set_register(PMU_REG_CLK_DIV3, temp); +#endif + *phandle = (HSDC)0; + + sdc = &sdc_array[which]; + sem = sdc->sem; + mutex = sdc->mutex; + memset((void *)sdc, 0, sizeof(*sdc)); + sdc->wkmod = wkmod; + sdc->idma_support = 0; + sdc->ip_base = base; + sdc->rca = 0; + sdc->card_type = NONE_TYPE; + if (!sem) { + sem = rt_sem_create("fh_sdio_sem", 0, RT_IPC_FLAG_PRIO);//OSSemCreate (0); + if ( !sem ) { + return ret; + } + } + sdc->sem = sem; + + if (!mutex) { + mutex = rt_sem_create("fh_sdio_mutex", 1, RT_IPC_FLAG_PRIO);//OSSemCreate (1); + if ( !mutex ) { + return ret; + } + } + sdc->mutex = mutex; + + synopmob_set_bits(base + CTRL, CTRL_RESET); //reset host controller + plat_loop(100); + + synopmob_clear_bits(base + CTRL,CTRL_USE_IDMAC); + sdc->idma_support = 1; //fixed to support IDMA + + pDmaDesc = (volatile DmaDesc *)dma_desc; + sdc->pDmaDesc = pDmaDesc; + if (sdc->idma_support) { + synopmob_set_bits(base + CTRL, DMA_RESET); + plat_loop(100); + synopmob_set_bits(base + CTRL, FIFO_RESET); + plat_loop(100); + synopmob_set_bits(base + BMOD, BMOD_SWR); + plat_loop(100); + + //synopmob_set_bits(base + BMOD,BMOD_DE); + pDmaDesc->desc0 = DescSecAddrChained; + pDmaDesc->desc1 = 0; + pDmaDesc->desc2 = 0; + pDmaDesc->desc3 = (unsigned int)(pDmaDesc); + synopmob_set_register(base + DBADDR, (unsigned int)(pDmaDesc)); + } + + synopmob_set_register(base+CTYPE, ONE_BIT_MODE); + + synopmob_set_register(base+RINTSTS, 0xffffffff);//clear interrupt. + synopmob_clear_bits(base+CTRL, INT_ENABLE); + synopmob_set_register(base+INTMSK, 0); // mask all INTR + synopmob_set_register(base+IDINTEN, IDMAINTBITS); //Enable DMA INTR + + + synopmob_set_register(base+TMOUT, 0xffffffff); /* Set Data and Response timeout to Maximum Value*/ + + /* Set the card Debounce to allow the CDETECT fluctuations to settle down*/ + synopmob_set_register(base+DEBNCE, 0x0FFFFF); + + fifo_thresh = synopmob_read_register(base+FIFOTH); + //fifo_thresh = GET_FIFO_DEPTH(fifo_thresh) / 2; + fifo_thresh = (GET_FIFO_DEPTH(fifo_thresh) + 1) / 2; + sdc->fifo_depth = fifo_thresh * 2; + sdc->fifo_threth = fifo_thresh; + /* Tx Watermark */ + synopmob_clear_bits(base+FIFOTH, 0xfff); + synopmob_set_bits(base+FIFOTH, fifo_thresh); + /* Rx Watermark */ + synopmob_clear_bits(base+FIFOTH, 0x0fff0000); + synopmob_set_bits(base+FIFOTH, (fifo_thresh-1) << 16); + //synopmob_set_bits(base+FIFOTH, 2<< 28); + + if (!sdio) { + ret = enum_sd_card(sdc); + } + else { + ret = enum_sdio_card(sdc); + } + + if (!ret) { + *phandle = (HSDC)sdc; + } + + return ret; +} + +int sdc_is_connected(unsigned int which) +{ + unsigned int base = SDC0_REG_BASE; + + if (which > 0) + base = SDC1_REG_BASE; + + return !(synopmob_read_register(base+CDETECT) & 1); +} + +int sdc_init(unsigned int which, unsigned int wkmod, unsigned int* dma_desc, HSDC* phandle) +{ + return common_init(which, 0, wkmod, dma_desc, phandle); +} + +int sdio_init(unsigned int which, unsigned int wkmod, unsigned int* dma_desc, HSDC* phandle) +{ + return common_init(which, 1, wkmod, dma_desc, phandle); +} + +int sdio_enable_card_int(HSDC handle, int enable) +{ + unsigned int base = ((sdc_t*)handle)->ip_base; + + if (enable) { + //synopmob_set_register(base+INTMSK, INTMSK_SDIO); + synopmob_set_register(base+INTMSK, (synopmob_read_register(base+INTMSK) | INTMSK_SDIO )); + } + else { + //synopmob_set_register(base+INTMSK, 0); + synopmob_set_register(base+INTMSK, (synopmob_read_register(base+INTMSK) & ~INTMSK_SDIO )); + } + + return 0; +} + +int sdio_set_card_int_cb(HSDC handle, void (*cb)(void)) +{ + ((sdc_t*)handle)->cb = cb; + + return 0; +} + +static void OSSDCISR(sdc_t* sdc) +{ + unsigned int sts; + unsigned int base; + + base = sdc->ip_base; + sts = synopmob_read_register(base+IDSTS); + if ( sts ) { + synopmob_set_register(base+IDSTS, sts); + sdc->idsts = sts; + rt_sem_release(sdc->sem); + } + + //sts = synopmob_read_register(base+RINTSTS); + sts = synopmob_read_register(base+MINTSTS); + sts &= INTMSK_SDIO; + if ( sts ) { //interrupt from WIFI card. + //synopmob_set_register(base+INTMSK, 0); //mask all the interrupt + synopmob_set_register(base+INTMSK, synopmob_read_register(base+INTMSK) & ~INTMSK_SDIO ); //mask sdio interrupt + synopmob_set_register(base+RINTSTS, sts); + synopmob_set_register(base+MINTSTS, sts); + if (sdc->cb) { + sdc->cb(); + } + } +} + +void OSSDCINTR_0(int vector, void *param) +{ + OSSDCISR(&sdc_array[0]); +} + +void OSSDCINTR_1(int vector, void *param) +{ + OSSDCISR(&sdc_array[1]); +} + +void fh_sdio0_init(void) +{ + int sd0_irq = SDC0_IRQn; + + rt_hw_interrupt_install(sd0_irq, OSSDCINTR_0, NULL, NULL); + rt_hw_interrupt_umask(sd0_irq); +} + +void fh_sdio1_init(void) +{ + int sd1_irq = SDC1_IRQn; + + rt_hw_interrupt_install(sd1_irq, OSSDCINTR_1, NULL, NULL); + rt_hw_interrupt_umask(sd1_irq); +} + +void fh_sdio_init(void) +{ + fh_sdio0_init(); + fh_sdio1_init(); +} + +int sdc_deinit(HSDC handle) +{ + return -1; // TBD... fix me +} + +int sdc_set_clk_divider(unsigned int divider) +{ + if(divider > 255) + return -1; + + sdc_clk_divider = divider; + return 0; +} + diff --git a/bsp/fh8620/libraries/driverlib/fh_spi.c b/bsp/fh8620/libraries/driverlib/fh_spi.c new file mode 100644 index 0000000000000000000000000000000000000000..14d80397325d41ccbc042f8f8facac947e9ea039 --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_spi.c @@ -0,0 +1,164 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "fh_def.h" +#include "fh_arch.h" +#include "inc/fh_driverlib.h" + +void SPI_EnableSlaveen(struct fh_spi_obj *spi_obj, rt_uint32_t port) +{ + rt_uint32_t reg; + + reg = GET_REG(spi_obj->base + OFFSET_SPI_SER); + reg |= (1 << port); + SET_REG(spi_obj->base + OFFSET_SPI_SER, reg); +} + +void SPI_DisableSlaveen(struct fh_spi_obj *spi_obj, rt_uint32_t port) +{ + rt_uint32_t reg; + + reg = GET_REG(spi_obj->base + OFFSET_SPI_SER); + reg &= ~(1 << port); + SET_REG(spi_obj->base + OFFSET_SPI_SER, reg); +} + +void SPI_SetTxLevel(struct fh_spi_obj *spi_obj, rt_uint32_t level) +{ + SET_REG(spi_obj->base + OFFSET_SPI_TXFTLR, level); +} + +void SPI_EnableInterrupt(struct fh_spi_obj *spi_obj, rt_uint32_t flag) +{ + rt_uint32_t reg; + + reg = GET_REG(spi_obj->base + OFFSET_SPI_IMR); + reg |= flag; + SET_REG(spi_obj->base + OFFSET_SPI_IMR, reg); +} + + +void SPI_EnableDma(struct fh_spi_obj *spi_obj, rt_uint32_t channel) +{ + rt_uint32_t reg; + + reg = GET_REG(spi_obj->base + OFFSET_SPI_DMACTRL); + reg |= channel; + SET_REG(spi_obj->base + OFFSET_SPI_DMACTRL, reg); +} + + +void SPI_DisableDma(struct fh_spi_obj *spi_obj, rt_uint32_t channel) +{ + rt_uint32_t reg; + + reg = GET_REG(spi_obj->base + OFFSET_SPI_DMACTRL); + reg &= ~channel; + SET_REG(spi_obj->base + OFFSET_SPI_DMACTRL, reg); + +} + + +void SPI_DisableInterrupt(struct fh_spi_obj *spi_obj, rt_uint32_t flag) +{ + rt_uint32_t reg; + + reg = GET_REG(spi_obj->base + OFFSET_SPI_IMR); + reg &= ~flag; + SET_REG(spi_obj->base + OFFSET_SPI_IMR, reg); + +} + +rt_uint32_t SPI_InterruptStatus(struct fh_spi_obj *spi_obj) +{ + return GET_REG(spi_obj->base + OFFSET_SPI_ISR); +} + +void SPI_ClearInterrupt(struct fh_spi_obj *spi_obj) +{ + GET_REG(spi_obj->base + OFFSET_SPI_ICR); +} + +rt_uint32_t SPI_ReadTxFifoLevel(struct fh_spi_obj *spi_obj) +{ + return GET_REG(spi_obj->base + OFFSET_SPI_TXFLR); +} + +rt_uint32_t SPI_ReadRxFifoLevel(struct fh_spi_obj *spi_obj) +{ + return GET_REG(spi_obj->base + OFFSET_SPI_RXFLR); +} + +UINT8 SPI_ReadData(struct fh_spi_obj *spi_obj) +{ + return GET_REG(spi_obj->base + OFFSET_SPI_DR) & 0xff; +} + +void SPI_WriteData(struct fh_spi_obj *spi_obj, UINT8 data) +{ + SET_REG(spi_obj->base + OFFSET_SPI_DR, data); +} + +rt_uint32_t SPI_ReadStatus(struct fh_spi_obj *spi_obj) +{ + return GET_REG(spi_obj->base + OFFSET_SPI_SR); +} + +void SPI_Enable(struct fh_spi_obj *spi_obj, int enable) +{ + SET_REG(spi_obj->base + OFFSET_SPI_SSIENR, enable); +} + +void SPI_WriteTxDmaLevel(struct fh_spi_obj *spi_obj, rt_uint32_t data) +{ + SET_REG(spi_obj->base + OFFSET_SPI_DMATDL, data); +} + +void SPI_WriteRxDmaLevel(struct fh_spi_obj *spi_obj, rt_uint32_t data) +{ + SET_REG(spi_obj->base + OFFSET_SPI_DMARDL, data); +} + +void SPI_SetParameter(struct fh_spi_obj *spi_obj) +{ + rt_uint32_t reg; + struct spi_config *config; + + config = &spi_obj->config; + + SET_REG(spi_obj->base + OFFSET_SPI_BAUD, config->clk_div); + + reg = GET_REG(spi_obj->base + OFFSET_SPI_CTRL0); + + reg &= ~(0x3ff); + reg |= config->data_size \ + | config->frame_format \ + | config->clk_phase \ + | config->clk_polarity \ + | config->transfer_mode; + + SET_REG(spi_obj->base + OFFSET_SPI_CTRL0, reg); +} diff --git a/bsp/fh8620/libraries/driverlib/fh_timer.c b/bsp/fh8620/libraries/driverlib/fh_timer.c new file mode 100644 index 0000000000000000000000000000000000000000..f3034adeaa3a19cba9d14890ddd97ea0c681dcac --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_timer.c @@ -0,0 +1,144 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +/***************************************************************************** + * Include Section + * add all #include here + *****************************************************************************/ +#include "inc/fh_driverlib.h" +/***************************************************************************** + * Define section + * add all #define here + *****************************************************************************/ + +/**************************************************************************** + * ADT section + * add definition of user defined Data Type that only be used in this file here + ***************************************************************************/ + + + +/****************************************************************************** + * Function prototype section + * add prototypes for all functions called by this file,execepting those + * declared in header file + *****************************************************************************/ + +/***************************************************************************** + * Global variables section - Exported + * add declaration of global variables that will be exported here + * e.g. + * int8_t foo; + ****************************************************************************/ + + +/***************************************************************************** + * Global variables section - Local + * define global variables(will be refered only in this file) here, + * static keyword should be used to limit scope of local variable to this file + * e.g. + * static uint8_t ufoo; + *****************************************************************************/ + + + + + /* function body */ + + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ +int timer_init(timer *tim) +{ + tim->TIMER_CTRL_REG = 0; +} + +int timer_set_mode(timer *tim, enum timer_mode mode) +{ + switch (mode) + { + case TIMER_MODE_PERIODIC: + tim->TIMER_CTRL_REG |= TIMER_CTRL_MODE; + break; + case TIMER_MODE_ONESHOT: + tim->TIMER_CTRL_REG |= TIMER_CTRL_MODE; + break; + default: + rt_kprintf("Not support TIMER mode\n"); + return -1; + break; + } + + return 0; +} + +void timer_set_period(timer *tim, UINT32 period, UINT32 clock) +{ + tim->TIMER_LOAD_COUNT = clock/period; +} + + + +void timer_enable(timer *tim) +{ + tim->TIMER_CTRL_REG |= TIMER_CTRL_ENABLE; +} + +void timer_disable(timer *tim) +{ + tim->TIMER_CTRL_REG &= ~TIMER_CTRL_ENABLE; +} + +void timer_enable_irq(timer *tim) +{ + tim->TIMER_CTRL_REG &= ~TIMER_CTRL_INTMASK; +} + +void timer_disable_irq(timer *tim) +{ + tim->TIMER_CTRL_REG |= TIMER_CTRL_INTMASK; +} + +UINT32 timer_get_status(timer *tim) +{ + return tim->TIMER_INT_STATUS; +} + +UINT32 timer_get_eoi(timer *tim) +{ + return tim->TIMER_EOI; +} + +UINT32 timer_get_value(timer *tim) +{ + return tim->TIMER_LOAD_COUNT - tim->TIMER_CURRENT_VALUE; +} diff --git a/bsp/fh8620/libraries/driverlib/fh_uart.c b/bsp/fh8620/libraries/driverlib/fh_uart.c new file mode 100644 index 0000000000000000000000000000000000000000..0d9ad1f35d0467721caf9b7d991e0130c50a8c41 --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_uart.c @@ -0,0 +1,278 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +/***************************************************************************** + * Include Section + * add all #include here + *****************************************************************************/ +#include "inc/fh_driverlib.h" +/***************************************************************************** + * Define section + * add all #define here + *****************************************************************************/ + +/**************************************************************************** + * ADT section + * add definition of user defined Data Type that only be used in this file here + ***************************************************************************/ + +/****************************************************************************** + * Function prototype section + * add prototypes for all functions called by this file,execepting those + * declared in header file + *****************************************************************************/ + +/***************************************************************************** + * Global variables section - Exported + * add declaration of global variables that will be exported here + * e.g. + * int8_t foo; + ****************************************************************************/ + + +/***************************************************************************** + * Global variables section - Local + * define global variables(will be refered only in this file) here, + * static keyword should be used to limit scope of local variable to this file + * e.g. + * static uint8_t ufoo; + *****************************************************************************/ + + + + + /* function body */ + + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + + +int uart_init(uart *port) +{ + port->UART_IER = 0; + port->UART_LCR = 0; + //port->UART_DLL = 0; + //port->UART_DLH = 0; +} + +UINT32 uart_get_status(uart *port) +{ + return port->UART_USR; +} + + +void uart_configure(uart *port, enum data_bits data_bit, + enum stop_bits stop_bit, enum parity parity, + UINT32 buard_rate, UINT32 uart_clk) +{ + UINT32 divisor; + UINT32 freq; + UINT32 baud_div; + UINT32 lcr_reg = 0; + UINT32 ret; + + /*divisor = DIV(buard_rate); + port->UART_LCR |= UART_LCR_DLAB; + port->UART_DLL = divisor & 0xFF; + port->UART_DLH = (divisor >> 8) & 0xFF; + port->UART_LCR &= ~UART_LCR_DLAB;*/ + + do{ + //clear fifo... + port->UART_FCR = UART_FCR_RFIFOR | UART_FCR_XFIFOR; + //read status.. + ret = uart_get_status(port); + }while(ret & UART_USR_BUSY); + switch (data_bit) { + case UART_DATA_BIT5: + lcr_reg |= UART_LCR_DLS5; + break; + case UART_DATA_BIT6: + lcr_reg |= UART_LCR_DLS6; + break; + case UART_DATA_BIT7: + lcr_reg |= UART_LCR_DLS7; + break; + case UART_DATA_BIT8: + lcr_reg |= UART_LCR_DLS8; + break; + default: + lcr_reg |= UART_LCR_DLS8; + break; + } + + switch (stop_bit) { + case UART_STOP_BIT1: + lcr_reg |= UART_LCR_STOP1; + break; + case UART_STOP_BIT2: + lcr_reg |= UART_LCR_STOP2; + break; + default: + lcr_reg |= UART_LCR_STOP1; + break; + } + + switch (parity) { + case UART_PARITY_EVEN: + lcr_reg |= UART_LCR_EVEN | UART_LCR_PEN; + break; + case UART_PARITY_ODD: + lcr_reg |= UART_LCR_PEN; + break; + case UART_PARITY_ST: + lcr_reg |= UART_LCR_SP; + break; + case UART_PARITY_NONE: + default: + break; + } + + + + switch (buard_rate) { + case 115200: + baud_div = BAUDRATE_115200; + break; + case 57600: + baud_div = BAUDRATE_57600; + break; + case 38400: + baud_div = BAUDRATE_38400; + break; + case 19200: + baud_div = BAUDRATE_19200; + break; + case 9600: + baud_div = BAUDRATE_9600; + break; + default: + baud_div = BAUDRATE_115200; + break; + } + + //clear fifo + port->UART_FCR = UART_FCR_RFIFOR | UART_FCR_XFIFOR; + + //div + ret = port->UART_LCR; + ret |= UART_LCR_DLAB; + port->UART_LCR = ret; + port->RBRTHRDLL = baud_div & 0x00ff; + port->DLHIER = (baud_div & 0x00ff)>>8; + /* clear DLAB */ + ret = ret & 0x7f; + port->UART_LCR = ret; + + //line control + port->UART_LCR = lcr_reg; + //fifo control + port->UART_FCR = UART_FCR_FIFOE | UART_FCR_RFIFOR | UART_FCR_XFIFOR | UART_FCR_TET_1_4 | UART_FCR_RT_ONE; + +} + + +int uart_enable_irq(uart *port, UINT32 mode) +{ + unsigned int ret; + ret = port->UART_IER; + ret |= mode; + port->UART_IER = ret; +} + +int uart_disable_irq(uart *port, UINT32 mode) +{ + unsigned int ret; + ret = port->UART_IER; + ret &= ~mode; + + port->UART_IER = ret; +} + +UINT32 uart_get_iir_status(uart *port) +{ + return port->UART_IIR; +} + +UINT32 uart_get_line_status(uart *port) +{ + return port->UART_LSR; +} + +UINT32 uart_is_rx_ready(uart *port) +{ + return port->UART_LSR & UART_LSR_DR; +} + +UINT8 uart_getc(uart *port) +{ + return port->UART_RBR & 0xFF; +} + +void uart_putc(uart *port, UINT8 c) +{ + //while(!(port->UART_USR & UART_USR_TFNF)); + port->UART_THR = c; +} + +void uart_set_fifo_mode(uart *port, UINT32 fifo_mode) +{ + port->UART_FCR = fifo_mode; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bsp/fh8620/libraries/driverlib/fh_wdt.c b/bsp/fh8620/libraries/driverlib/fh_wdt.c new file mode 100644 index 0000000000000000000000000000000000000000..269f1015c8ebce346dadc48ee5cfeb6b38216af3 --- /dev/null +++ b/bsp/fh8620/libraries/driverlib/fh_wdt.c @@ -0,0 +1,58 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "inc/fh_driverlib.h" + +void WDT_Enable(struct fh_wdt_obj *wdt_obj, int enable) +{ + SET_REG(wdt_obj->base + WDOG_CONTROL_REG_OFFSET, enable); +} + +inline int WDT_IsEnable(struct fh_wdt_obj *wdt_obj) +{ + return GET_REG(wdt_obj->base + WDOG_CONTROL_REG_OFFSET) & + WDOG_CONTROL_REG_WDT_EN_MASK; +} + +void WDT_SetTopValue(struct fh_wdt_obj *wdt_obj, int top) +{ + SET_REG(wdt_obj->base + WDOG_TIMEOUT_RANGE_REG_OFFSET, top); +} + +void WDT_SetCtrl(struct fh_wdt_obj *wdt_obj, UINT32 reg) +{ + SET_REG(wdt_obj->base + WDOG_CONTROL_REG_OFFSET, reg); +} + +void WDT_Kick(struct fh_wdt_obj *wdt_obj) +{ + SET_REG(wdt_obj->base + WDOG_COUNTER_RESTART_REG_OFFSET, WDOG_COUNTER_RESTART_KICK_VALUE); +} + +UINT32 WDT_GetCurrCount(struct fh_wdt_obj *wdt_obj) +{ + return GET_REG(wdt_obj->base + WDOG_CURRENT_COUNT_REG_OFFSET); +} diff --git a/bsp/fh8620/libraries/inc/fh_driverlib.h b/bsp/fh8620/libraries/inc/fh_driverlib.h new file mode 100644 index 0000000000000000000000000000000000000000..9bb6b8c75ddeef693d6f077b01dc000ba7e6b607 --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_driverlib.h @@ -0,0 +1,39 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include "fh_def.h" +#include "fh_arch.h" +#include "fh_ictl.h" +#include "fh_timer.h" +#include "fh_uart.h" +#include "fh_spi.h" +#include "fh_gpio.h" +#include "fh_mmc.h" +#include "fh_i2c.h" +#include "fh_pwm.h" +#include "fh_wdt.h" diff --git a/bsp/fh8620/libraries/inc/fh_gpio.h b/bsp/fh8620/libraries/inc/fh_gpio.h new file mode 100644 index 0000000000000000000000000000000000000000..62d1823ebab5643f8eb7df15ddc9574677416af1 --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_gpio.h @@ -0,0 +1,54 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_GPIO_H_ +#define FH_GPIO_H_ + + +#define REG_GPIO_SWPORTA_DR (0x0000) +#define REG_GPIO_SWPORTA_DDR (0x0004) +#define REG_GPIO_PORTA_CTL (0x0008) +#define REG_GPIO_INTEN (0x0030) +#define REG_GPIO_INTMASK (0x0034) +#define REG_GPIO_INTTYPE_LEVEL (0x0038) +#define REG_GPIO_INT_POLARITY (0x003C) +#define REG_GPIO_INTSTATUS (0x0040) +#define REG_GPIO_RAWINTSTATUS (0x0044) +#define REG_GPIO_DEBOUNCE (0x0048) +#define REG_GPIO_PORTA_EOI (0x004C) +#define REG_GPIO_EXT_PORTA (0x0050) + +#define NUM_OF_GPIO (64) + +struct fh_gpio_obj +{ + unsigned int id; + unsigned int irq; +}; + + + +#endif /* FH_GPIO_H_ */ diff --git a/bsp/fh8620/libraries/inc/fh_i2c.h b/bsp/fh8620/libraries/inc/fh_i2c.h new file mode 100644 index 0000000000000000000000000000000000000000..f981da431f5156ff4a19a0cf28d1f03d9edf2c53 --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_i2c.h @@ -0,0 +1,226 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_I2C_H_ +#define FH_I2C_H_ + +#include "fh_def.h" + +#define OFFSET_I2C_CON (0x0000) +#define OFFSET_I2C_TAR (0x0004) +#define OFFSET_I2C_SAR (0x0008) +#define OFFSET_I2C_HS_MADDR (0x000C) +#define OFFSET_I2C_DATA_CMD (0x0010) +#define OFFSET_I2C_SS_SCL_HCNT (0x0014) +#define OFFSET_I2C_SS_SCL_LCNT (0x0018) +#define OFFSET_I2C_FS_SCL_HCNT (0x001C) +#define OFFSET_I2C_FS_SCL_LCNT (0x0020) +#define OFFSET_I2C_HS_SCL_HCNT (0x0024) +#define OFFSET_I2C_HS_SCL_LCNT (0x0028) +#define OFFSET_I2C_INTR_STAT (0x002c) +#define OFFSET_I2C_INTR_MASK (0x0030) +#define OFFSET_I2C_RAW_INTR_STAT (0x0034) +#define OFFSET_I2C_RX_TL (0x0038) +#define OFFSET_I2C_TX_TL (0x003c) +#define OFFSET_I2C_CLR_INTR (0x0040) +#define OFFSET_I2C_CLR_RX_UNDER (0x0044) +#define OFFSET_I2C_CLR_RX_OVER (0x0048) +#define OFFSET_I2C_CLR_TX_OVER (0x004c) +#define OFFSET_I2C_CLR_RD_REQ (0x0050) +#define OFFSET_I2C_CLR_TX_ABRT (0x0054) +#define OFFSET_I2C_CLR_RX_DONE (0x0058) +#define OFFSET_I2C_CLR_ACTIVITY (0x005c) +#define OFFSET_I2C_CLR_STOP_DET (0x0060) +#define OFFSET_I2C_CLR_START_DET (0x0064) +#define OFFSET_I2C_CLR_GEN_CALL (0x0068) +#define OFFSET_I2C_ENABLE (0x006c) +#define OFFSET_I2C_STATUS (0x0070) +#define OFFSET_I2C_TXFLR (0x0074) +#define OFFSET_I2C_RXFLR (0x0078) +#define OFFSET_I2C_DMA_CR (0x0088) +#define OFFSET_I2C_DMA_TDLR (0x008c) +#define OFFSET_I2C_DMA_RDLR (0x0090) +#define OFFSET_I2C_COMP_PARAM1 (0x00f4) +#define OFFSET_I2C_TX_ABRT_SOURCE (0x0080) + +#define I2C_M_TEN 0x0100 /* this is a ten bit chip address */ +#define I2C_M_RD 0x0001 /* read data, from slave to master */ +#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ +#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ + +#define DW_IC_CON_MASTER 0x1 +#define DW_IC_CON_SPEED_STD 0x2 +#define DW_IC_CON_SPEED_FAST 0x4 +#define DW_IC_CON_10BITADDR_MASTER 0x10 +#define DW_IC_CON_RESTART_EN 0x20 +#define DW_IC_CON_SLAVE_DISABLE 0x40 + +#define DW_IC_INTR_RX_UNDER 0x001 +#define DW_IC_INTR_RX_OVER 0x002 +#define DW_IC_INTR_RX_FULL 0x004 +#define DW_IC_INTR_TX_OVER 0x008 +#define DW_IC_INTR_TX_EMPTY 0x010 +#define DW_IC_INTR_RD_REQ 0x020 +#define DW_IC_INTR_TX_ABRT 0x040 +#define DW_IC_INTR_RX_DONE 0x080 +#define DW_IC_INTR_ACTIVITY 0x100 +#define DW_IC_INTR_STOP_DET 0x200 +#define DW_IC_INTR_START_DET 0x400 +#define DW_IC_INTR_GEN_CALL 0x800 + +#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \ + DW_IC_INTR_TX_EMPTY | \ + DW_IC_INTR_TX_ABRT | \ + DW_IC_INTR_STOP_DET) + +#define DW_IC_STATUS_ACTIVITY 0x1 +#define DW_IC_STATUS_MASTER_ACTIVITY 0x20 + +#define DW_IC_ERR_TX_ABRT 0x1 + +/* + * status codes + */ +#define STATUS_IDLE 0x0 +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 + +#define TIMEOUT 20 /* ms */ + +/* + * hardware abort codes from the DW_IC_TX_ABRT_SOURCE register + * + * only expected abort codes are listed here + * refer to the datasheet for the full list + */ +#define ABRT_7B_ADDR_NOACK 0 +#define ABRT_10ADDR1_NOACK 1 +#define ABRT_10ADDR2_NOACK 2 +#define ABRT_TXDATA_NOACK 3 +#define ABRT_GCALL_NOACK 4 +#define ABRT_GCALL_READ 5 +#define ABRT_SBYTE_ACKDET 7 +#define ABRT_SBYTE_NORSTRT 9 +#define ABRT_10B_RD_NORSTRT 10 +#define ABRT_MASTER_DIS 11 +#define ARB_LOST 12 + +#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK) +#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK) +#define DW_IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK) +#define DW_IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK) +#define DW_IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK) +#define DW_IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ) +#define DW_IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET) +#define DW_IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT) +#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT) +#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS) +#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST) + +#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \ + DW_IC_TX_ABRT_10ADDR1_NOACK | \ + DW_IC_TX_ABRT_10ADDR2_NOACK | \ + DW_IC_TX_ABRT_TXDATA_NOACK | \ + DW_IC_TX_ABRT_GCALL_NOACK) + +static char *abort_sources[] = { + [ABRT_7B_ADDR_NOACK] = + "slave address not acknowledged (7bit mode)", + [ABRT_10ADDR1_NOACK] = + "first address byte not acknowledged (10bit mode)", + [ABRT_10ADDR2_NOACK] = + "second address byte not acknowledged (10bit mode)", + [ABRT_TXDATA_NOACK] = + "data not acknowledged", + [ABRT_GCALL_NOACK] = + "no acknowledgement for a general call", + [ABRT_GCALL_READ] = + "read after general call", + [ABRT_SBYTE_ACKDET] = + "start byte acknowledged", + [ABRT_SBYTE_NORSTRT] = + "trying to send start byte when restart is disabled", + [ABRT_10B_RD_NORSTRT] = + "trying to read when restart is disabled (10bit mode)", + [ABRT_MASTER_DIS] = + "trying to use disabled adapter", + [ARB_LOST] = + "lost arbitration", +}; +/* i2c interrput definition */ +#define M_GEN_CALL (1<<11) +#define M_START_DET (1<<10) +#define M_STOP_DET (1<<9) +#define M_ACTIVITY (1<<8) +#define M_RX_DONE (1<<7) +#define M_TX_ABRT (1<<6) +#define M_RD_REQ (1<<5) +#define M_TX_EMPTY (1<<4) +#define M_TX_OVER (1<<3) +#define M_RX_FULL (1<<2) +#define M_RX_OVER (1<<1) +#define M_RX_UNDER (1<<0) +#define M_NONE (0) + + +struct i2c_config +{ + int speed_mode; + UINT32 tx_fifo_depth; + UINT32 rx_fifo_depth; +}; + +struct fh_i2c_obj +{ + UINT32 id; + UINT32 irq; + UINT32 base; + UINT32 input_clock; + UINT32 abort_source; + struct i2c_config config; + + +}; + +void I2C_Init(struct fh_i2c_obj *i2c_obj); +inline void I2C_Enable(struct fh_i2c_obj *i2c_obj, int enable); +inline void I2C_SetSlaveAddress(struct fh_i2c_obj *i2c_obj, rt_uint16_t addr); +inline UINT32 I2C_GetTransmitFifoLevel(struct fh_i2c_obj *i2c_obj); +inline UINT32 I2C_GetReceiveFifoLevel(struct fh_i2c_obj *i2c_obj); +inline UINT32 I2C_SetTransmitThreshold(struct fh_i2c_obj *i2c_obj, int txtl); +int I2C_HandleTxAbort(struct fh_i2c_obj *i2c_obj); +UINT32 I2C_ClearAndGetInterrupts(struct fh_i2c_obj *i2c_obj); +inline void I2C_SetInterruptMask(struct fh_i2c_obj *i2c_obj, UINT32 mask); +inline UINT32 I2C_GetInterruptMask(struct fh_i2c_obj *i2c_obj); +inline void I2C_SetDataCmd(struct fh_i2c_obj *i2c_obj, UINT32 reg); +inline UINT8 I2C_GetData(struct fh_i2c_obj *i2c_obj); +int I2C_WaitMasterIdle(struct fh_i2c_obj *i2c_obj); +int I2C_WaitDeviceIdle(struct fh_i2c_obj *i2c_obj); + +#endif /* FH_I2C_H_ */ diff --git a/bsp/fh8620/libraries/inc/fh_ictl.h b/bsp/fh8620/libraries/inc/fh_ictl.h new file mode 100644 index 0000000000000000000000000000000000000000..9796c99afc2af0439c1a45e7af2d0b3d6b217ce2 --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_ictl.h @@ -0,0 +1,58 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_ICTL_H_ +#define FH_ICTL_H_ + +#include "fh_def.h" + +typedef struct { + RwReg IRQ_EN_L; + RwReg IRQ_EN_H; + RwReg IRQ_MASK_L; + RwReg IRQ_MASK_H; + RwReg IRQ_FORCE_L; + RwReg IRQ_FORCE_H; + RwReg IRQ_RAWSTARUS_L; + RwReg IRQ_RAWSTARUS_H; + RwReg IRQ_STATUS_L; + RwReg IRQ_STATUS_H; + RwReg IRQ_MASKSTATUS_L; + RwReg IRQ_MASKSTATUS_H; + RwReg IRQ_FINALSTATUS_L; + RwReg IRQ_FINALSTATUS_H; + RwReg IRQ_VECTOR; +}fh_intc; + + + +void ictl_close_all_isr(fh_intc *p); + +void ictl_mask_isr(fh_intc *p,int irq); + +void ictl_unmask_isr(fh_intc *p,int irq); + +#endif diff --git a/bsp/fh8620/libraries/inc/fh_mmc.h b/bsp/fh8620/libraries/inc/fh_mmc.h new file mode 100644 index 0000000000000000000000000000000000000000..642ed43ac7ea1e2c9db0cfe472ff3b680abfee47 --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_mmc.h @@ -0,0 +1,225 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_MMC_H_ +#define FH_MMC_H_ + +#include "fh_def.h" + + +#define OFFSET_SDC_CTRL (0x0000) +#define OFFSET_SDC_PWREN (0x0004) +#define OFFSET_SDC_CLKDIV (0x0008) +#define OFFSET_SDC_CLKSRC (0x000C) +#define OFFSET_SDC_CLKENA (0x0010) +#define OFFSET_SDC_TMOUT (0x0014) +#define OFFSET_SDC_CTYPE (0x0018) +#define OFFSET_SDC_BLKSIZ (0x001C) +#define OFFSET_SDC_BYTCNT (0x0020) +#define OFFSET_SDC_INTMASK (0x0024) +#define OFFSET_SDC_CMDARG (0x0028) +#define OFFSET_SDC_CMD (0x002C) +#define OFFSET_SDC_RESP0 (0x0030) +#define OFFSET_SDC_RESP1 (0x0034) +#define OFFSET_SDC_RESP2 (0x0038) +#define OFFSET_SDC_RESP3 (0x003C) +#define OFFSET_SDC_MINTSTS (0x0040) +#define OFFSET_SDC_RINTSTS (0x0044) +#define OFFSET_SDC_STATUS (0x0048) +#define OFFSET_SDC_FIFOTH (0x004C) +#define OFFSET_SDC_CDETECT (0x0050) +#define OFFSET_SDC_WRTPRT (0x0054) +#define OFFSET_SDC_GPIO (0x0058) +#define OFFSET_SDC_TCBCNT (0x005C) +#define OFFSET_SDC_TBBCNT (0x0060) +#define OFFSET_SDC_DEBNCE (0x0064) +#define OFFSET_SDC_USRID (0x0068) +#define OFFSET_SDC_VERID (0x006C) +#define OFFSET_SDC_HCON (0x0070) +#define OFFSET_SDC_UHS_REG (0x0074) +#define OFFSET_SDC_RST_N (0x0078) +#define OFFSET_SDC_BMOD (0x0080) +#define OFFSET_SDC_PLDMND (0x0084) +#define OFFSET_SDC_DBADDR (0x0088) +#define OFFSET_SDC_IDSTS (0x008C) +#define OFFSET_SDC_IDINTEN (0x0090) +#define OFFSET_SDC_DSCADDR (0x0094) +#define OFFSET_SDC_BUFADDR (0x0098) +#define OFFSET_SDC_CARDTHRCTL (0x0100) +#define OFFSET_SDC_BACK_END_POWER (0x0104) +#define OFFSET_SDC_FIFO (0x0200) + +#define MMC_FIFO_DEPTH (16) + +#define MMC_CMD_FLAG_RESPONSE_EXPECTED BIT(6) +#define MMC_CMD_FLAG_LONG_RESPONSE BIT(7) +#define MMC_CMD_FLAG_CHECK_RESP_CRC BIT(8) +#define MMC_CMD_FLAG_DATA_EXPECTED BIT(9) +#define MMC_CMD_FLAG_WRITE_TO_CARD BIT(10) +#define MMC_CMD_FLAG_DATA_STREAM BIT(11) +#define MMC_CMD_FLAG_AUTO_STOP BIT(12) +#define MMC_CMD_FLAG_WAIT_PREV_DATA BIT(13) +#define MMC_CMD_FLAG_STOP_TRANSFER BIT(14) +#define MMC_CMD_FLAG_SEND_INIT BIT(15) +#define MMC_CMD_FLAG_SWITCH_VOLTAGE BIT(28) + +#define MMC_STATUS_DATA_BUSY BIT(9) +#define MMC_CTRL_CONTROLLER_RESET BIT(0) +#define MMC_CTRL_FIFO_RESET BIT(1) +#define MMC_CTRL_DMA_RESET BIT(2) +#define MMC_CTRL_INT_ENABLE BIT(4) +#define MMC_CTRL_USE_DMA BIT(25) +#define MMC_CMD_START_CMD BIT(31) +#define MMC_BMOD_RESET BIT(0) + +#define MMC_CLOCK_IN (50000000) + +#define MMC_CARD_WIDTH_1BIT (0) +#define MMC_CARD_WIDTH_4BIT (1) + +#define MMC_INT_STATUS_CARD_DETECT BIT(0) +#define MMC_INT_STATUS_RESPONSE_ERROR BIT(1) +#define MMC_INT_STATUS_CMD_DONE BIT(2) +#define MMC_INT_STATUS_TRANSFER_OVER BIT(3) +#define MMC_INT_STATUS_TX_REQUEST BIT(4) +#define MMC_INT_STATUS_RX_REQUEST BIT(5) +#define MMC_INT_STATUS_RESP_CRC_ERROR BIT(6) +#define MMC_INT_STATUS_DATA_CRC_ERROR BIT(7) +#define MMC_INT_STATUS_RESPONSE_TIMEOUT BIT(8) +#define MMC_INT_STATUS_READ_TIMEOUT BIT(9) +#define MMC_INT_STATUS_STARVATION_TIMEOUT BIT(10) +#define MMC_INT_STATUS_OVERRUN_UNDERRUN BIT(11) +#define MMC_INT_STATUS_HARDWARE_LOCKED BIT(12) +#define MMC_INT_STATUS_START_BIT_ERROR BIT(13) +#define MMC_INT_STATUS_AUTO_CMD_DONE BIT(14) +#define MMC_INT_STATUS_END_BIT_ERROR BIT(15) +#define MMC_INT_STATUS_SDIO BIT(16) +#define MMC_INT_STATUS_ALL (~0) + +#define MMC_INIT_STATUS_DATA_ERROR (MMC_INT_STATUS_DATA_CRC_ERROR \ + | MMC_INT_STATUS_START_BIT_ERROR | MMC_INT_STATUS_END_BIT_ERROR) + +#define MMC_USE_DMA + +#ifdef MMC_USE_DMA +#define MMC_INT_STATUS_DATA (MMC_INT_STATUS_TRANSFER_OVER | MMC_INIT_STATUS_DATA_ERROR) +#else +#define MMC_INT_STATUS_DATA (MMC_INT_STATUS_TRANSFER_OVER | MMC_INIT_STATUS_DATA_ERROR \ + | MMC_INT_STATUS_TX_REQUEST | MMC_INT_STATUS_RX_REQUEST) +#endif + +#define MMC_DMA_DESC_BUFF_SIZE (0x1f00) + + +typedef union +{ + struct + { + UINT32 reserved :1; //0~15 + UINT32 disable_interrupt_on_completion :1; //16~31 + UINT32 last_descriptor :1; //0~15 + UINT32 first_descriptor :1; //16~31 + UINT32 sencond_address_chained :1; //0~15 + UINT32 end_of_ring :1; //16~31 + UINT32 reserved_29_6 :24; //0~15 + UINT32 card_error_summary :1; //16~31 + UINT32 own :1; //16~31 + }bit; + UINT32 dw; +}MMC_DMA_Descriptor0; + +typedef union +{ + struct + { + UINT32 buffer1_size :13; //0~15 + UINT32 buffer2_size :13; //16~31 + UINT32 reserved_26_31 :6; //0~15 + }bit; + UINT32 dw; +}MMC_DMA_Descriptor1; + +typedef union +{ + struct + { + UINT32 buffer_addr0 :32; //0~15 + }bit; + UINT32 dw; +}MMC_DMA_Descriptor2; + +typedef union +{ + struct + { + UINT32 buffer_addr1 :32; //0~15 + }bit; + UINT32 dw; +}MMC_DMA_Descriptor3; + +typedef struct +{ + MMC_DMA_Descriptor0 desc0; /* control and status information of descriptor */ + MMC_DMA_Descriptor1 desc1; /* buffer sizes */ + MMC_DMA_Descriptor2 desc2; /* physical address of the buffer 1 */ + MMC_DMA_Descriptor3 desc3; /* physical address of the buffer 2 */ +}MMC_DMA_Descriptors; + +struct fh_mmc_obj +{ + rt_uint32_t id; + rt_uint32_t irq; + rt_uint32_t base; + rt_uint32_t power_pin_gpio; + MMC_DMA_Descriptors *descriptors; + void (*mmc_reset)(struct fh_mmc_obj *); +}; +inline void MMC_SetBlockSize(struct fh_mmc_obj *mmc_obj, rt_uint32_t size); +inline void MMC_SetByteCount(struct fh_mmc_obj *mmc_obj, rt_uint32_t bytes); +inline rt_uint32_t MMC_GetWaterlevel(struct fh_mmc_obj *mmc_obj); +inline rt_uint32_t MMC_GetResponse(struct fh_mmc_obj *mmc_obj, int resp_num); +inline rt_uint32_t MMC_GetRegCmd(struct fh_mmc_obj *mmc_obj); +inline rt_uint32_t MMC_GetRegCtrl(struct fh_mmc_obj *mmc_obj); +inline rt_uint32_t MMC_SetInterruptMask(struct fh_mmc_obj *mmc_obj, rt_uint32_t mask); +inline rt_uint32_t MMC_GetInterruptMask(struct fh_mmc_obj *mmc_obj); +inline rt_uint32_t MMC_ClearRawInterrupt(struct fh_mmc_obj *mmc_obj, rt_uint32_t interrupts); +inline rt_uint32_t MMC_GetRawInterrupt(struct fh_mmc_obj *mmc_obj); +inline rt_uint32_t MMC_GetStatus(struct fh_mmc_obj *mmc_obj); +inline rt_uint32_t MMC_GetCardStatus(struct fh_mmc_obj *mmc_obj); + +void MMC_Init(struct fh_mmc_obj *mmc_obj); +int MMC_ResetFifo(struct fh_mmc_obj *mmc_obj); +int MMC_SetCardWidth(struct fh_mmc_obj *mmc_obj, int width); +int MMC_UpdateClockRegister(struct fh_mmc_obj *mmc_obj, int div); +int MMC_SendCommand(struct fh_mmc_obj *mmc_obj, rt_uint32_t cmd, rt_uint32_t arg, rt_uint32_t flags); +int MMC_WriteData(struct fh_mmc_obj *mmc_obj, rt_uint32_t *buf, rt_uint32_t size); +int MMC_ReadData(struct fh_mmc_obj *mmc_obj, rt_uint32_t *buf, rt_uint32_t size); + +inline void MMC_StartDma(struct fh_mmc_obj *mmc_obj); +inline void MMC_StopDma(struct fh_mmc_obj *mmc_obj); +void MMC_InitDescriptors(struct fh_mmc_obj *mmc_obj, rt_uint32_t *buf, rt_uint32_t size); + +#endif /* FH_MMC_H_ */ diff --git a/bsp/fh8620/libraries/inc/fh_pwm.h b/bsp/fh8620/libraries/inc/fh_pwm.h new file mode 100644 index 0000000000000000000000000000000000000000..011da43905c8092c12fc2b5adcc3100042fe11fa --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_pwm.h @@ -0,0 +1,44 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_PWM_H_ +#define FH_PWM_H_ + +#define OFFSET_PWM_CTRL (0x00) +#define OFFSET_PWM_CMD(n) (((n) * 4) + OFFSET_PWM_CTRL + 4) + +struct fh_pwm_obj +{ + int id; + int irq; + unsigned int base; +}; + +void PWM_Enable(struct fh_pwm_obj *pwm_obj, int enable); +void PWM_SetPwmCmd(struct fh_pwm_obj *pwm_obj, int device_id, unsigned int reg); +unsigned int PWM_GetPwmCmd(struct fh_pwm_obj *pwm_obj, int device_id); + +#endif /* FH_PWM_H_ */ diff --git a/bsp/fh8620/libraries/inc/fh_sdio.h b/bsp/fh8620/libraries/inc/fh_sdio.h new file mode 100644 index 0000000000000000000000000000000000000000..b268dbd322010fdf793f0f224070cc93a1531768 --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_sdio.h @@ -0,0 +1,356 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_SDIO_H_ +#define FH_SDIO_H_ + +#include + +//#define __ASIC_BRANCH__ + +enum { + CTRL = 0x0, /** Control */ + PWREN = 0x4, /** Power-enable */ + CLKDIV = 0x8, /** Clock divider */ + CLKSRC = 0xC, /** Clock source */ + CLKENA = 0x10, /** Clock enable */ + TMOUT = 0x14, /** Timeout */ + CTYPE = 0x18, /** Card type */ + BLKSIZ = 0x1C, /** Block Size */ + BYTCNT = 0x20, /** Byte count */ + INTMSK = 0x24, /** Interrupt Mask */ + CMDARG = 0x28, /** Command Argument */ + CMD = 0x2C, /** Command */ + RESP0 = 0x30, /** Response 0 */ + RESP1 = 0x34, /** Response 1 */ + RESP2 = 0x38, /** Response 2 */ + RESP3 = 0x3C, /** Response 3 */ + MINTSTS = 0x40, /** Masked interrupt status */ + RINTSTS = 0x44, /** Raw interrupt status */ + STATUS = 0x48, /** Status */ + FIFOTH = 0x4C, /** FIFO threshold */ + CDETECT = 0x50, /** Card detect */ + WRTPRT = 0x54, /** Write protect */ + GPIO = 0x58, /** General Purpose IO */ + TCBCNT = 0x5C, /** Transferred CIU byte count */ + TBBCNT = 0x60, /** Transferred host/DMA to/from byte count */ + DEBNCE = 0x64, /** Card detect debounce */ + USRID = 0x68, /** User ID */ + VERID = 0x6C, /** Version ID */ + HCON = 0x70, /** Hardware Configuration */ + UHSREG = 0x74, /** Reserved */ + BMOD = 0x80, /** Bus mode Register */ + PLDMND = 0x84, /** Poll Demand */ + DBADDR = 0x88, /** Descriptor Base Address */ + IDSTS = 0x8C, /** Internal DMAC Status */ + IDINTEN = 0x90, /** Internal DMAC Interrupt Enable */ + DSCADDR = 0x94, /** Current Host Descriptor Address */ + BUFADDR = 0x98, /** Current Host Buffer Address */ + FIFODAT = 0x200, /** FIFO data read write */ +}; + +/* Control register definitions */ +#define CTRL_RESET 0x00000001 +#define FIFO_RESET 0x00000002 +#define DMA_RESET 0x00000004 +#define INT_ENABLE 0x00000010 +#define READ_WAIT 0x00000040 +#define CTRL_USE_IDMAC 0x02000000 + +/* Interrupt mask defines */ +#define INTMSK_CDETECT 0x00000001 +#define INTMSK_RESP_ERR 0x00000002 +#define INTMSK_CMD_DONE 0x00000004 +#define INTMSK_DAT_OVER 0x00000008 +#define INTMSK_TXDR 0x00000010 +#define INTMSK_RXDR 0x00000020 +#define INTMSK_RCRC 0x00000040 +#define INTMSK_DCRC 0x00000080 +#define INTMSK_RTO 0x00000100 +#define INTMSK_DTO 0x00000200 +#define INTMSK_HTO 0x00000400 +#define INTMSK_VSI INTMSK_HTO // VSI => Voltage Switch Interrupt +#define INTMSK_FRUN 0x00000800 +#define INTMSK_HLE 0x00001000 +#define INTMSK_SBE 0x00002000 +#define INTMSK_ACD 0x00004000 +#define INTMSK_EBE 0x00008000 +#define INTMSK_SDIO 0x00010000 +#define INTMASK_ERROR (INTMSK_RESP_ERR|INTMSK_RCRC|INTMSK_DCRC|INTMSK_RTO|INTMSK_DTO|INTMSK_HTO|INTMSK_FRUN|INTMSK_HLE|INTMSK_SBE|INTMSK_EBE) + +/*BMOD register define */ +#define BMOD_SWR 0x00000001 +#define BMOD_DE 0x00000080 + +/* for STATUS register */ +#define GET_FIFO_COUNT(x) (((x)&0x3ffe0000)>>17) +#define GET_FIFO_DEPTH(x) ((((x)&0x0FFF0000)>>16)+1) + +/* for IDMA intr register */ +#define IDMAINTBITS 0x337 + +/* PMU related registers */ +//#define PMU_REG_BASE 0xF0000000 +#define PMU_REG_RST 0xF0000100 +#define PMU_REG_CLK_DIV3 0xF0000030 +#define PMU_SDC0_RST_BIT 9 +#define PMU_SDC1_RST_BIT 10 +#define PMU_RST_MODULE(x) *((volatile unsigned int*)(PMU_REG_RST)) = (~((1<<(x)))) + +/* Define Card status bits (R1 response) */ +#define R1CS_ADDRESS_OUT_OF_RANGE 0x80000000 +#define R1CS_ADDRESS_MISALIGN 0x40000000 +#define R1CS_BLOCK_LEN_ERR 0x20000000 +#define R1CS_ERASE_SEQ_ERR 0x10000000 +#define R1CS_ERASE_PARAM 0x08000000 +#define R1CS_WP_VIOLATION 0x04000000 +#define R1CS_CARD_IS_LOCKED 0x02000000 +#define R1CS_LCK_UNLCK_FAILED 0x01000000 +#define R1CS_COM_CRC_ERROR 0x00800000 +#define R1CS_ILLEGAL_COMMAND 0x00400000 +#define R1CS_CARD_ECC_FAILED 0x00200000 +#define R1CS_CC_ERROR 0x00100000 +#define R1CS_ERROR 0x00080000 +#define R1CS_UNDERRUN 0x00040000 +#define R1CS_OVERRUN 0x00020000 +#define R1CS_CSD_OVERWRITE 0x00010000 +#define R1CS_WP_ERASE_SKIP 0x00008000 +#define R1CS_RESERVED_0 0x00004000 +#define R1CS_ERASE_RESET 0x00002000 +#define R1CS_CURRENT_STATE_MASK 0x00001e00 +#define R1CS_READY_FOR_DATA 0x00000100 +#define R1CS_SWITCH_ERROR 0x00000080 +#define R1CS_RESERVED_1 0x00000040 +#define R1CS_APP_CMD 0x00000020 +#define R1CS_RESERVED_2 0x00000010 +#define R1CS_APP_SPECIFIC_MASK 0x0000000c +#define R1CS_MANUFAC_TEST_MASK 0x00000003 +#define R1CS_ERROR_OCCURED_MAP 0xfdffa080 +#define R1CS_CURRENT_STATE(x) (((x)&R1CS_CURRENT_STATE_MASK)>>9) + +/* R5 response */ +#define R5_IO_CRC_ERR 0x00008000 +#define R5_IO_BAD_CMD 0x00004000 +#define R5_IO_GEN_ERR 0x00000800 +#define R5_IO_FUNC_ERR 0x00000200 +#define R5_IO_OUT_RANGE 0x00000100 +#define R5_IO_ERR_BITS 0x0000cb00 + +enum { + NONE_TYPE = 0, + SD_TYPE, + SD_2_0_TYPE, + SDIO_TYPE, +}; + +enum { + CARD_STATE_EMPTY = -1, + CARD_STATE_IDLE = 0, + CARD_STATE_READY = 1, + CARD_STATE_IDENT = 2, + CARD_STATE_STBY = 3, + CARD_STATE_TRAN = 4, + CARD_STATE_DATA = 5, + CARD_STATE_RCV = 6, + CARD_STATE_PRG = 7, + CARD_STATE_DIS = 8, + CARD_STATE_INA = 9 +}; + +enum DmaDescriptorDES1 // Buffer's size field of Descriptor +{ + DescBuf2SizMsk = 0x03FFE000, /* Mask for Buffer2 Size 25:13 */ + DescBuf2SizeShift = 13, /* Shift value for Buffer2 Size */ + DescBuf1SizMsk = 0x00001FFF, /* Mask for Buffer1 Size 12:0 */ + DescBuf1SizeShift = 0, /* Shift value for Buffer2 Size */ +}; + +enum DmaDescriptorDES0 // Control and status word of DMA descriptor DES0 +{ + DescOwnByDma = 0x80000000, /* (OWN)Descriptor is owned by DMA engine 31 */ + DescCardErrSummary = 0x40000000, /* Indicates EBE/RTO/RCRC/SBE/DRTO/DCRC/RE 30 */ + DescEndOfRing = 0x00000020, /* A "1" indicates End of Ring for Ring Mode 05 */ + DescSecAddrChained = 0x00000010, /* A "1" indicates DES3 contains Next Desc Address 04 */ + DescFirstDesc = 0x00000008, /* A "1" indicates this Desc contains first 03 + buffer of the data */ + DescLastDesc = 0x00000004, /* A "1" indicates buffer pointed to by this this 02 + Desc contains last buffer of Data */ + DescDisInt = 0x00000002, /* A "1" in this field disables the RI/TI of IDSTS 01 + for data that ends in the buffer pointed to by + this descriptor */ +}; + +typedef struct DmaDescStruct { + unsigned int desc0; /* control and status information of descriptor */ + unsigned int desc1; /* buffer sizes */ + unsigned int desc2; /* physical address of the buffer 1 */ + unsigned int desc3; /* physical address of the buffer 2 */ +}DmaDesc; + +typedef struct { + unsigned int wkmod; + volatile DmaDesc *pDmaDesc; + unsigned int idma_support; + unsigned int rca; + unsigned int ip_base; + unsigned int card_type; + unsigned int fifo_depth; + unsigned int fifo_threth; + unsigned int sectors; + unsigned int scr[2]; + unsigned int csd[4]; + unsigned int idsts; + rt_sem_t sem; + rt_sem_t mutex; + void (*cb)(void); +} sdc_t; + +#define ONE_BIT_MODE (0) +#define FOUR_BIT_MODE (1) + +#define BE32_TO_CPU(x) ((unsigned int)( \ + (((unsigned int)(x) & (unsigned int)0x000000ffUL) << 24) | \ + (((unsigned int)(x) & (unsigned int)0x0000ff00UL) << 8) | \ + (((unsigned int)(x) & (unsigned int)0x00ff0000UL) >> 8) | \ + (((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24))) + +#define synopmob_set_bits(reg, bit_id) *((volatile unsigned int *)(reg)) |= ((unsigned int)(bit_id)) +#define synopmob_clear_bits(reg, bit_id) *((volatile unsigned int *)(reg)) &= (~((unsigned int)(bit_id))) +#define synopmob_set_register(reg, val) *((volatile unsigned int*)(reg)) = (val) +#define synopmob_read_register(reg) (*((volatile unsigned int*)(reg))) + + +enum { + ERRNOERROR = 0, + + // for raw interrupt status error + ERRRESPRECEP, // 1 + ERRRESPCRC, + ERRDCRC, + ERRRESPTIMEOUT, + ERRDRTIMEOUT, + ERRUNDERWRITE, + ERROVERREAD, + ERRHLE, + ERRSTARTBIT, + ERRENDBITERR, // 10 + + // for R1 response + ERRADDRESSRANGE, // 11 + ERRADDRESSMISALIGN, + ERRBLOCKLEN, + ERRERASESEQERR, + ERRERASEPARAM, + ERRPROT, + ERRCARDLOCKED, + ERRCRC, + ERRILLEGALCOMMAND, + ERRECCFAILED, + ERRCCERR, + ERRUNKNOWN, + ERRUNDERRUN, + ERROVERRUN, + ERRCSDOVERWRITE, + ERRERASERESET, + ERRFSMSTATE, // 27 + + // for R5 response + ERRBADFUNC, // 28 + + // others + ERRCARDNOTCONN, // 29 + ERRCARDWPROTECT, + ERRCMDRETRIESOVER, + ERRNOTSUPPORTED, + ERRHARDWARE, + ERRDATANOTREADY, + ERRCARDINTERNAL, + ERRACMD41TIMEOUT, + ERRIDMA, + ERRNORES, + + ERRNOTEQUAL, +}; + +#ifdef __ASIC_BRANCH__ +#define SDC_WHERE() *((volatile unsigned int*)0x9b800000) = ((unsigned int)(__LINE__)) +#define SDC_ERROR() do { *((volatile unsigned int*)0x9b800008)=ret; *((volatile unsigned int*)0x9a7ff000) = 0;}while(0) +#define SDC_FINISH() *((volatile unsigned int*)0x9a7ff004) = 0; +#else +#define SDC_WHERE() +#define SDC_ERROR() +#define SDC_FINISH() +#endif //__ASIC_BRANCH__ + +#define SDC_WKMOD_25M_STAND_SPEED 0x0002 // 25M standard speed +#define SDC_WKMOD_50M_HI_SPEED 0x0004 // 50M high speed +#define SDC_WKMOD_1WIRE 0x0008 // 1 wire transfer mode +#define SDC_WKMOD_4WIRE 0x0010 // 4 wire transfer mode + +#define SDC_RW_USE_DMA 0x80000000 // Use DMA transfer? + + +typedef void* HSDC; + +// map to DmaDesc +typedef struct _buf_chain { + unsigned int csi; // desc0 + unsigned int size; // desc1 + void * buf; // desc2 + struct _buf_chain *next; // desc3 +}buf_chain_t; + + +/* + * for SD card + */ +extern int sdc_is_connected(unsigned int which); +extern int sdc_init(unsigned int which, unsigned int wkmod, unsigned int* dma_desc/*4Byte aligned,16 bytes space*/, HSDC* phandle); +extern int sdc_read_block(HSDC handle, unsigned int blk, unsigned int num, unsigned char* buffer); +extern int sdc_write_block(HSDC handle, unsigned int blk, unsigned int num, unsigned char* buffer); +extern int sdc_erase_block(HSDC handle, unsigned int blk, unsigned int num); +extern int sdc_get_sector_num(HSDC handle); +extern int sdc_set_clk_divider(unsigned int divider); + +/* + * for SDIO WIFI card + */ +extern void fh_sdio0_init(void); +extern void fh_sdio1_init(void); +extern void fh_sdio_init(void); +extern int sdio_init(unsigned int which, unsigned int wkmod, unsigned int* dma_desc/*4Byte aligned,16 bytes space*/, HSDC* phandle); +extern int sdio_high_speed_mode(HSDC handle, int bitwidth, int freq); +extern int sdio_enable_card_int(HSDC handle, int enable); +extern int sdio_set_card_int_cb(HSDC handle, void (*cb)(void)); +extern int sdio_drv_read(HSDC handle, unsigned int addr, unsigned int fn, unsigned int bcnt, unsigned int bsize, unsigned char *buf); +extern int sdio_drv_write(HSDC handle, unsigned int addr, unsigned int fn, unsigned int bcnt, unsigned int bsize, unsigned char *buf); +extern int sdio_drv_creg_read(HSDC handle, int addr, int fn, unsigned int *resp); +extern int sdio_drv_creg_write(HSDC handle, int addr, int fn, unsigned char data, unsigned int *resp); + +extern void inv_dcache_range(unsigned long start, unsigned long len); +extern void flush_dcache_range(unsigned long start, unsigned long len); + +#endif //__sdcard_h__ diff --git a/bsp/fh8620/libraries/inc/fh_spi.h b/bsp/fh8620/libraries/inc/fh_spi.h new file mode 100644 index 0000000000000000000000000000000000000000..d81c2428bc177e10ec7ae2f861182ad02cc8732d --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_spi.h @@ -0,0 +1,148 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_SPI_H_ +#define FH_SPI_H_ + +#include "fh_def.h" +#include "fh_arch.h" + +#define OFFSET_SPI_CTRL0 (0x00) +#define OFFSET_SPI_CTRL1 (0x04) +#define OFFSET_SPI_SSIENR (0x08) +#define OFFSET_SPI_MWCR (0x0c) +#define OFFSET_SPI_SER (0x10) +#define OFFSET_SPI_BAUD (0x14) +#define OFFSET_SPI_TXFTLR (0x18) +#define OFFSET_SPI_RXFTLR (0x1c) +#define OFFSET_SPI_TXFLR (0x20) +#define OFFSET_SPI_RXFLR (0x24) +#define OFFSET_SPI_SR (0x28) +#define OFFSET_SPI_IMR (0x2c) +#define OFFSET_SPI_ISR (0x30) +#define OFFSET_SPI_RISR (0x34) +#define OFFSET_SPI_TXOIC (0x38) +#define OFFSET_SPI_RXOIC (0x3c) +#define OFFSET_SPI_RXUIC (0x40) +#define OFFSET_SPI_MSTIC (0x44) +#define OFFSET_SPI_ICR (0x48) +#define OFFSET_SPI_DMACTRL (0x4c) +#define OFFSET_SPI_DMATDL (0x50) +#define OFFSET_SPI_DMARDL (0x54) +#define OFFSET_SPI_IDR (0x58) +#define OFFSET_SPI_SSI_COMPVER (0x5c) +#define OFFSET_SPI_DR (0x60) + + +#define SPI_FORMAT_MOTOROLA (0x00) +#define SPI_FORMAT_TI (0x10) +#define SPI_FORMAT_MICROWIRE (0x20) + +#define SPI_MODE_TX_RX (0x000) +#define SPI_MODE_TX_ONLY (0x100) +#define SPI_MODE_RX_ONLY (0x200) +#define SPI_MODE_EEPROM (0x300) + +#define SPI_DATA_SIZE_4BIT (0x03) +#define SPI_DATA_SIZE_5BIT (0x04) +#define SPI_DATA_SIZE_6BIT (0x05) +#define SPI_DATA_SIZE_7BIT (0x06) +#define SPI_DATA_SIZE_8BIT (0x07) +#define SPI_DATA_SIZE_9BIT (0x08) +#define SPI_DATA_SIZE_10BIT (0x09) +#define SPI_DATA_SIZE_16BIT (0x0f) + +#define SPI_POLARITY_HIGH (1<<7) +#define SPI_POLARITY_LOW (0<<7) + +#define SPI_PHASE_RX_FIRST (0<<6) +#define SPI_PHASE_TX_FIRST (1<<6) + +#define SPI_FIFO_DEPTH (32) + +#define SPI_IRQ_TXEIM (1<<0) +#define SPI_IRQ_TXOIM (1<<1) +#define SPI_IRQ_RXUIM (1<<2) +#define SPI_IRQ_RXOIM (1<<3) +#define SPI_IRQ_RXFIM (1<<4) +#define SPI_IRQ_MSTIM (1<<5) +#define SPI_IRQ_ALL (0x3f) + +#define SPI_ISR_FLAG (SPI_IRQ_TXEIM|SPI_IRQ_TXOIM|SPI_IRQ_RXUIM|SPI_IRQ_RXOIM) +#define SPI_ISR_ERROR (SPI_IRQ_TXOIM | SPI_IRQ_RXUIM | SPI_IRQ_RXOIM) + +#define SPI_STATUS_BUSY (1) + + +#define SPI_TX_DMA (1<<1) +#define SPI_RX_DMA (1<<0) + + +struct spi_config +{ + rt_uint32_t frame_format; + rt_uint32_t transfer_mode; + rt_uint32_t clk_polarity; + rt_uint32_t clk_phase; + rt_uint32_t data_size; + rt_uint32_t clk_div; +}; + + + + +struct fh_spi_obj +{ + rt_uint32_t id; + rt_uint32_t irq; + rt_uint32_t base; + rt_uint32_t fifo_len; + rt_uint32_t transfered_len; + rt_uint32_t received_len; + rt_uint32_t cs_gpio_pin; + + struct spi_config config; +}; + +void SPI_EnableSlaveen(struct fh_spi_obj *spi_obj, rt_uint32_t port); +void SPI_DisableSlaveen(struct fh_spi_obj *spi_obj, rt_uint32_t port); +void SPI_SetTxLevel(struct fh_spi_obj *spi_obj, rt_uint32_t level); +void SPI_EnableIrq(struct fh_spi_obj *spi_obj, rt_uint32_t flag); +void SPI_DisableIrq(struct fh_spi_obj *spi_obj, rt_uint32_t flag); +rt_uint32_t SPI_InterruptStatus(struct fh_spi_obj *spi_obj); +void SPI_ClearInterrupt(struct fh_spi_obj *spi_obj); +rt_uint32_t SPI_ReadTxFifoLevel(struct fh_spi_obj *spi_obj); +rt_uint32_t SPI_ReadRxFifoLevel(struct fh_spi_obj *spi_obj); +UINT8 SPI_ReadData(struct fh_spi_obj *spi_obj); +void SPI_WriteData(struct fh_spi_obj *spi_obj, UINT8 data); +rt_uint32_t SPI_ReadStatus(struct fh_spi_obj *spi_obj); +void SPI_Enable(struct fh_spi_obj *spi_obj, int enable); +void SPI_SetParameter(struct fh_spi_obj *spi_obj); +void SPI_EnableDma(struct fh_spi_obj *spi_obj, rt_uint32_t channel); +void SPI_DisableDma(struct fh_spi_obj *spi_obj, rt_uint32_t channel); +void SPI_WriteTxDmaLevel(struct fh_spi_obj *spi_obj, rt_uint32_t data); +void SPI_WriteRxDmaLevel(struct fh_spi_obj *spi_obj, rt_uint32_t data); +#endif /* FH_SPI_H_ */ diff --git a/bsp/fh8620/libraries/inc/fh_timer.h b/bsp/fh8620/libraries/inc/fh_timer.h new file mode 100644 index 0000000000000000000000000000000000000000..715a73b252e9efff66618aef4f024e643eb2204f --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_timer.h @@ -0,0 +1,100 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_TIMER_H_ +#define FH_TIMER_H_ + + /**************************************************************************** + * #include section + * add #include here if any + ***************************************************************************/ +#include "fh_def.h" + + + + + /**************************************************************************** + * #define section + * add constant #define here if any + ***************************************************************************/ +#define TIMER_CTRL_ENABLE (1u << 0) +#define TIMER_CTRL_MODE (1u << 1) +#define TIMER_CTRL_INTMASK (1u << 2) +#define TIMER_CTRL_PWMEN (1u << 3) + + + + /**************************************************************************** + * ADT section + * add Abstract Data Type definition here + ***************************************************************************/ +typedef struct { + RwReg TIMER_LOAD_COUNT; + RwReg TIMER_CURRENT_VALUE; + RwReg TIMER_CTRL_REG; + RwReg TIMER_EOI; + RwReg TIMER_INT_STATUS; +}timer; + + enum timer_mode { + TIMER_MODE_PERIODIC = 0, + TIMER_MODE_ONESHOT = 1, + }; + + + + +/**************************************************************************** +* extern variable declaration section +***************************************************************************/ + +/**************************************************************************** +* section +* add function prototype here if any +***************************************************************************/ + + +int timer_init(timer *tim); + +int timer_set_mode(timer *tim, enum timer_mode); + +void timer_set_period(timer *tim, UINT32 period, UINT32 clock); + +void timer_enable(timer *tim); + +void timer_disable(timer *tim); + +void timer_enable_irq(timer *tim); + +void timer_disable_irq(timer *tim); + +UINT32 timer_get_status(timer *tim); + +void udelay(unsigned long usec); + + +#endif /* #ifndef _TIMER_ */ + diff --git a/bsp/fh8620/libraries/inc/fh_uart.h b/bsp/fh8620/libraries/inc/fh_uart.h new file mode 100644 index 0000000000000000000000000000000000000000..87da9ee2f787224906465f1915c3e98457ea3e9c --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_uart.h @@ -0,0 +1,245 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_UART_H_ +#define FH_UART_H_ + + /**************************************************************************** + * #include section + * add #include here if any + ***************************************************************************/ +#include "fh_def.h" + + /**************************************************************************** + * #define section + * add constant #define here if any + ***************************************************************************/ + +#define UART_RBR RBRTHRDLL +#define UART_THR RBRTHRDLL +#define UART_DLL RBRTHRDLL + +#define UART_DLH DLHIER +#define UART_IER DLHIER + +#define UART_IIR IIRFCR +#define UART_FCR IIRFCR + +/* LSR register bits */ +#define UART_LSR_DR (1 << 0) +#define UART_LSR_OE (1 << 1) +#define UART_LSR_PE (1 << 2) +#define UART_LSR_FE (1 << 3) +#define UART_LSR_BI (1 << 4) +#define UART_LSR_THER (1 << 5) +#define UART_LST_TEMT (1 << 6) +#define UART_LSR_RFE (1 << 7) + +/* LCR register bits */ +#define UART_LCR_DLS5 0x00 +#define UART_LCR_DLS6 0x01 +#define UART_LCR_DLS7 0x02 +#define UART_LCR_DLS8 0x03 +#define UART_LCR_STOP1 0 +#define UART_LCR_STOP2 (1 << 2) +#define UART_LCR_PEN (1 << 3) +#define UART_LCR_EVEN (1 << 4) +#define UART_LCR_SP (1 << 5) +#define UART_LCR_BC (1 << 6) +#define UART_LCR_DLAB (1 << 7) + +/* MCR register bits */ +#define UART_MCR_DTR (1 << 0) +#define UART_MCR_RTS (1 << 1) +#define UART_MCR_OUT1 (1 << 2) +#define UART_MCR_OUT2 (1 << 3) +#define UART_MCR_LB (1 << 4) +#define UART_MCR_AFCE (1 << 5) +#define UART_MCR_SIRE (1 << 6) + +/* FCR register bits */ +#define UART_FCR_FIFOE (1 << 0) +#define UART_FCR_RFIFOR (1 << 1) +#define UART_FCR_XFIFOR (1 << 2) +#define UART_FCR_DMAM (1 << 3) +#define UART_FCR_TET_EMPTY (0x00 << 4) +#define UART_FCR_TET_TWO (0x01 << 4) +#define UART_FCR_TET_1_4 (0x02 << 4) +#define UART_FCR_TET_1_2 (0x03 << 4) +#define UART_FCR_RT_ONE (0x00 << 6) +#define UART_FCR_RT_1_4 (0x01 << 6) +#define UART_FCR_RT_1_2 (0x02 << 6) +#define UART_FCR_RT_L2 (0x03 << 6) + +/* IER register bits */ +#define UART_IER_ERBFI (1 << 0) +#define UART_IER_ETBEI (1 << 1) +#define UART_IER_ELSI (1 << 2) +#define UART_IER_EDSSI (1 << 3) +#define UART_IER_PTIME (1 << 7) + +/* USR register bits */ +#define UART_USR_BUSY (1 << 0) +#define UART_USR_TFNF (1 << 1) +#define UART_USR_TFE (1 << 2) +#define UART_USR_RNFE (1 << 3) +#define UART_USR_RFE (1 << 4) + +/* IIR register bits */ +#define UART_IIR_RIID_MASK 0x0f +#define UART_IIR_MODEM 0x00 +#define UART_IIR_NOINT 0x01 +#define UART_IIR_THREMPTY 0x02 +#define UART_IIR_RDATD 0x04 +#define UART_IIR_RLINEST 0x06 +#define UART_IIR_BUSY 0x07 +#define UART_IIR_CHRTOUT 0x0c +#define UART_IIR_FIFOSE (0x03 << 6) + +//uart baudrate cofig +//#define UART_CLOCK_FREQ (27000000) //27MHZ +// +//#define DIV(n) (((UART_CLOCK_FREQ/(n))+8)/16) + + + /**************************************************************************** + * ADT section + * add Abstract Data Type definition here + ***************************************************************************/ + + + + +typedef struct { + RwReg RBRTHRDLL; /* UART_RBR, UART_THR, UART_DLL */ + RwReg DLHIER; /* UART_DLH, UART_IER */ + RwReg IIRFCR; /* UART_IIR, UART_FCR */ + RwReg UART_LCR; /*(0x000c) */ + RwReg UART_MCR; /*(0x0010) */ + RwReg UART_LSR; /*(0x0014) */ + RwReg UART_MSR; /*(0x0018) */ + RwReg UART_SCR; /*(0x001c) */ + RwReg reserved[20]; + RwReg UART_FAR; /* (0x0070) */ + RwReg UART_TFR; /* (0x0074) */ + RwReg UART_RFW; /* (0x0078) */ + RwReg UART_USR; /* (0x007c) */ + RwReg UART_TFL; /* (0x0080) */ + RwReg UART_RFL; /* (0x0084) */ + RwReg UART_SRR; /* (0x0088) */ + RwReg reserved1[3]; + RwReg UART_SFE; /* (0x0098) */ + RwReg UART_SRT; /* (0x009c) */ + RwReg UART_STET; /* (0x00a0) */ + RwReg UART_HTX; /* (0x00a4) */ + RwReg UART_DMASA; /* (0x00a8) */ + RwReg reserved2[18]; + RwReg UART_CPR; /* (0x00f4) */ + RwReg UART_UCV; /* (0x00f8) */ + RwReg UART_CTR; /* (0x00fc) */ +}uart; + + + +struct fh_uart { + uart *uart_port; + int irq; +}; + + + + + +enum data_bits { + UART_DATA_BIT5 = 0, + UART_DATA_BIT6 = 1, + UART_DATA_BIT7 = 2, + UART_DATA_BIT8 = 3 +}; + +enum stop_bits { + UART_STOP_BIT1 = 0, + UART_STOP_BIT1_5 = 1, + UART_STOP_BIT2 = 2 +}; + +enum parity { + UART_PARITY_NONE = 0, + UART_PARITY_EVEN = 1, + UART_PARITY_ODD = 2, + UART_PARITY_ST = 3 /* Stick Parity */ +}; + + +#define UART_CLOCK_FREQ (30000000) //30MHZ +typedef enum enum_uart_baudrate{ + BAUDRATE_9600 = (((UART_CLOCK_FREQ/9600)+8)/16), + BAUDRATE_19200 = (((UART_CLOCK_FREQ/19200)+8)/16), + BAUDRATE_38400 = (((UART_CLOCK_FREQ/38400)+8)/16), + BAUDRATE_57600 = (((UART_CLOCK_FREQ/57600)+8)/16), + BAUDRATE_115200 = (((UART_CLOCK_FREQ/115200)+8)/16), + BAUDRATE_194000 = (((UART_CLOCK_FREQ/194000)+8)/16), +}uart_baudrate_e; + +/**************************************************************************** +* extern variable declaration section +***************************************************************************/ + +extern int uart_init(uart *port); + +extern UINT32 uart_get_status(uart *port); + +extern void uart_configure(uart *port, enum data_bits data_bit, + enum stop_bits stop_bit, enum parity parity, + UINT32 buard_rate, UINT32 uart_clk); + + +extern int uart_enable_irq(uart *port, UINT32 mode); + +extern int uart_disable_irq(uart *port, UINT32 mode); + +extern UINT32 uart_get_iir_status(uart *port); + +extern UINT32 uart_get_line_status(uart *port); + +extern UINT32 uart_is_rx_ready(uart *port); + +extern UINT8 uart_getc(uart *port); + +extern void uart_putc(uart *port, UINT8 c); + +extern void uart_set_fifo_mode(uart *port, UINT32 fifo_mode); + +/**************************************************************************** +* section +* add function prototype here if any +***************************************************************************/ + + + + +#endif /* #ifndef _TIMER_ */ + diff --git a/bsp/fh8620/libraries/inc/fh_wdt.h b/bsp/fh8620/libraries/inc/fh_wdt.h new file mode 100644 index 0000000000000000000000000000000000000000..b00343ac6f3b93815192b6fa7044343f6f4fb643 --- /dev/null +++ b/bsp/fh8620/libraries/inc/fh_wdt.h @@ -0,0 +1,65 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_WDT_H_ +#define FH_WDT_H_ + +#include "fh_def.h" + +#define WDOG_CONTROL_REG_OFFSET 0x00 +#define WDOG_CONTROL_REG_WDT_EN_MASK 0x01 +#define WDOG_CONTROL_REG_RMOD_MASK 0x02 +#define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04 +#define WDOG_CURRENT_COUNT_REG_OFFSET 0x08 +#define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c + +#define WDOG_COUNTER_RESTART_KICK_VALUE 0x76 + +/* Hardware timeout in seconds */ +#define WDT_HW_TIMEOUT 2 +/* User land timeout */ +#define WDT_HEARTBEAT 15 + +/* The maximum TOP (timeout period) value that can be set in the watchdog. */ +#define FH_WDT_MAX_TOP 15 + +#define WDT_TIMEOUT (HZ / 2) + +struct fh_wdt_obj +{ + int id; + int irq; + unsigned int base; +}; + +void WDT_Enable(struct fh_wdt_obj *wdt_obj, int enable); +inline int WDT_IsEnable(struct fh_wdt_obj *wdt_obj); +void WDT_SetTopValue(struct fh_wdt_obj *wdt_obj, int top); +void WDT_SetCtrl(struct fh_wdt_obj *wdt_obj, UINT32 reg); +void WDT_Kick(struct fh_wdt_obj *wdt_obj); +unsigned int WDT_GetCurrCount(struct fh_wdt_obj *wdt_obj); + +#endif /* FH_WDT_H_ */ diff --git a/bsp/fh8620/libraries/startup/SConscript b/bsp/fh8620/libraries/startup/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..1e4964b3f2cfaa55ed52cd82ddc6e3958ddc96eb --- /dev/null +++ b/bsp/fh8620/libraries/startup/SConscript @@ -0,0 +1,10 @@ +from building import * + +Import('rtconfig') + +cwd = GetCurrentDir() +src = ['gcc/start_gcc.S'] + +group = DefineGroup('Libraries', src, depend = ['']) + +Return('group') diff --git a/bsp/fh8620/libraries/startup/gcc/start_gcc.S b/bsp/fh8620/libraries/startup/gcc/start_gcc.S new file mode 100644 index 0000000000000000000000000000000000000000..dccd94b7570eb0f4eab8133f01c01e2d40920196 --- /dev/null +++ b/bsp/fh8620/libraries/startup/gcc/start_gcc.S @@ -0,0 +1,416 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include +#include + +#define CONFIG_STACKSIZE 512 + +#define FPEXC_EN (1 << 30) /* VFP enable bit */ +.equ I_BIT, 0x80 @ when I bit is set, IRQ is disabled +.equ F_BIT, 0x40 @ when F bit is set, FIQ is disabled + +#.equ USERMODE, 0x10 +#.equ FIQMODE, 0x11 +#.equ IRQMODE, 0x12 +#.equ SVCMODE, 0x13 +#.equ ABORTMODE, 0x17 +#.equ UNDEFMODE, 0x1b +#.equ MODEMASK, 0x1f +#.equ NOINT, 0xc0 + + +.equ RAM_BASE, 0x00000000 /*Start address of RAM*/ +.equ ROM_BASE, 0xA0000000 /*Start address of Flash*/ + +#define FH81_INTC_BASE 0xE0200000 + +/* + ************************************************************************* + * + * Jump vector table + * + ************************************************************************* + */ + +.section .init, "ax" +.code 32 + +.globl _start +_start: + b reset + ldr pc, _vector_undef + ldr pc, _vector_swi + ldr pc, _vector_pabt + ldr pc, _vector_dabt + ldr pc, _vector_resv + ldr pc, _vector_irq + ldr pc, _vector_fiq + +_vector_undef: .word vector_undef +_vector_swi: .word vector_swi +_vector_pabt: .word vector_pabt +_vector_dabt: .word vector_dabt +_vector_resv: .word vector_resv +_vector_irq: .word vector_irq +_vector_fiq: .word vector_fiq + +.balignl 16,0xdeadbeef + +/* + ************************************************************************* + * + * Startup Code (reset vector) + * relocate armboot to ram + * setup stack + * jump to second stage + * + ************************************************************************* + */ + +/* + * rtthread kernel start and end + * which are defined in linker script + */ +.globl _rtthread_start +_rtthread_start: + .word _start + +.globl _rtthread_end +_rtthread_end: + .word _end + +/* + * rtthread bss start and end which are defined in linker script + */ +.globl _bss_start +_bss_start: + .word __bss_start + +.globl _bss_end +_bss_end: + .word __bss_end + +/* IRQ stack memory (calculated at run-time) */ +.globl IRQ_STACK_START +IRQ_STACK_START: + .word _irq_stack_start + 1024 + +.globl FIQ_STACK_START +FIQ_STACK_START: + .word _fiq_stack_start + 1024 + +.globl UNDEFINED_STACK_START +UNDEFINED_STACK_START: + .word _undefined_stack_start + CONFIG_STACKSIZE + +.globl ABORT_STACK_START +ABORT_STACK_START: + .word _abort_stack_start + CONFIG_STACKSIZE + +.globl _STACK_START +_STACK_START: + .word _svc_stack_start + 4096 + +/* ----------------------------------entry------------------------------*/ +reset: + + /* set the cpu to SVC32 mode */ + mrs r0,cpsr + bic r0,r0,#MODEMASK + orr r0,r0,#SVCMODE + msr cpsr,r0 + + @vector 0x0 + @enable icaches + @little endian + @disable dcaches, mmu + //ldr r0, =0x00400078 + @ldr r0, =0x0000107a + // mcr p15, 0, r0, c1, c0, 0 + + //read + ldr r0, =0x0 + mrc p15, 0, r0, c1, c0, 0 + + //change + ldr r0, =0x00400078 + mcr p15, 0, r0, c1, c0, 0 + //check + ldr r0, =0x0 + mrc p15, 0, r0, c1, c0, 0 + + ldr r0, =0x00000000 + mcr p15, 0, r0, c8, c7, 0 @ Flush TLB + mcr p15, 0, r0, c7, c7, 0 @ Flush Caches + mcr p15, 0, r0, c7, c10, 4 @ Flush Write Buffer + + /* mask all IRQs by clearing all bits in the INTMRs set low and high to 0 */ + ldr r1, =FH81_INTC_BASE + ldr r0, =0x0 + str r0, [r1] + str r0, [r1, #4] + + + /* setup stack */ + bl stack_setup + + /* clear .bss */ + mov r0,#0 /* get a zero */ + ldr r1,=__bss_start /* bss start */ + ldr r2,=__bss_end /* bss end */ + +bss_loop: + cmp r1,r2 /* check if data to clear */ + strlo r0,[r1],#4 /* clear 4 bytes */ + blo bss_loop /* loop until done */ + + /* call C++ constructors of global objects */ + ldr r0, =__ctors_start__ + ldr r1, =__ctors_end__ + +ctor_loop: + cmp r0, r1 + beq ctor_end + ldr r2, [r0], #4 + stmfd sp!, {r0-r1} + mov lr, pc + bx r2 + ldmfd sp!, {r0-r1} + b ctor_loop + +ctor_end: + + /* enable_unaligned_access */ + mrc p15, 0, r0, c1, c0, 0 + bic r0, r0, #0x02 /*Clear the A bit, bit 1*/ + and r0, r0, #(1 << 22) /*Set the U bit, bit 22*/ + mcr p15, 0, r0, c1, c0, 0 + +#ifdef RT_USING_VFP + bl vfp_init +#endif + + /* start RT-Thread Kernel */ + ldr pc, _rtthread_startup + +_rtthread_startup: + .word rtthread_startup + +.global cpu_reset +cpu_reset: + + #ldr r0, =0xfffffd00 + #ldr r1, =(AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST) + #str r1, [r0] + #mov pc, lr + B cpu_reset + +/* + ************************************************************************* + * + * Interrupt handling + * + ************************************************************************* + */ + +.macro push_exp_reg + sub sp, sp, #S_FRAME_SIZE @/* Sizeof(struct rt_hw_stack) */ + stmib sp, {r0 - r12} @/* Calling r0-r12 */ + mov r0, sp + mrs r6, spsr @/* Save CPSR */ + str lr, [r0, #S_PC] @/* Push PC */ + str r6, [r0, #S_CPSR] @/* Push CPSR */ + @ switch to SVC mode with no interrupt + msr cpsr_c, #I_BIT|F_BIT|SVCMODE + str sp, [r0, #S_SP] @/* Save calling SP */ + str lr, [r0, #S_LR] @/* Save calling PC */ +.endm + +#ifdef RT_USING_VFP +__ret_to_undef: + ldmfd sp!, {r4} @ pop task's cpsr to spsr + msr spsr_cxsf, r4 + ldmfd sp!, {r0-r12} + add sp, sp, #4 + ldmfd sp!, {lr,pc}^ +#endif + +/* exception handlers */ + .align 5 +vector_undef: + push_exp_reg +#ifdef RT_USING_VFP + cps #UNDEFMODE + ldr r2, [r0, #S_PC] @ r2=faulted PC+4 + adr r9, __ret_to_undef @ r9=exception success return + sub r1, r2, #4 + ldr r0, [r1] @ r0=faulted instruction + bl undef_entry + mov r0, sp +#endif + bl rt_hw_trap_udef + + .align 5 +vector_swi: + push_exp_reg + bl rt_hw_trap_swi + + .align 5 +vector_pabt: + push_exp_reg + bl rt_hw_trap_pabt + + .align 5 +vector_dabt: + push_exp_reg + bl rt_hw_trap_dabt + + .align 5 +vector_resv: + push_exp_reg + bl rt_hw_trap_resv + +.globl rt_interrupt_enter +.globl rt_interrupt_leave +.globl rt_thread_switch_interrupt_flag +.globl rt_interrupt_from_thread +.globl rt_interrupt_to_thread +vector_irq: + stmfd sp!, {r0-r12,lr} +@ +#ifdef RT_USING_VFP + vmrs r0, fpexc + stmfd sp!, {r0} @ save VFP status -- irq_fpexc = fpexc + bic r0, r0, #FPEXC_EN @ always disable VFP so we can + @ lazily save/restore the old state. + vmsr fpexc, r0 +#endif + + bl rt_interrupt_enter + bl rt_hw_trap_irq + bl rt_interrupt_leave + +#ifdef RT_USING_VFP + ldmfd sp!, {r1} + vmrs r0, fpexc + tst r0, #FPEXC_EN @ irq used VFP? + bicne r1, r0, #FPEXC_EN @ if irq used VFP, then clear fpexc.en bit + vmsr fpexc, r1 @ restore VFP status + @ if irq not used VFP, then fpexc = irq_fpexc + @ if irq used VFP, then disable VFP +#endif + + @ if rt_thread_switch_interrupt_flag set, jump to + @ rt_hw_context_switch_interrupt_do and don't return + ldr r0, =rt_thread_switch_interrupt_flag + ldr r1, [r0] + cmp r1, #1 + beq rt_hw_context_switch_interrupt_do + + ldmfd sp!, {r0-r12,lr} + @mrs r8,spsr + @msr cpsr, r8 + subs pc, lr, #4 + + .align 5 +vector_fiq: + stmfd sp!,{r0-r7,lr} + bl rt_hw_trap_fiq + ldmfd sp!,{r0-r7,lr} + subs pc,lr,#4 + +rt_hw_context_switch_interrupt_do: + mov r1, #0 @ clear flag + str r1, [r0] + + ldmfd sp!, {r0-r12,lr}@ reload saved registers + stmfd sp, {r0-r2} @ save r0-r2 + + mrs r0, spsr @ get cpsr of interrupt thread + + sub r1, sp, #4*3 + sub r2, lr, #4 @ save old task's pc to r2 + + @ switch to SVC mode with no interrupt + msr cpsr_c, #I_BIT|F_BIT|SVCMODE + + stmfd sp!, {r2} @ push old task's pc + stmfd sp!, {r3-r12,lr}@ push old task's lr,r12-r4 + ldmfd r1, {r1-r3} @ restore r0-r2 of the interrupt thread + stmfd sp!, {r1-r3} @ push old task's r0-r2 + stmfd sp!, {r0} @ push old task's cpsr + + ldr r4, =rt_interrupt_from_thread + ldr r5, [r4] + str sp, [r5] @ store sp in preempted tasks's TCB + + ldr r6, =rt_interrupt_to_thread + ldr r6, [r6] + ldr sp, [r6] @ get new task's stack pointer + + ldmfd sp!, {r4} @ pop new task's cpsr to spsr + msr spsr_cxsf, r4 + +#ifdef RT_USING_VFP + vmrs r0, fpexc + bic r0, r0, #FPEXC_EN @ always disable VFP so we can + @ lazily save/restore the old state. + vmsr fpexc, r0 +#endif + + ldmfd sp!, {r0-r12,lr,pc}^ @ pop new task's r0-r12,lr & pc, copy spsr to cpsr + + +stack_setup: + mrs r0, cpsr + bic r0, r0, #MODEMASK + orr r1, r0, #UNDEFMODE|NOINT + msr cpsr_cxsf, r1 /* undef mode */ + ldr sp, UNDEFINED_STACK_START + + orr r1,r0,#ABORTMODE|NOINT + msr cpsr_cxsf,r1 /* abort mode */ + ldr sp, ABORT_STACK_START + + orr r1,r0,#IRQMODE|NOINT + msr cpsr_cxsf,r1 /* IRQ mode */ + ldr sp, IRQ_STACK_START + + orr r1,r0,#FIQMODE|NOINT + msr cpsr_cxsf,r1 /* FIQ mode */ + ldr sp, FIQ_STACK_START + + bic r0,r0,#MODEMASK + orr r1,r0,#SVCMODE|NOINT + msr cpsr_cxsf,r1 /* SVC mode */ + + ldr sp, _STACK_START + + /* USER mode is not initialized. */ + bx lr /* The LR register may be not valid for the mode changes.*/ + +/*/*}*/ diff --git a/bsp/fh8620/link.ld b/bsp/fh8620/link.ld new file mode 100644 index 0000000000000000000000000000000000000000..7cd6904aef69b29ee0afa14beb5fdebb192f9462 --- /dev/null +++ b/bsp/fh8620/link.ld @@ -0,0 +1,96 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) +ENTRY(_start) +SECTIONS +{ + . =0xA0000000; + + . = ALIGN(4); + __text_start = .; + .text : + { + *(.init) + *(.text) + *(.gnu.linkonce.t*) + + /* section information for finsh shell */ + . = ALIGN(4); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + . = ALIGN(4); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + . = ALIGN(4); + + . = ALIGN(4); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + . = ALIGN(4); + + /* section information for modules */ + . = ALIGN(4); + __rtmsymtab_start = .; + KEEP(*(RTMSymTab)) + __rtmsymtab_end = .; + } + __text_end = .; + + . = ALIGN(4); + __rodata_start = .; + .rodata : { *(.rodata) *(.rodata.*) *(.gnu.linkonce.r*) *(.eh_frame) } + __rodata_end = .; + + . = ALIGN(4); + .ctors : + { + PROVIDE(__ctors_start__ = .); + KEEP(*(SORT(.ctors.*))) + KEEP(*(.ctors)) + PROVIDE(__ctors_end__ = .); + } + + .dtors : + { + PROVIDE(__dtors_start__ = .); + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + PROVIDE(__dtors_end__ = .); + } + + . = ALIGN(4); + __data_start = .; + .data : + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + } + __data_end = .; + + . = ALIGN(4); + .nobss : { *(.nobss) } + + . = ALIGN(4); + __bss_start = .; + .bss : { *(.bss) } + __bss_end = .; + + /* stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_info 0 : { *(.debug_info) } + .debug_line 0 : { *(.debug_line) } + .debug_pubnames 0 : { *(.debug_pubnames) } + .debug_aranges 0 : { *(.debug_aranges) } + + _end = .; +} diff --git a/bsp/fh8620/platform/SConscript b/bsp/fh8620/platform/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..4c815c49b835a3a5ea61f337dc17154dd316d7d1 --- /dev/null +++ b/bsp/fh8620/platform/SConscript @@ -0,0 +1,15 @@ +# RT-Thread building script for bridge + +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/fh8620/platform/board.h b/bsp/fh8620/platform/board.h new file mode 100644 index 0000000000000000000000000000000000000000..46b276b05214ac98364eec48b6a33bf4354a036d --- /dev/null +++ b/bsp/fh8620/platform/board.h @@ -0,0 +1,33 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __BOARD_H__ +#define __BOARD_H__ +#include "platform_def.h" + +void rt_hw_board_init(void); + +#endif diff --git a/bsp/fh8620/platform/board_info.h b/bsp/fh8620/platform/board_info.h new file mode 100644 index 0000000000000000000000000000000000000000..71f8d3c19e0c54163d8bee1d32084c03e1917dd5 --- /dev/null +++ b/bsp/fh8620/platform/board_info.h @@ -0,0 +1,83 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __BOARD_INFO_H__ +#define __BOARD_INFO_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************** + * #include section + * add #include here if any + ***************************************************************************/ + +/**************************************************************************** + * #define section + * add constant #define here if any + ***************************************************************************/ + +/**************************************************************************** + * ADT section + * add Abstract Data Type definition here + ***************************************************************************/ + +typedef int (*probe_p)(void *); +typedef int (*exit_p)(void *); + +struct fh_board_ops { + //void *ops_data; + probe_p probe; + probe_p exit; +}; + +struct fh_board_info { + char *name; + void *data; + struct fh_board_ops *ops; + +}; + +/**************************************************************************** + * extern variable declaration section + ***************************************************************************/ + +int fh_board_info_init(void); +struct fh_board_info *fh_board_info_register(char *info_name, void *data); +int fh_board_driver_register(char *info_name, struct fh_board_ops *ops); +void fh_print_all_board_info(void); +void fh_free_all_info(void); +/**************************************************************************** + * section + * add function prototype here if any + ***************************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/fh8620/platform/common/SConscript b/bsp/fh8620/platform/common/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..9679d6df56f5d8075139378f3d836c7466f95022 --- /dev/null +++ b/bsp/fh8620/platform/common/SConscript @@ -0,0 +1,8 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + +group = DefineGroup('Platform', src, depend = ['']) + +Return('group') diff --git a/bsp/fh8620/platform/common/board_info.c b/bsp/fh8620/platform/common/board_info.c new file mode 100644 index 0000000000000000000000000000000000000000..aa56cbac626b49e4ceeb50b9fc332b9272b44739 --- /dev/null +++ b/bsp/fh8620/platform/common/board_info.c @@ -0,0 +1,250 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +/***************************************************************************** + * Include Section + * add all #include here + *****************************************************************************/ +#include "board_info.h" +#include +#include +/***************************************************************************** + * Define section + * add all #define here + *****************************************************************************/ +struct fh_board_info_list_node { + struct fh_board_info obj; + rt_list_t list; +}; + +#define CHECK_TEST_LIST_EMPTY \ + if(rt_list_isempty(&board_info_head.list)) \ + rt_kprintf("board info is null...please register first..\n") + +/**************************************************************************** + * ADT section + * add definition of user defined Data Type that only be used in this file here + ***************************************************************************/ + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = rt_list_entry((head)->next, typeof(*pos), member), \ + n = rt_list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = rt_list_entry(n->member.next, typeof(*n), member)) + +#define PARA_ERROR (-1) +#define PROBE_FUNC_MISS (-2) + +/****************************************************************************** + * Function prototype section + * add prototypes for all functions called by this file,execepting those + * declared in header file + *****************************************************************************/ + +//static void fh81_soc_free_all_test(void); +//static void fh81_soc_print_all_test(void); +//static void fh81_soc_auto_test_all(void); +//static void fh81_soc_test_help(void); +/***************************************************************************** + * Global variables section - Exported + * add declaration of global variables that will be exported here + * e.g. + * int8_t foo; + ****************************************************************************/ + +/***************************************************************************** + + * static fun; + *****************************************************************************/ +static struct fh_board_info_list_node board_info_head; + + + +/***************************************************************************** + * Global variables section - Local + * define global variables(will be refered only in this file) here, + * static keyword should be used to limit scope of local variable to this file + * e.g. + * static uint8_t ufoo; + *****************************************************************************/ + + + /* function body */ +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + +int fh_board_info_init(void) { + + memset(&board_info_head, 0x0, sizeof(struct fh_board_info_list_node)); + rt_list_init(&board_info_head.list); + board_info_head.obj.name = "NO INFO"; + return 0; +} + +void fh_free_all_info(void) { + rt_list_t *p_list; + struct fh_board_info_list_node *info_node; + struct fh_board_info_list_node *_info_node; + p_list = &board_info_head.list; + + CHECK_TEST_LIST_EMPTY; + + list_for_each_entry_safe(info_node, _info_node, p_list, list) + { + + if (info_node->obj.ops->exit) { + info_node->obj.ops->exit(info_node->obj.data); + } + rt_kprintf("soc free list name:(%s)\n", info_node->obj.name); + rt_free(info_node); + } + fh_board_info_init(); +} + +void fh_print_all_board_info(void) { + rt_list_t *p_list; + + struct fh_board_info_list_node *info_node; + struct fh_board_info_list_node *_info_node; + p_list = &board_info_head.list; + + CHECK_TEST_LIST_EMPTY; + + list_for_each_entry_safe(info_node, _info_node, p_list, list) + { + rt_kprintf("%s\n", info_node->obj.name); + } +} + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + +//register the platform info such as base add,isr no.. +//caution:do not free the name and data because of here not copy +struct fh_board_info *fh_board_info_register(char *info_name, void *data) { + rt_list_t *p_list; + struct fh_board_info_list_node *new_node; + struct fh_board_info_list_node *info_node; + struct fh_board_info_list_node *_info_node; + p_list = &board_info_head.list; + + if (RT_NULL == info_name || RT_NULL == data) { + rt_kprintf("info name or info data is NULL!\n"); + return RT_NULL; + } + + //check if the func is already in the test list.... +#if(0) + list_for_each_entry_safe(info_node, _info_node, p_list, list) { + if (!memcmp(info_node->obj.name, info_name, strlen(info_name))) { + rt_kprintf("info_name(%s) is already registered\n", info_name); + return RT_NULL; + } + } +#endif + + new_node = (struct fh_board_info_list_node *) rt_malloc( + sizeof(struct fh_board_info_list_node)); + if (!new_node) { + rt_kprintf("malloc new_list_node failed~\n"); + return RT_NULL; + } + + new_node->obj.name = info_name; + new_node->obj.data = data; + //here insert "before" and test is "after" will make the list like a fifo... + rt_list_insert_before(&board_info_head.list, &new_node->list); + return &new_node->obj; +} + +//back the platform info +static void *fh_get_board_info_data(char *info_name) { + + rt_list_t *p_list; + struct fh_board_info_list_node *info_node; + struct fh_board_info_list_node *_info_node; + p_list = &board_info_head.list; + + //check info name + if (RT_NULL == info_name) { + rt_kprintf("info name is NULL!\n"); + return RT_NULL; + } + + CHECK_TEST_LIST_EMPTY; + + list_for_each_entry_safe(info_node, _info_node, p_list, list) + { + if (!strcmp(info_node->obj.name, info_name)) { + return info_node->obj.data; + } + } + + rt_kprintf("Can't find the board info name:%s\n", info_name); +} + +int fh_board_driver_register(char *info_name, struct fh_board_ops *ops) { + + rt_list_t *p_list; + struct fh_board_info_list_node *new_node; + struct fh_board_info_list_node *info_node; + struct fh_board_info_list_node *_info_node; + p_list = &board_info_head.list; + + if (RT_NULL == info_name || RT_NULL == ops) { + rt_kprintf("info name or ops func is NULL!\n"); + return PARA_ERROR; + } + + list_for_each_entry_safe(info_node, _info_node, p_list, list) + { + if (!strcmp(info_node->obj.name, info_name)) { + + info_node->obj.ops = ops; + if (info_node->obj.ops->probe) { + info_node->obj.ops->probe(info_node->obj.data); + } + + //return info_node->obj.data; + } + } + + //rt_kprintf("Can't find the board info name:%s\n",info_name); + + return 0; +} diff --git a/bsp/fh8620/platform/common/chkenv.c b/bsp/fh8620/platform/common/chkenv.c new file mode 100644 index 0000000000000000000000000000000000000000..563b5bb55ac1d3c84431661dc33cb901764fd250 --- /dev/null +++ b/bsp/fh8620/platform/common/chkenv.c @@ -0,0 +1,120 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +/***************************************************************************** + * Include Section + * add all #include here + * those macro below should define.... + * or the dsp or isp lib will not be supported + *****************************************************************************/ +#include + +//#pragma comment ( linker, "/EXPORT: MyExportFunction = _MyExportFunctio" ) +//#pragma message +//#warning RT_NAME_MAX=32 +//#error .... + +//#define yu_error(macro) #error ##macro + + +/************************** + * + * + * define value below. + * + * + **************************/ +#if RT_NAME_MAX != 16 +#error "define RT_NAME_MAX 16" +#endif + +#if RT_TICK_PER_SECOND != 100 +#warning "RT_TICK_PER_SECOND = 100" +#endif + + +#if RT_ALIGN_SIZE != 4 +#error "define RT_ALIGN_SIZE 4" +#endif + +/************************** + * + * + * should define below.. + * + * + **************************/ +#ifndef RT_USING_SEMAPHORE +#error need define "RT_USING_SEMAPHORE" +#endif + +#ifndef RT_USING_MUTEX +#error need define "RT_USING_MUTEX" +#endif + +#ifndef RT_USING_EVENT +#error need define "RT_USING_EVENT" +#endif + +#ifndef RT_USING_MAILBOX +#error need define "RT_USING_MAILBOX" +#endif + +#ifndef RT_USING_MESSAGEQUEUE +#warning need define "RT_USING_MESSAGEQUEUE" +#endif + +#ifndef RT_USING_MEMPOOL +#warning need define "RT_USING_MEMPOOL" +#endif + +#ifndef RT_USING_HEAP +#error need define "RT_USING_HEAP" +#endif + +#ifndef RT_USING_MEMHEAP +#warning need define "RT_USING_MEMHEAP" +#endif + +#ifndef RT_USING_DEVICE +#error need define "RT_USING_DEVICE" +#endif + +#ifndef RT_USING_CONSOLE +#error need define "RT_USING_CONSOLE" +#endif + +#ifndef RT_USING_FINSH +#error need define "RT_USING_FINSH" +#endif + +#ifndef RT_USING_I2C +#error need define "RT_USING_I2C" +#endif + + + + diff --git a/bsp/fh8620/platform/fh8620/SConscript b/bsp/fh8620/platform/fh8620/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..4c815c49b835a3a5ea61f337dc17154dd316d7d1 --- /dev/null +++ b/bsp/fh8620/platform/fh8620/SConscript @@ -0,0 +1,15 @@ +# RT-Thread building script for bridge + +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/fh8620/platform/fh8620/arch.h b/bsp/fh8620/platform/fh8620/arch.h new file mode 100644 index 0000000000000000000000000000000000000000..df0e7f1965c5f00855c69cf6e29a4d59bc39b6bf --- /dev/null +++ b/bsp/fh8620/platform/fh8620/arch.h @@ -0,0 +1,117 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef ARCH_H_ +#define ARCH_H_ + + +/*****************************/ +/* BSP CONTROLLER BASE */ +/*****************************/ +#define INTC_REG_BASE (0xE0200000) +#define SDC0_REG_BASE (0xE2000000) +#define SDC1_REG_BASE (0xE2100000) +#define TVE_REG_BASE (0xE8000000) +#define VOU_REG_BASE (0xE8100000) +#define AES_REG_BASE (0xE8200000) +#define JPEG_REG_BASE (0xE8300000) +#define ISPB_REG_BASE (0xEA000000) +#define ISPF_REG_BASE (0xEA100000) +#define VPU_REG_BASE (0xEC000000) +#define VCU_REG_BASE (0xEC100000) +#define DDRC_REG_BASE (0xED000000) +#define DMAC_REG_BASE (0xEE000000) +#define GMAC_REG_BASE (0xEF000000) +#define PMU_REG_BASE (0xF0000000) +#define I2C0_REG_BASE (0xF0200000) +#define GPIO0_REG_BASE (0xF0300000) +#define GPIO1_REG_BASE (0xf4000000) +#define PWM_REG_BASE (0xF0400000) +#define SPI0_REG_BASE (0xF0500000) +#define SPI1_REG_BASE (0xF0600000) +#define UART0_REG_BASE (0xF0700000) +#define UART1_REG_BASE (0xF0800000) +#define I2S_REG_BASE (0xF0900000) +#define ACODEC_REG_BASE (0xF0A00000) +#define I2C1_REG_BASE (0xF0B00000) +#define TMR_REG_BASE (0xF0C00000) +#define WDT_REG_BASE (0xF0D00000) +#define DPHY_REG_BASE (0xF1000000) +#define MIPIC_REG_BASE (0xF1100000) +#define SADC_REG_BASE (0xF1200000) + + + + + +typedef enum IRQn +{ + PAE_IRQn = 0, + VPU_IRQn = 1, + ISP_F_IRQn = 2, + ISP_B_IRQn = 3, + VOU_IRQn = 4, + JPEG_IRQn = 5, + TVE_IRQn = 6, + TOE_IRQn = 7, + DDRC_IRQn = 8, + DMAC_IRQn = 9, + AES_IRQn = 10, + MIPIC_IRQn = 11, + MIPI_WRAP_IRQn = 12, + PMU_IRQn = 13, + EMAC_IRQn = 14, + AXIC0_IRQn = 16, + AXIC1_IRQn = 17, + X2H0_IRQn = 18, + X2H1_IRQn = 19, + AHBC0_IRQn = 20, + AHBC1_IRQn = 21, + SADC_IRQn = 23, + SDC0_IRQn = 24, + SDC1_IRQn = 25, + ACW_IRQn = 26, + WDT_IRQn = 27, + SPI0_IRQn = 28, + SPI1_IRQn = 29, + UART0_IRQn = 30, + UART1_IRQn = 31, + I2S0_IRQn = 32, + I2S1_IRQn = 33, + RTC_IRQn = 34, + PWM_IRQn = 35, + TMR0_IRQn = 36, + TMR1_IRQn = 37, + USB0_IRQn = 38, + USB1_IRQn = 39, + GPIO0_IRQn = 40, + GPIO1_IRQn = 41, + I2C0_IRQn = 42, + I2C1_IRQn = 43, + +} IRQn_Type; + +#endif /* ARCH_H_ */ diff --git a/bsp/fh8620/platform/fh8620/iot_cam/SConscript b/bsp/fh8620/platform/fh8620/iot_cam/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..7f5236ef88a463f0b330d5090ada57fe38e9ac9e --- /dev/null +++ b/bsp/fh8620/platform/fh8620/iot_cam/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +path = [cwd, cwd + '/..'] + +group = DefineGroup('Platform', src, depend = ['CONFIG_BOARD_IOTCAM', 'CONFIG_CHIP_FH8620'], CPPPATH = path) + +Return('group') diff --git a/bsp/fh8620/platform/fh8620/iot_cam/board.c b/bsp/fh8620/platform/fh8620/iot_cam/board.c new file mode 100644 index 0000000000000000000000000000000000000000..f7c9ebdcb1b8093bbb0062545bcc48baca6d60e4 --- /dev/null +++ b/bsp/fh8620/platform/fh8620/iot_cam/board.c @@ -0,0 +1,692 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +/***************************************************************************** + * Include Section + * add all #include here + *****************************************************************************/ +#include +#include "fh_def.h" +#include "arch.h" +#include "board_info.h" +#include "inc/fh_driverlib.h" +#include "iomux.h" +#include "fh_pmu.h" +#include "spi_fh_adapt.h" + +#ifdef RT_USING_SADC +#include "sadc.h" +#endif + +#ifdef RT_USING_SPI +#include "ssi.h" +#include "fh_dma.h" +#endif + +#ifdef RT_USING_ENC28J60 +#include "enc28j60.h" +#include "gpio.h" +#endif + +#ifndef HW_WIFI_POWER_GPIO + #define HW_WIFI_POWER_GPIO 47 // wifi power on +#endif +#ifndef HW_WIFI_POWER_GPIO_ON_LEVEL + #define HW_WIFI_POWER_GPIO_ON_LEVEL 0 +#endif + +#ifndef HW_CIS_RST_GPIO + #define HW_CIS_RST_GPIO 45 // cis(sensor) reset +#endif +#ifndef HW_CIS_RST_GPIO_LEVEL + #define HW_CIS_RST_GPIO_LEVEL 1 +#endif + + +#ifndef HW_SDCARD_POWER_GPIO + #define HW_SDCARD_POWER_GPIO 63 //not used +#endif +/**************************************************************************** + * ADT section + * add definition of user defined Data Type that only be used in this file here + ***************************************************************************/ +struct st_platform_info { + char *name; + void *private_data; +}; + +/****************************************************************************** + * Function prototype section + * add prototypes for all functions called by this file,execepting those + * declared in header file + *****************************************************************************/ + +/***************************************************************************** + * Global variables section - Exported + * add declaration of global variables that will be exported here + * e.g. + * int8_t foo; + ****************************************************************************/ + +/***************************************************************************** + * Global variables section - Local + * define global variables(will be refered only in this file) here, + * static keyword should be used to limit scope of local variable to this file + * e.g. + * static uint8_t ufoo; + *****************************************************************************/ + +void fh_mmc_reset(struct fh_mmc_obj *mmc_obj) +{ + rt_uint32_t value; + if (mmc_obj->id) + fh_pmu_write(REG_PMU_SWRST_AHB_CTRL, 0xfffffffd); + else + fh_pmu_write(REG_PMU_SWRST_AHB_CTRL, 0xfffffffb); + do { + fh_pmu_read(REG_PMU_SWRST_AHB_CTRL, &value); + } while (value != 0xffffffff); +} + +static struct fh_mmc_obj mmc0_obj = +{ + .id = 0, + .irq = SDC0_IRQn, + .base = SDC0_REG_BASE, + .power_pin_gpio = HW_SDCARD_POWER_GPIO, + .mmc_reset = fh_mmc_reset, +}; + +static struct fh_mmc_obj mmc1_obj = +{ + .id = 1, + .irq = SDC1_IRQn, + .base = SDC1_REG_BASE, + .power_pin_gpio = HW_WIFI_POWER_GPIO, + .mmc_reset = fh_mmc_reset, +}; + +#ifdef RT_USING_SPI +#define SPI0_CLK_IN (50000000) +#define SPI0_MAX_BAUD (SPI0_CLK_IN/2) + +static struct spi_control_platform_data spi0_platform_data = +{ + .id = 0, + .irq = SPI0_IRQn, + .base = SPI0_REG_BASE, + .max_hz = SPI0_MAX_BAUD, + .slave_no = FH_SPI_SLAVE_MAX_NO, + .clk_in = SPI0_CLK_IN, + .rx_hs_no = SPI0_RX, + .tx_hs_no = SPI0_TX, + .dma_name = "fh81_dma", + .transfer_mode = SPI0_TRANSFER_MODE, + .plat_slave[0].cs_pin = SPI_CRTOLLER0_SLAVE0_CS, + .plat_slave[0].actice_level = ACTIVE_LOW, + .plat_slave[1].cs_pin = SPI_CRTOLLER0_SLAVE1_CS, + .plat_slave[1].actice_level = ACTIVE_LOW, +}; + + +#define SPI1_CLK_IN (50000000) +#define SPI1_MAX_BAUD (SPI1_CLK_IN/2) + +static struct spi_control_platform_data spi1_platform_data = +{ + .id = 1, + .irq = SPI1_IRQn, + .base = SPI1_REG_BASE, + .max_hz = SPI1_MAX_BAUD, + .slave_no = FH_SPI_SLAVE_MAX_NO, + .clk_in = SPI1_CLK_IN, + .rx_hs_no = SPI1_RX, + .tx_hs_no = SPI1_TX, + .dma_name = "fh81_dma", + .transfer_mode = SPI1_TRANSFER_MODE, + .plat_slave[0].cs_pin = SPI_CRTOLLER1_SLAVE0_CS, + .plat_slave[0].actice_level = ACTIVE_LOW, + .plat_slave[1].cs_pin = SPI_CRTOLLER1_SLAVE1_CS, + .plat_slave[1].actice_level = ACTIVE_LOW, +}; +#endif + +static struct fh_i2c_obj i2c0_obj = +{ + .id = 0, + .irq = I2C0_IRQn, + .base = I2C0_REG_BASE, +}; + +static struct fh_i2c_obj i2c1_obj = +{ + .id = 1, + .irq = I2C1_IRQn, + .base = I2C1_REG_BASE, +}; + +static struct fh_gpio_obj gpio0_obj = +{ + .id = 0, + .irq = GPIO0_IRQn, +}; + +static struct fh_gpio_obj gpio1_obj = +{ + .id = 1, + .irq = GPIO1_IRQn, +}; + +static struct fh_pwm_obj pwm_obj = +{ + .id = 0, + .base = PWM_REG_BASE, +}; + +static struct fh_wdt_obj wdt_obj = +{ + .id = 0, + .base = WDT_REG_BASE, + .irq = WDT_IRQn, +}; + + +#ifdef RT_USING_SADC +static struct wrap_sadc_obj sadc_obj = +{ + .id = 0, + .regs = (void *)SADC_REG_BASE, + .irq_no = SADC_IRQn, + .sample_mode = ISR_MODE, +}; +#endif + + +static struct mtd_partition fh_sf_parts[] = { + { + /* head & Ramboot */ + .name = "bootstrap", + .offset = 0, + .size = 0x4000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + /* isp param */ + .name = "isp-param", + .offset = 0x4000, + .size = 0x4000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + /* pae param */ + .name = "pae-param", + .offset = 0x8000, + .size = 0x8000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + /* Uboot SPL */ + .name = "uboot-spl", + .offset = 0x10000, + .size = 0x10000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + /* U-Boot environment */ + .name = "uboot-env", + .offset = 0x20000, + .size = 0x10000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + /* U-Boot */ + .name = "uboot", + .offset = 0x30000, + .size = 0x30000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "kernel", + .offset = 0x60000, + .size = 0x400000, + .mask_flags = 0, + }, { + .name = "rootfs", + .offset = 0x460000, + .size = 0x300000, + .mask_flags = 0, + }, { + .name = "app", + .offset = 0x760000, + .size = 0x8a0000, + .mask_flags = 0, + }//mtdparts=spi0.0:64k(bootstrap),64k(u-boot-env),192k(u-boot),4M(kernel),8M(rootfs),-(app) + /* two blocks with bad block table (and mirror) at the end */ +}; + + +static struct flash_platform_data fh_flash_platform_data = +{ + .flash_name = "fh_flash", + .spi_name = "ssi0_0", + .parts = fh_sf_parts, + .nr_parts = ARRAY_SIZE(fh_sf_parts), +}; + +struct st_platform_info plat_mmc0 = +{ + .name = "mmc", + .private_data = &mmc0_obj, +}; + + +struct st_platform_info plat_mmc1 = +{ + .name = "mmc", + .private_data = &mmc1_obj, +}; +#ifdef RT_USING_SPI +struct st_platform_info plat_spi0 = +{ + .name = "spi", + .private_data = &spi0_platform_data, +}; + +struct st_platform_info plat_spi1 = +{ + .name = "spi", + .private_data = &spi1_platform_data, +}; +#endif + +struct st_platform_info plat_flash = +{ + .name = "fh_flash", + .private_data = &fh_flash_platform_data, +}; + +struct st_platform_info plat_i2c0 = +{ + .name = "i2c", + .private_data = &i2c0_obj, +}; + +struct st_platform_info plat_i2c1 = +{ + .name = "i2c", + .private_data = &i2c1_obj, +}; + +struct st_platform_info plat_gpio0 = +{ + .name = "gpio", + .private_data = &gpio0_obj, +}; + +struct st_platform_info plat_gpio1 = +{ + .name = "gpio", + .private_data = &gpio1_obj, +}; + +struct st_platform_info plat_pwm = +{ + .name = "pwm", + .private_data = &pwm_obj, +}; + +struct st_platform_info plat_wdt = +{ + .name = "wdt", + .private_data = &wdt_obj, +}; +#ifdef RT_USING_SADC +struct st_platform_info plat_sadc = +{ + .name = "sadc", + .private_data = &sadc_obj, +}; +#endif + +const static struct st_platform_info *platform_info[] = { + &plat_mmc0, + //&plat_mmc1,//by PeterJiang, wifi don't use SDIO framework... +#ifdef RT_USING_SPI + &plat_spi0, +#endif +#ifdef RT_USING_SPI1 + &plat_spi1, +#endif + &plat_flash, + &plat_i2c0, + &plat_i2c1, + &plat_gpio0, + &plat_gpio1, + &plat_pwm, + &plat_wdt, +#ifdef RT_USING_SADC + &plat_sadc, +#endif +}; + + + /* function body */ + +/***************************************************************************** + * Description: + * add funtion description here + * Parameters: + * description for each argument, new argument starts at new line + * Return: + * what does this function returned? + *****************************************************************************/ + + void clock_init(void) + { + //UINT32 reg; + //gate enable, spi0, gmac, uart0, timer0, wdt, pts +#ifdef YG_TEK + fh_pmu_write_mask(REG_PMU_PAD_MAC_TXER_CFG, 0x100000, 0x100000); +#endif + //SPI0 + fh_pmu_write_mask(REG_PMU_CLK_DIV3, 0xb, 0xff); + + //GMAC + fh_pmu_write_mask(REG_PMU_CLK_DIV6, 0x5000000, 0xf000000); + + //UART0 + fh_pmu_write_mask(REG_PMU_CLK_DIV4, 0x1, 0xf); + + //TIMER0 + fh_pmu_write_mask(REG_PMU_CLK_DIV5, 0x1d0000, 0x3f0000); + + //PTS + fh_pmu_write_mask(REG_PMU_CLK_DIV2, 0x23, 0x3f); + + //WDT + //fh_pmu_write_mask(REG_PMU_CLK_DIV5, 0x1d00, 0x3f00); + fh_pmu_write_mask(REG_PMU_CLK_DIV5, 0x3500, 0x3f00); + + //clock enable + fh_pmu_write_mask(REG_PMU_CLK_GATE, 0, 0x720ba080); + + //sd0_drv_sel + fh_pmu_write_mask(REG_PMU_CLK_SEL, 0x200000, 0x300000); + //sd0_sample_sel + fh_pmu_write_mask(REG_PMU_CLK_SEL, 0x00000, 0x30000); + + //sd1_drv_sel + fh_pmu_write_mask(REG_PMU_CLK_SEL, 0x2000, 0x3000); + //sd1_sample_sel + fh_pmu_write_mask(REG_PMU_CLK_SEL, 0x000, 0x300); + + } + + +void fh_platform_info_register(void){ + struct fh_board_info *test_info; + int i; + + for(i=0;iname,platform_info[i]->private_data); + if(!test_info){ + rt_kprintf("info_name(%s) failed registered\n", platform_info[i]->name); + } + } +} + +void rt_hw_board_init() +{ + /* initialize the system clock */ + rt_hw_clock_init(); + //add iomux init 2015-3-11 by yu.zhang for fh81(fullhan) + //iomux_init(); + fh_iomux_init(PMU_REG_BASE + 0x5c); + //add clk init 2015-3-11 by yu.zhang for fh81(fullhan) + clock_init(); + /* initialize uart */ + rt_hw_uart_init(); + rt_console_set_device(RT_CONSOLE_DEVICE_NAME); + /* initialize timer1 */ + rt_hw_timer_init(); + //board data info init... + fh_board_info_init(); + fh_platform_info_register(); + +} + +void rt_board_driver_init(){ + + //add board init lock here... + /*rt_show_version();*/ + int ret; + +/* Filesystem Initialization */ +#ifdef RT_USING_DFS + { + /* init the device filesystem */ + dfs_init(); + rt_kprintf("DFS initialized!\n"); +#if defined(RT_USING_DFS_ELMFAT) + /* init the elm chan FatFs filesystam*/ + elm_init(); + rt_kprintf("ELM initialized!\n"); +#endif + +#if defined(RT_USING_DFS_ROMFS) + dfs_romfs_init(); + if (dfs_mount(RT_NULL, "/rom", "rom", 0, &romfs_root) == 0) + { + rt_kprintf("ROM File System initialized!\n"); + } + else + rt_kprintf("ROM File System initialzation failed!\n"); +#endif + +#if defined(RT_USING_DFS_DEVFS) + devfs_init(); + if (dfs_mount(RT_NULL, "/dev", "devfs", 0, 0) == 0) + rt_kprintf("Device File System initialized!\n"); + else + rt_kprintf("Device File System initialzation failed!\n"); + + #ifdef RT_USING_NEWLIB + /* init libc */ + libc_system_init(RT_CONSOLE_DEVICE_NAME); + #endif +#endif + +#if defined(RT_USING_DFS_UFFS) + { + /* init the uffs filesystem */ + dfs_uffs_init(); + + /* mount flash device as flash directory */ + if(dfs_mount("nand0", "/nand0", "uffs", 0, 0) == 0) + rt_kprintf("UFFS File System initialized!\n"); + else + rt_kprintf("UFFS File System initialzation failed!\n"); + } +#endif + + +#ifdef RT_USING_DFS_RAMFS + dfs_ramfs_init(); + { + rt_uint8_t *ramfs_pool = RT_NULL; + struct dfs_ramfs* ramfs; + ramfs_pool = rt_malloc(0x800000); + if(ramfs_pool) + { + ramfs =(struct dfs_ramfs*) dfs_ramfs_create((rt_uint8_t*)ramfs_pool, 0x800000); + if (ramfs != RT_NULL) + { + if (dfs_mount(RT_NULL, "/", "ram", 0, ramfs) == 0) + { + rt_kprintf("Mount RAMDisk done!\n"); + } + else + { + rt_kprintf("Mount RAMDisk failed.\n"); + } + } + } + else + { + rt_kprintf("alloc ramfs poll failed\n"); + } + } +#endif + } +#endif +/* Filesystem Initialization end*/ + +#ifdef RT_USING_GPIO + { + rt_hw_gpio_init(); + rt_kprintf("GPIO initialized!\n"); + +#ifdef RT_USING_SDIO + //wifi + gpio_request(HW_WIFI_POWER_GPIO); + gpio_direction_output(HW_WIFI_POWER_GPIO, !HW_WIFI_POWER_GPIO_ON_LEVEL); + udelay(1000); + gpio_direction_output(HW_WIFI_POWER_GPIO, HW_WIFI_POWER_GPIO_ON_LEVEL); + //micro sd + gpio_request(HW_SDCARD_POWER_GPIO); + gpio_direction_output(HW_SDCARD_POWER_GPIO, 0); + rt_kprintf("SDIO initialized!\n"); +#endif + //sensor + gpio_request(HW_CIS_RST_GPIO); + gpio_direction_output(HW_CIS_RST_GPIO, HW_CIS_RST_GPIO_LEVEL); + + + } +#endif + +#ifdef RT_USING_SDIO +#ifndef RT_USING_WIFI_MARVEL + rt_mmcsd_core_init(); + rt_kprintf("MMC CORE initialized!\n"); + rt_mmcsd_blk_init(); + rt_kprintf("MMC BLK initialized!\n"); + rt_hw_mmc_init(); + rt_kprintf("MMC initialized!\n"); + rt_thread_delay(RT_TICK_PER_SECOND*2); + /* mount sd card fat partition 1 as root directory */ + #ifdef RT_USING_DFS_ELMFAT + if (dfs_mount("sd0", "/", "elm", 0, 0) == 0) + { + rt_kprintf("File System initialized!\n"); + } + else + rt_kprintf("File System initialization failed!\n"); + #endif +#endif +#endif + + +#ifdef RT_USING_FH_DMA + { + rt_fh_dma_init(); + rt_kprintf("DMA initialized!\n"); + } +#endif + + + +#ifdef RT_USING_FH_ACW + { + fh_audio_init(); + rt_kprintf("AUDIO initialized!\n"); + } +#endif + +#ifdef RT_USING_LWIP + { + /* init lwip system */ + lwip_sys_init(); + rt_kprintf("LWIP SYS initialized!\n"); + eth_system_device_init(); + rt_kprintf("ETH initialized!\n"); + } +#endif + +#ifdef RT_USING_GMAC + /* register ethernetif device */ + rt_app_fh_gmac_init(); + rt_kprintf("GMAC initialized!\n"); +#endif + + +#ifdef RT_USING_I2C + { + rt_hw_i2c_init(); + rt_kprintf("I2C initialized!\n"); + } +#endif + +#ifdef RT_USING_PWM + { + rt_hw_pwm_init(); + rt_kprintf("PWM initialized!\n"); +} +#endif + +#ifdef RT_USING_WDT + { + rt_hw_wdt_init(); + rt_kprintf("WDT initialized!\n"); +} +#endif + + +#ifdef RT_USING_SPI + { + rt_hw_spi_init(); + rt_kprintf("SPI initialized!\n"); + } +#endif + + +#ifdef RT_USING_FH_FLASH_ADAPT + fh_flash_adapt_init(); + rt_kprintf("FLASH initialized!\n"); +#endif + + rt_kprintf("init done\n"); +#ifdef RT_USING_SADC + rt_hw_sadc_init(); + rt_kprintf("SADC initialized!\n"); +#endif + +#ifdef RT_USING_ENC28J60 + gpio_request(ENC28J60_INT); + gpio_direction_input(ENC28J60_INT); + gpio_set_irq_type(ENC28J60_INT, IRQ_TYPE_EDGE_FALLING); + rt_hw_interrupt_install(gpio_to_irq(ENC28J60_INT), (void *)enc28j60_isr, RT_NULL, RT_NULL); + gpio_irq_enable(gpio_to_irq(ENC28J60_INT)); + gpio_release(ENC28J60_INT); + + enc28j60_attach(ENC28J60_SPI_DEV); +#endif +} + diff --git a/bsp/fh8620/platform/fh8620/iot_cam/board_def.h b/bsp/fh8620/platform/fh8620/iot_cam/board_def.h new file mode 100644 index 0000000000000000000000000000000000000000..48551b7095ed7fe3567afd294903f321dc6dcbd2 --- /dev/null +++ b/bsp/fh8620/platform/fh8620/iot_cam/board_def.h @@ -0,0 +1,106 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef BOARD_DEF_H_ +#define BOARD_DEF_H_ + + +/* *********************** + * SECTION: DRIVE + * ***********************/ +// Basic drive.. +#define RT_USING_UART1 +#define RT_USING_GPIO +#define RT_USING_SDIO +#define RT_USING_FH_DMA +#define RT_USING_FH_ACW +#define RT_USING_I2C +#define RT_USING_PWM +#define RT_USING_WDT +#define RT_USING_SPI +#define RT_USING_SADC + +#define RT_USING_DSP +#define RT_USING_ISP + +#define CONFIG_PLAT_V2 + +#ifndef FH_DDR_START +#define FH_DDR_START 0xA0000000 +#define FH_DDR_END 0xA1000000 + +#define FH_RTT_OS_MEM_SIZE 0x00600000 +#define FH_DMA_MEM_SIZE 0x20000 /* 128k */ + +#define FH_RTT_OS_MEM_END (FH_DDR_START + FH_RTT_OS_MEM_SIZE) +#define FH_SDK_MEM_START (FH_RTT_OS_MEM_END + FH_DMA_MEM_SIZE) +#define FH_RTT_OS_HEAP_END FH_SDK_MEM_START +#define FH_SDK_MEM_SIZE (FH_DDR_END - FH_SDK_MEM_START) +#endif /* end of FH_DDR_START*/ + +/* *********************** + * SECTION: DRIVE COMPONENT + * ***********************/ +#define UART_NAME "uart1" +#define RT_USING_DMA_MEM + +#define RT_USING_MCI0 +#define RT_USING_GD +#define RT_USING_FLASH_DEFAULT +#define RT_USING_FH_FLASH_ADAPT + + +#ifndef _FH_AUTO_CONFIG_ +#define CHANNUM 2 +#define CHN0_Height 720 +#define CHN0_WIDTH 1280 +#define CHN0_Framerate 0x00010019 +#define CHN0_Bitrate (2048 *4) + + +#define CHN1_Framerate 0x00010019 +#define CHN1_WIDTH 352 +#define CHN1_Height 288 +#define CHN1_Bitrate 512 + + +#define VPU_FRAME_CTRL 1 +#define CONFIG_IRCUT_OFF 1 +#define CONFIG_ISP_25FPS 1 +#define NOT_RECORD 1 +#define ENABLE_JPEG 1 +#define CONFIG_ISP_720P 1 +#define ENABLE_COOLVIEW 1 +#define ENABLE_OSD 1 +#define USE_RC 1 +#define OSD_CHN_FONT_USING 1 +#endif + + + + + +#endif /* BOARD_H_ */ diff --git a/bsp/fh8620/platform/fh8620/iot_cam/iomux.c b/bsp/fh8620/platform/fh8620/iot_cam/iomux.c new file mode 100644 index 0000000000000000000000000000000000000000..ee0bb632609b45e35901001b283e54f5fb1eeaf0 --- /dev/null +++ b/bsp/fh8620/platform/fh8620/iot_cam/iomux.c @@ -0,0 +1,668 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "rtdef.h" +#include "iomux.h" +#include "rtconfig.h" + + +Iomux_Pad fh_iomux_cfg[] = { + { + .func_name = { "RESETN", "", "", "", }, + .reg_type = 9, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = -1, + }, + { + .func_name = { "TEST", "", "", "", }, + .reg_type = 9, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = -1, + }, + { + .func_name = { "CIS_CLK", "", "", "", }, + .reg_type = 5, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "CIS_HSYNC", "GPIO20", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_VSYNC", "GPIO21", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_PCLK", "", "", "", }, + .reg_type = 9, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 0, + }, + { + .func_name = { "CIS_D0", "GPIO22", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D1", "GPIO23", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D2", "GPIO24", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D3", "GPIO25", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D4", "GPIO26", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D5", "GPIO27", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D6", "GPIO28", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D7", "GPIO29", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D8", "GPIO30", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D9", "GPIO31", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D10", "GPIO32", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_D11", "GPIO33", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_REF_CLK", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 3, + }, + { + .func_name = { "MAC_MDC", "GPIO34", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 0, + }, + { + .func_name = { "MAC_MDIO", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_COL", "GPIO35", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_CRS", "GPIO36", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_RXCK", "", "", "", }, + .reg_type = 9, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = -1, + }, + { + .func_name = { "MAC_RXD0", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = -1, + }, + + { + .func_name = { "MAC_RXD1", "GPIO38", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_RXD2", "GPIO39", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_RXD3", "GPIO40", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_RXDV", "GPIO41", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_TXCK", "", "", "", }, + .reg_type = 9, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = -1, + }, + { + .func_name = { "MAC_TXD0", "GPIO42", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_TXD1", "GPIO43", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_TXD2", "GPIO44", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_TXD3", "GPIO45", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_TXEN", "GPIO46", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "MAC_RXER", "GPIO47", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "GPIO0", "ARC_JTAG_TCK", "GPIO0", "CIS_SSI0_CSN1", }, + .reg_type = 21, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "GPIO1", "ARC_JTAG_TRSTN", "GPIO1", "CIS_SSI0_RXD", }, + .reg_type = 21, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "GPIO2", "ARC_JTAG_TMS", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "GPIO3", "ARC_JTAG_TDI", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "GPIO4", "ARC_JTAG_TDO", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "JTAG_TCK", "GPIO5", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "JTAG_TRSTN", "GPIO6", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "JTAG_TMS", "GPIO7", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "JTAG_TDI", "GPIO8", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "JTAG_TDO", "GPIO9", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "GPIO10", "UART1_OUT", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 1, + }, + { + .func_name = { "GPIO11", "UART1_IN", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 1, + }, + { + .func_name = { "GPIO12", "PWM_OUT0", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "GPIO13", "PWM_OUT1", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "GPIO14", "PWM_OUT2", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "UART0_IN", "GPIO48", "UART0_IN", " I2S_WS", }, + .reg_type = 21, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 1, + }, + { + .func_name = { "UART0_OUT", "GPIO49", "UART0_OUT", "I2S_CLK", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "CIS_SCL", "GPIO56", "CIS_SCL", "CIS_SSI0_CLK", }, + .reg_type = 13, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "CIS_SDA", "GPIO57", "CIS_SDA", "CIS_SSI0_TXD", }, + .reg_type = 13, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "SCL1", "GPIO50", "SCL1", "I2S_DI", }, + .reg_type = 21, + .func_sel = 1, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "SDA1", "GPIO51", "I2S_DO", "", }, + .reg_type = 21, + .func_sel = 1, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "SSI0_CLK", "", "", "", }, + .reg_type = 5, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "SSI0_TXD", "", "", "", }, + .reg_type = 5, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "SSI0_CSN0", "GPIO54", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "SSI0_CSN1", "GPIO55", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "SSI0_RXD", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = -1, + }, + { + .func_name = { "SD0_CD", "GPIO52", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "SD0_WP", "GPIO53", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "SD0_CLK", "", "", "", }, + .reg_type = 5, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 3, + }, + { + .func_name = { "SD0_CMD_RSP", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 3, + }, + { + .func_name = { "SD0_DATA0", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 3, + }, + { + .func_name = { "SD0_DATA1", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 2, + }, + { + .func_name = { "SD0_DATA2", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 3, + }, + { + .func_name = { "SD0_DATA3", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 3, + }, + { + .func_name = { "SD1_CLK", "SSI1_CLK", "", "", }, + .reg_type = 8, + .func_sel = 0, + .pupd = IOMUX_PUPD_NONE, + .drv_cur = 1, + }, + { + .func_name = { "SD1_CD", "GPIO_58", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "SD1_WP", "GPIO_59", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, + { + .func_name = { "SD1_DATA0", "SSI1_TXD", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 3, + }, + { + .func_name = { "SD1_DATA1", "SSI1_CSN0", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 3, + }, + { + .func_name = { "SD1_DATA2", "SSI1_CSN1", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 3, + }, + { + .func_name = { "SD1_DATA3", "", "", "", }, + .reg_type = 17, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 3, + }, + { + .func_name = { "SD1_CMD_RSP", "SSI1_RXD", "", "", }, + .reg_type = 20, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = 3, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "CLK_SW0", "", "", "", }, + .reg_type = 9, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = -1, + }, + { + .func_name = { "CLK_SW1", "", "", "", }, + .reg_type = 9, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = -1, + }, + { + .func_name = { "CLK_SW2", "", "", "", }, + .reg_type = 9, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = -1, + }, + { + .func_name = { "CLK_SW3", "", "", "", }, + .reg_type = 9, + .func_sel = 0, + .pupd = IOMUX_PUPD_UP, + .drv_cur = -1, + }, + { + .func_name = { "RESERVED", "", "", "", }, + .reg_type = 20, + .func_sel = 0, + }, + { + .func_name = { "MAC_TXER", "GPIO37", "", "", }, + .reg_type = 20, + .func_sel = 1, + .pupd = IOMUX_PUPD_DOWN, + .drv_cur = 1, + }, +}; + + +const int fh_iomux_cfg_count = ARRAY_SIZE(fh_iomux_cfg); diff --git a/bsp/fh8620/platform/fh8620/iot_cam/readme b/bsp/fh8620/platform/fh8620/iot_cam/readme new file mode 100644 index 0000000000000000000000000000000000000000..13a46f4c47e016607abb308c98e67a90995b90eb --- /dev/null +++ b/bsp/fh8620/platform/fh8620/iot_cam/readme @@ -0,0 +1,4 @@ +IoT Camera +powered by RT-Thread +V1.0 +2016-3-14 \ No newline at end of file diff --git a/bsp/fh8620/platform/fh8620/iot_cam/startup.c b/bsp/fh8620/platform/fh8620/iot_cam/startup.c new file mode 100644 index 0000000000000000000000000000000000000000..52c4775da59f8edcb085f265c7617ad899f3feab --- /dev/null +++ b/bsp/fh8620/platform/fh8620/iot_cam/startup.c @@ -0,0 +1,123 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include + +#ifdef RT_USING_FINSH +#include +#endif + +#ifdef RT_USING_DMA_MEM +#include "dma_mem.h" +#endif + +#include "board_def.h" + +extern void rt_hw_interrupt_init(void); +extern void rt_hw_board_init(void); +extern void rt_system_timer_init(void); +extern void rt_system_scheduler_init(void); +extern void rt_thread_idle_init(void); +extern void mmu_invalidate_icache(); +extern void rt_hw_cpu_icache_enable(void); +extern void rt_show_version(void); +extern void rt_system_heap_init(void*, void*); +extern void rt_hw_finsh_init(void); +extern void rt_application_init(void); + +static struct mem_desc fh_mem_desc[] = +{ + { 0xA0000000, FH_RTT_OS_MEM_END-1, 0xA0000000, SECT_RWX_CB, 0, SECT_MAPPED }, + { FH_RTT_OS_MEM_END, FH_DDR_END-1, FH_RTT_OS_MEM_END, SECT_RWNX_NCNB, 0, SECT_MAPPED }, + { 0xFFFF0000, 0xFFFF1000-1, 0xA0000000, SECT_TO_PAGE, PAGE_ROX_CB, PAGE_MAPPED }, /* isr vector table */ + { 0xE0000000, 0xF1300000-1, 0xE0000000, SECT_RWNX_NCNB, 0, SECT_MAPPED }, /* io table */ + { 0xF4000000, 0xF4100000-1, 0xF4000000, SECT_RWNX_NCNB, 0, SECT_MAPPED }, /* GPIO#1 io table */ +}; + +rt_uint8_t _irq_stack_start[1024]; +rt_uint8_t _fiq_stack_start[1024]; +rt_uint8_t _undefined_stack_start[512]; +rt_uint8_t _abort_stack_start[512]; +rt_uint8_t _svc_stack_start[4096] SECTION(".nobss"); +extern unsigned char __bss_start; +extern unsigned char __bss_end; + +/** + * This function will startup RT-Thread RTOS. + */ +void rtthread_startup(void) +{ + /* disable interrupt first */ + rt_hw_interrupt_disable(); + /* initialize hardware interrupt */ + rt_hw_interrupt_init(); + + /* initialize mmu */ + rt_hw_mmu_init(fh_mem_desc, sizeof(fh_mem_desc)/sizeof(fh_mem_desc[0])); + + rt_system_heap_init((void*)&__bss_end, (void*)FH_RTT_OS_MEM_END); + +#ifdef RT_USING_DMA_MEM + //just use the last 100KB + fh_dma_mem_init((rt_uint32_t *)FH_RTT_OS_MEM_END, FH_DMA_MEM_SIZE); +#endif + + /* initialize board */ + rt_hw_board_init(); + + /* show version */ + rt_show_version(); + + /* initialize tick */ + rt_system_tick_init(); + + /* initialize kernel object */ + rt_system_object_init(); + + /* initialize timer system */ + rt_system_timer_init(); + + /* initialize scheduler system */ + rt_system_scheduler_init(); + + /* initialize application */ + rt_application_init(); + + /* initialize system timer thread */ + rt_system_timer_thread_init(); + + /* initialize idle thread */ + rt_thread_idle_init(); + + /* start scheduler */ + rt_system_scheduler_start(); + + /* never reach here */ + + return ; +} diff --git a/bsp/fh8620/platform/fh_arch.h b/bsp/fh8620/platform/fh_arch.h new file mode 100644 index 0000000000000000000000000000000000000000..2cbe36718ad7674b65b6dc86ff5ffbeb58390c8c --- /dev/null +++ b/bsp/fh8620/platform/fh_arch.h @@ -0,0 +1,44 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_ARCH_H_ +#define FH_ARCH_H_ + + +#ifdef CONFIG_CHIP_FH8620 +#include "plat-v2/arch.h" +#endif + +#ifdef CONFIG_CHIP_FH8620G +#include "plat-v2/arch.h" +#endif + +#ifdef CONFIG_CHIP_FH8810 +#include "plat-v2/arch.h" +#endif + + +#endif /* FH_ARCH_H_ */ diff --git a/bsp/fh8620/platform/fh_def.h b/bsp/fh8620/platform/fh_def.h new file mode 100644 index 0000000000000000000000000000000000000000..57bedcb9e70c22de3e24c774292d0d7db57eb0d2 --- /dev/null +++ b/bsp/fh8620/platform/fh_def.h @@ -0,0 +1,90 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_DEF_H_ +#define FH_DEF_H_ + + + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) + +typedef char SINT8; +typedef short SINT16; +typedef int SINT32; +typedef long long SINT64; +typedef unsigned char UINT8; +typedef unsigned short UINT16; +typedef unsigned int UINT32; +typedef unsigned long long UINT64; + +#ifndef TYPE_DEFINED +typedef unsigned char uchar; +typedef signed char int8; +typedef unsigned char uint8; +typedef signed short int16; +typedef unsigned short uint16; +typedef signed int int32; +typedef unsigned int uint32; +typedef signed long long int64; +typedef unsigned long long uint64; +typedef float ieee_single; +typedef double ieee_double; + +typedef unsigned long boolean; + +#define TYPE_DEFINED + +#endif + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define reg_read(addr) (*((volatile UINT32 *)(addr))) +#define reg_write(addr,value) (*(volatile UINT32 *)(addr)=(value)) + +#define GET_REG(addr) reg_read(addr) +#define SET_REG(addr,value) reg_write(addr,value) +#define SET_REG_M(addr,value,mask) reg_write(addr,(reg_read(addr)&(~(mask)))|((value)&(mask))) +#define SET_REG_B(addr,element,highbit,lowbit) SET_REG_M((addr),((element)<<(lowbit)),(((1<<((highbit)-(lowbit)+1))-1)<<(lowbit))) + +#define GET_REG8(addr) (*((volatile UINT8 *)(addr))) +#define SET_REG8(addr,value) (*(volatile UINT8 *)(addr)=(value)) + +#define read_reg(addr) (*((volatile uint32 *)(addr))) +#define write_reg(addr, reg) (*((volatile uint32 *)(addr))) = (uint32)(reg) +#define inw(addr) (*((volatile uint32 *)(addr))) +#define outw(addr, reg) (*((volatile uint32 *)(addr))) = (uint32)(reg) +#ifndef BIT +#define BIT(nr) (1UL << (nr)) +#endif + +typedef volatile const unsigned int RoReg; /**< Read only 32-bit register (volatile const unsigned int) */ +typedef volatile unsigned int WoReg; /**< Write only 32-bit register (volatile unsigned int) */ +typedef volatile unsigned int RwReg; /**< Read-Write 32-bit register (volatile unsigned int) */ + + + +#endif /* FH_DEF_H_ */ diff --git a/bsp/fh8620/platform/plat-v2/SConscript b/bsp/fh8620/platform/plat-v2/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..40240950c0b6e637c32c2b3325ad43f1dcaea4dd --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/SConscript @@ -0,0 +1,13 @@ +Import('RTT_ROOT') +Import('rtconfig') +from building import * + +cwd = GetCurrentDir() + +src = Glob('*.c') + +path = [cwd, cwd + '/..'] + +group = DefineGroup('Platform', src, depend = ['CONFIG_PLAT_V2'], CPPPATH = path) + +Return('group') diff --git a/bsp/fh8620/platform/plat-v2/arch.h b/bsp/fh8620/platform/plat-v2/arch.h new file mode 100644 index 0000000000000000000000000000000000000000..bb32d96ea48a975b9fb662248b5ae3364d04ff90 --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/arch.h @@ -0,0 +1,116 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef ARCH_H_ +#define ARCH_H_ + +/*****************************/ +/* BSP CONTROLLER BASE */ +/*****************************/ +#define INTC_REG_BASE (0xE0200000) +#define SDC0_REG_BASE (0xE2000000) +#define SDC1_REG_BASE (0xE2100000) +#define TVE_REG_BASE (0xE8000000) +#define VOU_REG_BASE (0xE8100000) +#define AES_REG_BASE (0xE8200000) +/* +#define JPEG_REG_BASE (0xE8300000) +#define ISPB_REG_BASE (0xEA000000) +#define ISPF_REG_BASE (0xEA100000) +#define VPU_REG_BASE (0xEC000000) +#define VCU_REG_BASE (0xEC100000) +#define DDRC_REG_BASE (0xED000000) +*/ +#define DMAC_REG_BASE (0xEE000000) +#define GMAC_REG_BASE (0xEF000000) +#define PMU_REG_BASE (0xF0000000) +#define I2C0_REG_BASE (0xF0200000) +#define GPIO0_REG_BASE (0xF0300000) +#define GPIO1_REG_BASE (0xf4000000) +#define PWM_REG_BASE (0xF0400000) +#define SPI0_REG_BASE (0xF0500000) +#define SPI1_REG_BASE (0xF0600000) +#define UART0_REG_BASE (0xF0700000) +#define UART1_REG_BASE (0xF0800000) +/*#define I2S_REG_BASE (0xF0900000)*/ +#define ACODEC_REG_BASE (0xF0A00000) +#define I2C1_REG_BASE (0xF0B00000) +#define TMR_REG_BASE (0xF0C00000) +#define WDT_REG_BASE (0xF0D00000) +/* +#define DPHY_REG_BASE (0xF1000000) +#define MIPIC_REG_BASE (0xF1100000) +*/ +#define SADC_REG_BASE (0xF1200000) + +typedef enum IRQn +{ + PAE_IRQn = 0, + VPU_IRQn = 1, + ISP_F_IRQn = 2, + ISP_B_IRQn = 3, + VOU_IRQn = 4, + JPEG_IRQn = 5, + TVE_IRQn = 6, + TOE_IRQn = 7, + DDRC_IRQn = 8, + DMAC_IRQn = 9, + AES_IRQn = 10, + MIPIC_IRQn = 11, + MIPI_WRAP_IRQn = 12, + PMU_IRQn = 13, + EMAC_IRQn = 14, + AXIC0_IRQn = 16, + AXIC1_IRQn = 17, + X2H0_IRQn = 18, + X2H1_IRQn = 19, + AHBC0_IRQn = 20, + AHBC1_IRQn = 21, + SADC_IRQn = 23, + SDC0_IRQn = 24, + SDC1_IRQn = 25, + ACW_IRQn = 26, + WDT_IRQn = 27, + SPI0_IRQn = 28, + SPI1_IRQn = 29, + UART0_IRQn = 30, + UART1_IRQn = 31, + I2S0_IRQn = 32, + I2S1_IRQn = 33, + RTC_IRQn = 34, + PWM_IRQn = 35, + TMR0_IRQn = 36, + TMR1_IRQn = 37, + USB0_IRQn = 38, + USB1_IRQn = 39, + GPIO0_IRQn = 40, + GPIO1_IRQn = 41, + I2C0_IRQn = 42, + I2C1_IRQn = 43, + +} IRQn_Type; + +#endif /* ARCH_H_ */ diff --git a/bsp/fh8620/platform/plat-v2/clock.c b/bsp/fh8620/platform/plat-v2/clock.c new file mode 100644 index 0000000000000000000000000000000000000000..3b04d69ff300007441e92423c486c67666d8a168 --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/clock.c @@ -0,0 +1,2658 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "clock.h" +#include +#include "fh_arch.h" +#include "Libraries/inc/fh_timer.h" +#include "fh_pmu.h" +//#include "chip_reg.h" +//NEED_CAUTION. + +#define TIMER_CLOCK 1000000 +#define FH_CLK_DEBUG + +#define FH_CLK_DIV_DEFAULT +//#define FH_CLK_GATE_DEFAULT + +//#define FH_DBG_CLK + +#define FH_CLK_DIV_DEFAULT_VALUE 0x55aaaa55 +#define FH_CLK_GATE_DEFAULT_VALUE 0xaa5555aa + +#define CONFIG_PAE_PTS_CLOCK (1000000) +#define TICKS_PER_USEC (CONFIG_PAE_PTS_CLOCK / 1000000) +#define REG_PAE_PTS_REG (0xec100000 + 0x0040) + + + +#define fh_clk_err(p,fmt,args...)\ + rt_kprintf("clk_err: %s->\t"fmt,p->name, ##args) + +#ifdef FH_CLK_DEBUG +#define fh_clk_debug(p,fmt,args...)\ + rt_kprintf("%s:\t\t"fmt,p->name, ##args) + + +#define fh_clk_debug_no_handle(fmt,args...)\ + rt_kprintf(fmt, ##args) +#else +//#define fh_clk_err(p,fmt,args...) +#define fh_clk_debug(p,fmt,args...) +#define fh_clk_debug_no_handle(fmt,args...) +#endif + +struct fh_clk_tree; + +static struct fh_clk_tree fh_clk_tree; + + + +#define __raw_writel(v,a) (*(volatile unsigned int *)(a) = (v)) +#define __raw_readl(a) (*(volatile unsigned int *)(a)) + + + + +#define FH_TIMER_WRITEL(offset,value) __raw_writel(value,(fh_clk_tree.c_base_addr + offset)) +#define FH_TIMER_READL(offset) __raw_readl((fh_clk_tree.c_base_addr + offset)) + + + + + +enum clk_gate_enum{ +#define CLK_GATE (1) +#define CLK_UNGATE (0) + ISP_ACLK_GATE = (1<<0), + HCLK_GATE = (1<<1), + CPU_FCLK0_GATE = (1<<3), + VCU_CLK_GATE = (1<<4), + VOU_CLK_GATE = (1<<5), + MCLK_GATE = (1<<6), + SPI0_CLK_GATE = (1<<7), + SPI1_CLK_GATE = (1<<8), + SDC0_CLK_GATE = (1<<9), + SDC1_CLK_GATE = (1<<10), + AC_MCLK_GATE = (1<<11), ///// + I2C0_CLK_GATE = (1<<12), + UART0_CLK_GATE = (1<<13), + UART1_CLK_GATE = (1<<14), + //can't change + WDT_CLK_GATE = (1<<15), + + PWM_CLK_GATE = (1<<16), + TMR0_CLK_GATE = (1<<17), + TMR1_CLK_GATE = (1<<18), + PTS_CLK_GATE = (1<<19), + MIPI_DPHY_CLK20M_GATE = (1<<20), + MIPI_P32_CLK_GATE = (1<<21), + PIX_CLK_GATE = (1<<22), //// + CIS_CLK_OUT_GATE = (1<<23), + I2S_SCLK_GATE = (1<<24), ////// + ETH_REF_CLK_GATE = (1<<25), + SADC_CLK_GATE = (1<<26), + I2C1_CLK_GATE = (1<<27), + ETH_RX_CLK_GATE = (1<<28), ///// + ETH_TX_CLK_GATE = (1<<29), ///// + ETH_RMII_CLK_GATE = (1<<30),//// + + +}; + + + +//struct fh_clk; + +typedef void (*clk_update)(struct fh_clk* p_clk); + +//update func... +void clk_in_update(struct fh_clk* p_clk); +void pll1_clk_update(struct fh_clk* p_clk); +void pll0_clk_update(struct fh_clk* p_clk); +void cis_pclk_update(struct fh_clk* p_clk); +void ddr_clk_update(struct fh_clk* p_clk); +void ddr_clk_update(struct fh_clk* p_clk); +void fclk_update(struct fh_clk* p_clk); +void aclk_update(struct fh_clk* p_clk); +void hclk_update(struct fh_clk* p_clk); +void pclk_update(struct fh_clk* p_clk); +void isp_aclk_update(struct fh_clk* p_clk); +void vcu_clk_update(struct fh_clk* p_clk); +void vou_clk_update(struct fh_clk* p_clk); +void mipi_p32_clk_update(struct fh_clk* p_clk); +void cis_clk_out_update(struct fh_clk* p_clk); +void pts_update(struct fh_clk* p_clk); +void mipi_pix_clk_update(struct fh_clk* p_clk); +void spi0_clk_update(struct fh_clk* p_clk); +void spi1_clk_update(struct fh_clk* p_clk); +void mipi_dphy_clk20m_update(struct fh_clk* p_clk); +void i2c0_clk_update(struct fh_clk* p_clk); +void i2c1_clk_update(struct fh_clk* p_clk); +void uart0_clk_update(struct fh_clk* p_clk); +void uart1_clk_update(struct fh_clk* p_clk); +void pwm_clk_update(struct fh_clk* p_clk); +void time0_clk_update(struct fh_clk* p_clk); +void time1_clk_update(struct fh_clk* p_clk); +void sadc_clk_update(struct fh_clk* p_clk); +void sdc0_clk2x_update(struct fh_clk* p_clk); +void sdc0_clk_update(struct fh_clk* p_clk); +void sdc0_clk_out_update(struct fh_clk* p_clk); +void sdc0_clk_sample_update(struct fh_clk* p_clk); +void sdc0_clk_drv_update(struct fh_clk* p_clk); +void sdc1_clk2x_update(struct fh_clk* p_clk); +void sdc1_clk_update(struct fh_clk* p_clk); +void sdc1_clk_out_update(struct fh_clk* p_clkt); +void sdc1_clk_sample_update(struct fh_clk* p_clk); +void sdc1_clk_drv_update(struct fh_clk* p_clk); +void eth_ref_clk_update(struct fh_clk* p_clk); +void wdt_clk_update(struct fh_clk* p_clk); + +rt_int32_t check_pix_clk_source(rt_uint32_t offset,rt_uint32_t mask,rt_uint32_t *value); +void pix_update(struct fh_clk* p_clk); + +struct fh_clk_div{ +//some has prediv.... +//this two could have or...... +#define PRE_DIV_CAL_ALREADY (0x80000000) +#define PRE_DIV_ENABLE (0x01) +#define DIV_ENABLE (0x10) + rt_uint32_t div_flag; + + rt_uint32_t pdiv_value; + + //rt_uint32_t hw_div_value; + rt_uint32_t sw_div_value; + rt_uint32_t sw_div_multi; + //rt_uint32_t clk_in_hz; + rt_uint32_t reg_offset; + rt_uint32_t reg_mask; + //rt_uint32_t rate; +}; + +struct fh_clk_mux{ +//#define MUX_LEVEL_1 (1) +//#define MUX_LEVEL_2 (2) +//#define MAX_MUX_LEVEL MUX_LEVEL_2 +// rt_uint32_t lev; +#define HAS_MUX (0) +#define HAS_NO_MUX (1) + rt_uint32_t mux_flag; + rt_uint32_t hw_mux_value; + rt_uint32_t sw_mux_value; + rt_uint32_t reg_offset; + rt_uint32_t reg_mask; +}; + +struct fh_clk_gate{ + +#define HAS_GATE (0) +#define HAS_NO_GATE (1) + rt_uint32_t gate_flag; +#define CLK_UNGATE (0) +#define CLK_GATE (1) + //rt_uint32_t hw_status; + rt_uint32_t sw_status; + //rt_uint32_t value; + + rt_uint32_t reg_offset; + rt_uint32_t reg_mask; + +}; + + + + + + +/*************** + * + * level 1 + * + ***************/ +struct fh_clk_level_1{ + rt_uint32_t clk_in_out; +}; + + +/*************** + * + * level 2 + * + ***************/ +struct fh_clk_level_2{ + rt_uint32_t clk_in_out; +}; + +/*************** + * + * level 3 + * + ***************/ +struct fh_clk_level_3_ddr{ + //rt_uint32_t mux_level; + + struct fh_clk_mux mux[2]; + struct fh_clk_gate gate; + struct fh_clk_div div; +}; + +struct fh_clk_level_3_sdc{ +#define DIFF_REFERENCE (0x80000000) + + rt_uint32_t phase_diff; + rt_uint32_t reg_offset; + rt_uint32_t reg_mask; + +}; + +struct fh_clk_level_3_gmac{ + +}; + +struct fh_clk_level_3_normal{ + struct fh_clk_mux mux; + struct fh_clk_gate gate; + struct fh_clk_div div; +}; + + +struct fh_clk_level_3 { + +#define LEVEL_PERI_NORMAL (0x301) +#define LEVEL_PERI_DDR (0x302) +#define LEVEL_PERI_SDC (0x303) +#define LEVEL_PERI_GMAC (0x304) + rt_uint32_t peri_flag; + union + { + struct fh_clk_level_3_ddr ddr; + struct fh_clk_level_3_sdc sdc; + struct fh_clk_level_3_gmac gmac; + struct fh_clk_level_3_normal normal; + }obj; + +}; + + + + +struct fh_clk { + char *name; +#define LEVEL_CRYSTAL (0x100) +#define LEVEL_PLL (0x200) +#define LEVEL_PERIPHERAL (0x300) + rt_uint32_t level; + +#define ROOT_NODE (RT_NULL) + struct fh_clk *parent; + + union + { + struct fh_clk_level_1 crystal; + struct fh_clk_level_2 pll; + struct fh_clk_level_3 peri; + }clk; + + rt_uint32_t clk_out_rate; +#define CLK_HAS_NO_GATE (0x80000000) + rt_uint32_t gate; + + clk_update update_func; + + //struct fh_clk_tree *p_tree; +}; + +struct fh_clk_tree{ + rt_uint32_t c_base_addr; + struct fh_clk **clk_head; +}; + + + +/********* + * + * + * clk map.... + * + * + ********/ +#define CRYSTAL_HZ (24000000) +struct fh_clk clk_in = { + .name = "clk_in", + .level = LEVEL_CRYSTAL, + .parent = ROOT_NODE, + .clk.crystal.clk_in_out = CRYSTAL_HZ, + //.clk_out_rate = clk_in.clk.crystal.clk_in_out, + .clk_out_rate = CRYSTAL_HZ, + .update_func = clk_in_update, +}; + + + +#define CIS_PCLK_HZ (108000000) +struct fh_clk cis_pclk = { + .name = "cis_pclk", + .level = LEVEL_CRYSTAL, + .parent = ROOT_NODE, + .clk.crystal.clk_in_out = CIS_PCLK_HZ, + //.clk_out_rate = clk_in.clk.crystal.clk_in_out, + .clk_out_rate = CIS_PCLK_HZ, + .update_func = cis_pclk_update, +}; + + +#define PLL0_HZ (864000000) +struct fh_clk pll0 = { + .name = "pll0", + .level = LEVEL_PLL, + .parent = &clk_in, + .clk.crystal.clk_in_out = PLL0_HZ, + //.clk_out_rate = pll0.clk.crystal.clk_in_out, + .clk_out_rate = PLL0_HZ, + .update_func = pll0_clk_update, +}; + + +#define PLL1_HZ (600000000) +struct fh_clk pll1 = { + .name = "pll1", + .level = LEVEL_PLL, + .parent = &clk_in, + .clk.crystal.clk_in_out = PLL1_HZ, + .clk_out_rate = PLL1_HZ, + .update_func = pll1_clk_update, + +}; + + +//NEED_CAUTION parent not fix... +static struct fh_clk ddr_clk_normal = { + .name = "ddr_normal", + .level = LEVEL_PERIPHERAL, + //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_DDR, + //0:xtal_clk + //1:pll0_clk +#define MUX0_XTAL_CLK (0) +#define MUX0_PLL0_CLK (1) + + .clk.peri.obj.ddr.mux[0].reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.ddr.mux[0].reg_mask = 1<<0, + + //0:pll0 clk default 864/2M + //1:pll1 clk default 600M +#define MUX1_PLL0_CLK (0) +#define MUX1_PLL1_CLK (1) + .clk.peri.obj.ddr.mux[1].reg_offset = REG_PMU_CLK_SEL, + .clk.peri.obj.ddr.mux[1].reg_mask = 1<<24, + + //gate + //.clk.peri.obj.ddr.gate.enable_status = CLK_ENABLE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.ddr.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.ddr.gate.sw_status = CLK_UNGATE, +#endif + .clk.peri.obj.ddr.gate.gate_flag = HAS_GATE, + .clk.peri.obj.ddr.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.ddr.gate.reg_mask = MCLK_GATE, + + + + //div + //clk in maybe cry or pll + .clk.peri.obj.ddr.div.div_flag = DIV_ENABLE, + //.clk.peri.obj.ddr.div.pdiv_value = 2, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.ddr.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.ddr.div.sw_div_value = 1, +#endif + .clk.peri.obj.ddr.div.sw_div_multi =1, + .clk.peri.obj.ddr.div.reg_offset = REG_PMU_CLK_DIV1, + .clk.peri.obj.ddr.div.reg_mask = 0xff <<0, + + .update_func = ddr_clk_update, +}; + + + + + + + + +//NEED_CAUTION parent not fix... +static struct fh_clk ddr_clk_div2 = { + .name = "ddr_div2", + .level = LEVEL_PERIPHERAL, + //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_DDR, + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.ddr.mux[0].reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.ddr.mux[0].reg_mask = 1<<0, + + //0:pll0 clk default 864/2M + //1:pll1 clk default 600M + .clk.peri.obj.ddr.mux[1].reg_offset = REG_PMU_CLK_SEL, + .clk.peri.obj.ddr.mux[1].reg_mask = 1<<24, + + //gate + //.clk.peri.obj.ddr.gate.enable_status = CLK_ENABLE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.ddr.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.ddr.gate.sw_status = CLK_UNGATE, +#endif + .clk.peri.obj.ddr.gate.gate_flag = HAS_GATE, + .clk.peri.obj.ddr.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.ddr.gate.reg_mask = MCLK_GATE, + + //div + //clk in maybe cry or pll + .clk.peri.obj.ddr.div.div_flag = PRE_DIV_ENABLE | DIV_ENABLE, + .clk.peri.obj.ddr.div.pdiv_value = 2, +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.ddr.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.ddr.div.sw_div_value = 1, +#endif + .clk.peri.obj.ddr.div.sw_div_multi =1, + .clk.peri.obj.ddr.div.reg_offset = REG_PMU_CLK_DIV1, + .clk.peri.obj.ddr.div.reg_mask = 0xff <<0, + + + .update_func = ddr_clk_update, +}; + + + +static struct fh_clk cpu_fclk = { + .name = "cpu_fclk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_NO_GATE, + //.clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + //.clk.peri.obj.normal.gate.reg_mask = CPU_FCLK0_GATE, + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 0, +#endif + + .clk.peri.obj.normal.div.sw_div_multi =1, + .clk.peri.obj.normal.div.reg_offset =REG_PMU_CLK_DIV0, + .clk.peri.obj.normal.div.reg_mask = 0xff << 0, + + .update_func = fclk_update, + +}; + + +//NEED_CAUTION parent not fix... +static struct fh_clk cpu_aclk = { + .name = "cpu_aclk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, +// .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, +// .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = CPU_FCLK0_GATE, + + + //div + .clk.peri.obj.normal.div.div_flag = PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.pdiv_value = 2, +// .clk.peri.obj.normal.div.reg_offset =REG_PMU_CLK_DIV0, +// .clk.peri.obj.normal.div.reg_mask = 0xff << 0, + + .update_func = aclk_update, + +}; + + + + +static struct fh_clk cpu_hclk = { + .name = "cpu_hclk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_NO_GATE, + //.clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + //.clk.peri.obj.normal.gate.reg_mask = CPU_FCLK0_GATE, + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 1, +#endif + .clk.peri.obj.normal.div.sw_div_multi =1, + .clk.peri.obj.normal.div.reg_offset =REG_PMU_CLK_DIV0, + .clk.peri.obj.normal.div.reg_mask = 0xff << 16, + + .update_func = hclk_update, +}; +//NEED_CAUTION parent not fix... + +static struct fh_clk cpu_pclk = { + .name = "cpu_pclk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = HCLK_GATE, + + + //div + .clk.peri.obj.normal.div.div_flag = PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.pdiv_value = 2, +// .clk.peri.obj.normal.div.reg_offset =REG_PMU_CLK_DIV0, +// .clk.peri.obj.normal.div.reg_mask = 0xff << 0, + + .update_func = pclk_update, + +}; + + +//NEED_CAUTION parent not fix... +static struct fh_clk isp_aclk = { + .name = "isp_aclk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = ISP_ACLK_GATE, + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV0, + .clk.peri.obj.normal.div.reg_mask = 0x03 << 8, + .clk.peri.obj.normal.div.sw_div_multi =1, + + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 1, +#endif + + .update_func = isp_aclk_update, + +}; + +// +////NEED_CAUTION parent not fix... +static struct fh_clk vcu_clk = { + .name = "vcu_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = VCU_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV0, + .clk.peri.obj.normal.div.reg_mask = 0x03 << 24, + .clk.peri.obj.normal.div.sw_div_multi =1, + + + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 1, +#endif + + + .update_func = vcu_clk_update, + +}; + +static struct fh_clk vou_clk = { + .name = "vou_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = VOU_CLK_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV1, + .clk.peri.obj.normal.div.reg_mask = 0x3f << 8, + .clk.peri.obj.normal.div.sw_div_multi =1, + + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 1, +#endif + + .update_func = vou_clk_update, +}; + + + +static struct fh_clk mipi_p32_clk = { + .name = "mipi_p32_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = MIPI_P32_CLK_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV2, + .clk.peri.obj.normal.div.reg_mask = 0x0f << 16, + .clk.peri.obj.normal.div.sw_div_multi =1, + + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 1, +#endif + + + .update_func = mipi_p32_clk_update, + +}; + + + +static struct fh_clk cis_clk_out = { + .name = "cis_clk_out", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = CIS_CLK_OUT_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV1, + .clk.peri.obj.normal.div.reg_mask = 0xff << 16, + .clk.peri.obj.normal.div.sw_div_multi =1, + + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 3, +#endif + + .update_func = cis_clk_out_update, + +}; + + + + +static struct fh_clk pts_clk = { + .name = "pts_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = PTS_CLK_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV2, + .clk.peri.obj.normal.div.reg_mask = 0xff << 0, + .clk.peri.obj.normal.div.sw_div_multi =1, +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 35, +#endif + + .clk.peri.obj.normal.div.pdiv_value = 12, + + .update_func = pts_update, + +}; + + + + +static struct fh_clk mipi_pix_clk = { + .name = "mipi_pix_clk_i", + .level = LEVEL_PERIPHERAL, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_SYS_CTRL, + .clk.peri.obj.normal.mux.reg_mask = 1<<0, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_NO_GATE, + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV2, + .clk.peri.obj.normal.div.reg_mask = 0x0f << 24, + .clk.peri.obj.normal.div.sw_div_multi =1, +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 3, +#endif + + .update_func = mipi_pix_clk_update, + + +}; + + + +static struct fh_clk pix_clk = { + .name = "pix_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + //0:xtal_clk + //1:pll0_clk +#define CIS_PIX_CLK (0) +#define CIS_PIX_CLK_OPPOSITE (1) +#define MIPI_PIX_CLK (2) + + .clk.peri.obj.normal.mux.reg_offset = REG_PMU_CLK_SEL, + .clk.peri.obj.normal.mux.reg_mask = 3<<4, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = PIX_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + //div + .clk.peri.obj.normal.div.div_flag = 0, + + + + .update_func = pix_update, + +}; + + + + + + +static struct fh_clk spi0_clk = { + .name = "spi0_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = SPI0_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV3, + .clk.peri.obj.normal.div.reg_mask = 0xff << 0, + .clk.peri.obj.normal.div.sw_div_multi =1, + + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 11, +#endif + + .update_func = spi0_clk_update, + +}; + + + + +static struct fh_clk spi1_clk = { + .name = "spi1_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = SPI1_CLK_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV3, + .clk.peri.obj.normal.div.reg_mask = 0xff << 16, + .clk.peri.obj.normal.div.sw_div_multi =1, + + + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 11, +#endif + +#ifdef RT_USING_SPI1 + .clk.peri.obj.normal.div.sw_div_value = 11, +#endif + + .update_func = spi1_clk_update, + +}; + + + + +static struct fh_clk mipi_dphy_clk20m = { + .name = "mipi_dphy_clk20m", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = MIPI_DPHY_CLK20M_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + + //div + .clk.peri.obj.normal.div.div_flag = PRE_DIV_ENABLE, +// .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV3, +// .clk.peri.obj.normal.div.reg_mask = 0xff << 16, + .clk.peri.obj.normal.div.sw_div_multi =1, + .clk.peri.obj.normal.div.pdiv_value = 30, +// .clk.peri.obj.normal.div.sw_div_value = 11, + + .update_func = mipi_dphy_clk20m_update, +}; + + + +static struct fh_clk i2c0_clk = { + .name = "i2c0_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = I2C0_CLK_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV4, + .clk.peri.obj.normal.div.reg_mask = 0x3f << 16, + .clk.peri.obj.normal.div.sw_div_multi = 1, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 1, +#endif + + .clk.peri.obj.normal.div.pdiv_value = 20, + + .update_func = i2c0_clk_update, + +}; + + +static struct fh_clk i2c1_clk = { + .name = "i2c1_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = I2C1_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV4, + .clk.peri.obj.normal.div.reg_mask = 0x3f << 24, + .clk.peri.obj.normal.div.sw_div_multi = 1, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 1, +#endif + .clk.peri.obj.normal.div.pdiv_value = 20, + + + .update_func = i2c1_clk_update, +}; + + +static struct fh_clk uart0_clk = { + .name = "uart0_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = UART0_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV4, + .clk.peri.obj.normal.div.reg_mask = 0x1f << 0, + .clk.peri.obj.normal.div.sw_div_multi = 1, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 1, +#endif + + + .clk.peri.obj.normal.div.pdiv_value = 10, + + + .update_func = uart0_clk_update, + +}; + +static struct fh_clk uart1_clk = { + .name = "uart1_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = UART1_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV4, + .clk.peri.obj.normal.div.reg_mask = 0x1f << 8, + .clk.peri.obj.normal.div.sw_div_multi = 1, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 1, +#endif + + + + .clk.peri.obj.normal.div.pdiv_value = 10, + + .update_func = uart1_clk_update, + +}; + + + + +static struct fh_clk pwm_clk = { + .name = "pwm_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = PWM_CLK_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV5, + .clk.peri.obj.normal.div.reg_mask = 0x3f << 0, + .clk.peri.obj.normal.div.sw_div_multi = 1, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 29, +#endif + + + + .clk.peri.obj.normal.div.pdiv_value = 20, + + .update_func = pwm_clk_update, +}; + + + +static struct fh_clk time0_clk = { + .name = "time0_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = TMR0_CLK_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV5, + .clk.peri.obj.normal.div.reg_mask = 0x3f << 16, + .clk.peri.obj.normal.div.sw_div_multi = 1, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 29, +#endif + + + + .clk.peri.obj.normal.div.pdiv_value = 20, + + + .update_func = time0_clk_update, + +}; + + +static struct fh_clk time1_clk = { + .name = "time1_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = TMR1_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV5, + .clk.peri.obj.normal.div.reg_mask = 0x3f << 24, + .clk.peri.obj.normal.div.sw_div_multi = 1, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 29, +#endif + + + .clk.peri.obj.normal.div.pdiv_value = 20, + + .update_func = time1_clk_update, + +}; + + + + +static struct fh_clk sadc_clk = { + .name = "sadc_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = SADC_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + //div + .clk.peri.obj.normal.div.div_flag = PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.sw_div_multi = 1, + .clk.peri.obj.normal.div.pdiv_value = 120, + + .update_func = sadc_clk_update, + +}; + + + +static struct fh_clk sdc0_clk2x = { + .name = "sdc0_clk2x", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = SDC0_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV3, + .clk.peri.obj.normal.div.reg_mask = 0x0f << 8, + .clk.peri.obj.normal.div.sw_div_multi = 1, + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 5, +#endif + + + .clk.peri.obj.normal.div.pdiv_value = 2, + + .update_func = sdc0_clk2x_update, + +}; + + + +static struct fh_clk sdc0_clk = { + .name = "sdc0_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_SDC, + .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0 | DIFF_REFERENCE, +// .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL, +// .clk.peri.obj.sdc.reg_mask = 0x0, + + .update_func = sdc0_clk_update, + +}; + + + +static struct fh_clk sdc0_clk_out = { + .name = "sdc0_clk_out", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_SDC, + .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0, +// .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL, +// .clk.peri.obj.sdc.reg_mask = 0x0, + + .update_func = sdc0_clk_out_update, +}; + + + +static struct fh_clk sdc0_clk_sample = { + .name = "sdc0_clk_sample", + .level = LEVEL_PERIPHERAL, + + .clk.peri.peri_flag = LEVEL_PERI_SDC, + .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0, + .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL, + .clk.peri.obj.sdc.reg_mask = 3<16, + + + .update_func = sdc0_clk_sample_update, +}; + +static struct fh_clk sdc0_clk_drive = { + .name = "sdc0_clk_drive", + .level = LEVEL_PERIPHERAL, + + .clk.peri.peri_flag = LEVEL_PERI_SDC, + .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0, + .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL, + .clk.peri.obj.sdc.reg_mask = 3<20, + + + .update_func = sdc0_clk_drv_update, +}; + + + + +static struct fh_clk sdc1_clk2x = { + .name = "sdc1_clk2x", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = SDC1_CLK_GATE, + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV3, + .clk.peri.obj.normal.div.reg_mask = 0x0f << 24, + .clk.peri.obj.normal.div.sw_div_multi = 1, +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 5, +#endif + + + .clk.peri.obj.normal.div.pdiv_value = 2, + + + .update_func = sdc1_clk2x_update, +}; + + + +static struct fh_clk sdc1_clk = { + .name = "sdc1_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_SDC, + .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0 | DIFF_REFERENCE, +// .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL, +// .clk.peri.obj.sdc.reg_mask = 0x0, + + .update_func = sdc1_clk_update, +}; + + + +static struct fh_clk sdc1_clk_out = { + .name = "sdc1_clk_out", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_SDC, + .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0, +// .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL, +// .clk.peri.obj.sdc.reg_mask = 0x0, + + .update_func = sdc1_clk_out_update, +}; + + + +static struct fh_clk sdc1_clk_sample = { + .name = "sdc1_clk_sample", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_SDC, + .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0, + .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL, + .clk.peri.obj.sdc.reg_mask = 3<8, + + .update_func = sdc1_clk_sample_update, +}; + +static struct fh_clk sdc1_clk_drive = { + .name = "sdc1_clk_drive", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_SDC, + .clk.peri.obj.sdc.phase_diff = DIFF_SDC_REFCLK_0, + .clk.peri.obj.sdc.reg_offset = REG_PMU_CLK_SEL, + .clk.peri.obj.sdc.reg_mask = 3<12, + + .update_func = sdc1_clk_drv_update, +}; + + + + + +static struct fh_clk eth_ref_clk = { + .name = "eth_ref_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_GATE, + .clk.peri.obj.normal.gate.reg_offset = REG_PMU_CLK_GATE, + .clk.peri.obj.normal.gate.reg_mask = ETH_REF_CLK_GATE, + + +#ifdef FH_CLK_GATE_DEFAULT + .clk.peri.obj.normal.gate.sw_status = FH_CLK_GATE_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.gate.sw_status = CLK_UNGATE, +#endif + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE | PRE_DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV6, + .clk.peri.obj.normal.div.reg_mask = 0x0f << 24, + .clk.peri.obj.normal.div.sw_div_multi = 1, + + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 5, +#endif + + + + + .clk.peri.obj.normal.div.pdiv_value = 2, + + + .update_func = eth_ref_clk_update, +}; + + + + +static struct fh_clk wdt_clk = { + .name = "wdt_clk", + .level = LEVEL_PERIPHERAL, +// //.parent = &clk_in, + .clk.peri.peri_flag = LEVEL_PERI_NORMAL, + .clk.peri.obj.normal.mux.mux_flag = HAS_NO_MUX, + + //gate + .clk.peri.obj.normal.gate.gate_flag = HAS_NO_GATE, + + + //div + .clk.peri.obj.normal.div.div_flag = DIV_ENABLE, + .clk.peri.obj.normal.div.reg_offset = REG_PMU_CLK_DIV5, + .clk.peri.obj.normal.div.reg_mask = 0x3f << 8, + .clk.peri.obj.normal.div.sw_div_multi = 1, + + + +#ifdef FH_CLK_DIV_DEFAULT + .clk.peri.obj.normal.div.sw_div_value = FH_CLK_DIV_DEFAULT_VALUE, +#else + .clk.peri.obj.normal.div.sw_div_value = 29, +#endif + + + .update_func = wdt_clk_update, +}; + + + +struct fh_clk *fh_clk_array[] = { + &clk_in, + &cis_pclk, + &pll0, + &pll1, + &ddr_clk_normal, + &ddr_clk_div2, + &cpu_fclk, + &cpu_aclk, + &cpu_hclk, + &cpu_pclk, + &isp_aclk, + &vcu_clk, + &vou_clk, + &mipi_p32_clk, + &cis_clk_out, + &pts_clk, + &mipi_pix_clk, + &pix_clk, + + //pll1 + &sdc0_clk2x, + &sdc0_clk, + &sdc0_clk_out, + &sdc0_clk_sample, + &sdc0_clk_drive, + + &sdc1_clk2x, + &sdc1_clk, + &sdc1_clk_out, + &sdc1_clk_sample, + &sdc1_clk_drive, + + + &spi0_clk, + &spi1_clk, + &mipi_dphy_clk20m, + &i2c0_clk, + &i2c1_clk, + &uart0_clk, + &uart1_clk, + &pwm_clk, + &time0_clk, + &time1_clk, + &sadc_clk, + ð_ref_clk, + + &wdt_clk, + + +}; + +static inline rt_int32_t wrap_read_reg(rt_uint32_t offset, rt_uint32_t mask, + rt_uint32_t *value) +{ + rt_uint32_t temp_v, temp_shift; + + /* if(fh_pmu_status() == PMU_STATUS_CLOSE) + return -1;*/ + temp_v = FH_TIMER_READL(offset); + temp_v &= mask; + temp_shift = __rt_ffs(mask); + temp_v = temp_v >> (temp_shift - 1); + *value = temp_v; + return 0; +} + +static inline rt_int32_t wrap_write_reg(rt_uint32_t offset, rt_uint32_t mask, + rt_uint32_t value) +{ + rt_uint32_t temp_v, temp_shift; + + /* + if(fh_pmu_status() == PMU_STATUS_CLOSE) + return -1; + */ + + temp_v = FH_TIMER_READL(offset); + temp_v &= ~mask; + temp_shift = __rt_ffs(mask); + temp_v |= value << (temp_shift - 1); + FH_TIMER_WRITEL(offset, temp_v); + return 0; +} + +rt_int32_t check_pix_clk_source(rt_uint32_t offset, rt_uint32_t mask, + rt_uint32_t *value) +{ + rt_uint32_t mux0; + rt_int32_t ret; + ret = wrap_read_reg(offset, mask, &mux0); + + if (ret != 0) { + return ret; + } + + *value = mux0; + return 0; +} + +rt_int32_t check_xtal_pll0(rt_uint32_t offset, rt_uint32_t mask, + rt_uint32_t *value) +{ + rt_uint32_t mux0; + rt_int32_t ret; + ret = wrap_read_reg(offset, mask, &mux0); + + if (ret != 0) { + return ret; + } + if (mux0 == MUX0_PLL0_CLK) + *value = MUX0_PLL0_CLK; + else + *value = MUX0_XTAL_CLK; + + return 0; +} + +void cal_pll0_prediv(rt_uint32_t *div_flag, rt_uint32_t *pre_value) +{ + + if (!(*div_flag & PRE_DIV_CAL_ALREADY)) { + //before has got the prediv value.. + if (*div_flag & PRE_DIV_ENABLE) { + + *pre_value *= 2; + } else { + *pre_value = 2; + } + *div_flag |= PRE_DIV_ENABLE | PRE_DIV_CAL_ALREADY; + } + +} + +rt_int32_t sw_div_process(rt_uint32_t div_flag, rt_uint32_t offset, + rt_uint32_t mask, rt_uint32_t *div_value) +{ + + //rt_kprintf("----------div go----------\n"); + rt_uint32_t div; + rt_int32_t ret; + if (div_flag & DIV_ENABLE) { + ret = wrap_read_reg(offset, mask, &div); + if (ret != 0) { + return ret; + } + +// +// rt_kprintf("hw value is %x\n",div); +// rt_kprintf("sw value is %x\n",div_value); +// +// rt_kprintf("offset is %x,value :%x\n",offset + 0xf0000000,*(rt_uint32_t*)(offset + 0xf0000000)); +// rt_kprintf("mask is %x\n",mask); + + //if use the hw default value.... + + if (*div_value == FH_CLK_DIV_DEFAULT_VALUE) { + *div_value = div; + return 0; + } + + if (div != *div_value) { + ret = wrap_write_reg(offset, mask, *div_value); + if (ret != 0) { + return ret; + } + } + } + //rt_kprintf("----------div done----------\n"); + return 0; + //*div_flag |= PRE_DIV_ENABLE; +} + +void cal_baud_hz(rt_uint32_t clk_in, rt_uint32_t div_flag, rt_uint32_t pre_div, + rt_uint32_t div, rt_uint32_t div_multi, rt_uint32_t *baud_out) +{ + //div += 1; + if (div_flag & PRE_DIV_ENABLE) { + *baud_out = (clk_in / pre_div); + } else { + *baud_out = clk_in; + } + + if (div_flag & DIV_ENABLE) { + *baud_out /= ((div + 1) * div_multi); + } + +} + +void cal_baud_div(rt_uint32_t clk_in, rt_uint32_t div_flag, rt_uint32_t pre_div, + rt_uint32_t *div, rt_uint32_t div_multi, rt_uint32_t baud_out) +{ + //div += 1; + rt_uint32_t temp_baud_hz, temp_baud_div; + + if (div_flag & DIV_ENABLE) { + if (div_flag & PRE_DIV_ENABLE) { + temp_baud_hz = (clk_in / pre_div); + } else { + temp_baud_hz = clk_in; + } + temp_baud_div = temp_baud_hz / baud_out; + *div = temp_baud_div - 1; + } + +} + +rt_int32_t process_gate(rt_uint32_t gate_flag, rt_uint32_t reg_offset, + rt_uint32_t reg_mask, rt_uint32_t *sw_status, + rt_uint32_t *pclk_status) +{ + //rt_kprintf("----------gate go----------\n"); + rt_uint32_t hw_gate; + rt_int32_t ret; + if (gate_flag == HAS_GATE) { + ret = wrap_read_reg(reg_offset, reg_mask, &hw_gate); + if (ret != 0) { + return ret; + } + + if (*sw_status == FH_CLK_GATE_DEFAULT_VALUE) { + *sw_status = hw_gate; + *pclk_status = *sw_status; + return 0; + } + +// rt_kprintf("gate hw is :%x\n",hw_gate); +// rt_kprintf("gate sw is :%x\n",sw_status); + if (hw_gate != *sw_status) { + //update the gate.. +// rt_kprintf("gate reg offset is :%x\n",reg_offset); +// rt_kprintf("gate reg mask is :%x\n",reg_mask); +// rt_kprintf("gate reg write is :%x\n",sw_status); + ret = wrap_write_reg(reg_offset, reg_mask, *sw_status); + if (ret != 0) { + return ret; + } + } + + *pclk_status = *sw_status; + } + + else { + *pclk_status |= CLK_HAS_NO_GATE; + } + //rt_kprintf("---------gate done---------\n"); + return 0; + +} + +void clk_handle(struct fh_clk* p_clk, struct fh_clk *parent) +{ + //rt_uint32_t div; + //rt_uint32_t sw_gate; + rt_uint32_t phase; + rt_int32_t ret; + p_clk->parent = parent; +// switch + //fh_clk_debug(p_clk,"----parent----\t ----clk out rate----\n "); + if (p_clk->parent) + //rt_kprintf("%-8.*s 0x%02x", RT_NAME_MAX, thread->name, thread->current_priority); + fh_clk_debug(p_clk, "parent:'%s'\n", p_clk->parent->name); + else + fh_clk_debug(p_clk, "'root node'\n"); + + switch (p_clk->level) { + + case LEVEL_CRYSTAL: + //fh_clk_debug(p_clk,"clk out:%d\n",p_clk->clk_out_rate); + break; + case LEVEL_PLL: + //fh_clk_debug(p_clk,"%d\n",p_clk->clk_out_rate); + break; + case LEVEL_PERIPHERAL: + + switch (p_clk->clk.peri.peri_flag) { + + case LEVEL_PERI_NORMAL: + //div = p_clk->clk.peri.obj.normal.div.sw_div_value; + ret = + sw_div_process( + p_clk->clk.peri.obj.normal.div.div_flag, + p_clk->clk.peri.obj.normal.div.reg_offset, + p_clk->clk.peri.obj.normal.div.reg_mask, + &p_clk->clk.peri.obj.normal.div.sw_div_value); + + if (ret != 0) { + fh_clk_err(p_clk, + "div process failed.error no:%x\n", + ret); + break; + } + + //fh_clk_debug(p_clk,"hw div is %d\n",p_clk->clk.peri.obj.ddr.div.hw_div_value); +// fh_clk_debug(p_clk,"sw div is %d\n",p_clk->clk.peri.obj.normal.div.sw_div_value); +// fh_clk_debug(p_clk,"pre div is %d\n",p_clk->clk.peri.obj.normal.div.pdiv_value); +// fh_clk_debug(p_clk,"clk in is %d\n",p_clk->parent->clk_out_rate); +// fh_clk_debug(p_clk,"peri flag is %x\n",p_clk->clk.peri.obj.normal.div.div_flag); + //hw will self add 1.. + + cal_baud_hz(p_clk->parent->clk_out_rate, + p_clk->clk.peri.obj.normal.div.div_flag, + p_clk->clk.peri.obj.normal.div.pdiv_value, + p_clk->clk.peri.obj.normal.div.sw_div_value, + p_clk->clk.peri.obj.normal.div.sw_div_multi, + &p_clk->clk_out_rate); + + //fh_clk_debug_no_handle("%d\n",p_clk->clk_out_rate); + //fix the gate.. + //sw_gate = p_clk->clk.peri.obj.normal.gate.sw_status; + ret = + process_gate( + p_clk->clk.peri.obj.normal.gate.gate_flag, + p_clk->clk.peri.obj.normal.gate.reg_offset, + p_clk->clk.peri.obj.normal.gate.reg_mask, + &p_clk->clk.peri.obj.normal.gate.sw_status, + &p_clk->gate); + + if (ret != 0) { + fh_clk_err(p_clk, + "gate process failed.error no:%x\n", + ret); + break; + } + + break; + case LEVEL_PERI_DDR: + //rt_uint32_t mux0,mux1; + //div = p_clk->clk.peri.obj.ddr.div.sw_div_value; + + ret = + sw_div_process( + p_clk->clk.peri.obj.ddr.div.div_flag, + p_clk->clk.peri.obj.ddr.div.reg_offset, + p_clk->clk.peri.obj.ddr.div.reg_mask, + &p_clk->clk.peri.obj.ddr.div.sw_div_value); + + if (ret != 0) { + fh_clk_err(p_clk, + "div process failed.error no:%x\n", + ret); + break; + } + +// fh_clk_debug(p_clk,"sw div is %d\n",p_clk->clk.peri.obj.ddr.div.sw_div_value); +// fh_clk_debug(p_clk,"pre div is %d\n",p_clk->clk.peri.obj.ddr.div.pdiv_value); +// fh_clk_debug(p_clk,"clk in is %d\n",p_clk->parent->clk_out_rate); +// fh_clk_debug(p_clk,"peri flag is %x\n",p_clk->clk.peri.obj.ddr.div.div_flag); + + cal_baud_hz(p_clk->parent->clk_out_rate, + p_clk->clk.peri.obj.ddr.div.div_flag, + p_clk->clk.peri.obj.ddr.div.pdiv_value, + p_clk->clk.peri.obj.ddr.div.sw_div_value, + p_clk->clk.peri.obj.ddr.div.sw_div_multi, + &p_clk->clk_out_rate); + + //fh_clk_debug_no_handle("%d\n",p_clk->clk_out_rate); + //fix the gate.. + //fh_clk_debug(p_clk,"gate reg add is:%x\t mask is:%x\n",p_clk->clk.peri.obj.ddr.gate.reg_offset,p_clk->clk.peri.obj.ddr.gate.reg_mask); + //sw_gate = p_clk->clk.peri.obj.ddr.gate.sw_status; + + ret = process_gate( + p_clk->clk.peri.obj.ddr.gate.gate_flag, + p_clk->clk.peri.obj.ddr.gate.reg_offset, + p_clk->clk.peri.obj.ddr.gate.reg_mask, + &p_clk->clk.peri.obj.ddr.gate.sw_status, + &p_clk->gate); + + if (ret != 0) { + fh_clk_err(p_clk, + "gate process failed.error no:%x\n", + ret); + break; + } + + break; + case LEVEL_PERI_SDC: + //just need to handle the phase.... + p_clk->clk_out_rate = p_clk->parent->clk_out_rate; + if (p_clk->clk.peri.obj.sdc.phase_diff & DIFF_REFERENCE) { + //fh_clk_debug(p_clk,"this is the reference..no need to process..\n"); + break; + } + + //baud ... + + //phase.. + //fh_clk_debug_no_handle("%d\n",p_clk->clk_out_rate); + //hw status.. + ret = wrap_read_reg(p_clk->clk.peri.obj.sdc.reg_offset, + p_clk->clk.peri.obj.sdc.reg_mask, + &phase); + + if (ret != 0) { + fh_clk_err(p_clk, + "read pmu failed.error no:%x\n", + ret); + break; + } + +// fh_clk_debug(p_clk,"hw phase is :%x\n",phase); +// fh_clk_debug(p_clk,"sw phase is :%x\n",p_clk->clk.peri.obj.sdc.phase_diff); + if (phase != p_clk->clk.peri.obj.sdc.phase_diff) { + //update the hw para.. + ret = + wrap_write_reg( + p_clk->clk.peri.obj.sdc.reg_offset, + p_clk->clk.peri.obj.sdc.reg_mask, + p_clk->clk.peri.obj.sdc.phase_diff); + if (ret != 0) { + fh_clk_err(p_clk, + "write pmu failed.error no:%x\n", + ret); + break; + } + } + + break; + case LEVEL_PERI_GMAC: + break; + default: + break; + + } + } + + fh_clk_debug(p_clk, "clk out:%d\n", p_clk->clk_out_rate); + +} + +// +void clk_in_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, RT_NULL); +} + +void cis_pclk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, RT_NULL); +} + +void pll1_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &clk_in); +} + +void pll0_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &clk_in); +} + +void ddr_clk_update(struct fh_clk* p_clk) +{ + + //check if pll0 or pll1 + rt_uint32_t mux0, mux1; + rt_int32_t ret; + struct fh_clk* parent; + //1 step: fix the parent.. + ret = wrap_read_reg(p_clk->clk.peri.obj.ddr.mux[1].reg_offset, + p_clk->clk.peri.obj.ddr.mux[1].reg_mask, &mux1); + if (ret != 0) { + fh_clk_err(p_clk, "read pmu failed.error no:%x\n", ret); + return; + } + + if (mux1 == MUX1_PLL0_CLK) { + ret = check_xtal_pll0(p_clk->clk.peri.obj.ddr.mux[0].reg_offset, + p_clk->clk.peri.obj.ddr.mux[0].reg_mask, &mux0); + if (ret != 0) { + fh_clk_err(p_clk, "read pmu failed.error no:%x\n", ret); + return; + } + if (mux0 == MUX0_PLL0_CLK) { + //ddr normal parent is pll0 + parent = &pll0; + } else { + //ddr normal parent is xtal + parent = &clk_in; + } + } else { + //ddr normal parent is pll1 + parent = &pll1; + } + p_clk->clk.peri.obj.ddr.mux[0].mux_flag = HAS_MUX; + p_clk->clk.peri.obj.ddr.mux[1].mux_flag = HAS_MUX; + clk_handle(p_clk, parent); + +} + +void fclk_update(struct fh_clk* p_clk) +{ + + //check if pll0 or xtal + rt_uint32_t mux0; + rt_int32_t ret; + struct fh_clk* parent; //1 step: fix the parent.. + + //mux0 = check_xtal_pll0(p_clk->clk.peri.obj.ddr.mux[0].reg_offset,p_clk->clk.peri.obj.ddr.mux[0].reg_mask); + ret = check_xtal_pll0(p_clk->clk.peri.obj.normal.mux.reg_offset, + p_clk->clk.peri.obj.normal.mux.reg_mask, &mux0); + if (ret != 0) { + fh_clk_err(p_clk, "read pmu failed.error no:%x\n", ret); + return; + } + + //fh_clk_debug(p_clk,"mux0 wrap value is %x\n",mux0); + if (mux0 == MUX0_PLL0_CLK) { + //ddr normal parent is pll0 + parent = &pll0; + } else { + //ddr normal parent is xtal + parent = &clk_in; + } + p_clk->clk.peri.obj.normal.mux.mux_flag = HAS_MUX; + //2 step:fix the div... + if (mux0 == MUX0_PLL0_CLK) { + //cal_pll0_prediv(&p_clk->clk.peri.obj.ddr.div.div_flag,&p_clk->clk.peri.obj.ddr.div.pdiv_value); + cal_pll0_prediv(&p_clk->clk.peri.obj.normal.div.div_flag, + &p_clk->clk.peri.obj.normal.div.pdiv_value); + } + clk_handle(p_clk, parent); +} + +void pix_update(struct fh_clk* p_clk) +{ + + //check if pll0 or xtal + rt_uint32_t mux0; + rt_int32_t ret; + struct fh_clk* parent; //1 step: fix the parent.. +#if(1) + //mux0 = check_xtal_pll0(p_clk->clk.peri.obj.ddr.mux[0].reg_offset,p_clk->clk.peri.obj.ddr.mux[0].reg_mask); + ret = check_pix_clk_source(p_clk->clk.peri.obj.normal.mux.reg_offset, + p_clk->clk.peri.obj.normal.mux.reg_mask, &mux0); + if (ret != 0) { + fh_clk_err(p_clk, "read pmu failed.error no:%x\n", ret); + return; + } +//#define CIS_PIX_CLK (0) +//#define CIS_PIX_CLK_OPPOSITE (1) +//#define MIPI_PIX_CLK (2) + + //fh_clk_debug(p_clk,"mux0 wrap value is %x\n",mux0); + if (mux0 == CIS_PIX_CLK || mux0 == CIS_PIX_CLK_OPPOSITE) { + //ddr normal parent is pll0 + parent = &cis_pclk; + } else { + parent = &mipi_pix_clk; + } + p_clk->clk.peri.obj.normal.mux.mux_flag = HAS_MUX; + +#endif + clk_handle(p_clk, parent); +} + +void aclk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &cpu_fclk); + +} + +void hclk_update(struct fh_clk* p_clk) +{ + + fclk_update(p_clk); +} + +void pclk_update(struct fh_clk* p_clk) +{ + + clk_handle(p_clk, &cpu_hclk); +} + +void isp_aclk_update(struct fh_clk* p_clk) +{ + + fclk_update(p_clk); +} + +void vcu_clk_update(struct fh_clk* p_clk) +{ + fclk_update(p_clk); +} + +void vou_clk_update(struct fh_clk* p_clk) +{ + fclk_update(p_clk); +} + +void mipi_p32_clk_update(struct fh_clk* p_clk) +{ + fclk_update(p_clk); +} + +void cis_clk_out_update(struct fh_clk* p_clk) +{ + fclk_update(p_clk); +} + +void pts_update(struct fh_clk* p_clk) +{ + fclk_update(p_clk); +} + +void mipi_pix_clk_update(struct fh_clk* p_clk) +{ + fclk_update(p_clk); +} + +void spi0_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void spi1_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void mipi_dphy_clk20m_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void i2c0_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void i2c1_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void uart0_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void pwm_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void time0_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void time1_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void uart1_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void sadc_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +//sdc0... +void sdc0_clk2x_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void sdc0_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &sdc0_clk2x); +} + +void sdc0_clk_out_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &sdc0_clk); +} + +void sdc0_clk_sample_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &sdc0_clk2x); +} + +void sdc0_clk_drv_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &sdc0_clk2x); +} + +void sdc1_clk2x_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void sdc1_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &sdc1_clk2x); +} + +void sdc1_clk_out_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &sdc1_clk); +} + +void sdc1_clk_sample_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &sdc1_clk2x); +} + +void sdc1_clk_drv_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &sdc1_clk2x); +} + +void eth_ref_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &pll1); +} + +void wdt_clk_update(struct fh_clk* p_clk) +{ + clk_handle(p_clk, &cpu_pclk); +} + +/** + * @brief System Clock Configuration + */ +#define CLK_CONTROL_BASE PMU_REG_BASE +void rt_hw_clock_init(void) +{ + struct fh_clk *p; + int i; + fh_clk_tree.c_base_addr = CLK_CONTROL_BASE; + fh_clk_tree.clk_head = fh_clk_array; + + //first open all the clock.. + FH_TIMER_WRITEL(REG_PMU_CLK_GATE, 0x0); + for (i = 0; i < sizeof(fh_clk_array) / sizeof(struct fh_clk *); i++) { + p = fh_clk_tree.clk_head[i]; + if (p->update_func) + p->update_func(p); + } +} + +/*************** + * + * new add + * + **************/ + +/* clocks cannot be de-registered no refcounting necessary */ +struct fh_clk *clk_get(const char *name) +{ + + struct fh_clk *p; + int i; + + for (i = 0; i < sizeof(fh_clk_array) / sizeof(struct fh_clk *); i++) { + p = fh_clk_tree.clk_head[i]; + if (!strcmp(p->name, name)) { + return p; + } + } + + return RT_NULL; +} + +// +//#define HAS_GATE (0) +//#define HAS_NO_GATE (1) +// rt_uint32_t gate_flag; +//#define CLK_UNGATE (0) +//#define CLK_GATE (1) + +void clk_gate_control(struct fh_clk *p_clk, rt_uint32_t status) +{ + + if (status > CLK_GATE) + return; + + if (p_clk->level == LEVEL_PERIPHERAL) { + + switch (p_clk->clk.peri.peri_flag) { + case LEVEL_PERI_NORMAL: + + if (p_clk->clk.peri.obj.normal.gate.gate_flag + == HAS_GATE) { + p_clk->clk.peri.obj.normal.gate.sw_status = + status; + } else { + rt_kprintf("[%-16.15s]: no gate...\t\n", + p_clk->name); + } + + break; + case LEVEL_PERI_DDR: + if (p_clk->clk.peri.obj.ddr.gate.gate_flag == HAS_GATE) { + p_clk->clk.peri.obj.ddr.gate.sw_status = status; + } else { + rt_kprintf("[%-16.15s]: no gate...\t\n", + p_clk->name); + } + + break; + + default: + break; + } + + p_clk->update_func(p_clk); + + } + +} + +void clk_gate(struct fh_clk *p_clk) +{ + clk_gate_control(p_clk, CLK_GATE); +} + +void clk_ungate(struct fh_clk *p_clk) +{ + clk_gate_control(p_clk, CLK_UNGATE); +} + +rt_uint32_t clk_get_rate(struct fh_clk *p_clk) +{ + rt_uint32_t rate; + //first update the status + p_clk->update_func(p_clk); + rate = p_clk->clk_out_rate; + return rate; +} + +void clk_set_rate(struct fh_clk *p_clk, rt_uint32_t rate_value) +{ + + rt_uint32_t clk_in, div_flag, pre_div, div_multi, baud_out; + + if (p_clk->level == LEVEL_PERIPHERAL) { + + switch (p_clk->clk.peri.peri_flag) { + case LEVEL_PERI_NORMAL: + + clk_in = p_clk->parent->clk_out_rate; + div_flag = p_clk->clk.peri.obj.normal.div.div_flag; + pre_div = p_clk->clk.peri.obj.normal.div.pdiv_value; + div_multi = p_clk->clk.peri.obj.normal.div.sw_div_multi; + baud_out = rate_value; + + cal_baud_div(clk_in, div_flag, pre_div, + &p_clk->clk.peri.obj.normal.div.sw_div_value, + div_multi, baud_out); + + break; + case LEVEL_PERI_DDR: + //rt_uint32_t mux0,mux1; + clk_in = p_clk->parent->clk_out_rate; + div_flag = p_clk->clk.peri.obj.ddr.div.div_flag; + pre_div = p_clk->clk.peri.obj.ddr.div.pdiv_value; + div_multi = p_clk->clk.peri.obj.ddr.div.sw_div_multi; + baud_out = rate_value; + + cal_baud_div(clk_in, div_flag, pre_div, + &p_clk->clk.peri.obj.ddr.div.sw_div_value, + div_multi, baud_out); + break; + case LEVEL_PERI_SDC: + fh_clk_debug(p_clk, + "sdc can't set baud,please set the 'sdcx_clk2x'\n"); + break; + case LEVEL_PERI_GMAC: + fh_clk_debug(p_clk, "gmac not support set baud\n"); + break; + default: + break; + } + p_clk->update_func(p_clk); + + } + +} + +rt_uint32_t sdc_get_phase(struct fh_clk *p_clk) +{ + + if (p_clk->level == LEVEL_PERIPHERAL) { + if (p_clk->clk.peri.peri_flag == LEVEL_PERI_SDC) { + + p_clk->update_func(p_clk); + return p_clk->clk.peri.obj.sdc.phase_diff; + } + } + return SDC_CLK_PARA_ERROR; + +} + +rt_uint32_t sdc_set_phase(struct fh_clk *p_clk, rt_uint32_t phase) +{ + + if (phase > DIFF_SDC_REFCLK_270) + return SDC_CLK_PARA_ERROR; + + if (p_clk->level == LEVEL_PERIPHERAL) { + if (p_clk->clk.peri.peri_flag == LEVEL_PERI_SDC) { + p_clk->clk.peri.obj.sdc.phase_diff = phase; + p_clk->update_func(p_clk); + return SDC_CLK_PARA_OK; + } + } + return SDC_CLK_PARA_ERROR; + +} + +#ifdef FH_DBG_CLK +int fh_clk_nlist() +{ + struct fh_clk *p; + int i; + + for(i = 0;iupdate_func(p); + rt_kprintf("[%-16.15s]:\t\t[baud]:%d\t\n",p->name,p->clk_out_rate); + } + + return 0; +} + +int fh_clk_glist() +{ + struct fh_clk *p; + int i; + rt_kprintf("first bit set means has no gate..\n"); + for(i = 0;iupdate_func(p); + if(!(p->gate & CLK_HAS_NO_GATE)) + rt_kprintf("[%-16.15s]:\t\t[gate]:%d\t\n",p->name,p->gate); + else + rt_kprintf("[%-16.15s]:\t\t[gate]:no gate..\t\n",p->name); + } + + return 0; +} +#endif + + +#ifdef RT_USING_FINSH +#include +#ifdef FH_DBG_CLK +FINSH_FUNCTION_EXPORT(fh_clk_nlist, fh_clk_name_list..); +FINSH_FUNCTION_EXPORT(fh_clk_glist, fh_clk_gate_list..); +#endif +#endif + + diff --git a/bsp/fh8620/platform/plat-v2/clock.h b/bsp/fh8620/platform/plat-v2/clock.h new file mode 100644 index 0000000000000000000000000000000000000000..60b0ed65382ababec4d5c25beb58a8652b72ad4e --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/clock.h @@ -0,0 +1,62 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef TIMER_H_ +#define TIMER_H_ + + +#include + + +void rt_hw_clock_init(void); +void rt_hw_get_clock(void); +void rt_hw_set_dividor(rt_uint8_t hdivn, rt_uint8_t pdivn); +void rt_hw_set_clock(rt_uint8_t sdiv, rt_uint8_t pdiv, rt_uint8_t mdiv); + + +struct fh_clk; +/* +void fh_pmu_open(void); +void fh_pmu_close(void); +*/ + +#define DIFF_SDC_REFCLK_0 (0) +#define DIFF_SDC_REFCLK_90 (1) +#define DIFF_SDC_REFCLK_180 (2) +#define DIFF_SDC_REFCLK_270 (3) +#define SDC_CLK_PARA_ERROR (0xffff0000) +#define SDC_CLK_PARA_OK (0) + + +void clk_gate(struct fh_clk *p_clk); +void clk_ungate(struct fh_clk *p_clk); +struct fh_clk *clk_get(const char *name); +rt_uint32_t clk_get_rate(struct fh_clk *p_clk); +void clk_set_rate(struct fh_clk *p_clk,rt_uint32_t rate_value); +rt_uint32_t sdc_get_phase(struct fh_clk *p_clk); +rt_uint32_t sdc_set_phase(struct fh_clk *p_clk,rt_uint32_t phase); + +#endif diff --git a/bsp/fh8620/platform/plat-v2/fh_pmu.c b/bsp/fh8620/platform/plat-v2/fh_pmu.c new file mode 100644 index 0000000000000000000000000000000000000000..8f518efd94f6a84b534692897da23f6c4389c0e0 --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/fh_pmu.c @@ -0,0 +1,62 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "rtdebug.h" +#include "arch.h" +#include "fh_pmu.h" +#include "fh_def.h" + +#define FH_PMU_WRITEL(offset,value) SET_REG((PMU_REG_BASE + offset),value) +#define FH_PMU_WRITEL_MASK(offset,value, mask) SET_REG_M((PMU_REG_BASE + offset), value, mask) +#define FH_PMU_READL(offset) GET_REG((PMU_REG_BASE + offset)) + +#define PMU_OFFSET_MAX 0x1d0 + +int fh_pmu_read(rt_uint32_t offset, rt_uint32_t *value) +{ + RT_ASSERT(offset < PMU_OFFSET_MAX); + + *value = FH_PMU_READL(offset); + return 0; +} + +int fh_pmu_write(rt_uint32_t offset, const rt_uint32_t value) +{ + RT_ASSERT(offset < PMU_OFFSET_MAX); + + FH_PMU_WRITEL(offset, value); + return 0; +} + +int fh_pmu_write_mask(rt_uint32_t offset, const rt_uint32_t value, + const rt_uint32_t mask) +{ + RT_ASSERT(offset < PMU_OFFSET_MAX); + + FH_PMU_WRITEL_MASK(offset, value, mask); + return 0; + +} diff --git a/bsp/fh8620/platform/plat-v2/fh_pmu.h b/bsp/fh8620/platform/plat-v2/fh_pmu.h new file mode 100644 index 0000000000000000000000000000000000000000..19a6279d1ae5340beef2c72b3baa6a325c7226a0 --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/fh_pmu.h @@ -0,0 +1,59 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef FH_PMU_H_ +#define FH_PMU_H_ + +#include + +#define REG_PMU_CHIP_ID (0x000) +#define REG_PMU_IP_VER (0x004) +#define REG_PMU_FW_VER (0x008) +#define REG_PMU_SYS_CTRL (0x00c) +#define REG_PMU_PLL0_CTRL (0x010) +#define REG_PMU_PLL1_CTRL (0x014) +#define REG_PMU_ARC_CLK_GATE (0x018) +#define REG_PMU_CLK_GATE (0x01c) +#define REG_PMU_CLK_SEL (0x020) +#define REG_PMU_CLK_DIV0 (0x024) +#define REG_PMU_CLK_DIV1 (0x028) +#define REG_PMU_CLK_DIV2 (0x02c) +#define REG_PMU_CLK_DIV3 (0x030) +#define REG_PMU_CLK_DIV4 (0x034) +#define REG_PMU_CLK_DIV5 (0x038) +#define REG_PMU_CLK_DIV6 (0x03c) +#define REG_PMU_SWRST_MAIN_CTRL (0x040) +#define REG_PMU_SWRST_AXI_CTRL (0x044) +#define REG_PMU_SWRST_AHB_CTRL (0x048) +#define REG_PMU_SWRST_APB_CTRL (0x04c) +#define REG_PMU_VDAC_CTRL (0x050) +#define REG_PMU_MAC_REF_CLK_CFG (0x0a4) + +int fh_pmu_read(rt_uint32_t offset,rt_uint32_t *value); +int fh_pmu_write(rt_uint32_t offset, const rt_uint32_t value); +int fh_pmu_write_mask(rt_uint32_t offset,const rt_uint32_t value, const rt_uint32_t mask); + +#endif /* FH_PMU_H_ */ diff --git a/bsp/fh8620/platform/plat-v2/iomux.c b/bsp/fh8620/platform/plat-v2/iomux.c new file mode 100644 index 0000000000000000000000000000000000000000..67acf6ca75c77f6e4f4c3ec4a40450c32864c3b4 --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/iomux.c @@ -0,0 +1,284 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "rtdebug.h" +#include "iomux.h" + +static void fh_iomux_setmfs(Iomux_Pad *pad) +{ + + switch (pad->reg_type) { + case 8: + (IOMUX_PADTYPE(8)pad->reg)->bit.mfs = pad->func_sel; + break; + case 13: + (IOMUX_PADTYPE(13)pad->reg)->bit.mfs = pad->func_sel; + break; + case 20: + (IOMUX_PADTYPE(20)pad->reg)->bit.mfs = pad->func_sel; + break; + case 21: + (IOMUX_PADTYPE(21)pad->reg)->bit.mfs = pad->func_sel; + break; + default: + break; + } + +} + +#ifdef IOMUX_DEBUG + +static int fh_iomux_getmfs(Iomux_Pad *pad) +{ + int mfs; + + switch (pad->reg_type) { + case 8: + mfs = (IOMUX_PADTYPE(8)pad->reg)->bit.mfs; + break; + case 13: + mfs = (IOMUX_PADTYPE(13)pad->reg)->bit.mfs; + break; + case 20: + mfs = (IOMUX_PADTYPE(20)pad->reg)->bit.mfs; + break; + case 21: + mfs = (IOMUX_PADTYPE(21)pad->reg)->bit.mfs; + break; + default: + mfs = -1; + break; + + } + return mfs; +} + + +static void fh_iomux_print() +{ + int i; + UINT32 reg; + + + for (i = 0; i < ARRAY_SIZE(fh81_iomux_cfg); i++) { + int curr_func; + + curr_func = fh81_iomux_getmfs(iomux_obj, &iomux_obj.pads[i]); + reg = readl((UINT32)iomux_obj.pads[i].reg); + + if (curr_func < 0) + rt_kprintf("\t%d\t\t%-8s(no mfs)\t0x%08x\n", i, iomux_obj.pads[i].func_name[0], + reg); + else + rt_kprintf("\t%d\t\t%-16s\t0x%08x\n", i, iomux_obj.pads[i].func_name[curr_func], + reg); + + } + +} + +#endif + +static void fh_iomux_setcur(Iomux_Pad *pad) +{ + + switch (pad->reg_type) { + case 5: + (IOMUX_PADTYPE(5)pad->reg)->bit.e8_e4 = pad->drv_cur; + break; + case 8: + (IOMUX_PADTYPE(8)pad->reg)->bit.e8_e4 = pad->drv_cur; + break; + case 13: + (IOMUX_PADTYPE(13)pad->reg)->bit.e4_e2 = pad->drv_cur; + break; + case 17: + (IOMUX_PADTYPE(17)pad->reg)->bit.e8_e4 = pad->drv_cur; + break; + case 20: + (IOMUX_PADTYPE(20)pad->reg)->bit.e4_e2 = pad->drv_cur; + break; + case 21: + (IOMUX_PADTYPE(21)pad->reg)->bit.e4_e2 = pad->drv_cur; + break; + default: + break; + } + +} + +static void fh_iomux_setpupd(Iomux_Pad *pad) +{ + + switch (pad->reg_type) { + case 9: + (IOMUX_PADTYPE(9)pad->reg)->bit.pu_pd = pad->pupd; + break; + case 17: + (IOMUX_PADTYPE(17)pad->reg)->bit.pu_pd = pad->pupd; + break; + case 20: + (IOMUX_PADTYPE(20)pad->reg)->bit.pu_pd = pad->pupd; + break; + case 21: + (IOMUX_PADTYPE(21)pad->reg)->bit.pu_pd = pad->pupd; + break; + default: + break; + } + +} + +static void fh_iomux_setrest(Iomux_Pad *pad) +{ + + switch (pad->reg_type) { + case 5: + (IOMUX_PADTYPE(5)pad->reg)->bit.sr = 0; + break; + case 8: + (IOMUX_PADTYPE(8)pad->reg)->bit.sr = 0; + break; + case 9: + (IOMUX_PADTYPE(9)pad->reg)->bit.ie = 1; + (IOMUX_PADTYPE(9)pad->reg)->bit.smt = 1; + break; + case 13: + (IOMUX_PADTYPE(13)pad->reg)->bit.ie = 1; + (IOMUX_PADTYPE(13)pad->reg)->bit.smt = 1; + break; + case 17: + (IOMUX_PADTYPE(17)pad->reg)->bit.sr = 0; + (IOMUX_PADTYPE(17)pad->reg)->bit.ie = 1; + (IOMUX_PADTYPE(17)pad->reg)->bit.e = 1; + (IOMUX_PADTYPE(17)pad->reg)->bit.smt = 1; + break; + case 20: + (IOMUX_PADTYPE(20)pad->reg)->bit.sr = 0; + (IOMUX_PADTYPE(20)pad->reg)->bit.ie = 1; + (IOMUX_PADTYPE(20)pad->reg)->bit.smt = 1; + break; + case 21: + (IOMUX_PADTYPE(21)pad->reg)->bit.sr = 0; + (IOMUX_PADTYPE(21)pad->reg)->bit.ie = 1; + (IOMUX_PADTYPE(21)pad->reg)->bit.smt = 1; + break; + default: + break; + } + +} + +static Iomux_Object iomux_obj; +extern Iomux_Pad fh_iomux_cfg[]; +extern const int fh_iomux_cfg_count; + +void __fh_setiomux(Iomux_Pad *pad, void *iobase) +{ + UINT32 regvalue = 0; + pad->reg = ®value; + fh_iomux_setmfs(pad); + fh_iomux_setcur(pad); + fh_iomux_setpupd(pad); + fh_iomux_setrest(pad); + SET_REG(iobase, regvalue); +} + +static UINT32 g_iomux_base; + +void fh_iomux_init(UINT32 base) +{ + +// return; + int i; +// int test_cnt = 0; + UINT32 reg; + g_iomux_base = base; + + iomux_obj.pbase = (void *)base; + +// iomux_obj.vbase = (UINT32 *)rt_malloc(1024); + iomux_obj.pads = fh_iomux_cfg; + + for (i = 0; i < fh_iomux_cfg_count; i++) { +#if (1) + iomux_obj.pads[i].id = i; + iomux_obj.pads[i].reg_offset = i * 4; + iomux_obj.pads[i].reg = ®//(UINT32 *)(iomux_obj.vbase + iomux_obj.pads[i].reg_offset); + fh_iomux_setmfs(&fh_iomux_cfg[i]); + fh_iomux_setcur(&fh_iomux_cfg[i]); + fh_iomux_setpupd(&fh_iomux_cfg[i]); + fh_iomux_setrest(&fh_iomux_cfg[i]); + SET_REG(iomux_obj.pbase + iomux_obj.pads[i].reg_offset, reg); +// *((UINT32 *)(iomux_obj.vbase + iomux_obj.pads[i].reg_offset))); + //rt_kprintf("addr: 0x%x, pmu data: 0x%x\n", iomux_obj.pbase + iomux_obj.pads[i].reg_offset, GET_REG(iomux_obj.pbase + iomux_obj.pads[i].reg_offset)); +// test_cnt++; +#else +#ifdef FH_USING_JTAG + if (strncmp(fh_iomux_cfg[i].func_name[0], "JTAG", 4) == 0) + continue; +#endif +/* + if (strncmp(fh_iomux_cfg[i].func_name[1], "UART1", 5) == 0) + break; +*/ + + __fh_setiomux(&fh_iomux_cfg[i], (void *) base + i * 4); +#endif + } + +#ifdef CONFIG_RMII + //(IOMUX_PADTYPE(17)(iomux_obj.pads[18]).reg)->bit.e = 1; + reg = GET_REG(0xf00000a4); + reg |= (1 << 13); + SET_REG(0xf00000a4, reg); +#else + //(IOMUX_PADTYPE(17)(iomux_obj.pads[18]).reg)->bit.e = 0; + reg = GET_REG(0xf00000a4); + reg &= ~(1 << 13); + SET_REG(0xf00000a4, reg); +#endif +#ifdef IOMUX_DEBUG + fh_iomux_print(iomux_obj); +#endif + + + //rt_free(iomux_obj.vbase); + //iomux_obj.vbase = 0; + +} + +void fh_iomux_pin_switch(int pin_num, int func_num) +{ + RT_ASSERT(pin_num < fh_iomux_cfg_count); + __fh_setiomux(&fh_iomux_cfg[pin_num], (void *)g_iomux_base + pin_num * 4); + /* + fh_iomux_cfg[pin_num].func_sel = func_num; + fh_iomux_setmfs(&fh_iomux_cfg[pin_num]); + SET_REG(iomux_obj.pbase + iomux_obj.pads[pin_num].reg_offset, *((UINT32 *)(iomux_obj.vbase + iomux_obj.pads[pin_num].reg_offset))); + */ +} + diff --git a/bsp/fh8620/platform/plat-v2/iomux.h b/bsp/fh8620/platform/plat-v2/iomux.h new file mode 100644 index 0000000000000000000000000000000000000000..e03273b2b8f680fdf8ce12d8cf3e13cdbc67f877 --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/iomux.h @@ -0,0 +1,314 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef IOMUX_H_ +#define IOMUX_H_ + + + +#include "fh_def.h" + +#define PMU_PAD_RESETN (0) +#define PMU_PAD_TEST (1) +#define PMU_PAD_CIS_CLK (2) +#define PMU_PAD_CIS_HSYNC (3) +#define PMU_PAD_CIS_VSYNC (4) +#define PMU_PAD_CIS_PCLK (5) +#define PMU_PAD_CIS_D_0 (6) +#define PMU_PAD_CIS_D_1 (7) +#define PMU_PAD_CIS_D_2 (8) +#define PMU_PAD_CIS_D_3 (9) +#define PMU_PAD_CIS_D_4 (10) +#define PMU_PAD_CIS_D_5 (11) +#define PMU_PAD_CIS_D_6 (12) +#define PMU_PAD_CIS_D_7 (13) +#define PMU_PAD_CIS_D_8 (14) +#define PMU_PAD_CIS_D_9 (15) +#define PMU_PAD_CIS_D_10 (16) +#define PMU_PAD_CIS_D_11 (17) +#define PMU_PAD_MAC_REF_CLK (18) +#define PMU_PAD_MAC_MDC (19) +#define PMU_PAD_MAC_MDIO (20) +#define PMU_PAD_MAC_COL (21) +#define PMU_PAD_MAC_CRS (22) +#define PMU_PAD_MAC_RXCK (23) +#define PMU_PAD_MAC_RXD0 (24) +#define PMU_PAD_MAC_RXD1 (25) +#define PMU_PAD_MAC_RXD2 (26) +#define PMU_PAD_MAC_RXD3 (27) +#define PMU_PAD_MAC_RXDV (28) +#define PMU_PAD_MAC_TXCK (29) +#define PMU_PAD_MAC_TXD0 (30) +#define PMU_PAD_MAC_TXD1 (31) +#define PMU_PAD_MAC_TXD2 (32) +#define PMU_PAD_MAC_TXD3 (33) +#define PMU_PAD_MAC_TXEN (34) +#define PMU_PAD_MAC_RXER (35) +#define PMU_PAD_GPIO_0 (36) +#define PMU_PAD_GPIO_1 (37) +#define PMU_PAD_GPIO_2 (38) +#define PMU_PAD_GPIO_3 (39) +#define PMU_PAD_GPIO_4 (40) +#define PMU_PAD_GPIO_5 (41) +#define PMU_PAD_GPIO_6 (42) +#define PMU_PAD_GPIO_7 (43) +#define PMU_PAD_GPIO_8 (44) +#define PMU_PAD_GPIO_9 (45) +#define PMU_PAD_GPIO_10 (46) +#define PMU_PAD_GPIO_11 (47) +#define PMU_PAD_GPIO_12 (48) +#define PMU_PAD_GPIO_13 (49) +#define PMU_PAD_GPIO_14 (50) +#define PMU_PAD_GPIO_15 (51) +#define PMU_PAD_GPIO_16 (52) +#define PMU_PAD_GPIO_17 (53) +#define PMU_PAD_GPIO_18 (54) +#define PMU_PAD_GPIO_19 (55) +#define PMU_PAD_UART0_IN (56) +#define PMU_PAD_UART0_OUT (57) +#define PMU_PAD_CIS_SCL (58) +#define PMU_PAD_CIS_SDA (59) +#define PMU_PAD_SCL1 (60) +#define PMU_PAD_SDA1 (61) +#define PMU_PAD_SSI0_CLK (62) +#define PMU_PAD_SSI0_TXD (63) +#define PMU_PAD_SSI0_CSN_0 (64) +#define PMU_PAD_SSI0_CSN_1 (65) +#define PMU_PAD_SSI0_RXD (66) +#define PMU_PAD_SD0_CD (67) +#define PMU_PAD_SD0_WP (68) +#define PMU_PAD_SD0_CLK (69) +#define PMU_PAD_SD0_CMD_RSP (70) +#define PMU_PAD_SD0_DATA_0 (71) +#define PMU_PAD_SD0_DATA_1 (72) +#define PMU_PAD_SD0_DATA_2 (73) +#define PMU_PAD_SD0_DATA_3 (74) +#define PMU_PAD_SD1_CLK (75) +#define PMU_PAD_SD1_CD (76) +#define PMU_PAD_SD1_WP (77) +#define PMU_PAD_SD1_DATA_0 (78) +#define PMU_PAD_SD1_DATA_1 (79) +#define PMU_PAD_SD1_DATA_2 (80) +#define PMU_PAD_SD1_DATA_3 (81) +#define PMU_PAD_SD1_CMD_RSP (82) +#define PMU_PAD_GPIO_60 (83) +#define PMU_PAD_GPIO_61 (84) +#define PMU_PAD_GPIO_62 (85) +#define PMU_PAD_GPIO_63 (86) +#define PMU_PAD_CLK_SW0 (87) +#define PMU_PAD_CLK_SW1 (88) +#define PMU_PAD_CLK_SW2 (89) +#define PMU_PAD_CLK_SW3 (90) +#define PMU_PAD_CRYSTAL (91) +#define PMU_PAD_MAC_TXER (92) + + +#define IOMUX_PADTYPE(n) (Iomux_PadType##n *) +#define IOMUX_PUPD_NONE 0 +#define IOMUX_PUPD_DOWN 1 +#define IOMUX_PUPD_UP 2 +#define IOMUX_PUPD_KEEPER 3 +//#define IOMUX_DEBUG + + +typedef union +{ + struct + { + UINT32 sr :1; + UINT32 reserved_3_1 :3; + + UINT32 e8_e4 :2; + UINT32 reserved_31_6 :24; + + }bit; + UINT32 dw; +}Iomux_PadType5; + +typedef union +{ + struct + { + UINT32 sr :1; + UINT32 reserved_3_1 :3; + + UINT32 e8_e4 :2; + UINT32 reserved_7_6 :2; + + UINT32 mfs :1; + UINT32 reserved_31_9 :23; + + }bit; + UINT32 dw; +}Iomux_PadType8; + + +typedef union +{ + struct + { + UINT32 smt :1; + UINT32 reserved_3_1 :3; + + UINT32 ie :1; + UINT32 reserved_7_5 :3; + + UINT32 pu_pd :2; + UINT32 reserved_31_10 :22; + + }bit; + UINT32 dw; +}Iomux_PadType9; + + +typedef union +{ + struct + { + UINT32 e4_e2 :2; + UINT32 reserved_3_2 :2; + + UINT32 smt :1; + UINT32 reserved_7_5 :3; + + UINT32 ie :1; + UINT32 reserved_11_9 :3; + + UINT32 mfs :2; + UINT32 reserved_31_14 :18; + + }bit; + UINT32 dw; +}Iomux_PadType13; + +typedef union +{ + struct + { + UINT32 sr :1; + UINT32 reserved_3_1 :3; + + UINT32 e8_e4 :2; + UINT32 reserved_7_6 :2; + + UINT32 smt :1; + UINT32 reserved_11_9 :3; + + UINT32 ie :1; + UINT32 e :1; //only for PAD_MAC_REF_CLK_CFG (0x00a4) + UINT32 reserved_15_12 :2; + + UINT32 pu_pd :2; + UINT32 reserved_31_18 :14; + + }bit; + UINT32 dw; +}Iomux_PadType17; + +typedef union +{ + struct + { + UINT32 sr :1; + UINT32 reserved_3_1 :3; + + UINT32 e4_e2 :2; + UINT32 reserved_7_6 :2; + + UINT32 smt :1; + UINT32 reserved_11_9 :3; + + UINT32 ie :1; + UINT32 reserved_15_13 :3; + + UINT32 pu_pd :2; + UINT32 reserved_19_18 :2; + + UINT32 mfs :1; + UINT32 reserved_31_21 :11; + + }bit; + UINT32 dw; +}Iomux_PadType20; + + +typedef union +{ + struct + { + UINT32 sr :1; + UINT32 reserved_3_1 :3; + + UINT32 e4_e2 :2; + UINT32 reserved_7_6 :2; + + UINT32 smt :1; + UINT32 reserved_11_9 :3; + + UINT32 ie :1; + UINT32 reserved_15_13 :3; + + UINT32 pu_pd :2; + UINT32 reserved_19_18 :2; + + UINT32 mfs :2; + UINT32 reserved_31_21 :10; + + }bit; + UINT32 dw; +}Iomux_PadType21; + +typedef struct +{ + int id; + UINT32* reg; + UINT32 reg_offset; + char* func_name[4]; + int reg_type; + int func_sel; + int drv_cur; + int pupd; + //UINT32 value; +}Iomux_Pad; + +typedef struct +{ + void *vbase; + void *pbase; + Iomux_Pad *pads; +}Iomux_Object; + + +void fh_iomux_init(UINT32 base); +void fh_iomux_pin_switch(int pin_num, int func_num); + + + + + +#endif /* IOMUX_H_ */ + + diff --git a/bsp/fh8620/platform/plat-v2/reset.c b/bsp/fh8620/platform/plat-v2/reset.c new file mode 100644 index 0000000000000000000000000000000000000000..654a0745e97e6b6c19fa10d531469d7aa6499f64 --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/reset.c @@ -0,0 +1,50 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include "fh_pmu.h" +#include "fh_def.h" +#include "fh_arch.h" + +void machine_reset(void) +{ + fh_pmu_write(REG_PMU_SWRST_MAIN_CTRL, 0x7fffffff); +} + +void machine_shutdown(void) +{ + while(1) + ; + +} + +#ifdef RT_USING_FINSH +#include +FINSH_FUNCTION_EXPORT_ALIAS(rt_hw_cpu_reset, reset, system reset); +#endif + +/*@}*/ diff --git a/bsp/fh8620/platform/plat-v2/timer.c b/bsp/fh8620/platform/plat-v2/timer.c new file mode 100644 index 0000000000000000000000000000000000000000..d5bd7f09e44310e3036a87db1ac6e13a4c680aaa --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/timer.c @@ -0,0 +1,109 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#include "timer.h" +#include +#include "fh_arch.h" +#include "Libraries/inc/fh_timer.h" +//#include "fh_pmu.h" +//#include "chip_reg.h" +//NEED_CAUTION. + +#define TIMER_CLOCK 1000000 + +#define CONFIG_PAE_PTS_CLOCK (1000000) +#define TICKS_PER_USEC (CONFIG_PAE_PTS_CLOCK / 1000000) +#define REG_PAE_PTS_REG (0xec100000 + 0x0040) + +static unsigned long lastdec; +static unsigned long long timestamp; + +rt_uint32_t read_pts(void) +{ + return GET_REG(REG_PAE_PTS_REG); +} + +unsigned long long get_ticks(void) +{ + rt_uint32_t now = read_pts(); + if (now >= lastdec) { + /* normal mode */ + timestamp += now - lastdec; + } else { + now = read_pts(); + if (now >= lastdec) + timestamp += now - lastdec; + else { + /* we have an overflow ... */ + timestamp += now + 0xffffffff - lastdec; + } + } + lastdec = now; + return timestamp / (TICKS_PER_USEC * 10); +} + +void udelay(unsigned long usec) +{ + unsigned long long tmp; + rt_uint32_t tmo; + tmo = (usec + 9) / 10; + tmp = get_ticks() + tmo; /* get current timestamp */ + + while (get_ticks() < tmp) + /* loop till event */ + /*NOP*/; +} + +void rt_timer_handler(int vector, void *param) +{ + timer *tim = param; + + rt_interrupt_enter(); + timer_get_eoi(tim); + rt_tick_increase(); + rt_interrupt_leave(); +} + +/** + * This function will init pit for system ticks + */ +void rt_hw_timer_init() +{ + timer *tim = (timer *) TMR_REG_BASE; + timer_init(tim); + /* install interrupt handler */ + rt_hw_interrupt_install(TMR0_IRQn, rt_timer_handler, (void *) tim, + "sys_tick"); + rt_hw_interrupt_umask(TMR0_IRQn); + + timer_set_mode(tim, TIMER_MODE_PERIODIC); + timer_set_period(tim, RT_TICK_PER_SECOND, TIMER_CLOCK); + //timer_set_period(tim, RT_TIMER_TICK_PER_SECOND, TIMER_CLOCK); + timer_enable_irq(tim); + timer_enable(tim); + +} + diff --git a/bsp/fh8620/platform/plat-v2/timer.h b/bsp/fh8620/platform/plat-v2/timer.h new file mode 100644 index 0000000000000000000000000000000000000000..5ee024a094809fb26a3b4044b53db1577ddeda3e --- /dev/null +++ b/bsp/fh8620/platform/plat-v2/timer.h @@ -0,0 +1,36 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef TIMER_H_ +#define TIMER_H_ + + +#include + + +void rt_hw_timer_init(void); + +#endif diff --git a/bsp/fh8620/platform/platform_def.h b/bsp/fh8620/platform/platform_def.h new file mode 100644 index 0000000000000000000000000000000000000000..b511863981988dbecbd0a2d651fcffc32bf3fec4 --- /dev/null +++ b/bsp/fh8620/platform/platform_def.h @@ -0,0 +1,81 @@ +/* + * This file is part of FH8620 BSP for RT-Thread distribution. + * + * Copyright (c) 2016 Shanghai Fullhan Microelectronics Co., Ltd. + * All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Visit http://www.fullhan.com to get contact with Fullhan. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef PLATFORM_DEF_H_ +#define PLATFORM_DEF_H_ + +#include "rtconfig.h" + +#ifdef CONFIG_CHIP_FH8620 +#ifdef CONFIG_BOARD_DEV +#include "fh8620/dev_board/board_def.h" +#endif +#endif + +#ifdef CONFIG_CHIP_FH8620 +#ifdef CONFIG_BOARD_TEST +#include "fh8620/test_board/board_def.h" +#endif +#endif + +#ifdef CONFIG_CHIP_FH8620 +#ifdef CONFIG_BOARD_IOTCAM +#include "fh8620/iot_cam/board_def.h" +#endif +#endif + +#ifdef CONFIG_CHIP_FH8620G +#ifdef CONFIG_BOARD_DEV +#include "fh8620g/dev_board/board_def.h" +#endif +#endif + +#ifdef CONFIG_CHIP_FH8620G +#ifdef CONFIG_BOARD_TEST +#include "fh8620g/test_board/board_def.h" +#endif +#endif + + +#ifdef CONFIG_CHIP_FH8810 +#ifdef CONFIG_BOARD_DEV +#include "fh8810/dev_board/board_def.h" +#endif +#endif + + + +#ifdef CONFIG_CHIP_FH8810 +#ifdef CONFIG_BOARD_TEST +#include "fh8810/test_board/board_def.h" +#endif +#endif + + + + + +#endif /* TEST_H_ */ diff --git a/bsp/fh8620/rtconfig.h b/bsp/fh8620/rtconfig.h new file mode 100644 index 0000000000000000000000000000000000000000..4c0d4579f21af6390c6630e581d64caac07871ca --- /dev/null +++ b/bsp/fh8620/rtconfig.h @@ -0,0 +1,208 @@ +/* RT-Thread config file */ +#ifndef __RTTHREAD_CFG_H__ +#define __RTTHREAD_CFG_H__ + +// + +// +#define RT_NAME_MAX 16 +// +#define RT_ALIGN_SIZE 4 +// +// 8 +// 32 +// 256 +// +#define RT_THREAD_PRIORITY_MAX 256 +// +#define RT_TICK_PER_SECOND 100 +// +#define IDLE_THREAD_STACK_SIZE 512 +// +// #define RT_USING_MODULE +// +#define RT_USING_CPU_FFS +//
+#define RT_DEBUG +// +// #define RT_THREAD_DEBUG +// +#define RT_USING_OVERFLOW_CHECK +//
+ +// +#define RT_USING_HOOK +//
+// #define RT_USING_TIMER_SOFT +// +#define RT_TIMER_THREAD_PRIO 4 +// +#define RT_TIMER_THREAD_STACK_SIZE 512 +// +#define RT_TIMER_TICK_PER_SECOND 100 +//
+ +//
+// +#define RT_USING_SEMAPHORE +// +#define RT_USING_MUTEX +// +#define RT_USING_EVENT +// +#define RT_USING_MAILBOX +// +#define RT_USING_MESSAGEQUEUE +//
+ +//
+// +#define RT_USING_MEMPOOL +// +#define RT_USING_MEMHEAP +// +#define RT_USING_HEAP +// +#define RT_USING_SMALL_MEM +// +// #define RT_USING_SLAB +//
+ +//
+#define RT_USING_DEVICE +// +#define RT_USING_DEVICE_IPC +// +#define RT_USING_SERIAL +// +#define RT_UART_RX_BUFFER_SIZE 64 +//
+// +//#define RT_USING_SPI +// +#define RT_USING_I2C +// +//#define RT_USING_RTC +// +#define RT_MMCSD_THREAD_PREORITY 15 +//
+#define RT_USING_CONSOLE +// +#define RT_CONSOLEBUF_SIZE 128 +// +#define RT_CONSOLE_DEVICE_NAME "uart1" +//
+ +// +#define RT_USING_COMPONENTS_INIT +//
+#define RT_USING_FINSH +#define FINSH_USING_MSH +// +#define FINSH_USING_SYMTAB +// +#define FINSH_USING_DESCRIPTION +// +#define FINSH_THREAD_STACK_SIZE 4096 +//
+ +//
+// +#define RT_USING_LIBC +// +#define RT_USING_PTHREADS +// +#define RT_USING_CPLUSPLUS +//
+ +//
+//#define RT_USING_DFS +// +#define DFS_USING_WORKDIR +// +#define DFS_FILESYSTEMS_MAX 2 +// +#define DFS_FD_MAX 16 +// +#define RT_USING_DFS_ELMFAT +// +#define RT_DFS_ELM_DRIVES 2 +// +#define RT_DFS_ELM_REENTRANT +// +// 1 +// 2 +// 3 +// +#define RT_DFS_ELM_USE_LFN 3 +// +#define RT_DFS_ELM_CODE_PAGE 936 +// +#define RT_DFS_ELM_CODE_PAGE_FILE +// +#define RT_DFS_ELM_MAX_LFN 256 +// +#define RT_DFS_ELM_MAX_SECTOR_SIZE 4096 +// +// #define RT_USING_DFS_YAFFS2 +// +// #define RT_USING_DFS_UFFS +// +#define RT_USING_DFS_DEVFS +// +//#define RT_USING_DFS_ROMFS +//
+ +//
+// #define RT_USING_LWIP +// +#define RT_LWIP_ICMP +// +// #define RT_LWIP_IGMP +// +#define RT_LWIP_UDP +// +#define RT_LWIP_TCP +// +#define RT_LWIP_DNS +// +// #define RT_LWIP_SNMP +// +#define RT_LWIP_DHCP +// +#define RT_LWIP_TCPTHREAD_PRIORITY 12 +// +#define RT_LWIP_TCPTHREAD_MBOX_SIZE 8 +// +#define RT_LWIP_TCPTHREAD_STACKSIZE 4096 +// +#define RT_LWIP_ETHTHREAD_PRIORITY 14 +// +#define RT_LWIP_ETHTHREAD_MBOX_SIZE 8 +// +#define RT_LWIP_ETHTHREAD_STACKSIZE 512 +// +#define RT_LWIP_IPADDR0 192 +#define RT_LWIP_IPADDR1 168 +#define RT_LWIP_IPADDR2 1 +#define RT_LWIP_IPADDR3 30 +// +#define RT_LWIP_GWADDR0 192 +#define RT_LWIP_GWADDR1 168 +#define RT_LWIP_GWADDR2 1 +#define RT_LWIP_GWADDR3 1 +// +#define RT_LWIP_MSKADDR0 255 +#define RT_LWIP_MSKADDR1 255 +#define RT_LWIP_MSKADDR2 255 +#define RT_LWIP_MSKADDR3 0 +//
+ +//
+ +#define CONFIG_BOARD_IOTCAM +#define CONFIG_CHIP_FH8620 +#define CONFIG_PLAT_V2 +#define RT_USING_DMA_MEM + +#endif diff --git a/bsp/fh8620/rtconfig.py b/bsp/fh8620/rtconfig.py new file mode 100644 index 0000000000000000000000000000000000000000..6a4d4e3d10d1038e89ef63495004b26baf2907ea --- /dev/null +++ b/bsp/fh8620/rtconfig.py @@ -0,0 +1,44 @@ +import os +import sys +import re + +# toolchains options +ARCH = 'arm' +CPU = 'armv6' +OUTPUT_NAME = 'rtthread' +CROSS_TOOL = 'gcc' # we use gcc compiler always +PLATFORM = 'gcc' +LD_NAME = 'link' + +EXEC_PATH = r'D:\arm-2013.11\bin' +if os.getenv('RTT_EXEC_PATH'): + EXEC_PATH = os.getenv('RTT_EXEC_PATH') + +BUILD = 'release' + +if PLATFORM == 'gcc': + # toolchains + PREFIX = 'arm-none-eabi-' + CC = PREFIX + 'gcc' + CXX = PREFIX + 'g++' + AS = PREFIX + 'gcc' + AR = PREFIX + 'ar' + LINK = PREFIX + 'gcc' + TARGET_EXT = '.elf' + SIZE = PREFIX + 'size' + OBJDUMP = PREFIX + 'objdump' + OBJCPY = PREFIX + 'objcopy' + DEVICE = ' -mcpu=arm1176jzf-s -mfpu=vfp -mfloat-abi=soft' + CFLAGS = DEVICE + ' -mno-unaligned-access' + AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__' + LFLAGS = DEVICE + ' -Wl,--gc-sections,-Map='+ OUTPUT_NAME +'.map,-cref,-u,_start -T' + LD_NAME +'.ld' + CPATH = '' + LPATH = '' + if BUILD == 'debug': + CFLAGS += ' -O0 -gdwarf-2 ' + AFLAGS += ' -gdwarf-2' + else: + CFLAGS += ' -O2' + + CXXFLAGS = CFLAGS + POST_ACTION = OBJCPY + ' -O binary $TARGET '+ OUTPUT_NAME +'.bin\n' + SIZE + ' $TARGET \n' diff --git a/libcpu/arm/armv6/arm_entry_gcc.S b/libcpu/arm/armv6/arm_entry_gcc.S new file mode 100644 index 0000000000000000000000000000000000000000..f20a384e84bfc91af82d7c28272898cce723a2af --- /dev/null +++ b/libcpu/arm/armv6/arm_entry_gcc.S @@ -0,0 +1,145 @@ +/* + * File : arm_entry_gcc.S + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2014-11-07 weety first version + */ + +#include + +#include "armv6.h" + +//#define DEBUG + +.macro PRINT, str +#ifdef DEBUG + stmfd sp!, {r0-r3, ip, lr} + add r0, pc, #4 + bl rt_kprintf + b 1f + .asciz "UNDEF: \str\n" + .balign 4 +1: ldmfd sp!, {r0-r3, ip, lr} +#endif + .endm + +.macro PRINT1, str, arg +#ifdef DEBUG + stmfd sp!, {r0-r3, ip, lr} + mov r1, \arg + add r0, pc, #4 + bl rt_kprintf + b 1f + .asciz "UNDEF: \str\n" + .balign 4 +1: ldmfd sp!, {r0-r3, ip, lr} +#endif + .endm + +.macro PRINT3, str, arg1, arg2, arg3 +#ifdef DEBUG + stmfd sp!, {r0-r3, ip, lr} + mov r3, \arg3 + mov r2, \arg2 + mov r1, \arg1 + add r0, pc, #4 + bl rt_kprintf + b 1f + .asciz "UNDEF: \str\n" + .balign 4 +1: ldmfd sp!, {r0-r3, ip, lr} +#endif + .endm + +.macro get_current_thread, rd + ldr \rd, .current_thread + ldr \rd, [\rd] + .endm + +.current_thread: + .word rt_current_thread + +#ifdef RT_USING_NEON + .align 6 + +/* is the neon instuction on arm mode? */ +.neon_opcode: + .word 0xfe000000 @ mask + .word 0xf2000000 @ opcode + + .word 0xff100000 @ mask + .word 0xf4000000 @ opcode + + .word 0x00000000 @ end mask + .word 0x00000000 @ end opcode +#endif + +/* undefined instruction exception processing */ +.globl undef_entry +undef_entry: + PRINT1 "r0=0x%08x", r0 + PRINT1 "r2=0x%08x", r2 + PRINT1 "r9=0x%08x", r9 + PRINT1 "sp=0x%08x", sp + +#ifdef RT_USING_NEON + ldr r6, .neon_opcode +__check_neon_instruction: + ldr r7, [r6], #4 @ load mask value + cmp r7, #0 @ end mask? + beq __check_vfp_instruction + and r8, r0, r7 + ldr r7, [r6], #4 @ load opcode value + cmp r8, r7 @ is NEON instruction? + bne __check_neon_instruction + b vfp_entry +__check_vfp_instruction: +#endif + tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC instruction has bit 27 + tstne r0, #0x04000000 @ bit 26 set on both ARM and Thumb-2 instruction + moveq pc, lr @ no vfp coprocessor instruction, return + get_current_thread r10 + and r8, r0, #0x00000f00 @ get coprocessor number + PRINT1 "CP=0x%08x", r8 + add pc, pc, r8, lsr #6 + nop + mov pc, lr @ CP0 + mov pc, lr @ CP1 + mov pc, lr @ CP2 + mov pc, lr @ CP3 + mov pc, lr @ CP4 + mov pc, lr @ CP5 + mov pc, lr @ CP6 + mov pc, lr @ CP7 + mov pc, lr @ CP8 + mov pc, lr @ CP9 +#ifdef RT_USING_VFP + b vfp_entry @ CP10 VFP + b vfp_entry @ CP11 VFP +#else + mov pc, lr @ CP10 VFP + mov pc, lr @ CP11 VFP +#endif + mov pc, lr @ CP12 + mov pc, lr @ CP13 + mov pc, lr @ CP14 DEBUG + mov pc, lr @ CP15 SYS CONTROL + + diff --git a/libcpu/arm/armv6/armv6.h b/libcpu/arm/armv6/armv6.h new file mode 100644 index 0000000000000000000000000000000000000000..860f09661fd40e97758b3d3815f43d5ce505d9a1 --- /dev/null +++ b/libcpu/arm/armv6/armv6.h @@ -0,0 +1,107 @@ +/* + * File : armv6.h + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __ARMV6_H__ +#define __ARMV6_H__ + + +/*****************************/ +/* CPU Mode */ +/*****************************/ +#define USERMODE 0x10 +#define FIQMODE 0x11 +#define IRQMODE 0x12 +#define SVCMODE 0x13 +#define ABORTMODE 0x17 +#define UNDEFMODE 0x1b +#define MODEMASK 0x1f +#define NOINT 0xc0 + +#ifndef __ASSEMBLY__ +struct rt_hw_register +{ + rt_uint32_t cpsr; + rt_uint32_t r0; + rt_uint32_t r1; + rt_uint32_t r2; + rt_uint32_t r3; + rt_uint32_t r4; + rt_uint32_t r5; + rt_uint32_t r6; + rt_uint32_t r7; + rt_uint32_t r8; + rt_uint32_t r9; + rt_uint32_t r10; + rt_uint32_t fp; + rt_uint32_t ip; + rt_uint32_t sp; + rt_uint32_t lr; + rt_uint32_t pc; +}; +#if(0) +struct rt_hw_register{ + rt_uint32_t r0; + rt_uint32_t r1; + rt_uint32_t r2; + rt_uint32_t r3; + rt_uint32_t r4; + rt_uint32_t r5; + rt_uint32_t r6; + rt_uint32_t r7; + rt_uint32_t r8; + rt_uint32_t r9; + rt_uint32_t r10; + rt_uint32_t fp; + rt_uint32_t ip; + rt_uint32_t sp; + rt_uint32_t lr; + rt_uint32_t pc; + rt_uint32_t cpsr; + rt_uint32_t ORIG_r0; +}; +#endif +#endif + +/* rt_hw_register offset */ +#define S_FRAME_SIZE 68 + +#define S_PC 64 +#define S_LR 60 +#define S_SP 56 +#define S_IP 52 +#define S_FP 48 +#define S_R10 44 +#define S_R9 40 +#define S_R8 36 +#define S_R7 32 +#define S_R6 28 +#define S_R5 24 +#define S_R4 20 +#define S_R3 16 +#define S_R2 12 +#define S_R1 8 +#define S_R0 4 +#define S_CPSR 0 + + +#endif diff --git a/libcpu/arm/armv6/context_gcc.S b/libcpu/arm/armv6/context_gcc.S new file mode 100644 index 0000000000000000000000000000000000000000..87ba43ae5221d764d14747d9814da7dc2450aca7 --- /dev/null +++ b/libcpu/arm/armv6/context_gcc.S @@ -0,0 +1,122 @@ +/* + * File : context.S + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2011-01-13 weety copy from mini2440 + */ + +/*! + * \addtogroup ARMv6 + */ +/*@{*/ + +#include + +#define NOINT 0xc0 +#define FPEXC_EN (1 << 30) /* VFP enable bit */ + +/* + * rt_base_t rt_hw_interrupt_disable(); + */ +.globl rt_hw_interrupt_disable +rt_hw_interrupt_disable: + mrs r0, cpsr + cpsid if + bx lr + +/* + * void rt_hw_interrupt_enable(rt_base_t level); + */ +.globl rt_hw_interrupt_enable +rt_hw_interrupt_enable: + msr cpsr_c, r0 + bx lr + +/* + * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to); + * r0 --> from + * r1 --> to + */ +.globl rt_hw_context_switch +rt_hw_context_switch: + stmfd sp!, {lr} @ push pc (lr should be pushed in place of PC) + stmfd sp!, {r0-r12, lr} @ push lr & register file + + mrs r4, cpsr + tst lr, #0x01 + orrne r4, r4, #0x20 @ it's thumb code + + stmfd sp!, {r4} @ push cpsr + + str sp, [r0] @ store sp in preempted tasks TCB + ldr sp, [r1] @ get new task stack pointer + + ldmfd sp!, {r4} @ pop new task cpsr to spsr + msr spsr_cxsf, r4 +#ifdef RT_USING_VFP + vmrs r0, fpexc + bic r0, r0, #FPEXC_EN @ always disable VFP so we can + @ lazily save/restore the old state. + vmsr fpexc, r0 +#endif +_do_switch: + ldmfd sp!, {r0-r12, lr, pc}^ @ pop new task r0-r12, lr & pc, copy spsr to cpsr + +/* + * void rt_hw_context_switch_to(rt_uint32 to); + * r0 --> to + */ +.globl rt_hw_context_switch_to +rt_hw_context_switch_to: + ldr sp, [r0] @ get new task stack pointer + + ldmfd sp!, {r4} @ pop new task spsr + msr spsr_cxsf, r4 + + bic r4, r4, #0x20 @ must be ARM mode + msr cpsr_cxsf, r4 +#ifdef RT_USING_VFP + vmrs r0, fpexc + bic r0, r0, #FPEXC_EN @ always disable VFP so we can + @ lazily save/restore the old state. + vmsr fpexc, r0 +#endif + ldmfd sp!, {r0-r12, lr, pc}^ @ pop new task r0-r12, lr & pc + +/* + * void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to); + */ +.globl rt_thread_switch_interrupt_flag +.globl rt_interrupt_from_thread +.globl rt_interrupt_to_thread +.globl rt_hw_context_switch_interrupt +rt_hw_context_switch_interrupt: + ldr r2, =rt_thread_switch_interrupt_flag + ldr r3, [r2] + cmp r3, #1 + beq _reswitch + mov r3, #1 @ set rt_thread_switch_interrupt_flag to 1 + str r3, [r2] + ldr r2, =rt_interrupt_from_thread @ set rt_interrupt_from_thread + str r0, [r2] +_reswitch: + ldr r2, =rt_interrupt_to_thread @ set rt_interrupt_to_thread + str r1, [r2] + bx lr diff --git a/libcpu/arm/armv6/cpuport.c b/libcpu/arm/armv6/cpuport.c new file mode 100644 index 0000000000000000000000000000000000000000..a0578a6d69844540704d16fa3bde5119f21c4779 --- /dev/null +++ b/libcpu/arm/armv6/cpuport.c @@ -0,0 +1,248 @@ +/* + * File : cpu.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Develop Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2011-01-13 weety modified from mini2440 + */ + +#include +#include + +#define ICACHE_MASK (rt_uint32_t)(1 << 12) +#define DCACHE_MASK (rt_uint32_t)(1 << 2) + +extern void machine_reset(void); +extern void machine_shutdown(void); + +#ifdef __GNUC__ +rt_inline rt_uint32_t cp15_rd(void) +{ + rt_uint32_t i; + + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + return i; +} + +rt_inline void cache_enable(rt_uint32_t bit) +{ + __asm__ __volatile__( \ + "mrc p15,0,r0,c1,c0,0\n\t" \ + "orr r0,r0,%0\n\t" \ + "mcr p15,0,r0,c1,c0,0" \ + : \ + :"r" (bit) \ + :"memory"); +} + +rt_inline void cache_disable(rt_uint32_t bit) +{ + __asm__ __volatile__( \ + "mrc p15,0,r0,c1,c0,0\n\t" \ + "bic r0,r0,%0\n\t" \ + "mcr p15,0,r0,c1,c0,0" \ + : \ + :"r" (bit) \ + :"memory"); +} + + +#endif + +#ifdef __CC_ARM +rt_inline rt_uint32_t cp15_rd(void) +{ + rt_uint32_t i; + + __asm + { + mrc p15, 0, i, c1, c0, 0 + } + + return i; +} + +rt_inline void cache_enable(rt_uint32_t bit) +{ + rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + orr value, value, bit + mcr p15, 0, value, c1, c0, 0 + } +} + +rt_inline void cache_disable(rt_uint32_t bit) +{ + rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + bic value, value, bit + mcr p15, 0, value, c1, c0, 0 + } +} +#endif + +/** + * enable I-Cache + * + */ +void rt_hw_cpu_icache_enable() +{ + cache_enable(ICACHE_MASK); +} + +/** + * disable I-Cache + * + */ +void rt_hw_cpu_icache_disable() +{ + cache_disable(ICACHE_MASK); +} + +/** + * return the status of I-Cache + * + */ +rt_base_t rt_hw_cpu_icache_status() +{ + return (cp15_rd() & ICACHE_MASK); +} + +/** + * enable D-Cache + * + */ +void rt_hw_cpu_dcache_enable() +{ + cache_enable(DCACHE_MASK); +} + +/** + * disable D-Cache + * + */ +void rt_hw_cpu_dcache_disable() +{ + cache_disable(DCACHE_MASK); +} + +/** + * return the status of D-Cache + * + */ +rt_base_t rt_hw_cpu_dcache_status() +{ + return (cp15_rd() & DCACHE_MASK); +} + +/** + * reset cpu by dog's time-out + * + */ +void rt_hw_cpu_reset() +{ + + rt_kprintf("Restarting system...\n"); + machine_reset(); + + while(1); /* loop forever and wait for reset to happen */ + + /* NEVER REACHED */ +} + +/** + * shutdown CPU + * + */ +void rt_hw_cpu_shutdown() +{ + rt_uint32_t level; + rt_kprintf("shutdown...\n"); + + level = rt_hw_interrupt_disable(); + machine_shutdown(); + while (level) + { + RT_ASSERT(0); + } +} + +#ifdef RT_USING_CPU_FFS +/** + * This function finds the first bit set (beginning with the least significant bit) + * in value and return the index of that bit. + * + * Bits are numbered starting at 1 (the least significant bit). A return value of + * zero from any of these functions means that the argument was zero. + * + * @return return the index of the first bit set. If value is 0, then this function + * shall return 0. + */ +#if defined(__CC_ARM) +int __rt_ffs(int value) +{ + register rt_uint32_t x; + + if (value == 0) + return value; + + __asm + { + rsb x, value, #0 + and x, x, value + clz x, x + rsb x, x, #32 + } + + return x; +} +#elif defined(__IAR_SYSTEMS_ICC__) +int __rt_ffs(int value) +{ + if (value == 0) + return value; + + __ASM("RSB r4, r0, #0"); + __ASM("AND r4, r4, r0"); + __ASM("CLZ r4, r4"); + __ASM("RSB r0, r4, #32"); +} +#elif defined(__GNUC__) +int __rt_ffs(int value) +{ + if (value == 0) + return value; + + value &= (-value); + asm ("clz %0, %1": "=r"(value) :"r"(value)); + + return (32 - value); +} +#endif + +#endif + + +/*@}*/ diff --git a/libcpu/arm/armv6/mmu.c b/libcpu/arm/armv6/mmu.c new file mode 100644 index 0000000000000000000000000000000000000000..9a6ebd4e6f7cec623b421e5411959b7f0cd62922 --- /dev/null +++ b/libcpu/arm/armv6/mmu.c @@ -0,0 +1,562 @@ +/* + * File : mmu.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + */ + +#include "mmu.h" + +#ifdef __CC_ARM +void mmu_setttbase(rt_uint32_t i) +{ + register rt_uint32_t value; + + /* Invalidates all TLBs.Domain access is selected as + * client by configuring domain access register, + * in that case access controlled by permission value + * set by page table entry + */ + value = 0; + __asm + { + mcr p15, 0, value, c8, c7, 0 + } + + value = 0x55555555; + __asm + { + mcr p15, 0, value, c3, c0, 0 + mcr p15, 0, i, c2, c0, 0 + } +} + +void mmu_set_domain(rt_uint32_t i) +{ + __asm + { + mcr p15,0, i, c3, c0, 0 + } +} + +void mmu_enable() +{ + register rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + orr value, value, #0x01 + mcr p15, 0, value, c1, c0, 0 + } +} + +void mmu_disable() +{ + register rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + bic value, value, #0x01 + mcr p15, 0, value, c1, c0, 0 + } +} + +void mmu_enable_icache() +{ + register rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + orr value, value, #0x1000 + mcr p15, 0, value, c1, c0, 0 + } +} + +void mmu_enable_dcache() +{ + register rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + orr value, value, #0x04 + mcr p15, 0, value, c1, c0, 0 + } +} + +void mmu_disable_icache() +{ + register rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + bic value, value, #0x1000 + mcr p15, 0, value, c1, c0, 0 + } +} + +void mmu_disable_dcache() +{ + register rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + bic value, value, #0x04 + mcr p15, 0, value, c1, c0, 0 + } +} + +void mmu_enable_alignfault() +{ + register rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + orr value, value, #0x02 + mcr p15, 0, value, c1, c0, 0 + } +} + +void mmu_disable_alignfault() +{ + register rt_uint32_t value; + + __asm + { + mrc p15, 0, value, c1, c0, 0 + bic value, value, #0x02 + mcr p15, 0, value, c1, c0, 0 + } +} + +void mmu_clean_invalidated_cache_index(int index) +{ + __asm + { + mcr p15, 0, index, c7, c14, 2 + } +} + +void mmu_clean_invalidated_dcache(rt_uint32_t buffer, rt_uint32_t size) +{ + unsigned int ptr; + + ptr = buffer & ~(CACHE_LINE_SIZE - 1); + + while(ptr < buffer + size) + { + __asm + { + MCR p15, 0, ptr, c7, c14, 1 + } + ptr += CACHE_LINE_SIZE; + } +} + +void mmu_clean_dcache(rt_uint32_t buffer, rt_uint32_t size) +{ + unsigned int ptr; + + ptr = buffer & ~(CACHE_LINE_SIZE - 1); + + while (ptr < buffer + size) + { + __asm + { + MCR p15, 0, ptr, c7, c10, 1 + } + ptr += CACHE_LINE_SIZE; + } +} + +void mmu_invalidate_dcache(rt_uint32_t buffer, rt_uint32_t size) +{ + unsigned int ptr; + + ptr = buffer & ~(CACHE_LINE_SIZE - 1); + + while (ptr < buffer + size) + { + __asm + { + MCR p15, 0, ptr, c7, c6, 1 + } + ptr += CACHE_LINE_SIZE; + } +} + +void mmu_invalidate_tlb() +{ + register rt_uint32_t value; + + value = 0; + __asm + { + mcr p15, 0, value, c8, c7, 0 + } +} + +void mmu_invalidate_icache() +{ + register rt_uint32_t value; + + value = 0; + + __asm + { + mcr p15, 0, value, c7, c5, 0 + } +} + + +void mmu_invalidate_dcache_all() +{ + register rt_uint32_t value; + + value = 0; + + __asm + { + mcr p15, 0, value, c7, c6, 0 + } +} +#elif defined(__GNUC__) +void mmu_setttbase(register rt_uint32_t i) +{ + register rt_uint32_t value; + + /* Invalidates all TLBs.Domain access is selected as + * client by configuring domain access register, + * in that case access controlled by permission value + * set by page table entry + */ + value = 0; + asm ("mcr p15, 0, %0, c8, c7, 0"::"r"(value)); + + value = 0x55555555; + asm ("mcr p15, 0, %0, c3, c0, 0"::"r"(value)); + asm ("mcr p15, 0, %0, c2, c0, 0"::"r"(i)); +} + +void mmu_set_domain(register rt_uint32_t i) +{ + asm ("mcr p15,0, %0, c3, c0, 0": :"r" (i)); +} + +void mmu_enable() +{ + register rt_uint32_t i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + i |= 0x1; + /* Enables the extended page tables to be configured for + the hardware page translation mechanism, Subpage AP bits disabled */ + i |= (1 << 23); /* support for ARMv6 MMU features */ + i |= (1 << 13); /* High exception vectors selected, address range = 0xFFFF0000-0xFFFF001C */ + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); +} + +void mmu_disable() +{ + register rt_uint32_t i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + i &= ~0x1; + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); +} + +void mmu_enable_icache() +{ + register rt_uint32_t i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + i |= (1 << 12); + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); +} + +void mmu_enable_dcache() +{ + register rt_uint32_t i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + i |= (1 << 2); + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); +} + +void mmu_disable_icache() +{ + register rt_uint32_t i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + i &= ~(1 << 12); + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); +} + +void mmu_disable_dcache() +{ + register rt_uint32_t i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + i &= ~(1 << 2); + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); +} + +void mmu_enable_alignfault() +{ + register rt_uint32_t i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + i |= (1 << 1); + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); +} + +void mmu_disable_alignfault() +{ + register rt_uint32_t i; + + /* read control register */ + asm ("mrc p15, 0, %0, c1, c0, 0":"=r" (i)); + + i &= ~(1 << 1); + + /* write back to control register */ + asm ("mcr p15, 0, %0, c1, c0, 0": :"r" (i)); +} + +void mmu_clean_invalidated_cache_index(int index) +{ + asm ("mcr p15, 0, %0, c7, c14, 2": :"r" (index)); +} + +void mmu_clean_invalidated_dcache(rt_uint32_t buffer, rt_uint32_t size) +{ + unsigned int ptr; + + ptr = buffer & ~(CACHE_LINE_SIZE - 1); + + while(ptr < buffer + size) + { + asm ("mcr p15, 0, %0, c7, c14, 1": :"r" (ptr)); + ptr += CACHE_LINE_SIZE; + } +} + + +void mmu_clean_dcache(rt_uint32_t buffer, rt_uint32_t size) +{ + unsigned int ptr; + + ptr = buffer & ~(CACHE_LINE_SIZE - 1); + + while (ptr < buffer + size) + { + asm ("mcr p15, 0, %0, c7, c10, 1": :"r" (ptr)); + ptr += CACHE_LINE_SIZE; + } +} + +void mmu_invalidate_dcache(rt_uint32_t buffer, rt_uint32_t size) +{ + unsigned int ptr; + + ptr = buffer & ~(CACHE_LINE_SIZE - 1); + + while (ptr < buffer + size) + { + asm ("mcr p15, 0, %0, c7, c6, 1": :"r" (ptr)); + ptr += CACHE_LINE_SIZE; + } +} + +void mmu_invalidate_tlb() +{ + asm ("mcr p15, 0, %0, c8, c7, 0": :"r" (0)); +} + +void mmu_invalidate_icache() +{ + asm ("mcr p15, 0, %0, c7, c5, 0": :"r" (0)); +} + +void mmu_invalidate_dcache_all() +{ + asm ("mcr p15, 0, %0, c7, c6, 0": :"r" (0)); +} +#endif + +/* level1 page table */ +static volatile unsigned int _pgd_table[4*1024] ALIGN(16*1024); +/* + * level2 page table + * RT_MMU_PTE_SIZE must be 1024*n + */ +#define RT_MMU_PTE_SIZE 4096 +static volatile unsigned int _pte_table[RT_MMU_PTE_SIZE] ALIGN(1*1024); + +void mmu_create_pgd(struct mem_desc *mdesc) +{ + volatile rt_uint32_t *pTT; + volatile int i, nSec; + pTT = (rt_uint32_t *)_pgd_table + (mdesc->vaddr_start >> 20); + nSec = (mdesc->vaddr_end >> 20) - (mdesc->vaddr_start >> 20); + for(i = 0; i <= nSec; i++) + { + *pTT = mdesc->sect_attr | (((mdesc->paddr_start >> 20) + i) << 20); + pTT++; + } +} + +void mmu_create_pte(struct mem_desc *mdesc) +{ + volatile rt_uint32_t *pTT; + volatile rt_uint32_t *p_pteentry; + int i; + rt_uint32_t vaddr; + rt_uint32_t total_page = 0; + rt_uint32_t pte_offset = 0; + rt_uint32_t sect_attr = 0; + + total_page = (mdesc->vaddr_end >> 12) - (mdesc->vaddr_start >> 12) + 1; + pte_offset = mdesc->sect_attr & 0xfffffc00; + sect_attr = mdesc->sect_attr & 0x3ff; + vaddr = mdesc->vaddr_start; + + for(i = 0; i < total_page; i++) + { + pTT = (rt_uint32_t *)_pgd_table + (vaddr >> 20); + if (*pTT == 0) /* Level 1 page table item not used, now update pgd item */ + { + *pTT = pte_offset | sect_attr; + p_pteentry = (rt_uint32_t *)pte_offset + + ((vaddr & 0x000ff000) >> 12); + pte_offset += 1024; + } + else /* using old Level 1 page table item */ + { + p_pteentry = (rt_uint32_t *)(*pTT & 0xfffffc00) + + ((vaddr & 0x000ff000) >> 12); + } + + + *p_pteentry = mdesc->page_attr | (((mdesc->paddr_start >> 12) + i) << 12); + vaddr += 0x1000; + } +} + +static void build_pte_mem_desc(struct mem_desc *mdesc, rt_uint32_t size) +{ + rt_uint32_t pte_offset = 0; + rt_uint32_t nsec = 0; + /* set page table */ + for (; size > 0; size--) + { + if (mdesc->mapped_mode == PAGE_MAPPED) + { + nsec = (RT_ALIGN(mdesc->vaddr_end, 0x100000) - RT_ALIGN_DOWN(mdesc->vaddr_start, 0x100000)) >> 20; + mdesc->sect_attr |= (((rt_uint32_t)_pte_table)& 0xfffffc00) + pte_offset; + pte_offset += nsec << 10; + } + if (pte_offset >= RT_MMU_PTE_SIZE) + { + rt_kprintf("PTE table size too little\n"); + RT_ASSERT(0); + } + + mdesc++; + } +} + + +void rt_hw_mmu_init(struct mem_desc *mdesc, rt_uint32_t size) +{ + /* disable I/D cache */ + mmu_disable_dcache(); + mmu_disable_icache(); + mmu_disable(); + mmu_invalidate_tlb(); + + /* clear pgd and pte table */ + rt_memset((void *)_pgd_table, 0, 16*1024); + rt_memset((void *)_pte_table, 0, RT_MMU_PTE_SIZE); + build_pte_mem_desc(mdesc, size); + /* set page table */ + for (; size > 0; size--) + { + if (mdesc->mapped_mode == SECT_MAPPED) + { + mmu_create_pgd(mdesc); + } + else + { + mmu_create_pte(mdesc); + } + + mdesc++; + } + + /* set MMU table address */ + mmu_setttbase((rt_uint32_t)_pgd_table); + + /* enables MMU */ + mmu_enable(); + + /* enable Instruction Cache */ + mmu_enable_icache(); + + /* enable Data Cache */ + mmu_enable_dcache(); + + mmu_invalidate_icache(); + mmu_invalidate_dcache_all(); +} + diff --git a/libcpu/arm/armv6/mmu.h b/libcpu/arm/armv6/mmu.h new file mode 100644 index 0000000000000000000000000000000000000000..f6e788d13d705794fd172ff3554eb081376a97bb --- /dev/null +++ b/libcpu/arm/armv6/mmu.h @@ -0,0 +1,208 @@ +/* + * File : mmu.h + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __MMU_H__ +#define __MMU_H__ + +#include + +#define CACHE_LINE_SIZE 32 + +/* + * Hardware page table definitions. + * + * + Level 1 descriptor (PGD) + * - common + */ +#define PGD_TYPE_MASK (3 << 0) +#define PGD_TYPE_FAULT (0 << 0) +#define PGD_TYPE_TABLE (1 << 0) +#define PGD_TYPE_SECT (2 << 0) +#define PGD_BIT4 (1 << 4) +#define PGD_DOMAIN(x) ((x) << 5) +#define PGD_PROTECTION (1 << 9) /* ARMv5 */ +/* + * - section + */ +#define PGD_SECT_BUFFERABLE (1 << 2) +#define PGD_SECT_CACHEABLE (1 << 3) +#define PGD_SECT_XN (1 << 4) /* ARMv6 */ +#define PGD_SECT_AP0 (1 << 10) +#define PGD_SECT_AP1 (1 << 11) +#define PGD_SECT_TEX(x) ((x) << 12) /* ARMv5 */ +#define PGD_SECT_APX (1 << 15) /* ARMv6 */ +#define PGD_SECT_S (1 << 16) /* ARMv6 */ +#define PGD_SECT_nG (1 << 17) /* ARMv6 */ +#define PGD_SECT_SUPER (1 << 18) /* ARMv6 */ + +#define PGD_SECT_UNCACHED (0) +#define PGD_SECT_BUFFERED (PGD_SECT_BUFFERABLE) +#define PGD_SECT_WT (PGD_SECT_CACHEABLE) +#define PGD_SECT_WB (PGD_SECT_CACHEABLE | PGD_SECT_BUFFERABLE) +#define PGD_SECT_MINICACHE (PGD_SECT_TEX(1) | PGD_SECT_CACHEABLE) +#define PGD_SECT_WBWA (PGD_SECT_TEX(1) | PGD_SECT_CACHEABLE | PGD_SECT_BUFFERABLE) +#define PGD_SECT_NONSHARED_DEV (PGD_SECT_TEX(2)) + + +/* + * + Level 2 descriptor (PTE) + * - common + */ +#define PTE_TYPE_MASK (3 << 0) +#define PTE_TYPE_FAULT (0 << 0) +#define PTE_TYPE_LARGE (1 << 0) +#define PTE_TYPE_SMALL (2 << 0) +#define PTE_TYPE_EXT (3 << 0) /* ARMv5 */ +#define PTE_BUFFERABLE (1 << 2) +#define PTE_CACHEABLE (1 << 3) + +/* + * - extended small page/tiny page + */ +#define PTE_EXT_XN (1 << 0) /* ARMv6 */ +#define PTE_EXT_AP_MASK (3 << 4) +#define PTE_EXT_AP0 (1 << 4) +#define PTE_EXT_AP1 (2 << 4) +#define PTE_EXT_AP_UNO_SRO (0 << 4) +#define PTE_EXT_AP_UNO_SRW (PTE_EXT_AP0) +#define PTE_EXT_AP_URO_SRW (PTE_EXT_AP1) +#define PTE_EXT_AP_URW_SRW (PTE_EXT_AP1|PTE_EXT_AP0) +#define PTE_EXT_TEX(x) ((x) << 6) /* ARMv5 */ +#define PTE_EXT_APX (1 << 9) /* ARMv6 */ +#define PTE_EXT_SHARED (1 << 10) /* ARMv6 */ +#define PTE_EXT_NG (1 << 11) /* ARMv6 */ + +/* + * - small page + */ +#define PTE_SMALL_AP_MASK (0xff << 4) +#define PTE_SMALL_AP_UNO_SRO (0x00 << 4) +#define PTE_SMALL_AP_UNO_SRW (0x55 << 4) +#define PTE_SMALL_AP_URO_SRW (0xaa << 4) +#define PTE_SMALL_AP_URW_SRW (0xff << 4) + + +/* + * sector table properities + */ +#define SECT_CB (PGD_SECT_CACHEABLE|PGD_SECT_BUFFERABLE) //cache_on, write_back +#define SECT_CNB (PGD_SECT_CACHEABLE) //cache_on, write_through +#define SECT_NCB (PGD_SECT_BUFFERABLE) //cache_off,WR_BUF on +#define SECT_NCNB (0 << 2) //cache_off,WR_BUF off + +#define SECT_AP_RW (PGD_SECT_AP0|PGD_SECT_AP1) //supervisor=RW, user=RW +#define SECT_AP_RO (PGD_SECT_AP0|PGD_SECT_AP1|PGD_SECT_APX) //supervisor=RO, user=RO + +#define SECT_RWX_CB (SECT_AP_RW|PGD_DOMAIN(0)|PGD_SECT_WB|PGD_TYPE_SECT) /* Read/Write/executable, cache, write back */ +#define SECT_RWX_CNB (SECT_AP_RW|PGD_DOMAIN(0)|PGD_SECT_WT|PGD_TYPE_SECT) /* Read/Write/executable, cache, write through */ +#define SECT_RWX_NCNB (SECT_AP_RW|PGD_DOMAIN(0)|PGD_TYPE_SECT) /* Read/Write/executable without cache and write buffer */ +#define SECT_RWX_FAULT (SECT_AP_RW|PGD_DOMAIN(1)|PGD_TYPE_SECT) /* Read/Write without cache and write buffer */ + +#define SECT_RWNX_CB (SECT_AP_RW|PGD_DOMAIN(0)|PGD_SECT_WB|PGD_TYPE_SECT|PGD_SECT_XN) /* Read/Write, cache, write back */ +#define SECT_RWNX_CNB (SECT_AP_RW|PGD_DOMAIN(0)|PGD_SECT_WT|PGD_TYPE_SECT|PGD_SECT_XN) /* Read/Write, cache, write through */ +#define SECT_RWNX_NCNB (SECT_AP_RW|PGD_DOMAIN(0)|PGD_TYPE_SECT|PGD_SECT_XN) /* Read/Write without cache and write buffer */ +#define SECT_RWNX_FAULT (SECT_AP_RW|PGD_DOMAIN(1)|PGD_TYPE_SECT|PGD_SECT_XN) /* Read/Write without cache and write buffer */ + + +#define SECT_ROX_CB (SECT_AP_RO|PGD_DOMAIN(0)|PGD_SECT_WB|PGD_TYPE_SECT) /* Read Only/executable, cache, write back */ +#define SECT_ROX_CNB (SECT_AP_RO|PGD_DOMAIN(0)|PGD_SECT_WT|PGD_TYPE_SECT) /* Read Only/executable, cache, write through */ +#define SECT_ROX_NCNB (SECT_AP_RO|PGD_DOMAIN(0)|PGD_TYPE_SECT) /* Read Only/executable without cache and write buffer */ +#define SECT_ROX_FAULT (SECT_AP_RO|PGD_DOMAIN(1)|PGD_TYPE_SECT) /* Read Only without cache and write buffer */ + +#define SECT_RONX_CB (SECT_AP_RO|PGD_DOMAIN(0)|PGD_SECT_WB|PGD_TYPE_SECT|PGD_SECT_XN) /* Read Only, cache, write back */ +#define SECT_RONX_CNB (SECT_AP_RO|PGD_DOMAIN(0)|PGD_SECT_WT|PGD_TYPE_SECT|PGD_SECT_XN) /* Read Only, cache, write through */ +#define SECT_RONX_NCNB (SECT_AP_RO|PGD_DOMAIN(0)|PGD_TYPE_SECT|PGD_SECT_XN) /* Read Only without cache and write buffer */ +#define SECT_RONX_FAULT (SECT_AP_RO|PGD_DOMAIN(1)|PGD_TYPE_SECT|PGD_SECT_XN) /* Read Only without cache and write buffer */ + +#define SECT_TO_PAGE (PGD_DOMAIN(0)|PGD_TYPE_TABLE) /* Level 2 descriptor (PTE) entry properity */ + +/* + * page table properities + */ +#define PAGE_CB (PTE_BUFFERABLE|PTE_CACHEABLE) //cache_on, write_back +#define PAGE_CNB (PTE_CACHEABLE) //cache_on, write_through +#define PAGE_NCB (PTE_BUFFERABLE) //cache_off,WR_BUF on +#define PAGE_NCNB (0 << 2) //cache_off,WR_BUF off + +#define PAGE_AP_RW (PTE_EXT_AP0|PTE_EXT_AP1) //supervisor=RW, user=RW +#define PAGE_AP_RO (PTE_EXT_AP0|PTE_EXT_AP1|PTE_EXT_APX) //supervisor=RO, user=RO + +#define PAGE_RWX_CB (PAGE_AP_RW|PAGE_CB|PTE_TYPE_SMALL) /* Read/Write/executable, cache, write back */ +#define PAGE_RWX_CNB (PAGE_AP_RW|PAGE_CNB|PTE_TYPE_SMALL) /* Read/Write/executable, cache, write through */ +#define PAGE_RWX_NCNB (PAGE_AP_RW|PTE_TYPE_SMALL) /* Read/Write/executable without cache and write buffer */ +#define PAGE_RWX_FAULT (PAGE_AP_RW|PTE_TYPE_SMALL) /* Read/Write without cache and write buffer */ + +#define PAGE_RWNX_CB (PAGE_AP_RW|PAGE_CB|PTE_TYPE_SMALL|PTE_EXT_XN) /* Read/Write, cache, write back */ +#define PAGE_RWNX_CNB (PAGE_AP_RW|PAGE_CNB|PTE_TYPE_SMALL|PTE_EXT_XN) /* Read/Write, cache, write through */ +#define PAGE_RWNX_NCNB (PAGE_AP_RW|PTE_TYPE_SMALL|PTE_EXT_XN) /* Read/Write without cache and write buffer */ +#define PAGE_RWNX_FAULT (PAGE_AP_RW|PTE_TYPE_SMALL|PTE_EXT_XN) /* Read/Write without cache and write buffer */ + + +#define PAGE_ROX_CB (PAGE_AP_RO|PAGE_CB|PTE_TYPE_SMALL) /* Read Only/executable, cache, write back */ +#define PAGE_ROX_CNB (PAGE_AP_RO|PAGE_CNB|PTE_TYPE_SMALL) /* Read Only/executable, cache, write through */ +#define PAGE_ROX_NCNB (PAGE_AP_RO|PTE_TYPE_SMALL) /* Read Only/executable without cache and write buffer */ +#define PAGE_ROX_FAULT (PAGE_AP_RO|PTE_TYPE_SMALL) /* Read Only without cache and write buffer */ + +#define PAGE_RONX_CB (PAGE_AP_RO|PAGE_CB|PTE_TYPE_SMALL|PTE_EXT_XN) /* Read Only, cache, write back */ +#define PAGE_RONX_CNB (PAGE_AP_RO|PAGE_CNB|PTE_TYPE_SMALL|PTE_EXT_XN) /* Read Only, cache, write through */ +#define PAGE_RONX_NCNB (PAGE_AP_RO|PTE_TYPE_SMALL|PTE_EXT_XN) /* Read Only without cache and write buffer */ +#define PAGE_RONX_FAULT (PAGE_AP_RO|PTE_TYPE_SMALL|PTE_EXT_XN) /* Read Only without cache and write buffer */ + + +#define DESC_SEC (0x2|(1<<4)) +#define CB (3<<2) //cache_on, write_back +#define CNB (2<<2) //cache_on, write_through +#define NCB (1<<2) //cache_off,WR_BUF on +#define NCNB (0<<2) //cache_off,WR_BUF off +#define AP_RW (3<<10) //supervisor=RW, user=RW +#define AP_RO (2<<10) //supervisor=RW, user=RO + +#define DOMAIN_FAULT (0x0) +#define DOMAIN_CHK (0x1) +#define DOMAIN_NOTCHK (0x3) +#define DOMAIN0 (0x0<<5) +#define DOMAIN1 (0x1<<5) + +#define DOMAIN0_ATTR (DOMAIN_CHK<<0) +#define DOMAIN1_ATTR (DOMAIN_FAULT<<2) + +#define RW_CB (AP_RW|DOMAIN0|CB|DESC_SEC) /* Read/Write, cache, write back */ +#define RW_CNB (AP_RW|DOMAIN0|CNB|DESC_SEC) /* Read/Write, cache, write through */ +#define RW_NCNB (AP_RW|DOMAIN0|NCNB|DESC_SEC) /* Read/Write without cache and write buffer */ +#define RW_FAULT (AP_RW|DOMAIN1|NCNB|DESC_SEC) /* Read/Write without cache and write buffer */ + +struct mem_desc { + rt_uint32_t vaddr_start; + rt_uint32_t vaddr_end; + rt_uint32_t paddr_start; + rt_uint32_t sect_attr; /* when page mapped */ + rt_uint32_t page_attr; /* only sector mapped valid */ + rt_uint32_t mapped_mode; +#define SECT_MAPPED 0 +#define PAGE_MAPPED 1 +}; + +void rt_hw_mmu_init(struct mem_desc *mdesc, rt_uint32_t size); + +#endif + diff --git a/libcpu/arm/armv6/stack.c b/libcpu/arm/armv6/stack.c new file mode 100644 index 0000000000000000000000000000000000000000..ceb04f0985b1eb6c484308c1b92cdbd62ecece5c --- /dev/null +++ b/libcpu/arm/armv6/stack.c @@ -0,0 +1,79 @@ +/* + * File : stack.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2011-01-13 weety copy from mini2440 + */ +#include + +/*****************************/ +/* CPU Mode */ +/*****************************/ +#define USERMODE 0x10 +#define FIQMODE 0x11 +#define IRQMODE 0x12 +#define SVCMODE 0x13 +#define ABORTMODE 0x17 +#define UNDEFMODE 0x1b +#define MODEMASK 0x1f +#define NOINT 0xc0 + +/** + * This function will initialize thread stack + * + * @param tentry the entry of thread + * @param parameter the parameter of entry + * @param stack_addr the beginning stack address + * @param texit the function will be called when thread exit + * + * @return stack address + */ +rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter, + rt_uint8_t *stack_addr, void *texit) +{ + rt_uint32_t *stk; + + stk = (rt_uint32_t*)stack_addr; + *(stk) = (rt_uint32_t)tentry; /* entry point */ + *(--stk) = (rt_uint32_t)texit; /* lr */ + *(--stk) = 0; /* r12 */ + *(--stk) = 0; /* r11 */ + *(--stk) = 0; /* r10 */ + *(--stk) = 0; /* r9 */ + *(--stk) = 0; /* r8 */ + *(--stk) = 0; /* r7 */ + *(--stk) = 0; /* r6 */ + *(--stk) = 0; /* r5 */ + *(--stk) = 0; /* r4 */ + *(--stk) = 0; /* r3 */ + *(--stk) = 0; /* r2 */ + *(--stk) = 0; /* r1 */ + *(--stk) = (rt_uint32_t)parameter; /* r0 : argument */ + + /* cpsr */ + if ((rt_uint32_t)tentry & 0x01) + *(--stk) = SVCMODE | 0x20; /* thumb mode */ + else + *(--stk) = SVCMODE; /* arm mode */ + + /* return task's current stack address */ + return (rt_uint8_t *)stk; +} + diff --git a/libcpu/arm/armv6/vfp.c b/libcpu/arm/armv6/vfp.c new file mode 100644 index 0000000000000000000000000000000000000000..478c0603d7848b339da3c9b2d573fb9f9c160c45 --- /dev/null +++ b/libcpu/arm/armv6/vfp.c @@ -0,0 +1,161 @@ +/* + * File : vfp.c + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Develop Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2014-11-07 weety first version + */ + +#include +#include +#include "vfp.h" + +#ifdef RT_USING_VFP + +rt_uint32_t vfpregs_offset = offsetof(struct rt_thread, vfpregs); + +struct vfp_context *last_vfp_context = RT_NULL; + +int vfp_switch(rt_uint32_t cmd, struct rt_thread *thread) +{ + rt_uint32_t fpexc; + //rt_kprintf("%s:%d, %x\n", __func__, cmd, thread); + + switch (cmd) { + case THREAD_INIT: + { + struct vfp_context *vfp = &thread->vfpregs; + + rt_memset(vfp, 0, sizeof(struct vfp_context)); + vfp->fpexc = FPEXC_EN; + vfp->fpscr = FPSCR_RN; + + if (last_vfp_context == vfp) + last_vfp_context = RT_NULL; + vmsr(FPEXC, vmrs(FPEXC) & ~FPEXC_EN); + break; + } + case THREAD_EXIT: + { + /* release case: Per-thread VFP cleanup. */ + struct vfp_context *vfp = &thread->vfpregs; + + if (last_vfp_context == vfp) + last_vfp_context = RT_NULL; + break; + } + default: + break; + } + + return 0; +} + +int vfp_thread_init(struct rt_thread *thread) +{ + return vfp_switch(THREAD_INIT, thread); +} + + +int vfp_thread_exit(struct rt_thread *thread) +{ + return vfp_switch(THREAD_EXIT, thread); +} + +rt_uint32_t read_vfp_regs(rt_uint32_t *buf) +{ + rt_uint32_t value; + rt_uint32_t length; + asm volatile ("vldmia %0!, {d0-d15}" + : + :"r"(buf) + :"cc"); + length = 32; +#ifdef RT_USING_VFPv3 + asm volatile ("vmrs %0, mvfr0" + :"=r"(value) + :); + if ((value & MVFR0_A_SIMD_MASK) == 2) + { + asm volatile ("vldmia %0!, {d16-d31}" + : + :"r"(buf) + :"cc"); + length = 64; + } + else + { + length = 32; + } +#endif + + return length; +} + +void vfp_exception(rt_uint32_t instruction, rt_uint32_t fpexc) +{ + int i = 0; + int length = 0; + rt_uint32_t fpscr, fpsid; +#ifdef RT_USING_VFPv3 + unsigned long long vfp_regs[32]; +#else + unsigned long long vfp_regs[16]; +#endif + + rt_kprintf("VFP: exception: instruction %08x fpexc %08x\n", instruction, fpexc); + fpsid = vmrs(FPSID); + fpscr = vmrs(FPSCR); + rt_kprintf("VFP: exception: fpsid %08x fpscr %08x\n", fpsid, fpscr); + + length = read_vfp_regs((rt_uint32_t *)vfp_regs); + rt_kprintf("VFP: exception registers: {s0~s%d}\n", length - 1); + for(i = 0; i < length; i++) + { + if (i && !(i & 0x3)) + rt_kprintf("\n"); + rt_kprintf("0x%08x ", ((rt_uint32_t *)vfp_regs)[i]); + } + rt_kprintf("\n"); + +} + +void vfp_init(void) +{ + int ret = 0; + unsigned int value; + asm volatile ("mrc p15, 0, %0, c1, c0, 2" + :"=r"(value) + :); + value |= 0xf00000;/*enable CP10, CP11 user access*/ + asm volatile("mcr p15, 0, %0, c1, c0, 2" + : + :"r"(value)); +#if 0 + asm volatile("fmrx %0, fpexc" + :"=r"(value)); + value |=(1<<30); + asm volatile("fmxr fpexc, %0" + : + :"r"(value)); +#endif +} + + +#endif diff --git a/libcpu/arm/armv6/vfp.h b/libcpu/arm/armv6/vfp.h new file mode 100644 index 0000000000000000000000000000000000000000..0ec7b26b72f1a53f57b2c5f38f35ff7f8b6d1e02 --- /dev/null +++ b/libcpu/arm/armv6/vfp.h @@ -0,0 +1,110 @@ +/* + * File : vfp.h + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Develop Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2014-11-07 weety first version + */ + +#ifndef __VFP_H__ +#define __VFP_H__ + +/* FPSID register bits */ +#define FPSID_IMPLEMENTER_BIT (24) +#define FPSID_IMPLEMENTER_MASK (0xff << FPSID_IMPLEMENTER_BIT) +#define FPSID_SW (1 << 23) +#define FPSID_FORMAT_BIT (21) +#define FPSID_FORMAT_MASK (0x3 << FPSID_FORMAT_BIT) +#define FPSID_NODOUBLE (1 << 20) +#define FPSID_ARCH_BIT (16) +#define FPSID_ARCH_MASK (0xF << FPSID_ARCH_BIT) +#define FPSID_PART_BIT (8) +#define FPSID_PART_MASK (0xFF << FPSID_PART_BIT) +#define FPSID_VARIANT_BIT (4) +#define FPSID_VARIANT_MASK (0xF << FPSID_VARIANT_BIT) +#define FPSID_REVISION_BIT (0) +#define FPSID_REVISION_MASK (0xF << FPSID_REVISION_BIT) + +/* FPSCR register bits */ +#define FPSCR_DN (1<<25) /* Default NaN mode enable bit */ +#define FPSCR_FZ (1<<24) /* Flush-to-zero mode enable bit */ +#define FPSCR_RN (0<<22) /* Round to nearest (RN) mode */ +#define FPSCR_RP (1<<22) /* Round towards plus infinity (RP) mode */ +#define FPSCR_RM (2<<22) /* Round towards minus infinity (RM) mode */ +#define FPSCR_RZ (3<<22) /* Round towards zero (RZ) mode */ +#define FPSCR_RMODE_BIT (22) +#define FPSCR_RMODE_MASK (3 << FPSCR_RMODE_BIT) +#define FPSCR_STRIDE_BIT (20) +#define FPSCR_STRIDE_MASK (3 << FPSCR_STRIDE_BIT) +#define FPSCR_LENGTH_BIT (16) +#define FPSCR_LENGTH_MASK (7 << FPSCR_LENGTH_BIT) +#define FPSCR_IDE (1<<15) /* Input Subnormal exception trap enable bit */ +#define FPSCR_IXE (1<<12) /* Inexact exception trap enable bit */ +#define FPSCR_UFE (1<<11) /* Underflow exception trap enable bit */ +#define FPSCR_OFE (1<<10) /* Overflow exception trap enable bit */ +#define FPSCR_DZE (1<<9) /* Division by Zero exception trap enable bit */ +#define FPSCR_IOE (1<<8) /* Invalid Operation exception trap enable bit */ +#define FPSCR_IDC (1<<7) /* Input Subnormal cumulative exception flag */ +#define FPSCR_IXC (1<<4) /* Inexact cumulative exception flag */ +#define FPSCR_UFC (1<<3) /* Underflow cumulative exception flag */ +#define FPSCR_OFC (1<<2) /* Overflow cumulative exception flag */ +#define FPSCR_DZC (1<<1) /* Division by Zero cumulative exception flag */ +#define FPSCR_IOC (1<<0) /* Invalid Operation cumulative exception flag */ + +/* FPEXC register bits */ +#define FPEXC_EX (1 << 31) /* When EX is set, the VFP coprocessor is in the exceptional state */ +#define FPEXC_EN (1 << 30) /* VFP enable bit */ +#define FPEXC_DEX (1 << 29) /* Defined synchronous instruction exceptional flag */ +#define FPEXC_FP2V (1 << 28) /* FPINST2 instruction valid flag */ +#define FPEXC_LENGTH_BIT (8) +#define FPEXC_LENGTH_MASK (7 << FPEXC_LENGTH_BIT) +#define FPEXC_INV (1 << 7) /* Input exception flag */ +#define FPEXC_UFC (1 << 3) /* Potential underflow flag */ +#define FPEXC_OFC (1 << 2) /* Potential overflow flag */ +#define FPEXC_IOC (1 << 0) /* Potential invalid operation flag */ +#define FPEXC_TRAP_MASK (FPEXC_INV|FPEXC_UFC|FPEXC_OFC|FPEXC_IOC) + + +/* MVFR0 register bits */ +#define MVFR0_A_SIMD_BIT (0) +#define MVFR0_A_SIMD_MASK (0xf << MVFR0_A_SIMD_BIT) + + +/* thread switch micro */ +#define THREAD_INIT 0 +#define THREAD_EXIT 1 + +/* + * get VFP register + */ + +#define vmrs(vfp) ({ \ + rt_uint32_t var; \ + asm("vmrs %0, "#vfp"" : "=r" (var) : : "cc"); \ + var; \ + }) + +#define vmsr(vfp, var) \ + asm("vmsr "#vfp", %0" \ + : : "r" (var) : "cc") + + +#endif + + diff --git a/libcpu/arm/armv6/vfp_entry_gcc.S b/libcpu/arm/armv6/vfp_entry_gcc.S new file mode 100644 index 0000000000000000000000000000000000000000..2c60d0f26a2594d16bced7f6ba83157336d5103b --- /dev/null +++ b/libcpu/arm/armv6/vfp_entry_gcc.S @@ -0,0 +1,239 @@ +/* + * File : vfp_entry_gcc.S + * This file is part of RT-Thread RTOS + * COPYRIGHT (C) 2006, RT-Thread Development Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Change Logs: + * Date Author Notes + * 2014-11-07 weety first version + */ + +#include + +#ifdef RT_USING_VFP + +#include "armv6.h" +#include "vfp.h" + +//#define DEBUG + +.macro PRINT, str +#ifdef DEBUG + stmfd sp!, {r0-r3, ip, lr} + add r0, pc, #4 + bl rt_kprintf + b 1f + .asciz "VFP: \str\n" + .balign 4 +1: ldmfd sp!, {r0-r3, ip, lr} +#endif + .endm + +.macro PRINT1, str, arg +#ifdef DEBUG + stmfd sp!, {r0-r3, ip, lr} + mov r1, \arg + add r0, pc, #4 + bl rt_kprintf + b 1f + .asciz "VFP: \str\n" + .balign 4 +1: ldmfd sp!, {r0-r3, ip, lr} +#endif + .endm + +.macro PRINT3, str, arg1, arg2, arg3 +#ifdef DEBUG + stmfd sp!, {r0-r3, ip, lr} + mov r3, \arg3 + mov r2, \arg2 + mov r1, \arg1 + add r0, pc, #4 + bl rt_kprintf + b 1f + .asciz "VFP: \str\n" + .balign 4 +1: ldmfd sp!, {r0-r3, ip, lr} +#endif + .endm + +.macro get_vfpregs_offset, rd + ldr \rd, .vfp_offset + ldr \rd, [\rd] + .endm + +.vfp_offset: + .word vfpregs_offset + +.macro vfp_restore_working_reg, base, rd0 + vldmia \base!, {d0-d15} +#ifdef RT_USING_VFPv3 + vmrs \rd0, mvfr0 + and \rd0, \rd0, #MVFR0_A_SIMD_MASK @ A_SIMD registers + cmp \rd0, #2 @ 0b0000 Not supported. + @ 0b0001 Supported, 16 กม64-bit registers. + @ 0b0010 Supported, 32 กม64-bit registers. + vldmiaeq \base!, {d16-d31} + addne \base, \base, #32*4 @ skip unused registers +#endif + .endm + +.macro vfp_save_working_reg, base, rd0 + vstmia \base!, {d0-d15} @ save the working registers +#ifdef RT_USING_VFPv3 + vmrs \rd0, mvfr0 + and \rd0, \rd0, #MVFR0_A_SIMD_MASK @ A_SIMD registers + cmp \rd0, #2 @ 0b0000 Not supported. + @ 0b0001 Supported, 16 กม64-bit registers. + @ 0b0010 Supported, 32 กม64-bit registers. + vstmiaeq \base!, {d16-d31} + addne \base, \base, #32*4 @ skip unused registers +#endif + .endm + +.macro vfp_restore_state, base, fpexc_rd, rd0, rd1, rd2 + ldmia \base, {\fpexc_rd, \rd0, \rd1, \rd2} @ load FPEXC, FPSCR, FPINST, FPINST2 + tst \fpexc_rd, #FPEXC_EX @ vfp is in the exceptional state? + beq 1f + vmsr fpinst, \rd1 @ restore fpinst + tst \fpexc_rd, #FPEXC_FP2V @ FPINST2 instruction valid + beq 1f + vmsr fpinst2, \rd2 @ restore fpinst2 +1: + vmsr fpscr, \rd0 @ restore fpscr + .endm + +.macro vfp_save_state, base, fpexc_rd, rd0, rd1, rd2 + vmrs \rd0, fpscr @ current status + tst \fpexc_rd, #FPEXC_EX @ vfp is in the exceptional state? + beq 1f + vmrs \rd1, fpinst @ get fpinst + tst \fpexc_rd, #FPEXC_FP2V @ FPINST2 instruction valid + beq 1f + vmrs \rd2, fpinst2 @ get fpinst2 +1: + stmia \base, {\fpexc_rd, \rd0, \rd1, \rd2} @ save FPEXC, FPSCR, FPINST, FPINST2 + .endm + + +/* + * VFP hardware support entry point. + * r0 = faulted instruction + * r2 = faulted PC+4 + * r9 = successful return + * r10 = rt_thread structure + * lr = failure return + */ + +.globl vfp_entry +vfp_entry: + ldr r1, =rt_interrupt_nest + ldr r1, [r1] @ get rt_interrupt_nest + cmp r1, #0 @ rt_interrupt_nest == 0? + bne irq_vfp_entry @ irq handler used VFP + + get_vfpregs_offset r11 + add r10, r10, r11 @ r10 = vfpregs + + vmrs r1, fpexc + tst r1, #FPEXC_EN + bne __lookup_vfp_exceptions @ if the VFP already enabled, now checking vfp exceptions + + ldr r3, last_vfp_context_address + orr r1, r1, #FPEXC_EN @ set VFP enable bit + ldr r4, [r3] @ get last_vfp_context pointer + bic r5, r1, #FPEXC_EX @ clear exceptions status + cmp r4, r10 + beq __switch_to_the_same_thread @ switch to the same thread, checking pending exception. + + vmsr fpexc, r5 @ enable VFP, clear any pending exceptions + + /* Save the current VFP registers to the old thread context */ + cmp r4, #0 + beq __no_last_vfp_context + vfp_save_working_reg r4, r5 @ save the working registers + vfp_save_state r4, r1, r5, r6, r8 @ save vfp state registers + +__no_last_vfp_context: + str r10, [r3] @ update the last_vfp_context pointer + + vfp_restore_working_reg r10, r5 @ restore the working registers + vfp_restore_state r10, r1, r5, r6, r8 @ restore vfp state registers + +__switch_to_the_same_thread: + tst r1, #FPEXC_EX + bne __do_exception + vmsr fpexc, r1 @ restore fpexc last + sub r2, r2, #4 + str r2, [sp, #S_PC] @ retry the faulted instruction + PRINT1 "return instr=0x%08x", r2 + mov pc, r9 + + +__lookup_vfp_exceptions: + tst r1, #FPEXC_EX | FPEXC_DEX @ Check for synchronous or asynchronous exception + bne __do_exception + vmrs r5, fpscr + tst r5, #FPSCR_IXE + bne __do_exception + + PRINT "__lookup_vfp_exceptions" + mov pc, lr + +__do_exception: + PRINT "__do_exception" + push {lr} + mov r5, r1 + bic r5, #FPEXC_EX @ clear exception + vmsr fpexc, r5 + bl vfp_exception @ r0 = faulted instruction, r1 = fpexc + pop {pc} + @mov pc, lr + + +irq_vfp_entry: + vmrs r1, fpexc + tst r1, #FPEXC_EN + bne __lookup_vfp_exceptions @ if the VFP already enabled, now checking vfp exceptions + + ldr r3, last_vfp_context_address + orr r1, r1, #FPEXC_EN @ set VFP enable bit + ldr r4, [r3] @ get last_vfp_context pointer + bic r5, r1, #FPEXC_EX @ clear exceptions status + + vmsr fpexc, r5 @ enable VFP, clear any pending exceptions + + /* Save the current VFP registers to the old thread context */ + cmp r4, #0 @ last_vfp_context != NULL ? + beq __no_save_vfp_context + vfp_save_working_reg r4, r5 @ save the working registers + vfp_save_state r4, r1, r5, r6, r8 @ save vfp state registers + + mov r4, #0 + str r4, [r3] @ update the last_vfp_context pointer + @ last_vfp_context = NULL +__no_save_vfp_context: + sub r2, r2, #4 + str r2, [sp, #S_PC] @ retry the faulted instruction + PRINT1 "return instr=0x%08x", r2 + mov pc, r9 + + .align +last_vfp_context_address: + .word last_vfp_context + +#endif