1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter.git synced 2025-03-09 15:40:20 +00:00

Fix kernel 6.10 patches

This commit is contained in:
Ycarus (Yannick Chabanois) 2024-07-05 21:10:44 +02:00
parent f023e9c6e5
commit a9809a6010
47 changed files with 0 additions and 9856 deletions

View file

@ -1,71 +0,0 @@
From: Daniel González Cabanelas <dgcbueu@gmail.com>
Subject: [PATCH 2/2] rtc: rs5c372: let the alarm to be used as wakeup source
Currently there is no use for the interrupts on the rs5c372 RTC and the
wakealarm isn't enabled. There are some devices like NASes which use this
RTC to wake up from the power off state when the INTR pin is activated by
the alarm clock.
Enable the alarm and let to be used as a wakeup source.
Tested on a Buffalo LS421DE NAS.
Signed-off-by: Daniel González Cabanelas <dgcbueu@gmail.com>
---
drivers/rtc/rtc-rs5c372.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -832,6 +832,7 @@ static int rs5c372_probe(struct i2c_clie
int err = 0;
int smbus_mode = 0;
struct rs5c372 *rs5c372;
+ bool rs5c372_can_wakeup_device = false;
dev_dbg(&client->dev, "%s\n", __func__);
@@ -868,6 +869,12 @@ static int rs5c372_probe(struct i2c_clie
rs5c372->type = id->driver_data;
}
+#ifdef CONFIG_OF
+ if(of_property_read_bool(client->dev.of_node,
+ "wakeup-source"))
+ rs5c372_can_wakeup_device = true;
+#endif
+
/* we read registers 0x0f then 0x00-0x0f; skip the first one */
rs5c372->regs = &rs5c372->buf[1];
rs5c372->smbus = smbus_mode;
@@ -901,6 +908,8 @@ static int rs5c372_probe(struct i2c_clie
goto exit;
}
+ rs5c372->has_irq = 1;
+
/* if the oscillator lost power and no other software (like
* the bootloader) set it up, do it here.
*
@@ -927,6 +936,10 @@ static int rs5c372_probe(struct i2c_clie
);
/* REVISIT use client->irq to register alarm irq ... */
+ if (rs5c372_can_wakeup_device) {
+ device_init_wakeup(&client->dev, true);
+ }
+
rs5c372->rtc = devm_rtc_device_register(&client->dev,
rs5c372_driver.driver.name,
&rs5c372_rtc_ops, THIS_MODULE);
@@ -940,6 +953,10 @@ static int rs5c372_probe(struct i2c_clie
if (err)
goto exit;
+ /* the rs5c372 alarm only supports a minute accuracy */
+ set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rs5c372->rtc->features);
+ clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rs5c372->rtc->features);
+
return 0;
exit:

View file

@ -1,31 +0,0 @@
From c2deb5ef01a0ef09088832744cbace9e239a6ee0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibaut=20VAR=C3=88NE?= <hacks@slashdirt.org>
Date: Sat, 28 Mar 2020 12:11:50 +0100
Subject: [PATCH] generic: platform/mikrotik build bits (5.4)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This patch adds platform/mikrotik kernel build bits
Signed-off-by: Thibaut VARÈNE <hacks@slashdirt.org>
---
drivers/platform/Kconfig | 2 ++
drivers/platform/Makefile | 1 +
2 files changed, 3 insertions(+)
--- a/drivers/platform/Kconfig
+++ b/drivers/platform/Kconfig
@@ -14,3 +14,5 @@ source "drivers/platform/olpc/Kconfig"
source "drivers/platform/surface/Kconfig"
source "drivers/platform/x86/Kconfig"
+
+source "drivers/platform/mikrotik/Kconfig"
--- a/drivers/platform/Makefile
+++ b/drivers/platform/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_OLPC_EC) += olpc/
obj-$(CONFIG_GOLDFISH) += goldfish/
obj-$(CONFIG_CHROME_PLATFORMS) += chrome/
obj-$(CONFIG_SURFACE_PLATFORMS) += surface/
+obj-$(CONFIG_MIKROTIK) += mikrotik/

View file

