diff --git a/content/post/rtl8153b-for-4.9.md b/content/post/rtl8153b-for-4.9.md
new file mode 100644
index 0000000..07e6ca3
--- /dev/null
+++ b/content/post/rtl8153b-for-4.9.md
@@ -0,0 +1,27 @@
+---
+title: RTL8153B support for 4.9 kernel
+date: !!timestamp '2019-03-17 00:00:00'
+tags:
+    - kernel
+---
+
+If you buy a *recent* USB to Ethernet adapter, embedding a Realtek chip, you
+possibly face, like me, the following error, when connecting it:
+
+```
+r8152 4-1.1:1.0 (unnamed net_device) (uninitialized): Unknown version 0x6010
+r8152 4-1.1:1.0 (unnamed net_device) (uninitialized): Unknown Device
+```
+
+<!--more-->
+
+This error is raised because your Linux r8152 driver is too old and doesn't
+support your chip's variant.
+
+In my case, I got the variant `0x6010` which has only been implemented in Linux
+4.13, in the [commit
+65b82d69...d3e7fb976](https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/net/usb/r8152.c?h=v4.13&id=65b82d696b9e84fda6dd7df61801b57d3e7fb976).
+
+As I run at the time a 4.9 kernel and can't upgrade to a newer release, I
+backport the commit to the 4.9 branch. You can find [the patch I made
+here](r8152-for-4.9.patch). It only adds support for RTL8153B.
diff --git a/static/post/rtl8153b-for-4.9/r8152-for-4.9.patch b/static/post/rtl8153b-for-4.9/r8152-for-4.9.patch
new file mode 100644
index 0000000..8122cd9
--- /dev/null
+++ b/static/post/rtl8153b-for-4.9/r8152-for-4.9.patch
@@ -0,0 +1,1179 @@
+From: Pierre-Olivier Mercier <nemunaire@nemunai.re>
+Date: Sat, 17 Mar 2019 00:23:00 +0100
+Subject: backport r8152: support RTL8153B
+
+This backports the upstream commit 65b82d696b9e84fda6dd7df61801b57d3e7fb976
+to the 4.9 branch.
+
+--- a/drivers/net/usb/r8152.c	2019-03-16 23:13:55.792422464 +0100
++++ b/drivers/net/usb/r8152.c	2019-03-16 23:52:01.542199652 +0100
+@@ -29,7 +29,7 @@
+ #include <linux/acpi.h>
+
+ /* Information for net-next */
+-#define NETNEXT_VERSION		"08"
++#define NETNEXT_VERSION		"09"
+
+ /* Information for net */
+ #define NET_VERSION		"7"
+@@ -51,11 +51,14 @@
+ #define PLA_FMC			0xc0b4
+ #define PLA_CFG_WOL		0xc0b6
+ #define PLA_TEREDO_CFG		0xc0bc
++#define PLA_TEREDO_WAKE_BASE	0xc0c4
+ #define PLA_MAR			0xcd00
+ #define PLA_BACKUP		0xd000
+ #define PAL_BDC_CR		0xd1a0
+ #define PLA_TEREDO_TIMER	0xd2cc
+ #define PLA_REALWOW_TIMER	0xd2e8
++#define PLA_EFUSE_DATA		0xdd00
++#define PLA_EFUSE_CMD		0xdd02
+ #define PLA_LEDSEL		0xdd90
+ #define PLA_LED_FEATURE		0xdd92
+ #define PLA_PHYAR		0xde00
+@@ -105,7 +108,9 @@
+ #define USB_CSR_DUMMY2		0xb466
+ #define USB_DEV_STAT		0xb808
+ #define USB_CONNECT_TIMER	0xcbf8
++#define USB_MSC_TIMER		0xcbfc
+ #define USB_BURST_SIZE		0xcfc0
++#define USB_LPM_CONFIG		0xcfd8
+ #define USB_USB_CTRL		0xd406
+ #define USB_PHY_CTRL		0xd408
+ #define USB_TX_AGG		0xd40a
+@@ -113,15 +118,20 @@
+ #define USB_USB_TIMER		0xd428
+ #define USB_RX_EARLY_TIMEOUT	0xd42c
+ #define USB_RX_EARLY_SIZE	0xd42e
+-#define USB_PM_CTRL_STATUS	0xd432
++#define USB_PM_CTRL_STATUS	0xd432	/* RTL8153A */
++#define USB_RX_EXTRA_AGGR_TMR	0xd432	/* RTL8153B */
+ #define USB_TX_DMA		0xd434
++#define USB_UPT_RXDMA_OWN	0xd437
+ #define USB_TOLERANCE		0xd490
+ #define USB_LPM_CTRL		0xd41a
+ #define USB_BMU_RESET		0xd4b0
++#define USB_U1U2_TIMER		0xd4da
+ #define USB_UPS_CTRL		0xd800
+-#define USB_MISC_0		0xd81a
+ #define USB_POWER_CUT		0xd80a
++#define USB_MISC_0		0xd81a
+ #define USB_AFE_CTRL2		0xd824
++#define USB_UPS_CFG		0xd842
++#define USB_UPS_FLAGS		0xd848
+ #define USB_WDT11_CTRL		0xe43c
+ #define USB_BP_BA		0xfc26
+ #define USB_BP_0		0xfc28
+@@ -133,6 +143,15 @@
+ #define USB_BP_6		0xfc34
+ #define USB_BP_7		0xfc36
+ #define USB_BP_EN		0xfc38
++#define USB_BP_8		0xfc38
++#define USB_BP_9		0xfc3a
++#define USB_BP_10		0xfc3c
++#define USB_BP_11		0xfc3e
++#define USB_BP_12		0xfc40
++#define USB_BP_13		0xfc42
++#define USB_BP_14		0xfc44
++#define USB_BP_15		0xfc46
++#define USB_BP2_EN		0xfc48
+
+ /* OCP Registers */
+ #define OCP_ALDPS_CONFIG	0x2010
+@@ -143,6 +162,7 @@
+ #define OCP_EEE_AR		0xa41a
+ #define OCP_EEE_DATA		0xa41c
+ #define OCP_PHY_STATUS		0xa420
++#define OCP_NCTL_CFG		0xa42c
+ #define OCP_POWER_CFG		0xa430
+ #define OCP_EEE_CFG		0xa432
+ #define OCP_SRAM_ADDR		0xa436
+@@ -152,9 +172,14 @@
+ #define OCP_EEE_ADV		0xa5d0
+ #define OCP_EEE_LPABLE		0xa5d2
+ #define OCP_PHY_STATE		0xa708		/* nway state for 8153 */
++#define OCP_PHY_PATCH_STAT	0xb800
++#define OCP_PHY_PATCH_CMD	0xb820
++#define OCP_ADC_IOFFSET		0xbcfc
+ #define OCP_ADC_CFG		0xbc06
++#define OCP_SYSCLK_CFG		0xc416
+
+ /* SRAM Register */
++#define SRAM_GREEN_CFG		0x8011
+ #define SRAM_LPF_CFG		0x8012
+ #define SRAM_10M_AMP1		0x8080
+ #define SRAM_10M_AMP2		0x8082
+@@ -252,6 +277,10 @@
+ /* PAL_BDC_CR */
+ #define ALDPS_PROXY_MODE	0x0001
+
++/* PLA_EFUSE_CMD */
++#define EFUSE_READ_CMD		BIT(15)
++#define EFUSE_DATA_BIT16	BIT(7)
++
+ /* PLA_CONFIG34 */
+ #define LINK_ON_WAKE_EN		0x0010
+ #define LINK_OFF_WAKE_EN	0x0008
+@@ -277,6 +306,7 @@
+
+ /* PLA_MAC_PWR_CTRL2 */
+ #define EEE_SPDWN_RATIO		0x8007
++#define MAC_CLK_SPDWN_EN	BIT(15)
+
+ /* PLA_MAC_PWR_CTRL3 */
+ #define PKT_AVAIL_SPDWN_EN	0x0100
+@@ -328,6 +358,9 @@
+ #define STAT_SPEED_HIGH		0x0000
+ #define STAT_SPEED_FULL		0x0002
+
++/* USB_LPM_CONFIG */
++#define LPM_U1U2_EN		BIT(0)
++
+ /* USB_TX_AGG */
+ #define TX_AGG_MAX_THRESHOLD	0x03
+
+@@ -335,6 +368,7 @@
+ #define RX_THR_SUPPER		0x0c350180
+ #define RX_THR_HIGH		0x7a120180
+ #define RX_THR_SLOW		0xffff0180
++#define RX_THR_B		0x00010001
+
+ /* USB_TX_DMA */
+ #define TEST_MODE_DISABLE	0x00000001
+@@ -344,6 +378,10 @@
+ #define BMU_RESET_EP_IN		0x01
+ #define BMU_RESET_EP_OUT	0x02
+
++/* USB_UPT_RXDMA_OWN */
++#define OWN_UPDATE		BIT(0)
++#define OWN_CLEAR		BIT(1)
++
+ /* USB_UPS_CTRL */
+ #define POWER_CUT		0x0100
+
+@@ -360,6 +398,8 @@
+ /* USB_POWER_CUT */
+ #define PWR_EN			0x0001
+ #define PHASE2_EN		0x0008
++#define UPS_EN			BIT(4)
++#define USP_PREWAKE		BIT(5)
+
+ /* USB_MISC_0 */
+ #define PCUT_STATUS		0x0001
+@@ -386,6 +426,37 @@
+ #define SEN_VAL_NORMAL		0xa000
+ #define SEL_RXIDLE		0x0100
+
++/* USB_UPS_CFG */
++#define SAW_CNT_1MS_MASK	0x0fff
++
++/* USB_UPS_FLAGS */
++#define UPS_FLAGS_R_TUNE		BIT(0)
++#define UPS_FLAGS_EN_10M_CKDIV		BIT(1)
++#define UPS_FLAGS_250M_CKDIV		BIT(2)
++#define UPS_FLAGS_EN_ALDPS		BIT(3)
++#define UPS_FLAGS_CTAP_SHORT_DIS	BIT(4)
++#define UPS_FLAGS_SPEED_MASK		(0xf << 16)
++#define ups_flags_speed(x)		((x) << 16)
++#define UPS_FLAGS_EN_EEE		BIT(20)
++#define UPS_FLAGS_EN_500M_EEE		BIT(21)
++#define UPS_FLAGS_EN_EEE_CKDIV		BIT(22)
++#define UPS_FLAGS_EEE_PLLOFF_GIGA	BIT(24)
++#define UPS_FLAGS_EEE_CMOD_LV_EN	BIT(25)
++#define UPS_FLAGS_EN_GREEN		BIT(26)
++#define UPS_FLAGS_EN_FLOW_CTR		BIT(27)
++
++enum spd_duplex {
++	NWAY_10M_HALF = 1,
++	NWAY_10M_FULL,
++	NWAY_100M_HALF,
++	NWAY_100M_FULL,
++	NWAY_1000M_FULL,
++	FORCE_10M_HALF,
++	FORCE_10M_FULL,
++	FORCE_100M_HALF,
++	FORCE_100M_FULL,
++};
++
+ /* OCP_ALDPS_CONFIG */
+ #define ENPWRSAVE		0x8000
+ #define ENPDNPS			0x0200
+@@ -394,9 +465,13 @@
+
+ /* OCP_PHY_STATUS */
+ #define PHY_STAT_MASK		0x0007
++#define PHY_STAT_EXT_INIT	2
+ #define PHY_STAT_LAN_ON		3
+ #define PHY_STAT_PWRDN		5
+
++/* OCP_NCTL_CFG */
++#define PGA_RETURN_EN		BIT(1)
++
+ /* OCP_POWER_CFG */
+ #define EEE_CLKDIV_EN		0x8000
+ #define EN_ALDPS		0x0004
+@@ -438,17 +513,34 @@
+ #define EEE10_EN		0x0010
+
+ /* OCP_DOWN_SPEED */
++#define EN_EEE_CMODE		BIT(14)
++#define EN_EEE_1000		BIT(13)
++#define EN_EEE_100		BIT(12)
++#define EN_10M_CLKDIV		BIT(11)
+ #define EN_10M_BGOFF		0x0080
+
+ /* OCP_PHY_STATE */
+ #define TXDIS_STATE		0x01
+ #define ABD_STATE		0x02
+
++/* OCP_PHY_PATCH_STAT */
++#define PATCH_READY		BIT(6)
++
++/* OCP_PHY_PATCH_CMD */
++#define PATCH_REQUEST		BIT(4)
++
+ /* OCP_ADC_CFG */
+ #define CKADSEL_L		0x0100
+ #define ADC_EN			0x0080
+ #define EN_EMI_L		0x0040
+
++/* OCP_SYSCLK_CFG */
++#define clk_div_expo(x)		(min(x, 5) << 8)
++
++/* SRAM_GREEN_CFG */
++#define GREEN_ETH_EN		BIT(15)
++#define R_TUNE_EN		BIT(11)
++
+ /* SRAM_LPF_CFG */
+ #define LPF_AUTO_TUNE		0x8000
+
+@@ -511,6 +603,7 @@
+ 	SELECTIVE_SUSPEND,
+ 	PHY_RESET,
+ 	SCHEDULE_NAPI,
++	GREEN_ETHERNET,
+ };
+
+ /* Define these values to match your device */
+@@ -655,6 +748,9 @@
+ 	RTL_VER_04,
+ 	RTL_VER_05,
+ 	RTL_VER_06,
++	RTL_VER_07,
++	RTL_VER_08,
++	RTL_VER_09,
+ 	RTL_VER_MAX
+ };
+
+@@ -985,6 +1081,12 @@
+ 	ocp_reg_write(tp, OCP_SRAM_DATA, data);
+ }
+
++static u16 sram_read(struct r8152 *tp, u16 addr)
++{
++	ocp_reg_write(tp, OCP_SRAM_ADDR, addr);
++	return ocp_reg_read(tp, OCP_SRAM_DATA);
++}
++
+ static int read_mii_word(struct net_device *netdev, int phy_id, int reg)
+ {
+ 	struct r8152 *tp = netdev_priv(netdev);
+@@ -2240,11 +2342,40 @@
+ 	return rtl_enable(tp);
+ }
+
++static inline void r8153b_rx_agg_chg_indicate(struct r8152 *tp)
++{
++	ocp_write_byte(tp, MCU_TYPE_USB, USB_UPT_RXDMA_OWN,
++		       OWN_UPDATE | OWN_CLEAR);
++}
++
+ static void r8153_set_rx_early_timeout(struct r8152 *tp)
+ {
+ 	u32 ocp_data = tp->coalesce / 8;
+
+-	ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT, ocp_data);
++	switch (tp->version) {
++	case RTL_VER_03:
++	case RTL_VER_04:
++	case RTL_VER_05:
++	case RTL_VER_06:
++		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT,
++			       ocp_data);
++		break;
++
++	case RTL_VER_08:
++	case RTL_VER_09:
++		/* The RTL8153B uses USB_RX_EXTRA_AGGR_TMR for rx timeout
++		 * primarily. For USB_RX_EARLY_TIMEOUT, we fix it to 128ns.
++		 */
++		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_TIMEOUT,
++			       128 / 8);
++		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EXTRA_AGGR_TMR,
++			       ocp_data);
++		r8153b_rx_agg_chg_indicate(tp);
++		break;
++
++	default:
++		break;
++	}
+ }
+
+ static void r8153_set_rx_early_size(struct r8152 *tp)
+@@ -2252,7 +2383,24 @@
+ 	u32 mtu = tp->netdev->mtu;
+ 	u32 ocp_data = (agg_buf_sz - mtu - VLAN_ETH_HLEN - VLAN_HLEN) / 8;
+
+-	ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE, ocp_data);
++	switch (tp->version) {
++	case RTL_VER_03:
++	case RTL_VER_04:
++	case RTL_VER_05:
++	case RTL_VER_06:
++		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
++			       ocp_data / 4);
++		break;
++	case RTL_VER_08:
++	case RTL_VER_09:
++		ocp_write_word(tp, MCU_TYPE_USB, USB_RX_EARLY_SIZE,
++			       ocp_data / 8);
++		r8153b_rx_agg_chg_indicate(tp);
++		break;
++	default:
++		WARN_ON_ONCE(1);
++		break;
++	}
+ }
+
+ static int rtl8153_enable(struct r8152 *tp)
+@@ -2438,6 +2586,19 @@
+ 	usb_ocp_write(tp, USB_TOLERANCE, BYTE_EN_SIX_BYTES, sizeof(u1u2), u1u2);
+ }
+
++static void r8153b_u1u2en(struct r8152 *tp, bool enable)
++{
++	u32 ocp_data;
++
++	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_LPM_CONFIG);
++	if (enable)
++		ocp_data |= LPM_U1U2_EN;
++	else
++		ocp_data &= ~LPM_U1U2_EN;
++
++	ocp_write_word(tp, MCU_TYPE_USB, USB_LPM_CONFIG, ocp_data);
++}
++
+ static void r8153_u2p3en(struct r8152 *tp, bool enable)
+ {
+ 	u32 ocp_data;
+@@ -2450,6 +2611,108 @@
+ 	ocp_write_word(tp, MCU_TYPE_USB, USB_U2P3_CTRL, ocp_data);
+ }
+
++static void r8153b_ups_flags_w1w0(struct r8152 *tp, u32 set, u32 clear)
++{
++	u32 ocp_data;
++
++	ocp_data = ocp_read_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS);
++	ocp_data &= ~clear;
++	ocp_data |= set;
++	ocp_write_dword(tp, MCU_TYPE_USB, USB_UPS_FLAGS, ocp_data);
++}
++
++static void r8153b_green_en(struct r8152 *tp, bool enable)
++{
++	u16 data;
++
++	if (enable) {
++		sram_write(tp, 0x8045, 0);	/* 10M abiq&ldvbias */
++		sram_write(tp, 0x804d, 0x1222);	/* 100M short abiq&ldvbias */
++		sram_write(tp, 0x805d, 0x0022);	/* 1000M short abiq&ldvbias */
++	} else {
++		sram_write(tp, 0x8045, 0x2444);	/* 10M abiq&ldvbias */
++		sram_write(tp, 0x804d, 0x2444);	/* 100M short abiq&ldvbias */
++		sram_write(tp, 0x805d, 0x2444);	/* 1000M short abiq&ldvbias */
++	}
++
++	data = sram_read(tp, SRAM_GREEN_CFG);
++	data |= GREEN_ETH_EN;
++	sram_write(tp, SRAM_GREEN_CFG, data);
++
++	r8153b_ups_flags_w1w0(tp, UPS_FLAGS_EN_GREEN, 0);
++}
++
++static u16 r8153_phy_status(struct r8152 *tp, u16 desired)
++{
++  u16 data;
++  int i;
++
++  for (i = 0; i < 500; i++) {
++    data = ocp_reg_read(tp, OCP_PHY_STATUS);
++    data &= PHY_STAT_MASK;
++    if (desired) {
++      if (data == desired)
++        break;
++    } else if (data == PHY_STAT_LAN_ON || data == PHY_STAT_PWRDN ||
++               data == PHY_STAT_EXT_INIT) {
++      break;
++    }
++
++    msleep(20);
++  }
++
++  return data;
++}
++
++static void r8153b_ups_en(struct r8152 *tp, bool enable)
++{
++	u32 ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_POWER_CUT);
++
++	if (enable) {
++		ocp_data |= UPS_EN | USP_PREWAKE | PHASE2_EN;
++		ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
++
++		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, 0xcfff);
++		ocp_data |= BIT(0);
++		ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data);
++	} else {
++		u16 data;
++
++		ocp_data &= ~(UPS_EN | USP_PREWAKE);
++		ocp_write_byte(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
++
++		ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, 0xcfff);
++		ocp_data &= ~BIT(0);
++		ocp_write_byte(tp, MCU_TYPE_USB, 0xcfff, ocp_data);
++
++		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
++		ocp_data &= ~PCUT_STATUS;
++		ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
++
++		data = r8153_phy_status(tp, 0);
++
++		switch (data) {
++		case PHY_STAT_PWRDN:
++		case PHY_STAT_EXT_INIT:
++			r8153b_green_en(tp,
++					test_bit(GREEN_ETHERNET, &tp->flags));
++
++			data = r8152_mdio_read(tp, MII_BMCR);
++			data &= ~BMCR_PDOWN;
++			data |= BMCR_RESET;
++			r8152_mdio_write(tp, MII_BMCR, data);
++
++			data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
++
++		default:
++			if (data != PHY_STAT_LAN_ON)
++				netif_warn(tp, link, tp->netdev,
++					   "PHY not ready");
++			break;
++		}
++	}
++}
++
+ static void r8153_power_cut_en(struct r8152 *tp, bool enable)
+ {
+ 	u32 ocp_data;
+@@ -2466,6 +2729,38 @@
+ 	ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
+ }
+
++static void r8153b_power_cut_en(struct r8152 *tp, bool enable)
++{
++	u32 ocp_data;
++
++	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_POWER_CUT);
++	if (enable)
++		ocp_data |= PWR_EN | PHASE2_EN;
++	else
++		ocp_data &= ~PWR_EN;
++	ocp_write_word(tp, MCU_TYPE_USB, USB_POWER_CUT, ocp_data);
++
++	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_MISC_0);
++	ocp_data &= ~PCUT_STATUS;
++	ocp_write_word(tp, MCU_TYPE_USB, USB_MISC_0, ocp_data);
++}
++
++static void r8153b_queue_wake(struct r8152 *tp, bool enable)
++{
++	u32 ocp_data;
++
++	ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, 0xd38a);
++	if (enable)
++		ocp_data |= BIT(0);
++	else
++		ocp_data &= ~BIT(0);
++	ocp_write_byte(tp, MCU_TYPE_PLA, 0xd38a, ocp_data);
++
++	ocp_data = ocp_read_byte(tp, MCU_TYPE_PLA, 0xd38c);
++	ocp_data &= ~BIT(0);
++	ocp_write_byte(tp, MCU_TYPE_PLA, 0xd38c, ocp_data);
++}
++
+ static bool rtl_can_wakeup(struct r8152 *tp)
+ {
+ 	struct usb_device *udev = tp->udev;
+@@ -2515,13 +2810,52 @@
+ 	}
+ }
+
++static void rtl8153b_runtime_enable(struct r8152 *tp, bool enable)
++{
++	if (enable) {
++		r8153b_queue_wake(tp, true);
++		r8153b_u1u2en(tp, false);
++		r8153_u2p3en(tp, false);
++		rtl_runtime_suspend_enable(tp, true);
++		r8153b_ups_en(tp, true);
++	} else {
++		r8153b_ups_en(tp, false);
++		r8153b_queue_wake(tp, false);
++		rtl_runtime_suspend_enable(tp, false);
++		r8153_u2p3en(tp, true);
++		r8153b_u1u2en(tp, true);
++	}
++}
++
+ static void r8153_teredo_off(struct r8152 *tp)
+ {
+ 	u32 ocp_data;
+
+-	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG);
+-	ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK | OOB_TEREDO_EN);
+-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data);
++	switch (tp->version) {
++	case RTL_VER_01:
++	case RTL_VER_02:
++	case RTL_VER_03:
++	case RTL_VER_04:
++	case RTL_VER_05:
++	case RTL_VER_06:
++	case RTL_VER_07:
++		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG);
++		ocp_data &= ~(TEREDO_SEL | TEREDO_RS_EVENT_MASK |
++			      OOB_TEREDO_EN);
++		ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data);
++		break;
++
++	case RTL_VER_08:
++	case RTL_VER_09:
++		/* The bit 0 ~ 7 are relative with teredo settings. They are
++		 * W1C (write 1 to clear), so set all 1 to disable it.
++		 */
++		ocp_write_byte(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, 0xff);
++		break;
++
++	default:
++		break;
++	}
+
+ 	ocp_write_word(tp, MCU_TYPE_PLA, PLA_WDT6_CTRL, WDT6_SET_MODE);
+ 	ocp_write_word(tp, MCU_TYPE_PLA, PLA_REALWOW_TIMER, 0);
+@@ -2767,6 +3101,33 @@
+ 	ocp_write_dword(tp, MCU_TYPE_PLA, PLA_RCR, ocp_data);
+ }
+
++static int r8153_patch_request(struct r8152 *tp, bool request)
++{
++	u16 data;
++	int i;
++
++	data = ocp_reg_read(tp, OCP_PHY_PATCH_CMD);
++	if (request)
++		data |= PATCH_REQUEST;
++	else
++		data &= ~PATCH_REQUEST;
++	ocp_reg_write(tp, OCP_PHY_PATCH_CMD, data);
++
++	for (i = 0; request && i < 5000; i++) {
++		usleep_range(1000, 2000);
++		if (ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)
++			break;
++	}
++
++	if (request && !(ocp_reg_read(tp, OCP_PHY_PATCH_STAT) & PATCH_READY)) {
++		netif_err(tp, drv, tp->netdev, "patch request fail\n");
++		r8153_patch_request(tp, false);
++		return -ETIME;
++	} else {
++		return 0;
++	}
++}
++
+ static void r8153_aldps_en(struct r8152 *tp, bool enable)
+ {
+ 	u16 data;
+@@ -2782,6 +3143,16 @@
+ 	}
+ }
+
++static void r8153b_aldps_en(struct r8152 *tp, bool enable)
++{
++	r8153_aldps_en(tp, enable);
++
++	if (enable)
++		r8153b_ups_flags_w1w0(tp, UPS_FLAGS_EN_ALDPS, 0);
++	else
++		r8153b_ups_flags_w1w0(tp, 0, UPS_FLAGS_EN_ALDPS);
++}
++
+ static void r8153_eee_en(struct r8152 *tp, bool enable)
+ {
+ 	u32 ocp_data;
+@@ -2802,6 +3173,22 @@
+ 	ocp_reg_write(tp, OCP_EEE_CFG, config);
+ }
+
++static void r8153b_eee_en(struct r8152 *tp, bool enable)
++{
++	r8153_eee_en(tp, enable);
++
++	if (enable)
++		r8153b_ups_flags_w1w0(tp, UPS_FLAGS_EN_EEE, 0);
++	else
++		r8153b_ups_flags_w1w0(tp, 0, UPS_FLAGS_EN_EEE);
++}
++
++static void r8153b_enable_fc(struct r8152 *tp)
++{
++	r8152b_enable_fc(tp);
++	r8153b_ups_flags_w1w0(tp, UPS_FLAGS_EN_FLOW_CTR, 0);
++}
++
+ static void r8153_hw_phy_cfg(struct r8152 *tp)
+ {
+ 	u32 ocp_data;
+@@ -2852,6 +3239,100 @@
+ 	set_bit(PHY_RESET, &tp->flags);
+ }
+
++static u32 r8152_efuse_read(struct r8152 *tp, u8 addr)
++{
++	u32 ocp_data;
++
++	ocp_write_word(tp, MCU_TYPE_PLA, PLA_EFUSE_CMD, EFUSE_READ_CMD | addr);
++	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_EFUSE_CMD);
++	ocp_data = (ocp_data & EFUSE_DATA_BIT16) << 9;	/* data of bit16 */
++	ocp_data |= ocp_read_word(tp, MCU_TYPE_PLA, PLA_EFUSE_DATA);
++
++	return ocp_data;
++}
++
++static void r8153b_hw_phy_cfg(struct r8152 *tp)
++{
++	u32 ocp_data, ups_flags = 0;
++	u16 data;
++
++	/* disable ALDPS before updating the PHY parameters */
++	r8153b_aldps_en(tp, false);
++
++	/* disable EEE before updating the PHY parameters */
++	r8153b_eee_en(tp, false);
++	ocp_reg_write(tp, OCP_EEE_ADV, 0);
++
++	r8153b_green_en(tp, test_bit(GREEN_ETHERNET, &tp->flags));
++
++	data = sram_read(tp, SRAM_GREEN_CFG);
++	data |= R_TUNE_EN;
++	sram_write(tp, SRAM_GREEN_CFG, data);
++	data = ocp_reg_read(tp, OCP_NCTL_CFG);
++	data |= PGA_RETURN_EN;
++	ocp_reg_write(tp, OCP_NCTL_CFG, data);
++
++	/* ADC Bias Calibration:
++	 * read efuse offset 0x7d to get a 17-bit data. Remove the dummy/fake
++	 * bit (bit3) to rebuild the real 16-bit data. Write the data to the
++	 * ADC ioffset.
++	 */
++	ocp_data = r8152_efuse_read(tp, 0x7d);
++	data = (u16)(((ocp_data & 0x1fff0) >> 1) | (ocp_data & 0x7));
++	if (data != 0xffff)
++		ocp_reg_write(tp, OCP_ADC_IOFFSET, data);
++
++	/* ups mode tx-link-pulse timing adjustment:
++	 * rg_saw_cnt = OCP reg 0xC426 Bit[13:0]
++	 * swr_cnt_1ms_ini = 16000000 / rg_saw_cnt
++	 */
++	ocp_data = ocp_reg_read(tp, 0xc426);
++	ocp_data &= 0x3fff;
++	if (ocp_data) {
++		u32 swr_cnt_1ms_ini;
++
++		swr_cnt_1ms_ini = (16000000 / ocp_data) & SAW_CNT_1MS_MASK;
++		ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_UPS_CFG);
++		ocp_data = (ocp_data & ~SAW_CNT_1MS_MASK) | swr_cnt_1ms_ini;
++		ocp_write_word(tp, MCU_TYPE_USB, USB_UPS_CFG, ocp_data);
++	}
++
++	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR);
++	ocp_data |= PFM_PWM_SWITCH;
++	ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data);
++
++	/* Advnace EEE */
++	if (!r8153_patch_request(tp, true)) {
++		data = ocp_reg_read(tp, OCP_POWER_CFG);
++		data |= EEE_CLKDIV_EN;
++		ocp_reg_write(tp, OCP_POWER_CFG, data);
++
++		data = ocp_reg_read(tp, OCP_DOWN_SPEED);
++		data |= EN_EEE_CMODE | EN_EEE_1000 | EN_10M_CLKDIV;
++		ocp_reg_write(tp, OCP_DOWN_SPEED, data);
++
++		ocp_reg_write(tp, OCP_SYSCLK_CFG, 0);
++		ocp_reg_write(tp, OCP_SYSCLK_CFG, clk_div_expo(5));
++
++		ups_flags |= UPS_FLAGS_EN_10M_CKDIV | UPS_FLAGS_250M_CKDIV |
++			     UPS_FLAGS_EN_EEE_CKDIV | UPS_FLAGS_EEE_CMOD_LV_EN |
++			     UPS_FLAGS_EEE_PLLOFF_GIGA;
++
++		r8153_patch_request(tp, false);
++	}
++
++	r8153b_ups_flags_w1w0(tp, ups_flags, 0);
++
++	r8153b_eee_en(tp, true);
++	ocp_reg_write(tp, OCP_EEE_ADV, MDIO_EEE_1000T | MDIO_EEE_100TX);
++
++	r8153b_aldps_en(tp, true);
++	r8153b_enable_fc(tp);
++	r8153_u2p3en(tp, true);
++
++	set_bit(PHY_RESET, &tp->flags);
++}
++
+ static void r8153_first_init(struct r8152 *tp)
+ {
+ 	u32 ocp_data;
+@@ -2949,9 +3430,28 @@
+
+ 	ocp_write_word(tp, MCU_TYPE_PLA, PLA_RMS, RTL8153_RMS);
+
+-	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG);
+-	ocp_data &= ~TEREDO_WAKE_MASK;
+-	ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data);
++	switch (tp->version) {
++	case RTL_VER_03:
++	case RTL_VER_04:
++	case RTL_VER_05:
++	case RTL_VER_06:
++		ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG);
++		ocp_data &= ~TEREDO_WAKE_MASK;
++		ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_CFG, ocp_data);
++		break;
++
++	case RTL_VER_08:
++	case RTL_VER_09:
++		/* Clear teredo wake event. bit[15:8] is the teredo wakeup
++		 * type. Set it to zero. bits[7:0] are the W1C bits about
++		 * the events. Set them to all 1 to clear them.
++		 */
++		ocp_write_word(tp, MCU_TYPE_PLA, PLA_TEREDO_WAKE_BASE, 0x00ff);
++		break;
++
++	default:
++		break;
++	}
+
+ 	rtl_rx_vlan_en(tp, true);
+
+@@ -2979,9 +3479,18 @@
+ 	usb_enable_lpm(tp->udev);
+ }
+
++static void rtl8153b_disable(struct r8152 *tp)
++{
++	r8153b_aldps_en(tp, false);
++	rtl_disable(tp);
++	rtl_reset_bmu(tp);
++	r8153b_aldps_en(tp, true);
++}
++
+ static int rtl8152_set_speed(struct r8152 *tp, u8 autoneg, u16 speed, u8 duplex)
+ {
+ 	u16 bmcr, anar, gbcr;
++	enum spd_duplex speed_duplex;
+ 	int ret = 0;
+
+ 	anar = r8152_mdio_read(tp, MII_ADVERTISE);
+@@ -2998,32 +3507,43 @@
+ 		if (speed == SPEED_10) {
+ 			bmcr = 0;
+ 			anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
++			speed_duplex = FORCE_10M_HALF;
+ 		} else if (speed == SPEED_100) {
+ 			bmcr = BMCR_SPEED100;
+ 			anar |= ADVERTISE_100HALF | ADVERTISE_100FULL;
++			speed_duplex = FORCE_100M_HALF;
+ 		} else if (speed == SPEED_1000 && tp->mii.supports_gmii) {
+ 			bmcr = BMCR_SPEED1000;
+ 			gbcr |= ADVERTISE_1000FULL | ADVERTISE_1000HALF;
++			speed_duplex = NWAY_1000M_FULL;
+ 		} else {
+ 			ret = -EINVAL;
+ 			goto out;
+ 		}
+
+-		if (duplex == DUPLEX_FULL)
++		if (duplex == DUPLEX_FULL) {
+ 			bmcr |= BMCR_FULLDPLX;
++			if (speed != SPEED_1000)
++				speed_duplex++;
++		}
+ 	} else {
+ 		if (speed == SPEED_10) {
+-			if (duplex == DUPLEX_FULL)
++			if (duplex == DUPLEX_FULL) {
+ 				anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+-			else
++				speed_duplex = NWAY_10M_FULL;
++			} else {
+ 				anar |= ADVERTISE_10HALF;
++				speed_duplex = NWAY_10M_HALF;
++			}
+ 		} else if (speed == SPEED_100) {
+ 			if (duplex == DUPLEX_FULL) {
+ 				anar |= ADVERTISE_10HALF | ADVERTISE_10FULL;
+ 				anar |= ADVERTISE_100HALF | ADVERTISE_100FULL;
++				speed_duplex = NWAY_100M_FULL;
+ 			} else {
+ 				anar |= ADVERTISE_10HALF;
+ 				anar |= ADVERTISE_100HALF;
++				speed_duplex = NWAY_100M_HALF;
+ 			}
+ 		} else if (speed == SPEED_1000 && tp->mii.supports_gmii) {
+ 			if (duplex == DUPLEX_FULL) {
+@@ -3035,6 +3555,7 @@
+ 				anar |= ADVERTISE_100HALF;
+ 				gbcr |= ADVERTISE_1000HALF;
+ 			}
++			speed_duplex = NWAY_1000M_FULL;
+ 		} else {
+ 			ret = -EINVAL;
+ 			goto out;
+@@ -3052,6 +3573,17 @@
+ 	r8152_mdio_write(tp, MII_ADVERTISE, anar);
+ 	r8152_mdio_write(tp, MII_BMCR, bmcr);
+
++	switch (tp->version) {
++	case RTL_VER_08:
++	case RTL_VER_09:
++		r8153b_ups_flags_w1w0(tp, ups_flags_speed(speed_duplex),
++				      UPS_FLAGS_SPEED_MASK);
++		break;
++
++	default:
++		break;
++	}
++
+ 	if (bmcr & BMCR_RESET) {
+ 		int i;
+
+@@ -3118,6 +3650,38 @@
+ 	r8153_aldps_en(tp, true);
+ }
+
++static void rtl8153b_up(struct r8152 *tp)
++{
++	if (test_bit(RTL8152_UNPLUG, &tp->flags))
++		return;
++
++	r8153b_u1u2en(tp, false);
++	r8153_u2p3en(tp, false);
++	r8153b_aldps_en(tp, false);
++
++	r8153_first_init(tp);
++	ocp_write_dword(tp, MCU_TYPE_USB, USB_RX_BUF_TH, RX_THR_B);
++
++	r8153b_aldps_en(tp, true);
++	r8153_u2p3en(tp, true);
++	r8153b_u1u2en(tp, true);
++}
++
++static void rtl8153b_down(struct r8152 *tp)
++{
++	if (test_bit(RTL8152_UNPLUG, &tp->flags)) {
++		rtl_drop_queued_tx(tp);
++		return;
++	}
++
++	r8153b_u1u2en(tp, false);
++	r8153_u2p3en(tp, false);
++	r8153b_power_cut_en(tp, false);
++	r8153b_aldps_en(tp, false);
++	r8153_enter_oob(tp);
++	r8153b_aldps_en(tp, true);
++}
++
+ static bool rtl8152_in_nway(struct r8152 *tp)
+ {
+ 	u16 nway_state;
+@@ -3503,6 +4067,66 @@
+ 	r8153_u2p3en(tp, true);
+ }
+
++static void r8153b_init(struct r8152 *tp)
++{
++	u32 ocp_data;
++	u16 data;
++	int i;
++
++	if (test_bit(RTL8152_UNPLUG, &tp->flags))
++		return;
++
++	r8153b_u1u2en(tp, false);
++
++	for (i = 0; i < 500; i++) {
++		if (ocp_read_word(tp, MCU_TYPE_PLA, PLA_BOOT_CTRL) &
++		    AUTOLOAD_DONE)
++			break;
++		msleep(20);
++	}
++
++	data = r8153_phy_status(tp, 0);
++
++	data = r8152_mdio_read(tp, MII_BMCR);
++	if (data & BMCR_PDOWN) {
++		data &= ~BMCR_PDOWN;
++		r8152_mdio_write(tp, MII_BMCR, data);
++	}
++
++	data = r8153_phy_status(tp, PHY_STAT_LAN_ON);
++
++	r8153_u2p3en(tp, false);
++
++	/* MSC timer = 0xfff * 8ms = 32760 ms */
++	ocp_write_word(tp, MCU_TYPE_USB, USB_MSC_TIMER, 0x0fff);
++
++	/* U1/U2/L1 idle timer. 500 us */
++	ocp_write_word(tp, MCU_TYPE_USB, USB_U1U2_TIMER, 500);
++
++	r8153b_power_cut_en(tp, false);
++	r8153b_ups_en(tp, false);
++	r8153b_queue_wake(tp, false);
++	rtl_runtime_suspend_enable(tp, false);
++	r8153b_u1u2en(tp, true);
++	usb_enable_lpm(tp->udev);
++
++	/* MAC clock speed down */
++	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2);
++	ocp_data |= MAC_CLK_SPDWN_EN;
++	ocp_write_word(tp, MCU_TYPE_PLA, PLA_MAC_PWR_CTRL2, ocp_data);
++
++	set_bit(GREEN_ETHERNET, &tp->flags);
++
++	/* rx aggregation */
++	ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_USB_CTRL);
++	ocp_data &= ~(RX_AGG_DISABLE | RX_ZERO_EN);
++	ocp_write_word(tp, MCU_TYPE_USB, USB_USB_CTRL, ocp_data);
++
++	rtl_tally_reset(tp);
++
++	tp->coalesce = 15000;	/* 15 us */
++}
++
+ static int rtl8152_pre_reset(struct usb_interface *intf)
+ {
+ 	struct r8152 *tp = usb_get_intfdata(intf);
+@@ -3973,6 +4597,20 @@
+ 	return 0;
+ }
+
++static int r8153b_set_eee(struct r8152 *tp, struct ethtool_eee *eee)
++{
++	u16 val = ethtool_adv_to_mmd_eee_adv_t(eee->advertised);
++
++	r8153b_eee_en(tp, eee->eee_enabled);
++
++	if (!eee->eee_enabled)
++		val = 0;
++
++	ocp_reg_write(tp, OCP_EEE_ADV, val);
++
++	return 0;
++}
++
+ static int
+ rtl_ethtool_get_eee(struct net_device *net, struct ethtool_eee *edata)
+ {
+@@ -4206,44 +4844,6 @@
+ 	.ndo_features_check	= rtl8152_features_check,
+ };
+
+-static void r8152b_get_version(struct r8152 *tp)
+-{
+-	u32	ocp_data;
+-	u16	version;
+-
+-	ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_TCR1);
+-	version = (u16)(ocp_data & VERSION_MASK);
+-
+-	switch (version) {
+-	case 0x4c00:
+-		tp->version = RTL_VER_01;
+-		break;
+-	case 0x4c10:
+-		tp->version = RTL_VER_02;
+-		break;
+-	case 0x5c00:
+-		tp->version = RTL_VER_03;
+-		tp->mii.supports_gmii = 1;
+-		break;
+-	case 0x5c10:
+-		tp->version = RTL_VER_04;
+-		tp->mii.supports_gmii = 1;
+-		break;
+-	case 0x5c20:
+-		tp->version = RTL_VER_05;
+-		tp->mii.supports_gmii = 1;
+-		break;
+-	case 0x5c30:
+-		tp->version = RTL_VER_06;
+-		tp->mii.supports_gmii = 1;
+-		break;
+-	default:
+-		netif_info(tp, probe, tp->netdev,
+-			   "Unknown version 0x%04x\n", version);
+-		break;
+-	}
+-}
+-
+ static void rtl8152_unload(struct r8152 *tp)
+ {
+ 	if (test_bit(RTL8152_UNPLUG, &tp->flags))
+@@ -4261,6 +4861,14 @@
+ 	r8153_power_cut_en(tp, false);
+ }
+
++static void rtl8153b_unload(struct r8152 *tp)
++{
++	if (test_bit(RTL8152_UNPLUG, &tp->flags))
++		return;
++
++	r8153b_power_cut_en(tp, false);
++}
++
+ static int rtl_ops_init(struct r8152 *tp)
+ {
+ 	struct rtl_ops *ops = &tp->rtl_ops;
+@@ -4299,6 +4907,21 @@
+ 		ops->autosuspend_en	= rtl8153_runtime_enable;
+ 		break;
+
++	case RTL_VER_08:
++	case RTL_VER_09:
++		ops->init		= r8153b_init;
++		ops->enable		= rtl8153_enable;
++		ops->disable		= rtl8153b_disable;
++		ops->up			= rtl8153b_up;
++		ops->down		= rtl8153b_down;
++		ops->unload		= rtl8153b_unload;
++		ops->eee_get		= r8153_get_eee;
++		ops->eee_set		= r8153b_set_eee;
++		ops->in_nway		= rtl8153_in_nway;
++		ops->hw_phy_cfg		= r8153b_hw_phy_cfg;
++		ops->autosuspend_en	= rtl8153b_runtime_enable;
++		break;
++
+ 	default:
+ 		ret = -ENODEV;
+ 		netif_err(tp, probe, tp->netdev, "Unknown Device\n");
+@@ -4308,14 +4931,77 @@
+ 	return ret;
+ }
+
++static u8 rtl_get_version(struct usb_interface *intf)
++{
++  struct usb_device *udev = interface_to_usbdev(intf);
++  u32 ocp_data = 0;
++  __le32 *tmp;
++  u8 version;
++  int ret;
++
++  tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
++  if (!tmp)
++    return 0;
++
++  ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
++                        RTL8152_REQ_GET_REGS, RTL8152_REQT_READ,
++                        PLA_TCR0, MCU_TYPE_PLA, tmp, sizeof(*tmp), 500);
++  if (ret > 0)
++    ocp_data = (__le32_to_cpu(*tmp) >> 16) & VERSION_MASK;
++
++  kfree(tmp);
++
++  switch (ocp_data) {
++    case 0x4c00:
++      version = RTL_VER_01;
++      break;
++    case 0x4c10:
++      version = RTL_VER_02;
++      break;
++    case 0x5c00:
++      version = RTL_VER_03;
++      break;
++    case 0x5c10:
++      version = RTL_VER_04;
++      break;
++    case 0x5c20:
++      version = RTL_VER_05;
++      break;
++    case 0x5c30:
++      version = RTL_VER_06;
++      break;
++    case 0x4800:
++      version = RTL_VER_07;
++      break;
++    case 0x6000:
++      version = RTL_VER_08;
++      break;
++    case 0x6010:
++      version = RTL_VER_09;
++      break;
++    default:
++      version = RTL_VER_UNKNOWN;
++      dev_info(&intf->dev, "Unknown version 0x%04x\n", ocp_data);
++      break;
++  }
++
++  dev_dbg(&intf->dev, "Detected version 0x%04x\n", version);
++
++  return version;
++}
++
+ static int rtl8152_probe(struct usb_interface *intf,
+ 			 const struct usb_device_id *id)
+ {
+ 	struct usb_device *udev = interface_to_usbdev(intf);
++        u8 version = rtl_get_version(intf);
+ 	struct r8152 *tp;
+ 	struct net_device *netdev;
+ 	int ret;
+
++        if (version == RTL_VER_UNKNOWN)
++          return -ENODEV;
++
+ 	if (udev->actconfig->desc.bConfigurationValue != 1) {
+ 		usb_driver_set_configuration(udev, 1);
+ 		return -ENODEV;
+@@ -4335,8 +5021,19 @@
+ 	tp->udev = udev;
+ 	tp->netdev = netdev;
+ 	tp->intf = intf;
++        tp->version = version;
++
++        switch (version) {
++          case RTL_VER_01:
++          case RTL_VER_02:
++          case RTL_VER_07:
++            tp->mii.supports_gmii = 0;
++            break;
++          default:
++            tp->mii.supports_gmii = 1;
++            break;
++        }
+
+-	r8152b_get_version(tp);
+ 	ret = rtl_ops_init(tp);
+ 	if (ret)
+ 		goto out;