summaryrefslogtreecommitdiffstats
path: root/drivers/i3c/master/mipi-i3c-hci/hci.h
blob: ee73f6e6756a126586f98cd3a06d8d70074ad022 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/* SPDX-License-Identifier: BSD-3-Clause */
/*
 * Copyright (c) 2020, MIPI Alliance, Inc.
 *
 * Author: Nicolas Pitre <npitre@baylibre.com>
 *
 * Common HCI stuff
 */

#ifndef HCI_H
#define HCI_H

#include <linux/io.h>
#include <linux/jiffies.h>

/* 32-bit word aware bit and mask macros */
#define W0_MASK(h, l)  GENMASK((h) - 0,  (l) - 0)
#define W1_MASK(h, l)  GENMASK((h) - 32, (l) - 32)
#define W2_MASK(h, l)  GENMASK((h) - 64, (l) - 64)
#define W3_MASK(h, l)  GENMASK((h) - 96, (l) - 96)

/* Same for single bit macros (trailing _ to align with W*_MASK width) */
#define W0_BIT_(x)  BIT((x) - 0)
#define W1_BIT_(x)  BIT((x) - 32)
#define W2_BIT_(x)  BIT((x) - 64)
#define W3_BIT_(x)  BIT((x) - 96)

#define reg_read(r)		readl(hci->base_regs + (r))
#define reg_write(r, v)		writel(v, hci->base_regs + (r))
#define reg_set(r, v)		reg_write(r, reg_read(r) | (v))
#define reg_clear(r, v)		reg_write(r, reg_read(r) & ~(v))

/* helper macro for HCI version check */
#define hci_version_at_least(hci, maj, min) \
	((hci)->version_major > (maj) || \
	((hci)->version_major == (maj) && (hci)->version_minor >= (min)))

struct hci_cmd_ops;

struct dat_words {
	u32 w0;
	u32 w1;
};

/* Our main structure */
struct i3c_hci {
	struct i3c_master_controller master;
	void __iomem *base_regs;
	void __iomem *DAT_regs;
	void __iomem *DCT_regs;
	void __iomem *RHS_regs;
	void __iomem *PIO_regs;
	void __iomem *EXTCAPS_regs;
	void __iomem *AUTOCMD_regs;
	void __iomem *DEBUG_regs;
	const struct hci_io_ops *io;
	void *io_data;
	const struct hci_cmd_ops *cmd;
	spinlock_t lock;
	struct mutex control_mutex;
	atomic_t next_cmd_tid;
	bool irq_inactive;
	bool enqueue_blocked;
	bool recovery_needed;
	bool hj_init_done;
	wait_queue_head_t enqueue_wait_queue;
	u32 caps;
	unsigned int quirks;
	unsigned int DAT_entries;
	unsigned int DAT_entry_size;
	void *DAT_data;
	struct dat_words *DAT;
	struct i3c_dev_desc **ibi_devs;
	unsigned int DCT_entries;
	unsigned int DCT_entry_size;
	u8 version_major;
	u8 version_minor;
	u8 revision;
	u8 dyn_addr;
	u32 vendor_mipi_id;
	u32 vendor_version_id;
	u32 vendor_product_id;
	void *vendor_data;
};

/*
 * Structure to represent a master initiated transfer.
 * The rnw, data and data_len fields must be initialized before calling any
 * hci->cmd->*() method. The cmd method will initialize cmd_desc[] and
 * possibly modify (clear) the data field. Then xfer->cmd_desc[0] can
 * be augmented with CMD_0_ROC and/or CMD_0_TOC.
 * The completion field needs to be initialized before queueing with
 * hci->io->queue_xfer(), and requires CMD_0_ROC to be set.
 */
