summaryrefslogtreecommitdiffstats
path: root/target/linux/generic/files/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/files/drivers')
-rw-r--r--target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c4
-rw-r--r--target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c51
-rw-r--r--target/linux/generic/files/drivers/net/phy/ar8216.c9
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8366_smi.h14
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8366rb.c1
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8366s.c1
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8367.c48
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8367b.c562
-rw-r--r--target/linux/generic/files/drivers/net/phy/swconfig.c3
-rw-r--r--target/linux/generic/files/drivers/net/phy/swconfig_leds.c16
-rw-r--r--target/linux/generic/files/drivers/platform/mikrotik/Kconfig7
-rw-r--r--target/linux/generic/files/drivers/platform/mikrotik/Makefile1
-rw-r--r--target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c90
-rw-r--r--target/linux/generic/files/drivers/platform/mikrotik/rb_lz77.c446
-rw-r--r--target/linux/generic/files/drivers/platform/mikrotik/rb_lz77.h35
-rw-r--r--target/linux/generic/files/drivers/platform/mikrotik/routerboot.h1
16 files changed, 886 insertions, 403 deletions
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c
index 3b71597d23..a271a676e1 100644
--- a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c
+++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c
@@ -278,7 +278,7 @@ mtdsplit_fit_parse(struct mtd_info *mtd,
parts[0].name = KERNEL_PART_NAME;
parts[0].offset = fit_offset;
- parts[0].size = mtd_rounddown_to_eb(fit_size + offset_start, mtd) + mtd->erasesize;
+ parts[0].size = mtd_roundup_to_eb(fit_size + offset_start, mtd);
if (type == MTDSPLIT_PART_TYPE_UBI)
parts[1].name = UBI_PART_NAME;
@@ -327,7 +327,7 @@ mtdsplit_fit_parse(struct mtd_info *mtd,
return -ENOMEM;
parts[0].name = ROOTFS_SPLIT_NAME;
- parts[0].offset = fit_offset + mtd_rounddown_to_eb(max_size, mtd) + mtd->erasesize;
+ parts[0].offset = fit_offset + mtd_roundup_to_eb(max_size, mtd);
parts[0].size = mtd->size - parts[0].offset;
*pparts = parts;
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c
index af6822e11a..053cba6272 100644
--- a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c
+++ b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c
@@ -34,7 +34,9 @@
#define YAFFS_OBJECT_TYPE_FILE 0x1
#define YAFFS_OBJECTID_ROOT 0x1
#define YAFFS_SUM_UNUSED 0xFFFF
-#define YAFFS_NAME "kernel"
+#define YAFFS_MAX_NAME_LENGTH 127
+#define YAFFS_NAME_KERNEL "kernel"
+#define YAFFS_NAME_BOOTIMAGE "bootimage"
#define MINOR_NR_PARTS 2
@@ -46,7 +48,7 @@ struct minor_header {
int yaffs_type;
int yaffs_obj_id;
u16 yaffs_sum_unused;
- char yaffs_name[sizeof(YAFFS_NAME)];
+ char yaffs_name[YAFFS_MAX_NAME_LENGTH];
};
static int mtdsplit_parse_minor(struct mtd_info *master,
@@ -61,29 +63,44 @@ static int mtdsplit_parse_minor(struct mtd_info *master,
hdr_len = sizeof(hdr);
err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
- if (err)
+ if (err) {
+ pr_err("MiNOR mtd_read error: %d\n", err);
return err;
+ }
- if (retlen != hdr_len)
+ if (retlen != hdr_len) {
+ pr_err("MiNOR mtd_read too short\n");
return -EIO;
+ }
/* match header */
- if (hdr.yaffs_type != YAFFS_OBJECT_TYPE_FILE)
- return -EINVAL;
-
- if (hdr.yaffs_obj_id != YAFFS_OBJECTID_ROOT)
- return -EINVAL;
-
- if (hdr.yaffs_sum_unused != YAFFS_SUM_UNUSED)
- return -EINVAL;
-
- if (memcmp(hdr.yaffs_name, YAFFS_NAME, sizeof(YAFFS_NAME)))
- return -EINVAL;
+ if (hdr.yaffs_type != YAFFS_OBJECT_TYPE_FILE) {
+ pr_info("MiNOR YAFFS first type not matched\n");
+ return 0;
+ }
+
+ if (hdr.yaffs_obj_id != YAFFS_OBJECTID_ROOT) {
+ pr_info("MiNOR YAFFS first objectid not matched\n");
+ return 0;
+ }
+
+ if (hdr.yaffs_sum_unused != YAFFS_SUM_UNUSED) {
+ pr_info("MiNOR YAFFS first sum not matched\n");
+ return 0;
+ }
+
+ if ((memcmp(hdr.yaffs_name, YAFFS_NAME_KERNEL, sizeof(YAFFS_NAME_KERNEL))) &&
+ (memcmp(hdr.yaffs_name, YAFFS_NAME_BOOTIMAGE, sizeof(YAFFS_NAME_BOOTIMAGE)))) {
+ pr_info("MiNOR YAFFS first name not matched\n");
+ return 0;
+ }
err = mtd_find_rootfs_from(master, master->erasesize, master->size,
&rootfs_offset, NULL);
- if (err)
- return err;
+ if (err) {
+ pr_info("MiNOR mtd_find_rootfs_from error: %d\n", err);
+ return 0;
+ }
parts = kzalloc(MINOR_NR_PARTS * sizeof(*parts), GFP_KERNEL);
if (!parts)
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c
index 850bcefb74..84b923ffc1 100644
--- a/target/linux/generic/files/drivers/net/phy/ar8216.c
+++ b/target/linux/generic/files/drivers/net/phy/ar8216.c
@@ -35,7 +35,6 @@
#include <linux/lockdep.h>
#include <linux/ar8216_platform.h>
#include <linux/workqueue.h>
-#include <linux/version.h>
#include "ar8216.h"
@@ -2458,11 +2457,7 @@ ar8xxx_phy_config_init(struct phy_device *phydev)
/* VID fixup only needed on ar8216 */
if (chip_is_ar8216(priv)) {
dev->phy_ptr = priv;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0)
dev->priv_flags |= IFF_NO_IP_ALIGN;
-#else
- dev->extra_priv_flags |= IFF_NO_IP_ALIGN;
-#endif
dev->eth_mangle_rx = ar8216_mangle_rx;
dev->eth_mangle_tx = ar8216_mangle_tx;
}
@@ -2697,11 +2692,7 @@ ar8xxx_phy_detach(struct phy_device *phydev)
#ifdef CONFIG_ETHERNET_PACKET_MANGLE
dev->phy_ptr = NULL;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0)
dev->priv_flags &= ~IFF_NO_IP_ALIGN;
-#else
- dev->extra_priv_flags &= ~IFF_NO_IP_ALIGN;
-#endif
dev->eth_mangle_rx = NULL;
dev->eth_mangle_tx = NULL;
#endif
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
index 3fcae81fa4..2608240bb0 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
+++ b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
@@ -23,6 +23,18 @@ struct dentry;
struct inode;
struct file;
+typedef enum rtl8367b_chip_e {
+ RTL8367B_CHIP_UNKNOWN,
+ /* Family B */
+ RTL8367B_CHIP_RTL8367RB,
+ RTL8367B_CHIP_RTL8367R_VB, /* chip with exception in extif assignment */
+/* Family C */
+ RTL8367B_CHIP_RTL8367RB_VB,
+ RTL8367B_CHIP_RTL8367S,
+/* Family D */
+ RTL8367B_CHIP_RTL8367S_VB /* chip with exception in extif assignment */
+} rtl8367b_chip_t;
+
struct rtl8366_mib_counter {
unsigned base;
unsigned offset;
@@ -64,7 +76,9 @@ struct rtl8366_smi {
u8 dbg_vlan_4k_page;
#endif
u32 phy_id;
+ rtl8367b_chip_t rtl8367b_chip;
struct mii_bus *ext_mbus;
+ struct rtl8366_vlan_mc *emu_vlanmc;
};
struct rtl8366_vlan_mc {
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366rb.c b/target/linux/generic/files/drivers/net/phy/rtl8366rb.c
index 0e0116051a..0878ca9f14 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8366rb.c
+++ b/target/linux/generic/files/drivers/net/phy/rtl8366rb.c
@@ -1503,7 +1503,6 @@ MODULE_DEVICE_TABLE(of, rtl8366rb_match);
static struct platform_driver rtl8366rb_driver = {
.driver = {
.name = RTL8366RB_DRIVER_NAME,
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(rtl8366rb_match),
},
.probe = rtl8366rb_probe,
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366s.c b/target/linux/generic/files/drivers/net/phy/rtl8366s.c
index 8c746778b8..d4045fcc06 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8366s.c
+++ b/target/linux/generic/files/drivers/net/phy/rtl8366s.c
@@ -1291,7 +1291,6 @@ MODULE_DEVICE_TABLE(of, rtl8366s_match);
static struct platform_driver rtl8366s_driver = {
.driver = {
.name = RTL8366S_DRIVER_NAME,
- .owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(rtl8366s_match),
#endif
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367.c b/target/linux/generic/files/drivers/net/phy/rtl8367.c
index b14b63e036..950e9d2767 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8367.c
+++ b/target/linux/generic/files/drivers/net/phy/rtl8367.c
@@ -1077,21 +1077,37 @@ static int rtl8367_led_blinkrate_set(struct rtl8366_smi *smi, unsigned int rate)
}
#ifdef CONFIG_OF
-static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi,
const char *name)
{
struct rtl8367_extif_config *cfg;
const __be32 *prop;
int size;
int err;
+ unsigned cpu_port;
+ unsigned id = UINT_MAX;
prop = of_get_property(smi->parent->of_node, name, &size);
- if (!prop)
- return rtl8367_extif_init(smi, id, NULL);
+ if (!prop || (size != (10 * sizeof(*prop)))) {
+ dev_err(smi->parent, "%s property is not defined or invalid\n", name);
+ err = -EINVAL;
+ goto err_init;
+ }
- if (size != (9 * sizeof(*prop))) {
- dev_err(smi->parent, "%s property is invalid\n", name);
- return -EINVAL;
+ cpu_port = be32_to_cpup(prop++);
+ switch (cpu_port) {
+ case RTL8367_CPU_PORT_NUM - 1:
+ case RTL8367_CPU_PORT_NUM:
+ id = RTL8367_CPU_PORT_NUM - cpu_port;
+ if (smi->cpu_port == UINT_MAX) {
+ dev_info(smi->parent, "cpu_port:%u, assigned to extif%u\n", cpu_port, id);
+ smi->cpu_port = cpu_port;
+ }
+ break;
+ default:
+ dev_err(smi->parent, "wrong cpu_port %u in %s property\n", cpu_port, name);
+ err = -EINVAL;
+ goto err_init;
}
cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
@@ -1111,10 +1127,14 @@ static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
err = rtl8367_extif_init(smi, id, cfg);
kfree(cfg);
+err_init:
+ if (id != 0) rtl8367_extif_init(smi, 0, NULL);
+ if (id != 1) rtl8367_extif_init(smi, 1, NULL);
+
return err;
}
#else
-static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi,
const char *name)
{
return -EINVAL;
@@ -1135,11 +1155,7 @@ static int rtl8367_setup(struct rtl8366_smi *smi)
/* initialize external interfaces */
if (smi->parent->of_node) {
- err = rtl8367_extif_init_of(smi, 0, "realtek,extif0");
- if (err)
- return err;
-
- err = rtl8367_extif_init_of(smi, 1, "realtek,extif1");
+ err = rtl8367_extif_init_of(smi, "realtek,extif");
if (err)
return err;
} else {
@@ -1722,11 +1738,6 @@ static int rtl8367_detect(struct rtl8366_smi *smi)
dev_info(smi->parent, "RTL%s ver. %u chip found\n",
chip_name, rtl_ver & RTL8367_RTL_VER_MASK);
- if (of_property_present(smi->parent->of_node, "realtek,extif1"))
- smi->cpu_port = RTL8367_CPU_PORT_NUM - 1;
-
- dev_info(smi->parent, "CPU port: %u\n", smi->cpu_port);
-
return 0;
}
@@ -1764,7 +1775,7 @@ static int rtl8367_probe(struct platform_device *pdev)
smi->cmd_read = 0xb9;
smi->cmd_write = 0xb8;
smi->ops = &rtl8367_smi_ops;
- smi->cpu_port = RTL8367_CPU_PORT_NUM;
+ smi->cpu_port = UINT_MAX; /* not defined yet */
smi->num_ports = RTL8367_NUM_PORTS;
smi->num_vlan_mc = RTL8367_NUM_VLANS;
smi->mib_counters = rtl8367_mib_counters;
@@ -1823,7 +1834,6 @@ MODULE_DEVICE_TABLE(of, rtl8367_match);
static struct platform_driver rtl8367_driver = {
.driver = {
.name = RTL8367_DRIVER_NAME,
- .owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(rtl8367_match),
#endif
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367b.c b/target/linux/generic/files/drivers/net/phy/rtl8367b.c
index 04c790e924..5f885aa5be 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8367b.c
+++ b/target/linux/generic/files/drivers/net/phy/rtl8367b.c
@@ -1,5 +1,7 @@
/*
- * Platform driver for the Realtek RTL8367R-VB ethernet switches
+ * Platform driver for Realtek RTL8367B family chips, i.e. RTL8367RB and RTL8367R-VB
+ * extended with support for RTL8367C family chips, i.e. RTL8367RB-VB and RTL8367S
+ * extended with support for RTL8367D family chips, i.e. RTL8367S-VB
*
* Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
*
@@ -266,6 +268,37 @@ struct rtl8367b_initval {
#define RTL8367B_MIB_RXB_ID 0 /* IfInOctets */
#define RTL8367B_MIB_TXB_ID 28 /* IfOutOctets */
+#define RTL8367D_PORT_STATUS_REG(_p) (0x12d0 + (_p))
+
+#define RTL8367D_PORT_STATUS_SPEED1_MASK 0x3000
+#define RTL8367D_PORT_STATUS_SPEED1_SHIFT 10 /*12-2*/
+
+#define RTL8367D_REG_MAC0_FORCE_SELECT 0x12c0
+#define RTL8367D_REG_MAC0_FORCE_SELECT_EN 0x12c8
+
+#define RTL8367D_VLAN_PVID_CTRL_REG(_p) (0x0700 + (_p))
+#define RTL8367D_VLAN_PVID_CTRL_MASK 0xfff
+#define RTL8367D_VLAN_PVID_CTRL_SHIFT(_p) 0
+
+#define RTL8367D_FIDMAX 3
+#define RTL8367D_FID_MASK 3
+#define RTL8367D_TA_VLAN1_FID_SHIFT 0
+#define RTL8367D_TA_VLAN1_FID_MASK RTL8367D_FID_MASK
+
+#define RTL8367D_VID_MASK 0xfff
+#define RTL8367D_TA_VLAN_VID_MASK RTL8367D_VID_MASK
+
+#define RTL8367D_REG_EXT_TXC_DLY 0x13f9
+#define RTL8367D_EXT1_RGMII_TX_DLY_MASK 0x38
+
+#define RTL8367D_REG_TOP_CON0 0x1d70
+#define RTL8367D_MAC7_SEL_EXT1_MASK 0x2000
+#define RTL8367D_MAC4_SEL_EXT1_MASK 0x1000
+
+#define RTL8367D_REG_SDS1_MISC0 0x1d78
+#define RTL8367D_SDS1_MODE_MASK 0x1f
+#define RTL8367D_PORT_SDS_MODE_DISABLE 0x1f
+
static struct rtl8366_mib_counter
rtl8367b_mib_counters[RTL8367B_NUM_MIB_COUNTERS] = {
{0, 0, 4, "ifInOctets" },
@@ -351,220 +384,7 @@ rtl8367b_mib_counters[RTL8367B_NUM_MIB_COUNTERS] = {
return err; \
} while (0)
-static const struct rtl8367b_initval rtl8367r_vb_initvals_0[] = {
- {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x0301, 0x0026}, {0x1722, 0x0E14},
- {0x205F, 0x0002}, {0x2059, 0x1A00}, {0x205F, 0x0000}, {0x207F, 0x0002},
- {0x2077, 0x0000}, {0x2078, 0x0000}, {0x2079, 0x0000}, {0x207A, 0x0000},
- {0x207B, 0x0000}, {0x207F, 0x0000}, {0x205F, 0x0002}, {0x2053, 0x0000},
- {0x2054, 0x0000}, {0x2055, 0x0000}, {0x2056, 0x0000}, {0x2057, 0x0000},
- {0x205F, 0x0000}, {0x12A4, 0x110A}, {0x12A6, 0x150A}, {0x13F1, 0x0013},
- {0x13F4, 0x0010}, {0x13F5, 0x0000}, {0x0018, 0x0F00}, {0x0038, 0x0F00},
- {0x0058, 0x0F00}, {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x12B6, 0x0C02},
- {0x12B7, 0x030F}, {0x12B8, 0x11FF}, {0x12BC, 0x0004}, {0x1362, 0x0115},
- {0x1363, 0x0002}, {0x1363, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E},
- {0x221F, 0x0007}, {0x221E, 0x002D}, {0x2218, 0xF030}, {0x221F, 0x0007},
- {0x221E, 0x0023}, {0x2216, 0x0005}, {0x2215, 0x00B9}, {0x2219, 0x0044},
- {0x2215, 0x00BA}, {0x2219, 0x0020}, {0x2215, 0x00BB}, {0x2219, 0x00C1},
- {0x2215, 0x0148}, {0x2219, 0x0096}, {0x2215, 0x016E}, {0x2219, 0x0026},
- {0x2216, 0x0000}, {0x2216, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
- {0x221F, 0x0007}, {0x221E, 0x0020}, {0x2215, 0x0D00}, {0x221F, 0x0000},
- {0x221F, 0x0000}, {0x2217, 0x2160}, {0x221F, 0x0001}, {0x2210, 0xF25E},
- {0x221F, 0x0007}, {0x221E, 0x0042}, {0x2215, 0x0F00}, {0x2215, 0x0F00},
- {0x2216, 0x7408}, {0x2215, 0x0E00}, {0x2215, 0x0F00}, {0x2215, 0x0F01},
- {0x2216, 0x4000}, {0x2215, 0x0E01}, {0x2215, 0x0F01}, {0x2215, 0x0F02},
- {0x2216, 0x9400}, {0x2215, 0x0E02}, {0x2215, 0x0F02}, {0x2215, 0x0F03},
- {0x2216, 0x7408}, {0x2215, 0x0E03}, {0x2215, 0x0F03}, {0x2215, 0x0F04},
- {0x2216, 0x4008}, {0x2215, 0x0E04}, {0x2215, 0x0F04}, {0x2215, 0x0F05},
- {0x2216, 0x9400}, {0x2215, 0x0E05}, {0x2215, 0x0F05}, {0x2215, 0x0F06},
- {0x2216, 0x0803}, {0x2215, 0x0E06}, {0x2215, 0x0F06}, {0x2215, 0x0D00},
- {0x2215, 0x0100}, {0x221F, 0x0001}, {0x2210, 0xF05E}, {0x221F, 0x0000},
- {0x2217, 0x2100}, {0x221F, 0x0000}, {0x220D, 0x0003}, {0x220E, 0x0015},
- {0x220D, 0x4003}, {0x220E, 0x0006}, {0x221F, 0x0000}, {0x2200, 0x1340},
- {0x133F, 0x0010}, {0x12A0, 0x0058}, {0x12A1, 0x0058}, {0x133E, 0x000E},
- {0x133F, 0x0030}, {0x221F, 0x0000}, {0x2210, 0x0166}, {0x221F, 0x0000},
- {0x133E, 0x000E}, {0x133F, 0x0010}, {0x133F, 0x0030}, {0x133E, 0x000E},
- {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8B6E},
- {0x2206, 0x0000}, {0x220F, 0x0100}, {0x2205, 0x8000}, {0x2206, 0x0280},
- {0x2206, 0x28F7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
- {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
- {0x2206, 0x6602}, {0x2206, 0x80B9}, {0x2206, 0xE08B}, {0x2206, 0x8CE1},
- {0x2206, 0x8B8D}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x8E1E},
- {0x2206, 0x01A0}, {0x2206, 0x00E7}, {0x2206, 0xAEDB}, {0x2206, 0xEEE0},
- {0x2206, 0x120E}, {0x2206, 0xEEE0}, {0x2206, 0x1300}, {0x2206, 0xEEE0},
- {0x2206, 0x2001}, {0x2206, 0xEEE0}, {0x2206, 0x2166}, {0x2206, 0xEEE0},
- {0x2206, 0xC463}, {0x2206, 0xEEE0}, {0x2206, 0xC5E8}, {0x2206, 0xEEE0},
- {0x2206, 0xC699}, {0x2206, 0xEEE0}, {0x2206, 0xC7C2}, {0x2206, 0xEEE0},
- {0x2206, 0xC801}, {0x2206, 0xEEE0}, {0x2206, 0xC913}, {0x2206, 0xEEE0},
- {0x2206, 0xCA30}, {0x2206, 0xEEE0}, {0x2206, 0xCB3E}, {0x2206, 0xEEE0},
- {0x2206, 0xDCE1}, {0x2206, 0xEEE0}, {0x2206, 0xDD00}, {0x2206, 0xEEE2},
- {0x2206, 0x0001}, {0x2206, 0xEEE2}, {0x2206, 0x0100}, {0x2206, 0xEEE4},
- {0x2206, 0x8860}, {0x2206, 0xEEE4}, {0x2206, 0x8902}, {0x2206, 0xEEE4},
- {0x2206, 0x8C00}, {0x2206, 0xEEE4}, {0x2206, 0x8D30}, {0x2206, 0xEEEA},
- {0x2206, 0x1480}, {0x2206, 0xEEEA}, {0x2206, 0x1503}, {0x2206, 0xEEEA},
- {0x2206, 0xC600}, {0x2206, 0xEEEA}, {0x2206, 0xC706}, {0x2206, 0xEE85},
- {0x2206, 0xEE00}, {0x2206, 0xEE85}, {0x2206, 0xEF00}, {0x2206, 0xEE8B},
- {0x2206, 0x6750}, {0x2206, 0xEE8B}, {0x2206, 0x6632}, {0x2206, 0xEE8A},
- {0x2206, 0xD448}, {0x2206, 0xEE8A}, {0x2206, 0xD548}, {0x2206, 0xEE8A},
- {0x2206, 0xD649}, {0x2206, 0xEE8A}, {0x2206, 0xD7F8}, {0x2206, 0xEE8B},
- {0x2206, 0x85E2}, {0x2206, 0xEE8B}, {0x2206, 0x8700}, {0x2206, 0xEEFF},
- {0x2206, 0xF600}, {0x2206, 0xEEFF}, {0x2206, 0xF7FC}, {0x2206, 0x04F8},
- {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2023}, {0x2206, 0xF620},
- {0x2206, 0xE48B}, {0x2206, 0x8E02}, {0x2206, 0x2877}, {0x2206, 0x0225},
- {0x2206, 0xC702}, {0x2206, 0x26A1}, {0x2206, 0x0281}, {0x2206, 0xB302},
- {0x2206, 0x8496}, {0x2206, 0x0202}, {0x2206, 0xA102}, {0x2206, 0x27F1},
- {0x2206, 0x0228}, {0x2206, 0xF902}, {0x2206, 0x2AA0}, {0x2206, 0x0282},
- {0x2206, 0xB8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD21}, {0x2206, 0x08F6},
- {0x2206, 0x21E4}, {0x2206, 0x8B8E}, {0x2206, 0x0202}, {0x2206, 0x80E0},
- {0x2206, 0x8B8E}, {0x2206, 0xAD22}, {0x2206, 0x05F6}, {0x2206, 0x22E4},
- {0x2206, 0x8B8E}, {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2305},
- {0x2206, 0xF623}, {0x2206, 0xE48B}, {0x2206, 0x8EE0}, {0x2206, 0x8B8E},
- {0x2206, 0xAD24}, {0x2206, 0x08F6}, {0x2206, 0x24E4}, {0x2206, 0x8B8E},
- {0x2206, 0x0227}, {0x2206, 0x6AE0}, {0x2206, 0x8B8E}, {0x2206, 0xAD25},
- {0x2206, 0x05F6}, {0x2206, 0x25E4}, {0x2206, 0x8B8E}, {0x2206, 0xE08B},
- {0x2206, 0x8EAD}, {0x2206, 0x260B}, {0x2206, 0xF626}, {0x2206, 0xE48B},
- {0x2206, 0x8E02}, {0x2206, 0x830D}, {0x2206, 0x021D}, {0x2206, 0x6BE0},
- {0x2206, 0x8B8E}, {0x2206, 0xAD27}, {0x2206, 0x05F6}, {0x2206, 0x27E4},
- {0x2206, 0x8B8E}, {0x2206, 0x0281}, {0x2206, 0x4402}, {0x2206, 0x045C},
- {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B83}, {0x2206, 0xAD23},
- {0x2206, 0x30E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x2359},
- {0x2206, 0x02E0}, {0x2206, 0x85EF}, {0x2206, 0xE585}, {0x2206, 0xEFAC},
- {0x2206, 0x2907}, {0x2206, 0x1F01}, {0x2206, 0x9E51}, {0x2206, 0xAD29},
- {0x2206, 0x20E0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x06E1},
- {0x2206, 0x8B84}, {0x2206, 0xAD28}, {0x2206, 0x42E0}, {0x2206, 0x8B85},
- {0x2206, 0xAD21}, {0x2206, 0x06E1}, {0x2206, 0x8B84}, {0x2206, 0xAD29},
- {0x2206, 0x36BF}, {0x2206, 0x34BF}, {0x2206, 0x022C}, {0x2206, 0x31AE},
- {0x2206, 0x2EE0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x10E0},
- {0x2206, 0x8B84}, {0x2206, 0xF620}, {0x2206, 0xE48B}, {0x2206, 0x84EE},
- {0x2206, 0x8ADA}, {0x2206, 0x00EE}, {0x2206, 0x8ADB}, {0x2206, 0x00E0},
- {0x2206, 0x8B85}, {0x2206, 0xAD21}, {0x2206, 0x0CE0}, {0x2206, 0x8B84},
- {0x2206, 0xF621}, {0x2206, 0xE48B}, {0x2206, 0x84EE}, {0x2206, 0x8B72},
- {0x2206, 0xFFBF}, {0x2206, 0x34C2}, {0x2206, 0x022C}, {0x2206, 0x31FC},
- {0x2206, 0x04F8}, {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B85},
- {0x2206, 0xAD21}, {0x2206, 0x42E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0},
- {0x2206, 0x2358}, {0x2206, 0xC059}, {0x2206, 0x021E}, {0x2206, 0x01E1},
- {0x2206, 0x8B72}, {0x2206, 0x1F10}, {0x2206, 0x9E2F}, {0x2206, 0xE48B},
- {0x2206, 0x72AD}, {0x2206, 0x2123}, {0x2206, 0xE18B}, {0x2206, 0x84F7},
- {0x2206, 0x29E5}, {0x2206, 0x8B84}, {0x2206, 0xAC27}, {0x2206, 0x10AC},
- {0x2206, 0x2605}, {0x2206, 0x0205}, {0x2206, 0x23AE}, {0x2206, 0x1602},
- {0x2206, 0x0535}, {0x2206, 0x0282}, {0x2206, 0x30AE}, {0x2206, 0x0E02},
- {0x2206, 0x056A}, {0x2206, 0x0282}, {0x2206, 0x75AE}, {0x2206, 0x0602},
- {0x2206, 0x04DC}, {0x2206, 0x0282}, {0x2206, 0x04EF}, {0x2206, 0x96FE},
- {0x2206, 0xFC04}, {0x2206, 0xF8F9}, {0x2206, 0xE08B}, {0x2206, 0x87AD},
- {0x2206, 0x2321}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
- {0x2206, 0xAD26}, {0x2206, 0x18F6}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
- {0x2206, 0xE5EA}, {0x2206, 0x15F6}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
- {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
- {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
- {0x2206, 0xE08B}, {0x2206, 0x87AD}, {0x2206, 0x233A}, {0x2206, 0xAD22},
- {0x2206, 0x37E0}, {0x2206, 0xE020}, {0x2206, 0xE1E0}, {0x2206, 0x21AC},
- {0x2206, 0x212E}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
- {0x2206, 0xF627}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
- {0x2206, 0xE2EA}, {0x2206, 0x12E3}, {0x2206, 0xEA13}, {0x2206, 0x5A8F},
- {0x2206, 0x6A20}, {0x2206, 0xE6EA}, {0x2206, 0x12E7}, {0x2206, 0xEA13},
- {0x2206, 0xF726}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
- {0x2206, 0xF727}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
- {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B87},
- {0x2206, 0xAD23}, {0x2206, 0x38AD}, {0x2206, 0x2135}, {0x2206, 0xE0E0},
- {0x2206, 0x20E1}, {0x2206, 0xE021}, {0x2206, 0xAC21}, {0x2206, 0x2CE0},
- {0x2206, 0xEA14}, {0x2206, 0xE1EA}, {0x2206, 0x15F6}, {0x2206, 0x27E4},
- {0x2206, 0xEA14}, {0x2206, 0xE5EA}, {0x2206, 0x15E2}, {0x2206, 0xEA12},
- {0x2206, 0xE3EA}, {0x2206, 0x135A}, {0x2206, 0x8FE6}, {0x2206, 0xEA12},
- {0x2206, 0xE7EA}, {0x2206, 0x13F7}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
- {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
- {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
- {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2146},
- {0x2206, 0xE0E0}, {0x2206, 0x22E1}, {0x2206, 0xE023}, {0x2206, 0x58C0},
- {0x2206, 0x5902}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x651F},
- {0x2206, 0x109E}, {0x2206, 0x33E4}, {0x2206, 0x8B65}, {0x2206, 0xAD21},
- {0x2206, 0x22AD}, {0x2206, 0x272A}, {0x2206, 0xD400}, {0x2206, 0x01BF},
- {0x2206, 0x34F2}, {0x2206, 0x022C}, {0x2206, 0xA2BF}, {0x2206, 0x34F5},
- {0x2206, 0x022C}, {0x2206, 0xE0E0}, {0x2206, 0x8B67}, {0x2206, 0x1B10},
- {0x2206, 0xAA14}, {0x2206, 0xE18B}, {0x2206, 0x660D}, {0x2206, 0x1459},
- {0x2206, 0x0FAE}, {0x2206, 0x05E1}, {0x2206, 0x8B66}, {0x2206, 0x590F},
- {0x2206, 0xBF85}, {0x2206, 0x6102}, {0x2206, 0x2CA2}, {0x2206, 0xEF96},
- {0x2206, 0xFEFC}, {0x2206, 0x04F8}, {0x2206, 0xF9FA}, {0x2206, 0xFBEF},
- {0x2206, 0x79E2}, {0x2206, 0x8AD2}, {0x2206, 0xAC19}, {0x2206, 0x2DE0},
- {0x2206, 0xE036}, {0x2206, 0xE1E0}, {0x2206, 0x37EF}, {0x2206, 0x311F},
- {0x2206, 0x325B}, {0x2206, 0x019E}, {0x2206, 0x1F7A}, {0x2206, 0x0159},
- {0x2206, 0x019F}, {0x2206, 0x0ABF}, {0x2206, 0x348E}, {0x2206, 0x022C},
- {0x2206, 0x31F6}, {0x2206, 0x06AE}, {0x2206, 0x0FF6}, {0x2206, 0x0302},
- {0x2206, 0x0470}, {0x2206, 0xF703}, {0x2206, 0xF706}, {0x2206, 0xBF34},
- {0x2206, 0x9302}, {0x2206, 0x2C31}, {0x2206, 0xAC1A}, {0x2206, 0x25E0},
- {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x23EF}, {0x2206, 0x300D},
- {0x2206, 0x311F}, {0x2206, 0x325B}, {0x2206, 0x029E}, {0x2206, 0x157A},
- {0x2206, 0x0258}, {0x2206, 0xC4A0}, {0x2206, 0x0408}, {0x2206, 0xBF34},
- {0x2206, 0x9E02}, {0x2206, 0x2C31}, {0x2206, 0xAE06}, {0x2206, 0xBF34},
- {0x2206, 0x9C02}, {0x2206, 0x2C31}, {0x2206, 0xAC1B}, {0x2206, 0x4AE0},
- {0x2206, 0xE012}, {0x2206, 0xE1E0}, {0x2206, 0x13EF}, {0x2206, 0x300D},
- {0x2206, 0x331F}, {0x2206, 0x325B}, {0x2206, 0x1C9E}, {0x2206, 0x3AEF},
- {0x2206, 0x325B}, {0x2206, 0x1C9F}, {0x2206, 0x09BF}, {0x2206, 0x3498},
- {0x2206, 0x022C}, {0x2206, 0x3102}, {0x2206, 0x83C5}, {0x2206, 0x5A03},
- {0x2206, 0x0D03}, {0x2206, 0x581C}, {0x2206, 0x1E20}, {0x2206, 0x0207},
- {0x2206, 0xA0A0}, {0x2206, 0x000E}, {0x2206, 0x0284}, {0x2206, 0x17AD},
- {0x2206, 0x1817}, {0x2206, 0xBF34}, {0x2206, 0x9A02}, {0x2206, 0x2C31},
- {0x2206, 0xAE0F}, {0x2206, 0xBF34}, {0x2206, 0xC802}, {0x2206, 0x2C31},
- {0x2206, 0xBF34}, {0x2206, 0xC502}, {0x2206, 0x2C31}, {0x2206, 0x0284},
- {0x2206, 0x52E6}, {0x2206, 0x8AD2}, {0x2206, 0xEF97}, {0x2206, 0xFFFE},
- {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xBF34}, {0x2206, 0xDA02},
- {0x2206, 0x2CE0}, {0x2206, 0xE58A}, {0x2206, 0xD3BF}, {0x2206, 0x34D4},
- {0x2206, 0x022C}, {0x2206, 0xE00C}, {0x2206, 0x1159}, {0x2206, 0x02E0},
- {0x2206, 0x8AD3}, {0x2206, 0x1E01}, {0x2206, 0xE48A}, {0x2206, 0xD3D1},
- {0x2206, 0x00BF}, {0x2206, 0x34DA}, {0x2206, 0x022C}, {0x2206, 0xA2D1},
- {0x2206, 0x01BF}, {0x2206, 0x34D4}, {0x2206, 0x022C}, {0x2206, 0xA2BF},
- {0x2206, 0x34CB}, {0x2206, 0x022C}, {0x2206, 0xE0E5}, {0x2206, 0x8ACE},
- {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CE0}, {0x2206, 0xE58A},
- {0x2206, 0xCFBF}, {0x2206, 0x8564}, {0x2206, 0x022C}, {0x2206, 0xE0E5},
- {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6A02}, {0x2206, 0x2CE0},
- {0x2206, 0xE58A}, {0x2206, 0xD1FC}, {0x2206, 0x04F8}, {0x2206, 0xE18A},
- {0x2206, 0xD1BF}, {0x2206, 0x856A}, {0x2206, 0x022C}, {0x2206, 0xA2E1},
- {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
- {0x2206, 0xE18A}, {0x2206, 0xCFBF}, {0x2206, 0x8567}, {0x2206, 0x022C},
- {0x2206, 0xA2E1}, {0x2206, 0x8ACE}, {0x2206, 0xBF34}, {0x2206, 0xCB02},
- {0x2206, 0x2CA2}, {0x2206, 0xE18A}, {0x2206, 0xD3BF}, {0x2206, 0x34DA},
- {0x2206, 0x022C}, {0x2206, 0xA2E1}, {0x2206, 0x8AD3}, {0x2206, 0x0D11},
- {0x2206, 0xBF34}, {0x2206, 0xD402}, {0x2206, 0x2CA2}, {0x2206, 0xFC04},
- {0x2206, 0xF9A0}, {0x2206, 0x0405}, {0x2206, 0xE38A}, {0x2206, 0xD4AE},
- {0x2206, 0x13A0}, {0x2206, 0x0805}, {0x2206, 0xE38A}, {0x2206, 0xD5AE},
- {0x2206, 0x0BA0}, {0x2206, 0x0C05}, {0x2206, 0xE38A}, {0x2206, 0xD6AE},
- {0x2206, 0x03E3}, {0x2206, 0x8AD7}, {0x2206, 0xEF13}, {0x2206, 0xBF34},
- {0x2206, 0xCB02}, {0x2206, 0x2CA2}, {0x2206, 0xEF13}, {0x2206, 0x0D11},
- {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CA2}, {0x2206, 0xEF13},
- {0x2206, 0x0D14}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
- {0x2206, 0xEF13}, {0x2206, 0x0D17}, {0x2206, 0xBF85}, {0x2206, 0x6A02},
- {0x2206, 0x2CA2}, {0x2206, 0xFD04}, {0x2206, 0xF8E0}, {0x2206, 0x8B85},
- {0x2206, 0xAD27}, {0x2206, 0x2DE0}, {0x2206, 0xE036}, {0x2206, 0xE1E0},
- {0x2206, 0x37E1}, {0x2206, 0x8B73}, {0x2206, 0x1F10}, {0x2206, 0x9E20},
- {0x2206, 0xE48B}, {0x2206, 0x73AC}, {0x2206, 0x200B}, {0x2206, 0xAC21},
- {0x2206, 0x0DAC}, {0x2206, 0x250F}, {0x2206, 0xAC27}, {0x2206, 0x0EAE},
- {0x2206, 0x0F02}, {0x2206, 0x84CC}, {0x2206, 0xAE0A}, {0x2206, 0x0284},
- {0x2206, 0xD1AE}, {0x2206, 0x05AE}, {0x2206, 0x0302}, {0x2206, 0x84D8},
- {0x2206, 0xFC04}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0x0402},
- {0x2206, 0x84E5}, {0x2206, 0x0285}, {0x2206, 0x2804}, {0x2206, 0x0285},
- {0x2206, 0x4904}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0xEE8B},
- {0x2206, 0x6902}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B85},
- {0x2206, 0xAD26}, {0x2206, 0x38D0}, {0x2206, 0x0B02}, {0x2206, 0x2B4D},
- {0x2206, 0x5882}, {0x2206, 0x7882}, {0x2206, 0x9F2D}, {0x2206, 0xE08B},
- {0x2206, 0x68E1}, {0x2206, 0x8B69}, {0x2206, 0x1F10}, {0x2206, 0x9EC8},
- {0x2206, 0x10E4}, {0x2206, 0x8B68}, {0x2206, 0xE0E0}, {0x2206, 0x00E1},
- {0x2206, 0xE001}, {0x2206, 0xF727}, {0x2206, 0xE4E0}, {0x2206, 0x00E5},
- {0x2206, 0xE001}, {0x2206, 0xE2E0}, {0x2206, 0x20E3}, {0x2206, 0xE021},
- {0x2206, 0xAD30}, {0x2206, 0xF7F6}, {0x2206, 0x27E4}, {0x2206, 0xE000},
- {0x2206, 0xE5E0}, {0x2206, 0x01FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
- {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2212},
- {0x2206, 0xE0E0}, {0x2206, 0x14E1}, {0x2206, 0xE015}, {0x2206, 0xAD26},
- {0x2206, 0x9CE1}, {0x2206, 0x85E0}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
- {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x04F8},
- {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B86}, {0x2206, 0xAD22},
- {0x2206, 0x09E1}, {0x2206, 0x85E1}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
- {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x0464},
- {0x2206, 0xE48C}, {0x2206, 0xFDE4}, {0x2206, 0x80CA}, {0x2206, 0xE480},
- {0x2206, 0x66E0}, {0x2206, 0x8E70}, {0x2206, 0xE076}, {0x2205, 0xE142},
- {0x2206, 0x0701}, {0x2205, 0xE140}, {0x2206, 0x0405}, {0x220F, 0x0000},
- {0x221F, 0x0000}, {0x2200, 0x1340}, {0x133E, 0x000E}, {0x133F, 0x0010},
- {0x13EB, 0x11BB}
-};
-
-static const struct rtl8367b_initval rtl8367r_vb_initvals_1[] = {
+static const struct rtl8367b_initval rtl8367b_initvals[] = {
{0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x1305, 0xC000}, {0x121E, 0x03CA},
{0x1233, 0x0352}, {0x1234, 0x0064}, {0x1237, 0x0096}, {0x1238, 0x0078},
{0x1239, 0x0084}, {0x123A, 0x0030}, {0x205F, 0x0002}, {0x2059, 0x1A00},
@@ -730,39 +550,28 @@ static int rtl8367b_write_phy_reg(struct rtl8366_smi *smi,
static int rtl8367b_init_regs(struct rtl8366_smi *smi)
{
const struct rtl8367b_initval *initvals;
- u32 chip_num;
- u32 chip_ver;
- u32 rlvid;
int count;
- int err;
- REG_WR(smi, RTL8367B_RTL_MAGIC_ID_REG, RTL8367B_RTL_MAGIC_ID_VAL);
- REG_RD(smi, RTL8367B_CHIP_NUMBER_REG, &chip_num);
- REG_RD(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
-
- if ((chip_ver == 0x0020 || chip_ver == 0x00A0) && chip_num == 0x6367) {
+ switch (smi->rtl8367b_chip) {
+ case RTL8367B_CHIP_RTL8367RB:
+ case RTL8367B_CHIP_RTL8367R_VB:
+ initvals = rtl8367b_initvals;
+ count = ARRAY_SIZE(rtl8367b_initvals);
+ break;
+ case RTL8367B_CHIP_RTL8367RB_VB:
+ case RTL8367B_CHIP_RTL8367S:
+ case RTL8367B_CHIP_RTL8367S_VB:
initvals = rtl8367c_initvals;
count = ARRAY_SIZE(rtl8367c_initvals);
- } else {
- rlvid = (chip_ver >> RTL8367B_CHIP_VER_RLVID_SHIFT) &
- RTL8367B_CHIP_VER_RLVID_MASK;
- switch (rlvid) {
- case 0:
- initvals = rtl8367r_vb_initvals_0;
- count = ARRAY_SIZE(rtl8367r_vb_initvals_0);
- break;
- case 1:
- initvals = rtl8367r_vb_initvals_1;
- count = ARRAY_SIZE(rtl8367r_vb_initvals_1);
- break;
- default:
- dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
- return -ENODEV;
+ if ((smi->rtl8367b_chip == RTL8367B_CHIP_RTL8367S_VB) && (smi->emu_vlanmc == NULL)) {
+ smi->emu_vlanmc = kzalloc(sizeof(struct rtl8366_vlan_mc) * smi->num_vlan_mc, GFP_KERNEL);
+ dev_info(smi->parent, "alloc vlan mc emulator");
}
+ break;
+ default:
+ return -ENODEV;
}
- /* TODO: disable RLTP */
-
return rtl8367b_write_initvals(smi, initvals, count);
}
@@ -795,6 +604,7 @@ static int rtl8367b_extif_set_mode(struct rtl8366_smi *smi, int id,
enum rtl8367_extif_mode mode)
{
int err;
+ u32 data;
/* set port mode */
switch (mode) {
@@ -814,6 +624,15 @@ static int rtl8367b_extif_set_mode(struct rtl8366_smi *smi, int id,
RTL8367B_DEBUG1_DP_MASK(id),
(7 << RTL8367B_DEBUG1_DN_SHIFT(id)) |
(7 << RTL8367B_DEBUG1_DP_SHIFT(id)));
+ if ((smi->rtl8367b_chip == RTL8367B_CHIP_RTL8367S_VB) && (id == 1)) {
+ REG_RMW(smi, RTL8367D_REG_EXT_TXC_DLY, RTL8367D_EXT1_RGMII_TX_DLY_MASK, 0);
+ /* Configure RGMII/MII mux to port 7 if UTP_PORT4 is not RGMII mode */
+ REG_RD(smi, RTL8367D_REG_TOP_CON0, &data);
+ data &= RTL8367D_MAC4_SEL_EXT1_MASK;
+ if (data == 0)
+ REG_RMW(smi, RTL8367D_REG_TOP_CON0, RTL8367D_MAC7_SEL_EXT1_MASK, RTL8367D_MAC7_SEL_EXT1_MASK);
+ REG_RMW(smi, RTL8367D_REG_SDS1_MISC0, RTL8367D_SDS1_MODE_MASK, RTL8367D_PORT_SDS_MODE_DISABLE);
+ }
} else {
REG_RMW(smi, RTL8367B_CHIP_DEBUG2_REG,
RTL8367B_DEBUG2_DRI_EXT2 |
@@ -872,23 +691,31 @@ static int rtl8367b_extif_set_force(struct rtl8366_smi *smi, int id,
u32 val;
int err;
- mask = (RTL8367B_DI_FORCE_MODE |
- RTL8367B_DI_FORCE_NWAY |
- RTL8367B_DI_FORCE_TXPAUSE |
- RTL8367B_DI_FORCE_RXPAUSE |
- RTL8367B_DI_FORCE_LINK |
- RTL8367B_DI_FORCE_DUPLEX |
- RTL8367B_DI_FORCE_SPEED_MASK);
-
- val = pa->speed;
- val |= pa->force_mode ? RTL8367B_DI_FORCE_MODE : 0;
+ val = pa->speed & RTL8367B_DI_FORCE_SPEED_MASK;
val |= pa->nway ? RTL8367B_DI_FORCE_NWAY : 0;
val |= pa->txpause ? RTL8367B_DI_FORCE_TXPAUSE : 0;
val |= pa->rxpause ? RTL8367B_DI_FORCE_RXPAUSE : 0;
val |= pa->link ? RTL8367B_DI_FORCE_LINK : 0;
val |= pa->duplex ? RTL8367B_DI_FORCE_DUPLEX : 0;
- REG_RMW(smi, RTL8367B_DI_FORCE_REG(id), mask, val);
+ if (smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) { /* Family D */
+ val |= (pa->speed << RTL8367D_PORT_STATUS_SPEED1_SHIFT) & RTL8367D_PORT_STATUS_SPEED1_MASK;
+ if (smi->cpu_port != UINT_MAX) {
+ REG_WR(smi, RTL8367D_REG_MAC0_FORCE_SELECT + smi->cpu_port, val);
+ REG_WR(smi, RTL8367D_REG_MAC0_FORCE_SELECT_EN + smi->cpu_port, pa->force_mode ? 0xffff : 0x0000);
+ }
+ } else {
+ val |= pa->force_mode ? RTL8367B_DI_FORCE_MODE : 0;
+ mask = (RTL8367B_DI_FORCE_MODE |
+ RTL8367B_DI_FORCE_NWAY |
+ RTL8367B_DI_FORCE_TXPAUSE |
+ RTL8367B_DI_FORCE_RXPAUSE |
+ RTL8367B_DI_FORCE_LINK |
+ RTL8367B_DI_FORCE_DUPLEX |
+ RTL8367B_DI_FORCE_SPEED_MASK);
+
+ REG_RMW(smi, RTL8367B_DI_FORCE_REG(id), mask, val);
+ }
return 0;
}
@@ -939,21 +766,56 @@ static int rtl8367b_extif_init(struct rtl8366_smi *smi, int id,
}
#ifdef CONFIG_OF
-static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi,
const char *name)
{
struct rtl8367_extif_config *cfg;
const __be32 *prop;
int size;
int err;
+ unsigned cpu_port;
+ unsigned id = UINT_MAX;
prop = of_get_property(smi->parent->of_node, name, &size);
- if (!prop)
- return rtl8367b_extif_init(smi, id, NULL);
+ if (!prop || (size != (10 * sizeof(*prop)))) {
+ dev_err(smi->parent, "%s property is not defined or invalid\n", name);
+ err = -EINVAL;
+ goto err_init;
+ }
- if (size != (9 * sizeof(*prop))) {
- dev_err(smi->parent, "%s property is invalid\n", name);
- return -EINVAL;
+ cpu_port = be32_to_cpup(prop++);
+ switch (cpu_port) {
+ case RTL8367B_CPU_PORT_NUM:
+ case RTL8367B_CPU_PORT_NUM + 1:
+ case RTL8367B_CPU_PORT_NUM + 2:
+ if (smi->rtl8367b_chip == RTL8367B_CHIP_RTL8367R_VB) { /* for the RTL8367R-VB chip, cpu_port 5 corresponds to extif1 */
+ if (cpu_port == RTL8367B_CPU_PORT_NUM)
+ id = 1;
+ else {
+ dev_err(smi->parent, "wrong cpu_port %u in %s property\n", cpu_port, name);
+ err = -EINVAL;
+ goto err_init;
+ }
+ } else if (smi->rtl8367b_chip == RTL8367B_CHIP_RTL8367S_VB) { /* for the RTL8367S-VB chip, cpu_port 7 corresponds to extif1, cpu_port 6 corresponds to extif0 */
+ if (cpu_port != RTL8367B_CPU_PORT_NUM) {
+ id = cpu_port - RTL8367B_CPU_PORT_NUM - 1;
+ } else {
+ dev_err(smi->parent, "wrong cpu_port %u in %s property\n", cpu_port, name);
+ err = -EINVAL;
+ goto err_init;
+ }
+ } else {
+ id = cpu_port - RTL8367B_CPU_PORT_NUM;
+ }
+ if (smi->cpu_port == UINT_MAX) {
+ dev_info(smi->parent, "cpu_port:%u, assigned to extif%u\n", cpu_port, id);
+ smi->cpu_port = cpu_port;
+ }
+ break;
+ default:
+ dev_err(smi->parent, "wrong cpu_port %u in %s property\n", cpu_port, name);
+ err = -EINVAL;
+ goto err_init;
}
cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
@@ -973,10 +835,15 @@ static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
err = rtl8367b_extif_init(smi, id, cfg);
kfree(cfg);
+err_init:
+ if (id != 0) rtl8367b_extif_init(smi, 0, NULL);
+ if (id != 1) rtl8367b_extif_init(smi, 1, NULL);
+ if (id != 2) rtl8367b_extif_init(smi, 2, NULL);
+
return err;
}
#else
-static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi,
const char *name)
{
return -EINVAL;
@@ -997,15 +864,7 @@ static int rtl8367b_setup(struct rtl8366_smi *smi)
/* initialize external interfaces */
if (smi->parent->of_node) {
- err = rtl8367b_extif_init_of(smi, 0, "realtek,extif0");
- if (err)
- return err;
-
- err = rtl8367b_extif_init_of(smi, 1, "realtek,extif1");
- if (err)
- return err;
-
- err = rtl8367b_extif_init_of(smi, 2, "realtek,extif2");
+ err = rtl8367b_extif_init_of(smi, "realtek,extif");
if (err)
return err;
} else {
@@ -1115,8 +974,12 @@ static int rtl8367b_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
RTL8367B_TA_VLAN0_MEMBER_MASK;
vlan4k->untag = (data[0] >> RTL8367B_TA_VLAN0_UNTAG_SHIFT) &
RTL8367B_TA_VLAN0_UNTAG_MASK;
- vlan4k->fid = (data[1] >> RTL8367B_TA_VLAN1_FID_SHIFT) &
- RTL8367B_TA_VLAN1_FID_MASK;
+ if (smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) /* Family D */
+ vlan4k->fid = (data[1] >> RTL8367D_TA_VLAN1_FID_SHIFT) &
+ RTL8367D_TA_VLAN1_FID_MASK;
+ else
+ vlan4k->fid = (data[1] >> RTL8367B_TA_VLAN1_FID_SHIFT) &
+ RTL8367B_TA_VLAN1_FID_MASK;
return 0;
}
@@ -1131,7 +994,7 @@ static int rtl8367b_set_vlan_4k(struct rtl8366_smi *smi,
if (vlan4k->vid >= RTL8367B_NUM_VIDS ||
vlan4k->member > RTL8367B_TA_VLAN0_MEMBER_MASK ||
vlan4k->untag > RTL8367B_UNTAG_MASK ||
- vlan4k->fid > RTL8367B_FIDMAX)
+ vlan4k->fid > ((smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) ? RTL8367D_FIDMAX : RTL8367B_FIDMAX))
return -EINVAL;
memset(data, 0, sizeof(data));
@@ -1140,15 +1003,24 @@ static int rtl8367b_set_vlan_4k(struct rtl8366_smi *smi,
RTL8367B_TA_VLAN0_MEMBER_SHIFT;
data[0] |= (vlan4k->untag & RTL8367B_TA_VLAN0_UNTAG_MASK) <<
RTL8367B_TA_VLAN0_UNTAG_SHIFT;
- data[1] = (vlan4k->fid & RTL8367B_TA_VLAN1_FID_MASK) <<
- RTL8367B_TA_VLAN1_FID_SHIFT;
+
+ if (smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) /* Family D */
+ data[1] = ((vlan4k->fid & RTL8367D_TA_VLAN1_FID_MASK) <<
+ RTL8367D_TA_VLAN1_FID_SHIFT) | 12; /* ivl_svl - BIT(3), svlan_chek_ivl_svl - BIT(2) */
+ else
+ data[1] = (vlan4k->fid & RTL8367B_TA_VLAN1_FID_MASK) <<
+ RTL8367B_TA_VLAN1_FID_SHIFT;
for (i = 0; i < ARRAY_SIZE(data); i++)
REG_WR(smi, RTL8367B_TA_WRDATA_REG(i), data[i]);
/* write VID */
- REG_WR(smi, RTL8367B_TA_ADDR_REG,
- vlan4k->vid & RTL8367B_TA_VLAN_VID_MASK);
+ if (smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) /* Family D */
+ REG_WR(smi, RTL8367B_TA_ADDR_REG,
+ vlan4k->vid & RTL8367D_TA_VLAN_VID_MASK);
+ else
+ REG_WR(smi, RTL8367B_TA_ADDR_REG,
+ vlan4k->vid & RTL8367B_TA_VLAN_VID_MASK);
/* write table access control word */
REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_WRITE);
@@ -1168,6 +1040,14 @@ static int rtl8367b_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
if (index >= RTL8367B_NUM_VLANS)
return -EINVAL;
+ if (smi->emu_vlanmc) { /* use vlan mc emulation */
+ vlanmc->vid = smi->emu_vlanmc[index].vid;
+ vlanmc->member = smi->emu_vlanmc[index].member;
+ vlanmc->fid = smi->emu_vlanmc[index].fid;
+ vlanmc->untag = smi->emu_vlanmc[index].untag;
+ return 0;
+ }
+
for (i = 0; i < ARRAY_SIZE(data); i++)
REG_RD(smi, RTL8367B_VLAN_MC_BASE(index) + i, &data[i]);
@@ -1193,9 +1073,17 @@ static int rtl8367b_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
vlanmc->priority > RTL8367B_PRIORITYMAX ||
vlanmc->member > RTL8367B_VLAN_MC0_MEMBER_MASK ||
vlanmc->untag > RTL8367B_UNTAG_MASK ||
- vlanmc->fid > RTL8367B_FIDMAX)
+ vlanmc->fid > ((smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) ? RTL8367D_FIDMAX : RTL8367B_FIDMAX))
return -EINVAL;
+ if (smi->emu_vlanmc) { /* use vlanmc emulation */
+ smi->emu_vlanmc[index].vid = vlanmc->vid;
+ smi->emu_vlanmc[index].member = vlanmc->member;
+ smi->emu_vlanmc[index].fid = vlanmc->fid;
+ smi->emu_vlanmc[index].untag = vlanmc->untag;
+ return 0;
+ }
+
data[0] = (vlanmc->member & RTL8367B_VLAN_MC0_MEMBER_MASK) <<
RTL8367B_VLAN_MC0_MEMBER_SHIFT;
data[1] = (vlanmc->fid & RTL8367B_VLAN_MC1_FID_MASK) <<
@@ -1218,10 +1106,41 @@ static int rtl8367b_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
if (port >= RTL8367B_NUM_PORTS)
return -EINVAL;
- REG_RD(smi, RTL8367B_VLAN_PVID_CTRL_REG(port), &data);
+ if (smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) { /* Family D */
+ int i;
+ struct rtl8366_vlan_mc vlanmc;
- *val = (data >> RTL8367B_VLAN_PVID_CTRL_SHIFT(port)) &
- RTL8367B_VLAN_PVID_CTRL_MASK;
+ err = rtl8366_smi_read_reg(smi, RTL8367D_VLAN_PVID_CTRL_REG(port), &data);
+
+ if (err) {
+ dev_err(smi->parent, "read pvid register 0x%04x fail", RTL8367D_VLAN_PVID_CTRL_REG(port));
+ return err;
+ }
+
+ data &= RTL8367D_VLAN_PVID_CTRL_MASK;
+ for (i = 0; i < smi->num_vlan_mc; i++) {
+ err = rtl8367b_get_vlan_mc(smi, i, &vlanmc);
+
+ if (err) {
+ dev_err(smi->parent, "get vlan mc index %d fail", i);
+ return err;
+ }
+
+ if (data == vlanmc.vid) break;
+ }
+
+ if (i < smi->num_vlan_mc) {
+ *val = i;
+ } else {
+ dev_err(smi->parent, "vlan mc index for pvid %d not found", data);
+ return -EINVAL;
+ }
+ } else {
+ REG_RD(smi, RTL8367B_VLAN_PVID_CTRL_REG(port), &data);
+
+ *val = (data >> RTL8367B_VLAN_PVID_CTRL_SHIFT(port)) &
+ RTL8367B_VLAN_PVID_CTRL_MASK;
+ }
return 0;
}
@@ -1231,7 +1150,28 @@ static int rtl8367b_set_mc_index(struct rtl8366_smi *smi, int port, int index)
if (port >= RTL8367B_NUM_PORTS || index >= RTL8367B_NUM_VLANS)
return -EINVAL;
- return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_PVID_CTRL_REG(port),
+ if (smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) { /* Family D */
+ int pvid, err;
+ struct rtl8366_vlan_mc vlanmc;
+
+ err = rtl8367b_get_vlan_mc(smi, index, &vlanmc);
+
+ if (err) {
+ dev_err(smi->parent, "get vlan mc index %d fail", index);
+ return err;
+ }
+
+ pvid = vlanmc.vid & RTL8367D_VLAN_PVID_CTRL_MASK;
+ err = rtl8366_smi_write_reg(smi, RTL8367D_VLAN_PVID_CTRL_REG(port), pvid);
+
+ if (err) {
+ dev_err(smi->parent, "set port %d pvid %d fail", port, pvid);
+ return err;
+ }
+
+ return 0;
+ } else
+ return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_PVID_CTRL_REG(port),
RTL8367B_VLAN_PVID_CTRL_MASK <<
RTL8367B_VLAN_PVID_CTRL_SHIFT(port),
(index & RTL8367B_VLAN_PVID_CTRL_MASK) <<
@@ -1294,7 +1234,10 @@ static int rtl8367b_sw_get_port_link(struct switch_dev *dev,
if (port >= RTL8367B_NUM_PORTS)
return -EINVAL;
- rtl8366_smi_read_reg(smi, RTL8367B_PORT_STATUS_REG(port), &data);
+ if (smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) /* Family D */
+ rtl8366_smi_read_reg(smi, RTL8367D_PORT_STATUS_REG(port), &data);
+ else
+ rtl8366_smi_read_reg(smi, RTL8367B_PORT_STATUS_REG(port), &data);
link->link = !!(data & RTL8367B_PORT_STATUS_LINK);
if (!link->link)
@@ -1305,15 +1248,18 @@ static int rtl8367b_sw_get_port_link(struct switch_dev *dev,
link->tx_flow = !!(data & RTL8367B_PORT_STATUS_TXPAUSE);
link->aneg = !!(data & RTL8367B_PORT_STATUS_NWAY);
- speed = (data & RTL8367B_PORT_STATUS_SPEED_MASK);
+ if (smi->rtl8367b_chip >= RTL8367B_CHIP_RTL8367S_VB) /* Family D */
+ speed = (data & RTL8367B_PORT_STATUS_SPEED_MASK) | ((data & RTL8367D_PORT_STATUS_SPEED1_MASK) >> RTL8367D_PORT_STATUS_SPEED1_SHIFT);
+ else
+ speed = (data & RTL8367B_PORT_STATUS_SPEED_MASK);
switch (speed) {
- case 0:
+ case RTL8367B_PORT_STATUS_SPEED_10:
link->speed = SWITCH_PORT_SPEED_10;
break;
- case 1:
+ case RTL8367B_PORT_STATUS_SPEED_100:
link->speed = SWITCH_PORT_SPEED_100;
break;
- case 2:
+ case RTL8367B_PORT_STATUS_SPEED_1000:
link->speed = SWITCH_PORT_SPEED_1000;
break;
default:
@@ -1530,10 +1476,11 @@ static int rtl8367b_detect(struct rtl8366_smi *smi)
const char *chip_name = NULL;
u32 chip_num;
u32 chip_ver;
- u32 chip_mode;
int ret;
- /* TODO: improve chip detection */
+ smi->emu_vlanmc = NULL;
+ smi->rtl8367b_chip = RTL8367B_CHIP_UNKNOWN;
+
rtl8366_smi_write_reg(smi, RTL8367B_RTL_MAGIC_ID_REG,
RTL8367B_RTL_MAGIC_ID_VAL);
@@ -1551,44 +1498,42 @@ static int rtl8367b_detect(struct rtl8366_smi *smi)
return ret;
}
- ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_MODE_REG, &chip_mode);
- if (ret) {
- dev_err(smi->parent, "unable to read %s register\n",
- "chip mode");
- return ret;
- }
-
switch (chip_ver) {
+ case 0x0010:
+ if (chip_num == 0x6642) {
+ chip_name = "8367S-VB";
+ smi->rtl8367b_chip = RTL8367B_CHIP_RTL8367S_VB;
+ }
+ break;
case 0x0020:
- if (chip_num == 0x6367)
+ if (chip_num == 0x6367) {
chip_name = "8367RB-VB";
+ smi->rtl8367b_chip = RTL8367B_CHIP_RTL8367RB_VB;
+ }
break;
case 0x00A0:
- if (chip_num == 0x6367)
+ if (chip_num == 0x6367) {
chip_name = "8367S";
+ smi->rtl8367b_chip = RTL8367B_CHIP_RTL8367S;
+ }
break;
case 0x1000:
chip_name = "8367RB";
+ smi->rtl8367b_chip = RTL8367B_CHIP_RTL8367RB;
break;
case 0x1010:
chip_name = "8367R-VB";
+ smi->rtl8367b_chip = RTL8367B_CHIP_RTL8367R_VB;
}
if (!chip_name) {
dev_err(smi->parent,
- "unknown chip num:%04x ver:%04x, mode:%04x\n",
- chip_num, chip_ver, chip_mode);
+ "unknown chip (num:%04x ver:%04x)\n",
+ chip_num, chip_ver);
return -ENODEV;
}
- dev_info(smi->parent, "RTL%s chip found\n", chip_name);
-
- if (of_property_present(smi->parent->of_node, "realtek,extif2"))
- smi->cpu_port = RTL8367B_CPU_PORT_NUM + 2;
- else if (of_property_present(smi->parent->of_node, "realtek,extif1") && (chip_ver != 0x1010)) /* for the RTL8367R-VB chip, extif1 corresponds to cpu_port 5 */
- smi->cpu_port = RTL8367B_CPU_PORT_NUM + 1;
-
- dev_info(smi->parent, "CPU port: %u\n", smi->cpu_port);
+ dev_info(smi->parent, "RTL%s chip found (num:%04x ver:%04x)\n", chip_name, chip_num, chip_ver);
return 0;
}
@@ -1628,7 +1573,7 @@ static int rtl8367b_probe(struct platform_device *pdev)
smi->cmd_write = 0xb8;
smi->ops = &rtl8367b_smi_ops;
smi->num_ports = RTL8367B_NUM_PORTS;
- smi->cpu_port = RTL8367B_CPU_PORT_NUM;
+ smi->cpu_port = UINT_MAX; /* not defined yet */
smi->num_vlan_mc = RTL8367B_NUM_VLANS;
smi->mib_counters = rtl8367b_mib_counters;
smi->num_mib_counters = ARRAY_SIZE(rtl8367b_mib_counters);
@@ -1649,6 +1594,8 @@ static int rtl8367b_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
rtl8366_smi_cleanup(smi);
err_free_smi:
+ if (smi->emu_vlanmc)
+ kfree(smi->emu_vlanmc);
kfree(smi);
return err;
}
@@ -1686,7 +1633,6 @@ MODULE_DEVICE_TABLE(of, rtl8367b_match);
static struct platform_driver rtl8367b_driver = {
.driver = {
.name = RTL8367B_DRIVER_NAME,
- .owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = of_match_ptr(rtl8367b_match),
#endif
diff --git a/target/linux/generic/files/drivers/net/phy/swconfig.c b/target/linux/generic/files/drivers/net/phy/swconfig.c
index 5fa2b147c6..10dc8d0607 100644
--- a/target/linux/generic/files/drivers/net/phy/swconfig.c
+++ b/target/linux/generic/files/drivers/net/phy/swconfig.c
@@ -24,7 +24,6 @@
#include <linux/skbuff.h>
#include <linux/switch.h>
#include <linux/of.h>
-#include <linux/version.h>
#include <uapi/linux/mii.h>
#define SWCONFIG_DEVNAME "switch%d"
@@ -1054,9 +1053,7 @@ static struct genl_family switch_fam = {
.module = THIS_MODULE,
.ops = swconfig_ops,
.n_ops = ARRAY_SIZE(swconfig_ops),
-#if LINUX_VERSION_CODE > KERNEL_VERSION(6,0,0)
.resv_start_op = SWITCH_CMD_SET_VLAN + 1,
-#endif
};
#ifdef CONFIG_OF
diff --git a/target/linux/generic/files/drivers/net/phy/swconfig_leds.c b/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
index 1d309c046c..1fcd4432b5 100644
--- a/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
+++ b/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
@@ -85,11 +85,7 @@ swconfig_trig_update_port_mask(struct led_trigger *trigger)
sw_trig = (void *) trigger;
port_mask = 0;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0)
spin_lock(&trigger->leddev_list_lock);
-#else
- read_lock(&trigger->leddev_list_lock);
-#endif
list_for_each(entry, &trigger->led_cdevs) {
struct led_classdev *led_cdev;
struct swconfig_trig_data *trig_data;
@@ -102,11 +98,7 @@ swconfig_trig_update_port_mask(struct led_trigger *trigger)
read_unlock(&trig_data->lock);
}
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0)
spin_unlock(&trigger->leddev_list_lock);
-#else
- read_unlock(&trigger->leddev_list_lock);
-#endif
sw_trig->port_mask = port_mask;
@@ -426,22 +418,14 @@ swconfig_trig_update_leds(struct switch_led_trigger *sw_trig)
struct led_trigger *trigger;
trigger = &sw_trig->trig;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0)
spin_lock(&trigger->leddev_list_lock);
-#else
- read_lock(&trigger->leddev_list_lock);
-#endif
list_for_each(entry, &trigger->led_cdevs) {
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
swconfig_trig_led_event(sw_trig, led_cdev);
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,16,0)
spin_unlock(&trigger->leddev_list_lock);
-#else
- read_unlock(&trigger->leddev_list_lock);
-#endif
}
static void
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/Kconfig b/target/linux/generic/files/drivers/platform/mikrotik/Kconfig
index 32ef8f29de..85fe7b2050 100644
--- a/target/linux/generic/files/drivers/platform/mikrotik/Kconfig
+++ b/target/linux/generic/files/drivers/platform/mikrotik/Kconfig
@@ -21,4 +21,11 @@ config NVMEM_LAYOUT_MIKROTIK
help
This driver exposes MikroTik hard_config via NVMEM layout.
+config MIKROTIK_WLAN_DECOMPRESS_LZ77
+ tristate "Mikrotik factory Wi-Fi caldata LZ77 decompression support"
+ depends on MIKROTIK_RB_SYSFS
+ help
+ Allow Mikrotik LZ77 factory flashed Wi-Fi calibration data to be
+ decompressed
+
endif # MIKROTIK
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/Makefile b/target/linux/generic/files/drivers/platform/mikrotik/Makefile
index 164b23b701..9ffb355c1e 100644
--- a/target/linux/generic/files/drivers/platform/mikrotik/Makefile
+++ b/target/linux/generic/files/drivers/platform/mikrotik/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_MIKROTIK_RB_SYSFS) += routerboot.o rb_hardconfig.o rb_softconfig.o
obj-$(CONFIG_NVMEM_LAYOUT_MIKROTIK) += rb_nvmem.o
+obj-$(CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77) += rb_lz77.o
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c
index 78e39a7f94..4c1edad081 100644
--- a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c
+++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c
@@ -39,8 +39,9 @@
#include "rb_hardconfig.h"
#include "routerboot.h"
+#include "rb_lz77.h"
-#define RB_HARDCONFIG_VER "0.07"
+#define RB_HARDCONFIG_VER "0.08"
#define RB_HC_PR_PFX "[rb_hardconfig] "
/* Bit definitions for hardware options */
@@ -465,23 +466,24 @@ fail:
/*
* If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_LZOR, then past
* that magic number is a payload that must be appended to the hc_lzor_prefix,
- * the resulting blob is LZO-compressed. In the LZO decompression result,
+ * the resulting blob is LZO-compressed.
+ * If payload starts with RB_MAGIC_LZ77, a separate (bit level LZ77)
+ * decompression function needs to be used. In the decompressed result,
* the RB_MAGIC_ERD magic number (aligned) must be located. Following that
* magic, there is one or more routerboot tag node(s) locating the RLE-encoded
* calibration data payload.
*/
-static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t inlen,
- void *outbuf, size_t *outlen)
+static int hc_wlan_data_unpack_lzor_lz77(const u16 tag_id, const u8 *inbuf, size_t inlen,
+ void *outbuf, size_t *outlen, u32 magic)
{
u16 rle_ofs, rle_len;
const u32 *needle;
u8 *tempbuf;
size_t templen, lzo_len;
int ret;
-
- lzo_len = inlen + sizeof(hc_lzor_prefix);
- if (lzo_len > *outlen)
- return -EFBIG;
+ const char lzor[] = "LZOR";
+ const char lz77[] = "LZ77";
+ const char *lz_type;
/* Temporary buffer same size as the outbuf */
templen = *outlen;
@@ -489,23 +491,50 @@ static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t in
if (!tempbuf)
return -ENOMEM;
- /* Concatenate into the outbuf */
- memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
- memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
+ lzo_len = inlen;
+ if (magic == RB_MAGIC_LZOR)
+ lzo_len += sizeof(hc_lzor_prefix);
+ if (lzo_len > *outlen)
+ return -EFBIG;
- /* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
- ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
- if (ret) {
- if (LZO_E_INPUT_NOT_CONSUMED == ret) {
- /*
- * The tag length is always aligned thus the LZO payload may be padded,
- * which can trigger a spurious error which we ignore here.
- */
- pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
- } else {
- pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
+ switch (magic) {
+ case RB_MAGIC_LZOR:
+ lz_type = lzor;
+
+ /* Concatenate into the outbuf */
+ memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
+ memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
+
+ /* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
+ ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
+ if (ret) {
+ if (LZO_E_INPUT_NOT_CONSUMED == ret) {
+ /*
+ * The tag length is always aligned thus the LZO payload may be padded,
+ * which can trigger a spurious error which we ignore here.
+ */
+ pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
+ } else {
+ pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
+ goto fail;
+ }
+ }
+ break;
+ case RB_MAGIC_LZ77:
+ lz_type = lz77;
+ /* LZO-decompress lzo_len bytes of inbuf into the tempbuf */
+ ret = rb_lz77_decompress(inbuf, inlen, tempbuf, &templen);
+ if (ret) {
+ pr_err(RB_HC_PR_PFX "LZ77: LZ77 decompress error %d\n", ret);
goto fail;
}
+
+ pr_debug(RB_HC_PR_PFX "LZ77: decompressed from %zu to %zu\n",
+ inlen, templen);
+ break;
+ default:
+ return -EINVAL;
+ break;
}
/*
@@ -516,7 +545,7 @@ static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t in
needle = (const u32 *)tempbuf;
while (RB_MAGIC_ERD != *needle++) {
if ((u8 *)needle >= tempbuf+templen) {
- pr_debug(RB_HC_PR_PFX "LZOR: ERD magic not found\n");
+ pr_warn(RB_HC_PR_PFX "%s: ERD magic not found. Decompressed first word: 0x%08x\n", lz_type, *(u32 *)tempbuf);
ret = -ENODATA;
goto fail;
}
@@ -526,12 +555,12 @@ static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t in
/* Past magic. Look for tag node */
ret = routerboot_tag_find((u8 *)needle, templen, tag_id, &rle_ofs, &rle_len);
if (ret) {
- pr_debug(RB_HC_PR_PFX "LZOR: no RLE data for id 0x%04x\n", tag_id);
+ pr_debug(RB_HC_PR_PFX "%s: no RLE data for id 0x%04x\n", lz_type, tag_id);
goto fail;
}
if (rle_len > templen) {
- pr_debug(RB_HC_PR_PFX "LZOR: Invalid RLE data length\n");
+ pr_debug(RB_HC_PR_PFX "%s: Invalid RLE data length\n", lz_type);
ret = -EINVAL;
goto fail;
}
@@ -539,7 +568,7 @@ static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t in
/* RLE-decode tempbuf from needle back into the outbuf */
ret = routerboot_rle_decode((u8 *)needle+rle_ofs, rle_len, outbuf, outlen);
if (ret)
- pr_debug(RB_HC_PR_PFX "LZOR: RLE decoding error (%d)\n", ret);
+ pr_debug(RB_HC_PR_PFX "%s: RLE decoding error (%d)\n", lz_type, ret);
fail:
kfree(tempbuf);
@@ -562,11 +591,18 @@ static int hc_wlan_data_unpack(const u16 tag_id, const size_t tofs, size_t tlen,
ret = -ENODATA;
switch (magic) {
+ case RB_MAGIC_LZ77:
+ /* no known instances of lz77 without 8001/8201 data, skip SOLO */
+ if (tag_id == RB_WLAN_ERD_ID_SOLO) {
+ pr_debug(RB_HC_PR_PFX "skipped LZ77 decompress in search for SOLO tag\n");
+ break;
+ }
+ fallthrough;
case RB_MAGIC_LZOR:
/* Skip magic */
lbuf += sizeof(magic);
tlen -= sizeof(magic);
- ret = hc_wlan_data_unpack_lzor(tag_id, lbuf, tlen, outbuf, outlen);
+ ret = hc_wlan_data_unpack_lzor_lz77(tag_id, lbuf, tlen, outbuf, outlen, magic);
break;
case RB_MAGIC_ERD:
/* Skip magic */
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_lz77.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_lz77.c
new file mode 100644
index 0000000000..d443adb128
--- /dev/null
+++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_lz77.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 John Thomson
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/minmax.h>
+
+#include "rb_lz77.h"
+
+#define MIKRO_LZ77 "[rb lz77] "
+
+/*
+ * The maximum number of bits used in a counter.
+ * For the look behind window, long instruction match offsets
+ * up to 6449 have been seen in provided compressed caldata blobs
+ * (that would need 21 counter bits: 4 to 12 + 11 to 0).
+ * conservative value here: 27 provides offset up to 0x8000 bytes
+ * uses a u8 in this code
+ */
+#define MIKRO_LZ77_MAX_COUNT_BIT_LEN 27
+
+enum rb_lz77_instruction {
+ INSTR_ERROR = -1,
+ INSTR_LITERAL_BYTE = 0,
+ /* a (non aligned) byte follows this instruction,
+ * which is directly copied into output
+ */
+ INSTR_PREVIOUS_OFFSET = 1,
+ /* this group is a match, with a bytes length defined by
+ * following counter bits, starting at bitshift 0,
+ * less the built-in count of 1
+ * using the previous offset as source
+ */
+ INSTR_LONG = 2
+ /* this group has two counters,
+ * the first counter starts at bitshift 4,
+ * if this counter == 0, this is a non-matching group
+ * the second counter (bytes length) starts at bitshift 4,
+ * less the built-in count of 11+1.
+ * The final match group has this count 0,
+ * and following bits which pad to byte-alignment.
+ *
+ * if this counter > 0, this is a matching group
+ * this first count is the match offset (in bytes)
+ * the second count is the match length (in bytes),
+ * less the built-in count of 2
+ * these groups can source bytes that are part of this group
+ */
+};
+
+struct rb_lz77_instr_opcodes {
+ /* group instruction */
+ enum rb_lz77_instruction instruction;
+ /* if >0, a match group,
+ * which starts at byte output_position - 1*offset
+ */
+ size_t offset;
+ /* how long the match group is,
+ * or how long the (following counter) non-match group is
+ */
+ size_t length;
+ /* how many bits were used for this instruction + op code(s) */
+ size_t bits_used;
+ /* input char */
+ u8 *in;
+ /* offset where this instruction started */
+ size_t in_pos;
+};
+
+/**
+ * rb_lz77_get_bit
+ *
+ * @in: compressed data ptr
+ * @in_offset_bit: bit offset to extract
+ *
+ * convert the bit offset to byte offset,
+ * shift to modulo of bits per bytes, so that wanted bit is lsb
+ * and to extract only that bit.
+ * Caller is responsible for ensuring that in_offset_bit/8
+ * does not exceed input length
+ */
+static inline u8 rb_lz77_get_bit(const u8 *in, const size_t in_offset_bit)
+{
+ return ((in[in_offset_bit / BITS_PER_BYTE] >>
+ (in_offset_bit % BITS_PER_BYTE)) &
+ 1);
+}
+
+/**
+ * rb_lz77_get_byte
+ *
+ * @in: compressed data
+ * @in_offset_bit: bit offset to extract byte
+ */
+static inline u8 rb_lz77_get_byte(const u8 *in, const size_t in_offset_bit)
+{
+ u8 buf = 0;
+ int i;
+
+ /* built a reversed byte from (likely) unaligned bits */
+ for (i = 0; i <= 7; ++i)
+ buf += rb_lz77_get_bit(in, in_offset_bit + i) << (7 - i);
+ return buf;
+}
+
+/**
+ * rb_lz77_decode_count - decode bits at given offset as a count
+ *
+ * @in: compressed data
+ * @in_len: length of compressed data
+ * @in_offset_bit: bit offset where count starts
+ * @shift: left shift operand value of first count bit
+ * @count: initial count
+ * @bits_used: how many bits were consumed by this count
+ * @max_bits: maximum bit count for this counter
+ *
+ * Returns the decoded count
+ */
+static int rb_lz77_decode_count(const u8 *in, const size_t in_len,
+ const size_t in_offset_bit, u8 shift,
+ size_t count, u8 *bits_used, const u8 max_bits)
+{
+ size_t pos = in_offset_bit;
+ const size_t max_pos = min(pos + max_bits, in_len * BITS_PER_BYTE);
+ bool up = true;
+
+ *bits_used = 0;
+ pr_debug(MIKRO_LZ77
+ "decode_count inbit: %zu, start shift:%u, initial count:%zu\n",
+ in_offset_bit, shift, count);
+
+ while (true) {
+ /* check the input offset bit does not overflow the minimum of
+ * a reasonable length for this encoded count, and
+ * the end of the input */
+ if (unlikely(pos >= max_pos)) {
+ pr_err(MIKRO_LZ77
+ "max bit index reached before count completed\n");
+ return -EFBIG;
+ }
+
+ /* if the bit value at offset is set */
+ if (rb_lz77_get_bit(in, pos))
+ count += (1 << shift);
+
+ /* shift increases until we find an unsed bit */
+ else if (up)
+ up = false;
+
+ if (up)
+ ++shift;
+ else {
+ if (!shift) {
+ *bits_used = pos - in_offset_bit + 1;
+ return count;
+ }
+ --shift;
+ }
+
+ ++pos;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * rb_lz77_decode_instruction
+ *
+ * @in: compressed data
+ * @in_offset_bit: bit offset where instruction starts
+ * @bits_used: how many bits were consumed by this count
+ *
+ * Returns the decoded instruction
+ */
+static enum rb_lz77_instruction
+rb_lz77_decode_instruction(const u8 *in, size_t in_offset_bit, u8 *bits_used)
+{
+ if (rb_lz77_get_bit(in, in_offset_bit)) {
+ *bits_used = 2;
+ if (rb_lz77_get_bit(in, ++in_offset_bit))
+ return INSTR_LONG;
+ else
+ return INSTR_PREVIOUS_OFFSET;
+ } else {
+ *bits_used = 1;
+ return INSTR_LITERAL_BYTE;
+ }
+ return INSTR_ERROR;
+}
+
+/**
+ * rb_lz77_decode_instruction_operators
+ *
+ * @in: compressed data
+ * @in_len: length of compressed data
+ * @in_offset_bit: bit offset where instruction starts
+ * @previous_offset: last used match offset
+ * @opcode: struct to hold instruction & operators
+ *
+ * Returns error code
+ */
+static int rb_lz77_decode_instruction_operators(
+ const u8 *in, const size_t in_len, const size_t in_offset_bit,
+ const size_t previous_offset, struct rb_lz77_instr_opcodes *opcode)
+{
+ enum rb_lz77_instruction instruction;
+ u8 bit_count = 0;
+ u8 bits_used = 0;
+ int offset = 0;
+ int length = 0;
+
+ instruction = rb_lz77_decode_instruction(in, in_offset_bit, &bit_count);
+
+ /* skip bits used by instruction */
+ bits_used += bit_count;
+
+ switch (instruction) {
+ case INSTR_LITERAL_BYTE:
+ /* non-matching char */
+ offset = 0;
+ length = 1;
+ break;
+
+ case INSTR_PREVIOUS_OFFSET:
+ /* matching group uses previous offset */
+ offset = previous_offset;
+
+ length = rb_lz77_decode_count(in, in_len,
+ in_offset_bit + bits_used, 0, 1,
+ &bit_count,
+ MIKRO_LZ77_MAX_COUNT_BIT_LEN);
+ if (unlikely(length < 0))
+ return length;
+ /* skip bits used by count */
+ bits_used += bit_count;
+ break;
+
+ case INSTR_LONG:
+ offset = rb_lz77_decode_count(in, in_len,
+ in_offset_bit + bits_used, 4, 0,
+ &bit_count,
+ MIKRO_LZ77_MAX_COUNT_BIT_LEN);
+ if (unlikely(offset < 0))
+ return offset;
+
+ /* skip bits used by offset count */
+ bits_used += bit_count;
+
+ if (offset == 0) {
+ /* non-matching long group */
+ length = rb_lz77_decode_count(
+ in, in_len, in_offset_bit + bits_used, 4, 12,
+ &bit_count, MIKRO_LZ77_MAX_COUNT_BIT_LEN);
+ if (unlikely(length < 0))
+ return length;
+ /* skip bits used by length count */
+ bits_used += bit_count;
+ } else {
+ /* matching group */
+ length = rb_lz77_decode_count(
+ in, in_len, in_offset_bit + bits_used, 0, 2,
+ &bit_count, MIKRO_LZ77_MAX_COUNT_BIT_LEN);
+ if (unlikely(length < 0))
+ return length;
+ /* skip bits used by length count */
+ bits_used += bit_count;
+ }
+
+ break;
+
+ case INSTR_ERROR:
+ return -EINVAL;
+ }
+
+ opcode->instruction = instruction;
+ opcode->offset = offset;
+ opcode->length = length;
+ opcode->bits_used = bits_used;
+ opcode->in = (u8 *)in;
+ opcode->in_pos = in_offset_bit;
+ return 0;
+}
+
+/**
+ * rb_lz77_decompress
+ *
+ * @in: compressed data ptr
+ * @in_len: length of compressed data
+ * @out: buffer ptr to decompress into
+ * @out_len: length of decompressed buffer in input,
+ * length of decompressed data in success
+ *
+ * Returns 0 on success, or negative error
+ */
+int rb_lz77_decompress(const u8 *in, const size_t in_len, u8 *out,
+ size_t *out_len)
+{
+ u8 *output_ptr;
+ size_t input_bit = 0;
+ const u8 *output_end = out + *out_len;
+ struct rb_lz77_instr_opcodes *opcode;
+ size_t match_offset = 0;
+ int rc = 0;
+ size_t match_length, partial_count, i;
+
+ output_ptr = out;
+
+ if (unlikely((in_len * BITS_PER_BYTE) > SIZE_MAX)) {
+ pr_err(MIKRO_LZ77 "input longer than expected\n");
+ return -EFBIG;
+ }
+
+ opcode = kmalloc(sizeof(*opcode), GFP_KERNEL);
+ if (!opcode)
+ return -ENOMEM;
+
+ while (true) {
+ if (unlikely(output_ptr > output_end)) {
+ pr_err(MIKRO_LZ77 "output overrun\n");
+ rc = -EOVERFLOW;
+ goto free_lz77_struct;
+ }
+ if (unlikely(input_bit > in_len * BITS_PER_BYTE)) {
+ pr_err(MIKRO_LZ77 "input overrun\n");
+ rc = -ENODATA;
+ goto free_lz77_struct;
+ }
+
+ rc = rb_lz77_decode_instruction_operators(in, in_len, input_bit,
+ match_offset, opcode);
+ if (unlikely(rc < 0)) {
+ pr_err(MIKRO_LZ77
+ "instruction operands decode error\n");
+ goto free_lz77_struct;
+ }
+
+ pr_debug(MIKRO_LZ77 "inbit:0x%zx->outbyte:0x%zx", input_bit,
+ output_ptr - out);
+
+ input_bit += opcode->bits_used;
+ switch (opcode->instruction) {
+ case INSTR_LITERAL_BYTE:
+ pr_debug(" short");
+ fallthrough;
+ case INSTR_LONG:
+ if (opcode->offset == 0) {
+ /* this is a non-matching group */
+ pr_debug(" non-match, len: 0x%zx\n",
+ opcode->length);
+ /* test end marker */
+ if (opcode->length == 0xc &&
+ ((input_bit +
+ opcode->length * BITS_PER_BYTE) >
+ in_len)) {
+ *out_len = output_ptr - out;
+ pr_debug(
+ MIKRO_LZ77
+ "lz77 decompressed from %zu to %zu\n",
+ in_len, *out_len);
+ rc = 0;
+ goto free_lz77_struct;
+ }
+ for (i = opcode->length; i > 0; --i) {
+ *output_ptr =
+ rb_lz77_get_byte(in, input_bit);
+ ++output_ptr;
+ input_bit += BITS_PER_BYTE;
+ }
+ /* do no fallthrough if a non-match group */
+ break;
+ }
+ match_offset = opcode->offset;
+ fallthrough;
+ case INSTR_PREVIOUS_OFFSET:
+ match_length = opcode->length;
+ partial_count = 0;
+
+ pr_debug(" match, offset: 0x%zx, len: 0x%zx",
+ opcode->offset, match_length);
+
+ if (unlikely(opcode->offset == 0)) {
+ pr_err(MIKRO_LZ77
+ "match group missing opcode->offset\n");
+ rc = -EBADMSG;
+ goto free_lz77_struct;
+ }
+
+ /* overflow */
+ if (unlikely((output_ptr + match_length) >
+ output_end)) {
+ pr_err(MIKRO_LZ77
+ "match group output overflow\n");
+ rc = -ENOBUFS;
+ goto free_lz77_struct;
+ }
+
+ /* underflow */
+ if (unlikely((output_ptr - opcode->offset) < out)) {
+ pr_err(MIKRO_LZ77
+ "match group offset underflow\n");
+ rc = -ESPIPE;
+ goto free_lz77_struct;
+ }
+
+ /* there are cases where the match (length) includes
+ * data that is a part of the same match
+ */
+ while (opcode->offset < match_length) {
+ ++partial_count;
+ memcpy(output_ptr, output_ptr - opcode->offset,
+ opcode->offset);
+ output_ptr += opcode->offset;
+ match_length -= opcode->offset;
+ }
+ memcpy(output_ptr, output_ptr - opcode->offset,
+ match_length);
+ output_ptr += match_length;
+ if (partial_count)
+ pr_debug(" (%zu partial memcpy)",
+ partial_count);
+ pr_debug("\n");
+
+ break;
+
+ case INSTR_ERROR:
+ rc = -EINVAL;
+ goto free_lz77_struct;
+ }
+ }
+
+ pr_err(MIKRO_LZ77 "decode loop broken\n");
+ rc = -EINVAL;
+
+free_lz77_struct:
+ kfree(opcode);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(rb_lz77_decompress);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Mikrotik Wi-Fi caldata LZ77 decompressor");
+MODULE_AUTHOR("John Thomson");
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_lz77.h b/target/linux/generic/files/drivers/platform/mikrotik/rb_lz77.h
new file mode 100644
index 0000000000..55179fcbc8
--- /dev/null
+++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_lz77.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2024 John Thomson
+ */
+
+#ifndef __MIKROTIK_WLAN_LZ77_H__
+#define __MIKROTIK_WLAN_LZ77_H__
+
+#include <linux/errno.h>
+
+#ifdef CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77
+/**
+ * rb_lz77_decompress
+ *
+ * @in: compressed data ptr
+ * @in_len: length of compressed data
+ * @out: buffer ptr to decompress into
+ * @out_len: length of decompressed buffer in input,
+ * length of decompressed data in success
+ *
+ * Returns 0 on success, or negative error
+ */
+int rb_lz77_decompress(const u8 *in, const size_t in_len, u8 *out,
+ size_t *out_len);
+
+#else /* CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77 */
+
+static inline int rb_lz77_decompress(const u8 *in, const size_t in_len, u8 *out,
+ size_t *out_len)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif /* CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77 */
+#endif /* __MIKROTIK_WLAN_LZ77_H__ */
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h
index e858a524af..723f993eeb 100644
--- a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h
+++ b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h
@@ -15,6 +15,7 @@
#define RB_MAGIC_HARD (('H') | ('a' << 8) | ('r' << 16) | ('d' << 24))
#define RB_MAGIC_SOFT (('S') | ('o' << 8) | ('f' << 16) | ('t' << 24))
#define RB_MAGIC_LZOR (('L') | ('Z' << 8) | ('O' << 16) | ('R' << 24))
+#define RB_MAGIC_LZ77 (('L' << 24) | ('Z' << 16) | ('7' << 8) | ('7'))
#define RB_MAGIC_ERD (('E' << 16) | ('R' << 8) | ('D'))
#define RB_ART_SIZE 0x10000