drivers/net/r8168: Add customized LED mode

This patch adds a function to program a customized LED setting
for Realtek 81xx family. It reads the settings from devicetree under
target board and programs the setting to offset 0x18 and 0x19.

BUG=b:65437780

TEST=Add customized_leds register in devicetree.cb under target board,
	enable RT8168_SET_LED_MODE flag. Make sure the setting is
	programmed correctly to offset 0x18 and 0x19. Observed the
	LEDs were behaving as expected. Executed suspend/resume and
	the LEDs were still working as expected.

Change-Id: Ib3d4f2cd98ac391e1661a891d604bdd1974d07f6
Signed-off-by: Gaggery Tsai <gaggery.tsai@intel.com>
Reviewed-on: https://review.coreboot.org/21862
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-by: Furquan Shaikh <furquan@google.com>
diff --git a/src/drivers/net/Kconfig b/src/drivers/net/Kconfig
index 67a1389..bd6f09a 100644
--- a/src/drivers/net/Kconfig
+++ b/src/drivers/net/Kconfig
@@ -18,3 +18,14 @@
 	bool
 	default n
 	select REALTEK_8168_RESET
+
+config RT8168_SET_LED_MODE
+	bool
+	default n
+	select REALTEK_8168_RESET
+	help
+	  This is to set a customized LED mode to distinguish 10/100/1000
+	  link and speed status with limited LEDs avaiable on a board.
+	  Please refer to RTL811x datasheet section 7.2 Customizable LED
+	  Configuration for details. With this flag enabled, the
+	  customized_leds variable will be read from devicetree setting.
diff --git a/src/drivers/net/chip.h b/src/drivers/net/chip.h
new file mode 100644
index 0000000..7a37cde
--- /dev/null
+++ b/src/drivers/net/chip.h
@@ -0,0 +1,21 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DRIVERS_R8168_CHIP_H__
+#define __DRIVERS_R8168_CHIP_H__
+
+struct drivers_net_config {
+	uint16_t customized_leds;
+};
+
+#endif /* __DRIVERS_R8168_CHIP_H__ */
diff --git a/src/drivers/net/r8168.c b/src/drivers/net/r8168.c
index 3727ccd..350dc37 100644
--- a/src/drivers/net/r8168.c
+++ b/src/drivers/net/r8168.c
@@ -31,11 +31,13 @@
 #include <device/pci_def.h>
 #include <delay.h>
 #include <fmap.h>
+#include "chip.h"
 
 #define NIC_TIMEOUT		1000
 
 #define CMD_REG			0x37
 #define  CMD_REG_RESET		0x10
+#define CMD_LED0_LED1		0x18
 
 #define CFG_9346		0x50
 #define  CFG_9346_LOCK		0x00
@@ -211,22 +213,59 @@
 	printk(BIOS_DEBUG, "done\n");
 }
 
+static void r8168_set_customized_led(struct device *dev, u16 io_base)
+{
+	struct drivers_net_config *config = dev->chip_info;
+
+	if (!config)
+		return;
+
+	/* Read the customized LED setting from devicetree */
+	printk(BIOS_DEBUG, "r8168: Customized LED 0x%x\n", config->customized_leds);
+
+	/*
+	 * Refer to RTL8111H datasheet 7.2 Customizable LED Configuration
+	 * Starting from offset 0x18
+	 * Bit[15:12]	LED Feature Control(FC)
+	 * Bit[11:08]	LED Select for PINLED2
+	 * Bit[07:04]	LED Select for PINLED1
+	 * Bit[03:00]	LED Select for PINLED0
+	 *
+	 * Speed	Link10M		Link100M	Link1000M	ACT/Full
+	 * LED0		Bit0		Bit1		Bit2		Bit3
+	 * LED1		Bit4		Bit5		Bit6		Bit7
+	 * LED2		Bit8		Bit9		Bit10		Bit11
+	 * FC		Bit12		Bit13		Bit14		Bit15
+	 */
+
+	/* Set customized LED registers */
+	outw(config->customized_leds, io_base + CMD_LED0_LED1);
+	printk(BIOS_DEBUG, "r8168: read back LED setting as 0x%x\n",
+		inw(io_base + CMD_LED0_LED1));
+}
+
 static void r8168_init(struct device *dev)
 {
 	/* Get the resource of the NIC mmio */
 	struct resource *nic_res = find_resource(dev, PCI_BASE_ADDRESS_0);
 	u16 io_base = (u16)nic_res->base;
 
+	/* Check if the base is invalid */
+	if (!io_base) {
+		printk(BIOS_ERR, "r8168: Error cant find IO resource\n");
+		return;
+	}
 	/* Enable but do not set bus master */
 	pci_write_config16(dev, PCI_COMMAND,
 			   PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
 
 	/* Program MAC address based on CBFS "macaddress" containing
 	 * a string AA:BB:CC:DD:EE:FF */
-	if (io_base)
-		program_mac_address(dev, io_base);
-	else
-		printk(BIOS_ERR, "r8168: Error cant find MMIO resource\n");
+	program_mac_address(dev, io_base);
+
+	/* Program customized LED mode */
+	if (IS_ENABLED(CONFIG_RT8168_SET_LED_MODE))
+		r8168_set_customized_led(dev, io_base);
 }
 
 static struct device_operations r8168_ops  = {