162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw> 562306a36Sopenharmony_ci * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com> 662306a36Sopenharmony_ci * Copyright (c) a lot of people too. Please respect their work. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * See MAINTAINERS file for support contact information. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/firmware.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "r8169_firmware.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cienum rtl_fw_opcode { 1762306a36Sopenharmony_ci PHY_READ = 0x0, 1862306a36Sopenharmony_ci PHY_DATA_OR = 0x1, 1962306a36Sopenharmony_ci PHY_DATA_AND = 0x2, 2062306a36Sopenharmony_ci PHY_BJMPN = 0x3, 2162306a36Sopenharmony_ci PHY_MDIO_CHG = 0x4, 2262306a36Sopenharmony_ci PHY_CLEAR_READCOUNT = 0x7, 2362306a36Sopenharmony_ci PHY_WRITE = 0x8, 2462306a36Sopenharmony_ci PHY_READCOUNT_EQ_SKIP = 0x9, 2562306a36Sopenharmony_ci PHY_COMP_EQ_SKIPN = 0xa, 2662306a36Sopenharmony_ci PHY_COMP_NEQ_SKIPN = 0xb, 2762306a36Sopenharmony_ci PHY_WRITE_PREVIOUS = 0xc, 2862306a36Sopenharmony_ci PHY_SKIPN = 0xd, 2962306a36Sopenharmony_ci PHY_DELAY_MS = 0xe, 3062306a36Sopenharmony_ci}; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct fw_info { 3362306a36Sopenharmony_ci u32 magic; 3462306a36Sopenharmony_ci char version[RTL_VER_SIZE]; 3562306a36Sopenharmony_ci __le32 fw_start; 3662306a36Sopenharmony_ci __le32 fw_len; 3762306a36Sopenharmony_ci u8 chksum; 3862306a36Sopenharmony_ci} __packed; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0]) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic bool rtl_fw_format_ok(struct rtl_fw *rtl_fw) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci const struct firmware *fw = rtl_fw->fw; 4562306a36Sopenharmony_ci struct fw_info *fw_info = (struct fw_info *)fw->data; 4662306a36Sopenharmony_ci struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (fw->size < FW_OPCODE_SIZE) 4962306a36Sopenharmony_ci return false; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (!fw_info->magic) { 5262306a36Sopenharmony_ci size_t i, size, start; 5362306a36Sopenharmony_ci u8 checksum = 0; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (fw->size < sizeof(*fw_info)) 5662306a36Sopenharmony_ci return false; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci for (i = 0; i < fw->size; i++) 5962306a36Sopenharmony_ci checksum += fw->data[i]; 6062306a36Sopenharmony_ci if (checksum != 0) 6162306a36Sopenharmony_ci return false; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci start = le32_to_cpu(fw_info->fw_start); 6462306a36Sopenharmony_ci if (start > fw->size) 6562306a36Sopenharmony_ci return false; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci size = le32_to_cpu(fw_info->fw_len); 6862306a36Sopenharmony_ci if (size > (fw->size - start) / FW_OPCODE_SIZE) 6962306a36Sopenharmony_ci return false; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci strscpy(rtl_fw->version, fw_info->version, RTL_VER_SIZE); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci pa->code = (__le32 *)(fw->data + start); 7462306a36Sopenharmony_ci pa->size = size; 7562306a36Sopenharmony_ci } else { 7662306a36Sopenharmony_ci if (fw->size % FW_OPCODE_SIZE) 7762306a36Sopenharmony_ci return false; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci strscpy(rtl_fw->version, rtl_fw->fw_name, RTL_VER_SIZE); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci pa->code = (__le32 *)fw->data; 8262306a36Sopenharmony_ci pa->size = fw->size / FW_OPCODE_SIZE; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return true; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic bool rtl_fw_data_ok(struct rtl_fw *rtl_fw) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; 9162306a36Sopenharmony_ci size_t index; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci for (index = 0; index < pa->size; index++) { 9462306a36Sopenharmony_ci u32 action = le32_to_cpu(pa->code[index]); 9562306a36Sopenharmony_ci u32 val = action & 0x0000ffff; 9662306a36Sopenharmony_ci u32 regno = (action & 0x0fff0000) >> 16; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci switch (action >> 28) { 9962306a36Sopenharmony_ci case PHY_READ: 10062306a36Sopenharmony_ci case PHY_DATA_OR: 10162306a36Sopenharmony_ci case PHY_DATA_AND: 10262306a36Sopenharmony_ci case PHY_CLEAR_READCOUNT: 10362306a36Sopenharmony_ci case PHY_WRITE: 10462306a36Sopenharmony_ci case PHY_WRITE_PREVIOUS: 10562306a36Sopenharmony_ci case PHY_DELAY_MS: 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci case PHY_MDIO_CHG: 10962306a36Sopenharmony_ci if (val > 1) 11062306a36Sopenharmony_ci goto out; 11162306a36Sopenharmony_ci break; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci case PHY_BJMPN: 11462306a36Sopenharmony_ci if (regno > index) 11562306a36Sopenharmony_ci goto out; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci case PHY_READCOUNT_EQ_SKIP: 11862306a36Sopenharmony_ci if (index + 2 >= pa->size) 11962306a36Sopenharmony_ci goto out; 12062306a36Sopenharmony_ci break; 12162306a36Sopenharmony_ci case PHY_COMP_EQ_SKIPN: 12262306a36Sopenharmony_ci case PHY_COMP_NEQ_SKIPN: 12362306a36Sopenharmony_ci case PHY_SKIPN: 12462306a36Sopenharmony_ci if (index + 1 + regno >= pa->size) 12562306a36Sopenharmony_ci goto out; 12662306a36Sopenharmony_ci break; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci default: 12962306a36Sopenharmony_ci dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action); 13062306a36Sopenharmony_ci return false; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return true; 13562306a36Sopenharmony_ciout: 13662306a36Sopenharmony_ci dev_err(rtl_fw->dev, "Out of range of firmware\n"); 13762306a36Sopenharmony_ci return false; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_civoid rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct rtl_fw_phy_action *pa = &rtl_fw->phy_action; 14362306a36Sopenharmony_ci rtl_fw_write_t fw_write = rtl_fw->phy_write; 14462306a36Sopenharmony_ci rtl_fw_read_t fw_read = rtl_fw->phy_read; 14562306a36Sopenharmony_ci int predata = 0, count = 0; 14662306a36Sopenharmony_ci size_t index; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci for (index = 0; index < pa->size; index++) { 14962306a36Sopenharmony_ci u32 action = le32_to_cpu(pa->code[index]); 15062306a36Sopenharmony_ci u32 data = action & 0x0000ffff; 15162306a36Sopenharmony_ci u32 regno = (action & 0x0fff0000) >> 16; 15262306a36Sopenharmony_ci enum rtl_fw_opcode opcode = action >> 28; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (!action) 15562306a36Sopenharmony_ci break; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci switch (opcode) { 15862306a36Sopenharmony_ci case PHY_READ: 15962306a36Sopenharmony_ci predata = fw_read(tp, regno); 16062306a36Sopenharmony_ci count++; 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci case PHY_DATA_OR: 16362306a36Sopenharmony_ci predata |= data; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci case PHY_DATA_AND: 16662306a36Sopenharmony_ci predata &= data; 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci case PHY_BJMPN: 16962306a36Sopenharmony_ci index -= (regno + 1); 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci case PHY_MDIO_CHG: 17262306a36Sopenharmony_ci if (data) { 17362306a36Sopenharmony_ci fw_write = rtl_fw->mac_mcu_write; 17462306a36Sopenharmony_ci fw_read = rtl_fw->mac_mcu_read; 17562306a36Sopenharmony_ci } else { 17662306a36Sopenharmony_ci fw_write = rtl_fw->phy_write; 17762306a36Sopenharmony_ci fw_read = rtl_fw->phy_read; 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci case PHY_CLEAR_READCOUNT: 18262306a36Sopenharmony_ci count = 0; 18362306a36Sopenharmony_ci break; 18462306a36Sopenharmony_ci case PHY_WRITE: 18562306a36Sopenharmony_ci fw_write(tp, regno, data); 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci case PHY_READCOUNT_EQ_SKIP: 18862306a36Sopenharmony_ci if (count == data) 18962306a36Sopenharmony_ci index++; 19062306a36Sopenharmony_ci break; 19162306a36Sopenharmony_ci case PHY_COMP_EQ_SKIPN: 19262306a36Sopenharmony_ci if (predata == data) 19362306a36Sopenharmony_ci index += regno; 19462306a36Sopenharmony_ci break; 19562306a36Sopenharmony_ci case PHY_COMP_NEQ_SKIPN: 19662306a36Sopenharmony_ci if (predata != data) 19762306a36Sopenharmony_ci index += regno; 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci case PHY_WRITE_PREVIOUS: 20062306a36Sopenharmony_ci fw_write(tp, regno, predata); 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci case PHY_SKIPN: 20362306a36Sopenharmony_ci index += regno; 20462306a36Sopenharmony_ci break; 20562306a36Sopenharmony_ci case PHY_DELAY_MS: 20662306a36Sopenharmony_ci msleep(data); 20762306a36Sopenharmony_ci break; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_civoid rtl_fw_release_firmware(struct rtl_fw *rtl_fw) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci release_firmware(rtl_fw->fw); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ciint rtl_fw_request_firmware(struct rtl_fw *rtl_fw) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci int rc; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev); 22262306a36Sopenharmony_ci if (rc < 0) 22362306a36Sopenharmony_ci goto out; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) { 22662306a36Sopenharmony_ci release_firmware(rtl_fw->fw); 22762306a36Sopenharmony_ci rc = -EINVAL; 22862306a36Sopenharmony_ci goto out; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ciout: 23362306a36Sopenharmony_ci dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n", 23462306a36Sopenharmony_ci rtl_fw->fw_name, rc); 23562306a36Sopenharmony_ci return rc; 23662306a36Sopenharmony_ci} 237