Unverified Commit 5735057d authored by Antonio Niño Díaz's avatar Antonio Niño Díaz Committed by GitHub
Browse files

Merge pull request #1796 from grandpaul/rpi3-sdhost-driver

RPi3 SDHost driver
parents 0d845356 92d2f491
/*
* Copyright (c) 2019, Linaro Limited
* Copyright (c) 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <common/debug.h>
#include <lib/mmio.h>
#include <drivers/delay_timer.h>
#include <drivers/rpi3/sdhost/rpi3_sdhost.h>
#include <drivers/mmc.h>
#include <drivers/rpi3/gpio/rpi3_gpio.h>
#include <errno.h>
#include <string.h>
static void rpi3_sdhost_initialize(void);
static int rpi3_sdhost_send_cmd(struct mmc_cmd *cmd);
static int rpi3_sdhost_set_ios(unsigned int clk, unsigned int width);
static int rpi3_sdhost_prepare(int lba, uintptr_t buf, size_t size);
static int rpi3_sdhost_read(int lba, uintptr_t buf, size_t size);
static int rpi3_sdhost_write(int lba, uintptr_t buf, size_t size);
static const struct mmc_ops rpi3_sdhost_ops = {
.init = rpi3_sdhost_initialize,
.send_cmd = rpi3_sdhost_send_cmd,
.set_ios = rpi3_sdhost_set_ios,
.prepare = rpi3_sdhost_prepare,
.read = rpi3_sdhost_read,
.write = rpi3_sdhost_write,
};
static struct rpi3_sdhost_params rpi3_sdhost_params;
/**
* Wait for command being processed.
*
* This function waits the command being processed. It compares
* the ENABLE flag of the HC_COMMAND register. When ENABLE flag disappeared
* it means the command is processed by the SDHOST.
* The timeout is currently 1000*100 us = 100 ms.
*
* @return 0: command finished. 1: command timed out.
*/
static int rpi3_sdhost_waitcommand(void)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
volatile int timeout = 1000;
while ((mmio_read_32(reg_base + HC_COMMAND) & HC_CMD_ENABLE)
&& (--timeout > 0)) {
udelay(100);
}
return ((timeout > 0) ? 0 : (-(ETIMEDOUT)));
}
/**
* Send the command and argument to the SDHOST
*
* This function will wait for the previous command finished. And then
* clear any error status of previous command. And then
* send out the command and args. The command will be turned on the ENABLE
* flag before sending out.
*/
static void send_command_raw(unsigned int cmd, unsigned int arg)
{
unsigned int status;
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
/* wait for previous command finish */
rpi3_sdhost_waitcommand();
/* clean error status */
status = mmio_read_32(reg_base + HC_HOSTSTATUS);
if (status & HC_HSTST_MASK_ERROR_ALL)
mmio_write_32(reg_base + HC_HOSTSTATUS, status);
/* recording the command */
rpi3_sdhost_params.current_cmd = cmd & HC_CMD_COMMAND_MASK;
/* send the argument and command */
mmio_write_32(reg_base + HC_ARGUMENT, arg);
mmio_write_32(reg_base + HC_COMMAND, cmd | HC_CMD_ENABLE);
}
/**
* Send the command and argument to the SDHOST, decorated with control
* flags.
*
* This function will use send_command_raw to send the commands to SDHOST.
* But before sending it will decorate the command with control flags specific
* to SDHOST.
*/
static void send_command_decorated(unsigned int cmd, unsigned int arg)
{
unsigned int cmd_flags = 0;
switch (cmd & HC_CMD_COMMAND_MASK) {
case MMC_CMD(0):
cmd_flags |= HC_CMD_RESPONSE_NONE;
break;
case MMC_ACMD(51):
cmd_flags |= HC_CMD_READ;
break;
case MMC_CMD(8):
case MMC_CMD(11):
case MMC_CMD(17):
case MMC_CMD(18):
cmd_flags |= HC_CMD_READ;
break;
case MMC_CMD(20):
case MMC_CMD(24):
case MMC_CMD(25):
cmd_flags |= HC_CMD_WRITE;
break;
case MMC_CMD(12):
cmd_flags |= HC_CMD_BUSY;
break;
default:
break;
}
send_command_raw(cmd | cmd_flags, arg);
}
/**
* drains the FIFO on DATA port
*
* This function drains any data left in the DATA port.
*/
static void rpi3_drain_fifo(void)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
volatile int timeout = 100000;
rpi3_sdhost_waitcommand();
while (mmio_read_32(reg_base + HC_HOSTSTATUS) & HC_HSTST_HAVEDATA) {
mmio_read_32(reg_base + HC_DATAPORT);
udelay(100);
}
while (1) {
uint32_t edm, fsm;
edm = mmio_read_32(reg_base + HC_DEBUG);
fsm = edm & HC_DBG_FSM_MASK;
if ((fsm == HC_DBG_FSM_IDENTMODE) ||
(fsm == HC_DBG_FSM_DATAMODE))
break;
if ((fsm == HC_DBG_FSM_READWAIT) ||
(fsm == HC_DBG_FSM_WRITESTART1) ||
(fsm == HC_DBG_FSM_READDATA)) {
mmio_write_32(reg_base + HC_DEBUG,
edm | HC_DBG_FORCE_DATA_MODE);
break;
}
if (--timeout <= 0) {
ERROR("rpi3_sdhost: %s cannot recover stat\n",
__func__);
return;
}
}
}
/**
* Dump SDHOST registers
*/
static void rpi3_sdhost_print_regs(void)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
INFO("rpi3_sdhost: HC_COMMAND: 0x%08x\n",
mmio_read_32(reg_base + HC_COMMAND));
INFO("rpi3_sdhost: HC_ARGUMENT: 0x%08x\n",
mmio_read_32(reg_base + HC_ARGUMENT));
INFO("rpi3_sdhost: HC_TIMEOUTCOUNTER: 0x%08x\n",
mmio_read_32(reg_base + HC_TIMEOUTCOUNTER));
INFO("rpi3_sdhost: HC_CLOCKDIVISOR: 0x%08x\n",
mmio_read_32(reg_base + HC_CLOCKDIVISOR));
INFO("rpi3_sdhost: HC_RESPONSE_0: 0x%08x\n",
mmio_read_32(reg_base + HC_RESPONSE_0));
INFO("rpi3_sdhost: HC_RESPONSE_1: 0x%08x\n",
mmio_read_32(reg_base + HC_RESPONSE_1));
INFO("rpi3_sdhost: HC_RESPONSE_2: 0x%08x\n",
mmio_read_32(reg_base + HC_RESPONSE_2));
INFO("rpi3_sdhost: HC_RESPONSE_3: 0x%08x\n",
mmio_read_32(reg_base + HC_RESPONSE_3));
INFO("rpi3_sdhost: HC_HOSTSTATUS: 0x%08x\n",
mmio_read_32(reg_base + HC_HOSTSTATUS));
INFO("rpi3_sdhost: HC_POWER: 0x%08x\n",
mmio_read_32(reg_base + HC_POWER));
INFO("rpi3_sdhost: HC_DEBUG: 0x%08x\n",
mmio_read_32(reg_base + HC_DEBUG));
INFO("rpi3_sdhost: HC_HOSTCONFIG: 0x%08x\n",
mmio_read_32(reg_base + HC_HOSTCONFIG));
INFO("rpi3_sdhost: HC_BLOCKSIZE: 0x%08x\n",
mmio_read_32(reg_base + HC_BLOCKSIZE));
INFO("rpi3_sdhost: HC_BLOCKCOUNT: 0x%08x\n",
mmio_read_32(reg_base + HC_BLOCKCOUNT));
}
/**
* Reset SDHOST
*/
static void rpi3_sdhost_reset(void)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
unsigned int dbg;
uint32_t tmp1;
mmio_write_32(reg_base + HC_POWER, 0);
mmio_write_32(reg_base + HC_COMMAND, 0);
mmio_write_32(reg_base + HC_ARGUMENT, 0);
mmio_write_32(reg_base + HC_TIMEOUTCOUNTER, HC_TIMEOUT_DEFAULT);
mmio_write_32(reg_base + HC_CLOCKDIVISOR, 0);
mmio_write_32(reg_base + HC_HOSTSTATUS, HC_HSTST_RESET);
mmio_write_32(reg_base + HC_HOSTCONFIG, 0);
mmio_write_32(reg_base + HC_BLOCKSIZE, 0);
mmio_write_32(reg_base + HC_BLOCKCOUNT, 0);
dbg = mmio_read_32(reg_base + HC_DEBUG);
dbg &= ~((HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_READ_SHIFT) |
(HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_WRITE_SHIFT));
dbg |= (HC_FIFO_THRESH_READ << HC_DBG_FIFO_THRESH_READ_SHIFT) |
(HC_FIFO_THRESH_WRITE << HC_DBG_FIFO_THRESH_WRITE_SHIFT);
mmio_write_32(reg_base + HC_DEBUG, dbg);
mdelay(250);
mmio_write_32(reg_base + HC_POWER, 1);
mdelay(250);
rpi3_sdhost_params.clk_rate = 0;
mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_MAXVAL);
tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1 | HC_HSTCF_INT_BUSY);
}
static void rpi3_sdhost_initialize(void)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
assert((rpi3_sdhost_params.reg_base & MMC_BLOCK_MASK) == 0);
rpi3_sdhost_reset();
mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_PREFERVAL);
udelay(300);
}
static int rpi3_sdhost_send_cmd(struct mmc_cmd *cmd)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
int err = 0;
uint32_t cmd_idx;
uint32_t cmd_arg;
uint32_t cmd_flags = 0;
uint32_t intmask;
/* Wait for the command done */
err = rpi3_sdhost_waitcommand();
if (err != 0) {
WARN("previous command not done yet\n");
return err;
}
cmd_idx = cmd->cmd_idx & HC_CMD_COMMAND_MASK;
if (cmd_idx == MMC_CMD(17))
cmd_idx = MMC_CMD(18);
cmd_arg = cmd->cmd_arg;
if (cmd_idx == MMC_ACMD(51)) {
/* if previous cmd send to SDHOST is not MMC_CMD(55).
* It means this MMC_ACMD(51) is a resend.
* And we must also resend MMC_CMD(55) in this case
*/
if (rpi3_sdhost_params.current_cmd != MMC_CMD(55)) {
send_command_decorated(
MMC_CMD(55),
rpi3_sdhost_params.sdcard_rca <<
RCA_SHIFT_OFFSET);
rpi3_sdhost_params.mmc_app_cmd = 1;
rpi3_sdhost_waitcommand();
/* Also we need to call prepare to clean the buffer */
rpi3_sdhost_prepare(0, (uintptr_t)NULL, 8);
}
}
/* We ignore MMC_CMD(12) sending from the TF-A's MMC driver
* because we send MMC_CMD(12) by ourselves.
*/
if (cmd_idx == MMC_CMD(12))
return 0;
if ((cmd->resp_type & MMC_RSP_136) &&
(cmd->resp_type & MMC_RSP_BUSY)) {
ERROR("rpi3_sdhost: unsupported response type!\n");
return -(EOPNOTSUPP);
}
if (cmd->resp_type & MMC_RSP_48 && cmd->resp_type != MMC_RESPONSE_R2) {
/* 48-bit command
* We don't need to set any flags here because it is default.
*/
} else if (cmd->resp_type & MMC_RSP_136) {
/* 136-bit command */
cmd_flags |= HC_CMD_RESPONSE_LONG;
} else {
/* no respond command */
cmd_flags |= HC_CMD_RESPONSE_NONE;
}
rpi3_sdhost_params.cmdbusy = 0;
if (cmd->resp_type & MMC_RSP_BUSY) {
cmd_flags |= HC_CMD_BUSY;
rpi3_sdhost_params.cmdbusy = 1;
}
if (rpi3_sdhost_params.mmc_app_cmd) {
switch (cmd_idx) {
case MMC_ACMD(41):
if (cmd_arg == OCR_HCS)
cmd_arg |= OCR_3_3_3_4;
break;
default:
break;
}
rpi3_sdhost_params.mmc_app_cmd = 0;
}
if (cmd_idx == MMC_CMD(55))
rpi3_sdhost_params.mmc_app_cmd = 1;
send_command_decorated(cmd_idx | cmd_flags, cmd_arg);
intmask = mmio_read_32(reg_base + HC_HOSTSTATUS);
if (rpi3_sdhost_params.cmdbusy && (intmask & HC_HSTST_INT_BUSY)) {
mmio_write_32(reg_base + HC_HOSTSTATUS, HC_HSTST_INT_BUSY);
rpi3_sdhost_params.cmdbusy = 0;
}
if (!(cmd_flags & HC_CMD_RESPONSE_NONE)) {
err = rpi3_sdhost_waitcommand();
if (err != 0)
ERROR("rpi3_sdhost: cmd cannot be finished\n");
}
cmd->resp_data[0] = mmio_read_32(reg_base + HC_RESPONSE_0);
cmd->resp_data[1] = mmio_read_32(reg_base + HC_RESPONSE_1);
cmd->resp_data[2] = mmio_read_32(reg_base + HC_RESPONSE_2);
cmd->resp_data[3] = mmio_read_32(reg_base + HC_RESPONSE_3);
if (mmio_read_32(reg_base + HC_COMMAND) & HC_CMD_FAILED) {
uint32_t sdhsts = mmio_read_32(reg_base + HC_HOSTSTATUS);
mmio_write_32(reg_base + HC_HOSTSTATUS,
HC_HSTST_MASK_ERROR_ALL);
if (!(sdhsts & HC_HSTST_ERROR_CRC7)
|| (cmd_idx != MMC_ACMD(51))) {
if (sdhsts & HC_HSTST_TIMEOUT_CMD) {
ERROR("rpi3_sdhost: timeout status 0x%x\n",
sdhsts);
err = -(ETIMEDOUT);
} else {
ERROR("rpi3_sdhost: unknown err, cmd = 0x%x\n",
mmio_read_32(reg_base + HC_COMMAND));
ERROR("rpi3_sdhost status: 0x%x\n", sdhsts);
err = -(EILSEQ);
}
}
}
if ((!err) && (cmd_idx == MMC_CMD(3))) {
/* we keep the RCA in case to send MMC_CMD(55) ourselves */
rpi3_sdhost_params.sdcard_rca = (cmd->resp_data[0]
& 0xFFFF0000U) >> 16;
}
return err;
}
static int rpi3_sdhost_set_clock(unsigned int clk)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
uint32_t max_clk = 250000000;
uint32_t div;
if (clk < 100000) {
mmio_write_32(reg_base + HC_CLOCKDIVISOR,
HC_CLOCKDIVISOR_MAXVAL);
return 0;
}
div = max_clk / clk;
if (div < 2)
div = 2;
if ((max_clk / div) > clk)
div++;
div -= 2;
if (div > HC_CLOCKDIVISOR_MAXVAL)
div = HC_CLOCKDIVISOR_MAXVAL;
rpi3_sdhost_params.clk_rate = max_clk / (div + 2);
rpi3_sdhost_params.ns_per_fifo_word = (1000000000 /
rpi3_sdhost_params.clk_rate)
* 8;
mmio_write_32(reg_base + HC_CLOCKDIVISOR, div);
return 0;
}
static int rpi3_sdhost_set_ios(unsigned int clk, unsigned int width)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
uint32_t tmp1;
rpi3_sdhost_set_clock(clk);
VERBOSE("rpi3_sdhost: Changing clock to %dHz for data mode\n", clk);
if (width != MMC_BUS_WIDTH_4 && width != MMC_BUS_WIDTH_1) {
ERROR("rpi3_sdhost: width %d not supported\n", width);
return -(EOPNOTSUPP);
}
rpi3_sdhost_params.bus_width = width;
tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
tmp1 &= ~(HC_HSTCF_EXTBUS_4BIT);
if (rpi3_sdhost_params.bus_width == MMC_BUS_WIDTH_4)
tmp1 |= HC_HSTCF_EXTBUS_4BIT;
mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1);
tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1 |
HC_HSTCF_SLOW_CARD | HC_HSTCF_INTBUS_WIDE);
return 0;
}
static int rpi3_sdhost_prepare(int lba, uintptr_t buf, size_t size)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
size_t blocks;
size_t blocksize;
if (size < 512) {
blocksize = size;
blocks = 1;
} else {
blocksize = 512;
blocks = size / blocksize;
if (size % blocksize != 0)
blocks++;
}
rpi3_drain_fifo();
mmio_write_32(reg_base + HC_BLOCKSIZE, blocksize);
mmio_write_32(reg_base + HC_BLOCKCOUNT, blocks);
udelay(100);
return 0;
}
static int rpi3_sdhost_read(int lba, uintptr_t buf, size_t size)
{
int err = 0;
uint32_t *buf1 = ((uint32_t *) buf);
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
int timeout = 100000;
int remaining_words = 0;
for (int i = 0; i < size / 4; i++) {
volatile int t = timeout;
uint32_t hsts_err;
while ((mmio_read_32(reg_base + HC_HOSTSTATUS)
& HC_HSTST_HAVEDATA) == 0) {
if (t == 0) {
ERROR("rpi3_sdhost: fifo timeout after %dus\n",
timeout);
err = -(ETIMEDOUT);
break;
}
t--;
udelay(10);
}
if (t == 0)
break;
uint32_t data = mmio_read_32(reg_base + HC_DATAPORT);
hsts_err = mmio_read_32(reg_base + HC_HOSTSTATUS)
& HC_HSTST_MASK_ERROR_ALL;
if (hsts_err) {
ERROR("rpi3_sdhost: transfer FIFO word %d: 0x%x\n",
i,
mmio_read_32(reg_base + HC_HOSTSTATUS));
rpi3_sdhost_print_regs();
err = -(EILSEQ);
/* clean the error status */
mmio_write_32(reg_base + HC_HOSTSTATUS, hsts_err);
}
if (buf1)
buf1[i] = data;
/* speeding up if the remaining words are still a lot */
remaining_words = (mmio_read_32(reg_base + HC_DEBUG) >> 4)
& HC_DBG_FIFO_THRESH_MASK;
if (remaining_words >= 7)
continue;
/* delay. slowing down the read process */
udelay(100);
}
/* We decide to stop by ourselves.
* It is because MMC_CMD(18) -> MMC_CMD(13) -> MMC_CMD(12)
* doesn't work for RPi3 SDHost.
*/
if (rpi3_sdhost_params.current_cmd == MMC_CMD(18))
send_command_decorated(MMC_CMD(12), 0);
if (err == -(EILSEQ)) {
const int max_retries = 20;
int r;
rpi3_sdhost_params.crc_err_retries++;
if (rpi3_sdhost_params.crc_err_retries < max_retries) {
/* retries if there's an CRC error */
r = rpi3_sdhost_prepare(lba, buf, size);
send_command_decorated(MMC_CMD(18), lba);
r = rpi3_sdhost_read(lba, buf, size);
if (r == 0)
err = 0;
}
}
return err;
}
static int rpi3_sdhost_write(int lba, uintptr_t buf, size_t size)
{
uint32_t *buf1 = ((uint32_t *) buf);
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
int err = 0;
int remaining_words = 0;
for (int i = 0; i < size / 4; i++) {
uint32_t hsts_err;
uint32_t data = buf1[i];
uint32_t dbg;
uint32_t fsm_state;
mmio_write_32(reg_base + HC_DATAPORT, data);
dbg = mmio_read_32(reg_base + HC_DEBUG);
fsm_state = dbg & HC_DBG_FSM_MASK;
if (fsm_state != HC_DBG_FSM_WRITEDATA
&& fsm_state != HC_DBG_FSM_WRITESTART1
&& fsm_state != HC_DBG_FSM_WRITESTART2
&& fsm_state != HC_DBG_FSM_WRITECRC
&& fsm_state != HC_DBG_FSM_WRITEWAIT1
&& fsm_state != HC_DBG_FSM_WRITEWAIT2) {
hsts_err = mmio_read_32(reg_base + HC_HOSTSTATUS)
& HC_HSTST_MASK_ERROR_ALL;
if (hsts_err)
err = -(EILSEQ);
}
/* speeding up if the remaining words are not many */
remaining_words = (mmio_read_32(reg_base + HC_DEBUG) >> 4)
& HC_DBG_FIFO_THRESH_MASK;
if (remaining_words <= 4)
continue;
udelay(100);
}
/* We decide to stop by ourselves.
* It is because MMC_CMD(25) -> MMC_CMD(13) -> MMC_CMD(12)
* doesn't work for RPi3 SDHost.
*/
if (rpi3_sdhost_params.current_cmd == MMC_CMD(25))
send_command_decorated(MMC_CMD(12), 0);
return err;
}
void rpi3_sdhost_init(struct rpi3_sdhost_params *params,
struct mmc_device_info *mmc_dev_info)
{
assert((params != 0) &&
((params->reg_base & MMC_BLOCK_MASK) == 0));
memcpy(&rpi3_sdhost_params, params, sizeof(struct rpi3_sdhost_params));
/* backup GPIO 48 to 53 configurations */
for (int i = 48; i <= 53; i++) {
rpi3_sdhost_params.gpio48_pinselect[i - 48]
= rpi3_gpio_get_select(i);
VERBOSE("rpi3_sdhost: Original GPIO state %d: %d\n",
i,
rpi3_sdhost_params.gpio48_pinselect[i - 48]);
}
/* setting pull resistors for 48 to 53.
* GPIO 48 (SD_CLK) to GPIO_PULL_UP
* GPIO 49 (SD_CMD) to GPIO_PULL_NONE
* GPIO 50 (SD_D0) to GPIO_PULL_NONE
* GPIO 51 (SD_D1) to GPIO_PULL_NONE
* GPIO 52 (SD_D2) to GPIO_PULL_NONE
* GPIO 53 (SD_D3) to GPIO_PULL_NONE
*/
gpio_set_pull(48, GPIO_PULL_UP);
for (int i = 49; i <= 53; i++)
gpio_set_pull(i, GPIO_PULL_NONE);
/* Set pin 48-53 to alt-0. It means route SDHOST to card slot */
for (int i = 48; i <= 53; i++)
rpi3_gpio_set_select(i, RPI3_GPIO_FUNC_ALT0);
mmc_init(&rpi3_sdhost_ops, params->clk_rate, params->bus_width,
params->flags, mmc_dev_info);
}
void rpi3_sdhost_stop(void)
{
uintptr_t reg_base = rpi3_sdhost_params.reg_base;
VERBOSE("rpi3_sdhost: Shutting down: drain FIFO out\n");
rpi3_drain_fifo();
VERBOSE("rpi3_sdhost: Shutting down: slowing down the clock\n");
mmio_write_32(reg_base+HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_SLOWVAL);
udelay(500);
VERBOSE("rpi3_sdhost: Shutting down: put SDHost into idle state\n");
send_command_decorated(MMC_CMD(0), 0);
udelay(500);
mmio_write_32(reg_base + HC_COMMAND, 0);
mmio_write_32(reg_base + HC_ARGUMENT, 0);
mmio_write_32(reg_base + HC_TIMEOUTCOUNTER, HC_TIMEOUT_IDLE);
mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_STOPVAL);
udelay(100);
mmio_write_32(reg_base + HC_POWER, 0);
mmio_write_32(reg_base + HC_HOSTCONFIG, 0);
mmio_write_32(reg_base + HC_BLOCKSIZE, 0x400);
mmio_write_32(reg_base + HC_BLOCKCOUNT, 0);
mmio_write_32(reg_base + HC_HOSTSTATUS, 0x7f8);
mmio_write_32(reg_base + HC_COMMAND, 0);
mmio_write_32(reg_base + HC_ARGUMENT, 0);
udelay(100);
/* Restore the pinmux to original state */
for (int i = 48; i <= 53; i++) {
rpi3_gpio_set_select(i,
rpi3_sdhost_params.gpio48_pinselect[i-48]);
}
/* Must reset the pull resistors for u-boot to work.
* GPIO 48 (SD_CLK) to GPIO_PULL_NONE
* GPIO 49 (SD_CMD) to GPIO_PULL_UP
* GPIO 50 (SD_D0) to GPIO_PULL_UP
* GPIO 51 (SD_D1) to GPIO_PULL_UP
* GPIO 52 (SD_D2) to GPIO_PULL_UP
* GPIO 53 (SD_D3) to GPIO_PULL_UP
*/
gpio_set_pull(48, GPIO_PULL_NONE);
for (int i = 49; i <= 53; i++)
gpio_set_pull(i, GPIO_PULL_UP);
}
/*
* Copyright (c) 2019, Linaro Limited
* Copyright (c) 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef RPI3_SDHOST_H
#define RPI3_SDHOST_H
#include <drivers/mmc.h>
#include <stdint.h>
#include <platform_def.h>
struct rpi3_sdhost_params {
uintptr_t reg_base;
uint32_t clk_rate;
uint32_t bus_width;
uint32_t flags;
uint32_t current_cmd;
uint8_t cmdbusy;
uint8_t mmc_app_cmd;
uint32_t ns_per_fifo_word;
uint32_t crc_err_retries;
uint32_t sdcard_rca;
uint32_t gpio48_pinselect[6];
};
void rpi3_sdhost_init(struct rpi3_sdhost_params *params,
struct mmc_device_info *mmc_dev_info);
void rpi3_sdhost_stop(void);
/* Registers */
#define HC_COMMAND 0x00 /* Command and flags */
#define HC_ARGUMENT 0x04
#define HC_TIMEOUTCOUNTER 0x08
#define HC_CLOCKDIVISOR 0x0c
#define HC_RESPONSE_0 0x10
#define HC_RESPONSE_1 0x14
#define HC_RESPONSE_2 0x18
#define HC_RESPONSE_3 0x1c
#define HC_HOSTSTATUS 0x20
#define HC_POWER 0x30
#define HC_DEBUG 0x34
#define HC_HOSTCONFIG 0x38
#define HC_BLOCKSIZE 0x3c
#define HC_DATAPORT 0x40
#define HC_BLOCKCOUNT 0x50
/* Flags for HC_COMMAND register */
#define HC_CMD_ENABLE 0x8000
#define HC_CMD_FAILED 0x4000
#define HC_CMD_BUSY 0x0800
#define HC_CMD_RESPONSE_NONE 0x0400
#define HC_CMD_RESPONSE_LONG 0x0200
#define HC_CMD_WRITE 0x0080
#define HC_CMD_READ 0x0040
#define HC_CMD_COMMAND_MASK 0x003f
#define HC_CLOCKDIVISOR_MAXVAL 0x07ff
#define HC_CLOCKDIVISOR_PREFERVAL 0x027b
#define HC_CLOCKDIVISOR_SLOWVAL 0x0148
#define HC_CLOCKDIVISOR_STOPVAL 0x01fb
/* Flags for HC_HOSTSTATUS register */
#define HC_HSTST_HAVEDATA 0x0001
#define HC_HSTST_ERROR_FIFO 0x0008
#define HC_HSTST_ERROR_CRC7 0x0010
#define HC_HSTST_ERROR_CRC16 0x0020
#define HC_HSTST_TIMEOUT_CMD 0x0040
#define HC_HSTST_TIMEOUT_DATA 0x0080
#define HC_HSTST_INT_BLOCK 0x0200
#define HC_HSTST_INT_BUSY 0x0400
#define HC_HSTST_RESET 0xffff
#define HC_HSTST_MASK_ERROR_DATA (HC_HSTST_ERROR_FIFO | \
HC_HSTST_ERROR_CRC7 | \
HC_HSTST_ERROR_CRC16 | \
HC_HSTST_TIMEOUT_DATA)
#define HC_HSTST_MASK_ERROR_ALL (HC_HSTST_MASK_ERROR_DATA | \
HC_HSTST_TIMEOUT_CMD)
/* Flags for HC_HOSTCONFIG register */
#define HC_HSTCF_INTBUS_WIDE 0x0002
#define HC_HSTCF_EXTBUS_4BIT 0x0004
#define HC_HSTCF_SLOW_CARD 0x0008
#define HC_HSTCF_INT_DATA 0x0010
#define HC_HSTCF_INT_BLOCK 0x0100
#define HC_HSTCF_INT_BUSY 0x0400
/* Flags for HC_DEBUG register */
#define HC_DBG_FIFO_THRESH_WRITE_SHIFT 9
#define HC_DBG_FIFO_THRESH_READ_SHIFT 14
#define HC_DBG_FIFO_THRESH_MASK 0x001f
#define HC_DBG_FSM_MASK 0xf
#define HC_DBG_FSM_IDENTMODE 0x0
#define HC_DBG_FSM_DATAMODE 0x1
#define HC_DBG_FSM_READDATA 0x2
#define HC_DBG_FSM_WRITEDATA 0x3
#define HC_DBG_FSM_READWAIT 0x4
#define HC_DBG_FSM_READCRC 0x5
#define HC_DBG_FSM_WRITECRC 0x6
#define HC_DBG_FSM_WRITEWAIT1 0x7
#define HC_DBG_FSM_POWERDOWN 0x8
#define HC_DBG_FSM_POWERUP 0x9
#define HC_DBG_FSM_WRITESTART1 0xa
#define HC_DBG_FSM_WRITESTART2 0xb
#define HC_DBG_FSM_GENPULSES 0xc
#define HC_DBG_FSM_WRITEWAIT2 0xd
#define HC_DBG_FSM_STARTPOWDOWN 0xf
#define HC_DBG_FORCE_DATA_MODE 0x40000
/* Settings */
#define HC_FIFO_SIZE 16
#define HC_FIFO_THRESH_READ 4
#define HC_FIFO_THRESH_WRITE 4
#define HC_TIMEOUT_DEFAULT 0x00f00000
#define HC_TIMEOUT_IDLE 0x00a00000
#endif /* RPI3_SDHOST_H */
......@@ -169,8 +169,12 @@ Raspberry Pi 3 platform port
----------------------------
:M: Antonio Niño Díaz <antonio.ninodiaz@arm.com>
:G: `antonio-nino-diaz-arm`_
:M: Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
:G: `grandpaul`_
:F: docs/plat/rpi3.rst
:F: plat/rpi3/
:F: drivers/rpi3/
:F: include/drivers/rpi3/
Renesas rcar-gen3 platform port
-------------------------------
......
......@@ -244,6 +244,8 @@
#define MAX_IO_DEVICES U(3)
#define MAX_IO_HANDLES U(4)
#define MAX_IO_BLOCK_DEVICES U(1)
/*
* Serial-related constants.
*/
......
......@@ -31,6 +31,9 @@ BL2_SOURCES += common/desc_image_load.c \
drivers/delay_timer/delay_timer.c \
drivers/delay_timer/generic_delay_timer.c \
drivers/rpi3/gpio/rpi3_gpio.c \
drivers/io/io_block.c \
drivers/mmc/mmc.c \
drivers/rpi3/sdhost/rpi3_sdhost.c \
plat/common/aarch64/platform_mp_stack.S \
plat/rpi3/aarch64/plat_helpers.S \
plat/rpi3/aarch64/rpi3_bl2_mem_params_desc.c \
......
......@@ -17,6 +17,7 @@
#include <lib/xlat_tables/xlat_tables_defs.h>
#include <drivers/generic_delay_timer.h>
#include <drivers/rpi3/gpio/rpi3_gpio.h>
#include <drivers/rpi3/sdhost/rpi3_sdhost.h>
#include "rpi3_private.h"
......@@ -34,6 +35,21 @@ static void rpi3_gpio_setup(void)
rpi3_gpio_init(&params);
}
/* Data structure which holds the MMC info */
static struct mmc_device_info mmc_info;
static void rpi3_sdhost_setup(void)
{
struct rpi3_sdhost_params params;
memset(&params, 0, sizeof(struct rpi3_sdhost_params));
params.reg_base = RPI3_SDHOST_BASE;
params.bus_width = MMC_BUS_WIDTH_4;
params.clk_rate = 392464;
mmc_info.mmc_dev_type = MMC_IS_SD_HC;
rpi3_sdhost_init(&params, &mmc_info);
}
/*******************************************************************************
* BL1 has passed the extents of the trusted SRAM that should be visible to BL2
* in x0. This memory layout is sitting at the base of the free trusted SRAM.
......@@ -57,6 +73,9 @@ void bl2_early_platform_setup2(u_register_t arg0, u_register_t arg1,
/* Setup the BL2 memory layout */
bl2_tzram_layout = *mem_layout;
/* Setup SDHost driver */
rpi3_sdhost_setup();
plat_rpi3_io_setup();
}
......@@ -122,6 +141,9 @@ int bl2_plat_handle_post_image_load(unsigned int image_id)
/* BL33 expects to receive the primary CPU MPID (through r0) */
bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr();
bl_mem_params->ep_info.spsr = rpi3_get_spsr_for_bl33_entry();
/* Shutting down the SDHost driver to let BL33 drives SDHost.*/
rpi3_sdhost_stop();
break;
default:
......
......@@ -89,6 +89,12 @@
#define RPI3_IO_GPIO_OFFSET ULL(0x00200000)
#define RPI3_GPIO_BASE (RPI3_IO_BASE + RPI3_IO_GPIO_OFFSET)
/*
* SDHost controller
*/
#define RPI3_IO_SDHOST_OFFSET ULL(0x00202000)
#define RPI3_SDHOST_BASE (RPI3_IO_BASE + RPI3_IO_SDHOST_OFFSET)
/*
* Local interrupt controller
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment