1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2075affb1SQianyu Gong /*
3075affb1SQianyu Gong * Copyright 2016 Freescale Semiconductor, Inc.
4075affb1SQianyu Gong */
5075affb1SQianyu Gong #include <asm/io.h>
6075affb1SQianyu Gong #include <fsl_qe.h> /* For struct qe_firmware */
7075affb1SQianyu Gong
8075affb1SQianyu Gong #ifdef CONFIG_SYS_DPAA_FMAN
9075affb1SQianyu Gong /**
10075affb1SQianyu Gong * fdt_fixup_fman_firmware -- insert the Fman firmware into the device tree
11075affb1SQianyu Gong *
12075affb1SQianyu Gong * The binding for an Fman firmware node is documented in
13075affb1SQianyu Gong * Documentation/powerpc/dts-bindings/fsl/dpaa/fman.txt. This node contains
14075affb1SQianyu Gong * the actual Fman firmware binary data. The operating system is expected to
15075affb1SQianyu Gong * be able to parse the binary data to determine any attributes it needs.
16075affb1SQianyu Gong */
fdt_fixup_fman_firmware(void * blob)17075affb1SQianyu Gong void fdt_fixup_fman_firmware(void *blob)
18075affb1SQianyu Gong {
19075affb1SQianyu Gong int rc, fmnode, fwnode = -1;
20075affb1SQianyu Gong uint32_t phandle;
21075affb1SQianyu Gong struct qe_firmware *fmanfw;
22075affb1SQianyu Gong const struct qe_header *hdr;
23075affb1SQianyu Gong unsigned int length;
24075affb1SQianyu Gong uint32_t crc;
25075affb1SQianyu Gong const char *p;
26075affb1SQianyu Gong
27075affb1SQianyu Gong /* The first Fman we find will contain the actual firmware. */
28075affb1SQianyu Gong fmnode = fdt_node_offset_by_compatible(blob, -1, "fsl,fman");
29075affb1SQianyu Gong if (fmnode < 0)
30075affb1SQianyu Gong /* Exit silently if there are no Fman devices */
31075affb1SQianyu Gong return;
32075affb1SQianyu Gong
33075affb1SQianyu Gong /* If we already have a firmware node, then also exit silently. */
34075affb1SQianyu Gong if (fdt_node_offset_by_compatible(blob, -1, "fsl,fman-firmware") > 0)
35075affb1SQianyu Gong return;
36075affb1SQianyu Gong
37075affb1SQianyu Gong /* If the environment variable is not set, then exit silently */
3800caae6dSSimon Glass p = env_get("fman_ucode");
39075affb1SQianyu Gong if (!p)
40075affb1SQianyu Gong return;
41075affb1SQianyu Gong
42075affb1SQianyu Gong fmanfw = (struct qe_firmware *)simple_strtoul(p, NULL, 16);
43075affb1SQianyu Gong if (!fmanfw)
44075affb1SQianyu Gong return;
45075affb1SQianyu Gong
46075affb1SQianyu Gong hdr = &fmanfw->header;
476fc9535fSQianyu Gong length = fdt32_to_cpu(hdr->length);
48075affb1SQianyu Gong
49075affb1SQianyu Gong /* Verify the firmware. */
50075affb1SQianyu Gong if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
51075affb1SQianyu Gong (hdr->magic[2] != 'F')) {
52075affb1SQianyu Gong printf("Data at %p is not an Fman firmware\n", fmanfw);
53075affb1SQianyu Gong return;
54075affb1SQianyu Gong }
55075affb1SQianyu Gong
56075affb1SQianyu Gong if (length > CONFIG_SYS_QE_FMAN_FW_LENGTH) {
57075affb1SQianyu Gong printf("Fman firmware at %p is too large (size=%u)\n",
58075affb1SQianyu Gong fmanfw, length);
59075affb1SQianyu Gong return;
60075affb1SQianyu Gong }
61075affb1SQianyu Gong
62075affb1SQianyu Gong length -= sizeof(u32); /* Subtract the size of the CRC */
636fc9535fSQianyu Gong crc = fdt32_to_cpu(*(u32 *)((void *)fmanfw + length));
64075affb1SQianyu Gong if (crc != crc32_no_comp(0, (void *)fmanfw, length)) {
65075affb1SQianyu Gong printf("Fman firmware at %p has invalid CRC\n", fmanfw);
66075affb1SQianyu Gong return;
67075affb1SQianyu Gong }
68075affb1SQianyu Gong
696fc9535fSQianyu Gong length += sizeof(u32);
706fc9535fSQianyu Gong
71075affb1SQianyu Gong /* Increase the size of the fdt to make room for the node. */
726fc9535fSQianyu Gong rc = fdt_increase_size(blob, length);
73075affb1SQianyu Gong if (rc < 0) {
74075affb1SQianyu Gong printf("Unable to make room for Fman firmware: %s\n",
75075affb1SQianyu Gong fdt_strerror(rc));
76075affb1SQianyu Gong return;
77075affb1SQianyu Gong }
78075affb1SQianyu Gong
79075affb1SQianyu Gong /* Create the firmware node. */
80075affb1SQianyu Gong fwnode = fdt_add_subnode(blob, fmnode, "fman-firmware");
81075affb1SQianyu Gong if (fwnode < 0) {
82075affb1SQianyu Gong char s[64];
83075affb1SQianyu Gong fdt_get_path(blob, fmnode, s, sizeof(s));
84075affb1SQianyu Gong printf("Could not add firmware node to %s: %s\n", s,
85075affb1SQianyu Gong fdt_strerror(fwnode));
86075affb1SQianyu Gong return;
87075affb1SQianyu Gong }
88075affb1SQianyu Gong rc = fdt_setprop_string(blob, fwnode, "compatible",
89075affb1SQianyu Gong "fsl,fman-firmware");
90075affb1SQianyu Gong if (rc < 0) {
91075affb1SQianyu Gong char s[64];
92075affb1SQianyu Gong fdt_get_path(blob, fwnode, s, sizeof(s));
93075affb1SQianyu Gong printf("Could not add compatible property to node %s: %s\n", s,
94075affb1SQianyu Gong fdt_strerror(rc));
95075affb1SQianyu Gong return;
96075affb1SQianyu Gong }
97075affb1SQianyu Gong phandle = fdt_create_phandle(blob, fwnode);
98075affb1SQianyu Gong if (!phandle) {
99075affb1SQianyu Gong char s[64];
100075affb1SQianyu Gong fdt_get_path(blob, fwnode, s, sizeof(s));
101075affb1SQianyu Gong printf("Could not add phandle property to node %s: %s\n", s,
102075affb1SQianyu Gong fdt_strerror(rc));
103075affb1SQianyu Gong return;
104075affb1SQianyu Gong }
1056fc9535fSQianyu Gong rc = fdt_setprop(blob, fwnode, "fsl,firmware", fmanfw, length);
106075affb1SQianyu Gong if (rc < 0) {
107075affb1SQianyu Gong char s[64];
108075affb1SQianyu Gong fdt_get_path(blob, fwnode, s, sizeof(s));
109075affb1SQianyu Gong printf("Could not add firmware property to node %s: %s\n", s,
110075affb1SQianyu Gong fdt_strerror(rc));
111075affb1SQianyu Gong return;
112075affb1SQianyu Gong }
113075affb1SQianyu Gong
114075affb1SQianyu Gong /* Find all other Fman nodes and point them to the firmware node. */
115075affb1SQianyu Gong while ((fmnode = fdt_node_offset_by_compatible(blob, fmnode,
116075affb1SQianyu Gong "fsl,fman")) > 0) {
117075affb1SQianyu Gong rc = fdt_setprop_cell(blob, fmnode, "fsl,firmware-phandle",
118075affb1SQianyu Gong phandle);
119075affb1SQianyu Gong if (rc < 0) {
120075affb1SQianyu Gong char s[64];
121075affb1SQianyu Gong fdt_get_path(blob, fmnode, s, sizeof(s));
122075affb1SQianyu Gong printf("Could not add pointer property to node %s: %s\n",
123075affb1SQianyu Gong s, fdt_strerror(rc));
124075affb1SQianyu Gong return;
125075affb1SQianyu Gong }
126075affb1SQianyu Gong }
127075affb1SQianyu Gong }
128075affb1SQianyu Gong #endif
129