1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. |
4 | * |
5 | * Contact Information: wlanfae <wlanfae@realtek.com> |
6 | */ |
7 | #include "rtl_core.h" |
8 | #include "r8192E_hw.h" |
9 | #include "table.h" |
10 | #include "r8192E_firmware.h" |
11 | #include "r8192E_cmdpkt.h" |
12 | #include <linux/firmware.h> |
13 | |
14 | static bool _rtl92e_wait_for_fw(struct net_device *dev, u32 mask, u32 timeout) |
15 | { |
16 | unsigned long deadline = jiffies + msecs_to_jiffies(m: timeout); |
17 | |
18 | while (time_before(jiffies, deadline)) { |
19 | if (rtl92e_readl(dev, x: CPU_GEN) & mask) |
20 | return true; |
21 | mdelay(2); |
22 | } |
23 | return false; |
24 | } |
25 | |
26 | static bool _rtl92e_fw_boot_cpu(struct net_device *dev) |
27 | { |
28 | u32 CPU_status = 0; |
29 | |
30 | if (!_rtl92e_wait_for_fw(dev, CPU_GEN_PUT_CODE_OK, timeout: 200)) { |
31 | netdev_err(dev, format: "Firmware download failed.\n" ); |
32 | return false; |
33 | } |
34 | netdev_dbg(dev, "Download Firmware: Put code ok!\n" ); |
35 | |
36 | CPU_status = rtl92e_readl(dev, x: CPU_GEN); |
37 | rtl92e_writeb(dev, x: CPU_GEN, y: (CPU_status | CPU_GEN_PWR_STB_CPU) & 0xff); |
38 | mdelay(1); |
39 | |
40 | if (!_rtl92e_wait_for_fw(dev, CPU_GEN_BOOT_RDY, timeout: 200)) { |
41 | netdev_err(dev, format: "Firmware boot failed.\n" ); |
42 | return false; |
43 | } |
44 | |
45 | netdev_dbg(dev, "Download Firmware: Boot ready!\n" ); |
46 | |
47 | return true; |
48 | } |
49 | |
50 | static bool _rtl92e_fw_check_ready(struct net_device *dev, |
51 | u8 load_fw_status) |
52 | { |
53 | struct r8192_priv *priv = rtllib_priv(dev); |
54 | struct rt_firmware *pfirmware = priv->fw_info; |
55 | bool rt_status = true; |
56 | |
57 | switch (load_fw_status) { |
58 | case FW_INIT_STEP0_BOOT: |
59 | pfirmware->status = FW_STATUS_1_MOVE_BOOT_CODE; |
60 | break; |
61 | |
62 | case FW_INIT_STEP1_MAIN: |
63 | pfirmware->status = FW_STATUS_2_MOVE_MAIN_CODE; |
64 | |
65 | rt_status = _rtl92e_fw_boot_cpu(dev); |
66 | if (rt_status) |
67 | pfirmware->status = FW_STATUS_3_TURNON_CPU; |
68 | else |
69 | netdev_dbg(dev, "_rtl92e_fw_boot_cpu fail!\n" ); |
70 | |
71 | break; |
72 | |
73 | case FW_INIT_STEP2_DATA: |
74 | pfirmware->status = FW_STATUS_4_MOVE_DATA_CODE; |
75 | mdelay(1); |
76 | |
77 | rt_status = _rtl92e_wait_for_fw(dev, CPU_GEN_FIRM_RDY, timeout: 20); |
78 | if (rt_status) |
79 | pfirmware->status = FW_STATUS_5_READY; |
80 | break; |
81 | default: |
82 | rt_status = false; |
83 | netdev_dbg(dev, "Unknown firmware status" ); |
84 | break; |
85 | } |
86 | |
87 | return rt_status; |
88 | } |
89 | |
90 | static bool _rtl92e_fw_prepare(struct net_device *dev, struct rt_fw_blob *blob, |
91 | const char *name, u8 padding) |
92 | { |
93 | const struct firmware *fw; |
94 | int rc, i; |
95 | bool ret = true; |
96 | |
97 | rc = request_firmware(fw: &fw, name, device: &dev->dev); |
98 | if (rc < 0) |
99 | return false; |
100 | |
101 | if (round_up(fw->size, 4) > MAX_FW_SIZE - padding) { |
102 | netdev_err(dev, format: "Firmware image %s too big for the device.\n" , |
103 | name); |
104 | ret = false; |
105 | goto out; |
106 | } |
107 | |
108 | if (padding) |
109 | memset(blob->data, 0, padding); |
110 | if (fw->size % 4) |
111 | memset(blob->data + padding + fw->size, 0, 4); |
112 | memcpy(blob->data + padding, fw->data, fw->size); |
113 | |
114 | blob->size = round_up(fw->size, 4) + padding; |
115 | |
116 | /* Swap endian - firmware is packaged in invalid endiannes*/ |
117 | for (i = padding; i < blob->size; i += 4) { |
118 | u32 *data = (u32 *)(blob->data + i); |
119 | *data = swab32p(p: data); |
120 | } |
121 | out: |
122 | release_firmware(fw); |
123 | return ret; |
124 | } |
125 | |
126 | bool rtl92e_init_fw(struct net_device *dev) |
127 | { |
128 | struct r8192_priv *priv = rtllib_priv(dev); |
129 | bool rt_status = true; |
130 | |
131 | u32 file_length = 0; |
132 | u8 *mapped_file = NULL; |
133 | u8 i = 0; |
134 | enum opt_rst_type rst_opt = OPT_SYSTEM_RESET; |
135 | enum firmware_init_step starting_state = FW_INIT_STEP0_BOOT; |
136 | |
137 | struct rt_firmware *pfirmware = priv->fw_info; |
138 | |
139 | netdev_dbg(dev, " PlatformInitFirmware()==>\n" ); |
140 | |
141 | if (pfirmware->status == FW_STATUS_0_INIT) { |
142 | rst_opt = OPT_SYSTEM_RESET; |
143 | starting_state = FW_INIT_STEP0_BOOT; |
144 | |
145 | } else if (pfirmware->status == FW_STATUS_5_READY) { |
146 | rst_opt = OPT_FIRMWARE_RESET; |
147 | starting_state = FW_INIT_STEP2_DATA; |
148 | } |
149 | |
150 | for (i = starting_state; i <= FW_INIT_STEP2_DATA; i++) { |
151 | if (rst_opt == OPT_SYSTEM_RESET) { |
152 | if (pfirmware->blobs[i].size == 0) { |
153 | const char *fw_name[3] = { |
154 | RTL8192E_BOOT_IMG_FW, |
155 | RTL8192E_MAIN_IMG_FW, |
156 | RTL8192E_DATA_IMG_FW |
157 | }; |
158 | int pad = 0; |
159 | |
160 | if (i == FW_INIT_STEP1_MAIN) |
161 | pad = 128; |
162 | |
163 | if (!_rtl92e_fw_prepare(dev, |
164 | blob: &pfirmware->blobs[i], |
165 | name: fw_name[i], |
166 | padding: pad)) |
167 | goto download_firmware_fail; |
168 | } |
169 | } |
170 | |
171 | mapped_file = pfirmware->blobs[i].data; |
172 | file_length = pfirmware->blobs[i].size; |
173 | |
174 | rt_status = rtl92e_send_cmd_pkt(dev, type: DESC_PACKET_TYPE_INIT, |
175 | data: mapped_file, len: file_length); |
176 | if (!rt_status) |
177 | goto download_firmware_fail; |
178 | |
179 | if (!_rtl92e_fw_check_ready(dev, load_fw_status: i)) |
180 | goto download_firmware_fail; |
181 | } |
182 | |
183 | netdev_dbg(dev, "Firmware Download Success\n" ); |
184 | return rt_status; |
185 | |
186 | download_firmware_fail: |
187 | netdev_err(dev, format: "%s: Failed to initialize firmware.\n" , __func__); |
188 | return false; |
189 | } |
190 | |