summaryrefslogtreecommitdiff
path: root/drivers/edgetpu/edgetpu-internal.h
blob: e8cbf9a58d828b92d137e3cc570dd2865c56fe05 (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
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Edge TPU driver common internal definitions.
 *
 * Copyright (C) 2019 Google, Inc.
 */
#ifndef __EDGETPU_INTERNAL_H__
#define __EDGETPU_INTERNAL_H__

#include <linux/printk.h>

#ifdef CONFIG_X86
#include <asm/pgtable_types.h>
#include <asm/set_memory.h>
#endif

#include <linux/atomic.h>
#include <linux/cdev.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/firmware.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/irqreturn.h>
#include <linux/mm_types.h>
#include <linux/mutex.h>
#include <linux/refcount.h>
#include <linux/scatterlist.h>
#include <linux/types.h>
#include <linux/workqueue.h>

#include "edgetpu.h"
#include "edgetpu-pm.h"
#include "edgetpu-thermal.h"
#include "edgetpu-usage-stats.h"

#define get_dev_for_logging(etdev)                                                                 \
	((etdev)->etiface && (etdev)->etiface->etcdev ? (etdev)->etiface->etcdev : (etdev)->dev)

#define etdev_err(etdev, fmt, ...) dev_err(get_dev_for_logging(etdev), fmt, ##__VA_ARGS__)
#define etdev_warn(etdev, fmt, ...)                                            \
	dev_warn(get_dev_for_logging(etdev), fmt, ##__VA_ARGS__)
#define etdev_info(etdev, fmt, ...)                                            \
	dev_info(get_dev_for_logging(etdev), fmt, ##__VA_ARGS__)
#define etdev_dbg(etdev, fmt, ...) dev_dbg(get_dev_for_logging(etdev), fmt, ##__VA_ARGS__)
#define etdev_err_ratelimited(etdev, fmt, ...)                                 \
	dev_err_ratelimited(get_dev_for_logging(etdev), fmt, ##__VA_ARGS__)
#define etdev_warn_ratelimited(etdev, fmt, ...)                                \
	dev_warn_ratelimited(get_dev_for_logging(etdev), fmt, ##__VA_ARGS__)
#define etdev_info_ratelimited(etdev, fmt, ...)                                \
	dev_info_ratelimited(get_dev_for_logging(etdev), fmt, ##__VA_ARGS__)
#define etdev_dbg_ratelimited(etdev, fmt, ...)                                 \
	dev_dbg_ratelimited(get_dev_for_logging(etdev), fmt, ##__VA_ARGS__)
#define etdev_warn_once(etdev, fmt, ...)                                       \
	dev_warn_once(get_dev_for_logging(etdev), fmt, ##__VA_ARGS__)

/* The number of TPU tiles in an edgetpu chip */
#define EDGETPU_NTILES	16

/*
 * Common-layer context IDs for non-secure TPU access, translated to chip-
 * specific values in the mmu driver.
 */
enum edgetpu_context_id {
	EDGETPU_CONTEXT_INVALID = -1,
	EDGETPU_CONTEXT_KCI = 0,	/* TPU firmware/kernel ID 0 */
	EDGETPU_CONTEXT_VII_BASE = 1,	/* groups IDs starts from 1 to (EDGETPU_CONTEXTS - 1) */
	/* A bit mask to mark the context is an IOMMU domain token */
	EDGETPU_CONTEXT_DOMAIN_TOKEN = 1 << 30,
};

typedef u64 tpu_addr_t;

struct edgetpu_coherent_mem {
	void *vaddr;		/* kernel VA, no allocation if NULL */
	dma_addr_t dma_addr;	/* DMA handle for downstream IOMMU, if any */
	tpu_addr_t tpu_addr;	/* DMA handle for TPU internal IOMMU, if any */
	u64 host_addr;		/* address mapped on host for debugging */
	u64 phys_addr;		/* physical address, if available */
	size_t size;
#ifdef CONFIG_X86
	bool is_set_uc;		/* memory has been marked uncached on X86 */
#endif
};

struct edgetpu_device_group;
struct edgetpu_p2p_csr_map;
struct edgetpu_remote_dram_map;
struct edgetpu_wakelock;
struct edgetpu_dev_iface;

#define EDGETPU_NUM_PERDIE_EVENTS	2
#define perdie_event_id_to_num(event_id)				      \
	(event_id - EDGETPU_PERDIE_EVENT_LOGS_AVAILABLE)

struct edgetpu_client {
	pid_t pid;
	pid_t tgid;
	/* Reference count */
	refcount_t count;
	/* protects group. */
	struct mutex group_lock;
	/*
	 * The virtual device group this client belongs to. Can be NULL if the
	 * client doesn't belong to any group.
	 */
	struct edgetpu_device_group *group;
	/*
	 * This client is the idx-th member of @group.
	 * It's meaningless if this client doesn't belong to a group.
	 */
	uint idx;
	/* the device opened by this client */
	struct edgetpu_dev *etdev;
	/* the interface from which this client was opened */
	struct edgetpu_dev_iface *etiface;
	/* Peer CSR dma addrs for this client, if in a group with P2P */
	dma_addr_t *p2p_csrs_dma_addrs;
	/* Peer DRAM dma addrs for this client, if has on-device DRAM */
	dma_addr_t *remote_drams_dma_addrs;
	/* Per-client request to keep device active */
	struct edgetpu_wakelock *wakelock;
	/* Bit field of registered per die events */
	u64 perdie_events;
};

/* Configurable parameters for an edgetpu interface */
struct edgetpu_iface_params {
	/*
	 * Interface-specific name.
	 * May be NULL for the default interface (etdev->dev_name will be used)
	 */
	const char *name;
};

/* edgetpu_dev#clients list entry. */
struct edgetpu_list_device_client {
	struct list_head list;
	struct edgetpu_client *client;
};

/* loop through etdev->clients (hold clients_lock prior). */
#define for_each_list_device_client(etdev, c)                                  \
	list_for_each_entry(c, &etdev->clients, list)

struct edgetpu_mapping;
struct edgetpu_mailbox_manager;
struct edgetpu_kci;
struct edgetpu_kci_response_element;
struct edgetpu_telemetry_ctx;
struct edgetpu_mempool;

typedef int(*edgetpu_debug_dump_handlers)(void *etdev, void *dump_setup);

#define EDGETPU_DEVICE_NAME_MAX	64

/* ioremapped resource */
struct edgetpu_mapped_resource {
	void __iomem *mem;	/* starting virtual address */
	phys_addr_t phys;	/* starting physical address */
	resource_size_t size;	/* size in bytes */
};

enum edgetpu_dev_state {
	ETDEV_STATE_NOFW = 0,	/* no firmware running on device. */
	ETDEV_STATE_GOOD = 1,	/* healthy firmware running. */
	ETDEV_STATE_FWLOADING = 2, /* firmware is getting loaded on device. */
	ETDEV_STATE_BAD = 3,	/* firmware/device is in unusable state. */
};

/* a mark to know whether we read valid versions from the firmware header */
#define EDGETPU_INVALID_KCI_VERSION (~0u)

struct edgetpu_dev {
	struct device *dev;	   /* platform/pci bus device */
	uint num_ifaces;		   /* Number of device interfaces */
	uint num_cores; /* Number of cores */
	/*
	 * Array of device interfaces
	 * First element is the default interface
	 */
	struct edgetpu_dev_iface *etiface;
	char dev_name[EDGETPU_DEVICE_NAME_MAX];
	struct edgetpu_mapped_resource regs; /* ioremapped CSRs */
	struct dentry *d_entry;    /* debugfs dir for this device */
	struct mutex state_lock;   /* protects state of this device */
	enum edgetpu_dev_state state;
	struct mutex groups_lock;
	/* fields protected by @groups_lock */

	struct list_head groups;
	uint n_groups;		   /* number of entries in @groups */
	bool group_join_lockout;   /* disable group join while reinit */
	u32 vcid_pool;		   /* bitmask of VCID to be allocated */

	/* end of fields protected by @groups_lock */

	struct mutex clients_lock; /* protects clients */
	struct list_head clients;
	void *mmu_cookie;	   /* mmu driver private data */
	void *dram_cookie;	   /* on-device DRAM private data */
	struct edgetpu_mailbox_manager *mailbox_manager;
	struct edgetpu_kci *kci;
	struct edgetpu_firmware *firmware; /* firmware management */
	struct edgetpu_telemetry_ctx *telemetry;
	struct edgetpu_thermal *thermal;
	struct edgetpu_usage_stats *usage_stats; /* usage stats private data */
	struct edgetpu_pm *pm;  /* Power management interface */
	/* Memory pool in instruction remap region */
	struct edgetpu_mempool *iremap_pool;
	int mcp_id;		/* multichip pkg id, or -1 for none */
	uint mcp_die_index;	/* physical die index w/in multichip pkg */
	u8 mcp_pkg_type;	/* multichip pkg type */
	struct edgetpu_sw_wdt *etdev_sw_wdt;	/* software watchdog */
	bool reset_needed;	/* error recovery requests full chip reset. */
	/* version read from the firmware binary file */
	struct edgetpu_fw_version fw_version;
	atomic_t job_count;	/* times joined to a device group */

	/* counts of error events */
	uint firmware_crash_count;
	uint watchdog_timeout_count;

	struct edgetpu_coherent_mem debug_dump_mem;	/* debug dump memory */
	/* debug dump handlers */
	edgetpu_debug_dump_handlers *debug_dump_handlers;
	struct work_struct debug_dump_work;

	struct mutex freq_lock;	/* protects below freq_* variables */
	uint32_t *freq_table;	/* Array to record reported frequencies by f/w */
	uint32_t freq_count;	/* Number of entries in freq_table */
};

struct edgetpu_dev_iface {
	struct cdev cdev; /* cdev char device structure */
	struct device *etcdev; /* edgetpu class char device */
	struct edgetpu_dev *etdev; /* Pointer to core device struct */
	dev_t devno; /* char device dev_t */
	const char *name; /* interface specific device name */
	struct dentry *d_entry; /* debugfs symlink if not default device name iface */
};

/* Firmware crash_type codes */
enum edgetpu_fw_crash_type {
	EDGETPU_FW_CRASH_ASSERT = 0,
	EDGETPU_FW_CRASH_DATA_ABORT = 1,
	EDGETPU_FW_CRASH_PREFETCH_ABORT = 2,
	EDGETPU_FW_CRASH_UNDEF_EXCEPT = 3,
	EDGETPU_FW_CRASH_UNRECOV_FAULT = 4,
};

extern const struct file_operations edgetpu_fops;

/* Status regs dump. */
struct edgetpu_dumpregs_range {
	u32 firstreg;
	u32 lastreg;
};
extern struct edgetpu_dumpregs_range edgetpu_chip_statusregs_ranges[];
extern int edgetpu_chip_statusregs_nranges;
extern struct edgetpu_dumpregs_range edgetpu_chip_tile_statusregs_ranges[];
extern int edgetpu_chip_tile_statusregs_nranges;

static inline const char *edgetpu_dma_dir_rw_s(enum dma_data_direction dir)
{
	static const char *tbl[4] = { "rw", "r", "w", "?" };

	return tbl[dir];
}

/* edgetpu device IO functions */

static inline u32 edgetpu_dev_read_32(struct edgetpu_dev *etdev,
				      uint reg_offset)
{
	return readl_relaxed(etdev->regs.mem + reg_offset);
}

/* Read 32-bit reg with memory barrier completing before following CPU reads. */
static inline u32 edgetpu_dev_read_32_sync(struct edgetpu_dev *etdev,
					   uint reg_offset)
{
	return readl(etdev->regs.mem + reg_offset);
}

static inline u64 edgetpu_dev_read_64(struct edgetpu_dev *etdev,
				      uint reg_offset)
{
	return readq_relaxed(etdev->regs.mem + reg_offset);
}

static inline void edgetpu_dev_write_32(struct edgetpu_dev *etdev,
					uint reg_offset, u32 value)
{
	writel_relaxed(value, etdev->regs.mem + reg_offset);
}

/* Write 32-bit reg with memory barrier completing CPU writes first. */
static inline void edgetpu_dev_write_32_sync(struct edgetpu_dev *etdev,
					     uint reg_offset, u32 value)
{
	writel(value, etdev->regs.mem + reg_offset);
}

static inline void edgetpu_dev_write_64(struct edgetpu_dev *etdev,
					uint reg_offset, u64 value)
{
	writeq_relaxed(value, etdev->regs.mem + reg_offset);
}

static inline void
edgetpu_x86_coherent_mem_init(struct edgetpu_coherent_mem *mem)
{
#ifdef CONFIG_X86
	mem->is_set_uc = false;
#endif
}

static inline void
edgetpu_x86_coherent_mem_set_uc(struct edgetpu_coherent_mem *mem)
{
#ifdef CONFIG_X86
	if (!mem->is_set_uc) {
		set_memory_uc((unsigned long)mem->vaddr, mem->size >>
			      PAGE_SHIFT);
		mem->is_set_uc = true;
	}
#endif
}

static inline void
edgetpu_x86_coherent_mem_set_wb(struct edgetpu_coherent_mem *mem)
{
#ifdef CONFIG_X86
	if (mem->is_set_uc) {
		set_memory_wb((unsigned long)mem->vaddr, mem->size >>
			      PAGE_SHIFT);
		mem->is_set_uc = false;
	}
#endif
}

/*
 * Attempt to allocate memory from the dma coherent memory using dma_alloc.
 * Use this to allocate memory outside the instruction remap pool.
 */
int edgetpu_alloc_coherent(struct edgetpu_dev *etdev, size_t size,
			   struct edgetpu_coherent_mem *mem,
			   enum edgetpu_context_id context_id);
/*
 * Free memory allocated by the function above from the dma coherent memory.
 */
void edgetpu_free_coherent(struct edgetpu_dev *etdev,
			   struct edgetpu_coherent_mem *mem,
			   enum edgetpu_context_id context_id);

/* Checks if @file belongs to edgetpu driver */
bool is_edgetpu_file(struct file *file);

/* External drivers can hook up to edgetpu driver using these calls. */
int edgetpu_open(struct edgetpu_dev_iface *etiface, struct file *file);
long edgetpu_ioctl(struct file *file, uint cmd, ulong arg);

#if IS_ENABLED(CONFIG_EDGETPU_EXTERNAL_WRAPPER_CLASS)
extern bool edgetpu_is_external_wrapper_class_file(struct file *file);
#else
static inline bool edgetpu_is_external_wrapper_class_file(struct file *file)
{
	return false;
}
#endif

/* Handle firmware crash event */
void edgetpu_handle_firmware_crash(struct edgetpu_dev *etdev,
				   enum edgetpu_fw_crash_type crash_type);

/* Handle notification of job lockup from firmware */
void edgetpu_handle_job_lockup(struct edgetpu_dev *etdev, u16 vcid);

/* Bus (Platform/PCI) <-> Core API */

int __init edgetpu_init(void);
void __exit edgetpu_exit(void);
int edgetpu_device_add(struct edgetpu_dev *etdev,
		       const struct edgetpu_mapped_resource *regs,
		       const struct edgetpu_iface_params *iface_params,
		       uint num_ifaces);
void edgetpu_device_remove(struct edgetpu_dev *etdev);
/* Registers IRQ. */
int edgetpu_register_irq(struct edgetpu_dev *etdev, int irq);
/* Reverts edgetpu_register_irq */
void edgetpu_unregister_irq(struct edgetpu_dev *etdev, int irq);

/* Core -> Device FS API */

int __init edgetpu_fs_init(void);
void __exit edgetpu_fs_exit(void);
int edgetpu_fs_add(struct edgetpu_dev *etdev, const struct edgetpu_iface_params *etiparams,
		   int num_ifaces);

void edgetpu_fs_remove(struct edgetpu_dev *dev);
/* Get the top-level debugfs directory for the device class */
struct dentry *edgetpu_fs_debugfs_dir(void);

/* Core/Device/FS -> Chip API */

/* Chip-specific init/exit */
void edgetpu_chip_init(struct edgetpu_dev *etdev);
void edgetpu_chip_exit(struct edgetpu_dev *etdev);

/* IRQ handler */
irqreturn_t edgetpu_chip_irq_handler(int irq, void *arg);

/*
 * Called from core to chip layer when MMU is needed during device init.
 *
 * Returns 0 on success, otherwise -errno.
 */
int edgetpu_chip_setup_mmu(struct edgetpu_dev *etdev);

/*
 * Reverts edgetpu_chip_setup_mmu().
 * This is called during device removal.
 */
void edgetpu_chip_remove_mmu(struct edgetpu_dev *etdev);

/* Read TPU timestamp */
u64 edgetpu_chip_tpu_timestamp(struct edgetpu_dev *etdev);

/*
 * Handle chip-specific incoming requests from firmware over KCI
 * Note: This will get called from the system's work queue.
 * Code should not block for extended periods of time
 */
void edgetpu_chip_handle_reverse_kci(struct edgetpu_dev *etdev,
				     struct edgetpu_kci_response_element *resp);

/* Device -> Core API */

/* Add current thread as new TPU client */
struct edgetpu_client *
edgetpu_client_add(struct edgetpu_dev_iface *etiface);

/* Remove TPU client */
void edgetpu_client_remove(struct edgetpu_client *client);

/* Handle chip-specific client removal */
void edgetpu_chip_client_remove(struct edgetpu_client *client);

/* mmap() device/queue memory */
int edgetpu_mmap(struct edgetpu_client *client, struct vm_area_struct *vma);

/* Increase reference count of @client. */
struct edgetpu_client *edgetpu_client_get(struct edgetpu_client *client);

/* Decrease reference count and free @client if count reaches zero */
void edgetpu_client_put(struct edgetpu_client *client);

/* Mark die that fails probe to allow bypassing */
void edgetpu_mark_probe_fail(struct edgetpu_dev *etdev);

/*
 * Get error code corresponding to @etdev state. Caller holds
 * etdev->state_lock.
 */
int edgetpu_get_state_errno_locked(struct edgetpu_dev *etdev);

/*
 * "External mailboxes" below refers to mailboxes that are not handled
 * directly by the runtime, such as secure or device-to-device.
 *
 * Chip specific code will typically keep track of state and inform the firmware
 * that a mailbox has become active/inactive.
 */

/* Chip-specific code to acquire external mailboxes */
int edgetpu_chip_acquire_ext_mailbox(struct edgetpu_client *client,
				     struct edgetpu_ext_mailbox_ioctl *args);

/* Chip-specific code to release external mailboxes */
int edgetpu_chip_release_ext_mailbox(struct edgetpu_client *client,
				     struct edgetpu_ext_mailbox_ioctl *args);

/*
 * Chip specific function to get indexes of external mailbox based on
 * @mbox_type
 */
int edgetpu_chip_get_ext_mailbox_index(u32 mbox_type, u32 *start, u32 *end);

#endif /* __EDGETPU_INTERNAL_H__ */