@ -1,370 +0,0 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: mips: replace -mlong-calls with -mno-long-calls to make function calls faster in kernel modules to achieve this, try to
lede-commit: 3b3d64743ba2a874df9d70cd19e242205b0a788c
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
arch/mips/Makefile | 5 +
arch/mips/include/asm/module.h | 5 +
arch/mips/kernel/module.c | 279 ++++++++++++++++++++++++++++++++++++++++-
3 files changed, 284 insertions(+), 5 deletions(-)
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -97,8 +97,18 @@ all-$(CONFIG_SYS_SUPPORTS_ZBOOT)+= vmlin
cflags-y += -G 0 -mno-abicalls -fno-pic -pipe -mno-branch-likely
cflags-y += -msoft-float -Wa,-msoft-float
LDFLAGS_vmlinux += -G 0 -static -n -nostdlib
+ifdef CONFIG_64BIT
KBUILD_AFLAGS_MODULE += -mlong-calls
KBUILD_CFLAGS_MODULE += -mlong-calls
+else
+ ifdef CONFIG_DYNAMIC_FTRACE
+ KBUILD_AFLAGS_MODULE += -mlong-calls
+ KBUILD_CFLAGS_MODULE += -mlong-calls
+ else
+ KBUILD_AFLAGS_MODULE += -mno-long-calls
+ KBUILD_CFLAGS_MODULE += -mno-long-calls
+ endif
+endif
ifeq ($(CONFIG_RELOCATABLE),y)
LDFLAGS_vmlinux += --emit-relocs
--- a/arch/mips/include/asm/module.h
+++ b/arch/mips/include/asm/module.h
@@ -12,6 +12,11 @@ struct mod_arch_specific {
const struct exception_table_entry *dbe_start;
const struct exception_table_entry *dbe_end;
struct mips_hi16 *r_mips_hi16_list;
+
+ void *phys_plt_tbl;
+ void *virt_plt_tbl;
+ unsigned int phys_plt_offset;
+ unsigned int virt_plt_offset;
};
typedef uint8_t Elf64_Byte; /* Type for a 8-bit quantity. */
--- a/arch/mips/kernel/module.c
+++ b/arch/mips/kernel/module.c
@@ -32,23 +32,261 @@ struct mips_hi16 {
static LIST_HEAD(dbe_list);
static DEFINE_SPINLOCK(dbe_lock);
-#ifdef MODULE_START
+/*
+ * Get the potential max trampolines size required of the init and
+ * non-init sections. Only used if we cannot find enough contiguous
+ * physically mapped memory to put the module into.
+ */
+static unsigned int
+get_plt_size(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
+ const char *secstrings, unsigned int symindex, bool is_init)
+{
+ unsigned long ret = 0;
+ unsigned int i, j;
+ Elf_Sym *syms;
+
+ /* Everything marked ALLOC (this includes the exported symbols) */
+ for (i = 1; i < hdr->e_shnum; ++i) {
+ unsigned int info = sechdrs[i].sh_info;
+
+ if (sechdrs[i].sh_type != SHT_REL
+ && sechdrs[i].sh_type != SHT_RELA)
+ continue;
+
+ /* Not a valid relocation section? */
+ if (info >= hdr->e_shnum)
+ continue;
+
+ /* Don't bother with non-allocated sections */
+ if (!(sechdrs[info].sh_flags & SHF_ALLOC))
+ continue;
+
+ /* If it's called *.init*, and we're not init, we're
+ not interested */
+ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != 0)
+ != is_init)
+ continue;
+
+ syms = (Elf_Sym *) sechdrs[symindex].sh_addr;
+ if (sechdrs[i].sh_type == SHT_REL) {
+ Elf_Mips_Rel *rel = (void *) sechdrs[i].sh_addr;
+ unsigned int size = sechdrs[i].sh_size / sizeof(*rel);
+
+ for (j = 0; j < size; ++j) {
+ Elf_Sym *sym;
+
+ if (ELF_MIPS_R_TYPE(rel[j]) != R_MIPS_26)
+ continue;
+
+ sym = syms + ELF_MIPS_R_SYM(rel[j]);
+ if (!is_init && sym->st_shndx != SHN_UNDEF)
+ continue;
+
+ ret += 4 * sizeof(int);
+ }
+ } else {
+ Elf_Mips_Rela *rela = (void *) sechdrs[i].sh_addr;
+ unsigned int size = sechdrs[i].sh_size / sizeof(*rela);
+
+ for (j = 0; j < size; ++j) {
+ Elf_Sym *sym;
+
+ if (ELF_MIPS_R_TYPE(rela[j]) != R_MIPS_26)
+ continue;
+
+ sym = syms + ELF_MIPS_R_SYM(rela[j]);
+ if (!is_init && sym->st_shndx != SHN_UNDEF)
+ continue;
+
+ ret += 4 * sizeof(int);
+ }
+ }
+ }
+
+ return ret;
+}
+
+#ifndef MODULE_START
+static void *alloc_phys(unsigned long size)
+{
+ unsigned order;
+ struct page *page;
+ struct page *p;
+
+ size = PAGE_ALIGN(size);
+ order = get_order(size);
+
+ page = alloc_pages(GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN |
+ __GFP_THISNODE, order);
+ if (!page)
+ return NULL;
+
+ split_page(page, order);
+
+ /* mark all pages except for the last one */
+ for (p = page; p + 1 < page + (size >> PAGE_SHIFT); ++p)
+ set_bit(PG_owner_priv_1, &p->flags);
+
+ for (p = page + (size >> PAGE_SHIFT); p < page + (1 << order); ++p)
+ __free_page(p);
+
+ return page_address(page);
+}
+#endif
+
+static void free_phys(void *ptr)
+{
+ struct page *page;
+ bool free;
+
+ page = virt_to_page(ptr);
+ do {
+ free = test_and_clear_bit(PG_owner_priv_1, &page->flags);
+ __free_page(page);
+ page++;
+ } while (free);
+}
+
+
void *module_alloc(unsigned long size)
{
+#ifdef MODULE_START
return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END,
GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE,
__builtin_return_address(0));
+#else
+ void *ptr;
+
+ if (size == 0)
+ return NULL;
+
+ ptr = alloc_phys(size);
+
+ /* If we failed to allocate physically contiguous memory,
+ * fall back to regular vmalloc. The module loader code will
+ * create jump tables to handle long jumps */
+ if (!ptr)
+ return vmalloc(size);
+
+ return ptr;
+#endif
}
+
+static inline bool is_phys_addr(void *ptr)
+{
+#ifdef CONFIG_64BIT
+ return (KSEGX((unsigned long)ptr) == CKSEG0);
+#else
+ return (KSEGX(ptr) == KSEG0);
#endif
+}
+
+/* Free memory returned from module_alloc */
+void module_memfree(void *module_region)
+{
+ if (is_phys_addr(module_region))
+ free_phys(module_region);
+ else
+ vfree(module_region);
+}
+
+static void *__module_alloc(int size, bool phys)
+{
+ void *ptr;
+
+ if (phys)
+ ptr = kmalloc(size, GFP_KERNEL);
+ else
+ ptr = vmalloc(size);
+ return ptr;
+}
+
+static void __module_free(void *ptr)
+{
+ if (is_phys_addr(ptr))
+ kfree(ptr);
+ else
+ vfree(ptr);
+}
+
+int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
+ char *secstrings, struct module *mod)
+{
+ unsigned int symindex = 0;
+ unsigned int core_size, init_size;
+ int i;
+
+ mod->arch.phys_plt_offset = 0;
+ mod->arch.virt_plt_offset = 0;
+ mod->arch.phys_plt_tbl = NULL;
+ mod->arch.virt_plt_tbl = NULL;
+
+ if (IS_ENABLED(CONFIG_64BIT))
+ return 0;
+
+ for (i = 1; i < hdr->e_shnum; i++)
+ if (sechdrs[i].sh_type == SHT_SYMTAB)
+ symindex = i;
+
+ core_size = get_plt_size(hdr, sechdrs, secstrings, symindex, false);
+ init_size = get_plt_size(hdr, sechdrs, secstrings, symindex, true);
+
+ if ((core_size + init_size) == 0)
+ return 0;
+
+ mod->arch.phys_plt_tbl = __module_alloc(core_size + init_size, 1);
+ if (!mod->arch.phys_plt_tbl)
+ return -ENOMEM;
+
+ mod->arch.virt_plt_tbl = __module_alloc(core_size + init_size, 0);
+ if (!mod->arch.virt_plt_tbl) {
+ __module_free(mod->arch.phys_plt_tbl);
+ mod->arch.phys_plt_tbl = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
static void apply_r_mips_32(u32 *location, u32 base, Elf_Addr v)
{
*location = base + v;
}
+static Elf_Addr add_plt_entry_to(unsigned *plt_offset,
+ void *start, Elf_Addr v)
+{
+ unsigned *tramp = start + *plt_offset;
+ *plt_offset += 4 * sizeof(int);
+
+ /* adjust carry for addiu */
+ if (v & 0x00008000)
+ v += 0x10000;
+
+ tramp[0] = 0x3c190000 | (v >> 16); /* lui t9, hi16 */
+ tramp[1] = 0x27390000 | (v & 0xffff); /* addiu t9, t9, lo16 */
+ tramp[2] = 0x03200008; /* jr t9 */
+ tramp[3] = 0x00000000; /* nop */
+
+ return (Elf_Addr) tramp;
+}
+
+static Elf_Addr add_plt_entry(struct module *me, void *location, Elf_Addr v)
+{
+ if (is_phys_addr(location))
+ return add_plt_entry_to(&me->arch.phys_plt_offset,
+ me->arch.phys_plt_tbl, v);
+ else
+ return add_plt_entry_to(&me->arch.virt_plt_offset,
+ me->arch.virt_plt_tbl, v);
+
+}
+
static int apply_r_mips_26(struct module *me, u32 *location, u32 base,
Elf_Addr v)
{
+ u32 ofs = base & 0x03ffffff;
+
if (v % 4) {
pr_err("module %s: dangerous R_MIPS_26 relocation\n",
me->name);
@@ -56,13 +294,17 @@ static int apply_r_mips_26(struct module
}
if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) {
- pr_err("module %s: relocation overflow\n",
- me->name);
- return -ENOEXEC;
+ v = add_plt_entry(me, location, v + (ofs << 2));
+ if (!v) {
+ pr_err("module %s: relocation overflow\n",
+ me->name);
+ return -ENOEXEC;
+ }
+ ofs = 0;
}
*location = (*location & ~0x03ffffff) |
- ((base + (v >> 2)) & 0x03ffffff);
+ ((ofs + (v >> 2)) & 0x03ffffff);
return 0;
}
@@ -442,9 +684,36 @@ int module_finalize(const Elf_Ehdr *hdr,
list_add(&me->arch.dbe_list, &dbe_list);
spin_unlock_irq(&dbe_lock);
}
+
+ /* Get rid of the fixup trampoline if we're running the module
+ * from physically mapped address space */
+ if (me->arch.phys_plt_offset == 0) {
+ __module_free(me->arch.phys_plt_tbl);
+ me->arch.phys_plt_tbl = NULL;
+ }
+ if (me->arch.virt_plt_offset == 0) {
+ __module_free(me->arch.virt_plt_tbl);
+ me->arch.virt_plt_tbl = NULL;
+ }
+
return 0;
}
+void module_arch_freeing_init(struct module *mod)
+{
+ if (mod->state == MODULE_STATE_LIVE)
+ return;
+
+ if (mod->arch.phys_plt_tbl) {
+ __module_free(mod->arch.phys_plt_tbl);
+ mod->arch.phys_plt_tbl = NULL;
+ }
+ if (mod->arch.virt_plt_tbl) {
+ __module_free(mod->arch.virt_plt_tbl);
+ mod->arch.virt_plt_tbl = NULL;
+ }
+}
+
void module_arch_cleanup(struct module *mod)
{
spin_lock_irq(&dbe_lock);

View file

@ -1,245 +0,0 @@
From acacdac272927ae1d96e0bca51eb82899671eaea Mon Sep 17 00:00:00 2001
From: John Thomson <git@johnthomson.fastmail.com.au>
Date: Fri, 25 Dec 2020 18:50:08 +1000
Subject: [PATCH] mtd: spi-nor: write support for minor aligned partitions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Do not prevent writing to mtd partitions where a partition boundary sits
on a minor erasesize boundary.
This addresses a FIXME that has been present since the start of the
linux git history:
/* Doesn't start on a boundary of major erase size */
/* FIXME: Let it be writable if it is on a boundary of
* _minor_ erase size though */
Allow a uniform erase region spi-nor device to be configured
to use the non-uniform erase regions code path for an erase with:
CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE=y
On supporting hardware (SECT_4K: majority of current SPI-NOR device)
provide the facility for an erase to use the least number
of SPI-NOR operations, as well as access to 4K erase without
requiring CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
Introduce erasesize_minor to the mtd struct,
the smallest erasesize supported by the device
On existing devices, this is useful where write support is wanted
for data on a 4K partition, such as some u-boot-env partitions,
or RouterBoot soft_config, while still netting the performance
benefits of using 64K sectors
Performance:
time mtd erase firmware
OpenWrt 5.10 ramips MT7621 w25q128jv 0xfc0000 partition length
Without this patch
MTD_SPI_NOR_USE_4K_SECTORS=y |n
real 2m 11.66s |0m 50.86s
user 0m 0.00s |0m 0.00s
sys 1m 56.20s |0m 50.80s
With this patch
MTD_SPI_NOR_USE_VARIABLE_ERASE=n|y |4K_SECTORS=y
real 0m 51.68s |0m 50.85s |2m 12.89s
user 0m 0.00s |0m 0.00s |0m 0.01s
sys 0m 46.94s |0m 50.38s |2m 12.46s
Signed-off-by: John Thomson <git@johnthomson.fastmail.com.au>
Signed-off-by: Thibaut VARÈNE <hacks+kernel@slashdirt.org>
---
checkpatch does not like the printk(KERN_WARNING
these should be changed separately beforehand?
Changes v1 -> v2:
Added mtdcore sysfs for erasesize_minor
Removed finding minor erasesize for variable erase regions device,
as untested and no responses regarding it.
Moved IF_ENABLED for SPINOR variable erase to guard setting
erasesize_minor in spi-nor/core.c
Removed setting erasesize to minor where partition boundaries require
minor erase to be writable
Simplified minor boundary check by relying on minor being a factor of
major
Changes RFC -> v1:
Fix uninitialized variable smatch warning
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>
---
drivers/mtd/mtdcore.c | 10 ++++++++++
drivers/mtd/mtdpart.c | 35 +++++++++++++++++++++++++----------
drivers/mtd/spi-nor/Kconfig | 10 ++++++++++
drivers/mtd/spi-nor/core.c | 11 +++++++++--
include/linux/mtd/mtd.h | 2 ++
5 files changed, 56 insertions(+), 12 deletions(-)
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -198,6 +198,15 @@ static ssize_t mtd_erasesize_show(struct
}
MTD_DEVICE_ATTR_RO(erasesize);
+static ssize_t mtd_erasesize_minor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mtd_info *mtd = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%lu\n", (unsigned long)mtd->erasesize_minor);
+}
+MTD_DEVICE_ATTR_RO(erasesize_minor);
+
static ssize_t mtd_writesize_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -343,6 +352,7 @@ static struct attribute *mtd_attrs[] = {
&dev_attr_flags.attr,
&dev_attr_size.attr,
&dev_attr_erasesize.attr,
+ &dev_attr_erasesize_minor.attr,
&dev_attr_writesize.attr,
&dev_attr_subpagesize.attr,
&dev_attr_oobsize.attr,
--- a/drivers/mtd/mtdpart.c
+++ b/drivers/mtd/mtdpart.c
@@ -47,6 +47,7 @@ static struct mtd_info *allocate_partiti
struct mtd_info *master = mtd_get_master(parent);
int wr_alignment = (parent->flags & MTD_NO_ERASE) ?
master->writesize : master->erasesize;
+ int wr_alignment_minor = 0;
u64 parent_size = mtd_is_partition(parent) ?
parent->part.size : parent->size;
struct mtd_info *child;
@@ -171,6 +172,7 @@ static struct mtd_info *allocate_partiti
} else {
/* Single erase size */
child->erasesize = master->erasesize;
+ child->erasesize_minor = master->erasesize_minor;
}
/*
@@ -178,26 +180,39 @@ static struct mtd_info *allocate_partiti
* exposes several regions with different erasesize. Adjust
* wr_alignment accordingly.
*/
- if (!(child->flags & MTD_NO_ERASE))
+ if (!(child->flags & MTD_NO_ERASE)) {
wr_alignment = child->erasesize;
+ wr_alignment_minor = child->erasesize_minor;
+ }
tmp = mtd_get_master_ofs(child, 0);
remainder = do_div(tmp, wr_alignment);
if ((child->flags & MTD_WRITEABLE) && remainder) {
- /* Doesn't start on a boundary of major erase size */
- /* FIXME: Let it be writable if it is on a boundary of
- * _minor_ erase size though */
- child->flags &= ~MTD_WRITEABLE;
- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
- part->name);
+ if (wr_alignment_minor) {
+ /* rely on minor being a factor of major erasesize */
+ tmp = remainder;
+ remainder = do_div(tmp, wr_alignment_minor);
+ }
+ if (remainder) {
+ child->flags &= ~MTD_WRITEABLE;
+ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
+ part->name);
+ }
}
tmp = mtd_get_master_ofs(child, 0) + child->part.size;
remainder = do_div(tmp, wr_alignment);
if ((child->flags & MTD_WRITEABLE) && remainder) {
- child->flags &= ~MTD_WRITEABLE;
- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
- part->name);
+ if (wr_alignment_minor) {
+ tmp = remainder;
+ remainder = do_div(tmp, wr_alignment_minor);
+ }
+
+ if (remainder) {
+ child->flags &= ~MTD_WRITEABLE;
+ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
+ part->name);
+ }
}
child->size = child->part.size;
--- a/drivers/mtd/spi-nor/Kconfig
+++ b/drivers/mtd/spi-nor/Kconfig
@@ -10,6 +10,16 @@ menuconfig MTD_SPI_NOR
if MTD_SPI_NOR
+config MTD_SPI_NOR_USE_VARIABLE_ERASE
+ bool "Disable uniform_erase to allow use of all hardware supported erasesizes"
+ depends on !MTD_SPI_NOR_USE_4K_SECTORS
+ default n
+ help
+ Allow mixed use of all hardware supported erasesizes,
+ by forcing spi_nor to use the multiple eraseregions code path.
+ For example: A 68K erase will use one 64K erase, and one 4K erase
+ on supporting hardware.
+
config MTD_SPI_NOR_USE_4K_SECTORS
bool "Use small 4096 B erase sectors"
default y
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -1150,6 +1150,8 @@ static u8 spi_nor_convert_3to4_erase(u8
static bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
{
+ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE))
+ return false;
return !!nor->params->erase_map.uniform_erase_type;
}
@@ -2582,6 +2584,7 @@ static int spi_nor_select_erase(struct s
{
struct spi_nor_erase_map *map = &nor->params->erase_map;
const struct spi_nor_erase_type *erase = NULL;
+ const struct spi_nor_erase_type *erase_minor = NULL;
struct mtd_info *mtd = &nor->mtd;
u32 wanted_size = nor->info->sector_size;
int i;
@@ -2614,8 +2617,9 @@ static int spi_nor_select_erase(struct s
*/
for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
if (map->erase_type[i].size) {
- erase = &map->erase_type[i];
- break;
+ if (!erase)
+ erase = &map->erase_type[i];
+ erase_minor = &map->erase_type[i];
}
}
@@ -2623,6 +2627,9 @@ static int spi_nor_select_erase(struct s
return -EINVAL;
mtd->erasesize = erase->size;
+ if (IS_ENABLED(CONFIG_MTD_SPI_NOR_USE_VARIABLE_ERASE) &&
+ erase_minor && erase_minor->size < erase->size)
+ mtd->erasesize_minor = erase_minor->size;
return 0;
}
--- a/include/linux/mtd/mtd.h
+++ b/include/linux/mtd/mtd.h
@@ -245,6 +245,8 @@ struct mtd_info {
* information below if they desire
*/
uint32_t erasesize;
+ /* "Minor" (smallest) erase size supported by the whole device */
+ uint32_t erasesize_minor;
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
* though individual bits can be cleared), in case of NAND flash it is
* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR

View file

@ -1,74 +0,0 @@
From 7f4c9c534aabe1315669e076d3fe0af0fd374cda Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Thu, 30 May 2024 03:13:19 +0100
Subject: [PATCH 2/9] block: partitions: populate fwnode
Let block partitions to be represented by a firmware node and hence
allow them to being referenced e.g. for use with blk-nvmem.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
block/partitions/core.c | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
--- a/block/partitions/core.c
+++ b/block/partitions/core.c
@@ -10,6 +10,8 @@
#include <linux/ctype.h>
#include <linux/vmalloc.h>
#include <linux/raid/detect.h>
+#include <linux/property.h>
+
#include "check.h"
static int (*const check_part[])(struct parsed_partitions *) = {
@@ -292,6 +294,40 @@ static ssize_t whole_disk_show(struct de
}
static const DEVICE_ATTR(whole_disk, 0444, whole_disk_show, NULL);
+static struct fwnode_handle *find_partition_fwnode(struct block_device *bdev)
+{
+ struct fwnode_handle *fw_parts, *fw_part;
+ struct device *ddev = disk_to_dev(bdev->bd_disk);
+ const char *partname, *uuid;
+ u32 partno;
+
+ fw_parts = device_get_named_child_node(ddev, "partitions");
+ if (!fw_parts)
+ return NULL;
+
+ fwnode_for_each_child_node(fw_parts, fw_part) {
+ if (!fwnode_property_read_string(fw_part, "uuid", &uuid) &&
+ (!bdev->bd_meta_info || strncmp(uuid,
+ bdev->bd_meta_info->uuid,
+ PARTITION_META_INFO_UUIDLTH)))
+ continue;
+
+ if (!fwnode_property_read_string(fw_part, "partname", &partname) &&
+ (!bdev->bd_meta_info || strncmp(partname,
+ bdev->bd_meta_info->volname,
+ PARTITION_META_INFO_VOLNAMELTH)))
+ continue;
+
+ if (!fwnode_property_read_u32(fw_part, "partno", &partno) &&
+ bdev->bd_partno != partno)
+ continue;
+
+ return fw_part;
+ }
+
+ return NULL;
+}
+
/*
* Must be called either with open_mutex held, before a disk can be opened or
* after all disk users are gone.
@@ -374,6 +410,8 @@ static struct block_device *add_partitio
goto out_put;
}
+ device_set_node(pdev, find_partition_fwnode(bdev));
+
/* delay uevent until 'holders' subdir is created */
dev_set_uevent_suppress(pdev, 1);
err = device_add(pdev);

View file

@ -1,19 +0,0 @@
From: Piotr Dymacz <pepe2k@gmail.com>
Subject: kernel/mtd: add support for EON EN25Q128
Signed-off-by: Piotr Dymacz <pepe2k@gmail.com>
---
drivers/mtd/spi-nor/spi-nor.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/mtd/spi-nor/eon.c
+++ b/drivers/mtd/spi-nor/eon.c
@@ -17,6 +17,8 @@ static const struct flash_info eon_nor_p
{ "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128) },
{ "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128)
NO_SFDP_FLAGS(SECT_4K) },
+ { "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256)
+ NO_SFDP_FLAGS(SECT_4K) },
{ "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
{ "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32)

View file

@ -1,21 +0,0 @@
From: Christian Marangi <ansuelsmth@gmail.com>
Subject: kernel/mtd: add support for EON EN25QX128A
Add support for EON EN25QX128A with no flags as it does
support SFDP parsing.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/mtd/spi-nor/spi-nor.c | 1 +
1 file changed, 1 insertion(+)
--- a/drivers/mtd/spi-nor/eon.c
+++ b/drivers/mtd/spi-nor/eon.c
@@ -19,6 +19,7 @@ static const struct flash_info eon_nor_p
NO_SFDP_FLAGS(SECT_4K) },
{ "en25q128", INFO(0x1c3018, 0, 64 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K) },
+ { "en25qx128a", INFO(0x1c7118, 0, 64 * 1024, 256) },
{ "en25q80a", INFO(0x1c3014, 0, 64 * 1024, 16)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ) },
{ "en25qh16", INFO(0x1c7015, 0, 64 * 1024, 32)

View file

@ -1,23 +0,0 @@
From d68b4aa22e8c625685bfad642dd7337948dc0ad1 Mon Sep 17 00:00:00 2001
From: Koen Vandeputte <koen.vandeputte@ncentric.com>
Date: Mon, 6 Jan 2020 13:07:56 +0100
Subject: [PATCH] mtd: spi-nor: add support for Gigadevice GD25D05
Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
---
drivers/mtd/spi-nor/spi-nor.c | 5 +++++
1 file changed, 5 insertions(+)
--- a/drivers/mtd/spi-nor/gigadevice.c
+++ b/drivers/mtd/spi-nor/gigadevice.c
@@ -34,6 +34,10 @@ static const struct spi_nor_fixups gd25q
};
static const struct flash_info gigadevice_nor_parts[] = {
+ { "gd25q05", INFO(0xc84010, 0, 64 * 1024, 1)
+ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
{ "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |

View file

@ -1,23 +0,0 @@
From f8943df3beb0d3f9754bb35320c3a378727175a8 Mon Sep 17 00:00:00 2001
From: OpenWrt community <openwrt-devel@lists.openwrt.org>
Date: Thu, 14 Jul 2022 08:38:07 +0200
Subject: [PATCH] spi-nor/gigadevic: add gd25q512
---
drivers/mtd/spi-nor/gigadevice.c | 3 +++
1 file changed, 3 insertions(+)
--- a/drivers/mtd/spi-nor/gigadevice.c
+++ b/drivers/mtd/spi-nor/gigadevice.c
@@ -71,6 +71,11 @@ static const struct flash_info gigadevic
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_TB_SR_BIT6)
FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
.fixups = &gd25q256_fixups },
+ { "gd25q512", INFO(0xc84020, 0, 64 * 1024, 1024)
+ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
+ FIXUP_FLAGS(SPI_NOR_4B_OPCODES)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
};
const struct spi_nor_manufacturer spi_nor_gigadevice = {

View file

@ -1,24 +0,0 @@
From 87363cc0e522de3294ea6ae10fb468d2a8d6fb2f Mon Sep 17 00:00:00 2001
From: OpenWrt community <openwrt-devel@lists.openwrt.org>
Date: Wed, 13 Jul 2022 12:17:21 +0200
Subject: [PATCH] spi-nor/esmt.c: add esmt f25l16pa
This fixes support for Dongwon T&I DW02-412H which uses F25L16PA(2S)
flash.
---
drivers/mtd/spi-nor/esmt.c | 2 ++
1 file changed, 2 insertions(+)
--- a/drivers/mtd/spi-nor/esmt.c
+++ b/drivers/mtd/spi-nor/esmt.c
@@ -10,6 +10,9 @@
static const struct flash_info esmt_nor_parts[] = {
/* ESMT */
+ { "f25l16pa-2s", INFO(0x8c2115, 0, 64 * 1024, 32)
+ FLAGS(SPI_NOR_HAS_LOCK)
+ NO_SFDP_FLAGS(SECT_4K) },
{ "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64)
FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
NO_SFDP_FLAGS(SECT_4K) },

View file

@ -1,25 +0,0 @@
From f6b33d850f7f12555df2fa0e3349b33427bf5890 Mon Sep 17 00:00:00 2001
From: OpenWrt community <openwrt-devel@lists.openwrt.org>
Date: Wed, 13 Jul 2022 12:19:01 +0200
Subject: [PATCH] spi-nor/xmc.c: add xm25qh128c
The XMC XM25QH128C is a 16MB SPI NOR chip. The patch is verified on
Ruijie RG-EW3200GX PRO.
Datasheet available at https://www.xmcwh.com/uploads/435/XM25QH128C.pdf
---
drivers/mtd/spi-nor/xmc.c | 2 ++
1 file changed, 2 insertions(+)
--- a/drivers/mtd/spi-nor/xmc.c
+++ b/drivers/mtd/spi-nor/xmc.c
@@ -16,6 +16,9 @@ static const struct flash_info xmc_nor_p
{ "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
SPI_NOR_QUAD_READ) },
+ { "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
};
const struct spi_nor_manufacturer spi_nor_xmc = {

View file

@ -1,170 +0,0 @@
From f32085fc0b87049491b07e198d924d738a1a2834 Mon Sep 17 00:00:00 2001
From: Daniel Danzberger <daniel@dd-wrt.com>
Date: Wed, 3 Aug 2022 17:31:03 +0200
Subject: [PATCH] mtd: spinand: Add support for Etron EM73D044VCx
Airoha is a new ARM platform based on Cortex-A53 which has recently been
merged into linux-next.
Due to BootROM limitations on this platform, the Cortex-A53 can't run in
Aarch64 mode and code must be compiled for 32-Bit ARM.
This support is based mostly on those linux-next commits backported
for kernel 5.15.
Patches:
1 - platform support = linux-next
2 - clock driver = linux-next
3 - gpio driver = linux-next
4 - linux,usable-memory-range dts support = linux-next
5 - mtd spinand driver
6 - spi driver
7 - pci driver (kconfig only, uses mediatek PCI) = linux-next
Still missing:
- Ethernet driver
- Sysupgrade support
A.t.m there exists one subtarget EN7523 with only one evaluation
board.
The initramfs can be run with the following commands from u-boot:
-
u-boot> setenv bootfile \
openwrt-airoha-airoha_en7523-evb-initramfs-kernel.bin
u-boot> tftpboot
u-boot> bootm 0x81800000
-
Submitted-by: Daniel Danzberger <daniel@dd-wrt.com>
--- a/drivers/mtd/nand/spi/Makefile
+++ b/drivers/mtd/nand/spi/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-spinand-objs := core.o alliancememory.o ato.o esmt.o gigadevice.o macronix.o
-spinand-objs += micron.o paragon.o toshiba.o winbond.o xtx.o
+spinand-objs := core.o alliancememory.o ato.o esmt.o etron.o gigadevice.o
+spinand-objs += macronix.o micron.o paragon.o toshiba.o winbond.o xtx.o
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -940,6 +940,7 @@ static const struct spinand_manufacturer
&alliancememory_spinand_manufacturer,
&ato_spinand_manufacturer,
&esmt_c8_spinand_manufacturer,
+ &etron_spinand_manufacturer,
&gigadevice_spinand_manufacturer,
&macronix_spinand_manufacturer,
&micron_spinand_manufacturer,
--- /dev/null
+++ b/drivers/mtd/nand/spi/etron.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_ETRON 0xd5
+
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+ SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+ SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+ SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+ SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+ SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+ SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+static int etron_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 72;
+ oobregion->length = 56;
+
+ return 0;
+}
+
+static int etron_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *oobregion)
+{
+ if (section)
+ return -ERANGE;
+
+ oobregion->offset = 1;
+ oobregion->length = 71;
+
+ return 0;
+}
+
+static int etron_ecc_get_status(struct spinand_device *spinand, u8 status)
+{
+ switch (status & STATUS_ECC_MASK) {
+ case STATUS_ECC_NO_BITFLIPS:
+ return 0;
+
+ case STATUS_ECC_HAS_BITFLIPS:
+ /* Between 1-7 bitflips were corrected */
+ return 7;
+
+ case STATUS_ECC_MASK:
+ /* Maximum bitflips were corrected */
+ return 8;
+
+ case STATUS_ECC_UNCOR_ERROR:
+ return -EBADMSG;
+ }
+
+ return -EINVAL;
+}
+
+static const struct mtd_ooblayout_ops etron_ooblayout = {
+ .ecc = etron_ooblayout_ecc,
+ .free = etron_ooblayout_free,
+};
+
+static const struct spinand_info etron_spinand_table[] = {
+ SPINAND_INFO("EM73D044VCx",
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x1f),
+ // bpc, pagesize, oobsize, pagesperblock, bperlun, maxbadplun, ppl, lpt, #t
+ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ SPINAND_HAS_QE_BIT,
+ SPINAND_ECCINFO(&etron_ooblayout, etron_ecc_get_status)),
+};
+
+static const struct spinand_manufacturer_ops etron_spinand_manuf_ops = {
+};
+
+const struct spinand_manufacturer etron_spinand_manufacturer = {
+ .id = SPINAND_MFR_ETRON,
+ .name = "Etron",
+ .chips = etron_spinand_table,
+ .nchips = ARRAY_SIZE(etron_spinand_table),
+ .ops = &etron_spinand_manuf_ops,
+};
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -263,6 +263,7 @@ struct spinand_manufacturer {
extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
extern const struct spinand_manufacturer ato_spinand_manufacturer;
extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
+extern const struct spinand_manufacturer etron_spinand_manufacturer;
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer;

View file

@ -1,23 +0,0 @@
From: Joe Mullally <jwmullally@gmail.com>
Subject: mtd/spi-nor/xmc: add support for XMC XM25QH64C
The XMC XM25QH64C is a 8MB SPI NOR chip. The patch is verified on TL-WPA8631P v3.
Datasheet available at https://www.xmcwh.com/uploads/442/XM25QH64C.pdf
Signed-off-by: Joe Mullally <jwmullally@gmail.com>
---
drivers/mtd/spi-nor/xmc.c | 2 ++
1 file changed, 2 insertions(+)
--- a/drivers/mtd/spi-nor/xmc.c
+++ b/drivers/mtd/spi-nor/xmc.c
@@ -13,6 +13,9 @@ static const struct flash_info xmc_nor_p
{ "XM25QH64A", INFO(0x207017, 0, 64 * 1024, 128)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
SPI_NOR_QUAD_READ) },
+ { "XM25QH64C", INFO(0x204017, 0, 64 * 1024, 128)
+ NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
+ SPI_NOR_QUAD_READ) },
{ "XM25QH128A", INFO(0x207018, 0, 64 * 1024, 256)
NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
SPI_NOR_QUAD_READ) },

View file

@ -1,32 +0,0 @@
From 8bf2ce6ea4ee840b70f55a27f80e1cd308051b13 Mon Sep 17 00:00:00 2001
From: Nick Hainke <vincent@systemli.org>
Date: Mon, 27 Dec 2021 00:38:13 +0100
Subject: [PATCH 1/2] mtd: spi-nor: locking support for MX25L6405D
Macronix MX25L6405D supports locking with four block-protection bits.
Currently, the driver only sets three bits. If the bootloader does not
sustain the flash chip in an unlocked state, the flash might be
non-writeable. Add the corresponding flag to enable locking support with
four bits in the status register.
Tested on Nanostation M2 XM.
Similar to commit 7ea40b54e83b ("mtd: spi-nor: enable locking support for
MX25L12805D")
Signed-off-by: David Bauer <mail@david-bauer.net>
Signed-off-by: Nick Hainke <vincent@systemli.org>
---
drivers/mtd/spi-nor/macronix.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
--- a/drivers/mtd/spi-nor/macronix.c
+++ b/drivers/mtd/spi-nor/macronix.c
@@ -48,6 +48,7 @@ static const struct flash_info macronix_
{ "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64)
NO_SFDP_FLAGS(SECT_4K) },
{ "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128)
+ FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_4BIT_BP)
NO_SFDP_FLAGS(SECT_4K) },
{ "mx25u2033e", INFO(0xc22532, 0, 64 * 1024, 4)
NO_SFDP_FLAGS(SECT_4K) },

View file

@ -1,578 +0,0 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Tue, 23 Apr 2024 11:23:03 +0200
Subject: [PATCH] net: add TCP fraglist GRO support
When forwarding TCP after GRO, software segmentation is very expensive,
especially when the checksum needs to be recalculated.
One case where that's currently unavoidable is when routing packets over
PPPoE. Performance improves significantly when using fraglist GRO
implemented in the same way as for UDP.
Here's a measurement of running 2 TCP streams through a MediaTek MT7622
device (2-core Cortex-A53), which runs NAT with flow offload enabled from
one ethernet port to PPPoE on another ethernet port + cake qdisc set to
1Gbps.
rx-gro-list off: 630 Mbit/s, CPU 35% idle
rx-gro-list on: 770 Mbit/s, CPU 40% idle
Signe-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/include/net/gro.h
+++ b/include/net/gro.h
@@ -439,6 +439,7 @@ static inline __wsum ip6_gro_compute_pse
}
int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb);
+int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb);
/* Pass the currently batched GRO_NORMAL SKBs up to the stack. */
static inline void gro_normal_list(struct napi_struct *napi)
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -2083,7 +2083,10 @@ void tcp_v4_destroy_sock(struct sock *sk
struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
netdev_features_t features);
-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb);
+struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb);
+struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th);
+struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th);
INDIRECT_CALLABLE_DECLARE(int tcp4_gro_complete(struct sk_buff *skb, int thoff));
INDIRECT_CALLABLE_DECLARE(struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb));
INDIRECT_CALLABLE_DECLARE(int tcp6_gro_complete(struct sk_buff *skb, int thoff));
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -233,6 +233,33 @@ done:
return 0;
}
+int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
+{
+ if (unlikely(p->len + skb->len >= 65536))
+ return -E2BIG;
+
+ if (NAPI_GRO_CB(p)->last == p)
+ skb_shinfo(p)->frag_list = skb;
+ else
+ NAPI_GRO_CB(p)->last->next = skb;
+
+ skb_pull(skb, skb_gro_offset(skb));
+
+ NAPI_GRO_CB(p)->last = skb;
+ NAPI_GRO_CB(p)->count++;
+ p->data_len += skb->len;
+
+ /* sk ownership - if any - completely transferred to the aggregated packet */
+ skb->destructor = NULL;
+ skb->sk = NULL;
+ p->truesize += skb->truesize;
+ p->len += skb->len;
+
+ NAPI_GRO_CB(skb)->same_flow = 1;
+
+ return 0;
+}
+
static void napi_gro_complete(struct napi_struct *napi, struct sk_buff *skb)
{
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -28,6 +28,70 @@ static void tcp_gso_tstamp(struct sk_buf
}
}
+static void __tcpv4_gso_segment_csum(struct sk_buff *seg,
+ __be32 *oldip, __be32 newip,
+ __be16 *oldport, __be16 newport)
+{
+ struct tcphdr *th;
+ struct iphdr *iph;
+
+ if (*oldip == newip && *oldport == newport)
+ return;
+
+ th = tcp_hdr(seg);
+ iph = ip_hdr(seg);
+
+ inet_proto_csum_replace4(&th->check, seg, *oldip, newip, true);
+ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
+ *oldport = newport;
+
+ csum_replace4(&iph->check, *oldip, newip);
+ *oldip = newip;
+}
+
+static struct sk_buff *__tcpv4_gso_segment_list_csum(struct sk_buff *segs)
+{
+ const struct tcphdr *th;
+ const struct iphdr *iph;
+ struct sk_buff *seg;
+ struct tcphdr *th2;
+ struct iphdr *iph2;
+
+ seg = segs;
+ th = tcp_hdr(seg);
+ iph = ip_hdr(seg);
+ th2 = tcp_hdr(seg->next);
+ iph2 = ip_hdr(seg->next);
+
+ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
+ iph->daddr == iph2->daddr && iph->saddr == iph2->saddr)
+ return segs;
+
+ while ((seg = seg->next)) {
+ th2 = tcp_hdr(seg);
+ iph2 = ip_hdr(seg);
+
+ __tcpv4_gso_segment_csum(seg,
+ &iph2->saddr, iph->saddr,
+ &th2->source, th->source);
+ __tcpv4_gso_segment_csum(seg,
+ &iph2->daddr, iph->daddr,
+ &th2->dest, th->dest);
+ }
+
+ return segs;
+}
+
+static struct sk_buff *__tcp4_gso_segment_list(struct sk_buff *skb,
+ netdev_features_t features)
+{
+ skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
+ if (IS_ERR(skb))
+ return skb;
+
+ return __tcpv4_gso_segment_list_csum(skb);
+}
+
static struct sk_buff *tcp4_gso_segment(struct sk_buff *skb,
netdev_features_t features)
{
@@ -37,6 +101,9 @@ static struct sk_buff *tcp4_gso_segment(
if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
return ERR_PTR(-EINVAL);
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
+ return __tcp4_gso_segment_list(skb, features);
+
if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
const struct iphdr *iph = ip_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);
@@ -178,61 +245,76 @@ out:
return segs;
}
-struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb)
+struct sk_buff *tcp_gro_lookup(struct list_head *head, struct tcphdr *th)
{
- struct sk_buff *pp = NULL;
+ struct tcphdr *th2;
struct sk_buff *p;
+
+ list_for_each_entry(p, head, list) {
+ if (!NAPI_GRO_CB(p)->same_flow)
+ continue;
+
+ th2 = tcp_hdr(p);
+ if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
+ NAPI_GRO_CB(p)->same_flow = 0;
+ continue;
+ }
+
+ return p;
+ }
+
+ return NULL;
+}
+
+struct tcphdr *tcp_gro_pull_header(struct sk_buff *skb)
+{
+ unsigned int thlen, hlen, off;
struct tcphdr *th;
- struct tcphdr *th2;
- unsigned int len;
- unsigned int thlen;
- __be32 flags;
- unsigned int mss = 1;
- unsigned int hlen;
- unsigned int off;
- int flush = 1;
- int i;
off = skb_gro_offset(skb);
hlen = off + sizeof(*th);
th = skb_gro_header(skb, hlen, off);
if (unlikely(!th))
- goto out;
+ return NULL;
thlen = th->doff * 4;
if (thlen < sizeof(*th))
- goto out;
+ return NULL;
hlen = off + thlen;
if (skb_gro_header_hard(skb, hlen)) {
th = skb_gro_header_slow(skb, hlen, off);
if (unlikely(!th))
- goto out;
+ return NULL;
}
skb_gro_pull(skb, thlen);
- len = skb_gro_len(skb);
- flags = tcp_flag_word(th);
-
- list_for_each_entry(p, head, list) {
- if (!NAPI_GRO_CB(p)->same_flow)
- continue;
+ return th;
+}
- th2 = tcp_hdr(p);
+struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th)
+{
+ unsigned int thlen = th->doff * 4;
+ struct sk_buff *pp = NULL;
+ struct sk_buff *p;
+ struct tcphdr *th2;
+ unsigned int len;
+ __be32 flags;
+ unsigned int mss = 1;
+ int flush = 1;
+ int i;
- if (*(u32 *)&th->source ^ *(u32 *)&th2->source) {
- NAPI_GRO_CB(p)->same_flow = 0;
- continue;
- }
+ len = skb_gro_len(skb);
+ flags = tcp_flag_word(th);
- goto found;
- }
- p = NULL;
- goto out_check_final;
+ p = tcp_gro_lookup(head, th);
+ if (!p)
+ goto out_check_final;
-found:
/* Include the IP ID check below from the inner most IP hdr */
+ th2 = tcp_hdr(p);
flush = NAPI_GRO_CB(p)->flush;
flush |= (__force int)(flags & TCP_FLAG_CWR);
flush |= (__force int)((flags ^ tcp_flag_word(th2)) &
@@ -269,6 +351,19 @@ found:
flush |= p->decrypted ^ skb->decrypted;
#endif
+ if (unlikely(NAPI_GRO_CB(p)->is_flist)) {
+ flush |= (__force int)(flags ^ tcp_flag_word(th2));
+ flush |= skb->ip_summed != p->ip_summed;
+ flush |= skb->csum_level != p->csum_level;
+ flush |= !pskb_may_pull(skb, skb_gro_offset(skb));
+ flush |= NAPI_GRO_CB(p)->count >= 64;
+
+ if (flush || skb_gro_receive_list(p, skb))
+ mss = 1;
+
+ goto out_check_final;
+ }
+
if (flush || skb_gro_receive(p, skb)) {
mss = 1;
goto out_check_final;
@@ -290,7 +385,6 @@ out_check_final:
if (p && (!NAPI_GRO_CB(skb)->same_flow || flush))
pp = p;
-out:
NAPI_GRO_CB(skb)->flush |= (flush != 0);
return pp;
@@ -314,18 +408,58 @@ void tcp_gro_complete(struct sk_buff *sk
}
EXPORT_SYMBOL(tcp_gro_complete);
+static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th)
+{
+ const struct iphdr *iph;
+ struct sk_buff *p;
+ struct sock *sk;
+ struct net *net;
+ int iif, sdif;
+
+ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST))
+ return;
+
+ p = tcp_gro_lookup(head, th);
+ if (p) {
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ return;
+ }
+
+ inet_get_iif_sdif(skb, &iif, &sdif);
+ iph = skb_gro_network_header(skb);
+ net = dev_net(skb->dev);
+ sk = __inet_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
+ iph->saddr, th->source,
+ iph->daddr, ntohs(th->dest),
+ iif, sdif);
+ NAPI_GRO_CB(skb)->is_flist = !sk;
+ if (sk)
+ sock_put(sk);
+}
+
INDIRECT_CALLABLE_SCOPE
struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
{
+ struct tcphdr *th;
+
/* Don't bother verifying checksum if we're going to flush anyway. */
if (!NAPI_GRO_CB(skb)->flush &&
skb_gro_checksum_validate(skb, IPPROTO_TCP,
- inet_gro_compute_pseudo)) {
- NAPI_GRO_CB(skb)->flush = 1;
- return NULL;
- }
+ inet_gro_compute_pseudo))
+ goto flush;
+
+ th = tcp_gro_pull_header(skb);
+ if (!th)
+ goto flush;
- return tcp_gro_receive(head, skb);
+ tcp4_check_fraglist_gro(head, skb, th);
+
+ return tcp_gro_receive(head, skb, th);
+
+flush:
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
}
INDIRECT_CALLABLE_SCOPE int tcp4_gro_complete(struct sk_buff *skb, int thoff)
@@ -333,6 +467,15 @@ INDIRECT_CALLABLE_SCOPE int tcp4_gro_com
const struct iphdr *iph = ip_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);
+ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
+ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV4;
+ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
+
+ __skb_incr_checksum_unnecessary(skb);
+
+ return 0;
+ }
+
th->check = ~tcp_v4_check(skb->len - thoff, iph->saddr,
iph->daddr, 0);
skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV4;
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -433,33 +433,6 @@ out:
return segs;
}
-static int skb_gro_receive_list(struct sk_buff *p, struct sk_buff *skb)
-{
- if (unlikely(p->len + skb->len >= 65536))
- return -E2BIG;
-
- if (NAPI_GRO_CB(p)->last == p)
- skb_shinfo(p)->frag_list = skb;
- else
- NAPI_GRO_CB(p)->last->next = skb;
-
- skb_pull(skb, skb_gro_offset(skb));
-
- NAPI_GRO_CB(p)->last = skb;
- NAPI_GRO_CB(p)->count++;
- p->data_len += skb->len;
-
- /* sk ownership - if any - completely transferred to the aggregated packet */
- skb->destructor = NULL;
- skb->sk = NULL;
- p->truesize += skb->truesize;
- p->len += skb->len;
-
- NAPI_GRO_CB(skb)->same_flow = 1;
-
- return 0;
-}
-
#define UDP_GRO_CNT_MAX 64
static struct sk_buff *udp_gro_receive_segment(struct list_head *head,
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -7,24 +7,67 @@
*/
#include <linux/indirect_call_wrapper.h>
#include <linux/skbuff.h>
+#include <net/inet6_hashtables.h>
#include <net/gro.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/ip6_checksum.h>
#include "ip6_offload.h"
+static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
+ struct tcphdr *th)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+ const struct ipv6hdr *hdr;
+ struct sk_buff *p;
+ struct sock *sk;
+ struct net *net;
+ int iif, sdif;
+
+ if (!(skb->dev->features & NETIF_F_GRO_FRAGLIST))
+ return;
+
+ p = tcp_gro_lookup(head, th);
+ if (p) {
+ NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
+ return;
+ }
+
+ inet6_get_iif_sdif(skb, &iif, &sdif);
+ hdr = skb_gro_network_header(skb);
+ net = dev_net(skb->dev);
+ sk = __inet6_lookup_established(net, net->ipv4.tcp_death_row.hashinfo,
+ &hdr->saddr, th->source,
+ &hdr->daddr, ntohs(th->dest),
+ iif, sdif);
+ NAPI_GRO_CB(skb)->is_flist = !sk;
+ if (sk)
+ sock_put(sk);
+#endif /* IS_ENABLED(CONFIG_IPV6) */
+}
+
INDIRECT_CALLABLE_SCOPE
struct sk_buff *tcp6_gro_receive(struct list_head *head, struct sk_buff *skb)
{
+ struct tcphdr *th;
+
/* Don't bother verifying checksum if we're going to flush anyway. */
if (!NAPI_GRO_CB(skb)->flush &&
skb_gro_checksum_validate(skb, IPPROTO_TCP,
- ip6_gro_compute_pseudo)) {
- NAPI_GRO_CB(skb)->flush = 1;
- return NULL;
- }
+ ip6_gro_compute_pseudo))
+ goto flush;
- return tcp_gro_receive(head, skb);
+ th = tcp_gro_pull_header(skb);
+ if (!th)
+ goto flush;
+
+ tcp6_check_fraglist_gro(head, skb, th);
+
+ return tcp_gro_receive(head, skb, th);
+
+flush:
+ NAPI_GRO_CB(skb)->flush = 1;
+ return NULL;
}
INDIRECT_CALLABLE_SCOPE int tcp6_gro_complete(struct sk_buff *skb, int thoff)
@@ -32,6 +75,15 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
const struct ipv6hdr *iph = ipv6_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);
+ if (unlikely(NAPI_GRO_CB(skb)->is_flist)) {
+ skb_shinfo(skb)->gso_type |= SKB_GSO_FRAGLIST | SKB_GSO_TCPV6;
+ skb_shinfo(skb)->gso_segs = NAPI_GRO_CB(skb)->count;
+
+ __skb_incr_checksum_unnecessary(skb);
+
+ return 0;
+ }
+
th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
&iph->daddr, 0);
skb_shinfo(skb)->gso_type |= SKB_GSO_TCPV6;
@@ -40,6 +92,61 @@ INDIRECT_CALLABLE_SCOPE int tcp6_gro_com
return 0;
}
+static void __tcpv6_gso_segment_csum(struct sk_buff *seg,
+ __be16 *oldport, __be16 newport)
+{
+ struct tcphdr *th;
+
+ if (*oldport == newport)
+ return;
+
+ th = tcp_hdr(seg);
+ inet_proto_csum_replace2(&th->check, seg, *oldport, newport, false);
+ *oldport = newport;
+}
+
+static struct sk_buff *__tcpv6_gso_segment_list_csum(struct sk_buff *segs)
+{
+ const struct tcphdr *th;
+ const struct ipv6hdr *iph;
+ struct sk_buff *seg;
+ struct tcphdr *th2;
+ struct ipv6hdr *iph2;
+
+ seg = segs;
+ th = tcp_hdr(seg);
+ iph = ipv6_hdr(seg);
+ th2 = tcp_hdr(seg->next);
+ iph2 = ipv6_hdr(seg->next);
+
+ if (!(*(const u32 *)&th->source ^ *(const u32 *)&th2->source) &&
+ ipv6_addr_equal(&iph->saddr, &iph2->saddr) &&
+ ipv6_addr_equal(&iph->daddr, &iph2->daddr))
+ return segs;
+
+ while ((seg = seg->next)) {
+ th2 = tcp_hdr(seg);
+ iph2 = ipv6_hdr(seg);
+
+ iph2->saddr = iph->saddr;
+ iph2->daddr = iph->daddr;
+ __tcpv6_gso_segment_csum(seg, &th2->source, th->source);
+ __tcpv6_gso_segment_csum(seg, &th2->dest, th->dest);
+ }
+
+ return segs;
+}
+
+static struct sk_buff *__tcp6_gso_segment_list(struct sk_buff *skb,
+ netdev_features_t features)
+{
+ skb = skb_segment_list(skb, features, skb_mac_header_len(skb));
+ if (IS_ERR(skb))
+ return skb;
+
+ return __tcpv6_gso_segment_list_csum(skb);
+}
+
static struct sk_buff *tcp6_gso_segment(struct sk_buff *skb,
netdev_features_t features)
{
@@ -51,6 +158,9 @@ static struct sk_buff *tcp6_gso_segment(
if (!pskb_may_pull(skb, sizeof(*th)))
return ERR_PTR(-EINVAL);
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST)
+ return __tcp6_gso_segment_list(skb, features);
+
if (unlikely(skb->ip_summed != CHECKSUM_PARTIAL)) {
const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);

View file

@ -1,21 +0,0 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Mon, 21 Mar 2022 20:39:59 +0100
Subject: [PATCH] net: ethernet: mtk_eth_soc: enable threaded NAPI
This can improve performance under load by ensuring that NAPI processing is
not pinned on CPU 0.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -4994,6 +4994,8 @@ static int mtk_probe(struct platform_dev
* for NAPI to work
*/
init_dummy_netdev(&eth->dummy_dev);
+ eth->dummy_dev.threaded = 1;
+ strcpy(eth->dummy_dev.name, "mtk_eth");
netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx);
netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx);

