drivers: espi: xec: Add support for eSPI flash channel

Add driver implementation to support flash read/writ transactions
over eSPI bus.

Signed-off-by: Jose Alberto Meza <jose.a.meza.arellano@intel.com>
This commit is contained in:
Jose Alberto Meza 2020-04-08 18:21:29 -07:00 committed by Carles Cufí
commit 9538f7896e
2 changed files with 146 additions and 7 deletions

View file

@ -14,6 +14,9 @@ if ESPI_XEC
config ESPI_OOB_CHANNEL
default y
config ESPI_FLASH_CHANNEL
default y
config ESPI_PERIPHERAL_HOST_IO
default y

View file

@ -19,7 +19,12 @@
/* Maximum timeout to transmit a virtual wire packet.
* 10 ms expresed in multiples of 100us
*/
#define ESPI_XEC_VWIRE_SEND_TIMEOUT 100
#define ESPI_XEC_VWIRE_SEND_TIMEOUT 100ul
/* 100ms */
#define MAX_OOB_TIMEOUT 100ul
/* 1s */
#define MAX_FLASH_TIMEOUT 1000ul
/* OOB maximum address configuration */
#define ESPI_XEC_OOB_ADDR_MSW 0x1FFFul
@ -36,9 +41,8 @@
#define ESPI_XEC_PORT80_BAR_ADDRESS 0x00800000
#define ESPI_XEC_PORT81_BAR_ADDRESS 0x00810000
#define MAX_OOB_BUFFER_SIZE 128
/* 1s */
#define MAX_OOB_TIMEOUT 1000
#define MAX_OOB_BUFFER_SIZE 128ul
#define MAX_SLAVE_BUFFER_SIZE 128ul
LOG_MODULE_REGISTER(espi, CONFIG_ESPI_LOG_LEVEL);
@ -58,6 +62,7 @@ struct espi_xec_data {
sys_slist_t callbacks;
struct k_sem tx_lock;
struct k_sem rx_lock;
struct k_sem flash_lock;
u8_t plt_rst_asserted;
u8_t espi_rst_asserted;
u8_t sx_state;
@ -189,6 +194,7 @@ static const struct xec_signal vw_tbl[] = {
static u32_t slave_rx_mem[MAX_OOB_BUFFER_SIZE];
static u32_t slave_tx_mem[MAX_OOB_BUFFER_SIZE];
static u32_t slave_mem[MAX_SLAVE_BUFFER_SIZE];
static int espi_xec_configure(struct device *dev, struct espi_cfg *cfg)
{
@ -263,6 +269,7 @@ static int espi_xec_configure(struct device *dev, struct espi_cfg *cfg)
if (IS_ENABLED(CONFIG_ESPI_FLASH_CHANNEL)) {
cap0 |= MCHP_ESPI_GBL_CAP0_FC_SUPP;
} else {
LOG_ERR("Flash channel not supported");
return -EINVAL;
}
}
@ -295,7 +302,7 @@ static bool espi_xec_channel_ready(struct device *dev, enum espi_channel ch)
sts = ESPI_CAP_REGS->OOB_RDY & MCHP_ESPI_OOB_READY;
break;
case ESPI_CHANNEL_FLASH:
sts = ESPI_CAP_REGS->FC_RDY & MCHP_ESPI_PC_READY;
sts = ESPI_CAP_REGS->FC_RDY & MCHP_ESPI_FC_READY;
break;
default:
sts = false;
@ -520,6 +527,105 @@ static int espi_xec_receive_oob(struct device *dev,
return 0;
}
static int espi_xec_flash_read(struct device *dev,
struct espi_flash_packet *pckt)
{
int ret;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->driver_data);
u32_t err_mask = MCHP_ESPI_FC_STS_IBERR |
MCHP_ESPI_FC_STS_FAIL |
MCHP_ESPI_FC_STS_OVFL |
MCHP_ESPI_FC_STS_BADREQ;
LOG_DBG("%s", __func__);
if (!(ESPI_FC_REGS->STS & MCHP_ESPI_FC_STS_CHAN_EN)) {
LOG_ERR("Flash channel is disabled");
return -EIO;
}
if (pckt->len > MAX_SLAVE_BUFFER_SIZE) {
LOG_ERR("Invalid size request");
return -EINVAL;
}
ESPI_FC_REGS->FL_ADDR_MSW = 0;
ESPI_FC_REGS->FL_ADDR_LSW = pckt->flash_addr;
ESPI_FC_REGS->MEM_ADDR_MSW = 0;
ESPI_FC_REGS->MEM_ADDR_LSW = (u32_t)&slave_mem[0];
ESPI_FC_REGS->XFR_LEN = pckt->len;
ESPI_FC_REGS->CTRL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_RD0);
ESPI_FC_REGS->CTRL |= MCHP_ESPI_FC_CTRL_START;
/* Wait until ISR or timeout */
ret = k_sem_take(&data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT));
if (ret == -EAGAIN) {
LOG_ERR("%s timeout", __func__);
return -ETIMEDOUT;
}
if (ESPI_FC_REGS->STS & err_mask) {
LOG_ERR("%s error %x", __func__, err_mask);
ESPI_FC_REGS->STS = err_mask;
return -EIO;
}
memcpy(pckt->buf, slave_mem, pckt->len);
return 0;
}
static int espi_xec_flash_write(struct device *dev,
struct espi_flash_packet *pckt)
{
int ret;
u32_t err_mask = MCHP_ESPI_FC_STS_IBERR |
MCHP_ESPI_FC_STS_OVRUN |
MCHP_ESPI_FC_STS_FAIL |
MCHP_ESPI_FC_STS_BADREQ;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->driver_data);
LOG_DBG("%s", __func__);
if (!(ESPI_FC_REGS->STS & MCHP_ESPI_FC_STS_CHAN_EN)) {
LOG_ERR("Flash channel is disabled");
return -EIO;
}
if ((ESPI_FC_REGS->CFG & MCHP_ESPI_FC_CFG_BUSY)) {
LOG_ERR("Flash channel is busy");
return -EBUSY;
}
memcpy(slave_mem, pckt->buf, pckt->len);
ESPI_FC_REGS->FL_ADDR_MSW = 0;
ESPI_FC_REGS->FL_ADDR_LSW = pckt->flash_addr;
ESPI_FC_REGS->MEM_ADDR_MSW = 0;
ESPI_FC_REGS->MEM_ADDR_LSW = (u32_t)&slave_mem[0];
ESPI_FC_REGS->XFR_LEN = pckt->len;
ESPI_FC_REGS->CTRL = MCHP_ESPI_FC_CTRL_FUNC(MCHP_ESPI_FC_CTRL_WR0);
ESPI_FC_REGS->CTRL |= MCHP_ESPI_FC_CTRL_START;
/* Wait until ISR or timeout */
ret = k_sem_take(&data->flash_lock, K_MSEC(MAX_FLASH_TIMEOUT));
if (ret == -EAGAIN) {
LOG_ERR("%s timeout", __func__);
return -ETIMEDOUT;
}
if (ESPI_FC_REGS->STS & err_mask) {
LOG_ERR("%s err: %x", __func__, err_mask);
ESPI_FC_REGS->STS = err_mask;
return -EIO;
}
memcpy(pckt->buf, slave_rx_mem, pckt->len);
return 0;
}
static int espi_xec_manage_callback(struct device *dev,
struct espi_callback *callback, bool set)
{
@ -570,7 +676,15 @@ static void espi_init_flash(struct device *dev)
struct espi_xec_config *config =
(struct espi_xec_config *)(dev->config->config_info);
LOG_DBG("%s", __func__);
/* Indicate slave flash channel is ready */
ESPI_CAP_REGS->FC_RDY |= MCHP_ESPI_FC_READY;
/* Enable interrupts */
MCHP_GIRQ_ENSET(config->bus_girq_id) = BIT(MCHP_ESPI_FC_GIRQ_POS);
ESPI_FC_REGS->IEN |= MCHP_ESPI_FC_IEN_CHG_EN;
ESPI_FC_REGS->IEN |= MCHP_ESPI_FC_IEN_DONE;
}
#endif
@ -694,7 +808,9 @@ static void espi_vwire_chanel_isr(struct device *dev)
{
struct espi_xec_data *data = (struct espi_xec_data *)(dev->driver_data);
const struct espi_xec_config *config = dev->config->config_info;
struct espi_event evt = { ESPI_BUS_EVENT_CHANNEL_READY, 0, 0 };
struct espi_event evt = { .evt_type = ESPI_BUS_EVENT_CHANNEL_READY,
.evt_details = 0,
.evt_data = 0 };
u32_t status;
status = ESPI_IO_VW_REGS->VW_EN_STS;
@ -758,14 +874,30 @@ static void espi_oob_up_isr(struct device *dev)
static void espi_flash_isr(struct device *dev)
{
u32_t status;
struct espi_xec_data *data = (struct espi_xec_data *)(dev->driver_data);
struct espi_event evt = { ESPI_BUS_EVENT_CHANNEL_READY, 0, 0 };
status = ESPI_FC_REGS->STS;
LOG_DBG("%s %x", __func__, status);
if (status & MCHP_ESPI_FC_STS_DONE) {
ESPI_FC_REGS->IEN = ~BIT(0);
/* Ensure to clear only relevant bit */
ESPI_FC_REGS->STS = MCHP_ESPI_FC_STS_DONE;
k_sem_give(&data->flash_lock);
}
if (status & MCHP_ESPI_FC_STS_CHAN_EN_CHG) {
/* Ensure to clear only relevant bit */
ESPI_FC_REGS->STS = MCHP_ESPI_FC_STS_CHAN_EN_CHG;
espi_init_flash(dev);
evt.evt_details = ESPI_CHANNEL_FLASH;
if (status & MCHP_ESPI_FC_STS_CHAN_EN) {
evt.evt_data = 1;
}
espi_send_callbacks(&data->callbacks, dev, evt);
}
}
#endif
@ -1055,6 +1187,8 @@ static const struct espi_driver_api espi_xec_driver_api = {
.receive_vwire = espi_xec_receive_vwire,
.send_oob = espi_xec_send_oob,
.receive_oob = espi_xec_receive_oob,
.flash_read = espi_xec_flash_read,
.flash_write = espi_xec_flash_write,
.manage_callback = espi_xec_manage_callback,
.read_lpc_request = espi_xec_read_lpc_request,
.write_lpc_request = espi_xec_write_lpc_request,
@ -1108,6 +1242,8 @@ static int espi_xec_init(struct device *dev)
ESPI_CAP_REGS->GLB_CAP0 |= MCHP_ESPI_FC_CAP_MAX_PLD_SZ_64;
ESPI_CAP_REGS->FC_CAP |= MCHP_ESPI_FC_CAP_SHARE_MAF_SAF;
ESPI_CAP_REGS->FC_CAP |= MCHP_ESPI_FC_CAP_MAX_RD_SZ_64;
k_sem_init(&data->flash_lock, 0, 1);
#else
ESPI_CAP_REGS->GLB_CAP0 &= ~MCHP_ESPI_GBL_CAP0_FC_SUPP;
#endif