diff options
author | Vikash Chandola <vikash.chandola@intel.com> | 2022-03-10 10:54:37 +0300 |
---|---|---|
committer | Vikash Chandola <vikash.chandola@intel.com> | 2022-03-12 10:19:12 +0300 |
commit | 3bf159b11e81df07a8619743f7bb0b1f657173f1 (patch) | |
tree | adccd39d77d982aab0a36cae85beecc520b1ef98 /drivers/soc | |
parent | 84921f2790920a2d6dd3c937bd914811335e2c1a (diff) | |
download | linux-3bf159b11e81df07a8619743f7bb0b1f657173f1.tar.xz |
soc: aspeed: Add support for eSPI OOB channel
Add eSPI OOB channel slave. Add new device file to transact eSPI OOB
messages with eSPI master in PCH.
Signed-off-by: Vikash Chandola <vikash.chandola@intel.com>
Diffstat (limited to 'drivers/soc')
-rw-r--r-- | drivers/soc/aspeed/Kconfig | 6 | ||||
-rw-r--r-- | drivers/soc/aspeed/Makefile | 3 | ||||
-rw-r--r-- | drivers/soc/aspeed/aspeed-espi-ctrl.h | 25 | ||||
-rw-r--r-- | drivers/soc/aspeed/aspeed-espi-oob.c | 488 | ||||
-rw-r--r-- | drivers/soc/aspeed/aspeed-espi-oob.h | 71 | ||||
-rw-r--r-- | drivers/soc/aspeed/aspeed-espi-slave.c | 39 |
6 files changed, 621 insertions, 11 deletions
diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index b61d0c17d26d..1914f33f0b3b 100644 --- a/drivers/soc/aspeed/Kconfig +++ b/drivers/soc/aspeed/Kconfig @@ -15,10 +15,10 @@ config ASPEED_BMC_MISC config ASPEED_ESPI_SLAVE depends on ARCH_ASPEED || COMPILE_TEST depends on REGMAP_MMIO - tristate "Aspeed ast2500 eSPI slave device driver" + tristate "Aspeed ast2500/2600 eSPI slave device driver" help - Control Aspeed ast2500 eSPI slave controller to handle event - which needs the firmware's processing. + Enable driver support for the Aspeed eSPI engine. Handles event + arising from firmware bootup and enable espi oob channel. config ASPEED_LPC_CTRL tristate "ASPEED LPC firmware cycle control" diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index 2be9c3930caa..7e9feefdab9a 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_ASPEED_BMC_MISC) += aspeed-bmc-misc.o -obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi-slave.o +obj-$(CONFIG_ASPEED_ESPI_SLAVE) += aspeed-espi.o +aspeed-espi-y := aspeed-espi-slave.o aspeed-espi-oob.o obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_MBOX) += aspeed-lpc-mbox.o obj-$(CONFIG_ASPEED_LPC_SIO) += aspeed-lpc-sio.o diff --git a/drivers/soc/aspeed/aspeed-espi-ctrl.h b/drivers/soc/aspeed/aspeed-espi-ctrl.h index 38f76820e7d3..14e0334b80c8 100644 --- a/drivers/soc/aspeed/aspeed-espi-ctrl.h +++ b/drivers/soc/aspeed/aspeed-espi-ctrl.h @@ -7,6 +7,31 @@ #include <linux/bits.h> +enum aspeed_espi_version { + ASPEED_ESPI_AST2500, + ASPEED_ESPI_AST2600, +}; + +struct aspeed_espi_model { + u32 version; +}; + +struct aspeed_espi_ctrl { + struct device *dev; + + struct regmap *map; + struct clk *clk; + + int irq; + + struct aspeed_espi_perif *perif; + struct aspeed_espi_vw *vw; + struct aspeed_espi_oob *oob; + struct aspeed_espi_flash *flash; + + const struct aspeed_espi_model *model; +}; + /* eSPI register offset */ #define ASPEED_ESPI_CTRL 0x000 #define ASPEED_ESPI_CTRL_SW_RESET GENMASK(31, 24) diff --git a/drivers/soc/aspeed/aspeed-espi-oob.c b/drivers/soc/aspeed/aspeed-espi-oob.c new file mode 100644 index 000000000000..645a7a9f424a --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-oob.c @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Aspeed Technology Inc. + */ +#include <linux/aspeed-espi-ioc.h> +#include <linux/dma-mapping.h> +#include <linux/of_device.h> +#include <linux/miscdevice.h> +#include <linux/regmap.h> +#include <linux/vmalloc.h> + +#include "aspeed-espi-ctrl.h" +#include "aspeed-espi-oob.h" + +#define OOB_MDEV_NAME "aspeed-espi-oob" + +#define OOB_DMA_DESC_MAX_NUM 1024 +#define OOB_DMA_TX_DESC_CUST 0x04 + +#define OOB_DEFAULT_RX_PACKET_LEN 0x1000 + +/* + * Descriptor-based RX DMA handling + */ +static long aspeed_espi_oob_dma_desc_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + int rc = 0; + struct espi_comm_hdr *hdr; + struct oob_rx_dma_desc *d; + unsigned long flags; + u32 wptr, sptr; + u32 pkt_len; + u32 reg; + u8 *pkt; + + regmap_read(espi_ctrl->map, ASPEED_ESPI_OOB_RX_DMA_WS_PTR, ®); + wptr = (reg & ASPEED_ESPI_OOB_RX_DMA_WS_PTR_WP_MASK) >> + ASPEED_ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT; + sptr = (reg & ASPEED_ESPI_OOB_RX_DMA_WS_PTR_SP_MASK) >> + ASPEED_ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT; + d = &espi_oob->dma.rx_desc[sptr]; + if (!d->dirty) + return -EFAULT; + + pkt_len = ((d->len) ? : OOB_DEFAULT_RX_PACKET_LEN) + sizeof(struct espi_comm_hdr); + if (ioc->pkt_len < pkt_len) + return -EINVAL; + + pkt = vmalloc(pkt_len); + if (!pkt) + return -ENOMEM; + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = d->cyc; + hdr->tag = d->tag; + hdr->len_h = ESPI_LEN_HIGH(d->len); + hdr->len_l = ESPI_LEN_LOW(d->len); + memcpy(hdr + 1, espi_oob->dma.rx_virt + (PAGE_SIZE * sptr), pkt_len - sizeof(*hdr)); + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* make current descriptor available again */ + d->dirty = 0; + sptr = (sptr + 1) % espi_oob->dma.rx_desc_num; + wptr = (wptr + 1) % espi_oob->dma.rx_desc_num; + reg = ASPEED_ESPI_OOB_RX_DMA_WS_PTR_RECV_EN; + reg |= (wptr << ASPEED_ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT) & + ASPEED_ESPI_OOB_RX_DMA_WS_PTR_WP_MASK; + reg |= (sptr << ASPEED_ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT) & + ASPEED_ESPI_OOB_RX_DMA_WS_PTR_SP_MASK; + + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_RX_DMA_WS_PTR, reg); + spin_lock_irqsave(&espi_oob->lock, flags); + espi_oob->rx_ready = espi_oob->dma.rx_desc[sptr].dirty; + spin_unlock_irqrestore(&espi_oob->lock, flags); + +free_n_out: + vfree(pkt); + return rc; +} + +static long aspeed_espi_oob_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + int i, rc = 0; + struct espi_comm_hdr *hdr; + unsigned long flags; + u32 cyc, tag, len; + u32 pkt_len; + u32 reg; + u8 *pkt; + + if (fp->f_flags & O_NONBLOCK) { + if (mutex_trylock(&espi_oob->get_rx_mtx)) + return -EBUSY; + + if (!espi_oob->rx_ready) { + rc = -ENODATA; + goto unlock_mtx_n_out; + } + } else { + mutex_lock(&espi_oob->get_rx_mtx); + if (!espi_oob->rx_ready) { + rc = wait_event_interruptible(espi_oob->wq, espi_oob->rx_ready); + if (rc == -ERESTARTSYS) { + rc = -EINTR; + goto unlock_mtx_n_out; + } + } + } + if (espi_oob->dma_mode && espi_ctrl->model->version != ASPEED_ESPI_AST2500) { + rc = aspeed_espi_oob_dma_desc_get_rx(fp, ioc, espi_oob); + goto unlock_mtx_n_out; + } + + regmap_read(espi_ctrl->map, ASPEED_ESPI_OOB_RX_CTRL, ®); + cyc = (reg & ASPEED_ESPI_OOB_RX_CTRL_CYC_MASK) >> ASPEED_ESPI_OOB_RX_CTRL_CYC_SHIFT; + tag = (reg & ASPEED_ESPI_OOB_RX_CTRL_TAG_MASK) >> ASPEED_ESPI_OOB_RX_CTRL_TAG_SHIFT; + len = (reg & ASPEED_ESPI_OOB_RX_CTRL_LEN_MASK) >> ASPEED_ESPI_OOB_RX_CTRL_LEN_SHIFT; + + /* + * Calculate the length of the rest part of the eSPI packet to be read from HW + * and copied to user space. + */ + pkt_len = (len ?: ASPEED_ESPI_PLD_LEN_MAX) + sizeof(*hdr); + if (ioc->pkt_len < pkt_len) { + rc = -EINVAL; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = cyc; + hdr->tag = tag; + hdr->len_h = ESPI_LEN_HIGH(len); + hdr->len_l = ESPI_LEN_LOW(len); + if (espi_oob->dma_mode) { + memcpy(hdr + 1, espi_oob->dma.rx_virt, pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) { + regmap_read(espi_ctrl->map, ASPEED_ESPI_OOB_RX_PORT, ®); + pkt[i] = reg & 0xff; + } + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + regmap_write_bits(espi_ctrl->map, ASPEED_ESPI_OOB_RX_CTRL, + ASPEED_ESPI_OOB_RX_CTRL_PEND_SERV, + ASPEED_ESPI_OOB_RX_CTRL_PEND_SERV); + + spin_lock_irqsave(&espi_oob->lock, flags); + espi_oob->rx_ready = 0; + spin_unlock_irqrestore(&espi_oob->lock, flags); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_oob->get_rx_mtx); + return rc; +} + +static long aspeed_espi_oob_dma_desc_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + int rc = 0; + struct espi_comm_hdr *hdr; + struct oob_tx_dma_desc *d; + u32 rptr, wptr; + u8 *pkt; + + pkt = vzalloc(ioc->pkt_len); + if (!pkt) + return -ENOMEM; + + hdr = (struct espi_comm_hdr *)pkt; + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_TX_DMA_RD_PTR, + ASPEED_ESPI_OOB_TX_DMA_RD_PTR_UPDATE); + regmap_read(espi_ctrl->map, ASPEED_ESPI_OOB_TX_DMA_RD_PTR, &rptr); + regmap_read(espi_ctrl->map, ASPEED_ESPI_OOB_TX_DMA_WR_PTR, &wptr); + if (((wptr + 1) % espi_oob->dma.tx_desc_num) == rptr) { + rc = -EBUSY; + goto free_n_out; + } + + d = &espi_oob->dma.tx_desc[wptr]; + d->cyc = hdr->cyc; + d->tag = hdr->tag; + d->len = ESPI_LEN(hdr->len_h, hdr->len_l); + d->msg_type = OOB_DMA_TX_DESC_CUST; + memcpy(espi_oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1, ioc->pkt_len - sizeof(*hdr)); + + wptr = (wptr + 1) % espi_oob->dma.tx_desc_num; + wptr |= ASPEED_ESPI_OOB_TX_DMA_WR_PTR_SEND_EN; + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_TX_DMA_WR_PTR, wptr); + +free_n_out: + vfree(pkt); + return rc; +} + +static long aspeed_espi_oob_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + int i, rc = 0; + struct espi_comm_hdr *hdr; + u32 reg, len; + u8 *pkt; + + if (!mutex_trylock(&espi_oob->put_tx_mtx)) + return -EBUSY; + + if (espi_oob->dma_mode && espi_ctrl->model->version != ASPEED_ESPI_AST2500) { + rc = aspeed_espi_oob_dma_desc_put_tx(fp, ioc, espi_oob); + goto unlock_mtx_n_out; + } + + regmap_read(espi_ctrl->map, ASPEED_ESPI_OOB_TX_CTRL, ®); + if (reg & ASPEED_ESPI_OOB_TX_CTRL_TRIGGER) { + rc = -EBUSY; + goto unlock_mtx_n_out; + } + + if (ioc->pkt_len > ASPEED_ESPI_PKT_LEN_MAX) { + rc = -EINVAL; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + if (espi_oob->dma_mode) { + memcpy(espi_oob->dma.tx_virt, hdr + 1, ioc->pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_TX_PORT, pkt[i]); + } + + len = ESPI_LEN(hdr->len_h, hdr->len_l); + reg = (hdr->cyc << ASPEED_ESPI_OOB_TX_CTRL_CYC_SHIFT) & ASPEED_ESPI_OOB_TX_CTRL_CYC_MASK; + reg |= (hdr->tag << ASPEED_ESPI_OOB_TX_CTRL_TAG_SHIFT) & ASPEED_ESPI_OOB_TX_CTRL_TAG_MASK; + reg |= (len << ASPEED_ESPI_OOB_TX_CTRL_LEN_SHIFT) & ASPEED_ESPI_OOB_TX_CTRL_LEN_MASK; + reg |= ASPEED_ESPI_OOB_TX_CTRL_TRIGGER; + + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_TX_CTRL, reg); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_oob->put_tx_mtx); + return rc; +} + +static long aspeed_espi_oob_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) +{ + struct aspeed_espi_oob *espi_oob = container_of(fp->private_data, + struct aspeed_espi_oob, + mdev); + struct aspeed_espi_ioc ioc; + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; + + if (ioc.pkt_len > ASPEED_ESPI_PKT_LEN_MAX) + return -EINVAL; + + switch (cmd) { + case ASPEED_ESPI_OOB_GET_RX: + return aspeed_espi_oob_get_rx(fp, &ioc, espi_oob); + case ASPEED_ESPI_OOB_PUT_TX: + return aspeed_espi_oob_put_tx(fp, &ioc, espi_oob); + }; + + return -ENOTTY; +} + +void aspeed_espi_oob_event(u32 sts, struct aspeed_espi_oob *espi_oob) +{ + unsigned long flags; + + if (sts & ASPEED_ESPI_INT_STS_OOB_RX_CMPLT) { + spin_lock_irqsave(&espi_oob->lock, flags); + espi_oob->rx_ready = 1; + spin_unlock_irqrestore(&espi_oob->lock, flags); + wake_up_interruptible(&espi_oob->wq); + } +} + +static void aspeed_espi_oob_dma_init(struct aspeed_espi_oob *espi_oob) +{ + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + struct aspeed_espi_oob_dma *dma = &espi_oob->dma; + int i; + + regmap_update_bits(espi_ctrl->map, ASPEED_ESPI_CTRL, + ASPEED_ESPI_CTRL_OOB_TX_DMA_EN | ASPEED_ESPI_CTRL_OOB_RX_DMA_EN, + ASPEED_ESPI_CTRL_OOB_TX_DMA_EN | ASPEED_ESPI_CTRL_OOB_RX_DMA_EN); + + if (espi_ctrl->model->version == ASPEED_ESPI_AST2500) { + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_TX_DMA, dma->tx_addr); + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_RX_DMA, dma->rx_addr); + } else { + for (i = 0; i < dma->tx_desc_num; ++i) + dma->tx_desc[i].data_addr = dma->tx_addr + (i * PAGE_SIZE); + + for (i = 0; i < dma->rx_desc_num; ++i) { + dma->rx_desc[i].data_addr = dma->rx_addr + (i * PAGE_SIZE); + dma->rx_desc[i].dirty = 0; + } + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_TX_DMA, dma->tx_desc_addr); + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_TX_DMA_RB_SIZE, + dma->tx_desc_num); + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_RX_DMA, dma->rx_desc_addr); + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_RX_DMA_RB_SIZE, + dma->rx_desc_num); + regmap_update_bits(espi_ctrl->map, ASPEED_ESPI_OOB_RX_DMA_WS_PTR, + ASPEED_ESPI_OOB_RX_DMA_WS_PTR_RECV_EN, + ASPEED_ESPI_OOB_RX_DMA_WS_PTR_RECV_EN); + } +} + +void aspeed_espi_oob_enable(struct aspeed_espi_oob *espi_oob) +{ + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + regmap_update_bits(espi_ctrl->map, ASPEED_ESPI_CTRL, + ASPEED_ESPI_CTRL_OOB_SW_RDY | ASPEED_ESPI_CTRL_OOB_RX_SW_RST, 0); + + if (espi_oob->dma_mode) + regmap_update_bits(espi_ctrl->map, ASPEED_ESPI_CTRL, + ASPEED_ESPI_CTRL_OOB_TX_DMA_EN | ASPEED_ESPI_CTRL_OOB_RX_DMA_EN, + 0); + else + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_RX_CTRL, + ASPEED_ESPI_OOB_RX_CTRL_PEND_SERV); + + regmap_update_bits(espi_ctrl->map, ASPEED_ESPI_CTRL, ASPEED_ESPI_CTRL_OOB_RX_SW_RST, + ASPEED_ESPI_CTRL_OOB_RX_SW_RST); + regmap_write(espi_ctrl->map, ASPEED_ESPI_OOB_RX_CTRL, ASPEED_ESPI_OOB_RX_CTRL_PEND_SERV); + + if (espi_oob->dma_mode) + aspeed_espi_oob_dma_init(espi_oob); + + regmap_write(espi_ctrl->map, ASPEED_ESPI_INT_STS, ASPEED_ESPI_INT_STS_OOB_BITS); + regmap_update_bits(espi_ctrl->map, ASPEED_ESPI_INT_EN, + ASPEED_ESPI_INT_EN_OOB_BITS, + ASPEED_ESPI_INT_EN_OOB_BITS); + regmap_update_bits(espi_ctrl->map, ASPEED_ESPI_CTRL, + ASPEED_ESPI_CTRL_OOB_SW_RDY, + ASPEED_ESPI_CTRL_OOB_SW_RDY); +} + +static const struct file_operations aspeed_espi_oob_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aspeed_espi_oob_ioctl, +}; + +static int aspeed_espi_oob_dma_alloc(struct device *dev, struct aspeed_espi_oob *espi_oob, + u32 version) +{ + struct aspeed_espi_oob_dma *dma = &espi_oob->dma; + + if (version != ASPEED_ESPI_AST2500) { + of_property_read_u32(dev->of_node, "oob,dma-tx-desc-num", &dma->tx_desc_num); + of_property_read_u32(dev->of_node, "oob,dma-rx-desc-num", &dma->rx_desc_num); + + if (!dma->tx_desc_num || !dma->rx_desc_num) { + dev_err(dev, "invalid zero number of DMA channels\n"); + return -EINVAL; + } + + if (dma->tx_desc_num >= OOB_DMA_DESC_MAX_NUM || + dma->rx_desc_num >= OOB_DMA_DESC_MAX_NUM) { + dev_err(dev, "too many number of DMA channels\n"); + return -EINVAL; + } + + dma->tx_desc = dmam_alloc_coherent(dev, sizeof(*dma->tx_desc) * dma->tx_desc_num, + &dma->tx_desc_addr, GFP_KERNEL); + if (!dma->tx_desc) { + dev_err(dev, "cannot allocate DMA TX descriptor\n"); + return -ENOMEM; + } + + dma->rx_desc = dmam_alloc_coherent(dev, sizeof(*dma->rx_desc) * dma->rx_desc_num, + &dma->rx_desc_addr, GFP_KERNEL); + if (!dma->rx_desc) { + dev_err(dev, "cannot allocate DMA RX descriptor\n"); + return -ENOMEM; + } + } + /* + * DMA descriptors are consumed in the circular queue paradigm. + * Therefore, one dummy slot is reserved to detect the full + * condition. For AST2500 without DMA descriptors supported, + * the number of the queue slot should be 1 here. + */ + dma->tx_desc_num += 1; + dma->rx_desc_num += 1; + dma->tx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * dma->tx_desc_num, &dma->tx_addr, + GFP_KERNEL); + if (!dma->tx_virt) + return -ENOMEM; + + dma->rx_virt = dmam_alloc_coherent(dev, PAGE_SIZE * dma->rx_desc_num, &dma->rx_addr, + GFP_KERNEL); + if (!dma->rx_virt) + return -ENOMEM; + return 0; +} + +void *aspeed_espi_oob_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) +{ + struct aspeed_espi_oob *espi_oob = + devm_kzalloc(dev, sizeof(struct aspeed_espi_oob), GFP_KERNEL); + int rc = 0; + + if (!espi_oob) + return ERR_PTR(-ENOMEM); + + espi_oob->ctrl = espi_ctrl; + init_waitqueue_head(&espi_oob->wq); + spin_lock_init(&espi_oob->lock); + mutex_init(&espi_oob->put_tx_mtx); + mutex_init(&espi_oob->get_rx_mtx); + if (of_property_read_bool(dev->of_node, "oob,dma-mode")) { + rc = aspeed_espi_oob_dma_alloc(dev, espi_oob, espi_ctrl->model->version); + if (rc) + return ERR_PTR(rc); + espi_oob->dma_mode = 1; + } + + espi_oob->mdev.parent = dev; + espi_oob->mdev.minor = MISC_DYNAMIC_MINOR; + espi_oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", OOB_MDEV_NAME); + espi_oob->mdev.fops = &aspeed_espi_oob_fops; + rc = misc_register(&espi_oob->mdev); + if (rc) { + dev_err(dev, "cannot register device\n"); + return ERR_PTR(rc); + } + + aspeed_espi_oob_enable(espi_oob); + return espi_oob; +} + +void aspeed_espi_oob_free(struct device *dev, struct aspeed_espi_oob *espi_oob) +{ + mutex_destroy(&espi_oob->put_tx_mtx); + mutex_destroy(&espi_oob->get_rx_mtx); + misc_deregister(&espi_oob->mdev); +} diff --git a/drivers/soc/aspeed/aspeed-espi-oob.h b/drivers/soc/aspeed/aspeed-espi-oob.h new file mode 100644 index 000000000000..ee5fb3d09770 --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-oob.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 Aspeed Technology Inc. + */ +#ifndef _ASPEED_ESPI_OOB_H_ +#define _ASPEED_ESPI_OOB_H_ + +struct oob_tx_dma_desc { + u32 data_addr; + u8 cyc; + u16 tag : 4; + u16 len : 12; + u8 msg_type : 3; + u8 raz0 : 1; + u8 pec : 1; + u8 int_en : 1; + u8 pause : 1; + u8 raz1 : 1; + u32 raz2; + u32 raz3; +} __packed; + +struct oob_rx_dma_desc { + u32 data_addr; + u8 cyc; + u16 tag : 4; + u16 len : 12; + u8 raz : 7; + u8 dirty : 1; +} __packed; + +struct aspeed_espi_oob_dma { + u32 tx_desc_num; + u32 rx_desc_num; + + struct oob_tx_dma_desc *tx_desc; + dma_addr_t tx_desc_addr; + + struct oob_rx_dma_desc *rx_desc; + dma_addr_t rx_desc_addr; + + void *tx_virt; + dma_addr_t tx_addr; + + void *rx_virt; + dma_addr_t rx_addr; +}; + +struct aspeed_espi_oob { + u32 dma_mode; + struct aspeed_espi_oob_dma dma; + + u32 rx_ready; + wait_queue_head_t wq; + /* Locks rx resources and allow one receive at a time */ + struct mutex get_rx_mtx; + /* Locks tx resources and allow one transmit at a time */ + struct mutex put_tx_mtx; + /* Lock to synchronize receive in irq context */ + spinlock_t lock; + + struct miscdevice mdev; + struct aspeed_espi_ctrl *ctrl; +}; + +void aspeed_espi_oob_event(u32 sts, struct aspeed_espi_oob *espi_oob); +void aspeed_espi_oob_enable(struct aspeed_espi_oob *espi_oob); +void *aspeed_espi_oob_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl); +void aspeed_espi_oob_free(struct device *dev, struct aspeed_espi_oob *espi_oob); + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-slave.c b/drivers/soc/aspeed/aspeed-espi-slave.c index 631d2484f8ca..431cfa383c3d 100644 --- a/drivers/soc/aspeed/aspeed-espi-slave.c +++ b/drivers/soc/aspeed/aspeed-espi-slave.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2015-2019, Intel Corporation. +#include <linux/aspeed-espi-ioc.h> #include <linux/clk.h> #include <linux/fs.h> #include <linux/interrupt.h> @@ -8,6 +9,7 @@ #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/poll.h> #include <linux/regmap.h> @@ -17,6 +19,7 @@ #include <linux/uaccess.h> #include "aspeed-espi-ctrl.h" +#include "aspeed-espi-oob.h" struct aspeed_espi { struct regmap *map; @@ -32,6 +35,8 @@ struct aspeed_espi { wait_queue_head_t pltrstn_waitq; char pltrstn; bool pltrstn_in_avail; + struct aspeed_espi_ctrl *espi_ctrl; + }; static void aspeed_espi_sys_event(struct aspeed_espi *priv) @@ -138,12 +143,14 @@ static irqreturn_t aspeed_espi_irq(int irq, void *arg) aspeed_espi_sys_event(priv); sts_handled |= ASPEED_ESPI_VW_SYSEVT; } - if (sts & ASPEED_ESPI_VW_SYSEVT1) { aspeed_espi_sys_event1(priv); sts_handled |= ASPEED_ESPI_VW_SYSEVT1; } - + if (sts & ASPEED_ESPI_INT_STS_OOB_BITS) { + aspeed_espi_oob_event(sts, priv->espi_ctrl->oob); + regmap_write(priv->map, ASPEED_ESPI_INT_STS, sts & ASPEED_ESPI_INT_STS_OOB_BITS); + } if (sts & ASPEED_ESPI_HW_RESET) { if (priv->rst_irq < 0) { regmap_write_bits(priv->map, ASPEED_ESPI_CTRL, @@ -158,6 +165,7 @@ static irqreturn_t aspeed_espi_irq(int irq, void *arg) ASPEED_ESPI_CTRL_OOB_CHRDY); aspeed_espi_boot_ack(priv); sts_handled |= ASPEED_ESPI_HW_RESET; + aspeed_espi_oob_enable(priv->espi_ctrl->oob); } regmap_write(priv->map, ASPEED_ESPI_INT_STS, sts); @@ -294,11 +302,12 @@ static const struct regmap_config aspeed_espi_regmap_cfg = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = ASPEED_ESPI_SYSEVT1_INT_STS, + .max_register = 0x200, }; static int aspeed_espi_probe(struct platform_device *pdev) { + struct aspeed_espi_ctrl *espi_ctrl; struct aspeed_espi *priv; struct resource *res; void __iomem *regs; @@ -314,13 +323,25 @@ static int aspeed_espi_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + espi_ctrl = devm_kzalloc(&pdev->dev, sizeof(*espi_ctrl), GFP_KERNEL); + if (!espi_ctrl) + return -ENOMEM; + dev_set_drvdata(&pdev->dev, priv); priv->dev = &pdev->dev; - + priv->espi_ctrl = espi_ctrl; + espi_ctrl->model = of_device_get_match_data(&pdev->dev); priv->map = devm_regmap_init_mmio(&pdev->dev, regs, &aspeed_espi_regmap_cfg); if (IS_ERR(priv->map)) return PTR_ERR(priv->map); + espi_ctrl->map = priv->map; + aspeed_espi_config_irq(priv); + espi_ctrl->oob = aspeed_espi_oob_alloc(&pdev->dev, espi_ctrl); + if (IS_ERR(espi_ctrl->oob)) { + dev_err(&pdev->dev, "Failed to allocate espi out-of-band channel\n"); + return PTR_ERR(espi_ctrl->oob); + } spin_lock_init(&priv->pltrstn_lock); init_waitqueue_head(&priv->pltrstn_waitq); @@ -388,7 +409,6 @@ static int aspeed_espi_probe(struct platform_device *pdev) goto err_clk_disable_out; } - aspeed_espi_config_irq(priv); aspeed_espi_boot_ack(priv); dev_info(&pdev->dev, "eSPI registered, irq %d\n", priv->irq); @@ -404,15 +424,20 @@ static int aspeed_espi_remove(struct platform_device *pdev) { struct aspeed_espi *priv = dev_get_drvdata(&pdev->dev); + aspeed_espi_oob_free(priv->dev, priv->espi_ctrl->oob); misc_deregister(&priv->pltrstn_miscdev); clk_disable_unprepare(priv->clk); - return 0; } +static const struct aspeed_espi_model ast2600_model = { + .version = ASPEED_ESPI_AST2600, +}; + static const struct of_device_id of_espi_match_table[] = { { .compatible = "aspeed,ast2500-espi-slave" }, - { .compatible = "aspeed,ast2600-espi-slave" }, + { .compatible = "aspeed,ast2600-espi-slave", + .data = &ast2600_model}, { } }; MODULE_DEVICE_TABLE(of, of_espi_match_table); |