View file

@ -1,24 +0,0 @@
From: Felix Fietkau <nbd@nbd.name>
Date: Wed, 14 Feb 2024 15:24:41 +0100
Subject: [PATCH] netfilter: nf_tables: fix bidirectional offload regression
Commit 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
made unidirectional flow offload possible, while completely ignoring (and
breaking) bidirectional flow offload for nftables.
Add the missing flag that was left out as an exercise for the reader :)
Cc: Vlad Buslov <vladbu@nvidia.com>
Fixes: 8f84780b84d6 ("netfilter: flowtable: allow unidirectional rules")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
--- a/net/netfilter/nft_flow_offload.c
+++ b/net/netfilter/nft_flow_offload.c
@@ -361,6 +361,7 @@ static void nft_flow_offload_eval(const
ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
}
+ __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
ret = flow_offload_add(flowtable, flow);
if (ret < 0)
goto err_flow_add;

View file

@ -1,158 +0,0 @@
From b2d6ebf2f92f8695c83fa6979f4ab579c588df76 Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Tue, 20 Jun 2023 07:57:38 +0200
Subject: [PATCH 4/4] net: dsa: qca8k: add support for port_change_master
Add support for port_change_master to permit assigning an alternative
CPU port if the switch have both CPU port connected or create a LAG on
both CPU port and assign the LAG as DSA master.
On port change master request, we check if the master is a LAG.
With LAG we compose the cpu_port_mask with the CPU port in the LAG, if
master is a simple dsa_port, we derive the index.
Finally we apply the new cpu_port_mask to the LOOKUP MEMBER to permit
the port to receive packet by the new CPU port setup for the port and we
refresh the CPU ports LOOKUP MEMBER configuration to reflect the new
user port state.
port_lag_join/leave is updated to refresh the user ports if we detect
that the LAG is a DSA master and we have user port using it as a master.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/dsa/qca/qca8k-8xxx.c | 116 ++++++++++++++++++++++++++++++-
1 file changed, 114 insertions(+), 2 deletions(-)
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -1738,6 +1738,117 @@ qca8k_get_tag_protocol(struct dsa_switch
return DSA_TAG_PROTO_QCA;
}
+static int qca8k_port_change_master(struct dsa_switch *ds, int port,
+ struct net_device *master,
+ struct netlink_ext_ack *extack)
+{
+ struct dsa_switch_tree *dst = ds->dst;
+ struct qca8k_priv *priv = ds->priv;
+ u8 cpu_port_mask = 0;
+ struct dsa_port *dp;
+ u32 val;
+ int ret;
+
+ /* With LAG of CPU port, compose the mask for port LOOKUP MEMBER */
+ if (netif_is_lag_master(master)) {
+ struct dsa_lag *lag;
+ int id;
+
+ id = dsa_lag_id(dst, master);
+ lag = dsa_lag_by_id(dst, id);
+
+ dsa_lag_foreach_port(dp, dst, lag)
+ if (dsa_port_is_cpu(dp))
+ cpu_port_mask |= BIT(dp->index);
+ } else {
+ dp = master->dsa_ptr;
+ cpu_port_mask |= BIT(dp->index);
+ }
+
+ /* Connect port to new cpu port */
+ ret = regmap_read(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), &val);
+ if (ret)
+ return ret;
+
+ /* Reset connected CPU port in port LOOKUP MEMBER */
+ val &= ~dsa_cpu_ports(ds);
+ /* Assign the new CPU port in port LOOKUP MEMBER */
+ val |= cpu_port_mask;
+
+ ret = regmap_update_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port),
+ QCA8K_PORT_LOOKUP_MEMBER,
+ val);
+ if (ret)
+ return ret;
+
+ /* Refresh CPU port LOOKUP MEMBER with new port */
+ dsa_tree_for_each_cpu_port(dp, ds->dst) {
+ u32 reg = QCA8K_PORT_LOOKUP_CTRL(dp->index);
+
+ /* If CPU port in mask assign port, else remove port */
+ if (BIT(dp->index) & cpu_port_mask)
+ ret = regmap_set_bits(priv->regmap, reg, BIT(port));
+ else
+ ret = regmap_clear_bits(priv->regmap, reg, BIT(port));
+
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qca8k_port_lag_refresh_user_ports(struct dsa_switch *ds,
+ struct dsa_lag lag)
+{
+ struct net_device *lag_dev = lag.dev;
+ struct dsa_port *dp;
+ int ret;
+
+ /* Ignore if LAG is not a DSA master */
+ if (!netif_is_lag_master(lag_dev))
+ return 0;
+
+ dsa_switch_for_each_user_port(dp, ds) {
+ /* Skip if assigned master is not the LAG */
+ if (dsa_port_to_master(dp) != lag_dev)
+ continue;
+
+ ret = qca8k_port_change_master(ds, dp->index,
+ lag_dev, NULL);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int qca8xxx_port_lag_join(struct dsa_switch *ds, int port,
+ struct dsa_lag lag,
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
+{
+ int ret;
+
+ ret = qca8k_port_lag_join(ds, port, lag, info, extack);
+ if (ret)
+ return ret;
+
+ return qca8k_port_lag_refresh_user_ports(ds, lag);
+}
+
+static int qca8xxx_port_lag_leave(struct dsa_switch *ds, int port,
+ struct dsa_lag lag)
+{
+ int ret;
+
+ ret = qca8k_port_lag_leave(ds, port, lag);
+ if (ret)
+ return ret;
+
+ return qca8k_port_lag_refresh_user_ports(ds, lag);
+}
+
static void
qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
bool operational)
@@ -2024,8 +2135,9 @@ static const struct dsa_switch_ops qca8k
.phylink_mac_link_down = qca8k_phylink_mac_link_down,
.phylink_mac_link_up = qca8k_phylink_mac_link_up,
.get_phy_flags = qca8k_get_phy_flags,
- .port_lag_join = qca8k_port_lag_join,
- .port_lag_leave = qca8k_port_lag_leave,
+ .port_lag_join = qca8xxx_port_lag_join,
+ .port_lag_leave = qca8xxx_port_lag_leave,
+ .port_change_master = qca8k_port_change_master,
.master_state_change = qca8k_master_change,
.connect_tag_protocol = qca8k_connect_tag_protocol,
};

View file

@ -1,61 +0,0 @@
From 312753d0aadba0f58841ae513b80fdbabc887523 Mon Sep 17 00:00:00 2001
From: Chukun Pan <amadeus@jmu.edu.cn>
Date: Wed, 8 Feb 2023 16:32:18 +0800
Subject: [PATCH] net: phy: realtek: support switching between SGMII and
2500BASE-X for RTL822x series
After commit ace6aba ("net: phy: realtek: rtl8221: allow to configure
SERDES mode"), the rtl8221 phy can work in SGMII and 2500base-x modes
respectively. So add interface automatic switching for rtl8221 phy to
match various wire speeds.
Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
---
drivers/net/phy/realtek.c | 26 ++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -714,6 +714,25 @@ static int rtl822x_config_aneg(struct ph
return __genphy_config_aneg(phydev, ret);
}
+static void rtl822x_update_interface(struct phy_device *phydev)
+{
+ /* Automatically switch SERDES interface between
+ * SGMII and 2500-BaseX according to speed.
+ */
+ switch (phydev->speed) {
+ case SPEED_2500:
+ phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+ break;
+ case SPEED_1000:
+ case SPEED_100:
+ case SPEED_10:
+ phydev->interface = PHY_INTERFACE_MODE_SGMII;
+ break;
+ default:
+ break;
+ }
+}
+
static int rtl822x_read_status(struct phy_device *phydev)
{
int ret;
@@ -732,11 +751,14 @@ static int rtl822x_read_status(struct ph
phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL);
}
- ret = genphy_read_status(phydev);
+ ret = rtlgen_read_status(phydev);
if (ret < 0)
return ret;
- return rtlgen_get_speed(phydev);
+ if (phydev->is_c45 && phydev->link)
+ rtl822x_update_interface(phydev);
+
+ return 0;
}
static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)

View file

@ -1,60 +0,0 @@
From 92c8b9d558160d94b981dd8a2b9c47657627ffdc Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sat, 22 Apr 2023 01:23:08 +0100
Subject: [PATCH 2/3] net: phy: realtek: use inline functions for 10GbE
advertisement
Use existing generic inline functions to encode local advertisement
of 10GbE link modes as well as to decode link-partner advertisement.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/phy/realtek.c | 22 +++++-----------------
1 file changed, 5 insertions(+), 17 deletions(-)
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -69,10 +69,6 @@
#define RTL_SUPPORTS_5000FULL BIT(14)
#define RTL_SUPPORTS_2500FULL BIT(13)
#define RTL_SUPPORTS_10000FULL BIT(0)
-#define RTL_ADV_2500FULL BIT(7)
-#define RTL_LPADV_10000FULL BIT(11)
-#define RTL_LPADV_5000FULL BIT(6)
-#define RTL_LPADV_2500FULL BIT(5)
#define RTL9000A_GINMR 0x14
#define RTL9000A_GINMR_LINK_STATUS BIT(4)
@@ -699,14 +695,11 @@ static int rtl822x_config_aneg(struct ph
int ret = 0;
if (phydev->autoneg == AUTONEG_ENABLE) {
- u16 adv2500 = 0;
-
- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
- phydev->advertising))
- adv2500 = RTL_ADV_2500FULL;
-
ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
- RTL_ADV_2500FULL, adv2500);
+ MDIO_AN_10GBT_CTRL_ADV10G |
+ MDIO_AN_10GBT_CTRL_ADV5G |
+ MDIO_AN_10GBT_CTRL_ADV2_5G,
+ linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising));
if (ret < 0)
return ret;
}
@@ -743,12 +736,7 @@ static int rtl822x_read_status(struct ph
if (lpadv < 0)
return lpadv;
- linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
- phydev->lp_advertising, lpadv & RTL_LPADV_10000FULL);
- linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
- phydev->lp_advertising, lpadv & RTL_LPADV_5000FULL);
- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
- phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL);
+ mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv);
}
ret = rtlgen_read_status(phydev);

