From e86fcdd203d39a616ae933780fe3edd973c43ecb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 31 Jul 2026 15:01:15 +0200 Subject: [PATCH 001/258] FROMLIST: Revert "thermal: hwmon: Register a hwmon device for each thermal zone" Revert commit d6323469bcfb ("thermal: hwmon: Register a hwmon device for each thermal zone") that changed the names of hwmon class devices associated with thermal zones and their sysfs layout which made user space unhappy. Closes: https://lore.kernel.org/linux-pm/cafd8af9-c6e9-4bf2-b496-23e796fbc9a6@linux.dev/ Closes: https://lore.kernel.org/linux-hwmon/ab8b093b-46e6-4738-afcf-4b97c9ad5af9@googlemail.com/ Cc: stable@vger.kernel.org Signed-off-by: Rafael J. Wysocki Link: https://lore.kernel.org/all/2301040.irdbgypaU6@rafael.j.wysocki/ Signed-off-by: Rong Bao --- drivers/thermal/thermal_hwmon.c | 151 ++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 47 deletions(-) diff --git a/drivers/thermal/thermal_hwmon.c b/drivers/thermal/thermal_hwmon.c index 223ae1571655bd..597c33c8a55508 100644 --- a/drivers/thermal/thermal_hwmon.c +++ b/drivers/thermal/thermal_hwmon.c @@ -19,33 +19,30 @@ #include "thermal_hwmon.h" #include "thermal_core.h" -/* - * Needs to be large enough to hold a thermal zone type string followed by an - * underline character and a 32-bit integer in decimal representation. - */ -#define THERMAL_HWMON_NAME_LENGTH (THERMAL_NAME_LENGTH + 11) +/* hwmon sys I/F */ +/* thermal zone devices with the same type share one hwmon device */ +struct thermal_hwmon_device { + char type[THERMAL_NAME_LENGTH]; + struct device *device; + int count; + struct list_head tz_list; + struct list_head node; +}; struct thermal_hwmon_attr { struct device_attribute attr; + char name[16]; }; /* one temperature input for each thermal zone */ struct thermal_hwmon_temp { + struct list_head hwmon_node; struct thermal_zone_device *tz; struct thermal_hwmon_attr temp_input; /* hwmon sys attr */ struct thermal_hwmon_attr temp_crit; /* hwmon sys attr */ bool temp_crit_present; }; -/* hwmon sys I/F */ -/* thermal zone devices with the same type share one hwmon device */ -struct thermal_hwmon_device { - char name[THERMAL_HWMON_NAME_LENGTH]; - struct device *device; - struct list_head node; - struct thermal_hwmon_temp tz_temp; -}; - static LIST_HEAD(thermal_hwmon_list); static DEFINE_MUTEX(thermal_hwmon_list_lock); @@ -91,6 +88,45 @@ temp_crit_show(struct device *dev, struct device_attribute *attr, char *buf) return sysfs_emit(buf, "%d\n", temperature); } + +static struct thermal_hwmon_device * +thermal_hwmon_lookup_by_type(const struct thermal_zone_device *tz) +{ + struct thermal_hwmon_device *hwmon; + char type[THERMAL_NAME_LENGTH]; + + mutex_lock(&thermal_hwmon_list_lock); + list_for_each_entry(hwmon, &thermal_hwmon_list, node) { + strscpy(type, tz->type); + strreplace(type, '-', '_'); + if (!strcmp(hwmon->type, type)) { + mutex_unlock(&thermal_hwmon_list_lock); + return hwmon; + } + } + mutex_unlock(&thermal_hwmon_list_lock); + + return NULL; +} + +/* Find the temperature input matching a given thermal zone */ +static struct thermal_hwmon_temp * +thermal_hwmon_lookup_temp(const struct thermal_hwmon_device *hwmon, + const struct thermal_zone_device *tz) +{ + struct thermal_hwmon_temp *temp; + + mutex_lock(&thermal_hwmon_list_lock); + list_for_each_entry(temp, &hwmon->tz_list, hwmon_node) + if (temp->tz == tz) { + mutex_unlock(&thermal_hwmon_list_lock); + return temp; + } + mutex_unlock(&thermal_hwmon_list_lock); + + return NULL; +} + static bool thermal_zone_crit_temp_valid(struct thermal_zone_device *tz) { int temp; @@ -101,39 +137,54 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; struct thermal_hwmon_temp *temp; + int new_hwmon_device = 1; int result; + hwmon = thermal_hwmon_lookup_by_type(tz); + if (hwmon) { + new_hwmon_device = 0; + goto register_sys_interface; + } + hwmon = kzalloc_obj(*hwmon); if (!hwmon) return -ENOMEM; - /* - * Append the thermal zone ID preceded by an underline character to the - * type to disambiguate the sensors command output. - */ - scnprintf(hwmon->name, THERMAL_HWMON_NAME_LENGTH, "%s_%d", tz->type, tz->id); - strreplace(hwmon->name, '-', '_'); + INIT_LIST_HEAD(&hwmon->tz_list); + strscpy(hwmon->type, tz->type, THERMAL_NAME_LENGTH); + strreplace(hwmon->type, '-', '_'); hwmon->device = hwmon_device_register_for_thermal(&tz->device, - hwmon->name, hwmon); + hwmon->type, hwmon); if (IS_ERR(hwmon->device)) { result = PTR_ERR(hwmon->device); goto free_mem; } - temp = &hwmon->tz_temp; + register_sys_interface: + temp = kzalloc_obj(*temp); + if (!temp) { + result = -ENOMEM; + goto unregister_name; + } temp->tz = tz; + hwmon->count++; - temp->temp_input.attr.attr.name = "temp1_input"; + snprintf(temp->temp_input.name, sizeof(temp->temp_input.name), + "temp%d_input", hwmon->count); + temp->temp_input.attr.attr.name = temp->temp_input.name; temp->temp_input.attr.attr.mode = 0444; temp->temp_input.attr.show = temp_input_show; sysfs_attr_init(&temp->temp_input.attr.attr); result = device_create_file(hwmon->device, &temp->temp_input.attr); if (result) - goto unregister_name; + goto free_temp_mem; if (thermal_zone_crit_temp_valid(tz)) { - temp->temp_crit.attr.attr.name = "temp1_crit"; + snprintf(temp->temp_crit.name, + sizeof(temp->temp_crit.name), + "temp%d_crit", hwmon->count); + temp->temp_crit.attr.attr.name = temp->temp_crit.name; temp->temp_crit.attr.attr.mode = 0444; temp->temp_crit.attr.show = temp_crit_show; sysfs_attr_init(&temp->temp_crit.attr.attr); @@ -145,17 +196,21 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) temp->temp_crit_present = true; } - /* The list is needed for hwmon lookup during removal. */ mutex_lock(&thermal_hwmon_list_lock); - list_add_tail(&hwmon->node, &thermal_hwmon_list); + if (new_hwmon_device) + list_add_tail(&hwmon->node, &thermal_hwmon_list); + list_add_tail(&temp->hwmon_node, &hwmon->tz_list); mutex_unlock(&thermal_hwmon_list_lock); return 0; unregister_input: device_remove_file(hwmon->device, &temp->temp_input.attr); + free_temp_mem: + kfree(temp); unregister_name: - hwmon_device_unregister(hwmon->device); + if (new_hwmon_device) + hwmon_device_unregister(hwmon->device); free_mem: kfree(hwmon); @@ -163,37 +218,39 @@ int thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) } EXPORT_SYMBOL_GPL(thermal_add_hwmon_sysfs); -static struct thermal_hwmon_device * -thermal_hwmon_lookup(const struct thermal_zone_device *tz) -{ - struct thermal_hwmon_device *hwmon; - - list_for_each_entry(hwmon, &thermal_hwmon_list, node) { - if (hwmon->tz_temp.tz == tz) - return hwmon; - } - return NULL; -} - void thermal_remove_hwmon_sysfs(struct thermal_zone_device *tz) { struct thermal_hwmon_device *hwmon; struct thermal_hwmon_temp *temp; - scoped_guard(mutex, &thermal_hwmon_list_lock) { - hwmon = thermal_hwmon_lookup(tz); - if (!hwmon) - return; - - list_del(&hwmon->node); + hwmon = thermal_hwmon_lookup_by_type(tz); + if (unlikely(!hwmon)) { + /* Should never happen... */ + dev_dbg(&tz->device, "hwmon device lookup failed!\n"); + return; } - temp = &hwmon->tz_temp; + temp = thermal_hwmon_lookup_temp(hwmon, tz); + if (unlikely(!temp)) { + /* Should never happen... */ + dev_dbg(&tz->device, "temperature input lookup failed!\n"); + return; + } device_remove_file(hwmon->device, &temp->temp_input.attr); if (temp->temp_crit_present) device_remove_file(hwmon->device, &temp->temp_crit.attr); + mutex_lock(&thermal_hwmon_list_lock); + list_del(&temp->hwmon_node); + kfree(temp); + if (!list_empty(&hwmon->tz_list)) { + mutex_unlock(&thermal_hwmon_list_lock); + return; + } + list_del(&hwmon->node); + mutex_unlock(&thermal_hwmon_list_lock); + hwmon_device_unregister(hwmon->device); kfree(hwmon); } From e9146fcfc7c6d8542f54831a8fff7123c7e57aef Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 25 Feb 2025 17:42:45 +0800 Subject: [PATCH 002/258] MARKER: FROMLIST: Start of Lemote patches Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 3799140b031a7f..45f411301903e9 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -550,6 +550,7 @@ config MACH_LOONGSON64 select USE_OF select BUILTIN_DTB select PCI_HOST_GENERIC + depends on BROKEN help This enables the support of Loongson-2/3 family of machines. From a451cd6e98796478806142735a1c66a234749ba2 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 19 Dec 2018 16:31:14 +0800 Subject: [PATCH 003/258] FROMLIST: scsi: lpfc: Switch memcpy_fromio() to __read32_copy() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit bc73905abf770192 ("[SCSI] lpfc 8.3.16: SLI Additions, updates, and code cleanup"), lpfc_memcpy_to_slim() have switched memcpy_toio() to __write32_copy() in order to prevent unaligned 64 bit copy. Recently, we found that lpfc_memcpy_from_slim() have similar issues, so let it switch memcpy_fromio() to __read32_copy(). As maintainer says, it seems that we can hardly see a real "unaligned 64 bit copy", but this patch is still useful. Because in our tests we found that lpfc doesn't support 128 bit access, but some optimized memcpy() use 128 bit access (at lease on Loongson). Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen Signed-off-by: James Smart  Link: https://lore.kernel.org/all/1545208274-13736-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/scsi/lpfc/lpfc_compat.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h index 43cf46a3a71fea..0cd1e3c82d8759 100644 --- a/drivers/scsi/lpfc/lpfc_compat.h +++ b/drivers/scsi/lpfc/lpfc_compat.h @@ -91,8 +91,8 @@ lpfc_memcpy_to_slim( void __iomem *dest, void *src, unsigned int bytes) static inline void lpfc_memcpy_from_slim( void *dest, void __iomem *src, unsigned int bytes) { - /* actually returns 1 byte past dest */ - memcpy_fromio( dest, src, bytes); + /* convert bytes in argument list to word count for copy function */ + __ioread32_copy(dest, src, bytes / sizeof(uint32_t)); } #endif /* __BIG_ENDIAN */ From 8f738194a8339ab32f66b1adbb7ae7ece297a59a Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 6 Feb 2019 20:03:14 +0800 Subject: [PATCH 004/258] FROMLIST: MIPS: math-emu: Add madd/msub/nmadd/nmsub emulation for Loongson-3 Add madd.s/madd.d/msub.s/msub.d/nmadd.s/nmadd.d/nmsub.s/nmsub.d emulation for Loongson-3. MIPS R2 suggest these instructions be unfused, but Loongson-3 suggest these instructions be fused, which is similar to maddf/msubf in MIPS R6. Signed-off-by: Huacai Chen Signed-off-by: Pei Huang Link: https://lore.kernel.org/all/1549454594-9056-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/math-emu/cp1emu.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index c89e70df43d82b..cc0360e8dd62ca 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -1451,6 +1451,7 @@ static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); } +#ifndef CONFIG_CPU_LOONGSON3 DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); @@ -1459,6 +1460,7 @@ DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, ); DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, ); DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); +#endif static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, mips_instruction ir, void __user **fault_addr) @@ -1513,6 +1515,20 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; +#ifdef CONFIG_CPU_LOONGSON3 + case madd_s_op: + handler = ieee754sp_madd; + goto scoptop; + case msub_s_op: + handler = ieee754sp_msub; + goto scoptop; + case nmadd_s_op: + handler = ieee754sp_nmadd; + goto scoptop; + case nmsub_s_op: + handler = ieee754sp_nmsub; + goto scoptop; +#else case madd_s_op: if (cpu_has_mac2008_only) handler = ieee754sp_madd; @@ -1537,6 +1553,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, else handler = fpemu_sp_nmsub; goto scoptop; +#endif scoptop: SPFROMREG(fr, MIPSInst_FR(ir)); @@ -1621,6 +1638,20 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; +#ifdef CONFIG_CPU_LOONGSON3 + case madd_d_op: + handler = ieee754dp_madd; + goto dcoptop; + case msub_d_op: + handler = ieee754dp_msub; + goto dcoptop; + case nmadd_d_op: + handler = ieee754dp_nmadd; + goto dcoptop; + case nmsub_d_op: + handler = ieee754dp_nmsub; + goto dcoptop; +#else case madd_d_op: if (cpu_has_mac2008_only) handler = ieee754dp_madd; @@ -1645,6 +1676,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, else handler = fpemu_dp_nmsub; goto dcoptop; +#endif dcoptop: DPFROMREG(fr, MIPSInst_FR(ir)); From 3f9e1ada24d1ed39333fe558b9554cff488ff76d Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 12 Mar 2020 17:40:53 +0800 Subject: [PATCH 005/258] BACKPORT: FROMLIST: MIPS: Add __cpu_full_name[] to make CPU names more human-readable In /proc/cpuinfo, we keep "cpu model" as is, since GCC should use it for -march=native. Besides, we add __cpu_full_name[] to describe the processor in a more human-readable manner. The full name is displayed as "model name" in cpuinfo, which is needed by some userspace tools such as "lscpu" and "gnome-system-monitor". The CPU frequency in "model name" is the default value (highest), and there is also a "CPU MHz" whose value can be changed by cpufreq. This is only used by Loongson now (ICT is dropped in cpu name, and cpu name can be overwritten by BIOS). Why drop ICT? At the beginning, Loongson is designed by ICT, but now all Loongson processors is designed by "Loongson Technology Corporation Limited" which is independent from ICT. We have search the code in https://codesearch.debian.net/, and the result is: 1, GCC searches the "cpu model" in cpuinfo, but it only matches "Loongson-2 V0.2" and so on, so drop "ICT" is comfortable; 2, Debian Installer searches the "cpu model" in cpuinfo and matches "ICT Loongson", but Yunqiang Su is modifying the code; 3, Valgrind searches the "cpu model" in cpuinfo and matches "ICT Loongson", and we are planning to fix it up. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1584006053-28887-1-git-send-email-chenhc@lemote.com/ [Kexy: Resolved minor conflicts in arch/mips/kernel/cpu-probe.c, arch/mips/loongson64/env.c, and arch/mips/loongson64/smp.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/include/asm/cpu-info.h | 2 ++ .../include/asm/mach-loongson64/boot_param.h | 1 + arch/mips/include/asm/time.h | 2 ++ arch/mips/kernel/cpu-probe.c | 30 ++++++++++++++----- arch/mips/kernel/proc.c | 6 ++++ arch/mips/kernel/time.c | 2 ++ arch/mips/loongson64/env.c | 13 ++++++++ arch/mips/loongson64/smp.c | 1 + arch/mips/loongson64/smp.h | 1 + 9 files changed, 51 insertions(+), 7 deletions(-) diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h index 211b578af6aa0a..b7b60a84beb056 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -124,7 +124,9 @@ extern void cpu_report(void); extern void cpu_disable_mmid(void); extern const char *__cpu_name[]; +extern const char *__cpu_full_name[]; #define cpu_name_string() __cpu_name[raw_smp_processor_id()] +#define cpu_full_name_string() __cpu_full_name[raw_smp_processor_id()] struct seq_file; struct notifier_block; diff --git a/arch/mips/include/asm/mach-loongson64/boot_param.h b/arch/mips/include/asm/mach-loongson64/boot_param.h index 3a11ce85762be6..943c5762207324 100644 --- a/arch/mips/include/asm/mach-loongson64/boot_param.h +++ b/arch/mips/include/asm/mach-loongson64/boot_param.h @@ -66,6 +66,7 @@ struct efi_cpuinfo_loongson { u16 reserved_cores_mask; u32 cpu_clock_freq; /* cpu_clock */ u32 nr_cpus; + char cpuname[64]; } __packed; #define MAX_UARTS 64 diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index 5e7193b759f337..93f8cd52e3d471 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h @@ -22,6 +22,8 @@ extern spinlock_t rtc_lock; */ extern void plat_time_init(void); +extern unsigned int mips_cpu_frequency; + /* * mips_hpt_frequency - must be set if you intend to use an R4k-compatible * counter as a timer interrupt source. diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 489612ed9d4987..88bae0ca721ca8 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -1253,32 +1253,44 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) switch (c->processor_id & PRID_REV_MASK) { case PRID_REV_LOONGSON2E: c->cputype = CPU_LOONGSON2EF; - __cpu_name[cpu] = "ICT Loongson-2"; + __cpu_name[cpu] = "Loongson-2"; set_elf_platform(cpu, "loongson2e"); set_isa(c, MIPS_CPU_ISA_III); c->fpu_msk31 |= FPU_CSR_CONDX; + __cpu_full_name[cpu] = "Loongson-2E"; break; case PRID_REV_LOONGSON2F: c->cputype = CPU_LOONGSON2EF; - __cpu_name[cpu] = "ICT Loongson-2"; + __cpu_name[cpu] = "Loongson-2"; set_elf_platform(cpu, "loongson2f"); set_isa(c, MIPS_CPU_ISA_III); c->fpu_msk31 |= FPU_CSR_CONDX; + __cpu_full_name[cpu] = "Loongson-2F"; break; case PRID_REV_LOONGSON3A_R1: c->cputype = CPU_LOONGSON64; - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R1); + __cpu_full_name[cpu] = "Loongson-3A R1 (Loongson-3A1000)"; c->ases |= (MIPS_ASE_LOONGSON_MMI | MIPS_ASE_LOONGSON_CAM | MIPS_ASE_LOONGSON_EXT); break; case PRID_REV_LOONGSON3B_R1: + c->cputype = CPU_LOONGSON64; + __cpu_name[cpu] = "Loongson-3"; + set_elf_platform(cpu, "loongson3b"); + set_isa(c, MIPS_CPU_ISA_M64R1); + __cpu_full_name[cpu] = "Loongson-3B R1 (Loongson-3B1000)"; + c->ases |= (MIPS_ASE_LOONGSON_MMI | MIPS_ASE_LOONGSON_CAM | + MIPS_ASE_LOONGSON_EXT); + break; case PRID_REV_LOONGSON3B_R2: c->cputype = CPU_LOONGSON64; - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3b"); set_isa(c, MIPS_CPU_ISA_M64R1); + __cpu_full_name[cpu] = "Loongson-3B R2 (Loongson-3B1500)"; c->ases |= (MIPS_ASE_LOONGSON_MMI | MIPS_ASE_LOONGSON_CAM | MIPS_ASE_LOONGSON_EXT); break; @@ -1706,15 +1718,17 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) switch (c->processor_id & PRID_REV_MASK) { case PRID_REV_LOONGSON3A_R2_0: case PRID_REV_LOONGSON3A_R2_1: - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R2); + __cpu_full_name[cpu] = "Loongson-3A R2 (Loongson-3A2000)"; break; case PRID_REV_LOONGSON3A_R3_0: case PRID_REV_LOONGSON3A_R3_1: - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R2); + __cpu_full_name[cpu] = "Loongson-3A R3 (Loongson-3A3000)"; break; } /* @@ -1733,9 +1747,10 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) LOONGSON_CONF6_INTIMER); break; case PRID_IMP_LOONGSON_64G: - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R2); + __cpu_full_name[cpu] = "Loongson-3A R4 (Loongson-3A4000)"; decode_cpucfg(c); change_c0_config6(LOONGSON_CONF6_EXTIMER | LOONGSON_CONF6_INTIMER, LOONGSON_CONF6_INTIMER); @@ -1841,6 +1856,7 @@ EXPORT_SYMBOL(__ua_limit); #endif const char *__cpu_name[NR_CPUS]; +const char *__cpu_full_name[NR_CPUS]; const char *__elf_platform; const char *__elf_base_platform; diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 8f0a0001540c7b..9b2e85a2a932e6 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -15,6 +15,7 @@ #include #include #include +#include unsigned int vced_count, vcei_count; @@ -63,6 +64,11 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_printf(m, fmt, __cpu_name[n], (version >> 4) & 0x0f, version & 0x0f, (fp_vers >> 4) & 0x0f, fp_vers & 0x0f); + if (__cpu_full_name[n]) + seq_printf(m, "model name\t\t: %s\n", __cpu_full_name[n]); + if (mips_cpu_frequency) + seq_printf(m, "CPU MHz\t\t\t: %u.%02u\n", + mips_cpu_frequency / 1000000, (mips_cpu_frequency / 10000) % 100); seq_printf(m, "BogoMIPS\t\t: %u.%02u\n", cpu_data[n].udelay_val / (500000/HZ), (cpu_data[n].udelay_val / (5000/HZ)) % 100); diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index ed339d7979f3f5..849c6be53b2968 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -120,6 +120,8 @@ EXPORT_SYMBOL(perf_irq); * 2) calculate a couple of cached variables for later usage */ +unsigned int mips_cpu_frequency; +EXPORT_SYMBOL_GPL(mips_cpu_frequency); unsigned int mips_hpt_frequency; EXPORT_SYMBOL_GPL(mips_hpt_frequency); diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c index 7abcca7ab4ed44..46bc2ffac652c0 100644 --- a/arch/mips/loongson64/env.c +++ b/arch/mips/loongson64/env.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ u32 cpu_clock_freq; EXPORT_SYMBOL(cpu_clock_freq); +char cpu_full_name[64]; struct efi_memory_map_loongson *loongson_memmap; struct loongson_system_configuration loongson_sysconf; @@ -171,6 +173,7 @@ static void __init lefi_fixup_fdt(struct system_loongson *system) void __init prom_lefi_init_env(void) { + char freq[12]; struct boot_params *boot_p; struct loongson_params *loongson_p; struct system_loongson *esys; @@ -265,6 +268,10 @@ void __init prom_lefi_init_env(void) loongson_sysconf.nr_nodes = (loongson_sysconf.nr_cpus + loongson_sysconf.cores_per_node - 1) / loongson_sysconf.cores_per_node; + if (!strncmp(ecpu->cpuname, "Loongson", 8)) + strncpy(cpu_full_name, ecpu->cpuname, sizeof(cpu_full_name)); + if (cpu_full_name[0] == 0) + strncpy(cpu_full_name, __cpu_full_name[0], sizeof(cpu_full_name)); loongson_sysconf.dma_mask_bits = eirq_source->dma_mask_bits; if (loongson_sysconf.dma_mask_bits < 32 || @@ -288,8 +295,14 @@ void __init prom_lefi_init_env(void) loongson_sysconf.workarounds |= esys->workarounds; + mips_cpu_frequency = cpu_clock_freq; pr_info("CpuClock = %u\n", cpu_clock_freq); + /* Append default cpu frequency with round-off */ + sprintf(freq, " @ %uMHz", (cpu_clock_freq + 500000) / 1000000); + strncat(cpu_full_name, freq, sizeof(cpu_full_name)); + __cpu_full_name[0] = cpu_full_name; + /* Read the ID of PCI host bridge to detect bridge type */ id = readl(HOST_BRIDGE_CONFIG_ADDR); vendor = id & 0xffff; diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c index e584299d0fde8e..c4e97a59df3055 100644 --- a/arch/mips/loongson64/smp.c +++ b/arch/mips/loongson64/smp.c @@ -428,6 +428,7 @@ static void loongson3_init_secondary(void) cpu_logical_map(cpu) % loongson_sysconf.cores_per_package); cpu_data[cpu].package = cpu_logical_map(cpu) / loongson_sysconf.cores_per_package; + __cpu_full_name[cpu] = cpu_full_name; } static void loongson3_smp_finish(void) diff --git a/arch/mips/loongson64/smp.h b/arch/mips/loongson64/smp.h index 957bde81e0e4e5..6112d9d7dd2268 100644 --- a/arch/mips/loongson64/smp.h +++ b/arch/mips/loongson64/smp.h @@ -3,6 +3,7 @@ #define __LOONGSON_SMP_H_ /* for Loongson-3 smp support */ +extern char cpu_full_name[64]; extern unsigned long long smp_group[4]; /* 4 groups(nodes) in maximum in numa case */ From 39c8e8f7657b520450f37cb7cf2af9dd571607fb Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 12 Mar 2020 17:41:57 +0800 Subject: [PATCH 006/258] FROMLIST: MIPS: Loongson: Add board_ebase_setup() support The EBase registers of old Loongson processor models before 3A2000 are 32bit and have no WG bit; those of newer models are 64bit and do have the WG bit. Unfortunately, dynamically allocated EBase addresses do not work well for the Loongson platform, because Loongson's memory layout is very limited below 0x20000000. The dynamically allocated EBase address above 0x20000000 is thus unmappable to a KSEG0/KSEG1 virtual address, but the cache error handler MUST be in KSEG1 (please see set_uncached_handler() in traps.c). Some might suggest that the cache error handler is hardly used so this is not a problem, but Loongson's MMIO configuration registers might be corrupted by set_uncached_handler(). To make Linux kernel on Loongson more robust, a board_ebase_setup() hook is added to ensure CKSEG0 is always used for EBase. This is also useful for configurations where firmware-provided EBase is not sane. Maybe this problem is present for all MIPSr2 processors, but it seems not all platforms have memory at physical address 0. So this patch only touches Loongson. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1584006117-28985-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/init.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/mips/loongson64/init.c b/arch/mips/loongson64/init.c index c7cc5a3d7817f1..9205195acca0b2 100644 --- a/arch/mips/loongson64/init.c +++ b/arch/mips/loongson64/init.c @@ -23,6 +23,16 @@ u32 node_id_offset; +static void __init mips_ebase_setup(void) +{ + ebase = CKSEG0; + + if (cpu_has_ebase_wg) + write_c0_ebase(ebase | MIPS_EBASE_WG); + + write_c0_ebase(ebase); +} + static void __init mips_nmi_setup(void) { void *base; @@ -147,6 +157,7 @@ void __init prom_init(void) setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE + 0x1e0), 0, 1024); register_smp_ops(&loongson3_smp_ops); + board_ebase_setup = mips_ebase_setup; board_nmi_handler_setup = mips_nmi_setup; } From 31d2353c54a2c4dcaca8629e3bc1e958cede7321 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 1 Nov 2020 11:33:58 +0800 Subject: [PATCH 007/258] FROMLIST: MIPS: Loongson64: Enlarge cross-package node distance NUMA node distances affect the NUMA balancing behaviors. The cost of cross-package memory access is very high, and our benchmarks show that 200 is a more appropriate value than 100 (for cross-package numa node distance) on Loongson64 platforms, so enlarge it. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1604201638-4001-3-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/numa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/numa.c b/arch/mips/loongson64/numa.c index 16ffb32cca5082..ae8f3758a27207 100644 --- a/arch/mips/loongson64/numa.c +++ b/arch/mips/loongson64/numa.c @@ -60,7 +60,7 @@ static int __init compute_node_distance(int row, int col) else if (package_row == package_col) return 40; else - return 100; + return 200; } static void __init init_topology_matrix(void) From b71e29e02adf70a37e616174e471821180f2baaf Mon Sep 17 00:00:00 2001 From: wangrui Date: Thu, 8 Apr 2021 19:30:59 +0800 Subject: [PATCH 008/258] BACKPORT: FROMLIST: MIPS: tlbex: Avoid access invalid address when pmd is modifying When user-space program accessing a virtual address and falls into TLB invalid exception handling. at almost the same time, if the pmd which that contains this virtual address is hit by THP scanning, and then a invalid address access may occurs in the tlb handler: CPU 0: (userspace) | CPU 1: (khugepaged) 1: | scan hit: set pmde to invalid_pmd_table | (by pmd_clear) 2: handle_tlbl(tlb invalid): | load pmde for huge page testing, | pmde doesn't contains _PAGE_HUGE | bit | 3: | collapsed: set pmde to huge page format 4: handle_tlbl(normal page case): | load pmde again as base address, | pmde doesn't contains an address, | access invalid address | This patch avoids the inconsistency of two memory loads by reusing the result of one load. Signed-off-by: wangrui Link: https://lore.kernel.org/all/20210212082058.40792-1-wangrui@loongson.cn/ Link: https://lore.kernel.org/all/CAAhV-H5a6f7SSaMfOJudFuqtzLqkDks_NkjsPe15gN3mJTGBDw@mail.gmail.com/ [Kexy: Resolved minor conflict in arch/mips/mm/tlbex.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/mm/tlbex.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index 69ea54bdc0c361..33f613d6665b8b 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -676,13 +676,12 @@ static void build_huge_tlb_write_entry(u32 **p, struct uasm_label **l, */ static void build_is_huge_pte(u32 **p, struct uasm_reloc **r, unsigned int tmp, - unsigned int pmd, int lid) + unsigned int pmde, int lid) { - UASM_i_LW(p, tmp, 0, pmd); if (use_bbit_insns()) { - uasm_il_bbit1(p, r, tmp, ilog2(_PAGE_HUGE), lid); + uasm_il_bbit1(p, r, pmde, ilog2(_PAGE_HUGE), lid); } else { - uasm_i_andi(p, tmp, tmp, _PAGE_HUGE); + uasm_i_andi(p, tmp, pmde, _PAGE_HUGE); uasm_il_bnez(p, r, tmp, lid); } } @@ -1042,7 +1041,6 @@ EXPORT_SYMBOL_GPL(build_update_entries); struct mips_huge_tlb_info { int huge_pte; int restore_scratch; - bool need_reload_pte; }; static struct mips_huge_tlb_info @@ -1057,7 +1055,6 @@ build_fast_tlb_refill_handler (u32 **p, struct uasm_label **l, rv.huge_pte = scratch; rv.restore_scratch = 0; - rv.need_reload_pte = false; if (check_for_high_segbits) { UASM_i_MFC0(p, tmp, C0_BADVADDR); @@ -1263,7 +1260,6 @@ static void build_r4000_tlb_refill_handler(void) } else { htlb_info.huge_pte = GPR_K0; htlb_info.restore_scratch = 0; - htlb_info.need_reload_pte = true; vmalloc_mode = refill_noscratch; /* * create the plain linear handler @@ -1288,11 +1284,14 @@ static void build_r4000_tlb_refill_handler(void) build_get_pgde32(&p, GPR_K0, GPR_K1); /* get pgd in GPR_K1 */ #endif + UASM_i_LW(&p, GPR_K0, 0, GPR_K1); /* get pmd entry in GPR_K0 */ #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT - build_is_huge_pte(&p, &r, GPR_K0, GPR_K1, label_tlb_huge_update); + build_is_huge_pte(&p, &r, GPR_K1, GPR_K0, label_tlb_huge_update); #endif - build_get_ptep(&p, GPR_K0, GPR_K1); + GET_CONTEXT(&p, GPR_K1); /* get context reg */ + build_adjust_context(&p, GPR_K1); + UASM_i_ADDU(&p, GPR_K1, GPR_K0, GPR_K1); /* add in offset */ build_update_entries(&p, GPR_K0, GPR_K1); build_tlb_write_entry(&p, &l, &r, tlb_random); uasm_l_leave(&l, p); @@ -1300,8 +1299,6 @@ static void build_r4000_tlb_refill_handler(void) } #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT uasm_l_tlb_huge_update(&l, p); - if (htlb_info.need_reload_pte) - UASM_i_LW(&p, htlb_info.huge_pte, 0, GPR_K1); build_huge_update_entries(&p, htlb_info.huge_pte, GPR_K1); build_huge_tlb_write_entry(&p, &l, &r, GPR_K0, tlb_random, htlb_info.restore_scratch); @@ -2001,20 +1998,20 @@ build_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l, build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */ #endif + UASM_i_LW(p, wr.r3, 0, wr.r2); /* get pmd entry in wr.r3 */ #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT /* - * For huge tlb entries, pmd doesn't contain an address but + * For huge tlb entries, pmde doesn't contain an address but * instead contains the tlb pte. Check the PAGE_HUGE bit and * see if we need to jump to huge tlb processing. */ - build_is_huge_pte(p, r, wr.r1, wr.r2, label_tlb_huge_update); + build_is_huge_pte(p, r, wr.r1, wr.r3, label_tlb_huge_update); #endif UASM_i_MFC0(p, wr.r1, C0_BADVADDR); - UASM_i_LW(p, wr.r2, 0, wr.r2); UASM_i_SRL(p, wr.r1, wr.r1, PAGE_SHIFT - PTE_T_LOG2); uasm_i_andi(p, wr.r1, wr.r1, (PTRS_PER_PTE - 1) << PTE_T_LOG2); - UASM_i_ADDU(p, wr.r2, wr.r2, wr.r1); + UASM_i_ADDU(p, wr.r2, wr.r3, wr.r1); #ifdef CONFIG_SMP uasm_l_smp_pgtable_change(l, *p); From 9a8ce7a0e3971a968a98772e85a445a4c1601d01 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 25 Feb 2025 17:43:01 +0800 Subject: [PATCH 009/258] MARKER: FROMLIST: End of Lemote patches Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 45f411301903e9..3799140b031a7f 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -550,7 +550,6 @@ config MACH_LOONGSON64 select USE_OF select BUILTIN_DTB select PCI_HOST_GENERIC - depends on BROKEN help This enables the support of Loongson-2/3 family of machines. From c641dad2384194acf2d8503b5ce851ba28dbccbc Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 29 Oct 2020 16:29:11 +0800 Subject: [PATCH 010/258] LOONGSON: LoongArch: Add CPU HWMon platform driver This add CPU HWMon (temperature sensor) platform driver for Loongson-3. Tested-by: Xi Ruoyao Signed-off-by: Huacai Chen Link: https://github.com/chenhuacai/linux/commit/2a6c1c74d93a21613a523aebc6494d654f35cf1a Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/platform/loongarch/Kconfig | 8 + drivers/platform/loongarch/Makefile | 1 + drivers/platform/loongarch/cpu_hwmon.c | 194 +++++++++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 drivers/platform/loongarch/cpu_hwmon.c diff --git a/drivers/platform/loongarch/Kconfig b/drivers/platform/loongarch/Kconfig index 447528797d07aa..b87f51c4e4b662 100644 --- a/drivers/platform/loongarch/Kconfig +++ b/drivers/platform/loongarch/Kconfig @@ -16,6 +16,14 @@ menuconfig LOONGARCH_PLATFORM_DEVICES if LOONGARCH_PLATFORM_DEVICES +config CPU_HWMON + bool "Loongson CPU HWMon Driver" + depends on MACH_LOONGSON64 + select HWMON + default y + help + Loongson-3A/3B/3C CPU HWMon (temperature sensor) driver. + config LOONGSON_LAPTOP tristate "Generic Loongson-3 Laptop Driver" depends on ACPI_EC diff --git a/drivers/platform/loongarch/Makefile b/drivers/platform/loongarch/Makefile index f43ab03db1a2d5..8038291bae3aa8 100644 --- a/drivers/platform/loongarch/Makefile +++ b/drivers/platform/loongarch/Makefile @@ -1 +1,2 @@ +obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o obj-$(CONFIG_LOONGSON_LAPTOP) += loongson-laptop.o diff --git a/drivers/platform/loongarch/cpu_hwmon.c b/drivers/platform/loongarch/cpu_hwmon.c new file mode 100644 index 00000000000000..93a05993745aaa --- /dev/null +++ b/drivers/platform/loongarch/cpu_hwmon.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Loongson Technology Corporation Limited + */ +#include +#include +#include +#include +#include + +#include + +static int nr_packages; +static struct device *cpu_hwmon_dev; + +static int loongson3_cpu_temp(int cpu) +{ + u32 reg; + + reg = iocsr_read32(LOONGARCH_IOCSR_CPUTEMP) & 0xff; + + return (int)((s8)reg) * 1000; +} + +static ssize_t cpu_temp_label(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + return sprintf(buf, "CPU %d Temperature\n", id); +} + +static ssize_t get_cpu_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + int value = loongson3_cpu_temp(id); + return sprintf(buf, "%d\n", value); +} + +static SENSOR_DEVICE_ATTR(temp1_input, 0444, get_cpu_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_label, 0444, cpu_temp_label, NULL, 1); +static SENSOR_DEVICE_ATTR(temp2_input, 0444, get_cpu_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_label, 0444, cpu_temp_label, NULL, 2); +static SENSOR_DEVICE_ATTR(temp3_input, 0444, get_cpu_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp3_label, 0444, cpu_temp_label, NULL, 3); +static SENSOR_DEVICE_ATTR(temp4_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp4_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp5_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp5_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp6_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp7_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp7_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp8_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp8_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp9_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp9_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp10_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp10_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp11_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp11_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp12_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp12_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp13_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp13_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp14_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp14_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp15_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp15_label, 0444, cpu_temp_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp16_input, 0444, get_cpu_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp16_label, 0444, cpu_temp_label, NULL, 4); + +static struct attribute *cpu_hwmon_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp5_label.dev_attr.attr, + &sensor_dev_attr_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp6_label.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp7_label.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp8_label.dev_attr.attr, + &sensor_dev_attr_temp9_input.dev_attr.attr, + &sensor_dev_attr_temp9_label.dev_attr.attr, + &sensor_dev_attr_temp10_input.dev_attr.attr, + &sensor_dev_attr_temp10_label.dev_attr.attr, + &sensor_dev_attr_temp11_input.dev_attr.attr, + &sensor_dev_attr_temp11_label.dev_attr.attr, + &sensor_dev_attr_temp12_input.dev_attr.attr, + &sensor_dev_attr_temp12_label.dev_attr.attr, + &sensor_dev_attr_temp13_input.dev_attr.attr, + &sensor_dev_attr_temp13_label.dev_attr.attr, + &sensor_dev_attr_temp14_input.dev_attr.attr, + &sensor_dev_attr_temp14_label.dev_attr.attr, + &sensor_dev_attr_temp15_input.dev_attr.attr, + &sensor_dev_attr_temp15_label.dev_attr.attr, + &sensor_dev_attr_temp16_input.dev_attr.attr, + &sensor_dev_attr_temp16_label.dev_attr.attr, + NULL +}; +static umode_t cpu_hwmon_is_visible(struct kobject *kobj, + struct attribute *attr, int i) +{ + int id = i / 2; + + if (id < nr_packages) + return attr->mode; + return 0; +} + +static struct attribute_group cpu_hwmon_group = { + .attrs = cpu_hwmon_attributes, + .is_visible = cpu_hwmon_is_visible, +}; + +static const struct attribute_group *cpu_hwmon_groups[] = { + &cpu_hwmon_group, + NULL +}; + +static int cpu_initial_threshold = 72000; +static int cpu_thermal_threshold = 96000; +module_param(cpu_thermal_threshold, int, 0644); +MODULE_PARM_DESC(cpu_thermal_threshold, "cpu thermal threshold (96000 (default))"); + +static struct delayed_work thermal_work; + +static void do_thermal_timer(struct work_struct *work) +{ + int i, value, temp_max = 0; + + for (i=0; i temp_max) + temp_max = value; + } + + if (temp_max <= cpu_thermal_threshold) + schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000)); + else + orderly_poweroff(true); +} + +static int __init loongson_hwmon_init(void) +{ + int i, value, temp_max = 0; + + pr_info("Loongson Hwmon Enter...\n"); + + nr_packages = loongson_sysconf.nr_cpus / + loongson_sysconf.cores_per_package; + + cpu_hwmon_dev = hwmon_device_register_with_groups(NULL, "cpu_hwmon", + NULL, cpu_hwmon_groups); + if (IS_ERR(cpu_hwmon_dev)) { + pr_err("Hwmon register fail with %ld!\n", PTR_ERR(cpu_hwmon_dev)); + return PTR_ERR(cpu_hwmon_dev); + } + + for (i = 0; i < nr_packages; i++) { + value = loongson3_cpu_temp(i); + if (value > temp_max) + temp_max = value; + } + + pr_info("Initial CPU temperature is %d (highest).\n", temp_max); + if (temp_max > cpu_initial_threshold) + cpu_thermal_threshold += temp_max - cpu_initial_threshold; + + INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer); + schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000)); + + return 0; +} + +static void __exit loongson_hwmon_exit(void) +{ + cancel_delayed_work_sync(&thermal_work); + hwmon_device_unregister(cpu_hwmon_dev); +} + +module_init(loongson_hwmon_init); +module_exit(loongson_hwmon_exit); + +MODULE_AUTHOR("Huacai Chen "); +MODULE_DESCRIPTION("Loongson CPU Hwmon driver"); +MODULE_LICENSE("GPL"); From ce287a98ae3b07e9e11fec3fb5c8df4385f630ed Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 28 Jan 2024 14:07:46 +0800 Subject: [PATCH 011/258] LOONGSON: drivers/firmware: Move sysfb_init() from device_initcall to fs_initcall Consider a configuration like this: 1, efifb (or simpledrm) is built-in; 2, a native display driver (such as radeon) is also built-in. As Javier said, this is not a common configuration (the native display driver is usually built as a module), but it can happen and cause some trouble. In this case, since efifb, radeon and sysfb are all in device_initcall() level, the order in practise is like this: efifb registered at first, but no "efi-framebuffer" device yet. radeon registered later, and /dev/fb0 created. sysfb_init() comes at last, it registers "efi-framebuffer" and then causes an error message "efifb: a framebuffer is already registered". Make sysfb_init() to be subsys_ initcall_sync() can avoid this. And Javier Martinez Canillas is trying to make a more general solution in commit 873eb3b11860 ("fbdev: Disable sysfb device registration when removing conflicting FBs"). However, this patch still makes sense because it can make the screen display as early as possible (We cannot move to subsys_initcall, since sysfb_init() should be executed after PCI enumeration). This is a better version of commit 60aebc9559492cea ("drivers/firmware: Move sysfb_init() from device_initcall to subsys_initcall_sync") since the previous commit leads to blank displays on some systems. The reason is that vgaarb initialization is also a subsys_initcall_sync function so sysfb_disable() is sometimes missed. So here we move sysfb_init() to an fs_initcall function which is ensured after vgaarb initialization. Signed-off-by: Huacai Chen Link: https://github.com/chenhuacai/linux/commit/8bea46f981eab88e6cc92f9ab287db16edff5cf2 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/firmware/sysfb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c index 8833582c188301..7d529d8bed5124 100644 --- a/drivers/firmware/sysfb.c +++ b/drivers/firmware/sysfb.c @@ -222,4 +222,4 @@ static __init int sysfb_init(void) } /* must execute after PCI subsystem for EFI quirks */ -device_initcall(sysfb_init); +fs_initcall(sysfb_init); From 16c04c82dc7e8b7a9ac9c5d837431b237c255c70 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Mon, 22 Feb 2021 10:53:47 +0800 Subject: [PATCH 012/258] LOONGSON: drm/radeon: Workaround radeon driver bug for Loongson Radeon driver can not handle the interrupt is faster than DMA data, so irq handler must update an old ih.rptr value in IH_RB_RPTR register to enable interrupt again when interrupt is faster than DMA data. Signed-off-by: Huacai Chen Signed-off-by: Zhijie Zhang Link: https://github.com/chenhuacai/linux/commit/6266d0082b020ad68a3b3c6f314ba299b9d06d3d Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/radeon/cik.c | 1 + drivers/gpu/drm/radeon/evergreen.c | 1 + drivers/gpu/drm/radeon/r600.c | 1 + drivers/gpu/drm/radeon/si.c | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 51a3e0fc2f56be..0b804e951b7b6f 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -8093,6 +8093,7 @@ int cik_irq_process(struct radeon_device *rdev) if (queue_thermal) schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rptr); atomic_set(&rdev->ih.lock, 0); /* make sure wptr hasn't changed while processing */ diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 3cbc6eedbf6639..ee79eeeb838ed3 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -4927,6 +4927,7 @@ int evergreen_irq_process(struct radeon_device *rdev) if (queue_thermal && rdev->pm.dpm_enabled) schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rptr); atomic_set(&rdev->ih.lock, 0); /* make sure wptr hasn't changed while processing */ diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 8b62f7faa5b99f..88e38fb3f3476b 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -4328,6 +4328,7 @@ int r600_irq_process(struct radeon_device *rdev) if (queue_thermal && rdev->pm.dpm_enabled) schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rptr); atomic_set(&rdev->ih.lock, 0); /* make sure wptr hasn't changed while processing */ diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 26197aceb001cc..2d7eeb781969d1 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -6423,6 +6423,7 @@ int si_irq_process(struct radeon_device *rdev) if (queue_thermal && rdev->pm.dpm_enabled) schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rptr); atomic_set(&rdev->ih.lock, 0); /* make sure wptr hasn't changed while processing */ From 731c8b384ea921bbfb2903bee5e8b52599662e2d Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 21 Jul 2026 12:29:47 +0800 Subject: [PATCH 013/258] LOONGSON: cpufreq: loongson3: Make this drvier depend on MACH_LOONGSON64 32BIT Loongson machines don't have SMC and FreqCtrl registers, so make this drvier depend on MACH_LOONGSON64. Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen Link: https://github.com/chenhuacai/linux/commits/master/2ae9fe9393b63dbd4ca83ed4cceaf287a2df82b5 Signed-off-by: Mingcong Bai --- drivers/cpufreq/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index db83f3365698c9..edc1299098d471 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -288,6 +288,7 @@ endif if LOONGARCH config LOONGSON3_CPUFREQ tristate "Loongson3 CPUFreq Driver" + depends on MACH_LOONGSON64 help This option adds a CPUFreq driver for Loongson processors which support software configurable cpu frequency. From a980a4286fa31894ea371456c1f882eb6205509d Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Mon, 20 Jul 2026 20:25:51 +0800 Subject: [PATCH 014/258] LOONGSON: cpufreq: loongson3: Adjust the width of id and val in smc_message The id field of smc_message is stand for the CPU ID. Traditionally this driver is supposed to used by desktop and laptop productions so 4-bits are enough. But now we have servers that can have as many as 256 cores, so we need 8-bits CPU ID. On the other hand, the val field is usually stand for CPU frequency so 12-bits are enough to represent 4GHz, and for higher frequencies there is an extra bit to extend. In theory, this is a incompatible change, but fortunately the old SMC firmwares are not widely shipped and can be updated on the air, thus we can safely adjust the widths. Cc: stable@vger.kernel.org Signed-off-by: Hongliang Wang Signed-off-by: Huacai Chen Link: https://github.com/chenhuacai/linux/commits/master/19ff9dc80cdd58051a9a5f8669f5eba66cd8a0cb Signed-off-by: Mingcong Bai --- drivers/cpufreq/loongson3_cpufreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/loongson3_cpufreq.c b/drivers/cpufreq/loongson3_cpufreq.c index 1e8715ea1b771d..630f679aa73957 100644 --- a/drivers/cpufreq/loongson3_cpufreq.c +++ b/drivers/cpufreq/loongson3_cpufreq.c @@ -21,9 +21,9 @@ union smc_message { u32 value; struct { - u32 id : 4; + u32 id : 8; u32 info : 4; - u32 val : 16; + u32 val : 12; u32 cmd : 6; u32 extra : 1; u32 complete : 1; From b7c0cb84a60101c0d6747c1ae90f0fc84d3d5114 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Mon, 20 Jul 2026 21:20:29 +0800 Subject: [PATCH 015/258] LOONGSON: cpufreq: loongson3: Replace per-package mutex with per-node Our server productions (e.g. Loongson-3D6000/3E6000) can have multiple nodes in one package and SMC mailboxes are also per-node. So replace the per-package mutex with per-node one. Cc: stable@vger.kernel.org Signed-off-by: Hongliang Wang Signed-off-by: Huacai Chen Link: https://github.com/chenhuacai/linux/commits/master/0aef298d02ef8c4b5d6456ab04b3f42b0102ed01 Signed-off-by: Mingcong Bai --- drivers/cpufreq/loongson3_cpufreq.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/loongson3_cpufreq.c b/drivers/cpufreq/loongson3_cpufreq.c index 630f679aa73957..e3cd78a5ab1874 100644 --- a/drivers/cpufreq/loongson3_cpufreq.c +++ b/drivers/cpufreq/loongson3_cpufreq.c @@ -169,7 +169,7 @@ struct loongson3_freq_data { struct cpufreq_frequency_table table[]; }; -static struct mutex cpufreq_mutex[MAX_PACKAGES]; +static struct mutex cpufreq_mutex[MAX_NUMNODES]; static struct cpufreq_driver loongson3_cpufreq_driver; static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data); @@ -177,14 +177,14 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext { int retries; unsigned int cpu = raw_smp_processor_id(); - unsigned int package = cpu_data[cpu].package; + unsigned int nid = cpu_to_node(cpu); union smc_message msg, last; - mutex_lock(&cpufreq_mutex[package]); + mutex_lock(&cpufreq_mutex[nid]); last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX); if (!last.complete) { - mutex_unlock(&cpufreq_mutex[package]); + mutex_unlock(&cpufreq_mutex[nid]); return -EPERM; } @@ -208,11 +208,11 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext } if (!msg.complete || msg.cmd != CMD_OK) { - mutex_unlock(&cpufreq_mutex[package]); + mutex_unlock(&cpufreq_mutex[nid]); return -EPERM; } - mutex_unlock(&cpufreq_mutex[package]); + mutex_unlock(&cpufreq_mutex[nid]); return msg.val; } @@ -337,7 +337,7 @@ static int loongson3_cpufreq_probe(struct platform_device *pdev) { int i, ret; - for (i = 0; i < MAX_PACKAGES; i++) { + for (i = 0; i < MAX_NUMNODES; i++) { ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]); if (ret) return ret; From 52128c4e9914d20f756d46502888875aee0b025e Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Mon, 20 Jul 2026 21:27:36 +0800 Subject: [PATCH 016/258] LOONGSON: cpufreq: loongson3: Use global physical CPU ID in get/target callbacks Our server productions (e.g. Loongson-3D6000/3E6000) can have discrete global physical CPU IDs while the core ID inside the packages are always continuous. In these cases we should use global physical CPU IDs to get and set frequencies. Cc: stable@vger.kernel.org Signed-off-by: Hongliang Wang Signed-off-by: Huacai Chen Link: https://github.com/chenhuacai/linux/commits/master/227212863d7696a7952d4e6c28afe0e6e9eb9f53 Signed-off-by: Mingcong Bai --- drivers/cpufreq/loongson3_cpufreq.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/loongson3_cpufreq.c b/drivers/cpufreq/loongson3_cpufreq.c index e3cd78a5ab1874..c75c0e30e88126 100644 --- a/drivers/cpufreq/loongson3_cpufreq.c +++ b/drivers/cpufreq/loongson3_cpufreq.c @@ -219,38 +219,40 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext static unsigned int loongson3_cpufreq_get(unsigned int cpu) { - int ret; + int ret, core = cpu_logical_map(cpu); - ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0); + ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0); return ret * KILO; } static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index) { - int ret; + int ret, core = cpu_logical_map(policy->cpu); - ret = do_service_request(cpu_data[policy->cpu].core, - FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0); + ret = do_service_request(core, FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, + index, 0); return (ret >= 0) ? 0 : ret; } static int configure_freq_table(int cpu) { - int i, ret, boost_level, max_level, freq_level; + int i, ret, core, boost_level, max_level, freq_level; struct platform_device *pdev = cpufreq_get_driver_data(); struct loongson3_freq_data *data; if (per_cpu(freq_data, cpu)) return 0; - ret = do_service_request(cpu, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0); + core = cpu_logical_map(cpu); + + ret = do_service_request(core, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0); if (ret < 0) return ret; max_level = ret; - ret = do_service_request(cpu, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0); + ret = do_service_request(core, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0); if (ret < 0) return ret; boost_level = ret; @@ -263,7 +265,7 @@ static int configure_freq_table(int cpu) data->def_freq_level = boost_level - 1; for (i = 0; i < freq_level; i++) { - ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0); + ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0); if (ret < 0) { devm_kfree(&pdev->dev, data); return ret; From 099994de58f41476bd3e13f368e82bc0783be72b Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 21 Jul 2026 22:41:02 +0800 Subject: [PATCH 017/258] LOONGSON: cpufreq: loongson3: Replace IOCSR read/write with MMIO ones Our server productions (e.g. Loongson-3D6000/3E6000) can have multiple nodes in one package and SMC mailboxes are also per-node. However, IOCSR read/write can only perform on the current node, while sometimes we want to perform on other nodes (e.g. when switch governor, the get and target callbacks are not run on target core). So replace IOCSR read/write with MMIO ones. Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen Link: https://github.com/chenhuacai/linux/commits/master/a2960fe265c2d2e0db0e356b0aee5a132faaae63 Signed-off-by: Mingcong Bai --- drivers/cpufreq/loongson3_cpufreq.c | 31 ++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/loongson3_cpufreq.c b/drivers/cpufreq/loongson3_cpufreq.c index c75c0e30e88126..e5062cd623908e 100644 --- a/drivers/cpufreq/loongson3_cpufreq.c +++ b/drivers/cpufreq/loongson3_cpufreq.c @@ -164,6 +164,12 @@ union smc_message { #define FREQ_MAX_LEVEL 16 +#define MMIO_SMCMBX(node) \ + ((void __iomem *)(IO_BASE | (u64)(node) << NODE_ADDRSPACE_SHIFT | LOONGSON_REG_BASE | LOONGARCH_IOCSR_SMCMBX)) + +#define MMIO_MISC_FUNC(node) \ + ((void __iomem *)(IO_BASE | (u64)(node) << NODE_ADDRSPACE_SHIFT | LOONGSON_REG_BASE | LOONGARCH_IOCSR_MISC_FUNC)) + struct loongson3_freq_data { unsigned int def_freq_level; struct cpufreq_frequency_table table[]; @@ -176,13 +182,25 @@ static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data); static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 extra) { int retries; - unsigned int cpu = raw_smp_processor_id(); - unsigned int nid = cpu_to_node(cpu); + unsigned int cpu, nid; union smc_message msg, last; + switch (cmd) { + case CMD_GET_FREQ_INFO: + case CMD_SET_FREQ_INFO: + case CMD_GET_FREQ_LEVEL_NUM: + case CMD_GET_FREQ_LEVEL_INFO: + case CMD_GET_FREQ_BOOST_LEVEL: + cpu = cpu_number_map(id); + break; + default: + cpu = raw_smp_processor_id(); + } + nid = cpu_to_node(cpu); + mutex_lock(&cpufreq_mutex[nid]); - last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX); + last.value = readl(MMIO_SMCMBX(nid)); if (!last.complete) { mutex_unlock(&cpufreq_mutex[nid]); return -EPERM; @@ -195,12 +213,11 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext msg.extra = extra; msg.complete = 0; - iocsr_write32(msg.value, LOONGARCH_IOCSR_SMCMBX); - iocsr_write32(iocsr_read32(LOONGARCH_IOCSR_MISC_FUNC) | IOCSR_MISC_FUNC_SOFT_INT, - LOONGARCH_IOCSR_MISC_FUNC); + writel(msg.value, MMIO_SMCMBX(nid)); + writel(readl(MMIO_MISC_FUNC(nid)) | IOCSR_MISC_FUNC_SOFT_INT, MMIO_MISC_FUNC(nid)); for (retries = 0; retries < 10000; retries++) { - msg.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX); + msg.value = readl(MMIO_SMCMBX(nid)); if (msg.complete) break; From cea6a27499af92bcd90fe0b2a182b1e73d679a5c Mon Sep 17 00:00:00 2001 From: Chen Baozi Date: Fri, 14 Jul 2023 08:33:58 +0800 Subject: [PATCH 018/258] BACKPORT: PHYTIUM: arm64: phytium: Add support for Phytium SoC family This patch adds supoort for the Phytium SoC family. Signed-off-by: Chen Baozi Change-Id: Ibe06dc2e54a413a8c1257fef3aade99e6ca6480f Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/154f8e5d365d57d38ad6fabc230861c9643db154 [Kexy: Resolved minor conflict in arch/arm64/Kconfig.platforms] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/arm64/Kconfig.platforms | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms index 72c812e76b0b11..cff15501771701 100644 --- a/arch/arm64/Kconfig.platforms +++ b/arch/arm64/Kconfig.platforms @@ -318,6 +318,14 @@ config ARCH_PENSANDO cores for a minimal latency/jitter datapath, and network interfaces up to 200 Gb/s. +config ARCH_PHYTIUM + bool "Phytium SoC Family" + help + This enables support for Phytium ARMv8 SoC family, including: + - Phytium Server SoC Family + - Phytium Desktop SoC Family + - Phytium Embedded SoC Family + config ARCH_QCOM bool "Qualcomm Platforms" select GPIOLIB From 2b1a6f03d67d839ae0c2f74437c868e34a07052b Mon Sep 17 00:00:00 2001 From: Chen Baozi Date: Fri, 14 Jul 2023 08:33:59 +0800 Subject: [PATCH 019/258] PHYTIUM: PCI: Add Phytium vendor ID Update pci_ids.h with the vendor ID for Phytium. Signed-off-by: Chen Baozi Change-Id: Ie0ea6afa71a8ba2f66f8426c1db063cd734d985c Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/b570810d41c9cb46f05c31b51d156c6b4441486a Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- include/linux/pci_ids.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 1c848bd40de45c..e312f1945f61a1 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -3269,4 +3269,6 @@ #define PCI_VENDOR_ID_NCUBE 0x10ff +#define PCI_VENDOR_ID_PHYTIUM 0x1db7 + #endif /* _LINUX_PCI_IDS_H */ From c801f0f929918962c5e2aea17ee73338c8c9fcdb Mon Sep 17 00:00:00 2001 From: Chen Baozi Date: Fri, 14 Jul 2023 08:34:22 +0800 Subject: [PATCH 020/258] BACKPORT: PHYTIUM: net: stmmac: Add Phytium GMAC glue layer This patch adds support for Phytium GMAC controller which derived from Synopsys Designware MAC 10/100/1000 Universal. As required by SBBR, ACPI DSDT is supported as well as device tree. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Signed-off-by: Chen Baozi Change-Id: Id72b75c7a398c98b94df07ae92d7aa72bdc4d8ad Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/bf64fd4ae38b9dee89dfe8b17b79a50eec56961e [Kexy: Resolved minor conflict in MAINTAINERS] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai [Icenowy: Remove lpi_irq reference] Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in drivers/net/ethernet/stmicro/stmmac/Kconfig drivers/net/ethernet/stmicro/stmmac/Makefile ] Signed-off-by: Mingcong Bai --- MAINTAINERS | 6 + drivers/net/ethernet/stmicro/stmmac/Kconfig | 10 + drivers/net/ethernet/stmicro/stmmac/Makefile | 1 + .../ethernet/stmicro/stmmac/dwmac-phytium.c | 228 ++++++++++++++++++ 4 files changed, 245 insertions(+) create mode 100644 drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c diff --git a/MAINTAINERS b/MAINTAINERS index fcd8e3aee952c0..d5d1425952e24a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3304,6 +3304,12 @@ S: Maintained W: http://www.digriz.org.uk/ts78xx/kernel F: arch/arm/mach-orion5x/ts78xx-* +ARM/PHYTIUM SOC SUPPORT +M: Chen Baozi +S: Maintained +W: https://www.phytium.com.cn +F: drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c + ARM/QUALCOMM CHROMEBOOK SUPPORT R: cros-qcom-dts-watchers@chromium.org F: arch/arm64/boot/dts/qcom/sc7180* diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index e3dd5adda5acaf..8fa5343bf65bc5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -142,6 +142,16 @@ config DWMAC_NUVOTON for the stmmac device driver. The nuvoton-dwmac driver is used for MA35 series SoCs. +config DWMAC_PHYTIUM + tristate "Phytium dwmac support" + default ARCH_PHYTIUM + depends on (OF || ACPI) && (ARCH_PHYTIUM || COMPILE_TEST) + help + Support for GMAC controller on Phytium SoCs. + + This selects the Phytium GMAC glue layer support for the + stmmac device driver. + config DWMAC_QCOM_ETHQOS tristate "Qualcomm ETHQOS support" default ARCH_QCOM diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index a1cea2f57252e6..a5b812a44701a1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o obj-$(CONFIG_DWMAC_MEDIATEK) += dwmac-mediatek.o obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o obj-$(CONFIG_DWMAC_NUVOTON) += dwmac-nuvoton.o +obj-$(CONFIG_DWMAC_PHYTIUM) += dwmac-phytium.o obj-$(CONFIG_DWMAC_QCOM_ETHQOS) += dwmac-qcom-ethqos.o obj-$(CONFIG_DWMAC_RENESAS_GBETH) += dwmac-renesas-gbeth.o obj-$(CONFIG_DWMAC_ROCKCHIP) += dwmac-rk.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c new file mode 100644 index 00000000000000..e04a91464d673e --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Phytium DWMAC specific glue layer + * + * Copyright (C) 2022, Phytium Technology Co., Ltd. + * + * Chen Baozi + */ + +#include +#include +#include +#include +#include +#include + +#include "stmmac.h" +#include "stmmac_platform.h" + +static int phytium_get_mac_mode(struct fwnode_handle *fwnode) +{ + const char *pm; + int err, i; + + err = fwnode_property_read_string(fwnode, "mac-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phy_modes(i))) + return i; + } + + return -ENODEV; +} + +static int phytium_dwmac_acpi_phy(struct plat_stmmacenet_data *plat, + struct fwnode_handle *np, struct device *dev) +{ + plat->mdio_bus_data = + devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data), GFP_KERNEL); + + if (!plat->mdio_bus_data) + return -ENOMEM; + + return 0; +} + +static int phytium_dwmac_probe(struct platform_device *pdev) +{ + struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev); + struct plat_stmmacenet_data *plat; + struct stmmac_resources stmmac_res; + struct device_node *np = pdev->dev.of_node; + u64 clk_freq; + char clk_name[20]; + int ret; + + plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return -ENOMEM; + + plat->dma_cfg = + devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); + if (!plat->dma_cfg) + return -ENOMEM; + + plat->axi = devm_kzalloc(&pdev->dev, sizeof(*plat->axi), GFP_KERNEL); + if (!plat->axi) + return -ENOMEM; + + plat->phy_interface = device_get_phy_mode(&pdev->dev); + if (plat->phy_interface < 0) + return plat->phy_interface; + + plat->interface = phytium_get_mac_mode(fwnode); + if (plat->interface < 0) + plat->interface = plat->phy_interface; + + /* Configure PHY if using device-tree */ + if (pdev->dev.of_node) { + plat->phy_node = of_parse_phandle(np, "phy-handle", 0); + plat->phylink_node = np; + } + + if (pdev->dev.of_node) { + plat->bus_id = of_alias_get_id(np, "ethernet"); + if (plat->bus_id < 0) + plat->bus_id = 0; + } else if (fwnode_property_read_u32(fwnode, "bus_id", &plat->bus_id)) { + plat->bus_id = 2; + } + + plat->phy_addr = -1; + plat->clk_csr = -1; + plat->has_gmac = 1; + plat->enh_desc = 1; + plat->bugged_jumbo = 1; + plat->pmt = 1; + plat->force_sf_dma_mode = 1; + + if (fwnode_property_read_u32(fwnode, "max-speed", &plat->max_speed)) + plat->max_speed = -1; + + if (fwnode_property_read_u32(fwnode, "max-frame-size", &plat->maxmtu)) + plat->maxmtu = JUMBO_LEN; + + if (fwnode_property_read_u32(fwnode, "snps,multicast-filter-bins", + &plat->multicast_filter_bins)) + plat->multicast_filter_bins = HASH_TABLE_SIZE; + + if (fwnode_property_read_u32(fwnode, "snps,perfect-filter-entries", + &plat->unicast_filter_entries)) + plat->unicast_filter_entries = 1; + + if (fwnode_property_read_u32(fwnode, "tx-fifo-depth", + &plat->tx_fifo_size)) + plat->tx_fifo_size = 0x1000; + + if (fwnode_property_read_u32(fwnode, "rx-fifo-depth", + &plat->rx_fifo_size)) + plat->rx_fifo_size = 0x1000; + + if (phytium_dwmac_acpi_phy(plat, fwnode, &pdev->dev)) + return -ENODEV; + + plat->rx_queues_to_use = 1; + plat->tx_queues_to_use = 1; + plat->rx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; + plat->tx_queues_cfg[0].mode_to_use = MTL_QUEUE_DCB; + + if (fwnode_property_read_u64(fwnode, "clock-frequency", &clk_freq)) + clk_freq = 125000000; + + /* Set system clock */ + snprintf(clk_name, sizeof(clk_name), "%s-%d", "stmmaceth", + plat->bus_id); + + plat->stmmac_clk = clk_register_fixed_rate(&pdev->dev, clk_name, NULL, + 0, clk_freq); + if (IS_ERR(plat->stmmac_clk)) { + dev_warn(&pdev->dev, "Fail to register stmmac-clk\n"); + plat->stmmac_clk = NULL; + } + + ret = clk_prepare_enable(plat->stmmac_clk); + if (ret) { + clk_unregister_fixed_rate(plat->stmmac_clk); + return ret; + } + + plat->clk_ptp_rate = clk_get_rate(plat->stmmac_clk); + plat->clk_ptp_ref = NULL; + + if (fwnode_property_read_u32(fwnode, "snps,pbl", &plat->dma_cfg->pbl)) + plat->dma_cfg->pbl = 16; + + fwnode_property_read_u32(fwnode, "snps,txpbl", &plat->dma_cfg->txpbl); + fwnode_property_read_u32(fwnode, "snps,rxpbl", &plat->dma_cfg->rxpbl); + + plat->dma_cfg->pblx8 = + !fwnode_property_read_bool(fwnode, "snps,no-pbl-x8"); + plat->dma_cfg->aal = fwnode_property_read_bool(fwnode, "snps,aal"); + plat->dma_cfg->fixed_burst = + fwnode_property_read_bool(fwnode, "snps,fixed-burst"); + plat->dma_cfg->mixed_burst = + fwnode_property_read_bool(fwnode, "snps,mixed-burst"); + + plat->axi->axi_lpi_en = false; + plat->axi->axi_xit_frm = false; + plat->axi->axi_wr_osr_lmt = 7; + plat->axi->axi_rd_osr_lmt = 7; + plat->axi->axi_blen[0] = 16; + + memset(&stmmac_res, 0, sizeof(stmmac_res)); + stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0); + stmmac_res.irq = platform_get_irq(pdev, 0); + if (stmmac_res.irq < 0) { + dev_err(&pdev->dev, "IRQ not found.\n"); + return -ENXIO; + } + stmmac_res.wol_irq = stmmac_res.irq; + + return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); +} + +int phytium_dwmac_remove(struct platform_device *pdev) +{ + int ret; + struct net_device *ndev = platform_get_drvdata(pdev); + struct stmmac_priv *priv = netdev_priv(ndev); + struct plat_stmmacenet_data *plat = priv->plat; + + ret = stmmac_pltfr_remove(pdev); + clk_unregister_fixed_rate(plat->stmmac_clk); + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id phytium_dwmac_of_match[] = { + { .compatible = "phytium,gmac" }, + {} +}; +MODULE_DEVICE_TABLE(of, phytium_dwmac_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_dwmac_acpi_ids[] = { + { .id = "PHYT0004" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, phytium_dwmac_acpi_ids); +#endif + +static struct platform_driver phytium_dwmac_driver = { + .probe = phytium_dwmac_probe, + .remove = phytium_dwmac_remove, + .driver = { + .name = "phytium-dwmac", + .of_match_table = of_match_ptr(phytium_dwmac_of_match), + .acpi_match_table = ACPI_PTR(phytium_dwmac_acpi_ids), + }, +}; +module_platform_driver(phytium_dwmac_driver); + +MODULE_AUTHOR("Chen Baozi "); +MODULE_DESCRIPTION("Phytium DWMAC specific glue layer"); +MODULE_LICENSE("GPL"); From 33f47937c17fe918c62fd423ce29b900d1276f22 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 20 Oct 2023 18:55:20 +0800 Subject: [PATCH 021/258] BACKPORT: PHYTIUM: net: stmmac: Add a barrier to make sure all access coherent Add a memory barrier to sync TX descriptor to avoid data error. Besides, increase the ring buffer size to avoid buffer overflow. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Signed-off-by: Chen Baozi Change-Id: I47bd63a472c915b7f88b16921e8fa63bf8b284d9 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/cc1c23a77c9282f285d22c046dc6908209e5a236 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai [ Mingcong Bai: Fixed a minor post-7.0.6 merge conflict in drivers/net/ethernet/stmicro/stmmac/common.h ] Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/common.h | 4 ++-- drivers/net/ethernet/stmicro/stmmac/norm_desc.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 927ea6230073f4..4446f7f66d811d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -59,10 +59,10 @@ static inline bool dwmac_is_xmac(enum dwmac_core_type core_type) */ #define DMA_MIN_TX_SIZE 64 #define DMA_MAX_TX_SIZE 1024 -#define DMA_DEFAULT_TX_SIZE 512 +#define DMA_DEFAULT_TX_SIZE 1024 #define DMA_MIN_RX_SIZE 64 #define DMA_MAX_RX_SIZE 1024 -#define DMA_DEFAULT_RX_SIZE 512 +#define DMA_DEFAULT_RX_SIZE 1024 #define STMMAC_NEXT_ENTRY(x, size) ((x + 1) & (size - 1)) #undef FRAME_FILTER_DEBUG diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c index c4b613564f87ce..6bb35c9031d087 100644 --- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c +++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c @@ -182,6 +182,10 @@ static void ndesc_prepare_tx_desc(struct dma_desc *p, int is_fs, int len, else norm_set_tx_desc_len_on_ring(p, len); + /* The own bit must be the latest setting done when prepare the + * descriptor and then barrier is needed to make sure that all is coherent. + */ + wmb(); if (tx_own) p->des0 |= cpu_to_le32(TDES0_OWN); } From 5342fd14e87cbec22b802bcf115f32e8778a87b4 Mon Sep 17 00:00:00 2001 From: zuoqian Date: Tue, 23 Jan 2024 09:24:03 +0800 Subject: [PATCH 022/258] BACKPORT: PHYTIUM: drivers: fix build errors 1. spi: Rename SPI_MASTER_GPIO_SS to SPI_CONTROLLER_GPIO_SS 82238 2. net: stmmac: clarify difference between "interface" and "phy_interface" a014c3555 3. net: stmmac: convert plat->phylink_node to fwnode e80af2a 4. net: stmmac: Make stmmac_pltfr_remove() return void 3246627f 5. Remove IOMMU_CAP_INTR_REMAP, instead rely on the interrupt subsystem 143c7bc 6. i2c: Switch .probe() to not take an id parameter 03c835f498b54 7. drivers: gpio: add #include Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/52a27ae1196d3714b22e430fb43ab9ac2e55e914 [Kexy: Resolved minor conflicts in drivers/gpio/gpio-phytium-core.c, drivers/input/keyboard/phytium-keypad.c, drivers/irqchip/irq-gic-phytium-2500-its.c, drivers/spi/spi-phytium.c, sound/soc/codecs/es8336.c, and sound/soc/codecs/es8388.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- .../net/ethernet/stmicro/stmmac/dwmac-phytium.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index e04a91464d673e..7ff6ad4d41bbf1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -73,14 +73,14 @@ static int phytium_dwmac_probe(struct platform_device *pdev) if (plat->phy_interface < 0) return plat->phy_interface; - plat->interface = phytium_get_mac_mode(fwnode); - if (plat->interface < 0) - plat->interface = plat->phy_interface; + plat->mac_interface = phytium_get_mac_mode(fwnode); + if (plat->mac_interface < 0) + plat->mac_interface = plat->phy_interface; /* Configure PHY if using device-tree */ if (pdev->dev.of_node) { plat->phy_node = of_parse_phandle(np, "phy-handle", 0); - plat->phylink_node = np; + plat->port_node = of_fwnode_handle(np); } if (pdev->dev.of_node) { @@ -184,16 +184,14 @@ static int phytium_dwmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); } -int phytium_dwmac_remove(struct platform_device *pdev) +void phytium_dwmac_remove(struct platform_device *pdev) { - int ret; struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); struct plat_stmmacenet_data *plat = priv->plat; - ret = stmmac_pltfr_remove(pdev); + stmmac_pltfr_remove(pdev); clk_unregister_fixed_rate(plat->stmmac_clk); - return ret; } #ifdef CONFIG_OF @@ -214,7 +212,7 @@ MODULE_DEVICE_TABLE(acpi, phytium_dwmac_acpi_ids); static struct platform_driver phytium_dwmac_driver = { .probe = phytium_dwmac_probe, - .remove = phytium_dwmac_remove, + .remove_new = phytium_dwmac_remove, .driver = { .name = "phytium-dwmac", .of_match_table = of_match_ptr(phytium_dwmac_of_match), From 40843254d2ddce8528b5f4a0abccbe4ead76dbc5 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Mon, 5 Feb 2024 09:52:51 +0800 Subject: [PATCH 023/258] BACKPORT: PHYTIUM: Update phytium copyright info to 2024 Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/f1b4bd2a3b847e061631dfea27f4c64bb75cfe9b [Kexy: Resolved minor conflicts in arch/arm64/boot/dts/phytium/pe2201-demo-ddr4.dts, arch/arm64/boot/dts/phytium/pe2201.dtsi, arch/arm64/boot/dts/phytium/pe2202-chillipi-edu-board.dts, arch/arm64/boot/dts/phytium/pe2202-demo-ddr4-local.dts, arch/arm64/boot/dts/phytium/pe2202-demo-ddr4.dts, arch/arm64/boot/dts/phytium/pe2202-miniitx-board.dts, arch/arm64/boot/dts/phytium/pe2202-power-board.dts, arch/arm64/boot/dts/phytium/pe2202.dtsi, arch/arm64/boot/dts/phytium/pe2204-come-board.dts, arch/arm64/boot/dts/phytium/pe2204-demo-ddr4-local.dts, arch/arm64/boot/dts/phytium/pe2204-demo-ddr4.dts, arch/arm64/boot/dts/phytium/pe2204-edu-board.dts, arch/arm64/boot/dts/phytium/pe2204-hanwei-board.dts, arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts, arch/arm64/boot/dts/phytium/pe2204-vpx-board.dts, arch/arm64/boot/dts/phytium/pe2204.dtsi, arch/arm64/boot/dts/phytium/pe220x.dtsi, arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts, drivers/char/hw_random/phytium-rng.c, drivers/char/ipmi/bt_bmc_phytium.c, drivers/char/ipmi/kcs_bmc_phytium.c, drivers/dma/phytium/phytium-ddmac.c, drivers/dma/phytium/phytium-ddmac.h, drivers/edac/phytium_pe220x_edac.c, drivers/gpio/gpio-phytium-core.c, drivers/gpio/gpio-phytium-core.h, drivers/gpio/gpio-phytium-pci.c, drivers/gpio/gpio-phytium-platform.c, drivers/gpio/gpio-phytium-sgpio.c, drivers/gpu/drm/phytium/pe220x_dc.c, drivers/gpu/drm/phytium/pe220x_dc.h, drivers/gpu/drm/phytium/pe220x_dp.c, drivers/gpu/drm/phytium/pe220x_dp.h, drivers/gpu/drm/phytium/pe220x_reg.h, drivers/gpu/drm/phytium/phytium_crtc.c, drivers/gpu/drm/phytium/phytium_crtc.h, drivers/gpu/drm/phytium/phytium_debugfs.c, drivers/gpu/drm/phytium/phytium_debugfs.h, drivers/gpu/drm/phytium/phytium_display_drv.c, drivers/gpu/drm/phytium/phytium_display_drv.h, drivers/gpu/drm/phytium/phytium_dp.c, drivers/gpu/drm/phytium/phytium_dp.h, drivers/gpu/drm/phytium/phytium_fb.c, drivers/gpu/drm/phytium/phytium_fb.h, drivers/gpu/drm/phytium/phytium_fbdev.c, drivers/gpu/drm/phytium/phytium_fbdev.h, drivers/gpu/drm/phytium/phytium_gem.c, drivers/gpu/drm/phytium/phytium_gem.h, drivers/gpu/drm/phytium/phytium_panel.c, drivers/gpu/drm/phytium/phytium_panel.h, drivers/gpu/drm/phytium/phytium_pci.c, drivers/gpu/drm/phytium/phytium_pci.h, drivers/gpu/drm/phytium/phytium_plane.c, drivers/gpu/drm/phytium/phytium_plane.h, drivers/gpu/drm/phytium/phytium_platform.c, drivers/gpu/drm/phytium/phytium_platform.h, drivers/gpu/drm/phytium/phytium_reg.h, drivers/gpu/drm/phytium/px210_dc.c, drivers/gpu/drm/phytium/px210_dc.h, drivers/gpu/drm/phytium/px210_dp.c, drivers/gpu/drm/phytium/px210_dp.h, drivers/gpu/drm/phytium/px210_reg.h, drivers/hwmon/tacho-phytium.c, drivers/hwspinlock/phytium_hwspinlock.c, drivers/i2c/busses/i2c-phytium-common.c, drivers/i2c/busses/i2c-phytium-core.h, drivers/i2c/busses/i2c-phytium-master.c, drivers/i2c/busses/i2c-phytium-pci.c, drivers/i2c/busses/i2c-phytium-platform.c, drivers/i2c/busses/i2c-phytium-slave.c, drivers/iio/adc/phytium-adc.c, drivers/input/keyboard/phytium-keypad.c, drivers/input/serio/phytium-ps2.c, drivers/irqchip/irq-gic-phytium-2500-its.c, drivers/irqchip/irq-gic-phytium-2500.c, drivers/irqchip/irq-phytium-ixic.c, drivers/mailbox/phytium_mailbox.c, drivers/media/platform/phytium/phytium_jpeg_core.c, drivers/media/platform/phytium/phytium_jpeg_core.h, drivers/media/platform/phytium/phytium_jpeg_reg.h, drivers/mfd/phytium_px210_i2s_lsd.c, drivers/mfd/phytium_px210_i2s_mmd.c, drivers/mmc/host/phytium-mci-pci.c, drivers/mmc/host/phytium-mci-plat.c, drivers/mmc/host/phytium-mci.c, drivers/mmc/host/phytium-mci.h, drivers/mmc/host/phytium-sdci.c, drivers/mmc/host/phytium-sdci.h, drivers/mtd/nand/raw/phytium_nand.c, drivers/mtd/nand/raw/phytium_nand.h, drivers/mtd/nand/raw/phytium_nand_pci.c, drivers/mtd/nand/raw/phytium_nand_plat.c, drivers/net/can/phytium/phytium_can.c, drivers/net/can/phytium/phytium_can.h, drivers/net/can/phytium/phytium_can_pci.c, drivers/net/can/phytium/phytium_can_platform.c, drivers/pci/controller/pcie-phytium-ep.c, drivers/pci/controller/pcie-phytium-ep.h, drivers/pci/controller/pcie-phytium-register.h, drivers/pwm/pwm-phytium.c, drivers/remoteproc/homo_remoteproc.c, drivers/spi/spi-phytium-dma.c, drivers/spi/spi-phytium-pci.c, drivers/spi/spi-phytium-plat.c, drivers/spi/spi-phytium-qspi.c, drivers/spi/spi-phytium.c, drivers/spi/spi-phytium.h, drivers/tty/serial/phytium-uart.c, drivers/w1/masters/phytium_w1.c, include/linux/irqchip/arm-gic-phytium-2500.h, sound/pci/hda/hda_phytium.c, sound/pci/hda/hda_phytium.h, sound/soc/codecs/es8336.c, sound/soc/codecs/es8336.h, sound/soc/codecs/es8388.c, sound/soc/phytium/local.h, sound/soc/phytium/phytium_i2s.c, sound/soc/phytium/pmdk_dp.c, sound/soc/phytium/pmdk_es8336.c, and sound/soc/phytium/pmdk_es8388.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 7ff6ad4d41bbf1..ab3e98535e1e2e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -2,7 +2,7 @@ /* * Phytium DWMAC specific glue layer * - * Copyright (C) 2022, Phytium Technology Co., Ltd. + * Copyright (c) 2022-2024 Phytium Technology Co., Ltd. * * Chen Baozi */ From 1304effeb9e7064a96c2eaee546de315e0a3c7d2 Mon Sep 17 00:00:00 2001 From: Zhu Honglei Date: Wed, 20 Mar 2024 14:53:55 +0800 Subject: [PATCH 024/258] BACKPORT: PHYTIUM: edac: Phytium: Add edac driver to receive RAS error Support for error detection and correction on the Phytium Pe220x family of SOCs. Signed-off-by: Zhu Honglei Signed-off-by: Lan Hengyu Signed-off-by: Li Mingzhe Signed-off-by: Wang Yinfeng Change-Id: Iaa02a12427e8a032e12dcf148da9b6deb574a2f6 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/fcb536a33d3ad3a005af1113aef503352fba964a [Kexy: Resolved minor conflict in MAINTAINERS, dropped changes in drivers/edac/Kconfig, drivers/edac/Makefile, and drivers/edac/phytium_edac.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- MAINTAINERS | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index d5d1425952e24a..dedd131668d3e1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3304,12 +3304,6 @@ S: Maintained W: http://www.digriz.org.uk/ts78xx/kernel F: arch/arm/mach-orion5x/ts78xx-* -ARM/PHYTIUM SOC SUPPORT -M: Chen Baozi -S: Maintained -W: https://www.phytium.com.cn -F: drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c - ARM/QUALCOMM CHROMEBOOK SUPPORT R: cros-qcom-dts-watchers@chromium.org F: arch/arm64/boot/dts/qcom/sc7180* @@ -21661,6 +21655,11 @@ F: include/sound/pxa2xx-lib.h F: sound/arm/pxa* F: sound/soc/pxa/ +ARM/PHYTIUM SOC SUPPORT +M: Wang Yinfeng +S: Maintained +W: https://www.phytium.com.cn + QAT DRIVER M: Giovanni Cabiddu L: qat-linux@intel.com From 27c5b259e2e3c99187a00a0ad1cea53ae1409522 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 25 Mar 2024 17:38:27 +0800 Subject: [PATCH 025/258] BACKPORT: PHYTIUM: net/stmmac: Add phytium DWMAC driver support v2 Modify stmmmac driver to support phytium DWMAC controler. Signed-off-by: Li Wencheng Signed-off-by: Chen Baozi Signed-off-by: Wang Yinfeng Change-Id: I878377c5fb88935dcbc7b83f368ae5408938f890 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/f18feb0bef4216b257a29d176e7366c7025f3115 [Kexy: Resolved minor conflict in MAINTAINERS] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- MAINTAINERS | 1 + .../ethernet/stmicro/stmmac/dwmac-phytium.c | 37 ++++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index dedd131668d3e1..99a988793f61b2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21659,6 +21659,7 @@ ARM/PHYTIUM SOC SUPPORT M: Wang Yinfeng S: Maintained W: https://www.phytium.com.cn +F: drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c QAT DRIVER M: Giovanni Cabiddu diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index ab3e98535e1e2e..b7e1c422284ff2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -37,8 +37,7 @@ static int phytium_get_mac_mode(struct fwnode_handle *fwnode) static int phytium_dwmac_acpi_phy(struct plat_stmmacenet_data *plat, struct fwnode_handle *np, struct device *dev) { - plat->mdio_bus_data = - devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data), GFP_KERNEL); + plat->mdio_bus_data = devm_kzalloc(dev, sizeof(struct stmmac_mdio_bus_data), GFP_KERNEL); if (!plat->mdio_bus_data) return -ENOMEM; @@ -60,8 +59,7 @@ static int phytium_dwmac_probe(struct platform_device *pdev) if (!plat) return -ENOMEM; - plat->dma_cfg = - devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); + plat->dma_cfg = devm_kzalloc(&pdev->dev, sizeof(*plat->dma_cfg), GFP_KERNEL); if (!plat->dma_cfg) return -ENOMEM; @@ -113,12 +111,10 @@ static int phytium_dwmac_probe(struct platform_device *pdev) &plat->unicast_filter_entries)) plat->unicast_filter_entries = 1; - if (fwnode_property_read_u32(fwnode, "tx-fifo-depth", - &plat->tx_fifo_size)) + if (fwnode_property_read_u32(fwnode, "tx-fifo-depth", &plat->tx_fifo_size)) plat->tx_fifo_size = 0x1000; - if (fwnode_property_read_u32(fwnode, "rx-fifo-depth", - &plat->rx_fifo_size)) + if (fwnode_property_read_u32(fwnode, "rx-fifo-depth", &plat->rx_fifo_size)) plat->rx_fifo_size = 0x1000; if (phytium_dwmac_acpi_phy(plat, fwnode, &pdev->dev)) @@ -133,11 +129,9 @@ static int phytium_dwmac_probe(struct platform_device *pdev) clk_freq = 125000000; /* Set system clock */ - snprintf(clk_name, sizeof(clk_name), "%s-%d", "stmmaceth", - plat->bus_id); + snprintf(clk_name, sizeof(clk_name), "%s-%d", "stmmaceth", plat->bus_id); - plat->stmmac_clk = clk_register_fixed_rate(&pdev->dev, clk_name, NULL, - 0, clk_freq); + plat->stmmac_clk = clk_register_fixed_rate(&pdev->dev, clk_name, NULL, 0, clk_freq); if (IS_ERR(plat->stmmac_clk)) { dev_warn(&pdev->dev, "Fail to register stmmac-clk\n"); plat->stmmac_clk = NULL; @@ -158,13 +152,10 @@ static int phytium_dwmac_probe(struct platform_device *pdev) fwnode_property_read_u32(fwnode, "snps,txpbl", &plat->dma_cfg->txpbl); fwnode_property_read_u32(fwnode, "snps,rxpbl", &plat->dma_cfg->rxpbl); - plat->dma_cfg->pblx8 = - !fwnode_property_read_bool(fwnode, "snps,no-pbl-x8"); + plat->dma_cfg->pblx8 = !fwnode_property_read_bool(fwnode, "snps,no-pbl-x8"); plat->dma_cfg->aal = fwnode_property_read_bool(fwnode, "snps,aal"); - plat->dma_cfg->fixed_burst = - fwnode_property_read_bool(fwnode, "snps,fixed-burst"); - plat->dma_cfg->mixed_burst = - fwnode_property_read_bool(fwnode, "snps,mixed-burst"); + plat->dma_cfg->fixed_burst = fwnode_property_read_bool(fwnode, "snps,fixed-burst"); + plat->dma_cfg->mixed_burst = fwnode_property_read_bool(fwnode, "snps,mixed-burst"); plat->axi->axi_lpi_en = false; plat->axi->axi_xit_frm = false; @@ -181,10 +172,10 @@ static int phytium_dwmac_probe(struct platform_device *pdev) } stmmac_res.wol_irq = stmmac_res.irq; - return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); + return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); } -void phytium_dwmac_remove(struct platform_device *pdev) +int phytium_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); @@ -192,12 +183,14 @@ void phytium_dwmac_remove(struct platform_device *pdev) stmmac_pltfr_remove(pdev); clk_unregister_fixed_rate(plat->stmmac_clk); + + return 0; } #ifdef CONFIG_OF static const struct of_device_id phytium_dwmac_of_match[] = { { .compatible = "phytium,gmac" }, - {} + { } }; MODULE_DEVICE_TABLE(of, phytium_dwmac_of_match); #endif @@ -212,7 +205,7 @@ MODULE_DEVICE_TABLE(acpi, phytium_dwmac_acpi_ids); static struct platform_driver phytium_dwmac_driver = { .probe = phytium_dwmac_probe, - .remove_new = phytium_dwmac_remove, + .remove = phytium_dwmac_remove, .driver = { .name = "phytium-dwmac", .of_match_table = of_match_ptr(phytium_dwmac_of_match), From 8b656ad000aff5ce170185f7ff3c68799fe34fa3 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Thu, 13 Jun 2024 17:50:28 +0800 Subject: [PATCH 026/258] PHYTIUM: net: stmmac: phytium driver add pm support Test S3 with net device open will got this cut log. ... NETDEV WATCHDOG: eth0 (phytium-dwmac): transmit queue 0 timed out 8532ms WARNING: CPU: 2 PID: 0 at net/sched/sch_generic.c:525 dev_watchdog+0x234/0x23c ... Miss pm function in phytium_dwmac_driver. So add it. Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/e4084ab6219ff1e5b2ad5cc68b58be491b3f448f Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index b7e1c422284ff2..7df8417da4b623 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -208,6 +208,7 @@ static struct platform_driver phytium_dwmac_driver = { .remove = phytium_dwmac_remove, .driver = { .name = "phytium-dwmac", + .pm = &stmmac_pltfr_pm_ops, .of_match_table = of_match_ptr(phytium_dwmac_of_match), .acpi_match_table = ACPI_PTR(phytium_dwmac_acpi_ids), }, From bfaaf02ff0408efd6cd162e887cd9ab6cc710997 Mon Sep 17 00:00:00 2001 From: Song Wenting Date: Mon, 25 Mar 2024 13:55:20 +0800 Subject: [PATCH 027/258] BACKPORT: PHYTIUM: net: phytium: Add support for phytium GMAC This patch provides support for Phytium GMAC controller driver. Signed-off-by: Song Wenting Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: If526fbf5cda879b4445b027ec32b94e9403b3870 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/4a478e247258caf39ae5d7541ecb6c4aa08c90b8 [Kexy: Resolved minor conflict in MAINTAINERS] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- MAINTAINERS | 3 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/phytium/Kconfig | 58 + drivers/net/ethernet/phytium/Makefile | 17 + drivers/net/ethernet/phytium/phytmac.h | 591 +++++ .../net/ethernet/phytium/phytmac_ethtool.c | 544 ++++ drivers/net/ethernet/phytium/phytmac_main.c | 2236 +++++++++++++++++ drivers/net/ethernet/phytium/phytmac_pci.c | 318 +++ .../net/ethernet/phytium/phytmac_platform.c | 255 ++ drivers/net/ethernet/phytium/phytmac_ptp.c | 304 +++ drivers/net/ethernet/phytium/phytmac_ptp.h | 55 + drivers/net/ethernet/phytium/phytmac_v1.c | 1370 ++++++++++ drivers/net/ethernet/phytium/phytmac_v1.h | 453 ++++ drivers/net/ethernet/phytium/phytmac_v2.c | 1254 +++++++++ drivers/net/ethernet/phytium/phytmac_v2.h | 362 +++ 16 files changed, 7822 insertions(+) create mode 100644 drivers/net/ethernet/phytium/Kconfig create mode 100644 drivers/net/ethernet/phytium/Makefile create mode 100644 drivers/net/ethernet/phytium/phytmac.h create mode 100644 drivers/net/ethernet/phytium/phytmac_ethtool.c create mode 100644 drivers/net/ethernet/phytium/phytmac_main.c create mode 100644 drivers/net/ethernet/phytium/phytmac_pci.c create mode 100644 drivers/net/ethernet/phytium/phytmac_platform.c create mode 100644 drivers/net/ethernet/phytium/phytmac_ptp.c create mode 100644 drivers/net/ethernet/phytium/phytmac_ptp.h create mode 100644 drivers/net/ethernet/phytium/phytmac_v1.c create mode 100644 drivers/net/ethernet/phytium/phytmac_v1.h create mode 100644 drivers/net/ethernet/phytium/phytmac_v2.c create mode 100644 drivers/net/ethernet/phytium/phytmac_v2.h diff --git a/MAINTAINERS b/MAINTAINERS index 99a988793f61b2..74848d12afb7dc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21659,6 +21659,9 @@ ARM/PHYTIUM SOC SUPPORT M: Wang Yinfeng S: Maintained W: https://www.phytium.com.cn +F: drivers/net/ethernet/Kconfig +F: drivers/net/ethernet/Makefile +F: drivers/net/ethernet/phytium/* F: drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c QAT DRIVER diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index b8f70e2a1763ad..637a07cd3fe1f7 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -158,6 +158,7 @@ config OA_TC6 source "drivers/net/ethernet/pasemi/Kconfig" source "drivers/net/ethernet/pensando/Kconfig" +source "drivers/net/ethernet/phytium/Kconfig" source "drivers/net/ethernet/qlogic/Kconfig" source "drivers/net/ethernet/brocade/Kconfig" source "drivers/net/ethernet/qualcomm/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index 57344fec6ce047..d2590894e2228b 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_LPC_ENET) += nxp/ obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/ obj-$(CONFIG_ETHOC) += ethoc.o obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ +obj-$(CONFIG_NET_VENDOR_PHYTIUM) += phytium/ obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig new file mode 100644 index 00000000000000..14a77adbfdc12f --- /dev/null +++ b/drivers/net/ethernet/phytium/Kconfig @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phytium device configuration +# + +config NET_VENDOR_PHYTIUM + bool "Phytium devices" + depends on HAS_IOMEM + default y + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all the + remaining Cadence network card questions. If you say Y, you will be + asked for your specific card in the following questions. + +if NET_VENDOR_PHYTIUM + +config PHYTMAC + tristate "Phytium GMAC support" + depends on HAS_DMA + select PHYLINK + select CRC32 + help + If you have a network (Ethernet) controller of this type, say Y + or M here. + + To compile this driver as a module, choose M here: the module + will be phytmac. + +config PHYTMAC_ENABLE_PTP + bool "Enable IEEE 1588 hwstamp" + depends on PHYTMAC + depends on PTP_1588_CLOCK + default y + help + Enable IEEE 1588 PTP support for PHYTMAC. + +config PHYTMAC_PLATFORM + tristate "Phytmac Platform support" + depends on PHYTMAC + help + This is Platform driver. + + To compile this driver as a module, choose M here: the module + will be called phytmac_platform. + +config PHYTMAC_PCI + tristate "Phytmac PCI support" + depends on PHYTMAC && PCI + help + This is PCI driver. + + To compile this driver as a module, choose M here: the module + will be called phytmac_pci. + +endif # NET_VENDOR_PHYTIUM diff --git a/drivers/net/ethernet/phytium/Makefile b/drivers/net/ethernet/phytium/Makefile new file mode 100644 index 00000000000000..6e710d4d54b664 --- /dev/null +++ b/drivers/net/ethernet/phytium/Makefile @@ -0,0 +1,17 @@ + +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the Phytium network device drivers. +# +# + +obj-$(CONFIG_PHYTMAC) += phytmac.o + +phytmac-objs := phytmac_main.o phytmac_ethtool.o phytmac_v1.o phytmac_v2.o +phytmac-$(CONFIG_PHYTMAC_ENABLE_PTP) += phytmac_ptp.o + +obj-$(CONFIG_PHYTMAC_PLATFORM) += phytmac-platform.o +phytmac-platform-objs := phytmac_platform.o + +obj-$(CONFIG_PHYTMAC_PCI) += phytmac-pci.o +phytmac-pci-objs := phytmac_pci.o diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h new file mode 100644 index 00000000000000..b90e1551499ef7 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -0,0 +1,591 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _PHYTMAC_H +#define _PHYTMAC_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PHYTMAC_DRV_NAME "phytium-mac" +#define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" + +#define PHYTMAC_DEFAULT_MSG_ENABLE \ + (NETIF_MSG_DRV | \ + NETIF_MSG_PROBE | \ + NETIF_MSG_LINK | \ + NETIF_MSG_INTR | \ + NETIF_MSG_HW |\ + NETIF_MSG_PKTDATA) + +#define IRQ_TYPE_INT 0 +#define IRQ_TYPE_MSI 1 +#define IRQ_TYPE_INTX 2 + +#define PHYTMAC_MAX_QUEUES 8 +#define DEFAULT_DMA_BURST_LENGTH 16 +#define DEFAULT_JUMBO_MAX_LENGTH 10240 +#define PHYTMAC_MAX_TX_LEN 16320 +#define PHYTMAC_MIN_TX_LEN 64 +#define DEFAULT_TX_RING_SIZE 512 +#define DEFAULT_RX_RING_SIZE 512 +#define MAX_TX_RING_SIZE 1024 +#define MAX_RX_RING_SIZE 4096 +#define MIN_TX_RING_SIZE 64 +#define MIN_RX_RING_SIZE 64 +#define DEFAULT_TX_DESC_MIN_FREE 64 +#define DEFAULT_RX_DESC_MIN_FREE 64 + +#define MEMORY_SIZE 4096 +#define MHU_SIZE 0x20 + +#define PHYTMAC_POWEROFF 1 +#define PHYTMAC_POWERON 2 + +#define PHYTMAC_WOL_MAGIC_PACKET 1 + +#define DEFAULT_MSG_RING_SIZE 16 + +#define PHYTMAC_CAPS_JUMBO 0x00000001 +#define PHYTMAC_CAPS_PTP 0x00000002 +#define PHYTMAC_CAPS_BD_RD_PREFETCH 0x00000004 +#define PHYTMAC_CAPS_PCS 0x00000008 +#define PHYTMAC_CAPS_LSO 0x00000010 +#define PHYTMAC_CAPS_SG_DISABLED 0x00000020 +#define PHYTMAC_CAPS_TAILPTR 0x00000040 +#define PHYTMAC_CAPS_START 0x00000080 +#define PHYTMAC_CAPS_NO_WOL 0x0000100 +#define PHYTMAC_CAPS_LPI 0x0000400 +#define PHYTMAC_CAPS_MSG 0x0000800 + +#define PHYTMAC_TX 0x1 +#define PHYTMAC_RX 0x2 + +#define PHYTMAC_GREGS_LEN 16 + +#define PHYTMAC_MTU_MIN_SIZE ETH_MIN_MTU + +#define EQUAL(a, b) ((a) == (b)) + +#define TXD_USE_COUNT(_pdata, s) DIV_ROUND_UP((s), (_pdata)->max_tx_length) + +/* Bit manipulation macros */ +#define PHYTMAC_BIT(_field) \ + (1 << PHYTMAC_##_field##_INDEX) + +#define PHYTMAC_BITS(_field, value) \ + (((value) & ((1 << PHYTMAC_##_field##_WIDTH) - 1)) \ + << PHYTMAC_##_field##_INDEX) + +#define PHYTMAC_GET_BITS(_var, _field) \ + (((_var) >> (PHYTMAC_##_field##_INDEX)) \ + & ((0x1 << (PHYTMAC_##_field##_WIDTH)) - 1)) + +#define PHYTMAC_SET_BITS(_var, _field, _val) \ + (((_var) & ~(((1 << PHYTMAC_##_field##_WIDTH) - 1) \ + << PHYTMAC_##_field##_INDEX)) \ + | (((_val) & ((1 << PHYTMAC_##_field##_WIDTH) - 1)) \ + << PHYTMAC_##_field##_INDEX)) + +#define PHYTMAC_READ(_pdata, _reg) \ + __raw_readl((_pdata)->mac_regs + (_reg)) + +#define PHYTMAC_READ_BITS(_pdata, _reg, _field) \ + PHYTMAC_GET_BITS(PHYTMAC_READ((_pdata), _reg), _field) + +#define PHYTMAC_WRITE(_pdata, _reg, _val) \ + __raw_writel((_val), (_pdata)->mac_regs + (_reg)) + +#define PHYTMAC_MSG_READ(_pdata, _reg) \ + __raw_readl((_pdata)->mac_regs + (_reg)) + +#define PHYTMAC_WRITE(_pdata, _reg, _val) \ + __raw_writel((_val), (_pdata)->mac_regs + (_reg)) + +#define LSO_UFO 1 +#define LSO_TSO 2 + +#define PHYTMAC_INT_TX_COMPLETE 0x1 +#define PHYTMAC_INT_TX_ERR 0x2 +#define PHYTMAC_INT_RX_COMPLETE 0x4 +#define PHYTMAC_INT_RX_OVERRUN 0x8 +#define PHYTMAC_INT_RX_DESC_FULL 0x10 +#define PHYTMAC_RX_INT_FLAGS (PHYTMAC_INT_RX_COMPLETE) +#define PHYTMAC_TX_INT_FLAGS (PHYTMAC_INT_TX_COMPLETE \ + | PHYTMAC_INT_TX_ERR) + +#define PHYTMAC_WAKE_MAGIC 0x00000001 +#define PHYTMAC_WAKE_ARP 0x00000002 +#define PHYTMAC_WAKE_UCAST 0x00000004 +#define PHYTMAC_WAKE_MCAST 0x00000008 + +struct packet_info { + int lso; + int desc_cnt; + int hdrlen; + int nocrc; + u32 mss; + u32 seq; +}; + +#define DEV_TYPE_PLATFORM 0 +#define DEV_TYPE_PCI 1 + +struct phytmac_statistics { + char stat_string[ETH_GSTRING_LEN]; +}; + +#define STAT_TITLE(title) { \ + .stat_string = title, \ +} + +static const struct phytmac_statistics phytmac_statistics[] = { + STAT_TITLE("tx_octets"), + STAT_TITLE("tx_packets"), + STAT_TITLE("tx_bcast_packets"), + STAT_TITLE("tx_mcase_packets"), + STAT_TITLE("tx_pause_packets"), + STAT_TITLE("tx_64_byte_packets"), + STAT_TITLE("tx_65_127_byte_packets"), + STAT_TITLE("tx_128_255_byte_packets"), + STAT_TITLE("tx_256_511_byte_packets"), + STAT_TITLE("tx_512_1023_byte_packets"), + STAT_TITLE("tx_1024_1518_byte_packets"), + STAT_TITLE("tx_more_than_1518_byte_packets"), + STAT_TITLE("tx_underrun"), + STAT_TITLE("tx_single_collisions"), + STAT_TITLE("tx_multiple_collisions"), + STAT_TITLE("tx_excessive_collisions"), + STAT_TITLE("tx_late_collisions"), + STAT_TITLE("tx_deferred"), + STAT_TITLE("tx_carrier_sense_errors"), + STAT_TITLE("rx_octets"), + STAT_TITLE("rx_packets"), + STAT_TITLE("rx_bcast_packets"), + STAT_TITLE("rx_mcast_packets"), + STAT_TITLE("rx_pause_packets"), + STAT_TITLE("rx_64_byte_packets"), + STAT_TITLE("rx_65_127_byte_packets"), + STAT_TITLE("rx_128_255_byte_packets"), + STAT_TITLE("rx_256_511_byte_packets"), + STAT_TITLE("rx_512_1023_byte_packets"), + STAT_TITLE("rx_1024_1518_byte_packets"), + STAT_TITLE("rx_more_than_1518_byte_packets"), + STAT_TITLE("rx_undersized_packets"), + STAT_TITLE("rx_oversize_packets"), + STAT_TITLE("rx_jabbers"), + STAT_TITLE("rx_fcs_errors"), + STAT_TITLE("rx_length_errors"), + STAT_TITLE("rx_symbol_errors"), + STAT_TITLE("rx_alignment_errors"), + STAT_TITLE("rx_resource_over"), + STAT_TITLE("rx_overruns"), + STAT_TITLE("rx_iphdr_csum_errors"), + STAT_TITLE("rx_tcp_csum_errors"), + STAT_TITLE("rx_udp_csum_errors"), +}; + +#define PHYTMAC_STATS_LEN ARRAY_SIZE(phytmac_statistics) + +/* per queue statistics, each should be unsigned long type */ +struct phytmac_queue_stats { + unsigned long rx_packets; + unsigned long rx_bytes; + unsigned long rx_dropped; + unsigned long tx_packets; + unsigned long tx_bytes; + unsigned long tx_dropped; +}; + +static const struct phytmac_statistics queue_statistics[] = { + STAT_TITLE("rx_packets"), + STAT_TITLE("rx_bytes"), + STAT_TITLE("rx_dropped"), + STAT_TITLE("tx_packets"), + STAT_TITLE("tx_bytes"), + STAT_TITLE("tx_dropped"), +}; + +#define QUEUE_STATS_LEN ARRAY_SIZE(queue_statistics) + +struct phytmac_config { + struct phytmac_hw_if *hw_if; + u32 caps; + u32 tsu_rate; + u16 queue_num; +}; + +struct phytmac_stats { + u64 tx_octets; + u64 tx_packets; + u64 tx_bcast_packets; + u64 tx_mcase_packets; + u64 tx_pause_packets; + u64 tx_64_byte_packets; + u64 tx_65_127_byte_packets; + u64 tx_128_255_byte_packets; + u64 tx_256_511_byte_packets; + u64 tx_512_1023_byte_packets; + u64 tx_1024_1518_byte_packets; + u64 tx_more_than_1518_byte_packets; + u64 tx_underrun; + u64 tx_single_collisions; + u64 tx_multiple_collisions; + u64 tx_excessive_collisions; + u64 tx_late_collisions; + u64 tx_deferred; + u64 tx_carrier_sense_errors; + u64 rx_octets; + u64 rx_packets; + u64 rx_bcast_packets; + u64 rx_mcast_packets; + u64 rx_pause_packets; + u64 rx_64_byte_packets; + u64 rx_65_127_byte_packets; + u64 rx_128_255_byte_packets; + u64 rx_256_511_byte_packets; + u64 rx_512_1023_byte_packets; + u64 rx_1024_1518_byte_packets; + u64 rx_more_than_1518_byte_packets; + u64 rx_undersized_packets; + u64 rx_oversize_packets; + u64 rx_jabbers; + u64 rx_fcs_errors; + u64 rx_length_errors; + u64 rx_symbol_errors; + u64 rx_alignment_errors; + u64 rx_resource_over; + u64 rx_overruns; + u64 rx_iphdr_csum_errors; + u64 rx_tcp_csum_errors; + u64 rx_udp_csum_errors; +}; + +struct ts_incr { + u32 sub_ns; + u32 ns; +}; + +enum phytmac_bd_control { + TS_DISABLED, + TS_FRAME_PTP_EVENT_ONLY, + TS_ALL_PTP_FRAMES, + TS_ALL_FRAMES, +}; + +#ifdef CONFIG_PHYTMAC_ENABLE_PTP +struct phytmac_dma_desc { + u32 desc0; + u32 desc1; + u32 desc2; + u32 desc3; + u32 desc4; + u32 desc5; +}; +#else +struct phytmac_dma_desc { + u32 desc0; + u32 desc1; + u32 desc2; + u32 desc3; +}; +#endif + +struct phytmac_tx_skb { + struct sk_buff *skb; + dma_addr_t addr; + size_t length; + bool mapped_as_page; +}; + +struct phytmac_tx_ts { + struct sk_buff *skb; + u32 ts_1; + u32 ts_2; +}; + +struct phytmac_queue { + struct phytmac *pdata; + int irq; + int index; + + /* tx queue info */ + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_xmit_more; + dma_addr_t tx_ring_addr; + struct work_struct tx_error_task; + struct napi_struct tx_napi; + struct phytmac_dma_desc *tx_ring; + struct phytmac_tx_skb *tx_skb; + /* Lock to protect tx */ + spinlock_t tx_lock; + + /* rx queue info */ + dma_addr_t rx_ring_addr; + unsigned int rx_head; + unsigned int rx_tail; + struct phytmac_dma_desc *rx_ring; + struct sk_buff **rx_skb; + struct napi_struct rx_napi; + struct phytmac_queue_stats stats; + +#ifdef CONFIG_PHYTMAC_ENABLE_PTP + struct work_struct tx_ts_task; + unsigned int tx_ts_head; + unsigned int tx_ts_tail; + struct phytmac_tx_ts tx_timestamps[128]; +#endif +}; + +struct ethtool_rx_fs_item { + struct ethtool_rx_flow_spec fs; + struct list_head list; +}; + +struct ethtool_rx_fs_list { + struct list_head list; + unsigned int count; +}; + +struct phytmac_msg { + struct completion tx_msg_comp; + u32 tx_msg_ring_size; + u32 rx_msg_ring_size; + u32 tx_msg_head; + u32 tx_msg_tail; + u32 rx_msg_head; + u32 rx_msg_tail; + /* Lock to protect msg */ + spinlock_t msg_lock; +}; + +struct ts_ctrl { + int tx_control; + int rx_control; + int one_step; +}; + +struct phytmac { + void __iomem *mac_regs; + void __iomem *msg_regs; + void __iomem *mhu_regs; + struct pci_dev *pcidev; + struct platform_device *platdev; + struct net_device *ndev; + struct device *dev; + struct ncsi_dev *ncsidev; + struct fwnode_handle *fwnode; + struct phytmac_hw_if *hw_if; + struct phytmac_msg msg_ring; + int dev_type; + int sfp_irq; + int irq_type; + int queue_irq[PHYTMAC_MAX_QUEUES]; + u32 rx_irq_mask; + u32 tx_irq_mask; + u32 msg_enable; + u32 capacities; + u32 max_tx_length; + u32 min_tx_length; + u32 jumbo_len; + u32 wol; + u32 lpi; + u32 power_state; + struct work_struct restart_task; + /* Lock to protect mac config */ + spinlock_t lock; + /* Lock to protect msg tx */ + spinlock_t msg_lock; + u32 rx_ring_size; + u32 tx_ring_size; + u32 dma_data_width; + u32 dma_addr_width; + u32 dma_burst_length; + int rx_bd_prefetch; + int tx_bd_prefetch; + int rx_buffer_len; + u16 queues_max_num; + u16 queues_num; + struct phytmac_queue queues[PHYTMAC_MAX_QUEUES]; + struct phytmac_stats stats; + u64 ethtool_stats[PHYTMAC_STATS_LEN + + QUEUE_STATS_LEN * PHYTMAC_MAX_QUEUES]; + int use_ncsi; + int use_mii; + struct mii_bus *mii_bus; + struct phylink *phylink; + struct phylink_config phylink_config; + struct phylink_pcs phylink_pcs; + int pause; + phy_interface_t phy_interface; + int speed; + int duplex; + int autoneg; + /* 1588 */ + spinlock_t ts_clk_lock; /* clock locking */ + unsigned int ts_rate; + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + struct ts_incr ts_incr; + struct hwtstamp_config ts_config; + struct ts_ctrl ts_ctrl; + /* RX queue filer rule set */ + struct ethtool_rx_fs_list rx_fs_list; + /* Lock to protect fs */ + spinlock_t rx_fs_lock; + unsigned int max_rx_fs; +}; + +struct phytmac_hw_if { + int (*init_msg_ring)(struct phytmac *pdata); + int (*init_hw)(struct phytmac *pdata); + void (*reset_hw)(struct phytmac *pdata); + int (*init_ring_hw)(struct phytmac *pdata); + int (*poweron)(struct phytmac *pdata, int on); + int (*set_wol)(struct phytmac *pdata, int wake); + int (*get_feature)(struct phytmac *pdata); + int (*set_mac_address)(struct phytmac *pdata, const u8 *addr); + int (*get_mac_address)(struct phytmac *pdata, u8 *addr); + int (*enable_promise)(struct phytmac *pdata, int enable); + int (*enable_multicast)(struct phytmac *pdata, int enable); + int (*set_hash_table)(struct phytmac *pdata, unsigned long *mc_filter); + int (*enable_rx_csum)(struct phytmac *pdata, int enable); + int (*enable_tx_csum)(struct phytmac *pdata, int enable); + int (*enable_pause)(struct phytmac *pdata, int enable); + int (*enable_autoneg)(struct phytmac *pdata, int enable); + int (*enable_network)(struct phytmac *pdata, int enable, int rx_tx); + void (*get_stats)(struct phytmac *pdata); + void (*get_regs)(struct phytmac *pdata, u32 *reg_buff); + int (*set_speed)(struct phytmac *pdata); + + void (*mac_config)(struct phytmac *pdata, u32 mode, + const struct phylink_link_state *state); + int (*mac_linkup)(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex); + int (*mac_linkdown)(struct phytmac *pdata); + int (*pcs_linkup)(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex); + int (*pcs_linkdown)(struct phytmac *pdata); + unsigned int (*get_link)(struct phytmac *pdata, phy_interface_t interface); + + /* For RX coalescing */ + int (*config_rx_coalesce)(struct phytmac *pdata); + int (*config_tx_coalesce)(struct phytmac *pdata); + + /* ethtool nfc */ + int (*add_fdir_entry)(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow); + int (*del_fdir_entry)(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow); + + /* mido ops */ + int (*enable_mdio_control)(struct phytmac *pdata, int enable); + int (*mdio_read)(struct phytmac *pdata, int mii_id, int regnum); + int (*mdio_write)(struct phytmac *pdata, int mii_id, + int regnum, u16 data); + int (*mdio_read_c45)(struct phytmac *pdata, int mii_id, int devad, int regnum); + int (*mdio_write_c45)(struct phytmac *pdata, int mii_id, int devad, + int regnum, u16 data); + void (*enable_irq)(struct phytmac *pdata, int queue_index, u32 mask); + void (*disable_irq)(struct phytmac *pdata, int queue_index, u32 mask); + void (*clear_irq)(struct phytmac *pdata, int queue_index, u32 mask); + void (*mask_irq)(struct phytmac *pdata, int queue_index, u32 mask); + unsigned int (*get_irq)(struct phytmac *pdata, int queue_index); + unsigned int (*get_intx_mask)(struct phytmac *pdata); + unsigned int (*tx_map)(struct phytmac_queue *pdata, u32 tx_tail, + struct packet_info *packet); + void (*init_rx_map)(struct phytmac_queue *queue, u32 index); + unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); + unsigned int (*rx_clean)(struct phytmac_queue *queue, u32 cleaned_count); + void (*transmit)(struct phytmac_queue *queue); + void (*restart)(struct phytmac *pdata); + int (*tx_complete)(const struct phytmac_dma_desc *desc); + int (*rx_complete)(const struct phytmac_dma_desc *desc); + int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); + dma_addr_t (*get_desc_addr)(const struct phytmac_dma_desc *desc); + bool (*rx_checksum)(const struct phytmac_dma_desc *desc); + void (*set_desc_rxused)(struct phytmac_dma_desc *desc); + bool (*rx_single_buffer)(const struct phytmac_dma_desc *desc); + bool (*rx_pkt_start)(const struct phytmac_dma_desc *desc); + bool (*rx_pkt_end)(const struct phytmac_dma_desc *desc); + void (*clear_rx_desc)(struct phytmac_queue *queue, int begin, int end); + void (*clear_tx_desc)(struct phytmac_queue *queue); + /* ptp */ + void (*init_ts_hw)(struct phytmac *pdata); + void (*get_time)(struct phytmac *pdata, struct timespec64 *ts); + void (*set_time)(struct phytmac *pdata, time64_t sec, long nsec); + int (*set_ts_config)(struct phytmac *pdata, struct ts_ctrl *ctrl); + void (*clear_time)(struct phytmac *pdata); + int (*set_incr)(struct phytmac *pdata, struct ts_incr *incr); + int (*adjust_fine)(struct phytmac *pdata, long ppm, bool negative); + int (*adjust_time)(struct phytmac *pdata, s64 delta, int neg); + int (*ts_valid)(struct phytmac *pdata, struct phytmac_dma_desc *desc, + int direction); + void (*get_timestamp)(struct phytmac *pdata, u32 ts_1, u32 ts_2, + struct timespec64 *ts); + unsigned int (*get_ts_rate)(struct phytmac *pdata); +}; + +/* mhu */ +#define PHYTMAC_MHU_AP_CPP_STAT 0x00 +#define PHYTMAC_MHU_AP_CPP_SET 0x04 +#define PHYTMAC_MHU_CPP_DATA0 0x18 +#define PHYTMAC_MHU_CPP_DATA1 0x1c + +#define PHYTMAC_MHU_STAT_BUSY_INDEX 0 +#define PHYTMAC_MHU_STAT_BUSY_WIDTH 1 + +#define PHYTMAC_MHU_SET_INDEX 0 +#define PHYTMAC_MHU_SET_WIDTH 1 + +#define PHYTMAC_DATA0_FREE_INDEX 0 +#define PHYTMAC_DATA0_FREE_WIDTH 1 +#define PHYTMAC_DATA0_DOMAIN_INDEX 1 +#define PHYTMAC_DATA0_DOMAIN_WIDTH 7 +#define PHYTMAC_DATA0_MSG_INDEX 8 +#define PHYTMAC_DATA0_MSG_WIDTH 8 +#define PHYTMAC_MSG_PM 0x04 +#define PHYTMAC_DATA0_PRO_INDEX 16 +#define PHYTMAC_DATA0_PRO_WIDTH 8 +#define PHYTMAC_PRO_ID 0x11 +#define PHYTMAC_DATA0_PAYLOAD_INDEX 24 +#define PHYTMAC_DATA0_PAYLOAD_WIDTH 8 + +#define PHYTMAC_DATA1_STAT_INDEX 0 +#define PHYTMAC_DATA1_STAT_WIDTH 28 +#define PHYTMAC_STATON 8 +#define PHYTMAC_STATOFF 0 +#define PHYTMAC_DATA1_MUST0_INDEX 28 +#define PHYTMAC_DATA1_MUST0_WIDTH 2 +#define PHYTMAC_DATA1_STATTYPE_INDEX 30 +#define PHYTMAC_DATA1_STATTYPE_WIDTH 1 +#define PHYTMAC_STATTYPE 0x1 +#define PHYTMAC_DATA1_MUST1_INDEX 31 +#define PHYTMAC_DATA1_MUST1_WIDTH 1 + +#define PHYTMAC_MHU_READ(_pdata, _reg) \ + __raw_readl((_pdata)->mhu_regs + (_reg)) +#define PHYTMAC_MHU_WRITE(_pdata, _reg, _val) \ + __raw_writel((_val), (_pdata)->mhu_regs + (_reg)) +#define PHYTMAC_READ_STAT(pdata) PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_AP_CPP_STAT) +#define PHYTMAC_READ_DATA0(pdata) PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA0) +#define PHYTMAC_TIMEOUT 1000000000 /* in usecs */ + +struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, + unsigned int index); +inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, + unsigned int index); +inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, + unsigned int index); +void phytmac_set_ethtool_ops(struct net_device *netdev); +int phytmac_drv_probe(struct phytmac *pdata); +int phytmac_drv_remove(struct phytmac *pdata); +int phytmac_drv_suspend(struct phytmac *pdata); +int phytmac_drv_resume(struct phytmac *pdata); +struct phytmac *phytmac_alloc_pdata(struct device *dev); +void phytmac_free_pdata(struct phytmac *pdata); +int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size); +#endif diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c new file mode 100644 index 00000000000000..592d2d9dc6d4b2 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" +#include "phytmac_v2.h" +#include "phytmac_ptp.h" + +static void phytmac_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *stats, + u64 *data) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + int i = PHYTMAC_STATS_LEN, j; + int q; + struct phytmac_queue *queue; + unsigned long *stat; + + hw_if->get_stats(pdata); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) + for (j = 0, stat = &queue->stats.rx_packets; j < QUEUE_STATS_LEN; ++j, ++stat) + pdata->ethtool_stats[i++] = *stat; + + memcpy(data, &pdata->ethtool_stats, sizeof(u64) + * (PHYTMAC_STATS_LEN + QUEUE_STATS_LEN * pdata->queues_num)); +} + +static inline int phytmac_get_sset_count(struct net_device *ndev, int sset) +{ + struct phytmac *pdata = netdev_priv(ndev); + + switch (sset) { + case ETH_SS_STATS: + return PHYTMAC_STATS_LEN + QUEUE_STATS_LEN * pdata->queues_num; + default: + return -EOPNOTSUPP; + } +} + +static void phytmac_get_ethtool_strings(struct net_device *ndev, u32 sset, u8 *p) +{ + char stat_string[ETH_GSTRING_LEN]; + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_queue *queue; + unsigned int i; + unsigned int q; + + switch (sset) { + case ETH_SS_STATS: + for (i = 0; i < PHYTMAC_STATS_LEN; i++, p += ETH_GSTRING_LEN) + memcpy(p, phytmac_statistics[i].stat_string, + ETH_GSTRING_LEN); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + for (i = 0; i < QUEUE_STATS_LEN; i++, p += ETH_GSTRING_LEN) { + snprintf(stat_string, ETH_GSTRING_LEN, "q%d_%s", + q, queue_statistics[i].stat_string); + memcpy(p, stat_string, ETH_GSTRING_LEN); + } + } + break; + } +} + +static inline int phytmac_get_regs_len(struct net_device *ndev) +{ + return PHYTMAC_GREGS_LEN; +} + +static void phytmac_get_regs(struct net_device *ndev, + struct ethtool_regs *regs, + void *p) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 *regs_buff = p; + + memset(p, 0, PHYTMAC_GREGS_LEN * sizeof(u32)); + + hw_if->get_regs(pdata, regs_buff); +} + +static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct phytmac *pdata = netdev_priv(ndev); + + phylink_ethtool_get_wol(pdata->phylink, wol); + + if (pdata->wol & PHYTMAC_WAKE_MAGIC) { + wol->wolopts |= WAKE_MAGIC; + wol->supported |= WAKE_MAGIC; + } + if (pdata->wol & PHYTMAC_WAKE_ARP) { + wol->wolopts |= WAKE_ARP; + wol->supported |= WAKE_ARP; + } + if (pdata->wol & PHYTMAC_WAKE_UCAST) { + wol->wolopts |= WAKE_UCAST; + wol->supported |= WAKE_UCAST; + } + if (pdata->wol & PHYTMAC_WAKE_MCAST) { + wol->wolopts |= WAKE_MCAST; + wol->supported |= WAKE_MCAST; + } +} + +static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct phytmac *pdata = netdev_priv(ndev); + int ret; + + ret = phylink_ethtool_set_wol(pdata->phylink, wol); + + if (!ret || ret != -EOPNOTSUPP) + return ret; + + pdata->wol = 0; + + if (wol->wolopts & WAKE_MAGIC) + pdata->wol |= PHYTMAC_WAKE_MAGIC; + if (wol->wolopts & WAKE_ARP) + pdata->wol |= PHYTMAC_WAKE_ARP; + if (wol->wolopts & WAKE_UCAST) + pdata->wol |= PHYTMAC_WAKE_UCAST; + if (wol->wolopts & WAKE_MCAST) + pdata->wol |= PHYTMAC_WAKE_MCAST; + + device_set_wakeup_enable(pdata->dev, pdata->wol ? 1 : 0); + + return 0; +} + +static void phytmac_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct phytmac *pdata = netdev_priv(ndev); + + ring->rx_max_pending = MAX_RX_RING_SIZE; + ring->tx_max_pending = MAX_TX_RING_SIZE; + + ring->rx_pending = pdata->rx_ring_size; + ring->tx_pending = pdata->tx_ring_size; +} + +static int phytmac_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct phytmac *pdata = netdev_priv(ndev); + u32 new_rx_size, new_tx_size; + + if (ring->rx_mini_pending || ring->rx_jumbo_pending) + return -EINVAL; + + new_rx_size = clamp_t(u32, ring->rx_pending, + MIN_RX_RING_SIZE, MAX_RX_RING_SIZE); + new_rx_size = roundup_pow_of_two(new_rx_size); + + new_tx_size = clamp_t(u32, ring->tx_pending, + MIN_TX_RING_SIZE, MAX_TX_RING_SIZE); + new_tx_size = roundup_pow_of_two(new_tx_size); + + if (EQUAL(new_tx_size, pdata->tx_ring_size) && + EQUAL(new_rx_size, pdata->rx_ring_size)) { + /* nothing to do */ + return 0; + } + + return phytmac_reset_ringsize(pdata, new_rx_size, new_tx_size); +} + +static int phytmac_get_ts_info(struct net_device *ndev, + struct ethtool_ts_info *info) +{ + struct phytmac *pdata = netdev_priv(ndev); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + info->so_timestamping = + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE | + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->tx_types = + (1 << HWTSTAMP_TX_ONESTEP_SYNC) | + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_ALL); + + info->phc_index = pdata->ptp_clock ? ptp_clock_index(pdata->ptp_clock) : -1; + + return 0; + } + + return ethtool_op_get_ts_info(ndev, info); +} + +static int phytmac_add_fdir_ethtool(struct net_device *ndev, + struct ethtool_rxnfc *cmd) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_flow_spec *fs = &cmd->fs; + struct ethtool_rx_fs_item *item, *newfs; + unsigned long flags; + int ret = -EINVAL; + bool added = false; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (cmd->fs.location >= pdata->max_rx_fs || + cmd->fs.ring_cookie >= pdata->queues_num) { + return -EINVAL; + } + + newfs = kmalloc(sizeof(*newfs), GFP_KERNEL); + if (!newfs) + return -ENOMEM; + memcpy(&newfs->fs, fs, sizeof(newfs->fs)); + + netdev_dbg(ndev, "Adding flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n", + fs->flow_type, (int)fs->ring_cookie, fs->location, + htonl(fs->h_u.tcp_ip4_spec.ip4src), + htonl(fs->h_u.tcp_ip4_spec.ip4dst), + htons(fs->h_u.tcp_ip4_spec.psrc), htons(fs->h_u.tcp_ip4_spec.pdst)); + + spin_lock_irqsave(&pdata->rx_fs_lock, flags); + + /* find correct place to add in list */ + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (item->fs.location > newfs->fs.location) { + list_add_tail(&newfs->list, &item->list); + added = true; + break; + } else if (item->fs.location == fs->location) { + netdev_err(ndev, "Rule not added: location %d not free!\n", + fs->location); + ret = -EBUSY; + goto err; + } + } + if (!added) + list_add_tail(&newfs->list, &pdata->rx_fs_list.list); + + hw_if->add_fdir_entry(pdata, fs); + pdata->rx_fs_list.count++; + + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + return 0; + +err: + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + kfree(newfs); + return ret; +} + +static int phytmac_del_fdir_ethtool(struct net_device *ndev, + struct ethtool_rxnfc *cmd) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_fs_item *item; + struct ethtool_rx_flow_spec *fs; + unsigned long flags; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + spin_lock_irqsave(&pdata->rx_fs_lock, flags); + + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (item->fs.location == cmd->fs.location) { + /* disable screener regs for the flow entry */ + fs = &item->fs; + netdev_dbg(ndev, "Deleting flow filter entry,type=%u,queue=%u,loc=%u,src=%08X,dst=%08X,ps=%u,pd=%u\n", + fs->flow_type, (int)fs->ring_cookie, fs->location, + htonl(fs->h_u.tcp_ip4_spec.ip4src), + htonl(fs->h_u.tcp_ip4_spec.ip4dst), + htons(fs->h_u.tcp_ip4_spec.psrc), + htons(fs->h_u.tcp_ip4_spec.pdst)); + + hw_if->del_fdir_entry(pdata, fs); + + list_del(&item->list); + pdata->rx_fs_list.count--; + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + kfree(item); + return 0; + } + } + + spin_unlock_irqrestore(&pdata->rx_fs_lock, flags); + return -EINVAL; +} + +static int phytmac_get_fdir_entry(struct net_device *ndev, + struct ethtool_rxnfc *cmd) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_fs_item *item; + + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (item->fs.location == cmd->fs.location) { + memcpy(&cmd->fs, &item->fs, sizeof(cmd->fs)); + return 0; + } + } + return -EINVAL; +} + +static int phytmac_get_all_fdir_entries(struct net_device *ndev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct ethtool_rx_fs_item *item; + u32 cnt = 0; + + list_for_each_entry(item, &pdata->rx_fs_list.list, list) { + if (cnt == cmd->rule_cnt) + return -EMSGSIZE; + rule_locs[cnt] = item->fs.location; + cnt++; + } + cmd->data = pdata->max_rx_fs; + cmd->rule_cnt = cnt; + + return 0; +} + +static int phytmac_get_rxnfc(struct net_device *ndev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct phytmac *pdata = netdev_priv(ndev); + int ret = 0; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = pdata->queues_num; + break; + case ETHTOOL_GRXCLSRLCNT: + cmd->rule_cnt = pdata->rx_fs_list.count; + break; + case ETHTOOL_GRXCLSRULE: + ret = phytmac_get_fdir_entry(ndev, cmd); + break; + case ETHTOOL_GRXCLSRLALL: + ret = phytmac_get_all_fdir_entries(ndev, cmd, rule_locs); + break; + default: + netdev_err(ndev, "Command parameter %d is not supported\n", cmd->cmd); + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int phytmac_set_rxnfc(struct net_device *ndev, struct ethtool_rxnfc *cmd) +{ + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + return phytmac_add_fdir_ethtool(ndev, cmd); + case ETHTOOL_SRXCLSRLDEL: + return phytmac_del_fdir_ethtool(ndev, cmd); + default: + netdev_err(ndev, "Command parameter %d is not supported\n", cmd->cmd); + return -EOPNOTSUPP; + } +} + +static int phytmac_get_link_ksettings(struct net_device *ndev, + struct ethtool_link_ksettings *kset) +{ + int ret = 0; + struct phytmac *pdata = netdev_priv(ndev); + u32 supported = 0; + u32 advertising = 0; + + if (!ndev->phydev) { + if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { + supported = SUPPORTED_10000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_10000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_10000; + } else if (pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { + supported = SUPPORTED_2500baseX_Full | SUPPORTED_Pause; + advertising = ADVERTISED_2500baseX_Full | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_2500; + } else if (pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX) { + supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE + | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE + | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_100; + } else if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII) { + supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full + | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full + | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Pause; + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = DUPLEX_FULL; + kset->base.speed = SPEED_1000; + } + + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.supported, + supported); + ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.advertising, + advertising); + } else { + phy_ethtool_get_link_ksettings(ndev, kset); + } + + return ret; +} + +static int phytmac_set_link_ksettings(struct net_device *ndev, + const struct ethtool_link_ksettings *kset) +{ + int ret = 0; + + if (!ndev->phydev) { + netdev_err(ndev, "fixed link interface not supported set link\n"); + ret = -EOPNOTSUPP; + } else { + phy_ethtool_set_link_ksettings(ndev, kset); + } + + return ret; +} + +static inline void phytmac_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct phytmac *pdata = netdev_priv(ndev); + + pause->rx_pause = pdata->pause; + pause->tx_pause = pdata->pause; +} + +static int phytmac_set_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (pause->rx_pause != pdata->pause) + hw_if->enable_pause(pdata, pause->rx_pause); + + pdata->pause = pause->rx_pause; + + return 0; +} + +static inline void phytmac_get_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct phytmac *pdata = netdev_priv(ndev); + + ch->max_combined = pdata->queues_max_num; + ch->combined_count = pdata->queues_num; +} + +static int phytmac_set_channels(struct net_device *ndev, + struct ethtool_channels *ch) +{ + struct phytmac *pdata = netdev_priv(ndev); + + if (netif_running(ndev)) + return -EBUSY; + + if (ch->combined_count > pdata->queues_max_num) { + netdev_err(ndev, "combined channel count cannot exceed %u\n", + ch->combined_count); + + return -EINVAL; + } + + pdata->queues_num = ch->combined_count; + + return 0; +} + +static inline u32 phytmac_get_msglevel(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + + return pdata->msg_enable; +} + +static inline void phytmac_set_msglevel(struct net_device *ndev, u32 level) +{ + struct phytmac *pdata = netdev_priv(ndev); + + pdata->msg_enable = level; +} + +static const struct ethtool_ops phytmac_ethtool_ops = { + .get_regs_len = phytmac_get_regs_len, + .get_regs = phytmac_get_regs, + .get_msglevel = phytmac_get_msglevel, + .set_msglevel = phytmac_set_msglevel, + .get_link = ethtool_op_get_link, + .get_ts_info = phytmac_get_ts_info, + .get_ethtool_stats = phytmac_get_ethtool_stats, + .get_strings = phytmac_get_ethtool_strings, + .get_sset_count = phytmac_get_sset_count, + .get_link_ksettings = phytmac_get_link_ksettings, + .set_link_ksettings = phytmac_set_link_ksettings, + .get_ringparam = phytmac_get_ringparam, + .set_ringparam = phytmac_set_ringparam, + .get_rxnfc = phytmac_get_rxnfc, + .set_rxnfc = phytmac_set_rxnfc, + .get_pauseparam = phytmac_get_pauseparam, + .set_pauseparam = phytmac_set_pauseparam, + .get_channels = phytmac_get_channels, + .set_channels = phytmac_set_channels, + .get_wol = phytmac_get_wol, + .set_wol = phytmac_set_wol, +}; + +void phytmac_set_ethtool_ops(struct net_device *ndev) +{ + ndev->ethtool_ops = &phytmac_ethtool_ops; +} + diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c new file mode 100644 index 00000000000000..b0977f5cae755c --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -0,0 +1,2236 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium Ethernet Controller driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_ptp.h" + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +#define RX_BUFFER_MULTIPLE 64 /* bytes */ +#define MAX_MTU 3072 +#define RING_ADDR_INTERVAL 128 + +#define RX_RING_BYTES(pdata) (sizeof(struct phytmac_dma_desc) \ + * (pdata)->rx_ring_size) + +#define TX_RING_BYTES(pdata) (sizeof(struct phytmac_dma_desc)\ + * (pdata)->tx_ring_size) + +/* Max length of transmit frame must be a multiple of 8 bytes */ +#define PHYTMAC_TX_LEN_ALIGN 8 +/* Limit maximum TX length as per Cadence TSO errata. This is to avoid a + * false amba_error in TX path from the DMA assuming there is not enough + * space in the SRAM (16KB) even when there is. + */ + +static int phytmac_change_mtu(struct net_device *ndev, int new_mtu) +{ + if (netif_running(ndev)) + return -EBUSY; + + if (new_mtu > MAX_MTU) { + netdev_info(ndev, "Can not set MTU over %d.\n", MAX_MTU); + return -EINVAL; + } + + ndev->mtu = new_mtu; + + return 0; +} + +static int phytmac_set_mac_address(struct net_device *netdev, void *addr) +{ + struct phytmac *pdata = netdev_priv(netdev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct sockaddr *saddr = addr; + + if (netif_msg_drv(pdata)) + netdev_info(netdev, "phytmac set mac address"); + + if (!is_valid_ether_addr(saddr->sa_data)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(netdev, saddr->sa_data); + + hw_if->set_mac_address(pdata, saddr->sa_data); + + return 0; +} + +static int phytmac_get_mac_address(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + u8 addr[6]; + + hw_if->get_mac_address(pdata, addr); + + if (is_valid_ether_addr(addr)) { + eth_hw_addr_set(pdata->ndev, addr); + return 0; + } + dev_info(pdata->dev, "invalid hw address, using random\n"); + eth_hw_addr_random(pdata->ndev); + + return 0; +} + +static int phytmac_mdio_read_c22(struct mii_bus *bus, int mii_id, int regnum) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int data; + + data = hw_if->mdio_read(pdata, mii_id, regnum); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio read c22 mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return data; +} + +static int phytmac_mdio_write_c22(struct mii_bus *bus, int mii_id, int regnum, u16 data) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int ret; + + ret = hw_if->mdio_write(pdata, mii_id, regnum, data); + if (ret) + netdev_err(pdata->ndev, "mdio %d, reg %x, data %x write failed!\n", + mii_id, regnum, data); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio write c22 mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return 0; +} + +static int phytmac_mdio_read_c45(struct mii_bus *bus, int mii_id, int devad, int regnum) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int data; + + data = hw_if->mdio_read_c45(pdata, mii_id, devad, regnum); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio read c45 mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return data; +} + +static int phytmac_mdio_write_c45(struct mii_bus *bus, int mii_id, int devad, int regnum, u16 data) +{ + struct phytmac *pdata = bus->priv; + struct phytmac_hw_if *hw_if = pdata->hw_if; + int ret; + + ret = hw_if->mdio_write_c45(pdata, mii_id, devad, regnum, data); + if (ret) + netdev_err(pdata->ndev, "mdio %d, reg %x, data %x write failed!\n", + mii_id, regnum, data); + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "mdio write c45 mii_id=%d, regnum=%x, data=%x\n", + mii_id, regnum, data); + + return 0; +} + +static inline int hash_bit_value(int bitnr, __u8 *addr) +{ + if (addr[bitnr / 8] & (1 << (bitnr % 8))) + return 1; + return 0; +} + +/* Return the hash index value for the specified address. */ +static int phytmac_get_hash_index(__u8 *addr) +{ + int i, j, bitval; + int hash_index = 0; + + for (j = 0; j < 6; j++) { + for (i = 0, bitval = 0; i < 8; i++) + bitval ^= hash_bit_value(i * 6 + j, addr); + + hash_index |= (bitval << j); + } + + return hash_index; +} + +static void phytmac_set_mac_hash_table(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct netdev_hw_addr *ha; + unsigned long mc_filter[2]; + unsigned int bitnr; + + mc_filter[0] = 0; + mc_filter[1] = 0; + + netdev_for_each_mc_addr(ha, ndev) { + bitnr = phytmac_get_hash_index(ha->addr); + mc_filter[bitnr >> 5] |= 1 << (bitnr & 31); + } + + hw_if->set_hash_table(pdata, mc_filter); +} + +/* Enable/Disable promiscuous and multicast modes. */ +static void phytmac_set_rx_mode(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + hw_if->enable_promise(pdata, ndev->flags & IFF_PROMISC); + + hw_if->enable_multicast(pdata, ndev->flags & IFF_ALLMULTI); + if (!netdev_mc_empty(ndev)) + phytmac_set_mac_hash_table(ndev); +} + +static struct net_device_stats *phytmac_get_stats(struct net_device *dev) +{ + struct phytmac *pdata = netdev_priv(dev); + struct net_device_stats *nstat = &pdata->ndev->stats; + struct phytmac_stats *stat = &pdata->stats; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (pdata->power_state == PHYTMAC_POWEROFF) + return nstat; + + hw_if->get_stats(pdata); + + nstat->rx_errors = (stat->rx_fcs_errors + + stat->rx_alignment_errors + + stat->rx_overruns + + stat->rx_oversize_packets + + stat->rx_jabbers + + stat->rx_undersized_packets + + stat->rx_length_errors); + nstat->rx_dropped = stat->rx_resource_over; + nstat->tx_errors = (stat->tx_late_collisions + + stat->tx_excessive_collisions + + stat->tx_underrun + + stat->tx_carrier_sense_errors); + nstat->multicast = stat->rx_mcast_packets; + nstat->collisions = (stat->tx_single_collisions + + stat->tx_multiple_collisions + + stat->tx_excessive_collisions + + stat->tx_late_collisions); + nstat->rx_length_errors = (stat->rx_oversize_packets + + stat->rx_jabbers + + stat->rx_undersized_packets + + stat->rx_length_errors); + nstat->rx_over_errors = stat->rx_resource_over; + nstat->rx_crc_errors = stat->rx_fcs_errors; + nstat->rx_frame_errors = stat->rx_alignment_errors; + nstat->rx_fifo_errors = stat->rx_overruns; + nstat->tx_aborted_errors = stat->tx_excessive_collisions; + nstat->tx_carrier_errors = stat->tx_carrier_sense_errors; + nstat->tx_fifo_errors = stat->tx_underrun; + + return nstat; +} + +static int phytmac_calc_rx_buf_len(struct phytmac *pdata, u32 mtu) +{ + unsigned int size = mtu + ETH_HLEN + ETH_FCS_LEN; + int rx_buf_len = roundup(size, RX_BUFFER_MULTIPLE); + + netdev_dbg(pdata->ndev, "mtu [%u] rx_buffer_size [%u]\n", + mtu, rx_buf_len); + + return rx_buf_len; +} + +inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, + unsigned int index) +{ + return &queue->rx_ring[index & (queue->pdata->rx_ring_size - 1)]; +} + +struct sk_buff *phytmac_get_rx_skb(struct phytmac_queue *queue, + unsigned int index) +{ + return queue->rx_skb[index & (queue->pdata->rx_ring_size - 1)]; +} + +struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, + unsigned int index) +{ + return &queue->tx_skb[index & (queue->pdata->tx_ring_size - 1)]; +} + +inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, + unsigned int index) +{ + return &queue->tx_ring[index & (queue->pdata->tx_ring_size - 1)]; +} + +static int phytmac_free_tx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct phytmac_dma_desc *tx_ring_base = NULL; + dma_addr_t tx_ring_base_addr; + unsigned int q; + int size; + + queue = pdata->queues; + if (queue->tx_ring) { + tx_ring_base = queue->tx_ring; + tx_ring_base_addr = queue->tx_ring_addr; + } + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + kfree(queue->tx_skb); + queue->tx_skb = NULL; + + if (queue->tx_ring) + queue->tx_ring = NULL; + } + + if (tx_ring_base) { + size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + + RING_ADDR_INTERVAL); + dma_free_coherent(pdata->dev, size, tx_ring_base, tx_ring_base_addr); + } + + return 0; +} + +static int phytmac_free_rx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct sk_buff *skb; + struct phytmac_dma_desc *desc; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *rx_ring_base = NULL; + dma_addr_t rx_ring_base_addr; + dma_addr_t addr; + unsigned int q; + int size, i; + + queue = pdata->queues; + if (queue->rx_ring) { + rx_ring_base = queue->rx_ring; + rx_ring_base_addr = queue->rx_ring_addr; + } + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + if (queue->rx_skb) { + for (i = 0; i < pdata->rx_ring_size; i++) { + skb = phytmac_get_rx_skb(queue, i); + if (skb) { + desc = &queue->rx_ring[i]; + addr = hw_if->get_desc_addr(desc); + dma_unmap_single(pdata->dev, addr, pdata->rx_buffer_len, + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + skb = NULL; + } + } + + kfree(queue->rx_skb); + queue->rx_skb = NULL; + } + + if (queue->rx_ring) + queue->rx_ring = NULL; + } + + if (rx_ring_base) { + size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + + RING_ADDR_INTERVAL); + dma_free_coherent(pdata->dev, size, rx_ring_base, rx_ring_base_addr); + } + + return 0; +} + +static int phytmac_alloc_tx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct phytmac_dma_desc *tx_ring_base; + dma_addr_t tx_ring_base_addr; + unsigned int q; + int size; + + size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + + RING_ADDR_INTERVAL); + tx_ring_base = dma_alloc_coherent(pdata->dev, size, + &tx_ring_base_addr, GFP_KERNEL); + if (!tx_ring_base) + goto err; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + size = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + queue->tx_ring = (void *)tx_ring_base + q * size; + queue->tx_ring_addr = tx_ring_base_addr + q * size; + if (!queue->tx_ring) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated TX ring for queue %u of %d bytes at %08lx\n", + q, size, (unsigned long)queue->tx_ring_addr); + + size = pdata->tx_ring_size * sizeof(struct phytmac_tx_skb); + queue->tx_skb = kzalloc(size, GFP_KERNEL); + if (!queue->tx_skb) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated %d TX struct tx_skb entries at %p\n", + pdata->tx_ring_size, queue->tx_skb); + } + tx_ring_base = NULL; + + return 0; +err: + phytmac_free_tx_resource(pdata); + + return -ENOMEM; +} + +static int phytmac_alloc_rx_resource(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *rx_ring_base; + dma_addr_t rx_ring_base_addr; + unsigned int q; + int size; + int i; + + size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + + RING_ADDR_INTERVAL); + rx_ring_base = dma_alloc_coherent(pdata->dev, size, + &rx_ring_base_addr, GFP_KERNEL); + if (!rx_ring_base) + goto err; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + size = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + queue->rx_ring = (void *)rx_ring_base + q * size; + queue->rx_ring_addr = rx_ring_base_addr + q * size; + if (!queue->rx_ring) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated RX ring for queue %u of %d bytes at %08lx\n", + q, size, (unsigned long)queue->rx_ring_addr); + + for (i = 0; i < pdata->rx_ring_size; i++) + hw_if->init_rx_map(queue, i); + + size = pdata->rx_ring_size * sizeof(struct sk_buff *); + queue->rx_skb = kzalloc(size, GFP_KERNEL); + if (!queue->rx_skb) + goto err; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, + "Allocated %d RX struct sk_buff entries at %p\n", + pdata->rx_ring_size, queue->rx_skb); + } + rx_ring_base = NULL; + + return 0; +err: + phytmac_free_rx_resource(pdata); + + return -ENOMEM; +} + +static int phytmac_alloc_resource(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + int ret; + + pdata->rx_buffer_len = phytmac_calc_rx_buf_len(pdata, ndev->mtu); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "alloc resource, rx_buffer_len = %d\n", + pdata->rx_buffer_len); + + ret = phytmac_alloc_tx_resource(pdata); + if (ret) + return ret; + + ret = phytmac_alloc_rx_resource(pdata); + if (ret) { + phytmac_free_tx_resource(pdata); + return ret; + } + + return 0; +} + +static void phytmac_free_resource(struct phytmac *pdata) +{ + phytmac_free_tx_resource(pdata); + phytmac_free_rx_resource(pdata); +} + +static irqreturn_t phytmac_irq(int irq, void *data) +{ + struct phytmac_queue *queue = data; + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 status; + + status = hw_if->get_irq(pdata, queue->index); + + if (netif_msg_intr(pdata)) + netdev_info(pdata->ndev, "phymac irq status = %x\n", status); + + if (unlikely(!status)) + return IRQ_NONE; + + while (status) { + if (status & pdata->rx_irq_mask) { + /* Disable RX interrupts */ + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_RX_COMPLETE); + + if (napi_schedule_prep(&queue->rx_napi)) + __napi_schedule(&queue->rx_napi); + } + + if (status & (PHYTMAC_INT_TX_COMPLETE)) { + /* Disable TX interrupts */ + hw_if->disable_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + + if (napi_schedule_prep(&queue->tx_napi)) + __napi_schedule(&queue->tx_napi); + } + + if (status & PHYTMAC_INT_TX_ERR) + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_TX_ERR); + + if (status & PHYTMAC_INT_RX_OVERRUN) { + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_RX_OVERRUN); + pdata->stats.rx_overruns++; + } + status = hw_if->get_irq(pdata, queue->index); + } + + return IRQ_HANDLED; +} + +static irqreturn_t phytmac_intx_irq(int irq, void *data) +{ + struct phytmac *pdata = data; + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 irq_mask; + int i; + + irq_mask = hw_if->get_intx_mask(pdata); + + if (unlikely(!irq_mask)) + return IRQ_NONE; + + for (i = 0; i < pdata->queues_num; i++) { + if (irq_mask & BIT(i)) + phytmac_irq(irq, &pdata->queues[i]); + } + + return IRQ_HANDLED; +} + +static void phytmac_dump_pkt(struct phytmac *pdata, struct sk_buff *skb, bool tx) +{ + struct net_device *ndev = pdata->ndev; + + if (tx) { + netdev_dbg(ndev, "start_xmit: queue %u len %u head %p data %p tail %p end %p\n", + skb->queue_mapping, skb->len, skb->head, skb->data, + skb_tail_pointer(skb), skb_end_pointer(skb)); + } else { + netdev_dbg(ndev, "queue %u received skb of length %u, csum: %08x\n", + skb->queue_mapping, skb->len, skb->csum); + print_hex_dump(KERN_DEBUG, " mac: ", DUMP_PREFIX_ADDRESS, 16, 1, + skb_mac_header(skb), 16, true); + } + + print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_OFFSET, 16, 1, + skb->data, skb->len, true); +} + +static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phytmac_dma_desc *desc) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct sk_buff *skb; + unsigned int len; + dma_addr_t addr; + + skb = phytmac_get_rx_skb(queue, queue->rx_tail); + if (unlikely(!skb)) { + netdev_err(pdata->ndev, + "inconsistent Rx descriptor chain\n"); + pdata->ndev->stats.rx_dropped++; + queue->stats.rx_dropped++; + return NULL; + } + + queue->rx_skb[queue->rx_tail & (pdata->rx_ring_size - 1)] = NULL; + len = hw_if->get_rx_pkt_len(pdata, desc); + addr = hw_if->get_desc_addr(desc); + + skb_put(skb, len); + dma_unmap_single(pdata->dev, addr, + pdata->rx_buffer_len, DMA_FROM_DEVICE); + skb->protocol = eth_type_trans(skb, pdata->ndev); + skb_checksum_none_assert(skb); + + if (pdata->ndev->features & NETIF_F_RXCSUM && + !(pdata->ndev->flags & IFF_PROMISC) && + hw_if->rx_checksum(desc)) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (netif_msg_pktdata(pdata)) + phytmac_dump_pkt(pdata, skb, false); + + return skb; +} + +static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, + unsigned int first_frag, unsigned int last_frag, int len) +{ + unsigned int offset = 0; + unsigned int frag = 0; + unsigned int entry = 0; + dma_addr_t addr = 0; + struct sk_buff *skb; + struct phytmac_dma_desc *desc; + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int frag_len = pdata->rx_buffer_len; + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "rx frame %u - %u (len %u)\n", + first_frag, last_frag, len); + + skb = netdev_alloc_skb(pdata->ndev, len); + if (!skb) { + pdata->ndev->stats.rx_dropped++; + netdev_err(pdata->ndev, "rx frame alloc skb failed\n"); + return NULL; + } + + skb_checksum_none_assert(skb); + + if (pdata->ndev->features & NETIF_F_RXCSUM && + !(pdata->ndev->flags & IFF_PROMISC) && + hw_if->rx_checksum(phytmac_get_rx_desc(queue, last_frag))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + skb_put(skb, len); + + for (frag = first_frag; ; frag++) { + if (offset + frag_len > len) { + if (unlikely(frag != last_frag)) { + dev_kfree_skb_any(skb); + return NULL; + } + frag_len = len - offset; + } + + desc = phytmac_get_rx_desc(queue, frag); + addr = hw_if->get_desc_addr(desc); + dma_sync_single_for_cpu(pdata->dev, addr, frag_len, + DMA_FROM_DEVICE); + + entry = frag & (pdata->rx_ring_size - 1); + skb_copy_to_linear_data_offset(skb, offset, queue->rx_skb[entry]->data, frag_len); + + offset += pdata->rx_buffer_len; + + dma_sync_single_for_device(pdata->dev, addr, frag_len, + DMA_FROM_DEVICE); + + if (frag == last_frag) + break; + } + + skb->protocol = eth_type_trans(skb, pdata->ndev); + if (netif_msg_pktdata(pdata)) + phytmac_dump_pkt(pdata, skb, false); + + return skb; +} + +static struct sk_buff *phytmac_rx_mbuffer(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *desc; + struct sk_buff *skb = NULL; + unsigned int rx_tail = 0; + int first_frag = -1; + int len; + + for (rx_tail = queue->rx_tail; ; rx_tail++) { + desc = phytmac_get_rx_desc(queue, rx_tail); + if (hw_if->rx_pkt_start(desc)) { + if (first_frag != -1) + hw_if->clear_rx_desc(queue, first_frag, rx_tail); + first_frag = rx_tail; + continue; + } + + if (hw_if->rx_pkt_end(desc)) { + queue->rx_tail = rx_tail; + len = hw_if->get_rx_pkt_len(pdata, desc); + skb = phytmac_rx_frame(queue, first_frag, rx_tail, len); + first_frag = -1; + break; + } + } + return skb; +} + +static void phytmac_rx_clean(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int index, space; + dma_addr_t paddr; + struct sk_buff *skb; + unsigned int rx_unclean = 0; + + space = CIRC_SPACE(queue->rx_head, queue->rx_tail, + pdata->rx_ring_size); + + if (space < DEFAULT_RX_DESC_MIN_FREE) + return; + + index = queue->rx_head & (pdata->rx_ring_size - 1); + while (space > 0) { + if (!queue->rx_skb[index]) { + skb = netdev_alloc_skb(pdata->ndev, pdata->rx_buffer_len); + if (unlikely(!skb)) { + netdev_err(pdata->ndev, "rx clean alloc skb failed\n"); + break; + } + + paddr = dma_map_single(pdata->dev, skb->data, + pdata->rx_buffer_len, DMA_FROM_DEVICE); + if (dma_mapping_error(pdata->dev, paddr)) { + dev_kfree_skb(skb); + break; + } + + queue->rx_skb[index] = skb; + + hw_if->rx_map(queue, index, paddr); + } + + index = (index + 1) & (pdata->rx_ring_size - 1); + rx_unclean++; + space--; + } + + /* make newly descriptor to hardware */ + wmb(); + hw_if->rx_clean(queue, rx_unclean); + /* make newly descriptor to hardware */ + wmb(); + queue->rx_head += rx_unclean; + if (queue->rx_head >= pdata->rx_ring_size) + queue->rx_head &= (pdata->rx_ring_size - 1); +} + +static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, + int budget) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct sk_buff *skb; + struct phytmac_dma_desc *desc; + int count = 0; + + while (count < budget) { + desc = phytmac_get_rx_desc(queue, queue->rx_tail); + /* make newly desc to cpu */ + rmb(); + + if (!hw_if->rx_complete(desc)) + break; + + /* Ensure ctrl is at least as up-to-date as rxused */ + dma_rmb(); + + if (hw_if->rx_single_buffer(desc)) + skb = phytmac_rx_single(queue, desc); + else + skb = phytmac_rx_mbuffer(queue); + + if (!skb) { + netdev_warn(pdata->ndev, "phytmac rx skb is NULL\n"); + break; + } + + pdata->ndev->stats.rx_packets++; + queue->stats.rx_packets++; + pdata->ndev->stats.rx_bytes += skb->len; + queue->stats.rx_bytes += skb->len; + queue->rx_tail = (queue->rx_tail + 1) & (pdata->rx_ring_size - 1); + + count++; + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + phytmac_ptp_rxstamp(pdata, skb, desc); + + napi_gro_receive(napi, skb); + } + + phytmac_rx_clean(queue); + + return count; +} + +static void phytmac_tx_unmap(struct phytmac *pdata, struct phytmac_tx_skb *tx_skb, int budget) +{ + if (tx_skb->addr) { + if (tx_skb->mapped_as_page) + dma_unmap_page(pdata->dev, tx_skb->addr, + tx_skb->length, DMA_TO_DEVICE); + else + dma_unmap_single(pdata->dev, tx_skb->addr, + tx_skb->length, DMA_TO_DEVICE); + tx_skb->addr = 0; + } + + if (tx_skb->skb) { + napi_consume_skb(tx_skb->skb, budget); + tx_skb->skb = NULL; + } +} + +static int phytmac_maybe_stop_tx_queue(struct phytmac_queue *queue, + unsigned int count) +{ + struct phytmac *pdata = queue->pdata; + int space = CIRC_SPACE(queue->tx_tail, queue->tx_head, + pdata->tx_ring_size); + + if (space < count) { + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "Tx queue %d stopped, not enough descriptors available\n", + queue->index); + + netif_stop_subqueue(pdata->ndev, queue->index); + + return NETDEV_TX_BUSY; + } + + return 0; +} + +static int phytmac_maybe_wake_tx_queue(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + int space = CIRC_CNT(queue->tx_tail, queue->tx_head, + pdata->tx_ring_size); + + return (space <= (3 * pdata->tx_ring_size / 4)) ? 1 : 0; +} + +static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) +{ + struct phytmac *pdata = queue->pdata; + u16 queue_index = queue->index; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_tx_skb *tx_skb; + struct phytmac_dma_desc *desc; + int complete = 0; + int packet_count = 0; + unsigned int tail = queue->tx_tail; + unsigned int head; + + spin_lock(&pdata->lock); + + for (head = queue->tx_head; head != tail && packet_count < budget; ) { + struct sk_buff *skb; + + desc = phytmac_get_tx_desc(queue, head); + /* make newly desc to cpu */ + rmb(); + if (!hw_if->tx_complete(desc)) + break; + + /* Process all buffers of the current transmitted frame */ + for (;; head++) { + tx_skb = phytmac_get_tx_skb(queue, head); + skb = tx_skb->skb; + + if (skb) { + complete = 1; + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + if (unlikely(skb_shinfo(skb)->tx_flags & + SKBTX_HW_TSTAMP) && + !phytmac_ptp_one_step(skb)) { + phytmac_ptp_txstamp(queue, skb, desc); + } + } + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "desc %u (data %p) tx complete\n", + head, tx_skb->skb->data); + + pdata->ndev->stats.tx_packets++; + queue->stats.tx_packets++; + pdata->ndev->stats.tx_bytes += tx_skb->skb->len; + queue->stats.tx_bytes += tx_skb->skb->len; + packet_count++; + } + + /* Now we can safely release resources */ + phytmac_tx_unmap(pdata, tx_skb, budget); + + if (complete) { + complete = 0; + break; + } + } + + head++; + if (head >= pdata->tx_ring_size) + head &= (pdata->tx_ring_size - 1); + } + + queue->tx_head = head; + if (__netif_subqueue_stopped(pdata->ndev, queue_index) && + (phytmac_maybe_wake_tx_queue(queue))) + netif_wake_subqueue(pdata->ndev, queue_index); + spin_unlock(&pdata->lock); + + return packet_count; +} + +static int phytmac_rx_poll(struct napi_struct *napi, int budget) +{ + struct phytmac_queue *queue = container_of(napi, struct phytmac_queue, rx_napi); + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *desc; + int work_done; + + work_done = phytmac_rx(queue, napi, budget); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "RX poll: queue = %u, work_done = %d, budget = %d\n", + (unsigned int)(queue->index), work_done, budget); + if (work_done < budget && napi_complete_done(napi, work_done)) { + hw_if->enable_irq(pdata, queue->index, pdata->rx_irq_mask); + + desc = phytmac_get_rx_desc(queue, queue->rx_tail); + /* make newly desc to cpu */ + rmb(); + + if (hw_if->rx_complete(desc)) { + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_RX_COMPLETE); + + napi_schedule(napi); + } + } + + return work_done; +} + +static int phytmac_tx_poll(struct napi_struct *napi, int budget) +{ + struct phytmac_queue *queue = container_of(napi, struct phytmac_queue, tx_napi); + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_dma_desc *desc; + int work_done; + + work_done = phytmac_tx_clean(queue, budget); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "TX poll: queue = %u, work_done = %d, budget = %d\n", + (unsigned int)(queue->index), work_done, budget); + if (work_done < budget && napi_complete_done(napi, work_done)) { + hw_if->enable_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + if (queue->tx_head != queue->tx_tail) { + desc = phytmac_get_tx_desc(queue, queue->tx_head); + /* make newly desc to cpu */ + rmb(); + + if (hw_if->tx_complete(desc)) { + hw_if->disable_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + hw_if->clear_irq(pdata, queue->index, PHYTMAC_INT_TX_COMPLETE); + + napi_schedule(napi); + } + } + } + + return work_done; +} + +static inline int phytmac_clear_csum(struct sk_buff *skb) +{ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + /* make sure we can modify the header */ + if (unlikely(skb_cow_head(skb, 0))) + return -1; + + *(__sum16 *)(skb_checksum_start(skb) + skb->csum_offset) = 0; + return 0; +} + +static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) +{ + bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) || + skb_is_nonlinear(*skb); + int padlen = ETH_ZLEN - (*skb)->len; + int headroom = skb_headroom(*skb); + int tailroom = skb_tailroom(*skb); + struct sk_buff *nskb; + u32 fcs; + int i; + + if ((ndev->features & NETIF_F_HW_CSUM) || + !((*skb)->ip_summed != CHECKSUM_PARTIAL) || + skb_shinfo(*skb)->gso_size || phytmac_ptp_one_step(*skb)) + return 0; + + if (padlen <= 0) { + if (tailroom >= ETH_FCS_LEN) + goto add_fcs; + else if (!cloned && headroom + tailroom >= ETH_FCS_LEN) + padlen = 0; + else + padlen = ETH_FCS_LEN; + } else { + padlen += ETH_FCS_LEN; + } + + if (!cloned && headroom + tailroom >= padlen) { + (*skb)->data = memmove((*skb)->head, (*skb)->data, (*skb)->len); + skb_set_tail_pointer(*skb, (*skb)->len); + } else { + nskb = skb_copy_expand(*skb, 0, padlen, GFP_ATOMIC); + if (!nskb) + return -ENOMEM; + + dev_consume_skb_any(*skb); + *skb = nskb; + } + + if (padlen > ETH_FCS_LEN) + skb_put_zero(*skb, padlen - ETH_FCS_LEN); + +add_fcs: + fcs = crc32_le(~0, (*skb)->data, (*skb)->len); + fcs = ~fcs; + + for (i = 0; i < 4; ++i) + skb_put_u8(*skb, (fcs >> (i * 8)) & 0xff); + return 0; +} + +static int phytmac_packet_info(struct phytmac *pdata, + struct phytmac_queue *queue, struct sk_buff *skb, + struct packet_info *packet) +{ + int is_lso; + unsigned int hdrlen, f; + int desc_cnt; + + memset(packet, 0, sizeof(struct packet_info)); + + is_lso = (skb_shinfo(skb)->gso_size != 0); + + if (is_lso) { + /* length of headers */ + if (ip_hdr(skb)->protocol == IPPROTO_UDP) { + /* only queue eth + ip headers separately for UDP */ + hdrlen = skb_transport_offset(skb); + packet->lso = LSO_UFO; + packet->mss = skb_shinfo(skb)->gso_size + hdrlen + ETH_FCS_LEN; + } else { + hdrlen = skb_transport_offset(skb) + tcp_hdrlen(skb); + packet->lso = LSO_TSO; + packet->mss = skb_shinfo(skb)->gso_size; + } + + if (skb_headlen(skb) < hdrlen) { + dev_err(pdata->dev, "Error - LSO headers fragmented!!!\n"); + return NETDEV_TX_BUSY; + } + } else { + hdrlen = min(skb_headlen(skb), pdata->max_tx_length); + packet->lso = 0; + packet->mss = 0; + } + + packet->hdrlen = hdrlen; + + if (is_lso && (skb_headlen(skb) > hdrlen)) + desc_cnt = TXD_USE_COUNT(pdata, (skb_headlen(skb) - hdrlen)) + 1; + else + desc_cnt = TXD_USE_COUNT(pdata, hdrlen); + + for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) + desc_cnt += TXD_USE_COUNT(pdata, skb_frag_size(&skb_shinfo(skb)->frags[f])); + packet->desc_cnt = desc_cnt; + + if ((!(pdata->ndev->features & NETIF_F_HW_CSUM)) && + skb->ip_summed != CHECKSUM_PARTIAL && + !is_lso && + !phytmac_ptp_one_step(skb)) + packet->nocrc = 1; + else + packet->nocrc = 0; + + if (netif_msg_pktdata(pdata)) { + netdev_info(pdata->ndev, "packet info: desc_cnt=%d, nocrc=%d,ip_summed=%d\n", + desc_cnt, packet->nocrc, skb->ip_summed); + netdev_info(pdata->ndev, "packet info: mss=%d, lso=%d,skb_len=%d, nr_frags=%d\n", + packet->mss, packet->lso, skb->len, skb_shinfo(skb)->nr_frags); + } + + return 0; +} + +static unsigned int phytmac_tx_map(struct phytmac *pdata, + struct phytmac_queue *queue, + struct sk_buff *skb, + struct packet_info *packet) +{ + dma_addr_t mapping; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int len, i, tx_tail = queue->tx_tail; + struct phytmac_tx_skb *tx_skb = NULL; + unsigned int offset, size, count = 0; + unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags; + + len = skb_headlen(skb); + size = packet->hdrlen; + + offset = 0; + tx_tail = queue->tx_tail; + while (len) { + tx_skb = phytmac_get_tx_skb(queue, tx_tail); + + mapping = dma_map_single(pdata->dev, + skb->data + offset, + size, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, mapping)) + goto dma_error; + + /* Save info to properly release resources */ + tx_skb->skb = NULL; + tx_skb->addr = mapping; + tx_skb->length = size; + tx_skb->mapped_as_page = false; + + len -= size; + offset += size; + count++; + tx_tail++; + + size = min(len, pdata->max_tx_length); + } + + /* Then, map paged data from fragments */ + for (f = 0; f < nr_frags; f++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; + + len = skb_frag_size(frag); + offset = 0; + while (len) { + size = min(len, pdata->max_tx_length); + tx_skb = phytmac_get_tx_skb(queue, tx_tail); + mapping = skb_frag_dma_map(pdata->dev, frag, + offset, size, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, mapping)) + goto dma_error; + + /* Save info to properly release resources */ + tx_skb->skb = NULL; + tx_skb->addr = mapping; + tx_skb->length = size; + tx_skb->mapped_as_page = true; + + len -= size; + offset += size; + count++; + tx_tail++; + } + } + + /* Should never happen */ + if (unlikely(!tx_skb)) { + netdev_err(pdata->ndev, "BUG! empty skb!\n"); + return 0; + } + + /* This is the last buffer of the frame: save socket buffer */ + tx_skb->skb = skb; + + if (hw_if->tx_map(queue, tx_tail, packet)) { + netdev_err(pdata->ndev, "BUG!hw tx map failed!\n"); + return 0; + } + + queue->tx_tail = tx_tail & (pdata->tx_ring_size - 1); + + return count; + +dma_error: + netdev_err(pdata->ndev, "TX DMA map failed\n"); + + for (i = queue->tx_tail; i != tx_tail; i++) { + tx_skb = phytmac_get_tx_skb(queue, i); + phytmac_tx_unmap(pdata, tx_skb, 0); + } + + return 0; +} + +static inline void phytmac_init_ring(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_queue *queue; + unsigned int q = 0; + + for (queue = pdata->queues; q < pdata->queues_num; ++q) { + queue->tx_head = 0; + queue->tx_tail = 0; + hw_if->clear_tx_desc(queue); + + queue->rx_head = 0; + queue->rx_tail = 0; + phytmac_rx_clean(queue); + ++queue; + } + + hw_if->init_ring_hw(pdata); +} + +static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + u16 queue_index = skb->queue_mapping; + struct phytmac_queue *queue = &pdata->queues[queue_index]; + netdev_tx_t ret = NETDEV_TX_OK; + struct packet_info packet; + unsigned long flags; + + if (phytmac_clear_csum(skb)) { + dev_kfree_skb_any(skb); + return ret; + } + + if (phytmac_add_fcs(&skb, ndev)) { + dev_kfree_skb_any(skb); + return ret; + } + + ret = phytmac_packet_info(pdata, queue, skb, &packet); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (netif_msg_pktdata(pdata)) + phytmac_dump_pkt(pdata, skb, true); + + spin_lock_irqsave(&pdata->lock, flags); + /* Check that there are enough descriptors available */ + ret = phytmac_maybe_stop_tx_queue(queue, packet.desc_cnt); + if (ret) + goto tx_return; + + /* Map socket buffer for DMA transfer */ + if (!phytmac_tx_map(pdata, queue, skb, &packet)) { + dev_kfree_skb_any(skb); + goto tx_return; + } + + skb_tx_timestamp(skb); + /* Make newly descriptor to hardware */ + wmb(); + + hw_if->transmit(queue); + +tx_return: + spin_unlock_irqrestore(&pdata->lock, flags); + return ret; +} + +static int phytmac_phylink_connect(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + struct phy_device *phydev; + struct fwnode_handle *fwnode = dev_fwnode(pdata->dev); + int ret = 0; + + if (fwnode) + ret = phylink_fwnode_phy_connect(pdata->phylink, fwnode, 0); + + if (!fwnode || ret) { + if (pdata->mii_bus) { + phydev = phy_find_first(pdata->mii_bus); + if (!phydev) { + dev_err(pdata->dev, "no PHY found\n"); + return -ENXIO; + } + /* attach the mac to the phy */ + ret = phylink_connect_phy(pdata->phylink, phydev); + } else { + netdev_err(ndev, "Not mii register\n"); + return -ENXIO; + } + } + + if (ret) { + netdev_err(ndev, "Could not attach PHY (%d)\n", ret); + return ret; + } + + return 0; +} + +int phytmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + return 0; +} + +void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, int duplex) +{ + struct phytmac *pdata = container_of(pcs, struct phytmac, phylink_pcs); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "pcs link up, interface = %s, speed = %d, duplex = %d\n", + phy_modes(interface), speed, duplex); + hw_if->pcs_linkup(pdata, interface, speed, duplex); +} + +static const struct phylink_pcs_ops phytmac_pcs_phylink_ops = { + .pcs_config = phytmac_pcs_config, + .pcs_link_up = phytmac_pcs_link_up, +}; + +static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct phytmac *pdata = netdev_priv(to_net_dev(config->dev)); + + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER || + interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX || + interface == PHY_INTERFACE_MODE_2500BASEX) { + pdata->phylink_pcs.ops = &phytmac_pcs_phylink_ops; + } else { + pdata->phylink_pcs.ops = NULL; + } + + return &pdata->phylink_pcs; +} + +static void phytmac_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct phytmac *pdata = netdev_priv(to_net_dev(config->dev)); + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + + if (netif_msg_link(pdata)) { + netdev_info(pdata->ndev, "mac config interface=%s, mode=%d\n", + phy_modes(state->interface), mode); + } + + spin_lock_irqsave(&pdata->lock, flags); + hw_if->mac_config(pdata, mode, state); + spin_unlock_irqrestore(&pdata->lock, flags); +} + +static void phytmac_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_queue *queue; + unsigned int q; + unsigned long flags; + struct phytmac_tx_skb *tx_skb; + int i; + + if (netif_msg_link(pdata)) { + netdev_info(ndev, "link down interface:%s, mode=%d\n", + phy_modes(interface), mode); + } + + if (pdata->use_ncsi) + ncsi_stop_dev(pdata->ncsidev); + + spin_lock_irqsave(&pdata->lock, flags); + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + hw_if->clear_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + } + + /* Disable Rx and Tx */ + hw_if->enable_network(pdata, false, PHYTMAC_RX | PHYTMAC_TX); + + /* Tx clean */ + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + for (i = 0; i < pdata->tx_ring_size; i++) { + tx_skb = phytmac_get_tx_skb(queue, i); + if (tx_skb) + phytmac_tx_unmap(pdata, tx_skb, 0); + } + } + + spin_unlock_irqrestore(&pdata->lock, flags); + + netif_tx_stop_all_queues(ndev); +} + +static void phytmac_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_queue *queue; + unsigned long flags; + unsigned int q; + int ret; + + if (netif_msg_link(pdata)) + netdev_info(pdata->ndev, "link up interface:%s, speed:%d, duplex:%s\n", + phy_modes(interface), speed, duplex ? "full-duplex" : "half-duplex"); + + spin_lock_irqsave(&pdata->lock, flags); + + hw_if->mac_linkup(pdata, interface, speed, duplex); + + if (rx_pause != pdata->pause) { + hw_if->enable_pause(pdata, rx_pause); + pdata->pause = rx_pause; + } + + phytmac_init_ring(pdata); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) + hw_if->enable_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + + /* Enable Rx and Tx */ + hw_if->enable_network(pdata, true, PHYTMAC_RX | PHYTMAC_TX); + spin_unlock_irqrestore(&pdata->lock, flags); + + if (pdata->use_ncsi) { + /* Start the NCSI device */ + ret = ncsi_start_dev(pdata->ncsidev); + if (ret) { + netdev_err(pdata->ndev, "Ncsi start dev failed (error %d)\n", ret); + return; + } + } + + netif_tx_wake_all_queues(ndev); +} + +int phytmac_mdio_register(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + int ret; + + pdata->mii_bus = mdiobus_alloc(); + if (!pdata->mii_bus) { + ret = -ENOMEM; + goto free_mdio; + } + + pdata->mii_bus->name = "phytmac_mii_bus"; + pdata->mii_bus->read = &phytmac_mdio_read_c22; + pdata->mii_bus->write = &phytmac_mdio_write_c22; + pdata->mii_bus->read_c45 = &phytmac_mdio_read_c45; + pdata->mii_bus->write_c45 = &phytmac_mdio_write_c45; + + if (pdata->platdev) { + snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", + pdata->mii_bus->name, netdev_name(pdata->ndev)); + } else if (pdata->pcidev) { + snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", + pdata->mii_bus->name, pci_name(pdata->pcidev)); + } else { + ret = -ENOMEM; + goto free_mdio; + } + + pdata->mii_bus->priv = pdata; + pdata->mii_bus->parent = pdata->dev; + + hw_if->enable_mdio_control(pdata, 1); + + return mdiobus_register(pdata->mii_bus); +free_mdio: + mdiobus_free(pdata->mii_bus); + return ret; +} + +static void phytmac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) +{ + struct phytmac *pdata = container_of(config, struct phytmac, phylink_config); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + state->link = hw_if->get_link(pdata, state->interface); +} + +static void phytmac_validate(struct phylink_config *config, + unsigned long *supported, + struct phylink_link_state *state) +{ + struct net_device *ndev = to_net_dev(config->dev); + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + struct phytmac *pdata = netdev_priv(ndev); + + if (state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_2500BASEX && + state->interface != PHY_INTERFACE_MODE_5GBASER && + state->interface != PHY_INTERFACE_MODE_10GBASER && + state->interface != PHY_INTERFACE_MODE_USXGMII && + !phy_interface_mode_is_rgmii(state->interface)) { + bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + return; + } + + phylink_set_port_modes(mask); + phylink_set(mask, Autoneg); + phylink_set(mask, Asym_Pause); + + if (state->interface == PHY_INTERFACE_MODE_10GBASER || + state->interface == PHY_INTERFACE_MODE_USXGMII) { + pdata->speed = state->speed; + pdata->duplex = state->duplex; + if (pdata->speed == SPEED_5000) { + phylink_set(mask, 5000baseT_Full); + } else { + phylink_set(mask, 10000baseCR_Full); + phylink_set(mask, 10000baseER_Full); + phylink_set(mask, 10000baseKR_Full); + phylink_set(mask, 10000baseLR_Full); + phylink_set(mask, 10000baseLRM_Full); + phylink_set(mask, 10000baseSR_Full); + phylink_set(mask, 10000baseT_Full); + } + } + + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + phylink_set(mask, 2500baseX_Full); + + if (state->interface == PHY_INTERFACE_MODE_5GBASER) + phylink_set(mask, 5000baseT_Full); + + if (state->interface == PHY_INTERFACE_MODE_1000BASEX || + state->interface == PHY_INTERFACE_MODE_SGMII || + phy_interface_mode_is_rgmii(state->interface)) { + phylink_set(mask, 1000baseT_Full); + phylink_set(mask, 1000baseX_Full); + phylink_set(mask, 1000baseT_Half); + phylink_set(mask, 10baseT_Half); + phylink_set(mask, 10baseT_Full); + phylink_set(mask, 100baseT_Half); + phylink_set(mask, 100baseT_Full); + } + + bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); + bitmap_and(state->advertising, state->advertising, mask, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + +static const struct phylink_mac_ops phytmac_phylink_ops = { + .validate = phytmac_validate, + .mac_select_pcs = phytmac_mac_select_pcs, + .mac_config = phytmac_mac_config, + .mac_link_down = phytmac_mac_link_down, + .mac_link_up = phytmac_mac_link_up, +}; + +static inline void set_phy_interface(unsigned long *intf) +{ + __set_bit(PHY_INTERFACE_MODE_SGMII, intf); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, intf); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, intf); + __set_bit(PHY_INTERFACE_MODE_USXGMII, intf); + __set_bit(PHY_INTERFACE_MODE_10GBASER, intf); +} + +static int phytmac_phylink_create(struct phytmac *pdata) +{ + struct fwnode_handle *fw_node = dev_fwnode(pdata->dev); + + pdata->phylink_config.dev = &pdata->ndev->dev; + pdata->phylink_config.type = PHYLINK_NETDEV; + if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX || + pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX || + pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { + pdata->phylink_config.poll_fixed_state = true; + pdata->phylink_config.get_fixed_state = phytmac_pcs_get_state; + pdata->phylink_pcs.ops = &phytmac_pcs_phylink_ops; + } + + set_phy_interface(pdata->phylink_config.supported_interfaces); + pdata->phylink = phylink_create(&pdata->phylink_config, fw_node, + pdata->phy_interface, &phytmac_phylink_ops); + if (IS_ERR(pdata->phylink)) { + dev_err(pdata->dev, "Could not create a phylink instance (%ld)\n", + PTR_ERR(pdata->phylink)); + return PTR_ERR(pdata->phylink); + } + + return 0; +} + +static int phytmac_open(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned int q = 0; + int ret; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "open\n"); + + /* phytmac_powerup */ + if (pdata->power_state == PHYTMAC_POWEROFF) + hw_if->poweron(pdata, PHYTMAC_POWERON); + + if (hw_if->init_msg_ring) + hw_if->init_msg_ring(pdata); + + ret = hw_if->get_feature(pdata); + if (ret) { + netdev_err(ndev, "phytmac get features failed\n"); + return ret; + } + + hw_if->reset_hw(pdata); + + ret = phytmac_get_mac_address(pdata); + if (ret) { + netdev_err(ndev, "phytmac get mac address failed\n"); + goto reset_hw; + } + + ret = netif_set_real_num_tx_queues(ndev, pdata->queues_num); + if (ret) { + netdev_err(ndev, "error setting real tx queue number\n"); + return ret; + } + ret = netif_set_real_num_rx_queues(ndev, pdata->queues_num); + if (ret) { + netdev_err(ndev, "error setting real tx queue number\n"); + return ret; + } + + /* RX buffers initialization */ + ret = phytmac_alloc_resource(pdata); + if (ret) { + netdev_err(ndev, "Unable to allocate DMA memory (error %d)\n", + ret); + goto reset_hw; + } + + for (queue = pdata->queues; q < pdata->queues_num; ++q) { + napi_enable(&queue->tx_napi); + napi_enable(&queue->rx_napi); + ++queue; + } + + phytmac_init_ring(pdata); + hw_if->init_hw(pdata); + + ret = phytmac_phylink_connect(pdata); + if (ret) { + netdev_err(ndev, "phylink connet failed,(error %d)\n", + ret); + goto reset_hw; + } + + phylink_start(pdata->phylink); + + netif_tx_start_all_queues(pdata->ndev); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + ret = phytmac_ptp_register(pdata); + if (ret) { + netdev_err(ndev, "ptp register failed, (error %d)\n", + ret); + goto reset_hw; + } + + phytmac_ptp_init(pdata->ndev); + } + + return 0; + +reset_hw: + hw_if->reset_hw(pdata); + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q) { + napi_disable(&queue->tx_napi); + napi_disable(&queue->rx_napi); + ++queue; + } + phytmac_free_resource(pdata); + return ret; +} + +static int phytmac_close(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + unsigned int q; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "close"); + + netif_tx_stop_all_queues(ndev); + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + napi_disable(&queue->tx_napi); + napi_disable(&queue->rx_napi); + } + + phylink_stop(pdata->phylink); + phylink_disconnect_phy(pdata->phylink); + + netif_carrier_off(ndev); + + spin_lock_irqsave(&pdata->lock, flags); + hw_if->reset_hw(pdata); + spin_unlock_irqrestore(&pdata->lock, flags); + + phytmac_free_resource(pdata); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + phytmac_ptp_unregister(pdata); + + /* phytmac_powerup */ + if (pdata->power_state == PHYTMAC_POWERON) + hw_if->poweron(pdata, PHYTMAC_POWEROFF); + + return 0; +} + +static int phytmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct phytmac *pdata = netdev_priv(dev); + int ret; + + if (!netif_running(dev)) + return -EINVAL; + + switch (cmd) { + case SIOCGMIIPHY: + case SIOCGMIIREG: + case SIOCSMIIREG: + ret = phylink_mii_ioctl(pdata->phylink, rq, cmd); + break; +#ifdef CONFIG_PHYTMAC_ENABLE_PTP + case SIOCSHWTSTAMP: + ret = phytmac_ptp_set_ts_config(dev, rq, cmd); + break; + case SIOCGHWTSTAMP: + ret = phytmac_ptp_get_ts_config(dev, rq); + break; +#endif + default: + break; + } + + return ret; +} + +static inline int phytmac_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct phytmac *pdata = netdev_priv(netdev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + netdev_features_t changed = features ^ netdev->features; + + /* TX checksum offload */ + if (changed & NETIF_F_HW_CSUM) { + if (features & NETIF_F_HW_CSUM) + hw_if->enable_tx_csum(pdata, 1); + else + hw_if->enable_tx_csum(pdata, 0); + } + + /* RX checksum offload */ + if (changed & NETIF_F_RXCSUM) { + if (features & NETIF_F_RXCSUM && + !(netdev->flags & IFF_PROMISC)) + hw_if->enable_rx_csum(pdata, 1); + else + hw_if->enable_rx_csum(pdata, 0); + } + return 0; +} + +static netdev_features_t phytmac_features_check(struct sk_buff *skb, + struct net_device *dev, + netdev_features_t features) +{ + unsigned int nr_frags, f; + unsigned int hdrlen; + + /* there is only one buffer or protocol is not UDP */ + if (!skb_is_nonlinear(skb) || (ip_hdr(skb)->protocol != IPPROTO_UDP)) + return features; + + /* length of header */ + hdrlen = skb_transport_offset(skb); + + if (!IS_ALIGNED(skb_headlen(skb) - hdrlen, PHYTMAC_TX_LEN_ALIGN)) + return features & ~NETIF_F_TSO; + + nr_frags = skb_shinfo(skb)->nr_frags; + /* No need to check last fragment */ + nr_frags--; + for (f = 0; f < nr_frags; f++) { + const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; + + if (!IS_ALIGNED(skb_frag_size(frag), PHYTMAC_TX_LEN_ALIGN)) + return features & ~NETIF_F_TSO; + } + return features; +} + +int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size) +{ + int ret = 0; + int reset = 0; + + if (netif_running(pdata->ndev)) { + reset = 1; + phytmac_close(pdata->ndev); + } + + pdata->rx_ring_size = rx_size; + pdata->tx_ring_size = tx_size; + + if (reset) + phytmac_open(pdata->ndev); + + return ret; +} + +static const struct net_device_ops phytmac_netdev_ops = { + .ndo_open = phytmac_open, + .ndo_stop = phytmac_close, + .ndo_start_xmit = phytmac_start_xmit, + .ndo_set_rx_mode = phytmac_set_rx_mode, + .ndo_get_stats = phytmac_get_stats, + .ndo_eth_ioctl = phytmac_ioctl, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = phytmac_change_mtu, + .ndo_set_mac_address = phytmac_set_mac_address, + .ndo_set_features = phytmac_set_features, + .ndo_features_check = phytmac_features_check, + .ndo_vlan_rx_add_vid = ncsi_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = ncsi_vlan_rx_kill_vid, +}; + +static int phytmac_init(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + unsigned int q; + struct phytmac_queue *queue; + int ret; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "phytmac init !\n"); + + spin_lock_init(&pdata->lock); + + /* set the queue register mapping once for all: queue0 has a special + * register mapping but we don't want to test the queue index then + * compute the corresponding register offset at run time. + */ + for (q = 0; q < pdata->queues_num; ++q) { + queue = &pdata->queues[q]; + queue->pdata = pdata; + queue->index = q; + spin_lock_init(&queue->tx_lock); + + netif_napi_add(ndev, &queue->tx_napi, phytmac_tx_poll); + netif_napi_add(ndev, &queue->rx_napi, phytmac_rx_poll); + + if (pdata->irq_type == IRQ_TYPE_INT || pdata->irq_type == IRQ_TYPE_MSI) { + queue->irq = pdata->queue_irq[q]; + if (pdata->irq_type == IRQ_TYPE_INT) + ret = devm_request_irq(pdata->dev, queue->irq, phytmac_irq, + IRQF_SHARED, ndev->name, queue); + else + ret = devm_request_irq(pdata->dev, queue->irq, phytmac_irq, + 0, ndev->name, queue); + + if (ret) { + dev_err(pdata->dev, + "Unable to request IRQ %d (error %d)\n", + queue->irq, ret); + return ret; + } + } + } + + if (pdata->irq_type == IRQ_TYPE_INTX) { + ret = devm_request_irq(pdata->dev, pdata->queue_irq[0], phytmac_intx_irq, + IRQF_SHARED, ndev->name, pdata); + if (ret) { + dev_err(pdata->dev, + "Unable to request INTX IRQ %d (error %d)\n", + pdata->queue_irq[0], ret); + return ret; + } + } + + ndev->netdev_ops = &phytmac_netdev_ops; + phytmac_set_ethtool_ops(ndev); + eth_hw_addr_random(pdata->ndev); + + if (ndev->hw_features & NETIF_F_NTUPLE) { + INIT_LIST_HEAD(&pdata->rx_fs_list.list); + pdata->rx_fs_list.count = 0; + spin_lock_init(&pdata->rx_fs_lock); + } + + device_set_wakeup_enable(pdata->dev, pdata->wol ? 1 : 0); + + return 0; +} + +void phytmac_default_config(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + + pdata->rx_irq_mask = PHYTMAC_RX_INT_FLAGS; + pdata->tx_irq_mask = PHYTMAC_TX_INT_FLAGS; + pdata->tx_ring_size = DEFAULT_TX_RING_SIZE; + pdata->rx_ring_size = DEFAULT_RX_RING_SIZE; + pdata->max_tx_length = PHYTMAC_MAX_TX_LEN; + pdata->min_tx_length = PHYTMAC_MIN_TX_LEN; + pdata->pause = true; + + ndev->hw_features = NETIF_F_SG; + + if (pdata->capacities & PHYTMAC_CAPS_LSO) + ndev->hw_features |= NETIF_F_TSO; + + if (pdata->use_ncsi) { + ndev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); + ndev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + } else { + ndev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + } + + if (pdata->capacities & PHYTMAC_CAPS_SG_DISABLED) + ndev->hw_features &= ~NETIF_F_SG; + + ndev->hw_features |= NETIF_F_NTUPLE; + + ndev->min_mtu = ETH_MIN_MTU; + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + ndev->max_mtu = pdata->jumbo_len - ETH_HLEN - ETH_FCS_LEN; + else + ndev->max_mtu = ETH_DATA_LEN; + + ndev->features = ndev->hw_features; +} + +static void phytmac_ncsi_handler(struct ncsi_dev *nd) +{ + if (unlikely(nd->state != ncsi_dev_state_functional)) + return; + + netdev_dbg(nd->dev, "NCSI interface %s\n", + nd->link_up ? "up" : "down"); +} + +int phytmac_drv_probe(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + struct device *dev = pdata->dev; + int ret = 0; + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "phytmac drv probe start\n"); + + phytmac_default_config(pdata); + + if (dma_set_mask(dev, DMA_BIT_MASK(40)) || + dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40))) { + dev_err(dev, "dma_set_mask or coherent failed\n"); + return 1; + } + + ret = phytmac_init(pdata); + if (ret) + goto err_out_free_netdev; + + if (pdata->use_ncsi) { + pdata->ncsidev = ncsi_register_dev(ndev, phytmac_ncsi_handler); + if (!pdata->ncsidev) + goto err_out_free_netdev; + } + + netif_carrier_off(ndev); + ret = register_netdev(ndev); + if (ret) { + dev_err(pdata->dev, "Cannot register net device, aborting.\n"); + goto err_out_free_netdev; + } + + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "probe success!Phytium %s at 0x%08lx irq %d (%pM)\n", + "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); + + if (pdata->use_mii && !pdata->mii_bus) { + ret = phytmac_mdio_register(pdata); + if (ret) { + netdev_err(ndev, "MDIO bus registration failed\n"); + goto err_phylink_init; + } + } + + ret = phytmac_phylink_create(pdata); + if (ret) { + netdev_err(ndev, "phytmac phylink create failed, error %d\n", ret); + goto err_phylink_init; + } + + return 0; + +err_phylink_init: + if (pdata->mii_bus) + mdiobus_unregister(pdata->mii_bus); + +err_out_free_netdev: + free_netdev(ndev); + + return ret; +} +EXPORT_SYMBOL_GPL(phytmac_drv_probe); + +int phytmac_drv_remove(struct phytmac *pdata) +{ + struct net_device *ndev = pdata->ndev; + + if (ndev) { + if (pdata->use_ncsi && pdata->ncsidev) + ncsi_unregister_dev(pdata->ncsidev); + + unregister_netdev(ndev); + + if (pdata->use_mii && pdata->mii_bus) { + mdiobus_unregister(pdata->mii_bus); + mdiobus_free(pdata->mii_bus); + } + + if (pdata->phylink) + phylink_destroy(pdata->phylink); + } + + return 0; +} +EXPORT_SYMBOL_GPL(phytmac_drv_remove); + +int phytmac_drv_suspend(struct phytmac *pdata) +{ + int q; + unsigned long flags; + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (!netif_running(pdata->ndev)) + return 0; + + if (pdata->power_state == PHYTMAC_POWEROFF) + return 0; + + netif_carrier_off(pdata->ndev); + netif_device_detach(pdata->ndev); + + /* napi_disable */ + for (q = 0, queue = pdata->queues; q < pdata->queues_num; + ++q, ++queue) { + napi_disable(&queue->tx_napi); + napi_disable(&queue->rx_napi); + } + + if (pdata->wol) { + hw_if->set_wol(pdata, pdata->wol); + } else { + rtnl_lock(); + phylink_stop(pdata->phylink); + rtnl_unlock(); + spin_lock_irqsave(&pdata->lock, flags); + hw_if->reset_hw(pdata); + hw_if->poweron(pdata, PHYTMAC_POWEROFF); + spin_unlock_irqrestore(&pdata->lock, flags); + } + + return 0; +} +EXPORT_SYMBOL_GPL(phytmac_drv_suspend); + +int phytmac_drv_resume(struct phytmac *pdata) +{ + int q; + struct phytmac_queue *queue; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct ethtool_rx_fs_item *item; + + if (!netif_running(pdata->ndev)) + return 0; + + if (pdata->power_state == PHYTMAC_POWEROFF) + hw_if->poweron(pdata, PHYTMAC_POWERON); + + if (hw_if->init_msg_ring) + hw_if->init_msg_ring(pdata); + + if (pdata->wol) { + hw_if->set_wol(pdata, 0); + rtnl_lock(); + phylink_stop(pdata->phylink); + rtnl_unlock(); + } + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; + ++q, ++queue) { + napi_enable(&queue->tx_napi); + napi_enable(&queue->rx_napi); + } + + hw_if->init_hw(pdata); + phytmac_set_rx_mode(pdata->ndev); + phytmac_set_features(pdata->ndev, pdata->ndev->features); + list_for_each_entry(item, &pdata->rx_fs_list.list, list) + hw_if->add_fdir_entry(pdata, &item->fs); + + rtnl_lock(); + phylink_start(pdata->phylink); + rtnl_unlock(); + + netif_device_attach(pdata->ndev); + + return 0; +} +EXPORT_SYMBOL_GPL(phytmac_drv_resume); + +struct phytmac *phytmac_alloc_pdata(struct device *dev) +{ + struct phytmac *pdata; + struct net_device *netdev; + + netdev = alloc_etherdev_mq(sizeof(struct phytmac), + PHYTMAC_MAX_QUEUES); + if (!netdev) { + dev_err(dev, "alloc_etherdev_mq failed\n"); + return ERR_PTR(-ENOMEM); + } + SET_NETDEV_DEV(netdev, dev); + pdata = netdev_priv(netdev); + pdata->ndev = netdev; + pdata->dev = dev; + + spin_lock_init(&pdata->lock); + spin_lock_init(&pdata->msg_lock); + spin_lock_init(&pdata->ts_clk_lock); + pdata->msg_enable = netif_msg_init(debug, PHYTMAC_DEFAULT_MSG_ENABLE); + + return pdata; +} +EXPORT_SYMBOL_GPL(phytmac_alloc_pdata); + +void phytmac_free_pdata(struct phytmac *pdata) +{ + struct net_device *netdev = pdata->ndev; + + free_netdev(netdev); +} +EXPORT_SYMBOL_GPL(phytmac_free_pdata); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium Ethernet driver"); +MODULE_AUTHOR("Wenting Song"); +MODULE_ALIAS("platform:phytmac"); + diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c new file mode 100644 index 00000000000000..fd21bf80f13883 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium GMAC PCI wrapper. + * + */ + +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" +#include "phytmac_v2.h" + +#define PCI_DEVICE_ID_GMAC 0xDC3B +#define PCI_SUBDEVICE_ID_SGMII 0x1000 +#define PCI_SUBDEVICE_ID_1000BASEX 0x1001 +#define PCI_SUBDEVICE_ID_2500BASEX 0x1002 +#define PCI_SUBDEVICE_ID_5GBASER 0x1003 +#define PCI_SUBDEVICE_ID_USXGMII 0x1004 +#define PCI_SUBDEVICE_ID_10GBASER 0x1005 + +struct phytmac_data { + struct phytmac_hw_if *hw_if; + u32 caps; + u32 tsu_rate; + u16 queue_num; + int speed; + bool duplex; + bool use_mii; + bool use_ncsi; + phy_interface_t interface; + const struct property_entry *properties; +}; + +static const u32 fixedlink[][5] = { + {0, 1, 1000, 1, 0}, + {0, 1, 2500, 1, 0}, + {0, 1, 5000, 1, 0}, + {0, 1, 10000, 1, 0}, +}; + +static const struct property_entry fl_properties[][2] = { + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[0]), {} }, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[1]), {} }, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[2]), {} }, + {PROPERTY_ENTRY_U32_ARRAY("fixed-link", fixedlink[3]), {} }, +}; + +static int phytmac_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct phytmac_data *data = (struct phytmac_data *)id->driver_data; + struct phytmac *pdata; + struct device *dev = &pdev->dev; + void __iomem * const *iomap_table; + struct fwnode_handle *fw_node = NULL; + int bar_mask; + int ret, i; + + pdata = phytmac_alloc_pdata(dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + pdata->pcidev = pdev; + pci_set_drvdata(pdev, pdata); + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(dev, "pcim_enable_device failed\n"); + goto err_pci_enable; + } + + /* Obtain the mmio areas for the device */ + bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); + ret = pcim_iomap_regions(pdev, bar_mask, PHYTMAC_DRV_NAME); + if (ret) { + dev_err(dev, "pcim_iomap_regions failed\n"); + goto err_pci_enable; + } + + iomap_table = pcim_iomap_table(pdev); + if (!iomap_table) { + dev_err(dev, "pcim_iomap_table failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pdata->mac_regs = iomap_table[0]; + if (!pdata->mac_regs) { + dev_err(dev, "xgmac ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pdata->msg_regs = iomap_table[1]; + if (!pdata->msg_regs) { + dev_err(dev, "xpcs ioremap failed\n"); + ret = -ENOMEM; + goto err_pci_enable; + } + + pci_set_master(pdev); + + /* para */ + pdata->dma_burst_length = DEFAULT_DMA_BURST_LENGTH; + pdata->jumbo_len = DEFAULT_DMA_BURST_LENGTH; + pdata->wol |= PHYTMAC_WAKE_MAGIC; + pdata->use_ncsi = data->use_ncsi; + pdata->use_mii = data->use_mii; + pdata->phy_interface = data->interface; + pdata->queues_num = data->queue_num; + pdata->capacities = data->caps; + pdata->hw_if = data->hw_if; + + if (!pdata->use_mii) { + fw_node = fwnode_create_software_node(data->properties, NULL); + if (IS_ERR(fw_node)) { + dev_err(dev, "Failed to create software node\n"); + goto err_pci_enable; + } + pdata->dev->fwnode = fw_node; + } + + /* irq */ + ret = pci_alloc_irq_vectors(pdev, pdata->queues_num, pdata->queues_num, PCI_IRQ_MSI); + if (ret < 0) { + pdata->irq_type = IRQ_TYPE_INTX; + pdata->queue_irq[0] = pdev->irq; + } else { + pdata->irq_type = IRQ_TYPE_MSI; + for (i = 0; i < pdata->queues_num; i++) + pdata->queue_irq[i] = pci_irq_vector(pdev, i); + } + + /* Configure the netdev resource */ + ret = phytmac_drv_probe(pdata); + if (ret) + goto err_irq_vectors; + + netdev_notice(pdata->ndev, "net device enabled\n"); + + return 0; + +err_irq_vectors: + if (fw_node) + fwnode_remove_software_node(fw_node); + pci_free_irq_vectors(pdata->pcidev); + +err_pci_enable: + phytmac_free_pdata(pdata); + +err_alloc: + dev_notice(dev, "net device not enabled\n"); + + return ret; +} + +static void phytmac_pci_remove(struct pci_dev *pdev) +{ + struct phytmac *pdata = pci_get_drvdata(pdev); + struct fwnode_handle *fw_node = dev_fwnode(pdata->dev); + int i = 0; + int bar_mask; + + if (fw_node) { + fwnode_remove_software_node(fw_node); + pdata->dev->fwnode = NULL; + } + + phytmac_drv_remove(pdata); + + for (i = 0; i < pdata->queues_num; i++) + free_irq(pci_irq_vector(pdev, i), &pdata->queues[i]); + pci_free_irq_vectors(pdev); + + phytmac_free_pdata(pdata); + bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); + pcim_iounmap_regions(pdev, bar_mask); + + pci_disable_device(pdev); +} + +static int __maybe_unused phytmac_pci_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytmac *pdata = pci_get_drvdata(pdev); + int ret; + + ret = phytmac_drv_suspend(pdata); + + return ret; +} + +static int __maybe_unused phytmac_pci_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct phytmac *pdata = pci_get_drvdata(pdev); + int ret; + + ret = phytmac_drv_resume(pdata); + + return ret; +} + +struct phytmac_data phytmac_sgmii = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = true, + .interface = PHY_INTERFACE_MODE_SGMII, +}; + +struct phytmac_data phytmac_1000basex = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 1000, + .duplex = true, + .interface = PHY_INTERFACE_MODE_SGMII, + .properties = fl_properties[0], +}; + +struct phytmac_data phytmac_2500basex = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 2500, + .duplex = true, + .interface = PHY_INTERFACE_MODE_2500BASEX, + .properties = fl_properties[1], +}; + +struct phytmac_data phytmac_5000baser = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 5000, + .duplex = true, + .interface = PHY_INTERFACE_MODE_5GBASER, + .properties = fl_properties[2], +}; + +struct phytmac_data phytmac_usxgmii = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .use_ncsi = false, + .use_mii = false, + .speed = 10000, + .duplex = true, + .interface = PHY_INTERFACE_MODE_USXGMII, + .properties = fl_properties[3], +}; + +static const struct pci_device_id phytmac_pci_table[] = { + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_SGMII), + .driver_data = (kernel_ulong_t)&phytmac_sgmii}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_1000BASEX), + .driver_data = (kernel_ulong_t)&phytmac_1000basex}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_2500BASEX), + .driver_data = (kernel_ulong_t)&phytmac_2500basex}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_5GBASER), + .driver_data = (kernel_ulong_t)&phytmac_5000baser}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_USXGMII), + .driver_data = (kernel_ulong_t)&phytmac_usxgmii}, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_PHYTIUM, PCI_DEVICE_ID_GMAC, + PCI_VENDOR_ID_PHYTIUM, PCI_SUBDEVICE_ID_10GBASER), + .driver_data = (kernel_ulong_t)&phytmac_usxgmii}, + /* Last entry must be zero */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, phytmac_pci_table); + +static const struct dev_pm_ops phytmac_pci_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytmac_pci_suspend, phytmac_pci_resume) +}; + +static struct pci_driver phytmac_driver = { + .name = PHYTMAC_DRV_NAME, + .id_table = phytmac_pci_table, + .probe = phytmac_pci_probe, + .remove = phytmac_pci_remove, + .driver = { + .pm = &phytmac_pci_pm_ops, + } +}; + +module_pci_driver(phytmac_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium NIC PCI wrapper"); diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c new file mode 100644 index 00000000000000..305ff5866e2fe0 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Phytium GMAC Platform wrapper. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" +#include "phytmac_v2.h" + +static const struct phytmac_config phytium_1p0_config = { + .hw_if = &phytmac_1p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_START + | PHYTMAC_CAPS_JUMBO + | PHYTMAC_CAPS_LSO, + .queue_num = 4, + .tsu_rate = 300000000, +}; + +static const struct phytmac_config phytium_2p0_config = { + .hw_if = &phytmac_2p0_hw, + .caps = PHYTMAC_CAPS_TAILPTR + | PHYTMAC_CAPS_LPI + | PHYTMAC_CAPS_LSO + | PHYTMAC_CAPS_MSG + | PHYTMAC_CAPS_JUMBO, + .queue_num = 2, + .tsu_rate = 300000000, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id phytmac_dt_ids[] = { + { .compatible = "phytium,gmac-1.0", .data = &phytium_1p0_config }, + { .compatible = "phytium,gmac-2.0", .data = &phytium_2p0_config }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, phytmac_dt_ids); +#endif /* CONFIG_OF */ + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytmac_acpi_ids[] = { + { .id = "PHYT0046", .driver_data = (kernel_ulong_t)&phytium_1p0_config }, + { } +}; + +MODULE_DEVICE_TABLE(acpi, phytmac_acpi_ids); +#else +#define phytmac_acpi_ids NULL +#endif + +static int phytmac_get_phy_mode(struct platform_device *pdev) +{ + const char *pm; + int err, i; + + err = device_property_read_string(&pdev->dev, "phy-mode", &pm); + if (err < 0) + return err; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phy_modes(i))) + return i; + } + + return -ENODEV; +} + +static int phytmac_plat_probe(struct platform_device *pdev) +{ + const struct phytmac_config *phytmac_config = &phytium_1p0_config; + struct device_node *np = pdev->dev.of_node; + struct resource *regs; + struct phytmac *pdata; + int ret, i; + u32 queue_num; + + pdata = phytmac_alloc_pdata(&pdev->dev); + if (IS_ERR(pdata)) { + ret = PTR_ERR(pdata); + goto err_alloc; + } + + platform_set_drvdata(pdev, pdata); + + pdata->platdev = pdev; + + if (pdev->dev.of_node) { + const struct of_device_id *match; + + match = of_match_node(phytmac_dt_ids, np); + if (match && match->data) { + phytmac_config = match->data; + pdata->hw_if = phytmac_config->hw_if; + pdata->capacities = phytmac_config->caps; + pdata->queues_max_num = phytmac_config->queue_num; + } + } else if (has_acpi_companion(&pdev->dev)) { + const struct acpi_device_id *match; + + match = acpi_match_device(phytmac_acpi_ids, &pdev->dev); + if (match && match->driver_data) { + phytmac_config = (void *)match->driver_data; + pdata->hw_if = phytmac_config->hw_if; + pdata->capacities = phytmac_config->caps; + pdata->queues_max_num = phytmac_config->queue_num; + } + } + + i = 0; + pdata->mac_regs = devm_platform_get_and_ioremap_resource(pdev, i, ®s); + if (IS_ERR(pdata->mac_regs)) { + dev_err(&pdev->dev, "mac_regs ioremap failed\n"); + ret = PTR_ERR(pdata->mac_regs); + goto err_mem; + } + pdata->ndev->base_addr = regs->start; + + if (pdata->capacities & PHYTMAC_CAPS_MSG) { + ++i; + regs = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (regs) { + pdata->msg_regs = ioremap_wt(regs->start, MEMORY_SIZE); + if (!pdata->msg_regs) { + dev_err(&pdev->dev, "msg_regs ioremap failed, i=%d\n", i); + goto err_mem; + } + } + } + + if (device_property_read_bool(&pdev->dev, "lpi")) + pdata->capacities |= PHYTMAC_CAPS_LPI; + + if (pdata->capacities & PHYTMAC_CAPS_LPI) { + /* lpi resource */ + ++i; + regs = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (regs) { + pdata->mhu_regs = ioremap(regs->start, MHU_SIZE); + if (!pdata->mhu_regs) + dev_err(&pdev->dev, "mhu_regs ioremap failed, i=%d\n", i); + } + } + + if (device_property_read_u32(&pdev->dev, "dma-burst-length", &pdata->dma_burst_length)) + pdata->dma_burst_length = DEFAULT_DMA_BURST_LENGTH; + + if (device_property_read_u32(&pdev->dev, "jumbo-max-length", &pdata->jumbo_len)) + pdata->jumbo_len = DEFAULT_JUMBO_MAX_LENGTH; + + if (device_property_read_u32(&pdev->dev, "queue-number", &queue_num)) + pdata->queues_num = pdata->queues_max_num; + else + pdata->queues_num = queue_num; + + pdata->wol = 0; + if (device_property_read_bool(&pdev->dev, "magic-packet")) + pdata->wol |= PHYTMAC_WAKE_MAGIC; + + pdata->use_ncsi = device_property_read_bool(&pdev->dev, "use-ncsi"); + pdata->use_mii = device_property_read_bool(&pdev->dev, "use-mii"); + + pdata->power_state = PHYTMAC_POWEROFF; + + device_set_wakeup_capable(&pdev->dev, pdata->wol & PHYTMAC_WOL_MAGIC_PACKET); + + for (i = 0; i < pdata->queues_num; i++) { + pdata->irq_type = IRQ_TYPE_INT; + pdata->queue_irq[i] = platform_get_irq(pdev, i); + } + + ret = phytmac_get_phy_mode(pdev); + if (ret < 0) + pdata->phy_interface = PHY_INTERFACE_MODE_MII; + else + pdata->phy_interface = ret; + + ret = phytmac_drv_probe(pdata); + if (ret) + goto err_mem; + + if (netif_msg_probe(pdata)) { + dev_notice(&pdev->dev, "phytium net device enabled\n"); + dev_dbg(pdata->dev, "use_ncsi:%d, use_mii:%d, wol:%d, queues_num:%d\n", + pdata->use_ncsi, pdata->use_mii, pdata->wol, pdata->queues_num); + } + + return 0; + +err_mem: + phytmac_free_pdata(pdata); + +err_alloc: + dev_err(&pdev->dev, "phytium net device not enabled\n"); + + return ret; +} + +static int phytmac_plat_remove(struct platform_device *pdev) +{ + struct phytmac *pdata = platform_get_drvdata(pdev); + + phytmac_drv_remove(pdata); + phytmac_free_pdata(pdata); + + return 0; +} + +static int __maybe_unused phytmac_plat_suspend(struct device *dev) +{ + struct phytmac *pdata = dev_get_drvdata(dev); + int ret; + + ret = phytmac_drv_suspend(pdata); + + return ret; +} + +static int __maybe_unused phytmac_plat_resume(struct device *dev) +{ + struct phytmac *pdata = dev_get_drvdata(dev); + int ret; + + ret = phytmac_drv_resume(pdata); + + return ret; +} + +static const struct dev_pm_ops phytmac_plat_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(phytmac_plat_suspend, phytmac_plat_resume) +}; + +static struct platform_driver phytmac_driver = { + .probe = phytmac_plat_probe, + .remove = phytmac_plat_remove, + .driver = { + .name = PHYTMAC_DRV_NAME, + .of_match_table = of_match_ptr(phytmac_dt_ids), + .acpi_match_table = phytmac_acpi_ids, + .pm = &phytmac_plat_pm_ops, + }, +}; + +module_platform_driver(phytmac_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium Ethernet driver"); +MODULE_AUTHOR("Wenting Song"); +MODULE_ALIAS("platform:phytmac"); diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.c b/drivers/net/ethernet/phytium/phytmac_ptp.c new file mode 100644 index 00000000000000..26b1b75edbde1d --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_ptp.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0-only +/** + * 1588 PTP support for Phytium GMAC device. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_ptp.h" + +bool phytmac_ptp_one_step(struct sk_buff *skb) +{ + struct ptp_header *hdr; + unsigned int ptp_class; + u8 msgtype; + + /* No need to parse packet if PTP TS is not involved */ + if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))) + goto not_oss; + + /* Identify and return whether PTP one step sync is being processed */ + ptp_class = ptp_classify_raw(skb); + if (ptp_class == PTP_CLASS_NONE) + goto not_oss; + + hdr = ptp_parse_header(skb, ptp_class); + if (!hdr) + goto not_oss; + + if (hdr->flag_field[0] & 0x2) + goto not_oss; + + msgtype = ptp_get_msgtype(hdr, ptp_class); + if (msgtype == PTP_MSGTYPE_SYNC) + return true; + +not_oss: + return false; +} + +int phytmac_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + + spin_lock_irqsave(&pdata->ts_clk_lock, flags); + hw_if->get_time(pdata, ts); + spin_unlock_irqrestore(&pdata->ts_clk_lock, flags); + + return 0; +} + +int phytmac_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + unsigned long flags; + + spin_lock_irqsave(&pdata->ts_clk_lock, flags); + hw_if->set_time(pdata, ts->tv_sec, ts->tv_nsec); + spin_unlock_irqrestore(&pdata->ts_clk_lock, flags); + + return 0; +} + +int phytmac_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + bool negative = false; + + if (scaled_ppm < 0) { + negative = true; + scaled_ppm = -scaled_ppm; + } + + hw_if->adjust_fine(pdata, scaled_ppm, negative); + return 0; +} + +int phytmac_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct phytmac *pdata = container_of(ptp, struct phytmac, ptp_clock_info); + struct phytmac_hw_if *hw_if = pdata->hw_if; + int negative = 0; + + if (delta < 0) { + negative = 1; + delta = -delta; + } + + spin_lock_irq(&pdata->ts_clk_lock); + hw_if->adjust_time(pdata, delta, negative); + spin_unlock_irq(&pdata->ts_clk_lock); + + return 0; +} + +int phytmac_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + return -EOPNOTSUPP; +} + +void phytmac_ptp_init_timer(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + u32 rem = 0; + u64 adj; + + pdata->ts_rate = hw_if->get_ts_rate(pdata); + pdata->ts_incr.ns = div_u64_rem(NSEC_PER_SEC, pdata->ts_rate, &rem); + if (rem) { + adj = rem; + adj <<= 24; + pdata->ts_incr.sub_ns = div_u64(adj, pdata->ts_rate); + } else { + pdata->ts_incr.sub_ns = 0; + } +} + +void phytmac_ptp_rxstamp(struct phytmac *pdata, struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct timespec64 ts; + + if (hw_if->ts_valid(pdata, desc, PHYTMAC_RX)) { + hw_if->get_timestamp(pdata, desc->desc4, desc->desc5, &ts); + memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); + shhwtstamps->hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + } +} + +int phytmac_ptp_txstamp(struct phytmac_queue *queue, struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct timespec64 ts; + struct skb_shared_hwtstamps shhwtstamps; + + if (queue->pdata->ts_config.tx_type == TS_DISABLED) + return -EOPNOTSUPP; + + if (!hw_if->ts_valid(pdata, desc, PHYTMAC_TX)) + return -EINVAL; + + skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; + hw_if->get_timestamp(pdata, desc->desc4, desc->desc5, &ts); + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb, &shhwtstamps); + + return 0; +} + +int phytmac_ptp_register(struct phytmac *pdata) +{ + pdata->ptp_clock_info.owner = THIS_MODULE; + snprintf(pdata->ptp_clock_info.name, 16, "%s", pdata->ndev->name); + pdata->ptp_clock_info.max_adj = 64000000; /* In PPB */ + pdata->ptp_clock_info.n_alarm = 0; + pdata->ptp_clock_info.n_ext_ts = 0; + pdata->ptp_clock_info.n_per_out = 0; + pdata->ptp_clock_info.pps = 1; + pdata->ptp_clock_info.adjfine = phytmac_ptp_adjfine; + pdata->ptp_clock_info.adjtime = phytmac_ptp_adjtime; + pdata->ptp_clock_info.gettime64 = phytmac_ptp_gettime; + pdata->ptp_clock_info.settime64 = phytmac_ptp_settime; + pdata->ptp_clock_info.enable = phytmac_ptp_enable; + pdata->ptp_clock = ptp_clock_register(&pdata->ptp_clock_info, pdata->dev); + if (IS_ERR_OR_NULL(pdata->ptp_clock)) { + dev_err(pdata->dev, "ptp_clock_register failed %lu\n", + PTR_ERR(pdata->ptp_clock)); + return -EINVAL; + } + + return 0; +} + +void phytmac_ptp_unregister(struct phytmac *pdata) +{ + struct phytmac_hw_if *hw_if = pdata->hw_if; + + if (pdata->ptp_clock) + ptp_clock_unregister(pdata->ptp_clock); + pdata->ptp_clock = NULL; + + hw_if->clear_time(pdata); + + dev_info(pdata->dev, "phytmac ptp clock unregistered.\n"); +} + +void phytmac_ptp_init(struct net_device *ndev) +{ + struct phytmac *pdata = netdev_priv(ndev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + + phytmac_ptp_init_timer(pdata); + + hw_if->init_ts_hw(pdata); + + dev_info(pdata->dev, "phytmac ptp clock init success.\n"); +} + +int phytmac_ptp_get_ts_config(struct net_device *dev, struct ifreq *rq) +{ + struct hwtstamp_config *tstamp_config; + struct phytmac *pdata = netdev_priv(dev); + + if (!IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + return -EOPNOTSUPP; + + tstamp_config = &pdata->ts_config; + + if (copy_to_user(rq->ifr_data, tstamp_config, sizeof(*tstamp_config))) + return -EFAULT; + else + return 0; +} + +int phytmac_ptp_set_ts_config(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct hwtstamp_config config; + struct phytmac *pdata = netdev_priv(dev); + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct ts_ctrl tstamp_ctrl; + int ret; + + memset(&tstamp_ctrl, 0, sizeof(struct ts_ctrl)); + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + break; + case HWTSTAMP_TX_ONESTEP_SYNC: + tstamp_ctrl.one_step = 1; + tstamp_ctrl.tx_control = TS_ALL_FRAMES; + break; + case HWTSTAMP_TX_ON: + tstamp_ctrl.one_step = 0; + tstamp_ctrl.tx_control = TS_ALL_FRAMES; + break; + default: + return -ERANGE; + } + + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + break; + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + tstamp_ctrl.rx_control = TS_ALL_PTP_FRAMES; + config.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; + break; + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_ALL: + tstamp_ctrl.rx_control = TS_ALL_FRAMES; + config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + config.rx_filter = HWTSTAMP_FILTER_NONE; + return -ERANGE; + } + + ret = hw_if->set_ts_config(pdata, &tstamp_ctrl); + if (ret) + return ret; + + /* save these settings for future reference */ + pdata->ts_config = config; + memcpy(&pdata->ts_config, &config, sizeof(config)); + + if (copy_to_user(ifr->ifr_data, &config, sizeof(config))) + return -EFAULT; + else + return 0; +} + diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.h b/drivers/net/ethernet/phytium/phytmac_ptp.h new file mode 100644 index 00000000000000..72c8b7c6741370 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_ptp.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Phytium Ethernet Controller driver + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _PHYTMAC_PTP_H +#define _PHYTMAC_PTP_H + +#ifdef CONFIG_PHYTMAC_ENABLE_PTP +bool phytmac_ptp_one_step(struct sk_buff *skb); +int phytmac_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts); +int phytmac_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts); +int phytmac_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm); +int phytmac_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta); +int phytmac_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on); +void phytmac_ptp_init_timer(struct phytmac *pdata); +void phytmac_ptp_rxstamp(struct phytmac *pdata, struct sk_buff *skb, + struct phytmac_dma_desc *desc); +int phytmac_ptp_txstamp(struct phytmac_queue *queue, struct sk_buff *skb, + struct phytmac_dma_desc *desc); +int phytmac_ptp_register(struct phytmac *pdata); +void phytmac_ptp_unregister(struct phytmac *pdata); +void phytmac_ptp_init(struct net_device *ndev); +int phytmac_ptp_get_ts_config(struct net_device *dev, struct ifreq *rq); +int phytmac_ptp_set_ts_config(struct net_device *dev, struct ifreq *ifr, int cmd); +#else +static inline bool phytmac_ptp_one_step(struct sk_buff *skb) +{ + return 1; +} + +static inline void phytmac_ptp_rxstamp(struct phytmac *pdata, struct sk_buff *skb, + struct phytmac_dma_desc *desc) {} +static inline int phytmac_ptp_txstamp(struct phytmac_queue *queue, struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + return -1; +} + +static inline int phytmac_ptp_register(struct phytmac *pdata) +{ + return 0; +} + +static inline void phytmac_ptp_unregister(struct phytmac *pdata) {} +static inline void phytmac_ptp_init(struct net_device *ndev) {} + +#endif +#endif diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c new file mode 100644 index 00000000000000..2d9f67c82050c9 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -0,0 +1,1370 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_v1.h" + +static int phytmac_enable_promise(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + if (enable) + value |= PHYTMAC_BIT(PROMISC); + else + value &= ~PHYTMAC_BIT(PROMISC); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + + return 0; +} + +static int phytmac_enable_multicast(struct phytmac *pdata, int enable) +{ + u32 config; + + if (enable) { + PHYTMAC_WRITE(pdata, PHYTMAC_HASHB, -1); + PHYTMAC_WRITE(pdata, PHYTMAC_HASHT, -1); + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + config |= PHYTMAC_BIT(MH_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_HASHB, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_HASHT, 0); + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + config &= ~PHYTMAC_BIT(MH_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + } + + return 0; +} + +static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) +{ + u32 config; + + PHYTMAC_WRITE(pdata, PHYTMAC_HASHB, mc_filter[0]); + PHYTMAC_WRITE(pdata, PHYTMAC_HASHT, mc_filter[1]); + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + config |= PHYTMAC_BIT(MH_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + return 0; +} + +static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + if (enable) + value |= PHYTMAC_BIT(RCO_EN); + else + value &= ~PHYTMAC_BIT(RCO_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + + return 0; +} + +static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_DCONFIG); + + if (enable) + value |= PHYTMAC_BIT(TCO_EN); + else + value &= ~PHYTMAC_BIT(TCO_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, value); + + return 0; +} + +static int phytmac_enable_mdio(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + + if (enable) + value |= PHYTMAC_BIT(MPE); + else + value &= ~PHYTMAC_BIT(MPE); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, value); + + return 0; +} + +static int phytmac_enable_pause(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + if (enable) + value |= PHYTMAC_BIT(PAUSE_EN); + else + value &= ~PHYTMAC_BIT(PAUSE_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + return 0; +} + +static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) +{ + u32 old_ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + u32 ctrl; + + ctrl = old_ctrl; + + if (rx_tx & PHYTMAC_TX) { + if (enable) + ctrl |= PHYTMAC_BIT(TE); + else + ctrl &= ~PHYTMAC_BIT(TE); + } + + if (rx_tx & PHYTMAC_RX) { + if (enable) + ctrl |= PHYTMAC_BIT(RE); + else + ctrl &= ~PHYTMAC_BIT(RE); + } + + if (ctrl ^ old_ctrl) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + + return 0; +} + +static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); + + if (enable) + value |= PHYTMAC_BIT(AUTONEG); + else + value &= ~PHYTMAC_BIT(AUTONEG); + + PHYTMAC_WRITE(pdata, PHYTMAC_PCSCTRL, value); + + return 0; +} + +static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + u32 ctrl, config; + + config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + + config &= ~(PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD)); + + if (speed == SPEED_100) + config |= PHYTMAC_BIT(SPEED); + else if (speed == SPEED_1000 || speed == SPEED_2500) + config |= PHYTMAC_BIT(GM_EN); + + if (duplex) + config |= PHYTMAC_BIT(FD); + + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + if (speed == SPEED_2500) { + ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + ctrl |= PHYTMAC_BIT(2PT5G); + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + } + + if (speed == SPEED_10000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_10000M); + else if (speed == SPEED_5000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_5000M); + else if (speed == SPEED_2500) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_2500M); + else if (speed == SPEED_1000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_1000M); + else + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_100M); + + return 0; +} + +static int phytmac_mac_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static int phytmac_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + u32 config; + + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) { + config = PHYTMAC_READ(pdata, PHYTMAC_USXCTRL); + if (speed == SPEED_10000) { + config = PHYTMAC_SET_BITS(config, SERDES_RATE, PHYTMAC_SERDES_RATE_10G); + config = PHYTMAC_SET_BITS(config, USX_SPEED, PHYTMAC_SPEED_10000M); + } else if (speed == SPEED_5000) { + config = PHYTMAC_SET_BITS(config, SERDES_RATE, PHYTMAC_SERDES_RATE_5G); + config = PHYTMAC_SET_BITS(config, USX_SPEED, PHYTMAC_SPEED_5000M); + } + + /* reset */ + config &= ~(PHYTMAC_BIT(RX_EN) | PHYTMAC_BIT(TX_EN)); + config |= PHYTMAC_BIT(RX_SYNC_RESET); + + PHYTMAC_WRITE(pdata, PHYTMAC_USXCTRL, config); + + /* enable rx and tx */ + config &= ~(PHYTMAC_BIT(RX_SYNC_RESET)); + config |= PHYTMAC_BIT(RX_EN) | PHYTMAC_BIT(TX_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_USXCTRL, config); + } + + return 0; +} + +static int phytmac_pcs_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) +{ + u32 bottom; + u16 top; + + bottom = PHYTMAC_READ(pdata, PHYTMAC_MAC1B); + top = PHYTMAC_READ(pdata, PHYTMAC_MAC1T); + + addr[0] = bottom & 0xff; + addr[1] = (bottom >> 8) & 0xff; + addr[2] = (bottom >> 16) & 0xff; + addr[3] = (bottom >> 24) & 0xff; + addr[4] = top & 0xff; + addr[5] = (top >> 8) & 0xff; + + return 0; +} + +static int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) +{ + u32 bottom; + u16 top; + + bottom = cpu_to_le32(*((u32 *)addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_MAC1B, bottom); + top = cpu_to_le16(*((u16 *)(addr + 4))); + PHYTMAC_WRITE(pdata, PHYTMAC_MAC1T, top); + + return 0; +} + +static void phytmac_reset_hw(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + unsigned int q; + u32 ctrl; + + ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + + ctrl &= ~(PHYTMAC_BIT(RE) | PHYTMAC_BIT(TE)); + ctrl |= PHYTMAC_BIT(CLEARSTAT); + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + + /* Disable and clear all interrupts and disable queues */ + for (q = 0, queue = pdata->queues; q < pdata->queues_max_num; ++q, ++queue) { + if (q == 0) { + PHYTMAC_WRITE(pdata, PHYTMAC_ID, -1); + PHYTMAC_WRITE(pdata, PHYTMAC_IS, -1); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR_Q0, 1); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR_Q0, 1); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_IDR(q - 1), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_ISR(q - 1), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR(q - 1), 1); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR(q - 1), 1); + } + + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTRH(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), 0); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), 0); + } +} + +static void phytmac_get_regs(struct phytmac *pdata, u32 *reg_buff) +{ + reg_buff[0] = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + reg_buff[1] = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + reg_buff[2] = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); + reg_buff[3] = PHYTMAC_READ(pdata, PHYTMAC_DCONFIG); + reg_buff[4] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR_Q0); + reg_buff[5] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR_Q0); + reg_buff[6] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR(1)); + reg_buff[7] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR(1)); + reg_buff[8] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR(2)); + reg_buff[9] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR(2)); + reg_buff[10] = PHYTMAC_READ(pdata, PHYTMAC_TXPTR(3)); + reg_buff[11] = PHYTMAC_READ(pdata, PHYTMAC_RXPTR(3)); + reg_buff[12] = PHYTMAC_READ(pdata, PHYTMAC_HCONFIG); + reg_buff[13] = PHYTMAC_READ(pdata, PHYTMAC_IM); + if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || + pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { + reg_buff[14] = PHYTMAC_READ(pdata, PHYTMAC_USXCTRL); + reg_buff[15] = PHYTMAC_READ(pdata, PHYTMAC_USXSTATUS); + } else { + reg_buff[14] = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); + reg_buff[15] = PHYTMAC_READ(pdata, PHYTMAC_PCSSTATUS); + } +} + +static int phytmac_init_hw(struct phytmac *pdata) +{ + u32 config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + u32 dmaconfig; + u32 nctrlconfig; + + nctrlconfig = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + nctrlconfig |= PHYTMAC_BIT(MPE); + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, nctrlconfig); + + phytmac_set_mac_addr(pdata, pdata->ndev->dev_addr); + + PHYTMAC_WRITE(pdata, PHYTMAC_AXICTRL, 0x1010); + + /* jumbo */ + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + config |= PHYTMAC_BIT(JUMBO_EN); + else + config |= PHYTMAC_BIT(RCV_BIG); + /* promisc */ + if (pdata->ndev->flags & IFF_PROMISC) + config |= PHYTMAC_BIT(PROMISC); + if (pdata->ndev->features & NETIF_F_RXCSUM) + config |= PHYTMAC_BIT(RCO_EN); + if (!(pdata->ndev->flags & IFF_BROADCAST)) + config |= PHYTMAC_BIT(NO_BCAST); + /* pause enable */ + config |= PHYTMAC_BIT(PAUSE_EN); + /* Rx Fcs remove */ + config |= PHYTMAC_BIT(FCS_REMOVE); + if (pdata->dma_data_width == PHYTMAC_DBW_64) + config |= PHYTMAC_BIT(DBW64); + if (pdata->dma_data_width == PHYTMAC_DBW_128) + config |= PHYTMAC_BIT(DBW128); + /* mdc div */ + config = PHYTMAC_SET_BITS(config, MCD, 6); + netdev_dbg(pdata->ndev, "phytmac configure NetConfig with 0x%08x\n", + config); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + /* init dma */ + dmaconfig = PHYTMAC_READ(pdata, PHYTMAC_DCONFIG); + if (pdata->dma_burst_length) + dmaconfig = PHYTMAC_SET_BITS(dmaconfig, BURST, pdata->dma_burst_length); + /* default in small endian */ + dmaconfig &= ~(PHYTMAC_BIT(ENDIA_PKT) | PHYTMAC_BIT(ENDIA_DESC)); + + if (pdata->ndev->features & NETIF_F_HW_CSUM) + dmaconfig |= PHYTMAC_BIT(TCO_EN); + else + dmaconfig &= ~PHYTMAC_BIT(TCO_EN); + + if (pdata->dma_addr_width) + dmaconfig |= PHYTMAC_BIT(ABW); + + /* fdir ethtype -- ipv4 */ + PHYTMAC_WRITE(pdata, PHYTMAC_ETHT(0), (uint16_t)ETH_P_IP); + + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + dmaconfig |= PHYTMAC_BIT(RX_EXBD_EN) | PHYTMAC_BIT(TX_EXBD_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, dmaconfig); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, 0x80000001); + return 0; +} + +static int phytmac_powerup_hw(struct phytmac *pdata, int on) +{ + u32 status, data0, data1, rdata1; + int ret; + + if (pdata->capacities & PHYTMAC_CAPS_LPI) { + ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh status is busy, status=%x\n", status); + + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy, data0=%x\n", data0); + + data0 = 0; + data0 = PHYTMAC_SET_BITS(data0, DATA0_MSG, PHYTMAC_MSG_PM); + data0 = PHYTMAC_SET_BITS(data0, DATA0_PRO, PHYTMAC_PRO_ID); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA0, data0); + data1 = 0; + + if (on == PHYTMAC_POWERON) { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATON); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } else { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATOFF); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } + + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_AP_CPP_SET, 1); + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy"); + + rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); + if (rdata1 == data1) + netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + else + netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + } + pdata->power_state = on; + + return 0; +} + +static int phytmac_set_wake(struct phytmac *pdata, int wake) +{ + u32 value = 0; + + if (wake & PHYTMAC_WAKE_MAGIC) + value |= PHYTMAC_BIT(MAGIC); + if (wake & PHYTMAC_WAKE_ARP) + value |= PHYTMAC_BIT(ARP); + if (wake & PHYTMAC_WAKE_UCAST) + value |= PHYTMAC_BIT(UCAST); + if (wake & PHYTMAC_WAKE_MCAST) + value |= PHYTMAC_BIT(MCAST); + + PHYTMAC_WRITE(pdata, PHYTMAC_WOL, value); + + return 0; +} + +static void phytmac_mdio_idle(struct phytmac *pdata) +{ + u32 val; + + /* wait for end of transfer */ + val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); + while (!(val & PHYTMAC_BIT(MDIO_IDLE))) { + cpu_relax(); + val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); + } +} + +static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) +{ + u16 data; + + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C22) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C22_READ) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, regnum) + | PHYTMAC_BITS(MUST, 2))); + + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDATA) & 0xffff; + phytmac_mdio_idle(pdata); + + return data; +} + +static int phytmac_mdio_data_write_c22(struct phytmac *pdata, int mii_id, + int regnum, u16 data) +{ + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C22) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C22_WRITE) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, regnum) + | PHYTMAC_BITS(DATA, data) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int devad, int regnum) +{ + u16 data; + + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_ADDR) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, devad & 0x1F) + | PHYTMAC_BITS(DATA, regnum & 0xFFFF) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_READ) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, devad & 0x1F) + | PHYTMAC_BITS(DATA, regnum & 0xFFFF) + | PHYTMAC_BITS(MUST, 2))); + + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDATA) & 0xffff; + phytmac_mdio_idle(pdata); + + return data; +} + +static int phytmac_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, + int regnum, u16 data) +{ + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_ADDR) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, devad & 0x1F) + | PHYTMAC_BITS(DATA, regnum & 0xFFFF) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDATA, (PHYTMAC_BITS(CLAUSE_SEL, PHYTMAC_CLAUSE_C45) + | PHYTMAC_BITS(OPS, PHYTMAC_OPS_C45_WRITE) + | PHYTMAC_BITS(PHY_ADDR, mii_id) + | PHYTMAC_BITS(REG_ADDR, devad & 0x1F) + | PHYTMAC_BITS(DATA, data) + | PHYTMAC_BITS(MUST, 2))); + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_get_feature_all(struct phytmac *pdata) +{ + unsigned int queue_mask; + unsigned int num_queues; + int val; + + /* get max queues */ + queue_mask = 0x1; + queue_mask |= PHYTMAC_READ(pdata, PHYTMAC_DEFAULT2) & 0xff; + num_queues = hweight32(queue_mask); + pdata->queues_max_num = num_queues; + + /* get dma desc prefetch number */ + val = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT4, TXDESCRD); + if (val) + pdata->tx_bd_prefetch = (2 << (val - 1)) * + sizeof(struct phytmac_dma_desc); + + val = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT4, RXDESCRD); + if (val) + pdata->rx_bd_prefetch = (2 << (val - 1)) * + sizeof(struct phytmac_dma_desc); + + /* dma bus width */ + pdata->dma_data_width = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT1, DBW); + + /* dma addr width */ + if (PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT2, DAW64)) + pdata->dma_addr_width = 64; + else + pdata->dma_addr_width = 32; + + /* max rx fs */ + pdata->max_rx_fs = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT3, SCR2CMP); + + if (netif_msg_hw(pdata)) + netdev_info(pdata->ndev, "get feature queue_num=%d, daw=%d, dbw=%d, rx_fs=%d, rx_bd=%d, tx_bd=%d\n", + pdata->queues_num, pdata->dma_addr_width, pdata->dma_data_width, + pdata->max_rx_fs, pdata->rx_bd_prefetch, pdata->tx_bd_prefetch); + return 0; +} + +static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; + u16 index = rx_flow->location; + u32 tmp, fdir_ctrl; + bool ipsrc = false; + bool ipdst = false; + bool port = false; + + tp4sp_v = &rx_flow->h_u.tcp_ip4_spec; + tp4sp_m = &rx_flow->m_u.tcp_ip4_spec; + + if (tp4sp_m->ip4src == 0xFFFFFFFF) { + tmp = 0; + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, ETHTYPE_SIP_OFFSET); + tmp = PHYTMAC_SET_BITS(tmp, OFFSET_TYPE, PHYTMAC_OFFSET_AFTER_L2HEAD); + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 1); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index), tp4sp_v->ip4src); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index), tmp); + ipsrc = true; + } + + if (tp4sp_m->ip4dst == 0xFFFFFFFF) { + /* 2nd compare reg - IP destination address */ + tmp = 0; + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, ETHTYPE_DIP_OFFSET); + tmp = PHYTMAC_SET_BITS(tmp, OFFSET_TYPE, PHYTMAC_OFFSET_AFTER_L2HEAD); + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 1); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 1), tp4sp_v->ip4dst); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index + 1), tmp); + ipdst = true; + } + + if (tp4sp_m->psrc == 0xFFFF || tp4sp_m->pdst == 0xFFFF) { + tmp = 0; + tmp = PHYTMAC_SET_BITS(tmp, OFFSET_TYPE, PHYTMAC_OFFSET_AFTER_L3HEAD); + if (tp4sp_m->psrc == tp4sp_m->pdst) { + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 1); + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, IPHEAD_SPORT_OFFSET); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 2), + tp4sp_v->psrc | (u32)(tp4sp_v->pdst << 16)); + } else { + tmp = PHYTMAC_SET_BITS(tmp, DIS_MASK, 0); + if (tp4sp_m->psrc == 0xFFFF) { /* src port */ + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, IPHEAD_SPORT_OFFSET); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 2), + tp4sp_m->psrc | (u32)(tp4sp_v->psrc << 16)); + } else { /* dst port */ + tmp = PHYTMAC_SET_BITS(tmp, OFFSET, IPHEAD_DPORT_OFFSET); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + 2), + tp4sp_m->pdst | (u32)(tp4sp_v->pdst << 16)); + } + } + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index + 2), tmp); + port = true; + } + + fdir_ctrl = PHYTMAC_READ(pdata, PHYTMAC_FDIR(rx_flow->location)); + + if (ipsrc) { + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CA, 3 * index); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CA_EN, 1); + } + + if (ipdst) { + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CB, 3 * index + 1); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CB_EN, 1); + } + + if (port) { + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CC, 3 * index + 2); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, CC_EN, 1); + } + + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, QUEUE_NUM, (rx_flow->ring_cookie) & 0xFF); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, ETH_TYPE, 0); + fdir_ctrl = PHYTMAC_SET_BITS(fdir_ctrl, ETH_EN, 1); + + PHYTMAC_WRITE(pdata, PHYTMAC_FDIR(rx_flow->location), fdir_ctrl); + + return 0; +} + +static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + int i; + int index = rx_flow->location; + + PHYTMAC_WRITE(pdata, PHYTMAC_FDIR(index), 0); + for (i = 0; i < 3; i++) { + PHYTMAC_WRITE(pdata, PHYTMAC_COMP1(3 * index + i), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_COMP2(3 * index + i), 0); + } + return 0; +} + +static int phytmac_init_ring_hw(struct phytmac *pdata) +{ + struct phytmac_queue *queue; + unsigned int q = 0; + u32 buffer_size = pdata->rx_buffer_len / 64; + + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + if (q == 0) { + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR_Q0, + lower_32_bits(queue->rx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR_Q0, + lower_32_bits(queue->tx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, + PHYTMAC_SET_BITS(PHYTMAC_READ(pdata, PHYTMAC_DCONFIG), + RX_BUF_LEN, buffer_size)); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTR(q - 1), + lower_32_bits(queue->rx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTR(q - 1), + lower_32_bits(queue->tx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_RBQS(q - 1), buffer_size); + } + + PHYTMAC_WRITE(pdata, PHYTMAC_TXPTRH(q), upper_32_bits(queue->tx_ring_addr)); + PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), upper_32_bits(queue->rx_ring_addr)); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), queue->tx_tail); + } + + return 0; +} + +static u32 phytmac_get_irq_mask(u32 mask) +{ + u32 value = 0; + + value |= (mask & PHYTMAC_INT_TX_COMPLETE) ? PHYTMAC_BIT(TXCOMP) : 0; + value |= (mask & PHYTMAC_INT_TX_ERR) ? PHYTMAC_BIT(BUS_ERR) : 0; + value |= (mask & PHYTMAC_INT_RX_COMPLETE) ? PHYTMAC_BIT(RXCOMP) : 0; + value |= (mask & PHYTMAC_INT_RX_OVERRUN) ? PHYTMAC_BIT(RXOVERRUN) : 0; + value |= (mask & PHYTMAC_INT_RX_DESC_FULL) ? PHYTMAC_BIT(RUB) : 0; + + return value; +} + +static u32 phytmac_get_irq_status(u32 value) +{ + u32 status = 0; + + status |= (value & PHYTMAC_BIT(TXCOMP)) ? PHYTMAC_INT_TX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(BUS_ERR)) ? PHYTMAC_INT_TX_ERR : 0; + status |= (value & PHYTMAC_BIT(RXCOMP)) ? PHYTMAC_INT_RX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(RXOVERRUN)) ? PHYTMAC_INT_RX_OVERRUN : 0; + status |= (value & PHYTMAC_BIT(RUB)) ? PHYTMAC_INT_RX_DESC_FULL : 0; + + return status; +} + +static void phytmac_enable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + + if (queue_index == 0) + PHYTMAC_WRITE(pdata, PHYTMAC_IE, value); + else + PHYTMAC_WRITE(pdata, PHYTMAC_IER(queue_index - 1), value); +} + +static void phytmac_disable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + + if (queue_index == 0) + PHYTMAC_WRITE(pdata, PHYTMAC_ID, value); + else + PHYTMAC_WRITE(pdata, PHYTMAC_IDR(queue_index - 1), value); +} + +static void phytmac_clear_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + + if (queue_index == 0) + PHYTMAC_WRITE(pdata, PHYTMAC_IS, value); + else + PHYTMAC_WRITE(pdata, PHYTMAC_ISR(queue_index - 1), value); +} + +static unsigned int phytmac_get_intx_mask(struct phytmac *pdata) +{ + u32 value; + + value = PHYTMAC_READ(pdata, PHYTMAC_INTX_IRQ_MASK); + PHYTMAC_WRITE(pdata, PHYTMAC_INTX_IRQ_MASK, value); + + return value; +} + +static unsigned int phytmac_get_irq(struct phytmac *pdata, int queue_index) +{ + u32 status, value; + + if (queue_index == 0) + value = PHYTMAC_READ(pdata, PHYTMAC_IS); + else + value = PHYTMAC_READ(pdata, PHYTMAC_ISR(queue_index - 1)); + + status = phytmac_get_irq_status(value); + + return status; +} + +static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, + u32 tx_tail, struct packet_info *packet) +{ + unsigned int i, ctrl; + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + struct phytmac_tx_skb *tx_skb; + unsigned int eof = 1; + + i = tx_tail; + + if (!(pdata->capacities & PHYTMAC_CAPS_TAILPTR)) { + ctrl = PHYTMAC_BIT(TX_USED); + desc = phytmac_get_tx_desc(queue, i); + desc->desc1 = ctrl; + } + + do { + i--; + tx_skb = phytmac_get_tx_skb(queue, i); + desc = phytmac_get_tx_desc(queue, i); + + ctrl = (u32)tx_skb->length; + if (eof) { + ctrl |= PHYTMAC_BIT(TX_LAST); + eof = 0; + } + + if (unlikely(i == (pdata->tx_ring_size - 1))) + ctrl |= PHYTMAC_BIT(TX_WRAP); + + if (i == queue->tx_tail) { + ctrl |= PHYTMAC_BITS(TX_LSO, packet->lso); + ctrl |= PHYTMAC_BITS(TX_TCP_SEQ_SRC, packet->seq); + if (packet->nocrc) + ctrl |= PHYTMAC_BIT(TX_NOCRC); + } else { + ctrl |= PHYTMAC_BITS(MSS_MFS, packet->mss); + } + + desc->desc2 = upper_32_bits(tx_skb->addr); + desc->desc0 = lower_32_bits(tx_skb->addr); + /* Make newly descriptor visible to hardware */ + wmb(); + desc->desc1 = ctrl; + } while (i != queue->tx_tail); + + return 0; +} + +static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, + u32 index) +{ + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + desc->desc1 = 0; + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 |= PHYTMAC_BIT(RX_USED); +} + +static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, + u32 index, dma_addr_t addr) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + if (addr) { + if (unlikely(index == (pdata->rx_ring_size - 1))) + addr |= PHYTMAC_BIT(RX_WRAP); + desc->desc1 = 0; + desc->desc2 = upper_32_bits(addr); + desc->desc0 = lower_32_bits(addr) | PHYTMAC_BIT(RX_USED); + } + return 0; +} + +static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count) +{ + struct phytmac_dma_desc *desc; + u32 index = queue->rx_head + count - 1; + + while (count) { + desc = phytmac_get_rx_desc(queue, index); + desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + dma_wmb(); + index--; + count--; + } + + return 0; +} + +static void phytmac_tx_start(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), + BIT(31) | queue->tx_tail); + + if (pdata->capacities & PHYTMAC_CAPS_START) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, + PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); +} + +static void phytmac_restart(struct phytmac *pdata) +{ + int q; + struct phytmac_queue *queue; + + for (q = 0; q < pdata->queues_num; q++) { + queue = &pdata->queues[q]; + if (queue->tx_head != queue->tx_tail) { + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, + PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); + break; + } + } +} + +static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) +{ + return PHYTMAC_GET_BITS(desc->desc1, TX_USED); +} + +static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +{ + return (desc->desc0 & PHYTMAC_BIT(RX_USED)) != 0; +} + +static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) +{ + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + return desc->desc1 & PHYTMAC_JUMBO_FRAME_MASK; + else + return desc->desc1 & PHYTMAC_FRAME_MASK; +} + +static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) +{ + dma_addr_t addr = 0; + + addr = ((u64)(desc->desc2) << 32); + + addr |= (desc->desc0 & 0xfffffffc); + return addr; +} + +static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + u32 check = value >> PHYTMAC_RX_CSUM_INDEX & 0x3; + + return (check == PHYTMAC_RX_CSUM_IP_TCP || check == PHYTMAC_RX_CSUM_IP_UDP); +} + +static bool phytmac_rx_single_buffer(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return ((value & PHYTMAC_BIT(RX_SOF)) && (value & PHYTMAC_BIT(RX_EOF))); +} + +static bool phytmac_rx_sof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RX_SOF)); +} + +static bool phytmac_rx_eof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RX_EOF)); +} + +static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) +{ + unsigned int frag; + unsigned int tmp = end; + struct phytmac_dma_desc *desc; + + if (begin > end) + tmp = end + queue->pdata->rx_ring_size; + + for (frag = begin; frag != end; frag++) { + desc = phytmac_get_rx_desc(queue, frag); + desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + } +} + +static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mode, + const struct phylink_link_state *state) +{ + u32 old_ctrl, old_config; + u32 ctrl, config, usxctl; + + old_ctrl = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); + old_config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); + ctrl = old_ctrl; + config = old_config; + + if (state->speed == SPEED_10000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_10000M); + else if (state->speed == SPEED_5000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_5000M); + else if (state->speed == SPEED_2500) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_2500M); + else if (state->speed == SPEED_1000) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_1000M); + else if (state->speed == SPEED_100 || state->speed == SPEED_10) + PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_100M); + + config &= ~(PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN) + | PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD) | PHYTMAC_BIT(GM_EN)); + ctrl &= ~(PHYTMAC_BIT(HIGHSPEED) | PHYTMAC_BIT(2PT5G)); + + if (state->interface == PHY_INTERFACE_MODE_SGMII || + state->interface == PHY_INTERFACE_MODE_2500BASEX) { + config |= PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN); + } else if (state->interface == PHY_INTERFACE_MODE_10GBASER || + state->interface == PHY_INTERFACE_MODE_USXGMII || + state->interface == PHY_INTERFACE_MODE_5GBASER) { + usxctl = PHYTMAC_READ(pdata, PHYTMAC_USXCTRL); + if (state->speed == SPEED_10000) { + usxctl = PHYTMAC_SET_BITS(usxctl, SERDES_RATE, PHYTMAC_SERDES_RATE_10G); + usxctl = PHYTMAC_SET_BITS(usxctl, USX_SPEED, PHYTMAC_SPEED_10000M); + } else if (state->speed == SPEED_5000) { + usxctl = PHYTMAC_SET_BITS(usxctl, SERDES_RATE, PHYTMAC_SERDES_RATE_5G); + usxctl = PHYTMAC_SET_BITS(usxctl, USX_SPEED, PHYTMAC_SPEED_5000M); + } + usxctl |= PHYTMAC_BIT(RX_EN) | PHYTMAC_BIT(TX_EN); + PHYTMAC_WRITE(pdata, PHYTMAC_USXCTRL, usxctl); + + config |= PHYTMAC_BIT(PCS_EN); + ctrl |= PHYTMAC_BIT(HIGHSPEED); + } + + if (state->duplex) + config |= PHYTMAC_BIT(FD); + + if (old_ctrl ^ ctrl) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); + + if (old_config ^ config) + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); + + /* Disable AN for SGMII fixed link configuration, enable otherwise.*/ + if (state->interface == PHY_INTERFACE_MODE_SGMII || + state->interface == PHY_INTERFACE_MODE_2500BASEX) + phytmac_enable_autoneg(pdata, mode == MLO_AN_FIXED ? 0 : 1); +} + +static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, + phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_2500BASEX) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_NSTATUS, PCS_LINK); + else if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_USXSTATUS, USX_PCS_LINK); + + return 0; +} + +static void phytmac_clear_tx_desc(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc = NULL; + int i; + + for (i = 0; i < queue->pdata->tx_ring_size; i++) { + desc = phytmac_get_tx_desc(queue, i); + desc->desc2 = 0; + desc->desc0 = 0; + /* make newly desc1 to hardware */ + wmb(); + desc->desc1 = PHYTMAC_BIT(TX_USED); + } + desc->desc1 |= PHYTMAC_BIT(TX_WRAP); + + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), queue->tx_tail); +} + +static void phytmac_get_hw_stats(struct phytmac *pdata) +{ + u32 stats[45]; + int i, j; + u64 val; + u64 *p = &pdata->stats.tx_octets; + + for (i = 0 ; i < 45; i++) + stats[i] = PHYTMAC_READ(pdata, PHYTMAC_OCTTX + i * 4); + + for (i = 0, j = 0; i < 45; i++) { + if (i == 0 || i == 20) { + val = (u64)stats[i + 1] << 32 | stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } else { + if (i != 1 && i != 21) { + val = stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } + } + } +} + +static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) +{ + u32 ns, secl, sech; + + ns = PHYTMAC_READ(pdata, PHYTMAC_TN); + secl = PHYTMAC_READ(pdata, PHYTMAC_TSL); + sech = PHYTMAC_READ(pdata, PHYTMAC_TSH); + + ts->tv_nsec = ns; + ts->tv_sec = (((u64)sech << 32) | secl) & SEC_MAX_VAL; +} + +static void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) +{ + u32 secl, sech; + + secl = (u32)sec; + sech = (sec >> 32) & (0xffff); + + PHYTMAC_WRITE(pdata, PHYTMAC_TN, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TSH, sech); + PHYTMAC_WRITE(pdata, PHYTMAC_TSL, secl); + PHYTMAC_WRITE(pdata, PHYTMAC_TN, nsec); +} + +static void phytmac_clear_time(struct phytmac *pdata) +{ + u32 value; + + pdata->ts_incr.sub_ns = 0; + pdata->ts_incr.ns = 0; + + value = PHYTMAC_READ(pdata, PHYTMAC_TISN); + value = PHYTMAC_SET_BITS(value, SUB_NSECL, 0); + value = PHYTMAC_SET_BITS(value, SUB_NSECH, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TISN, value); + + value = PHYTMAC_READ(pdata, PHYTMAC_TI); + value = PHYTMAC_SET_BITS(value, INCR_NS, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TI, value); + + PHYTMAC_WRITE(pdata, PHYTMAC_TA, 0); +} + +static int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) +{ + if (ctrl->rx_control == TS_ALL_PTP_FRAMES) + PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, + PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(STORE_RX_TS)); + + PHYTMAC_WRITE(pdata, PHYTMAC_TXBDCTRL, PHYTMAC_BITS(TX_TSMODE, ctrl->tx_control)); + PHYTMAC_WRITE(pdata, PHYTMAC_RXBDCTRL, PHYTMAC_BITS(RX_TSMODE, ctrl->rx_control)); + + return 0; +} + +static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) +{ + u32 value; + + value = PHYTMAC_BITS(SUB_NSECL, incr->sub_ns) | + PHYTMAC_BITS(SUB_NSECH, incr->sub_ns >> 8); + PHYTMAC_WRITE(pdata, PHYTMAC_TISN, value); + PHYTMAC_WRITE(pdata, PHYTMAC_TI, incr->ns); + + return 0; +} + +static void phytmac_ptp_init_hw(struct phytmac *pdata) +{ + struct timespec64 ts; + + ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); + phytmac_set_time(pdata, ts.tv_sec, ts.tv_nsec); + + phytmac_set_tsincr(pdata, &pdata->ts_incr); +} + +static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) +{ + struct ts_incr ts_incr; + u32 tmp; + u64 adj; + + ts_incr.ns = pdata->ts_incr.ns; + ts_incr.sub_ns = pdata->ts_incr.sub_ns; + + tmp = ((u64)ts_incr.ns << PHYTMAC_SUB_NSECL_INDEX) + ts_incr.sub_ns; + adj = ((u64)ppm * tmp + (USEC_PER_SEC >> 1)) >> PHYTMAC_SUB_NSECL_INDEX; + + adj = div_u64(adj, USEC_PER_SEC); + adj = negative ? (tmp - adj) : (tmp + adj); + + ts_incr.ns = (adj >> PHYTMAC_SUB_NSEC_WIDTH) + & ((1 << PHYTMAC_SUB_NSECL_WIDTH) - 1); + ts_incr.sub_ns = adj & ((1 << PHYTMAC_SUB_NSEC_WIDTH) - 1); + + phytmac_set_tsincr(pdata, &ts_incr); + + return 0; +} + +static int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) +{ + u32 adj; + + if (delta > PHYTMAC_ADJUST_SEC_MAX) { + struct timespec64 now, then; + + if (neg) + then = ns_to_timespec64(-delta); + else + then = ns_to_timespec64(delta); + phytmac_get_time(pdata, &now); + now = timespec64_add(now, then); + phytmac_set_time(pdata, now.tv_sec, now.tv_nsec); + } else { + adj = (neg << PHYTMAC_INCR_ADD_INDEX) | delta; + PHYTMAC_WRITE(pdata, PHYTMAC_TA, adj); + } + + return 0; +} + +static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) +{ + int ts_valid = 0; + + if (direction == PHYTMAC_TX) + ts_valid = desc->desc1 & PHYTMAC_BIT(TX_TS_VALID); + else if (direction == PHYTMAC_RX) + ts_valid = desc->desc0 & PHYTMAC_BIT(RX_TS_VALID); + return ts_valid; +} + +static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) +{ + struct timespec64 ts2; + + ts->tv_sec = (PHYTMAC_GET_BITS(ts_2, DMA_SECH) << 2) | + PHYTMAC_GET_BITS(ts_1, DMA_SECL); + ts->tv_nsec = PHYTMAC_GET_BITS(ts_1, DMA_NSEC); + + phytmac_get_time(pdata, &ts2); + + if (((ts->tv_sec ^ ts2.tv_sec) & (PHYTMAC_DMA_SEC_TOP >> 1)) != 0) + ts->tv_sec -= PHYTMAC_DMA_SEC_TOP; + + ts->tv_sec += (ts2.tv_sec & (~PHYTMAC_DMA_SEC_MASK)); +} + +static unsigned int phytmac_get_ts_rate(struct phytmac *pdata) +{ + return 300000000; +} + +struct phytmac_hw_if phytmac_1p0_hw = { + .reset_hw = phytmac_reset_hw, + .init_hw = phytmac_init_hw, + .init_ring_hw = phytmac_init_ring_hw, + .get_feature = phytmac_get_feature_all, + .get_regs = phytmac_get_regs, + .get_stats = phytmac_get_hw_stats, + .set_mac_address = phytmac_set_mac_addr, + .get_mac_address = phytmac_get_mac_addr, + .mdio_read = phytmac_mdio_data_read_c22, + .mdio_write = phytmac_mdio_data_write_c22, + .mdio_read_c45 = phytmac_mdio_data_read_c45, + .mdio_write_c45 = phytmac_mdio_data_write_c45, + .poweron = phytmac_powerup_hw, + .set_wol = phytmac_set_wake, + .enable_promise = phytmac_enable_promise, + .enable_multicast = phytmac_enable_multicast, + .set_hash_table = phytmac_set_mc_hash, + .enable_rx_csum = phytmac_enable_rxcsum, + .enable_tx_csum = phytmac_enable_txcsum, + .enable_mdio_control = phytmac_enable_mdio, + .enable_autoneg = phytmac_enable_autoneg, + .enable_pause = phytmac_enable_pause, + .enable_network = phytmac_enable_network, + .add_fdir_entry = phytmac_add_fdir_entry, + .del_fdir_entry = phytmac_del_fdir_entry, + /* mac config */ + .mac_config = phytmac_mac_interface_config, + .mac_linkup = phytmac_mac_linkup, + .mac_linkdown = phytmac_mac_linkdown, + .pcs_linkup = phytmac_pcs_linkup, + .pcs_linkdown = phytmac_pcs_linkdown, + .get_link = phytmac_pcs_get_link, + /* irq */ + .enable_irq = phytmac_enable_irq, + .disable_irq = phytmac_disable_irq, + .clear_irq = phytmac_clear_irq, + .get_irq = phytmac_get_irq, + .get_intx_mask = phytmac_get_intx_mask, + /* tx and rx */ + .tx_map = phytmac_tx_map_desc, + .transmit = phytmac_tx_start, + .restart = phytmac_restart, + .tx_complete = phytmac_tx_complete, + .rx_complete = phytmac_rx_complete, + .get_rx_pkt_len = phytmac_rx_pkt_len, + .get_desc_addr = phytmac_get_desc_addr, + .init_rx_map = phytmac_init_rx_map_desc, + .rx_map = phytmac_rx_map_desc, + .rx_clean = phytmac_rx_clean_desc, + .rx_checksum = phytmac_rx_checksum, + .rx_single_buffer = phytmac_rx_single_buffer, + .rx_pkt_start = phytmac_rx_sof, + .rx_pkt_end = phytmac_rx_eof, + .clear_rx_desc = phytmac_clear_rx_desc, + .clear_tx_desc = phytmac_clear_tx_desc, + /* ptp */ + .init_ts_hw = phytmac_ptp_init_hw, + .set_time = phytmac_set_time, + .clear_time = phytmac_clear_time, + .get_time = phytmac_get_time, + .set_ts_config = phytmac_set_tsmode, + .set_incr = phytmac_set_tsincr, + .adjust_fine = phytmac_adjust_fine, + .adjust_time = phytmac_adjust_time, + .ts_valid = phytmac_ts_valid, + .get_timestamp = phytmac_get_dma_ts, + .get_ts_rate = phytmac_get_ts_rate, +}; +EXPORT_SYMBOL_GPL(phytmac_1p0_hw); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h new file mode 100644 index 00000000000000..6f2b521aaebdbb --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -0,0 +1,453 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _PHYTMAC_V1_H +#define _PHYTMAC_V1_H + +extern struct phytmac_hw_if phytmac_1p0_hw; + +#define PHYTMAC_FRAME_MASK 0x1fff +#define PHYTMAC_JUMBO_FRAME_MASK 0x3fff + +#define PHYTMAC_SPEED_100M 0 +#define PHYTMAC_SPEED_1000M 1 +#define PHYTMAC_SPEED_2500M 2 +#define PHYTMAC_SPEED_5000M 3 +#define PHYTMAC_SPEED_10000M 4 +#define PHYTMAC_SERDES_RATE_5G 0 +#define PHYTMAC_SERDES_RATE_10G 1 + +#define PHYTMAC_NCTRL 0x0000 +#define PHYTMAC_NCONFIG 0x0004 +#define PHYTMAC_NSTATUS 0x0008 +#define PHYTMAC_DCONFIG 0x0010 +#define PHYTMAC_RXPTR_Q0 0x0018 +#define PHYTMAC_TXPTR_Q0 0x001C +#define PHYTMAC_IS 0x0024 +#define PHYTMAC_IE 0x0028 +#define PHYTMAC_ID 0x002C +#define PHYTMAC_IM 0x0030 +#define PHYTMAC_MDATA 0x0034 +#define PHYTMAC_HCONFIG 0x0050 +#define PHYTMAC_AXICTRL 0x0054 +#define PHYTMAC_INT_MODERATION 0x005C +#define PHYTMAC_HASHB 0x0080 +#define PHYTMAC_HASHT 0x0084 +#define PHYTMAC_MAC1B 0x0088 +#define PHYTMAC_MAC1T 0x008C +#define PHYTMAC_WOL 0x00B8 +#define PHYTMAC_OCTTX 0x0100 +#define PHYTMAC_TISN 0x01BC +#define PHYTMAC_TSH 0x01C0 +#define PHYTMAC_TSL 0x01D0 +#define PHYTMAC_TN 0x01D4 +#define PHYTMAC_TA 0x01D8 +#define PHYTMAC_TI 0x01DC +#define PHYTMAC_PCSCTRL 0x0200 +#define PHYTMAC_PCSSTATUS 0x0204 +#define PHYTMAC_PCSANLPBASE 0x0214 +#define PHYTMAC_DEFAULT1 0x0280 /* Default HW Config 1 */ +#define PHYTMAC_DEFAULT2 0x0294 /* Default HW Config 2 */ +#define PHYTMAC_DEFAULT3 0x029C /* Default HW Config 3 */ +#define PHYTMAC_DEFAULT4 0x02A4 /* Default HW Config 4 */ +#define PHYTMAC_DEFAULT5 0x02AC /* Default HW Config 5 */ +#define PHYTMAC_USXCTRL 0x0A80 +#define PHYTMAC_USXSTATUS 0x0A88 +#define PHYTMAC_TXBDCTRL 0x04CC +#define PHYTMAC_RXBDCTRL 0x04D0 + +/* Fdir match registers */ +#define PHYTMAC_FDIR(i) (0x0540 + ((i) << 2)) + +/* EtherType registers */ +#define PHYTMAC_ETHT(i) (0x06E0 + ((i) << 2)) + +/* Fdir compare registers */ +#define PHYTMAC_COMP1(i) (0x0700 + ((i) << 3)) +#define PHYTMAC_COMP2(i) (0x0704 + ((i) << 3)) + +#define PHYTMAC_ISR(i) (0x0400 + ((i) << 2)) +#define PHYTMAC_TXPTR(i) (0x0440 + ((i) << 2)) +#define PHYTMAC_RXPTR(i) (0x0480 + ((i) << 2)) +#define PHYTMAC_RBQS(i) (0x04A0 + ((i) << 2)) +#define PHYTMAC_TXPTRH(i) (0x04c8) +#define PHYTMAC_RXPTRH(i) (0x04d4) + +#define PHYTMAC_IER(i) (0x0600 + ((i) << 2)) +#define PHYTMAC_IDR(i) (0x0620 + ((i) << 2)) +#define PHYTMAC_IMR(i) (0x0640 + ((i) << 2)) +#define PHYTMAC_TAIL_ENABLE (0x0e7c) +#define PHYTMAC_TAILPTR(i) (0x0e80 + ((i) << 2)) + +#define PHYTMAC_PHY_INT_ENABLE 0x1C88 +#define PHYTMAC_PHY_INT_CLEAR 0x1C8C +#define PHYTMAC_PHY_INT_STATE 0x1C90 +#define PHYTMAC_INTX_IRQ_MASK 0x1C7C + +#define PHYTMAC_READ_NSTATUS(pdata) PHYTMAC_READ(pdata, PHYTMAC_NSTATUS) + +/* Ethernet Network Control Register */ +#define PHYTMAC_RE_INDEX 2 /* Receive enable */ +#define PHYTMAC_RE_WIDTH 1 +#define PHYTMAC_TE_INDEX 3 /* Transmit enable */ +#define PHYTMAC_TE_WIDTH 1 +#define PHYTMAC_MPE_INDEX 4 /* Management port enable */ +#define PHYTMAC_MPE_WIDTH 1 +#define PHYTMAC_CLEARSTAT_INDEX 5 /* Clear stats regs */ +#define PHYTMAC_CLEARSTAT_WIDTH 1 +#define PHYTMAC_TSTART_INDEX 9 /* Start transmission */ +#define PHYTMAC_TSTART_WIDTH 1 +#define PHYTMAC_THALT_INDEX 10 /* Transmit halt */ +#define PHYTMAC_THALT_WIDTH 1 +#define PHYTMAC_STORE_RX_TS_INDEX 15 +#define PHYTMAC_STORE_RX_TS_WIDTH 1 +#define PHYTMAC_OSSMODE_INDEX 24 /* Enable One Step Synchro Mode */ +#define PHYTMAC_OSSMODE_WIDTH 1 +#define PHYTMAC_2PT5G_INDEX 29 /* 2.5G operation selected */ +#define PHYTMAC_2PT5G_WIDTH 1 +#define PHYTMAC_HIGHSPEED_INDEX 31 /* High speed enable */ +#define PHYTMAC_HIGHSPEED_WIDTH 1 + +/* Ethernet Network Config Register */ +#define PHYTMAC_SPEED_INDEX 0 /* Speed */ +#define PHYTMAC_SPEED_WIDTH 1 +#define PHYTMAC_FD_INDEX 1 /* Full duplex */ +#define PHYTMAC_FD_WIDTH 1 +#define PHYTMAC_JUMBO_EN_INDEX 3 /* Transmit enable */ +#define PHYTMAC_JUMBO_EN_WIDTH 1 +#define PHYTMAC_PROMISC_INDEX 4 /* Copy all frames */ +#define PHYTMAC_PROMISC_WIDTH 1 +#define PHYTMAC_NO_BCAST_INDEX 5 /* No broadcast */ +#define PHYTMAC_NO_BCAST_WIDTH 1 +#define PHYTMAC_MH_EN_INDEX 6 /* Multicast hash enable */ +#define PHYTMAC_MH_EN_WIDTH 1 +#define PHYTMAC_RCV_BIG_INDEX 8 +#define PHYTMAC_RCV_BIG_WIDTH 1 +#define PHYTMAC_GM_EN_INDEX 10 /* Gigabit mode enable */ +#define PHYTMAC_GM_EN_WIDTH 1 +#define PHYTMAC_PCS_EN_INDEX 11 /* PCS select */ +#define PHYTMAC_PCS_EN_WIDTH 1 +#define PHYTMAC_PAUSE_EN_INDEX 13 /* Pause enable */ +#define PHYTMAC_PAUSE_EN_WIDTH 1 +#define PHYTMAC_FCS_REMOVE_INDEX 17 /* FCS remove */ +#define PHYTMAC_FCS_REMOVE_WIDTH 1 +#define PHYTMAC_MCD_INDEX 18 /* MDC clock division */ +#define PHYTMAC_MCD_WIDTH 3 +#define PHYTMAC_DBW64_INDEX 21 /* Data bus width */ +#define PHYTMAC_DBW64_WIDTH 1 +#define PHYTMAC_DBW128_INDEX 22 /* Data bus width */ +#define PHYTMAC_DBW128_WIDTH 1 +#define PHYTMAC_DBW_32 1 +#define PHYTMAC_DBW_64 2 +#define PHYTMAC_DBW_128 4 +#define PHYTMAC_RCO_EN_INDEX 24 /* Receive checksum offload enable */ +#define PHYTMAC_RCO_EN_WIDTH 1 +#define PHYTMAC_SGMII_EN_INDEX 27 /* Sgmii mode enable */ +#define PHYTMAC_SGMII_EN_WIDTH 1 + +/* Ethernet Network Status Register */ +#define PHYTMAC_PCS_LINK_INDEX 0 /* PCS link status */ +#define PHYTMAC_PCS_LINK_WIDTH 1 +#define PHYTMAC_MDIO_IDLE_INDEX 2 /* Mdio idle */ +#define PHYTMAC_MDIO_IDLE_WIDTH 1 +#define PHYTMAC_PCS_FD_INDEX 3 /* PCS auto negotiation duplex resolution */ +#define PHYTMAC_PCS_FD__WIDTH 1 + +/* Ethernet Network Dma config Register */ +#define PHYTMAC_BURST_INDEX 0 /* Amba burst length */ +#define PHYTMAC_BURST_WIDTH 5 +#define PHYTMAC_ENDIA_PKT_INDEX 6 +#define PHYTMAC_ENDIA_PKT_WIDTH 1 +#define PHYTMAC_ENDIA_DESC_INDEX 7 +#define PHYTMAC_ENDIA_DESC_WIDTH 1 +#define PHYTMAC_TCO_EN_INDEX 11 /* Tx Checksum Offload en */ +#define PHYTMAC_TCO_EN_WIDTH 1 +#define PHYTMAC_RX_BUF_LEN_INDEX 16 /* DMA receive buffer size */ +#define PHYTMAC_RX_BUF_LEN_WIDTH 8 +#define PHYTMAC_RX_EXBD_EN_INDEX 28 /* Enable RX extended BD mode */ +#define PHYTMAC_RX_EXBD_EN_WIDTH 1 +#define PHYTMAC_TX_EXBD_EN_INDEX 29 /* Enable TX extended BD mode */ +#define PHYTMAC_TX_EXBD_EN_WIDTH 1 +#define PHYTMAC_ABW_INDEX 30 /* DMA address bus width */ +#define PHYTMAC_ABW_WIDTH 1 + +/* Int stauts/Enable/Disable/Mask Register */ +#define PHYTMAC_RXCOMP_INDEX 1 /* Rx complete */ +#define PHYTMAC_RXCOMP_WIDTH 1 +#define PHYTMAC_RUB_INDEX 2 /* Rx used bit read */ +#define PHYTMAC_RUB_WIDTH 1 +#define PHYTMAC_BUS_ERR_INDEX 6 /* AMBA error */ +#define PHYTMAC_BUS_ERR_WIDTH 1 +#define PHYTMAC_TXCOMP_INDEX 7 /* Tx complete */ +#define PHYTMAC_TXCOMP_WIDTH 1 +#define PHYTMAC_RXOVERRUN_INDEX 10 /* Tx overrun */ +#define PHYTMAC_RXOVERRUN_WIDTH 1 +#define PHYTMAC_RESP_ERR_INDEX 11 /* Resp not ok */ +#define PHYTMAC_RESP_ERR_WIDTH 1 + +/* Mdio read/write Register */ +#define PHYTMAC_DATA_INDEX 0 /* Data */ +#define PHYTMAC_DATA_WIDTH 16 +#define PHYTMAC_MUST_INDEX 16 /* Must Be 10 */ +#define PHYTMAC_MUST_WIDTH 2 +#define PHYTMAC_REG_ADDR_INDEX 18 /* Register address */ +#define PHYTMAC_REG_ADDR_WIDTH 5 +#define PHYTMAC_PHY_ADDR_INDEX 23 /* Phy address */ +#define PHYTMAC_PHY_ADDR_WIDTH 5 +#define PHYTMAC_OPS_INDEX 28 +#define PHYTMAC_OPS_WIDTH 2 +#define PHYTMAC_CLAUSE_SEL_INDEX 30 +#define PHYTMAC_CLAUSE_SEL_WIDTH 1 +#define PHYTMAC_CLAUSE_C22 1 +#define PHYTMAC_CLAUSE_C45 0 +#define PHYTMAC_OPS_C45_ADDR 0 +#define PHYTMAC_OPS_C45_WRITE 1 +#define PHYTMAC_OPS_C45_READ 3 +#define PHYTMAC_OPS_C22_WRITE 1 +#define PHYTMAC_OPS_C22_READ 2 + +/* hs mac config register */ +#define PHYTMAC_HS_SPEED_INDEX 0 +#define PHYTMAC_HS_SPEED_WIDTH 3 +#define PHYTMAC_HS_SPEED_100M 0 +#define PHYTMAC_HS_SPEED_1000M 1 +#define PHYTMAC_HS_SPEED_2500M 2 +#define PHYTMAC_HS_SPEED_5000M 3 +#define PHYTMAC_HS_SPEED_10G 4 + +/* WOL register */ +#define PHYTMAC_ARP_IP_INDEX 0 +#define PHYTMAC_ARP_IP_WIDTH 16 +#define PHYTMAC_MAGIC_INDEX 16 +#define PHYTMAC_MAGIC_WIDTH 1 +#define PHYTMAC_ARP_INDEX 17 +#define PHYTMAC_ARP_WIDTH 1 +#define PHYTMAC_UCAST_INDEX 18 +#define PHYTMAC_UCAST_WIDTH 1 +#define PHYTMAC_MCAST_INDEX 19 +#define PHYTMAC_MCAST_WIDTH 1 + +/* PCSCTRL register */ +#define PHYTMAC_AUTONEG_INDEX 12 +#define PHYTMAC_AUTONEG_WIDTH 1 + +/* DEFAULT1 register */ +#define PHYTMAC_DBW_INDEX 25 +#define PHYTMAC_DBW_WIDTH 3 + +/* DEFAULT2 register */ +#define PHYTMAC_DAW64_INDEX 23 +#define PHYTMAC_DAW64_WIDTH 1 + +/* DEFAULT3 register */ +#define PHYTMAC_SCR2CMP_INDEX 0 +#define PHYTMAC_SCR2CMP_WIDTH 8 +#define PHYTMAC_SCR2ETH_INDEX 8 +#define PHYTMAC_SCR2ETH_WIDTH 8 + +/* DEFAULT4 register */ +#define PHYTMAC_TXDESCRD_INDEX 12 +#define PHYTMAC_TXDESCRD_WIDTH 4 +#define PHYTMAC_RXDESCRD_INDEX 8 +#define PHYTMAC_RXDESCRD_WIDTH 4 + +/* USXCTRL register */ +#define PHYTMAC_RX_EN_INDEX 0 +#define PHYTMAC_RX_EN_WIDTH 1 +#define PHYTMAC_TX_EN_INDEX 1 +#define PHYTMAC_TX_EN_WIDTH 1 +#define PHYTMAC_RX_SYNC_RESET_INDEX 2 +#define PHYTMAC_RX_SYNC_RESET_WIDTH 1 +#define PHYTMAC_SERDES_RATE_INDEX 12 +#define PHYTMAC_SERDES_RATE_WIDTH 2 +#define PHYTMAC_USX_SPEED_INDEX 14 +#define PHYTMAC_USX_SPEED_WIDTH 3 + +/* Bitfields in USX_STATUS. */ +#define PHYTMAC_USX_PCS_LINK_INDEX 0 +#define PHYTMAC_USX_PCS_LINK_WIDTH 1 + +/* Bitfields in PHYTMAC_TISN */ +#define PHYTMAC_SUB_NSECH_INDEX 0 +#define PHYTMAC_SUB_NSECH_WIDTH 16 +#define PHYTMAC_SUB_NSECL_INDEX 24 +#define PHYTMAC_SUB_NSECL_WIDTH 8 +#define PHYTMAC_SUB_NSEC_WIDTH 24 + +/* Bitfields in PHYTMAC_TSH */ +#define PHYTMAC_SECH_INDEX 0 +#define PHYTMAC_SECH_WIDTH 16 + +/* Bitfields in PHYTMAC_TSL */ +#define PHYTMAC_SECL_INDEX 0 +#define PHYTMAC_SECL_WIDTH 32 + +/* Bitfields in PHYTMAC_TN */ +#define PHYTMAC_NSEC_INDEX 0 +#define PHYTMAC_NSEC_WIDTH 30 + +/* Bitfields in PHYTMAC_TA */ +#define PHYTMAC_INCR_SEC_INDEX 0 +#define PHYTMAC_INCR_SEC_WIDTH 30 +#define PHYTMAC_INCR_ADD_INDEX 31 +#define PHYTMAC_INCR_ADD_WIDTH 1 +#define PHYTMAC_ADJUST_SEC_MAX ((1 << PHYTMAC_INCR_SEC_WIDTH) - 1) + +/* Bitfields in PHYTMAC_TI */ +#define PHYTMAC_INCR_NS_INDEX 0 +#define PHYTMAC_INCR_NS_WIDTH 8 + +/* PHYTMAC_TXBDCTRL register */ +#define PHYTMAC_TX_TSMODE_INDEX 4 +#define PHYTMAC_TX_TSMODE_WIDTH 2 + +#define PHYTMAC_RX_TSMODE_INDEX 4 +#define PHYTMAC_RX_TSMODE_WIDTH 2 + +/* Bitfields in PHYTMAC_FDIR */ +#define PHYTMAC_QUEUE_NUM_INDEX 0 +#define PHYTMAC_QUEUE_NUM_WIDTH 4 +#define PHYTMAC_VLAN_PRI_INDEX 4 +#define PHYTMAC_VLAN_PRI_WIDTH 3 +#define PHYTMAC_VLAN_EN_INDEX 8 +#define PHYTMAC_VLAN_EN_WIDTH 1 +#define PHYTMAC_ETH_TYPE_INDEX 9 +#define PHYTMAC_ETH_TYPE_WIDTH 3 +#define PHYTMAC_ETH_EN_INDEX 12 +#define PHYTMAC_ETH_EN_WIDTH 1 +#define PHYTMAC_CA_INDEX 13 +#define PHYTMAC_CA_WIDTH 5 +#define PHYTMAC_CA_EN_INDEX 18 +#define PHYTMAC_CA_EN_WIDTH 1 +#define PHYTMAC_CB_INDEX 19 +#define PHYTMAC_CB_WIDTH 5 +#define PHYTMAC_CB_EN_INDEX 24 +#define PHYTMAC_CB_EN_WIDTH 1 +#define PHYTMAC_CC_INDEX 25 +#define PHYTMAC_CC_WIDTH 5 +#define PHYTMAC_CC_EN_INDEX 30 +#define PHYTMAC_CC_EN_WIDTH 1 + +/* Bitfields in PHYTMAC_ETHERTYPE */ +#define PHYTMAC_ETHTYPE_VALUE_INDEX 0 +#define PHYTMAC_ETHTYPE_VALUE_WIDTH 16 + +/* Bitfields in PHYTMAC_COMP1 */ +#define PHYTMAC_SPORT_INDEX 0 +#define PHYTMAC_SPORT_WIDTH 16 +#define PHYTMAC_DPORT_INDEX 16 +#define PHYTMAC_DPORTE_WIDTH 16 + +/* Bitfields in PHYTMAC_COMP2 */ +#define PHYTMAC_OFFSET_INDEX 0 +#define PHYTMAC_OFFSET_WIDTH 7 +#define ETHTYPE_SIP_OFFSET 12 +#define ETHTYPE_DIP_OFFSET 16 +#define IPHEAD_SPORT_OFFSET 0 +#define IPHEAD_DPORT_OFFSET 2 +#define PHYTMAC_OFFSET_TYPE_INDEX 7 +#define PHYTMAC_OFFSET_TYPE_WIDTH 2 +#define PHYTMAC_OFFSET_BEGIN 0 +#define PHYTMAC_OFFSET_AFTER_L2HEAD 1 +#define PHYTMAC_OFFSET_AFTER_L3HEAD 2 +#define PHYTMAC_OFFSET_AFTER_L4HEAD 3 +#define PHYTMAC_DIS_MASK_INDEX 9 +#define PHYTMAC_DIS_MASK_WIDTH 1 +#define PHYTMAC_VLAN_ID_INDEX 10 +#define PHYTMAC_VLAN_ID_WIDTH 1 + +#define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) +#define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) +#define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) + +/* rx dma desc bit */ +/* DMA descriptor bitfields */ +#define PHYTMAC_RX_USED_INDEX 0 +#define PHYTMAC_RX_USED_WIDTH 1 +#define PHYTMAC_RX_WRAP_INDEX 1 +#define PHYTMAC_RX_WRAP_WIDTH 1 +#define PHYTMAC_RX_TS_VALID_INDEX 2 +#define PHYTMAC_RX_TS_VALID_WIDTH 1 +#define PHYTMAC_RX_WADDR_INDEX 2 +#define PHYTMAC_RX_WADDR_WIDTH 30 + +#define PHYTMAC_RX_FRMLEN_INDEX 0 +#define PHYTMAC_RX_FRMLEN_WIDTH 12 +#define PHYTMAC_RX_INDEX_INDEX 12 +#define PHYTMAC_RX_INDEX_WIDTH 2 +#define PHYTMAC_RX_SOF_INDEX 14 +#define PHYTMAC_RX_SOF_WIDTH 1 +#define PHYTMAC_RX_EOF_INDEX 15 +#define PHYTMAC_RX_EOF_WIDTH 1 +#define PHYTMAC_RX_CFI_INDEX 16 +#define PHYTMAC_RX_CFI_WIDTH 1 +#define PHYTMAC_RX_VLAN_PRI_INDEX 17 +#define PHYTMAC_RX_VLAN_PRI_WIDTH 3 +#define PHYTMAC_RX_PRI_TAG_INDEX 20 +#define PHYTMAC_RX_PRI_TAG_WIDTH 1 +#define PHYTMAC_RX_VLAN_TAG_INDEX 21 +#define PHYTMAC_RX_VLAN_TAG_WIDTH 1 +#define PHYTMAC_RX_UHASH_MATCH_INDEX 29 +#define PHYTMAC_RX_UHASH_MATCH_WIDTH 1 +#define PHYTMAC_RX_MHASH_MATCH_INDEX 30 +#define PHYTMAC_RX_MHASH_MATCH_WIDTH 1 +#define PHYTMAC_RX_BROADCAST_INDEX 31 +#define PHYTMAC_RX_BROADCAST_WIDTH 1 + +#define PHYTMAC_RX_FRMLEN_MASK 0x1FFF +#define PHYTMAC_RX_JFRMLEN_MASK 0x3FFF + +/* RX checksum offload disabled: bit 24 clear in NCFGR */ +#define PHYTMAC_RX_TYPEID_MATCH_INDEX 22 +#define PHYTMAC_RX_TYPEID_MATCH_WIDTH 2 + +/* RX checksum offload enabled: bit 24 set in NCFGR */ +#define PHYTMAC_RX_CSUM_INDEX 22 +#define PHYTMAC_RX_CSUM_WIDTH 2 + +/* tx dma desc bit */ +#define PHYTMAC_TX_FRMLEN_INDEX 0 +#define PHYTMAC_TX_FRMLEN_WIDTH 14 +#define PHYTMAC_TX_LAST_INDEX 15 +#define PHYTMAC_TX_LAST_WIDTH 1 +#define PHYTMAC_TX_NOCRC_INDEX 16 +#define PHYTMAC_TX_NOCRC_WIDTH 1 +#define PHYTMAC_MSS_MFS_INDEX 16 +#define PHYTMAC_MSS_MFS_WIDTH 14 +#define PHYTMAC_TX_LSO_INDEX 17 +#define PHYTMAC_TX_LSO_WIDTH 2 +#define PHYTMAC_TX_TCP_SEQ_SRC_INDEX 19 +#define PHYTMAC_TX_TCP_SEQ_SRC_WIDTH 1 +#define PHYTMAC_TX_TS_VALID_INDEX 23 +#define PHYTMAC_TX_TS_VALID_WIDTH 1 +#define PHYTMAC_TX_BUF_EXHAUSTED_INDEX 27 +#define PHYTMAC_TX_BUF_EXHAUSTED_WIDTH 1 +#define PHYTMAC_TX_UNDERRUN_INDEX 28 +#define PHYTMAC_TX_UNDERRUN_WIDTH 1 +#define PHYTMAC_TX_ERROR_INDEX 29 +#define PHYTMAC_TX_ERROR_WIDTH 1 +#define PHYTMAC_TX_WRAP_INDEX 30 +#define PHYTMAC_TX_WRAP_WIDTH 1 +#define PHYTMAC_TX_USED_INDEX 31 +#define PHYTMAC_TX_USED_WIDTH 1 + +/* Transmit DMA buffer descriptor Word 4 */ +#define PHYTMAC_DMA_NSEC_INDEX 0 +#define PHYTMAC_DMA_NSEC_WIDTH 30 +#define PHYTMAC_DMA_SECL_INDEX 30 +#define PHYTMAC_DMA_SECL_WIDTH 2 + +/* Transmit DMA buffer descriptor Word 4 */ +#define PHYTMAC_DMA_SECH_INDEX 0 +#define PHYTMAC_DMA_SECH_WIDTH 4 +#define PHYTMAC_DMA_SEC_MASK 0x3f +#define PHYTMAC_DMA_SEC_TOP 0x40 + +/* Buffer descriptor constants */ +#define PHYTMAC_RX_CSUM_NONE 0 +#define PHYTMAC_RX_CSUM_IP_ONLY 1 +#define PHYTMAC_RX_CSUM_IP_TCP 2 +#define PHYTMAC_RX_CSUM_IP_UDP 3 + +/* limit RX checksum offload to TCP and UDP packets */ +#define PHYTMAC_RX_CSUM_CHECKED_MASK 2 + +#endif diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c new file mode 100644 index 00000000000000..df142aa6797f68 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -0,0 +1,1254 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytmac.h" +#include "phytmac_v2.h" + +static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, + u16 cmd_subid, void *data, int len, int wait) +{ + int index = 0; + struct phytmac_msg_info msg; + struct phytmac_msg_info msg_rx; + int ret = 0; + + ++pdata->msg_ring.tx_msg_tail; + if (pdata->msg_ring.tx_msg_tail > pdata->msg_ring.tx_msg_ring_size) + pdata->msg_ring.tx_msg_tail = 1; + index = pdata->msg_ring.tx_msg_tail; + + wait = 1; + memset(&msg, 0, sizeof(msg)); + memset(&msg_rx, 0, sizeof(msg_rx)); + msg.module_id = PHYTMAC_MODULE_ID_GMAC; + msg.cmd_id = cmd_id; + msg.cmd_subid = cmd_subid; + msg.flags = PHYTMAC_FLAGS_MSG_NOINT; + + if (len) + memcpy(&msg.para[0], data, len); + + if (netif_msg_hw(pdata)) { + netdev_info(pdata->ndev, "tx msg: cmdid=%d, subid=%d, flags=%d, len=%d, tail=%d\n", + msg.cmd_id, msg.cmd_subid, msg.flags, len, pdata->msg_ring.tx_msg_tail); + } + + memcpy(pdata->msg_regs + PHYTMAC_MSG(index), &msg, sizeof(msg)); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_MSG_TAIL, + pdata->msg_ring.tx_msg_tail | PHYTMAC_BIT(TX_MSG_INT)); + + if (wait) { + memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); + while (!(msg_rx.flags & 0x1)) { + cpu_relax(); + memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); + } + } + + return ret; +} + +void phytmac_reset_hw(struct phytmac *pdata) +{ + int q; + u16 cmd_id, cmd_subid; + struct phytmac_ring_info ring; + + /* Disable and clear all interrupts and disable queues */ + for (q = 0; q < pdata->queues_max_num; ++q) { + PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(q), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(q), -1); + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), 0); + } + + /* reset hw rx/tx enable */ + cmd_id = PHYTMAC_MSG_CMD_DEFAULT; + cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_HW; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + /* reset tx ring */ + memset(&ring, 0, sizeof(ring)); + ring.queue_num = pdata->queues_max_num; + cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 0); + + /* reset rx ring */ + cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 1); +} + +static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) +{ + int index; + u16 cmd_id, cmd_subid; + struct phytmac_mac para; + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_ADDR; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + index = pdata->msg_ring.tx_msg_tail; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + sizeof(struct phytmac_mac)); + + addr[0] = para.addrl & 0xff; + addr[1] = (para.addrl >> 8) & 0xff; + addr[2] = (para.addrl >> 16) & 0xff; + addr[3] = (para.addrl >> 24) & 0xff; + addr[4] = para.addrh & 0xff; + addr[5] = (para.addrh >> 8) & 0xff; + + return 0; +} + +int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) +{ + u16 cmd_id; + u16 cmd_subid; + struct phytmac_mac para; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ADDR; + para.addrl = cpu_to_le32(*((u32 *)addr)); + para.addrh = cpu_to_le16(*((u16 *)(addr + 4))); + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_init_hw(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + struct phytmac_dma_info dma; + struct phytmac_eth_info eth; + + phytmac_set_mac_addr(pdata, pdata->ndev->dev_addr); + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_JUMBO; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_1536_FRAMES; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + if (pdata->ndev->flags & IFF_PROMISC) { + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + if (pdata->ndev->features & NETIF_F_RXCSUM) { + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + if (!(pdata->ndev->flags & IFF_BROADCAST)) { + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_BC; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } + + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_STRIPCRC; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + + memset(&dma, 0, sizeof(dma)); + cmd_subid = PHYTMAC_MSG_CMD_SET_DMA; + dma.dma_burst_length = pdata->dma_burst_length; + if (pdata->dma_addr_width) + dma.hw_dma_cap |= HW_DMA_CAP_64B; + if (pdata->ndev->features & NETIF_F_HW_CSUM) + dma.hw_dma_cap |= HW_DMA_CAP_CSUM; + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) + dma.hw_dma_cap |= HW_DMA_CAP_PTP; + if (pdata->dma_data_width == PHYTMAC_DBW64) + dma.hw_dma_cap |= HW_DMA_CAP_DDW64; + if (pdata->dma_data_width == PHYTMAC_DBW128) + dma.hw_dma_cap |= HW_DMA_CAP_DDW128; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); + + memset(ð, 0, sizeof(eth)); + cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; + eth.index = 0; + eth.etype = (uint16_t)ETH_P_IP; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)ð, sizeof(eth), 1); + + return 0; +} + +static int phytmac_enable_multicast(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MC; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MC; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + return 0; +} + +static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) +{ + u16 cmd_id, cmd_subid; + struct phytmac_mc_info para; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_HASH_MC; + para.mc_bottom = (u32)mc_filter[0]; + para.mc_top = (u32)mc_filter[1]; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_init_ring_hw(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + struct phytmac_ring_info rxring; + struct phytmac_ring_info txring; + struct phytmac_rxbuf_info rxbuf; + struct phytmac_queue *queue; + u32 q; + + memset(&rxring, 0, sizeof(rxring)); + memset(&txring, 0, sizeof(txring)); + memset(&rxbuf, 0, sizeof(rxbuf)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_TX_RING; + txring.queue_num = pdata->queues_num; + rxring.queue_num = pdata->queues_num; + txring.hw_dma_cap |= HW_DMA_CAP_64B; + rxring.hw_dma_cap |= HW_DMA_CAP_64B; + for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), queue->tx_head); + txring.addr[q] = queue->tx_ring_addr; + rxring.addr[q] = queue->rx_ring_addr; + } + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&txring), sizeof(txring), 0); + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE; + rxbuf.queue_num = pdata->queues_num; + rxbuf.buffer_size = pdata->rx_buffer_len / 64; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxbuf), sizeof(rxbuf), 0); + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_RX_RING; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); + + return 0; +} + +int phytmac_init_msg_ring(struct phytmac *pdata) +{ + u32 size = 0; + + pdata->msg_ring.tx_msg_tail = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_TAIL) & 0xff; + size = PHYTMAC_READ_BITS(pdata, PHYTMAC_SIZE, TXRING_SIZE); + pdata->msg_ring.tx_msg_ring_size = size; + if (pdata->msg_ring.tx_msg_tail == size) + pdata->msg_ring.tx_msg_tail = 0; + + PHYTMAC_WRITE(pdata, PHYTMAC_MSG_IMR, 0xfffffffe); + if (netif_msg_hw(pdata)) + netdev_info(pdata->ndev, "mac msg ring: tx_msg_ring_size=%d, tx_msg_tail=%d\n", + size, pdata->msg_ring.tx_msg_tail); + + return 0; +} + +static int phytmac_get_feature_all(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + int index; + struct phytmac_feature para; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_CAPS; + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + index = pdata->msg_ring.tx_msg_tail; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + sizeof(struct phytmac_feature)); + + pdata->queues_max_num = para.queue_num; + if (para.dma_addr_width) + pdata->dma_addr_width = 64; + else + pdata->dma_addr_width = 32; + pdata->dma_data_width = para.dma_data_width; + pdata->max_rx_fs = para.max_rx_fs; + pdata->tx_bd_prefetch = (2 << (para.tx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); + pdata->rx_bd_prefetch = (2 << (para.rx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); + + if (netif_msg_hw(pdata)) { + netdev_info(pdata->ndev, "feature qnum=%d, daw=%d, dbw=%d, rxfs=%d, rxbd=%d, txbd=%d\n", + pdata->queues_num, pdata->dma_addr_width, pdata->dma_data_width, + pdata->max_rx_fs, pdata->rx_bd_prefetch, pdata->tx_bd_prefetch); + } + + return 0; +} + +void phytmac_get_regs(struct phytmac *pdata, u32 *reg_buff) +{ + u16 cmd_id, cmd_subid; + u16 reg_num; + int index; + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_REG_READ; + reg_num = 16; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)®_num, 2, 1); + + index = pdata->msg_ring.tx_msg_tail; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + + memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); +} + +static void phytmac_get_hw_stats(struct phytmac *pdata) +{ + u16 cmd_id, cmd_subid; + u8 count; + int i, j, index; + u32 stats[48]; + u64 val; + u64 *p = &pdata->stats.tx_octets; + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; + count = 1; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; + count = 2; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); + + cmd_id = PHYTMAC_MSG_CMD_GET; + cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; + count = 3; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); + + for (i = 0; i < 3; i++) { + index = pdata->msg_ring.tx_msg_tail + i - 2; + if (index <= 0) + index += pdata->msg_ring.tx_msg_ring_size; + memcpy(&stats[i * 16], pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); + } + + for (i = 0, j = 0; i < 44; i++) { + if (i == 0 || i == 20) { + val = (u64)stats[i + 1] << 32 | stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } else { + if (i != 1 && i != 21) { + val = stats[i]; + *p += val; + pdata->ethtool_stats[j] = *p; + ++j; + ++p; + } + } + } +} + +static void phytmac_mdio_idle(struct phytmac *pdata) +{ + u32 val; + + /* wait for end of transfer */ + val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); + while (!(val & PHYTMAC_BIT(MIDLE))) { + cpu_relax(); + val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); + } +} + +static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) +{ + u16 data; + + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_READ) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, regnum) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; + phytmac_mdio_idle(pdata); + return data; +} + +static int phytmac_mdio_data_write_c22(struct phytmac *pdata, int mii_id, + int regnum, u16 data) +{ + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_WRITE) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, regnum) + | PHYTMAC_BITS(VALUE, data) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int devad, int regnum) +{ + u16 data; + + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, devad & 0x1F) + | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_READ) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, devad & 0x1F) + | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; + phytmac_mdio_idle(pdata); + return data; +} + +static int phytmac_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, + int regnum, u16 data) +{ + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) + | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_WRITE) + | PHYTMAC_BITS(PHYADDR, mii_id) + | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) + | PHYTMAC_BITS(VALUE, data) + | PHYTMAC_BITS(CONST, 2))); + phytmac_mdio_idle(pdata); + + return 0; +} + +static int phytmac_powerup_hw(struct phytmac *pdata, int on) +{ + u32 status, data0, data1, rdata1; + int ret; + + if (pdata->capacities & PHYTMAC_CAPS_LPI) { + ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh status is busy"); + + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy"); + + data0 = 0; + data0 = PHYTMAC_SET_BITS(data0, DATA0_MSG, PHYTMAC_MSG_PM); + data0 = PHYTMAC_SET_BITS(data0, DATA0_PRO, PHYTMAC_PRO_ID); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA0, data0); + data1 = 0; + + if (on == PHYTMAC_POWERON) { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATON); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } else { + data1 = PHYTMAC_SET_BITS(data1, DATA1_STAT, PHYTMAC_STATOFF); + data1 = PHYTMAC_SET_BITS(data1, DATA1_STATTYPE, PHYTMAC_STATTYPE); + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_CPP_DATA1, data1); + } + + PHYTMAC_MHU_WRITE(pdata, PHYTMAC_MHU_AP_CPP_SET, 1); + ret = readx_poll_timeout(PHYTMAC_READ_DATA0, pdata, data0, + data0 & PHYTMAC_BIT(DATA0_FREE), + 1, PHYTMAC_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mnh data0 is busy\n"); + + rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); + if (rdata1 == data1) + netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + else + netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", + on ? "up" : "down", data1, rdata1); + } + + pdata->power_state = on; + + return 0; +} + +static int phytmac_set_wake(struct phytmac *pdata, int wake) +{ + u16 cmd_id, cmd_subid; + u8 wol = (u8)wake; + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_WOL; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&wol), 1, 1); + + return 0; +} + +static int phytmac_enable_promise(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + u8 rxcsum = 0; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) { + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE; + } else { + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PROMISE; + if (pdata->ndev->features & NETIF_F_RXCSUM) + rxcsum = 1; + } + + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxcsum), 1, 1); + + return 0; +} + +static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_RXCSUM; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_TXCSUM; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_TXCSUM; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_mdio(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MDIO; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_AUTONEG; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + pdata->autoneg = enable; + return 0; +} + +static int phytmac_enable_pause(struct phytmac *pdata, int enable) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PAUSE; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) +{ + u16 cmd_id, cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (enable) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_NETWORK; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_NETWORK; + + phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + + return 0; +} + +static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; + struct phytmac_fdir_info fdir; + u16 cmd_id, cmd_subid; + + memset(&fdir, 0, sizeof(fdir)); + + tp4sp_v = &rx_flow->h_u.tcp_ip4_spec; + tp4sp_m = &rx_flow->m_u.tcp_ip4_spec; + if (tp4sp_m->ip4src == 0xFFFFFFFF) { + fdir.ipsrc_en = true; + fdir.ip4src = tp4sp_v->ip4src; + } + + if (tp4sp_m->ip4dst == 0xFFFFFFFF) { + fdir.ipdst_en = true; + fdir.ip4dst = tp4sp_v->ip4dst; + } + + if (tp4sp_m->psrc == 0xFFFF || tp4sp_m->pdst == 0xFFFF) { + fdir.port_en = true; + fdir.dstport = tp4sp_v->pdst; + fdir.srcport = tp4sp_v->psrc; + fdir.dstport_mask = tp4sp_m->pdst; + fdir.srcport_mask = tp4sp_m->psrc; + } + + fdir.location = rx_flow->location; + fdir.queue = rx_flow->ring_cookie; + + if (fdir.ipsrc_en || fdir.ipdst_en || fdir.port_en) { + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ADD_FDIR; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + } + + return 0; +} + +static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +{ + struct phytmac_fdir_info fdir; + u16 cmd_id, cmd_subid; + + memset(&fdir, 0, sizeof(fdir)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_DEL_FDIR; + fdir.location = (u8)rx_flow->location; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + + return 0; +} + +static void phytmac_tx_start(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); + queue->tx_xmit_more = 0; +} + +static u32 phytmac_get_irq_mask(u32 mask) +{ + u32 value = 0; + + value |= (mask & PHYTMAC_INT_TX_COMPLETE) ? PHYTMAC_BIT(TXCOMP) : 0; + value |= (mask & PHYTMAC_INT_TX_ERR) ? PHYTMAC_BIT(DMA_ERR) : 0; + value |= (mask & PHYTMAC_INT_RX_COMPLETE) ? PHYTMAC_BIT(RXCOMP) : 0; + value |= (mask & PHYTMAC_INT_RX_OVERRUN) ? PHYTMAC_BIT(RXOVERRUN) : 0; + value |= (mask & PHYTMAC_INT_RX_DESC_FULL) ? PHYTMAC_BIT(RUSED) : 0; + + return value; +} + +static u32 phytmac_get_irq_status(u32 value) +{ + u32 status = 0; + + status |= (value & PHYTMAC_BIT(TXCOMP)) ? PHYTMAC_INT_TX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(DMA_ERR)) ? PHYTMAC_INT_TX_ERR : 0; + status |= (value & PHYTMAC_BIT(RXCOMP)) ? PHYTMAC_INT_RX_COMPLETE : 0; + status |= (value & PHYTMAC_BIT(RXOVERRUN)) ? PHYTMAC_INT_RX_OVERRUN : 0; + status |= (value & PHYTMAC_BIT(RUSED)) ? PHYTMAC_INT_RX_DESC_FULL : 0; + + return status; +} + +static void phytmac_enable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_ER(queue_index), value); +} + +static void phytmac_disable_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(queue_index), value); +} + +static void phytmac_clear_irq(struct phytmac *pdata, + int queue_index, u32 mask) +{ + u32 value; + + value = phytmac_get_irq_mask(mask); + PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(queue_index), value); +} + +static unsigned int phytmac_get_irq(struct phytmac *pdata, int queue_index) +{ + u32 status; + u32 value; + + value = PHYTMAC_READ(pdata, PHYTMAC_INT_SR(queue_index)); + status = phytmac_get_irq_status(value); + + return status; +} + +static void phytmac_interface_config(struct phytmac *pdata, unsigned int mode, + const struct phylink_link_state *state) +{ + struct phytmac_interface_info para; + u16 cmd_id, cmd_subid; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_CONFIG; + para.interface = state->interface; + para.autoneg = (mode == MLO_AN_FIXED ? 0 : 1); + para.speed = state->speed; + para.duplex = state->duplex; + pdata->autoneg = para.autoneg; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); +} + +static int phytmac_interface_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + struct phytmac_interface_info para; + u16 cmd_id, cmd_subid; + + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_LINK_CONFIG; + para.interface = interface; + para.duplex = duplex; + para.speed = speed; + para.autoneg = pdata->autoneg; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_interface_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static int phytmac_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) +{ + struct phytmac_interface_info para; + u16 cmd_id, cmd_subid; + + if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) { + memset(¶, 0, sizeof(para)); + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_PCS_LINK_UP; + para.interface = interface; + para.duplex = duplex; + para.speed = speed; + para.autoneg = 0; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + } + + return 0; +} + +static int phytmac_pcs_linkdown(struct phytmac *pdata) +{ + return 0; +} + +static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_2500BASEX) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_NETWORK_STATUS, LINK); + else if (interface == PHY_INTERFACE_MODE_USXGMII || + interface == PHY_INTERFACE_MODE_10GBASER) + return PHYTMAC_READ_BITS(pdata, PHYTMAC_USX_LINK_STATUS, USX_LINK); + + return 0; +} + +static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, + u32 tx_tail, struct packet_info *packet) +{ + unsigned int i, ctrl; + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + struct phytmac_tx_skb *tx_skb; + unsigned int eof = 1; + + i = tx_tail; + + do { + i--; + tx_skb = phytmac_get_tx_skb(queue, i); + desc = phytmac_get_tx_desc(queue, i); + + ctrl = (u32)tx_skb->length; + if (eof) { + ctrl |= PHYTMAC_BIT(TXLAST); + eof = 0; + } + + if (unlikely(i == (pdata->tx_ring_size - 1))) + ctrl |= PHYTMAC_BIT(TXWRAP); + + if (i == queue->tx_tail) { + ctrl |= PHYTMAC_BITS(TXLSO, packet->lso); + ctrl |= PHYTMAC_BITS(TXTCP_SEQ_SRC, packet->seq); + if (packet->nocrc) + ctrl |= PHYTMAC_BIT(TXNOCRC); + } else { + ctrl |= PHYTMAC_BITS(MSSMFS, packet->mss); + } + + desc->desc2 = upper_32_bits(tx_skb->addr); + desc->desc0 = lower_32_bits(tx_skb->addr); + /* make newly desc1 to hardware */ + wmb(); + desc->desc1 = ctrl; + } while (i != queue->tx_tail); + + return 0; +} + +static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, + u32 index) +{ + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + desc->desc1 = 0; + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 |= PHYTMAC_BIT(RXUSED); +} + +static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, dma_addr_t addr) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc; + + desc = phytmac_get_rx_desc(queue, index); + + if (addr) { + if (unlikely(index == (pdata->rx_ring_size - 1))) + addr |= PHYTMAC_BIT(RXWRAP); + desc->desc1 = 0; + desc->desc2 = upper_32_bits(addr); + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 = lower_32_bits(addr); + } else { + desc->desc1 = 0; + /* make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 &= ~PHYTMAC_BIT(RXUSED); + } + + return 0; +} + +static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) +{ + return PHYTMAC_GET_BITS(desc->desc1, TXUSED); +} + +static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +{ + return PHYTMAC_GET_BITS(desc->desc0, RXUSED); +} + +static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) +{ + if (pdata->capacities & PHYTMAC_CAPS_JUMBO) + return desc->desc1 & PHYTMAC_RXJFRMLEN_MASK; + else + return desc->desc1 & PHYTMAC_RXFRMLEN_MASK; +} + +static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) +{ + dma_addr_t addr = 0; + + addr = ((u64)(desc->desc2) << 32); + + addr |= (desc->desc0 & 0xfffffffc); + return addr; +} + +static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + u32 check = value >> PHYTMAC_RXCSUM_INDEX & 0x3; + + return (check == PHYTMAC_RXCSUM_IP_TCP || check == PHYTMAC_RXCSUM_IP_UDP); +} + +static bool phytmac_rx_single_buffer(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return ((value & PHYTMAC_BIT(RXSOF)) && (value & PHYTMAC_BIT(RXEOF))); +} + +static bool phytmac_rx_sof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RXSOF)); +} + +static bool phytmac_rx_eof(const struct phytmac_dma_desc *desc) +{ + u32 value = desc->desc1; + + return (value & PHYTMAC_BIT(RXEOF)); +} + +static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) +{ + unsigned int frag; + unsigned int tmp = end; + struct phytmac_dma_desc *desc; + + if (begin > end) + tmp = end + queue->pdata->rx_ring_size; + + for (frag = begin; frag != end; frag++) { + desc = phytmac_get_rx_desc(queue, frag); + desc->desc0 &= ~PHYTMAC_BIT(RXUSED); + } +} + +static void phytmac_clear_tx_desc(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_dma_desc *desc = NULL; + struct phytmac_tx_skb *tx_skb = NULL; + int i; + + for (i = 0; i < queue->pdata->tx_ring_size; i++) { + desc = phytmac_get_tx_desc(queue, i); + tx_skb = phytmac_get_tx_skb(queue, i); + desc->desc2 = upper_32_bits(tx_skb->addr); + desc->desc0 = lower_32_bits(tx_skb->addr); + /* make newly desc to hardware */ + wmb(); + desc->desc1 = PHYTMAC_BIT(TXUSED); + } + desc->desc1 |= PHYTMAC_BIT(TXWRAP); + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); +} + +static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) +{ + u32 ns, secl, sech; + + ns = PHYTMAC_READ(pdata, PHYTMAC_TIMER_NSEC); + secl = PHYTMAC_READ(pdata, PHYTMAC_TIMER_SEC); + sech = PHYTMAC_READ(pdata, PHYTMAC_TIMER_MSB_SEC); + + ts->tv_nsec = ns; + ts->tv_sec = (((u64)sech << 32) | secl) & TIMER_SEC_MAX_VAL; +} + +void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) +{ + u32 secl, sech; + + secl = (u32)sec; + sech = (sec >> 32) & (0xffff); + + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_NSEC, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_MSB_SEC, sech); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_SEC, secl); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_NSEC, nsec); +} + +void phytmac_clear_time(struct phytmac *pdata) +{ + u32 value; + + pdata->ts_incr.sub_ns = 0; + pdata->ts_incr.ns = 0; + + value = PHYTMAC_READ(pdata, PHYTMAC_TIMER_INCR); + value = PHYTMAC_SET_BITS(value, INCR_NSEC, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR, value); + + value = PHYTMAC_READ(pdata, PHYTMAC_TIMER_INCR_SUB_NSEC); + value = PHYTMAC_SET_BITS(value, INCR_SNSEC, 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR_SUB_NSEC, value); + + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, 0); +} + +int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) +{ + u16 cmd_id, cmd_subid; + struct phytmac_ts_config para; + + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_TS_CONFIG; + para.tx_mode = ctrl->tx_control; + para.rx_mode = ctrl->rx_control; + para.one_step = ctrl->one_step; + phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) +{ + u32 value; + + value = PHYTMAC_BITS(INCR_SNSEC, incr->sub_ns); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR_SUB_NSEC, value); + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_INCR, incr->ns); + + return 0; +} + +static void phytmac_ptp_init_hw(struct phytmac *pdata) +{ + struct timespec64 ts; + + ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); + phytmac_set_time(pdata, ts.tv_sec, ts.tv_nsec); + + phytmac_set_tsincr(pdata, &pdata->ts_incr); +} + +static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) +{ + struct ts_incr ts_incr; + u32 tmp; + u64 adj; + + ts_incr.ns = pdata->ts_incr.ns; + ts_incr.sub_ns = pdata->ts_incr.sub_ns; + + /* scaling: unused(8bit) | ns(8bit) | fractions(16bit) */ + tmp = ((u64)ts_incr.ns << PHYTMAC_INCR_SNSECL_INDEX) + ts_incr.sub_ns; + adj = ((u64)ppm * tmp + (USEC_PER_SEC >> 1)) >> PHYTMAC_INCR_SNSECL_INDEX; + + adj = div_u64(adj, USEC_PER_SEC); + adj = negative ? (tmp - adj) : (tmp + adj); + + ts_incr.ns = (adj >> PHYTMAC_INCR_SNSEC_WIDTH) + & ((1 << PHYTMAC_INCR_NSEC_WIDTH) - 1); + ts_incr.sub_ns = adj & ((1 << PHYTMAC_INCR_SNSEC_WIDTH) - 1); + + phytmac_set_tsincr(pdata, &ts_incr); + + return 0; +} + +int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) +{ + u32 adj; + + if (delta > PHYTMAC_ASEC_MAX) { + struct timespec64 now, then; + + then = ns_to_timespec64(delta); + phytmac_get_time(pdata, &now); + now = timespec64_add(now, then); + phytmac_set_time(pdata, now.tv_sec, now.tv_nsec); + } else { + adj = (neg << PHYTMAC_AADD_INDEX) | delta; + PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, adj); + } + + return 0; +} + +static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) +{ + int ts_valid = 0; + + if (direction == PHYTMAC_TX) + ts_valid = desc->desc1 & PHYTMAC_BIT(TXTSVALID); + else if (direction == PHYTMAC_RX) + ts_valid = desc->desc0 & PHYTMAC_BIT(RXTSVALID); + return ts_valid; +} + +static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) +{ + struct timespec64 ts2; + + ts->tv_sec = (PHYTMAC_GET_BITS(ts_2, TS_SECH) << 2) | + PHYTMAC_GET_BITS(ts_1, TS_SECL); + ts->tv_nsec = PHYTMAC_GET_BITS(ts_1, TS_NSEC); + + phytmac_get_time(pdata, &ts2); + + if (((ts->tv_sec ^ ts2.tv_sec) & (PHYTMAC_TS_SEC_TOP >> 1)) != 0) + ts->tv_sec -= PHYTMAC_TS_SEC_TOP; + + ts->tv_sec += (ts2.tv_sec & (~PHYTMAC_TS_SEC_MASK)); +} + +static unsigned int phytmac_get_ts_rate(struct phytmac *pdata) +{ + return 300000000; +} + +struct phytmac_hw_if phytmac_2p0_hw = { + .init_msg_ring = phytmac_init_msg_ring, + .reset_hw = phytmac_reset_hw, + .init_hw = phytmac_init_hw, + .init_ring_hw = phytmac_init_ring_hw, + .get_feature = phytmac_get_feature_all, + .get_regs = phytmac_get_regs, + .get_stats = phytmac_get_hw_stats, + .set_mac_address = phytmac_set_mac_addr, + .get_mac_address = phytmac_get_mac_addr, + .mdio_read = phytmac_mdio_data_read_c22, + .mdio_write = phytmac_mdio_data_write_c22, + .mdio_read_c45 = phytmac_mdio_data_read_c45, + .mdio_write_c45 = phytmac_mdio_data_write_c45, + .poweron = phytmac_powerup_hw, + .set_wol = phytmac_set_wake, + .enable_promise = phytmac_enable_promise, + .enable_multicast = phytmac_enable_multicast, + .set_hash_table = phytmac_set_mc_hash, + .enable_rx_csum = phytmac_enable_rxcsum, + .enable_tx_csum = phytmac_enable_txcsum, + .enable_mdio_control = phytmac_enable_mdio, + .enable_autoneg = phytmac_enable_autoneg, + .enable_pause = phytmac_enable_pause, + .enable_network = phytmac_enable_network, + .add_fdir_entry = phytmac_add_fdir_entry, + .del_fdir_entry = phytmac_del_fdir_entry, + + /* mac config */ + .mac_config = phytmac_interface_config, + .mac_linkup = phytmac_interface_linkup, + .mac_linkdown = phytmac_interface_linkdown, + .pcs_linkup = phytmac_pcs_linkup, + .pcs_linkdown = phytmac_pcs_linkdown, + .get_link = phytmac_pcs_get_link, + + /* irq */ + .enable_irq = phytmac_enable_irq, + .disable_irq = phytmac_disable_irq, + .clear_irq = phytmac_clear_irq, + .get_irq = phytmac_get_irq, + + /* tx and rx */ + .tx_map = phytmac_tx_map_desc, + .transmit = phytmac_tx_start, + .tx_complete = phytmac_tx_complete, + .rx_complete = phytmac_rx_complete, + .get_rx_pkt_len = phytmac_rx_pkt_len, + .get_desc_addr = phytmac_get_desc_addr, + .init_rx_map = phytmac_init_rx_map_desc, + .rx_map = phytmac_rx_map_desc, + .rx_checksum = phytmac_rx_checksum, + .rx_single_buffer = phytmac_rx_single_buffer, + .rx_pkt_start = phytmac_rx_sof, + .rx_pkt_end = phytmac_rx_eof, + .clear_rx_desc = phytmac_clear_rx_desc, + .clear_tx_desc = phytmac_clear_tx_desc, + + /* ptp */ + .init_ts_hw = phytmac_ptp_init_hw, + .set_time = phytmac_set_time, + .clear_time = phytmac_clear_time, + .get_time = phytmac_get_time, + .set_ts_config = phytmac_set_tsmode, + .set_incr = phytmac_set_tsincr, + .adjust_fine = phytmac_adjust_fine, + .adjust_time = phytmac_adjust_time, + .ts_valid = phytmac_ts_valid, + .get_timestamp = phytmac_get_dma_ts, + .get_ts_rate = phytmac_get_ts_rate, +}; +EXPORT_SYMBOL_GPL(phytmac_2p0_hw); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h new file mode 100644 index 00000000000000..92e4806ac2c1f7 --- /dev/null +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _PHYTMAC_V2_H +#define _PHYTMAC_V2_H + +extern struct phytmac_hw_if phytmac_2p0_hw; + +#define PHYTMAC_MSG_SRAM_SIZE 4096 +#define MSG_HDR_LEN 8 + +#define PHYTMAC_TX_MSG_HEAD 0x000 +#define PHYTMAC_TX_MSG_TAIL 0x004 +#define PHYTMAC_RX_MSG_HEAD 0x008 +#define PHYTMAC_RX_MSG_TAIL 0x00c +#define PHYTMAC_MSG_IMR 0x020 +#define PHYTMAC_MSG_ISR 0x02c + +#define PHYTMAC_SIZE 0x0048 +#define PHYTMAC_NETWORK_STATUS 0x0240 +#define PHYTMAC_PCS_AN_LP 0x0244 +#define PHYTMAC_USX_LINK_STATUS 0x0248 +#define PHYTMAC_MDIO 0x0264 +#define PHYTMAC_TIMER_INCR_SUB_NSEC 0x024c +#define PHYTMAC_TIMER_INCR 0x0250 +#define PHYTMAC_TIMER_MSB_SEC 0x0254 +#define PHYTMAC_TIMER_SEC 0x0258 +#define PHYTMAC_TIMER_NSEC 0x025c +#define PHYTMAC_TIMER_ADJUST 0x0260 +#define PHYTMAC_MSG(i) (((i) - 1) * 0x48) + +#define PHYTMAC_MODULE_ID_GMAC 0x60 +#define PHYTMAC_FLAGS_MSG_COMP 0x1 +#define PHYTMAC_FLAGS_MSG_NOINT 0x2 + +/* Bitfields in PHYTMAC_TX_MSG_TAIL */ +#define PHYTMAC_TX_MSG_INT_INDEX 16 +#define PHYTMAC_TX_MSG_INT_WIDTH 1 + +/* Bitfields in PHYTMAC_MSG_ISR */ +#define PHYTMAC_MSG_COMPLETE_INDEX 0 +#define PHYTMAC_MSG_COMPLETE_WIDTH 1 + +/* Bitfields in PHYTMAC_SIZE */ +#define PHYTMAC_MEM_SIZE_INDEX 0 +#define PHYTMAC_MEM_SIZE_WIDTH 4 +#define PHYTMAC_TXRING_SIZE_INDEX 8 +#define PHYTMAC_TXRING_SIZE_WIDTH 6 + +/* Bitfields in PHYTMAC_TIMER_INCR_SUB_NSEC */ +#define PHYTMAC_INCR_SNSECH_INDEX 0 +#define PHYTMAC_INCR_SNSECH_WIDTH 16 +#define PHYTMAC_INCR_SNSECL_INDEX 24 +#define PHYTMAC_INCR_SNSECL_WIDTH 8 +#define PHYTMAC_INCR_SNSEC_WIDTH 24 + +/* Bitfields in PHYTMAC_TIMER_INCR_SUB_NSEC */ +#define PHYTMAC_INCR_SNSEC_INDEX 0 +#define PHYTMAC_INCR_SNSEC_WIDTH 24 + +/* Bitfields in PHYTMAC_TIMER_INCR */ +#define PHYTMAC_INCR_NSEC_INDEX 0 +#define PHYTMAC_INCR_NSEC_WIDTH 8 + +/* Bitfields in PHYTMAC_TIMER_MSB_SEC */ +#define PHYTMAC_TIMER_SECH_INDEX 0 +#define PHYTMAC_TIMER_SECH_WIDTH 16 + +/* Bitfields in PHYTMAC_TIMER_SEC */ +#define PHYTMAC_TIMER_SECL_INDEX 0 +#define PHYTMAC_TIMER_SECL_WIDTH 32 + +/* Bitfields in PHYTMAC_TIMER_NSEC */ +#define PHYTMAC_TIMER_NSEC_INDEX 0 +#define PHYTMAC_TIMER_NSEC_WIDTH 30 + +/* Bitfields in PHYTMAC_TIMER_ADJUST */ +#define PHYTMAC_ASEC_INDEX 0 +#define PHYTMAC_ASEC_WIDTH 30 +#define PHYTMAC_AADD_INDEX 31 +#define PHYTMAC_AADD_WIDTH 1 +#define PHYTMAC_ASEC_MAX ((1 << PHYTMAC_ASEC_WIDTH) - 1) + +#define PHYTMAC_TIMER_SEC_WIDTH (PHYTMAC_TIMER_SECH_WIDTH + PHYTMAC_TIMER_SECL_WIDTH) +#define TIMER_SEC_MAX_VAL (((u64)1 << PHYTMAC_TIMER_SEC_WIDTH) - 1) +#define TIMER_NSEC_MAX_VAL ((1 << PHYTMAC_TIMER_NSEC_WIDTH) - 1) + +#define PHYTMAC_TAIL_PTR(i) (0x0100 + ((i) * 4)) +#define PHYTMAC_INT_ER(i) (0x0140 + ((i) * 4)) +#define PHYTMAC_INT_DR(i) (0x0180 + ((i) * 4)) +#define PHYTMAC_INT_MR(i) (0x01c0 + ((i) * 4)) +#define PHYTMAC_INT_SR(i) (0x0200 + ((i) * 4)) + +#define PHYTMAC_LINK_INDEX 0 /* PCS link status */ +#define PHYTMAC_LINK_WIDTH 1 +#define PHYTMAC_MIDLE_INDEX 2 /* Mdio idle */ +#define PHYTMAC_MIDLE_WIDTH 1 + +/* Int stauts/Enable/Disable/Mask Register */ +#define PHYTMAC_RXCOMP_INDEX 1 /* Rx complete */ +#define PHYTMAC_RXCOMP_WIDTH 1 +#define PHYTMAC_RUSED_INDEX 2 /* Rx used bit read */ +#define PHYTMAC_RUSED_WIDTH 1 +#define PHYTMAC_DMA_ERR_INDEX 6 /* AMBA error */ +#define PHYTMAC_DMA_ERR_WIDTH 1 +#define PHYTMAC_TXCOMP_INDEX 7 /* Tx complete */ +#define PHYTMAC_TXCOMP_WIDTH 1 +#define PHYTMAC_RXOVERRUN_INDEX 10 /* Rx overrun */ +#define PHYTMAC_RXOVERRUN_WIDTH 1 +#define PHYTMAC_RESP_ERR_INDEX 11 /* Resp not ok */ +#define PHYTMAC_RESP_ERR_WIDTH 1 + +/* pcs an lp */ +#define PHYTMAC_AUTO_NEG_INDEX 12 +#define PHYTMAC_AUTO_NEG_WIDTH 1 + +/* Bitfields in USX_STATUS. */ +#define PHYTMAC_USX_LINK_INDEX 0 +#define PHYTMAC_USX_LINK_WIDTH 1 + +/* Mdio read/write Register */ +#define PHYTMAC_VALUE_INDEX 0 /* value */ +#define PHYTMAC_VALUE_WIDTH 16 +#define PHYTMAC_CONST_INDEX 16 /* Must Be 10 */ +#define PHYTMAC_CONST_WIDTH 2 +#define PHYTMAC_REGADDR_INDEX 18 /* Register address */ +#define PHYTMAC_REGADDR_WIDTH 5 +#define PHYTMAC_PHYADDR_INDEX 23 /* Phy address */ +#define PHYTMAC_PHYADDR_WIDTH 5 +#define PHYTMAC_MDCOPS_INDEX 28 +#define PHYTMAC_MDCOPS_WIDTH 2 +#define PHYTMAC_CLAUSESEL_INDEX 30 +#define PHYTMAC_CLAUSESEL_WIDTH 1 +#define PHYTMAC_C22 1 +#define PHYTMAC_C45 0 +#define PHYTMAC_C45_ADDR 0 +#define PHYTMAC_C45_WRITE 1 +#define PHYTMAC_C45_READ 3 +#define PHYTMAC_C22_WRITE 1 +#define PHYTMAC_C22_READ 2 + +/* rx dma desc bit */ +/* DMA descriptor bitfields */ +#define PHYTMAC_RXUSED_INDEX 0 +#define PHYTMAC_RXUSED_WIDTH 1 +#define PHYTMAC_RXWRAP_INDEX 1 +#define PHYTMAC_RXWRAP_WIDTH 1 +#define PHYTMAC_RXTSVALID_INDEX 2 +#define PHYTMAC_RXTSVALID_WIDTH 1 +#define PHYTMAC_RXWADDR_INDEX 2 +#define PHYTMAC_RXWADDR_WIDTH 30 + +#define PHYTMAC_RXFRMLEN_INDEX 0 +#define PHYTMAC_RXFRMLEN_WIDTH 12 +#define PHYTMAC_RXINDEX_INDEX 12 +#define PHYTMAC_RXINDEX_WIDTH 2 +#define PHYTMAC_RXSOF_INDEX 14 +#define PHYTMAC_RXSOF_WIDTH 1 +#define PHYTMAC_RXEOF_INDEX 15 +#define PHYTMAC_RXEOF_WIDTH 1 + +#define PHYTMAC_RXFRMLEN_MASK 0x1FFF +#define PHYTMAC_RXJFRMLEN_MASK 0x3FFF + +#define PHYTMAC_RXTYPEID_MATCH_INDEX 22 +#define PHYTMAC_RXTYPEID_MATCH_WIDTH 2 +#define PHYTMAC_RXCSUM_INDEX 22 +#define PHYTMAC_RXCSUM_WIDTH 2 + +/* Buffer descriptor constants */ +#define PHYTMAC_RXCSUM_NONE 0 +#define PHYTMAC_RXCSUM_IP 1 +#define PHYTMAC_RXCSUM_IP_TCP 2 +#define PHYTMAC_RXCSUM_IP_UDP 3 + +#define PHYTMAC_TXFRMLEN_INDEX 0 +#define PHYTMAC_TXFRMLEN_WIDTH 14 +#define PHYTMAC_TXLAST_INDEX 15 +#define PHYTMAC_TXLAST_WIDTH 1 +#define PHYTMAC_TXNOCRC_INDEX 16 +#define PHYTMAC_TXNOCRC_WIDTH 1 +#define PHYTMAC_MSSMFS_INDEX 16 +#define PHYTMAC_MSSMFS_WIDTH 14 +#define PHYTMAC_TXLSO_INDEX 17 +#define PHYTMAC_TXLSO_WIDTH 2 +#define PHYTMAC_TXTCP_SEQ_SRC_INDEX 19 +#define PHYTMAC_TXTCP_SEQ_SRC_WIDTH 1 +#define PHYTMAC_TXTSVALID_INDEX 23 +#define PHYTMAC_TXTSVALID_WIDTH 1 +#define PHYTMAC_TXWRAP_INDEX 30 +#define PHYTMAC_TXWRAP_WIDTH 1 +#define PHYTMAC_TXUSED_INDEX 31 +#define PHYTMAC_TXUSED_WIDTH 1 + +/* dma ts */ +#define PHYTMAC_TS_NSEC_INDEX 0 +#define PHYTMAC_TS_NSEC_WIDTH 30 +#define PHYTMAC_TS_SECL_INDEX 30 +#define PHYTMAC_TS_SECL_WIDTH 2 +#define PHYTMAC_TS_SECH_INDEX 0 +#define PHYTMAC_TS_SECH_WIDTH 4 +#define PHYTMAC_TS_SEC_MASK 0x3f +#define PHYTMAC_TS_SEC_TOP 0x40 + +#define HW_DMA_CAP_64B 0x1 +#define HW_DMA_CAP_CSUM 0x2 +#define HW_DMA_CAP_PTP 0x4 +#define HW_DMA_CAP_DDW64 0x8 +#define HW_DMA_CAP_DDW128 0x10 + +#define PHYTMAC_DBW64 2 +#define PHYTMAC_DBW128 4 + +enum phytmac_msg_cmd_id { + PHYTMAC_MSG_CMD_DEFAULT = 0, + PHYTMAC_MSG_CMD_SET, + PHYTMAC_MSG_CMD_GET, + PHYTMAC_MSG_CMD_DATA, + PHYTMAC_MSG_CMD_REPORT, +}; + +enum phytmac_default_subid { + PHYTMAC_MSG_CMD_DEFAULT_RESET_HW = 0, + PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE, + PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE, +}; + +enum phytmac_set_subid { + PHYTMAC_MSG_CMD_SET_INIT_ALL = 0, + PHYTMAC_MSG_CMD_SET_INIT_RING = 1, + PHYTMAC_MSG_CMD_SET_INIT_TX_RING = 2, + PHYTMAC_MSG_CMD_SET_INIT_RX_RING = 3, + PHYTMAC_MSG_CMD_SET_MAC_CONFIG = 4, + PHYTMAC_MSG_CMD_SET_ADDR = 5, + PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE = 6, + PHYTMAC_MSG_CMD_SET_DMA = 7, + PHYTMAC_MSG_CMD_SET_CAPS = 8, + PHYTMAC_MSG_CMD_SET_TS_CONFIG = 9, + PHYTMAC_MSG_CMD_SET_INIT_TX_ENABLE_TRANSMIT = 10, + PHYTMAC_MSG_CMD_SET_INIT_RX_ENABLE_RECEIVE = 11, + PHYTMAC_MSG_CMD_SET_ENABLE_NETWORK = 12, + PHYTMAC_MSG_CMD_SET_DISABLE_NETWORK = 13, + PHYTMAC_MSG_CMD_SET_ENABLE_MDIO = 14, + PHYTMAC_MSG_CMD_SET_DISABLE_MDIO = 15, + PHYTMAC_MSG_CMD_SET_ENABLE_TXCSUM = 16, + PHYTMAC_MSG_CMD_SET_DISABLE_TXCSUM = 17, + PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM = 18, + PHYTMAC_MSG_CMD_SET_DISABLE_RXCSUM = 19, + PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE = 20, + PHYTMAC_MSG_CMD_SET_DISABLE_PROMISE = 21, + PHYTMAC_MSG_CMD_SET_ENABLE_MC = 22, + PHYTMAC_MSG_CMD_SET_DISABLE_MC = 23, + PHYTMAC_MSG_CMD_SET_ENABLE_HASH_MC = 24, + PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE = 25, + PHYTMAC_MSG_CMD_SET_DISABLE_PAUSE = 26, + PHYTMAC_MSG_CMD_SET_ENABLE_JUMBO = 27, + PHYTMAC_MSG_CMD_SET_DISABLE_JUMBO = 28, + PHYTMAC_MSG_CMD_SET_ENABLE_1536_FRAMES = 29, + PHYTMAC_MSG_CMD_SET_ENABLE_STRIPCRC = 30, + PHYTMAC_MSG_CMD_SET_DISABLE_STRIPCRC = 31, + PHYTMAC_MSG_CMD_SET_PCS_LINK_UP = 32, + PHYTMAC_MSG_CMD_SET_PCS_LINK_DOWN = 33, + PHYTMAC_MSG_CMD_SET_MAC_LINK_CONFIG = 34, + PHYTMAC_MSG_CMD_SET_REG_WRITE = 35, + PHYTMAC_MSG_CMD_SET_ENABLE_BC = 36, + PHYTMAC_MSG_CMD_SET_DISABLE_BC = 37, + PHYTMAC_MSG_CMD_SET_ETH_MATCH = 38, + PHYTMAC_MSG_CMD_SET_ADD_FDIR = 39, + PHYTMAC_MSG_CMD_SET_DEL_FDIR = 40, + PHYTMAC_MSG_CMD_SET_ENABLE_AUTONEG = 41, + PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG = 42, + PHYTMAC_MSG_CMD_SET_RX_DATA_OFFSET = 43, + PHYTMAC_MSG_CMD_SET_WOL = 44, +}; + +enum phytmac_get_subid { + PHYTMAC_MSG_CMD_GET_ADDR, + PHYTMAC_MSG_CMD_GET_QUEUENUMS, + PHYTMAC_MSG_CMD_GET_CAPS, + PHYTMAC_MSG_CMD_GET_BD_PREFETCH, + PHYTMAC_MSG_CMD_GET_STATS, + PHYTMAC_MSG_CMD_GET_REG_READ, +}; + +struct phytmac_interface_info { + u8 interface; + u8 autoneg; + u16 duplex; + u32 speed; +} __packed; + +struct phytmac_mc_info { + u32 mc_bottom; + u32 mc_top; +} __packed; + +struct phytmac_fdir_info { + u32 ip4src; + u32 ip4dst; + u16 srcport; + u16 srcport_mask; + u16 dstport; + u16 dstport_mask; + u8 location; + u8 queue; + u8 ipsrc_en; + u8 ipdst_en; + u8 port_en; +} __packed; + +struct phytmac_ts_config { + u8 tx_mode; + u8 rx_mode; + u8 one_step; +} __packed; + +struct phytmac_ring_info { + u64 addr[4]; + u8 queue_num; + u8 hw_dma_cap; +} __packed; + +struct phytmac_rxbuf_info { + u8 queue_num; + u8 buffer_size; +} __packed; + +struct phytmac_dma_info { + u16 dma_burst_length; + u8 hw_dma_cap; +} __packed; + +struct phytmac_eth_info { + u16 index; + u16 etype; +} __packed; + +struct phytmac_mac { + u32 addrl; + u16 addrh; +} __packed; + +struct phytmac_feature { + u8 irq_read_clear; + u8 dma_data_width; + u8 dma_addr_width; + u8 tx_pkt_buffer; + u8 rx_pkt_buffer; + u8 pbuf_lso; + u8 queue_num; + u8 tx_bd_prefetch; + u8 rx_bd_prefetch; + u8 max_rx_fs; +} __packed; + +struct phytmac_msg_info { + u16 module_id; + u16 cmd_id; + u16 cmd_subid; + u16 flags; + u8 para[64]; +} __packed; + +#endif From d255a1f8c83a1075a650d5ae6ebab2ac6b68bede Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 29 Mar 2024 16:45:04 +0800 Subject: [PATCH 028/258] PHYTIUM: net/phytmac: Bugfixed the MDIO registration failures When MDIO registration fails, it will double free netdev, which will cause the system to crash. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I2605bc0ac4554c81b18e7874a68409e0a535da7a Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/593ac038cf31fbf629ed1c84176c349976e31272 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 31 +++++++++++++-------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index b0977f5cae755c..da4e0673628031 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1508,7 +1508,7 @@ int phytmac_mdio_register(struct phytmac *pdata) pdata->mii_bus = mdiobus_alloc(); if (!pdata->mii_bus) { ret = -ENOMEM; - goto free_mdio; + goto err_out; } pdata->mii_bus->name = "phytmac_mii_bus"; @@ -1519,7 +1519,7 @@ int phytmac_mdio_register(struct phytmac *pdata) if (pdata->platdev) { snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", - pdata->mii_bus->name, netdev_name(pdata->ndev)); + pdata->mii_bus->name, pdata->platdev->name); } else if (pdata->pcidev) { snprintf(pdata->mii_bus->id, MII_BUS_ID_SIZE, "%s-%s", pdata->mii_bus->name, pci_name(pdata->pcidev)); @@ -1536,6 +1536,9 @@ int phytmac_mdio_register(struct phytmac *pdata) return mdiobus_register(pdata->mii_bus); free_mdio: mdiobus_free(pdata->mii_bus); + pdata->mii_bus = NULL; + +err_out: return ret; } @@ -2043,30 +2046,26 @@ int phytmac_drv_probe(struct phytmac *pdata) ret = phytmac_init(pdata); if (ret) - goto err_out_free_netdev; + goto err_out; if (pdata->use_ncsi) { pdata->ncsidev = ncsi_register_dev(ndev, phytmac_ncsi_handler); if (!pdata->ncsidev) - goto err_out_free_netdev; + goto err_out; } netif_carrier_off(ndev); ret = register_netdev(ndev); if (ret) { dev_err(pdata->dev, "Cannot register net device, aborting.\n"); - goto err_out_free_netdev; + goto err_out; } - if (netif_msg_probe(pdata)) - dev_dbg(pdata->dev, "probe success!Phytium %s at 0x%08lx irq %d (%pM)\n", - "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); - if (pdata->use_mii && !pdata->mii_bus) { ret = phytmac_mdio_register(pdata); if (ret) { netdev_err(ndev, "MDIO bus registration failed\n"); - goto err_phylink_init; + goto err_out_free_mdiobus; } } @@ -2076,15 +2075,23 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_phylink_init; } + if (netif_msg_probe(pdata)) + dev_dbg(pdata->dev, "probe successfully! Phytium %s at 0x%08lx irq %d (%pM)\n", + "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); + return 0; err_phylink_init: if (pdata->mii_bus) mdiobus_unregister(pdata->mii_bus); -err_out_free_netdev: - free_netdev(ndev); +err_out_free_mdiobus: + if (pdata->mii_bus) + mdiobus_free(pdata->mii_bus); + + unregister_netdev(ndev); +err_out: return ret; } EXPORT_SYMBOL_GPL(phytmac_drv_probe); From 9b354a042a40b4e13fbd322758a21ddb3ec654ce Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 29 Mar 2024 16:46:54 +0800 Subject: [PATCH 029/258] PHYTIUM: net/phytmac: Fixed the issue of pxe startup failure. When network driver supports broadcast mode, no_broadcast bit of the network config register needs to be set to 0. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I4721a61da370130dc6dce608448681b3fe8298d3 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/82a2fb0b2765a621ca2617c2471856264f909332 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_v1.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 2d9f67c82050c9..79df9a7f981f1c 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -357,8 +357,11 @@ static int phytmac_init_hw(struct phytmac *pdata) config |= PHYTMAC_BIT(PROMISC); if (pdata->ndev->features & NETIF_F_RXCSUM) config |= PHYTMAC_BIT(RCO_EN); - if (!(pdata->ndev->flags & IFF_BROADCAST)) + if (pdata->ndev->flags & IFF_BROADCAST) + config &= ~PHYTMAC_BIT(NO_BCAST); + else config |= PHYTMAC_BIT(NO_BCAST); + /* pause enable */ config |= PHYTMAC_BIT(PAUSE_EN); /* Rx Fcs remove */ From 6cff9c7d8daf8d1817b814b85c326f8ed88af697 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 29 Mar 2024 16:49:17 +0800 Subject: [PATCH 030/258] PHYTIUM: net/phytmac: Adapt interface type 1000BASE-x Added 1000BASE-x interface type support for phymac driver. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I7f950dad763afd4814c73e0ca67d08168d5b2cb8 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/b590f0e74c19a7da0dd49e0777600cb39d50bf84 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 1 + drivers/net/ethernet/phytium/phytmac_v1.c | 35 ++++++++++++++++++--- drivers/net/ethernet/phytium/phytmac_v1.h | 2 ++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index da4e0673628031..c172103a973628 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1560,6 +1560,7 @@ static void phytmac_validate(struct phylink_config *config, struct phytmac *pdata = netdev_priv(ndev); if (state->interface != PHY_INTERFACE_MODE_SGMII && + state->interface != PHY_INTERFACE_MODE_1000BASEX && state->interface != PHY_INTERFACE_MODE_2500BASEX && state->interface != PHY_INTERFACE_MODE_5GBASER && state->interface != PHY_INTERFACE_MODE_10GBASER && diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 79df9a7f981f1c..ec95c6c79b0668 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -161,6 +161,20 @@ static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) return 0; } +static int phytmac_pcs_software_reset(struct phytmac *pdata, int reset) +{ + u32 value = PHYTMAC_READ(pdata, PHYTMAC_PCSCTRL); + + if (reset) + value |= PHYTMAC_BIT(PCS_RESET); + else + value &= ~PHYTMAC_BIT(PCS_RESET); + + PHYTMAC_WRITE(pdata, PHYTMAC_PCSCTRL, value); + + return 0; +} + static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, int speed, int duplex) { @@ -401,6 +415,10 @@ static int phytmac_init_hw(struct phytmac *pdata) if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, 0x80000001); + + if (phy_interface_mode_is_8023z(pdata->phy_interface)) + phytmac_pcs_software_reset(pdata, 1); + return 0; } @@ -1047,9 +1065,16 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod | PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD) | PHYTMAC_BIT(GM_EN)); ctrl &= ~(PHYTMAC_BIT(HIGHSPEED) | PHYTMAC_BIT(2PT5G)); - if (state->interface == PHY_INTERFACE_MODE_SGMII || - state->interface == PHY_INTERFACE_MODE_2500BASEX) { + if (state->interface == PHY_INTERFACE_MODE_SGMII) { config |= PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN); + if (state->speed == SPEED_1000) + config |= PHYTMAC_BIT(GM_EN); + else if (state->speed == SPEED_2500) + config |= PHYTMAC_BIT(2PT5G); + } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { + config |= PHYTMAC_BIT(PCS_EN) | PHYTMAC_BIT(GM_EN); + } else if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { + config |= PHYTMAC_BIT(2PT5G) | PHYTMAC_BIT(PCS_EN); } else if (state->interface == PHY_INTERFACE_MODE_10GBASER || state->interface == PHY_INTERFACE_MODE_USXGMII || state->interface == PHY_INTERFACE_MODE_5GBASER) { @@ -1078,15 +1103,17 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); /* Disable AN for SGMII fixed link configuration, enable otherwise.*/ - if (state->interface == PHY_INTERFACE_MODE_SGMII || - state->interface == PHY_INTERFACE_MODE_2500BASEX) + if (state->interface == PHY_INTERFACE_MODE_SGMII) phytmac_enable_autoneg(pdata, mode == MLO_AN_FIXED ? 0 : 1); + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + phytmac_enable_autoneg(pdata, 1); } static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) { if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX || interface == PHY_INTERFACE_MODE_2500BASEX) return PHYTMAC_READ_BITS(pdata, PHYTMAC_NSTATUS, PCS_LINK); else if (interface == PHY_INTERFACE_MODE_USXGMII || diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index 6f2b521aaebdbb..d8de2c26cab4b2 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -228,6 +228,8 @@ extern struct phytmac_hw_if phytmac_1p0_hw; /* PCSCTRL register */ #define PHYTMAC_AUTONEG_INDEX 12 #define PHYTMAC_AUTONEG_WIDTH 1 +#define PHYTMAC_PCS_RESET_INDEX 15 +#define PHYTMAC_PCS_RESET_WIDTH 1 /* DEFAULT1 register */ #define PHYTMAC_DBW_INDEX 25 From 3e4dff7aedebd8cd52e7bfea51aaf8b4e8e8e499 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 14:33:32 +0800 Subject: [PATCH 031/258] PHYTIUM: net/phytmac:Added high address check A maximum of three DMA ring addresses can be allocated, and check whether the high addresses in multiple queues are consistent after the address is assigned. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ifd4d5c486341e1ed4cc1e433799bc53d15647f15 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/1ed603cd93296e432c6322f6fac2d968170eafd0 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 103 ++++++++++++++------ 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index c172103a973628..07f8476946bc60 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -46,6 +46,7 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); #define RX_BUFFER_MULTIPLE 64 /* bytes */ #define MAX_MTU 3072 #define RING_ADDR_INTERVAL 128 +#define MAX_RING_ADDR_ALLOC_TIMES 3 #define RX_RING_BYTES(pdata) (sizeof(struct phytmac_dma_desc) \ * (pdata)->rx_ring_size) @@ -60,6 +61,23 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); * space in the SRAM (16KB) even when there is. */ +static int phytmac_queue_phyaddr_check(struct phytmac *pdata, dma_addr_t ring_base_addr, + int offset) +{ + u32 bus_addr_high; + int i; + + /* Check the high address of the DMA ring. */ + bus_addr_high = upper_32_bits(ring_base_addr); + for (i = 1; i < pdata->queues_num; i++) { + ring_base_addr += offset; + if (bus_addr_high != upper_32_bits(ring_base_addr)) + return -EFAULT; + } + + return 0; +} + static int phytmac_change_mtu(struct net_device *ndev, int new_mtu) { if (netif_running(ndev)) @@ -176,7 +194,6 @@ static int phytmac_mdio_write_c45(struct mii_bus *bus, int mii_id, int devad, in return 0; } - static inline int hash_bit_value(int bitnr, __u8 *addr) { if (addr[bitnr / 8] & (1 << (bitnr % 8))) @@ -397,19 +414,36 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) struct phytmac_dma_desc *tx_ring_base; dma_addr_t tx_ring_base_addr; unsigned int q; - int size; + int tx_offset; + int tx_size; + int size = 0; + int ret, i; + + tx_offset = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + tx_size = pdata->queues_num * tx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) + goto err; + + tx_ring_base = dma_alloc_coherent(pdata->dev, tx_size, + &tx_ring_base_addr, GFP_KERNEL); + if (!tx_ring_base) + continue; - size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + - RING_ADDR_INTERVAL); - tx_ring_base = dma_alloc_coherent(pdata->dev, size, - &tx_ring_base_addr, GFP_KERNEL); - if (!tx_ring_base) - goto err; + ret = phytmac_queue_phyaddr_check(pdata, tx_ring_base_addr, + tx_offset); + if (ret) { + dma_free_coherent(pdata->dev, tx_size, tx_ring_base, + tx_ring_base_addr); + continue; + } else { + break; + } + } for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - size = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; - queue->tx_ring = (void *)tx_ring_base + q * size; - queue->tx_ring_addr = tx_ring_base_addr + q * size; + queue->tx_ring = (void *)tx_ring_base + q * tx_offset; + queue->tx_ring_addr = tx_ring_base_addr + q * tx_offset; if (!queue->tx_ring) goto err; @@ -428,7 +462,6 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) "Allocated %d TX struct tx_skb entries at %p\n", pdata->tx_ring_size, queue->tx_skb); } - tx_ring_base = NULL; return 0; err: @@ -443,21 +476,37 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_dma_desc *rx_ring_base; dma_addr_t rx_ring_base_addr; + int rx_offset; + int rx_size; unsigned int q; - int size; - int i; + int size = 0; + int ret, i; - size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + - RING_ADDR_INTERVAL); - rx_ring_base = dma_alloc_coherent(pdata->dev, size, - &rx_ring_base_addr, GFP_KERNEL); - if (!rx_ring_base) - goto err; + rx_offset = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + rx_size = pdata->queues_num * rx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) + goto err; + + rx_ring_base = dma_alloc_coherent(pdata->dev, rx_size, + &rx_ring_base_addr, GFP_KERNEL); + if (!rx_ring_base) + continue; + + ret = phytmac_queue_phyaddr_check(pdata, rx_ring_base_addr, + rx_offset); + if (ret) { + dma_free_coherent(pdata->dev, rx_size, rx_ring_base, + rx_ring_base_addr); + continue; + } else { + break; + } + } for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - size = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; - queue->rx_ring = (void *)rx_ring_base + q * size; - queue->rx_ring_addr = rx_ring_base_addr + q * size; + queue->rx_ring = (void *)rx_ring_base + q * rx_offset; + queue->rx_ring_addr = rx_ring_base_addr + q * rx_offset; if (!queue->rx_ring) goto err; @@ -479,7 +528,6 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) "Allocated %d RX struct sk_buff entries at %p\n", pdata->rx_ring_size, queue->rx_skb); } - rx_ring_base = NULL; return 0; err: @@ -763,7 +811,7 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) } paddr = dma_map_single(pdata->dev, skb->data, - pdata->rx_buffer_len, DMA_FROM_DEVICE); + pdata->rx_buffer_len, DMA_FROM_DEVICE); if (dma_mapping_error(pdata->dev, paddr)) { dev_kfree_skb(skb); break; @@ -1368,14 +1416,12 @@ void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, phy_modes(interface), speed, duplex); hw_if->pcs_linkup(pdata, interface, speed, duplex); } - static const struct phylink_pcs_ops phytmac_pcs_phylink_ops = { .pcs_config = phytmac_pcs_config, .pcs_link_up = phytmac_pcs_link_up, }; - static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, - phy_interface_t interface) + phy_interface_t interface) { struct phytmac *pdata = netdev_priv(to_net_dev(config->dev)); @@ -1391,7 +1437,6 @@ static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, return &pdata->phylink_pcs; } - static void phytmac_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { From e78db3745fd632d0587cba79815512163fb336be Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 14:52:58 +0800 Subject: [PATCH 032/258] PHYTIUM: net/phytmac: Adjust the code style Fix spelling mistakes and replace the default value with a macro definition. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Icc51d45f0fb10eb8dad8aad59f839f2c0406653c Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/6848fd7516a4ab90e013a42513bb8c8b6a8b6cf8 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 4 +++- drivers/net/ethernet/phytium/phytmac_v1.c | 4 ++-- drivers/net/ethernet/phytium/phytmac_v1.h | 8 ++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 07f8476946bc60..ca1a03fa355262 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1416,10 +1416,12 @@ void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, phy_modes(interface), speed, duplex); hw_if->pcs_linkup(pdata, interface, speed, duplex); } + static const struct phylink_pcs_ops phytmac_pcs_phylink_ops = { .pcs_config = phytmac_pcs_config, .pcs_link_up = phytmac_pcs_link_up, }; + static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, phy_interface_t interface) { @@ -1766,7 +1768,7 @@ static int phytmac_open(struct net_device *ndev) ret = phytmac_phylink_connect(pdata); if (ret) { - netdev_err(ndev, "phylink connet failed,(error %d)\n", + netdev_err(ndev, "phylink connect failed,(error %d)\n", ret); goto reset_hw; } diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index ec95c6c79b0668..4a690b6a96cfe8 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -414,7 +414,7 @@ static int phytmac_init_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, dmaconfig); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, 0x80000001); + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, PHYTMAC_BIT(TXTAIL_ENABLE)); if (phy_interface_mode_is_8023z(pdata->phy_interface)) phytmac_pcs_software_reset(pdata, 1); @@ -945,7 +945,7 @@ static void phytmac_tx_start(struct phytmac_queue *queue) if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), - BIT(31) | queue->tx_tail); + PHYTMAC_BIT(TXTAIL_UPDATE) | queue->tx_tail); if (pdata->capacities & PHYTMAC_CAPS_START) PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index d8de2c26cab4b2..2cabadfed2f11f 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -356,6 +356,14 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_VLAN_ID_INDEX 10 #define PHYTMAC_VLAN_ID_WIDTH 1 +/* Bitfields in TAILPTR */ +#define PHYTMAC_TXTAIL_UPDATE_INDEX 31 /* Update tx tail */ +#define PHYTMAC_TXTAIL_UPDATE_WIDTH 1 + +/* Bitfields in TAIL_ENABLE */ +#define PHYTMAC_TXTAIL_ENABLE_INDEX 0 /* Enable tx tail */ +#define PHYTMAC_TXTAIL_ENABLE_WIDTH 1 + #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) From d1d2b6eb9244cd6a6d8e49fc9933cc23a945740d Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 14:55:39 +0800 Subject: [PATCH 033/258] PHYTIUM: net/phytmac: Get device type error fixed We should return -EOPNOTSUPP to the unsupported device types, otherwise the wireless attribute will be added by default. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie0522ed5c744ddc885ab633dac5ecc9661c580f2 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/66403a1703bf00c249e081103f7efbc2867c53bc Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index ca1a03fa355262..058f9647b85516 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1843,7 +1843,7 @@ static int phytmac_close(struct net_device *ndev) static int phytmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct phytmac *pdata = netdev_priv(dev); - int ret; + int ret = -EOPNOTSUPP; if (!netif_running(dev)) return -EINVAL; From 89ca94e1b46e498963282c6115d7a8a06378932c Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 14:57:15 +0800 Subject: [PATCH 034/258] PHYTIUM: net/phytmac: Modify the method of obtaining the link status Firstly, modify the state of pdata->speed and pdata->duplex to be obtained in the phytmac_mac_config. Secondly, the base.speed and base.duplex in the phytmac_get_link_ ksettings were changed from the default values to pdata. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ifda2028cfd32381f97b57ad1a46f0861c0c21105 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/a728bf473513902c773a61d68ea35a1dd08c39d6 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- .../net/ethernet/phytium/phytmac_ethtool.c | 45 +++++++------------ drivers/net/ethernet/phytium/phytmac_pci.c | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 1 + 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 592d2d9dc6d4b2..7cddb01802f217 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -382,43 +382,32 @@ static int phytmac_get_link_ksettings(struct net_device *ndev, u32 advertising = 0; if (!ndev->phydev) { + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = pdata->duplex; + kset->base.speed = pdata->speed; + if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { supported = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; advertising = ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_10000; } else if (pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - supported = SUPPORTED_2500baseX_Full | SUPPORTED_Pause; - advertising = ADVERTISED_2500baseX_Full | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_2500; + supported = SUPPORTED_2500baseX_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_2500baseX_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; } else if (pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX) { - supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full - | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE - | SUPPORTED_Pause; - advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full - | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE - | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_100; + supported = SUPPORTED_1000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; } else if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII) { - supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full - | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; - advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full - | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_1000; + supported = SUPPORTED_1000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; } ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.supported, diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index fd21bf80f13883..af69329fe06de5 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -225,7 +225,7 @@ struct phytmac_data phytmac_1000basex = { .use_mii = false, .speed = 1000, .duplex = true, - .interface = PHY_INTERFACE_MODE_SGMII, + .interface = PHY_INTERFACE_MODE_1000BASEX, .properties = fl_properties[0], }; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index df142aa6797f68..41e5df41226e96 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -842,6 +842,7 @@ static int phytmac_pcs_linkdown(struct phytmac *pdata) static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) { if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX || interface == PHY_INTERFACE_MODE_2500BASEX) return PHYTMAC_READ_BITS(pdata, PHYTMAC_NETWORK_STATUS, LINK); else if (interface == PHY_INTERFACE_MODE_USXGMII || From adfa2fbe78067741bddd2d6a990033d8a4660189 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:02:13 +0800 Subject: [PATCH 035/258] PHYTIUM: net/phytmac: Support usxgmii wol feature Support usxgmii wol by change the value of registers, including wol register, int register, net config register and spec_add1 register. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie651779a4bc2487aaaa51b24c69a5cc9d8de3784 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/11ba74ddc3fcd568a41284b08ff69a86809e38fa Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 12 ++++++------ drivers/net/ethernet/phytium/phytmac_v1.c | 9 +++++++++ drivers/net/ethernet/phytium/phytmac_v1.h | 11 +++++++++++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 058f9647b85516..28b6dc86f6688f 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1732,12 +1732,6 @@ static int phytmac_open(struct net_device *ndev) hw_if->reset_hw(pdata); - ret = phytmac_get_mac_address(pdata); - if (ret) { - netdev_err(ndev, "phytmac get mac address failed\n"); - goto reset_hw; - } - ret = netif_set_real_num_tx_queues(ndev, pdata->queues_num); if (ret) { netdev_err(ndev, "error setting real tx queue number\n"); @@ -2123,6 +2117,12 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_phylink_init; } + ret = phytmac_get_mac_address(pdata); + if (ret) { + netdev_err(ndev, "phytmac get mac address failed\n"); + goto err_phylink_init; + } + if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "probe successfully! Phytium %s at 0x%08lx irq %d (%pM)\n", "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 4a690b6a96cfe8..b96547e54d822c 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -489,6 +489,15 @@ static int phytmac_set_wake(struct phytmac *pdata, int wake) value |= PHYTMAC_BIT(MCAST); PHYTMAC_WRITE(pdata, PHYTMAC_WOL, value); + if (wake) { + PHYTMAC_WRITE(pdata, PHYTMAC_IE, PHYTMAC_BIT(WOL_RECEIVE_ENABLE)); + value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG) | PHYTMAC_BIT(IGNORE_RX_FCS); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_ID, PHYTMAC_BIT(WOL_RECEIVE_DISABLE)); + value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG) & ~PHYTMAC_BIT(IGNORE_RX_FCS); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + } return 0; } diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index 2cabadfed2f11f..1f49d4ec79a04a 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -140,6 +140,8 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_DBW_128 4 #define PHYTMAC_RCO_EN_INDEX 24 /* Receive checksum offload enable */ #define PHYTMAC_RCO_EN_WIDTH 1 +#define PHYTMAC_IGNORE_RX_FCS_INDEX 26 +#define PHYTMAC_IGNORE_RX_FCS_WIDTH 1 #define PHYTMAC_SGMII_EN_INDEX 27 /* Sgmii mode enable */ #define PHYTMAC_SGMII_EN_WIDTH 1 @@ -364,6 +366,15 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_TXTAIL_ENABLE_INDEX 0 /* Enable tx tail */ #define PHYTMAC_TXTAIL_ENABLE_WIDTH 1 +/* Bitfields in INT ENABLE */ +#define PHYTMAC_WOL_RECEIVE_ENABLE_INDEX 28 /* Enable wol_event_recieve */ +#define PHYTMAC_WOL_RECEIVE_ENABLE_WIDTH 1 + +/* Bitfields in INT DISABLE */ +#define PHYTMAC_WOL_RECEIVE_DISABLE_INDEX 28 /* Disable wol_event_recieve */ +#define PHYTMAC_WOL_RECEIVE_DISABLE_WIDTH 1 + + #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) From 7f870e1470fc04bc0efdb4b51f3ada4708f4c442 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:04:49 +0800 Subject: [PATCH 036/258] PHYTIUM: net/phytmac: Roll back rx_clean version Roll back rx_clean version to not use batching. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie6e7585304ac6a7b9101e04c5af5219b3a361996 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/1040f4e7b52143571a6fa9e85e13cd0c1642aafc Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 -- drivers/net/ethernet/phytium/phytmac_main.c | 24 ++++++++------------- drivers/net/ethernet/phytium/phytmac_v1.c | 23 ++++++-------------- 3 files changed, 16 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index b90e1551499ef7..33c0fc78415b25 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -38,7 +38,6 @@ #define MIN_TX_RING_SIZE 64 #define MIN_RX_RING_SIZE 64 #define DEFAULT_TX_DESC_MIN_FREE 64 -#define DEFAULT_RX_DESC_MIN_FREE 64 #define MEMORY_SIZE 4096 #define MHU_SIZE 0x20 @@ -499,7 +498,6 @@ struct phytmac_hw_if { struct packet_info *packet); void (*init_rx_map)(struct phytmac_queue *queue, u32 index); unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); - unsigned int (*rx_clean)(struct phytmac_queue *queue, u32 cleaned_count); void (*transmit)(struct phytmac_queue *queue); void (*restart)(struct phytmac *pdata); int (*tx_complete)(const struct phytmac_dma_desc *desc); diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 28b6dc86f6688f..0053412c2632c5 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -793,16 +793,12 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) unsigned int index, space; dma_addr_t paddr; struct sk_buff *skb; - unsigned int rx_unclean = 0; space = CIRC_SPACE(queue->rx_head, queue->rx_tail, pdata->rx_ring_size); - if (space < DEFAULT_RX_DESC_MIN_FREE) - return; - - index = queue->rx_head & (pdata->rx_ring_size - 1); while (space > 0) { + index = queue->rx_head & (pdata->rx_ring_size - 1); if (!queue->rx_skb[index]) { skb = netdev_alloc_skb(pdata->ndev, pdata->rx_buffer_len); if (unlikely(!skb)) { @@ -811,7 +807,7 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) } paddr = dma_map_single(pdata->dev, skb->data, - pdata->rx_buffer_len, DMA_FROM_DEVICE); + pdata->rx_buffer_len, DMA_FROM_DEVICE); if (dma_mapping_error(pdata->dev, paddr)) { dev_kfree_skb(skb); break; @@ -820,21 +816,19 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) queue->rx_skb[index] = skb; hw_if->rx_map(queue, index, paddr); + } else { + hw_if->rx_map(queue, index, 0); } - index = (index + 1) & (pdata->rx_ring_size - 1); - rx_unclean++; + queue->rx_head++; + if (queue->rx_head >= pdata->rx_ring_size) + queue->rx_head &= (pdata->rx_ring_size - 1); + space--; } /* make newly descriptor to hardware */ wmb(); - hw_if->rx_clean(queue, rx_unclean); - /* make newly descriptor to hardware */ - wmb(); - queue->rx_head += rx_unclean; - if (queue->rx_head >= pdata->rx_ring_size) - queue->rx_head &= (pdata->rx_ring_size - 1); } static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, @@ -1203,7 +1197,7 @@ static unsigned int phytmac_tx_map(struct phytmac *pdata, { dma_addr_t mapping; struct phytmac_hw_if *hw_if = pdata->hw_if; - unsigned int len, i, tx_tail = queue->tx_tail; + unsigned int len, i, tx_tail; struct phytmac_tx_skb *tx_skb = NULL; unsigned int offset, size, count = 0; unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags; diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index b96547e54d822c..b823a07a731b67 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -927,22 +927,14 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, addr |= PHYTMAC_BIT(RX_WRAP); desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); - desc->desc0 = lower_32_bits(addr) | PHYTMAC_BIT(RX_USED); - } - return 0; -} - -static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count) -{ - struct phytmac_dma_desc *desc; - u32 index = queue->rx_head + count - 1; - - while (count) { - desc = phytmac_get_rx_desc(queue, index); - desc->desc0 &= ~PHYTMAC_BIT(RX_USED); + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 = lower_32_bits(addr); + } else { + desc->desc1 = 0; + /* Make newly descriptor to hardware */ dma_wmb(); - index--; - count--; + desc->desc0 &= ~PHYTMAC_BIT(RX_USED); } return 0; @@ -1386,7 +1378,6 @@ struct phytmac_hw_if phytmac_1p0_hw = { .get_desc_addr = phytmac_get_desc_addr, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, - .rx_clean = phytmac_rx_clean_desc, .rx_checksum = phytmac_rx_checksum, .rx_single_buffer = phytmac_rx_single_buffer, .rx_pkt_start = phytmac_rx_sof, From 2a265092e1fa853ca0ce4fb87498016b891a0c48 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:09:08 +0800 Subject: [PATCH 037/258] PHYTIUM: net/phytmac: Override rx_skb allocation function Changing the way netdev_alloc_skb function assigns SKB directly, so that MAC driver alloc pages in advance and build SKB in the phytmac RX function. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ia765cea707d97a212ab9864c139a6198668c8604 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/bb1fd376f46bcf15d08af0210af4b45ca5990fcb Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 24 +- drivers/net/ethernet/phytium/phytmac_main.c | 432 +++++++++++++++----- drivers/net/ethernet/phytium/phytmac_v1.c | 11 - drivers/net/ethernet/phytium/phytmac_v2.c | 11 - 4 files changed, 341 insertions(+), 137 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 33c0fc78415b25..363bc65c72d474 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -307,6 +307,13 @@ struct phytmac_tx_ts { u32 ts_2; }; +struct phytmac_rx_buffer { + dma_addr_t addr; + struct page *page; + __u16 page_offset; + __u16 pagecnt_bias; +}; + struct phytmac_queue { struct phytmac *pdata; int irq; @@ -328,8 +335,9 @@ struct phytmac_queue { dma_addr_t rx_ring_addr; unsigned int rx_head; unsigned int rx_tail; + unsigned int rx_next_to_alloc; struct phytmac_dma_desc *rx_ring; - struct sk_buff **rx_skb; + struct phytmac_rx_buffer *rx_buffer_info; struct napi_struct rx_napi; struct phytmac_queue_stats stats; @@ -503,7 +511,6 @@ struct phytmac_hw_if { int (*tx_complete)(const struct phytmac_dma_desc *desc); int (*rx_complete)(const struct phytmac_dma_desc *desc); int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); - dma_addr_t (*get_desc_addr)(const struct phytmac_dma_desc *desc); bool (*rx_checksum)(const struct phytmac_dma_desc *desc); void (*set_desc_rxused)(struct phytmac_dma_desc *desc); bool (*rx_single_buffer)(const struct phytmac_dma_desc *desc); @@ -572,6 +579,19 @@ struct phytmac_hw_if { #define PHYTMAC_READ_DATA0(pdata) PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA0) #define PHYTMAC_TIMEOUT 1000000000 /* in usecs */ +#define PHYTMAC_GFP_FLAGS \ + (GFP_ATOMIC | __GFP_NOWARN | GFP_DMA | __GFP_DMA32) +#define PHYTMAC_RX_DMA_ATTR \ + (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) +#define PHYTMAC_SKB_PAD (NET_SKB_PAD) + +#define PHYTMAC_RXBUFFER_2048 2048 +#define PHYTMAC_MAX_FRAME_BUILD_SKB \ + (SKB_WITH_OVERHEAD(PHYTMAC_RXBUFFER_2048) - PHYTMAC_SKB_PAD) + +#define PHYTMAC_RX_PAGE_ORDER 0 +#define PHYTMAC_RX_PAGE_SIZE (PAGE_SIZE << PHYTMAC_RX_PAGE_ORDER) + struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, unsigned int index); inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 0053412c2632c5..a2b3ed84e895a9 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "phytmac.h" #include "phytmac_ptp.h" @@ -293,15 +295,12 @@ static struct net_device_stats *phytmac_get_stats(struct net_device *dev) return nstat; } -static int phytmac_calc_rx_buf_len(struct phytmac *pdata, u32 mtu) +static inline int phytmac_calc_rx_buf_len(void) { - unsigned int size = mtu + ETH_HLEN + ETH_FCS_LEN; - int rx_buf_len = roundup(size, RX_BUFFER_MULTIPLE); - - netdev_dbg(pdata->ndev, "mtu [%u] rx_buffer_size [%u]\n", - mtu, rx_buf_len); - - return rx_buf_len; +#if (PAGE_SIZE < 8192) + return PHYTMAC_MAX_FRAME_BUILD_SKB; +#endif + return PHYTMAC_RXBUFFER_2048; } inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, @@ -310,12 +309,6 @@ inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, return &queue->rx_ring[index & (queue->pdata->rx_ring_size - 1)]; } -struct sk_buff *phytmac_get_rx_skb(struct phytmac_queue *queue, - unsigned int index) -{ - return queue->rx_skb[index & (queue->pdata->rx_ring_size - 1)]; -} - struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, unsigned int index) { @@ -328,6 +321,49 @@ inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, return &queue->tx_ring[index & (queue->pdata->tx_ring_size - 1)]; } +static void phytmac_rx_unmap(struct phytmac_queue *queue) +{ + struct phytmac_rx_buffer *rx_buffer_info; + struct phytmac *pdata = queue->pdata; + int i; + + if (queue->rx_buffer_info) { + /* Free all the Rx ring sk_buffs */ + i = queue->rx_tail; + + while (i != queue->rx_next_to_alloc) { + rx_buffer_info = &queue->rx_buffer_info[i]; + + /* Invalidate cache lines that may have been written to by + * device so that we avoid corrupting memory. + */ + dma_sync_single_range_for_cpu(pdata->dev, + rx_buffer_info->addr, + rx_buffer_info->page_offset, + pdata->rx_buffer_len, + DMA_FROM_DEVICE); + + /* free resources associated with mapping */ + dma_unmap_page_attrs(pdata->dev, + rx_buffer_info->addr, + PHYTMAC_RX_PAGE_SIZE, + DMA_FROM_DEVICE, + PHYTMAC_RX_DMA_ATTR); + + __page_frag_cache_drain(rx_buffer_info->page, + rx_buffer_info->pagecnt_bias); + + i++; + if (i == pdata->rx_ring_size) + i = 0; + } + + queue->rx_tail = 0; + queue->rx_head = 0; + queue->rx_next_to_alloc = 0; + } +} + static int phytmac_free_tx_resource(struct phytmac *pdata) { struct phytmac_queue *queue; @@ -362,14 +398,10 @@ static int phytmac_free_tx_resource(struct phytmac *pdata) static int phytmac_free_rx_resource(struct phytmac *pdata) { struct phytmac_queue *queue; - struct sk_buff *skb; - struct phytmac_dma_desc *desc; - struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_dma_desc *rx_ring_base = NULL; dma_addr_t rx_ring_base_addr; - dma_addr_t addr; unsigned int q; - int size, i; + int size; queue = pdata->queues; if (queue->rx_ring) { @@ -378,25 +410,15 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) } for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - if (queue->rx_skb) { - for (i = 0; i < pdata->rx_ring_size; i++) { - skb = phytmac_get_rx_skb(queue, i); - if (skb) { - desc = &queue->rx_ring[i]; - addr = hw_if->get_desc_addr(desc); - dma_unmap_single(pdata->dev, addr, pdata->rx_buffer_len, - DMA_FROM_DEVICE); - dev_kfree_skb_any(skb); - skb = NULL; - } - } - - kfree(queue->rx_skb); - queue->rx_skb = NULL; - } + phytmac_rx_unmap(queue); if (queue->rx_ring) queue->rx_ring = NULL; + + if (queue->rx_buffer_info) { + vfree(queue->rx_buffer_info); + queue->rx_buffer_info = NULL; + } } if (rx_ring_base) { @@ -450,7 +472,7 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "Allocated TX ring for queue %u of %d bytes at %08lx\n", - q, size, (unsigned long)queue->tx_ring_addr); + q, tx_offset, (unsigned long)queue->tx_ring_addr); size = pdata->tx_ring_size * sizeof(struct phytmac_tx_skb); queue->tx_skb = kzalloc(size, GFP_KERNEL); @@ -473,7 +495,6 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) static int phytmac_alloc_rx_resource(struct phytmac *pdata) { struct phytmac_queue *queue; - struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_dma_desc *rx_ring_base; dma_addr_t rx_ring_base_addr; int rx_offset; @@ -513,20 +534,12 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "Allocated RX ring for queue %u of %d bytes at %08lx\n", - q, size, (unsigned long)queue->rx_ring_addr); + q, rx_offset, (unsigned long)queue->rx_ring_addr); - for (i = 0; i < pdata->rx_ring_size; i++) - hw_if->init_rx_map(queue, i); - - size = pdata->rx_ring_size * sizeof(struct sk_buff *); - queue->rx_skb = kzalloc(size, GFP_KERNEL); - if (!queue->rx_skb) + size = pdata->rx_ring_size * sizeof(struct phytmac_rx_buffer); + queue->rx_buffer_info = vzalloc(size); + if (!queue->rx_buffer_info) goto err; - - if (netif_msg_drv(pdata)) - netdev_info(pdata->ndev, - "Allocated %d RX struct sk_buff entries at %p\n", - pdata->rx_ring_size, queue->rx_skb); } return 0; @@ -538,10 +551,9 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) static int phytmac_alloc_resource(struct phytmac *pdata) { - struct net_device *ndev = pdata->ndev; int ret; - pdata->rx_buffer_len = phytmac_calc_rx_buf_len(pdata, ndev->mtu); + pdata->rx_buffer_len = phytmac_calc_rx_buf_len(); if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "alloc resource, rx_buffer_len = %d\n", @@ -652,30 +664,226 @@ static void phytmac_dump_pkt(struct phytmac *pdata, struct sk_buff *skb, bool tx skb->data, skb->len, true); } +static bool phytmac_alloc_mapped_page(struct phytmac *pdata, + struct phytmac_rx_buffer *bi) +{ + struct page *page = bi->page; + dma_addr_t dma; + + /* since we are recycling buffers we should seldom need to alloc */ + if (likely(page)) + return true; + + /* alloc new page for storage */ + page = __dev_alloc_pages(PHYTMAC_GFP_FLAGS, PHYTMAC_RX_PAGE_ORDER); + if (unlikely(!page)) { + netdev_err(pdata->ndev, "rx alloc page failed\n"); + return false; + } + + /* map page for use */ + dma = dma_map_page_attrs(pdata->dev, page, 0, + PHYTMAC_RX_PAGE_SIZE, + DMA_FROM_DEVICE, PHYTMAC_RX_DMA_ATTR); + if (dma_mapping_error(pdata->dev, dma)) { + __free_pages(page, PHYTMAC_RX_PAGE_ORDER); + return false; + } + + bi->addr = dma; + bi->page = page; + bi->page_offset = PHYTMAC_SKB_PAD; + bi->pagecnt_bias = 1; + + return true; +} + +static inline bool phytmac_page_is_reserved(struct page *page) +{ + return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); +} + +static bool phytmac_can_reuse_rx_page(struct phytmac_rx_buffer *rx_buffer) +{ + unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; + struct page *page = rx_buffer->page; + + /* avoid re-using remote pages */ + if (unlikely(phytmac_page_is_reserved(page))) + return false; + +#if (PAGE_SIZE < 8192) + /* if we are only owner of page we can reuse it */ + if (unlikely((page_ref_count(page) - pagecnt_bias) > 1)) + return false; +#else +#define PHYTMAC_LAST_OFFSET \ + (SKB_WITH_OVERHEAD(PAGE_SIZE) - PHYTMAC_RXBUFFER_2048) + + if (rx_buffer->page_offset > PHYTMAC_LAST_OFFSET) + return false; +#endif + + /* If we have drained the page fragment pool we need to update + * the pagecnt_bias and page count so that we fully restock the + * number of references the driver holds. + */ + if (unlikely(!pagecnt_bias)) { + page_ref_add(page, USHRT_MAX); + rx_buffer->pagecnt_bias = USHRT_MAX; + } + + return true; +} + +static void phytmac_reuse_rx_page(struct phytmac_queue *queue, + struct phytmac_rx_buffer *old_buff) +{ + struct phytmac_rx_buffer *new_buff; + struct phytmac *pdata = queue->pdata; + u16 nta = queue->rx_next_to_alloc; + + new_buff = &queue->rx_buffer_info[nta & (pdata->rx_ring_size - 1)]; + + /* update, and store next to alloc */ + nta++; + queue->rx_next_to_alloc = (nta < pdata->rx_ring_size) ? nta : 0; + + /* Transfer page from old buffer to new buffer. + * Move each member individually to avoid possible store + * forwarding stalls. + */ + new_buff->addr = old_buff->addr; + new_buff->page = old_buff->page; + new_buff->page_offset = old_buff->page_offset; + new_buff->pagecnt_bias = old_buff->pagecnt_bias; +} + +static struct phytmac_rx_buffer *phytmac_get_rx_buffer(struct phytmac_queue *queue, + unsigned int index, + const unsigned int size) +{ + struct phytmac_rx_buffer *rx_buffer; + struct phytmac *pdata = queue->pdata; + + rx_buffer = &queue->rx_buffer_info[index & (pdata->rx_ring_size - 1)]; + prefetchw(rx_buffer->page); + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(pdata->dev, + rx_buffer->addr, + rx_buffer->page_offset, + size, + DMA_FROM_DEVICE); + + rx_buffer->pagecnt_bias--; + + return rx_buffer; +} + +static void phytmac_put_rx_buffer(struct phytmac_queue *queue, + struct phytmac_rx_buffer *rx_buffer) +{ + struct phytmac *pdata = queue->pdata; + + if (phytmac_can_reuse_rx_page(rx_buffer)) { + /* hand second half of page back to the ring */ + phytmac_reuse_rx_page(queue, rx_buffer); + } else { + dma_unmap_page_attrs(pdata->dev, rx_buffer->addr, + PHYTMAC_RX_PAGE_SIZE, + DMA_FROM_DEVICE, PHYTMAC_RX_DMA_ATTR); + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); + } + + /* clear contents of rx_buffer */ + rx_buffer->page = NULL; +} + +static void phytmac_add_rx_frag(struct phytmac_queue *queue, + struct phytmac_rx_buffer *rx_buffer, + struct sk_buff *skb, + unsigned int size) +{ + unsigned int truesize; + +#if (PAGE_SIZE < 8192) + truesize = PHYTMAC_RX_PAGE_SIZE / 2; +#else + truesize = SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + size); +#endif + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, + rx_buffer->page_offset, size, truesize); +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif +} + +static struct sk_buff *phytmac_build_skb(struct phytmac_rx_buffer *rx_buffer, + struct phytmac_dma_desc *desc, + unsigned int size) +{ + struct sk_buff *skb; + unsigned int truesize; + void *va; + +#if (PAGE_SIZE < 8192) + truesize = PHYTMAC_RX_PAGE_SIZE / 2; +#else + truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + size); +#endif + + va = page_address(rx_buffer->page) + rx_buffer->page_offset; + /* prefetch first cache line of first page */ + prefetch(va); + + /* build an skb around the page buffer */ + skb = build_skb(va - PHYTMAC_SKB_PAD, truesize); + if (unlikely(!skb)) + return NULL; + + /* update pointers within the skb to store the data */ + skb_reserve(skb, PHYTMAC_SKB_PAD); + __skb_put(skb, size); + + /* update buffer offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + + return skb; +} + static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phytmac_dma_desc *desc) { struct phytmac *pdata = queue->pdata; struct phytmac_hw_if *hw_if = pdata->hw_if; - struct sk_buff *skb; + struct phytmac_rx_buffer *rx_buffer; + struct sk_buff *skb = NULL; unsigned int len; - dma_addr_t addr; - skb = phytmac_get_rx_skb(queue, queue->rx_tail); + len = hw_if->get_rx_pkt_len(pdata, desc); + rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); + + skb = phytmac_build_skb(rx_buffer, desc, len); if (unlikely(!skb)) { netdev_err(pdata->ndev, - "inconsistent Rx descriptor chain\n"); + "rx single build skb failed\n"); pdata->ndev->stats.rx_dropped++; queue->stats.rx_dropped++; + rx_buffer->pagecnt_bias++; return NULL; } - queue->rx_skb[queue->rx_tail & (pdata->rx_ring_size - 1)] = NULL; - len = hw_if->get_rx_pkt_len(pdata, desc); - addr = hw_if->get_desc_addr(desc); + phytmac_put_rx_buffer(queue, rx_buffer); - skb_put(skb, len); - dma_unmap_single(pdata->dev, addr, - pdata->rx_buffer_len, DMA_FROM_DEVICE); skb->protocol = eth_type_trans(skb, pdata->ndev); skb_checksum_none_assert(skb); @@ -691,64 +899,65 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy } static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, - unsigned int first_frag, unsigned int last_frag, int len) + unsigned int first_frag, unsigned int last_frag, + unsigned int total_len) { - unsigned int offset = 0; unsigned int frag = 0; - unsigned int entry = 0; - dma_addr_t addr = 0; struct sk_buff *skb; struct phytmac_dma_desc *desc; struct phytmac *pdata = queue->pdata; struct phytmac_hw_if *hw_if = pdata->hw_if; unsigned int frag_len = pdata->rx_buffer_len; + unsigned int offset = frag_len; + struct phytmac_rx_buffer *rx_buffer; if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "rx frame %u - %u (len %u)\n", - first_frag, last_frag, len); + first_frag, last_frag, total_len); + + desc = phytmac_get_rx_desc(queue, first_frag); + rx_buffer = phytmac_get_rx_buffer(queue, first_frag, frag_len); - skb = netdev_alloc_skb(pdata->ndev, len); - if (!skb) { + skb = phytmac_build_skb(rx_buffer, desc, frag_len); + if (unlikely(!skb)) { + netdev_err(pdata->ndev, "rx frame build skb failed\n"); pdata->ndev->stats.rx_dropped++; - netdev_err(pdata->ndev, "rx frame alloc skb failed\n"); + queue->stats.rx_dropped++; + rx_buffer->pagecnt_bias++; return NULL; } - skb_checksum_none_assert(skb); + phytmac_put_rx_buffer(queue, rx_buffer); - if (pdata->ndev->features & NETIF_F_RXCSUM && - !(pdata->ndev->flags & IFF_PROMISC) && - hw_if->rx_checksum(phytmac_get_rx_desc(queue, last_frag))) - skb->ip_summed = CHECKSUM_UNNECESSARY; - - skb_put(skb, len); + for (frag = first_frag + 1; ; frag++) { + desc = phytmac_get_rx_desc(queue, frag); + rx_buffer = phytmac_get_rx_buffer(queue, frag, frag_len); - for (frag = first_frag; ; frag++) { - if (offset + frag_len > len) { + if (offset + frag_len > total_len) { if (unlikely(frag != last_frag)) { dev_kfree_skb_any(skb); + phytmac_put_rx_buffer(queue, rx_buffer); return NULL; } - frag_len = len - offset; + frag_len = total_len - offset; } - desc = phytmac_get_rx_desc(queue, frag); - addr = hw_if->get_desc_addr(desc); - dma_sync_single_for_cpu(pdata->dev, addr, frag_len, - DMA_FROM_DEVICE); - - entry = frag & (pdata->rx_ring_size - 1); - skb_copy_to_linear_data_offset(skb, offset, queue->rx_skb[entry]->data, frag_len); - - offset += pdata->rx_buffer_len; + phytmac_add_rx_frag(queue, rx_buffer, skb, frag_len); + phytmac_put_rx_buffer(queue, rx_buffer); - dma_sync_single_for_device(pdata->dev, addr, frag_len, - DMA_FROM_DEVICE); + offset += frag_len; if (frag == last_frag) break; } + skb_checksum_none_assert(skb); + + if (pdata->ndev->features & NETIF_F_RXCSUM && + !(pdata->ndev->flags & IFF_PROMISC) && + hw_if->rx_checksum(phytmac_get_rx_desc(queue, last_frag))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->protocol = eth_type_trans(skb, pdata->ndev); if (netif_msg_pktdata(pdata)) phytmac_dump_pkt(pdata, skb, false); @@ -764,10 +973,13 @@ static struct sk_buff *phytmac_rx_mbuffer(struct phytmac_queue *queue) struct sk_buff *skb = NULL; unsigned int rx_tail = 0; int first_frag = -1; - int len; + unsigned int len; for (rx_tail = queue->rx_tail; ; rx_tail++) { desc = phytmac_get_rx_desc(queue, rx_tail); + if (!hw_if->rx_complete(desc)) + return NULL; + if (hw_if->rx_pkt_start(desc)) { if (first_frag != -1) hw_if->clear_rx_desc(queue, first_frag, rx_tail); @@ -791,34 +1003,24 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) struct phytmac *pdata = queue->pdata; struct phytmac_hw_if *hw_if = pdata->hw_if; unsigned int index, space; - dma_addr_t paddr; - struct sk_buff *skb; + struct phytmac_rx_buffer *rx_buf_info; space = CIRC_SPACE(queue->rx_head, queue->rx_tail, pdata->rx_ring_size); while (space > 0) { index = queue->rx_head & (pdata->rx_ring_size - 1); - if (!queue->rx_skb[index]) { - skb = netdev_alloc_skb(pdata->ndev, pdata->rx_buffer_len); - if (unlikely(!skb)) { - netdev_err(pdata->ndev, "rx clean alloc skb failed\n"); - break; - } + rx_buf_info = &queue->rx_buffer_info[index]; - paddr = dma_map_single(pdata->dev, skb->data, - pdata->rx_buffer_len, DMA_FROM_DEVICE); - if (dma_mapping_error(pdata->dev, paddr)) { - dev_kfree_skb(skb); - break; - } - - queue->rx_skb[index] = skb; + if (!phytmac_alloc_mapped_page(pdata, rx_buf_info)) + break; + /* sync the buffer for use by the device */ + dma_sync_single_range_for_device(pdata->dev, rx_buf_info->addr, + rx_buf_info->page_offset, + pdata->rx_buffer_len, + DMA_FROM_DEVICE); - hw_if->rx_map(queue, index, paddr); - } else { - hw_if->rx_map(queue, index, 0); - } + hw_if->rx_map(queue, index, rx_buf_info->addr + rx_buf_info->page_offset); queue->rx_head++; if (queue->rx_head >= pdata->rx_ring_size) @@ -827,6 +1029,7 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) space--; } + queue->rx_next_to_alloc = queue->rx_head; /* make newly descriptor to hardware */ wmb(); } @@ -922,7 +1125,7 @@ static int phytmac_maybe_wake_tx_queue(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; int space = CIRC_CNT(queue->tx_tail, queue->tx_head, - pdata->tx_ring_size); + pdata->tx_ring_size); return (space <= (3 * pdata->tx_ring_size / 4)) ? 1 : 0; } @@ -1291,14 +1494,18 @@ static inline void phytmac_init_ring(struct phytmac *pdata) struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_queue *queue; unsigned int q = 0; + int i; for (queue = pdata->queues; q < pdata->queues_num; ++q) { queue->tx_head = 0; queue->tx_tail = 0; hw_if->clear_tx_desc(queue); + for (i = 0; i < pdata->rx_ring_size; i++) + hw_if->init_rx_map(queue, i); queue->rx_head = 0; queue->rx_tail = 0; + queue->rx_next_to_alloc = 0; phytmac_rx_clean(queue); ++queue; } @@ -1751,7 +1958,6 @@ static int phytmac_open(struct net_device *ndev) ++queue; } - phytmac_init_ring(pdata); hw_if->init_hw(pdata); ret = phytmac_phylink_connect(pdata); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index b823a07a731b67..9582b587342bb1 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -986,16 +986,6 @@ static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_de return desc->desc1 & PHYTMAC_FRAME_MASK; } -static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) -{ - dma_addr_t addr = 0; - - addr = ((u64)(desc->desc2) << 32); - - addr |= (desc->desc0 & 0xfffffffc); - return addr; -} - static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; @@ -1375,7 +1365,6 @@ struct phytmac_hw_if phytmac_1p0_hw = { .tx_complete = phytmac_tx_complete, .rx_complete = phytmac_rx_complete, .get_rx_pkt_len = phytmac_rx_pkt_len, - .get_desc_addr = phytmac_get_desc_addr, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, .rx_checksum = phytmac_rx_checksum, diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 41e5df41226e96..bcfc8ad68edc10 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -952,16 +952,6 @@ static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_de return desc->desc1 & PHYTMAC_RXFRMLEN_MASK; } -static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) -{ - dma_addr_t addr = 0; - - addr = ((u64)(desc->desc2) << 32); - - addr |= (desc->desc0 & 0xfffffffc); - return addr; -} - static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; @@ -1229,7 +1219,6 @@ struct phytmac_hw_if phytmac_2p0_hw = { .tx_complete = phytmac_tx_complete, .rx_complete = phytmac_rx_complete, .get_rx_pkt_len = phytmac_rx_pkt_len, - .get_desc_addr = phytmac_get_desc_addr, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, .rx_checksum = phytmac_rx_checksum, From 116bcd25ec359158877ae82934ffc85c11d0bcc6 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:10:12 +0800 Subject: [PATCH 038/258] PHYTIUM: net/phytmac: Round down rx_buf_len to 64 bytes DMA config register needs the rx_buf_len to be aligned to 64-bytes. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I7535ac7d8abaf80397b39ec1a16b1b959c9decfd Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/38380eb6dd3618217d0bc254e0d4b126b331c0c1 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index a2b3ed84e895a9..72b5a15870a08d 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -298,9 +298,9 @@ static struct net_device_stats *phytmac_get_stats(struct net_device *dev) static inline int phytmac_calc_rx_buf_len(void) { #if (PAGE_SIZE < 8192) - return PHYTMAC_MAX_FRAME_BUILD_SKB; + return rounddown(PHYTMAC_MAX_FRAME_BUILD_SKB, RX_BUFFER_MULTIPLE); #endif - return PHYTMAC_RXBUFFER_2048; + return rounddown(PHYTMAC_RXBUFFER_2048, RX_BUFFER_MULTIPLE); } inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, From e010a6cd0b453709e0ebb9801b6381489884cfbf Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 26 Apr 2024 15:11:35 +0800 Subject: [PATCH 039/258] PHYTIUM: net/phytmac: Add version display for phytmac driver Add support for Using the ethtool -i interface or modinfo phytmac/phytmac_platform command to display driver version information. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I6577196db18b5b71252ed1e890bb997693460b2d Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/8f25123cc1d1a2ee58e4a61da2ba52e423a13ecf Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_ethtool.c | 16 ++++++++++++++++ drivers/net/ethernet/phytium/phytmac_main.c | 1 + drivers/net/ethernet/phytium/phytmac_pci.c | 1 + drivers/net/ethernet/phytium/phytmac_platform.c | 1 + 5 files changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 363bc65c72d474..7e6cf22d7b4f77 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" - +#define PHYTMAC_DRIVER_VERSION "1.0.0" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 7cddb01802f217..4f632591142fcc 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include "phytmac.h" #include "phytmac_v1.h" #include "phytmac_v2.h" @@ -502,6 +504,19 @@ static inline void phytmac_set_msglevel(struct net_device *ndev, u32 level) pdata->msg_enable = level; } +static void phytmac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *drvinfo) +{ + struct phytmac *pdata = netdev_priv(ndev); + + strscpy(drvinfo->driver, PHYTMAC_DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, PHYTMAC_DRIVER_VERSION, sizeof(drvinfo->version)); + + if (pdata->platdev) + strscpy(drvinfo->bus_info, pdata->platdev->name, sizeof(drvinfo->bus_info)); + else if (pdata->pcidev) + strscpy(drvinfo->bus_info, pci_name(pdata->pcidev), sizeof(drvinfo->bus_info)); +} + static const struct ethtool_ops phytmac_ethtool_ops = { .get_regs_len = phytmac_get_regs_len, .get_regs = phytmac_get_regs, @@ -524,6 +539,7 @@ static const struct ethtool_ops phytmac_ethtool_ops = { .set_channels = phytmac_set_channels, .get_wol = phytmac_get_wol, .set_wol = phytmac_set_wol, + .get_drvinfo = phytmac_get_drvinfo, }; void phytmac_set_ethtool_ops(struct net_device *ndev) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 72b5a15870a08d..79170097fd8300 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2488,4 +2488,5 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); MODULE_ALIAS("platform:phytmac"); +MODULE_VERSION(PHYTMAC_DRIVER_VERSION); diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index af69329fe06de5..60bd296d8f0b6c 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -316,3 +316,4 @@ module_pci_driver(phytmac_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium NIC PCI wrapper"); +MODULE_VERSION(PHYTMAC_DRIVER_VERSION); diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 305ff5866e2fe0..9390056fdc7a95 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -253,3 +253,4 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); MODULE_ALIAS("platform:phytmac"); +MODULE_VERSION(PHYTMAC_DRIVER_VERSION); From 36da9a110ab3bab6728d86b082f3ec6ce3a5be9c Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 29 Apr 2024 10:47:47 +0800 Subject: [PATCH 040/258] PHYTIUM: net/phytmac: Bugfix about dma conflict problem When setting RX_USED bit of the last descriptor for rx ring to 1, which will lead to the soft think it' valid and cause dma conflict. So change valid condition according to RX_USED bit and non-zero address. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I784537d0d55665587e01a186499416e48d46ad35 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/63d1449e25be6d4e0a3a6b9d0be14b59327e75fa Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 5 +++-- drivers/net/ethernet/phytium/phytmac_main.c | 19 ++++++++-------- drivers/net/ethernet/phytium/phytmac_v1.c | 25 ++++++++++++++++++--- drivers/net/ethernet/phytium/phytmac_v1.h | 1 - drivers/net/ethernet/phytium/phytmac_v2.c | 25 ++++++++++++++++++--- 5 files changed, 56 insertions(+), 19 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 7e6cf22d7b4f77..6eb1d7d1b8293b 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.0" +#define PHYTMAC_DRIVER_VERSION "1.0.1" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -509,13 +509,14 @@ struct phytmac_hw_if { void (*transmit)(struct phytmac_queue *queue); void (*restart)(struct phytmac *pdata); int (*tx_complete)(const struct phytmac_dma_desc *desc); - int (*rx_complete)(const struct phytmac_dma_desc *desc); + bool (*rx_complete)(const struct phytmac_dma_desc *desc); int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); bool (*rx_checksum)(const struct phytmac_dma_desc *desc); void (*set_desc_rxused)(struct phytmac_dma_desc *desc); bool (*rx_single_buffer)(const struct phytmac_dma_desc *desc); bool (*rx_pkt_start)(const struct phytmac_dma_desc *desc); bool (*rx_pkt_end)(const struct phytmac_dma_desc *desc); + unsigned int (*zero_rx_desc_addr)(struct phytmac_dma_desc *desc); void (*clear_rx_desc)(struct phytmac_queue *queue, int begin, int end); void (*clear_tx_desc)(struct phytmac_queue *queue); /* ptp */ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 79170097fd8300..d932d011b261fd 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -442,6 +442,7 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) int ret, i; tx_offset = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + tx_offset = ALIGN(tx_offset, 4096); tx_size = pdata->queues_num * tx_offset; for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { if (i == MAX_RING_ADDR_ALLOC_TIMES) @@ -504,6 +505,7 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) int ret, i; rx_offset = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + rx_offset = ALIGN(rx_offset, 4096); rx_size = pdata->queues_num * rx_offset; for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { if (i == MAX_RING_ADDR_ALLOC_TIMES) @@ -698,18 +700,13 @@ static bool phytmac_alloc_mapped_page(struct phytmac *pdata, return true; } -static inline bool phytmac_page_is_reserved(struct page *page) -{ - return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); -} - static bool phytmac_can_reuse_rx_page(struct phytmac_rx_buffer *rx_buffer) { unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; struct page *page = rx_buffer->page; - /* avoid re-using remote pages */ - if (unlikely(phytmac_page_is_reserved(page))) + /* avoid re-using remote and pfmemalloc pages */ + if (!dev_page_is_reusable(page)) return false; #if (PAGE_SIZE < 8192) @@ -824,7 +821,6 @@ static void phytmac_add_rx_frag(struct phytmac_queue *queue, } static struct sk_buff *phytmac_build_skb(struct phytmac_rx_buffer *rx_buffer, - struct phytmac_dma_desc *desc, unsigned int size) { struct sk_buff *skb; @@ -871,8 +867,9 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy len = hw_if->get_rx_pkt_len(pdata, desc); rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); + hw_if->zero_rx_desc_addr(desc); - skb = phytmac_build_skb(rx_buffer, desc, len); + skb = phytmac_build_skb(rx_buffer, len); if (unlikely(!skb)) { netdev_err(pdata->ndev, "rx single build skb failed\n"); @@ -917,8 +914,9 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, desc = phytmac_get_rx_desc(queue, first_frag); rx_buffer = phytmac_get_rx_buffer(queue, first_frag, frag_len); + hw_if->zero_rx_desc_addr(desc); - skb = phytmac_build_skb(rx_buffer, desc, frag_len); + skb = phytmac_build_skb(rx_buffer, frag_len); if (unlikely(!skb)) { netdev_err(pdata->ndev, "rx frame build skb failed\n"); pdata->ndev->stats.rx_dropped++; @@ -932,6 +930,7 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, for (frag = first_frag + 1; ; frag++) { desc = phytmac_get_rx_desc(queue, frag); rx_buffer = phytmac_get_rx_buffer(queue, frag, frag_len); + hw_if->zero_rx_desc_addr(desc); if (offset + frag_len > total_len) { if (unlikely(frag != last_frag)) { diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 9582b587342bb1..1d7c2e17591784 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -940,6 +940,14 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, return 0; } +static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = PHYTMAC_BIT(RX_USED); + + return 0; +} + static void phytmac_tx_start(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; @@ -973,9 +981,19 @@ static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) return PHYTMAC_GET_BITS(desc->desc1, TX_USED); } -static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) { - return (desc->desc0 & PHYTMAC_BIT(RX_USED)) != 0; + dma_addr_t addr; + bool used; + + used = desc->desc0 & PHYTMAC_BIT(RX_USED); + addr = ((u64)(desc->desc2) << 32); + addr |= desc->desc0 & 0xfffffff8; + + if (used != 0 && addr != 0) + return true; + else + return false; } static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) @@ -1024,7 +1042,7 @@ static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int en if (begin > end) tmp = end + queue->pdata->rx_ring_size; - for (frag = begin; frag != end; frag++) { + for (frag = begin; frag != tmp; frag++) { desc = phytmac_get_rx_desc(queue, frag); desc->desc0 &= ~PHYTMAC_BIT(RX_USED); } @@ -1373,6 +1391,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { .rx_pkt_end = phytmac_rx_eof, .clear_rx_desc = phytmac_clear_rx_desc, .clear_tx_desc = phytmac_clear_tx_desc, + .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, /* ptp */ .init_ts_hw = phytmac_ptp_init_hw, .set_time = phytmac_set_time, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index 1f49d4ec79a04a..32bb1294910963 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -374,7 +374,6 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_WOL_RECEIVE_DISABLE_INDEX 28 /* Disable wol_event_recieve */ #define PHYTMAC_WOL_RECEIVE_DISABLE_WIDTH 1 - #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index bcfc8ad68edc10..25f1ba6c1d6ef4 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -934,14 +934,32 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, return 0; } +static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = PHYTMAC_BIT(RXUSED); + + return 0; +} + static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) { return PHYTMAC_GET_BITS(desc->desc1, TXUSED); } -static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) { - return PHYTMAC_GET_BITS(desc->desc0, RXUSED); + dma_addr_t addr; + bool used; + + used = PHYTMAC_GET_BITS(desc->desc0, RXUSED); + addr = ((u64)(desc->desc2) << 32); + addr |= desc->desc0 & 0xfffffff8; + + if (used != 0 && addr != 0) + return true; + else + return false; } static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) @@ -990,7 +1008,7 @@ static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int en if (begin > end) tmp = end + queue->pdata->rx_ring_size; - for (frag = begin; frag != end; frag++) { + for (frag = begin; frag != tmp; frag++) { desc = phytmac_get_rx_desc(queue, frag); desc->desc0 &= ~PHYTMAC_BIT(RXUSED); } @@ -1227,6 +1245,7 @@ struct phytmac_hw_if phytmac_2p0_hw = { .rx_pkt_end = phytmac_rx_eof, .clear_rx_desc = phytmac_clear_rx_desc, .clear_tx_desc = phytmac_clear_tx_desc, + .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, /* ptp */ .init_ts_hw = phytmac_ptp_init_hw, From b96d56db59b13d50fac73d60772ed6a9c18631eb Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Mon, 29 Apr 2024 18:23:45 +0800 Subject: [PATCH 041/258] PHYTIUM: net/phytmac: Bugfix MAC address change when rmmod and insmod module The eth_hw_addr_random function cause systemd-udevd to generate MAC address, which will modify the default MAC address. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie9fa00c3a4c9310104fd888526571df95f15c664 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/6302b0ab85a13bec77c34afd9e0a015ac3b0ceda Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_main.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 6eb1d7d1b8293b..cb9359c7bd4dbb 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.1" +#define PHYTMAC_DRIVER_VERSION "1.0.2" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index d932d011b261fd..732358ea40afef 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2208,7 +2208,6 @@ static int phytmac_init(struct phytmac *pdata) ndev->netdev_ops = &phytmac_netdev_ops; phytmac_set_ethtool_ops(ndev); - eth_hw_addr_random(pdata->ndev); if (ndev->hw_features & NETIF_F_NTUPLE) { INIT_LIST_HEAD(&pdata->rx_fs_list.list); From 8161f6e6efd409e9154b75079f3e51f07416a6f5 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Tue, 21 May 2024 14:20:05 +0800 Subject: [PATCH 042/258] PHYTIUM: net/phytmac: Bugfix MAC 10/100M speed does't work problem Gigabit_mode_enable bit should be clear when the speed is switched to 10M/100M. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I16e54d6e173c15c890788cf0bdcff2facca0f490 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/6b70dbeaafdc06a66410e77bfba11c5595f25a4e Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_ethtool.c | 2 +- drivers/net/ethernet/phytium/phytmac_v1.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index cb9359c7bd4dbb..bc0d2670c2f59a 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.2" +#define PHYTMAC_DRIVER_VERSION "1.0.3" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 4f632591142fcc..a97a4c28c04fea 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -429,7 +429,7 @@ static int phytmac_set_link_ksettings(struct net_device *ndev, int ret = 0; if (!ndev->phydev) { - netdev_err(ndev, "fixed link interface not supported set link\n"); + netdev_err(ndev, "Without a PHY, setting link is not supported\n"); ret = -EOPNOTSUPP; } else { phy_ethtool_set_link_ksettings(ndev, kset); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 1d7c2e17591784..20595aca178343 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -182,7 +182,7 @@ static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); - config &= ~(PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD)); + config &= ~(PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD) | PHYTMAC_BIT(GM_EN)); if (speed == SPEED_100) config |= PHYTMAC_BIT(SPEED); From 8ba8cfa5e9c0128007a46b226a7a176ca3f57814 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 27 Jun 2024 10:09:53 +0800 Subject: [PATCH 043/258] PHYTIUM: net/phytmac: Adapt interface type 2500BASE-x Add 2500BASE-x interface type support for phytmac driver. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I9e0f81c8c9224a89be17714fb8a1b2905499f8bf Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/e1983bd12b88ce9b07660251c9d73d400cbab2e0 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_main.c | 10 +++++++--- drivers/net/ethernet/phytium/phytmac_v1.c | 21 +++++++++++++++------ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index bc0d2670c2f59a..a4e0d4e4fa1f35 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.3" +#define PHYTMAC_DRIVER_VERSION "1.0.4" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 732358ea40afef..43c15b8baf0b72 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1726,6 +1726,9 @@ static void phytmac_mac_link_up(struct phylink_config *config, pdata->pause = rx_pause; } + pdata->speed = speed; + pdata->duplex = duplex; + phytmac_init_ring(pdata); for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) @@ -1844,11 +1847,12 @@ static void phytmac_validate(struct phylink_config *config, if (state->interface == PHY_INTERFACE_MODE_5GBASER) phylink_set(mask, 5000baseT_Full); - if (state->interface == PHY_INTERFACE_MODE_1000BASEX || - state->interface == PHY_INTERFACE_MODE_SGMII || + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + phylink_set(mask, 1000baseX_Full); + + if (state->interface == PHY_INTERFACE_MODE_SGMII || phy_interface_mode_is_rgmii(state->interface)) { phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); phylink_set(mask, 1000baseT_Half); phylink_set(mask, 10baseT_Half); phylink_set(mask, 10baseT_Full); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 20595aca178343..72a6eeaec3563d 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1078,12 +1078,15 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod config |= PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN); if (state->speed == SPEED_1000) config |= PHYTMAC_BIT(GM_EN); - else if (state->speed == SPEED_2500) - config |= PHYTMAC_BIT(2PT5G); + else if (state->speed == SPEED_2500) { + ctrl |= PHYTMAC_BIT(2PT5G); + config |= PHYTMAC_BIT(GM_EN); + } } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { config |= PHYTMAC_BIT(PCS_EN) | PHYTMAC_BIT(GM_EN); } else if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { - config |= PHYTMAC_BIT(2PT5G) | PHYTMAC_BIT(PCS_EN); + ctrl |= PHYTMAC_BIT(2PT5G); + config |= PHYTMAC_BIT(PCS_EN) | PHYTMAC_BIT(GM_EN); } else if (state->interface == PHY_INTERFACE_MODE_10GBASER || state->interface == PHY_INTERFACE_MODE_USXGMII || state->interface == PHY_INTERFACE_MODE_5GBASER) { @@ -1111,11 +1114,17 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod if (old_config ^ config) PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); - /* Disable AN for SGMII fixed link configuration, enable otherwise.*/ - if (state->interface == PHY_INTERFACE_MODE_SGMII) - phytmac_enable_autoneg(pdata, mode == MLO_AN_FIXED ? 0 : 1); + /* Disable AN for SGMII fixed link or speed equal to 2.5G, enable otherwise.*/ + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + if (state->speed == SPEED_2500 || mode == MLO_AN_FIXED) + phytmac_enable_autoneg(pdata, 0); + else + phytmac_enable_autoneg(pdata, 1); + } if (state->interface == PHY_INTERFACE_MODE_1000BASEX) phytmac_enable_autoneg(pdata, 1); + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + phytmac_enable_autoneg(pdata, 0); } static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, From d30907fe665b4e102ad88e15d4ffe618aa3fc87f Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 27 Jun 2024 10:15:06 +0800 Subject: [PATCH 044/258] PHYTIUM: net/phytmac: Support WOL interaction with BIOS It should notify the BIOS to enable or disable the WOL function. This patch adds support for it. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I1c900bc38faff9d0054c38329e55df8dd16e8d28 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/4c06f35e7d80ce9c350cfcfe8f35ec66326a12c0 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 3 +- .../net/ethernet/phytium/phytmac_ethtool.c | 1 + drivers/net/ethernet/phytium/phytmac_main.c | 34 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index a4e0d4e4fa1f35..df4ec49a085122 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.4" +#define PHYTMAC_DRIVER_VERSION "1.0.5" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -607,4 +607,5 @@ int phytmac_drv_resume(struct phytmac *pdata); struct phytmac *phytmac_alloc_pdata(struct device *dev); void phytmac_free_pdata(struct phytmac *pdata); int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size); +void phytmac_set_bios_wol_enable(struct phytmac *pdata, u32 wol); #endif diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index a97a4c28c04fea..306a9934deabd7 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -131,6 +131,7 @@ static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) pdata->wol |= PHYTMAC_WAKE_MCAST; device_set_wakeup_enable(pdata->dev, pdata->wol ? 1 : 0); + phytmac_set_bios_wol_enable(pdata, pdata->wol ? 1 : 0); return 0; } diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 43c15b8baf0b72..fd29c647e2775b 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -111,6 +111,8 @@ static int phytmac_set_mac_address(struct net_device *netdev, void *addr) hw_if->set_mac_address(pdata, saddr->sa_data); + phytmac_set_bios_wol_enable(pdata, pdata->wol ? 1 : 0); + return 0; } @@ -1971,6 +1973,7 @@ static int phytmac_open(struct net_device *ndev) } phylink_start(pdata->phylink); + phytmac_set_bios_wol_enable(pdata, pdata->wol ? 1 : 0); netif_tx_start_all_queues(pdata->ndev); @@ -2121,6 +2124,37 @@ static netdev_features_t phytmac_features_check(struct sk_buff *skb, return features; } +void phytmac_set_bios_wol_enable(struct phytmac *pdata, u32 wol) +{ + struct net_device *ndev = pdata->ndev; + + if (ndev->phydev) { +#ifdef CONFIG_ACPI + if (has_acpi_companion(pdata->dev)) { + acpi_handle handle = ACPI_HANDLE(pdata->dev); + + if (acpi_has_method(handle, "PWOL")) { + union acpi_object args[] = { + { .type = ACPI_TYPE_INTEGER, }, + }; + struct acpi_object_list arg_input = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + + /* Set the input parameters */ + args[0].integer.value = wol; + + status = acpi_evaluate_object(handle, "PWOL", &arg_input, NULL); + if (ACPI_FAILURE(status)) + netdev_err(ndev, "The PWOL method failed to be executed.\n"); + } + } +#endif + } +} + int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size) { int ret = 0; From 29c9301f9545190709d0d6dffade4c9181ad0465 Mon Sep 17 00:00:00 2001 From: zuoqian Date: Wed, 26 Feb 2025 03:50:13 +0000 Subject: [PATCH 045/258] PHYTIUM: net: phytmac: Manage WOL on MAC if PHY supports WOL Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/98c9f77e608579c85080570858a83b2eb8e3f307 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 306a9934deabd7..8bba0af005be88 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -116,7 +116,8 @@ static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) ret = phylink_ethtool_set_wol(pdata->phylink, wol); - if (!ret || ret != -EOPNOTSUPP) + /* Don't manage WoL on MAC, if PHY set_wol() fails */ + if (ret && ret != -EOPNOTSUPP) return ret; pdata->wol = 0; From 51857e748e68629573b272499e533f1b7a7affa5 Mon Sep 17 00:00:00 2001 From: zuoqian Date: Thu, 27 Feb 2025 05:44:58 +0000 Subject: [PATCH 046/258] PHYTIUM: net/phytmac: Bugfix set WOL failed issue Before configuring WOL, we need to obtain the packet types that WOL supports. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/88f00b76168d9ba6ee37c0286d5c750a4c6ab806 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 8bba0af005be88..e1698fa10b0955 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -89,23 +89,23 @@ static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol { struct phytmac *pdata = netdev_priv(ndev); + wol->wolopts = 0; phylink_ethtool_get_wol(pdata->phylink, wol); + wol->supported = WAKE_MAGIC | WAKE_ARP | + WAKE_UCAST | WAKE_MCAST; + if (pdata->wol & PHYTMAC_WAKE_MAGIC) { wol->wolopts |= WAKE_MAGIC; - wol->supported |= WAKE_MAGIC; } if (pdata->wol & PHYTMAC_WAKE_ARP) { wol->wolopts |= WAKE_ARP; - wol->supported |= WAKE_ARP; } if (pdata->wol & PHYTMAC_WAKE_UCAST) { wol->wolopts |= WAKE_UCAST; - wol->supported |= WAKE_UCAST; } if (pdata->wol & PHYTMAC_WAKE_MCAST) { wol->wolopts |= WAKE_MCAST; - wol->supported |= WAKE_MCAST; } } From 5f642b85f0edc6ab1af76497defaef5bdb5d672c Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Thu, 15 Aug 2024 17:06:59 +0800 Subject: [PATCH 047/258] PHYTIUM: net/phytmac: Add support for phytmac v2.0 This patch Adds support for phtymac v2.0,including acpi description, rx tail pointer supporting, power management and other functions. Mainline: NA Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I4758cfb98004683e6ea79079ec263fbc49e46c69 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/3f2b924070a4f3937eef439656d0971eaaa2aa14 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/Makefile | 2 +- drivers/net/ethernet/phytium/phytmac.h | 57 +- .../net/ethernet/phytium/phytmac_ethtool.c | 1 + drivers/net/ethernet/phytium/phytmac_main.c | 104 ++- drivers/net/ethernet/phytium/phytmac_pci.c | 4 +- .../net/ethernet/phytium/phytmac_platform.c | 147 ++++- drivers/net/ethernet/phytium/phytmac_ptp.c | 3 + drivers/net/ethernet/phytium/phytmac_ptp.h | 1 + drivers/net/ethernet/phytium/phytmac_v1.c | 118 +++- drivers/net/ethernet/phytium/phytmac_v1.h | 19 +- drivers/net/ethernet/phytium/phytmac_v2.c | 606 +++++++++++------- drivers/net/ethernet/phytium/phytmac_v2.h | 74 ++- 12 files changed, 796 insertions(+), 340 deletions(-) diff --git a/drivers/net/ethernet/phytium/Makefile b/drivers/net/ethernet/phytium/Makefile index 6e710d4d54b664..debaed63e45388 100644 --- a/drivers/net/ethernet/phytium/Makefile +++ b/drivers/net/ethernet/phytium/Makefile @@ -1,9 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. # # Makefile for the Phytium network device drivers. # -# obj-$(CONFIG_PHYTMAC) += phytmac.o diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index df4ec49a085122..3806e09bc1220a 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -1,4 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #ifndef _PHYTMAC_H #define _PHYTMAC_H @@ -13,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.5" +#define PHYTMAC_DRIVER_VERSION "1.0.29" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -44,6 +46,8 @@ #define PHYTMAC_POWEROFF 1 #define PHYTMAC_POWERON 2 +#define PHYTMAC_PWCTL_GMAC_ID 6 +#define PHYTMAC_PWCTL_DEFAULT_VAL 0 #define PHYTMAC_WOL_MAGIC_PACKET 1 @@ -58,8 +62,12 @@ #define PHYTMAC_CAPS_TAILPTR 0x00000040 #define PHYTMAC_CAPS_START 0x00000080 #define PHYTMAC_CAPS_NO_WOL 0x0000100 -#define PHYTMAC_CAPS_LPI 0x0000400 +#define PHYTMAC_CAPS_PWCTRL 0x0000200 #define PHYTMAC_CAPS_MSG 0x0000800 +#define PHYTMAC_CAPS_RXPTR 0x0001000 + +#define VERSION_V0 0 +#define VERSION_V3 0x3 #define PHYTMAC_TX 0x1 #define PHYTMAC_RX 0x2 @@ -122,6 +130,39 @@ #define PHYTMAC_WAKE_UCAST 0x00000004 #define PHYTMAC_WAKE_MCAST 0x00000008 +enum phytmac_interface { + PHYTMAC_PHY_INTERFACE_MODE_NA, + PHYTMAC_PHY_INTERFACE_MODE_INTERNAL, + PHYTMAC_PHY_INTERFACE_MODE_MII, + PHYTMAC_PHY_INTERFACE_MODE_GMII, + PHYTMAC_PHY_INTERFACE_MODE_SGMII, + PHYTMAC_PHY_INTERFACE_MODE_TBI, + PHYTMAC_PHY_INTERFACE_MODE_REVMII, + PHYTMAC_PHY_INTERFACE_MODE_RMII, + PHYTMAC_PHY_INTERFACE_MODE_RGMII, + PHYTMAC_PHY_INTERFACE_MODE_RGMII_ID, + PHYTMAC_PHY_INTERFACE_MODE_RGMII_RXID, + PHYTMAC_PHY_INTERFACE_MODE_RGMII_TXID, + PHYTMAC_PHY_INTERFACE_MODE_RTBI, + PHYTMAC_PHY_INTERFACE_MODE_SMII, + PHYTMAC_PHY_INTERFACE_MODE_XGMII, + PHYTMAC_PHY_INTERFACE_MODE_MOCA, + PHYTMAC_PHY_INTERFACE_MODE_QSGMII, + PHYTMAC_PHY_INTERFACE_MODE_TRGMII, + PHYTMAC_PHY_INTERFACE_MODE_100BASEX, + PHYTMAC_PHY_INTERFACE_MODE_1000BASEX, + PHYTMAC_PHY_INTERFACE_MODE_2500BASEX, + PHYTMAC_PHY_INTERFACE_MODE_5GBASER, + PHYTMAC_PHY_INTERFACE_MODE_RXAUI, + PHYTMAC_PHY_INTERFACE_MODE_XAUI, + /* 10GBASE-R, XFI, SFI - single lane 10G Serdes */ + PHYTMAC_PHY_INTERFACE_MODE_10GBASER, + PHYTMAC_PHY_INTERFACE_MODE_USXGMII, + /* 10GBASE-KR - with Clause 73 AN */ + PHYTMAC_PHY_INTERFACE_MODE_10GKR, + PHYTMAC_PHY_INTERFACE_MODE_MAX, +}; + struct packet_info { int lso; int desc_cnt; @@ -214,7 +255,6 @@ static const struct phytmac_statistics queue_statistics[] = { struct phytmac_config { struct phytmac_hw_if *hw_if; u32 caps; - u32 tsu_rate; u16 queue_num; }; @@ -367,8 +407,8 @@ struct phytmac_msg { u32 tx_msg_tail; u32 rx_msg_head; u32 rx_msg_tail; - /* Lock to protect msg */ - spinlock_t msg_lock; + /*use msg_mutex to protect msg */ + struct mutex msg_mutex; }; struct ts_ctrl { @@ -401,7 +441,6 @@ struct phytmac { u32 min_tx_length; u32 jumbo_len; u32 wol; - u32 lpi; u32 power_state; struct work_struct restart_task; /* Lock to protect mac config */ @@ -430,6 +469,7 @@ struct phytmac { struct phylink_pcs phylink_pcs; int pause; phy_interface_t phy_interface; + enum phytmac_interface phytmac_v2_interface; int speed; int duplex; int autoneg; @@ -446,6 +486,7 @@ struct phytmac { /* Lock to protect fs */ spinlock_t rx_fs_lock; unsigned int max_rx_fs; + u32 version; }; struct phytmac_hw_if { @@ -507,7 +548,7 @@ struct phytmac_hw_if { void (*init_rx_map)(struct phytmac_queue *queue, u32 index); unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); void (*transmit)(struct phytmac_queue *queue); - void (*restart)(struct phytmac *pdata); + void (*update_rx_tail)(struct phytmac_queue *queue); int (*tx_complete)(const struct phytmac_dma_desc *desc); bool (*rx_complete)(const struct phytmac_dma_desc *desc); int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); @@ -517,6 +558,7 @@ struct phytmac_hw_if { bool (*rx_pkt_start)(const struct phytmac_dma_desc *desc); bool (*rx_pkt_end)(const struct phytmac_dma_desc *desc); unsigned int (*zero_rx_desc_addr)(struct phytmac_dma_desc *desc); + unsigned int (*zero_tx_desc)(struct phytmac_dma_desc *desc); void (*clear_rx_desc)(struct phytmac_queue *queue, int begin, int end); void (*clear_tx_desc)(struct phytmac_queue *queue); /* ptp */ @@ -604,6 +646,7 @@ int phytmac_drv_probe(struct phytmac *pdata); int phytmac_drv_remove(struct phytmac *pdata); int phytmac_drv_suspend(struct phytmac *pdata); int phytmac_drv_resume(struct phytmac *pdata); +void phytmac_drv_shutdown(struct phytmac *pdata); struct phytmac *phytmac_alloc_pdata(struct device *dev); void phytmac_free_pdata(struct phytmac *pdata); int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size); diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index e1698fa10b0955..e05274b961e4b1 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ #include #include diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index fd29c647e2775b..dcede058512c65 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2,6 +2,8 @@ /* * Phytium Ethernet Controller driver * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -125,10 +127,12 @@ static int phytmac_get_mac_address(struct phytmac *pdata) if (is_valid_ether_addr(addr)) { eth_hw_addr_set(pdata->ndev, addr); + ether_addr_copy(pdata->ndev->perm_addr, addr); return 0; } dev_info(pdata->dev, "invalid hw address, using random\n"); eth_hw_addr_random(pdata->ndev); + ether_addr_copy(pdata->ndev->perm_addr, pdata->ndev->dev_addr); return 0; } @@ -1033,6 +1037,8 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) queue->rx_next_to_alloc = queue->rx_head; /* make newly descriptor to hardware */ wmb(); + + hw_if->update_rx_tail(queue); } static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, @@ -1143,7 +1149,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) unsigned int tail = queue->tx_tail; unsigned int head; - spin_lock(&pdata->lock); + spin_lock(&queue->tx_lock); for (head = queue->tx_head; head != tail && packet_count < budget; ) { struct sk_buff *skb; @@ -1180,7 +1186,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) packet_count++; } - /* Now we can safely release resources */ + /* Now we can safely release resources */ phytmac_tx_unmap(pdata, tx_skb, budget); if (complete) { @@ -1198,7 +1204,7 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) if (__netif_subqueue_stopped(pdata->ndev, queue_index) && (phytmac_maybe_wake_tx_queue(queue))) netif_wake_subqueue(pdata->ndev, queue_index); - spin_unlock(&pdata->lock); + spin_unlock(&queue->tx_lock); return packet_count; } @@ -1281,6 +1287,7 @@ static inline int phytmac_clear_csum(struct sk_buff *skb) static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) { +#ifdef PHYTMAC_SW_FCS bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) || skb_is_nonlinear(*skb); int padlen = ETH_ZLEN - (*skb)->len; @@ -1290,7 +1297,7 @@ static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) u32 fcs; int i; - if ((ndev->features & NETIF_F_HW_CSUM) || + if (!(ndev->features & NETIF_F_HW_CSUM) || !((*skb)->ip_summed != CHECKSUM_PARTIAL) || skb_shinfo(*skb)->gso_size || phytmac_ptp_one_step(*skb)) return 0; @@ -1327,6 +1334,8 @@ static int phytmac_add_fcs(struct sk_buff **skb, struct net_device *ndev) for (i = 0; i < 4; ++i) skb_put_u8(*skb, (fcs >> (i * 8)) & 0xff); +#endif + return 0; } @@ -1376,13 +1385,13 @@ static int phytmac_packet_info(struct phytmac *pdata, desc_cnt += TXD_USE_COUNT(pdata, skb_frag_size(&skb_shinfo(skb)->frags[f])); packet->desc_cnt = desc_cnt; - if ((!(pdata->ndev->features & NETIF_F_HW_CSUM)) && +#ifdef PHYTMAC_SW_FCS + if ((pdata->ndev->features & NETIF_F_HW_CSUM) && skb->ip_summed != CHECKSUM_PARTIAL && !is_lso && !phytmac_ptp_one_step(skb)) packet->nocrc = 1; - else - packet->nocrc = 0; +#endif if (netif_msg_pktdata(pdata)) { netdev_info(pdata->ndev, "packet info: desc_cnt=%d, nocrc=%d,ip_summed=%d\n", @@ -1522,7 +1531,6 @@ static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *nd struct phytmac_queue *queue = &pdata->queues[queue_index]; netdev_tx_t ret = NETDEV_TX_OK; struct packet_info packet; - unsigned long flags; if (phytmac_clear_csum(skb)) { dev_kfree_skb_any(skb); @@ -1543,7 +1551,7 @@ static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *nd if (netif_msg_pktdata(pdata)) phytmac_dump_pkt(pdata, skb, true); - spin_lock_irqsave(&pdata->lock, flags); + spin_lock_bh(&queue->tx_lock); /* Check that there are enough descriptors available */ ret = phytmac_maybe_stop_tx_queue(queue, packet.desc_cnt); if (ret) @@ -1562,7 +1570,7 @@ static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *nd hw_if->transmit(queue); tx_return: - spin_unlock_irqrestore(&pdata->lock, flags); + spin_unlock_bh(&queue->tx_lock); return ret; } @@ -1668,6 +1676,7 @@ static void phytmac_mac_link_down(struct phylink_config *config, unsigned int mo unsigned int q; unsigned long flags; struct phytmac_tx_skb *tx_skb; + struct phytmac_dma_desc *tx_desc = NULL; int i; if (netif_msg_link(pdata)) { @@ -1686,18 +1695,22 @@ static void phytmac_mac_link_down(struct phylink_config *config, unsigned int mo /* Disable Rx and Tx */ hw_if->enable_network(pdata, false, PHYTMAC_RX | PHYTMAC_TX); + spin_unlock_irqrestore(&pdata->lock, flags); /* Tx clean */ for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + spin_lock_bh(&queue->tx_lock); for (i = 0; i < pdata->tx_ring_size; i++) { tx_skb = phytmac_get_tx_skb(queue, i); if (tx_skb) phytmac_tx_unmap(pdata, tx_skb, 0); + + tx_desc = phytmac_get_tx_desc(queue, i); + hw_if->zero_tx_desc(tx_desc); } + spin_unlock_bh(&queue->tx_lock); } - spin_unlock_irqrestore(&pdata->lock, flags); - netif_tx_stop_all_queues(ndev); } @@ -1890,6 +1903,8 @@ static int phytmac_phylink_create(struct phytmac *pdata) pdata->phylink_config.dev = &pdata->ndev->dev; pdata->phylink_config.type = PHYLINK_NETDEV; + pdata->phylink_config.mac_managed_pm = true; + if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII || pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX || pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX || @@ -1923,10 +1938,6 @@ static int phytmac_open(struct net_device *ndev) if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "open\n"); - /* phytmac_powerup */ - if (pdata->power_state == PHYTMAC_POWEROFF) - hw_if->poweron(pdata, PHYTMAC_POWERON); - if (hw_if->init_msg_ring) hw_if->init_msg_ring(pdata); @@ -2033,10 +2044,6 @@ static int phytmac_close(struct net_device *ndev) if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) phytmac_ptp_unregister(pdata); - /* phytmac_powerup */ - if (pdata->power_state == PHYTMAC_POWERON) - hw_if->poweron(pdata, PHYTMAC_POWEROFF); - return 0; } @@ -2309,6 +2316,7 @@ int phytmac_drv_probe(struct phytmac *pdata) { struct net_device *ndev = pdata->ndev; struct device *dev = pdata->dev; + struct phytmac_hw_if *hw_if = pdata->hw_if; int ret = 0; if (netif_msg_probe(pdata)) @@ -2332,12 +2340,13 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_out; } - netif_carrier_off(ndev); - ret = register_netdev(ndev); - if (ret) { - dev_err(pdata->dev, "Cannot register net device, aborting.\n"); - goto err_out; - } + if (pdata->power_state == PHYTMAC_POWEROFF) + hw_if->poweron(pdata, PHYTMAC_POWERON); + + if (hw_if->init_msg_ring) + hw_if->init_msg_ring(pdata); + + mutex_init(&pdata->msg_ring.msg_mutex); if (pdata->use_mii && !pdata->mii_bus) { ret = phytmac_mdio_register(pdata); @@ -2359,6 +2368,13 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_phylink_init; } + ret = register_netdev(ndev); + if (ret) { + dev_err(pdata->dev, "Cannot register net device, aborting.\n"); + goto err_phylink_init; + } + netif_carrier_off(ndev); + if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "probe successfully! Phytium %s at 0x%08lx irq %d (%pM)\n", "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); @@ -2373,8 +2389,6 @@ int phytmac_drv_probe(struct phytmac *pdata) if (pdata->mii_bus) mdiobus_free(pdata->mii_bus); - unregister_netdev(ndev); - err_out: return ret; } @@ -2383,12 +2397,15 @@ EXPORT_SYMBOL_GPL(phytmac_drv_probe); int phytmac_drv_remove(struct phytmac *pdata) { struct net_device *ndev = pdata->ndev; + struct phytmac_hw_if *hw_if = pdata->hw_if; if (ndev) { if (pdata->use_ncsi && pdata->ncsidev) ncsi_unregister_dev(pdata->ncsidev); unregister_netdev(ndev); + if (pdata->power_state == PHYTMAC_POWERON) + hw_if->poweron(pdata, PHYTMAC_POWEROFF); if (pdata->use_mii && pdata->mii_bus) { mdiobus_unregister(pdata->mii_bus); @@ -2397,6 +2414,8 @@ int phytmac_drv_remove(struct phytmac *pdata) if (pdata->phylink) phylink_destroy(pdata->phylink); + + mutex_destroy(&pdata->msg_ring.msg_mutex); } return 0; @@ -2422,12 +2441,15 @@ int phytmac_drv_suspend(struct phytmac *pdata) /* napi_disable */ for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { + hw_if->disable_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); + hw_if->clear_irq(pdata, queue->index, pdata->rx_irq_mask | pdata->tx_irq_mask); napi_disable(&queue->tx_napi); napi_disable(&queue->rx_napi); } if (pdata->wol) { hw_if->set_wol(pdata, pdata->wol); + pdata->power_state = PHYTMAC_POWEROFF; } else { rtnl_lock(); phylink_stop(pdata->phylink); @@ -2449,15 +2471,14 @@ int phytmac_drv_resume(struct phytmac *pdata) struct phytmac_hw_if *hw_if = pdata->hw_if; struct ethtool_rx_fs_item *item; - if (!netif_running(pdata->ndev)) - return 0; - - if (pdata->power_state == PHYTMAC_POWEROFF) - hw_if->poweron(pdata, PHYTMAC_POWERON); + hw_if->poweron(pdata, PHYTMAC_POWERON); if (hw_if->init_msg_ring) hw_if->init_msg_ring(pdata); + if (!netif_running(pdata->ndev)) + return 0; + if (pdata->wol) { hw_if->set_wol(pdata, 0); rtnl_lock(); @@ -2520,6 +2541,23 @@ void phytmac_free_pdata(struct phytmac *pdata) } EXPORT_SYMBOL_GPL(phytmac_free_pdata); +void phytmac_drv_shutdown(struct phytmac *pdata) +{ + struct net_device *netdev = pdata->ndev; + struct phytmac_hw_if *hw_if = pdata->hw_if; + + rtnl_lock(); + netif_device_detach(netdev); + + if (netif_running(netdev)) + phytmac_close(netdev); + rtnl_unlock(); + + if (pdata->power_state == PHYTMAC_POWERON) + hw_if->poweron(pdata, PHYTMAC_POWEROFF); +} +EXPORT_SYMBOL_GPL(phytmac_drv_shutdown); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index 60bd296d8f0b6c..62766c49d1a626 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -2,6 +2,9 @@ /* * Phytium GMAC PCI wrapper. * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * + * Author: Wenting Song */ #include @@ -21,7 +24,6 @@ struct phytmac_data { struct phytmac_hw_if *hw_if; u32 caps; - u32 tsu_rate; u16 queue_num; int speed; bool duplex; diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 9390056fdc7a95..95509711894b05 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -2,6 +2,9 @@ /* * Phytium GMAC Platform wrapper. * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * + * Author: Wenting Song */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -21,18 +24,17 @@ static const struct phytmac_config phytium_1p0_config = { | PHYTMAC_CAPS_JUMBO | PHYTMAC_CAPS_LSO, .queue_num = 4, - .tsu_rate = 300000000, }; static const struct phytmac_config phytium_2p0_config = { .hw_if = &phytmac_2p0_hw, .caps = PHYTMAC_CAPS_TAILPTR - | PHYTMAC_CAPS_LPI + | PHYTMAC_CAPS_RXPTR + | PHYTMAC_CAPS_PWCTRL | PHYTMAC_CAPS_LSO | PHYTMAC_CAPS_MSG | PHYTMAC_CAPS_JUMBO, .queue_num = 2, - .tsu_rate = 300000000, }; #if defined(CONFIG_OF) @@ -47,7 +49,8 @@ MODULE_DEVICE_TABLE(of, phytmac_dt_ids); #ifdef CONFIG_ACPI static const struct acpi_device_id phytmac_acpi_ids[] = { { .id = "PHYT0046", .driver_data = (kernel_ulong_t)&phytium_1p0_config }, - { } + { .id = "PHYT0056", .driver_data = (kernel_ulong_t)&phytium_2p0_config }, + {} }; MODULE_DEVICE_TABLE(acpi, phytmac_acpi_ids); @@ -55,6 +58,95 @@ MODULE_DEVICE_TABLE(acpi, phytmac_acpi_ids); #define phytmac_acpi_ids NULL #endif +static const char *phytmac_phy_modes(enum phytmac_interface interface) +{ + switch (interface) { + case PHYTMAC_PHY_INTERFACE_MODE_NA: + return ""; + case PHYTMAC_PHY_INTERFACE_MODE_INTERNAL: + return "internal"; + case PHYTMAC_PHY_INTERFACE_MODE_MII: + return "mii"; + case PHYTMAC_PHY_INTERFACE_MODE_GMII: + return "gmii"; + case PHYTMAC_PHY_INTERFACE_MODE_SGMII: + return "sgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_TBI: + return "tbi"; + case PHYTMAC_PHY_INTERFACE_MODE_REVMII: + return "rev-mii"; + case PHYTMAC_PHY_INTERFACE_MODE_RMII: + return "rmii"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII: + return "rgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII_ID: + return "rgmii-id"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII_RXID: + return "rgmii-rxid"; + case PHYTMAC_PHY_INTERFACE_MODE_RGMII_TXID: + return "rgmii-txid"; + case PHYTMAC_PHY_INTERFACE_MODE_RTBI: + return "rtbi"; + case PHYTMAC_PHY_INTERFACE_MODE_SMII: + return "smii"; + case PHYTMAC_PHY_INTERFACE_MODE_XGMII: + return "xgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_MOCA: + return "moca"; + case PHYTMAC_PHY_INTERFACE_MODE_QSGMII: + return "qsgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_TRGMII: + return "trgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_100BASEX: + return "100base-x"; + case PHYTMAC_PHY_INTERFACE_MODE_1000BASEX: + return "1000base-x"; + case PHYTMAC_PHY_INTERFACE_MODE_2500BASEX: + return "2500base-x"; + case PHYTMAC_PHY_INTERFACE_MODE_5GBASER: + return "5gbase-r"; + case PHYTMAC_PHY_INTERFACE_MODE_RXAUI: + return "rxaui"; + case PHYTMAC_PHY_INTERFACE_MODE_XAUI: + return "xaui"; + case PHYTMAC_PHY_INTERFACE_MODE_10GBASER: + return "10gbase-r"; + case PHYTMAC_PHY_INTERFACE_MODE_USXGMII: + return "usxgmii"; + case PHYTMAC_PHY_INTERFACE_MODE_10GKR: + return "10gbase-kr"; + default: + return "unknown"; + } +} + +static int phytmac_v2_get_phy_mode(struct platform_device *pdev) +{ + const char *pm; + int err, i; + int phy_interface; + + err = device_property_read_string(&pdev->dev, "phy-mode", &pm); + if (err < 0) + return err; + + phy_interface = PHYTMAC_PHY_INTERFACE_MODE_MAX + 1; + for (i = 0; i < PHYTMAC_PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(pm, phytmac_phy_modes(i))) { + phy_interface = i; + dev_notice(&pdev->dev, "Phy mode is %s.\n", pm); + break; + } + } + + if (phy_interface > PHYTMAC_PHY_INTERFACE_MODE_MAX) { + dev_err(&pdev->dev, "Invalid phy mode value: %s!\n", pm); + return -EINVAL; + } + + return phy_interface; +} + static int phytmac_get_phy_mode(struct platform_device *pdev) { const char *pm; @@ -80,6 +172,8 @@ static int phytmac_plat_probe(struct platform_device *pdev) struct phytmac *pdata; int ret, i; u32 queue_num; + const struct of_device_id *match = NULL; + const struct acpi_device_id *match_acpi = NULL; pdata = phytmac_alloc_pdata(&pdev->dev); if (IS_ERR(pdata)) { @@ -92,8 +186,6 @@ static int phytmac_plat_probe(struct platform_device *pdev) pdata->platdev = pdev; if (pdev->dev.of_node) { - const struct of_device_id *match; - match = of_match_node(phytmac_dt_ids, np); if (match && match->data) { phytmac_config = match->data; @@ -102,11 +194,9 @@ static int phytmac_plat_probe(struct platform_device *pdev) pdata->queues_max_num = phytmac_config->queue_num; } } else if (has_acpi_companion(&pdev->dev)) { - const struct acpi_device_id *match; - - match = acpi_match_device(phytmac_acpi_ids, &pdev->dev); - if (match && match->driver_data) { - phytmac_config = (void *)match->driver_data; + match_acpi = acpi_match_device(phytmac_acpi_ids, &pdev->dev); + if (match_acpi && match_acpi->driver_data) { + phytmac_config = (void *)match_acpi->driver_data; pdata->hw_if = phytmac_config->hw_if; pdata->capacities = phytmac_config->caps; pdata->queues_max_num = phytmac_config->queue_num; @@ -122,6 +212,18 @@ static int phytmac_plat_probe(struct platform_device *pdev) } pdata->ndev->base_addr = regs->start; + if (pdev->dev.of_node && match) { + if (!strcmp(match->compatible, "phytium,gmac-1.0")) + pdata->version = PHYTMAC_READ(pdata, PHYTMAC_VERSION) & 0xff; + else + pdata->version = VERSION_V3; + } else if (has_acpi_companion(&pdev->dev) && match_acpi) { + if (!strcmp(match_acpi->id, "PHYT0046")) + pdata->version = PHYTMAC_READ(pdata, PHYTMAC_VERSION) & 0xff; + else + pdata->version = VERSION_V3; + } + if (pdata->capacities & PHYTMAC_CAPS_MSG) { ++i; regs = platform_get_resource(pdev, IORESOURCE_MEM, i); @@ -134,11 +236,10 @@ static int phytmac_plat_probe(struct platform_device *pdev) } } - if (device_property_read_bool(&pdev->dev, "lpi")) - pdata->capacities |= PHYTMAC_CAPS_LPI; + if (device_property_read_bool(&pdev->dev, "powerctrl")) + pdata->capacities |= PHYTMAC_CAPS_PWCTRL; - if (pdata->capacities & PHYTMAC_CAPS_LPI) { - /* lpi resource */ + if (pdata->version == VERSION_V3 && pdev->dev.of_node) { ++i; regs = platform_get_resource(pdev, IORESOURCE_MEM, i); if (regs) { @@ -181,6 +282,14 @@ static int phytmac_plat_probe(struct platform_device *pdev) else pdata->phy_interface = ret; + if (pdata->version == VERSION_V3) { + ret = phytmac_v2_get_phy_mode(pdev); + if (ret < 0) + pdata->phytmac_v2_interface = PHYTMAC_PHY_INTERFACE_MODE_USXGMII; + else + pdata->phytmac_v2_interface = ret; + } + ret = phytmac_drv_probe(pdata); if (ret) goto err_mem; @@ -212,6 +321,13 @@ static int phytmac_plat_remove(struct platform_device *pdev) return 0; } +static void phytmac_plat_shutdown(struct platform_device *pdev) +{ + struct phytmac *pdata = platform_get_drvdata(pdev); + + phytmac_drv_shutdown(pdata); +} + static int __maybe_unused phytmac_plat_suspend(struct device *dev) { struct phytmac *pdata = dev_get_drvdata(dev); @@ -245,6 +361,7 @@ static struct platform_driver phytmac_driver = { .acpi_match_table = phytmac_acpi_ids, .pm = &phytmac_plat_pm_ops, }, + .shutdown = phytmac_plat_shutdown, }; module_platform_driver(phytmac_driver); diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.c b/drivers/net/ethernet/phytium/phytmac_ptp.c index 26b1b75edbde1d..4803842bd7c17c 100644 --- a/drivers/net/ethernet/phytium/phytmac_ptp.c +++ b/drivers/net/ethernet/phytium/phytmac_ptp.c @@ -2,6 +2,9 @@ /** * 1588 PTP support for Phytium GMAC device. * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. + * + * Author: Wenting Song */ #include #include diff --git a/drivers/net/ethernet/phytium/phytmac_ptp.h b/drivers/net/ethernet/phytium/phytmac_ptp.h index 72c8b7c6741370..e9497a172d25e2 100644 --- a/drivers/net/ethernet/phytium/phytmac_ptp.h +++ b/drivers/net/ethernet/phytium/phytmac_ptp.h @@ -2,6 +2,7 @@ /* * Phytium Ethernet Controller driver * + * Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 72a6eeaec3563d..4fc0bfa2f696ec 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include "phytmac.h" #include "phytmac_v1.h" @@ -211,6 +213,11 @@ static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, else PHYTMAC_WRITE(pdata, PHYTMAC_HCONFIG, PHYTMAC_SPEED_100M); + if (interface == PHY_INTERFACE_MODE_SGMII) { + if (speed == SPEED_2500) + phytmac_enable_autoneg(pdata, 0); + } + return 0; } @@ -289,7 +296,6 @@ static int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) static void phytmac_reset_hw(struct phytmac *pdata) { - struct phytmac_queue *queue; unsigned int q; u32 ctrl; @@ -300,7 +306,7 @@ static void phytmac_reset_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, ctrl); /* Disable and clear all interrupts and disable queues */ - for (q = 0, queue = pdata->queues; q < pdata->queues_max_num; ++q, ++queue) { + for (q = 0; q < pdata->queues_max_num; ++q) { if (q == 0) { PHYTMAC_WRITE(pdata, PHYTMAC_ID, -1); PHYTMAC_WRITE(pdata, PHYTMAC_IS, -1); @@ -317,7 +323,10 @@ static void phytmac_reset_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), 0); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(q), 0); + + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_RX_TAILPTR(q), 0); } } @@ -352,6 +361,7 @@ static int phytmac_init_hw(struct phytmac *pdata) u32 config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); u32 dmaconfig; u32 nctrlconfig; + u32 ptrconfig = 0; nctrlconfig = PHYTMAC_READ(pdata, PHYTMAC_NCTRL); nctrlconfig |= PHYTMAC_BIT(MPE); @@ -414,7 +424,14 @@ static int phytmac_init_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, dmaconfig); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, PHYTMAC_BIT(TXTAIL_ENABLE)); + ptrconfig |= PHYTMAC_BIT(TXPTR_EN); + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + ptrconfig |= PHYTMAC_BIT(RXPTR_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, ptrconfig); + + if (pdata->capacities & PHYTMAC_CAPS_START) + PHYTMAC_WRITE(pdata, PHYTMAC_TXSTARTSEL, PHYTMAC_BIT(TSTARTSEL_ENABLE)); if (phy_interface_mode_is_8023z(pdata->phy_interface)) phytmac_pcs_software_reset(pdata, 1); @@ -426,8 +443,46 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) { u32 status, data0, data1, rdata1; int ret; + acpi_handle handle; + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status acpi_sts; + unsigned long long rv; + + if (!(pdata->capacities & PHYTMAC_CAPS_PWCTRL)) { + pdata->power_state = on; + return 0; + } + + if (has_acpi_companion(pdata->dev)) { + handle = ACPI_HANDLE(pdata->dev); - if (pdata->capacities & PHYTMAC_CAPS_LPI) { + netdev_info(pdata->ndev, "set gmac power %s\n", + on == PHYTMAC_POWERON ? "on" : "off"); + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = PHYTMAC_PWCTL_GMAC_ID; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + + if (on == PHYTMAC_POWERON) { + acpi_sts = acpi_evaluate_integer(handle, "PPWO", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWO Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power on\n"); + } else { + acpi_sts = acpi_evaluate_integer(handle, "PPWD", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWD Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power off\n"); + } + } else { ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, 1, PHYTMAC_TIMEOUT); if (ret) @@ -460,16 +515,17 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) data0 & PHYTMAC_BIT(DATA0_FREE), 1, PHYTMAC_TIMEOUT); if (ret) - netdev_err(pdata->ndev, "mnh data0 is busy"); + netdev_err(pdata->ndev, "mnh data0 is busy\n"); rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); if (rdata1 == data1) netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); else netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); } + pdata->power_state = on; return 0; @@ -626,6 +682,9 @@ static int phytmac_get_feature_all(struct phytmac *pdata) /* max rx fs */ pdata->max_rx_fs = PHYTMAC_READ_BITS(pdata, PHYTMAC_DEFAULT3, SCR2CMP); + if (pdata->version == VERSION_V3) + pdata->capacities |= PHYTMAC_CAPS_RXPTR; + if (netif_msg_hw(pdata)) netdev_info(pdata->ndev, "get feature queue_num=%d, daw=%d, dbw=%d, rx_fs=%d, rx_bd=%d, tx_bd=%d\n", pdata->queues_num, pdata->dma_addr_width, pdata->dma_data_width, @@ -756,7 +815,7 @@ static int phytmac_init_ring_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_RXPTRH(q), upper_32_bits(queue->rx_ring_addr)); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(q), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(q), queue->tx_tail); } return 0; @@ -928,7 +987,8 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); /* Make newly descriptor to hardware */ - dma_wmb(); + if (!(pdata->capacities & PHYTMAC_CAPS_RXPTR)) + dma_wmb(); desc->desc0 = lower_32_bits(addr); } else { desc->desc1 = 0; @@ -948,32 +1008,33 @@ static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) return 0; } +static unsigned int phytmac_zero_tx_desc(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = 0; + desc->desc1 &= ~PHYTMAC_BIT(TX_USED); + + return 0; +} + static void phytmac_tx_start(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(queue->index), PHYTMAC_BIT(TXTAIL_UPDATE) | queue->tx_tail); - if (pdata->capacities & PHYTMAC_CAPS_START) - PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, - PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); + if (likely(pdata->capacities & PHYTMAC_CAPS_START)) + PHYTMAC_WRITE(pdata, PHYTMAC_TXSTART, PHYTMAC_BIT(TSTART)); } -static void phytmac_restart(struct phytmac *pdata) +static void phytmac_update_rx_tail(struct phytmac_queue *queue) { - int q; - struct phytmac_queue *queue; + struct phytmac *pdata = queue->pdata; - for (q = 0; q < pdata->queues_num; q++) { - queue = &pdata->queues[q]; - if (queue->tx_head != queue->tx_tail) { - PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, - PHYTMAC_READ(pdata, PHYTMAC_NCTRL) | PHYTMAC_BIT(TSTART)); - break; - } - } + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_RX_TAILPTR(queue->index), queue->rx_head); } static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) @@ -1116,7 +1177,7 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod /* Disable AN for SGMII fixed link or speed equal to 2.5G, enable otherwise.*/ if (state->interface == PHY_INTERFACE_MODE_SGMII) { - if (state->speed == SPEED_2500 || mode == MLO_AN_FIXED) + if (mode == MLO_AN_FIXED) phytmac_enable_autoneg(pdata, 0); else phytmac_enable_autoneg(pdata, 1); @@ -1158,7 +1219,7 @@ static void phytmac_clear_tx_desc(struct phytmac_queue *queue) desc->desc1 |= PHYTMAC_BIT(TX_WRAP); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_TAILPTR(queue->index), queue->tx_tail); } static void phytmac_get_hw_stats(struct phytmac *pdata) @@ -1388,7 +1449,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { /* tx and rx */ .tx_map = phytmac_tx_map_desc, .transmit = phytmac_tx_start, - .restart = phytmac_restart, + .update_rx_tail = phytmac_update_rx_tail, .tx_complete = phytmac_tx_complete, .rx_complete = phytmac_rx_complete, .get_rx_pkt_len = phytmac_rx_pkt_len, @@ -1401,6 +1462,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { .clear_rx_desc = phytmac_clear_rx_desc, .clear_tx_desc = phytmac_clear_tx_desc, .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, + .zero_tx_desc = phytmac_zero_tx_desc, /* ptp */ .init_ts_hw = phytmac_ptp_init_hw, .set_time = phytmac_set_time, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index 32bb1294910963..e0ca0ab8354749 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -1,4 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #ifndef _PHYTMAC_V1_H #define _PHYTMAC_V1_H @@ -53,6 +55,9 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_USXSTATUS 0x0A88 #define PHYTMAC_TXBDCTRL 0x04CC #define PHYTMAC_RXBDCTRL 0x04D0 +#define PHYTMAC_TXSTART 0x0E70 +#define PHYTMAC_TXSTARTSEL 0x0E74 +#define PHYTMAC_VERSION 0x0FF0 /* Fdir match registers */ #define PHYTMAC_FDIR(i) (0x0540 + ((i) << 2)) @@ -75,7 +80,8 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_IDR(i) (0x0620 + ((i) << 2)) #define PHYTMAC_IMR(i) (0x0640 + ((i) << 2)) #define PHYTMAC_TAIL_ENABLE (0x0e7c) -#define PHYTMAC_TAILPTR(i) (0x0e80 + ((i) << 2)) +#define PHYTMAC_TX_TAILPTR(i) (0x0e80 + ((i) << 2)) +#define PHYTMAC_RX_TAILPTR(i) (0x0ec0 + ((i) << 2)) #define PHYTMAC_PHY_INT_ENABLE 0x1C88 #define PHYTMAC_PHY_INT_CLEAR 0x1C8C @@ -363,8 +369,11 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_TXTAIL_UPDATE_WIDTH 1 /* Bitfields in TAIL_ENABLE */ -#define PHYTMAC_TXTAIL_ENABLE_INDEX 0 /* Enable tx tail */ -#define PHYTMAC_TXTAIL_ENABLE_WIDTH 1 +#define PHYTMAC_TXPTR_EN_INDEX 0 /* Enable tx tail */ +#define PHYTMAC_TXPTR_EN_WIDTH 1 + +#define PHYTMAC_RXPTR_EN_INDEX 16 /* Enable rx tail */ +#define PHYTMAC_RXPTR_EN_WIDTH 1 /* Bitfields in INT ENABLE */ #define PHYTMAC_WOL_RECEIVE_ENABLE_INDEX 28 /* Enable wol_event_recieve */ @@ -374,6 +383,10 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_WOL_RECEIVE_DISABLE_INDEX 28 /* Disable wol_event_recieve */ #define PHYTMAC_WOL_RECEIVE_DISABLE_WIDTH 1 +/* Bitfields in PHYTMAC_TXSTARTSEL */ +#define PHYTMAC_TSTARTSEL_ENABLE_INDEX 0 /* Enable Tstart_sel */ +#define PHYTMAC_TSTARTSEL_ENABLE_WIDTH 1 + #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 25f1ba6c1d6ef4..b8af75b3d93412 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -1,4 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #include #include #include @@ -13,17 +15,19 @@ #include #include #include +#include #include "phytmac.h" #include "phytmac_v2.h" -static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, - u16 cmd_subid, void *data, int len, int wait) +static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, + u16 cmd_subid, void *data, int len, int wait) { int index = 0; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; int ret = 0; + mutex_lock(&pdata->msg_ring.msg_mutex); ++pdata->msg_ring.tx_msg_tail; if (pdata->msg_ring.tx_msg_tail > pdata->msg_ring.tx_msg_ring_size) pdata->msg_ring.tx_msg_tail = 1; @@ -32,17 +36,17 @@ static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, wait = 1; memset(&msg, 0, sizeof(msg)); memset(&msg_rx, 0, sizeof(msg_rx)); - msg.module_id = PHYTMAC_MODULE_ID_GMAC; - msg.cmd_id = cmd_id; + msg.cmd_type = cmd_id; msg.cmd_subid = cmd_subid; - msg.flags = PHYTMAC_FLAGS_MSG_NOINT; + msg.status0 = PHYTMAC_FLAGS_MSG_NOINT; if (len) memcpy(&msg.para[0], data, len); if (netif_msg_hw(pdata)) { - netdev_info(pdata->ndev, "tx msg: cmdid=%d, subid=%d, flags=%d, len=%d, tail=%d\n", - msg.cmd_id, msg.cmd_subid, msg.flags, len, pdata->msg_ring.tx_msg_tail); + netdev_info(pdata->ndev, "tx msg: cmdid:%d, subid:%d, status0:%d, len:%d, tail:%d\n", + msg.cmd_type, msg.cmd_subid, msg.status0, len, + pdata->msg_ring.tx_msg_tail); } memcpy(pdata->msg_regs + PHYTMAC_MSG(index), &msg, sizeof(msg)); @@ -51,16 +55,17 @@ static int phytmac_msg_send(struct phytmac *pdata, u16 cmd_id, if (wait) { memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); - while (!(msg_rx.flags & 0x1)) { + while (!(msg_rx.status0 & PHYTMAC_CMD_PRC_COMPLETED)) { cpu_relax(); memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); } } + mutex_unlock(&pdata->msg_ring.msg_mutex); return ret; } -void phytmac_reset_hw(struct phytmac *pdata) +static void phytmac_v2_reset_hw(struct phytmac *pdata) { int q; u16 cmd_id, cmd_subid; @@ -70,26 +75,27 @@ void phytmac_reset_hw(struct phytmac *pdata) for (q = 0; q < pdata->queues_max_num; ++q) { PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(q), -1); PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(q), -1); - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(q), 0); + PHYTMAC_WRITE(pdata, PHYTMAC_RX_PTR(q), 0); } /* reset hw rx/tx enable */ cmd_id = PHYTMAC_MSG_CMD_DEFAULT; cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_HW; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); /* reset tx ring */ memset(&ring, 0, sizeof(ring)); ring.queue_num = pdata->queues_max_num; cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 0); /* reset rx ring */ cmd_subid = PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&ring), sizeof(ring), 1); } -static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) +static int phytmac_v2_get_mac_addr(struct phytmac *pdata, u8 *addr) { int index; u16 cmd_id, cmd_subid; @@ -97,7 +103,7 @@ static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_ADDR; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); index = pdata->msg_ring.tx_msg_tail; if (index <= 0) @@ -115,7 +121,7 @@ static int phytmac_get_mac_addr(struct phytmac *pdata, u8 *addr) return 0; } -int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) +static int phytmac_v2_set_mac_addr(struct phytmac *pdata, const u8 *addr) { u16 cmd_id; u16 cmd_subid; @@ -127,46 +133,70 @@ int phytmac_set_mac_addr(struct phytmac *pdata, const u8 *addr) para.addrl = cpu_to_le32(*((u32 *)addr)); para.addrh = cpu_to_le16(*((u16 *)(addr + 4))); - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + return 0; +} + +static int phytmac_v2_pcs_software_reset(struct phytmac *pdata, int reset) +{ + u16 cmd_id; + u16 cmd_subid; + + cmd_id = PHYTMAC_MSG_CMD_SET; + if (reset) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PCS_RESET; + else + cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PCS_RESET; + + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); return 0; } -static int phytmac_init_hw(struct phytmac *pdata) +static int phytmac_v2_init_hw(struct phytmac *pdata) { u16 cmd_id, cmd_subid; struct phytmac_dma_info dma; struct phytmac_eth_info eth; + u8 mdc; + + if (pdata->mii_bus) { + cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + } - phytmac_set_mac_addr(pdata, pdata->ndev->dev_addr); + phytmac_v2_set_mac_addr(pdata, pdata->ndev->dev_addr); cmd_id = PHYTMAC_MSG_CMD_SET; if (pdata->capacities & PHYTMAC_CAPS_JUMBO) cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_JUMBO; else cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_1536_FRAMES; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); if (pdata->ndev->flags & IFF_PROMISC) { cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PROMISE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); } if (pdata->ndev->features & NETIF_F_RXCSUM) { cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_RXCSUM; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); } - if (!(pdata->ndev->flags & IFF_BROADCAST)) { + if (pdata->ndev->flags & IFF_BROADCAST) + cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_BC; + else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_BC; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); - } + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_PAUSE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_STRIPCRC; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); memset(&dma, 0, sizeof(dma)); cmd_subid = PHYTMAC_MSG_CMD_SET_DMA; @@ -177,22 +207,31 @@ static int phytmac_init_hw(struct phytmac *pdata) dma.hw_dma_cap |= HW_DMA_CAP_CSUM; if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) dma.hw_dma_cap |= HW_DMA_CAP_PTP; + if (pdata->dma_data_width == PHYTMAC_DBW32) + dma.hw_dma_cap |= HW_DMA_CAP_DDW32; if (pdata->dma_data_width == PHYTMAC_DBW64) dma.hw_dma_cap |= HW_DMA_CAP_DDW64; if (pdata->dma_data_width == PHYTMAC_DBW128) dma.hw_dma_cap |= HW_DMA_CAP_DDW128; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); + + cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; + mdc = PHYTMAC_CLK_DIV96; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 1); memset(ð, 0, sizeof(eth)); cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; eth.index = 0; eth.etype = (uint16_t)ETH_P_IP; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)ð, sizeof(eth), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)ð, sizeof(eth), 1); + + if (phy_interface_mode_is_8023z(pdata->phy_interface)) + phytmac_v2_pcs_software_reset(pdata, 1); return 0; } -static int phytmac_enable_multicast(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_multicast(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -202,11 +241,11 @@ static int phytmac_enable_multicast(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MC; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) +static int phytmac_v2_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) { u16 cmd_id, cmd_subid; struct phytmac_mc_info para; @@ -216,12 +255,12 @@ static int phytmac_set_mc_hash(struct phytmac *pdata, unsigned long *mc_filter) cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_HASH_MC; para.mc_bottom = (u32)mc_filter[0]; para.mc_top = (u32)mc_filter[1]; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_init_ring_hw(struct phytmac *pdata) +static int phytmac_v2_init_ring_hw(struct phytmac *pdata) { u16 cmd_id, cmd_subid; struct phytmac_ring_info rxring; @@ -240,27 +279,27 @@ static int phytmac_init_ring_hw(struct phytmac *pdata) txring.hw_dma_cap |= HW_DMA_CAP_64B; rxring.hw_dma_cap |= HW_DMA_CAP_64B; for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(q), queue->tx_head); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(q), queue->tx_head); txring.addr[q] = queue->tx_ring_addr; rxring.addr[q] = queue->rx_ring_addr; } - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&txring), sizeof(txring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&txring), sizeof(txring), 0); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE; rxbuf.queue_num = pdata->queues_num; rxbuf.buffer_size = pdata->rx_buffer_len / 64; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxbuf), sizeof(rxbuf), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxbuf), sizeof(rxbuf), 0); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_RX_RING; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); return 0; } -int phytmac_init_msg_ring(struct phytmac *pdata) +static int phytmac_v2_init_msg_ring(struct phytmac *pdata) { u32 size = 0; @@ -278,7 +317,7 @@ int phytmac_init_msg_ring(struct phytmac *pdata) return 0; } -static int phytmac_get_feature_all(struct phytmac *pdata) +static int phytmac_v2_get_feature_all(struct phytmac *pdata) { u16 cmd_id, cmd_subid; int index; @@ -287,7 +326,7 @@ static int phytmac_get_feature_all(struct phytmac *pdata) memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_CAPS; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); index = pdata->msg_ring.tx_msg_tail; if (index <= 0) @@ -317,25 +356,26 @@ static int phytmac_get_feature_all(struct phytmac *pdata) return 0; } -void phytmac_get_regs(struct phytmac *pdata, u32 *reg_buff) +static void phytmac_v2_get_regs(struct phytmac *pdata, u32 *reg_buff) { u16 cmd_id, cmd_subid; - u16 reg_num; int index; + u8 interface; cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_REG_READ; - reg_num = 16; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)®_num, 2, 1); + cmd_subid = PHYTMAC_MSG_CMD_GET_REGS_FOR_ETHTOOL; + interface = pdata->phytmac_v2_interface; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&interface), sizeof(interface), 1); index = pdata->msg_ring.tx_msg_tail; if (index <= 0) index += pdata->msg_ring.tx_msg_ring_size; - memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); + memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + READ_REG_NUM_MAX * sizeof(u32)); } -static void phytmac_get_hw_stats(struct phytmac *pdata) +static void phytmac_v2_get_hw_stats(struct phytmac *pdata) { u16 cmd_id, cmd_subid; u8 count; @@ -346,27 +386,20 @@ static void phytmac_get_hw_stats(struct phytmac *pdata) cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - count = 1; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); - - cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - count = 2; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 0); + /* There are 45 registers in total, read 16 regs at a time, read 13 regs at last time */ + for (i = 1; i <= 3; i++) { + count = i; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); - cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - count = 3; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); - - for (i = 0; i < 3; i++) { - index = pdata->msg_ring.tx_msg_tail + i - 2; + index = pdata->msg_ring.tx_msg_tail; if (index <= 0) index += pdata->msg_ring.tx_msg_ring_size; - memcpy(&stats[i * 16], pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, 64); + + memcpy(&stats[(i - 1) * READ_REG_NUM_MAX], pdata->msg_regs + PHYTMAC_MSG(index) + + MSG_HDR_LEN, sizeof(u32) * READ_REG_NUM_MAX); } - for (i = 0, j = 0; i < 44; i++) { + for (i = 0, j = 0; i < 45; i++) { if (i == 0 || i == 20) { val = (u64)stats[i + 1] << 32 | stats[i]; *p += val; @@ -385,7 +418,7 @@ static void phytmac_get_hw_stats(struct phytmac *pdata) } } -static void phytmac_mdio_idle(struct phytmac *pdata) +static void phytmac_v2_mdio_idle(struct phytmac *pdata) { u32 val; @@ -397,7 +430,7 @@ static void phytmac_mdio_idle(struct phytmac *pdata) } } -static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) +static int phytmac_v2_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) { u16 data; @@ -406,14 +439,14 @@ static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int reg | PHYTMAC_BITS(PHYADDR, mii_id) | PHYTMAC_BITS(REGADDR, regnum) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return data; } -static int phytmac_mdio_data_write_c22(struct phytmac *pdata, int mii_id, - int regnum, u16 data) +static int phytmac_v2_mdio_data_write_c22(struct phytmac *pdata, int mii_id, + int regnum, u16 data) { PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_WRITE) @@ -421,12 +454,12 @@ static int phytmac_mdio_data_write_c22(struct phytmac *pdata, int mii_id, | PHYTMAC_BITS(REGADDR, regnum) | PHYTMAC_BITS(VALUE, data) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return 0; } -static int phytmac_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int devad, int regnum) +static int phytmac_v2_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int devad, int regnum) { u16 data; @@ -436,21 +469,21 @@ static int phytmac_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int dev | PHYTMAC_BITS(REGADDR, devad & 0x1F) | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_READ) | PHYTMAC_BITS(PHYADDR, mii_id) | PHYTMAC_BITS(REGADDR, devad & 0x1F) | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); data = PHYTMAC_READ(pdata, PHYTMAC_MDIO) & 0xffff; - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return data; } -static int phytmac_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, - int regnum, u16 data) +static int phytmac_v2_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, + int regnum, u16 data) { PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) @@ -458,24 +491,62 @@ static int phytmac_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int de | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) | PHYTMAC_BITS(VALUE, regnum & 0xFFFF) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_WRITE) | PHYTMAC_BITS(PHYADDR, mii_id) | PHYTMAC_BITS(REGADDR, (regnum >> 16) & 0x1F) | PHYTMAC_BITS(VALUE, data) | PHYTMAC_BITS(CONST, 2))); - phytmac_mdio_idle(pdata); + phytmac_v2_mdio_idle(pdata); return 0; } -static int phytmac_powerup_hw(struct phytmac *pdata, int on) +static int phytmac_v2_powerup_hw(struct phytmac *pdata, int on) { u32 status, data0, data1, rdata1; int ret; + acpi_handle handle; + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status acpi_sts; + unsigned long long rv; + + if (!(pdata->capacities & PHYTMAC_CAPS_PWCTRL)) { + pdata->power_state = on; + return 0; + } - if (pdata->capacities & PHYTMAC_CAPS_LPI) { + if (has_acpi_companion(pdata->dev)) { + handle = ACPI_HANDLE(pdata->dev); + + netdev_info(pdata->ndev, "set gmac power %s\n", + on == PHYTMAC_POWERON ? "on" : "off"); + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = PHYTMAC_PWCTL_GMAC_ID; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = PHYTMAC_PWCTL_DEFAULT_VAL; + + if (on == PHYTMAC_POWERON) { + acpi_sts = acpi_evaluate_integer(handle, "PPWO", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWO Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power on\n"); + } else { + acpi_sts = acpi_evaluate_integer(handle, "PPWD", &arg_list, &rv); + if (ACPI_FAILURE(acpi_sts)) + netdev_err(pdata->ndev, "NO PPWD Method\n"); + if (rv) + netdev_err(pdata->ndev, "Failed to power off\n"); + } + } else { ret = readx_poll_timeout(PHYTMAC_READ_STAT, pdata, status, !status, 1, PHYTMAC_TIMEOUT); if (ret) @@ -513,10 +584,10 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); if (rdata1 == data1) netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); else netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", - on ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); } pdata->power_state = on; @@ -524,20 +595,32 @@ static int phytmac_powerup_hw(struct phytmac *pdata, int on) return 0; } -static int phytmac_set_wake(struct phytmac *pdata, int wake) +static int phytmac_v2_set_wake(struct phytmac *pdata, int wake) { u16 cmd_id, cmd_subid; - u8 wol = (u8)wake; + struct phytmac_wol para; + u32 wol_type = 0; + + if (wake & PHYTMAC_WAKE_MAGIC) + wol_type |= PHYTMAC_BIT(MAGIC); + if (wake & PHYTMAC_WAKE_ARP) + wol_type |= PHYTMAC_BIT(ARP); + if (wake & PHYTMAC_WAKE_UCAST) + wol_type |= PHYTMAC_BIT(UCAST); + if (wake & PHYTMAC_WAKE_MCAST) + wol_type |= PHYTMAC_BIT(MCAST); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_WOL; - - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&wol), 1, 1); + memset(¶, 0, sizeof(para)); + para.wol_type = cpu_to_le32(wol_type); + para.wake = (u8)wake; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_enable_promise(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_promise(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; u8 rxcsum = 0; @@ -551,12 +634,12 @@ static int phytmac_enable_promise(struct phytmac *pdata, int enable) rxcsum = 1; } - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxcsum), 1, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxcsum), 1, 1); return 0; } -static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_rxcsum(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -566,12 +649,12 @@ static int phytmac_enable_rxcsum(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_RXCSUM; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_txcsum(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -581,12 +664,12 @@ static int phytmac_enable_txcsum(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_TXCSUM; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_mdio(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_mdio(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -596,12 +679,12 @@ static int phytmac_enable_mdio(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_MDIO; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_autoneg(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -611,13 +694,13 @@ static int phytmac_enable_autoneg(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); pdata->autoneg = enable; return 0; } -static int phytmac_enable_pause(struct phytmac *pdata, int enable) +static int phytmac_v2_enable_pause(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; @@ -627,12 +710,12 @@ static int phytmac_enable_pause(struct phytmac *pdata, int enable) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PAUSE; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) +static int phytmac_v2_enable_network(struct phytmac *pdata, int enable, int rx_tx) { u16 cmd_id, cmd_subid; @@ -642,12 +725,12 @@ static int phytmac_enable_network(struct phytmac *pdata, int enable, int rx_tx) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_NETWORK; - phytmac_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } -static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +static int phytmac_v2_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) { struct ethtool_tcpip4_spec *tp4sp_v, *tp4sp_m; struct phytmac_fdir_info fdir; @@ -675,19 +758,19 @@ static int phytmac_add_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_ fdir.srcport_mask = tp4sp_m->psrc; } - fdir.location = rx_flow->location; - fdir.queue = rx_flow->ring_cookie; + fdir.location = (u8)(rx_flow->location); + fdir.queue = (u8)(rx_flow->ring_cookie); if (fdir.ipsrc_en || fdir.ipdst_en || fdir.port_en) { cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_ADD_FDIR; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); } return 0; } -static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) +static int phytmac_v2_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_spec *rx_flow) { struct phytmac_fdir_info fdir; u16 cmd_id, cmd_subid; @@ -695,21 +778,21 @@ static int phytmac_del_fdir_entry(struct phytmac *pdata, struct ethtool_rx_flow_ memset(&fdir, 0, sizeof(fdir)); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_DEL_FDIR; - fdir.location = (u8)rx_flow->location; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); + fdir.location = (u8)(rx_flow->location); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&fdir), sizeof(fdir), 1); return 0; } -static void phytmac_tx_start(struct phytmac_queue *queue) +static void phytmac_v2_tx_start(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(queue->index), queue->tx_tail); queue->tx_xmit_more = 0; } -static u32 phytmac_get_irq_mask(u32 mask) +static u32 phytmac_v2_get_irq_mask(u32 mask) { u32 value = 0; @@ -722,7 +805,7 @@ static u32 phytmac_get_irq_mask(u32 mask) return value; } -static u32 phytmac_get_irq_status(u32 value) +static u32 phytmac_v2_get_irq_status(u32 value) { u32 status = 0; @@ -735,111 +818,124 @@ static u32 phytmac_get_irq_status(u32 value) return status; } -static void phytmac_enable_irq(struct phytmac *pdata, - int queue_index, u32 mask) +static void phytmac_v2_enable_irq(struct phytmac *pdata, + int queue_index, u32 mask) { u32 value; - value = phytmac_get_irq_mask(mask); + value = phytmac_v2_get_irq_mask(mask); PHYTMAC_WRITE(pdata, PHYTMAC_INT_ER(queue_index), value); } -static void phytmac_disable_irq(struct phytmac *pdata, - int queue_index, u32 mask) +static void phytmac_v2_disable_irq(struct phytmac *pdata, + int queue_index, u32 mask) { u32 value; - value = phytmac_get_irq_mask(mask); + value = phytmac_v2_get_irq_mask(mask); PHYTMAC_WRITE(pdata, PHYTMAC_INT_DR(queue_index), value); } -static void phytmac_clear_irq(struct phytmac *pdata, - int queue_index, u32 mask) +static void phytmac_v2_clear_irq(struct phytmac *pdata, + int queue_index, u32 mask) { u32 value; - value = phytmac_get_irq_mask(mask); + value = phytmac_v2_get_irq_mask(mask); PHYTMAC_WRITE(pdata, PHYTMAC_INT_SR(queue_index), value); } -static unsigned int phytmac_get_irq(struct phytmac *pdata, int queue_index) +static unsigned int phytmac_v2_get_irq(struct phytmac *pdata, int queue_index) { u32 status; u32 value; value = PHYTMAC_READ(pdata, PHYTMAC_INT_SR(queue_index)); - status = phytmac_get_irq_status(value); + status = phytmac_v2_get_irq_status(value); return status; } -static void phytmac_interface_config(struct phytmac *pdata, unsigned int mode, - const struct phylink_link_state *state) +static void phytmac_v2_interface_config(struct phytmac *pdata, unsigned int mode, + const struct phylink_link_state *state) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; + u8 autoneg = 0; + + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + if (mode == MLO_AN_FIXED) + autoneg = 0; + else + autoneg = 1; + } + + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + autoneg = 1; + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + autoneg = 0; memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_SET; - cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_CONFIG; - para.interface = state->interface; - para.autoneg = (mode == MLO_AN_FIXED ? 0 : 1); + cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_MAC_CONFIG; + para.interface = pdata->phytmac_v2_interface; + para.autoneg = autoneg; para.speed = state->speed; para.duplex = state->duplex; pdata->autoneg = para.autoneg; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); } -static int phytmac_interface_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) +static int phytmac_v2_interface_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; + if (interface == PHY_INTERFACE_MODE_SGMII) { + if (speed == SPEED_2500) + pdata->autoneg = 0; + } + memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_MAC_LINK_CONFIG; - para.interface = interface; + para.interface = pdata->phytmac_v2_interface; para.duplex = duplex; para.speed = speed; para.autoneg = pdata->autoneg; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_interface_linkdown(struct phytmac *pdata) +static int phytmac_v2_interface_linkdown(struct phytmac *pdata) { return 0; } -static int phytmac_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) +static int phytmac_v2_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, + int speed, int duplex) { - struct phytmac_interface_info para; u16 cmd_id, cmd_subid; if (interface == PHY_INTERFACE_MODE_USXGMII || interface == PHY_INTERFACE_MODE_10GBASER) { - memset(¶, 0, sizeof(para)); cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_PCS_LINK_UP; - para.interface = interface; - para.duplex = duplex; - para.speed = speed; - para.autoneg = 0; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); } return 0; } -static int phytmac_pcs_linkdown(struct phytmac *pdata) +static int phytmac_v2_pcs_linkdown(struct phytmac *pdata) { return 0; } -static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) +static unsigned int phytmac_v2_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) { if (interface == PHY_INTERFACE_MODE_SGMII || interface == PHY_INTERFACE_MODE_1000BASEX || @@ -852,8 +948,8 @@ static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t return 0; } -static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, - u32 tx_tail, struct packet_info *packet) +static unsigned int phytmac_v2_tx_map_desc(struct phytmac_queue *queue, + u32 tx_tail, struct packet_info *packet) { unsigned int i, ctrl; struct phytmac *pdata = queue->pdata; @@ -896,8 +992,8 @@ static unsigned int phytmac_tx_map_desc(struct phytmac_queue *queue, return 0; } -static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, - u32 index) +static void phytmac_v2_init_rx_map_desc(struct phytmac_queue *queue, + u32 index) { struct phytmac_dma_desc *desc; @@ -909,7 +1005,7 @@ static void phytmac_init_rx_map_desc(struct phytmac_queue *queue, desc->desc0 |= PHYTMAC_BIT(RXUSED); } -static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, dma_addr_t addr) +static unsigned int phytmac_v2_rx_map_desc(struct phytmac_queue *queue, u32 index, dma_addr_t addr) { struct phytmac *pdata = queue->pdata; struct phytmac_dma_desc *desc; @@ -922,19 +1018,21 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); /* Make newly descriptor to hardware */ - dma_wmb(); + if (!(pdata->capacities & PHYTMAC_CAPS_RXPTR)) + dma_wmb(); desc->desc0 = lower_32_bits(addr); } else { desc->desc1 = 0; /* make newly descriptor to hardware */ - dma_wmb(); + if (!(pdata->capacities & PHYTMAC_CAPS_RXPTR)) + dma_wmb(); desc->desc0 &= ~PHYTMAC_BIT(RXUSED); } return 0; } -static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) +static unsigned int phytmac_v2_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { desc->desc2 = 0; desc->desc0 = PHYTMAC_BIT(RXUSED); @@ -942,12 +1040,29 @@ static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) return 0; } -static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) +static unsigned int phytmac_v2_zero_tx_desc(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = 0; + desc->desc1 &= ~PHYTMAC_BIT(TXUSED); + + return 0; +} + +static void phytmac_v2_update_rx_tail(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + PHYTMAC_WRITE(pdata, PHYTMAC_RX_PTR(queue->index), queue->rx_head); +} + +static int phytmac_v2_tx_complete(const struct phytmac_dma_desc *desc) { return PHYTMAC_GET_BITS(desc->desc1, TXUSED); } -static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_complete(const struct phytmac_dma_desc *desc) { dma_addr_t addr; bool used; @@ -962,7 +1077,7 @@ static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) return false; } -static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) +static int phytmac_v2_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) { if (pdata->capacities & PHYTMAC_CAPS_JUMBO) return desc->desc1 & PHYTMAC_RXJFRMLEN_MASK; @@ -970,7 +1085,7 @@ static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_de return desc->desc1 & PHYTMAC_RXFRMLEN_MASK; } -static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_checksum(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; u32 check = value >> PHYTMAC_RXCSUM_INDEX & 0x3; @@ -978,28 +1093,28 @@ static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) return (check == PHYTMAC_RXCSUM_IP_TCP || check == PHYTMAC_RXCSUM_IP_UDP); } -static bool phytmac_rx_single_buffer(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_single_buffer(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; return ((value & PHYTMAC_BIT(RXSOF)) && (value & PHYTMAC_BIT(RXEOF))); } -static bool phytmac_rx_sof(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_sof(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; return (value & PHYTMAC_BIT(RXSOF)); } -static bool phytmac_rx_eof(const struct phytmac_dma_desc *desc) +static bool phytmac_v2_rx_eof(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; return (value & PHYTMAC_BIT(RXEOF)); } -static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) +static void phytmac_v2_clear_rx_desc(struct phytmac_queue *queue, int begin, int end) { unsigned int frag; unsigned int tmp = end; @@ -1014,7 +1129,7 @@ static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int en } } -static void phytmac_clear_tx_desc(struct phytmac_queue *queue) +static void phytmac_v2_clear_tx_desc(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; struct phytmac_dma_desc *desc = NULL; @@ -1031,10 +1146,10 @@ static void phytmac_clear_tx_desc(struct phytmac_queue *queue) desc->desc1 = PHYTMAC_BIT(TXUSED); } desc->desc1 |= PHYTMAC_BIT(TXWRAP); - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_PTR(queue->index), queue->tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_PTR(queue->index), queue->tx_tail); } -static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) +static void phytmac_v2_get_time(struct phytmac *pdata, struct timespec64 *ts) { u32 ns, secl, sech; @@ -1046,7 +1161,7 @@ static void phytmac_get_time(struct phytmac *pdata, struct timespec64 *ts) ts->tv_sec = (((u64)sech << 32) | secl) & TIMER_SEC_MAX_VAL; } -void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) +static void phytmac_v2_set_time(struct phytmac *pdata, time64_t sec, long nsec) { u32 secl, sech; @@ -1059,7 +1174,7 @@ void phytmac_set_time(struct phytmac *pdata, time64_t sec, long nsec) PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_NSEC, nsec); } -void phytmac_clear_time(struct phytmac *pdata) +static void phytmac_v2_clear_time(struct phytmac *pdata) { u32 value; @@ -1077,7 +1192,7 @@ void phytmac_clear_time(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, 0); } -int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) +static int phytmac_v2_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) { u16 cmd_id, cmd_subid; struct phytmac_ts_config para; @@ -1087,12 +1202,12 @@ int phytmac_set_tsmode(struct phytmac *pdata, struct ts_ctrl *ctrl) para.tx_mode = ctrl->tx_control; para.rx_mode = ctrl->rx_control; para.one_step = ctrl->one_step; - phytmac_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(¶), sizeof(para), 1); return 0; } -static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) +static int phytmac_v2_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) { u32 value; @@ -1103,17 +1218,17 @@ static int phytmac_set_tsincr(struct phytmac *pdata, struct ts_incr *incr) return 0; } -static void phytmac_ptp_init_hw(struct phytmac *pdata) +static void phytmac_v2_ptp_init_hw(struct phytmac *pdata) { struct timespec64 ts; ts = ns_to_timespec64(ktime_to_ns(ktime_get_real())); - phytmac_set_time(pdata, ts.tv_sec, ts.tv_nsec); + phytmac_v2_set_time(pdata, ts.tv_sec, ts.tv_nsec); - phytmac_set_tsincr(pdata, &pdata->ts_incr); + phytmac_v2_set_tsincr(pdata, &pdata->ts_incr); } -static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) +static int phytmac_v2_adjust_fine(struct phytmac *pdata, long ppm, bool negative) { struct ts_incr ts_incr; u32 tmp; @@ -1133,22 +1248,25 @@ static int phytmac_adjust_fine(struct phytmac *pdata, long ppm, bool negative) & ((1 << PHYTMAC_INCR_NSEC_WIDTH) - 1); ts_incr.sub_ns = adj & ((1 << PHYTMAC_INCR_SNSEC_WIDTH) - 1); - phytmac_set_tsincr(pdata, &ts_incr); + phytmac_v2_set_tsincr(pdata, &ts_incr); return 0; } -int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) +static int phytmac_v2_adjust_time(struct phytmac *pdata, s64 delta, int neg) { u32 adj; if (delta > PHYTMAC_ASEC_MAX) { struct timespec64 now, then; - then = ns_to_timespec64(delta); - phytmac_get_time(pdata, &now); + if (neg) + then = ns_to_timespec64(-delta); + else + then = ns_to_timespec64(delta); + phytmac_v2_get_time(pdata, &now); now = timespec64_add(now, then); - phytmac_set_time(pdata, now.tv_sec, now.tv_nsec); + phytmac_v2_set_time(pdata, now.tv_sec, now.tv_nsec); } else { adj = (neg << PHYTMAC_AADD_INDEX) | delta; PHYTMAC_WRITE(pdata, PHYTMAC_TIMER_ADJUST, adj); @@ -1157,7 +1275,7 @@ int phytmac_adjust_time(struct phytmac *pdata, s64 delta, int neg) return 0; } -static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) +static int phytmac_v2_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc, int direction) { int ts_valid = 0; @@ -1168,7 +1286,7 @@ static int phytmac_ts_valid(struct phytmac *pdata, struct phytmac_dma_desc *desc return ts_valid; } -static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) +static void phytmac_v2_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct timespec64 *ts) { struct timespec64 ts2; @@ -1176,7 +1294,7 @@ static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct PHYTMAC_GET_BITS(ts_1, TS_SECL); ts->tv_nsec = PHYTMAC_GET_BITS(ts_1, TS_NSEC); - phytmac_get_time(pdata, &ts2); + phytmac_v2_get_time(pdata, &ts2); if (((ts->tv_sec ^ ts2.tv_sec) & (PHYTMAC_TS_SEC_TOP >> 1)) != 0) ts->tv_sec -= PHYTMAC_TS_SEC_TOP; @@ -1184,80 +1302,82 @@ static void phytmac_get_dma_ts(struct phytmac *pdata, u32 ts_1, u32 ts_2, struct ts->tv_sec += (ts2.tv_sec & (~PHYTMAC_TS_SEC_MASK)); } -static unsigned int phytmac_get_ts_rate(struct phytmac *pdata) +static unsigned int phytmac_v2_get_ts_rate(struct phytmac *pdata) { return 300000000; } struct phytmac_hw_if phytmac_2p0_hw = { - .init_msg_ring = phytmac_init_msg_ring, - .reset_hw = phytmac_reset_hw, - .init_hw = phytmac_init_hw, - .init_ring_hw = phytmac_init_ring_hw, - .get_feature = phytmac_get_feature_all, - .get_regs = phytmac_get_regs, - .get_stats = phytmac_get_hw_stats, - .set_mac_address = phytmac_set_mac_addr, - .get_mac_address = phytmac_get_mac_addr, - .mdio_read = phytmac_mdio_data_read_c22, - .mdio_write = phytmac_mdio_data_write_c22, - .mdio_read_c45 = phytmac_mdio_data_read_c45, - .mdio_write_c45 = phytmac_mdio_data_write_c45, - .poweron = phytmac_powerup_hw, - .set_wol = phytmac_set_wake, - .enable_promise = phytmac_enable_promise, - .enable_multicast = phytmac_enable_multicast, - .set_hash_table = phytmac_set_mc_hash, - .enable_rx_csum = phytmac_enable_rxcsum, - .enable_tx_csum = phytmac_enable_txcsum, - .enable_mdio_control = phytmac_enable_mdio, - .enable_autoneg = phytmac_enable_autoneg, - .enable_pause = phytmac_enable_pause, - .enable_network = phytmac_enable_network, - .add_fdir_entry = phytmac_add_fdir_entry, - .del_fdir_entry = phytmac_del_fdir_entry, + .init_msg_ring = phytmac_v2_init_msg_ring, + .reset_hw = phytmac_v2_reset_hw, + .init_hw = phytmac_v2_init_hw, + .init_ring_hw = phytmac_v2_init_ring_hw, + .get_feature = phytmac_v2_get_feature_all, + .get_regs = phytmac_v2_get_regs, + .get_stats = phytmac_v2_get_hw_stats, + .set_mac_address = phytmac_v2_set_mac_addr, + .get_mac_address = phytmac_v2_get_mac_addr, + .mdio_read = phytmac_v2_mdio_data_read_c22, + .mdio_write = phytmac_v2_mdio_data_write_c22, + .mdio_read_c45 = phytmac_v2_mdio_data_read_c45, + .mdio_write_c45 = phytmac_v2_mdio_data_write_c45, + .poweron = phytmac_v2_powerup_hw, + .set_wol = phytmac_v2_set_wake, + .enable_promise = phytmac_v2_enable_promise, + .enable_multicast = phytmac_v2_enable_multicast, + .set_hash_table = phytmac_v2_set_mc_hash, + .enable_rx_csum = phytmac_v2_enable_rxcsum, + .enable_tx_csum = phytmac_v2_enable_txcsum, + .enable_mdio_control = phytmac_v2_enable_mdio, + .enable_autoneg = phytmac_v2_enable_autoneg, + .enable_pause = phytmac_v2_enable_pause, + .enable_network = phytmac_v2_enable_network, + .add_fdir_entry = phytmac_v2_add_fdir_entry, + .del_fdir_entry = phytmac_v2_del_fdir_entry, /* mac config */ - .mac_config = phytmac_interface_config, - .mac_linkup = phytmac_interface_linkup, - .mac_linkdown = phytmac_interface_linkdown, - .pcs_linkup = phytmac_pcs_linkup, - .pcs_linkdown = phytmac_pcs_linkdown, - .get_link = phytmac_pcs_get_link, + .mac_config = phytmac_v2_interface_config, + .mac_linkup = phytmac_v2_interface_linkup, + .mac_linkdown = phytmac_v2_interface_linkdown, + .pcs_linkup = phytmac_v2_pcs_linkup, + .pcs_linkdown = phytmac_v2_pcs_linkdown, + .get_link = phytmac_v2_pcs_get_link, /* irq */ - .enable_irq = phytmac_enable_irq, - .disable_irq = phytmac_disable_irq, - .clear_irq = phytmac_clear_irq, - .get_irq = phytmac_get_irq, + .enable_irq = phytmac_v2_enable_irq, + .disable_irq = phytmac_v2_disable_irq, + .clear_irq = phytmac_v2_clear_irq, + .get_irq = phytmac_v2_get_irq, /* tx and rx */ - .tx_map = phytmac_tx_map_desc, - .transmit = phytmac_tx_start, - .tx_complete = phytmac_tx_complete, - .rx_complete = phytmac_rx_complete, - .get_rx_pkt_len = phytmac_rx_pkt_len, - .init_rx_map = phytmac_init_rx_map_desc, - .rx_map = phytmac_rx_map_desc, - .rx_checksum = phytmac_rx_checksum, - .rx_single_buffer = phytmac_rx_single_buffer, - .rx_pkt_start = phytmac_rx_sof, - .rx_pkt_end = phytmac_rx_eof, - .clear_rx_desc = phytmac_clear_rx_desc, - .clear_tx_desc = phytmac_clear_tx_desc, - .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, + .tx_map = phytmac_v2_tx_map_desc, + .transmit = phytmac_v2_tx_start, + .update_rx_tail = phytmac_v2_update_rx_tail, + .tx_complete = phytmac_v2_tx_complete, + .rx_complete = phytmac_v2_rx_complete, + .get_rx_pkt_len = phytmac_v2_rx_pkt_len, + .init_rx_map = phytmac_v2_init_rx_map_desc, + .rx_map = phytmac_v2_rx_map_desc, + .rx_checksum = phytmac_v2_rx_checksum, + .rx_single_buffer = phytmac_v2_rx_single_buffer, + .rx_pkt_start = phytmac_v2_rx_sof, + .rx_pkt_end = phytmac_v2_rx_eof, + .clear_rx_desc = phytmac_v2_clear_rx_desc, + .clear_tx_desc = phytmac_v2_clear_tx_desc, + .zero_rx_desc_addr = phytmac_v2_zero_rx_desc_addr, + .zero_tx_desc = phytmac_v2_zero_tx_desc, /* ptp */ - .init_ts_hw = phytmac_ptp_init_hw, - .set_time = phytmac_set_time, - .clear_time = phytmac_clear_time, - .get_time = phytmac_get_time, - .set_ts_config = phytmac_set_tsmode, - .set_incr = phytmac_set_tsincr, - .adjust_fine = phytmac_adjust_fine, - .adjust_time = phytmac_adjust_time, - .ts_valid = phytmac_ts_valid, - .get_timestamp = phytmac_get_dma_ts, - .get_ts_rate = phytmac_get_ts_rate, + .init_ts_hw = phytmac_v2_ptp_init_hw, + .set_time = phytmac_v2_set_time, + .clear_time = phytmac_v2_clear_time, + .get_time = phytmac_v2_get_time, + .set_ts_config = phytmac_v2_set_tsmode, + .set_incr = phytmac_v2_set_tsincr, + .adjust_fine = phytmac_v2_adjust_fine, + .adjust_time = phytmac_v2_adjust_time, + .ts_valid = phytmac_v2_ts_valid, + .get_timestamp = phytmac_v2_get_dma_ts, + .get_ts_rate = phytmac_v2_get_ts_rate, }; EXPORT_SYMBOL_GPL(phytmac_2p0_hw); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 92e4806ac2c1f7..d2da4acb69a7d5 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright(c) 2022 - 2025 Phytium Technology Co., Ltd. */ + #ifndef _PHYTMAC_V2_H #define _PHYTMAC_V2_H extern struct phytmac_hw_if phytmac_2p0_hw; +#define PHYTMAC_CMD_PRC_COMPLETED 0x1 #define PHYTMAC_MSG_SRAM_SIZE 4096 -#define MSG_HDR_LEN 8 +#define MSG_HDR_LEN 8 +#define READ_REG_NUM_MAX 16 #define PHYTMAC_TX_MSG_HEAD 0x000 #define PHYTMAC_TX_MSG_TAIL 0x004 @@ -83,7 +87,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define TIMER_SEC_MAX_VAL (((u64)1 << PHYTMAC_TIMER_SEC_WIDTH) - 1) #define TIMER_NSEC_MAX_VAL ((1 << PHYTMAC_TIMER_NSEC_WIDTH) - 1) -#define PHYTMAC_TAIL_PTR(i) (0x0100 + ((i) * 4)) +#define PHYTMAC_TX_PTR(i) (0x0100 + ((i) * 4)) +#define PHYTMAC_RX_PTR(i) (0x0030 + ((i) * 4)) #define PHYTMAC_INT_ER(i) (0x0140 + ((i) * 4)) #define PHYTMAC_INT_DR(i) (0x0180 + ((i) * 4)) #define PHYTMAC_INT_MR(i) (0x01c0 + ((i) * 4)) @@ -200,15 +205,38 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_TS_SEC_MASK 0x3f #define PHYTMAC_TS_SEC_TOP 0x40 +/* WOL register */ +#define PHYTMAC_ARP_IP_INDEX 0 +#define PHYTMAC_ARP_IP_WIDTH 16 +#define PHYTMAC_MAGIC_INDEX 16 +#define PHYTMAC_MAGIC_WIDTH 1 +#define PHYTMAC_ARP_INDEX 17 +#define PHYTMAC_ARP_WIDTH 1 +#define PHYTMAC_UCAST_INDEX 18 +#define PHYTMAC_UCAST_WIDTH 1 +#define PHYTMAC_MCAST_INDEX 19 +#define PHYTMAC_MCAST_WIDTH 1 + #define HW_DMA_CAP_64B 0x1 #define HW_DMA_CAP_CSUM 0x2 #define HW_DMA_CAP_PTP 0x4 -#define HW_DMA_CAP_DDW64 0x8 -#define HW_DMA_CAP_DDW128 0x10 +#define HW_DMA_CAP_DDW32 0x8 +#define HW_DMA_CAP_DDW64 0x10 +#define HW_DMA_CAP_DDW128 0x20 +#define PHYTMAC_DBW32 1 #define PHYTMAC_DBW64 2 #define PHYTMAC_DBW128 4 +#define PHYTMAC_CLK_DIV8 0 +#define PHYTMAC_CLK_DIV16 1 +#define PHYTMAC_CLK_DIV32 2 +#define PHYTMAC_CLK_DIV48 3 +#define PHYTMAC_CLK_DIV64 4 +#define PHYTMAC_CLK_DIV96 5 +#define PHYTMAC_CLK_DIV128 6 +#define PHYTMAC_CLK_DIV224 7 + enum phytmac_msg_cmd_id { PHYTMAC_MSG_CMD_DEFAULT = 0, PHYTMAC_MSG_CMD_SET, @@ -228,7 +256,7 @@ enum phytmac_set_subid { PHYTMAC_MSG_CMD_SET_INIT_RING = 1, PHYTMAC_MSG_CMD_SET_INIT_TX_RING = 2, PHYTMAC_MSG_CMD_SET_INIT_RX_RING = 3, - PHYTMAC_MSG_CMD_SET_MAC_CONFIG = 4, + PHYTMAC_MSG_CMD_SET_INIT_MAC_CONFIG = 4, PHYTMAC_MSG_CMD_SET_ADDR = 5, PHYTMAC_MSG_CMD_SET_DMA_RX_BUFSIZE = 6, PHYTMAC_MSG_CMD_SET_DMA = 7, @@ -269,6 +297,13 @@ enum phytmac_set_subid { PHYTMAC_MSG_CMD_SET_DISABLE_AUTONEG = 42, PHYTMAC_MSG_CMD_SET_RX_DATA_OFFSET = 43, PHYTMAC_MSG_CMD_SET_WOL = 44, + PHYTMAC_MSG_CMD_SET_ENABLE_RSC = 45, + PHYTMAC_MSG_CMD_SET_DISABLE_RSC = 46, + PHYTMAC_MSG_CMD_SET_ENABLE_TX_START = 47, + PHYTMAC_MSG_CMD_SET_ENABLE_PCS_RESET = 48, + PHYTMAC_MSG_CMD_SET_DISABLE_PCS_RESET = 49, + PHYTMAC_MSG_CMD_SET_MDC = 50, + PHYTMAC_MSG_CMD_SET_OUTSTANDING = 51, }; enum phytmac_get_subid { @@ -278,6 +313,8 @@ enum phytmac_get_subid { PHYTMAC_MSG_CMD_GET_BD_PREFETCH, PHYTMAC_MSG_CMD_GET_STATS, PHYTMAC_MSG_CMD_GET_REG_READ, + PHYTMAC_MSG_CMD_GET_RX_FLOW, + PHYTMAC_MSG_CMD_GET_REGS_FOR_ETHTOOL, }; struct phytmac_interface_info { @@ -292,6 +329,11 @@ struct phytmac_mc_info { u32 mc_top; } __packed; +struct phytmac_reg_info { + u32 offset; + u16 regnum; +} __packed; + struct phytmac_fdir_info { u32 ip4src; u32 ip4dst; @@ -351,12 +393,26 @@ struct phytmac_feature { u8 max_rx_fs; } __packed; +struct phytmac_wol { + u32 wol_type; + u8 wake; +} __packed; + struct phytmac_msg_info { - u16 module_id; - u16 cmd_id; - u16 cmd_subid; - u16 flags; + u8 reserved; + u8 seq; + u8 cmd_type; + u8 cmd_subid; + u16 len; + u8 status1; + u8 status0; u8 para[64]; } __packed; +struct phytmac_ots_config { + u32 axi_rd; + u32 axi_wr; + u8 queuenum; +} __packed; + #endif From d229edb7fafa825fd519e8a972e3007ea3c0b3b9 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 048/258] PHYTIUM: net/phytmac: Slove left-shift out of bound issue When the value of the bd_prefetch is equal to 0, subtracting 1 and shifting left will cause the overflow issue. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Idc6b8f8100c62331ca6e9778082c533221558e8a Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/ec5f2c26458fcf71564603c01901a717eeddee19 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 3806e09bc1220a..4d66ae498b88fe 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.29" +#define PHYTMAC_DRIVER_VERSION "1.0.39" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index b8af75b3d93412..9bab3d2a5ca4d5 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -342,10 +342,13 @@ static int phytmac_v2_get_feature_all(struct phytmac *pdata) pdata->dma_addr_width = 32; pdata->dma_data_width = para.dma_data_width; pdata->max_rx_fs = para.max_rx_fs; - pdata->tx_bd_prefetch = (2 << (para.tx_bd_prefetch - 1)) * - sizeof(struct phytmac_dma_desc); - pdata->rx_bd_prefetch = (2 << (para.rx_bd_prefetch - 1)) * - sizeof(struct phytmac_dma_desc); + + if (para.tx_bd_prefetch) + pdata->tx_bd_prefetch = (2 << (para.tx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); + if (para.rx_bd_prefetch) + pdata->rx_bd_prefetch = (2 << (para.rx_bd_prefetch - 1)) * + sizeof(struct phytmac_dma_desc); if (netif_msg_hw(pdata)) { netdev_info(pdata->ndev, "feature qnum=%d, daw=%d, dbw=%d, rxfs=%d, rxbd=%d, txbd=%d\n", From 7974e41ea87beda14d05d264d4faf3d002881d0c Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 30 May 2025 10:29:38 +0800 Subject: [PATCH 049/258] PHYTIUM: Revert "net: phytmac: Manage WOL on MAC if PHY supports WOL" This reverts commit 94967211afe596a9e9c5d80ca9d54e915de3a335. Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/15076a68a5a825e0275fb71dea20e9a0a1d54057 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index e05274b961e4b1..7e0c749477914d 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -117,8 +117,7 @@ static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) ret = phylink_ethtool_set_wol(pdata->phylink, wol); - /* Don't manage WoL on MAC, if PHY set_wol() fails */ - if (ret && ret != -EOPNOTSUPP) + if (!ret || ret != -EOPNOTSUPP) return ret; pdata->wol = 0; From 3c49bcc6aceba11fe2d5ddcc3a0eb1c769afce98 Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 30 May 2025 10:36:36 +0800 Subject: [PATCH 050/258] PHYTIUM: Revert "net/phytmac: Bugfix set WOL failed issue" This reverts commit 3161ed880479e35c7759e127c62c37d2d722edee. Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/bbbe8cf7d988f8c0cd1369b8aa4700541444226a Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 7e0c749477914d..23c46bd91f2630 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -90,23 +90,23 @@ static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol { struct phytmac *pdata = netdev_priv(ndev); - wol->wolopts = 0; phylink_ethtool_get_wol(pdata->phylink, wol); - wol->supported = WAKE_MAGIC | WAKE_ARP | - WAKE_UCAST | WAKE_MCAST; - if (pdata->wol & PHYTMAC_WAKE_MAGIC) { wol->wolopts |= WAKE_MAGIC; + wol->supported |= WAKE_MAGIC; } if (pdata->wol & PHYTMAC_WAKE_ARP) { wol->wolopts |= WAKE_ARP; + wol->supported |= WAKE_ARP; } if (pdata->wol & PHYTMAC_WAKE_UCAST) { wol->wolopts |= WAKE_UCAST; + wol->supported |= WAKE_UCAST; } if (pdata->wol & PHYTMAC_WAKE_MCAST) { wol->wolopts |= WAKE_MCAST; + wol->supported |= WAKE_MCAST; } } From e9b7cf89a0f242981ce9f366e06c2d5a3b27102e Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 051/258] PHYTIUM: net/phytmac: Manage WOL on MAC if PHY supports WOL feature Don't manage WoL on MAC, if PHY sets wol fails, So modify if statement to handle abnormal scenarios correctly. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I54cb5ea7f3a51d1b5c4768c6f15fd7e3bb6502ae Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/3b966d55109f329286fb6c3da0c57ac4a0fa5bfa Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 4d66ae498b88fe..2c9f2966071c17 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.39" +#define PHYTMAC_DRIVER_VERSION "1.0.40" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ From bd315a2182bdea2217f02c0a82a8b45335d6d830 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 052/258] PHYTIUM: net/phytmac: Fixed the PTP test failure issue The bit of RX_TS_VALID should be retained in the process of zero RX address description, which indicates a valid timestamp in the BD entry. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I9b6b89ea3bb9907d2a929e4fa630b6cf28dac4ef Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/f1ff6d0c777103aba315cc3427f4b6dc5f18560b Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v1.c | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 2c9f2966071c17..1351f8f7a89535 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.40" +#define PHYTMAC_DRIVER_VERSION "1.0.41" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 4fc0bfa2f696ec..a9cd9cb9a2f57a 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1003,7 +1003,7 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { desc->desc2 = 0; - desc->desc0 = PHYTMAC_BIT(RX_USED); + desc->desc0 = (desc->desc0 & PHYTMAC_BIT(RX_TS_VALID)) | PHYTMAC_BIT(RX_USED); return 0; } diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 9bab3d2a5ca4d5..3eb4b0424bf75c 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -1038,7 +1038,7 @@ static unsigned int phytmac_v2_rx_map_desc(struct phytmac_queue *queue, u32 inde static unsigned int phytmac_v2_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { desc->desc2 = 0; - desc->desc0 = PHYTMAC_BIT(RXUSED); + desc->desc0 = (desc->desc0 & PHYTMAC_BIT(RXTSVALID)) | PHYTMAC_BIT(RXUSED); return 0; } From fb25bba7c85d0a36750902e834ed7d3db8df8eb6 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 053/258] PHYTIUM: net/phytmac: Cancels the power-on/off capability The MAC register is powered on by default in the firmware and the driver cancels the power-on and power-off capability. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I6a55c577f92fe4f39bc7290861f217b7699ba9ee Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/ba2e5fa1f8e4e92786347336f7fd56c2cd4f65fc Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_platform.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 1351f8f7a89535..c53277e5663187 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.41" +#define PHYTMAC_DRIVER_VERSION "1.0.42" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 95509711894b05..eb3cc7d8f42906 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -30,7 +30,6 @@ static const struct phytmac_config phytium_2p0_config = { .hw_if = &phytmac_2p0_hw, .caps = PHYTMAC_CAPS_TAILPTR | PHYTMAC_CAPS_RXPTR - | PHYTMAC_CAPS_PWCTRL | PHYTMAC_CAPS_LSO | PHYTMAC_CAPS_MSG | PHYTMAC_CAPS_JUMBO, From e9ac3b277cff206e85ffe32af12d47d0992cd4c7 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 054/258] PHYTIUM: net/phytmac: Exit probe when MDIO times out Exit early to avoid system stuck due to MDIO timeout. We have added a callback function for mdio_idle and improved its return value for judgment. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I89bd4a42402bdcab150f0808444d776ee7f7a76e Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/75f8c2f029695ed165dfcbeca5fe7cda24dd9b3f Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 5 ++++- drivers/net/ethernet/phytium/phytmac_main.c | 6 ++++++ drivers/net/ethernet/phytium/phytmac_v1.c | 15 +++++++++------ drivers/net/ethernet/phytium/phytmac_v2.c | 15 +++++++++------ drivers/net/ethernet/phytium/phytmac_v2.h | 2 ++ 5 files changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index c53277e5663187..98f2cd1ba0299a 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -15,7 +15,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.42" +#define PHYTMAC_DRIVER_VERSION "1.0.43" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -53,6 +53,8 @@ #define DEFAULT_MSG_RING_SIZE 16 +#define PHYTMAC_MDIO_TIMEOUT 1000000 /* in usecs */ + #define PHYTMAC_CAPS_JUMBO 0x00000001 #define PHYTMAC_CAPS_PTP 0x00000002 #define PHYTMAC_CAPS_BD_RD_PREFETCH 0x00000004 @@ -534,6 +536,7 @@ struct phytmac_hw_if { int (*mdio_read)(struct phytmac *pdata, int mii_id, int regnum); int (*mdio_write)(struct phytmac *pdata, int mii_id, int regnum, u16 data); + int (*mdio_idle)(struct phytmac *pdata); int (*mdio_read_c45)(struct phytmac *pdata, int mii_id, int devad, int regnum); int (*mdio_write_c45)(struct phytmac *pdata, int mii_id, int devad, int regnum, u16 data); diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index dcede058512c65..80d3495362ad48 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1776,6 +1776,12 @@ int phytmac_mdio_register(struct phytmac *pdata) goto err_out; } + if (hw_if->mdio_idle) { + ret = hw_if->mdio_idle(pdata); + if (ret) + goto free_mdio; + } + pdata->mii_bus->name = "phytmac_mii_bus"; pdata->mii_bus->read = &phytmac_mdio_read_c22; pdata->mii_bus->write = &phytmac_mdio_write_c22; diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index a9cd9cb9a2f57a..e7ac69753dd199 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -558,16 +558,18 @@ static int phytmac_set_wake(struct phytmac *pdata, int wake) return 0; } -static void phytmac_mdio_idle(struct phytmac *pdata) +static int phytmac_mdio_idle(struct phytmac *pdata) { u32 val; + int ret; /* wait for end of transfer */ - val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); - while (!(val & PHYTMAC_BIT(MDIO_IDLE))) { - cpu_relax(); - val = PHYTMAC_READ(pdata, PHYTMAC_NSTATUS); - } + ret = readx_poll_timeout(PHTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(NDI_IDLE), + 1, PHYTMAC_MDIO_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mdio wait for idle time out!"); + + return ret; } static int phytmac_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) @@ -1416,6 +1418,7 @@ struct phytmac_hw_if phytmac_1p0_hw = { .get_stats = phytmac_get_hw_stats, .set_mac_address = phytmac_set_mac_addr, .get_mac_address = phytmac_get_mac_addr, + .mdio_idle = phytmac_mdio_idle, .mdio_read = phytmac_mdio_data_read_c22, .mdio_write = phytmac_mdio_data_write_c22, .mdio_read_c45 = phytmac_mdio_data_read_c45, diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 3eb4b0424bf75c..7b337d13329fa5 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -421,16 +421,18 @@ static void phytmac_v2_get_hw_stats(struct phytmac *pdata) } } -static void phytmac_v2_mdio_idle(struct phytmac *pdata) +static int phytmac_v2_mdio_idle(struct phytmac *pdata) { u32 val; + int ret; /* wait for end of transfer */ - val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); - while (!(val & PHYTMAC_BIT(MIDLE))) { - cpu_relax(); - val = PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS); - } + ret = readx_poll_timeout(PHTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(NDI_IDLE), + 1, PHYTMAC_MDIO_TIMEOUT); + if (ret) + netdev_err(pdata->ndev, "mdio wait for idle time out!"); + + return ret; } static int phytmac_v2_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int regnum) @@ -1320,6 +1322,7 @@ struct phytmac_hw_if phytmac_2p0_hw = { .get_stats = phytmac_v2_get_hw_stats, .set_mac_address = phytmac_v2_set_mac_addr, .get_mac_address = phytmac_v2_get_mac_addr, + .mdio_idle = phytmac_v2_mdio_idle, .mdio_read = phytmac_v2_mdio_data_read_c22, .mdio_write = phytmac_v2_mdio_data_write_c22, .mdio_read_c45 = phytmac_v2_mdio_data_read_c45, diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index d2da4acb69a7d5..57594732fab989 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -237,6 +237,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_CLK_DIV128 6 #define PHYTMAC_CLK_DIV224 7 +#define PHYTMAC_READ_NSR(pdata) PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS) + enum phytmac_msg_cmd_id { PHYTMAC_MSG_CMD_DEFAULT = 0, PHYTMAC_MSG_CMD_SET, From 4273a9e2a5bda526b5f5b47bacea41bfcbcf71ac Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 055/258] PHYTIUM: net/phytmac: Add XDP feature support Add XDP feature support to the phytmac driver.XDP by optimizing the data processing flow provided significant performance improvements for high-performance network application. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Icebe6d715b844b4386be631c292349d0a9a70b50 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/a6378f4881507aa0a5536551fe357ed0ab9065c8 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 44 +- drivers/net/ethernet/phytium/phytmac_main.c | 444 ++++++++++++++++++-- 2 files changed, 452 insertions(+), 36 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 98f2cd1ba0299a..1d89c9bd023deb 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -12,6 +12,7 @@ #include #include #include +#include #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" @@ -132,6 +133,14 @@ #define PHYTMAC_WAKE_UCAST 0x00000004 #define PHYTMAC_WAKE_MCAST 0x00000008 +/* XDP */ +#define PHYTMAC_XDP_PASS 0 +#define PHYTMAC_XDP_CONSUMED BIT(0) +#define PHYTMAC_XDP_TX BIT(1) +#define PHYTMAC_XDP_REDIR BIT(2) + +#define PHYTMAC_DESC_NEEDED (MAX_SKB_FRAGS + 4) + enum phytmac_interface { PHYTMAC_PHY_INTERFACE_MODE_NA, PHYTMAC_PHY_INTERFACE_MODE_INTERNAL, @@ -336,8 +345,20 @@ struct phytmac_dma_desc { }; #endif +/* TX resources are shared between XDP and netstack + * and we need to tag the buffer type to distinguish them + */ +enum phytmac_tx_buf_type { + PHYTMAC_TYPE_SKB = 0, + PHYTMAC_TYPE_XDP, +}; + struct phytmac_tx_skb { - struct sk_buff *skb; + union { + struct sk_buff *skb; + struct xdp_frame *xdpf; + }; + enum phytmac_tx_buf_type type; dma_addr_t addr; size_t length; bool mapped_as_page; @@ -360,6 +381,7 @@ struct phytmac_queue { struct phytmac *pdata; int irq; int index; + struct bpf_prog *xdp_prog; /* tx queue info */ unsigned int tx_head; @@ -382,6 +404,7 @@ struct phytmac_queue { struct phytmac_rx_buffer *rx_buffer_info; struct napi_struct rx_napi; struct phytmac_queue_stats stats; + struct xdp_rxq_info xdp_rxq; #ifdef CONFIG_PHYTMAC_ENABLE_PTP struct work_struct tx_ts_task; @@ -427,6 +450,7 @@ struct phytmac { struct platform_device *platdev; struct net_device *ndev; struct device *dev; + struct bpf_prog *xdp_prog; struct ncsi_dev *ncsidev; struct fwnode_handle *fwnode; struct phytmac_hw_if *hw_if; @@ -491,6 +515,23 @@ struct phytmac { u32 version; }; +/* phytmac_desc_unused - calculate if we have unused descriptors */ +static inline int phytmac_txdesc_unused(struct phytmac_queue *queue) +{ + struct phytmac *pdata = queue->pdata; + + if (queue->tx_head > queue->tx_tail) + return queue->tx_head - queue->tx_tail - 1; + + return pdata->tx_ring_size + queue->tx_head - queue->tx_tail - 1; +} + +static inline struct netdev_queue *phytmac_get_txq(const struct phytmac *pdata, + struct phytmac_queue *queue) +{ + return netdev_get_tx_queue(pdata->ndev, queue->index); +} + struct phytmac_hw_if { int (*init_msg_ring)(struct phytmac *pdata); int (*init_hw)(struct phytmac *pdata); @@ -630,6 +671,7 @@ struct phytmac_hw_if { #define PHYTMAC_RX_DMA_ATTR \ (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) #define PHYTMAC_SKB_PAD (NET_SKB_PAD) +#define PHYTMAC_ETH_PKT_HDR_PAD (ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2)) #define PHYTMAC_RXBUFFER_2048 2048 #define PHYTMAC_MAX_FRAME_BUILD_SKB \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 80d3495362ad48..26d70d9ccaef91 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include "phytmac.h" #include "phytmac_ptp.h" @@ -65,6 +67,16 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); * space in the SRAM (16KB) even when there is. */ +static int phytmac_xdp_xmit_back(struct phytmac *pdata, struct xdp_buff *xdp); + +static inline int phytmac_calc_rx_buf_len(void) +{ +#if (PAGE_SIZE < 8192) + return rounddown(PHYTMAC_MAX_FRAME_BUILD_SKB, RX_BUFFER_MULTIPLE); +#endif + return rounddown(PHYTMAC_RXBUFFER_2048, RX_BUFFER_MULTIPLE); +} + static int phytmac_queue_phyaddr_check(struct phytmac *pdata, dma_addr_t ring_base_addr, int offset) { @@ -84,9 +96,20 @@ static int phytmac_queue_phyaddr_check(struct phytmac *pdata, dma_addr_t ring_ba static int phytmac_change_mtu(struct net_device *ndev, int new_mtu) { + struct phytmac *pdata = netdev_priv(ndev); + int max_frame = new_mtu + PHYTMAC_ETH_PKT_HDR_PAD; + if (netif_running(ndev)) return -EBUSY; + if (pdata->xdp_prog) { + if (max_frame > phytmac_calc_rx_buf_len()) { + netdev_warn(pdata->ndev, + "Requested MTU size is not supported with XDP.\n"); + return -EINVAL; + } + } + if (new_mtu > MAX_MTU) { netdev_info(ndev, "Can not set MTU over %d.\n", MAX_MTU); return -EINVAL; @@ -301,14 +324,6 @@ static struct net_device_stats *phytmac_get_stats(struct net_device *dev) return nstat; } -static inline int phytmac_calc_rx_buf_len(void) -{ -#if (PAGE_SIZE < 8192) - return rounddown(PHYTMAC_MAX_FRAME_BUILD_SKB, RX_BUFFER_MULTIPLE); -#endif - return rounddown(PHYTMAC_RXBUFFER_2048, RX_BUFFER_MULTIPLE); -} - inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, unsigned int index) { @@ -421,6 +436,11 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) if (queue->rx_ring) queue->rx_ring = NULL; + if (queue->xdp_prog) { + queue->xdp_prog = NULL; + xdp_rxq_info_unreg(&queue->xdp_rxq); + } + if (queue->rx_buffer_info) { vfree(queue->rx_buffer_info); queue->rx_buffer_info = NULL; @@ -548,6 +568,19 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) queue->rx_buffer_info = vzalloc(size); if (!queue->rx_buffer_info) goto err; + + memset(&queue->xdp_rxq, 0, sizeof(queue->xdp_rxq)); + WRITE_ONCE(queue->xdp_prog, pdata->xdp_prog); + + /* XDP RX-queue info */ + ret = xdp_rxq_info_reg(&queue->xdp_rxq, queue->pdata->ndev, q, 0); + if (ret < 0) { + netdev_err(pdata->ndev, "Failed to register xdp_rxq index %u\n", q); + goto err; + } + + xdp_rxq_info_unreg_mem_model(&queue->xdp_rxq); + WARN_ON(xdp_rxq_info_reg_mem_model(&queue->xdp_rxq, MEM_TYPE_PAGE_SHARED, NULL)); } return 0; @@ -863,6 +896,116 @@ static struct sk_buff *phytmac_build_skb(struct phytmac_rx_buffer *rx_buffer, return skb; } +static void phytmac_rx_buffer_flip(struct phytmac_rx_buffer *rx_buffer, unsigned int size) +{ + unsigned int truesize; + +#if (PAGE_SIZE < 8192) + truesize = PHYTMAC_RX_PAGE_SIZE / 2; + rx_buffer->page_offset ^= truesize; +#else + truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + size); + rx_buffer->page_offset += truesize; +#endif +} + +static struct sk_buff *phytmac_run_xdp(struct phytmac *pdata, + struct xdp_buff *xdp) +{ + int err, result = PHYTMAC_XDP_PASS; + struct bpf_prog *xdp_prog; + u32 act; + + rcu_read_lock(); + xdp_prog = READ_ONCE(pdata->xdp_prog); + + if (!xdp_prog) + goto xdp_out; + + prefetchw(xdp->data_hard_start); /* xdp_frame write */ + + act = bpf_prog_run_xdp(xdp_prog, xdp); + switch (act) { + case XDP_PASS: + break; + case XDP_TX: + result = phytmac_xdp_xmit_back(pdata, xdp); + if (result == PHYTMAC_XDP_CONSUMED) + goto out_failure; + break; + case XDP_REDIRECT: + err = xdp_do_redirect(pdata->ndev, xdp, xdp_prog); + if (err) + goto out_failure; + result = PHYTMAC_XDP_REDIR; + break; + default: + bpf_warn_invalid_xdp_action(pdata->ndev, xdp_prog, act); + fallthrough; + case XDP_ABORTED: +out_failure: + trace_xdp_exception(pdata->ndev, xdp_prog, act); + fallthrough; + case XDP_DROP: + result = PHYTMAC_XDP_CONSUMED; + break; + } +xdp_out: + rcu_read_unlock(); + return ERR_PTR(-result); +} + +static struct sk_buff *phytmac_rx_xdp_single(struct phytmac_queue *queue, + struct phytmac_dma_desc *desc, + unsigned int *xdp_xmit) +{ + struct phytmac *pdata = queue->pdata; + struct phytmac_hw_if *hw_if = pdata->hw_if; + struct phytmac_rx_buffer *rx_buffer; + struct sk_buff *skb = NULL; + unsigned int len; + struct xdp_buff xdp; + unsigned char *hard_start; + u32 frame_sz = 0; + + len = hw_if->get_rx_pkt_len(pdata, desc); + rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); + + hw_if->zero_rx_desc_addr(desc); + +#if (PAGE_SIZE < 8192) + frame_sz = PHYTMAC_RX_PAGE_SIZE / 2; +#else + frame_sz = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + len); +#endif + xdp_init_buff(&xdp, frame_sz, &queue->xdp_rxq); + + hard_start = page_address(rx_buffer->page) + rx_buffer->page_offset - PHYTMAC_SKB_PAD; + xdp_prepare_buff(&xdp, hard_start, PHYTMAC_SKB_PAD, len, true); + xdp_buff_clear_frags_flag(&xdp); + skb = phytmac_run_xdp(pdata, &xdp); + + if (IS_ERR(skb)) { + unsigned int xdp_res = -PTR_ERR(skb); + + if (xdp_res & (PHYTMAC_XDP_TX | PHYTMAC_XDP_REDIR)) { + *xdp_xmit |= xdp_res; + phytmac_rx_buffer_flip(rx_buffer, len); + } else { + rx_buffer->pagecnt_bias++; + } + phytmac_put_rx_buffer(queue, rx_buffer); + pdata->ndev->stats.rx_bytes += len; + queue->stats.rx_bytes += len; + } else { + rx_buffer->pagecnt_bias++; + } + + return skb; +} + static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phytmac_dma_desc *desc) { struct phytmac *pdata = queue->pdata; @@ -1048,6 +1191,7 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, struct phytmac_hw_if *hw_if = pdata->hw_if; struct sk_buff *skb; struct phytmac_dma_desc *desc; + unsigned int xdp_xmit = 0; int count = 0; while (count < budget) { @@ -1061,10 +1205,15 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, /* Ensure ctrl is at least as up-to-date as rxused */ dma_rmb(); - if (hw_if->rx_single_buffer(desc)) - skb = phytmac_rx_single(queue, desc); - else + if (hw_if->rx_single_buffer(desc)) { + skb = phytmac_rx_xdp_single(queue, desc, &xdp_xmit); + if (!IS_ERR(skb)) + skb = phytmac_rx_single(queue, desc); + } else { + if (pdata->xdp_prog) + netdev_warn(pdata->ndev, "xdp does not support multiple buffers!!\n"); skb = phytmac_rx_mbuffer(queue); + } if (!skb) { netdev_warn(pdata->ndev, "phytmac rx skb is NULL\n"); @@ -1073,18 +1222,28 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, pdata->ndev->stats.rx_packets++; queue->stats.rx_packets++; - pdata->ndev->stats.rx_bytes += skb->len; - queue->stats.rx_bytes += skb->len; + if (!IS_ERR(skb)) { + pdata->ndev->stats.rx_bytes += skb->len; + queue->stats.rx_bytes += skb->len; + } queue->rx_tail = (queue->rx_tail + 1) & (pdata->rx_ring_size - 1); count++; + if (IS_ERR(skb)) { + skb = NULL; + continue; + } + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) phytmac_ptp_rxstamp(pdata, skb, desc); napi_gro_receive(napi, skb); } + if (xdp_xmit & PHYTMAC_XDP_REDIR) + xdp_do_flush_map(); + phytmac_rx_clean(queue); return count; @@ -1102,8 +1261,13 @@ static void phytmac_tx_unmap(struct phytmac *pdata, struct phytmac_tx_skb *tx_sk tx_skb->addr = 0; } - if (tx_skb->skb) { - napi_consume_skb(tx_skb->skb, budget); + if (tx_skb->type == PHYTMAC_TYPE_XDP) { + if (tx_skb->xdpf) + xdp_return_frame(tx_skb->xdpf); + tx_skb->xdpf = NULL; + } else { + if (tx_skb->skb) + napi_consume_skb(tx_skb->skb, budget); tx_skb->skb = NULL; } } @@ -1137,6 +1301,19 @@ static int phytmac_maybe_wake_tx_queue(struct phytmac_queue *queue) return (space <= (3 * pdata->tx_ring_size / 4)) ? 1 : 0; } +static inline void phytmac_do_ptp_txstamp(struct phytmac_queue *queue, + struct sk_buff *skb, + struct phytmac_dma_desc *desc) +{ + if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { + if (unlikely(skb_shinfo(skb)->tx_flags & + SKBTX_HW_TSTAMP) && + !phytmac_ptp_one_step(skb)) { + phytmac_ptp_txstamp(queue, skb, desc); + } + } +} + static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) { struct phytmac *pdata = queue->pdata; @@ -1163,27 +1340,32 @@ static int phytmac_tx_clean(struct phytmac_queue *queue, int budget) /* Process all buffers of the current transmitted frame */ for (;; head++) { tx_skb = phytmac_get_tx_skb(queue, head); - skb = tx_skb->skb; - - if (skb) { - complete = 1; - if (IS_REACHABLE(CONFIG_PHYTMAC_ENABLE_PTP)) { - if (unlikely(skb_shinfo(skb)->tx_flags & - SKBTX_HW_TSTAMP) && - !phytmac_ptp_one_step(skb)) { - phytmac_ptp_txstamp(queue, skb, desc); - } - } - - if (netif_msg_drv(pdata)) - netdev_info(pdata->ndev, "desc %u (data %p) tx complete\n", - head, tx_skb->skb->data); - pdata->ndev->stats.tx_packets++; - queue->stats.tx_packets++; - pdata->ndev->stats.tx_bytes += tx_skb->skb->len; - queue->stats.tx_bytes += tx_skb->skb->len; - packet_count++; + if (tx_skb->type == PHYTMAC_TYPE_SKB) { + skb = tx_skb->skb; + if (skb) { + complete = 1; + phytmac_do_ptp_txstamp(queue, skb, desc); + + if (netif_msg_drv(pdata)) + netdev_info(pdata->ndev, "desc %u (data %p) tx complete\n", + head, tx_skb->skb->data); + + pdata->ndev->stats.tx_packets++; + queue->stats.tx_packets++; + pdata->ndev->stats.tx_bytes += tx_skb->skb->len; + queue->stats.tx_bytes += tx_skb->skb->len; + packet_count++; + } + } else if (tx_skb->type == PHYTMAC_TYPE_XDP) { + if (tx_skb->xdpf) { + complete = 1; + pdata->ndev->stats.tx_packets++; + queue->stats.tx_packets++; + pdata->ndev->stats.tx_bytes += tx_skb->xdpf->len; + queue->stats.tx_bytes += tx_skb->xdpf->len; + packet_count++; + } } /* Now we can safely release resources */ @@ -1431,6 +1613,7 @@ static unsigned int phytmac_tx_map(struct phytmac *pdata, /* Save info to properly release resources */ tx_skb->skb = NULL; + tx_skb->type = PHYTMAC_TYPE_SKB; tx_skb->addr = mapping; tx_skb->length = size; tx_skb->mapped_as_page = false; @@ -1459,6 +1642,7 @@ static unsigned int phytmac_tx_map(struct phytmac *pdata, /* Save info to properly release resources */ tx_skb->skb = NULL; + tx_skb->type = PHYTMAC_TYPE_SKB; tx_skb->addr = mapping; tx_skb->length = size; tx_skb->mapped_as_page = true; @@ -1523,6 +1707,59 @@ static inline void phytmac_init_ring(struct phytmac *pdata) hw_if->init_ring_hw(pdata); } +static int phytmac_start_xmit_xdp(struct phytmac *pdata, + struct phytmac_queue *queue, + struct xdp_frame *xdpf) +{ + u32 len; + struct phytmac_tx_skb *tx_buffer; + struct packet_info packet; + dma_addr_t dma; + struct phytmac_hw_if *hw_if = pdata->hw_if; + u16 tx_tail; + + len = xdpf->len; + + memset(&packet, 0, sizeof(struct packet_info)); + + if (unlikely(!phytmac_txdesc_unused(queue))) + return PHYTMAC_XDP_CONSUMED; + + dma = dma_map_single(pdata->dev, xdpf->data, len, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, dma)) + return PHYTMAC_XDP_CONSUMED; + + /* record the location of the first descriptor for this packet */ + tx_buffer = phytmac_get_tx_skb(queue, queue->tx_tail); + tx_buffer->mapped_as_page = false; + + /* Temporarily set the tail pointer for the next package */ + tx_tail = queue->tx_tail + 1; + + dma_unmap_len_set(tx_buffer, length, len); + dma_unmap_addr_set(tx_buffer, addr, dma); + tx_buffer->type = PHYTMAC_TYPE_XDP; + tx_buffer->xdpf = xdpf; + + packet.lso = 0; + packet.mss = 0; + packet.nocrc = 0; + + /* Avoid any potential race with xdp_xmit and cleanup */ + smp_wmb(); + + hw_if->tx_map(queue, tx_tail, &packet); + + queue->tx_tail = tx_tail & (pdata->tx_ring_size - 1); + + hw_if->transmit(queue); + + /* Make sure there is space in the ring for the next send. */ + phytmac_maybe_stop_tx_queue(queue, PHYTMAC_DESC_NEEDED); + + return PHYTMAC_XDP_TX; +} + static netdev_tx_t phytmac_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct phytmac *pdata = netdev_priv(ndev); @@ -2187,6 +2424,140 @@ int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size) return ret; } +static int phytmac_xdp_setup(struct net_device *dev, struct bpf_prog *prog) +{ + int i, frame_size = dev->mtu + PHYTMAC_ETH_PKT_HDR_PAD; + struct phytmac *pdata = netdev_priv(dev); + struct bpf_prog *old_prog; + bool running = netif_running(dev); + bool need_reset; + + /* verify phytmac rx ring attributes are sufficient for XDP */ + if (frame_size > phytmac_calc_rx_buf_len()) { + netdev_warn(dev, "XDP RX buffer size %d is too small for the frame size %d\n", + phytmac_calc_rx_buf_len(), frame_size); + return -EINVAL; + } + + old_prog = xchg(&pdata->xdp_prog, prog); + need_reset = (!!prog != !!old_prog); + + /* device is up and bpf is added/removed, must setup the RX queues */ + if (need_reset && running) { + phytmac_close(dev); + } else { + for (i = 0; i < pdata->queues_num; i++) + (void)xchg(&pdata->queues[i].xdp_prog, + pdata->xdp_prog); + } + + if (old_prog) + bpf_prog_put(old_prog); + + /* bpf is just replaced, RXQ and MTU are already setup */ + if (!need_reset) + return 0; + + if (prog) + xdp_features_set_redirect_target(dev, false); + else + xdp_features_clear_redirect_target(dev); + + if (running) + phytmac_open(dev); + + return 0; +} + +static int phytmac_xdp(struct net_device *dev, struct netdev_bpf *xdp) +{ + switch (xdp->command) { + case XDP_SETUP_PROG: + return phytmac_xdp_setup(dev, xdp->prog); + default: + return -EINVAL; + } +} + +static struct phytmac_queue *phytmac_xdp_txq_mapping(struct phytmac *pdata) +{ + unsigned int r_idx = smp_processor_id(); + + if (r_idx >= pdata->queues_num) + r_idx = r_idx % pdata->queues_num; + + return &pdata->queues[r_idx]; +} + +static int phytmac_xdp_xmit_back(struct phytmac *pdata, struct xdp_buff *xdp) +{ + struct xdp_frame *xdpf = xdp_convert_buff_to_frame(xdp); + int cpu = smp_processor_id(); + struct phytmac_queue *queue; + struct netdev_queue *nq; + u32 ret; + + if (unlikely(!xdpf)) + return PHYTMAC_XDP_CONSUMED; + + /* During program transitions its possible adapter->xdp_prog is assigned + * but ring has not been configured yet. In this case simply abort xmit. + */ + queue = pdata->xdp_prog ? phytmac_xdp_txq_mapping(pdata) : NULL; + if (unlikely(!queue)) + return PHYTMAC_XDP_CONSUMED; + + nq = phytmac_get_txq(pdata, queue); + __netif_tx_lock(nq, cpu); + /* Avoid transmit queue timeout since we share it with the slow path */ + txq_trans_cond_update(nq); + ret = phytmac_start_xmit_xdp(pdata, queue, xdpf); + __netif_tx_unlock(nq); + + return ret; +} + +static int phytmac_xdp_xmit(struct net_device *dev, int n, + struct xdp_frame **frames, u32 flags) +{ + struct phytmac *pdata = netdev_priv(dev); + int cpu = smp_processor_id(); + struct phytmac_queue *queue; + struct netdev_queue *nq; + int nxmit = 0; + int i; + + if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) + return -EINVAL; + + /* During program transitions its possible pdata->xdp_prog is assigned + * but ring has not been configured yet. In this case simply abort xmit. + */ + queue = pdata->xdp_prog ? phytmac_xdp_txq_mapping(pdata) : NULL; + if (unlikely(!queue)) + return -ENXIO; + + nq = phytmac_get_txq(pdata, queue); + __netif_tx_lock(nq, cpu); + + /* Avoid transmit queue timeout since we share it with the slow path */ + txq_trans_cond_update(nq); + + for (i = 0; i < n; i++) { + struct xdp_frame *xdpf = frames[i]; + int err; + + err = phytmac_start_xmit_xdp(pdata, queue, xdpf); + if (err != PHYTMAC_XDP_TX) + break; + nxmit++; + } + + __netif_tx_unlock(nq); + + return nxmit; +} + static const struct net_device_ops phytmac_netdev_ops = { .ndo_open = phytmac_open, .ndo_stop = phytmac_close, @@ -2201,6 +2572,8 @@ static const struct net_device_ops phytmac_netdev_ops = { .ndo_features_check = phytmac_features_check, .ndo_vlan_rx_add_vid = ncsi_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = ncsi_vlan_rx_kill_vid, + .ndo_bpf = phytmac_xdp, + .ndo_xdp_xmit = phytmac_xdp_xmit, }; static int phytmac_init(struct phytmac *pdata) @@ -2307,6 +2680,7 @@ void phytmac_default_config(struct phytmac *pdata) ndev->max_mtu = ETH_DATA_LEN; ndev->features = ndev->hw_features; + ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT; } static void phytmac_ncsi_handler(struct ncsi_dev *nd) From 28d04024977b286f7b762c6a2cbbed1d41c69af4 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 056/258] PHYTIUM: net/phytmac: Clear RX descriptor address after the skb construction If the skb build failed, the descriptor address has been cleared, and the current descriptor will be considered invalid in the next round of processing. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I67900da07421ed3773e3f29dd2ddb16ce39fae1d Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/35be7e0f4fe7ed1612b14c56a83f497d06138afd Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_main.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 1d89c9bd023deb..5ea2a383c10742 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.43" +#define PHYTMAC_DRIVER_VERSION "1.0.44" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 26d70d9ccaef91..df0aea0de7a412 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -972,8 +972,6 @@ static struct sk_buff *phytmac_rx_xdp_single(struct phytmac_queue *queue, len = hw_if->get_rx_pkt_len(pdata, desc); rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); - hw_if->zero_rx_desc_addr(desc); - #if (PAGE_SIZE < 8192) frame_sz = PHYTMAC_RX_PAGE_SIZE / 2; #else @@ -996,6 +994,7 @@ static struct sk_buff *phytmac_rx_xdp_single(struct phytmac_queue *queue, } else { rx_buffer->pagecnt_bias++; } + hw_if->zero_rx_desc_addr(desc); phytmac_put_rx_buffer(queue, rx_buffer); pdata->ndev->stats.rx_bytes += len; queue->stats.rx_bytes += len; @@ -1016,7 +1015,6 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy len = hw_if->get_rx_pkt_len(pdata, desc); rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); - hw_if->zero_rx_desc_addr(desc); skb = phytmac_build_skb(rx_buffer, len); if (unlikely(!skb)) { @@ -1028,6 +1026,7 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy return NULL; } + hw_if->zero_rx_desc_addr(desc); phytmac_put_rx_buffer(queue, rx_buffer); skb->protocol = eth_type_trans(skb, pdata->ndev); @@ -1063,7 +1062,6 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, desc = phytmac_get_rx_desc(queue, first_frag); rx_buffer = phytmac_get_rx_buffer(queue, first_frag, frag_len); - hw_if->zero_rx_desc_addr(desc); skb = phytmac_build_skb(rx_buffer, frag_len); if (unlikely(!skb)) { @@ -1074,6 +1072,7 @@ static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, return NULL; } + hw_if->zero_rx_desc_addr(desc); phytmac_put_rx_buffer(queue, rx_buffer); for (frag = first_frag + 1; ; frag++) { From 928390ca1233d9be44f4879641f3290b35745db3 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 057/258] PHYTIUM: net/phytmac: Enable the tail pointer by the driver It is need to enable the RX/TX tail pointer to ensure the NIC works properly. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I6e7fbf04484539add0834d0aaa0fee1cb0001355 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/e1f3cb62ca1bb3fc7267def60f2f853ad27d2280 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 8 ++++++++ drivers/net/ethernet/phytium/phytmac_v2.h | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 5ea2a383c10742..abe43324cd6aac 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -16,7 +16,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.44" +#define PHYTMAC_DRIVER_VERSION "1.0.45" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 7b337d13329fa5..2e9d14dcb9f31c 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -159,8 +159,16 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) u16 cmd_id, cmd_subid; struct phytmac_dma_info dma; struct phytmac_eth_info eth; + u32 ptrconfig = 0; u8 mdc; + if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) + ptrconfig |= PHYTMAC_BIT(TXTAIL_EN); + if (pdata->capacities & PHYTMAC_CAPS_RXPTR) + ptrconfig |= PHYTMAC_BIT(RXTAIL_EN); + + PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR_ENABLE, ptrconfig); + if (pdata->mii_bus) { cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 57594732fab989..1303bde7cff058 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -17,6 +17,7 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_RX_MSG_TAIL 0x00c #define PHYTMAC_MSG_IMR 0x020 #define PHYTMAC_MSG_ISR 0x02c +#define PHYTMAC_TAILPTR_ENABLE 0x038 #define PHYTMAC_SIZE 0x0048 #define PHYTMAC_NETWORK_STATUS 0x0240 @@ -43,6 +44,12 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_MSG_COMPLETE_INDEX 0 #define PHYTMAC_MSG_COMPLETE_WIDTH 1 +/* Bitfields in PHYTMAC_TAILPTR_ENABLE */ +#define PHYTMAC_TXTAIL_EN_INDEX 0 /* Enable tx tail */ +#define PHYTMAC_TXTAIL_EN_WIDTH 1 +#define PHYTMAC_RXTAIL_EN_INDEX 16 /* Enable rx tail */ +#define PHYTMAC_RXTAIL_EN_WIDTH 1 + /* Bitfields in PHYTMAC_SIZE */ #define PHYTMAC_MEM_SIZE_INDEX 0 #define PHYTMAC_MEM_SIZE_WIDTH 4 From 8c8508b0e5c3a3042a22938d8ecae4ad2b46a7db Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 058/258] PHYTIUM: net/phytmac: Modify cmd processing function Change the para size in the msg struct from 64 to 56 and add mutux to avoid race. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I295f019f2d659c656816c88c42ea74caf3afa1f0 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/3b223d03776f8201d3f0487fe61a10b5cd6b1b90 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 6 +- .../net/ethernet/phytium/phytmac_ethtool.c | 4 +- drivers/net/ethernet/phytium/phytmac_v1.c | 6 +- drivers/net/ethernet/phytium/phytmac_v2.c | 170 +++++++++--------- drivers/net/ethernet/phytium/phytmac_v2.h | 24 ++- 5 files changed, 114 insertions(+), 96 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index abe43324cd6aac..e8a640de47a69b 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -75,7 +75,8 @@ #define PHYTMAC_TX 0x1 #define PHYTMAC_RX 0x2 -#define PHYTMAC_GREGS_LEN 16 +#define PHYTMAC_ETHTOOLD_REGS_LEN 64 +#define PHYTMAC_STATIS_REG_NUM 45 #define PHYTMAC_MTU_MIN_SIZE ETH_MIN_MTU @@ -429,7 +430,8 @@ struct phytmac_msg { u32 tx_msg_ring_size; u32 rx_msg_ring_size; u32 tx_msg_head; - u32 tx_msg_tail; + u32 tx_msg_wr_tail; + u32 tx_msg_rd_tail; u32 rx_msg_head; u32 rx_msg_tail; /*use msg_mutex to protect msg */ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 23c46bd91f2630..2c0bbb8f277203 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -70,7 +70,7 @@ static void phytmac_get_ethtool_strings(struct net_device *ndev, u32 sset, u8 *p static inline int phytmac_get_regs_len(struct net_device *ndev) { - return PHYTMAC_GREGS_LEN; + return PHYTMAC_ETHTOOLD_REGS_LEN; } static void phytmac_get_regs(struct net_device *ndev, @@ -81,7 +81,7 @@ static void phytmac_get_regs(struct net_device *ndev, struct phytmac_hw_if *hw_if = pdata->hw_if; u32 *regs_buff = p; - memset(p, 0, PHYTMAC_GREGS_LEN * sizeof(u32)); + memset(p, 0, PHYTMAC_ETHTOOLD_REGS_LEN); hw_if->get_regs(pdata, regs_buff); } diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index e7ac69753dd199..515825627fedc1 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -1226,15 +1226,15 @@ static void phytmac_clear_tx_desc(struct phytmac_queue *queue) static void phytmac_get_hw_stats(struct phytmac *pdata) { - u32 stats[45]; + u32 stats[PHYTMAC_STATIS_REG_NUM]; int i, j; u64 val; u64 *p = &pdata->stats.tx_octets; - for (i = 0 ; i < 45; i++) + for (i = 0 ; i < PHYTMAC_STATIS_REG_NUM; i++) stats[i] = PHYTMAC_READ(pdata, PHYTMAC_OCTTX + i * 4); - for (i = 0, j = 0; i < 45; i++) { + for (i = 0, j = 0; i < PHYTMAC_STATIS_REG_NUM; i++) { if (i == 0 || i == 20) { val = (u64)stats[i + 1] << 32 | stats[i]; *p += val; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 2e9d14dcb9f31c..e43a55fe477004 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -20,44 +20,64 @@ #include "phytmac_v2.h" static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, - u16 cmd_subid, void *data, int len, int wait) + u16 cmd_subid, void *data, int len, int wait) { - int index = 0; + u32 tx_head, tx_tail, ring_size; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; int ret = 0; mutex_lock(&pdata->msg_ring.msg_mutex); - ++pdata->msg_ring.tx_msg_tail; - if (pdata->msg_ring.tx_msg_tail > pdata->msg_ring.tx_msg_ring_size) - pdata->msg_ring.tx_msg_tail = 1; - index = pdata->msg_ring.tx_msg_tail; + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + tx_tail = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_wr_tail); + pdata->msg_ring.tx_msg_rd_tail = tx_tail; + ring_size = pdata->msg_ring.tx_msg_ring_size; + + while ((tx_tail + 1) % ring_size == tx_head) { + netdev_info(pdata->ndev, "Tx msg ring is overrun, tx_tail:0x%x, tx_head:0x%x", + tx_tail, tx_head); + cpu_relax(); + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + } wait = 1; memset(&msg, 0, sizeof(msg)); memset(&msg_rx, 0, sizeof(msg_rx)); msg.cmd_type = cmd_id; msg.cmd_subid = cmd_subid; - msg.status0 = PHYTMAC_FLAGS_MSG_NOINT; - - if (len) + if (len > 0 && len <= PHYTMAC_MSG_PARA_LEN) { memcpy(&msg.para[0], data, len); + } else if (len > PHYTMAC_MSG_PARA_LEN) { + netdev_err(pdata->ndev, "Tx msg para len %d is greater than the max len %d", + len, PHYTMAC_MSG_PARA_LEN); + mutex_unlock(&pdata->msg_ring.msg_mutex); + return -EINVAL; + } if (netif_msg_hw(pdata)) { - netdev_info(pdata->ndev, "tx msg: cmdid:%d, subid:%d, status0:%d, len:%d, tail:%d\n", - msg.cmd_type, msg.cmd_subid, msg.status0, len, - pdata->msg_ring.tx_msg_tail); + netdev_info(pdata->ndev, "Tx msg: cmdid:%d, subid:%d, status0:%d, len:%d, tail:%d", + msg.cmd_type, msg.cmd_subid, msg.status0, len, tx_tail); } - memcpy(pdata->msg_regs + PHYTMAC_MSG(index), &msg, sizeof(msg)); - PHYTMAC_WRITE(pdata, PHYTMAC_TX_MSG_TAIL, - pdata->msg_ring.tx_msg_tail | PHYTMAC_BIT(TX_MSG_INT)); + memcpy(pdata->msg_regs + PHYTMAC_MSG(tx_tail), &msg, sizeof(msg)); + tx_tail = phytmac_v2_tx_ring_wrap(pdata, ++tx_tail); + PHYTMAC_WRITE(pdata, PHYTMAC_TX_MSG_TAIL, tx_tail | PHYTMAC_BIT(TX_MSG_INT)); + pdata->msg_ring.tx_msg_wr_tail = tx_tail; if (wait) { - memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); - while (!(msg_rx.status0 & PHYTMAC_CMD_PRC_COMPLETED)) { + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + while (tx_head != tx_tail) { cpu_relax(); - memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(index), MSG_HDR_LEN); + tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + } + + memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(pdata->msg_ring.tx_msg_rd_tail), + PHYTMAC_MSG_HDR_LEN); + if (!(msg_rx.status0 & PHYTMAC_CMD_PRC_SUCCESS)) { + netdev_err(pdata->ndev, "Msg process error, cmdid:%d, subid:%d, status0:%d, tail:%d", + msg.cmd_type, msg.cmd_subid, msg.status0, tx_tail); + mutex_unlock(&pdata->msg_ring.msg_mutex); + return -EINVAL; } } @@ -105,10 +125,8 @@ static int phytmac_v2_get_mac_addr(struct phytmac *pdata, u8 *addr) cmd_subid = PHYTMAC_MSG_CMD_GET_ADDR; phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, sizeof(struct phytmac_mac)); addr[0] = para.addrl & 0xff; @@ -149,7 +167,7 @@ static int phytmac_v2_pcs_software_reset(struct phytmac *pdata, int reset) else cmd_subid = PHYTMAC_MSG_CMD_SET_DISABLE_PCS_RESET; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); return 0; } @@ -225,7 +243,7 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; mdc = PHYTMAC_CLK_DIV96; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 1); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 0); memset(ð, 0, sizeof(eth)); cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; @@ -302,25 +320,23 @@ static int phytmac_v2_init_ring_hw(struct phytmac *pdata) cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_INIT_RX_RING; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&rxring), sizeof(rxring), 1); return 0; } static int phytmac_v2_init_msg_ring(struct phytmac *pdata) { - u32 size = 0; - - pdata->msg_ring.tx_msg_tail = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_TAIL) & 0xff; - size = PHYTMAC_READ_BITS(pdata, PHYTMAC_SIZE, TXRING_SIZE); - pdata->msg_ring.tx_msg_ring_size = size; - if (pdata->msg_ring.tx_msg_tail == size) - pdata->msg_ring.tx_msg_tail = 0; + u32 tx_msg_tail; + pdata->msg_ring.tx_msg_ring_size = PHYTMAC_READ_BITS(pdata, PHYTMAC_SIZE, TXRING_SIZE); + tx_msg_tail = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_TAIL) & 0xff; + pdata->msg_ring.tx_msg_wr_tail = phytmac_v2_tx_ring_wrap(pdata, tx_msg_tail); + pdata->msg_ring.tx_msg_rd_tail = pdata->msg_ring.tx_msg_wr_tail; PHYTMAC_WRITE(pdata, PHYTMAC_MSG_IMR, 0xfffffffe); if (netif_msg_hw(pdata)) - netdev_info(pdata->ndev, "mac msg ring: tx_msg_ring_size=%d, tx_msg_tail=%d\n", - size, pdata->msg_ring.tx_msg_tail); + netdev_info(pdata->ndev, "Msg ring size:%d, tx msg tail=%d\n", + pdata->msg_ring.tx_msg_ring_size, tx_msg_tail); return 0; } @@ -335,12 +351,8 @@ static int phytmac_v2_get_feature_all(struct phytmac *pdata) cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_CAPS; phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); - - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - - memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(¶, pdata->msg_regs + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, sizeof(struct phytmac_feature)); pdata->queues_max_num = para.queue_num; @@ -371,46 +383,38 @@ static void phytmac_v2_get_regs(struct phytmac *pdata, u32 *reg_buff) { u16 cmd_id, cmd_subid; int index; - u8 interface; + struct phytmac_ethtool_reg msg; + memset(&msg, 0, sizeof(msg)); cmd_id = PHYTMAC_MSG_CMD_GET; cmd_subid = PHYTMAC_MSG_CMD_GET_REGS_FOR_ETHTOOL; - interface = pdata->phytmac_v2_interface; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&interface), sizeof(interface), 1); - - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - - memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + MSG_HDR_LEN, - READ_REG_NUM_MAX * sizeof(u32)); + msg.interface = pdata->phytmac_v2_interface; + /* There are 16 regs in total, read 14 regs at first time, read 2 regs at last time */ + msg.cnt = 0; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&msg), sizeof(msg), 1); + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(reg_buff, pdata->msg_regs + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, + PHYTMAC_MSG_PARA_LEN); + + msg.cnt = 1; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&msg), sizeof(msg), 1); + index = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_rd_tail); + memcpy(reg_buff + PHYTMAC_MSG_PARA_LEN / sizeof(u32), pdata->msg_regs + + PHYTMAC_MSG(index) + PHYTMAC_MSG_HDR_LEN, + PHYTMAC_ETHTOOLD_REGS_LEN - PHYTMAC_MSG_PARA_LEN); } static void phytmac_v2_get_hw_stats(struct phytmac *pdata) { - u16 cmd_id, cmd_subid; - u8 count; - int i, j, index; - u32 stats[48]; + u32 stats[PHYTMAC_STATIS_REG_NUM]; + int i, j; u64 val; u64 *p = &pdata->stats.tx_octets; - cmd_id = PHYTMAC_MSG_CMD_GET; - cmd_subid = PHYTMAC_MSG_CMD_GET_STATS; - /* There are 45 registers in total, read 16 regs at a time, read 13 regs at last time */ - for (i = 1; i <= 3; i++) { - count = i; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&count, sizeof(count), 1); - - index = pdata->msg_ring.tx_msg_tail; - if (index <= 0) - index += pdata->msg_ring.tx_msg_ring_size; - - memcpy(&stats[(i - 1) * READ_REG_NUM_MAX], pdata->msg_regs + PHYTMAC_MSG(index) + - MSG_HDR_LEN, sizeof(u32) * READ_REG_NUM_MAX); - } + for (i = 0 ; i < PHYTMAC_STATIS_REG_NUM; i++) + stats[i] = PHYTMAC_READ(pdata, PHYTMAC_OCT_TX + i * 4); - for (i = 0, j = 0; i < 45; i++) { + for (i = 0, j = 0; i < PHYTMAC_STATIS_REG_NUM; i++) { if (i == 0 || i == 20) { val = (u64)stats[i + 1] << 32 | stats[i]; *p += val; @@ -459,7 +463,7 @@ static int phytmac_v2_mdio_data_read_c22(struct phytmac *pdata, int mii_id, int } static int phytmac_v2_mdio_data_write_c22(struct phytmac *pdata, int mii_id, - int regnum, u16 data) + int regnum, u16 data) { PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C22) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C22_WRITE) @@ -496,7 +500,7 @@ static int phytmac_v2_mdio_data_read_c45(struct phytmac *pdata, int mii_id, int } static int phytmac_v2_mdio_data_write_c45(struct phytmac *pdata, int mii_id, int devad, - int regnum, u16 data) + int regnum, u16 data) { PHYTMAC_WRITE(pdata, PHYTMAC_MDIO, (PHYTMAC_BITS(CLAUSESEL, PHYTMAC_C45) | PHYTMAC_BITS(MDCOPS, PHYTMAC_C45_ADDR) @@ -538,7 +542,7 @@ static int phytmac_v2_powerup_hw(struct phytmac *pdata, int on) handle = ACPI_HANDLE(pdata->dev); netdev_info(pdata->ndev, "set gmac power %s\n", - on == PHYTMAC_POWERON ? "on" : "off"); + on == PHYTMAC_POWERON ? "on" : "off"); args[0].type = ACPI_TYPE_INTEGER; args[0].integer.value = PHYTMAC_PWCTL_GMAC_ID; args[1].type = ACPI_TYPE_INTEGER; @@ -597,10 +601,10 @@ static int phytmac_v2_powerup_hw(struct phytmac *pdata, int on) rdata1 = PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA1); if (rdata1 == data1) netdev_err(pdata->ndev, "gmac power %s success, data1 = %x, rdata1=%x\n", - on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); else netdev_err(pdata->ndev, "gmac power %s failed, data1 = %x, rdata1=%x\n", - on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); + on == PHYTMAC_POWERON ? "up" : "down", data1, rdata1); } pdata->power_state = on; @@ -832,7 +836,7 @@ static u32 phytmac_v2_get_irq_status(u32 value) } static void phytmac_v2_enable_irq(struct phytmac *pdata, - int queue_index, u32 mask) + int queue_index, u32 mask) { u32 value; @@ -841,7 +845,7 @@ static void phytmac_v2_enable_irq(struct phytmac *pdata, } static void phytmac_v2_disable_irq(struct phytmac *pdata, - int queue_index, u32 mask) + int queue_index, u32 mask) { u32 value; @@ -850,7 +854,7 @@ static void phytmac_v2_disable_irq(struct phytmac *pdata, } static void phytmac_v2_clear_irq(struct phytmac *pdata, - int queue_index, u32 mask) + int queue_index, u32 mask) { u32 value; @@ -870,7 +874,7 @@ static unsigned int phytmac_v2_get_irq(struct phytmac *pdata, int queue_index) } static void phytmac_v2_interface_config(struct phytmac *pdata, unsigned int mode, - const struct phylink_link_state *state) + const struct phylink_link_state *state) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; @@ -900,7 +904,7 @@ static void phytmac_v2_interface_config(struct phytmac *pdata, unsigned int mode } static int phytmac_v2_interface_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) + int speed, int duplex) { struct phytmac_interface_info para; u16 cmd_id, cmd_subid; @@ -928,7 +932,7 @@ static int phytmac_v2_interface_linkdown(struct phytmac *pdata) } static int phytmac_v2_pcs_linkup(struct phytmac *pdata, phy_interface_t interface, - int speed, int duplex) + int speed, int duplex) { u16 cmd_id, cmd_subid; @@ -937,7 +941,7 @@ static int phytmac_v2_pcs_linkup(struct phytmac *pdata, phy_interface_t interfac cmd_id = PHYTMAC_MSG_CMD_SET; cmd_subid = PHYTMAC_MSG_CMD_SET_PCS_LINK_UP; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 0); + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, NULL, 0, 1); } return 0; @@ -962,7 +966,7 @@ static unsigned int phytmac_v2_pcs_get_link(struct phytmac *pdata, phy_interface } static unsigned int phytmac_v2_tx_map_desc(struct phytmac_queue *queue, - u32 tx_tail, struct packet_info *packet) + u32 tx_tail, struct packet_info *packet) { unsigned int i, ctrl; struct phytmac *pdata = queue->pdata; @@ -1006,7 +1010,7 @@ static unsigned int phytmac_v2_tx_map_desc(struct phytmac_queue *queue, } static void phytmac_v2_init_rx_map_desc(struct phytmac_queue *queue, - u32 index) + u32 index) { struct phytmac_dma_desc *desc; diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 1303bde7cff058..4e195dcef04fe8 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -6,10 +6,11 @@ extern struct phytmac_hw_if phytmac_2p0_hw; -#define PHYTMAC_CMD_PRC_COMPLETED 0x1 +#define PHYTMAC_CMD_PRC_SUCCESS 0x1 #define PHYTMAC_MSG_SRAM_SIZE 4096 -#define MSG_HDR_LEN 8 -#define READ_REG_NUM_MAX 16 +#define PHYTMAC_MSG_HDR_LEN 8 +#define PHYTMAC_MSG_PARA_LEN 56 +#define PHYTMAC_READ_REG_NUM_MAX (PHYTMAC_MSG_PARA_LEN / sizeof(u32)) #define PHYTMAC_TX_MSG_HEAD 0x000 #define PHYTMAC_TX_MSG_TAIL 0x004 @@ -30,7 +31,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_TIMER_SEC 0x0258 #define PHYTMAC_TIMER_NSEC 0x025c #define PHYTMAC_TIMER_ADJUST 0x0260 -#define PHYTMAC_MSG(i) (((i) - 1) * 0x48) +#define PHYTMAC_MSG(i) ((i) * sizeof(struct phytmac_msg_info)) +#define PHYTMAC_OCT_TX 0x400 #define PHYTMAC_MODULE_ID_GMAC 0x60 #define PHYTMAC_FLAGS_MSG_COMP 0x1 @@ -255,7 +257,7 @@ enum phytmac_msg_cmd_id { }; enum phytmac_default_subid { - PHYTMAC_MSG_CMD_DEFAULT_RESET_HW = 0, + PHYTMAC_MSG_CMD_DEFAULT_RESET_HW = 1, PHYTMAC_MSG_CMD_DEFAULT_RESET_TX_QUEUE, PHYTMAC_MSG_CMD_DEFAULT_RESET_RX_QUEUE, }; @@ -415,7 +417,7 @@ struct phytmac_msg_info { u16 len; u8 status1; u8 status0; - u8 para[64]; + u8 para[PHYTMAC_MSG_PARA_LEN]; } __packed; struct phytmac_ots_config { @@ -424,4 +426,14 @@ struct phytmac_ots_config { u8 queuenum; } __packed; +struct phytmac_ethtool_reg { + u8 interface; + u8 cnt; +} __packed; + +static inline unsigned int phytmac_v2_tx_ring_wrap(struct phytmac *pdata, unsigned int index) +{ + return index & (pdata->msg_ring.tx_msg_ring_size - 1); +} + #endif From 84624dc9569243e27ebd3a582e7002a6de136a7c Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:12 +0800 Subject: [PATCH 059/258] PHYTIUM: net/phytmac: Bugfix set WOL failed issue Before configuring WOL, we need to obtain the packet types that WOL supports. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I22ed39f691044f35fa09ee76788711fc723fc6fc Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/b44b37fdc57a32edb8bf0a5e763ea71c716cce09 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- .../net/ethernet/phytium/phytmac_ethtool.c | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 2c0bbb8f277203..bac59327087fc9 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -90,24 +90,20 @@ static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol { struct phytmac *pdata = netdev_priv(ndev); + wol->wolopts = 0; phylink_ethtool_get_wol(pdata->phylink, wol); - if (pdata->wol & PHYTMAC_WAKE_MAGIC) { + wol->supported = WAKE_MAGIC | WAKE_ARP | + WAKE_UCAST | WAKE_MCAST; + + if (pdata->wol & PHYTMAC_WAKE_MAGIC) wol->wolopts |= WAKE_MAGIC; - wol->supported |= WAKE_MAGIC; - } - if (pdata->wol & PHYTMAC_WAKE_ARP) { + if (pdata->wol & PHYTMAC_WAKE_ARP) wol->wolopts |= WAKE_ARP; - wol->supported |= WAKE_ARP; - } - if (pdata->wol & PHYTMAC_WAKE_UCAST) { + if (pdata->wol & PHYTMAC_WAKE_UCAST) wol->wolopts |= WAKE_UCAST; - wol->supported |= WAKE_UCAST; - } - if (pdata->wol & PHYTMAC_WAKE_MCAST) { + if (pdata->wol & PHYTMAC_WAKE_MCAST) wol->wolopts |= WAKE_MCAST; - wol->supported |= WAKE_MCAST; - } } static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) From 21e98054ac40ed5b5ab0221c8228577044c024f3 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:13 +0800 Subject: [PATCH 060/258] PHYTIUM: net/phytmac: Bugfix invalid wait context After the spinlock is called, the mutex should no longer to be invoked, which will lead to unnecessary sleep. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I325188e12bdf0674454aba238624d05e4d877d38 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/d35f06971ac92e652e17033d40067c58e0cfd90c Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 6 ++---- drivers/net/ethernet/phytium/phytmac_main.c | 6 +----- drivers/net/ethernet/phytium/phytmac_v2.c | 10 ++++------ 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index e8a640de47a69b..7acfc97d3fc293 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -434,8 +434,8 @@ struct phytmac_msg { u32 tx_msg_rd_tail; u32 rx_msg_head; u32 rx_msg_tail; - /*use msg_mutex to protect msg */ - struct mutex msg_mutex; + /*use msg_lock to protect msg */ + spinlock_t msg_lock; }; struct ts_ctrl { @@ -473,8 +473,6 @@ struct phytmac { struct work_struct restart_task; /* Lock to protect mac config */ spinlock_t lock; - /* Lock to protect msg tx */ - spinlock_t msg_lock; u32 rx_ring_size; u32 tx_ring_size; u32 dma_data_width; diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index df0aea0de7a412..765b01503c1f40 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2725,8 +2725,6 @@ int phytmac_drv_probe(struct phytmac *pdata) if (hw_if->init_msg_ring) hw_if->init_msg_ring(pdata); - mutex_init(&pdata->msg_ring.msg_mutex); - if (pdata->use_mii && !pdata->mii_bus) { ret = phytmac_mdio_register(pdata); if (ret) { @@ -2793,8 +2791,6 @@ int phytmac_drv_remove(struct phytmac *pdata) if (pdata->phylink) phylink_destroy(pdata->phylink); - - mutex_destroy(&pdata->msg_ring.msg_mutex); } return 0; @@ -2904,7 +2900,7 @@ struct phytmac *phytmac_alloc_pdata(struct device *dev) pdata->dev = dev; spin_lock_init(&pdata->lock); - spin_lock_init(&pdata->msg_lock); + spin_lock_init(&pdata->msg_ring.msg_lock); spin_lock_init(&pdata->ts_clk_lock); pdata->msg_enable = netif_msg_init(debug, PHYTMAC_DEFAULT_MSG_ENABLE); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index e43a55fe477004..65db2c0d15dea8 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -27,7 +27,7 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, struct phytmac_msg_info msg_rx; int ret = 0; - mutex_lock(&pdata->msg_ring.msg_mutex); + spin_lock(&pdata->msg_ring.msg_lock); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; tx_tail = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_wr_tail); pdata->msg_ring.tx_msg_rd_tail = tx_tail; @@ -36,7 +36,6 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, while ((tx_tail + 1) % ring_size == tx_head) { netdev_info(pdata->ndev, "Tx msg ring is overrun, tx_tail:0x%x, tx_head:0x%x", tx_tail, tx_head); - cpu_relax(); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; } @@ -50,7 +49,7 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, } else if (len > PHYTMAC_MSG_PARA_LEN) { netdev_err(pdata->ndev, "Tx msg para len %d is greater than the max len %d", len, PHYTMAC_MSG_PARA_LEN); - mutex_unlock(&pdata->msg_ring.msg_mutex); + spin_unlock(&pdata->msg_ring.msg_lock); return -EINVAL; } @@ -67,7 +66,6 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (wait) { tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; while (tx_head != tx_tail) { - cpu_relax(); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; } @@ -76,12 +74,12 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (!(msg_rx.status0 & PHYTMAC_CMD_PRC_SUCCESS)) { netdev_err(pdata->ndev, "Msg process error, cmdid:%d, subid:%d, status0:%d, tail:%d", msg.cmd_type, msg.cmd_subid, msg.status0, tx_tail); - mutex_unlock(&pdata->msg_ring.msg_mutex); + spin_unlock(&pdata->msg_ring.msg_lock); return -EINVAL; } } - mutex_unlock(&pdata->msg_ring.msg_mutex); + spin_unlock(&pdata->msg_ring.msg_lock); return ret; } From 93e4900b3f6c5f4ffff1cbb2e726b70a0b4a5e05 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:13 +0800 Subject: [PATCH 061/258] PHYTIUM: net/phytmac: Change the requested resource type from nRE to nRnE The resource type is changed from nRE to nRnE to avoid interrupt loss at low probability of occurrence. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: If47dd823936b7c66b809952c844e2acff06a8ed1 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/cd72b3d4a536cd3f67210de692345ae704133d5f Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 + drivers/net/ethernet/phytium/phytmac_main.c | 66 +++++++++++++++++++ .../net/ethernet/phytium/phytmac_platform.c | 3 +- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 7acfc97d3fc293..6b72b45bc0d4a7 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -680,6 +680,8 @@ struct phytmac_hw_if { #define PHYTMAC_RX_PAGE_ORDER 0 #define PHYTMAC_RX_PAGE_SIZE (PAGE_SIZE << PHYTMAC_RX_PAGE_ORDER) +void __iomem * +phytmac_devm_ioremap_resource_np(struct device *dev, const struct resource *res); struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, unsigned int index); inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 765b01503c1f40..c32dd959cca556 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2933,6 +2933,72 @@ void phytmac_drv_shutdown(struct phytmac *pdata) } EXPORT_SYMBOL_GPL(phytmac_drv_shutdown); +static void phytmac_devm_iounmap_np(struct device *dev, void *res) +{ + iounmap(*(void __iomem **)res); +} + +static void __iomem *phytmac_devm_ioremap_np(struct device *dev, resource_size_t offset, + resource_size_t size) +{ + void __iomem **ptr, *addr = NULL; + + ptr = devres_alloc_node(phytmac_devm_iounmap_np, sizeof(*ptr), GFP_KERNEL, + dev_to_node(dev)); + if (!ptr) + return NULL; + + addr = ioremap_np(offset, size); + if (addr) { + *ptr = addr; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return addr; +} + +void __iomem * +phytmac_devm_ioremap_resource_np(struct device *dev, const struct resource *res) +{ + resource_size_t size; + void __iomem *dest_ptr; + char *pretty_name; + + if (!res || resource_type(res) != IORESOURCE_MEM) { + dev_err(dev, "invalid resource %pR\n", res); + return IOMEM_ERR_PTR(-EINVAL); + } + + size = resource_size(res); + + if (res->name) + pretty_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s", + dev_name(dev), res->name); + else + pretty_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL); + if (!pretty_name) { + dev_err(dev, "can't generate pretty name for resource %pR\n", res); + return IOMEM_ERR_PTR(-ENOMEM); + } + + if (!devm_request_mem_region(dev, res->start, size, pretty_name)) { + dev_err(dev, "can't request region for resource %pR\n", res); + return IOMEM_ERR_PTR(-EBUSY); + } + + dest_ptr = phytmac_devm_ioremap_np(dev, res->start, size); + if (!dest_ptr) { + dev_err(dev, "ioremap failed for resource %pR\n", res); + devm_release_mem_region(dev, res->start, size); + dest_ptr = IOMEM_ERR_PTR(-ENOMEM); + } + + return dest_ptr; +} +EXPORT_SYMBOL_GPL(phytmac_devm_ioremap_resource_np); + MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index eb3cc7d8f42906..540159f8fc3a83 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -203,7 +203,8 @@ static int phytmac_plat_probe(struct platform_device *pdev) } i = 0; - pdata->mac_regs = devm_platform_get_and_ioremap_resource(pdev, i, ®s); + regs = platform_get_resource(pdev, IORESOURCE_MEM, i); + pdata->mac_regs = phytmac_devm_ioremap_resource_np(&pdev->dev, regs); if (IS_ERR(pdata->mac_regs)) { dev_err(&pdev->dev, "mac_regs ioremap failed\n"); ret = PTR_ERR(pdata->mac_regs); From 0e937eff8f6726ea90e7ac5e59a52232fb2ba349 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:13 +0800 Subject: [PATCH 062/258] PHYTIUM: net/phytmac: BugFix Memory leak when releasing resource The size of the memory released in the dma_free_coherent is smaller than the size of the memory allocated in the dma_alloc_coherent, which will lead to memory leakage and fragmentation. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: If06e83aec976cc485829b8e75d07e22ae7c6cfae Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/93ba71759d83f0122e818867a56bffcdc31f278d Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 3 --- drivers/net/ethernet/phytium/phytmac_main.c | 12 ++++++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 6b72b45bc0d4a7..09e3c99232d354 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -114,9 +114,6 @@ #define PHYTMAC_MSG_READ(_pdata, _reg) \ __raw_readl((_pdata)->mac_regs + (_reg)) -#define PHYTMAC_WRITE(_pdata, _reg, _val) \ - __raw_writel((_val), (_pdata)->mac_regs + (_reg)) - #define LSO_UFO 1 #define LSO_TSO 2 diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index c32dd959cca556..eeaf60a6e9cde1 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -391,6 +391,7 @@ static int phytmac_free_tx_resource(struct phytmac *pdata) struct phytmac_dma_desc *tx_ring_base = NULL; dma_addr_t tx_ring_base_addr; unsigned int q; + int tx_offset; int size; queue = pdata->queues; @@ -408,8 +409,9 @@ static int phytmac_free_tx_resource(struct phytmac *pdata) } if (tx_ring_base) { - size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + - RING_ADDR_INTERVAL); + tx_offset = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + tx_offset = ALIGN(tx_offset, 4096); + size = pdata->queues_num * tx_offset; dma_free_coherent(pdata->dev, size, tx_ring_base, tx_ring_base_addr); } @@ -422,6 +424,7 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) struct phytmac_dma_desc *rx_ring_base = NULL; dma_addr_t rx_ring_base_addr; unsigned int q; + int rx_offset; int size; queue = pdata->queues; @@ -448,8 +451,9 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) } if (rx_ring_base) { - size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + - RING_ADDR_INTERVAL); + rx_offset = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + rx_offset = ALIGN(rx_offset, 4096); + size = pdata->queues_num * rx_offset; dma_free_coherent(pdata->dev, size, rx_ring_base, rx_ring_base_addr); } From 4c5cf62e3bca7874f902b0f4393738a358255e38 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Wed, 7 May 2025 17:22:13 +0800 Subject: [PATCH 063/258] PHYTIUM: net/phytmac: Limit the number of retries to avoid deadlock It is need to limit retries that messages are processed to avoid deadlock, when RV has no response. Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ic300b8c8544af91c62564caf538a2a2eb6c0b512 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/d8fbda0b50a320673194e971fac55b6adbb2a85f Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_v2.c | 20 ++++++++++++++++++-- drivers/net/ethernet/phytium/phytmac_v2.h | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 65db2c0d15dea8..20c49bf5f4b86e 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -25,6 +25,7 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, u32 tx_head, tx_tail, ring_size; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; + u32 retry = 0; int ret = 0; spin_lock(&pdata->msg_ring.msg_lock); @@ -34,11 +35,19 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, ring_size = pdata->msg_ring.tx_msg_ring_size; while ((tx_tail + 1) % ring_size == tx_head) { - netdev_info(pdata->ndev, "Tx msg ring is overrun, tx_tail:0x%x, tx_head:0x%x", - tx_tail, tx_head); + udelay(1); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + retry++; + if (retry >= PHYTMAC_RETRY_TIMES) { + netdev_err(pdata->ndev, + "Time out waiting for Tx msg ring free, tx_tail:0x%x, tx_head:0x%x", + tx_tail, tx_head); + spin_unlock(&pdata->msg_ring.msg_lock); + return -EINVAL; + } } + retry = 0; wait = 1; memset(&msg, 0, sizeof(msg)); memset(&msg_rx, 0, sizeof(msg_rx)); @@ -66,7 +75,14 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (wait) { tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; while (tx_head != tx_tail) { + udelay(1); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; + retry++; + if (retry >= PHYTMAC_RETRY_TIMES) { + netdev_err(pdata->ndev, "Msg process time out!"); + spin_unlock(&pdata->msg_ring.msg_lock); + return -EINVAL; + } } memcpy(&msg_rx, pdata->msg_regs + PHYTMAC_MSG(pdata->msg_ring.tx_msg_rd_tail), diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index 4e195dcef04fe8..b32044d696ae2d 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -246,6 +246,8 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_CLK_DIV128 6 #define PHYTMAC_CLK_DIV224 7 +#define PHYTMAC_RETRY_TIMES 50000 + #define PHYTMAC_READ_NSR(pdata) PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS) enum phytmac_msg_cmd_id { From ca1974c1875915964ec2b41e9130ca8d997cbc2e Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Fri, 30 May 2025 11:41:46 +0800 Subject: [PATCH 064/258] PHYTIUM: net: phytmac: update to 1.0.47 Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/f12de776aa34dbdc7e389542ade8a67325d18925 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 8 +++++--- drivers/net/ethernet/phytium/phytmac_ethtool.c | 9 ++++++--- drivers/net/ethernet/phytium/phytmac_main.c | 17 ++++++++++++++--- drivers/net/ethernet/phytium/phytmac_pci.c | 4 ++-- drivers/net/ethernet/phytium/phytmac_platform.c | 2 +- drivers/net/ethernet/phytium/phytmac_v1.c | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 2 +- drivers/net/ethernet/phytium/phytmac_v2.h | 2 +- 8 files changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 09e3c99232d354..ee22cda030e94a 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -14,9 +14,10 @@ #include #include -#define PHYTMAC_DRV_NAME "phytium-mac" +#define PHYTMAC_PCI_DRV_NAME "phytmac_pci" +#define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.45" +#define PHYTMAC_DRIVER_VERSION "1.0.47" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -54,7 +55,7 @@ #define DEFAULT_MSG_RING_SIZE 16 -#define PHYTMAC_MDIO_TIMEOUT 1000000 /* in usecs */ +#define PHYTMAC_MDIO_TIMEOUT 1000000 /* in usecs */ #define PHYTMAC_CAPS_JUMBO 0x00000001 #define PHYTMAC_CAPS_PTP 0x00000002 @@ -510,6 +511,7 @@ struct phytmac { spinlock_t rx_fs_lock; unsigned int max_rx_fs; u32 version; + char fw_version[32]; }; /* phytmac_desc_unused - calculate if we have unused descriptors */ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index bac59327087fc9..26478b090ced0d 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -506,13 +506,16 @@ static void phytmac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo { struct phytmac *pdata = netdev_priv(ndev); - strscpy(drvinfo->driver, PHYTMAC_DRV_NAME, sizeof(drvinfo->driver)); strscpy(drvinfo->version, PHYTMAC_DRIVER_VERSION, sizeof(drvinfo->version)); + strscpy(drvinfo->fw_version, pdata->fw_version, sizeof(drvinfo->fw_version)); - if (pdata->platdev) + if (pdata->platdev) { + strscpy(drvinfo->driver, PHYTMAC_PLAT_DRV_NAME, sizeof(drvinfo->driver)); strscpy(drvinfo->bus_info, pdata->platdev->name, sizeof(drvinfo->bus_info)); - else if (pdata->pcidev) + } else if (pdata->pcidev) { + strscpy(drvinfo->driver, PHYTMAC_PCI_DRV_NAME, sizeof(drvinfo->driver)); strscpy(drvinfo->bus_info, pci_name(pdata->pcidev), sizeof(drvinfo->bus_info)); + } } static const struct ethtool_ops phytmac_ethtool_ops = { diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index eeaf60a6e9cde1..9ed5ec09523b3e 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -62,7 +62,7 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); /* Max length of transmit frame must be a multiple of 8 bytes */ #define PHYTMAC_TX_LEN_ALIGN 8 -/* Limit maximum TX length as per Cadence TSO errata. This is to avoid a +/* Limit maximum TX length as per TSO errata. This is to avoid a * false amba_error in TX path from the DMA assuming there is not enough * space in the SRAM (16KB) even when there is. */ @@ -584,7 +584,8 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) } xdp_rxq_info_unreg_mem_model(&queue->xdp_rxq); - WARN_ON(xdp_rxq_info_reg_mem_model(&queue->xdp_rxq, MEM_TYPE_PAGE_SHARED, NULL)); + WARN_ON(xdp_rxq_info_reg_mem_model(&queue->xdp_rxq, + MEM_TYPE_PAGE_SHARED, NULL)); } return 0; @@ -2684,6 +2685,16 @@ void phytmac_default_config(struct phytmac *pdata) ndev->features = ndev->hw_features; ndev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT; + + switch (pdata->version) { + case VERSION_V3: + strscpy(pdata->fw_version, "MAC_FTM300", sizeof(pdata->fw_version)); + break; + + default: + strscpy(pdata->fw_version, "", sizeof(pdata->fw_version)); + break; + } } static void phytmac_ncsi_handler(struct ncsi_dev *nd) @@ -2835,8 +2846,8 @@ int phytmac_drv_suspend(struct phytmac *pdata) rtnl_unlock(); spin_lock_irqsave(&pdata->lock, flags); hw_if->reset_hw(pdata); - hw_if->poweron(pdata, PHYTMAC_POWEROFF); spin_unlock_irqrestore(&pdata->lock, flags); + hw_if->poweron(pdata, PHYTMAC_POWEROFF); } return 0; diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index 62766c49d1a626..fac5d717ad7669 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -74,7 +74,7 @@ static int phytmac_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i /* Obtain the mmio areas for the device */ bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); - ret = pcim_iomap_regions(pdev, bar_mask, PHYTMAC_DRV_NAME); + ret = pcim_iomap_regions(pdev, bar_mask, PHYTMAC_PCI_DRV_NAME); if (ret) { dev_err(dev, "pcim_iomap_regions failed\n"); goto err_pci_enable; @@ -305,7 +305,7 @@ static const struct dev_pm_ops phytmac_pci_pm_ops = { }; static struct pci_driver phytmac_driver = { - .name = PHYTMAC_DRV_NAME, + .name = PHYTMAC_PCI_DRV_NAME, .id_table = phytmac_pci_table, .probe = phytmac_pci_probe, .remove = phytmac_pci_remove, diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 540159f8fc3a83..2f2170cf0775ae 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -356,7 +356,7 @@ static struct platform_driver phytmac_driver = { .probe = phytmac_plat_probe, .remove = phytmac_plat_remove, .driver = { - .name = PHYTMAC_DRV_NAME, + .name = PHYTMAC_PLAT_DRV_NAME, .of_match_table = of_match_ptr(phytmac_dt_ids), .acpi_match_table = phytmac_acpi_ids, .pm = &phytmac_plat_pm_ops, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index 515825627fedc1..870f07edc58fc5 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -564,7 +564,7 @@ static int phytmac_mdio_idle(struct phytmac *pdata) int ret; /* wait for end of transfer */ - ret = readx_poll_timeout(PHTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(NDI_IDLE), + ret = readx_poll_timeout(PHYTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(MDIO_IDLE), 1, PHYTMAC_MDIO_TIMEOUT); if (ret) netdev_err(pdata->ndev, "mdio wait for idle time out!"); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 20c49bf5f4b86e..1afff67d625d99 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -453,7 +453,7 @@ static int phytmac_v2_mdio_idle(struct phytmac *pdata) int ret; /* wait for end of transfer */ - ret = readx_poll_timeout(PHTMAC_READ_NSTATUS, pdata, val, val & PHYTMAC_BIT(NDI_IDLE), + ret = readx_poll_timeout(PHYTMAC_READ_NSR, pdata, val, val & PHYTMAC_BIT(MIDLE), 1, PHYTMAC_MDIO_TIMEOUT); if (ret) netdev_err(pdata->ndev, "mdio wait for idle time out!"); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.h b/drivers/net/ethernet/phytium/phytmac_v2.h index b32044d696ae2d..c9e159b1eb9b64 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.h +++ b/drivers/net/ethernet/phytium/phytmac_v2.h @@ -248,7 +248,7 @@ extern struct phytmac_hw_if phytmac_2p0_hw; #define PHYTMAC_RETRY_TIMES 50000 -#define PHYTMAC_READ_NSR(pdata) PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS) +#define PHYTMAC_READ_NSR(pdata) PHYTMAC_READ(pdata, PHYTMAC_NETWORK_STATUS) enum phytmac_msg_cmd_id { PHYTMAC_MSG_CMD_DEFAULT = 0, From 4756e39d6feda780986c9e48f8371a39051a52d8 Mon Sep 17 00:00:00 2001 From: zuoqian Date: Thu, 26 Jun 2025 14:43:04 +0800 Subject: [PATCH 065/258] PHYTIUM: net: phytmac: fix page refcounting for XDP_REDIRECT The current page counting scheme assumes that the reference count cannot decrease until the received frame is sent to the upper layers of the networking stack. This assumption does not hold for the XDP_REDIRECT action, particularly with BPF_MAP_TYPE_XSKMAP handling, since a page (pointed out by xdp_buff) can have its reference count decreased via the xdp_do_redirect call. To work around that, now start off by a large page count and then don't allow a refcount less than two. Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/ecb37f4f54397f2d41013318df9a5fde759da478 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 9ed5ec09523b3e..ece14f0a7b47eb 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -739,7 +739,8 @@ static bool phytmac_alloc_mapped_page(struct phytmac *pdata, bi->addr = dma; bi->page = page; bi->page_offset = PHYTMAC_SKB_PAD; - bi->pagecnt_bias = 1; + page_ref_add(page, USHRT_MAX - 1); + bi->pagecnt_bias = USHRT_MAX; return true; } @@ -769,8 +770,8 @@ static bool phytmac_can_reuse_rx_page(struct phytmac_rx_buffer *rx_buffer) * the pagecnt_bias and page count so that we fully restock the * number of references the driver holds. */ - if (unlikely(!pagecnt_bias)) { - page_ref_add(page, USHRT_MAX); + if (unlikely(pagecnt_bias == 1)) { + page_ref_add(page, USHRT_MAX - 1); rx_buffer->pagecnt_bias = USHRT_MAX; } From 7ec181d32c291f37d1c00a812a590771a68a3499 Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 18 Jul 2025 15:57:40 +0800 Subject: [PATCH 066/258] PHYTIUM: net/phytmac: Fix compile error on X86 architecture Add dependency on ARCH_PHYTIUM to slove the compilation problem of hybrid architecture, such as X86 and RISCV. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ie2d1bafaa559ece975d0a892cc758ff210776efd Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/fc38c345dd09bec8628c897823a62428200bfea5 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/Kconfig | 1 + drivers/net/ethernet/phytium/phytmac.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig index 14a77adbfdc12f..e68ca5d82181e8 100644 --- a/drivers/net/ethernet/phytium/Kconfig +++ b/drivers/net/ethernet/phytium/Kconfig @@ -5,6 +5,7 @@ config NET_VENDOR_PHYTIUM bool "Phytium devices" + depends on ARCH_PHYTIUM depends on HAS_IOMEM default y help diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index ee22cda030e94a..7d1ecbacf9c1a9 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -17,7 +17,7 @@ #define PHYTMAC_PCI_DRV_NAME "phytmac_pci" #define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.47" +#define PHYTMAC_DRIVER_VERSION "1.0.48" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ From 78428fd73071114ff55505ae9536c64c479f3efb Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 18 Jul 2025 16:07:06 +0800 Subject: [PATCH 067/258] PHYTIUM: net/phytmac: Fix page refcounting issue for XDP_REDIRECT The current page counting scheme assumes that the reference count cannot decrease until the received frame is sent to the upper layers of the networking stack. This assumption does not hold for the XDP_REDIRECT action, particularly with BPF_MAP_TYPE_XSKMAP handling, since a page that pointed out by xdp_buff can have its reference count decreased via the xdp_do_redirect call. To work around that, now start off by a large page count and then don't allow a refcount less than two. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: Ieea455dabd7c5f4827b560bf82bd81ba6bd18784 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/71bc411dd6878806fe5590276b04ea8f9c9741ae Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index 7d1ecbacf9c1a9..f483524b395016 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -17,7 +17,7 @@ #define PHYTMAC_PCI_DRV_NAME "phytmac_pci" #define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.48" +#define PHYTMAC_DRIVER_VERSION "1.0.49" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ From 9432a7e498f3e6b103119732f04a9d5a508812fa Mon Sep 17 00:00:00 2001 From: Li Wencheng Date: Fri, 18 Jul 2025 16:14:52 +0800 Subject: [PATCH 068/258] PHYTIUM: net/phytmac: Bugfix the potential deadlock issue If the SOFTIRQ-unsafe lock is called inside the SOFTIRQ-safe lock, it will cause high potential deadlock risk. In dependency chain &(&mc->mca_lock)->rlock --> &dev->addr_list_lock_key --> &(&pdata->msg_ring.msg_lock)->rlock, msg_ring.msg_lock use spin_lock, which is SOFTIRQ-unsafe. it should be modified to use spin_lock_irqsave. Mainline: Open-Source Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng Change-Id: I08e5787cd8537414ef132004d21d6a4504738304 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/39f4e319cea6eb80aab0e997335db58d2f21d381 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac.h | 2 +- drivers/net/ethernet/phytium/phytmac_v2.c | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index f483524b395016..c3feca3d9611a0 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -17,7 +17,7 @@ #define PHYTMAC_PCI_DRV_NAME "phytmac_pci" #define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.49" +#define PHYTMAC_DRIVER_VERSION "1.0.50" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 1afff67d625d99..4dd3ddd18e7144 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -25,10 +25,11 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, u32 tx_head, tx_tail, ring_size; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; + unsigned long flags; u32 retry = 0; int ret = 0; - spin_lock(&pdata->msg_ring.msg_lock); + spin_lock_irqsave(&pdata->msg_ring.msg_lock, flags); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; tx_tail = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_wr_tail); pdata->msg_ring.tx_msg_rd_tail = tx_tail; @@ -42,8 +43,8 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, netdev_err(pdata->ndev, "Time out waiting for Tx msg ring free, tx_tail:0x%x, tx_head:0x%x", tx_tail, tx_head); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } } @@ -58,8 +59,8 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, } else if (len > PHYTMAC_MSG_PARA_LEN) { netdev_err(pdata->ndev, "Tx msg para len %d is greater than the max len %d", len, PHYTMAC_MSG_PARA_LEN); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } if (netif_msg_hw(pdata)) { @@ -80,8 +81,8 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, retry++; if (retry >= PHYTMAC_RETRY_TIMES) { netdev_err(pdata->ndev, "Msg process time out!"); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } } @@ -90,12 +91,13 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (!(msg_rx.status0 & PHYTMAC_CMD_PRC_SUCCESS)) { netdev_err(pdata->ndev, "Msg process error, cmdid:%d, subid:%d, status0:%d, tail:%d", msg.cmd_type, msg.cmd_subid, msg.status0, tx_tail); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } } - spin_unlock(&pdata->msg_ring.msg_lock); +err_out: + spin_unlock_irqrestore(&pdata->msg_ring.msg_lock, flags); return ret; } From 71cb89f6f5bccd628c48ec9fcc03b8b57c46f6ef Mon Sep 17 00:00:00 2001 From: zuoqian Date: Sun, 28 Sep 2025 14:38:21 +0800 Subject: [PATCH 069/258] PHYTIUM: net: phytmac: fix fixed-link link down ethtool status Add reset pdata->speed & duplex when not link in fixed-link mode. When link is down, ethtool will show: ... Speed: Unknown! Duplex: Unknown! (255) ... rather than the old speed & duplex info at link up. Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/d93fa9c65b76e648488778d48eb205a47bcd04e3 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 26478b090ced0d..cd9d702da113ff 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -384,8 +384,13 @@ static int phytmac_get_link_ksettings(struct net_device *ndev, if (!ndev->phydev) { kset->base.port = PORT_FIBRE; kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = pdata->duplex; - kset->base.speed = pdata->speed; + if (netif_carrier_ok(ndev)) { + kset->base.duplex = pdata->duplex; + kset->base.speed = pdata->speed; + } else { + kset->base.duplex = DUPLEX_UNKNOWN; + kset->base.speed = SPEED_UNKNOWN; + } if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { From 633912c14e914c486450426507acc43406faf82d Mon Sep 17 00:00:00 2001 From: liutianyu1250 Date: Thu, 16 Oct 2025 18:05:41 +0800 Subject: [PATCH 070/258] PHYTIUM: net/phytmac: update mdc early to fix PHY ID read issue The MDC modification in the driver was previously positioned after mdiobus_register, which caused the MDC not to be properly configured when reading the PHY ID. This resulted in potential issues during PHY ID detection. This commit adjusts the driver by moving the MDC modification logic to before the call to mdiobus_register, ensuring the MDC is correctly set up prior to PHY ID reading operations. Signed-off-by: liutianyu1250 Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/7c9ca9af7d15d02f20347c3030f179881f3eb596 Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_v2.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 4dd3ddd18e7144..0df1771ca3972f 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -194,7 +194,6 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) struct phytmac_dma_info dma; struct phytmac_eth_info eth; u32 ptrconfig = 0; - u8 mdc; if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) ptrconfig |= PHYTMAC_BIT(TXTAIL_EN); @@ -257,10 +256,6 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) dma.hw_dma_cap |= HW_DMA_CAP_DDW128; phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); - cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; - mdc = PHYTMAC_CLK_DIV96; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 0); - memset(ð, 0, sizeof(eth)); cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; eth.index = 0; @@ -705,8 +700,13 @@ static int phytmac_v2_enable_txcsum(struct phytmac *pdata, int enable) static int phytmac_v2_enable_mdio(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; + u8 mdc; cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; + mdc = PHYTMAC_CLK_DIV96; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 0); + if (enable) cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; else From 1779442e736812e352f0c3e9b8b70200c8b4832a Mon Sep 17 00:00:00 2001 From: zuoqian Date: Thu, 16 Oct 2025 17:26:01 +0800 Subject: [PATCH 071/258] PHYTIUM: net: phytmac: support ipv6 TSO Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/9bc24c634575287721a14bbd1aa0944dd17d9bfe Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index ece14f0a7b47eb..ccdb2be4ab0bb2 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2365,7 +2365,7 @@ static netdev_features_t phytmac_features_check(struct sk_buff *skb, hdrlen = skb_transport_offset(skb); if (!IS_ALIGNED(skb_headlen(skb) - hdrlen, PHYTMAC_TX_LEN_ALIGN)) - return features & ~NETIF_F_TSO; + return features & ~(NETIF_F_TSO | NETIF_F_TSO6); nr_frags = skb_shinfo(skb)->nr_frags; /* No need to check last fragment */ @@ -2374,7 +2374,7 @@ static netdev_features_t phytmac_features_check(struct sk_buff *skb, const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; if (!IS_ALIGNED(skb_frag_size(frag), PHYTMAC_TX_LEN_ALIGN)) - return features & ~NETIF_F_TSO; + return features & ~(NETIF_F_TSO | NETIF_F_TSO6); } return features; } @@ -2664,7 +2664,7 @@ void phytmac_default_config(struct phytmac *pdata) ndev->hw_features = NETIF_F_SG; if (pdata->capacities & PHYTMAC_CAPS_LSO) - ndev->hw_features |= NETIF_F_TSO; + ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6; if (pdata->use_ncsi) { ndev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); From 202bb2bcf8fd4337bb7109889d25bde8267a7cde Mon Sep 17 00:00:00 2001 From: zuoqian Date: Fri, 24 Oct 2025 18:14:25 +0800 Subject: [PATCH 072/258] PHYTIUM: net: phytmac: resolve warnings when using jumbo frames Signed-off-by: zuoqian Link: https://gitee.com/phytium_embedded/phytium-linux-kernel/commit/b63c60849bffdf07de18d048f39b64323186d51e Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index ccdb2be4ab0bb2..1f4e9a2b97ee4f 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1131,7 +1131,7 @@ static struct sk_buff *phytmac_rx_mbuffer(struct phytmac_queue *queue) for (rx_tail = queue->rx_tail; ; rx_tail++) { desc = phytmac_get_rx_desc(queue, rx_tail); if (!hw_if->rx_complete(desc)) - return NULL; + return ERR_PTR(-EAGAIN); if (hw_if->rx_pkt_start(desc)) { if (first_frag != -1) @@ -1218,6 +1218,9 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, if (pdata->xdp_prog) netdev_warn(pdata->ndev, "xdp does not support multiple buffers!!\n"); skb = phytmac_rx_mbuffer(queue); + if (PTR_ERR(skb) == -EAGAIN) { + break; + } } if (!skb) { From c11845124f61f63cbc19bdbebd2f1f89d8cc7047 Mon Sep 17 00:00:00 2001 From: YunQiang Su Date: Mon, 16 Nov 2020 09:11:00 +0800 Subject: [PATCH 073/258] DEBIAN: Use RELAXED ieee754 mode for Loongson-3 as 3A 4000 is 2008-only There are 2 mode of value of IEEE NaN hardcoded by CPU. Currently, our mipsel/mips64el port is in so-called lagacy mode. Loongson 3A 4000 is set as the so-called 2008 mode. To make Debian workable on Loongson 3A 4000, we need set the kerenl in RELAXED mode. https://web.archive.org/web/20180830093617/https://dmz-portal.mips.com/wiki/MIPS_ABI_-_NaN_Interlinking [bwh: Update for addition of EMULATED mode in 6.11] Signed-off-by: YunQiang Su Link: https://salsa.debian.org/kernel-team/linux/-/blob/a71628c034f0c156c0868f53b14a9928c4d02f91/debian/patches/debian/mips-ieee754-relaxed.patch Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/fpu-probe.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/arch/mips/kernel/fpu-probe.c b/arch/mips/kernel/fpu-probe.c index 6bf3f19b1c335d..404d7531205824 100644 --- a/arch/mips/kernel/fpu-probe.c +++ b/arch/mips/kernel/fpu-probe.c @@ -144,7 +144,12 @@ static void cpu_set_fpu_2008(struct cpuinfo_mips *c) * IEEE 754 conformance mode to use. Affects the NaN encoding and the * ABS.fmt/NEG.fmt execution mode. */ -static enum { STRICT, EMULATED, LEGACY, STD2008, RELAXED } ieee754 = STRICT; +enum ieee754_mode { STRICT, EMULATED, LEGACY, STD2008, RELAXED }; +#ifdef CONFIG_CPU_LOONGSON64 +static enum ieee754_mode ieee754 = RELAXED; +#else +static enum ieee754_mode ieee754 = STRICT; +#endif /* * Set the IEEE 754 NaN encodings and the ABS.fmt/NEG.fmt execution modes From 972cf8f91ced2fd14c984c244b58db79aca5ac8c Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Wed, 16 Jun 2021 13:57:01 +0800 Subject: [PATCH 074/258] UBUNTU: ODM: hwmon: add driver for AAEON devices BugLink: https://bugs.launchpad.net/bugs/1929504 This refator patch adds support for the hwmon information which are transported to userspace through ASUS WMI interface. Signed-off-by: Kunyang_Fan Review-by: Kai-Heng Feng Review-by: Chia-Lin Kao (AceLan) Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Stefan Bader Acked-by: Kleber Sacilotto de Souza Signed-off-by: Andrea Righi Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/d927fbdbccb2b2d8f6891b6f51e1570e74e25e14 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/hwmon-aaeon.c | 568 ++++++++++++++++++++++++++++++++++++ 3 files changed, 581 insertions(+) create mode 100644 drivers/hwmon/hwmon-aaeon.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index b1f121b84bf685..4b816f08b01359 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -38,6 +38,18 @@ config HWMON_DEBUG_CHIP comment "Native drivers" +config SENSORS_AAEON + tristate "AAEON hwmon driver" + depends on X86 + depends on UBUNTU_ODM_DRIVERS + select MFD_AAEON + help + This hwmon driver adds support for reporting temperature or fan + speed and voltage on Single Board Computers produced by AAEON. + + This driver leverages the ASUS WMI interface to access device + resources. + config SENSORS_ABITUGURU tristate "Abit uGuru (rev 1 & 2)" depends on (X86 && DMI) || COMPILE_TEST && HAS_IOPORT diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 982ee2c6f9deb6..3a8bd0f9e64f29 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_HP_WMI) += hp-wmi-sensors.o # Native drivers # asb100, then w83781d go first, as they can override other drivers' addresses. +obj-$(CONFIG_SENSORS_AAEON) += hwmon-aaeon.o obj-$(CONFIG_SENSORS_ASB100) += asb100.o obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o obj-$(CONFIG_SENSORS_W83773G) += w83773g.o diff --git a/drivers/hwmon/hwmon-aaeon.c b/drivers/hwmon/hwmon-aaeon.c new file mode 100644 index 00000000000000..146da10309bbb9 --- /dev/null +++ b/drivers/hwmon/hwmon-aaeon.c @@ -0,0 +1,568 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AAEON HWMON driver + * Copyright (c) 2021, AAEON Ltd. + * + * Author: Edward Lin + * Author: Kunyang Fan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "hwmon-aaeon" + +#define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" + +#define AAEON_VERSION_METHOD_ID 0x00000000 +#define HWM_INFORMATION_METHOD_ID 0x00030000 +#define HWM_METHOD_ID 0x00030001 + +#define BITMAP_TEMP_ARG 0x12 +#define BITMAP_FAN_ARG 0x13 +#define BITMAP_VOLTAGE_ARG 0x14 + +#define SENSOR_TEMP_NUMBER 0 +#define SENSOR_FAN_NUMBER 1 +#define SENSOR_VOLTAGE_NUMBER 2 +#define SENSOR_MAX_NUMBER 2 + +static ssize_t aaeon_show_sensor(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t aaeon_show_sensor_name(struct device *dev, + struct device_attribute *devattr, + char *buf); +static ssize_t aaeon_show_version(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t name_show(struct device *dev, struct device_attribute *devattr, + char *buf); +static int aaeon_get_version(void); +static int aaeon_hwmon_probe(struct platform_device *pdev); +static int aaeon_hwmon_remove(struct platform_device *pdev); + +static const char * const temp_sensors_name_table[] = { + "CPU_Temp", + "SYS1_Temp", + "SYS2_Temp", +}; + +static const char * const temp_sensors_name_table_V3[] = { + "SYS_Temp", + "CPU_Temp", +}; + +static const char * const fan_sensors_name_table[] = { + "CPU_FAN", + "SYS1_FAN", + "SYS2_FAN", + "Chasis1_FAN", + "Chasis2_FAN", +}; + +static const char * const fan_sensors_name_table_V3[] = { + "Chasis_FAN", + "CPU_FAN", +}; + +static const char * const voltage_sensors_name_table[] = { + "VCORE_Voltage", + "VMEM_Voltage", + "+12_Voltage", + "+5_Voltage", + "+3.3_Voltage", + "+1.8_Voltage", + "5VSB_Voltage", + "3VSB_Voltage", + "VBAT_Voltage", +}; + +static const char * const voltage_sensors_name_table_V3[] = { + "VCORE_Voltage", + "+5_Voltage", + "AVCC_Voltage", + "+3.3_Voltage", + "+12_Voltage", + "VCOREREFIN_Voltage", + "VIN4_Voltage", + "3VSB_Voltage", + "VBAT_Voltage", +}; + +struct aaeon_hwmon_data { + struct device *hwmon_dev; + int bfpi_version; + u32 temp_bitmap; + u32 fan_bitmap; + u32 voltage_bitmap; + unsigned int sensors_number[SENSOR_MAX_NUMBER + 1]; + const char * const *temp_names; + const char * const *fan_names; + const char * const *voltage_names; +}; + +/* Temperature attributes */ +static struct sensor_device_attribute_2 temp_sys_nodes_atts[] = { + SENSOR_ATTR_2(temp1_input, 0444, aaeon_show_sensor, NULL, + SENSOR_TEMP_NUMBER, 0), + SENSOR_ATTR_2(temp1_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_TEMP_NUMBER, 0), + SENSOR_ATTR_2(temp2_input, 0444, aaeon_show_sensor, NULL, + SENSOR_TEMP_NUMBER, 1), + SENSOR_ATTR_2(temp2_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_TEMP_NUMBER, 1), + SENSOR_ATTR_2(temp3_input, 0444, aaeon_show_sensor, NULL, + SENSOR_TEMP_NUMBER, 2), + SENSOR_ATTR_2(temp3_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_TEMP_NUMBER, 2), +}; + +/* Cooler Fan attributes */ +static struct sensor_device_attribute_2 fan_sys_nodes_atts[] = { + SENSOR_ATTR_2(fan1_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 0), + SENSOR_ATTR_2(fan1_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 0), + SENSOR_ATTR_2(fan2_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 1), + SENSOR_ATTR_2(fan2_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 1), + SENSOR_ATTR_2(fan3_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 2), + SENSOR_ATTR_2(fan3_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 2), + SENSOR_ATTR_2(fan4_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 3), + SENSOR_ATTR_2(fan4_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 3), + SENSOR_ATTR_2(fan5_input, 0444, aaeon_show_sensor, NULL, + SENSOR_FAN_NUMBER, 4), + SENSOR_ATTR_2(fan5_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_FAN_NUMBER, 4), +}; + +/* Voltage attributes */ +static struct sensor_device_attribute_2 voltage_sys_nodes_atts[] = { + SENSOR_ATTR_2(in1_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 0), + SENSOR_ATTR_2(in1_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 0), + SENSOR_ATTR_2(in2_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 1), + SENSOR_ATTR_2(in2_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 1), + SENSOR_ATTR_2(in3_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 2), + SENSOR_ATTR_2(in3_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 2), + SENSOR_ATTR_2(in4_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 3), + SENSOR_ATTR_2(in4_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 3), + SENSOR_ATTR_2(in5_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 4), + SENSOR_ATTR_2(in5_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 4), + SENSOR_ATTR_2(in6_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 5), + SENSOR_ATTR_2(in6_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 5), + SENSOR_ATTR_2(in7_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 6), + SENSOR_ATTR_2(in7_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 6), + SENSOR_ATTR_2(in8_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 7), + SENSOR_ATTR_2(in8_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 7), + SENSOR_ATTR_2(in9_input, 0444, aaeon_show_sensor, NULL, + SENSOR_VOLTAGE_NUMBER, 8), + SENSOR_ATTR_2(in9_label, 0444, aaeon_show_sensor_name, NULL, + SENSOR_VOLTAGE_NUMBER, 8), + +}; + +static struct sensor_device_attribute_2 info_sys_nodes_atts[] = { + /* WMI version Information */ + SENSOR_ATTR_2(AAEON_VERSION, 0444, aaeon_show_version, NULL, 0, 0), +}; + +DEVICE_ATTR_RO(name); +static ssize_t name_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + return sprintf(buf, "%s\n", DRVNAME); +} + +static ssize_t aaeon_show_version(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct aaeon_hwmon_data *data = + (struct aaeon_hwmon_data *)dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", data->bfpi_version); +} + +static ssize_t aaeon_show_sensor_name(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u8 nr = to_sensor_dev_attr_2(devattr)->nr; + u8 index = to_sensor_dev_attr_2(devattr)->index; + struct aaeon_hwmon_data *data = + (struct aaeon_hwmon_data *)dev_get_drvdata(dev); + + if (nr > SENSOR_MAX_NUMBER || index >= data->sensors_number[nr]) { + pr_debug("Can not check the device"); + return -1; + } + + switch (nr) { + case SENSOR_TEMP_NUMBER: + return sprintf(buf, "%s\n", data->temp_names[index]); + case SENSOR_FAN_NUMBER: + return sprintf(buf, "%s\n", data->fan_names[index]); + case SENSOR_VOLTAGE_NUMBER: + return sprintf(buf, "%s\n", data->voltage_names[index]); + default: + break; + } + + return 0; +} + +static ssize_t aaeon_show_sensor(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u8 nr = to_sensor_dev_attr_2(devattr)->nr; + u8 index = to_sensor_dev_attr_2(devattr)->index; + u32 dev_id; + int retval, err; + struct aaeon_hwmon_data *data = + (struct aaeon_hwmon_data *)dev_get_drvdata(dev); + + if (nr > SENSOR_MAX_NUMBER || index >= data->sensors_number[nr]) { + pr_debug("Can not check the device"); + return -1; + } + + /* For the V3 version, index need offset */ + if (data->bfpi_version == 0x03 && nr != SENSOR_VOLTAGE_NUMBER) + index++; + + dev_id = (index << 12) | (nr << 8); + err = asus_wmi_evaluate_method(HWM_METHOD_ID, dev_id, 0, &retval); + if (err) + return err; + + /* For the V3 version, need to convert the raw value*/ + if (nr == SENSOR_VOLTAGE_NUMBER && data->bfpi_version == 0x03) { + switch (index) { + case 0: /* VCORE */ + retval = retval * 16; + break; + case 1: /* +5V */ + retval = (retval * 2008) / 50; + break; + case 2: /* AVCC */ + retval = retval * 16; + break; + case 3: /* +3.3V */ + retval = retval * 16; + break; + case 4: /* +12V */ + retval = retval * 96; + break; + case 5: /* VCOREREFIN */ + retval = (retval * 552) / 41; + break; + case 6: /* VIN4 */ + retval = retval * 8; + break; + case 7: /* 3VSB */ + retval = retval * 16; + break; + case 8: /* VBAT */ + retval = retval * 16; + break; + default: + break; + } + } else if (nr == SENSOR_TEMP_NUMBER && data->bfpi_version == 0x03) + retval = retval * 1000; + + return sprintf(buf, "%d\n", retval); +} + +static int aaeon_hwmon_create_sub_sysfs_fs(struct platform_device *pdev, + struct sensor_device_attribute_2 *attr, + int sensor_number, + u32 sensor_mask, + int bfpi_version) +{ + int i, err = 0; + + for (i = 0; i < sensor_number; i++) { + if (bfpi_version == 0x03 || sensor_mask & BIT(i)) { + err = device_create_file(&pdev->dev, &attr[2 * i].dev_attr); + if (err) + break; + err = device_create_file(&pdev->dev, &attr[2 * i + 1].dev_attr); + if (err) + break; + } + } + + return err; +} + +static int +aaeon_hwmon_create_sysfs_files(struct platform_device *pdev, struct aaeon_hwmon_data *data) +{ + int err; + + /* register sysfs interface files */ + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + return err; + + /* registe sysfs to dump sensors BFPI version */ + err = device_create_file(&pdev->dev, &info_sys_nodes_atts[0].dev_attr); + if (err) + return err; + + /* create temperature name and value node */ + err = aaeon_hwmon_create_sub_sysfs_fs(pdev, temp_sys_nodes_atts, + data->sensors_number[SENSOR_TEMP_NUMBER], + data->temp_bitmap, data->bfpi_version); + if (err) + return err; + + /* create fan name and value node */ + err = aaeon_hwmon_create_sub_sysfs_fs(pdev, fan_sys_nodes_atts, + data->sensors_number[SENSOR_FAN_NUMBER], + data->fan_bitmap, data->bfpi_version); + if (err) + return err; + + /* create voltage name and value node */ + err = aaeon_hwmon_create_sub_sysfs_fs(pdev, voltage_sys_nodes_atts, + data->sensors_number[SENSOR_VOLTAGE_NUMBER], + data->voltage_bitmap, data->bfpi_version); + if (err) + return err; + + return 0; +} + +static void aaeon_hwmon_remove_sub_sysfs_fs(struct platform_device *pdev, + struct sensor_device_attribute_2 *attr, + int sensor_number, + u32 sensor_mask, + int bfpi_version) +{ + int i; + + for (i = 0; i < sensor_number; i++) { + if (bfpi_version == 0x03 || sensor_mask & BIT(i)) { + device_remove_file(&pdev->dev, &attr[2 * i].dev_attr); + device_remove_file(&pdev->dev, &attr[2 * i + 1].dev_attr); + } + } +} + +static void +aaeon_hwmon_remove_sysfs_files(struct platform_device *pdev, + struct aaeon_hwmon_data *data) +{ + /* degister sysfs interface files */ + device_remove_file(&pdev->dev, &dev_attr_name); + + /* degiste sysfs to dump sensors BFPI version */ + device_remove_file(&pdev->dev, &info_sys_nodes_atts[0].dev_attr); + + /* remove temperature name and value node */ + aaeon_hwmon_remove_sub_sysfs_fs(pdev, temp_sys_nodes_atts, + data->sensors_number[SENSOR_TEMP_NUMBER], + data->temp_bitmap, + data->bfpi_version); + + /* remove fan name and value node */ + aaeon_hwmon_remove_sub_sysfs_fs(pdev, fan_sys_nodes_atts, + data->sensors_number[SENSOR_FAN_NUMBER], + data->fan_bitmap, + data->bfpi_version); + + /* remove voltage name and value node */ + aaeon_hwmon_remove_sub_sysfs_fs(pdev, voltage_sys_nodes_atts, + data->sensors_number[SENSOR_VOLTAGE_NUMBER], + data->voltage_bitmap, + data->bfpi_version); +} + +static int aaeon_hwmon_remove(struct platform_device *pdev) +{ + struct aaeon_hwmon_data *data = platform_get_drvdata(pdev); + + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); + + aaeon_hwmon_remove_sysfs_files(pdev, data); + + return 0; +} + +static int aaeon_get_version(void) +{ + int err, retval; + u32 dev_id = 0x00; + + err = asus_wmi_evaluate_method(AAEON_VERSION_METHOD_ID, dev_id, 0, + &retval); + if (err) + return err; + + return retval; +} + +static int aaeon_hwmon_init_drv_data(struct aaeon_hwmon_data *data) +{ + int err; + + data->bfpi_version = aaeon_get_version(); + if (data->bfpi_version < 0) { + pr_debug("Error BFPI verion\n"); + return -1; + } + + if (data->bfpi_version == 0x03) { + /* set the number of bits in temp bitmap */ + data->sensors_number[SENSOR_TEMP_NUMBER] = + ARRAY_SIZE(temp_sensors_name_table_V3); + data->temp_names = temp_sensors_name_table_V3; + + /* set the number of bits in fan bitmap */ + data->sensors_number[SENSOR_FAN_NUMBER] = + ARRAY_SIZE(fan_sensors_name_table_V3); + data->fan_names = fan_sensors_name_table_V3; + + /* set the number of bits in voltage bitmap */ + data->sensors_number[SENSOR_VOLTAGE_NUMBER] = + ARRAY_SIZE(voltage_sensors_name_table_V3); + data->voltage_names = voltage_sensors_name_table_V3; + } else { + /* set the number of bits in temp bitmap */ + data->sensors_number[SENSOR_TEMP_NUMBER] = + ARRAY_SIZE(temp_sensors_name_table); + data->temp_names = temp_sensors_name_table; + + /* set the number of bits in fan bitmap */ + data->sensors_number[SENSOR_FAN_NUMBER] = + ARRAY_SIZE(fan_sensors_name_table); + data->fan_names = fan_sensors_name_table; + + /* set the number of bits in voltage bitmap */ + data->sensors_number[SENSOR_VOLTAGE_NUMBER] = + ARRAY_SIZE(voltage_sensors_name_table); + data->voltage_names = voltage_sensors_name_table; + } + + /* get temp supported bitmap */ + err = asus_wmi_evaluate_method(HWM_INFORMATION_METHOD_ID, + BITMAP_TEMP_ARG, 0, &data->temp_bitmap); + if (err) + return err; + + /* get fan supported bitmap */ + err = asus_wmi_evaluate_method(HWM_INFORMATION_METHOD_ID, + BITMAP_FAN_ARG, 0, &data->fan_bitmap); + if (err) + return err; + + /* get voltage supported bitmap */ + err = asus_wmi_evaluate_method(HWM_INFORMATION_METHOD_ID, + BITMAP_VOLTAGE_ARG, 0, &data->voltage_bitmap); + if (err) + return err; + + return 0; +} + +static int aaeon_hwmon_probe(struct platform_device *pdev) +{ + int err; + struct aaeon_hwmon_data *data; + + pr_debug("aaeon hwomon device probe (support V3)!\n"); + if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) { + pr_info("AAEON Management GUID not found\n"); + return -ENODEV; + } + + data = devm_kzalloc(&pdev->dev, sizeof(struct aaeon_hwmon_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + err = aaeon_hwmon_init_drv_data(data); + if (err) { + pr_info("Error to get sensor support bitmap\n"); + goto exit; + } + + if (data->bfpi_version != 0x03 && data->temp_bitmap == 0 && + data->fan_bitmap == 0 && data->voltage_bitmap == 0) { + pr_debug("No sensors found\n"); + err = -ENODEV; + goto exit; + } + + platform_set_drvdata(pdev, data); + err = aaeon_hwmon_create_sysfs_files(pdev, data); + if (err) + goto exit; + + data->hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "AAEON_HWM", + data, + NULL, + NULL); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + data->hwmon_dev = NULL; + goto exit_unregister_sysfs; + } + + return 0; + +exit_unregister_sysfs: + aaeon_hwmon_remove(pdev); +exit: + return err; +} + +static struct platform_driver aaeon_hwmon_driver = { + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + }, + .probe = aaeon_hwmon_probe, + .remove = aaeon_hwmon_remove, +}; + +module_platform_driver_probe(aaeon_hwmon_driver, aaeon_hwmon_probe); + +MODULE_ALIAS("platform:hwmon-aaeon"); +MODULE_DESCRIPTION("AAEON Hardware Monitoring Driver"); +MODULE_AUTHOR("Edward Lin "); +MODULE_AUTHOR("Kunyang Fan "); +MODULE_LICENSE("GPL v2"); From ad0f141eee0fe57861a629999b0a69850e005da4 Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Wed, 16 Jun 2021 13:57:02 +0800 Subject: [PATCH 075/258] UBUNTU: ODM: leds: add driver for AAEON devices BugLink: https://bugs.launchpad.net/bugs/1929504 This patch adds support for the led devices which can be controlled from sysfs through ASUS WMI interface. Signed-off-by: Kunyang_Fan Review-by: Kai-Heng Feng Review-by: Chia-Lin Kao (AceLan) Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Stefan Bader Acked-by: Kleber Sacilotto de Souza Signed-off-by: Andrea Righi Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/dea44875e926deababd0efc7d3200319562e3337 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/leds/Kconfig | 12 ++++ drivers/leds/Makefile | 1 + drivers/leds/leds-aaeon.c | 142 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 drivers/leds/leds-aaeon.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index f4a0a3c8c8705e..22748404a7f061 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -72,6 +72,18 @@ config LEDS_88PM860X This option enables support for on-chip LED drivers found on Marvell Semiconductor 88PM8606 PMIC. +config LEDS_AAEON + tristate "AAEON LED driver" + depends on X86 + depends on UBUNTU_ODM_DRIVERS + select MFD_AAEON + help + This led driver adds support for LED brightness control on Single + Board Computers produced by AAEON. + + This driver leverages the ASUS WMI interface to access device + resources. + config LEDS_AN30259A tristate "LED support for Panasonic AN30259A" depends on LEDS_CLASS && I2C && OF diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 8fdb45d5b43930..2b9506f9a69487 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_KUNIT_TEST) += led-test.o # LED Platform Drivers (keep this sorted, M-| sort) obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o +obj-$(CONFIG_LEDS_AAEON) += leds-aaeon.o obj-$(CONFIG_LEDS_ACER_A500) += leds-acer-a500.o obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o diff --git a/drivers/leds/leds-aaeon.c b/drivers/leds/leds-aaeon.c new file mode 100644 index 00000000000000..10090a4bff6545 --- /dev/null +++ b/drivers/leds/leds-aaeon.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AAEON LED driver + * + * Copyright (c) 2021, AAEON Ltd. + * + * Author: Kunyang Fan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include + +#define DRVNAME "led_aaeon" +#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" +#define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" + +#define GET_LED_NUMBER_ID 0x00060000 +#define GET_LED_METHOD_ID 0x00060001 +#define SET_LED_METHOD_ID 0x00060002 +#define GET_LED_NUMBER_METHOD_ID 0x10 + + +struct aaeon_led_data { + int id; + struct led_classdev cdev; +}; + +static int aaeon_led_get_number(void) +{ + int err, retval; + + err = asus_wmi_evaluate_method(GET_LED_NUMBER_ID, + GET_LED_NUMBER_METHOD_ID, + 0, &retval); + if (err) + return err; + + return retval; +} + +static enum led_brightness aaeon_led_brightness_get(struct led_classdev + *cdev) +{ + int err, brightness; + struct aaeon_led_data *led = + container_of(cdev, struct aaeon_led_data, cdev); + u32 arg0; + + arg0 = (u32)(led->id & 0xF); + err = asus_wmi_evaluate_method(GET_LED_METHOD_ID, arg0, 0, &brightness); + if (err) + return err; + + return brightness; +}; + +static void aaeon_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + int err, retval; + struct aaeon_led_data *led = + container_of(cdev, struct aaeon_led_data, cdev); + u32 arg0; + + arg0 = (u32)(led->id & 0xF); + if (brightness != LED_OFF) + arg0 |= BIT(16); + + err = asus_wmi_evaluate_method(SET_LED_METHOD_ID, arg0, 0, &retval); +}; + +static int __init aaeon_add_led_device(struct platform_device *pdev, + int id) +{ + struct aaeon_led_data *led; + + led = devm_kzalloc(&pdev->dev, sizeof(struct aaeon_led_data), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->id = id; + led->cdev.brightness_get = aaeon_led_brightness_get; + led->cdev.brightness_set = aaeon_led_brightness_set; + led->cdev.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "led:%d:", id); + + if (!led->cdev.name) + return -ENOMEM; + + return devm_led_classdev_register(&pdev->dev, &led->cdev); +} + +static int aaeon_led_probe(struct platform_device *pdev) +{ + int err = -ENODEV, i; + int led_number = 0; + + pr_debug("aaeon led device probe!\n"); + /* Prevent other drivers adding this platfom device */ + if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) { + pr_debug("AAEON Management GUID not found\n"); + return -ENODEV; + } + + /* Query the number of led devices board support */ + led_number = aaeon_led_get_number(); + + /* + * If the number is 0 or can't get the number of leds, + * no need to register any led device node. + */ + if (led_number <= 0) + return -ENODEV; + + for (i = 0; i < led_number; i++) { + err = aaeon_add_led_device(pdev, i); + if (err) + break; + } + + return err; +} + +static struct platform_driver aaeon_led_driver = { + .driver = { + .name = "leds-aaeon", + }, +}; + +module_platform_driver_probe(aaeon_led_driver, aaeon_led_probe); + +MODULE_ALIAS("platform:leds-aaeon"); +MODULE_DESCRIPTION("AAEON LED Driver"); +MODULE_AUTHOR("Kunyang Fan "); +MODULE_LICENSE("GPL v2"); From f9e21900c81576fde80f0e5e71306bd963b6da76 Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Wed, 16 Jun 2021 13:56:59 +0800 Subject: [PATCH 076/258] UBUNTU: ODM: gpio: add driver for AAEON devices BugLink: https://bugs.launchpad.net/bugs/1929504 This patch add support for the GPIO pins whose control are transported to BIOS through ASUS WMI interface. Signed-off-by: Kunyang_Fan Review-by: Kai-Heng Feng Review-by: Chia-Lin Kao (AceLan) Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Stefan Bader Acked-by: Kleber Sacilotto de Souza Signed-off-by: Andrea Righi Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/37faff18ec5a41729832e8229cda02d7c0e47f72 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpio/Kconfig | 12 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-aaeon.c | 205 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 drivers/gpio/gpio-aaeon.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 020e51e30317a8..254bd37f75acf8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1786,6 +1786,18 @@ endmenu menu "PCI GPIO expanders" depends on PCI +config GPIO_AAEON + tristate "AAEON GPIO support" + depends on ASUS_WMI + depends on UBUNTU_ODM_DRIVERS + select MFD_AAEON + help + Say yes here to support GPIO pins on Single Board Computers produced + by AAEON. + + This driver leverages the ASUS WMI interface to access device + resources. + config GPIO_AMD8111 tristate "AMD 8111 GPIO driver" depends on X86 || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b267598b517de0..d2ee444bebe31b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o +obj-$(CONFIG_GPIO_AAEON) += gpio-aaeon.o obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o diff --git a/drivers/gpio/gpio-aaeon.c b/drivers/gpio/gpio-aaeon.c new file mode 100644 index 00000000000000..3183c45dd19461 --- /dev/null +++ b/drivers/gpio/gpio-aaeon.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AAEON GPIO driver + * Copyright (c) 2021, AAEON Ltd. + * + * Author: Edward Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "gpio_aaeon" +#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C" +#define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" + +#define GET_GPIO_NUMBER_ID 0x00010000 +#define GET_LEVEL_METHOD_ID 0x00010001 +#define SET_LEVEL_METHOD_ID 0x00010002 +#define GET_DIRECTION_METHOD_ID 0x00010003 +#define SET_DIRECTION_METHOD_ID 0x00010004 +#define GET_SIO_NUMBER_METHOD_ID 0xF0010 + +struct aaeon_gpio_bank { + struct gpio_chip chip; + unsigned int regbase; + struct aaeon_gpio_data *data; +}; + +struct aaeon_gpio_data { + int nr_bank; + struct aaeon_gpio_bank *bank; +}; + +static int aaeon_gpio_get_number(void); +static int aaeon_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset); +static int aaeon_gpio_output_set_direction(struct gpio_chip *chip, + unsigned int offset, int value); +static int aaeon_gpio_input_set_direction(struct gpio_chip *chip, + unsigned int offset); +static int aaeon_gpio_get(struct gpio_chip *chip, + unsigned int offset); +static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value); + +#define AAEON_GPIO_BANK(_base, _ngpio, _regbase) \ +{ \ + .chip = { \ + .label = DRVNAME, \ + .owner = THIS_MODULE, \ + .get_direction = aaeon_gpio_get_direction, \ + .direction_input = aaeon_gpio_input_set_direction, \ + .direction_output = aaeon_gpio_output_set_direction, \ + .get = aaeon_gpio_get, \ + .set = aaeon_gpio_set, \ + .base = _base, \ + .ngpio = _ngpio, \ + .can_sleep = true, \ + }, \ + .regbase = _regbase, \ +} + +static struct aaeon_gpio_bank aaeon_gpio_bank[] = { + AAEON_GPIO_BANK(0, 0, 0xF0), +}; + +static int aaeon_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + int err, retval; + u32 dev_id = 0x0; + + dev_id |= offset; + err = asus_wmi_evaluate_method(GET_DIRECTION_METHOD_ID, dev_id, + 0, &retval); + if (err) + return err; + + return retval; +} + +static int aaeon_gpio_input_set_direction(struct gpio_chip *chip, + unsigned int offset) +{ + int err, retval; + u32 dev_id; + + dev_id = BIT(16) | offset; + err = asus_wmi_evaluate_method(SET_DIRECTION_METHOD_ID, dev_id, + 0, &retval); + if (err) + return err; + + return retval; +} + +static int aaeon_gpio_output_set_direction(struct gpio_chip *chip, + unsigned int offset, int value) +{ + int err, retval; + u32 dev_id = 0x0; + + dev_id |= offset; + err = asus_wmi_evaluate_method(SET_DIRECTION_METHOD_ID, dev_id, + 0, &retval); + if (err) + return err; + + return retval; +} + +static int aaeon_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + int err, retval; + u32 dev_id = 0x0; + + dev_id |= offset; + err = asus_wmi_evaluate_method(GET_LEVEL_METHOD_ID, dev_id, 0, &retval); + if (err) + return err; + + return retval; +} + +static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + int retval; + u32 dev_id = offset; + + if (value) + dev_id = BIT(16) | dev_id; + + asus_wmi_evaluate_method(SET_LEVEL_METHOD_ID, dev_id, 0, &retval); +} + +static int aaeon_gpio_get_number(void) +{ + int err, retval; + + err = asus_wmi_evaluate_method(GET_GPIO_NUMBER_ID, + GET_SIO_NUMBER_METHOD_ID, + 0, &retval); + if (err) + return err; + + return retval; +} + +static int __init aaeon_gpio_probe(struct platform_device *pdev) +{ + int err, i; + int dio_number = 0; + struct aaeon_gpio_data *data; + struct aaeon_gpio_bank *bank; + + /* Prevent other drivers adding this platfom device */ + if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) { + pr_debug("AAEON Management GUID not found\n"); + return -ENODEV; + } + + dio_number = aaeon_gpio_get_number(); + if (dio_number < 0) + return -ENODEV; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->nr_bank = ARRAY_SIZE(aaeon_gpio_bank); + data->bank = aaeon_gpio_bank; + platform_set_drvdata(pdev, data); + bank = &data->bank[0]; + bank->chip.parent = &pdev->dev; + bank->chip.ngpio = dio_number; + bank->data = data; + err = devm_gpiochip_add_data(&pdev->dev, &bank->chip, bank); + if (err) + pr_debug("Failed to register gpiochip %d: %d\n", i, err); + + return err; +} + +static struct platform_driver aaeon_gpio_driver = { + .driver = { + .name = "gpio-aaeon", + }, +}; + +module_platform_driver_probe(aaeon_gpio_driver, aaeon_gpio_probe); + +MODULE_ALIAS("platform:gpio-aaeon"); +MODULE_DESCRIPTION("AAEON GPIO Driver"); +MODULE_AUTHOR("Edward Lin "); +MODULE_LICENSE("GPL v2"); From 057fe82785adc8a8dcb069e0abc9c8f69b419e42 Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Wed, 16 Jun 2021 13:56:58 +0800 Subject: [PATCH 077/258] UBUNTU: ODM: mfd: Add support for IO functions of AAEON devices BugLink: https://bugs.launchpad.net/bugs/1929504 This adds the supports for multiple IO functions of the AAEON x86 devices and makes use of the WMI interface to control the these IO devices including: - GPIO - LED - Watchdog - HWMON It also adds the mfd child device drivers to support the above IO functions. Signed-off-by: Kunyang_Fan Review-by: Kai-Heng Feng Review-by: Chia-Lin Kao (AceLan) Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Stefan Bader Acked-by: Kleber Sacilotto de Souza Signed-off-by: Andrea Righi Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/9c320f62f5890f8e78485453db7c0eec68884d34 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- MAINTAINERS | 12 +++++++ drivers/mfd/Kconfig | 12 +++++++ drivers/mfd/Makefile | 1 + drivers/mfd/mfd-aaeon.c | 77 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+) create mode 100644 drivers/mfd/mfd-aaeon.c diff --git a/MAINTAINERS b/MAINTAINERS index 74848d12afb7dc..3a1f16a36f055b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -200,6 +200,18 @@ M: Linus Walleij F: Documentation/devicetree/bindings/power/supply/*ab8500* F: drivers/power/supply/*ab8500* +AAEON DEVICE DRIVER WITH WMI INTERFACE +M: Edward Lin +M: Kunyang Fan +M: Frank Hsieh +M: Jacob Wu +S: Supported +F: drivers/gpio/gpio-aaeon.c +F: drivers/hwmon/hwmon-aaeon.c +F: drivers/leds/leds-aaeon.c +F: drivers/mfd/mfd-aaeon.c +F: drivers/watchdog/wdt_aaeon.c + ABI/API L: linux-api@vger.kernel.org F: include/linux/syscalls.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7192c9d1d268e9..1463c0b0a55d3b 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2402,6 +2402,18 @@ config MFD_QCOM_PM8008 under it in the device tree. Additional drivers must be enabled in order to use the functionality of the device. +config MFD_AAEON + tristate "AAEON WMI MFD devices" + depends on ASUS_WMI + depends on UBUNTU_ODM_DRIVERS + help + Say yes here to support mltiple IO devices on Single Board Computers + produced by AAEON. + + This driver leverages the ASUS WMI interface to access device + resources. + + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e75e8045c28afa..438827883d8061 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -295,6 +295,7 @@ obj-$(CONFIG_MFD_LS2K_BMC_CORE) += ls2k-bmc-core.o obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o +obj-$(CONFIG_MFD_AAEON) += mfd-aaeon.o obj-$(CONFIG_MFD_QNAP_MCU) += qnap-mcu.o diff --git a/drivers/mfd/mfd-aaeon.c b/drivers/mfd/mfd-aaeon.c new file mode 100644 index 00000000000000..9d2efde53cad38 --- /dev/null +++ b/drivers/mfd/mfd-aaeon.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * UP Board main platform driver and FPGA configuration support + * + * Copyright (c) 2021, AAEON Ltd. + * + * Author: Kunyang_Fan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" + +struct aaeon_wmi_priv { + const struct mfd_cell *cells; + size_t ncells; +}; + +static const struct mfd_cell aaeon_mfd_cells[] = { + { .name = "gpio-aaeon" }, + { .name = "hwmon-aaeon"}, + { .name = "leds-aaeon"}, + { .name = "wdt-aaeon"}, +}; + +static const struct aaeon_wmi_priv aaeon_wmi_priv_data = { + .cells = aaeon_mfd_cells, + .ncells = ARRAY_SIZE(aaeon_mfd_cells), +}; + +static int aaeon_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct aaeon_wmi_priv *priv; + + if (!wmi_has_guid(AAEON_WMI_MGMT_GUID)) { + dev_info(&wdev->dev, "AAEON Management GUID not found\n"); + return -ENODEV; + } + + + priv = (struct aaeon_wmi_priv *)context; + dev_set_drvdata(&wdev->dev, priv); + + return devm_mfd_add_devices(&wdev->dev, 0, priv->cells, + priv->ncells, NULL, 0, NULL); +} + +static const struct wmi_device_id aaeon_wmi_id_table[] = { + { AAEON_WMI_MGMT_GUID, (void *)&aaeon_wmi_priv_data }, + {} +}; + +static struct wmi_driver aaeon_wmi_driver = { + .driver = { + .name = "mfd-aaeon", + }, + .id_table = aaeon_wmi_id_table, + .probe = aaeon_wmi_probe, +}; + +module_wmi_driver(aaeon_wmi_driver); + +MODULE_DEVICE_TABLE(wmi, aaeon_wmi_id_table); +MODULE_AUTHOR("Kunyang Fan "); +MODULE_DESCRIPTION("AAEON Board WMI driver"); +MODULE_LICENSE("GPL v2"); From ad717005c98a24108ac30bf75963627052103a70 Mon Sep 17 00:00:00 2001 From: Kunyang_Fan Date: Tue, 24 Aug 2021 15:26:59 +0800 Subject: [PATCH 078/258] UBUNTU: ODM: mfd: Check AAEON BFPI version before adding device BugLink: https://bugs.launchpad.net/bugs/1937897 For the below: error log occurring in some devices: gpio gpiochip0: (gpio_aaeon): tried to insert a GPIO chip with zero lines gpiochip_add_data_with_key: GPIOs 0..-1 (gpio_aaeon) failed to register Add the BFPI version checking mechanism to prevent error log bumping. Fixes: 424945128781 ("UBUNTU: ODM: mfd: Add support for IO functions of AAEON devices") Signed-off-by: Kunyang_Fan Acked-by: Kai-Heng Feng Signed-off-by: Chia-Lin Kao (AceLan) Signed-off-by: Paolo Pisati Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/3d4fbe8d0b4b0f023fbfc2b45e6005b470a5db37 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/mfd/mfd-aaeon.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/mfd/mfd-aaeon.c b/drivers/mfd/mfd-aaeon.c index 9d2efde53cad38..74211d01ce6566 100644 --- a/drivers/mfd/mfd-aaeon.c +++ b/drivers/mfd/mfd-aaeon.c @@ -16,12 +16,17 @@ #include #include #include +#include #include #include #include #define AAEON_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66" +#define WMI_REPORT_CAPABILITY_METHOD 0x00000000 +#define MAX_BFPI_VERSION 255 +#define GET_REVISION_ID 0x00 + struct aaeon_wmi_priv { const struct mfd_cell *cells; size_t ncells; @@ -39,6 +44,21 @@ static const struct aaeon_wmi_priv aaeon_wmi_priv_data = { .ncells = ARRAY_SIZE(aaeon_mfd_cells), }; +static int aaeon_wmi_check_device(void) +{ + int err; + int retval; + + err = asus_wmi_evaluate_method(WMI_REPORT_CAPABILITY_METHOD, GET_REVISION_ID, 0, + &retval); + if (err) + return -ENODEV; + if (retval < 3 || retval > MAX_BFPI_VERSION) + return -ENODEV; + + return 0; +} + static int aaeon_wmi_probe(struct wmi_device *wdev, const void *context) { struct aaeon_wmi_priv *priv; @@ -48,6 +68,8 @@ static int aaeon_wmi_probe(struct wmi_device *wdev, const void *context) return -ENODEV; } + if (aaeon_wmi_check_device()) + return -ENODEV; priv = (struct aaeon_wmi_priv *)context; dev_set_drvdata(&wdev->dev, priv); From 29917d0d9a7d6a77394a84a12245cdaa7eb7323e Mon Sep 17 00:00:00 2001 From: Timo Aaltonen Date: Mon, 5 Aug 2024 14:48:08 +0300 Subject: [PATCH 079/258] UBUNTU: SAUCE: hwmon: Fix aaeon driver for 6.11. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the build after commit 0edb555a65d1ef047a9805051c36922b52a38a9d Author: Uwe Kleine-König Date: Mon Oct 9 12:37:26 2023 +0200 platform: Make platform_driver::remove() return void Signed-off-by: Timo Aaltonen Link: https://kernel.ubuntu.com/forgejo/ubuntu-kernel-next/linux/commit/6dd48b760b813aae56a9b0d55d91b2e52e3ac73e Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/hwmon/hwmon-aaeon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/hwmon-aaeon.c b/drivers/hwmon/hwmon-aaeon.c index 146da10309bbb9..80bf4d1224f3fa 100644 --- a/drivers/hwmon/hwmon-aaeon.c +++ b/drivers/hwmon/hwmon-aaeon.c @@ -49,7 +49,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *devattr, char *buf); static int aaeon_get_version(void); static int aaeon_hwmon_probe(struct platform_device *pdev); -static int aaeon_hwmon_remove(struct platform_device *pdev); +static void aaeon_hwmon_remove(struct platform_device *pdev); static const char * const temp_sensors_name_table[] = { "CPU_Temp", @@ -409,7 +409,7 @@ aaeon_hwmon_remove_sysfs_files(struct platform_device *pdev, data->bfpi_version); } -static int aaeon_hwmon_remove(struct platform_device *pdev) +static void aaeon_hwmon_remove(struct platform_device *pdev) { struct aaeon_hwmon_data *data = platform_get_drvdata(pdev); @@ -418,7 +418,7 @@ static int aaeon_hwmon_remove(struct platform_device *pdev) aaeon_hwmon_remove_sysfs_files(pdev, data); - return 0; + return; } static int aaeon_get_version(void) From f3f8774d57c85b66463e6e0ad81dad052c316e79 Mon Sep 17 00:00:00 2001 From: Timo Aaltonen Date: Wed, 20 Aug 2025 13:47:36 +0300 Subject: [PATCH 080/258] UBUNTU: SAUCE: gpio: aaeon: use new GPIO line value setter callbacks Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/gpio/gpio-aaeon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-aaeon.c b/drivers/gpio/gpio-aaeon.c index 3183c45dd19461..fb3adf8f226af8 100644 --- a/drivers/gpio/gpio-aaeon.c +++ b/drivers/gpio/gpio-aaeon.c @@ -50,7 +50,7 @@ static int aaeon_gpio_input_set_direction(struct gpio_chip *chip, unsigned int offset); static int aaeon_gpio_get(struct gpio_chip *chip, unsigned int offset); -static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, +static int aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, int value); #define AAEON_GPIO_BANK(_base, _ngpio, _regbase) \ @@ -131,7 +131,7 @@ static int aaeon_gpio_get(struct gpio_chip *chip, unsigned int offset) return retval; } -static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, +static int aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { int retval; @@ -140,7 +140,7 @@ static void aaeon_gpio_set(struct gpio_chip *chip, unsigned int offset, if (value) dev_id = BIT(16) | dev_id; - asus_wmi_evaluate_method(SET_LEVEL_METHOD_ID, dev_id, 0, &retval); + return asus_wmi_evaluate_method(SET_LEVEL_METHOD_ID, dev_id, 0, &retval); } static int aaeon_gpio_get_number(void) From 3b120499ab2a39a50fa6ad9a178f907ec5ce9c20 Mon Sep 17 00:00:00 2001 From: Timo Aaltonen Date: Wed, 7 Jan 2026 12:54:41 +0200 Subject: [PATCH 081/258] UBUNTU: SAUCE: aaeon: The modules need to import ASUS_WMI now Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/gpio/gpio-aaeon.c | 1 + drivers/hwmon/hwmon-aaeon.c | 1 + drivers/leds/leds-aaeon.c | 1 + drivers/mfd/mfd-aaeon.c | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/gpio/gpio-aaeon.c b/drivers/gpio/gpio-aaeon.c index fb3adf8f226af8..e57f41eed3ec0a 100644 --- a/drivers/gpio/gpio-aaeon.c +++ b/drivers/gpio/gpio-aaeon.c @@ -203,3 +203,4 @@ MODULE_ALIAS("platform:gpio-aaeon"); MODULE_DESCRIPTION("AAEON GPIO Driver"); MODULE_AUTHOR("Edward Lin "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("ASUS_WMI"); diff --git a/drivers/hwmon/hwmon-aaeon.c b/drivers/hwmon/hwmon-aaeon.c index 80bf4d1224f3fa..ea6df3c61a05f1 100644 --- a/drivers/hwmon/hwmon-aaeon.c +++ b/drivers/hwmon/hwmon-aaeon.c @@ -566,3 +566,4 @@ MODULE_DESCRIPTION("AAEON Hardware Monitoring Driver"); MODULE_AUTHOR("Edward Lin "); MODULE_AUTHOR("Kunyang Fan "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("ASUS_WMI"); diff --git a/drivers/leds/leds-aaeon.c b/drivers/leds/leds-aaeon.c index 10090a4bff6545..4034dfaba9eab0 100644 --- a/drivers/leds/leds-aaeon.c +++ b/drivers/leds/leds-aaeon.c @@ -140,3 +140,4 @@ MODULE_ALIAS("platform:leds-aaeon"); MODULE_DESCRIPTION("AAEON LED Driver"); MODULE_AUTHOR("Kunyang Fan "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("ASUS_WMI"); diff --git a/drivers/mfd/mfd-aaeon.c b/drivers/mfd/mfd-aaeon.c index 74211d01ce6566..de702c45a3b6dc 100644 --- a/drivers/mfd/mfd-aaeon.c +++ b/drivers/mfd/mfd-aaeon.c @@ -97,3 +97,4 @@ MODULE_DEVICE_TABLE(wmi, aaeon_wmi_id_table); MODULE_AUTHOR("Kunyang Fan "); MODULE_DESCRIPTION("AAEON Board WMI driver"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("ASUS_WMI"); From 42187ac3dc9bbd8cc1ad0cb88a5619d75b80d7fb Mon Sep 17 00:00:00 2001 From: zouxiaoh Date: Wed, 23 Feb 2022 10:21:25 +0800 Subject: [PATCH 082/258] BACKPORT: UBUNTU: SAUCE: iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs BugLink: https://bugs.launchpad.net/bugs/1958004 Intel IPU(Image Processing Unit) has its own (IO)MMU hardware, The IPU driver allocates its own page table that is not mapped via the DMA, and thus the Intel IOMMU driver blocks access giving this error: DMAR: DRHD: handling fault status reg 3 DMAR: [DMA Read] Request device [00:05.0] PASID ffffffff fault addr 76406000 [fault reason 06] PTE Read access is not set As IPU is not an external facing device which is not risky, so use IOMMU passthrough mode for Intel IPUs. Change-Id: I6dcccdadac308cf42e20a18e1b593381391e3e6b Depends-On: Iacd67578e8c6a9b9ac73285f52b4081b72fb68a6 Tracked-On: #JIITL8-411 Signed-off-by: Bingbu Cao Signed-off-by: zouxiaoh Signed-off-by: Xu Chongyang (cherry picked from https://github.com/intel/ipu6-drivers/blob/5d5526d2b2811aa52590c2fa513ba989e7e594ab/patch/IOMMU-passthrough-for-intel-ipu.diff) Signed-off-by: You-Sheng Yang Signed-off-by: Paolo Pisati Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai [ Mingcong Bai: Fixed a minor post-7.0.9 merge conflict in drivers/iommu/intel/iommu.c ] Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 849d06dfe1aecf..d4e1eba21a7f00 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -37,6 +37,12 @@ #define IS_GFX_DEVICE(pdev) pci_is_display(pdev) #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB) #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) +#define IS_INTEL_IPU(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \ + ((pdev)->device == 0x9a19 || \ + (pdev)->device == 0x9a39 || \ + (pdev)->device == 0x4e19 || \ + (pdev)->device == 0x465d || \ + (pdev)->device == 0x1919)) #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e) #define IOAPIC_RANGE_START (0xfee00000) @@ -205,12 +211,14 @@ int intel_iommu_sm = IS_ENABLED(CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON); int intel_iommu_enabled = 0; EXPORT_SYMBOL_GPL(intel_iommu_enabled); +static int dmar_map_ipu = 1; static int intel_iommu_superpage = 1; static int iommu_identity_mapping; static int iommu_skip_te_disable; static int disable_igfx_iommu; #define IDENTMAP_AZALIA 4 +#define IDENTMAP_IPU 8 const struct iommu_ops intel_iommu_ops; @@ -1401,6 +1409,9 @@ static int device_def_domain_type(struct device *dev) if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev)) return IOMMU_DOMAIN_IDENTITY; + + if ((iommu_identity_mapping & IDENTMAP_IPU) && IS_INTEL_IPU(pdev)) + return IOMMU_DOMAIN_IDENTITY; } return 0; @@ -1691,6 +1702,9 @@ static int __init init_dmars(void) iommu_set_root_entry(iommu); } + if (!dmar_map_ipu) + iommu_identity_mapping |= IDENTMAP_IPU; + check_tylersburg_isoch(); /* @@ -3983,6 +3997,21 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx); +static void quirk_iommu_ipu(struct pci_dev *dev) +{ + if (!IS_INTEL_IPU(dev)) + return; + + if (risky_device(dev)) + return; + + pci_info(dev, "Passthrough IOMMU for integrated Intel IPU\n"); + dmar_map_ipu = 0; +} + +/* disable IPU dmar support */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_iommu_ipu); + static void quirk_iommu_rwbf(struct pci_dev *dev) { if (risky_device(dev)) From 6c2cd8157ef2aaf1b7c899247d4029b355285e67 Mon Sep 17 00:00:00 2001 From: zouxiaoh Date: Wed, 23 Feb 2022 10:21:25 +0800 Subject: [PATCH 083/258] UBUNTU: SAUCE: iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs on Raptor Lake BugLink: https://bugs.launchpad.net/bugs/1989041 Intel IPU(Image Processing Unit) has its own (IO)MMU hardware, The IPU driver allocates its own page table that is not mapped via the DMA, and thus the Intel IOMMU driver blocks access giving this error: DMAR: DRHD: handling fault status reg 3 DMAR: [DMA Read] Request device [00:05.0] PASID ffffffff fault addr 76406000 [fault reason 06] PTE Read access is not set As IPU is not an external facing device which is not risky, so use IOMMU passthrough mode for Intel IPUs. (backported from https://github.com/intel/ipu6-drivers/blob/89c6b99e4bec00a04b53f37cd8c3c01ce824a4ab/patch/IOMMU-passthrough-for-intel-ipu.diff) Signed-off-by: You-Sheng Yang Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index d4e1eba21a7f00..e9fe20b84e0879 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -38,7 +38,8 @@ #define IS_USB_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_SERIAL_USB) #define IS_ISA_DEVICE(pdev) ((pdev->class >> 8) == PCI_CLASS_BRIDGE_ISA) #define IS_INTEL_IPU(pdev) ((pdev)->vendor == PCI_VENDOR_ID_INTEL && \ - ((pdev)->device == 0x9a19 || \ + ((pdev)->device == 0xa75d || \ + (pdev)->device == 0x9a19 || \ (pdev)->device == 0x9a39 || \ (pdev)->device == 0x4e19 || \ (pdev)->device == 0x465d || \ From 3b067393550ff95260194e3c1e6c386a4a3d7e0e Mon Sep 17 00:00:00 2001 From: You-Sheng Yang Date: Thu, 26 Oct 2023 00:06:41 +0800 Subject: [PATCH 084/258] UBUNTU: SAUCE: iommu: intel-ipu: use IOMMU passthrough mode for Intel IPUs on Meteor Lake BugLink: https://bugs.launchpad.net/bugs/2031412 Signed-off-by: You-Sheng Yang Acked-by: Stefan Bader Acked-by: Roxana Nicolescu Signed-off-by: Roxana Nicolescu Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index e9fe20b84e0879..3547c72489cbdc 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -41,6 +41,7 @@ ((pdev)->device == 0xa75d || \ (pdev)->device == 0x9a19 || \ (pdev)->device == 0x9a39 || \ + (pdev)->device == 0x7d19 || \ (pdev)->device == 0x4e19 || \ (pdev)->device == 0x465d || \ (pdev)->device == 0x1919)) From a137a2b961ad60c7abfb168df493bc85f440008a Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 12 Aug 2024 14:28:31 +0800 Subject: [PATCH 085/258] BACKPORT: UBUNTU: SAUCE: iommu/intel: disable DMAR for SKL integrated gfx BugLink: https://bugs.launchpad.net/bugs/2062951 This patch addresses a screen flickering issues on systems with Skylake integrated graphics, identified under Ubuntu kernel version 6.8.0-x. Initially thought to be a regression from kernel 6.6, it has been determined that the flickering was caused by changes to the Ubuntu kernel configuration, specifically the enabling of CONFIG_INTEL_IOMMU_DEFAULT_ON and CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON. 77e530c1a864c ("UBUNTU: [Config] enable Intel DMA remapping by default") The problem persists in the latest drm tip, and there is an upstream bug addressing the issue. https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/11504 Before there is a real fix for the issue, we may have to add the affected GPU IDs to the quirk to disable its DMAR. Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Kuan-Ying Lee Acked-by: Aaron Jauregui Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0.9 merge conflict in drivers/iommu/intel/iommu.c ] Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 3547c72489cbdc..82b30802cb9a2e 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -3999,6 +3999,33 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx); +/* SKL */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1906, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1913, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x190E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1915, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1902, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x190A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x190B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1917, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1916, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1921, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x191E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1912, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x191A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x191B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x191D, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1923, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1926, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1927, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x192A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x192B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x192D, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1932, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193D, quirk_iommu_igfx); + static void quirk_iommu_ipu(struct pci_dev *dev) { if (!IS_INTEL_IPU(dev)) From 01c4871e484ecaa0a46ade5932eb45f50a3de94a Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 18 Nov 2024 09:28:40 +0800 Subject: [PATCH 086/258] BACKPORT: UBUNTU: SAUCE: iommu/intel: disable DMAR for KBL and CML integrated gfx BugLink: https://bugs.launchpad.net/bugs/2086587 This patch addresses a screen flickering issues on systems with KBL and CML integrated graphics, identified under Ubuntu kernel version 6.8.0-x. Initially thought to be a regression from kernel 6.6, it has been determined that the flickering was caused by changes to the Ubuntu kernel configuration, specifically the enabling of CONFIG_INTEL_IOMMU_DEFAULT_ON and CONFIG_INTEL_IOMMU_SCALABLE_MODE_DEFAULT_ON. 77e530c1a864c ("UBUNTU: [Config] enable Intel DMA remapping by default") The problem persists in the latest drm tip, and there is an upstream bug addressing the issue. https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/11504 Before there is a real fix for the issue, we may have to add the affected GPU IDs to the quirk to disable its DMAR. Signed-off-by: Chia-Lin Kao (AceLan) Acked-by: Magali Lemes Signed-off-by: Paolo Pisati Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0.9 merge conflict in drivers/iommu/intel/iommu.c ] Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 82b30802cb9a2e..3f74fe3a370474 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -4026,6 +4026,47 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193A, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193B, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x193D, quirk_iommu_igfx); +/* KBL */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5902, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5906, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5908, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x590A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x590B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x590E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5912, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5913, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5915, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5916, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5917, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x591A, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x591B, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x591D, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x591E, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5921, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5923, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5926, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x5927, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x593B, quirk_iommu_igfx); + +/* CML */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9B21, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BA2, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BA4, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BA5, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BA8, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BAA, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BAC, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC2, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC4, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC5, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC6, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BC8, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BE6, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BF6, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9B41, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BCA, quirk_iommu_igfx); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BCC, quirk_iommu_igfx); + static void quirk_iommu_ipu(struct pci_dev *dev) { if (!IS_INTEL_IPU(dev)) From cac9a1e4fb8be904ba527f2cd7ce9fc415930b0a Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Wed, 31 Aug 2011 10:25:24 -0700 Subject: [PATCH 087/258] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Latitude E6520 use reboot=pci The Dell Latitude E6520 doesn't reboot unless reboot=pci is set. BugLink: http://bugs.launchpad.net/bugs/833705 Cc: Signed-off-by: Leann Ogasawara Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 0fed6d0d7e322c..de9f35ade26e57 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -490,7 +490,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "VGN-Z540N"), }, }, - + { /* Handle problems with rebooting on the Latitude E6520. */ + .callback = set_pci_reboot, + .ident = "Dell Latitude E6520", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6520"), + }, + }, { } }; From 53e1c517225f3c19d21407c1b26e9c8cdab45fcf Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Fri, 9 Sep 2011 13:23:51 -0700 Subject: [PATCH 088/258] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Optiplex 790 use reboot=pci BugLink: http://bugs.launchpad.net/bugs/818933 The Dell Optiplex 790 doesn't reboot unless reboot=pci is set. Signed-off-by: Leann Ogasawara Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index de9f35ade26e57..0a72663093d3db 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -498,6 +498,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6520"), }, }, + { /* Handle problems with rebooting on the OptiPlex 790. */ + .callback = set_pci_reboot, + .ident = "Dell OptiPlex 790", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 790"), + }, + }, { } }; From 2337d8aefdf10241b656b8cb221ef1385e709581 Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Wed, 14 Sep 2011 11:27:20 -0700 Subject: [PATCH 089/258] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Optiplex 990 use reboot=pci BugLink: http://bugs.launchpad.net/bugs/768039 The Dell Optiplex 990 doesn't reboot unless reboot=pci is set. Signed-off-by: Leann Ogasawara Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 0a72663093d3db..d7f5a9924f9280 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -506,6 +506,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 790"), }, }, + { /* Handle problems with rebooting on the OptiPlex 990. */ + .callback = set_pci_reboot, + .ident = "Dell OptiPlex 990", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 990"), + }, + }, { } }; From d39b026ea448417024337a46606b868e617071ca Mon Sep 17 00:00:00 2001 From: Leann Ogasawara Date: Fri, 26 Aug 2011 07:30:16 -0700 Subject: [PATCH 090/258] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Latitude E6220 use reboot=pci BugLink: http://bugs.launchpad.net/bugs/838402 The Dell Latitude E6220 doesn't reboot unless reboot=pci is set. Signed-off-by: Leann Ogasawara Acked-by: Tim Gardner Acked-by: Seth Forshee Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index d7f5a9924f9280..bfbac81d964705 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -514,6 +514,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 990"), }, }, + { /* Handle problems with rebooting on the Latitude E6220. */ + .callback = set_pci_reboot, + .ident = "Dell Latitude E6220", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6220"), + }, + }, { } }; From 4417473d133e51864d293d4f7c1249f0cb0491e3 Mon Sep 17 00:00:00 2001 From: "joseph.salisbury@canonical.com" Date: Tue, 16 Apr 2013 17:03:51 -0400 Subject: [PATCH 091/258] UBUNTU: SAUCE: (no-up) x86: reboot: Make Dell Optiplex 390 use reboot=pci BugLink: http://bugs.launchpad.net/bugs/800660 From: Leann Ogasawara Signed-off-by: Leann Ogasawara Signed-off-by: Joseph Salisbury Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- arch/x86/kernel/reboot.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index bfbac81d964705..ed427d8bd8f186 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -522,6 +522,14 @@ static const struct dmi_system_id reboot_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6220"), }, }, + { /* Handle problems with rebooting on the OptiPlex 390. */ + .callback = set_pci_reboot, + .ident = "Dell OptiPlex 390", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 390"), + }, + }, { } }; From 355a249ffd5ea9320708440b1b50bf2de10ddb51 Mon Sep 17 00:00:00 2001 From: Xiangliang Yu Date: Thu, 7 Mar 2013 14:29:16 +0000 Subject: [PATCH 092/258] UBUNTU: SAUCE: (no-up) PCI: fix system hang issue of Marvell SATA host controller BugLink: http://bugs.launchpad.net/bugs/1159863 Hassle someone if this patch hasn't been removed by 13.10. See https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1159863/comments/2 Fix system hang issue: if first accessed resource file of BAR0 ~ BAR4, system will hang after executing lspci command Signed-off-by: Kamal Mostafa Signed-off-by: Tim Gardner Signed-off-by: Mingcong Bai --- drivers/pci/quirks.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index fcee338bf0a169..f1692c782f2dd8 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -302,6 +302,21 @@ static void quirk_mmio_always_on(struct pci_dev *dev) DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on); +/* The BAR0 ~ BAR4 of Marvell 9125 device can't be accessed +* by IO resource file, and need to skip the files +*/ +static void quirk_marvell_mask_bar(struct pci_dev *dev) +{ + int i; + + for (i = 0; i < 5; i++) + if (dev->resource[i].start) + dev->resource[i].start = + dev->resource[i].end = 0; +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL_EXT, 0x9125, + quirk_marvell_mask_bar); + /* * The Mellanox Tavor device gives false positive parity errors. Disable * parity error reporting. From 5238fd82c251bebbf579ccf523f47aa762f7b0c0 Mon Sep 17 00:00:00 2001 From: You-Sheng Yang Date: Mon, 16 Mar 2020 17:27:21 +0800 Subject: [PATCH 093/258] BACKPORT: UBUNTU: SAUCE: Input: i8042 - fix the selftest retry logic BugLink: https://bugs.launchpad.net/bugs/1866734 It returns -NODEV at the first selftest timeout, so the retry logic doesn't work. Move the return outside of the while loop to make it real retry 5 times before returns -ENODEV. BTW, the origin loop will retry 6 times, also fix this. Signed-off-by: You-Sheng Yang (backported from https://lore.kernel.org/linux-input/20200310033640.14440-1-vicamo@gmail.com/) Signed-off-by: Paolo Pisati [ Mingcong Bai: Resolved a minor mrege conflict in drivers/input/serio/i8042.c ] Signed-off-by: Mingcong Bai --- drivers/input/serio/i8042.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 8bcce11cb7ce3d..4633fb1d32be3d 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -942,25 +942,28 @@ static int i8042_controller_selftest(void) { unsigned char param; int i = 0; + int ret; /* * We try this 5 times; on some really fragile systems this does not * take the first time... */ - do { - - if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { - pr_err("i8042 controller selftest timeout\n"); - return -ENODEV; - } + while (i++ < 5) { - if (param == I8042_RET_CTL_TEST) + ret = i8042_command(¶m, I8042_CMD_CTL_TEST); + if (ret) + pr_err("i8042 controller selftest timeout (%d/5)\n", i); + else if (param == I8042_RET_CTL_TEST) return 0; + else + dbg("i8042 controller selftest: %#x != %#x\n", + param, I8042_RET_CTL_TEST); - dbg("i8042 controller selftest: %#x != %#x\n", - param, I8042_RET_CTL_TEST); msleep(50); - } while (i++ < 5); + } + + if (ret) + return -ENODEV; #ifdef CONFIG_X86 /* From f20c175a37f5a1e944c4a9851bf800aa9e5ffc7a Mon Sep 17 00:00:00 2001 From: "Chia-Lin Kao (AceLan)" Date: Mon, 15 Mar 2021 16:05:40 +0800 Subject: [PATCH 094/258] UBUNTU: SAUCE: Input: i8042 - add dmi quirk BugLink: https://bugs.launchpad.net/bugs/1919123 On some platforms, the EC doesn't support the register reading sequence for sentelic[1], and then make the EC can't respond commands for a while when probing. It leads to the keyboard non-responsive for around 10 seconds while waking up from s2idle. [ 44.304488] i8042: [9804] d4 -> i8042 (command) [ 44.304634] i8042: [9804] f3 -> i8042 (parameter) [ 44.304787] i8042: [9804] fa <- i8042 (interrupt, 1, 12) [ 44.304855] i8042: [9804] d4 -> i8042 (command) [ 44.304938] i8042: [9804] 66 -> i8042 (parameter) [ 44.337698] i8042: [9813] d4 -> i8042 (command) [ 44.905695] i8042: [9942] 88 -> i8042 (parameter) [ 45.497478] i8042: [10102] d4 -> i8042 (command) [ 46.098041] i8042: [10253] f3 -> i8042 (parameter) [ 46.098070] i8042: [10253] fe <- i8042 (interrupt, 1, 12) [ 46.718154] i8042: [10386] d4 -> i8042 (command) [ 47.309915] i8042: [10386] f4 -> i8042 (parameter) [ 47.918961] i8042: [10556] d4 -> i8042 (command) [ 48.402624] i8042: [10556] f6 -> i8042 (parameter) A DMI quirk to mark this platform doesn't have aux device could avoid those commands to be sent. And the system could still using i2c interface to communicate with the touchpad. 1. https://www.kernel.org/doc/html/v5.11/input/devices/sentelic.html#programming-sequence-for-register-reading-writing Signed-off-by: Chia-Lin Kao (AceLan) (cherry picked from https://lkml.org/lkml/2021/3/15/126) Signed-off-by: Chia-Lin Kao (AceLan) Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/input/serio/i8042.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 4633fb1d32be3d..e065411853223f 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -183,6 +184,24 @@ static bool i8042_handle_data(int irq); static i8042_filter_t i8042_platform_filter; static void *i8042_platform_filter_context; +static int __init i8042_set_noaux(const struct dmi_system_id *dmi) +{ + i8042_noaux = true; + return 1; +} + +static const struct dmi_system_id i8042_quirks[] __initconst = { + { + .callback = i8042_set_noaux, + .ident = "Dell laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Precision 5550"), + }, + }, + {}, +}; + void i8042_lock_chip(void) { mutex_lock(&i8042_mutex); @@ -1556,6 +1575,8 @@ static int i8042_probe(struct platform_device *dev) i8042_dritek_enable(); #endif + dmi_check_system(i8042_quirks); + if (!i8042_noaux) { error = i8042_setup_aux(); if (error && error != -ENODEV && error != -EBUSY) From 00112faf4662e586cf0c11d85274ab8ad7e75a8f Mon Sep 17 00:00:00 2001 From: Andrea Righi Date: Thu, 15 Dec 2022 14:03:38 +0100 Subject: [PATCH 095/258] UBUNTU: SAUCE: input: i8042: fix section mismatch warning Fix the following warning introduced by an old sauce patch: WARNING: modpost: vmlinux.o: section mismatch in reference: i8042_probe (section: .text) -> i8042_quirks (section: .init.rodata) Fixes: ec05d1e5ffd7 ("UBUNTU: SAUCE: Input: i8042 - add dmi quirk") Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/input/serio/i8042.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index e065411853223f..c7638b3efc0e7f 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -190,7 +190,7 @@ static int __init i8042_set_noaux(const struct dmi_system_id *dmi) return 1; } -static const struct dmi_system_id i8042_quirks[] __initconst = { +static const struct dmi_system_id i8042_quirks[] = { { .callback = i8042_set_noaux, .ident = "Dell laptop", From 9512b249a20e8294e591ce3fc46454ccd5e7f2a1 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Thu, 5 May 2022 14:20:14 +0800 Subject: [PATCH 096/258] UBUNTU: SAUCE: net: phy: marvell: Skip setting LED on Dell EMC board BugLink: https://bugs.launchpad.net/bugs/1971667 The board in question requires certain LED config, which is already configured by BIOS, to correctly show its networking status. However, Marvell PHY driver hardcodes LED value so we need a way to preserve the default set by BIOS. PHY maintainer asked for a "generic" approach which goes no where [1], so let's move on and use a quirk to handle it. [1] https://lore.kernel.org/lkml/20220420124053.853891-2-kai.heng.feng@canonical.com/ Signed-off-by: Kai-Heng Feng Acked-by: Tim Gardner Signed-off-by: Andrea Righi Signed-off-by: Mingcong Bai --- drivers/net/phy/marvell.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index f71cffa8840628..053f53ee42d63c 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -356,6 +357,16 @@ struct marvell_priv { u8 vct_phase; }; +static const struct dmi_system_id skip_config_led_tbl[] = { + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Dell EMC"), + DMI_MATCH(DMI_BOARD_NAME, "0d370eed-89ca-4dc0-a365-e9904c4c62bb"), + }, + }, + {} +}; + static int marvell_read_page(struct phy_device *phydev) { return __phy_read(phydev, MII_MARVELL_PHY_PAGE); @@ -845,6 +856,9 @@ static void marvell_config_led(struct phy_device *phydev) u16 def_config; int err; + if (dmi_check_system(skip_config_led_tbl)) + return; + switch (MARVELL_PHY_FAMILY_ID(phydev->phy_id)) { /* Default PHY LED config: LED[0] .. Link, LED[1] .. Activity */ case MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E1121R): From abbc3f64086e8fe150326fe25728b360e03cc4af Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Mon, 19 Aug 2024 10:59:07 +0800 Subject: [PATCH 097/258] UBUNTU: SAUCE: PCI: ASPM: Allow OS to configure ASPM where BIOS is incapable of BugLink: https://bugs.launchpad.net/bugs/2072679 Since commit f492edb40b54 ("PCI: vmd: Add quirk to configure PCIe ASPM and LTR"), ASPM is configured for NVMe devices enabled in VMD domain. However, that doesn't cover the case when FADT has ACPI_FADT_NO_ASPM set. So add a new attribute to bypass aspm_disabled so OS can configure ASPM. Fixes: f492edb40b54 ("PCI: vmd: Add quirk to configure PCIe ASPM and LTR") Link: https://lore.kernel.org/linux-pm/218aa81f-9c6-5929-578d-8dc15f83dd48@panix.com/ (cherry picked from https://lore.kernel.org/linux-pci/20240530085227.91168-1-kai.heng.feng@canonical.com/) Signed-off-by: Kai-Heng Feng Signed-off-by: Hui Wang Acked-by: Kuan-Ying Lee Acked-by: Aaron Jauregui Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/pci/pcie/aspm.c | 8 ++++++-- include/linux/pci.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 925373b98dff0c..7295d037934fd4 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1472,8 +1472,12 @@ static int __pci_enable_link_state(struct pci_dev *pdev, int state, bool locked) * the _OSC method), we can't honor that request. */ if (aspm_disabled) { - pci_warn(pdev, "can't override BIOS ASPM; OS doesn't have ASPM control\n"); - return -EPERM; + if (aspm_support_enabled && pdev->aspm_os_control) + pci_info(pdev, "BIOS can't program ASPM, let OS control it\n"); + else { + pci_warn(pdev, "can't override BIOS ASPM; OS doesn't have ASPM control\n"); + return -EPERM; + } } if (!locked) diff --git a/include/linux/pci.h b/include/linux/pci.h index 10685bc9fd4c47..2b46163dac4dbb 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -511,6 +511,7 @@ struct pci_dev { unsigned int rom_bar_overlap:1; /* ROM BAR disable broken */ unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */ unsigned int non_mappable_bars:1; /* BARs can't be mapped to user-space */ + unsigned int aspm_os_control:1; /* Display of ROM attribute enabled? */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ From fdddbf02fd24e5b8a305e3dcf8be251eed6ece48 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Mon, 19 Aug 2024 10:59:08 +0800 Subject: [PATCH 098/258] UBUNTU: SAUCE: PCI: vmd: Let OS control ASPM for devices under VMD domain BugLink: https://bugs.launchpad.net/bugs/2072679 Intel SoC cannot reach lower power states when mapped VMD PCIe bridges and NVMe devices don't have ASPM configured. So set aspm_os_control attribute to let OS really enable ASPM for those devices. Fixes: f492edb40b54 ("PCI: vmd: Add quirk to configure PCIe ASPM and LTR") Link: https://lore.kernel.org/linux-pm/218aa81f-9c6-5929-578d-8dc15f83dd48@panix.com/ (cherry picked from https://lore.kernel.org/linux-pci/20240530085227.91168-2-kai.heng.feng@canonical.com/) Signed-off-by: Kai-Heng Feng Signed-off-by: Hui Wang Acked-by: Kuan-Ying Lee Acked-by: Aaron Jauregui Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/pci/controller/vmd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index d4ae250d4bc6d7..3650c6b6038137 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -739,6 +739,8 @@ static int vmd_pm_enable_quirk(struct pci_dev *pdev, void *userdata) if (!(features & VMD_FEAT_BIOS_PM_QUIRK)) return 0; + pdev->aspm_os_control = 1; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_LTR); if (!pos) goto out_state_change; From 310e14f04f860b70ace16ec45040afdc8023c961 Mon Sep 17 00:00:00 2001 From: "Yo-Jung (Leo) Lin" Date: Wed, 3 Sep 2025 15:04:20 -0400 Subject: [PATCH 099/258] UBUNTU: SAUCE: i2c: i801: Do not instantiate spd5118 under SPD Write Disable BugLink: https://bugs.launchpad.net/bugs/2114963 If SPD Write Disable bit in the SMBus is enabled, writing data to addresses from 0x50 to 0x57 is forbidden. This may lead to the following issues for spd5118 devices: 1) Writes to the sensor hwmon sysfs attributes will always result in ENXIO. 2) During system-wide resume, errors may occur during regcache sync, resulting in the following error messages: kernel: spd5118 1-0050: failed to write b = 0: -6 kernel: spd5118 1-0050: pm: dpm_run_callback(): spd5118_resume [spd5118] returns -6 kernel: spd5118 1-0050: pm: failed to resume async: error -6 3) nvmem won't be usable, because writing to the page selector becomes impossible. Also, BIOS vendors may choose to set the page to a value != 0 after a board reset. This will make the sensor not functional unless its MR11 register can be changed, which is impossible due to writes being disabled. To address these issues, don't instantiate it at all if the SPD Write Disable bit is set. Signed-off-by: Yo-Jung Lin (Leo) (cherry picked from https://lore.kernel.org/all/20250613-for-upstream-not-instantiate-spd5118-v2-1-cf456ed9b587@canonical.com/) Signed-off-by: Yo-Jung (Leo) Lin (backported from commit cbb6c74a6507331f0bfd03b33fb763a1005b29ac noble-linux-oem) [cremfuelled: context differed between patch and file, and so a verbatim copy of] [cremfuelled: each hunk was placed where it most faithfully followed the original patch] Signed-off-by: Alice C. Munduruca Acked-by: Chia-Lin Kao (AceLan) Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/i2c/busses/i2c-i801.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index b29c99ed388393..c8e0ebe58e374a 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -988,6 +988,14 @@ static void i801_disable_host_notify(struct i801_priv *priv) iowrite8(priv->original_slvcmd, SMBSLVCMD(priv)); } +static inline __maybe_unused void __i801_register_spd(struct i801_priv *priv) +{ + if (priv->original_hstcfg & SMBHSTCFG_SPD_WD) + i2c_register_spd_write_disable(&priv->adapter); + else + i2c_register_spd_write_enable(&priv->adapter); +} + static const struct i2c_algorithm smbus_algorithm = { .smbus_xfer = i801_access, .functionality = i801_func, @@ -1170,6 +1178,19 @@ static void dmi_check_onboard_devices(const struct dmi_header *dm, void *adap) } } +#ifdef CONFIG_I2C_I801_MUX +static void i801_register_spd(struct i801_priv *priv) +{ + if (!priv->mux_pdev) + __i801_register_spd(priv); +} +#else +static void i801_register_spd(struct i801_priv *priv) +{ + __i801_register_spd(priv); +} +#endif + /* Register optional targets */ static void i801_probe_optional_targets(struct i801_priv *priv) { @@ -1190,10 +1211,7 @@ static void i801_probe_optional_targets(struct i801_priv *priv) dmi_walk(dmi_check_onboard_devices, &priv->adapter); /* Instantiate SPD EEPROMs unless the SMBus is multiplexed */ -#ifdef CONFIG_I2C_I801_MUX - if (!priv->mux_pdev) -#endif - i2c_register_spd_write_enable(&priv->adapter); + i801_register_spd(priv); } #else static void __init input_apanel_init(void) {} @@ -1296,7 +1314,7 @@ static int i801_notifier_call(struct notifier_block *nb, unsigned long action, return NOTIFY_DONE; /* Call i2c_register_spd for muxed child segments */ - i2c_register_spd_write_enable(to_i2c_adapter(dev)); + __i801_register_spd(priv); return NOTIFY_OK; } From 2c383369ea3a6380e20daa31351a93c51c116f52 Mon Sep 17 00:00:00 2001 From: Jason Chen Date: Thu, 19 Dec 2024 03:38:36 +0800 Subject: [PATCH 100/258] UBUNTU: SAUCE: ACPI: scan: Update HID for new platform BugLink: https://bugs.launchpad.net/bugs/2090932 To resolve the probe-ordering issue on the newer platform Update the HID for LNL and ARL Signed-off-by: Hao Yao Signed-off-by: Jason Chen (cherry picked from commit https://raw.githubusercontent.com/drhooray/vision-drivers/6424af8b225f136375f1a8b01500217d0545c8e6/patch/0001-ACPI-scan-Update-HID-for-new-platform.patch) Signed-off-by: You-Sheng Yang Acked-by: Philip Cox Acked-by: John Cabaj Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/acpi/scan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 530547cda8b286..6aafbf7a2e4dc2 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -861,6 +861,8 @@ static const char * const acpi_honor_dep_ids[] = { "INTC1095", /* IVSC (ADL) driver must be loaded to allow i2c access to camera sensors */ "INTC100A", /* IVSC (RPL) driver must be loaded to allow i2c access to camera sensors */ "INTC10CF", /* IVSC (MTL) driver must be loaded to allow i2c access to camera sensors */ + "INTC10DE", /* IVSC (LNL) driver must be loaded to allow i2c access to camera sensors */ + "INTC10E0", /* IVSC (ARL) driver must be loaded to allow i2c access to camera sensors */ "RSCV0001", /* RISC-V PLIC */ "RSCV0002", /* RISC-V APLIC */ "RSCV0005", /* RISC-V SBI MPXY MBOX */ From e74f935b526be1274ad9dd3c9e78905dc78819b6 Mon Sep 17 00:00:00 2001 From: You-Sheng Yang Date: Fri, 27 Mar 2026 17:38:04 +0800 Subject: [PATCH 101/258] UBUNTU: SAUCE: ACPI: respect items already in honor_dep before skipping BugLink: https://bugs.launchpad.net/bugs/2145171 Upstream v6.18-rc1 commit 4405a214df146775338a1e6232701a29024b82e1 "ACPI: scan: Add Intel CVS ACPI HIDs to acpi_ignore_dep_ids[]" (also backported to v6.17.8, v6.12.58), which in effect reverts a not yet upstreamed Intel MIPI camera patch titled "ACPI: scan: Update HID for new platform" meant for bug 2090932, and therefore they share the same symptom: [ 4.484426] ov02c10 i2c-OVTI02C1:00: chip id mismatch: 560243!=0 [ 4.484432] ov02c10 i2c-OVTI02C1:00: failed to find sensor: -6 This patch respects upstream change while keeping Ubuntu's enablement efforts remaining effective. Signed-off-by: You-Sheng Yang Acked-by: Yufeng Gao Signed-off-by: Timo Aaltonen Signed-off-by: Mingcong Bai --- drivers/acpi/scan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 6aafbf7a2e4dc2..3b0cf49acaed56 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2027,7 +2027,7 @@ int acpi_scan_add_dep(acpi_handle handle, struct acpi_handle_list *dep_devices) honor_dep = acpi_info_matches_ids(info, acpi_honor_dep_ids); kfree(info); - if (skip) + if (skip && !honor_dep) continue; dep = kzalloc_obj(*dep); From 387692b4f48a4cb0e8ed5607de74b0092c26ed08 Mon Sep 17 00:00:00 2001 From: Aaron Ma Date: Wed, 3 Aug 2022 09:55:00 +0200 Subject: [PATCH 102/258] UBUNTU: SAUCE: igc: wait for the MAC copy when enabled MAC passthrough BugLink: https://bugs.launchpad.net/bugs/1942999 Such as dock hot plug event when runtime, for hardware implementation, the MAC copy takes less than one second when BIOS enabled MAC passthrough. After test on Lenovo TBT4 dock, 600ms is enough to update the MAC address. Otherwise ethernet fails to work. Link: https://lore.kernel.org/lkml/20210702045120.22855-2-aaron.ma@canonical.com/ Signed-off-by: Aaron Ma Acked-by: Stefan Bader [note: this is a non-upstream work-around since upstream does not seem to be to hard at work to do a proper fix] Acked-by: Tim Gardner Signed-off-by: Stefan Bader Signed-off-by: Paolo Pisati Signed-off-by: Mingcong Bai --- drivers/net/ethernet/intel/igc/igc_main.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index c470d2354ce8ff..5b7cea8fe412f2 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -7202,6 +7202,9 @@ static int igc_probe(struct pci_dev *pdev, memcpy(&hw->mac.ops, ei->mac_ops, sizeof(hw->mac.ops)); memcpy(&hw->phy.ops, ei->phy_ops, sizeof(hw->phy.ops)); + if (pci_is_thunderbolt_attached(pdev)) + msleep(600); + /* Initialize skew-specific constants */ err = ei->get_invariants(hw); if (err) From 2f9222a60cdf651a1fd9231a8c34a35f7a5e1a1a Mon Sep 17 00:00:00 2001 From: Max Lee Date: Wed, 11 Mar 2026 15:51:07 +0800 Subject: [PATCH 103/258] UBUNTU: SAUCE: igc: Increase Thunderbolt MAC passthrough delay to 1000ms BugLink: https://bugs.launchpad.net/bugs/2143197 [Fix] Increase the existing Thunderbolt MAC passthrough wait in igc probe path: from 600ms to 1000ms Ubuntu already carries a SAUCE workaround (commit 534981aaa8) that uses fixed delay timing. Newer Dell docks need a longer timing window. Debug testing shows BIOS MAC update timing: cold boot: MAC already correct at probe start hotplug: MAC becomes correct at ~300-800ms after probe starts Use 1000ms (observed 800ms worst-case + margin) to make hotplug reliable. Upstream context: https://lore.kernel.org/lkml/20210702045120.22855-2-aaron.ma@canonical.com/ (Upstream preferred polling over fixed delay; Ubuntu keeps minimal stable fixed-delay workaround.) Signed-off-by: Max Lee Signed-off-by: Paolo Pisati Signed-off-by: Mingcong Bai --- drivers/net/ethernet/intel/igc/igc_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 5b7cea8fe412f2..367da3fd1b710a 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -7203,7 +7203,7 @@ static int igc_probe(struct pci_dev *pdev, memcpy(&hw->phy.ops, ei->phy_ops, sizeof(hw->phy.ops)); if (pci_is_thunderbolt_attached(pdev)) - msleep(600); + msleep(1000); /* Initialize skew-specific constants */ err = ei->get_invariants(hw); From ccc2500ad0f63153c4fbb2fe56aef6ffeb964652 Mon Sep 17 00:00:00 2001 From: Wentao Guan Date: Wed, 8 May 2024 18:11:37 +0800 Subject: [PATCH 104/258] DEEPIN: net: stmmac: Add phytium old dwmac acpi_device_id As Phytium ACPI Description Specification document v1.2 p13 Device HID said for old v1.0 Spec,add FTGM0001 for such as some FT2000 device compat. Signed-off-by: Wentao Guan Link: https://github.com/deepin-community/kernel/commit/5c2ef68bfa7f52128c717efb29c3891152fb1c16 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 7df8417da4b623..07614a06be386b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -197,6 +197,7 @@ MODULE_DEVICE_TABLE(of, phytium_dwmac_of_match); #ifdef CONFIG_ACPI static const struct acpi_device_id phytium_dwmac_acpi_ids[] = { + { .id = "FTGM0001" }, // compat FT2000/4 id { .id = "PHYT0004" }, { } }; From 0ccf719836417cee2a7e947287a36eee59ab2a33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=98=E6=80=80?= Date: Tue, 9 Jul 2024 17:17:48 +0800 Subject: [PATCH 105/258] DEEPIN: net: stmmac: dwmac-phytium: compat some FT2000 Compat with some quirk platform FT2000/4 as id "FTGM0001", use rgmii-rxid instead of rgmii (wrong ACPI DSDT), and set dma_ops and host_dma_width (fix code style). origin code in k4.19 is following: acpi_dma_configure(priv->device,DEV_DMA_COHERENT); static u64 my_mask=0xffffffff; priv->device->dma_mask = &my_mask; priv->device->coherent_dma_mask = (u32)~0; port to k5.10 add: pdev->dev.dma_ops = NULL; Signed-off-by: Caicai Signed-off-by: hmy Signed-off-by: wenlunpeng Signed-off-by: Wentao Guan Link: https://github.com/deepin-community/kernel/commit/dbd62afd7ac965ad1637f3b051af98e2362c156d Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- .../ethernet/stmicro/stmmac/dwmac-phytium.c | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 07614a06be386b..8beb560f9aad5c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -68,6 +68,21 @@ static int phytium_dwmac_probe(struct platform_device *pdev) return -ENOMEM; plat->phy_interface = device_get_phy_mode(&pdev->dev); +#ifdef CONFIG_ACPI + static const struct acpi_device_id phytium_old_acpi_id[] = { + { .id = "FTGM0001" }, // compat FT2000/4 id + { } + }; + /* "phy-mode" in phytium platform DSDT is not correct in some old device. + * Force this PHY mode to rgmii-rxid and info of its use. + * If the phy-mode rgmii is realy used, a blacklist may need to be added. + */ + if (acpi_match_device_ids(to_acpi_device(&pdev->dev), phytium_old_acpi_id) && + plat->phy_interface == PHY_INTERFACE_MODE_RGMII) { + plat->phy_interface = PHY_INTERFACE_MODE_RGMII_RXID; + dev_info(&pdev->dev, "phytium workaround: phy-mode from rgmii to rgmii-rxid\n"); + } +#endif if (plat->phy_interface < 0) return plat->phy_interface; @@ -156,7 +171,15 @@ static int phytium_dwmac_probe(struct platform_device *pdev) plat->dma_cfg->aal = fwnode_property_read_bool(fwnode, "snps,aal"); plat->dma_cfg->fixed_burst = fwnode_property_read_bool(fwnode, "snps,fixed-burst"); plat->dma_cfg->mixed_burst = fwnode_property_read_bool(fwnode, "snps,mixed-burst"); - +#ifdef CONFIG_ACPI + /* Some old phytium 2000/4 FTGM0001 cannot auto deferred stmmac DMA settings + * show kernel error 'DMA descriptors allocation failed' + */ + if (acpi_match_device_ids(to_acpi_device(&pdev->dev), phytium_old_acpi_id)) { + pdev->dev.dma_ops = NULL; // solved set DMA mask Failed + plat->host_dma_width = 32; + } +#endif plat->axi->axi_lpi_en = false; plat->axi->axi_xit_frm = false; plat->axi->axi_wr_osr_lmt = 7; From f6fdf947cd343c375db20e83b0da8d66b5bdd219 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Fri, 14 Feb 2025 17:48:41 +0800 Subject: [PATCH 106/258] BACKPORT: DEEPIN: pci/quirks: LS7A2000: Fix pm transition of devices under pcie port The CPU may not be able to access the PCI header of the device if it is in low-power mode. Signed-off-by: Zhao Qunqin Signed-off-by: Binbin Zhou Link: https://github.com/deepin-community/kernel/commit/3be8f9c563edaa97e28e3154d3d396b3ba90bf47 [Mingcong Bai: Resolved minor conflict in include/linux/pci.h] Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit [Mingcong Bai: Resolved a minor post-6.19 merge conflict in include/linux/pci.h] Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in include/linux/pci.h ] Signed-off-by: Mingcong Bai --- drivers/pci/quirks.c | 30 ++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index f1692c782f2dd8..d048a94f2e4981 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -400,6 +400,36 @@ static void quirk_tigerpoint_bm_sts(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_TGP_LPC, quirk_tigerpoint_bm_sts); #endif +#define DEV_PCIE_PORT_4 0x7a39 +#define DEV_PCIE_PORT_5 0x7a49 +#define DEV_PCIE_PORT_6 0x7a59 +#define DEV_PCIE_PORT_7 0x7a69 +static void loongson_d3_and_link_quirk(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + struct pci_dev *bridge; + static const struct pci_device_id bridge_devids[] = { + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_4) }, + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_5) }, + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_6) }, + { PCI_VDEVICE(LOONGSON, DEV_PCIE_PORT_7) }, + { 0, }, + }; + + /* look for the matching bridge */ + while (!pci_is_root_bus(bus)) { + bridge = bus->self; + bus = bus->parent; + if (bridge && pci_match_id(bridge_devids, bridge)) { + dev->dev_flags |= (PCI_DEV_FLAGS_NO_D3 | + PCI_DEV_FLAGS_NO_LINK_SPEED_CHANGE); + dev->no_d1d2 = 1; + break; + } + } +} +DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, loongson_d3_and_link_quirk); + /* Chipsets where PCI->PCI transfers vanish or hang */ static void quirk_nopcipci(struct pci_dev *dev) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 2b46163dac4dbb..c022efb28471bf 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -263,6 +263,7 @@ enum pci_dev_flags { PCI_DEV_FLAGS_PCI_BRIDGE_NO_ALIAS = (__force pci_dev_flags_t) (1 << 14), /* Do not use native PCIe port services (equivalent to pcie_ports=compat) */ PCI_DEV_FLAGS_NO_PORT_SERVICES = (__force pci_dev_flags_t) (1 << 15), + PCI_DEV_FLAGS_NO_LINK_SPEED_CHANGE = (__force pci_dev_flags_t) (1 << 16), }; enum pci_irq_reroute_variant { From d449deb49b0a9b765c1d038941e6399d31ca3d42 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 27 Jun 2024 14:37:53 +0800 Subject: [PATCH 107/258] DEEPIN: net: stmmac: fix dwmac-phytium build on 6.9 Declare phytium_dwmac_remove() as a static function to resolve a missing prototype warning. Signed-off-by: Mingcong Bai Link: https://github.com/deepin-community/kernel-rolling/commit/ece68ac12adb566d8ba0c93ae8f3f3abef9e041d Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 8beb560f9aad5c..7adeddd82d33c7 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -198,7 +198,7 @@ static int phytium_dwmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); } -int phytium_dwmac_remove(struct platform_device *pdev) +static int phytium_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); From b1031c9d60960930e4df032e59fdcfeba9d9a800 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Fri, 28 Jun 2024 11:45:05 +0800 Subject: [PATCH 108/258] DEEPIN: net: ethernet: phytium: fix phytmac_platform on 6.9 Add missing include. Signed-off-by: Mingcong Bai Link: https://github.com/deepin-community/kernel-rolling/commit/02f5b7aec92af6b6cfe6bda39ed1d884b5471606 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 2f2170cf0775ae..ce6271ac52cd08 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -17,6 +17,8 @@ #include "phytmac_v1.h" #include "phytmac_v2.h" +#include + static const struct phytmac_config phytium_1p0_config = { .hw_if = &phytmac_1p0_hw, .caps = PHYTMAC_CAPS_TAILPTR From 0a225afe215c4a3b0996cd82dd2cf50d8b518ae9 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Fri, 28 Jun 2024 14:08:40 +0800 Subject: [PATCH 109/258] BACKPORT: DEEPIN: net: ethernet: fix phytmac on 6.9 Declare multiple functions as static functions to suppress warnings about missing prototypes. Signed-off-by: Mingcong Bai Link: https://github.com/deepin-community/kernel-rolling/commit/3672205e780ee88d27b7885e27f450cd332f842c [Kexy: Resolved minor conflicts in drivers/net/ethernet/phytium/phytmac_main.c, and drivers/net/ethernet/phytium/phytmac_v2.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 8 ++++---- drivers/net/ethernet/phytium/phytmac_platform.c | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 1f4e9a2b97ee4f..4ba9e447ac3667 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1852,7 +1852,7 @@ static int phytmac_phylink_connect(struct phytmac *pdata) return 0; } -int phytmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, +static int phytmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, phy_interface_t interface, const unsigned long *advertising, bool permit_pause_to_mac) @@ -1860,7 +1860,7 @@ int phytmac_pcs_config(struct phylink_pcs *pcs, unsigned int mode, return 0; } -void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, +static void phytmac_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, phy_interface_t interface, int speed, int duplex) { struct phytmac *pdata = container_of(pcs, struct phytmac, phylink_pcs); @@ -2010,7 +2010,7 @@ static void phytmac_mac_link_up(struct phylink_config *config, netif_tx_wake_all_queues(ndev); } -int phytmac_mdio_register(struct phytmac *pdata) +static int phytmac_mdio_register(struct phytmac *pdata) { struct phytmac_hw_if *hw_if = pdata->hw_if; int ret; @@ -2652,7 +2652,7 @@ static int phytmac_init(struct phytmac *pdata) return 0; } -void phytmac_default_config(struct phytmac *pdata) +static void phytmac_default_config(struct phytmac *pdata) { struct net_device *ndev = pdata->ndev; diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index ce6271ac52cd08..cf628a45172848 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -168,7 +168,6 @@ static int phytmac_get_phy_mode(struct platform_device *pdev) static int phytmac_plat_probe(struct platform_device *pdev) { const struct phytmac_config *phytmac_config = &phytium_1p0_config; - struct device_node *np = pdev->dev.of_node; struct resource *regs; struct phytmac *pdata; int ret, i; From bc38bf87ec2e3d5b82dd1033b7ac3544c779d96d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Fri, 28 Jun 2024 16:23:20 +0800 Subject: [PATCH 110/258] DEEPIN: net: ethernet: phytium: add a missing declaration for *np MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a compilation error: drivers/net/ethernet/phytium/phytmac_platform.c: In function ‘phytmac_plat_probe’: drivers/net/ethernet/phytium/phytmac_platform.c:98:55: error: ‘np’ undeclared (first use in this function); did you mean ‘up’? 98 | match = of_match_node(phytmac_dt_ids, np); | ^~ | up Signed-off-by: Mingcong Bai Link: https://github.com/deepin-community/kernel-rolling/commit/7b83119ea1c8d2509ad5c8042f05334eddfcc6d9 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_platform.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index cf628a45172848..d958ca1f168bb2 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -168,6 +168,7 @@ static int phytmac_get_phy_mode(struct platform_device *pdev) static int phytmac_plat_probe(struct platform_device *pdev) { const struct phytmac_config *phytmac_config = &phytium_1p0_config; + struct device_node *np; struct resource *regs; struct phytmac *pdata; int ret, i; From 7058219fa59c9ccd9a5b665f490b7de8ec078e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=98=E6=80=80?= Date: Wed, 3 Jul 2024 03:13:45 +0000 Subject: [PATCH 111/258] BACKPORT: DEEPIN: net: phytium: convert and remove validate() references Populate the supported interfaces bitmap and MAC capabilities mask for the phytium driver and remove the old validate implementation. Link:https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=cc0a75eb037539f68f7117a632673a75e39d8d6b Signed-off-by: Wentao Guan Link: https://github.com/deepin-community/kernel-rolling/commit/a5281306bf470a59541210d7e3d7e6636798d348 [Kexy: Resolved minor conflict in drivers/net/ethernet/phytium/phytmac_main.c] Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 71 ++------------------- 1 file changed, 6 insertions(+), 65 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 4ba9e447ac3667..9895b3fccd26e3 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -2067,72 +2067,7 @@ static void phytmac_pcs_get_state(struct phylink_config *config, state->link = hw_if->get_link(pdata, state->interface); } -static void phytmac_validate(struct phylink_config *config, - unsigned long *supported, - struct phylink_link_state *state) -{ - struct net_device *ndev = to_net_dev(config->dev); - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - struct phytmac *pdata = netdev_priv(ndev); - - if (state->interface != PHY_INTERFACE_MODE_SGMII && - state->interface != PHY_INTERFACE_MODE_1000BASEX && - state->interface != PHY_INTERFACE_MODE_2500BASEX && - state->interface != PHY_INTERFACE_MODE_5GBASER && - state->interface != PHY_INTERFACE_MODE_10GBASER && - state->interface != PHY_INTERFACE_MODE_USXGMII && - !phy_interface_mode_is_rgmii(state->interface)) { - bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); - return; - } - - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); - phylink_set(mask, Asym_Pause); - - if (state->interface == PHY_INTERFACE_MODE_10GBASER || - state->interface == PHY_INTERFACE_MODE_USXGMII) { - pdata->speed = state->speed; - pdata->duplex = state->duplex; - if (pdata->speed == SPEED_5000) { - phylink_set(mask, 5000baseT_Full); - } else { - phylink_set(mask, 10000baseCR_Full); - phylink_set(mask, 10000baseER_Full); - phylink_set(mask, 10000baseKR_Full); - phylink_set(mask, 10000baseLR_Full); - phylink_set(mask, 10000baseLRM_Full); - phylink_set(mask, 10000baseSR_Full); - phylink_set(mask, 10000baseT_Full); - } - } - - if (state->interface == PHY_INTERFACE_MODE_2500BASEX) - phylink_set(mask, 2500baseX_Full); - - if (state->interface == PHY_INTERFACE_MODE_5GBASER) - phylink_set(mask, 5000baseT_Full); - - if (state->interface == PHY_INTERFACE_MODE_1000BASEX) - phylink_set(mask, 1000baseX_Full); - - if (state->interface == PHY_INTERFACE_MODE_SGMII || - phy_interface_mode_is_rgmii(state->interface)) { - phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseT_Half); - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - } - - bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_and(state->advertising, state->advertising, mask, - __ETHTOOL_LINK_MODE_MASK_NBITS); -} - static const struct phylink_mac_ops phytmac_phylink_ops = { - .validate = phytmac_validate, .mac_select_pcs = phytmac_mac_select_pcs, .mac_config = phytmac_mac_config, .mac_link_down = phytmac_mac_link_down, @@ -2145,7 +2080,9 @@ static inline void set_phy_interface(unsigned long *intf) __set_bit(PHY_INTERFACE_MODE_1000BASEX, intf); __set_bit(PHY_INTERFACE_MODE_2500BASEX, intf); __set_bit(PHY_INTERFACE_MODE_USXGMII, intf); + __set_bit(PHY_INTERFACE_MODE_5GBASER, intf); __set_bit(PHY_INTERFACE_MODE_10GBASER, intf); + phy_interface_set_rgmii(intf); } static int phytmac_phylink_create(struct phytmac *pdata) @@ -2167,6 +2104,10 @@ static int phytmac_phylink_create(struct phytmac *pdata) } set_phy_interface(pdata->phylink_config.supported_interfaces); + pdata->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD | + MAC_5000FD | MAC_10000FD; + pdata->phylink = phylink_create(&pdata->phylink_config, fw_node, pdata->phy_interface, &phytmac_phylink_ops); if (IS_ERR(pdata->phylink)) { From fb7fe09977d700c72f9b6f07b004388074d398f3 Mon Sep 17 00:00:00 2001 From: Ayufan Date: Sun, 30 Dec 2018 13:32:47 +0100 Subject: [PATCH 112/258] ARMBIAN: ayufan: dts: rockpro64: change rx_delay for gmac Change-Id: Ib3899f684188aa1ed1545717af004bba53fe0e07 Signed-off-by: Ayufan Link: https://github.com/armbian/build/blob/dda6950c34f51e167811aee6d0ec220a492aac1a/patch/kernel/archive/rockchip64-6.16/board-rockpro64-change-rx_delay-for-gmac.patch Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi index 8b72ae6449c91a..5f607e83dd44cc 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi @@ -300,7 +300,7 @@ snps,reset-active-low; snps,reset-delays-us = <0 10000 50000>; tx_delay = <0x28>; - rx_delay = <0x11>; + rx_delay = <0x20>; status = "okay"; }; From 9e872452fab3e809358c72bd1e5a1a926d53e683 Mon Sep 17 00:00:00 2001 From: The-going <48602507+The-going@users.noreply.github.com> Date: Tue, 1 Feb 2022 19:22:21 +0300 Subject: [PATCH 113/258] ARMBIAN: drv:of: Device Tree Overlay ConfigFS interface Link: https://github.com/armbian/build/blob/bb1753ac3605e7b4988186f7b310f62589b0114e/patch/kernel/archive/sunxi-7.0/patches.armbian/drv-of-add-overlay-configfs-interface.patch Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- drivers/of/Kconfig | 7 + drivers/of/Makefile | 1 + drivers/of/configfs.c | 278 +++++++++++++++++++++++++++++++++++++++ drivers/of/fdt_address.c | 2 +- 4 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 drivers/of/configfs.c diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 50697cc3b07ebe..f9d2fc56fc41c4 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -123,6 +123,13 @@ config OF_OVERLAY_KUNIT_TEST If unsure, say N here, but this option is safe to enable. +config OF_CONFIGFS + bool "Device Tree Overlay ConfigFS interface" + select CONFIGFS_FS + depends on OF_OVERLAY + help + Enable a simple user-space driven DT overlay interface. + config OF_NUMA bool diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 379a0afcbdc0bf..2be9021017ba5a 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_OF_UNITTEST) += unittest.o obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o obj-$(CONFIG_OF_RESOLVE) += resolver.o obj-$(CONFIG_OF_OVERLAY) += overlay.o +obj-$(CONFIG_OF_CONFIGFS) += configfs.o obj-$(CONFIG_OF_NUMA) += of_numa.o ifdef CONFIG_KEXEC_FILE diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c new file mode 100644 index 00000000000000..87974779561fc9 --- /dev/null +++ b/drivers/of/configfs.c @@ -0,0 +1,278 @@ +/* + * Configfs entries for device-tree + * + * Copyright (C) 2013 - Pantelis Antoniou + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "of_private.h" + +struct cfs_overlay_item { + struct config_item item; + + char path[PATH_MAX]; + + const struct firmware *fw; + struct device_node *overlay; + int ov_id; + + void *dtbo; + int dtbo_size; +}; + +static inline struct cfs_overlay_item *to_cfs_overlay_item( + struct config_item *item) +{ + return item ? container_of(item, struct cfs_overlay_item, item) : NULL; +} + +static ssize_t cfs_overlay_item_path_show(struct config_item *item, + char *page) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + return sprintf(page, "%s\n", overlay->path); +} + +static ssize_t cfs_overlay_item_path_store(struct config_item *item, + const char *page, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + const char *p = page; + char *s; + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy to path buffer (and make sure it's always zero terminated */ + count = snprintf(overlay->path, sizeof(overlay->path) - 1, "%s", p); + overlay->path[sizeof(overlay->path) - 1] = '\0'; + + /* strip trailing newlines */ + s = overlay->path + strlen(overlay->path); + while (s > overlay->path && *--s == '\n') + *s = '\0'; + + pr_debug("%s: path is '%s'\n", __func__, overlay->path); + + err = request_firmware(&overlay->fw, overlay->path, NULL); + if (err != 0) + goto out_err; + + err = of_overlay_fdt_apply((void *)overlay->fw->data, + (u32)overlay->fw->size, + &overlay->ov_id, NULL); + if (err != 0) + goto out_err; + + return count; + +out_err: + + release_firmware(overlay->fw); + overlay->fw = NULL; + + overlay->path[0] = '\0'; + return err; +} + +static ssize_t cfs_overlay_item_status_show(struct config_item *item, + char *page) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + return sprintf(page, "%s\n", + overlay->ov_id > 0 ? "applied" : "unapplied"); +} + +CONFIGFS_ATTR(cfs_overlay_item_, path); +CONFIGFS_ATTR_RO(cfs_overlay_item_, status); + +static struct configfs_attribute *cfs_overlay_attrs[] = { + &cfs_overlay_item_attr_path, + &cfs_overlay_item_attr_status, + NULL, +}; + +ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, + void *buf, size_t max_count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + pr_debug("%s: buf=%p max_count=%zu\n", __func__, + buf, max_count); + + if (overlay->dtbo == NULL) + return 0; + + /* copy if buffer provided */ + if (buf != NULL) { + /* the buffer must be large enough */ + if (overlay->dtbo_size > max_count) + return -ENOSPC; + + memcpy(buf, overlay->dtbo, overlay->dtbo_size); + } + + return overlay->dtbo_size; +} + +ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, + const void *buf, size_t count) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + int err; + + /* if it's set do not allow changes */ + if (overlay->path[0] != '\0' || overlay->dtbo_size > 0) + return -EPERM; + + /* copy the contents */ + overlay->dtbo = kmemdup(buf, count, GFP_KERNEL); + if (overlay->dtbo == NULL) + return -ENOMEM; + + overlay->dtbo_size = count; + + err = of_overlay_fdt_apply(overlay->dtbo, overlay->dtbo_size, + &overlay->ov_id, NULL); + if (err != 0) + goto out_err; + + return count; + +out_err: + kfree(overlay->dtbo); + overlay->dtbo = NULL; + overlay->dtbo_size = 0; + overlay->ov_id = 0; + + return err; +} + +CONFIGFS_BIN_ATTR(cfs_overlay_item_, dtbo, NULL, SZ_1M); + +static struct configfs_bin_attribute *cfs_overlay_bin_attrs[] = { + &cfs_overlay_item_attr_dtbo, + NULL, +}; + +static void cfs_overlay_release(struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + if (overlay->ov_id > 0) + of_overlay_remove(&overlay->ov_id); + if (overlay->fw) + release_firmware(overlay->fw); + /* kfree with NULL is safe */ + kfree(overlay->dtbo); + kfree(overlay); +} + +static struct configfs_item_operations cfs_overlay_item_ops = { + .release = cfs_overlay_release, +}; + +static struct config_item_type cfs_overlay_type = { + .ct_item_ops = &cfs_overlay_item_ops, + .ct_attrs = cfs_overlay_attrs, + .ct_bin_attrs = cfs_overlay_bin_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *cfs_overlay_group_make_item( + struct config_group *group, const char *name) +{ + struct cfs_overlay_item *overlay; + + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); + if (!overlay) + return ERR_PTR(-ENOMEM); + + config_item_init_type_name(&overlay->item, name, &cfs_overlay_type); + return &overlay->item; +} + +static void cfs_overlay_group_drop_item(struct config_group *group, + struct config_item *item) +{ + struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); + + config_item_put(&overlay->item); +} + +static struct configfs_group_operations overlays_ops = { + .make_item = cfs_overlay_group_make_item, + .drop_item = cfs_overlay_group_drop_item, +}; + +static struct config_item_type overlays_type = { + .ct_group_ops = &overlays_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_group_operations of_cfs_ops = { + /* empty - we don't allow anything to be created */ +}; + +static struct config_item_type of_cfs_type = { + .ct_group_ops = &of_cfs_ops, + .ct_owner = THIS_MODULE, +}; + +struct config_group of_cfs_overlay_group; + +static struct configfs_subsystem of_cfs_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "device-tree", + .ci_type = &of_cfs_type, + }, + }, + .su_mutex = __MUTEX_INITIALIZER(of_cfs_subsys.su_mutex), +}; + +static int __init of_cfs_init(void) +{ + int ret; + + pr_info("%s\n", __func__); + + config_group_init(&of_cfs_subsys.su_group); + config_group_init_type_name(&of_cfs_overlay_group, "overlays", + &overlays_type); + configfs_add_default_group(&of_cfs_overlay_group, + &of_cfs_subsys.su_group); + + ret = configfs_register_subsystem(&of_cfs_subsys); + if (ret != 0) { + pr_err("%s: failed to register subsys\n", __func__); + goto out; + } + pr_info("%s: OK\n", __func__); +out: + return ret; +} +late_initcall(of_cfs_init); diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c index f358d2c807540e..e1353d7a58e6ea 100644 --- a/drivers/of/fdt_address.c +++ b/drivers/of/fdt_address.c @@ -147,7 +147,7 @@ static int __init fdt_translate_one(const void *blob, int parent, * that can be mapped to a cpu physical address). This is not really specified * that way, but this is traditionally the way IBM at least do things */ -static u64 __init fdt_translate_address(const void *blob, int node_offset) +u64 __init fdt_translate_address(const void *blob, int node_offset) { int parent, len; const struct of_bus *bus, *pbus; From 4b9e6cd99056ad63f7549639fa3503f2723835df Mon Sep 17 00:00:00 2001 From: Patrick Yavitz Date: Mon, 23 Dec 2024 10:14:57 -0500 Subject: [PATCH 114/258] ARMBIAN: Enable creation of __symbols__ node Signed-off-by: Patrick Yavitz Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- scripts/Makefile.dtbs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/Makefile.dtbs b/scripts/Makefile.dtbs index c4e4663902844d..8c03bd8a9bc43c 100644 --- a/scripts/Makefile.dtbs +++ b/scripts/Makefile.dtbs @@ -111,6 +111,11 @@ else DTC_FLAGS += -Wunique_unit_address_if_enabled endif +ifeq ($(CONFIG_OF_OVERLAY),y) +# enable creation of __symbols__ node +DTC_FLAGS += -@ +endif + ifneq ($(findstring 2,$(KBUILD_EXTRA_WARN)),) DTC_FLAGS += -Wnode_name_chars_strict \ -Wproperty_name_chars_strict \ From 55420740773505df57a896eda06781c3b78e6ead Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Tue, 23 Jun 2015 01:26:52 -0500 Subject: [PATCH 115/258] BACKPORT: CLEARLINUX: i8042: decrease debug message level to info Author: Arjan van de Ven Signed-off-by: Miguel Bernal Marin Signed-off-by: Jose Carlos Venegas Munoz [ Mingcong Bai: Debatable, but I think it wouldn't hurt since they were meant for debugging anyway - plus it eliminates 1-2 meaningless errors on LoongArch platforms. ] [ Mingcong Bai: Fixed a minor merge conflict in drivers/input/serio/i8042.c ] Link: https://github.com/clearlinux-pkgs/linux-lts2020/blob/a391d18e5991540961542376a19ff45ac91143e3/0101-i8042-decrease-debug-message-level-to-info.patch Signed-off-by: Mingcong Bai --- drivers/input/serio/i8042.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index c7638b3efc0e7f..316e9506f26171 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -641,7 +641,7 @@ static int i8042_enable_kbd_port(void) if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { i8042_ctr &= ~I8042_CTR_KBDINT; i8042_ctr |= I8042_CTR_KBDDIS; - pr_err("Failed to enable KBD port\n"); + pr_info("Failed to enable KBD port\n"); return -EIO; } @@ -660,7 +660,7 @@ static int i8042_enable_aux_port(void) if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { i8042_ctr &= ~I8042_CTR_AUXINT; i8042_ctr |= I8042_CTR_AUXDIS; - pr_err("Failed to enable AUX port\n"); + pr_info("Failed to enable AUX port\n"); return -EIO; } @@ -752,7 +752,7 @@ static int i8042_check_mux(void) i8042_ctr &= ~I8042_CTR_AUXINT; if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) { - pr_err("Failed to disable AUX port, can't use MUX\n"); + pr_info("Failed to disable AUX port, can't use MUX\n"); return -EIO; } @@ -971,7 +971,7 @@ static int i8042_controller_selftest(void) ret = i8042_command(¶m, I8042_CMD_CTL_TEST); if (ret) - pr_err("i8042 controller selftest timeout (%d/5)\n", i); + pr_info("i8042 controller selftest timeout (%d/5)\n", i); else if (param == I8042_RET_CTL_TEST) return 0; else @@ -994,7 +994,7 @@ static int i8042_controller_selftest(void) pr_info("giving up on controller selftest, continuing anyway...\n"); return 0; #else - pr_err("i8042 controller selftest failed\n"); + pr_info("i8042 controller selftest failed\n"); return -EIO; #endif } From b7da746ddb97018095106b9a495d16aaf47a8e9e Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 18 Oct 2020 16:42:44 +0900 Subject: [PATCH 116/258] SURFACE: (surface3-oemb) add DMI matches for Surface 3 with broken DMI table On some Surface 3, the DMI table gets corrupted for unknown reasons and breaks existing DMI matching used for device-specific quirks. This commit adds the (broken) DMI data into dmi_system_id tables used for quirks so that each driver can enable quirks even on the affected systems. On affected systems, DMI data will look like this: $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ chassis_vendor,product_name,sys_vendor} /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. /sys/devices/virtual/dmi/id/board_name:OEMB /sys/devices/virtual/dmi/id/board_vendor:OEMB /sys/devices/virtual/dmi/id/chassis_vendor:OEMB /sys/devices/virtual/dmi/id/product_name:OEMB /sys/devices/virtual/dmi/id/sys_vendor:OEMB Expected: $ grep . /sys/devices/virtual/dmi/id/{bios_vendor,board_name,board_vendor,\ chassis_vendor,product_name,sys_vendor} /sys/devices/virtual/dmi/id/bios_vendor:American Megatrends Inc. /sys/devices/virtual/dmi/id/board_name:Surface 3 /sys/devices/virtual/dmi/id/board_vendor:Microsoft Corporation /sys/devices/virtual/dmi/id/chassis_vendor:Microsoft Corporation /sys/devices/virtual/dmi/id/product_name:Surface 3 /sys/devices/virtual/dmi/id/sys_vendor:Microsoft Corporation Signed-off-by: Tsuchiya Yuto Patchset: surface3 Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/surface/surface3-wmi.c | 7 +++++++ sound/soc/codecs/rt5645.c | 9 +++++++++ sound/soc/intel/common/soc-acpi-intel-cht-match.c | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/platform/surface/surface3-wmi.c b/drivers/platform/surface/surface3-wmi.c index 6c8fb7a4dde444..22797a53f4d8f1 100644 --- a/drivers/platform/surface/surface3-wmi.c +++ b/drivers/platform/surface/surface3-wmi.c @@ -37,6 +37,13 @@ static const struct dmi_system_id surface3_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), }, }, + { + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "OEMB"), + DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"), + }, + }, #endif { } }; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index f7701b8d0d3c7e..0708c2fe8f6102 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3793,6 +3793,15 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&intel_braswell_platform_data, }, + { + .ident = "Microsoft Surface 3", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "OEMB"), + DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"), + }, + .driver_data = (void *)&intel_braswell_platform_data, + }, { /* * Match for the GPDwin which unfortunately uses somewhat diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c index e4c3492a0c2824..0b930c91bccb33 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cht-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -27,6 +27,14 @@ static const struct dmi_system_id cht_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), }, }, + { + .callback = cht_surface_quirk_cb, + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."), + DMI_MATCH(DMI_SYS_VENDOR, "OEMB"), + DMI_MATCH(DMI_PRODUCT_NAME, "OEMB"), + }, + }, { } }; From c9f29d0f4fd2071e780e01d45bb2f8864bc157cd Mon Sep 17 00:00:00 2001 From: kitakar5525 <34676735+kitakar5525@users.noreply.github.com> Date: Fri, 6 Dec 2019 23:10:30 +0900 Subject: [PATCH 117/258] SURFACE: surface3-spi: workaround: disable DMA mode to avoid crash by default On Arch Linux kernel at least after 4.19, touch input is broken after suspend (s2idle). kern :err : [ +0.203408] Surface3-spi spi-MSHW0037:00: SPI transfer timed out On recent stable Arch Linux kernel (at least after 5.1), touch input will crash after the first touch. kern :err : [ +0.203592] Surface3-spi spi-MSHW0037:00: SPI transfer timed out kern :err : [ +0.000173] spi_master spi1: failed to transfer one message from queue I found on an affected system (Arch Linux kernel, etc.), the touchscreen driver uses DMA mode by default. Then, we found some kernels with different kernel config (5.1 kernel config from Jakeday [1] or Chromium OS kernel chromeos-4.19 [2]) will use PIO mode by default and no such issues there. So, this commit disables DMA mode on the touchscreen driver side as a quick workaround to avoid touch input crash. We may need to properly set up DMA mode to use the touchscreen driver with DMA mode. You can still switch DMA/PIO mode if you want: switch to DMA mode (maybe broken) echo 1 | sudo tee /sys/module/surface3_spi/parameters/use_dma back to PIO mode echo 0 | sudo tee /sys/module/surface3_spi/parameters/use_dma Link to issue: https://github.com/jakeday/linux-surface/issues/596 References: [1] https://github.com/jakeday/linux-surface/blob/master/configs/5.1/config [2] https://chromium.googlesource.com/chromiumos/third_party/kernel/+/refs/heads/chromeos-4.19 Tested on Arch Linux 5.4.1 with Surface 3, which will use DMA by default. This commit made the driver use PIO by default and no touch input crash. Also tested on chromeos-4.19 4.19.90 with Surface 3, which will use PIO by default even without this commit. After this commit, it still uses PIO and confirmed no functional changes regarding touch input. More details: We can confirm which mode the touchscreen driver uses; first, enable debug output: echo "file drivers/spi/spi-pxa2xx.c +p" | sudo tee /sys/kernel/debug/dynamic_debug/control echo "file drivers/input/touchscreen/surface3_spi.c +p" | sudo tee /sys/kernel/debug/dynamic_debug/control Then, try to make a touch input and see dmesg log (On Arch Linux kernel, uses DMA) kern :debug : [ +0.006383] Surface3-spi spi-MSHW0037:00: 7692307 Hz actual, DMA kern :debug : [ +0.000495] Surface3-spi spi-MSHW0037:00: surface3_spi_irq_handler received -> ff ff ff ff a5 5a e7 7e 01 d2 00 80 01 03 03 18 00 e4 01 00 04 1a 04 1a e3 0c e3 0c b0 00 c5 00 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff (On the kernels I referenced above, uses PIO) kern :debug : [ +0.009260] Surface3-spi spi-MSHW0037:00: 7692307 Hz actual, PIO kern :debug : [ +0.001105] Surface3-spi spi-MSHW0037:00: surface3_spi_irq_handler received -> ff ff ff ff a5 5a e7 7e 01 d2 00 80 01 03 03 24 00 e4 01 00 58 0b 58 0b 83 12 83 12 26 01 95 01 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff Note (2025-03-08): This patch was originally dropped due to the comments in [3]. However, according to the commments in [4] it is still required. [3]: https://github.com/linux-surface/kernel/commit/a3421c12bed0e46c28518bcb8c6b22f237c6dc7a [4]: https://github.com/linux-surface/linux-surface/issues/1184 Patchset: surface3 Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/input/touchscreen/surface3_spi.c | 26 ++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/input/touchscreen/surface3_spi.c b/drivers/input/touchscreen/surface3_spi.c index 6074b7730e8621..6aa3e1d6f160a8 100644 --- a/drivers/input/touchscreen/surface3_spi.c +++ b/drivers/input/touchscreen/surface3_spi.c @@ -25,6 +25,12 @@ #define SURFACE3_REPORT_TOUCH 0xd2 #define SURFACE3_REPORT_PEN 0x16 +bool use_dma = false; +module_param(use_dma, bool, 0644); +MODULE_PARM_DESC(use_dma, + "Disable DMA mode if you encounter touch input crash. " + "(default: false, disabled to avoid crash)"); + struct surface3_ts_data { struct spi_device *spi; struct gpio_desc *gpiod_rst[2]; @@ -317,6 +323,13 @@ static int surface3_spi_create_pen_input(struct surface3_ts_data *data) return 0; } +static bool surface3_spi_can_dma(struct spi_controller *ctlr, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + return use_dma; +} + static int surface3_spi_probe(struct spi_device *spi) { struct surface3_ts_data *data; @@ -359,6 +372,19 @@ static int surface3_spi_probe(struct spi_device *spi) if (error) return error; + /* + * Set up DMA + * + * TODO: Currently, touch input with DMA seems to be broken. + * On 4.19 LTS, touch input will crash after suspend. + * On recent stable kernel (at least after 5.1), touch input will crash after + * the first touch. No problem with PIO on those kernels. + * Maybe we need to configure DMA here. + * + * Link to issue: https://github.com/jakeday/linux-surface/issues/596 + */ + spi->controller->can_dma = surface3_spi_can_dma; + return 0; } From e0cff5298fdb238456e5b8844d27b17bfce1f4a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Tue, 3 Nov 2020 13:28:04 +0100 Subject: [PATCH 118/258] SURFACE: mwifiex: Add quirk resetting the PCI bridge on MS Surface devices The most recent firmware of the 88W8897 card reports a hardcoded LTR value to the system during initialization, probably as an (unsuccessful) attempt of the developers to fix firmware crashes. This LTR value prevents most of the Microsoft Surface devices from entering deep powersaving states (either platform C-State 10 or S0ix state), because the exit latency of that state would be higher than what the card can tolerate. Turns out the card works just the same (including the firmware crashes) no matter if that hardcoded LTR value is reported or not, so it's kind of useless and only prevents us from saving power. To get rid of those hardcoded LTR reports, it's possible to reset the PCI bridge device after initializing the cards firmware. I'm not exactly sure why that works, maybe the power management subsystem of the PCH resets its stored LTR values when doing a function level reset of the bridge device. Doing the reset once after starting the wifi firmware works very well, probably because the firmware only reports that LTR value a single time during firmware startup. Patchset: mwifiex Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/net/wireless/marvell/mwifiex/pcie.c | 12 +++++++++ .../wireless/marvell/mwifiex/pcie_quirks.c | 26 +++++++++++++------ .../wireless/marvell/mwifiex/pcie_quirks.h | 1 + 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index a760de191fce73..db9b203226a89a 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -1702,9 +1702,21 @@ mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) static void mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter) { struct pcie_service_card *card = adapter->card; + struct pci_dev *pdev = card->dev; + struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask; + /* Trigger a function level reset of the PCI bridge device, this makes + * the firmware of PCIe 88W8897 cards stop reporting a fixed LTR value + * that prevents the system from entering package C10 and S0ix powersaving + * states. + * We need to do it here because it must happen after firmware + * initialization and this function is called after that is done. + */ + if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE) + pci_reset_function(parent_pdev); + /* Write the RX ring read pointer in to reg->rx_rdptr */ mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | tx_wrap); } diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c index dd6d21f1dbfd77..f46b06f8d64353 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c @@ -13,7 +13,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Pro 5", @@ -22,7 +23,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Pro 5 (LTE)", @@ -31,7 +33,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Pro 6", @@ -39,7 +42,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Book 1", @@ -47,7 +51,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Book 2", @@ -55,7 +60,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Laptop 1", @@ -63,7 +69,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, { .ident = "Surface Laptop 2", @@ -71,7 +78,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), }, - .driver_data = (void *)QUIRK_FW_RST_D3COLD, + .driver_data = (void *)(QUIRK_FW_RST_D3COLD | + QUIRK_DO_FLR_ON_BRIDGE), }, {} }; @@ -89,6 +97,8 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) dev_info(&pdev->dev, "no quirks enabled\n"); if (card->quirks & QUIRK_FW_RST_D3COLD) dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); + if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE) + dev_info(&pdev->dev, "quirk do_flr_on_bridge enabled\n"); } static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h index d6ff964aec5bfd..5d30ae39d65ec6 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h @@ -4,6 +4,7 @@ #include "pcie.h" #define QUIRK_FW_RST_D3COLD BIT(0) +#define QUIRK_DO_FLR_ON_BRIDGE BIT(1) void mwifiex_initialize_quirks(struct pcie_service_card *card); int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); From 029e1698c354ffaaa7d830fa4f155e8f737dc032 Mon Sep 17 00:00:00 2001 From: Tsuchiya Yuto Date: Sun, 4 Oct 2020 00:11:49 +0900 Subject: [PATCH 119/258] SURFACE: mwifiex: pcie: disable bridge_d3 for Surface gen4+ Currently, mwifiex fw will crash after suspend on recent kernel series. On Windows, it seems that the root port of wifi will never enter D3 state (stay on D0 state). And on Linux, disabling the D3 state for the bridge fixes fw crashing after suspend. This commit disables the D3 state of root port on driver initialization and fixes fw crashing after suspend. Signed-off-by: Tsuchiya Yuto Patchset: mwifiex Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/net/wireless/marvell/mwifiex/pcie.c | 7 +++++ .../wireless/marvell/mwifiex/pcie_quirks.c | 27 +++++++++++++------ .../wireless/marvell/mwifiex/pcie_quirks.h | 1 + 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index db9b203226a89a..ffd0c1fe922399 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -377,6 +377,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct pcie_service_card *card; + struct pci_dev *parent_pdev = pci_upstream_bridge(pdev); int ret; pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", @@ -418,6 +419,12 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, return -1; } + /* disable bridge_d3 for Surface gen4+ devices to fix fw crashing + * after suspend + */ + if (card->quirks & QUIRK_NO_BRIDGE_D3) + parent_pdev->bridge_d3 = false; + return 0; } diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c index f46b06f8d64353..99b024ecbadeaf 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c @@ -14,7 +14,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 4"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Pro 5", @@ -24,7 +25,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1796"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Pro 5 (LTE)", @@ -34,7 +36,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_1807"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Pro 6", @@ -43,7 +46,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Pro 6"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Book 1", @@ -52,7 +56,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Book 2", @@ -61,7 +66,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Book 2"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Laptop 1", @@ -70,7 +76,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, { .ident = "Surface Laptop 2", @@ -79,7 +86,8 @@ static const struct dmi_system_id mwifiex_quirk_table[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 2"), }, .driver_data = (void *)(QUIRK_FW_RST_D3COLD | - QUIRK_DO_FLR_ON_BRIDGE), + QUIRK_DO_FLR_ON_BRIDGE | + QUIRK_NO_BRIDGE_D3), }, {} }; @@ -99,6 +107,9 @@ void mwifiex_initialize_quirks(struct pcie_service_card *card) dev_info(&pdev->dev, "quirk reset_d3cold enabled\n"); if (card->quirks & QUIRK_DO_FLR_ON_BRIDGE) dev_info(&pdev->dev, "quirk do_flr_on_bridge enabled\n"); + if (card->quirks & QUIRK_NO_BRIDGE_D3) + dev_info(&pdev->dev, + "quirk no_brigde_d3 enabled\n"); } static void mwifiex_pcie_set_power_d3cold(struct pci_dev *pdev) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h index 5d30ae39d65ec6..c14eb56eb9118b 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h @@ -5,6 +5,7 @@ #define QUIRK_FW_RST_D3COLD BIT(0) #define QUIRK_DO_FLR_ON_BRIDGE BIT(1) +#define QUIRK_NO_BRIDGE_D3 BIT(2) void mwifiex_initialize_quirks(struct pcie_service_card *card); int mwifiex_pcie_reset_d3cold_quirk(struct pci_dev *pdev); From 82117fa19a160656ca3446fe0a7b5d7ec453b204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Thu, 25 Mar 2021 11:33:02 +0100 Subject: [PATCH 120/258] SURFACE: Bluetooth: btusb: Lower passive lescan interval on Marvell 88W8897 The Marvell 88W8897 combined wifi and bluetooth card (pcie+usb version) is used in a lot of Microsoft Surface devices, and all those devices suffer from very low 2.4GHz wifi connection speeds while bluetooth is enabled. The reason for that is that the default passive scanning interval for Bluetooth Low Energy devices is quite high in Linux (interval of 60 msec and scan window of 30 msec, see hci_core.c), and the Marvell chip is known for its bad bt+wifi coexisting performance. So decrease that passive scan interval and make the scan window shorter on this particular device to allow for spending more time transmitting wifi signals: The new scan interval is 250 msec (0x190 * 0.625 msec) and the new scan window is 6.25 msec (0xa * 0,625 msec). This change has a very large impact on the 2.4GHz wifi speeds and gets it up to performance comparable with the Windows driver, which seems to apply a similar quirk. The interval and window length were tested and found to work very well with a lot of Bluetooth Low Energy devices, including the Surface Pen, a Bluetooth Speaker and two modern Bluetooth headphones. All devices were discovered immediately after turning them on. Even lower values were also tested, but they introduced longer delays until devices get discovered. Patchset: mwifiex Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/bluetooth/btusb.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 7f66e7c6e77ad4..37d0b4c9202769 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -67,6 +67,7 @@ static struct usb_driver btusb_driver; #define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26) #define BTUSB_ACTIONS_SEMI BIT(27) #define BTUSB_BARROT BIT(28) +#define BTUSB_LOWER_LESCAN_INTERVAL BIT(29) static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -472,6 +473,7 @@ static const struct usb_device_id quirks_table[] = { { USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL }, { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL }, { USB_DEVICE(0x1286, 0x204e), .driver_info = BTUSB_MARVELL }, + { USB_DEVICE(0x1286, 0x204c), .driver_info = BTUSB_LOWER_LESCAN_INTERVAL }, /* Intel Bluetooth devices */ { USB_DEVICE(0x8087, 0x0025), .driver_info = BTUSB_INTEL_COMBINED }, @@ -4250,6 +4252,19 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_MARVELL) hdev->set_bdaddr = btusb_set_bdaddr_marvell; + /* The Marvell 88W8897 combined wifi and bluetooth card is known for + * very bad bt+wifi coexisting performance. + * + * Decrease the passive BT Low Energy scan interval a bit + * (0x0190 * 0.625 msec = 250 msec) and make the scan window shorter + * (0x000a * 0,625 msec = 6.25 msec). This allows for significantly + * higher wifi throughput while passively scanning for BT LE devices. + */ + if (id->driver_info & BTUSB_LOWER_LESCAN_INTERVAL) { + hdev->le_scan_interval = 0x0190; + hdev->le_scan_window = 0x000a; + } + if (IS_ENABLED(CONFIG_BT_HCIBTUSB_MTK) && (id->driver_info & BTUSB_MEDIATEK)) { hdev->setup = btusb_mtk_setup; From 8191da8453e3babdb059321b2384efcfa8c03032 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 27 Feb 2021 00:45:52 +0100 Subject: [PATCH 121/258] SURFACE: ath10k: Add module parameters to override board files Some Surface devices, specifically the Surface Go and AMD version of the Surface Laptop 3 (wich both come with QCA6174 WiFi chips), work better with a different board file, as it seems that the firmeware included upstream is buggy. As it is generally not a good idea to randomly overwrite files, let alone doing so via packages, we add module parameters to override those file names in the driver. This allows us to package/deploy the override via a modprobe.d config. Signed-off-by: Maximilian Luz Patchset: ath10k Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/net/wireless/ath/ath10k/core.c | 57 ++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7c2939cbde5f05..eb402a3a8c5f88 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -41,6 +41,9 @@ static bool fw_diag_log; /* frame mode values are mapped as per enum ath10k_hw_txrx_mode */ unsigned int ath10k_frame_mode = ATH10K_HW_TXRX_NATIVE_WIFI; +static char *override_board = ""; +static char *override_board2 = ""; + unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) | BIT(ATH10K_FW_CRASH_DUMP_CE_DATA); @@ -53,6 +56,9 @@ module_param(fw_diag_log, bool, 0644); module_param_named(frame_mode, ath10k_frame_mode, uint, 0644); module_param_named(coredump_mask, ath10k_coredump_mask, ulong, 0444); +module_param(override_board, charp, 0644); +module_param(override_board2, charp, 0644); + MODULE_PARM_DESC(debug_mask, "Debugging mask"); MODULE_PARM_DESC(uart_print, "Uart target debugging"); MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); @@ -62,6 +68,9 @@ MODULE_PARM_DESC(frame_mode, MODULE_PARM_DESC(coredump_mask, "Bitfield of what to include in firmware crash file"); MODULE_PARM_DESC(fw_diag_log, "Diag based fw log debugging"); +MODULE_PARM_DESC(override_board, "Override for board.bin file"); +MODULE_PARM_DESC(override_board2, "Override for board-2.bin file"); + static const struct ath10k_hw_params ath10k_hw_params_list[] = { { .id = QCA988X_HW_2_0_VERSION, @@ -932,6 +941,42 @@ static int ath10k_init_configure_target(struct ath10k *ar) return 0; } +static const char *ath10k_override_board_fw_file(struct ath10k *ar, + const char *file) +{ + if (strcmp(file, "board.bin") == 0) { + if (strcmp(override_board, "") == 0) + return file; + + if (strcmp(override_board, "none") == 0) { + dev_info(ar->dev, "firmware override: pretending 'board.bin' does not exist\n"); + return NULL; + } + + dev_info(ar->dev, "firmware override: replacing 'board.bin' with '%s'\n", + override_board); + + return override_board; + } + + if (strcmp(file, "board-2.bin") == 0) { + if (strcmp(override_board2, "") == 0) + return file; + + if (strcmp(override_board2, "none") == 0) { + dev_info(ar->dev, "firmware override: pretending 'board-2.bin' does not exist\n"); + return NULL; + } + + dev_info(ar->dev, "firmware override: replacing 'board-2.bin' with '%s'\n", + override_board2); + + return override_board2; + } + + return file; +} + static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, const char *dir, const char *file) @@ -946,6 +991,18 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, if (dir == NULL) dir = "."; + /* HACK: Override board.bin and board-2.bin files if specified. + * + * Some Surface devices perform better with a different board + * configuration. To this end, one would need to replace the board.bin + * file with the modified config and remove the board-2.bin file. + * Unfortunately, that's not a solution that we can easily package. So + * we add module options to perform these overrides here. + */ + file = ath10k_override_board_fw_file(ar, file); + if (!file) + return ERR_PTR(-ENOENT); + if (ar->board_name) { snprintf(filename, sizeof(filename), "%s/%s/%s", dir, ar->board_name, file); From 41b43adadb17ccb9cc62351f00a4e5eb602a37fc Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Thu, 30 Jul 2020 13:21:53 +0200 Subject: [PATCH 122/258] SURFACE: mei: me: Add Icelake device ID for iTouch Signed-off-by: Dorian Stoll Patchset: ipts Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/misc/mei/hw-me-regs.h | 4 ++++ drivers/misc/mei/pci-me.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index f145e8e36cb3d0..0436998e8126d1 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -94,6 +94,10 @@ #define PCI_DEVICE_ID_INTEL_MEI_ICP_LP 0x34E0 /* Ice Lake Point LP */ #define PCI_DEVICE_ID_INTEL_MEI_ICP_N 0x38E0 /* Ice Lake Point N */ +#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ +#define MEI_DEV_ID_ICP_LP_3 0x34E4 /* Ice Lake Point LP 3 (iTouch) */ +#define MEI_DEV_ID_ICP_N 0x38E0 /* Ice Lake Point N */ + #define PCI_DEVICE_ID_INTEL_MEI_JSP_N 0x4DE0 /* Jasper Lake Point N */ #define PCI_DEVICE_ID_INTEL_MEI_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 9efeafa8f1cae9..d97945f5b8683b 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -100,6 +100,10 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP_3, MEI_ME_PCH12_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)}, From 117234189efb21c34c12fbc994a35287be98ba37 Mon Sep 17 00:00:00 2001 From: Liban Hannan Date: Tue, 12 Apr 2022 23:31:12 +0100 Subject: [PATCH 123/258] BACKPORT: SURFACE: iommu: Use IOMMU passthrough mode for IPTS Adds a quirk so that IOMMU uses passthrough mode for the IPTS device. Otherwise, when IOMMU is enabled, IPTS produces DMAR errors like: DMAR: [DMA Read NO_PASID] Request device [00:16.4] fault addr 0x104ea3000 [fault reason 0x06] PTE Read access is not set This is very similar to the bug described at: https://bugs.launchpad.net/bugs/1958004 Fixed with the following patch which this patch basically copies: https://launchpadlibrarian.net/586396847/43255ca.diff Signed-off-by: Dorian Stoll Patchset: ipts [ Mingcong Bai: Resolved a minor merge conflict in drivers/iommu/intel/iommu.c ] Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/iommu/intel/iommu.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/iommu/intel/iommu.c b/drivers/iommu/intel/iommu.c index 3f74fe3a370474..43fbda2305823a 100644 --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -47,6 +47,11 @@ (pdev)->device == 0x1919)) #define IS_AZALIA(pdev) ((pdev)->vendor == 0x8086 && (pdev)->device == 0x3a3e) +#define IS_IPTS(pdev) ( \ + ((pdev)->vendor == PCI_VENDOR_ID_INTEL && (pdev)->device == 0x9D3E) || \ + ((pdev)->vendor == PCI_VENDOR_ID_INTEL && (pdev)->device == 0x34E4) \ + ) + #define IOAPIC_RANGE_START (0xfee00000) #define IOAPIC_RANGE_END (0xfeefffff) #define IOVA_START_ADDR (0x1000) @@ -214,6 +219,7 @@ int intel_iommu_enabled = 0; EXPORT_SYMBOL_GPL(intel_iommu_enabled); static int dmar_map_ipu = 1; +static int dmar_map_ipts = 1; static int intel_iommu_superpage = 1; static int iommu_identity_mapping; static int iommu_skip_te_disable; @@ -221,6 +227,7 @@ static int disable_igfx_iommu; #define IDENTMAP_AZALIA 4 #define IDENTMAP_IPU 8 +#define IDENTMAP_IPTS 16 const struct iommu_ops intel_iommu_ops; @@ -1414,6 +1421,9 @@ static int device_def_domain_type(struct device *dev) if ((iommu_identity_mapping & IDENTMAP_IPU) && IS_INTEL_IPU(pdev)) return IOMMU_DOMAIN_IDENTITY; + + if ((iommu_identity_mapping & IDENTMAP_IPTS) && IS_IPTS(pdev)) + return IOMMU_DOMAIN_IDENTITY; } return 0; @@ -1707,6 +1717,9 @@ static int __init init_dmars(void) if (!dmar_map_ipu) iommu_identity_mapping |= IDENTMAP_IPU; + if (!dmar_map_ipts) + iommu_identity_mapping |= IDENTMAP_IPTS; + check_tylersburg_isoch(); /* @@ -4067,6 +4080,22 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9B41, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BCA, quirk_iommu_igfx); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9BCC, quirk_iommu_igfx); +static void quirk_iommu_ipts(struct pci_dev *dev) +{ + if (!IS_IPTS(dev)) + return; + + if (risky_device(dev)) + return; + + pci_info(dev, "Disabling IOMMU for IPTS\n"); + dmar_map_ipts = 0; +} + +/* disable IPTS dmar support */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9D3E, quirk_iommu_ipts); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x34E4, quirk_iommu_ipts); + static void quirk_iommu_ipu(struct pci_dev *dev) { if (!IS_INTEL_IPU(dev)) From c0256c6be0f0a2a9e93fed162fd2b7c8df8b9a52 Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Sun, 11 Dec 2022 12:00:59 +0100 Subject: [PATCH 124/258] SURFACE: hid: Add support for Intel Precise Touch and Stylus Based on linux-surface/intel-precise-touch@8abe268 Signed-off-by: Dorian Stoll Patchset: ipts Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 2 + drivers/hid/ipts/Kconfig | 14 + drivers/hid/ipts/Makefile | 16 ++ drivers/hid/ipts/cmd.c | 61 +++++ drivers/hid/ipts/cmd.h | 60 ++++ drivers/hid/ipts/context.h | 52 ++++ drivers/hid/ipts/control.c | 486 +++++++++++++++++++++++++++++++++ drivers/hid/ipts/control.h | 126 +++++++++ drivers/hid/ipts/desc.h | 80 ++++++ drivers/hid/ipts/eds1.c | 104 +++++++ drivers/hid/ipts/eds1.h | 35 +++ drivers/hid/ipts/eds2.c | 145 ++++++++++ drivers/hid/ipts/eds2.h | 35 +++ drivers/hid/ipts/hid.c | 225 +++++++++++++++ drivers/hid/ipts/hid.h | 24 ++ drivers/hid/ipts/main.c | 126 +++++++++ drivers/hid/ipts/mei.c | 188 +++++++++++++ drivers/hid/ipts/mei.h | 66 +++++ drivers/hid/ipts/receiver.c | 251 +++++++++++++++++ drivers/hid/ipts/receiver.h | 16 ++ drivers/hid/ipts/resources.c | 131 +++++++++ drivers/hid/ipts/resources.h | 41 +++ drivers/hid/ipts/spec-data.h | 100 +++++++ drivers/hid/ipts/spec-device.h | 290 ++++++++++++++++++++ drivers/hid/ipts/spec-hid.h | 34 +++ drivers/hid/ipts/thread.c | 84 ++++++ drivers/hid/ipts/thread.h | 59 ++++ 28 files changed, 2853 insertions(+) create mode 100644 drivers/hid/ipts/Kconfig create mode 100644 drivers/hid/ipts/Makefile create mode 100644 drivers/hid/ipts/cmd.c create mode 100644 drivers/hid/ipts/cmd.h create mode 100644 drivers/hid/ipts/context.h create mode 100644 drivers/hid/ipts/control.c create mode 100644 drivers/hid/ipts/control.h create mode 100644 drivers/hid/ipts/desc.h create mode 100644 drivers/hid/ipts/eds1.c create mode 100644 drivers/hid/ipts/eds1.h create mode 100644 drivers/hid/ipts/eds2.c create mode 100644 drivers/hid/ipts/eds2.h create mode 100644 drivers/hid/ipts/hid.c create mode 100644 drivers/hid/ipts/hid.h create mode 100644 drivers/hid/ipts/main.c create mode 100644 drivers/hid/ipts/mei.c create mode 100644 drivers/hid/ipts/mei.h create mode 100644 drivers/hid/ipts/receiver.c create mode 100644 drivers/hid/ipts/receiver.h create mode 100644 drivers/hid/ipts/resources.c create mode 100644 drivers/hid/ipts/resources.h create mode 100644 drivers/hid/ipts/spec-data.h create mode 100644 drivers/hid/ipts/spec-device.h create mode 100644 drivers/hid/ipts/spec-hid.h create mode 100644 drivers/hid/ipts/thread.c create mode 100644 drivers/hid/ipts/thread.h diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index ff2f580b660ba6..cecb264a229f99 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1466,6 +1466,8 @@ source "drivers/hid/surface-hid/Kconfig" source "drivers/hid/intel-thc-hid/Kconfig" +source "drivers/hid/ipts/Kconfig" + endif # HID # USB support may be used with HID disabled diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 0597fd6a4ffd32..b46dec8ad0bfbb 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -180,3 +180,5 @@ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/ + +obj-$(CONFIG_HID_IPTS) += ipts/ diff --git a/drivers/hid/ipts/Kconfig b/drivers/hid/ipts/Kconfig new file mode 100644 index 00000000000000..297401bd388dd9 --- /dev/null +++ b/drivers/hid/ipts/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config HID_IPTS + tristate "Intel Precise Touch & Stylus" + depends on INTEL_MEI + depends on HID + help + Say Y here if your system has a touchscreen using Intels + Precise Touch & Stylus (IPTS) technology. + + If unsure say N. + + To compile this driver as a module, choose M here: the + module will be called ipts. diff --git a/drivers/hid/ipts/Makefile b/drivers/hid/ipts/Makefile new file mode 100644 index 00000000000000..883896f68e6ada --- /dev/null +++ b/drivers/hid/ipts/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for the IPTS touchscreen driver +# + +obj-$(CONFIG_HID_IPTS) += ipts.o +ipts-objs := cmd.o +ipts-objs += control.o +ipts-objs += eds1.o +ipts-objs += eds2.o +ipts-objs += hid.o +ipts-objs += main.o +ipts-objs += mei.o +ipts-objs += receiver.o +ipts-objs += resources.o +ipts-objs += thread.o diff --git a/drivers/hid/ipts/cmd.c b/drivers/hid/ipts/cmd.c new file mode 100644 index 00000000000000..63a4934bbc5fae --- /dev/null +++ b/drivers/hid/ipts/cmd.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include + +#include "cmd.h" +#include "context.h" +#include "mei.h" +#include "spec-device.h" + +int ipts_cmd_recv_timeout(struct ipts_context *ipts, enum ipts_command_code code, + struct ipts_response *rsp, u64 timeout) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (!rsp) + return -EFAULT; + + /* + * In a response, the command code will have the most significant bit flipped to 1. + * If code is passed to ipts_mei_recv as is, no messages will be received. + */ + ret = ipts_mei_recv(&ipts->mei, code | IPTS_RSP_BIT, rsp, timeout); + if (ret < 0) + return ret; + + dev_dbg(ipts->dev, "Received 0x%02X with status 0x%02X\n", code, rsp->status); + + /* + * Some devices will always return this error. + * It is allowed to ignore it and to try continuing. + */ + if (rsp->status == IPTS_STATUS_COMPAT_CHECK_FAIL) + rsp->status = IPTS_STATUS_SUCCESS; + + return 0; +} + +int ipts_cmd_send(struct ipts_context *ipts, enum ipts_command_code code, void *data, size_t size) +{ + struct ipts_command cmd = { 0 }; + + if (!ipts) + return -EFAULT; + + cmd.cmd = code; + + if (data && size > 0) + memcpy(cmd.payload, data, size); + + dev_dbg(ipts->dev, "Sending 0x%02X with %ld bytes payload\n", code, size); + return ipts_mei_send(&ipts->mei, &cmd, sizeof(cmd.cmd) + size); +} diff --git a/drivers/hid/ipts/cmd.h b/drivers/hid/ipts/cmd.h new file mode 100644 index 00000000000000..2b4079075b6428 --- /dev/null +++ b/drivers/hid/ipts/cmd.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_CMD_H +#define IPTS_CMD_H + +#include + +#include "context.h" +#include "spec-device.h" + +/* + * The default timeout for receiving responses + */ +#define IPTS_CMD_DEFAULT_TIMEOUT 1000 + +/** + * ipts_cmd_recv_timeout() - Receives a response to a command. + * @ipts: The IPTS driver context. + * @code: The type of the command / response. + * @rsp: The address that the received response will be copied to. + * @timeout: How many milliseconds the function will wait at most. + * + * A negative timeout means to wait forever. + * + * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received. + */ +int ipts_cmd_recv_timeout(struct ipts_context *ipts, enum ipts_command_code code, + struct ipts_response *rsp, u64 timeout); + +/** + * ipts_cmd_recv() - Receives a response to a command. + * @ipts: The IPTS driver context. + * @code: The type of the command / response. + * @rsp: The address that the received response will be copied to. + * + * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received. + */ +static inline int ipts_cmd_recv(struct ipts_context *ipts, enum ipts_command_code code, + struct ipts_response *rsp) +{ + return ipts_cmd_recv_timeout(ipts, code, rsp, IPTS_CMD_DEFAULT_TIMEOUT); +} + +/** + * ipts_cmd_send() - Executes a command on the device. + * @ipts: The IPTS driver context. + * @code: The type of the command to execute. + * @data: The payload containing parameters for the command. + * @size: The size of the payload. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_cmd_send(struct ipts_context *ipts, enum ipts_command_code code, void *data, size_t size); + +#endif /* IPTS_CMD_H */ diff --git a/drivers/hid/ipts/context.h b/drivers/hid/ipts/context.h new file mode 100644 index 00000000000000..ba33259f1f7c5e --- /dev/null +++ b/drivers/hid/ipts/context.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_CONTEXT_H +#define IPTS_CONTEXT_H + +#include +#include +#include +#include +#include +#include +#include + +#include "mei.h" +#include "resources.h" +#include "spec-device.h" +#include "thread.h" + +struct ipts_context { + struct device *dev; + struct ipts_mei mei; + + enum ipts_mode mode; + + /* + * Prevents concurrent GET_FEATURE reports. + */ + struct mutex feature_lock; + struct completion feature_event; + + /* + * These are not inside of struct ipts_resources + * because they don't own the memory they point to. + */ + struct ipts_buffer feature_report; + struct ipts_buffer descriptor; + + bool hid_active; + struct hid_device *hid; + + struct ipts_device_info info; + struct ipts_resources resources; + + struct ipts_thread receiver_loop; +}; + +#endif /* IPTS_CONTEXT_H */ diff --git a/drivers/hid/ipts/control.c b/drivers/hid/ipts/control.c new file mode 100644 index 00000000000000..5360842d260bae --- /dev/null +++ b/drivers/hid/ipts/control.c @@ -0,0 +1,486 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "context.h" +#include "control.h" +#include "desc.h" +#include "hid.h" +#include "receiver.h" +#include "resources.h" +#include "spec-data.h" +#include "spec-device.h" + +static int ipts_control_get_device_info(struct ipts_context *ipts, struct ipts_device_info *info) +{ + int ret = 0; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + if (!info) + return -EFAULT; + + ret = ipts_cmd_send(ipts, IPTS_CMD_GET_DEVICE_INFO, NULL, 0); + if (ret) { + dev_err(ipts->dev, "GET_DEVICE_INFO: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_GET_DEVICE_INFO, &rsp); + if (ret) { + dev_err(ipts->dev, "GET_DEVICE_INFO: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "GET_DEVICE_INFO: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + memcpy(info, rsp.payload, sizeof(*info)); + return 0; +} + +static int ipts_control_set_mode(struct ipts_context *ipts, enum ipts_mode mode) +{ + int ret = 0; + struct ipts_set_mode cmd = { 0 }; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + cmd.mode = mode; + + ret = ipts_cmd_send(ipts, IPTS_CMD_SET_MODE, &cmd, sizeof(cmd)); + if (ret) { + dev_err(ipts->dev, "SET_MODE: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_SET_MODE, &rsp); + if (ret) { + dev_err(ipts->dev, "SET_MODE: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "SET_MODE: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +static int ipts_control_set_mem_window(struct ipts_context *ipts, struct ipts_resources *res) +{ + int i = 0; + int ret = 0; + struct ipts_mem_window cmd = { 0 }; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + if (!res) + return -EFAULT; + + for (i = 0; i < IPTS_BUFFERS; i++) { + cmd.data_addr_lower[i] = lower_32_bits(res->data[i].dma_address); + cmd.data_addr_upper[i] = upper_32_bits(res->data[i].dma_address); + cmd.feedback_addr_lower[i] = lower_32_bits(res->feedback[i].dma_address); + cmd.feedback_addr_upper[i] = upper_32_bits(res->feedback[i].dma_address); + } + + cmd.workqueue_addr_lower = lower_32_bits(res->workqueue.dma_address); + cmd.workqueue_addr_upper = upper_32_bits(res->workqueue.dma_address); + + cmd.doorbell_addr_lower = lower_32_bits(res->doorbell.dma_address); + cmd.doorbell_addr_upper = upper_32_bits(res->doorbell.dma_address); + + cmd.hid2me_addr_lower = lower_32_bits(res->hid2me.dma_address); + cmd.hid2me_addr_upper = upper_32_bits(res->hid2me.dma_address); + + cmd.workqueue_size = IPTS_WORKQUEUE_SIZE; + cmd.workqueue_item_size = IPTS_WORKQUEUE_ITEM_SIZE; + + ret = ipts_cmd_send(ipts, IPTS_CMD_SET_MEM_WINDOW, &cmd, sizeof(cmd)); + if (ret) { + dev_err(ipts->dev, "SET_MEM_WINDOW: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_SET_MEM_WINDOW, &rsp); + if (ret) { + dev_err(ipts->dev, "SET_MEM_WINDOW: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "SET_MEM_WINDOW: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +static int ipts_control_get_descriptor(struct ipts_context *ipts) +{ + int ret = 0; + struct ipts_data_header *header = NULL; + struct ipts_get_descriptor cmd = { 0 }; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + if (!ipts->resources.descriptor.address) + return -EFAULT; + + memset(ipts->resources.descriptor.address, 0, ipts->resources.descriptor.size); + + cmd.addr_lower = lower_32_bits(ipts->resources.descriptor.dma_address); + cmd.addr_upper = upper_32_bits(ipts->resources.descriptor.dma_address); + cmd.magic = 8; + + ret = ipts_cmd_send(ipts, IPTS_CMD_GET_DESCRIPTOR, &cmd, sizeof(cmd)); + if (ret) { + dev_err(ipts->dev, "GET_DESCRIPTOR: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_GET_DESCRIPTOR, &rsp); + if (ret) { + dev_err(ipts->dev, "GET_DESCRIPTOR: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "GET_DESCRIPTOR: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + header = (struct ipts_data_header *)ipts->resources.descriptor.address; + + if (header->type == IPTS_DATA_TYPE_DESCRIPTOR) { + ipts->descriptor.address = &header->data[8]; + ipts->descriptor.size = header->size - 8; + + return 0; + } + + return -ENODATA; +} + +int ipts_control_request_flush(struct ipts_context *ipts) +{ + int ret = 0; + struct ipts_quiesce_io cmd = { 0 }; + + if (!ipts) + return -EFAULT; + + ret = ipts_cmd_send(ipts, IPTS_CMD_QUIESCE_IO, &cmd, sizeof(cmd)); + if (ret) + dev_err(ipts->dev, "QUIESCE_IO: send failed: %d\n", ret); + + return ret; +} + +int ipts_control_wait_flush(struct ipts_context *ipts) +{ + int ret = 0; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + ret = ipts_cmd_recv(ipts, IPTS_CMD_QUIESCE_IO, &rsp); + if (ret) { + dev_err(ipts->dev, "QUIESCE_IO: recv failed: %d\n", ret); + return ret; + } + + if (rsp.status == IPTS_STATUS_TIMEOUT) + return -EAGAIN; + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "QUIESCE_IO: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +int ipts_control_request_data(struct ipts_context *ipts) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + ret = ipts_cmd_send(ipts, IPTS_CMD_READY_FOR_DATA, NULL, 0); + if (ret) + dev_err(ipts->dev, "READY_FOR_DATA: send failed: %d\n", ret); + + return ret; +} + +int ipts_control_wait_data(struct ipts_context *ipts, bool shutdown) +{ + int ret = 0; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + if (!shutdown) + ret = ipts_cmd_recv_timeout(ipts, IPTS_CMD_READY_FOR_DATA, &rsp, 0); + else + ret = ipts_cmd_recv(ipts, IPTS_CMD_READY_FOR_DATA, &rsp); + + if (ret) { + if (ret != -EAGAIN) + dev_err(ipts->dev, "READY_FOR_DATA: recv failed: %d\n", ret); + + return ret; + } + + /* + * During shutdown, it is possible that the sensor has already been disabled. + */ + if (rsp.status == IPTS_STATUS_SENSOR_DISABLED) + return 0; + + if (rsp.status == IPTS_STATUS_TIMEOUT) + return -EAGAIN; + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "READY_FOR_DATA: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer) +{ + int ret = 0; + struct ipts_feedback cmd = { 0 }; + struct ipts_response rsp = { 0 }; + + if (!ipts) + return -EFAULT; + + cmd.buffer = buffer; + + ret = ipts_cmd_send(ipts, IPTS_CMD_FEEDBACK, &cmd, sizeof(cmd)); + if (ret) { + dev_err(ipts->dev, "FEEDBACK: send failed: %d\n", ret); + return ret; + } + + ret = ipts_cmd_recv(ipts, IPTS_CMD_FEEDBACK, &rsp); + if (ret) { + dev_err(ipts->dev, "FEEDBACK: recv failed: %d\n", ret); + return ret; + } + + /* + * We don't know what feedback data looks like so we are sending zeros. + * See also ipts_control_refill_buffer. + */ + if (rsp.status == IPTS_STATUS_INVALID_PARAMS) + return 0; + + if (rsp.status != IPTS_STATUS_SUCCESS) { + dev_err(ipts->dev, "FEEDBACK: cmd failed: %d\n", rsp.status); + return -EBADR; + } + + return 0; +} + +int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd, + enum ipts_feedback_data_type type, void *data, size_t size) +{ + struct ipts_feedback_header *header = NULL; + + if (!ipts) + return -EFAULT; + + if (!ipts->resources.hid2me.address) + return -EFAULT; + + memset(ipts->resources.hid2me.address, 0, ipts->resources.hid2me.size); + header = (struct ipts_feedback_header *)ipts->resources.hid2me.address; + + header->cmd_type = cmd; + header->data_type = type; + header->size = size; + header->buffer = IPTS_HID2ME_BUFFER; + + if (size + sizeof(*header) > ipts->resources.hid2me.size) + return -EINVAL; + + if (data && size > 0) + memcpy(header->payload, data, size); + + return ipts_control_send_feedback(ipts, IPTS_HID2ME_BUFFER); +} + +int ipts_control_start(struct ipts_context *ipts) +{ + int ret = 0; + struct ipts_device_info info = { 0 }; + + if (!ipts) + return -EFAULT; + + dev_info(ipts->dev, "Starting IPTS\n"); + + ret = ipts_control_get_device_info(ipts, &info); + if (ret) { + dev_err(ipts->dev, "Failed to get device info: %d\n", ret); + return ret; + } + + ipts->info = info; + + ret = ipts_resources_init(&ipts->resources, ipts->dev, info.data_size, info.feedback_size); + if (ret) { + dev_err(ipts->dev, "Failed to allocate buffers: %d", ret); + return ret; + } + + dev_info(ipts->dev, "IPTS EDS Version: %d\n", info.intf_eds); + + /* + * Handle newer devices + */ + if (info.intf_eds > 1) { + /* + * Fetching the descriptor will only work on newer devices. + * For older devices, a fallback descriptor will be used. + */ + ret = ipts_control_get_descriptor(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to fetch HID descriptor: %d\n", ret); + return ret; + } + + /* + * Newer devices can be directly initialized in polling mode. + */ + ipts->mode = IPTS_MODE_POLL; + } + + ret = ipts_control_set_mode(ipts, ipts->mode); + if (ret) { + dev_err(ipts->dev, "Failed to set mode: %d\n", ret); + return ret; + } + + ret = ipts_control_set_mem_window(ipts, &ipts->resources); + if (ret) { + dev_err(ipts->dev, "Failed to set memory window: %d\n", ret); + return ret; + } + + ret = ipts_receiver_start(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to start receiver: %d\n", ret); + return ret; + } + + ret = ipts_control_request_data(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to request data: %d\n", ret); + return ret; + } + + ipts_hid_enable(ipts); + + ret = ipts_hid_init(ipts, info); + if (ret) { + dev_err(ipts->dev, "Failed to initialize HID device: %d\n", ret); + return ret; + } + + return 0; +} + +static int _ipts_control_stop(struct ipts_context *ipts) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + ipts_hid_disable(ipts); + dev_info(ipts->dev, "Stopping IPTS\n"); + + ret = ipts_receiver_stop(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to stop receiver: %d\n", ret); + return ret; + } + + ret = ipts_resources_free(&ipts->resources); + if (ret) { + dev_err(ipts->dev, "Failed to free resources: %d\n", ret); + return ret; + } + + return 0; +} + +int ipts_control_stop(struct ipts_context *ipts) +{ + int ret = 0; + + ret = _ipts_control_stop(ipts); + if (ret) + return ret; + + ret = ipts_hid_free(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to free HID device: %d\n", ret); + return ret; + } + + return 0; +} + +int ipts_control_restart(struct ipts_context *ipts) +{ + int ret = 0; + + ret = _ipts_control_stop(ipts); + if (ret) + return ret; + + /* + * Wait a second to give the sensor time to fully shut down. + */ + msleep(1000); + + ret = ipts_control_start(ipts); + if (ret) + return ret; + + return 0; +} diff --git a/drivers/hid/ipts/control.h b/drivers/hid/ipts/control.h new file mode 100644 index 00000000000000..26629c5144edb7 --- /dev/null +++ b/drivers/hid/ipts/control.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_CONTROL_H +#define IPTS_CONTROL_H + +#include + +#include "context.h" +#include "spec-data.h" +#include "spec-device.h" + +/** + * ipts_control_request_flush() - Stop the data flow. + * @ipts: The IPTS driver context. + * + * Runs the command to stop the data flow on the device. + * All outstanding data needs to be acknowledged using feedback before the command will return. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_request_flush(struct ipts_context *ipts); + +/** + * ipts_control_wait_flush() - Wait until data flow has been stopped. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_wait_flush(struct ipts_context *ipts); + +/** + * ipts_control_wait_flush() - Notify the device that the driver can receive new data. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_request_data(struct ipts_context *ipts); + +/** + * ipts_control_wait_data() - Wait until new data is available. + * @ipts: The IPTS driver context. + * @block: Whether to block execution until data is available. + * + * In poll mode, this function will never return while the data flow is active. Instead, + * the poll will be incremented when new data is available. + * + * Returns: 0 on success, <0 on error, -EAGAIN if no data is available. + */ +int ipts_control_wait_data(struct ipts_context *ipts, bool block); + +/** + * ipts_control_send_feedback() - Submits a feedback buffer to the device. + * @ipts: The IPTS driver context. + * @buffer: The ID of the buffer containing feedback data. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_send_feedback(struct ipts_context *ipts, u32 buffer); + +/** + * ipts_control_hid2me_feedback() - Sends HID2ME feedback, a special type of feedback. + * @ipts: The IPTS driver context. + * @cmd: The command that will be run on the device. + * @type: The type of the payload that is sent to the device. + * @data: The payload of the feedback command. + * @size: The size of the payload. + * + * HID2ME feedback is a special type of feedback, because it allows interfacing with + * the HID API of the device at any moment, without requiring a buffer that has to + * be acknowledged. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_hid2me_feedback(struct ipts_context *ipts, enum ipts_feedback_cmd_type cmd, + enum ipts_feedback_data_type type, void *data, size_t size); + +/** + * ipts_control_refill_buffer() - Acknowledges that data in a buffer has been processed. + * @ipts: The IPTS driver context. + * @buffer: The buffer that has been processed and can be refilled. + * + * Returns: 0 on success, <0 on error. + */ +static inline int ipts_control_refill_buffer(struct ipts_context *ipts, u32 buffer) +{ + /* + * IPTS expects structured data in the feedback buffer matching the buffer that will be + * refilled. We don't know what that data looks like, so we just keep the buffer empty. + * This results in an INVALID_PARAMS error, but the buffer gets refilled without an issue. + * Sending a minimal structure with the buffer ID fixes the error, but breaks refilling + * the buffers on some devices. + */ + + return ipts_control_send_feedback(ipts, buffer); +} + +/** + * ipts_control_start() - Initialized the device and starts the data flow. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_start(struct ipts_context *ipts); + +/** + * ipts_control_stop() - Stops the data flow and resets the device. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_stop(struct ipts_context *ipts); + +/** + * ipts_control_restart() - Stops the device and starts it again. + * @ipts: The IPTS driver context. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_control_restart(struct ipts_context *ipts); + +#endif /* IPTS_CONTROL_H */ diff --git a/drivers/hid/ipts/desc.h b/drivers/hid/ipts/desc.h new file mode 100644 index 00000000000000..307438c7c80cd7 --- /dev/null +++ b/drivers/hid/ipts/desc.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_DESC_H +#define IPTS_DESC_H + +#include + +#define IPTS_HID_REPORT_SINGLETOUCH 64 +#define IPTS_HID_REPORT_DATA 65 +#define IPTS_HID_REPORT_SET_MODE 66 + +#define IPTS_HID_REPORT_DATA_SIZE 7485 + +/* + * HID descriptor for singletouch data. + * This descriptor should be present on all IPTS devices. + */ +static const u8 ipts_singletouch_descriptor[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x04, /* Usage (Touchscreen), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x40, /* Report ID (64), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x15, 0x00, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x07, /* Report Count (7), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0xA4, /* Push, */ + 0x55, 0x0E, /* Unit Exponent (14), */ + 0x65, 0x11, /* Unit (Centimeter), */ + 0x46, 0x76, 0x0B, /* Physical Maximum (2934), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x46, 0x74, 0x06, /* Physical Maximum (1652), */ + 0x26, 0xFF, 0x7F, /* Logical Maximum (32767), */ + 0x81, 0x02, /* Input (Variable), */ + 0xB4, /* Pop, */ + 0xC0, /* End Collection */ +}; + +/* + * Fallback HID descriptor for older devices that do not have + * the ability to query their HID descriptor. + */ +static const u8 ipts_fallback_descriptor[] = { + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x0F, /* Usage (Capacitive Hm Digitizer), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, 0x41, /* Report ID (65), */ + 0x09, 0x56, /* Usage (Scan Time), */ + 0x95, 0x01, /* Report Count (1), */ + 0x75, 0x10, /* Report Size (16), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x61, /* Usage (Gesture Char Quality), */ + 0x75, 0x08, /* Report Size (8), */ + 0x96, 0x3D, 0x1D, /* Report Count (7485), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x85, 0x42, /* Report ID (66), */ + 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ + 0x09, 0xC8, /* Usage (C8h), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0xB1, 0x02, /* Feature (Variable), */ + 0xC0, /* End Collection, */ +}; + +#endif /* IPTS_DESC_H */ diff --git a/drivers/hid/ipts/eds1.c b/drivers/hid/ipts/eds1.c new file mode 100644 index 00000000000000..7b9f54388a9f68 --- /dev/null +++ b/drivers/hid/ipts/eds1.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include + +#include "context.h" +#include "control.h" +#include "desc.h" +#include "eds1.h" +#include "spec-device.h" + +int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size) +{ + size_t size = 0; + u8 *buffer = NULL; + + if (!ipts) + return -EFAULT; + + if (!desc_buffer) + return -EFAULT; + + if (!desc_size) + return -EFAULT; + + size = sizeof(ipts_singletouch_descriptor) + sizeof(ipts_fallback_descriptor); + + buffer = kzalloc(size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + memcpy(buffer, ipts_singletouch_descriptor, sizeof(ipts_singletouch_descriptor)); + memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], ipts_fallback_descriptor, + sizeof(ipts_fallback_descriptor)); + + *desc_size = size; + *desc_buffer = buffer; + + return 0; +} + +static int ipts_eds1_switch_mode(struct ipts_context *ipts, enum ipts_mode mode) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (ipts->mode == mode) + return 0; + + ipts->mode = mode; + + ret = ipts_control_restart(ipts); + if (ret) + dev_err(ipts->dev, "Failed to switch modes: %d\n", ret); + + return ret; +} + +int ipts_eds1_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum hid_report_type report_type, enum hid_class_request request_type) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (!buffer) + return -EFAULT; + + if (report_id != IPTS_HID_REPORT_SET_MODE) + return -EIO; + + if (report_type != HID_FEATURE_REPORT) + return -EIO; + + if (size != 2) + return -EINVAL; + + /* + * Implement mode switching report for older devices without native HID support. + */ + + if (request_type == HID_REQ_GET_REPORT) { + memset(buffer, 0, size); + buffer[0] = report_id; + buffer[1] = ipts->mode; + } else if (request_type == HID_REQ_SET_REPORT) { + return ipts_eds1_switch_mode(ipts, buffer[1]); + } else { + return -EIO; + } + + return ret; +} diff --git a/drivers/hid/ipts/eds1.h b/drivers/hid/ipts/eds1.h new file mode 100644 index 00000000000000..eeeb6575e3e89f --- /dev/null +++ b/drivers/hid/ipts/eds1.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include + +#include "context.h" + +/** + * ipts_eds1_get_descriptor() - Assembles the HID descriptor of the device. + * @ipts: The IPTS driver context. + * @desc_buffer: A pointer to the location where the address of the allocated buffer is stored. + * @desc_size: A pointer to the location where the size of the allocated buffer is stored. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_eds1_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size); + +/** + * ipts_eds1_raw_request() - Executes an output or feature report on the device. + * @ipts: The IPTS driver context. + * @buffer: The buffer containing the report. + * @size: The size of the buffer. + * @report_id: The HID report ID. + * @report_type: Whether this report is an output or a feature report. + * @request_type: Whether this report requests or sends data. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_eds1_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum hid_report_type report_type, enum hid_class_request request_type); diff --git a/drivers/hid/ipts/eds2.c b/drivers/hid/ipts/eds2.c new file mode 100644 index 00000000000000..639940794615df --- /dev/null +++ b/drivers/hid/ipts/eds2.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include + +#include "context.h" +#include "control.h" +#include "desc.h" +#include "eds2.h" +#include "spec-data.h" + +int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size) +{ + size_t size = 0; + u8 *buffer = NULL; + + if (!ipts) + return -EFAULT; + + if (!desc_buffer) + return -EFAULT; + + if (!desc_size) + return -EFAULT; + + size = sizeof(ipts_singletouch_descriptor) + ipts->descriptor.size; + + buffer = kzalloc(size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + memcpy(buffer, ipts_singletouch_descriptor, sizeof(ipts_singletouch_descriptor)); + memcpy(&buffer[sizeof(ipts_singletouch_descriptor)], ipts->descriptor.address, + ipts->descriptor.size); + + *desc_size = size; + *desc_buffer = buffer; + + return 0; +} + +static int ipts_eds2_get_feature(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum ipts_feedback_data_type type) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (!buffer) + return -EFAULT; + + mutex_lock(&ipts->feature_lock); + + memset(buffer, 0, size); + buffer[0] = report_id; + + memset(&ipts->feature_report, 0, sizeof(ipts->feature_report)); + reinit_completion(&ipts->feature_event); + + ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buffer, size); + if (ret) { + dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret); + goto out; + } + + ret = wait_for_completion_timeout(&ipts->feature_event, msecs_to_jiffies(5000)); + if (ret == 0) { + dev_warn(ipts->dev, "GET_FEATURES timed out!\n"); + ret = -EIO; + goto out; + } + + if (!ipts->feature_report.address) { + ret = -EFAULT; + goto out; + } + + if (ipts->feature_report.size > size) { + ret = -ETOOSMALL; + goto out; + } + + ret = ipts->feature_report.size; + memcpy(buffer, ipts->feature_report.address, ipts->feature_report.size); + +out: + mutex_unlock(&ipts->feature_lock); + return ret; +} + +static int ipts_eds2_set_feature(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum ipts_feedback_data_type type) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (!buffer) + return -EFAULT; + + buffer[0] = report_id; + + ret = ipts_control_hid2me_feedback(ipts, IPTS_FEEDBACK_CMD_TYPE_NONE, type, buffer, size); + if (ret) + dev_err(ipts->dev, "Failed to send hid2me feedback: %d\n", ret); + + return ret; +} + +int ipts_eds2_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum hid_report_type report_type, enum hid_class_request request_type) +{ + enum ipts_feedback_data_type feedback_type = IPTS_FEEDBACK_DATA_TYPE_VENDOR; + + if (!ipts) + return -EFAULT; + + if (!buffer) + return -EFAULT; + + if (report_type == HID_OUTPUT_REPORT && request_type == HID_REQ_SET_REPORT) + feedback_type = IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT; + else if (report_type == HID_FEATURE_REPORT && request_type == HID_REQ_GET_REPORT) + feedback_type = IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES; + else if (report_type == HID_FEATURE_REPORT && request_type == HID_REQ_SET_REPORT) + feedback_type = IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES; + else + return -EIO; + + if (request_type == HID_REQ_GET_REPORT) + return ipts_eds2_get_feature(ipts, buffer, size, report_id, feedback_type); + else + return ipts_eds2_set_feature(ipts, buffer, size, report_id, feedback_type); +} diff --git a/drivers/hid/ipts/eds2.h b/drivers/hid/ipts/eds2.h new file mode 100644 index 00000000000000..064e3716907ab5 --- /dev/null +++ b/drivers/hid/ipts/eds2.h @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include + +#include "context.h" + +/** + * ipts_eds2_get_descriptor() - Assembles the HID descriptor of the device. + * @ipts: The IPTS driver context. + * @desc_buffer: A pointer to the location where the address of the allocated buffer is stored. + * @desc_size: A pointer to the location where the size of the allocated buffer is stored. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_eds2_get_descriptor(struct ipts_context *ipts, u8 **desc_buffer, size_t *desc_size); + +/** + * ipts_eds2_raw_request() - Executes an output or feature report on the device. + * @ipts: The IPTS driver context. + * @buffer: The buffer containing the report. + * @size: The size of the buffer. + * @report_id: The HID report ID. + * @report_type: Whether this report is an output or a feature report. + * @request_type: Whether this report requests or sends data. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_eds2_raw_request(struct ipts_context *ipts, u8 *buffer, size_t size, u8 report_id, + enum hid_report_type report_type, enum hid_class_request request_type); diff --git a/drivers/hid/ipts/hid.c b/drivers/hid/ipts/hid.c new file mode 100644 index 00000000000000..e34a1a4f9fa779 --- /dev/null +++ b/drivers/hid/ipts/hid.c @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2022-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "context.h" +#include "desc.h" +#include "eds1.h" +#include "eds2.h" +#include "hid.h" +#include "spec-data.h" +#include "spec-hid.h" + +void ipts_hid_enable(struct ipts_context *ipts) +{ + WRITE_ONCE(ipts->hid_active, true); +} + +void ipts_hid_disable(struct ipts_context *ipts) +{ + WRITE_ONCE(ipts->hid_active, false); +} + +static int ipts_hid_start(struct hid_device *hid) +{ + return 0; +} + +static void ipts_hid_stop(struct hid_device *hid) +{ +} + +static int ipts_hid_parse(struct hid_device *hid) +{ + int ret = 0; + struct ipts_context *ipts = NULL; + + u8 *buffer = NULL; + size_t size = 0; + + if (!hid) + return -ENODEV; + + ipts = hid->driver_data; + + if (!ipts) + return -EFAULT; + + if (!READ_ONCE(ipts->hid_active)) + return -ENODEV; + + if (ipts->info.intf_eds == 1) + ret = ipts_eds1_get_descriptor(ipts, &buffer, &size); + else + ret = ipts_eds2_get_descriptor(ipts, &buffer, &size); + + if (ret) { + dev_err(ipts->dev, "Failed to allocate HID descriptor: %d\n", ret); + return ret; + } + + ret = hid_parse_report(hid, buffer, size); + kfree(buffer); + + if (ret) { + dev_err(ipts->dev, "Failed to parse HID descriptor: %d\n", ret); + return ret; + } + + return 0; +} + +static int ipts_hid_raw_request(struct hid_device *hid, unsigned char report_id, __u8 *buffer, + size_t size, unsigned char report_type, int request_type) +{ + struct ipts_context *ipts = NULL; + + if (!hid) + return -ENODEV; + + ipts = hid->driver_data; + + if (!ipts) + return -EFAULT; + + if (!READ_ONCE(ipts->hid_active)) + return -ENODEV; + + if (ipts->info.intf_eds == 1) { + return ipts_eds1_raw_request(ipts, buffer, size, report_id, report_type, + request_type); + } else { + return ipts_eds2_raw_request(ipts, buffer, size, report_id, report_type, + request_type); + } +} + +static struct hid_ll_driver ipts_hid_driver = { + .start = ipts_hid_start, + .stop = ipts_hid_stop, + .open = ipts_hid_start, + .close = ipts_hid_stop, + .parse = ipts_hid_parse, + .raw_request = ipts_hid_raw_request, +}; + +int ipts_hid_input_data(struct ipts_context *ipts, u32 buffer) +{ + u8 *temp = NULL; + struct ipts_hid_header *frame = NULL; + struct ipts_data_header *header = NULL; + + if (!ipts) + return -EFAULT; + + if (!ipts->hid) + return -ENODEV; + + if (!READ_ONCE(ipts->hid_active)) + return -ENODEV; + + header = (struct ipts_data_header *)ipts->resources.data[buffer].address; + + temp = ipts->resources.report.address; + memset(temp, 0, ipts->resources.report.size); + + if (!header) + return -EFAULT; + + if (header->size == 0) + return 0; + + if (header->type == IPTS_DATA_TYPE_HID) + return hid_input_report(ipts->hid, HID_INPUT_REPORT, header->data, header->size, 1); + + if (header->type == IPTS_DATA_TYPE_GET_FEATURES) { + ipts->feature_report.address = header->data; + ipts->feature_report.size = header->size; + + complete_all(&ipts->feature_event); + return 0; + } + + if (header->type != IPTS_DATA_TYPE_FRAME) + return 0; + + if (header->size + 3 + sizeof(struct ipts_hid_header) > IPTS_HID_REPORT_DATA_SIZE) + return -ERANGE; + + /* + * Synthesize a HID report matching the devices that natively send HID reports + */ + temp[0] = IPTS_HID_REPORT_DATA; + + frame = (struct ipts_hid_header *)&temp[3]; + frame->type = IPTS_HID_FRAME_TYPE_RAW; + frame->size = header->size + sizeof(*frame); + + memcpy(frame->data, header->data, header->size); + + return hid_input_report(ipts->hid, HID_INPUT_REPORT, temp, IPTS_HID_REPORT_DATA_SIZE, 1); +} + +int ipts_hid_init(struct ipts_context *ipts, struct ipts_device_info info) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (ipts->hid) + return 0; + + ipts->hid = hid_allocate_device(); + if (IS_ERR(ipts->hid)) { + int err = PTR_ERR(ipts->hid); + + dev_err(ipts->dev, "Failed to allocate HID device: %d\n", err); + return err; + } + + ipts->hid->driver_data = ipts; + ipts->hid->dev.parent = ipts->dev; + ipts->hid->ll_driver = &ipts_hid_driver; + + ipts->hid->vendor = info.vendor; + ipts->hid->product = info.product; + ipts->hid->group = HID_GROUP_GENERIC; + + snprintf(ipts->hid->name, sizeof(ipts->hid->name), "IPTS %04X:%04X", info.vendor, + info.product); + + ret = hid_add_device(ipts->hid); + if (ret) { + dev_err(ipts->dev, "Failed to add HID device: %d\n", ret); + ipts_hid_free(ipts); + return ret; + } + + return 0; +} + +int ipts_hid_free(struct ipts_context *ipts) +{ + if (!ipts) + return -EFAULT; + + if (!ipts->hid) + return 0; + + hid_destroy_device(ipts->hid); + ipts->hid = NULL; + + return 0; +} diff --git a/drivers/hid/ipts/hid.h b/drivers/hid/ipts/hid.h new file mode 100644 index 00000000000000..1ebe77447903ad --- /dev/null +++ b/drivers/hid/ipts/hid.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2022-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_HID_H +#define IPTS_HID_H + +#include + +#include "context.h" +#include "spec-device.h" + +void ipts_hid_enable(struct ipts_context *ipts); +void ipts_hid_disable(struct ipts_context *ipts); + +int ipts_hid_input_data(struct ipts_context *ipts, u32 buffer); + +int ipts_hid_init(struct ipts_context *ipts, struct ipts_device_info info); +int ipts_hid_free(struct ipts_context *ipts); + +#endif /* IPTS_HID_H */ diff --git a/drivers/hid/ipts/main.c b/drivers/hid/ipts/main.c new file mode 100644 index 00000000000000..fb5b5c13ee3eaa --- /dev/null +++ b/drivers/hid/ipts/main.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "context.h" +#include "control.h" +#include "mei.h" +#include "receiver.h" +#include "spec-device.h" + +/* + * The MEI client ID for IPTS functionality. + */ +#define IPTS_ID UUID_LE(0x3e8d0870, 0x271a, 0x4208, 0x8e, 0xb5, 0x9a, 0xcb, 0x94, 0x02, 0xae, 0x04) + +static int ipts_set_dma_mask(struct mei_cl_device *cldev) +{ + if (!cldev) + return -EFAULT; + + if (!dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(64))) + return 0; + + return dma_coerce_mask_and_coherent(&cldev->dev, DMA_BIT_MASK(32)); +} + +static int ipts_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) +{ + int ret = 0; + struct ipts_context *ipts = NULL; + + if (!cldev) + return -EFAULT; + + ret = ipts_set_dma_mask(cldev); + if (ret) { + dev_err(&cldev->dev, "Failed to set DMA mask for IPTS: %d\n", ret); + return ret; + } + + ret = mei_cldev_enable(cldev); + if (ret) { + dev_err(&cldev->dev, "Failed to enable MEI device: %d\n", ret); + return ret; + } + + ipts = devm_kzalloc(&cldev->dev, sizeof(*ipts), GFP_KERNEL); + if (!ipts) { + mei_cldev_disable(cldev); + return -ENOMEM; + } + + ret = ipts_mei_init(&ipts->mei, cldev); + if (ret) { + dev_err(&cldev->dev, "Failed to init MEI bus logic: %d\n", ret); + return ret; + } + + ipts->dev = &cldev->dev; + ipts->mode = IPTS_MODE_EVENT; + + mutex_init(&ipts->feature_lock); + init_completion(&ipts->feature_event); + + mei_cldev_set_drvdata(cldev, ipts); + + ret = ipts_control_start(ipts); + if (ret) { + dev_err(&cldev->dev, "Failed to start IPTS: %d\n", ret); + return ret; + } + + return 0; +} + +static void ipts_remove(struct mei_cl_device *cldev) +{ + int ret = 0; + struct ipts_context *ipts = NULL; + + if (!cldev) { + pr_err("MEI device is NULL!"); + return; + } + + ipts = mei_cldev_get_drvdata(cldev); + + ret = ipts_control_stop(ipts); + if (ret) + dev_err(&cldev->dev, "Failed to stop IPTS: %d\n", ret); + + mei_cldev_disable(cldev); +} + +static struct mei_cl_device_id ipts_device_id_table[] = { + { .uuid = IPTS_ID, .version = MEI_CL_VERSION_ANY }, + {}, +}; +MODULE_DEVICE_TABLE(mei, ipts_device_id_table); + +static struct mei_cl_driver ipts_driver = { + .id_table = ipts_device_id_table, + .name = "ipts", + .probe = ipts_probe, + .remove = ipts_remove, +}; +module_mei_cl_driver(ipts_driver); + +MODULE_DESCRIPTION("IPTS touchscreen driver"); +MODULE_AUTHOR("Dorian Stoll "); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/ipts/mei.c b/drivers/hid/ipts/mei.c new file mode 100644 index 00000000000000..1e0395ceae4a4b --- /dev/null +++ b/drivers/hid/ipts/mei.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "context.h" +#include "mei.h" + +static void locked_list_add(struct list_head *new, struct list_head *head, + struct rw_semaphore *lock) +{ + down_write(lock); + list_add(new, head); + up_write(lock); +} + +static void locked_list_del(struct list_head *entry, struct rw_semaphore *lock) +{ + down_write(lock); + list_del(entry); + up_write(lock); +} + +static void ipts_mei_incoming(struct mei_cl_device *cldev) +{ + ssize_t ret = 0; + struct ipts_mei_message *entry = NULL; + struct ipts_context *ipts = NULL; + + if (!cldev) { + pr_err("MEI device is NULL!"); + return; + } + + ipts = mei_cldev_get_drvdata(cldev); + if (!ipts) { + pr_err("IPTS driver context is NULL!"); + return; + } + + entry = devm_kzalloc(ipts->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return; + + INIT_LIST_HEAD(&entry->list); + + do { + ret = mei_cldev_recv(cldev, (u8 *)&entry->rsp, sizeof(entry->rsp)); + } while (ret == -EINTR); + + if (ret < 0) { + dev_err(ipts->dev, "Error while reading response: %ld\n", ret); + return; + } + + if (ret == 0) { + dev_err(ipts->dev, "Received empty response\n"); + return; + } + + locked_list_add(&entry->list, &ipts->mei.messages, &ipts->mei.message_lock); + wake_up_all(&ipts->mei.message_queue); +} + +static int ipts_mei_search(struct ipts_mei *mei, enum ipts_command_code code, + struct ipts_response *rsp) +{ + struct ipts_mei_message *entry = NULL; + + if (!mei) + return -EFAULT; + + if (!rsp) + return -EFAULT; + + down_read(&mei->message_lock); + + /* + * Iterate over the list of received messages, and check if there is one + * matching the requested command code. + */ + list_for_each_entry(entry, &mei->messages, list) { + if (entry->rsp.cmd == code) + break; + } + + up_read(&mei->message_lock); + + /* + * If entry is not the list head, this means that the loop above has been stopped early, + * and that we found a matching element. We drop the message from the list and return it. + */ + if (!list_entry_is_head(entry, &mei->messages, list)) { + locked_list_del(&entry->list, &mei->message_lock); + + *rsp = entry->rsp; + devm_kfree(&mei->cldev->dev, entry); + + return 0; + } + + return -EAGAIN; +} + +int ipts_mei_recv(struct ipts_mei *mei, enum ipts_command_code code, struct ipts_response *rsp, + u64 timeout) +{ + int ret = 0; + + if (!mei) + return -EFAULT; + + /* + * A timeout of 0 means check and return immideately. + */ + if (timeout == 0) + return ipts_mei_search(mei, code, rsp); + + /* + * A timeout of less than 0 means to wait forever. + */ + if (timeout < 0) { + wait_event(mei->message_queue, ipts_mei_search(mei, code, rsp) == 0); + return 0; + } + + ret = wait_event_timeout(mei->message_queue, ipts_mei_search(mei, code, rsp) == 0, + msecs_to_jiffies(timeout)); + + if (ret > 0) + return 0; + + return -EAGAIN; +} + +int ipts_mei_send(struct ipts_mei *mei, void *data, size_t length) +{ + int ret = 0; + + if (!mei) + return -EFAULT; + + if (!mei->cldev) + return -EFAULT; + + if (!data) + return -EFAULT; + + do { + ret = mei_cldev_send(mei->cldev, (u8 *)data, length); + } while (ret == -EINTR); + + if (ret < 0) + return ret; + + return 0; +} + +int ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev) +{ + if (!mei) + return -EFAULT; + + if (!cldev) + return -EFAULT; + + mei->cldev = cldev; + + INIT_LIST_HEAD(&mei->messages); + init_waitqueue_head(&mei->message_queue); + init_rwsem(&mei->message_lock); + + mei_cldev_register_rx_cb(cldev, ipts_mei_incoming); + + return 0; +} diff --git a/drivers/hid/ipts/mei.h b/drivers/hid/ipts/mei.h new file mode 100644 index 00000000000000..973bade6b0fdd1 --- /dev/null +++ b/drivers/hid/ipts/mei.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_MEI_H +#define IPTS_MEI_H + +#include +#include +#include +#include +#include + +#include "spec-device.h" + +struct ipts_mei_message { + struct list_head list; + struct ipts_response rsp; +}; + +struct ipts_mei { + struct mei_cl_device *cldev; + + struct list_head messages; + + wait_queue_head_t message_queue; + struct rw_semaphore message_lock; +}; + +/** + * ipts_mei_recv() - Receive data from a MEI device. + * @mei: The IPTS MEI device context. + * @code: The IPTS command code to look for. + * @rsp: The address that the received data will be copied to. + * @timeout: How many milliseconds the function will wait at most. + * + * A negative timeout means to wait forever. + * + * Returns: 0 on success, <0 on error, -EAGAIN if no response has been received. + */ +int ipts_mei_recv(struct ipts_mei *mei, enum ipts_command_code code, struct ipts_response *rsp, + u64 timeout); + +/** + * ipts_mei_send() - Send data to a MEI device. + * @ipts: The IPTS MEI device context. + * @data: The data to send. + * @size: The size of the data. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_mei_send(struct ipts_mei *mei, void *data, size_t length); + +/** + * ipts_mei_init() - Initialize the MEI device context. + * @mei: The MEI device context to initialize. + * @cldev: The MEI device the context will be bound to. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_mei_init(struct ipts_mei *mei, struct mei_cl_device *cldev); + +#endif /* IPTS_MEI_H */ diff --git a/drivers/hid/ipts/receiver.c b/drivers/hid/ipts/receiver.c new file mode 100644 index 00000000000000..977724c728c3e5 --- /dev/null +++ b/drivers/hid/ipts/receiver.c @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include +#include +#include + +#include "cmd.h" +#include "context.h" +#include "control.h" +#include "hid.h" +#include "receiver.h" +#include "resources.h" +#include "spec-device.h" +#include "thread.h" + +static void ipts_receiver_next_doorbell(struct ipts_context *ipts) +{ + u32 *doorbell = (u32 *)ipts->resources.doorbell.address; + *doorbell = *doorbell + 1; +} + +static u32 ipts_receiver_current_doorbell(struct ipts_context *ipts) +{ + u32 *doorbell = (u32 *)ipts->resources.doorbell.address; + return *doorbell; +} + +static void ipts_receiver_backoff(time64_t last, u32 n) +{ + /* + * If the last change was less than n seconds ago, + * sleep for a shorter period so that new data can be + * processed quickly. If there was no change for more than + * n seconds, sleep longer to avoid wasting CPU cycles. + */ + if (last + n > ktime_get_seconds()) + usleep_range(1 * USEC_PER_MSEC, 5 * USEC_PER_MSEC); + else + msleep(200); +} + +static int ipts_receiver_event_loop(struct ipts_thread *thread) +{ + int ret = 0; + u32 buffer = 0; + + struct ipts_context *ipts = NULL; + time64_t last = ktime_get_seconds(); + + if (!thread) + return -EFAULT; + + ipts = thread->data; + + if (!ipts) + return -EFAULT; + + dev_info(ipts->dev, "IPTS running in event mode\n"); + + while (!ipts_thread_should_stop(thread)) { + int i = 0; + + for (i = 0; i < IPTS_BUFFERS; i++) { + ret = ipts_control_wait_data(ipts, false); + if (ret == -EAGAIN) + break; + + if (ret) { + dev_err(ipts->dev, "Failed to wait for data: %d\n", ret); + continue; + } + + buffer = ipts_receiver_current_doorbell(ipts) % IPTS_BUFFERS; + ipts_receiver_next_doorbell(ipts); + + ret = ipts_hid_input_data(ipts, buffer); + if (ret) + dev_err(ipts->dev, "Failed to process buffer: %d\n", ret); + + ret = ipts_control_refill_buffer(ipts, buffer); + if (ret) + dev_err(ipts->dev, "Failed to send feedback: %d\n", ret); + + ret = ipts_control_request_data(ipts); + if (ret) + dev_err(ipts->dev, "Failed to request data: %d\n", ret); + + last = ktime_get_seconds(); + } + + ipts_receiver_backoff(last, 5); + } + + ret = ipts_control_request_flush(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to request flush: %d\n", ret); + return ret; + } + + ret = ipts_control_wait_data(ipts, true); + if (ret) { + dev_err(ipts->dev, "Failed to wait for data: %d\n", ret); + + if (ret != -EAGAIN) + return ret; + else + return 0; + } + + ret = ipts_control_wait_flush(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret); + + if (ret != -EAGAIN) + return ret; + else + return 0; + } + + return 0; +} + +static int ipts_receiver_poll_loop(struct ipts_thread *thread) +{ + int ret = 0; + u32 buffer = 0; + + u32 doorbell = 0; + u32 lastdb = 0; + + struct ipts_context *ipts = NULL; + time64_t last = ktime_get_seconds(); + + if (!thread) + return -EFAULT; + + ipts = thread->data; + + if (!ipts) + return -EFAULT; + + dev_info(ipts->dev, "IPTS running in poll mode\n"); + + while (true) { + if (ipts_thread_should_stop(thread)) { + ret = ipts_control_request_flush(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to request flush: %d\n", ret); + return ret; + } + } + + doorbell = ipts_receiver_current_doorbell(ipts); + + /* + * After filling up one of the data buffers, IPTS will increment + * the doorbell. The value of the doorbell stands for the *next* + * buffer that IPTS is going to fill. + */ + while (lastdb != doorbell) { + buffer = lastdb % IPTS_BUFFERS; + + ret = ipts_hid_input_data(ipts, buffer); + if (ret) + dev_err(ipts->dev, "Failed to process buffer: %d\n", ret); + + ret = ipts_control_refill_buffer(ipts, buffer); + if (ret) + dev_err(ipts->dev, "Failed to send feedback: %d\n", ret); + + last = ktime_get_seconds(); + lastdb++; + } + + if (ipts_thread_should_stop(thread)) + break; + + ipts_receiver_backoff(last, 5); + } + + ret = ipts_control_wait_data(ipts, true); + if (ret) { + dev_err(ipts->dev, "Failed to wait for data: %d\n", ret); + + if (ret != -EAGAIN) + return ret; + else + return 0; + } + + ret = ipts_control_wait_flush(ipts); + if (ret) { + dev_err(ipts->dev, "Failed to wait for flush: %d\n", ret); + + if (ret != -EAGAIN) + return ret; + else + return 0; + } + + return 0; +} + +int ipts_receiver_start(struct ipts_context *ipts) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + if (ipts->mode == IPTS_MODE_EVENT) { + ret = ipts_thread_start(&ipts->receiver_loop, ipts_receiver_event_loop, ipts, + "ipts_event"); + } else if (ipts->mode == IPTS_MODE_POLL) { + ret = ipts_thread_start(&ipts->receiver_loop, ipts_receiver_poll_loop, ipts, + "ipts_poll"); + } else { + ret = -EINVAL; + } + + if (ret) { + dev_err(ipts->dev, "Failed to start receiver loop: %d\n", ret); + return ret; + } + + return 0; +} + +int ipts_receiver_stop(struct ipts_context *ipts) +{ + int ret = 0; + + if (!ipts) + return -EFAULT; + + ret = ipts_thread_stop(&ipts->receiver_loop); + if (ret) { + dev_err(ipts->dev, "Failed to stop receiver loop: %d\n", ret); + return ret; + } + + return 0; +} diff --git a/drivers/hid/ipts/receiver.h b/drivers/hid/ipts/receiver.h new file mode 100644 index 00000000000000..3de7da62d40c14 --- /dev/null +++ b/drivers/hid/ipts/receiver.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_RECEIVER_H +#define IPTS_RECEIVER_H + +#include "context.h" + +int ipts_receiver_start(struct ipts_context *ipts); +int ipts_receiver_stop(struct ipts_context *ipts); + +#endif /* IPTS_RECEIVER_H */ diff --git a/drivers/hid/ipts/resources.c b/drivers/hid/ipts/resources.c new file mode 100644 index 00000000000000..cc14653b2a9f59 --- /dev/null +++ b/drivers/hid/ipts/resources.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include + +#include "desc.h" +#include "resources.h" +#include "spec-device.h" + +static int ipts_resources_alloc_buffer(struct ipts_buffer *buffer, struct device *dev, size_t size) +{ + if (!buffer) + return -EFAULT; + + if (buffer->address) + return 0; + + buffer->address = dma_alloc_coherent(dev, size, &buffer->dma_address, GFP_KERNEL); + + if (!buffer->address) + return -ENOMEM; + + buffer->size = size; + buffer->device = dev; + + return 0; +} + +static void ipts_resources_free_buffer(struct ipts_buffer *buffer) +{ + if (!buffer->address) + return; + + dma_free_coherent(buffer->device, buffer->size, buffer->address, buffer->dma_address); + + buffer->address = NULL; + buffer->size = 0; + + buffer->dma_address = 0; + buffer->device = NULL; +} + +int ipts_resources_init(struct ipts_resources *res, struct device *dev, size_t ds, size_t fs) +{ + int ret = 0; + + /* + * Some compilers (AOSP clang) complain about a redefined + * variable when this is declared inside of the for loop. + */ + int i = 0; + + if (!res) + return -EFAULT; + + for (i = 0; i < IPTS_BUFFERS; i++) { + ret = ipts_resources_alloc_buffer(&res->data[i], dev, ds); + if (ret) + goto err; + } + + for (i = 0; i < IPTS_BUFFERS; i++) { + ret = ipts_resources_alloc_buffer(&res->feedback[i], dev, fs); + if (ret) + goto err; + } + + ret = ipts_resources_alloc_buffer(&res->doorbell, dev, sizeof(u32)); + if (ret) + goto err; + + ret = ipts_resources_alloc_buffer(&res->workqueue, dev, sizeof(u32)); + if (ret) + goto err; + + ret = ipts_resources_alloc_buffer(&res->hid2me, dev, fs); + if (ret) + goto err; + + ret = ipts_resources_alloc_buffer(&res->descriptor, dev, ds + 8); + if (ret) + goto err; + + if (!res->report.address) { + res->report.size = IPTS_HID_REPORT_DATA_SIZE; + res->report.address = kzalloc(res->report.size, GFP_KERNEL); + + if (!res->report.address) { + ret = -ENOMEM; + goto err; + } + } + + return 0; + +err: + + ipts_resources_free(res); + return ret; +} + +int ipts_resources_free(struct ipts_resources *res) +{ + int i = 0; + + if (!res) + return -EFAULT; + + for (i = 0; i < IPTS_BUFFERS; i++) + ipts_resources_free_buffer(&res->data[i]); + + for (i = 0; i < IPTS_BUFFERS; i++) + ipts_resources_free_buffer(&res->feedback[i]); + + ipts_resources_free_buffer(&res->doorbell); + ipts_resources_free_buffer(&res->workqueue); + ipts_resources_free_buffer(&res->hid2me); + ipts_resources_free_buffer(&res->descriptor); + + kfree(res->report.address); + res->report.address = NULL; + res->report.size = 0; + + return 0; +} diff --git a/drivers/hid/ipts/resources.h b/drivers/hid/ipts/resources.h new file mode 100644 index 00000000000000..2068e13285f0ef --- /dev/null +++ b/drivers/hid/ipts/resources.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_RESOURCES_H +#define IPTS_RESOURCES_H + +#include +#include + +#include "spec-device.h" + +struct ipts_buffer { + u8 *address; + size_t size; + + dma_addr_t dma_address; + struct device *device; +}; + +struct ipts_resources { + struct ipts_buffer data[IPTS_BUFFERS]; + struct ipts_buffer feedback[IPTS_BUFFERS]; + + struct ipts_buffer doorbell; + struct ipts_buffer workqueue; + struct ipts_buffer hid2me; + + struct ipts_buffer descriptor; + + // Buffer for synthesizing HID reports + struct ipts_buffer report; +}; + +int ipts_resources_init(struct ipts_resources *res, struct device *dev, size_t ds, size_t fs); +int ipts_resources_free(struct ipts_resources *res); + +#endif /* IPTS_RESOURCES_H */ diff --git a/drivers/hid/ipts/spec-data.h b/drivers/hid/ipts/spec-data.h new file mode 100644 index 00000000000000..e8dd98895a7eee --- /dev/null +++ b/drivers/hid/ipts/spec-data.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_SPEC_DATA_H +#define IPTS_SPEC_DATA_H + +#include +#include + +/** + * enum ipts_feedback_cmd_type - Commands that can be executed on the sensor through feedback. + */ +enum ipts_feedback_cmd_type { + IPTS_FEEDBACK_CMD_TYPE_NONE = 0, + IPTS_FEEDBACK_CMD_TYPE_SOFT_RESET = 1, + IPTS_FEEDBACK_CMD_TYPE_GOTO_ARMED = 2, + IPTS_FEEDBACK_CMD_TYPE_GOTO_SENSING = 3, + IPTS_FEEDBACK_CMD_TYPE_GOTO_SLEEP = 4, + IPTS_FEEDBACK_CMD_TYPE_GOTO_DOZE = 5, + IPTS_FEEDBACK_CMD_TYPE_HARD_RESET = 6, +}; + +/** + * enum ipts_feedback_data_type - Defines what data a feedback buffer contains. + * @IPTS_FEEDBACK_DATA_TYPE_VENDOR: The buffer contains vendor specific feedback. + * @IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES: The buffer contains a HID set features report. + * @IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES: The buffer contains a HID get features report. + * @IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT: The buffer contains a HID output report. + * @IPTS_FEEDBACK_DATA_TYPE_STORE_DATA: The buffer contains calibration data for the sensor. + */ +enum ipts_feedback_data_type { + IPTS_FEEDBACK_DATA_TYPE_VENDOR = 0, + IPTS_FEEDBACK_DATA_TYPE_SET_FEATURES = 1, + IPTS_FEEDBACK_DATA_TYPE_GET_FEATURES = 2, + IPTS_FEEDBACK_DATA_TYPE_OUTPUT_REPORT = 3, + IPTS_FEEDBACK_DATA_TYPE_STORE_DATA = 4, +}; + +/** + * struct ipts_feedback_header - Header that is prefixed to the data in a feedback buffer. + * @cmd_type: A command that should be executed on the sensor. + * @size: The size of the payload to be written. + * @buffer: The ID of the buffer that contains this feedback data. + * @protocol: The protocol version of the EDS. + * @data_type: The type of data that the buffer contains. + * @spi_offset: The offset at which to write the payload data to the sensor. + * @payload: Payload for the feedback command, or 0 if no payload is sent. + */ +struct ipts_feedback_header { + enum ipts_feedback_cmd_type cmd_type; + u32 size; + u32 buffer; + u32 protocol; + enum ipts_feedback_data_type data_type; + u32 spi_offset; + u8 reserved[40]; + u8 payload[]; +} __packed; + +static_assert(sizeof(struct ipts_feedback_header) == 64); + +/** + * enum ipts_data_type - Defines what type of data a buffer contains. + * @IPTS_DATA_TYPE_FRAME: Raw data frame. + * @IPTS_DATA_TYPE_ERROR: Error data. + * @IPTS_DATA_TYPE_VENDOR: Vendor specific data. + * @IPTS_DATA_TYPE_HID: A HID report. + * @IPTS_DATA_TYPE_GET_FEATURES: The response to a GET_FEATURES HID2ME command. + */ +enum ipts_data_type { + IPTS_DATA_TYPE_FRAME = 0x00, + IPTS_DATA_TYPE_ERROR = 0x01, + IPTS_DATA_TYPE_VENDOR = 0x02, + IPTS_DATA_TYPE_HID = 0x03, + IPTS_DATA_TYPE_GET_FEATURES = 0x04, + IPTS_DATA_TYPE_DESCRIPTOR = 0x05, +}; + +/** + * struct ipts_data_header - Header that is prefixed to the data in a data buffer. + * @type: What data the buffer contains. + * @size: How much data the buffer contains. + * @buffer: Which buffer the data is in. + */ +struct ipts_data_header { + enum ipts_data_type type; + u32 size; + u32 buffer; + u8 reserved[52]; + u8 data[]; +} __packed; + +static_assert(sizeof(struct ipts_data_header) == 64); + +#endif /* IPTS_SPEC_DATA_H */ diff --git a/drivers/hid/ipts/spec-device.h b/drivers/hid/ipts/spec-device.h new file mode 100644 index 00000000000000..41845f9d902579 --- /dev/null +++ b/drivers/hid/ipts/spec-device.h @@ -0,0 +1,290 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_SPEC_DEVICE_H +#define IPTS_SPEC_DEVICE_H + +#include +#include + +/* + * The amount of buffers that IPTS can use for data transfer. + */ +#define IPTS_BUFFERS 16 + +/* + * The buffer ID that is used for HID2ME feedback + */ +#define IPTS_HID2ME_BUFFER IPTS_BUFFERS + +/** + * enum ipts_command - Commands that can be sent to the IPTS hardware. + * @IPTS_CMD_GET_DEVICE_INFO: Retrieves vendor information from the device. + * @IPTS_CMD_SET_MODE: Changes the mode that the device will operate in. + * @IPTS_CMD_SET_MEM_WINDOW: Configures memory buffers for passing data between device and driver. + * @IPTS_CMD_QUIESCE_IO: Stops the data flow from the device to the driver. + * @IPTS_CMD_READY_FOR_DATA: Informs the device that the driver is ready to receive data. + * @IPTS_CMD_FEEDBACK: Informs the device that a buffer was processed and can be refilled. + * @IPTS_CMD_CLEAR_MEM_WINDOW: Stops the data flow and clears the buffer addresses on the device. + * @IPTS_CMD_RESET_SENSOR: Resets the sensor to its default state. + * @IPTS_CMD_GET_DESCRIPTOR: Retrieves the HID descriptor of the device. + */ +enum ipts_command_code { + IPTS_CMD_GET_DEVICE_INFO = 0x01, + IPTS_CMD_SET_MODE = 0x02, + IPTS_CMD_SET_MEM_WINDOW = 0x03, + IPTS_CMD_QUIESCE_IO = 0x04, + IPTS_CMD_READY_FOR_DATA = 0x05, + IPTS_CMD_FEEDBACK = 0x06, + IPTS_CMD_CLEAR_MEM_WINDOW = 0x07, + IPTS_CMD_RESET_SENSOR = 0x0B, + IPTS_CMD_GET_DESCRIPTOR = 0x0F, +}; + +/** + * enum ipts_status - Possible status codes returned by the IPTS device. + * @IPTS_STATUS_SUCCESS: Operation completed successfully. + * @IPTS_STATUS_INVALID_PARAMS: Command contained an invalid payload. + * @IPTS_STATUS_ACCESS_DENIED: ME could not validate a buffer address. + * @IPTS_STATUS_CMD_SIZE_ERROR: Command contains an invalid payload. + * @IPTS_STATUS_NOT_READY: Buffer addresses have not been set. + * @IPTS_STATUS_REQUEST_OUTSTANDING: There is an outstanding command of the same type. + * @IPTS_STATUS_NO_SENSOR_FOUND: No sensor could be found. + * @IPTS_STATUS_OUT_OF_MEMORY: Not enough free memory for requested operation. + * @IPTS_STATUS_INTERNAL_ERROR: An unexpected error occurred. + * @IPTS_STATUS_SENSOR_DISABLED: The sensor has been disabled and must be reinitialized. + * @IPTS_STATUS_COMPAT_CHECK_FAIL: Compatibility revision check between sensor and ME failed. + * The host can ignore this error and attempt to continue. + * @IPTS_STATUS_SENSOR_EXPECTED_RESET: The sensor went through a reset initiated by the driver. + * @IPTS_STATUS_SENSOR_UNEXPECTED_RESET: The sensor went through an unexpected reset. + * @IPTS_STATUS_RESET_FAILED: Requested sensor reset failed to complete. + * @IPTS_STATUS_TIMEOUT: The operation timed out. + * @IPTS_STATUS_TEST_MODE_FAIL: Test mode pattern did not match expected values. + * @IPTS_STATUS_SENSOR_FAIL_FATAL: The sensor reported an error during reset sequence. + * Further progress is not possible. + * @IPTS_STATUS_SENSOR_FAIL_NONFATAL: The sensor reported an error during reset sequence. + * The driver can attempt to continue. + * @IPTS_STATUS_INVALID_DEVICE_CAPS: The device reported invalid capabilities. + * @IPTS_STATUS_QUIESCE_IO_IN_PROGRESS: Command cannot be completed until Quiesce IO is done. + */ +enum ipts_status { + IPTS_STATUS_SUCCESS = 0x00, + IPTS_STATUS_INVALID_PARAMS = 0x01, + IPTS_STATUS_ACCESS_DENIED = 0x02, + IPTS_STATUS_CMD_SIZE_ERROR = 0x03, + IPTS_STATUS_NOT_READY = 0x04, + IPTS_STATUS_REQUEST_OUTSTANDING = 0x05, + IPTS_STATUS_NO_SENSOR_FOUND = 0x06, + IPTS_STATUS_OUT_OF_MEMORY = 0x07, + IPTS_STATUS_INTERNAL_ERROR = 0x08, + IPTS_STATUS_SENSOR_DISABLED = 0x09, + IPTS_STATUS_COMPAT_CHECK_FAIL = 0x0A, + IPTS_STATUS_SENSOR_EXPECTED_RESET = 0x0B, + IPTS_STATUS_SENSOR_UNEXPECTED_RESET = 0x0C, + IPTS_STATUS_RESET_FAILED = 0x0D, + IPTS_STATUS_TIMEOUT = 0x0E, + IPTS_STATUS_TEST_MODE_FAIL = 0x0F, + IPTS_STATUS_SENSOR_FAIL_FATAL = 0x10, + IPTS_STATUS_SENSOR_FAIL_NONFATAL = 0x11, + IPTS_STATUS_INVALID_DEVICE_CAPS = 0x12, + IPTS_STATUS_QUIESCE_IO_IN_PROGRESS = 0x13, +}; + +/** + * struct ipts_command - Message that is sent to the device for calling a command. + * @cmd: The command that will be called. + * @payload: Payload containing parameters for the called command. + */ +struct ipts_command { + enum ipts_command_code cmd; + u8 payload[320]; +} __packed; + +static_assert(sizeof(struct ipts_command) == 324); + +/** + * enum ipts_mode - Configures what data the device produces and how its sent. + * @IPTS_MODE_EVENT: The device will send an event once a buffer was filled. + * Older devices will return singletouch data in this mode. + * @IPTS_MODE_POLL: The device will notify the driver by incrementing the doorbell value. + * Older devices will return multitouch data in this mode. + */ +enum ipts_mode { + IPTS_MODE_EVENT = 0x00, + IPTS_MODE_POLL = 0x01, +}; + +/** + * struct ipts_set_mode - Payload for the SET_MODE command. + * @mode: Changes the mode that IPTS will operate in. + */ +struct ipts_set_mode { + enum ipts_mode mode; + u8 reserved[12]; +} __packed; + +static_assert(sizeof(struct ipts_set_mode) == 16); + +#define IPTS_WORKQUEUE_SIZE 8192 +#define IPTS_WORKQUEUE_ITEM_SIZE 16 + +/** + * struct ipts_mem_window - Payload for the SET_MEM_WINDOW command. + * @data_addr_lower: Lower 32 bits of the data buffer addresses. + * @data_addr_upper: Upper 32 bits of the data buffer addresses. + * @workqueue_addr_lower: Lower 32 bits of the workqueue buffer address. + * @workqueue_addr_upper: Upper 32 bits of the workqueue buffer address. + * @doorbell_addr_lower: Lower 32 bits of the doorbell buffer address. + * @doorbell_addr_upper: Upper 32 bits of the doorbell buffer address. + * @feedbackaddr_lower: Lower 32 bits of the feedback buffer addresses. + * @feedbackaddr_upper: Upper 32 bits of the feedback buffer addresses. + * @hid2me_addr_lower: Lower 32 bits of the hid2me buffer address. + * @hid2me_addr_upper: Upper 32 bits of the hid2me buffer address. + * @hid2me_size: Size of the hid2me feedback buffer. + * @workqueue_item_size: Magic value. Must be 16. + * @workqueue_size: Magic value. Must be 8192. + * + * The workqueue related items in this struct are required for using + * GuC submission with binary processing firmware. Since this driver does + * not use GuC submission and instead exports raw data to userspace, these + * items are not actually used, but they need to be allocated and passed + * to the device, otherwise initialization will fail. + */ +struct ipts_mem_window { + u32 data_addr_lower[IPTS_BUFFERS]; + u32 data_addr_upper[IPTS_BUFFERS]; + u32 workqueue_addr_lower; + u32 workqueue_addr_upper; + u32 doorbell_addr_lower; + u32 doorbell_addr_upper; + u32 feedback_addr_lower[IPTS_BUFFERS]; + u32 feedback_addr_upper[IPTS_BUFFERS]; + u32 hid2me_addr_lower; + u32 hid2me_addr_upper; + u32 hid2me_size; + u8 reserved1; + u8 workqueue_item_size; + u16 workqueue_size; + u8 reserved[32]; +} __packed; + +static_assert(sizeof(struct ipts_mem_window) == 320); + +/** + * struct ipts_quiesce_io - Payload for the QUIESCE_IO command. + */ +struct ipts_quiesce_io { + u8 reserved[12]; +} __packed; + +static_assert(sizeof(struct ipts_quiesce_io) == 12); + +/** + * struct ipts_feedback - Payload for the FEEDBACK command. + * @buffer: The buffer that the device should refill. + */ +struct ipts_feedback { + u32 buffer; + u8 reserved[12]; +} __packed; + +static_assert(sizeof(struct ipts_feedback) == 16); + +/** + * enum ipts_reset_type - Possible ways of resetting the device. + * @IPTS_RESET_TYPE_HARD: Perform hardware reset using GPIO pin. + * @IPTS_RESET_TYPE_SOFT: Perform software reset using SPI command. + */ +enum ipts_reset_type { + IPTS_RESET_TYPE_HARD = 0x00, + IPTS_RESET_TYPE_SOFT = 0x01, +}; + +/** + * struct ipts_reset - Payload for the RESET_SENSOR command. + * @type: How the device should get reset. + */ +struct ipts_reset_sensor { + enum ipts_reset_type type; + u8 reserved[4]; +} __packed; + +static_assert(sizeof(struct ipts_reset_sensor) == 8); + +/** + * struct ipts_get_descriptor - Payload for the GET_DESCRIPTOR command. + * @addr_lower: The lower 32 bits of the descriptor buffer address. + * @addr_upper: The upper 32 bits of the descriptor buffer address. + * @magic: A magic value. Must be 8. + */ +struct ipts_get_descriptor { + u32 addr_lower; + u32 addr_upper; + u32 magic; + u8 reserved[12]; +} __packed; + +static_assert(sizeof(struct ipts_get_descriptor) == 24); + +/* + * The type of a response is indicated by a + * command code, with the most significant bit flipped to 1. + */ +#define IPTS_RSP_BIT BIT(31) + +/** + * struct ipts_response - Data returned from the device in response to a command. + * @cmd: The command that this response answers (IPTS_RSP_BIT will be 1). + * @status: The return code of the command. + * @payload: The data that was produced by the command. + */ +struct ipts_response { + enum ipts_command_code cmd; + enum ipts_status status; + u8 payload[80]; +} __packed; + +static_assert(sizeof(struct ipts_response) == 88); + +/** + * struct ipts_device_info - Vendor information of the IPTS device. + * @vendor: Vendor ID of this device. + * @product: Product ID of this device. + * @hw_version: Hardware revision of this device. + * @fw_version: Firmware revision of this device. + * @data_size: Requested size for a data buffer. + * @feedback_size: Requested size for a feedback buffer. + * @mode: Mode that the device currently operates in. + * @max_contacts: Maximum amount of concurrent touches the sensor can process. + * @sensor_min_eds: The minimum EDS version supported by the sensor. + * @sensor_max_eds: The maximum EDS version supported by the sensor. + * @me_min_eds: The minimum EDS version supported by the ME for communicating with the sensor. + * @me_max_eds: The maximum EDS version supported by the ME for communicating with the sensor. + * @intf_eds: The EDS version implemented by the interface between ME and host. + */ +struct ipts_device_info { + u16 vendor; + u16 product; + u32 hw_version; + u32 fw_version; + u32 data_size; + u32 feedback_size; + enum ipts_mode mode; + u8 max_contacts; + u8 reserved1[3]; + u8 sensor_min_eds; + u8 sensor_maj_eds; + u8 me_min_eds; + u8 me_maj_eds; + u8 intf_eds; + u8 reserved2[11]; +} __packed; + +static_assert(sizeof(struct ipts_device_info) == 44); + +#endif /* IPTS_SPEC_DEVICE_H */ diff --git a/drivers/hid/ipts/spec-hid.h b/drivers/hid/ipts/spec-hid.h new file mode 100644 index 00000000000000..5a58d4a0a610f4 --- /dev/null +++ b/drivers/hid/ipts/spec-hid.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2020-2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_SPEC_HID_H +#define IPTS_SPEC_HID_H + +#include +#include + +/* + * Made-up type for passing raw IPTS data in a HID report. + */ +#define IPTS_HID_FRAME_TYPE_RAW 0xEE + +/** + * struct ipts_hid_frame - Header that is prefixed to raw IPTS data wrapped in a HID report. + * @size: Size of the data inside the report, including this header. + * @type: What type of data does this report contain. + */ +struct ipts_hid_header { + u32 size; + u8 reserved1; + u8 type; + u8 reserved2; + u8 data[]; +} __packed; + +static_assert(sizeof(struct ipts_hid_header) == 7); + +#endif /* IPTS_SPEC_HID_H */ diff --git a/drivers/hid/ipts/thread.c b/drivers/hid/ipts/thread.c new file mode 100644 index 00000000000000..355e92bea26f81 --- /dev/null +++ b/drivers/hid/ipts/thread.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#include +#include +#include +#include + +#include "thread.h" + +bool ipts_thread_should_stop(struct ipts_thread *thread) +{ + if (!thread) + return false; + + return READ_ONCE(thread->should_stop); +} + +static int ipts_thread_runner(void *data) +{ + int ret = 0; + struct ipts_thread *thread = data; + + if (!thread) + return -EFAULT; + + if (!thread->threadfn) + return -EFAULT; + + ret = thread->threadfn(thread); + complete_all(&thread->done); + + return ret; +} + +int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread), + void *data, const char *name) +{ + if (!thread) + return -EFAULT; + + if (!threadfn) + return -EFAULT; + + init_completion(&thread->done); + + thread->data = data; + thread->should_stop = false; + thread->threadfn = threadfn; + + thread->thread = kthread_run(ipts_thread_runner, thread, name); + return PTR_ERR_OR_ZERO(thread->thread); +} + +int ipts_thread_stop(struct ipts_thread *thread) +{ + int ret = 0; + + if (!thread) + return -EFAULT; + + if (!thread->thread) + return 0; + + WRITE_ONCE(thread->should_stop, true); + + /* + * Make sure that the write has gone through before waiting. + */ + wmb(); + + wait_for_completion(&thread->done); + ret = kthread_stop(thread->thread); + + thread->thread = NULL; + thread->data = NULL; + thread->threadfn = NULL; + + return ret; +} diff --git a/drivers/hid/ipts/thread.h b/drivers/hid/ipts/thread.h new file mode 100644 index 00000000000000..1f966b8b32c45b --- /dev/null +++ b/drivers/hid/ipts/thread.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Dorian Stoll + * + * Linux driver for Intel Precise Touch & Stylus + */ + +#ifndef IPTS_THREAD_H +#define IPTS_THREAD_H + +#include +#include +#include + +/* + * This wrapper over kthread is necessary, because calling kthread_stop makes it impossible + * to issue MEI commands from that thread while it shuts itself down. By using a custom + * boolean variable and a completion object, we can call kthread_stop only when the thread + * already finished all of its work and has returned. + */ +struct ipts_thread { + struct task_struct *thread; + + bool should_stop; + struct completion done; + + void *data; + int (*threadfn)(struct ipts_thread *thread); +}; + +/** + * ipts_thread_should_stop() - Returns true if the thread is asked to terminate. + * @thread: The current thread. + * + * Returns: true if the thread should stop, false if not. + */ +bool ipts_thread_should_stop(struct ipts_thread *thread); + +/** + * ipts_thread_start() - Starts an IPTS thread. + * @thread: The thread to initialize and start. + * @threadfn: The function to execute. + * @data: An argument that will be passed to threadfn. + * @name: The name of the new thread. + * + * Returns: 0 on success, <0 on error. + */ +int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread), + void *data, const char name[]); + +/** + * ipts_thread_stop() - Asks the thread to terminate and waits until it has finished. + * @thread: The thread that should stop. + * + * Returns: The return value of the thread function. + */ +int ipts_thread_stop(struct ipts_thread *thread); + +#endif /* IPTS_THREAD_H */ From 129ccb768c326223f32310a38eb4ce4edfebe2ab Mon Sep 17 00:00:00 2001 From: Dorian Stoll Date: Sun, 11 Dec 2022 12:03:38 +0100 Subject: [PATCH 125/258] SURFACE: iommu: intel: Disable source id verification for ITHC Signed-off-by: Dorian Stoll Patchset: ithc Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/iommu/intel/irq_remapping.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/iommu/intel/irq_remapping.c b/drivers/iommu/intel/irq_remapping.c index 25c26f706984a4..772bb0d8e5888b 100644 --- a/drivers/iommu/intel/irq_remapping.c +++ b/drivers/iommu/intel/irq_remapping.c @@ -381,6 +381,22 @@ static int set_msi_sid(struct irte *irte, struct pci_dev *dev) data.busmatch_count = 0; pci_for_each_dma_alias(dev, set_msi_sid_cb, &data); + /* + * The Intel Touch Host Controller is at 00:10.6, but for some reason + * the MSI interrupts have request id 01:05.0. + * Disable id verification to work around this. + * FIXME Find proper fix or turn this into a quirk. + */ + if (dev->vendor == PCI_VENDOR_ID_INTEL && (dev->class >> 8) == PCI_CLASS_INPUT_PEN) { + switch(dev->device) { + case 0x98d0: case 0x98d1: // LKF + case 0xa0d0: case 0xa0d1: // TGL LP + case 0x43d0: case 0x43d1: // TGL H + set_irte_sid(irte, SVT_NO_VERIFY, SQ_ALL_16, 0); + return 0; + } + } + /* * DMA alias provides us with a PCI device and alias. The only case * where the it will return an alias on a different bus than the From 27291744f44ecf21e9d04d5b135074f8c027ebc8 Mon Sep 17 00:00:00 2001 From: quo Date: Sun, 11 Dec 2022 12:10:54 +0100 Subject: [PATCH 126/258] SURFACE: hid: Add support for Intel Touch Host Controller Based on quo/ithc-linux@34539af4726d. Signed-off-by: Maximilian Stoll Patchset: ithc Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/Kconfig | 2 + drivers/hid/Makefile | 1 + drivers/hid/ithc/Kbuild | 6 + drivers/hid/ithc/Kconfig | 12 + drivers/hid/ithc/ithc-debug.c | 149 ++++++++ drivers/hid/ithc/ithc-debug.h | 7 + drivers/hid/ithc/ithc-dma.c | 312 ++++++++++++++++ drivers/hid/ithc/ithc-dma.h | 47 +++ drivers/hid/ithc/ithc-hid.c | 207 +++++++++++ drivers/hid/ithc/ithc-hid.h | 32 ++ drivers/hid/ithc/ithc-legacy.c | 254 +++++++++++++ drivers/hid/ithc/ithc-legacy.h | 8 + drivers/hid/ithc/ithc-main.c | 438 ++++++++++++++++++++++ drivers/hid/ithc/ithc-quickspi.c | 607 +++++++++++++++++++++++++++++++ drivers/hid/ithc/ithc-quickspi.h | 39 ++ drivers/hid/ithc/ithc-regs.c | 154 ++++++++ drivers/hid/ithc/ithc-regs.h | 211 +++++++++++ drivers/hid/ithc/ithc.h | 89 +++++ 18 files changed, 2575 insertions(+) create mode 100644 drivers/hid/ithc/Kbuild create mode 100644 drivers/hid/ithc/Kconfig create mode 100644 drivers/hid/ithc/ithc-debug.c create mode 100644 drivers/hid/ithc/ithc-debug.h create mode 100644 drivers/hid/ithc/ithc-dma.c create mode 100644 drivers/hid/ithc/ithc-dma.h create mode 100644 drivers/hid/ithc/ithc-hid.c create mode 100644 drivers/hid/ithc/ithc-hid.h create mode 100644 drivers/hid/ithc/ithc-legacy.c create mode 100644 drivers/hid/ithc/ithc-legacy.h create mode 100644 drivers/hid/ithc/ithc-main.c create mode 100644 drivers/hid/ithc/ithc-quickspi.c create mode 100644 drivers/hid/ithc/ithc-quickspi.h create mode 100644 drivers/hid/ithc/ithc-regs.c create mode 100644 drivers/hid/ithc/ithc-regs.h create mode 100644 drivers/hid/ithc/ithc.h diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index cecb264a229f99..2886bde63605c3 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1468,6 +1468,8 @@ source "drivers/hid/intel-thc-hid/Kconfig" source "drivers/hid/ipts/Kconfig" +source "drivers/hid/ithc/Kconfig" + endif # HID # USB support may be used with HID disabled diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index b46dec8ad0bfbb..fdc4e3d459d15a 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -182,3 +182,4 @@ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/ obj-$(CONFIG_HID_IPTS) += ipts/ +obj-$(CONFIG_HID_ITHC) += ithc/ diff --git a/drivers/hid/ithc/Kbuild b/drivers/hid/ithc/Kbuild new file mode 100644 index 00000000000000..4937ba1312973a --- /dev/null +++ b/drivers/hid/ithc/Kbuild @@ -0,0 +1,6 @@ +obj-$(CONFIG_HID_ITHC) := ithc.o + +ithc-objs := ithc-main.o ithc-regs.o ithc-dma.o ithc-hid.o ithc-legacy.o ithc-quickspi.o ithc-debug.o + +ccflags-y := -std=gnu11 -Wno-declaration-after-statement + diff --git a/drivers/hid/ithc/Kconfig b/drivers/hid/ithc/Kconfig new file mode 100644 index 00000000000000..ede71302360964 --- /dev/null +++ b/drivers/hid/ithc/Kconfig @@ -0,0 +1,12 @@ +config HID_ITHC + tristate "Intel Touch Host Controller" + depends on PCI + depends on HID + help + Say Y here if your system has a touchscreen using Intels + Touch Host Controller (ITHC / IPTS) technology. + + If unsure say N. + + To compile this driver as a module, choose M here: the + module will be called ithc. diff --git a/drivers/hid/ithc/ithc-debug.c b/drivers/hid/ithc/ithc-debug.c new file mode 100644 index 00000000000000..2d8c6afe99663a --- /dev/null +++ b/drivers/hid/ithc/ithc-debug.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +void ithc_log_regs(struct ithc *ithc) +{ + if (!ithc->prev_regs) + return; + u32 __iomem *cur = (__iomem void *)ithc->regs; + u32 *prev = (void *)ithc->prev_regs; + for (int i = 1024; i < sizeof(*ithc->regs) / 4; i++) { + u32 x = readl(cur + i); + if (x != prev[i]) { + pci_info(ithc->pci, "reg %04x: %08x -> %08x\n", i * 4, prev[i], x); + prev[i] = x; + } + } +} + +static ssize_t ithc_debugfs_cmd_write(struct file *f, const char __user *buf, size_t len, + loff_t *offset) +{ + // Debug commands consist of a single letter followed by a list of numbers (decimal or + // hexadecimal, space-separated). + struct ithc *ithc = file_inode(f)->i_private; + char cmd[256]; + if (!ithc || !ithc->pci) + return -ENODEV; + if (!len) + return -EINVAL; + if (len >= sizeof(cmd)) + return -EINVAL; + if (copy_from_user(cmd, buf, len)) + return -EFAULT; + cmd[len] = 0; + if (cmd[len-1] == '\n') + cmd[len-1] = 0; + pci_info(ithc->pci, "debug command: %s\n", cmd); + + // Parse the list of arguments into a u32 array. + u32 n = 0; + const char *s = cmd + 1; + u32 a[32]; + while (*s && *s != '\n') { + if (n >= ARRAY_SIZE(a)) + return -EINVAL; + if (*s++ != ' ') + return -EINVAL; + char *e; + a[n++] = simple_strtoul(s, &e, 0); + if (e == s) + return -EINVAL; + s = e; + } + ithc_log_regs(ithc); + + // Execute the command. + switch (cmd[0]) { + case 'x': // reset + ithc_reset(ithc); + break; + case 'w': // write register: offset mask value + if (n != 3 || (a[0] & 3)) + return -EINVAL; + pci_info(ithc->pci, "debug write 0x%04x = 0x%08x (mask 0x%08x)\n", + a[0], a[2], a[1]); + bitsl(((__iomem u32 *)ithc->regs) + a[0] / 4, a[1], a[2]); + break; + case 'r': // read register: offset + if (n != 1 || (a[0] & 3)) + return -EINVAL; + pci_info(ithc->pci, "debug read 0x%04x = 0x%08x\n", a[0], + readl(((__iomem u32 *)ithc->regs) + a[0] / 4)); + break; + case 's': // spi command: cmd offset len data... + // read config: s 4 0 64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + // set touch cfg: s 6 12 4 XX + if (n < 3 || a[2] > (n - 3) * 4) + return -EINVAL; + pci_info(ithc->pci, "debug spi command %u with %u bytes of data\n", a[0], a[2]); + if (!CHECK(ithc_spi_command, ithc, a[0], a[1], a[2], a + 3)) + for (u32 i = 0; i < (a[2] + 3) / 4; i++) + pci_info(ithc->pci, "resp %u = 0x%08x\n", i, a[3+i]); + break; + case 'd': // dma command: cmd len data... + // get report descriptor: d 7 8 0 0 + // enable multitouch: d 3 2 0x0105 + if (n < 1) + return -EINVAL; + pci_info(ithc->pci, "debug dma command with %u bytes of data\n", n * 4); + struct ithc_data data = { .type = ITHC_DATA_RAW, .size = n * 4, .data = a }; + if (ithc_dma_tx(ithc, &data)) + pci_err(ithc->pci, "dma tx failed\n"); + break; + default: + return -EINVAL; + } + ithc_log_regs(ithc); + return len; +} + +static struct dentry *dbg_dir; + +void __init ithc_debug_init_module(void) +{ + struct dentry *d = debugfs_create_dir(DEVNAME, NULL); + if (IS_ERR(d)) + pr_warn("failed to create debugfs dir (%li)\n", PTR_ERR(d)); + else + dbg_dir = d; +} + +void __exit ithc_debug_exit_module(void) +{ + debugfs_remove_recursive(dbg_dir); + dbg_dir = NULL; +} + +static const struct file_operations ithc_debugfops_cmd = { + .owner = THIS_MODULE, + .write = ithc_debugfs_cmd_write, +}; + +static void ithc_debugfs_devres_release(struct device *dev, void *res) +{ + struct dentry **dbgm = res; + debugfs_remove_recursive(*dbgm); +} + +int ithc_debug_init_device(struct ithc *ithc) +{ + if (!dbg_dir) + return -ENOENT; + struct dentry **dbgm = devres_alloc(ithc_debugfs_devres_release, sizeof(*dbgm), GFP_KERNEL); + if (!dbgm) + return -ENOMEM; + devres_add(&ithc->pci->dev, dbgm); + struct dentry *dbg = debugfs_create_dir(pci_name(ithc->pci), dbg_dir); + if (IS_ERR(dbg)) + return PTR_ERR(dbg); + *dbgm = dbg; + + struct dentry *cmd = debugfs_create_file("cmd", 0220, dbg, ithc, &ithc_debugfops_cmd); + if (IS_ERR(cmd)) + return PTR_ERR(cmd); + + return 0; +} + diff --git a/drivers/hid/ithc/ithc-debug.h b/drivers/hid/ithc/ithc-debug.h new file mode 100644 index 00000000000000..38c53d916bdb5e --- /dev/null +++ b/drivers/hid/ithc/ithc-debug.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +void ithc_debug_init_module(void); +void ithc_debug_exit_module(void); +int ithc_debug_init_device(struct ithc *ithc); +void ithc_log_regs(struct ithc *ithc); + diff --git a/drivers/hid/ithc/ithc-dma.c b/drivers/hid/ithc/ithc-dma.c new file mode 100644 index 00000000000000..bf4eab33062b0e --- /dev/null +++ b/drivers/hid/ithc/ithc-dma.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +// The THC uses tables of PRDs (physical region descriptors) to describe the TX and RX data buffers. +// Each PRD contains the DMA address and size of a block of DMA memory, and some status flags. +// This allows each data buffer to consist of multiple non-contiguous blocks of memory. + +static int ithc_dma_prd_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *p, + unsigned int num_buffers, unsigned int num_pages, enum dma_data_direction dir) +{ + p->num_pages = num_pages; + p->dir = dir; + // We allocate enough space to have one PRD per data buffer page, however if the data + // buffer pages happen to be contiguous, we can describe the buffer using fewer PRDs, so + // some will remain unused (which is fine). + p->size = round_up(num_buffers * num_pages * sizeof(struct ithc_phys_region_desc), PAGE_SIZE); + p->addr = dmam_alloc_coherent(&ithc->pci->dev, p->size, &p->dma_addr, GFP_KERNEL); + if (!p->addr) + return -ENOMEM; + if (p->dma_addr & (PAGE_SIZE - 1)) + return -EFAULT; + return 0; +} + +// Devres managed sg_table wrapper. +struct ithc_sg_table { + void *addr; + struct sg_table sgt; + enum dma_data_direction dir; +}; +static void ithc_dma_sgtable_free(struct sg_table *sgt) +{ + struct scatterlist *sg; + int i; + for_each_sgtable_sg(sgt, sg, i) { + struct page *p = sg_page(sg); + if (p) + __free_page(p); + } + sg_free_table(sgt); +} +static void ithc_dma_data_devres_release(struct device *dev, void *res) +{ + struct ithc_sg_table *sgt = res; + if (sgt->addr) + vunmap(sgt->addr); + dma_unmap_sgtable(dev, &sgt->sgt, sgt->dir, 0); + ithc_dma_sgtable_free(&sgt->sgt); +} + +static int ithc_dma_data_alloc(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, + struct ithc_dma_data_buffer *b) +{ + // We don't use dma_alloc_coherent() for data buffers, because they don't have to be + // coherent (they are unidirectional) or contiguous (we can use one PRD per page). + // We could use dma_alloc_noncontiguous(), however this still always allocates a single + // DMA mapped segment, which is more restrictive than what we need. + // Instead we use an sg_table of individually allocated pages. + struct page *pages[16]; + if (prds->num_pages == 0 || prds->num_pages > ARRAY_SIZE(pages)) + return -EINVAL; + b->active_idx = -1; + struct ithc_sg_table *sgt = devres_alloc( + ithc_dma_data_devres_release, sizeof(*sgt), GFP_KERNEL); + if (!sgt) + return -ENOMEM; + sgt->dir = prds->dir; + + if (!sg_alloc_table(&sgt->sgt, prds->num_pages, GFP_KERNEL)) { + struct scatterlist *sg; + int i; + bool ok = true; + for_each_sgtable_sg(&sgt->sgt, sg, i) { + // NOTE: don't need __GFP_DMA for PCI DMA + struct page *p = pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (!p) { + ok = false; + break; + } + sg_set_page(sg, p, PAGE_SIZE, 0); + } + if (ok && !dma_map_sgtable(&ithc->pci->dev, &sgt->sgt, prds->dir, 0)) { + devres_add(&ithc->pci->dev, sgt); + b->sgt = &sgt->sgt; + b->addr = sgt->addr = vmap(pages, prds->num_pages, 0, PAGE_KERNEL); + if (!b->addr) + return -ENOMEM; + return 0; + } + ithc_dma_sgtable_free(&sgt->sgt); + } + devres_free(sgt); + return -ENOMEM; +} + +static int ithc_dma_data_buffer_put(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, + struct ithc_dma_data_buffer *b, unsigned int idx) +{ + // Give a buffer to the THC. + struct ithc_phys_region_desc *prd = prds->addr; + prd += idx * prds->num_pages; + if (b->active_idx >= 0) { + pci_err(ithc->pci, "buffer already active\n"); + return -EINVAL; + } + b->active_idx = idx; + if (prds->dir == DMA_TO_DEVICE) { + // TX buffer: Caller should have already filled the data buffer, so just fill + // the PRD and flush. + // (TODO: Support multi-page TX buffers. So far no device seems to use or need + // these though.) + if (b->data_size > PAGE_SIZE) + return -EINVAL; + prd->addr = sg_dma_address(b->sgt->sgl) >> 10; + prd->size = b->data_size | PRD_FLAG_END; + flush_kernel_vmap_range(b->addr, b->data_size); + } else if (prds->dir == DMA_FROM_DEVICE) { + // RX buffer: Reset PRDs. + struct scatterlist *sg; + int i; + for_each_sgtable_dma_sg(b->sgt, sg, i) { + prd->addr = sg_dma_address(sg) >> 10; + prd->size = sg_dma_len(sg); + prd++; + } + prd[-1].size |= PRD_FLAG_END; + } + dma_wmb(); // for the prds + dma_sync_sgtable_for_device(&ithc->pci->dev, b->sgt, prds->dir); + return 0; +} + +static int ithc_dma_data_buffer_get(struct ithc *ithc, struct ithc_dma_prd_buffer *prds, + struct ithc_dma_data_buffer *b, unsigned int idx) +{ + // Take a buffer from the THC. + struct ithc_phys_region_desc *prd = prds->addr; + prd += idx * prds->num_pages; + // This is purely a sanity check. We don't strictly need the idx parameter for this + // function, because it should always be the same as active_idx, unless we have a bug. + if (b->active_idx != idx) { + pci_err(ithc->pci, "wrong buffer index\n"); + return -EINVAL; + } + b->active_idx = -1; + if (prds->dir == DMA_FROM_DEVICE) { + // RX buffer: Calculate actual received data size from PRDs. + dma_rmb(); // for the prds + b->data_size = 0; + struct scatterlist *sg; + int i; + for_each_sgtable_dma_sg(b->sgt, sg, i) { + unsigned int size = prd->size; + b->data_size += size & PRD_SIZE_MASK; + if (size & PRD_FLAG_END) + break; + if ((size & PRD_SIZE_MASK) != sg_dma_len(sg)) { + pci_err(ithc->pci, "truncated prd\n"); + break; + } + prd++; + } + invalidate_kernel_vmap_range(b->addr, b->data_size); + } + dma_sync_sgtable_for_cpu(&ithc->pci->dev, b->sgt, prds->dir); + return 0; +} + +int ithc_dma_rx_init(struct ithc *ithc, u8 channel) +{ + struct ithc_dma_rx *rx = &ithc->dma_rx[channel]; + mutex_init(&rx->mutex); + + // Allocate buffers. + unsigned int num_pages = (ithc->max_rx_size + PAGE_SIZE - 1) / PAGE_SIZE; + pci_dbg(ithc->pci, "allocating rx buffers: num = %u, size = %u, pages = %u\n", + NUM_RX_BUF, ithc->max_rx_size, num_pages); + CHECK_RET(ithc_dma_prd_alloc, ithc, &rx->prds, NUM_RX_BUF, num_pages, DMA_FROM_DEVICE); + for (unsigned int i = 0; i < NUM_RX_BUF; i++) + CHECK_RET(ithc_dma_data_alloc, ithc, &rx->prds, &rx->bufs[i]); + + // Init registers. + writeb(DMA_RX_CONTROL2_RESET, &ithc->regs->dma_rx[channel].control2); + lo_hi_writeq(rx->prds.dma_addr, &ithc->regs->dma_rx[channel].addr); + writeb(NUM_RX_BUF - 1, &ithc->regs->dma_rx[channel].num_bufs); + writeb(num_pages - 1, &ithc->regs->dma_rx[channel].num_prds); + u8 head = readb(&ithc->regs->dma_rx[channel].head); + if (head) { + pci_err(ithc->pci, "head is nonzero (%u)\n", head); + return -EIO; + } + + // Init buffers. + for (unsigned int i = 0; i < NUM_RX_BUF; i++) + CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, &rx->bufs[i], i); + + writeb(head ^ DMA_RX_WRAP_FLAG, &ithc->regs->dma_rx[channel].tail); + return 0; +} + +void ithc_dma_rx_enable(struct ithc *ithc, u8 channel) +{ + bitsb_set(&ithc->regs->dma_rx[channel].control, + DMA_RX_CONTROL_ENABLE | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_DATA); + CHECK(waitl, ithc, &ithc->regs->dma_rx[channel].status, + DMA_RX_STATUS_ENABLED, DMA_RX_STATUS_ENABLED); +} + +int ithc_dma_tx_init(struct ithc *ithc) +{ + struct ithc_dma_tx *tx = &ithc->dma_tx; + mutex_init(&tx->mutex); + + // Allocate buffers. + unsigned int num_pages = (ithc->max_tx_size + PAGE_SIZE - 1) / PAGE_SIZE; + pci_dbg(ithc->pci, "allocating tx buffers: size = %u, pages = %u\n", + ithc->max_tx_size, num_pages); + CHECK_RET(ithc_dma_prd_alloc, ithc, &tx->prds, 1, num_pages, DMA_TO_DEVICE); + CHECK_RET(ithc_dma_data_alloc, ithc, &tx->prds, &tx->buf); + + // Init registers. + lo_hi_writeq(tx->prds.dma_addr, &ithc->regs->dma_tx.addr); + writeb(num_pages - 1, &ithc->regs->dma_tx.num_prds); + + // Init buffers. + CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + return 0; +} + +static int ithc_dma_rx_unlocked(struct ithc *ithc, u8 channel) +{ + // Process all filled RX buffers from the ringbuffer. + struct ithc_dma_rx *rx = &ithc->dma_rx[channel]; + unsigned int n = rx->num_received; + u8 head_wrap = readb(&ithc->regs->dma_rx[channel].head); + while (1) { + u8 tail = n % NUM_RX_BUF; + u8 tail_wrap = tail | ((n / NUM_RX_BUF) & 1 ? 0 : DMA_RX_WRAP_FLAG); + writeb(tail_wrap, &ithc->regs->dma_rx[channel].tail); + // ringbuffer is full if tail_wrap == head_wrap + // ringbuffer is empty if tail_wrap == head_wrap ^ WRAP_FLAG + if (tail_wrap == (head_wrap ^ DMA_RX_WRAP_FLAG)) + return 0; + + // take the buffer that the device just filled + struct ithc_dma_data_buffer *b = &rx->bufs[n % NUM_RX_BUF]; + CHECK_RET(ithc_dma_data_buffer_get, ithc, &rx->prds, b, tail); + rx->num_received = ++n; + + // process data + struct ithc_data d; + if ((ithc->use_quickspi ? ithc_quickspi_decode_rx : ithc_legacy_decode_rx) + (ithc, b->addr, b->data_size, &d) < 0) { + pci_err(ithc->pci, "invalid dma rx data! channel %u, buffer %u, size %u: %*ph\n", + channel, tail, b->data_size, min((int)b->data_size, 64), b->addr); + print_hex_dump_debug(DEVNAME " data: ", DUMP_PREFIX_OFFSET, 32, 1, + b->addr, min(b->data_size, 0x400u), 0); + } else { + ithc_hid_process_data(ithc, &d); + } + + // give the buffer back to the device + CHECK_RET(ithc_dma_data_buffer_put, ithc, &rx->prds, b, tail); + } +} +int ithc_dma_rx(struct ithc *ithc, u8 channel) +{ + struct ithc_dma_rx *rx = &ithc->dma_rx[channel]; + mutex_lock(&rx->mutex); + int ret = ithc_dma_rx_unlocked(ithc, channel); + mutex_unlock(&rx->mutex); + return ret; +} + +static int ithc_dma_tx_unlocked(struct ithc *ithc, const struct ithc_data *data) +{ + // Send a single TX buffer to the THC. + pci_dbg(ithc->pci, "dma tx data type %u, size %u\n", data->type, data->size); + CHECK_RET(ithc_dma_data_buffer_get, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + + // Fill the TX buffer with header and data. + ssize_t sz; + if (data->type == ITHC_DATA_RAW) { + sz = min(data->size, ithc->max_tx_size); + memcpy(ithc->dma_tx.buf.addr, data->data, sz); + } else { + sz = (ithc->use_quickspi ? ithc_quickspi_encode_tx : ithc_legacy_encode_tx) + (ithc, data, ithc->dma_tx.buf.addr, ithc->max_tx_size); + } + ithc->dma_tx.buf.data_size = sz < 0 ? 0 : sz; + CHECK_RET(ithc_dma_data_buffer_put, ithc, &ithc->dma_tx.prds, &ithc->dma_tx.buf, 0); + if (sz < 0) { + pci_err(ithc->pci, "failed to encode tx data type %i, size %u, error %i\n", + data->type, data->size, (int)sz); + return -EINVAL; + } + + // Let the THC process the buffer. + bitsb_set(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND); + CHECK_RET(waitb, ithc, &ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND, 0); + writel(DMA_TX_STATUS_DONE, &ithc->regs->dma_tx.status); + return 0; +} +int ithc_dma_tx(struct ithc *ithc, const struct ithc_data *data) +{ + mutex_lock(&ithc->dma_tx.mutex); + int ret = ithc_dma_tx_unlocked(ithc, data); + mutex_unlock(&ithc->dma_tx.mutex); + return ret; +} + diff --git a/drivers/hid/ithc/ithc-dma.h b/drivers/hid/ithc/ithc-dma.h new file mode 100644 index 00000000000000..1749a5819b3e74 --- /dev/null +++ b/drivers/hid/ithc/ithc-dma.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +#define PRD_SIZE_MASK 0xffffff +#define PRD_FLAG_END 0x1000000 +#define PRD_FLAG_SUCCESS 0x2000000 +#define PRD_FLAG_ERROR 0x4000000 + +struct ithc_phys_region_desc { + u64 addr; // physical addr/1024 + u32 size; // num bytes, PRD_FLAG_END marks last prd for data split over multiple prds + u32 unused; +}; + +struct ithc_dma_prd_buffer { + void *addr; + dma_addr_t dma_addr; + u32 size; + u32 num_pages; // per data buffer + enum dma_data_direction dir; +}; + +struct ithc_dma_data_buffer { + void *addr; + struct sg_table *sgt; + int active_idx; + u32 data_size; +}; + +struct ithc_dma_tx { + struct mutex mutex; + struct ithc_dma_prd_buffer prds; + struct ithc_dma_data_buffer buf; +}; + +struct ithc_dma_rx { + struct mutex mutex; + u32 num_received; + struct ithc_dma_prd_buffer prds; + struct ithc_dma_data_buffer bufs[NUM_RX_BUF]; +}; + +int ithc_dma_rx_init(struct ithc *ithc, u8 channel); +void ithc_dma_rx_enable(struct ithc *ithc, u8 channel); +int ithc_dma_tx_init(struct ithc *ithc); +int ithc_dma_rx(struct ithc *ithc, u8 channel); +int ithc_dma_tx(struct ithc *ithc, const struct ithc_data *data); + diff --git a/drivers/hid/ithc/ithc-hid.c b/drivers/hid/ithc/ithc-hid.c new file mode 100644 index 00000000000000..065646ab499ef2 --- /dev/null +++ b/drivers/hid/ithc/ithc-hid.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +static int ithc_hid_start(struct hid_device *hdev) { return 0; } +static void ithc_hid_stop(struct hid_device *hdev) { } +static int ithc_hid_open(struct hid_device *hdev) { return 0; } +static void ithc_hid_close(struct hid_device *hdev) { } + +static int ithc_hid_parse(struct hid_device *hdev) +{ + struct ithc *ithc = hdev->driver_data; + const struct ithc_data get_report_desc = { .type = ITHC_DATA_REPORT_DESCRIPTOR }; + WRITE_ONCE(ithc->hid.parse_done, false); + for (int retries = 0; ; retries++) { + ithc_log_regs(ithc); + CHECK_RET(ithc_dma_tx, ithc, &get_report_desc); + if (wait_event_timeout(ithc->hid.wait_parse, READ_ONCE(ithc->hid.parse_done), + msecs_to_jiffies(200))) { + ithc_log_regs(ithc); + return 0; + } + if (retries > 5) { + ithc_log_regs(ithc); + pci_err(ithc->pci, "failed to read report descriptor\n"); + return -ETIMEDOUT; + } + pci_warn(ithc->pci, "failed to read report descriptor, retrying\n"); + } +} + +static int ithc_hid_raw_request(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, + size_t len, unsigned char rtype, int reqtype) +{ + struct ithc *ithc = hdev->driver_data; + if (!buf || !len) + return -EINVAL; + + struct ithc_data d = { .size = len, .data = buf }; + buf[0] = reportnum; + + if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) { + d.type = ITHC_DATA_OUTPUT_REPORT; + CHECK_RET(ithc_dma_tx, ithc, &d); + return 0; + } + + if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_SET_REPORT) { + d.type = ITHC_DATA_SET_FEATURE; + CHECK_RET(ithc_dma_tx, ithc, &d); + return 0; + } + + if (rtype == HID_FEATURE_REPORT && reqtype == HID_REQ_GET_REPORT) { + d.type = ITHC_DATA_GET_FEATURE; + d.data = &reportnum; + d.size = 1; + + // Prepare for response. + mutex_lock(&ithc->hid.get_feature_mutex); + ithc->hid.get_feature_buf = buf; + ithc->hid.get_feature_size = len; + mutex_unlock(&ithc->hid.get_feature_mutex); + + // Transmit 'get feature' request. + int r = CHECK(ithc_dma_tx, ithc, &d); + if (!r) { + r = wait_event_interruptible_timeout(ithc->hid.wait_get_feature, + !ithc->hid.get_feature_buf, msecs_to_jiffies(1000)); + if (!r) + r = -ETIMEDOUT; + else if (r < 0) + r = -EINTR; + else + r = 0; + } + + // If everything went ok, the buffer has been filled with the response data. + // Return the response size. + mutex_lock(&ithc->hid.get_feature_mutex); + ithc->hid.get_feature_buf = NULL; + if (!r) + r = ithc->hid.get_feature_size; + mutex_unlock(&ithc->hid.get_feature_mutex); + return r; + } + + pci_err(ithc->pci, "unhandled hid request %i %i for report id %i\n", + rtype, reqtype, reportnum); + return -EINVAL; +} + +// FIXME hid_input_report()/hid_parse_report() currently don't take const buffers, so we have to +// cast away the const to avoid a compiler warning... +#define NOCONST(x) ((void *)x) + +void ithc_hid_process_data(struct ithc *ithc, struct ithc_data *d) +{ + WARN_ON(!ithc->hid.dev); + if (!ithc->hid.dev) + return; + + switch (d->type) { + + case ITHC_DATA_IGNORE: + return; + + case ITHC_DATA_ERROR: + CHECK(ithc_reset, ithc); + return; + + case ITHC_DATA_REPORT_DESCRIPTOR: + // Response to the report descriptor request sent by ithc_hid_parse(). + CHECK(hid_parse_report, ithc->hid.dev, NOCONST(d->data), d->size); + WRITE_ONCE(ithc->hid.parse_done, true); + wake_up(&ithc->hid.wait_parse); + return; + + case ITHC_DATA_INPUT_REPORT: + { + // Standard HID input report. + int r = hid_input_report(ithc->hid.dev, HID_INPUT_REPORT, NOCONST(d->data), d->size, 1); + if (r < 0) { + pci_warn(ithc->pci, "hid_input_report failed with %i (size %u, report ID 0x%02x)\n", + r, d->size, d->size ? *(u8 *)d->data : 0); + print_hex_dump_debug(DEVNAME " report: ", DUMP_PREFIX_OFFSET, 32, 1, + d->data, min(d->size, 0x400u), 0); + } + return; + } + + case ITHC_DATA_GET_FEATURE: + { + // Response to a 'get feature' request sent by ithc_hid_raw_request(). + bool done = false; + mutex_lock(&ithc->hid.get_feature_mutex); + if (ithc->hid.get_feature_buf) { + if (d->size < ithc->hid.get_feature_size) + ithc->hid.get_feature_size = d->size; + memcpy(ithc->hid.get_feature_buf, d->data, ithc->hid.get_feature_size); + ithc->hid.get_feature_buf = NULL; + done = true; + } + mutex_unlock(&ithc->hid.get_feature_mutex); + if (done) { + wake_up(&ithc->hid.wait_get_feature); + } else { + // Received data without a matching request, or the request already + // timed out. (XXX What's the correct thing to do here?) + CHECK(hid_input_report, ithc->hid.dev, HID_FEATURE_REPORT, + NOCONST(d->data), d->size, 1); + } + return; + } + + default: + pci_err(ithc->pci, "unhandled data type %i\n", d->type); + return; + } +} + +static struct hid_ll_driver ithc_ll_driver = { + .start = ithc_hid_start, + .stop = ithc_hid_stop, + .open = ithc_hid_open, + .close = ithc_hid_close, + .parse = ithc_hid_parse, + .raw_request = ithc_hid_raw_request, +}; + +static void ithc_hid_devres_release(struct device *dev, void *res) +{ + struct hid_device **hidm = res; + if (*hidm) + hid_destroy_device(*hidm); +} + +int ithc_hid_init(struct ithc *ithc) +{ + struct hid_device **hidm = devres_alloc(ithc_hid_devres_release, sizeof(*hidm), GFP_KERNEL); + if (!hidm) + return -ENOMEM; + devres_add(&ithc->pci->dev, hidm); + struct hid_device *hid = hid_allocate_device(); + if (IS_ERR(hid)) + return PTR_ERR(hid); + *hidm = hid; + + strscpy(hid->name, DEVFULLNAME, sizeof(hid->name)); + strscpy(hid->phys, ithc->phys, sizeof(hid->phys)); + hid->ll_driver = &ithc_ll_driver; + hid->bus = BUS_PCI; + hid->vendor = ithc->vendor_id; + hid->product = ithc->product_id; + hid->version = 0x100; + hid->dev.parent = &ithc->pci->dev; + hid->driver_data = ithc; + + ithc->hid.dev = hid; + + init_waitqueue_head(&ithc->hid.wait_parse); + init_waitqueue_head(&ithc->hid.wait_get_feature); + mutex_init(&ithc->hid.get_feature_mutex); + + return 0; +} + diff --git a/drivers/hid/ithc/ithc-hid.h b/drivers/hid/ithc/ithc-hid.h new file mode 100644 index 00000000000000..599eb912c8c845 --- /dev/null +++ b/drivers/hid/ithc/ithc-hid.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +enum ithc_data_type { + ITHC_DATA_IGNORE, + ITHC_DATA_RAW, + ITHC_DATA_ERROR, + ITHC_DATA_REPORT_DESCRIPTOR, + ITHC_DATA_INPUT_REPORT, + ITHC_DATA_OUTPUT_REPORT, + ITHC_DATA_GET_FEATURE, + ITHC_DATA_SET_FEATURE, +}; + +struct ithc_data { + enum ithc_data_type type; + u32 size; + const void *data; +}; + +struct ithc_hid { + struct hid_device *dev; + bool parse_done; + wait_queue_head_t wait_parse; + wait_queue_head_t wait_get_feature; + struct mutex get_feature_mutex; + void *get_feature_buf; + size_t get_feature_size; +}; + +int ithc_hid_init(struct ithc *ithc); +void ithc_hid_process_data(struct ithc *ithc, struct ithc_data *d); + diff --git a/drivers/hid/ithc/ithc-legacy.c b/drivers/hid/ithc/ithc-legacy.c new file mode 100644 index 00000000000000..8883987fb35219 --- /dev/null +++ b/drivers/hid/ithc/ithc-legacy.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +#define DEVCFG_DMA_RX_SIZE(x) ((((x) & 0x3fff) + 1) << 6) +#define DEVCFG_DMA_TX_SIZE(x) (((((x) >> 14) & 0x3ff) + 1) << 6) + +#define DEVCFG_TOUCH_MASK 0x3f +#define DEVCFG_TOUCH_ENABLE BIT(0) +#define DEVCFG_TOUCH_PROP_DATA_ENABLE BIT(1) +#define DEVCFG_TOUCH_HID_REPORT_ENABLE BIT(2) +#define DEVCFG_TOUCH_POWER_STATE(x) (((x) & 7) << 3) +#define DEVCFG_TOUCH_UNKNOWN_6 BIT(6) + +#define DEVCFG_DEVICE_ID_TIC 0x43495424 // "$TIC" + +#define DEVCFG_SPI_CLKDIV(x) (((x) >> 1) & 7) +#define DEVCFG_SPI_CLKDIV_8 BIT(4) +#define DEVCFG_SPI_SUPPORTS_SINGLE BIT(5) +#define DEVCFG_SPI_SUPPORTS_DUAL BIT(6) +#define DEVCFG_SPI_SUPPORTS_QUAD BIT(7) +#define DEVCFG_SPI_MAX_TOUCH_POINTS(x) (((x) >> 8) & 0x3f) +#define DEVCFG_SPI_MIN_RESET_TIME(x) (((x) >> 16) & 0xf) +#define DEVCFG_SPI_NEEDS_HEARTBEAT BIT(20) // TODO implement heartbeat +#define DEVCFG_SPI_HEARTBEAT_INTERVAL(x) (((x) >> 21) & 7) +#define DEVCFG_SPI_UNKNOWN_25 BIT(25) +#define DEVCFG_SPI_UNKNOWN_26 BIT(26) +#define DEVCFG_SPI_UNKNOWN_27 BIT(27) +#define DEVCFG_SPI_DELAY(x) (((x) >> 28) & 7) // TODO use this +#define DEVCFG_SPI_USE_EXT_READ_CFG BIT(31) // TODO use this? + +struct ithc_device_config { // (Example values are from an SP7+.) + u32 irq_cause; // 00 = 0xe0000402 (0xe0000401 after DMA_RX_CODE_RESET) + u32 error; // 04 = 0x00000000 + u32 dma_buf_sizes; // 08 = 0x000a00ff + u32 touch_cfg; // 0c = 0x0000001c + u32 touch_state; // 10 = 0x0000001c + u32 device_id; // 14 = 0x43495424 = "$TIC" + u32 spi_config; // 18 = 0xfda00a2e + u16 vendor_id; // 1c = 0x045e = Microsoft Corp. + u16 product_id; // 1e = 0x0c1a + u32 revision; // 20 = 0x00000001 + u32 fw_version; // 24 = 0x05008a8b = 5.0.138.139 (this value looks more random on newer devices) + u32 command; // 28 = 0x00000000 + u32 fw_mode; // 2c = 0x00000000 (for fw update?) + u32 _unknown_30; // 30 = 0x00000000 + u8 eds_minor_ver; // 34 = 0x5e + u8 eds_major_ver; // 35 = 0x03 + u8 interface_rev; // 36 = 0x04 + u8 eu_kernel_ver; // 37 = 0x04 + u32 _unknown_38; // 38 = 0x000001c0 (0x000001c1 after DMA_RX_CODE_RESET) + u32 _unknown_3c; // 3c = 0x00000002 +}; +static_assert(sizeof(struct ithc_device_config) == 64); + +#define RX_CODE_INPUT_REPORT 3 +#define RX_CODE_FEATURE_REPORT 4 +#define RX_CODE_REPORT_DESCRIPTOR 5 +#define RX_CODE_RESET 7 + +#define TX_CODE_SET_FEATURE 3 +#define TX_CODE_GET_FEATURE 4 +#define TX_CODE_OUTPUT_REPORT 5 +#define TX_CODE_GET_REPORT_DESCRIPTOR 7 + +static int ithc_set_device_enabled(struct ithc *ithc, bool enable) +{ + u32 x = ithc->legacy_touch_cfg = + (ithc->legacy_touch_cfg & ~(u32)DEVCFG_TOUCH_MASK) | + DEVCFG_TOUCH_HID_REPORT_ENABLE | + (enable ? DEVCFG_TOUCH_ENABLE | DEVCFG_TOUCH_POWER_STATE(3) : 0); + return ithc_spi_command(ithc, SPI_CMD_CODE_WRITE, + offsetof(struct ithc_device_config, touch_cfg), sizeof(x), &x); +} + +int ithc_legacy_init(struct ithc *ithc) +{ + // Since we don't yet know which SPI config the device wants, use default speed and mode + // initially for reading config data. + CHECK(ithc_set_spi_config, ithc, 2, true, SPI_MODE_SINGLE, SPI_MODE_SINGLE); + + // Setting the following bit seems to make reading the config more reliable. + bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31); + + // Setting this bit may be necessary on ADL devices. + switch (ithc->pci->device) { + case PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2: + case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2: + case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2: + bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_5); + break; + } + + // Take the touch device out of reset. + bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0); + CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, 0); + for (int retries = 0; ; retries++) { + ithc_log_regs(ithc); + bitsl_set(&ithc->regs->control_bits, CONTROL_NRESET); + if (!waitl(ithc, &ithc->regs->irq_cause, 0xf, 2)) + break; + if (retries > 5) { + pci_err(ithc->pci, "failed to reset device, irq_cause = 0x%08x\n", + readl(&ithc->regs->irq_cause)); + return -ETIMEDOUT; + } + pci_warn(ithc->pci, "invalid irq_cause, retrying reset\n"); + bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0); + if (msleep_interruptible(1000)) + return -EINTR; + } + ithc_log_regs(ithc); + + CHECK(waitl, ithc, &ithc->regs->dma_rx[0].status, DMA_RX_STATUS_READY, DMA_RX_STATUS_READY); + + // Read configuration data. + u32 spi_cfg; + for (int retries = 0; ; retries++) { + ithc_log_regs(ithc); + struct ithc_device_config config = { 0 }; + CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, 0, sizeof(config), &config); + u32 *p = (void *)&config; + pci_info(ithc->pci, "config: %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); + if (config.device_id == DEVCFG_DEVICE_ID_TIC) { + spi_cfg = config.spi_config; + ithc->vendor_id = config.vendor_id; + ithc->product_id = config.product_id; + ithc->product_rev = config.revision; + ithc->max_rx_size = DEVCFG_DMA_RX_SIZE(config.dma_buf_sizes); + ithc->max_tx_size = DEVCFG_DMA_TX_SIZE(config.dma_buf_sizes); + ithc->legacy_touch_cfg = config.touch_cfg; + ithc->have_config = true; + break; + } + if (retries > 10) { + pci_err(ithc->pci, "failed to read config, unknown device ID 0x%08x\n", + config.device_id); + return -EIO; + } + pci_warn(ithc->pci, "failed to read config, retrying\n"); + if (msleep_interruptible(100)) + return -EINTR; + } + ithc_log_regs(ithc); + + // Apply SPI config and enable touch device. + CHECK_RET(ithc_set_spi_config, ithc, + DEVCFG_SPI_CLKDIV(spi_cfg), (spi_cfg & DEVCFG_SPI_CLKDIV_8) != 0, + spi_cfg & DEVCFG_SPI_SUPPORTS_QUAD ? SPI_MODE_QUAD : + spi_cfg & DEVCFG_SPI_SUPPORTS_DUAL ? SPI_MODE_DUAL : + SPI_MODE_SINGLE, + SPI_MODE_SINGLE); + CHECK_RET(ithc_set_device_enabled, ithc, true); + ithc_log_regs(ithc); + return 0; +} + +void ithc_legacy_exit(struct ithc *ithc) +{ + CHECK(ithc_set_device_enabled, ithc, false); +} + +int ithc_legacy_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest) +{ + const struct { + u32 code; + u32 data_size; + u32 _unknown[14]; + } *hdr = src; + + if (len < sizeof(*hdr)) + return -ENODATA; + // Note: RX data is not padded, even though TX data must be padded. + if (len != sizeof(*hdr) + hdr->data_size) + return -EMSGSIZE; + + dest->data = hdr + 1; + dest->size = hdr->data_size; + + switch (hdr->code) { + case RX_CODE_RESET: + // The THC sends a reset request when we need to reinitialize the device. + // This usually only happens if we send an invalid command or put the device + // in a bad state. + dest->type = ITHC_DATA_ERROR; + return 0; + case RX_CODE_REPORT_DESCRIPTOR: + // The descriptor is preceded by 8 nul bytes. + if (hdr->data_size < 8) + return -ENODATA; + dest->type = ITHC_DATA_REPORT_DESCRIPTOR; + dest->data = (char *)(hdr + 1) + 8; + dest->size = hdr->data_size - 8; + return 0; + case RX_CODE_INPUT_REPORT: + dest->type = ITHC_DATA_INPUT_REPORT; + return 0; + case RX_CODE_FEATURE_REPORT: + dest->type = ITHC_DATA_GET_FEATURE; + return 0; + default: + return -EINVAL; + } +} + +ssize_t ithc_legacy_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, + size_t maxlen) +{ + struct { + u32 code; + u32 data_size; + } *hdr = dest; + + size_t src_size = src->size; + const void *src_data = src->data; + const u64 get_report_desc_data = 0; + u32 code; + + switch (src->type) { + case ITHC_DATA_SET_FEATURE: + code = TX_CODE_SET_FEATURE; + break; + case ITHC_DATA_GET_FEATURE: + code = TX_CODE_GET_FEATURE; + break; + case ITHC_DATA_OUTPUT_REPORT: + code = TX_CODE_OUTPUT_REPORT; + break; + case ITHC_DATA_REPORT_DESCRIPTOR: + code = TX_CODE_GET_REPORT_DESCRIPTOR; + src_size = sizeof(get_report_desc_data); + src_data = &get_report_desc_data; + break; + default: + return -EINVAL; + } + + // Data must be padded to next 4-byte boundary. + size_t padded = round_up(src_size, 4); + if (sizeof(*hdr) + padded > maxlen) + return -EOVERFLOW; + + // Fill the TX buffer with header and data. + hdr->code = code; + hdr->data_size = src_size; + memcpy_and_pad(hdr + 1, padded, src_data, src_size, 0); + + return sizeof(*hdr) + padded; +} + diff --git a/drivers/hid/ithc/ithc-legacy.h b/drivers/hid/ithc/ithc-legacy.h new file mode 100644 index 00000000000000..28d69246207227 --- /dev/null +++ b/drivers/hid/ithc/ithc-legacy.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +int ithc_legacy_init(struct ithc *ithc); +void ithc_legacy_exit(struct ithc *ithc); +int ithc_legacy_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest); +ssize_t ithc_legacy_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, + size_t maxlen); + diff --git a/drivers/hid/ithc/ithc-main.c b/drivers/hid/ithc/ithc-main.c new file mode 100644 index 00000000000000..094d878d671b31 --- /dev/null +++ b/drivers/hid/ithc/ithc-main.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +MODULE_DESCRIPTION("Intel Touch Host Controller driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +static const struct pci_device_id ithc_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_LKF_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_LKF_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT1) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2) }, + // MTL and up are handled by drivers/hid/intel-thc-hid + {} +}; +MODULE_DEVICE_TABLE(pci, ithc_pci_tbl); + +// Module parameters + +static bool ithc_use_polling = false; +module_param_named(poll, ithc_use_polling, bool, 0); +MODULE_PARM_DESC(poll, "Use polling instead of interrupts"); + +// Since all known devices seem to use only channel 1, by default we disable channel 0. +static bool ithc_use_rx0 = false; +module_param_named(rx0, ithc_use_rx0, bool, 0); +MODULE_PARM_DESC(rx0, "Use DMA RX channel 0"); + +static bool ithc_use_rx1 = true; +module_param_named(rx1, ithc_use_rx1, bool, 0); +MODULE_PARM_DESC(rx1, "Use DMA RX channel 1"); + +static int ithc_active_ltr_us = -1; +module_param_named(activeltr, ithc_active_ltr_us, int, 0); +MODULE_PARM_DESC(activeltr, "Active LTR value override (in microseconds)"); + +static int ithc_idle_ltr_us = -1; +module_param_named(idleltr, ithc_idle_ltr_us, int, 0); +MODULE_PARM_DESC(idleltr, "Idle LTR value override (in microseconds)"); + +static unsigned int ithc_idle_delay_ms = 1000; +module_param_named(idledelay, ithc_idle_delay_ms, uint, 0); +MODULE_PARM_DESC(idleltr, "Minimum idle time before applying idle LTR value (in milliseconds)"); + +static bool ithc_log_regs_enabled = false; +module_param_named(logregs, ithc_log_regs_enabled, bool, 0); +MODULE_PARM_DESC(logregs, "Log changes in register values (for debugging)"); + +// Interrupts/polling + +static void ithc_disable_interrupts(struct ithc *ithc) +{ + writel(0, &ithc->regs->error_control); + bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_IRQ, 0); + bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_READY | DMA_RX_CONTROL_IRQ_DATA, 0); + bitsb(&ithc->regs->dma_rx[1].control, DMA_RX_CONTROL_IRQ_UNKNOWN_1 | DMA_RX_CONTROL_IRQ_ERROR | DMA_RX_CONTROL_IRQ_READY | DMA_RX_CONTROL_IRQ_DATA, 0); + bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_IRQ, 0); +} + +static void ithc_clear_dma_rx_interrupts(struct ithc *ithc, unsigned int channel) +{ + writel(DMA_RX_STATUS_ERROR | DMA_RX_STATUS_READY | DMA_RX_STATUS_HAVE_DATA, + &ithc->regs->dma_rx[channel].status); +} + +static void ithc_clear_interrupts(struct ithc *ithc) +{ + writel(0xffffffff, &ithc->regs->error_flags); + writel(ERROR_STATUS_DMA | ERROR_STATUS_SPI, &ithc->regs->error_status); + writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status); + ithc_clear_dma_rx_interrupts(ithc, 0); + ithc_clear_dma_rx_interrupts(ithc, 1); + writel(DMA_TX_STATUS_DONE | DMA_TX_STATUS_ERROR | DMA_TX_STATUS_UNKNOWN_2, + &ithc->regs->dma_tx.status); +} + +static void ithc_idle_timer_callback(struct timer_list *t) +{ + struct ithc *ithc = container_of(t, struct ithc, idle_timer); + ithc_set_ltr_idle(ithc); +} + +static void ithc_process(struct ithc *ithc) +{ + ithc_log_regs(ithc); + + // The THC automatically transitions from LTR idle to active at the start of a DMA transfer. + // It does not appear to automatically go back to idle, so we switch it back after a delay. + mod_timer(&ithc->idle_timer, jiffies + msecs_to_jiffies(ithc_idle_delay_ms)); + + bool rx0 = ithc_use_rx0 && (readl(&ithc->regs->dma_rx[0].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0; + bool rx1 = ithc_use_rx1 && (readl(&ithc->regs->dma_rx[1].status) & (DMA_RX_STATUS_ERROR | DMA_RX_STATUS_HAVE_DATA)) != 0; + + // Read and clear error bits + u32 err = readl(&ithc->regs->error_flags); + if (err) { + writel(err, &ithc->regs->error_flags); + if (err & ~ERROR_FLAG_DMA_RX_TIMEOUT) + pci_err(ithc->pci, "error flags: 0x%08x\n", err); + if (err & ERROR_FLAG_DMA_RX_TIMEOUT) + pci_err(ithc->pci, "DMA RX timeout/error (try decreasing activeltr/idleltr if this happens frequently)\n"); + } + + // Process DMA rx + if (ithc_use_rx0) { + ithc_clear_dma_rx_interrupts(ithc, 0); + if (rx0) + ithc_dma_rx(ithc, 0); + } + if (ithc_use_rx1) { + ithc_clear_dma_rx_interrupts(ithc, 1); + if (rx1) + ithc_dma_rx(ithc, 1); + } + + ithc_log_regs(ithc); +} + +static irqreturn_t ithc_interrupt_thread(int irq, void *arg) +{ + struct ithc *ithc = arg; + pci_dbg(ithc->pci, "IRQ! err=%08x/%08x/%08x, cmd=%02x/%08x, rx0=%02x/%08x, rx1=%02x/%08x, tx=%02x/%08x\n", + readl(&ithc->regs->error_control), readl(&ithc->regs->error_status), readl(&ithc->regs->error_flags), + readb(&ithc->regs->spi_cmd.control), readl(&ithc->regs->spi_cmd.status), + readb(&ithc->regs->dma_rx[0].control), readl(&ithc->regs->dma_rx[0].status), + readb(&ithc->regs->dma_rx[1].control), readl(&ithc->regs->dma_rx[1].status), + readb(&ithc->regs->dma_tx.control), readl(&ithc->regs->dma_tx.status)); + ithc_process(ithc); + return IRQ_HANDLED; +} + +static int ithc_poll_thread(void *arg) +{ + struct ithc *ithc = arg; + unsigned int sleep = 100; + while (!kthread_should_stop()) { + u32 n = ithc->dma_rx[1].num_received; + ithc_process(ithc); + // Decrease polling interval to 20ms if we received data, otherwise slowly + // increase it up to 200ms. + sleep = n != ithc->dma_rx[1].num_received ? 20 + : min(200u, sleep + (sleep >> 4) + 1); + msleep_interruptible(sleep); + } + return 0; +} + +// Device initialization and shutdown + +static void ithc_disable(struct ithc *ithc) +{ + bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE); + CHECK(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED); + bitsl(&ithc->regs->control_bits, CONTROL_NRESET, 0); + bitsb(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_SEND, 0); + bitsb(&ithc->regs->dma_tx.control, DMA_TX_CONTROL_SEND, 0); + bitsb(&ithc->regs->dma_rx[0].control, DMA_RX_CONTROL_ENABLE, 0); + bitsb(&ithc->regs->dma_rx[1].control, DMA_RX_CONTROL_ENABLE, 0); + ithc_disable_interrupts(ithc); + ithc_clear_interrupts(ithc); +} + +static int ithc_init_device(struct ithc *ithc) +{ + // Read ACPI config for QuickSPI mode + struct ithc_acpi_config cfg = { 0 }; + CHECK_RET(ithc_read_acpi_config, ithc, &cfg); + if (!cfg.has_config) + pci_info(ithc->pci, "no ACPI config, using legacy mode\n"); + else + ithc_print_acpi_config(ithc, &cfg); + ithc->use_quickspi = cfg.has_config; + + // Shut down device + ithc_log_regs(ithc); + bool was_enabled = (readl(&ithc->regs->control_bits) & CONTROL_NRESET) != 0; + ithc_disable(ithc); + CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_READY, CONTROL_READY); + ithc_log_regs(ithc); + + // If the device was previously enabled, wait a bit to make sure it's fully shut down. + if (was_enabled) + if (msleep_interruptible(100)) + return -EINTR; + + // Set Latency Tolerance Reporting config. The device will automatically + // apply these values depending on whether it is active or idle. + // If active value is too high, DMA buffer data can become truncated. + // By default, we set the active LTR value to 50us, and idle to 100ms. + u64 active_ltr_ns = ithc_active_ltr_us >= 0 ? (u64)ithc_active_ltr_us * 1000 + : cfg.has_config && cfg.has_active_ltr ? (u64)cfg.active_ltr << 10 + : 50 * 1000; + u64 idle_ltr_ns = ithc_idle_ltr_us >= 0 ? (u64)ithc_idle_ltr_us * 1000 + : cfg.has_config && cfg.has_idle_ltr ? (u64)cfg.idle_ltr << 10 + : 100 * 1000 * 1000; + ithc_set_ltr_config(ithc, active_ltr_ns, idle_ltr_ns); + + if (ithc->use_quickspi) + CHECK_RET(ithc_quickspi_init, ithc, &cfg); + else + CHECK_RET(ithc_legacy_init, ithc); + + return 0; +} + +int ithc_reset(struct ithc *ithc) +{ + // FIXME This should probably do devres_release_group()+ithc_start(). + // But because this is called during DMA processing, that would have to be done + // asynchronously (schedule_work()?). And with extra locking? + pci_err(ithc->pci, "reset\n"); + CHECK(ithc_init_device, ithc); + if (ithc_use_rx0) + ithc_dma_rx_enable(ithc, 0); + if (ithc_use_rx1) + ithc_dma_rx_enable(ithc, 1); + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "reset completed\n"); + return 0; +} + +static void ithc_stop(void *res) +{ + struct ithc *ithc = res; + pci_dbg(ithc->pci, "stopping\n"); + ithc_log_regs(ithc); + + if (ithc->poll_thread) + CHECK(kthread_stop, ithc->poll_thread); + if (ithc->irq >= 0) + disable_irq(ithc->irq); + if (ithc->use_quickspi) + ithc_quickspi_exit(ithc); + else + ithc_legacy_exit(ithc); + ithc_disable(ithc); + timer_delete_sync(&ithc->idle_timer); + + // Clear DMA config. + for (unsigned int i = 0; i < 2; i++) { + CHECK(waitl, ithc, &ithc->regs->dma_rx[i].status, DMA_RX_STATUS_ENABLED, 0); + lo_hi_writeq(0, &ithc->regs->dma_rx[i].addr); + writeb(0, &ithc->regs->dma_rx[i].num_bufs); + writeb(0, &ithc->regs->dma_rx[i].num_prds); + } + lo_hi_writeq(0, &ithc->regs->dma_tx.addr); + writeb(0, &ithc->regs->dma_tx.num_prds); + + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "stopped\n"); +} + +static void ithc_clear_drvdata(void *res) +{ + struct pci_dev *pci = res; + pci_set_drvdata(pci, NULL); +} + +static int ithc_start(struct pci_dev *pci) +{ + pci_dbg(pci, "starting\n"); + if (pci_get_drvdata(pci)) { + pci_err(pci, "device already initialized\n"); + return -EINVAL; + } + if (!devres_open_group(&pci->dev, ithc_start, GFP_KERNEL)) + return -ENOMEM; + + // Allocate/init main driver struct. + struct ithc *ithc = devm_kzalloc(&pci->dev, sizeof(*ithc), GFP_KERNEL); + if (!ithc) + return -ENOMEM; + ithc->irq = -1; + ithc->pci = pci; + snprintf(ithc->phys, sizeof(ithc->phys), "pci-%s/" DEVNAME, pci_name(pci)); + pci_set_drvdata(pci, ithc); + CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_clear_drvdata, pci); + if (ithc_log_regs_enabled) + ithc->prev_regs = devm_kzalloc(&pci->dev, sizeof(*ithc->prev_regs), GFP_KERNEL); + + // PCI initialization. + CHECK_RET(pcim_enable_device, pci); + pci_set_master(pci); + CHECK_RET(pcim_iomap_regions, pci, BIT(0), DEVNAME " regs"); + CHECK_RET(dma_set_mask_and_coherent, &pci->dev, DMA_BIT_MASK(64)); + CHECK_RET(pci_set_power_state, pci, PCI_D0); + ithc->regs = pcim_iomap_table(pci)[0]; + + // Allocate IRQ. + if (!ithc_use_polling) { + CHECK_RET(pci_alloc_irq_vectors, pci, 1, 1, PCI_IRQ_MSI | PCI_IRQ_MSIX); + ithc->irq = CHECK(pci_irq_vector, pci, 0); + if (ithc->irq < 0) + return ithc->irq; + } + + // Initialize THC and touch device. + CHECK_RET(ithc_init_device, ithc); + + // Initialize HID and DMA. + CHECK_RET(ithc_hid_init, ithc); + if (ithc_use_rx0) + CHECK_RET(ithc_dma_rx_init, ithc, 0); + if (ithc_use_rx1) + CHECK_RET(ithc_dma_rx_init, ithc, 1); + CHECK_RET(ithc_dma_tx_init, ithc); + + timer_setup(&ithc->idle_timer, ithc_idle_timer_callback, 0); + + // Add ithc_stop() callback AFTER setting up DMA buffers, so that polling/irqs/DMA are + // disabled BEFORE the buffers are freed. + CHECK_RET(devm_add_action_or_reset, &pci->dev, ithc_stop, ithc); + + // Start polling/IRQ. + if (ithc_use_polling) { + pci_info(pci, "using polling instead of irq\n"); + // Use a thread instead of simple timer because we want to be able to sleep. + ithc->poll_thread = kthread_run(ithc_poll_thread, ithc, DEVNAME "poll"); + if (IS_ERR(ithc->poll_thread)) { + int err = PTR_ERR(ithc->poll_thread); + ithc->poll_thread = NULL; + return err; + } + } else { + CHECK_RET(devm_request_threaded_irq, &pci->dev, ithc->irq, NULL, + ithc_interrupt_thread, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, DEVNAME, ithc); + } + + if (ithc_use_rx0) + ithc_dma_rx_enable(ithc, 0); + if (ithc_use_rx1) + ithc_dma_rx_enable(ithc, 1); + + // hid_add_device() can only be called after irq/polling is started and DMA is enabled, + // because it calls ithc_hid_parse() which reads the report descriptor via DMA. + CHECK_RET(hid_add_device, ithc->hid.dev); + + CHECK(ithc_debug_init_device, ithc); + + ithc_set_ltr_idle(ithc); + + pci_dbg(pci, "started\n"); + return 0; +} + +static int ithc_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + pci_dbg(pci, "device probe\n"); + return ithc_start(pci); +} + +static void ithc_remove(struct pci_dev *pci) +{ + pci_dbg(pci, "device remove\n"); + // all cleanup is handled by devres +} + +// For suspend/resume, we just deinitialize and reinitialize everything. +// TODO It might be cleaner to keep the HID device around, however we would then have to signal +// to userspace that the touch device has lost state and userspace needs to e.g. resend 'set +// feature' requests. Hidraw does not seem to have a facility to do that. +static int ithc_suspend(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm suspend\n"); + devres_release_group(dev, ithc_start); + return 0; +} + +static int ithc_resume(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm resume\n"); + return ithc_start(pci); +} + +static int ithc_freeze(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm freeze\n"); + devres_release_group(dev, ithc_start); + return 0; +} + +static int ithc_thaw(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm thaw\n"); + return ithc_start(pci); +} + +static int ithc_restore(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + pci_dbg(pci, "pm restore\n"); + return ithc_start(pci); +} + +static struct pci_driver ithc_driver = { + .name = DEVNAME, + .id_table = ithc_pci_tbl, + .probe = ithc_probe, + .remove = ithc_remove, + .driver.pm = &(const struct dev_pm_ops) { + .suspend = ithc_suspend, + .resume = ithc_resume, + .freeze = ithc_freeze, + .thaw = ithc_thaw, + .restore = ithc_restore, + }, + .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, +}; + +static int __init ithc_init(void) +{ + ithc_debug_init_module(); + return pci_register_driver(&ithc_driver); +} + +static void __exit ithc_exit(void) +{ + pci_unregister_driver(&ithc_driver); + ithc_debug_exit_module(); +} + +module_init(ithc_init); +module_exit(ithc_exit); + diff --git a/drivers/hid/ithc/ithc-quickspi.c b/drivers/hid/ithc/ithc-quickspi.c new file mode 100644 index 00000000000000..e2d1690b8cf8c1 --- /dev/null +++ b/drivers/hid/ithc/ithc-quickspi.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +// Some public THC/QuickSPI documentation can be found in: +// - Intel Firmware Support Package repo: https://github.com/intel/FSP +// - HID over SPI (HIDSPI) spec: https://www.microsoft.com/en-us/download/details.aspx?id=103325 + +#include "ithc.h" + +static const guid_t guid_hidspi = + GUID_INIT(0x6e2ac436, 0x0fcf, 0x41af, 0xa2, 0x65, 0xb3, 0x2a, 0x22, 0x0d, 0xcf, 0xab); +static const guid_t guid_thc_quickspi = + GUID_INIT(0x300d35b7, 0xac20, 0x413e, 0x8e, 0x9c, 0x92, 0xe4, 0xda, 0xfd, 0x0a, 0xfe); +static const guid_t guid_thc_ltr = + GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38); + +// TODO The HIDSPI spec says revision should be 3. Should we try both? +#define DSM_REV 2 + +struct hidspi_header { + u8 type; + u16 len; + u8 id; +} __packed; +static_assert(sizeof(struct hidspi_header) == 4); + +#define HIDSPI_INPUT_TYPE_DATA 1 +#define HIDSPI_INPUT_TYPE_RESET_RESPONSE 3 +#define HIDSPI_INPUT_TYPE_COMMAND_RESPONSE 4 +#define HIDSPI_INPUT_TYPE_GET_FEATURE_RESPONSE 5 +#define HIDSPI_INPUT_TYPE_DEVICE_DESCRIPTOR 7 +#define HIDSPI_INPUT_TYPE_REPORT_DESCRIPTOR 8 +#define HIDSPI_INPUT_TYPE_SET_FEATURE_RESPONSE 9 +#define HIDSPI_INPUT_TYPE_OUTPUT_REPORT_RESPONSE 10 +#define HIDSPI_INPUT_TYPE_GET_INPUT_REPORT_RESPONSE 11 + +#define HIDSPI_OUTPUT_TYPE_DEVICE_DESCRIPTOR_REQUEST 1 +#define HIDSPI_OUTPUT_TYPE_REPORT_DESCRIPTOR_REQUEST 2 +#define HIDSPI_OUTPUT_TYPE_SET_FEATURE 3 +#define HIDSPI_OUTPUT_TYPE_GET_FEATURE 4 +#define HIDSPI_OUTPUT_TYPE_OUTPUT_REPORT 5 +#define HIDSPI_OUTPUT_TYPE_INPUT_REPORT_REQUEST 6 +#define HIDSPI_OUTPUT_TYPE_COMMAND 7 + +struct hidspi_device_descriptor { + u16 wDeviceDescLength; + u16 bcdVersion; + u16 wReportDescLength; + u16 wMaxInputLength; + u16 wMaxOutputLength; + u16 wMaxFragmentLength; + u16 wVendorID; + u16 wProductID; + u16 wVersionID; + u16 wFlags; + u32 dwReserved; +}; +static_assert(sizeof(struct hidspi_device_descriptor) == 24); + +static int read_acpi_u32(struct ithc *ithc, const guid_t *guid, u32 func, u32 *dest) +{ + acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev); + union acpi_object *o = acpi_evaluate_dsm(handle, guid, DSM_REV, func, NULL); + if (!o) + return 0; + if (o->type != ACPI_TYPE_INTEGER) { + pci_err(ithc->pci, "DSM %pUl %u returned type %i instead of integer\n", + guid, func, o->type); + ACPI_FREE(o); + return -1; + } + pci_dbg(ithc->pci, "DSM %pUl %u = 0x%08x\n", guid, func, (u32)o->integer.value); + *dest = (u32)o->integer.value; + ACPI_FREE(o); + return 1; +} + +static int read_acpi_buf(struct ithc *ithc, const guid_t *guid, u32 func, size_t len, u8 *dest) +{ + acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev); + union acpi_object *o = acpi_evaluate_dsm(handle, guid, DSM_REV, func, NULL); + if (!o) + return 0; + if (o->type != ACPI_TYPE_BUFFER) { + pci_err(ithc->pci, "DSM %pUl %u returned type %i instead of buffer\n", + guid, func, o->type); + ACPI_FREE(o); + return -1; + } + if (o->buffer.length != len) { + pci_err(ithc->pci, "DSM %pUl %u returned len %u instead of %zu\n", + guid, func, o->buffer.length, len); + ACPI_FREE(o); + return -1; + } + memcpy(dest, o->buffer.pointer, len); + pci_dbg(ithc->pci, "DSM %pUl %u = 0x%02x\n", guid, func, dest[0]); + ACPI_FREE(o); + return 1; +} + +int ithc_read_acpi_config(struct ithc *ithc, struct ithc_acpi_config *cfg) +{ + int r; + acpi_handle handle = ACPI_HANDLE(&ithc->pci->dev); + + cfg->has_config = acpi_check_dsm(handle, &guid_hidspi, DSM_REV, BIT(0)); + if (!cfg->has_config) + return 0; + + // HIDSPI settings + + r = read_acpi_u32(ithc, &guid_hidspi, 1, &cfg->input_report_header_address); + if (r < 0) + return r; + cfg->has_input_report_header_address = r > 0; + if (r > 0 && cfg->input_report_header_address > 0xffffff) { + pci_err(ithc->pci, "Invalid input report header address 0x%x\n", + cfg->input_report_header_address); + return -1; + } + + r = read_acpi_u32(ithc, &guid_hidspi, 2, &cfg->input_report_body_address); + if (r < 0) + return r; + cfg->has_input_report_body_address = r > 0; + if (r > 0 && cfg->input_report_body_address > 0xffffff) { + pci_err(ithc->pci, "Invalid input report body address 0x%x\n", + cfg->input_report_body_address); + return -1; + } + + r = read_acpi_u32(ithc, &guid_hidspi, 3, &cfg->output_report_body_address); + if (r < 0) + return r; + cfg->has_output_report_body_address = r > 0; + if (r > 0 && cfg->output_report_body_address > 0xffffff) { + pci_err(ithc->pci, "Invalid output report body address 0x%x\n", + cfg->output_report_body_address); + return -1; + } + + r = read_acpi_buf(ithc, &guid_hidspi, 4, sizeof(cfg->read_opcode), &cfg->read_opcode); + if (r < 0) + return r; + cfg->has_read_opcode = r > 0; + + r = read_acpi_buf(ithc, &guid_hidspi, 5, sizeof(cfg->write_opcode), &cfg->write_opcode); + if (r < 0) + return r; + cfg->has_write_opcode = r > 0; + + u32 flags; + r = read_acpi_u32(ithc, &guid_hidspi, 6, &flags); + if (r < 0) + return r; + cfg->has_read_mode = cfg->has_write_mode = r > 0; + if (r > 0) { + cfg->read_mode = (flags >> 14) & 3; + cfg->write_mode = flags & BIT(13) ? cfg->read_mode : SPI_MODE_SINGLE; + } + + // Quick SPI settings + + r = read_acpi_u32(ithc, &guid_thc_quickspi, 1, &cfg->spi_frequency); + if (r < 0) + return r; + cfg->has_spi_frequency = r > 0; + + r = read_acpi_u32(ithc, &guid_thc_quickspi, 2, &cfg->limit_packet_size); + if (r < 0) + return r; + cfg->has_limit_packet_size = r > 0; + + r = read_acpi_u32(ithc, &guid_thc_quickspi, 3, &cfg->tx_delay); + if (r < 0) + return r; + cfg->has_tx_delay = r > 0; + if (r > 0) + cfg->tx_delay &= 0xffff; + + // LTR settings + + r = read_acpi_u32(ithc, &guid_thc_ltr, 1, &cfg->active_ltr); + if (r < 0) + return r; + cfg->has_active_ltr = r > 0; + if (r > 0 && (!cfg->active_ltr || cfg->active_ltr > 0x3ff)) { + if (cfg->active_ltr != 0xffffffff) + pci_warn(ithc->pci, "Ignoring invalid active LTR value 0x%x\n", + cfg->active_ltr); + cfg->active_ltr = 500; + } + + r = read_acpi_u32(ithc, &guid_thc_ltr, 2, &cfg->idle_ltr); + if (r < 0) + return r; + cfg->has_idle_ltr = r > 0; + if (r > 0 && (!cfg->idle_ltr || cfg->idle_ltr > 0x3ff)) { + if (cfg->idle_ltr != 0xffffffff) + pci_warn(ithc->pci, "Ignoring invalid idle LTR value 0x%x\n", + cfg->idle_ltr); + cfg->idle_ltr = 500; + if (cfg->has_active_ltr && cfg->active_ltr > cfg->idle_ltr) + cfg->idle_ltr = cfg->active_ltr; + } + + return 0; +} + +void ithc_print_acpi_config(struct ithc *ithc, const struct ithc_acpi_config *cfg) +{ + if (!cfg->has_config) { + pci_info(ithc->pci, "No ACPI config"); + return; + } + + char input_report_header_address[16] = "-"; + if (cfg->has_input_report_header_address) + sprintf(input_report_header_address, "0x%x", cfg->input_report_header_address); + char input_report_body_address[16] = "-"; + if (cfg->has_input_report_body_address) + sprintf(input_report_body_address, "0x%x", cfg->input_report_body_address); + char output_report_body_address[16] = "-"; + if (cfg->has_output_report_body_address) + sprintf(output_report_body_address, "0x%x", cfg->output_report_body_address); + char read_opcode[16] = "-"; + if (cfg->has_read_opcode) + sprintf(read_opcode, "0x%02x", cfg->read_opcode); + char write_opcode[16] = "-"; + if (cfg->has_write_opcode) + sprintf(write_opcode, "0x%02x", cfg->write_opcode); + char read_mode[16] = "-"; + if (cfg->has_read_mode) + sprintf(read_mode, "%i", cfg->read_mode); + char write_mode[16] = "-"; + if (cfg->has_write_mode) + sprintf(write_mode, "%i", cfg->write_mode); + char spi_frequency[16] = "-"; + if (cfg->has_spi_frequency) + sprintf(spi_frequency, "%u", cfg->spi_frequency); + char limit_packet_size[16] = "-"; + if (cfg->has_limit_packet_size) + sprintf(limit_packet_size, "%u", cfg->limit_packet_size); + char tx_delay[16] = "-"; + if (cfg->has_tx_delay) + sprintf(tx_delay, "%u", cfg->tx_delay); + char active_ltr[16] = "-"; + if (cfg->has_active_ltr) + sprintf(active_ltr, "%u", cfg->active_ltr); + char idle_ltr[16] = "-"; + if (cfg->has_idle_ltr) + sprintf(idle_ltr, "%u", cfg->idle_ltr); + + pci_info(ithc->pci, "ACPI config: InputHeaderAddr=%s InputBodyAddr=%s OutputBodyAddr=%s ReadOpcode=%s WriteOpcode=%s ReadMode=%s WriteMode=%s Frequency=%s LimitPacketSize=%s TxDelay=%s ActiveLTR=%s IdleLTR=%s\n", + input_report_header_address, input_report_body_address, output_report_body_address, + read_opcode, write_opcode, read_mode, write_mode, + spi_frequency, limit_packet_size, tx_delay, active_ltr, idle_ltr); +} + +static void set_opcode(struct ithc *ithc, size_t i, u8 opcode) +{ + writeb(opcode, &ithc->regs->opcode[i].header); + writeb(opcode, &ithc->regs->opcode[i].single); + writeb(opcode, &ithc->regs->opcode[i].dual); + writeb(opcode, &ithc->regs->opcode[i].quad); +} + +static int ithc_quickspi_init_regs(struct ithc *ithc, const struct ithc_acpi_config *cfg) +{ + pci_dbg(ithc->pci, "initializing QuickSPI registers\n"); + + // SPI frequency and mode + if (!cfg->has_spi_frequency || !cfg->spi_frequency) { + pci_err(ithc->pci, "Missing SPI frequency in configuration\n"); + return -EINVAL; + } + unsigned int clkdiv = DIV_ROUND_UP(SPI_CLK_FREQ_BASE, cfg->spi_frequency); + bool clkdiv8 = clkdiv > 7; + if (clkdiv8) + clkdiv = min(7u, DIV_ROUND_UP(clkdiv, 8u)); + if (!clkdiv) + clkdiv = 1; + CHECK_RET(ithc_set_spi_config, ithc, clkdiv, clkdiv8, + cfg->has_read_mode ? cfg->read_mode : SPI_MODE_SINGLE, + cfg->has_write_mode ? cfg->write_mode : SPI_MODE_SINGLE); + + // SPI addresses and opcodes + if (cfg->has_input_report_header_address) + writel(cfg->input_report_header_address, &ithc->regs->spi_header_addr); + if (cfg->has_input_report_body_address) { + writel(cfg->input_report_body_address, &ithc->regs->dma_rx[0].spi_addr); + writel(cfg->input_report_body_address, &ithc->regs->dma_rx[1].spi_addr); + } + if (cfg->has_output_report_body_address) + writel(cfg->output_report_body_address, &ithc->regs->dma_tx.spi_addr); + + switch (ithc->pci->device) { + // LKF/TGL don't support QuickSPI. + // For ADL, opcode layout is RX/TX/unused. + case PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2: + case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2: + case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1: + case PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2: + if (cfg->has_read_opcode) { + set_opcode(ithc, 0, cfg->read_opcode); + } + if (cfg->has_write_opcode) { + set_opcode(ithc, 1, cfg->write_opcode); + } + break; + // For MTL, opcode layout was changed to RX/RX/TX. + // (RPL layout is unknown.) + default: + if (cfg->has_read_opcode) { + set_opcode(ithc, 0, cfg->read_opcode); + set_opcode(ithc, 1, cfg->read_opcode); + } + if (cfg->has_write_opcode) { + set_opcode(ithc, 2, cfg->write_opcode); + } + break; + } + + ithc_log_regs(ithc); + + // The rest... + bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31); + + bitsl(&ithc->regs->quickspi_config1, + QUICKSPI_CONFIG1_UNKNOWN_0(0xff) | QUICKSPI_CONFIG1_UNKNOWN_5(0xff) | + QUICKSPI_CONFIG1_UNKNOWN_10(0xff) | QUICKSPI_CONFIG1_UNKNOWN_16(0xffff), + QUICKSPI_CONFIG1_UNKNOWN_0(4) | QUICKSPI_CONFIG1_UNKNOWN_5(4) | + QUICKSPI_CONFIG1_UNKNOWN_10(22) | QUICKSPI_CONFIG1_UNKNOWN_16(2)); + + bitsl(&ithc->regs->quickspi_config2, + QUICKSPI_CONFIG2_UNKNOWN_0(0xff) | QUICKSPI_CONFIG2_UNKNOWN_5(0xff) | + QUICKSPI_CONFIG2_UNKNOWN_12(0xff), + QUICKSPI_CONFIG2_UNKNOWN_0(8) | QUICKSPI_CONFIG2_UNKNOWN_5(14) | + QUICKSPI_CONFIG2_UNKNOWN_12(2)); + + u32 pktsize = cfg->has_limit_packet_size && cfg->limit_packet_size == 1 ? 4 : 0x80; + bitsl(&ithc->regs->spi_config, + SPI_CONFIG_READ_PACKET_SIZE(0xfff) | SPI_CONFIG_WRITE_PACKET_SIZE(0xfff), + SPI_CONFIG_READ_PACKET_SIZE(pktsize) | SPI_CONFIG_WRITE_PACKET_SIZE(pktsize)); + + bitsl_set(&ithc->regs->quickspi_config2, + QUICKSPI_CONFIG2_UNKNOWN_16 | QUICKSPI_CONFIG2_UNKNOWN_17); + bitsl(&ithc->regs->quickspi_config2, + QUICKSPI_CONFIG2_DISABLE_READ_ADDRESS_INCREMENT | + QUICKSPI_CONFIG2_DISABLE_WRITE_ADDRESS_INCREMENT | + QUICKSPI_CONFIG2_ENABLE_WRITE_STREAMING_MODE, 0); + + return 0; +} + +static int wait_for_report(struct ithc *ithc) +{ + CHECK_RET(waitl, ithc, &ithc->regs->dma_rx[0].status, + DMA_RX_STATUS_READY, DMA_RX_STATUS_READY); + writel(DMA_RX_STATUS_READY, &ithc->regs->dma_rx[0].status); + + u32 h = readl(&ithc->regs->input_header); + ithc_log_regs(ithc); + if (INPUT_HEADER_SYNC(h) != INPUT_HEADER_SYNC_VALUE + || INPUT_HEADER_VERSION(h) != INPUT_HEADER_VERSION_VALUE) { + pci_err(ithc->pci, "invalid input report frame header 0x%08x\n", h); + return -ENODATA; + } + return INPUT_HEADER_REPORT_LENGTH(h) * 4; +} + +static int ithc_quickspi_init_hidspi(struct ithc *ithc, const struct ithc_acpi_config *cfg) +{ + pci_dbg(ithc->pci, "initializing HIDSPI\n"); + + // HIDSPI initialization sequence: + // "1. The host shall invoke the ACPI reset method to clear the device state." + acpi_status s = acpi_evaluate_object(ACPI_HANDLE(&ithc->pci->dev), "_RST", NULL, NULL); + if (ACPI_FAILURE(s)) { + pci_err(ithc->pci, "ACPI reset failed\n"); + return -EIO; + } + + bitsl(&ithc->regs->control_bits, CONTROL_QUIESCE, 0); + + // "2. Within 1 second, the device shall signal an interrupt and make available to the host + // an input report containing a device reset response." + int size = wait_for_report(ithc); + if (size < 0) + return size; + if (size < sizeof(struct hidspi_header)) { + pci_err(ithc->pci, "SPI data size too small for reset response (%u)\n", size); + return -EMSGSIZE; + } + + // "3. The host shall read the reset response from the device at the Input Report addresses + // specified in ACPI." + u32 in_addr = cfg->has_input_report_body_address ? cfg->input_report_body_address : 0x1000; + struct { + struct hidspi_header header; + union { + struct hidspi_device_descriptor device_desc; + u32 data[16]; + }; + } resp = { 0 }; + if (size > sizeof(resp)) { + pci_err(ithc->pci, "SPI data size for reset response too big (%u)\n", size); + return -EMSGSIZE; + } + CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, in_addr, size, &resp); + if (resp.header.type != HIDSPI_INPUT_TYPE_RESET_RESPONSE) { + pci_err(ithc->pci, "received type %i instead of reset response\n", resp.header.type); + return -ENOMSG; + } + + // "4. The host shall then write an Output Report to the device at the Output Report Address + // specified in ACPI, requesting the Device Descriptor from the device." + u32 out_addr = cfg->has_output_report_body_address ? cfg->output_report_body_address : 0x1000; + struct hidspi_header req = { .type = HIDSPI_OUTPUT_TYPE_DEVICE_DESCRIPTOR_REQUEST }; + CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_WRITE, out_addr, sizeof(req), &req); + + // "5. Within 1 second, the device shall signal an interrupt and make available to the host + // an input report containing the Device Descriptor." + size = wait_for_report(ithc); + if (size < 0) + return size; + if (size < sizeof(resp.header) + sizeof(resp.device_desc)) { + pci_err(ithc->pci, "SPI data size too small for device descriptor (%u)\n", size); + return -EMSGSIZE; + } + + // "6. The host shall read the Device Descriptor from the Input Report addresses specified + // in ACPI." + if (size > sizeof(resp)) { + pci_err(ithc->pci, "SPI data size for device descriptor too big (%u)\n", size); + return -EMSGSIZE; + } + memset(&resp, 0, sizeof(resp)); + CHECK_RET(ithc_spi_command, ithc, SPI_CMD_CODE_READ, in_addr, size, &resp); + if (resp.header.type != HIDSPI_INPUT_TYPE_DEVICE_DESCRIPTOR) { + pci_err(ithc->pci, "received type %i instead of device descriptor\n", + resp.header.type); + return -ENOMSG; + } + struct hidspi_device_descriptor *d = &resp.device_desc; + if (resp.header.len < sizeof(*d)) { + pci_err(ithc->pci, "response too small for device descriptor (%u)\n", + resp.header.len); + return -EMSGSIZE; + } + if (d->wDeviceDescLength != sizeof(*d)) { + pci_err(ithc->pci, "invalid device descriptor length (%u)\n", + d->wDeviceDescLength); + return -EMSGSIZE; + } + + pci_info(ithc->pci, "Device descriptor: bcdVersion=0x%04x wReportDescLength=%u wMaxInputLength=%u wMaxOutputLength=%u wMaxFragmentLength=%u wVendorID=0x%04x wProductID=0x%04x wVersionID=0x%04x wFlags=0x%04x dwReserved=0x%08x\n", + d->bcdVersion, d->wReportDescLength, + d->wMaxInputLength, d->wMaxOutputLength, d->wMaxFragmentLength, + d->wVendorID, d->wProductID, d->wVersionID, + d->wFlags, d->dwReserved); + + ithc->vendor_id = d->wVendorID; + ithc->product_id = d->wProductID; + ithc->product_rev = d->wVersionID; + ithc->max_rx_size = max_t(u32, d->wMaxInputLength, + d->wReportDescLength + sizeof(struct hidspi_header)); + ithc->max_tx_size = d->wMaxOutputLength; + ithc->have_config = true; + + // "7. The device and host shall then enter their "Ready" states - where the device may + // begin sending Input Reports, and the device shall be prepared for Output Reports from + // the host." + + return 0; +} + +int ithc_quickspi_init(struct ithc *ithc, const struct ithc_acpi_config *cfg) +{ + bitsl_set(&ithc->regs->control_bits, CONTROL_QUIESCE); + CHECK_RET(waitl, ithc, &ithc->regs->control_bits, CONTROL_IS_QUIESCED, CONTROL_IS_QUIESCED); + + ithc_log_regs(ithc); + CHECK_RET(ithc_quickspi_init_regs, ithc, cfg); + ithc_log_regs(ithc); + CHECK_RET(ithc_quickspi_init_hidspi, ithc, cfg); + ithc_log_regs(ithc); + + // This value is set to 2 in ithc_quickspi_init_regs(). It needs to be set to 1 here, + // otherwise DMA will not work. Maybe selects between DMA and PIO mode? + bitsl(&ithc->regs->quickspi_config1, + QUICKSPI_CONFIG1_UNKNOWN_16(0xffff), QUICKSPI_CONFIG1_UNKNOWN_16(1)); + + // TODO Do we need to set any of the following bits here? + //bitsb_set(&ithc->regs->dma_rx[1].control2, DMA_RX_CONTROL2_UNKNOWN_4); + //bitsb_set(&ithc->regs->dma_rx[0].control2, DMA_RX_CONTROL2_UNKNOWN_5); + //bitsb_set(&ithc->regs->dma_rx[1].control2, DMA_RX_CONTROL2_UNKNOWN_5); + //bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_3); + //bitsl_set(&ithc->regs->dma_rx[0].init_unknown, INIT_UNKNOWN_31); + + ithc_log_regs(ithc); + + return 0; +} + +void ithc_quickspi_exit(struct ithc *ithc) +{ + // TODO Should we send HIDSPI 'power off' command? + //struct hidspi_header h = { .type = HIDSPI_OUTPUT_TYPE_COMMAND, .id = 3, }; + //struct ithc_data d = { .type = ITHC_DATA_RAW, .data = &h, .size = sizeof(h) }; + //CHECK(ithc_dma_tx, ithc, &d); // or ithc_spi_command() +} + +int ithc_quickspi_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest) +{ + const struct hidspi_header *hdr = src; + + if (len < sizeof(*hdr)) + return -ENODATA; + // TODO Do we need to handle HIDSPI packet fragmentation? + if (len < sizeof(*hdr) + hdr->len) + return -EMSGSIZE; + if (len > round_up(sizeof(*hdr) + hdr->len, 4)) + return -EMSGSIZE; + + switch (hdr->type) { + case HIDSPI_INPUT_TYPE_RESET_RESPONSE: + // TODO "When the device detects an error condition, it may interrupt and make + // available to the host an Input Report containing an unsolicited Reset Response. + // After receiving an unsolicited Reset Response, the host shall initiate the + // request procedure from step (4) in the [HIDSPI initialization] process." + dest->type = ITHC_DATA_ERROR; + return 0; + case HIDSPI_INPUT_TYPE_REPORT_DESCRIPTOR: + dest->type = ITHC_DATA_REPORT_DESCRIPTOR; + dest->data = hdr + 1; + dest->size = hdr->len; + return 0; + case HIDSPI_INPUT_TYPE_DATA: + case HIDSPI_INPUT_TYPE_GET_INPUT_REPORT_RESPONSE: + dest->type = ITHC_DATA_INPUT_REPORT; + dest->data = &hdr->id; + dest->size = hdr->len + 1; + return 0; + case HIDSPI_INPUT_TYPE_GET_FEATURE_RESPONSE: + dest->type = ITHC_DATA_GET_FEATURE; + dest->data = &hdr->id; + dest->size = hdr->len + 1; + return 0; + case HIDSPI_INPUT_TYPE_SET_FEATURE_RESPONSE: + case HIDSPI_INPUT_TYPE_OUTPUT_REPORT_RESPONSE: + dest->type = ITHC_DATA_IGNORE; + return 0; + default: + return -EINVAL; + } +} + +ssize_t ithc_quickspi_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, + size_t maxlen) +{ + struct hidspi_header *hdr = dest; + + size_t src_size = src->size; + const u8 *src_data = src->data; + u8 type; + + switch (src->type) { + case ITHC_DATA_SET_FEATURE: + type = HIDSPI_OUTPUT_TYPE_SET_FEATURE; + break; + case ITHC_DATA_GET_FEATURE: + type = HIDSPI_OUTPUT_TYPE_GET_FEATURE; + break; + case ITHC_DATA_OUTPUT_REPORT: + type = HIDSPI_OUTPUT_TYPE_OUTPUT_REPORT; + break; + case ITHC_DATA_REPORT_DESCRIPTOR: + type = HIDSPI_OUTPUT_TYPE_REPORT_DESCRIPTOR_REQUEST; + src_size = 0; + break; + default: + return -EINVAL; + } + + u8 id = 0; + if (src_size) { + id = *src_data++; + src_size--; + } + + // Data must be padded to next 4-byte boundary. + size_t padded = round_up(src_size, 4); + if (sizeof(*hdr) + padded > maxlen) + return -EOVERFLOW; + + // Fill the TX buffer with header and data. + hdr->type = type; + hdr->len = (u16)src_size; + hdr->id = id; + memcpy_and_pad(hdr + 1, padded, src_data, src_size, 0); + + return sizeof(*hdr) + padded; +} + diff --git a/drivers/hid/ithc/ithc-quickspi.h b/drivers/hid/ithc/ithc-quickspi.h new file mode 100644 index 00000000000000..74d882f6b2f0a7 --- /dev/null +++ b/drivers/hid/ithc/ithc-quickspi.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +struct ithc_acpi_config { + bool has_config: 1; + bool has_input_report_header_address: 1; + bool has_input_report_body_address: 1; + bool has_output_report_body_address: 1; + bool has_read_opcode: 1; + bool has_write_opcode: 1; + bool has_read_mode: 1; + bool has_write_mode: 1; + bool has_spi_frequency: 1; + bool has_limit_packet_size: 1; + bool has_tx_delay: 1; + bool has_active_ltr: 1; + bool has_idle_ltr: 1; + u32 input_report_header_address; + u32 input_report_body_address; + u32 output_report_body_address; + u8 read_opcode; + u8 write_opcode; + u8 read_mode; + u8 write_mode; + u32 spi_frequency; + u32 limit_packet_size; + u32 tx_delay; // us/10 // TODO use? + u32 active_ltr; // ns/1024 + u32 idle_ltr; // ns/1024 +}; + +int ithc_read_acpi_config(struct ithc *ithc, struct ithc_acpi_config *cfg); +void ithc_print_acpi_config(struct ithc *ithc, const struct ithc_acpi_config *cfg); + +int ithc_quickspi_init(struct ithc *ithc, const struct ithc_acpi_config *cfg); +void ithc_quickspi_exit(struct ithc *ithc); +int ithc_quickspi_decode_rx(struct ithc *ithc, const void *src, size_t len, struct ithc_data *dest); +ssize_t ithc_quickspi_encode_tx(struct ithc *ithc, const struct ithc_data *src, void *dest, + size_t maxlen); + diff --git a/drivers/hid/ithc/ithc-regs.c b/drivers/hid/ithc/ithc-regs.c new file mode 100644 index 00000000000000..c0f13506af205e --- /dev/null +++ b/drivers/hid/ithc/ithc-regs.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause + +#include "ithc.h" + +#define reg_num(r) (0x1fff & (u16)(__force u64)(r)) + +void bitsl(__iomem u32 *reg, u32 mask, u32 val) +{ + if (val & ~mask) + pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", + reg_num(reg), val, mask); + writel((readl(reg) & ~mask) | (val & mask), reg); +} + +void bitsb(__iomem u8 *reg, u8 mask, u8 val) +{ + if (val & ~mask) + pr_err("register 0x%x: invalid value 0x%x for bitmask 0x%x\n", + reg_num(reg), val, mask); + writeb((readb(reg) & ~mask) | (val & mask), reg); +} + +int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val) +{ + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", + reg_num(reg), mask, val); + u32 x; + if (readl_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) { + ithc_log_regs(ithc); + pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%08x val 0x%08x\n", + reg_num(reg), mask, val); + return -ETIMEDOUT; + } + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "done waiting\n"); + return 0; +} + +int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val) +{ + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", + reg_num(reg), mask, val); + u8 x; + if (readb_poll_timeout(reg, x, (x & mask) == val, 200, 1000*1000)) { + ithc_log_regs(ithc); + pci_err(ithc->pci, "timed out waiting for reg 0x%04x mask 0x%02x val 0x%02x\n", + reg_num(reg), mask, val); + return -ETIMEDOUT; + } + ithc_log_regs(ithc); + pci_dbg(ithc->pci, "done waiting\n"); + return 0; +} + +static void calc_ltr(u64 *ns, unsigned int *val, unsigned int *scale) +{ + unsigned int s = 0; + u64 v = *ns; + while (v > 0x3ff) { + s++; + v >>= 5; + } + if (s > 5) { + s = 5; + v = 0x3ff; + } + *val = v; + *scale = s; + *ns = v << (5 * s); +} + +void ithc_set_ltr_config(struct ithc *ithc, u64 active_ltr_ns, u64 idle_ltr_ns) +{ + unsigned int active_val, active_scale, idle_val, idle_scale; + calc_ltr(&active_ltr_ns, &active_val, &active_scale); + calc_ltr(&idle_ltr_ns, &idle_val, &idle_scale); + pci_dbg(ithc->pci, "setting active LTR value to %llu ns, idle LTR value to %llu ns\n", + active_ltr_ns, idle_ltr_ns); + writel(LTR_CONFIG_ENABLE_ACTIVE | LTR_CONFIG_ENABLE_IDLE | LTR_CONFIG_APPLY | + LTR_CONFIG_ACTIVE_LTR_SCALE(active_scale) | LTR_CONFIG_ACTIVE_LTR_VALUE(active_val) | + LTR_CONFIG_IDLE_LTR_SCALE(idle_scale) | LTR_CONFIG_IDLE_LTR_VALUE(idle_val), + &ithc->regs->ltr_config); +} + +void ithc_set_ltr_idle(struct ithc *ithc) +{ + u32 ltr = readl(&ithc->regs->ltr_config); + switch (ltr & (LTR_CONFIG_STATUS_ACTIVE | LTR_CONFIG_STATUS_IDLE)) { + case LTR_CONFIG_STATUS_IDLE: + break; + case LTR_CONFIG_STATUS_ACTIVE: + writel(ltr | LTR_CONFIG_TOGGLE | LTR_CONFIG_APPLY, &ithc->regs->ltr_config); + break; + default: + pci_err(ithc->pci, "invalid LTR state 0x%08x\n", ltr); + break; + } +} + +int ithc_set_spi_config(struct ithc *ithc, u8 clkdiv, bool clkdiv8, u8 read_mode, u8 write_mode) +{ + if (clkdiv == 0 || clkdiv > 7 || read_mode > SPI_MODE_QUAD || write_mode > SPI_MODE_QUAD) + return -EINVAL; + static const char * const modes[] = { "single", "dual", "quad" }; + pci_dbg(ithc->pci, "setting SPI frequency to %i Hz, %s read, %s write\n", + SPI_CLK_FREQ_BASE / (clkdiv * (clkdiv8 ? 8 : 1)), + modes[read_mode], modes[write_mode]); + bitsl(&ithc->regs->spi_config, + SPI_CONFIG_READ_MODE(0xff) | SPI_CONFIG_READ_CLKDIV(0xff) | + SPI_CONFIG_WRITE_MODE(0xff) | SPI_CONFIG_WRITE_CLKDIV(0xff) | + SPI_CONFIG_CLKDIV_8, + SPI_CONFIG_READ_MODE(read_mode) | SPI_CONFIG_READ_CLKDIV(clkdiv) | + SPI_CONFIG_WRITE_MODE(write_mode) | SPI_CONFIG_WRITE_CLKDIV(clkdiv) | + (clkdiv8 ? SPI_CONFIG_CLKDIV_8 : 0)); + return 0; +} + +int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data) +{ + pci_dbg(ithc->pci, "SPI command %u, size %u, offset 0x%x\n", command, size, offset); + if (size > sizeof(ithc->regs->spi_cmd.data)) + return -EINVAL; + + // Wait if the device is still busy. + CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0); + // Clear result flags. + writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status); + + // Init SPI command data. + writeb(command, &ithc->regs->spi_cmd.code); + writew(size, &ithc->regs->spi_cmd.size); + writel(offset, &ithc->regs->spi_cmd.offset); + u32 *p = data, n = (size + 3) / 4; + for (u32 i = 0; i < n; i++) + writel(p[i], &ithc->regs->spi_cmd.data[i]); + + // Start transmission. + bitsb_set(&ithc->regs->spi_cmd.control, SPI_CMD_CONTROL_SEND); + CHECK_RET(waitl, ithc, &ithc->regs->spi_cmd.status, SPI_CMD_STATUS_BUSY, 0); + + // Read response. + if ((readl(&ithc->regs->spi_cmd.status) & (SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR)) != SPI_CMD_STATUS_DONE) + return -EIO; + if (readw(&ithc->regs->spi_cmd.size) != size) + return -EMSGSIZE; + for (u32 i = 0; i < n; i++) + p[i] = readl(&ithc->regs->spi_cmd.data[i]); + + writel(SPI_CMD_STATUS_DONE | SPI_CMD_STATUS_ERROR, &ithc->regs->spi_cmd.status); + return 0; +} + diff --git a/drivers/hid/ithc/ithc-regs.h b/drivers/hid/ithc/ithc-regs.h new file mode 100644 index 00000000000000..4f541fe533facf --- /dev/null +++ b/drivers/hid/ithc/ithc-regs.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +#define LTR_CONFIG_ENABLE_ACTIVE BIT(0) +#define LTR_CONFIG_TOGGLE BIT(1) +#define LTR_CONFIG_ENABLE_IDLE BIT(2) +#define LTR_CONFIG_APPLY BIT(3) +#define LTR_CONFIG_IDLE_LTR_SCALE(x) (((x) & 7) << 4) +#define LTR_CONFIG_IDLE_LTR_VALUE(x) (((x) & 0x3ff) << 7) +#define LTR_CONFIG_ACTIVE_LTR_SCALE(x) (((x) & 7) << 17) +#define LTR_CONFIG_ACTIVE_LTR_VALUE(x) (((x) & 0x3ff) << 20) +#define LTR_CONFIG_STATUS_ACTIVE BIT(30) +#define LTR_CONFIG_STATUS_IDLE BIT(31) + +#define CONTROL_QUIESCE BIT(1) +#define CONTROL_IS_QUIESCED BIT(2) +#define CONTROL_NRESET BIT(3) +#define CONTROL_UNKNOWN_24(x) (((x) & 3) << 24) +#define CONTROL_READY BIT(29) + +#define SPI_CONFIG_READ_MODE(x) (((x) & 3) << 2) +#define SPI_CONFIG_READ_CLKDIV(x) (((x) & 7) << 4) +#define SPI_CONFIG_READ_PACKET_SIZE(x) (((x) & 0x1ff) << 7) +#define SPI_CONFIG_WRITE_MODE(x) (((x) & 3) << 18) +#define SPI_CONFIG_WRITE_CLKDIV(x) (((x) & 7) << 20) +#define SPI_CONFIG_CLKDIV_8 BIT(23) // additionally divide clk by 8, for both read and write +#define SPI_CONFIG_WRITE_PACKET_SIZE(x) (((x) & 0xff) << 24) + +#define SPI_CLK_FREQ_BASE 125000000 +#define SPI_MODE_SINGLE 0 +#define SPI_MODE_DUAL 1 +#define SPI_MODE_QUAD 2 + +#define ERROR_CONTROL_UNKNOWN_0 BIT(0) +#define ERROR_CONTROL_DISABLE_DMA BIT(1) // clears DMA_RX_CONTROL_ENABLE when a DMA error occurs +#define ERROR_CONTROL_UNKNOWN_2 BIT(2) +#define ERROR_CONTROL_UNKNOWN_3 BIT(3) +#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_9 BIT(9) +#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_10 BIT(10) +#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_12 BIT(12) +#define ERROR_CONTROL_IRQ_DMA_UNKNOWN_13 BIT(13) +#define ERROR_CONTROL_UNKNOWN_16(x) (((x) & 0xff) << 16) // spi error code irq? +#define ERROR_CONTROL_SET_DMA_STATUS BIT(29) // sets DMA_RX_STATUS_ERROR when a DMA error occurs + +#define ERROR_STATUS_DMA BIT(28) +#define ERROR_STATUS_SPI BIT(30) + +#define ERROR_FLAG_DMA_UNKNOWN_9 BIT(9) +#define ERROR_FLAG_DMA_UNKNOWN_10 BIT(10) +#define ERROR_FLAG_DMA_RX_TIMEOUT BIT(12) // set when we receive a truncated DMA message +#define ERROR_FLAG_DMA_UNKNOWN_13 BIT(13) +#define ERROR_FLAG_SPI_BUS_TURNAROUND BIT(16) +#define ERROR_FLAG_SPI_RESPONSE_TIMEOUT BIT(17) +#define ERROR_FLAG_SPI_INTRA_PACKET_TIMEOUT BIT(18) +#define ERROR_FLAG_SPI_INVALID_RESPONSE BIT(19) +#define ERROR_FLAG_SPI_HS_RX_TIMEOUT BIT(20) +#define ERROR_FLAG_SPI_TOUCH_IC_INIT BIT(21) + +#define SPI_CMD_CONTROL_SEND BIT(0) // cleared by device when sending is complete +#define SPI_CMD_CONTROL_IRQ BIT(1) + +#define SPI_CMD_CODE_READ 4 +#define SPI_CMD_CODE_WRITE 6 + +#define SPI_CMD_STATUS_DONE BIT(0) +#define SPI_CMD_STATUS_ERROR BIT(1) +#define SPI_CMD_STATUS_BUSY BIT(3) + +#define DMA_TX_CONTROL_SEND BIT(0) // cleared by device when sending is complete +#define DMA_TX_CONTROL_IRQ BIT(3) + +#define DMA_TX_STATUS_DONE BIT(0) +#define DMA_TX_STATUS_ERROR BIT(1) +#define DMA_TX_STATUS_UNKNOWN_2 BIT(2) +#define DMA_TX_STATUS_UNKNOWN_3 BIT(3) // busy? + +#define INPUT_HEADER_VERSION(x) ((x) & 0xf) +#define INPUT_HEADER_REPORT_LENGTH(x) (((x) >> 8) & 0x3fff) +#define INPUT_HEADER_SYNC(x) ((x) >> 24) +#define INPUT_HEADER_VERSION_VALUE 3 +#define INPUT_HEADER_SYNC_VALUE 0x5a + +#define QUICKSPI_CONFIG1_UNKNOWN_0(x) (((x) & 0x1f) << 0) +#define QUICKSPI_CONFIG1_UNKNOWN_5(x) (((x) & 0x1f) << 5) +#define QUICKSPI_CONFIG1_UNKNOWN_10(x) (((x) & 0x1f) << 10) +#define QUICKSPI_CONFIG1_UNKNOWN_16(x) (((x) & 0xffff) << 16) + +#define QUICKSPI_CONFIG2_UNKNOWN_0(x) (((x) & 0x1f) << 0) +#define QUICKSPI_CONFIG2_UNKNOWN_5(x) (((x) & 0x1f) << 5) +#define QUICKSPI_CONFIG2_UNKNOWN_12(x) (((x) & 0xf) << 12) +#define QUICKSPI_CONFIG2_UNKNOWN_16 BIT(16) +#define QUICKSPI_CONFIG2_UNKNOWN_17 BIT(17) +#define QUICKSPI_CONFIG2_DISABLE_READ_ADDRESS_INCREMENT BIT(24) +#define QUICKSPI_CONFIG2_DISABLE_WRITE_ADDRESS_INCREMENT BIT(25) +#define QUICKSPI_CONFIG2_ENABLE_WRITE_STREAMING_MODE BIT(27) +#define QUICKSPI_CONFIG2_IRQ_POLARITY BIT(28) + +#define DMA_RX_CONTROL_ENABLE BIT(0) +#define DMA_RX_CONTROL_IRQ_UNKNOWN_1 BIT(1) // rx1 only? +#define DMA_RX_CONTROL_IRQ_ERROR BIT(3) // rx1 only? +#define DMA_RX_CONTROL_IRQ_READY BIT(4) // rx0 only +#define DMA_RX_CONTROL_IRQ_DATA BIT(5) + +#define DMA_RX_CONTROL2_UNKNOWN_4 BIT(4) // rx1 only? +#define DMA_RX_CONTROL2_UNKNOWN_5 BIT(5) // rx0 only? +#define DMA_RX_CONTROL2_RESET BIT(7) // resets ringbuffer indices + +#define DMA_RX_WRAP_FLAG BIT(7) + +#define DMA_RX_STATUS_ERROR BIT(3) +#define DMA_RX_STATUS_READY BIT(4) // set in rx0 after using CONTROL_NRESET when it becomes possible to read config (can take >100ms) +#define DMA_RX_STATUS_HAVE_DATA BIT(5) +#define DMA_RX_STATUS_ENABLED BIT(8) + +#define INIT_UNKNOWN_GUC_2 BIT(2) +#define INIT_UNKNOWN_3 BIT(3) +#define INIT_UNKNOWN_GUC_4 BIT(4) +#define INIT_UNKNOWN_5 BIT(5) +#define INIT_UNKNOWN_31 BIT(31) + +// COUNTER_RESET can be written to counter registers to reset them to zero. However, in some cases this can mess up the THC. +#define COUNTER_RESET BIT(31) + +struct ithc_registers { + /* 0000 */ u32 _unknown_0000[5]; + /* 0014 */ u32 ltr_config; + /* 0018 */ u32 _unknown_0018[1018]; + /* 1000 */ u32 _unknown_1000; + /* 1004 */ u32 _unknown_1004; + /* 1008 */ u32 control_bits; + /* 100c */ u32 _unknown_100c; + /* 1010 */ u32 spi_config; + struct { + /* 1014/1018/101c */ u8 header; + /* 1015/1019/101d */ u8 quad; + /* 1016/101a/101e */ u8 dual; + /* 1017/101b/101f */ u8 single; + } opcode[3]; + /* 1020 */ u32 error_control; + /* 1024 */ u32 error_status; // write to clear + /* 1028 */ u32 error_flags; // write to clear + /* 102c */ u32 _unknown_102c[5]; + struct { + /* 1040 */ u8 control; + /* 1041 */ u8 code; + /* 1042 */ u16 size; + /* 1044 */ u32 status; // write to clear + /* 1048 */ u32 offset; + /* 104c */ u32 data[16]; + /* 108c */ u32 _unknown_108c; + } spi_cmd; + struct { + /* 1090 */ u64 addr; // cannot be written with writeq(), must use lo_hi_writeq() + /* 1098 */ u8 control; + /* 1099 */ u8 _unknown_1099; + /* 109a */ u8 _unknown_109a; + /* 109b */ u8 num_prds; + /* 109c */ u32 status; // write to clear + /* 10a0 */ u32 _unknown_10a0[5]; + /* 10b4 */ u32 spi_addr; + } dma_tx; + /* 10b8 */ u32 spi_header_addr; + union { + /* 10bc */ u32 irq_cause; // in legacy THC mode + /* 10bc */ u32 input_header; // in QuickSPI mode (see HIDSPI spec) + }; + /* 10c0 */ u32 _unknown_10c0[8]; + /* 10e0 */ u32 _unknown_10e0_counters[3]; + /* 10ec */ u32 quickspi_config1; + /* 10f0 */ u32 quickspi_config2; + /* 10f4 */ u32 _unknown_10f4[3]; + struct { + /* 1100/1200 */ u64 addr; // cannot be written with writeq(), must use lo_hi_writeq() + /* 1108/1208 */ u8 num_bufs; + /* 1109/1209 */ u8 num_prds; + /* 110a/120a */ u16 _unknown_110a; + /* 110c/120c */ u8 control; + /* 110d/120d */ u8 head; + /* 110e/120e */ u8 tail; + /* 110f/120f */ u8 control2; + /* 1110/1210 */ u32 status; // write to clear + /* 1114/1214 */ u32 _unknown_1114; + /* 1118/1218 */ u64 _unknown_1118_guc_addr; + /* 1120/1220 */ u32 _unknown_1120_guc; + /* 1124/1224 */ u32 _unknown_1124_guc; + /* 1128/1228 */ u32 init_unknown; + /* 112c/122c */ u32 _unknown_112c; + /* 1130/1230 */ u64 _unknown_1130_guc_addr; + /* 1138/1238 */ u32 _unknown_1138_guc; + /* 113c/123c */ u32 _unknown_113c; + /* 1140/1240 */ u32 _unknown_1140_guc; + /* 1144/1244 */ u32 _unknown_1144[11]; + /* 1170/1270 */ u32 spi_addr; + /* 1174/1274 */ u32 _unknown_1174[11]; + /* 11a0/12a0 */ u32 _unknown_11a0_counters[6]; + /* 11b8/12b8 */ u32 _unknown_11b8[18]; + } dma_rx[2]; +}; +static_assert(sizeof(struct ithc_registers) == 0x1300); + +void bitsl(__iomem u32 *reg, u32 mask, u32 val); +void bitsb(__iomem u8 *reg, u8 mask, u8 val); +#define bitsl_set(reg, x) bitsl(reg, x, x) +#define bitsb_set(reg, x) bitsb(reg, x, x) +int waitl(struct ithc *ithc, __iomem u32 *reg, u32 mask, u32 val); +int waitb(struct ithc *ithc, __iomem u8 *reg, u8 mask, u8 val); + +void ithc_set_ltr_config(struct ithc *ithc, u64 active_ltr_ns, u64 idle_ltr_ns); +void ithc_set_ltr_idle(struct ithc *ithc); +int ithc_set_spi_config(struct ithc *ithc, u8 clkdiv, bool clkdiv8, u8 read_mode, u8 write_mode); +int ithc_spi_command(struct ithc *ithc, u8 command, u32 offset, u32 size, void *data); + diff --git a/drivers/hid/ithc/ithc.h b/drivers/hid/ithc/ithc.h new file mode 100644 index 00000000000000..aec320d4e945c0 --- /dev/null +++ b/drivers/hid/ithc/ithc.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVNAME "ithc" +#define DEVFULLNAME "Intel Touch Host Controller" + +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#define CHECK(fn, ...) ({ int r = fn(__VA_ARGS__); if (r < 0) pci_err(ithc->pci, "%s: %s failed with %i\n", __func__, #fn, r); r; }) +#define CHECK_RET(...) do { int r = CHECK(__VA_ARGS__); if (r < 0) return r; } while (0) + +#define NUM_RX_BUF 16 + +// PCI device IDs: +// Lakefield +#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT1 0x98d0 +#define PCI_DEVICE_ID_INTEL_THC_LKF_PORT2 0x98d1 +// Tiger Lake +#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT1 0xa0d0 +#define PCI_DEVICE_ID_INTEL_THC_TGL_LP_PORT2 0xa0d1 +#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT1 0x43d0 +#define PCI_DEVICE_ID_INTEL_THC_TGL_H_PORT2 0x43d1 +// Alder Lake +#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT1 0x7ad8 +#define PCI_DEVICE_ID_INTEL_THC_ADL_S_PORT2 0x7ad9 +#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT1 0x51d0 +#define PCI_DEVICE_ID_INTEL_THC_ADL_P_PORT2 0x51d1 +#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT1 0x54d0 +#define PCI_DEVICE_ID_INTEL_THC_ADL_M_PORT2 0x54d1 +// Raptor Lake +#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT1 0x7a58 +#define PCI_DEVICE_ID_INTEL_THC_RPL_S_PORT2 0x7a59 +// Meteor Lake +#define PCI_DEVICE_ID_INTEL_THC_MTL_S_PORT1 0x7f59 +#define PCI_DEVICE_ID_INTEL_THC_MTL_S_PORT2 0x7f5b +#define PCI_DEVICE_ID_INTEL_THC_MTL_MP_PORT1 0x7e49 +#define PCI_DEVICE_ID_INTEL_THC_MTL_MP_PORT2 0x7e4b + +struct ithc; + +#include "ithc-regs.h" +#include "ithc-hid.h" +#include "ithc-dma.h" +#include "ithc-legacy.h" +#include "ithc-quickspi.h" +#include "ithc-debug.h" + +struct ithc { + char phys[32]; + struct pci_dev *pci; + int irq; + struct task_struct *poll_thread; + struct timer_list idle_timer; + + struct ithc_registers __iomem *regs; + struct ithc_registers *prev_regs; // for debugging + struct ithc_dma_rx dma_rx[2]; + struct ithc_dma_tx dma_tx; + struct ithc_hid hid; + + bool use_quickspi; + bool have_config; + u16 vendor_id; + u16 product_id; + u32 product_rev; + u32 max_rx_size; + u32 max_tx_size; + u32 legacy_touch_cfg; +}; + +int ithc_reset(struct ithc *ithc); + From 19dc0b07df1ad774c45e60bcead4df8e5ff996db Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Fri, 17 Jun 2022 02:14:00 +0200 Subject: [PATCH 127/258] SURFACE: rtc: Add basic support for RTC via Surface System Aggregator Module Signed-off-by: Maximilian Luz Patchset: surface-sam Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/rtc/Kconfig | 7 +++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-surface.c | 129 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 drivers/rtc/rtc-surface.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index af420a5f9585a2..8676b35b63aa66 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1425,6 +1425,13 @@ config RTC_DRV_NTXEC embedded controller found in certain e-book readers designed by the original design manufacturer Netronix. +config RTC_DRV_SURFACE + tristate "Microsoft Surface Aggregator RTC" + depends on SURFACE_AGGREGATOR + depends on SURFACE_AGGREGATOR_BUS + help + TODO + comment "on-CPU RTC drivers" config RTC_DRV_ASM9260 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6cf7e066314e13..18bd33b84d1399 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -186,6 +186,7 @@ obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o obj-$(CONFIG_RTC_DRV_SUNPLUS) += rtc-sunplus.o obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o +obj-$(CONFIG_RTC_DRV_SURFACE) += rtc-surface.o obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_TI_K3) += rtc-ti-k3.o diff --git a/drivers/rtc/rtc-surface.c b/drivers/rtc/rtc-surface.c new file mode 100644 index 00000000000000..f6c17c4e98d5f1 --- /dev/null +++ b/drivers/rtc/rtc-surface.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AC driver for 7th-generation Microsoft Surface devices via Surface System + * Aggregator Module (SSAM). + * + * Copyright (C) 2019-2021 Maximilian Luz + */ + +#include +#include +#include +#include +#include + +#include + +struct surface_rtc { + struct ssam_device *sdev; + struct rtc_device *rtc; +}; + +SSAM_DEFINE_SYNC_REQUEST_R(__ssam_rtc_get_unix_time, __le32, { + .target_category = SSAM_SSH_TC_SAM, + .target_id = SSAM_SSH_TID_SAM, + .instance_id = 0x00, + .command_id = 0x10, +}); + +SSAM_DEFINE_SYNC_REQUEST_W(__ssam_rtc_set_unix_time, __le32, { + .target_category = SSAM_SSH_TC_SAM, + .target_id = SSAM_SSH_TID_SAM, + .instance_id = 0x00, + .command_id = 0x0f, +}); + +static int ssam_rtc_get_unix_time(struct surface_rtc *srtc, u32 *time) +{ + __le32 time_le; + int status; + + status = __ssam_rtc_get_unix_time(srtc->sdev->ctrl, &time_le); + if (status) + return status; + + *time = le32_to_cpu(time_le); + return 0; +} + +static int ssam_rtc_set_unix_time(struct surface_rtc *srtc, u32 time) +{ + __le32 time_le = cpu_to_le32(time); + + return __ssam_rtc_set_unix_time(srtc->sdev->ctrl, &time_le); +} + +static int surface_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct surface_rtc *srtc = dev_get_drvdata(dev); + int status; + u32 time; + + status = ssam_rtc_get_unix_time(srtc, &time); + if (status) + return status; + + rtc_time64_to_tm(time, tm); + return 0; +} + +static int surface_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct surface_rtc *srtc = dev_get_drvdata(dev); + time64_t time = rtc_tm_to_time64(tm); + + return ssam_rtc_set_unix_time(srtc, (u32)time); +} + +static const struct rtc_class_ops surface_rtc_ops = { + .read_time = surface_rtc_read_time, + .set_time = surface_rtc_set_time, +}; + +static int surface_rtc_probe(struct ssam_device *sdev) +{ + struct surface_rtc *srtc; + + srtc = devm_kzalloc(&sdev->dev, sizeof(*srtc), GFP_KERNEL); + if (!srtc) + return -ENOMEM; + + srtc->sdev = sdev; + + srtc->rtc = devm_rtc_allocate_device(&sdev->dev); + if (IS_ERR(srtc->rtc)) + return PTR_ERR(srtc->rtc); + + srtc->rtc->ops = &surface_rtc_ops; + srtc->rtc->range_max = U32_MAX; + + ssam_device_set_drvdata(sdev, srtc); + + return devm_rtc_register_device(srtc->rtc); +} + +static void surface_rtc_remove(struct ssam_device *sdev) +{ + /* Device-managed allocations take care of everything... */ +} + +static const struct ssam_device_id surface_rtc_match[] = { + { SSAM_SDEV(SAM, SAM, 0x00, 0x00) }, + { }, +}; +MODULE_DEVICE_TABLE(ssam, surface_rtc_match); + +static struct ssam_device_driver surface_rtc_driver = { + .probe = surface_rtc_probe, + .remove = surface_rtc_remove, + .match_table = surface_rtc_match, + .driver = { + .name = "surface_rtc", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_ssam_device_driver(surface_rtc_driver); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("RTC driver for Surface System Aggregator Module"); +MODULE_LICENSE("GPL"); From 4a7af8b5bf8ed90f282ccf650f93976889cd65cb Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 20 Apr 2025 01:05:14 +0200 Subject: [PATCH 128/258] SURFACE: platform/surface: aggregator_registry: Add Surface Laptop 7 (ACPI) Patchset: surface-sam Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/surface/surface_aggregator_registry.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index f0881edfb61610..c935d2518615f6 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -474,6 +474,9 @@ static const struct acpi_device_id ssam_platform_hub_acpi_match[] = { /* Surface Laptop 6 */ { "MSHW0530", (unsigned long)ssam_node_group_sl6 }, + /* Surface Laptop 7 */ + { "MSHW0551", (unsigned long)ssam_node_group_sl7 }, + /* Surface Laptop Go 1 */ { "MSHW0118", (unsigned long)ssam_node_group_slg1 }, From 39ef7c3253516e3a20c215e3c3bc5e9d9bd0706f Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 25 Jul 2020 17:19:53 +0200 Subject: [PATCH 129/258] SURFACE: i2c: acpi: Implement RawBytes read access Microsoft Surface Pro 4 and Book 1 devices access the MSHW0030 I2C device via a generic serial bus operation region and RawBytes read access. On the Surface Book 1, this access is required to turn on (and off) the discrete GPU. Multiple things are to note here: a) The RawBytes access is device/driver dependent. The ACPI specification states: > Raw accesses assume that the writer has knowledge of the bus that > the access is made over and the device that is being accessed. The > protocol may only ensure that the buffer is transmitted to the > appropriate driver, but the driver must be able to interpret the > buffer to communicate to a register. Thus this implementation may likely not work on other devices accessing I2C via the RawBytes accessor type. b) The MSHW0030 I2C device is an HID-over-I2C device which seems to serve multiple functions: 1. It is the main access point for the legacy-type Surface Aggregator Module (also referred to as SAM-over-HID, as opposed to the newer SAM-over-SSH/UART). It has currently not been determined on how support for the legacy SAM should be implemented. Likely via a custom HID driver. 2. It seems to serve as the HID device for the Integrated Sensor Hub. This might complicate matters with regards to implementing a SAM-over-HID driver required by legacy SAM. In light of this, the simplest approach has been chosen for now. However, it may make more sense regarding breakage and compatibility to either provide functionality for replacing or enhancing the default operation region handler via some additional API functions, or even to completely blacklist MSHW0030 from the I2C core and provide a custom driver for it. Replacing/enhancing the default operation region handler would, however, either require some sort of secondary driver and access point for it, from which the new API functions would be called and the new handler (part) would be installed, or hard-coding them via some sort of quirk-like interface into the I2C core. Signed-off-by: Maximilian Luz Patchset: surface-sam-over-hid Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/i2c/i2c-core-acpi.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 28c0e4884a7f2e..26ae925ab5d60c 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -663,6 +663,27 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, return (ret == 1) ? 0 : -EIO; } +static int acpi_gsb_i2c_write_raw_bytes(struct i2c_client *client, + u8 *data, u8 data_len) +{ + struct i2c_msg msgs[1]; + int ret; + + msgs[0].addr = client->addr; + msgs[0].flags = client->flags; + msgs[0].len = data_len + 1; + msgs[0].buf = data; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + dev_err(&client->adapter->dev, "i2c write failed: %d\n", ret); + return ret; + } + + /* 1 transfer must have completed successfully */ + return (ret == 1) ? 0 : -EIO; +} + static acpi_status i2c_acpi_space_handler(u32 function, acpi_physical_address command, u32 bits, u64 *value64, @@ -764,6 +785,19 @@ i2c_acpi_space_handler(u32 function, acpi_physical_address command, } break; + case ACPI_GSB_ACCESS_ATTRIB_RAW_BYTES: + if (action == ACPI_READ) { + dev_warn(&adapter->dev, + "protocol 0x%02x not supported for client 0x%02x\n", + accessor_type, client->addr); + ret = AE_BAD_PARAMETER; + goto err; + } else { + status = acpi_gsb_i2c_write_raw_bytes(client, + gsb->data, info->access_length); + } + break; + default: dev_warn(&adapter->dev, "protocol 0x%02x not supported for client 0x%02x\n", accessor_type, client->addr); From 0722446db0d52e0612b3d5ed7ff1a03b5ba1ab3e Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 13 Feb 2021 16:41:18 +0100 Subject: [PATCH 130/258] SURFACE: platform/surface: Add driver for Surface Book 1 dGPU switch Add driver exposing the discrete GPU power-switch of the Microsoft Surface Book 1 to user-space. On the Surface Book 1, the dGPU power is controlled via the Surface System Aggregator Module (SAM). The specific SAM-over-HID command for this is exposed via ACPI. This module provides a simple driver exposing the ACPI call via a sysfs parameter to user-space, so that users can easily power-on/-off the dGPU. Patchset: surface-sam-over-hid Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/surface/Kconfig | 7 + drivers/platform/surface/Makefile | 1 + .../surface/surfacebook1_dgpu_switch.c | 136 ++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 drivers/platform/surface/surfacebook1_dgpu_switch.c diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig index f775c6ca1ec1dd..2075e3852053a8 100644 --- a/drivers/platform/surface/Kconfig +++ b/drivers/platform/surface/Kconfig @@ -149,6 +149,13 @@ config SURFACE_AGGREGATOR_TABLET_SWITCH Select M or Y here, if you want to provide tablet-mode switch input events on the Surface Pro 8, Surface Pro X, and Surface Laptop Studio. +config SURFACE_BOOK1_DGPU_SWITCH + tristate "Surface Book 1 dGPU Switch Driver" + depends on SYSFS + help + This driver provides a sysfs switch to set the power-state of the + discrete GPU found on the Microsoft Surface Book 1. + config SURFACE_DTX tristate "Surface DTX (Detachment System) Driver" depends on SURFACE_AGGREGATOR diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile index 53344330939bf0..7efcd0cdb5329f 100644 --- a/drivers/platform/surface/Makefile +++ b/drivers/platform/surface/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o obj-$(CONFIG_SURFACE_AGGREGATOR_HUB) += surface_aggregator_hub.o obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o obj-$(CONFIG_SURFACE_AGGREGATOR_TABLET_SWITCH) += surface_aggregator_tabletsw.o +obj-$(CONFIG_SURFACE_BOOK1_DGPU_SWITCH) += surfacebook1_dgpu_switch.o obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o diff --git a/drivers/platform/surface/surfacebook1_dgpu_switch.c b/drivers/platform/surface/surfacebook1_dgpu_switch.c new file mode 100644 index 00000000000000..68db237734a13e --- /dev/null +++ b/drivers/platform/surface/surfacebook1_dgpu_switch.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include + +/* MSHW0040/VGBI DSM UUID: 6fd05c69-cde3-49f4-95ed-ab1665498035 */ +static const guid_t dgpu_sw_guid = + GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, + 0x95, 0xed, 0xab, 0x16, 0x65, 0x49, 0x80, 0x35); + +#define DGPUSW_ACPI_PATH_DSM "\\_SB_.PCI0.LPCB.EC0_.VGBI" +#define DGPUSW_ACPI_PATH_HGON "\\_SB_.PCI0.RP05.HGON" +#define DGPUSW_ACPI_PATH_HGOF "\\_SB_.PCI0.RP05.HGOF" + +static int sb1_dgpu_sw_dsmcall(void) +{ + union acpi_object *obj; + acpi_handle handle; + acpi_status status; + + status = acpi_get_handle(NULL, DGPUSW_ACPI_PATH_DSM, &handle); + if (status) + return -EINVAL; + + obj = acpi_evaluate_dsm_typed(handle, &dgpu_sw_guid, 1, 1, NULL, ACPI_TYPE_BUFFER); + if (!obj) + return -EINVAL; + + ACPI_FREE(obj); + return 0; +} + +static int sb1_dgpu_sw_hgon(struct device *dev) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + + status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGON, NULL, &buf); + if (status) { + dev_err(dev, "failed to run HGON: %d\n", status); + return -EINVAL; + } + + ACPI_FREE(buf.pointer); + + dev_info(dev, "turned-on dGPU via HGON\n"); + return 0; +} + +static int sb1_dgpu_sw_hgof(struct device *dev) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_status status; + + status = acpi_evaluate_object(NULL, DGPUSW_ACPI_PATH_HGOF, NULL, &buf); + if (status) { + dev_err(dev, "failed to run HGOF: %d\n", status); + return -EINVAL; + } + + ACPI_FREE(buf.pointer); + + dev_info(dev, "turned-off dGPU via HGOF\n"); + return 0; +} + +static ssize_t dgpu_dsmcall_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + bool value; + int status; + + status = kstrtobool(buf, &value); + if (status < 0) + return status; + + if (!value) + return 0; + + status = sb1_dgpu_sw_dsmcall(); + + return status < 0 ? status : len; +} +static DEVICE_ATTR_WO(dgpu_dsmcall); + +static ssize_t dgpu_power_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + bool power; + int status; + + status = kstrtobool(buf, &power); + if (status < 0) + return status; + + if (power) + status = sb1_dgpu_sw_hgon(dev); + else + status = sb1_dgpu_sw_hgof(dev); + + return status < 0 ? status : len; +} +static DEVICE_ATTR_WO(dgpu_power); + +static struct attribute *sb1_dgpu_sw_attrs[] = { + &dev_attr_dgpu_dsmcall.attr, + &dev_attr_dgpu_power.attr, + NULL +}; +ATTRIBUTE_GROUPS(sb1_dgpu_sw); + +/* + * The dGPU power seems to be actually handled by MSHW0040. However, that is + * also the power-/volume-button device with a mainline driver. So let's use + * MSHW0041 instead for now, which seems to be the LTCH (latch/DTX) device. + */ +static const struct acpi_device_id sb1_dgpu_sw_match[] = { + { "MSHW0041", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, sb1_dgpu_sw_match); + +static struct platform_driver sb1_dgpu_sw = { + .driver = { + .name = "surfacebook1_dgpu_switch", + .acpi_match_table = sb1_dgpu_sw_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .dev_groups = sb1_dgpu_sw_groups, + }, +}; +module_platform_driver(sb1_dgpu_sw); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Discrete GPU Power-Switch for Surface Book 1"); +MODULE_LICENSE("GPL"); From e2841f95f0680f8f328b897d316993c5f5f6dbf1 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Tue, 5 Oct 2021 00:05:09 +1100 Subject: [PATCH 131/258] SURFACE: Input: soc_button_array - support AMD variant Surface devices The power button on the AMD variant of the Surface Laptop uses the same MSHW0040 device ID as the 5th and later generation of Surface devices, however they report 0 for their OEM platform revision. As the _DSM does not exist on the devices requiring special casing, check for the existance of the _DSM to determine if soc_button_array should be loaded. Fixes: c394159310d0 ("Input: soc_button_array - add support for newer surface devices") Co-developed-by: Maximilian Luz Signed-off-by: Sachi King Patchset: surface-button Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/input/misc/soc_button_array.c | 33 +++++++-------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index b8cad415c62ca2..43b5d56383e36c 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -540,8 +540,8 @@ static const struct soc_device_data soc_device_MSHW0028 = { * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned * devices use MSHW0040 for power and volume buttons, however the way they * have to be addressed differs. Make sure that we only load this drivers - * for the correct devices by checking the OEM Platform Revision provided by - * the _DSM method. + * for the correct devices by checking if the OEM Platform Revision DSM call + * exists. */ #define MSHW0040_DSM_REVISION 0x01 #define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision @@ -552,31 +552,14 @@ static const guid_t MSHW0040_DSM_UUID = static int soc_device_check_MSHW0040(struct device *dev) { acpi_handle handle = ACPI_HANDLE(dev); - union acpi_object *result; - u64 oem_platform_rev = 0; // valid revisions are nonzero - - // get OEM platform revision - result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, - MSHW0040_DSM_REVISION, - MSHW0040_DSM_GET_OMPR, NULL, - ACPI_TYPE_INTEGER); - - if (result) { - oem_platform_rev = result->integer.value; - ACPI_FREE(result); - } - - /* - * If the revision is zero here, the _DSM evaluation has failed. This - * indicates that we have a Pro 4 or Book 1 and this driver should not - * be used. - */ - if (oem_platform_rev == 0) - return -ENODEV; + bool exists; - dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); + // check if OEM platform revision DSM call exists + exists = acpi_check_dsm(handle, &MSHW0040_DSM_UUID, + MSHW0040_DSM_REVISION, + BIT(MSHW0040_DSM_GET_OMPR)); - return 0; + return exists ? 0 : -ENODEV; } /* From c063ed58c6117a5678d6f3b56fc5a8579982dd42 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Tue, 5 Oct 2021 00:22:57 +1100 Subject: [PATCH 132/258] BACKPORT: SURFACE: platform/surface: surfacepro3_button: don't load on amd variant The AMD variant of the Surface Laptop report 0 for their OEM platform revision. The Surface devices that require the surfacepro3_button driver do not have the _DSM that gets the OEM platform revision. If the method does not exist, load surfacepro3_button. Fixes: 64dd243d7356 ("platform/x86: surfacepro3_button: Fix device check") Co-developed-by: Maximilian Luz Signed-off-by: Sachi King Patchset: surface-button Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in drivers/platform/surface/surfacepro3_button.c ] Signed-off-by: Mingcong Bai --- drivers/platform/surface/surfacepro3_button.c | 31 ++++--------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c index 388a3e1a488cb7..f94510dbda44dd 100644 --- a/drivers/platform/surface/surfacepro3_button.c +++ b/drivers/platform/surface/surfacepro3_button.c @@ -149,37 +149,18 @@ static int surface_button_resume(struct device *dev) /* * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device * ID (MSHW0040) for the power/volume buttons. Make sure this is the right - * device by checking for the _DSM method and OEM Platform Revision. + * device by checking for the _DSM method and OEM Platform Revision DSM + * function. * * Returns true if the driver should bind to this device, i.e. the device is * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. */ static bool surface_button_check_MSHW0040(struct device *dev, acpi_handle handle) { - union acpi_object *result; - u64 oem_platform_rev = 0; // valid revisions are nonzero - - // get OEM platform revision - result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, - MSHW0040_DSM_REVISION, - MSHW0040_DSM_GET_OMPR, - NULL, ACPI_TYPE_INTEGER); - - /* - * If evaluating the _DSM fails, the method is not present. This means - * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we - * should use this driver. We use revision 0 indicating it is - * unavailable. - */ - - if (result) { - oem_platform_rev = result->integer.value; - ACPI_FREE(result); - } - - dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); - - return oem_platform_rev == 0; + // make sure that OEM platform revision DSM call does not exist + return !acpi_check_dsm(handle, &MSHW0040_DSM_UUID, + MSHW0040_DSM_REVISION, + BIT(MSHW0040_DSM_GET_OMPR)); } From b3b07e05acf06f3a13401c5e74dc6d0096641e6c Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sat, 18 Feb 2023 01:02:49 +0100 Subject: [PATCH 133/258] SURFACE: USB: quirks: Add USB_QUIRK_DELAY_INIT for Surface Go 3 Type-Cover The touchpad on the Type-Cover of the Surface Go 3 is sometimes not being initialized properly. Apply USB_QUIRK_DELAY_INIT to fix this issue. More specifically, the device in question is a fairly standard modern touchpad with pointer and touchpad input modes. During setup, the device needs to be switched from pointer- to touchpad-mode (which is done in hid-multitouch) to fully utilize it as intended. Unfortunately, however, this seems to occasionally fail silently, leaving the device in pointer-mode. Applying USB_QUIRK_DELAY_INIT seems to fix this. Link: https://github.com/linux-surface/linux-surface/issues/1059 Signed-off-by: Maximilian Luz Patchset: surface-typecover Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/usb/core/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 87ee2d938bc038..5e8ee248da9bdb 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -228,6 +228,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* Microsoft Surface Dock Ethernet (RTL8153 GigE) */ { USB_DEVICE(0x045e, 0x07c6), .driver_info = USB_QUIRK_NO_LPM }, + /* Microsoft Surface Go 3 Type-Cover */ + { USB_DEVICE(0x045e, 0x09b5), .driver_info = USB_QUIRK_DELAY_INIT }, + /* Cherry Stream G230 2.0 (G85-231) and 3.0 (G85-232) */ { USB_DEVICE(0x046a, 0x0023), .driver_info = USB_QUIRK_RESET_RESUME }, From 70961f8663f245229587e51f35383b86d9b37595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Dre=C3=9Fler?= Date: Thu, 5 Nov 2020 13:09:45 +0100 Subject: [PATCH 134/258] SURFACE: hid/multitouch: Turn off Type Cover keyboard backlight when suspending The Type Cover for Microsoft Surface devices supports a special usb control request to disable or enable the built-in keyboard backlight. On Windows, this request happens when putting the device into suspend or resuming it, without it the backlight of the Type Cover will remain enabled for some time even though the computer is suspended, which looks weird to the user. So add support for this special usb control request to hid-multitouch, which is the driver that's handling the Type Cover. The reason we have to use a pm_notifier for this instead of the usual suspend/resume methods is that those won't get called in case the usb device is already autosuspended. Also, if the device is autosuspended, we have to briefly autoresume it in order to send the request. Doing that should be fine, the usb-core driver does something similar during suspend inside choose_wakeup(). To make sure we don't send that request to every device but only to devices which support it, add a new quirk MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER to hid-multitouch. For now this quirk is only enabled for the usb id of the Surface Pro 2017 Type Cover, which is where I confirmed that it's working. Patchset: surface-typecover Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/hid-multitouch.c | 100 ++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index a02ca0bb98efbb..74a18881de2bc6 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -36,7 +36,10 @@ #include #include #include +#include #include +#include +#include #include #include #include @@ -49,6 +52,7 @@ MODULE_DESCRIPTION("HID multitouch panels"); MODULE_LICENSE("GPL"); #include "hid-ids.h" +#include "usbhid/usbhid.h" #include "hid-haptic.h" @@ -79,6 +83,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_APPLE_TOUCHBAR BIT(23) #define MT_QUIRK_YOGABOOK9I BIT(24) #define MT_QUIRK_KEEP_LATENCY_ON_CLOSE BIT(25) +#define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(26) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 @@ -86,6 +91,8 @@ MODULE_LICENSE("GPL"); #define MT_BUTTONTYPE_CLICKPAD 0 #define MT_BUTTONTYPE_PRESSUREPAD 1 +#define MS_TYPE_COVER_FEATURE_REPORT_USAGE 0xff050086 + enum latency_mode { HID_LATENCY_NORMAL = 0, HID_LATENCY_HIGH = 1, @@ -187,6 +194,8 @@ struct mt_device { struct list_head applications; struct list_head reports; + + struct notifier_block pm_notifier; }; static void mt_post_parse_default_settings(struct mt_device *td, @@ -235,6 +244,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); #define MT_CLS_APPLE_TOUCHBAR 0x0114 #define MT_CLS_YOGABOOK9I 0x0115 #define MT_CLS_EGALAX_P80H84 0x0116 +#define MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER 0x0117 #define MT_CLS_SIS 0x0457 #define MT_DEFAULT_MAXCONTACT 10 @@ -454,6 +464,16 @@ static const struct mt_class mt_classes[] = { MT_QUIRK_IGNORE_DUPLICATES | MT_QUIRK_CONTACT_CNT_ACCURATE, }, + { .name = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER, + .quirks = MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT | + MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_IGNORE_DUPLICATES | + MT_QUIRK_HOVERING | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_STICKY_FINGERS | + MT_QUIRK_WIN8_PTP_BUTTONS, + .export_all_inputs = true + }, { } }; @@ -1949,6 +1969,69 @@ static void mt_expired_timeout(struct timer_list *t) clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } +static void get_type_cover_backlight_field(struct hid_device *hdev, + struct hid_field **field) +{ + struct hid_report_enum *rep_enum; + struct hid_report *rep; + struct hid_field *cur_field; + int i, j; + + rep_enum = &hdev->report_enum[HID_FEATURE_REPORT]; + list_for_each_entry(rep, &rep_enum->report_list, list) { + for (i = 0; i < rep->maxfield; i++) { + cur_field = rep->field[i]; + + for (j = 0; j < cur_field->maxusage; j++) { + if (cur_field->usage[j].hid + == MS_TYPE_COVER_FEATURE_REPORT_USAGE) { + *field = cur_field; + return; + } + } + } + } +} + +static void update_keyboard_backlight(struct hid_device *hdev, bool enabled) +{ + struct usb_device *udev = hid_to_usb_dev(hdev); + struct hid_field *field = NULL; + + /* Wake up the device in case it's already suspended */ + pm_runtime_get_sync(&udev->dev); + + get_type_cover_backlight_field(hdev, &field); + if (!field) { + hid_err(hdev, "couldn't find backlight field\n"); + goto out; + } + + field->value[field->index] = enabled ? 0x01ff00ff : 0x00ff00ff; + hid_hw_request(hdev, field->report, HID_REQ_SET_REPORT); + +out: + pm_runtime_put_sync(&udev->dev); +} + +static int mt_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, + void *unused) +{ + struct mt_device *td = + container_of(notifier, struct mt_device, pm_notifier); + struct hid_device *hdev = td->hdev; + + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT) { + if (pm_event == PM_SUSPEND_PREPARE) + update_keyboard_backlight(hdev, 0); + else if (pm_event == PM_POST_SUSPEND) + update_keyboard_backlight(hdev, 1); + } + + return NOTIFY_DONE; +} + static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) { int ret, i; @@ -1977,6 +2060,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) td->inputmode_value = MT_INPUTMODE_TOUCHSCREEN; hid_set_drvdata(hdev, td); + td->pm_notifier.notifier_call = mt_pm_notifier; + register_pm_notifier(&td->pm_notifier); + INIT_LIST_HEAD(&td->applications); INIT_LIST_HEAD(&td->reports); @@ -2015,8 +2101,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) timer_setup(&td->release_timer, mt_expired_timeout, 0); ret = hid_parse(hdev); - if (ret != 0) + if (ret != 0) { + unregister_pm_notifier(&td->pm_notifier); return ret; + } if (mtclass->name == MT_CLS_APPLE_TOUCHBAR && !hid_find_field(hdev, HID_INPUT_REPORT, @@ -2030,8 +2118,10 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id) hdev->quirks |= HID_QUIRK_NOGET; ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) + if (ret) { + unregister_pm_notifier(&td->pm_notifier); return ret; + } ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group); if (ret) @@ -2092,6 +2182,7 @@ static void mt_remove(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + unregister_pm_notifier(&td->pm_notifier); timer_delete_sync(&td->release_timer); sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group); @@ -2559,6 +2650,11 @@ static const struct hid_device_id mt_devices[] = { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_TOUCHBAR_DISPLAY) }, + /* Microsoft Surface type cover */ + { .driver_data = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, + USB_VENDOR_ID_MICROSOFT, 0x09c0) }, + /* Google MT devices */ { .driver_data = MT_CLS_GOOGLE, HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE, From cb9377b51422b19fee04d7f55dbce1ad3b928cb4 Mon Sep 17 00:00:00 2001 From: PJungkamp Date: Fri, 25 Feb 2022 12:04:25 +0100 Subject: [PATCH 135/258] SURFACE: hid/multitouch: Add support for surface pro type cover tablet switch The Surface Pro Type Cover has several non standard HID usages in it's hid report descriptor. I noticed that, upon folding the typecover back, a vendor specific range of 4 32 bit integer hid usages is transmitted. Only the first byte of the message seems to convey reliable information about the keyboard state. 0x22 => Normal (keys enabled) 0x33 => Folded back (keys disabled) 0x53 => Rotated left/right side up (keys disabled) 0x13 => Cover closed (keys disabled) 0x43 => Folded back and Tablet upside down (keys disabled) This list may not be exhaustive. The tablet mode switch will be disabled for a value of 0x22 and enabled on any other value. Patchset: surface-typecover Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/hid-multitouch.c | 148 +++++++++++++++++++++++++++++------ 1 file changed, 122 insertions(+), 26 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 74a18881de2bc6..25e3425c0c0ada 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -84,6 +84,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_YOGABOOK9I BIT(24) #define MT_QUIRK_KEEP_LATENCY_ON_CLOSE BIT(25) #define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(26) +#define MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH BIT(27) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 @@ -92,6 +93,8 @@ MODULE_LICENSE("GPL"); #define MT_BUTTONTYPE_PRESSUREPAD 1 #define MS_TYPE_COVER_FEATURE_REPORT_USAGE 0xff050086 +#define MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE 0xff050072 +#define MS_TYPE_COVER_APPLICATION 0xff050050 enum latency_mode { HID_LATENCY_NORMAL = 0, @@ -466,6 +469,7 @@ static const struct mt_class mt_classes[] = { }, { .name = MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER, .quirks = MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT | + MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH | MT_QUIRK_ALWAYS_VALID | MT_QUIRK_IGNORE_DUPLICATES | MT_QUIRK_HOVERING | @@ -1513,6 +1517,9 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, field->application != HID_CP_CONSUMER_CONTROL && field->application != HID_GD_WIRELESS_RADIO_CTLS && field->application != HID_GD_SYSTEM_MULTIAXIS && + !(field->application == MS_TYPE_COVER_APPLICATION && + application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH && + usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) && !(field->application == HID_VD_ASUS_CUSTOM_MEDIA_KEYS && application->quirks & MT_QUIRK_ASUS_CUSTOM_UP)) return -1; @@ -1540,6 +1547,21 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 1; } + /* + * The Microsoft Surface Pro Typecover has a non-standard HID + * tablet mode switch on a vendor specific usage page with vendor + * specific usage. + */ + if (field->application == MS_TYPE_COVER_APPLICATION && + application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH && + usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) { + usage->type = EV_SW; + usage->code = SW_TABLET_MODE; + *max = SW_MAX; + *bit = hi->input->swbit; + return 1; + } + if (rdata->is_mt_collection) return mt_touch_input_mapping(hdev, hi, field, usage, bit, max, application); @@ -1566,6 +1588,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, { struct mt_device *td = hid_get_drvdata(hdev); struct mt_report_data *rdata; + struct input_dev *input; rdata = mt_find_report_data(td, field->report); if (rdata && rdata->is_mt_collection) { @@ -1573,6 +1596,19 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, return -1; } + /* + * We own an input device which acts as a tablet mode switch for + * the Surface Pro Typecover. + */ + if (field->application == MS_TYPE_COVER_APPLICATION && + rdata->application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH && + usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) { + input = hi->input; + input_set_capability(input, EV_SW, SW_TABLET_MODE); + input_report_switch(input, SW_TABLET_MODE, 0); + return -1; + } + /* let hid-core decide for the others */ return 0; } @@ -1582,11 +1618,21 @@ static int mt_event(struct hid_device *hid, struct hid_field *field, { struct mt_device *td = hid_get_drvdata(hid); struct mt_report_data *rdata; + struct input_dev *input; rdata = mt_find_report_data(td, field->report); if (rdata && rdata->is_mt_collection) return mt_touch_event(hid, field, usage, value); + if (field->application == MS_TYPE_COVER_APPLICATION && + rdata->application->quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH && + usage->hid == MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE) { + input = field->hidinput->input; + input_report_switch(input, SW_TABLET_MODE, (value & 0xFF) != 0x22); + input_sync(input); + return 1; + } + return 0; } @@ -1801,6 +1847,42 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app) app->quirks &= ~MT_QUIRK_CONTACT_CNT_ACCURATE; } +static int get_type_cover_field(struct hid_report_enum *rep_enum, + struct hid_field **field, int usage) +{ + struct hid_report *rep; + struct hid_field *cur_field; + int i, j; + + list_for_each_entry(rep, &rep_enum->report_list, list) { + for (i = 0; i < rep->maxfield; i++) { + cur_field = rep->field[i]; + if (cur_field->application != MS_TYPE_COVER_APPLICATION) + continue; + for (j = 0; j < cur_field->maxusage; j++) { + if (cur_field->usage[j].hid == usage) { + *field = cur_field; + return true; + } + } + } + } + return false; +} + +static void request_type_cover_tablet_mode_switch(struct hid_device *hdev) +{ + struct hid_field *field; + + if (get_type_cover_field(&hdev->report_enum[HID_INPUT_REPORT], + &field, + MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE)) { + hid_hw_request(hdev, field->report, HID_REQ_GET_REPORT); + } else { + hid_err(hdev, "couldn't find tablet mode field\n"); + } +} + static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) { struct mt_device *td = hid_get_drvdata(hdev); @@ -1859,6 +1941,13 @@ static int mt_input_configured(struct hid_device *hdev, struct hid_input *hi) /* force BTN_STYLUS to allow tablet matching in udev */ __set_bit(BTN_STYLUS, hi->input->keybit); break; + case MS_TYPE_COVER_APPLICATION: + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) { + suffix = "Tablet Mode Switch"; + request_type_cover_tablet_mode_switch(hdev); + break; + } + fallthrough; default: suffix = "UNKNOWN"; break; @@ -1969,30 +2058,6 @@ static void mt_expired_timeout(struct timer_list *t) clear_bit_unlock(MT_IO_FLAGS_RUNNING, &td->mt_io_flags); } -static void get_type_cover_backlight_field(struct hid_device *hdev, - struct hid_field **field) -{ - struct hid_report_enum *rep_enum; - struct hid_report *rep; - struct hid_field *cur_field; - int i, j; - - rep_enum = &hdev->report_enum[HID_FEATURE_REPORT]; - list_for_each_entry(rep, &rep_enum->report_list, list) { - for (i = 0; i < rep->maxfield; i++) { - cur_field = rep->field[i]; - - for (j = 0; j < cur_field->maxusage; j++) { - if (cur_field->usage[j].hid - == MS_TYPE_COVER_FEATURE_REPORT_USAGE) { - *field = cur_field; - return; - } - } - } - } -} - static void update_keyboard_backlight(struct hid_device *hdev, bool enabled) { struct usb_device *udev = hid_to_usb_dev(hdev); @@ -2001,8 +2066,9 @@ static void update_keyboard_backlight(struct hid_device *hdev, bool enabled) /* Wake up the device in case it's already suspended */ pm_runtime_get_sync(&udev->dev); - get_type_cover_backlight_field(hdev, &field); - if (!field) { + if (!get_type_cover_field(&hdev->report_enum[HID_FEATURE_REPORT], + &field, + MS_TYPE_COVER_FEATURE_REPORT_USAGE)) { hid_err(hdev, "couldn't find backlight field\n"); goto out; } @@ -2160,13 +2226,24 @@ static int mt_suspend(struct hid_device *hdev, pm_message_t state) static int mt_reset_resume(struct hid_device *hdev) { + struct mt_device *td = hid_get_drvdata(hdev); + mt_release_contacts(hdev); mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); + + /* Request an update on the typecover folding state on resume + * after reset. + */ + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) + request_type_cover_tablet_mode_switch(hdev); + return 0; } static int mt_resume(struct hid_device *hdev) { + struct mt_device *td = hid_get_drvdata(hdev); + /* Some Elan legacy devices require SET_IDLE to be set on resume. * It should be safe to send it to other devices too. * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */ @@ -2175,12 +2252,31 @@ static int mt_resume(struct hid_device *hdev) mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); + /* Request an update on the typecover folding state on resume. */ + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) + request_type_cover_tablet_mode_switch(hdev); + return 0; } static void mt_remove(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + struct hid_field *field; + struct input_dev *input; + + /* Reset tablet mode switch on disconnect. */ + if (td->mtclass.quirks & MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH) { + if (get_type_cover_field(&hdev->report_enum[HID_INPUT_REPORT], + &field, + MS_TYPE_COVER_TABLET_MODE_SWITCH_USAGE)) { + input = field->hidinput->input; + input_report_switch(input, SW_TABLET_MODE, 0); + input_sync(input); + } else { + hid_err(hdev, "couldn't find tablet mode field\n"); + } + } unregister_pm_notifier(&td->pm_notifier); timer_delete_sync(&td->release_timer); From 810e2380039cfac3734785ddc02d45a0dca31857 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 19 Feb 2023 22:12:24 +0100 Subject: [PATCH 136/258] BACKPORT: SURFACE: PCI: Add quirk to prevent calling shutdown method Work around buggy EFI firmware: On some Microsoft Surface devices (Surface Pro 9 and Surface Laptop 5) the EFI ResetSystem call with EFI_RESET_SHUTDOWN doesn't function properly. Instead of shutting the system down, it returns and the system stays on. It turns out that this only happens after PCI shutdown callbacks ran for specific devices. Excluding those devices from the shutdown process makes the ResetSystem call work as expected. TODO: Maybe we can find a better way or the root cause of this? Not-Signed-off-by: Maximilian Luz Patchset: surface-shutdown [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in include/linux/pci.h ] Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/pci/pci-driver.c | 3 +++ drivers/pci/quirks.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 1 + 3 files changed, 40 insertions(+) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 682c34a174dade..5790d901033e84 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -543,6 +543,9 @@ static void pci_device_shutdown(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct pci_driver *drv = pci_dev->driver; + if (pci_dev->no_shutdown) + return; + pm_runtime_resume(dev); if (drv && drv->shutdown) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d048a94f2e4981..bf02bf255ee630 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6446,3 +6446,39 @@ static void quirk_sg2042_no_port_services(struct pci_dev *dev) dev->dev_flags |= PCI_DEV_FLAGS_NO_PORT_SERVICES; } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SOPHGO, 0x2042, quirk_sg2042_no_port_services); + +static const struct dmi_system_id no_shutdown_dmi_table[] = { + /* + * Systems on which some devices should not be touched during shutdown. + */ + { + .ident = "Microsoft Surface Pro 9", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Pro 9"), + }, + }, + { + .ident = "Microsoft Surface Laptop 5", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 5"), + }, + }, + {} +}; + +static void quirk_no_shutdown(struct pci_dev *dev) +{ + if (!dmi_check_system(no_shutdown_dmi_table)) + return; + + dev->no_shutdown = 1; + pci_info(dev, "disabling shutdown ops for [%04x:%04x]\n", + dev->vendor, dev->device); +} +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x461e, quirk_no_shutdown); // Thunderbolt 4 USB Controller +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x461f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x462f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown); // Thunderbolt 4 NHI +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown); // GPU diff --git a/include/linux/pci.h b/include/linux/pci.h index c022efb28471bf..bfae14bc5e62a1 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -513,6 +513,7 @@ struct pci_dev { unsigned int rom_attr_enabled:1; /* Display of ROM attribute enabled? */ unsigned int non_mappable_bars:1; /* BARs can't be mapped to user-space */ unsigned int aspm_os_control:1; /* Display of ROM attribute enabled? */ + unsigned int no_shutdown:1; /* Do not touch device on shutdown */ pci_dev_flags_t dev_flags; atomic_t enable_cnt; /* pci_enable_device has been called */ From bd0876bc15498269f09259433161094774cc9bda Mon Sep 17 00:00:00 2001 From: LegendaryFire Date: Wed, 7 Jan 2026 01:06:25 +0100 Subject: [PATCH 137/258] SURFACE: PCI: Add Surface Laptop Studio 2 devices to shutdown ops quirk Link: https://github.com/linux-surface/linux-surface/pull/1948 Patchset: surface-shutdown Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/pci/quirks.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index bf02bf255ee630..3c612bdbab517f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -6465,6 +6465,13 @@ static const struct dmi_system_id no_shutdown_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface Laptop 5"), }, }, + { + .ident = "Microsoft Surface Laptop Studio 2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Laptop Studio 2"), + }, + }, {} }; @@ -6482,3 +6489,9 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x461f, quirk_no_shutdown); // Thu DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x462f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x466d, quirk_no_shutdown); // Thunderbolt 4 NHI DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x46a8, quirk_no_shutdown); // GPU + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa76e, quirk_no_shutdown); // Thunderbolt 4 USB Controller +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa71e, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa73f, quirk_no_shutdown); // Thunderbolt 4 PCI Express Root Port +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa73e, quirk_no_shutdown); // Thunderbolt 4 NHI +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0xa7a0, quirk_no_shutdown); // GPU From aad3393426b83904da87dd7919ca6f3651faa2f1 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 12 Mar 2023 01:41:57 +0100 Subject: [PATCH 138/258] SURFACE: platform/surface: gpe: Add support for Surface Pro 9 Add the lid GPE used by the Surface Pro 9. Signed-off-by: Maximilian Luz Patchset: surface-gpe Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/surface/surface_gpe.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c index b359413903b13c..b4496db79f392e 100644 --- a/drivers/platform/surface/surface_gpe.c +++ b/drivers/platform/surface/surface_gpe.c @@ -41,6 +41,11 @@ static const struct property_entry lid_device_props_l4F[] = { {}, }; +static const struct property_entry lid_device_props_l52[] = { + PROPERTY_ENTRY_U32("gpe", 0x52), + {}, +}; + static const struct property_entry lid_device_props_l57[] = { PROPERTY_ENTRY_U32("gpe", 0x57), {}, @@ -107,6 +112,18 @@ static const struct dmi_system_id dmi_lid_device_table[] = { }, .driver_data = (void *)lid_device_props_l4B, }, + { + /* + * We match for SKU here due to product name clash with the ARM + * version. + */ + .ident = "Surface Pro 9", + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Pro_9_2038"), + }, + .driver_data = (void *)lid_device_props_l52, + }, { .ident = "Surface Book 1", .matches = { From 1c14f4dbde5dc0fa2b16785c3e0e30dc9b7caf08 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Oct 2021 20:56:57 +0200 Subject: [PATCH 139/258] SURFACE: ACPI: delay enumeration of devices with a _DEP pointing to an INT3472 device The clk and regulator frameworks expect clk/regulator consumer-devices to have info about the consumed clks/regulators described in the device's fw_node. To work around cases where this info is not present in the firmware tables, which is often the case on x86/ACPI devices, both frameworks allow the provider-driver to attach info about consumers to the clks/regulators when registering these. This causes problems with the probe ordering wrt drivers for consumers of these clks/regulators. Since the lookups are only registered when the provider-driver binds, trying to get these clks/regulators before then results in a -ENOENT error for clks and a dummy regulator for regulators. One case where we hit this issue is camera sensors such as e.g. the OV8865 sensor found on the Microsoft Surface Go. The sensor uses clks, regulators and GPIOs provided by a TPS68470 PMIC which is described in an INT3472 ACPI device. There is special platform code handling this and setting platform_data with the necessary consumer info on the MFD cells instantiated for the PMIC under: drivers/platform/x86/intel/int3472. For this to work properly the ov8865 driver must not bind to the I2C-client for the OV8865 sensor until after the TPS68470 PMIC gpio, regulator and clk MFD cells have all been fully setup. The OV8865 on the Microsoft Surface Go is just one example, all X86 devices using the Intel IPU3 camera block found on recent Intel SoCs have similar issues where there is an INT3472 HID ACPI-device, which describes the clks and regulators, and the driver for this INT3472 device must be fully initialized before the sensor driver (any sensor driver) binds for things to work properly. On these devices the ACPI nodes describing the sensors all have a _DEP dependency on the matching INT3472 ACPI device (there is one per sensor). This allows solving the probe-ordering problem by delaying the enumeration (instantiation of the I2C-client in the ov8865 example) of ACPI-devices which have a _DEP dependency on an INT3472 device. The new acpi_dev_ready_for_enumeration() helper used for this is also exported because for devices, which have the enumeration_by_parent flag set, the parent-driver will do its own scan of child ACPI devices and it will try to enumerate those during its probe(). Code doing this such as e.g. the i2c-core-acpi.c code must call this new helper to ensure that it too delays the enumeration until all the _DEP dependencies are met on devices which have the new honor_deps flag set. Signed-off-by: Hans de Goede Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/acpi/scan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 3b0cf49acaed56..de8df157be0bcd 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2246,6 +2246,9 @@ static const char * const acpi_system_dev_ids[] = { static void acpi_default_enumeration(struct acpi_device *device) { + if (!acpi_dev_ready_for_enumeration(device)) + return; + /* * Do not enumerate devices with enumeration_by_parent flag set as * they will be enumerated by their respective parents. From e931436cc03d6ff2a469bc3fe106753e88f8bc8c Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Sun, 10 Oct 2021 20:57:02 +0200 Subject: [PATCH 140/258] SURFACE: platform/x86: int3472: Enable I2c daisy chain The TPS68470 PMIC has an I2C passthrough mode through which I2C traffic can be forwarded to a device connected to the PMIC as though it were connected directly to the system bus. Enable this mode when the chip is initialised. Signed-off-by: Daniel Scally Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/platform/x86/intel/int3472/tps68470.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c index a77ed32abe55f1..3ef46bace18745 100644 --- a/drivers/platform/x86/intel/int3472/tps68470.c +++ b/drivers/platform/x86/intel/int3472/tps68470.c @@ -46,6 +46,13 @@ static int tps68470_chip_init(struct device *dev, struct regmap *regmap) return ret; } + /* Enable I2C daisy chain */ + ret = regmap_write(regmap, TPS68470_REG_S_I2C_CTL, 0x03); + if (ret) { + dev_err(dev, "Failed to enable i2c daisy chain\n"); + return ret; + } + dev_info(dev, "TPS68470 REVID: 0x%02x\n", version); return 0; From caa6ed743eca3d985de0b16376de25b87ed7e58f Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Tue, 21 Mar 2023 13:45:26 +0000 Subject: [PATCH 141/258] SURFACE: media: i2c: Clarify that gain is Analogue gain in OV7251 Update the control ID for the gain control in the ov7251 driver to V4L2_CID_ANALOGUE_GAIN. Signed-off-by: Daniel Scally Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/i2c/ov7251.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index 27afc3fc01752e..28b192c9a5ec5b 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -1053,7 +1053,7 @@ static int ov7251_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE: ret = ov7251_set_exposure(ov7251, ctrl->val); break; - case V4L2_CID_GAIN: + case V4L2_CID_ANALOGUE_GAIN: ret = ov7251_set_gain(ov7251, ctrl->val); break; case V4L2_CID_TEST_PATTERN: @@ -1579,7 +1579,7 @@ static int ov7251_init_ctrls(struct ov7251 *ov7251) ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, V4L2_CID_EXPOSURE, 1, 32, 1, 32); ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops, - V4L2_CID_GAIN, 16, 1023, 1, 16); + V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 16); v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(ov7251_test_pattern_menu) - 1, From 7677397b56a7efc7dc62a244f8e54ab2ac6ff365 Mon Sep 17 00:00:00 2001 From: Daniel Scally Date: Wed, 22 Mar 2023 11:01:42 +0000 Subject: [PATCH 142/258] SURFACE: media: v4l2-core: Acquire privacy led in v4l2_async_register_subdev() The current call to v4l2_subdev_get_privacy_led() is contained in v4l2_async_register_subdev_sensor(), but that function isn't used by all the sensor drivers. Move the acquisition of the privacy led to v4l2_async_register_subdev() instead. Signed-off-by: Daniel Scally Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/v4l2-core/v4l2-async.c | 4 ++++ drivers/media/v4l2-core/v4l2-fwnode.c | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 888a2e213b0885..f1043fce13e273 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -811,6 +811,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module) INIT_LIST_HEAD(&sd->asc_list); + ret = v4l2_subdev_get_privacy_led(sd); + if (ret < 0) + return ret; + /* * No reference taken. The reference is held by the device (struct * v4l2_subdev.dev), and async sub-device does not exist independently diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 62a3a452f7884d..349aebb77f2e10 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -1270,10 +1270,6 @@ int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *m v4l2_async_subdev_nf_init(notifier, sd); - ret = v4l2_subdev_get_privacy_led(sd); - if (ret < 0) - goto out_cleanup; - ret = v4l2_async_nf_parse_fwnode_sensor(sd->dev, notifier); if (ret < 0) goto out_cleanup; From 8ffdf49904e5a3c75c9a74db4827cf40946f7426 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Tue, 21 Mar 2023 23:37:16 +0800 Subject: [PATCH 143/258] BACKPORT: SURFACE: platform: x86: int3472: Add MFD cell for tps68470 LED Add MFD cell for tps68470-led. Reviewed-by: Daniel Scally Signed-off-by: Kate Hsuan Reviewed-by: Hans de Goede Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in drivers/platform/x86/intel/int3472/tps68470.c ] Signed-off-by: Mingcong Bai --- drivers/platform/x86/intel/int3472/tps68470.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c index 3ef46bace18745..8dece4261fc4c4 100644 --- a/drivers/platform/x86/intel/int3472/tps68470.c +++ b/drivers/platform/x86/intel/int3472/tps68470.c @@ -17,7 +17,7 @@ #define DESIGNED_FOR_CHROMEOS 1 #define DESIGNED_FOR_WINDOWS 2 -#define TPS68470_WIN_MFD_CELL_COUNT 3 +#define TPS68470_WIN_MFD_CELL_COUNT 4 static const struct mfd_cell tps68470_cros[] = { { .name = "tps68470-gpio" }, @@ -205,6 +205,7 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client) cells[1].pdata_size = sizeof(struct tps68470_regulator_platform_data); cells[2].name = "tps68470-gpio"; cells[2].swnode = board_data->tps68470_gpio_swnode; + cells[3].name = "tps68470-led"; for (i = 0; i < board_data->n_gpiod_lookups; i++) gpiod_add_lookup_table(board_data->tps68470_gpio_lookup_tables[i]); From bfa59cdc8cca55bc4cabf5b9a8fdb89562ef48e4 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Tue, 21 Mar 2023 23:37:17 +0800 Subject: [PATCH 144/258] SURFACE: include: mfd: tps68470: Add masks for LEDA and LEDB Add flags for both LEDA(TPS68470_ILEDCTL_ENA), LEDB (TPS68470_ILEDCTL_ENB), and current control mask for LEDB (TPS68470_ILEDCTL_CTRLB) Reviewed-by: Daniel Scally Reviewed-by: Hans de Goede Signed-off-by: Kate Hsuan Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- include/linux/mfd/tps68470.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/linux/mfd/tps68470.h b/include/linux/mfd/tps68470.h index 7807fa329db001..2d2abb25b944f4 100644 --- a/include/linux/mfd/tps68470.h +++ b/include/linux/mfd/tps68470.h @@ -34,6 +34,7 @@ #define TPS68470_REG_SGPO 0x22 #define TPS68470_REG_GPDI 0x26 #define TPS68470_REG_GPDO 0x27 +#define TPS68470_REG_ILEDCTL 0x28 #define TPS68470_REG_VCMVAL 0x3C #define TPS68470_REG_VAUX1VAL 0x3D #define TPS68470_REG_VAUX2VAL 0x3E @@ -94,4 +95,8 @@ #define TPS68470_GPIO_MODE_OUT_CMOS 2 #define TPS68470_GPIO_MODE_OUT_ODRAIN 3 +#define TPS68470_ILEDCTL_ENA BIT(2) +#define TPS68470_ILEDCTL_ENB BIT(6) +#define TPS68470_ILEDCTL_CTRLB GENMASK(5, 4) + #endif /* __LINUX_MFD_TPS68470_H */ From a163da3f169fa518aa131c9b6e7115ce40e02837 Mon Sep 17 00:00:00 2001 From: Kate Hsuan Date: Tue, 21 Mar 2023 23:37:18 +0800 Subject: [PATCH 145/258] SURFACE: leds: tps68470: Add LED control for tps68470 There are two LED controllers, LEDA indicator LED and LEDB flash LED for tps68470. LEDA can be enabled by setting TPS68470_ILEDCTL_ENA. Moreover, tps68470 provides four levels of power status for LEDB. If the properties called "ti,ledb-current" can be found, the current will be set according to the property values. These two LEDs can be controlled through the LED class of sysfs (tps68470-leda and tps68470-ledb). Signed-off-by: Kate Hsuan Reviewed-by: Hans de Goede Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/leds/Kconfig | 12 +++ drivers/leds/Makefile | 1 + drivers/leds/leds-tps68470.c | 185 +++++++++++++++++++++++++++++++++++ 3 files changed, 198 insertions(+) create mode 100644 drivers/leds/leds-tps68470.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 22748404a7f061..7a1ad03e1db2c8 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -1022,6 +1022,18 @@ config LEDS_TPS6105X It is a single boost converter primarily for white LEDs and audio amplifiers. +config LEDS_TPS68470 + tristate "LED support for TI TPS68470" + depends on LEDS_CLASS + depends on INTEL_SKL_INT3472 + help + This driver supports TPS68470 PMIC with LED chip. + It provides two LED controllers, with the ability to drive 2 + indicator LEDs and 2 flash LEDs. + + To compile this driver as a module, choose M and it will be + called leds-tps68470 + config LEDS_IP30 tristate "LED support for SGI Octane machines" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 2b9506f9a69487..9d3cc8a0b6b460 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -95,6 +95,7 @@ obj-$(CONFIG_LEDS_TCA6507) += leds-tca6507.o obj-$(CONFIG_LEDS_TI_LMU_COMMON) += leds-ti-lmu-common.o obj-$(CONFIG_LEDS_TLC591XX) += leds-tlc591xx.o obj-$(CONFIG_LEDS_TPS6105X) += leds-tps6105x.o +obj-$(CONFIG_LEDS_TPS68470) += leds-tps68470.o obj-$(CONFIG_LEDS_TURRIS_OMNIA) += leds-turris-omnia.o obj-$(CONFIG_LEDS_UPBOARD) += leds-upboard.o obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o diff --git a/drivers/leds/leds-tps68470.c b/drivers/leds/leds-tps68470.c new file mode 100644 index 00000000000000..35aeb5db89c8fe --- /dev/null +++ b/drivers/leds/leds-tps68470.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * LED driver for TPS68470 PMIC + * + * Copyright (C) 2023 Red Hat + * + * Authors: + * Kate Hsuan + */ + +#include +#include +#include +#include +#include +#include + + +#define lcdev_to_led(led_cdev) \ + container_of(led_cdev, struct tps68470_led, lcdev) + +#define led_to_tps68470(led, index) \ + container_of(led, struct tps68470_device, leds[index]) + +enum tps68470_led_ids { + TPS68470_ILED_A, + TPS68470_ILED_B, + TPS68470_NUM_LEDS +}; + +static const char *tps68470_led_names[] = { + [TPS68470_ILED_A] = "tps68470-iled_a", + [TPS68470_ILED_B] = "tps68470-iled_b", +}; + +struct tps68470_led { + unsigned int led_id; + struct led_classdev lcdev; +}; + +struct tps68470_device { + struct device *dev; + struct regmap *regmap; + struct tps68470_led leds[TPS68470_NUM_LEDS]; +}; + +enum ctrlb_current { + CTRLB_2MA = 0, + CTRLB_4MA = 1, + CTRLB_8MA = 2, + CTRLB_16MA = 3, +}; + +static int tps68470_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) +{ + struct tps68470_led *led = lcdev_to_led(led_cdev); + struct tps68470_device *tps68470 = led_to_tps68470(led, led->led_id); + struct regmap *regmap = tps68470->regmap; + + switch (led->led_id) { + case TPS68470_ILED_A: + return regmap_update_bits(regmap, TPS68470_REG_ILEDCTL, TPS68470_ILEDCTL_ENA, + brightness ? TPS68470_ILEDCTL_ENA : 0); + case TPS68470_ILED_B: + return regmap_update_bits(regmap, TPS68470_REG_ILEDCTL, TPS68470_ILEDCTL_ENB, + brightness ? TPS68470_ILEDCTL_ENB : 0); + } + return -EINVAL; +} + +static enum led_brightness tps68470_brightness_get(struct led_classdev *led_cdev) +{ + struct tps68470_led *led = lcdev_to_led(led_cdev); + struct tps68470_device *tps68470 = led_to_tps68470(led, led->led_id); + struct regmap *regmap = tps68470->regmap; + int ret = 0; + int value = 0; + + ret = regmap_read(regmap, TPS68470_REG_ILEDCTL, &value); + if (ret) + return dev_err_probe(led_cdev->dev, -EINVAL, "failed on reading register\n"); + + switch (led->led_id) { + case TPS68470_ILED_A: + value = value & TPS68470_ILEDCTL_ENA; + break; + case TPS68470_ILED_B: + value = value & TPS68470_ILEDCTL_ENB; + break; + } + + return value ? LED_ON : LED_OFF; +} + + +static int tps68470_ledb_current_init(struct platform_device *pdev, + struct tps68470_device *tps68470) +{ + int ret = 0; + unsigned int curr; + + /* configure LEDB current if the properties can be got */ + if (!device_property_read_u32(&pdev->dev, "ti,ledb-current", &curr)) { + if (curr > CTRLB_16MA) { + dev_err(&pdev->dev, + "Invalid LEDB current value: %d\n", + curr); + return -EINVAL; + } + ret = regmap_update_bits(tps68470->regmap, TPS68470_REG_ILEDCTL, + TPS68470_ILEDCTL_CTRLB, curr); + } + return ret; +} + +static int tps68470_leds_probe(struct platform_device *pdev) +{ + int i = 0; + int ret = 0; + struct tps68470_device *tps68470; + struct tps68470_led *led; + struct led_classdev *lcdev; + + tps68470 = devm_kzalloc(&pdev->dev, sizeof(struct tps68470_device), + GFP_KERNEL); + if (!tps68470) + return -ENOMEM; + + tps68470->dev = &pdev->dev; + tps68470->regmap = dev_get_drvdata(pdev->dev.parent); + + for (i = 0; i < TPS68470_NUM_LEDS; i++) { + led = &tps68470->leds[i]; + lcdev = &led->lcdev; + + led->led_id = i; + + lcdev->name = devm_kasprintf(tps68470->dev, GFP_KERNEL, "%s::%s", + tps68470_led_names[i], LED_FUNCTION_INDICATOR); + if (!lcdev->name) + return -ENOMEM; + + lcdev->max_brightness = 1; + lcdev->brightness = 0; + lcdev->brightness_set_blocking = tps68470_brightness_set; + lcdev->brightness_get = tps68470_brightness_get; + lcdev->dev = &pdev->dev; + + ret = devm_led_classdev_register(tps68470->dev, lcdev); + if (ret) { + dev_err_probe(tps68470->dev, ret, + "error registering led\n"); + goto err_exit; + } + + if (i == TPS68470_ILED_B) { + ret = tps68470_ledb_current_init(pdev, tps68470); + if (ret) + goto err_exit; + } + } + +err_exit: + if (ret) { + for (i = 0; i < TPS68470_NUM_LEDS; i++) { + if (tps68470->leds[i].lcdev.name) + devm_led_classdev_unregister(&pdev->dev, + &tps68470->leds[i].lcdev); + } + } + + return ret; +} +static struct platform_driver tps68470_led_driver = { + .driver = { + .name = "tps68470-led", + }, + .probe = tps68470_leds_probe, +}; + +module_platform_driver(tps68470_led_driver); + +MODULE_ALIAS("platform:tps68470-led"); +MODULE_DESCRIPTION("LED driver for TPS68470 PMIC"); +MODULE_LICENSE("GPL v2"); From 57136f6aefbe42f4952efbb4fa9de6de392152b9 Mon Sep 17 00:00:00 2001 From: mojyack Date: Tue, 26 Mar 2024 05:55:44 +0900 Subject: [PATCH 146/258] SURFACE: media: i2c: dw9719: fix probe error on surface go 2 On surface go 2, sometimes probing dw9719 fails with "dw9719: probe of i2c-INT347A:00-VCM failed with error -121". The -121(-EREMOTEIO) is came from drivers/i2c/busses/i2c-designware-common.c:575, and indicates the initialize occurs too early. So just add some delay. There is no exact reason for this 10000us, but 100us failed. Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/i2c/dw9719.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c index 3b7ba88fd67c04..44088850780227 100644 --- a/drivers/media/i2c/dw9719.c +++ b/drivers/media/i2c/dw9719.c @@ -121,6 +121,9 @@ static int dw9719_power_up(struct dw9719_device *dw9719, bool detect) if (ret) return ret; + /* Wait for device to be acknowledged */ + fsleep(10000); + /* * Need 100us to transition from SHUTDOWN to STANDBY. * Jiggle the SCL pin to wake up the device (even when the regulator is From 1720530134649b5ecc905b44d6217e285fd34c1a Mon Sep 17 00:00:00 2001 From: Tooraj Taraz Date: Wed, 31 Dec 2025 00:35:50 +0100 Subject: [PATCH 147/258] BACKPORT: SURFACE: Add camera support for Surface Pro 9 Experimental camera support for the Surface Pro 9. Link: https://github.com/linux-surface/linux-surface/pull/1867 Patchset: cameras [ Mingcong Bai: Since commit 2a7b7652b1bb ("platform/x86: int3472: Handle GPIO type 0x10 (DOVDD)") took the 0x10 entry used by the original patch for Surface Pro 9's secondary power rail, change the latter to 0x11 to adapt to this change. Link: https://github.com/linux-surface/linux-surface/issues/2057 Link: https://github.com/linux-surface/linux-surface/pull/2079 ] Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/i2c/ov13858.c | 158 +++++++++++++++++- drivers/media/i2c/ov5693.c | 1 + drivers/media/pci/intel/ipu-bridge.c | 4 + drivers/platform/x86/intel/int3472/discrete.c | 23 +++ 4 files changed, 184 insertions(+), 2 deletions(-) diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index 162b49046990a1..85cd0c98af031e 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -2,6 +2,7 @@ // Copyright (c) 2017 Intel Corporation. #include +#include #include #include #include @@ -119,6 +120,14 @@ struct ov13858_mode { struct ov13858_reg_list reg_list; }; +// Or use standard names that might be more correct: +static const char * const ov13858_supply_names[] = { + "avdd", // Analog voltage + "pwr1", // Digital core voltage +}; + +#define OV13858_NUM_SUPPLIES ARRAY_SIZE(ov13858_supply_names) + /* 4224x3136 needs 1080Mbps/lane, 4 lanes */ static const struct ov13858_reg mipi_data_rate_1080mbps[] = { /* PLL1 registers */ @@ -1046,12 +1055,126 @@ struct ov13858 { /* Current mode */ const struct ov13858_mode *cur_mode; + struct regulator_bulk_data supplies[OV13858_NUM_SUPPLIES]; + struct gpio_desc *reset; + struct clk *xvclk; + /* Mutex for serialized access */ struct mutex mutex; }; #define to_ov13858(_sd) container_of(_sd, struct ov13858, sd) + +static int ov13858_get_regulators(struct ov13858 *ov13858) +{ + unsigned int i; + + for (i = 0; i < OV13858_NUM_SUPPLIES; i++) + ov13858->supplies[i].supply = ov13858_supply_names[i]; + + return devm_regulator_bulk_get(ov13858->dev, OV13858_NUM_SUPPLIES, + ov13858->supplies); +} + +static int ov13858_sensor_powerup(struct ov13858 *ov13858) +{ + int ret; + + dev_info(ov13858->dev, "Powering up sensor\n"); + + /* Assert reset */ + gpiod_set_value_cansleep(ov13858->reset, 1); + + /* Enable clock FIRST - sensor needs clock to communicate */ + ret = clk_prepare_enable(ov13858->xvclk); + if (ret) { + dev_err(ov13858->dev, "Failed to enable clock: %d\n", ret); + return ret; + } + dev_info(ov13858->dev, "Clock enabled\n"); + + /* Enable regulators */ + ret = regulator_bulk_enable(OV13858_NUM_SUPPLIES, ov13858->supplies); + if (ret) { + dev_err(ov13858->dev, "Failed to enable regulators: %d\n", ret); + clk_disable_unprepare(ov13858->xvclk); + return ret; + } + + dev_info(ov13858->dev, "Regulators enabled\n"); + + /* Wait for power to stabilize */ + usleep_range(5000, 10000); + + /* De-assert reset */ + gpiod_set_value_cansleep(ov13858->reset, 0); + + /* Wait for sensor to boot */ + msleep(20); + + dev_info(ov13858->dev, "Reset de-asserted, sensor should be ready\n"); + + return 0; +} + +static void ov13858_sensor_powerdown(struct ov13858 *ov13858) +{ + dev_info(ov13858->dev, "Powering down sensor\n"); + + /* Assert reset to put sensor in reset state */ + gpiod_set_value_cansleep(ov13858->reset, 1); + + /* Disable regulators */ + regulator_bulk_disable(OV13858_NUM_SUPPLIES, ov13858->supplies); + dev_info(ov13858->dev, "Regulators disabled\n"); + + /* Disable clock */ + clk_disable_unprepare(ov13858->xvclk); + dev_info(ov13858->dev, "Clock disabled\n"); +} + +static int __maybe_unused ov13858_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov13858 *ov13858 = to_ov13858(sd); + + ov13858_sensor_powerdown(ov13858); + + return 0; +} + +static int __maybe_unused ov13858_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct ov13858 *ov13858 = to_ov13858(sd); + int ret; + + ret = ov13858_sensor_powerup(ov13858); + if (ret) + return ret; + + return 0; +} + +static const struct dev_pm_ops ov13858_pm_ops = { + SET_RUNTIME_PM_OPS(ov13858_suspend, ov13858_resume, NULL) +}; + +static int ov13858_get_gpios(struct ov13858 *ov13858) +{ + ov13858->reset = devm_gpiod_get_optional(ov13858->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(ov13858->reset)) { + dev_err(ov13858->dev, "Error fetching reset GPIO\n"); + return PTR_ERR(ov13858->reset); + } + + return 0; +} + /* Read registers up to 4 at a time */ static int ov13858_read_reg(struct ov13858 *ov13858, u16 reg, u32 len, u32 *val) @@ -1498,8 +1621,11 @@ static int ov13858_identify_module(struct ov13858 *ov13858) int ret; u32 val; + dev_info(ov13858->dev, "Attempting to read chip ID from register 0x300a\n"); ret = ov13858_read_reg(ov13858, OV13858_REG_CHIP_ID, OV13858_REG_VALUE_24BIT, &val); + dev_info(ov13858->dev, "Chip ID read result: ret=%d, val=0x%06x (expected 0x%06x)\n", + ret, val, OV13858_CHIP_ID); if (ret) return ret; @@ -1509,6 +1635,7 @@ static int ov13858_identify_module(struct ov13858 *ov13858) return -EIO; } + dev_info(ov13858->dev, "Chip ID verified successfully!\n"); return 0; } @@ -1678,14 +1805,33 @@ static int ov13858_probe(struct i2c_client *client) "external clock %lu is not supported\n", freq); + /* Get the external clock */ + ov13858->xvclk = devm_clk_get_optional(ov13858->dev, "xvclk"); + if (IS_ERR(ov13858->xvclk)) + return dev_err_probe(ov13858->dev, PTR_ERR(ov13858->xvclk), + "failed to get xvclk\n"); + /* Initialize subdev */ v4l2_i2c_subdev_init(&ov13858->sd, client, &ov13858_subdev_ops); + ret = ov13858_get_regulators(ov13858); + if (ret) + return dev_err_probe(ov13858->dev, ret, + "Error fetching regulators\n"); + + ret = ov13858_get_gpios(ov13858); + if (ret) + return ret; + + ret = ov13858_sensor_powerup(ov13858); + if (ret) + return ret; // No cleanup needed yet, devm handles GPIOs/regulators + /* Check module identity */ ret = ov13858_identify_module(ov13858); if (ret) { dev_err(ov13858->dev, "failed to find sensor: %d\n", ret); - return ret; + goto error_power_off; // Now we need to power down } /* Set default mode to max resolution */ @@ -1693,7 +1839,7 @@ static int ov13858_probe(struct i2c_client *client) ret = ov13858_init_controls(ov13858); if (ret) - return ret; + goto error_power_off; /* Initialize subdev */ ov13858->sd.internal_ops = &ov13858_internal_ops; @@ -1729,6 +1875,10 @@ static int ov13858_probe(struct i2c_client *client) error_handler_free: ov13858_free_controls(ov13858); + +error_power_off: + ov13858_sensor_powerdown(ov13858); + dev_err(ov13858->dev, "%s failed:%d\n", __func__, ret); return ret; @@ -1744,6 +1894,9 @@ static void ov13858_remove(struct i2c_client *client) ov13858_free_controls(ov13858); pm_runtime_disable(ov13858->dev); + if (!pm_runtime_status_suspended(ov13858->dev)) + ov13858_sensor_powerdown(ov13858); + pm_runtime_set_suspended(ov13858->dev); } static const struct i2c_device_id ov13858_id_table[] = { @@ -1765,6 +1918,7 @@ MODULE_DEVICE_TABLE(acpi, ov13858_acpi_ids); static struct i2c_driver ov13858_i2c_driver = { .driver = { .name = "ov13858", + .pm = &ov13858_pm_ops, .acpi_match_table = ACPI_PTR(ov13858_acpi_ids), }, .probe = ov13858_probe, diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c index 4cc796bbee9263..02236f3db19dc7 100644 --- a/drivers/media/i2c/ov5693.c +++ b/drivers/media/i2c/ov5693.c @@ -1396,6 +1396,7 @@ static const struct dev_pm_ops ov5693_pm_ops = { static const struct acpi_device_id ov5693_acpi_match[] = { {"INT33BE"}, + {"OVTI5693"}, {}, }; MODULE_DEVICE_TABLE(acpi, ov5693_acpi_match); diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index fc6608e33de40a..7ce4692d33b871 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -61,6 +61,10 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = { IPU_SENSOR_CONFIG("INT33BE", 1, 419200000), /* Onsemi MT9M114 */ IPU_SENSOR_CONFIG("INT33F0", 1, 384000000), + /* Omnivision OV5693 - Surface Pro 9 */ + IPU_SENSOR_CONFIG("OVTI5693", 1, 419200000), + /* Omnivision OV13858 - Surface Pro 9 */ + IPU_SENSOR_CONFIG("OVTID858", 4, 540000000), /* Omnivision OV2740 */ IPU_SENSOR_CONFIG("INT3474", 1, 180000000), /* Omnivision OV5670 */ diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 115bb37577a18d..b2a54733df2021 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -237,6 +237,14 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 /* Setups using a handshake pin need 25 ms enable delay */ *enable_time_us = 25 * USEC_PER_MSEC; break; + case 0x08: /* Surface Pro 9 - additional power rail */ + *con_id = "pwr1"; + *gpio_flags = GPIO_ACTIVE_HIGH; + break; + case 0x11: /* Surface Pro 9 - secondary power rail */ + *con_id = "pwr2"; + *gpio_flags = GPIO_ACTIVE_HIGH; + break; default: *con_id = "unknown"; *gpio_flags = GPIO_ACTIVE_HIGH; @@ -345,6 +353,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, case INT3472_GPIO_TYPE_POWER_ENABLE: case INT3472_GPIO_TYPE_DOVDD: case INT3472_GPIO_TYPE_HANDSHAKE: + case 0x08: /* Surface Pro 9 power rails */ + case 0x11: gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, con_id, gpio_flags); if (IS_ERR(gpio)) { ret = PTR_ERR(gpio); @@ -377,6 +387,19 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, err_msg = "Failed to register regulator\n"; break; + case 0x08: /* Surface Pro 9 - treat as power*/ + dev_info(int3472->dev, "GPIO type 0x%02x detected on pin 0x%02x\n", type, agpio->pin_table[0]); + dev_info(int3472->dev, " con_id=%s, flags=0x%x\n", con_id, gpio_flags); + ret = skl_int3472_register_regulator(int3472, gpio, + GPIO_REGULATOR_ENABLE_TIME, + con_id, NULL); + dev_info(int3472->dev, " register_regulator returned: %d\n", ret); + if (ret) { + dev_err(int3472->dev, "Failed to register type 0x02x: %d\n", type, ret); + } + break; + case 0x11: + break; default: /* Never reached */ ret = -EINVAL; break; From ca8dfca8db0e989c2758d9f9ae1b6f8a9d798016 Mon Sep 17 00:00:00 2001 From: gabrielstedman Date: Fri, 13 Mar 2026 08:52:07 +0000 Subject: [PATCH 148/258] SURFACE: media: ipu-bridge: Add front camera rotation quirks for Surface Pro 8 and 9 On the Surface Pro 8 and 9, camera rotation in `ssdb.degree` is `0` (no rotation) but should be `180`. Add these devices to the respective quirk list. Link: https://github.com/linux-surface/kernel/pull/160 Patchset: cameras Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/media/pci/intel/ipu-bridge.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c index 7ce4692d33b871..209767f44bd21f 100644 --- a/drivers/media/pci/intel/ipu-bridge.c +++ b/drivers/media/pci/intel/ipu-bridge.c @@ -138,6 +138,20 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = { }, .driver_data = "OVTI02C1", }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Pro 8"), + }, + .driver_data = "INT33BE", + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Pro 9"), + }, + .driver_data = "OVTI5693", + }, {} /* Terminating entry */ }; From 5579d51cf61c2bbe610e275cc5fe2ec6cb2a3780 Mon Sep 17 00:00:00 2001 From: Sachi King Date: Sat, 29 May 2021 17:47:38 +1000 Subject: [PATCH 149/258] SURFACE: ACPI: Add quirk for Surface Laptop 4 AMD missing irq 7 override This patch is the work of Thomas Gleixner and is copied from: https://lore.kernel.org/lkml/87lf8ddjqx.ffs@nanos.tec.linutronix.de/ This patch adds a quirk to the ACPI setup to patch in the the irq 7 pin setup that is missing in the laptops ACPI table. This patch was used for validation of the issue, and is not a proper fix, but is probably a better temporary hack than continuing to probe the Legacy PIC and run with the PIC in an unknown state. Patchset: amd-gpio Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- arch/x86/kernel/acpi/boot.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index ceba24f65ae3a8..b9bb3748642e93 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -1176,6 +1177,17 @@ static void __init mp_config_acpi_legacy_irqs(void) } } +static const struct dmi_system_id surface_quirk[] __initconst = { + { + .ident = "Microsoft Surface Laptop 4 (AMD)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1952:1953") + }, + }, + {} +}; + /* * Parse IOAPIC related entries in MADT * returns 0 on success, < 0 on error @@ -1232,6 +1244,11 @@ static int __init acpi_parse_madt_ioapic_entries(void) acpi_sci_ioapic_setup(acpi_gbl_FADT.sci_interrupt, 0, 0, acpi_gbl_FADT.sci_interrupt); + if (dmi_check_system(surface_quirk)) { + pr_warn("Surface hack: Override irq 7\n"); + mp_override_legacy_irq(7, 3, 3, 7); + } + /* Fill in identity legacy mappings where no override */ mp_config_acpi_legacy_irqs(); From a90266fcd83a74166672d90cbaaacedb06c347dd Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Thu, 3 Jun 2021 14:04:26 +0200 Subject: [PATCH 150/258] SURFACE: ACPI: Add AMD 13" Surface Laptop 4 model to irq 7 override quirk The 13" version of the Surface Laptop 4 has the same problem as the 15" version, but uses a different SKU. Add that SKU to the quirk as well. Patchset: amd-gpio Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- arch/x86/kernel/acpi/boot.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index b9bb3748642e93..07384e7718e7e1 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -1179,12 +1179,19 @@ static void __init mp_config_acpi_legacy_irqs(void) static const struct dmi_system_id surface_quirk[] __initconst = { { - .ident = "Microsoft Surface Laptop 4 (AMD)", + .ident = "Microsoft Surface Laptop 4 (AMD 15\")", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1952:1953") }, }, + { + .ident = "Microsoft Surface Laptop 4 (AMD 13\")", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1958:1959") + }, + }, {} }; From 567af451087cfa4fc92daa26da6b74572248625c Mon Sep 17 00:00:00 2001 From: "Bart Groeneveld | GPX Solutions B.V" Date: Mon, 5 Dec 2022 16:08:46 +0100 Subject: [PATCH 151/258] BACKPORT: SURFACE: acpi: allow usage of acpi_tad on HW-reduced platforms The specification [1] allows so-called HW-reduced platforms, which do not implement everything, especially the wakeup related stuff. In that case, it is still usable as a RTC. This is helpful for [2] and [3], which is about a device with no other working RTC, but it does have an HW-reduced TAD, which can be used as a RTC instead. [1]: https://uefi.org/specs/ACPI/6.5/09_ACPI_Defined_Devices_and_Device_Specific_Objects.html#time-and-alarm-device [2]: https://bugzilla.kernel.org/show_bug.cgi?id=212313 [3]: https://github.com/linux-surface/linux-surface/issues/415 Signed-off-by: Bart Groeneveld | GPX Solutions B.V. Patchset: rtc Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai [ Mingcong Bai: Attempted to revise this patch since commit 8be4a29b495e ("ACPI: TAD: Create one attribute group") ] Signed-off-by: Mingcong Bai --- drivers/acpi/acpi_tad.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index fc43df083738c4..ce0794e076aae6 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -570,7 +570,6 @@ static ssize_t dc_status_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR_RW(dc_status); static struct attribute *acpi_tad_attrs[] = { - &dev_attr_caps.attr, &dev_attr_ac_alarm.attr, &dev_attr_ac_policy.attr, &dev_attr_ac_status.attr, @@ -835,11 +834,6 @@ static int acpi_tad_probe(struct platform_device *pdev) return -ENODEV; } - if (!acpi_has_method(handle, "_PRW")) { - dev_info(dev, "Missing _PRW\n"); - caps &= ~(ACPI_TAD_AC_WAKE | ACPI_TAD_DC_WAKE); - } - if (!(caps & ACPI_TAD_AC_WAKE)) caps &= ~ACPI_TAD_DC_WAKE; From 58fed3074a7be8eb1aa955ef2fb1481bce7f973b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciar=C3=A1n=20Coffey?= Date: Fri, 10 Oct 2025 19:04:03 +0100 Subject: [PATCH 152/258] SURFACE: HID: Add hid-surface driver to filter BTN_0 (FN key) The Surface Aggregator Module firmware incorrectly reports the FN key as BTN_0 (button 256) through runtime HID events. This button can get stuck in pressed state, flooding the input system and breaking focus tracking in KWin Wayland compositor. Add a new hid-surface driver that filters BTN_0 events using the event callback. The FN key should be handled as a hardware modifier and not reported to the OS. Tested on Surface Laptop 4 AMD with KDE Plasma Wayland. After the fix: - FN key combinations work correctly (volume, brightness) - Focus tracking no longer breaks - Mouse clicks work on all windows Fixes: https://github.com/linux-surface/linux-surface/issues/1851 Patchset: hid-surface Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/Kconfig | 8 ++++ drivers/hid/Makefile | 1 + drivers/hid/hid-surface.c | 90 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 drivers/hid/hid-surface.c diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 2886bde63605c3..1640864b9c8e13 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1159,6 +1159,14 @@ config HID_SUNPLUS help Support for Sunplus wireless desktop. +config HID_SURFACE + tristate "Microsoft Surface" + depends on SURFACE_AGGREGATOR + help + Say Y here to enable HID driver for Microsoft Surface integrated + keyboard and touchpad. This driver filters out erroneous BTN_0 + (FN key) events that can cause input focus issues. + config HID_RMI tristate "Synaptics RMI4 device support" select RMI4_CORE diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fdc4e3d459d15a..e71461250b981f 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -178,6 +178,7 @@ obj-$(CONFIG_INTEL_ISH_HID) += intel-ish-hid/ obj-$(CONFIG_AMD_SFH_HID) += amd-sfh-hid/ obj-$(CONFIG_SURFACE_HID_CORE) += surface-hid/ +obj-$(CONFIG_HID_SURFACE) += hid-surface.o obj-$(CONFIG_INTEL_THC_HID) += intel-thc-hid/ diff --git a/drivers/hid/hid-surface.c b/drivers/hid/hid-surface.c new file mode 100644 index 00000000000000..a171ea65672fea --- /dev/null +++ b/drivers/hid/hid-surface.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * HID driver for Microsoft Surface devices + * + * Copyright (c) 2025 Linux Surface Project + */ + +#include +#include +#include +#include + +#include "hid-ids.h" + +/* + * The Surface Aggregator Module firmware incorrectly reports the FN key + * as BTN_0 (button 256). This button can get stuck in pressed state, + * flooding the input system and breaking focus tracking in some + * compositors. Filter out BTN_0 as FN should be handled as a hardware + * modifier, not reported to the OS. + */ +static int surface_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + /* + * Filter BTN_0 during input mapping in case it appears in the + * HID descriptor (defense in depth). + */ + if (usage->type == EV_KEY && usage->code == BTN_0) + return -1; /* Don't map this usage */ + + return 0; /* Use default mapping */ +} + +static int surface_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + /* + * The Surface Aggregator Module firmware reports the FN key as BTN_0 + * at runtime. This button can get stuck in pressed state, flooding + * the input system and breaking focus tracking. Filter out these + * events as FN should be a hardware modifier, not reported to the OS. + */ + if (usage->type == EV_KEY && usage->code == BTN_0) + return 1; /* Event handled, don't process further */ + + return 0; /* Process event normally */ +} + +static int surface_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + return ret; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) { + hid_err(hdev, "hw start failed\n"); + return ret; + } + + return 0; +} + +static const struct hid_device_id surface_devices[] = { + { HID_DEVICE(BUS_HOST, HID_GROUP_GENERIC, + USB_VENDOR_ID_MICROSOFT, 0x09AE) }, /* Surface Keyboard */ + { HID_DEVICE(BUS_HOST, HID_GROUP_GENERIC, + USB_VENDOR_ID_MICROSOFT, 0x09AF) }, /* Surface Mouse/Touchpad */ + { } +}; +MODULE_DEVICE_TABLE(hid, surface_devices); + +static struct hid_driver surface_driver = { + .name = "surface", + .id_table = surface_devices, + .probe = surface_probe, + .input_mapping = surface_input_mapping, + .event = surface_event, +}; +module_hid_driver(surface_driver); + +MODULE_AUTHOR("Linux Surface Project"); +MODULE_DESCRIPTION("Microsoft Surface HID driver"); +MODULE_LICENSE("GPL"); From 85d3f439b4055561a1a5af96af9962c65befa7f7 Mon Sep 17 00:00:00 2001 From: LegendaryFire Date: Fri, 13 Mar 2026 10:16:37 +0100 Subject: [PATCH 153/258] SURFACE: hid/multitouch: Prevent mode set on Surface Laptop Studio 2 touch pad Note: Is this still required with MT_QUIRK_KEEP_LATENCY_ON_CLOSE now being available? Patchset: hid-surface Link: https://github.com/linux-surface/linux-surface/tree/4c136ec928ce0b458562c7533852db67a8e0dc91 Signed-off-by: Mingcong Bai --- drivers/hid/hid-multitouch.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 25e3425c0c0ada..944ed209088882 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -85,6 +85,7 @@ MODULE_LICENSE("GPL"); #define MT_QUIRK_KEEP_LATENCY_ON_CLOSE BIT(25) #define MT_QUIRK_HAS_TYPE_COVER_BACKLIGHT BIT(26) #define MT_QUIRK_HAS_TYPE_COVER_TABLET_MODE_SWITCH BIT(27) +#define MT_QUIRK_SKIP_MODESET_ON_HW_OPEN_CLOSE BIT(28) #define MT_INPUTMODE_TOUCHSCREEN 0x02 #define MT_INPUTMODE_TOUCHPAD 0x03 @@ -248,6 +249,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); #define MT_CLS_YOGABOOK9I 0x0115 #define MT_CLS_EGALAX_P80H84 0x0116 #define MT_CLS_WIN_8_MS_SURFACE_TYPE_COVER 0x0117 +#define MT_CLS_SURFACE_TOUCHPAD 0x0118 #define MT_CLS_SIS 0x0457 #define MT_DEFAULT_MAXCONTACT 10 @@ -478,6 +480,10 @@ static const struct mt_class mt_classes[] = { MT_QUIRK_WIN8_PTP_BUTTONS, .export_all_inputs = true }, + { .name = MT_CLS_SURFACE_TOUCHPAD, + .quirks = MT_QUIRK_ALWAYS_VALID | + MT_QUIRK_SKIP_MODESET_ON_HW_OPEN_CLOSE + }, { } }; @@ -2287,6 +2293,17 @@ static void mt_remove(struct hid_device *hdev) static void mt_on_hid_hw_open(struct hid_device *hdev) { + struct mt_device *td = hid_get_drvdata(hdev); + + /* + * Some devices (e.g. Surface Laptop Studio 2 touchpad) can get stuck + * non-functional if we change touchpad reporting modes from the HID + * open/close hooks. Avoid mode switching on hw_open/hw_close for + * those devices. + */ + if (td && td->mtclass.quirks & MT_QUIRK_SKIP_MODESET_ON_HW_OPEN_CLOSE) + return; + mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_ALL); } @@ -2294,6 +2311,15 @@ static void mt_on_hid_hw_close(struct hid_device *hdev) { struct mt_device *td = hid_get_drvdata(hdev); + /* + * Some devices (e.g. Surface Laptop Studio 2 touchpad) can get stuck + * non-functional if we change touchpad reporting modes from the HID + * open/close hooks. Avoid mode switching on hw_open/hw_close for + * those devices. + */ + if (td && td->mtclass.quirks & MT_QUIRK_SKIP_MODESET_ON_HW_OPEN_CLOSE) + return; + if (td->mtclass.quirks & MT_QUIRK_KEEP_LATENCY_ON_CLOSE) mt_set_modes(hdev, HID_LATENCY_NORMAL, TOUCHPAD_REPORT_NONE); else @@ -2751,6 +2777,11 @@ static const struct hid_device_id mt_devices[] = { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_MICROSOFT, 0x09c0) }, + /* Microsoft Surface touch pad */ + { .driver_data = MT_CLS_SURFACE_TOUCHPAD, + HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, + USB_VENDOR_ID_MICROSOFT, 0x0C46) }, + /* Google MT devices */ { .driver_data = MT_CLS_GOOGLE, HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_GOOGLE, From ffd7455943ec685926544519edb9b79b5824dceb Mon Sep 17 00:00:00 2001 From: Han Gao Date: Mon, 24 Nov 2025 20:38:44 +0800 Subject: [PATCH 154/258] XUANTIE: riscv: dts: th1520: add licheepi4a 16g support From: https://github.com/revyos/th1520-linux-kernel/commit/01a510898e41e704bee1fe58a2c0c0a29cb96548 Signed-off-by: Han Gao Signed-off-by: Mingcong Bai --- arch/riscv/boot/dts/thead/Makefile | 1 + .../boot/dts/thead/th1520-lichee-pi-4a-16g.dts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 arch/riscv/boot/dts/thead/th1520-lichee-pi-4a-16g.dts diff --git a/arch/riscv/boot/dts/thead/Makefile b/arch/riscv/boot/dts/thead/Makefile index b55a17127c2bc0..281849e71ccb84 100644 --- a/arch/riscv/boot/dts/thead/Makefile +++ b/arch/riscv/boot/dts/thead/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 dtb-$(CONFIG_ARCH_THEAD) += th1520-lichee-pi-4a.dtb th1520-beaglev-ahead.dtb +dtb-$(CONFIG_ARCH_THEAD) += th1520-lichee-pi-4a-16g.dtb diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a-16g.dts b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a-16g.dts new file mode 100644 index 00000000000000..a3a991baf716be --- /dev/null +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a-16g.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (C) 2023 Han Gao + */ + +/dts-v1/; + +#include "th1520-lichee-pi-4a.dts" + +/ { + model = "Sipeed Lichee Pi 4A 16G"; + compatible = "sipeed,lichee-pi-4a", "sipeed,lichee-module-4a", "thead,th1520"; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x00000000 0x4 0x00000000>; + }; +}; From 16bda64b327bc1ecb0889cd8993941f854e7dc1e Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 14 May 2025 08:16:15 +0800 Subject: [PATCH 155/258] REVYOS: HACK: riscv: dts: th1520: rename thead to xuantie From: https://github.com/revyos/th1520-linux-kernel/commit/fe55f7b77b81749bfd2a5eac9328e016f299d7a6 HACK patch for compatibility with thead-u-boot and vendor opensbi Signed-off-by: Han Gao [Icenowy: preserve the original compatible to allow Linux to match] Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- arch/riscv/boot/dts/thead/th1520.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index e44010810c0766..f85d93227170fb 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -286,7 +286,7 @@ }; aon: aon { - compatible = "thead,th1520-aon"; + compatible = "xuantie,th1520-aon", "thead,th1520-aon"; mboxes = <&mbox_910t 1>; mbox-names = "aon"; resets = <&rst TH1520_RESET_ID_GPU_CLKGEN>; From 854b5b5e796cb82aa2a3e7c65499823102792e53 Mon Sep 17 00:00:00 2001 From: Han Gao Date: Wed, 14 May 2025 08:27:18 +0800 Subject: [PATCH 156/258] REVYOS: HACK: riscv: dts: th1520: add xuantie,th1520-mbox-r From: https://github.com/revyos/th1520-linux-kernel/commit/1e57185ba18320bb4fb747a6089f9404f970964c HACK patch for compatibility with thead-u-boot and vendor opensbi Signed-off-by: Han Gao [Icenowy: remove the interrupt-controller property] Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- arch/riscv/boot/dts/thead/th1520.dtsi | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/thead/th1520.dtsi index f85d93227170fb..ab681cf850d196 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -292,6 +292,24 @@ resets = <&rst TH1520_RESET_ID_GPU_CLKGEN>; reset-names = "gpu-clkgen"; #power-domain-cells = <1>; + opensbi-mboxes = <&mbox_910r>; + status = "okay"; + }; + + mbox_910r: mbox@ffefc53000 { + compatible = "xuantie,th1520-mbox-r"; + reg = <0xff 0xefc53000 0x0 0x4000>, + <0xff 0xefc3f000 0x0 0x1000>, + <0xff 0xefc47000 0x0 0x1000>, + <0xff 0xefc4f000 0x0 0x1000>; + reg-names = "local_base", + "remote_icu0", + "remote_icu1", + "remote_icu2"; + clocks = <&clk CLK_PERI_APB_PCLK>; + clock-names = "ipg"; + icu_cpu_id = <3>; + #mbox-cells = <2>; }; soc { From 755f231f03d970391d73bf99d039a4cea2835526 Mon Sep 17 00:00:00 2001 From: Zhang Meng Date: Mon, 5 Jan 2026 20:05:04 +0800 Subject: [PATCH 157/258] SPACEMIT: riscv: uaccess: don't use vector if buffer is not cacheable FROM: https://github.com/spacemit-com/linux-6.18/commit/9168f7e0c6bfdcfa3b6a64a4d45e3cd68a81618f Change-Id: I040d597ee246777767f7be747fa9202154524538 [ Vivian: Rebase and move check into enter_vector_usercopy ] Signed-off-by: Vivian Wang Signed-off-by: Han Gao --- arch/riscv/include/asm/uaccess.h | 5 +++ arch/riscv/lib/Makefile | 1 + arch/riscv/lib/riscv_v_helpers.c | 5 +++ arch/riscv/lib/uaccess_cache_check.c | 65 ++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+) create mode 100644 arch/riscv/lib/uaccess_cache_check.c diff --git a/arch/riscv/include/asm/uaccess.h b/arch/riscv/include/asm/uaccess.h index 11c9886c3b704b..69bdc070f4204b 100644 --- a/arch/riscv/include/asm/uaccess.h +++ b/arch/riscv/include/asm/uaccess.h @@ -487,6 +487,11 @@ static inline void user_access_restore(unsigned long enabled) { } if (__asm_copy_from_user_sum_enabled(_dst, _src, _len)) \ goto label; +/* Memory cacheability check for vector uaccess optimization */ +#ifdef CONFIG_RISCV_ISA_V +int is_cacheable_safe(const void *addr); +#endif + #else /* CONFIG_MMU */ #include #endif /* CONFIG_MMU */ diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 6f767b2a349d76..01c53576be98dd 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -14,6 +14,7 @@ endif lib-y += csum.o ifeq ($(CONFIG_MMU), y) lib-$(CONFIG_RISCV_ISA_V) += uaccess_vector.o +lib-$(CONFIG_RISCV_ISA_V) += uaccess_cache_check.o endif lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_64BIT) += tishift.o diff --git a/arch/riscv/lib/riscv_v_helpers.c b/arch/riscv/lib/riscv_v_helpers.c index 7bbdfc6d4552d4..7ab2cea280f8bb 100644 --- a/arch/riscv/lib/riscv_v_helpers.c +++ b/arch/riscv/lib/riscv_v_helpers.c @@ -8,6 +8,7 @@ #include #include +#include #ifdef CONFIG_MMU #include @@ -28,6 +29,10 @@ asmlinkage int enter_vector_usercopy(void *dst, void *src, size_t n, if (!may_use_simd()) goto fallback; + /* HACK */ + if (!is_cacheable_safe(dst) || !is_cacheable_safe(src)) + goto fallback; + kernel_vector_begin(); remain = enable_sum ? __asm_vector_usercopy(dst, src, n) : __asm_vector_usercopy_sum_enabled(dst, src, n); diff --git a/arch/riscv/lib/uaccess_cache_check.c b/arch/riscv/lib/uaccess_cache_check.c new file mode 100644 index 00000000000000..0b0996fa2d371a --- /dev/null +++ b/arch/riscv/lib/uaccess_cache_check.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Memory cacheability check for RISC-V uaccess optimization + * + * This file provides a C function that can be called from assembly + * to determine if a buffer is cacheable before using vector instructions. + */ + +#include +#include +#include + +/** + * is_cacheable_safe - Check if memory buffer is cacheable + * @addr: Virtual address to check (kernel or user space) + * + * Returns: 1 if cacheable, 0 if non-cacheable + * + * This function is designed to be called from assembly code in uaccess.S + * to determine if vector instructions are safe to use for memory copy. + * + * Non-cacheable memory (device IO, DMA coherent buffers) should not use + * vector instructions as they may cause cache coherency issues. + * + * Handles both kernel and user space addresses safely: + * - Kernel direct mapping: Always cacheable + * - Kernel vmalloc: Check VM flags + * - User space: Check page table (most are cacheable) + * - ioremap/DMA: Non-cacheable + */ +int is_cacheable_safe(const void *addr) +{ + unsigned long vaddr = (unsigned long)addr; + + /* Kernel direct mapped memory - always cacheable */ + if (virt_addr_valid(addr)) + return 1; + + if (vaddr < TASK_SIZE) { + /* + * User space address, Determine it as a cacheable buffer, + * maybe not safe!! + */ + return 1; + } + + /* Check if it's a vmalloc region (kernel virtual address) */ + if (is_vmalloc_addr(addr)) { + struct vm_struct *vm; + + vm = find_vm_area(addr); + if (!vm) + return 0; + + /* Exclude ioremap and DMA coherent buffers */ + if (vm->flags & (VM_IOREMAP | VM_DMA_COHERENT)) + return 0; + + /* Normal vmalloc - cacheable */ + return 1; + } + + /* Unknown kernel region - assume non-cacheable for safety */ + return 0; +} From 5c6f01e4f8bbe2bfef1f4eacd2e5b6556ae4b19f Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Sat, 24 Jan 2026 08:48:53 +0800 Subject: [PATCH 158/258] RUYI: riscv: dts: spacemit: k3: Add USB2.0 support FROM: https://github.com/spacemit-com/linux/commit/6f1578894e4484f8a6724aceff099d2e90450e10 The USB2.0 controller on Pico-ITX board connnect to a Terminus FE1.1 Hub which fully USB2.0 protocol compliant and provides 4 ports. Signed-off-by: Yixun Lan Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 25 ++++++++++++++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 31 ++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index 61cbf924830b07..ac965ec83f2c0d 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -33,6 +33,15 @@ regulator-max-microvolt = <5000000>; regulator-always-on; }; + + aux_vcc3v3: regulator-aux-vcc3v3 { + compatible = "regulator-fixed"; + regulator-name = "AUX_VCC3V3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + }; &i2c8 { @@ -255,3 +264,19 @@ pinctrl-0 = <&uart0_0_cfg>; status = "okay"; }; + +&usb2_host { + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + + hub@1 { + compatible = "usb1a40,0101"; + reg = <1>; + vdd-supply = <&aux_vcc3v3>; + }; +}; + +&usb2_phy { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 5b17612fe58ea9..66dcabd0a81567 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -637,6 +637,37 @@ status = "disabled"; }; + usb2_host: usb@c0a00000 { + compatible = "spacemit,k3-dwc3"; + reg = <0x0 0xc0a00000 0x0 0x10000>; + clocks = <&syscon_apmu CLK_APMU_USB2_BUS>; + clock-names = "usbdrd30"; + resets = <&syscon_apmu RESET_APMU_USB2_AHB>, + <&syscon_apmu RESET_APMU_USB2_VCC>, + <&syscon_apmu RESET_APMU_USB2_PHY>; + reset-names = "ahb", "vcc", "phy"; + interrupts = <105 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + phys = <&usb2_phy>; + phy-names = "usb2-phy"; + phy_type = "utmi"; + snps,dis_enblslpm_quirk; + snps,dis_u2_susphy_quirk; + snps,dis-del-phy-power-chg-quirk; + snps,dis-tx-ipgap-linecheck-quirk; + dr_mode = "host"; + maximum-speed = "high-speed"; + status = "disabled"; + }; + + usb2_phy: phy@c0a20000 { + compatible = "spacemit,k3-usb2-phy"; + reg = <0x0 0xc0a20000 0x0 0x200>; + clocks = <&syscon_apmu CLK_APMU_USB2_BUS>; + #phy-cells = <0>; + status = "disabled"; + }; + syscon_apbc: system-controller@d4015000 { compatible = "spacemit,k3-syscon-apbc"; reg = <0x0 0xd4015000 0x0 0x1000>; From 972797a44e57a3af0b29c02a65181a2625379c56 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Fri, 13 Feb 2026 09:01:58 +0800 Subject: [PATCH 159/258] RUYI: dt-bindings: phy: Add Spacemit K3 USB3/PCIe comb phy support The USB3/PCIe comb PHY on the K3 is a complex PHY group that can provide multiple phy for both PCIe and USB controller. Its mux configuration is controlled by the APMU syscon device. Signed-off-by: Inochi Amaoto Signed-off-by: Han Gao --- .../bindings/phy/spacemit,k3-combo-phy.yaml | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml b/Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml new file mode 100644 index 00000000000000..eafc753b7e9bdf --- /dev/null +++ b/Documentation/devicetree/bindings/phy/spacemit,k3-combo-phy.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/spacemit,k3-combo-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Spacemit K3 PCIE/USB3 Comb PHY + +maintainers: + - Inochi Amaoto + +properties: + compatible: + const: spacemit,k3-combo-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 2 + description: + The first one is phy id, the second one is phy type. + + spacemit,apb-spare: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to APB SPARE system controller interface, used for + PHY calibration. + + spacemit,apmu: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle of APMU syscon + - description: configuration of the PHY lanes + description: | + Phandle to control PHY mux configuration. The configuration + is described as follows: + bit 4: 0 - PCIe A x8 mode, 1 - PCIe lane share mode + bit 3: 0 - PCIe A x4 mode, 1 - PCIe A x2 and PCIe B x2 mode + bit 2: 0 - PCIe C lane 0 is PCIe mode , 1 - USB mode + bit 1: 0 - PCIe C lane 1 is PCIe mode , 1 - USB mode + bit 0: 0 - PCIe D lane is PCIe mode , 1 - USB mode + + The bit[3:0] is only valid when bit 4 is 1. + +required: + - compatible + - reg + - "#phy-cells" + - spacemit,apb-spare + - spacemit,apmu + +additionalProperties: false + +examples: + - | + phy@81d00000 { + compatible = "spacemit,k3-combo-phy"; + reg = <0x81d00000 0x600000>; + #phy-cells = <2>; + spacemit,apb-spare = <&apb_spare>; + spacemit,apmu = <&apmu 0x00>; + }; From 0e7ecda2d6e9417387fbc2bb7c1189dd4a16185c Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Fri, 13 Feb 2026 09:09:58 +0800 Subject: [PATCH 160/258] RUYI: phy: spacemit: Add USB3/PCIe comb PHY driver for Spacemit K3 The comb PHY on K3 requires to configure a syscon device for the right mux configuration. And it requires calibration before any usage. Add USB3/PCIe comb PHY driver for Spacemit K3. Signed-off-by: Inochi Amaoto Signed-off-by: Han Gao --- drivers/phy/spacemit/Kconfig | 16 ++ drivers/phy/spacemit/Makefile | 2 + drivers/phy/spacemit/phy-k3-combo.c | 252 ++++++++++++++++++ drivers/phy/spacemit/phy-k3-common.c | 372 +++++++++++++++++++++++++++ drivers/phy/spacemit/phy-k3-common.h | 27 ++ 5 files changed, 669 insertions(+) create mode 100644 drivers/phy/spacemit/phy-k3-combo.c create mode 100644 drivers/phy/spacemit/phy-k3-common.c create mode 100644 drivers/phy/spacemit/phy-k3-common.h diff --git a/drivers/phy/spacemit/Kconfig b/drivers/phy/spacemit/Kconfig index 50b0005acf6634..5fdf18fce4995c 100644 --- a/drivers/phy/spacemit/Kconfig +++ b/drivers/phy/spacemit/Kconfig @@ -23,3 +23,19 @@ config PHY_SPACEMIT_K1_USB2 help Enable this to support K1 USB 2.0 PHY driver. This driver takes care of enabling and clock setup and will be used by K1 udc/ehci/otg/xhci driver. + +config PHY_SPACEMIT_K3_COMMON_OPS + tristate + select MFD_SYSCON + select GENERIC_PHY + +config PHY_SPACEMIT_K3_COMBO_PHY + tristate "SpacemiT K3 USB3/PCIe PHY support" + depends on (ARCH_SPACEMIT || COMPILE_TEST) && OF + depends on COMMON_CLK + select PHY_SPACEMIT_K3_COMMON_OPS + help + Enable this to support K3 USB3/PCIe combo PHY driver. This + driver takes care of enabling and clock setup and will be used + by K3 dwc3 driver. + If unsure, say N. diff --git a/drivers/phy/spacemit/Makefile b/drivers/phy/spacemit/Makefile index a821a21d614229..f679c1851b4f0b 100644 --- a/drivers/phy/spacemit/Makefile +++ b/drivers/phy/spacemit/Makefile @@ -1,3 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_PHY_SPACEMIT_K1_PCIE) += phy-k1-pcie.o obj-$(CONFIG_PHY_SPACEMIT_K1_USB2) += phy-k1-usb2.o +obj-$(CONFIG_PHY_SPACEMIT_K3_COMBO_PHY) += phy-k3-combo.o +obj-$(CONFIG_PHY_SPACEMIT_K3_COMMON_OPS) += phy-k3-common.o diff --git a/drivers/phy/spacemit/phy-k3-combo.c b/drivers/phy/spacemit/phy-k3-combo.c new file mode 100644 index 00000000000000..abd0aad18893ed --- /dev/null +++ b/drivers/phy/spacemit/phy-k3-combo.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * phy-k3-usb3.c - SpacemiT K3 Type-C Orientation Switch Driver + * + * Copyright (c) 2025 SpacemiT Technology Co. Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "phy-k3-common.h" + +/* + * The PCIE/USB Subsystem on SpacemiT K3 have 3 single lane PIPE3 PHYs + * (PHY2/3/4) shared by PCIE PortC/D and USB3 PortB/C/D. + * + * PMUA_PCIE_SUBSYS_MGMT[4:0] + * + * bit4 = 0 : PCIe A X8 mode, all 8 lanes dedicated to PCIe Port A + * 1 : PHY lanes shared between PCIe or USB according to [3:0] + * + * All PHY matrix combinations according to [4:0]: + * + * 0x0X : PCIe-A X8 + * 0x10 : PCIe-C x2 (PHY2+PHY3) + PCIe-D x1 (PHY4) + * 0x11 : PCIe-C x2 (PHY2+PHY3) + USB-D (PHY4) + * 0x12 : PCIe-C x1 (PHY2) + USB-C (PHY3) + * 0x13 : PCIe-C x1 (PHY2) + USB-C (PHY3) + USB-D (PHY4) + * 0x14 : PCIe-C x1 (PHY3) + USB-B (PHY2) + * 0x15 : PCIe-C x1 (PHY3) + USB-B (PHY2) + USB-D (PHY4) + * 0x16 : USB-B (PHY2) + USB-C (PHY3) + PCIe D x1 (PHY4) + * 0x17 : USB-B (PHY2) + USB-C (PHY3) + USB-D (PHY4) + * + * So any USB Port B/C/D operation requires PCIe A X8 mode to be disabled. + */ +#define PMUA_PCIE_SUBSYS_MGMT 0x1d8 +#define PU_MATRIX_CONF_MASK GENMASK(4, 0) + +#define COMBPHY_MAX_SUBPHYS 6 + +struct k3_combo_phy { + struct device *dev; + struct k3_lane_group groups[COMBPHY_MAX_SUBPHYS]; + void __iomem *base; + struct regmap *apb_spare; +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group0 = { + .lanes = 2, + .config = 0xff, + .mask = 0x00, + .offsets = { + 0x0, 0x400 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group1 = { + .lanes = 2, + .config = 0xff, + .mask = 0x00, + .offsets = { + 0x100000, 0x100400 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group2 = { + .lanes = 1, + .config = 0x14, + .mask = 0x14, + .offsets = { + 0x200000 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group3 = { + .lanes = 1, + .config = 0x12, + .mask = 0x12, + .offsets = { + 0x300000 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group4 = { + .lanes = 1, + .config = 0x11, + .mask = 0x11, + .offsets = { + 0x400000 + }, +}; + +static const struct k3_phy_lane_group_data k3_combphy_lane_group5 = { + .lanes = 1, + .config = 0xff, + .mask = 0x00, + .offsets = { + 0x500000 + }, +}; + +static const struct k3_phy_lane_group_data *k3_combphy_lane_datas[] = { + &k3_combphy_lane_group0, + &k3_combphy_lane_group1, + &k3_combphy_lane_group2, + &k3_combphy_lane_group3, + &k3_combphy_lane_group4, + &k3_combphy_lane_group5, +}; + +static int k3_combo_phy_init_lanes(struct k3_combo_phy *phy, unsigned int config) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(k3_combphy_lane_datas); i++) { + const struct k3_phy_lane_group_data *data = k3_combphy_lane_datas[i]; + struct k3_lane_group *lg = &phy->groups[i]; + const struct phy_ops *ops; + bool is_usb; + + is_usb = (data->mask & config) == data->config; + if (is_usb) + ops = &k3_usb3_phy_ops; + else + ops = &k3_pcie_phy_ops; + + dev_dbg(phy->dev, "phy %d is %s\n", i, is_usb ? "usb" : "pcie"); + + lg->phy = devm_phy_create(phy->dev, NULL, ops); + if (IS_ERR(lg->phy)) + return PTR_ERR(lg->phy); + + lg->is_pcie = !is_usb; + lg->data = data; + lg->base = phy->base; + phy_set_drvdata(lg->phy, lg); + } + + return 0; +} + +static int k3_combo_phy_update_config(struct regmap *apmu, unsigned int config) +{ + if (config & ~PU_MATRIX_CONF_MASK) + return -EINVAL; + + return regmap_update_bits(apmu, PMUA_PCIE_SUBSYS_MGMT, PU_MATRIX_CONF_MASK, config); +} + +static struct phy *k3_combo_phy_xlate(struct device *dev, const struct of_phandle_args *args) +{ + struct k3_combo_phy *phy = dev_get_drvdata(dev); + struct k3_lane_group *lg; + + if (args->args_count != 2) { + dev_err(dev, "Invalid number of arguments\n"); + return ERR_PTR(-EINVAL); + } + + if (args->args[0] >= ARRAY_SIZE(k3_combphy_lane_datas)) { + dev_err(dev, "Invalid PHY id\n"); + return ERR_PTR(-EINVAL); + } + + lg = &phy->groups[args->args[0]]; + + if ((lg->is_pcie && args->args[1] != PHY_TYPE_PCIE) || + (!lg->is_pcie && args->args[1] != PHY_TYPE_USB3)) { + dev_err(dev, "Invalid PHY mode\n"); + return ERR_PTR(-EINVAL); + } + + return lg->phy; +} + +static int k3_combo_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct phy_provider *provider; + struct k3_combo_phy *phy; + struct regmap *apmu; + u32 config = 0; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(phy->base)) + return PTR_ERR(phy->base); + + phy->apb_spare = syscon_regmap_lookup_by_phandle(node, "spacemit,apb-spare"); + if (IS_ERR(phy->apb_spare)) + return dev_err_probe(dev, PTR_ERR(phy->apb_spare), + "Failed to fine APB SPARE syscon"); + + apmu = syscon_regmap_lookup_by_phandle_args(node, "spacemit,apmu", 1, &config); + if (IS_ERR(apmu)) + return dev_err_probe(dev, PTR_ERR(apmu), + "Failed to find APMU syscon"); + + ret = k3_combo_phy_update_config(apmu, config); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to set lane configuration"); + + phy->dev = dev; + platform_set_drvdata(pdev, phy); + + ret = k3_phy_calibrate(phy->apb_spare); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to calibrate phy"); + + ret = k3_combo_phy_init_lanes(phy, config); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to init lanes"); + + provider = devm_of_phy_provider_register(dev, k3_combo_phy_xlate); + if (IS_ERR(provider)) + return dev_err_probe(dev, PTR_ERR(provider), + "Failed to register provider\n"); + + return 0; +} + +static const struct of_device_id k3_combo_phy_of_match[] = { + { .compatible = "spacemit,k3-combo-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, k3_combo_phy_of_match); + +static struct platform_driver k3_combo_phy_driver = { + .probe = k3_combo_phy_probe, + .driver = { + .name = "spacemit,k3-combo-phy", + .of_match_table = k3_combo_phy_of_match, + }, +}; +module_platform_driver(k3_combo_phy_driver); + +MODULE_DESCRIPTION("SpacemiT K3 USB3/PCIe combo PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/spacemit/phy-k3-common.c b/drivers/phy/spacemit/phy-k3-common.c new file mode 100644 index 00000000000000..840524cbe5334b --- /dev/null +++ b/drivers/phy/spacemit/phy-k3-common.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "phy-k3-common.h" + +/* PHY Registers */ +#define PHY_VERSION 0x0 + +#define PHY_RESET_CFG 0x04 + +#define PHY_RESET_RXBUF_RST BIT(0) +#define PHY_RESET_SOFT_RST_PCS BIT(1) +#define PHY_RESET_SOFT_RST_AHB BIT(2) +#define PHY_RESET_EN_SD_AFTER_LOCK BIT(6) + +#define PHY_CLK_CFG 0x08 + +#define PHY_CLK_PLL_READY BIT(0) +#define PHY_CLK_TXCLK_INV BIT(2) +#define PHY_CLK_RXCLK_EN BIT(3) +#define PHY_CLK_TXCLK_EN BIT(4) +#define PHY_CLK_PCLK_EN BIT(5) +#define PHY_CLK_PIPE_PCLK_EN BIT(6) +#define PHY_CLK_REFCLK_FREQ GENMASK(10, 7) +#define PHY_CLK_REFCLK_24M 2 +#define PHY_CLK_SW_INIT_DONE BIT(11) +#define PHY_CLK_PU_SSC_OUT BIT(23) + +#define PHY_MODE_CFG 0x0C + +#define PHY_MODE_PCIE_INT_EN BIT(0) +#define PHY_MODE_LFPS_TPERIOD GENMASK(9, 8) +#define PHY_MODE_LFPS_TPERIOD_USB 3 + +#define PHY_PU_SEL 0x40 + +#define PHY_PU_CFG_STATUS BIT(9) +#define PHY_PU_OVRD_STATUS BIT(10) + +#define PHY_PU_CK_REG 0x54 + +#define PHY_PU_REFCLK_100 BIT(25) + +#define PHY_PLL_REG1 0x58 + +#define PHY_PLL_FREF_SEL GENMASK(15, 13) +#define PHY_PLL_FREF_24M 0x1 +#define PHY_PLL_SSC_DEP_SEL GENMASK(27, 24) +#define PHY_PLL_SSC_5000PPM 0xa +#define PHY_PLL_SSC_MODE GENMASK(29, 28) +#define PHY_PLL_SSC_MODE_CENTER_SPREAD 0 +#define PHY_PLL_SSC_MODE_UP_SPREAD 1 +#define PHY_PLL_SSC_MODE_DOWN_SPREAD 2 +#define PHY_PLL_SSC_MODE_DOWN_SPREAD1 3 + +#define PHY_PLL_REG2 0x5c + +#define PHY_PLL_SEL_REF100 BIT(21) + +/* PHY RX Register Definitions */ +#define PHY_RX_REG_A 0x60 + +#define PHY_RX_REG0_RLOAD BIT(4) +#define PHY_RX_REG1_RTERM GENMASK(11, 8) +#define PHY_RX_REG1_RC_CALI GENMASK(15, 12) +#define PHY_RX_REG2_CSEL GENMASK(19, 16) +#define PHY_RX_REG2_FORCE_CSEL BIT(20) +#define PHY_RX_REG2_PSEL GENMASK(23, 21) +#define PHY_RX_REG3_I_LOAD GENMASK(26, 24) +#define PHY_RX_REG3_SEL_CBOOST_CODE BIT(27) +#define PHY_RX_REG3_ADJ_BIAS GENMASK(29, 28) +#define PHY_RX_REG3_RDEG1 GENMASK(31, 30) + +#define PHY_RX_REG_B 0x64 + +#define PHY_RX_REGB_MASK GENMASK(23, 0) + +#define PHY_RX_REG4_RDEG2 GENMASK(2, 1) +#define PHY_RX_REG4_ENVOS BIT(4) +#define PHY_RX_REG4_RTERM_SEL BIT(5) +#define PHY_RX_REG4_MANUAL_CFG BIT(7) +#define PHY_RX_REG5_RCELL_VCM GENMASK(11, 8) +#define PHY_RX_REG5_RCELL_BIAS GENMASK(15, 12) +#define PHY_RX_REG6_H1_REG GENMASK(19, 16) +#define PHY_RX_REG6_ADAPT_GAIN GENMASK(21, 20) +#define PHY_RX_REG6_BYPASS_ADPT BIT(22) + +#define PHY_ADPT_CFG0 0x140 +#define PHY_ADPT_AFE_RST_OVRD_EN BIT(1) +#define PHY_ADPT_AFE_RST_OVRD_VAL BIT(4) + +#define PHY_RXEQ_TIME 0xb4 +#define PHY_RXEQ_TIME_OVRD_POST_C_SOC BIT(21) +#define PHY_RXEQ_TIME_CFG_AMP_SOC GENMASK(23, 22) +#define PHY_RXEQ_TIME_AMP_SOC_650M 0 +#define PHY_RXEQ_TIME_AMP_SOC_800M 1 +#define PHY_RXEQ_TIME_AMP_SOC_870M 2 +#define PHY_RXEQ_TIME_AMP_SOC_900M 3 +#define PHY_RXEQ_TIME_OVRD_AMP_SOC BIT(24) + +#define PCIE_PU_ADDR_CLK_CFG 0x0008 +#define PHY_CLK_PLL_READY BIT(0) +#define PCIE_INITAL_TIMER GENMASK(6, 3) +#define CFG_INTERNAL_TIMER_ADJ GENMASK(10, 7) +#define CFG_SW_PHY_INIT_DONE BIT(11) + +/* Lane RX/TX configuration (per‑lane, at lane_base) */ +#define PCIE_RX_REG1 0x050 +#define PCIE_TX_REG1 0x064 + +#define PCIE_PLL_TIMEOUT 500000 +#define PCIE_POLL_DELAY 500 + +static int k3_usb3phy_init_single(struct k3_lane_group *lg, void __iomem *base) +{ + struct phy *phy = lg->phy; + u32 val, tmp; + int ret; + + /* Do not wait CDR lock before sampling data */ + val = readl(base + PHY_RESET_CFG); + val = u32_replace_bits(val, 0, PHY_RESET_EN_SD_AFTER_LOCK); + writel(val, base + PHY_RESET_CFG); + + /* Power down 100MHz refclk buffer */ + val = readl(base + PHY_PU_CK_REG); + val = u32_replace_bits(val, 0, PHY_PU_REFCLK_100); + writel(val, base + PHY_PU_CK_REG); + + /* Program PLL REG1 configure the SSC */ + val = FIELD_PREP(PHY_PLL_SSC_MODE, PHY_PLL_SSC_MODE_DOWN_SPREAD1) | + FIELD_PREP(PHY_PLL_SSC_DEP_SEL, PHY_PLL_SSC_5000PPM) | + FIELD_PREP(PHY_PLL_FREF_SEL, PHY_PLL_FREF_24M); + writel(val, base + PHY_PLL_REG1); + + /* Un-select 100MHz PLL reference */ + val = readl(base + PHY_PLL_REG2); + val = u32_replace_bits(val, 0, PHY_PLL_SEL_REF100); + writel(val, base + PHY_PLL_REG2); + + /* USB LFPS period configuration */ + val = readl(base + PHY_MODE_CFG); + val = u32_replace_bits(val, PHY_MODE_LFPS_TPERIOD_USB, PHY_MODE_LFPS_TPERIOD); + writel(val, base + PHY_MODE_CFG); + + /* Force AFE adaptation reset */ + val = readl(base + PHY_ADPT_CFG0); + val |= PHY_ADPT_AFE_RST_OVRD_EN | PHY_ADPT_AFE_RST_OVRD_VAL; + writel(val, base + PHY_ADPT_CFG0); + + /* Override driver amplitude value to 900m */ + val = readl(base + PHY_RXEQ_TIME); + val |= PHY_RXEQ_TIME_OVRD_AMP_SOC; + val = u32_replace_bits(val, PHY_RXEQ_TIME_AMP_SOC_900M, PHY_RXEQ_TIME_CFG_AMP_SOC); + writel(val, base + PHY_RXEQ_TIME); + + /* Configure RX parameters */ + val = PHY_RX_REG0_RLOAD | + FIELD_PREP(PHY_RX_REG1_RTERM, 0x8) | + FIELD_PREP(PHY_RX_REG1_RC_CALI, 0x7) | + FIELD_PREP(PHY_RX_REG2_CSEL, 0x8) | + PHY_RX_REG2_FORCE_CSEL | + FIELD_PREP(PHY_RX_REG2_PSEL, 0x4) | + FIELD_PREP(PHY_RX_REG3_I_LOAD, 0x7) | + PHY_RX_REG3_SEL_CBOOST_CODE | + FIELD_PREP(PHY_RX_REG3_ADJ_BIAS, 0x1) | + FIELD_PREP(PHY_RX_REG3_RDEG1, 0x3); + writel(val, base + PHY_RX_REG_A); + + val = readl(base + PHY_RX_REG_B); + tmp = FIELD_PREP(PHY_RX_REG4_RDEG2, 0x2) | + PHY_RX_REG4_ENVOS | PHY_RX_REG4_RTERM_SEL | PHY_RX_REG4_MANUAL_CFG | + FIELD_PREP(PHY_RX_REG5_RCELL_VCM, 0x8) | + FIELD_PREP(PHY_RX_REG5_RCELL_BIAS, 0x8) | + FIELD_PREP(PHY_RX_REG6_H1_REG, 0x8) | + FIELD_PREP(PHY_RX_REG6_ADAPT_GAIN, 0x2); + val = u32_replace_bits(val, tmp, PHY_RX_REGB_MASK); + writel(val, base + PHY_RX_REG_B); + + /* + * Inform PHY that all PLL-related configuration is done. + * PLL will not start locking until PHY_CLK_SW_INIT_DONE is set. + */ + val = PHY_CLK_SW_INIT_DONE | PHY_CLK_PU_SSC_OUT | + FIELD_PREP(PHY_CLK_REFCLK_FREQ, PHY_CLK_REFCLK_24M) | + PHY_CLK_RXCLK_EN | PHY_CLK_TXCLK_EN | + PHY_CLK_PCLK_EN | PHY_CLK_PIPE_PCLK_EN; + writel(val, base + PHY_CLK_CFG); + + ret = readl_poll_timeout(base + PHY_CLK_CFG, val, + (val & PHY_CLK_PLL_READY), + PCIE_POLL_DELAY, PCIE_PLL_TIMEOUT); + if (ret) { + dev_err(&phy->dev, "PHY PLL polling timeout\n"); + return ret; + } + + return 0; +} + +static int k3_usb3phy_init(struct phy *phy) +{ + struct k3_lane_group *lg = phy_get_drvdata(phy); + int ret, i; + + for (i = 0; i < lg->data->lanes; i++) { + ret = k3_usb3phy_init_single(lg, lg->base + lg->data->offsets[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +const struct phy_ops k3_usb3_phy_ops = { + .init = k3_usb3phy_init, + .owner = THIS_MODULE, +}; +EXPORT_SYMBOL_GPL(k3_usb3_phy_ops); + +static int k3_pcie_phy_init(struct phy *phy) +{ + struct k3_lane_group *lg = phy_get_drvdata(phy); + void __iomem *phy_base = lg->base + lg->data->offsets[0]; + u32 val; + int ret; + int i; + + val = readl(phy_base + PHY_PLL_REG1); + val = u32_replace_bits(val, 0x2, GENMASK(15, 12)); + writel(val, phy_base + PHY_PLL_REG1); + + val = readl(phy_base + PHY_PLL_REG2); + val = u32_replace_bits(val, 0, BIT(21)); + writel(val, phy_base + PHY_PLL_REG2); + + for (i = 0; i < lg->data->lanes; i++) { + void __iomem *lane_base = lg->base + lg->data->offsets[i]; + + val = readl(lane_base + PCIE_RX_REG1); + val = u32_replace_bits(val, 0, 0x3); + writel(val, lane_base + PCIE_RX_REG1); + } + + val = readl(phy_base + PHY_PLL_REG2); + val |= BIT(20); + writel(val, phy_base + PHY_PLL_REG2); + + writel(0x00006505, phy_base + PCIE_RX_REG1); + + /* pll_reg1 of lane0, disable SSC: pll[27:24] = 0 */ + val = readl(phy_base + PHY_PLL_REG1); + val = u32_replace_bits(val, 0, GENMASK(27, 24)); + writel(val, phy_base + PHY_PLL_REG1); + + for (i = 0; i < lg->data->lanes; i++) { + void __iomem *lane_base = lg->base + lg->data->offsets[i]; + + /* set cfg_tx_send_dummy_data to be 1'b1 for disable dash data */ + val = readl(lane_base + PHY_PU_SEL); + val = u32_replace_bits(val, 1, BIT(13)); + writel(val, lane_base + PHY_PU_SEL); + + /* disable en_sample_data_after_cdr_locked */ + val = readl(lane_base + PHY_RESET_CFG); + val = u32_replace_bits(val, 0, BIT(6)); + writel(val, lane_base + PHY_RESET_CFG); + + /* Dynamic Lock */ + val = readl(lane_base + PHY_MODE_CFG); + val = u32_replace_bits(val, 1, BIT(2)); + writel(val, lane_base + PHY_MODE_CFG); + + val = FIELD_PREP(GENMASK(7, 0), 0x10) | + FIELD_PREP(GENMASK(15, 8), 0x78) | + FIELD_PREP(GENMASK(23, 16), 0x98) | + FIELD_PREP(GENMASK(31, 24), 0xdf); + writel(val, lane_base + PHY_RX_REG_A); + + val = readl(lane_base + PHY_RX_REG_B); + val &= ~PHY_RX_REGB_MASK; + val |= FIELD_PREP(GENMASK(7, 0), 0xb4) | + FIELD_PREP(GENMASK(15, 8), 0x88) | + FIELD_PREP(GENMASK(23, 16), 0x28); + writel(val, lane_base + PHY_RX_REG_B); + + /* Set init done */ + val = readl(lane_base + PCIE_PU_ADDR_CLK_CFG); + val = u32_replace_bits(val, 1, CFG_SW_PHY_INIT_DONE); + writel(val, lane_base + PCIE_PU_ADDR_CLK_CFG); + } + + ret = readl_poll_timeout(phy_base + PCIE_PU_ADDR_CLK_CFG, val, + (val & PHY_CLK_PLL_READY), PCIE_POLL_DELAY, + PCIE_PLL_TIMEOUT); + if (ret) { + dev_err(&lg->phy->dev, "PHY PLL lock timeout\n"); + return ret; + } + + return 0; +} + +const struct phy_ops k3_pcie_phy_ops = { + .init = k3_pcie_phy_init, + .owner = THIS_MODULE, +}; +EXPORT_SYMBOL_GPL(k3_pcie_phy_ops); + +/* PHY rcal init requires APB_SPARE regmap access */ + +#define APB_SPARE_PU_CAL 0x178 +#define PU_CAL BIT(17) + +#define APB_SPARE_RCAL_HSIO 0x17c +#define APB_SPARE_PU_CAL_DONE BIT(8) +#define RCAL_OVRD_PTRIM GENMASK(23, 20) +#define RCAL_OVRD_NTRIM GENMASK(27, 24) +#define RCAL_OVRD_PTRIM_EN BIT(28) +#define RCAL_OVRD_NTRIM_EN BIT(29) +#define RCAL_OVRD_STABLE_VAL BIT(30) +#define RCAL_OVRD_STABLE_EN BIT(31) + +#define RCAL_OVRD_TRIM_EN (RCAL_OVRD_NTRIM_EN | RCAL_OVRD_PTRIM_EN) +#define RCAL_OVRD_TRIM_MASK (RCAL_OVRD_NTRIM | RCAL_OVRD_PTRIM) + +#define PU_CAL_TIMEOUT 2000000 + +static DEFINE_MUTEX(calibrate_lock); + +int k3_phy_calibrate(struct regmap *apb_spare) +{ + unsigned int val = 0; + int ret; + + guard(mutex)(&calibrate_lock); + + regmap_read(apb_spare, APB_SPARE_RCAL_HSIO, &val); + if (val & APB_SPARE_PU_CAL_DONE) + return 0; + + regmap_update_bits(apb_spare, APB_SPARE_PU_CAL, PU_CAL, + PU_CAL); + + ret = regmap_read_poll_timeout(apb_spare, APB_SPARE_RCAL_HSIO, + val, (val & APB_SPARE_PU_CAL_DONE), PCIE_POLL_DELAY, + PU_CAL_TIMEOUT); + + if (ret) + regmap_update_bits(apb_spare, APB_SPARE_RCAL_HSIO, + RCAL_OVRD_TRIM_EN | RCAL_OVRD_STABLE_VAL | + RCAL_OVRD_TRIM_MASK | RCAL_OVRD_STABLE_EN, + RCAL_OVRD_TRIM_EN | RCAL_OVRD_STABLE_VAL | + FIELD_PREP(RCAL_OVRD_NTRIM, 0x6) | + FIELD_PREP(RCAL_OVRD_PTRIM, 0xa) | + RCAL_OVRD_STABLE_EN); + + return 0; +} +EXPORT_SYMBOL_GPL(k3_phy_calibrate); + +MODULE_DESCRIPTION("SpacemiT K3 PHY common ops"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/spacemit/phy-k3-common.h b/drivers/phy/spacemit/phy-k3-common.h new file mode 100644 index 00000000000000..49009c3c313a29 --- /dev/null +++ b/drivers/phy/spacemit/phy-k3-common.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _PHY_K3_COMMON_H +#define _PHY_K3_COMMON_H + +#include + +struct k3_phy_lane_group_data { + u32 lanes; + u8 config; + u8 mask; + u32 offsets[] __counted_by(lanes); +}; + +struct k3_lane_group { + const struct k3_phy_lane_group_data *data; + void __iomem *base; + struct phy *phy; + bool is_pcie; +}; + +extern const struct phy_ops k3_pcie_phy_ops; +extern const struct phy_ops k3_usb3_phy_ops; + +int k3_phy_calibrate(struct regmap *apb_spare); + +#endif From 843f6dbcc271210651b1398db1afc9b2ca9369a6 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Fri, 27 Feb 2026 09:46:06 +0800 Subject: [PATCH 161/258] RUYI: riscv: dts: spacemit: k3: add USB controller and USB phy support Add all USB device node to the Spacemit K3. Signed-off-by: Inochi Amaoto Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 13 ++++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 42 ++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index ac965ec83f2c0d..acfbb5029c1525 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -183,6 +183,11 @@ }; }; +&combophy { + spacemit,apmu = <&syscon_apmu 0x11>; + status = "okay"; +}; + ð0 { pinctrl-names = "default"; pinctrl-0 = <&gmac0_rgmii_0_cfg>, <&gmac0_phy_0_cfg>; @@ -280,3 +285,11 @@ &usb2_phy { status = "okay"; }; + +&usb3d_u2phy { + status = "okay"; +}; + +&usb3d { + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 66dcabd0a81567..130828ca3b437d 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include /dts-v1/; @@ -637,6 +638,47 @@ status = "disabled"; }; + usb3d: usb@81a00000 { + compatible = "spacemit,k3-dwc3"; + reg = <0x0 0x81a00000 0x0 0x10000>; + interrupts = <149 IRQ_TYPE_LEVEL_HIGH>; + interrupt-parent = <&saplic>; + clocks = <&syscon_apmu CLK_APMU_USB3_PORTD_BUS>; + clock-names = "usbdrd30"; + resets = <&syscon_apmu RESET_APMU_USB3_D_AHB>, + <&syscon_apmu RESET_APMU_USB3_D_VCC>, + <&syscon_apmu RESET_APMU_USB3_D_PHY>; + reset-names = "ahb", "vcc", "phy"; + phys = <&usb3d_u2phy>, + <&combophy 4 PHY_TYPE_USB3>; + phy-names = "usb2-phy", "usb3-phy"; + phy_type = "utmi"; + snps,dis_enblslpm_quirk; + snps,dis_u2_susphy_quirk; + snps,dis_u3_susphy_quirk; + snps,dis-del-phy-power-chg-quirk; + snps,dis-tx-ipgap-linecheck-quirk; + snps,parkmode-disable-ss-quirk; + dr_mode = "host"; + status = "disabled"; + }; + + usb3d_u2phy: phy@81b00000 { + compatible = "spacemit,k3-usb2-phy"; + reg = <0x0 0x81b00000 0x0 0x200>; + clocks = <&syscon_apmu CLK_APMU_USB2_BUS>; + #phy-cells = <0>; + status = "disabled"; + }; + + combophy: phy@81d00000 { + compatible = "spacemit,k3-combo-phy"; + reg = <0x0 0x81d00000 0x0 0x600000>; + #phy-cells = <2>; + spacemit,apb-spare = <&pll>; + status = "disabled"; + }; + usb2_host: usb@c0a00000 { compatible = "spacemit,k3-dwc3"; reg = <0x0 0xc0a00000 0x0 0x10000>; From fefaf11837cc9373bea1f9b429e5d92644a5f008 Mon Sep 17 00:00:00 2001 From: Inochi Amaoto Date: Tue, 24 Mar 2026 11:06:24 +0800 Subject: [PATCH 162/258] RUYI: riscv: dts: spacemit: k3: Add PCIe device node Add all PCIe device node for Spacemit K3. Signed-off-by: Inochi Amaoto Signed-off-by: Han Gao --- arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 29 ++++ arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi | 33 ++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 150 +++++++++++++++++++ 3 files changed, 212 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts index acfbb5029c1525..f24ada15f18218 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -264,6 +264,35 @@ }; }; +&pcie0_rc { + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_0_cfg>; + phys = <&combophy 0 PHY_TYPE_PCIE>, + <&combophy 1 PHY_TYPE_PCIE>; + phy-names = "pcie-phy0", "pcie-phy1"; + num-lanes = <4>; + status = "okay"; +}; + +&pcie2_rc { + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_0_cfg>; + phys = <&combophy 2 PHY_TYPE_PCIE>, + <&combophy 3 PHY_TYPE_PCIE>; + phy-names = "pcie-phy0", "pcie-phy1"; + num-lanes = <2>; + status = "okay"; +}; + +&pcie4_rc { + pinctrl-names = "default"; + pinctrl-0 = <&pcie4_0_cfg>; + phys = <&combophy 5 PHY_TYPE_PCIE>; + phy-names = "pcie-phy0"; + num-lanes = <1>; + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_0_cfg>; diff --git a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi index 846d5e8cc78377..5a817610101bd9 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3-pinctrl.dtsi @@ -710,4 +710,37 @@ drive-strength = <25>; }; }; + + pcie0_0_cfg: pcie0-0-cfg { + pcie0-0-pins { + pinmux = , /* pcie0 perst */ + ; /* pcie0 clkreq */ + + bias-pull-up = <1>; + drive-strength = <33>; + power-source = <1800>; + }; + }; + + pcie2_0_cfg: pcie2-0-cfg { + pcie2-0-pins { + pinmux = , /* pcie2 perst */ + ; /* pcie2 clkreq */ + + drive-strength = <38>; + power-source = <3300>; + }; + }; + + pcie4_0_cfg: pcie4-0-cfg { + pcie4-0-pins { + pinmux = , /* pcie4 perst */ + , /* pcie4 wake */ + ; /* pcie4 clkreq */ + + bias-pull-up = <1>; + drive-strength = <33>; + power-source = <1800>; + }; + }; }; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spacemit/k3.dtsi index 130828ca3b437d..6adfbd505e9e6e 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -638,6 +638,156 @@ status = "disabled"; }; + pcie0_rc: pcie@80000000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x80000000 0x0 0x00001000>, + <0x0 0x80100000 0x0 0x00001000>, + <0x0 0x80300000 0x0 0x00003f20>, + <0x11 0x00000000 0x0 0x00010000>, + <0x0 0x82900000 0x0 0x00001000>; + reg-names = "dbi", "dbi2", "atu", "config", "link"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTA_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTA_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTA_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x00 0x00010000 0x11 0x00010000 0x0 0x00100000>, + <0x02000000 0x0 0x00110000 0x11 0x00110000 0x0 0x7fef0000>, + <0x43000000 0x18 0x00000000 0x18 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_A_DBI>, + <&syscon_apmu RESET_APMU_PCIE_A_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_A_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + bus-range = <0x00 0xff>; + max-link-speed = <3>; + linux,pci-domain = <0>; + spacemit,apmu = <&syscon_apmu 0x1f0>; + status = "disabled"; + }; + + pcie1_rc: pcie@80400000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x80400000 0x0 0x00001000>, + <0x0 0x80500000 0x0 0x00001000>, + <0x0 0x80700000 0x0 0x00003f20>, + <0x11 0x80000000 0x0 0x00010000>, + <0x0 0x82c00000 0x0 0x00001000>; + reg-names = "dbi", "dbi2", "atu", "config", "link"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTB_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTB_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTB_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x0 0x00010000 0x11 0x80010000 0x0 0x00100000>, + <0x02000000 0x0 0x80110000 0x11 0x80110000 0x0 0x7fef0000>, + <0x43000000 0x16 0x00000000 0x16 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_B_DBI>, + <&syscon_apmu RESET_APMU_PCIE_B_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_B_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + bus-range = <0x00 0xff>; + max-link-speed = <3>; + linux,pci-domain = <1>; + spacemit,apmu = <&syscon_apmu 0x1d0>; + status = "disabled"; + }; + + pcie2_rc: pcie@80800000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x80800000 0x0 0x00001000>, + <0x0 0x80900000 0x0 0x00001000>, + <0x0 0x80b00000 0x0 0x00003f20>, + <0x12 0x00000000 0x0 0x00010000>, + <0x0 0x82d00000 0x0 0x00001000>; + reg-names = "dbi", "dbi2", "atu", "config", "link"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTC_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTC_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTC_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x00 0x00000000 0x12 0x00010000 0x0 0x00100000>, + <0x02000000 0x0 0x00110000 0x12 0x00110000 0x0 0x7fef0000>, + <0x43000000 0x15 0x00000000 0x15 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_C_DBI>, + <&syscon_apmu RESET_APMU_PCIE_C_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_C_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + linux,pci-domain = <2>; + bus-range = <0x00 0xff>; + max-link-speed = <3>; + spacemit,apmu = <&syscon_apmu 0x1c8>; + status = "disabled"; + }; + + pcie3_rc: pcie@80c00000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x80c00000 0x0 0x00001000>, + <0x0 0x80d00000 0x0 0x00001000>, + <0x0 0x80f00000 0x0 0x00003f20>, + <0x12 0x80000000 0x0 0x00010000>, + <0x0 0x82a00000 0x0 0x00001000>; + reg-names = "dbi", "dbi2", "atu", "config", "link"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTD_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTD_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTD_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x0 0x00010000 0x12 0x80010000 0x0 0x100000>, + <0x02000000 0x0 0x80110000 0x12 0x80110000 0x0 0x3fef0000>, + <0x43000000 0x14 0x00000000 0x14 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_D_DBI>, + <&syscon_apmu RESET_APMU_PCIE_D_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_D_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + linux,pci-domain = <3>; + bus-range = <0x00 0xff>; + max-link-speed = <3>; + spacemit,apmu = <&syscon_apmu 0x1e0>; + status = "disabled"; + }; + + pcie4_rc: pcie@81000000 { + compatible = "spacemit,k3-pcie"; + reg = <0x0 0x81000000 0x0 0x00001000>, + <0x0 0x81100000 0x0 0x00001000>, + <0x0 0x81300000 0x0 0x00003f20>, + <0x12 0xc0000000 0x0 0x00010000>, + <0x0 0x82b00000 0x0 0x00001000>; + reg-names = "dbi", "dbi2", "atu", "config", "link"; + device_type = "pci"; + #address-cells = <3>; + #size-cells = <2>; + clocks = <&syscon_apmu CLK_APMU_PCIE_PORTE_DBI>, + <&syscon_apmu CLK_APMU_PCIE_PORTE_MSTE>, + <&syscon_apmu CLK_APMU_PCIE_PORTE_SLV>; + clock-names = "dbi", "mstr", "slv"; + msi-parent = <&simsic>; + ranges = <0x01000000 0x0 0x00000000 0x12 0xc0010000 0x0 0x100000>, + <0x02000000 0x0 0xc0110000 0x12 0xc0110000 0x0 0x3fef0000>, + <0x43000000 0x13 0x00000000 0x13 0x00000000 0x1 0x00000000>; + resets = <&syscon_apmu RESET_APMU_PCIE_E_DBI>, + <&syscon_apmu RESET_APMU_PCIE_E_MASTER>, + <&syscon_apmu RESET_APMU_PCIE_E_SLAVE>; + reset-names = "dbi", "mstr", "slv"; + linux,pci-domain = <4>; + bus-range = <0x00 0xff>; + max-link-speed = <3>; + spacemit,apmu = <&syscon_apmu 0x1e8>; + status = "disabled"; + }; + usb3d: usb@81a00000 { compatible = "spacemit,k3-dwc3"; reg = <0x0 0x81a00000 0x0 0x10000>; From 3565170edfd3397724a1c5e4f886946ac5753fd5 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Wed, 20 May 2026 23:28:15 +0800 Subject: [PATCH 163/258] RUYI: PCI: add SpacemiT vendor id and its K3 device id to pci_ids The SpacemiT K3 chip's root complex needs to be listed in the allowlist of rtw89 driver to allow 36-bit DMA. Add the vendor and device IDs to pci_ids.h header file. Signed-off-by: Icenowy Zheng Signed-off-by: Han Gao --- include/linux/pci_ids.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index e312f1945f61a1..67cf7e74eccf13 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2642,6 +2642,9 @@ #define PCI_VENDOR_ID_SUNIX 0x1fd4 #define PCI_DEVICE_ID_SUNIX_1999 0x1999 +#define PCI_VENDOR_ID_SPACEMIT 0x201f +#define PCI_DEVICE_ID_SPACEMIT_K3 0x0002 + #define PCI_VENDOR_ID_HINT 0x3388 #define PCI_DEVICE_ID_HINT_VXPROII_IDE 0x8013 From 1c78408b10e33b865bac2758939ebaae100b780c Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Wed, 20 May 2026 23:29:46 +0800 Subject: [PATCH 164/258] RUYI: wifi: rtw89: pci: add SpacemiT K3 to 36-bit DMA allowlist The SpacemiT K3 platform has no system memory in the 32-bit address space, and it's verified that the chip works well with 36-bit DMA of RTL8852BE. Add it to the 36-bit DMA allowlist of rtw89_pci. Signed-off-by: Icenowy Zheng Signed-off-by: Han Gao --- drivers/net/wireless/realtek/rtw89/pci.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index 64554eb35a72cf..2d507fe8a8db90 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -3312,6 +3312,10 @@ static bool rtw89_pci_is_dac_compatible_bridge(struct rtw89_dev *rtwdev) if (bridge->device == 0x2806) return true; break; + case PCI_VENDOR_ID_SPACEMIT: + if (bridge->device == PCI_DEVICE_ID_SPACEMIT_K3) + return true; + break; } return false; From 4cf429c8be554ba9a49903dfb2087fca2cb502af Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 19 May 2026 19:57:52 +0800 Subject: [PATCH 165/258] RUYI: drm/amdgpu: disable dynamic PCIe speed switch on SpacemiT K3 The dynamic speed switch functionality seems to be broken on SpacemiT K3, and leads to frequent GPU crashes at least with Polaris GPUs. Disable dynamic speed switch on this platform. Signed-off-by: Icenowy Zheng Signed-off-by: Han Gao --- drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 3fad14824d1ecd..05e43cfa247013 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1309,6 +1309,14 @@ bool amdgpu_device_seamless_boot_supported(struct amdgpu_device *adev) */ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device *adev) { + struct pci_dev *parent = adev->pdev; + static const struct pci_device_id broken_devids[] = { + /* SpacemiT K3 */ + { PCI_DEVICE(PCI_VENDOR_ID_SPACEMIT, + PCI_DEVICE_ID_SPACEMIT_K3) }, + {} + }; + #if IS_ENABLED(CONFIG_X86) struct cpuinfo_x86 *c = &cpu_data(0); @@ -1328,6 +1336,17 @@ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device c->x86_model == 0x08) return false; #endif + /* skip upstream/downstream switches internal to dGPU */ + while (parent->vendor == PCI_VENDOR_ID_ATI) { + parent = pci_upstream_bridge(parent); + } + + if (!parent) + return true; + + if (pci_match_id(broken_devids, parent)) + return false; + return true; } From c610c0f2bc7bce71befeb2ed40df121784a8dda7 Mon Sep 17 00:00:00 2001 From: Zhang Meng Date: Wed, 4 Feb 2026 08:54:40 +0800 Subject: [PATCH 166/258] RVCK: driver: clk: k3: keep some system based clock always on FROM: https://github.com/RVCK-Project/rvck/pull/213 community inclusion category: bugfix bugzilla: https://github.com/RVCK-Project/rvck/issues/212 -------------------------------- The hdma clk is used by some component of CCI bus, it should be keep always on, regardless of whether hdma enabled. The rcpu clk should be always on because it is running backround. Signed-off-by: Zhang Meng Signed-off-by: Han Gao --- drivers/clk/spacemit/ccu-k3.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index 03de0414496377..6acde3b76b7b98 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -866,7 +866,7 @@ static const struct clk_parent_data rcpu_clk_parents[] = { CCU_PARENT_HW(pll1_d6_409p6), }; CCU_MUX_DIV_GATE_FC_DEFINE(rcpu_clk, rcpu_clk_parents, APMU_RCPU_CLK_RES_CTRL, - 4, 3, BIT(15), 7, 3, BIT(12), 0); + 4, 3, BIT(15), 7, 3, BIT(12), CLK_IS_CRITICAL); static const struct clk_parent_data dsi4ln2_dsi_esc_parents[] = { CCU_PARENT_HW(pll1_d48_51p2_ap), @@ -1026,7 +1026,7 @@ CCU_MUX_DIV_GATE_DEFINE(isim_vclk_out3, isim_vclk_parents, APMU_SNR_ISIM_VCLK_CT /* APMU clocks end */ /* DCIU clocks start */ -CCU_GATE_DEFINE(hdma_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_CLK_EN, BIT(0), 0); +CCU_GATE_DEFINE(hdma_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_CLK_EN, BIT(0), CLK_IS_CRITICAL); CCU_GATE_DEFINE(dma350_clk, CCU_PARENT_HW(axi_clk), DCIU_DMASYS_SDMA_CLK_EN, BIT(0), 0); CCU_GATE_DEFINE(c2_tcm_pipe_clk, CCU_PARENT_HW(axi_clk), DCIU_C2_TCM_PIPE_CLK, BIT(0), 0); CCU_GATE_DEFINE(c3_tcm_pipe_clk, CCU_PARENT_HW(axi_clk), DCIU_C3_TCM_PIPE_CLK, BIT(0), 0); From 8ad80b14b7c34f0ce3c2c0238be671f99d8196f6 Mon Sep 17 00:00:00 2001 From: Rene Rebe Date: Sun, 2 Aug 2026 04:07:52 +0800 Subject: [PATCH 167/258] T2SDE: arch: x86: expand whitelist for no-speculation whitelist on 5x86 systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AFAIC the upstream cpu vuln whitelist misses many 5x86 clones, "CyrixInstead", "NexGenDriven", "RiseRiseRise", "SiS SiS SiS ", "UMC UMC UMC ", "VIA VIA VIA ". - René Rebe Link: https://github.com/rxrbln/t2sde/blob/aefea81ae35770a36de34f2ef969fac8fbb9f6fa/package/kernel/linux/5x86-no-speculation.patch Signed-off-by: Mingcong Bai --- arch/x86/kernel/cpu/common.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index a4268c47f2bc35..22f6125cf90197 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -1183,10 +1183,7 @@ static void identify_cpu_without_cpuid(struct cpuinfo_x86 *c) static const __initconst struct x86_cpu_id cpu_vuln_whitelist[] = { VULNWL(ANY, 4, X86_MODEL_ANY, NO_SPECULATION), - VULNWL(CENTAUR, 5, X86_MODEL_ANY, NO_SPECULATION), - VULNWL(INTEL, 5, X86_MODEL_ANY, NO_SPECULATION), - VULNWL(NSC, 5, X86_MODEL_ANY, NO_SPECULATION), - VULNWL(VORTEX, 5, X86_MODEL_ANY, NO_SPECULATION), + VULNWL(ANY, 5, X86_MODEL_ANY, NO_SPECULATION), VULNWL(VORTEX, 6, X86_MODEL_ANY, NO_SPECULATION), /* Intel Family 6 */ From c8330eb1f0c7ce4a6cdcb4e7b315437a3249035d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 22 Sep 2024 01:07:11 +0800 Subject: [PATCH 168/258] AOSCOS: drm: amdgpu: radeon: disable cache flush workaround for LoongArch and Loongson64 This workaround causes instability for LoongArch/Loongson (MIPS) devices based on the 7A1000/2000 chipset under heavy I/O load. FIXME: Disable this workaround until we find a better fix (possibly in the platform-specific PCI code). Signed-off-by: Mingcong Bai Link: https://lore.kernel.org/all/20240617105846.1516006-1-uwu@icenowy.me/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 8 ++++++++ drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 8 ++++++++ drivers/gpu/drm/radeon/cik.c | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 2b691452775bc4..083f9e8e1862e0 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -2117,6 +2117,13 @@ static void gfx_v7_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, bool int_sel = flags & AMDGPU_FENCE_FLAG_INT; bool exec = flags & AMDGPU_FENCE_FLAG_EXEC; +/* This workaround causes instability for LoongArch/Loongson (MIPS) + * devices based on the 7A1000/2000 chipset under heavy I/O load. + * + * FIXME: Disable this workaround until we find a better fix (possibly in + * the platform-specific PCI code). + */ +#ifndef CONFIG_MACH_LOONGSON64 /* Workaround for cache flush problems. First send a dummy EOP * event down the pipe with seq one below. */ @@ -2130,6 +2137,7 @@ static void gfx_v7_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, DATA_SEL(1) | INT_SEL(0)); amdgpu_ring_write(ring, lower_32_bits(seq - 1)); amdgpu_ring_write(ring, upper_32_bits(seq - 1)); +#endif /* Then send the real EOP event down the pipe. */ amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 36b6214f4c6673..2d9e3b6f791bd6 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -6139,6 +6139,13 @@ static void gfx_v8_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, bool int_sel = flags & AMDGPU_FENCE_FLAG_INT; bool exec = flags & AMDGPU_FENCE_FLAG_EXEC; +/* This workaround causes instability for LoongArch/Loongson (MIPS) + * devices based on the 7A1000/2000 chipset under heavy I/O load. + * + * FIXME: Disable this workaround until we find a better fix (possibly in + * the platform-specific PCI code). + */ +#ifndef CONFIG_MACH_LOONGSON64 /* Workaround for cache flush problems. First send a dummy EOP * event down the pipe with seq one below. */ @@ -6153,6 +6160,7 @@ static void gfx_v8_0_ring_emit_fence_gfx(struct amdgpu_ring *ring, u64 addr, DATA_SEL(1) | INT_SEL(0)); amdgpu_ring_write(ring, lower_32_bits(seq - 1)); amdgpu_ring_write(ring, upper_32_bits(seq - 1)); +#endif /* Then send the real EOP event down the pipe: * EVENT_WRITE_EOP - flush caches, send int */ diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 0b804e951b7b6f..7e7f6248b63a63 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3543,6 +3543,13 @@ void cik_fence_gfx_ring_emit(struct radeon_device *rdev, struct radeon_ring *ring = &rdev->ring[fence->ring]; u64 addr = rdev->fence_drv[fence->ring].gpu_addr; +/* This workaround causes instability for LoongArch/Loongson (MIPS) + * devices based on the 7A1000/2000 chipset under heavy I/O load. + * + * FIXME: Disable this workaround until we find a better fix (possibly in + * the platform-specific PCI code). + */ +#ifndef CONFIG_MACH_LOONGSON64 /* Workaround for cache flush problems. First send a dummy EOP * event down the pipe with seq one below. */ @@ -3556,6 +3563,7 @@ void cik_fence_gfx_ring_emit(struct radeon_device *rdev, DATA_SEL(1) | INT_SEL(0)); radeon_ring_write(ring, fence->seq - 1); radeon_ring_write(ring, 0); +#endif /* Then send the real EOP event down the pipe. */ radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); From dfe4c5445bd3bddef019def053c7582c8feda764 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sun, 22 Sep 2024 03:19:19 +0800 Subject: [PATCH 169/258] AOSCOS: net: stmmac: Make phytium_dwmac_remove() return void Fixes: 731b13ef92e8 ("BACKPORT: PHYTIUM: net/stmmac: Add phytium DWMAC driver support v2") Fixes: 0edb555a65d1 ("platform: Make platform_driver::remove() return void") Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 7adeddd82d33c7..fb6929552380d1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -198,7 +198,7 @@ static int phytium_dwmac_probe(struct platform_device *pdev) return stmmac_dvr_probe(&pdev->dev, plat, &stmmac_res); } -static int phytium_dwmac_remove(struct platform_device *pdev) +static void phytium_dwmac_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct stmmac_priv *priv = netdev_priv(ndev); @@ -206,8 +206,6 @@ static int phytium_dwmac_remove(struct platform_device *pdev) stmmac_pltfr_remove(pdev); clk_unregister_fixed_rate(plat->stmmac_clk); - - return 0; } #ifdef CONFIG_OF From 7bc14a3c796dd5203a9e9da2b020589ab49686b3 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sun, 22 Sep 2024 03:42:30 +0800 Subject: [PATCH 170/258] AOSCOS: net: phytium: Adapt struct kernel_ethtool_ts_info Fixes: 53f8c475850d ("BACKPORT: PHYTIUM: net: phytium: Add support for phytium GMAC") Fixes: 2111375b85ad ("net: Add struct kernel_ethtool_ts_info") Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_ethtool.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index cd9d702da113ff..cf6085d2dc04ff 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -176,7 +176,7 @@ static int phytmac_set_ringparam(struct net_device *ndev, } static int phytmac_get_ts_info(struct net_device *ndev, - struct ethtool_ts_info *info) + struct kernel_ethtool_ts_info *info) { struct phytmac *pdata = netdev_priv(ndev); From 99c16187f0fe1604442a4cca352353ae1d36f989 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sun, 22 Sep 2024 03:57:43 +0800 Subject: [PATCH 171/258] AOSCOS: net: ethernet: phytium: Make phytmac_plat_remove() return void Fixes: 53f8c475850d ("BACKPORT: PHYTIUM: net: phytium: Add support for phytium GMAC") Fixes: 0edb555a65d1 ("platform: Make platform_driver::remove() return void") Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_platform.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index d958ca1f168bb2..3724936cc826e2 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -313,14 +313,12 @@ static int phytmac_plat_probe(struct platform_device *pdev) return ret; } -static int phytmac_plat_remove(struct platform_device *pdev) +static void phytmac_plat_remove(struct platform_device *pdev) { struct phytmac *pdata = platform_get_drvdata(pdev); phytmac_drv_remove(pdata); phytmac_free_pdata(pdata); - - return 0; } static void phytmac_plat_shutdown(struct platform_device *pdev) From aa57ab3cf1c8dbae9cf291a3b89524bb5433fc7d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 15 Oct 2025 16:26:32 +0800 Subject: [PATCH 172/258] AOSCOS: net: stmmac: dwmac-phytium: convert to use phy_interface Follow upstream changes post-6.17 and replace mac_interface with phy_interface, the former was removed since commit 6b0ed6a3a89c ("net: stmmac: remove mac_interface"). Fixes: "BACKPORT: PHYTIUM: net/stmmac: Add phytium DWMAC driver support v2" Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index fb6929552380d1..0681225be8c32f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -86,9 +86,9 @@ static int phytium_dwmac_probe(struct platform_device *pdev) if (plat->phy_interface < 0) return plat->phy_interface; - plat->mac_interface = phytium_get_mac_mode(fwnode); - if (plat->mac_interface < 0) - plat->mac_interface = plat->phy_interface; + plat->phy_interface = phytium_get_mac_mode(fwnode); + if (plat->phy_interface < 0) + plat->phy_interface = plat->phy_interface; /* Configure PHY if using device-tree */ if (pdev->dev.of_node) { From 041a0f742721accf9620b59ef55d1e74deaa1f22 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 5 Nov 2025 10:58:36 +0800 Subject: [PATCH 173/258] AOSCOS: stmmac: stmmac-phytium: fix build against post-6.18 net-next Since commit "net: stmmac: replace has_xxxx with core_type" from post- 6.18 net-next, `has_gmac = [0|1]' has been replaced with `core_type = DWMAC_CORE_GMAC'. This patch series was required by the pre- upstream YT6801 (v2) patchset. Since commit "net: stmmac: remove axi_blen array", the `axi_blen' array is no longer set on the driver level. Drop code to set burst array to fix build since this change. Revise dwmac-phytium.c to adapt to the above changes. Signed-off-by: Mingcong Bai --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 0681225be8c32f..18950063f37200 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -106,7 +106,7 @@ static int phytium_dwmac_probe(struct platform_device *pdev) plat->phy_addr = -1; plat->clk_csr = -1; - plat->has_gmac = 1; + plat->core_type = DWMAC_CORE_GMAC; plat->enh_desc = 1; plat->bugged_jumbo = 1; plat->pmt = 1; @@ -184,7 +184,6 @@ static int phytium_dwmac_probe(struct platform_device *pdev) plat->axi->axi_xit_frm = false; plat->axi->axi_wr_osr_lmt = 7; plat->axi->axi_rd_osr_lmt = 7; - plat->axi->axi_blen[0] = 16; memset(&stmmac_res, 0, sizeof(stmmac_res)); stmmac_res.addr = devm_platform_ioremap_resource(pdev, 0); From 68c47ef6928bfb2e63412f8d6369eca35e844b21 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 22 Sep 2024 04:13:54 +0800 Subject: [PATCH 174/258] AOSCOS: arm64: dts: rockchip: disable usb3 on quartz64 USB 3 ports on Quartz64 is of bad quality as it shares lines with SATA. Disable usb3 support to prevent issues. Signed-off-by: Icenowy Zheng Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts index a9021c524afbf8..429ac5492ab4d8 100644 --- a/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts +++ b/arch/arm64/boot/dts/rockchip/rk3566-quartz64-a.dts @@ -795,6 +795,10 @@ /* usb3 controller is muxed with sata1 */ &usb_host1_xhci { + phys = <&usb2phy1_otg>; + phy-names = "usb2-phy"; + extcon = <&usb2phy1>; + maximum-speed = "high-speed"; status = "okay"; }; @@ -808,7 +812,7 @@ }; &usb2phy0_otg { - phy-supply = <&vcc5v0_usb20_otg>; + phy-supply = <&vcc5v0_usb20_host>; status = "okay"; }; From 929eb0bedca135ea12a76313fc406d86aa2f8fca Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 22 Sep 2024 04:18:12 +0800 Subject: [PATCH 175/258] AOSCOS: arm64: drop hisi_ddrc_pmu driver hisi_ddrc_pmu is broken and causes Kunpeng 920-based desktop systems to freeze during boot-up. Disable this driver to workaround this issue. Signed-off-by: Icenowy Zheng Signed-off-by: Kexy Biscuit [Mingcong Bai: Resolved a minor merge conflict since 6.18 in drivers/perf/hisilicon/Makefile.] Signed-off-by: Mingcong Bai --- drivers/perf/hisilicon/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index 186be3d02238b1..1009d4f616aa4e 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o \ - hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o hisi_uncore_sllc_pmu.o \ + hisi_uncore_hha_pmu.o hisi_uncore_sllc_pmu.o \ hisi_uncore_pa_pmu.o hisi_uncore_cpa_pmu.o hisi_uncore_uc_pmu.o \ hisi_uncore_noc_pmu.o hisi_uncore_mn_pmu.o From 0e45f1c74bba5680b9a8487328b43a85aa66bc4c Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 26 Nov 2025 22:21:05 +0800 Subject: [PATCH 176/258] AOSCOS: loongarch: re-introduce add_numamem_region(), init_node_memblock() This is a partial revert for commit acf5de1b23b0 ("LoongArch: Fix NUMA node parsing with numa_memblks") to keep our BPI/legacy firmware patches working. Further testing and refactoring (and perhaps discussion) pending. Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/numa.c | 67 ++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/arch/loongarch/kernel/numa.c b/arch/loongarch/kernel/numa.c index 8b89898e20df10..3efd2b4fe37bb8 100644 --- a/arch/loongarch/kernel/numa.c +++ b/arch/loongarch/kernel/numa.c @@ -158,8 +158,75 @@ static void __init node_mem_init(unsigned int node) #ifdef CONFIG_ACPI_NUMA +/* + * add_numamem_region + * + * Add a uasable memory region described by BIOS. The + * routine gets each intersection between BIOS's region + * and node's region, and adds them into node's memblock + * pool. + * + */ +static void __init add_numamem_region(u64 start, u64 end, u32 type) +{ + u32 node = pa_to_nid(start); + u64 size = end - start; + static unsigned long num_physpages; + + if (start >= end) { + pr_debug("Invalid region: %016llx-%016llx\n", start, end); + return; + } + + num_physpages += (size >> PAGE_SHIFT); + pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n", + node, type, start, size); + pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n", + start >> PAGE_SHIFT, end >> PAGE_SHIFT, num_physpages); + memblock_set_node(start, size, &memblock.memory, node); +} + static unsigned long num_physpages; +static void __init init_node_memblock(void) +{ + u32 mem_type; + u64 mem_end, mem_start, mem_size; + efi_memory_desc_t *md; + + /* Parse memory information and activate */ + for_each_efi_memory_desc(md) { + mem_type = md->type; + mem_start = md->phys_addr; + mem_size = md->num_pages << EFI_PAGE_SHIFT; + mem_end = mem_start + mem_size; + + switch (mem_type) { + case EFI_LOADER_CODE: + case EFI_LOADER_DATA: + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: + case EFI_PERSISTENT_MEMORY: + case EFI_CONVENTIONAL_MEMORY: + add_numamem_region(mem_start, mem_end, mem_type); + break; + case EFI_PAL_CODE: + case EFI_UNUSABLE_MEMORY: + case EFI_ACPI_RECLAIM_MEMORY: + add_numamem_region(mem_start, mem_end, mem_type); + fallthrough; + case EFI_RESERVED_TYPE: + case EFI_RUNTIME_SERVICES_CODE: + case EFI_RUNTIME_SERVICES_DATA: + case EFI_MEMORY_MAPPED_IO: + case EFI_MEMORY_MAPPED_IO_PORT_SPACE: + pr_info("Resvd: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n", + mem_type, mem_start, mem_size); + break; + } + } +} + static void __init info_node_memblock(void) { u32 mem_type; From 260ab333d04afd90bb60fa09f6d9ba2ebaf96dbe Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Wed, 17 Jul 2024 09:03:32 +0800 Subject: [PATCH 177/258] AOSCOS: loongarch: basic boot support for legacy firmware The addresses passed from legacy firmware in efi system tables, the initrd table, the efi system memory mapping table are virtual addresses. This patch converts all that addresses back to physical addresses, to make sure all the following booting processes can run smoothly like on modern firmwares. The conversion happens unconditionally, since physical addresses passed in by modern firmwares remain unchanged after the conversion. In the EFI Stub, the mapping entries given by GetMemoryMap() on legacy firmwares contain virtual addresses in the phys_addr fields and 0x1xxxx addresses in the virt_addr fields, causing the later call to SetVirtualAddressMap() fails. This patch fixes this by correcting the addresses in the virt_addr fields. This patch detects the existence of the legacy firmware by reading DMW1 CSR, as done in the legacy loongarch GRUB port. Only if legacy firmwares detected, this correction happens. With this patch, the linux kernel is basically able to boot. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/efi.c | 18 ++++++++++++++++++ arch/loongarch/kernel/mem.c | 1 + drivers/firmware/efi/libstub/loongarch.c | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c index 69dd83f8082fba..5d818e389b129b 100644 --- a/arch/loongarch/kernel/efi.c +++ b/arch/loongarch/kernel/efi.c @@ -99,6 +99,23 @@ static void __init init_primary_display(void) sysfb_primary_display.screen.lfb_size); } +static void __init fix_initrd_table(const efi_config_table_t *config_tables, + int count) +{ + for(int i = 0; i < count; i++) { + if (efi_guidcmp(config_tables[i].guid, + LINUX_EFI_INITRD_MEDIA_GUID) == 0) { + struct linux_efi_initrd *tbl = + early_memremap((u64)config_tables[i].table, sizeof(*tbl)); + if (tbl) { + tbl->base = TO_PHYS(tbl->base); + early_memunmap(tbl, sizeof(*tbl)); + } + break; + } + } +} + void __init efi_init(void) { int size; @@ -124,6 +141,7 @@ void __init efi_init(void) size = sizeof(efi_config_table_t); config_tables = early_memremap(efi_config_table, efi_nr_tables * size); + fix_initrd_table(config_tables, efi_systab->nr_tables); efi_config_parse_tables(config_tables, efi_systab->nr_tables, arch_tables); early_memunmap(config_tables, efi_nr_tables * size); diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c index 8ab1ffedc52c51..d870e9e189736b 100644 --- a/arch/loongarch/kernel/mem.c +++ b/arch/loongarch/kernel/mem.c @@ -19,6 +19,7 @@ void __init memblock_init(void) /* Parse memory information */ for_each_efi_memory_desc(md) { mem_type = md->type; + md->phys_addr = TO_PHYS(md->phys_addr); mem_start = md->phys_addr; mem_size = md->num_pages << EFI_PAGE_SHIFT; diff --git a/drivers/firmware/efi/libstub/loongarch.c b/drivers/firmware/efi/libstub/loongarch.c index 2b0c87dc990881..11a93c46a34497 100644 --- a/drivers/firmware/efi/libstub/loongarch.c +++ b/drivers/firmware/efi/libstub/loongarch.c @@ -44,6 +44,8 @@ struct exit_boot_struct { int runtime_entry_count; }; +static int is_oldworld = 0; + static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) { struct exit_boot_struct *p = priv; @@ -59,6 +61,15 @@ static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv) return EFI_SUCCESS; } +static void detect_oldworld(void) +{ + is_oldworld = !!(csr_read64(LOONGARCH_CSR_DMWIN1) & CSR_DMW1_PLV0); + efi_debug("is_oldworld: %d\n", is_oldworld); + if(is_oldworld) { + efi_info("Booting on OldWorld firmware\n"); + } +} + unsigned long __weak kernel_entry_address(unsigned long kernel_addr, efi_loaded_image_t *image) { @@ -74,6 +85,8 @@ efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, efi_status_t status; u32 desc_ver; + detect_oldworld(); + status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver); if (status != EFI_SUCCESS) { efi_err("Unable to retrieve UEFI memory map.\n"); @@ -87,11 +100,16 @@ efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image, if (status != EFI_SUCCESS) return status; + if (is_oldworld) { + goto skip_set_virtual_address_map; + } + /* Install the new virtual address map */ efi_rt_call(set_virtual_address_map, priv.runtime_entry_count * desc_size, desc_size, desc_ver, priv.runtime_map); +skip_set_virtual_address_map: /* Config Direct Mapping */ csr_write(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0); csr_write(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1); From d84f5b16d2cd5d250a303f86f1019f7531da0378 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Thu, 18 Jul 2024 18:55:59 +0800 Subject: [PATCH 178/258] AOSCOS: loongarch: parse BPI data and add memory mapping On legacy firmwares, the memory mapping information passed in the EFI system table is not enough to cover all the available memory regions, but only contains the memory region which occupied by the firmware, with the total size of ~1GiB. More information is stored in the BPI data structure. The complete information is combined into from the both sources. This patch addes the mechanism to parse the BPI data structure and extract the memory mapping information from it. This patch also adds the memory regions defined in the BPI to the kernel, letting the kernel to discover all the available memory regions. With this patch, machines with legacy firmware, with the BPI version BPI01001, i.e. the newer version, can boot normally. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit [Fixed minor merge conflicts since v6.18-rc6 in arch/loongarch/kernel/mem.c arch/loongarch/kernel/numa.c] Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/Makefile | 2 + arch/loongarch/kernel/efi.c | 3 + arch/loongarch/kernel/legacy_boot.c | 297 ++++++++++++++++++++++++++++ arch/loongarch/kernel/legacy_boot.h | 18 ++ arch/loongarch/kernel/mem.c | 5 + arch/loongarch/kernel/numa.c | 10 + arch/loongarch/kernel/setup.c | 3 + arch/loongarch/kernel/smp.c | 4 + 8 files changed, 342 insertions(+) create mode 100644 arch/loongarch/kernel/legacy_boot.c create mode 100644 arch/loongarch/kernel/legacy_boot.h diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile index 001924877772f0..53ad38c9c98f5b 100644 --- a/arch/loongarch/kernel/Makefile +++ b/arch/loongarch/kernel/Makefile @@ -80,3 +80,5 @@ obj-$(CONFIG_UPROBES) += uprobes.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o CPPFLAGS_vmlinux.lds := $(KBUILD_CFLAGS) + +obj-y += legacy_boot.o diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c index 5d818e389b129b..ad42d6b1f2909c 100644 --- a/arch/loongarch/kernel/efi.c +++ b/arch/loongarch/kernel/efi.c @@ -25,6 +25,8 @@ #include #include +#include "legacy_boot.h" + static unsigned long efi_nr_tables; static unsigned long efi_config_table; @@ -35,6 +37,7 @@ static efi_system_table_t *efi_systab; static efi_config_table_type_t arch_tables[] __initdata = { {LINUX_EFI_BOOT_MEMMAP_GUID, &boot_memmap, "MEMMAP" }, {DEVICE_TREE_GUID, &fdt_pointer, "FDTPTR" }, + {LOONGARCH_BPI_GUID, &loongarch_bpi_info.bpi, "BPI" }, {}, }; diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c new file mode 100644 index 00000000000000..30c01c8b22a0a5 --- /dev/null +++ b/arch/loongarch/kernel/legacy_boot.c @@ -0,0 +1,297 @@ +#include +#include + +#include + +#include "legacy_boot.h" + +#define LOONGARCH_BOOT_MEM_MAP_MAX 128 + +enum bpi_vers { + BPI_VERSION_NONE = 0, + BPI_VERSION_V1 = 1000, + BPI_VERSION_V2 = 1001, +}; + +struct loongarch_bpi_ext_hdr { + u64 signature; + u32 length; + u8 revision; + u8 checksum; + u64 next; +} __packed; + +struct loongarch_bpi_hdr { + u64 signature; + u64 systemtable; + u64 extlist; + u64 flags; +} __packed; + +enum bpi_mem_type { + ADDRESS_TYPE_SYSRAM = 1, + ADDRESS_TYPE_RESERVED = 2, + ADDRESS_TYPE_ACPI = 3, + ADDRESS_TYPE_NVS = 4, + ADDRESS_TYPE_PMEM = 5, + ADDRESS_TYPE_MAX, +}; + +struct loongarch_bpi_mem_map { + struct loongarch_bpi_ext_hdr header; /*{"M", "E", "M"}*/ + u8 map_count; + struct loongarch_bpi_mem_map_entry { + u32 mem_type; + u64 mem_start; + u64 mem_size; + } __packed map[]; +} __packed; + +struct loongarch_bpi_info __initdata loongarch_bpi_info = { + .bpi = EFI_INVALID_TABLE_ADDR, +}; + +static enum bpi_vers __initdata bpi_version = BPI_VERSION_NONE; +static u64 __initdata bpi_flags = 0; + +static int have_bpi = 0; + +static __initdata struct { + size_t map_count; + struct loongarch_bpi_memmap{ + enum bpi_mem_type type; + unsigned long mem_start; + size_t mem_size; + } map[LOONGARCH_BOOT_MEM_MAP_MAX]; +} bpi_meminfo = {0}; + +static __initdata struct { + unsigned long addr; + size_t size; +} bpi_memmap = { + .addr = EFI_INVALID_TABLE_ADDR, + .size = 0, +}; + +static const __initconst struct bpi_extlist_desc { + union { + u64 signature; + char chars[8]; + }; + unsigned long *ptr; + size_t *length; +} bpi_extlist[] = { + { .chars = "MEM", &bpi_memmap.addr, &bpi_memmap.size }, + { .signature = 0, NULL, NULL }, +}; + + +static void __init parse_bpi_ext_list(unsigned long exthdr) +{ + unsigned long cur_hdr = exthdr; + while(cur_hdr){ + struct loongarch_bpi_ext_hdr *hdr = early_memremap_ro(cur_hdr, sizeof(*hdr)); + if(!hdr) { + break; + } + u64 sig = hdr->signature; + u32 len = hdr->length; + u8 rev = hdr->revision; + unsigned long next = hdr->next; + early_memunmap(hdr, sizeof(*hdr)); + + pr_info("BPI: ext hdr: %.8s, rev %u\n", (const char *)&sig, rev); + + for(const struct bpi_extlist_desc *desc = bpi_extlist; desc->signature; desc++) { + if(sig == desc->signature) { + *(desc->ptr) = cur_hdr; + *(desc->length) = len; + } + } + cur_hdr = next; + } +} + + +static u8 __init ext_listhdr_checksum(void *buffer, size_t length) +{ + u8 sum = 0; + u8 *end = buffer + length; + u8 *buf = buffer; + + while (buf < end) + sum = (u8)(sum + *(buf++)); + + return sum; +} + +static void __init parse_bpi_mem_map(void) +{ + if(bpi_memmap.addr == EFI_INVALID_TABLE_ADDR) { + return; + } + if(bpi_memmap.size < sizeof(struct loongarch_bpi_mem_map)) { + pr_err("BPI: invalid memmap size %ld\n", bpi_memmap.size); + return; + } + struct loongarch_bpi_mem_map *memmap = early_memremap_ro(bpi_memmap.addr, bpi_memmap.size); + if(!memmap) { + return; + } + + u8 checksum = ext_listhdr_checksum(memmap, bpi_memmap.size); + if (checksum != 0) { + pr_err("BPI: memmap checksum mismatch\n"); + goto err_out; + } + + size_t map_count = memmap->map_count; + if (map_count > LOONGARCH_BOOT_MEM_MAP_MAX) { + pr_err("BPI: too many memmap entries\n"); + goto err_out; + } + if (map_count * sizeof(memmap->map[0]) + sizeof(*memmap) > bpi_memmap.size) { + pr_err("BPI: invalid memmap size %ld, not enough to hold %ld entries\n", bpi_memmap.size, map_count); + goto err_out; + } + for(int i = 0; i < map_count; i++) { + bpi_meminfo.map[i].type = memmap->map[i].mem_type; + bpi_meminfo.map[i].mem_start = memmap->map[i].mem_start; + bpi_meminfo.map[i].mem_size = memmap->map[i].mem_size; + + static const char * const __initconst mem_type_str[] = { + NULL, + [ADDRESS_TYPE_SYSRAM] = "SYSRAM", + [ADDRESS_TYPE_RESERVED] = "RESERVED", + [ADDRESS_TYPE_ACPI] = "ACPI", + [ADDRESS_TYPE_NVS] = "NVS", + [ADDRESS_TYPE_PMEM] = "PMEM", + }; + if(bpi_meminfo.map[i].type >= ADDRESS_TYPE_MAX || bpi_meminfo.map[i].type == 0) { + pr_info("BPI: memmap type unknown(%d), start 0x%lx, size 0x%lx\n", bpi_meminfo.map[i].type, bpi_meminfo.map[i].mem_start, bpi_meminfo.map[i].mem_size); + }else{ + pr_info("BPI: memmap type %s, start 0x%lx, size 0x%lx\n", mem_type_str[bpi_meminfo.map[i].type], bpi_meminfo.map[i].mem_start, bpi_meminfo.map[i].mem_size); + } + } + bpi_meminfo.map_count = map_count; + +err_out: + early_memunmap(memmap, bpi_memmap.size); +} + +static int __init bpi_parse_signature (u64 signature) +{ + union { + u64 signature; + char chars[9]; + } sig; + sig.signature = signature; + sig.chars[8] = '\0'; + + if (!(sig.chars[0] == 'B' && sig.chars[1] == 'P' && sig.chars[2] == 'I')) { + pr_err("BPI: invalid signature\n"); + return -EINVAL; + } + int version; + int rc = kstrtoint(&sig.chars[3], 10, &version); + if(rc != 0 || version == BPI_VERSION_NONE) { + pr_err("BPI: invalid version\n"); + return -EINVAL; + } + bpi_version = version; + pr_info("BPI: version %d\n", bpi_version); + return 0; +} + +void __init bpi_init(void) +{ + if (loongarch_bpi_info.bpi == EFI_INVALID_TABLE_ADDR) { + return; + } + struct loongarch_bpi_hdr *tbl; + + tbl = early_memremap_ro(loongarch_bpi_info.bpi, sizeof(*tbl)); + if (tbl) { + int rc = bpi_parse_signature(tbl->signature); + if (rc != 0) { + goto err_out; + } + have_bpi = 1; + if (bpi_version >= BPI_VERSION_V2) { + bpi_flags = tbl->flags; + pr_info("BPI: flags 0x%llx\n", bpi_flags); + } + unsigned long bpi_extlist = tbl->extlist; + parse_bpi_ext_list(bpi_extlist); + parse_bpi_mem_map(); +err_out: + early_memunmap(tbl, sizeof(*tbl)); + } +} + +void __init bpi_memblock_init(unsigned long *p_max_low_pfn) +{ + for(int i = 0; i < bpi_meminfo.map_count; i++) { + unsigned long start = bpi_meminfo.map[i].mem_start; + size_t size = bpi_meminfo.map[i].mem_size; + unsigned long end = start + size; + + switch(bpi_meminfo.map[i].type) { + case ADDRESS_TYPE_SYSRAM: + // EFI_CONVENTIONAL_MEMORY + case ADDRESS_TYPE_PMEM: + // EFI_PERSISTENT_MEMORY + memblock_add(start, size); + if (*p_max_low_pfn < (end >> PAGE_SHIFT)) + *p_max_low_pfn = end >> PAGE_SHIFT; + break; + case ADDRESS_TYPE_ACPI: + // EFI_ACPI_RECLAIM_MEMORY + memblock_add(start, size); + fallthrough; + case ADDRESS_TYPE_RESERVED: + // EFI_RUNTIME_SERVICES_DATA or + // EFI_RUNTIME_SERVICES_CODE or + // EFI_RESERVED_MEMORY_TYPE + memblock_reserve(start, size); + break; + default: + break; + } + } +} + +void __init bpi_init_node_memblock(void (*p_add_numamem_region)(u64 start, u64 end, u32 type)) +{ + for(int i = 0; i < bpi_meminfo.map_count; i++) { + u64 mem_start = bpi_meminfo.map[i].mem_start; + u64 mem_size = bpi_meminfo.map[i].mem_size; + u64 mem_end = mem_start + mem_size; + u32 mem_type; + + switch(bpi_meminfo.map[i].type) { + case ADDRESS_TYPE_SYSRAM: + mem_type = EFI_CONVENTIONAL_MEMORY; + p_add_numamem_region(mem_start, mem_end, mem_type); + break; + case ADDRESS_TYPE_PMEM: + mem_type = EFI_PERSISTENT_MEMORY; + p_add_numamem_region(mem_start, mem_end, mem_type); + break; + case ADDRESS_TYPE_ACPI: + mem_type = EFI_ACPI_RECLAIM_MEMORY; + p_add_numamem_region(mem_start, mem_end, mem_type); + fallthrough; + case ADDRESS_TYPE_RESERVED: + pr_info("Resvd: mem_type:BPI_%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n", + bpi_meminfo.map[i].type, mem_start, mem_size); + break; + default: + break; + } + } +} + +int loongarch_have_legacy_bpi (void){ + return have_bpi; +} diff --git a/arch/loongarch/kernel/legacy_boot.h b/arch/loongarch/kernel/legacy_boot.h new file mode 100644 index 00000000000000..6d3a36ebaab717 --- /dev/null +++ b/arch/loongarch/kernel/legacy_boot.h @@ -0,0 +1,18 @@ +#ifndef __LEGACY_BOOT_H_ +#define __LEGACY_BOOT_H_ + +#include + +#define LOONGARCH_BPI_GUID EFI_GUID(0x4660f721, 0x2ec5, 0x416a, 0x89, 0x9a, 0x43, 0x18, 0x02, 0x50, 0xa0, 0xc9) + +struct loongarch_bpi_info { + unsigned long bpi; +}; + +extern struct loongarch_bpi_info loongarch_bpi_info; +extern void bpi_init(void); +extern void bpi_memblock_init(unsigned long *p_max_low_pfn); +extern void bpi_init_node_memblock(void (*p_add_numamem_region)(u64 start, u64 end, u32 type)); +extern int loongarch_have_legacy_bpi(void); + +#endif /* __LEGACY_BOOT_H_ */ diff --git a/arch/loongarch/kernel/mem.c b/arch/loongarch/kernel/mem.c index d870e9e189736b..86156ffdee5dc5 100644 --- a/arch/loongarch/kernel/mem.c +++ b/arch/loongarch/kernel/mem.c @@ -10,6 +10,8 @@ #include #include +#include "legacy_boot.h" + void __init memblock_init(void) { u32 mem_type; @@ -49,6 +51,9 @@ void __init memblock_init(void) max_pfn = PFN_DOWN(memblock_end_of_DRAM()); max_low_pfn = min(PFN_DOWN(HIGHMEM_START), max_pfn); + + bpi_memblock_init(&max_low_pfn); + memblock_set_current_limit(PFN_PHYS(max_low_pfn)); /* Reserve the first 2MB */ diff --git a/arch/loongarch/kernel/numa.c b/arch/loongarch/kernel/numa.c index 3efd2b4fe37bb8..38a10744e9a528 100644 --- a/arch/loongarch/kernel/numa.c +++ b/arch/loongarch/kernel/numa.c @@ -27,6 +27,8 @@ #include #include +#include "legacy_boot.h" + int numa_off; cpumask_t cpus_on_node[MAX_NUMNODES]; cpumask_t phys_cpus_on_node[MAX_NUMNODES]; @@ -308,6 +310,14 @@ int __init init_numa_memory(void) return ret; info_node_memblock(); + + node_possible_map = numa_nodes_parsed; + if (WARN_ON(nodes_empty(node_possible_map))) + return -EINVAL; + + init_node_memblock(); + bpi_init_node_memblock(add_numamem_region); + if (!memblock_validate_numa_coverage(SZ_1M)) return -EINVAL; diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index d37e2439f4f24c..e17f7337961cbb 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -49,6 +49,8 @@ #include #include +#include "legacy_boot.h" + #define SMBIOS_BIOSSIZE_OFFSET 0x09 #define SMBIOS_BIOSEXTERN_OFFSET 0x13 #define SMBIOS_FREQLOW_OFFSET 0x16 @@ -599,6 +601,7 @@ void __init setup_arch(char **cmdline_p) init_environ(); efi_init(); fdt_setup(); + bpi_init(); memblock_init(); pagetable_init(); bootcmdline_init(cmdline_p); diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 3d16f8311dce8b..bee39fc39dced8 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -36,6 +36,8 @@ #include #include +#include "legacy_boot.h" + int __cpu_number_map[NR_CPUS]; /* Map physical to logical */ EXPORT_SYMBOL(__cpu_number_map); @@ -400,6 +402,8 @@ void loongson_boot_secondary(int cpu, struct task_struct *idle) pr_info("Booting CPU#%d...\n", cpu); entry = __pa_symbol((unsigned long)&smpboot_entry); + if (loongarch_have_legacy_bpi()) + entry = (unsigned long)&smpboot_entry; cpuboot_data.stack = (unsigned long)__KSTK_TOS(idle); cpuboot_data.thread_info = (unsigned long)task_thread_info(idle); From 6b830f7d1cf710a29b8eee4206ff54c284b14c3b Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Sat, 20 Jul 2024 06:32:15 +0800 Subject: [PATCH 179/258] AOSCOS: loongarch: add MADT ACPI table conversion On machines with legacy firmware with BPI version BPI01000, i.e. the older version, the content of the MADT ACPI table is not following the later finalized ACPI standard. A conversion of the content is added by this patch. Also the patch generates and adds a MCFG table accordingly. The above behavior is only enabled when BPI data and the BPI01000 version is detected. Signed-off-by: Miao Wang Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/include/asm/acpi.h | 5 + arch/loongarch/kernel/legacy_boot.c | 304 ++++++++++++++++++++++++++++ drivers/acpi/tables.c | 2 + include/linux/acpi.h | 10 + 4 files changed, 321 insertions(+) diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index c05168aedcaa2d..519d6401ae21e0 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -42,6 +42,11 @@ extern void acpi_add_early_pio(void); extern void acpi_remove_early_pio(void); extern int __init parse_acpi_topology(void); +#define ACPI_HAVE_ARCH_TABLE_OVERRIDE +extern void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table); +#define ACPI_HAVE_ARCH_TABLE_INIT_COMPLETE +extern void acpi_arch_table_init_complete(void); + #endif /* !CONFIG_ACPI */ #define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 30c01c8b22a0a5..841d8db85a3cb1 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -1,7 +1,13 @@ #include #include +#include +#include +#include #include +#include +#include +#include #include "legacy_boot.h" @@ -37,6 +43,9 @@ enum bpi_mem_type { ADDRESS_TYPE_MAX, }; +#define MSI_MSG_ADDRESS 0x2FF00000 +#define MSI_MSG_DEFAULT_COUNT 0xC0 + struct loongarch_bpi_mem_map { struct loongarch_bpi_ext_hdr header; /*{"M", "E", "M"}*/ u8 map_count; @@ -203,6 +212,9 @@ static int __init bpi_parse_signature (u64 signature) return 0; } +static void (*p_init_acpi_arch_os_table_override)(struct acpi_table_header *, struct acpi_table_header **) = NULL; +static void init_acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table); + void __init bpi_init(void) { if (loongarch_bpi_info.bpi == EFI_INVALID_TABLE_ADDR) { @@ -220,6 +232,8 @@ void __init bpi_init(void) if (bpi_version >= BPI_VERSION_V2) { bpi_flags = tbl->flags; pr_info("BPI: flags 0x%llx\n", bpi_flags); + } else { + p_init_acpi_arch_os_table_override = init_acpi_arch_os_table_override; } unsigned long bpi_extlist = tbl->extlist; parse_bpi_ext_list(bpi_extlist); @@ -292,6 +306,296 @@ void __init bpi_init_node_memblock(void (*p_add_numamem_region)(u64 start, u64 e } } +static u8 new_mcfg_buf [ 0 + + sizeof(struct acpi_table_mcfg) + + MAX_IO_PICS * sizeof(struct acpi_mcfg_allocation) +]; +static int __initdata new_mcfg_available = 0; + +void __init acpi_arch_table_init_complete(void){ + if (!new_mcfg_available){ + return; + } + pr_info("BPI: installing new MCFG table\n"); + acpi_install_table((struct acpi_table_header *) new_mcfg_buf); +} + +static void __init init_acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ + static int __initdata madt_table_installed = 0; + static u8 new_madt_buf[ 0 + + sizeof(struct acpi_table_madt) + + sizeof(struct acpi_madt_lio_pic) + + sizeof(struct acpi_madt_core_pic) * MAX_CORE_PIC + + MAX_IO_PICS * ( 0 + + sizeof(struct acpi_madt_eio_pic) + + sizeof(struct acpi_madt_msi_pic) + + sizeof(struct acpi_madt_bio_pic) + ) + ]; + if (madt_table_installed){ + return; + } + if (bpi_version == BPI_VERSION_NONE) { + return; + } + if (bpi_version > BPI_VERSION_V1) { + return; + } + if (strncmp(existing_table->signature, ACPI_SIG_MADT, 4) != 0) { + return; + } + pr_info("BPI: replacing MADT table\n"); + struct acpi_table_madt *madt = (struct acpi_table_madt *)existing_table; + if (madt->header.length < sizeof(struct acpi_table_madt)) { + pr_warn("BPI: MADT table length %u is too small\n", madt->header.length); + return; + } + void *madt_end = (void *)madt + madt->header.length; + struct acpi_subtable_header *entry = (struct acpi_subtable_header *)(madt + 1); + int entry_count = 0; + int local_apic_count = 0; + int io_apic_count = 0; + u64 node_map = 0; + while((void *)entry < madt_end) { + unsigned int ent_len = entry->length; + if (ent_len < sizeof(struct acpi_subtable_header)) { + pr_warn("BPI: MADT entry %d length %u is too small\n", entry_count, ent_len); + return; + } + if((void *)entry + ent_len > madt_end) { + pr_warn("BPI: MADT entry %d length overflow\n", entry_count); + return; + } + switch(entry->type) { + case ACPI_MADT_TYPE_LOCAL_APIC: + local_apic_count++; + struct acpi_madt_local_apic *lapic = (struct acpi_madt_local_apic *)entry; + node_map |= 1 << (lapic->id / CORES_PER_EIO_NODE); + break; + case ACPI_MADT_TYPE_IO_APIC: + io_apic_count++; + break; + } + acpi_table_print_madt_entry(entry); + + entry = (struct acpi_subtable_header *)((void *)entry + ent_len); + } + + if (local_apic_count == 0) { + pr_warn("BPI: MADT has no local APIC entries\n"); + return; + } + if (local_apic_count > MAX_CORE_PIC) { + pr_warn("BPI: MADT has too many local APIC entries\n"); + return; + } + if (io_apic_count == 0) { + pr_warn("BPI: MADT has no IO APIC entries\n"); + return; + } + if (io_apic_count > MAX_IO_PICS) { + pr_warn("BPI: MADT has too many IO APIC entries\n"); + return; + } + size_t new_madt_size = sizeof(struct acpi_table_madt); + size_t new_mcfg_size = sizeof(struct acpi_table_mcfg); + new_madt_size += sizeof(struct acpi_madt_lio_pic); + new_madt_size += sizeof(struct acpi_madt_core_pic) * local_apic_count; + if (cpu_has_extioi) { + pr_info("BPI: Using EIOINTC interrupt mode\n"); + new_madt_size += io_apic_count * ( 0 + + sizeof(struct acpi_madt_eio_pic) + + sizeof(struct acpi_madt_msi_pic) + + sizeof(struct acpi_madt_bio_pic) + ); + new_madt_size += sizeof(struct acpi_madt_lpc_pic); + new_mcfg_size += io_apic_count * sizeof(struct acpi_mcfg_allocation); + } else { + pr_info("BPI: Using HTVECINTC interrupt mode\n"); + new_madt_size += 0; + new_mcfg_size += sizeof(struct acpi_mcfg_allocation); + } + + if (new_madt_size > sizeof(new_madt_buf)) { + pr_warn("BPI: new madt will be too large"); + return; + } + if (new_mcfg_size > sizeof(new_mcfg_buf)) { + pr_warn("BPI: new mcfg will be too large"); + return; + } + madt_table_installed = 1; + new_mcfg_available = 1; + + struct acpi_table_madt *new_madt = (struct acpi_table_madt *)new_madt_buf; + + new_madt->header = madt->header; + new_madt->header.length = new_madt_size; + new_madt->header.checksum = 0; + new_madt->header.asl_compiler_id[0] = 'B'; + new_madt->header.asl_compiler_id[1] = 'P'; + new_madt->header.asl_compiler_id[2] = 'I'; + new_madt->address = madt->address; + new_madt->flags = madt->flags; + + struct acpi_table_mcfg *new_mcfg = (struct acpi_table_mcfg *)new_mcfg_buf; + + memcpy(new_mcfg->header.signature, ACPI_SIG_MCFG, sizeof(new_mcfg->header.signature)); + new_mcfg->header.length = new_mcfg_size; + new_mcfg->header.revision = 1; + new_mcfg->header.checksum = 0; + memcpy(new_mcfg->header.oem_id, madt->header.oem_id, sizeof(new_mcfg->header.oem_id)); + memcpy(new_mcfg->header.oem_table_id, madt->header.oem_table_id, sizeof(new_mcfg->header.oem_table_id)); + new_mcfg->header.oem_revision = madt->header.oem_revision; + memcpy(new_mcfg->header.asl_compiler_id, "BPI", sizeof(new_mcfg->header.asl_compiler_id)); + new_mcfg->header.asl_compiler_revision = 1; + memset(new_mcfg->reserved, 0, sizeof(new_mcfg->reserved)); + + struct acpi_mcfg_allocation *mcfg_entry = (struct acpi_mcfg_allocation *)(new_mcfg + 1); + + static struct acpi_madt_core_pic __initdata core_pics[MAX_CORE_PIC]; + static struct acpi_madt_eio_pic __initdata eio_pics[MAX_IO_PICS]; + static struct acpi_madt_msi_pic __initdata msi_pics[MAX_IO_PICS]; + static struct acpi_madt_bio_pic __initdata bio_pics[MAX_IO_PICS]; + + entry = (struct acpi_subtable_header *)(madt + 1); + int core_idx = 0; + int eio_idx = 0; + while((void *)entry < madt_end) { + unsigned int ent_len = entry->length; + if(entry->type == ACPI_MADT_TYPE_LOCAL_APIC) { + struct acpi_madt_local_apic *lapic = (struct acpi_madt_local_apic *)entry; + + if(core_idx >= local_apic_count){ + panic("BPI: MADT local APIC entries are more than expected\n"); + } + + core_pics[core_idx].header.type = ACPI_MADT_TYPE_CORE_PIC; + core_pics[core_idx].header.length = sizeof(core_pics[0]); + core_pics[core_idx].version = ACPI_MADT_CORE_PIC_VERSION_V1; + core_pics[core_idx].processor_id = lapic->processor_id; + core_pics[core_idx].core_id = lapic->id; + core_pics[core_idx].flags = lapic->lapic_flags; + + core_idx++; + }else if(entry->type == ACPI_MADT_TYPE_IO_APIC) { + struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry; + + if(eio_idx >= io_apic_count){ + panic("BPI: MADT IO APIC entries are more than expected\n"); + } + + eio_pics[eio_idx].header.type = ACPI_MADT_TYPE_EIO_PIC; + eio_pics[eio_idx].header.length = sizeof(eio_pics[0]); + eio_pics[eio_idx].version = ACPI_MADT_EIO_PIC_VERSION_V1; + eio_pics[eio_idx].cascade = 3 + eio_idx; + eio_pics[eio_idx].node = ioapic->id; + if(eio_idx == 0){ + eio_pics[eio_idx].node_map = node_map; + }else{ + eio_pics[0].node_map = node_map & 0x0f0f0f0f0f0f0f0full; + eio_pics[eio_idx].node_map = node_map & 0xf0f0f0f0f0f0f0f0ull; + } + + unsigned long addr = ioapic->address; + if(eio_idx > 0){ + addr |= nid_to_addrbase(ioapic->id) | HT1LO_OFFSET; + } + + msi_pics[eio_idx].header.type = ACPI_MADT_TYPE_MSI_PIC; + msi_pics[eio_idx].header.length = sizeof(msi_pics[0]); + msi_pics[eio_idx].version = ACPI_MADT_MSI_PIC_VERSION_V1; + msi_pics[eio_idx].msg_address = MSI_MSG_ADDRESS; + pr_info("BPI: will read MSI start addr for node %d from 0x%lx\n", ioapic->id, addr); + msi_pics[eio_idx].start = (((unsigned long)ls7a_readq(addr) >> 48) & 0xff) + 1; + pr_info("BPI: done read MSI start addr for node %d from 0x%lx\n", ioapic->id, addr); + msi_pics[eio_idx].count = MSI_MSG_DEFAULT_COUNT; + + bio_pics[eio_idx].header.type = ACPI_MADT_TYPE_BIO_PIC; + bio_pics[eio_idx].header.length = sizeof(bio_pics[0]); + bio_pics[eio_idx].version = ACPI_MADT_BIO_PIC_VERSION_V1; + bio_pics[eio_idx].address = addr; + bio_pics[eio_idx].size = 0x1000; + bio_pics[eio_idx].id = ioapic->id; + bio_pics[eio_idx].gsi_base = ioapic->global_irq_base; + + mcfg_entry->address = mcfg_addr_init(ioapic->id); + mcfg_entry->pci_segment = eio_idx; + mcfg_entry->start_bus_number = 0; + mcfg_entry->end_bus_number = 0xFF; // Who knows? + mcfg_entry->reserved = 0; + mcfg_entry++; + + eio_idx++; + } + entry = (struct acpi_subtable_header *)((void *)entry + ent_len); + } + if(eio_idx != io_apic_count || core_idx != local_apic_count) { + panic("BPI: MADT entries are less than expected\n"); + } + + // 1. Core APIC 0x11 + struct acpi_subtable_header *new_entry = (struct acpi_subtable_header *)(new_madt + 1); + + memcpy(new_entry, core_pics, local_apic_count * sizeof(core_pics[0])); + new_entry = (struct acpi_subtable_header *)((void *)new_entry + local_apic_count * sizeof(core_pics[0])); + + // 2. LIO PIC 0x12 + { + struct acpi_madt_lio_pic *lio_pic = (struct acpi_madt_lio_pic *)new_entry; + lio_pic->header.type = ACPI_MADT_TYPE_LIO_PIC; + lio_pic->header.length = sizeof(*lio_pic); + lio_pic->version = ACPI_MADT_LIO_PIC_VERSION_V1; + lio_pic->address = LOONGSON_REG_BASE + 0x1400; + lio_pic->size = 256; + lio_pic->cascade[0] = 2; + lio_pic->cascade[1] = 3; + lio_pic->cascade_map[0] = 0x00FFFFFF; + lio_pic->cascade_map[1] = 0xFF000000; + new_entry = (struct acpi_subtable_header *)((void *)new_entry + sizeof(*lio_pic)); + } + // 3. HT PIC 0x13 + if (!cpu_has_extioi) { + // FIX ME: Unsupported + } else { + // 4. EIO PIC 0x14 + memcpy(new_entry, eio_pics, io_apic_count * sizeof(eio_pics[0])); + new_entry = (struct acpi_subtable_header *)((void *)new_entry + io_apic_count * sizeof(eio_pics[0])); + // 5. MSI PIC 0x15 + memcpy(new_entry, msi_pics, io_apic_count * sizeof(msi_pics[0])); + new_entry = (struct acpi_subtable_header *)((void *)new_entry + io_apic_count * sizeof(msi_pics[0])); + // 6. BIO PIC 0x16 + memcpy(new_entry, bio_pics, io_apic_count * sizeof(bio_pics[0])); + new_entry = (struct acpi_subtable_header *)((void *)new_entry + io_apic_count * sizeof(bio_pics[0])); + // 7. LPC PIC 0x17 + { + struct acpi_madt_lpc_pic *lpc_pic = (struct acpi_madt_lpc_pic *)new_entry; + lpc_pic->header.type = ACPI_MADT_TYPE_LPC_PIC; + lpc_pic->header.length = sizeof(*lpc_pic); + lpc_pic->version = ACPI_MADT_LPC_PIC_VERSION_V1; + lpc_pic->address = LS7A_LPC_REG_BASE; + lpc_pic->size = SZ_4K; + lpc_pic->cascade = 19; + new_entry = (struct acpi_subtable_header *)((void *)new_entry + sizeof(*lpc_pic)); + } + } + if((void *)new_entry != (void *)new_madt + new_madt_size) { + panic("BPI: missing bytes while constructing new MADT\n"); + } + if((void *)mcfg_entry != (void *)new_mcfg + new_mcfg_size) { + panic("BPI: missing bytes while constructing new MCFG\n"); + } + new_madt->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_madt, new_madt_size); + new_mcfg->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_mcfg, new_mcfg_size); + *new_table = (struct acpi_table_header *)new_madt; +} + +void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ + if(p_init_acpi_arch_os_table_override && system_state == SYSTEM_BOOTING) { + p_init_acpi_arch_os_table_override(existing_table, new_table); + } +} + int loongarch_have_legacy_bpi (void){ return have_bpi; } diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 4286e4af10924a..f6fc6bb215c086 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -693,6 +693,7 @@ acpi_status acpi_os_table_override(struct acpi_table_header *existing_table, *new_table = (struct acpi_table_header *)&dsdt_amlcode; } #endif + acpi_arch_os_table_override(existing_table, new_table); if (*new_table != NULL) acpi_table_taint(existing_table); return AE_OK; @@ -752,6 +753,7 @@ void __init acpi_table_init_complete(void) { acpi_table_initrd_scan(); check_multiple_madt(); + acpi_arch_table_init_complete(); } int __init acpi_table_init(void) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 67effb91fa9837..613d4b49ae3834 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -810,6 +810,16 @@ int acpi_mrrm_max_mem_region(void); extern bool cmos_rtc_platform_device_present; +#ifndef ACPI_HAVE_ARCH_TABLE_OVERRIDE +static inline void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ +} +#endif +#ifndef ACPI_HAVE_ARCH_TABLE_INIT_COMPLETE +static inline void acpi_arch_table_init_complete(void) +{ +} +#endif + #else /* !CONFIG_ACPI */ #define acpi_disabled 1 From b7d9a5249956c674694d34c4394de85aed27c3ab Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Wed, 24 Jul 2024 09:06:27 +0800 Subject: [PATCH 180/258] AOSCOS: loongarch: correct missing offset of PCI root controller in DSDT table On machines with legacy firmware with BPI version BPI01000, the PCI root controller information in the DSDT table lacks AddressTranslation in its WordIO resource defination, causing the failure of the registration for the LIO address space. This patch corrects this issue when the given offset is zero for the PCI root controller. This patch also fixes the lack of the leading 16K, i.e. ISA_IOSIZE, in the defination of WordIO resource. This is because that address range is registered unconditionally on the legacy loongarch linux port. This patch also fixes the start addresses or end addresses of WordIO resource not aligned to the page size by rouding the addresses up to the nearest page starting. The above behavior is only enabled when BPI data and the BPI01000 version is detected. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/include/asm/acpi.h | 2 ++ arch/loongarch/kernel/legacy_boot.c | 23 ++++++++++++++++++++++- drivers/acpi/pci_root.c | 1 + include/linux/pci-acpi.h | 4 ++++ 4 files changed, 29 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index 519d6401ae21e0..6a7299be0878f8 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -46,6 +46,8 @@ extern int __init parse_acpi_topology(void); extern void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table); #define ACPI_HAVE_ARCH_TABLE_INIT_COMPLETE extern void acpi_arch_table_init_complete(void); +#define ACPI_HAVE_ARCH_PCI_ROOT_RES_FILTER +extern void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry); #endif /* !CONFIG_ACPI */ diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 841d8db85a3cb1..7043bfd0399d66 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -60,7 +60,7 @@ struct loongarch_bpi_info __initdata loongarch_bpi_info = { .bpi = EFI_INVALID_TABLE_ADDR, }; -static enum bpi_vers __initdata bpi_version = BPI_VERSION_NONE; +static enum bpi_vers bpi_version = BPI_VERSION_NONE; static u64 __initdata bpi_flags = 0; static int have_bpi = 0; @@ -596,6 +596,27 @@ void acpi_arch_os_table_override (struct acpi_table_header *existing_table, stru } } +void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry) +{ + if (bpi_version == BPI_VERSION_NONE) { + return; + } + if (bpi_version > BPI_VERSION_V1) { + return; + } + + if (entry->res->flags & IORESOURCE_IO) { + if (entry->offset == 0) { + if (entry->res->start == ISA_IOSIZE) { + entry->res->start = 0; + } + entry->offset = LOONGSON_LIO_BASE; + entry->res->start = LOONGSON_LIO_BASE + PFN_ALIGN(entry->res->start); + entry->res->end = LOONGSON_LIO_BASE + PFN_ALIGN(entry->res->end + 1) - 1; + } + } +} + int loongarch_have_legacy_bpi (void){ return have_bpi; } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index de9d14fa9528ad..4fca040a52350f 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -912,6 +912,7 @@ int acpi_pci_probe_root_resources(struct acpi_pci_root_info *info) "no IO and memory resources present in _CRS\n"); else { resource_list_for_each_entry_safe(entry, tmp, list) { + acpi_arch_pci_probe_root_dev_filter(entry); if (entry->res->flags & IORESOURCE_IO) acpi_pci_root_remap_iospace(device, entry); diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index c0c54baadf04ed..99fc8051585b09 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -134,6 +134,10 @@ static inline void pci_acpi_remove_edr_notifier(struct pci_dev *pdev) { } int pci_acpi_set_companion_lookup_hook(struct acpi_device *(*func)(struct pci_dev *)); void pci_acpi_clear_companion_lookup_hook(void); +#ifndef ACPI_HAVE_ARCH_PCI_ROOT_RES_FILTER +static inline void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry) { } +#endif + #else /* CONFIG_ACPI */ static inline void acpi_pci_add_bus(struct pci_bus *bus) { } static inline void acpi_pci_remove_bus(struct pci_bus *bus) { } From ad4d868acbca4f57ef7b5f1b341a70d3ed24006a Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Thu, 25 Jul 2024 16:09:19 +0800 Subject: [PATCH 181/258] AOSCOS: loongarch: fix missing dependency info in DSDT On machines with legacy firmware with BPI01000 version, the devices on the LIO bus lackes the dependency on the PCI root controller, causing the memory-mapped address of the legacy IO ports read before the setup of the mapping, resulting in kernel panic. Such DSDT can work on the legacy loongarch linux port because the leading 16K is unconditionally registered, before the enumeration of the devices in the DSDT table. This patch addes such dependency info, to order the initialization of the devices on the LIO bus after the initialization of the PCI root controller, fixing this problem. However, the addition should be done on each possible LIO device, and currently the patch only includes the legacy EC device on some laptops located at the path \_SB.PCI0.LPC.EC. Thus, this patch will be improved to include more devices. The above behavior is only enabled when BPI data and the BPI01000 version is detected. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/include/asm/acpi.h | 2 ++ arch/loongarch/kernel/legacy_boot.c | 47 +++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index 6a7299be0878f8..7c42fc15720dae 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -48,6 +48,8 @@ extern void acpi_arch_os_table_override (struct acpi_table_header *existing_tabl extern void acpi_arch_table_init_complete(void); #define ACPI_HAVE_ARCH_PCI_ROOT_RES_FILTER extern void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry); +#define ACPI_HAVE_ARCH_INIT +extern void acpi_arch_init(void); #endif /* !CONFIG_ACPI */ diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 7043bfd0399d66..3ec69ffd8b9420 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -617,6 +617,53 @@ void acpi_arch_pci_probe_root_dev_filter(struct resource_entry *entry) } } +static __initconst const struct { + struct acpi_table_header header; + unsigned char code []; +} __packed dsdt_add_aml_code = { + .header = { + .signature = ACPI_SIG_DSDT + }, + .code = { + 0x14,0x21,0x5C,0x2F, /* 00000020 " .!\/" */ + 0x05,0x5F,0x53,0x42,0x5F,0x50,0x43,0x49, /* 00000028 "._SB_PCI" */ + 0x30,0x4C,0x50,0x43,0x5F,0x45,0x43,0x5F, /* 00000030 "0LPC_EC_" */ + 0x5F,0x5F,0x44,0x45,0x50,0x08,0xA4,0x12, /* 00000038 "__DEP..." */ + 0x06,0x01,0x50,0x43,0x49,0x30 /* 00000040 "..PCI0" */ + }, +}; + +void __init acpi_arch_init (){ + if (bpi_version == BPI_VERSION_NONE) { + return; + } + if (bpi_version > BPI_VERSION_V1) { + return; + } + pr_info("BPI: Trying to patch DSDT\n"); + + acpi_status status; + acpi_handle handle; + + status = acpi_get_handle(NULL, "\\_SB.PCI0.LPC.EC", &handle); + if (ACPI_FAILURE(status)) { + if (status != AE_NOT_FOUND) { + pr_info("BPI: Unable to find EC device: %s\n", acpi_format_exception(status)); + } + return; + } + if (acpi_has_method(handle, "_DEP")) { + return; + } + + status = acpi_install_method((u8 *)&dsdt_add_aml_code); + if (ACPI_FAILURE(status)) { + pr_info("BPI: Unable to patch DSDT(0x%x)\n", status); + return; + } + acpi_handle_info(handle, "BPI: Patched DSDT\n"); +} + int loongarch_have_legacy_bpi (void){ return have_bpi; } From ed8bc25220188530ce64eb4f5a3d18434be42212 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Tue, 13 Aug 2024 16:05:28 +0800 Subject: [PATCH 182/258] AOSCOS: loongarch: fix DMA address offset Loongarch CPUs are using HT bus interface, which is only 40-bit wide, to communicate with the bridge chipset. However, the actual memory bus of the CPUs is 48-bit wide, and the available address space of DMA requests of PCI devices is also larger than the 40-bit address space. The address space of loongarch systems is not continuous, the bits [47:44] of which are used to denote the belonging node id, which is far beyond the space provided by the 40-bit wide HT bus. As a result, a translation on the both LS7A and CPU sides is needed. The translation happens on the LS7A side is controlled by the higher half of the register, HT_ROUTE. Bits [12:8] denotes dma_node_id_offset and bits[15:13] denotes dma_node_id_offset_mapped. The behavior of the translation is that the chip extracts the node id from bit dma_node_id_offset + 36 of a DMA address, places it at bit dma_node_id_offset_mapped + 32, and generates the address on the HT bus. On the CPU side, an alike translation happens, to convert the address on the HT bus back to a proper memory address. On machines with legacy firmware with BPI01000 version, dma_node_id_offset is configured with 0, resulting the address which should be used by the DMA engine of a PCI device differs from the actural physical memeory address, which requires a pair of arch-specific phys_to_dma and dma_to_phys functions or setting up the whole mapping in the _DMA method of the PCI root device in the DSDT table. The former method requires we add back the prevoiusly removed functions, and the latter method degrades the performance since when the translation happens, the mapping table is scanned linearly. This patch addresses this issue by directly setting dma_node_id_offset to 8, like what is done by modern firmwares, making the address used by the DMA engine just the same as the actual physical memory address, and eliminating the need of DMA address translation on the kernel side. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/legacy_boot.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 3ec69ffd8b9420..2888e2b9fb4566 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -46,6 +46,19 @@ enum bpi_mem_type { #define MSI_MSG_ADDRESS 0x2FF00000 #define MSI_MSG_DEFAULT_COUNT 0xC0 +/* + ``This should be configured by firmware" + -- Section 4, Page 25 of Loongson-7A1000 User Manual v2.0 + but unable to figure out the exact value. + The manual gives a typical value in Table 3.2, Page 23. +*/ +#define LS7A_CHIPCFG_REG_OFF 0x00010000 + +/* Section 4.1, Page 26 of Loongson-7A1000 User Manual v2.0 */ +#define LS7A_DMA_CFG_OFF 0x041c +#define LS7A_DMA_NODE_ID_OFFSET_SHF 8 +#define LS7A_DMA_NODE_ID_OFFSET_MASK GENMASK(12, 8) + struct loongarch_bpi_mem_map { struct loongarch_bpi_ext_hdr header; /*{"M", "E", "M"}*/ u8 map_count; @@ -588,6 +601,20 @@ static void __init init_acpi_arch_os_table_override (struct acpi_table_header *e new_madt->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_madt, new_madt_size); new_mcfg->header.checksum = 0 - ext_listhdr_checksum((u8 *)new_mcfg, new_mcfg_size); *new_table = (struct acpi_table_header *)new_madt; + + // Override LS7A dma_node_id_offset + for(int i = 0; i < io_apic_count; i++){ + u64 ls7a_base_addr = bio_pics[i].address; + void __iomem *dma_node_id_addr = (void __iomem *) TO_UNCACHE(ls7a_base_addr + LS7A_CHIPCFG_REG_OFF + LS7A_DMA_CFG_OFF); + u32 dma_cfg = readl(dma_node_id_addr); + u32 dma_node_id_offset = (dma_cfg & LS7A_DMA_NODE_ID_OFFSET_MASK) >> LS7A_DMA_NODE_ID_OFFSET_SHF; + if(dma_node_id_offset != 8){ + pr_info("BPI: LS7A %d DMA node id offset is %d, will set to 8\n", i, dma_node_id_offset); + dma_cfg &= ~LS7A_DMA_NODE_ID_OFFSET_MASK; + dma_cfg |= 8 << LS7A_DMA_NODE_ID_OFFSET_SHF; + writel(dma_cfg, dma_node_id_addr); + } + } } void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ From 956a2e2d2d592869f4d3dddcd1df3bdcaeb1d8e7 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Tue, 13 Aug 2024 22:33:01 +0800 Subject: [PATCH 183/258] AOSCOS: loongarch: fix HT_RX_INT_TRANS register On machines with legacy firmware with BPI01000 version, the HT_RX_INT_TRANS register on the node 5, i.e. the node connected with the second LS7A bridge chip, is not initialized correctly, causing the failure of the delivery of interrupts from PCIe devices connected to the second LS7A bridge chip. This patch fixes this by correctly pointing the HT_RX_INT_TRANS register to the EXT_IOI_SEND_OFF register on the corresponding node. Signed-off-by: Miao Wang Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/kernel/legacy_boot.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/arch/loongarch/kernel/legacy_boot.c b/arch/loongarch/kernel/legacy_boot.c index 2888e2b9fb4566..204b33109f37db 100644 --- a/arch/loongarch/kernel/legacy_boot.c +++ b/arch/loongarch/kernel/legacy_boot.c @@ -59,6 +59,16 @@ enum bpi_mem_type { #define LS7A_DMA_NODE_ID_OFFSET_SHF 8 #define LS7A_DMA_NODE_ID_OFFSET_MASK GENMASK(12, 8) +/* Section 14.4.1, Page 100 of Loongson-3A5000 User Manual v1.03 */ +#define HT_CTRL_CFG_OFF 0xfdfb000000ull +/* Section 14.5.34, Page 141 of Loongson-3A5000 User Manual v1.03 */ +#define HT_CTRL_HT_RX_INT_TRANS_LO_OFF 0x270 +#define HT_CTRL_HT_RX_INT_TRANS_HI_OFF 0x274 +#define HT_CTRL_HT_RX_INT_TRANS_INT_TRANS_ALLOW BIT(30) + +/* Section 11.2.3, Page 61 of Loongson-3A5000 User Manual v1.03 */ +#define EXT_IOI_SEND_OFF 0x1140 + struct loongarch_bpi_mem_map { struct loongarch_bpi_ext_hdr header; /*{"M", "E", "M"}*/ u8 map_count; @@ -615,6 +625,20 @@ static void __init init_acpi_arch_os_table_override (struct acpi_table_header *e writel(dma_cfg, dma_node_id_addr); } } + + // Override HT_RX_INT_TRANS + for(int i = 0; i < io_apic_count; i++){ + unsigned int node = eio_pics[i].node; + void __iomem *ht_rx_int_trans_hi = (void __iomem *) TO_UNCACHE(nid_to_addrbase(node) + HT1LO_OFFSET + HT_CTRL_CFG_OFF + HT_CTRL_HT_RX_INT_TRANS_HI_OFF); + void __iomem *ht_rx_int_trans_lo = (void __iomem *) TO_UNCACHE(nid_to_addrbase(node) + HT1LO_OFFSET + HT_CTRL_CFG_OFF + HT_CTRL_HT_RX_INT_TRANS_LO_OFF); + u64 ext_ioi_addr = nid_to_addrbase(node) + LOONGSON_REG_BASE + EXT_IOI_SEND_OFF; + u64 ht_rx_int_trans_cfg_orig = ((u64)readl(ht_rx_int_trans_hi) << 32) | readl(ht_rx_int_trans_lo); + u32 ht_rx_int_trans_cfg_hi = HT_CTRL_HT_RX_INT_TRANS_INT_TRANS_ALLOW | (ext_ioi_addr >> 32); + u32 ht_rx_int_trans_cfg_lo = ext_ioi_addr & GENMASK(31, 0); + pr_info("BPI: HT controller on node %u RX_INT_TRANS is 0x%llx, will set to 0x%llx\n", node, ht_rx_int_trans_cfg_orig, ((u64)ht_rx_int_trans_cfg_hi << 32) | ht_rx_int_trans_cfg_lo); + writel(ht_rx_int_trans_cfg_hi, ht_rx_int_trans_hi); + writel(ht_rx_int_trans_cfg_lo, ht_rx_int_trans_lo); + } } void acpi_arch_os_table_override (struct acpi_table_header *existing_table, struct acpi_table_header **new_table){ From 7799b2079cd74a9be26165e3ea26ff6da8c37188 Mon Sep 17 00:00:00 2001 From: Miao Wang Date: Wed, 28 Aug 2024 03:09:24 +0800 Subject: [PATCH 184/258] AOSCOS: arch/loongarch: add la_ow_syscall as in-tree module Signed-off-by: Miao Wang Link: https://github.com/AOSC-Dev/la_ow_syscall/tree/492c16dc5f6d2a8606b25077086a42cb4b0347ea Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/Kbuild | 2 + arch/loongarch/ow_syscall/Kbuild | 38 ++ arch/loongarch/ow_syscall/LICENSE | 339 ++++++++++++++++++ arch/loongarch/ow_syscall/Makefile | 37 ++ arch/loongarch/ow_syscall/README.md | 77 ++++ arch/loongarch/ow_syscall/VERSION | 1 + arch/loongarch/ow_syscall/dkms.conf.in | 11 + arch/loongarch/ow_syscall/feat_test.c | 16 + arch/loongarch/ow_syscall/fsstat.c | 87 +++++ arch/loongarch/ow_syscall/fsstat.h | 4 + .../loongarch/ow_syscall/la_ow_syscall_main.c | 274 ++++++++++++++ arch/loongarch/ow_syscall/signal.c | 239 ++++++++++++ arch/loongarch/ow_syscall/signal.h | 44 +++ arch/loongarch/ow_syscall/systable.c | 65 ++++ arch/loongarch/ow_syscall/systable.h | 8 + 15 files changed, 1242 insertions(+) create mode 100644 arch/loongarch/ow_syscall/Kbuild create mode 100644 arch/loongarch/ow_syscall/LICENSE create mode 100644 arch/loongarch/ow_syscall/Makefile create mode 100644 arch/loongarch/ow_syscall/README.md create mode 100644 arch/loongarch/ow_syscall/VERSION create mode 100644 arch/loongarch/ow_syscall/dkms.conf.in create mode 100644 arch/loongarch/ow_syscall/feat_test.c create mode 100644 arch/loongarch/ow_syscall/fsstat.c create mode 100644 arch/loongarch/ow_syscall/fsstat.h create mode 100644 arch/loongarch/ow_syscall/la_ow_syscall_main.c create mode 100644 arch/loongarch/ow_syscall/signal.c create mode 100644 arch/loongarch/ow_syscall/signal.h create mode 100644 arch/loongarch/ow_syscall/systable.c create mode 100644 arch/loongarch/ow_syscall/systable.h diff --git a/arch/loongarch/Kbuild b/arch/loongarch/Kbuild index 1c7a0dbe5e72f2..c0857841e3d197 100644 --- a/arch/loongarch/Kbuild +++ b/arch/loongarch/Kbuild @@ -7,3 +7,5 @@ obj-$(subst m,y,$(CONFIG_KVM)) += kvm/ # for cleaning subdir- += boot + +obj-y += ow_syscall/ diff --git a/arch/loongarch/ow_syscall/Kbuild b/arch/loongarch/ow_syscall/Kbuild new file mode 100644 index 00000000000000..65cd78995176f9 --- /dev/null +++ b/arch/loongarch/ow_syscall/Kbuild @@ -0,0 +1,38 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for LoongArch old-world syscall compatible layer. +# + +ifdef KBUILD_EXTMOD +CONFIG_LOONGARCH_OW_SYSCALL := m +endif + +obj-$(CONFIG_LOONGARCH_OW_SYSCALL) += la_ow_syscall.o +la_ow_syscall-y += fsstat.o la_ow_syscall_main.o signal.o systable.o +targets += feat_test.o + +$(obj)/kernel_feature.h: $(obj)/feat_test.syms + @$(kecho) ' GEN $@' + $(Q)if grep "kernel_have_new_stat" $< >/dev/null; then \ + echo "#define KERNEL_HAVE_NEW_STAT"; \ + else \ + echo "/* KERNEL_HAVE_NEW_STAT is not defined */"; \ + fi > $@ + $(Q)if grep "kernel_have_systbl" $< >/dev/null; then \ + echo "#define KERNEL_HAVE_SYSTBL"; \ + else \ + echo "/* KERNEL_HAVE_SYSTBL is not defined */"; \ + fi >> $@ + +$(obj)/feat_test.syms: $(obj)/feat_test.o + @$(kecho) ' GEN $@' + $(Q)$(OBJDUMP) -t --section=.text $< >$@ + +$(obj)/module_version.h: $(src)/VERSION + @$(kecho) ' GEN $@' + $(Q)echo "#define THIS_MODULE_VERSION \"$(shell cat $<)\"" > $@ + +$(obj)/systable.o $(obj)/fsstat.o: $(obj)/kernel_feature.h +$(obj)/la_ow_syscall_main.o: $(obj)/module_version.h + +clean-files += kernel_feature.h feat_test.syms module_version.h diff --git a/arch/loongarch/ow_syscall/LICENSE b/arch/loongarch/ow_syscall/LICENSE new file mode 100644 index 00000000000000..d159169d105089 --- /dev/null +++ b/arch/loongarch/ow_syscall/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/arch/loongarch/ow_syscall/Makefile b/arch/loongarch/ow_syscall/Makefile new file mode 100644 index 00000000000000..e975aa1cdb7af8 --- /dev/null +++ b/arch/loongarch/ow_syscall/Makefile @@ -0,0 +1,37 @@ +obj-m += la_ow_syscall.o fsstat.o la_ow_syscall_main.o signal.o + +KVER ?= $(shell uname -r) +KDIR ?= /lib/modules/$(KVER)/build +VERSION ?= $(shell cat VERSION) + +default: + $(MAKE) -C $(KDIR) M=$(CURDIR) modules + +clean: + $(MAKE) -C $(KDIR) M=$(CURDIR) clean + +install: + $(MAKE) -C $(KDIR) M=$(CURDIR) modules_install + +dkms.conf: dkms.conf.in + m4 -DVERSION=$(VERSION) $^ > $@ + +dkms-add: dkms.conf + /usr/sbin/dkms add $(CURDIR) + +dkms-build: dkms.conf + /usr/sbin/dkms build la_ow_syscall/$(VERSION) + +dkms-install: dkms.conf + /usr/sbin/dkms install la_ow_syscall/$(VERSION) + +dkms-remove: dkms.conf + /usr/sbin/dkms remove la_ow_syscall/$(VERSION) --all + +modprobe-install: + modprobe la_ow_syscall + +modprobe-remove: + modprobe -r la_ow_syscall + +dev: modprobe-remove dkms-remove dkms-add dkms-build dkms-install modprobe-install diff --git a/arch/loongarch/ow_syscall/README.md b/arch/loongarch/ow_syscall/README.md new file mode 100644 index 00000000000000..25c6757983896f --- /dev/null +++ b/arch/loongarch/ow_syscall/README.md @@ -0,0 +1,77 @@ +la\_ow\_syscall +==== + +This kernel modules provides compatibility with LoongArch's old-world ABI, +making it possible to run old-world applications (such as Kingsoft's WPS Office +and Tencent QQ) transparently on new-world (ABI 2.0) kernels and userspaces. + +Requirements +---- + +Linux Kernel >= 6.1.0 for `loongarch64` with the following option(s) set: + +- `CONFIG_KALLSYMS=y` (for reading kernel symbol addresses). +- `CONFIG_KPROBES=y` (for probing kernel symbol addresses using kernels where + base address randomisation - `CONFIG_RANDOMIZE_BASE` - is enabled). + +Installation +---- + +You may install this kernel both as an in-tree module, an out-of-tree DKMS +dynamic module, or a version-specific module. You may pick any option that best +suits your needs. + +### In-tree module + +Copy this source tree as `arch/loongarch/ow_syscall` in your kernel tree and +append the following to `arch/loongarch/Kbuild`: + +``` +obj-y += ow_syscall/ +``` + +After building the kernel with `make`, run the following command to build the +kernel module: + +``` +# $PWD is containing built objects +# /path/to/source_dir is containing Linux source code +make \ + -C /path/to/source_dir \ + ARCH=loongarch \ + O="$PWD" \ + arch/loongarch/ow_syscall/la_ow_syscall.ko \ + CONFIG_LOONGARCH_OW_SYSCALL=m +``` + +Upon completion, copy the kernel module in place +(`/lib/modules/.../arch/loongarch/ow_syscall/la_ow_syscall.ko`) and +re-generate modules.dep and map files: + +``` +depmod +``` + +### DKMS dynamic module + +Generate a `dkms.conf`: + +``` +make dkms.conf +``` + +For installation and version management, refer to dkms(8) for details. + +### Version-specific module + +Build the kernel module: + +``` +make +``` + +Load the module with super user or root privilege: + +``` +insmod la_ow_syscall.ko +``` diff --git a/arch/loongarch/ow_syscall/VERSION b/arch/loongarch/ow_syscall/VERSION new file mode 100644 index 00000000000000..17e51c385ea382 --- /dev/null +++ b/arch/loongarch/ow_syscall/VERSION @@ -0,0 +1 @@ +0.1.1 diff --git a/arch/loongarch/ow_syscall/dkms.conf.in b/arch/loongarch/ow_syscall/dkms.conf.in new file mode 100644 index 00000000000000..ad9aec128e6f8e --- /dev/null +++ b/arch/loongarch/ow_syscall/dkms.conf.in @@ -0,0 +1,11 @@ +PACKAGE_NAME="la_ow_syscall" +PACKAGE_VERSION="VERSION" + +BUILD_EXCLUSIVE_KERNEL_MIN="5.19" +BUILD_EXCLUSIVE_CONFIG="CONFIG_KALLSYMS" + +MAKE="KDIR=/lib/modules/${kernelver}/build make" +CLEAN="make clean" +BUILT_MODULE_NAME[0]="$PACKAGE_NAME" +AUTOINSTALL="yes" +DEST_MODULE_LOCATION[0]="/extra" diff --git a/arch/loongarch/ow_syscall/feat_test.c b/arch/loongarch/ow_syscall/feat_test.c new file mode 100644 index 00000000000000..5fc3b8709364d4 --- /dev/null +++ b/arch/loongarch/ow_syscall/feat_test.c @@ -0,0 +1,16 @@ +/* + This file is used to test if the coresponding macro is defined in unistd.h + If a macro is defined, the coresponding function will be defined, and + the symbol will be shown in the .o file. Later, the Kbuild script will + generate kernel_feature.h according to the existence of the symbols. +*/ + +#include +#ifdef __ARCH_WANT_NEW_STAT +void kernel_have_new_stat(void); +void kernel_have_new_stat(void){ } +#endif +#ifndef __SYSCALL +void kernel_have_systbl(void); +void kernel_have_systbl(void){ } +#endif diff --git a/arch/loongarch/ow_syscall/fsstat.c b/arch/loongarch/ow_syscall/fsstat.c new file mode 100644 index 00000000000000..1f5c336f52ec15 --- /dev/null +++ b/arch/loongarch/ow_syscall/fsstat.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include "fsstat.h" +#include "kernel_feature.h" + +#ifndef KERNEL_HAVE_NEW_STAT +#define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st)) + +struct __old_kernel_stat { + unsigned short st_dev; + unsigned short st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned int st_size; + unsigned int st_atime; + unsigned int st_mtime; + unsigned int st_ctime; +}; + +static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) +{ + struct stat tmp; + + if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev)) + return -EOVERFLOW; + if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev)) + return -EOVERFLOW; +#if BITS_PER_LONG == 32 + if (stat->size > MAX_NON_LFS) + return -EOVERFLOW; +#endif + + INIT_STRUCT_STAT_PADDING(tmp); + tmp.st_dev = new_encode_dev(stat->dev); + tmp.st_ino = stat->ino; + if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) + return -EOVERFLOW; + tmp.st_mode = stat->mode; + tmp.st_nlink = stat->nlink; + if (tmp.st_nlink != stat->nlink) + return -EOVERFLOW; + SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); + SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); + tmp.st_rdev = new_encode_dev(stat->rdev); + tmp.st_size = stat->size; + tmp.st_atime = stat->atime.tv_sec; + tmp.st_mtime = stat->mtime.tv_sec; + tmp.st_ctime = stat->ctime.tv_sec; +#ifdef STAT_HAVE_NSEC + tmp.st_atime_nsec = stat->atime.tv_nsec; + tmp.st_mtime_nsec = stat->mtime.tv_nsec; + tmp.st_ctime_nsec = stat->ctime.tv_nsec; +#endif + tmp.st_blocks = stat->blocks; + tmp.st_blksize = stat->blksize; + return copy_to_user(statbuf, &tmp, sizeof(tmp)) ? -EFAULT : 0; +} + +__SYSCALL_DEFINEx(2, _newfstat, unsigned int, fd, struct stat __user *, statbuf) +{ + struct kstat stat; + int error = p_vfs_fstat(fd, &stat); + + if (!error) + error = cp_new_stat(&stat, statbuf); + + return error; +} + +__SYSCALL_DEFINEx(4, _newfstatat, int, dfd, const char __user *, filename, + struct stat __user *, statbuf, int, flag) +{ + struct kstat stat; + int error; + + error = p_vfs_fstatat(dfd, filename, &stat, flag); + if (error) + return error; + return cp_new_stat(&stat, statbuf); +} +#endif diff --git a/arch/loongarch/ow_syscall/fsstat.h b/arch/loongarch/ow_syscall/fsstat.h new file mode 100644 index 00000000000000..fa40914afa4141 --- /dev/null +++ b/arch/loongarch/ow_syscall/fsstat.h @@ -0,0 +1,4 @@ +#include +extern int (*p_vfs_fstatat)(int dfd, const char __user *filename, + struct kstat *stat, int flags); +extern int (*p_vfs_fstat)(int fd, struct kstat *stat); diff --git a/arch/loongarch/ow_syscall/la_ow_syscall_main.c b/arch/loongarch/ow_syscall/la_ow_syscall_main.c new file mode 100644 index 00000000000000..4ae3fc6d707851 --- /dev/null +++ b/arch/loongarch/ow_syscall/la_ow_syscall_main.c @@ -0,0 +1,274 @@ +#include /* Needed by all modules */ +#include /* Needed for KERN_INFO */ +#include /* Needed for the macros */ +#include /* Needed for kprobe calls */ +#include "module_version.h" + +///< The license type -- this affects runtime behavior +MODULE_LICENSE("GPL"); + +///< The author -- visible when you use modinfo +MODULE_AUTHOR("Miao Wang"); + +///< The description -- see modinfo +MODULE_DESCRIPTION("LoongArch old-world syscall compatibility module"); + +///< The version of the module +MODULE_VERSION(THIS_MODULE_VERSION); + +#include +#include + +#include "systable.h" + +#define __EXTERN +#include "fsstat.h" +#include "signal.h" + +#ifndef __loongarch64 +#error This Linux kernel module is only supported on LoongArch +#endif + +static unsigned long kallsyms_lookup_name_addr = 0; +static unsigned int allow_mod_unreg = 0; + +#include + +// Taken from https://github.com/zizzu0/LinuxKernelModules/blob/main/FindKallsymsLookupName.c +#define KPROBE_PRE_HANDLER(fname) \ + static int __kprobes fname(struct kprobe *p, struct pt_regs *regs) + +static struct kprobe kp0, kp1; + +KPROBE_PRE_HANDLER(handler_pre0) +{ + kallsyms_lookup_name_addr = regs->csr_era; + + return 0; +} + +KPROBE_PRE_HANDLER(handler_pre1) +{ + return 0; +} + +#undef KPROBE_PRE_HANDLER + +static int do_register_kprobe(struct kprobe *kp, char *symbol_name, + void *handler) +{ + int ret = 0; + + kp->symbol_name = symbol_name; + kp->pre_handler = handler; + + ret = register_kprobe(kp); + if (ret < 0) { + pr_err("register_probe() for symbol %s failed, returned %d\n", + symbol_name, ret); + return ret; + } + + pr_debug("planted kprobe for symbol %s at %p\n", symbol_name, kp->addr); + + return ret; +} + +static int __init kprobe_kallsyms_lookup_name(void) +{ + int ret = 0; + + ret = do_register_kprobe(&kp0, "kallsyms_lookup_name", handler_pre0); + if (ret < 0) + return ret; + + ret = do_register_kprobe(&kp1, "kallsyms_lookup_name", handler_pre1); + if (ret < 0) { + unregister_kprobe(&kp0); + return ret; + } + + unregister_kprobe(&kp0); + unregister_kprobe(&kp1); + + return ret; +} + + +static int __init find_kallsyms_lookup_name(void) +{ + char fn_name[KSYM_SYMBOL_LEN]; + + int ret = 0; + + if (kallsyms_lookup_name_addr == 0 || + kallsyms_lookup_name_addr == (unsigned long)-1) { + ret = kprobe_kallsyms_lookup_name(); + if ( ret < 0 ) { + return ret; + } + if (kallsyms_lookup_name_addr == 0 || + kallsyms_lookup_name_addr == (unsigned long)-1) { + return -EINVAL; + } + } + sprint_symbol(fn_name, kallsyms_lookup_name_addr); + if (strncmp(fn_name, "kallsyms_lookup_name+0x0", + strlen("kallsyms_lookup_name+0x0")) == 0) { + pr_debug("got kallsyms_lookup_name = %lx\n", + kallsyms_lookup_name_addr); + return 0; + } else { + pr_debug("got %s at %lx, not kallsyms_lookup_name\n", fn_name, + kallsyms_lookup_name_addr); + return -EINVAL; + } +} + +int (*p_vfs_fstatat)(int dfd, const char __user *filename, struct kstat *stat, + int flags); +int (*p_vfs_fstat)(int fd, struct kstat *stat); + +void *p_sys_setxattr, *p_sys_close, *p_sys_clone; + +static struct { + const char *func_name; + void **stor; +} relocation_table[] = { +#define __rel(func) \ + { \ + (#func), ((void **)&(p_##func)) \ + } + __rel(vfs_fstatat), __rel(vfs_fstat), + __rel(sys_setxattr), __rel(sys_close), + __rel(sys_clone), __rel(sys_rt_sigprocmask), + __rel(sys_rt_sigpending), __rel(sys_rt_sigtimedwait), + __rel(sys_rt_sigaction), __rel(sys_rt_sigsuspend), + __rel(sys_pselect6), __rel(sys_ppoll), +#ifdef CONFIG_SIGNALFD + __rel(sys_signalfd4), +#endif +#ifdef CONFIG_EPOLL + __rel(sys_epoll_pwait), __rel(sys_epoll_pwait2), +#endif +}; +#define nr_rel_tab (sizeof(relocation_table) / sizeof(relocation_table[0])) + +static void **p_sys_call_table; + +#include +#include +static int __init find_sys_call_table(void) +{ + unsigned long (*p_kallsyms_lookup_name)(const char *name) = + (void *)kallsyms_lookup_name_addr; + unsigned long *sys_table; + + if (kallsyms_lookup_name_addr == 0) { + return -ENOSYS; + } + + if ((sys_table = (unsigned long *)p_kallsyms_lookup_name( + "sys_call_table"))) { + p_sys_call_table = (void **)sys_table; + pr_debug("found sys_call_table=%px\n", p_sys_call_table); + return 0; + } + + pr_info("failed to find sys_call_table using kallsyms_lookup_name()\n"); + pr_info("trying to find sys_call_table using memory scanning\n"); + + for (sys_table = (void *)&jiffies; + (void *)sys_table < (void *)&reboot_mode; sys_table++) { + if (sys_table[__NR_setxattr] == (unsigned long)p_sys_setxattr && + sys_table[__NR_close] == (unsigned long)p_sys_close && + sys_table[__NR_clone] == (unsigned long)p_sys_clone) { + p_sys_call_table = (void **)sys_table; + pr_debug("found sys_call_table=%px\n", + p_sys_call_table); + return 0; + } + } + + return -ENOSYS; +} + +static int __init oldsyscall_start(void) +{ + unsigned long (*p_kallsyms_lookup_name)(const char *name); + int rc = find_kallsyms_lookup_name(); + if (rc < 0) { + return rc; + } + p_kallsyms_lookup_name = (void *)kallsyms_lookup_name_addr; + + for (int i = 0; i < nr_rel_tab; i++) { + unsigned long p = + p_kallsyms_lookup_name(relocation_table[i].func_name); + if (p == 0) { + pr_warn("cannot find symbol %s\n", + relocation_table[i].func_name); + return -EINVAL; + } + pr_debug("found symbol %s at %px\n", + relocation_table[i].func_name, (void *)p); + *relocation_table[i].stor = (void *)p; + } + rc = find_sys_call_table(); + if (rc < 0) { + return rc; + } + for (int i = 0; syscall_to_replace[i].syscall_num != -1; i++) { + if (syscall_to_replace[i].symbol_addr) { + continue; + } + const char *symbol_name = + sys_call_table_name[syscall_to_replace[i].syscall_num]; + unsigned long symbol_addr = p_kallsyms_lookup_name(symbol_name); + if (symbol_addr) { + pr_debug("found %s at %px\n", symbol_name, + (void *)symbol_addr); + } else { + pr_warn("cannot find symbol %s\n", symbol_name); + return -EINVAL; + } + syscall_to_replace[i].symbol_addr = (void *)symbol_addr; + } + if (!allow_mod_unreg) { + bool succ = try_module_get(THIS_MODULE); + if (!succ) { + return -EINVAL; + } + } + for (int i = 0; syscall_to_replace[i].syscall_num != -1; i++) { + pr_debug("will replace syscall_%ld with %px, orig %px\n", + syscall_to_replace[i].syscall_num, + syscall_to_replace[i].symbol_addr, + p_sys_call_table[syscall_to_replace[i].syscall_num]); + syscall_to_replace[i].orig = + p_sys_call_table[syscall_to_replace[i].syscall_num]; + p_sys_call_table[syscall_to_replace[i].syscall_num] = + syscall_to_replace[i].symbol_addr; + } + pr_info("la_ow_syscall module successfully initialized\n"); + return 0; +} + +static void __exit oldsyscall_end(void) +{ + for (int i = 0; syscall_to_replace[i].syscall_num != -1; i++) { + pr_debug("will restore syscall_%ld to %px\n", + syscall_to_replace[i].syscall_num, + syscall_to_replace[i].orig); + p_sys_call_table[syscall_to_replace[i].syscall_num] = + syscall_to_replace[i].orig; + } +} + +module_init(oldsyscall_start); +module_exit(oldsyscall_end); +module_param(allow_mod_unreg, uint, 0000); +MODULE_PARM_DESC(allow_mod_unreg, + "Allow this module to be unload (Danger! Debug use only)"); +module_param(kallsyms_lookup_name_addr, ulong, 0000); +MODULE_PARM_DESC(kallsyms_lookup_name_addr, "Address for kallsyms_lookup_name, provide this when unable to find using kprobe"); diff --git a/arch/loongarch/ow_syscall/signal.c b/arch/loongarch/ow_syscall/signal.c new file mode 100644 index 00000000000000..90eb34727680dc --- /dev/null +++ b/arch/loongarch/ow_syscall/signal.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include "signal.h" + +#define _LA_OW_NSIG 128 +#define _LA_OW_NSIG_WORDS (_LA_OW_NSIG / _NSIG_BPW) + +typedef struct { + unsigned long sig[_LA_OW_NSIG_WORDS]; +} _la_ow_sigset_t; + +static inline int clear_user_sigset_extension(sigset_t __user *to) +{ + char __user *expansion = (char __user *)to + sizeof(sigset_t); + int rc = clear_user(expansion, + sizeof(_la_ow_sigset_t) - sizeof(sigset_t)); + if (rc < 0) { + return -EFAULT; + } + return 0; +} + +__SYSCALL_DEFINEx(4, _rt_sigprocmask, int, how, sigset_t __user *, nset, + sigset_t __user *, oset, size_t, sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigprocmask(how, nset, oset, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = + p_sys_rt_sigprocmask(how, nset, oset, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + if (oset) { + int rc2 = clear_user_sigset_extension(oset); + if (rc2 < 0) { + return rc2; + } + } + return 0; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(2, _rt_sigpending, sigset_t __user *, uset, size_t, + sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigpending(uset, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_rt_sigpending(uset, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + int rc2 = clear_user_sigset_extension(uset); + if (rc2 < 0) { + return rc2; + } + return 0; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(4, _rt_sigtimedwait, const sigset_t __user *, uthese, + siginfo_t __user *, uinfo, + const struct __kernel_timespec __user *, uts, size_t, + sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigtimedwait(uthese, uinfo, uts, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_rt_sigtimedwait(uthese, uinfo, uts, + sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(4, _rt_sigaction, int, sig, const struct sigaction __user *, + act, struct sigaction __user *, oact, size_t, sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigaction(sig, act, oact, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_rt_sigaction(sig, act, oact, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + if (oact) { + int rc2 = clear_user_sigset_extension(&oact->sa_mask); + if (rc2 < 0) { + return rc2; + } + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(2, _rt_sigsuspend, sigset_t __user *, unewset, size_t, + sigsetsize) +{ + if (sigsetsize == sizeof(sigset_t)) { + return p_sys_rt_sigsuspend(unewset, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_rt_sigsuspend(unewset, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(6, _pselect6, int, n, fd_set __user *, inp, fd_set __user *, + outp, fd_set __user *, exp, struct __kernel_timespec __user *, + tsp, void __user *, sig) +{ + struct sigset_argpack { + sigset_t __user *p; + size_t size; + } x = { NULL, 0 }; + struct sigset_argpack __user *siginfo = + (struct sigset_argpack __user *)sig; + + if (siginfo) { + int rc = get_user(x.size, &siginfo->size); + if (rc < 0) { + return -EFAULT; + } + } + if (siginfo == NULL || x.size == sizeof(sigset_t)) { + return p_sys_pselect6(n, inp, outp, exp, tsp, sig); + } else if (x.size == sizeof(_la_ow_sigset_t)) { + int rc = put_user(sizeof(sigset_t), &siginfo->size); + if (rc < 0) { + return -EFAULT; + } + rc = p_sys_pselect6(n, inp, outp, exp, tsp, sig); + int rc2 = put_user(sizeof(_la_ow_sigset_t), &siginfo->size); + if (rc2 < 0) { + return -EFAULT; + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(5, _ppoll, struct pollfd __user *, ufds, unsigned int, nfds, + struct __kernel_timespec __user *, tsp, const sigset_t __user *, sigmask, + size_t, sigsetsize) +{ + if (sigmask == NULL || sigsetsize == sizeof(sigset_t)) { + return p_sys_ppoll(ufds, nfds, tsp, sigmask, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_ppoll(ufds, nfds, tsp, sigmask, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +#ifdef CONFIG_EPOLL + +__SYSCALL_DEFINEx(6, _epoll_pwait, int, epfd, struct epoll_event __user *, + events, int, maxevents, int, timeout, const sigset_t __user *, + sigmask, size_t, sigsetsize) +{ + if (sigmask == NULL || sigsetsize == sizeof(sigset_t)) { + return p_sys_epoll_pwait(epfd, events, maxevents, timeout, + sigmask, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_epoll_pwait(epfd, events, maxevents, timeout, + sigmask, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +__SYSCALL_DEFINEx(6, _epoll_pwait2, int, epfd, struct epoll_event __user *, + events, int, maxevents, + const struct __kernel_timespec __user *, timeout, + const sigset_t __user *, sigmask, size_t, sigsetsize) +{ + if (sigmask == NULL || sigsetsize == sizeof(sigset_t)) { + return p_sys_epoll_pwait2(epfd, events, maxevents, timeout, + sigmask, sigsetsize); + } else if (sigsetsize == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_epoll_pwait2(epfd, events, maxevents, timeout, + sigmask, sizeof(sigset_t)); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +#endif + +#ifdef CONFIG_SIGNALFD + +__SYSCALL_DEFINEx(4, _signalfd4, int, ufd, sigset_t __user *, user_mask, size_t, + sizemask, int, flags) +{ + if (sizemask == sizeof(sigset_t)) { + return p_sys_signalfd4(ufd, user_mask, sizemask, flags); + } else if (sizemask == sizeof(_la_ow_sigset_t)) { + int rc = p_sys_signalfd4(ufd, user_mask, sizeof(sigset_t), + flags); + if (rc < 0) { + return rc; + } + return rc; + } else { + return -EINVAL; + } +} + +#endif diff --git a/arch/loongarch/ow_syscall/signal.h b/arch/loongarch/ow_syscall/signal.h new file mode 100644 index 00000000000000..adcd53bedc662d --- /dev/null +++ b/arch/loongarch/ow_syscall/signal.h @@ -0,0 +1,44 @@ +#include + +#ifndef __EXTERN +#define __EXTERN extern +#endif +#define P__SYSCALL_DEFINEx(x, name, ...) \ + __EXTERN int (*p_sys##name)(__MAP(x, __SC_DECL, __VA_ARGS__)) + +P__SYSCALL_DEFINEx(4, _rt_sigprocmask, int, how, sigset_t __user *, nset, + sigset_t __user *, oset, size_t, sigsetsize); + +P__SYSCALL_DEFINEx(2, _rt_sigpending, sigset_t __user *, uset, size_t, + sigsetsize); + +P__SYSCALL_DEFINEx(4, _rt_sigtimedwait, const sigset_t __user *, uthese, + siginfo_t __user *, uinfo, + const struct __kernel_timespec __user *, uts, size_t, + sigsetsize); + +P__SYSCALL_DEFINEx(4, _rt_sigaction, int, sig, const struct sigaction __user *, + act, struct sigaction __user *, oact, size_t, sigsetsize); + +P__SYSCALL_DEFINEx(2, _rt_sigsuspend, sigset_t __user *, unewset, size_t, + sigsetsize); +P__SYSCALL_DEFINEx(6, _pselect6, int, n, fd_set __user *, inp, fd_set __user *, + outp, fd_set __user *, exp, + struct __kernel_timespec __user *, tsp, void __user *, sig); +P__SYSCALL_DEFINEx(5, _ppoll, struct pollfd __user *, ufds, unsigned int, nfds, + struct __kernel_timespec __user *, tsp, const sigset_t __user *, sigmask, + size_t, sigsetsize); +#ifdef CONFIG_SIGNALFD +P__SYSCALL_DEFINEx(4, _signalfd4, int, ufd, sigset_t __user *, user_mask, + size_t, sizemask, int, flags); +#endif +#ifdef CONFIG_EPOLL +P__SYSCALL_DEFINEx(6, _epoll_pwait, int, epfd, struct epoll_event __user *, + events, int, maxevents, int, timeout, + const sigset_t __user *, sigmask, size_t, sigsetsize); +P__SYSCALL_DEFINEx(6, _epoll_pwait2, int, epfd, struct epoll_event __user *, + events, int, maxevents, + const struct __kernel_timespec __user *, timeout, + const sigset_t __user *, sigmask, size_t, sigsetsize); +#undef P__SYSCALL_DEFINEx +#endif diff --git a/arch/loongarch/ow_syscall/systable.c b/arch/loongarch/ow_syscall/systable.c new file mode 100644 index 00000000000000..8b311954d62ef1 --- /dev/null +++ b/arch/loongarch/ow_syscall/systable.c @@ -0,0 +1,65 @@ +#include "systable.h" +#include "kernel_feature.h" + +/* + In 6.11 or newer, the asm-generic/unistd.h is not used by the loongarch + asm/unistd.h. Instead, the definition of the syscall table and the syscall + numbers is generated after commit 26a3b85bac08 ("loongarch: convert to + generic syscall table"). As a result, we can no more get the definition of + those syscalls that are not available on Loongarch. + + To solve this problem, we still use asm-generic/unistd.h to retrive the + definitions. + + To avoid any possible conflicts, only syscall related data are defined here. + No functions are implemented here, so the above hacks will not affect other + parts of our module and will not introduce unexpected changes related to the + syscall numbers, ensuring the consistence of our module with the kernel. +*/ + +#include + +/* + And we also need those hacks to prevent including asm/unistd.h, which will + cause redefinition of the syscall numbers. +*/ + +#define _LINUX_UNISTD_H_ /* prevent loading uapi/linux/unistd.h */ +#define __ASM_VDSO_H /* prevent loading asm/vdso.h */ +#define _ASM_SECCOMP_H /* prevent loading asm/seccomp.h */ +#include + +#define __ARCH_WANT_SET_GET_RLIMIT +#define __ARCH_WANT_NEW_STAT + +#undef __SYSCALL +#define __SYSCALL(nr, call) [nr] = (#call), + +const char *sys_call_table_name[__NR_syscalls] = { + [0 ... __NR_syscalls - 1] = "sys_ni_syscall", +#include +}; + +struct syscall_replace_table syscall_to_replace[] = { +#ifndef KERNEL_HAVE_NEW_STAT + { __NR_fstat, sys_newfstat }, + { __NR_newfstatat, sys_newfstatat }, +#endif + { __NR_getrlimit, NULL }, + { __NR_setrlimit, NULL }, + { __NR_rt_sigprocmask, sys_rt_sigprocmask }, + { __NR_rt_sigpending, sys_rt_sigpending }, + { __NR_rt_sigtimedwait, sys_rt_sigtimedwait }, + { __NR_rt_sigaction, sys_rt_sigaction }, + { __NR_rt_sigsuspend, sys_rt_sigsuspend }, + { __NR_pselect6, sys_pselect6 }, + { __NR_ppoll, sys_ppoll }, +#ifdef CONFIG_SIGNALFD + { __NR_signalfd4, sys_signalfd4 }, +#endif +#ifdef CONFIG_EPOLL + { __NR_epoll_pwait, sys_epoll_pwait }, + { __NR_epoll_pwait2, sys_epoll_pwait2 }, +#endif + { -1, NULL }, +}; diff --git a/arch/loongarch/ow_syscall/systable.h b/arch/loongarch/ow_syscall/systable.h new file mode 100644 index 00000000000000..f2b6602cc7ef4f --- /dev/null +++ b/arch/loongarch/ow_syscall/systable.h @@ -0,0 +1,8 @@ +struct syscall_replace_table { + long syscall_num; + void *symbol_addr; + void *orig; +}; + +extern const char *sys_call_table_name[]; +extern struct syscall_replace_table syscall_to_replace[]; From 68cd277c949e851a45b3153dd649d6afd0790e9a Mon Sep 17 00:00:00 2001 From: Tianhao Chai Date: Thu, 18 Jan 2024 18:07:58 -0500 Subject: [PATCH 185/258] AOSCOS: la_ow_syscall: add kconfig for module Signed-off-by: Tianhao Chai [Xi Ruoyao: Select KALLSYMS and KPROBE] Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/loongarch/Kconfig | 2 ++ arch/loongarch/ow_syscall/Kconfig | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 arch/loongarch/ow_syscall/Kconfig diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 606597da46b8df..5a7d2e12beea48 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -820,3 +820,5 @@ source "drivers/cpufreq/Kconfig" endmenu source "arch/loongarch/kvm/Kconfig" + +source "arch/loongarch/ow_syscall/Kconfig" diff --git a/arch/loongarch/ow_syscall/Kconfig b/arch/loongarch/ow_syscall/Kconfig new file mode 100644 index 00000000000000..8ef159cf10557c --- /dev/null +++ b/arch/loongarch/ow_syscall/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Old-world system call compat +# + +config LOONGARCH_OW_SYSCALL + tristate "Old-world system call compatibility module" + depends on m + select KALLSYMS + select KPROBES + help + Say M to compile support for old-world system calls + as a module. This is needed to run liblol and programs + compiled for old world. + + If unsure, say N. + From d583ffbf7473caf63f369821653e129134db11a6 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 15 Oct 2024 20:32:21 +0800 Subject: [PATCH 186/258] AOSCOS: Revert "rcu: Fix rcu_barrier() VS post CPUHP_TEARDOWN_CPU invocation" When this change was introduced between v6.10.4 and v6.10.5, the Broadcom Tigon3 Ethernet interface (tg3) found on Apple MacBook Pro (15'', Mid 2010) would throw many rcu stall errors during boot up, causing peripherals such as the wireless card to misbehave. [ 24.153855] rcu: INFO: rcu_preempt detected expedited stalls on CPUs/tasks: { 2-.... } 21 jiffies s: 973 root: 0x4/. [ 24.166938] rcu: blocking rcu_node structures (internal RCU debug): [ 24.177800] Sending NMI from CPU 3 to CPUs 2: [ 24.183113] NMI backtrace for cpu 2 [ 24.183119] CPU: 2 PID: 1049 Comm: NetworkManager Not tainted 6.10.5-aosc-main #1 [ 24.183123] Hardware name: Apple Inc. MacBookPro6,2/Mac-F22586C8, BIOS MBP61.88Z.005D.B00.1804100943 04/10/18 [ 24.183125] RIP: 0010:__this_module+0x2d3d1/0x4f310 [tg3] [ 24.183135] Code: c3 cc cc cc cc 0f 1f 40 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 0f 1f 44 00 00 89 f6 48 03 77 30 8b 06 <31> f6 31 ff c3 cc cc cc cc 66 0f 1f 44 00 00 90 90 90 90 90 90 90 [ 24.183138] RSP: 0018:ffffbf1a011d75e8 EFLAGS: 00000082 [ 24.183141] RAX: 0000000000000000 RBX: ffffa04ec78f8a00 RCX: 0000000000000000 [ 24.183143] RDX: 0000000000000000 RSI: ffffbf1a00fb007c RDI: ffffa04ec78f8a00 [ 24.183145] RBP: 0000000000000b50 R08: 0000000000000000 R09: 0000000000000000 [ 24.183147] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000216 [ 24.183148] R13: ffffbf1a011d7624 R14: ffffa04ec78f8a08 R15: ffffa04ec78f8b40 [ 24.183151] FS: 00007f4c524b2140(0000) GS:ffffa05007d00000(0000) knlGS:0000000000000000 [ 24.183153] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 24.183155] CR2: 00007f7025eae3e8 CR3: 00000001040f8000 CR4: 00000000000006f0 [ 24.183157] Call Trace: [ 24.183162] [ 24.183167] ? nmi_cpu_backtrace+0xbf/0x140 [ 24.183175] ? nmi_cpu_backtrace_handler+0x11/0x20 [ 24.183181] ? nmi_handle+0x61/0x160 [ 24.183186] ? default_do_nmi+0x42/0x110 [ 24.183191] ? exc_nmi+0x1bd/0x290 [ 24.183194] ? end_repeat_nmi+0xf/0x53 [ 24.183203] ? __this_module+0x2d3d1/0x4f310 [tg3] [ 24.183207] ? __this_module+0x2d3d1/0x4f310 [tg3] [ 24.183210] ? __this_module+0x2d3d1/0x4f310 [tg3] [ 24.183213] [ 24.183214] [ 24.183215] __this_module+0x31828/0x4f310 [tg3] [ 24.183218] ? __this_module+0x2d390/0x4f310 [tg3] [ 24.183221] __this_module+0x398e6/0x4f310 [tg3] [ 24.183225] __this_module+0x3baf8/0x4f310 [tg3] [ 24.183229] __this_module+0x4733f/0x4f310 [tg3] [ 24.183233] ? _raw_spin_unlock_irqrestore+0x25/0x70 [ 24.183237] ? __this_module+0x398e6/0x4f310 [tg3] [ 24.183241] __this_module+0x4b943/0x4f310 [tg3] [ 24.183244] ? delay_tsc+0x89/0xf0 [ 24.183249] ? preempt_count_sub+0x51/0x60 [ 24.183254] __this_module+0x4be4b/0x4f310 [tg3] [ 24.183258] __dev_open+0x103/0x1c0 [ 24.183265] __dev_change_flags+0x1bd/0x230 [ 24.183269] ? rtnl_getlink+0x362/0x400 [ 24.183276] dev_change_flags+0x26/0x70 [ 24.183280] do_setlink+0xe16/0x11f0 [ 24.183286] ? __nla_validate_parse+0x61/0xd40 [ 24.183295] __rtnl_newlink+0x63d/0x9f0 [ 24.183301] ? kmem_cache_alloc_node_noprof+0x12b/0x360 [ 24.183308] ? kmalloc_trace_noprof+0x11e/0x350 [ 24.183312] ? rtnl_newlink+0x2e/0x70 [ 24.183316] rtnl_newlink+0x47/0x70 [ 24.183320] rtnetlink_rcv_msg+0x152/0x400 [ 24.183324] ? __netlink_sendskb+0x68/0x90 [ 24.183329] ? netlink_unicast+0x237/0x290 [ 24.183333] ? __pfx_rtnetlink_rcv_msg+0x10/0x10 [ 24.183336] netlink_rcv_skb+0x5b/0x110 [ 24.183343] netlink_unicast+0x1a4/0x290 [ 24.183347] netlink_sendmsg+0x222/0x4a0 [ 24.183350] ? proc_get_long.constprop.0+0x116/0x210 [ 24.183358] ____sys_sendmsg+0x379/0x3b0 [ 24.183363] ? copy_msghdr_from_user+0x6d/0xb0 [ 24.183368] ___sys_sendmsg+0x86/0xe0 [ 24.183372] ? addrconf_sysctl_forward+0xf3/0x270 [ 24.183378] ? _copy_from_iter+0x8b/0x570 [ 24.183384] ? __pfx_addrconf_sysctl_forward+0x10/0x10 [ 24.183388] ? _raw_spin_unlock+0x19/0x50 [ 24.183392] ? proc_sys_call_handler+0xf3/0x2f0 [ 24.183397] ? trace_hardirqs_on+0x29/0x90 [ 24.183401] ? __fdget+0xc2/0xf0 [ 24.183405] __sys_sendmsg+0x5b/0xc0 [ 24.183410] ? syscall_trace_enter+0x110/0x1b0 [ 24.183416] do_syscall_64+0x64/0x150 [ 24.183423] entry_SYSCALL_64_after_hwframe+0x76/0x7e I have bisected the error to this commit. Reverting it caused no new or perceivable issues on both the MacBook and a Zen4-based laptop. Revert this commit as a workaround. This reverts commit aa162aa4aa383a0a714b1c36e8fcc77612ddd1a2. Upstream report: https://bugzilla.kernel.org/show_bug.cgi?id=219390 Signed-off-by: Mingcong Bai Bug: https://lore.kernel.org/all/b8da4aec-4cca-4eb0-ba87-5f8641aa2ca9@leemhuis.info/ Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- kernel/rcu/tree.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 55df6d37145e87..06496a92efe4f9 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -4452,15 +4452,11 @@ void rcutree_migrate_callbacks(int cpu) struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); bool needwake; - if (rcu_rdp_is_offloaded(rdp)) - return; - - raw_spin_lock_irqsave(&rcu_state.barrier_lock, flags); - if (rcu_segcblist_empty(&rdp->cblist)) { - raw_spin_unlock_irqrestore(&rcu_state.barrier_lock, flags); + if (rcu_rdp_is_offloaded(rdp) || + rcu_segcblist_empty(&rdp->cblist)) return; /* No callbacks to migrate. */ - } + raw_spin_lock_irqsave(&rcu_state.barrier_lock, flags); WARN_ON_ONCE(rcu_rdp_cpu_online(rdp)); rcu_barrier_entrain(rdp); my_rdp = this_cpu_ptr(&rcu_data); From 2687cb792035549234e4c0f624df031721bb13f2 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Fri, 18 Oct 2024 20:51:39 +0800 Subject: [PATCH 187/258] AOSCOS: MIPS: Temporarily disable Loongson3 LL/SC errata check To workaround the following errors: LLSCCHK vmlinux ffffffff848a10c0: Branch target not a sync (lots of these errors) loongson3-llsc-check returns failure Fixes: e4acfbc18fc9 ("MIPS: Check Loongson3 LL/SC errata workaround correctness") Signed-off-by: Jiaxun Yang Link: https://t.me/c/1109254909/582977 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Makefile.postlink | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/mips/Makefile.postlink b/arch/mips/Makefile.postlink index ea0add7d56b23a..2543679da14924 100644 --- a/arch/mips/Makefile.postlink +++ b/arch/mips/Makefile.postlink @@ -24,9 +24,6 @@ quiet_cmd_relocs = RELOCS $@ vmlinux vmlinux.unstripped: FORCE @true -ifeq ($(CONFIG_CPU_LOONGSON3_WORKAROUNDS),y) - $(call if_changed,ls3_llsc) -endif ifeq ($(CONFIG_RELOCATABLE),y) $(call if_changed,relocs) endif From 322749e2207a6d56396eafa2f610f7a4ad3434c0 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sat, 2 Nov 2024 22:25:37 +0800 Subject: [PATCH 188/258] AOSCOS: platform/x86: hp-wmi: Mark 8BAB board for OMEN thermal profile Similar to 3a057bf30e044a51af8e6a8fe8cbfac49e2b9bc5 ("platform/x86: hp-wmi: Add thermal profile support for 8BAD boards"), the HP OMEN 9 2023 (8BAB) board (China-specific model) should also be added to the list of boards handled by the OMEN thermal profiles. Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/613200 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai [Mingcong Bai: (6.19) Resolved minor merge conflicts in drivers/platform/x86/hp/hp-wmi.c] Signed-off-by: Mingcong Bai [Mingcong Bai: Fixed a minor post-7.0-rc2 merge conflict in drivers/platform/x86/hp/hp-wmi.c] Signed-off-by: Mingcong Bai --- drivers/platform/x86/hp/hp-wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 37937d8a059bf9..2b35ebdafaeeba 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -158,7 +158,7 @@ static const char * const omen_thermal_profile_boards[] = { "88F7", "88FD", "88FE", "88FF", "8900", "8901", "8902", "8912", "8917", "8918", "8949", "894A", "89EB", "8A15", "8A42", - "8BAD", + "8BAB", "8BAD", "8C58", "8E41", }; @@ -178,7 +178,7 @@ static const char * const omen_thermal_profile_force_v0_boards[] = { */ static const char * const omen_timed_thermal_profile_boards[] = { "8A15", "8A42", - "8BAD", + "8BAB", "8BAD", }; /* DMI Board names of Victus 16-d laptops */ From f13d63890f142b2b5ded59f51f896145c0dad9d7 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 24 Oct 2024 20:53:23 +0800 Subject: [PATCH 189/258] AOSCOS: drm: amdgpu: disable ABM (Adaptive Backlight Management) by default Per report from a contributor, since v6.9, ABM was set to be automatically enabled/disabled. It may have seemed like a good idea for brighter screens, but the brightness adjustment is quite abrupt, which is quite bothersome. Disable this feature by default for now. Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/594612 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 60debd543e44ee..26b48c0100978b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -881,9 +881,9 @@ module_param_named(visualconfirm, amdgpu_dc_visual_confirm, uint, 0444); * Defaults to -1, or auto. Userspace can only override this level after * boot if it's set to auto. */ -int amdgpu_dm_abm_level = -1; +int amdgpu_dm_abm_level = 0; MODULE_PARM_DESC(abmlevel, - "ABM level (0 = off, 1-4 = backlight reduction level, -1 auto (default))"); + "ABM level (0 = off (default), 1-4 = backlight reduction level, -1 auto)"); module_param_named(abmlevel, amdgpu_dm_abm_level, int, 0444); int amdgpu_backlight = -1; From b8d2d0ee262487997a3767c59418875b70b9c2d8 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Tue, 22 Oct 2024 12:55:46 +0100 Subject: [PATCH 190/258] AOSCOS: MIPS: Check address space in ADE Signed-off-by: Jiaxun Yang Bug: https://t.me/c/1109254909/588345 Link: https://t.me/c/1109254909/588669 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/unaligned.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index db652c99b72e32..4f2055afbd7e83 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c @@ -1514,14 +1514,37 @@ static void emulate_load_store_MIPS16e(struct pt_regs *regs, void __user * addr) force_sig(SIGILL); } +static inline bool addr_legal(unsigned long addr, bool user) +{ + if (addr < TASK_SIZE) + return true; + + if (user) + return false; + + if (addr >= CKSEG3) + return true; + +#ifdef CONFIG_64BIT + if (addr >= XKPHYS && addr < CKSSEG) + return true; +#else + if (addr >= CKSEG0 && addr < CKSEG2) + return true; +#endif + + return false; +} + asmlinkage void do_ade(struct pt_regs *regs) { enum ctx_state prev_state; unsigned int *pc; prev_state = exception_enter(); - perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, - 1, regs, regs->cp0_badvaddr); + + if (!addr_legal(regs->cp0_badvaddr, user_mode(regs))) + goto sigbus; #ifdef CONFIG_64BIT /* @@ -1545,6 +1568,9 @@ asmlinkage void do_ade(struct pt_regs *regs) if (regs->cp0_badvaddr == regs->cp0_epc) goto sigbus; + perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, + 1, regs, regs->cp0_badvaddr); + if (user_mode(regs) && !test_thread_flag(TIF_FIXADE)) goto sigbus; if (unaligned_action == UNALIGNED_ACTION_SIGNAL) From db5ec278f06883c2137b8f98de5f41623367677c Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Thu, 21 Nov 2024 18:53:37 +0800 Subject: [PATCH 191/258] AOSCOS: remove dependencies on UBUNTU_ODM_DRIVERS That specific config option doesn't exist in AOSC OS. Link: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1912789 Co-authored-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpio/Kconfig | 1 - drivers/hwmon/Kconfig | 1 - drivers/leds/Kconfig | 1 - drivers/mfd/Kconfig | 1 - 4 files changed, 4 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 254bd37f75acf8..650ee3f44c672c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1789,7 +1789,6 @@ menu "PCI GPIO expanders" config GPIO_AAEON tristate "AAEON GPIO support" depends on ASUS_WMI - depends on UBUNTU_ODM_DRIVERS select MFD_AAEON help Say yes here to support GPIO pins on Single Board Computers produced diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 4b816f08b01359..7313b2b58e4ecd 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -41,7 +41,6 @@ comment "Native drivers" config SENSORS_AAEON tristate "AAEON hwmon driver" depends on X86 - depends on UBUNTU_ODM_DRIVERS select MFD_AAEON help This hwmon driver adds support for reporting temperature or fan diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 7a1ad03e1db2c8..646f1836005d96 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -75,7 +75,6 @@ config LEDS_88PM860X config LEDS_AAEON tristate "AAEON LED driver" depends on X86 - depends on UBUNTU_ODM_DRIVERS select MFD_AAEON help This led driver adds support for LED brightness control on Single diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1463c0b0a55d3b..b596ce54a6645c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2405,7 +2405,6 @@ config MFD_QCOM_PM8008 config MFD_AAEON tristate "AAEON WMI MFD devices" depends on ASUS_WMI - depends on UBUNTU_ODM_DRIVERS help Say yes here to support mltiple IO devices on Single Board Computers produced by AAEON. From af79842d242e12864191974f48f619390951fd79 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 12 Jan 2025 15:43:08 +0800 Subject: [PATCH 192/258] AOSCOS: kvm: disable enable_virt_at_load by default Disable enable_virt_at_load, a >= 6.12 on-by-default parameter introduced with commit b4886fab6fb6 ("KVM: Add a module param to allow enabling virtualization when KVM is loaded"). By enabling this paramter, VirtualBox (as of 7.1.4) will fail to launch any virtualised systems with the following error: VirtualBox can't operate in VMX root mode. Please disable the KVM kernel extension, recompile your kernel and reboot (VERR_VMX_IN_VMX_ROOT_MODE). The original commit indicated that this was introduced to reduce latency during KVM initialisation and that it "could be problematic for use case that need to spin up VMs quickly." An optimisation that is more than likely unnecessary for our (predominantly desktop-oriented) crowd. Bottom line - make VirtualBox work again (until they come up with a fix). Fixes: b4886fab6fb6 ("KVM: Add a module param to allow enabling virtualization when KVM is loaded") Reported-by: xtex Link: https://github.com/AOSC-Dev/aosc-os-abbs/issues/8897 Link: https://lore.kernel.org/kvm/ZwQjUSOle6sWARsr@google.com/T/ Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/715020 Acked-by: xtex Reviewed-by: xtex Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- virt/kvm/kvm_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 1a529098eec98f..3bca32fc408649 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -5568,7 +5568,7 @@ static struct miscdevice kvm_dev = { }; #ifdef CONFIG_KVM_GENERIC_HARDWARE_ENABLING -bool __ro_after_init enable_virt_at_load = true; +bool __ro_after_init enable_virt_at_load = false; module_param(enable_virt_at_load, bool, 0444); EXPORT_SYMBOL_FOR_KVM_INTERNAL(enable_virt_at_load); From 87fc347fe0fc82aa3133c01dd7e343df3190887c Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sat, 25 Jan 2025 03:03:03 +0800 Subject: [PATCH 193/258] AOSCOS: MIPS: loongson64: deselect ARCH_SUPPORTS_HUGETLBFS Disable HugeTLB support as it causes TLB exceptions and illegal memory access on 3A4000 and 3B4000 (possibly more, but we haven't tested other platforms yet). Suggested-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 3799140b031a7f..2f8402b01c41c6 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -26,7 +26,7 @@ config MIPS select ARCH_USE_MEMTEST select ARCH_USE_QUEUED_RWLOCKS select ARCH_USE_QUEUED_SPINLOCKS - select ARCH_SUPPORTS_HUGETLBFS if CPU_SUPPORTS_HUGEPAGES + select ARCH_SUPPORTS_HUGETLBFS if CPU_SUPPORTS_HUGEPAGES && !CPU_LOONGSON64 select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU select ARCH_WANT_IPC_PARSE_VERSION select ARCH_WANT_LD_ORPHAN_WARN From 3781b463596caf3ecaf581a9dd8da7b24ce88466 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sat, 25 Jan 2025 13:00:30 +0800 Subject: [PATCH 194/258] AOSCOS: MIPS: platform: disable unreliable CSR-based temperature reading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable CSR-based temperature reading, found only on Loongson 3A/B4000 processors. We found that on the Excelsior E23L (3B4000) boards, the temperature reading would erratically pin to 239°C, triggering immediate thermal shutdown. We don't yet know about the true cause behind this issue, as both boards we own are indeed using non-original heatsinks (but they can in fact sustain >= 6 hours of stress testing with s-tui). For now, disable this feature so that we can keep those systems working and Loongson 3A/B4000 can still report their temperatures via the Chip Temperature register (loongson_chiptemp). Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/736238 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/platform/mips/cpu_hwmon.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/platform/mips/cpu_hwmon.c b/drivers/platform/mips/cpu_hwmon.c index 2ac2f31090f96f..93619227f0396e 100644 --- a/drivers/platform/mips/cpu_hwmon.c +++ b/drivers/platform/mips/cpu_hwmon.c @@ -135,9 +135,22 @@ static int __init loongson_hwmon_init(void) { pr_info("Loongson Hwmon Enter...\n"); + /* + * FIXME: Disable CSR-based temperature reading, found only on Loongson + * 3A/B4000 processors. We found that on the Excelsior E23L (3B4000) + * boards, the temperature reading would erratically pin to 239°C, + * triggering immediate thermal shutdown. + * + * We don't yet know about the true cause behind this issue, as both + * boards we own are indeed using non-original heatsinks (but they can + * in fact sustain >= 6 hours of stress testing with s-tui). + * + * For now, disable this feature so that we can keep those systems + * working and Loongson 3A/B4000 can still report their temperatures + * via the Chip Temperature register (loongson_chiptemp). + */ if (cpu_has_csr()) - csr_temp_enable = csr_readl(LOONGSON_CSR_FEATURES) & - LOONGSON_CSRF_TEMP; + csr_temp_enable = 0; if (!csr_temp_enable && !loongson_chiptemp[0]) return -ENODEV; From 672ea1b64c0d436b509fe58a76b4206dc9dcd0e9 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 24 Feb 2025 14:01:03 +0800 Subject: [PATCH 195/258] AOSCOS: drm/radeon: limit mmiowb() hack for radeon_ring_commit() to MACH_LOONGSON64 As mentioned in the previous patch ("LOONGARCH64: FROMLIST: drm/radeon: Call mmiowb() at the end of radeon_ring_commit()") that attempts to fix improper removal of all `mmiowb()' calls in commit fb24ea52f78e ("drivers: Remove explicit invocations of mmiowb()"), the the wptr needs to be more effectively updated on weak ordering architectures such as LoongArch, as the `radeon_ring_commit()' was protected by a mutex. To quote Huacai Chen: "The mmio in radeon_ring_commit() is protected by a mutex rather than a spinlock, but in the mutex fastpath it behaves similar to spinlock and need a mmiowb() to make sure the wptr is up-to-date for hardware." From what could be seen in arch/, `mmiowb()' is only implemented for loongarch, mips, powerpc, riscv64, s390, and sh. With regards to the analysis above, this hack should technically be enabled for all the aforementioned architecture/machine with PCI support. However, according to our testing, this hack is only needed for LoongArch and Loongson 3 (MIPS) platforms based on the 7A bridge chip. Limit this hack to MACH_LOONGSON64 which matches this description to free the previous patch from a .loongarch64 suffix in AOSC OS, easing patchset maintenance. Fixes: "FROMLIST: drm/radeon: Call mmiowb() at the end of radeon_ring_commit()" Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/768003 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/gpu/drm/radeon/radeon_ring.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index ed5bd6079a8415..ee0e64b4ff3187 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -185,7 +185,9 @@ void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring, if (hdp_flush && rdev->asic->mmio_hdp_flush) rdev->asic->mmio_hdp_flush(rdev); radeon_ring_set_wptr(rdev, ring); +#if defined(CONFIG_MACH_LOONGSON64) mmiowb(); /* Make sure wptr is up-to-date for hw */ +#endif } /** From 7ed04043851109d45d6d7eed61acf5c83a5374bf Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 26 Feb 2025 11:43:02 +0800 Subject: [PATCH 196/258] AOSCOS: USB: core: only enable root_hub wakeup on MACH_LOONGSON64 The previous attempt to fix USB remote wake on Loongson 7A platforms broke S3 suspend on at least the following device(s): - Lenovo ThinkPad P14s Gen 1 (20Y1CTO1WW) Where the device would wake up immediately after entering S3 suspend. Since this fix was really only intended as a quirk for Loongson 7A platforms, guard the additional calls to `usb_enable_remote_wakeup()' and `usb_disable_remote_wakeup()' within `CONFIG_MACH_LOONGSON64', corresponding to all 64-bit Loongson platforms (unless this also breaks non-7A devices, in which case we will tighten the conditions further). Fixes: "FROMLIST: USB: core: Enable root_hub's remote wakeup for wakeup sources" Signed-off-by: Mingcong Bai Link: https://t.me/c/1109254909/816220 Link: https://t.me/c/1109254909/820298 Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/usb/core/hub.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index e90a72bd1cc348..69b5f376889b33 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3523,7 +3523,9 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (PMSG_IS_AUTO(msg)) goto err_wakeup; } +#if defined(CONFIG_MACH_LOONGSON64) usb_enable_remote_wakeup(udev->bus->root_hub); +#endif } /* disable USB2 hardware LPM */ @@ -3589,7 +3591,9 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) if (udev->do_remote_wakeup) { (void) usb_disable_remote_wakeup(udev); +#if defined(CONFIG_MACH_LOONGSON64) (void) usb_disable_remote_wakeup(udev->bus->root_hub); +#endif } err_wakeup: From f7c2b60004620bd2155415bf6dc86d00fad495e4 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Thu, 27 Mar 2025 13:53:42 +0800 Subject: [PATCH 197/258] AOSCOS: net/phytmac: include linux/vmalloc.h to fix build errors Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 9895b3fccd26e3..e9bf8ec24fa577 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -42,6 +42,7 @@ #include #include #include +#include #include "phytmac.h" #include "phytmac_ptp.h" From cd52e5808e8ce24110fea9c97363b07611f7c04b Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Wed, 9 Apr 2025 00:10:16 +0800 Subject: [PATCH 198/258] AOSCOS: bpftool: install into bin instead of sbin Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- tools/bpf/bpftool/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile index 0febf60e1b6465..f7b90e64ce64dc 100644 --- a/tools/bpf/bpftool/Makefile +++ b/tools/bpf/bpftool/Makefile @@ -297,8 +297,8 @@ clean: $(LIBBPF)-clean $(LIBBPF_BOOTSTRAP)-clean feature-detect-clean install-bin: $(OUTPUT)bpftool $(call QUIET_INSTALL, bpftool) - $(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/sbin - $(Q)$(INSTALL) $(OUTPUT)bpftool $(DESTDIR)$(prefix)/sbin/bpftool + $(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(prefix)/bin + $(Q)$(INSTALL) $(OUTPUT)bpftool $(DESTDIR)$(prefix)/bin/bpftool install: install-bin $(Q)$(INSTALL) -m 0755 -d $(DESTDIR)$(bash_compdir) @@ -306,7 +306,7 @@ install: install-bin uninstall: $(call QUIET_UNINST, bpftool) - $(Q)$(RM) -- $(DESTDIR)$(prefix)/sbin/bpftool + $(Q)$(RM) -- $(DESTDIR)$(prefix)/bin/bpftool $(Q)$(RM) -- $(DESTDIR)$(bash_compdir)/bpftool doc: From 846baa76d402564ede4b1840fc404a701386aa68 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sat, 12 Jul 2025 19:36:15 +0800 Subject: [PATCH 199/258] AOSCOS: net/phytmac: Replace xdp_do_flush_map() with xdp_do_flush() Per 7f04bd109d4c ("net: Tree wide: Replace xdp_do_flush_map() with xdp_do_flush()."): xdp_do_flush_map() is deprecated and new code should use xdp_do_flush() instead. Replace xdp_do_flush_map() with xdp_do_flush(). Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index e9bf8ec24fa577..3f1f2098284de9 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -1251,7 +1251,7 @@ static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, } if (xdp_xmit & PHYTMAC_XDP_REDIR) - xdp_do_flush_map(); + xdp_do_flush(); phytmac_rx_clean(queue); From 5fb3832b1a2ef8d40c01ed5058bd5f831d98cca2 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sat, 12 Jul 2025 19:38:09 +0800 Subject: [PATCH 200/258] AOSCOS: net/phytmac: Remove unnecessary pcim_iounmap_regions() call pcim_iounmap_regions() is removed in commit 855c634930f0 ("PCI: Remove pcim_iounmap_regions()"). Moreover, it is not necessary to call it in the driver's remove() function, since it does cleanup automatically on driver detach. Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/phytmac_pci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index fac5d717ad7669..69f34c2faaf5b0 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -177,7 +177,6 @@ static void phytmac_pci_remove(struct pci_dev *pdev) phytmac_free_pdata(pdata); bar_mask = pci_select_bars(pdev, IORESOURCE_MEM); - pcim_iounmap_regions(pdev, bar_mask); pci_disable_device(pdev); } From b0c13768781313a73c0653a96222505e021c2465 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 13 Jul 2025 21:59:21 +0800 Subject: [PATCH 201/258] AOSCOS: net/phytium: Depends on ACPI and ARCH_PHYTIUM The driver seems relying on ACPI to power up the device. And the device is only available as a part of Phytium SoC. Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/net/ethernet/phytium/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig index e68ca5d82181e8..74f671b5e0d416 100644 --- a/drivers/net/ethernet/phytium/Kconfig +++ b/drivers/net/ethernet/phytium/Kconfig @@ -20,6 +20,8 @@ if NET_VENDOR_PHYTIUM config PHYTMAC tristate "Phytium GMAC support" + depends on ACPI + depends on ARCH_PHYTIUM depends on HAS_DMA select PHYLINK select CRC32 From a142b785b4317331b1a17f77d768b3d7f69b1794 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Mon, 14 Jul 2025 18:06:42 +0800 Subject: [PATCH 202/258] AOSCOS: arm64: Disable MPAM by default We have to work OotB on W510, and ARM64 does not support extending the built-in command line with the one from the bootloader. So only try MPAM if the user explicitly gives "aosc.try_mpam=1" via the cmdline. Signed-off-by: Xi Ruoyao Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/arm64/kernel/pi/idreg-override.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/kernel/pi/idreg-override.c b/arch/arm64/kernel/pi/idreg-override.c index bc57b290e5e7ba..2d45dbb6894760 100644 --- a/arch/arm64/kernel/pi/idreg-override.c +++ b/arch/arm64/kernel/pi/idreg-override.c @@ -215,6 +215,17 @@ static const struct ftr_set_desc sw_features __prel64_initconst = { }, }; +static struct arm64_ftr_override __read_mostly aosc_feature_override; + +static const struct ftr_set_desc aosc_features __prel64_initconst = { + .name = "aosc", + .override = &aosc_feature_override, + .fields = { + FIELD("try_mpam", 0, NULL), + {} + }, +}; + static const PREL64(const struct ftr_set_desc, reg) regs[] __prel64_initconst = { { &mmfr0 }, @@ -226,6 +237,7 @@ PREL64(const struct ftr_set_desc, reg) regs[] __prel64_initconst = { { &isar2 }, { &smfr0 }, { &sw_features }, + { &aosc_features }, }; static const struct { @@ -390,6 +402,14 @@ static __init void parse_cmdline(const void *fdt, int chosen) if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && prop) __parse_cmdline(prop, true); + + /* + * Sorry but we have to work OotB on some platforms with broken + * firmware, notably W510. Use "aosc.try_mpam=1" if you really need + * MPAM on AOSC. + */ + if (!arm64_apply_feature_override(0, 0, 4, &aosc_feature_override)) + __parse_cmdline("arm64.nompam", true); } void __init init_feature_override(u64 boot_status, const void *fdt, From 9fa045653dd0075c695be458f77546d38aa5e33f Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 25 Feb 2025 17:43:40 +0800 Subject: [PATCH 203/258] MARKER: AOSCOS: Start of Lemote patches Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 2f8402b01c41c6..93fc1dba4faf2d 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -550,6 +550,7 @@ config MACH_LOONGSON64 select USE_OF select BUILTIN_DTB select PCI_HOST_GENERIC + depends on BROKEN help This enables the support of Loongson-2/3 family of machines. From ccfd54cd3727e42fa0599b4e54e799938655a009 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Fri, 29 Nov 2019 09:11:38 +0800 Subject: [PATCH 204/258] AOSCOS: MIPS: Loongson: Add constant timer support Partially implemented by commit 8267e78f020a ("MIPS: Tidy up CP0.Config6 bits definition"). [Mingcong Bai: Resolved merge conflicts in the following... arch/mips/include/asm/clocksource.h arch/mips/include/asm/cpu-features.h arch/mips/include/asm/cpu.h arch/mips/include/asm/mach-loongson64/loongson_regs.h arch/mips/include/asm/mipsregs.h arch/mips/include/asm/vdso/gettimeofday.h arch/mips/kernel/cpu-probe.c arch/mips/loongson64/constant_timer.c arch/mips/loongson64/loongson-3/Makefile => arch/mips/loongson64/Makefile arch/mips/loongson64/smp.c] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/include/asm/cpu-features.h | 4 + arch/mips/include/asm/cpu.h | 1 + .../asm/mach-loongson64/loongson_regs.h | 30 ++ arch/mips/include/asm/mipsregs.h | 1 + arch/mips/include/asm/time.h | 20 +- arch/mips/include/asm/vdso/clocksource.h | 3 +- arch/mips/include/asm/vdso/gettimeofday.h | 20 ++ arch/mips/kernel/cpu-probe.c | 16 ++ arch/mips/loongson64/Makefile | 1 + arch/mips/loongson64/constant_timer.c | 256 ++++++++++++++++++ arch/mips/loongson64/smp.c | 4 +- 11 files changed, 352 insertions(+), 4 deletions(-) create mode 100644 arch/mips/loongson64/constant_timer.c diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index 3f11e5218e6c65..cbc1795e204779 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -658,6 +658,10 @@ # define cpu_has_mm_full __opt(MIPS_CPU_MM_FULL) #endif +#ifndef cpu_has_constant_timer +# define cpu_has_constant_timer __opt(MIPS_CPU_CONST_TIMER) +#endif + /* * Guest capabilities */ diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h index 0fd9f9bbd21f1d..05e6ce0bb3fb75 100644 --- a/arch/mips/include/asm/cpu.h +++ b/arch/mips/include/asm/cpu.h @@ -420,6 +420,7 @@ enum cpu_type_enum { #define MIPS_CPU_MAC_2008_ONLY BIT_ULL(60) /* CPU Only support MAC2008 Fused multiply-add instruction */ #define MIPS_CPU_FTLBPAREX BIT_ULL(61) /* CPU has FTLB parity exception */ #define MIPS_CPU_GSEXCEX BIT_ULL(62) /* CPU has GSExc exception */ +#define MIPS_CPU_CONST_TIMER BIT_ULL(63) /* CPU has constant timer */ /* * CPU ASE encodings diff --git a/arch/mips/include/asm/mach-loongson64/loongson_regs.h b/arch/mips/include/asm/mach-loongson64/loongson_regs.h index fec76750760492..47294ab78a2cd3 100644 --- a/arch/mips/include/asm/mach-loongson64/loongson_regs.h +++ b/arch/mips/include/asm/mach-loongson64/loongson_regs.h @@ -131,6 +131,9 @@ static inline u32 read_cpucfg(u32 reg) #define LOONGSON_CFG7_GCCAEQRP BIT(0) #define LOONGSON_CFG7_UCAWINP BIT(1) +#define CSR_TO_RAW_ADDR(cpu, csr) ((0x900000003ff00000 | csr | \ + (((u64)cpu & 0xc) << 42)) | (((u64)cpu & 0x3) << 8)) + static inline bool cpu_has_csr(void) { if (cpu_has_cfg()) @@ -247,6 +250,12 @@ static inline void csr_writeq(u64 val, u32 reg) #define CSR_MAIL_SEND_BUF_SHIFT 32 #define CSR_MAIL_SEND_H32_MASK 0xFFFFFFFF00000000ULL +#define LOONGSON_CSR_TIMER_CFG 0x1060 +#define LOONGSON_CSR_TIMER_TICK 0x1070 +#define CONSTANT_TIMER_CFG_PERIODIC (_ULCAST_(1) << 62) +#define CONSTANT_TIMER_CFG_EN (_ULCAST_(1) << 61) +#define CONSTANT_TIMER_INITVAL_RESET (_ULCAST_(0xffff) << 48) + static inline u64 drdtime(void) { int rID = 0; @@ -265,4 +274,25 @@ static inline u64 drdtime(void) return val; } +static inline unsigned int calc_const_freq(void) +{ + unsigned int res; + unsigned int base_freq; + unsigned int cfm, cfd; + + res = read_cpucfg(LOONGSON_CFG2); + if (!(res & LOONGSON_CFG2_LLFTP)) + return 0; + + res = read_cpucfg(LOONGSON_CFG5); + cfm = res & 0xffff; + cfd = (res >> 16) & 0xffff; + base_freq = read_cpucfg(LOONGSON_CFG4); + + if (!base_freq || !cfm || !cfd) + return 0; + else + return (base_freq * cfm / cfd); +} + #endif diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 12a095dbf9e2a9..805efd3ea9697d 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -838,6 +838,7 @@ #define MTI_CONF6_SYND (_ULCAST_(1) << 13) /* Sleep state performance counter disable */ #define MTI_CONF6_SPCD (_ULCAST_(1) << 14) + /* proAptiv FTLB on/off bit */ #define MTI_CONF6_FTLBEN (_ULCAST_(1) << 15) /* Disable load/store bonding */ diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index 93f8cd52e3d471..72fec943a9e442 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h @@ -42,11 +42,19 @@ extern int __weak get_c0_perfcount_int(void); */ extern unsigned int get_c0_compare_int(void); extern int r4k_clockevent_init(void); +#ifdef CONFIG_CPU_LOONGSON64 +extern int constant_clockevent_init(void); +#else +static inline int constant_clockevent_init(void) { return 0; } +#endif static inline int mips_clockevent_init(void) { #ifdef CONFIG_CEVT_R4K - return r4k_clockevent_init(); + if (!cpu_has_constant_timer) + return r4k_clockevent_init(); + else + return constant_clockevent_init(); #else return -ENXIO; #endif @@ -56,11 +64,19 @@ static inline int mips_clockevent_init(void) * Initialize the count register as a clocksource */ extern int init_r4k_clocksource(void); +#ifdef CONFIG_CPU_LOONGSON64 +extern int init_constant_clocksource(void); +#else +static inline int init_constant_clocksource(void) { return 0; } +#endif static inline __init int init_mips_clocksource(void) { #ifdef CONFIG_CSRC_R4K - return init_r4k_clocksource(); + if (!cpu_has_constant_timer) + return init_r4k_clocksource(); + else + return init_constant_clocksource(); #else return 0; #endif diff --git a/arch/mips/include/asm/vdso/clocksource.h b/arch/mips/include/asm/vdso/clocksource.h index 510e1671d89851..7fd43ca06eb117 100644 --- a/arch/mips/include/asm/vdso/clocksource.h +++ b/arch/mips/include/asm/vdso/clocksource.h @@ -4,6 +4,7 @@ #define VDSO_ARCH_CLOCKMODES \ VDSO_CLOCKMODE_R4K, \ - VDSO_CLOCKMODE_GIC + VDSO_CLOCKMODE_GIC, \ + VDSO_CLOCKMODE_CONST #endif /* __ASM_VDSOCLOCKSOURCE_H */ diff --git a/arch/mips/include/asm/vdso/gettimeofday.h b/arch/mips/include/asm/vdso/gettimeofday.h index 32d2d173fdc0be..8cc0beef182d18 100644 --- a/arch/mips/include/asm/vdso/gettimeofday.h +++ b/arch/mips/include/asm/vdso/gettimeofday.h @@ -183,6 +183,22 @@ static __always_inline u64 read_gic_count(const struct vdso_time_data *data) #endif +#ifdef CONFIG_CPU_LOONGSON64 +static __always_inline u64 read_const_count(void) +{ + unsigned long count; + + __asm__ __volatile__( + " .set push\n" + " .set mips32r2\n" + " rdhwr %0, $30\n" + " .set pop\n" + : "=r" (count)); + + return count; +} +#endif + static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, const struct vdso_time_data *vd) { @@ -193,6 +209,10 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, #ifdef CONFIG_CLKSRC_MIPS_GIC if (clock_mode == VDSO_CLOCKMODE_GIC) return read_gic_count(vd); +#endif +#ifdef CONFIG_CPU_LOONGSON64 + if (clock_mode == VDSO_CLOCKMODE_CONST) + return read_const_count(); #endif /* * Core checks mode already. So this raced against a concurrent diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 88bae0ca721ca8..3647315658b752 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -34,6 +34,10 @@ #include +#ifdef CONFIG_CPU_LOONGSON64 +#include +#endif + /* Hardware capabilities */ unsigned int elf_hwcap __read_mostly; EXPORT_SYMBOL_GPL(elf_hwcap); @@ -1691,6 +1695,17 @@ static inline void decode_cpucfg(struct cpuinfo_mips *c) c->ases |= MIPS_ASE_LOONGSON_CAM; } +static void decode_loongson_cpucfg(struct cpuinfo_mips *c) +{ +#ifdef CONFIG_CPU_LOONGSON64 + unsigned int cpucfg; + + cpucfg = read_cpucfg(LOONGSON_CFG2); + if (cpucfg & LOONGSON_CFG2_LLFTP) + c->options |= MIPS_CPU_CONST_TIMER; +#endif +} + static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) { c->cputype = CPU_LOONGSON64; @@ -1752,6 +1767,7 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) set_isa(c, MIPS_CPU_ISA_M64R2); __cpu_full_name[cpu] = "Loongson-3A R4 (Loongson-3A4000)"; decode_cpucfg(c); + decode_loongson_cpucfg(c); change_c0_config6(LOONGSON_CONF6_EXTIMER | LOONGSON_CONF6_INTIMER, LOONGSON_CONF6_INTIMER); break; diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index cbba30dfddf5d0..ba083beb6ca818 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ + constant_timer.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/constant_timer.c b/arch/mips/loongson64/constant_timer.c new file mode 100644 index 00000000000000..b8bdbb2c47b513 --- /dev/null +++ b/arch/mips/loongson64/constant_timer.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int constant_timer_irq_installed; + +static DEFINE_SPINLOCK(state_lock); +DEFINE_PER_CPU(struct clock_event_device, constant_clockevent_device); + +static void constant_event_handler(struct clock_event_device *dev) +{ +} + +static irqreturn_t constant_timer_interrupt(int irq, void *data) +{ + int cpu = smp_processor_id(); + const int r2 = cpu_has_mips_r2_r6; + struct clock_event_device *cd; + + if ((cp0_perfcount_irq < 0) && perf_irq() == IRQ_HANDLED && !r2) + return IRQ_HANDLED; + + /* + * The same applies to performance counter interrupts. But with the + * above we now know that the reason we got here must be a timer + * interrupt. Being the paranoiacs we are we check anyway. + */ + if (!r2 || (read_c0_cause() & CAUSEF_TI)) { + /* Clear Count/Compare Interrupt */ + write_c0_compare(read_c0_compare()); + cd = &per_cpu(constant_clockevent_device, cpu); + cd->event_handler(cd); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static struct irqaction constant_timer_irqaction = { + .handler = constant_timer_interrupt, + .flags = IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED, + .name = "timer", +}; + +static int constant_set_state_oneshot(struct clock_event_device *evt) +{ + unsigned int raw_cpuid; + unsigned long cfg, addr; + + spin_lock(&state_lock); + + raw_cpuid = cpu_logical_map(smp_processor_id()); + addr = CSR_TO_RAW_ADDR(raw_cpuid, LOONGSON_CSR_TIMER_CFG); + + if (!cpu_has_csr()) + cfg = readq((void *)addr); + else + cfg = csr_readq(LOONGSON_CSR_TIMER_CFG); + + /* set timer type + * 1 : periodic interrupt + * 0 : non-periodic(oneshot) interrupt + */ + cfg |= CONSTANT_TIMER_CFG_EN; + cfg &= ~CONSTANT_TIMER_CFG_PERIODIC; + + if (!cpu_has_csr()) + writeq(cfg, (void *)addr); + else + csr_writeq(cfg, LOONGSON_CSR_TIMER_CFG); + + spin_unlock(&state_lock); + + return 0; +} + +static int constant_set_state_oneshot_stopped(struct clock_event_device *evt) +{ + return 0; +} + +static int constant_set_state_periodic(struct clock_event_device *evt) +{ + unsigned int period; + unsigned int raw_cpuid; + unsigned long cfg, addr; + + spin_lock(&state_lock); + + raw_cpuid = cpu_logical_map(smp_processor_id()); + addr = CSR_TO_RAW_ADDR(raw_cpuid, LOONGSON_CSR_TIMER_CFG); + + if (!cpu_has_csr()) + cfg = readq((void *)addr); + else + cfg = csr_readq(LOONGSON_CSR_TIMER_CFG); + + cfg &= CONSTANT_TIMER_INITVAL_RESET; + cfg |= (CONSTANT_TIMER_CFG_PERIODIC | CONSTANT_TIMER_CFG_EN); + + period = calc_const_freq(); + if (!period) + period = cpu_clock_freq; + + period = period / HZ; + + if (!cpu_has_csr()) + writeq(cfg | period, (void *)addr); + else + csr_writeq(cfg | period, LOONGSON_CSR_TIMER_CFG); + + spin_unlock(&state_lock); + + return 0; +} + +static int constant_set_state_shutdown(struct clock_event_device *evt) +{ + return 0; +} + +static int constant_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + unsigned long addr; + unsigned int raw_cpuid; + + raw_cpuid = cpu_logical_map(smp_processor_id()); + addr = CSR_TO_RAW_ADDR(raw_cpuid, LOONGSON_CSR_TIMER_CFG); + + writeq(delta | CONSTANT_TIMER_CFG_EN, (void *)addr); + + return 0; +} + +static int csr_constant_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + csr_writeq(delta | CONSTANT_TIMER_CFG_EN, LOONGSON_CSR_TIMER_CFG); + return 0; +} + +int constant_clockevent_init(void) +{ + unsigned int irq; + unsigned int config; + unsigned int const_freq; + unsigned long min_delta = 0x600; + unsigned long max_delta = (1UL << 48) - 1; + unsigned int cpu = smp_processor_id(); + struct clock_event_device *cd; + + config = read_c0_config6(); + config |= LOONGSON_CONF6_EXTIMER; + write_c0_config6(config); + + const_freq = calc_const_freq(); + if (!const_freq) + const_freq = cpu_clock_freq; + + irq = get_c0_compare_int(); + + cd = &per_cpu(constant_clockevent_device, cpu); + + cd->name = "Constant"; + cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_PERCPU; + + cd->rating = 320; + cd->irq = irq; + cd->cpumask = cpumask_of(cpu); + cd->set_state_oneshot = constant_set_state_oneshot; + cd->set_state_oneshot_stopped = constant_set_state_oneshot_stopped; + cd->set_state_periodic = constant_set_state_periodic; + cd->set_state_shutdown = constant_set_state_shutdown; + if (!cpu_has_csr()) + cd->set_next_event = constant_next_event; + else + cd->set_next_event = csr_constant_next_event; + + cd->event_handler = constant_event_handler; + + clockevents_config_and_register(cd, const_freq, min_delta, max_delta); + + if (constant_timer_irq_installed) + return 0; + + constant_timer_irq_installed = 1; + setup_irq(irq, &constant_timer_irqaction); + + return 0; +} + +static u64 read_const_counter(struct clocksource *clk) +{ + u64 count; + + __asm__ __volatile__( + " .set push\n" + " .set mips32r2\n" + " rdhwr %0, $30\n" + " .set pop\n" + : "=r" (count)); + + return count; +} + +static struct clocksource clocksource_const = { + .name = "Constant", + .rating = 400, + .read = read_const_counter, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .mult = 0, + .shift = 10, +}; + +static u64 read_sched_clock(void) +{ + u64 count; + + __asm__ __volatile__( + " .set push\n" + " .set mips32r2\n" + " rdhwr %0, $30\n" + " .set pop\n" + : "=r" (count)); + + return count; +} + +int __init init_constant_clocksource(void) +{ + int res; + unsigned long freq; + + freq = calc_const_freq(); + if (freq) + freq = cpu_clock_freq; + + clocksource_const.mult = + clocksource_hz2mult(freq, clocksource_const.shift); + + res = clocksource_register_hz(&clocksource_const, freq); + + sched_clock_register(read_sched_clock, 64, freq); + clocksource_const.archdata.vdso_clock_mode = VDSO_CLOCKMODE_CONST; + + return res; +} diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c index c4e97a59df3055..c30f83d58b37b9 100644 --- a/arch/mips/loongson64/smp.c +++ b/arch/mips/loongson64/smp.c @@ -435,7 +435,9 @@ static void loongson3_smp_finish(void) { int cpu = smp_processor_id(); - write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); + if (!cpu_has_constant_timer) + write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); + local_irq_enable(); ipi_clear_buf(cpu); From a57ff1b6c73d7b8842f70cc733539f3abe64c924 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 3 Dec 2024 16:52:14 +0800 Subject: [PATCH 205/258] AOSCOS: MIPS: loongson64: fix constant timer build on kernel versions >= v5.7-rc2 With commit 07d8350ede4c ("genirq: Remove setup_irq() and remove_irq()"), setup_irq() was removed in favour of request_irq(). This was done following commit cc2550b421aa ("clocksource: Replace setup_irq() by request_irq()") (v5.7-rc1). Follow the latter commit as a model to revise loongson64's constant_timer and use request_irq() instead. Also add error handling and output for cases of failure. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/constant_timer.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/arch/mips/loongson64/constant_timer.c b/arch/mips/loongson64/constant_timer.c index b8bdbb2c47b513..975fb93c4efa2e 100644 --- a/arch/mips/loongson64/constant_timer.c +++ b/arch/mips/loongson64/constant_timer.c @@ -42,12 +42,6 @@ static irqreturn_t constant_timer_interrupt(int irq, void *data) return IRQ_NONE; } -static struct irqaction constant_timer_irqaction = { - .handler = constant_timer_interrupt, - .flags = IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED, - .name = "timer", -}; - static int constant_set_state_oneshot(struct clock_event_device *evt) { unsigned int raw_cpuid; @@ -155,6 +149,7 @@ int constant_clockevent_init(void) unsigned long max_delta = (1UL << 48) - 1; unsigned int cpu = smp_processor_id(); struct clock_event_device *cd; + int err; config = read_c0_config6(); config |= LOONGSON_CONF6_EXTIMER; @@ -192,7 +187,14 @@ int constant_clockevent_init(void) return 0; constant_timer_irq_installed = 1; - setup_irq(irq, &constant_timer_irqaction); + + err = request_irq(irq, constant_timer_interrupt, + IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED, + "timer", cd); + if (err) { + pr_err("loongson64: setup irq for constant_clock_event failed: %d\n", err); + return err; + } return 0; } From 637c0b1aaf6915a3fd21afcc3a10242431cff301 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 3 Dec 2024 16:56:36 +0800 Subject: [PATCH 206/258] AOSCOS: MIPS: loongson64: use generic vDSO clock mode storage for constant_timer Follow commit e1bdb22ebe53 ("mips: vdso: Use generic VDSO clock mode storage") (v5.7-rc1) and revise vdso_clock_mode setting in constant_timer code for loongson64 platforms. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/constant_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/constant_timer.c b/arch/mips/loongson64/constant_timer.c index 975fb93c4efa2e..c1fda4a3ae1657 100644 --- a/arch/mips/loongson64/constant_timer.c +++ b/arch/mips/loongson64/constant_timer.c @@ -252,7 +252,7 @@ int __init init_constant_clocksource(void) res = clocksource_register_hz(&clocksource_const, freq); sched_clock_register(read_sched_clock, 64, freq); - clocksource_const.archdata.vdso_clock_mode = VDSO_CLOCKMODE_CONST; + clocksource_const.vdso_clock_mode = VDSO_CLOCKMODE_CONST; return res; } From 9aeb1e3cd926d988aa11cbca3ad7bdeeabf72227 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 207/258] AOSCOS: MIPS: Loongson: Add PMON read/write in OS support PMON is the firmware(BIOS) of Loongson. Usage: modprobe mtd modprobe mtdblock modprobe pmon_flash dd if=pmon.bin of=/dev/mtdblcok0 Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/platform/mips/Makefile | 4 ++ drivers/platform/mips/pmon_flash.c | 100 +++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 drivers/platform/mips/pmon_flash.c diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile index 4c71444e453a6c..4dbbc9bd9d87fd 100644 --- a/drivers/platform/mips/Makefile +++ b/drivers/platform/mips/Makefile @@ -2,3 +2,7 @@ obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o obj-$(CONFIG_RS780E_ACPI) += rs780e-acpi.o obj-$(CONFIG_LS2K_RESET) += ls2k-reset.o + +ifdef CONFIG_MTD +obj-m += pmon_flash.o +endif diff --git a/drivers/platform/mips/pmon_flash.c b/drivers/platform/mips/pmon_flash.c new file mode 100644 index 00000000000000..8fd5f4bf8b1bb3 --- /dev/null +++ b/drivers/platform/mips/pmon_flash.c @@ -0,0 +1,100 @@ +/* + * Copyright www.lemote.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FLASH_PHYS_ADDR 0x1fc00000 +#define FLASH_SIZE 0x100000 + +#define FLASH_PARTITION0_ADDR 0x00000000 +#define FLASH_PARTITION0_SIZE 0x00100000 + +struct map_info flash_map = { + .name = "flash device", + .size = FLASH_SIZE, + .bankwidth = 1, +}; + +struct mtd_partition flash_parts[] = { + { + .name = "Bootloader", + .offset = FLASH_PARTITION0_ADDR, + .size = FLASH_PARTITION0_SIZE + }, +}; + +#define PARTITION_COUNT ARRAY_SIZE(flash_parts) + +static struct mtd_info *mymtd; + +int __init init_flash(void) +{ + printk(KERN_NOTICE "Flash flash device: %x at %x\n", + FLASH_SIZE, FLASH_PHYS_ADDR); + + flash_map.phys = FLASH_PHYS_ADDR; + flash_map.virt = ioremap(FLASH_PHYS_ADDR, + FLASH_SIZE); + + if (!flash_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&flash_map); + + mymtd = do_map_probe("jedec_probe", &flash_map); + if (mymtd) { + mtd_device_register(mymtd, flash_parts, PARTITION_COUNT); + printk(KERN_NOTICE "pmon flash device initialized\n"); + return 0; + } + + iounmap((void *)flash_map.virt); + return -ENXIO; +} + +static void __exit cleanup_flash(void) +{ + if (mymtd) { + mtd_device_unregister(mymtd); + map_destroy(mymtd); + } + if (flash_map.virt) { + iounmap((void *)flash_map.virt); + flash_map.virt = 0; + } +} + +module_init(init_flash); +module_exit(cleanup_flash); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yanhua"); +MODULE_DESCRIPTION("MTD map driver for pmon programming module"); From 0201c4a1b69aa192507f155139a98d72205f093d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 5 Dec 2024 16:19:39 +0800 Subject: [PATCH 208/258] AOSCOS: platform: pmon_flash: mark init_flash() function as static Suppress a missing prototypes warning during build. Fixes: "AOSCOS: MIPS: Loongson: Add PMON read/write in OS support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/platform/mips/pmon_flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mips/pmon_flash.c b/drivers/platform/mips/pmon_flash.c index 8fd5f4bf8b1bb3..6c5a3c4342b294 100644 --- a/drivers/platform/mips/pmon_flash.c +++ b/drivers/platform/mips/pmon_flash.c @@ -53,7 +53,7 @@ struct mtd_partition flash_parts[] = { static struct mtd_info *mymtd; -int __init init_flash(void) +static int __init init_flash(void) { printk(KERN_NOTICE "Flash flash device: %x at %x\n", FLASH_SIZE, FLASH_PHYS_ADDR); From f563810440e898dab81a31ff1c6eccda47e20fef Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 00:29:07 +0800 Subject: [PATCH 209/258] AOSCOS: MIPS: math-emu: replace CPU_LOONGSON3 conditions with CPU_LOONGSON64 Per commit 30ad29bb4888 ("MIPS: Loongson: Naming style cleanup and rework"), rename the CPU_LOONGSON3 definition as CPU_LOONGSON64. Fixes: "FROMLIST: MIPS: math-emu: Add madd/msub/nmadd/nmsub emulation for Loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/math-emu/cp1emu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index cc0360e8dd62ca..fc1d0c83b554aa 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -1451,7 +1451,7 @@ static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); } -#ifndef CONFIG_CPU_LOONGSON3 +#ifndef CONFIG_CPU_LOONGSON64 DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); @@ -1515,7 +1515,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; -#ifdef CONFIG_CPU_LOONGSON3 +#ifdef CONFIG_CPU_LOONGSON64 case madd_s_op: handler = ieee754sp_madd; goto scoptop; @@ -1638,7 +1638,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; -#ifdef CONFIG_CPU_LOONGSON3 +#ifdef CONFIG_CPU_LOONGSON64 case madd_d_op: handler = ieee754dp_madd; goto dcoptop; From 3129f3924b5005e111216d1cf0c4efc9877229ab Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 210/258] AOSCOS: Optimize clear_page Signed-off-by: chenj Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/include/asm/uasm.h | 5 +++++ arch/mips/include/uapi/asm/inst.h | 7 +++++++ arch/mips/mm/page.c | 9 +++++++++ arch/mips/mm/uasm-mips.c | 24 ++++++++++++++++++++++++ arch/mips/mm/uasm.c | 12 +++++++++++- 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/arch/mips/include/asm/uasm.h b/arch/mips/include/asm/uasm.h index b43bfd44525213..2573d559320e63 100644 --- a/arch/mips/include/asm/uasm.h +++ b/arch/mips/include/asm/uasm.h @@ -42,6 +42,10 @@ void uasm_i##op(u32 **buf, unsigned int a, signed int b, unsigned int c) #define Ip_s3s1s2(op) \ void uasm_i##op(u32 **buf, int a, int b, int c) +#define Ip_u4u2u1s3(op) \ +void uasm_i##op(u32 **buf, unsigned int a, unsigned int b, signed int c, \ + unsigned int d) + #define Ip_u2u1s3(op) \ void uasm_i##op(u32 **buf, unsigned int a, unsigned int b, signed int c) @@ -159,6 +163,7 @@ Ip_u2s3u1(_sd); Ip_u3u1u2(_seleqz); Ip_u3u1u2(_selnez); Ip_u2s3u1(_sh); +Ip_u4u2u1s3(_gssq); Ip_u2u1u3(_sll); Ip_u3u2u1(_sllv); Ip_s3s1s2(_slt); diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h index c29dbc8c1d491e..6d41c8bbbdec2c 100644 --- a/arch/mips/include/uapi/asm/inst.h +++ b/arch/mips/include/uapi/asm/inst.h @@ -137,6 +137,13 @@ enum ddivu_op { ddivu_dmodu_op = 0x3, }; +/* + * func field of spec opcode. + */ +enum swc2_op { + gssq_op = 0x20, +}; + /* * rt field of bcond opcodes. */ diff --git a/arch/mips/mm/page.c b/arch/mips/mm/page.c index 1df237bd4a72bd..21c9d35ec25e86 100644 --- a/arch/mips/mm/page.c +++ b/arch/mips/mm/page.c @@ -206,6 +206,11 @@ static void set_prefetch_parameters(void) else if (cpu_has_cache_cdex_p) cache_line_size = cpu_dcache_line_size(); } + +#ifdef CONFIG_CPU_LOONGSON3 + clear_word_size = 16; +#endif + /* * Too much unrolling will overflow the available space in * clear_space_array / copy_page_array. @@ -220,11 +225,15 @@ static void set_prefetch_parameters(void) static void build_clear_store(u32 **buf, int off) { +#ifdef CONFIG_CPU_LOONGSON3 + uasm_i_gssq(buf, ZERO, ZERO, off, A0); +#else if (cpu_has_64bit_gp_regs || cpu_has_64bit_zero_reg) { uasm_i_sd(buf, GPR_ZERO, off, GPR_A0); } else { uasm_i_sw(buf, GPR_ZERO, off, GPR_A0); } +#endif } static inline void build_clear_pref(u32 **buf, int off) diff --git a/arch/mips/mm/uasm-mips.c b/arch/mips/mm/uasm-mips.c index e15c6700cd0884..7e44ea365795b1 100644 --- a/arch/mips/mm/uasm-mips.c +++ b/arch/mips/mm/uasm-mips.c @@ -27,6 +27,10 @@ #define RT_SH 16 #define SCIMM_MASK 0xfffff #define SCIMM_SH 6 +#define RZ_MASK 0x1f +#define RZ_SH 0 +#define RC_MASK 0x1ff +#define RC_SH 6 /* This macro sets the non-variable bits of an instruction. */ #define M(a, b, c, d, e, f) \ @@ -203,6 +207,7 @@ static const struct insn insn_table[insn_invalid] = { [insn_xor] = {M(spec_op, 0, 0, 0, 0, xor_op), RS | RT | RD}, [insn_xori] = {M(xori_op, 0, 0, 0, 0, 0), RS | RT | UIMM}, [insn_yield] = {M(spec3_op, 0, 0, 0, 0, yield_op), RS | RD}, + [insn_gssq] = {M(swc2_op, 0, 0, 0, 0, gssq_op), RT | RS | RZ | RC}, }; #undef M @@ -225,6 +230,21 @@ static inline u32 build_jimm(u32 arg) return (arg >> 2) & JIMM_MASK; } +static inline u32 build_rz(u32 arg) +{ + WARN(arg & ~RZ_MASK, KERN_WARNING "Micro-assembler field overflow\n"); + + return (arg & RZ_MASK) << RZ_SH; +} + +static inline u32 build_rc(s32 arg) +{ + WARN((arg >> 4) > 0xff || + (arg >> 4) < -0x100, KERN_WARNING "Micro-assembler field overflow\n"); + + return ((arg >> 4) & RC_MASK) << RC_SH; +} + /* * The order of opcode arguments is implicitly left to right, * starting with RS and ending with FUNC or IMM. @@ -252,6 +272,10 @@ static void build_insn(u32 **buf, enum opcode opc, ...) op |= build_rd(va_arg(ap, u32)); if (ip->fields & RE) op |= build_re(va_arg(ap, u32)); + if (ip->fields & RZ) + op |= build_rz(va_arg(ap, u32)); + if (ip->fields & RC) + op |= build_rc(va_arg(ap, s32)); if (ip->fields & SIMM) op |= build_simm(va_arg(ap, s32)); if (ip->fields & UIMM) diff --git a/arch/mips/mm/uasm.c b/arch/mips/mm/uasm.c index 125140979d62c5..2a9ca7de437603 100644 --- a/arch/mips/mm/uasm.c +++ b/arch/mips/mm/uasm.c @@ -26,6 +26,8 @@ enum fields { SET = 0x200, SCIMM = 0x400, SIMM9 = 0x800, + RZ = 0x1000, + RC = 0x2000 }; #define OP_MASK 0x3f @@ -65,7 +67,7 @@ enum opcode { insn_sllv, insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra, insn_srav, insn_srl, insn_srlv, insn_subu, insn_sw, insn_sync, insn_syscall, insn_tlbp, insn_tlbr, insn_tlbwi, insn_tlbwr, insn_wait, - insn_wsbh, insn_xor, insn_xori, insn_yield, + insn_wsbh, insn_xor, insn_xori, insn_yield, insn_gssq, insn_invalid /* insn_invalid must be last */ }; @@ -198,6 +200,13 @@ Ip_u2s3u1(op) \ } \ UASM_EXPORT_SYMBOL(uasm_i##op); +#define I_u4u2u1s3(op) \ +Ip_u4u2u1s3(op) \ +{ \ + build_insn(buf, insn##op, d, b, a, c); \ +} \ +UASM_EXPORT_SYMBOL(uasm_i##op); + #define I_u2u1s3(op) \ Ip_u2u1s3(op) \ { \ @@ -356,6 +365,7 @@ I_u2s3u1(_sd) I_u3u1u2(_seleqz) I_u3u1u2(_selnez) I_u2s3u1(_sh) +I_u4u2u1s3(_gssq) I_u2u1u3(_sll) I_u3u2u1(_sllv) I_s3s1s2(_slt) From 903b5b69ce4be2a02995abda5038a0567233f62b Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 211/258] AOSCOS: memset optimization for loongson-3 [Mingcong Bai: Resolved a minor conflict in arch/mips/loongson64/Makefile.] Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/Makefile | 1 + arch/mips/loongson64/loongson3-memset.S | 206 ++++++++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 arch/mips/loongson64/loongson3-memset.S diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index ba083beb6ca818..ed7e550913fe8e 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ constant_timer.o \ + loongson3-memset.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/loongson3-memset.S b/arch/mips/loongson64/loongson3-memset.S new file mode 100644 index 00000000000000..227b2d67082e30 --- /dev/null +++ b/arch/mips/loongson64/loongson3-memset.S @@ -0,0 +1,206 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1998, 1999, 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2007 by Maciej W. Rozycki + * Copyright (C) 2011, 2012 MIPS Technologies, Inc. + */ +#include +#include +#include +#include + +#define LONG_S_L sdl +#define LONG_S_R sdr + +#define STORSIZE 16 +#define STORMASK 15 + +#define EX(insn,reg,addr,handler) \ +9: insn reg, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + +#define EX_GSSQ(reg, addr, handler) \ + .set push; \ + .set arch=loongson3a; \ +9: gssq reg, reg, addr; \ + .set pop; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + + .macro f_fill128 dst, offset, val, fixup + EX_GSSQ(\val, (\offset + 0 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 1 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 2 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 3 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 4 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 5 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 6 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 7 * STORSIZE)(\dst), \fixup) + .endm + +/* + * memset(void *s, int c, size_t n) + * + * a0: start of area to clear + * a1: char to fill with + * a2: size of area to clear + */ + .set noreorder + .align 5 +LEAF(memset) +EXPORT_SYMBOL(memset) + beqz a1, 1f + move v0, a0 /* result */ + + andi a1, 0xff /* spread fillword */ + LONG_SLL t1, a1, 8 + or a1, t1 + LONG_SLL t1, a1, 16 + or a1, t1 + LONG_SLL t1, a1, 32 + or a1, t1 +1: + +FEXPORT(__bzero) +EXPORT_SYMBOL(__bzero) + sltiu t0, a2, STORSIZE /* very small region? */ + bnez t0, .Lsmall_memset + andi t0, a0, STORMASK /* aligned? */ + + .set noat + li AT, STORSIZE + beqz t0, 1f + PTR_SUBU t0, AT /* alignment in bytes */ + .set at + + EX(LONG_S_R, a1, (a0), .Lfirst_fixup) /* make word/dword 8B aligned */ + .set push + .set arch=mips64r2 + PTR_ADDIU t1, a0, 8 + dins t1, zero, 0, 3 + .set pop + EX(LONG_S, a1, (t1), .Lsecond_fixup) /* May double copy 8B */ + + PTR_SUBU a0, t0 /* long align ptr */ + PTR_ADDU a2, t0 /* correct size */ + +1: ori t1, a2, 0x7f /* # of full blocks */ + xori t1, 0x7f + beqz t1, .Lmemset_partial /* no block to fill */ + andi t0, a2, 0x80-STORSIZE + + PTR_ADDU t1, a0 /* end address */ + .set reorder +1: PTR_ADDIU a0, 128 + f_fill128 a0, -128, a1, .Lfwd_fixup + bne t1, a0, 1b + .set noreorder + +.Lmemset_partial: + PTR_LA t1, 2f /* where to start */ + .set noat + LONG_SRL AT, t0, 2 + PTR_SUBU t1, AT + .set at + jr t1 + PTR_ADDU a0, t0 /* dest ptr */ + + .set push + .set noreorder + .set nomacro + f_fill128 a0, -128, a1, .Lpartial_fixup /* ... but first do 16Bs ... */ +2: .set pop + andi a2, STORMASK /* At most 15B to go */ + + beqz a2, 1f + PTR_ADDU a0, a2 /* What's left */ + .set push + .set arch=mips64r2 + PTR_ADDI t1, a0, -8 + dins t1, zero, 0, 3 + .set pop + EX(LONG_S, a1, (t1), .Lnotlast_fixup) /* May double copy 8B */ + EX(LONG_S_L, a1, -1(a0), .Llast_fixup) +1: jr ra + move a2, zero + +.Lsmall_memset: + andi t1, a2, 7 + beq t1, a2, 1f + LONG_SLL t1, 2 + + EX(LONG_S_R, a1, (a0), .Lfirst_fixup) + EX(LONG_S_L, a1, 7(a0), .Lsmall_memset_fixup) + +1: PTR_LA t0, 2f + PTR_SUBU t1, t0, t1 + jr t1 + PTR_ADDU a0, a2 + + EX(sb, a1, -7(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -6(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -5(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -4(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -3(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -2(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -1(a0), .Lsmall_memset_partial_fixup) + +2: jr ra /* done */ + move a2, zero + END(memset) + +.Lsmall_memset_fixup: + PTR_ADDIU t0, a0, 8 + .set push + .set arch=mips64r2 + dins t0, zero, 0, 3 + .set pop + LONG_ADDU a2, a0 + jr ra + LONG_SUBU a2, t0 + +.Lsmall_memset_partial_fixup: + PTR_L t0, TI_TASK($28) + LONG_L t0, THREAD_BUADDR(t0) + jr ra + LONG_SUBU a2, a0, t0 + +.Lfirst_fixup: + jr ra + nop + +.Lsecond_fixup: + LONG_ADDU a2, a0 + jr ra + LONG_SUBU a2, t1 + +.Lfwd_fixup: + PTR_L t0, TI_TASK($28) + andi a2, 0x7f + LONG_L t0, THREAD_BUADDR(t0) + LONG_ADDU a2, t1 + jr ra + LONG_SUBU a2, t0 + +.Lpartial_fixup: + PTR_L t0, TI_TASK($28) + andi a2, STORMASK + LONG_L t0, THREAD_BUADDR(t0) + LONG_ADDU a2, a0 + jr ra + LONG_SUBU a2, t0 + +.Llast_fixup: + jr ra + andi a2, 0x7 + +.Lnotlast_fixup: + jr ra + PTR_SUBU a2, a0, t1 From 87efd0a9936f9867afb66d664fce219b43b317f4 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 01:27:08 +0800 Subject: [PATCH 212/258] AOSCOS: MIPS: loongson3-memset: replace with Per commit 9259e15b3f27 ("mips: replace #include with for . Follow the commit and revise loongson3-memset.S accordingly. Fixes: "AOSCOS: memset optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memset.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/loongson3-memset.S b/arch/mips/loongson64/loongson3-memset.S index 227b2d67082e30..0a35c013f6df49 100644 --- a/arch/mips/loongson64/loongson3-memset.S +++ b/arch/mips/loongson64/loongson3-memset.S @@ -8,9 +8,9 @@ * Copyright (C) 2007 by Maciej W. Rozycki * Copyright (C) 2011, 2012 MIPS Technologies, Inc. */ +#include #include #include -#include #include #define LONG_S_L sdl From 0b6b91d336425d8ec83afab91a5fe8b62e2bfc1b Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 13:30:55 +0800 Subject: [PATCH 213/258] AOSCOS: MIPS: loongson3-memset: use PTR_WD to fix build Per commit fa62f39dc7e2 ("MIPS: Fix build error due to PTR used in more places"), `PTR' was renamed to `PTR_WD' to avoid definition conflicts. Follow this commit to fix build. Fixes: "AOSCOS: memset optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memset.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/loongson3-memset.S b/arch/mips/loongson64/loongson3-memset.S index 0a35c013f6df49..55c3b8d7bc458e 100644 --- a/arch/mips/loongson64/loongson3-memset.S +++ b/arch/mips/loongson64/loongson3-memset.S @@ -22,7 +22,7 @@ #define EX(insn,reg,addr,handler) \ 9: insn reg, addr; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous #define EX_GSSQ(reg, addr, handler) \ @@ -31,7 +31,7 @@ 9: gssq reg, reg, addr; \ .set pop; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous .macro f_fill128 dst, offset, val, fixup From a8b6ea412b22542018c70100bb0c98148d2d12b4 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 13:33:50 +0800 Subject: [PATCH 214/258] AOSCOS: MIPS: lib: exclude generic memset.o if CPU_LOONGSON64 is set In "AOSCOS: memset optimization for loongson-3", a Loongson-3-specific memset implementation was introduced, with its own set of definitions for `bzero' and `memset' routines. This resulted in multiple definitions for the aforementioned assembly routines, causing vmlinux to fail to link. Exclude generic memset.o if `CPU_LOONGSON64' is set to avoid this error. Fixes: "AOSCOS: memset optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/lib/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index 5d5b993cbc2bff..b5532bab9337bd 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -10,6 +10,7 @@ lib-y += bitops.o csum_partial.o delay.o memcpy.o memset.o \ obj-y += iomap_copy.o obj-$(CONFIG_PCI) += iomap-pci.o lib-$(CONFIG_GENERIC_CSUM) := $(filter-out csum_partial.o, $(lib-y)) +lib-$(CONFIG_CPU_LOONGSON64) := $(filter-out memset.o, $(lib-y)) obj-$(CONFIG_CPU_GENERIC_DUMP_TLB) += dump_tlb.o obj-$(CONFIG_CPU_R3000) += r3k_dump_tlb.o From af6be972e3c3f3fdad0eae7697561dd4e3680126 Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 215/258] AOSCOS: memcpy optimization for loongson-3 [Mingcong Bai: Resolved a minor merge conflict in arch/mips/loongson64/Makefile.] Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/Makefile | 2 +- arch/mips/loongson64/loongson3-memcpy.S | 506 ++++++++++++++++++++++++ 2 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 arch/mips/loongson64/loongson3-memcpy.S diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index ed7e550913fe8e..438828f554a259 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ constant_timer.o \ - loongson3-memset.o + loongson3-memset.o loongson3-memcpy.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S new file mode 100644 index 00000000000000..28b785363351c8 --- /dev/null +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -0,0 +1,506 @@ +/* + ============================================================================ + Name : memcpy.S + Author : Heiher + Chen Jie + Version : 20140307 + Copyright : GPLv2 + Description : The memcpy for Loongson 3. + ============================================================================ + */ + +#define LINUX_KERNEL +#ifndef LINUX_KERNEL +#include +#include + +#define EXC(inst_reg,addr,handler) \ + inst_reg, addr; +#define EXCQ(inst,reg1,reg2,addr,handler) \ + inst reg1, reg2, addr; + +#if _MIPS_SIM == _ABI64 +#define CONFIG_64BIT +#else +#define CONFIG_32BIT +#endif + +#define FEXPORT(symbol) + +#else /* LINUX_KERNEL */ + +#include +#include +#include +#include + +#define EXC(inst_reg,addr,handler) \ +9: inst_reg, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + +#define EXCQ(inst,reg1,reg2,addr,handler) \ +9: inst reg1, reg2, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + +#endif + +#define dst a0 +#define src a1 +#define len a2 +#define rem t8 + +/* + * 64bit ABI vs 32bit ABI + */ +#ifdef CONFIG_64BIT +#define ADDU daddu +#define ADDI daddi +#define SUBU dsubu +#define SLL dsll +#define SRL dsrl +#define PTR_LA dla + +#define LOAD ld + +/* + * As we are sharing code base with the mips32 tree (which use the o32 ABI + * register definitions). We need to redefine the register definitions from + * the n64 ABI register naming to the o32 ABI register naming. + */ +#undef t0 +#undef t1 +#undef t2 +#undef t3 +#define t0 $8 +#define t1 $9 +#define t2 $10 +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 + +#else + +#define ADDU addu +#define ADDI addi +#define SUBU subu +#define SLL sll +#define SRL srl +#define PTR_LA la + +#define LOAD lw +#endif /* CONFIG_64BIT */ + +#define LDFIRST ldr +#define LDREST ldl + +#define SDFIRST sdr +#define SDREST sdl + +#define LWFIRST lwr +#define LWREST lwl + +#define SWFIRST swr +#define SWREST swl + +/* void * memcpy (void *s1, const void *s2, size_t n); */ + .text + .align 5 + .set noreorder + .set noat + .set arch=loongson3a + +LEAF(memcpy) /* a0=dst a1=src a2=len */ +EXPORT_SYMBOL(memcpy) + move v0, dst +.L__memcpy: +FEXPORT(__copy_user) +EXPORT_SYMBOL(__copy_user) + /* if less then 0x28 bytes */ + sltu t2, a2, 0x28 + andi t0, dst, 0xf + bnez t2, .L_memcpy_less + andi t1, src, 0xf + + beqz t0, 1f + ADDI rem, t0, -0x10 + + /* upgrade */ +EXC( LDFIRST t3, 0(src), .Ll_exc) + sltu t4, t0, 0x8 +EXC( LDREST t3, 7(src), .Ll_exc_copy) + SUBU src, rem +EXC( SDFIRST t3, 0(dst), .Ls_exc) + SUBU dst, rem + beqz t4, 1f + ADDU len, rem +EXC( LDFIRST t3, -8(src), .Ll_exc_a8) +EXC( LDREST t3, -1(src), .Ll_exc_copy_a8) +EXC( sd t3, -8(dst), .Ls_exc_p8) + +1: andi t7, src, 0x7 + beq t0, t1, .L_memcpy_16_16 + nop + bnez t7, .L_memcpy_16_4_2_1 + +.L_memcpy_16_8: + SRL t0, len, 6 # 64B per iteration + beqz t0, 2f + and rem, len, 0x3f + .align 4 +1: +EXC( ld t4, (16 * 0)(src), .Ll_exc) +EXC( ld t7, (8 + 16 * 0)(src), .Ll_exc_copy) +EXC( ld t2, (16 * 1)(src), .Ll_exc_copy) +EXC( ld t3, (8 + 16 * 1)(src), .Ll_exc_copy) +EXC( ld t0, (16 * 2)(src), .Ll_exc_copy) +EXC( ld t1, (8 + 16 * 2)(src), .Ll_exc_copy) + ADDI len, -16 * 4 +EXCQ( gssq, t7, t4, (16 * 0)(dst), .Ls_exc_p64) +EXC( ld t4, (16 * 3)(src), .Ll_exc_copy) +EXC( ld t7, (8 + 16 * 3)(src), .Ll_exc_copy) + ADDU src, 16 * 4 + ADDU dst, 16 * 4 +EXCQ( gssq, t3, t2, (-16 * 3)(dst), .Ls_exc_p48) +EXCQ( gssq, t1, t0, (-16 * 2)(dst), .Ls_exc_p32) +EXCQ( gssq, t7, t4, (-16 * 1)(dst), .Ls_exc_p16) + bne len, rem, 1b + nop + beqz len, .Ldone +2: sltu t0, len, 32 + bnez t0, 3f + and rem, len, 0xf +EXC( ld t2, (16 * 0)(src), .Ll_exc) +EXC( ld t3, (8 + 16 * 0)(src), .Ll_exc_copy) +EXC( ld t0, (16 * 1)(src), .Ll_exc_copy) +EXC( ld t1, (8 + 16 * 1)(src), .Ll_exc_copy) + ADDI len, -16 * 2 + ADDU src, 16 * 2 +EXCQ( gssq, t3, t2, (16 * 0)(dst), .Ls_exc_p32) +EXCQ( gssq, t1, t0, (16 * 1)(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 32 +3: /* copy less than 32B */ + beq rem, len, .L_memcpy_1_15B_8B_aligned + nop +EXC( ld t2, 0(src), .Ll_exc) +EXC( ld t3, 8(src), .Ll_exc_copy) + ADDI len, -16 + ADDU src, 16 +EXCQ( gssq, t3, t2, 0(dst), .Ls_exc_p16) + bnez len, .L_memcpy_1_15B_8B_aligned + ADDU dst, 16 + + jr ra + nop + +.L_memcpy_16_16: + SRL t0, len, 6 # 64B per iteration + beqz t0, 2f + and rem, len, 0x3f + .align 4 +1: +EXCQ( gslq, t7, t4, (16 * 0)(src), .Ll_exc) +EXCQ( gslq, t3, t2, (16 * 1)(src), .Ll_exc_copy) +EXCQ( gslq, t1, t0, (16 * 2)(src), .Ll_exc_copy) + ADDI len, -16 * 4 +EXCQ( gssq, t7, t4, (16 * 0)(dst), .Ls_exc_p64) +EXCQ( gslq, t7, t4, (16 * 3)(src), .Ll_exc_copy) + ADDU src, 16 * 4 + ADDU dst, 16 * 4 +EXCQ( gssq, t3, t2, (-16 * 3)(dst), .Ls_exc_p48) +EXCQ( gssq, t1, t0, (-16 * 2)(dst), .Ls_exc_p32) +EXCQ( gssq, t7, t4, (-16 * 1)(dst), .Ls_exc_p16) + bne len, rem, 1b + nop + beqz len, .Ldone +2: sltu t0, len, 32 + bnez t0, 3f + and rem, len, 0xf +EXCQ( gslq, t3, t2, (16 * 0)(src), .Ll_exc) +EXCQ( gslq, t1, t0, (16 * 1)(src), .Ll_exc_copy) + ADDI len, -16 * 2 + ADDU src, 32 +EXCQ( gssq, t3, t2, (16 * 0)(dst), .Ls_exc_p32) +EXCQ( gssq, t1, t0, (16 * 1)(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 32 +3: /* copy less than 32B */ + beq rem, len, .L_memcpy_1_15B_8B_aligned + nop +EXCQ( gslq, t3, t2, 0(src), .Ll_exc) + ADDI len, -16 + ADDU src, 16 +EXCQ( gssq, t3, t2, 0(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 16 +/* + * copy 1 - 15B, src & dst are 8B aligned + */ +.L_memcpy_1_15B_8B_aligned: + sltu t0, len, 0x9 + bnez t0, 1f + nop +EXC( ld t1, (src), .Ll_exc) +EXC( sd t1, (dst), .Ls_exc) +1: ADDU src, len + ADDU dst, len +EXC( LDREST t1, -1(src), .Ll_exc_copy_len) +EXC( SDREST t1, -1(dst), .Ls_exc) +.Ldone: + jr ra + move len, zero + +.L_memcpy_16_4_2_1: + SRL t0, len, 5 # 32B per iteration + beqz t0, 2f + and rem, len, 0x1f +1: +EXC( LDFIRST t4, 0(src), .Ll_exc) +EXC( LDFIRST t7, 8(src), .Ll_exc_copy) + ADDI len, -16 * 2 +EXC( LDREST t4, 7(src), .Ll_exc_copy) +EXC( LDREST t7, 15(src), .Ll_exc_copy) +EXC( LDFIRST t2, 16(src), .Ll_exc_copy) +EXC( LDFIRST t3, 24(src), .Ll_exc_copy) +EXC( LDREST t2, 23(src), .Ll_exc_copy) +EXC( LDREST t3, 31(src), .Ll_exc_copy) + ADDU src, 16 * 2 +EXCQ( gssq, t7, t4, (16 * 0)(dst), .Ls_exc_p32) +EXCQ( gssq, t3, t2, (16 * 1)(dst), .Ls_exc_p16) + bne len, rem, 1b + ADDU dst, 16 * 2 + beqz len, .Ldone +2: and rem, len, 0xf + beq rem, len, .L_memcpy_less + nop +EXC( LDFIRST t0, 0(src), .Ll_exc) +EXC( LDFIRST t1, 8(src), .Ll_exc_copy) + ADDI len, -16 +EXC( LDREST t0, 7(src), .Ll_exc_copy) +EXC( LDREST t1, 15(src), .Ll_exc_copy) + ADDU src, 16 +EXCQ( gssq, t1, t0, 0(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 16 + +.L_memcpy_less: + andi t0, len, 0x7 + beq t0, len, 2f + andi t4, len, 0x3 + + .set reorder + SUBU t1, len, t0 + ADDU dst, t1 + ADDU src, t1 + .set at=t2 + PTR_LA t3, 1f + .set noat + SLL t2, t1, 0x1 /* 4 * 4B instructions move 8B data*/ + SUBU t3, t2 + jr t3 + .set noreorder + +EXC( LDFIRST t2, (-8 * 6)(src), .Ll_exc_a48) +EXC( LDREST t2, (-8 * 6 + 7)(src), .Ll_exc_copy_a48) +EXC( SDFIRST t2, (-8 * 6)(dst), .Ls_exc) +EXC( SDREST t2, (-8 * 6 + 7)(dst), .Ls_exc_a8) + +EXC( LDFIRST t3, (-8 * 5)(src), .Ll_exc_a40) +EXC( LDREST t3, (-8 * 5 + 7)(src), .Ll_exc_copy_a40) +EXC( SDFIRST t3, (-8 * 5)(dst), .Ls_exc_a8) +EXC( SDREST t3, (-8 * 5 + 7)(dst), .Ls_exc_a16) + +EXC( LDFIRST t1, (-8 * 4)(src), .Ll_exc_a32) +EXC( LDREST t1, (-8 * 4 + 7)(src), .Ll_exc_copy_a32) +EXC( SDFIRST t1, (-8 * 4)(dst), .Ls_exc_a16) +EXC( SDREST t1, (-8 * 4 + 7)(dst), .Ls_exc_a24) + +EXC( LDFIRST t2, (-8 * 3)(src), .Ll_exc_a24) +EXC( LDREST t2, (-8 * 3 + 7)(src), .Ll_exc_copy_a24) +EXC( SDFIRST t2, (-8 * 3)(dst), .Ls_exc_a24) +EXC( SDREST t2, (-8 * 3 + 7)(dst), .Ls_exc_a32) + +EXC( LDFIRST t3, (-8 * 2)(src), .Ll_exc_a16) +EXC( LDREST t3, (-8 * 2 + 7)(src), .Ll_exc_copy_a16) +EXC( SDFIRST t3, (-8 * 2)(dst), .Ls_exc_a32) +EXC( SDREST t3, (-8 * 2 + 7)(dst), .Ls_exc_a40) + +EXC( LDFIRST t1, (-8 * 1)(src), .Ll_exc_a8) +EXC( LDREST t1, (-8 * 1 + 7)(src), .Ll_exc_copy_a8) +EXC( SDFIRST t1, (-8 * 1)(dst), .Ls_exc_a40) +EXC( SDREST t1, (-8 * 1 + 7)(dst), .Ls_exc_a48) +1: beqz t0, .Ldone + ADDU src, t0 + ADDU dst, t0 +EXC( LDFIRST t2, -8(src), .Ll_exc_a8) +EXC( LDREST t2, -1(src), .Ll_exc_copy_a8) +EXC( SDFIRST t2, -8(dst), .Ls_exc) +EXC( SDREST t2, -1(dst), .Ls_exc) + jr ra + move len, zero + +2: + beq t4, len, 3f + nop +EXC( LWFIRST t2, (src), .Ll_exc) +EXC( LWREST t2, 3(src), .Ll_exc_copy) + ADDU src, len +EXC( SWFIRST t2, (dst), .Ls_exc) +EXC( SWREST t2, 3(dst), .Ls_exc) + beqz t4, .Ldone + ADDU dst, len + +EXC( LWFIRST t1, -4(src), .Ll_exc_a4) +EXC( LWREST t1, -1(src), .Ll_exc_copy_a4) +EXC( SWFIRST t1, -4(dst), .Ls_exc) +EXC( SWREST t1, -1(dst), .Ls_exc) + jr ra + move len, zero + +3: + beqz len, .Ldone + ADDU t0, src, len +1: +EXC( lb t2, (src), .Ll_exc) + ADDU src, 1 +EXC( sb t2, (dst), .Ls_exc_p1) + bne t0, src, 1b + ADDU dst, 1 + + jr ra + move len, zero + + END(memcpy) + +#ifdef LINUX_KERNEL + +#define LEXC_a(n) \ +.Ll_exc_copy_a ## n: \ + ADDI src, -n; \ + b .Ll_exc_copy; \ + ADDI dst, -n; \ +.Ll_exc_a ## n: \ + ADDI src, -n; \ + b .Ll_exc; \ + ADDI dst, -n; + +.Ll_exc_copy_len: + SUBU src, len + b .Ll_exc_copy + SUBU dst, len + +LEXC_a(4) +LEXC_a(8) +LEXC_a(16) +LEXC_a(24) +LEXC_a(32) +LEXC_a(40) +LEXC_a(48) + +.Ll_exc_copy: + /* + * Copy bytes from src until faulting load address (or until a + * lb faults) + * + * When reached by a faulting LDFIRST/LDREST, THREAD_BUADDR($28) + * may be more than a byte beyond the last address. + * Hence, the lb below may get an exception. + * + * Assumes src < THREAD_BUADDR($28) + */ + LOAD t0, TI_TASK($28) + nop + LOAD t0, THREAD_BUADDR(t0) +1: +EXC( lb t1, 0(src), .Ll_exc) + ADDU src, 1 + sb t1, 0(dst) # can't fault -- we're copy_from_user + bne src, t0, 1b + ADDU dst, 1 +.Ll_exc: + LOAD t0, TI_TASK($28) + nop + LOAD t0, THREAD_BUADDR(t0) # t0 is just past last good address + nop + SUBU len, AT, t0 # len number of uncopied bytes + jr ra + nop + +#define SEXC_p(n) \ +.Ls_exc_p ## n: \ + jr ra; \ + ADDU len, n; + +#define SEXC_a(n) \ +.Ls_exc_a ## n: \ + jr ra; \ + ADDI len, -n; + +SEXC_p(1) +SEXC_p(8) +SEXC_p(16) +SEXC_p(32) +SEXC_p(48) +SEXC_p(64) +SEXC_a(48) +SEXC_a(40) +SEXC_a(32) +SEXC_a(24) +SEXC_a(16) +SEXC_a(8) + +.Ls_exc: + jr ra + nop + + .align 5 +LEAF(memmove) +EXPORT_SYMBOL(memmove) + ADDU t0, a0, a2 + ADDU t1, a1, a2 + sltu t0, a1, t0 # dst + len <= src -> memcpy + sltu t1, a0, t1 # dst >= src + len -> memcpy + and t0, t1 + beqz t0, .L__memcpy + move v0, a0 /* return value */ + beqz a2, .Lr_out + END(memmove) + + /* fall through to __rmemcpy */ +LEAF(__rmemcpy) /* a0=dst a1=src a2=len */ + sltu t0, a1, a0 + beqz t0, .Lr_end_bytes_up # src >= dst + nop + ADDU a0, a2 # dst = dst + len + ADDU a1, a2 # src = src + len + +.Lr_end_bytes: + lb t0, -1(a1) + ADDI a2, -0x1 + sb t0, -1(a0) + ADDI a1, -0x1 + bnez a2, .Lr_end_bytes + ADDI a0, -0x1 + +.Lr_out: + jr ra + move a2, zero + +.Lr_end_bytes_up: + lb t0, (a1) + ADDI a2, -0x1 + sb t0, (a0) + ADDU a1, 0x1 + bnez a2, .Lr_end_bytes_up + ADDU a0, 0x1 + + jr ra + move a2, zero + END(__rmemcpy) +#endif From fd5dec5027a6dbd826414447646273d42502de1c Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:03:01 +0800 Subject: [PATCH 216/258] AOSCOS: MIPS: loongson3-memcpy: use PTR_WD to fix build Per commit fa62f39dc7e2 ("MIPS: Fix build error due to PTR used in more places"), `PTR' was renamed to `PTR_WD' to avoid definition conflicts. Follow this commit to fix build. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memcpy.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S index 28b785363351c8..4c0b6b8474f043 100644 --- a/arch/mips/loongson64/loongson3-memcpy.S +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -37,13 +37,13 @@ #define EXC(inst_reg,addr,handler) \ 9: inst_reg, addr; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous #define EXCQ(inst,reg1,reg2,addr,handler) \ 9: inst reg1, reg2, addr; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous #endif From d30d38da24bd406e4d79577d2160404516f135ab Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:04:20 +0800 Subject: [PATCH 217/258] AOSCOS: MIPS: loongson3-memcpy: replace with Per commit 9259e15b3f27 ("mips: replace #include with for . Follow the commit and revise loongson3-memcpy.S accordingly. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memcpy.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S index 4c0b6b8474f043..2021e75d5dd2e6 100644 --- a/arch/mips/loongson64/loongson3-memcpy.S +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -29,9 +29,9 @@ #else /* LINUX_KERNEL */ +#include #include #include -#include #include #define EXC(inst_reg,addr,handler) \ From d4f84a8d3a42337bc155858ea1c90871d9d62253 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:09:34 +0800 Subject: [PATCH 218/258] AOSCOS: MIPS: mark CPU_LOONGSON64 as HAVE_PLAT_MEMCPY loongson3-memcpy is a platform-specific memcpy implementation, add HAVE_PLAT_MEMCPY under CPU_LOONGSON64. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 93fc1dba4faf2d..6aa299bd8cb319 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1386,6 +1386,7 @@ config CPU_LOONGSON64 select MIPS_FP_SUPPORT select GPIOLIB select SWIOTLB + select HAVE_PLAT_MEMCPY help The Loongson GSx64(GS264/GS464/GS464E/GS464V) series of processor cores implements the MIPS64R2 instruction set with many extensions, From 7371d82a5119cf42e3d2ce9eadeb76ffe2ab829f Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:10:57 +0800 Subject: [PATCH 219/258] AOSCOS: MIPS: loongson3-memcpy: adapt to RAW_COPY_USER Since commit 2260ea86c0e7 ("mips: switch to RAW_COPY_USER"), it is required to export `__raw_copy_from_user' and `__raw_copy_from_user', which are then referenced by certain kernel modules. Export these symbols under `__memcpy', also remove the export for the now- unused `__copy_user' symbol. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/loongson64/loongson3-memcpy.S | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S index 2021e75d5dd2e6..c5dd2e2866c3c7 100644 --- a/arch/mips/loongson64/loongson3-memcpy.S +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -119,8 +119,10 @@ LEAF(memcpy) /* a0=dst a1=src a2=len */ EXPORT_SYMBOL(memcpy) move v0, dst .L__memcpy: -FEXPORT(__copy_user) -EXPORT_SYMBOL(__copy_user) +FEXPORT(__raw_copy_from_user) +EXPORT_SYMBOL(__raw_copy_from_user) +FEXPORT(__raw_copy_to_user) +EXPORT_SYMBOL(__raw_copy_to_user) /* if less then 0x28 bytes */ sltu t2, a2, 0x28 andi t0, dst, 0xf From fc925f847ed9ced70b1544222001559d85a47d80 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:45:22 +0800 Subject: [PATCH 220/258] AOSCOS: spi: spi-loongson: allow building on MACH_LOONGSON32/64 LS2K/LS7A chipsets are also found on some MIPS-based Loongson hardware, allow building for 32-bit (2K) and 64-bit (3A/B) hardware. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/spi/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8782514bb89b0f..800fafb938d5e2 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -592,12 +592,12 @@ config SPI_LM70_LLP config SPI_LOONGSON_CORE tristate - depends on LOONGARCH || COMPILE_TEST + depends on LOONGARCH || MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST config SPI_LOONGSON_PCI tristate "Loongson SPI Controller PCI Driver Support" select SPI_LOONGSON_CORE - depends on PCI && (LOONGARCH || COMPILE_TEST) + depends on PCI && (LOONGARCH || MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST) help This bus driver supports the Loongson SPI hardware controller in the Loongson platforms and supports to use PCI framework to @@ -608,7 +608,7 @@ config SPI_LOONGSON_PCI config SPI_LOONGSON_PLATFORM tristate "Loongson SPI Controller Platform Driver Support" select SPI_LOONGSON_CORE - depends on OF && (LOONGARCH || COMPILE_TEST) + depends on OF && (LOONGARCH || MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST) help This bus driver supports the Loongson SPI hardware controller in the Loongson platforms and supports to use DTS framework to From ff27b956ae9e709a6b18baab282c903e389ca00d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 9 Dec 2024 10:35:39 +0800 Subject: [PATCH 221/258] AOSCOS: mvsas: Optimise performance and stability on CPU_LOONGSON64 Optimize this driver conditionally for MIPS-based Loongson 3 (CPU_LOONGSON64): - Reduce queue depth of SAS disks for MIPS-based Loongson 3. - Reduce granularity and timeout value of interrupt coalescing for MIPS- based Loongson 3. Also include an optimisation: - Use bidirectional DMA for SMP request/response. This is a partial patch from the Lemote v5.4 tree, "mvsas: Optimise performance and stability", stripping out the part about "Use cyclic searching to alloc command slot" as SCSI command tag allocation for this driver was extensively revised with commit 2acf97f199f9 ("scsi: mvsas: Use sas_task_find_rq() for tagging"). Not sure if this optimization would still be worthwhile to port over. Also, since CPU_LOONGSON64 is also defined for LoongArch processors, add additionally an ifdef condition for CONFIG_MIPS to ensure that quirk code only applied to MIPS && CPU_LOONGSON64 targets. As a side note, `sas_slave_configure()' was renamed to `sas_device_configure()' in commit a25a9c85d17f ("scsi: libata: Switch to using ->device_configure"), so I revised the invocations and function namings from the original patch accordingly (`mvs_slave_configure()' => `mvs_device_configure()'). This commit also introduces and additional argument for queue limits to the `sas_device_configure()' function, so revise accordingly. Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- drivers/scsi/mvsas/mv_64xx.c | 8 ++++++++ drivers/scsi/mvsas/mv_94xx.c | 8 ++++++++ drivers/scsi/mvsas/mv_init.c | 5 +++++ drivers/scsi/mvsas/mv_sas.c | 26 ++++++++++++++++++++------ drivers/scsi/mvsas/mv_sas.h | 1 + 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/mvsas/mv_64xx.c b/drivers/scsi/mvsas/mv_64xx.c index 1f2b61de8c6326..855d8465b30f24 100644 --- a/drivers/scsi/mvsas/mv_64xx.c +++ b/drivers/scsi/mvsas/mv_64xx.c @@ -376,10 +376,14 @@ static int mvs_64xx_init(struct mvs_info *mvi) * it will make count 0. */ tmp = 0; +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + mw32(MVS_INT_COAL, (ATA_MAX_QUEUE - 1) | COAL_EN); +#else if (MVS_CHIP_SLOT_SZ > 0x1ff) mw32(MVS_INT_COAL, 0x1ff | COAL_EN); else mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN); +#endif tmp = 0x10000 | interrupt_coalescing; mw32(MVS_INT_COAL_TMOUT, tmp); @@ -748,10 +752,14 @@ static void mvs_64xx_tune_interrupt(struct mvs_info *mvi, u32 time) mw32(MVS_INT_COAL, 0); mw32(MVS_INT_COAL_TMOUT, 0x10000); } else { +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + mw32(MVS_INT_COAL, (ATA_MAX_QUEUE - 1) | COAL_EN); +#else if (MVS_CHIP_SLOT_SZ > 0x1ff) mw32(MVS_INT_COAL, 0x1ff|COAL_EN); else mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN); +#endif tmp = 0x10000 | time; mw32(MVS_INT_COAL_TMOUT, tmp); diff --git a/drivers/scsi/mvsas/mv_94xx.c b/drivers/scsi/mvsas/mv_94xx.c index fc0b8eb682045f..1d1042ab82c8c0 100644 --- a/drivers/scsi/mvsas/mv_94xx.c +++ b/drivers/scsi/mvsas/mv_94xx.c @@ -515,10 +515,14 @@ static int mvs_94xx_init(struct mvs_info *mvi) * it will make count 0. */ tmp = 0; +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + mw32(MVS_INT_COAL, (ATA_MAX_QUEUE - 1) | COAL_EN); +#else if (MVS_CHIP_SLOT_SZ > 0x1ff) mw32(MVS_INT_COAL, 0x1ff | COAL_EN); else mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ | COAL_EN); +#endif /* default interrupt coalescing time is 128us */ tmp = 0x10000 | interrupt_coalescing; @@ -1027,10 +1031,14 @@ static void mvs_94xx_tune_interrupt(struct mvs_info *mvi, u32 time) mw32(MVS_INT_COAL, 0); mw32(MVS_INT_COAL_TMOUT, 0x10000); } else { +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + mw32(MVS_INT_COAL, (ATA_MAX_QUEUE - 1) | COAL_EN); +#else if (MVS_CHIP_SLOT_SZ > 0x1ff) mw32(MVS_INT_COAL, 0x1ff|COAL_EN); else mw32(MVS_INT_COAL, MVS_CHIP_SLOT_SZ|COAL_EN); +#endif tmp = 0x10000 | time; mw32(MVS_INT_COAL_TMOUT, tmp); diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index 5abc17a2e26109..b73ee560939265 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -10,7 +10,11 @@ #include "mv_sas.h" +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) +int interrupt_coalescing = 0x08; +#else int interrupt_coalescing = 0x80; +#endif static struct scsi_transport_template *mvs_stt; static const struct mvs_chip_info mvs_chips[] = { @@ -32,6 +36,7 @@ static const struct attribute_group *mvst_sdev_groups[]; static const struct scsi_host_template mvs_sht = { LIBSAS_SHT_BASE + .device_configure = mvs_device_configure, .scan_finished = mvs_scan_finished, .scan_start = mvs_scan_start, .can_queue = 1, diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index 359226e80eae82..d8fbbfd07a1d7a 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -255,6 +255,20 @@ static void mvs_bytes_dmaed(struct mvs_info *mvi, int i, gfp_t gfp_flags) sas_notify_port_event(sas_phy, PORTE_BYTES_DMAED, gfp_flags); } +int mvs_device_configure(struct scsi_device *sdev, struct queue_limits *lim) +{ + struct domain_device *dev = sdev_to_domain_dev(sdev); + + sas_device_configure(sdev, lim); + +#if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + if (!dev_is_sata(dev)) + scsi_change_queue_depth(sdev, MVS_QUEUE_SIZE); +#endif + + return 0; +} + void mvs_scan_start(struct Scsi_Host *shost) { int i, j; @@ -309,13 +323,13 @@ static int mvs_task_prep_smp(struct mvs_info *mvi, * DMA-map SMP request, response buffers */ sg_req = &task->smp_task.smp_req; - elem = dma_map_sg(mvi->dev, sg_req, 1, DMA_TO_DEVICE); + elem = dma_map_sg(mvi->dev, sg_req, 1, DMA_BIDIRECTIONAL); if (!elem) return -ENOMEM; req_len = sg_dma_len(sg_req); sg_resp = &task->smp_task.smp_resp; - elem = dma_map_sg(mvi->dev, sg_resp, 1, DMA_FROM_DEVICE); + elem = dma_map_sg(mvi->dev, sg_resp, 1, DMA_BIDIRECTIONAL); if (!elem) { rc = -ENOMEM; goto err_out; @@ -389,10 +403,10 @@ static int mvs_task_prep_smp(struct mvs_info *mvi, err_out_2: dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_resp, 1, - DMA_FROM_DEVICE); + DMA_BIDIRECTIONAL); err_out: dma_unmap_sg(mvi->dev, &tei->task->smp_task.smp_req, 1, - DMA_TO_DEVICE); + DMA_BIDIRECTIONAL); return rc; } @@ -869,9 +883,9 @@ static void mvs_slot_task_free(struct mvs_info *mvi, struct sas_task *task, switch (task->task_proto) { case SAS_PROTOCOL_SMP: dma_unmap_sg(mvi->dev, &task->smp_task.smp_resp, 1, - DMA_FROM_DEVICE); + DMA_BIDIRECTIONAL); dma_unmap_sg(mvi->dev, &task->smp_task.smp_req, 1, - DMA_TO_DEVICE); + DMA_BIDIRECTIONAL); break; case SAS_PROTOCOL_SATA: diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index 09ce3f2241f240..b6f5b739d67c93 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -429,6 +429,7 @@ int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, void *funcdata); void mvs_set_sas_addr(struct mvs_info *mvi, int port_id, u32 off_lo, u32 off_hi, u64 sas_addr); +int mvs_device_configure(struct scsi_device *sdev, struct queue_limits *lim); void mvs_scan_start(struct Scsi_Host *shost); int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time); int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags); From 095516dda77ff95e81e0c4f37b2cea05eeb52f59 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 4 Feb 2025 12:31:07 +0800 Subject: [PATCH 222/258] AOSCOS: mvsas: Rename .device_configure() into .sdev_configure() Following upstream name changes; Also move the struct domain_device inside the ifdef to avoid unused variables. Fixes: 47c2e30afcec ("scsi: Rename .device_configure() into .sdev_configure()") Fixes: "AOSCOS: mvsas: Optimise performance and stability on CPU_LOONGSON64" Signed-off-by: Kexy Biscuit Signed-off-by: Xinhui Yang Signed-off-by: Mingcong Bai --- drivers/scsi/mvsas/mv_init.c | 2 +- drivers/scsi/mvsas/mv_sas.c | 8 ++++---- drivers/scsi/mvsas/mv_sas.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c index b73ee560939265..642ec7f713ac90 100644 --- a/drivers/scsi/mvsas/mv_init.c +++ b/drivers/scsi/mvsas/mv_init.c @@ -36,7 +36,7 @@ static const struct attribute_group *mvst_sdev_groups[]; static const struct scsi_host_template mvs_sht = { LIBSAS_SHT_BASE - .device_configure = mvs_device_configure, + .sdev_configure = mvs_sdev_configure, .scan_finished = mvs_scan_finished, .scan_start = mvs_scan_start, .can_queue = 1, diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c index d8fbbfd07a1d7a..7b4c113e8a41cf 100644 --- a/drivers/scsi/mvsas/mv_sas.c +++ b/drivers/scsi/mvsas/mv_sas.c @@ -255,13 +255,13 @@ static void mvs_bytes_dmaed(struct mvs_info *mvi, int i, gfp_t gfp_flags) sas_notify_port_event(sas_phy, PORTE_BYTES_DMAED, gfp_flags); } -int mvs_device_configure(struct scsi_device *sdev, struct queue_limits *lim) +int mvs_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim) { - struct domain_device *dev = sdev_to_domain_dev(sdev); - - sas_device_configure(sdev, lim); + sas_sdev_configure(sdev, lim); #if defined(CONFIG_MIPS) && defined(CONFIG_CPU_LOONGSON64) + struct domain_device *dev = sdev_to_domain_dev(sdev); + if (!dev_is_sata(dev)) scsi_change_queue_depth(sdev, MVS_QUEUE_SIZE); #endif diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index b6f5b739d67c93..6d7831bd9c0148 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -429,7 +429,7 @@ int mvs_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func, void *funcdata); void mvs_set_sas_addr(struct mvs_info *mvi, int port_id, u32 off_lo, u32 off_hi, u64 sas_addr); -int mvs_device_configure(struct scsi_device *sdev, struct queue_limits *lim); +int mvs_sdev_configure(struct scsi_device *sdev, struct queue_limits *lim); void mvs_scan_start(struct Scsi_Host *shost); int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time); int mvs_queue_command(struct sas_task *task, gfp_t gfp_flags); From 48f4efc35b21d71fc7b37dfd29edcb233c6a877b Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Tue, 25 Feb 2025 17:45:08 +0800 Subject: [PATCH 223/258] MARKER: AOSCOS: End of Lemote patches Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 6aa299bd8cb319..4de8bafd90fbd7 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -550,7 +550,6 @@ config MACH_LOONGSON64 select USE_OF select BUILTIN_DTB select PCI_HOST_GENERIC - depends on BROKEN help This enables the support of Loongson-2/3 family of machines. From 078bce8f5fecfee77a32f91ea79746ddaae580f4 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sat, 16 Aug 2025 09:50:44 +0800 Subject: [PATCH 224/258] AOSCOS: ACPI / scan: Add pwm_lookup_entry for PWM3 on LS7A On LoongArch laptops using LG110 GPU for display, LS7A PWM3 is used for backlight brightness control. Thus we need to call pwm_add_table() so that the loonggpu driver can find the PWM for controlling the backlight. Signed-off-by: Xi Ruoyao Signed-off-by: Mingcong Bai --- drivers/acpi/Makefile | 1 + drivers/acpi/internal.h | 6 +++ drivers/acpi/loongarch/Makefile | 3 ++ drivers/acpi/loongarch/acpi-ls7a-backlight.c | 42 ++++++++++++++++++++ drivers/acpi/scan.c | 1 + 5 files changed, 53 insertions(+) create mode 100644 drivers/acpi/loongarch/Makefile create mode 100644 drivers/acpi/loongarch/acpi-ls7a-backlight.c diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index d1b0affb844f05..2148e6a5ebccae 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -134,3 +134,4 @@ obj-$(CONFIG_ACPI_VIOT) += viot.o obj-$(CONFIG_RISCV) += riscv/ obj-$(CONFIG_X86) += x86/ +obj-$(CONFIG_LOONGARCH) += loongarch/ diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 40f875b265a942..c53b0c4777a645 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -75,6 +75,12 @@ void acpi_lpss_init(void); static inline void acpi_lpss_init(void) {} #endif +#if IS_ENABLED(CONFIG_PWM_LOONGSON) +void acpi_ls7a_pwm_init(void); +#else +static inline void acpi_ls7a_pwm_init(void) {} +#endif + void acpi_apd_init(void); acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src); diff --git a/drivers/acpi/loongarch/Makefile b/drivers/acpi/loongarch/Makefile new file mode 100644 index 00000000000000..25f207f841222c --- /dev/null +++ b/drivers/acpi/loongarch/Makefile @@ -0,0 +1,3 @@ +ifdef CONFIG_PWM_LOONGSON +obj-y += acpi-ls7a-backlight.o +endif diff --git a/drivers/acpi/loongarch/acpi-ls7a-backlight.c b/drivers/acpi/loongarch/acpi-ls7a-backlight.c new file mode 100644 index 00000000000000..5363747cd3a7f0 --- /dev/null +++ b/drivers/acpi/loongarch/acpi-ls7a-backlight.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025 Xi Ruoyao + * + * ACPI support for devices using LG110 discrete graphics + * with LS7A PWM controlling the backlight. + */ + +#include +#include + +#include "../internal.h" + +static struct pwm_lookup ls7a_pwm_lookup[] = { + PWM_LOOKUP_WITH_MODULE("LOON0006:03", 0, NULL, "gsgpu_backlight", 0, + PWM_POLARITY_NORMAL, "pwm-loongson"), +}; + +static int acpi_ls7a_pwm_attach(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + if (acpi_dev_uid_match(adev, 3)) + pwm_add_table(ls7a_pwm_lookup, ARRAY_SIZE(ls7a_pwm_lookup)); + + return 0; +} + +static const struct acpi_device_id acpi_ls7a_pwm_ids[] = { + { "LOON0006", 0 }, + { }, +}; + +static struct acpi_scan_handler ls7a_pwm_handler = { + .ids = acpi_ls7a_pwm_ids, + .attach = acpi_ls7a_pwm_attach, +}; + + +void __init acpi_ls7a_pwm_init(void) +{ + acpi_scan_add_handler(&ls7a_pwm_handler); +} diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index de8df157be0bcd..d810aa6137440f 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2839,6 +2839,7 @@ void __init acpi_scan_init(void) acpi_pnp_init(); acpi_power_resources_init(); acpi_init_lpit(); + acpi_ls7a_pwm_init(); acpi_scan_add_handler(&generic_device_handler); From 5c355e885eaf5725361a4858c4a044ff6ea12b1c Mon Sep 17 00:00:00 2001 From: Xinhui Yang Date: Wed, 10 Sep 2025 23:21:26 +0800 Subject: [PATCH 225/258] AOSCOS: scsi: dc395x: correctly discard the return value in certain reads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are certain read operations performed in this code which doesn't really don't need its return value. Those read operations either clears the FIFO buffer, or clears the interruption status. However, unused read triggers compiler warnings. With CONFIG_WERROR on, these warnings get converted into errors: drivers/scsi/dc395x.c: In function ‘__dc395x_eh_bus_reset’: drivers/scsi/dc395x.c:97:49: error: value computed is not used [-Werror=unused-value] 97 | #define DC395x_read8(acb,address) (u8)(inb(acb->io_port_base + (address))) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/scsi/dc395x.c:1003:9: note: in expansion of macro ‘DC395x_read8’ 1003 | DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS); | ^~~~~~~~~~~~ drivers/scsi/dc395x.c: In function ‘data_io_transfer’: drivers/scsi/dc395x.c:97:49: error: value computed is not used [-Werror=unused-value] 97 | #define DC395x_read8(acb,address) (u8)(inb(acb->io_port_base + (address))) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/scsi/dc395x.c:2032:33: note: in expansion of macro ‘DC395x_read8’ 2032 | DC395x_read8(acb, TRM_S1040_SCSI_FIFO); Create a new macro DC395x_peek8() to deliberately cast the return value to void, which tells the compiler we really don't need the return value of such read operations. Signed-off-by: Xinhui Yang Signed-off-by: Mingcong Bai --- drivers/scsi/dc395x.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index 6183ce05d8cf61..acf836312912d3 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -94,6 +94,12 @@ #define DC395x_LOCK_IO(dev,flags) spin_lock_irqsave(((struct Scsi_Host *)dev)->host_lock, flags) #define DC395x_UNLOCK_IO(dev,flags) spin_unlock_irqrestore(((struct Scsi_Host *)dev)->host_lock, flags) +/* + * read operations that may trigger side effects in the hardware, + * but the value can or should be discarded. + */ +#define DC395x_peek8(acb,address) (void)(inb(acb->io_port_base + (address))) + #define DC395x_read8(acb,address) (u8)(inb(acb->io_port_base + (address))) #define DC395x_read16(acb,address) (u16)(inw(acb->io_port_base + (address))) #define DC395x_read32(acb,address) (u32)(inl(acb->io_port_base + (address))) @@ -1000,7 +1006,7 @@ static int __dc395x_eh_bus_reset(struct scsi_cmnd *cmd) DC395x_write8(acb, TRM_S1040_DMA_CONTROL, CLRXFIFO); clear_fifo(acb, "eh_bus_reset"); /* Delete pending IRQ */ - DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS); + DC395x_peek8(acb, TRM_S1040_SCSI_INTSTATUS); set_basic_config(acb); reset_dev_param(acb); @@ -2029,8 +2035,8 @@ static void data_io_transfer(struct AdapterCtlBlk *acb, DC395x_write8(acb, TRM_S1040_SCSI_CONFIG2, CFG2_WIDEFIFO); if (io_dir & DMACMD_DIR) { - DC395x_read8(acb, TRM_S1040_SCSI_FIFO); - DC395x_read8(acb, TRM_S1040_SCSI_FIFO); + DC395x_peek8(acb, TRM_S1040_SCSI_FIFO); + DC395x_peek8(acb, TRM_S1040_SCSI_FIFO); } else { /* Danger, Robinson: If you find KGs * scattered over the wide disk, the driver @@ -2044,7 +2050,7 @@ static void data_io_transfer(struct AdapterCtlBlk *acb, /* Danger, Robinson: If you find a collection of Ks on your disk * something broke :-( */ if (io_dir & DMACMD_DIR) - DC395x_read8(acb, TRM_S1040_SCSI_FIFO); + DC395x_peek8(acb, TRM_S1040_SCSI_FIFO); else DC395x_write8(acb, TRM_S1040_SCSI_FIFO, 'K'); } @@ -2892,7 +2898,7 @@ static void set_basic_config(struct AdapterCtlBlk *acb) DMA_FIFO_HALF_HALF | DMA_ENHANCE /*| DMA_MEM_MULTI_READ */ ; DC395x_write16(acb, TRM_S1040_DMA_CONFIG, wval); /* Clear pending interrupt status */ - DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS); + DC395x_peek8(acb, TRM_S1040_SCSI_INTSTATUS); /* Enable SCSI interrupt */ DC395x_write8(acb, TRM_S1040_SCSI_INTEN, 0x7F); DC395x_write8(acb, TRM_S1040_DMA_INTEN, EN_SCSIINTR | EN_DMAXFERERROR @@ -3799,7 +3805,7 @@ static void adapter_uninit_chip(struct AdapterCtlBlk *acb) reset_scsi_bus(acb); /* clear any pending interrupt state */ - DC395x_read8(acb, TRM_S1040_SCSI_INTSTATUS); + DC395x_peek8(acb, TRM_S1040_SCSI_INTSTATUS); } From 888398bbc2504d9fa6544083f8f594b905e5704b Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 12 Nov 2025 11:41:05 +0800 Subject: [PATCH 226/258] AOSCOS: crypto: padlock-sha: return -ENODEV on Zhaoxin KaiXian KX-6000/6000G On Zhaoxin KaiXian KX-6000/6000G series of processors, this crypto module causes a null pointer dereference. Zhaoxin submitted upstream an implementation update but does not specify a fix. Per our testing, blacklisting this module causes the null pointer dereference to go away. Block x86 Family 7 Model 59 for now. Link: https://lore.kernel.org/all/20250114121301.156359-1-TonyWWang-oc%40zhaoxin.com/ */ Signed-off-by: Mingcong Bai --- drivers/crypto/padlock-sha.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c index 9214bbfc868f5d..5104f8adfa3417 100644 --- a/drivers/crypto/padlock-sha.c +++ b/drivers/crypto/padlock-sha.c @@ -329,7 +329,17 @@ static int __init padlock_init(void) struct shash_alg *sha1; struct shash_alg *sha256; - if (!x86_match_cpu(padlock_sha_ids) || !boot_cpu_has(X86_FEATURE_PHE_EN)) + /* On Zhaoxin KaiXian KX-6000/6000G series of processors, this crypto + * module causes a null pointer dereference. + * + * Zhaoxin submitted upstream an implementation update but does not + * specify a fix. Per our testing, blacklisting this module causes the + * null pointer dereference to go away. + * + * Link: https://lore.kernel.org/all/20250114121301.156359-1-TonyWWang-oc%40zhaoxin.com/ */ + if (!x86_match_cpu(padlock_sha_ids) || + !boot_cpu_has(X86_FEATURE_PHE_EN) || + (c->x86 == 7 && c->x86_model == 59)) return -ENODEV; /* From a0af4cd5ce2fbb6798564f2e735aaf61033f0d82 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 23 Dec 2025 20:08:10 +0800 Subject: [PATCH 227/258] AOSCOS: Revert "FROMLIST: rust: generate a fatal error if BINDGEN_TARGET is undefined" This reverts "FROMLIST: rust: generate a fatal error if BINDGEN_TARGET is undefined" as it breaks build on architectures that do not HAVE_RUST. Link: https://lore.kernel.org/all/6bd26802-e3ce-43e2-b28d-f23e13492458@aosc.io/ Signed-off-by: Mingcong Bai --- scripts/Makefile.rust | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/Makefile.rust b/scripts/Makefile.rust index b7a6b1e2a88a5e..ae51e68dcd59fd 100644 --- a/scripts/Makefile.rust +++ b/scripts/Makefile.rust @@ -10,8 +10,4 @@ BINDGEN_TARGET_um := $(BINDGEN_TARGET_$(SUBARCH)) BINDGEN_TARGET_riscv := riscv64-linux-gnu BINDGEN_TARGET := $(BINDGEN_TARGET_$(SRCARCH)) -ifeq ($(BINDGEN_TARGET),) -$(error add '--target=' option to scripts/Makefile.rust) -else export BINDGEN_TARGET -endif From 9e127c3ebd3c9f0715d89968c69d42555a9276fb Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Sun, 25 Jan 2026 00:54:35 +0800 Subject: [PATCH 228/258] AOSCOS: MIPS: Loongson: Salvage the full count from the constant timer HWR $30 (Loongson64 constant timer) is 64 bits wide. However, vDSO uses `unsigned long' to read the timer count, which leads to frozen time (delta ~= 3s) when O32/N32 userspace programs request HRES time. For N32, using `u64' instead of `unsigned long' fixes the issue. The issue becomes trickier on O32. RDHWR never sign-extends the timer count before copying it into a GPR, as the kernel always sets Status.UX=1 even in O32 userspace. Aside from the frozen time, the "shadow" on the upper half also breaks subsequent logical/branch instructions relying on the GPR. Thankfully, Status.UX==1 also means that we have the full timer count available and can use 64-bit instructions to salvage it into a pair of GPR to comply with O32 ABI. Utilize (abuse?) this fact to salvage the full timer count in O32 vDSO. Fixes: "AOSCOS: MIPS: Loongson: Add constant timer support" Signed-off-by: Rong Zhang Signed-off-by: Mingcong Bai --- arch/mips/include/asm/vdso/gettimeofday.h | 25 ++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/arch/mips/include/asm/vdso/gettimeofday.h b/arch/mips/include/asm/vdso/gettimeofday.h index 8cc0beef182d18..2dba88c691edd9 100644 --- a/arch/mips/include/asm/vdso/gettimeofday.h +++ b/arch/mips/include/asm/vdso/gettimeofday.h @@ -183,17 +183,40 @@ static __always_inline u64 read_gic_count(const struct vdso_time_data *data) #endif +/* + * HWR $30 (Loongson64 constant timer) is 64 bits wide. RDHWR never sign-extends + * the timer count before copying it into a GPR, as the kernel always sets + * Status.UX=1 even in O32 userspace. If it's kept as is, the "shadow" on the + * upper half breaks subsequent logical/branch instructions relying on the GPR. + * + * Thankfully, Status.UX==1 also means that we have the full timer count + * available and can use 64-bit instructions to salvage it into a pair of GPR + * to comply with O32 ABI. + */ #ifdef CONFIG_CPU_LOONGSON64 static __always_inline u64 read_const_count(void) { - unsigned long count; + u64 count; +/* N32 ABI stores 64-bit value in a single register. */ +#if _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 __asm__ __volatile__( " .set push\n" " .set mips32r2\n" " rdhwr %0, $30\n" " .set pop\n" : "=r" (count)); +#else /* _MIPS_SIM == _MIPS_SIM_ABI32 */ + __asm__ __volatile__( + " .set push\n" + " .set mips64r2\n" /* Enable 64-bit instructions. */ + " .set noreorder\n" + " rdhwr %0, $30\n" /* Now %0 holds the full 64-bit count. */ + " dsra %D0, %0, 32\n" /* Salvage and sign-extend the upper half. */ + " sll %0, %0, 0\n" /* Sign-extend the lower half. */ + " .set pop\n" + : "=r" (count)); +#endif return count; } From b108ae8e3e54a35467cdac3bc7eb12934f181464 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 19 Apr 2026 02:08:51 +0800 Subject: [PATCH 229/258] AOSCOS: dt-bindings: pinctrl: mediatek: mt8188: allow gpio hogs Add gpio hogs subnode rules to the MT8188 pinctrl binding. Signed-off-by: Icenowy Zheng Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- .../devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml index e994b0c70dbfd2..1cf06e46f7bb0f 100644 --- a/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/mediatek,mt8188-pinctrl.yaml @@ -67,6 +67,11 @@ properties: # PIN CONFIGURATION NODES patternProperties: + "-hog(-[0-9]+)?$": + type: object + required: + - gpio-hog + '-pins$': type: object additionalProperties: false From c2cbd09220a66f3b8c59de9231123e6ba67b8466 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Sun, 19 Apr 2026 02:12:40 +0800 Subject: [PATCH 230/258] AOSCOS: arm64: dts: mediatek: mt8188-geralt: enable Wi-Fi card The mainline pcie-mediatek-gen3 driver does not have code managing downstream device power / reset. As the Wi-Fi card on ciri is a fixed device, set the related regulator to always-on and use GPIO hog to set the status of its reset pin. Signed-off-by: Icenowy Zheng Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi index 8e423504ec052c..c25780098103bf 100644 --- a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi @@ -544,6 +544,11 @@ mediatek,mic-type-2 = <2>; /* DMIC */ }; +&mt6359_vcn18_ldo_reg { + /* Used by WLAN */ + regulator-always-on; +}; + &mt6359_vcore_buck_reg { regulator-always-on; }; @@ -1145,6 +1150,12 @@ output-low; }; }; + + wlan-reset-hog { + gpio-hog; + gpios = <145 GPIO_ACTIVE_HIGH>; + output-high; + }; }; &pmic { From 59d1a241bb2b865c2a3432173f5fe9d80c157756 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Mon, 20 Apr 2026 00:07:49 +0800 Subject: [PATCH 231/258] AOSCOS: arm64: dts: mediatek: mt8188-geralt: enable touchpad Despite the Ciri device's touchpad is on the detachable keyboard, the I2C HID device seen by Linux is emulated by ChromeOS EC and always present regardless of the presence of physical touchpad. Enable the device in the device tree. Signed-off-by: Icenowy Zheng Signed-off-by: Icenowy Zheng Signed-off-by: Mingcong Bai --- .../boot/dts/mediatek/mt8188-geralt.dtsi | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi index c25780098103bf..57cd2deb882fe3 100644 --- a/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi @@ -460,6 +460,18 @@ pinctrl-0 = <&i2c4_pins>; clock-frequency = <400000>; status = "okay"; + + /* The touchpad HID device is emulated by EC so it's always present */ + touchpad: touchpad@56 { + compatible = "hid-over-i2c"; + reg = <0x56>; + hid-descr-addr = <0x0001>; + interrupt-parent = <&pio>; + interrupts = <148 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&touchpad_int>; + wakeup-source; + }; }; &i2c5 { @@ -1136,6 +1148,14 @@ }; }; + touchpad_int: touchpad-int-pins { + pins-ec-ap-touchpad-int-odl { + pinmux = ; + input-enable; + bias-disable; + }; + }; + uart0_pins: uart0-pins { pins-bus { pinmux = , From 9e44542200b19a2820fa33e766a1324bbbe8ea43 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 3 May 2026 22:30:54 +0800 Subject: [PATCH 232/258] AOSCOS: of: mark a few custom Armbian functions as static The following functions were never declared nor used by anything other than the Device Tree Overlay's ConfigFS interface. Mark them as static to resolve a few `-Werror=missing-prototypes' errors (CONFIG_WERROR). - fdt_translate_address() - ssize_t cfs_overlay_item_dtbo_read() - ssize_t cfs_overlay_item_dtbo_write() Fixes: "ARMBIAN: drv:of: Device Tree Overlay ConfigFS interface" Signed-off-by: Mingcong Bai --- drivers/of/configfs.c | 4 ++-- drivers/of/fdt_address.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/of/configfs.c b/drivers/of/configfs.c index 87974779561fc9..f4f1e1e5135c23 100644 --- a/drivers/of/configfs.c +++ b/drivers/of/configfs.c @@ -115,7 +115,7 @@ static struct configfs_attribute *cfs_overlay_attrs[] = { NULL, }; -ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, +static ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, void *buf, size_t max_count) { struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); @@ -138,7 +138,7 @@ ssize_t cfs_overlay_item_dtbo_read(struct config_item *item, return overlay->dtbo_size; } -ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, +static ssize_t cfs_overlay_item_dtbo_write(struct config_item *item, const void *buf, size_t count) { struct cfs_overlay_item *overlay = to_cfs_overlay_item(item); diff --git a/drivers/of/fdt_address.c b/drivers/of/fdt_address.c index e1353d7a58e6ea..f358d2c807540e 100644 --- a/drivers/of/fdt_address.c +++ b/drivers/of/fdt_address.c @@ -147,7 +147,7 @@ static int __init fdt_translate_one(const void *blob, int parent, * that can be mapped to a cpu physical address). This is not really specified * that way, but this is traditionally the way IBM at least do things */ -u64 __init fdt_translate_address(const void *blob, int node_offset) +static u64 __init fdt_translate_address(const void *blob, int node_offset) { int parent, len; const struct of_bus *bus, *pbus; From baa730bc9190c6679071e0c330e03df65719972a Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 3 May 2026 22:38:08 +0800 Subject: [PATCH 233/258] AOSCOS: platform/x86: int3472: fix an incorrect number conversation specification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building with CONFIG_WERROR: drivers/platform/x86/intel/int3472/discrete.c:385:44: error: format ‘%x’ expects argument of type ‘unsigned int’, but argument 4 has type ‘long unsigned int’ [-Werror=format=] 385 | dev_info(int3472->dev, " con_id=%s, flags=0x%x\n", con_id, gpio_flags); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ Correct the number conversation specification as `%lx'. Fixes: "BACKPORT: SURFACE: Add camera support for Surface Pro 9" Signed-off-by: Mingcong Bai --- drivers/platform/x86/intel/int3472/discrete.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index b2a54733df2021..355b2d5af737be 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -389,7 +389,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case 0x08: /* Surface Pro 9 - treat as power*/ dev_info(int3472->dev, "GPIO type 0x%02x detected on pin 0x%02x\n", type, agpio->pin_table[0]); - dev_info(int3472->dev, " con_id=%s, flags=0x%x\n", con_id, gpio_flags); + dev_info(int3472->dev, " con_id=%s, flags=0x%lx\n", con_id, gpio_flags); ret = skl_int3472_register_regulator(int3472, gpio, GPIO_REGULATOR_ENABLE_TIME, con_id, NULL); From e9df7fa8b139f4b916545aa9397b3a4de000232d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 3 May 2026 22:39:35 +0800 Subject: [PATCH 234/258] AOSCOS: platform/x86: int3472: remove an unused argument to dev_err() When building with CONFIG_WERROR: drivers/platform/x86/intel/int3472/discrete.c:391:47: error: too many arguments for format [-Werror=format-extra-args] 391 | dev_err(int3472->dev, "Failed to register type 0x02x: %d\n", type, ret); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Remove the unused `type' argument to fix build. Fixes: "BACKPORT: SURFACE: Add camera support for Surface Pro 9" Signed-off-by: Mingcong Bai --- drivers/platform/x86/intel/int3472/discrete.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 355b2d5af737be..c1d6c5121a5004 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -395,7 +395,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, con_id, NULL); dev_info(int3472->dev, " register_regulator returned: %d\n", ret); if (ret) { - dev_err(int3472->dev, "Failed to register type 0x02x: %d\n", type, ret); + dev_err(int3472->dev, "Failed to register type 0x02x: %d\n", ret); } break; case 0x11: From 2f01602a39a68e89beee1201a3845d6021bc7195 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 3 May 2026 23:01:20 +0800 Subject: [PATCH 235/258] AOSCOS: drm: amdgpu: disable Panel Self Refresh v1 and v2 (PSR-SU), and Panel Replay There has been a lot of report about freezing displays on laptops, especially on those based on RDNA 2/3/4 architectures. These features are probably more trouble than they are worth, so disable them by default. Signed-off-by: Mingcong Bai --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 26b48c0100978b..53d196ef53a43c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -863,7 +863,8 @@ module_param_named(dcfeaturemask, amdgpu_dc_feature_mask, uint, 0444); * DOC: dcdebugmask (uint) * Display debug options. See enum DC_DEBUG_MASK in drivers/gpu/drm/amd/include/amd_shared.h. */ -MODULE_PARM_DESC(dcdebugmask, "all debug options disabled (default))"); +uint dcdebugmask = 0x610; +MODULE_PARM_DESC(dcdebugmask, "Disable debug options (default: disable Panel Self Refresh v1 and PSR-SU, or v2, as well as Panel Replay)"); module_param_named(dcdebugmask, amdgpu_dc_debug_mask, uint, 0444); MODULE_PARM_DESC(visualconfirm, "Visual confirm (0 = off (default), 1 = MPO, 5 = PSR)"); From 422d31c18f19103f19cf9a58bdc45365e7263e1f Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Fri, 8 May 2026 09:55:19 +0800 Subject: [PATCH 236/258] AOSCOS: mei: me: use PCI_DEVICE_DATA macro for linux-surface additions With commit 09033be12a82 ("mei: me: use PCI_DEVICE_DATA macro"), calls to the `MEI_PCI_DEVICE' macro were replaced with `PCI_DEVICE_DATA'. Convert entries added by linux-surface patches to fix build. Fixes: "BACKPORT: SURFACE: mei: me: Add Icelake device ID for iTouch" Signed-off-by: Mingcong Bai --- drivers/misc/mei/hw-me-regs.h | 5 +---- drivers/misc/mei/pci-me.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 0436998e8126d1..513ddb26c11a7b 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -92,12 +92,9 @@ #define PCI_DEVICE_ID_INTEL_MEI_CDF 0x18D3 /* Cedar Fork */ #define PCI_DEVICE_ID_INTEL_MEI_ICP_LP 0x34E0 /* Ice Lake Point LP */ +#define PCI_DEVICE_ID_INTEL_MEI_ICP_LP_3 0x34E4 /* Ice Lake Point LP 3 (iTouch) */ #define PCI_DEVICE_ID_INTEL_MEI_ICP_N 0x38E0 /* Ice Lake Point N */ -#define MEI_DEV_ID_ICP_LP 0x34E0 /* Ice Lake Point LP */ -#define MEI_DEV_ID_ICP_LP_3 0x34E4 /* Ice Lake Point LP 3 (iTouch) */ -#define MEI_DEV_ID_ICP_N 0x38E0 /* Ice Lake Point N */ - #define PCI_DEVICE_ID_INTEL_MEI_JSP_N 0x4DE0 /* Jasper Lake Point N */ #define PCI_DEVICE_ID_INTEL_MEI_TGP_LP 0xA0E0 /* Tiger Lake Point LP */ diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index d97945f5b8683b..e5063149c47d3f 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -98,12 +98,9 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {PCI_DEVICE_DATA(INTEL, MEI_CMP_H_3, MEI_ME_PCH8_ITOUCH_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP, MEI_ME_PCH12_CFG)}, + {PCI_DEVICE_DATA(INTEL, MEI_ICP_LP_3, MEI_ME_PCH12_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_ICP_N, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_LP_3, MEI_ME_PCH12_CFG)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICP_N, MEI_ME_PCH12_CFG)}, - {PCI_DEVICE_DATA(INTEL, MEI_TGP_LP, MEI_ME_PCH15_CFG)}, {PCI_DEVICE_DATA(INTEL, MEI_TGP_H, MEI_ME_PCH15_SPS_CFG)}, From f949a0a50836585435a4041e339a1b985d84ec89 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 17 May 2026 12:00:22 +0800 Subject: [PATCH 237/258] AOSCOS: powerpc: define __LITTLE_ENDIAN and __BIG_ENDIAN for math-emu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to commit b929926f01f2 ("sh: define __BIG_ENDIAN for math-emu"), define __LITTLE_ENDIAN and __BIG_ENDIAN as 0 to mitigate build-time warnings: ./include/math-emu/double.h:59:21: error: ‘__BIG_ENDIAN’ is not defined, evaluates to ‘0’ [-Werror=undef] 59 | #if __BYTE_ORDER == __BIG_ENDIAN | Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202507301656.7FEX6J5W-lkp@intel.com/ Signed-off-by: Mingcong Bai --- arch/powerpc/include/asm/sfp-machine.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/powerpc/include/asm/sfp-machine.h b/arch/powerpc/include/asm/sfp-machine.h index 8b957aabb826d3..db8525605c026d 100644 --- a/arch/powerpc/include/asm/sfp-machine.h +++ b/arch/powerpc/include/asm/sfp-machine.h @@ -319,10 +319,12 @@ #define abort() \ return 0 -#ifdef __BIG_ENDIAN +#ifdef __BIG_ENDIAN__ #define __BYTE_ORDER __BIG_ENDIAN +#define __LITTLE_ENDIAN 0 #else #define __BYTE_ORDER __LITTLE_ENDIAN +#define __BIG_ENDIAN 0 #endif /* Exception flags. */ From 800b525d30f4e0b0185749b10ef05ac89fd89d35 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 24 May 2026 21:06:23 +0800 Subject: [PATCH 238/258] AOSCOS: rust: Add -fpatchable-function-entry= to bindgen_skip_c_flags This flag was introduced with commit eec37961a56a ("powerpc64/ftrace: Move ftrace sequence out of line") to workaround an issue with ftrace. However, this flag was passed to bindgen and caused the following error: Unable to generate bindings: clang diagnosed error: error: unsupported option '-fpatchable-function-entry=1' for target 'powerpc64le-unknown-linux-gnu' Add this flag to bindgen_skip_c_flags to fix build. Fixes: "BACKPORT: FROMLIST: powerpc: Enable Rust for ppc64le" Tested-by: Mingcong Bai --- rust/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Makefile b/rust/Makefile index e071de1155dd4d..5f663d5e3a8652 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -397,7 +397,7 @@ bindgen_skip_c_flags := -mno-fp-ret-in-387 -mpreferred-stack-boundary=% \ -fzero-init-padding-bits=% -mno-fdpic \ -fdiagnostics-show-context -fdiagnostics-show-context=% \ --param=% --param asan-% -fno-isolate-erroneous-paths-dereference \ - -mno-riscv-attribute + -mno-riscv-attribute -fpatchable-function-entry=% # All warnings are inhibited since GCC builds are very experimental, # many GCC warnings are not supported by Clang, they may only appear in From 92c2a9026f8f40d8a1b9e5e38cf91b2b43f25bca Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 16:05:51 +0800 Subject: [PATCH 239/258] AOSCOS: MIPS: select ARCH_FORCE_MAX_ORDER >= 11 if NUMA_BALANCING is selected This should suppress an assertion failure during build. Further investigation should be conducted when possible. Fixes: "BACKPORT: FROMLIST: mips/mm: Add NUMA balancing support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 4de8bafd90fbd7..3ff89b82e2d7be 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2139,9 +2139,9 @@ config ZBOOT_LOAD_ADDRESS config ARCH_FORCE_MAX_ORDER int "Maximum zone order" - default "13" if MIPS_HUGE_TLB_SUPPORT && PAGE_SIZE_64KB - default "12" if MIPS_HUGE_TLB_SUPPORT && PAGE_SIZE_32KB - default "11" if MIPS_HUGE_TLB_SUPPORT && PAGE_SIZE_16KB + default "13" if (NUMA_BALANCING || MIPS_HUGE_TLB_SUPPORT) && PAGE_SIZE_64KB + default "12" if (NUMA_BALANCING || MIPS_HUGE_TLB_SUPPORT) && PAGE_SIZE_32KB + default "11" if (NUMA_BALANCING || MIPS_HUGE_TLB_SUPPORT) && PAGE_SIZE_16KB default "10" help The kernel memory allocator divides physically contiguous memory From 61eea78e10483b513277ccc7ff7fec9920460dc6 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 16:07:57 +0800 Subject: [PATCH 240/258] AOSCOS: init: make NUMA_BALANCING depend on TRANSPARENT_HUGEPAGE if MIPS This should suppress an assertion failure during build. Further investigation should be conducted when possible. Fixes: "BACKPORT: FROMLIST: mips/mm: Add NUMA balancing support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- init/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/init/Kconfig b/init/Kconfig index d78a6e616311e8..abd4845b7fbe53 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1015,6 +1015,7 @@ config NUMA_BALANCING depends on ARCH_SUPPORTS_NUMA_BALANCING depends on !ARCH_WANT_NUMA_VARIABLE_LOCALITY depends on SMP && NUMA_MIGRATION && !PREEMPT_RT + depends on TRANSPARENT_HUGEPAGE || !MIPS help This option adds support for automatic NUMA aware memory/task placement. The mechanism is quite primitive and is based on migrating memory when From ff8ccfbdd72b3e2b55832816418255a7c88d4689 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 10 Dec 2024 16:36:25 +0800 Subject: [PATCH 241/258] AOSCOS: MIPS: audit: declare function prototypes for audit_classify_syscall_*() Functions `audit_classify_syscall_n32()', `audit_classify_syscall_o32()` were never declared in the previous code and causes build errors due to `-Werror=missing-prototype'. Create function prototype declarations in `audit-{n,o}32.h' and reference them in each of the respective sources. Also remove the extern references as they are no longer needed. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/audit-n32.c | 1 + arch/mips/kernel/audit-n32.h | 5 +++++ arch/mips/kernel/audit-native.c | 5 ++--- arch/mips/kernel/audit-o32.c | 1 + arch/mips/kernel/audit-o32.h | 5 +++++ 5 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 arch/mips/kernel/audit-n32.h create mode 100644 arch/mips/kernel/audit-o32.h diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c index 2248badc3acb65..2e9ac29588c810 100644 --- a/arch/mips/kernel/audit-n32.c +++ b/arch/mips/kernel/audit-n32.c @@ -4,6 +4,7 @@ #include #include #include +#include "audit-n32.h" static unsigned dir_class_n32[] = { #include diff --git a/arch/mips/kernel/audit-n32.h b/arch/mips/kernel/audit-n32.h new file mode 100644 index 00000000000000..781de2e9ad66be --- /dev/null +++ b/arch/mips/kernel/audit-n32.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef CONFIG_AUDITSYSCALL_N32 +int audit_classify_syscall_n32(int abi, unsigned syscall); +#endif /* CONFIG_AUDITSYSCALL_N32 */ diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c index 09ae3dbcfecbcc..88e283f8df3850 100644 --- a/arch/mips/kernel/audit-native.c +++ b/arch/mips/kernel/audit-native.c @@ -2,6 +2,8 @@ #include #include #include +#include "audit-n32.h" +#include "audit-o32.h" static unsigned dir_class[] = { #include @@ -37,9 +39,6 @@ int audit_classify_arch(int arch) return 0; } -extern int audit_classify_syscall_o32(int abi, unsigned syscall); -extern int audit_classify_syscall_n32(int abi, unsigned syscall); - int audit_classify_syscall(int abi, unsigned syscall) { int res; diff --git a/arch/mips/kernel/audit-o32.c b/arch/mips/kernel/audit-o32.c index e8b9b50f7d2671..6bf302c04e5d3b 100644 --- a/arch/mips/kernel/audit-o32.c +++ b/arch/mips/kernel/audit-o32.c @@ -4,6 +4,7 @@ #include #include #include +#include "audit-o32.h" static unsigned dir_class_o32[] = { #include diff --git a/arch/mips/kernel/audit-o32.h b/arch/mips/kernel/audit-o32.h new file mode 100644 index 00000000000000..8a782fdd1c6e78 --- /dev/null +++ b/arch/mips/kernel/audit-o32.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef CONFIG_AUDITSYSCALL_O32 +int audit_classify_syscall_o32(int abi, unsigned syscall); +#endif /* CONFIG_AUDITSYSCALL_O32 */ From b3724376ba44281bd9eed61edf8a55eb05749415 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 10 Dec 2024 16:39:13 +0800 Subject: [PATCH 242/258] AOSCOS: MIPS: audit: replace magic audit syscall class numbers with macros Follow commit 42f355ef59a2 ("audit: replace magic audit syscall class numbers with macros") and replace all magic numbers in MIPS audit code. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/audit-n32.c | 6 +++--- arch/mips/kernel/audit-native.c | 8 ++++---- arch/mips/kernel/audit-o32.c | 10 +++++----- include/linux/audit_arch.h | 3 +++ kernel/auditsc.c | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c index 2e9ac29588c810..8d114bf8542548 100644 --- a/arch/mips/kernel/audit-n32.c +++ b/arch/mips/kernel/audit-n32.c @@ -35,11 +35,11 @@ int audit_classify_syscall_n32(int abi, unsigned syscall) { switch (syscall) { case __NR_open: - return 2; + return AUDITSC_OPEN; case __NR_openat: - return 3; + return AUDITSC_OPENAT; case __NR_execve: - return 5; + return AUDITSC_EXECVE; default: return 0; } diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c index 88e283f8df3850..4a0fecd0e48674 100644 --- a/arch/mips/kernel/audit-native.c +++ b/arch/mips/kernel/audit-native.c @@ -45,20 +45,20 @@ int audit_classify_syscall(int abi, unsigned syscall) switch (syscall) { case __NR_open: - res = 2; + res = AUDITSC_OPEN; break; case __NR_openat: - res = 3; + res = AUDITSC_OPEN; break; #ifdef __NR_socketcall /* Only exists on O32 */ case __NR_socketcall: - res = 4; + res = AUDITSC_SOCKETCALL; break; #endif case __NR_execve: - res = 5; + res = AUDITSC_EXECVE; break; default: #ifdef CONFIG_AUDITSYSCALL_O32 diff --git a/arch/mips/kernel/audit-o32.c b/arch/mips/kernel/audit-o32.c index 6bf302c04e5d3b..818f91ce14acf3 100644 --- a/arch/mips/kernel/audit-o32.c +++ b/arch/mips/kernel/audit-o32.c @@ -35,15 +35,15 @@ int audit_classify_syscall_o32(int abi, unsigned syscall) { switch (syscall) { case __NR_open: - return 2; + return AUDITSC_OPEN; case __NR_openat: - return 3; + return AUDITSC_OPENAT; case __NR_socketcall: - return 4; + return AUDITSC_SOCKETCALL; case __NR_execve: - return 5; + return AUDITSC_EXECVE; default: - return 0; + return AUDITSC_NATIVE; } } diff --git a/include/linux/audit_arch.h b/include/linux/audit_arch.h index a35069a6c15de2..b6385104d34be7 100644 --- a/include/linux/audit_arch.h +++ b/include/linux/audit_arch.h @@ -17,6 +17,9 @@ enum auditsc_class_t { AUDITSC_SOCKETCALL, AUDITSC_EXECVE, AUDITSC_OPENAT2, +#ifdef CONFIG_MIPS + AUDITSC_MIPS_N32, +#endif AUDITSC_NVALS /* count */ }; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index d3099b4cec6d3b..a2471370d88b7a 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -190,7 +190,7 @@ static int audit_match_perm(struct audit_context *ctx, int mask) case AUDITSC_OPENAT2: return mask & ACC_MODE((u32)ctx->openat2.flags); #ifdef CONFIG_MIPS - case 6: /* for N32 */ + case AUDITSC_MIPS_N32: /* for N32 */ if ((mask & AUDIT_PERM_WRITE) && audit_match_class(AUDIT_CLASS_WRITE_N32, n)) return 1; From 9cd2ba2e0c2082659abed3fcad41a9387ac39112 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 17 Dec 2024 02:00:03 +0800 Subject: [PATCH 243/258] AOSCOS: MIPS: audit: clean up the last remnants of magic numbers Follow commit 42f355ef59a2 ("audit: replace magic audit syscall class numbers with macros") and replace all magic numbers in MIPS audit code. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- arch/mips/kernel/audit-n32.c | 2 +- arch/mips/kernel/audit-native.c | 33 +++++++-------------------------- 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c index 8d114bf8542548..76c95ff924f83b 100644 --- a/arch/mips/kernel/audit-n32.c +++ b/arch/mips/kernel/audit-n32.c @@ -41,7 +41,7 @@ int audit_classify_syscall_n32(int abi, unsigned syscall) case __NR_execve: return AUDITSC_EXECVE; default: - return 0; + return AUDITSC_NATIVE; } } diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c index 4a0fecd0e48674..763f0e6b3636aa 100644 --- a/arch/mips/kernel/audit-native.c +++ b/arch/mips/kernel/audit-native.c @@ -41,45 +41,26 @@ int audit_classify_arch(int arch) int audit_classify_syscall(int abi, unsigned syscall) { - int res; - switch (syscall) { case __NR_open: - res = AUDITSC_OPEN; - break; - + return AUDITSC_OPEN; case __NR_openat: - res = AUDITSC_OPEN; - break; - + return AUDITSC_OPEN; #ifdef __NR_socketcall /* Only exists on O32 */ case __NR_socketcall: - res = AUDITSC_SOCKETCALL; - break; + return AUDITSC_SOCKETCALL; #endif case __NR_execve: - res = AUDITSC_EXECVE; - break; + return AUDITSC_EXECVE; default: #ifdef CONFIG_AUDITSYSCALL_O32 - res = audit_classify_syscall_o32(abi, syscall); - if (res) - break; + return audit_classify_syscall_o32(abi, syscall); #endif #ifdef CONFIG_AUDITSYSCALL_N32 - res = audit_classify_syscall_n32(abi, syscall); - if (res) - break; + return audit_classify_syscall_n32(abi, syscall); #endif - if (abi == AUDIT_ARCH_MIPS || abi == AUDIT_ARCH_MIPSEL) - res = 1; - else if (abi == AUDIT_ARCH_MIPS64 || abi == AUDIT_ARCH_MIPSEL64) - res = 0; - else if (abi == AUDIT_ARCH_MIPS64N32 || abi == AUDIT_ARCH_MIPSEL64N32) - res = 6; + return AUDITSC_NATIVE; } - - return res; } static int __init audit_classes_init(void) From 9d7f2d17829704bae1a11b87256019a687463018 Mon Sep 17 00:00:00 2001 From: Kexy Biscuit Date: Sun, 15 Dec 2024 11:21:54 +0800 Subject: [PATCH 244/258] AOSCOS: audit: remove duplicate functions for MIPS audit_classify_arch() and audit_classify_syscall() are already defined in arch/mips/kernel/audit-native.c, adding preprocessor directives to remove them from lib/audit.c on MIPS. Signed-off-by: Kexy Biscuit Signed-off-by: Mingcong Bai --- lib/audit.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/audit.c b/lib/audit.c index bc07fbd3a6983f..7063bd38f2c635 100644 --- a/lib/audit.c +++ b/lib/audit.c @@ -29,6 +29,7 @@ static unsigned signal_class[] = { ~0U }; +#if !defined(CONFIG_MIPS) int audit_classify_arch(int arch) { if (audit_is_compat(arch)) @@ -68,6 +69,7 @@ int audit_classify_syscall(int abi, unsigned syscall) return AUDITSC_NATIVE; } } +#endif static int __init audit_classes_init(void) { From 756972b11e8fc51bf79e81ebe44e3b938d3b0215 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Fri, 12 Jun 2026 23:32:42 +0800 Subject: [PATCH 245/258] AOSCOS: drm/ttm: fix incorrect caching type caused by a previous AOSCOS patch The ttm_mem_io_reserve() function calls a driver-defined callback to set mem->bus.caching. Thus we cannot use the value read before calling ttm_mem_io_reserve(), or we'd effectively erase the set from driver. Instead we should only downgrade cached to write_combined if the driver says to use cached. Fixes: eb4186e62799 ("BACKPORT: FROMLIST: drm/ttm: downgrade cached to write_combined when snooping not available") Signed-off-by: Xi Ruoyao --- drivers/gpu/drm/ttm/ttm_resource.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 9ba711790a9528..6cd19a877b2ce4 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -848,9 +848,12 @@ ttm_kmap_iter_linear_io_init(struct ttm_kmap_iter_linear_io *iter_io, struct ttm_device *bdev, struct ttm_resource *mem) { - enum ttm_caching caching = mem->bus.caching; + enum ttm_caching caching; int ret; + ret = ttm_mem_io_reserve(bdev, mem); + caching = mem->bus.caching; + #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -859,7 +862,6 @@ ttm_kmap_iter_linear_io_init(struct ttm_kmap_iter_linear_io *iter_io, caching = ttm_write_combined; #endif - ret = ttm_mem_io_reserve(bdev, mem); if (ret) goto out_err; if (!mem->bus.is_iomem) { From 08508ba4f77a3726ae465282826d4618a3e89ee3 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 28 Jun 2026 21:39:08 +0800 Subject: [PATCH 246/258] AOSCOS: net/phytium: allow building the driver everywhere if CONFIG_COMPILE_TEST Also add a guard for dma_ops assignment as it only exists on the architectures selecting CONFIG_ARCH_HAS_DMA_OPS. It makes hacking the driver without an aarch64 machine easier. Signed-off-by: Xi Ruoyao --- drivers/net/ethernet/phytium/Kconfig | 4 ++-- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig index 74f671b5e0d416..4ba96e11a59490 100644 --- a/drivers/net/ethernet/phytium/Kconfig +++ b/drivers/net/ethernet/phytium/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_PHYTIUM bool "Phytium devices" - depends on ARCH_PHYTIUM + depends on ARCH_PHYTIUM || COMPILE_TEST depends on HAS_IOMEM default y help @@ -21,7 +21,7 @@ if NET_VENDOR_PHYTIUM config PHYTMAC tristate "Phytium GMAC support" depends on ACPI - depends on ARCH_PHYTIUM + depends on ARCH_PHYTIUM || COMPILE_TEST depends on HAS_DMA select PHYLINK select CRC32 diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index 18950063f37200..d769da9ac3cc9f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -176,7 +176,9 @@ static int phytium_dwmac_probe(struct platform_device *pdev) * show kernel error 'DMA descriptors allocation failed' */ if (acpi_match_device_ids(to_acpi_device(&pdev->dev), phytium_old_acpi_id)) { +#ifdef CONFIG_ARCH_HAS_DMA_OPS pdev->dev.dma_ops = NULL; // solved set DMA mask Failed +#endif plat->host_dma_width = 32; } #endif From 19c1aa9834e416391e3c95911e6bfd712e2e3303 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 28 Jun 2026 21:40:17 +0800 Subject: [PATCH 247/258] AOSCOS: net/phytium: drop of_gpio.h inclusion This seems used nowhere and the header is removed in Linux 7.1. Signed-off-by: Xi Ruoyao --- drivers/net/ethernet/phytium/phytmac_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 3f1f2098284de9..0d6bdb44cf516b 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include From cbe9046675fcbdb85d27e3bfb10588204005e6ac Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 28 Jun 2026 21:41:29 +0800 Subject: [PATCH 248/258] AOSCOS: net/phytium: remove plat_dat->port_node Link: https://git.kernel.org/torvalds/c/44a2ec96d374 Signed-off-by: Xi Ruoyao --- drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c index d769da9ac3cc9f..3c19f212704195 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-phytium.c @@ -91,10 +91,8 @@ static int phytium_dwmac_probe(struct platform_device *pdev) plat->phy_interface = plat->phy_interface; /* Configure PHY if using device-tree */ - if (pdev->dev.of_node) { + if (pdev->dev.of_node) plat->phy_node = of_parse_phandle(np, "phy-handle", 0); - plat->port_node = of_fwnode_handle(np); - } if (pdev->dev.of_node) { plat->bus_id = of_alias_get_id(np, "ethernet"); From d3b6c6d9eb51a1145f323d5a3afc59460dd09cd7 Mon Sep 17 00:00:00 2001 From: Xi Ruoyao Date: Sun, 28 Jun 2026 21:57:39 +0800 Subject: [PATCH 249/258] AOSCOS: net/phytium: guard "np" variable definition with #ifdef CONFIG_OF The of_match_node macro is no-op if !CONFIG_OF, so with CONFIG_WERROR the build would fail as nothing uses the "np" variable. Signed-off-by: Xi Ruoyao --- drivers/net/ethernet/phytium/phytmac_platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 3724936cc826e2..225ba0835df927 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -168,7 +168,9 @@ static int phytmac_get_phy_mode(struct platform_device *pdev) static int phytmac_plat_probe(struct platform_device *pdev) { const struct phytmac_config *phytmac_config = &phytium_1p0_config; +#ifdef CONFIG_OF struct device_node *np; +#endif struct resource *regs; struct phytmac *pdata; int ret, i; From 532d2c2a7bc8a6ef30cc105f8f5d84833f35dbe3 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Fri, 24 Jul 2026 21:39:18 +0800 Subject: [PATCH 250/258] AOSCOS: mmc: loongson2: add back the so-called "ABI 1.0" compatible naming for 2K2000/2K3000/3B6000M Older Loongson 2K3000/3B6000M firmware uses the following so-called "ABI 1.0" compatible name. Add this back for compatibility's sake. Update your firmware and care for your customer's right! Ugh. Signed-off-by: Mingcong Bai --- drivers/mmc/host/loongson2-mmc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c index f553e92fd9e541..b37c4afc5ee812 100644 --- a/drivers/mmc/host/loongson2-mmc.c +++ b/drivers/mmc/host/loongson2-mmc.c @@ -1014,6 +1014,12 @@ static const struct of_device_id loongson2_mmc_of_ids[] = { { .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata }, { .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata }, { .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata }, + /* + * Older Loongson 2K3000/3B6000M firmware uses the following so-called + * "ABI 1.0" compatible name. Add this back for compatibility's sake... + * Update your firmware and care for your customer's right! Ugh. + */ + { .compatible = "loongson,ls2k_sdio_1.2", .data = &ls2k2000_mmc_pdata }, { }, }; MODULE_DEVICE_TABLE(of, loongson2_mmc_of_ids); From 4fac367223b842639005524788eb01c2640b76d3 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 26 Jul 2026 00:13:50 +0800 Subject: [PATCH 251/258] AOSCOS: ALSA: usb-audio: partially revert "ALSA: usb-audio: add IFB_SILENCE_ON_EMPTY quirk for Behringer Flow 8" Revert the only possible common code path in that change, which apparently broke S3 resume on MECHREVO WUJIE 14 Pro A-7BC4U. This reverts commit 344b64d4d411f5605867d988a076757bdabd7b46. Signed-off-by: Mingcong Bai --- sound/usb/endpoint.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 24cd7692bd01ca..6fbcb117555c6b 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1780,16 +1780,8 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, /* * skip empty packets. At least M-Audio's Fast Track Ultra stops * streaming once it received a 0-byte OUT URB - * - * However, on devices where bytes==0 means every sync-source - * packet errored (e.g. Behringer Flow 8 returning -EXDEV bursts - * for entire capture URBs), an unconditional return starves the - * IFB-fed OUT ring permanently. Such devices set - * QUIRK_FLAG_IFB_SILENCE_ON_EMPTY to fall through and enqueue a - * packet_info with size 0 packets, so playback emits silence - * and the OUT ring keeps moving. */ - if (bytes == 0 && !(ep->chip->quirk_flags & QUIRK_FLAG_IFB_SILENCE_ON_EMPTY)) + if (bytes == 0) return; spin_lock_irqsave(&ep->lock, flags); From dc18e2760d845acaf11975079ab38eeb015332bd Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 28 Jul 2026 15:35:22 +0800 Subject: [PATCH 252/258] AOSCOS: init: allow disabling DEBUG_KERNEL when EXPERT is selected This made no sense in our case, especially for Afterglow. Signed-off-by: Mingcong Bai --- init/Kconfig | 2 -- 1 file changed, 2 deletions(-) diff --git a/init/Kconfig b/init/Kconfig index abd4845b7fbe53..57f01b204ded5d 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1695,8 +1695,6 @@ config HAVE_PCSPKR_PLATFORM menuconfig EXPERT bool "Configure standard kernel features (expert users)" - # Unhide debug options, to make the on-by-default options visible - select DEBUG_KERNEL help This option allows certain base kernel options and settings to be disabled or tweaked. This is for specialized From 34be08369dacc2972fcff1f8a0f9b21d9367807b Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 28 Jul 2026 16:21:07 +0800 Subject: [PATCH 253/258] AOSCOS: x86/boot: use zstd -19 if building for 32-bit Afterglow uses native build environments (that is, 32-bit x86 chroot/ container as build environment for 32-bit x86 binaries), `zstd -22' requests more virtual memory than that could be allowed. Introduce `cmd_zstd_with_size' for CONFIG_X86_32 builds. DO NOT UPSTREAM: This should really be done through build environment detection, not by Kconfig - cross-compilation breaks the aforementioned assumptions. Signed-off-by: Mingcong Bai --- arch/x86/boot/compressed/Makefile | 5 +++++ scripts/Makefile.lib | 3 +++ 2 files changed, 8 insertions(+) diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 06934f9691d6a9..9963ed23b8d6ec 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile @@ -144,8 +144,13 @@ $(obj)/vmlinux.bin.lzo: $(vmlinux.bin.all-y) FORCE $(call if_changed,lzo_with_size) $(obj)/vmlinux.bin.lz4: $(vmlinux.bin.all-y) FORCE $(call if_changed,lz4_with_size) +ifdef CONFIG_X86_32 +$(obj)/vmlinux.bin.zst: $(vmlinux.bin.all-y) FORCE + $(call if_changed,zstd_with_size) +else $(obj)/vmlinux.bin.zst: $(vmlinux.bin.all-y) FORCE $(call if_changed,zstd22_with_size) +endif suffix-$(CONFIG_KERNEL_GZIP) := gz suffix-$(CONFIG_KERNEL_BZIP2) := bz2 diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 0718e39cedda2a..09910dc57af3c0 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -464,6 +464,9 @@ quiet_cmd_xzmisc = XZMISC $@ quiet_cmd_zstd = ZSTD $@ cmd_zstd = cat $(real-prereqs) | $(ZSTD) -19 > $@ +quiet_cmd_zstd = ZSTD $@ + cmd_zstd_with_size = { cat $(real-prereqs) | $(ZSTD) -19; $(size_append); } > $@ + quiet_cmd_zstd22 = ZSTD22 $@ cmd_zstd22 = cat $(real-prereqs) | $(ZSTD) -22 --ultra > $@ From 7842e430782d25b1655806cd5592b20a1905d49c Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 2 Aug 2026 03:56:34 +0800 Subject: [PATCH 254/258] AOSCOS: REVERT: Revert "x86/cpu: Remove M486/M486SX/ELAN support" This reverts commit 8b793a92d862c89055daa97ffa61a6929cf732f9. Signed-off-by: Mingcong Bai --- arch/x86/Kconfig | 10 ++++++++ arch/x86/Kconfig.cpu | 44 +++++++++++++++++++++++++-------- arch/x86/Makefile_32.cpu | 2 ++ arch/x86/include/asm/vermagic.h | 6 +++++ 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f3f7cb01d69d02..589e056a90abcd 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -447,6 +447,11 @@ config SMP uniprocessor machines. On a uniprocessor machine, the kernel will run faster if you say N here. + Note that if you say Y here and choose architecture "586" or + "Pentium" under "Processor family", the kernel will not work on 486 + architectures. Similarly, multiprocessor kernels for the "PPro" + architecture may not work on all Pentium based boards. + People using multiprocessor machines who say Y here should also say Y to "Enhanced Real Time Clock Support", below. The "Advanced Power Management" code will be disabled if you say Y here. @@ -2768,6 +2773,11 @@ menuconfig APM manpage ("man 8 hdparm") for that), and it doesn't turn off VESA-compliant "green" monitors. + This driver does not support the TI 4000M TravelMate and the ACER + 486/DX4/75 because they don't have compliant BIOSes. Many "green" + desktop machines also don't have compliant BIOSes, and this driver + may cause those machines to panic during the boot phase. + Generally, if you don't have a battery in your machine, there isn't much point in using this driver and you should say N. If you get random kernel OOPSes or reboots that don't seem to be related to diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu index df003a42d25a6d..755d64bb9d2652 100644 --- a/arch/x86/Kconfig.cpu +++ b/arch/x86/Kconfig.cpu @@ -8,18 +8,19 @@ choice This is the processor type of your CPU. This information is used for optimizing purposes. In order to compile a kernel that can run on all supported x86 CPU types (albeit not - optimally fast), you can specify "586" here. + optimally fast), you can specify "486" here. - Note that the 386 and 486 is no longer supported, this includes + Note that the 386 is no longer supported, this includes AMD/Cyrix/Intel 386DX/DXL/SL/SLC/SX, Cyrix/TI 486DLC/DLC2, - UMC 486SX-S and the NexGen Nx586, AMD ELAN and all 486 based - CPUs. + UMC 486SX-S and the NexGen Nx586. The kernel will not necessarily run on earlier architectures than the one you have chosen, e.g. a Pentium optimized kernel will run on a PPro, but not necessarily on a i486. Here are the settings recommended for greatest speed: + - "486" for the AMD/Cyrix/IBM/Intel 486DX/DX2/DX4 or + SL/SLC/SLC2/SLC3/SX/SX2 and UMC U5D or U5S. - "586" for generic Pentium CPUs lacking the TSC (time stamp counter) register. - "Pentium-Classic" for the Intel Pentium. @@ -45,6 +46,20 @@ choice See each option's help text for additional details. If you don't know what to do, choose "Pentium-Pro". +config M486SX + bool "486SX" + depends on X86_32 + help + Select this for an 486-class CPU without an FPU such as + AMD/Cyrix/IBM/Intel SL/SLC/SLC2/SLC3/SX/SX2 and UMC U5S. + +config M486 + bool "486DX" + depends on X86_32 + help + Select this for an 486-class CPU such as AMD/Cyrix/IBM/Intel + 486DX/DX2/DX4 and UMC U5D. + config M586 bool "586/K5/5x86/6x86/6x86MX" depends on X86_32 @@ -173,6 +188,14 @@ config MWINCHIP3D stores for this CPU, which can increase performance of some operations. +config MELAN + bool "AMD Elan" + depends on X86_32 + help + Select this for an AMD Elan processor. + + Do not use this option for K6/Athlon/Opteron processors! + config MGEODEGX1 bool "GeodeGX1" depends on X86_32 @@ -269,12 +292,12 @@ config X86_L1_CACHE_SHIFT int default "7" if MPENTIUM4 default "6" if MK7 || MPENTIUMM || MATOM || MVIAC7 || X86_GENERIC || X86_64 - default "4" if MGEODEGX1 + default "4" if MELAN || M486SX || M486 || MGEODEGX1 default "5" if MWINCHIP3D || MWINCHIPC6 || MCRUSOE || MEFFICEON || MCYRIXIII || MK6 || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODE_LX config X86_F00F_BUG def_bool y - depends on M586MMX || M586TSC || M586 + depends on M586MMX || M586TSC || M586 || M486SX || M486 config X86_INVD_BUG def_bool y @@ -282,7 +305,7 @@ config X86_INVD_BUG config X86_ALIGNMENT_16 def_bool y - depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || MK6 || M586MMX || M586TSC || M586 || MVIAC3_2 || MGEODEGX1 + depends on MWINCHIP3D || MWINCHIPC6 || MCYRIXIII || MELAN || MK6 || M586MMX || M586TSC || M586 || M486SX || M486 || MVIAC3_2 || MGEODEGX1 config X86_INTEL_USERCOPY def_bool y @@ -314,11 +337,12 @@ config X86_MINIMUM_CPU_FAMILY int default "64" if X86_64 default "6" if X86_32 && (MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || MVIAC3_2 || MVIAC7 || MEFFICEON || MATOM || MK7) - default "5" + default "5" if X86_32 && X86_CX8 + default "4" config X86_DEBUGCTLMSR def_bool y - depends on !(MK6 || MWINCHIPC6 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586) && !UML + depends on !(MK6 || MWINCHIPC6 || MWINCHIP3D || MCYRIXIII || M586MMX || M586TSC || M586 || M486SX || M486) && !UML config IA32_FEAT_CTL def_bool y @@ -350,7 +374,7 @@ config CPU_SUP_INTEL config CPU_SUP_CYRIX_32 default y bool "Support Cyrix processors" if PROCESSOR_SELECT - depends on M586 || M586TSC || M586MMX || (EXPERT && !64BIT) + depends on M486SX || M486 || M586 || M586TSC || M586MMX || (EXPERT && !64BIT) help This enables detection, tunings and quirks for Cyrix processors diff --git a/arch/x86/Makefile_32.cpu b/arch/x86/Makefile_32.cpu index 7c9898c1537613..a3dda95e47f412 100644 --- a/arch/x86/Makefile_32.cpu +++ b/arch/x86/Makefile_32.cpu @@ -10,6 +10,8 @@ else align := -falign-functions=0 -falign-jumps=0 -falign-loops=0 endif +cflags-$(CONFIG_M486SX) += -march=i486 +cflags-$(CONFIG_M486) += -march=i486 cflags-$(CONFIG_M586) += -march=i586 cflags-$(CONFIG_M586TSC) += -march=i586 cflags-$(CONFIG_M586MMX) += -march=pentium-mmx diff --git a/arch/x86/include/asm/vermagic.h b/arch/x86/include/asm/vermagic.h index eda233a90ea86a..5d471253c75549 100644 --- a/arch/x86/include/asm/vermagic.h +++ b/arch/x86/include/asm/vermagic.h @@ -5,6 +5,10 @@ #ifdef CONFIG_X86_64 /* X86_64 does not define MODULE_PROC_FAMILY */ +#elif defined CONFIG_M486SX +#define MODULE_PROC_FAMILY "486SX " +#elif defined CONFIG_M486 +#define MODULE_PROC_FAMILY "486 " #elif defined CONFIG_M586 #define MODULE_PROC_FAMILY "586 " #elif defined CONFIG_M586TSC @@ -27,6 +31,8 @@ #define MODULE_PROC_FAMILY "K6 " #elif defined CONFIG_MK7 #define MODULE_PROC_FAMILY "K7 " +#elif defined CONFIG_MELAN +#define MODULE_PROC_FAMILY "ELAN " #elif defined CONFIG_MCRUSOE #define MODULE_PROC_FAMILY "CRUSOE " #elif defined CONFIG_MEFFICEON From 9561ead71976f8f028f3573301ea683f6720e9a8 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 2 Aug 2026 04:19:30 +0800 Subject: [PATCH 255/258] AOSCOS: BACKPORT: REVERT: Revert "Input: logibm - remove driver" This reverts commit 931e3151dba74786f36a948a8d08490f3657c1f3. [ Mingcong Bai: Resolved a minor post-7.0 merge conflict in drivers/input/mouse/Kconfig ] Signed-off-by: Mingcong Bai --- drivers/input/mouse/Kconfig | 10 +++ drivers/input/mouse/Makefile | 1 + drivers/input/mouse/logibm.c | 166 +++++++++++++++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 drivers/input/mouse/logibm.c diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 086b2e9d443e47..8796ebd50ea97d 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -302,6 +302,16 @@ config MOUSE_ELAN_I2C_SMBUS If unsure, say Y. +config MOUSE_LOGIBM + tristate "Logitech busmouse" + depends on ISA + help + Say Y here if you have a Logitech busmouse. + They are rather rare these days. + + To compile this driver as a module, choose M here: the + module will be called logibm. + config MOUSE_AMIGA tristate "Amiga mouse" depends on AMIGA diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 0572a02be2388a..50d5248599723d 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o +obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o diff --git a/drivers/input/mouse/logibm.c b/drivers/input/mouse/logibm.c new file mode 100644 index 00000000000000..0aab63dbc30a32 --- /dev/null +++ b/drivers/input/mouse/logibm.c @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * Based on the work of: + * James Banks Matthew Dillon + * David Giller Nathan Laredo + * Linus Torvalds Johan Myreen + * Cliff Matthews Philip Blundell + * Russell King + */ + +/* + * Logitech Bus Mouse Driver for Linux + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Logitech busmouse driver"); +MODULE_LICENSE("GPL"); + +#define LOGIBM_BASE 0x23c +#define LOGIBM_EXTENT 4 + +#define LOGIBM_DATA_PORT LOGIBM_BASE + 0 +#define LOGIBM_SIGNATURE_PORT LOGIBM_BASE + 1 +#define LOGIBM_CONTROL_PORT LOGIBM_BASE + 2 +#define LOGIBM_CONFIG_PORT LOGIBM_BASE + 3 + +#define LOGIBM_ENABLE_IRQ 0x00 +#define LOGIBM_DISABLE_IRQ 0x10 +#define LOGIBM_READ_X_LOW 0x80 +#define LOGIBM_READ_X_HIGH 0xa0 +#define LOGIBM_READ_Y_LOW 0xc0 +#define LOGIBM_READ_Y_HIGH 0xe0 + +#define LOGIBM_DEFAULT_MODE 0x90 +#define LOGIBM_CONFIG_BYTE 0x91 +#define LOGIBM_SIGNATURE_BYTE 0xa5 + +#define LOGIBM_IRQ 5 + +static int logibm_irq = LOGIBM_IRQ; +module_param_hw_named(irq, logibm_irq, uint, irq, 0); +MODULE_PARM_DESC(irq, "IRQ number (5=default)"); + +static struct input_dev *logibm_dev; + +static irqreturn_t logibm_interrupt(int irq, void *dev_id) +{ + char dx, dy; + unsigned char buttons; + + outb(LOGIBM_READ_X_LOW, LOGIBM_CONTROL_PORT); + dx = (inb(LOGIBM_DATA_PORT) & 0xf); + outb(LOGIBM_READ_X_HIGH, LOGIBM_CONTROL_PORT); + dx |= (inb(LOGIBM_DATA_PORT) & 0xf) << 4; + outb(LOGIBM_READ_Y_LOW, LOGIBM_CONTROL_PORT); + dy = (inb(LOGIBM_DATA_PORT) & 0xf); + outb(LOGIBM_READ_Y_HIGH, LOGIBM_CONTROL_PORT); + buttons = inb(LOGIBM_DATA_PORT); + dy |= (buttons & 0xf) << 4; + buttons = ~buttons >> 5; + + input_report_rel(logibm_dev, REL_X, dx); + input_report_rel(logibm_dev, REL_Y, dy); + input_report_key(logibm_dev, BTN_RIGHT, buttons & 1); + input_report_key(logibm_dev, BTN_MIDDLE, buttons & 2); + input_report_key(logibm_dev, BTN_LEFT, buttons & 4); + input_sync(logibm_dev); + + outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); + return IRQ_HANDLED; +} + +static int logibm_open(struct input_dev *dev) +{ + if (request_irq(logibm_irq, logibm_interrupt, 0, "logibm", NULL)) { + printk(KERN_ERR "logibm.c: Can't allocate irq %d\n", logibm_irq); + return -EBUSY; + } + outb(LOGIBM_ENABLE_IRQ, LOGIBM_CONTROL_PORT); + return 0; +} + +static void logibm_close(struct input_dev *dev) +{ + outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); + free_irq(logibm_irq, NULL); +} + +static int __init logibm_init(void) +{ + int err; + + if (!request_region(LOGIBM_BASE, LOGIBM_EXTENT, "logibm")) { + printk(KERN_ERR "logibm.c: Can't allocate ports at %#x\n", LOGIBM_BASE); + return -EBUSY; + } + + outb(LOGIBM_CONFIG_BYTE, LOGIBM_CONFIG_PORT); + outb(LOGIBM_SIGNATURE_BYTE, LOGIBM_SIGNATURE_PORT); + udelay(100); + + if (inb(LOGIBM_SIGNATURE_PORT) != LOGIBM_SIGNATURE_BYTE) { + printk(KERN_INFO "logibm.c: Didn't find Logitech busmouse at %#x\n", LOGIBM_BASE); + err = -ENODEV; + goto err_release_region; + } + + outb(LOGIBM_DEFAULT_MODE, LOGIBM_CONFIG_PORT); + outb(LOGIBM_DISABLE_IRQ, LOGIBM_CONTROL_PORT); + + logibm_dev = input_allocate_device(); + if (!logibm_dev) { + printk(KERN_ERR "logibm.c: Not enough memory for input device\n"); + err = -ENOMEM; + goto err_release_region; + } + + logibm_dev->name = "Logitech bus mouse"; + logibm_dev->phys = "isa023c/input0"; + logibm_dev->id.bustype = BUS_ISA; + logibm_dev->id.vendor = 0x0003; + logibm_dev->id.product = 0x0001; + logibm_dev->id.version = 0x0100; + + logibm_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + logibm_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + logibm_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + + logibm_dev->open = logibm_open; + logibm_dev->close = logibm_close; + + err = input_register_device(logibm_dev); + if (err) + goto err_free_dev; + + return 0; + + err_free_dev: + input_free_device(logibm_dev); + err_release_region: + release_region(LOGIBM_BASE, LOGIBM_EXTENT); + + return err; +} + +static void __exit logibm_exit(void) +{ + input_unregister_device(logibm_dev); + release_region(LOGIBM_BASE, LOGIBM_EXTENT); +} + +module_init(logibm_init); +module_exit(logibm_exit); From 9b5f5f8598938bc2721f6dc80f1b01f980a4d345 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 2 Aug 2026 04:20:01 +0800 Subject: [PATCH 256/258] AOSCOS: REVERT: Revert "Input: inport - remove driver" This reverts commit 8291ffa3e51d6a280b9348fcf5ac6cf45abd2fb8. Signed-off-by: Mingcong Bai --- drivers/input/mouse/Kconfig | 16 ++++ drivers/input/mouse/Makefile | 1 + drivers/input/mouse/inport.c | 177 +++++++++++++++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100644 drivers/input/mouse/inport.c diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 8796ebd50ea97d..c6027ab07a14b9 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -302,6 +302,22 @@ config MOUSE_ELAN_I2C_SMBUS If unsure, say Y. +config MOUSE_INPORT + tristate "InPort/MS/ATIXL busmouse" + depends on ISA + help + Say Y here if you have an InPort, Microsoft or ATI XL busmouse. + They are rather rare these days. + + To compile this driver as a module, choose M here: the + module will be called inport. + +config MOUSE_ATIXL + bool "ATI XL variant" + depends on MOUSE_INPORT + help + Say Y here if your mouse is of the ATI XL variety. + config MOUSE_LOGIBM tristate "Logitech busmouse" depends on ISA diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 50d5248599723d..6a0829bf487391 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o +obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o diff --git a/drivers/input/mouse/inport.c b/drivers/input/mouse/inport.c new file mode 100644 index 00000000000000..401d8bff8e8423 --- /dev/null +++ b/drivers/input/mouse/inport.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 1999-2001 Vojtech Pavlik + * + * Based on the work of: + * Teemu Rantanen Derrick Cole + * Peter Cervasio Christoph Niemann + * Philip Blundell Russell King + * Bob Harris + */ + +/* + * Inport (ATI XL and Microsoft) busmouse driver for Linux + */ + +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("Inport (ATI XL and Microsoft) busmouse driver"); +MODULE_LICENSE("GPL"); + +#define INPORT_BASE 0x23c +#define INPORT_EXTENT 4 + +#define INPORT_CONTROL_PORT INPORT_BASE + 0 +#define INPORT_DATA_PORT INPORT_BASE + 1 +#define INPORT_SIGNATURE_PORT INPORT_BASE + 2 + +#define INPORT_REG_BTNS 0x00 +#define INPORT_REG_X 0x01 +#define INPORT_REG_Y 0x02 +#define INPORT_REG_MODE 0x07 +#define INPORT_RESET 0x80 + +#ifdef CONFIG_MOUSE_ATIXL +#define INPORT_NAME "ATI XL Mouse" +#define INPORT_VENDOR 0x0002 +#define INPORT_SPEED_30HZ 0x01 +#define INPORT_SPEED_50HZ 0x02 +#define INPORT_SPEED_100HZ 0x03 +#define INPORT_SPEED_200HZ 0x04 +#define INPORT_MODE_BASE INPORT_SPEED_100HZ +#define INPORT_MODE_IRQ 0x08 +#else +#define INPORT_NAME "Microsoft InPort Mouse" +#define INPORT_VENDOR 0x0001 +#define INPORT_MODE_BASE 0x10 +#define INPORT_MODE_IRQ 0x01 +#endif +#define INPORT_MODE_HOLD 0x20 + +#define INPORT_IRQ 5 + +static int inport_irq = INPORT_IRQ; +module_param_hw_named(irq, inport_irq, uint, irq, 0); +MODULE_PARM_DESC(irq, "IRQ number (5=default)"); + +static struct input_dev *inport_dev; + +static irqreturn_t inport_interrupt(int irq, void *dev_id) +{ + unsigned char buttons; + + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_HOLD | INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); + + outb(INPORT_REG_X, INPORT_CONTROL_PORT); + input_report_rel(inport_dev, REL_X, inb(INPORT_DATA_PORT)); + + outb(INPORT_REG_Y, INPORT_CONTROL_PORT); + input_report_rel(inport_dev, REL_Y, inb(INPORT_DATA_PORT)); + + outb(INPORT_REG_BTNS, INPORT_CONTROL_PORT); + buttons = inb(INPORT_DATA_PORT); + + input_report_key(inport_dev, BTN_MIDDLE, buttons & 1); + input_report_key(inport_dev, BTN_LEFT, buttons & 2); + input_report_key(inport_dev, BTN_RIGHT, buttons & 4); + + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); + + input_sync(inport_dev); + return IRQ_HANDLED; +} + +static int inport_open(struct input_dev *dev) +{ + if (request_irq(inport_irq, inport_interrupt, 0, "inport", NULL)) + return -EBUSY; + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_IRQ | INPORT_MODE_BASE, INPORT_DATA_PORT); + + return 0; +} + +static void inport_close(struct input_dev *dev) +{ + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_BASE, INPORT_DATA_PORT); + free_irq(inport_irq, NULL); +} + +static int __init inport_init(void) +{ + unsigned char a, b, c; + int err; + + if (!request_region(INPORT_BASE, INPORT_EXTENT, "inport")) { + printk(KERN_ERR "inport.c: Can't allocate ports at %#x\n", INPORT_BASE); + return -EBUSY; + } + + a = inb(INPORT_SIGNATURE_PORT); + b = inb(INPORT_SIGNATURE_PORT); + c = inb(INPORT_SIGNATURE_PORT); + if (a == b || a != c) { + printk(KERN_INFO "inport.c: Didn't find InPort mouse at %#x\n", INPORT_BASE); + err = -ENODEV; + goto err_release_region; + } + + inport_dev = input_allocate_device(); + if (!inport_dev) { + printk(KERN_ERR "inport.c: Not enough memory for input device\n"); + err = -ENOMEM; + goto err_release_region; + } + + inport_dev->name = INPORT_NAME; + inport_dev->phys = "isa023c/input0"; + inport_dev->id.bustype = BUS_ISA; + inport_dev->id.vendor = INPORT_VENDOR; + inport_dev->id.product = 0x0001; + inport_dev->id.version = 0x0100; + + inport_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); + inport_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | + BIT_MASK(BTN_MIDDLE) | BIT_MASK(BTN_RIGHT); + inport_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y); + + inport_dev->open = inport_open; + inport_dev->close = inport_close; + + outb(INPORT_RESET, INPORT_CONTROL_PORT); + outb(INPORT_REG_MODE, INPORT_CONTROL_PORT); + outb(INPORT_MODE_BASE, INPORT_DATA_PORT); + + err = input_register_device(inport_dev); + if (err) + goto err_free_dev; + + return 0; + + err_free_dev: + input_free_device(inport_dev); + err_release_region: + release_region(INPORT_BASE, INPORT_EXTENT); + + return err; +} + +static void __exit inport_exit(void) +{ + input_unregister_device(inport_dev); + release_region(INPORT_BASE, INPORT_EXTENT); +} + +module_init(inport_init); +module_exit(inport_exit); From ef22842dc79ef6a4f8320acf5cc23c087fe82fdd Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 2 Aug 2026 04:20:06 +0800 Subject: [PATCH 257/258] AOSCOS: AOSCOS: Revert "Input: pc110pad - remove driver" This reverts commit 6f468ea360f0a6a1e45854afbc3019842ed891a8. Signed-off-by: Mingcong Bai --- drivers/input/mouse/Kconfig | 10 +++ drivers/input/mouse/Makefile | 1 + drivers/input/mouse/pc110pad.c | 160 +++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 drivers/input/mouse/pc110pad.c diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index c6027ab07a14b9..e6ea04353eb5e9 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -328,6 +328,16 @@ config MOUSE_LOGIBM To compile this driver as a module, choose M here: the module will be called logibm. +config MOUSE_PC110PAD + tristate "IBM PC110 touchpad" + depends on ISA + help + Say Y if you have the IBM PC-110 micro-notebook and want its + touchpad supported. + + To compile this driver as a module, choose M here: the + module will be called pc110pad. + config MOUSE_AMIGA tristate "Amiga mouse" depends on AMIGA diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 6a0829bf487391..0918230e9e2436 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o +obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o diff --git a/drivers/input/mouse/pc110pad.c b/drivers/input/mouse/pc110pad.c new file mode 100644 index 00000000000000..efa58049f746ee --- /dev/null +++ b/drivers/input/mouse/pc110pad.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * Alan Cox Robin O'Leary + */ + +/* + * IBM PC110 touchpad driver for Linux + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_DESCRIPTION("IBM PC110 touchpad driver"); +MODULE_LICENSE("GPL"); + +#define PC110PAD_OFF 0x30 +#define PC110PAD_ON 0x38 + +static int pc110pad_irq = 10; +static int pc110pad_io = 0x15e0; + +static struct input_dev *pc110pad_dev; +static int pc110pad_data[3]; +static int pc110pad_count; + +static irqreturn_t pc110pad_interrupt(int irq, void *ptr) +{ + int value = inb_p(pc110pad_io); + int handshake = inb_p(pc110pad_io + 2); + + outb(handshake | 1, pc110pad_io + 2); + udelay(2); + outb(handshake & ~1, pc110pad_io + 2); + udelay(2); + inb_p(0x64); + + pc110pad_data[pc110pad_count++] = value; + + if (pc110pad_count < 3) + return IRQ_HANDLED; + + input_report_key(pc110pad_dev, BTN_TOUCH, + pc110pad_data[0] & 0x01); + input_report_abs(pc110pad_dev, ABS_X, + pc110pad_data[1] | ((pc110pad_data[0] << 3) & 0x80) | ((pc110pad_data[0] << 1) & 0x100)); + input_report_abs(pc110pad_dev, ABS_Y, + pc110pad_data[2] | ((pc110pad_data[0] << 4) & 0x80)); + input_sync(pc110pad_dev); + + pc110pad_count = 0; + return IRQ_HANDLED; +} + +static void pc110pad_close(struct input_dev *dev) +{ + outb(PC110PAD_OFF, pc110pad_io + 2); +} + +static int pc110pad_open(struct input_dev *dev) +{ + pc110pad_interrupt(0, NULL); + pc110pad_interrupt(0, NULL); + pc110pad_interrupt(0, NULL); + outb(PC110PAD_ON, pc110pad_io + 2); + pc110pad_count = 0; + + return 0; +} + +/* + * We try to avoid enabling the hardware if it's not + * there, but we don't know how to test. But we do know + * that the PC110 is not a PCI system. So if we find any + * PCI devices in the machine, we don't have a PC110. + */ +static int __init pc110pad_init(void) +{ + int err; + + if (!no_pci_devices()) + return -ENODEV; + + if (!request_region(pc110pad_io, 4, "pc110pad")) { + printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n", + pc110pad_io, pc110pad_io + 4); + return -EBUSY; + } + + outb(PC110PAD_OFF, pc110pad_io + 2); + + if (request_irq(pc110pad_irq, pc110pad_interrupt, 0, "pc110pad", NULL)) { + printk(KERN_ERR "pc110pad: Unable to get irq %d.\n", pc110pad_irq); + err = -EBUSY; + goto err_release_region; + } + + pc110pad_dev = input_allocate_device(); + if (!pc110pad_dev) { + printk(KERN_ERR "pc110pad: Not enough memory.\n"); + err = -ENOMEM; + goto err_free_irq; + } + + pc110pad_dev->name = "IBM PC110 TouchPad"; + pc110pad_dev->phys = "isa15e0/input0"; + pc110pad_dev->id.bustype = BUS_ISA; + pc110pad_dev->id.vendor = 0x0003; + pc110pad_dev->id.product = 0x0001; + pc110pad_dev->id.version = 0x0100; + + pc110pad_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + pc110pad_dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y); + pc110pad_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_abs_set_max(pc110pad_dev, ABS_X, 0x1ff); + input_abs_set_max(pc110pad_dev, ABS_Y, 0x0ff); + + pc110pad_dev->open = pc110pad_open; + pc110pad_dev->close = pc110pad_close; + + err = input_register_device(pc110pad_dev); + if (err) + goto err_free_dev; + + return 0; + + err_free_dev: + input_free_device(pc110pad_dev); + err_free_irq: + free_irq(pc110pad_irq, NULL); + err_release_region: + release_region(pc110pad_io, 4); + + return err; +} + +static void __exit pc110pad_exit(void) +{ + outb(PC110PAD_OFF, pc110pad_io + 2); + free_irq(pc110pad_irq, NULL); + input_unregister_device(pc110pad_dev); + release_region(pc110pad_io, 4); +} + +module_init(pc110pad_init); +module_exit(pc110pad_exit); From dea982d8bc33dca92302f98a1b78711523e89d4c Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Sun, 2 Aug 2026 09:45:35 +0800 Subject: [PATCH 258/258] AOSCOS: REVERT: Revert "PCI: Remove no_pci_devices()" This reverts commit d79dc408deb6c192adbad7893ee0c22d50826511. Fixes: "AOSCOS: AOSCOS: Revert "Input: pc110pad - remove driver"" Signed-off-by: Mingcong Bai --- drivers/pci/probe.c | 17 +++++++++++++++++ include/linux/pci.h | 3 +++ 2 files changed, 20 insertions(+) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b63cd0c310bc0f..eaa4a3d662e8d3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -67,6 +67,23 @@ static struct resource *get_pci_domain_busn_res(int domain_nr) return &r->res; } +/* + * Some device drivers need know if PCI is initiated. + * Basically, we think PCI is not initiated when there + * is no device to be found on the pci_bus_type. + */ +int no_pci_devices(void) +{ + struct device *dev; + int no_devices; + + dev = bus_find_next_device(&pci_bus_type, NULL); + no_devices = (dev == NULL); + put_device(dev); + return no_devices; +} +EXPORT_SYMBOL(no_pci_devices); + /* * PCI Bus Class */ diff --git a/include/linux/pci.h b/include/linux/pci.h index bfae14bc5e62a1..cc56689049dc30 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1200,6 +1200,8 @@ extern const struct bus_type pci_bus_type; /* Do NOT directly access these two variables, unless you are arch-specific PCI * code, or PCI core code. */ extern struct list_head pci_root_buses; /* List of all known PCI buses */ +/* Some device drivers need know if PCI is initiated */ +int no_pci_devices(void); void pcibios_resource_survey_bus(struct pci_bus *bus); void pcibios_bus_add_device(struct pci_dev *pdev); @@ -2143,6 +2145,7 @@ static inline struct pci_dev *pci_get_base_class(unsigned int class, static inline int pci_dev_present(const struct pci_device_id *ids) { return 0; } +#define no_pci_devices() (1) #define pci_dev_put(dev) do { } while (0) static inline void pci_set_master(struct pci_dev *dev) { }