Add new IOCTL commands to perform eSPI peripheral channel(PC) IO read/write operations with peripherals connected to eSPI slave0. The changes include validation of IO configuration before initiating the read/write operations. Co-developed-by: Krishnamoorthi M <krishnamoorthi.m@xxxxxxx> Signed-off-by: Krishnamoorthi M <krishnamoorthi.m@xxxxxxx> Co-developed-by: Akshata MukundShetty <akshata.mukundshetty@xxxxxxx> Signed-off-by: Akshata MukundShetty <akshata.mukundshetty@xxxxxxx> Signed-off-by: Raju Rangoju <Raju.Rangoju@xxxxxxx> --- drivers/spi/espi-amd-core.c | 95 +++++++++++++++++++++++++++++++++++++ drivers/spi/espi-amd-dev.c | 53 +++++++++++++++++++++ drivers/spi/espi-amd.h | 16 +++++++ 3 files changed, 164 insertions(+) diff --git a/drivers/spi/espi-amd-core.c b/drivers/spi/espi-amd-core.c index 3704cbd816ae..18bfd2ca2316 100644 --- a/drivers/spi/espi-amd-core.c +++ b/drivers/spi/espi-amd-core.c @@ -114,6 +114,8 @@ static int amd_espi_alloc_cmd_data(struct espi_txcmd *cmd) case GET_CONFIGURATION: case IN_BAND_RESET: size = 1; + break; + case PERIPHERAL_CHNL: default: break; } @@ -710,6 +712,99 @@ void amd_espi_disable_io_decode_range(struct amd_espi *amd_espi, u32 io_range) writel(io_mmio_dc_enable, (ESPI_BASE + AMD_ESPI_SLAVE0_DECODE_EN_REG)); } +static int is_port_address_valid(struct amd_espi *amd_espi, struct periph_io_rw *msg_io) +{ + struct io_mmio_decode_config io_conf; + u16 port_end_addr = msg_io->port + msg_io->len - 1; + + amd_espi_get_io_mmio_decode_info(amd_espi, &io_conf); + + if (msg_io->port >= io_conf.range0.base_addr_range0 && + (port_end_addr <= ((u16)io_conf.range0.base_addr_range0 + + io_conf.range2.io_range0_size))) { + if (!(io_conf.io_mmio_dc_enable & IO_DECODE_RANGE0)) { + dev_err(amd_espi->dev, "IO range0 not enabled for port address: 0x%x\n", + msg_io->port); + return -EINVAL; + } + } else if ((msg_io->port >= io_conf.range0.base_addr_range1) && + (port_end_addr <= ((u16)io_conf.range0.base_addr_range1 + + io_conf.range2.io_range1_size))) { + if (!(io_conf.io_mmio_dc_enable & IO_DECODE_RANGE1)) { + dev_err(amd_espi->dev, "IO range1 not enabled for port address: 0x%x\n", + msg_io->port); + return -EINVAL; + } + } else if ((msg_io->port >= io_conf.range1.base_addr_range2) && + (port_end_addr <= ((u16)io_conf.range1.base_addr_range2 + + io_conf.range2.io_range2_size))) { + if (!(io_conf.io_mmio_dc_enable & IO_DECODE_RANGE2)) { + dev_err(amd_espi->dev, "IO range2 not enabled for port address: 0x%x\n", + msg_io->port); + return -EINVAL; + } + } else if ((msg_io->port >= io_conf.range1.base_addr_range3) && + (port_end_addr <= ((u16)io_conf.range1.base_addr_range3 + + io_conf.range2.io_range3_size))) { + if (!(io_conf.io_mmio_dc_enable & IO_DECODE_RANGE3)) { + dev_err(amd_espi->dev, "IO range3 not enabled for port address: 0x%x\n", + msg_io->port); + return -EINVAL; + } + } else { + dev_err(amd_espi->dev, "Port address 0x%x is invalid\n", msg_io->port); + return -EINVAL; + } + + return CB_SUCCESS; +} + +int amd_espi_periph_io_write(struct amd_espi *amd_espi, struct periph_io_rw *msg_io) +{ + if (is_port_address_valid(amd_espi, msg_io) != CB_SUCCESS) + return -EINVAL; + + switch (msg_io->len) { + case 1: + outb(msg_io->data.data_b, msg_io->port); + break; + case 2: + outw(msg_io->data.data_w, msg_io->port); + break; + case 4: + outl(msg_io->data.data_l, msg_io->port); + break; + default: + dev_err(amd_espi->dev, "Length of IO packet is not valid\n"); + return -EINVAL; + } + + return CB_SUCCESS; +} + +int amd_espi_periph_io_read(struct amd_espi *amd_espi, struct periph_io_rw *msg_io) +{ + if (is_port_address_valid(amd_espi, msg_io) != CB_SUCCESS) + return -EINVAL; + + switch (msg_io->len) { + case 1: + msg_io->data.data_b = inb(msg_io->port); + break; + case 2: + msg_io->data.data_w = inw(msg_io->port); + break; + case 4: + msg_io->data.data_l = inl(msg_io->port); + break; + default: + dev_err(amd_espi->dev, "Length of IO packet is not valid\n"); + return -EINVAL; + } + + return CB_SUCCESS; +} + static int amd_espi_get_master_cap(struct amd_espi *amd_espi, struct espi_master *master) { u32 master_cap_reg, info; diff --git a/drivers/spi/espi-amd-dev.c b/drivers/spi/espi-amd-dev.c index 31fb06f4a3ff..184ffdad4a48 100644 --- a/drivers/spi/espi-amd-dev.c +++ b/drivers/spi/espi-amd-dev.c @@ -325,6 +325,53 @@ static int amd_espi_ioctl_enable_io_decode_conf(struct amd_espi *amd_espi, unsig return ret; } +static int amd_espi_ioctl_io_write(struct amd_espi *amd_espi, unsigned long arg) +{ + struct periph_io_rw *message_io; + int ret; + + message_io = kzalloc(sizeof(*message_io), GFP_KERNEL); + if (!message_io) + return -ENOMEM; + + if (copy_from_user(message_io, (void __user *)arg, sizeof(struct periph_io_rw))) { + ret = -EFAULT; + goto io_write_free; + } + + ret = amd_espi_periph_io_write(amd_espi, message_io); + +io_write_free: + kfree(message_io); + return ret; +} + +static int amd_espi_ioctl_io_read(struct amd_espi *amd_espi, unsigned long arg) +{ + struct periph_io_rw *message_io; + int ret; + + message_io = kzalloc(sizeof(*message_io), GFP_KERNEL); + if (!message_io) + return -ENOMEM; + + if (copy_from_user(message_io, (void __user *)arg, sizeof(struct periph_io_rw))) { + ret = -EFAULT; + goto io_read_free; + } + + ret = amd_espi_periph_io_read(amd_espi, message_io); + if (ret != CB_SUCCESS) + goto io_read_free; + + if (copy_to_user((void __user *)arg, message_io, sizeof(struct periph_io_rw))) + ret = -EFAULT; + +io_read_free: + kfree(message_io); + return ret; +} + static long amd_espi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct amd_espi *amd_espi = filp->private_data; @@ -369,6 +416,12 @@ static long amd_espi_ioctl(struct file *filp, unsigned int cmd, unsigned long ar } amd_espi_disable_io_decode_range(amd_espi, io_range); break; + case ESPI_IO_WRITE: + ret = amd_espi_ioctl_io_write(amd_espi, arg); + break; + case ESPI_IO_READ: + ret = amd_espi_ioctl_io_read(amd_espi, arg); + break; default: dev_err(amd_espi->dev, "ESPI command not found, returning error\n"); ret = -EINVAL; diff --git a/drivers/spi/espi-amd.h b/drivers/spi/espi-amd.h index ae76243786f0..4c67aa9f45a6 100644 --- a/drivers/spi/espi-amd.h +++ b/drivers/spi/espi-amd.h @@ -176,6 +176,8 @@ #define ESPI_GET_IODECODE_CONFIG _IOWR(ESPI_MAGIC_NUMBER, 0x7, struct io_mmio_decode_config) #define ESPI_EN_IODECODE_CONFIG _IOWR(ESPI_MAGIC_NUMBER, 0x8, struct io_mmio_decode_config) #define ESPI_DS_IODECODE_CONFIG _IOWR(ESPI_MAGIC_NUMBER, 0x9, u32) +#define ESPI_IO_WRITE _IOWR(ESPI_MAGIC_NUMBER, 0xa, struct periph_io_rw) +#define ESPI_IO_READ _IOWR(ESPI_MAGIC_NUMBER, 0xb, struct periph_io_rw) /* * enum amd_espi_versions - eSPI controller versions @@ -368,6 +370,18 @@ struct io_mmio_decode_config { union mmio_target_range5 mmio_range5; }; +union io_data { + u8 data_b; + u16 data_w; + u32 data_l; +}; + +struct periph_io_rw { + u8 len; + u16 port; + union io_data data; +} __packed; + /* Function prototypes */ int amd_espi_device_create(struct amd_espi *amd_espi, struct device *dev); void amd_espi_device_remove(struct amd_espi *amd_espi); @@ -388,4 +402,6 @@ void amd_espi_get_io_mmio_decode_info(struct amd_espi *amd_espi, void amd_espi_set_io_mmio_decode_config(struct amd_espi *amd_espi, struct io_mmio_decode_config *config); void amd_espi_disable_io_decode_range(struct amd_espi *amd_espi, u32 io_range); +int amd_espi_periph_io_write(struct amd_espi *amd_espi, struct periph_io_rw *message_io); +int amd_espi_periph_io_read(struct amd_espi *amd_espi, struct periph_io_rw *message_io); #endif -- 2.34.1