xref: /linux/drivers/gpu/host1x/hw/intr_hw.c (revision f017f1e9cb3458a86f586a171e284e2ec46286db)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Tegra host1x Interrupt Management
4  *
5  * Copyright (C) 2010 Google, Inc.
6  * Copyright (c) 2010-2013, NVIDIA Corporation.
7  */
8 
9 #include <linux/interrupt.h>
10 #include <linux/irq.h>
11 #include <linux/io.h>
12 
13 #include "../intr.h"
14 #include "../dev.h"
15 
16 struct host1x_intr_irq_data {
17 	struct host1x *host;
18 	u32 offset;
19 };
20 
21 static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
22 {
23 	struct host1x_intr_irq_data *irq_data = dev_id;
24 	struct host1x *host = irq_data->host;
25 	unsigned long reg;
26 	unsigned int i, id;
27 
28 	for (i = irq_data->offset; i < DIV_ROUND_UP(host->info->nb_pts, 32);
29 	     i += host->num_syncpt_irqs) {
30 		reg = host1x_sync_readl(host,
31 			HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
32 
33 		host1x_sync_writel(host, reg,
34 			HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i));
35 		host1x_sync_writel(host, reg,
36 			HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
37 
38 		for_each_set_bit(id, &reg, 32)
39 			host1x_intr_handle_interrupt(host, i * 32 + id);
40 	}
41 
42 	return IRQ_HANDLED;
43 }
44 
45 static void host1x_intr_disable_all_syncpt_intrs(struct host1x *host)
46 {
47 	unsigned int i;
48 
49 	for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); ++i) {
50 		host1x_sync_writel(host, 0xffffffffu,
51 			HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i));
52 		host1x_sync_writel(host, 0xffffffffu,
53 			HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
54 	}
55 }
56 
57 static void intr_hw_init(struct host1x *host, u32 cpm)
58 {
59 #if HOST1X_HW < 6
60 	/* disable the ip_busy_timeout. this prevents write drops */
61 	host1x_sync_writel(host, 0, HOST1X_SYNC_IP_BUSY_TIMEOUT);
62 
63 	/*
64 	 * increase the auto-ack timout to the maximum value. 2d will hang
65 	 * otherwise on Tegra2.
66 	 */
67 	host1x_sync_writel(host, 0xff, HOST1X_SYNC_CTXSW_TIMEOUT_CFG);
68 
69 	/* update host clocks per usec */
70 	host1x_sync_writel(host, cpm, HOST1X_SYNC_USEC_CLK);
71 #endif
72 #if HOST1X_HW >= 8
73 	u32 id;
74 
75 	/*
76 	 * Program threshold interrupt destination among 8 lines per VM,
77 	 * per syncpoint. For each group of 32 syncpoints (corresponding to one
78 	 * interrupt status register), direct to one interrupt line, going
79 	 * around in a round robin fashion.
80 	 */
81 	for (id = 0; id < host->info->nb_pts; id++) {
82 		u32 reg_offset = id / 32;
83 		u32 irq_index = reg_offset % host->num_syncpt_irqs;
84 
85 		host1x_sync_writel(host, irq_index, HOST1X_SYNC_SYNCPT_INTR_DEST(id));
86 	}
87 #endif
88 }
89 
90 static int
91 host1x_intr_init_host_sync(struct host1x *host, u32 cpm)
92 {
93 	int err, i;
94 	struct host1x_intr_irq_data *irq_data;
95 
96 	irq_data = devm_kcalloc(host->dev, host->num_syncpt_irqs, sizeof(irq_data[0]), GFP_KERNEL);
97 	if (!irq_data)
98 		return -ENOMEM;
99 
100 	host1x_hw_intr_disable_all_syncpt_intrs(host);
101 
102 	for (i = 0; i < host->num_syncpt_irqs; i++) {
103 		irq_data[i].host = host;
104 		irq_data[i].offset = i;
105 
106 		err = devm_request_irq(host->dev, host->syncpt_irqs[i],
107 				       syncpt_thresh_isr, IRQF_SHARED,
108 				       "host1x_syncpt", &irq_data[i]);
109 		if (err < 0)
110 			return err;
111 	}
112 
113 	intr_hw_init(host, cpm);
114 
115 	return 0;
116 }
117 
118 static void host1x_intr_set_syncpt_threshold(struct host1x *host,
119 					      unsigned int id,
120 					      u32 thresh)
121 {
122 	host1x_sync_writel(host, thresh, HOST1X_SYNC_SYNCPT_INT_THRESH(id));
123 }
124 
125 static void host1x_intr_enable_syncpt_intr(struct host1x *host,
126 					    unsigned int id)
127 {
128 	host1x_sync_writel(host, BIT(id % 32),
129 		HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id / 32));
130 }
131 
132 static void host1x_intr_disable_syncpt_intr(struct host1x *host,
133 					     unsigned int id)
134 {
135 	host1x_sync_writel(host, BIT(id % 32),
136 		HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id / 32));
137 	host1x_sync_writel(host, BIT(id % 32),
138 		HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32));
139 }
140 
141 static const struct host1x_intr_ops host1x_intr_ops = {
142 	.init_host_sync = host1x_intr_init_host_sync,
143 	.set_syncpt_threshold = host1x_intr_set_syncpt_threshold,
144 	.enable_syncpt_intr = host1x_intr_enable_syncpt_intr,
145 	.disable_syncpt_intr = host1x_intr_disable_syncpt_intr,
146 	.disable_all_syncpt_intrs = host1x_intr_disable_all_syncpt_intrs,
147 };
148