Commit be51ed10 authored by Heiner Kallweit's avatar Heiner Kallweit Committed by Jakub Kicinski
Browse files

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: default avatarHeiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Link: https://lore.kernel.org/r/f982602c-9de3-4ca6-85a3-2c1d118dcb15@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 4a78f017
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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);
+106 −0
Original line number Diff line number Diff line
@@ -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);
}
+57 −4
Original line number Diff line number Diff line
@@ -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)
	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);