GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Linaro Limited
|
||||
* Copyright (c) 2019, Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <lib/mmio.h>
|
||||
#include <drivers/delay_timer.h>
|
||||
#include <drivers/rpi3/gpio/rpi3_gpio.h>
|
||||
#include <platform_def.h>
|
||||
|
||||
static uintptr_t reg_base;
|
||||
|
||||
static int rpi3_gpio_get_direction(int gpio);
|
||||
static void rpi3_gpio_set_direction(int gpio, int direction);
|
||||
static int rpi3_gpio_get_value(int gpio);
|
||||
static void rpi3_gpio_set_value(int gpio, int value);
|
||||
static void rpi3_gpio_set_pull(int gpio, int pull);
|
||||
|
||||
static const gpio_ops_t rpi3_gpio_ops = {
|
||||
.get_direction = rpi3_gpio_get_direction,
|
||||
.set_direction = rpi3_gpio_set_direction,
|
||||
.get_value = rpi3_gpio_get_value,
|
||||
.set_value = rpi3_gpio_set_value,
|
||||
.set_pull = rpi3_gpio_set_pull,
|
||||
};
|
||||
|
||||
/**
|
||||
* Get selection of GPIO pinmux settings.
|
||||
*
|
||||
* @param gpio The pin number of GPIO. From 0 to 53.
|
||||
* @return The selection of pinmux. RPI3_GPIO_FUNC_INPUT: input,
|
||||
* RPI3_GPIO_FUNC_OUTPUT: output,
|
||||
* RPI3_GPIO_FUNC_ALT0: alt-0,
|
||||
* RPI3_GPIO_FUNC_ALT1: alt-1,
|
||||
* RPI3_GPIO_FUNC_ALT2: alt-2,
|
||||
* RPI3_GPIO_FUNC_ALT3: alt-3,
|
||||
* RPI3_GPIO_FUNC_ALT4: alt-4,
|
||||
* RPI3_GPIO_FUNC_ALT5: alt-5
|
||||
*/
|
||||
int rpi3_gpio_get_select(int gpio)
|
||||
{
|
||||
int ret;
|
||||
int regN = gpio / 10;
|
||||
int shift = 3 * (gpio % 10);
|
||||
uintptr_t reg_sel = reg_base + RPI3_GPIO_GPFSEL(regN);
|
||||
uint32_t sel = mmio_read_32(reg_sel);
|
||||
|
||||
ret = (sel >> shift) & 0x07;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set selection of GPIO pinmux settings.
|
||||
*
|
||||
* @param gpio The pin number of GPIO. From 0 to 53.
|
||||
* @param fsel The selection of pinmux. RPI3_GPIO_FUNC_INPUT: input,
|
||||
* RPI3_GPIO_FUNC_OUTPUT: output,
|
||||
* RPI3_GPIO_FUNC_ALT0: alt-0,
|
||||
* RPI3_GPIO_FUNC_ALT1: alt-1,
|
||||
* RPI3_GPIO_FUNC_ALT2: alt-2,
|
||||
* RPI3_GPIO_FUNC_ALT3: alt-3,
|
||||
* RPI3_GPIO_FUNC_ALT4: alt-4,
|
||||
* RPI3_GPIO_FUNC_ALT5: alt-5
|
||||
*/
|
||||
void rpi3_gpio_set_select(int gpio, int fsel)
|
||||
{
|
||||
int regN = gpio / 10;
|
||||
int shift = 3 * (gpio % 10);
|
||||
uintptr_t reg_sel = reg_base + RPI3_GPIO_GPFSEL(regN);
|
||||
uint32_t sel = mmio_read_32(reg_sel);
|
||||
uint32_t mask = U(0x07) << shift;
|
||||
|
||||
sel = (sel & (~mask)) | ((fsel << shift) & mask);
|
||||
mmio_write_32(reg_sel, sel);
|
||||
}
|
||||
|
||||
static int rpi3_gpio_get_direction(int gpio)
|
||||
{
|
||||
int result = rpi3_gpio_get_select(gpio);
|
||||
|
||||
if (result == RPI3_GPIO_FUNC_INPUT)
|
||||
return GPIO_DIR_IN;
|
||||
else if (result == RPI3_GPIO_FUNC_OUTPUT)
|
||||
return GPIO_DIR_OUT;
|
||||
|
||||
return GPIO_DIR_IN;
|
||||
}
|
||||
|
||||
static void rpi3_gpio_set_direction(int gpio, int direction)
|
||||
{
|
||||
switch (direction) {
|
||||
case GPIO_DIR_IN:
|
||||
rpi3_gpio_set_select(gpio, RPI3_GPIO_FUNC_INPUT);
|
||||
break;
|
||||
case GPIO_DIR_OUT:
|
||||
rpi3_gpio_set_select(gpio, RPI3_GPIO_FUNC_OUTPUT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int rpi3_gpio_get_value(int gpio)
|
||||
{
|
||||
int regN = gpio / 32;
|
||||
int shift = gpio % 32;
|
||||
uintptr_t reg_lev = reg_base + RPI3_GPIO_GPLEV(regN);
|
||||
uint32_t value = mmio_read_32(reg_lev);
|
||||
|
||||
if ((value >> shift) & 0x01)
|
||||
return GPIO_LEVEL_HIGH;
|
||||
return GPIO_LEVEL_LOW;
|
||||
}
|
||||
|
||||
static void rpi3_gpio_set_value(int gpio, int value)
|
||||
{
|
||||
int regN = gpio / 32;
|
||||
int shift = gpio % 32;
|
||||
uintptr_t reg_set = reg_base + RPI3_GPIO_GPSET(regN);
|
||||
uintptr_t reg_clr = reg_base + RPI3_GPIO_GPSET(regN);
|
||||
|
||||
switch (value) {
|
||||
case GPIO_LEVEL_LOW:
|
||||
mmio_write_32(reg_clr, U(1) << shift);
|
||||
break;
|
||||
case GPIO_LEVEL_HIGH:
|
||||
mmio_write_32(reg_set, U(1) << shift);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rpi3_gpio_set_pull(int gpio, int pull)
|
||||
{
|
||||
int regN = gpio / 32;
|
||||
int shift = gpio % 32;
|
||||
uintptr_t reg_pud = reg_base + RPI3_GPIO_GPPUD;
|
||||
uintptr_t reg_clk = reg_base + RPI3_GPIO_GPPUDCLK(regN);
|
||||
|
||||
switch (pull) {
|
||||
case GPIO_PULL_NONE:
|
||||
mmio_write_32(reg_pud, 0x0);
|
||||
break;
|
||||
case GPIO_PULL_UP:
|
||||
mmio_write_32(reg_pud, 0x2);
|
||||
break;
|
||||
case GPIO_PULL_DOWN:
|
||||
mmio_write_32(reg_pud, 0x1);
|
||||
break;
|
||||
}
|
||||
mdelay(150);
|
||||
mmio_write_32(reg_clk, U(1) << shift);
|
||||
mdelay(150);
|
||||
mmio_write_32(reg_clk, 0x0);
|
||||
mmio_write_32(reg_pud, 0x0);
|
||||
}
|
||||
|
||||
void rpi3_gpio_init(void)
|
||||
{
|
||||
reg_base = RPI3_GPIO_BASE;
|
||||
gpio_init(&rpi3_gpio_ops);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
#include <arch_helpers.h>
|
||||
#include <common/debug.h>
|
||||
#include <lib/mmio.h>
|
||||
|
||||
#include <rpi_hw.h>
|
||||
|
||||
#include <drivers/rpi3/mailbox/rpi3_mbox.h>
|
||||
|
||||
#define RPI3_MAILBOX_MAX_RETRIES U(1000000)
|
||||
|
||||
/*******************************************************************************
|
||||
* Routine to send requests to the VideoCore using the mailboxes.
|
||||
******************************************************************************/
|
||||
void rpi3_vc_mailbox_request_send(rpi3_mbox_request_t *req, int req_size)
|
||||
{
|
||||
uint32_t st, data;
|
||||
uintptr_t resp_addr, addr;
|
||||
unsigned int retries;
|
||||
|
||||
/* This is the location of the request buffer */
|
||||
addr = (uintptr_t)req;
|
||||
|
||||
/* Make sure that the changes are seen by the VideoCore */
|
||||
flush_dcache_range(addr, req_size);
|
||||
|
||||
/* Wait until the outbound mailbox is empty */
|
||||
retries = 0U;
|
||||
|
||||
do {
|
||||
st = mmio_read_32(RPI3_MBOX_BASE + RPI3_MBOX1_STATUS_OFFSET);
|
||||
|
||||
retries++;
|
||||
if (retries == RPI3_MAILBOX_MAX_RETRIES) {
|
||||
ERROR("rpi3: mbox: Send request timeout\n");
|
||||
return;
|
||||
}
|
||||
|
||||
} while ((st & RPI3_MBOX_STATUS_EMPTY_MASK) == 0U);
|
||||
|
||||
/* Send base address of this message to start request */
|
||||
mmio_write_32(RPI3_MBOX_BASE + RPI3_MBOX1_WRITE_OFFSET,
|
||||
RPI3_CHANNEL_ARM_TO_VC | (uint32_t) addr);
|
||||
|
||||
/* Wait until the inbound mailbox isn't empty */
|
||||
retries = 0U;
|
||||
|
||||
do {
|
||||
st = mmio_read_32(RPI3_MBOX_BASE + RPI3_MBOX0_STATUS_OFFSET);
|
||||
|
||||
retries++;
|
||||
if (retries == RPI3_MAILBOX_MAX_RETRIES) {
|
||||
ERROR("rpi3: mbox: Receive response timeout\n");
|
||||
return;
|
||||
}
|
||||
|
||||
} while ((st & RPI3_MBOX_STATUS_EMPTY_MASK) != 0U);
|
||||
|
||||
/* Get location and channel */
|
||||
data = mmio_read_32(RPI3_MBOX_BASE + RPI3_MBOX0_READ_OFFSET);
|
||||
|
||||
if ((data & RPI3_CHANNEL_MASK) != RPI3_CHANNEL_ARM_TO_VC) {
|
||||
ERROR("rpi3: mbox: Wrong channel: 0x%08x\n", data);
|
||||
panic();
|
||||
}
|
||||
|
||||
resp_addr = (uintptr_t)(data & ~RPI3_CHANNEL_MASK);
|
||||
if (addr != resp_addr) {
|
||||
ERROR("rpi3: mbox: Unexpected address: 0x%08x\n", data);
|
||||
panic();
|
||||
}
|
||||
|
||||
/* Make sure that the data seen by the CPU is up to date */
|
||||
inv_dcache_range(addr, req_size);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <lib/mmio.h>
|
||||
|
||||
#include <rpi_hw.h>
|
||||
|
||||
/* Initial amount of values to discard */
|
||||
#define RNG_WARMUP_COUNT U(0x40000)
|
||||
|
||||
static void rpi3_rng_initialize(void)
|
||||
{
|
||||
uint32_t int_mask, ctrl;
|
||||
|
||||
/* Return if it is already enabled */
|
||||
ctrl = mmio_read_32(RPI3_RNG_BASE + RPI3_RNG_CTRL_OFFSET);
|
||||
if ((ctrl & RPI3_RNG_CTRL_ENABLE) != 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mask interrupts */
|
||||
int_mask = mmio_read_32(RPI3_RNG_BASE + RPI3_RNG_INT_MASK_OFFSET);
|
||||
int_mask |= RPI3_RNG_INT_MASK_DISABLE;
|
||||
mmio_write_32(RPI3_RNG_BASE + RPI3_RNG_INT_MASK_OFFSET, int_mask);
|
||||
|
||||
/* Discard several values when initializing to give it time to warmup */
|
||||
mmio_write_32(RPI3_RNG_BASE + RPI3_RNG_STATUS_OFFSET, RNG_WARMUP_COUNT);
|
||||
|
||||
mmio_write_32(RPI3_RNG_BASE + RPI3_RNG_CTRL_OFFSET,
|
||||
RPI3_RNG_CTRL_ENABLE);
|
||||
}
|
||||
|
||||
static uint32_t rpi3_rng_get_word(void)
|
||||
{
|
||||
size_t nwords;
|
||||
|
||||
do {
|
||||
/* Get number of available words to read */
|
||||
nwords = (mmio_read_32(RPI3_RNG_BASE + RPI3_RNG_STATUS_OFFSET)
|
||||
>> RPI3_RNG_STATUS_NUM_WORDS_SHIFT)
|
||||
& RPI3_RNG_STATUS_NUM_WORDS_MASK;
|
||||
} while (nwords == 0U);
|
||||
|
||||
return mmio_read_32(RPI3_RNG_BASE + RPI3_RNG_DATA_OFFSET);
|
||||
}
|
||||
|
||||
void rpi3_rng_read(void *buf, size_t len)
|
||||
{
|
||||
uint32_t data;
|
||||
size_t left = len;
|
||||
uint32_t *dst = buf;
|
||||
|
||||
assert(buf != NULL);
|
||||
assert(len != 0U);
|
||||
assert(check_uptr_overflow((uintptr_t) buf, (uintptr_t) len) == 0);
|
||||
|
||||
rpi3_rng_initialize();
|
||||
|
||||
while (left >= sizeof(uint32_t)) {
|
||||
data = rpi3_rng_get_word();
|
||||
*dst++ = data;
|
||||
left -= sizeof(uint32_t);
|
||||
}
|
||||
|
||||
if (left > 0U) {
|
||||
data = rpi3_rng_get_word();
|
||||
memcpy(dst, &data, left);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,679 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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 the command SEND_OP_COND returns with CRC7 error,
|
||||
* it can be considered as having completed successfully.
|
||||
*/
|
||||
if (!(sdhsts & HC_HSTST_ERROR_CRC7)
|
||||
|| (cmd_idx != MMC_CMD(1))) {
|
||||
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);
|
||||
|
||||
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.
|
||||
* It is debatable to set SD_CLK to UP or NONE. We massively
|
||||
* tested different brands of SD Cards and found NONE works
|
||||
* most stable.
|
||||
*
|
||||
* 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);
|
||||
|
||||
/* 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]);
|
||||
}
|
||||
|
||||
/* Reset the pull resistors before entering BL33.
|
||||
* GPIO 48 (SD_CLK) to GPIO_PULL_UP
|
||||
* 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
|
||||
*/
|
||||
for (int i = 48; i <= 53; i++)
|
||||
gpio_set_pull(i, GPIO_PULL_UP);
|
||||
}
|
||||
Reference in New Issue
Block a user