View file

@ -1,28 +0,0 @@
From 929bb4d3cfbc7878326c0771a01a636d49c54b40 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sat, 22 Apr 2023 01:25:39 +0100
Subject: [PATCH 3/3] net: phy: realtek: check validity of 10GbE link-partner
advertisement
Only use link-partner advertisement bits for 10GbE modes if they are
actually valid. Check LOCALOK and REMOTEOK bits and clear 10GbE modes
unless both of them are set.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/phy/realtek.c | 4 ++++
1 file changed, 4 insertions(+)
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -736,6 +736,10 @@ static int rtl822x_read_status(struct ph
if (lpadv < 0)
return lpadv;
+ if (!(lpadv & MDIO_AN_10GBT_STAT_REMOK) ||
+ !(lpadv & MDIO_AN_10GBT_STAT_LOCOK))
+ lpadv = 0;
+
mii_10gbt_stat_mod_linkmode_lpa_t(phydev->lp_advertising, lpadv);
}

View file

@ -1,84 +0,0 @@
From 9155098547fb1172d4fa536f3f6bc9d42f59d08c Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sat, 22 Apr 2023 03:26:01 +0100
Subject: [PATCH] net: phy: realtek: setup ALDPS on RTL822x
Setup Link Down Power Saving Mode according the DTS property
just like for RTL821x 1GE PHYs.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/phy/realtek.c | 11 +++++++++++
1 file changed, 11 insertions(+)
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -63,6 +63,10 @@
#define RTL8221B_SERDES_OPTION_MODE_2500BASEX 2
#define RTL8221B_SERDES_OPTION_MODE_HISGMII 3
+#define RTL8221B_PHYCR1 0xa430
+#define RTL8221B_PHYCR1_ALDPS_EN BIT(2)
+#define RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN BIT(12)
+
#define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12)
@@ -778,6 +782,25 @@ static int rtl8226_match_phy_device(stru
rtlgen_supports_2_5gbps(phydev);
}
+static int rtl822x_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ int val;
+
+ val = phy_read_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_PHYCR1);
+ if (val < 0)
+ return val;
+
+ if (of_property_read_bool(dev->of_node, "realtek,aldps-enable"))
+ val |= RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN;
+ else
+ val &= ~(RTL8221B_PHYCR1_ALDPS_EN | RTL8221B_PHYCR1_ALDPS_XTAL_OFF_EN);
+
+ phy_write_mmd(phydev, RTL8221B_MMD_SERDES_CTRL, RTL8221B_PHYCR1, val);
+
+ return 0;
+}
+
static int rtlgen_resume(struct phy_device *phydev)
{
int ret = genphy_resume(phydev);
@@ -1091,6 +1114,7 @@ static struct phy_driver realtek_drvs[]
.name = "RTL8226-CG 2.5Gbps PHY",
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
+ .probe = rtl822x_probe,
.read_status = rtl822x_read_status,
.suspend = genphy_suspend,
.resume = rtlgen_resume,
@@ -1102,6 +1126,7 @@ static struct phy_driver realtek_drvs[]
.name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
+ .probe = rtl822x_probe,
.read_status = rtl822x_read_status,
.suspend = genphy_suspend,
.resume = rtlgen_resume,
@@ -1114,6 +1139,7 @@ static struct phy_driver realtek_drvs[]
.get_features = rtl822x_get_features,
.config_init = rtl8221b_config_init,
.config_aneg = rtl822x_config_aneg,
+ .probe = rtl822x_probe,
.read_status = rtl822x_read_status,
.suspend = genphy_suspend,
.resume = rtlgen_resume,
@@ -1126,6 +1152,7 @@ static struct phy_driver realtek_drvs[]
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
.config_init = rtl8221b_config_init,
+ .probe = rtl822x_probe,
.read_status = rtl822x_read_status,
.suspend = genphy_suspend,
.resume = rtlgen_resume,

View file

@ -1,71 +0,0 @@
From 0de82310d2b32e78ff79d42c08b1122a6ede3778 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Sun, 30 Apr 2023 00:15:41 +0100
Subject: [PATCH] net: phy: realtek: detect early version of RTL8221B
Early versions (?) of the RTL8221B PHY cannot be identified in a regular
Clause-45 bus scan as the PHY doesn't report the implemented MMDs
correctly but returns 0 instead.
Implement custom identify function using the PKGID instead of iterating
over the implemented MMDs.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -81,6 +81,7 @@
#define RTL_GENERIC_PHYID 0x001cc800
#define RTL_8211FVD_PHYID 0x001cc878
+#define RTL_8221B_VB_CG_PHYID 0x001cc849
MODULE_DESCRIPTION("Realtek PHY driver");
MODULE_AUTHOR("Johnson Leung");
@@ -782,6 +783,38 @@ static int rtl8226_match_phy_device(stru
rtlgen_supports_2_5gbps(phydev);
}
+static int rtl8221b_vb_cg_match_phy_device(struct phy_device *phydev)
+{
+ int val;
+ u32 id;
+
+ if (phydev->mdio.bus->read_c45) {
+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID1);
+ if (val < 0)
+ return 0;
+
+ id = val << 16;
+ val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PKGID2);
+ if (val < 0)
+ return 0;
+
+ id |= val;
+ } else {
+ val = phy_read(phydev, MII_PHYSID1);
+ if (val < 0)
+ return 0;
+
+ id = val << 16;
+ val = phy_read(phydev, MII_PHYSID2);
+ if (val < 0)
+ return 0;
+
+ id |= val;
+ }
+
+ return (id == RTL_8221B_VB_CG_PHYID);
+}
+
static int rtl822x_probe(struct phy_device *phydev)
{
struct device *dev = &phydev->mdio.dev;
@@ -1134,7 +1167,7 @@ static struct phy_driver realtek_drvs[]
.write_page = rtl821x_write_page,
.soft_reset = genphy_soft_reset,
}, {
- PHY_ID_MATCH_EXACT(0x001cc849),
+ .match_phy_device = rtl8221b_vb_cg_match_phy_device,
.name = "RTL8221B-VB-CG 2.5Gbps PHY",
.get_features = rtl822x_get_features,
.config_init = rtl8221b_config_init,

View file

@ -1,498 +0,0 @@
From patchwork Thu Feb 1 21:53:06 2024
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Daniel Golle <daniel@makrotopia.org>
X-Patchwork-Id: 13541843
Date: Thu, 1 Feb 2024 21:53:06 +0000
From: Daniel Golle <daniel@makrotopia.org>
To: Bc-bocun Chen <bc-bocun.chen@mediatek.com>,
Chunfeng Yun <chunfeng.yun@mediatek.com>,
Vinod Koul <vkoul@kernel.org>,
Kishon Vijay Abraham I <kishon@kernel.org>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
Conor Dooley <conor+dt@kernel.org>,
Daniel Golle <daniel@makrotopia.org>,
Qingfang Deng <dqfext@gmail.com>,
SkyLake Huang <SkyLake.Huang@mediatek.com>,
Matthias Brugger <matthias.bgg@gmail.com>,
AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>,
Philipp Zabel <p.zabel@pengutronix.de>,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-phy@lists.infradead.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
netdev@vger.kernel.org
Subject: [PATCH 2/2] phy: add driver for MediaTek XFI T-PHY
Message-ID:
<dd6b40ea1f7f8459a9a2cfe7fa60c1108332ade6.1706823233.git.daniel@makrotopia.org>
References:
<702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org>
MIME-Version: 1.0
Content-Disposition: inline
In-Reply-To:
<702afb0c1246d95c90b22e57105304028bdd3083.1706823233.git.daniel@makrotopia.org>
List-Id: Linux Phy Mailing list <linux-phy.lists.infradead.org>
Add driver for MediaTek's XFI T-PHY, 10 Gigabit/s Ethernet SerDes PHY
which can be found in the MT7988 SoC.
The PHY can operates only in PHY_MODE_ETHERNET, the submode is one of
PHY_INTERFACE_MODE_* corresponding to the supported modes:
* USXGMII \
* 10GBase-R }- USXGMII PCS - XGDM \
* 5GBase-R / \
}- Ethernet MAC
* 2500Base-X \ /
* 1000Base-X }- LynxI PCS - GDM /
* Cisco SGMII (MAC side) /
In order to work-around a performance issue present on the first of
two XFI T-PHYs present in MT7988, special tuning is applied which can be
selected by adding the 'mediatek,usxgmii-performance-errata' property to
the device tree node.
There is no documentation for most registers used for the
analog/tuning part, however, most of the registers have been partially
reverse-engineered from MediaTek's SDK implementation (an opaque
sequence of 32-bit register writes) and descriptions for all relevant
digital registers and bits such as resets and muxes have been supplied
by MediaTek.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
MAINTAINERS | 1 +
drivers/phy/mediatek/Kconfig | 12 +
drivers/phy/mediatek/Makefile | 1 +
drivers/phy/mediatek/phy-mtk-xfi-tphy.c | 392 ++++++++++++++++++++++++
4 files changed, 406 insertions(+)
create mode 100644 drivers/phy/mediatek/phy-mtk-xfi-tphy.c
--- a/drivers/phy/mediatek/Kconfig
+++ b/drivers/phy/mediatek/Kconfig
@@ -13,6 +13,18 @@ config PHY_MTK_PCIE
callback for PCIe GEN3 port, it supports software efuse
initialization.
+config PHY_MTK_XFI_TPHY
+ tristate "MediaTek XFI T-PHY Driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on OF && OF_ADDRESS
+ depends on HAS_IOMEM
+ select GENERIC_PHY
+ help
+ Say 'Y' here to add support for MediaTek XFI T-PHY driver.
+ The driver provides access to the Ethernet SerDes T-PHY supporting
+ 1GE and 2.5GE modes via the LynxI PCS, and 5GE and 10GE modes
+ via the USXGMII PCS found in MediaTek SoCs with 10G Ethernet.
+
config PHY_MTK_TPHY
tristate "MediaTek T-PHY Driver"
depends on ARCH_MEDIATEK || COMPILE_TEST
--- a/drivers/phy/mediatek/Makefile
+++ b/drivers/phy/mediatek/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_MTK_PCIE) += phy-mtk-p
obj-$(CONFIG_PHY_MTK_TPHY) += phy-mtk-tphy.o
obj-$(CONFIG_PHY_MTK_UFS) += phy-mtk-ufs.o
obj-$(CONFIG_PHY_MTK_XSPHY) += phy-mtk-xsphy.o
+obj-$(CONFIG_PHY_MTK_XFI_TPHY) += phy-mtk-xfi-tphy.o
phy-mtk-hdmi-drv-y := phy-mtk-hdmi.o
phy-mtk-hdmi-drv-y += phy-mtk-hdmi-mt2701.o
--- /dev/null
+++ b/drivers/phy/mediatek/phy-mtk-xfi-tphy.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* MediaTek 10GE SerDes PHY driver
+ *
+ * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
+ * Bc-bocun Chen <bc-bocun.chen@mediatek.com>
+ * based on mtk_usxgmii.c found in MediaTek's SDK released under GPL-2.0
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Henry Yen <henry.yen@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/phy.h>
+#include <linux/phy/phy.h>
+
+#define MTK_XFI_TPHY_NUM_CLOCKS 2
+
+#define REG_DIG_GLB_70 0x0070
+#define XTP_PCS_RX_EQ_IN_PROGRESS(x) FIELD_PREP(GENMASK(25, 24), (x))
+#define XTP_PCS_MODE_MASK GENMASK(17, 16)
+#define XTP_PCS_MODE(x) FIELD_PREP(GENMASK(17, 16), (x))
+#define XTP_PCS_RST_B BIT(15)
+#define XTP_FRC_PCS_RST_B BIT(14)
+#define XTP_PCS_PWD_SYNC_MASK GENMASK(13, 12)
+#define XTP_PCS_PWD_SYNC(x) FIELD_PREP(XTP_PCS_PWD_SYNC_MASK, (x))
+#define XTP_PCS_PWD_ASYNC_MASK GENMASK(11, 10)
+#define XTP_PCS_PWD_ASYNC(x) FIELD_PREP(XTP_PCS_PWD_ASYNC_MASK, (x))
+#define XTP_FRC_PCS_PWD_ASYNC BIT(8)
+#define XTP_PCS_UPDT BIT(4)
+#define XTP_PCS_IN_FR_RG BIT(0)
+
+#define REG_DIG_GLB_F4 0x00f4
+#define XFI_DPHY_PCS_SEL BIT(0)
+#define XFI_DPHY_PCS_SEL_SGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 1)
+#define XFI_DPHY_PCS_SEL_USXGMII FIELD_PREP(XFI_DPHY_PCS_SEL, 0)
+#define XFI_DPHY_AD_SGDT_FRC_EN BIT(5)
+
+#define REG_DIG_LN_TRX_40 0x3040
+#define XTP_LN_FRC_TX_DATA_EN BIT(29)
+#define XTP_LN_TX_DATA_EN BIT(28)
+
+#define REG_DIG_LN_TRX_B0 0x30b0
+#define XTP_LN_FRC_TX_MACCK_EN BIT(5)
+#define XTP_LN_TX_MACCK_EN BIT(4)
+
+#define REG_ANA_GLB_D0 0x90d0
+#define XTP_GLB_USXGMII_SEL_MASK GENMASK(3, 1)
+#define XTP_GLB_USXGMII_SEL(x) FIELD_PREP(GENMASK(3, 1), (x))
+#define XTP_GLB_USXGMII_EN BIT(0)
+
+struct mtk_xfi_tphy {
+ void __iomem *base;
+ struct device *dev;
+ struct reset_control *reset;
+ struct clk_bulk_data clocks[MTK_XFI_TPHY_NUM_CLOCKS];
+ bool da_war;
+};
+
+static void mtk_xfi_tphy_write(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
+ u32 value)
+{
+ iowrite32(value, xfi_tphy->base + reg);
+}
+
+static void mtk_xfi_tphy_rmw(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
+ u32 clr, u32 set)
+{
+ u32 val;
+
+ val = ioread32(xfi_tphy->base + reg);
+ val &= ~clr;
+ val |= set;
+ iowrite32(val, xfi_tphy->base + reg);
+}
+
+static void mtk_xfi_tphy_set(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
+ u32 set)
+{
+ mtk_xfi_tphy_rmw(xfi_tphy, reg, 0, set);
+}
+
+static void mtk_xfi_tphy_clear(struct mtk_xfi_tphy *xfi_tphy, u16 reg,
+ u32 clr)
+{
+ mtk_xfi_tphy_rmw(xfi_tphy, reg, clr, 0);
+}
+
+static void mtk_xfi_tphy_setup(struct mtk_xfi_tphy *xfi_tphy,
+ phy_interface_t interface)
+{
+ bool is_2p5g = (interface == PHY_INTERFACE_MODE_2500BASEX);
+ bool is_1g = (interface == PHY_INTERFACE_MODE_1000BASEX ||
+ interface == PHY_INTERFACE_MODE_SGMII);
+ bool is_10g = (interface == PHY_INTERFACE_MODE_10GBASER ||
+ interface == PHY_INTERFACE_MODE_USXGMII);
+ bool is_5g = (interface == PHY_INTERFACE_MODE_5GBASER);
+ bool is_xgmii = (is_10g || is_5g);
+
+ dev_dbg(xfi_tphy->dev, "setting up for mode %s\n", phy_modes(interface));
+
+ /* Setup PLL setting */
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x9024, 0x100000, is_10g ? 0x0 : 0x100000);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x2020, 0x202000, is_5g ? 0x202000 : 0x0);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x2030, 0x500, is_1g ? 0x0 : 0x500);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x2034, 0xa00, is_1g ? 0x0 : 0xa00);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x2040, 0x340000, is_1g ? 0x200000 :
+ 0x140000);
+
+ /* Setup RXFE BW setting */
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50f0, 0xc10, is_1g ? 0x410 :
+ is_5g ? 0x800 : 0x400);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e0, 0x4000, is_5g ? 0x0 : 0x4000);
+
+ /* Setup RX CDR setting */
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x506c, 0x30000, is_5g ? 0x0 : 0x30000);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5070, 0x670000, is_5g ? 0x620000 : 0x50000);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5074, 0x180000, is_5g ? 0x180000 : 0x0);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5078, 0xf000400, is_5g ? 0x8000000 :
+ 0x7000400);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x507c, 0x5000500, is_5g ? 0x4000400 :
+ 0x1000100);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5080, 0x1410, is_1g ? 0x400 :
+ is_5g ? 0x1010 : 0x0);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5084, 0x30300, is_1g ? 0x30300 :
+ is_5g ? 0x30100 :
+ 0x100);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x5088, 0x60200, is_1g ? 0x20200 :
+ is_5g ? 0x40000 :
+ 0x20000);
+
+ /* Setting RXFE adaptation range setting */
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e4, 0xc0000, is_5g ? 0x0 : 0xc0000);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50e8, 0x40000, is_5g ? 0x0 : 0x40000);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50ec, 0xa00, is_1g ? 0x200 : 0x800);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x50a8, 0xee0000, is_5g ? 0x800000 :
+ 0x6e0000);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x6004, 0x190000, is_5g ? 0x0 : 0x190000);
+ if (is_10g)
+ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x01423342);
+ else if (is_5g)
+ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00a132a1);
+ else if (is_2p5g)
+ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x009c329c);
+ else
+ mtk_xfi_tphy_write(xfi_tphy, 0x00f8, 0x00fa32fa);
+
+ /* Force SGDT_OUT off and select PCS */
+ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_F4,
+ XFI_DPHY_AD_SGDT_FRC_EN | XFI_DPHY_PCS_SEL,
+ XFI_DPHY_AD_SGDT_FRC_EN |
+ (is_xgmii ? XFI_DPHY_PCS_SEL_USXGMII :
+ XFI_DPHY_PCS_SEL_SGMII));
+
+
+ /* Force GLB_CKDET_OUT */
+ mtk_xfi_tphy_set(xfi_tphy, 0x0030, 0xc00);
+
+ /* Force AEQ on */
+ mtk_xfi_tphy_write(xfi_tphy, REG_DIG_GLB_70,
+ XTP_PCS_RX_EQ_IN_PROGRESS(2) |
+ XTP_PCS_PWD_SYNC(2) |
+ XTP_PCS_PWD_ASYNC(2));
+
+ usleep_range(1, 5);
+ writel(XTP_LN_FRC_TX_DATA_EN, xfi_tphy->base + REG_DIG_LN_TRX_40);
+
+ /* Setup TX DA default value */
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x30b0, 0x30, 0x20);
+ mtk_xfi_tphy_write(xfi_tphy, 0x3028, 0x00008a01);
+ mtk_xfi_tphy_write(xfi_tphy, 0x302c, 0x0000a884);
+ mtk_xfi_tphy_write(xfi_tphy, 0x3024, 0x00083002);
+
+ /* Setup RG default value */
+ if (is_xgmii) {
+ mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00022220);
+ mtk_xfi_tphy_write(xfi_tphy, 0x5064, 0x0f020a01);
+ mtk_xfi_tphy_write(xfi_tphy, 0x50b4, 0x06100600);
+ if (interface == PHY_INTERFACE_MODE_USXGMII)
+ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000);
+ else
+ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x47684100);
+ } else {
+ mtk_xfi_tphy_write(xfi_tphy, 0x3010, 0x00011110);
+ mtk_xfi_tphy_write(xfi_tphy, 0x3048, 0x40704000);
+ }
+
+ if (is_1g)
+ mtk_xfi_tphy_write(xfi_tphy, 0x3064, 0x0000c000);
+
+ /* Setup RX EQ initial value */
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x3050, 0xa8000000,
+ (interface != PHY_INTERFACE_MODE_10GBASER) ?
+ 0xa8000000 : 0x0);
+ mtk_xfi_tphy_rmw(xfi_tphy, 0x3054, 0xaa,
+ (interface != PHY_INTERFACE_MODE_10GBASER) ?
+ 0xaa : 0x0);
+
+ if (is_xgmii)
+ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x00000f00);
+ else if (is_2p5g)
+ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x22000f00);
+ else
+ mtk_xfi_tphy_write(xfi_tphy, 0x306c, 0x20200f00);
+
+ if (interface == PHY_INTERFACE_MODE_10GBASER && xfi_tphy->da_war)
+ mtk_xfi_tphy_rmw(xfi_tphy, 0xa008, 0x10000, 0x10000);
+
+ mtk_xfi_tphy_rmw(xfi_tphy, 0xa060, 0x50000, is_xgmii ? 0x40000 :
+ 0x50000);
+
+ /* Setup PHYA speed */
+ mtk_xfi_tphy_rmw(xfi_tphy, REG_ANA_GLB_D0,
+ XTP_GLB_USXGMII_SEL_MASK | XTP_GLB_USXGMII_EN,
+ is_10g ? XTP_GLB_USXGMII_SEL(0) :
+ is_5g ? XTP_GLB_USXGMII_SEL(1) :
+ is_2p5g ? XTP_GLB_USXGMII_SEL(2) :
+ XTP_GLB_USXGMII_SEL(3));
+ mtk_xfi_tphy_set(xfi_tphy, REG_ANA_GLB_D0, XTP_GLB_USXGMII_EN);
+
+ /* Release reset */
+ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_GLB_70,
+ XTP_PCS_RST_B | XTP_FRC_PCS_RST_B);
+ usleep_range(150, 500);
+
+ /* Switch to P0 */
+ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70,
+ XTP_PCS_PWD_SYNC_MASK |
+ XTP_PCS_PWD_ASYNC_MASK,
+ XTP_FRC_PCS_PWD_ASYNC |
+ XTP_PCS_UPDT | XTP_PCS_IN_FR_RG);
+ usleep_range(1, 5);
+
+ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT);
+ usleep_range(15, 50);
+
+ if (is_xgmii) {
+ /* Switch to Gen3 */
+ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70,
+ XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
+ XTP_PCS_MODE(2) | XTP_PCS_UPDT);
+ } else {
+ /* Switch to Gen2 */
+ mtk_xfi_tphy_rmw(xfi_tphy, REG_DIG_GLB_70,
+ XTP_PCS_MODE_MASK | XTP_PCS_UPDT,
+ XTP_PCS_MODE(1) | XTP_PCS_UPDT);
+ }
+ usleep_range(1, 5);
+
+ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_70, XTP_PCS_UPDT);
+
+ usleep_range(100, 500);
+
+ /* Enable MAC CK */
+ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_B0, XTP_LN_TX_MACCK_EN);
+ mtk_xfi_tphy_clear(xfi_tphy, REG_DIG_GLB_F4, XFI_DPHY_AD_SGDT_FRC_EN);
+
+ /* Enable TX data */
+ mtk_xfi_tphy_set(xfi_tphy, REG_DIG_LN_TRX_40,
+ XTP_LN_FRC_TX_DATA_EN | XTP_LN_TX_DATA_EN);
+ usleep_range(400, 1000);
+}
+
+static int mtk_xfi_tphy_set_mode(struct phy *phy, enum phy_mode mode, int
+ submode)
+{
+ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
+
+ if (mode != PHY_MODE_ETHERNET)
+ return -EINVAL;
+
+ switch (submode) {
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_5GBASER:
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_USXGMII:
+ mtk_xfi_tphy_setup(xfi_tphy, submode);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mtk_xfi_tphy_reset(struct phy *phy)
+{
+ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
+
+ reset_control_assert(xfi_tphy->reset);
+ usleep_range(100, 500);
+ reset_control_deassert(xfi_tphy->reset);
+ usleep_range(1, 10);
+
+ return 0;
+}
+
+static int mtk_xfi_tphy_power_on(struct phy *phy)
+{
+ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
+
+ return clk_bulk_prepare_enable(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
+}
+
+static int mtk_xfi_tphy_power_off(struct phy *phy)
+{
+ struct mtk_xfi_tphy *xfi_tphy = phy_get_drvdata(phy);
+
+ clk_bulk_disable_unprepare(MTK_XFI_TPHY_NUM_CLOCKS, xfi_tphy->clocks);
+
+ return 0;
+}
+
+static const struct phy_ops mtk_xfi_tphy_ops = {
+ .power_on = mtk_xfi_tphy_power_on,
+ .power_off = mtk_xfi_tphy_power_off,
+ .set_mode = mtk_xfi_tphy_set_mode,
+ .reset = mtk_xfi_tphy_reset,
+ .owner = THIS_MODULE,
+};
+
+static int mtk_xfi_tphy_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct phy_provider *phy_provider;
+ struct mtk_xfi_tphy *xfi_tphy;
+ struct phy *phy;
+
+ if (!np)
+ return -ENODEV;
+
+ xfi_tphy = devm_kzalloc(&pdev->dev, sizeof(*xfi_tphy), GFP_KERNEL);
+ if (!xfi_tphy)
+ return -ENOMEM;
+
+ xfi_tphy->base = devm_of_iomap(&pdev->dev, np, 0, NULL);
+ if (!xfi_tphy->base)
+ return -EIO;
+
+ xfi_tphy->dev = &pdev->dev;
+
+ xfi_tphy->clocks[0].id = "topxtal";
+ xfi_tphy->clocks[0].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[0].id);
+ if (IS_ERR(xfi_tphy->clocks[0].clk))
+ return PTR_ERR(xfi_tphy->clocks[0].clk);
+
+ xfi_tphy->clocks[1].id = "xfipll";
+ xfi_tphy->clocks[1].clk = devm_clk_get(&pdev->dev, xfi_tphy->clocks[1].id);
+ if (IS_ERR(xfi_tphy->clocks[1].clk))
+ return PTR_ERR(xfi_tphy->clocks[1].clk);
+
+ xfi_tphy->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(xfi_tphy->reset))
+ return PTR_ERR(xfi_tphy->reset);
+
+ xfi_tphy->da_war = of_property_read_bool(np,
+ "mediatek,usxgmii-performance-errata");
+
+ phy = devm_phy_create(&pdev->dev, NULL, &mtk_xfi_tphy_ops);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ phy_set_drvdata(phy, xfi_tphy);
+
+ phy_provider = devm_of_phy_provider_register(&pdev->dev,
+ of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id mtk_xfi_tphy_match[] = {
+ { .compatible = "mediatek,mt7988-xfi-tphy", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mtk_xfi_tphy_match);
+
+static struct platform_driver mtk_xfi_tphy_driver = {
+ .probe = mtk_xfi_tphy_probe,
+ .driver = {
+ .name = "mtk-xfi-tphy",
+ .of_match_table = mtk_xfi_tphy_match,
+ },
+};
+module_platform_driver(mtk_xfi_tphy_driver);
+
+MODULE_DESCRIPTION("MediaTek XFI T-PHY driver");
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_AUTHOR("Bc-bocun Chen <bc-bocun.chen@mediatek.com>");
+MODULE_LICENSE("GPL");

View file

@ -1,371 +0,0 @@
From 4b1a2716299c0e96a698044aebf3f80513509ae7 Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Tue, 12 Dec 2023 03:47:18 +0000
Subject: [PATCH 3/5] net: pcs: pcs-mtk-lynxi: add platform driver for MT7988
Introduce a proper platform MFD driver for the LynxI (H)SGMII PCS which
is going to initially be used for the MT7988 SoC.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/pcs/pcs-mtk-lynxi.c | 227 ++++++++++++++++++++++++++++--
include/linux/pcs/pcs-mtk-lynxi.h | 11 ++
2 files changed, 227 insertions(+), 11 deletions(-)
--- a/drivers/net/pcs/pcs-mtk-lynxi.c
+++ b/drivers/net/pcs/pcs-mtk-lynxi.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018-2019 MediaTek Inc.
-/* A library for MediaTek SGMII circuit
+/* A library and platform driver for the MediaTek LynxI SGMII circuit
*
* Author: Sean Wang <sean.wang@mediatek.com>
* Author: Alexander Couzens <lynxis@fe80.eu>
@@ -8,11 +8,17 @@
*
*/
+#include <linux/clk.h>
#include <linux/mdio.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/pcs/pcs-mtk-lynxi.h>
#include <linux/phylink.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
/* SGMII subsystem config registers */
/* BMCR (low 16) BMSR (high 16) */
@@ -65,6 +71,8 @@
#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
+#define MTK_NETSYS_V3_AMA_RGC3 0x128
+
/* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated
* data
* @regmap: The register map pointing at the range used to setup
@@ -74,15 +82,29 @@
* @interface: Currently configured interface mode
* @pcs: Phylink PCS structure
* @flags: Flags indicating hardware properties
+ * @rstc: Reset controller
+ * @sgmii_sel: SGMII Register Clock
+ * @sgmii_rx: SGMII RX Clock
+ * @sgmii_tx: SGMII TX Clock
+ * @node: List node
*/
struct mtk_pcs_lynxi {
struct regmap *regmap;
+ struct device *dev;
u32 ana_rgc3;
phy_interface_t interface;
struct phylink_pcs pcs;
u32 flags;
+ struct reset_control *rstc;
+ struct clk *sgmii_sel;
+ struct clk *sgmii_rx;
+ struct clk *sgmii_tx;
+ struct list_head node;
};
+static LIST_HEAD(mtk_pcs_lynxi_instances);
+static DEFINE_MUTEX(instance_mutex);
+
static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
{
return container_of(pcs, struct mtk_pcs_lynxi, pcs);
@@ -102,6 +124,17 @@ static void mtk_pcs_lynxi_get_state(stru
FIELD_GET(SGMII_LPA, adv));
}
+static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs)
+{
+ if (!mpcs->rstc)
+ return;
+
+ reset_control_assert(mpcs->rstc);
+ udelay(100);
+ reset_control_deassert(mpcs->rstc);
+ mdelay(1);
+}
+
static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
phy_interface_t interface,
const unsigned long *advertising,
@@ -147,6 +180,7 @@ static int mtk_pcs_lynxi_config(struct p
SGMII_PHYA_PWD);
/* Reset SGMII PCS state */
+ mtk_sgmii_reset(mpcs);
regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
SGMII_SW_RESET);
@@ -233,10 +267,29 @@ static void mtk_pcs_lynxi_link_up(struct
}
}
+static int mtk_pcs_lynxi_enable(struct phylink_pcs *pcs)
+{
+ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
+
+ if (mpcs->sgmii_tx && mpcs->sgmii_rx) {
+ clk_prepare_enable(mpcs->sgmii_rx);
+ clk_prepare_enable(mpcs->sgmii_tx);
+ }
+
+ return 0;
+}
+
static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs)
{
struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
+ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
+
+ if (mpcs->sgmii_tx && mpcs->sgmii_rx) {
+ clk_disable_unprepare(mpcs->sgmii_tx);
+ clk_disable_unprepare(mpcs->sgmii_rx);
+ }
+
mpcs->interface = PHY_INTERFACE_MODE_NA;
}
@@ -246,11 +299,12 @@ static const struct phylink_pcs_ops mtk_
.pcs_an_restart = mtk_pcs_lynxi_restart_an,
.pcs_link_up = mtk_pcs_lynxi_link_up,
.pcs_disable = mtk_pcs_lynxi_disable,
+ .pcs_enable = mtk_pcs_lynxi_enable,
};
-struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
- struct regmap *regmap, u32 ana_rgc3,
- u32 flags)
+static struct phylink_pcs *mtk_pcs_lynxi_init(struct device *dev, struct regmap *regmap,
+ u32 ana_rgc3, u32 flags,
+ struct mtk_pcs_lynxi *prealloc)
{
struct mtk_pcs_lynxi *mpcs;
u32 id, ver;
@@ -258,29 +312,33 @@ struct phylink_pcs *mtk_pcs_lynxi_create
ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
if (ret < 0)
- return NULL;
+ return ERR_PTR(ret);
if (id != SGMII_LYNXI_DEV_ID) {
dev_err(dev, "unknown PCS device id %08x\n", id);
- return NULL;
+ return ERR_PTR(-ENODEV);
}
ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
if (ret < 0)
- return NULL;
+ return ERR_PTR(ret);
ver = FIELD_GET(SGMII_DEV_VERSION, ver);
if (ver != 0x1) {
dev_err(dev, "unknown PCS device version %04x\n", ver);
- return NULL;
+ return ERR_PTR(-ENODEV);
}
dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
ver);
- mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
- if (!mpcs)
- return NULL;
+ if (prealloc) {
+ mpcs = prealloc;
+ } else {
+ mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
+ if (!mpcs)
+ return ERR_PTR(-ENOMEM);
+ };
mpcs->ana_rgc3 = ana_rgc3;
mpcs->regmap = regmap;
@@ -291,6 +349,13 @@ struct phylink_pcs *mtk_pcs_lynxi_create
mpcs->interface = PHY_INTERFACE_MODE_NA;
return &mpcs->pcs;
+};
+
+struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
+ struct regmap *regmap, u32 ana_rgc3,
+ u32 flags)
+{
+ return mtk_pcs_lynxi_init(dev, regmap, ana_rgc3, flags, NULL);
}
EXPORT_SYMBOL(mtk_pcs_lynxi_create);
@@ -303,4 +368,144 @@ void mtk_pcs_lynxi_destroy(struct phylin
}
EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
+static int mtk_pcs_lynxi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct mtk_pcs_lynxi *mpcs;
+ struct phylink_pcs *pcs;
+ struct regmap *regmap;
+ u32 flags = 0;
+
+ mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL);
+ if (!mpcs)
+ return -ENOMEM;
+
+ mpcs->dev = dev;
+ regmap = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ if (of_property_read_bool(np->parent, "mediatek,pnswap"))
+ flags |= MTK_SGMII_FLAG_PN_SWAP;
+
+ mpcs->rstc = of_reset_control_get_shared(np->parent, NULL);
+ if (IS_ERR(mpcs->rstc))
+ return PTR_ERR(mpcs->rstc);
+
+ reset_control_deassert(mpcs->rstc);
+ mpcs->sgmii_sel = devm_clk_get_enabled(dev, "sgmii_sel");
+ if (IS_ERR(mpcs->sgmii_sel))
+ return PTR_ERR(mpcs->sgmii_sel);
+
+ mpcs->sgmii_rx = devm_clk_get(dev, "sgmii_rx");
+ if (IS_ERR(mpcs->sgmii_rx))
+ return PTR_ERR(mpcs->sgmii_rx);
+
+ mpcs->sgmii_tx = devm_clk_get(dev, "sgmii_tx");
+ if (IS_ERR(mpcs->sgmii_tx))
+ return PTR_ERR(mpcs->sgmii_tx);
+
+ pcs = mtk_pcs_lynxi_init(dev, regmap, (uintptr_t)of_device_get_match_data(dev),
+ flags, mpcs);
+ if (IS_ERR(pcs))
+ return PTR_ERR(pcs);
+
+ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
+
+ platform_set_drvdata(pdev, mpcs);
+
+ mutex_lock(&instance_mutex);
+ list_add_tail(&mpcs->node, &mtk_pcs_lynxi_instances);
+ mutex_unlock(&instance_mutex);
+
+ return 0;
+}
+
+static int mtk_pcs_lynxi_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_pcs_lynxi *cur, *tmp;
+
+ mutex_lock(&instance_mutex);
+ list_for_each_entry_safe(cur, tmp, &mtk_pcs_lynxi_instances, node)
+ if (cur->dev == dev) {
+ list_del(&cur->node);
+ kfree(cur);
+ break;
+ }
+ mutex_unlock(&instance_mutex);
+
+ return 0;
+}
+
+static const struct of_device_id mtk_pcs_lynxi_of_match[] = {
+ { .compatible = "mediatek,mt7988-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mtk_pcs_lynxi_of_match);
+
+struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np)
+{
+ struct platform_device *pdev;
+ struct mtk_pcs_lynxi *mpcs;
+
+ if (!np)
+ return NULL;
+
+ if (!of_device_is_available(np))
+ return ERR_PTR(-ENODEV);
+
+ if (!of_match_node(mtk_pcs_lynxi_of_match, np))
+ return ERR_PTR(-EINVAL);
+
+ pdev = of_find_device_by_node(np);
+ if (!pdev || !platform_get_drvdata(pdev)) {
+ if (pdev)
+ put_device(&pdev->dev);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ mpcs = platform_get_drvdata(pdev);
+ device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
+
+ return &mpcs->pcs;
+}
+EXPORT_SYMBOL(mtk_pcs_lynxi_get);
+
+void mtk_pcs_lynxi_put(struct phylink_pcs *pcs)
+{
+ struct mtk_pcs_lynxi *cur, *mpcs = NULL;
+
+ if (!pcs)
+ return;
+
+ mutex_lock(&instance_mutex);
+ list_for_each_entry(cur, &mtk_pcs_lynxi_instances, node)
+ if (pcs == &cur->pcs) {
+ mpcs = cur;
+ break;
+ }
+ mutex_unlock(&instance_mutex);
+
+ if (WARN_ON(!mpcs))
+ return;
+
+ put_device(mpcs->dev);
+}
+EXPORT_SYMBOL(mtk_pcs_lynxi_put);
+
+static struct platform_driver mtk_pcs_lynxi_driver = {
+ .driver = {
+ .name = "mtk-pcs-lynxi",
+ .suppress_bind_attrs = true,
+ .of_match_table = mtk_pcs_lynxi_of_match,
+ },
+ .probe = mtk_pcs_lynxi_probe,
+ .remove = mtk_pcs_lynxi_remove,
+};
+module_platform_driver(mtk_pcs_lynxi_driver);
+
+MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
+MODULE_DESCRIPTION("MediaTek LynxI HSGMII PCS");
MODULE_LICENSE("GPL");
--- a/include/linux/pcs/pcs-mtk-lynxi.h
+++ b/include/linux/pcs/pcs-mtk-lynxi.h
@@ -10,4 +10,15 @@ struct phylink_pcs *mtk_pcs_lynxi_create
struct regmap *regmap,
u32 ana_rgc3, u32 flags);
void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
+
+#if IS_ENABLED(CONFIG_PCS_MTK_LYNXI)
+struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np);
+void mtk_pcs_lynxi_put(struct phylink_pcs *pcs);
+#else
+static inline struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np)
+{
+ return NULL;
+}
+static inline void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) { }
+#endif /* IS_ENABLED(CONFIG_PCS_MTK_LYNXI) */
#endif

