1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved
4 *
5 * The driver handles Error's from Control Backbone(CBB) version 2.0.
6 * generated due to illegal accesses. The driver prints debug information
7 * about failed transaction on receiving interrupt from Error Notifier.
8 * Error types supported by CBB2.0 are:
9 *   UNSUPPORTED_ERR, PWRDOWN_ERR, TIMEOUT_ERR, FIREWALL_ERR, DECODE_ERR,
10 *   SLAVE_ERR
11 */
12
13#include <linux/acpi.h>
14#include <linux/clk.h>
15#include <linux/cpufeature.h>
16#include <linux/debugfs.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/platform_device.h>
20#include <linux/device.h>
21#include <linux/io.h>
22#include <linux/interrupt.h>
23#include <linux/ioport.h>
24#include <soc/tegra/fuse.h>
25#include <soc/tegra/tegra-cbb.h>
26
27#define FABRIC_EN_CFG_INTERRUPT_ENABLE_0_0	0x0
28#define FABRIC_EN_CFG_STATUS_0_0		0x40
29#define FABRIC_EN_CFG_ADDR_INDEX_0_0		0x60
30#define FABRIC_EN_CFG_ADDR_LOW_0		0x80
31#define FABRIC_EN_CFG_ADDR_HI_0			0x84
32
33#define FABRIC_MN_MASTER_ERR_EN_0		0x200
34#define FABRIC_MN_MASTER_ERR_FORCE_0		0x204
35#define FABRIC_MN_MASTER_ERR_STATUS_0		0x208
36#define FABRIC_MN_MASTER_ERR_OVERFLOW_STATUS_0	0x20c
37
38#define FABRIC_MN_MASTER_LOG_ERR_STATUS_0	0x300
39#define FABRIC_MN_MASTER_LOG_ADDR_LOW_0		0x304
40#define FABRIC_MN_MASTER_LOG_ADDR_HIGH_0	0x308
41#define FABRIC_MN_MASTER_LOG_ATTRIBUTES0_0	0x30c
42#define FABRIC_MN_MASTER_LOG_ATTRIBUTES1_0	0x310
43#define FABRIC_MN_MASTER_LOG_ATTRIBUTES2_0	0x314
44#define FABRIC_MN_MASTER_LOG_USER_BITS0_0	0x318
45
46#define AXI_SLV_TIMEOUT_STATUS_0_0		0x8
47#define APB_BLOCK_TMO_STATUS_0			0xc00
48#define APB_BLOCK_NUM_TMO_OFFSET		0x20
49
50#define FAB_EM_EL_MSTRID		GENMASK(29, 24)
51#define FAB_EM_EL_VQC			GENMASK(17, 16)
52#define FAB_EM_EL_GRPSEC		GENMASK(14, 8)
53#define FAB_EM_EL_FALCONSEC		GENMASK(1, 0)
54
55#define FAB_EM_EL_FABID			GENMASK(20, 16)
56#define FAB_EM_EL_SLAVEID		GENMASK(7, 0)
57
58#define FAB_EM_EL_ACCESSID		GENMASK(7, 0)
59
60#define FAB_EM_EL_AXCACHE		GENMASK(27, 24)
61#define FAB_EM_EL_AXPROT		GENMASK(22, 20)
62#define FAB_EM_EL_BURSTLENGTH		GENMASK(19, 12)
63#define FAB_EM_EL_BURSTTYPE		GENMASK(9, 8)
64#define FAB_EM_EL_BEATSIZE		GENMASK(6, 4)
65#define FAB_EM_EL_ACCESSTYPE		GENMASK(0, 0)
66
67#define USRBITS_MSTR_ID			GENMASK(29, 24)
68
69#define REQ_SOCKET_ID			GENMASK(27, 24)
70
71#define CCPLEX_MSTRID			0x1
72#define FIREWALL_APERTURE_SZ		0x10000
73/* Write firewall check enable */
74#define WEN				0x20000
75
76enum tegra234_cbb_fabric_ids {
77	CBB_FAB_ID,
78	SCE_FAB_ID,
79	RCE_FAB_ID,
80	DCE_FAB_ID,
81	AON_FAB_ID,
82	PSC_FAB_ID,
83	BPMP_FAB_ID,
84	FSI_FAB_ID,
85	MAX_FAB_ID,
86};
87
88struct tegra234_slave_lookup {
89	const char *name;
90	unsigned int offset;
91};
92
93struct tegra234_cbb_fabric {
94	const char *name;
95	phys_addr_t off_mask_erd;
96	phys_addr_t firewall_base;
97	unsigned int firewall_ctl;
98	unsigned int firewall_wr_ctl;
99	const char * const *master_id;
100	unsigned int notifier_offset;
101	const struct tegra_cbb_error *errors;
102	const int max_errors;
103	const struct tegra234_slave_lookup *slave_map;
104	const int max_slaves;
105};
106
107struct tegra234_cbb {
108	struct tegra_cbb base;
109
110	const struct tegra234_cbb_fabric *fabric;
111	struct resource *res;
112	void __iomem *regs;
113
114	int num_intr;
115	int sec_irq;
116
117	/* record */
118	void __iomem *mon;
119	unsigned int type;
120	u32 mask;
121	u64 access;
122	u32 mn_attr0;
123	u32 mn_attr1;
124	u32 mn_attr2;
125	u32 mn_user_bits;
126};
127
128static inline struct tegra234_cbb *to_tegra234_cbb(struct tegra_cbb *cbb)
129{
130	return container_of(cbb, struct tegra234_cbb, base);
131}
132
133static LIST_HEAD(cbb_list);
134static DEFINE_SPINLOCK(cbb_lock);
135
136static bool
137tegra234_cbb_write_access_allowed(struct platform_device *pdev, struct tegra234_cbb *cbb)
138{
139	u32 val;
140
141	if (!cbb->fabric->firewall_base ||
142	    !cbb->fabric->firewall_ctl ||
143	    !cbb->fabric->firewall_wr_ctl) {
144		dev_info(&pdev->dev, "SoC data missing for firewall\n");
145		return false;
146	}
147
148	if ((cbb->fabric->firewall_ctl > FIREWALL_APERTURE_SZ) ||
149	    (cbb->fabric->firewall_wr_ctl > FIREWALL_APERTURE_SZ)) {
150		dev_err(&pdev->dev, "wrong firewall offset value\n");
151		return false;
152	}
153
154	val = readl(cbb->regs + cbb->fabric->firewall_base + cbb->fabric->firewall_ctl);
155	/*
156	 * If the firewall check feature for allowing or blocking the
157	 * write accesses through the firewall of a fabric is disabled
158	 * then CCPLEX can write to the registers of that fabric.
159	 */
160	if (!(val & WEN))
161		return true;
162
163	/*
164	 * If the firewall check is enabled then check whether CCPLEX
165	 * has write access to the fabric's error notifier registers
166	 */
167	val = readl(cbb->regs + cbb->fabric->firewall_base + cbb->fabric->firewall_wr_ctl);
168	if (val & (BIT(CCPLEX_MSTRID)))
169		return true;
170
171	return false;
172}
173
174static void tegra234_cbb_fault_enable(struct tegra_cbb *cbb)
175{
176	struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
177	void __iomem *addr;
178
179	addr = priv->regs + priv->fabric->notifier_offset;
180	writel(0x1ff, addr + FABRIC_EN_CFG_INTERRUPT_ENABLE_0_0);
181	dsb(sy);
182}
183
184static void tegra234_cbb_error_clear(struct tegra_cbb *cbb)
185{
186	struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
187
188	writel(0x3f, priv->mon + FABRIC_MN_MASTER_ERR_STATUS_0);
189	dsb(sy);
190}
191
192static u32 tegra234_cbb_get_status(struct tegra_cbb *cbb)
193{
194	struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
195	void __iomem *addr;
196	u32 value;
197
198	addr = priv->regs + priv->fabric->notifier_offset;
199	value = readl(addr + FABRIC_EN_CFG_STATUS_0_0);
200	dsb(sy);
201
202	return value;
203}
204
205static void tegra234_cbb_mask_serror(struct tegra234_cbb *cbb)
206{
207	writel(0x1, cbb->regs + cbb->fabric->off_mask_erd);
208	dsb(sy);
209}
210
211static u32 tegra234_cbb_get_tmo_slv(void __iomem *addr)
212{
213	u32 timeout;
214
215	timeout = readl(addr);
216	return timeout;
217}
218
219static void tegra234_cbb_tmo_slv(struct seq_file *file, const char *slave, void __iomem *addr,
220				 u32 status)
221{
222	tegra_cbb_print_err(file, "\t  %s : %#x\n", slave, status);
223}
224
225static void tegra234_cbb_lookup_apbslv(struct seq_file *file, const char *slave,
226				       void __iomem *base)
227{
228	unsigned int block = 0;
229	void __iomem *addr;
230	char name[64];
231	u32 status;
232
233	status = tegra234_cbb_get_tmo_slv(base);
234	if (status)
235		tegra_cbb_print_err(file, "\t  %s_BLOCK_TMO_STATUS : %#x\n", slave, status);
236
237	while (status) {
238		if (status & BIT(0)) {
239			u32 timeout, clients, client = 0;
240
241			addr = base + APB_BLOCK_NUM_TMO_OFFSET + (block * 4);
242			timeout = tegra234_cbb_get_tmo_slv(addr);
243			clients = timeout;
244
245			while (timeout) {
246				if (timeout & BIT(0)) {
247					if (clients != 0xffffffff)
248						clients &= BIT(client);
249
250					sprintf(name, "%s_BLOCK%d_TMO", slave, block);
251
252					tegra234_cbb_tmo_slv(file, name, addr, clients);
253				}
254
255				timeout >>= 1;
256				client++;
257			}
258		}
259
260		status >>= 1;
261		block++;
262	}
263}
264
265static void tegra234_lookup_slave_timeout(struct seq_file *file, struct tegra234_cbb *cbb,
266					  u8 slave_id, u8 fab_id)
267{
268	const struct tegra234_slave_lookup *map = cbb->fabric->slave_map;
269	void __iomem *addr;
270
271	/*
272	 * 1) Get slave node name and address mapping using slave_id.
273	 * 2) Check if the timed out slave node is APB or AXI.
274	 * 3) If AXI, then print timeout register and reset axi slave
275	 *    using <FABRIC>_SN_<>_SLV_TIMEOUT_STATUS_0_0 register.
276	 * 4) If APB, then perform an additional lookup to find the client
277	 *    which timed out.
278	 *	a) Get block number from the index of set bit in
279	 *	   <FABRIC>_SN_AXI2APB_<>_BLOCK_TMO_STATUS_0 register.
280	 *	b) Get address of register repective to block number i.e.
281	 *	   <FABRIC>_SN_AXI2APB_<>_BLOCK<index-set-bit>_TMO_0.
282	 *	c) Read the register in above step to get client_id which
283	 *	   timed out as per the set bits.
284	 *      d) Reset the timedout client and print details.
285	 *	e) Goto step-a till all bits are set.
286	 */
287
288	addr = cbb->regs + map[slave_id].offset;
289
290	if (strstr(map[slave_id].name, "AXI2APB")) {
291		addr += APB_BLOCK_TMO_STATUS_0;
292
293		tegra234_cbb_lookup_apbslv(file, map[slave_id].name, addr);
294	} else {
295		char name[64];
296		u32 status;
297
298		addr += AXI_SLV_TIMEOUT_STATUS_0_0;
299
300		status = tegra234_cbb_get_tmo_slv(addr);
301		if (status) {
302			sprintf(name, "%s_SLV_TIMEOUT_STATUS", map[slave_id].name);
303			tegra234_cbb_tmo_slv(file, name, addr, status);
304		}
305	}
306}
307
308static void tegra234_cbb_print_error(struct seq_file *file, struct tegra234_cbb *cbb, u32 status,
309				     u32 overflow)
310{
311	unsigned int type = 0;
312
313	if (status & (status - 1))
314		tegra_cbb_print_err(file, "\t  Multiple type of errors reported\n");
315
316	while (status) {
317		if (type >= cbb->fabric->max_errors) {
318			tegra_cbb_print_err(file, "\t  Wrong type index:%u, status:%u\n",
319					    type, status);
320			return;
321		}
322
323		if (status & 0x1)
324			tegra_cbb_print_err(file, "\t  Error Code\t\t: %s\n",
325					    cbb->fabric->errors[type].code);
326
327		status >>= 1;
328		type++;
329	}
330
331	type = 0;
332
333	while (overflow) {
334		if (type >= cbb->fabric->max_errors) {
335			tegra_cbb_print_err(file, "\t  Wrong type index:%u, overflow:%u\n",
336					    type, overflow);
337			return;
338		}
339
340		if (overflow & 0x1)
341			tegra_cbb_print_err(file, "\t  Overflow\t\t: Multiple %s\n",
342					    cbb->fabric->errors[type].code);
343
344		overflow >>= 1;
345		type++;
346	}
347}
348
349static void print_errlog_err(struct seq_file *file, struct tegra234_cbb *cbb)
350{
351	u8 cache_type, prot_type, burst_length, mstr_id, grpsec, vqc, falconsec, beat_size;
352	u8 access_type, access_id, requester_socket_id, local_socket_id, slave_id, fab_id;
353	char fabric_name[20];
354	bool is_numa = false;
355	u8 burst_type;
356
357	if (num_possible_nodes() > 1)
358		is_numa = true;
359
360	mstr_id = FIELD_GET(FAB_EM_EL_MSTRID, cbb->mn_user_bits);
361	vqc = FIELD_GET(FAB_EM_EL_VQC, cbb->mn_user_bits);
362	grpsec = FIELD_GET(FAB_EM_EL_GRPSEC, cbb->mn_user_bits);
363	falconsec = FIELD_GET(FAB_EM_EL_FALCONSEC, cbb->mn_user_bits);
364
365	/*
366	 * For SOC with multiple NUMA nodes, print cross socket access
367	 * errors only if initiator/master_id is CCPLEX, CPMU or GPU.
368	 */
369	if (is_numa) {
370		local_socket_id = numa_node_id();
371		requester_socket_id = FIELD_GET(REQ_SOCKET_ID, cbb->mn_attr2);
372
373		if (requester_socket_id != local_socket_id) {
374			if ((mstr_id != 0x1) && (mstr_id != 0x2) && (mstr_id != 0xB))
375				return;
376		}
377	}
378
379	fab_id = FIELD_GET(FAB_EM_EL_FABID, cbb->mn_attr2);
380	slave_id = FIELD_GET(FAB_EM_EL_SLAVEID, cbb->mn_attr2);
381
382	access_id = FIELD_GET(FAB_EM_EL_ACCESSID, cbb->mn_attr1);
383
384	cache_type = FIELD_GET(FAB_EM_EL_AXCACHE, cbb->mn_attr0);
385	prot_type = FIELD_GET(FAB_EM_EL_AXPROT, cbb->mn_attr0);
386	burst_length = FIELD_GET(FAB_EM_EL_BURSTLENGTH, cbb->mn_attr0);
387	burst_type = FIELD_GET(FAB_EM_EL_BURSTTYPE, cbb->mn_attr0);
388	beat_size = FIELD_GET(FAB_EM_EL_BEATSIZE, cbb->mn_attr0);
389	access_type = FIELD_GET(FAB_EM_EL_ACCESSTYPE, cbb->mn_attr0);
390
391	tegra_cbb_print_err(file, "\n");
392	if (cbb->type < cbb->fabric->max_errors)
393		tegra_cbb_print_err(file, "\t  Error Code\t\t: %s\n",
394				    cbb->fabric->errors[cbb->type].code);
395	else
396		tegra_cbb_print_err(file, "\t  Wrong type index:%u\n", cbb->type);
397
398	tegra_cbb_print_err(file, "\t  MASTER_ID\t\t: %s\n", cbb->fabric->master_id[mstr_id]);
399	tegra_cbb_print_err(file, "\t  Address\t\t: %#llx\n", cbb->access);
400
401	tegra_cbb_print_cache(file, cache_type);
402	tegra_cbb_print_prot(file, prot_type);
403
404	tegra_cbb_print_err(file, "\t  Access_Type\t\t: %s", (access_type) ? "Write\n" : "Read\n");
405	tegra_cbb_print_err(file, "\t  Access_ID\t\t: %#x", access_id);
406
407	if (fab_id == PSC_FAB_ID)
408		strcpy(fabric_name, "psc-fabric");
409	else if (fab_id == FSI_FAB_ID)
410		strcpy(fabric_name, "fsi-fabric");
411	else
412		strcpy(fabric_name, cbb->fabric->name);
413
414	if (is_numa) {
415		tegra_cbb_print_err(file, "\t  Requester_Socket_Id\t: %#x\n",
416				    requester_socket_id);
417		tegra_cbb_print_err(file, "\t  Local_Socket_Id\t: %#x\n",
418				    local_socket_id);
419		tegra_cbb_print_err(file, "\t  No. of NUMA_NODES\t: %#x\n",
420				    num_possible_nodes());
421	}
422
423	tegra_cbb_print_err(file, "\t  Fabric\t\t: %s\n", fabric_name);
424	tegra_cbb_print_err(file, "\t  Slave_Id\t\t: %#x\n", slave_id);
425	tegra_cbb_print_err(file, "\t  Burst_length\t\t: %#x\n", burst_length);
426	tegra_cbb_print_err(file, "\t  Burst_type\t\t: %#x\n", burst_type);
427	tegra_cbb_print_err(file, "\t  Beat_size\t\t: %#x\n", beat_size);
428	tegra_cbb_print_err(file, "\t  VQC\t\t\t: %#x\n", vqc);
429	tegra_cbb_print_err(file, "\t  GRPSEC\t\t: %#x\n", grpsec);
430	tegra_cbb_print_err(file, "\t  FALCONSEC\t\t: %#x\n", falconsec);
431
432	if ((fab_id == PSC_FAB_ID) || (fab_id == FSI_FAB_ID))
433		return;
434
435	if (slave_id >= cbb->fabric->max_slaves) {
436		tegra_cbb_print_err(file, "\t  Invalid slave_id:%d\n", slave_id);
437		return;
438	}
439
440	if (!strcmp(cbb->fabric->errors[cbb->type].code, "TIMEOUT_ERR")) {
441		tegra234_lookup_slave_timeout(file, cbb, slave_id, fab_id);
442		return;
443	}
444
445	tegra_cbb_print_err(file, "\t  Slave\t\t\t: %s\n", cbb->fabric->slave_map[slave_id].name);
446}
447
448static int print_errmonX_info(struct seq_file *file, struct tegra234_cbb *cbb)
449{
450	u32 overflow, status, error;
451
452	status = readl(cbb->mon + FABRIC_MN_MASTER_ERR_STATUS_0);
453	if (!status) {
454		pr_err("Error Notifier received a spurious notification\n");
455		return -ENODATA;
456	}
457
458	if (status == 0xffffffff) {
459		pr_err("CBB registers returning all 1's which is invalid\n");
460		return -EINVAL;
461	}
462
463	overflow = readl(cbb->mon + FABRIC_MN_MASTER_ERR_OVERFLOW_STATUS_0);
464
465	tegra234_cbb_print_error(file, cbb, status, overflow);
466
467	error = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ERR_STATUS_0);
468	if (!error) {
469		pr_info("Error Monitor doesn't have Error Logger\n");
470		return -EINVAL;
471	}
472
473	cbb->type = 0;
474
475	while (error) {
476		if (error & BIT(0)) {
477			u32 hi, lo;
478
479			hi = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ADDR_HIGH_0);
480			lo = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ADDR_LOW_0);
481
482			cbb->access = (u64)hi << 32 | lo;
483
484			cbb->mn_attr0 = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES0_0);
485			cbb->mn_attr1 = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES1_0);
486			cbb->mn_attr2 = readl(cbb->mon + FABRIC_MN_MASTER_LOG_ATTRIBUTES2_0);
487			cbb->mn_user_bits = readl(cbb->mon + FABRIC_MN_MASTER_LOG_USER_BITS0_0);
488
489			print_errlog_err(file, cbb);
490		}
491
492		cbb->type++;
493		error >>= 1;
494	}
495
496	return 0;
497}
498
499static int print_err_notifier(struct seq_file *file, struct tegra234_cbb *cbb, u32 status)
500{
501	unsigned int index = 0;
502	int err;
503
504	pr_crit("**************************************\n");
505	pr_crit("CPU:%d, Error:%s, Errmon:%d\n", smp_processor_id(),
506		cbb->fabric->name, status);
507
508	while (status) {
509		if (status & BIT(0)) {
510			unsigned int notifier = cbb->fabric->notifier_offset;
511			u32 hi, lo, mask = BIT(index);
512			phys_addr_t addr;
513			u64 offset;
514
515			writel(mask, cbb->regs + notifier + FABRIC_EN_CFG_ADDR_INDEX_0_0);
516			hi = readl(cbb->regs + notifier + FABRIC_EN_CFG_ADDR_HI_0);
517			lo = readl(cbb->regs + notifier + FABRIC_EN_CFG_ADDR_LOW_0);
518
519			addr = (u64)hi << 32 | lo;
520
521			offset = addr - cbb->res->start;
522			cbb->mon = cbb->regs + offset;
523			cbb->mask = BIT(index);
524
525			err = print_errmonX_info(file, cbb);
526			tegra234_cbb_error_clear(&cbb->base);
527			if (err)
528				return err;
529		}
530
531		status >>= 1;
532		index++;
533	}
534
535	tegra_cbb_print_err(file, "\t**************************************\n");
536	return 0;
537}
538
539#ifdef CONFIG_DEBUG_FS
540static DEFINE_MUTEX(cbb_debugfs_mutex);
541
542static int tegra234_cbb_debugfs_show(struct tegra_cbb *cbb, struct seq_file *file, void *data)
543{
544	int err = 0;
545
546	mutex_lock(&cbb_debugfs_mutex);
547
548	list_for_each_entry(cbb, &cbb_list, node) {
549		struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
550		u32 status;
551
552		status = tegra_cbb_get_status(&priv->base);
553		if (status) {
554			err = print_err_notifier(file, priv, status);
555			if (err)
556				break;
557		}
558	}
559
560	mutex_unlock(&cbb_debugfs_mutex);
561	return err;
562}
563#endif
564
565/*
566 * Handler for CBB errors
567 */
568static irqreturn_t tegra234_cbb_isr(int irq, void *data)
569{
570	bool is_inband_err = false;
571	struct tegra_cbb *cbb;
572	unsigned long flags;
573	u8 mstr_id;
574	int err;
575
576	spin_lock_irqsave(&cbb_lock, flags);
577
578	list_for_each_entry(cbb, &cbb_list, node) {
579		struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
580		u32 status = tegra_cbb_get_status(cbb);
581
582		if (status && (irq == priv->sec_irq)) {
583			tegra_cbb_print_err(NULL, "CPU:%d, Error: %s@0x%llx, irq=%d\n",
584					    smp_processor_id(), priv->fabric->name,
585					    priv->res->start, irq);
586
587			err = print_err_notifier(NULL, priv, status);
588			if (err)
589				goto unlock;
590
591			/*
592			 * If illegal request is from CCPLEX(id:0x1) master then call WARN()
593			 */
594			if (priv->fabric->off_mask_erd) {
595				mstr_id =  FIELD_GET(USRBITS_MSTR_ID, priv->mn_user_bits);
596				if (mstr_id == CCPLEX_MSTRID)
597					is_inband_err = 1;
598			}
599		}
600	}
601
602unlock:
603	spin_unlock_irqrestore(&cbb_lock, flags);
604	WARN_ON(is_inband_err);
605	return IRQ_HANDLED;
606}
607
608/*
609 * Register handler for CBB_SECURE interrupt for reporting errors
610 */
611static int tegra234_cbb_interrupt_enable(struct tegra_cbb *cbb)
612{
613	struct tegra234_cbb *priv = to_tegra234_cbb(cbb);
614
615	if (priv->sec_irq) {
616		int err = devm_request_irq(cbb->dev, priv->sec_irq, tegra234_cbb_isr, 0,
617					   dev_name(cbb->dev), priv);
618		if (err) {
619			dev_err(cbb->dev, "failed to register interrupt %u: %d\n", priv->sec_irq,
620				err);
621			return err;
622		}
623	}
624
625	return 0;
626}
627
628static void tegra234_cbb_error_enable(struct tegra_cbb *cbb)
629{
630	tegra_cbb_fault_enable(cbb);
631}
632
633static const struct tegra_cbb_ops tegra234_cbb_ops = {
634	.get_status = tegra234_cbb_get_status,
635	.error_clear = tegra234_cbb_error_clear,
636	.fault_enable = tegra234_cbb_fault_enable,
637	.error_enable = tegra234_cbb_error_enable,
638	.interrupt_enable = tegra234_cbb_interrupt_enable,
639#ifdef CONFIG_DEBUG_FS
640	.debugfs_show = tegra234_cbb_debugfs_show,
641#endif
642};
643
644static const char * const tegra234_master_id[] = {
645	[0x00] = "TZ",
646	[0x01] = "CCPLEX",
647	[0x02] = "CCPMU",
648	[0x03] = "BPMP_FW",
649	[0x04] = "AON",
650	[0x05] = "SCE",
651	[0x06] = "GPCDMA_P",
652	[0x07] = "TSECA_NONSECURE",
653	[0x08] = "TSECA_LIGHTSECURE",
654	[0x09] = "TSECA_HEAVYSECURE",
655	[0x0a] = "CORESIGHT",
656	[0x0b] = "APE",
657	[0x0c] = "PEATRANS",
658	[0x0d] = "JTAGM_DFT",
659	[0x0e] = "RCE",
660	[0x0f] = "DCE",
661	[0x10] = "PSC_FW_USER",
662	[0x11] = "PSC_FW_SUPERVISOR",
663	[0x12] = "PSC_FW_MACHINE",
664	[0x13] = "PSC_BOOT",
665	[0x14] = "BPMP_BOOT",
666	[0x15] = "NVDEC_NONSECURE",
667	[0x16] = "NVDEC_LIGHTSECURE",
668	[0x17] = "NVDEC_HEAVYSECURE",
669	[0x18] = "CBB_INTERNAL",
670	[0x19] = "RSVD"
671};
672
673static const struct tegra_cbb_error tegra234_cbb_errors[] = {
674	{
675		.code = "SLAVE_ERR",
676		.desc = "Slave being accessed responded with an error"
677	}, {
678		.code = "DECODE_ERR",
679		.desc = "Attempt to access an address hole"
680	}, {
681		.code = "FIREWALL_ERR",
682		.desc = "Attempt to access a region which is firewall protected"
683	}, {
684		.code = "TIMEOUT_ERR",
685		.desc = "No response returned by slave"
686	}, {
687		.code = "PWRDOWN_ERR",
688		.desc = "Attempt to access a portion of fabric that is powered down"
689	}, {
690		.code = "UNSUPPORTED_ERR",
691		.desc = "Attempt to access a slave through an unsupported access"
692	}
693};
694
695static const struct tegra234_slave_lookup tegra234_aon_slave_map[] = {
696	{ "AXI2APB", 0x00000 },
697	{ "AST",     0x14000 },
698	{ "CBB",     0x15000 },
699	{ "CPU",     0x16000 },
700};
701
702static const struct tegra234_cbb_fabric tegra234_aon_fabric = {
703	.name = "aon-fabric",
704	.master_id = tegra234_master_id,
705	.slave_map = tegra234_aon_slave_map,
706	.max_slaves = ARRAY_SIZE(tegra234_aon_slave_map),
707	.errors = tegra234_cbb_errors,
708	.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
709	.notifier_offset = 0x17000,
710	.firewall_base = 0x30000,
711	.firewall_ctl = 0x8d0,
712	.firewall_wr_ctl = 0x8c8,
713};
714
715static const struct tegra234_slave_lookup tegra234_bpmp_slave_map[] = {
716	{ "AXI2APB", 0x00000 },
717	{ "AST0",    0x15000 },
718	{ "AST1",    0x16000 },
719	{ "CBB",     0x17000 },
720	{ "CPU",     0x18000 },
721};
722
723static const struct tegra234_cbb_fabric tegra234_bpmp_fabric = {
724	.name = "bpmp-fabric",
725	.master_id = tegra234_master_id,
726	.slave_map = tegra234_bpmp_slave_map,
727	.max_slaves = ARRAY_SIZE(tegra234_bpmp_slave_map),
728	.errors = tegra234_cbb_errors,
729	.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
730	.notifier_offset = 0x19000,
731	.firewall_base = 0x30000,
732	.firewall_ctl = 0x8f0,
733	.firewall_wr_ctl = 0x8e8,
734};
735
736static const struct tegra234_slave_lookup tegra234_cbb_slave_map[] = {
737	{ "AON",        0x40000 },
738	{ "BPMP",       0x41000 },
739	{ "CBB",        0x42000 },
740	{ "HOST1X",     0x43000 },
741	{ "STM",        0x44000 },
742	{ "FSI",        0x45000 },
743	{ "PSC",        0x46000 },
744	{ "PCIE_C1",    0x47000 },
745	{ "PCIE_C2",    0x48000 },
746	{ "PCIE_C3",    0x49000 },
747	{ "PCIE_C0",    0x4a000 },
748	{ "PCIE_C4",    0x4b000 },
749	{ "GPU",        0x4c000 },
750	{ "SMMU0",      0x4d000 },
751	{ "SMMU1",      0x4e000 },
752	{ "SMMU2",      0x4f000 },
753	{ "SMMU3",      0x50000 },
754	{ "SMMU4",      0x51000 },
755	{ "PCIE_C10",   0x52000 },
756	{ "PCIE_C7",    0x53000 },
757	{ "PCIE_C8",    0x54000 },
758	{ "PCIE_C9",    0x55000 },
759	{ "PCIE_C5",    0x56000 },
760	{ "PCIE_C6",    0x57000 },
761	{ "DCE",        0x58000 },
762	{ "RCE",        0x59000 },
763	{ "SCE",        0x5a000 },
764	{ "AXI2APB_1",  0x70000 },
765	{ "AXI2APB_10", 0x71000 },
766	{ "AXI2APB_11", 0x72000 },
767	{ "AXI2APB_12", 0x73000 },
768	{ "AXI2APB_13", 0x74000 },
769	{ "AXI2APB_14", 0x75000 },
770	{ "AXI2APB_15", 0x76000 },
771	{ "AXI2APB_16", 0x77000 },
772	{ "AXI2APB_17", 0x78000 },
773	{ "AXI2APB_18", 0x79000 },
774	{ "AXI2APB_19", 0x7a000 },
775	{ "AXI2APB_2",  0x7b000 },
776	{ "AXI2APB_20", 0x7c000 },
777	{ "AXI2APB_21", 0x7d000 },
778	{ "AXI2APB_22", 0x7e000 },
779	{ "AXI2APB_23", 0x7f000 },
780	{ "AXI2APB_25", 0x80000 },
781	{ "AXI2APB_26", 0x81000 },
782	{ "AXI2APB_27", 0x82000 },
783	{ "AXI2APB_28", 0x83000 },
784	{ "AXI2APB_29", 0x84000 },
785	{ "AXI2APB_30", 0x85000 },
786	{ "AXI2APB_31", 0x86000 },
787	{ "AXI2APB_32", 0x87000 },
788	{ "AXI2APB_33", 0x88000 },
789	{ "AXI2APB_34", 0x89000 },
790	{ "AXI2APB_35", 0x92000 },
791	{ "AXI2APB_4",  0x8b000 },
792	{ "AXI2APB_5",  0x8c000 },
793	{ "AXI2APB_6",  0x8d000 },
794	{ "AXI2APB_7",  0x8e000 },
795	{ "AXI2APB_8",  0x8f000 },
796	{ "AXI2APB_9",  0x90000 },
797	{ "AXI2APB_3",  0x91000 },
798};
799
800static const struct tegra234_cbb_fabric tegra234_cbb_fabric = {
801	.name = "cbb-fabric",
802	.master_id = tegra234_master_id,
803	.slave_map = tegra234_cbb_slave_map,
804	.max_slaves = ARRAY_SIZE(tegra234_cbb_slave_map),
805	.errors = tegra234_cbb_errors,
806	.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
807	.notifier_offset = 0x60000,
808	.off_mask_erd = 0x3a004,
809	.firewall_base = 0x10000,
810	.firewall_ctl = 0x23f0,
811	.firewall_wr_ctl = 0x23e8,
812};
813
814static const struct tegra234_slave_lookup tegra234_common_slave_map[] = {
815	{ "AXI2APB", 0x00000 },
816	{ "AST0",    0x15000 },
817	{ "AST1",    0x16000 },
818	{ "CBB",     0x17000 },
819	{ "RSVD",    0x00000 },
820	{ "CPU",     0x18000 },
821};
822
823static const struct tegra234_cbb_fabric tegra234_dce_fabric = {
824	.name = "dce-fabric",
825	.master_id = tegra234_master_id,
826	.slave_map = tegra234_common_slave_map,
827	.max_slaves = ARRAY_SIZE(tegra234_common_slave_map),
828	.errors = tegra234_cbb_errors,
829	.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
830	.notifier_offset = 0x19000,
831	.firewall_base = 0x30000,
832	.firewall_ctl = 0x290,
833	.firewall_wr_ctl = 0x288,
834};
835
836static const struct tegra234_cbb_fabric tegra234_rce_fabric = {
837	.name = "rce-fabric",
838	.master_id = tegra234_master_id,
839	.slave_map = tegra234_common_slave_map,
840	.max_slaves = ARRAY_SIZE(tegra234_common_slave_map),
841	.errors = tegra234_cbb_errors,
842	.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
843	.notifier_offset = 0x19000,
844	.firewall_base = 0x30000,
845	.firewall_ctl = 0x290,
846	.firewall_wr_ctl = 0x288,
847};
848
849static const struct tegra234_cbb_fabric tegra234_sce_fabric = {
850	.name = "sce-fabric",
851	.master_id = tegra234_master_id,
852	.slave_map = tegra234_common_slave_map,
853	.max_slaves = ARRAY_SIZE(tegra234_common_slave_map),
854	.errors = tegra234_cbb_errors,
855	.max_errors = ARRAY_SIZE(tegra234_cbb_errors),
856	.notifier_offset = 0x19000,
857	.firewall_base = 0x30000,
858	.firewall_ctl = 0x290,
859	.firewall_wr_ctl = 0x288,
860};
861
862static const char * const tegra241_master_id[] = {
863	[0x0] = "TZ",
864	[0x1] = "CCPLEX",
865	[0x2] = "CCPMU",
866	[0x3] = "BPMP_FW",
867	[0x4] = "PSC_FW_USER",
868	[0x5] = "PSC_FW_SUPERVISOR",
869	[0x6] = "PSC_FW_MACHINE",
870	[0x7] = "PSC_BOOT",
871	[0x8] = "BPMP_BOOT",
872	[0x9] = "JTAGM_DFT",
873	[0xa] = "CORESIGHT",
874	[0xb] = "GPU",
875	[0xc] = "PEATRANS",
876	[0xd ... 0x3f] = "RSVD"
877};
878
879/*
880 * Possible causes for Slave and Timeout errors.
881 * SLAVE_ERR:
882 * Slave being accessed responded with an error. Slave could return
883 * an error for various cases :
884 *   Unsupported access, clamp setting when power gated, register
885 *   level firewall(SCR), address hole within the slave, etc
886 *
887 * TIMEOUT_ERR:
888 * No response returned by slave. Can be due to slave being clock
889 * gated, under reset, powered down or slave inability to respond
890 * for an internal slave issue
891 */
892static const struct tegra_cbb_error tegra241_cbb_errors[] = {
893	{
894		.code = "SLAVE_ERR",
895		.desc = "Slave being accessed responded with an error."
896	}, {
897		.code = "DECODE_ERR",
898		.desc = "Attempt to access an address hole or Reserved region of memory."
899	}, {
900		.code = "FIREWALL_ERR",
901		.desc = "Attempt to access a region which is firewalled."
902	}, {
903		.code = "TIMEOUT_ERR",
904		.desc = "No response returned by slave."
905	}, {
906		.code = "PWRDOWN_ERR",
907		.desc = "Attempt to access a portion of the fabric that is powered down."
908	}, {
909		.code = "UNSUPPORTED_ERR",
910		.desc = "Attempt to access a slave through an unsupported access."
911	}, {
912		.code = "POISON_ERR",
913		.desc = "Slave responds with poison error to indicate error in data."
914	}, {
915		.code = "RSVD"
916	}, {
917		.code = "RSVD"
918	}, {
919		.code = "RSVD"
920	}, {
921		.code = "RSVD"
922	}, {
923		.code = "RSVD"
924	}, {
925		.code = "RSVD"
926	}, {
927		.code = "RSVD"
928	}, {
929		.code = "RSVD"
930	}, {
931		.code = "RSVD"
932	}, {
933		.code = "NO_SUCH_ADDRESS_ERR",
934		.desc = "The address belongs to the pri_target range but there is no register "
935			"implemented at the address."
936	}, {
937		.code = "TASK_ERR",
938		.desc = "Attempt to update a PRI task when the current task has still not "
939			"completed."
940	}, {
941		.code = "EXTERNAL_ERR",
942		.desc = "Indicates that an external PRI register access met with an error due to "
943			"any issue in the unit."
944	}, {
945		.code = "INDEX_ERR",
946		.desc = "Applicable to PRI index aperture pair, when the programmed index is "
947			"outside the range defined in the manual."
948	}, {
949		.code = "RESET_ERR",
950		.desc = "Target in Reset Error: Attempt to access a SubPri or external PRI "
951			"register but they are in reset."
952	}, {
953		.code = "REGISTER_RST_ERR",
954		.desc = "Attempt to access a PRI register but the register is partial or "
955			"completely in reset."
956	}, {
957		.code = "POWER_GATED_ERR",
958		.desc = "Returned by external PRI client when the external access goes to a power "
959			"gated domain."
960	}, {
961		.code = "SUBPRI_FS_ERR",
962		.desc = "Subpri is floorswept: Attempt to access a subpri through the main pri "
963			"target but subPri logic is floorswept."
964	}, {
965		.code = "SUBPRI_CLK_OFF_ERR",
966		.desc = "Subpri clock is off: Attempt to access a subpri through the main pri "
967			"target but subPris clock is gated/off."
968	},
969};
970
971static const struct tegra234_slave_lookup tegra241_cbb_slave_map[] = {
972	{ "RSVD",       0x00000 },
973	{ "PCIE_C8",    0x51000 },
974	{ "PCIE_C9",    0x52000 },
975	{ "RSVD",       0x00000 },
976	{ "RSVD",       0x00000 },
977	{ "RSVD",       0x00000 },
978	{ "RSVD",       0x00000 },
979	{ "RSVD",       0x00000 },
980	{ "RSVD",       0x00000 },
981	{ "RSVD",       0x00000 },
982	{ "RSVD",       0x00000 },
983	{ "AON",        0x5b000 },
984	{ "BPMP",       0x5c000 },
985	{ "RSVD",       0x00000 },
986	{ "RSVD",       0x00000 },
987	{ "PSC",        0x5d000 },
988	{ "STM",        0x5e000 },
989	{ "AXI2APB_1",  0x70000 },
990	{ "AXI2APB_10", 0x71000 },
991	{ "AXI2APB_11", 0x72000 },
992	{ "AXI2APB_12", 0x73000 },
993	{ "AXI2APB_13", 0x74000 },
994	{ "AXI2APB_14", 0x75000 },
995	{ "AXI2APB_15", 0x76000 },
996	{ "AXI2APB_16", 0x77000 },
997	{ "AXI2APB_17", 0x78000 },
998	{ "AXI2APB_18", 0x79000 },
999	{ "AXI2APB_19", 0x7a000 },
1000	{ "AXI2APB_2",  0x7b000 },
1001	{ "AXI2APB_20", 0x7c000 },
1002	{ "AXI2APB_4",  0x87000 },
1003	{ "AXI2APB_5",  0x88000 },
1004	{ "AXI2APB_6",  0x89000 },
1005	{ "AXI2APB_7",  0x8a000 },
1006	{ "AXI2APB_8",  0x8b000 },
1007	{ "AXI2APB_9",  0x8c000 },
1008	{ "AXI2APB_3",  0x8d000 },
1009	{ "AXI2APB_21", 0x7d000 },
1010	{ "AXI2APB_22", 0x7e000 },
1011	{ "AXI2APB_23", 0x7f000 },
1012	{ "AXI2APB_24", 0x80000 },
1013	{ "AXI2APB_25", 0x81000 },
1014	{ "AXI2APB_26", 0x82000 },
1015	{ "AXI2APB_27", 0x83000 },
1016	{ "AXI2APB_28", 0x84000 },
1017	{ "PCIE_C4",    0x53000 },
1018	{ "PCIE_C5",    0x54000 },
1019	{ "PCIE_C6",    0x55000 },
1020	{ "PCIE_C7",    0x56000 },
1021	{ "PCIE_C2",    0x57000 },
1022	{ "PCIE_C3",    0x58000 },
1023	{ "PCIE_C0",    0x59000 },
1024	{ "PCIE_C1",    0x5a000 },
1025	{ "CCPLEX",     0x50000 },
1026	{ "AXI2APB_29", 0x85000 },
1027	{ "AXI2APB_30", 0x86000 },
1028	{ "CBB_CENTRAL", 0x00000 },
1029	{ "AXI2APB_31", 0x8E000 },
1030	{ "AXI2APB_32", 0x8F000 },
1031};
1032
1033static const struct tegra234_cbb_fabric tegra241_cbb_fabric = {
1034	.name = "cbb-fabric",
1035	.master_id = tegra241_master_id,
1036	.slave_map = tegra241_cbb_slave_map,
1037	.max_slaves = ARRAY_SIZE(tegra241_cbb_slave_map),
1038	.errors = tegra241_cbb_errors,
1039	.max_errors = ARRAY_SIZE(tegra241_cbb_errors),
1040	.notifier_offset = 0x60000,
1041	.off_mask_erd = 0x40004,
1042	.firewall_base = 0x20000,
1043	.firewall_ctl = 0x2370,
1044	.firewall_wr_ctl = 0x2368,
1045};
1046
1047static const struct tegra234_slave_lookup tegra241_bpmp_slave_map[] = {
1048	{ "RSVD",    0x00000 },
1049	{ "RSVD",    0x00000 },
1050	{ "RSVD",    0x00000 },
1051	{ "CBB",     0x15000 },
1052	{ "CPU",     0x16000 },
1053	{ "AXI2APB", 0x00000 },
1054	{ "DBB0",    0x17000 },
1055	{ "DBB1",    0x18000 },
1056};
1057
1058static const struct tegra234_cbb_fabric tegra241_bpmp_fabric = {
1059	.name = "bpmp-fabric",
1060	.master_id = tegra241_master_id,
1061	.slave_map = tegra241_bpmp_slave_map,
1062	.max_slaves = ARRAY_SIZE(tegra241_bpmp_slave_map),
1063	.errors = tegra241_cbb_errors,
1064	.max_errors = ARRAY_SIZE(tegra241_cbb_errors),
1065	.notifier_offset = 0x19000,
1066	.firewall_base = 0x30000,
1067	.firewall_ctl = 0x8f0,
1068	.firewall_wr_ctl = 0x8e8,
1069};
1070
1071static const struct of_device_id tegra234_cbb_dt_ids[] = {
1072	{ .compatible = "nvidia,tegra234-cbb-fabric", .data = &tegra234_cbb_fabric },
1073	{ .compatible = "nvidia,tegra234-aon-fabric", .data = &tegra234_aon_fabric },
1074	{ .compatible = "nvidia,tegra234-bpmp-fabric", .data = &tegra234_bpmp_fabric },
1075	{ .compatible = "nvidia,tegra234-dce-fabric", .data = &tegra234_dce_fabric },
1076	{ .compatible = "nvidia,tegra234-rce-fabric", .data = &tegra234_rce_fabric },
1077	{ .compatible = "nvidia,tegra234-sce-fabric", .data = &tegra234_sce_fabric },
1078	{ /* sentinel */ },
1079};
1080MODULE_DEVICE_TABLE(of, tegra234_cbb_dt_ids);
1081
1082struct tegra234_cbb_acpi_uid {
1083	const char *hid;
1084	const char *uid;
1085	const struct tegra234_cbb_fabric *fabric;
1086};
1087
1088static const struct tegra234_cbb_acpi_uid tegra234_cbb_acpi_uids[] = {
1089	{ "NVDA1070", "1", &tegra241_cbb_fabric },
1090	{ "NVDA1070", "2", &tegra241_bpmp_fabric },
1091	{ },
1092};
1093
1094static const struct
1095tegra234_cbb_fabric *tegra234_cbb_acpi_get_fabric(struct acpi_device *adev)
1096{
1097	const struct tegra234_cbb_acpi_uid *entry;
1098
1099	for (entry = tegra234_cbb_acpi_uids; entry->hid; entry++) {
1100		if (acpi_dev_hid_uid_match(adev, entry->hid, entry->uid))
1101			return entry->fabric;
1102	}
1103
1104	return NULL;
1105}
1106
1107static const struct acpi_device_id tegra241_cbb_acpi_ids[] = {
1108	{ "NVDA1070" },
1109	{ },
1110};
1111MODULE_DEVICE_TABLE(acpi, tegra241_cbb_acpi_ids);
1112
1113static int tegra234_cbb_probe(struct platform_device *pdev)
1114{
1115	const struct tegra234_cbb_fabric *fabric;
1116	struct tegra234_cbb *cbb;
1117	unsigned long flags = 0;
1118	int err;
1119
1120	if (pdev->dev.of_node) {
1121		fabric = of_device_get_match_data(&pdev->dev);
1122	} else {
1123		struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
1124		if (!device)
1125			return -ENODEV;
1126
1127		fabric = tegra234_cbb_acpi_get_fabric(device);
1128		if (!fabric) {
1129			dev_err(&pdev->dev, "no device match found\n");
1130			return -ENODEV;
1131		}
1132	}
1133
1134	cbb = devm_kzalloc(&pdev->dev, sizeof(*cbb), GFP_KERNEL);
1135	if (!cbb)
1136		return -ENOMEM;
1137
1138	INIT_LIST_HEAD(&cbb->base.node);
1139	cbb->base.ops = &tegra234_cbb_ops;
1140	cbb->base.dev = &pdev->dev;
1141	cbb->fabric = fabric;
1142
1143	cbb->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &cbb->res);
1144	if (IS_ERR(cbb->regs))
1145		return PTR_ERR(cbb->regs);
1146
1147	err = tegra_cbb_get_irq(pdev, NULL, &cbb->sec_irq);
1148	if (err)
1149		return err;
1150
1151	platform_set_drvdata(pdev, cbb);
1152
1153	/*
1154	 * Don't enable error reporting for a Fabric if write to it's registers
1155	 * is blocked by CBB firewall.
1156	 */
1157	if (!tegra234_cbb_write_access_allowed(pdev, cbb)) {
1158		dev_info(&pdev->dev, "error reporting not enabled due to firewall\n");
1159		return 0;
1160	}
1161
1162	spin_lock_irqsave(&cbb_lock, flags);
1163	list_add(&cbb->base.node, &cbb_list);
1164	spin_unlock_irqrestore(&cbb_lock, flags);
1165
1166	/* set ERD bit to mask SError and generate interrupt to report error */
1167	if (cbb->fabric->off_mask_erd)
1168		tegra234_cbb_mask_serror(cbb);
1169
1170	return tegra_cbb_register(&cbb->base);
1171}
1172
1173static int __maybe_unused tegra234_cbb_resume_noirq(struct device *dev)
1174{
1175	struct tegra234_cbb *cbb = dev_get_drvdata(dev);
1176
1177	tegra234_cbb_error_enable(&cbb->base);
1178
1179	dev_dbg(dev, "%s resumed\n", cbb->fabric->name);
1180
1181	return 0;
1182}
1183
1184static const struct dev_pm_ops tegra234_cbb_pm = {
1185	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, tegra234_cbb_resume_noirq)
1186};
1187
1188static struct platform_driver tegra234_cbb_driver = {
1189	.probe = tegra234_cbb_probe,
1190	.driver = {
1191		.name = "tegra234-cbb",
1192		.of_match_table = tegra234_cbb_dt_ids,
1193		.acpi_match_table = tegra241_cbb_acpi_ids,
1194		.pm = &tegra234_cbb_pm,
1195	},
1196};
1197
1198static int __init tegra234_cbb_init(void)
1199{
1200	return platform_driver_register(&tegra234_cbb_driver);
1201}
1202pure_initcall(tegra234_cbb_init);
1203
1204static void __exit tegra234_cbb_exit(void)
1205{
1206	platform_driver_unregister(&tegra234_cbb_driver);
1207}
1208module_exit(tegra234_cbb_exit);
1209
1210MODULE_DESCRIPTION("Control Backbone 2.0 error handling driver for Tegra234");
1211