18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> 58c2ecf20Sopenharmony_ci * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> 68c2ecf20Sopenharmony_ci * Copyright (c) a lot of people too. Please respect their work. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * See MAINTAINERS file for support contact information. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/firmware.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "r8169_firmware.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cienum rtl_fw_opcode { 178c2ecf20Sopenharmony_ci PHY_READ = 0x0, 188c2ecf20Sopenharmony_ci PHY_DATA_OR = 0x1, 198c2ecf20Sopenharmony_ci PHY_DATA_AND = 0x2, 208c2ecf20Sopenharmony_ci PHY_BJMPN = 0x3, 218c2ecf20Sopenharmony_ci PHY_MDIO_CHG = 0x4, 228c2ecf20Sopenharmony_ci PHY_CLEAR_READCOUNT = 0x7, 238c2ecf20Sopenharmony_ci PHY_WRITE = 0x8, 248c2ecf20Sopenharmony_ci PHY_READCOUNT_EQ_SKIP = 0x9, 258c2ecf20Sopenharmony_ci PHY_COMP_EQ_SKIPN = 0xa, 268c2ecf20Sopenharmony_ci PHY_COMP_NEQ_SKIPN = 0xb, 278c2ecf20Sopenharmony_ci PHY_WRITE_PREVIOUS = 0xc, 288c2ecf20Sopenharmony_ci PHY_SKIPN = 0xd, 298c2ecf20Sopenharmony_ci PHY_DELAY_MS = 0xe, 308c2ecf20Sopenharmony_ci}; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct fw_info { 338c2ecf20Sopenharmony_ci u32 magic; 348c2ecf20Sopenharmony_ci char version[RTL_VER_SIZE]; 358c2ecf20Sopenharmony_ci __le32 fw_start; 368c2ecf20Sopenharmony_ci __le32 fw_len; 378c2ecf20Sopenharmony_ci u8 chksum; 388c2ecf20Sopenharmony_ci} __packed; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0]) 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic bool rtl_fw_format_ok(struct rtl_fw *rtl_fw) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci const struct firmware *fw = rtl_fw->fw; 458c2ecf20Sopenharmony_ci struct fw_info *fw_info = (struct fw_info *)fw->data; 468c2ecf20Sopenharmony_ci struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (fw->size < FW_OPCODE_SIZE) 498c2ecf20Sopenharmony_ci return false; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (!fw_info->magic) { 528c2ecf20Sopenharmony_ci size_t i, size, start; 538c2ecf20Sopenharmony_ci u8 checksum = 0; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (fw->size < sizeof(*fw_info)) 568c2ecf20Sopenharmony_ci return false; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci for (i = 0; i < fw->size; i++) 598c2ecf20Sopenharmony_ci checksum += fw->data[i]; 608c2ecf20Sopenharmony_ci if (checksum != 0) 618c2ecf20Sopenharmony_ci return false; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci start = le32_to_cpu(fw_info->fw_start); 648c2ecf20Sopenharmony_ci if (start > fw->size) 658c2ecf20Sopenharmony_ci return false; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci size = le32_to_cpu(fw_info->fw_len); 688c2ecf20Sopenharmony_ci if (size > (fw->size - start) / FW_OPCODE_SIZE) 698c2ecf20Sopenharmony_ci return false; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci strscpy(rtl_fw->version, fw_info->version, RTL_VER_SIZE); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci pa->code = (__le32 *)(fw->data + start); 748c2ecf20Sopenharmony_ci pa->size = size; 758c2ecf20Sopenharmony_ci } else { 768c2ecf20Sopenharmony_ci if (fw->size % FW_OPCODE_SIZE) 778c2ecf20Sopenharmony_ci return false; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci strscpy(rtl_fw->version, rtl_fw->fw_name, RTL_VER_SIZE); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci pa->code = (__le32 *)fw->data; 828c2ecf20Sopenharmony_ci pa->size = fw->size / FW_OPCODE_SIZE; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return true; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic bool rtl_fw_data_ok(struct rtl_fw *rtl_fw) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; 918c2ecf20Sopenharmony_ci size_t index; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci for (index = 0; index < pa->size; index++) { 948c2ecf20Sopenharmony_ci u32 action = le32_to_cpu(pa->code[index]); 958c2ecf20Sopenharmony_ci u32 val = action & 0x0000ffff; 968c2ecf20Sopenharmony_ci u32 regno = (action & 0x0fff0000) >> 16; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci switch (action >> 28) { 998c2ecf20Sopenharmony_ci case PHY_READ: 1008c2ecf20Sopenharmony_ci case PHY_DATA_OR: 1018c2ecf20Sopenharmony_ci case PHY_DATA_AND: 1028c2ecf20Sopenharmony_ci case PHY_CLEAR_READCOUNT: 1038c2ecf20Sopenharmony_ci case PHY_WRITE: 1048c2ecf20Sopenharmony_ci case PHY_WRITE_PREVIOUS: 1058c2ecf20Sopenharmony_ci case PHY_DELAY_MS: 1068c2ecf20Sopenharmony_ci break; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci case PHY_MDIO_CHG: 1098c2ecf20Sopenharmony_ci if (val > 1) 1108c2ecf20Sopenharmony_ci goto out; 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci case PHY_BJMPN: 1148c2ecf20Sopenharmony_ci if (regno > index) 1158c2ecf20Sopenharmony_ci goto out; 1168c2ecf20Sopenharmony_ci break; 1178c2ecf20Sopenharmony_ci case PHY_READCOUNT_EQ_SKIP: 1188c2ecf20Sopenharmony_ci if (index + 2 >= pa->size) 1198c2ecf20Sopenharmony_ci goto out; 1208c2ecf20Sopenharmony_ci break; 1218c2ecf20Sopenharmony_ci case PHY_COMP_EQ_SKIPN: 1228c2ecf20Sopenharmony_ci case PHY_COMP_NEQ_SKIPN: 1238c2ecf20Sopenharmony_ci case PHY_SKIPN: 1248c2ecf20Sopenharmony_ci if (index + 1 + regno >= pa->size) 1258c2ecf20Sopenharmony_ci goto out; 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci default: 1298c2ecf20Sopenharmony_ci dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action); 1308c2ecf20Sopenharmony_ci return false; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return true; 1358c2ecf20Sopenharmony_ciout: 1368c2ecf20Sopenharmony_ci dev_err(rtl_fw->dev, "Out of range of firmware\n"); 1378c2ecf20Sopenharmony_ci return false; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_civoid rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; 1438c2ecf20Sopenharmony_ci rtl_fw_write_t fw_write = rtl_fw->phy_write; 1448c2ecf20Sopenharmony_ci rtl_fw_read_t fw_read = rtl_fw->phy_read; 1458c2ecf20Sopenharmony_ci int predata = 0, count = 0; 1468c2ecf20Sopenharmony_ci size_t index; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (index = 0; index < pa->size; index++) { 1498c2ecf20Sopenharmony_ci u32 action = le32_to_cpu(pa->code[index]); 1508c2ecf20Sopenharmony_ci u32 data = action & 0x0000ffff; 1518c2ecf20Sopenharmony_ci u32 regno = (action & 0x0fff0000) >> 16; 1528c2ecf20Sopenharmony_ci enum rtl_fw_opcode opcode = action >> 28; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (!action) 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci switch (opcode) { 1588c2ecf20Sopenharmony_ci case PHY_READ: 1598c2ecf20Sopenharmony_ci predata = fw_read(tp, regno); 1608c2ecf20Sopenharmony_ci count++; 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci case PHY_DATA_OR: 1638c2ecf20Sopenharmony_ci predata |= data; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci case PHY_DATA_AND: 1668c2ecf20Sopenharmony_ci predata &= data; 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci case PHY_BJMPN: 1698c2ecf20Sopenharmony_ci index -= (regno + 1); 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case PHY_MDIO_CHG: 1728c2ecf20Sopenharmony_ci if (data) { 1738c2ecf20Sopenharmony_ci fw_write = rtl_fw->mac_mcu_write; 1748c2ecf20Sopenharmony_ci fw_read = rtl_fw->mac_mcu_read; 1758c2ecf20Sopenharmony_ci } else { 1768c2ecf20Sopenharmony_ci fw_write = rtl_fw->phy_write; 1778c2ecf20Sopenharmony_ci fw_read = rtl_fw->phy_read; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci case PHY_CLEAR_READCOUNT: 1828c2ecf20Sopenharmony_ci count = 0; 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci case PHY_WRITE: 1858c2ecf20Sopenharmony_ci fw_write(tp, regno, data); 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci case PHY_READCOUNT_EQ_SKIP: 1888c2ecf20Sopenharmony_ci if (count == data) 1898c2ecf20Sopenharmony_ci index++; 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci case PHY_COMP_EQ_SKIPN: 1928c2ecf20Sopenharmony_ci if (predata == data) 1938c2ecf20Sopenharmony_ci index += regno; 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci case PHY_COMP_NEQ_SKIPN: 1968c2ecf20Sopenharmony_ci if (predata != data) 1978c2ecf20Sopenharmony_ci index += regno; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case PHY_WRITE_PREVIOUS: 2008c2ecf20Sopenharmony_ci fw_write(tp, regno, predata); 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case PHY_SKIPN: 2038c2ecf20Sopenharmony_ci index += regno; 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci case PHY_DELAY_MS: 2068c2ecf20Sopenharmony_ci msleep(data); 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_civoid rtl_fw_release_firmware(struct rtl_fw *rtl_fw) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci release_firmware(rtl_fw->fw); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciint rtl_fw_request_firmware(struct rtl_fw *rtl_fw) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int rc; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev); 2228c2ecf20Sopenharmony_ci if (rc < 0) 2238c2ecf20Sopenharmony_ci goto out; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) { 2268c2ecf20Sopenharmony_ci release_firmware(rtl_fw->fw); 2278c2ecf20Sopenharmony_ci rc = -EINVAL; 2288c2ecf20Sopenharmony_ci goto out; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ciout: 2338c2ecf20Sopenharmony_ci dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n", 2348c2ecf20Sopenharmony_ci rtl_fw->fw_name, rc); 2358c2ecf20Sopenharmony_ci return rc; 2368c2ecf20Sopenharmony_ci} 237