467 lines
13 KiB
C
467 lines
13 KiB
C
/*
|
|
* Copyright (c) 2023 Nuvoton Technology Corporation.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT nuvoton_npcx_espi_taf
|
|
|
|
#include <soc.h>
|
|
#include <zephyr/drivers/espi.h>
|
|
#include <zephyr/drivers/espi_saf.h>
|
|
#include <zephyr/drivers/flash.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(espi_taf, CONFIG_ESPI_LOG_LEVEL);
|
|
|
|
static const struct device *const spi_dev = DEVICE_DT_GET(DT_ALIAS(taf_flash));
|
|
|
|
struct espi_taf_npcx_config {
|
|
uintptr_t base;
|
|
uintptr_t mapped_addr;
|
|
uintptr_t rx_plsz;
|
|
enum NPCX_ESPI_TAF_ERASE_BLOCK_SIZE erase_sz;
|
|
enum NPCX_ESPI_TAF_MAX_READ_REQ max_rd_sz;
|
|
};
|
|
|
|
struct espi_taf_npcx_data {
|
|
sys_slist_t callbacks;
|
|
};
|
|
|
|
#define HAL_INSTANCE(dev) \
|
|
((struct espi_reg *)((const struct espi_taf_npcx_config *) \
|
|
(dev)->config)->base)
|
|
|
|
#define FLBASE_ADDR ( \
|
|
GET_FIELD(inst->FLASHBASE, NPCX_FLASHBASE_FLBASE_ADDR) \
|
|
<< GET_FIELD_POS(NPCX_FLASHBASE_FLBASE_ADDR))
|
|
|
|
#define PRTR_BADDR(i) ( \
|
|
GET_FIELD(inst->FLASH_PRTR_BADDR[i], NPCX_FLASH_PRTR_BADDR) \
|
|
<< GET_FIELD_POS(NPCX_FLASH_PRTR_BADDR))
|
|
|
|
#define PRTR_HADDR(i) ( \
|
|
GET_FIELD(inst->FLASH_PRTR_HADDR[i], NPCX_FLASH_PRTR_HADDR) \
|
|
<< GET_FIELD_POS(NPCX_FLASH_PRTR_HADDR)) | 0xFFF;
|
|
|
|
/* Check access region of read request is protected or not */
|
|
static bool espi_taf_check_read_protect(const struct device *dev, uint32_t addr, uint32_t len,
|
|
uint8_t tag)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
uint32_t flash_addr = addr;
|
|
uint8_t i;
|
|
uint16_t override_rd;
|
|
uint32_t base, high;
|
|
bool rdpr;
|
|
|
|
flash_addr += FLBASE_ADDR;
|
|
|
|
for (i = 0; i < CONFIG_ESPI_TAF_PR_NUM; i++) {
|
|
base = PRTR_BADDR(i);
|
|
high = PRTR_HADDR(i);
|
|
|
|
rdpr = IS_BIT_SET(inst->FLASH_PRTR_BADDR[i], NPCX_FRGN_RPR);
|
|
override_rd = GET_FIELD(inst->FLASH_RGN_TAG_OVR[i], NPCX_FLASH_TAG_OVR_RPR);
|
|
|
|
if (rdpr && !IS_BIT_SET(override_rd, tag) &&
|
|
(base <= flash_addr + len - 1 && flash_addr <= high)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Check access region of write request is protected or not */
|
|
static bool espi_taf_check_write_protect(const struct device *dev, uint32_t addr,
|
|
uint32_t len, uint8_t tag)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
uint32_t flash_addr = addr;
|
|
uint8_t i;
|
|
uint16_t override_wr;
|
|
uint32_t base, high;
|
|
bool wrpr;
|
|
|
|
flash_addr += FLBASE_ADDR;
|
|
|
|
for (i = 0; i < CONFIG_ESPI_TAF_PR_NUM; i++) {
|
|
base = PRTR_BADDR(i);
|
|
high = PRTR_HADDR(i);
|
|
|
|
wrpr = IS_BIT_SET(inst->FLASH_PRTR_BADDR[i], NPCX_FRGN_WPR);
|
|
override_wr = GET_FIELD(inst->FLASH_RGN_TAG_OVR[i], NPCX_FLASH_TAG_OVR_WPR);
|
|
|
|
if (wrpr && !IS_BIT_SET(override_wr, tag) &&
|
|
(base <= flash_addr + len - 1 && flash_addr <= high)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int espi_taf_npcx_configure(const struct device *dev, const struct espi_saf_cfg *cfg)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
|
|
#if defined(CONFIG_ESPI_TAF_AUTO_MODE)
|
|
inst->FLASHCTL |= BIT(NPCX_FLASHCTL_SAF_AUTO_READ);
|
|
#else
|
|
inst->FLASHCTL &= ~BIT(NPCX_FLASHCTL_SAF_AUTO_READ);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int espi_taf_npcx_set_pr(const struct device *dev, const struct espi_saf_protection *pr)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
const struct espi_saf_pr *preg = pr->pregions;
|
|
size_t n = pr->nregions;
|
|
uint8_t regnum;
|
|
uint16_t bitmask, offset;
|
|
uint32_t rw_pr, override_rw;
|
|
|
|
if ((dev == NULL) || (pr == NULL)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (pr->nregions >= CONFIG_ESPI_TAF_PR_NUM) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
while (n--) {
|
|
regnum = preg->pr_num;
|
|
|
|
if (regnum >= CONFIG_ESPI_TAF_PR_NUM) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
rw_pr = preg->master_bm_we << NPCX_FRGN_WPR;
|
|
rw_pr = rw_pr | (preg->master_bm_rd << NPCX_FRGN_RPR);
|
|
|
|
if (preg->flags) {
|
|
bitmask = BIT_MASK(GET_FIELD_SZ(NPCX_FLASH_PRTR_BADDR));
|
|
offset = GET_FIELD_POS(NPCX_FLASH_PRTR_BADDR);
|
|
inst->FLASH_PRTR_BADDR[regnum] = ((preg->start & bitmask) << offset)
|
|
| rw_pr;
|
|
bitmask = BIT_MASK(GET_FIELD_SZ(NPCX_FLASH_PRTR_HADDR));
|
|
offset = GET_FIELD_POS(NPCX_FLASH_PRTR_HADDR);
|
|
inst->FLASH_PRTR_HADDR[regnum] = (preg->end & bitmask) << offset;
|
|
}
|
|
|
|
override_rw = (preg->override_r << 16) | preg->override_w;
|
|
inst->FLASH_RGN_TAG_OVR[regnum] = override_rw;
|
|
preg++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int espi_taf_npcx_activate(const struct device *dev)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
|
|
inst->FLASHCTL &= ~BIT(NPCX_FLASHCTL_AUTO_RD_DIS_CTL);
|
|
inst->FLASHCTL &= ~BIT(NPCX_FLASHCTL_BLK_FLASH_NP_FREE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool espi_taf_npcx_channel_ready(const struct device *dev)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
|
|
if (!IS_BIT_SET(inst->ESPICFG, NPCX_ESPICFG_FLCHANMODE)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* This routine set FLASH_C_AVAIL for standard request */
|
|
static void taf_set_flash_c_avail(const struct device *dev)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
uint32_t tmp = inst->FLASHCTL;
|
|
|
|
/*
|
|
* Clear FLASHCTL_FLASH_NP_FREE to avoid host puts a flash
|
|
* standard request command at here.
|
|
*/
|
|
tmp &= NPCX_FLASHCTL_ACCESS_MASK;
|
|
|
|
/* Set FLASHCTL_FLASH_TX_AVAIL */
|
|
tmp |= BIT(NPCX_FLASHCTL_FLASH_TX_AVAIL);
|
|
inst->FLASHCTL = tmp;
|
|
}
|
|
|
|
/* This routine release FLASH_NP_FREE for standard request */
|
|
static void taf_release_flash_np_free(const struct device *dev)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
uint32_t tmp = inst->FLASHCTL;
|
|
|
|
/*
|
|
* Clear FLASHCTL_FLASH_TX_AVAIL to avoid host puts a
|
|
* GET_FLASH_C command at here.
|
|
*/
|
|
tmp &= NPCX_FLASHCTL_ACCESS_MASK;
|
|
|
|
/* Release FLASH_NP_FREE */
|
|
tmp |= BIT(NPCX_FLASHCTL_FLASH_NP_FREE);
|
|
inst->FLASHCTL = tmp;
|
|
}
|
|
|
|
static int taf_npcx_completion_handler(const struct device *dev, uint32_t *buffer)
|
|
{
|
|
uint16_t size = DIV_ROUND_UP((uint8_t)(buffer[0]) + 1, sizeof(uint32_t));
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
struct npcx_taf_head *head = (struct npcx_taf_head *)buffer;
|
|
uint8_t i;
|
|
|
|
/* Check the Flash Access TX Queue is empty by polling
|
|
* FLASH_TX_AVAIL.
|
|
*/
|
|
if (WAIT_FOR(IS_BIT_SET(inst->FLASHCTL, NPCX_FLASHCTL_FLASH_TX_AVAIL),
|
|
NPCX_FLASH_CHK_TIMEOUT, NULL)) {
|
|
LOG_ERR("Check TX Queue Is Empty Timeout");
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Check ESPISTS.FLNACS is clear (no slave completion is detected) */
|
|
if (WAIT_FOR(IS_BIT_SET(inst->ESPISTS, NPCX_ESPISTS_FLNACS),
|
|
NPCX_FLASH_CHK_TIMEOUT, NULL)) {
|
|
LOG_ERR("Check Slave Completion Timeout");
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Write packet to FLASHTXBUF */
|
|
for (i = 0; i < size; i++) {
|
|
inst->FLASHTXBUF[i] = buffer[i];
|
|
}
|
|
|
|
/* Set the FLASHCTL.FLASH_TX_AVAIL bit to 1 to enqueue the packet */
|
|
taf_set_flash_c_avail(dev);
|
|
|
|
/* Release FLASH_NP_FREE here to ready get next TAF request */
|
|
if ((head->type != CYC_SCS_CMP_WITH_DATA_FIRST) &&
|
|
(head->type != CYC_SCS_CMP_WITH_DATA_MIDDLE)) {
|
|
taf_release_flash_np_free(dev);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int espi_taf_npcx_flash_read(const struct device *dev, struct espi_saf_packet *pckt)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
struct espi_taf_npcx_config *config = ((struct espi_taf_npcx_config *)(dev)->config);
|
|
struct espi_taf_npcx_pckt *taf_data_ptr = (struct espi_taf_npcx_pckt *)pckt->buf;
|
|
uint8_t *data_ptr = (uint8_t *)taf_data_ptr->data;
|
|
uint8_t cycle_type = CYC_SCS_CMP_WITH_DATA_ONLY;
|
|
uint32_t total_len = pckt->len;
|
|
uint32_t len = total_len;
|
|
uint32_t addr = pckt->flash_addr;
|
|
uint8_t flash_req_size = GET_FIELD(inst->FLASHCFG, NPCX_FLASHCFG_FLASHREQSIZE);
|
|
uint8_t target_max_size = GET_FIELD(inst->FLASHCFG, NPCX_FLASHCFG_FLREQSUP);
|
|
uint16_t max_read_req = 32 << flash_req_size;
|
|
struct npcx_taf_head taf_head;
|
|
int rc;
|
|
|
|
if (flash_req_size > target_max_size) {
|
|
LOG_DBG("Exceeded the maximum supported length");
|
|
if (target_max_size == 0) {
|
|
target_max_size = 1;
|
|
}
|
|
max_read_req = 32 << target_max_size;
|
|
}
|
|
|
|
if (total_len > max_read_req) {
|
|
LOG_ERR("Exceeded the limitation of read length");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (espi_taf_check_read_protect(dev, addr, len, taf_data_ptr->tag)) {
|
|
LOG_ERR("Access protect region");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (total_len <= config->rx_plsz) {
|
|
cycle_type = CYC_SCS_CMP_WITH_DATA_ONLY;
|
|
len = total_len;
|
|
} else {
|
|
cycle_type = CYC_SCS_CMP_WITH_DATA_FIRST;
|
|
len = config->rx_plsz;
|
|
}
|
|
|
|
do {
|
|
data_ptr = (uint8_t *)taf_data_ptr->data;
|
|
|
|
taf_head.pkt_len = len + NPCX_TAF_CMP_HEADER_LEN;
|
|
taf_head.type = cycle_type;
|
|
taf_head.tag_hlen = (taf_data_ptr->tag << 4) | ((len & 0xF00) >> 8);
|
|
taf_head.llen = len & 0xFF;
|
|
memcpy(data_ptr, &taf_head, sizeof(taf_head));
|
|
|
|
rc = flash_read(spi_dev, addr, data_ptr + 4, len);
|
|
if (rc) {
|
|
LOG_ERR("flash read fail 0x%x", rc);
|
|
return -EIO;
|
|
}
|
|
|
|
rc = taf_npcx_completion_handler(dev, (uint32_t *)taf_data_ptr->data);
|
|
if (rc) {
|
|
LOG_ERR("espi taf completion handler fail");
|
|
return rc;
|
|
}
|
|
|
|
total_len -= len;
|
|
addr += len;
|
|
|
|
if (total_len <= config->rx_plsz) {
|
|
cycle_type = CYC_SCS_CMP_WITH_DATA_LAST;
|
|
len = total_len;
|
|
} else {
|
|
cycle_type = CYC_SCS_CMP_WITH_DATA_MIDDLE;
|
|
}
|
|
} while (total_len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int espi_taf_npcx_flash_write(const struct device *dev, struct espi_saf_packet *pckt)
|
|
{
|
|
struct espi_taf_npcx_pckt *taf_data_ptr = (struct espi_taf_npcx_pckt *)pckt->buf;
|
|
uint8_t *data_ptr = (uint8_t *)(taf_data_ptr->data);
|
|
struct npcx_taf_head taf_head;
|
|
int rc;
|
|
|
|
if (espi_taf_check_write_protect(dev, pckt->flash_addr,
|
|
pckt->len, taf_data_ptr->tag)) {
|
|
LOG_ERR("Access protection region");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = flash_write(spi_dev, pckt->flash_addr, data_ptr, pckt->len);
|
|
if (rc) {
|
|
LOG_ERR("flash write fail 0x%x", rc);
|
|
return -EIO;
|
|
}
|
|
|
|
taf_head.pkt_len = NPCX_TAF_CMP_HEADER_LEN;
|
|
taf_head.type = CYC_SCS_CMP_WITHOUT_DATA;
|
|
taf_head.tag_hlen = (taf_data_ptr->tag << 4);
|
|
taf_head.llen = 0x0;
|
|
memcpy(data_ptr, &taf_head, sizeof(taf_head));
|
|
|
|
rc = taf_npcx_completion_handler(dev, (uint32_t *)taf_data_ptr->data);
|
|
if (rc) {
|
|
LOG_ERR("espi taf completion handler fail");
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int espi_taf_npcx_flash_erase(const struct device *dev, struct espi_saf_packet *pckt)
|
|
{
|
|
struct espi_taf_npcx_pckt *taf_data_ptr = (struct espi_taf_npcx_pckt *)pckt->buf;
|
|
uint8_t *data_ptr = (uint8_t *)taf_data_ptr->data;
|
|
uint32_t addr = pckt->flash_addr;
|
|
uint32_t len = pckt->len;
|
|
struct npcx_taf_head taf_head;
|
|
int rc;
|
|
|
|
if (espi_taf_check_write_protect(dev, addr, len, taf_data_ptr->tag)) {
|
|
LOG_ERR("Access protection region");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = flash_erase(spi_dev, addr, len);
|
|
if (rc) {
|
|
LOG_ERR("flash erase fail");
|
|
return -EIO;
|
|
}
|
|
|
|
taf_head.pkt_len = NPCX_TAF_CMP_HEADER_LEN;
|
|
taf_head.type = CYC_SCS_CMP_WITHOUT_DATA;
|
|
taf_head.tag_hlen = (taf_data_ptr->tag << 4);
|
|
taf_head.llen = 0x0;
|
|
memcpy(data_ptr, &taf_head, sizeof(taf_head));
|
|
|
|
rc = taf_npcx_completion_handler(dev, (uint32_t *)taf_data_ptr->data);
|
|
if (rc) {
|
|
LOG_ERR("espi taf completion handler fail");
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int espi_taf_npcx_flash_unsuccess(const struct device *dev, struct espi_saf_packet *pckt)
|
|
{
|
|
struct espi_taf_npcx_pckt *taf_data_ptr
|
|
= (struct espi_taf_npcx_pckt *)pckt->buf;
|
|
uint8_t *data_ptr = (uint8_t *)taf_data_ptr->data;
|
|
struct npcx_taf_head taf_head;
|
|
int rc;
|
|
|
|
taf_head.pkt_len = NPCX_TAF_CMP_HEADER_LEN;
|
|
taf_head.type = CYC_UNSCS_CMP_WITHOUT_DATA_ONLY;
|
|
taf_head.tag_hlen = (taf_data_ptr->tag << 4);
|
|
taf_head.llen = 0x0;
|
|
memcpy(data_ptr, &taf_head, sizeof(taf_head));
|
|
|
|
rc = taf_npcx_completion_handler(dev, (uint32_t *)taf_data_ptr->data);
|
|
if (rc) {
|
|
LOG_ERR("espi taf completion handler fail");
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int espi_taf_npcx_init(const struct device *dev)
|
|
{
|
|
struct espi_reg *const inst = HAL_INSTANCE(dev);
|
|
struct espi_taf_npcx_config *config = ((struct espi_taf_npcx_config *)(dev)->config);
|
|
|
|
SET_FIELD(inst->FLASHCFG, NPCX_FLASHCFG_FLCAPA,
|
|
NPCX_FLASH_SHARING_CAP_SUPP_TAF_AND_CAF);
|
|
SET_FIELD(inst->FLASHCFG, NPCX_FLASHCFG_TRGFLEBLKSIZE,
|
|
BIT(config->erase_sz));
|
|
SET_FIELD(inst->FLASHCFG, NPCX_FLASHCFG_FLREQSUP,
|
|
config->max_rd_sz);
|
|
inst->FLASHBASE = config->mapped_addr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct espi_saf_driver_api espi_taf_npcx_driver_api = {
|
|
.config = espi_taf_npcx_configure,
|
|
.set_protection_regions = espi_taf_npcx_set_pr,
|
|
.activate = espi_taf_npcx_activate,
|
|
.get_channel_status = espi_taf_npcx_channel_ready,
|
|
.flash_read = espi_taf_npcx_flash_read,
|
|
.flash_write = espi_taf_npcx_flash_write,
|
|
.flash_erase = espi_taf_npcx_flash_erase,
|
|
.flash_unsuccess = espi_taf_npcx_flash_unsuccess,
|
|
};
|
|
|
|
static struct espi_taf_npcx_data npcx_espi_taf_data;
|
|
|
|
static const struct espi_taf_npcx_config espi_taf_npcx_config = {
|
|
.base = DT_INST_REG_ADDR(0),
|
|
.mapped_addr = DT_INST_PROP(0, mapped_addr),
|
|
.rx_plsz = DT_PROP(DT_INST_PARENT(0), rx_plsize),
|
|
.erase_sz = DT_INST_STRING_TOKEN(0, erase_sz),
|
|
.max_rd_sz = DT_INST_STRING_TOKEN(0, max_read_sz),
|
|
};
|
|
|
|
DEVICE_DT_INST_DEFINE(0, &espi_taf_npcx_init, NULL,
|
|
&npcx_espi_taf_data, &espi_taf_npcx_config,
|
|
PRE_KERNEL_2, CONFIG_ESPI_INIT_PRIORITY,
|
|
&espi_taf_npcx_driver_api);
|