summaryrefslogtreecommitdiff
path: root/drivers/edgetpu/edgetpu-mailbox.h
blob: c4b131841c44d20f29db05865d7aa33720e5d72b (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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Utility functions of mailbox protocol for Edge TPU ML accelerator.
 *
 * Copyright (C) 2019 Google, Inc.
 */
#ifndef __EDGETPU_MAILBOX_H__
#define __EDGETPU_MAILBOX_H__

#include <linux/compiler.h>
#include <linux/irqreturn.h>
#include <linux/spinlock.h>
#include <linux/types.h>

#include "edgetpu-internal.h"
#include "edgetpu.h"

#define CIRCULAR_QUEUE_WRAP_BIT (1 << 10)
#define CIRCULAR_QUEUE_INDEX_MASK (CIRCULAR_QUEUE_WRAP_BIT - 1)
#define CIRCULAR_QUEUE_VALID_MASK                                              \
	(CIRCULAR_QUEUE_INDEX_MASK | CIRCULAR_QUEUE_WRAP_BIT)
#define CIRCULAR_QUEUE_WRAPPED(idx) ((idx) & CIRCULAR_QUEUE_WRAP_BIT)
#define CIRCULAR_QUEUE_REAL_INDEX(idx) ((idx) & CIRCULAR_QUEUE_INDEX_MASK)

/* CMD_QUEUE_SIZE register is 10 bits wide */
#define MAX_QUEUE_SIZE (CIRCULAR_QUEUE_WRAP_BIT - 1)

/* the index of mailbox for kernel control interface */
#define KERNEL_MAILBOX_INDEX 0

/* Size of CSRs start from cmd_queue_csr_base can be mmap-ed to userspace. */
#define USERSPACE_CSR_SIZE 0x1000ul

/* Mailbox ID to indicate external mailboxes */
#define EDGETPU_MAILBOX_ID_USE_ASSOC -1

struct edgetpu_device_group;

struct edgetpu_mailbox {
	uint mailbox_id;
	struct edgetpu_dev *etdev;
	/* base offset for CSRs in struct edgetpu_mailbox_context_csr */
	u32 context_csr_base;
	/* base offset for CSRs in struct edgetpu_mailbox_cmd_queue_csr */
	u32 cmd_queue_csr_base;
	/* base offset for CSRs in struct edgetpu_mailbox_resp_queue_csr */
	u32 resp_queue_csr_base;

	/*
	 * Queue-related fields, all of them are in units of number of elements.
	 */

	u32 cmd_queue_size; /* size of cmd queue */
	u32 cmd_queue_tail; /* offset within the cmd queue */
	u32 resp_queue_size; /* size of resp queue */
	u32 resp_queue_head; /* offset within the resp queue */

	/* IRQ handler */
	void (*handle_irq)(struct edgetpu_mailbox *mailbox);

	/*
	 * Internal data. It's edgetpu_kci* for KCI mailbox,
	 * edgetpu_device_group* for VII mailboxes.
	 */
	union {
		struct edgetpu_kci *kci;
		struct edgetpu_device_group *group;
	} internal;
};

typedef struct edgetpu_coherent_mem edgetpu_queue_mem;

struct edgetpu_vii {
	/*
	 * The mailbox this VII uses, can be NULL when uninitialized or mailbox
	 * detached.
	 */
	struct edgetpu_mailbox *mailbox;
	struct edgetpu_dev *etdev;
	edgetpu_queue_mem cmd_queue_mem;
	edgetpu_queue_mem resp_queue_mem;
};

/* Structure to hold info about mailbox and its queues. */
struct edgetpu_mailbox_descriptor {
	struct edgetpu_mailbox *mailbox;
	edgetpu_queue_mem cmd_queue_mem;
	edgetpu_queue_mem resp_queue_mem;
};

/* Structure to hold multiple external mailboxes allocated for a device group. */
struct edgetpu_external_mailbox {
	/* Number of external mailboxes allocated for a device group. */
	int count;
	/* Leader of device group. */
	struct edgetpu_dev *etdev;
	/* Array of external mailboxes info with length @count. */
	struct edgetpu_mailbox_descriptor *descriptors;
	/* Mailbox attribute for allocated external mailboxes. */
	struct edgetpu_mailbox_attr attr;
};

/* Structure used for requesting to allocate external mailboxes. */
struct edgetpu_external_mailbox_req {
	uint start; /* starting index of external mailbox in mailbox_manager */
	uint end; /* end index of external mailbox in mailbox_manager */
	/* number of mailboxes to be allocated, should be less or equal to (end - start + 1) */
	uint count;
	struct edgetpu_mailbox_attr attr; /* mailbox attribute for allocation */
};

/*
 * Structure for recording the driver state vs FW state.
 *
 * Example usage:
 *   @state is a bit mask that denotes each mailbox to which an "OPEN_DEVICE"
 *   KCI has been sent.
 *   @fw_state is the bit mask of mailbox IDs for which the FW has received the
 *   "OPEN_DEVICE" KCI.
 *   In usual cases @state always equals @fw_state. But when the FW is reloaded,
 *   @fw_state is reset to zero, then this structure can be used to know the FW
 *   state is out-of-sync and need further actions.
 */
struct edgetpu_handshake {
	struct mutex lock;
	/* fields protected by @lock */
	u32 state;
	u32 fw_state;
};

typedef u32 (*get_csr_base_t)(uint index);

struct edgetpu_mailbox_manager {
	struct edgetpu_dev *etdev;
	/* total number of mailboxes that edgetpu device could provide */
	u8 num_mailbox;
	/* indices reserved for VII, the range is [from, to) */
	u8 vii_index_from, vii_index_to;
	/* indices reserved for P2P, the range is [from, to) */
	u8 p2p_index_from, p2p_index_to;
	/* indices reserved for external mailboxes */
	u8 ext_index_from, ext_index_to;
	rwlock_t mailboxes_lock;	/* protects mailboxes */
	struct edgetpu_mailbox **mailboxes;
	/* converts index (0 ~ num_mailbox - 1) of mailbox to CSR offset */
	get_csr_base_t get_context_csr_base;
	get_csr_base_t get_cmd_queue_csr_base;
	get_csr_base_t get_resp_queue_csr_base;
	struct edgetpu_handshake open_devices;
};

/* the structure to configure a mailbox manager */
struct edgetpu_mailbox_manager_desc {
	u8 num_mailbox;
	u8 num_vii_mailbox;
	u8 num_p2p_mailbox;
	u8 num_ext_mailbox;
	get_csr_base_t get_context_csr_base;
	get_csr_base_t get_cmd_queue_csr_base;
	get_csr_base_t get_resp_queue_csr_base;
};

/* Mailbox CSRs. The order and size are exactly the same as RTL defined. */

/* CSRs that can be accessed by AP kernel only, don't mmap them to userspace */
struct edgetpu_mailbox_context_csr {
	u32 context_enable;
	u32 priority;
	u32 cmd_queue_doorbell_enable;
	/* doorbell will be triggered automatically on cmd_queue_tail updated */
	u32 cmd_queue_tail_doorbell_enable;
	u32 cmd_queue_doorbell_clear;
	u32 cmd_queue_address_low;
	u32 cmd_queue_address_high;
	u32 cmd_queue_size;
	u32 resp_queue_doorbell_enable;
	u32 resp_queue_tail_doorbell_enable;
	u32 resp_queue_address_low;
	u32 resp_queue_address_high;
	u32 resp_queue_size;
	u32 config_spare_0;
	u32 config_spare_1;
	u32 config_spare_2;
	u32 config_spare_3;
} __packed;

/* CSRs that can be accessed by AP runtime */

struct edgetpu_mailbox_cmd_queue_csr {
	u32 doorbell_set;
	u32 doorbell_status;
	u32 head;
	u32 tail;
	u32 config;
	u32 error_status;
} __packed;

struct edgetpu_mailbox_resp_queue_csr {
	u32 doorbell_set;
	u32 doorbell_clear;
	u32 doorbell_status;
	u32 head;
	u32 tail;
	u32 config;
	u32 error_status;
} __packed;

/* Mailbox APIs */

/* to specify the operation is toward cmd or resp queue */
enum mailbox_queue_type {
	MAILBOX_CMD_QUEUE,
	MAILBOX_RESP_QUEUE
};

/*
 * Allocates the mailbox manager.
 *
 * Allocations are device-managed so no release function is needed to free the
 * manager.
 */
struct edgetpu_mailbox_manager *
edgetpu_mailbox_create_mgr(struct edgetpu_dev *etdev,
			   const struct edgetpu_mailbox_manager_desc *desc);

/* interrupt handler */
irqreturn_t edgetpu_mailbox_handle_irq(struct edgetpu_mailbox_manager *mgr);

/*
 * Removes the mailbox previously requested from a mailbox manager.
 *
 * This function doesn't change the state of mailbox enable/disable.
 */
int edgetpu_mailbox_remove(struct edgetpu_mailbox_manager *mgr, struct edgetpu_mailbox *mailbox);
/* Removes and disables all the mailboxes previously requested. */
void edgetpu_mailbox_remove_all(struct edgetpu_mailbox_manager *mgr);

/* configure mailbox */

/* set cmd/resp_queue's address and size */
int edgetpu_mailbox_set_queue(struct edgetpu_mailbox *mailbox,
			      enum mailbox_queue_type type, u64 addr, u32 size);
void edgetpu_mailbox_set_priority(struct edgetpu_mailbox *mailbox,
				  u32 priority);

/* Reset mailbox queues, clear out any commands/responses left from before. */
void edgetpu_mailbox_reset(struct edgetpu_mailbox *mailbox);

/*
 * Clears any stale doorbell requests and enables the doorbell interrupts
 * at the mailbox level
 */
void edgetpu_mailbox_init_doorbells(struct edgetpu_mailbox *mailbox);

/* utility functions for KCI */

/* requests the mailbox for KCI */
struct edgetpu_mailbox *edgetpu_mailbox_kci(
		struct edgetpu_mailbox_manager *mgr);
void edgetpu_mailbox_inc_cmd_queue_tail(struct edgetpu_mailbox *mailbox,
					u32 inc);
void edgetpu_mailbox_inc_resp_queue_head(struct edgetpu_mailbox *mailbox,
					 u32 inc);

/* utility functions for VII */

/*
 * Request the mailbox with mailbox_id equals @id.
 * @id = 0 means there is no preference, @mgr will return a spare mailbox.
 *
 * Caller calls edgetpu_mailbox_enable() to enable the returned mailbox.
 *
 * -EBUSY is returned if the requested @id is used or there is no mailbox
 * available.
 */
struct edgetpu_mailbox *
edgetpu_mailbox_vii_add(struct edgetpu_mailbox_manager *mgr, uint id);

/*
 * Validates the mailbox attributes.
 * Returns 0 if valid, otherwise a negative errno.
 *
 * See the error cases of EDGETPU_CREATE_GROUP in edgetpu.h for when will @attr
 * be considered as invalid.
 */
int edgetpu_mailbox_validate_attr(const struct edgetpu_mailbox_attr *attr);
/*
 * Sets mailbox and allocates queues to @vii.
 *
 * @group is the device group that @vii will be associated with,
 * @group->mbox_attr is used to set the VII mailbox attributes.
 *
 * @group->mbox_attr must be checked by edgetpu_mailbox_validate_attr() before
 * calling this function.
 *
 * Returns 0 on success.
 */
int edgetpu_mailbox_init_vii(struct edgetpu_vii *vii,
			     struct edgetpu_device_group *group);
void edgetpu_mailbox_remove_vii(struct edgetpu_vii *vii);


/*
 * Reset VII mailboxes CSRs to valid values, needed after the device is power
 * gated.
 */
void edgetpu_mailbox_reset_vii(struct edgetpu_mailbox_manager *mgr);


/* For VII and P2P mailboxes to allocate/free queue memory */

int edgetpu_mailbox_alloc_queue(struct edgetpu_dev *etdev,
				struct edgetpu_mailbox *mailbox, u32 queue_size,
				u32 unit, enum mailbox_queue_type type,
				edgetpu_queue_mem *mem);
void edgetpu_mailbox_free_queue(struct edgetpu_dev *etdev,
				struct edgetpu_mailbox *mailbox,
				edgetpu_queue_mem *mem);

/*
 * Re-programs the CSRs of queue addresses, context, priority etc. to @group's
 * VII mailbox.
 *
 * Caller holds @group->lock and ensures @group has mailbox attached.
 */
void edgetpu_mailbox_reinit_vii(struct edgetpu_device_group *group);

/*
 * Re-configure VII mailbox queues which have an active client, re-using
 * existing buffers
 */
void edgetpu_mailbox_restore_active_vii_queues(struct edgetpu_dev *etdev);

/* utility functions for P2P */

int edgetpu_mailbox_p2p_batch(struct edgetpu_mailbox_manager *mgr, uint n,
			      uint skip_i, struct edgetpu_mailbox **mailboxes);

/*
 * If @mailbox_id is EDGETPU_MAILBOX_ID_USE_ASSOC, use @ext_mailbox_req to
 * allocate external mailboxes and activate the allocated mailboxes.
 * Otherwise, activate the external mailbox with id @mailbox_id.
 */
int edgetpu_mailbox_enable_ext(struct edgetpu_client *client, int mailbox_id,
			       struct edgetpu_external_mailbox_req *ext_mailbox_req);

/*
 * Notify firmware of an external mailboxes becoming inactive.
 */
int edgetpu_mailbox_disable_ext(struct edgetpu_client *client, int mailbox_id);

/*
 * Activates @mailbox_id, OPEN_DEVICE KCI will be sent.
 *
 * If @mailbox_id is known to be activated, KCI is not sent and this function
 * returns 0.
 *
 * Returns what edgetpu_kci_open_device() returned.
 * Caller ensures device is powered on.
 */
int edgetpu_mailbox_activate(struct edgetpu_dev *etdev, u32 mailbox_id, s16 vcid, bool first_open);
/*
 * Similar to edgetpu_mailbox_activate() but sends CLOSE_DEVICE KCI instead.
 */
void edgetpu_mailbox_deactivate(struct edgetpu_dev *etdev, u32 mailbox_id);
/* Sets @eh->fw_state to 0. */
void edgetpu_handshake_clear_fw_state(struct edgetpu_handshake *eh);
/*
 * Disables and frees any external mailboxes allocated for @group.
 *
 * Caller must hold @group->lock.
 */
void edgetpu_mailbox_external_disable_free_locked(struct edgetpu_device_group *group);

/* Utilities of circular queue operations */

/*
 * Returns the number of elements in a circular queue given its @head, @tail,
 * and @queue_size.
 */
static inline u32 circular_queue_count(u32 head, u32 tail, u32 queue_size)
{
	u32 ret;

	if (CIRCULAR_QUEUE_WRAPPED(tail) != CIRCULAR_QUEUE_WRAPPED(head))
		ret = queue_size - CIRCULAR_QUEUE_REAL_INDEX(head) +
		      CIRCULAR_QUEUE_REAL_INDEX(tail);
	else
		ret = tail - head;

	if (unlikely(ret > queue_size))
		return 0;

	return ret;
}

/* Increases @index of a circular queue by @inc. */
static inline u32 circular_queue_inc(u32 index, u32 inc, u32 queue_size)
{
	u32 new_index = CIRCULAR_QUEUE_REAL_INDEX(index) + inc;

	if (unlikely(new_index >= queue_size))
		return (index + inc - queue_size) ^ CIRCULAR_QUEUE_WRAP_BIT;
	else
		return index + inc;
}

/* Macros for accessing mailbox CSRs. */

/* Read mailbox register with no memory barrier / access ordering guarantee. */
#define EDGETPU_MAILBOX_READ(mailbox, base, type, field) \
	edgetpu_dev_read_32(mailbox->etdev, base + offsetof(type, field))

/*
 * Read mailbox register with memory barrier, ensuring the register read
 * completes prior to any following CPU reads by this thread.
 */
#define EDGETPU_MAILBOX_READ_SYNC(mailbox, base, type, field) \
	edgetpu_dev_read_32_sync(mailbox->etdev, base + offsetof(type, field))

#define EDGETPU_MAILBOX_CONTEXT_READ(mailbox, field) \
	EDGETPU_MAILBOX_READ(mailbox, mailbox->context_csr_base, \
			     struct edgetpu_mailbox_context_csr, field)

#define EDGETPU_MAILBOX_CMD_QUEUE_READ(mailbox, field) \
	EDGETPU_MAILBOX_READ(mailbox, mailbox->cmd_queue_csr_base, \
			     struct edgetpu_mailbox_cmd_queue_csr, field)

/* Read response queue register, no memory barrier / access ordering. */
#define EDGETPU_MAILBOX_RESP_QUEUE_READ(mailbox, field) \
	EDGETPU_MAILBOX_READ(mailbox, mailbox->resp_queue_csr_base, \
			     struct edgetpu_mailbox_resp_queue_csr, field)

/* Read response queue register with memory barrier. */
#define EDGETPU_MAILBOX_RESP_QUEUE_READ_SYNC(mailbox, field) \
	EDGETPU_MAILBOX_READ_SYNC(mailbox, mailbox->resp_queue_csr_base, \
			     struct edgetpu_mailbox_resp_queue_csr, field)

/* Write mailbox register with no memory barrier / access ordering guarantee. */
#define EDGETPU_MAILBOX_WRITE(mailbox, base, type, field, value) \
	edgetpu_dev_write_32(mailbox->etdev, base + offsetof(type, field), \
			     value)

/*
 * Write mailbox register with memory barrier, ensuring all CPU memory writes
 * by this thread complete prior to the register write.
 */
#define EDGETPU_MAILBOX_WRITE_SYNC(mailbox, base, type, field, value) \
	edgetpu_dev_write_32_sync(mailbox->etdev, \
				  base + offsetof(type, field), \
				  value)

/* Write context register with no memory barrier / access ordering. */
#define EDGETPU_MAILBOX_CONTEXT_WRITE(mailbox, field, value) \
	EDGETPU_MAILBOX_WRITE(mailbox, mailbox->context_csr_base, \
			      struct edgetpu_mailbox_context_csr, field, value)

/* Write context register with memory barrier. */
#define EDGETPU_MAILBOX_CONTEXT_WRITE_SYNC(mailbox, field, value) \
	EDGETPU_MAILBOX_WRITE_SYNC(mailbox, mailbox->context_csr_base, \
				   struct edgetpu_mailbox_context_csr, field, \
				   value)

/* Write command queue register with no memory barrier / access ordering. */
#define EDGETPU_MAILBOX_CMD_QUEUE_WRITE(mailbox, field, value) \
	EDGETPU_MAILBOX_WRITE(mailbox, mailbox->cmd_queue_csr_base, \
			      struct edgetpu_mailbox_cmd_queue_csr, field, \
			      value)

/* Write command queue register with memory barrier. */
#define EDGETPU_MAILBOX_CMD_QUEUE_WRITE_SYNC(mailbox, field, value) \
	EDGETPU_MAILBOX_WRITE_SYNC(mailbox, mailbox->cmd_queue_csr_base, \
				   struct edgetpu_mailbox_cmd_queue_csr, \
				   field, value)

#define EDGETPU_MAILBOX_RESP_QUEUE_WRITE(mailbox, field, value) \
	EDGETPU_MAILBOX_WRITE(mailbox, mailbox->resp_queue_csr_base, \
			      struct edgetpu_mailbox_resp_queue_csr, field, \
			      value)

/* Enables a mailbox by setting CSR. */
static inline void edgetpu_mailbox_enable(struct edgetpu_mailbox *mailbox)
{
	EDGETPU_MAILBOX_CONTEXT_WRITE(mailbox, context_enable, 1);
}

/* Disables a mailbox by setting CSR. */
static inline void edgetpu_mailbox_disable(struct edgetpu_mailbox *mailbox)
{
	EDGETPU_MAILBOX_CONTEXT_WRITE(mailbox, context_enable, 0);
}

#endif /* __EDGETPU_MAILBOX_H__ */