1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver. |
3 | * |
4 | * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> |
5 | * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> |
6 | * Copyright (c) a lot of people too. Please respect their work. |
7 | * |
8 | * See MAINTAINERS file for support contact information. |
9 | */ |
10 | |
11 | #include <linux/delay.h> |
12 | #include <linux/firmware.h> |
13 | |
14 | #include "r8169_firmware.h" |
15 | |
16 | enum rtl_fw_opcode { |
17 | PHY_READ = 0x0, |
18 | PHY_DATA_OR = 0x1, |
19 | PHY_DATA_AND = 0x2, |
20 | PHY_BJMPN = 0x3, |
21 | PHY_MDIO_CHG = 0x4, |
22 | PHY_CLEAR_READCOUNT = 0x7, |
23 | PHY_WRITE = 0x8, |
24 | PHY_READCOUNT_EQ_SKIP = 0x9, |
25 | PHY_COMP_EQ_SKIPN = 0xa, |
26 | PHY_COMP_NEQ_SKIPN = 0xb, |
27 | PHY_WRITE_PREVIOUS = 0xc, |
28 | PHY_SKIPN = 0xd, |
29 | PHY_DELAY_MS = 0xe, |
30 | }; |
31 | |
32 | struct fw_info { |
33 | u32 magic; |
34 | char version[RTL_VER_SIZE]; |
35 | __le32 fw_start; |
36 | __le32 fw_len; |
37 | u8 chksum; |
38 | } __packed; |
39 | |
40 | #define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0]) |
41 | |
42 | static bool rtl_fw_format_ok(struct rtl_fw *rtl_fw) |
43 | { |
44 | const struct firmware *fw = rtl_fw->fw; |
45 | struct fw_info *fw_info = (struct fw_info *)fw->data; |
46 | struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; |
47 | |
48 | if (fw->size < FW_OPCODE_SIZE) |
49 | return false; |
50 | |
51 | if (!fw_info->magic) { |
52 | size_t i, size, start; |
53 | u8 checksum = 0; |
54 | |
55 | if (fw->size < sizeof(*fw_info)) |
56 | return false; |
57 | |
58 | for (i = 0; i < fw->size; i++) |
59 | checksum += fw->data[i]; |
60 | if (checksum != 0) |
61 | return false; |
62 | |
63 | start = le32_to_cpu(fw_info->fw_start); |
64 | if (start > fw->size) |
65 | return false; |
66 | |
67 | size = le32_to_cpu(fw_info->fw_len); |
68 | if (size > (fw->size - start) / FW_OPCODE_SIZE) |
69 | return false; |
70 | |
71 | strscpy(rtl_fw->version, fw_info->version, RTL_VER_SIZE); |
72 | |
73 | pa->code = (__le32 *)(fw->data + start); |
74 | pa->size = size; |
75 | } else { |
76 | if (fw->size % FW_OPCODE_SIZE) |
77 | return false; |
78 | |
79 | strscpy(rtl_fw->version, rtl_fw->fw_name, RTL_VER_SIZE); |
80 | |
81 | pa->code = (__le32 *)fw->data; |
82 | pa->size = fw->size / FW_OPCODE_SIZE; |
83 | } |
84 | |
85 | return true; |
86 | } |
87 | |
88 | static bool rtl_fw_data_ok(struct rtl_fw *rtl_fw) |
89 | { |
90 | struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; |
91 | size_t index; |
92 | |
93 | for (index = 0; index < pa->size; index++) { |
94 | u32 action = le32_to_cpu(pa->code[index]); |
95 | u32 val = action & 0x0000ffff; |
96 | u32 regno = (action & 0x0fff0000) >> 16; |
97 | |
98 | switch (action >> 28) { |
99 | case PHY_READ: |
100 | case PHY_DATA_OR: |
101 | case PHY_DATA_AND: |
102 | case PHY_CLEAR_READCOUNT: |
103 | case PHY_WRITE: |
104 | case PHY_WRITE_PREVIOUS: |
105 | case PHY_DELAY_MS: |
106 | break; |
107 | |
108 | case PHY_MDIO_CHG: |
109 | if (val > 1) |
110 | goto out; |
111 | break; |
112 | |
113 | case PHY_BJMPN: |
114 | if (regno > index) |
115 | goto out; |
116 | break; |
117 | case PHY_READCOUNT_EQ_SKIP: |
118 | if (index + 2 >= pa->size) |
119 | goto out; |
120 | break; |
121 | case PHY_COMP_EQ_SKIPN: |
122 | case PHY_COMP_NEQ_SKIPN: |
123 | case PHY_SKIPN: |
124 | if (index + 1 + regno >= pa->size) |
125 | goto out; |
126 | break; |
127 | |
128 | default: |
129 | dev_err(rtl_fw->dev, "Invalid action 0x%08x\n" , action); |
130 | return false; |
131 | } |
132 | } |
133 | |
134 | return true; |
135 | out: |
136 | dev_err(rtl_fw->dev, "Out of range of firmware\n" ); |
137 | return false; |
138 | } |
139 | |
140 | void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw) |
141 | { |
142 | struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; |
143 | rtl_fw_write_t fw_write = rtl_fw->phy_write; |
144 | rtl_fw_read_t fw_read = rtl_fw->phy_read; |
145 | int predata = 0, count = 0; |
146 | size_t index; |
147 | |
148 | for (index = 0; index < pa->size; index++) { |
149 | u32 action = le32_to_cpu(pa->code[index]); |
150 | u32 data = action & 0x0000ffff; |
151 | u32 regno = (action & 0x0fff0000) >> 16; |
152 | enum rtl_fw_opcode opcode = action >> 28; |
153 | |
154 | switch (opcode) { |
155 | case PHY_READ: |
156 | predata = fw_read(tp, regno); |
157 | count++; |
158 | break; |
159 | case PHY_DATA_OR: |
160 | predata |= data; |
161 | break; |
162 | case PHY_DATA_AND: |
163 | predata &= data; |
164 | break; |
165 | case PHY_BJMPN: |
166 | index -= (regno + 1); |
167 | break; |
168 | case PHY_MDIO_CHG: |
169 | if (data) { |
170 | fw_write = rtl_fw->mac_mcu_write; |
171 | fw_read = rtl_fw->mac_mcu_read; |
172 | } else { |
173 | fw_write = rtl_fw->phy_write; |
174 | fw_read = rtl_fw->phy_read; |
175 | } |
176 | |
177 | break; |
178 | case PHY_CLEAR_READCOUNT: |
179 | count = 0; |
180 | break; |
181 | case PHY_WRITE: |
182 | fw_write(tp, regno, data); |
183 | break; |
184 | case PHY_READCOUNT_EQ_SKIP: |
185 | if (count == data) |
186 | index++; |
187 | break; |
188 | case PHY_COMP_EQ_SKIPN: |
189 | if (predata == data) |
190 | index += regno; |
191 | break; |
192 | case PHY_COMP_NEQ_SKIPN: |
193 | if (predata != data) |
194 | index += regno; |
195 | break; |
196 | case PHY_WRITE_PREVIOUS: |
197 | fw_write(tp, regno, predata); |
198 | break; |
199 | case PHY_SKIPN: |
200 | index += regno; |
201 | break; |
202 | case PHY_DELAY_MS: |
203 | msleep(msecs: data); |
204 | break; |
205 | } |
206 | } |
207 | } |
208 | |
209 | void rtl_fw_release_firmware(struct rtl_fw *rtl_fw) |
210 | { |
211 | release_firmware(fw: rtl_fw->fw); |
212 | } |
213 | |
214 | int rtl_fw_request_firmware(struct rtl_fw *rtl_fw) |
215 | { |
216 | int rc; |
217 | |
218 | rc = request_firmware(fw: &rtl_fw->fw, name: rtl_fw->fw_name, device: rtl_fw->dev); |
219 | if (rc < 0) |
220 | goto out; |
221 | |
222 | if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) { |
223 | release_firmware(fw: rtl_fw->fw); |
224 | rc = -EINVAL; |
225 | goto out; |
226 | } |
227 | |
228 | return 0; |
229 | out: |
230 | dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n" , |
231 | rtl_fw->fw_name, rc); |
232 | return rc; |
233 | } |
234 | |