/* 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;
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;
/** * 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;
/** * 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;
/** * 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(conststruct thc_device *dev, bool int_quiesce)
{
u32 ctrl; int ret;
/* 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);
/** * 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;
/** * 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;
/* 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;
/** * 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);
} elseif (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;
/** * 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;
/** * 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, &dev->i2c_subip_regs[i]); if (ret < 0) return ret;
}
/** * 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, &dev->i2c_subip_regs[i]); if (ret < 0) return ret;
}
/** * thc_i2c_set_rx_max_size - Set I2C Rx transfer max input size * @dev: The pointer of THC private device context * @max_rx_size: Max input report packet size for input report * * Set @max_rx_size for I2C RxDMA max input size control feature. * * Return: 0 on success, other error codes on failure.
*/ int thc_i2c_set_rx_max_size(struct thc_device *dev, u32 max_rx_size)
{
u32 val; int ret;
if (!dev) return -EINVAL;
if (!max_rx_size) return -EOPNOTSUPP;
ret = regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &val); if (ret) return ret;
val |= FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE, max_rx_size);
ret = regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, val); if (ret) return ret;
/** * thc_i2c_rx_max_size_enable - Enable I2C Rx max input size control * @dev: The pointer of THC private device context * @enable: Enable max input size control or not * * Enable or disable I2C RxDMA max input size control feature. * Max input size control only can be enabled after max input size * was set by thc_i2c_set_rx_max_size(). * * Return: 0 on success, other error codes on failure.
*/ int thc_i2c_rx_max_size_enable(struct thc_device *dev, bool enable)
{
u32 mask = THC_M_PRT_SPI_ICRRD_OPCODE_I2C_MAX_SIZE_EN;
u32 val = enable ? mask : 0; int ret;
if (!dev) return -EINVAL;
if (!dev->i2c_max_rx_size) return -EOPNOTSUPP;
ret = regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, mask, val); if (ret) return ret;
/** * thc_i2c_set_rx_int_delay - Set I2C Rx input interrupt delay value * @dev: The pointer of THC private device context * @delay_us: Interrupt delay value, unit is us * * Set @delay_us for I2C RxDMA input interrupt delay feature. * * Return: 0 on success, other error codes on failure.
*/ int thc_i2c_set_rx_int_delay(struct thc_device *dev, u32 delay_us)
{
u32 val; int ret;
if (!dev) return -EINVAL;
if (!delay_us) return -EOPNOTSUPP;
ret = regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &val); if (ret) return ret;
/* THC hardware counts at 10us unit */
val |= FIELD_PREP(THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL, DIV_ROUND_UP(delay_us, 10));
ret = regmap_write(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, val); if (ret) return ret;
/** * thc_i2c_rx_int_delay_enable - Enable I2C Rx interrupt delay * @dev: The pointer of THC private device context * @enable: Enable interrupt delay or not * * Enable or disable I2C RxDMA input interrupt delay feature. * Input interrupt delay can only be enabled after interrupt delay value * was set by thc_i2c_set_rx_int_delay(). * * Return: 0 on success, other error codes on failure.
*/ int thc_i2c_rx_int_delay_enable(struct thc_device *dev, bool enable)
{
u32 mask = THC_M_PRT_SPI_ICRRD_OPCODE_I2C_INTERVAL_EN;
u32 val = enable ? mask : 0; int ret;
if (!dev) return -EINVAL;
if (!dev->i2c_int_delay_us) return -EOPNOTSUPP;
ret = regmap_write_bits(dev->thc_regmap, THC_M_PRT_SPI_ICRRD_OPCODE_OFFSET, mask, val); if (ret) return ret;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.