GNU Linux-libre 5.10.215-gnu1
[releases.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 <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                 if (!action)
155                         break;
156
157                 switch (opcode) {
158                 case PHY_READ:
159                         predata = fw_read(tp, regno);
160                         count++;
161                         break;
162                 case PHY_DATA_OR:
163                         predata |= data;
164                         break;
165                 case PHY_DATA_AND:
166                         predata &= data;
167                         break;
168                 case PHY_BJMPN:
169                         index -= (regno + 1);
170                         break;
171                 case PHY_MDIO_CHG:
172                         if (data) {
173                                 fw_write = rtl_fw->mac_mcu_write;
174                                 fw_read = rtl_fw->mac_mcu_read;
175                         } else {
176                                 fw_write = rtl_fw->phy_write;
177                                 fw_read = rtl_fw->phy_read;
178                         }
179
180                         break;
181                 case PHY_CLEAR_READCOUNT:
182                         count = 0;
183                         break;
184                 case PHY_WRITE:
185                         fw_write(tp, regno, data);
186                         break;
187                 case PHY_READCOUNT_EQ_SKIP:
188                         if (count == data)
189                                 index++;
190                         break;
191                 case PHY_COMP_EQ_SKIPN:
192                         if (predata == data)
193                                 index += regno;
194                         break;
195                 case PHY_COMP_NEQ_SKIPN:
196                         if (predata != data)
197                                 index += regno;
198                         break;
199                 case PHY_WRITE_PREVIOUS:
200                         fw_write(tp, regno, predata);
201                         break;
202                 case PHY_SKIPN:
203                         index += regno;
204                         break;
205                 case PHY_DELAY_MS:
206                         msleep(data);
207                         break;
208                 }
209         }
210 }
211
212 void rtl_fw_release_firmware(struct rtl_fw *rtl_fw)
213 {
214         release_firmware(rtl_fw->fw);
215 }
216
217 int rtl_fw_request_firmware(struct rtl_fw *rtl_fw)
218 {
219         int rc;
220
221         rc = reject_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev);
222         if (rc < 0)
223                 goto out;
224
225         if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) {
226                 release_firmware(rtl_fw->fw);
227                 rc = -EINVAL;
228                 goto out;
229         }
230
231         return 0;
232 out:
233         dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n",
234                 rtl_fw->fw_name, rc);
235         return rc;
236 }