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