Linux-libre 5.4.47-gnu
[librecmc/linux-libre.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(typeof(*((struct rtl_fw_phy_action *)0)->code))
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 regno = (action & 0x0fff0000) >> 16;
96
97                 switch (action >> 28) {
98                 case PHY_READ:
99                 case PHY_DATA_OR:
100                 case PHY_DATA_AND:
101                 case PHY_MDIO_CHG:
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_BJMPN:
109                         if (regno > index)
110                                 goto out;
111                         break;
112                 case PHY_READCOUNT_EQ_SKIP:
113                         if (index + 2 >= pa->size)
114                                 goto out;
115                         break;
116                 case PHY_COMP_EQ_SKIPN:
117                 case PHY_COMP_NEQ_SKIPN:
118                 case PHY_SKIPN:
119                         if (index + 1 + regno >= pa->size)
120                                 goto out;
121                         break;
122
123                 default:
124                         dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action);
125                         return false;
126                 }
127         }
128
129         return true;
130 out:
131         dev_err(rtl_fw->dev, "Out of range of firmware\n");
132         return false;
133 }
134
135 void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
136 {
137         struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
138         rtl_fw_write_t fw_write = rtl_fw->phy_write;
139         rtl_fw_read_t fw_read = rtl_fw->phy_read;
140         int predata = 0, count = 0;
141         size_t index;
142
143         for (index = 0; index < pa->size; index++) {
144                 u32 action = le32_to_cpu(pa->code[index]);
145                 u32 data = action & 0x0000ffff;
146                 u32 regno = (action & 0x0fff0000) >> 16;
147                 enum rtl_fw_opcode opcode = action >> 28;
148
149                 if (!action)
150                         break;
151
152                 switch (opcode) {
153                 case PHY_READ:
154                         predata = fw_read(tp, regno);
155                         count++;
156                         break;
157                 case PHY_DATA_OR:
158                         predata |= data;
159                         break;
160                 case PHY_DATA_AND:
161                         predata &= data;
162                         break;
163                 case PHY_BJMPN:
164                         index -= (regno + 1);
165                         break;
166                 case PHY_MDIO_CHG:
167                         if (data == 0) {
168                                 fw_write = rtl_fw->phy_write;
169                                 fw_read = rtl_fw->phy_read;
170                         } else if (data == 1) {
171                                 fw_write = rtl_fw->mac_mcu_write;
172                                 fw_read = rtl_fw->mac_mcu_read;
173                         }
174
175                         break;
176                 case PHY_CLEAR_READCOUNT:
177                         count = 0;
178                         break;
179                 case PHY_WRITE:
180                         fw_write(tp, regno, data);
181                         break;
182                 case PHY_READCOUNT_EQ_SKIP:
183                         if (count == data)
184                                 index++;
185                         break;
186                 case PHY_COMP_EQ_SKIPN:
187                         if (predata == data)
188                                 index += regno;
189                         break;
190                 case PHY_COMP_NEQ_SKIPN:
191                         if (predata != data)
192                                 index += regno;
193                         break;
194                 case PHY_WRITE_PREVIOUS:
195                         fw_write(tp, regno, predata);
196                         break;
197                 case PHY_SKIPN:
198                         index += regno;
199                         break;
200                 case PHY_DELAY_MS:
201                         mdelay(data);
202                         break;
203                 }
204         }
205 }
206
207 void rtl_fw_release_firmware(struct rtl_fw *rtl_fw)
208 {
209         release_firmware(rtl_fw->fw);
210 }
211
212 int rtl_fw_request_firmware(struct rtl_fw *rtl_fw)
213 {
214         int rc;
215
216         rc = reject_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev);
217         if (rc < 0)
218                 goto out;
219
220         if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) {
221                 release_firmware(rtl_fw->fw);
222                 rc = -EINVAL;
223                 goto out;
224         }
225
226         return 0;
227 out:
228         dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n",
229                 rtl_fw->fw_name, rc);
230         return rc;
231 }