r8169: add LED support for RTL8125/RTL8126
This adds LED support for RTL8125/RTL8126. Note: Due to missing datasheets changing the 5Gbps link mode isn't supported for RTL8126. Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Link: https://lore.kernel.org/r/f982602c-9de3-4ca6-85a3-2c1d118dcb15@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
4a78f0173b
commit
be51ed104b
@ -85,3 +85,6 @@ void r8169_get_led_name(struct rtl8169_private *tp, int idx,
|
||||
int rtl8168_get_led_mode(struct rtl8169_private *tp);
|
||||
int rtl8168_led_mod_ctrl(struct rtl8169_private *tp, u16 mask, u16 val);
|
||||
void 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);
|
||||
void rtl8125_init_leds(struct net_device *ndev);
|
||||
|
@ -18,7 +18,14 @@
|
||||
#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;
|
||||
@ -156,3 +163,102 @@ void rtl8168_init_leds(struct net_device *ndev)
|
||||
for (i = 0; i < RTL8168_NUM_LEDS; i++)
|
||||
rtl8168_setup_ldev(leds + i, ndev, i);
|
||||
}
|
||||
|
||||
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 */
|
||||
devm_led_classdev_register(&ndev->dev, led_cdev);
|
||||
}
|
||||
|
||||
void rtl8125_init_leds(struct net_device *ndev)
|
||||
{
|
||||
/* bind resource mgmt to netdev */
|
||||
struct device *dev = &ndev->dev;
|
||||
struct r8169_led_classdev *leds;
|
||||
int i;
|
||||
|
||||
leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
|
||||
if (!leds)
|
||||
return;
|
||||
|
||||
for (i = 0; i < RTL8125_NUM_LEDS; i++)
|
||||
rtl8125_setup_led_ldev(leds + i, ndev, i);
|
||||
}
|
||||
|
@ -330,17 +330,23 @@ enum rtl8168_registers {
|
||||
};
|
||||
|
||||
enum rtl8125_registers {
|
||||
LEDSEL0 = 0x18,
|
||||
INT_CFG0_8125 = 0x34,
|
||||
#define INT_CFG0_ENABLE_8125 BIT(0)
|
||||
#define INT_CFG0_CLKREQEN BIT(3)
|
||||
IntrMask_8125 = 0x38,
|
||||
IntrStatus_8125 = 0x3c,
|
||||
INT_CFG1_8125 = 0x7a,
|
||||
LEDSEL2 = 0x84,
|
||||
LEDSEL1 = 0x86,
|
||||
TxPoll_8125 = 0x90,
|
||||
LEDSEL3 = 0x96,
|
||||
MAC0_BKP = 0x19e0,
|
||||
EEE_TXIDLE_TIMER_8125 = 0x6048,
|
||||
};
|
||||
|
||||
#define LEDSEL_MASK_8125 0x23f
|
||||
|
||||
#define RX_VLAN_INNER_8125 BIT(22)
|
||||
#define RX_VLAN_OUTER_8125 BIT(23)
|
||||
#define RX_VLAN_8125 (RX_VLAN_INNER_8125 | RX_VLAN_OUTER_8125)
|
||||
@ -830,6 +836,51 @@ int rtl8168_get_led_mode(struct rtl8169_private *tp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtl8125_get_led_reg(int index)
|
||||
{
|
||||
static const int led_regs[] = { LEDSEL0, LEDSEL1, LEDSEL2, LEDSEL3 };
|
||||
|
||||
return led_regs[index];
|
||||
}
|
||||
|
||||
int rtl8125_set_led_mode(struct rtl8169_private *tp, int index, u16 mode)
|
||||
{
|
||||
int reg = rtl8125_get_led_reg(index);
|
||||
struct device *dev = tp_to_dev(tp);
|
||||
int ret;
|
||||
u16 val;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&tp->led_lock);
|
||||
val = RTL_R16(tp, reg) & ~LEDSEL_MASK_8125;
|
||||
RTL_W16(tp, reg, val | mode);
|
||||
mutex_unlock(&tp->led_lock);
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rtl8125_get_led_mode(struct rtl8169_private *tp, int index)
|
||||
{
|
||||
int reg = rtl8125_get_led_reg(index);
|
||||
struct device *dev = tp_to_dev(tp);
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = RTL_R16(tp, reg);
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void r8169_get_led_name(struct rtl8169_private *tp, int idx,
|
||||
char *buf, int buf_len)
|
||||
{
|
||||
@ -5385,10 +5436,12 @@ static int rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (IS_ENABLED(CONFIG_R8169_LEDS) &&
|
||||
tp->mac_version > RTL_GIGA_MAC_VER_06 &&
|
||||
tp->mac_version < RTL_GIGA_MAC_VER_61)
|
||||
rtl8168_init_leds(dev);
|
||||
if (IS_ENABLED(CONFIG_R8169_LEDS)) {
|
||||
if (rtl_is_8125(tp))
|
||||
rtl8125_init_leds(dev);
|
||||
else if (tp->mac_version > RTL_GIGA_MAC_VER_06)
|
||||
rtl8168_init_leds(dev);
|
||||
}
|
||||
|
||||
netdev_info(dev, "%s, %pM, XID %03x, IRQ %d\n",
|
||||
rtl_chip_infos[chipset].name, dev->dev_addr, xid, tp->irq);
|
||||
|
Loading…
x
Reference in New Issue
Block a user