Added r8169 for 6.12.
This commit is contained in:
parent
8174451e9b
commit
6b21038e58
|
|
@ -30,6 +30,8 @@ EXTRA_DIST = \
|
|||
r8169-5.15-orig.h \
|
||||
r8169-6.1-ethercat.h \
|
||||
r8169-6.1-orig.h \
|
||||
r8169-6.12-ethercat.h \
|
||||
r8169-6.12-orig.h \
|
||||
r8169-6.4-ethercat.h \
|
||||
r8169-6.4-orig.h \
|
||||
r8169_firmware-5.10-ethercat.c \
|
||||
|
|
@ -48,10 +50,16 @@ EXTRA_DIST = \
|
|||
r8169_firmware-6.1-ethercat.h \
|
||||
r8169_firmware-6.1-orig.c \
|
||||
r8169_firmware-6.1-orig.h \
|
||||
r8169_firmware-6.12-ethercat.c \
|
||||
r8169_firmware-6.12-ethercat.h \
|
||||
r8169_firmware-6.12-orig.c \
|
||||
r8169_firmware-6.12-orig.h \
|
||||
r8169_firmware-6.4-ethercat.c \
|
||||
r8169_firmware-6.4-ethercat.h \
|
||||
r8169_firmware-6.4-orig.c \
|
||||
r8169_firmware-6.4-orig.h \
|
||||
r8169_leds-6.12-ethercat.c \
|
||||
r8169_leds-6.12-orig.c \
|
||||
r8169_main-5.10-ethercat.c \
|
||||
r8169_main-5.10-orig.c \
|
||||
r8169_main-5.14-ethercat.c \
|
||||
|
|
@ -60,6 +68,8 @@ EXTRA_DIST = \
|
|||
r8169_main-5.15-orig.c \
|
||||
r8169_main-6.1-ethercat.c \
|
||||
r8169_main-6.1-orig.c \
|
||||
r8169_main-6.12-ethercat.c \
|
||||
r8169_main-6.12-orig.c \
|
||||
r8169_main-6.4-ethercat.c \
|
||||
r8169_main-6.4-orig.c \
|
||||
r8169_phy_config-5.10-ethercat.c \
|
||||
|
|
@ -70,6 +80,8 @@ EXTRA_DIST = \
|
|||
r8169_phy_config-5.15-orig.c \
|
||||
r8169_phy_config-6.1-ethercat.c \
|
||||
r8169_phy_config-6.1-orig.c \
|
||||
r8169_phy_config-6.12-ethercat.c \
|
||||
r8169_phy_config-6.12-orig.c \
|
||||
r8169_phy_config-6.4-ethercat.c \
|
||||
r8169_phy_config-6.4-orig.c
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* r8169.h: RealTek 8169/8168/8101 ethernet driver.
|
||||
*
|
||||
* Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
|
||||
* Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
|
||||
* Copyright (c) a lot of people too. Please respect their work.
|
||||
*
|
||||
* See MAINTAINERS file for support contact information.
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
enum mac_version {
|
||||
/* support for ancient RTL_GIGA_MAC_VER_01 has been removed */
|
||||
RTL_GIGA_MAC_VER_02,
|
||||
RTL_GIGA_MAC_VER_03,
|
||||
RTL_GIGA_MAC_VER_04,
|
||||
RTL_GIGA_MAC_VER_05,
|
||||
RTL_GIGA_MAC_VER_06,
|
||||
RTL_GIGA_MAC_VER_07,
|
||||
RTL_GIGA_MAC_VER_08,
|
||||
RTL_GIGA_MAC_VER_09,
|
||||
RTL_GIGA_MAC_VER_10,
|
||||
RTL_GIGA_MAC_VER_11,
|
||||
/* RTL_GIGA_MAC_VER_12 was handled the same as VER_17 */
|
||||
/* RTL_GIGA_MAC_VER_13 was merged with VER_10 */
|
||||
RTL_GIGA_MAC_VER_14,
|
||||
/* RTL_GIGA_MAC_VER_16 was merged with VER_10 */
|
||||
RTL_GIGA_MAC_VER_17,
|
||||
RTL_GIGA_MAC_VER_18,
|
||||
RTL_GIGA_MAC_VER_19,
|
||||
RTL_GIGA_MAC_VER_20,
|
||||
RTL_GIGA_MAC_VER_21,
|
||||
RTL_GIGA_MAC_VER_22,
|
||||
RTL_GIGA_MAC_VER_23,
|
||||
RTL_GIGA_MAC_VER_24,
|
||||
RTL_GIGA_MAC_VER_25,
|
||||
RTL_GIGA_MAC_VER_26,
|
||||
/* support for RTL_GIGA_MAC_VER_27 has been removed */
|
||||
RTL_GIGA_MAC_VER_28,
|
||||
RTL_GIGA_MAC_VER_29,
|
||||
RTL_GIGA_MAC_VER_30,
|
||||
RTL_GIGA_MAC_VER_31,
|
||||
RTL_GIGA_MAC_VER_32,
|
||||
RTL_GIGA_MAC_VER_33,
|
||||
RTL_GIGA_MAC_VER_34,
|
||||
RTL_GIGA_MAC_VER_35,
|
||||
RTL_GIGA_MAC_VER_36,
|
||||
RTL_GIGA_MAC_VER_37,
|
||||
RTL_GIGA_MAC_VER_38,
|
||||
RTL_GIGA_MAC_VER_39,
|
||||
RTL_GIGA_MAC_VER_40,
|
||||
/* support for RTL_GIGA_MAC_VER_41 has been removed */
|
||||
RTL_GIGA_MAC_VER_42,
|
||||
RTL_GIGA_MAC_VER_43,
|
||||
RTL_GIGA_MAC_VER_44,
|
||||
/* support for RTL_GIGA_MAC_VER_45 has been removed */
|
||||
RTL_GIGA_MAC_VER_46,
|
||||
/* support for RTL_GIGA_MAC_VER_47 has been removed */
|
||||
RTL_GIGA_MAC_VER_48,
|
||||
/* support for RTL_GIGA_MAC_VER_49 has been removed */
|
||||
/* support for RTL_GIGA_MAC_VER_50 has been removed */
|
||||
RTL_GIGA_MAC_VER_51,
|
||||
RTL_GIGA_MAC_VER_52,
|
||||
RTL_GIGA_MAC_VER_53,
|
||||
/* support for RTL_GIGA_MAC_VER_60 has been removed */
|
||||
RTL_GIGA_MAC_VER_61,
|
||||
RTL_GIGA_MAC_VER_63,
|
||||
RTL_GIGA_MAC_VER_65,
|
||||
RTL_GIGA_MAC_VER_66,
|
||||
RTL_GIGA_MAC_NONE
|
||||
};
|
||||
|
||||
struct rtl8169_private;
|
||||
struct r8169_led_classdev;
|
||||
|
||||
void r8169_apply_firmware(struct rtl8169_private *tp);
|
||||
u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp);
|
||||
u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr);
|
||||
void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev,
|
||||
enum mac_version ver);
|
||||
|
||||
void r8169_get_led_name(struct rtl8169_private *tp, int idx,
|
||||
char *buf, int buf_len);
|
||||
int rtl8168_get_led_mode(struct rtl8169_private *tp);
|
||||
int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
|
||||
struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev);
|
||||
int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
|
||||
int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
|
||||
struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev);
|
||||
void r8169_remove_leds(struct r8169_led_classdev *leds);
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* r8169.h: RealTek 8169/8168/8101 ethernet driver.
|
||||
*
|
||||
* Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
|
||||
* Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
|
||||
* Copyright (c) a lot of people too. Please respect their work.
|
||||
*
|
||||
* See MAINTAINERS file for support contact information.
|
||||
*/
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
enum mac_version {
|
||||
/* support for ancient RTL_GIGA_MAC_VER_01 has been removed */
|
||||
RTL_GIGA_MAC_VER_02,
|
||||
RTL_GIGA_MAC_VER_03,
|
||||
RTL_GIGA_MAC_VER_04,
|
||||
RTL_GIGA_MAC_VER_05,
|
||||
RTL_GIGA_MAC_VER_06,
|
||||
RTL_GIGA_MAC_VER_07,
|
||||
RTL_GIGA_MAC_VER_08,
|
||||
RTL_GIGA_MAC_VER_09,
|
||||
RTL_GIGA_MAC_VER_10,
|
||||
RTL_GIGA_MAC_VER_11,
|
||||
/* RTL_GIGA_MAC_VER_12 was handled the same as VER_17 */
|
||||
/* RTL_GIGA_MAC_VER_13 was merged with VER_10 */
|
||||
RTL_GIGA_MAC_VER_14,
|
||||
/* RTL_GIGA_MAC_VER_16 was merged with VER_10 */
|
||||
RTL_GIGA_MAC_VER_17,
|
||||
RTL_GIGA_MAC_VER_18,
|
||||
RTL_GIGA_MAC_VER_19,
|
||||
RTL_GIGA_MAC_VER_20,
|
||||
RTL_GIGA_MAC_VER_21,
|
||||
RTL_GIGA_MAC_VER_22,
|
||||
RTL_GIGA_MAC_VER_23,
|
||||
RTL_GIGA_MAC_VER_24,
|
||||
RTL_GIGA_MAC_VER_25,
|
||||
RTL_GIGA_MAC_VER_26,
|
||||
/* support for RTL_GIGA_MAC_VER_27 has been removed */
|
||||
RTL_GIGA_MAC_VER_28,
|
||||
RTL_GIGA_MAC_VER_29,
|
||||
RTL_GIGA_MAC_VER_30,
|
||||
RTL_GIGA_MAC_VER_31,
|
||||
RTL_GIGA_MAC_VER_32,
|
||||
RTL_GIGA_MAC_VER_33,
|
||||
RTL_GIGA_MAC_VER_34,
|
||||
RTL_GIGA_MAC_VER_35,
|
||||
RTL_GIGA_MAC_VER_36,
|
||||
RTL_GIGA_MAC_VER_37,
|
||||
RTL_GIGA_MAC_VER_38,
|
||||
RTL_GIGA_MAC_VER_39,
|
||||
RTL_GIGA_MAC_VER_40,
|
||||
/* support for RTL_GIGA_MAC_VER_41 has been removed */
|
||||
RTL_GIGA_MAC_VER_42,
|
||||
RTL_GIGA_MAC_VER_43,
|
||||
RTL_GIGA_MAC_VER_44,
|
||||
/* support for RTL_GIGA_MAC_VER_45 has been removed */
|
||||
RTL_GIGA_MAC_VER_46,
|
||||
/* support for RTL_GIGA_MAC_VER_47 has been removed */
|
||||
RTL_GIGA_MAC_VER_48,
|
||||
/* support for RTL_GIGA_MAC_VER_49 has been removed */
|
||||
/* support for RTL_GIGA_MAC_VER_50 has been removed */
|
||||
RTL_GIGA_MAC_VER_51,
|
||||
RTL_GIGA_MAC_VER_52,
|
||||
RTL_GIGA_MAC_VER_53,
|
||||
/* support for RTL_GIGA_MAC_VER_60 has been removed */
|
||||
RTL_GIGA_MAC_VER_61,
|
||||
RTL_GIGA_MAC_VER_63,
|
||||
RTL_GIGA_MAC_VER_65,
|
||||
RTL_GIGA_MAC_VER_66,
|
||||
RTL_GIGA_MAC_NONE
|
||||
};
|
||||
|
||||
struct rtl8169_private;
|
||||
struct r8169_led_classdev;
|
||||
|
||||
void r8169_apply_firmware(struct rtl8169_private *tp);
|
||||
u16 rtl8168h_2_get_adc_bias_ioffset(struct rtl8169_private *tp);
|
||||
u8 rtl8168d_efuse_read(struct rtl8169_private *tp, int reg_addr);
|
||||
void r8169_hw_phy_config(struct rtl8169_private *tp, struct phy_device *phydev,
|
||||
enum mac_version ver);
|
||||
|
||||
void r8169_get_led_name(struct rtl8169_private *tp, int idx,
|
||||
char *buf, int buf_len);
|
||||
int rtl8168_get_led_mode(struct rtl8169_private *tp);
|
||||
int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
|
||||
struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev);
|
||||
int rtl8125_get_led_mode(struct rtl8169_private *tp, int index);
|
||||
int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode);
|
||||
struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev);
|
||||
void r8169_remove_leds(struct r8169_led_classdev *leds);
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver.
|
||||
*
|
||||
* Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
|
||||
* Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
|
||||
* Copyright (c) a lot of people too. Please respect their work.
|
||||
*
|
||||
* See MAINTAINERS file for support contact information.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "r8169_firmware-6.12-ethercat.h"
|
||||
|
||||
enum rtl_fw_opcode {
|
||||
PHY_READ = 0x0,
|
||||
PHY_DATA_OR = 0x1,
|
||||
PHY_DATA_AND = 0x2,
|
||||
PHY_BJMPN = 0x3,
|
||||
PHY_MDIO_CHG = 0x4,
|
||||
PHY_CLEAR_READCOUNT = 0x7,
|
||||
PHY_WRITE = 0x8,
|
||||
PHY_READCOUNT_EQ_SKIP = 0x9,
|
||||
PHY_COMP_EQ_SKIPN = 0xa,
|
||||
PHY_COMP_NEQ_SKIPN = 0xb,
|
||||
PHY_WRITE_PREVIOUS = 0xc,
|
||||
PHY_SKIPN = 0xd,
|
||||
PHY_DELAY_MS = 0xe,
|
||||
};
|
||||
|
||||
struct fw_info {
|
||||
u32 magic;
|
||||
char version[RTL_VER_SIZE];
|
||||
__le32 fw_start;
|
||||
__le32 fw_len;
|
||||
u8 chksum;
|
||||
} __packed;
|
||||
|
||||
#define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0])
|
||||
|
||||
static bool rtl_fw_format_ok(struct rtl_fw *rtl_fw)
|
||||
{
|
||||
const struct firmware *fw = rtl_fw->fw;
|
||||
struct fw_info *fw_info = (struct fw_info *)fw->data;
|
||||
struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
|
||||
|
||||
if (fw->size < FW_OPCODE_SIZE)
|
||||
return false;
|
||||
|
||||
if (!fw_info->magic) {
|
||||
size_t i, size, start;
|
||||
u8 checksum = 0;
|
||||
|
||||
if (fw->size < sizeof(*fw_info))
|
||||
return false;
|
||||
|
||||
for (i = 0; i < fw->size; i++)
|
||||
checksum += fw->data[i];
|
||||
if (checksum != 0)
|
||||
return false;
|
||||
|
||||
start = le32_to_cpu(fw_info->fw_start);
|
||||
if (start > fw->size)
|
||||
return false;
|
||||
|
||||
size = le32_to_cpu(fw_info->fw_len);
|
||||
if (size > (fw->size - start) / FW_OPCODE_SIZE)
|
||||
return false;
|
||||
|
||||
strscpy(rtl_fw->version, fw_info->version, RTL_VER_SIZE);
|
||||
|
||||
pa->code = (__le32 *)(fw->data + start);
|
||||
pa->size = size;
|
||||
} else {
|
||||
if (fw->size % FW_OPCODE_SIZE)
|
||||
return false;
|
||||
|
||||
strscpy(rtl_fw->version, rtl_fw->fw_name, RTL_VER_SIZE);
|
||||
|
||||
pa->code = (__le32 *)fw->data;
|
||||
pa->size = fw->size / FW_OPCODE_SIZE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool rtl_fw_data_ok(struct rtl_fw *rtl_fw)
|
||||
{
|
||||
struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
|
||||
size_t index;
|
||||
|
||||
for (index = 0; index < pa->size; index++) {
|
||||
u32 action = le32_to_cpu(pa->code[index]);
|
||||
u32 val = action & 0x0000ffff;
|
||||
u32 regno = (action & 0x0fff0000) >> 16;
|
||||
|
||||
switch (action >> 28) {
|
||||
case PHY_READ:
|
||||
case PHY_DATA_OR:
|
||||
case PHY_DATA_AND:
|
||||
case PHY_CLEAR_READCOUNT:
|
||||
case PHY_WRITE:
|
||||
case PHY_WRITE_PREVIOUS:
|
||||
case PHY_DELAY_MS:
|
||||
break;
|
||||
|
||||
case PHY_MDIO_CHG:
|
||||
if (val > 1)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case PHY_BJMPN:
|
||||
if (regno > index)
|
||||
goto out;
|
||||
break;
|
||||
case PHY_READCOUNT_EQ_SKIP:
|
||||
if (index + 2 >= pa->size)
|
||||
goto out;
|
||||
break;
|
||||
case PHY_COMP_EQ_SKIPN:
|
||||
case PHY_COMP_NEQ_SKIPN:
|
||||
case PHY_SKIPN:
|
||||
if (index + 1 + regno >= pa->size)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
out:
|
||||
dev_err(rtl_fw->dev, "Out of range of firmware\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
|
||||
{
|
||||
struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
|
||||
rtl_fw_write_t fw_write = rtl_fw->phy_write;
|
||||
rtl_fw_read_t fw_read = rtl_fw->phy_read;
|
||||
int predata = 0, count = 0;
|
||||
size_t index;
|
||||
|
||||
for (index = 0; index < pa->size; index++) {
|
||||
u32 action = le32_to_cpu(pa->code[index]);
|
||||
u32 data = action & 0x0000ffff;
|
||||
u32 regno = (action & 0x0fff0000) >> 16;
|
||||
enum rtl_fw_opcode opcode = action >> 28;
|
||||
|
||||
switch (opcode) {
|
||||
case PHY_READ:
|
||||
predata = fw_read(tp, regno);
|
||||
count++;
|
||||
break;
|
||||
case PHY_DATA_OR:
|
||||
predata |= data;
|
||||
break;
|
||||
case PHY_DATA_AND:
|
||||
predata &= data;
|
||||
break;
|
||||
case PHY_BJMPN:
|
||||
index -= (regno + 1);
|
||||
break;
|
||||
case PHY_MDIO_CHG:
|
||||
if (data) {
|
||||
fw_write = rtl_fw->mac_mcu_write;
|
||||
fw_read = rtl_fw->mac_mcu_read;
|
||||
} else {
|
||||
fw_write = rtl_fw->phy_write;
|
||||
fw_read = rtl_fw->phy_read;
|
||||
}
|
||||
|
||||
break;
|
||||
case PHY_CLEAR_READCOUNT:
|
||||
count = 0;
|
||||
break;
|
||||
case PHY_WRITE:
|
||||
fw_write(tp, regno, data);
|
||||
break;
|
||||
case PHY_READCOUNT_EQ_SKIP:
|
||||
if (count == data)
|
||||
index++;
|
||||
break;
|
||||
case PHY_COMP_EQ_SKIPN:
|
||||
if (predata == data)
|
||||
index += regno;
|
||||
break;
|
||||
case PHY_COMP_NEQ_SKIPN:
|
||||
if (predata != data)
|
||||
index += regno;
|
||||
break;
|
||||
case PHY_WRITE_PREVIOUS:
|
||||
fw_write(tp, regno, predata);
|
||||
break;
|
||||
case PHY_SKIPN:
|
||||
index += regno;
|
||||
break;
|
||||
case PHY_DELAY_MS:
|
||||
msleep(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rtl_fw_release_firmware(struct rtl_fw *rtl_fw)
|
||||
{
|
||||
release_firmware(rtl_fw->fw);
|
||||
}
|
||||
|
||||
int rtl_fw_request_firmware(struct rtl_fw *rtl_fw)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) {
|
||||
release_firmware(rtl_fw->fw);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n",
|
||||
rtl_fw->fw_name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* r8169_firmware.h: RealTek 8169/8168/8101 ethernet driver.
|
||||
*
|
||||
* Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
|
||||
* Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
|
||||
* Copyright (c) a lot of people too. Please respect their work.
|
||||
*
|
||||
* See MAINTAINERS file for support contact information.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
struct rtl8169_private;
|
||||
typedef void (*rtl_fw_write_t)(struct rtl8169_private *tp, int reg, int val);
|
||||
typedef int (*rtl_fw_read_t)(struct rtl8169_private *tp, int reg);
|
||||
|
||||
#define RTL_VER_SIZE 32
|
||||
|
||||
struct rtl_fw {
|
||||
rtl_fw_write_t phy_write;
|
||||
rtl_fw_read_t phy_read;
|
||||
rtl_fw_write_t mac_mcu_write;
|
||||
rtl_fw_read_t mac_mcu_read;
|
||||
const struct firmware *fw;
|
||||
const char *fw_name;
|
||||
struct device *dev;
|
||||
|
||||
char version[RTL_VER_SIZE];
|
||||
|
||||
struct rtl_fw_phy_action {
|
||||
__le32 *code;
|
||||
size_t size;
|
||||
} phy_action;
|
||||
};
|
||||
|
||||
int rtl_fw_request_firmware(struct rtl_fw *rtl_fw);
|
||||
void rtl_fw_release_firmware(struct rtl_fw *rtl_fw);
|
||||
void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw);
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver.
|
||||
*
|
||||
* Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
|
||||
* Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
|
||||
* Copyright (c) a lot of people too. Please respect their work.
|
||||
*
|
||||
* See MAINTAINERS file for support contact information.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "r8169_firmware.h"
|
||||
|
||||
enum rtl_fw_opcode {
|
||||
PHY_READ = 0x0,
|
||||
PHY_DATA_OR = 0x1,
|
||||
PHY_DATA_AND = 0x2,
|
||||
PHY_BJMPN = 0x3,
|
||||
PHY_MDIO_CHG = 0x4,
|
||||
PHY_CLEAR_READCOUNT = 0x7,
|
||||
PHY_WRITE = 0x8,
|
||||
PHY_READCOUNT_EQ_SKIP = 0x9,
|
||||
PHY_COMP_EQ_SKIPN = 0xa,
|
||||
PHY_COMP_NEQ_SKIPN = 0xb,
|
||||
PHY_WRITE_PREVIOUS = 0xc,
|
||||
PHY_SKIPN = 0xd,
|
||||
PHY_DELAY_MS = 0xe,
|
||||
};
|
||||
|
||||
struct fw_info {
|
||||
u32 magic;
|
||||
char version[RTL_VER_SIZE];
|
||||
__le32 fw_start;
|
||||
__le32 fw_len;
|
||||
u8 chksum;
|
||||
} __packed;
|
||||
|
||||
#define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0])
|
||||
|
||||
static bool rtl_fw_format_ok(struct rtl_fw *rtl_fw)
|
||||
{
|
||||
const struct firmware *fw = rtl_fw->fw;
|
||||
struct fw_info *fw_info = (struct fw_info *)fw->data;
|
||||
struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
|
||||
|
||||
if (fw->size < FW_OPCODE_SIZE)
|
||||
return false;
|
||||
|
||||
if (!fw_info->magic) {
|
||||
size_t i, size, start;
|
||||
u8 checksum = 0;
|
||||
|
||||
if (fw->size < sizeof(*fw_info))
|
||||
return false;
|
||||
|
||||
for (i = 0; i < fw->size; i++)
|
||||
checksum += fw->data[i];
|
||||
if (checksum != 0)
|
||||
return false;
|
||||
|
||||
start = le32_to_cpu(fw_info->fw_start);
|
||||
if (start > fw->size)
|
||||
return false;
|
||||
|
||||
size = le32_to_cpu(fw_info->fw_len);
|
||||
if (size > (fw->size - start) / FW_OPCODE_SIZE)
|
||||
return false;
|
||||
|
||||
strscpy(rtl_fw->version, fw_info->version, RTL_VER_SIZE);
|
||||
|
||||
pa->code = (__le32 *)(fw->data + start);
|
||||
pa->size = size;
|
||||
} else {
|
||||
if (fw->size % FW_OPCODE_SIZE)
|
||||
return false;
|
||||
|
||||
strscpy(rtl_fw->version, rtl_fw->fw_name, RTL_VER_SIZE);
|
||||
|
||||
pa->code = (__le32 *)fw->data;
|
||||
pa->size = fw->size / FW_OPCODE_SIZE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool rtl_fw_data_ok(struct rtl_fw *rtl_fw)
|
||||
{
|
||||
struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
|
||||
size_t index;
|
||||
|
||||
for (index = 0; index < pa->size; index++) {
|
||||
u32 action = le32_to_cpu(pa->code[index]);
|
||||
u32 val = action & 0x0000ffff;
|
||||
u32 regno = (action & 0x0fff0000) >> 16;
|
||||
|
||||
switch (action >> 28) {
|
||||
case PHY_READ:
|
||||
case PHY_DATA_OR:
|
||||
case PHY_DATA_AND:
|
||||
case PHY_CLEAR_READCOUNT:
|
||||
case PHY_WRITE:
|
||||
case PHY_WRITE_PREVIOUS:
|
||||
case PHY_DELAY_MS:
|
||||
break;
|
||||
|
||||
case PHY_MDIO_CHG:
|
||||
if (val > 1)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
case PHY_BJMPN:
|
||||
if (regno > index)
|
||||
goto out;
|
||||
break;
|
||||
case PHY_READCOUNT_EQ_SKIP:
|
||||
if (index + 2 >= pa->size)
|
||||
goto out;
|
||||
break;
|
||||
case PHY_COMP_EQ_SKIPN:
|
||||
case PHY_COMP_NEQ_SKIPN:
|
||||
case PHY_SKIPN:
|
||||
if (index + 1 + regno >= pa->size)
|
||||
goto out;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
out:
|
||||
dev_err(rtl_fw->dev, "Out of range of firmware\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
|
||||
{
|
||||
struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
|
||||
rtl_fw_write_t fw_write = rtl_fw->phy_write;
|
||||
rtl_fw_read_t fw_read = rtl_fw->phy_read;
|
||||
int predata = 0, count = 0;
|
||||
size_t index;
|
||||
|
||||
for (index = 0; index < pa->size; index++) {
|
||||
u32 action = le32_to_cpu(pa->code[index]);
|
||||
u32 data = action & 0x0000ffff;
|
||||
u32 regno = (action & 0x0fff0000) >> 16;
|
||||
enum rtl_fw_opcode opcode = action >> 28;
|
||||
|
||||
switch (opcode) {
|
||||
case PHY_READ:
|
||||
predata = fw_read(tp, regno);
|
||||
count++;
|
||||
break;
|
||||
case PHY_DATA_OR:
|
||||
predata |= data;
|
||||
break;
|
||||
case PHY_DATA_AND:
|
||||
predata &= data;
|
||||
break;
|
||||
case PHY_BJMPN:
|
||||
index -= (regno + 1);
|
||||
break;
|
||||
case PHY_MDIO_CHG:
|
||||
if (data) {
|
||||
fw_write = rtl_fw->mac_mcu_write;
|
||||
fw_read = rtl_fw->mac_mcu_read;
|
||||
} else {
|
||||
fw_write = rtl_fw->phy_write;
|
||||
fw_read = rtl_fw->phy_read;
|
||||
}
|
||||
|
||||
break;
|
||||
case PHY_CLEAR_READCOUNT:
|
||||
count = 0;
|
||||
break;
|
||||
case PHY_WRITE:
|
||||
fw_write(tp, regno, data);
|
||||
break;
|
||||
case PHY_READCOUNT_EQ_SKIP:
|
||||
if (count == data)
|
||||
index++;
|
||||
break;
|
||||
case PHY_COMP_EQ_SKIPN:
|
||||
if (predata == data)
|
||||
index += regno;
|
||||
break;
|
||||
case PHY_COMP_NEQ_SKIPN:
|
||||
if (predata != data)
|
||||
index += regno;
|
||||
break;
|
||||
case PHY_WRITE_PREVIOUS:
|
||||
fw_write(tp, regno, predata);
|
||||
break;
|
||||
case PHY_SKIPN:
|
||||
index += regno;
|
||||
break;
|
||||
case PHY_DELAY_MS:
|
||||
msleep(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rtl_fw_release_firmware(struct rtl_fw *rtl_fw)
|
||||
{
|
||||
release_firmware(rtl_fw->fw);
|
||||
}
|
||||
|
||||
int rtl_fw_request_firmware(struct rtl_fw *rtl_fw)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) {
|
||||
release_firmware(rtl_fw->fw);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n",
|
||||
rtl_fw->fw_name, rc);
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* r8169_firmware.h: RealTek 8169/8168/8101 ethernet driver.
|
||||
*
|
||||
* Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
|
||||
* Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
|
||||
* Copyright (c) a lot of people too. Please respect their work.
|
||||
*
|
||||
* See MAINTAINERS file for support contact information.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
struct rtl8169_private;
|
||||
typedef void (*rtl_fw_write_t)(struct rtl8169_private *tp, int reg, int val);
|
||||
typedef int (*rtl_fw_read_t)(struct rtl8169_private *tp, int reg);
|
||||
|
||||
#define RTL_VER_SIZE 32
|
||||
|
||||
struct rtl_fw {
|
||||
rtl_fw_write_t phy_write;
|
||||
rtl_fw_read_t phy_read;
|
||||
rtl_fw_write_t mac_mcu_write;
|
||||
rtl_fw_read_t mac_mcu_read;
|
||||
const struct firmware *fw;
|
||||
const char *fw_name;
|
||||
struct device *dev;
|
||||
|
||||
char version[RTL_VER_SIZE];
|
||||
|
||||
struct rtl_fw_phy_action {
|
||||
__le32 *code;
|
||||
size_t size;
|
||||
} phy_action;
|
||||
};
|
||||
|
||||
int rtl_fw_request_firmware(struct rtl_fw *rtl_fw);
|
||||
void rtl_fw_release_firmware(struct rtl_fw *rtl_fw);
|
||||
void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw);
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* r8169_leds.c: Realtek 8169/8168/8101/8125 ethernet driver.
|
||||
*
|
||||
* Copyright (c) 2023 Heiner Kallweit <hkallweit1@gmail.com>
|
||||
*
|
||||
* See MAINTAINERS file for support contact information.
|
||||
*/
|
||||
|
||||
#include <linux/leds.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <uapi/linux/uleds.h>
|
||||
|
||||
#include "r8169.h"
|
||||
|
||||
#define RTL8168_LED_CTRL_OPTION2 BIT(15)
|
||||
#define RTL8168_LED_CTRL_ACT BIT(3)
|
||||
#define RTL8168_LED_CTRL_LINK_1000 BIT(2)
|
||||
#define RTL8168_LED_CTRL_LINK_100 BIT(1)
|
||||
#define RTL8168_LED_CTRL_LINK_10 BIT(0)
|
||||
|
||||
#define RTL8125_LED_CTRL_ACT BIT(9)
|
||||
#define RTL8125_LED_CTRL_LINK_2500 BIT(5)
|
||||
#define RTL8125_LED_CTRL_LINK_1000 BIT(3)
|
||||
#define RTL8125_LED_CTRL_LINK_100 BIT(1)
|
||||
#define RTL8125_LED_CTRL_LINK_10 BIT(0)
|
||||
|
||||
#define RTL8168_NUM_LEDS 3
|
||||
#define RTL8125_NUM_LEDS 4
|
||||
|
||||
struct r8169_led_classdev {
|
||||
struct led_classdev led;
|
||||
struct net_device *ndev;
|
||||
int index;
|
||||
};
|
||||
|
||||
#define lcdev_to_r8169_ldev(lcdev) container_of(lcdev, struct r8169_led_classdev, led)
|
||||
|
||||
static bool r8169_trigger_mode_is_valid(unsigned long flags)
|
||||
{
|
||||
bool rx, tx;
|
||||
|
||||
if (flags & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
|
||||
return false;
|
||||
if (flags & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
|
||||
return false;
|
||||
|
||||
rx = flags & BIT(TRIGGER_NETDEV_RX);
|
||||
tx = flags & BIT(TRIGGER_NETDEV_TX);
|
||||
|
||||
return rx == tx;
|
||||
}
|
||||
|
||||
static int rtl8168_led_hw_control_is_supported(struct led_classdev *led_cdev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
int shift = ldev->index * 4;
|
||||
|
||||
if (!r8169_trigger_mode_is_valid(flags)) {
|
||||
/* Switch LED off to indicate that mode isn't supported */
|
||||
rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8168_led_hw_control_set(struct led_classdev *led_cdev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
int shift = ldev->index * 4;
|
||||
u16 mode = 0;
|
||||
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_10))
|
||||
mode |= RTL8168_LED_CTRL_LINK_10;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_100))
|
||||
mode |= RTL8168_LED_CTRL_LINK_100;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
|
||||
mode |= RTL8168_LED_CTRL_LINK_1000;
|
||||
if (flags & BIT(TRIGGER_NETDEV_TX))
|
||||
mode |= RTL8168_LED_CTRL_ACT;
|
||||
|
||||
return rtl8168_led_mod_ctrl(tp, 0x000f << shift, mode << shift);
|
||||
}
|
||||
|
||||
static int rtl8168_led_hw_control_get(struct led_classdev *led_cdev,
|
||||
unsigned long *flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
int shift = ldev->index * 4;
|
||||
int mode;
|
||||
|
||||
mode = rtl8168_get_led_mode(tp);
|
||||
if (mode < 0)
|
||||
return mode;
|
||||
|
||||
if (mode & RTL8168_LED_CTRL_OPTION2) {
|
||||
rtl8168_led_mod_ctrl(tp, RTL8168_LED_CTRL_OPTION2, 0);
|
||||
netdev_notice(ldev->ndev, "Deactivating unsupported Option2 LED mode\n");
|
||||
}
|
||||
|
||||
mode = (mode >> shift) & 0x000f;
|
||||
|
||||
if (mode & RTL8168_LED_CTRL_ACT)
|
||||
*flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
|
||||
|
||||
if (mode & RTL8168_LED_CTRL_LINK_10)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_10);
|
||||
if (mode & RTL8168_LED_CTRL_LINK_100)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_100);
|
||||
if (mode & RTL8168_LED_CTRL_LINK_1000)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device *
|
||||
r8169_led_hw_control_get_device(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
|
||||
return &ldev->ndev->dev;
|
||||
}
|
||||
|
||||
static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev,
|
||||
struct net_device *ndev, int index)
|
||||
{
|
||||
struct rtl8169_private *tp = netdev_priv(ndev);
|
||||
struct led_classdev *led_cdev = &ldev->led;
|
||||
char led_name[LED_MAX_NAME_SIZE];
|
||||
|
||||
ldev->ndev = ndev;
|
||||
ldev->index = index;
|
||||
|
||||
r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);
|
||||
led_cdev->name = led_name;
|
||||
led_cdev->hw_control_trigger = "netdev";
|
||||
led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
|
||||
led_cdev->hw_control_is_supported = rtl8168_led_hw_control_is_supported;
|
||||
led_cdev->hw_control_set = rtl8168_led_hw_control_set;
|
||||
led_cdev->hw_control_get = rtl8168_led_hw_control_get;
|
||||
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
|
||||
|
||||
/* ignore errors */
|
||||
led_classdev_register(&ndev->dev, led_cdev);
|
||||
}
|
||||
|
||||
struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev)
|
||||
{
|
||||
struct r8169_led_classdev *leds;
|
||||
int i;
|
||||
|
||||
leds = kcalloc(RTL8168_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
|
||||
if (!leds)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < RTL8168_NUM_LEDS; i++)
|
||||
rtl8168_setup_ldev(leds + i, ndev, i);
|
||||
|
||||
return leds;
|
||||
}
|
||||
|
||||
static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
|
||||
if (!r8169_trigger_mode_is_valid(flags)) {
|
||||
/* Switch LED off to indicate that mode isn't supported */
|
||||
rtl8125_set_led_mode(tp, ldev->index, 0);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8125_led_hw_control_set(struct led_classdev *led_cdev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
u16 mode = 0;
|
||||
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_10))
|
||||
mode |= RTL8125_LED_CTRL_LINK_10;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_100))
|
||||
mode |= RTL8125_LED_CTRL_LINK_100;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
|
||||
mode |= RTL8125_LED_CTRL_LINK_1000;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_2500))
|
||||
mode |= RTL8125_LED_CTRL_LINK_2500;
|
||||
if (flags & (BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX)))
|
||||
mode |= RTL8125_LED_CTRL_ACT;
|
||||
|
||||
return rtl8125_set_led_mode(tp, ldev->index, mode);
|
||||
}
|
||||
|
||||
static int rtl8125_led_hw_control_get(struct led_classdev *led_cdev,
|
||||
unsigned long *flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
int mode;
|
||||
|
||||
mode = rtl8125_get_led_mode(tp, ldev->index);
|
||||
if (mode < 0)
|
||||
return mode;
|
||||
|
||||
if (mode & RTL8125_LED_CTRL_LINK_10)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_10);
|
||||
if (mode & RTL8125_LED_CTRL_LINK_100)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_100);
|
||||
if (mode & RTL8125_LED_CTRL_LINK_1000)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_1000);
|
||||
if (mode & RTL8125_LED_CTRL_LINK_2500)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_2500);
|
||||
if (mode & RTL8125_LED_CTRL_ACT)
|
||||
*flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev,
|
||||
struct net_device *ndev, int index)
|
||||
{
|
||||
struct rtl8169_private *tp = netdev_priv(ndev);
|
||||
struct led_classdev *led_cdev = &ldev->led;
|
||||
char led_name[LED_MAX_NAME_SIZE];
|
||||
|
||||
ldev->ndev = ndev;
|
||||
ldev->index = index;
|
||||
|
||||
r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);
|
||||
led_cdev->name = led_name;
|
||||
led_cdev->hw_control_trigger = "netdev";
|
||||
led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
|
||||
led_cdev->hw_control_is_supported = rtl8125_led_hw_control_is_supported;
|
||||
led_cdev->hw_control_set = rtl8125_led_hw_control_set;
|
||||
led_cdev->hw_control_get = rtl8125_led_hw_control_get;
|
||||
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
|
||||
|
||||
/* ignore errors */
|
||||
led_classdev_register(&ndev->dev, led_cdev);
|
||||
}
|
||||
|
||||
struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev)
|
||||
{
|
||||
struct r8169_led_classdev *leds;
|
||||
int i;
|
||||
|
||||
leds = kcalloc(RTL8125_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
|
||||
if (!leds)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < RTL8125_NUM_LEDS; i++)
|
||||
rtl8125_setup_led_ldev(leds + i, ndev, i);
|
||||
|
||||
return leds;
|
||||
}
|
||||
|
||||
void r8169_remove_leds(struct r8169_led_classdev *leds)
|
||||
{
|
||||
if (!leds)
|
||||
return;
|
||||
|
||||
for (struct r8169_led_classdev *l = leds; l->ndev; l++)
|
||||
led_classdev_unregister(&l->led);
|
||||
|
||||
kfree(leds);
|
||||
}
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* r8169_leds.c: Realtek 8169/8168/8101/8125 ethernet driver.
|
||||
*
|
||||
* Copyright (c) 2023 Heiner Kallweit <hkallweit1@gmail.com>
|
||||
*
|
||||
* See MAINTAINERS file for support contact information.
|
||||
*/
|
||||
|
||||
#include <linux/leds.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <uapi/linux/uleds.h>
|
||||
|
||||
#include "r8169.h"
|
||||
|
||||
#define RTL8168_LED_CTRL_OPTION2 BIT(15)
|
||||
#define RTL8168_LED_CTRL_ACT BIT(3)
|
||||
#define RTL8168_LED_CTRL_LINK_1000 BIT(2)
|
||||
#define RTL8168_LED_CTRL_LINK_100 BIT(1)
|
||||
#define RTL8168_LED_CTRL_LINK_10 BIT(0)
|
||||
|
||||
#define RTL8125_LED_CTRL_ACT BIT(9)
|
||||
#define RTL8125_LED_CTRL_LINK_2500 BIT(5)
|
||||
#define RTL8125_LED_CTRL_LINK_1000 BIT(3)
|
||||
#define RTL8125_LED_CTRL_LINK_100 BIT(1)
|
||||
#define RTL8125_LED_CTRL_LINK_10 BIT(0)
|
||||
|
||||
#define RTL8168_NUM_LEDS 3
|
||||
#define RTL8125_NUM_LEDS 4
|
||||
|
||||
struct r8169_led_classdev {
|
||||
struct led_classdev led;
|
||||
struct net_device *ndev;
|
||||
int index;
|
||||
};
|
||||
|
||||
#define lcdev_to_r8169_ldev(lcdev) container_of(lcdev, struct r8169_led_classdev, led)
|
||||
|
||||
static bool r8169_trigger_mode_is_valid(unsigned long flags)
|
||||
{
|
||||
bool rx, tx;
|
||||
|
||||
if (flags & BIT(TRIGGER_NETDEV_HALF_DUPLEX))
|
||||
return false;
|
||||
if (flags & BIT(TRIGGER_NETDEV_FULL_DUPLEX))
|
||||
return false;
|
||||
|
||||
rx = flags & BIT(TRIGGER_NETDEV_RX);
|
||||
tx = flags & BIT(TRIGGER_NETDEV_TX);
|
||||
|
||||
return rx == tx;
|
||||
}
|
||||
|
||||
static int rtl8168_led_hw_control_is_supported(struct led_classdev *led_cdev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
int shift = ldev->index * 4;
|
||||
|
||||
if (!r8169_trigger_mode_is_valid(flags)) {
|
||||
/* Switch LED off to indicate that mode isn't supported */
|
||||
rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8168_led_hw_control_set(struct led_classdev *led_cdev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
int shift = ldev->index * 4;
|
||||
u16 mode = 0;
|
||||
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_10))
|
||||
mode |= RTL8168_LED_CTRL_LINK_10;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_100))
|
||||
mode |= RTL8168_LED_CTRL_LINK_100;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
|
||||
mode |= RTL8168_LED_CTRL_LINK_1000;
|
||||
if (flags & BIT(TRIGGER_NETDEV_TX))
|
||||
mode |= RTL8168_LED_CTRL_ACT;
|
||||
|
||||
return rtl8168_led_mod_ctrl(tp, 0x000f << shift, mode << shift);
|
||||
}
|
||||
|
||||
static int rtl8168_led_hw_control_get(struct led_classdev *led_cdev,
|
||||
unsigned long *flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
int shift = ldev->index * 4;
|
||||
int mode;
|
||||
|
||||
mode = rtl8168_get_led_mode(tp);
|
||||
if (mode < 0)
|
||||
return mode;
|
||||
|
||||
if (mode & RTL8168_LED_CTRL_OPTION2) {
|
||||
rtl8168_led_mod_ctrl(tp, RTL8168_LED_CTRL_OPTION2, 0);
|
||||
netdev_notice(ldev->ndev, "Deactivating unsupported Option2 LED mode\n");
|
||||
}
|
||||
|
||||
mode = (mode >> shift) & 0x000f;
|
||||
|
||||
if (mode & RTL8168_LED_CTRL_ACT)
|
||||
*flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
|
||||
|
||||
if (mode & RTL8168_LED_CTRL_LINK_10)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_10);
|
||||
if (mode & RTL8168_LED_CTRL_LINK_100)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_100);
|
||||
if (mode & RTL8168_LED_CTRL_LINK_1000)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device *
|
||||
r8169_led_hw_control_get_device(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
|
||||
return &ldev->ndev->dev;
|
||||
}
|
||||
|
||||
static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev,
|
||||
struct net_device *ndev, int index)
|
||||
{
|
||||
struct rtl8169_private *tp = netdev_priv(ndev);
|
||||
struct led_classdev *led_cdev = &ldev->led;
|
||||
char led_name[LED_MAX_NAME_SIZE];
|
||||
|
||||
ldev->ndev = ndev;
|
||||
ldev->index = index;
|
||||
|
||||
r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);
|
||||
led_cdev->name = led_name;
|
||||
led_cdev->hw_control_trigger = "netdev";
|
||||
led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
|
||||
led_cdev->hw_control_is_supported = rtl8168_led_hw_control_is_supported;
|
||||
led_cdev->hw_control_set = rtl8168_led_hw_control_set;
|
||||
led_cdev->hw_control_get = rtl8168_led_hw_control_get;
|
||||
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
|
||||
|
||||
/* ignore errors */
|
||||
led_classdev_register(&ndev->dev, led_cdev);
|
||||
}
|
||||
|
||||
struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev)
|
||||
{
|
||||
struct r8169_led_classdev *leds;
|
||||
int i;
|
||||
|
||||
leds = kcalloc(RTL8168_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
|
||||
if (!leds)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < RTL8168_NUM_LEDS; i++)
|
||||
rtl8168_setup_ldev(leds + i, ndev, i);
|
||||
|
||||
return leds;
|
||||
}
|
||||
|
||||
static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
|
||||
if (!r8169_trigger_mode_is_valid(flags)) {
|
||||
/* Switch LED off to indicate that mode isn't supported */
|
||||
rtl8125_set_led_mode(tp, ldev->index, 0);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8125_led_hw_control_set(struct led_classdev *led_cdev,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
u16 mode = 0;
|
||||
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_10))
|
||||
mode |= RTL8125_LED_CTRL_LINK_10;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_100))
|
||||
mode |= RTL8125_LED_CTRL_LINK_100;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
|
||||
mode |= RTL8125_LED_CTRL_LINK_1000;
|
||||
if (flags & BIT(TRIGGER_NETDEV_LINK_2500))
|
||||
mode |= RTL8125_LED_CTRL_LINK_2500;
|
||||
if (flags & (BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX)))
|
||||
mode |= RTL8125_LED_CTRL_ACT;
|
||||
|
||||
return rtl8125_set_led_mode(tp, ldev->index, mode);
|
||||
}
|
||||
|
||||
static int rtl8125_led_hw_control_get(struct led_classdev *led_cdev,
|
||||
unsigned long *flags)
|
||||
{
|
||||
struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);
|
||||
struct rtl8169_private *tp = netdev_priv(ldev->ndev);
|
||||
int mode;
|
||||
|
||||
mode = rtl8125_get_led_mode(tp, ldev->index);
|
||||
if (mode < 0)
|
||||
return mode;
|
||||
|
||||
if (mode & RTL8125_LED_CTRL_LINK_10)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_10);
|
||||
if (mode & RTL8125_LED_CTRL_LINK_100)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_100);
|
||||
if (mode & RTL8125_LED_CTRL_LINK_1000)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_1000);
|
||||
if (mode & RTL8125_LED_CTRL_LINK_2500)
|
||||
*flags |= BIT(TRIGGER_NETDEV_LINK_2500);
|
||||
if (mode & RTL8125_LED_CTRL_ACT)
|
||||
*flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev,
|
||||
struct net_device *ndev, int index)
|
||||
{
|
||||
struct rtl8169_private *tp = netdev_priv(ndev);
|
||||
struct led_classdev *led_cdev = &ldev->led;
|
||||
char led_name[LED_MAX_NAME_SIZE];
|
||||
|
||||
ldev->ndev = ndev;
|
||||
ldev->index = index;
|
||||
|
||||
r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);
|
||||
led_cdev->name = led_name;
|
||||
led_cdev->hw_control_trigger = "netdev";
|
||||
led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
|
||||
led_cdev->hw_control_is_supported = rtl8125_led_hw_control_is_supported;
|
||||
led_cdev->hw_control_set = rtl8125_led_hw_control_set;
|
||||
led_cdev->hw_control_get = rtl8125_led_hw_control_get;
|
||||
led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;
|
||||
|
||||
/* ignore errors */
|
||||
led_classdev_register(&ndev->dev, led_cdev);
|
||||
}
|
||||
|
||||
struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev)
|
||||
{
|
||||
struct r8169_led_classdev *leds;
|
||||
int i;
|
||||
|
||||
leds = kcalloc(RTL8125_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);
|
||||
if (!leds)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < RTL8125_NUM_LEDS; i++)
|
||||
rtl8125_setup_led_ldev(leds + i, ndev, i);
|
||||
|
||||
return leds;
|
||||
}
|
||||
|
||||
void r8169_remove_leds(struct r8169_led_classdev *leds)
|
||||
{
|
||||
if (!leds)
|
||||
return;
|
||||
|
||||
for (struct r8169_led_classdev *l = leds; l->ndev; l++)
|
||||
led_classdev_unregister(&l->led);
|
||||
|
||||
kfree(leds);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue