1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2015, NVIDIA Corporation. |
4 | */ |
5 | |
6 | #include <linux/platform_device.h> |
7 | #include <linux/dma-mapping.h> |
8 | #include <linux/firmware.h> |
9 | #include <linux/pci_ids.h> |
10 | #include <linux/iopoll.h> |
11 | |
12 | #include "falcon.h" |
13 | #include "drm.h" |
14 | |
15 | enum falcon_memory { |
16 | FALCON_MEMORY_IMEM, |
17 | FALCON_MEMORY_DATA, |
18 | }; |
19 | |
20 | static void falcon_writel(struct falcon *falcon, u32 value, u32 offset) |
21 | { |
22 | writel(val: value, addr: falcon->regs + offset); |
23 | } |
24 | |
25 | int falcon_wait_idle(struct falcon *falcon) |
26 | { |
27 | u32 value; |
28 | |
29 | return readl_poll_timeout(falcon->regs + FALCON_IDLESTATE, value, |
30 | (value == 0), 10, 100000); |
31 | } |
32 | |
33 | static int falcon_dma_wait_idle(struct falcon *falcon) |
34 | { |
35 | u32 value; |
36 | |
37 | return readl_poll_timeout(falcon->regs + FALCON_DMATRFCMD, value, |
38 | (value & FALCON_DMATRFCMD_IDLE), 10, 100000); |
39 | } |
40 | |
41 | static int falcon_copy_chunk(struct falcon *falcon, |
42 | phys_addr_t base, |
43 | unsigned long offset, |
44 | enum falcon_memory target) |
45 | { |
46 | u32 cmd = FALCON_DMATRFCMD_SIZE_256B; |
47 | |
48 | if (target == FALCON_MEMORY_IMEM) |
49 | cmd |= FALCON_DMATRFCMD_IMEM; |
50 | |
51 | /* |
52 | * Use second DMA context (i.e. the one for firmware). Strictly |
53 | * speaking, at this point both DMA contexts point to the firmware |
54 | * stream ID, but this register's value will be reused by the firmware |
55 | * for later DMA transactions, so we need to use the correct value. |
56 | */ |
57 | cmd |= FALCON_DMATRFCMD_DMACTX(1); |
58 | |
59 | falcon_writel(falcon, value: offset, FALCON_DMATRFMOFFS); |
60 | falcon_writel(falcon, value: base, FALCON_DMATRFFBOFFS); |
61 | falcon_writel(falcon, value: cmd, FALCON_DMATRFCMD); |
62 | |
63 | return falcon_dma_wait_idle(falcon); |
64 | } |
65 | |
66 | static void falcon_copy_firmware_image(struct falcon *falcon, |
67 | const struct firmware *firmware) |
68 | { |
69 | u32 *virt = falcon->firmware.virt; |
70 | size_t i; |
71 | |
72 | /* copy the whole thing taking into account endianness */ |
73 | for (i = 0; i < firmware->size / sizeof(u32); i++) |
74 | virt[i] = le32_to_cpu(((__le32 *)firmware->data)[i]); |
75 | } |
76 | |
77 | static int falcon_parse_firmware_image(struct falcon *falcon) |
78 | { |
79 | struct falcon_fw_bin_header_v1 *bin = (void *)falcon->firmware.virt; |
80 | struct falcon_fw_os_header_v1 *os; |
81 | |
82 | /* endian problems would show up right here */ |
83 | if (bin->magic != PCI_VENDOR_ID_NVIDIA && bin->magic != 0x10fe) { |
84 | dev_err(falcon->dev, "incorrect firmware magic\n" ); |
85 | return -EINVAL; |
86 | } |
87 | |
88 | /* currently only version 1 is supported */ |
89 | if (bin->version != 1) { |
90 | dev_err(falcon->dev, "unsupported firmware version\n" ); |
91 | return -EINVAL; |
92 | } |
93 | |
94 | /* check that the firmware size is consistent */ |
95 | if (bin->size > falcon->firmware.size) { |
96 | dev_err(falcon->dev, "firmware image size inconsistency\n" ); |
97 | return -EINVAL; |
98 | } |
99 | |
100 | os = falcon->firmware.virt + bin->os_header_offset; |
101 | |
102 | falcon->firmware.bin_data.size = bin->os_size; |
103 | falcon->firmware.bin_data.offset = bin->os_data_offset; |
104 | falcon->firmware.code.offset = os->code_offset; |
105 | falcon->firmware.code.size = os->code_size; |
106 | falcon->firmware.data.offset = os->data_offset; |
107 | falcon->firmware.data.size = os->data_size; |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | int falcon_read_firmware(struct falcon *falcon, const char *name) |
113 | { |
114 | int err; |
115 | |
116 | /* request_firmware prints error if it fails */ |
117 | err = request_firmware(fw: &falcon->firmware.firmware, name, device: falcon->dev); |
118 | if (err < 0) |
119 | return err; |
120 | |
121 | falcon->firmware.size = falcon->firmware.firmware->size; |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | int falcon_load_firmware(struct falcon *falcon) |
127 | { |
128 | const struct firmware *firmware = falcon->firmware.firmware; |
129 | int err; |
130 | |
131 | /* copy firmware image into local area. this also ensures endianness */ |
132 | falcon_copy_firmware_image(falcon, firmware); |
133 | |
134 | /* parse the image data */ |
135 | err = falcon_parse_firmware_image(falcon); |
136 | if (err < 0) { |
137 | dev_err(falcon->dev, "failed to parse firmware image\n" ); |
138 | return err; |
139 | } |
140 | |
141 | release_firmware(fw: firmware); |
142 | falcon->firmware.firmware = NULL; |
143 | |
144 | return 0; |
145 | } |
146 | |
147 | int falcon_init(struct falcon *falcon) |
148 | { |
149 | falcon->firmware.virt = NULL; |
150 | |
151 | return 0; |
152 | } |
153 | |
154 | void falcon_exit(struct falcon *falcon) |
155 | { |
156 | if (falcon->firmware.firmware) |
157 | release_firmware(fw: falcon->firmware.firmware); |
158 | } |
159 | |
160 | int falcon_boot(struct falcon *falcon) |
161 | { |
162 | unsigned long offset; |
163 | u32 value; |
164 | int err; |
165 | |
166 | if (!falcon->firmware.virt) |
167 | return -EINVAL; |
168 | |
169 | err = readl_poll_timeout(falcon->regs + FALCON_DMACTL, value, |
170 | (value & (FALCON_DMACTL_IMEM_SCRUBBING | |
171 | FALCON_DMACTL_DMEM_SCRUBBING)) == 0, |
172 | 10, 10000); |
173 | if (err < 0) |
174 | return err; |
175 | |
176 | falcon_writel(falcon, value: 0, FALCON_DMACTL); |
177 | |
178 | /* setup the address of the binary data so Falcon can access it later */ |
179 | falcon_writel(falcon, value: (falcon->firmware.iova + |
180 | falcon->firmware.bin_data.offset) >> 8, |
181 | FALCON_DMATRFBASE); |
182 | |
183 | /* copy the data segment into Falcon internal memory */ |
184 | for (offset = 0; offset < falcon->firmware.data.size; offset += 256) |
185 | falcon_copy_chunk(falcon, |
186 | base: falcon->firmware.data.offset + offset, |
187 | offset, target: FALCON_MEMORY_DATA); |
188 | |
189 | /* copy the code segment into Falcon internal memory */ |
190 | for (offset = 0; offset < falcon->firmware.code.size; offset += 256) |
191 | falcon_copy_chunk(falcon, base: falcon->firmware.code.offset + offset, |
192 | offset, target: FALCON_MEMORY_IMEM); |
193 | |
194 | /* setup falcon interrupts */ |
195 | falcon_writel(falcon, FALCON_IRQMSET_EXT(0xff) | |
196 | FALCON_IRQMSET_SWGEN1 | |
197 | FALCON_IRQMSET_SWGEN0 | |
198 | FALCON_IRQMSET_EXTERR | |
199 | FALCON_IRQMSET_HALT | |
200 | FALCON_IRQMSET_WDTMR, |
201 | FALCON_IRQMSET); |
202 | falcon_writel(falcon, FALCON_IRQDEST_EXT(0xff) | |
203 | FALCON_IRQDEST_SWGEN1 | |
204 | FALCON_IRQDEST_SWGEN0 | |
205 | FALCON_IRQDEST_EXTERR | |
206 | FALCON_IRQDEST_HALT, |
207 | FALCON_IRQDEST); |
208 | |
209 | /* enable interface */ |
210 | falcon_writel(falcon, FALCON_ITFEN_MTHDEN | |
211 | FALCON_ITFEN_CTXEN, |
212 | FALCON_ITFEN); |
213 | |
214 | /* boot falcon */ |
215 | falcon_writel(falcon, value: 0x00000000, FALCON_BOOTVEC); |
216 | falcon_writel(falcon, FALCON_CPUCTL_STARTCPU, FALCON_CPUCTL); |
217 | |
218 | err = falcon_wait_idle(falcon); |
219 | if (err < 0) { |
220 | dev_err(falcon->dev, "Falcon boot failed due to timeout\n" ); |
221 | return err; |
222 | } |
223 | |
224 | return 0; |
225 | } |
226 | |
227 | void falcon_execute_method(struct falcon *falcon, u32 method, u32 data) |
228 | { |
229 | falcon_writel(falcon, value: method >> 2, FALCON_UCLASS_METHOD_OFFSET); |
230 | falcon_writel(falcon, value: data, FALCON_UCLASS_METHOD_DATA); |
231 | } |
232 | |