Skip to content

Commit e584b13

Browse files
sunxinpengJiri Kosina
authored andcommitted
HID: intel-thc-hid: intel-thc: Add THC PIO operation APIs
THC PIO (programmed I/O) operations are very similar with general SPI/I2C read/write operation to access external slave device on the bus through internal FIFO. THC PIO operations are split into 4 steps: 1. prepare: configure hardware with correct opcode, slave address, and fill the PIO FIFO 2. start: set start bit to issue a bus send/receive 3. wait: wait for bus sending/receiving completion 4. complete: check send/receive data in FIFO and return Co-developed-by: Even Xu <[email protected]> Signed-off-by: Even Xu <[email protected]> Signed-off-by: Xinpeng Sun <[email protected]> Tested-by: Rui Zhang <[email protected]> Tested-by: Mark Pearson <[email protected]> Reviewed-by: Srinivas Pandruvada <[email protected]> Reviewed-by: Mark Pearson <[email protected]> Tested-by: Aaron Ma <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 050427e commit e584b13

File tree

3 files changed

+292
-0
lines changed

3 files changed

+292
-0
lines changed

drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.c

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
/* SPDX-License-Identifier: GPL-2.0 */
22
/* Copyright (c) 2024 Intel Corporation */
33

4+
#include <linux/bitfield.h>
45
#include <linux/regmap.h>
56

67
#include "intel-thc-dev.h"
8+
#include "intel-thc-hw.h"
79

810
static int thc_regmap_read(void *context, unsigned int reg,
911
unsigned int *val)
@@ -208,10 +210,253 @@ struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr)
208210

209211
thc_clear_state(thc_dev);
210212

213+
mutex_init(&thc_dev->thc_bus_lock);
214+
211215
return thc_dev;
212216
}
213217
EXPORT_SYMBOL_NS_GPL(thc_dev_init, "INTEL_THC");
214218

219+
static int prepare_pio(const struct thc_device *dev, const u8 pio_op,
220+
const u32 address, const u32 size)
221+
{
222+
u32 sts, ctrl, addr, mask;
223+
224+
regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts);
225+
226+
/* Check if THC previous PIO still in progress */
227+
if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP) {
228+
dev_err_once(dev->dev, "THC PIO is still busy!\n");
229+
return -EBUSY;
230+
}
231+
232+
/* Clear error bit and complete bit in state register */
233+
sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR |
234+
THC_M_PRT_SW_SEQ_STS_TSSDONE;
235+
regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts);
236+
237+
/* Set PIO data size, opcode and interrupt capability */
238+
ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, size) |
239+
FIELD_PREP(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD, pio_op);
240+
if (dev->pio_int_supported)
241+
ctrl |= THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE;
242+
243+
mask = THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC |
244+
THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CMD |
245+
THC_M_PRT_SW_SEQ_CNTRL_THC_SS_CD_IE;
246+
regmap_write_bits(dev->thc_regmap,
247+
THC_M_PRT_SW_SEQ_CNTRL_OFFSET, mask, ctrl);
248+
249+
/* Set PIO target address */
250+
addr = FIELD_PREP(THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR, address);
251+
mask = THC_M_PRT_SW_SEQ_DATA0_ADDR_THC_SW_SEQ_DATA0_ADDR;
252+
regmap_write_bits(dev->thc_regmap,
253+
THC_M_PRT_SW_SEQ_DATA0_ADDR_OFFSET, mask, addr);
254+
return 0;
255+
}
256+
257+
static void pio_start(const struct thc_device *dev,
258+
u32 size_in_bytes, const u32 *buffer)
259+
{
260+
if (size_in_bytes && buffer)
261+
regmap_bulk_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET,
262+
buffer, size_in_bytes / sizeof(u32));
263+
264+
/* Enable Start bit */
265+
regmap_write_bits(dev->thc_regmap,
266+
THC_M_PRT_SW_SEQ_CNTRL_OFFSET,
267+
THC_M_PRT_SW_SEQ_CNTRL_TSSGO,
268+
THC_M_PRT_SW_SEQ_CNTRL_TSSGO);
269+
}
270+
271+
static int pio_complete(const struct thc_device *dev,
272+
u32 *buffer, u32 *size)
273+
{
274+
u32 sts, ctrl;
275+
276+
regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, &sts);
277+
if (sts & THC_M_PRT_SW_SEQ_STS_THC_SS_ERR) {
278+
dev_err_once(dev->dev, "PIO operation error\n");
279+
return -EBUSY;
280+
}
281+
282+
if (buffer && size) {
283+
regmap_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_CNTRL_OFFSET, &ctrl);
284+
*size = FIELD_GET(THC_M_PRT_SW_SEQ_CNTRL_THC_SS_BC, ctrl);
285+
286+
regmap_bulk_read(dev->thc_regmap, THC_M_PRT_SW_SEQ_DATA1_OFFSET,
287+
buffer, *size / sizeof(u32));
288+
}
289+
290+
sts |= THC_M_PRT_SW_SEQ_STS_THC_SS_ERR | THC_M_PRT_SW_SEQ_STS_TSSDONE;
291+
regmap_write(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts);
292+
return 0;
293+
}
294+
295+
static int pio_wait(const struct thc_device *dev)
296+
{
297+
u32 sts = 0;
298+
int ret;
299+
300+
ret = regmap_read_poll_timeout(dev->thc_regmap, THC_M_PRT_SW_SEQ_STS_OFFSET, sts,
301+
!(sts & THC_M_PRT_SW_SEQ_STS_THC_SS_CIP ||
302+
!(sts & THC_M_PRT_SW_SEQ_STS_TSSDONE)),
303+
THC_REGMAP_POLLING_INTERVAL_US, THC_PIO_DONE_TIMEOUT_US);
304+
if (ret)
305+
dev_err_once(dev->dev, "Timeout while polling PIO operation done\n");
306+
307+
return ret;
308+
}
309+
310+
/**
311+
* thc_tic_pio_read - Read data from touch device by PIO
312+
*
313+
* @dev: The pointer of THC private device context
314+
* @address: Slave address for the PIO operation
315+
* @size: Expected read data size
316+
* @actual_size: The pointer of the actual data size read from touch device
317+
* @buffer: The pointer of data buffer to store the data read from touch device
318+
*
319+
* Return: 0 on success, other error codes on failed.
320+
*/
321+
int thc_tic_pio_read(struct thc_device *dev, const u32 address,
322+
const u32 size, u32 *actual_size, u32 *buffer)
323+
{
324+
u8 opcode;
325+
int ret;
326+
327+
if (size <= 0 || !actual_size || !buffer) {
328+
dev_err(dev->dev, "Invalid input parameters, size %u, actual_size %p, buffer %p\n",
329+
size, actual_size, buffer);
330+
return -EINVAL;
331+
}
332+
333+
if (mutex_lock_interruptible(&dev->thc_bus_lock))
334+
return -EINTR;
335+
336+
opcode = (dev->port_type == THC_PORT_TYPE_SPI) ?
337+
THC_PIO_OP_SPI_TIC_READ : THC_PIO_OP_I2C_TIC_READ;
338+
339+
ret = prepare_pio(dev, opcode, address, size);
340+
if (ret < 0)
341+
goto end;
342+
343+
pio_start(dev, 0, NULL);
344+
345+
ret = pio_wait(dev);
346+
if (ret < 0)
347+
goto end;
348+
349+
ret = pio_complete(dev, buffer, actual_size);
350+
351+
end:
352+
mutex_unlock(&dev->thc_bus_lock);
353+
return ret;
354+
}
355+
EXPORT_SYMBOL_NS_GPL(thc_tic_pio_read, "INTEL_THC");
356+
357+
/**
358+
* thc_tic_pio_write - Write data to touch device by PIO
359+
*
360+
* @dev: The pointer of THC private device context
361+
* @address: Slave address for the PIO operation
362+
* @size: PIO write data size
363+
* @buffer: The pointer of the write data buffer
364+
*
365+
* Return: 0 on success, other error codes on failed.
366+
*/
367+
int thc_tic_pio_write(struct thc_device *dev, const u32 address,
368+
const u32 size, const u32 *buffer)
369+
{
370+
u8 opcode;
371+
int ret;
372+
373+
if (size <= 0 || !buffer) {
374+
dev_err(dev->dev, "Invalid input parameters, size %u, buffer %p\n",
375+
size, buffer);
376+
return -EINVAL;
377+
}
378+
379+
if (mutex_lock_interruptible(&dev->thc_bus_lock))
380+
return -EINTR;
381+
382+
opcode = (dev->port_type == THC_PORT_TYPE_SPI) ?
383+
THC_PIO_OP_SPI_TIC_WRITE : THC_PIO_OP_I2C_TIC_WRITE;
384+
385+
ret = prepare_pio(dev, opcode, address, size);
386+
if (ret < 0)
387+
goto end;
388+
389+
pio_start(dev, size, buffer);
390+
391+
ret = pio_wait(dev);
392+
if (ret < 0)
393+
goto end;
394+
395+
ret = pio_complete(dev, NULL, NULL);
396+
397+
end:
398+
mutex_unlock(&dev->thc_bus_lock);
399+
return ret;
400+
}
401+
EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write, "INTEL_THC");
402+
403+
/**
404+
* thc_tic_pio_write_and_read - Write data followed by read data by PIO
405+
*
406+
* @dev: The pointer of THC private device context
407+
* @address: Slave address for the PIO operation
408+
* @write_size: PIO write data size
409+
* @write_buffer: The pointer of the write data buffer
410+
* @read_size: Expected PIO read data size
411+
* @actual_size: The pointer of the actual read data size
412+
* @read_buffer: The pointer of PIO read data buffer
413+
*
414+
* Return: 0 on success, other error codes on failed.
415+
*/
416+
int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address,
417+
const u32 write_size, const u32 *write_buffer,
418+
const u32 read_size, u32 *actual_size, u32 *read_buffer)
419+
{
420+
u32 i2c_ctrl, mask;
421+
int ret;
422+
423+
if (dev->port_type == THC_PORT_TYPE_SPI) {
424+
dev_err(dev->dev, "SPI port type doesn't support pio write and read!");
425+
return -EINVAL;
426+
}
427+
428+
if (mutex_lock_interruptible(&dev->thc_bus_lock))
429+
return -EINTR;
430+
431+
/* Config i2c PIO write and read sequence */
432+
i2c_ctrl = FIELD_PREP(THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC, write_size);
433+
mask = THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_PIO_I2C_WBC;
434+
435+
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET,
436+
mask, i2c_ctrl);
437+
438+
regmap_write_bits(dev->thc_regmap, THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_OFFSET,
439+
THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN,
440+
THC_M_PRT_SW_SEQ_I2C_WR_CNTRL_THC_I2C_RW_PIO_EN);
441+
442+
ret = prepare_pio(dev, THC_PIO_OP_I2C_TIC_WRITE_AND_READ, address, read_size);
443+
if (ret < 0)
444+
goto end;
445+
446+
pio_start(dev, write_size, write_buffer);
447+
448+
ret = pio_wait(dev);
449+
if (ret < 0)
450+
goto end;
451+
452+
ret = pio_complete(dev, read_buffer, actual_size);
453+
454+
end:
455+
mutex_unlock(&dev->thc_bus_lock);
456+
return ret;
457+
}
458+
EXPORT_SYMBOL_NS_GPL(thc_tic_pio_write_and_read, "INTEL_THC");
459+
215460
MODULE_AUTHOR("Xinpeng Sun <[email protected]>");
216461
MODULE_AUTHOR("Even Xu <[email protected]>");
217462

drivers/hid/intel-thc-hid/intel-thc/intel-thc-dev.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,45 @@
55
#define _INTEL_THC_DEV_H_
66

77
#include <linux/cdev.h>
8+
#include <linux/mutex.h>
89

910
#define THC_REGMAP_COMMON_OFFSET 0x10
1011
#define THC_REGMAP_MMIO_OFFSET 0x1000
1112

13+
/*
14+
* THC Port type
15+
* @THC_PORT_TYPE_SPI: This port is used for HIDSPI
16+
* @THC_PORT_TYPE_I2C: This port is used for HIDI2C
17+
*/
18+
enum thc_port_type {
19+
THC_PORT_TYPE_SPI = 0,
20+
THC_PORT_TYPE_I2C = 1,
21+
};
22+
1223
/**
1324
* struct thc_device - THC private device struct
1425
* @thc_regmap: MMIO regmap structure for accessing THC registers
1526
* @mmio_addr: MMIO registers address
27+
* @thc_bus_lock: mutex locker for THC config
28+
* @port_type: port type of THC port instance
29+
* @pio_int_supported: PIO interrupt supported flag
1630
*/
1731
struct thc_device {
1832
struct device *dev;
1933
struct regmap *thc_regmap;
2034
void __iomem *mmio_addr;
35+
struct mutex thc_bus_lock;
36+
enum thc_port_type port_type;
37+
bool pio_int_supported;
2138
};
2239