struct hci_xfer {
	u32 cmd_desc[4];
	u32 response;
	bool rnw;
	bool started;
	void *data;
	unsigned int data_len;
	unsigned int cmd_tid;
	struct completion *completion;
	unsigned long timeout;
	unsigned long start_jiffies;
	union {
		struct {
			/* PIO specific */
			struct hci_xfer *next_xfer;
			struct hci_xfer *next_data;
			struct hci_xfer *next_resp;
			unsigned int data_left;
			u32 data_word_before_partial;
		};
		struct {
			/* DMA specific */
			struct i3c_dma *dma;
			struct hci_xfer *final_xfer;
			int ring_number;
			int ring_entry;
			int xfer_list_pos;
		};
	};
};

static inline struct hci_xfer *hci_alloc_xfer(unsigned int n)
{
	return kzalloc_objs(struct hci_xfer, n);
}

static inline void hci_free_xfer(struct hci_xfer *xfer, unsigned int n)
{
	kfree(xfer);
}

static inline void hci_start_xfer(struct hci_xfer *xfer)
{
	if (!xfer->started) {
		xfer->started = true;
		xfer->start_jiffies = jiffies;
	}
}

/* This abstracts PIO vs DMA operations */
struct hci_io_ops {
	bool (*irq_handler)(struct i3c_hci *hci);
	int (*queue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
	bool (*dequeue_xfer)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
	int (*handle_error)(struct i3c_hci *hci, struct hci_xfer *xfer, int n);
	int (*request_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev,
			   const struct i3c_ibi_setup *req);
	void (*free_ibi)(struct i3c_hci *hci, struct i3c_dev_desc *dev);
	void (*recycle_ibi_slot)(struct i3c_hci *hci, struct i3c_dev_desc *dev,
				struct i3c_ibi_slot *slot);
	int (*init)(struct i3c_hci *hci);
	void (*cleanup)(struct i3c_hci *hci);
	void (*suspend)(struct i3c_hci *hci);
	void (*resume)(struct i3c_hci *hci);
};

extern const struct hci_io_ops mipi_i3c_hci_pio;
extern const struct hci_io_ops mipi_i3c_hci_dma;

/* Our per device master private data */
struct i3c_hci_dev_data {
	int dat_idx;
	void *ibi_data;
};

/* list of quirks */
#define HCI_QUIRK_RAW_CCC	BIT(1)	/* CCC framing must be explicit */
#define HCI_QUIRK_PIO_MODE	BIT(2)  /* Set PIO mode for AMD platforms */
#define HCI_QUIRK_OD_PP_TIMING		BIT(3)  /* Set OD and PP timings for AMD platforms */
#define HCI_QUIRK_RESP_BUF_THLD		BIT(4)  /* Set resp buf thld to 0 for AMD platforms */
#define HCI_QUIRK_RPM_ALLOWED		BIT(5)  /* Runtime PM allowed */
#define HCI_QUIRK_RPM_IBI_ALLOWED	BIT(6)  /* IBI and Hot-Join allowed while runtime suspended */
#define HCI_QUIRK_RPM_PARENT_MANAGED	BIT(7)  /* Runtime PM managed by parent device */
#define HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET	BIT(8)  /* Do PIO queue SW resets after DMA abort */
#define HCI_QUIRK_DMA_REQUIRES_HC_ABORT		BIT(9)  /* Use HC_CONTROL ABORT to abort DMA */

/* global functions */
void mipi_i3c_hci_resume(struct i3c_hci *hci);
void mipi_i3c_hci_abort(struct i3c_hci *hci);
void mipi_i3c_hci_pio_reset(struct i3c_hci *hci);
void mipi_i3c_hci_pio_reset_all_queues(struct i3c_hci *hci);
void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci);
void amd_set_od_pp_timing(struct i3c_hci *hci);
void amd_set_resp_buf_thld(struct i3c_hci *hci);
void i3c_hci_sync_irq_inactive(struct i3c_hci *hci);
int i3c_hci_process_xfer(struct i3c_hci *hci, struct hci_xfer *xfer, int n);

struct device *i3c_hci_sysdev(struct device *dev);

#define DEFAULT_AUTOSUSPEND_DELAY_MS 1000

int i3c_hci_rpm_suspend(struct device *dev);
int i3c_hci_rpm_resume(struct device *dev);

int i3c_hci_reset_and_restore(struct i3c_hci *hci);

#endif