drivers: espi: Microchip eSPI add SAF support
Add ESPI SAF features to the Microchip eSPI driver as a separate file. ESPI SAF depends upon the core eSPI driver adding the ability to attach the system SPI flash to the EC eSPI endpoint instead of the host eSPI controller. Signed-off-by: Scott Worley <scott.worley@microchip.com>
This commit is contained in:
parent
5c649921de
commit
2b926db4e1
8 changed files with 1882 additions and 4 deletions
|
@ -3,7 +3,8 @@
|
|||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_ESPI_XEC espi_mchp_xec.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ESPI_NPCX espi_npcx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ESPI_NPCX host_subs_npcx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE espi_handlers.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ESPI_EMUL espi_emul.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ESPI_NPCX espi_npcx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ESPI_NPCX host_subs_npcx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE espi_handlers.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ESPI_EMUL espi_emul.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_ESPI_SAF espi_saf_mchp_xec.c)
|
||||
|
|
|
@ -37,4 +37,19 @@ config ESPI_PERIPHERAL_UART_SOC_MAPPING
|
|||
This tells the driver to which SoC UART to direct the UART traffic
|
||||
send over eSPI from host.
|
||||
|
||||
config ESPI_SAF
|
||||
bool "XEC Microchip ESPI SAF driver"
|
||||
depends on ESPI_FLASH_CHANNEL
|
||||
default n
|
||||
help
|
||||
Enable Slave Attached Flash eSPI driver. SAF depends upon ESPI XEC driver
|
||||
and flash channel.
|
||||
|
||||
config ESPI_SAF_INIT_PRIORITY
|
||||
int "ESPI SAF driver initialization priority"
|
||||
depends on ESPI_SAF
|
||||
default 4
|
||||
help
|
||||
Driver initialization priority for eSPI SAF driver.
|
||||
|
||||
endif #ESPI_XEC
|
||||
|
|
876
drivers/espi/espi_saf_mchp_xec.c
Normal file
876
drivers/espi/espi_saf_mchp_xec.c
Normal file
|
@ -0,0 +1,876 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Intel Corporation
|
||||
* Copyright (c) 2020 Microchip Technology Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT microchip_xec_espi_saf
|
||||
|
||||
#include <kernel.h>
|
||||
#include <soc.h>
|
||||
#include <errno.h>
|
||||
#include <drivers/espi.h>
|
||||
#include <drivers/espi_saf.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#include "espi_utils.h"
|
||||
LOG_MODULE_REGISTER(espi_saf, CONFIG_ESPI_LOG_LEVEL);
|
||||
|
||||
/* SAF EC Portal read/write flash access limited to 1-64 bytes */
|
||||
#define MAX_SAF_ECP_BUFFER_SIZE 64ul
|
||||
|
||||
/* 1 second maximum for flash operations */
|
||||
#define MAX_SAF_FLASH_TIMEOUT 125000ul /* 1000ul */
|
||||
|
||||
/* 64 bytes @ 24MHz quad is approx. 6 us */
|
||||
#define SAF_WAIT_INTERVAL 8
|
||||
|
||||
/* After 8 wait intervals yield */
|
||||
#define SAF_YIELD_THRESHOLD 64
|
||||
|
||||
struct espi_isr {
|
||||
uint32_t girq_bit;
|
||||
void (*the_isr)(const struct device *dev);
|
||||
};
|
||||
|
||||
/*
|
||||
* SAF configuration from Device Tree
|
||||
* SAF controller register block base address
|
||||
* QMSPI controller register block base address
|
||||
* SAF communications register block base address
|
||||
* Flash STATUS1 poll timeout in 32KHz periods
|
||||
* Flash consecutive read timeout in units of 20 ns
|
||||
* Delay before first Poll-1 command after suspend in 20 ns units
|
||||
* Hold off suspend for this interval if erase or program in 32KHz periods.
|
||||
* Add delay between Poll STATUS1 commands in 20 ns units.
|
||||
*/
|
||||
struct espi_saf_xec_config {
|
||||
uintptr_t saf_base_addr;
|
||||
uintptr_t qmspi_base_addr;
|
||||
uintptr_t saf_comm_base_addr;
|
||||
uint32_t poll_timeout;
|
||||
uint32_t consec_rd_timeout;
|
||||
uint32_t sus_chk_delay;
|
||||
uint16_t sus_rsm_interval;
|
||||
uint16_t poll_interval;
|
||||
};
|
||||
|
||||
struct espi_saf_xec_data {
|
||||
sys_slist_t callbacks;
|
||||
struct k_sem ecp_lock;
|
||||
uint32_t hwstatus;
|
||||
};
|
||||
|
||||
/* convenience defines */
|
||||
#define DEV_CFG(dev) ((const struct espi_saf_xec_config *const)(dev)->config)
|
||||
#define DEV_DATA(dev) ((struct espi_saf_xec_data *const)(dev)->data)
|
||||
|
||||
/* EC portal local flash r/w buffer */
|
||||
static uint32_t slave_mem[MAX_SAF_ECP_BUFFER_SIZE];
|
||||
|
||||
/*
|
||||
* @brief eSPI SAF configuration
|
||||
*/
|
||||
|
||||
static inline void mchp_saf_cs_descr_wr(MCHP_SAF_HW_REGS *regs, uint8_t cs,
|
||||
uint32_t val)
|
||||
{
|
||||
regs->SAF_CS_OP[cs].OP_DESCR = val;
|
||||
}
|
||||
|
||||
static inline void mchp_saf_poll2_mask_wr(MCHP_SAF_HW_REGS *regs, uint8_t cs,
|
||||
uint16_t val)
|
||||
{
|
||||
LOG_DBG("%s cs: %d mask %x", __func__, cs, val);
|
||||
if (cs == 0) {
|
||||
regs->SAF_CS0_CFG_P2M = val;
|
||||
} else {
|
||||
regs->SAF_CS1_CFG_P2M = val;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mchp_saf_cm_prefix_wr(MCHP_SAF_HW_REGS *regs, uint8_t cs,
|
||||
uint16_t val)
|
||||
{
|
||||
if (cs == 0) {
|
||||
regs->SAF_CS0_CM_PRF = val;
|
||||
} else {
|
||||
regs->SAF_CS1_CM_PRF = val;
|
||||
}
|
||||
}
|
||||
|
||||
/* busy wait or yield until we have SAF interrupt support */
|
||||
static int xec_saf_spin_yield(int *counter)
|
||||
{
|
||||
*counter = *counter + 1;
|
||||
|
||||
if (*counter > MAX_SAF_FLASH_TIMEOUT) {
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (*counter > SAF_YIELD_THRESHOLD) {
|
||||
k_yield();
|
||||
} else {
|
||||
k_busy_wait(SAF_WAIT_INTERVAL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize SAF flash protection regions.
|
||||
* SAF HW implements 17 protection regions.
|
||||
* At least one protection region must be configured to allow
|
||||
* EC access to the local flash through the EC Portal.
|
||||
* Each protection region is composed of 4 32-bit registers
|
||||
* Start bits[19:0] = bits[31:12] region start address (4KB boundaries)
|
||||
* Limit bits[19:0] = bits[31:12] region limit address (4KB boundaries)
|
||||
* Write protect b[7:0] = masters[7:0] allow write/erase. 1=allowed
|
||||
* Read protetc b[7:0] = masters[7:0] allow read. 1=allowed
|
||||
*
|
||||
* This routine configures protection region 0 for full flash array
|
||||
* address range and read-write-erase for all masters.
|
||||
* This routine must be called AFTER the flash configuration size/limit and
|
||||
* threshold registers have been programmed.
|
||||
*
|
||||
* POR default values:
|
||||
* Start = 0x7ffff
|
||||
* Limit = 0
|
||||
* Write Prot = 0x01 Master 0 always granted write/erase
|
||||
* Read Prot = 0x01 Master 0 always granted read
|
||||
*
|
||||
* Sample code configures PR[0]
|
||||
* Start = 0
|
||||
* Limit = 0x7ffff
|
||||
* WR = 0xFF
|
||||
* RD = 0xFF
|
||||
*/
|
||||
static void saf_protection_regions_init(MCHP_SAF_HW_REGS *regs)
|
||||
{
|
||||
LOG_DBG("%s", __func__);
|
||||
|
||||
for (size_t n = 0; n < MCHP_ESPI_SAF_PR_MAX; n++) {
|
||||
if (n == 0) {
|
||||
regs->SAF_PROT_RG[0].START = 0U;
|
||||
regs->SAF_PROT_RG[0].LIMIT =
|
||||
regs->SAF_FL_CFG_SIZE_LIM >> 12;
|
||||
regs->SAF_PROT_RG[0].WEBM = MCHP_SAF_MSTR_ALL;
|
||||
regs->SAF_PROT_RG[0].RDBM = MCHP_SAF_MSTR_ALL;
|
||||
} else {
|
||||
regs->SAF_PROT_RG[n].START =
|
||||
MCHP_SAF_PROT_RG_START_DFLT;
|
||||
regs->SAF_PROT_RG[n].LIMIT =
|
||||
MCHP_SAF_PROT_RG_LIMIT_DFLT;
|
||||
regs->SAF_PROT_RG[n].WEBM = 0U;
|
||||
regs->SAF_PROT_RG[n].RDBM = 0U;
|
||||
}
|
||||
|
||||
LOG_DBG("PROT[%d] START %x", n, regs->SAF_PROT_RG[n].START);
|
||||
LOG_DBG("PROT[%d] LIMIT %x", n, regs->SAF_PROT_RG[n].LIMIT);
|
||||
LOG_DBG("PROT[%d] WEBM %x", n, regs->SAF_PROT_RG[n].WEBM);
|
||||
LOG_DBG("PROT[%d] RDBM %x", n, regs->SAF_PROT_RG[n].RDBM);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t qmspi_freq_div(uint32_t freqhz)
|
||||
{
|
||||
uint32_t fdiv;
|
||||
|
||||
if (freqhz < (MCHP_QMSPI_MIN_FREQ_KHZ * 1000U)) {
|
||||
fdiv = 0U; /* freq divider field -> 256 */
|
||||
} else if (freqhz >= (MCHP_QMSPI_MAX_FREQ_KHZ * 1000U)) {
|
||||
fdiv = 1U;
|
||||
} else {
|
||||
/* truncation produces next higher integer frequency */
|
||||
fdiv = MCHP_QMSPI_INPUT_CLOCK_FREQ_HZ / freqhz;
|
||||
}
|
||||
|
||||
fdiv &= MCHP_QMSPI_M_FDIV_MASK0;
|
||||
fdiv <<= MCHP_QMSPI_M_FDIV_POS;
|
||||
|
||||
return fdiv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Take over and re-initialize QMSPI for use by SAF HW engine.
|
||||
* When SAF is activated, QMSPI registers are controlled by SAF
|
||||
* HW engine. CPU no longer has access to QMSPI registers.
|
||||
* 1. Save QMSPI driver frequency divider, SPI signalling mode, and
|
||||
* chip select timing.
|
||||
* 2. Put QMSPI controller in a known state by performing a soft reset.
|
||||
* 3. Clear QMSPI GIRQ status
|
||||
* 4. Configure QMSPI interface control for SAF.
|
||||
* 5. Load flash device independent (generic) descriptors.
|
||||
* 6. Enable transfer done interrupt in QMSPI
|
||||
* 7. Enable QMSPI SAF mode
|
||||
* 8. If user configuration overrides frequency, signalling mode,
|
||||
* or chip select timing derive user values.
|
||||
* 9. Program QMSPI MODE and CSTIM registers with activate set.
|
||||
*/
|
||||
static int saf_qmspi_init(const struct espi_saf_xec_config *xcfg,
|
||||
const struct espi_saf_cfg *cfg)
|
||||
{
|
||||
uint32_t qmode, cstim, n;
|
||||
QMSPI_Type *regs = (QMSPI_Type *)xcfg->qmspi_base_addr;
|
||||
const struct espi_saf_hw_cfg *hwcfg = &cfg->hwcfg;
|
||||
|
||||
qmode = regs->MODE;
|
||||
if (!(qmode & MCHP_QMSPI_M_ACTIVATE)) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
qmode = regs->MODE & (MCHP_QMSPI_M_FDIV_MASK | MCHP_QMSPI_M_SIG_MASK);
|
||||
cstim = regs->CSTM;
|
||||
regs->MODE = MCHP_QMSPI_M_SRST;
|
||||
regs->STS = MCHP_QMSPI_STS_RW1C_MASK;
|
||||
|
||||
MCHP_GIRQ_ENCLR(MCHP_QMSPI_GIRQ_NUM) = MCHP_QMSPI_GIRQ_VAL;
|
||||
MCHP_GIRQ_SRC(MCHP_QMSPI_GIRQ_NUM) = MCHP_QMSPI_GIRQ_VAL;
|
||||
|
||||
regs->IFCTRL =
|
||||
(MCHP_QMSPI_IFC_WP_OUT_HI | MCHP_QMSPI_IFC_WP_OUT_EN |
|
||||
MCHP_QMSPI_IFC_HOLD_OUT_HI | MCHP_QMSPI_IFC_HOLD_OUT_EN);
|
||||
|
||||
for (n = 0; n < MCHP_SAF_NUM_GENERIC_DESCR; n++) {
|
||||
regs->DESCR[MCHP_SAF_CM_EXIT_START_DESCR + n] =
|
||||
hwcfg->generic_descr[n];
|
||||
}
|
||||
|
||||
regs->IEN = MCHP_QMSPI_IEN_XFR_DONE;
|
||||
|
||||
qmode |= (MCHP_QMSPI_M_SAF_DMA_MODE_EN | MCHP_QMSPI_M_CS0 |
|
||||
MCHP_QMSPI_M_ACTIVATE);
|
||||
|
||||
if (hwcfg->flags & MCHP_SAF_HW_CFG_FLAG_CPHA) {
|
||||
qmode = (qmode & ~(MCHP_QMSPI_M_SIG_MASK)) |
|
||||
((hwcfg->qmspi_cpha << MCHP_QMSPI_M_SIG_POS) &
|
||||
MCHP_QMSPI_M_SIG_MASK);
|
||||
}
|
||||
|
||||
if (hwcfg->flags & MCHP_SAF_HW_CFG_FLAG_FREQ) {
|
||||
qmode = (qmode & ~(MCHP_QMSPI_M_FDIV_MASK)) |
|
||||
qmspi_freq_div(hwcfg->qmspi_freq_hz);
|
||||
}
|
||||
|
||||
if (hwcfg->flags & MCHP_SAF_HW_CFG_FLAG_CSTM) {
|
||||
cstim = hwcfg->qmspi_cs_timing;
|
||||
}
|
||||
|
||||
regs->MODE = qmode;
|
||||
regs->CSTM = cstim;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Registers at offsets:
|
||||
* SAF Poll timeout @ 0x194. Hard coded to 0x28000. Default value = 0.
|
||||
* recommended value = 0x28000 32KHz clocks (5 seconds). b[17:0]
|
||||
* SAF Poll interval @ 0x198. Hard coded to 0
|
||||
* Default value = 0. Recommended = 0. b[15:0]
|
||||
* SAF Suspend/Resume Interval @ 0x19c. Hard coded to 0x8
|
||||
* Default value = 0x01. Min time erase/prog in 32KHz units.
|
||||
* SAF Consecutive Read Timeout @ 0x1a0. Hard coded to 0x2. b[15:0]
|
||||
* Units of MCLK. Recommend < 20us. b[19:0]
|
||||
* SAF Suspend Check Delay @ 0x1ac. Not touched.
|
||||
* Default = 0. Recommend = 20us. Units = MCLK. b[19:0]
|
||||
*/
|
||||
static void saf_flash_timing_init(MCHP_SAF_HW_REGS *regs,
|
||||
const struct espi_saf_xec_config *cfg)
|
||||
{
|
||||
LOG_DBG("%s\n", __func__);
|
||||
regs->SAF_POLL_TMOUT = cfg->poll_timeout;
|
||||
regs->SAF_POLL_INTRVL = cfg->poll_interval;
|
||||
regs->SAF_SUS_RSM_INTRVL = cfg->sus_rsm_interval;
|
||||
regs->SAF_CONSEC_RD_TMOUT = cfg->consec_rd_timeout;
|
||||
regs->SAF_SUS_CHK_DLY = cfg->sus_chk_delay;
|
||||
LOG_DBG("SAF_POLL_TMOUT %x\n", regs->SAF_POLL_TMOUT);
|
||||
LOG_DBG("SAF_POLL_INTRVL %x\n", regs->SAF_POLL_INTRVL);
|
||||
LOG_DBG("SAF_SUS_RSM_INTRVL %x\n", regs->SAF_SUS_RSM_INTRVL);
|
||||
LOG_DBG("SAF_CONSEC_RD_TMOUT %x\n", regs->SAF_CONSEC_RD_TMOUT);
|
||||
LOG_DBG("SAF_SUS_CHK_DLY %x\n", regs->SAF_SUS_CHK_DLY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable DnX bypass feature.
|
||||
*/
|
||||
static void saf_dnx_bypass_init(MCHP_SAF_HW_REGS *regs)
|
||||
{
|
||||
regs->SAF_DNX_PROT_BYP = 0;
|
||||
regs->SAF_DNX_PROT_BYP = 0xffffffff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitmap of flash erase size from 1KB up to 128KB.
|
||||
* eSPI SAF specification requires 4KB erase support.
|
||||
* MCHP SAF supports 4KB, 32KB, and 64KB.
|
||||
* Only report 32KB and 64KB to Host if supported by both
|
||||
* flash devices.
|
||||
*/
|
||||
static int saf_init_erase_block_size(const struct espi_saf_cfg *cfg)
|
||||
{
|
||||
struct espi_saf_flash_cfg *fcfg = cfg->flash_cfgs;
|
||||
uint32_t opb = fcfg->opb;
|
||||
uint8_t erase_bitmap = MCHP_ESPI_SERASE_SZ_4K;
|
||||
|
||||
LOG_DBG("%s\n", __func__);
|
||||
|
||||
if (cfg->nflash_devices > 1) {
|
||||
fcfg++;
|
||||
opb &= fcfg->opb;
|
||||
}
|
||||
|
||||
if ((opb & MCHP_SAF_CS_OPB_ER0_MASK) == 0) {
|
||||
/* One or both do not support 4KB erase! */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (opb & MCHP_SAF_CS_OPB_ER1_MASK) {
|
||||
erase_bitmap |= MCHP_ESPI_SERASE_SZ_32K;
|
||||
}
|
||||
|
||||
if (opb & MCHP_SAF_CS_OPB_ER2_MASK) {
|
||||
erase_bitmap |= MCHP_ESPI_SERASE_SZ_64K;
|
||||
}
|
||||
|
||||
ESPI_CAP_REGS->FC_SERBZ = erase_bitmap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the continuous mode prefix and 4-byte address mode bits
|
||||
* based upon the flash configuration information.
|
||||
* Updates:
|
||||
* SAF Flash Config Poll2 Mask @ 0x1A4
|
||||
* SAF Flash Config Special Mode @ 0x1B0
|
||||
* SAF Flash Misc Config @ 0x38
|
||||
*/
|
||||
static void saf_flash_misc_cfg(MCHP_SAF_HW_REGS *regs, uint8_t cs,
|
||||
const struct espi_saf_flash_cfg *fcfg)
|
||||
{
|
||||
uint32_t d, v;
|
||||
|
||||
d = regs->SAF_FL_CFG_MISC;
|
||||
|
||||
v = MCHP_SAF_FL_CFG_MISC_CS0_CPE;
|
||||
if (cs) {
|
||||
v = MCHP_SAF_FL_CFG_MISC_CS1_CPE;
|
||||
}
|
||||
|
||||
/* Does this flash device require a prefix for continuous mode? */
|
||||
if (fcfg->cont_prefix != 0) {
|
||||
d |= v;
|
||||
} else {
|
||||
d &= ~v;
|
||||
}
|
||||
|
||||
v = MCHP_SAF_FL_CFG_MISC_CS0_4BM;
|
||||
if (cs) {
|
||||
v = MCHP_SAF_FL_CFG_MISC_CS1_4BM;
|
||||
}
|
||||
|
||||
/* Use 32-bit addressing for this flash device? */
|
||||
if (fcfg->flags & MCHP_FLASH_FLAG_ADDR32) {
|
||||
d |= v;
|
||||
} else {
|
||||
d &= ~v;
|
||||
}
|
||||
|
||||
regs->SAF_FL_CFG_MISC = d;
|
||||
LOG_DBG("%s SAF_FL_CFG_MISC: %x", __func__, d);
|
||||
}
|
||||
|
||||
/*
|
||||
* Program flash device specific SAF and QMSPI registers.
|
||||
*
|
||||
* CS0 OpA @ 0x4c or CS1 OpA @ 0x5C
|
||||
* CS0 OpB @ 0x50 or CS1 OpB @ 0x60
|
||||
* CS0 OpC @ 0x54 or CS1 OpC @ 0x64
|
||||
* Poll 2 Mask @ 0x1a4
|
||||
* Continuous Prefix @ 0x1b0
|
||||
* CS0: QMSPI descriptors 0-5 or CS1 QMSPI descriptors 6-11
|
||||
* CS0 Descrs @ 0x58 or CS1 Descrs @ 0x68
|
||||
*/
|
||||
static void saf_flash_cfg(const struct device *dev,
|
||||
const struct espi_saf_flash_cfg *fcfg, uint8_t cs)
|
||||
{
|
||||
uint32_t d, did;
|
||||
const struct espi_saf_xec_config *xcfg = DEV_CFG(dev);
|
||||
MCHP_SAF_HW_REGS *regs = (MCHP_SAF_HW_REGS *)xcfg->saf_base_addr;
|
||||
QMSPI_Type *qregs = (QMSPI_Type *)xcfg->qmspi_base_addr;
|
||||
|
||||
LOG_DBG("%s cs=%u", __func__, cs);
|
||||
|
||||
regs->SAF_CS_OP[cs].OPA = fcfg->opa;
|
||||
regs->SAF_CS_OP[cs].OPB = fcfg->opb;
|
||||
regs->SAF_CS_OP[cs].OPC = fcfg->opc;
|
||||
regs->SAF_CS_OP[cs].OP_DESCR = (uint32_t)fcfg->cs_cfg_descr_ids;
|
||||
|
||||
did = MCHP_SAF_QMSPI_CS0_START_DESCR;
|
||||
if (cs != 0) {
|
||||
did = MCHP_SAF_QMSPI_CS1_START_DESCR;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MCHP_SAF_QMSPI_NUM_FLASH_DESCR; i++) {
|
||||
d = fcfg->descr[i] & ~(MCHP_QMSPI_C_NEXT_DESCR_MASK);
|
||||
d |= (((did + 1) << MCHP_QMSPI_C_NEXT_DESCR_POS) &
|
||||
MCHP_QMSPI_C_NEXT_DESCR_MASK);
|
||||
qregs->DESCR[did++] = d;
|
||||
}
|
||||
|
||||
mchp_saf_poll2_mask_wr(regs, cs, fcfg->poll2_mask);
|
||||
mchp_saf_cm_prefix_wr(regs, cs, fcfg->cont_prefix);
|
||||
saf_flash_misc_cfg(regs, cs, fcfg);
|
||||
}
|
||||
|
||||
static const uint32_t tag_map_dflt[MCHP_ESPI_SAF_TAGMAP_MAX] = {
|
||||
MCHP_SAF_TAG_MAP0_DFLT, MCHP_SAF_TAG_MAP1_DFLT, MCHP_SAF_TAG_MAP2_DFLT
|
||||
};
|
||||
|
||||
static void saf_tagmap_init(MCHP_SAF_HW_REGS *regs,
|
||||
const struct espi_saf_cfg *cfg)
|
||||
{
|
||||
const struct espi_saf_hw_cfg *hwcfg = &cfg->hwcfg;
|
||||
|
||||
for (int i = 0; i < MCHP_ESPI_SAF_TAGMAP_MAX; i++) {
|
||||
if (hwcfg->tag_map[i] & MCHP_SAF_HW_CFG_TAGMAP_USE) {
|
||||
regs->SAF_TAG_MAP[i] = hwcfg->tag_map[i];
|
||||
} else {
|
||||
regs->SAF_TAG_MAP[i] = tag_map_dflt[i];
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DBG("SAF TAG0 %x", regs->SAF_TAG_MAP[0]);
|
||||
LOG_DBG("SAF TAG1 %x", regs->SAF_TAG_MAP[1]);
|
||||
LOG_DBG("SAF TAG2 %x", regs->SAF_TAG_MAP[2]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure SAF and QMSPI for SAF operation based upon the
|
||||
* number and characteristics of local SPI flash devices.
|
||||
* NOTE: SAF is configured but not activated. SAF should be
|
||||
* activated only when eSPI master sends Flash Channel enable
|
||||
* message with MAF/SAF select flag.
|
||||
*/
|
||||
static int espi_saf_xec_configuration(const struct device *dev,
|
||||
const struct espi_saf_cfg *cfg)
|
||||
{
|
||||
int ret = 0;
|
||||
uint32_t totalsz = 0;
|
||||
uint32_t u = 0;
|
||||
|
||||
LOG_DBG("%s", __func__);
|
||||
|
||||
if ((dev == NULL) || (cfg == NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const struct espi_saf_xec_config *xcfg = DEV_CFG(dev);
|
||||
MCHP_SAF_HW_REGS *regs = (MCHP_SAF_HW_REGS *)xcfg->saf_base_addr;
|
||||
const struct espi_saf_flash_cfg *fcfg = cfg->flash_cfgs;
|
||||
|
||||
if ((fcfg == NULL) || (cfg->nflash_devices == 0U) ||
|
||||
(cfg->nflash_devices > MCHP_SAF_MAX_FLASH_DEVICES)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (regs->SAF_FL_CFG_MISC & MCHP_SAF_FL_CFG_MISC_SAF_EN) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
saf_qmspi_init(xcfg, cfg);
|
||||
|
||||
regs->SAF_CS0_CFG_P2M = 0;
|
||||
regs->SAF_CS1_CFG_P2M = 0;
|
||||
|
||||
regs->SAF_FL_CFG_GEN_DESCR = MCHP_SAF_FL_CFG_GEN_DESCR_STD;
|
||||
|
||||
/* flash device connected to CS0 required */
|
||||
totalsz = fcfg->flashsz;
|
||||
regs->SAF_FL_CFG_THRH = totalsz;
|
||||
saf_flash_cfg(dev, fcfg, 0);
|
||||
|
||||
/* optional second flash device connected to CS1 */
|
||||
if (cfg->nflash_devices > 1) {
|
||||
fcfg++;
|
||||
totalsz += fcfg->flashsz;
|
||||
}
|
||||
/* Program CS1 configuration (same as CS0 if only one device) */
|
||||
saf_flash_cfg(dev, fcfg, 1);
|
||||
|
||||
if (totalsz == 0) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
regs->SAF_FL_CFG_SIZE_LIM = totalsz - 1;
|
||||
|
||||
LOG_DBG("SAF_FL_CFG_THRH = %x SAF_FL_CFG_SIZE_LIM = %x",
|
||||
regs->SAF_FL_CFG_THRH, regs->SAF_FL_CFG_SIZE_LIM);
|
||||
|
||||
saf_tagmap_init(regs, cfg);
|
||||
|
||||
saf_protection_regions_init(regs);
|
||||
|
||||
saf_dnx_bypass_init(regs);
|
||||
|
||||
saf_flash_timing_init(regs, xcfg);
|
||||
|
||||
ret = saf_init_erase_block_size(cfg);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("SAF Config bad flash erase config");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Default or expedited prefetch? */
|
||||
u = MCHP_SAF_FL_CFG_MISC_PFOE_DFLT;
|
||||
if (cfg->hwcfg.flags & MCHP_SAF_HW_CFG_FLAG_PFEXP) {
|
||||
u = MCHP_SAF_FL_CFG_MISC_PFOE_EXP;
|
||||
}
|
||||
|
||||
regs->SAF_FL_CFG_MISC =
|
||||
(regs->SAF_FL_CFG_MISC & ~(MCHP_SAF_FL_CFG_MISC_PFOE_MASK)) | u;
|
||||
|
||||
/* enable prefetch ? */
|
||||
if (cfg->hwcfg.flags & MCHP_SAF_HW_CFG_FLAG_PFEN) {
|
||||
MCHP_SAF_COMM_MODE_REG |= MCHP_SAF_COMM_MODE_PF_EN;
|
||||
} else {
|
||||
MCHP_SAF_COMM_MODE_REG &= ~(MCHP_SAF_COMM_MODE_PF_EN);
|
||||
}
|
||||
|
||||
LOG_DBG("%s SAF_FL_CFG_MISC: %x", __func__, regs->SAF_FL_CFG_MISC);
|
||||
LOG_DBG("%s Aft MCHP_SAF_COMM_MODE_REG: %x", __func__,
|
||||
MCHP_SAF_COMM_MODE_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int espi_saf_xec_set_pr(const struct device *dev,
|
||||
const struct espi_saf_protection *pr)
|
||||
{
|
||||
if ((dev == NULL) || (pr == NULL)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (pr->nregions >= MCHP_ESPI_SAF_PR_MAX) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const struct espi_saf_xec_config *xcfg = DEV_CFG(dev);
|
||||
MCHP_SAF_HW_REGS *regs = (MCHP_SAF_HW_REGS *)xcfg->saf_base_addr;
|
||||
|
||||
if (regs->SAF_FL_CFG_MISC & MCHP_SAF_FL_CFG_MISC_SAF_EN) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
const struct espi_saf_pr *preg = pr->pregions;
|
||||
size_t n = pr->nregions;
|
||||
|
||||
while (n--) {
|
||||
uint8_t regnum = preg->pr_num;
|
||||
|
||||
if (regnum >= MCHP_ESPI_SAF_PR_MAX) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* NOTE: If previously locked writes have no effect */
|
||||
if (preg->flags & MCHP_SAF_PR_FLAG_ENABLE) {
|
||||
regs->SAF_PROT_RG[regnum].START = preg->start >> 12U;
|
||||
regs->SAF_PROT_RG[regnum].LIMIT =
|
||||
(preg->start + preg->size - 1U) >> 12U;
|
||||
regs->SAF_PROT_RG[regnum].WEBM = preg->master_bm_we;
|
||||
regs->SAF_PROT_RG[regnum].RDBM = preg->master_bm_rd;
|
||||
} else {
|
||||
regs->SAF_PROT_RG[regnum].START = 0x7FFFFU;
|
||||
regs->SAF_PROT_RG[regnum].LIMIT = 0U;
|
||||
regs->SAF_PROT_RG[regnum].WEBM = 0U;
|
||||
regs->SAF_PROT_RG[regnum].RDBM = 0U;
|
||||
}
|
||||
|
||||
if (preg->flags & MCHP_SAF_PR_FLAG_LOCK) {
|
||||
regs->SAF_PROT_LOCK |= (1UL << regnum);
|
||||
}
|
||||
|
||||
preg++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool espi_saf_xec_channel_ready(const struct device *dev)
|
||||
{
|
||||
const struct espi_saf_xec_config *cfg = DEV_CFG(dev);
|
||||
MCHP_SAF_HW_REGS *regs = (MCHP_SAF_HW_REGS *)cfg->saf_base_addr;
|
||||
|
||||
if (regs->SAF_FL_CFG_MISC & MCHP_SAF_FL_CFG_MISC_SAF_EN) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* MCHP SAF hardware supports a range of flash block erase
|
||||
* sizes from 1KB to 128KB. The eSPI Host specification requires
|
||||
* 4KB must be supported. The MCHP SAF QMSPI HW interface only
|
||||
* supported three erase sizes. Most SPI flash devices chosen for
|
||||
* SAF support 4KB, 32KB, and 64KB.
|
||||
* Get flash erase sizes driver has configured from eSPI capabilities
|
||||
* registers. We assume driver flash tables have opcodes to match
|
||||
* capabilities configuration.
|
||||
* Check requested erase size is supported.
|
||||
*/
|
||||
struct erase_size_encoding {
|
||||
uint8_t hwbitpos;
|
||||
uint8_t encoding;
|
||||
};
|
||||
|
||||
static const struct erase_size_encoding ersz_enc[] = {
|
||||
{ MCHP_ESPI_SERASE_SZ_4K_BITPOS, 0 },
|
||||
{ MCHP_ESPI_SERASE_SZ_32K_BITPOS, 1 },
|
||||
{ MCHP_ESPI_SERASE_SZ_64K_BITPOS, 2 }
|
||||
};
|
||||
|
||||
#define SAF_ERASE_ENCODING_MAX_ENTRY \
|
||||
(sizeof(ersz_enc) / sizeof(struct erase_size_encoding))
|
||||
|
||||
static uint32_t get_erase_size_encoding(uint32_t erase_size)
|
||||
{
|
||||
uint8_t supsz = ESPI_CAP_REGS->FC_SERBZ;
|
||||
|
||||
LOG_DBG("%s\n", __func__);
|
||||
for (int i = 0; i < SAF_ERASE_ENCODING_MAX_ENTRY; i++) {
|
||||
uint32_t sz = MCHP_ESPI_SERASE_SZ(ersz_enc[i].hwbitpos);
|
||||
|
||||
if ((sz == erase_size) &&
|
||||
(supsz & (1 << ersz_enc[i].hwbitpos))) {
|
||||
return ersz_enc[i].encoding;
|
||||
}
|
||||
}
|
||||
|
||||
return 0xffffffffU;
|
||||
}
|
||||
|
||||
static int check_ecp_access_size(uint32_t reqlen)
|
||||
{
|
||||
if ((reqlen < MCHP_SAF_ECP_CMD_RW_LEN_MIN) ||
|
||||
(reqlen > MCHP_SAF_ECP_CMD_RW_LEN_MAX)) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* EC access (read/erase/write) to SAF atttached flash array
|
||||
* cmd 0 = read
|
||||
* 1 = write
|
||||
* 2 = erase
|
||||
*/
|
||||
static int saf_ecp_access(const struct device *dev,
|
||||
struct espi_saf_packet *pckt, uint8_t cmd)
|
||||
{
|
||||
uint32_t err_mask, n;
|
||||
int rc, counter;
|
||||
struct espi_saf_xec_data *xdat = DEV_DATA(dev);
|
||||
const struct espi_saf_xec_config *cfg = DEV_CFG(dev);
|
||||
MCHP_SAF_HW_REGS *regs = (MCHP_SAF_HW_REGS *)cfg->saf_base_addr;
|
||||
|
||||
counter = 0;
|
||||
err_mask = MCHP_SAF_ECP_STS_ERR_MASK;
|
||||
|
||||
LOG_DBG("%s", __func__);
|
||||
|
||||
if (!(regs->SAF_FL_CFG_MISC & MCHP_SAF_FL_CFG_MISC_SAF_EN)) {
|
||||
LOG_ERR("SAF is disabled");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (regs->SAF_ECP_BUSY & MCHP_SAF_ECP_BUSY) {
|
||||
LOG_ERR("SAF EC Portal is busy");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if ((cmd == MCHP_SAF_ECP_CMD_CTYPE_READ0) ||
|
||||
(cmd == MCHP_SAF_ECP_CMD_CTYPE_WRITE0)) {
|
||||
rc = check_ecp_access_size(pckt->len);
|
||||
if (rc) {
|
||||
LOG_ERR("SAF EC Portal size out of bounds");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (cmd == MCHP_SAF_ECP_CMD_CTYPE_WRITE0) {
|
||||
memcpy(slave_mem, pckt->buf, pckt->len);
|
||||
}
|
||||
|
||||
n = pckt->len;
|
||||
} else if (cmd == MCHP_SAF_ECP_CMD_CTYPE_ERASE0) {
|
||||
n = get_erase_size_encoding(pckt->len);
|
||||
if (n == 0xffffffff) {
|
||||
LOG_ERR("SAF EC Portal unsupported erase size");
|
||||
return -EAGAIN;
|
||||
}
|
||||
} else {
|
||||
LOG_ERR("SAF EC Portal bad cmd");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
LOG_DBG("%s params val done", __func__);
|
||||
|
||||
k_sem_take(&xdat->ecp_lock, K_FOREVER);
|
||||
|
||||
regs->SAF_ECP_INTEN = 0;
|
||||
regs->SAF_ECP_STATUS = 0xffffffff;
|
||||
|
||||
/*
|
||||
* TODO - Force SAF Done interrupt disabled until we have support
|
||||
* from eSPI driver.
|
||||
*/
|
||||
MCHP_GIRQ_ENCLR(MCHP_SAF_GIRQ) = MCHP_SAF_GIRQ_ECP_DONE_BIT;
|
||||
MCHP_GIRQ_SRC(MCHP_SAF_GIRQ) = MCHP_SAF_GIRQ_ECP_DONE_BIT;
|
||||
|
||||
regs->SAF_ECP_FLAR = pckt->flash_addr;
|
||||
regs->SAF_ECP_BFAR = (uint32_t)&slave_mem[0];
|
||||
|
||||
regs->SAF_ECP_CMD =
|
||||
MCHP_SAF_ECP_CMD_PUT_FLASH_NP |
|
||||
((uint32_t)cmd << MCHP_SAF_ECP_CMD_CTYPE_POS) |
|
||||
((n << MCHP_SAF_ECP_CMD_LEN_POS) & MCHP_SAF_ECP_CMD_LEN_MASK);
|
||||
|
||||
/* TODO when interrupts are available enable here */
|
||||
regs->SAF_ECP_START = MCHP_SAF_ECP_START;
|
||||
|
||||
/* TODO
|
||||
* ISR is in eSPI driver. Use polling until eSPI driver has been
|
||||
* modified to provide callback for GIRQ19 SAF ECP Done.
|
||||
*/
|
||||
rc = 0;
|
||||
xdat->hwstatus = regs->SAF_ECP_STATUS;
|
||||
while (!(xdat->hwstatus & MCHP_SAF_ECP_STS_DONE)) {
|
||||
rc = xec_saf_spin_yield(&counter);
|
||||
if (rc < 0) {
|
||||
goto ecp_exit;
|
||||
}
|
||||
xdat->hwstatus = regs->SAF_ECP_STATUS;
|
||||
}
|
||||
|
||||
/* clear hardware status and check for errors */
|
||||
regs->SAF_ECP_STATUS = xdat->hwstatus;
|
||||
if (xdat->hwstatus & MCHP_SAF_ECP_STS_ERR_MASK) {
|
||||
rc = -EIO;
|
||||
goto ecp_exit;
|
||||
}
|
||||
|
||||
if (cmd == MCHP_SAF_ECP_CMD_CTYPE_READ0) {
|
||||
memcpy(pckt->buf, slave_mem, pckt->len);
|
||||
}
|
||||
|
||||
ecp_exit:
|
||||
k_sem_give(&xdat->ecp_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Flash read using SAF EC Portal */
|
||||
static int saf_xec_flash_read(const struct device *dev,
|
||||
struct espi_saf_packet *pckt)
|
||||
{
|
||||
LOG_DBG("%s", __func__);
|
||||
return saf_ecp_access(dev, pckt, MCHP_SAF_ECP_CMD_CTYPE_READ0);
|
||||
}
|
||||
|
||||
/* Flash write using SAF EC Portal */
|
||||
static int saf_xec_flash_write(const struct device *dev,
|
||||
struct espi_saf_packet *pckt)
|
||||
{
|
||||
return saf_ecp_access(dev, pckt, MCHP_SAF_ECP_CMD_CTYPE_WRITE0);
|
||||
}
|
||||
|
||||
/* Flash erase using SAF EC Portal */
|
||||
static int saf_xec_flash_erase(const struct device *dev,
|
||||
struct espi_saf_packet *pckt)
|
||||
{
|
||||
return saf_ecp_access(dev, pckt, MCHP_SAF_ECP_CMD_CTYPE_ERASE0);
|
||||
}
|
||||
|
||||
static int espi_saf_xec_manage_callback(const struct device *dev,
|
||||
struct espi_callback *callback,
|
||||
bool set)
|
||||
{
|
||||
struct espi_saf_xec_data *data = DEV_DATA(dev);
|
||||
|
||||
return espi_manage_callback(&data->callbacks, callback, set);
|
||||
}
|
||||
|
||||
static int espi_saf_xec_activate(const struct device *dev)
|
||||
{
|
||||
const struct espi_saf_xec_config *cfg;
|
||||
MCHP_SAF_HW_REGS *regs;
|
||||
|
||||
if (dev == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cfg = DEV_CFG(dev);
|
||||
regs = (MCHP_SAF_HW_REGS *)cfg->saf_base_addr;
|
||||
|
||||
regs->SAF_FL_CFG_MISC |= MCHP_SAF_FL_CFG_MISC_SAF_EN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int espi_saf_xec_init(const struct device *dev);
|
||||
|
||||
static const struct espi_saf_driver_api espi_saf_xec_driver_api = {
|
||||
.config = espi_saf_xec_configuration,
|
||||
.set_protection_regions = espi_saf_xec_set_pr,
|
||||
.activate = espi_saf_xec_activate,
|
||||
.get_channel_status = espi_saf_xec_channel_ready,
|
||||
.flash_read = saf_xec_flash_read,
|
||||
.flash_write = saf_xec_flash_write,
|
||||
.flash_erase = saf_xec_flash_erase,
|
||||
.manage_callback = espi_saf_xec_manage_callback,
|
||||
};
|
||||
|
||||
static struct espi_saf_xec_data espi_saf_xec_data;
|
||||
|
||||
static const struct espi_saf_xec_config espi_saf_xec_config = {
|
||||
.saf_base_addr = DT_INST_REG_ADDR_BY_IDX(0, 0),
|
||||
.qmspi_base_addr = DT_INST_REG_ADDR_BY_IDX(0, 1),
|
||||
.saf_comm_base_addr = DT_INST_REG_ADDR_BY_IDX(0, 2),
|
||||
.poll_timeout = DT_INST_PROP_OR(inst, poll_timeout,
|
||||
MCHP_SAF_FLASH_POLL_TIMEOUT),
|
||||
.consec_rd_timeout = DT_INST_PROP_OR(
|
||||
inst, consec_rd_timeout, MCHP_SAF_FLASH_CONSEC_READ_TIMEOUT),
|
||||
.sus_chk_delay = DT_INST_PROP_OR(inst, sus_chk_delay,
|
||||
MCHP_SAF_FLASH_SUS_CHK_DELAY),
|
||||
.sus_rsm_interval = DT_INST_PROP_OR(inst, sus_rsm_interval,
|
||||
MCHP_SAF_FLASH_SUS_RSM_INTERVAL),
|
||||
.poll_interval = DT_INST_PROP_OR(inst, poll_interval,
|
||||
MCHP_SAF_FLASH_POLL_INTERVAL),
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, &espi_saf_xec_init, device_pm_control_nop,
|
||||
&espi_saf_xec_data, &espi_saf_xec_config, POST_KERNEL,
|
||||
CONFIG_ESPI_SAF_INIT_PRIORITY, &espi_saf_xec_driver_api);
|
||||
|
||||
static int espi_saf_xec_init(const struct device *dev)
|
||||
{
|
||||
struct espi_saf_xec_data *data = DEV_DATA(dev);
|
||||
|
||||
/* ungate SAF clocks by disabling PCR sleep enable */
|
||||
mchp_pcr_periph_slp_ctrl(PCR_ESPI_SAF, MCHP_PCR_SLEEP_DIS);
|
||||
|
||||
/* reset the SAF block */
|
||||
mchp_pcr_periph_reset(PCR_ESPI_SAF);
|
||||
|
||||
/* Configure the channels and its capabilities based on build config */
|
||||
ESPI_CAP_REGS->GLB_CAP0 |= MCHP_ESPI_GBL_CAP0_FC_SUPP;
|
||||
ESPI_CAP_REGS->FC_CAP &= ~(MCHP_ESPI_FC_CAP_SHARE_MASK);
|
||||
ESPI_CAP_REGS->FC_CAP |= MCHP_ESPI_FC_CAP_SHARE_MAF_SAF;
|
||||
|
||||
k_sem_init(&data->ecp_lock, 1, 1);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue