Commit a688404b authored by Even Xu's avatar Even Xu Committed by Jiri Kosina
Browse files

HID: intel-thc-hid: intel-thc: Add THC DMA interfaces



As THC PIO only has 64 bytes FIFO length, THC DMAs are introduced to
send/receive large data packets.

THC has three types of DMA channels: Read DMA channel (RxDMA), Write DMA
channel (TxDMA) and Software DMA (SWDMA).

In addition to basic DMA functions, THC RxDMA also includes an auto
hardware sequence which can handle external touch device's interrupt
automatically without software involved. THC RxDMA channel usually is
used for handling touch input reports.

THC TxDMA is very similar with general IO TxDMA, and usually is used
for sending command/request to exteranl touch device.

THC SWDMA can perform read, write followed by read operation
according to different configurations. Unlike RxDMA triggered by bus
activity, SWDMA can be triggered by SW driver at any time, for example:
- Retrieving an input report without interrupt
- Sending command followed by reading response

THC DMA operation flow includes 4 steps:
1. Allocate DMA buffers
2. Configure opcode, fill PRD table with DMA buffers, enable DMA channel
3. Wait for completion, read out DMA buffers and update buffer pointers
4. Stop DMA and release DMA buffers

THC Hardware layer driver provides APIs for all above DMA Steps.

Co-developed-by: default avatarXinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: default avatarXinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: default avatarEven Xu <even.xu@intel.com>
Tested-by: default avatarRui Zhang <rui1.zhang@intel.com>
Tested-by: default avatarMark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: default avatarSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Reviewed-by: default avatarMark Pearson <mpearson-lenovo@squebb.ca>
Tested-by: default avatarAaron Ma <aaron.ma@canonical.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.com>
parent 86f5f4ab
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -7,5 +7,6 @@

obj-$(CONFIG_INTEL_THC_HID) += intel-thc.o
intel-thc-objs += intel-thc/intel-thc-dev.o
intel-thc-objs += intel-thc/intel-thc-dma.o

ccflags-y += -I $(src)/intel-thc
+8 −0
Original line number Diff line number Diff line
@@ -211,6 +211,14 @@ struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr)
	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;
}
+18 −0
Original line number Diff line number Diff line
@@ -6,6 +6,9 @@

#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
@@ -27,6 +30,12 @@ enum thc_port_type {
 * @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
 */
struct thc_device {
	struct device *dev;
@@ -35,6 +44,15 @@ struct thc_device {
	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;
};

struct thc_device *thc_dev_init(struct device *device, void __iomem *mem_addr);
+969 −0

File added.

Preview size limit exceeded, changes collapsed.

+146 −0
Original line number Diff line number Diff line
/* 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_ */