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:
parent
f023e9c6e5
commit
a9809a6010
47 changed files with 0 additions and 9856 deletions
|
@ -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:
|
|
@ -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/
|
|
@ -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);
|
|
@ -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
|
|
@ -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);
|
|
@ -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)
|
|
@ -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)
|
|
@ -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 |
|
|
@ -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 = {
|
|
@ -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) },
|
|
@ -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 = {
|
|
@ -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,
|
||||
¯onix_spinand_manufacturer,
|
||||
µn_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;
|
|
@ -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) },
|
|
@ -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) },
|
|
@ -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);
|
|
@ -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(ð->dummy_dev);
|
||||
+ eth->dummy_dev.threaded = 1;
|
||||
+ strcpy(eth->dummy_dev.name, "mtk_eth");
|
||||
netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx);
|
||||
netif_napi_add(ð->dummy_dev, ð->rx_napi, mtk_napi_rx);
|
||||
|
|
@ -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;
|
|
@ -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,
|
||||
};
|
|
@ -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)
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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,
|
|
@ -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,
|
|
@ -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");
|
|
@ -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
|
|
@ -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,
|
|
@ -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
|
||||
},
|
||||
};
|
||||
|
|
@ -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 */
|
|
@ -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;
|
|
@ -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"
|
|
@ -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"
|
|
@ -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,
|
|
@ -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;
|
Loading…
Add table
Add a link
Reference in a new issue