| /**************************************************************************** |
| * arch/arm/src/lc823450/lc823450_sdc.c |
| * |
| * Copyright (C) 2014-2015 ON Semiconductor. All rights reserved. |
| * Copyright 2014,2015,2016,2017 Sony Video & Sound Products Inc. |
| * Author: Masayuki Ishikawa <Masayuki.Ishikawa@jp.sony.com> |
| * Author: Masatoshi Tateishi <Masatoshi.Tateishi@jp.sony.com> |
| * Author: Nobutaka Toyoshima <Nobutaka.Toyoshima@jp.sony.com> |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * 3. Neither the name NuttX nor the names of its contributors may be |
| * used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
| * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
| * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| #include <sys/types.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| #include <nuttx/clock.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/kthread.h> |
| #include <arch/board/board.h> |
| |
| #include <inttypes.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "chip.h" |
| #include "arm_internal.h" |
| #include "lc823450_sddrv_if.h" |
| #include "lc823450_sdc.h" |
| #include "lc823450_syscontrol.h" |
| #include "lc823450_clockconfig.h" |
| |
| #ifdef CONFIG_LED_ACCLED_SDIF |
| # include <nuttx/led.h> |
| #endif |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define SDIF0_BASE (0x4004A000) |
| #define SDIF1_BASE (0x4004B000) |
| |
| #define DET_TIME (10000) /* 10ms */ |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| static mutex_t _sdc_lock[2] = |
| { |
| NXMUTEX_INITIALIZER, |
| NXMUTEX_INITIALIZER, |
| }; |
| |
| static struct sddrcfg_s _sdch0; |
| static struct sddrcfg_s _sdch1; |
| |
| static struct sddrcfg_s *_cfg[2] = |
| { |
| &_sdch0, |
| &_sdch1 |
| }; |
| |
| static unsigned long _work0[512 / 4]; |
| #ifdef CONFIG_LC823450_SDIF_SDC |
| static unsigned long _work1[512 / 4]; |
| #endif |
| |
| #ifdef CONFIG_LC823450_SDC_CACHE |
| static uint8_t _sec_cache_enabled; |
| static uint32_t _sec_cache[512 / 4]; |
| static uint32_t _sec_cache_add = 0xffffffff; |
| #endif |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /* ROM symbol address */ |
| |
| extern uint8_t cpu_ver; |
| |
| extern SINT_T sddep0_hw_init(struct sddrcfg_s *); |
| extern SINT_T sddep0_hw_exit(struct sddrcfg_s *); |
| extern SINT_T sddep1_hw_init(struct sddrcfg_s *); |
| extern SINT_T sddep1_hw_exit(struct sddrcfg_s *); |
| |
| extern SINT_T sddep_os_init(struct sddrcfg_s *); |
| extern SINT_T sddep_os_exit(struct sddrcfg_s *); |
| extern void sddep_voltage_switch(struct sddrcfg_s *cfg); |
| extern void sddep_set_clk(struct sddrcfg_s *); |
| extern SINT_T sddep_wait(UI_32, struct sddrcfg_s *); |
| extern SINT_T sddep_wait_status(UI_32 req, UI_32 *status, |
| struct sddrcfg_s *cfg); |
| extern SINT_T sddep_read(void *src, void *dst, UI_32 size, SINT_T type, |
| struct sddrcfg_s *cfg); |
| extern SINT_T sddep_write(void *src, void *dst, UI_32 size, SINT_T type, |
| struct sddrcfg_s *cfg); |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: _lc823450_sdc_support_trim |
| ****************************************************************************/ |
| |
| static int _lc823450_sdc_support_trim(struct sddrcfg_s *cf) |
| { |
| /* NOTE: to avoid conflicts, SDDR_SUPPORT_TRIM() macro is not used here */ |
| |
| int ret = ((sddr_refmediatype(cf) == SDDR_MEDIA_TYPE_MMC) ? |
| (((cf)->ex.mmc.extcsd_sec_feature_support & (1UL << 4)) ? |
| 1 : 0) : 0); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_access_led |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LED_ACCLED_SDIF |
| static void lc823450_sdc_access_led(uint32_t ch, unsigned long sector) |
| { |
| if (ch == 0) |
| { |
| if (sector >= CONFIG_MTD_CP_STARTBLOCK) |
| { |
| led_start_accled(false); |
| } |
| } |
| else |
| { |
| led_start_accled(false); |
| } |
| } |
| #else |
| # define lc823450_sdc_access_led(a,b) |
| #endif |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_clearcardinfo |
| ****************************************************************************/ |
| |
| int lc823450_sdc_clearcardinfo(uint32_t ch) |
| { |
| int ret; |
| |
| mcinfo("++++ start\n"); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ret = sddr_clearcardinfo(_cfg[ch]); |
| |
| #ifdef CONFIG_LC823450_SDC_CACHE |
| if (ch) |
| { |
| _sec_cache_enabled = 0; |
| _sec_cache_add = 0xffffffff; /* invalid */ |
| } |
| #endif |
| |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_initialize |
| ****************************************************************************/ |
| |
| int lc823450_sdc_initialize(uint32_t ch) |
| { |
| int ret; |
| |
| /* Only ES2 is supported */ |
| |
| DEBUGASSERT(1 == cpu_ver); |
| |
| struct sddrcfg_s *psd = _cfg[ch]; |
| |
| psd->sysclk = lc823450_get_systemfreq(); |
| psd->detecttime = DET_TIME; |
| |
| #ifdef CONFIG_LC823450_SDC_UHS1 |
| psd->setting = SDDR_SD_SWITCH_18V; |
| #endif |
| |
| psd->deposinit = sddep_os_init; |
| psd->deposexit = sddep_os_exit; |
| psd->depsetclk = sddep_set_clk; |
| psd->depwait = sddep_wait; |
| psd->depwaitstatus = sddep_wait_status; |
| psd->depreaddata = sddep_read; |
| psd->depwritedata = sddep_write; |
| psd->depvoltageswitch = sddep_voltage_switch; |
| |
| switch (ch) |
| { |
| case 0: |
| psd->dephwinit = sddep0_hw_init; |
| psd->dephwexit = sddep0_hw_exit; |
| psd->regbase = SDIF0_BASE; |
| psd->workbuf = _work0; |
| break; |
| |
| #ifdef CONFIG_LC823450_SDIF_SDC |
| case 1: |
| psd->dephwinit = sddep1_hw_init; |
| psd->dephwexit = sddep1_hw_exit; |
| psd->regbase = SDIF1_BASE; |
| psd->workbuf = _work1; |
| break; |
| #endif |
| |
| default: |
| DEBUGPANIC(); |
| } |
| |
| mcinfo("++++ start\n"); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret >= 0) |
| { |
| ret = sddr_initialize(_cfg[ch]); |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_finalize |
| ****************************************************************************/ |
| |
| int lc823450_sdc_finalize(uint32_t ch) |
| { |
| int ret; |
| |
| mcinfo("++++ start ch=%" PRId32 "\n", ch); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret >= 0) |
| { |
| ret = sddr_finalize(_cfg[ch]); |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_identifycard |
| ****************************************************************************/ |
| |
| int lc823450_sdc_identifycard(uint32_t ch) |
| { |
| int ret; |
| |
| mcinfo("++++ start\n"); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ret = sddr_identifycard(_cfg[ch]); |
| |
| #ifdef CONFIG_LC823450_SDC_CACHE |
| if (ch) |
| { |
| _sec_cache_enabled = 0; |
| _sec_cache_add = 0xffffffff; /* invalid */ |
| } |
| #endif |
| |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_setclock |
| ****************************************************************************/ |
| |
| int lc823450_sdc_setclock(uint32_t ch, uint32_t limitclk, uint32_t sysclk) |
| { |
| int ret; |
| |
| mcinfo("++++ start ch=%" PRId32 " limitClk=%" PRId32 |
| " sysClk=%" PRId32 "\n", ch, limitclk, sysclk); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret >= 0) |
| { |
| ret = sddr_setclock(limitclk, sysclk, _cfg[ch]); |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_refmediatype |
| * |
| * Returned Value: |
| * 0(sd), 1(emmc) |
| * |
| ****************************************************************************/ |
| |
| int lc823450_sdc_refmediatype(uint32_t ch) |
| { |
| int ret; |
| |
| mcinfo("++++ start\n"); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret >= 0) |
| { |
| ret = sddr_refmediatype(_cfg[ch]); |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_getcardsize |
| ****************************************************************************/ |
| |
| int lc823450_sdc_getcardsize(uint32_t ch, |
| unsigned long *psecnum, unsigned long *psecsize) |
| { |
| int ret; |
| |
| mcinfo("++++ start\n"); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret >= 0) |
| { |
| ret = sddr_getcardsize(psecnum, psecsize, _cfg[ch]); |
| |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_readsector |
| ****************************************************************************/ |
| |
| int lc823450_sdc_readsector(uint32_t ch, |
| unsigned long addr, unsigned short cnt, |
| void *pbuf, unsigned long type) |
| { |
| int ret; |
| int i = 0; |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| #ifdef CONFIG_LC823450_SDC_LOG |
| mcinfo("++++ start ch=%d, addr=%ld, cnt=%d\n", ch, addr, cnt); |
| #endif |
| |
| #ifdef CONFIG_LC823450_SDC_CACHE |
| if (ch && _sec_cache_enabled && 1 == cnt && addr == _sec_cache_add) |
| { |
| memcpy(pbuf, _sec_cache, sizeof(_sec_cache)); |
| goto errout_with_lock; |
| } |
| #endif |
| |
| lc823450_sdc_access_led(ch, addr); |
| |
| #ifdef CONFIG_SCHED_INSTRUMENTATION_IO |
| sched_add_bi((uint64_t)cnt); |
| #endif |
| |
| for (i = 0; i < 5; i++) |
| { |
| #ifdef CONFIG_LC823450_SDIF_PATCH |
| ret = fixed_sddr_readsector(addr, cnt, pbuf, type, _cfg[ch]); |
| #else |
| ret = sddr_readsector(addr, cnt, pbuf, type, _cfg[ch]); |
| #endif |
| if (0 == ret) |
| { |
| break; |
| } |
| |
| mcinfo("ret=%d ch=%" PRId32 " add=%ld cnt=%d i=%d\n", |
| ret, ch, addr, cnt, i); |
| } |
| |
| #ifdef CONFIG_LC823450_SDC_CACHE |
| if (ch) |
| { |
| if (0 == addr) |
| { |
| uint8_t *p = (pbuf + 0x1c2); /* partition id */ |
| if (0x7 == *p) |
| { |
| mcinfo("exFAT (NTFS) detected\n"); |
| _sec_cache_enabled = 1; |
| } |
| } |
| |
| if (_sec_cache_enabled) |
| { |
| if (1 == cnt && 0 == ret) |
| { |
| memcpy(_sec_cache, pbuf, sizeof(_sec_cache)); |
| _sec_cache_add = addr; |
| } |
| else |
| { |
| _sec_cache_add = 0xffffffff; /* invalid */ |
| } |
| } |
| } |
| |
| errout_with_lock: |
| #endif |
| nxmutex_unlock(&_sdc_lock[ch]); |
| |
| mcinfo("---- end ret=%d\n", ret); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * name: lc823450_sdc_writesector |
| ****************************************************************************/ |
| |
| int lc823450_sdc_writesector(uint32_t ch, |
| unsigned long addr, unsigned short cnt, |
| void *pbuf, unsigned long type) |
| { |
| int ret; |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| #ifdef CONFIG_LC823450_SDC_LOG |
| mcinfo("++++ start ch=%d, addr=%ld, cnt=%d\n", ch, addr, cnt); |
| #endif |
| |
| #ifdef CONFIG_LC823450_SDC_CACHE |
| if (1 == ch && _sec_cache_enabled) |
| { |
| _sec_cache_add = 0xffffffff; /* invalid */ |
| } |
| #endif |
| |
| lc823450_sdc_access_led(ch, addr); |
| |
| #ifdef CONFIG_SCHED_INSTRUMENTATION_IO |
| sched_add_bo((uint64_t)cnt); |
| #endif |
| |
| ret = sddr_writesector(addr, cnt, pbuf, type, _cfg[ch]); |
| |
| if (0 > ret) |
| { |
| mcinfo("ret=%d ch=%" PRId32 " add=%ld cnt=%d\n", ret, ch, addr, cnt); |
| } |
| |
| nxmutex_unlock(&_sdc_lock[ch]); |
| |
| mcinfo("---- end ret=%d\n", ret); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_checktrim |
| ****************************************************************************/ |
| |
| int lc823450_sdc_checktrim(uint32_t ch) |
| { |
| return _lc823450_sdc_support_trim(_cfg[ch]); |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_trimsector |
| ****************************************************************************/ |
| |
| int lc823450_sdc_trimsector(uint32_t ch, unsigned long addr, |
| unsigned short cnt) |
| { |
| int ret; |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| #ifdef CONFIG_LC823450_SDC_LOG |
| mcinfo("++++ start ch=%d, addr=%ld, cnt=%d\n", ch, addr, cnt); |
| #endif |
| |
| lc823450_sdc_access_led(ch, addr); |
| |
| #ifdef CONFIG_SCHED_INSTRUMENTATION_IO |
| sched_add_bt((uint64_t)cnt); |
| #endif |
| |
| ret = sddr_eraseseq(0x00000001, addr, cnt, _cfg[ch]); |
| if (0 > ret) |
| { |
| mcinfo("ret=%d ch=%" PRId32 " add=%ld cnt=%d\n", ret, ch, addr, cnt); |
| } |
| |
| nxmutex_unlock(&_sdc_lock[ch]); |
| |
| mcinfo("---- end ret=%d\n", ret); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_cachectl |
| ****************************************************************************/ |
| |
| int lc823450_sdc_cachectl(uint32_t ch, int ctrl) |
| { |
| int ret; |
| |
| mcinfo("++++ ch=%" PRId32 ", ctrl=%d\n", ch, ctrl); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret >= 0) |
| { |
| ret = sddr_cachectrl(ctrl, _cfg[ch]); |
| |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_changespeedmode |
| ****************************************************************************/ |
| |
| int lc823450_sdc_changespeedmode(uint32_t ch, int mode) |
| { |
| int ret; |
| |
| mcinfo("++++ ch=%" PRId32 ", mode=%d\n", ch, mode); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ret = sddr_changespeedmode(mode, _cfg[ch]); |
| |
| if (0 == ret) |
| { |
| switch (mode) |
| { |
| case 1: /* High Speed */ |
| modifyreg32(SDCTL, |
| SDCTL_ACSMODE0_MASK << (ch * 8), |
| SDCTL_ACSMODE0_HS << (ch * 8)); |
| break; |
| |
| case 4: /* DDR */ |
| modifyreg32(SDCTL, |
| SDCTL_ACSMODE0_MASK << (ch * 8), |
| SDCTL_ACSMODE0_MMCDDR << (ch * 8)); |
| break; |
| } |
| } |
| |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_getcid |
| ****************************************************************************/ |
| |
| int lc823450_sdc_getcid(uint32_t ch, char *cidstr, int length) |
| { |
| uint8_t cid[16]; |
| int ret; |
| |
| mcinfo("++++ ch=%" PRId32 "\n", ch); |
| |
| ret = nxmutex_lock(&_sdc_lock[ch]); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| ret = sddr_getcid((UI_32 *)cid, _cfg[ch]); |
| |
| if (0 == ret && length >= (2 * sizeof(cid) + 1)) |
| { |
| int i; |
| |
| for (i = 15; i >= 0; i--) |
| { |
| snprintf(cidstr, 3, "%02x", cid[i]); |
| cidstr += 2; |
| } |
| |
| *cidstr = '\0'; |
| } |
| |
| nxmutex_unlock(&_sdc_lock[ch]); |
| mcinfo("---- end ret=%d\n", ret); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lc823450_sdc_locked |
| ****************************************************************************/ |
| |
| int lc823450_sdc_locked(void) |
| { |
| int ret; |
| int i; |
| |
| ret = 0; |
| |
| for (i = 0; i < 2; i++) |
| { |
| if (nxmutex_is_locked(&_sdc_lock[i])) |
| { |
| ret = 1; |
| break; |
| } |
| } |
| |
| return ret; |
| } |