GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <drivers/delay_timer.h>
|
||||
|
||||
#include <chimp.h>
|
||||
#include <chimp_nv_defs.h>
|
||||
|
||||
#define CHIMP_DEFAULT_STARTUP_ADDR 0xb4300000
|
||||
|
||||
/* ChiMP's view of APE scratchpad memory for fastboot */
|
||||
#define CHIMP_FASTBOOT_ADDR 0x61000000
|
||||
|
||||
#define CHIMP_PREPARE_ACCESS_WINDOW(addr) \
|
||||
(\
|
||||
mmio_write_32(\
|
||||
NIC400_NITRO_CHIMP_S_IDM_IO_CONTROL_DIRECT, \
|
||||
addr & 0xffc00000)\
|
||||
)
|
||||
#define CHIMP_INDIRECT_TGT_ADDR(addr) \
|
||||
(CHIMP_INDIRECT_BASE + (addr & CHIMP_INDIRECT_ADDR_MASK))
|
||||
|
||||
#define CHIMP_CTRL_ADDR(x) (CHIMP_REG_CTRL_BASE + x)
|
||||
|
||||
/* For non-PAXC builds */
|
||||
#ifndef CHIMP_FB1_ENTRY
|
||||
#define CHIMP_FB1_ENTRY 0
|
||||
#endif
|
||||
|
||||
#define CHIMP_DBG VERBOSE
|
||||
|
||||
void bcm_chimp_write(uintptr_t addr, uint32_t value)
|
||||
{
|
||||
CHIMP_PREPARE_ACCESS_WINDOW(addr);
|
||||
mmio_write_32(CHIMP_INDIRECT_TGT_ADDR(addr), value);
|
||||
}
|
||||
|
||||
uint32_t bcm_chimp_read(uintptr_t addr)
|
||||
{
|
||||
CHIMP_PREPARE_ACCESS_WINDOW(addr);
|
||||
return mmio_read_32(CHIMP_INDIRECT_TGT_ADDR(addr));
|
||||
}
|
||||
|
||||
void bcm_chimp_clrbits(uintptr_t addr, uint32_t bits)
|
||||
{
|
||||
CHIMP_PREPARE_ACCESS_WINDOW(addr);
|
||||
mmio_clrbits_32(CHIMP_INDIRECT_TGT_ADDR(addr), bits);
|
||||
}
|
||||
|
||||
void bcm_chimp_setbits(uintptr_t addr, uint32_t bits)
|
||||
{
|
||||
CHIMP_PREPARE_ACCESS_WINDOW(addr);
|
||||
mmio_setbits_32(CHIMP_INDIRECT_TGT_ADDR(addr), bits);
|
||||
}
|
||||
|
||||
int bcm_chimp_is_nic_mode(void)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
/* Check if ChiMP straps are set */
|
||||
val = mmio_read_32(CDRU_CHIP_STRAP_DATA_LSW);
|
||||
val &= CDRU_CHIP_STRAP_DATA_LSW__NIC_MODE_MASK;
|
||||
|
||||
return val == CDRU_CHIP_STRAP_DATA_LSW__NIC_MODE_MASK;
|
||||
}
|
||||
|
||||
void bcm_chimp_fru_prog_done(bool is_done)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = is_done ? (1 << CHIMP_FRU_PROG_DONE_BIT) : 0;
|
||||
bcm_chimp_setbits(CHIMP_REG_ECO_RESERVED, val);
|
||||
}
|
||||
|
||||
int bcm_chimp_handshake_done(void)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
value = bcm_chimp_read(CHIMP_REG_ECO_RESERVED);
|
||||
value &= (1 << CHIMP_FLASH_ACCESS_DONE_BIT);
|
||||
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
int bcm_chimp_wait_handshake(void)
|
||||
{
|
||||
uint32_t timeout = CHIMP_HANDSHAKE_TIMEOUT_MS;
|
||||
uint32_t status;
|
||||
|
||||
INFO("Waiting for ChiMP handshake...\n");
|
||||
do {
|
||||
if (bcm_chimp_handshake_done())
|
||||
break;
|
||||
/* No need to wait if ChiMP reported an error */
|
||||
status = bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_STAT_REG);
|
||||
if (status & CHIMP_ERROR_MASK) {
|
||||
ERROR("ChiMP error 0x%x. Wait aborted\n", status);
|
||||
break;
|
||||
}
|
||||
mdelay(1);
|
||||
} while (--timeout);
|
||||
|
||||
if (!bcm_chimp_handshake_done()) {
|
||||
if (timeout == 0) {
|
||||
WARN("Timeout waiting for ChiMP handshake\n");
|
||||
}
|
||||
} else {
|
||||
INFO("Got handshake from ChiMP!\n");
|
||||
}
|
||||
|
||||
return bcm_chimp_handshake_done();
|
||||
}
|
||||
|
||||
uint32_t bcm_chimp_read_ctrl(uint32_t offset)
|
||||
{
|
||||
return bcm_chimp_read(CHIMP_CTRL_ADDR(offset));
|
||||
}
|
||||
|
||||
static int bcm_chimp_nitro_reset(void)
|
||||
{
|
||||
uint32_t timeout;
|
||||
|
||||
/* Perform tasks done by M0 in NIC mode */
|
||||
CHIMP_DBG("Taking Nitro out of reset\n");
|
||||
mmio_setbits_32(CDRU_MISC_RESET_CONTROL,
|
||||
/* MHB_RESET_N */
|
||||
(1 << CDRU_MISC_RESET_CONTROL__CDRU_MHB_RESET_N_R) |
|
||||
/* PCI_RESET_N */
|
||||
(1 << CDRU_MISC_RESET_CONTROL__CDRU_PCIE_RESET_N_R) |
|
||||
/* PM_RESET_N */
|
||||
(1 << CDRU_MISC_RESET_CONTROL__CDRU_PM_RESET_N_R) |
|
||||
/* NIC_RESET_N */
|
||||
(1 << CDRU_MISC_RESET_CONTROL__CDRU_NITRO_RESET_N_R)
|
||||
);
|
||||
|
||||
/* Wait until Nitro is out of reset */
|
||||
timeout = NIC_RESET_RELEASE_TIMEOUT_US;
|
||||
do {
|
||||
uint32_t value;
|
||||
|
||||
value = bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_MODE_REG);
|
||||
if ((value & CHIMP_BPE_MODE_ID_MASK) ==
|
||||
CHIMP_BPE_MODE_ID_PATTERN)
|
||||
break;
|
||||
udelay(1);
|
||||
} while (--timeout);
|
||||
|
||||
if (timeout == 0) {
|
||||
ERROR("NIC reset release timed out\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm_nitro_secure_mode_enable(void)
|
||||
{
|
||||
mmio_setbits_32(CDRU_NITRO_CONTROL,
|
||||
(1 << CDRU_NITRO_CONTROL__CDRU_NITRO_SEC_MODE_R) |
|
||||
(1 << CDRU_NITRO_CONTROL__CDRU_NITRO_SEC_OVERRIDE_R));
|
||||
mmio_write_32(NITRO_TZPC_TZPCDECPROT0clr,
|
||||
/* NITRO_TZPC */
|
||||
1 << NITRO_TZPC_TZPCDECPROT0clr__DECPROT0_chimp_m_clr_R);
|
||||
}
|
||||
|
||||
static int bcm_chimp_reset_and_initial_setup(void)
|
||||
{
|
||||
|
||||
int err;
|
||||
uint32_t handshake_reg;
|
||||
|
||||
err = bcm_chimp_nitro_reset();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Enable Nitro secure mode */
|
||||
bcm_nitro_secure_mode_enable();
|
||||
|
||||
/* Force ChiMP back into reset */
|
||||
bcm_chimp_setbits(CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_BPE_MODE_REG),
|
||||
1 << CHIMP_REG_CHIMP_REG_CTRL_BPE_MODE_REG__cm3_rst_R);
|
||||
|
||||
handshake_reg = (1 << SR_IN_SMARTNIC_MODE_BIT);
|
||||
|
||||
/* Get OTP secure Chimp boot status */
|
||||
if (mmio_read_32(CRMU_OTP_STATUS) & (1 << CRMU_OTP_STATUS_BIT))
|
||||
handshake_reg |= (1 << SR_CHIMP_SECURE_BOOT_BIT);
|
||||
|
||||
bcm_chimp_write(CHIMP_REG_ECO_RESERVED, handshake_reg);
|
||||
|
||||
CHIMP_DBG("ChiMP reset and initial handshake parameters set\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bcm_nitro_chimp_release_reset(void)
|
||||
{
|
||||
bcm_chimp_clrbits(CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_BPE_MODE_REG),
|
||||
1 << CHIMP_REG_CHIMP_REG_CTRL_BPE_MODE_REG__cm3_rst_R);
|
||||
|
||||
CHIMP_DBG("Nitro Reset Released\n");
|
||||
}
|
||||
|
||||
static void bcm_chimp_set_fastboot(int mode)
|
||||
{
|
||||
uint32_t fb_entry;
|
||||
|
||||
/* 1. Enable fastboot */
|
||||
bcm_chimp_setbits(CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_BPE_MODE_REG),
|
||||
(1 << CHIMP_FAST_BOOT_MODE_BIT));
|
||||
fb_entry = CHIMP_FASTBOOT_ADDR | mode;
|
||||
if (mode == CHIMP_FASTBOOT_JUMP_IN_PLACE)
|
||||
fb_entry = CHIMP_FB1_ENTRY;
|
||||
/* 2. Write startup address and mode */
|
||||
INFO("Setting fastboot type %d entry to 0x%x\n", mode, fb_entry);
|
||||
bcm_chimp_write(
|
||||
CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_FSTBOOT_PTR_REG),
|
||||
fb_entry);
|
||||
}
|
||||
|
||||
#ifndef CHIMPFW_USE_SIDELOAD
|
||||
static void bcm_chimp_load_fw_from_spi(uintptr_t spi_addr, size_t size)
|
||||
{
|
||||
uintptr_t ape_scpad;
|
||||
uintptr_t dest;
|
||||
size_t bytes_left;
|
||||
|
||||
ape_scpad = CHIMP_REG_CHIMP_APE_SCPAD;
|
||||
dest = CHIMP_INDIRECT_TGT_ADDR(CHIMP_REG_CHIMP_APE_SCPAD);
|
||||
bytes_left = size;
|
||||
|
||||
while (bytes_left) {
|
||||
uint32_t delta;
|
||||
|
||||
delta = bytes_left > CHIMP_WINDOW_SIZE ?
|
||||
bytes_left - CHIMP_WINDOW_SIZE : bytes_left;
|
||||
CHIMP_PREPARE_ACCESS_WINDOW(ape_scpad);
|
||||
INFO("Transferring %d byte(s) from 0x%lx to 0x%lx\n",
|
||||
delta, spi_addr, dest);
|
||||
/*
|
||||
* This single memcpy call takes significant amount of time
|
||||
* on Palladium. Be patient
|
||||
*/
|
||||
memcpy((void *)dest, (void *)spi_addr, delta);
|
||||
bytes_left -= delta;
|
||||
INFO("Transferred %d byte(s) from 0x%lx to 0x%lx (%lu%%)\n",
|
||||
delta, spi_addr, dest,
|
||||
((size - bytes_left) * 100)/size);
|
||||
spi_addr += delta;
|
||||
dest += delta;
|
||||
ape_scpad += delta;
|
||||
}
|
||||
}
|
||||
|
||||
static int bcm_chimp_find_fw_in_spi(uintptr_t *addr, size_t *size)
|
||||
{
|
||||
int i;
|
||||
bnxnvm_master_block_header_t *master_block_hdr;
|
||||
bnxnvm_directory_block_header_t *dir_block_hdr;
|
||||
bnxnvm_directory_entry_t *dir_entry;
|
||||
int found;
|
||||
|
||||
found = 0;
|
||||
|
||||
/* Read the master block */
|
||||
master_block_hdr =
|
||||
(bnxnvm_master_block_header_t *)(uintptr_t)QSPI_BASE_ADDR;
|
||||
if (master_block_hdr->sig != BNXNVM_MASTER_BLOCK_SIG) {
|
||||
WARN("Invalid masterblock 0x%x (expected 0x%x)\n",
|
||||
master_block_hdr->sig,
|
||||
BNXNVM_MASTER_BLOCK_SIG);
|
||||
return -NV_NOT_NVRAM;
|
||||
}
|
||||
if ((master_block_hdr->block_size > NV_MAX_BLOCK_SIZE) ||
|
||||
(master_block_hdr->directory_offset >=
|
||||
master_block_hdr->nvram_size)) {
|
||||
WARN("Invalid masterblock block size 0x%x or directory offset 0x%x\n",
|
||||
master_block_hdr->block_size,
|
||||
master_block_hdr->directory_offset);
|
||||
return -NV_BAD_MB;
|
||||
}
|
||||
|
||||
/* Skip to the Directory block start */
|
||||
dir_block_hdr =
|
||||
(bnxnvm_directory_block_header_t *)
|
||||
((uintptr_t)QSPI_BASE_ADDR +
|
||||
master_block_hdr->directory_offset);
|
||||
if (dir_block_hdr->sig != BNXNVM_DIRECTORY_BLOCK_SIG) {
|
||||
WARN("Invalid directory header 0x%x (expected 0x%x)\n",
|
||||
dir_block_hdr->sig,
|
||||
BNXNVM_DIRECTORY_BLOCK_SIG);
|
||||
return -NV_BAD_DIR_HEADER;
|
||||
}
|
||||
|
||||
/* Locate the firmware */
|
||||
for (i = 0; i < dir_block_hdr->entries; i++) {
|
||||
*addr = ((uintptr_t)dir_block_hdr + dir_block_hdr->length +
|
||||
i * dir_block_hdr->entry_length);
|
||||
dir_entry = (bnxnvm_directory_entry_t *)(*addr);
|
||||
if ((dir_entry->type == BNX_DIR_TYPE_BOOTCODE) ||
|
||||
(dir_entry->type == BNX_DIR_TYPE_BOOTCODE_2)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
return -NV_FW_NOT_FOUND;
|
||||
|
||||
*addr = QSPI_BASE_ADDR + dir_entry->item_location;
|
||||
*size = dir_entry->data_length;
|
||||
|
||||
INFO("Found chimp firmware at 0x%lx, size %lu byte(s)\n",
|
||||
*addr, *size);
|
||||
|
||||
return NV_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
int bcm_chimp_initiate_fastboot(int fastboot_type)
|
||||
{
|
||||
int err;
|
||||
|
||||
if ((fastboot_type != CHIMP_FASTBOOT_NITRO_RESET) &&
|
||||
(fastboot_type <= CHIMP_FASTBOOT_JUMP_DECOMPRESS)) {
|
||||
CHIMP_DBG("Initiating ChiMP fastboot type %d\n", fastboot_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are here, M0 did not setup Nitro because NIC mode
|
||||
* strap was not present
|
||||
*/
|
||||
err = bcm_chimp_reset_and_initial_setup();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (fastboot_type > CHIMP_FASTBOOT_JUMP_DECOMPRESS) {
|
||||
WARN("ChiMP setup deferred\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fastboot_type != CHIMP_FASTBOOT_NITRO_RESET) {
|
||||
|
||||
if ((fastboot_type == CHIMP_FASTBOOT_JUMP_IN_PLACE) &&
|
||||
(CHIMP_FB1_ENTRY == 0)) {
|
||||
ERROR("Missing ESAL entry point for fastboot type 1.\n"
|
||||
"Fastboot failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: We need to think of the way to load the ChiMP fw.
|
||||
* This could be SPI, NAND, etc.
|
||||
* For now we temporarily stick to the SPI load unless
|
||||
* CHIMPFW_USE_SIDELOAD is defined. Note that for the SPI NVRAM
|
||||
* image we need to parse directory and get the image.
|
||||
* When we load image from other media there is no need to
|
||||
* parse because fw image can be directly placed into the APE's
|
||||
* scratchpad.
|
||||
* For sideload method we simply reset the ChiMP, set bpe_reg
|
||||
* to do fastboot with the type we define, and release from
|
||||
* reset so that ROM loader would initiate fastboot immediately
|
||||
*/
|
||||
#ifndef CHIMPFW_USE_SIDELOAD
|
||||
{
|
||||
uintptr_t spi_addr;
|
||||
size_t size;
|
||||
|
||||
err = bcm_chimp_find_fw_in_spi(&spi_addr, &size);
|
||||
if (!err) {
|
||||
INFO("Loading ChiMP firmware, addr 0x%lx, size %lu byte(s)\n",
|
||||
spi_addr, size);
|
||||
bcm_chimp_load_fw_from_spi(spi_addr, size);
|
||||
} else {
|
||||
ERROR("Error %d ChiMP firmware not in NVRAM directory!\n",
|
||||
err);
|
||||
}
|
||||
}
|
||||
#else
|
||||
INFO("Skip ChiMP QSPI fastboot type %d due to sideload requested\n",
|
||||
fastboot_type);
|
||||
#endif
|
||||
if (!err) {
|
||||
INFO("Instruct ChiMP to fastboot\n");
|
||||
bcm_chimp_set_fastboot(fastboot_type);
|
||||
INFO("Fastboot mode set\n");
|
||||
}
|
||||
}
|
||||
|
||||
bcm_nitro_chimp_release_reset();
|
||||
|
||||
return err;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,842 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "bcm_emmc.h"
|
||||
#include "emmc_chal_types.h"
|
||||
#include "emmc_chal_sd.h"
|
||||
#include "emmc_csl_sdprot.h"
|
||||
#include "emmc_csl_sdcmd.h"
|
||||
#include "emmc_csl_sd.h"
|
||||
#include "emmc_chal_sd.h"
|
||||
#include "emmc_pboot_hal_memory_drv.h"
|
||||
|
||||
int sd_cmd0(struct sd_handle *handle)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument = 0x0; /* Go to IDLE state. */
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_GO_IDLE_STATE, argument, 0, NULL);
|
||||
|
||||
if (res == SD_OK) {
|
||||
/* Clear all other interrupts */
|
||||
chal_sd_clear_irq((void *)handle->device, 0xffffffff);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd1(struct sd_handle *handle, uint32_t ocr, uint32_t *ocr_output)
|
||||
{
|
||||
int res;
|
||||
uint32_t options;
|
||||
struct sd_resp resp;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R3_4 << SD_CMDR_RSP_TYPE_S;
|
||||
|
||||
if (ocr_output == NULL) {
|
||||
EMMC_TRACE("Invalid args\n");
|
||||
return SD_FAIL;
|
||||
}
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_SEND_OPCOND, ocr, options, &resp);
|
||||
|
||||
if (res == SD_OK)
|
||||
*ocr_output = resp.data.r3.ocr;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd2(struct sd_handle *handle)
|
||||
{
|
||||
uint32_t options;
|
||||
struct sd_resp resp;
|
||||
|
||||
/* send cmd and parse result */
|
||||
options = SD_CMDR_RSP_TYPE_R2 << SD_CMDR_RSP_TYPE_S;
|
||||
|
||||
return send_cmd(handle, SD_CMD_ALL_SEND_CID, 0, options, &resp);
|
||||
}
|
||||
|
||||
int sd_cmd3(struct sd_handle *handle)
|
||||
{
|
||||
int res;
|
||||
uint32_t options = 0;
|
||||
uint32_t argument;
|
||||
struct sd_resp resp;
|
||||
|
||||
/* use non zero and non 0x1 value for rca */
|
||||
handle->device->ctrl.rca = 0x5;
|
||||
argument = handle->device->ctrl.rca << SD_CMD7_ARG_RCA_SHIFT;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK;
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_MMC_SET_RCA, argument, options, &resp);
|
||||
|
||||
if (res != SD_OK)
|
||||
handle->device->ctrl.rca = 0;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd7(struct sd_handle *handle, uint32_t rca)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options;
|
||||
struct sd_resp resp;
|
||||
|
||||
argument = (rca << SD_CMD7_ARG_RCA_SHIFT);
|
||||
|
||||
/*
|
||||
* Response to CMD7 is:
|
||||
* R1 while selectiing from Stand-By State to Transfer State
|
||||
* R1b while selecting from Disconnected State to Programming State.
|
||||
*
|
||||
* In this driver, we only issue a CMD7 once, to go to transfer mode
|
||||
* during init_mmc_card().
|
||||
*/
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK;
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_SELECT_DESELECT_CARD, argument, options,
|
||||
&resp);
|
||||
|
||||
if (res == SD_OK)
|
||||
/* Clear all other interrupts */
|
||||
chal_sd_clear_irq((void *)handle->device, 0xffffffff);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CMD8 Get CSD_EXT
|
||||
*/
|
||||
int mmc_cmd8(struct sd_handle *handle, uint8_t *extCsdReg)
|
||||
{
|
||||
uint32_t res, options;
|
||||
struct sd_resp resp;
|
||||
|
||||
data_xfer_setup(handle, extCsdReg, CEATA_EXT_CSDBLOCK_SIZE,
|
||||
SD_XFER_CARD_TO_HOST);
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_DTDS_MASK |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK | SD4_EMMC_TOP_CMD_CRC_EN_MASK;
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_READ_EXT_CSD, 0, options, &resp);
|
||||
|
||||
if (res == SD_OK)
|
||||
res = process_data_xfer(handle, extCsdReg, 0,
|
||||
CEATA_EXT_CSDBLOCK_SIZE,
|
||||
SD_XFER_CARD_TO_HOST);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd9(struct sd_handle *handle, struct sd_card_data *card)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options, iBlkNum, multiFactor = 1;
|
||||
uint32_t maxReadBlockLen = 1, maxWriteBlockLen = 1;
|
||||
struct sd_resp resp;
|
||||
|
||||
argument = handle->device->ctrl.rca << SD_CMD7_ARG_RCA_SHIFT;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R2 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK;
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_SEND_CSD, argument, options, &resp);
|
||||
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
if (handle->card->type == SD_CARD_MMC) {
|
||||
card->csd.mmc.structure = (resp.data.r2.rsp4 >> 22) & 0x3;
|
||||
card->csd.mmc.csdSpecVer = (resp.data.r2.rsp4 >> 18) & 0x0f;
|
||||
card->csd.mmc.taac = (resp.data.r2.rsp4 >> 8) & 0xff;
|
||||
card->csd.mmc.nsac = resp.data.r2.rsp4 & 0xff;
|
||||
card->csd.mmc.speed = resp.data.r2.rsp3 >> 24;
|
||||
card->csd.mmc.classes = (resp.data.r2.rsp3 >> 12) & 0xfff;
|
||||
card->csd.mmc.rdBlkLen = (resp.data.r2.rsp3 >> 8) & 0xf;
|
||||
card->csd.mmc.rdBlkPartial = (resp.data.r2.rsp3 >> 7) & 0x01;
|
||||
card->csd.mmc.wrBlkMisalign = (resp.data.r2.rsp3 >> 6) & 0x1;
|
||||
card->csd.mmc.rdBlkMisalign = (resp.data.r2.rsp3 >> 5) & 0x1;
|
||||
card->csd.mmc.dsr = (resp.data.r2.rsp2 >> 4) & 0x01;
|
||||
card->csd.mmc.size =
|
||||
((resp.data.r2.rsp3 & 0x3) << 10) +
|
||||
((resp.data.r2.rsp2 >> 22) & 0x3ff);
|
||||
card->csd.mmc.vddRdCurrMin = (resp.data.r2.rsp2 >> 19) & 0x7;
|
||||
card->csd.mmc.vddRdCurrMax = (resp.data.r2.rsp2 >> 16) & 0x7;
|
||||
card->csd.mmc.vddWrCurrMin = (resp.data.r2.rsp2 >> 13) & 0x7;
|
||||
card->csd.mmc.vddWrCurrMax = (resp.data.r2.rsp2 >> 10) & 0x7;
|
||||
card->csd.mmc.devSizeMulti = (resp.data.r2.rsp2 >> 7) & 0x7;
|
||||
card->csd.mmc.eraseGrpSize = (resp.data.r2.rsp2 >> 2) & 0x1f;
|
||||
card->csd.mmc.eraseGrpSizeMulti =
|
||||
((resp.data.r2.rsp2 & 0x3) << 3) +
|
||||
((resp.data.r2.rsp1 >> 29) & 0x7);
|
||||
card->csd.mmc.wrProtGroupSize =
|
||||
((resp.data.r2.rsp1 >> 24) & 0x1f);
|
||||
card->csd.mmc.wrProtGroupEnable =
|
||||
(resp.data.r2.rsp1 >> 23) & 0x1;
|
||||
card->csd.mmc.manuDefEcc = (resp.data.r2.rsp1 >> 21) & 0x3;
|
||||
card->csd.mmc.wrSpeedFactor = (resp.data.r2.rsp1 >> 18) & 0x7;
|
||||
card->csd.mmc.wrBlkLen = (resp.data.r2.rsp1 >> 14) & 0xf;
|
||||
card->csd.mmc.wrBlkPartial = (resp.data.r2.rsp1 >> 13) & 0x1;
|
||||
card->csd.mmc.protAppl = (resp.data.r2.rsp1 >> 8) & 0x1;
|
||||
card->csd.mmc.copyFlag = (resp.data.r2.rsp1 >> 7) & 0x1;
|
||||
card->csd.mmc.permWrProt = (resp.data.r2.rsp1 >> 6) & 0x1;
|
||||
card->csd.mmc.tmpWrProt = (resp.data.r2.rsp1 >> 5) & 0x1;
|
||||
card->csd.mmc.fileFormat = (resp.data.r2.rsp1 >> 4) & 0x03;
|
||||
card->csd.mmc.eccCode = resp.data.r2.rsp1 & 0x03;
|
||||
maxReadBlockLen <<= card->csd.mmc.rdBlkLen;
|
||||
maxWriteBlockLen <<= card->csd.mmc.wrBlkLen;
|
||||
|
||||
iBlkNum = card->csd.mmc.size + 1;
|
||||
multiFactor = (1 << (card->csd.mmc.devSizeMulti + 2));
|
||||
|
||||
handle->card->size =
|
||||
iBlkNum * multiFactor * (1 << card->csd.mmc.rdBlkLen);
|
||||
}
|
||||
|
||||
handle->card->maxRdBlkLen = maxReadBlockLen;
|
||||
handle->card->maxWtBlkLen = maxWriteBlockLen;
|
||||
|
||||
if (handle->card->size < 0xA00000) {
|
||||
/*
|
||||
* 10MB Too small size mean, cmd9 response is wrong,
|
||||
* Use default value 1G
|
||||
*/
|
||||
handle->card->size = 0x40000000;
|
||||
handle->card->maxRdBlkLen = 512;
|
||||
handle->card->maxWtBlkLen = 512;
|
||||
}
|
||||
|
||||
if ((handle->card->maxRdBlkLen > 512) ||
|
||||
(handle->card->maxWtBlkLen > 512)) {
|
||||
handle->card->maxRdBlkLen = 512;
|
||||
handle->card->maxWtBlkLen = 512;
|
||||
} else if ((handle->card->maxRdBlkLen == 0) ||
|
||||
(handle->card->maxWtBlkLen == 0)) {
|
||||
handle->card->maxRdBlkLen = 512;
|
||||
handle->card->maxWtBlkLen = 512;
|
||||
}
|
||||
|
||||
handle->device->cfg.blockSize = handle->card->maxRdBlkLen;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd13(struct sd_handle *handle, uint32_t *status)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options;
|
||||
struct sd_resp resp;
|
||||
|
||||
argument = handle->device->ctrl.rca << SD_CMD7_ARG_RCA_SHIFT;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK;
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_SEND_STATUS, argument, options, &resp);
|
||||
|
||||
if (res == SD_OK) {
|
||||
*status = resp.cardStatus;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd16(struct sd_handle *handle, uint32_t length)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options, ntry;
|
||||
struct sd_resp resp;
|
||||
|
||||
argument = length;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK;
|
||||
|
||||
ntry = 0;
|
||||
do {
|
||||
res = sd_cmd13(handle, &resp.cardStatus);
|
||||
if (res != SD_OK) {
|
||||
EMMC_TRACE(
|
||||
"cmd13 failed before cmd16: rca 0x%0x, return %d, response 0x%0x\n",
|
||||
handle->device->ctrl.rca, res, resp.cardStatus);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (resp.cardStatus & 0x100)
|
||||
break;
|
||||
|
||||
EMMC_TRACE("cmd13 rsp:0x%08x before cmd16\n", resp.cardStatus);
|
||||
|
||||
if (ntry > handle->device->cfg.retryLimit) {
|
||||
EMMC_TRACE("cmd13 retry reach limit %d\n",
|
||||
handle->device->cfg.retryLimit);
|
||||
return SD_CMD_TIMEOUT;
|
||||
}
|
||||
|
||||
ntry++;
|
||||
EMMC_TRACE("cmd13 retry %d\n", ntry);
|
||||
|
||||
SD_US_DELAY(1000);
|
||||
|
||||
} while (1);
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_SET_BLOCKLEN, argument, options, &resp);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd17(struct sd_handle *handle,
|
||||
uint32_t addr, uint32_t len, uint8_t *buffer)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options, ntry;
|
||||
struct sd_resp resp;
|
||||
|
||||
ntry = 0;
|
||||
do {
|
||||
res = sd_cmd13(handle, &resp.cardStatus);
|
||||
if (res != SD_OK) {
|
||||
EMMC_TRACE(
|
||||
"cmd 13 failed before cmd17: rca 0x%0x, return %d, response 0x%0x\n",
|
||||
handle->device->ctrl.rca, res, resp.cardStatus);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (resp.cardStatus & 0x100)
|
||||
break;
|
||||
|
||||
EMMC_TRACE("cmd13 rsp:0x%08x before cmd17\n", resp.cardStatus);
|
||||
|
||||
if (ntry > handle->device->cfg.retryLimit) {
|
||||
EMMC_TRACE("cmd13 retry reach limit %d\n",
|
||||
handle->device->cfg.retryLimit);
|
||||
return SD_CMD_TIMEOUT;
|
||||
}
|
||||
|
||||
ntry++;
|
||||
EMMC_TRACE("cmd13 retry %d\n", ntry);
|
||||
|
||||
SD_US_DELAY(1000);
|
||||
|
||||
} while (1);
|
||||
|
||||
data_xfer_setup(handle, buffer, len, SD_XFER_CARD_TO_HOST);
|
||||
|
||||
/* send cmd and parse result */
|
||||
argument = addr;
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_DTDS_MASK |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK | SD4_EMMC_TOP_CMD_CCHK_EN_MASK;
|
||||
|
||||
res = send_cmd(handle, SD_CMD_READ_SINGLE_BLOCK, argument, options,
|
||||
&resp);
|
||||
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
res = process_data_xfer(handle, buffer, addr, len, SD_XFER_CARD_TO_HOST);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd18(struct sd_handle *handle,
|
||||
uint32_t addr, uint32_t len, uint8_t *buffer)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options, ntry;
|
||||
struct sd_resp resp;
|
||||
|
||||
ntry = 0;
|
||||
do {
|
||||
res = sd_cmd13(handle, &resp.cardStatus);
|
||||
if (res != SD_OK) {
|
||||
EMMC_TRACE(
|
||||
"cmd 13 failed before cmd18: rca 0x%0x, return %d, response 0x%0x\n",
|
||||
handle->device->ctrl.rca, res, resp.cardStatus);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (resp.cardStatus & 0x100)
|
||||
break;
|
||||
|
||||
EMMC_TRACE("cmd13 rsp:0x%08x before cmd18\n", resp.cardStatus);
|
||||
|
||||
if (ntry > handle->device->cfg.retryLimit) {
|
||||
EMMC_TRACE("cmd13 retry reach limit %d\n",
|
||||
handle->device->cfg.retryLimit);
|
||||
return SD_CMD_TIMEOUT;
|
||||
}
|
||||
|
||||
ntry++;
|
||||
EMMC_TRACE("cmd13 retry %d\n", ntry);
|
||||
|
||||
SD_US_DELAY(1000);
|
||||
} while (1);
|
||||
|
||||
data_xfer_setup(handle, buffer, len, SD_XFER_CARD_TO_HOST);
|
||||
|
||||
argument = addr;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_DTDS_MASK |
|
||||
SD4_EMMC_TOP_CMD_MSBS_MASK | SD4_EMMC_TOP_CMD_CCHK_EN_MASK |
|
||||
SD4_EMMC_TOP_CMD_BCEN_MASK | SD4_EMMC_TOP_CMD_CRC_EN_MASK |
|
||||
BIT(SD4_EMMC_TOP_CMD_ACMDEN_SHIFT);
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_READ_MULTIPLE_BLOCK, argument, options,
|
||||
&resp);
|
||||
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
res = process_data_xfer(handle, buffer, addr, len, SD_XFER_CARD_TO_HOST);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_ERASE_CODE
|
||||
static int card_sts_resp(struct sd_handle *handle, uint32_t *status)
|
||||
{
|
||||
int res;
|
||||
uint32_t ntry = 0;
|
||||
|
||||
do {
|
||||
res = sd_cmd13(handle, status);
|
||||
if (res != SD_OK) {
|
||||
EMMC_TRACE(
|
||||
"cmd 13 failed before cmd35: rca 0x%0x, return %d\n",
|
||||
handle->device->ctrl.rca, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (*status & 0x100)
|
||||
break;
|
||||
|
||||
EMMC_TRACE("cmd13 rsp:0x%08x before cmd35\n", resp.cardStatus);
|
||||
|
||||
if (ntry > handle->device->cfg.retryLimit) {
|
||||
EMMC_TRACE("cmd13 retry reach limit %d\n",
|
||||
handle->device->cfg.retryLimit);
|
||||
return SD_CMD_TIMEOUT;
|
||||
}
|
||||
|
||||
ntry++;
|
||||
EMMC_TRACE("cmd13 retry %d\n", ntry);
|
||||
|
||||
SD_US_DELAY(1000);
|
||||
} while (1);
|
||||
|
||||
return SD_OK;
|
||||
}
|
||||
|
||||
int sd_cmd35(struct sd_handle *handle, uint32_t start)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options;
|
||||
struct sd_resp resp;
|
||||
|
||||
res = card_sts_resp(handle, &resp.cardStatus);
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
argument = start;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK;
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_ERASE_GROUP_START,
|
||||
argument, options, &resp);
|
||||
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd36(struct sd_handle *handle, uint32_t end)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options;
|
||||
struct sd_resp resp;
|
||||
|
||||
res = card_sts_resp(handle, &resp.cardStatus);
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
argument = end;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK;
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_ERASE_GROUP_END,
|
||||
argument, options, &resp);
|
||||
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd38(struct sd_handle *handle)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options;
|
||||
struct sd_resp resp;
|
||||
|
||||
res = card_sts_resp(handle, &resp.cardStatus);
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
argument = 0;
|
||||
|
||||
options = (SD_CMDR_RSP_TYPE_R1b_5b << SD_CMDR_RSP_TYPE_S) |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK;
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_ERASE, argument, options, &resp);
|
||||
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
|
||||
|
||||
int sd_cmd24(struct sd_handle *handle,
|
||||
uint32_t addr, uint32_t len, uint8_t *buffer)
|
||||
{
|
||||
int res;
|
||||
uint32_t argument, options, ntry;
|
||||
struct sd_resp resp;
|
||||
|
||||
ntry = 0;
|
||||
do {
|
||||
res = sd_cmd13(handle, &resp.cardStatus);
|
||||
if (res != SD_OK) {
|
||||
EMMC_TRACE(
|
||||
"cmd 13 failed before cmd24: rca 0x%0x, return %d, response 0x%0x\n",
|
||||
handle->device->ctrl.rca, res, &resp.cardStatus);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (resp.cardStatus & 0x100)
|
||||
break;
|
||||
|
||||
EMMC_TRACE("cmd13 rsp:0x%08x before cmd24\n", resp.cardStatus);
|
||||
|
||||
if (ntry > handle->device->cfg.retryLimit) {
|
||||
EMMC_TRACE("cmd13 retry reach limit %d\n",
|
||||
handle->device->cfg.retryLimit);
|
||||
return SD_CMD_TIMEOUT;
|
||||
}
|
||||
|
||||
ntry++;
|
||||
EMMC_TRACE("cmd13 retry %d\n", ntry);
|
||||
|
||||
SD_US_DELAY(1000);
|
||||
|
||||
} while (1);
|
||||
|
||||
data_xfer_setup(handle, buffer, len, SD_XFER_HOST_TO_CARD);
|
||||
|
||||
argument = addr;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_CRC_EN_MASK |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK;
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_WRITE_BLOCK, argument, options, &resp);
|
||||
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
res = process_data_xfer(handle, buffer, addr, len, SD_XFER_HOST_TO_CARD);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sd_cmd25(struct sd_handle *handle,
|
||||
uint32_t addr, uint32_t len, uint8_t *buffer)
|
||||
{
|
||||
int res = SD_OK;
|
||||
uint32_t argument, options, ntry;
|
||||
struct sd_resp resp;
|
||||
|
||||
ntry = 0;
|
||||
do {
|
||||
res = sd_cmd13(handle, &resp.cardStatus);
|
||||
if (res != SD_OK) {
|
||||
EMMC_TRACE(
|
||||
"cmd 13 failed before cmd25: rca 0x%0x, return %d, response 0x%0x\n",
|
||||
handle->device->ctrl.rca, res, &resp.cardStatus);
|
||||
return res;
|
||||
}
|
||||
|
||||
if (resp.cardStatus & 0x100)
|
||||
break;
|
||||
|
||||
EMMC_TRACE("cmd13 rsp:0x%08x before cmd25\n", resp.cardStatus);
|
||||
|
||||
if (ntry > handle->device->cfg.retryLimit) {
|
||||
EMMC_TRACE("cmd13 retry reach limit %d\n",
|
||||
handle->device->cfg.retryLimit);
|
||||
return SD_CMD_TIMEOUT;
|
||||
}
|
||||
|
||||
ntry++;
|
||||
EMMC_TRACE("cmd13 retry %d\n", ntry);
|
||||
|
||||
SD_US_DELAY(1000);
|
||||
} while (1);
|
||||
|
||||
data_xfer_setup(handle, buffer, len, SD_XFER_HOST_TO_CARD);
|
||||
|
||||
argument = addr;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1_5_6 << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_DPS_MASK | SD4_EMMC_TOP_CMD_MSBS_MASK |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK | SD4_EMMC_TOP_CMD_BCEN_MASK |
|
||||
SD4_EMMC_TOP_CMD_CRC_EN_MASK |
|
||||
BIT(SD4_EMMC_TOP_CMD_ACMDEN_SHIFT);
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_CMD_WRITE_MULTIPLE_BLOCK,
|
||||
argument, options, &resp);
|
||||
|
||||
if (res != SD_OK)
|
||||
return res;
|
||||
|
||||
res = process_data_xfer(handle, buffer, addr, len, SD_XFER_HOST_TO_CARD);
|
||||
|
||||
return res;
|
||||
}
|
||||
#endif /* INCLUDE_EMMC_DRIVER_WRITE_CODE */
|
||||
|
||||
int mmc_cmd6(struct sd_handle *handle, uint32_t argument)
|
||||
{
|
||||
int res;
|
||||
uint32_t options;
|
||||
struct sd_resp resp;
|
||||
|
||||
options = SD_CMDR_RSP_TYPE_R1b_5b << SD_CMDR_RSP_TYPE_S |
|
||||
SD4_EMMC_TOP_CMD_CCHK_EN_MASK | SD4_EMMC_TOP_CMD_CRC_EN_MASK;
|
||||
|
||||
EMMC_TRACE("Sending CMD6 with argument 0x%X\n", argument);
|
||||
|
||||
/* send cmd and parse result */
|
||||
res = send_cmd(handle, SD_ACMD_SET_BUS_WIDTH, argument, options, &resp);
|
||||
|
||||
/*
|
||||
* For R1b type response:
|
||||
* controller issues a COMMAND COMPLETE interrupt when the R1
|
||||
* response is received,
|
||||
* then controller monitors DAT0 for busy status,
|
||||
* controller issues a TRANSFER COMPLETE interrupt when busy signal
|
||||
* clears.
|
||||
*/
|
||||
wait_for_event(handle,
|
||||
SD4_EMMC_TOP_INTR_TXDONE_MASK | SD_ERR_INTERRUPTS,
|
||||
handle->device->cfg.wfe_retry);
|
||||
|
||||
if (res == SD_OK) {
|
||||
/* Check result of Cmd6 using Cmd13 to check card status */
|
||||
|
||||
/* Check status using Cmd13 */
|
||||
res = sd_cmd13(handle, &resp.cardStatus);
|
||||
|
||||
if (res == SD_OK) {
|
||||
/* Check bit 7 (SWITCH_ERROR) in card status */
|
||||
if ((resp.cardStatus & 0x80) != 0) {
|
||||
EMMC_TRACE("cmd6 failed: SWITCH_ERROR\n");
|
||||
res = SD_FAIL;
|
||||
}
|
||||
} else {
|
||||
EMMC_TRACE("cmd13 failed after cmd6: ");
|
||||
EMMC_TRACE("rca 0x%0x, return %d, response 0x%0x\n",
|
||||
handle->device->ctrl.rca, res, resp.cardStatus);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
#define SD_BUSY_CHECK 0x00203000
|
||||
#define DAT0_LEVEL_MASK 0x100000 /* bit20 in PSTATE */
|
||||
#define DEV_BUSY_TIMEOUT 600000 /* 60 Sec : 600000 * 100us */
|
||||
|
||||
int send_cmd(struct sd_handle *handle, uint32_t cmdIndex, uint32_t argument,
|
||||
uint32_t options, struct sd_resp *resp)
|
||||
{
|
||||
int status = SD_OK;
|
||||
uint32_t event = 0, present, timeout = 0, retry = 0, mask = 3;
|
||||
uint32_t temp_resp[4];
|
||||
|
||||
if (handle == NULL) {
|
||||
EMMC_TRACE("Invalid handle for cmd%d\n", cmdIndex);
|
||||
return SD_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
mask = (SD_BUSY_CHECK & options) ? 3 : 1;
|
||||
|
||||
RETRY_WRITE_CMD:
|
||||
do {
|
||||
/* Make sure it is ok to send command */
|
||||
present =
|
||||
chal_sd_get_present_status((CHAL_HANDLE *) handle->device);
|
||||
timeout++;
|
||||
|
||||
if (present & mask)
|
||||
SD_US_DELAY(1000);
|
||||
else
|
||||
break;
|
||||
|
||||
} while (timeout < EMMC_BUSY_CMD_TIMEOUT_MS);
|
||||
|
||||
if (timeout >= EMMC_BUSY_CMD_TIMEOUT_MS) {
|
||||
status = SD_CMD_MISSING;
|
||||
EMMC_TRACE("cmd%d timedout %dms\n", cmdIndex, timeout);
|
||||
}
|
||||
|
||||
/* Reset both DAT and CMD line if only of them are stuck */
|
||||
if (present & mask)
|
||||
check_error(handle, SD4_EMMC_TOP_INTR_CMDERROR_MASK);
|
||||
|
||||
handle->device->ctrl.argReg = argument;
|
||||
chal_sd_send_cmd((CHAL_HANDLE *) handle->device, cmdIndex,
|
||||
handle->device->ctrl.argReg, options);
|
||||
|
||||
handle->device->ctrl.cmdIndex = cmdIndex;
|
||||
|
||||
event = wait_for_event(handle,
|
||||
(SD4_EMMC_TOP_INTR_CMDDONE_MASK |
|
||||
SD_ERR_INTERRUPTS),
|
||||
handle->device->cfg.wfe_retry);
|
||||
|
||||
if (handle->device->ctrl.cmdStatus == SD_CMD_MISSING) {
|
||||
retry++;
|
||||
|
||||
if (retry >= handle->device->cfg.retryLimit) {
|
||||
status = SD_CMD_MISSING;
|
||||
EMMC_TRACE("cmd%d retry reaches the limit %d\n",
|
||||
cmdIndex, retry);
|
||||
} else {
|
||||
/* reset both DAT & CMD line if one of them is stuck */
|
||||
present = chal_sd_get_present_status((CHAL_HANDLE *)
|
||||
handle->device);
|
||||
|
||||
if (present & mask)
|
||||
check_error(handle,
|
||||
SD4_EMMC_TOP_INTR_CMDERROR_MASK);
|
||||
|
||||
EMMC_TRACE("cmd%d retry %d PSTATE[0x%08x]\n",
|
||||
cmdIndex, retry,
|
||||
chal_sd_get_present_status((CHAL_HANDLE *)
|
||||
handle->device));
|
||||
goto RETRY_WRITE_CMD;
|
||||
}
|
||||
}
|
||||
|
||||
if (handle->device->ctrl.cmdStatus == SD_OK) {
|
||||
if (resp != NULL) {
|
||||
status =
|
||||
chal_sd_get_response((CHAL_HANDLE *) handle->device,
|
||||
temp_resp);
|
||||
process_cmd_response(handle,
|
||||
handle->device->ctrl.cmdIndex,
|
||||
temp_resp[0], temp_resp[1],
|
||||
temp_resp[2], temp_resp[3], resp);
|
||||
}
|
||||
|
||||
/* Check Device busy after CMD */
|
||||
if ((cmdIndex == 5) || (cmdIndex == 6) || (cmdIndex == 7) ||
|
||||
(cmdIndex == 28) || (cmdIndex == 29) || (cmdIndex == 38)) {
|
||||
|
||||
timeout = 0;
|
||||
do {
|
||||
present =
|
||||
chal_sd_get_present_status((CHAL_HANDLE *)
|
||||
handle->device);
|
||||
|
||||
timeout++;
|
||||
|
||||
/* Dat[0]:bit20 low means device busy */
|
||||
if ((present & DAT0_LEVEL_MASK) == 0) {
|
||||
EMMC_TRACE("Device busy: ");
|
||||
EMMC_TRACE(
|
||||
"cmd%d arg:0x%08x: PSTATE[0x%08x]\n",
|
||||
cmdIndex, argument, present);
|
||||
SD_US_DELAY(100);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (timeout < DEV_BUSY_TIMEOUT);
|
||||
}
|
||||
} else if (handle->device->ctrl.cmdStatus &&
|
||||
handle->device->ctrl.cmdStatus != SD_CMD_MISSING) {
|
||||
retry++;
|
||||
status = check_error(handle, handle->device->ctrl.cmdStatus);
|
||||
|
||||
EMMC_TRACE(
|
||||
"cmd%d error: cmdStatus:0x%08x error_status:0x%08x\n",
|
||||
cmdIndex, handle->device->ctrl.cmdStatus, status);
|
||||
|
||||
if ((handle->device->ctrl.cmdIndex == 1) ||
|
||||
(handle->device->ctrl.cmdIndex == 5)) {
|
||||
status = event;
|
||||
} else if ((handle->device->ctrl.cmdIndex == 7) ||
|
||||
(handle->device->ctrl.cmdIndex == 41)) {
|
||||
status = event;
|
||||
} else if ((status == SD_ERROR_RECOVERABLE) &&
|
||||
(retry < handle->device->cfg.retryLimit)) {
|
||||
EMMC_TRACE("cmd%d recoverable error ", cmdIndex);
|
||||
EMMC_TRACE("retry %d PSTATE[0x%08x].\n", retry,
|
||||
chal_sd_get_present_status((CHAL_HANDLE *)
|
||||
handle->device));
|
||||
goto RETRY_WRITE_CMD;
|
||||
} else {
|
||||
EMMC_TRACE("cmd%d retry reaches the limit %d\n",
|
||||
cmdIndex, retry);
|
||||
status = event;
|
||||
}
|
||||
}
|
||||
|
||||
handle->device->ctrl.blkReg = 0;
|
||||
/* clear error status for next command */
|
||||
handle->device->ctrl.cmdStatus = 0;
|
||||
|
||||
return status;
|
||||
}
|
||||
+621
@@ -0,0 +1,621 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <emmc_api.h>
|
||||
#include <cmn_plat_util.h>
|
||||
|
||||
#define MAX_CMD_RETRY 10
|
||||
|
||||
#if EMMC_USE_DMA
|
||||
#define USE_DMA 1
|
||||
#else
|
||||
#define USE_DMA 0
|
||||
#endif
|
||||
|
||||
struct emmc_global_buffer emmc_global_buf;
|
||||
struct emmc_global_buffer *emmc_global_buf_ptr = &emmc_global_buf;
|
||||
|
||||
struct emmc_global_vars emmc_global_vars;
|
||||
struct emmc_global_vars *emmc_global_vars_ptr = &emmc_global_vars;
|
||||
|
||||
static struct sd_handle *sdio_gethandle(void);
|
||||
static uint32_t sdio_idle(struct sd_handle *p_sdhandle);
|
||||
|
||||
static uint32_t sdio_read(struct sd_handle *p_sdhandle,
|
||||
uintptr_t mem_addr,
|
||||
uintptr_t storage_addr,
|
||||
size_t storage_size,
|
||||
size_t bytes_to_read);
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
|
||||
static uint32_t sdio_write(struct sd_handle *p_sdhandle,
|
||||
uintptr_t mem_addr,
|
||||
uintptr_t data_addr,
|
||||
size_t bytes_to_write);
|
||||
#endif
|
||||
|
||||
static struct sd_handle *sdio_init(void);
|
||||
static int32_t bcm_emmc_card_ready_state(struct sd_handle *p_sdhandle);
|
||||
|
||||
static void init_globals(void)
|
||||
{
|
||||
memset((void *)emmc_global_buf_ptr, 0, sizeof(*emmc_global_buf_ptr));
|
||||
memset((void *)emmc_global_vars_ptr, 0, sizeof(*emmc_global_vars_ptr));
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is used to change partition
|
||||
*/
|
||||
uint32_t emmc_partition_select(uint32_t partition)
|
||||
{
|
||||
int rc;
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
EMMC_TRACE("eMMC init is not done");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (partition) {
|
||||
case EMMC_BOOT_PARTITION1:
|
||||
rc = set_boot_config(sd_handle,
|
||||
SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_BOOT1);
|
||||
EMMC_TRACE(
|
||||
"Change to Boot Partition 1 result:%d (0 means SD_OK)\n",
|
||||
rc);
|
||||
break;
|
||||
|
||||
case EMMC_BOOT_PARTITION2:
|
||||
rc = set_boot_config(sd_handle,
|
||||
SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_BOOT2);
|
||||
EMMC_TRACE(
|
||||
"Change to Boot Partition 2 result:%d (0 means SD_OK)\n",
|
||||
rc);
|
||||
break;
|
||||
|
||||
case EMMC_USE_CURRENT_PARTITION:
|
||||
rc = SD_OK;
|
||||
EMMC_TRACE("Stay on current partition");
|
||||
break;
|
||||
|
||||
case EMMC_USER_AREA:
|
||||
default:
|
||||
rc = set_boot_config(sd_handle,
|
||||
SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_USER);
|
||||
EMMC_TRACE("Change to User area result:%d (0 means SD_OK)\n",
|
||||
rc);
|
||||
break;
|
||||
|
||||
}
|
||||
return (rc == SD_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize emmc controller for eMMC
|
||||
* Returns 0 on fail condition
|
||||
*/
|
||||
uint32_t bcm_emmc_init(bool card_rdy_only)
|
||||
{
|
||||
struct sd_handle *p_sdhandle;
|
||||
uint32_t result = 0;
|
||||
|
||||
EMMC_TRACE("Enter emmc_controller_init()\n");
|
||||
|
||||
/* If eMMC is already initialized, skip init */
|
||||
if (emmc_global_vars_ptr->init_done)
|
||||
return 1;
|
||||
|
||||
init_globals();
|
||||
|
||||
p_sdhandle = sdio_init();
|
||||
|
||||
if (p_sdhandle == NULL) {
|
||||
ERROR("eMMC init failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (card_rdy_only) {
|
||||
/* Put the card in Ready state, Not complete init */
|
||||
result = bcm_emmc_card_ready_state(p_sdhandle);
|
||||
return !result;
|
||||
}
|
||||
|
||||
if (sdio_idle(p_sdhandle) == EMMC_BOOT_OK) {
|
||||
set_config(p_sdhandle, SD_NORMAL_SPEED, MAX_CMD_RETRY, USE_DMA,
|
||||
SD_DMA_BOUNDARY_256K, EMMC_BLOCK_SIZE,
|
||||
EMMC_WFE_RETRY);
|
||||
|
||||
if (!select_blk_sz(p_sdhandle,
|
||||
p_sdhandle->device->cfg.blockSize)) {
|
||||
emmc_global_vars_ptr->init_done = 1;
|
||||
result = 1;
|
||||
} else {
|
||||
ERROR("Select Block Size failed\n");
|
||||
}
|
||||
} else {
|
||||
ERROR("eMMC init failed");
|
||||
}
|
||||
|
||||
/* Initialization is failed, so deinit HW setting */
|
||||
if (result == 0)
|
||||
emmc_deinit();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to de-init SDIO controller for eMMC
|
||||
*/
|
||||
void emmc_deinit(void)
|
||||
{
|
||||
emmc_global_vars_ptr->init_done = 0;
|
||||
emmc_global_vars_ptr->sdHandle.card = 0;
|
||||
emmc_global_vars_ptr->sdHandle.device = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read eMMC memory
|
||||
* Returns read_size
|
||||
*/
|
||||
uint32_t emmc_read(uintptr_t mem_addr, uintptr_t storage_addr,
|
||||
size_t storage_size, size_t bytes_to_read)
|
||||
{
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
EMMC_TRACE("eMMC init is not done");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sdio_read(sdio_gethandle(), mem_addr, storage_addr,
|
||||
storage_size, bytes_to_read);
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_ERASE_CODE
|
||||
#define EXT_CSD_ERASE_GRP_SIZE 224
|
||||
|
||||
static int emmc_block_erase(uintptr_t mem_addr, size_t blocks)
|
||||
{
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
ERROR("eMMC init is not done");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return erase_card(sdio_gethandle(), mem_addr, blocks);
|
||||
}
|
||||
|
||||
int emmc_erase(uintptr_t mem_addr, size_t num_of_blocks, uint32_t partition)
|
||||
{
|
||||
int err = 0;
|
||||
size_t block_count = 0, blocks = 0;
|
||||
size_t erase_group = 0;
|
||||
|
||||
erase_group =
|
||||
emmc_global_buf_ptr->u.Ext_CSD_storage[EXT_CSD_ERASE_GRP_SIZE]*1024;
|
||||
|
||||
INFO("eMMC Erase Group Size=0x%lx\n", erase_group);
|
||||
|
||||
emmc_partition_select(partition);
|
||||
|
||||
while (block_count < num_of_blocks) {
|
||||
blocks = ((num_of_blocks - block_count) > erase_group) ?
|
||||
erase_group : (num_of_blocks - block_count);
|
||||
err = emmc_block_erase(mem_addr + block_count, blocks);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
block_count += blocks;
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
INFO("eMMC Erase of partition %d successful\n", partition);
|
||||
else
|
||||
ERROR("eMMC Erase of partition %d Failed(%i)\n", partition, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
|
||||
/*
|
||||
* Write to eMMC memory
|
||||
* Returns written_size
|
||||
*/
|
||||
uint32_t emmc_write(uintptr_t mem_addr, uintptr_t data_addr,
|
||||
size_t bytes_to_write)
|
||||
{
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
EMMC_TRACE("eMMC init is not done");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sdio_write(sd_handle, mem_addr, data_addr, bytes_to_write);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Send SDIO Cmd
|
||||
* Return 0 for pass condition
|
||||
*/
|
||||
uint32_t send_sdio_cmd(uint32_t cmdIndex, uint32_t argument,
|
||||
uint32_t options, struct sd_resp *resp)
|
||||
{
|
||||
struct sd_handle *sd_handle = sdio_gethandle();
|
||||
|
||||
if (sd_handle->device == 0) {
|
||||
EMMC_TRACE("eMMC init is not done");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return send_cmd(sd_handle, cmdIndex, argument, options, resp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function return SDIO handle
|
||||
*/
|
||||
struct sd_handle *sdio_gethandle(void)
|
||||
{
|
||||
return &emmc_global_vars_ptr->sdHandle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize SDIO controller
|
||||
*/
|
||||
struct sd_handle *sdio_init(void)
|
||||
{
|
||||
uint32_t SDIO_base;
|
||||
struct sd_handle *p_sdhandle = &emmc_global_vars_ptr->sdHandle;
|
||||
|
||||
SDIO_base = EMMC_CTRL_REGS_BASE_ADDR;
|
||||
|
||||
if (SDIO_base == SDIO0_EMMCSDXC_SYSADDR)
|
||||
EMMC_TRACE(" ---> for SDIO 0 Controller\n\n");
|
||||
|
||||
memset(p_sdhandle, 0, sizeof(struct sd_handle));
|
||||
|
||||
p_sdhandle->device = &emmc_global_vars_ptr->sdDevice;
|
||||
p_sdhandle->card = &emmc_global_vars_ptr->sdCard;
|
||||
|
||||
memset(p_sdhandle->device, 0, sizeof(struct sd_dev));
|
||||
memset(p_sdhandle->card, 0, sizeof(struct sd_card_info));
|
||||
|
||||
if (chal_sd_start((CHAL_HANDLE *) p_sdhandle->device,
|
||||
SD_PIO_MODE, SDIO_base, SDIO_base) != SD_OK)
|
||||
return NULL;
|
||||
|
||||
set_config(p_sdhandle, SD_NORMAL_SPEED, MAX_CMD_RETRY, SD_DMA_OFF,
|
||||
SD_DMA_BOUNDARY_4K, EMMC_BLOCK_SIZE, EMMC_WFE_RETRY);
|
||||
|
||||
return &emmc_global_vars_ptr->sdHandle;
|
||||
}
|
||||
|
||||
uint32_t sdio_idle(struct sd_handle *p_sdhandle)
|
||||
{
|
||||
reset_card(p_sdhandle);
|
||||
|
||||
SD_US_DELAY(1000);
|
||||
|
||||
if (init_card(p_sdhandle, SD_CARD_DETECT_MMC) != SD_OK) {
|
||||
reset_card(p_sdhandle);
|
||||
reset_host_ctrl(p_sdhandle);
|
||||
return EMMC_BOOT_NO_CARD;
|
||||
}
|
||||
|
||||
return EMMC_BOOT_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function read eMMC
|
||||
*/
|
||||
uint32_t sdio_read(struct sd_handle *p_sdhandle,
|
||||
uintptr_t mem_addr,
|
||||
uintptr_t storage_addr,
|
||||
size_t storage_size, size_t bytes_to_read)
|
||||
{
|
||||
uint32_t offset = 0, blockAddr, readLen = 0, rdCount;
|
||||
uint32_t remSize, manual_copy_size;
|
||||
uint8_t *outputBuf = (uint8_t *) storage_addr;
|
||||
const size_t blockSize = p_sdhandle->device->cfg.blockSize;
|
||||
|
||||
VERBOSE("EMMC READ: dst=0x%lx, src=0x%lx, size=0x%lx\n",
|
||||
storage_addr, mem_addr, bytes_to_read);
|
||||
|
||||
if (storage_size < bytes_to_read)
|
||||
/* Don't have sufficient storage to complete the operation */
|
||||
return 0;
|
||||
|
||||
/* Range check non high capacity memory */
|
||||
if ((p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) == 0) {
|
||||
if (mem_addr > 0x80000000)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* High capacity card use block address mode */
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) {
|
||||
blockAddr = (uint32_t) (mem_addr / blockSize);
|
||||
offset = (uint32_t) (mem_addr - (blockAddr * blockSize));
|
||||
} else {
|
||||
blockAddr = (uint32_t) (mem_addr / blockSize) * blockSize;
|
||||
offset = (uint32_t) (mem_addr - blockAddr);
|
||||
}
|
||||
|
||||
remSize = bytes_to_read;
|
||||
|
||||
rdCount = 0;
|
||||
|
||||
/* Process first unaligned block of MAX_READ_LENGTH */
|
||||
if (offset > 0) {
|
||||
if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr, SD_MAX_READ_LENGTH)) {
|
||||
|
||||
if (remSize < (blockSize - offset)) {
|
||||
rdCount += remSize;
|
||||
manual_copy_size = remSize;
|
||||
remSize = 0; /* read is done */
|
||||
} else {
|
||||
remSize -= (blockSize - offset);
|
||||
rdCount += (blockSize - offset);
|
||||
manual_copy_size = blockSize - offset;
|
||||
}
|
||||
|
||||
/* Check for overflow */
|
||||
if (manual_copy_size > storage_size ||
|
||||
(((uintptr_t)outputBuf + manual_copy_size) >
|
||||
(storage_addr + storage_size))) {
|
||||
ERROR("EMMC READ: Overflow 1\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(outputBuf,
|
||||
(void *)((uintptr_t)
|
||||
(emmc_global_buf_ptr->u.tempbuf + offset)),
|
||||
manual_copy_size);
|
||||
|
||||
/* Update Physical address */
|
||||
outputBuf += manual_copy_size;
|
||||
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY)
|
||||
blockAddr++;
|
||||
else
|
||||
blockAddr += blockSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (remSize >= blockSize) {
|
||||
|
||||
if (remSize >= SD_MAX_BLK_TRANSFER_LENGTH)
|
||||
readLen = SD_MAX_BLK_TRANSFER_LENGTH;
|
||||
else
|
||||
readLen = (remSize / blockSize) * blockSize;
|
||||
|
||||
/* Check for overflow */
|
||||
if ((rdCount + readLen) > storage_size ||
|
||||
(((uintptr_t) outputBuf + readLen) >
|
||||
(storage_addr + storage_size))) {
|
||||
ERROR("EMMC READ: Overflow\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!read_block(p_sdhandle, outputBuf, blockAddr, readLen)) {
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY)
|
||||
blockAddr += (readLen / blockSize);
|
||||
else
|
||||
blockAddr += readLen;
|
||||
|
||||
remSize -= readLen;
|
||||
rdCount += readLen;
|
||||
|
||||
/* Update Physical address */
|
||||
outputBuf += readLen;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* process the last unaligned block reading */
|
||||
if (remSize > 0) {
|
||||
if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr, SD_MAX_READ_LENGTH)) {
|
||||
|
||||
rdCount += remSize;
|
||||
/* Check for overflow */
|
||||
if (rdCount > storage_size ||
|
||||
(((uintptr_t) outputBuf + remSize) >
|
||||
(storage_addr + storage_size))) {
|
||||
ERROR("EMMC READ: Overflow\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(outputBuf,
|
||||
emmc_global_buf_ptr->u.tempbuf, remSize);
|
||||
|
||||
/* Update Physical address */
|
||||
outputBuf += remSize;
|
||||
} else {
|
||||
rdCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rdCount;
|
||||
}
|
||||
|
||||
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
|
||||
static uint32_t sdio_write(struct sd_handle *p_sdhandle, uintptr_t mem_addr,
|
||||
uintptr_t data_addr, size_t bytes_to_write)
|
||||
{
|
||||
|
||||
uint32_t offset, blockAddr, writeLen, wtCount = 0;
|
||||
uint32_t remSize, manual_copy_size = 0;
|
||||
|
||||
uint8_t *inputBuf = (uint8_t *)data_addr;
|
||||
|
||||
/* range check non high capacity memory */
|
||||
if ((p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) == 0) {
|
||||
if (mem_addr > 0x80000000)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* the high capacity card use block address mode */
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) {
|
||||
blockAddr =
|
||||
(uint32_t)(mem_addr / p_sdhandle->device->cfg.blockSize);
|
||||
offset =
|
||||
(uint32_t)(mem_addr -
|
||||
blockAddr * p_sdhandle->device->cfg.blockSize);
|
||||
} else {
|
||||
blockAddr =
|
||||
((uint32_t)mem_addr / p_sdhandle->device->cfg.blockSize) *
|
||||
p_sdhandle->device->cfg.blockSize;
|
||||
offset = (uint32_t) mem_addr - blockAddr;
|
||||
}
|
||||
|
||||
remSize = bytes_to_write;
|
||||
|
||||
wtCount = 0;
|
||||
|
||||
/* process first unaligned block */
|
||||
if (offset > 0) {
|
||||
if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr, p_sdhandle->device->cfg.blockSize)) {
|
||||
|
||||
if (remSize <
|
||||
(p_sdhandle->device->cfg.blockSize - offset))
|
||||
manual_copy_size = remSize;
|
||||
else
|
||||
manual_copy_size =
|
||||
p_sdhandle->device->cfg.blockSize - offset;
|
||||
|
||||
memcpy((void *)((uintptr_t)
|
||||
(emmc_global_buf_ptr->u.tempbuf + offset)),
|
||||
inputBuf,
|
||||
manual_copy_size);
|
||||
|
||||
/* Update Physical address */
|
||||
|
||||
if (!write_block(p_sdhandle,
|
||||
emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr,
|
||||
p_sdhandle->device->cfg.blockSize)) {
|
||||
|
||||
if (remSize <
|
||||
(p_sdhandle->device->cfg.blockSize -
|
||||
offset)) {
|
||||
wtCount += remSize;
|
||||
manual_copy_size = remSize;
|
||||
remSize = 0; /* read is done */
|
||||
} else {
|
||||
remSize -=
|
||||
(p_sdhandle->device->cfg.blockSize -
|
||||
offset);
|
||||
wtCount +=
|
||||
(p_sdhandle->device->cfg.blockSize -
|
||||
offset);
|
||||
manual_copy_size =
|
||||
p_sdhandle->device->cfg.blockSize -
|
||||
offset;
|
||||
}
|
||||
|
||||
inputBuf += manual_copy_size;
|
||||
|
||||
if (p_sdhandle->device->ctrl.ocr &
|
||||
SD_CARD_HIGH_CAPACITY)
|
||||
blockAddr++;
|
||||
else
|
||||
blockAddr +=
|
||||
p_sdhandle->device->cfg.blockSize;
|
||||
} else
|
||||
return 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* process block writing */
|
||||
while (remSize >= p_sdhandle->device->cfg.blockSize) {
|
||||
if (remSize >= SD_MAX_READ_LENGTH) {
|
||||
writeLen = SD_MAX_READ_LENGTH;
|
||||
} else {
|
||||
writeLen =
|
||||
(remSize / p_sdhandle->device->cfg.blockSize) *
|
||||
p_sdhandle->device->cfg.blockSize;
|
||||
}
|
||||
|
||||
if (!write_block(p_sdhandle, inputBuf, blockAddr, writeLen)) {
|
||||
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY)
|
||||
blockAddr +=
|
||||
(writeLen /
|
||||
p_sdhandle->device->cfg.blockSize);
|
||||
else
|
||||
blockAddr += writeLen;
|
||||
|
||||
remSize -= writeLen;
|
||||
wtCount += writeLen;
|
||||
inputBuf += writeLen;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* process the last unaligned block reading */
|
||||
if (remSize > 0) {
|
||||
if (!read_block(p_sdhandle,
|
||||
emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr, p_sdhandle->device->cfg.blockSize)) {
|
||||
|
||||
memcpy(emmc_global_buf_ptr->u.tempbuf,
|
||||
inputBuf, remSize);
|
||||
|
||||
/* Update Physical address */
|
||||
|
||||
if (!write_block(p_sdhandle,
|
||||
emmc_global_buf_ptr->u.tempbuf,
|
||||
blockAddr,
|
||||
p_sdhandle->device->cfg.blockSize)) {
|
||||
wtCount += remSize;
|
||||
inputBuf += remSize;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
wtCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return wtCount;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Function to put the card in Ready state by sending CMD0 and CMD1
|
||||
*/
|
||||
static int32_t bcm_emmc_card_ready_state(struct sd_handle *p_sdhandle)
|
||||
{
|
||||
int32_t result = 0;
|
||||
uint32_t argument = MMC_CMD_IDLE_RESET_ARG; /* Exit from Boot mode */
|
||||
|
||||
if (p_sdhandle) {
|
||||
send_sdio_cmd(SD_CMD_GO_IDLE_STATE, argument, 0, NULL);
|
||||
|
||||
result = reset_card(p_sdhandle);
|
||||
if (result != SD_OK) {
|
||||
EMMC_TRACE("eMMC Reset error\n");
|
||||
return SD_RESET_ERROR;
|
||||
}
|
||||
SD_US_DELAY(2000);
|
||||
result = mmc_cmd1(p_sdhandle);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,886 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - 2021, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <drivers/delay_timer.h>
|
||||
#include <i2c.h>
|
||||
#include <i2c_regs.h>
|
||||
#include <lib/mmio.h>
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
/* Max instances */
|
||||
#define MAX_I2C 2U
|
||||
|
||||
/* Transaction error codes defined in Master command register (0x30) */
|
||||
#define MSTR_STS_XACT_SUCCESS 0U
|
||||
#define MSTR_STS_LOST_ARB 1U
|
||||
#define MSTR_STS_NACK_FIRST_BYTE 2U
|
||||
/* NACK on a byte other than the first byte */
|
||||
#define MSTR_STS_NACK_NON_FIRST_BYTE 3U
|
||||
|
||||
#define MSTR_STS_TTIMEOUT_EXCEEDED 4U
|
||||
#define MSTR_STS_TX_TLOW_MEXT_EXCEEDED 5U
|
||||
#define MSTR_STS_RX_TLOW_MEXT_EXCEEDED 6U
|
||||
|
||||
/* SMBUS protocol values defined in register 0x30 */
|
||||
#define SMBUS_PROT_QUICK_CMD 0U
|
||||
#define SMBUS_PROT_SEND_BYTE 1U
|
||||
#define SMBUS_PROT_RECV_BYTE 2U
|
||||
#define SMBUS_PROT_WR_BYTE 3U
|
||||
#define SMBUS_PROT_RD_BYTE 4U
|
||||
#define SMBUS_PROT_WR_WORD 5U
|
||||
#define SMBUS_PROT_RD_WORD 6U
|
||||
#define SMBUS_PROT_BLK_WR 7U
|
||||
#define SMBUS_PROT_BLK_RD 8U
|
||||
#define SMBUS_PROT_PROC_CALL 9U
|
||||
#define SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL 10U
|
||||
|
||||
/* Number can be changed later */
|
||||
#define BUS_BUSY_COUNT 100000U
|
||||
|
||||
#define IPROC_I2C_INVALID_ADDR 0xFFU
|
||||
|
||||
#define I2C_SMBUS_BLOCK_MAX 32U
|
||||
|
||||
/*
|
||||
* Enum to specify clock speed. The user will provide it during initialization.
|
||||
* If needed, it can be changed dynamically
|
||||
*/
|
||||
typedef enum iproc_smb_clk_freq {
|
||||
IPROC_SMB_SPEED_100KHz = 0,
|
||||
IPROC_SMB_SPEED_400KHz = 1,
|
||||
IPROC_SMB_SPEED_INVALID = 255
|
||||
} smb_clk_freq_t;
|
||||
|
||||
/* Structure used to pass information to read/write functions. */
|
||||
struct iproc_xact_info {
|
||||
/* Bus Identifier */
|
||||
uint32_t bus_id;
|
||||
/* Device Address */
|
||||
uint8_t devaddr;
|
||||
/* Passed by caller to send SMBus command cod e*/
|
||||
uint8_t command;
|
||||
/* actual data passed by the caller */
|
||||
uint8_t *data;
|
||||
/* Size of data buffer passed */
|
||||
uint32_t size;
|
||||
/* Sent by caller specifying PEC, 10-bit addresses */
|
||||
uint16_t flags;
|
||||
/* SMBus protocol to use to perform transaction */
|
||||
uint8_t smb_proto;
|
||||
/* true if command field below is valid. Otherwise, false */
|
||||
uint32_t cmd_valid;
|
||||
};
|
||||
|
||||
static const uintptr_t smbus_base_reg_addr[MAX_I2C] = {
|
||||
SMBUS0_REGS_BASE,
|
||||
SMBUS1_REGS_BASE
|
||||
};
|
||||
|
||||
/* Function to read a value from specified register. */
|
||||
static uint32_t iproc_i2c_reg_read(uint32_t bus_id, unsigned long reg_addr)
|
||||
{
|
||||
uint32_t val;
|
||||
uintptr_t smbus;
|
||||
|
||||
smbus = smbus_base_reg_addr[bus_id];
|
||||
|
||||
val = mmio_read_32(smbus + reg_addr);
|
||||
VERBOSE("i2c %u: reg %p read 0x%x\n", bus_id,
|
||||
(void *)(smbus + reg_addr), val);
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Function to write a value ('val') in to a specified register. */
|
||||
static void iproc_i2c_reg_write(uint32_t bus_id,
|
||||
unsigned long reg_addr,
|
||||
uint32_t val)
|
||||
{
|
||||
uintptr_t smbus;
|
||||
|
||||
smbus = smbus_base_reg_addr[bus_id];
|
||||
|
||||
mmio_write_32((smbus + reg_addr), val);
|
||||
VERBOSE("i2c %u: reg %p wrote 0x%x\n", bus_id,
|
||||
(void *)(smbus + reg_addr), val);
|
||||
}
|
||||
|
||||
/* Function to clear and set bits in a specified register. */
|
||||
static void iproc_i2c_reg_clearset(uint32_t bus_id,
|
||||
unsigned long reg_addr,
|
||||
uint32_t clear,
|
||||
uint32_t set)
|
||||
{
|
||||
uintptr_t smbus;
|
||||
|
||||
smbus = smbus_base_reg_addr[bus_id];
|
||||
|
||||
mmio_clrsetbits_32((smbus + reg_addr), clear, set);
|
||||
VERBOSE("i2c %u: reg %p clear 0x%x, set 0x%x\n", bus_id,
|
||||
(void *)(smbus + reg_addr), clear, set);
|
||||
}
|
||||
|
||||
/* Function to dump all SMBUS register */
|
||||
#ifdef BCM_I2C_DEBUG
|
||||
static int iproc_dump_i2c_regs(uint32_t bus_id)
|
||||
{
|
||||
uint32_t regval;
|
||||
|
||||
if (bus_id > MAX_I2C) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
INFO("----------------------------------------------\n");
|
||||
INFO("%s: Dumping SMBus %u registers...\n", __func__, bus_id);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_CFG_REG);
|
||||
INFO("SMB_CFG_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_TIMGCFG_REG);
|
||||
INFO("SMB_TIMGCFG_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_ADDR_REG);
|
||||
INFO("SMB_ADDR_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_MSTRFIFOCTL_REG);
|
||||
INFO("SMB_MSTRFIFOCTL_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_SLVFIFOCTL_REG);
|
||||
INFO("SMB_SLVFIFOCTL_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_BITBANGCTL_REG);
|
||||
INFO("SMB_BITBANGCTL_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_MSTRCMD_REG);
|
||||
INFO("SMB_MSTRCMD_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_SLVCMD_REG);
|
||||
INFO("SMB_SLVCMD_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_EVTEN_REG);
|
||||
INFO("SMB_EVTEN_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_EVTSTS_REG);
|
||||
INFO("SMB_EVTSTS_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_MSTRDATAWR_REG);
|
||||
INFO("SMB_MSTRDATAWR_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_MSTRDATARD_REG);
|
||||
INFO("SMB_MSTRDATARD_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_SLVDATAWR_REG);
|
||||
INFO("SMB_SLVDATAWR_REG=0x%x\n", regval);
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_SLVDATARD_REG);
|
||||
INFO("SMB_SLVDATARD_REG=0x%x\n", regval);
|
||||
|
||||
INFO("----------------------------------------------\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Function to ensure that the previous transaction was completed before
|
||||
* initiating a new transaction. It can also be used in polling mode to
|
||||
* check status of completion of a command
|
||||
*/
|
||||
static int iproc_i2c_startbusy_wait(uint32_t bus_id)
|
||||
{
|
||||
uint32_t regval;
|
||||
uint32_t retry = 0U;
|
||||
|
||||
/*
|
||||
* Check if an operation is in progress. During probe it won't be.
|
||||
* Want to make sure that the transaction in progress is completed.
|
||||
*/
|
||||
do {
|
||||
udelay(1U);
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_MSTRCMD_REG);
|
||||
regval &= SMB_MSTRSTARTBUSYCMD_MASK;
|
||||
if (retry++ > BUS_BUSY_COUNT) {
|
||||
ERROR("%s: START_BUSY bit didn't clear, exiting\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
} while (regval != 0U);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function copies data to SMBus's Tx FIFO. Valid for write transactions
|
||||
* info: Data to copy in to Tx FIFO. For read commands, the size should be
|
||||
* set to zero by the caller
|
||||
*/
|
||||
static void iproc_i2c_write_trans_data(struct iproc_xact_info *info)
|
||||
{
|
||||
uint32_t regval;
|
||||
uint8_t devaddr;
|
||||
uint32_t i;
|
||||
uint32_t num_data_bytes = 0U;
|
||||
|
||||
#ifdef BCM_I2C_DEBUG
|
||||
INFO("%s:dev_addr=0x%x,cmd_valid=%d, cmd=0x%x, size=%u proto=%d\n",
|
||||
__func__, info->devaddr, info->cmd_valid, info->command,
|
||||
info->size, info->smb_proto);
|
||||
#endif
|
||||
/* Shift devaddr by 1 bit since SMBus uses the low bit[0] for R/W_n */
|
||||
devaddr = (info->devaddr << 1);
|
||||
|
||||
/*
|
||||
* Depending on the SMBus protocol, we need to write additional
|
||||
* transaction data in to Tx FIFO. Refer to section 5.5 of SMBus spec
|
||||
* for sequence for a transaction
|
||||
*/
|
||||
switch (info->smb_proto) {
|
||||
case SMBUS_PROT_RECV_BYTE:
|
||||
/* No additional data to be written */
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRDATAWR_REG,
|
||||
devaddr | 0x1U | SMB_MSTRWRSTS_MASK);
|
||||
break;
|
||||
case SMBUS_PROT_SEND_BYTE:
|
||||
num_data_bytes = info->size;
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRDATAWR_REG,
|
||||
devaddr);
|
||||
break;
|
||||
case SMBUS_PROT_RD_BYTE:
|
||||
case SMBUS_PROT_RD_WORD:
|
||||
case SMBUS_PROT_BLK_RD:
|
||||
/* Write slave address with R/W~ set (bit #0) */
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRDATAWR_REG,
|
||||
devaddr | 0x1U);
|
||||
break;
|
||||
case SMBUS_PROT_BLK_WR_BLK_RD_PROC_CALL:
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRDATAWR_REG,
|
||||
devaddr | 0x1U | SMB_MSTRWRSTS_MASK);
|
||||
break;
|
||||
case SMBUS_PROT_WR_BYTE:
|
||||
case SMBUS_PROT_WR_WORD:
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRDATAWR_REG,
|
||||
devaddr);
|
||||
/*
|
||||
* No additional bytes to be written. Data portion is written
|
||||
* in the 'for' loop below
|
||||
*/
|
||||
num_data_bytes = info->size;
|
||||
break;
|
||||
case SMBUS_PROT_BLK_WR:
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRDATAWR_REG,
|
||||
devaddr);
|
||||
/* 3rd byte is byte count */
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRDATAWR_REG,
|
||||
info->size);
|
||||
num_data_bytes = info->size;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
/* If the protocol needs command code, copy it */
|
||||
if (info->cmd_valid) {
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRDATAWR_REG,
|
||||
info->command);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy actual data from caller. In general, for reads,
|
||||
* no data is copied.
|
||||
*/
|
||||
for (i = 0U; num_data_bytes; --num_data_bytes, i++) {
|
||||
/* For the last byte, set MASTER_WR_STATUS bit */
|
||||
regval = (num_data_bytes == 1U) ?
|
||||
info->data[i] | SMB_MSTRWRSTS_MASK : info->data[i];
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRDATAWR_REG,
|
||||
regval);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function writes to the master command register and
|
||||
* then polls for completion
|
||||
*/
|
||||
static int iproc_i2c_write_master_command(uint32_t mastercmd,
|
||||
struct iproc_xact_info *info)
|
||||
{
|
||||
uint32_t retry = 0U;
|
||||
uint32_t regval;
|
||||
|
||||
iproc_i2c_reg_write(info->bus_id, SMB_MSTRCMD_REG, mastercmd);
|
||||
|
||||
/* Check for Master Busy status */
|
||||
regval = iproc_i2c_reg_read(info->bus_id, SMB_MSTRCMD_REG);
|
||||
while ((regval & SMB_MSTRSTARTBUSYCMD_MASK) != 0U) {
|
||||
udelay(1U);
|
||||
if (retry++ > BUS_BUSY_COUNT) {
|
||||
ERROR("%s: START_BUSY bit didn't clear, exiting\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
regval = iproc_i2c_reg_read(info->bus_id, SMB_MSTRCMD_REG);
|
||||
}
|
||||
|
||||
/* If start_busy bit cleared, check if there are any errors */
|
||||
if (!(regval & SMB_MSTRSTARTBUSYCMD_MASK)) {
|
||||
/* start_busy bit cleared, check master_status field now */
|
||||
regval &= SMB_MSTRSTS_MASK;
|
||||
regval >>= SMB_MSTRSTS_SHIFT;
|
||||
if (regval != MSTR_STS_XACT_SUCCESS) {
|
||||
/* Error We can flush Tx FIFO here */
|
||||
ERROR("%s: ERROR: %u exiting\n", __func__, regval);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
/* Function to initiate data send and verify completion status */
|
||||
static int iproc_i2c_data_send(struct iproc_xact_info *info)
|
||||
{
|
||||
int rc;
|
||||
uint32_t mastercmd;
|
||||
|
||||
/* Make sure the previous transaction completed */
|
||||
rc = iproc_i2c_startbusy_wait(info->bus_id);
|
||||
|
||||
if (rc < 0) {
|
||||
WARN("%s: Send: bus is busy, exiting\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
/* Write transaction bytes to Tx FIFO */
|
||||
iproc_i2c_write_trans_data(info);
|
||||
|
||||
/*
|
||||
* Program master command register (0x30) with protocol type and set
|
||||
* start_busy_command bit to initiate the write transaction
|
||||
*/
|
||||
mastercmd = (info->smb_proto << SMB_MSTRSMBUSPROTO_SHIFT) |
|
||||
SMB_MSTRSTARTBUSYCMD_MASK;
|
||||
|
||||
if (iproc_i2c_write_master_command(mastercmd, info)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to initiate data receive, verify completion status,
|
||||
* and read from SMBUS Read FIFO
|
||||
*/
|
||||
static int iproc_i2c_data_recv(struct iproc_xact_info *info,
|
||||
uint32_t *num_bytes_read)
|
||||
{
|
||||
int rc;
|
||||
uint32_t mastercmd;
|
||||
uint32_t regval;
|
||||
|
||||
/* Make sure the previous transaction completed */
|
||||
rc = iproc_i2c_startbusy_wait(info->bus_id);
|
||||
|
||||
if (rc < 0) {
|
||||
WARN("%s: Receive: Bus is busy, exiting\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Program all transaction bytes into master Tx FIFO */
|
||||
iproc_i2c_write_trans_data(info);
|
||||
|
||||
/*
|
||||
* Program master command register (0x30) with protocol type and set
|
||||
* start_busy_command bit to initiate the write transaction
|
||||
*/
|
||||
mastercmd = (info->smb_proto << SMB_MSTRSMBUSPROTO_SHIFT) |
|
||||
SMB_MSTRSTARTBUSYCMD_MASK | info->size;
|
||||
|
||||
if (iproc_i2c_write_master_command(mastercmd, info)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read received byte(s), after TX out address etc */
|
||||
regval = iproc_i2c_reg_read(info->bus_id, SMB_MSTRDATARD_REG);
|
||||
|
||||
/* For block read, protocol (hw) returns byte count,as the first byte */
|
||||
if (info->smb_proto == SMBUS_PROT_BLK_RD) {
|
||||
uint32_t i;
|
||||
|
||||
*num_bytes_read = regval & SMB_MSTRRDDATA_MASK;
|
||||
/*
|
||||
* Limit to reading a max of 32 bytes only; just a safeguard.
|
||||
* If # bytes read is a number > 32, check transaction set up,
|
||||
* and contact hw engg.
|
||||
* Assumption: PEC is disabled
|
||||
*/
|
||||
for (i = 0U; (i < *num_bytes_read) &&
|
||||
(i < I2C_SMBUS_BLOCK_MAX); i++) {
|
||||
/* Read Rx FIFO for data bytes */
|
||||
regval = iproc_i2c_reg_read(info->bus_id,
|
||||
SMB_MSTRDATARD_REG);
|
||||
info->data[i] = regval & SMB_MSTRRDDATA_MASK;
|
||||
}
|
||||
} else {
|
||||
/* 1 Byte data */
|
||||
*info->data = regval & SMB_MSTRRDDATA_MASK;
|
||||
*num_bytes_read = 1U;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function set clock frequency for SMBus block. As per hardware
|
||||
* engineering, the clock frequency can be changed dynamically.
|
||||
*/
|
||||
static int iproc_i2c_set_clk_freq(uint32_t bus_id, smb_clk_freq_t freq)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
switch (freq) {
|
||||
case IPROC_SMB_SPEED_100KHz:
|
||||
val = 0U;
|
||||
break;
|
||||
case IPROC_SMB_SPEED_400KHz:
|
||||
val = 1U;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
iproc_i2c_reg_clearset(bus_id, SMB_TIMGCFG_REG,
|
||||
SMB_TIMGCFG_MODE400_MASK,
|
||||
val << SMB_TIMGCFG_MODE400_SHIFT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function to fill the iproc_xact_info structure */
|
||||
static void iproc_i2c_fill_info(struct iproc_xact_info *info, uint32_t bus_id,
|
||||
uint8_t devaddr, uint8_t cmd, uint8_t *value,
|
||||
uint8_t smb_proto, uint32_t cmd_valid)
|
||||
{
|
||||
info->bus_id = bus_id;
|
||||
info->devaddr = devaddr;
|
||||
info->command = (uint8_t)cmd;
|
||||
info->smb_proto = smb_proto;
|
||||
info->data = value;
|
||||
info->size = 1U;
|
||||
info->flags = 0U;
|
||||
info->cmd_valid = cmd_valid;
|
||||
}
|
||||
|
||||
/* This function initializes the SMBUS */
|
||||
static void iproc_i2c_init(uint32_t bus_id, int speed)
|
||||
{
|
||||
uint32_t regval;
|
||||
|
||||
#ifdef BCM_I2C_DEBUG
|
||||
INFO("%s: Enter Init\n", __func__);
|
||||
#endif
|
||||
|
||||
/* Put controller in reset */
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_CFG_REG);
|
||||
regval |= BIT(SMB_CFG_RST_SHIFT);
|
||||
regval &= ~(BIT(SMB_CFG_SMBEN_SHIFT));
|
||||
iproc_i2c_reg_write(bus_id, SMB_CFG_REG, regval);
|
||||
|
||||
/* Wait 100 usec per spec */
|
||||
udelay(100U);
|
||||
|
||||
/* Bring controller out of reset */
|
||||
regval &= ~(BIT(SMB_CFG_RST_SHIFT));
|
||||
iproc_i2c_reg_write(bus_id, SMB_CFG_REG, regval);
|
||||
|
||||
/*
|
||||
* Flush Tx, Rx FIFOs. Note we are setting the Rx FIFO threshold to 0.
|
||||
* May be OK since we are setting RX_EVENT and RX_FIFO_FULL interrupts
|
||||
*/
|
||||
regval = SMB_MSTRRXFIFOFLSH_MASK | SMB_MSTRTXFIFOFLSH_MASK;
|
||||
iproc_i2c_reg_write(bus_id, SMB_MSTRFIFOCTL_REG, regval);
|
||||
|
||||
/*
|
||||
* Enable SMbus block. Note, we are setting MASTER_RETRY_COUNT to zero
|
||||
* since there will be only one master
|
||||
*/
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_CFG_REG);
|
||||
regval |= SMB_CFG_SMBEN_MASK;
|
||||
iproc_i2c_reg_write(bus_id, SMB_CFG_REG, regval);
|
||||
/* Wait a minimum of 50 Usec, as per SMB hw doc. But we wait longer */
|
||||
mdelay(10U);
|
||||
|
||||
/* If error then set default speed */
|
||||
if (i2c_set_bus_speed(bus_id, speed)) {
|
||||
i2c_set_bus_speed(bus_id, I2C_SPEED_DEFAULT);
|
||||
}
|
||||
|
||||
/* Disable intrs */
|
||||
regval = 0x0U;
|
||||
iproc_i2c_reg_write(bus_id, SMB_EVTEN_REG, regval);
|
||||
|
||||
/* Clear intrs (W1TC) */
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_EVTSTS_REG);
|
||||
iproc_i2c_reg_write(bus_id, SMB_EVTSTS_REG, regval);
|
||||
|
||||
#ifdef BCM_I2C_DEBUG
|
||||
iproc_dump_i2c_regs(bus_id);
|
||||
|
||||
INFO("%s: Exit Init Successfully\n", __func__);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Function Name: i2c_init
|
||||
*
|
||||
* Description:
|
||||
* This function initializes the SMBUS.
|
||||
*
|
||||
* Parameters:
|
||||
* bus_id - I2C bus ID
|
||||
* speed - I2C bus speed in Hz
|
||||
*
|
||||
* Return:
|
||||
* 0 on success, or -1 on failure.
|
||||
*/
|
||||
int i2c_init(uint32_t bus_id, int speed)
|
||||
{
|
||||
if (bus_id > MAX_I2C) {
|
||||
WARN("%s: Invalid Bus %u\n", __func__, bus_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iproc_i2c_init(bus_id, speed);
|
||||
return 0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function Name: i2c_probe
|
||||
*
|
||||
* Description:
|
||||
* This function probes the I2C bus for the existence of the specified
|
||||
* device.
|
||||
*
|
||||
* Parameters:
|
||||
* bus_id - I2C bus ID
|
||||
* devaddr - Device Address
|
||||
*
|
||||
* Return:
|
||||
* 0 on success, or -1 on failure.
|
||||
*/
|
||||
int i2c_probe(uint32_t bus_id, uint8_t devaddr)
|
||||
{
|
||||
uint32_t regval;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* i2c_init() Initializes internal regs, disable intrs (and then clear intrs),
|
||||
* set fifo thresholds, etc.
|
||||
* Shift devaddr by 1 bit since SMBus uses the low bit[0] for R/W_n
|
||||
*/
|
||||
regval = (devaddr << 1U);
|
||||
iproc_i2c_reg_write(bus_id, SMB_MSTRDATAWR_REG, regval);
|
||||
|
||||
regval = ((SMBUS_PROT_QUICK_CMD << SMB_MSTRSMBUSPROTO_SHIFT) |
|
||||
SMB_MSTRSTARTBUSYCMD_MASK);
|
||||
iproc_i2c_reg_write(bus_id, SMB_MSTRCMD_REG, regval);
|
||||
|
||||
rc = iproc_i2c_startbusy_wait(bus_id);
|
||||
|
||||
if (rc < 0) {
|
||||
WARN("%s: Probe: bus is busy, exiting\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_MSTRCMD_REG);
|
||||
if (((regval & SMB_MSTRSTS_MASK) >> SMB_MSTRSTS_SHIFT) == 0)
|
||||
VERBOSE("i2c device address: 0x%x\n", devaddr);
|
||||
else
|
||||
return -1;
|
||||
|
||||
#ifdef BCM_I2C_DEBUG
|
||||
iproc_dump_i2c_regs(bus_id);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function Name: i2c_recv_byte
|
||||
*
|
||||
* Description:
|
||||
* This function reads I2C data from a device without specifying
|
||||
* a command regsiter.
|
||||
*
|
||||
* Parameters:
|
||||
* bus_id - I2C bus ID
|
||||
* devaddr - Device Address
|
||||
* value - Data Read
|
||||
*
|
||||
* Return:
|
||||
* 0 on success, or -1 on failure.
|
||||
*/
|
||||
int i2c_recv_byte(uint32_t bus_id, uint8_t devaddr, uint8_t *value)
|
||||
{
|
||||
int rc;
|
||||
struct iproc_xact_info info;
|
||||
uint32_t num_bytes_read = 0;
|
||||
|
||||
iproc_i2c_fill_info(&info, bus_id, devaddr, 0U, value,
|
||||
SMBUS_PROT_RECV_BYTE, 0U);
|
||||
|
||||
/* Refer to i2c_smbus_read_byte for params passed. */
|
||||
rc = iproc_i2c_data_recv(&info, &num_bytes_read);
|
||||
|
||||
if (rc < 0) {
|
||||
printf("%s: %s error accessing device 0x%x\n",
|
||||
__func__, "Read", devaddr);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function Name: i2c_send_byte
|
||||
*
|
||||
* Description:
|
||||
* This function send I2C data to a device without specifying
|
||||
* a command regsiter.
|
||||
*
|
||||
* Parameters:
|
||||
* bus_id - I2C bus ID
|
||||
* devaddr - Device Address
|
||||
* value - Data Send
|
||||
*
|
||||
* Return:
|
||||
* 0 on success, or -1 on failure.
|
||||
*/
|
||||
int i2c_send_byte(uint32_t bus_id, uint8_t devaddr, uint8_t value)
|
||||
{
|
||||
int rc;
|
||||
struct iproc_xact_info info;
|
||||
|
||||
iproc_i2c_fill_info(&info, bus_id, devaddr, 0U, &value,
|
||||
SMBUS_PROT_SEND_BYTE, 0U);
|
||||
|
||||
/* Refer to i2c_smbus_write_byte params passed. */
|
||||
rc = iproc_i2c_data_send(&info);
|
||||
|
||||
if (rc < 0) {
|
||||
ERROR("%s: %s error accessing device 0x%x\n",
|
||||
__func__, "Write", devaddr);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Helper function to read a single byte */
|
||||
static int i2c_read_byte(uint32_t bus_id,
|
||||
uint8_t devaddr,
|
||||
uint8_t regoffset,
|
||||
uint8_t *value)
|
||||
{
|
||||
int rc;
|
||||
struct iproc_xact_info info;
|
||||
uint32_t num_bytes_read = 0U;
|
||||
|
||||
iproc_i2c_fill_info(&info, bus_id, devaddr, regoffset, value,
|
||||
SMBUS_PROT_RD_BYTE, 1U);
|
||||
|
||||
/* Refer to i2c_smbus_read_byte for params passed. */
|
||||
rc = iproc_i2c_data_recv(&info, &num_bytes_read);
|
||||
|
||||
if (rc < 0) {
|
||||
ERROR("%s: %s error accessing device 0x%x\n",
|
||||
__func__, "Read", devaddr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function Name: i2c_read
|
||||
*
|
||||
* Description:
|
||||
* This function reads I2C data from a device with a designated
|
||||
* command register
|
||||
*
|
||||
* Parameters:
|
||||
* bus_id - I2C bus ID
|
||||
* devaddr - Device Address
|
||||
* addr - Register Offset
|
||||
* alen - Address Length, 1 for byte, 2 for word (not supported)
|
||||
* buffer - Data Buffer
|
||||
* len - Data Length in bytes
|
||||
*
|
||||
* Return:
|
||||
* 0 on success, or -1 on failure.
|
||||
*/
|
||||
int i2c_read(uint32_t bus_id,
|
||||
uint8_t devaddr,
|
||||
uint32_t addr,
|
||||
int alen,
|
||||
uint8_t *buffer,
|
||||
int len)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (alen > 1) {
|
||||
WARN("I2C read: addr len %d not supported\n", alen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr + len > 256) {
|
||||
WARN("I2C read: address out of range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0U; i < len; i++) {
|
||||
if (i2c_read_byte(bus_id, devaddr, addr + i, &buffer[i])) {
|
||||
ERROR("I2C read: I/O error\n");
|
||||
iproc_i2c_init(bus_id, i2c_get_bus_speed(bus_id));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Helper function to write a single byte */
|
||||
static int i2c_write_byte(uint32_t bus_id,
|
||||
uint8_t devaddr,
|
||||
uint8_t regoffset,
|
||||
uint8_t value)
|
||||
{
|
||||
int rc;
|
||||
struct iproc_xact_info info;
|
||||
|
||||
iproc_i2c_fill_info(&info, bus_id, devaddr, regoffset, &value,
|
||||
SMBUS_PROT_WR_BYTE, 1U);
|
||||
|
||||
/* Refer to i2c_smbus_write_byte params passed. */
|
||||
rc = iproc_i2c_data_send(&info);
|
||||
|
||||
if (rc < 0) {
|
||||
ERROR("%s: %s error accessing device 0x%x\n",
|
||||
__func__, "Write", devaddr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function Name: i2c_write
|
||||
*
|
||||
* Description:
|
||||
* This function write I2C data to a device with a designated
|
||||
* command register
|
||||
*
|
||||
* Parameters:
|
||||
* bus_id - I2C bus ID
|
||||
* devaddr - Device Address
|
||||
* addr - Register Offset
|
||||
* alen - Address Length, 1 for byte, 2 for word (not supported)
|
||||
* buffer - Data Buffer
|
||||
* len - Data Length in bytes
|
||||
*
|
||||
* Return:
|
||||
* 0 on success, or -1 on failure.
|
||||
*/
|
||||
int i2c_write(uint32_t bus_id,
|
||||
uint8_t devaddr,
|
||||
uint32_t addr,
|
||||
int alen,
|
||||
uint8_t *buffer,
|
||||
int len)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (alen > 1) {
|
||||
WARN("I2C write: addr len %d not supported\n", alen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (addr + len > 256U) {
|
||||
WARN("I2C write: address out of range\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0U; i < len; i++) {
|
||||
if (i2c_write_byte(bus_id, devaddr, addr + i, buffer[i])) {
|
||||
ERROR("I2C write: I/O error\n");
|
||||
iproc_i2c_init(bus_id, i2c_get_bus_speed(bus_id));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function Name: i2c_set_bus_speed
|
||||
*
|
||||
* Description:
|
||||
* This function configures the SMBUS speed
|
||||
*
|
||||
* Parameters:
|
||||
* bus_id - I2C bus ID
|
||||
* speed - I2C bus speed in Hz
|
||||
*
|
||||
* Return:
|
||||
* 0 on success, or -1 on failure.
|
||||
*/
|
||||
int i2c_set_bus_speed(uint32_t bus_id, uint32_t speed)
|
||||
{
|
||||
switch (speed) {
|
||||
case I2C_SPEED_100KHz:
|
||||
iproc_i2c_set_clk_freq(bus_id, IPROC_SMB_SPEED_100KHz);
|
||||
break;
|
||||
|
||||
case I2C_SPEED_400KHz:
|
||||
iproc_i2c_set_clk_freq(bus_id, IPROC_SMB_SPEED_400KHz);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function Name: i2c_get_bus_speed
|
||||
*
|
||||
* Description:
|
||||
* This function returns the SMBUS speed.
|
||||
*
|
||||
* Parameters:
|
||||
* bus_id - I2C bus ID
|
||||
*
|
||||
* Return:
|
||||
* Bus speed in Hz, 0 on failure
|
||||
*/
|
||||
uint32_t i2c_get_bus_speed(uint32_t bus_id)
|
||||
{
|
||||
uint32_t regval;
|
||||
uint32_t retval = 0U;
|
||||
|
||||
regval = iproc_i2c_reg_read(bus_id, SMB_TIMGCFG_REG);
|
||||
regval &= SMB_TIMGCFG_MODE400_MASK;
|
||||
regval >>= SMB_TIMGCFG_MODE400_SHIFT;
|
||||
|
||||
switch (regval) {
|
||||
case IPROC_SMB_SPEED_100KHz:
|
||||
retval = I2C_SPEED_100KHz;
|
||||
break;
|
||||
|
||||
case IPROC_SMB_SPEED_400KHz:
|
||||
retval = I2C_SPEED_400KHz;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <drivers/gpio.h>
|
||||
#include <lib/mmio.h>
|
||||
#include <plat/common/platform.h>
|
||||
|
||||
#include <iproc_gpio.h>
|
||||
#include <platform_def.h>
|
||||
|
||||
#define IPROC_GPIO_DATA_IN_OFFSET 0x00
|
||||
#define IPROC_GPIO_DATA_OUT_OFFSET 0x04
|
||||
#define IPROC_GPIO_OUT_EN_OFFSET 0x08
|
||||
#define IPROC_GPIO_PAD_RES_OFFSET 0x34
|
||||
#define IPROC_GPIO_RES_EN_OFFSET 0x38
|
||||
|
||||
#define PINMUX_OFFSET(gpio) ((gpio) * 4)
|
||||
#define PINCONF_OFFSET(gpio) ((gpio) * 4)
|
||||
#define PINCONF_PULL_UP BIT(4)
|
||||
#define PINCONF_PULL_DOWN BIT(5)
|
||||
|
||||
/*
|
||||
* iProc GPIO bank is always 0x200 per bank,
|
||||
* with each bank supporting 32 GPIOs.
|
||||
*/
|
||||
#define GPIO_BANK_SIZE 0x200
|
||||
#define NGPIOS_PER_BANK 32
|
||||
#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
|
||||
|
||||
#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
|
||||
#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
|
||||
|
||||
#define MUX_GPIO_MODE 0x3
|
||||
|
||||
/*
|
||||
* @base: base address of the gpio controller
|
||||
* @pinconf_base: base address of the pinconf
|
||||
* @pinmux_base: base address of the mux controller
|
||||
* @nr_gpios: maxinum number of GPIOs
|
||||
*/
|
||||
struct iproc_gpio {
|
||||
uintptr_t base;
|
||||
uintptr_t pinconf_base;
|
||||
uintptr_t pinmux_base;
|
||||
int nr_gpios;
|
||||
};
|
||||
|
||||
static struct iproc_gpio iproc_gpio;
|
||||
|
||||
static void gpio_set_bit(uintptr_t base, unsigned int reg, int gpio, bool set)
|
||||
{
|
||||
unsigned int offset = IPROC_GPIO_REG(gpio, reg);
|
||||
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
|
||||
uint32_t val;
|
||||
|
||||
val = mmio_read_32(base + offset);
|
||||
if (set)
|
||||
val |= BIT(shift);
|
||||
else
|
||||
val &= ~BIT(shift);
|
||||
|
||||
mmio_write_32(base + offset, val);
|
||||
}
|
||||
|
||||
static bool gpio_get_bit(uintptr_t base, unsigned int reg, int gpio)
|
||||
{
|
||||
unsigned int offset = IPROC_GPIO_REG(gpio, reg);
|
||||
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
|
||||
|
||||
return !!(mmio_read_32(base + offset) & BIT(shift));
|
||||
}
|
||||
|
||||
static void mux_to_gpio(struct iproc_gpio *g, int gpio)
|
||||
{
|
||||
/* mux pad to GPIO if IOPAD configuration is mandatory */
|
||||
if (g->pinmux_base)
|
||||
mmio_write_32(g->pinmux_base + PINMUX_OFFSET(gpio),
|
||||
MUX_GPIO_MODE);
|
||||
}
|
||||
|
||||
static void set_direction(int gpio, int direction)
|
||||
{
|
||||
struct iproc_gpio *g = &iproc_gpio;
|
||||
bool dir = (direction == GPIO_DIR_OUT) ? true : false;
|
||||
|
||||
assert(gpio < g->nr_gpios);
|
||||
|
||||
mux_to_gpio(g, gpio);
|
||||
gpio_set_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio, dir);
|
||||
}
|
||||
|
||||
static int get_direction(int gpio)
|
||||
{
|
||||
struct iproc_gpio *g = &iproc_gpio;
|
||||
int dir;
|
||||
|
||||
assert(gpio < g->nr_gpios);
|
||||
|
||||
mux_to_gpio(g, gpio);
|
||||
dir = gpio_get_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio) ?
|
||||
GPIO_DIR_OUT : GPIO_DIR_IN;
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
static int get_value(int gpio)
|
||||
{
|
||||
struct iproc_gpio *g = &iproc_gpio;
|
||||
unsigned int offset;
|
||||
|
||||
assert(gpio < g->nr_gpios);
|
||||
|
||||
mux_to_gpio(g, gpio);
|
||||
|
||||
/*
|
||||
* If GPIO is configured as output, read from the GPIO_OUT register;
|
||||
* otherwise, read from the GPIO_IN register
|
||||
*/
|
||||
offset = gpio_get_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio) ?
|
||||
IPROC_GPIO_DATA_OUT_OFFSET : IPROC_GPIO_DATA_IN_OFFSET;
|
||||
|
||||
return gpio_get_bit(g->base, offset, gpio);
|
||||
}
|
||||
|
||||
static void set_value(int gpio, int val)
|
||||
{
|
||||
struct iproc_gpio *g = &iproc_gpio;
|
||||
|
||||
assert(gpio < g->nr_gpios);
|
||||
|
||||
mux_to_gpio(g, gpio);
|
||||
|
||||
/* make sure GPIO is configured to output, and then set the value */
|
||||
gpio_set_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio, true);
|
||||
gpio_set_bit(g->base, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val));
|
||||
}
|
||||
|
||||
static int get_pull(int gpio)
|
||||
{
|
||||
struct iproc_gpio *g = &iproc_gpio;
|
||||
uint32_t val;
|
||||
|
||||
assert(gpio < g->nr_gpios);
|
||||
mux_to_gpio(g, gpio);
|
||||
|
||||
/* when there's a valid pinconf_base, use it */
|
||||
if (g->pinconf_base) {
|
||||
val = mmio_read_32(g->pinconf_base + PINCONF_OFFSET(gpio));
|
||||
|
||||
if (val & PINCONF_PULL_UP)
|
||||
return GPIO_PULL_UP;
|
||||
else if (val & PINCONF_PULL_DOWN)
|
||||
return GPIO_PULL_DOWN;
|
||||
else
|
||||
return GPIO_PULL_NONE;
|
||||
}
|
||||
|
||||
/* no pinconf_base. fall back to GPIO internal pull control */
|
||||
if (!gpio_get_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio))
|
||||
return GPIO_PULL_NONE;
|
||||
|
||||
return gpio_get_bit(g->base, IPROC_GPIO_PAD_RES_OFFSET, gpio) ?
|
||||
GPIO_PULL_UP : GPIO_PULL_DOWN;
|
||||
}
|
||||
|
||||
static void set_pull(int gpio, int pull)
|
||||
{
|
||||
struct iproc_gpio *g = &iproc_gpio;
|
||||
uint32_t val;
|
||||
|
||||
assert(gpio < g->nr_gpios);
|
||||
mux_to_gpio(g, gpio);
|
||||
|
||||
/* when there's a valid pinconf_base, use it */
|
||||
if (g->pinconf_base) {
|
||||
val = mmio_read_32(g->pinconf_base + PINCONF_OFFSET(gpio));
|
||||
|
||||
if (pull == GPIO_PULL_NONE) {
|
||||
val &= ~(PINCONF_PULL_UP | PINCONF_PULL_DOWN);
|
||||
} else if (pull == GPIO_PULL_UP) {
|
||||
val |= PINCONF_PULL_UP;
|
||||
val &= ~PINCONF_PULL_DOWN;
|
||||
} else if (pull == GPIO_PULL_DOWN) {
|
||||
val |= PINCONF_PULL_DOWN;
|
||||
val &= ~PINCONF_PULL_UP;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
mmio_write_32(g->pinconf_base + PINCONF_OFFSET(gpio), val);
|
||||
}
|
||||
|
||||
/* no pinconf_base. fall back to GPIO internal pull control */
|
||||
if (pull == GPIO_PULL_NONE) {
|
||||
gpio_set_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio, false);
|
||||
return;
|
||||
}
|
||||
|
||||
/* enable pad register and pull up or down */
|
||||
gpio_set_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio, true);
|
||||
gpio_set_bit(g->base, IPROC_GPIO_PAD_RES_OFFSET, gpio,
|
||||
!!(pull == GPIO_PULL_UP));
|
||||
}
|
||||
|
||||
const gpio_ops_t iproc_gpio_ops = {
|
||||
.get_direction = get_direction,
|
||||
.set_direction = set_direction,
|
||||
.get_value = get_value,
|
||||
.set_value = set_value,
|
||||
.get_pull = get_pull,
|
||||
.set_pull = set_pull,
|
||||
};
|
||||
|
||||
void iproc_gpio_init(uintptr_t base, int nr_gpios, uintptr_t pinmux_base,
|
||||
uintptr_t pinconf_base)
|
||||
{
|
||||
iproc_gpio.base = base;
|
||||
iproc_gpio.nr_gpios = nr_gpios;
|
||||
|
||||
/* pinmux/pinconf base is optional for some SoCs */
|
||||
if (pinmux_base)
|
||||
iproc_gpio.pinmux_base = pinmux_base;
|
||||
|
||||
if (pinconf_base)
|
||||
iproc_gpio.pinconf_base = pinconf_base;
|
||||
|
||||
gpio_init(&iproc_gpio_ops);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2016 - 2021, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <drivers/delay_timer.h>
|
||||
#include <lib/mmio.h>
|
||||
#include <mdio.h>
|
||||
|
||||
static int mdio_op_status(uint32_t result)
|
||||
{
|
||||
uint32_t timeout = 1000000U; /* loop for 1s */
|
||||
uint32_t val;
|
||||
|
||||
do {
|
||||
val = mmio_read_32(CMIC_MIIM_STAT);
|
||||
if ((val & MDIO_STAT_DONE) == result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
udelay(1U);
|
||||
} while (timeout-- != 0U);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int mdio_op(uint16_t busid, uint16_t phyid, uint32_t reg,
|
||||
uint16_t val, uint8_t op)
|
||||
{
|
||||
uint32_t param;
|
||||
int ret;
|
||||
|
||||
mmio_write_32(CMIC_MIIM_CTRL, 0U);
|
||||
ret = mdio_op_status(0U);
|
||||
if (ret != 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
param = 0U;
|
||||
param |= 1U << MDIO_PARAM_INTERNAL_SEL;
|
||||
param |= (busid & MDIO_PARAM_BUSID_MASK) << MDIO_PARAM_BUSID;
|
||||
param |= (phyid & MDIO_PARAM_PHYID_MASK) << MDIO_PARAM_PHYID;
|
||||
param |= (val & MDIO_PARAM_DATA_MASK) << MDIO_PARAM_DATA;
|
||||
|
||||
mmio_write_32(CMIC_MIIM_PARAM, param);
|
||||
|
||||
mmio_write_32(CMIC_MIIM_ADDRESS, reg);
|
||||
|
||||
mmio_write_32(CMIC_MIIM_CTRL, op);
|
||||
|
||||
ret = mdio_op_status(1U);
|
||||
if (ret != 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (op == MDIO_CTRL_READ_OP) {
|
||||
ret = mmio_read_32(CMIC_MIIM_READ_DATA) & MDIO_READ_DATA_MASK;
|
||||
}
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mdio_write(uint16_t busid, uint16_t phyid, uint32_t reg, uint16_t val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mdio_op(busid, phyid, reg, val, MDIO_CTRL_WRITE_OP);
|
||||
if (ret == -1) {
|
||||
INFO("MDIO write fail\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mdio_read(uint16_t busid, uint16_t phyid, uint32_t reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = mdio_op(busid, phyid, reg, 0U, MDIO_CTRL_READ_OP);
|
||||
if (ret == -1) {
|
||||
INFO("MDIO read fail\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) 2017 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <drivers/delay_timer.h>
|
||||
#include <lib/mmio.h>
|
||||
|
||||
#include <ocotp.h>
|
||||
#include <platform_def.h>
|
||||
|
||||
#define OTP_MAP 2
|
||||
#define OTP_NUM_WORDS 2048
|
||||
/*
|
||||
* # of tries for OTP Status. The time to execute a command varies. The slowest
|
||||
* commands are writes which also vary based on the # of bits turned on. Writing
|
||||
* 0xffffffff takes ~3800 us.
|
||||
*/
|
||||
#define OTPC_RETRIES_US 5000
|
||||
|
||||
/* Sequence to enable OTP program */
|
||||
#define OTPC_PROG_EN_SEQ { 0xf, 0x4, 0x8, 0xd }
|
||||
|
||||
/* OTPC Commands */
|
||||
#define OTPC_CMD_READ 0x0
|
||||
#define OTPC_CMD_OTP_PROG_ENABLE 0x2
|
||||
#define OTPC_CMD_OTP_PROG_DISABLE 0x3
|
||||
#define OTPC_CMD_PROGRAM 0x8
|
||||
#define OTPC_CMD_ECC 0x10
|
||||
#define OTPC_ECC_ADDR 0x1A
|
||||
#define OTPC_ECC_VAL 0x00EC0000
|
||||
|
||||
/* OTPC Status Bits */
|
||||
#define OTPC_STAT_CMD_DONE BIT(1)
|
||||
#define OTPC_STAT_PROG_OK BIT(2)
|
||||
|
||||
/* OTPC register definition */
|
||||
#define OTPC_MODE_REG_OFFSET 0x0
|
||||
#define OTPC_MODE_REG_OTPC_MODE 0
|
||||
#define OTPC_COMMAND_OFFSET 0x4
|
||||
#define OTPC_COMMAND_COMMAND_WIDTH 6
|
||||
#define OTPC_CMD_START_OFFSET 0x8
|
||||
#define OTPC_CMD_START_START 0
|
||||
#define OTPC_CPU_STATUS_OFFSET 0xc
|
||||
#define OTPC_CPUADDR_REG_OFFSET 0x28
|
||||
#define OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH 16
|
||||
#define OTPC_CPU_WRITE_REG_OFFSET 0x2c
|
||||
|
||||
#define OTPC_CMD_MASK (BIT(OTPC_COMMAND_COMMAND_WIDTH) - 1)
|
||||
#define OTPC_ADDR_MASK (BIT(OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH) - 1)
|
||||
|
||||
#define OTPC_MODE_REG OCOTP_REGS_BASE
|
||||
|
||||
struct chip_otp_cfg {
|
||||
uint32_t base;
|
||||
uint32_t num_words;
|
||||
};
|
||||
|
||||
struct chip_otp_cfg ocotp_cfg = {
|
||||
.base = OTPC_MODE_REG,
|
||||
.num_words = 2048,
|
||||
};
|
||||
|
||||
struct otpc_priv {
|
||||
uint32_t base;
|
||||
struct otpc_map *map;
|
||||
int size;
|
||||
int state;
|
||||
};
|
||||
|
||||
struct otpc_priv otpc_info;
|
||||
|
||||
static inline void set_command(uint32_t base, uint32_t command)
|
||||
{
|
||||
mmio_write_32(base + OTPC_COMMAND_OFFSET, command & OTPC_CMD_MASK);
|
||||
}
|
||||
|
||||
static inline void set_cpu_address(uint32_t base, uint32_t addr)
|
||||
{
|
||||
mmio_write_32(base + OTPC_CPUADDR_REG_OFFSET, addr & OTPC_ADDR_MASK);
|
||||
}
|
||||
|
||||
static inline void set_start_bit(uint32_t base)
|
||||
{
|
||||
mmio_write_32(base + OTPC_CMD_START_OFFSET, 1 << OTPC_CMD_START_START);
|
||||
}
|
||||
|
||||
static inline void reset_start_bit(uint32_t base)
|
||||
{
|
||||
mmio_write_32(base + OTPC_CMD_START_OFFSET, 0);
|
||||
}
|
||||
|
||||
static inline void write_cpu_data(uint32_t base, uint32_t value)
|
||||
{
|
||||
mmio_write_32(base + OTPC_CPU_WRITE_REG_OFFSET, value);
|
||||
}
|
||||
|
||||
static int poll_cpu_status(uint32_t base, uint32_t value)
|
||||
{
|
||||
uint32_t status;
|
||||
uint32_t retries;
|
||||
|
||||
for (retries = 0; retries < OTPC_RETRIES_US; retries++) {
|
||||
status = mmio_read_32(base + OTPC_CPU_STATUS_OFFSET);
|
||||
if (status & value)
|
||||
break;
|
||||
udelay(1);
|
||||
}
|
||||
if (retries == OTPC_RETRIES_US)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_otpc_ecc(uint32_t enable)
|
||||
{
|
||||
struct otpc_priv *priv = &otpc_info;
|
||||
int ret;
|
||||
|
||||
set_command(priv->base, OTPC_CMD_ECC);
|
||||
set_cpu_address(priv->base, OTPC_ECC_ADDR);
|
||||
|
||||
if (!enable)
|
||||
write_cpu_data(priv->base, OTPC_ECC_VAL);
|
||||
else
|
||||
write_cpu_data(priv->base, ~OTPC_ECC_VAL);
|
||||
|
||||
set_start_bit(priv->base);
|
||||
ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
|
||||
if (ret) {
|
||||
ERROR("otp ecc op error: 0x%x", ret);
|
||||
return -1;
|
||||
}
|
||||
reset_start_bit(priv->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* bcm_otpc_read read otp data in the size of 8 byte rows.
|
||||
* bytes has to be the multiple of 8.
|
||||
* return -1 in error case, return read bytes in success.
|
||||
*/
|
||||
int bcm_otpc_read(unsigned int offset, void *val, uint32_t bytes,
|
||||
uint32_t ecc_flag)
|
||||
{
|
||||
struct otpc_priv *priv = &otpc_info;
|
||||
uint32_t *buf = val;
|
||||
uint32_t bytes_read;
|
||||
uint32_t address = offset / priv->map->word_size;
|
||||
int i, ret;
|
||||
|
||||
if (!priv->state) {
|
||||
ERROR("OCOTP read failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bcm_otpc_ecc(ecc_flag);
|
||||
|
||||
for (bytes_read = 0; (bytes_read + priv->map->word_size) <= bytes;) {
|
||||
set_command(priv->base, OTPC_CMD_READ);
|
||||
set_cpu_address(priv->base, address++);
|
||||
set_start_bit(priv->base);
|
||||
ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
|
||||
if (ret) {
|
||||
ERROR("otp read error: 0x%x", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->map->otpc_row_size; i++) {
|
||||
*buf++ = mmio_read_32(priv->base +
|
||||
priv->map->data_r_offset[i]);
|
||||
bytes_read += sizeof(*buf);
|
||||
}
|
||||
|
||||
reset_start_bit(priv->base);
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
int bcm_otpc_init(struct otpc_map *map)
|
||||
{
|
||||
struct otpc_priv *priv;
|
||||
|
||||
priv = &otpc_info;
|
||||
priv->base = ocotp_cfg.base;
|
||||
priv->map = map;
|
||||
|
||||
priv->size = 4 * ocotp_cfg.num_words;
|
||||
|
||||
/* Enable CPU access to OTPC. */
|
||||
mmio_setbits_32(priv->base + OTPC_MODE_REG_OFFSET,
|
||||
BIT(OTPC_MODE_REG_OTPC_MODE));
|
||||
reset_start_bit(priv->base);
|
||||
priv->state = 1;
|
||||
VERBOSE("OTPC Initialization done\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2017 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <drivers/delay_timer.h>
|
||||
#include <lib/mmio.h>
|
||||
#include <platform_def.h>
|
||||
|
||||
#define RNG_CTRL_REG (RNG_BASE_ADDR + 0x00)
|
||||
#define RNG_CTRL_MASK 0x00001FFF
|
||||
#define RNG_CTRL_ENABLE 0x00000001
|
||||
#define RNG_CTRL_DISABLE 0x00000000
|
||||
|
||||
#define RNG_SOFT_RESET_REG (RNG_BASE_ADDR + 0x04)
|
||||
#define RNG_SOFT_RESET_MASK 0x00000001
|
||||
|
||||
#define RNG_FIFO_DATA_REG (RNG_BASE_ADDR + 0x20)
|
||||
|
||||
#define RNG_FIFO_COUNT_REG (RNG_BASE_ADDR + 0x24)
|
||||
#define RNG_FIFO_COUNT_MASK 0x000000FF
|
||||
|
||||
#define RNG_FIFO_WORDS_MAX 16
|
||||
#define MAX_WAIT_COUNT_50US 20000
|
||||
|
||||
|
||||
static void rng_reset(void)
|
||||
{
|
||||
/* Disable RBG */
|
||||
mmio_clrbits_32(RNG_CTRL_REG, RNG_CTRL_MASK);
|
||||
|
||||
/* Reset RNG and RBG */
|
||||
mmio_setbits_32(RNG_SOFT_RESET_REG, RNG_SOFT_RESET_MASK);
|
||||
|
||||
/* Take all out of reset */
|
||||
mmio_clrbits_32(RNG_SOFT_RESET_REG, RNG_SOFT_RESET_MASK);
|
||||
}
|
||||
|
||||
static void rng_enable(void)
|
||||
{
|
||||
/* Setup RNG. */
|
||||
mmio_clrsetbits_32(RNG_CTRL_REG, RNG_CTRL_MASK, RNG_CTRL_ENABLE);
|
||||
}
|
||||
|
||||
int rng_init(void)
|
||||
{
|
||||
rng_reset();
|
||||
|
||||
rng_enable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rng_read(uint32_t *p_out, uint32_t *words_read)
|
||||
{
|
||||
uint32_t available_words;
|
||||
uint32_t i;
|
||||
uint32_t word_processed = 0;
|
||||
uint32_t wait_count = MAX_WAIT_COUNT_50US;
|
||||
|
||||
if (*words_read == 0) {
|
||||
ERROR("RNG Parameter: No word requested\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
available_words = mmio_read_32(RNG_FIFO_COUNT_REG);
|
||||
available_words &= RNG_FIFO_COUNT_MASK;
|
||||
|
||||
if (available_words != 0) {
|
||||
available_words = MIN(available_words,
|
||||
*words_read - word_processed);
|
||||
|
||||
for (i = 0; i < available_words; i++)
|
||||
p_out[word_processed + i] =
|
||||
mmio_read_32(RNG_FIFO_DATA_REG);
|
||||
word_processed += available_words;
|
||||
} else {
|
||||
udelay(50);
|
||||
}
|
||||
|
||||
if (word_processed == *words_read)
|
||||
break;
|
||||
|
||||
} while (--wait_count);
|
||||
|
||||
if (word_processed != *words_read) {
|
||||
ERROR("RNG Timeout: requested %d word(s) got %d\n",
|
||||
*words_read, word_processed);
|
||||
*words_read = word_processed;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2017 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <arch_helpers.h>
|
||||
#include <common/debug.h>
|
||||
|
||||
/* MCU binary image structure: <header> <data>
|
||||
*
|
||||
* Header structure:
|
||||
* <magic-start>
|
||||
* <num-sections>
|
||||
* {<src-offset> <src-size> <dst-addr>}*
|
||||
* <magic-end>
|
||||
*
|
||||
* MCU data (<data>) consists of several sections of code/data, to be
|
||||
* installed (copied) into MCU memories.
|
||||
* Header (<header>) gives information about sections contained in <data>.
|
||||
*
|
||||
* The installer code iterates over sections in MCU binary.
|
||||
* For each section, it copies the section into MCU memory.
|
||||
*
|
||||
* The header contains:
|
||||
* - <magic-start> - 32-bit magic number to mark header start
|
||||
* - <num-sections> - number of sections in <data>
|
||||
* - <num-sections> tuples. Each tuple describes a section.
|
||||
* A tuple contains three 32-bit words.
|
||||
* - <magic-end> - 32-bit magic number to mark header end
|
||||
*
|
||||
* Each section is describes by a tuple, consisting of three 32-bit words:
|
||||
* - offset of section within MCU binary (relative to beginning of <data>)
|
||||
* - section size (in bytes) in MCU binary
|
||||
* - target address (in MCU memory). Section is copied to this location.
|
||||
*
|
||||
* All fields are 32-bit unsigned integers in little endian format.
|
||||
* All sizes are assumed to be 32-bit aligned.
|
||||
*/
|
||||
|
||||
#define SCP_BIN_HEADER_MAGIC_START 0xfa587D01
|
||||
#define SCP_BIN_HEADER_MAGIC_END 0xf3e06a85
|
||||
|
||||
int download_scp_patch(void *image, unsigned int image_size)
|
||||
{
|
||||
unsigned int *pheader = (unsigned int *)(image);
|
||||
unsigned int header_size;
|
||||
unsigned char *pdata;
|
||||
void *dest;
|
||||
unsigned int num_sections;
|
||||
unsigned int section_src_offset;
|
||||
unsigned int section_size;
|
||||
|
||||
if (pheader && (pheader[0] != SCP_BIN_HEADER_MAGIC_START)) {
|
||||
ERROR("SCP: Could not find SCP header.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
num_sections = pheader[1];
|
||||
INFO("...Number of sections: %d\n", num_sections);
|
||||
header_size = 4 * (1 + 1 + 3 * num_sections + 1);
|
||||
|
||||
if (image_size < header_size) {
|
||||
ERROR("SCP: Wrong size.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*(pheader + header_size/4 - 1) != SCP_BIN_HEADER_MAGIC_END) {
|
||||
ERROR("SCP: Could not find SCP footer.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
VERBOSE("SCP image header validated successfully\n");
|
||||
pdata = (unsigned char *)pheader + header_size;
|
||||
|
||||
for (pheader += 2; num_sections > 0; num_sections--) {
|
||||
|
||||
section_src_offset = pheader[0];
|
||||
section_size = pheader[1];
|
||||
dest = (void *)(unsigned long)pheader[2];
|
||||
|
||||
INFO("section: src:0x%x, size:%d, dst:0x%x\n",
|
||||
section_src_offset, section_size, pheader[2]);
|
||||
|
||||
if ((section_src_offset + section_size) > image_size) {
|
||||
ERROR("SCP: Section points to outside of patch.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* copy from source to target section */
|
||||
memcpy(dest, pdata + section_src_offset, section_size);
|
||||
flush_dcache_range((uintptr_t)dest, section_size);
|
||||
|
||||
/* next section */
|
||||
pheader += 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <lib/mmio.h>
|
||||
#include <sotp.h>
|
||||
|
||||
#include <platform_def.h>
|
||||
#include <platform_sotp.h>
|
||||
|
||||
#ifdef USE_SOFT_SOTP
|
||||
extern uint64_t soft_sotp[];
|
||||
#endif
|
||||
|
||||
#define SOTP_PROG_CONTROL (SOTP_REGS_OTP_BASE + 0x0000)
|
||||
#define SOTP_PROG_CONTROL__OTP_CPU_MODE_EN 15
|
||||
#define SOTP_PROG_CONTROL__OTP_DISABLE_ECC 9
|
||||
#define SOTP_PROG_CONTROL__OTP_ECC_WREN 8
|
||||
|
||||
#define SOTP_WRDATA_0 (SOTP_REGS_OTP_BASE + 0x0004)
|
||||
#define SOTP_WRDATA_1 (SOTP_REGS_OTP_BASE + 0x0008)
|
||||
|
||||
#define SOTP_ADDR (SOTP_REGS_OTP_BASE + 0x000c)
|
||||
#define SOTP_ADDR__OTP_ROW_ADDR_R 6
|
||||
#define SOTP_ADDR_MASK 0x3FF
|
||||
|
||||
#define SOTP_CTRL_0 (SOTP_REGS_OTP_BASE + 0x0010)
|
||||
#define SOTP_CTRL_0__START 0
|
||||
#define SOTP_CTRL_0__OTP_CMD 1
|
||||
|
||||
#define SOTP_STATUS_0 (SOTP_REGS_OTP_BASE + 0x0018)
|
||||
#define SOTP_STATUS__FDONE 3
|
||||
|
||||
#define SOTP_STATUS_1 (SOTP_REGS_OTP_BASE + 0x001c)
|
||||
#define SOTP_STATUS_1__CMD_DONE 1
|
||||
#define SOTP_STATUS_1__ECC_DET 17
|
||||
|
||||
#define SOTP_RDDATA_0 (SOTP_REGS_OTP_BASE + 0x0020)
|
||||
#define SOTP_RDDATA_1 (SOTP_REGS_OTP_BASE + 0x0024)
|
||||
|
||||
#define SOTP_READ 0
|
||||
|
||||
#define SOTP_PROG_WORD 10
|
||||
#define SOTP_STATUS__PROGOK 2
|
||||
#define SOTP_PROG_ENABLE 2
|
||||
|
||||
#define SOTP_ROW_DATA_MASK 0xffffffff
|
||||
#define SOTP_ECC_ERR_BITS_MASK 0x1ff00000000
|
||||
|
||||
#define SOTP_CHIP_CTRL_SW_OVERRIDE_CHIP_STATES 4
|
||||
#define SOTP_CHIP_CTRL_SW_MANU_PROG 5
|
||||
#define SOTP_CHIP_CTRL_SW_CID_PROG 6
|
||||
#define SOTP_CHIP_CTRL_SW_AB_DEVICE 8
|
||||
#define SOTP_CHIP_CTRL_SW_AB_DEV_MODE 9
|
||||
#define CHIP_STATE_UNPROGRAMMED 0x1
|
||||
#define CHIP_STATE_UNASSIGNED 0x2
|
||||
|
||||
uint64_t sotp_mem_read(uint32_t offset, uint32_t sotp_add_ecc)
|
||||
{
|
||||
#ifdef USE_SOFT_SOTP
|
||||
(void)sotp_add_ecc;
|
||||
|
||||
return soft_sotp[offset];
|
||||
#else
|
||||
uint64_t read_data = 0;
|
||||
uint64_t read_data1 = 0;
|
||||
uint64_t read_data2 = 0;
|
||||
|
||||
/* Check for FDONE status */
|
||||
while ((mmio_read_32(SOTP_STATUS_0) & BIT(SOTP_STATUS__FDONE)) !=
|
||||
BIT(SOTP_STATUS__FDONE))
|
||||
;
|
||||
|
||||
/* Enable OTP access by CPU */
|
||||
mmio_setbits_32(SOTP_PROG_CONTROL,
|
||||
BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
|
||||
|
||||
if (sotp_add_ecc == 1) {
|
||||
mmio_clrbits_32(SOTP_PROG_CONTROL,
|
||||
BIT(SOTP_PROG_CONTROL__OTP_DISABLE_ECC));
|
||||
}
|
||||
|
||||
if (sotp_add_ecc == 0) {
|
||||
mmio_setbits_32(SOTP_PROG_CONTROL,
|
||||
BIT(SOTP_PROG_CONTROL__OTP_DISABLE_ECC));
|
||||
}
|
||||
|
||||
mmio_write_32(SOTP_ADDR,
|
||||
((offset & SOTP_ADDR_MASK) << SOTP_ADDR__OTP_ROW_ADDR_R));
|
||||
mmio_write_32(SOTP_CTRL_0, (SOTP_READ << SOTP_CTRL_0__OTP_CMD));
|
||||
|
||||
/* Start bit to tell SOTP to send command to the OTP controller */
|
||||
mmio_setbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
|
||||
|
||||
/* Wait for SOTP command done to be set */
|
||||
while ((mmio_read_32(SOTP_STATUS_1) & BIT(SOTP_STATUS_1__CMD_DONE)) !=
|
||||
BIT(SOTP_STATUS_1__CMD_DONE))
|
||||
;
|
||||
|
||||
/* Clr Start bit after command done */
|
||||
mmio_clrbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
|
||||
|
||||
if ((offset > SOTP_DEVICE_SECURE_CFG3_ROW) &&
|
||||
(mmio_read_32(SOTP_STATUS_1) & BIT(SOTP_STATUS_1__ECC_DET))) {
|
||||
ERROR("SOTP ECC ERROR Detected row offset %d\n", offset);
|
||||
read_data = SOTP_ECC_ERR_DETECT;
|
||||
} else {
|
||||
read_data1 = (uint64_t)mmio_read_32(SOTP_RDDATA_0);
|
||||
read_data1 = read_data1 & 0xFFFFFFFF;
|
||||
read_data2 = (uint64_t)mmio_read_32(SOTP_RDDATA_1);
|
||||
read_data2 = (read_data2 & 0x1ff) << 32;
|
||||
read_data = read_data1 | read_data2;
|
||||
}
|
||||
|
||||
/* Command done is cleared */
|
||||
mmio_setbits_32(SOTP_STATUS_1, BIT(SOTP_STATUS_1__CMD_DONE));
|
||||
|
||||
/* disable OTP access by CPU */
|
||||
mmio_clrbits_32(SOTP_PROG_CONTROL,
|
||||
BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
|
||||
|
||||
return read_data;
|
||||
#endif
|
||||
}
|
||||
|
||||
void sotp_mem_write(uint32_t addr, uint32_t sotp_add_ecc, uint64_t wdata)
|
||||
{
|
||||
#ifdef USE_SOFT_SOTP
|
||||
(void)sotp_add_ecc;
|
||||
|
||||
soft_sotp[addr] = wdata;
|
||||
#else
|
||||
uint32_t loop;
|
||||
uint8_t prog_array[4] = { 0x0F, 0x04, 0x08, 0x0D };
|
||||
|
||||
uint32_t chip_state_default =
|
||||
(CHIP_STATE_UNASSIGNED|CHIP_STATE_UNPROGRAMMED);
|
||||
uint32_t chip_state = mmio_read_32(SOTP_REGS_SOTP_CHIP_STATES);
|
||||
uint32_t chip_ctrl_default = 0;
|
||||
|
||||
/*
|
||||
* The override settings is required to allow the customer to program
|
||||
* the application specific keys into SOTP, before the conversion to
|
||||
* one of the AB modes.
|
||||
* At the end of write operation, the chip ctrl settings will restored
|
||||
* to the state prior to write call
|
||||
*/
|
||||
if (chip_state & chip_state_default) {
|
||||
uint32_t chip_ctrl;
|
||||
|
||||
chip_ctrl_default = mmio_read_32(SOTP_CHIP_CTRL);
|
||||
INFO("SOTP: enable special prog mode\n");
|
||||
|
||||
chip_ctrl = BIT(SOTP_CHIP_CTRL_SW_OVERRIDE_CHIP_STATES) |
|
||||
BIT(SOTP_CHIP_CTRL_SW_MANU_PROG) |
|
||||
BIT(SOTP_CHIP_CTRL_SW_CID_PROG) |
|
||||
BIT(SOTP_CHIP_CTRL_SW_AB_DEVICE);
|
||||
mmio_write_32(SOTP_CHIP_CTRL, chip_ctrl);
|
||||
}
|
||||
|
||||
/* Check for FDONE status */
|
||||
while ((mmio_read_32(SOTP_STATUS_0) & BIT(SOTP_STATUS__FDONE)) !=
|
||||
BIT(SOTP_STATUS__FDONE))
|
||||
;
|
||||
|
||||
/* Enable OTP acces by CPU */
|
||||
mmio_setbits_32(SOTP_PROG_CONTROL,
|
||||
BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
|
||||
|
||||
if (addr > SOTP_DEVICE_SECURE_CFG3_ROW) {
|
||||
if (sotp_add_ecc == 0) {
|
||||
mmio_clrbits_32(SOTP_PROG_CONTROL,
|
||||
BIT(SOTP_PROG_CONTROL__OTP_ECC_WREN));
|
||||
}
|
||||
if (sotp_add_ecc == 1) {
|
||||
mmio_setbits_32(SOTP_PROG_CONTROL,
|
||||
BIT(SOTP_PROG_CONTROL__OTP_ECC_WREN));
|
||||
}
|
||||
} else {
|
||||
mmio_clrbits_32(SOTP_PROG_CONTROL,
|
||||
BIT(SOTP_PROG_CONTROL__OTP_ECC_WREN));
|
||||
}
|
||||
|
||||
mmio_write_32(SOTP_CTRL_0, (SOTP_PROG_ENABLE << 1));
|
||||
|
||||
/*
|
||||
* In order to avoid unintentional writes / programming of the OTP
|
||||
* array, the OTP Controller must be put into programming mode before
|
||||
* it will accept program commands. This is done by writing 0xF, 0x4,
|
||||
* 0x8, 0xD with program commands prior to starting the actual
|
||||
* programming sequence
|
||||
*/
|
||||
for (loop = 0; loop < 4; loop++) {
|
||||
mmio_write_32(SOTP_WRDATA_0, prog_array[loop]);
|
||||
|
||||
/*
|
||||
* Start bit to tell SOTP to send command to the OTP controller
|
||||
*/
|
||||
mmio_setbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
|
||||
|
||||
/* Wait for SOTP command done to <-- be set */
|
||||
while ((mmio_read_32(SOTP_STATUS_1) &
|
||||
BIT(SOTP_STATUS_1__CMD_DONE)) !=
|
||||
BIT(SOTP_STATUS_1__CMD_DONE))
|
||||
;
|
||||
|
||||
/* Command done is cleared w1c */
|
||||
mmio_setbits_32(SOTP_STATUS_1, BIT(SOTP_STATUS_1__CMD_DONE));
|
||||
|
||||
/* Clr Start bit after command done */
|
||||
mmio_clrbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
|
||||
}
|
||||
|
||||
/* Check for PROGOK */
|
||||
while ((mmio_read_32(SOTP_STATUS_0) & 0x4) != BIT(SOTP_STATUS__PROGOK))
|
||||
;
|
||||
|
||||
/* Set 10 bit row address */
|
||||
mmio_write_32(SOTP_ADDR,
|
||||
((addr & SOTP_ADDR_MASK) << SOTP_ADDR__OTP_ROW_ADDR_R));
|
||||
|
||||
/* Set SOTP Row data */
|
||||
mmio_write_32(SOTP_WRDATA_0, (wdata & SOTP_ROW_DATA_MASK));
|
||||
|
||||
/* Set SOTP ECC and error bits */
|
||||
mmio_write_32(SOTP_WRDATA_1, ((wdata & SOTP_ECC_ERR_BITS_MASK) >> 32));
|
||||
|
||||
/* Set prog_word command */
|
||||
mmio_write_32(SOTP_CTRL_0, (SOTP_PROG_WORD << 1));
|
||||
|
||||
/* Start bit to tell SOTP to send command to the OTP controller */
|
||||
mmio_setbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
|
||||
|
||||
/* Wait for SOTP command done to be set */
|
||||
while ((mmio_read_32(SOTP_STATUS_1) & BIT(SOTP_STATUS_1__CMD_DONE)) !=
|
||||
BIT(SOTP_STATUS_1__CMD_DONE))
|
||||
;
|
||||
|
||||
/* Command done is cleared w1c */
|
||||
mmio_setbits_32(SOTP_STATUS_1, BIT(SOTP_STATUS_1__CMD_DONE));
|
||||
|
||||
/* disable OTP acces by CPU */
|
||||
mmio_clrbits_32(SOTP_PROG_CONTROL,
|
||||
BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
|
||||
|
||||
/* Clr Start bit after command done */
|
||||
mmio_clrbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
|
||||
|
||||
if (chip_state & chip_state_default)
|
||||
mmio_write_32(SOTP_CHIP_CTRL, chip_ctrl_default);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
int sotp_read_key(uint8_t *key, size_t keysize, int start_row, int end_row)
|
||||
{
|
||||
int row;
|
||||
uint32_t status = 0;
|
||||
uint32_t status2 = 0xFFFFFFFF;
|
||||
uint64_t row_data;
|
||||
uint32_t data;
|
||||
uint32_t *temp_key = (uint32_t *)key;
|
||||
|
||||
row = start_row;
|
||||
while ((keysize > 0) && (row <= end_row)) {
|
||||
row_data = sotp_mem_read(row, SOTP_ROW_ECC);
|
||||
if (!(row_data & (SOTP_ECC_ERR_DETECT | SOTP_FAIL_BITS))) {
|
||||
memcpy(temp_key++, &row_data, sizeof(uint32_t));
|
||||
keysize -= sizeof(uint32_t);
|
||||
data = (uint32_t)(row_data & SOTP_ROW_DATA_MASK);
|
||||
status |= data;
|
||||
status2 &= data;
|
||||
}
|
||||
row++;
|
||||
}
|
||||
|
||||
if ((status2 == 0xFFFFFFFF) || (status == 0) || (row > end_row))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sotp_key_erased(void)
|
||||
{
|
||||
uint64_t row_data;
|
||||
int status = 0;
|
||||
|
||||
row_data = sotp_mem_read(SOTP_DEVICE_SECURE_CFG0_ROW, 0);
|
||||
if (row_data & SOTP_DEVICE_SECURE_CFG0_OTP_ERASED_MASK)
|
||||
status = 1;
|
||||
|
||||
else if (mmio_read_32(SOTP_REGS_SOTP_CHIP_STATES) &
|
||||
SOTP_REGS_SOTP_CHIP_STATES_OTP_ERASED_MASK)
|
||||
status = 1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function optimise the SOTP redundancy
|
||||
* by considering the 00- zero and 01,10,11 - one
|
||||
*/
|
||||
uint32_t sotp_redundancy_reduction(uint32_t sotp_row_data)
|
||||
{
|
||||
uint32_t opt_data;
|
||||
uint32_t opt_loop;
|
||||
uint32_t temp_data;
|
||||
|
||||
opt_data = 0;
|
||||
|
||||
for (opt_loop = 0; opt_loop < 16; opt_loop = opt_loop + 1) {
|
||||
temp_data = ((sotp_row_data >> (opt_loop * 2)) & 0x3);
|
||||
|
||||
if (temp_data != 0x0)
|
||||
opt_data = (opt_data | (1 << opt_loop));
|
||||
}
|
||||
return opt_data;
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Copyright (c) 2017 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <drivers/delay_timer.h>
|
||||
#include <endian.h>
|
||||
#include <lib/mmio.h>
|
||||
|
||||
#include <platform_def.h>
|
||||
#include <spi.h>
|
||||
|
||||
#include "iproc_qspi.h"
|
||||
|
||||
struct bcmspi_priv spi_cfg;
|
||||
|
||||
/* Redefined by platform to force appropriate information */
|
||||
#pragma weak plat_spi_init
|
||||
int plat_spi_init(uint32_t *max_hz)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize & setup iproc qspi controller */
|
||||
int iproc_qspi_setup(uint32_t bus, uint32_t cs, uint32_t max_hz, uint32_t mode)
|
||||
{
|
||||
struct bcmspi_priv *priv = NULL;
|
||||
uint32_t spbr;
|
||||
|
||||
priv = &spi_cfg;
|
||||
priv->spi_mode = mode;
|
||||
priv->state = QSPI_STATE_DISABLED;
|
||||
priv->bspi_hw = QSPI_BSPI_MODE_REG_BASE;
|
||||
priv->mspi_hw = QSPI_MSPI_MODE_REG_BASE;
|
||||
|
||||
/* Initialize clock and platform specific */
|
||||
if (plat_spi_init(&max_hz) != 0)
|
||||
return -1;
|
||||
|
||||
priv->max_hz = max_hz;
|
||||
|
||||
/* MSPI: Basic hardware initialization */
|
||||
mmio_write_32(priv->mspi_hw + MSPI_SPCR1_LSB_REG, 0);
|
||||
mmio_write_32(priv->mspi_hw + MSPI_SPCR1_MSB_REG, 0);
|
||||
mmio_write_32(priv->mspi_hw + MSPI_NEWQP_REG, 0);
|
||||
mmio_write_32(priv->mspi_hw + MSPI_ENDQP_REG, 0);
|
||||
mmio_write_32(priv->mspi_hw + MSPI_SPCR2_REG, 0);
|
||||
|
||||
/* MSPI: SCK configuration */
|
||||
spbr = (QSPI_AXI_CLK - 1) / (2 * priv->max_hz) + 1;
|
||||
spbr = MIN(spbr, SPBR_DIV_MAX);
|
||||
spbr = MAX(spbr, SPBR_DIV_MIN);
|
||||
mmio_write_32(priv->mspi_hw + MSPI_SPCR0_LSB_REG, spbr);
|
||||
|
||||
/* MSPI: Mode configuration (8 bits by default) */
|
||||
priv->mspi_16bit = 0;
|
||||
mmio_write_32(priv->mspi_hw + MSPI_SPCR0_MSB_REG,
|
||||
BIT(MSPI_SPCR0_MSB_REG_MSTR_SHIFT) | /* Master */
|
||||
MSPI_SPCR0_MSB_REG_16_BITS_PER_WD_SHIFT | /* 16 bits per word */
|
||||
(priv->spi_mode & MSPI_SPCR0_MSB_REG_MODE_MASK)); /* mode: CPOL / CPHA */
|
||||
|
||||
/* Display bus info */
|
||||
VERBOSE("SPI: SPCR0_LSB: 0x%x\n",
|
||||
mmio_read_32(priv->mspi_hw + MSPI_SPCR0_LSB_REG));
|
||||
VERBOSE("SPI: SPCR0_MSB: 0x%x\n",
|
||||
mmio_read_32(priv->mspi_hw + MSPI_SPCR0_MSB_REG));
|
||||
VERBOSE("SPI: SPCR1_LSB: 0x%x\n",
|
||||
mmio_read_32(priv->mspi_hw + MSPI_SPCR1_LSB_REG));
|
||||
VERBOSE("SPI: SPCR1_MSB: 0x%x\n",
|
||||
mmio_read_32(priv->mspi_hw + MSPI_SPCR1_MSB_REG));
|
||||
VERBOSE("SPI: SPCR2: 0x%x\n",
|
||||
mmio_read_32(priv->mspi_hw + MSPI_SPCR2_REG));
|
||||
VERBOSE("SPI: CLK: %d\n", priv->max_hz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bcmspi_enable_bspi(struct bcmspi_priv *priv)
|
||||
{
|
||||
if (priv->state != QSPI_STATE_BSPI) {
|
||||
/* Switch to BSPI */
|
||||
mmio_write_32(priv->bspi_hw + BSPI_MAST_N_BOOT_CTRL_REG, 0);
|
||||
|
||||
priv->state = QSPI_STATE_BSPI;
|
||||
}
|
||||
}
|
||||
|
||||
static int bcmspi_disable_bspi(struct bcmspi_priv *priv)
|
||||
{
|
||||
uint32_t retry;
|
||||
|
||||
if (priv->state == QSPI_STATE_MSPI)
|
||||
return 0;
|
||||
|
||||
/* Switch to MSPI if not yet */
|
||||
if ((mmio_read_32(priv->bspi_hw + BSPI_MAST_N_BOOT_CTRL_REG) &
|
||||
MSPI_CTRL_MASK) == 0) {
|
||||
retry = QSPI_RETRY_COUNT_US_MAX;
|
||||
do {
|
||||
if ((mmio_read_32(
|
||||
priv->bspi_hw + BSPI_BUSY_STATUS_REG) &
|
||||
BSPI_BUSY_MASK) == 0) {
|
||||
mmio_write_32(priv->bspi_hw +
|
||||
BSPI_MAST_N_BOOT_CTRL_REG,
|
||||
MSPI_CTRL_MASK);
|
||||
udelay(1);
|
||||
break;
|
||||
}
|
||||
udelay(1);
|
||||
} while (retry--);
|
||||
|
||||
if ((mmio_read_32(priv->bspi_hw + BSPI_MAST_N_BOOT_CTRL_REG) &
|
||||
MSPI_CTRL_MASK) != MSPI_CTRL_MASK) {
|
||||
ERROR("QSPI: Switching to QSPI error.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update state */
|
||||
priv->state = QSPI_STATE_MSPI;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iproc_qspi_claim_bus(void)
|
||||
{
|
||||
struct bcmspi_priv *priv = &spi_cfg;
|
||||
|
||||
/* Switch to MSPI by default */
|
||||
if (bcmspi_disable_bspi(priv) != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iproc_qspi_release_bus(void)
|
||||
{
|
||||
struct bcmspi_priv *priv = &spi_cfg;
|
||||
|
||||
/* Switch to BSPI by default */
|
||||
bcmspi_enable_bspi(priv);
|
||||
}
|
||||
|
||||
static int mspi_xfer(struct bcmspi_priv *priv, uint32_t bytes,
|
||||
const uint8_t *tx, uint8_t *rx, uint32_t flag)
|
||||
{
|
||||
uint32_t retry;
|
||||
uint32_t mode = CDRAM_PCS0;
|
||||
|
||||
if (flag & SPI_XFER_QUAD) {
|
||||
mode |= CDRAM_QUAD_MODE;
|
||||
VERBOSE("SPI: QUAD mode\n");
|
||||
|
||||
if (!tx) {
|
||||
VERBOSE("SPI: 4 lane input\n");
|
||||
mode |= CDRAM_RBIT_INPUT;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use 8-bit queue for odd-bytes transfer */
|
||||
if (bytes & 1)
|
||||
priv->mspi_16bit = 0;
|
||||
else {
|
||||
priv->mspi_16bit = 1;
|
||||
mode |= CDRAM_BITS_EN;
|
||||
}
|
||||
|
||||
while (bytes) {
|
||||
uint32_t chunk;
|
||||
uint32_t queues;
|
||||
uint32_t i;
|
||||
|
||||
/* Separate code for 16bit and 8bit transfers for performance */
|
||||
if (priv->mspi_16bit) {
|
||||
VERBOSE("SPI: 16 bits xfer\n");
|
||||
/* Determine how many bytes to process this time */
|
||||
chunk = MIN(bytes, NUM_CDRAM_BYTES * 2);
|
||||
queues = (chunk - 1) / 2 + 1;
|
||||
bytes -= chunk;
|
||||
|
||||
/* Fill CDRAMs */
|
||||
for (i = 0; i < queues; i++)
|
||||
mmio_write_32(priv->mspi_hw + MSPI_CDRAM_REG +
|
||||
(i << 2), mode | CDRAM_CONT);
|
||||
|
||||
/* Fill TXRAMs */
|
||||
for (i = 0; i < chunk; i++)
|
||||
if (tx)
|
||||
mmio_write_32(priv->mspi_hw +
|
||||
MSPI_TXRAM_REG +
|
||||
(i << 2), tx[i]);
|
||||
} else {
|
||||
VERBOSE("SPI: 8 bits xfer\n");
|
||||
/* Determine how many bytes to process this time */
|
||||
chunk = MIN(bytes, NUM_CDRAM_BYTES);
|
||||
queues = chunk;
|
||||
bytes -= chunk;
|
||||
|
||||
/* Fill CDRAMs and TXRAMS */
|
||||
for (i = 0; i < chunk; i++) {
|
||||
mmio_write_32(priv->mspi_hw + MSPI_CDRAM_REG +
|
||||
(i << 2), mode | CDRAM_CONT);
|
||||
if (tx)
|
||||
mmio_write_32(priv->mspi_hw +
|
||||
MSPI_TXRAM_REG +
|
||||
(i << 3), tx[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Advance pointers */
|
||||
if (tx)
|
||||
tx += chunk;
|
||||
|
||||
/* Setup queue pointers */
|
||||
mmio_write_32(priv->mspi_hw + MSPI_NEWQP_REG, 0);
|
||||
mmio_write_32(priv->mspi_hw + MSPI_ENDQP_REG, queues - 1);
|
||||
|
||||
/* Remove CONT on the last byte command */
|
||||
if (bytes == 0 && (flag & SPI_XFER_END))
|
||||
mmio_write_32(priv->mspi_hw + MSPI_CDRAM_REG +
|
||||
((queues - 1) << 2), mode);
|
||||
|
||||
/* Kick off */
|
||||
mmio_write_32(priv->mspi_hw + MSPI_STATUS_REG, 0);
|
||||
if (bytes == 0 && (flag & SPI_XFER_END))
|
||||
mmio_write_32(priv->mspi_hw + MSPI_SPCR2_REG, MSPI_SPE);
|
||||
else
|
||||
mmio_write_32(priv->mspi_hw + MSPI_SPCR2_REG,
|
||||
MSPI_SPE | MSPI_CONT_AFTER_CMD);
|
||||
|
||||
/* Wait for completion */
|
||||
retry = QSPI_RETRY_COUNT_US_MAX;
|
||||
do {
|
||||
if (mmio_read_32(priv->mspi_hw + MSPI_STATUS_REG) &
|
||||
MSPI_CMD_COMPLETE_MASK)
|
||||
break;
|
||||
udelay(1);
|
||||
} while (retry--);
|
||||
|
||||
if ((mmio_read_32(priv->mspi_hw + MSPI_STATUS_REG) &
|
||||
MSPI_CMD_COMPLETE_MASK) == 0) {
|
||||
ERROR("SPI: Completion timeout.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read data out */
|
||||
if (rx) {
|
||||
if (priv->mspi_16bit) {
|
||||
for (i = 0; i < chunk; i++) {
|
||||
rx[i] = mmio_read_32(priv->mspi_hw +
|
||||
MSPI_RXRAM_REG +
|
||||
(i << 2))
|
||||
& 0xff;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < chunk; i++) {
|
||||
rx[i] = mmio_read_32(priv->mspi_hw +
|
||||
MSPI_RXRAM_REG +
|
||||
(((i << 1) + 1) << 2))
|
||||
& 0xff;
|
||||
}
|
||||
}
|
||||
rx += chunk;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iproc_qspi_xfer(uint32_t bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct bcmspi_priv *priv;
|
||||
const uint8_t *tx = dout;
|
||||
uint8_t *rx = din;
|
||||
uint32_t bytes = bitlen / 8;
|
||||
int ret = 0;
|
||||
|
||||
priv = &spi_cfg;
|
||||
|
||||
if (priv->state == QSPI_STATE_DISABLED) {
|
||||
ERROR("QSPI: state disabled\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we can only do 8 bit transfers */
|
||||
if (bitlen % 8) {
|
||||
ERROR("QSPI: Only support 8 bit transfers (requested %d)\n",
|
||||
bitlen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* MSPI: Enable write lock at the beginning */
|
||||
if (flags & SPI_XFER_BEGIN) {
|
||||
/* Switch to MSPI if not yet */
|
||||
if (bcmspi_disable_bspi(priv) != 0) {
|
||||
ERROR("QSPI: Switch to MSPI failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
mmio_write_32(priv->mspi_hw + MSPI_WRITE_LOCK_REG, 1);
|
||||
}
|
||||
|
||||
/* MSPI: Transfer it */
|
||||
if (bytes)
|
||||
ret = mspi_xfer(priv, bytes, tx, rx, flags);
|
||||
|
||||
/* MSPI: Disable write lock if it's done */
|
||||
if (flags & SPI_XFER_END)
|
||||
mmio_write_32(priv->mspi_hw + MSPI_WRITE_LOCK_REG, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2017 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef IPROC_QSPI_H
|
||||
#define IPROC_QSPI_H
|
||||
|
||||
#include <platform_def.h>
|
||||
|
||||
/*SPI configuration enable*/
|
||||
#define IPROC_QSPI_CLK_SPEED 62500000
|
||||
#define SPI_CPHA (1 << 0)
|
||||
#define SPI_CPOL (1 << 1)
|
||||
#define IPROC_QSPI_MODE0 0
|
||||
#define IPROC_QSPI_MODE3 (SPI_CPOL|SPI_CPHA)
|
||||
|
||||
#define IPROC_QSPI_BUS 0
|
||||
#define IPROC_QSPI_CS 0
|
||||
#define IPROC_QSPI_BASE_REG QSPI_CTRL_BASE_ADDR
|
||||
#define IPROC_QSPI_CRU_CONTROL_REG QSPI_CLK_CTRL
|
||||
|
||||
#define QSPI_AXI_CLK 200000000
|
||||
|
||||
#define QSPI_RETRY_COUNT_US_MAX 200000
|
||||
|
||||
/* Chip attributes */
|
||||
#define QSPI_REG_BASE IPROC_QSPI_BASE_REG
|
||||
#define CRU_CONTROL_REG IPROC_QSPI_CRU_CONTROL_REG
|
||||
#define SPBR_DIV_MIN 8U
|
||||
#define SPBR_DIV_MAX 255U
|
||||
#define NUM_CDRAM_BYTES 16U
|
||||
|
||||
/* Register fields */
|
||||
#define MSPI_SPCR0_MSB_BITS_8 0x00000020
|
||||
|
||||
/* Flash opcode and parameters */
|
||||
#define CDRAM_PCS0 2
|
||||
#define CDRAM_CONT (1 << 7)
|
||||
#define CDRAM_BITS_EN (1 << 6)
|
||||
#define CDRAM_QUAD_MODE (1 << 8)
|
||||
#define CDRAM_RBIT_INPUT (1 << 10)
|
||||
|
||||
/* MSPI registers */
|
||||
#define QSPI_MSPI_MODE_REG_BASE (QSPI_REG_BASE + 0x200)
|
||||
#define MSPI_SPCR0_LSB_REG 0x000
|
||||
#define MSPI_SPCR0_MSB_REG 0x004
|
||||
#define MSPI_SPCR1_LSB_REG 0x008
|
||||
#define MSPI_SPCR1_MSB_REG 0x00c
|
||||
#define MSPI_NEWQP_REG 0x010
|
||||
#define MSPI_ENDQP_REG 0x014
|
||||
#define MSPI_SPCR2_REG 0x018
|
||||
#define MSPI_STATUS_REG 0x020
|
||||
#define MSPI_CPTQP_REG 0x024
|
||||
#define MSPI_TXRAM_REG 0x040
|
||||
#define MSPI_RXRAM_REG 0x0c0
|
||||
#define MSPI_CDRAM_REG 0x140
|
||||
#define MSPI_WRITE_LOCK_REG 0x180
|
||||
#define MSPI_DISABLE_FLUSH_GEN_REG 0x184
|
||||
|
||||
#define MSPI_SPCR0_MSB_REG_MSTR_SHIFT 7
|
||||
#define MSPI_SPCR0_MSB_REG_16_BITS_PER_WD_SHIFT (0 << 2)
|
||||
#define MSPI_SPCR0_MSB_REG_MODE_MASK 0x3
|
||||
|
||||
/* BSPI registers */
|
||||
#define QSPI_BSPI_MODE_REG_BASE QSPI_REG_BASE
|
||||
#define BSPI_MAST_N_BOOT_CTRL_REG 0x008
|
||||
#define BSPI_BUSY_STATUS_REG 0x00c
|
||||
|
||||
#define MSPI_CMD_COMPLETE_MASK 1
|
||||
#define BSPI_BUSY_MASK 1
|
||||
#define MSPI_CTRL_MASK 1
|
||||
|
||||
#define MSPI_SPE (1 << 6)
|
||||
#define MSPI_CONT_AFTER_CMD (1 << 7)
|
||||
|
||||
/* State */
|
||||
enum bcm_qspi_state {
|
||||
QSPI_STATE_DISABLED,
|
||||
QSPI_STATE_MSPI,
|
||||
QSPI_STATE_BSPI
|
||||
};
|
||||
|
||||
/* QSPI private data */
|
||||
struct bcmspi_priv {
|
||||
/* Specified SPI parameters */
|
||||
uint32_t max_hz;
|
||||
uint32_t spi_mode;
|
||||
|
||||
/* State */
|
||||
enum bcm_qspi_state state;
|
||||
int mspi_16bit;
|
||||
|
||||
/* Registers */
|
||||
uintptr_t mspi_hw;
|
||||
uintptr_t bspi_hw;
|
||||
};
|
||||
|
||||
int iproc_qspi_setup(uint32_t bus, uint32_t cs,
|
||||
uint32_t max_hz, uint32_t mode);
|
||||
int iproc_qspi_claim_bus(void);
|
||||
void iproc_qspi_release_bus(void);
|
||||
int iproc_qspi_xfer(uint32_t bitlen, const void *dout,
|
||||
void *din, unsigned long flags);
|
||||
|
||||
#endif /* _IPROC_QSPI_H_ */
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2017 - 2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <spi.h>
|
||||
|
||||
#include "iproc_qspi.h"
|
||||
|
||||
int spi_init(void)
|
||||
{
|
||||
return iproc_qspi_setup(IPROC_QSPI_BUS, IPROC_QSPI_CS,
|
||||
IPROC_QSPI_CLK_SPEED, IPROC_QSPI_MODE0);
|
||||
}
|
||||
|
||||
int spi_claim_bus(void)
|
||||
{
|
||||
return iproc_qspi_claim_bus();
|
||||
}
|
||||
|
||||
void spi_release_bus(void)
|
||||
{
|
||||
iproc_qspi_release_bus();
|
||||
}
|
||||
|
||||
int spi_xfer(uint32_t bitlen, const void *dout,
|
||||
void *din, uint32_t flags)
|
||||
{
|
||||
return iproc_qspi_xfer(bitlen, dout, din, flags);
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <common/debug.h>
|
||||
#include <drivers/delay_timer.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sf.h>
|
||||
#include <spi.h>
|
||||
|
||||
#define SPI_FLASH_CMD_LEN 4
|
||||
#define QSPI_WAIT_TIMEOUT_US 200000U /* usec */
|
||||
|
||||
#define FINFO(jedec_id, ext_id, _sector_size, _n_sectors, _page_size, _flags) \
|
||||
.id = { \
|
||||
((jedec_id) >> 16) & 0xff, \
|
||||
((jedec_id) >> 8) & 0xff, \
|
||||
(jedec_id) & 0xff, \
|
||||
((ext_id) >> 8) & 0xff, \
|
||||
(ext_id) & 0xff, \
|
||||
}, \
|
||||
.id_len = (!(jedec_id) ? 0 : (3 + ((ext_id) ? 2 : 0))), \
|
||||
.sector_size = (_sector_size), \
|
||||
.n_sectors = (_n_sectors), \
|
||||
.page_size = _page_size, \
|
||||
.flags = (_flags),
|
||||
|
||||
/* SPI/QSPI flash device params structure */
|
||||
const struct spi_flash_info spi_flash_ids[] = {
|
||||
{"W25Q64CV", FINFO(0xef4017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)},
|
||||
{"W25Q64DW", FINFO(0xef6017, 0x0, 64 * 1024, 128, 256, WR_QPP | SECT_4K)},
|
||||
{"W25Q32", FINFO(0xef4016, 0x0, 64 * 1024, 64, 256, SECT_4K)},
|
||||
{"MX25l3205D", FINFO(0xc22016, 0x0, 64 * 1024, 64, 256, SECT_4K)},
|
||||
};
|
||||
|
||||
static void spi_flash_addr(uint32_t addr, uint8_t *cmd)
|
||||
{
|
||||
/*
|
||||
* cmd[0] holds a SPI Flash command, stored earlier
|
||||
* cmd[1/2/3] holds 24bit flash address
|
||||
*/
|
||||
cmd[1] = addr >> 16;
|
||||
cmd[2] = addr >> 8;
|
||||
cmd[3] = addr >> 0;
|
||||
}
|
||||
|
||||
static const struct spi_flash_info *spi_flash_read_id(void)
|
||||
{
|
||||
const struct spi_flash_info *info;
|
||||
uint8_t id[SPI_FLASH_MAX_ID_LEN];
|
||||
int ret;
|
||||
|
||||
ret = spi_flash_cmd(CMD_READ_ID, id, SPI_FLASH_MAX_ID_LEN);
|
||||
if (ret < 0) {
|
||||
ERROR("SF: Error %d reading JEDEC ID\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (info = spi_flash_ids; info->name != NULL; info++) {
|
||||
if (info->id_len) {
|
||||
if (!memcmp(info->id, id, info->id_len))
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
printf("SF: unrecognized JEDEC id bytes: %02x, %02x, %02x\n",
|
||||
id[0], id[1], id[2]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Enable writing on the SPI flash */
|
||||
static inline int spi_flash_cmd_write_enable(struct spi_flash *flash)
|
||||
{
|
||||
return spi_flash_cmd(CMD_WRITE_ENABLE, NULL, 0);
|
||||
}
|
||||
|
||||
static int spi_flash_cmd_wait(struct spi_flash *flash)
|
||||
{
|
||||
uint8_t cmd;
|
||||
uint32_t i;
|
||||
uint8_t status;
|
||||
int ret;
|
||||
|
||||
i = 0;
|
||||
while (1) {
|
||||
cmd = CMD_RDSR;
|
||||
ret = spi_flash_cmd_read(&cmd, 1, &status, 1);
|
||||
if (ret < 0) {
|
||||
ERROR("SF: cmd wait failed\n");
|
||||
break;
|
||||
}
|
||||
if (!(status & STATUS_WIP))
|
||||
break;
|
||||
|
||||
i++;
|
||||
if (i >= QSPI_WAIT_TIMEOUT_US) {
|
||||
ERROR("SF: cmd wait timeout\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_flash_write_common(struct spi_flash *flash, const uint8_t *cmd,
|
||||
size_t cmd_len, const void *buf,
|
||||
size_t buf_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = spi_flash_cmd_write_enable(flash);
|
||||
if (ret < 0) {
|
||||
ERROR("SF: enabling write failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_flash_cmd_write(cmd, cmd_len, buf, buf_len);
|
||||
if (ret < 0) {
|
||||
ERROR("SF: write cmd failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_flash_cmd_wait(flash);
|
||||
if (ret < 0) {
|
||||
ERROR("SF: write timed out\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int spi_flash_read_common(const uint8_t *cmd, size_t cmd_len,
|
||||
void *data, size_t data_len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = spi_flash_cmd_read(cmd, cmd_len, data, data_len);
|
||||
if (ret < 0) {
|
||||
ERROR("SF: read cmd failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int spi_flash_read(struct spi_flash *flash, uint32_t offset,
|
||||
uint32_t len, void *data)
|
||||
{
|
||||
uint32_t read_len = 0, read_addr;
|
||||
uint8_t cmd[SPI_FLASH_CMD_LEN];
|
||||
int ret;
|
||||
|
||||
ret = spi_claim_bus();
|
||||
if (ret) {
|
||||
ERROR("SF: unable to claim SPI bus\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
cmd[0] = CMD_READ_NORMAL;
|
||||
while (len) {
|
||||
read_addr = offset;
|
||||
read_len = MIN(flash->page_size, (len - read_len));
|
||||
spi_flash_addr(read_addr, cmd);
|
||||
|
||||
ret = spi_flash_read_common(cmd, sizeof(cmd), data, read_len);
|
||||
if (ret < 0) {
|
||||
ERROR("SF: read failed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
offset += read_len;
|
||||
len -= read_len;
|
||||
data += read_len;
|
||||
}
|
||||
SPI_DEBUG("SF read done\n");
|
||||
|
||||
spi_release_bus();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int spi_flash_write(struct spi_flash *flash, uint32_t offset,
|
||||
uint32_t len, void *buf)
|
||||
{
|
||||
unsigned long byte_addr, page_size;
|
||||
uint8_t cmd[SPI_FLASH_CMD_LEN];
|
||||
uint32_t chunk_len, actual;
|
||||
uint32_t write_addr;
|
||||
int ret;
|
||||
|
||||
ret = spi_claim_bus();
|
||||
if (ret) {
|
||||
ERROR("SF: unable to claim SPI bus\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
page_size = flash->page_size;
|
||||
|
||||
cmd[0] = flash->write_cmd;
|
||||
for (actual = 0; actual < len; actual += chunk_len) {
|
||||
write_addr = offset;
|
||||
byte_addr = offset % page_size;
|
||||
chunk_len = MIN(len - actual,
|
||||
(uint32_t)(page_size - byte_addr));
|
||||
spi_flash_addr(write_addr, cmd);
|
||||
|
||||
SPI_DEBUG("SF:0x%p=>cmd:{0x%02x 0x%02x%02x%02x} chunk_len:%d\n",
|
||||
buf + actual, cmd[0], cmd[1],
|
||||
cmd[2], cmd[3], chunk_len);
|
||||
|
||||
ret = spi_flash_write_common(flash, cmd, sizeof(cmd),
|
||||
buf + actual, chunk_len);
|
||||
if (ret < 0) {
|
||||
ERROR("SF: write cmd failed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
offset += chunk_len;
|
||||
}
|
||||
SPI_DEBUG("SF write done\n");
|
||||
|
||||
spi_release_bus();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int spi_flash_erase(struct spi_flash *flash, uint32_t offset, uint32_t len)
|
||||
{
|
||||
uint8_t cmd[SPI_FLASH_CMD_LEN];
|
||||
uint32_t erase_size, erase_addr;
|
||||
int ret;
|
||||
|
||||
erase_size = flash->erase_size;
|
||||
|
||||
if (offset % erase_size || len % erase_size) {
|
||||
ERROR("SF: Erase offset/length not multiple of erase size\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = spi_claim_bus();
|
||||
if (ret) {
|
||||
ERROR("SF: unable to claim SPI bus\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
cmd[0] = flash->erase_cmd;
|
||||
while (len) {
|
||||
erase_addr = offset;
|
||||
spi_flash_addr(erase_addr, cmd);
|
||||
|
||||
SPI_DEBUG("SF: erase %2x %2x %2x %2x (%x)\n", cmd[0], cmd[1],
|
||||
cmd[2], cmd[3], erase_addr);
|
||||
|
||||
ret = spi_flash_write_common(flash, cmd, sizeof(cmd), NULL, 0);
|
||||
if (ret < 0) {
|
||||
ERROR("SF: erase failed\n");
|
||||
break;
|
||||
}
|
||||
|
||||
offset += erase_size;
|
||||
len -= erase_size;
|
||||
}
|
||||
SPI_DEBUG("sf erase done\n");
|
||||
|
||||
spi_release_bus();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int spi_flash_probe(struct spi_flash *flash)
|
||||
{
|
||||
const struct spi_flash_info *info = NULL;
|
||||
int ret;
|
||||
|
||||
ret = spi_claim_bus();
|
||||
if (ret) {
|
||||
ERROR("SF: Unable to claim SPI bus\n");
|
||||
ERROR("SF: probe failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
info = spi_flash_read_id();
|
||||
if (!info)
|
||||
goto probe_fail;
|
||||
|
||||
INFO("Flash Name: %s sectors %x, sec size %x\n",
|
||||
info->name, info->n_sectors,
|
||||
info->sector_size);
|
||||
flash->size = info->n_sectors * info->sector_size;
|
||||
flash->sector_size = info->sector_size;
|
||||
flash->page_size = info->page_size;
|
||||
flash->flags = info->flags;
|
||||
|
||||
flash->read_cmd = CMD_READ_NORMAL;
|
||||
flash->write_cmd = CMD_PAGE_PROGRAM;
|
||||
flash->erase_cmd = CMD_ERASE_64K;
|
||||
flash->erase_size = ERASE_SIZE_64K;
|
||||
|
||||
probe_fail:
|
||||
spi_release_bus();
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020, Broadcom
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <common/debug.h>
|
||||
|
||||
#include <spi.h>
|
||||
|
||||
#define BITS_PER_BYTE 8
|
||||
#define CMD_LEN1 1
|
||||
|
||||
static int spi_flash_read_write(const uint8_t *cmd,
|
||||
size_t cmd_len,
|
||||
const uint8_t *data_out,
|
||||
uint8_t *data_in,
|
||||
size_t data_len)
|
||||
{
|
||||
unsigned long flags = SPI_XFER_BEGIN;
|
||||
int ret;
|
||||
|
||||
if (data_len == 0)
|
||||
flags |= SPI_XFER_END;
|
||||
|
||||
ret = spi_xfer(cmd_len * BITS_PER_BYTE, cmd, NULL, flags);
|
||||
if (ret) {
|
||||
ERROR("SF: Failed to send command (%zu bytes): %d\n",
|
||||
cmd_len, ret);
|
||||
} else if (data_len != 0) {
|
||||
ret = spi_xfer(data_len * BITS_PER_BYTE, data_out,
|
||||
data_in, SPI_XFER_END);
|
||||
if (ret)
|
||||
ERROR("SF: Failed to transfer %zu bytes of data: %d\n",
|
||||
data_len, ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int spi_flash_cmd_read(const uint8_t *cmd,
|
||||
size_t cmd_len,
|
||||
void *data,
|
||||
size_t data_len)
|
||||
{
|
||||
return spi_flash_read_write(cmd, cmd_len, NULL, data, data_len);
|
||||
}
|
||||
|
||||
int spi_flash_cmd(uint8_t cmd, void *response, size_t len)
|
||||
{
|
||||
return spi_flash_cmd_read(&cmd, CMD_LEN1, response, len);
|
||||
}
|
||||
|
||||
int spi_flash_cmd_write(const uint8_t *cmd,
|
||||
size_t cmd_len,
|
||||
const void *data,
|
||||
size_t data_len)
|
||||
{
|
||||
return spi_flash_read_write(cmd, cmd_len, data, NULL, data_len);
|
||||
}
|
||||
Reference in New Issue
Block a user