]> Git Repo - linux.git/blob - drivers/net/ethernet/realtek/r8169_firmware.c
Linux 6.14-rc3
[linux.git] / drivers / net / ethernet / realtek / r8169_firmware.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver.
3  *
4  * Copyright (c) 2002 ShuChen <[email protected]>
5  * Copyright (c) 2003 - 2007 Francois Romieu <[email protected]>
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(data);
204                         break;
205                 }
206         }
207 }
208
209 void rtl_fw_release_firmware(struct rtl_fw *rtl_fw)
210 {
211         release_firmware(rtl_fw->fw);
212 }
213
214 int rtl_fw_request_firmware(struct rtl_fw *rtl_fw)
215 {
216         int rc;
217
218         rc = firmware_request_nowarn(&rtl_fw->fw, rtl_fw->fw_name, 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(rtl_fw->fw);
224                 rc = -EINVAL;
225                 goto out;
226         }
227
228         return 0;
229 out:
230         dev_warn(rtl_fw->dev, "Unable to load firmware %s (%d)\n",
231                  rtl_fw->fw_name, rc);
232         return rc;
233 }
This page took 0.040403 seconds and 4 git commands to generate.