pi1541/src/emmc.cpp

1999 lines
44 KiB
C++
Raw Normal View History

2018-05-20 04:53:34 +00:00
//
// emmc.cpp
//
// Provides an interface to the EMMC controller and commands for interacting
// with an sd card
//
// Copyright(C) 2013 by John Cronin <jncronin@tysos.org>
//
// Modified for Circle by R. Stange
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// References:
//
// PLSS - SD Group Physical Layer Simplified Specification ver 3.00
// HCSS - SD Group Host Controller Simplified Specification ver 3.00
//
// Broadcom BCM2835 ARM Peripherals Guide
//
#include "emmc.h"
//#include <circle/bcm2835.h>
//#include <circle/bcmpropertytags.h>
//#include <circle/devicenameservice.h>
//#include <circle/synchronize.h>
//#include <circle/memio.h>
//#include <circle/util.h>
//#include <circle/stdarg.h>
#include <assert.h>
extern "C"
{
#include "rpiHardware.h"
}
//
// Configuration options
//
//#define EMMC_DEBUG
//#define EMMC_DEBUG2
//
// According to the BCM2835 ARM Peripherals Guide the EMMC STATUS register
// should not be used for polling. The original driver does not meet this
// specification in this point but the modified driver should do so.
// Define EMMC_POLL_STATUS_REG if you want the original function!
//
//#define EMMC_POLL_STATUS_REG
// Enable 1.8V support
//#define SD_1_8V_SUPPORT
// Enable 4-bit support
#define SD_4BIT_DATA
// SD Clock Frequencies(in Hz)
#define SD_CLOCK_ID 400000
#define SD_CLOCK_NORMAL 25000000
#define SD_CLOCK_HIGH 50000000
#define SD_CLOCK_100 100000000
#define SD_CLOCK_208 208000000
// Enable SDXC maximum performance mode
// Requires 150 mA power so disabled on the RPi for now
#define SDXC_MAXIMUM_PERFORMANCE
// Enable card interrupts
//#define SD_CARD_INTERRUPTS
#define EMMC_ARG2 (ARM_EMMC_BASE + 0x00)
#define EMMC_BLKSIZECNT (ARM_EMMC_BASE + 0x04)
#define EMMC_ARG1 (ARM_EMMC_BASE + 0x08)
#define EMMC_CMDTM (ARM_EMMC_BASE + 0x0C)
#define EMMC_RESP0 (ARM_EMMC_BASE + 0x10)
#define EMMC_RESP1 (ARM_EMMC_BASE + 0x14)
#define EMMC_RESP2 (ARM_EMMC_BASE + 0x18)
#define EMMC_RESP3 (ARM_EMMC_BASE + 0x1C)
#define EMMC_DATA (ARM_EMMC_BASE + 0x20)
#define EMMC_STATUS (ARM_EMMC_BASE + 0x24)
#define EMMC_CONTROL0 (ARM_EMMC_BASE + 0x28)
#define EMMC_CONTROL1 (ARM_EMMC_BASE + 0x2C)
#define EMMC_INTERRUPT (ARM_EMMC_BASE + 0x30)
#define EMMC_IRPT_MASK (ARM_EMMC_BASE + 0x34)
#define EMMC_IRPT_EN (ARM_EMMC_BASE + 0x38)
#define EMMC_CONTROL2 (ARM_EMMC_BASE + 0x3C)
#define EMMC_CAPABILITIES_0 (ARM_EMMC_BASE + 0x40)
#define EMMC_CAPABILITIES_1 (ARM_EMMC_BASE + 0x44)
#define EMMC_FORCE_IRPT (ARM_EMMC_BASE + 0x50)
#define EMMC_BOOT_TIMEOUT (ARM_EMMC_BASE + 0x70)
#define EMMC_DBG_SEL (ARM_EMMC_BASE + 0x74)
#define EMMC_EXRDFIFO_CFG (ARM_EMMC_BASE + 0x80)
#define EMMC_EXRDFIFO_EN (ARM_EMMC_BASE + 0x84)
#define EMMC_TUNE_STEP (ARM_EMMC_BASE + 0x88)
#define EMMC_TUNE_STEPS_STD (ARM_EMMC_BASE + 0x8C)
#define EMMC_TUNE_STEPS_DDR (ARM_EMMC_BASE + 0x90)
#define EMMC_SPI_INT_SPT (ARM_EMMC_BASE + 0xF0)
#define EMMC_SLOTISR_VER (ARM_EMMC_BASE + 0xFC)
#define SD_CMD_INDEX(a) ((a) << 24)
#define SD_CMD_TYPE_NORMAL 0
#define SD_CMD_TYPE_SUSPEND (1 << 22)
#define SD_CMD_TYPE_RESUME (2 << 22)
#define SD_CMD_TYPE_ABORT (3 << 22)
#define SD_CMD_TYPE_MASK (3 << 22)
#define SD_CMD_ISDATA (1 << 21)
#define SD_CMD_IXCHK_EN (1 << 20)
#define SD_CMD_CRCCHK_EN (1 << 19)
#define SD_CMD_RSPNS_TYPE_NONE 0 // For no response
#define SD_CMD_RSPNS_TYPE_136 (1 << 16) // For response R2(with CRC), R3,4(no CRC)
#define SD_CMD_RSPNS_TYPE_48 (2 << 16) // For responses R1, R5, R6, R7(with CRC)
#define SD_CMD_RSPNS_TYPE_48B (3 << 16) // For responses R1b, R5b(with CRC)
#define SD_CMD_RSPNS_TYPE_MASK (3 << 16)
#define SD_CMD_MULTI_BLOCK (1 << 5)
#define SD_CMD_DAT_DIR_HC 0
#define SD_CMD_DAT_DIR_CH (1 << 4)
#define SD_CMD_AUTO_CMD_EN_NONE 0
#define SD_CMD_AUTO_CMD_EN_CMD12 (1 << 2)
#define SD_CMD_AUTO_CMD_EN_CMD23 (2 << 2)
#define SD_CMD_BLKCNT_EN (1 << 1)
#define SD_CMD_DMA 1
#define SD_ERR_CMD_TIMEOUT 0
#define SD_ERR_CMD_CRC 1
#define SD_ERR_CMD_END_BIT 2
#define SD_ERR_CMD_INDEX 3
#define SD_ERR_DATA_TIMEOUT 4
#define SD_ERR_DATA_CRC 5
#define SD_ERR_DATA_END_BIT 6
#define SD_ERR_CURRENT_LIMIT 7
#define SD_ERR_AUTO_CMD12 8
#define SD_ERR_ADMA 9
#define SD_ERR_TUNING 10
#define SD_ERR_RSVD 11
#define SD_ERR_MASK_CMD_TIMEOUT (1 <<(16 + SD_ERR_CMD_TIMEOUT))
#define SD_ERR_MASK_CMD_CRC (1 <<(16 + SD_ERR_CMD_CRC))
#define SD_ERR_MASK_CMD_END_BIT (1 <<(16 + SD_ERR_CMD_END_BIT))
#define SD_ERR_MASK_CMD_INDEX (1 <<(16 + SD_ERR_CMD_INDEX))
#define SD_ERR_MASK_DATA_TIMEOUT (1 <<(16 + SD_ERR_CMD_TIMEOUT))
#define SD_ERR_MASK_DATA_CRC (1 <<(16 + SD_ERR_CMD_CRC))
#define SD_ERR_MASK_DATA_END_BIT (1 <<(16 + SD_ERR_CMD_END_BIT))
#define SD_ERR_MASK_CURRENT_LIMIT (1 <<(16 + SD_ERR_CMD_CURRENT_LIMIT))
#define SD_ERR_MASK_AUTO_CMD12 (1 <<(16 + SD_ERR_CMD_AUTO_CMD12))
#define SD_ERR_MASK_ADMA (1 <<(16 + SD_ERR_CMD_ADMA))
#define SD_ERR_MASK_TUNING (1 <<(16 + SD_ERR_CMD_TUNING))
#define SD_COMMAND_COMPLETE 1
#define SD_TRANSFER_COMPLETE (1 << 1)
#define SD_BLOCK_GAP_EVENT (1 << 2)
#define SD_DMA_INTERRUPT (1 << 3)
#define SD_BUFFER_WRITE_READY (1 << 4)
#define SD_BUFFER_READ_READY (1 << 5)
#define SD_CARD_INSERTION (1 << 6)
#define SD_CARD_REMOVAL (1 << 7)
#define SD_CARD_INTERRUPT (1 << 8)
#define SD_RESP_NONE SD_CMD_RSPNS_TYPE_NONE
#define SD_RESP_R1 (SD_CMD_RSPNS_TYPE_48 | SD_CMD_CRCCHK_EN)
#define SD_RESP_R1b (SD_CMD_RSPNS_TYPE_48B | SD_CMD_CRCCHK_EN)
#define SD_RESP_R2 (SD_CMD_RSPNS_TYPE_136 | SD_CMD_CRCCHK_EN)
#define SD_RESP_R3 SD_CMD_RSPNS_TYPE_48
#define SD_RESP_R4 SD_CMD_RSPNS_TYPE_136
#define SD_RESP_R5 (SD_CMD_RSPNS_TYPE_48 | SD_CMD_CRCCHK_EN)
#define SD_RESP_R5b (SD_CMD_RSPNS_TYPE_48B | SD_CMD_CRCCHK_EN)
#define SD_RESP_R6 (SD_CMD_RSPNS_TYPE_48 | SD_CMD_CRCCHK_EN)
#define SD_RESP_R7 (SD_CMD_RSPNS_TYPE_48 | SD_CMD_CRCCHK_EN)
#define SD_DATA_READ (SD_CMD_ISDATA | SD_CMD_DAT_DIR_CH)
#define SD_DATA_WRITE (SD_CMD_ISDATA | SD_CMD_DAT_DIR_HC)
#define SD_CMD_RESERVED(a) 0xffffffff
#define SUCCESS (m_last_cmd_success)
#define FAIL (m_last_cmd_success == 0)
#define TIMEOUT (FAIL &&(m_last_error == 0))
#define CMD_TIMEOUT (FAIL &&(m_last_error &(1 << 16)))
#define CMD_CRC (FAIL &&(m_last_error &(1 << 17)))
#define CMD_END_BIT (FAIL &&(m_last_error &(1 << 18)))
#define CMD_INDEX (FAIL &&(m_last_error &(1 << 19)))
#define DATA_TIMEOUT (FAIL &&(m_last_error &(1 << 20)))
#define DATA_CRC (FAIL &&(m_last_error &(1 << 21)))
#define DATA_END_BIT (FAIL &&(m_last_error &(1 << 22)))
#define CURRENT_LIMIT (FAIL &&(m_last_error &(1 << 23)))
#define ACMD12_ERROR (FAIL &&(m_last_error &(1 << 24)))
#define ADMA_ERROR (FAIL &&(m_last_error &(1 << 25)))
#define TUNING_ERROR (FAIL &&(m_last_error &(1 << 26)))
#define SD_VER_UNKNOWN 0
#define SD_VER_1 1
#define SD_VER_1_1 2
#define SD_VER_2 3
#define SD_VER_3 4
#define SD_VER_4 5
const char *CEMMCDevice::sd_versions[] =
{
"unknown",
"1.0 or 1.01",
"1.10",
"2.00",
"3.0x",
"4.xx"
};
#ifdef EMMC_DEBUG2
const char *CEMMCDevice::err_irpts[] =
{
"CMD_TIMEOUT",
"CMD_CRC",
"CMD_END_BIT",
"CMD_INDEX",
"DATA_TIMEOUT",
"DATA_CRC",
"DATA_END_BIT",
"CURRENT_LIMIT",
"AUTO_CMD12",
"ADMA",
"TUNING",
"RSVD"
};
#endif
const u32 CEMMCDevice::sd_commands[] =
{
SD_CMD_INDEX(0),
SD_CMD_RESERVED(1),
SD_CMD_INDEX(2) | SD_RESP_R2,
SD_CMD_INDEX(3) | SD_RESP_R6,
SD_CMD_INDEX(4),
SD_CMD_INDEX(5) | SD_RESP_R4,
SD_CMD_INDEX(6) | SD_RESP_R1,
SD_CMD_INDEX(7) | SD_RESP_R1b,
SD_CMD_INDEX(8) | SD_RESP_R7,
SD_CMD_INDEX(9) | SD_RESP_R2,
SD_CMD_INDEX(10) | SD_RESP_R2,
SD_CMD_INDEX(11) | SD_RESP_R1,
SD_CMD_INDEX(12) | SD_RESP_R1b | SD_CMD_TYPE_ABORT,
SD_CMD_INDEX(13) | SD_RESP_R1,
SD_CMD_RESERVED(14),
SD_CMD_INDEX(15),
SD_CMD_INDEX(16) | SD_RESP_R1,
SD_CMD_INDEX(17) | SD_RESP_R1 | SD_DATA_READ,
SD_CMD_INDEX(18) | SD_RESP_R1 | SD_DATA_READ | SD_CMD_MULTI_BLOCK | SD_CMD_BLKCNT_EN | SD_CMD_AUTO_CMD_EN_CMD12, // SD_CMD_AUTO_CMD_EN_CMD12 not in original driver
SD_CMD_INDEX(19) | SD_RESP_R1 | SD_DATA_READ,
SD_CMD_INDEX(20) | SD_RESP_R1b,
SD_CMD_RESERVED(21),
SD_CMD_RESERVED(22),
SD_CMD_INDEX(23) | SD_RESP_R1,
SD_CMD_INDEX(24) | SD_RESP_R1 | SD_DATA_WRITE,
SD_CMD_INDEX(25) | SD_RESP_R1 | SD_DATA_WRITE | SD_CMD_MULTI_BLOCK | SD_CMD_BLKCNT_EN | SD_CMD_AUTO_CMD_EN_CMD12, // SD_CMD_AUTO_CMD_EN_CMD12 not in original driver
SD_CMD_RESERVED(26),
SD_CMD_INDEX(27) | SD_RESP_R1 | SD_DATA_WRITE,
SD_CMD_INDEX(28) | SD_RESP_R1b,
SD_CMD_INDEX(29) | SD_RESP_R1b,
SD_CMD_INDEX(30) | SD_RESP_R1 | SD_DATA_READ,
SD_CMD_RESERVED(31),
SD_CMD_INDEX(32) | SD_RESP_R1,
SD_CMD_INDEX(33) | SD_RESP_R1,
SD_CMD_RESERVED(34),
SD_CMD_RESERVED(35),
SD_CMD_RESERVED(36),
SD_CMD_RESERVED(37),
SD_CMD_INDEX(38) | SD_RESP_R1b,
SD_CMD_RESERVED(39),
SD_CMD_RESERVED(40),
SD_CMD_RESERVED(41),
SD_CMD_RESERVED(42) | SD_RESP_R1,
SD_CMD_RESERVED(43),
SD_CMD_RESERVED(44),
SD_CMD_RESERVED(45),
SD_CMD_RESERVED(46),
SD_CMD_RESERVED(47),
SD_CMD_RESERVED(48),
SD_CMD_RESERVED(49),
SD_CMD_RESERVED(50),
SD_CMD_RESERVED(51),
SD_CMD_RESERVED(52),
SD_CMD_RESERVED(53),
SD_CMD_RESERVED(54),
SD_CMD_INDEX(55) | SD_RESP_R1,
SD_CMD_INDEX(56) | SD_RESP_R1 | SD_CMD_ISDATA,
SD_CMD_RESERVED(57),
SD_CMD_RESERVED(58),
SD_CMD_RESERVED(59),
SD_CMD_RESERVED(60),
SD_CMD_RESERVED(61),
SD_CMD_RESERVED(62),
SD_CMD_RESERVED(63)
};
const u32 CEMMCDevice::sd_acommands[] =
{
SD_CMD_RESERVED(0),
SD_CMD_RESERVED(1),
SD_CMD_RESERVED(2),
SD_CMD_RESERVED(3),
SD_CMD_RESERVED(4),
SD_CMD_RESERVED(5),
SD_CMD_INDEX(6) | SD_RESP_R1,
SD_CMD_RESERVED(7),
SD_CMD_RESERVED(8),
SD_CMD_RESERVED(9),
SD_CMD_RESERVED(10),
SD_CMD_RESERVED(11),
SD_CMD_RESERVED(12),
SD_CMD_INDEX(13) | SD_RESP_R1,
SD_CMD_RESERVED(14),
SD_CMD_RESERVED(15),
SD_CMD_RESERVED(16),
SD_CMD_RESERVED(17),
SD_CMD_RESERVED(18),
SD_CMD_RESERVED(19),
SD_CMD_RESERVED(20),
SD_CMD_RESERVED(21),
SD_CMD_INDEX(22) | SD_RESP_R1 | SD_DATA_READ,
SD_CMD_INDEX(23) | SD_RESP_R1,
SD_CMD_RESERVED(24),
SD_CMD_RESERVED(25),
SD_CMD_RESERVED(26),
SD_CMD_RESERVED(27),
SD_CMD_RESERVED(28),
SD_CMD_RESERVED(29),
SD_CMD_RESERVED(30),
SD_CMD_RESERVED(31),
SD_CMD_RESERVED(32),
SD_CMD_RESERVED(33),
SD_CMD_RESERVED(34),
SD_CMD_RESERVED(35),
SD_CMD_RESERVED(36),
SD_CMD_RESERVED(37),
SD_CMD_RESERVED(38),
SD_CMD_RESERVED(39),
SD_CMD_RESERVED(40),
SD_CMD_INDEX(41) | SD_RESP_R3,
SD_CMD_INDEX(42) | SD_RESP_R1,
SD_CMD_RESERVED(43),
SD_CMD_RESERVED(44),
SD_CMD_RESERVED(45),
SD_CMD_RESERVED(46),
SD_CMD_RESERVED(47),
SD_CMD_RESERVED(48),
SD_CMD_RESERVED(49),
SD_CMD_RESERVED(50),
SD_CMD_INDEX(51) | SD_RESP_R1 | SD_DATA_READ,
SD_CMD_RESERVED(52),
SD_CMD_RESERVED(53),
SD_CMD_RESERVED(54),
SD_CMD_RESERVED(55),
SD_CMD_RESERVED(56),
SD_CMD_RESERVED(57),
SD_CMD_RESERVED(58),
SD_CMD_RESERVED(59),
SD_CMD_RESERVED(60),
SD_CMD_RESERVED(61),
SD_CMD_RESERVED(62),
SD_CMD_RESERVED(63)
};
// The actual command indices
#define GO_IDLE_STATE 0
#define ALL_SEND_CID 2
#define SEND_RELATIVE_ADDR 3
#define SET_DSR 4
#define IO_SET_OP_COND 5
#define SWITCH_FUNC 6
#define SELECT_CARD 7
#define DESELECT_CARD 7
#define SELECT_DESELECT_CARD 7
#define SEND_IF_COND 8
#define SEND_CSD 9
#define SEND_CID 10
#define VOLTAGE_SWITCH 11
#define STOP_TRANSMISSION 12
#define SEND_STATUS 13
#define GO_INACTIVE_STATE 15
#define SET_BLOCKLEN 16
#define READ_SINGLE_BLOCK 17
#define READ_MULTIPLE_BLOCK 18
#define SEND_TUNING_BLOCK 19
#define SPEED_CLASS_CONTROL 20
#define SET_BLOCK_COUNT 23
#define WRITE_BLOCK 24
#define WRITE_MULTIPLE_BLOCK 25
#define PROGRAM_CSD 27
#define SET_WRITE_PROT 28
#define CLR_WRITE_PROT 29
#define SEND_WRITE_PROT 30
#define ERASE_WR_BLK_START 32
#define ERASE_WR_BLK_END 33
#define ERASE 38
#define LOCK_UNLOCK 42
#define APP_CMD 55
#define GEN_CMD 56
#define IS_APP_CMD 0x80000000
#define ACMD(a) (a | IS_APP_CMD)
#define SET_BUS_WIDTH (6 | IS_APP_CMD)
#define SD_STATUS (13 | IS_APP_CMD)
#define SEND_NUM_WR_BLOCKS (22 | IS_APP_CMD)
#define SET_WR_BLK_ERASE_COUNT (23 | IS_APP_CMD)
#define SD_SEND_OP_COND (41 | IS_APP_CMD)
#define SET_CLR_CARD_DETECT (42 | IS_APP_CMD)
#define SEND_SCR (51 | IS_APP_CMD)
#define SD_RESET_CMD (1 << 25)
#define SD_RESET_DAT (1 << 26)
#define SD_RESET_ALL (1 << 24)
#define SD_GET_CLOCK_DIVIDER_FAIL 0xffffffff
#define SD_BLOCK_SIZE 512
CEMMCDevice::CEMMCDevice()
: m_ullOffset(0),
m_hci_ver(0)
{
}
CEMMCDevice::~CEMMCDevice(void)
{
}
bool CEMMCDevice::Initialize(void)
{
//DEBUG_LOG("CEMMCDevice::Initialize\r\n");
PowerOn();
//if (PowerOn() == false)
//{
// DEBUG_LOG("BCM2708 controller did not power on successfully");
// return false;
//}
// Check the sanity of the sd_commands and sd_acommands structures
assert(sizeof(sd_commands) == (64 * sizeof(u32)));
assert(sizeof(sd_acommands) == (64 * sizeof(u32)));
// Read the controller version
u32 ver = read32(EMMC_SLOTISR_VER);
u32 sdversion = (ver >> 16) & 0xff;
#ifdef EMMC_DEBUG2
u32 vendor = ver >> 24;
u32 slot_status = ver & 0xff;
DEBUG_LOG("Vendor %x, SD version %x, slot status %x", vendor, sdversion, slot_status);
#endif
m_hci_ver = sdversion;
if (m_hci_ver < 2)
{
DEBUG_LOG("Only SDHCI versions >= 3.0 are supported");
return false;
}
if (CardReset() != 0)
return false;
return true;
}
int CEMMCDevice::Read(void *pBuffer, unsigned nCount)
{
if (m_ullOffset % SD_BLOCK_SIZE != 0)
{
return -1;
}
unsigned nBlock = m_ullOffset / SD_BLOCK_SIZE;
//if (m_pActLED != 0)
// m_pActLED->On();
DataMemBarrier();
if (DoRead((u8 *) pBuffer, nCount, nBlock) !=(int) nCount)
{
DataMemBarrier();
//if (m_pActLED != 0)
// m_pActLED->Off();
return -1;
}
DataMemBarrier();
//if (m_pActLED != 0)
// m_pActLED->Off();
return nCount;
}
int CEMMCDevice::Write(const void *pBuffer, unsigned nCount)
{
if (m_ullOffset % SD_BLOCK_SIZE != 0)
{
return -1;
}
unsigned nBlock = m_ullOffset / SD_BLOCK_SIZE;
//if (m_pActLED != 0)
// m_pActLED->On();
DataMemBarrier();
if (DoWrite((u8 *) pBuffer, nCount, nBlock) !=(int) nCount)
{
DataMemBarrier();
//if (m_pActLED != 0)
// m_pActLED->Off();
return -1;
}
DataMemBarrier();
//if (m_pActLED != 0)
// m_pActLED->Off();
return nCount;
}
unsigned long long CEMMCDevice::Seek(unsigned long long ullOffset)
{
m_ullOffset = ullOffset;
return m_ullOffset;
}
bool CEMMCDevice::PowerOn(void)
{
rpi_mailbox_property_t *buf;
RPI_PropertyInit();
RPI_PropertyAddTag(TAG_SET_POWER_STATE, DEVICE_ID_SD_CARD, POWER_STATE_ON | POWER_STATE_WAIT);
RPI_PropertyProcess();
//RPI_PropertyInit();
//RPI_PropertyAddTag(TAG_GET_POWER_STATE);
//RPI_PropertyProcess();
//buf = RPI_PropertyGet(TAG_GET_POWER_STATE);
buf = RPI_PropertyGet(TAG_SET_POWER_STATE);
if (buf)
{
u32 state = buf->data.buffer_32[0];
//DEBUG_LOG("state = %x\r\n, state");
//DEBUG_LOG("state = %x %x %x\r\n", buf->data.buffer_32[0], buf->data.buffer_32[1], buf->data.buffer_32[2]);
if ((state & POWER_STATE_DEVICE_DOESNT_EXIST) || ((state & 1) == POWER_STATE_OFF))
{
//DEBUG_LOG("Device did not power on successfully\r\n");
return false;
}
}
else
{
//DEBUG_LOG("RPI_PropertyGet(TAG_GET_POWER_STATE) failed\r\n");
return false;
}
return true;
}
void CEMMCDevice::PowerOff(void)
{
// Power off the SD card
u32 control0 = read32(EMMC_CONTROL0);
control0 &= ~(1 << 8); // Set SD Bus Power bit off in Power Control Register
write32(EMMC_CONTROL0, control0);
}
// Set the clock dividers to generate a target value
u32 CEMMCDevice::GetClockDivider(u32 base_clock, u32 target_rate)
{
// TODO: implement use of preset value registers
u32 targetted_divisor = 1;
if (target_rate <= base_clock)
{
targetted_divisor = base_clock / target_rate;
if (base_clock % target_rate)
{
targetted_divisor--;
}
}
// Decide on the clock mode to use
// Currently only 10-bit divided clock mode is supported
if (m_hci_ver >= 2)
{
// HCI version 3 or greater supports 10-bit divided clock mode
// This requires a power-of-two divider
// Find the first bit set
int divisor = -1;
for(int first_bit = 31; first_bit >= 0; first_bit--)
{
u32 bit_test =(1 << first_bit);
if (targetted_divisor & bit_test)
{
divisor = first_bit;
targetted_divisor &= ~bit_test;
if (targetted_divisor)
{
// The divisor is not a power-of-two, increase it
divisor++;
}
break;
}
}
if (divisor == -1)
{
divisor = 31;
}
if (divisor >= 32)
{
divisor = 31;
}
if (divisor != 0)
{
divisor =(1 <<(divisor - 1));
}
if (divisor >= 0x400)
{
divisor = 0x3ff;
}
u32 freq_select = divisor & 0xff;
u32 upper_bits =(divisor >> 8) & 0x3;
u32 ret =(freq_select << 8) |(upper_bits << 6) |(0 << 5);
#ifdef EMMC_DEBUG2
int denominator = 1;
if (divisor != 0)
{
denominator = divisor * 2;
}
int actual_clock = base_clock / denominator;
DEBUG_LOG("base_clock: %d, target_rate: %d, divisor: %08x, actual_clock: %d, ret: %08x", base_clock, target_rate, divisor, actual_clock, ret);
#endif
return ret;
}
else
{
DEBUG_LOG("Unsupported host version");
return SD_GET_CLOCK_DIVIDER_FAIL;
}
}
// Switch the clock rate whilst running
int CEMMCDevice::SwitchClockRate(u32 base_clock, u32 target_rate)
{
// Decide on an appropriate divider
u32 divider = GetClockDivider(base_clock, target_rate);
if (divider == SD_GET_CLOCK_DIVIDER_FAIL)
{
DEBUG_LOG("Couldn't get a valid divider for target rate %d Hz\r\n", target_rate);
return -1;
}
// Wait for the command inhibit(CMD and DAT) bits to clear
while(read32(EMMC_STATUS) & 3)
{
delay_us(1000);
}
// Set the SD clock off
u32 control1 = read32(EMMC_CONTROL1);
control1 &= ~(1 << 2);
write32(EMMC_CONTROL1, control1);
delay_us(2000);
// Write the new divider
control1 &= ~0xffe0; // Clear old setting + clock generator select
control1 |= divider;
write32(EMMC_CONTROL1, control1);
delay_us(2000);
// Enable the SD clock
control1 |=(1 << 2);
write32(EMMC_CONTROL1, control1);
delay_us(2000);
#ifdef EMMC_DEBUG2
DEBUG_LOG("Successfully set clock rate to %d Hz\r\n", target_rate);
#endif
return 0;
}
int CEMMCDevice::ResetCmd(void)
{
u32 control1 = read32(EMMC_CONTROL1);
control1 |= SD_RESET_CMD;
write32(EMMC_CONTROL1, control1);
if (TimeoutWait(EMMC_CONTROL1, SD_RESET_CMD, 0, 1000000) < 0)
{
DEBUG_LOG("CMD line did not reset properly");
return -1;
}
return 0;
}
int CEMMCDevice::ResetDat(void)
{
u32 control1 = read32(EMMC_CONTROL1);
control1 |= SD_RESET_DAT;
write32(EMMC_CONTROL1, control1);
if (TimeoutWait(EMMC_CONTROL1, SD_RESET_DAT, 0, 1000000) < 0)
{
DEBUG_LOG("DAT line did not reset properly");
return -1;
}
return 0;
}
void CEMMCDevice::IssueCommandInt(u32 cmd_reg, u32 argument, int timeout)
{
m_last_cmd_reg = cmd_reg;
m_last_cmd_success = 0;
// This is as per HCSS 3.7.1.1/3.7.2.2
#ifdef EMMC_POLL_STATUS_REG
// Check Command Inhibit
while(read32(EMMC_STATUS) & 1)
{
delay_us(1000);
}
// Is the command with busy?
if ((cmd_reg & SD_CMD_RSPNS_TYPE_MASK) == SD_CMD_RSPNS_TYPE_48B)
{
// With busy
// Is is an abort command?
if ((cmd_reg & SD_CMD_TYPE_MASK) != SD_CMD_TYPE_ABORT)
{
// Not an abort command
// Wait for the data line to be free
while(read32(EMMC_STATUS) & 2)
{
delay_us(1000);
}
}
}
#endif
// Set block size and block count
// For now, block size = 512 bytes, block count = 1,
if (m_blocks_to_transfer > 0xffff)
{
DEBUG_LOG("blocks_to_transfer too great(%d)\r\n", m_blocks_to_transfer);
m_last_cmd_success = 0;
return;
}
u32 blksizecnt = m_block_size |(m_blocks_to_transfer << 16);
write32(EMMC_BLKSIZECNT, blksizecnt);
// Set argument 1 reg
write32(EMMC_ARG1, argument);
// Set command reg
write32(EMMC_CMDTM, cmd_reg);
//delay_us(2000);
// Wait for command complete interrupt
TimeoutWait(EMMC_INTERRUPT, 0x8001, 1, timeout);
u32 irpts = read32(EMMC_INTERRUPT);
// Clear command complete status
write32(EMMC_INTERRUPT, 0xffff0001);
// Test for errors
if ((irpts & 0xffff0001) != 1)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("Error occured whilst waiting for command complete interrupt");
#endif
m_last_error = irpts & 0xffff0000;
m_last_interrupt = irpts;
return;
}
//delay_us(2000);
// Get response data
switch(cmd_reg & SD_CMD_RSPNS_TYPE_MASK)
{
case SD_CMD_RSPNS_TYPE_48:
case SD_CMD_RSPNS_TYPE_48B:
m_last_r0 = read32(EMMC_RESP0);
break;
case SD_CMD_RSPNS_TYPE_136:
m_last_r0 = read32(EMMC_RESP0);
m_last_r1 = read32(EMMC_RESP1);
m_last_r2 = read32(EMMC_RESP2);
m_last_r3 = read32(EMMC_RESP3);
break;
}
// If with data, wait for the appropriate interrupt
if (cmd_reg & SD_CMD_ISDATA)
{
u32 wr_irpt;
int is_write = 0;
if (cmd_reg & SD_CMD_DAT_DIR_CH)
{
wr_irpt =(1 << 5); // read
}
else
{
is_write = 1;
wr_irpt =(1 << 4); // write
}
#ifdef EMMC_DEBUG2
if (m_blocks_to_transfer > 1)
{
DEBUG_LOG("Multi block transfer");
}
#endif
TimeoutWait(EMMC_INTERRUPT, wr_irpt | 0x8000, 1, timeout);
irpts = read32(EMMC_INTERRUPT);
write32(EMMC_INTERRUPT, 0xffff0000 | wr_irpt);
if ((irpts &(0xffff0000 | wr_irpt)) != wr_irpt)
{
#ifdef EMMC_DEBUG
DEBUG_LOG("Error occured whilst waiting for data ready interrupt");
#endif
m_last_error = irpts & 0xffff0000;
m_last_interrupt = irpts;
return;
}
// Transfer the block
assert(m_block_size <= 1024); // internal FIFO size of EMMC
size_t length = m_block_size * m_blocks_to_transfer;
assert(((u32) m_buf & 3) == 0);
assert((length & 3) == 0);
u32 *pData =(u32 *) m_buf;
if (is_write)
{
for(; length > 0; length -= 4)
{
write32(EMMC_DATA, *pData++);
}
}
else
{
for(; length > 0; length -= 4)
{
*pData++ = read32(EMMC_DATA);
}
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("Block transfer complete");
#endif
}
// Wait for transfer complete(set if read/write transfer or with busy)
if ( (cmd_reg & SD_CMD_RSPNS_TYPE_MASK) == SD_CMD_RSPNS_TYPE_48B
||(cmd_reg & SD_CMD_ISDATA))
{
#ifdef EMMC_POLL_STATUS_REG
// First check command inhibit(DAT) is not already 0
if ((read32(EMMC_STATUS) & 2) == 0)
{
write32(EMMC_INTERRUPT, 0xffff0002);
}
else
#endif
{
TimeoutWait(EMMC_INTERRUPT, 0x8002, 1, timeout);
irpts = read32(EMMC_INTERRUPT);
write32(EMMC_INTERRUPT, 0xffff0002);
// Handle the case where both data timeout and transfer complete
// are set - transfer complete overrides data timeout: HCSS 2.2.17
if ( ((irpts & 0xffff0002) != 2)
&&((irpts & 0xffff0002) != 0x100002))
{
#ifdef EMMC_DEBUG
DEBUG_LOG("Error occured whilst waiting for transfer complete interrupt");
#endif
m_last_error = irpts & 0xffff0000;
m_last_interrupt = irpts;
return;
}
write32(EMMC_INTERRUPT, 0xffff0002);
}
}
// Return success
m_last_cmd_success = 1;
}
void CEMMCDevice::HandleCardInterrupt(void)
{
// Handle a card interrupt
#ifdef EMMC_DEBUG2
u32 status = read32(EMMC_STATUS);
DEBUG_LOG("Card interrupt");
DEBUG_LOG("controller status: %08x\r\n", status);
#endif
// Get the card status
if (m_card_rca)
{
IssueCommandInt(sd_commands[SEND_STATUS], m_card_rca << 16, 500000);
if (FAIL)
{
#ifdef EMMC_DEBUG
DEBUG_LOG("Unable to get card status");
#endif
}
else
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("card status: %08x\r\n", m_last_r0);
#endif
}
}
else
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("no card currently selected");
#endif
}
}
void CEMMCDevice::HandleInterrupts(void)
{
u32 irpts = read32(EMMC_INTERRUPT);
u32 reset_mask = 0;
if (irpts & SD_COMMAND_COMPLETE)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("spurious command complete interrupt");
#endif
reset_mask |= SD_COMMAND_COMPLETE;
}
if (irpts & SD_TRANSFER_COMPLETE)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("spurious transfer complete interrupt");
#endif
reset_mask |= SD_TRANSFER_COMPLETE;
}
if (irpts & SD_BLOCK_GAP_EVENT)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("spurious block gap event interrupt");
#endif
reset_mask |= SD_BLOCK_GAP_EVENT;
}
if (irpts & SD_DMA_INTERRUPT)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("spurious DMA interrupt");
#endif
reset_mask |= SD_DMA_INTERRUPT;
}
if (irpts & SD_BUFFER_WRITE_READY)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("spurious buffer write ready interrupt");
#endif
reset_mask |= SD_BUFFER_WRITE_READY;
ResetDat();
}
if (irpts & SD_BUFFER_READ_READY)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("spurious buffer read ready interrupt");
#endif
reset_mask |= SD_BUFFER_READ_READY;
ResetDat();
}
if (irpts & SD_CARD_INSERTION)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("card insertion detected");
#endif
reset_mask |= SD_CARD_INSERTION;
}
if (irpts & SD_CARD_REMOVAL)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("card removal detected");
#endif
reset_mask |= SD_CARD_REMOVAL;
m_card_removal = 1;
}
if (irpts & SD_CARD_INTERRUPT)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("card interrupt detected");
#endif
HandleCardInterrupt();
reset_mask |= SD_CARD_INTERRUPT;
}
if (irpts & 0x8000)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("spurious error interrupt: %08x\r\n", irpts);
#endif
reset_mask |= 0xffff0000;
}
write32(EMMC_INTERRUPT, reset_mask);
}
bool CEMMCDevice::IssueCommand(u32 command, u32 argument, int timeout)
{
// First, handle any pending interrupts
HandleInterrupts();
// Stop the command issue if it was the card remove interrupt that was handled
if (m_card_removal)
{
m_last_cmd_success = 0;
return false;
}
// Now run the appropriate commands by calling IssueCommandInt()
if (command & IS_APP_CMD)
{
command &= 0xff;
#ifdef EMMC_DEBUG2
DEBUG_LOG("Issuing command ACMD%d\r\n", command);
#endif
if (sd_acommands[command] == SD_CMD_RESERVED(0))
{
DEBUG_LOG("Invalid command ACMD%d\r\n", command);
m_last_cmd_success = 0;
return false;
}
m_last_cmd = APP_CMD;
u32 rca = 0;
if (m_card_rca)
{
rca = m_card_rca << 16;
}
IssueCommandInt(sd_commands[APP_CMD], rca, timeout);
if (m_last_cmd_success)
{
m_last_cmd = command | IS_APP_CMD;
IssueCommandInt(sd_acommands[command], argument, timeout);
}
}
else
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("Issuing command CMD%d\r\n", command);
#endif
if (sd_commands[command] == SD_CMD_RESERVED(0))
{
DEBUG_LOG("Invalid command CMD%d\r\n", command);
m_last_cmd_success = 0;
return false;
}
m_last_cmd = command;
IssueCommandInt(sd_commands[command], argument, timeout);
}
#ifdef EMMC_DEBUG2
if (FAIL)
{
DEBUG_LOG("Error issuing %s%u(intr %08x)\r\n", m_last_cmd & IS_APP_CMD ? "ACMD" : "CMD\r\n", m_last_cmd & 0xff, m_last_interrupt);
if (m_last_error == 0)
{
DEBUG_LOG("TIMEOUT");
}
else
{
for(int i = 0; i < SD_ERR_RSVD; i++)
{
if (m_last_error &(1 <<(i + 16)))
{
DEBUG_LOG(err_irpts[i]);
}
}
}
}
else
{
DEBUG_LOG("command completed successfully");
}
#endif
return m_last_cmd_success;
}
int CEMMCDevice::CardReset(void)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("Resetting controller");
#endif
u32 control1 = read32(EMMC_CONTROL1);
control1 |=(1 << 24);
// Disable clock
control1 &= ~(1 << 2);
control1 &= ~(1 << 0);
write32(EMMC_CONTROL1, control1);
if (TimeoutWait(EMMC_CONTROL1, 7 << 24, 0, 1000000) < 0)
{
DEBUG_LOG("Controller did not reset properly");
return -1;
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("control0: %08x, control1: %08x, control2: %08x\r\n",
read32(EMMC_CONTROL0), read32(EMMC_CONTROL1), read32(EMMC_CONTROL2));
#endif
// Check for a valid card
#ifdef EMMC_DEBUG2
DEBUG_LOG("checking for an inserted card");
#endif
TimeoutWait(EMMC_STATUS, 1 << 16, 1, 500000);
u32 status_reg = read32(EMMC_STATUS);
if ((status_reg &(1 << 16)) == 0)
{
DEBUG_LOG("no card inserted");
return -1;
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("status: %08x\r\n", status_reg);
#endif
// Clear control2
write32(EMMC_CONTROL2, 0);
// Get the base clock rate
u32 base_clock = get_clock_rate(CLOCK_ID_EMMC);
if (base_clock == 0)
{
DEBUG_LOG("assuming clock rate to be 100MHz");
base_clock = 100000000;
}
// Set clock rate to something slow
#ifdef EMMC_DEBUG2
DEBUG_LOG("setting clock rate");
#endif
control1 = read32(EMMC_CONTROL1);
control1 |= 1; // enable clock
// Set to identification frequency(400 kHz)
u32 f_id = GetClockDivider(base_clock, SD_CLOCK_ID);
if (f_id == SD_GET_CLOCK_DIVIDER_FAIL)
{
DEBUG_LOG("unable to get a valid clock divider for ID frequency");
return -1;
}
control1 |= f_id;
// was not masked out and or'd with(7 << 16) in original driver
control1 &= ~(0xF << 16);
control1 |=(11 << 16); // data timeout = TMCLK * 2^24
write32(EMMC_CONTROL1, control1);
if (TimeoutWait(EMMC_CONTROL1, 2, 1, 1000000) < 0)
{
DEBUG_LOG("Clock did not stabilise within 1 second");
return -1;
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("control0: %08x, control1: %08x\r\n",
read32(EMMC_CONTROL0), read32(EMMC_CONTROL1));
#endif
// Enable the SD clock
#ifdef EMMC_DEBUG2
DEBUG_LOG("enabling SD clock");
#endif
delay_us(2000);
control1 = read32(EMMC_CONTROL1);
control1 |= 4;
write32(EMMC_CONTROL1, control1);
delay_us(2000);
// Mask off sending interrupts to the ARM
write32(EMMC_IRPT_EN, 0);
// Reset interrupts
write32(EMMC_INTERRUPT, 0xffffffff);
// Have all interrupts sent to the INTERRUPT register
u32 irpt_mask = 0xffffffff &(~SD_CARD_INTERRUPT);
#ifdef SD_CARD_INTERRUPTS
irpt_mask |= SD_CARD_INTERRUPT;
#endif
write32(EMMC_IRPT_MASK, irpt_mask);
delay_us(2000);
// >> Prepare the device structure
m_device_id[0] = 0;
m_device_id[1] = 0;
m_device_id[2] = 0;
m_device_id[3] = 0;
m_card_supports_sdhc = 0;
m_card_supports_18v = 0;
m_card_ocr = 0;
m_card_rca = 0;
m_last_interrupt = 0;
m_last_error = 0;
m_failed_voltage_switch = 0;
m_last_cmd_reg = 0;
m_last_cmd = 0;
m_last_cmd_success = 0;
m_last_r0 = 0;
m_last_r1 = 0;
m_last_r2 = 0;
m_last_r3 = 0;
m_buf = 0;
m_blocks_to_transfer = 0;
m_block_size = 0;
m_card_removal = 0;
m_base_clock = 0;
// << Prepare the device structure
m_base_clock = base_clock;
// Send CMD0 to the card(reset to idle state)
if (!IssueCommand(GO_IDLE_STATE, 0))
{
DEBUG_LOG("no CMD0 response");
return -1;
}
// Send CMD8 to the card
// Voltage supplied = 0x1 = 2.7-3.6V(standard)
// Check pattern = 10101010b(as per PLSS 4.3.13) = 0xAA
#ifdef EMMC_DEBUG2
DEBUG_LOG("Note a timeout error on the following command(CMD8) is normal and expected if the SD card version is less than 2.0");
#endif
IssueCommand(SEND_IF_COND, 0x1aa);
int v2_later = 0;
if (TIMEOUT)
{
v2_later = 0;
}
else if (CMD_TIMEOUT)
{
if (ResetCmd() == -1)
{
return -1;
}
write32(EMMC_INTERRUPT, SD_ERR_MASK_CMD_TIMEOUT);
v2_later = 0;
}
else if (FAIL)
{
DEBUG_LOG("failure sending CMD8(%08x)\r\n", m_last_interrupt);
return -1;
}
else
{
if ((m_last_r0 & 0xfff) != 0x1aa)
{
DEBUG_LOG("unusable card");
#ifdef EMMC_DEBUG
DEBUG_LOG("CMD8 response %08x\r\n", m_last_r0);
#endif
return -1;
}
else
{
v2_later = 1;
}
}
// Here we are supposed to check the response to CMD5(HCSS 3.6)
// It only returns if the card is a SDIO card
#ifdef EMMC_DEBUG2
DEBUG_LOG("Note that a timeout error on the following command(CMD5) is normal and expected if the card is not a SDIO card.");
#endif
IssueCommand(IO_SET_OP_COND, 0, 10000);
if (!TIMEOUT)
{
if (CMD_TIMEOUT)
{
if (ResetCmd() == -1)
{
return -1;
}
write32(EMMC_INTERRUPT, SD_ERR_MASK_CMD_TIMEOUT);
}
else
{
DEBUG_LOG("SDIO card detected - not currently supported");
#ifdef EMMC_DEBUG2
DEBUG_LOG("CMD5 returned %08x\r\n", m_last_r0);
#endif
return -1;
}
}
// Call an inquiry ACMD41(voltage window = 0) to get the OCR
#ifdef EMMC_DEBUG2
DEBUG_LOG("sending inquiry ACMD41");
#endif
if (!IssueCommand(ACMD(41), 0))
{
DEBUG_LOG("Inquiry ACMD41 failed");
return -1;
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("inquiry ACMD41 returned %08x\r\n", m_last_r0);
#endif
// Call initialization ACMD41
int card_is_busy = 1;
while(card_is_busy)
{
u32 v2_flags = 0;
if (v2_later)
{
// Set SDHC support
v2_flags |=(1 << 30);
// Set 1.8v support
#ifdef SD_1_8V_SUPPORT
if (!m_failed_voltage_switch)
{
v2_flags |=(1 << 24);
}
#endif
#ifdef SDXC_MAXIMUM_PERFORMANCE
// Enable SDXC maximum performance
v2_flags |=(1 << 28);
#endif
}
if (!IssueCommand(ACMD(41), 0x00ff8000 | v2_flags))
{
DEBUG_LOG("Error issuing ACMD41");
return -1;
}
if ((m_last_r0 >> 31) & 1)
{
// Initialization is complete
m_card_ocr =(m_last_r0 >> 8) & 0xffff;
m_card_supports_sdhc =(m_last_r0 >> 30) & 0x1;
#ifdef SD_1_8V_SUPPORT
if (!m_failed_voltage_switch)
{
m_card_supports_18v =(m_last_r0 >> 24) & 0x1;
}
#endif
card_is_busy = 0;
}
else
{
// Card is still busy
#ifdef EMMC_DEBUG2
DEBUG_LOG("Card is busy, retrying");
#endif
delay_us(500000);
}
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("card identified: OCR: %04x, 1.8v support: %d, SDHC support: %d\r\n", m_card_ocr, m_card_supports_18v, m_card_supports_sdhc);
#endif
// At this point, we know the card is definitely an SD card, so will definitely
// support SDR12 mode which runs at 25 MHz
SwitchClockRate(base_clock, SD_CLOCK_NORMAL);
// A small wait before the voltage switch
delay_us(5000);
// Switch to 1.8V mode if possible
if (m_card_supports_18v)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("switching to 1.8V mode");
#endif
// As per HCSS 3.6.1
// Send VOLTAGE_SWITCH
if (!IssueCommand(VOLTAGE_SWITCH, 0))
{
#ifdef EMMC_DEBUG
DEBUG_LOG("error issuing VOLTAGE_SWITCH");
#endif
m_failed_voltage_switch = 1;
PowerOff();
return CardReset();
}
// Disable SD clock
control1 = read32(EMMC_CONTROL1);
control1 &= ~(1 << 2);
write32(EMMC_CONTROL1, control1);
// Check DAT[3:0]
status_reg = read32(EMMC_STATUS);
u32 dat30 =(status_reg >> 20) & 0xf;
if (dat30 != 0)
{
#ifdef EMMC_DEBUG
DEBUG_LOG("DAT[3:0] did not settle to 0");
#endif
m_failed_voltage_switch = 1;
PowerOff();
return CardReset();
}
// Set 1.8V signal enable to 1
u32 control0 = read32(EMMC_CONTROL0);
control0 |=(1 << 8);
write32(EMMC_CONTROL0, control0);
// Wait 5 ms
delay_us(5000);
// Check the 1.8V signal enable is set
control0 = read32(EMMC_CONTROL0);
if (((control0 >> 8) & 1) == 0)
{
#ifdef EMMC_DEBUG
DEBUG_LOG("controller did not keep 1.8V signal enable high");
#endif
m_failed_voltage_switch = 1;
PowerOff();
return CardReset();
}
// Re-enable the SD clock
control1 = read32(EMMC_CONTROL1);
control1 |=(1 << 2);
write32(EMMC_CONTROL1, control1);
delay_us(10000);
// Check DAT[3:0]
status_reg = read32(EMMC_STATUS);
dat30 =(status_reg >> 20) & 0xf;
if (dat30 != 0xf)
{
#ifdef EMMC_DEBUG
DEBUG_LOG("DAT[3:0] did not settle to 1111b(%01x)\r\n", dat30);
#endif
m_failed_voltage_switch = 1;
PowerOff();
return CardReset();
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("voltage switch complete");
#endif
}
// Send CMD2 to get the cards CID
if (!IssueCommand(ALL_SEND_CID, 0))
{
DEBUG_LOG("error sending ALL_SEND_CID");
return -1;
}
m_device_id[0] = m_last_r0;
m_device_id[1] = m_last_r1;
m_device_id[2] = m_last_r2;
m_device_id[3] = m_last_r3;
#ifdef EMMC_DEBUG2
DEBUG_LOG("Card CID: %08x%08x%08x%08x",
m_device_id[3], m_device_id[2], m_device_id[1], m_device_id[0]);
#endif
// Send CMD3 to enter the data state
if (!IssueCommand(SEND_RELATIVE_ADDR, 0))
{
DEBUG_LOG("error sending SEND_RELATIVE_ADDR");
return -1;
}
u32 cmd3_resp = m_last_r0;
#ifdef EMMC_DEBUG2
DEBUG_LOG("CMD3 response: %08x\r\n", cmd3_resp);
#endif
m_card_rca =(cmd3_resp >> 16) & 0xffff;
u32 crc_error =(cmd3_resp >> 15) & 0x1;
u32 illegal_cmd =(cmd3_resp >> 14) & 0x1;
u32 error =(cmd3_resp >> 13) & 0x1;
u32 status =(cmd3_resp >> 9) & 0xf;
u32 ready =(cmd3_resp >> 8) & 0x1;
if (crc_error)
{
DEBUG_LOG("CRC error");
return -1;
}
if (illegal_cmd)
{
DEBUG_LOG("illegal command");
return -1;
}
if (error)
{
DEBUG_LOG("generic error");
return -1;
}
if (!ready)
{
DEBUG_LOG("not ready for data");
return -1;
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("RCA: %04x\r\n", m_card_rca);
#endif
// Now select the card(toggles it to transfer state)
if (!IssueCommand(SELECT_CARD, m_card_rca << 16))
{
DEBUG_LOG("error sending CMD7");
return -1;
}
u32 cmd7_resp = m_last_r0;
status =(cmd7_resp >> 9) & 0xf;
if ((status != 3) &&(status != 4))
{
DEBUG_LOG("Invalid status(%d)\r\n", status);
return -1;
}
// If not an SDHC card, ensure BLOCKLEN is 512 bytes
if (!m_card_supports_sdhc)
{
if (!IssueCommand(SET_BLOCKLEN, SD_BLOCK_SIZE))
{
DEBUG_LOG("Error sending SET_BLOCKLEN");
return -1;
}
}
u32 controller_block_size = read32(EMMC_BLKSIZECNT);
controller_block_size &=(~0xfff);
controller_block_size |= 0x200;
write32(EMMC_BLKSIZECNT, controller_block_size);
// Get the cards SCR register
m_buf = &m_SCR.scr[0];
m_block_size = 8;
m_blocks_to_transfer = 1;
IssueCommand(SEND_SCR, 0);
m_block_size = SD_BLOCK_SIZE;
if (FAIL)
{
DEBUG_LOG("Error sending SEND_SCR");
return -1;
}
// Determine card version
// Note that the SCR is big-endian
u32 scr0 = __builtin_bswap32(m_SCR.scr[0]);
m_SCR.sd_version = SD_VER_UNKNOWN;
u32 sd_spec =(scr0 >>(56 - 32)) & 0xf;
u32 sd_spec3 =(scr0 >>(47 - 32)) & 0x1;
u32 sd_spec4 =(scr0 >>(42 - 32)) & 0x1;
m_SCR.sd_bus_widths =(scr0 >>(48 - 32)) & 0xf;
if (sd_spec == 0)
{
m_SCR.sd_version = SD_VER_1;
}
else if (sd_spec == 1)
{
m_SCR.sd_version = SD_VER_1_1;
}
else if (sd_spec == 2)
{
if (sd_spec3 == 0)
{
m_SCR.sd_version = SD_VER_2;
}
else if (sd_spec3 == 1)
{
if (sd_spec4 == 0)
{
m_SCR.sd_version = SD_VER_3;
}
else if (sd_spec4 == 1)
{
m_SCR.sd_version = SD_VER_4;
}
}
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("SCR[0]: %08x, SCR[1]: %08x\r\n", m_SCR.scr[0], m_SCR.scr[1]);;
DEBUG_LOG("SCR: %08x%08x\r\n", __builtin_bswap32(m_SCR.scr[0]), __builtin_bswap32(m_SCR.scr[1]));
DEBUG_LOG("SCR: version %s, bus_widths %01x\r\n", sd_versions[m_SCR.sd_version], m_SCR.sd_bus_widths);
#endif
if (m_SCR.sd_bus_widths & 4)
{
// Set 4-bit transfer mode(ACMD6)
// See HCSS 3.4 for the algorithm
#ifdef SD_4BIT_DATA
#ifdef EMMC_DEBUG2
DEBUG_LOG("Switching to 4-bit data mode");
#endif
// Disable card interrupt in host
u32 old_irpt_mask = read32(EMMC_IRPT_MASK);
u32 new_iprt_mask = old_irpt_mask & ~(1 << 8);
write32(EMMC_IRPT_MASK, new_iprt_mask);
// Send ACMD6 to change the card's bit mode
if (!IssueCommand(SET_BUS_WIDTH, 2))
{
DEBUG_LOG("Switch to 4-bit data mode failed");
}
else
{
// Change bit mode for Host
u32 control0 = read32(EMMC_CONTROL0);
control0 |= 0x2;
write32(EMMC_CONTROL0, control0);
// Re-enable card interrupt in host
write32(EMMC_IRPT_MASK, old_irpt_mask);
#ifdef EMMC_DEBUG2
DEBUG_LOG("switch to 4-bit complete");
#endif
}
#endif
}
DEBUG_LOG("Found a valid version %s SD card\r\n", sd_versions[m_SCR.sd_version]);
#ifdef EMMC_DEBUG2
DEBUG_LOG("setup successful(status %d)\r\n", status);
#endif
// Reset interrupt register
write32(EMMC_INTERRUPT, 0xffffffff);
return 0;
}
int CEMMCDevice::EnsureDataMode(void)
{
if (m_card_rca == 0)
{
// Try again to initialise the card
int ret = CardReset();
if (ret != 0)
{
return ret;
}
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("EnsureDataMode() obtaining status register for card_rca %08x: \r\n", m_card_rca);
#endif
if (!IssueCommand(SEND_STATUS, m_card_rca << 16))
{
DEBUG_LOG("EnsureDataMode() error sending CMD13");
m_card_rca = 0;
return -1;
}
u32 status = m_last_r0;
u32 cur_state =(status >> 9) & 0xf;
#ifdef EMMC_DEBUG2
DEBUG_LOG("status %d\r\n", cur_state);
#endif
if (cur_state == 3)
{
// Currently in the stand-by state - select it
if (!IssueCommand(SELECT_CARD, m_card_rca << 16))
{
DEBUG_LOG("EnsureDataMode() no response from CMD17");
m_card_rca = 0;
return -1;
}
}
else if (cur_state == 5)
{
// In the data transfer state - cancel the transmission
if (!IssueCommand(STOP_TRANSMISSION, 0))
{
DEBUG_LOG("EnsureDataMode() no response from CMD12");
m_card_rca = 0;
return -1;
}
// Reset the data circuit
ResetDat();
}
else if (cur_state != 4)
{
// Not in the transfer state - re-initialise
int ret = CardReset();
if (ret != 0)
{
return ret;
}
}
// Check again that we're now in the correct mode
if (cur_state != 4)
{
#ifdef EMMC_DEBUG2
DEBUG_LOG("EnsureDataMode() rechecking status: ");
#endif
if (!IssueCommand(SEND_STATUS, m_card_rca << 16))
{
DEBUG_LOG("EnsureDataMode() no response from CMD13");
m_card_rca = 0;
return -1;
}
status = m_last_r0;
cur_state =(status >> 9) & 0xf;
#ifdef EMMC_DEBUG2
DEBUG_LOG("status %d\r\n", cur_state);
#endif
if (cur_state != 4)
{
DEBUG_LOG("unable to initialise SD card to data mode(state %d)\r\n", cur_state);
m_card_rca = 0;
return -1;
}
}
return 0;
}
int CEMMCDevice::DoDataCommand(int is_write, u8 *buf, size_t buf_size, u32 block_no)
{
// PLSS table 4.20 - SDSC cards use byte addresses rather than block addresses
if (!m_card_supports_sdhc)
{
block_no *= SD_BLOCK_SIZE;
}
// This is as per HCSS 3.7.2.1
if (buf_size < m_block_size)
{
DEBUG_LOG("DoDataCommand() called with buffer size(%d) less than block size(%d)\r\n", buf_size, m_block_size);
return -1;
}
m_blocks_to_transfer = buf_size / m_block_size;
if (buf_size % m_block_size)
{
DEBUG_LOG("DoDataCommand() called with buffer size(%d) not an exact multiple of block size(%d)\r\n", buf_size, m_block_size);
return -1;
}
m_buf = buf;
// Decide on the command to use
int command;
if (is_write)
{
if (m_blocks_to_transfer > 1)
{
command = WRITE_MULTIPLE_BLOCK;
}
else
{
command = WRITE_BLOCK;
}
}
else
{
if (m_blocks_to_transfer > 1)
{
command = READ_MULTIPLE_BLOCK;
}
else
{
command = READ_SINGLE_BLOCK;
}
}
int retry_count = 0;
int max_retries = 3;
while(retry_count < max_retries)
{
if (IssueCommand(command, block_no, 5000000))
{
break;
}
else
{
DEBUG_LOG("error sending CMD%d\r\n", command);
DEBUG_LOG("error = %08x\r\n", m_last_error);
if (++retry_count < max_retries)
{
DEBUG_LOG("Retrying");
}
else
{
DEBUG_LOG("Giving up");
}
}
}
if (retry_count == max_retries)
{
m_card_rca = 0;
return -1;
}
return 0;
}
int CEMMCDevice::DoRead(u8 *buf, size_t buf_size, u32 block_no)
{
// g_pLogger->Write("\r\n", LogNotice, "DoRead %d\r\n", block_no);
// Check the status of the card
if (EnsureDataMode() != 0)
{
return -1;
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("Reading from block %u %08x\r\n", block_no,(unsigned)buf);
#endif
if (DoDataCommand(0, buf, buf_size, block_no) < 0)
{
return -1;
}
//int y = 0;
//int index = 0;
//for(y = 0; y <(512 / 8); ++y)
//{
// g_pLogger->Write("\r\n", LogNotice, "%04x, %02x %02x %02x %02x %02x %02x %02x %02x"
// , index
// , buf[index]
// , buf[index + 1]
// , buf[index + 2]
// , buf[index + 3]
// , buf[index + 4]
// , buf[index + 5]
// , buf[index + 6]
// , buf[index + 7]
// );
// index += 8;
//}
#ifdef EMMC_DEBUG2
DEBUG_LOG("Data read successful");
#endif
return buf_size;
}
int CEMMCDevice::DoWrite(u8 *buf, size_t buf_size, u32 block_no)
{
// Check the status of the card
if (EnsureDataMode() != 0)
{
return -1;
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("Writing to block %u\r\n", block_no);
#endif
if (DoDataCommand(1, buf, buf_size, block_no) < 0)
{
return -1;
}
#ifdef EMMC_DEBUG2
DEBUG_LOG("Data write successful");
#endif
return buf_size;
}
int CEMMCDevice::TimeoutWait(unsigned reg, unsigned mask, int value, unsigned usec)
{
unsigned nCount = usec / 1000;
do
{
delay_us(1);
if ((read32(reg) & mask) ? value : !value)
{
return 0;
}
delay_us(999);
}
while(nCount--);
return -1;
}