View file

@ -1,75 +0,0 @@
From d7943c31d57c11e1a517aa3ce2006fca44866870 Mon Sep 17 00:00:00 2001
From: Jianhui Zhao <zhaojh329@gmail.com>
Date: Sun, 24 Sep 2023 22:15:00 +0800
Subject: [PATCH] net: phy: realtek: add interrupt support for RTL8221B
This commit introduces interrupt support for RTL8221B.
Signed-off-by: Jianhui Zhao <zhaojh329@gmail.com>
---
drivers/net/phy/realtek.c | 47 +++++++++++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
--- a/drivers/net/phy/realtek.c
+++ b/drivers/net/phy/realtek.c
@@ -1010,6 +1010,51 @@ static int rtl8221b_config_init(struct p
return 0;
}
+static int rtl8221b_ack_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = phy_read_mmd(phydev, RTL8221B_MMD_PHY_CTRL, 0xa4d4);
+
+ return (err < 0) ? err : 0;
+}
+
+static int rtl8221b_config_intr(struct phy_device *phydev)
+{
+ int err;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ err = rtl8221b_ack_interrupt(phydev);
+ if (err)
+ return err;
+
+ err = phy_write_mmd(phydev, RTL8221B_MMD_PHY_CTRL, 0xa4d2, 0x7ff);
+ } else {
+ err = phy_write_mmd(phydev, RTL8221B_MMD_PHY_CTRL, 0xa4d2, 0x0);
+ if (err)
+ return err;
+
+ err = rtl8221b_ack_interrupt(phydev);
+ }
+
+ return err;
+}
+
+static irqreturn_t rtl8221b_handle_interrupt(struct phy_device *phydev)
+{
+ int err;
+
+ err = rtl8221b_ack_interrupt(phydev);
+ if (err) {
+ phy_error(phydev);
+ return IRQ_NONE;
+ }
+
+ phy_trigger_machine(phydev);
+
+ return IRQ_HANDLED;
+}
+
static struct phy_driver realtek_drvs[] = {
{
PHY_ID_MATCH_EXACT(0x00008201),
@@ -1172,6 +1217,8 @@ static struct phy_driver realtek_drvs[]
.get_features = rtl822x_get_features,
.config_init = rtl8221b_config_init,
.config_aneg = rtl822x_config_aneg,
+ .config_intr = rtl8221b_config_intr,
+ .handle_interrupt = rtl8221b_handle_interrupt,
.probe = rtl822x_probe,
.read_status = rtl822x_read_status,
.suspend = genphy_suspend,

View file

@ -1,368 +0,0 @@
From c6a1759365fc35463138a7d9e335ee53f384b8df Mon Sep 17 00:00:00 2001
From: Daniel Golle <daniel@makrotopia.org>
Date: Fri, 10 May 2024 02:53:52 +0100
Subject: [PATCH] net: phy: aquantia: add support for PHY LEDs
Aquantia Ethernet PHYs got 3 LED output pins which are typically used
to indicate link status and activity.
Add a minimal LED controller driver supporting the most common uses
with the 'netdev' trigger as well as software-driven forced control of
the LEDs.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/phy/aquantia/Makefile | 3 +
drivers/net/phy/aquantia/aquantia.h | 84 +++++++++++++
drivers/net/phy/aquantia/aquantia_leds.c | 152 +++++++++++++++++++++++
drivers/net/phy/aquantia/aquantia_main.c | 127 +++++++++++++------
4 files changed, 329 insertions(+), 37 deletions(-)
create mode 100644 drivers/net/phy/aquantia/aquantia_leds.c
--- a/drivers/net/phy/aquantia/Makefile
+++ b/drivers/net/phy/aquantia/Makefile
@@ -3,4 +3,7 @@ aquantia-objs += aquantia_main.o aquan
ifdef CONFIG_HWMON
aquantia-objs += aquantia_hwmon.o
endif
+ifdef CONFIG_PHYLIB_LEDS
+aquantia-objs += aquantia_leds.o
+endif
obj-$(CONFIG_AQUANTIA_PHY) += aquantia.o
--- a/drivers/net/phy/aquantia/aquantia.h
+++ b/drivers/net/phy/aquantia/aquantia.h
@@ -62,6 +62,26 @@
#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424
+
+#define AQR_NUM_LEDS 3
+
+#define VEND1_GLOBAL_LED_PROV 0xc430
+#define AQR_LED_PROV(x) (VEND1_GLOBAL_LED_PROV + x)
+#define VEND1_GLOBAL_LED_PROV_ACT_STRETCH GENMASK(0, 1)
+#define VEND1_GLOBAL_LED_PROV_TX_ACT BIT(2)
+#define VEND1_GLOBAL_LED_PROV_RX_ACT BIT(3)
+#define VEND1_GLOBAL_LED_PROV_LINK_MASK (GENMASK(15, 14) | GENMASK(8, 5))
+#define VEND1_GLOBAL_LED_PROV_LINK100 BIT(5)
+#define VEND1_GLOBAL_LED_PROV_LINK1000 BIT(6)
+#define VEND1_GLOBAL_LED_PROV_LINK10000 BIT(7)
+#define VEND1_GLOBAL_LED_PROV_FORCE_ON BIT(8)
+#define VEND1_GLOBAL_LED_PROV_LINK2500 BIT(14)
+#define VEND1_GLOBAL_LED_PROV_LINK5000 BIT(15)
+
+#define VEND1_GLOBAL_LED_DRIVE 0xc438
+#define VEND1_GLOBAL_LED_DRIVE_VDD BIT(1)
+#define AQR_LED_DRIVE(x) (VEND1_GLOBAL_LED_DRIVE + x)
+
#define VEND1_THERMAL_STAT1 0xc820
#define VEND1_THERMAL_STAT2 0xc821
#define VEND1_THERMAL_STAT2_VALID BIT(0)
@@ -115,3 +135,23 @@ static inline int aqr_hwmon_probe(struct
#endif
int aqr_firmware_load(struct phy_device *phydev);
+
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+int aqr_phy_led_blink_set(struct phy_device *phydev, u8 index,
+ unsigned long *delay_on,
+ unsigned long *delay_off);
+
+int aqr_phy_led_brightness_set(struct phy_device *phydev,
+ u8 index, enum led_brightness value);
+
+int aqr_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules);
+
+int aqr_phy_led_hw_control_get(struct phy_device *phydev, u8 index,
+ unsigned long *rules);
+
+int aqr_phy_led_hw_control_set(struct phy_device *phydev, u8 index,
+ unsigned long rules);
+
+int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes);
+#endif
--- /dev/null
+++ b/drivers/net/phy/aquantia/aquantia_leds.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/* LED driver for Aquantia PHY
+ *
+ * Author: Daniel Golle <daniel@makrotopia.org>
+ */
+
+#include <linux/phy.h>
+
+#include "aquantia.h"
+
+int aqr_phy_led_brightness_set(struct phy_device *phydev,
+ u8 index, enum led_brightness value)
+{
+ if (index > 2)
+ return -EINVAL;
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index), VEND1_GLOBAL_LED_PROV_LINK_MASK |
+ VEND1_GLOBAL_LED_PROV_FORCE_ON |
+ VEND1_GLOBAL_LED_PROV_RX_ACT |
+ VEND1_GLOBAL_LED_PROV_TX_ACT,
+ value ? VEND1_GLOBAL_LED_PROV_FORCE_ON : 0);
+}
+
+static const unsigned long supported_triggers = (BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_LINK_2500) |
+ BIT(TRIGGER_NETDEV_LINK_5000) |
+ BIT(TRIGGER_NETDEV_LINK_10000) |
+ BIT(TRIGGER_NETDEV_RX) |
+ BIT(TRIGGER_NETDEV_TX));
+
+int aqr_phy_led_hw_is_supported(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ if (index >= AQR_NUM_LEDS)
+ return -EINVAL;
+
+ /* All combinations of the supported triggers are allowed */
+ if (rules & ~supported_triggers)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+int aqr_phy_led_hw_control_get(struct phy_device *phydev, u8 index,
+ unsigned long *rules)
+{
+ int val;
+
+ if (index >= AQR_NUM_LEDS)
+ return -EINVAL;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index));
+ if (val < 0)
+ return val;
+
+ *rules = 0;
+ if (val & VEND1_GLOBAL_LED_PROV_LINK100)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_100);
+
+ if (val & VEND1_GLOBAL_LED_PROV_LINK1000)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
+
+ if (val & VEND1_GLOBAL_LED_PROV_LINK2500)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_2500);
+
+ if (val & VEND1_GLOBAL_LED_PROV_LINK5000)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_5000);
+
+ if (val & VEND1_GLOBAL_LED_PROV_LINK10000)
+ *rules |= BIT(TRIGGER_NETDEV_LINK_10000);
+
+ if (val & VEND1_GLOBAL_LED_PROV_RX_ACT)
+ *rules |= BIT(TRIGGER_NETDEV_RX);
+
+ if (val & VEND1_GLOBAL_LED_PROV_TX_ACT)
+ *rules |= BIT(TRIGGER_NETDEV_TX);
+
+ return 0;
+}
+
+int aqr_phy_led_hw_control_set(struct phy_device *phydev, u8 index,
+ unsigned long rules)
+{
+ u16 val = 0;
+
+ if (index >= AQR_NUM_LEDS)
+ return -EINVAL;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
+ val |= VEND1_GLOBAL_LED_PROV_LINK100;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
+ val |= VEND1_GLOBAL_LED_PROV_LINK1000;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
+ val |= VEND1_GLOBAL_LED_PROV_LINK2500;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_5000) | BIT(TRIGGER_NETDEV_LINK)))
+ val |= VEND1_GLOBAL_LED_PROV_LINK5000;
+
+ if (rules & (BIT(TRIGGER_NETDEV_LINK_10000) | BIT(TRIGGER_NETDEV_LINK)))
+ val |= VEND1_GLOBAL_LED_PROV_LINK10000;
+
+ if (rules & BIT(TRIGGER_NETDEV_RX))
+ val |= VEND1_GLOBAL_LED_PROV_RX_ACT;
+
+ if (rules & BIT(TRIGGER_NETDEV_TX))
+ val |= VEND1_GLOBAL_LED_PROV_TX_ACT;
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index),
+ VEND1_GLOBAL_LED_PROV_LINK_MASK |
+ VEND1_GLOBAL_LED_PROV_FORCE_ON |
+ VEND1_GLOBAL_LED_PROV_RX_ACT |
+ VEND1_GLOBAL_LED_PROV_TX_ACT, val);
+}
+
+int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes)
+{
+ bool active_low = false;
+ u32 mode;
+
+ if (index >= AQR_NUM_LEDS)
+ return -EINVAL;
+
+ for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
+ switch (mode) {
+ case PHY_LED_ACTIVE_LOW:
+ active_low = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index),
+ VEND1_GLOBAL_LED_DRIVE_VDD,
+ active_low ? VEND1_GLOBAL_LED_DRIVE_VDD : 0);
+}
--- a/drivers/net/phy/aquantia/aquantia_main.c
+++ b/drivers/net/phy/aquantia/aquantia_main.c
@@ -740,6 +740,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQCS109),
@@ -759,6 +766,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR111),
@@ -778,6 +792,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR111B0),
@@ -797,6 +818,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR405),
@@ -823,6 +851,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR412),
@@ -841,6 +876,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR113),
@@ -860,6 +902,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR113C),
@@ -879,6 +928,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR114C),
@@ -898,6 +954,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
{
PHY_ID_MATCH_MODEL(PHY_ID_AQR813),
@@ -917,6 +980,13 @@ static struct phy_driver aqr_driver[] =
.get_strings = aqr107_get_strings,
.get_stats = aqr107_get_stats,
.link_change_notify = aqr107_link_change_notify,
+#if IS_ENABLED(CONFIG_PHYLIB_LEDS)
+ .led_brightness_set = aqr_phy_led_brightness_set,
+ .led_hw_is_supported = aqr_phy_led_hw_is_supported,
+ .led_hw_control_set = aqr_phy_led_hw_control_set,
+ .led_hw_control_get = aqr_phy_led_hw_control_get,
+ .led_polarity_set = aqr_phy_led_polarity_set,
+#endif
},
};

