git subrepo clone --force --branch=exo2 https://github.com/m4xw/emummc

subrepo:
  subdir:   "emummc"
  merged:   "3791be9f"
upstream:
  origin:   "https://github.com/m4xw/emummc"
  branch:   "exo2"
  commit:   "3791be9f"
git-subrepo:
  version:  "0.4.1"
  origin:   "???"
  commit:   "???"
This commit is contained in:
Michael Scire 2020-06-08 16:26:55 -07:00 committed by SciresM
parent 6c145d76c7
commit f82954e98b
29 changed files with 1653 additions and 871 deletions

View file

@ -5,8 +5,8 @@
; ;
[subrepo] [subrepo]
remote = https://github.com/m4xw/emuMMC remote = https://github.com/m4xw/emuMMC
branch = develop branch = exo2
commit = 292a8ad42c8e9f4c9a474b46a5a3190398581131 commit = 3791be9fd207811cce221a4311da63fda1d32d4a
parent = 491ba8fdcfd39a503bedd21b282991fc19aec7d4 parent = ff7bed5db7d441eecc9444a9068ae59e7f9b7744
method = rebase method = rebase
cmdver = 0.4.1 cmdver = 0.4.1

View file

@ -21,9 +21,10 @@ include $(DEVKITPRO)/libnx/switch_rules
ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE
DEFINES := -DINNER_HEAP_SIZE=0x80000 # Current max usage is 0x4600. (512 * 34 FatFS file objects + 1 fsync buffer).
DEFINES := -DINNER_HEAP_SIZE=0x8000
CFLAGS := -Wall -O2 -ffunction-sections -Wno-unused-function \ CFLAGS := -Wall -O2 -ffunction-sections -fdata-sections -Wno-unused-function \
$(ARCH) $(DEFINES) $(ARCH) $(DEFINES)
CFLAGS += $(INCLUDE) -D__SWITCH__ CFLAGS += $(INCLUDE) -D__SWITCH__

View file