2340
struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr);
41+
int thc_tic_pio_read(struct thc_device *dev, const u32 address,
42+
const u32 size, u32 *actual_size, u32 *buffer);
43+
int thc_tic_pio_write(struct thc_device *dev, const u32 address,
44+
const u32 size, const u32 *buffer);
45+
int thc_tic_pio_write_and_read(struct thc_device *dev, const u32 address,
46+
const u32 write_size, const u32 *write_buffer,
47+
const u32 read_size, u32 *actual_size, u32 *read_buffer);
2448

2549
#endif /* _INTEL_THC_DEV_H_ */

drivers/hid/intel-thc-hid/intel-thc/intel-thc-hw.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,4 +636,27 @@
636636

637637
#define THC_M_PRT_SW_DMA_PRD_TABLE_LEN_THC_M_PRT_SW_DMA_PRD_TABLE_LEN GENMASK(23, 0)
638638

639+
#define THC_REGMAP_POLLING_INTERVAL_US 10 /* 10us */
640+
#define THC_PIO_DONE_TIMEOUT_US USEC_PER_SEC /* 1s */
641+
642+
/*
643+
* THC PIO opcode default value
644+
* @THC_PIO_OP_SPI_TIC_READ: THC opcode for SPI PIO read
645+
* @THC_PIO_OP_SPI_TIC_WRITE: THC opcode for SPI PIO write
646+
* @THC_PIO_OP_I2C_SUBSYSTEM_READ: THC opcode for read I2C subsystem registers
647+
* @THC_PIO_OP_I2C_SUBSYSTEM_WRITE: THC opcode for write I2C subsystem registers
648+
* @THC_PIO_OP_I2C_TIC_READ: THC opcode for read I2C device
649+
* @THC_PIO_OP_I2C_TIC_WRITE: THC opcode for write I2C device
650+
* @THC_PIO_OP_I2C_TIC_WRITE_AND_READ: THC opcode for write followed by read I2C device
651+
*/
652+
enum thc_pio_opcode {
653+
THC_PIO_OP_SPI_TIC_READ = 0x4,
654+
THC_PIO_OP_SPI_TIC_WRITE = 0x6,
655+
THC_PIO_OP_I2C_SUBSYSTEM_READ = 0x12,
656+
THC_PIO_OP_I2C_SUBSYSTEM_WRITE = 0x13,
657+
THC_PIO_OP_I2C_TIC_READ = 0x14,
658+
THC_PIO_OP_I2C_TIC_WRITE = 0x18,
659+
THC_PIO_OP_I2C_TIC_WRITE_AND_READ = 0x1C,
660+
};
661+
639662
#endif /* _INTEL_THC_HW_H_ */

0 commit comments

Comments
 (0)