View file

@ -1,115 +0,0 @@
From: Felix Fietkau <nbd@nbd.name>
Subject: debloat: disable common USB quirks
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/usb/host/pci-quirks.c | 16 ++++++++++++++++
drivers/usb/host/pci-quirks.h | 18 +++++++++++++++++-
include/linux/usb/hcd.h | 7 +++++++
3 files changed, 40 insertions(+), 1 deletion(-)
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -128,6 +128,8 @@ struct amd_chipset_type {
u8 rev;
};
+#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
+
static struct amd_chipset_info {
struct pci_dev *nb_dev;
struct pci_dev *smbus_dev;
@@ -631,6 +633,10 @@ bool usb_amd_pt_check_port(struct device
}
EXPORT_SYMBOL_GPL(usb_amd_pt_check_port);
+#endif /* CONFIG_PCI_DISABLE_COMMON_QUIRKS */
+
+#if IS_ENABLED(CONFIG_USB_UHCI_HCD)
+
/*
* Make sure the controller is completely inactive, unable to
* generate interrupts or do DMA.
@@ -710,8 +716,17 @@ reset_needed:
uhci_reset_hc(pdev, base);
return 1;
}
+#else
+int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base)
+{
+ return 0;
+}
+
+#endif
EXPORT_SYMBOL_GPL(uhci_check_and_reset_hc);
+#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
+
static inline int io_type_enabled(struct pci_dev *pdev, unsigned int mask)
{
u16 cmd;
@@ -1283,3 +1298,4 @@ static void quirk_usb_early_handoff(stru
}
DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
+#endif
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -5,6 +5,9 @@
#ifdef CONFIG_USB_PCI
void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
+#endif /* CONFIG_USB_PCI */
+
+#if defined(CONFIG_USB_PCI) && !defined(CONFIG_PCI_DISABLE_COMMON_QUIRKS)
int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev);
bool usb_amd_hang_symptom_quirk(void);
bool usb_amd_prefetch_quirk(void);
@@ -19,6 +22,18 @@ void sb800_prefetch(struct device *dev,
bool usb_amd_pt_check_port(struct device *device, int port);
#else
struct pci_dev;
+static inline int usb_amd_quirk_pll_check(void)
+{
+ return 0;
+}
+static inline bool usb_amd_hang_symptom_quirk(void)
+{
+ return false;
+}
+static inline bool usb_amd_prefetch_quirk(void)
+{
+ return false;
+}
static inline void usb_amd_quirk_pll_disable(void) {}
static inline void usb_amd_quirk_pll_enable(void) {}
static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {}
@@ -29,6 +44,11 @@ static inline bool usb_amd_pt_check_port
{
return false;
}
+static inline void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev) {}
+static inline bool usb_xhci_needs_pci_reset(struct pci_dev *pdev)
+{
+ return false;
+}
#endif /* CONFIG_USB_PCI */
#endif /* __LINUX_USB_PCI_QUIRKS_H */
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -485,7 +485,14 @@ extern int usb_hcd_pci_probe(struct pci_
extern void usb_hcd_pci_remove(struct pci_dev *dev);
extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
+#ifndef CONFIG_PCI_DISABLE_COMMON_QUIRKS
extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev);
+#else
+static inline int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev)
+{
+ return 0;
+}
+#endif
extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
#endif /* CONFIG_USB_PCI */

