343 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
#define LOG_TAG "SFCtrl"
#include "cts_config.h"
#include "cts_platform.h"
#include "cts_core.h"
#include "cts_sfctrl.h"
#define rSFCTRLv2_CMD_SEL (0x0000)
#define rSFCTRLv2_FLASH_ADDR (0x0004)
#define rSFCTRLv2_SRAM_ADDR (0x0008)
#define rSFCTRLv2_DATA_LENGTH (0x000C)
#define rSFCTRLv2_START_DEXC (0x0010)
#define rSFCTRLv2_RELEASE_FLASH (0x0014)
#define rSFCTRLv2_HW_STATE (0x0018)
#define rSFCTRLv2_CRC_RESULT (0x001C)
#define rSFCTRLv2_SRAM_CRC_START (0x0020)
#define rSFCTRLv2_FLASH_CRC_START (0x0022)
#define rSFCTRLv2_SF_BUSY (0x0024)
/** Constants for register @ref rSFCTRLv2_CMD_SEL */
enum sfctrlv2_cmd {
SFCTRLv2_CMD_FAST_READ = 0x01u,
SFCTRLv2_CMD_SE = 0x02u,
SFCTRLv2_CMD_BE = 0x03u,
SFCTRLv2_CMD_PP = 0x04u,
SFCTRLv2_CMD_RDSR = 0x05u,
SFCTRLv2_CMD_RDID = 0x06u
};
/** SPI flash controller v2 operation flags. */
enum sfctrlv2_opflags {
SFCTRLv2_OPFLAG_READ = BIT(0),
SFCTRLv2_OPFLAG_SET_FLASH_ADDR = BIT(1),
SFCTRLv2_OPFLAG_SRAM_DATA_XCHG = BIT(2),
SFCTRLv2_OPFLAG_SET_DATA_LENGTH = BIT(3),
SFCTRLv2_OPFLAG_WAIT_WIP_CLR = BIT(4),
};
#define SFCTRLv2_CMD_RDID_FLAGS \
(SFCTRLv2_OPFLAG_READ | \
SFCTRLv2_OPFLAG_SRAM_DATA_XCHG)
#define SFCTRLv2_CMD_RDSR_FLAGS \
(SFCTRLv2_OPFLAG_READ | \
SFCTRLv2_OPFLAG_SRAM_DATA_XCHG)
#define SFCTRLv2_CMD_SE_FLAGS \
(SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \
SFCTRLv2_OPFLAG_WAIT_WIP_CLR)
#define SFCTRLv2_CMD_BE_FLAGS \
(SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \
SFCTRLv2_OPFLAG_WAIT_WIP_CLR)
#define SFCTRLv2_CMD_PP_FLAGS \
(SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \
SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \
SFCTRLv2_OPFLAG_SET_DATA_LENGTH | \
SFCTRLv2_OPFLAG_WAIT_WIP_CLR)
#define SFCTRLv2_CMD_FAST_READ_FLAGS \
(SFCTRLv2_OPFLAG_READ | \
SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \
SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \
SFCTRLv2_OPFLAG_SET_DATA_LENGTH)
#define sfctrl_reg_addr(cts_dev, offset) \
((cts_dev)->hwdata->sfctrl->reg_base + offset)
#define DEFINE_SFCTRL_REG_ACCESS_FUNC(access_type, data_type) \
static inline int sfctrl_reg_ ## access_type(struct cts_device *cts_dev, \
u32 reg, data_type data) { \
return cts_hw_reg_ ## access_type(cts_dev, sfctrl_reg_addr(cts_dev, reg), data); \
}
DEFINE_SFCTRL_REG_ACCESS_FUNC(writeb_relaxed, u8)
DEFINE_SFCTRL_REG_ACCESS_FUNC(writew_relaxed, u16)
DEFINE_SFCTRL_REG_ACCESS_FUNC(writel_relaxed, u32)
DEFINE_SFCTRL_REG_ACCESS_FUNC(readb_relaxed, u8 *)
DEFINE_SFCTRL_REG_ACCESS_FUNC(readw_relaxed, u16 *)
DEFINE_SFCTRL_REG_ACCESS_FUNC(readl_relaxed, u32 *)
#define sfctrl_write_reg_check_ret(access_type, reg, val) \
do { \
int ret; \
cts_dbg("Write " #reg " to 0x%x", val); \
ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \
if (ret) { \
cts_err("Write " #reg " failed %d", ret); \
return ret; \
} \
} while (0)
#define sfctrl_read_reg_check_ret(access_type, reg, val) \
do { \
int ret; \
cts_dbg("Read " #reg ""); \
ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \
if (ret) { \
cts_err("Read " #reg " failed %d", ret); \
return ret; \
} \
} while (0)
static int wait_sfctrl_xfer_comp(struct cts_device *cts_dev)
{
int retries = 0;
u8 status;
do {
sfctrl_read_reg_check_ret(readb_relaxed, rSFCTRLv2_SF_BUSY, &status);
if (status == 0)
return 0;
mdelay(1);
} while (status && retries++ < 1000);
return -ETIMEDOUT;
}
static int sfctrlv2_rdsr(struct cts_device *cts_dev, u8 *status)
{
#define RDSR_XCHG_SRAM_ADDR (cts_dev->hwdata->sfctrl->xchg_sram_base + cts_dev->hwdata->sfctrl->xchg_sram_size - 1)
int ret;
sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL,
SFCTRLv2_CMD_RDSR);
sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR,
RDSR_XCHG_SRAM_ADDR);
sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1);
ret = wait_sfctrl_xfer_comp(cts_dev);
if (ret != 0) {
cts_err("Wait sfctrl ready failed %d", ret);
return ret;
}
ret = cts_sram_readb(cts_dev, RDSR_XCHG_SRAM_ADDR, status);
if (ret) {
cts_err("Read exchange sram failed %d", ret);
return ret;
}
#undef RDSR_XCHG_SRAM_ADDR
return 0;
}
static int wait_flash_wip_clear(struct cts_device *cts_dev)
{
#define FLASH_SR_WIP BIT(0) /*!< Write in progress */
int ret, retries = 0;
u8 status;
do {
ret = sfctrlv2_rdsr(cts_dev, &status);
if (ret) {
cts_err("Read flash status register failed %d", ret);
return ret;
}
if (status & FLASH_SR_WIP)
mdelay(1);
else
return 0;
} while (status & FLASH_SR_WIP && retries++ < 1000);
return -ETIMEDOUT;
#undef FLASH_SR_WIP
}
static int sfctrlv2_transfer(struct cts_device *cts_dev,
u8 cmd, void *data, u32 flash_addr, u32 sram_addr,
size_t size, u32 flags)
{
int ret;
sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL, cmd);
if (flags & SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) {
sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR,
sram_addr);
/** - Write data to exchange SRAM if operation is write and
* data != NULL(data not in SRAM)
*/
if ((!(flags & SFCTRLv2_OPFLAG_READ)) && data) {
ret = cts_sram_writesb(cts_dev, sram_addr, data, size);
if (ret) {
cts_err("Write data to exchange sram 0x%06x size %zu failed %d",
sram_addr, size, ret);
return ret;
}
}
}
if (flags & SFCTRLv2_OPFLAG_SET_FLASH_ADDR) {
sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR,
flash_addr);
}
if (flags & SFCTRLv2_OPFLAG_SET_DATA_LENGTH) {
sfctrl_write_reg_check_ret(writel_relaxed,
rSFCTRLv2_DATA_LENGTH, (u32) size);
}
sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1);
ret = wait_sfctrl_xfer_comp(cts_dev);
if (ret != 0) {
cts_err("Wait sfctrl ready failed %d", ret);
return ret;
}
ret = wait_flash_wip_clear(cts_dev);
if (flags & SFCTRLv2_OPFLAG_WAIT_WIP_CLR && ret != 0) {
cts_err("Wait WIP clear failed %d", ret);
return ret;
}
if ((flags & SFCTRLv2_OPFLAG_READ) && data) {
ret = cts_sram_readsb(cts_dev, sram_addr, data, size);
if (ret) {
cts_err("Read data from exchange sram 0x%06x size %zu failed %d",
sram_addr, size, ret);
return ret;
}
}
return 0;
}
static int sfctrlv2_rdid(struct cts_device *cts_dev, u32 *id)
{
int ret;
u8 buf[4];
ret = sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_RDID, buf,
0, cts_dev->hwdata->sfctrl->xchg_sram_base, 3,
SFCTRLv2_CMD_RDID_FLAGS);
*id = ret ? 0 : get_unaligned_be24(buf);
return ret;
}
static int sfctrlv2_se(struct cts_device *cts_dev, u32 sector_addr)
{
return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_SE, NULL,
sector_addr, 0, 0, SFCTRLv2_CMD_SE_FLAGS);
}
static int sfctrlv2_be(struct cts_device *cts_dev, u32 block_addr)
{
return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_BE, NULL,
block_addr, 0, 0, SFCTRLv2_CMD_BE_FLAGS);
}
static int sfctrlv2_pp(struct cts_device *cts_dev,
u32 flash_addr, const void *src, size_t size)
{
return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, (void *)src,
flash_addr, cts_dev->hwdata->sfctrl->xchg_sram_base, size,
SFCTRLv2_CMD_PP_FLAGS);
}
static int sfctrlv2_program_flash_from_sram(struct cts_device *cts_dev,
u32 flash_addr, u32 sram_addr, size_t len)
{
return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, NULL,
flash_addr, sram_addr, len, SFCTRLv2_CMD_PP_FLAGS);
}
static int sfctrlv2_read(struct cts_device *cts_dev,
u32 flash_addr, void *dst, size_t size)
{
return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, dst,
flash_addr, cts_dev->hwdata->sfctrl->xchg_sram_base, size,
SFCTRLv2_CMD_FAST_READ_FLAGS);
}
static int sfctrlv2_read_flash_to_sram(struct cts_device *cts_dev,
u32 flash_addr, u32 sram_addr, size_t size)
{
return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, NULL,
flash_addr, sram_addr, size, SFCTRLv2_CMD_FAST_READ_FLAGS);
}
static int sfctrlv2_calc_sram_crc(struct cts_device *cts_dev,
u32 sram_addr, size_t size, u32 *crc)
{
int ret;
sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, sram_addr);
mdelay(1);
sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, (u32) size);
mdelay(1);
sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_SRAM_CRC_START, 1);
mdelay(10);
ret = wait_sfctrl_xfer_comp(cts_dev);
if (ret != 0) {
cts_err("Wait sfctrl ready failed %d", ret);
return ret;
}
sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc);
return 0;
}
static int sfctrlv2_calc_flash_crc(struct cts_device *cts_dev,
u32 flash_addr, size_t size, u32 *crc)
{
int ret;
cts_info("Calc crc from flash 0x%06x size %zu", flash_addr, size);
sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR, flash_addr);
sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, (u32) size);
sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_FLASH_CRC_START, 1);
ret = wait_sfctrl_xfer_comp(cts_dev);
if (ret != 0) {
cts_err("Wait sfctrl ready failed %d", ret);
return ret;
}
sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc);
return 0;
}
const struct cts_sfctrl_ops cts_sfctrlv2_ops = {
.rdid = sfctrlv2_rdid,
.se = sfctrlv2_se,
.be = sfctrlv2_be,
.read = sfctrlv2_read,
.read_to_sram = sfctrlv2_read_flash_to_sram,
.program = sfctrlv2_pp,
.program_from_sram = sfctrlv2_program_flash_from_sram,
.calc_sram_crc = sfctrlv2_calc_sram_crc,
.calc_flash_crc = sfctrlv2_calc_flash_crc,
};