diff options
Diffstat (limited to 'drivers/hid/intel-thc-hid/intel-thc')
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c | 1578 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h | 116 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c | 969 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h | 146 | ||||
-rw-r--r-- | drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h | 881 |
5 files changed, 3690 insertions, 0 deletions
diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c new file mode 100644 index 000000000000..4fc78b5a04b5 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c @@ -0,0 +1,1578 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include <linux/bitfield.h> +#include <linux/regmap.h> + +#include "intel-thc-dev.h" +#include "intel-thc-hw.h" + +static int thc_regmap_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct thc_device *thc_ctx = context; + void __iomem *base = thc_ctx->mmio_addr; + + *val = ioread32(base + reg); + return 0; +} + +static int thc_regmap_write(void *context, unsigned int reg, + unsigned int val) +{ + struct thc_device *thc_ctx = context; + void __iomem *base = thc_ctx->mmio_addr; + + iowrite32(val, base + reg); + return 0; +} + +static const struct regmap_range thc_rw_ranges[] = { + regmap_reg_range(0x10, 0x14), + regmap_reg_range(0x1000, 0x1320), +}; + +static const struct regmap_access_table thc_rw_table = { + .yes_ranges = thc_rw_ranges, + .n_yes_ranges = ARRAY_SIZE(thc_rw_ranges), +}; + +static const struct regmap_config thc_regmap_cfg = { + .name = "thc_regmap_common", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x1320, + .reg_read = thc_regmap_read, + .reg_write = thc_regmap_write, + .cache_type = REGCACHE_NONE, + .fast_io = true, + .rd_table = &thc_rw_table, + .wr_table = &thc_rw_table, + .volatile_table = &thc_rw_table, +}; + +/** + * thc_clear_state - Clear THC hardware state + * + * @dev: The pointer of THC device structure + */ +static void thc_clear_state(const struct thc_device *dev) +{ + u32 val; + + /* Clear interrupt cause register */ + val = THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY | + THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR | + THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR | + THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, val, val); + + /* Clear interrupt error state */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_IE_STALL, + THC_M_PRT_READ_DMA_CNTRL_IE_STALL); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_IE_STALL, + THC_M_PRT_READ_DMA_CNTRL_IE_STALL); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS, + THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS, + THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS); + + val = THC_M_PRT_INT_EN_TXN_ERR_INT_EN | + THC_M_PRT_INT_EN_FATAL_ERR_INT_EN | + THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET, val, val); + + val = THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | + THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, val, val); + + /* Clear RxDMA state */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_IE_EOF, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_IE_EOF, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS, + THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS); + + /* Clear TxDMA state */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL); + + val = THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS | + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS | + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_INT_STS_OFFSET, val, val); + + /* Reset all DMAs count */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DB_CNT_1_OFFSET, + THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT_RST, + THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CNT_OFFSET, + THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT_RST, + THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); + + /* Reset THC hardware sequence state */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRAME_DROP_CNT_1_OFFSET, + THC_M_PRT_FRAME_DROP_CNT_1_RFDC, + THC_M_PRT_FRAME_DROP_CNT_1_RFDC); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRAME_DROP_CNT_2_OFFSET, + THC_M_PRT_FRAME_DROP_CNT_2_RFDC, + THC_M_PRT_FRAME_DROP_CNT_2_RFDC); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRM_CNT_1_OFFSET, + THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT_RST, + THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_FRM_CNT_2_OFFSET, + THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT_RST, + THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_RXDMA_PKT_CNT_1_OFFSET, + THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT_RST, + THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_RXDMA_PKT_CNT_2_OFFSET, + THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT_RST, + THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SWINT_CNT_1_OFFSET, + THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST, + THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SWINT_CNT_1_OFFSET, + THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST, + THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TX_FRM_CNT_OFFSET, + THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT_RST, + THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TXDMA_PKT_CNT_OFFSET, + THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT_RST, + THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_UFRM_CNT_1_OFFSET, + THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT_RST, + THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT_RST); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_UFRM_CNT_2_OFFSET, + THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT_RST, + THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT_RST); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_PRD_EMPTY_CNT_1_OFFSET, + THC_M_PRT_PRD_EMPTY_CNT_1_RPTEC, + THC_M_PRT_PRD_EMPTY_CNT_1_RPTEC); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_PRD_EMPTY_CNT_2_OFFSET, + THC_M_PRT_PRD_EMPTY_CNT_2_RPTEC, + THC_M_PRT_PRD_EMPTY_CNT_2_RPTEC); +} + +/** + * thc_dev_init - Allocate and initialize the THC device structure + * + * @device: The pointer of device structure + * @mem_addr: The pointer of MMIO memory address + * + * Return: The thc_device pointer on success, NULL on failed. + */ +struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr) +{ + struct thc_device *thc_dev; + int ret; + + thc_dev = devm_kzalloc(device, sizeof(*thc_dev), GFP_KERNEL); + if (!thc_dev) + return ERR_PTR(-ENOMEM); + + thc_dev->dev = device; + thc_dev->mmio_addr = mem_addr; + thc_dev->thc_regmap = devm_regmap_init(device, NULL, thc_dev, &thc_regmap_cfg); + if (IS_ERR(thc_dev->thc_regmap)) { + ret = PTR_ERR(thc_dev->thc_regmap); + dev_err_once(device, "Failed to init thc_regmap: %d\n", ret); + return ERR_PTR(ret); + } + + thc_clear_state(thc_dev); + + mutex_init(&thc_dev->thc_bus_lock); + init_waitqueue_head(&thc_dev->write_complete_wait); + init_waitqueue_head(&thc_dev->swdma_complete_wait); + + thc_dev->dma_ctx = thc_dma_init(thc_dev); + if (!thc_dev->dma_ctx) { + dev_err_once(device, "DMA context init failed\n"); + return ERR_PTR(-ENOMEM); + } + + return thc_dev; +} +EXPORT_SYMBOL_NS_GPL(thc_dev_init, "INTEL_THC"); + +static int prepare_pio(const struct thc_device *dev, const u8 pio_op, + const u32 address, const u32 size) +{ + u32 sts, ctrl, addr, mask; + + regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts); + + /* Check if THC previous PIO still in progress */ + if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP) { + dev_err_once(dev->dev, "THC PIO is still busy!\n"); + return -EBUSY; + } + + /* Clear error bit and complete bit in state register */ + sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | + THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts); + + /* Set PIO data size, opcode and interrupt capability */ + ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, size) | + FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD, pio_op); + if (dev->pio_int_supported) + ctrl |= THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE; + + mask = THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC | + THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD | + THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_CNTRL_OFFSET, mask, ctrl); + + /* Set PIO target address */ + addr = FIELD_PREP(THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR, address); + mask = THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, mask, addr); + return 0; +} + +static void pio_start(const struct thc_device *dev, + u32 size_in_bytes, const u32 *buffer) +{ + if (size_in_bytes && buffer) + regmap_bulk_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET, + buffer, size_in_bytes / sizeof(u32)); + + /* Enable Start bit */ + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_CNTRL_OFFSET, + THC_M_PRT_SW_SEQ_CNTRL_TSSGO, + THC_M_PRT_SW_SEQ_CNTRL_TSSGO); +} + +static int pio_complete(const struct thc_device *dev, + u32 *buffer, u32 *size) +{ + u32 sts, ctrl; + + regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts); + if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_ERR) { + dev_err_once(dev->dev, "PIO operation error\n"); + return -EBUSY; + } + + if (buffer && size) { + regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_CNTRL_OFFSET, &ctrl); + *size = FIELD_GET(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, ctrl); + + regmap_bulk_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET, + buffer, *size / sizeof(u32)); + } + + sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts); + return 0; +} + +static int pio_wait(const struct thc_device *dev) +{ + u32 sts = 0; + int ret; + + ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts, + !(sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP || + !(sts & THC_M_PRT_SW_SEQ_STS_TSSDONE)), + THC_REGMAP_POLLING_INTERVAL_US, THC_PIO_DONE_TIMEOUT_US); + if (ret) + dev_err_once(dev->dev, "Timeout while polling PIO operation done\n"); + + return ret; +} + +/** + * thc_tic_pio_read - Read data from touch device by PIO + * + * @dev: The pointer of THC private device context + * @address: Slave address for the PIO operation + * @size: Expected read data size + * @actual_size: The pointer of the actual data size read from touch device + * @buffer: The pointer of data buffer to store the data read from touch device + * + * Return: 0 on success, other error codes on failed. + */ +int thc_tic_pio_read(struct thc_device *dev, const u32 address, + const u32 size, u32 *actual_size, u32 *buffer) +{ + u8 opcode; + int ret; + + if (size <= 0 || !actual_size || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %u, actual_size %p, buffer %p\n", + size, actual_size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + opcode = (dev->port_type == THC_PORT_TYPE_SPI) ? + THC_PIO_OP_SPI_TIC_READ : THC_PIO_OP_I2C_TIC_READ; + + ret = prepare_pio(dev, opcode, address, size); + if (ret < 0) + goto end; + + pio_start(dev, 0, NULL); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, buffer, actual_size); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_tic_pio_read, "INTEL_THC"); + +/** + * thc_tic_pio_write - Write data to touch device by PIO + * + * @dev: The pointer of THC private device context + * @address: Slave address for the PIO operation + * @size: PIO write data size + * @buffer: The pointer of the write data buffer + * + * Return: 0 on success, other error codes on failed. + */ +int thc_tic_pio_write(struct thc_device *dev, const u32 address, + const u32 size, const u32 *buffer) +{ + u8 opcode; + int ret; + + if (size <= 0 || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %u, buffer %p\n", + size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + opcode = (dev->port_type == THC_PORT_TYPE_SPI) ? + THC_PIO_OP_SPI_TIC_WRITE : THC_PIO_OP_I2C_TIC_WRITE; + + ret = prepare_pio(dev, opcode, address, size); + if (ret < 0) + goto end; + + pio_start(dev, size, buffer); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, NULL, NULL); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write, "INTEL_THC"); + +/** + * thc_tic_pio_write_and_read - Write data followed by read data by PIO + * + * @dev: The pointer of THC private device context + * @address: Slave address for the PIO operation + * @write_size: PIO write data size + * @write_buffer: The pointer of the write data buffer + * @read_size: Expected PIO read data size + * @actual_size: The pointer of the actual read data size + * @read_buffer: The pointer of PIO read data buffer + * + * Return: 0 on success, other error codes on failed. + */ +int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address, + const u32 write_size, const u32 *write_buffer, + const u32 read_size, u32 *actual_size, u32 *read_buffer) +{ + u32 i2c_ctrl, mask; + int ret; + + if (dev->port_type == THC_PORT_TYPE_SPI) { + dev_err(dev->dev, "SPI port type doesn't support pio write and read!"); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + /* Config i2c PIO write and read sequence */ + i2c_ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC, write_size); + mask = THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC; + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET, + mask, i2c_ctrl); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET, + THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN, + THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN); + + ret = prepare_pio(dev, THC_PIO_OP_I2C_TIC_WRITE_AND_READ, address, read_size); + if (ret < 0) + goto end; + + pio_start(dev, write_size, write_buffer); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, read_buffer, actual_size); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write_and_read, "INTEL_THC"); + +/** + * thc_interrupt_config - Configure THC interrupts + * + * @dev: The pointer of THC private device context + */ +void thc_interrupt_config(struct thc_device *dev) +{ + u32 mbits, mask, r_dma_ctrl_1; + + /* Clear Error reporting interrupt status bits */ + mbits = THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS | + THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_INT_STATUS_OFFSET, + mbits, mbits); + + /* Enable Error Reporting Interrupts */ + mbits = THC_M_PRT_INT_EN_TXN_ERR_INT_EN | + THC_M_PRT_INT_EN_FATAL_ERR_INT_EN | + THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_INT_EN_OFFSET, + mbits, mbits); + + /* Clear PIO Interrupt status bits */ + mbits = THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | + THC_M_PRT_SW_SEQ_STS_TSSDONE; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SW_SEQ_STS_OFFSET, + mbits, mbits); + + /* Read Interrupts */ + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + &r_dma_ctrl_1); + /* Disable RxDMA1 */ + r_dma_ctrl_1 &= ~THC_M_PRT_READ_DMA_CNTRL_IE_EOF; + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + r_dma_ctrl_1); + + /* Ack EOF Interrupt RxDMA1 */ + mbits = THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS; + /* Ack NonDMA Interrupt */ + mbits |= THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + mbits, mbits); + + /* Ack EOF Interrupt RxDMA2 */ + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS, + THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS); + + /* Write Interrupts */ + /* Disable TxDMA */ + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL, + 0); + + /* Clear TxDMA interrupt status bits */ + mbits = THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS; + mbits |= THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_WRITE_INT_STS_OFFSET, + mbits, mbits); + + /* Enable Non-DMA device inband interrupt */ + r_dma_ctrl_1 |= THC_M_PRT_READ_DMA_CNTRL_IE_NDDI; + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + r_dma_ctrl_1); + + if (dev->port_type == THC_PORT_TYPE_SPI) { + /* Edge triggered interrupt */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN); + } else { + /* Level triggered interrupt */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, 0); + + mbits = THC_M_PRT_INT_EN_THC_I2C_IC_MST_ON_HOLD_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_SCL_STUCK_AT_LOW_DET_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_TX_ABRT_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_TX_OVER_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_RX_FULL_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_RX_OVER_INT_EN | + THC_M_PRT_INT_EN_THC_I2C_IC_RX_UNDER_INT_EN; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET, + mbits, mbits); + } + + thc_set_pio_interrupt_support(dev, false); + + /* HIDSPI specific settings */ + if (dev->port_type == THC_PORT_TYPE_SPI) { + mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET, + THC_BIT_OFFSET_INTERRUPT_TYPE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN, + THC_BIT_LENGTH_INTERRUPT_TYPE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET, + THC_BIT_OFFSET_LAST_FRAGMENT_FLAG) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL, + THC_BITMASK_INVALID_TYPE_DATA); + mask = THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET | + THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN | + THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET | + THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_1_OFFSET, + mask, mbits); + + mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET, + THC_BIT_OFFSET_MICROFRAME_SIZE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN, + THC_BIT_LENGTH_MICROFRAME_SIZE) | + FIELD_PREP(THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT, + THC_UNIT_MICROFRAME_SIZE) | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL; + mask = THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE | + THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_DEVINT_CFG_2_OFFSET, + mask, mbits); + } +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_config, "INTEL_THC"); + +/** + * thc_int_trigger_type_select - Select THC interrupt trigger type + * + * @dev: the pointer of THC private device context + * @edge_trigger: determine the interrupt is edge triggered or level triggered + */ +void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger) +{ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_TSEQ_CNTRL_1_OFFSET, + THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN, + edge_trigger ? THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN : 0); +} +EXPORT_SYMBOL_NS_GPL(thc_int_trigger_type_select, "INTEL_THC"); + +/** + * thc_interrupt_enable - Enable or disable THC interrupt + * + * @dev: the pointer of THC private device context + * @int_enable: the flag to control THC interrupt enable or disable + */ +void thc_interrupt_enable(struct thc_device *dev, bool int_enable) +{ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_INT_EN_OFFSET, + THC_M_PRT_INT_EN_GBL_INT_EN, + int_enable ? THC_M_PRT_INT_EN_GBL_INT_EN : 0); +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_enable, "INTEL_THC"); + +/** + * thc_interrupt_quiesce - Quiesce or unquiesce external touch device interrupt + * + * @dev: the pointer of THC private device context + * @int_quiesce: the flag to determine quiesce or unquiesce device interrupt + * + * Return: 0 on success, other error codes on failed + */ +int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce) +{ + u32 ctrl; + int ret; + + regmap_read(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, &ctrl); + if (!(ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && !int_quiesce) { + dev_warn(dev->dev, "THC interrupt already unquiesce\n"); + return 0; + } + + if ((ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN) && int_quiesce) { + dev_warn(dev->dev, "THC interrupt already quiesce\n"); + return 0; + } + + /* Quiesce device interrupt - Set quiesce bit and waiting for THC HW to ACK */ + if (int_quiesce) + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, + THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN, + THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN); + + ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, ctrl, + ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS, + THC_REGMAP_POLLING_INTERVAL_US, THC_QUIESCE_EN_TIMEOUT_US); + if (ret) { + dev_err_once(dev->dev, + "Timeout while waiting THC idle, target quiesce state = %s\n", + int_quiesce ? "true" : "false"); + return ret; + } + + /* Unquiesce device interrupt - Clear the quiesce bit */ + if (!int_quiesce) + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, + THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN, 0); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_quiesce, "INTEL_THC"); + +/** + * thc_set_pio_interrupt_support - Determine PIO interrupt is supported or not + * + * @dev: The pointer of THC private device context + * @supported: The flag to determine enabling PIO interrupt or not + */ +void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported) +{ + dev->pio_int_supported = supported; +} +EXPORT_SYMBOL_NS_GPL(thc_set_pio_interrupt_support, "INTEL_THC"); + +/** + * thc_ltr_config - Configure THC Latency Tolerance Reporting(LTR) settings + * + * @dev: The pointer of THC private device context + * @active_ltr_us: active LTR value, unit is us + * @lp_ltr_us: low power LTR value, unit is us + */ +void thc_ltr_config(struct thc_device *dev, u32 active_ltr_us, u32 lp_ltr_us) +{ + u32 active_ltr_scale, lp_ltr_scale, ltr_ctrl, ltr_mask, orig, tmp; + + if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_3 && + active_ltr_us < THC_LTR_MAX_VAL_SCALE_3) { + active_ltr_scale = THC_LTR_SCALE_3; + active_ltr_us = active_ltr_us >> 5; + } else if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_4 && + active_ltr_us < THC_LTR_MAX_VAL_SCALE_4) { + active_ltr_scale = THC_LTR_SCALE_4; + active_ltr_us = active_ltr_us >> 10; + } else if (active_ltr_us >= THC_LTR_MIN_VAL_SCALE_5 && + active_ltr_us < THC_LTR_MAX_VAL_SCALE_5) { + active_ltr_scale = THC_LTR_SCALE_5; + active_ltr_us = active_ltr_us >> 15; + } else { + active_ltr_scale = THC_LTR_SCALE_2; + } + + if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_3 && + lp_ltr_us < THC_LTR_MAX_VAL_SCALE_3) { + lp_ltr_scale = THC_LTR_SCALE_3; + lp_ltr_us = lp_ltr_us >> 5; + } else if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_4 && + lp_ltr_us < THC_LTR_MAX_VAL_SCALE_4) { + lp_ltr_scale = THC_LTR_SCALE_4; + lp_ltr_us = lp_ltr_us >> 10; + } else if (lp_ltr_us >= THC_LTR_MIN_VAL_SCALE_5 && + lp_ltr_us < THC_LTR_MAX_VAL_SCALE_5) { + lp_ltr_scale = THC_LTR_SCALE_5; + lp_ltr_us = lp_ltr_us >> 15; + } else { + lp_ltr_scale = THC_LTR_SCALE_2; + } + + regmap_read(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, &orig); + ltr_ctrl = FIELD_PREP(THC_M_CMN_LTR_CTRL_ACT_LTR_VAL, active_ltr_us) | + FIELD_PREP(THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE, active_ltr_scale) | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN | + FIELD_PREP(THC_M_CMN_LTR_CTRL_LP_LTR_VAL, lp_ltr_us) | + FIELD_PREP(THC_M_CMN_LTR_CTRL_LP_LTR_SCALE, lp_ltr_scale) | + THC_M_CMN_LTR_CTRL_LP_LTR_REQ; + + ltr_mask = THC_M_CMN_LTR_CTRL_ACT_LTR_VAL | + THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN | + THC_M_CMN_LTR_CTRL_LP_LTR_VAL | + THC_M_CMN_LTR_CTRL_LP_LTR_SCALE | + THC_M_CMN_LTR_CTRL_LP_LTR_REQ | + THC_M_CMN_LTR_CTRL_LP_LTR_EN; + + tmp = orig & ~ltr_mask; + tmp |= ltr_ctrl & ltr_mask; + + regmap_write(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, tmp); +} +EXPORT_SYMBOL_NS_GPL(thc_ltr_config, "INTEL_THC"); + +/** + * thc_change_ltr_mode - Change THC LTR mode + * + * @dev: The pointer of THC private device context + * @ltr_mode: LTR mode(active or low power) + */ +void thc_change_ltr_mode(struct thc_device *dev, u32 ltr_mode) +{ + if (ltr_mode == THC_LTR_MODE_ACTIVE) { + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_LP_LTR_EN, 0); + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN, + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN); + return; + } + + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN, 0); + regmap_write_bits(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, + THC_M_CMN_LTR_CTRL_LP_LTR_EN, + THC_M_CMN_LTR_CTRL_LP_LTR_EN); +} +EXPORT_SYMBOL_NS_GPL(thc_change_ltr_mode, "INTEL_THC"); + +/** + * thc_ltr_unconfig - Unconfigure THC Latency Tolerance Reporting(LTR) settings + * + * @dev: The pointer of THC private device context + */ +void thc_ltr_unconfig(struct thc_device *dev) +{ + u32 ltr_ctrl, bits_clear; + + regmap_read(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, <r_ctrl); + bits_clear = THC_M_CMN_LTR_CTRL_LP_LTR_EN | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN | + THC_M_CMN_LTR_CTRL_LP_LTR_REQ | + THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ; + + ltr_ctrl &= ~bits_clear; + + regmap_write(dev->thc_regmap, THC_M_CMN_LTR_CTRL_OFFSET, ltr_ctrl); +} +EXPORT_SYMBOL_NS_GPL(thc_ltr_unconfig, "INTEL_THC"); + +/** + * thc_int_cause_read - Read interrupt cause register value + * + * @dev: The pointer of THC private device context + * + * Return: The interrupt cause register value + */ +u32 thc_int_cause_read(struct thc_device *dev) +{ + u32 int_cause; + + regmap_read(dev->thc_regmap, + THC_M_PRT_DEV_INT_CAUSE_REG_VAL_OFFSET, &int_cause); + + return int_cause; +} +EXPORT_SYMBOL_NS_GPL(thc_int_cause_read, "INTEL_THC"); + +static void thc_print_txn_error_cause(const struct thc_device *dev) +{ + bool known_error = false; + u32 cause = 0; + + regmap_read(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, &cause); + + if (cause & THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR) { + dev_err(dev->dev, "TXN Error: Invalid PRD Entry\n"); + known_error = true; + } + if (cause & THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR) { + dev_err(dev->dev, "TXN Error: THC Buffer Overrun\n"); + known_error = true; + } + if (cause & THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR) { + dev_err(dev->dev, "TXN Error: Frame Babble\n"); + known_error = true; + } + if (cause & THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY) { + dev_err(dev->dev, "TXN Error: Invalid Device Register Setting\n"); + known_error = true; + } + + /* Clear interrupt status bits */ + regmap_write(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, cause); + + if (!known_error) + dev_err(dev->dev, "TXN Error does not match any known value: 0x%X\n", + cause); +} + +/** + * thc_interrupt_handler - Handle THC interrupts + * + * THC interrupts include several types: external touch device (TIC) non-DMA + * interrupts, PIO completion interrupts, DMA interrtups, I2C subIP raw + * interrupts and error interrupts. + * + * This is a help function for interrupt processing, it detects interrupt + * type, clear the interrupt status bit and return the interrupt type to caller + * for future processing. + * + * @dev: The pointer of THC private device context + * + * Return: The combined flag for interrupt type + */ +int thc_interrupt_handler(struct thc_device *dev) +{ + u32 read_sts_1, read_sts_2, read_sts_sw, write_sts; + u32 int_sts, err_cause, seq_cntrl, seq_sts; + int interrupt_type = 0; + + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, &read_sts_1); + + if (read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS) { + dev_dbg(dev->dev, "THC non-DMA device interrupt\n"); + + regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + NONDMA_INT_STS_BIT); + + interrupt_type |= BIT(THC_NONDMA_INT); + + return interrupt_type; + } + + regmap_read(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, &int_sts); + + if (int_sts & THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS) { + dev_err(dev->dev, "THC transaction error, int_sts: 0x%08X\n", int_sts); + thc_print_txn_error_cause(dev); + + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + TXN_ERR_INT_STS_BIT); + + interrupt_type |= BIT(THC_TXN_ERR_INT); + + return interrupt_type; + } + + regmap_read(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, &err_cause); + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, &read_sts_2); + + if (err_cause & THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR || + read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_STALL_STS || + read_sts_2 & THC_M_PRT_READ_DMA_INT_STS_STALL_STS) { + dev_err(dev->dev, "Buffer overrun or RxDMA engine stalled!\n"); + thc_print_txn_error_cause(dev); + + regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_STALL_STS); + regmap_write(dev->thc_regmap, THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, + THC_M_PRT_READ_DMA_INT_STS_STALL_STS); + regmap_write(dev->thc_regmap, THC_M_PRT_ERR_CAUSE_OFFSET, + THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR); + + interrupt_type |= BIT(THC_TXN_ERR_INT); + + return interrupt_type; + } + + if (int_sts & THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS) { + dev_err_once(dev->dev, "THC FATAL error, int_sts: 0x%08X\n", int_sts); + + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + TXN_FATAL_INT_STS_BIT); + + interrupt_type |= BIT(THC_FATAL_ERR_INT); + + return interrupt_type; + } + + regmap_read(dev->thc_regmap, + THC_M_PRT_SW_SEQ_CNTRL_OFFSET, &seq_cntrl); + regmap_read(dev->thc_regmap, + THC_M_PRT_SW_SEQ_STS_OFFSET, &seq_sts); + + if (seq_cntrl & THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE && + seq_sts & THC_M_PRT_SW_SEQ_STS_TSSDONE) { + dev_dbg(dev->dev, "THC_SS_CD_IE and TSSDONE are set\n"); + interrupt_type |= BIT(THC_PIO_DONE_INT); + } + + if (read_sts_1 & THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS) { + dev_dbg(dev->dev, "Got RxDMA1 Read Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_1_OFFSET, read_sts_1); + + interrupt_type |= BIT(THC_RXDMA1_INT); + } + + if (read_sts_2 & THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS) { + dev_dbg(dev->dev, "Got RxDMA2 Read Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_2_OFFSET, read_sts_2); + + interrupt_type |= BIT(THC_RXDMA2_INT); + } + + regmap_read(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET, &read_sts_sw); + + if (read_sts_sw & THC_M_PRT_READ_DMA_INT_STS_DMACPL_STS) { + dev_dbg(dev->dev, "Got SwDMA Read Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET, read_sts_sw); + + dev->swdma_done = true; + wake_up_interruptible(&dev->swdma_complete_wait); + + interrupt_type |= BIT(THC_SWDMA_INT); + } + + regmap_read(dev->thc_regmap, + THC_M_PRT_WRITE_INT_STS_OFFSET, &write_sts); + + if (write_sts & THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS) { + dev_dbg(dev->dev, "Got TxDMA Write complete Interrupt\n"); + + regmap_write(dev->thc_regmap, + THC_M_PRT_WRITE_INT_STS_OFFSET, write_sts); + + dev->write_done = true; + wake_up_interruptible(&dev->write_complete_wait); + + interrupt_type |= BIT(THC_TXDMA_INT); + } + + if (int_sts & THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + if (int_sts & THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS) { + regmap_write(dev->thc_regmap, THC_M_PRT_INT_STATUS_OFFSET, + THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS); + interrupt_type |= BIT(THC_I2CSUBIP_INT); + } + + if (!interrupt_type) + interrupt_type |= BIT(THC_UNKNOWN_INT); + + return interrupt_type; +} +EXPORT_SYMBOL_NS_GPL(thc_interrupt_handler, "INTEL_THC"); + +/** + * thc_port_select - Set THC port type + * + * @dev: The pointer of THC private device context + * @port_type: THC port type to use for current device + * + * Return: 0 on success, other error codes on failed. + */ +int thc_port_select(struct thc_device *dev, enum thc_port_type port_type) +{ + u32 ctrl, mask; + + if (port_type == THC_PORT_TYPE_SPI) { + dev_dbg(dev->dev, "Set THC port type to SPI\n"); + dev->port_type = THC_PORT_TYPE_SPI; + + /* Enable delay of CS assertion and set to default value */ + ctrl = THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN | + FIELD_PREP(THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL, + THC_CSA_CK_DELAY_VAL_DEFAULT); + mask = THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN | + THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_DUTYC_CFG_OFFSET, + mask, ctrl); + } else if (port_type == THC_PORT_TYPE_I2C) { + dev_dbg(dev->dev, "Set THC port type to I2C\n"); + dev->port_type = THC_PORT_TYPE_I2C; + + /* Set THC transition arbitration policy to frame boundary for I2C */ + ctrl = FIELD_PREP(THC_M_PRT_CONTROL_THC_ARB_POLICY, + THC_ARB_POLICY_FRAME_BOUNDARY); + mask = THC_M_PRT_CONTROL_THC_ARB_POLICY; + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, mask, ctrl); + } else { + dev_err(dev->dev, "unsupported THC port type: %d\n", port_type); + return -EINVAL; + } + + ctrl = FIELD_PREP(THC_M_PRT_CONTROL_PORT_TYPE, port_type); + mask = THC_M_PRT_CONTROL_PORT_TYPE; + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, mask, ctrl); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_port_select, "INTEL_THC"); + +#define THC_SPI_FREQUENCY_7M 7812500 +#define THC_SPI_FREQUENCY_15M 15625000 +#define THC_SPI_FREQUENCY_17M 17857100 +#define THC_SPI_FREQUENCY_20M 20833000 +#define THC_SPI_FREQUENCY_25M 25000000 +#define THC_SPI_FREQUENCY_31M 31250000 +#define THC_SPI_FREQUENCY_41M 41666700 + +#define THC_SPI_LOW_FREQUENCY THC_SPI_FREQUENCY_17M + +static u8 thc_get_spi_freq_div_val(struct thc_device *dev, u32 spi_freq_val) +{ + int frequency[] = { + THC_SPI_FREQUENCY_7M, + THC_SPI_FREQUENCY_15M, + THC_SPI_FREQUENCY_17M, + THC_SPI_FREQUENCY_20M, + THC_SPI_FREQUENCY_25M, + THC_SPI_FREQUENCY_31M, + THC_SPI_FREQUENCY_41M, + }; + u8 frequency_div[] = { + THC_SPI_FRQ_DIV_2, + THC_SPI_FRQ_DIV_1, + THC_SPI_FRQ_DIV_7, + THC_SPI_FRQ_DIV_6, + THC_SPI_FRQ_DIV_5, + THC_SPI_FRQ_DIV_4, + THC_SPI_FRQ_DIV_3, + }; + int size = ARRAY_SIZE(frequency); + u32 closest_freq; + u8 freq_div; + int i; + + for (i = size - 1; i >= 0; i--) + if ((int)spi_freq_val - frequency[i] >= 0) + break; + + if (i < 0) { + dev_err_once(dev->dev, "Not supported SPI frequency %d\n", spi_freq_val); + return THC_SPI_FRQ_RESERVED; + } + + closest_freq = frequency[i]; + freq_div = frequency_div[i]; + + dev_dbg(dev->dev, + "Setting SPI frequency: spi_freq_val = %u, Closest freq = %u\n", + spi_freq_val, closest_freq); + + return freq_div; +} + +/** + * thc_spi_read_config - Configure SPI bus read attributes + * + * @dev: The pointer of THC private device context + * @spi_freq_val: SPI read frequecy value + * @io_mode: SPI read IO mode + * @opcode: Read opcode + * @spi_rd_mps: SPI read max packet size + * + * Return: 0 on success, other error codes on failed. + */ +int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_rd_mps) +{ + bool is_low_freq = false; + u32 cfg, mask; + u8 freq_div; + + freq_div = thc_get_spi_freq_div_val(dev, spi_freq_val); + if (freq_div == THC_SPI_FRQ_RESERVED) + return -EINVAL; + + if (spi_freq_val < THC_SPI_LOW_FREQUENCY) + is_low_freq = true; + + cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCRF, freq_div) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TRMODE, io_mode) | + (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_RD_MPS, spi_rd_mps); + mask = THC_M_PRT_SPI_CFG_SPI_TCRF | + THC_M_PRT_SPI_CFG_SPI_TRMODE | + THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN | + THC_M_PRT_SPI_CFG_SPI_RD_MPS; + + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SPI_CFG_OFFSET, mask, cfg); + + if (io_mode == THC_QUAD_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO, opcode); + else if (io_mode == THC_DUAL_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO, opcode); + else + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO, opcode); + + regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, opcode); + regmap_write(dev->thc_regmap, THC_M_PRT_SPI_DMARD_OPCODE_OFFSET, opcode); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_spi_read_config, "INTEL_THC"); + +/** + * thc_spi_write_config - Configure SPI bus write attributes + * + * @dev: The pointer of THC private device context + * @spi_freq_val: SPI write frequecy value + * @io_mode: SPI write IO mode + * @opcode: Write opcode + * @spi_wr_mps: SPI write max packet size + * @perf_limit: Performance limitation in unit of 10us + * + * Return: 0 on success, other error codes on failed. + */ +int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_wr_mps, + u32 perf_limit) +{ + bool is_low_freq = false; + u32 cfg, mask; + u8 freq_div; + + freq_div = thc_get_spi_freq_div_val(dev, spi_freq_val); + if (freq_div == THC_SPI_FRQ_RESERVED) + return -EINVAL; + + if (spi_freq_val < THC_SPI_LOW_FREQUENCY) + is_low_freq = true; + + cfg = FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TCWF, freq_div) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_TWMODE, io_mode) | + (is_low_freq ? THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN : 0) | + FIELD_PREP(THC_M_PRT_SPI_CFG_SPI_WR_MPS, spi_wr_mps); + mask = THC_M_PRT_SPI_CFG_SPI_TCWF | + THC_M_PRT_SPI_CFG_SPI_TWMODE | + THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN | + THC_M_PRT_SPI_CFG_SPI_WR_MPS; + + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_SPI_CFG_OFFSET, mask, cfg); + + if (io_mode == THC_QUAD_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO, opcode); + else if (io_mode == THC_DUAL_IO) + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO, opcode); + else + opcode = FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO, opcode); + + regmap_write(dev->thc_regmap, THC_M_PRT_SPI_WR_OPCODE_OFFSET, opcode); + + dev->perf_limit = perf_limit; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_spi_write_config, "INTEL_THC"); + +/** + * thc_spi_input_output_address_config - Configure SPI input and output addresses + * + * @dev: the pointer of THC private device context + * @input_hdr_addr: input report header address + * @input_bdy_addr: input report body address + * @output_addr: output report address + */ +void thc_spi_input_output_address_config(struct thc_device *dev, u32 input_hdr_addr, + u32 input_bdy_addr, u32 output_addr) +{ + regmap_write(dev->thc_regmap, + THC_M_PRT_DEV_INT_CAUSE_ADDR_OFFSET, input_hdr_addr); + regmap_write(dev->thc_regmap, + THC_M_PRT_RD_BULK_ADDR_1_OFFSET, input_bdy_addr); + regmap_write(dev->thc_regmap, + THC_M_PRT_RD_BULK_ADDR_2_OFFSET, input_bdy_addr); + regmap_write(dev->thc_regmap, + THC_M_PRT_WR_BULK_ADDR_OFFSET, output_addr); +} +EXPORT_SYMBOL_NS_GPL(thc_spi_input_output_address_config, "INTEL_THC"); + +static int thc_i2c_subip_pio_read(struct thc_device *dev, const u32 address, + u32 *size, u32 *buffer) +{ + int ret; + + if (!size || *size == 0 || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %p, buffer %p\n", + size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + ret = prepare_pio(dev, THC_PIO_OP_I2C_SUBSYSTEM_READ, address, *size); + if (ret < 0) + goto end; + + pio_start(dev, 0, NULL); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, buffer, size); + if (ret < 0) + goto end; + +end: + mutex_unlock(&dev->thc_bus_lock); + + if (ret) + dev_err_once(dev->dev, "Read THC I2C SubIP register failed %d, offset %u\n", + ret, address); + + return ret; +} + +static int thc_i2c_subip_pio_write(struct thc_device *dev, const u32 address, + const u32 size, const u32 *buffer) +{ + int ret; + + if (size == 0 || !buffer) { + dev_err(dev->dev, "Invalid input parameters, size %u, buffer %p\n", + size, buffer); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + ret = prepare_pio(dev, THC_PIO_OP_I2C_SUBSYSTEM_WRITE, address, size); + if (ret < 0) + goto end; + + pio_start(dev, size, buffer); + + ret = pio_wait(dev); + if (ret < 0) + goto end; + + ret = pio_complete(dev, NULL, NULL); + if (ret < 0) + goto end; + +end: + mutex_unlock(&dev->thc_bus_lock); + + if (ret) + dev_err_once(dev->dev, "Write THC I2C SubIP register failed %d, offset %u\n", + ret, address); + + return ret; +} + +#define I2C_SUBIP_CON_DEFAULT 0x663 +#define I2C_SUBIP_INT_MASK_DEFAULT 0x7FFF +#define I2C_SUBIP_RX_TL_DEFAULT 62 +#define I2C_SUBIP_TX_TL_DEFAULT 0 +#define I2C_SUBIP_DMA_TDLR_DEFAULT 7 +#define I2C_SUBIP_DMA_RDLR_DEFAULT 7 + +static int thc_i2c_subip_set_speed(struct thc_device *dev, const u32 speed, + const u32 hcnt, const u32 lcnt) +{ + u32 hcnt_offset, lcnt_offset; + u32 val; + int ret; + + switch (speed) { + case THC_I2C_STANDARD: + hcnt_offset = THC_I2C_IC_SS_SCL_HCNT_OFFSET; + lcnt_offset = THC_I2C_IC_SS_SCL_LCNT_OFFSET; + break; + + case THC_I2C_FAST_AND_PLUS: + hcnt_offset = THC_I2C_IC_FS_SCL_HCNT_OFFSET; + lcnt_offset = THC_I2C_IC_FS_SCL_LCNT_OFFSET; + break; + + case THC_I2C_HIGH_SPEED: + hcnt_offset = THC_I2C_IC_HS_SCL_HCNT_OFFSET; + lcnt_offset = THC_I2C_IC_HS_SCL_LCNT_OFFSET; + break; + + default: + dev_err_once(dev->dev, "Unsupported i2c speed %d\n", speed); + ret = -EINVAL; + return ret; + } + + ret = thc_i2c_subip_pio_write(dev, hcnt_offset, sizeof(u32), &hcnt); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_pio_write(dev, lcnt_offset, sizeof(u32), &lcnt); + if (ret < 0) + return ret; + + val = I2C_SUBIP_CON_DEFAULT & ~THC_I2C_IC_CON_SPEED; + val |= FIELD_PREP(THC_I2C_IC_CON_SPEED, speed); + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_CON_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + return 0; +} + +static u32 i2c_subip_regs[] = { + THC_I2C_IC_CON_OFFSET, + THC_I2C_IC_TAR_OFFSET, + THC_I2C_IC_INTR_MASK_OFFSET, + THC_I2C_IC_RX_TL_OFFSET, + THC_I2C_IC_TX_TL_OFFSET, + THC_I2C_IC_DMA_CR_OFFSET, + THC_I2C_IC_DMA_TDLR_OFFSET, + THC_I2C_IC_DMA_RDLR_OFFSET, + THC_I2C_IC_SS_SCL_HCNT_OFFSET, + THC_I2C_IC_SS_SCL_LCNT_OFFSET, + THC_I2C_IC_FS_SCL_HCNT_OFFSET, + THC_I2C_IC_FS_SCL_LCNT_OFFSET, + THC_I2C_IC_HS_SCL_HCNT_OFFSET, + THC_I2C_IC_HS_SCL_LCNT_OFFSET, + THC_I2C_IC_ENABLE_OFFSET, +}; + +/** + * thc_i2c_subip_init - Initialize and configure THC I2C subsystem + * + * @dev: The pointer of THC private device context + * @target_address: Slave address of touch device (TIC) + * @speed: I2C bus frequency speed mode + * @hcnt: I2C clock SCL high count + * @lcnt: I2C clock SCL low count + * + * Return: 0 on success, other error codes on failed. + */ +int thc_i2c_subip_init(struct thc_device *dev, const u32 target_address, + const u32 speed, const u32 hcnt, const u32 lcnt) +{ + u32 read_size = sizeof(u32); + u32 val; + int ret; + + ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_ENABLE_OFFSET, &read_size, &val); + if (ret < 0) + return ret; + + val &= ~THC_I2C_IC_ENABLE_ENABLE; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_ENABLE_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_TAR_OFFSET, &read_size, &val); + if (ret < 0) + return ret; + + val &= ~THC_I2C_IC_TAR_IC_TAR; + val |= FIELD_PREP(THC_I2C_IC_TAR_IC_TAR, target_address); + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_TAR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_set_speed(dev, speed, hcnt, lcnt); + if (ret < 0) + return ret; + + val = I2C_SUBIP_INT_MASK_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_INTR_MASK_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_RX_TL_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_RX_TL_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_TX_TL_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_TX_TL_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = THC_I2C_IC_DMA_CR_RDMAE | THC_I2C_IC_DMA_CR_TDMAE; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_CR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_DMA_TDLR_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_TDLR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + val = I2C_SUBIP_DMA_RDLR_DEFAULT; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_DMA_RDLR_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + ret = thc_i2c_subip_pio_read(dev, THC_I2C_IC_ENABLE_OFFSET, &read_size, &val); + if (ret < 0) + return ret; + + val |= THC_I2C_IC_ENABLE_ENABLE; + ret = thc_i2c_subip_pio_write(dev, THC_I2C_IC_ENABLE_OFFSET, sizeof(u32), &val); + if (ret < 0) + return ret; + + dev->i2c_subip_regs = devm_kzalloc(dev->dev, sizeof(i2c_subip_regs), GFP_KERNEL); + if (!dev->i2c_subip_regs) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_init, "INTEL_THC"); + +/** + * thc_i2c_subip_regs_save - Save THC I2C sub-subsystem register values to THC device context + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_i2c_subip_regs_save(struct thc_device *dev) +{ + int ret; + u32 read_size = sizeof(u32); + + for (int i = 0; i < ARRAY_SIZE(i2c_subip_regs); i++) { + ret = thc_i2c_subip_pio_read(dev, i2c_subip_regs[i], + &read_size, (u32 *)&dev->i2c_subip_regs + i); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_save, "INTEL_THC"); + +/** + * thc_i2c_subip_regs_restore - Restore THC I2C subsystem registers from THC device context + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_i2c_subip_regs_restore(struct thc_device *dev) +{ + int ret; + u32 write_size = sizeof(u32); + + for (int i = 0; i < ARRAY_SIZE(i2c_subip_regs); i++) { + ret = thc_i2c_subip_pio_write(dev, i2c_subip_regs[i], + write_size, (u32 *)&dev->i2c_subip_regs + i); + if (ret < 0) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_i2c_subip_regs_restore, "INTEL_THC"); + +MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>"); +MODULE_AUTHOR("Even Xu <even.xu@intel.com>"); + +MODULE_DESCRIPTION("Intel(R) Intel THC Hardware Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h new file mode 100644 index 000000000000..0517fee2c668 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _INTEL_THC_DEV_H_ +#define _INTEL_THC_DEV_H_ + +#include <linux/cdev.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#include "intel-thc-dma.h" + +#define THC_REGMAP_COMMON_OFFSET 0x10 +#define THC_REGMAP_MMIO_OFFSET 0x1000 + +/* + * THC Port type + * @THC_PORT_TYPE_SPI: This port is used for HIDSPI + * @THC_PORT_TYPE_I2C: This port is used for HIDI2C + */ +enum thc_port_type { + THC_PORT_TYPE_SPI = 0, + THC_PORT_TYPE_I2C = 1, +}; + +/** + * THC interrupt flag + * @THC_NONDMA_INT: THC non-DMA interrupt + * @THC_RXDMA1_INT: THC RxDMA1 interrupt + * @THC_RXDMA2_INT: THC RxDMA2 interrupt + * @THC_SWDMA_INT: THC SWDMA interrupt + * @THC_TXDMA_INT: THC TXDMA interrupt + * @THC_PIO_DONE_INT: THC PIO complete interrupt + * @THC_I2CSUBIP_INT: THC I2C subsystem interrupt + * @THC_TXN_ERR_INT: THC transfer error interrupt + * @THC_FATAL_ERR_INT: THC fatal error interrupt + */ +enum thc_int_type { + THC_NONDMA_INT = 0, + THC_RXDMA1_INT = 1, + THC_RXDMA2_INT = 2, + THC_SWDMA_INT = 3, + THC_TXDMA_INT = 4, + THC_PIO_DONE_INT = 5, + THC_I2CSUBIP_INT = 6, + THC_TXN_ERR_INT = 7, + THC_FATAL_ERR_INT = 8, + THC_UNKNOWN_INT +}; + +/** + * struct thc_device - THC private device struct + * @thc_regmap: MMIO regmap structure for accessing THC registers + * @mmio_addr: MMIO registers address + * @thc_bus_lock: mutex locker for THC config + * @port_type: port type of THC port instance + * @pio_int_supported: PIO interrupt supported flag + * @dma_ctx: DMA specific data + * @write_complete_wait: signal event for DMA write complete + * @swdma_complete_wait: signal event for SWDMA sequence complete + * @write_done: bool value that indicates if DMA write is done + * @swdma_done: bool value that indicates if SWDMA swquence is done + * @perf_limit: the delay between read operation and write operation + * @i2c_subip_regs: the copy of THC I2C sub-system registers for resuming restore + */ +struct thc_device { + struct device *dev; + struct regmap *thc_regmap; + void __iomem *mmio_addr; + struct mutex thc_bus_lock; + enum thc_port_type port_type; + bool pio_int_supported; + + struct thc_dma_context *dma_ctx; + + wait_queue_head_t write_complete_wait; + wait_queue_head_t swdma_complete_wait; + bool write_done; + bool swdma_done; + + u32 perf_limit; + + u32 *i2c_subip_regs; +}; + +struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr); +int thc_tic_pio_read(struct thc_device *dev, const u32 address, + const u32 size, u32 *actual_size, u32 *buffer); +int thc_tic_pio_write(struct thc_device *dev, const u32 address, + const u32 size, const u32 *buffer); +int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address, + const u32 write_size, const u32 *write_buffer, + const u32 read_size, u32 *actual_size, u32 *read_buffer); +void thc_interrupt_config(struct thc_device *dev); +void thc_int_trigger_type_select(struct thc_device *dev, bool edge_trigger); +void thc_interrupt_enable(struct thc_device *dev, bool int_enable); +void thc_set_pio_interrupt_support(struct thc_device *dev, bool supported); +int thc_interrupt_quiesce(const struct thc_device *dev, bool int_quiesce); +void thc_ltr_config(struct thc_device *dev, u32 active_ltr_us, u32 lp_ltr_us); +void thc_change_ltr_mode(struct thc_device *dev, u32 ltr_mode); +void thc_ltr_unconfig(struct thc_device *dev); +u32 thc_int_cause_read(struct thc_device *dev); +int thc_interrupt_handler(struct thc_device *dev); +int thc_port_select(struct thc_device *dev, enum thc_port_type port_type); +int thc_spi_read_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_rd_mps); +int thc_spi_write_config(struct thc_device *dev, u32 spi_freq_val, + u32 io_mode, u32 opcode, u32 spi_wr_mps, u32 perf_limit); +void thc_spi_input_output_address_config(struct thc_device *dev, u32 input_hdr_addr, + u32 input_bdy_addr, u32 output_addr); +int thc_i2c_subip_init(struct thc_device *dev, const u32 target_address, + const u32 speed, const u32 hcnt, const u32 lcnt); +int thc_i2c_subip_regs_save(struct thc_device *dev); +int thc_i2c_subip_regs_restore(struct thc_device *dev); + +#endif /* _INTEL_THC_DEV_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c new file mode 100644 index 000000000000..8f97e71df7f4 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.c @@ -0,0 +1,969 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/overflow.h> +#include <linux/regmap.h> +#include <linux/scatterlist.h> + +#include "intel-thc-dev.h" +#include "intel-thc-dma.h" +#include "intel-thc-hw.h" + +static void dma_set_prd_base_addr(struct thc_device *dev, u64 physical_addr, + struct thc_dma_configuration *dma_config) +{ + u32 addr_high, addr_low; + + if (!dma_config->is_enabled) + return; + + addr_high = upper_32_bits(physical_addr); + addr_low = lower_32_bits(physical_addr); + + regmap_write(dev->thc_regmap, dma_config->prd_base_addr_high, addr_high); + regmap_write(dev->thc_regmap, dma_config->prd_base_addr_low, addr_low); +} + +static void dma_set_start_bit(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, mask, mbits, data, offset; + + if (!dma_config->is_enabled) + return; + + switch (dma_config->dma_channel) { + case THC_RXDMA1: + case THC_RXDMA2: + if (dma_config->dma_channel == THC_RXDMA2) { + mbits = FIELD_PREP(THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL, + THC_BITMASK_INTERRUPT_TYPE_DATA); + mask = THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL; + regmap_write_bits(dev->thc_regmap, + THC_M_PRT_DEVINT_CFG_1_OFFSET, mask, mbits); + } + + mbits = THC_M_PRT_READ_DMA_CNTRL_IE_EOF | + THC_M_PRT_READ_DMA_CNTRL_SOO | + THC_M_PRT_READ_DMA_CNTRL_IE_STALL | + THC_M_PRT_READ_DMA_CNTRL_IE_ERROR | + THC_M_PRT_READ_DMA_CNTRL_START; + + mask = THC_M_PRT_READ_DMA_CNTRL_TPCWP | mbits; + mask |= THC_M_PRT_READ_DMA_CNTRL_INT_SW_DMA_EN; + ctrl = FIELD_PREP(THC_M_PRT_READ_DMA_CNTRL_TPCWP, THC_POINTER_WRAPAROUND) | mbits; + offset = dma_config->dma_channel == THC_RXDMA1 ? + THC_M_PRT_READ_DMA_CNTRL_1_OFFSET : THC_M_PRT_READ_DMA_CNTRL_2_OFFSET; + regmap_write_bits(dev->thc_regmap, offset, mask, ctrl); + break; + + case THC_SWDMA: + mbits = THC_M_PRT_READ_DMA_CNTRL_IE_DMACPL | + THC_M_PRT_READ_DMA_CNTRL_IE_IOC | + THC_M_PRT_READ_DMA_CNTRL_SOO | + THC_M_PRT_READ_DMA_CNTRL_START; + + mask = THC_M_PRT_READ_DMA_CNTRL_TPCWP | mbits; + ctrl = FIELD_PREP(THC_M_PRT_READ_DMA_CNTRL_TPCWP, THC_POINTER_WRAPAROUND) | mbits; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET, + mask, ctrl); + break; + + case THC_TXDMA: + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_INT_STS_OFFSET, + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS, + THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS); + + /* Select interrupt or polling method upon Write completion */ + if (dev->dma_ctx->use_write_interrupts) + data = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL; + else + data = 0; + + data |= THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START; + mask = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL | + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START; + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + mask, data); + break; + + default: + break; + } +} + +static void dma_set_prd_control(struct thc_device *dev, u8 entry_count, u8 cb_depth, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, mask; + + if (!dma_config->is_enabled) + return; + + if (dma_config->dma_channel == THC_TXDMA) { + mask = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC; + ctrl = FIELD_PREP(THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC, entry_count); + } else { + mask = THC_M_PRT_RPRD_CNTRL_PTEC | THC_M_PRT_RPRD_CNTRL_PCD; + ctrl = FIELD_PREP(THC_M_PRT_RPRD_CNTRL_PTEC, entry_count) | + FIELD_PREP(THC_M_PRT_RPRD_CNTRL_PCD, cb_depth); + } + + regmap_write_bits(dev->thc_regmap, dma_config->prd_cntrl, mask, ctrl); +} + +static void dma_clear_prd_control(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 mask; + + if (!dma_config->is_enabled) + return; + + if (dma_config->dma_channel == THC_TXDMA) + mask = THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC; + else + mask = THC_M_PRT_RPRD_CNTRL_PTEC | THC_M_PRT_RPRD_CNTRL_PCD; + + regmap_write_bits(dev->thc_regmap, dma_config->prd_cntrl, mask, 0); +} + +static u8 dma_get_read_pointer(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, read_pointer; + + regmap_read(dev->thc_regmap, dma_config->dma_cntrl, &ctrl); + read_pointer = FIELD_GET(THC_M_PRT_READ_DMA_CNTRL_TPCRP, ctrl); + + dev_dbg(dev->dev, "THC_M_PRT_READ_DMA_CNTRL 0x%x offset 0x%x TPCRP 0x%x\n", + ctrl, dma_config->dma_cntrl, read_pointer); + + return read_pointer; +} + +static u8 dma_get_write_pointer(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, write_pointer; + + regmap_read(dev->thc_regmap, dma_config->dma_cntrl, &ctrl); + write_pointer = FIELD_GET(THC_M_PRT_READ_DMA_CNTRL_TPCWP, ctrl); + + dev_dbg(dev->dev, "THC_M_PRT_READ_DMA_CNTRL 0x%x offset 0x%x TPCWP 0x%x\n", + ctrl, dma_config->dma_cntrl, write_pointer); + + return write_pointer; +} + +static void dma_set_write_pointer(struct thc_device *dev, u8 value, + struct thc_dma_configuration *dma_config) +{ + u32 ctrl, mask; + + mask = THC_M_PRT_READ_DMA_CNTRL_TPCWP; + ctrl = FIELD_PREP(THC_M_PRT_READ_DMA_CNTRL_TPCWP, value); + regmap_write_bits(dev->thc_regmap, dma_config->dma_cntrl, mask, ctrl); +} + +static size_t dma_get_max_packet_size(struct thc_device *dev, + struct thc_dma_configuration *dma_config) +{ + return dma_config->max_packet_size; +} + +static void dma_set_max_packet_size(struct thc_device *dev, size_t size, + struct thc_dma_configuration *dma_config) +{ + if (size) { + dma_config->max_packet_size = ALIGN(size, SZ_4K); + dma_config->is_enabled = true; + } +} + +static void thc_copy_one_sgl_to_prd(struct thc_device *dev, + struct thc_dma_configuration *config, + unsigned int ind) +{ + struct thc_prd_table *prd_tbl; + struct scatterlist *sg; + int j; + + prd_tbl = &config->prd_tbls[ind]; + + for_each_sg(config->sgls[ind], sg, config->sgls_nent[ind], j) { + prd_tbl->entries[j].dest_addr = + sg_dma_address(sg) >> THC_ADDRESS_SHIFT; + prd_tbl->entries[j].len = sg_dma_len(sg); + prd_tbl->entries[j].hw_status = 0; + prd_tbl->entries[j].end_of_prd = 0; + } + + /* Set the end_of_prd flag in the last filled entry */ + if (j > 0) + prd_tbl->entries[j - 1].end_of_prd = 1; +} + +static void thc_copy_sgls_to_prd(struct thc_device *dev, + struct thc_dma_configuration *config) +{ + unsigned int i; + + memset(config->prd_tbls, 0, array_size(PRD_TABLE_SIZE, config->prd_tbl_num)); + + for (i = 0; i < config->prd_tbl_num; i++) + thc_copy_one_sgl_to_prd(dev, config, i); +} + +static int setup_dma_buffers(struct thc_device *dev, + struct thc_dma_configuration *config, + enum dma_data_direction dir) +{ + size_t prd_tbls_size = array_size(PRD_TABLE_SIZE, config->prd_tbl_num); + unsigned int i, nent = PRD_ENTRIES_NUM; + dma_addr_t dma_handle; + void *cpu_addr; + size_t buf_sz; + int count; + + if (!config->is_enabled) + return 0; + + memset(config->sgls, 0, sizeof(config->sgls)); + memset(config->sgls_nent, 0, sizeof(config->sgls_nent)); + + cpu_addr = dma_alloc_coherent(dev->dev, prd_tbls_size, + &dma_handle, GFP_KERNEL); + if (!cpu_addr) + return -ENOMEM; + + config->prd_tbls = cpu_addr; + config->prd_tbls_dma_handle = dma_handle; + + buf_sz = dma_get_max_packet_size(dev, config); + + /* Allocate and map the scatter-gather lists, one for each PRD table */ + for (i = 0; i < config->prd_tbl_num; i++) { + config->sgls[i] = sgl_alloc(buf_sz, GFP_KERNEL, &nent); + if (!config->sgls[i] || nent > PRD_ENTRIES_NUM) { + dev_err_once(dev->dev, "sgl_alloc (%uth) failed, nent %u\n", + i, nent); + return -ENOMEM; + } + count = dma_map_sg(dev->dev, config->sgls[i], nent, dir); + + config->sgls_nent[i] = count; + } + + thc_copy_sgls_to_prd(dev, config); + + return 0; +} + +static void thc_reset_dma_settings(struct thc_device *dev) +{ + /* Stop all DMA channels and reset DMA read pointers */ + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_WRITE_DMA_CNTRL_OFFSET, + THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_TPCPR, + THC_M_PRT_READ_DMA_CNTRL_TPCPR); +} + +static void release_dma_buffers(struct thc_device *dev, + struct thc_dma_configuration *config) +{ + size_t prd_tbls_size = array_size(PRD_TABLE_SIZE, config->prd_tbl_num); + unsigned int i; + + if (!config->is_enabled) + return; + + for (i = 0; i < config->prd_tbl_num; i++) { + if (!config->sgls[i] || !config->sgls_nent[i]) + continue; + + dma_unmap_sg(dev->dev, config->sgls[i], + config->sgls_nent[i], + config->dir); + + sgl_free(config->sgls[i]); + config->sgls[i] = NULL; + } + + memset(config->prd_tbls, 0, prd_tbls_size); + + if (config->prd_tbls) { + dma_free_coherent(dev->dev, prd_tbls_size, config->prd_tbls, + config->prd_tbls_dma_handle); + config->prd_tbls = NULL; + config->prd_tbls_dma_handle = 0; + } +} + +struct thc_dma_context *thc_dma_init(struct thc_device *dev) +{ + struct thc_dma_context *dma_ctx; + + dma_ctx = devm_kzalloc(dev->dev, sizeof(*dma_ctx), GFP_KERNEL); + if (!dma_ctx) + return NULL; + + dev->dma_ctx = dma_ctx; + + dma_ctx->dma_config[THC_RXDMA1].dma_channel = THC_RXDMA1; + dma_ctx->dma_config[THC_RXDMA2].dma_channel = THC_RXDMA2; + dma_ctx->dma_config[THC_TXDMA].dma_channel = THC_TXDMA; + dma_ctx->dma_config[THC_SWDMA].dma_channel = THC_SWDMA; + + dma_ctx->dma_config[THC_RXDMA1].dir = DMA_FROM_DEVICE; + dma_ctx->dma_config[THC_RXDMA2].dir = DMA_FROM_DEVICE; + dma_ctx->dma_config[THC_TXDMA].dir = DMA_TO_DEVICE; + dma_ctx->dma_config[THC_SWDMA].dir = DMA_FROM_DEVICE; + + dma_ctx->dma_config[THC_RXDMA1].prd_tbl_num = PRD_TABLES_NUM; + dma_ctx->dma_config[THC_RXDMA2].prd_tbl_num = PRD_TABLES_NUM; + dma_ctx->dma_config[THC_TXDMA].prd_tbl_num = 1; + dma_ctx->dma_config[THC_SWDMA].prd_tbl_num = 1; + + dma_ctx->dma_config[THC_RXDMA1].prd_base_addr_high = THC_M_PRT_RPRD_BA_HI_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].prd_base_addr_high = THC_M_PRT_RPRD_BA_HI_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].prd_base_addr_high = THC_M_PRT_WPRD_BA_HI_OFFSET; + dma_ctx->dma_config[THC_SWDMA].prd_base_addr_high = THC_M_PRT_RPRD_BA_HI_SW_OFFSET; + + dma_ctx->dma_config[THC_RXDMA1].prd_base_addr_low = THC_M_PRT_RPRD_BA_LOW_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].prd_base_addr_low = THC_M_PRT_RPRD_BA_LOW_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].prd_base_addr_low = THC_M_PRT_WPRD_BA_LOW_OFFSET; + dma_ctx->dma_config[THC_SWDMA].prd_base_addr_low = THC_M_PRT_RPRD_BA_LOW_SW_OFFSET; + + dma_ctx->dma_config[THC_RXDMA1].prd_cntrl = THC_M_PRT_RPRD_CNTRL_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].prd_cntrl = THC_M_PRT_RPRD_CNTRL_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].prd_cntrl = THC_M_PRT_WRITE_DMA_CNTRL_OFFSET; + dma_ctx->dma_config[THC_SWDMA].prd_cntrl = THC_M_PRT_RPRD_CNTRL_SW_OFFSET; + + dma_ctx->dma_config[THC_RXDMA1].dma_cntrl = THC_M_PRT_READ_DMA_CNTRL_1_OFFSET; + dma_ctx->dma_config[THC_RXDMA2].dma_cntrl = THC_M_PRT_READ_DMA_CNTRL_2_OFFSET; + dma_ctx->dma_config[THC_TXDMA].dma_cntrl = THC_M_PRT_WRITE_DMA_CNTRL_OFFSET; + dma_ctx->dma_config[THC_SWDMA].dma_cntrl = THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET; + + /* Enable write DMA completion interrupt by default */ + dma_ctx->use_write_interrupts = 1; + + return dma_ctx; +} + +/** + * thc_dma_set_max_packet_sizes - Set max packet sizes for all DMA engines + * + * @dev: The pointer of THC private device context + * @mps_read1: RxDMA1 max packet size + * @mps_read2: RxDMA2 max packet size + * @mps_write: TxDMA max packet size + * @mps_swdma: Software DMA max packet size + * + * If mps is not 0, it means the corresponding DMA channel is used, then set + * the flag to turn on this channel. + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_set_max_packet_sizes(struct thc_device *dev, size_t mps_read1, + size_t mps_read2, size_t mps_write, + size_t mps_swdma) +{ + if (!dev->dma_ctx) { + dev_err_once(dev->dev, + "Cannot set max packet sizes because DMA context is NULL!\n"); + return -EINVAL; + } + + dma_set_max_packet_size(dev, mps_read1, &dev->dma_ctx->dma_config[THC_RXDMA1]); + dma_set_max_packet_size(dev, mps_read2, &dev->dma_ctx->dma_config[THC_RXDMA2]); + dma_set_max_packet_size(dev, mps_write, &dev->dma_ctx->dma_config[THC_TXDMA]); + dma_set_max_packet_size(dev, mps_swdma, &dev->dma_ctx->dma_config[THC_SWDMA]); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_set_max_packet_sizes, "INTEL_THC"); + +/** + * thc_dma_allocate - Allocate DMA buffers for all DMA engines + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_allocate(struct thc_device *dev) +{ + int ret, chan; + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) { + ret = setup_dma_buffers(dev, &dev->dma_ctx->dma_config[chan], + dev->dma_ctx->dma_config[chan].dir); + if (ret < 0) { + dev_err_once(dev->dev, "DMA setup failed for DMA channel %d\n", chan); + goto release_bufs; + } + } + + return 0; + +release_bufs: + while (chan--) + release_dma_buffers(dev, &dev->dma_ctx->dma_config[chan]); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_allocate, "INTEL_THC"); + +/** + * thc_dma_release - Release DMA buffers for all DMA engines + * + * @dev: The pointer of THC private device context + */ +void thc_dma_release(struct thc_device *dev) +{ + int chan; + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) + release_dma_buffers(dev, &dev->dma_ctx->dma_config[chan]); +} +EXPORT_SYMBOL_NS_GPL(thc_dma_release, "INTEL_THC"); + +static int calc_prd_entries_num(struct thc_prd_table *prd_tbl, + size_t mes_len, u8 *nent) +{ + *nent = DIV_ROUND_UP(mes_len, THC_MIN_BYTES_PER_SG_LIST_ENTRY); + if (*nent > PRD_ENTRIES_NUM) + return -EMSGSIZE; + + return 0; +} + +static size_t calc_message_len(struct thc_prd_table *prd_tbl, u8 *nent) +{ + size_t mes_len = 0; + unsigned int j; + + for (j = 0; j < PRD_ENTRIES_NUM; j++) { + mes_len += prd_tbl->entries[j].len; + if (prd_tbl->entries[j].end_of_prd) + break; + } + + *nent = j + 1; + + return mes_len; +} + +/** + * thc_dma_configure - Configure DMA settings for all DMA engines + * + * @dev: The pointer of THC private device context + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_configure(struct thc_device *dev) +{ + struct thc_dma_context *dma_ctx = dev->dma_ctx; + int chan; + + thc_reset_dma_settings(dev); + + if (!dma_ctx) { + dev_err_once(dev->dev, "Cannot do DMA configure because DMA context is NULL\n"); + return -EINVAL; + } + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) { + dma_set_prd_base_addr(dev, + dma_ctx->dma_config[chan].prd_tbls_dma_handle, + &dma_ctx->dma_config[chan]); + + dma_set_prd_control(dev, PRD_ENTRIES_NUM - 1, + dma_ctx->dma_config[chan].prd_tbl_num - 1, + &dma_ctx->dma_config[chan]); + } + + /* Start read2 DMA engine */ + dma_set_start_bit(dev, &dma_ctx->dma_config[THC_RXDMA2]); + + dev_dbg(dev->dev, "DMA configured successfully!\n"); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_configure, "INTEL_THC"); + +/** + * thc_dma_unconfigure - Unconfigure DMA settings for all DMA engines + * + * @dev: The pointer of THC private device context + */ +void thc_dma_unconfigure(struct thc_device *dev) +{ + int chan; + + for (chan = 0; chan < MAX_THC_DMA_CHANNEL; chan++) { + dma_set_prd_base_addr(dev, 0, &dev->dma_ctx->dma_config[chan]); + dma_clear_prd_control(dev, &dev->dma_ctx->dma_config[chan]); + } + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_1_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); + + regmap_write_bits(dev->thc_regmap, THC_M_PRT_READ_DMA_CNTRL_2_OFFSET, + THC_M_PRT_READ_DMA_CNTRL_START, 0); +} +EXPORT_SYMBOL_NS_GPL(thc_dma_unconfigure, "INTEL_THC"); + +static int thc_wait_for_dma_pause(struct thc_device *dev, enum thc_dma_channel channel) +{ + u32 ctrl_reg, sts_reg, sts; + int ret; + + ctrl_reg = (channel == THC_RXDMA1) ? THC_M_PRT_READ_DMA_CNTRL_1_OFFSET : + ((channel == THC_RXDMA2) ? THC_M_PRT_READ_DMA_CNTRL_2_OFFSET : + THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET); + + regmap_write_bits(dev->thc_regmap, ctrl_reg, THC_M_PRT_READ_DMA_CNTRL_START, 0); + + sts_reg = (channel == THC_RXDMA1) ? THC_M_PRT_READ_DMA_INT_STS_1_OFFSET : + ((channel == THC_RXDMA2) ? THC_M_PRT_READ_DMA_INT_STS_2_OFFSET : + THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET); + + ret = regmap_read_poll_timeout(dev->thc_regmap, sts_reg, sts, + !(sts & THC_M_PRT_READ_DMA_INT_STS_ACTIVE), + THC_DEFAULT_RXDMA_POLLING_US_INTERVAL, + THC_DEFAULT_RXDMA_POLLING_US_TIMEOUT); + + if (ret) { + dev_err_once(dev->dev, + "Timeout while waiting for DMA %d stop\n", channel); + return ret; + } + + return 0; +} + +static int read_dma_buffer(struct thc_device *dev, + struct thc_dma_configuration *read_config, + u8 prd_table_index, void *read_buff) +{ + struct thc_prd_table *prd_tbl; + struct scatterlist *sg; + size_t mes_len, ret; + u8 nent; + + if (prd_table_index >= read_config->prd_tbl_num) { + dev_err_once(dev->dev, "PRD table index %d too big\n", prd_table_index); + return -EINVAL; + } + + prd_tbl = &read_config->prd_tbls[prd_table_index]; + mes_len = calc_message_len(prd_tbl, &nent); + if (mes_len > read_config->max_packet_size) { + dev_err(dev->dev, + "Message length %zu is bigger than buffer length %lu\n", + mes_len, read_config->max_packet_size); + return -EMSGSIZE; + } + + sg = read_config->sgls[prd_table_index]; + ret = sg_copy_to_buffer(sg, nent, read_buff, mes_len); + if (ret != mes_len) { + dev_err_once(dev->dev, "Copied %zu bytes instead of requested %zu\n", + ret, mes_len); + return -EIO; + } + + return mes_len; +} + +static void update_write_pointer(struct thc_device *dev, + struct thc_dma_configuration *read_config) +{ + u8 write_ptr = dma_get_write_pointer(dev, read_config); + + if (write_ptr + 1 == THC_WRAPAROUND_VALUE_ODD) + dma_set_write_pointer(dev, THC_POINTER_WRAPAROUND, read_config); + else if (write_ptr + 1 == THC_WRAPAROUND_VALUE_EVEN) + dma_set_write_pointer(dev, 0, read_config); + else + dma_set_write_pointer(dev, write_ptr + 1, read_config); +} + +static int is_dma_buf_empty(struct thc_device *dev, + struct thc_dma_configuration *read_config, + u8 *read_ptr, u8 *write_ptr) +{ + *read_ptr = dma_get_read_pointer(dev, read_config); + *write_ptr = dma_get_write_pointer(dev, read_config); + + if ((*read_ptr & THC_POINTER_MASK) == (*write_ptr & THC_POINTER_MASK)) + if (*read_ptr != *write_ptr) + return true; + + return false; +} + +static int thc_dma_read(struct thc_device *dev, + struct thc_dma_configuration *read_config, + void *read_buff, size_t *read_len, int *read_finished) +{ + u8 read_ptr, write_ptr, prd_table_index; + int status; + + if (!is_dma_buf_empty(dev, read_config, &read_ptr, &write_ptr)) { + prd_table_index = write_ptr & THC_POINTER_MASK; + + status = read_dma_buffer(dev, read_config, prd_table_index, read_buff); + if (status <= 0) { + dev_err_once(dev->dev, "read DMA buffer failed %d\n", status); + return -EIO; + } + + *read_len = status; + + /* Clear the relevant PRD table */ + thc_copy_one_sgl_to_prd(dev, read_config, prd_table_index); + + /* Increment the write pointer to let the HW know we have processed this PRD */ + update_write_pointer(dev, read_config); + } + + /* + * This function only reads one frame from PRD table for each call, so we need to + * check if all DMAed data is read out and return the flag to the caller. Caller + * should repeatedly call thc_dma_read() until all DMAed data is handled. + */ + if (read_finished) + *read_finished = is_dma_buf_empty(dev, read_config, &read_ptr, &write_ptr) ? 1 : 0; + + return 0; +} + +/** + * thc_rxdma_read - Read data from RXDMA buffer + * + * @dev: The pointer of THC private device context + * @dma_channel: The RXDMA engine of read data source + * @read_buff: The pointer of the read data buffer + * @read_len: The pointer of the read data length + * @read_finished: The pointer of the flag indicating if all pending data has been read out + * + * Return: 0 on success, other error codes on failed. + */ +int thc_rxdma_read(struct thc_device *dev, enum thc_dma_channel dma_channel, + void *read_buff, size_t *read_len, int *read_finished) +{ + struct thc_dma_configuration *dma_config; + int ret; + + dma_config = &dev->dma_ctx->dma_config[dma_channel]; + + if (!dma_config->is_enabled) { + dev_err_once(dev->dev, "The DMA channel %d is not enabled", dma_channel); + return -EINVAL; + } + + if (!read_buff || !read_len) { + dev_err(dev->dev, "Invalid input parameters, read_buff %p, read_len %p\n", + read_buff, read_len); + return -EINVAL; + } + + if (dma_channel >= THC_TXDMA) { + dev_err(dev->dev, "Unsupported DMA channel for RxDMA read, %d\n", dma_channel); + return -EINVAL; + } + + ret = thc_dma_read(dev, dma_config, read_buff, read_len, read_finished); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_rxdma_read, "INTEL_THC"); + +static int thc_swdma_read_start(struct thc_device *dev, void *write_buff, + size_t write_len, u32 *prd_tbl_len) +{ + u32 mask, val, data0 = 0, data1 = 0; + int ret; + + ret = thc_interrupt_quiesce(dev, true); + if (ret) + return ret; + + if (thc_wait_for_dma_pause(dev, THC_RXDMA1) || thc_wait_for_dma_pause(dev, THC_RXDMA2)) + return -EIO; + + thc_reset_dma_settings(dev); + + mask = THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC | + THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN; + val = FIELD_PREP(THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC, write_len) | + ((!prd_tbl_len) ? THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN : 0); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_RPRD_CNTRL_SW_OFFSET, + mask, val); + + if (prd_tbl_len) { + mask = THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN; + val = FIELD_PREP(THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN, + *prd_tbl_len); + regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_DMA_PRD_TABLE_LEN_OFFSET, + mask, val); + } + + if (write_len <= sizeof(u32)) { + for (int i = 0; i < write_len; i++) + data0 |= *(((u8 *)write_buff) + i) << (i * 8); + + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, data0); + } else if (write_len <= 2 * sizeof(u32)) { + data0 = *(u32 *)write_buff; + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, data0); + + for (int i = 0; i < write_len - sizeof(u32); i++) + data1 |= *(((u8 *)write_buff) + sizeof(u32) + i) << (i * 8); + + regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET, data1); + } + dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_SWDMA]); + + return 0; +} + +static int thc_swdma_read_completion(struct thc_device *dev) +{ + int ret; + + ret = thc_wait_for_dma_pause(dev, THC_SWDMA); + if (ret) + return ret; + + thc_reset_dma_settings(dev); + + dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_RXDMA2]); + + ret = thc_interrupt_quiesce(dev, false); + + return ret; +} + +/** + * thc_swdma_read - Use software DMA to read data from touch device + * + * @dev: The pointer of THC private device context + * @write_buff: The pointer of write buffer for SWDMA sequence + * @write_len: The write data length for SWDMA sequence + * @prd_tbl_len: The prd table length of SWDMA engine, can be set to NULL + * @read_buff: The pointer of the read data buffer + * @read_len: The pointer of the read data length + * + * Return: 0 on success, other error codes on failed. + */ +int thc_swdma_read(struct thc_device *dev, void *write_buff, size_t write_len, + u32 *prd_tbl_len, void *read_buff, size_t *read_len) +{ + int ret; + + if (!(&dev->dma_ctx->dma_config[THC_SWDMA])->is_enabled) { + dev_err_once(dev->dev, "The SWDMA channel is not enabled"); + return -EINVAL; + } + + if (!read_buff || !read_len) { + dev_err(dev->dev, "Invalid input parameters, read_buff %p, read_len %p\n", + read_buff, read_len); + return -EINVAL; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + dev->swdma_done = false; + + ret = thc_swdma_read_start(dev, write_buff, write_len, prd_tbl_len); + if (ret) + goto end; + + ret = wait_event_interruptible_timeout(dev->swdma_complete_wait, dev->swdma_done, 1 * HZ); + if (ret <= 0 || !dev->swdma_done) { + dev_err_once(dev->dev, "timeout for waiting SWDMA completion\n"); + ret = -ETIMEDOUT; + goto end; + } + + ret = thc_dma_read(dev, &dev->dma_ctx->dma_config[THC_SWDMA], read_buff, read_len, NULL); + if (ret) + goto end; + + ret = thc_swdma_read_completion(dev); + +end: + mutex_unlock(&dev->thc_bus_lock); + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_swdma_read, "INTEL_THC"); + +static int write_dma_buffer(struct thc_device *dev, + void *buffer, size_t buf_len) +{ + struct thc_dma_configuration *write_config = &dev->dma_ctx->dma_config[THC_TXDMA]; + struct thc_prd_table *prd_tbl; + struct scatterlist *sg; + unsigned long len_left; + size_t ret; + u8 nent; + int i; + + /* There is only one PRD table for write */ + prd_tbl = &write_config->prd_tbls[0]; + + if (calc_prd_entries_num(prd_tbl, buf_len, &nent) < 0) { + dev_err(dev->dev, "Tx message length too big (%zu)\n", buf_len); + return -EOVERFLOW; + } + + sg = write_config->sgls[0]; + ret = sg_copy_from_buffer(sg, nent, buffer, buf_len); + if (ret != buf_len) { + dev_err_once(dev->dev, "Copied %zu bytes instead of requested %zu\n", + ret, buf_len); + return -EIO; + } + + prd_tbl = &write_config->prd_tbls[0]; + len_left = buf_len; + + for_each_sg(write_config->sgls[0], sg, write_config->sgls_nent[0], i) { + if (sg_dma_address(sg) == 0 || sg_dma_len(sg) == 0) { + dev_err_once(dev->dev, "SGList: zero address or length\n"); + return -EINVAL; + } + + prd_tbl->entries[i].dest_addr = + sg_dma_address(sg) >> THC_ADDRESS_SHIFT; + + if (len_left < sg_dma_len(sg)) { + prd_tbl->entries[i].len = len_left; + prd_tbl->entries[i].end_of_prd = 1; + break; + } + + prd_tbl->entries[i].len = sg_dma_len(sg); + prd_tbl->entries[i].end_of_prd = 0; + + len_left -= sg_dma_len(sg); + } + + dma_set_prd_control(dev, i, 0, write_config); + + return 0; +} + +static void thc_ensure_performance_limitations(struct thc_device *dev) +{ + unsigned long delay_usec = 0; + /* + * Minimum amount of delay the THC / QUICKSPI driver must wait + * between end of write operation and begin of read operation. + * This value shall be in 10us multiples. + */ + if (dev->perf_limit > 0) { + delay_usec = dev->perf_limit * 10; + udelay(delay_usec); + } +} + +static void thc_dma_write_completion(struct thc_device *dev) +{ + thc_ensure_performance_limitations(dev); +} + +/** + * thc_dma_write - Use TXDMA to write data to touch device + * + * @dev: The pointer of THC private device context + * @buffer: The pointer of write data buffer + * @buf_len: The write data length + * + * Return: 0 on success, other error codes on failed. + */ +int thc_dma_write(struct thc_device *dev, void *buffer, size_t buf_len) +{ + bool restore_interrupts = false; + u32 sts, ctrl; + int ret; + + if (!(&dev->dma_ctx->dma_config[THC_TXDMA])->is_enabled) { + dev_err_once(dev->dev, "The TxDMA channel is not enabled\n"); + return -EINVAL; + } + + if (!buffer || buf_len <= 0) { + dev_err(dev->dev, "Invalid input parameters, buffer %p\n, buf_len %zu\n", + buffer, buf_len); + return -EINVAL; + } + + regmap_read(dev->thc_regmap, THC_M_PRT_WRITE_INT_STS_OFFSET, &sts); + if (sts & THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ACTIVE) { + dev_err_once(dev->dev, "THC TxDMA is till active and can't start again\n"); + return -EBUSY; + } + + if (mutex_lock_interruptible(&dev->thc_bus_lock)) + return -EINTR; + + regmap_read(dev->thc_regmap, THC_M_PRT_CONTROL_OFFSET, &ctrl); + + ret = write_dma_buffer(dev, buffer, buf_len); + if (ret) + goto end; + + if (dev->perf_limit && !(ctrl & THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS)) { + ret = thc_interrupt_quiesce(dev, true); + if (ret) + goto end; + + restore_interrupts = true; + } + + dev->write_done = false; + + dma_set_start_bit(dev, &dev->dma_ctx->dma_config[THC_TXDMA]); + + ret = wait_event_interruptible_timeout(dev->write_complete_wait, dev->write_done, 1 * HZ); + if (ret <= 0 || !dev->write_done) { + dev_err_once(dev->dev, "timeout for waiting TxDMA completion\n"); + ret = -ETIMEDOUT; + goto end; + } + + thc_dma_write_completion(dev); + mutex_unlock(&dev->thc_bus_lock); + return 0; + +end: + mutex_unlock(&dev->thc_bus_lock); + + if (restore_interrupts) + ret = thc_interrupt_quiesce(dev, false); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(thc_dma_write, "INTEL_THC"); diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h new file mode 100644 index 000000000000..ca923ff2bef9 --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-dma.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _INTEL_THC_DMA_H_ +#define _INTEL_THC_DMA_H_ + +#include <linux/bits.h> +#include <linux/dma-mapping.h> +#include <linux/sizes.h> +#include <linux/time64.h> +#include <linux/types.h> + +#define THC_POINTER_MASK GENMASK(6, 0) +#define THC_POINTER_WRAPAROUND 0x80 +#define THC_WRAPAROUND_VALUE_ODD 0x10 +#define THC_WRAPAROUND_VALUE_EVEN 0x90 +#define THC_MIN_BYTES_PER_SG_LIST_ENTRY SZ_4K + +#define THC_DEFAULT_RXDMA_POLLING_US_INTERVAL 100 +#define THC_DEFAULT_RXDMA_POLLING_US_TIMEOUT (10 * USEC_PER_MSEC) + +/* + * THC needs 1KB aligned address, dest_addr is 54 bits, not 64, + * so don't need to send the lower 10-bits of address. + */ +#define THC_ADDRESS_SHIFT 10 + +/** + * THC DMA channels: + * @THC_RXDMA1: legacy channel, reserved for raw data reading + * @THC_RXDMA2: DMA to read HID data from touch device + * @THC_TXDMA: DMA to write to touch device + * @THC_SWDMA: SW triggered DMA to write and read from touch device + */ +enum thc_dma_channel { + THC_RXDMA1 = 0, + THC_RXDMA2 = 1, + THC_TXDMA = 2, + THC_SWDMA = 3, + MAX_THC_DMA_CHANNEL +}; + +/** + * THC DMA Physical Memory Descriptor (PRD) + * @dest_addr: bit[53:0], destination address in system memory + * @int_on_completion: bit[63], if set, thc will trigger interrupt to driver + * @len: bit[87:64], length of this entry + * @end_of_prd: bit[88], if set, this entry is last one of current PRD table + * @hw_status: bit[90:89], hw status bits + */ +struct thc_prd_entry { + u64 dest_addr : 54; + u64 reserved1 : 9; + u64 int_on_completion : 1; + u64 len : 24; + u64 end_of_prd : 1; + u64 hw_status : 2; + u64 reserved2 : 37; +}; + +/* + * Max OS memory fragmentation will be at a 4KB boundary, thus to address 1MB + * of virtually contiguous memory 256 PRD entries are required for a single + * PRD Table. SW writes the number of PRD Entries for each PRD table in the + * THC_M_PRT_RPRD_CNTRL.PTEC register field. The PRD entry's length must be + * multiple of 4KB except for the last entry in a PRD table. + * This is the max possible number of etries supported by HW, in practise we + * there will be less entries in each prd table(the actual number will be + * given by scatter-gather list allocation). + */ +#define PRD_ENTRIES_NUM 16 + +/* + * Number of PRD tables equals to number of data buffers. + * The max number of PRD tables supported by the HW is 128, + * but we allocate only 16. + */ +#define PRD_TABLES_NUM 16 + +/* THC DMA Physical Memory Descriptor Table */ +struct thc_prd_table { + struct thc_prd_entry entries[PRD_ENTRIES_NUM]; +}; + +#define PRD_TABLE_SIZE sizeof(struct thc_prd_table) + +/** + * struct thc_dma_configuration - THC DMA configure + * @dma_channel: DMA channel for current DMA configuration + * @prd_tbls_dma_handle: DMA buffer handle + * @dir: direction of DMA for this config + * @prd_tbls: PRD tables for current DMA + * @sgls: array of pointers to scatter-gather lists + * @sgls_nent: actual number of entries per sg list + * @prd_tbl_num: actual number of PRD tables + * @max_packet_size: size of the buffer needed for 1 DMA message (1 PRD table) + * @prd_base_addr_high: High 32bits memory address where stores PRD table + * @prd_base_addr_low: low 32bits memory address where stores PRD table + * @prd_cntrl: PRD control register value + * @dma_cntrl: DMA control register value + */ +struct thc_dma_configuration { + enum thc_dma_channel dma_channel; + dma_addr_t prd_tbls_dma_handle; + enum dma_data_direction dir; + bool is_enabled; + + struct thc_prd_table *prd_tbls; + struct scatterlist *sgls[PRD_TABLES_NUM]; + u8 sgls_nent[PRD_TABLES_NUM]; + u8 prd_tbl_num; + + size_t max_packet_size; + u32 prd_base_addr_high; + u32 prd_base_addr_low; + u32 prd_cntrl; + u32 dma_cntrl; +}; + +/* + * THC DMA context + * Store all THC Channel configures + */ +struct thc_dma_context { + struct thc_dma_configuration dma_config[MAX_THC_DMA_CHANNEL]; + u8 use_write_interrupts; +}; + +struct thc_device; + +int thc_dma_set_max_packet_sizes(struct thc_device *dev, + size_t mps_read1, size_t mps_read2, + size_t mps_write, size_t mps_swdma); +int thc_dma_allocate(struct thc_device *dev); +int thc_dma_configure(struct thc_device *dev); +void thc_dma_unconfigure(struct thc_device *dev); +void thc_dma_release(struct thc_device *dev); +int thc_rxdma_read(struct thc_device *dev, enum thc_dma_channel dma_channel, + void *read_buff, size_t *read_len, int *read_finished); +int thc_swdma_read(struct thc_device *dev, void *write_buff, size_t write_len, + u32 *prd_tbl_len, void *read_buff, size_t *read_len); +int thc_dma_write(struct thc_device *dev, void *buffer, size_t buf_len); + +struct thc_dma_context *thc_dma_init(struct thc_device *dev); + +#endif /* _INTEL_THC_DMA_H_ */ diff --git a/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h new file mode 100644 index 000000000000..6729c4c25dab --- /dev/null +++ b/drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h @@ -0,0 +1,881 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2024 Intel Corporation */ + +#ifndef _INTEL_THC_HW_H_ +#define _INTEL_THC_HW_H_ + +#include <linux/bits.h> + +/* THC registers offset */ +/* Touch Host Controller Control Register */ +#define THC_M_PRT_CONTROL_OFFSET 0x1008 +/* THC SPI Bus Configuration Register */ +#define THC_M_PRT_SPI_CFG_OFFSET 0x1010 +/* THC SPI Bus Read Opcode Register */ +#define THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET 0x1014 +/* THC SPI Bus Read Opcode Register */ +#define THC_M_PRT_SPI_DMARD_OPCODE_OFFSET 0x1018 +/* THC SPI Bus Write Opcode Register */ +#define THC_M_PRT_SPI_WR_OPCODE_OFFSET 0x101C +/* THC Interrupt Enable Register */ +#define THC_M_PRT_INT_EN_OFFSET 0x1020 +/* THC Interrupt Status Register */ +#define THC_M_PRT_INT_STATUS_OFFSET 0x1024 +/* THC Error Cause Register */ +#define THC_M_PRT_ERR_CAUSE_OFFSET 0x1028 +/* THC SW sequencing Control */ +#define THC_M_PRT_SW_SEQ_CNTRL_OFFSET 0x1040 +/* THC SW sequencing Status */ +#define THC_M_PRT_SW_SEQ_STS_OFFSET 0x1044 +/* THC SW Sequencing Data DW0 or SPI Address Register */ +#define THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET 0x1048 +/* THC SW sequencing Data DW1 */ +#define THC_M_PRT_SW_SEQ_DATA1_OFFSET 0x104C +/* THC SW sequencing Data DW2 */ +#define THC_M_PRT_SW_SEQ_DATA2_OFFSET 0x1050 +/* THC SW sequencing Data DW3 */ +#define THC_M_PRT_SW_SEQ_DATA3_OFFSET 0x1054 +/* THC SW sequencing Data DW4 */ +#define THC_M_PRT_SW_SEQ_DATA4_OFFSET 0x1058 +/* THC SW sequencing Data DW5 */ +#define THC_M_PRT_SW_SEQ_DATA5_OFFSET 0x105C +/* THC SW sequencing Data DW6 */ +#define THC_M_PRT_SW_SEQ_DATA6_OFFSET 0x1060 +/* THC SW sequencing Data DW7 */ +#define THC_M_PRT_SW_SEQ_DATA7_OFFSET 0x1064 +/* THC SW sequencing Data DW8 */ +#define THC_M_PRT_SW_SEQ_DATA8_OFFSET 0x1068 +/* THC SW sequencing Data DW9 */ +#define THC_M_PRT_SW_SEQ_DATA9_OFFSET 0x106C +/* THC SW sequencing Data DW10 */ +#define THC_M_PRT_SW_SEQ_DATA10_OFFSET 0x1070 +/* THC SW sequencing Data DW11 */ +#define THC_M_PRT_SW_SEQ_DATA11_OFFSET 0x1074 +/* THC SW sequencing Data DW12 */ +#define THC_M_PRT_SW_SEQ_DATA12_OFFSET 0x1078 +/* THC SW sequencing Data DW13 */ +#define THC_M_PRT_SW_SEQ_DATA13_OFFSET 0x107C +/* THC SW sequencing Data DW14 */ +#define THC_M_PRT_SW_SEQ_DATA14_OFFSET 0x1080 +/* THC SW sequencing Data DW15 */ +#define THC_M_PRT_SW_SEQ_DATA15_OFFSET 0x1084 +/* THC SW sequencing Data DW16 */ +#define THC_M_PRT_SW_SEQ_DATA16_OFFSET 0x1088 +/* THC Write PRD Base Address Register Low */ +#define THC_M_PRT_WPRD_BA_LOW_OFFSET 0x1090 +/* THC Write PRD Base Address Register High */ +#define THC_M_PRT_WPRD_BA_HI_OFFSET 0x1094 +/* THC Write DMA Control */ +#define THC_M_PRT_WRITE_DMA_CNTRL_OFFSET 0x1098 +/* THC Write Interrupt Status */ +#define THC_M_PRT_WRITE_INT_STS_OFFSET 0x109C +/* THC Write DMA Error Register */ +#define THC_M_PRT_WRITE_DMA_ERR_OFFSET 0x10A0 +/* THC device address for the bulk write */ +#define THC_M_PRT_WR_BULK_ADDR_OFFSET 0x10B4 +/* THC Device Interrupt Cause Register Address */ +#define THC_M_PRT_DEV_INT_CAUSE_ADDR_OFFSET 0x10B8 +/* THC Device Interrupt Cause Register Value */ +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_OFFSET 0x10BC +/* THC TXDMA Frame Count */ +#define THC_M_PRT_TX_FRM_CNT_OFFSET 0x10E0 +/* THC TXDMA Packet Count */ +#define THC_M_PRT_TXDMA_PKT_CNT_OFFSET 0x10E4 +/* THC Device Interrupt Count on this port */ +#define THC_M_PRT_DEVINT_CNT_OFFSET 0x10E8 +/* Touch Device Interrupt Cause register Format Configuration Register 1 */ +#define THC_M_PRT_DEVINT_CFG_1_OFFSET 0x10EC +/* Touch Device Interrupt Cause register Format Configuration Register 2 */ +#define THC_M_PRT_DEVINT_CFG_2_OFFSET 0x10F0 +/* THC Read PRD Base Address Low for the 1st RXDMA */ +#define THC_M_PRT_RPRD_BA_LOW_1_OFFSET 0x1100 +/* THC Read PRD Base Address High for the 1st RXDMA */ +#define THC_M_PRT_RPRD_BA_HI_1_OFFSET 0x1104 +/* THC Read PRD Control for the 1st RXDMA */ +#define THC_M_PRT_RPRD_CNTRL_1_OFFSET 0x1108 +/* THC Read DMA Control for the 1st RXDMA */ +#define THC_M_PRT_READ_DMA_CNTRL_1_OFFSET 0x110C +/* THC Read Interrupt Status for the 1st RXDMA */ +#define THC_M_PRT_READ_DMA_INT_STS_1_OFFSET 0x1110 +/* THC Read DMA Error Register for the 1st RXDMA */ +#define THC_M_PRT_READ_DMA_ERR_1_OFFSET 0x1114 +/* Touch Sequencer GuC Tail Offset Address Low for the 1st RXDMA */ +#define THC_M_PRT_GUC_OFFSET_LOW_1_OFFSET 0x1118 +/* Touch Sequencer GuC Tail Offset Address High for the 1st RXDMA */ +#define THC_M_PRT_GUC_OFFSET_HI_1_OFFSET 0x111C +/* Touch Host Controller GuC Work Queue Item Size for the 1st RXDMA */ +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_1_OFFSET 0x1120 +/* Touch Host Controller GuC Control register for the 1st RXDMA */ +#define THC_M_PRT_GUC_WORKQ_SZ_1_OFFSET 0x1124 +/* Touch Sequencer Control for the 1st DMA */ +#define THC_M_PRT_TSEQ_CNTRL_1_OFFSET 0x1128 +/* Touch Sequencer GuC Doorbell Address Low for the 1st RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_LOW_1_OFFSET 0x1130 +/* Touch Sequencer GuC Doorbell Address High for the 1st RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_HI_1_OFFSET 0x1134 +/* Touch Sequencer GuC Doorbell Data */ +#define THC_M_PRT_GUC_DB_DATA_1_OFFSET 0x1138 +/* Touch Sequencer GuC Tail Offset Initial Value for the 1st RXDMA */ +#define THC_M_PRT_GUC_OFFSET_INITVAL_1_OFFSET 0x1140 +/* THC Device Address for the bulk/touch data read for the 1st RXDMA */ +#define THC_M_PRT_RD_BULK_ADDR_1_OFFSET 0x1170 +/* THC Gfx/SW Doorbell Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_DB_CNT_1_OFFSET 0x11A0 +/* THC Frame Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_FRM_CNT_1_OFFSET 0x11A4 +/* THC Micro Frame Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_UFRM_CNT_1_OFFSET 0x11A8 +/* THC Packet Count from the 1st Stream RXDMA on this port */ +#define THC_M_PRT_RXDMA_PKT_CNT_1_OFFSET 0x11AC +/* + * THC Software Interrupt Count from the 1st Stream RXDMA + * on this port + */ +#define THC_M_PRT_SWINT_CNT_1_OFFSET 0x11B0 +/* Touch Sequencer Frame Drop Counter for the 1st RXDMA */ +#define THC_M_PRT_FRAME_DROP_CNT_1_OFFSET 0x11B4 +/* THC Coaescing 1 */ +#define THC_M_PRT_COALESCE_1_OFFSET 0x11B8 +/* THC Read PRD Base Address Low for the 2nd RXDMA */ +#define THC_M_PRT_RPRD_BA_LOW_2_OFFSET 0x1200 +/* THC Read PRD Base Address High for the 2nd RXDMA */ +#define THC_M_PRT_RPRD_BA_HI_2_OFFSET 0x1204 +/* THC Read PRD Control for the 2nd RXDMA */ +#define THC_M_PRT_RPRD_CNTRL_2_OFFSET 0x1208 +/* THC Read DMA Control for the 2nd RXDMA */ +#define THC_M_PRT_READ_DMA_CNTRL_2_OFFSET 0x120C +/* THC Read Interrupt Status for the 2nd RXDMA */ +#define THC_M_PRT_READ_DMA_INT_STS_2_OFFSET 0x1210 +/* THC Read DMA Error Register for the 2nd RXDMA */ +#define THC_M_PRT_READ_DMA_ERR_2_OFFSET 0x1214 +/* Touch Sequencer GuC Tail Offset Address Low for the 2nd RXDMA */ +#define THC_M_PRT_GUC_OFFSET_LOW_2_OFFSET 0x1218 +/* Touch Sequencer GuC Tail Offset Address High for the 2nd RXDMA */ +#define THC_M_PRT_GUC_OFFSET_HI_2_OFFSET 0x121C +/* Touch Host Controller GuC Work Queue Item Size for the 2nd RXDMA */ +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_2_OFFSET 0x1220 +/* Touch Host Controller GuC Control register for the 2nd RXDMA */ +#define THC_M_PRT_GUC_WORKQ_SZ_2_OFFSET 0x1224 +/* Touch Sequencer Control for the 2nd DMA */ +#define THC_M_PRT_TSEQ_CNTRL_2_OFFSET 0x1228 +/* Touch Sequencer GuC Doorbell Address Low for the 2nd RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_LOW_2_OFFSET 0x1230 +/* Touch Sequencer GuC Doorbell Address High for the 2nd RXDMA */ +#define THC_M_PRT_GUC_DB_ADDR_HI_2_OFFSET 0x1234 +/* Touch Sequencer GuC Doorbell Data for PRD2 */ +#define THC_M_PRT_GUC_DB_DATA_2_OFFSET 0x1238 +/* Touch Sequencer GuC Tail Offset Initial Value for the 2nd RXDMA */ +#define THC_M_PRT_GUC_OFFSET_INITVAL_2_OFFSET 0x1240 +/* THC Device Address for the bulk/touch data read for the 2nd RXDMA */ +#define THC_M_PRT_RD_BULK_ADDR_2_OFFSET 0x1270 +/* THC Gfx/SW Doorbell Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_DB_CNT_2_OFFSET 0x12A0 +/* THC Frame Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_FRM_CNT_2_OFFSET 0x12A4 +/* THC Micro Frame Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_UFRM_CNT_2_OFFSET 0x12A8 +/* THC Packet Count from the 2nd Stream RXDMA on this port */ +#define THC_M_PRT_RXDMA_PKT_CNT_2_OFFSET 0x12AC +/* + * THC Software Interrupt Count from the 2nd Stream RXDMA + * on this port + */ +#define THC_M_PRT_SWINT_CNT_2_OFFSET 0x12B0 +/* Touch Sequencer Frame Drop Counter for the 2nd RXDMA */ +#define THC_M_PRT_FRAME_DROP_CNT_2_OFFSET 0x12B4 +/* THC Coaescing 2 */ +#define THC_M_PRT_COALESCE_2_OFFSET 0x12B8 +/* THC SPARE REGISTER */ +#define THC_M_PRT_SPARE_REG_OFFSET 0x12BC +/* THC Read PRD Base Address Low for the SW RXDMA */ +#define THC_M_PRT_RPRD_BA_LOW_SW_OFFSET 0x12C0 +/* THC Read PRD Base Address High for the SW RXDMA */ +#define THC_M_PRT_RPRD_BA_HI_SW_OFFSET 0x12C4 +/* THC Read PRD Control for the SW RXDMA */ +#define THC_M_PRT_RPRD_CNTRL_SW_OFFSET 0x12C8 +/* THC Read DMA Control for the SW RXDMA */ +#define THC_M_PRT_READ_DMA_CNTRL_SW_OFFSET 0x12CC +/* THC Read Interrupt Status for the SW RXDMA */ +#define THC_M_PRT_READ_DMA_INT_STS_SW_OFFSET 0x12D0 +/* Touch Sequencer Control for the SW DMA */ +#define THC_M_PRT_TSEQ_CNTRL_SW_OFFSET 0x12D4 +/* Address for the bulk read for SW DMA engine */ +#define THC_M_PRT_RD_BULK_ADDR_SW_OFFSET 0x12D8 +/* THC Frame Count from the SW RXDMA on this port */ +#define THC_M_PRT_FRM_CNT_SW_OFFSET 0x12DC +/* THC Packet Count from the SW RXDMA on this port */ +#define THC_M_PRT_RXDMA_PKT_CNT_SW_OFFSET 0x12E0 +/* SW DMA PRD Table Length */ +#define THC_M_PRT_SW_DMA_PRD_TABLE_LEN_OFFSET 0x12E4 +/* THC timing based Frame/Interrupt caolescing control register for 1st RXDMA */ +#define THC_M_PRT_COALESCE_CNTRL_1_OFFSET 0x12E8 +/* THC timing based Frame/Interrupt caolescing control register for 2nd RXDMA */ +#define THC_M_PRT_COALESCE_CNTRL_2_OFFSET 0x12EC +/* Touch Sequencer PRD Table Empty Counter for the 1st RXDMA */ +#define THC_M_PRT_PRD_EMPTY_CNT_1_OFFSET 0x12F0 +/* Touch Sequencer PRD Table Empty Counter for the 2nd RXDM */ +#define THC_M_PRT_PRD_EMPTY_CNT_2_OFFSET 0x12F4 +/* THC coalescing status to reflect the current coalescing FSM state for 1st RXDMA */ +#define THC_M_PRT_COALESCE_STS_1_OFFSET 0x12F8 +/* THC coalescing status to reflect the current coalescing FSM state for 2nd RXDMA */ +#define THC_M_PRT_COALESCE_STS_2_OFFSET 0x12FC +/* THC Register for the SPI Port Duty Cycle Configuration */ +#define THC_M_PRT_SPI_DUTYC_CFG_OFFSET 0x1300 +/* THC Register for SW I2C Wtite Sequecning control */ +#define THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET 0x1304 +/* THC current Timestamp Register for RXDMA1 */ +#define THC_M_PRT_TIMESTAMP_1_OFFSET 0x1308 +/* THC current Timestamp Register for RXDMA2 */ +#define THC_M_PRT_TIMESTAMP_2_OFFSET 0x130C +/* Current SYNC Event Timestamp Register */ +#define THC_M_PRT_SYNC_TIMESTAMP_OFFSET 0x1310 +/* THC Display Sync Register */ +#define THC_M_PRT_DISP_SYNC_OFFSET 0x1314 +/* THC Display Sync Register */ +#define THC_M_PRT_DISP_SYNC_2_OFFSET 0x1318 +/* THC Register for SW I2C Wtite Sequecning control */ +#define THC_M_PRT_I2C_CFG_OFFSET 0x131C + +/* THC register bits definition */ +#define TXN_ERR_INT_STS_BIT BIT(28) +#define TXN_FATAL_INT_STS_BIT BIT(30) + +#define NONDMA_INT_STS_BIT BIT(4) +#define EOF_INT_STS_BIT BIT(5) + +#define THC_CFG_DID_VID_VID GENMASK(15, 0) +#define THC_CFG_DID_VID_DID GENMASK(31, 16) + +#define THC_CFG_STS_CMD_IOSE BIT(0) +#define THC_CFG_STS_CMD_MSE BIT(1) +#define THC_CFG_STS_CMD_BME BIT(2) +#define THC_CFG_STS_CMD_SPCYC BIT(3) +#define THC_CFG_STS_CMD_MWRIEN BIT(4) +#define THC_CFG_STS_CMD_VGAPS BIT(5) +#define THC_CFG_STS_CMD_PERRR BIT(6) +#define THC_CFG_STS_CMD_SERREN BIT(8) +#define THC_CFG_STS_CMD_FBTBEN BIT(9) +#define THC_CFG_STS_CMD_INTD BIT(10) +#define THC_CFG_STS_CMD_INTS BIT(19) +#define THC_CFG_STS_CMD_CAPL BIT(20) +#define THC_CFG_STS_CMD_MCAP BIT(21) +#define THC_CFG_STS_CMD_FBTBC BIT(23) +#define THC_CFG_STS_CMD_MDPE BIT(24) +#define THC_CFG_STS_CMD_DEVT GENMASK(26, 25) +#define THC_CFG_STS_CMD_STA BIT(27) +#define THC_CFG_STS_CMD_RTA BIT(28) +#define THC_CFG_STS_CMD_RMA BIT(29) +#define THC_CFG_STS_CMD_SSE BIT(30) +#define THC_CFG_STS_CMD_DPE BIT(31) + +#define THC_CFG_CC_RID_RID GENMASK(7, 0) +#define THC_CFG_CC_RID_PI GENMASK(15, 8) +#define THC_CFG_CC_RID_SCC GENMASK(23, 16) +#define THC_CFG_CC_RID_BCC GENMASK(31, 24) + +#define THC_CFG_BIST_HTYPE_LT_CLS_CLSZ GENMASK(7, 0) +#define THC_CFG_BIST_HTYPE_LT_CLS_LT GENMASK(15, 8) +#define THC_CFG_BIST_HTYPE_LT_CLS_HTYPE GENMASK(22, 16) +#define THC_CFG_BIST_HTYPE_LT_CLS_MFD BIT(23) + +#define THC_CFG_BAR0_LOW_MEMSPACE BIT(0) +#define THC_CFG_BAR0_LOW_TYP GENMASK(2, 1) +#define THC_CFG_BAR0_LOW_PREFETCH BIT(3) +#define THC_CFG_BAR0_LOW_MEMSIZE GENMASK(14, 4) +#define THC_CFG_BAR0_LOW_MEMBAR GENMASK(31, 15) +#define THC_CFG_BAR0_HI_MEMBAR GENMASK(31, 0) + +#define THC_CFG_SID_SVID_SSVID GENMASK(15, 0) +#define THC_CFG_SID_SVID_SSID GENMASK(31, 16) + +#define THC_CFG_CAPP_CP GENMASK(7, 0) + +#define THC_CFG_INT_ILINE GENMASK(7, 0) +#define THC_CFG_INT_IPIN GENMASK(15, 8) + +#define THC_CFG_UR_STS_CTL_URRE BIT(0) +#define THC_CFG_UR_STS_CTL_URD BIT(1) +#define THC_CFG_UR_STS_CTL_FD BIT(2) + +#define THC_CFG_MSIMC_MSINP_MSICID_CAPID GENMASK(7, 0) +#define THC_CFG_MSIMC_MSINP_MSICID_NXTP GENMASK(15, 8) +#define THC_CFG_MSIMC_MSINP_MSICID_MSIE BIT(16) +#define THC_CFG_MSIMC_MSINP_MSICID_MMC GENMASK(19, 17) +#define THC_CFG_MSIMC_MSINP_MSICID_MMEN GENMASK(22, 20) +#define THC_CFG_MSIMC_MSINP_MSICID_XAC BIT(23) +#define THC_CFG_MSIMC_MSINP_MSICID_PVMC BIT(24) +#define THC_CFG_MSIMA_MADDR GENMASK(31, 2) +#define THC_CFG_MSIMUA_MAUDDR GENMASK(31, 0) +#define THC_CFG_MSIMD_MDAT GENMASK(15, 0) + +#define THC_CFG_PMCAP_PMNP_PMCID_CAPP GENMASK(7, 0) +#define THC_CFG_PMCAP_PMNP_PMCID_NXTP GENMASK(15, 8) +#define THC_CFG_PMCAP_PMNP_PMCID_VER GENMASK(18, 16) +#define THC_CFG_PMCAP_PMNP_PMCID_PMECLK BIT(19) +#define THC_CFG_PMCAP_PMNP_PMCID_DSI BIT(21) +#define THC_CFG_PMCAP_PMNP_PMCID_AUXC GENMASK(24, 22) +#define THC_CFG_PMCAP_PMNP_PMCID_D1S BIT(25) +#define THC_CFG_PMCAP_PMNP_PMCID_D2S BIT(26) +#define THC_CFG_PMCAP_PMNP_PMCID_PMES GENMASK(31, 27) + +#define THC_CFG_PMD_PMCSRBSE_PMCSR_PWRST GENMASK(1, 0) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_NSR BIT(3) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_PMEEN BIT(8) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_DSEL GENMASK(12, 9) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_DS GENMASK(14, 13) +#define THC_CFG_PMD_PMCSRBSE_PMCSR_PMESTS BIT(15) + +#define THC_CFG_DEVIDLE_CAPPID GENMASK(7, 0) +#define THC_CFG_DEVIDLE_NCAPPP GENMASK(15, 8) +#define THC_CFG_DEVIDLE_LENGTH GENMASK(23, 16) +#define THC_CFG_DEVIDLE_REV GENMASK(27, 24) +#define THC_CFG_DEVIDLE_VID GENMASK(31, 28) + +#define THC_CFG_VSHDR_VSECID GENMASK(15, 0) +#define THC_CFG_VSHDR_VSECR GENMASK(19, 16) +#define THC_CFG_VSHDR_VSECL GENMASK(31, 20) + +#define THC_CFG_SWLTRPTR_VALID BIT(0) +#define THC_CFG_SWLTRPTR_BARNUM GENMASK(3, 1) +#define THC_CFG_SWLTRPTR_SWLTRLOC GENMASK(31, 4) + +#define THC_CFG_DEVIDLEPTR_VALID BIT(0) +#define THC_CFG_DEVIDLEPTR_BARNUM GENMASK(3, 1) +#define THC_CFG_DEVIDLEPTR_DEVIDLELOC GENMASK(31, 4) +#define THC_CFG_DEVIDLEPOL_POLV GENMASK(9, 0) +#define THC_CFG_DEVIDLEPOL_POLS GENMASK(12, 10) + +#define THC_CFG_PCE_SPE BIT(0) +#define THC_CFG_PCE_I3E BIT(1) +#define THC_CFG_PCE_D3HE BIT(2) +#define THC_CFG_PCE_SE BIT(3) +#define THC_CFG_PCE_HAE BIT(5) + +#define THC_CFG_MANID_PROC GENMASK(7, 0) +#define THC_CFG_MANID_MID GENMASK(15, 8) +#define THC_CFG_MANID_MSID GENMASK(23, 16) +#define THC_CFG_MANID_DOT GENMASK(27, 24) + +#define THC_M_CMN_DEVIDLECTRL_CIP BIT(0) +#define THC_M_CMN_DEVIDLECTRL_IR BIT(1) +#define THC_M_CMN_DEVIDLECTRL_DEVIDLE BIT(2) +#define THC_M_CMN_DEVIDLECTRL_RR BIT(3) +#define THC_M_CMN_DEVIDLECTRL_IRC BIT(4) + +#define THC_M_CMN_LTR_CTRL_OFFSET 0x14 +#define THC_M_CMN_LTR_CTRL_ACTIVE_LTR_REQ BIT(0) +#define THC_M_CMN_LTR_CTRL_ACTIVE_LTR_EN BIT(1) +#define THC_M_CMN_LTR_CTRL_LP_LTR_REQ BIT(2) +#define THC_M_CMN_LTR_CTRL_LP_LTR_EN BIT(3) +#define THC_M_CMN_LTR_CTRL_LP_LTR_SCALE GENMASK(6, 4) +#define THC_M_CMN_LTR_CTRL_LP_LTR_VAL GENMASK(16, 7) +#define THC_M_CMN_LTR_CTRL_ACT_LTR_SCALE GENMASK(19, 17) +#define THC_M_CMN_LTR_CTRL_ACT_LTR_VAL GENMASK(29, 20) +#define THC_M_CMN_LTR_CTRL_LAST_LTR_SENT GENMASK(31, 30) + +#define THC_M_PRT_CONTROL_TSFTRST BIT(0) +#define THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_EN BIT(1) +#define THC_M_PRT_CONTROL_THC_DEVINT_QUIESCE_HW_STS BIT(2) +#define THC_M_PRT_CONTROL_DEVRST BIT(3) +#define THC_M_PRT_CONTROL_THC_DRV_LOCK_EN BIT(13) +#define THC_M_PRT_CONTROL_THC_INSTANCE_INDEX GENMASK(18, 16) +#define THC_M_PRT_CONTROL_PORT_INDEX GENMASK(22, 20) +#define THC_M_PRT_CONTROL_THC_ARB_POLICY GENMASK(25, 24) +#define THC_M_PRT_CONTROL_THC_BIOS_LOCK_EN BIT(27) +#define THC_M_PRT_CONTROL_PORT_SUPPORTED BIT(28) +#define THC_M_PRT_CONTROL_SPI_IO_RDY BIT(29) +#define THC_M_PRT_CONTROL_PORT_TYPE GENMASK(31, 30) + +#define THC_M_PRT_SPI_CFG_SPI_TRDC GENMASK(1, 0) +#define THC_M_PRT_SPI_CFG_SPI_TRMODE GENMASK(3, 2) +#define THC_M_PRT_SPI_CFG_SPI_TCRF GENMASK(6, 4) +#define THC_M_PRT_SPI_CFG_SPI_RD_MPS GENMASK(15, 7) +#define THC_M_PRT_SPI_CFG_SPI_TWMODE GENMASK(19, 18) +#define THC_M_PRT_SPI_CFG_SPI_TCWF GENMASK(22, 20) +#define THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN BIT(23) +#define THC_M_PRT_SPI_CFG_SPI_WR_MPS GENMASK(31, 24) + +#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_SIO GENMASK(31, 24) +#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_DIO GENMASK(23, 16) +#define THC_M_PRT_SPI_ICRRD_OPCODE_SPI_QIO GENMASK(15, 8) + +#define THC_M_PRT_INT_EN_SIPE BIT(0) +#define THC_M_PRT_INT_EN_SBO BIT(1) +#define THC_M_PRT_INT_EN_SIDR BIT(2) +#define THC_M_PRT_INT_EN_SOFB BIT(3) +#define THC_M_PRT_INT_EN_INVLD_DEV_ENTRY_INT_EN BIT(9) +#define THC_M_PRT_INT_EN_FRAME_BABBLE_ERR_INT_EN BIT(10) +#define THC_M_PRT_INT_EN_BUF_OVRRUN_ERR_INT_EN BIT(12) +#define THC_M_PRT_INT_EN_PRD_ENTRY_ERR_INT_EN BIT(13) +#define THC_M_PRT_INT_EN_DISP_SYNC_EVT_INT_EN BIT(14) +#define THC_M_PRT_INT_EN_DEV_RAW_INT_EN BIT(15) +#define THC_M_PRT_INT_EN_FATAL_ERR_INT_EN BIT(16) +#define THC_M_PRT_INT_EN_THC_I2C_IC_RX_UNDER_INT_EN BIT(17) +#define THC_M_PRT_INT_EN_THC_I2C_IC_RX_OVER_INT_EN BIT(18) +#define THC_M_PRT_INT_EN_THC_I2C_IC_RX_FULL_INT_EN BIT(19) +#define THC_M_PRT_INT_EN_THC_I2C_IC_TX_OVER_INT_EN BIT(20) +#define THC_M_PRT_INT_EN_THC_I2C_IC_TX_EMPTY_INT_EN BIT(21) +#define THC_M_PRT_INT_EN_THC_I2C_IC_TX_ABRT_INT_EN BIT(22) +#define THC_M_PRT_INT_EN_THC_I2C_IC_SCL_STUCK_AT_LOW_DET_INT_EN BIT(24) +#define THC_M_PRT_INT_EN_THC_I2C_IC_STOP_DET_INT_EN BIT(25) +#define THC_M_PRT_INT_EN_THC_I2C_IC_START_DET_INT_EN BIT(26) +#define THC_M_PRT_INT_EN_THC_I2C_IC_MST_ON_HOLD_INT_EN BIT(27) +#define THC_M_PRT_INT_EN_TXN_ERR_INT_EN BIT(29) +#define THC_M_PRT_INT_EN_GBL_INT_EN BIT(31) + +#define THC_M_PRT_INT_STATUS_DISP_SYNC_EVT_INT_STS BIT(14) +#define THC_M_PRT_INT_STATUS_DEV_RAW_INT_STS BIT(15) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_UNDER_INT_STS BIT(17) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_OVER_INT_STS BIT(18) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_RX_FULL_INT_STS BIT(19) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_OVER_INT_STS BIT(20) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_EMPTY_INT_STS BIT(21) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_TX_ABRT_INT_STS BIT(22) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_ACTIVITY_INT_STS BIT(23) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_SCL_STUCK_AT_LOW_INT_STS BIT(24) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_STOP_DET_INT_STS BIT(25) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_START_DET_INT_STS BIT(26) +#define THC_M_PRT_INT_STATUS_THC_I2C_IC_MST_ON_HOLD_INT_STS BIT(27) +#define THC_M_PRT_INT_STATUS_TXN_ERR_INT_STS BIT(28) +#define THC_M_PRT_INT_STATUS_FATAL_ERR_INT_STS BIT(30) + +#define THC_M_PRT_ERR_CAUSE_INVLD_DEV_ENTRY BIT(9) +#define THC_M_PRT_ERR_CAUSE_FRAME_BABBLE_ERR BIT(10) +#define THC_M_PRT_ERR_CAUSE_BUF_OVRRUN_ERR BIT(12) +#define THC_M_PRT_ERR_CAUSE_PRD_ENTRY_ERR BIT(13) +#define THC_M_PRT_ERR_CAUSE_FATAL_ERR_CAUSE GENMASK(23, 16) + +#define THC_M_PRT_SW_SEQ_CNTRL_TSSGO BIT(0) +#define THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE BIT(1) +#define THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD GENMASK(15, 8) +#define THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC GENMASK(31, 16) +#define THC_M_PRT_SW_SEQ_STS_TSSDONE BIT(0) +#define THC_M_PRT_SW_SEQ_STS_THC_SS_ERR BIT(1) +#define THC_M_PRT_SW_SEQ_STS_THC_SS_CIP BIT(3) +#define THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR GENMASK(31, 0) +#define THC_M_PRT_SW_SEQ_DATA1_THC_SW_SEQ_DATA1 GENMASK(31, 0) + +#define THC_M_PRT_WPRD_BA_LOW_THC_M_PRT_WPRD_BA_LOW GENMASK(31, 12) +#define THC_M_PRT_WPRD_BA_HI_THC_M_PRT_WPRD_BA_HI GENMASK(31, 0) + +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_START BIT(0) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_ERROR BIT(1) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC BIT(2) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_IE_IOC_DMACPL BIT(3) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_UHS BIT(23) +#define THC_M_PRT_WRITE_DMA_CNTRL_THC_WRDMA_PTEC GENMASK(31, 24) + +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_CMPL_STATUS BIT(0) +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ERROR_STS BIT(1) +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_IOC_STS BIT(2) +#define THC_M_PRT_WRITE_INT_STS_THC_WRDMA_ACTIVE BIT(3) + +#define THC_M_PRT_WR_BULK_ADDR_THC_M_PRT_WR_BULK_ADDR GENMASK(31, 0) + +#define THC_M_PRT_DEV_INT_CAUSE_ADDR_THC_M_PRT_DEV_INT_CAUSE_ADDR GENMASK(31, 0) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_INTERRUPT_TYPE GENMASK(3, 0) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_MICRO_FRAME_SIZE GENMASK(23, 4) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_BEGINNING_OF_FRAME BIT(29) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_END_OF_FRAME BIT(30) +#define THC_M_PRT_DEV_INT_CAUSE_REG_VAL_FRAME_TYPE BIT(31) + +#define THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT GENMASK(30, 0) +#define THC_M_PRT_TX_FRM_CNT_THC_M_PRT_TX_FRM_CNT_RST BIT(31) + +#define THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT GENMASK(30, 0) +#define THC_M_PRT_TXDMA_PKT_CNT_THC_M_PRT_TXDMA_PKT_CNT_RST BIT(31) + +#define THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT GENMASK(30, 0) +#define THC_M_PRT_DEVINT_CNT_THC_M_PRT_DEVINT_CNT_RST BIT(31) + +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_OFFSET GENMASK(4, 0) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_LEN GENMASK(9, 5) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_EOF_OFFSET GENMASK(14, 10) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_SEND_ICR_US_EN BIT(15) +#define THC_M_PRT_DEVINT_CFG_1_THC_M_PRT_INTTYP_DATA_VAL GENMASK(31, 16) + +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_OFFSET GENMASK(4, 0) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_LEN GENMASK(9, 5) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_UFSIZE_UNIT GENMASK(15, 12) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_IGNORE BIT(16) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_FTYPE_VAL BIT(17) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_RXDMA_ADDRINC_DIS BIT(24) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_TXDMA_ADDRINC_DIS BIT(25) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_RXDMA_PKT_STRM_EN BIT(26) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_TXDMA_PKT_STRM_EN BIT(27) +#define THC_M_PRT_DEVINT_CFG_2_THC_M_PRT_DEVINT_POL BIT(28) + +#define THC_M_PRT_RPRD_BA_LOW_1_THC_M_PRT_RPRD_BA_LOW GENMASK(31, 12) +#define THC_M_PRT_RPRD_BA_HI_1_THC_M_PRT_RPRD_BA_HI GENMASK(31, 0) + +#define THC_M_PRT_RPRD_CNTRL_PCD GENMASK(6, 0) +#define THC_M_PRT_RPRD_CNTRL_PTEC GENMASK(15, 8) +#define THC_M_PRT_RPRD_CNTRL_PREFETCH_WM GENMASK(19, 16) + +#define THC_M_PRT_READ_DMA_CNTRL_START BIT(0) +#define THC_M_PRT_READ_DMA_CNTRL_IE_ERROR BIT(1) +#define THC_M_PRT_READ_DMA_CNTRL_IE_IOC BIT(2) +#define THC_M_PRT_READ_DMA_CNTRL_IE_STALL BIT(3) +#define THC_M_PRT_READ_DMA_CNTRL_IE_NDDI BIT(4) +#define THC_M_PRT_READ_DMA_CNTRL_IE_EOF BIT(5) +#define THC_M_PRT_READ_DMA_CNTRL_IE_DMACPL BIT(7) +#define THC_M_PRT_READ_DMA_CNTRL_TPCRP GENMASK(15, 8) +#define THC_M_PRT_READ_DMA_CNTRL_TPCWP GENMASK(23, 16) +#define THC_M_PRT_READ_DMA_CNTRL_INT_SW_DMA_EN BIT(28) +#define THC_M_PRT_READ_DMA_CNTRL_SOO BIT(29) +#define THC_M_PRT_READ_DMA_CNTRL_UHS BIT(30) +#define THC_M_PRT_READ_DMA_CNTRL_TPCPR BIT(31) + +#define THC_M_PRT_READ_DMA_INT_STS_DMACPL_STS BIT(0) +#define THC_M_PRT_READ_DMA_INT_STS_ERROR_STS BIT(1) +#define THC_M_PRT_READ_DMA_INT_STS_IOC_STS BIT(2) +#define THC_M_PRT_READ_DMA_INT_STS_STALL_STS BIT(3) +#define THC_M_PRT_READ_DMA_INT_STS_NONDMA_INT_STS BIT(4) +#define THC_M_PRT_READ_DMA_INT_STS_EOF_INT_STS BIT(5) +#define THC_M_PRT_READ_DMA_INT_STS_ACTIVE BIT(8) + +#define THC_M_PRT_READ_DMA_ERR_1_DLERR BIT(0) + +#define THC_M_PRT_GUC_OFFSET_LOW_1_THC_M_PRT_GUC_OFFSET_LOW GENMASK(31, 3) +#define THC_M_PRT_GUC_OFFSET_HI_1_THC_M_PRT_GUC_OFFSET_HI GENMASK(31, 0) +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_1_WORKQ_ITEM_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_1_WORKQ_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_1_FCD GENMASK(27, 24) +#define THC_M_PRT_GUC_WORKQ_SZ_1_GIC GENMASK(31, 28) + +#define THC_M_PRT_TSEQ_CNTRL_1_RGD BIT(2) +#define THC_M_PRT_TSEQ_CNTRL_1_EGP BIT(3) +#define THC_M_PRT_TSEQ_CNTRL_1_RTO BIT(4) +#define THC_M_PRT_TSEQ_CNTRL_1_EWOG BIT(5) +#define THC_M_PRT_TSEQ_CNTRL_1_RWOGC BIT(6) +#define THC_M_PRT_TSEQ_CNTRL_1_RX_DATA_FIFO_WR_WM GENMASK(25, 16) +#define THC_M_PRT_TSEQ_CNTRL_1_RESET_PREP_CHICKEN BIT(30) +#define THC_M_PRT_TSEQ_CNTRL_1_INT_EDG_DET_EN BIT(31) + +#define THC_M_PRT_GUC_DB_ADDR_LOW_1_GUC_DB_ADDR_LOW GENMASK(31, 2) +#define THC_M_PRT_GUC_DB_ADDR_HI_1_GUC_DB_ADDR_HI GENMASK(31, 0) +#define THC_M_PRT_GUC_DB_DATA_1_GUC_DB_DATA GENMASK(31, 0) +#define THC_M_PRT_GUC_OFFSET_INITVAL_1_THC_M_PRT_GUC_OFFSET_INITVAL GENMASK(31, 0) + +#define THC_M_PRT_RD_BULK_ADDR_1_THC_M_PRT_RD_BULK_ADDR GENMASK(31, 0) + +#define THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT GENMASK(30, 0) +#define THC_M_PRT_DB_CNT_1_THC_M_PRT_DB_CNT_RST BIT(31) + +#define THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT GENMASK(30, 0) +#define THC_M_PRT_FRM_CNT_1_THC_M_PRT_FRM_CNT_RST BIT(31) + +#define THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT GENMASK(30, 0) +#define THC_M_PRT_UFRM_CNT_1_THC_M_PRT_UFRM_CNT_RST BIT(31) + +#define THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT GENMASK(30, 0) +#define THC_M_PRT_RXDMA_PKT_CNT_1_THC_M_PRT_RXDMA_PKT_CNT_RST BIT(31) + +#define THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT GENMASK(30, 0) +#define THC_M_PRT_SWINT_CNT_1_THC_M_PRT_SWINT_CNT_RST BIT(31) + +#define THC_M_PRT_FRAME_DROP_CNT_1_NOFD GENMASK(30, 0) +#define THC_M_PRT_FRAME_DROP_CNT_1_RFDC BIT(31) + +#define THC_M_PRT_COALESCE_1_COALESCE_TIMEOUT GENMASK(6, 0) + +#define THC_M_PRT_RPRD_BA_LOW_2_THC_M_PRT_RPRD_BA_LOW GENMASK(31, 12) +#define THC_M_PRT_RPRD_BA_HI_2_THC_M_PRT_RPRD_BA_HI GENMASK(31, 0) + +#define THC_M_PRT_READ_DMA_ERR_2_DLERR BIT(0) + +#define THC_M_PRT_GUC_OFFSET_LOW_2_THC_M_PRT_GUC_OFFSET_LOW GENMASK(31, 3) +#define THC_M_PRT_GUC_OFFSET_HI_2_THC_M_PRT_GUC_OFFSET_HI GENMASK(31, 0) + +#define THC_M_PRT_GUC_WORKQ_ITEM_SZ_2_WORKQ_ITEM_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_2_WORKQ_SZ GENMASK(23, 0) +#define THC_M_PRT_GUC_WORKQ_SZ_2_FCD GENMASK(27, 24) +#define THC_M_PRT_GUC_WORKQ_SZ_2_GIC GENMASK(31, 28) + +#define THC_M_PRT_TSEQ_CNTRL_2_RGD BIT(2) +#define THC_M_PRT_TSEQ_CNTRL_2_EGP BIT(3) +#define THC_M_PRT_TSEQ_CNTRL_2_RTO BIT(4) + +#define THC_M_PRT_GUC_DB_ADDR_LOW_2_GUC_DB_ADDR_LOW GENMASK(31, 2) +#define THC_M_PRT_GUC_DB_ADDR_HI_2_GUC_DB_ADDR_HI GENMASK(31, 0) + +#define THC_M_PRT_GUC_DB_DATA_2_GUC_DB_DATA GENMASK(31, 0) + +#define THC_M_PRT_GUC_OFFSET_INITVAL_2_THC_M_PRT_GUC_OFFSET_INITVAL GENMASK(31, 0) + +#define THC_M_PRT_RD_BULK_ADDR_2_THC_M_PRT_RD_BULK_ADDR GENMASK(31, 0) + +#define THC_M_PRT_DB_CNT_2_THC_M_PRT_DB_CNT GENMASK(30, 0) +#define THC_M_PRT_DB_CNT_2_THC_M_PRT_DB_CNT_RST BIT(31) + +#define THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT GENMASK(30, 0) +#define THC_M_PRT_FRM_CNT_2_THC_M_PRT_FRM_CNT_RST BIT(31) + +#define THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT GENMASK(30, 0) +#define THC_M_PRT_UFRM_CNT_2_THC_M_PRT_UFRM_CNT_RST BIT(31) + +#define THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT GENMASK(30, 0) +#define THC_M_PRT_RXDMA_PKT_CNT_2_THC_M_PRT_RXDMA_PKT_CNT_RST BIT(31) + +#define THC_M_PRT_SWINT_CNT_2_THC_M_PRT_SWINT_CNT GENMASK(30, 0) +#define THC_M_PRT_SWINT_CNT_2_THC_M_PRT_SWINT_CNT_RST BIT(31) + +#define THC_M_PRT_FRAME_DROP_CNT_2_NOFD GENMASK(30, 0) +#define THC_M_PRT_FRAME_DROP_CNT_2_RFDC BIT(31) + +#define THC_M_PRT_COALESCE_2_COALESCE_TIMEOUT GENMASK(6, 0) + +#define THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN BIT(23) +#define THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC GENMASK(31, 26) + +#define THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_RX_DLEN_EN BIT(23) +#define THC_M_PRT_RPRD_CNTRL_SW_THC_SWDMA_I2C_WBC GENMASK(31, 26) + +#define THC_M_PRT_PRD_EMPTY_CNT_1_RPTEC BIT(31) +#define THC_M_PRT_PRD_EMPTY_CNT_2_RPTEC BIT(31) + +#define THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN GENMASK(23, 0) + +#define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_VAL GENMASK(3, 0) +#define THC_M_PRT_SPI_DUTYC_CFG_SPI_CSA_CK_DELAY_EN BIT(25) + +/* CS Assertion delay default value */ +#define THC_CSA_CK_DELAY_VAL_DEFAULT 4 + +/* ARB policy definition */ +/* Arbiter switches on packet boundary */ +#define THC_ARB_POLICY_PACKET_BOUNDARY 0 +/* Arbiter switches on Micro Frame boundary */ +#define THC_ARB_POLICY_UFRAME_BOUNDARY 1 +/* Arbiter switches on Frame boundary */ +#define THC_ARB_POLICY_FRAME_BOUNDARY 2 + +#define THC_REGMAP_POLLING_INTERVAL_US 10 /* 10us */ +#define THC_PIO_DONE_TIMEOUT_US USEC_PER_SEC /* 1s */ + +/* Default configures for HIDSPI */ +#define THC_BIT_OFFSET_INTERRUPT_TYPE 4 +/* input_report_type is 4 bits for HIDSPI */ +#define THC_BIT_LENGTH_INTERRUPT_TYPE 4 +/* Last fragment indicator is bit 15 for HIDSPI */ +#define THC_BIT_OFFSET_LAST_FRAGMENT_FLAG 22 +#define THC_BIT_OFFSET_MICROFRAME_SIZE 8 +/* input_report_length is 14 bits for HIDSPI */ +#define THC_BIT_LENGTH_MICROFRAME_SIZE 14 +/* MFS unit in power of 2 */ +#define THC_UNIT_MICROFRAME_SIZE 2 +#define THC_BITMASK_INTERRUPT_TYPE_DATA 1 +#define THC_BITMASK_INVALID_TYPE_DATA 2 + +/* Interrupt Quiesce default timeout value */ +#define THC_QUIESCE_EN_TIMEOUT_US USEC_PER_SEC /* 1s */ + +/* LTR definition */ +/* + * THC uses scale to calculate final LTR value. + * Scale is geometric progression of 2^5 step, starting from 2^0. + * For example, THC_LTR_SCALE_2(2) means 2^(5 * 2) = 1024, unit is ns. + */ +#define THC_LTR_SCALE_0 0 +#define THC_LTR_SCALE_1 1 +#define THC_LTR_SCALE_2 2 +#define THC_LTR_SCALE_3 3 +#define THC_LTR_SCALE_4 4 +#define THC_LTR_SCALE_5 5 +#define THC_LTR_MODE_ACTIVE 0 +#define THC_LTR_MODE_LP 1 +#define THC_LTR_MIN_VAL_SCALE_3 BIT(10) +#define THC_LTR_MAX_VAL_SCALE_3 BIT(15) +#define THC_LTR_MIN_VAL_SCALE_4 BIT(15) +#define THC_LTR_MAX_VAL_SCALE_4 BIT(20) +#define THC_LTR_MIN_VAL_SCALE_5 BIT(20) +#define THC_LTR_MAX_VAL_SCALE_5 BIT(25) + +/* + * THC PIO opcode default value + * @THC_PIO_OP_SPI_TIC_READ: THC opcode for SPI PIO read + * @THC_PIO_OP_SPI_TIC_WRITE: THC opcode for SPI PIO write + * @THC_PIO_OP_I2C_SUBSYSTEM_READ: THC opcode for read I2C subsystem registers + * @THC_PIO_OP_I2C_SUBSYSTEM_WRITE: THC opcode for write I2C subsystem registers + * @THC_PIO_OP_I2C_TIC_READ: THC opcode for read I2C device + * @THC_PIO_OP_I2C_TIC_WRITE: THC opcode for write I2C device + * @THC_PIO_OP_I2C_TIC_WRITE_AND_READ: THC opcode for write followed by read I2C device + */ +enum thc_pio_opcode { + THC_PIO_OP_SPI_TIC_READ = 0x4, + THC_PIO_OP_SPI_TIC_WRITE = 0x6, + THC_PIO_OP_I2C_SUBSYSTEM_READ = 0x12, + THC_PIO_OP_I2C_SUBSYSTEM_WRITE = 0x13, + THC_PIO_OP_I2C_TIC_READ = 0x14, + THC_PIO_OP_I2C_TIC_WRITE = 0x18, + THC_PIO_OP_I2C_TIC_WRITE_AND_READ = 0x1C, +}; + +/** + * THC SPI IO mode + * @THC_SINGLE_IO: single IO mode, 1(opcode) - 1(address) - 1(data) + * @THC_DUAL_IO: dual IO mode, 1(opcode) - 2(address) - 2(data) + * @THC_QUAD_IO: quad IO mode, 1(opcode) - 4(address) - 4(data) + * @THC_QUAD_PARALLEL_IO: parallel quad IO mode, 4(opcode) - 4(address) - 4(data) + */ +enum thc_spi_iomode { + THC_SINGLE_IO = 0, + THC_DUAL_IO = 1, + THC_QUAD_IO = 2, + THC_QUAD_PARALLEL_IO = 3, +}; + +/** + * THC SPI frequency divider + * + * This DIV final value is determined by THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN bit. + * If THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN isn't be set, THC takes the DIV value directly; + * If THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN is set, THC takes the DIV value multiply by 8. + * + * For example, if THC input clock is 125MHz: + * When THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN isn't set, THC_SPI_FRQ_DIV_3 means DIV is 3, + * THC final clock is 125 / 3 = 41.667MHz; + * When THC_M_PRT_SPI_CFG_SPI_LOW_FREQ_EN is set, THC_SPI_FRQ_DIV_3 means DIV is 3 * 8, + * THC final clock is 125 / (3 * 8) = 5.208MHz; + */ +enum thc_spi_frq_div { + THC_SPI_FRQ_RESERVED = 0, + THC_SPI_FRQ_DIV_1 = 1, + THC_SPI_FRQ_DIV_2 = 2, + THC_SPI_FRQ_DIV_3 = 3, + THC_SPI_FRQ_DIV_4 = 4, + THC_SPI_FRQ_DIV_5 = 5, + THC_SPI_FRQ_DIV_6 = 6, + THC_SPI_FRQ_DIV_7 = 7, +}; + +/* THC I2C sub-system registers */ +#define THC_I2C_IC_CON_OFFSET 0x0 +#define THC_I2C_IC_TAR_OFFSET 0x4 +#define THC_I2C_IC_SAR_OFFSET 0x8 +#define THC_I2C_IC_HS_MADDR_OFFSET 0xC +#define THC_I2C_IC_DATA_CMD_OFFSET 0x10 +#define THC_I2C_IC_SS_SCL_HCNT_OFFSET 0x14 +#define THC_I2C_IC_UFM_SCL_HCNT_OFFSET 0x14 +#define THC_I2C_IC_SS_SCL_LCNT_OFFSET 0x18 +#define THC_I2C_IC_UFM_SCL_LCNT_OFFSET 0x18 +#define THC_I2C_IC_FS_SCL_HCNT_OFFSET 0x1C +#define THC_I2C_IC_UFM_TBUF_CNT_OFFSET 0x1C +#define THC_I2C_IC_FS_SCL_LCNT_OFFSET 0x20 +#define THC_I2C_IC_HS_SCL_HCNT_OFFSET 0x24 +#define THC_I2C_IC_HS_SCL_LCNT_OFFSET 0x28 +#define THC_I2C_IC_INTR_STAT_OFFSET 0x2C +#define THC_I2C_IC_INTR_MASK_OFFSET 0x30 +#define THC_I2C_IC_RAW_INTR_STAT_OFFSET 0x34 +#define THC_I2C_IC_RX_TL_OFFSET 0x38 +#define THC_I2C_IC_TX_TL_OFFSET 0x3C +#define THC_I2C_IC_CLR_INTR_OFFSET 0x40 +#define THC_I2C_IC_CLR_RX_UNDER_OFFSET 0x44 +#define THC_I2C_IC_CLR_RX_OVER_OFFSET 0x48 +#define THC_I2C_IC_CLR_TX_OVER_OFFSET 0x4C +#define THC_I2C_IC_CLR_RD_REQ_OFFSET 0x50 +#define THC_I2C_IC_CLR_TX_ABRT_OFFSET 0x54 +#define THC_I2C_IC_CLR_RX_DONE_OFFSET 0x58 +#define THC_I2C_IC_CLR_ACTIVITY_OFFSET 0x5C +#define THC_I2C_IC_CLR_STOP_DET_OFFSET 0x60 +#define THC_I2C_IC_CLR_START_DET_OFFSET 0x64 +#define THC_I2C_IC_CLR_GEN_CALL_OFFSET 0x68 +#define THC_I2C_IC_ENABLE_OFFSET 0x6C +#define THC_I2C_IC_STATUS_OFFSET 0x70 +#define THC_I2C_IC_TXFLR_OFFSET 0x74 +#define THC_I2C_IC_RXFLR_OFFSET 0x78 +#define THC_I2C_IC_SDA_HOLD_OFFSET 0x7C +#define THC_I2C_IC_TX_ABRT_SOURCE_OFFSET 0x80 +#define THC_I2C_IC_SLV_DATA_NACK_ONLY_OFFSET 0x84 +#define THC_I2C_IC_DMA_CR_OFFSET 0x88 +#define THC_I2C_IC_DMA_TDLR_OFFSET 0x8C +#define THC_I2C_IC_DMA_RDLR_OFFSET 0x90 +#define THC_I2C_IC_SDA_SETUP_OFFSET 0x94 +#define THC_I2C_IC_ACK_GENERAL_CALL_OFFSET 0x98 +#define THC_I2C_IC_ENABLE_STATUS_OFFSET 0x9C +#define THC_I2C_IC_FS_SPKLEN_OFFSET 0xA0 +#define THC_I2C_IC_UFM_SPKLEN_OFFSET 0xA0 +#define THC_I2C_IC_HS_SPKLEN_OFFSET 0xA4 +#define THC_I2C_IC_CLR_RESTART_DET_OFFSET 0xA8 +#define THC_I2C_IC_SCL_STUCK_AT_LOW_TIMEOUT_OFFSET 0xAC +#define THC_I2C_IC_SDA_STUCK_AT_LOW_TIMEOUT_OFFSET 0xB0 +#define THC_I2C_IC_CLR_SCL_STUCK_DET_OFFSET 0xB4 +#define THC_I2C_IC_DEVICE_ID_OFFSET 0xB8 +#define THC_I2C_IC_SMBUS_CLK_LOW_SEXT_OFFSET 0xBC +#define THC_I2C_IC_SMBUS_CLK_LOW_MEXT_OFFSET 0xC0 +#define THC_I2C_IC_SMBUS_THIGH_MAX_IDLE_COUNT_OFFSET 0xC4 +#define THC_I2C_IC_SMBUS_INTR_STAT_OFFSET 0xC8 +#define THC_I2C_IC_SMBUS_INTR_MASK_OFFSET 0xCC +#define THC_I2C_IC_SMBUS_RAW_INTR_STAT_OFFSET 0xD0 +#define THC_I2C_IC_CLR_SMBUS_INTR_OFFSET 0xD4 +#define THC_I2C_IC_OPTIONAL_SAR_OFFSET 0xD8 +#define THC_I2C_IC_SMBUS_UDID_LSB_OFFSET 0xDC +#define THC_I2C_IC_SMBUS_UDID_WORD0_OFFSET 0xDC +#define THC_I2C_IC_SMBUS_UDID_WORD1_OFFSET 0xE0 +#define THC_I2C_IC_SMBUS_UDID_WORD2_OFFSET 0xE4 +#define THC_I2C_IC_SMBUS_UDID_WORD3_OFFSET 0xE8 +#define THC_I2C_IC_COMP_PARAM_1_OFFSET 0xF4 +#define THC_I2C_IC_COMP_VERSION_OFFSET 0xF8 +#define THC_I2C_IC_COMP_TYPE_OFFSET 0xFC + +/** + * THC I2C sub-system supported speed mode + */ +enum THC_I2C_SPEED_MODE { + THC_I2C_STANDARD = 1, + THC_I2C_FAST_AND_PLUS = 2, + THC_I2C_HIGH_SPEED = 3, +}; + +/* THC I2C sub-system register bits definition */ +#define THC_I2C_IC_ENABLE_ENABLE BIT(0) +#define THC_I2C_IC_ENABLE_ABORT BIT(1) +#define THC_I2C_IC_ENABLE_TX_CMD_BLOCK BIT(2) +#define THC_I2C_IC_ENABLE_SDA_STUCK_RECOVERY_ENABLE BIT(3) +#define THC_I2C_IC_ENABLE_SMBUS_CLK_RESET BIT(16) +#define THC_I2C_IC_ENABLE_SMBUS_SUSPEND_EN BIT(17) +#define THC_I2C_IC_ENABLE_SMBUS_ALERT_EN BIT(18) + +#define THC_I2C_IC_CON_MASTER_MODE BIT(0) +#define THC_I2C_IC_CON_SPEED GENMASK(2, 1) +#define THC_I2C_IC_CON_IC_10BITADDR_SLAVE BIT(3) +#define THC_I2C_IC_CON_IC_10BITADDR_MASTER BIT(4) +#define THC_I2C_IC_CON_IC_RESTART_EN BIT(5) +#define THC_I2C_IC_CON_IC_SLAVE_DISABLE BIT(6) +#define THC_I2C_IC_CON_STOP_DET_IFADDRESSED BIT(7) +#define THC_I2C_IC_CON_TX_EMPTY_CTRL BIT(8) +#define THC_I2C_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9) +#define THC_I2C_IC_CON_STOP_DET_IF_MASTER_ACTIVE BIT(10) +#define THC_I2C_IC_CON_BUS_CLEAR_FEATURE_CTRL BIT(11) +#define THC_I2C_IC_CON_OPTIONAL_SAR_CTRL BIT(16) +#define THC_I2C_IC_CON_SMBUS_SLAVE_QUICK_EN BIT(17) +#define THC_I2C_IC_CON_SMBUS_ARP_EN BIT(18) +#define THC_I2C_IC_CON_SMBUS_PERSISTENT_SLV_ADDR_EN BIT(19) + +#define THC_I2C_IC_TAR_IC_TAR GENMASK(9, 0) +#define THC_I2C_IC_TAR_GC_OR_START BIT(10) +#define THC_I2C_IC_TAR_SPECIAL BIT(11) +#define THC_I2C_IC_TAR_IC_10BITADDR_MASTER BIT(12) +#define THC_I2C_IC_TAR_DEVICE_ID BIT(13) +#define THC_I2C_IC_TAR_SMBUS_QUICK_CMD BIT(16) + +#define THC_I2C_IC_INTR_MASK_M_RX_UNDER BIT(0) +#define THC_I2C_IC_INTR_MASK_M_RX_OVER BIT(1) +#define THC_I2C_IC_INTR_MASK_M_RX_FULL BIT(2) +#define THC_I2C_IC_INTR_MASK_M_TX_OVER BIT(3) +#define THC_I2C_IC_INTR_MASK_M_TX_EMPTY BIT(4) +#define THC_I2C_IC_INTR_MASK_M_RD_REQ BIT(5) +#define THC_I2C_IC_INTR_MASK_M_TX_ABRT BIT(6) +#define THC_I2C_IC_INTR_MASK_M_RX_DONE BIT(7) +#define THC_I2C_IC_INTR_MASK_M_ACTIVITY BIT(8) +#define THC_I2C_IC_INTR_MASK_M_STOP_DET BIT(9) +#define THC_I2C_IC_INTR_MASK_M_START_DET BIT(10) +#define THC_I2C_IC_INTR_MASK_M_GEN_CALL BIT(11) +#define THC_I2C_IC_INTR_MASK_M_RESTART_DET BIT(12) +#define THC_I2C_IC_INTR_MASK_M_MASTER_ON_HOLD BIT(13) +#define THC_I2C_IC_INTR_MASK_M_SCL_STUCK_AT_LOW BIT(14) + +#define THC_I2C_IC_DMA_CR_RDMAE BIT(0) +#define THC_I2C_IC_DMA_CR_TDMAE BIT(1) + +#endif /* _INTEL_THC_HW_H_ */ |