xref: /openbmc/linux/drivers/gpu/drm/tegra/falcon.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28746f659SArto Merilainen /*
38746f659SArto Merilainen  * Copyright (c) 2015, NVIDIA Corporation.
48746f659SArto Merilainen  */
58746f659SArto Merilainen 
68746f659SArto Merilainen #include <linux/platform_device.h>
78746f659SArto Merilainen #include <linux/dma-mapping.h>
88746f659SArto Merilainen #include <linux/firmware.h>
98746f659SArto Merilainen #include <linux/pci_ids.h>
108746f659SArto Merilainen #include <linux/iopoll.h>
118746f659SArto Merilainen 
128746f659SArto Merilainen #include "falcon.h"
138746f659SArto Merilainen #include "drm.h"
148746f659SArto Merilainen 
158746f659SArto Merilainen enum falcon_memory {
168746f659SArto Merilainen 	FALCON_MEMORY_IMEM,
178746f659SArto Merilainen 	FALCON_MEMORY_DATA,
188746f659SArto Merilainen };
198746f659SArto Merilainen 
falcon_writel(struct falcon * falcon,u32 value,u32 offset)208746f659SArto Merilainen static void falcon_writel(struct falcon *falcon, u32 value, u32 offset)
218746f659SArto Merilainen {
228746f659SArto Merilainen 	writel(value, falcon->regs + offset);
238746f659SArto Merilainen }
248746f659SArto Merilainen 
falcon_wait_idle(struct falcon * falcon)258746f659SArto Merilainen int falcon_wait_idle(struct falcon *falcon)
268746f659SArto Merilainen {
278746f659SArto Merilainen 	u32 value;
288746f659SArto Merilainen 
298746f659SArto Merilainen 	return readl_poll_timeout(falcon->regs + FALCON_IDLESTATE, value,
308746f659SArto Merilainen 				  (value == 0), 10, 100000);
318746f659SArto Merilainen }
328746f659SArto Merilainen 
falcon_dma_wait_idle(struct falcon * falcon)338746f659SArto Merilainen static int falcon_dma_wait_idle(struct falcon *falcon)
348746f659SArto Merilainen {
358746f659SArto Merilainen 	u32 value;
368746f659SArto Merilainen 
378746f659SArto Merilainen 	return readl_poll_timeout(falcon->regs + FALCON_DMATRFCMD, value,
388746f659SArto Merilainen 				  (value & FALCON_DMATRFCMD_IDLE), 10, 100000);
398746f659SArto Merilainen }
408746f659SArto Merilainen 
falcon_copy_chunk(struct falcon * falcon,phys_addr_t base,unsigned long offset,enum falcon_memory target)418746f659SArto Merilainen static int falcon_copy_chunk(struct falcon *falcon,
428746f659SArto Merilainen 			     phys_addr_t base,
438746f659SArto Merilainen 			     unsigned long offset,
448746f659SArto Merilainen 			     enum falcon_memory target)
458746f659SArto Merilainen {
468746f659SArto Merilainen 	u32 cmd = FALCON_DMATRFCMD_SIZE_256B;
478746f659SArto Merilainen 
488746f659SArto Merilainen 	if (target == FALCON_MEMORY_IMEM)
498746f659SArto Merilainen 		cmd |= FALCON_DMATRFCMD_IMEM;
508746f659SArto Merilainen 
51*e078d8d6SMikko Perttunen 	/*
52*e078d8d6SMikko Perttunen 	 * Use second DMA context (i.e. the one for firmware). Strictly
53*e078d8d6SMikko Perttunen 	 * speaking, at this point both DMA contexts point to the firmware
54*e078d8d6SMikko Perttunen 	 * stream ID, but this register's value will be reused by the firmware
55*e078d8d6SMikko Perttunen 	 * for later DMA transactions, so we need to use the correct value.
56*e078d8d6SMikko Perttunen 	 */
57*e078d8d6SMikko Perttunen 	cmd |= FALCON_DMATRFCMD_DMACTX(1);
58*e078d8d6SMikko Perttunen 
598746f659SArto Merilainen 	falcon_writel(falcon, offset, FALCON_DMATRFMOFFS);
608746f659SArto Merilainen 	falcon_writel(falcon, base, FALCON_DMATRFFBOFFS);
618746f659SArto Merilainen 	falcon_writel(falcon, cmd, FALCON_DMATRFCMD);
628746f659SArto Merilainen 
638746f659SArto Merilainen 	return falcon_dma_wait_idle(falcon);
648746f659SArto Merilainen }
658746f659SArto Merilainen 
falcon_copy_firmware_image(struct falcon * falcon,const struct firmware * firmware)668746f659SArto Merilainen static void falcon_copy_firmware_image(struct falcon *falcon,
678746f659SArto Merilainen 				       const struct firmware *firmware)
688746f659SArto Merilainen {
69d972d624SThierry Reding 	u32 *virt = falcon->firmware.virt;
708746f659SArto Merilainen 	size_t i;
718746f659SArto Merilainen 
728746f659SArto Merilainen 	/* copy the whole thing taking into account endianness */
738746f659SArto Merilainen 	for (i = 0; i < firmware->size / sizeof(u32); i++)
7416693c1bSJon Hunter 		virt[i] = le32_to_cpu(((__le32 *)firmware->data)[i]);
758746f659SArto Merilainen }
768746f659SArto Merilainen 
falcon_parse_firmware_image(struct falcon * falcon)778746f659SArto Merilainen static int falcon_parse_firmware_image(struct falcon *falcon)
788746f659SArto Merilainen {
79d972d624SThierry Reding 	struct falcon_fw_bin_header_v1 *bin = (void *)falcon->firmware.virt;
808746f659SArto Merilainen 	struct falcon_fw_os_header_v1 *os;
818746f659SArto Merilainen 
828746f659SArto Merilainen 	/* endian problems would show up right here */
8358ef3aebSMikko Perttunen 	if (bin->magic != PCI_VENDOR_ID_NVIDIA && bin->magic != 0x10fe) {
848746f659SArto Merilainen 		dev_err(falcon->dev, "incorrect firmware magic\n");
858746f659SArto Merilainen 		return -EINVAL;
868746f659SArto Merilainen 	}
878746f659SArto Merilainen 
888746f659SArto Merilainen 	/* currently only version 1 is supported */
898746f659SArto Merilainen 	if (bin->version != 1) {
908746f659SArto Merilainen 		dev_err(falcon->dev, "unsupported firmware version\n");
918746f659SArto Merilainen 		return -EINVAL;
928746f659SArto Merilainen 	}
938746f659SArto Merilainen 
948746f659SArto Merilainen 	/* check that the firmware size is consistent */
958746f659SArto Merilainen 	if (bin->size > falcon->firmware.size) {
968746f659SArto Merilainen 		dev_err(falcon->dev, "firmware image size inconsistency\n");
978746f659SArto Merilainen 		return -EINVAL;
988746f659SArto Merilainen 	}
998746f659SArto Merilainen 
100d972d624SThierry Reding 	os = falcon->firmware.virt + bin->os_header_offset;
1018746f659SArto Merilainen 
1028746f659SArto Merilainen 	falcon->firmware.bin_data.size = bin->os_size;
1038746f659SArto Merilainen 	falcon->firmware.bin_data.offset = bin->os_data_offset;
1048746f659SArto Merilainen 	falcon->firmware.code.offset = os->code_offset;
1058746f659SArto Merilainen 	falcon->firmware.code.size = os->code_size;
1068746f659SArto Merilainen 	falcon->firmware.data.offset = os->data_offset;
1078746f659SArto Merilainen 	falcon->firmware.data.size = os->data_size;
1088746f659SArto Merilainen 
1098746f659SArto Merilainen 	return 0;
1108746f659SArto Merilainen }
1118746f659SArto Merilainen 
falcon_read_firmware(struct falcon * falcon,const char * name)1128746f659SArto Merilainen int falcon_read_firmware(struct falcon *falcon, const char *name)
1138746f659SArto Merilainen {
1148746f659SArto Merilainen 	int err;
1158746f659SArto Merilainen 
1168746f659SArto Merilainen 	/* request_firmware prints error if it fails */
1178746f659SArto Merilainen 	err = request_firmware(&falcon->firmware.firmware, name, falcon->dev);
1188746f659SArto Merilainen 	if (err < 0)
1198746f659SArto Merilainen 		return err;
1208746f659SArto Merilainen 
12120e7dce2SThierry Reding 	falcon->firmware.size = falcon->firmware.firmware->size;
12220e7dce2SThierry Reding 
1238746f659SArto Merilainen 	return 0;
1248746f659SArto Merilainen }
1258746f659SArto Merilainen 
falcon_load_firmware(struct falcon * falcon)1268746f659SArto Merilainen int falcon_load_firmware(struct falcon *falcon)
1278746f659SArto Merilainen {
1288746f659SArto Merilainen 	const struct firmware *firmware = falcon->firmware.firmware;
1298746f659SArto Merilainen 	int err;
1308746f659SArto Merilainen 
1318746f659SArto Merilainen 	/* copy firmware image into local area. this also ensures endianness */
1328746f659SArto Merilainen 	falcon_copy_firmware_image(falcon, firmware);
1338746f659SArto Merilainen 
1348746f659SArto Merilainen 	/* parse the image data */
1358746f659SArto Merilainen 	err = falcon_parse_firmware_image(falcon);
1368746f659SArto Merilainen 	if (err < 0) {
1378746f659SArto Merilainen 		dev_err(falcon->dev, "failed to parse firmware image\n");
13820e7dce2SThierry Reding 		return err;
1398746f659SArto Merilainen 	}
1408746f659SArto Merilainen 
1418746f659SArto Merilainen 	release_firmware(firmware);
1428746f659SArto Merilainen 	falcon->firmware.firmware = NULL;
1438746f659SArto Merilainen 
1448746f659SArto Merilainen 	return 0;
1458746f659SArto Merilainen }
1468746f659SArto Merilainen 
falcon_init(struct falcon * falcon)1478746f659SArto Merilainen int falcon_init(struct falcon *falcon)
1488746f659SArto Merilainen {
149d972d624SThierry Reding 	falcon->firmware.virt = NULL;
1508746f659SArto Merilainen 
1518746f659SArto Merilainen 	return 0;
1528746f659SArto Merilainen }
1538746f659SArto Merilainen 
falcon_exit(struct falcon * falcon)1548746f659SArto Merilainen void falcon_exit(struct falcon *falcon)
1558746f659SArto Merilainen {
15620e7dce2SThierry Reding 	if (falcon->firmware.firmware)
1578746f659SArto Merilainen 		release_firmware(falcon->firmware.firmware);
1588746f659SArto Merilainen }
1598746f659SArto Merilainen 
falcon_boot(struct falcon * falcon)1608746f659SArto Merilainen int falcon_boot(struct falcon *falcon)
1618746f659SArto Merilainen {
1628746f659SArto Merilainen 	unsigned long offset;
163b91bf997SThierry Reding 	u32 value;
1648746f659SArto Merilainen 	int err;
1658746f659SArto Merilainen 
166d972d624SThierry Reding 	if (!falcon->firmware.virt)
1678746f659SArto Merilainen 		return -EINVAL;
1688746f659SArto Merilainen 
169b91bf997SThierry Reding 	err = readl_poll_timeout(falcon->regs + FALCON_DMACTL, value,
170b91bf997SThierry Reding 				 (value & (FALCON_DMACTL_IMEM_SCRUBBING |
171b91bf997SThierry Reding 					   FALCON_DMACTL_DMEM_SCRUBBING)) == 0,
172b91bf997SThierry Reding 				 10, 10000);
173b91bf997SThierry Reding 	if (err < 0)
174b91bf997SThierry Reding 		return err;
175b91bf997SThierry Reding 
1768746f659SArto Merilainen 	falcon_writel(falcon, 0, FALCON_DMACTL);
1778746f659SArto Merilainen 
1788746f659SArto Merilainen 	/* setup the address of the binary data so Falcon can access it later */
179d972d624SThierry Reding 	falcon_writel(falcon, (falcon->firmware.iova +
1808746f659SArto Merilainen 			       falcon->firmware.bin_data.offset) >> 8,
1818746f659SArto Merilainen 		      FALCON_DMATRFBASE);
1828746f659SArto Merilainen 
1838746f659SArto Merilainen 	/* copy the data segment into Falcon internal memory */
1848746f659SArto Merilainen 	for (offset = 0; offset < falcon->firmware.data.size; offset += 256)
1858746f659SArto Merilainen 		falcon_copy_chunk(falcon,
1868746f659SArto Merilainen 				  falcon->firmware.data.offset + offset,
1878746f659SArto Merilainen 				  offset, FALCON_MEMORY_DATA);
1888746f659SArto Merilainen 
18958ef3aebSMikko Perttunen 	/* copy the code segment into Falcon internal memory */
19058ef3aebSMikko Perttunen 	for (offset = 0; offset < falcon->firmware.code.size; offset += 256)
19158ef3aebSMikko Perttunen 		falcon_copy_chunk(falcon, falcon->firmware.code.offset + offset,
19258ef3aebSMikko Perttunen 				  offset, FALCON_MEMORY_IMEM);
1938746f659SArto Merilainen 
1948746f659SArto Merilainen 	/* setup falcon interrupts */
1958746f659SArto Merilainen 	falcon_writel(falcon, FALCON_IRQMSET_EXT(0xff) |
1968746f659SArto Merilainen 			      FALCON_IRQMSET_SWGEN1 |
1978746f659SArto Merilainen 			      FALCON_IRQMSET_SWGEN0 |
1988746f659SArto Merilainen 			      FALCON_IRQMSET_EXTERR |
1998746f659SArto Merilainen 			      FALCON_IRQMSET_HALT |
2008746f659SArto Merilainen 			      FALCON_IRQMSET_WDTMR,
2018746f659SArto Merilainen 		      FALCON_IRQMSET);
2028746f659SArto Merilainen 	falcon_writel(falcon, FALCON_IRQDEST_EXT(0xff) |
2038746f659SArto Merilainen 			      FALCON_IRQDEST_SWGEN1 |
2048746f659SArto Merilainen 			      FALCON_IRQDEST_SWGEN0 |
2058746f659SArto Merilainen 			      FALCON_IRQDEST_EXTERR |
2068746f659SArto Merilainen 			      FALCON_IRQDEST_HALT,
2078746f659SArto Merilainen 		      FALCON_IRQDEST);
2088746f659SArto Merilainen 
2098746f659SArto Merilainen 	/* enable interface */
2108746f659SArto Merilainen 	falcon_writel(falcon, FALCON_ITFEN_MTHDEN |
2118746f659SArto Merilainen 			      FALCON_ITFEN_CTXEN,
2128746f659SArto Merilainen 		      FALCON_ITFEN);
2138746f659SArto Merilainen 
2148746f659SArto Merilainen 	/* boot falcon */
2158746f659SArto Merilainen 	falcon_writel(falcon, 0x00000000, FALCON_BOOTVEC);
2168746f659SArto Merilainen 	falcon_writel(falcon, FALCON_CPUCTL_STARTCPU, FALCON_CPUCTL);
2178746f659SArto Merilainen 
2188746f659SArto Merilainen 	err = falcon_wait_idle(falcon);
2198746f659SArto Merilainen 	if (err < 0) {
2208746f659SArto Merilainen 		dev_err(falcon->dev, "Falcon boot failed due to timeout\n");
2218746f659SArto Merilainen 		return err;
2228746f659SArto Merilainen 	}
2238746f659SArto Merilainen 
2248746f659SArto Merilainen 	return 0;
2258746f659SArto Merilainen }
2268746f659SArto Merilainen 
falcon_execute_method(struct falcon * falcon,u32 method,u32 data)2278746f659SArto Merilainen void falcon_execute_method(struct falcon *falcon, u32 method, u32 data)
2288746f659SArto Merilainen {
2298746f659SArto Merilainen 	falcon_writel(falcon, method >> 2, FALCON_UCLASS_METHOD_OFFSET);
2308746f659SArto Merilainen 	falcon_writel(falcon, data, FALCON_UCLASS_METHOD_DATA);
2318746f659SArto Merilainen }
232