@ -31,7 +31,7 @@
#define MMC_ALL_SEND_CID 2 /* bcr R2 */ #define MMC_ALL_SEND_CID 2 /* bcr R2 */
#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ #define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
#define MMC_SET_DSR 4 /* bc [31:16] RCA */ #define MMC_SET_DSR 4 /* bc [31:16] RCA */
#define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */ #define MMC_SLEEP_AWAKE 5 /* ac [31:16] RCA 15:flg R1b */
#define MMC_SWITCH 6 /* ac [31:0] See below R1b */ #define MMC_SWITCH 6 /* ac [31:0] See below R1b */
#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ #define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */
#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ #define MMC_SEND_EXT_CSD 8 /* adtc R1 */
@ -51,7 +51,7 @@
#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ #define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ #define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ #define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */ #define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
/* class 3 */ /* class 3 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include "nx_sd.h"
#include "sdmmc.h"
#include "sdmmc_driver.h"
#include "../soc/gpio.h"
#include "../libs/fatfs/ff.h"
extern sdmmc_t sd_sdmmc;
extern sdmmc_storage_t sd_storage;
static u32 sd_mode = SD_UHS_SDR104;
u32 nx_sd_mode_get()
{
return sd_mode;
}
int nx_sd_init_retry(bool power_cycle)
{
u32 bus_width = SDMMC_BUS_WIDTH_4;
u32 type = SDHCI_TIMING_UHS_SDR104;
// Power cycle SD card.
if (power_cycle)
{
sd_mode--;
sdmmc_storage_end(&sd_storage);
}
// Get init parameters.
switch (sd_mode)
{
case SD_INIT_FAIL: // Reset to max.
return 0;
case SD_1BIT_HS25:
bus_width = SDMMC_BUS_WIDTH_1;
type = SDHCI_TIMING_SD_HS25;
break;
case SD_4BIT_HS25:
type = SDHCI_TIMING_SD_HS25;
break;
case SD_UHS_SDR82:
type = SDHCI_TIMING_UHS_SDR82;
break;
case SD_UHS_SDR104:
type = SDHCI_TIMING_UHS_SDR104;
break;
default:
sd_mode = SD_UHS_SDR104;
}
return sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, bus_width, type);
}
bool nx_sd_initialize(bool power_cycle)
{
if (power_cycle)
sdmmc_storage_end(&sd_storage);
int res = !nx_sd_init_retry(false);
while (true)
{
if (!res)
return true;
else
{
if (sd_mode == SD_INIT_FAIL)
break;
res = !nx_sd_init_retry(true);
}
}
return false;
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef NX_SD_H
#define NX_SD_H
#include "../utils/types.h"
enum
{
SD_INIT_FAIL = 0,
SD_1BIT_HS25 = 1,
SD_4BIT_HS25 = 2,
SD_UHS_SDR82 = 3,
SD_UHS_SDR104 = 4
};
u32 nx_sd_get_mode();
int nx_sd_init_retry(bool power_cycle);
bool nx_sd_initialize(bool power_cycle);
#endif

View file

@ -1,8 +1,8 @@
/* /*
* include/linux/mmc/sd.h * include/linux/mmc/sd.h
* *
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. * Copyright (c) 2005-2007 Pierre Ossman, All Rights Reserved.
* Copyright (C) 2018 CTCaer * Copyright (c) 2018 CTCaer
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -40,7 +40,9 @@
#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */ #define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */
#define SD_OCR_XPC (1 << 28) /* SDXC power control */ #define SD_OCR_XPC (1 << 28) /* SDXC power control */
#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */ #define SD_OCR_CCS (1 << 30) /* Card Capacity Status */
#define SD_OCR_VDD_27_34 (0x7F << 15) /* VDD voltage 2.7 ~ 3.4 */
#define SD_OCR_VDD_32_33 (1 << 20) /* VDD voltage 3.2 ~ 3.3 */ #define SD_OCR_VDD_32_33 (1 << 20) /* VDD voltage 3.2 ~ 3.3 */
#define SD_OCR_VDD_18 (1 << 7) /* VDD voltage 1.8 */
/* /*
* SD_SWITCH argument format: * SD_SWITCH argument format:
@ -104,6 +106,11 @@
#define SD_SET_CURRENT_LIMIT_600 2 #define SD_SET_CURRENT_LIMIT_600 2
#define SD_SET_CURRENT_LIMIT_800 3 #define SD_SET_CURRENT_LIMIT_800 3
#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200)
#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400)
#define SD_MAX_CURRENT_600 (1 << SD_SET_CURRENT_LIMIT_600)
#define SD_MAX_CURRENT_800 (1 << SD_SET_CURRENT_LIMIT_800)
/* /*
* SD_SWITCH mode * SD_SWITCH mode
*/ */

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2018 naehrwert * Copyright (c) 2018 naehrwert
* Copyright (C) 2018 CTCaer * Copyright (c) 2018-2019 CTCaer
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,
@ -20,6 +20,7 @@
#include <stdio.h> #include <stdio.h>
#include "sdmmc.h" #include "sdmmc.h"
#include "mmc.h" #include "mmc.h"
#include "nx_sd.h"
#include "sd.h" #include "sd.h"
#include "../utils/types.h" #include "../utils/types.h"
#include "../utils/util.h" #include "../utils/util.h"
@ -188,6 +189,7 @@ static int _sdmmc_storage_execute_cmd_type1_ex(sdmmc_storage_t *storage, u32 *re
if (_sdmmc_storage_check_result(*resp)) if (_sdmmc_storage_check_result(*resp))
if (expected_state == 0x10 || R1_CURRENT_STATE(*resp) == expected_state) if (expected_state == 0x10 || R1_CURRENT_STATE(*resp) == expected_state)
return 1; return 1;
return 0; return 0;
} }
@ -201,6 +203,7 @@ static int _sdmmc_storage_go_idle_state(sdmmc_storage_t *storage)
{ {
sdmmc_cmd_t cmd; sdmmc_cmd_t cmd;
sdmmc_init_cmd(&cmd, MMC_GO_IDLE_STATE, 0, SDMMC_RSP_TYPE_0, 0); sdmmc_init_cmd(&cmd, MMC_GO_IDLE_STATE, 0, SDMMC_RSP_TYPE_0, 0);
return sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0); return sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0);
} }
@ -210,7 +213,9 @@ static int _sdmmc_storage_get_cid(sdmmc_storage_t *storage, void *buf)
sdmmc_init_cmd(&cmd, MMC_ALL_SEND_CID, 0, SDMMC_RSP_TYPE_2, 0); sdmmc_init_cmd(&cmd, MMC_ALL_SEND_CID, 0, SDMMC_RSP_TYPE_2, 0);
if (!sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0)) if (!sdmmc_execute_cmd(storage->sdmmc, &cmd, 0, 0))
return 0; return 0;
sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2); sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2);
return 1; return 1;
} }
@ -225,7 +230,9 @@ static int _sdmmc_storage_get_csd(sdmmc_storage_t *storage, void *buf)
sdmmc_init_cmd(&cmdbuf, MMC_SEND_CSD, storage->rca << 16, SDMMC_RSP_TYPE_2, 0); sdmmc_init_cmd(&cmdbuf, MMC_SEND_CSD, storage->rca << 16, SDMMC_RSP_TYPE_2, 0);
if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0)) if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, 0, 0))
return 0; return 0;
sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2); sdmmc_get_rsp(storage->sdmmc, buf, 0x10, SDMMC_RSP_TYPE_2);
return 1; return 1;
} }
@ -247,9 +254,9 @@ static int _sdmmc_storage_check_status(sdmmc_storage_t *storage)
static int _sdmmc_storage_readwrite_ex(sdmmc_storage_t *storage, u32 *blkcnt_out, u32 sector, u32 num_sectors, void *buf, u32 is_write) static int _sdmmc_storage_readwrite_ex(sdmmc_storage_t *storage, u32 *blkcnt_out, u32 sector, u32 num_sectors, void *buf, u32 is_write)
{ {
u32 tmp = 0;
sdmmc_cmd_t cmdbuf; sdmmc_cmd_t cmdbuf;
sdmmc_req_t reqbuf; sdmmc_req_t reqbuf;
u32 tmp = 0;
sdmmc_init_cmd(&cmdbuf, is_write ? MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK, sector, SDMMC_RSP_TYPE_1, 0); sdmmc_init_cmd(&cmdbuf, is_write ? MMC_WRITE_MULTIPLE_BLOCK : MMC_READ_MULTIPLE_BLOCK, sector, SDMMC_RSP_TYPE_1, 0);
@ -261,12 +268,13 @@ static int _sdmmc_storage_readwrite_ex(sdmmc_storage_t *storage, u32 *blkcnt_out
reqbuf.is_auto_cmd12 = 1; reqbuf.is_auto_cmd12 = 1;
if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, blkcnt_out)) if (!sdmmc_execute_cmd(storage->sdmmc, &cmdbuf, &reqbuf, blkcnt_out))
{ {
sdmmc_stop_transmission(storage->sdmmc, &tmp); sdmmc_stop_transmission(storage->sdmmc, &tmp);
_sdmmc_storage_get_status(storage, &tmp, 0); _sdmmc_storage_get_status(storage, &tmp, 0);
return 0; return 0;
} }
return 1; return 1;
} }
@ -274,36 +282,58 @@ int sdmmc_storage_end(sdmmc_storage_t *storage)
{ {
if (!_sdmmc_storage_go_idle_state(storage)) if (!_sdmmc_storage_go_idle_state(storage))
return 0; return 0;
sdmmc_end(storage->sdmmc); sdmmc_end(storage->sdmmc);
return 1; return 1;
} }
static int _sdmmc_storage_readwrite(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf, u32 is_write) static int _sdmmc_storage_readwrite(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf, u32 is_write)
{ {
u8 *bbuf = (u8 *)buf; u8 *bbuf = (u8 *)buf;
bool first_reinit = false;
while (num_sectors) while (num_sectors)
{ {
u32 blkcnt = 0; u32 blkcnt = 0;
//Retry 9 times on error. // Retry 5 times if failed.
u32 retries = 10; u32 retries = 5;
do do
{ {
reinit_try:
if (_sdmmc_storage_readwrite_ex(storage, &blkcnt, sector, MIN(num_sectors, 0xFFFF), bbuf, is_write)) if (_sdmmc_storage_readwrite_ex(storage, &blkcnt, sector, MIN(num_sectors, 0xFFFF), bbuf, is_write))
goto out; goto out;
else else
retries--; retries--;
msleep(100); msleep(50);
} while (retries); } while (retries);
// Disk IO failure! Reinit SD Card to a lower speed.
if (storage->sdmmc->id == SDMMC_1)
{
int res;
if (!first_reinit)
res = nx_sd_initialize(true);
else
res = nx_sd_init_retry(true);
retries = 3;
first_reinit = true;
if (res)
goto reinit_try;
}
return 0; return 0;
out:; out:
DPRINTF("readwrite: %08X\n", blkcnt); DPRINTF("readwrite: %08X\n", blkcnt);
sector += blkcnt; sector += blkcnt;
num_sectors -= blkcnt; num_sectors -= blkcnt;
bbuf += 512 * blkcnt; bbuf += 512 * blkcnt;
} }
return 1; return 1;
} }
@ -427,10 +457,10 @@ static int _mmc_storage_get_op_cond_inner(sdmmc_storage_t *storage, u32 *pout, u
switch (power) switch (power)
{ {
case SDMMC_POWER_1_8: case SDMMC_POWER_1_8:
arg = 0x40000080; //Sector access, voltage. arg = SD_OCR_CCS | SD_OCR_VDD_18;
break; break;
case SDMMC_POWER_3_3: case SDMMC_POWER_3_3:
arg = 0x403F8000; //Sector access, voltage. arg = SD_OCR_CCS | SD_OCR_VDD_27_34;
break; break;
default: default:
return 0; return 0;
@ -445,21 +475,24 @@ static int _mmc_storage_get_op_cond_inner(sdmmc_storage_t *storage, u32 *pout, u
static int _mmc_storage_get_op_cond(sdmmc_storage_t *storage, u32 power) static int _mmc_storage_get_op_cond(sdmmc_storage_t *storage, u32 power)
{ {
u32 timeout = get_tmr_ms() + 1500; u64 timeout = get_tmr_ms() + 1500;
while (1) while (1)
{ {
u32 cond = 0; u32 cond = 0;
if (!_mmc_storage_get_op_cond_inner(storage, &cond, power)) if (!_mmc_storage_get_op_cond_inner(storage, &cond, power))
break; break;
if (cond & MMC_CARD_BUSY) if (cond & MMC_CARD_BUSY)
{ {
if (cond & 0x40000000) if (cond & SD_OCR_CCS)
storage->has_sector_access = 1; storage->has_sector_access = 1;
return 1; return 1;
} }
if (get_tmr_ms() > timeout) if (get_tmr_ms() > timeout)
break; break;
usleep(1000); usleep(1000);
} }
@ -589,6 +622,7 @@ static int _mmc_storage_switch_buswidth(sdmmc_storage_t *storage, u32 bus_width)
if (_sdmmc_storage_check_status(storage)) if (_sdmmc_storage_check_status(storage))
{ {
sdmmc_set_bus_width(storage->sdmmc, bus_width); sdmmc_set_bus_width(storage->sdmmc, bus_width);
return 1; return 1;
} }
@ -599,14 +633,19 @@ static int _mmc_storage_enable_HS(sdmmc_storage_t *storage, int check)
{ {
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS))) if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS)))
return 0; return 0;
if (check && !_sdmmc_storage_check_status(storage)) if (check && !_sdmmc_storage_check_status(storage))
return 0; return 0;
if (!sdmmc_setup_clock(storage->sdmmc, 2))
if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_MMC_HS52))
return 0; return 0;
DPRINTF("[MMC] switched to HS\n");
DPRINTF("[MMC] switched to HS\n");
storage->csd.busspeed = 52; storage->csd.busspeed = 52;
if (check || _sdmmc_storage_check_status(storage)) if (check || _sdmmc_storage_check_status(storage))
return 1; return 1;
return 0; return 0;
} }
@ -614,12 +653,16 @@ static int _mmc_storage_enable_HS200(sdmmc_storage_t *storage)
{ {
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200))) if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200)))
return 0; return 0;
if (!sdmmc_setup_clock(storage->sdmmc, 3))
if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_MMC_HS200))
return 0; return 0;
if (!sdmmc_config_tuning(storage->sdmmc, 3, MMC_SEND_TUNING_BLOCK_HS200))
if (!sdmmc_tuning_execute(storage->sdmmc, SDHCI_TIMING_MMC_HS200, MMC_SEND_TUNING_BLOCK_HS200))
return 0; return 0;
DPRINTF("[MMC] switched to HS200\n");
DPRINTF("[MMC] switched to HS200\n");
storage->csd.busspeed = 200; storage->csd.busspeed = 200;
return _sdmmc_storage_check_status(storage); return _sdmmc_storage_check_status(storage);
} }
@ -627,41 +670,46 @@ static int _mmc_storage_enable_HS400(sdmmc_storage_t *storage)
{ {
if (!_mmc_storage_enable_HS200(storage)) if (!_mmc_storage_enable_HS200(storage))
return 0; return 0;
sdmmc_get_venclkctl(storage->sdmmc);
sdmmc_set_tap_value(storage->sdmmc);
if (!_mmc_storage_enable_HS(storage, 0)) if (!_mmc_storage_enable_HS(storage, 0))
return 0; return 0;
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_DDR_BUS_WIDTH_8))) if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_BUS_WIDTH, EXT_CSD_DDR_BUS_WIDTH_8)))
return 0; return 0;
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400))) if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400)))
return 0; return 0;
if (!sdmmc_setup_clock(storage->sdmmc, 4))
if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_MMC_HS400))
return 0; return 0;
DPRINTF("[MMC] switched to HS400\n");
DPRINTF("[MMC] switched to HS400\n");
storage->csd.busspeed = 400; storage->csd.busspeed = 400;
return _sdmmc_storage_check_status(storage); return _sdmmc_storage_check_status(storage);
} }
static int _mmc_storage_enable_highspeed(sdmmc_storage_t *storage, u32 card_type, u32 type) static int _mmc_storage_enable_highspeed(sdmmc_storage_t *storage, u32 card_type, u32 type)
{ {
//TODO: this should be a config item. if (sdmmc_get_io_power(storage->sdmmc) != SDMMC_POWER_1_8)
// --v
if (!1 || sdmmc_get_voltage(storage->sdmmc) != SDMMC_POWER_1_8)
goto out; goto out;
if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 && if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 &&
card_type & EXT_CSD_CARD_TYPE_HS400_1_8V && card_type & EXT_CSD_CARD_TYPE_HS400_1_8V && type == SDHCI_TIMING_MMC_HS400)
type == 4)
return _mmc_storage_enable_HS400(storage); return _mmc_storage_enable_HS400(storage);
if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 || if (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_8 ||
(sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_4 (sdmmc_get_bus_width(storage->sdmmc) == SDMMC_BUS_WIDTH_4
&& card_type & EXT_CSD_CARD_TYPE_HS200_1_8V && card_type & EXT_CSD_CARD_TYPE_HS200_1_8V
&& (type == 4 || type == 3))) && (type == SDHCI_TIMING_MMC_HS400 || type == SDHCI_TIMING_MMC_HS200)))
return _mmc_storage_enable_HS200(storage); return _mmc_storage_enable_HS200(storage);
out:; out:
if (card_type & EXT_CSD_CARD_TYPE_HS_52) if (card_type & EXT_CSD_CARD_TYPE_HS_52)
return _mmc_storage_enable_HS(storage, 1); return _mmc_storage_enable_HS(storage, 1);
return 1; return 1;
} }
@ -669,53 +717,54 @@ static int _mmc_storage_enable_bkops(sdmmc_storage_t *storage)
{ {
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_SET_BITS, EXT_CSD_BKOPS_EN, EXT_CSD_BKOPS_LEVEL_2))) if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_SET_BITS, EXT_CSD_BKOPS_EN, EXT_CSD_BKOPS_LEVEL_2)))
return 0; return 0;
return _sdmmc_storage_check_status(storage); return _sdmmc_storage_check_status(storage);
} }
int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type) int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type)
{ {
memset(storage, 0, sizeof(sdmmc_storage_t)); memset(storage, 0, sizeof(sdmmc_storage_t));
storage->sdmmc = sdmmc; storage->sdmmc = sdmmc;
storage->rca = 2; //TODO: this could be a config item. storage->rca = 2; //TODO: this could be a config item.
if (!sdmmc_init(sdmmc, id, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_1, 0, 0)) if (!sdmmc_init(sdmmc, SDMMC_4, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_1, SDHCI_TIMING_MMC_ID, SDMMC_AUTO_CAL_DISABLE))
return 0; return 0;
DPRINTF("[MMC] after init\n"); DPRINTF("[MMC] after init\n");
usleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor); usleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor);
if (!_sdmmc_storage_go_idle_state(storage)) if (!_sdmmc_storage_go_idle_state(storage))
return 0; return 0;
DPRINTF("[MMC] went to idle state\n"); DPRINTF("[MMC] went to idle state\n");
if (!_mmc_storage_get_op_cond(storage, SDMMC_POWER_1_8)) if (!_mmc_storage_get_op_cond(storage, SDMMC_POWER_1_8))
return 0; return 0;
DPRINTF("[MMC] got op cond\n"); DPRINTF("[MMC] got op cond\n");
if (!_sdmmc_storage_get_cid(storage, storage->raw_cid)) if (!_sdmmc_storage_get_cid(storage, storage->raw_cid))
return 0; return 0;
DPRINTF("[MMC] got cid\n"); DPRINTF("[MMC] got cid\n");
if (!_mmc_storage_set_relative_addr(storage)) if (!_mmc_storage_set_relative_addr(storage))
return 0; return 0;
DPRINTF("[MMC] set relative addr\n"); DPRINTF("[MMC] set relative addr\n");
if (!_sdmmc_storage_get_csd(storage, storage->raw_csd)) if (!_sdmmc_storage_get_csd(storage, storage->raw_csd))
return 0; return 0;
DPRINTF("[MMC] got csd\n"); DPRINTF("[MMC] got csd\n");
_mmc_storage_parse_csd(storage); _mmc_storage_parse_csd(storage);
if (!sdmmc_setup_clock(storage->sdmmc, 1)) if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_MMC_LS26))
return 0; return 0;
DPRINTF("[MMC] after setup clock\n"); DPRINTF("[MMC] after setup clock\n");
if (!_sdmmc_storage_select_card(storage)) if (!_sdmmc_storage_select_card(storage))
return 0; return 0;
DPRINTF("[MMC] card selected\n"); DPRINTF("[MMC] card selected\n");
if (!_sdmmc_storage_set_blocklen(storage, 512)) if (!_sdmmc_storage_set_blocklen(storage, 512))
return 0; return 0;
DPRINTF("[MMC] set blocklen to 512\n"); DPRINTF("[MMC] set blocklen to 512\n");
u32 *csd = (u32 *)storage->raw_csd; u32 *csd = (u32 *)storage->raw_csd;
//Check system specification version, only version 4.0 and later support below features. //Check system specification version, only version 4.0 and later support below features.
@ -727,36 +776,29 @@ int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32
if (!_mmc_storage_switch_buswidth(storage, bus_width)) if (!_mmc_storage_switch_buswidth(storage, bus_width))
return 0; return 0;
DPRINTF("[MMC] switched buswidth\n"); DPRINTF("[MMC] switched buswidth\n");
u8 buf[512];
u8 *ext_csd = (u8 *)malloc(512); memset(buf, 0, sizeof(buf));
if (!_mmc_storage_get_ext_csd(storage, ext_csd)) if (!_mmc_storage_get_ext_csd(storage, buf))
{
free(ext_csd);
return 0; return 0;
} DPRINTF("[MMC] got ext_csd\n");
free(ext_csd);
DPRINTF("[MMC] got ext_csd\n");
_mmc_storage_parse_cid(storage); //This needs to be after csd and ext_csd _mmc_storage_parse_cid(storage); //This needs to be after csd and ext_csd
/* When auto BKOPS is enabled the mmc device should be powered all the time until we disable this and check status. /* When auto BKOPS is enabled the mmc device should be powered all the time until we disable this and check status.
Disable it for now until BKOPS disable added to power down sequence at sdmmc_storage_end(). Disable it for now until BKOPS disable added to power down sequence at sdmmc_storage_end().
Additionally this works only when we put the device in idle mode which we don't after enabling it. */ Additionally this works only when we put the device in idle mode which we don't after enabling it. */
if (storage->ext_csd.bkops & 0x1 && !(storage->ext_csd.bkops_en & EXT_CSD_BKOPS_LEVEL_2) && 0) if (0 && storage->ext_csd.bkops & 0x1 && !(storage->ext_csd.bkops_en & EXT_CSD_BKOPS_LEVEL_2))
{ {
_mmc_storage_enable_bkops(storage); _mmc_storage_enable_bkops(storage);
DPRINTF("[MMC] BKOPS enabled\n"); DPRINTF("[MMC] BKOPS enabled\n");
}
else
{
DPRINTF("[MMC] BKOPS disabled\n");
} }
if (!_mmc_storage_enable_highspeed(storage, storage->ext_csd.card_type, type)) if (!_mmc_storage_enable_highspeed(storage, storage->ext_csd.card_type, type))
return 0; return 0;
DPRINTF("[MMC] succesfully switched to highspeed mode\n"); DPRINTF("[MMC] succesfully switched to HS mode\n");
sdmmc_sd_clock_ctrl(storage->sdmmc, 1); sdmmc_card_clock_ctrl(storage->sdmmc, SDMMC_AUTO_CAL_ENABLE);
return 1; return 1;
} }
@ -765,8 +807,10 @@ int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition)
{ {
if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_PART_CONFIG, partition))) if (!_mmc_storage_switch(storage, SDMMC_SWITCH(MMC_SWITCH_MODE_WRITE_BYTE, EXT_CSD_PART_CONFIG, partition)))
return 0; return 0;
if (!_sdmmc_storage_check_status(storage)) if (!_sdmmc_storage_check_status(storage))
return 0; return 0;
storage->partition = partition; storage->partition = partition;
return 1; return 1;
} }
@ -780,6 +824,7 @@ static int _sd_storage_execute_app_cmd(sdmmc_storage_t *storage, u32 expected_st
u32 tmp; u32 tmp;
if (!_sdmmc_storage_execute_cmd_type1_ex(storage, &tmp, MMC_APP_CMD, storage->rca << 16, 0, expected_state, mask)) if (!_sdmmc_storage_execute_cmd_type1_ex(storage, &tmp, MMC_APP_CMD, storage->rca << 16, 0, expected_state, mask))
return 0; return 0;
return sdmmc_execute_cmd(storage->sdmmc, cmd, req, blkcnt_out); return sdmmc_execute_cmd(storage->sdmmc, cmd, req, blkcnt_out);
} }
@ -787,6 +832,7 @@ static int _sd_storage_execute_app_cmd_type1(sdmmc_storage_t *storage, u32 *resp
{ {
if (!_sdmmc_storage_execute_cmd_type1(storage, MMC_APP_CMD, storage->rca << 16, 0, R1_STATE_TRAN)) if (!_sdmmc_storage_execute_cmd_type1(storage, MMC_APP_CMD, storage->rca << 16, 0, R1_STATE_TRAN))
return 0; return 0;
return _sdmmc_storage_execute_cmd_type1_ex(storage, resp, cmd, arg, check_busy, expected_state, 0); return _sdmmc_storage_execute_cmd_type1_ex(storage, resp, cmd, arg, check_busy, expected_state, 0);
} }
@ -816,32 +862,27 @@ static int _sd_storage_get_op_cond_once(sdmmc_storage_t *storage, u32 *cond, int
// This is needed for most cards. Do not set bit7 even if 1.8V is supported. // This is needed for most cards. Do not set bit7 even if 1.8V is supported.
arg |= SD_OCR_VDD_32_33; arg |= SD_OCR_VDD_32_33;
sdmmc_init_cmd(&cmdbuf, SD_APP_OP_COND, arg, SDMMC_RSP_TYPE_3, 0); sdmmc_init_cmd(&cmdbuf, SD_APP_OP_COND, arg, SDMMC_RSP_TYPE_3, 0);
DPRINTF("[SD] before _sd_storage_execute_app_cmd\n");
if (!_sd_storage_execute_app_cmd(storage, 0x10, is_version_1 ? 0x400000 : 0, &cmdbuf, 0, 0)) if (!_sd_storage_execute_app_cmd(storage, 0x10, is_version_1 ? 0x400000 : 0, &cmdbuf, 0, 0))
return 0; return 0;
DPRINTF("[SD] before sdmmc_get_rsp\n");
return sdmmc_get_rsp(storage->sdmmc, cond, 4, SDMMC_RSP_TYPE_3); return sdmmc_get_rsp(storage->sdmmc, cond, 4, SDMMC_RSP_TYPE_3);
} }
static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, int supports_low_voltage) static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, int supports_low_voltage)
{ {
u32 timeout = get_tmr_ms() + 1500; u64 timeout = get_tmr_ms() + 1500;
while (1) while (1)
{ {
u32 cond = 0; u32 cond = 0;
if (!_sd_storage_get_op_cond_once(storage, &cond, is_version_1, supports_low_voltage)) if (!_sd_storage_get_op_cond_once(storage, &cond, is_version_1, supports_low_voltage))
{
DPRINTF("[SD] _sd_storage_get_op_cond_once failed\r\n");
break; break;
}
if (cond & MMC_CARD_BUSY) if (cond & MMC_CARD_BUSY)
{ {
if (cond & SD_OCR_CCS) if (cond & SD_OCR_CCS)
storage->has_sector_access = 1; storage->has_sector_access = 1;
// Check if card supports 1.8V signaling.
if (cond & SD_ROCR_S18A && supports_low_voltage) if (cond & SD_ROCR_S18A && supports_low_voltage)
{ {
//The low voltage regulator configuration is valid for SDMMC1 only. //The low voltage regulator configuration is valid for SDMMC1 only.
@ -852,7 +893,7 @@ static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, i
return 0; return 0;
storage->is_low_voltage = 1; storage->is_low_voltage = 1;
DPRINTF("-> switched to low voltage\n"); DPRINTF("-> switched to low voltage\n");
} }
} }
@ -863,8 +904,6 @@ static int _sd_storage_get_op_cond(sdmmc_storage_t *storage, int is_version_1, i
msleep(10); // Needs to be at least 10ms for some SD Cards msleep(10); // Needs to be at least 10ms for some SD Cards
} }
DPRINTF("[SD] _sd_storage_get_op_cond Timeout\r\n");
return 0; return 0;
} }
@ -873,7 +912,7 @@ static int _sd_storage_get_rca(sdmmc_storage_t *storage)
sdmmc_cmd_t cmdbuf; sdmmc_cmd_t cmdbuf;
sdmmc_init_cmd(&cmdbuf, SD_SEND_RELATIVE_ADDR, 0, SDMMC_RSP_TYPE_4, 0); sdmmc_init_cmd(&cmdbuf, SD_SEND_RELATIVE_ADDR, 0, SDMMC_RSP_TYPE_4, 0);
u32 timeout = get_tmr_ms() + 1500; u64 timeout = get_tmr_ms() + 1500;
while (1) while (1)
{ {
@ -991,34 +1030,37 @@ int _sd_storage_switch(sdmmc_storage_t *storage, void *buf, int mode, int group,
return _sdmmc_storage_check_result(tmp); return _sdmmc_storage_check_result(tmp);
} }
void _sd_storage_set_current_limit(sdmmc_storage_t *storage, u8 *buf) void _sd_storage_set_current_limit(sdmmc_storage_t *storage, u16 current_limit, u8 *buf)
{ {
u32 pwr = SD_SET_CURRENT_LIMIT_800; u32 pwr = SD_SET_CURRENT_LIMIT_200;
if (current_limit & SD_MAX_CURRENT_800)
pwr = SD_SET_CURRENT_LIMIT_800;
else if (current_limit & SD_MAX_CURRENT_600)
pwr = SD_SET_CURRENT_LIMIT_600;
else if (current_limit & SD_MAX_CURRENT_400)
pwr = SD_SET_CURRENT_LIMIT_400;
_sd_storage_switch(storage, buf, SD_SWITCH_SET, 3, pwr); _sd_storage_switch(storage, buf, SD_SWITCH_SET, 3, pwr);
while (pwr > 0) if (((buf[15] >> 4) & 0x0F) == pwr)
{ {
pwr--; switch (pwr)
_sd_storage_switch(storage, buf, SD_SWITCH_SET, 3, pwr); {
if (((buf[15] >> 4) & 0x0F) == pwr) case SD_SET_CURRENT_LIMIT_800:
DPRINTF("[SD] power limit raised to 800mA\n");
break; break;
} case SD_SET_CURRENT_LIMIT_600:
DPRINTF("[SD] power limit raised to 600mA\n");
switch (pwr) break;
{ case SD_SET_CURRENT_LIMIT_400:
case SD_SET_CURRENT_LIMIT_800: DPRINTF("[SD] power limit raised to 400mA\n");
DPRINTF("[SD] Power limit raised to 800mA\n"); break;
break; default:
case SD_SET_CURRENT_LIMIT_600: case SD_SET_CURRENT_LIMIT_200:
DPRINTF("[SD] Power limit raised to 600mA\n"); DPRINTF("[SD] power limit defaulted to 200mA\n");
break; break;
case SD_SET_CURRENT_LIMIT_400: }
DPRINTF("[SD] Power limit raised to 800mA\n");
break;
default:
case SD_SET_CURRENT_LIMIT_200:
DPRINTF("[SD] Power limit defaulted to 200mA\n");
break;
} }
} }
@ -1026,62 +1068,91 @@ int _sd_storage_enable_highspeed(sdmmc_storage_t *storage, u32 hs_type, u8 *buf)
{ {
if (!_sd_storage_switch(storage, buf, SD_SWITCH_CHECK, 0, hs_type)) if (!_sd_storage_switch(storage, buf, SD_SWITCH_CHECK, 0, hs_type))
return 0; return 0;
DPRINTF("[SD] supports switch to (U)HS mode\n");
u32 type_out = buf[16] & 0xF; u32 type_out = buf[16] & 0xF;
if (type_out != hs_type) if (type_out != hs_type)
return 0; return 0;
DPRINTF("[SD] supports selected (U)HS mode\n");
if ((((u16)buf[0] << 8) | buf[1]) < 0x320) u16 total_pwr_consumption = ((u16)buf[0] << 8) | buf[1];
DPRINTF("[SD] total max current: %d\n", total_pwr_consumption);
if (total_pwr_consumption <= 800)
{ {
if (!_sd_storage_switch(storage, buf, SD_SWITCH_SET, 0, hs_type)) if (!_sd_storage_switch(storage, buf, SD_SWITCH_SET, 0, hs_type))
return 0; return 0;
if (type_out != (buf[16] & 0xF)) if (type_out != (buf[16] & 0xF))
return 0; return 0;
}
return 1; return 1;
}
DPRINTF("[SD] card max current over limit\n");
return 0;
} }
int _sd_storage_enable_highspeed_low_volt(sdmmc_storage_t *storage, u32 type, u8 *buf) int _sd_storage_enable_uhs_low_volt(sdmmc_storage_t *storage, u32 type, u8 *buf)
{ {
// Try to raise the current limit to let the card perform better.
_sd_storage_set_current_limit(storage, buf);
if (sdmmc_get_bus_width(storage->sdmmc) != SDMMC_BUS_WIDTH_4) if (sdmmc_get_bus_width(storage->sdmmc) != SDMMC_BUS_WIDTH_4)
return 0; return 0;
if (!_sd_storage_switch_get(storage, buf)) if (!_sd_storage_switch_get(storage, buf))
return 0; return 0;
//gfx_hexdump(0, (u8 *)buf, 64);
u8 access_mode = buf[13];
u16 current_limit = buf[7] | buf[6] << 8;
// Try to raise the current limit to let the card perform better.
_sd_storage_set_current_limit(storage, current_limit, buf);
u32 hs_type = 0; u32 hs_type = 0;
switch (type) switch (type)
{ {
case 11: case SDHCI_TIMING_UHS_SDR104:
case SDHCI_TIMING_UHS_SDR82:
// Fall through if not supported. // Fall through if not supported.
if (buf[13] & SD_MODE_UHS_SDR104) if (access_mode & SD_MODE_UHS_SDR104)
{ {
type = 11;
hs_type = UHS_SDR104_BUS_SPEED; hs_type = UHS_SDR104_BUS_SPEED;
DPRINTF("[SD] Bus speed set to SDR104\n"); DPRINTF("[SD] bus speed set to SDR104\n");
storage->csd.busspeed = 104; switch (type)
{
case SDHCI_TIMING_UHS_SDR104:
storage->csd.busspeed = 104;
break;
case SDHCI_TIMING_UHS_SDR82:
storage->csd.busspeed = 82;
break;
}
break; break;
} }
case 10: case SDHCI_TIMING_UHS_SDR50:
if (buf[13] & SD_MODE_UHS_SDR50) if (access_mode & SD_MODE_UHS_SDR50)
{ {
type = 10; type = SDHCI_TIMING_UHS_SDR50;
hs_type = UHS_SDR50_BUS_SPEED; hs_type = UHS_SDR50_BUS_SPEED;
DPRINTF("[SD] Bus speed set to SDR50\n"); DPRINTF("[SD] bus speed set to SDR50\n");
storage->csd.busspeed = 50; storage->csd.busspeed = 50;
break; break;
} }
case 8: case SDHCI_TIMING_UHS_SDR25:
if (!(buf[13] & SD_MODE_UHS_SDR12)) if (access_mode & SD_MODE_UHS_SDR25)
{
type = SDHCI_TIMING_UHS_SDR25;
hs_type = UHS_SDR50_BUS_SPEED;
DPRINTF("[SD] bus speed set to SDR25\n");
storage->csd.busspeed = 25;
break;
}
case SDHCI_TIMING_UHS_SDR12:
if (!(access_mode & SD_MODE_UHS_SDR12))
return 0; return 0;
type = 8; type = SDHCI_TIMING_UHS_SDR12;
hs_type = UHS_SDR12_BUS_SPEED; hs_type = UHS_SDR12_BUS_SPEED;
DPRINTF("[SD] Bus speed set to SDR12\n"); DPRINTF("[SD] bus speed set to SDR12\n");
storage->csd.busspeed = 12; storage->csd.busspeed = 12;
break; break;
default: default:
@ -1091,106 +1162,38 @@ int _sd_storage_enable_highspeed_low_volt(sdmmc_storage_t *storage, u32 type, u8
if (!_sd_storage_enable_highspeed(storage, hs_type, buf)) if (!_sd_storage_enable_highspeed(storage, hs_type, buf))
return 0; return 0;
DPRINTF("[SD] card accepted UHS\n");
if (!sdmmc_setup_clock(storage->sdmmc, type)) if (!sdmmc_setup_clock(storage->sdmmc, type))
return 0; return 0;
if (!sdmmc_config_tuning(storage->sdmmc, type, MMC_SEND_TUNING_BLOCK)) DPRINTF("[SD] setup clock\n");
if (!sdmmc_tuning_execute(storage->sdmmc, type, MMC_SEND_TUNING_BLOCK))
return 0; return 0;
DPRINTF("[SD] config tuning\n");
return _sdmmc_storage_check_status(storage); return _sdmmc_storage_check_status(storage);
} }
int _sd_storage_enable_highspeed_high_volt(sdmmc_storage_t *storage, u8 *buf) int _sd_storage_enable_hs_high_volt(sdmmc_storage_t *storage, u8 *buf)
{ {
if (!_sd_storage_switch_get(storage, buf)) if (!_sd_storage_switch_get(storage, buf))
return 0; return 0;
if (!(buf[13] & SD_MODE_HIGH_SPEED)) //gfx_hexdump(0, (u8 *)buf, 64);
u8 access_mode = buf[13];
u16 current_limit = buf[7] | buf[6] << 8;
// Try to raise the current limit to let the card perform better.
_sd_storage_set_current_limit(storage, current_limit, buf);
if (!(access_mode & SD_MODE_HIGH_SPEED))
return 1; return 1;
if (!_sd_storage_enable_highspeed(storage, 1, buf)) if (!_sd_storage_enable_highspeed(storage, HIGH_SPEED_BUS_SPEED, buf))
return 0; return 0;
if (!_sdmmc_storage_check_status(storage)) if (!_sdmmc_storage_check_status(storage))
return 0; return 0;
return sdmmc_setup_clock(storage->sdmmc, 7);
}
static void _sd_storage_parse_ssr(sdmmc_storage_t *storage) return sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_SD_HS25);
{
// unstuff_bits supports only 4 u32 so break into 2 x 16byte groups
u32 raw_ssr1[4];
u32 raw_ssr2[4];
raw_ssr1[3] = *(u32 *)&storage->raw_ssr[12];
raw_ssr1[2] = *(u32 *)&storage->raw_ssr[8];
raw_ssr1[1] = *(u32 *)&storage->raw_ssr[4];
raw_ssr1[0] = *(u32 *)&storage->raw_ssr[0];
raw_ssr2[3] = *(u32 *)&storage->raw_ssr[28];
raw_ssr2[2] = *(u32 *)&storage->raw_ssr[24];
raw_ssr2[1] = *(u32 *)&storage->raw_ssr[20];
raw_ssr2[0] = *(u32 *)&storage->raw_ssr[16];
storage->ssr.bus_width = (unstuff_bits(raw_ssr1, 510 - 384, 2) & SD_BUS_WIDTH_4) ? 4 : 1;
switch(unstuff_bits(raw_ssr1, 440 - 384, 8))
{
case 0:
storage->ssr.speed_class = 0;
break;
case 1:
storage->ssr.speed_class = 2;
break;
case 2:
storage->ssr.speed_class = 4;
break;
case 3:
storage->ssr.speed_class = 6;
break;
case 4:
storage->ssr.speed_class = 10;
break;
default:
storage->ssr.speed_class = unstuff_bits(raw_ssr1, 440 - 384, 8);
break;
}
storage->ssr.uhs_grade = unstuff_bits(raw_ssr1, 396 - 384, 4);
storage->ssr.video_class = unstuff_bits(raw_ssr1, 384 - 384, 8);
storage->ssr.app_class = unstuff_bits(raw_ssr2, 336 - 256, 4);
}
static int _sd_storage_get_ssr(sdmmc_storage_t *storage, u8 *buf)
{
sdmmc_cmd_t cmdbuf;
sdmmc_init_cmd(&cmdbuf, SD_APP_SD_STATUS, 0, SDMMC_RSP_TYPE_1, 0);
sdmmc_req_t reqbuf;
reqbuf.buf = buf;
reqbuf.blksize = 64;
reqbuf.num_sectors = 1;
reqbuf.is_write = 0;
reqbuf.is_multi_block = 0;
reqbuf.is_auto_cmd12 = 0;
if (!(storage->csd.cmdclass & CCC_APP_SPEC))
{
DPRINTF("[SD] ssr: Card lacks mandatory SD Status function\n");
return 0;
}
if (!_sd_storage_execute_app_cmd(storage, R1_STATE_TRAN, 0, &cmdbuf, &reqbuf, 0))
return 0;
u32 tmp = 0;
sdmmc_get_rsp(storage->sdmmc, &tmp, 4, SDMMC_RSP_TYPE_1);
//Prepare buffer for unstuff_bits
for (int i = 0; i < 64; i+=4)
{
storage->raw_ssr[i + 3] = buf[i];
storage->raw_ssr[i + 2] = buf[i + 1];
storage->raw_ssr[i + 1] = buf[i + 2];
storage->raw_ssr[i] = buf[i + 3];
}
_sd_storage_parse_ssr(storage);
return _sdmmc_storage_check_result(tmp);
} }
static void _sd_storage_parse_cid(sdmmc_storage_t *storage) static void _sd_storage_parse_cid(sdmmc_storage_t *storage)
@ -1232,45 +1235,65 @@ static void _sd_storage_parse_csd(sdmmc_storage_t *storage)
} }
} }
int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type) static bool _sdmmc_storage_supports_low_voltage(u32 bus_width, u32 type)
{ {
switch (type)
{
case SDHCI_TIMING_UHS_SDR12:
case SDHCI_TIMING_UHS_SDR25:
case SDHCI_TIMING_UHS_SDR50:
case SDHCI_TIMING_UHS_SDR104:
case SDHCI_TIMING_UHS_SDR82:
case SDHCI_TIMING_UHS_DDR50:
if (bus_width == SDMMC_BUS_WIDTH_4)
return true;
default:
return false;
}
}
int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type)
{
u8 buf[512];
int is_version_1 = 0; int is_version_1 = 0;
memset(buf, 0, sizeof(buf));
memset(storage, 0, sizeof(sdmmc_storage_t)); memset(storage, 0, sizeof(sdmmc_storage_t));
storage->sdmmc = sdmmc; storage->sdmmc = sdmmc;
DPRINTF("[SD] before init\n"); if (!sdmmc_init(sdmmc, SDMMC_1, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, SDHCI_TIMING_SD_ID, SDMMC_AUTO_CAL_DISABLE))
if (!sdmmc_init(sdmmc, id, SDMMC_POWER_3_3, SDMMC_BUS_WIDTH_1, 5, 0))
return 0; return 0;
DPRINTF("[SD] after init\n"); DPRINTF("[SD] after init\n");
usleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor); usleep(1000 + (74000 + sdmmc->divisor - 1) / sdmmc->divisor);
if (!_sdmmc_storage_go_idle_state(storage)) if (!_sdmmc_storage_go_idle_state(storage))
return 0; return 0;
DPRINTF("[SD] went to idle state\n"); DPRINTF("[SD] went to idle state\n");
is_version_1 = _sd_storage_send_if_cond(storage); is_version_1 = _sd_storage_send_if_cond(storage);
if (is_version_1 == 2) if (is_version_1 == 2)
return 0; return 0;
DPRINTF("[SD] after send if cond\n"); DPRINTF("[SD] after send if cond\n");
if (!_sd_storage_get_op_cond(storage, is_version_1, bus_width == SDMMC_BUS_WIDTH_4 && type == 11)) bool supports_low_voltage = _sdmmc_storage_supports_low_voltage(bus_width, type);
if (!_sd_storage_get_op_cond(storage, is_version_1, supports_low_voltage))
return 0; return 0;
DPRINTF("[SD] got op cond\n"); DPRINTF("[SD] got op cond\n");
if (!_sdmmc_storage_get_cid(storage, storage->raw_cid)) if (!_sdmmc_storage_get_cid(storage, storage->raw_cid))
return 0; return 0;
DPRINTF("[SD] got cid\n"); DPRINTF("[SD] got cid\n");
_sd_storage_parse_cid(storage); _sd_storage_parse_cid(storage);
if (!_sd_storage_get_rca(storage)) if (!_sd_storage_get_rca(storage))
return 0; return 0;
DPRINTF("[SD] got rca (= %04X)\n", storage->rca); DPRINTF("[SD] got rca (= %04X)\n", storage->rca);
if (!_sdmmc_storage_get_csd(storage, storage->raw_csd)) if (!_sdmmc_storage_get_csd(storage, storage->raw_csd))
return 0; return 0;
DPRINTF("[SD] got csd\n"); DPRINTF("[SD] got csd\n");
//Parse CSD. //Parse CSD.
_sd_storage_parse_csd(storage); _sd_storage_parse_csd(storage);
@ -1283,84 +1306,75 @@ int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32
storage->sec_cnt = storage->csd.c_size << 10; storage->sec_cnt = storage->csd.c_size << 10;
break; break;
default: default:
DPRINTF("[SD] Unknown CSD structure %d\n", storage->csd.structure); DPRINTF("[SD] unknown CSD structure %d\n", storage->csd.structure);
break; break;
} }
if (!storage->is_low_voltage) if (!storage->is_low_voltage)
{ {
if (!sdmmc_setup_clock(storage->sdmmc, 6)) if (!sdmmc_setup_clock(storage->sdmmc, SDHCI_TIMING_SD_DS12))
return 0; return 0;
DPRINTF("[SD] after setup clock\n"); DPRINTF("[SD] after setup clock\n");
} }
if (!_sdmmc_storage_select_card(storage)) if (!_sdmmc_storage_select_card(storage))
return 0; return 0;
DPRINTF("[SD] card selected\n"); DPRINTF("[SD] card selected\n");
if (!_sdmmc_storage_set_blocklen(storage, 512)) if (!_sdmmc_storage_set_blocklen(storage, 512))
return 0; return 0;
DPRINTF("[SD] set blocklen to 512\n"); DPRINTF("[SD] set blocklen to 512\n");
u32 tmp = 0; u32 tmp = 0;
if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_CLR_CARD_DETECT, 0, 0, R1_STATE_TRAN)) if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_CLR_CARD_DETECT, 0, 0, R1_STATE_TRAN))
return 0; return 0;
DPRINTF("[SD] cleared card detect\n"); DPRINTF("[SD] cleared card detect\n");
u8 *buf = (u8 *)malloc(512);
if (!_sd_storage_get_scr(storage, buf)) if (!_sd_storage_get_scr(storage, buf))
{
free(buf);
return 0; return 0;
}
//gfx_hexdump(0, storage->raw_scr, 8);
DPRINTF("[SD] got scr\n"); DPRINTF("[SD] got scr\n");
// Check if card supports a wider bus and if it's not SD Version 1.X // Check if card supports a wider bus and if it's not SD Version 1.X
if (bus_width == SDMMC_BUS_WIDTH_4 && (storage->scr.bus_widths & 4) && (storage->scr.sda_vsn & 0xF)) if (bus_width == SDMMC_BUS_WIDTH_4 && (storage->scr.bus_widths & 4) && (storage->scr.sda_vsn & 0xF))
{ {
if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_BUS_WIDTH, SD_BUS_WIDTH_4, 0, R1_STATE_TRAN)) if (!_sd_storage_execute_app_cmd_type1(storage, &tmp, SD_APP_SET_BUS_WIDTH, SD_BUS_WIDTH_4, 0, R1_STATE_TRAN))
{
free(buf);
return 0; return 0;
}
sdmmc_set_bus_width(storage->sdmmc, SDMMC_BUS_WIDTH_4); sdmmc_set_bus_width(storage->sdmmc, SDMMC_BUS_WIDTH_4);
DPRINTF("[SD] switched to wide bus width\n"); DPRINTF("[SD] switched to wide bus width\n");
} }
else else
{ {
DPRINTF("[SD] SD does not support wide bus width\n"); DPRINTF("[SD] SD does not support wide bus width\n");
} }
if (storage->is_low_voltage) if (storage->is_low_voltage)
{ {
if (!_sd_storage_enable_highspeed_low_volt(storage, type, buf)) if (!_sd_storage_enable_uhs_low_volt(storage, type, buf))
{
free(buf);
return 0; return 0;
} DPRINTF("[SD] enabled UHS\n");
DPRINTF("[SD] enabled highspeed (low voltage)\n");
sdmmc_card_clock_ctrl(sdmmc, SDMMC_AUTO_CAL_ENABLE);
} }
else if (type != 6 && (storage->scr.sda_vsn & 0xF) != 0) else if (type != SDHCI_TIMING_SD_DS12 && (storage->scr.sda_vsn & 0xF) != 0)
{ {
if (!_sd_storage_enable_highspeed_high_volt(storage, buf)) if (!_sd_storage_enable_hs_high_volt(storage, buf))
{
free(buf);
return 0; return 0;
DPRINTF("[SD] enabled HS\n");
switch (bus_width)
{
case SDMMC_BUS_WIDTH_4:
storage->csd.busspeed = 25;
break;
case SDMMC_BUS_WIDTH_1:
storage->csd.busspeed = 6;
break;
} }
DPRINTF("[SD] enabled highspeed (high voltage)\n");
storage->csd.busspeed = 25;
} }
sdmmc_sd_clock_ctrl(sdmmc, 1);
// Parse additional card info from sd status.
if (_sd_storage_get_ssr(storage, buf))
{
DPRINTF("[SD] got sd status\n");
}
free(buf);
return 1; return 1;
} }
@ -1400,17 +1414,17 @@ int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc)
memset(storage, 0, sizeof(sdmmc_storage_t)); memset(storage, 0, sizeof(sdmmc_storage_t));
storage->sdmmc = sdmmc; storage->sdmmc = sdmmc;
if (!sdmmc_init(sdmmc, SDMMC_2, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_8, 14, 0)) if (!sdmmc_init(sdmmc, SDMMC_2, SDMMC_POWER_1_8, SDMMC_BUS_WIDTH_8, SDHCI_TIMING_MMC_DDR52, SDMMC_AUTO_CAL_DISABLE))
return 0; return 0;
DPRINTF("[gc] after init\n"); DPRINTF("[gc] after init\n");
usleep(1000 + (10000 + sdmmc->divisor - 1) / sdmmc->divisor); usleep(1000 + (10000 + sdmmc->divisor - 1) / sdmmc->divisor);
if (!sdmmc_config_tuning(storage->sdmmc, 14, MMC_SEND_TUNING_BLOCK_HS200)) if (!sdmmc_tuning_execute(storage->sdmmc, SDHCI_TIMING_MMC_DDR52, MMC_SEND_TUNING_BLOCK_HS200))
return 0; return 0;
DPRINTF("[gc] after tuning\n"); DPRINTF("[gc] after tuning\n");
sdmmc_sd_clock_ctrl(sdmmc, 1); sdmmc_card_clock_ctrl(sdmmc, SDMMC_AUTO_CAL_ENABLE);
return 1; return 1;
} }

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (c) 2018 naehrwert * Copyright (c) 2018 naehrwert
* Copyright (C) 2018 CTCaer * Copyright (c) 2018 CTCaer
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,
@ -95,12 +95,10 @@ typedef struct _sdmmc_storage_t
u8 raw_cid[0x10]; u8 raw_cid[0x10];
u8 raw_csd[0x10]; u8 raw_csd[0x10];
u8 raw_scr[8]; u8 raw_scr[8];
u8 raw_ssr[0x40];
mmc_cid_t cid; mmc_cid_t cid;
mmc_csd_t csd; mmc_csd_t csd;
mmc_ext_csd_t ext_csd; mmc_ext_csd_t ext_csd;
sd_scr_t scr; sd_scr_t scr;
sd_ssr_t ssr;
} sdmmc_storage_t; } sdmmc_storage_t;
extern sdmmc_accessor_t *_current_accessor; extern sdmmc_accessor_t *_current_accessor;
@ -109,9 +107,9 @@ extern bool sdmmc_memcpy_buf;
int sdmmc_storage_end(sdmmc_storage_t *storage); int sdmmc_storage_end(sdmmc_storage_t *storage);
int sdmmc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf); int sdmmc_storage_read(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf);
int sdmmc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf); int sdmmc_storage_write(sdmmc_storage_t *storage, u32 sector, u32 num_sectors, void *buf);
int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type); int sdmmc_storage_init_mmc(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type);
int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition); int sdmmc_storage_set_mmc_partition(sdmmc_storage_t *storage, u32 partition);
int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 id, u32 bus_width, u32 type); int sdmmc_storage_init_sd(sdmmc_storage_t *storage, sdmmc_t *sdmmc, u32 bus_width, u32 type);
int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc); int sdmmc_storage_init_gc(sdmmc_storage_t *storage, sdmmc_t *sdmmc);
intptr_t sdmmc_calculate_dma_addr(sdmmc_accessor_t *_this, void *buf, unsigned int num_sectors); intptr_t sdmmc_calculate_dma_addr(sdmmc_accessor_t *_this, void *buf, unsigned int num_sectors);
int sdmmc_calculate_dma_index(sdmmc_accessor_t *_this, void *buf, unsigned int num_sectors); int sdmmc_calculate_dma_index(sdmmc_accessor_t *_this, void *buf, unsigned int num_sectors);

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2018 naehrwert * Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2019 CTCaer
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,
@ -49,24 +50,158 @@
#define SDMMC_MASKINT_NOERROR -1 #define SDMMC_MASKINT_NOERROR -1
#define SDMMC_MASKINT_ERROR -2 #define SDMMC_MASKINT_ERROR -2
/*! SDMMC host control 2 */ /*! SDMMC present state. */
#define SDHCI_CTRL_UHS_MASK 0xFFF8 #define SDHCI_CMD_INHIBIT 0x1
#define SDHCI_CTRL_VDD_330 0xFFF7 #define SDHCI_DATA_INHIBIT 0x2
#define SDHCI_CTRL_VDD_180 8 #define SDHCI_DOING_WRITE 0x100
#define SDHCI_CTRL_EXEC_TUNING 0x40 #define SDHCI_DOING_READ 0x200
#define SDHCI_CTRL_TUNED_CLK 0x80 #define SDHCI_SPACE_AVAILABLE 0x400
#define SDHCI_HOST_VERSION_4_EN 0x1000 #define SDHCI_DATA_AVAILABLE 0x800
#define SDHCI_ADDRESSING_64BIT_EN 0x2000 #define SDHCI_CARD_PRESENT 0x10000
#define SDHCI_CTRL_PRESET_VAL_EN 0x8000 #define SDHCI_CD_STABLE 0x20000
#define SDHCI_CD_LVL 0x40000
#define SDHCI_WRITE_PROTECT 0x80000
#define SDHCI_DATA_LVL_MASK 0xF00000
#define SDHCI_DATA_0_LVL_MASK 0x100000
#define SDHCI_CMD_LVL 0x1000000
/*! SDMMC transfer mode. */
#define SDHCI_TRNS_DMA 0x01
#define SDHCI_TRNS_BLK_CNT_EN 0x02
#define SDHCI_TRNS_AUTO_CMD12 0x04
#define SDHCI_TRNS_AUTO_CMD23 0x08
#define SDHCI_TRNS_AUTO_SEL 0x0C
#define SDHCI_TRNS_WRITE 0x00
#define SDHCI_TRNS_READ 0x10
#define SDHCI_TRNS_MULTI 0x20
/*! SDMMC command. */
#define SDHCI_CMD_RESP_MASK 0x3
#define SDHCI_CMD_RESP_NO_RESP 0x0
#define SDHCI_CMD_RESP_LEN136 0x1
#define SDHCI_CMD_RESP_LEN48 0x2
#define SDHCI_CMD_RESP_LEN48_BUSY 0x3
#define SDHCI_CMD_CRC 0x08
#define SDHCI_CMD_INDEX 0x10
#define SDHCI_CMD_DATA 0x20
#define SDHCI_CMD_ABORTCMD 0xC0
/*! SDMMC host control. */
#define SDHCI_CTRL_LED 0x01
#define SDHCI_CTRL_4BITBUS 0x02
#define SDHCI_CTRL_HISPD 0x04
#define SDHCI_CTRL_DMA_MASK 0x18
#define SDHCI_CTRL_SDMA 0x00
#define SDHCI_CTRL_ADMA1 0x08
#define SDHCI_CTRL_ADMA32 0x10
#define SDHCI_CTRL_ADMA64 0x18
#define SDHCI_CTRL_8BITBUS 0x20
#define SDHCI_CTRL_CDTEST_INS 0x40
#define SDHCI_CTRL_CDTEST_EN 0x80
/*! SDMMC host control 2. */
#define SDHCI_CTRL_UHS_MASK 0xFFF8
#define SDHCI_CTRL_VDD_180 8
#define SDHCI_CTRL_DRV_TYPE_B 0x00
#define SDHCI_CTRL_DRV_TYPE_A 0x10
#define SDHCI_CTRL_DRV_TYPE_C 0x20
#define SDHCI_CTRL_DRV_TYPE_D 0x30
#define SDHCI_CTRL_EXEC_TUNING 0x40
#define SDHCI_CTRL_TUNED_CLK 0x80
#define SDHCI_HOST_VERSION_4_EN 0x1000
#define SDHCI_ADDRESSING_64BIT_EN 0x2000
#define SDHCI_CTRL_PRESET_VAL_EN 0x8000
/*! SDMMC power control. */
#define SDHCI_POWER_ON 0x01
#define SDHCI_POWER_180 0x0A
#define SDHCI_POWER_300 0x0C
#define SDHCI_POWER_330 0x0E
#define SDHCI_POWER_MASK 0xF1
// /*! SDMMC max current. */
// #define SDHCI_MAX_CURRENT_330_MASK 0xFF
// #define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
// #define SDHCI_MAX_CURRENT_MULTIPLIER 4
/*! SDMMC clock control. */
#define SDHCI_DIVIDER_SHIFT 8
#define SDHCI_DIVIDER_HI_SHIFT 6
#define SDHCI_DIV_MASK 0xFF00
#define SDHCI_DIV_HI_MASK 0xC0
#define SDHCI_PROG_CLOCK_MODE 0x20
#define SDHCI_CLOCK_CARD_EN 0x4
#define SDHCI_CLOCK_INT_STABLE 0x2
#define SDHCI_CLOCK_INT_EN 0x1
/*! SDMMC software reset. */
#define SDHCI_RESET_ALL 0x01
#define SDHCI_RESET_CMD 0x02
#define SDHCI_RESET_DATA 0x04
/*! SDMMC interrupt status and control. */
#define SDHCI_INT_RESPONSE 0x1
#define SDHCI_INT_DATA_END 0x2
#define SDHCI_INT_BLK_GAP 0x4
#define SDHCI_INT_DMA_END 0x8
#define SDHCI_INT_SPACE_AVAIL 0x10
#define SDHCI_INT_DATA_AVAIL 0x20
#define SDHCI_INT_CARD_INSERT 0x40
#define SDHCI_INT_CARD_REMOVE 0x80
#define SDHCI_INT_CARD_INT 0x100
#define SDHCI_INT_RETUNE 0x1000
#define SDHCI_INT_CQE 0x4000
#define SDHCI_INT_ERROR 0x8000
/*! SDMMC error interrupt status and control. */
#define SDHCI_ERR_INT_TIMEOUT 0x1
#define SDHCI_ERR_INT_CRC 0x2
#define SDHCI_ERR_INT_END_BIT 0x4
#define SDHCI_ERR_INT_INDEX 0x8
#define SDHCI_ERR_INT_DATA_TIMEOUT 0x10
#define SDHCI_ERR_INT_DATA_CRC 0x20
#define SDHCI_ERR_INT_DATA_END_BIT 0x40
#define SDHCI_ERR_INT_BUS_POWER 0x80
#define SDHCI_ERR_INT_AUTO_CMD_ERR 0x100
#define SDHCI_ERR_INT_ADMA_ERROR 0x200
#define SDHCI_ERR_INT_ALL_EXCEPT_ADMA_BUSPWR \
(SDHCI_ERR_INT_AUTO_CMD_ERR | SDHCI_ERR_INT_DATA_END_BIT | \
SDHCI_ERR_INT_DATA_CRC | SDHCI_ERR_INT_DATA_TIMEOUT | \
SDHCI_ERR_INT_INDEX | SDHCI_ERR_INT_END_BIT | \
SDHCI_ERR_INT_CRC | SDHCI_ERR_INT_TIMEOUT)
/*! SD bus speeds. */ /*! SD bus speeds. */
#define UHS_SDR12_BUS_SPEED 0 #define UHS_SDR12_BUS_SPEED 0
#define HIGH_SPEED_BUS_SPEED 1 #define HIGH_SPEED_BUS_SPEED 1
#define UHS_SDR25_BUS_SPEED 1 #define UHS_SDR25_BUS_SPEED 1
#define UHS_SDR50_BUS_SPEED 2 #define UHS_SDR50_BUS_SPEED 2
#define UHS_SDR104_BUS_SPEED 3 #define UHS_SDR104_BUS_SPEED 3
#define UHS_DDR50_BUS_SPEED 4 #define UHS_DDR50_BUS_SPEED 4
#define HS400_BUS_SPEED 5 #define HS400_BUS_SPEED 5
/*! SDMMC timmings. */
#define SDHCI_TIMING_MMC_ID 0
#define SDHCI_TIMING_MMC_LS26 1
#define SDHCI_TIMING_MMC_HS52 2
#define SDHCI_TIMING_MMC_HS200 3
#define SDHCI_TIMING_MMC_HS400 4
#define SDHCI_TIMING_SD_ID 5
#define SDHCI_TIMING_SD_DS12 6
#define SDHCI_TIMING_SD_HS25 7
#define SDHCI_TIMING_UHS_SDR12 8
#define SDHCI_TIMING_UHS_SDR25 9
#define SDHCI_TIMING_UHS_SDR50 10
#define SDHCI_TIMING_UHS_SDR104 11
#define SDHCI_TIMING_UHS_SDR82 12 // SDR104 with a 163.2MHz -> 81.6MHz clock.
#define SDHCI_TIMING_UHS_DDR50 13
#define SDHCI_TIMING_MMC_DDR52 14
#define SDHCI_CAN_64BIT 0x10000000
/*! SDMMC Low power features. */
#define SDMMC_AUTO_CAL_DISABLE 0
#define SDMMC_AUTO_CAL_ENABLE 1
/*! Helper for SWITCH command argument. */ /*! Helper for SWITCH command argument. */
#define SDMMC_SWITCH(mode, index, value) (((mode) << 24) | ((index) << 16) | ((value) << 8)) #define SDMMC_SWITCH(mode, index, value) (((mode) << 24) | ((index) << 16) | ((value) << 8))
@ -78,8 +213,8 @@ typedef struct _sdmmc_t
u32 id; u32 id;
u32 divisor; u32 divisor;
u32 clock_stopped; u32 clock_stopped;
int no_sd; int auto_cal_enabled;
int sd_clock_enabled; int card_clock_enabled;
int venclkctl_set; int venclkctl_set;
u32 venclkctl_tap; u32 venclkctl_tap;
u32 expected_rsp_type; u32 expected_rsp_type;
@ -109,19 +244,21 @@ typedef struct _sdmmc_req_t
int is_auto_cmd12; int is_auto_cmd12;
} sdmmc_req_t; } sdmmc_req_t;
int sdmmc_get_voltage(sdmmc_t *sdmmc); int sdmmc_get_io_power(sdmmc_t *sdmmc);
u32 sdmmc_get_bus_width(sdmmc_t *sdmmc); u32 sdmmc_get_bus_width(sdmmc_t *sdmmc);
void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width); void sdmmc_set_bus_width(sdmmc_t *sdmmc, u32 bus_width);
void sdmmc_get_venclkctl(sdmmc_t *sdmmc); void sdmmc_set_tap_value(sdmmc_t *sdmmc);
int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type); int sdmmc_setup_clock(sdmmc_t *sdmmc, u32 type);
void sdmmc_sd_clock_ctrl(sdmmc_t *sdmmc, int no_sd); void sdmmc_card_clock_ctrl(sdmmc_t *sdmmc, int auto_cal_enable);
int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type); int sdmmc_get_rsp(sdmmc_t *sdmmc, u32 *rsp, u32 size, u32 type);
int sdmmc_config_tuning(sdmmc_t *sdmmc, u32 type, u32 cmd); int sdmmc_tuning_execute(sdmmc_t *sdmmc, u32 type, u32 cmd);
int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp); int sdmmc_stop_transmission(sdmmc_t *sdmmc, u32 *rsp);
int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int no_sd); int sdmmc_get_sd_power_enabled();
bool sdmmc_get_sd_inserted();
int sdmmc_init(sdmmc_t *sdmmc, u32 id, u32 power, u32 bus_width, u32 type, int auto_cal_enable);
void sdmmc_end(sdmmc_t *sdmmc); void sdmmc_end(sdmmc_t *sdmmc);
void sdmmc_init_cmd(sdmmc_cmd_t *cmdbuf, u16 cmd, u32 arg, u32 rsp_type, u32 check_busy); void sdmmc_init_cmd(sdmmc_cmd_t *cmdbuf, u16 cmd, u32 arg, u32 rsp_type, u32 check_busy);
int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out); int sdmmc_execute_cmd(sdmmc_t *sdmmc, sdmmc_cmd_t *cmd, sdmmc_req_t *req, u32 *blkcnt_out);
int sdmmc_enable_low_voltage(sdmmc_t *sdmmc); int sdmmc_enable_low_voltage(sdmmc_t *sdmmc);
#endif #endif

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2018 naehrwert * Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2019 CTCaer
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,
@ -19,49 +20,14 @@
#include "../utils/types.h" #include "../utils/types.h"
#define TEGRA_MMC_PWRCTL_SD_BUS_POWER 0x1 #define TEGRA_MMC_VNDR_TUN_CTRL0_TAP_VAL_UPDATED_BY_HW 0x20000
#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V1_8 0xA #define TEGRA_MMC_DLLCAL_CFG_EN_CALIBRATE 0x80000000
#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_0 0xC #define TEGRA_MMC_DLLCAL_CFG_STATUS_DLL_ACTIVE 0x80000000
#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_V3_3 0xE #define TEGRA_MMC_SDMEMCOMPPADCTRL_PAD_E_INPUT_PWRD 0x80000000
#define TEGRA_MMC_PWRCTL_SD_BUS_VOLTAGE_MASK 0xF1 #define TEGRA_MMC_SDMEMCOMPPADCTRL_COMP_VREF_SEL_MASK 0xFFFFFFF0
#define TEGRA_MMC_AUTOCALCFG_AUTO_CAL_ENABLE 0x20000000
#define TEGRA_MMC_HOSTCTL_1BIT 0x00 #define TEGRA_MMC_AUTOCALCFG_AUTO_CAL_START 0x80000000
#define TEGRA_MMC_HOSTCTL_4BIT 0x02 #define TEGRA_MMC_AUTOCALSTS_AUTO_CAL_ACTIVE 0x80000000
#define TEGRA_MMC_HOSTCTL_8BIT 0x20
#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE 0x1
#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE 0x2
#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE 0x4
#define TEGRA_MMC_CLKCON_CLKGEN_SELECT 0x20
#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL 0x1
#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE 0x2
#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE 0x4
#define TEGRA_MMC_TRNMOD_DMA_ENABLE 0x1
#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE 0x2
#define TEGRA_MMC_TRNMOD_AUTO_CMD12 0x4
#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE 0x0
#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ 0x10
#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT 0x20
#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK 0x8
#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK 0x10
#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER 0x20
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK 0x3
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE 0x0
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 0x1
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 0x2
#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY 0x3
#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE 0x1
#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE 0x2
#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT 0x8
#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT 0x8000
#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT 0x10000
#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY 0x20
typedef struct _t210_sdmmc_t typedef struct _t210_sdmmc_t
{ {
@ -77,56 +43,66 @@ typedef struct _t210_sdmmc_t
vu32 rspreg3; vu32 rspreg3;
vu32 bdata; vu32 bdata;
vu32 prnsts; vu32 prnsts;
vu8 hostctl; vu8 hostctl;
vu8 pwrcon; vu8 pwrcon;
vu8 blkgap; vu8 blkgap;
vu8 wakcon; vu8 wakcon;
vu16 clkcon; vu16 clkcon;
vu8 timeoutcon; vu8 timeoutcon;
vu8 swrst; vu8 swrst;
vu16 norintsts; vu16 norintsts;
vu16 errintsts; vu16 errintsts;
vu16 norintstsen; vu16 norintstsen; // Enable irq status.
vu16 errintstsen; vu16 errintstsen; // Enable irq status.
vu16 norintsigen; vu16 norintsigen; // Enable irq signal to LIC/GIC.
vu16 errintsigen; vu16 errintsigen; // Enable irq signal to LIC/GIC.
vu16 acmd12errsts; vu16 acmd12errsts;
vu16 hostctl2; vu16 hostctl2;
vu32 capareg; vu32 capareg;
vu32 capareg_1; vu32 capareg_1;
vu32 maxcurr; vu32 maxcurr;
vu8 res3[4]; vu8 rsvd0[4]; // 4C-4F reserved for more max current.
vu16 setacmd12err; vu16 setacmd12err;
vu16 setinterr; vu16 setinterr;
vu8 admaerr; vu8 admaerr;
vu8 res4[3]; vu8 rsvd1[3]; // 55-57 reserved.
vu32 admaaddr; vu32 admaaddr;
vu32 admaaddr_hi; vu32 admaaddr_hi;
vu8 res5[156]; vu8 rsvd2[156]; // 60-FB reserved.
vu16 slotintstatus; vu16 slotintsts;
vu16 hcver; vu16 hcver;
vu32 venclkctl; vu32 venclkctl;
vu32 venspictl; vu32 vensysswctl;
vu32 venspiintsts; vu32 venerrintsts;
vu32 venceatactl; vu32 vencapover;
vu32 venbootctl; vu32 venbootctl;
vu32 venbootacktout; vu32 venbootacktout;
vu32 venbootdattout; vu32 venbootdattout;
vu32 vendebouncecnt; vu32 vendebouncecnt;
vu32 venmiscctl; vu32 venmiscctl;
vu32 res6[34]; vu32 maxcurrover;
vu32 maxcurrover_hi;
vu32 unk0[32]; // 0x12C
vu32 veniotrimctl; vu32 veniotrimctl;
vu32 vendllcal; vu32 vendllcalcfg;
vu8 res7[8]; vu32 vendllctl0;
vu32 dllcfgstatus; vu32 vendllctl1;
vu32 vendllcalcfgsts;
vu32 ventunctl0; vu32 ventunctl0;
vu32 field_1C4; vu32 ventunctl1;
vu8 field_1C8[24]; vu32 ventunsts0;
vu32 ventunsts1;
vu32 venclkgatehystcnt;
vu32 venpresetval0;
vu32 venpresetval1;
vu32 venpresetval2;
vu32 sdmemcmppadctl; vu32 sdmemcmppadctl;
vu32 autocalcfg; vu32 autocalcfg;
vu32 autocalintval; vu32 autocalintval;
vu32 autocalsts; vu32 autocalsts;
vu32 iospare; vu32 iospare;
vu32 mcciffifoctl;
vu32 timeoutwcoal;
} t210_sdmmc_t; } t210_sdmmc_t;
#endif #endif