View file

@ -1,26 +0,0 @@
From d9c8bc8c1408f3e8529db6e4e04017b4c579c342 Mon Sep 17 00:00:00 2001
From: Pawel Dembicki <paweldembicki@gmail.com>
Date: Sun, 18 Feb 2018 17:08:04 +0100
Subject: [PATCH] w1: gpio: fix problem with platfom data in w1-gpio
In devices, where fdt is used, is impossible to apply platform data
without proper fdt node.
This patch allow to use platform data in devices with fdt.
Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com>
---
drivers/w1/masters/w1-gpio.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
--- a/drivers/w1/masters/w1-gpio.c
+++ b/drivers/w1/masters/w1-gpio.c
@@ -76,7 +76,7 @@ static int w1_gpio_probe(struct platform
enum gpiod_flags gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
int err;
- if (of_have_populated_dt()) {
+ if (of_have_populated_dt() && !dev_get_platdata(&pdev->dev)) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;

View file

@ -1,37 +0,0 @@
From 38eb5b3370c29515d2ce92adac2d6eba96f276f5 Mon Sep 17 00:00:00 2001
From: INAGAKI Hiroshi <musashino.open@gmail.com>
Date: Wed, 20 Mar 2024 15:32:18 +0900
Subject: [PATCH v2 1/2] dt-bindings: leds: add LED_FUNCTION_MOBILE for mobile
network
Add LED_FUNCTION_MOBILE for LEDs that indicate status of mobile network
connection. This is useful to distinguish those LEDs from LEDs that
indicates status of wired "wan" connection.
example (on stock fw):
IIJ SA-W2 has "Mobile" LEDs that indicate status (no signal, too low,
low, good) of mobile network connection via dongle connected to USB
port.
- no signal: (none, turned off)
- too low: green:mobile & red:mobile (amber, blink)
- low: green:mobile & red:mobile (amber, turned on)
- good: green:mobile (turned on)
Suggested-by: Hauke Mehrtens <hauke@hauke-m.de>
Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
---
include/dt-bindings/leds/common.h | 1 +
1 file changed, 1 insertion(+)
--- a/include/dt-bindings/leds/common.h
+++ b/include/dt-bindings/leds/common.h
@@ -90,6 +90,7 @@
#define LED_FUNCTION_INDICATOR "indicator"
#define LED_FUNCTION_LAN "lan"
#define LED_FUNCTION_MAIL "mail"
+#define LED_FUNCTION_MOBILE "mobile"
#define LED_FUNCTION_MTD "mtd"
#define LED_FUNCTION_PANIC "panic"
#define LED_FUNCTION_PROGRAMMING "programming"

View file

@ -1,37 +0,0 @@
From e22afe910afcfb51b6ba6a0ae776939959727f54 Mon Sep 17 00:00:00 2001
From: INAGAKI Hiroshi <musashino.open@gmail.com>
Date: Wed, 20 Mar 2024 15:59:06 +0900
Subject: [PATCH v2 2/2] dt-bindings: leds: add LED_FUNCTION_SPEED_* for link
speed on LAN/WAN
Add LED_FUNCTION_SPEED_LAN and LED_FUNCTION_SPEED_WAN for LEDs that
indicate link speed of ethernet ports on LAN/WAN. This is useful to
distinguish those LEDs from LEDs that indicate link status (up/down).
example:
Fortinet FortiGate 30E/50E have LEDs that indicate link speed on each
of the ethernet ports in addition to LEDs that indicate link status
(up/down).
- 1000 Mbps: green:speed-(lan|wan)-N
- 100 Mbps: amber:speed-(lan|wan)-N
- 10 Mbps: (none, turned off)
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
---
include/dt-bindings/leds/common.h | 2 ++
1 file changed, 2 insertions(+)
--- a/include/dt-bindings/leds/common.h
+++ b/include/dt-bindings/leds/common.h
@@ -96,6 +96,8 @@
#define LED_FUNCTION_PROGRAMMING "programming"
#define LED_FUNCTION_RX "rx"
#define LED_FUNCTION_SD "sd"
+#define LED_FUNCTION_SPEED_LAN "speed-lan"
+#define LED_FUNCTION_SPEED_WAN "speed-wan"
#define LED_FUNCTION_STANDBY "standby"
#define LED_FUNCTION_TORCH "torch"
#define LED_FUNCTION_TX "tx"

View file

@ -1,51 +0,0 @@
From a7a94ca21ac0f347f683d33c72b4aab57ce5eec3 Mon Sep 17 00:00:00 2001
From: Florian Eckert <fe@dev.tdt.de>
Date: Mon, 20 Nov 2023 11:13:20 +0100
Subject: [PATCH] tools/thermal/tmon: Fix compilation warning for wrong format
The following warnings are shown during compilation:
tui.c: In function 'show_cooling_device':
tui.c:216:40: warning: format '%d' expects argument of type 'int', but
argument 7 has type 'long unsigned int' [-Wformat=]
216 | "%02d %12.12s%6d %6d",
| ~~^
| |
| int
| %6ld
......
219 | ptdata.cdi[j].cur_state,
| ~~~~~~~~~~~~~~~~~~~~~~~
| |
| long unsigned int
tui.c:216:44: warning: format '%d' expects argument of type 'int', but
argument 8 has type 'long unsigned int' [-Wformat=]
216 | "%02d %12.12s%6d %6d",
| ~~^
| |
| int
| %6ld
......
220 | ptdata.cdi[j].max_state);
| ~~~~~~~~~~~~~~~~~~~~~~~
| |
| long unsigned int
To fix this, the correct string format must be used for printing.
Signed-off-by: Florian Eckert <fe@dev.tdt.de>
---
tools/thermal/tmon/tui.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/tools/thermal/tmon/tui.c
+++ b/tools/thermal/tmon/tui.c
@@ -213,7 +213,7 @@ void show_cooling_device(void)
* cooling device instances. skip unused idr.
*/
mvwprintw(cooling_device_window, j + 2, 1,
- "%02d %12.12s%6d %6d",
+ "%02d %12.12s%6lu %6lu",
ptdata.cdi[j].instance,
ptdata.cdi[j].type,
ptdata.cdi[j].cur_state,

View file

@ -1,100 +0,0 @@
From 6e6fff51ae5e54092611d174fa45fa78c237a415 Mon Sep 17 00:00:00 2001
From: Christian Marangi <ansuelsmth@gmail.com>
Date: Tue, 21 May 2024 20:01:46 +0200
Subject: [PATCH] net: phy: move LED polarity to phy_init_hw
Some PHY reset the polarity on reset and this cause the LED to
malfunction as LED polarity is configured only when LED is
registered.
To better handle this, move the LED polarity configuration in
phy_init_hw to reconfigure it after PHY reset.
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
---
drivers/net/phy/phy_device.c | 53 +++++++++++++++++++++++++-----------
1 file changed, 37 insertions(+), 16 deletions(-)
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1223,6 +1223,37 @@ static int phy_poll_reset(struct phy_dev
return 0;
}
+static int of_phy_led_init(struct phy_device *phydev)
+{
+ struct phy_led *phyled;
+
+ list_for_each_entry(phyled, &phydev->leds, list) {
+ struct led_classdev *cdev = &phyled->led_cdev;
+ struct device_node *np = cdev->dev->of_node;
+ unsigned long modes = 0;
+ int err;
+
+ if (of_property_read_bool(np, "active-low"))
+ set_bit(PHY_LED_ACTIVE_LOW, &modes);
+ if (of_property_read_bool(np, "inactive-high-impedance"))
+ set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes);
+
+ if (!modes)
+ continue;
+
+ /* Return error if asked to set polarity modes but not supported */
+ if (!phydev->drv->led_polarity_set)
+ return -EINVAL;
+
+ err = phydev->drv->led_polarity_set(phydev, phyled->index,
+ modes);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
int phy_init_hw(struct phy_device *phydev)
{
int ret = 0;
@@ -1259,6 +1290,12 @@ int phy_init_hw(struct phy_device *phyde
return ret;
}
+ if (IS_ENABLED(CONFIG_PHYLIB_LEDS)) {
+ ret = of_phy_led_init(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
EXPORT_SYMBOL(phy_init_hw);
@@ -3204,7 +3241,6 @@ static int of_phy_led(struct phy_device
struct device *dev = &phydev->mdio.dev;
struct led_init_data init_data = {};
struct led_classdev *cdev;
- unsigned long modes = 0;
struct phy_led *phyled;
u32 index;
int err;
@@ -3222,21 +3258,6 @@ static int of_phy_led(struct phy_device
if (index > U8_MAX)
return -EINVAL;
- if (of_property_read_bool(led, "active-low"))
- set_bit(PHY_LED_ACTIVE_LOW, &modes);
- if (of_property_read_bool(led, "inactive-high-impedance"))
- set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes);
-
- if (modes) {
- /* Return error if asked to set polarity modes but not supported */
- if (!phydev->drv->led_polarity_set)
- return -EINVAL;
-
- err = phydev->drv->led_polarity_set(phydev, index, modes);
- if (err)
- return err;
- }
-
phyled->index = index;
if (phydev->drv->led_brightness_set)
cdev->brightness_set_blocking = phy_led_set_brightness;