View file

@ -18,14 +18,12 @@
#include <stdlib.h> #include <stdlib.h>
#include "../soc/gpio.h"
#include "../utils/fatal.h"
#include "../libs/fatfs/diskio.h"
#include "emummc.h" #include "emummc.h"
#include "emummc_ctx.h" #include "emummc_ctx.h"
#include "../utils/fatal.h"
#include "../libs/fatfs/diskio.h"
static bool sdmmc_first_init = false; static bool sdmmc_first_init = false;
static bool storageMMCinitialized = false;
static bool storageSDinitialized = false; static bool storageSDinitialized = false;
// hekate sdmmmc vars // hekate sdmmmc vars
@ -35,6 +33,7 @@ sdmmc_t sd_sdmmc;
sdmmc_storage_t sd_storage; sdmmc_storage_t sd_storage;
// init vars // init vars
bool init_done = false;
bool custom_driver = true; bool custom_driver = true;
// FS funcs // FS funcs
@ -51,8 +50,8 @@ volatile int *active_partition;
volatile Handle *sdmmc_das_handle; volatile Handle *sdmmc_das_handle;
// FatFS // FatFS
file_based_ctxt f_emu;
static bool fat_mounted = false; static bool fat_mounted = false;
static file_based_ctxt f_emu;
static void _sdmmc_ensure_device_attached(void) static void _sdmmc_ensure_device_attached(void)
{ {
@ -68,8 +67,6 @@ static void _sdmmc_ensure_device_attached(void)
static void _sdmmc_ensure_initialized(void) static void _sdmmc_ensure_initialized(void)
{ {
static bool init_done = false;
// First Initial init // First Initial init
if (!sdmmc_first_init) if (!sdmmc_first_init)
{ {
@ -78,10 +75,11 @@ static void _sdmmc_ensure_initialized(void)
} }
else else
{ {
// The boot sysmodule will eventually kill power to SD. Detect this, and reinitialize when it happens. // The boot sysmodule will eventually kill power to SD.
// Detect this, and reinitialize when it happens.
if (!init_done) if (!init_done)
{ {
if (gpio_read(GPIO_PORT_E, GPIO_PIN_4) == 0) if (sdmmc_get_sd_power_enabled() == 0)
{ {
sdmmc_finalize(); sdmmc_finalize();
sdmmc_initialize(); sdmmc_initialize();
@ -118,8 +116,6 @@ static void _file_based_emmc_finalize(void)
void sdmmc_finalize(void) void sdmmc_finalize(void)
{ {
_file_based_emmc_finalize();
if (!sdmmc_storage_end(&sd_storage)) if (!sdmmc_storage_end(&sd_storage))
{ {
fatal_abort(Fatal_InitSD); fatal_abort(Fatal_InitSD);
@ -132,7 +128,6 @@ static void _file_based_emmc_initialize(void)
{ {
char path[sizeof(emuMMC_ctx.storagePath) + 0x20]; char path[sizeof(emuMMC_ctx.storagePath) + 0x20];
memset(&path, 0, sizeof(path)); memset(&path, 0, sizeof(path));
memset(&f_emu, 0, sizeof(file_based_ctxt));
memcpy(path, (void *)emuMMC_ctx.storagePath, sizeof(emuMMC_ctx.storagePath)); memcpy(path, (void *)emuMMC_ctx.storagePath, sizeof(emuMMC_ctx.storagePath));
strcat(path, "/eMMC/"); strcat(path, "/eMMC/");
@ -141,18 +136,24 @@ static void _file_based_emmc_initialize(void)
// Open BOOT0 physical partition. // Open BOOT0 physical partition.
memcpy(path + path_len, "BOOT0", 6); memcpy(path + path_len, "BOOT0", 6);
if (f_open(&f_emu.fp_boot0, path, FA_READ | FA_WRITE) != FR_OK) if (f_open(&f_emu.fp_boot0, path, FA_READ | FA_WRITE) != FR_OK)
fatal_abort(Fatal_InitSD); fatal_abort(Fatal_FatfsFileOpen);
if (!f_expand_cltbl(&f_emu.fp_boot0, 0x400, f_emu.clmt_boot0, f_size(&f_emu.fp_boot0)))
fatal_abort(Fatal_FatfsMemExhaustion);
// Open BOOT1 physical partition. // Open BOOT1 physical partition.
memcpy(path + path_len, "BOOT1", 6); memcpy(path + path_len, "BOOT1", 6);
if (f_open(&f_emu.fp_boot1, path, FA_READ | FA_WRITE) != FR_OK) if (f_open(&f_emu.fp_boot1, path, FA_READ | FA_WRITE) != FR_OK)
fatal_abort(Fatal_InitSD); fatal_abort(Fatal_FatfsFileOpen);
if (!f_expand_cltbl(&f_emu.fp_boot1, 0x400, f_emu.clmt_boot1, f_size(&f_emu.fp_boot1)))
fatal_abort(Fatal_FatfsMemExhaustion);
// Open handles for GPP physical partition files. // Open handles for GPP physical partition files.
_file_based_update_filename(path, path_len, 00); _file_based_update_filename(path, path_len, 00);
if (f_open(&f_emu.fp_gpp[0], path, FA_READ | FA_WRITE) != FR_OK) if (f_open(&f_emu.fp_gpp[0], path, FA_READ | FA_WRITE) != FR_OK)
fatal_abort(Fatal_InitSD); fatal_abort(Fatal_FatfsFileOpen);
if (!f_expand_cltbl(&f_emu.fp_gpp[0], 0x400, &f_emu.clmt_gpp[0], f_size(&f_emu.fp_gpp[0])))
fatal_abort(Fatal_FatfsMemExhaustion);
f_emu.part_size = f_size(&f_emu.fp_gpp[0]) >> 9; f_emu.part_size = f_size(&f_emu.fp_gpp[0]) >> 9;
@ -171,38 +172,27 @@ static void _file_based_emmc_initialize(void)
return; return;
} }
if (!f_expand_cltbl(&f_emu.fp_gpp[f_emu.parts], 0x400, &f_emu.clmt_gpp[f_emu.parts * 0x400], f_size(&f_emu.fp_gpp[f_emu.parts])))
fatal_abort(Fatal_FatfsMemExhaustion);
} }
} }
bool sdmmc_initialize(void) bool sdmmc_initialize(void)
{ {
if (!storageMMCinitialized)
{
if (sdmmc_storage_init_mmc(&storage, &sdmmc, SDMMC_4, SDMMC_BUS_WIDTH_8, 4))
{
if (sdmmc_storage_set_mmc_partition(&storage, FS_EMMC_PARTITION_GPP))
storageMMCinitialized = true;
}
else
{
fatal_abort(Fatal_InitMMC);
}
}
if (!storageSDinitialized) if (!storageSDinitialized)
{ {
int retries = 5; int retries = 3;
while (retries) while (retries)
{ {
if (sdmmc_storage_init_sd(&sd_storage, &sd_sdmmc, SDMMC_1, SDMMC_BUS_WIDTH_4, 11)) if (nx_sd_initialize(false))
{ {
storageSDinitialized = true; storageSDinitialized = true;
// File based emummc. // File based emummc.
if ((emuMMC_ctx.EMMC_Type == emuMMC_SD_File) && !fat_mounted) if ((emuMMC_ctx.EMMC_Type == emuMMC_SD_File) && !fat_mounted)
{ {
f_emu.sd_fs = (FATFS *)malloc(sizeof(FATFS)); if (f_mount(&f_emu.sd_fs, "", 1) != FR_OK)
if (f_mount(f_emu.sd_fs, "", 1) != FR_OK)
fatal_abort(Fatal_InitSD); fatal_abort(Fatal_InitSD);
else else
fat_mounted = true; fat_mounted = true;
@ -214,7 +204,6 @@ bool sdmmc_initialize(void)
} }
retries--; retries--;
msleep(100);
} }
if (!storageSDinitialized) if (!storageSDinitialized)
@ -223,7 +212,7 @@ bool sdmmc_initialize(void)
} }
} }
return storageMMCinitialized && storageSDinitialized; return storageSDinitialized;
} }
sdmmc_accessor_t *sdmmc_accessor_get(int mmc_id) sdmmc_accessor_t *sdmmc_accessor_get(int mmc_id)
@ -295,38 +284,36 @@ static uint64_t emummc_read_write_inner(void *buf, unsigned int sector, unsigned
} }
// File based emummc. // File based emummc.
FIL *fp_tmp = NULL; FIL *fp = NULL;
switch (*active_partition) switch (*active_partition)
{ {
case FS_EMMC_PARTITION_GPP: case FS_EMMC_PARTITION_GPP:
if (f_emu.parts) if (f_emu.parts)
{ {
fp_tmp = &f_emu.fp_gpp[sector / f_emu.part_size]; fp = &f_emu.fp_gpp[sector / f_emu.part_size];
sector = sector % f_emu.part_size; sector = sector % f_emu.part_size;
} }
else else
{ {
fp_tmp = &f_emu.fp_gpp[0]; fp = &f_emu.fp_gpp[0];
} }
break; break;
case FS_EMMC_PARTITION_BOOT1: case FS_EMMC_PARTITION_BOOT1:
fp_tmp = &f_emu.fp_boot1; fp = &f_emu.fp_boot1;
break; break;
case FS_EMMC_PARTITION_BOOT0: case FS_EMMC_PARTITION_BOOT0:
fp_tmp = &f_emu.fp_boot0; fp = &f_emu.fp_boot0;
break; break;
} }
if (f_lseek(fp_tmp, sector << 9) != FR_OK) if (f_lseek(fp, sector << 9) != FR_OK)
{ return 0; // Out of bounds.
; //TODO. Out of range. close stuff and fatal?
}
uint64_t res = 0; uint64_t res = 0;
if (!is_write) if (!is_write)
res = !(f_read(fp_tmp, buf, num_sectors << 9, NULL)); res = !f_read_fast(fp, buf, num_sectors << 9);
else else
res = !(f_write(fp_tmp, buf, num_sectors << 9, NULL)); res = !f_write_fast(fp, buf, num_sectors << 9);
return res; return res;
} }
@ -402,12 +389,15 @@ uint64_t sdmmc_wrapper_read(void *buf, uint64_t bufSize, int mmc_id, unsigned in
if (first_sd_read) if (first_sd_read)
{ {
first_sd_read = false; first_sd_read = false;
// Because some SD cards have issues with emuMMC's driver if (emuMMC_ctx.EMMC_Type == emuMMC_SD)
// we currently swap to FS's driver after first SD read {
// TODO: Fix remaining driver issues // Because some SD cards have issues with emuMMC's driver
custom_driver = false; // we currently swap to FS's driver after first SD read
// FS will handle sd mutex w/o custom driver from here on // for raw based emuMMC
unlock_mutex(sd_mutex); custom_driver = false;
// FS will handle sd mutex w/o custom driver from here on
unlock_mutex(sd_mutex);
}
} }
// Call hekates driver. // Call hekates driver.

View file

@ -28,6 +28,7 @@ extern "C" {
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "../emmc/nx_sd.h"
#include "../emmc/sdmmc.h" #include "../emmc/sdmmc.h"
#include "../soc/i2c.h" #include "../soc/i2c.h"
#include "../soc/gpio.h" #include "../soc/gpio.h"
@ -55,15 +56,17 @@ uint64_t sdmmc_wrapper_controller_close(int mmc_id);
uint64_t sdmmc_wrapper_read(void *buf, uint64_t bufSize, int mmc_id, unsigned int sector, unsigned int num_sectors); uint64_t sdmmc_wrapper_read(void *buf, uint64_t bufSize, int mmc_id, unsigned int sector, unsigned int num_sectors);
uint64_t sdmmc_wrapper_write(int mmc_id, unsigned int sector, unsigned int num_sectors, void *buf, uint64_t bufSize); uint64_t sdmmc_wrapper_write(int mmc_id, unsigned int sector, unsigned int num_sectors, void *buf, uint64_t bufSize);
// TODO: check if FatFS internal buffers are good (perf wise) to have a x16 alignment.
typedef struct _file_based_ctxt typedef struct _file_based_ctxt
{ {
FATFS sd_fs;
uint64_t parts; uint64_t parts;
uint64_t part_size; uint64_t part_size;
FATFS *sd_fs;
FIL fp_boot0; FIL fp_boot0;
DWORD clmt_boot0[0x400];
FIL fp_boot1; FIL fp_boot1;
DWORD clmt_boot1[0x400];
FIL fp_gpp[32]; FIL fp_gpp[32];
DWORD clmt_gpp[0x8000];
} file_based_ctxt; } file_based_ctxt;
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -44,7 +44,7 @@ typedef struct _emuMMC_ctx_t
enum FS_VER fs_ver; enum FS_VER fs_ver;
enum emuMMC_Type EMMC_Type; enum emuMMC_Type EMMC_Type;
enum emuMMC_Type SD_Type; enum emuMMC_Type SD_Type;
/* Partition based */ /* Partition based */
u64 EMMC_StoragePartitionOffset; u64 EMMC_StoragePartitionOffset;
u64 SD_StoragePartitionOffset; u64 SD_StoragePartitionOffset;

View file

@ -1,10 +1,25 @@
/*
* Copyright (c) 2018 naehrwert
* Copyright (c) 2018-2019 CTCaer
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
/*----------------------------------------------------------------------------/ /*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem Module R0.13c (p3) / / FatFs - Generic FAT Filesystem Module R0.13c (p4) /
/-----------------------------------------------------------------------------/ /-----------------------------------------------------------------------------/
/ /
/ Copyright (C) 2018, ChaN, all right reserved. / Copyright (C) 2018, ChaN, all right reserved.
/ Copyright (c) 2018 naehrwert
/ Copyright (C) 2018-2019 CTCaer
/ /
/ FatFs module is an open source software. Redistribution and use of FatFs in / FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided / source and binary forms, with or without modification, are permitted provided
@ -515,7 +530,7 @@ static WCHAR LfnBuf[FF_MAX_LFN + 1]; /* LFN working buffer */
#define FREE_NAMBUF() ff_memfree(lfn) #define FREE_NAMBUF() ff_memfree(lfn)
#endif #endif
#define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; } #define LEAVE_MKFS(res) { if (!work) ff_memfree(buf); return res; }
#define MAX_MALLOC 0x8000 /* Must be >=FF_MAX_SS */ #define MAX_MALLOC 0x4000 /* Must be >=FF_MAX_SS */
#else #else
#error Wrong setting of FF_USE_LFN #error Wrong setting of FF_USE_LFN
@ -3879,6 +3894,109 @@ FRESULT f_read (
#ifdef FF_FASTFS
/*-----------------------------------------------------------------------*/
/* Fast Read Aligned Sized File Without a Cache */
/*-----------------------------------------------------------------------*/
#if FF_USE_FASTSEEK
FRESULT f_read_fast (
FIL* fp, /* Pointer to the file object */
const void* buff, /* Pointer to the data to be written */
UINT btr /* Number of bytes to read */
)
{
FRESULT res;
FATFS *fs;
UINT csize_bytes;
DWORD clst;
DWORD wbytes;
UINT count;
FSIZE_t work_sector = 0;
FSIZE_t sector_base = 0;
BYTE *wbuff = (BYTE*)buff;
res = validate(&fp->obj, &fs); /* Check validity of the file object */
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
EFSPRINTF("FOV");
LEAVE_FF(fs, res); /* Check validity */
}
if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
FSIZE_t remain = fp->obj.objsize - fp->fptr;
if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
csize_bytes = fs->csize * SS(fs);
DWORD csect = (UINT)((fp->fptr / SS(fs)) & (fs->csize - 1)); /* Sector offset in the cluster */
/* If inside a cluster, read the sectors and align to cluster. */
if (csect) {
wbytes = MIN(btr, (fs->csize - csect) * SS(fs));
f_read(fp, wbuff, wbytes, (void *)0);
wbuff += wbytes;
btr -= wbytes;
if (!btr)
goto out;
}
if (!fp->fptr) { /* On the top of the file? */
clst = fp->obj.sclust; /* Follow from the origin */
} else {
if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
}
if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
fp->clust = clst; /* Set working cluster */
wbytes = MIN(btr, csize_bytes);
sector_base = clst2sect(fs, fp->clust);
count = wbytes / SS(fs);
fp->fptr += wbytes;
btr -= wbytes;
if (!btr) { /* Final cluster/sectors read. */
if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
goto out;
}
while (btr) {
clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
fp->clust = clst;
work_sector = clst2sect(fs, fp->clust);
wbytes = MIN(btr, csize_bytes);
if ((work_sector - sector_base) == count) count += wbytes / SS(fs);
else {
if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
wbuff += count * SS(fs);
sector_base = work_sector;
count = wbytes / SS(fs);
}
fp->fptr += wbytes;
btr -= wbytes;
if (!btr) { /* Final cluster/sectors read. */
if (disk_read(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
}
}
out:
LEAVE_FF(fs, FR_OK);
}
#endif
#endif
#if !FF_FS_READONLY #if !FF_FS_READONLY
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* Write File */ /* Write File */
@ -4018,6 +4136,117 @@ FRESULT f_write (
#ifdef FF_FASTFS
/*-----------------------------------------------------------------------*/
/* Fast Write Aligned Sized File Without a Cache */
/*-----------------------------------------------------------------------*/
#if FF_USE_FASTSEEK
FRESULT f_write_fast (
FIL* fp, /* Pointer to the file object */
const void* buff, /* Pointer to the data to be written */
UINT btw /* Number of bytes to write */
)
{
FRESULT res;
FATFS *fs;
UINT csize_bytes;
DWORD clst;
DWORD wbytes;
UINT count;
FSIZE_t work_sector = 0;
FSIZE_t sector_base = 0;
BYTE *wbuff = (BYTE*)buff;
res = validate(&fp->obj, &fs); /* Check validity of the file object */
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) {
EFSPRINTF("FOV");
LEAVE_FF(fs, res); /* Check validity */
}
if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
/* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
}
csize_bytes = fs->csize * SS(fs);
DWORD csect = (UINT)((fp->fptr / SS(fs)) & (fs->csize - 1)); /* Sector offset in the cluster */
/* If inside a cluster, write the sectors and align to cluster. */
if (csect) {
wbytes = MIN(btw, (fs->csize - csect) * SS(fs));
f_write(fp, wbuff, wbytes, (void *)0);
/* Ensure flushing of it. FatFS is not notified for next write if raw. */
f_sync(fp);
wbuff += wbytes;
btw -= wbytes;
if (!btw)
goto out;
}
if (!fp->fptr) { /* On the top of the file? */
clst = fp->obj.sclust; /* Follow from the origin */
} else {
if (fp->cltbl) clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
else { EFSPRINTF("CLTBL"); ABORT(fs, FR_CLTBL_NO_INIT); }
}
if (clst < 2) { EFSPRINTF("CCHK"); ABORT(fs, FR_INT_ERR); }
else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
fp->clust = clst; /* Set working cluster */
wbytes = MIN(btw, csize_bytes);
sector_base = clst2sect(fs, fp->clust);
count = wbytes / SS(fs);
fp->fptr += wbytes;
btw -= wbytes;
if (!btw) { /* Final cluster/sectors write. */
if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
fp->flag &= (BYTE)~FA_DIRTY;
goto out;
}
while (btw) {
clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
if (clst < 2) { EFSPRINTF("CCHK2"); ABORT(fs, FR_INT_ERR); }
else if (clst == 0xFFFFFFFF) { EFSPRINTF("DSKC"); ABORT(fs, FR_DISK_ERR); }
fp->clust = clst;
work_sector = clst2sect(fs, fp->clust);
wbytes = MIN(btw, csize_bytes);
if ((work_sector - sector_base) == count) count += wbytes / SS(fs);
else {
if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
wbuff += count * SS(fs);
sector_base = work_sector;
count = wbytes / SS(fs);
}
fp->fptr += wbytes;
btw -= wbytes;
if (!btw) { /* Final cluster/sectors write. */
if (disk_write(fs->pdrv, wbuff, sector_base, count) != RES_OK) ABORT(fs, FR_DISK_ERR);
fp->flag &= (BYTE)~FA_DIRTY;
}
}
out:
fp->flag |= FA_MODIFIED; /* Set file change flag */
LEAVE_FF(fs, FR_OK);
}
#endif
#endif
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* Synchronize the File */ /* Synchronize the File */
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
@ -4231,9 +4460,9 @@ FRESULT f_getcwd (
TCHAR *tp = buff; TCHAR *tp = buff;
#if FF_VOLUMES >= 2 #if FF_VOLUMES >= 2
UINT vl; UINT vl;
#endif
#if FF_STR_VOLUME_ID #if FF_STR_VOLUME_ID
const char *vp; const char *vp;
#endif
#endif #endif
FILINFO fno; FILINFO fno;
DEF_NAMBUF DEF_NAMBUF
@ -4474,6 +4703,37 @@ FRESULT f_lseek (
#ifdef FF_FASTFS
#if FF_USE_FASTSEEK
/*-----------------------------------------------------------------------*/
/* Seek File Read/Write Pointer */
/*-----------------------------------------------------------------------*/
DWORD *f_expand_cltbl (
FIL* fp, /* Pointer to the file object */
UINT tblsz, /* Size of table */
DWORD *tbl, /* Table pointer */
FSIZE_t ofs /* File pointer from top of file */
)
{
if (fp->flag & FA_WRITE) f_lseek(fp, ofs); /* Expand file if write is enabled */
fp->cltbl = (DWORD *)tbl;
fp->cltbl[0] = tblsz;
if (f_lseek(fp, CREATE_LINKMAP)) { /* Create cluster link table */
fp->cltbl = (void *)0;
EFSPRINTF("CLTBLSZ");
return (void *)0;
}
f_lseek(fp, 0);
return fp->cltbl;
}
#endif
#endif
#if FF_FS_MINIMIZE <= 1 #if FF_FS_MINIMIZE <= 1
/*-----------------------------------------------------------------------*/ /*-----------------------------------------------------------------------*/
/* Create a Directory Object */ /* Create a Directory Object */
@ -4714,7 +4974,7 @@ FRESULT f_getfree (
/* Get logical drive */ /* Get logical drive */
res = find_volume(&path, &fs, 0); res = find_volume(&path, &fs, 0);
if (res == FR_OK) { if (res == FR_OK) {
*fatfs = fs; /* Return ptr to the fs object */ if (fatfs) *fatfs = fs; /* Return ptr to the fs object */
/* If free_clst is valid, return it without full FAT scan */ /* If free_clst is valid, return it without full FAT scan */
if (fs->free_clst <= fs->n_fatent - 2) { if (fs->free_clst <= fs->n_fatent - 2) {
*nclst = fs->free_clst; *nclst = fs->free_clst;

View file

@ -246,7 +246,12 @@ typedef enum {
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */ FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
#ifdef FF_FASTFS
FR_INVALID_PARAMETER, /* (19) Given parameter is invalid */
FR_CLTBL_NO_INIT /* (20) The cluster table for fast seek/read/write was not created */
#else
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
#endif
} FRESULT; } FRESULT;
@ -258,6 +263,10 @@ FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a f
FRESULT f_close (FIL* fp); /* Close an open file object */ FRESULT f_close (FIL* fp); /* Close an open file object */
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
#ifdef FF_FASTFS
FRESULT f_read_fast (FIL* fp, const void* buff, UINT btr); /* Fast read data from the file */
FRESULT f_write_fast (FIL* fp, const void* buff, UINT btw); /* Fast write data to the file */
#endif
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
FRESULT f_truncate (FIL* fp); /* Truncate the file */ FRESULT f_truncate (FIL* fp); /* Truncate the file */
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
@ -279,7 +288,10 @@ FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get numbe
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ #ifdef FF_FASTFS
DWORD *f_expand_cltbl (FIL* fp, UINT tblsz, DWORD *tbl, FSIZE_t ofs); /* Expand file and populate cluster table */
#endif
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */

View file

@ -15,7 +15,7 @@
/ and optional writing functions as well. */ / and optional writing functions as well. */
#define FF_FS_MINIMIZE 0 #define FF_FS_MINIMIZE 2
/* This option defines minimization level to remove some basic API functions. /* This option defines minimization level to remove some basic API functions.
/ /
/ 0: Basic functions are fully enabled. / 0: Basic functions are fully enabled.
@ -41,8 +41,13 @@
#define FF_USE_MKFS 0 #define FF_USE_MKFS 0
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ /* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
#define FF_FASTFS 1
#ifdef FF_FASTFS
#define FF_USE_FASTSEEK 1
#else
#define FF_USE_FASTSEEK 0 #define FF_USE_FASTSEEK 0
#endif
/* This option switches fast seek function. (0:Disable or 1:Enable) */ /* This option switches fast seek function. (0:Disable or 1:Enable) */
@ -50,7 +55,7 @@
/* This option switches f_expand function. (0:Disable or 1:Enable) */ /* This option switches f_expand function. (0:Disable or 1:Enable) */
#define FF_USE_CHMOD 1 #define FF_USE_CHMOD 0
/* This option switches attribute manipulation functions, f_chmod() and f_utime(). /* This option switches attribute manipulation functions, f_chmod() and f_utime().
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */ / (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
@ -239,7 +244,7 @@
#define FF_FS_NORTC 1 #define FF_FS_NORTC 1
#define FF_NORTC_MON 1 #define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1 #define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2019 #define FF_NORTC_YEAR 2020
/* The option FF_FS_NORTC switches timestamp function. If the system does not have /* The option FF_FS_NORTC switches timestamp function. If the system does not have
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable / any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp / the timestamp function. Every object modified by FatFs will have a fixed timestamp

View file

@ -34,7 +34,6 @@
#define MERGE2(a, b) a ## b #define MERGE2(a, b) a ## b
#define CVTBL(tbl, cp) MERGE2(tbl, cp) #define CVTBL(tbl, cp) MERGE2(tbl, cp)
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
/* Code Conversion Tables */ /* Code Conversion Tables */
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
@ -623,5 +622,4 @@ DWORD ff_wtoupper ( /* Returns up-converted code point */
return uni; return uni;
} }
#endif /* #if FF_USE_LFN */ #endif /* #if FF_USE_LFN */

View file

@ -1,7 +1,7 @@
/* /*
* Defining registers address and its bit definitions of MAX77620 and MAX20024 * Defining registers address and its bit definitions of MAX77620 and MAX20024
* *
* Copyright (C) 2016 NVIDIA CORPORATION. All rights reserved. * Copyright (c) 2016 NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2019 CTCaer * Copyright (c) 2019 CTCaer
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
@ -19,9 +19,19 @@
#define MAX77620_CNFGGLBL1_LBDAC_EN (1 << 7) #define MAX77620_CNFGGLBL1_LBDAC_EN (1 << 7)
#define MAX77620_CNFGGLBL1_MPPLD (1 << 6) #define MAX77620_CNFGGLBL1_MPPLD (1 << 6)
#define MAX77620_CNFGGLBL1_LBHYST ((1 << 5) | (1 << 4)) #define MAX77620_CNFGGLBL1_LBHYST ((1 << 5) | (1 << 4))
#define MAX77620_CNFGGLBL1_LBHYST_N (1 << 4) #define MAX77620_CNFGGLBL1_LBHYST_100 (0 << 4)
#define MAX77620_CNFGGLBL1_LBDAC 0x0E #define MAX77620_CNFGGLBL1_LBHYST_200 (1 << 4)
#define MAX77620_CNFGGLBL1_LBDAC_N (1 << 1) #define MAX77620_CNFGGLBL1_LBHYST_300 (2 << 4)
#define MAX77620_CNFGGLBL1_LBHYST_400 (3 << 4)
#define MAX77620_CNFGGLBL1_LBDAC_MASK 0x0E
#define MAX77620_CNFGGLBL1_LBDAC_2700 (0 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_2800 (1 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_2900 (2 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3000 (3 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3100 (4 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3200 (5 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3300 (6 << 1)
#define MAX77620_CNFGGLBL1_LBDAC_3400 (7 << 1)
#define MAX77620_CNFGGLBL1_LBRSTEN (1 << 0) #define MAX77620_CNFGGLBL1_LBRSTEN (1 << 0)
#define MAX77620_REG_CNFGGLBL2 0x01 #define MAX77620_REG_CNFGGLBL2 0x01
@ -130,7 +140,7 @@
#define MAX77620_POWER_MODE_DISABLE 0 #define MAX77620_POWER_MODE_DISABLE 0
#define MAX20024_LDO_CFG2_MPOK_MASK (1 << 2) #define MAX20024_LDO_CFG2_MPOK_MASK (1 << 2)
#define MAX77620_LDO_CFG2_ADE_MASK (1 << 1) #define MAX77620_LDO_CFG2_ADE_MASK (1 << 1)
#define MAX77620_LDO_CFG2_ADE_DISABLE 0 #define MAX77620_LDO_CFG2_ADE_DISABLE (0 << 1)
#define MAX77620_LDO_CFG2_ADE_ENABLE (1 << 1) #define MAX77620_LDO_CFG2_ADE_ENABLE (1 << 1)
#define MAX77620_LDO_CFG2_SS_MASK (1 << 0) #define MAX77620_LDO_CFG2_SS_MASK (1 << 0)
#define MAX77620_LDO_CFG2_SS_FAST (1 << 0) #define MAX77620_LDO_CFG2_SS_FAST (1 << 0)
@ -153,6 +163,24 @@
#define MAX77620_REG_PUE_GPIO 0x3E #define MAX77620_REG_PUE_GPIO 0x3E
#define MAX77620_REG_PDE_GPIO 0x3F #define MAX77620_REG_PDE_GPIO 0x3F
#define MAX77620_REG_AME_GPIO 0x40 #define MAX77620_REG_AME_GPIO 0x40
#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0)
#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0)
#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN (0 << 0)
#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1)
#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1)
#define MAX77620_CNFG_GPIO_DIR_OUTPUT (0 << 1)
#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2)
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3)
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3)
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW (0 << 3)
#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4)
#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4)
#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5)
#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6)
#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6)
#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6)
#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6)
#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6)
#define MAX77620_REG_ONOFFCNFG1 0x41 #define MAX77620_REG_ONOFFCNFG1 0x41
#define MAX77620_ONOFFCNFG1_SFT_RST (1 << 7) #define MAX77620_ONOFFCNFG1_SFT_RST (1 << 7)
@ -259,25 +287,6 @@
#define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0 #define MAX77620_SD_CFG1_FSRADE_SD_DISABLE 0
#define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0) #define MAX77620_SD_CFG1_FSRADE_SD_ENABLE (1 << 0)
#define MAX77620_CNFG_GPIO_DRV_MASK (1 << 0)
#define MAX77620_CNFG_GPIO_DRV_PUSHPULL (1 << 0)
#define MAX77620_CNFG_GPIO_DRV_OPENDRAIN 0
#define MAX77620_CNFG_GPIO_DIR_MASK (1 << 1)
#define MAX77620_CNFG_GPIO_DIR_INPUT (1 << 1)
#define MAX77620_CNFG_GPIO_DIR_OUTPUT 0
#define MAX77620_CNFG_GPIO_INPUT_VAL_MASK (1 << 2)
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK (1 << 3)
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH (1 << 3)
#define MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW 0
#define MAX77620_CNFG_GPIO_INT_MASK (0x3 << 4)
#define MAX77620_CNFG_GPIO_INT_FALLING (1 << 4)
#define MAX77620_CNFG_GPIO_INT_RISING (1 << 5)
#define MAX77620_CNFG_GPIO_DBNC_MASK (0x3 << 6)
#define MAX77620_CNFG_GPIO_DBNC_None (0x0 << 6)
#define MAX77620_CNFG_GPIO_DBNC_8ms (0x1 << 6)
#define MAX77620_CNFG_GPIO_DBNC_16ms (0x2 << 6)
#define MAX77620_CNFG_GPIO_DBNC_32ms (0x3 << 6)
#define MAX77620_IRQ_LVL2_GPIO_EDGE0 (1 << 0) #define MAX77620_IRQ_LVL2_GPIO_EDGE0 (1 << 0)
#define MAX77620_IRQ_LVL2_GPIO_EDGE1 (1 << 1) #define MAX77620_IRQ_LVL2_GPIO_EDGE1 (1 << 1)
#define MAX77620_IRQ_LVL2_GPIO_EDGE2 (1 << 2) #define MAX77620_IRQ_LVL2_GPIO_EDGE2 (1 << 2)

View file

@ -157,9 +157,3 @@ void max77620_config_default()
} }
i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_SD_CFG2, 4); i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_SD_CFG2, 4);
} }
void max77620_low_battery_monitor_config()
{
i2c_send_byte(I2C_5, MAX77620_I2C_ADDR, MAX77620_REG_CNFGGLBL1,
MAX77620_CNFGGLBL1_LBDAC_EN | MAX77620_CNFGGLBL1_LBHYST_N | MAX77620_CNFGGLBL1_LBDAC_N);
}

View file

@ -24,16 +24,16 @@
* Switch Power domains (max77620): * Switch Power domains (max77620):
* Name | Usage | uV step | uV min | uV default | uV max | Init * Name | Usage | uV step | uV min | uV default | uV max | Init
*-------+---------------+---------+--------+------------+---------+------------------ *-------+---------------+---------+--------+------------+---------+------------------
* sd0 | core | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1) * sd0 | SoC | 12500 | 600000 | 625000 | 1400000 | 1.125V (pkg1.1)
* sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1) * sd1 | SDRAM | 12500 | 600000 | 1125000 | 1125000 | 1.1V (pkg1.1)
* sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv) * sd2 | ldo{0-1, 7-8} | 12500 | 600000 | 1325000 | 1350000 | 1.325V (pcv)
* sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 | * sd3 | 1.8V general | 12500 | 600000 | 1800000 | 1800000 |
* ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1) * ldo0 | Display Panel | 25000 | 800000 | 1200000 | 1200000 | 1.2V (pkg1.1)
* ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv) * ldo1 | XUSB, PCIE | 25000 | 800000 | 1050000 | 1050000 | 1.05V (pcv)
* ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 | * ldo2 | SDMMC1 | 50000 | 800000 | 1800000 | 3300000 |
* ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv) * ldo3 | GC ASIC | 50000 | 800000 | 3100000 | 3100000 | 3.1V (pcv)
* ldo4 | RTC | 12500 | 800000 | 850000 | 850000 | * ldo4 | RTC | 12500 | 800000 | 850000 | 850000 |
* ldo5 | GC ASIC | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv) * ldo5 | GC ASIC | 50000 | 800000 | 1800000 | 1800000 | 1.8V (pcv)
* ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V * ldo6 | Touch, ALS | 50000 | 800000 | 2900000 | 2900000 | 2.9V
* ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 | * ldo7 | XUSB | 50000 | 800000 | 1050000 | 1050000 |
* ldo8 | XUSB, DC | 50000 | 800000 | 1050000 | 1050000 | * ldo8 | XUSB, DC | 50000 | 800000 | 1050000 | 1050000 |
@ -71,6 +71,8 @@
/* MAX77621_VOUT */ /* MAX77621_VOUT */
#define MAX77621_VOUT_ENABLE (1 << 7) #define MAX77621_VOUT_ENABLE (1 << 7)
#define MAX77621_VOUT_MASK 0x7F #define MAX77621_VOUT_MASK 0x7F
#define MAX77621_VOUT_0_95V 0x37
#define MAX77621_VOUT_1_09V 0x4F
/* MAX77621_VOUT_DVC_DVS */ /* MAX77621_VOUT_DVC_DVS */
#define MAX77621_DVS_VOUT_MASK 0x7F #define MAX77621_DVS_VOUT_MASK 0x7F
@ -111,6 +113,5 @@ int max77620_regulator_set_voltage(u32 id, u32 mv);
int max77620_regulator_enable(u32 id, int enable); int max77620_regulator_enable(u32 id, int enable);
int max77620_regulator_set_volt_and_flags(u32 id, u32 mv, u8 flags); int max77620_regulator_set_volt_and_flags(u32 id, u32 mv, u8 flags);
void max77620_config_default(); void max77620_config_default();
void max77620_low_battery_monitor_config();
#endif #endif

View file

@ -23,6 +23,10 @@ static const sclock_t _clock_i2c5 = {
CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, 0xF, 0, 4 //81.6MHz -> 400KHz CLK_RST_CONTROLLER_RST_DEVICES_H, CLK_RST_CONTROLLER_CLK_OUT_ENB_H, CLK_RST_CONTROLLER_CLK_SOURCE_I2C5, 0xF, 0, 4 //81.6MHz -> 400KHz
}; };
static sclock_t _clock_sdmmc_legacy_tm = {
CLK_RST_CONTROLLER_RST_DEVICES_Y, CLK_RST_CONTROLLER_CLK_OUT_ENB_Y, CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM, 1, 4, 66
};
void clock_enable(const sclock_t *clk) void clock_enable(const sclock_t *clk)
{ {
// Put clock into reset. // Put clock into reset.
@ -34,6 +38,8 @@ void clock_enable(const sclock_t *clk)
CLOCK(clk->source) = clk->clk_div | (clk->clk_src << 29); CLOCK(clk->source) = clk->clk_div | (clk->clk_src << 29);
// Enable. // Enable.
CLOCK(clk->enable) = (CLOCK(clk->enable) & ~(1 << clk->index)) | (1 << clk->index); CLOCK(clk->enable) = (CLOCK(clk->enable) & ~(1 << clk->index)) | (1 << clk->index);
usleep(2);
// Take clock off reset. // Take clock off reset.
CLOCK(clk->reset) &= ~(1 << clk->index); CLOCK(clk->reset) &= ~(1 << clk->index);
} }
@ -56,6 +62,33 @@ void clock_disable_i2c5()
clock_disable(&_clock_i2c5); clock_disable(&_clock_i2c5);
} }
static void _clock_enable_pllc4()
{
if ((CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) & (PLLCX_BASE_ENABLE | PLLCX_BASE_LOCK | 0xFFFFFF))
== (PLLCX_BASE_ENABLE | PLLCX_BASE_LOCK | (104 << 8) | 4))
return;
// Enable Phase and Frequency lock detection.
//CLOCK(CLK_RST_CONTROLLER_PLLC4_MISC) = PLLC4_MISC_EN_LCKDET;
// Disable PLL and IDDQ in case they are on.
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) &= ~PLLCX_BASE_ENABLE;
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) &= ~PLLC4_BASE_IDDQ;
(void)CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE);
usleep(10);
// Set PLLC4 dividers.
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) = (104 << 8) | 4; // DIVM: 4, DIVP: 1.
// Enable PLLC4 and wait for Phase and Frequency lock.
CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) |= PLLCX_BASE_ENABLE;
(void)CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE);
while (!(CLOCK(CLK_RST_CONTROLLER_PLLC4_BASE) & PLLCX_BASE_LOCK))
;
msleep(1); // Wait a bit for PLL to stabilize.
}
#define L_SWR_SDMMC1_RST (1 << 14) #define L_SWR_SDMMC1_RST (1 << 14)
#define L_SWR_SDMMC2_RST (1 << 9) #define L_SWR_SDMMC2_RST (1 << 9)
#define L_SWR_SDMMC4_RST (1 << 15) #define L_SWR_SDMMC4_RST (1 << 15)
@ -194,57 +227,103 @@ static void _clock_sdmmc_clear_enable(u32 id)
} }
} }
static u32 _clock_sdmmc_table[8] = { 0 }; static void _clock_sdmmc_config_legacy_tm()
{
sclock_t *clk = &_clock_sdmmc_legacy_tm;
if (!(CLOCK(clk->enable) & (1 << clk->index)))
clock_enable(clk);
}
#define PLLP_OUT0 0x0 typedef struct _clock_sdmmc_t
{
u32 clock;
u32 real_clock;
} clock_sdmmc_t;
static int _clock_sdmmc_config_clock_source_inner(u32 *pout, u32 id, u32 val) static clock_sdmmc_t _clock_sdmmc_table[4] = { 0 };
#define SDMMC_CLOCK_SRC_PLLP_OUT0 0x0
#define SDMMC_CLOCK_SRC_PLLC4_OUT2 0x3
#define SDMMC4_CLOCK_SRC_PLLC4_OUT2_LJ 0x1
static int _clock_sdmmc_config_clock_host(u32 *pclock, u32 id, u32 val)
{ {
u32 divisor = 0; u32 divisor = 0;
u32 source = PLLP_OUT0; u32 source = SDMMC_CLOCK_SRC_PLLP_OUT0;
if (id > SDMMC_4)
return 0;
// Get IO clock divisor.
switch (val) switch (val)
{ {
case 25000: case 25000:
*pout = 24728; *pclock = 24728;
divisor = 31; divisor = 31; // 16.5 div.
break; break;
case 26000: case 26000:
*pout = 25500; *pclock = 25500;
divisor = 30; divisor = 30; // 16 div.
break; break;
case 40800: case 40800:
*pout = 40800; *pclock = 40800;
divisor = 18; divisor = 18; // 10 div.
break; break;
case 50000: case 50000:
*pout = 48000; *pclock = 48000;
divisor = 15; divisor = 15; // 8.5 div.
break; break;
case 52000: case 52000:
*pout = 51000; *pclock = 51000;
divisor = 14; divisor = 14; // 8 div.
break; break;
case 100000: case 100000:
*pout = 90667; source = SDMMC_CLOCK_SRC_PLLC4_OUT2;
divisor = 7; *pclock = 99840;
divisor = 2; // 2 div.
break;
case 164000:
*pclock = 163200;
divisor = 3; // 2.5 div.
break; break;
case 200000: case 200000:
*pout = 163200; switch (id)
divisor = 3; {
break; case SDMMC_1:
case 208000: source = SDMMC_CLOCK_SRC_PLLC4_OUT2;
*pout = 204000; break;
divisor = 2; case SDMMC_2:
source = SDMMC4_CLOCK_SRC_PLLC4_OUT2_LJ;
break;
case SDMMC_3:
source = SDMMC_CLOCK_SRC_PLLC4_OUT2;
break;
case SDMMC_4:
source = SDMMC4_CLOCK_SRC_PLLC4_OUT2_LJ;
break;
}
*pclock = 199680;
divisor = 0; // 1 div.
break; break;
default: default:
*pout = 24728; *pclock = 24728;
divisor = 31; divisor = 31; // 16.5 div.
} }
_clock_sdmmc_table[2 * id] = val; _clock_sdmmc_table[id].clock = val;
_clock_sdmmc_table[2 * id + 1] = *pout; _clock_sdmmc_table[id].real_clock = *pclock;
// PLLC4 and LEGACY_TM clocks are already initialized,
// because we init at the first eMMC read.
// // Enable PLLC4 if in use by any SDMMC.
// if (source)
// _clock_enable_pllc4();
// // Set SDMMC legacy timeout clock.
// _clock_sdmmc_config_legacy_tm();
// Set SDMMC clock.
switch (id) switch (id)
{ {
case SDMMC_1: case SDMMC_1:
@ -264,69 +343,75 @@ static int _clock_sdmmc_config_clock_source_inner(u32 *pout, u32 id, u32 val)
return 1; return 1;
} }
void clock_sdmmc_config_clock_source(u32 *pout, u32 id, u32 val) void clock_sdmmc_config_clock_source(u32 *pclock, u32 id, u32 val)
{ {
if (_clock_sdmmc_table[2 * id] == val) if (_clock_sdmmc_table[id].clock == val)
{ {
*pout = _clock_sdmmc_table[2 * id + 1]; *pclock = _clock_sdmmc_table[id].real_clock;
} }
else else
{ {
int is_enabled = _clock_sdmmc_is_enabled(id); int is_enabled = _clock_sdmmc_is_enabled(id);
if (is_enabled) if (is_enabled)
_clock_sdmmc_clear_enable(id); _clock_sdmmc_clear_enable(id);
_clock_sdmmc_config_clock_source_inner(pout, id, val); _clock_sdmmc_config_clock_host(pclock, id, val);
if (is_enabled) if (is_enabled)
_clock_sdmmc_set_enable(id); _clock_sdmmc_set_enable(id);
_clock_sdmmc_is_reset(id); _clock_sdmmc_is_reset(id);
} }
} }
void clock_sdmmc_get_params(u32 *pout, u16 *pdivisor, u32 type) void clock_sdmmc_get_card_clock_div(u32 *pclock, u16 *pdivisor, u32 type)
{ {
// Get Card clock divisor.
switch (type) switch (type)
{ {
case 0: case SDHCI_TIMING_MMC_ID: // Actual IO Freq: 380.59 KHz.
*pout = 26000; *pclock = 26000;
*pdivisor = 66; *pdivisor = 66;
break; break;
case 1: case SDHCI_TIMING_MMC_LS26:
*pout = 26000; *pclock = 26000;
*pdivisor = 1; *pdivisor = 1;
break; break;
case 2: case SDHCI_TIMING_MMC_HS52:
*pout = 52000; *pclock = 52000;
*pdivisor = 1; *pdivisor = 1;
break; break;
case 3: case SDHCI_TIMING_MMC_HS200:
case 4: case SDHCI_TIMING_MMC_HS400:
case 11: case SDHCI_TIMING_UHS_SDR104:
*pout = 200000; *pclock = 200000;
*pdivisor = 1; *pdivisor = 1;
break; break;
case 5: case SDHCI_TIMING_SD_ID: // Actual IO Freq: 380.43 KHz.
*pout = 25000; *pclock = 25000;
*pdivisor = 64; *pdivisor = 64;
break; break;
case 6: case SDHCI_TIMING_SD_DS12:
case 8: case SDHCI_TIMING_UHS_SDR12:
*pout = 25000; *pclock = 25000;
*pdivisor = 1; *pdivisor = 1;
break; break;
case 7: case SDHCI_TIMING_SD_HS25:
*pout = 50000; case SDHCI_TIMING_UHS_SDR25:
*pclock = 50000;
*pdivisor = 1; *pdivisor = 1;
break; break;
case 10: case SDHCI_TIMING_UHS_SDR50:
*pout = 100000; *pclock = 100000;
*pdivisor = 1; *pdivisor = 1;
break; break;
case 13: case SDHCI_TIMING_UHS_SDR82:
*pout = 40800; *pclock = 164000;
*pdivisor = 1; *pdivisor = 1;
break; break;
case 14: case SDHCI_TIMING_UHS_DDR50:
*pout = 200000; *pclock = 40800;
*pdivisor = 1;
break;
case SDHCI_TIMING_MMC_DDR52: // Actual IO Freq: 49.92 MHz.
*pclock = 200000;
*pdivisor = 2; *pdivisor = 2;
break; break;
} }
@ -339,15 +424,15 @@ int clock_sdmmc_is_not_reset_and_enabled(u32 id)
void clock_sdmmc_enable(u32 id, u32 val) void clock_sdmmc_enable(u32 id, u32 val)
{ {
u32 div = 0; u32 clock = 0;
if (_clock_sdmmc_is_enabled(id)) if (_clock_sdmmc_is_enabled(id))
_clock_sdmmc_clear_enable(id); _clock_sdmmc_clear_enable(id);
_clock_sdmmc_set_reset(id); _clock_sdmmc_set_reset(id);
_clock_sdmmc_config_clock_source_inner(&div, id, val); _clock_sdmmc_config_clock_host(&clock, id, val);
_clock_sdmmc_set_enable(id); _clock_sdmmc_set_enable(id);
_clock_sdmmc_is_reset(id); _clock_sdmmc_is_reset(id);
usleep((100000 + div - 1) / div); usleep((100000 + clock - 1) / clock);
_clock_sdmmc_clear_reset(id); _clock_sdmmc_clear_reset(id);
_clock_sdmmc_is_reset(id); _clock_sdmmc_is_reset(id);
} }

View file

@ -35,12 +35,16 @@
#define CLK_RST_CONTROLLER_MISC_CLK_ENB 0x48 #define CLK_RST_CONTROLLER_MISC_CLK_ENB 0x48
#define CLK_RST_CONTROLLER_OSC_CTRL 0x50 #define CLK_RST_CONTROLLER_OSC_CTRL 0x50
#define CLK_RST_CONTROLLER_PLLC_BASE 0x80 #define CLK_RST_CONTROLLER_PLLC_BASE 0x80
#define CLK_RST_CONTROLLER_PLLC_OUT 0x84
#define CLK_RST_CONTROLLER_PLLC_MISC 0x88 #define CLK_RST_CONTROLLER_PLLC_MISC 0x88
#define CLK_RST_CONTROLLER_PLLC_MISC_1 0x8C
#define CLK_RST_CONTROLLER_PLLM_BASE 0x90 #define CLK_RST_CONTROLLER_PLLM_BASE 0x90
#define CLK_RST_CONTROLLER_PLLM_MISC1 0x98 #define CLK_RST_CONTROLLER_PLLM_MISC1 0x98
#define CLK_RST_CONTROLLER_PLLM_MISC2 0x9C #define CLK_RST_CONTROLLER_PLLM_MISC2 0x9C
#define CLK_RST_CONTROLLER_PLLP_BASE 0xA0 #define CLK_RST_CONTROLLER_PLLP_BASE 0xA0
#define CLK_RST_CONTROLLER_PLLD_BASE 0xD0 #define CLK_RST_CONTROLLER_PLLD_BASE 0xD0
#define CLK_RST_CONTROLLER_PLLD_MISC1 0xD8
#define CLK_RST_CONTROLLER_PLLD_MISC 0xDC
#define CLK_RST_CONTROLLER_PLLX_BASE 0xE0 #define CLK_RST_CONTROLLER_PLLX_BASE 0xE0
#define CLK_RST_CONTROLLER_PLLX_MISC 0xE4 #define CLK_RST_CONTROLLER_PLLX_MISC 0xE4
#define CLK_RST_CONTROLLER_PLLE_BASE 0xE8 #define CLK_RST_CONTROLLER_PLLE_BASE 0xE8
@ -50,6 +54,7 @@
#define CLK_RST_CONTROLLER_CLK_SOURCE_PWM 0x110 #define CLK_RST_CONTROLLER_CLK_SOURCE_PWM 0x110
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 0x124 #define CLK_RST_CONTROLLER_CLK_SOURCE_I2C1 0x124
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 0x128 #define CLK_RST_CONTROLLER_CLK_SOURCE_I2C5 0x128
#define CLK_RST_CONTROLLER_CLK_SOURCE_DISP1 0x138
#define CLK_RST_CONTROLLER_CLK_SOURCE_VI 0x148 #define CLK_RST_CONTROLLER_CLK_SOURCE_VI 0x148
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 0x150 #define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 0x150
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 0x154 #define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 0x154
@ -57,11 +62,13 @@
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA 0x178 #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTA 0x178
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB 0x17C #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTB 0x17C
#define CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X 0x180 #define CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X 0x180
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C2 0x198
#define CLK_RST_CONTROLLER_CLK_SOURCE_EMC 0x19C
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC 0x1A0 #define CLK_RST_CONTROLLER_CLK_SOURCE_UARTC 0x1A0
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C3 0x1B8 #define CLK_RST_CONTROLLER_CLK_SOURCE_I2C3 0x1B8
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 0x1BC #define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 0x1BC
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTD 0x1C0
#define CLK_RST_CONTROLLER_CLK_SOURCE_CSITE 0x1D4 #define CLK_RST_CONTROLLER_CLK_SOURCE_CSITE 0x1D4
#define CLK_RST_CONTROLLER_CLK_SOURCE_EMC 0x19C
#define CLK_RST_CONTROLLER_CLK_SOURCE_TSEC 0x1F4 #define CLK_RST_CONTROLLER_CLK_SOURCE_TSEC 0x1F4
#define CLK_RST_CONTROLLER_CLK_OUT_ENB_X 0x280 #define CLK_RST_CONTROLLER_CLK_OUT_ENB_X 0x280
#define CLK_RST_CONTROLLER_CLK_ENB_X_SET 0x284 #define CLK_RST_CONTROLLER_CLK_ENB_X_SET 0x284
@ -95,9 +102,13 @@
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC 0x3A0 #define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRC 0x3A0
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD 0x3A4 #define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRD 0x3A4
#define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT 0x3B4 #define CLK_RST_CONTROLLER_CLK_SOURCE_MSELECT 0x3B4
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C4 0x3C4
#define CLK_RST_CONTROLLER_CLK_SOURCE_SYS 0x400
#define CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 0x410 #define CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 0x410
#define CLK_RST_CONTROLLER_CLK_SOURCE_SE 0x42C #define CLK_RST_CONTROLLER_CLK_SOURCE_SE 0x42C
#define CLK_RST_CONTROLLER_RST_DEV_V_CLR 0x434
#define CLK_RST_CONTROLLER_CLK_ENB_V_SET 0x440 #define CLK_RST_CONTROLLER_CLK_ENB_V_SET 0x440
#define CLK_RST_CONTROLLER_CLK_ENB_V_CLR 0x444
#define CLK_RST_CONTROLLER_CLK_ENB_W_SET 0x448 #define CLK_RST_CONTROLLER_CLK_ENB_W_SET 0x448
#define CLK_RST_CONTROLLER_CLK_ENB_W_CLR 0x44C #define CLK_RST_CONTROLLER_CLK_ENB_W_CLR 0x44C
#define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET 0x450 #define CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET 0x450
@ -108,16 +119,32 @@
#define CLK_RST_CONTROLLER_PLLX_MISC_3 0x518 #define CLK_RST_CONTROLLER_PLLX_MISC_3 0x518
#define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE 0x554 #define CLK_RST_CONTROLLER_LVL2_CLK_GATE_OVRE 0x554
#define CLK_RST_CONTROLLER_SPARE_REG0 0x55C #define CLK_RST_CONTROLLER_SPARE_REG0 0x55C
#define CLK_RST_CONTROLLER_PLLC4_BASE 0x5A4
#define CLK_RST_CONTROLLER_PLLC4_MISC 0x5A8
#define CLK_RST_CONTROLLER_PLLC_MISC_2 0x5D0
#define CLK_RST_CONTROLLER_PLLC4_OUT 0x5E4
#define CLK_RST_CONTROLLER_PLLMB_BASE 0x5E8 #define CLK_RST_CONTROLLER_PLLMB_BASE 0x5E8
#define CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP 0x620 #define CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP 0x620
#define CLK_RST_CONTROLLER_CLK_SOURCE_I2C6 0x65C
#define CLK_RST_CONTROLLER_CLK_SOURCE_EMC_DLL 0x664 #define CLK_RST_CONTROLLER_CLK_SOURCE_EMC_DLL 0x664
#define CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIP_CAL 0x66C #define CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL 0x66C
#define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM 0x694 #define CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM 0x694
#define CLK_RST_CONTROLLER_CLK_SOURCE_NVENC 0x6A0 #define CLK_RST_CONTROLLER_CLK_SOURCE_NVENC 0x6A0
#define CLK_RST_CONTROLLER_SE_SUPER_CLK_DIVIDER 0x704 #define CLK_RST_CONTROLLER_SE_SUPER_CLK_DIVIDER 0x704
#define CLK_RST_CONTROLLER_CLK_SOURCE_UARTAPE 0x710
#define CLK_NO_SOURCE 0x0 #define CLK_NO_SOURCE 0x0
/*! PLL control and status bits */
#define PLLCX_BASE_ENABLE (1 << 30)
#define PLLCX_BASE_REF_DIS (1 << 29)
#define PLLCX_BASE_LOCK (1 << 27)
#define PLLC4_MISC_EN_LCKDET (1 << 30)
#define PLLC4_BASE_IDDQ (1 << 18)
#define PLLC4_OUT3_CLKEN (1 << 1)
#define PLLC4_OUT3_RSTN_CLR (1 << 0)
/*! Generic clock descriptor. */ /*! Generic clock descriptor. */
typedef struct _sclock_t typedef struct _sclock_t
{ {
@ -136,8 +163,8 @@ void clock_disable(const sclock_t *clk);
/*! Clock control for specific hardware portions. */ /*! Clock control for specific hardware portions. */
void clock_enable_i2c5(); void clock_enable_i2c5();
void clock_disable_i2c5(); void clock_disable_i2c5();
void clock_sdmmc_config_clock_source(u32 *pout, u32 id, u32 val); void clock_sdmmc_config_clock_source(u32 *pclock, u32 id, u32 val);
void clock_sdmmc_get_params(u32 *pout, u16 *pdivisor, u32 type); void clock_sdmmc_get_card_clock_div(u32 *pclock, u16 *pdivisor, u32 type);
int clock_sdmmc_is_not_reset_and_enabled(u32 id); int clock_sdmmc_is_not_reset_and_enabled(u32 id);
void clock_sdmmc_enable(u32 id, u32 val); void clock_sdmmc_enable(u32 id, u32 val);
void clock_sdmmc_disable(u32 id); void clock_sdmmc_disable(u32 id);

View file

@ -49,12 +49,17 @@ static int _i2c_send_pkt(u32 idx, u32 x, u8 *buf, u32 size)
vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[I2C_5], 0x1000); vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[I2C_5], 0x1000);
base[I2C_CMD_ADDR0] = x << 1; //Set x (send mode). base[I2C_CMD_ADDR0] = x << 1; //Set x (send mode).
base[I2C_CMD_DATA1] = tmp; //Set value. base[I2C_CMD_DATA1] = tmp; //Set value.
base[I2C_CNFG] = (2 * size - 2) | 0x2800; //Set size and send mode. base[I2C_CNFG] = ((size - 1) << 1) | 0x2800; //Set size and send mode.
_i2c_wait(base); //Kick transaction. _i2c_wait(base); //Kick transaction.
base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200; base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200;
u32 timeout = get_tmr_ms() + 1500;
while (base[I2C_STATUS] & 0x100) while (base[I2C_STATUS] & 0x100)
; {
if (get_tmr_ms() > timeout)
return 0;
}
if (base[I2C_STATUS] << 28) if (base[I2C_STATUS] << 28)
return 0; return 0;
@ -68,14 +73,18 @@ static int _i2c_recv_pkt(u32 idx, u8 *buf, u32 size, u32 x)
return 0; return 0;
vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[I2C_5], 0x1000); vu32 *base = (vu32 *)QueryIoMapping(i2c_addrs[I2C_5], 0x1000);
base[I2C_CMD_ADDR0] = (x << 1) | 1; // Set x (recv mode).
base[I2C_CMD_ADDR0] = (x << 1) | 1; // Set x (recv mode). base[I2C_CNFG] = ((size - 1) << 1) | 0x2840; // Set size and recv mode.
base[I2C_CNFG] = (size - 1) << 1 | 0x2840; // Set size and recv mode. _i2c_wait(base); // Kick transaction.
_i2c_wait(base); // Kick transaction.
base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200; base[I2C_CNFG] = (base[I2C_CNFG] & 0xFFFFFDFF) | 0x200;
u32 timeout = get_tmr_ms() + 1500;
while (base[I2C_STATUS] & 0x100) while (base[I2C_STATUS] & 0x100)
; {
if (get_tmr_ms() > timeout)
return 0;
}
if (base[I2C_STATUS] << 28) if (base[I2C_STATUS] << 28)
return 0; return 0;

View file

@ -25,6 +25,7 @@
#define APBDEV_PMC_PWRGATE_TOGGLE 0x30 #define APBDEV_PMC_PWRGATE_TOGGLE 0x30
#define APBDEV_PMC_PWRGATE_STATUS 0x38 #define APBDEV_PMC_PWRGATE_STATUS 0x38
#define APBDEV_PMC_NO_IOPOWER 0x44 #define APBDEV_PMC_NO_IOPOWER 0x44
#define PMC_NO_IOPOWER_SDMMC1_IO_EN (1 << 12)
#define APBDEV_PMC_SCRATCH0 0x50 #define APBDEV_PMC_SCRATCH0 0x50
#define APBDEV_PMC_SCRATCH1 0x54 #define APBDEV_PMC_SCRATCH1 0x54
#define APBDEV_PMC_SCRATCH20 0xA0 #define APBDEV_PMC_SCRATCH20 0xA0
@ -37,6 +38,7 @@
#define APBDEV_PMC_SCRATCH33 0x120 #define APBDEV_PMC_SCRATCH33 0x120
#define APBDEV_PMC_SCRATCH40 0x13C #define APBDEV_PMC_SCRATCH40 0x13C
#define APBDEV_PMC_OSC_EDPD_OVER 0x1A4 #define APBDEV_PMC_OSC_EDPD_OVER 0x1A4
#define PMC_OSC_EDPD_OVER_OSC_CTRL_OVER 0x400000
#define APBDEV_PMC_RST_STATUS 0x1B4 #define APBDEV_PMC_RST_STATUS 0x1B4
#define APBDEV_PMC_IO_DPD_REQ 0x1B8 #define APBDEV_PMC_IO_DPD_REQ 0x1B8
#define APBDEV_PMC_IO_DPD2_REQ 0x1C0 #define APBDEV_PMC_IO_DPD2_REQ 0x1C0
@ -51,9 +53,11 @@
#define APBDEV_PMC_REG_SHORT 0x2CC #define APBDEV_PMC_REG_SHORT 0x2CC
#define APBDEV_PMC_SEC_DISABLE3 0x2D8 #define APBDEV_PMC_SEC_DISABLE3 0x2D8
#define APBDEV_PMC_SECURE_SCRATCH21 0x334 #define APBDEV_PMC_SECURE_SCRATCH21 0x334
#define PMC_FUSE_PRIVATEKEYDISABLE_TZ_STICKY_BIT 0x10
#define APBDEV_PMC_SECURE_SCRATCH32 0x360 #define APBDEV_PMC_SECURE_SCRATCH32 0x360
#define APBDEV_PMC_SECURE_SCRATCH49 0x3A4 #define APBDEV_PMC_SECURE_SCRATCH49 0x3A4
#define APBDEV_PMC_CNTRL2 0x440 #define APBDEV_PMC_CNTRL2 0x440
#define PMC_CNTRL2_HOLD_CKE_LOW_EN 0x1000
#define APBDEV_PMC_IO_DPD3_REQ 0x45C #define APBDEV_PMC_IO_DPD3_REQ 0x45C
#define APBDEV_PMC_IO_DPD4_REQ 0x464 #define APBDEV_PMC_IO_DPD4_REQ 0x464
#define APBDEV_PMC_UTMIP_PAD_CFG1 0x4C4 #define APBDEV_PMC_UTMIP_PAD_CFG1 0x4C4

View file

@ -88,7 +88,9 @@ intptr_t QueryIoMapping(u64 addr, u64 size);
#define APB_MISC_PP_PINMUX_GLOBAL 0x40 #define APB_MISC_PP_PINMUX_GLOBAL 0x40
#define APB_MISC_GP_LCD_BL_PWM_CFGPADCTRL 0xA34 #define APB_MISC_GP_LCD_BL_PWM_CFGPADCTRL 0xA34
#define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL 0xA98 #define APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL 0xA98
#define APB_MISC_GP_EMMC2_PAD_CFGPADCTRL 0xA9C
#define APB_MISC_GP_EMMC4_PAD_CFGPADCTRL 0xAB4 #define APB_MISC_GP_EMMC4_PAD_CFGPADCTRL 0xAB4
#define APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL 0xABC
#define APB_MISC_GP_WIFI_EN_CFGPADCTRL 0xB64 #define APB_MISC_GP_WIFI_EN_CFGPADCTRL 0xB64
#define APB_MISC_GP_WIFI_RST_CFGPADCTRL 0xB68 #define APB_MISC_GP_WIFI_RST_CFGPADCTRL 0xB68

View file

@ -29,8 +29,12 @@ enum FatalReason
Fatal_UnknownVersion, Fatal_UnknownVersion,
Fatal_BadResult, Fatal_BadResult,
Fatal_GetConfig, Fatal_GetConfig,
Fatal_OpenAccessor,
Fatal_CloseAccessor, Fatal_CloseAccessor,
Fatal_IoMapping, Fatal_IoMapping,
Fatal_FatfsMount,
Fatal_FatfsFileOpen,
Fatal_FatfsMemExhaustion,
Fatal_Max Fatal_Max
}; };

View file

@ -96,7 +96,7 @@ u64 get_tmr_us()
void msleep(u64 milliseconds) void msleep(u64 milliseconds)
{ {
u64 now = get_tmr_ms(); u64 now = get_tmr_ms();
while (get_tmr_ms() - now < milliseconds) while (((u64)get_tmr_ms() - now) < milliseconds)
; ;
//svcSleepThread(1000000 * milliseconds); //svcSleepThread(1000000 * milliseconds);
} }
@ -105,7 +105,7 @@ void msleep(u64 milliseconds)
void usleep(u64 microseconds) void usleep(u64 microseconds)
{ {
u64 now = get_tmr_us(); u64 now = get_tmr_us();
while (get_tmr_us() - now < microseconds) while (((u64)get_tmr_us() - now) < microseconds)
; ;
//svcSleepThread(1000 * microseconds); //svcSleepThread(1000 * microseconds);
} }

View file

@ -22,8 +22,8 @@
#include "../emuMMC/emummc_ctx.h" #include "../emuMMC/emummc_ctx.h"
intptr_t QueryIoMapping(u64 addr, u64 size); intptr_t QueryIoMapping(u64 addr, u64 size);
#define byte_swap_32(num) ((num >> 24) & 0xff) | ((num << 8) & 0xff0000) | \ #define byte_swap_32(num) (((num >> 24) & 0xff) | ((num << 8) & 0xff0000) | \
((num >> 8 )& 0xff00) | ((num << 24) & 0xff000000) ((num >> 8 )& 0xff00) | ((num << 24) & 0xff000000))
typedef struct _cfg_op_t typedef struct _cfg_op_t
{ {