From a974468f012b9f84fdbf35b8d12ec0eda9692d54 Mon Sep 17 00:00:00 2001 From: Lei Yilong Date: Wed, 8 May 2024 15:48:21 +0800 Subject: [PATCH 01/25] anolis: sw64: change FORCE_MAX_ZONEORDER default value ANBZ: #4688 FORCE_MAX_ZONEORDER=16 is used for 256M hugepage on C3B. To support guest memory hotplug, change its default value to 11 for C4. Signed-off-by: Lei Yilong Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sw_64/Kconfig b/arch/sw_64/Kconfig index fd36da7add8f..5756c27234ef 100644 --- a/arch/sw_64/Kconfig +++ b/arch/sw_64/Kconfig @@ -626,7 +626,7 @@ endchoice config FORCE_MAX_ZONEORDER int - default "16" if (HUGETLB_PAGE) + default "16" if (HUGETLB_PAGE && SUBARCH_C3B) default "11" help The kernel memory allocator divides physically contiguous memory -- Gitee From 580c8938abebe5dd2b497a1c3eeaa7cc59b61fee Mon Sep 17 00:00:00 2001 From: Lei Yilong Date: Wed, 8 May 2024 15:56:13 +0800 Subject: [PATCH 02/25] anolis: sw64: fix usage of __add_memory() in sunway-ged driver ANBZ: #4688 This driver has not been tested yet. Add the missing fourth argument mhp_flags which is MHP_NONE. Fixes: 51f31c3271e5 ("anolis: sw64: add memhotplug support for guest os") Signed-off-by: Lei Yilong Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- drivers/misc/sunway-ged.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/sunway-ged.c b/drivers/misc/sunway-ged.c index b4e4ca315852..5c397cb454c9 100644 --- a/drivers/misc/sunway-ged.c +++ b/drivers/misc/sunway-ged.c @@ -58,7 +58,7 @@ static int sunway_memory_enable_device(struct sunway_memory_device *mem_device) lock_device_hotplug(); /* suppose node = 0, fix me! */ - result = __add_memory(0, mem_device->start_addr, mem_device->length); + result = __add_memory(0, mem_device->start_addr, mem_device->length, MHP_NONE); unlock_device_hotplug(); /* * If the memory block has been used by the kernel, add_memory() -- Gitee From eb738613226cb3303a6d2440e71c9cc6d6f8a7e4 Mon Sep 17 00:00:00 2001 From: Lei Yilong Date: Wed, 8 May 2024 19:10:53 +0800 Subject: [PATCH 03/25] anolis: sw64: fix compile warning of handle_pci_msi_interrupt ANBZ: #4688 Fix the following warnings: drivers/irqchip/irq-sunway-msi-v2.c: In function 'handle_pci_msi_interrupt': drivers/irqchip/irq-sunway-msi-v2.c:461:9: warning: unused variable 'irq' [-Wunused-variable] int i, irq, msi_index = 0; ^~~ Fixes: f9d96be59d6c ("anolis: sw64: msi: split the msi file") Signed-off-by: Lei Yilong Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- drivers/irqchip/irq-sunway-msi-v2.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/irqchip/irq-sunway-msi-v2.c b/drivers/irqchip/irq-sunway-msi-v2.c index 6f5c921fc520..2beacb1be39d 100644 --- a/drivers/irqchip/irq-sunway-msi-v2.c +++ b/drivers/irqchip/irq-sunway-msi-v2.c @@ -489,8 +489,6 @@ void handle_pci_msi_interrupt(unsigned long type, unsigned long vector, unsigned for (i = 0; i < 4; i++) { vector_index = i * 64; while (vector != 0) { - int irq = 0; - msi_index = find_next_bit(&vector, 64, msi_index); if (msi_index == 64) { msi_index = 0; -- Gitee From 8f896650ce72437d5ec7910db4fe6330a71f5be1 Mon Sep 17 00:00:00 2001 From: Xu Yiwei Date: Wed, 8 May 2024 14:29:07 +0000 Subject: [PATCH 04/25] anolis: sw64: fix typo in gpio-sunway ANBZ: #4688 Fix a typo in gpio-sunway.c, which is causing failed GPIO compilation. Signed-off-by: Xu Yiwei Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- drivers/gpio/gpio-sunway.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-sunway.c b/drivers/gpio/gpio-sunway.c index ecb87c41dc65..0fd113ff6b11 100644 --- a/drivers/gpio/gpio-sunway.c +++ b/drivers/gpio/gpio-sunway.c @@ -730,7 +730,7 @@ static int sunway_gpio_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, gpio); - def_info(&pdev->dev, "GPIO probe succeed\n"); + dev_info(&pdev->dev, "GPIO probe succeed\n"); return 0; -- Gitee From ffdeeda7962065fb4dc0c352f59513ad8e8f7e77 Mon Sep 17 00:00:00 2001 From: Xu Chenjiao Date: Thu, 9 May 2024 09:43:19 +0000 Subject: [PATCH 05/25] anolis: sw64: lpc: fix compile error with CONFIG_SUNWAY_SUPERIO_AST2400=y ANBZ: #4688 This patch fixes undefined reference to DRIVER_NAME. Signed-off-by: Xu Chenjiao Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- drivers/mfd/sunway_ast2400.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/sunway_ast2400.c b/drivers/mfd/sunway_ast2400.c index ea981d9d0215..4e1e3e3d21e2 100644 --- a/drivers/mfd/sunway_ast2400.c +++ b/drivers/mfd/sunway_ast2400.c @@ -22,6 +22,7 @@ #include #include +#define DRIVER_NAME "sunway_superio_ast2400" static int superio_uart0_irq; static int superio_uart1_irq; @@ -199,7 +200,7 @@ static struct platform_driver superio_nuvoton_ast2400_driver = { .probe = superio_ast2400_probe, .remove = superio_ast2400_remove, .driver = { - .name = "sunway_superio_ast2400" + .name = DRIVER_NAME }, }; -- Gitee From 7fe422e5080aa1d46ce4b88aba9947f3d2e1b230 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Tue, 16 Apr 2024 10:30:15 +0800 Subject: [PATCH 06/25] anolis: sw64: mm: take memory information from firmware ANBZ: #4688 Currently, we directly detect the memory via I/O operations in kernel. Considering that it is the responsibility of the firmware to mask hardware differences, it is best for the firmware to detect memory and pass the memory information to kernel via a standard interface. The standard interface stated above mainly contains two aspects: 1. UEFI memory map If UEFI BIOS is used to boot kernel, the UEFI BIOS is responsible for detecting usable physical memory and passing it to kernel via UEFI memory map. 2. Memory node in device tree If UEFI BIOS is not used, the firmware in use is responsible for detecting usable physical memory and passing it to kernel via memory node in device tree. An example of a memory node is as follows: memory@0 { device_type = "memory"; reg = ; /* base address and size */ numa-node-id = <0>; /* memory of NUMA node 0 */ } This commit brings two changes: 1. Take memory information from firmware when receive the boot magic "0xDEED2024". 2. Some refactoring, moving memblock related initialization from setup.c to other files. Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/include/asm/kexec.h | 10 ++ arch/sw_64/include/asm/memory.h | 1 - arch/sw_64/kernel/machine_kexec.c | 69 ++++++++- arch/sw_64/kernel/setup.c | 233 ++---------------------------- arch/sw_64/mm/init.c | 219 +++++++++++++++++++--------- 5 files changed, 241 insertions(+), 291 deletions(-) diff --git a/arch/sw_64/include/asm/kexec.h b/arch/sw_64/include/asm/kexec.h index 25e0d8da84f8..1a63899f9c11 100644 --- a/arch/sw_64/include/asm/kexec.h +++ b/arch/sw_64/include/asm/kexec.h @@ -72,11 +72,21 @@ static inline void crash_setup_regs(struct pt_regs *newregs, /* Function pointer to optional machine-specific reinitialization */ extern void (*kexec_reinit)(void); +extern void __init reserve_crashkernel(void); +extern void __init kexec_control_page_init(void); + #endif /* __ASSEMBLY__ */ struct kimage; extern unsigned long kexec_args[4]; +#else /* CONFIG_KEXEC */ + +#ifndef __ASSEMBLY__ +static inline void __init reserve_crashkernel(void) {} +static inline void __init kexec_control_page_init(void) {} +#endif + #endif /* CONFIG_KEXEC */ #endif /* _ASM_SW64_KEXEC_H */ diff --git a/arch/sw_64/include/asm/memory.h b/arch/sw_64/include/asm/memory.h index b2b7492ae477..65e5f8f79727 100644 --- a/arch/sw_64/include/asm/memory.h +++ b/arch/sw_64/include/asm/memory.h @@ -26,7 +26,6 @@ struct numa_node_desc_t { extern struct numa_node_desc_t numa_nodes_desc[]; void __init callback_init(void); -void __init mem_detect(void); void __init sw64_memblock_init(void); void __init zone_sizes_init(void); void __init sw64_numa_init(void); diff --git a/arch/sw_64/kernel/machine_kexec.c b/arch/sw_64/kernel/machine_kexec.c index dc1b2b4f5949..a85cca14444b 100644 --- a/arch/sw_64/kernel/machine_kexec.c +++ b/arch/sw_64/kernel/machine_kexec.c @@ -14,11 +14,11 @@ #include #include #include +#include #include #include -extern void *kexec_control_page; extern const unsigned char relocate_new_kernel[]; extern const size_t relocate_new_kernel_size; @@ -26,6 +26,7 @@ extern unsigned long kexec_start_address; extern unsigned long kexec_indirection_page; static atomic_t waiting_for_crash_ipi; +static void *kexec_control_page; #ifdef CONFIG_SMP extern struct smp_rcb_struct *smp_rcb; @@ -46,6 +47,72 @@ static void kexec_smp_down(void *ignored) } #endif +#define KTEXT_MAX KERNEL_IMAGE_SIZE + +void __init kexec_control_page_init(void) +{ + phys_addr_t addr; + + addr = memblock_phys_alloc_range(KEXEC_CONTROL_PAGE_SIZE, PAGE_SIZE, + 0, KTEXT_MAX); + kexec_control_page = (void *)(__START_KERNEL_map + addr); +} + +/* + * reserve_crashkernel() - reserves memory are for crash kernel + * + * This function reserves memory area given in "crashkernel=" kernel command + * line parameter. The memory reserved is used by a dump capture kernel when + * primary kernel is crashing. + */ +void __init reserve_crashkernel(void) +{ + unsigned long long crash_size, crash_base; + unsigned long long mem_size = memblock_phys_mem_size(); + int ret; + + ret = parse_crashkernel(boot_command_line, mem_size, + &crash_size, &crash_base); + if (ret || !crash_size) + return; + + if (!crash_size) { + pr_warn("size of crash kernel memory unspecified, no memory reserved for crash kernel\n"); + return; + } + if (!crash_base) { + pr_warn("base of crash kernel memory unspecified, no memory reserved for crash kernel\n"); + return; + } + + if (!memblock_is_region_memory(crash_base, crash_size)) + memblock_add(crash_base, crash_size); + + ret = memblock_reserve(crash_base, crash_size); + if (ret < 0) { + pr_warn("crashkernel reservation failed - memory is in use [mem %#018llx-%#018llx]\n", + crash_base, crash_base + crash_size - 1); + return; + } + + pr_info("Reserving %ldMB of memory at %ldMB for crashkernel (System RAM: %ldMB)\n", + (unsigned long)(crash_size >> 20), + (unsigned long)(crash_base >> 20), + (unsigned long)(mem_size >> 20)); + + ret = add_memmap_region(crash_base, crash_size, memmap_crashkernel); + if (ret) + pr_warn("Add crash kernel area [mem %#018llx-%#018llx] to memmap region failed.\n", + crash_base, crash_base + crash_size - 1); + + if (crash_base >= KERNEL_IMAGE_SIZE) + pr_warn("Crash base should be less than %#x\n", KERNEL_IMAGE_SIZE); + + crashk_res.start = crash_base; + crashk_res.end = crash_base + crash_size - 1; + insert_resource(&iomem_resource, &crashk_res); +} + int machine_kexec_prepare(struct kimage *kimage) { return 0; diff --git a/arch/sw_64/kernel/setup.c b/arch/sw_64/kernel/setup.c index 6505fe0486aa..95162a3d073f 100644 --- a/arch/sw_64/kernel/setup.c +++ b/arch/sw_64/kernel/setup.c @@ -20,16 +20,15 @@ #include #include #include -#include #include #include #include -#include #include #include #include #include +#include #include "proto.h" @@ -46,19 +45,6 @@ EXPORT_SYMBOL(__cpu_to_rcid); DEFINE_PER_CPU(unsigned long, hard_node_id) = { 0 }; static DEFINE_PER_CPU(struct cpu, cpu_devices); -#ifdef CONFIG_SUBARCH_C3B -#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) -struct cma *sw64_kvm_cma; -EXPORT_SYMBOL(sw64_kvm_cma); - -static phys_addr_t kvm_mem_size; -static phys_addr_t kvm_mem_base; - -struct gen_pool *sw64_kvm_pool; -EXPORT_SYMBOL(sw64_kvm_pool); -#endif -#endif - static inline int phys_addr_valid(unsigned long addr) { /* @@ -159,79 +145,6 @@ void store_cpu_data(int cpu) cpu_data[cpu].last_asid = ASID_FIRST_VERSION; } -#ifdef CONFIG_KEXEC - -void *kexec_control_page; - -#define KTEXT_MAX KERNEL_IMAGE_SIZE - -static void __init kexec_control_page_init(void) -{ - phys_addr_t addr; - - addr = memblock_phys_alloc_range(KEXEC_CONTROL_PAGE_SIZE, PAGE_SIZE, - 0, KTEXT_MAX); - kexec_control_page = (void *)(__START_KERNEL_map + addr); -} - -/* - * reserve_crashkernel() - reserves memory are for crash kernel - * - * This function reserves memory area given in "crashkernel=" kernel command - * line parameter. The memory reserved is used by a dump capture kernel when - * primary kernel is crashing. - */ -static void __init reserve_crashkernel(void) -{ - unsigned long long crash_size, crash_base; - int ret; - - ret = parse_crashkernel(boot_command_line, mem_desc.size, - &crash_size, &crash_base); - if (ret || !crash_size) - return; - - if (!crash_size) { - pr_warn("size of crash kernel memory unspecified, no memory reserved for crash kernel\n"); - return; - } - if (!crash_base) { - pr_warn("base of crash kernel memory unspecified, no memory reserved for crash kernel\n"); - return; - } - - if (!memblock_is_region_memory(crash_base, crash_size)) - memblock_add(crash_base, crash_size); - - ret = memblock_reserve(crash_base, crash_size); - if (ret < 0) { - pr_warn("crashkernel reservation failed - memory is in use [mem %#018llx-%#018llx]\n", - crash_base, crash_base + crash_size - 1); - return; - } - - pr_info("Reserving %ldMB of memory at %ldMB for crashkernel (System RAM: %ldMB)\n", - (unsigned long)(crash_size >> 20), - (unsigned long)(crash_base >> 20), - (unsigned long)(mem_desc.size >> 20)); - - ret = add_memmap_region(crash_base, crash_size, memmap_crashkernel); - if (ret) - pr_warn("Add crash kernel area [mem %#018llx-%#018llx] to memmap region failed.\n", - crash_base, crash_base + crash_size - 1); - - if (crash_base >= KERNEL_IMAGE_SIZE) - pr_warn("Crash base should be less than %#x\n", KERNEL_IMAGE_SIZE); - - crashk_res.start = crash_base; - crashk_res.end = crash_base + crash_size - 1; - insert_resource(&iomem_resource, &crashk_res); -} -#else /* !defined(CONFIG_KEXEC) */ -static void __init reserve_crashkernel(void) {} -static void __init kexec_control_page_init(void) {} -#endif /* !defined(CONFIG_KEXEC) */ - /* * I/O resources inherited from PeeCees. Except for perhaps the * turbochannel SWs, everyone has these on some sort of SuperIO chip. @@ -488,26 +401,6 @@ subsys_initcall(request_standard_resources); extern void cpu_set_node(void); #endif -static void __init show_socket_mem_layout(void) -{ - int i; - phys_addr_t base, size, end; - - base = 0; - - pr_info("Socket memory layout:\n"); - for (i = 0; i < MAX_NUMSOCKETS; i++) { - if (socket_desc[i].is_online) { - size = socket_desc[i].socket_mem; - end = base + size - 1; - pr_info("Socket %d: [mem %#018llx-%#018llx], size %llu\n", - i, base, end, size); - base = end + 1; - } - } - pr_info("Reserved memory size for Socket 0: %#lx\n", NODE0_START); -} - int page_is_ram(unsigned long pfn) { pfn <<= PAGE_SHIFT; @@ -592,16 +485,14 @@ static void __init setup_firmware_fdt(void) goto cmd_handle; dt_virt = (void *)sunway_boot_params->dtb_start; } else { - /** - * Use DTB provided by firmware for early initialization, - * regardless of whether a Built-in DTB configured or not. - * Since we need the boot params from firmware when using - * new method to pass boot params. - */ + /* Use DTB provided by firmware for early initialization */ pr_info("Parse boot params in DTB chosen node\n"); dt_virt = (void *)sunway_dtb_address; } + /* reserve the DTB from firmware in case it is used later */ + memblock_reserve(__boot_pa(dt_virt), fdt_totalsize(dt_virt)); + if (!arch_dtb_verify(dt_virt, true) || !early_init_dt_scan(dt_virt)) { pr_crit("Invalid DTB(from firmware) at virtual address 0x%lx\n", @@ -791,45 +682,6 @@ static void __init setup_run_mode(void) } #endif -static void __init setup_socket_info(void) -{ - int i; - int numsockets = sw64_chip->get_cpu_num(); - - memset(socket_desc, 0, MAX_NUMSOCKETS * sizeof(struct socket_desc_t)); - - for (i = 0; i < numsockets; i++) { - socket_desc[i].is_online = 1; - if (sw64_chip_init->early_init.get_node_mem) - socket_desc[i].socket_mem = sw64_chip_init->early_init.get_node_mem(i); - } -} - -#ifdef CONFIG_SUBARCH_C3B -#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) -static int __init early_kvm_reserved_mem(char *p) -{ - if (!p) { - pr_err("Config string not provided\n"); - return -EINVAL; - } - - kvm_mem_size = memparse(p, &p); - if (*p != '@') - return -EINVAL; - kvm_mem_base = memparse(p + 1, &p); - return 0; -} -early_param("kvm_mem", early_kvm_reserved_mem); - -void __init sw64_kvm_reserve(void) -{ - kvm_cma_declare_contiguous(kvm_mem_base, kvm_mem_size, 0, - PAGE_SIZE, 0, "sw64_kvm_cma", &sw64_kvm_cma); -} -#endif -#endif - void __init setup_arch(char **cmdline_p) { @@ -843,8 +695,6 @@ setup_arch(char **cmdline_p) setup_cpu_info(); setup_run_mode(); setup_chip_ops(); - setup_socket_info(); - show_socket_mem_layout(); sw64_chip_init->early_init.setup_core_map(&core_start); if (is_guest_or_emul()) get_vt_smp_info(); @@ -868,30 +718,19 @@ setup_arch(char **cmdline_p) */ parse_early_param(); - /* Find our memory. */ - mem_detect(); - -#ifdef CONFIG_PCI - reserve_mem_for_pci(); -#endif - - sw64_memblock_init(); - - reserve_crashkernel(); - - /* Reserve large chunks of memory for use by CMA for KVM. */ -#ifdef CONFIG_SUBARCH_C3B -#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) - sw64_kvm_reserve(); -#endif -#endif - efi_init(); - /* After efi initialization, switch to Builtin-in DTB if configured */ + /** + * Switch to builtin-in DTB if configured. + * Must be placed after efi_init(), Since + * efi_init() may parse boot params from DTB + * provided by firmware. + */ if (IS_ENABLED(CONFIG_BUILTIN_DTB)) setup_builtin_fdt(); + sw64_memblock_init(); + /* Try to upgrade ACPI tables via initrd */ acpi_table_upgrade(); @@ -1096,49 +935,3 @@ static int __init sw64_of_init(void) core_initcall(sw64_of_init); #endif -#ifdef CONFIG_SUBARCH_C3B -#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) -static int __init sw64_kvm_pool_init(void) -{ - int status = 0; - unsigned long kvm_pool_virt; - struct page *base_page, *end_page, *p; - - if (!sw64_kvm_cma) - goto out; - - kvm_pool_virt = (unsigned long)kvm_mem_base; - - sw64_kvm_pool = gen_pool_create(PAGE_SHIFT, -1); - if (!sw64_kvm_pool) - goto out; - - status = gen_pool_add_virt(sw64_kvm_pool, kvm_pool_virt, kvm_mem_base, - kvm_mem_size, -1); - if (status < 0) { - pr_err("failed to add memory chunks to sw64 kvm pool\n"); - gen_pool_destroy(sw64_kvm_pool); - sw64_kvm_pool = NULL; - goto out; - } - gen_pool_set_algo(sw64_kvm_pool, gen_pool_best_fit, NULL); - - base_page = pfn_to_page(kvm_mem_base >> PAGE_SHIFT); - end_page = pfn_to_page((kvm_mem_base + kvm_mem_size - 1) >> PAGE_SHIFT); - - p = base_page; - while (p <= end_page && page_ref_count(p) == 0) { - set_page_count(p, 1); - page_mapcount_reset(p); - SetPageReserved(p); - p++; - } - - return status; - -out: - return -ENOMEM; -} -core_initcall_sync(sw64_kvm_pool_init); -#endif -#endif diff --git a/arch/sw_64/mm/init.c b/arch/sw_64/mm/init.c index 8acd7f8b3661..5a795d58db81 100644 --- a/arch/sw_64/mm/init.c +++ b/arch/sw_64/mm/init.c @@ -14,10 +14,14 @@ #include #include #include +#include #include #include #include +#include +#include +#include struct mem_desc_t mem_desc; #ifndef CONFIG_NUMA @@ -138,7 +142,41 @@ void __init paging_init(void) { } -void __init mem_detect(void) +static void __init setup_socket_info(void) +{ + int i; + int numsockets = sw64_chip->get_cpu_num(); + + memset(socket_desc, 0, MAX_NUMSOCKETS * sizeof(struct socket_desc_t)); + + for (i = 0; i < numsockets; i++) { + socket_desc[i].is_online = 1; + if (sw64_chip_init->early_init.get_node_mem) + socket_desc[i].socket_mem = sw64_chip_init->early_init.get_node_mem(i); + } +} + +static void __init show_socket_mem_layout(void) +{ + int i; + phys_addr_t base, size, end; + + base = 0; + + pr_info("Socket memory layout:\n"); + for (i = 0; i < MAX_NUMSOCKETS; i++) { + if (socket_desc[i].is_online) { + size = socket_desc[i].socket_mem; + end = base + size - 1; + pr_info("Socket %d: [mem %#018llx-%#018llx], size %llu\n", + i, base, end, size); + base = end + 1; + } + } + pr_info("Reserved memory size for Socket 0: %#lx\n", NODE0_START); +} + +static void __init mem_detect(void) { int i; @@ -216,14 +254,110 @@ static void __init reserve_mem_for_initrd(void) } #endif /* CONFIG_BLK_DEV_INITRD */ +#ifdef CONFIG_SUBARCH_C3B +#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) +struct cma *sw64_kvm_cma; +EXPORT_SYMBOL(sw64_kvm_cma); + +static phys_addr_t kvm_mem_size; +static phys_addr_t kvm_mem_base; + +struct gen_pool *sw64_kvm_pool; +EXPORT_SYMBOL(sw64_kvm_pool); + +static int __init early_kvm_reserved_mem(char *p) +{ + if (!p) { + pr_err("Config string not provided\n"); + return -EINVAL; + } + + kvm_mem_size = memparse(p, &p); + if (*p != '@') + return -EINVAL; + kvm_mem_base = memparse(p + 1, &p); + return 0; +} +early_param("kvm_mem", early_kvm_reserved_mem); + +void __init sw64_kvm_reserve(void) +{ + kvm_cma_declare_contiguous(kvm_mem_base, kvm_mem_size, 0, + PAGE_SIZE, 0, "sw64_kvm_cma", &sw64_kvm_cma); +} + +static int __init sw64_kvm_pool_init(void) +{ + int status = 0; + unsigned long kvm_pool_virt; + struct page *base_page, *end_page, *p; + + if (!sw64_kvm_cma) + goto out; + + kvm_pool_virt = (unsigned long)kvm_mem_base; + + sw64_kvm_pool = gen_pool_create(PAGE_SHIFT, -1); + if (!sw64_kvm_pool) + goto out; + + status = gen_pool_add_virt(sw64_kvm_pool, kvm_pool_virt, kvm_mem_base, + kvm_mem_size, -1); + if (status < 0) { + pr_err("failed to add memory chunks to sw64 kvm pool\n"); + gen_pool_destroy(sw64_kvm_pool); + sw64_kvm_pool = NULL; + goto out; + } + gen_pool_set_algo(sw64_kvm_pool, gen_pool_best_fit, NULL); + + base_page = pfn_to_page(kvm_mem_base >> PAGE_SHIFT); + end_page = pfn_to_page((kvm_mem_base + kvm_mem_size - 1) >> PAGE_SHIFT); + + p = base_page; + while (p <= end_page && page_ref_count(p) == 0) { + set_page_count(p, 1); + page_mapcount_reset(p); + SetPageReserved(p); + p++; + } + + return status; + +out: + return -ENOMEM; +} +core_initcall_sync(sw64_kvm_pool_init); +#endif +#endif + void __init sw64_memblock_init(void) { - memblock_add(mem_desc.base, mem_desc.size); + /** + * Detect all memory on all nodes, used in the following + * cases: + * 1. Legacy memory detect + * 2. Legacy NUMA initialization + */ + setup_socket_info(); + show_socket_mem_layout(); + + if (sunway_boot_magic != 0xDEED2024UL) { + /* Find our usable memory */ + mem_detect(); + + /* Add usable memory */ + memblock_add(mem_desc.base, mem_desc.size); + } memblock_remove(1ULL << MAX_PHYSMEM_BITS, PHYS_ADDR_MAX); max_pfn = max_low_pfn = PFN_DOWN(memblock_end_of_DRAM()); +#ifdef CONFIG_PCI + reserve_mem_for_pci(); +#endif + memblock_allow_resize(); memblock_initialized = true; process_memmap(); @@ -236,17 +370,15 @@ void __init sw64_memblock_init(void) /* Make sure initrd is in memory range. */ reserve_mem_for_initrd(); #endif - /** - * When using non built-in DTB, we need to reserve - * it but avoid using early_init_fdt_reserve_self() - * since __pa() does not work for some old firmware. - * - * We can use early_init_fdt_reserve_self() instead - * when kernel no longer support C3B. - */ - if (!IS_ENABLED(CONFIG_BUILTIN_DTB)) - memblock_reserve(__boot_pa(initial_boot_params), - fdt_totalsize(initial_boot_params)); + +#ifdef CONFIG_SUBARCH_C3B +#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE) + /* Reserve large chunks of memory for use by CMA for KVM. */ + sw64_kvm_reserve(); +#endif +#endif + + reserve_crashkernel(); /* end of DRAM range may have been changed */ max_pfn = max_low_pfn = PFN_DOWN(memblock_end_of_DRAM()); @@ -255,12 +387,14 @@ void __init sw64_memblock_init(void) #ifndef CONFIG_NUMA void __init sw64_numa_init(void) { + phys_addr_t mem_base = memblock_start_of_DRAM(); + phys_addr_t mem_size = memblock_phys_mem_size(); const size_t nd_size = roundup(sizeof(pg_data_t), SMP_CACHE_BYTES); u64 nd_pa; void *nd; int tnid; - memblock_set_node(mem_desc.base, mem_desc.size, &memblock.memory, 0); + memblock_set_node(mem_base, mem_size, &memblock.memory, 0); nd_pa = memblock_phys_alloc(nd_size, SMP_CACHE_BYTES); nd = __va(nd_pa); @@ -274,8 +408,8 @@ void __init sw64_numa_init(void) node_data[0] = nd; memset(NODE_DATA(0), 0, sizeof(pg_data_t)); NODE_DATA(0)->node_id = 0; - NODE_DATA(0)->node_start_pfn = mem_desc.base >> PAGE_SHIFT; - NODE_DATA(0)->node_spanned_pages = mem_desc.size >> PAGE_SHIFT; + NODE_DATA(0)->node_start_pfn = mem_base >> PAGE_SHIFT; + NODE_DATA(0)->node_spanned_pages = mem_size >> PAGE_SHIFT; node_set_online(0); } #endif /* CONFIG_NUMA */ @@ -304,59 +438,6 @@ void vmemmap_free(unsigned long start, unsigned long end, } #endif -#ifdef CONFIG_HAVE_MEMBLOCK -#ifndef MIN_MEMBLOCK_ADDR -#define MIN_MEMBLOCK_ADDR __pa(PAGE_OFFSET) -#endif -#ifndef MAX_MEMBLOCK_ADDR -#define MAX_MEMBLOCK_ADDR ((phys_addr_t)~0) -#endif -void __init early_init_dt_add_memory_arch(u64 base, u64 size) -{ - const u64 phys_offset = MIN_MEMBLOCK_ADDR; - - if (acpi_disabled) { - if (!PAGE_ALIGNED(base)) { - if (size < PAGE_SIZE - (base & ~PAGE_MASK)) { - pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", - base, base + size); - return; - } - size -= PAGE_SIZE - (base & ~PAGE_MASK); - base = PAGE_ALIGN(base); - } - size &= PAGE_MASK; - - if (base > MAX_MEMBLOCK_ADDR) { - pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", - base, base + size); - return; - } - - if (base + size - 1 > MAX_MEMBLOCK_ADDR) { - pr_warn("Ignoring memory range 0x%llx - 0x%llx\n", - ((u64)MAX_MEMBLOCK_ADDR) + 1, base + size); - size = MAX_MEMBLOCK_ADDR - base + 1; - } - - if (base + size < phys_offset) { - pr_warn("Ignoring memory block 0x%llx - 0x%llx\n", - base, base + size); - return; - } - - if (base < phys_offset) { - pr_warn("Ignoring memory range 0x%llx - 0x%llx\n", - base, phys_offset); - size -= phys_offset - base; - base = phys_offset; - } - memblock_add(base, size); - } else - return; -} -#endif - #ifdef CONFIG_MEMORY_HOTPLUG int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params) { -- Gitee From a2431ad165226436e81db20e53587db36a27e1d1 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Fri, 26 Apr 2024 10:23:49 +0800 Subject: [PATCH 07/25] anolis: sw64: use generic page_is_ram() ANBZ: #4688 The generic page_is_ram() provides more rigorous check. Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/kernel/setup.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/sw_64/kernel/setup.c b/arch/sw_64/kernel/setup.c index 95162a3d073f..aceaecb5bf8e 100644 --- a/arch/sw_64/kernel/setup.c +++ b/arch/sw_64/kernel/setup.c @@ -401,13 +401,6 @@ subsys_initcall(request_standard_resources); extern void cpu_set_node(void); #endif -int page_is_ram(unsigned long pfn) -{ - pfn <<= PAGE_SHIFT; - - return pfn >= mem_desc.base && pfn < (mem_desc.base + mem_desc.size); -} - static int __init topology_init(void) { int i, ret; -- Gitee From 7c4b92f57a9b248af933c3139aeb28b2957f7c67 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Fri, 26 Apr 2024 14:48:25 +0800 Subject: [PATCH 08/25] anolis: sw64: mm: fix mem=xxx command line invalid in some cases ANBZ: #4688 The strategy of adding the intersection of [start, start + size) and usable physical memory is flawed. Since efi_init() adds memory without considering [start, start + size). We need to explicitly remove memory. Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/mm/init.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/arch/sw_64/mm/init.c b/arch/sw_64/mm/init.c index 5a795d58db81..ed4f213c7165 100644 --- a/arch/sw_64/mm/init.c +++ b/arch/sw_64/mm/init.c @@ -69,6 +69,13 @@ static int __init setup_mem_size(char *p) mem_start = start; mem_size_limit = size; + + if (mem_start < NODE0_START) { + mem_start = NODE0_START; + mem_size_limit -= min(mem_size_limit, + NODE0_START - mem_start); + } + return 0; } early_param("mem", setup_mem_size); @@ -186,17 +193,8 @@ static void __init mem_detect(void) mem_desc.phys_size += socket_desc[i].socket_mem; } - if (mem_start >= NODE0_START) { - mem_desc.base = mem_start; - } else { - mem_desc.base = NODE0_START; - mem_size_limit -= NODE0_START - mem_start; - } - - if (mem_size_limit && mem_size_limit < mem_desc.phys_size - NODE0_START) - mem_desc.size = mem_size_limit; - else - mem_desc.size = mem_desc.phys_size - NODE0_START; + mem_desc.base = NODE0_START; + mem_desc.size = mem_desc.phys_size - NODE0_START; } #ifdef CONFIG_BLK_DEV_INITRD @@ -380,6 +378,16 @@ void __init sw64_memblock_init(void) reserve_crashkernel(); + /* All memory has been added, it's time to handle memory limitation */ + if (mem_size_limit) { + memblock_remove(0, mem_start); + memblock_remove(mem_start + mem_size_limit, PHYS_ADDR_MAX); + if (sunway_boot_magic != 0xDEED2024UL) { + mem_desc.base = mem_start; + mem_desc.size = memblock_phys_mem_size(); + } + } + /* end of DRAM range may have been changed */ max_pfn = max_low_pfn = PFN_DOWN(memblock_end_of_DRAM()); } -- Gitee From 0b3b72c25df636cace9af1725a83ab157675ad76 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Tue, 30 Apr 2024 09:45:42 +0800 Subject: [PATCH 09/25] anolis: sw64: acpi: enable ACPI by default ANBZ: #4688 ACPI is enabled by default and can be disabled via the command line parameter "acpi=off". Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/kernel/acpi.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/arch/sw_64/kernel/acpi.c b/arch/sw_64/kernel/acpi.c index d0e853659211..5be5d93018cc 100644 --- a/arch/sw_64/kernel/acpi.c +++ b/arch/sw_64/kernel/acpi.c @@ -12,11 +12,11 @@ #include #endif -int acpi_disabled = 1; +int acpi_disabled; EXPORT_SYMBOL(acpi_disabled); -int acpi_noirq = 1; /* skip ACPI IRQ initialization */ -int acpi_pci_disabled = 1; /* skip ACPI PCI scan and IRQ initialization */ +int acpi_noirq; /* skip ACPI IRQ initialization */ +int acpi_pci_disabled; /* skip ACPI PCI scan and IRQ initialization */ EXPORT_SYMBOL(acpi_pci_disabled); static bool param_acpi_on __initdata; @@ -357,12 +357,18 @@ void __init acpi_boot_table_init(void) } /** - * ACPI is disabled by default. - * ACPI is only enabled when firmware passes ACPI table - * and sets boot parameter "acpi=on". + * ACPI is enabled by default. + * + * ACPI is disabled only when firmware explicitly passes + * the boot cmdline "acpi=off". + * + * Note: If no valid ACPI table is found, it will eventually + * be disabled. */ if (param_acpi_on) enable_acpi(); + else if (param_acpi_off) + disable_acpi(); /* * If acpi_disabled, bail out -- Gitee From d4deffede0b3331277e29239163a2a43f6a4a8d6 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Fri, 10 May 2024 15:21:13 +0800 Subject: [PATCH 10/25] anolis: sw64: irq: support interrupt for virtual GPIO ANBZ: #4688 As a hardware-reduced ACPI platform, sw64 uses GPIO to signal ACPI events. This commit is used to support interrupt for virtual GPIO, specifically pin 0 of GPIO port A. We use the virtual pin 0 to signal power button, so that we can do soft power off based on ACPI in virtual environment. Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/include/asm/irq_impl.h | 1 + drivers/irqchip/irq-sunway-cpu.c | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/arch/sw_64/include/asm/irq_impl.h b/arch/sw_64/include/asm/irq_impl.h index 797af433a126..8040d22c53a5 100644 --- a/arch/sw_64/include/asm/irq_impl.h +++ b/arch/sw_64/include/asm/irq_impl.h @@ -33,6 +33,7 @@ enum sw64_irq_type { INT_FAULT = 10, INT_VT_SERIAL = 12, INT_VT_HOTPLUG = 13, + INT_VT_GPIOA_PIN0 = 15, INT_DEV = 17, INT_NMI = 18, INT_LEGACY = 31, diff --git a/drivers/irqchip/irq-sunway-cpu.c b/drivers/irqchip/irq-sunway-cpu.c index dca5020a789d..b8204fbdd6e7 100644 --- a/drivers/irqchip/irq-sunway-cpu.c +++ b/drivers/irqchip/irq-sunway-cpu.c @@ -223,11 +223,8 @@ asmlinkage void do_entInt(unsigned long type, unsigned long vector, set_irq_regs(old_regs); return; case INT_VT_SERIAL: - old_regs = set_irq_regs(regs); - handle_irq(type); - set_irq_regs(old_regs); - return; case INT_VT_HOTPLUG: + case INT_VT_GPIOA_PIN0: old_regs = set_irq_regs(regs); handle_irq(type); set_irq_regs(old_regs); -- Gitee From e452795d47b924ba79720bb37ef00219b393c477 Mon Sep 17 00:00:00 2001 From: Xu Yiwei Date: Sat, 11 May 2024 10:33:14 +0000 Subject: [PATCH 11/25] anolis: sw64: remove redundant intx code ANBZ: #4688 Remove redundant pci set_intx codes in pci_sunway.c. They have already been included as one of the irqchip behaviours. Signed-off-by: Xu Yiwei Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- drivers/pci/controller/pci-sunway.c | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/drivers/pci/controller/pci-sunway.c b/drivers/pci/controller/pci-sunway.c index 420572592779..36e87a639bed 100644 --- a/drivers/pci/controller/pci-sunway.c +++ b/drivers/pci/controller/pci-sunway.c @@ -242,25 +242,6 @@ static void set_rc_piu(unsigned long node, unsigned long index) } } -static void set_intx(unsigned long node, unsigned long index, - unsigned long int_conf) -{ - if (is_guest_or_emul()) - return; - -#if defined(CONFIG_UNCORE_XUELANG) - write_piu_ior0(node, index, INTACONFIG, int_conf | (0x8UL << 10)); - write_piu_ior0(node, index, INTBCONFIG, int_conf | (0x4UL << 10)); - write_piu_ior0(node, index, INTCCONFIG, int_conf | (0x2UL << 10)); - write_piu_ior0(node, index, INTDCONFIG, int_conf | (0x1UL << 10)); -#elif defined(CONFIG_UNCORE_JUNZHANG) - write_piu_ior0(node, index, INTACONFIG, int_conf | (0x1UL << 10)); - write_piu_ior0(node, index, INTBCONFIG, int_conf | (0x2UL << 10)); - write_piu_ior0(node, index, INTCCONFIG, int_conf | (0x4UL << 10)); - write_piu_ior0(node, index, INTDCONFIG, int_conf | (0x8UL << 10)); -#endif -} - static unsigned long get_rc_enable(unsigned long node) { unsigned long rc_enable; @@ -338,7 +319,6 @@ static struct sw64_pci_init_ops chip_pci_init_ops = { .hose_init = hose_init, .set_rc_piu = set_rc_piu, .check_pci_linkup = check_pci_linkup, - .set_intx = set_intx, }; void __init setup_chip_pci_ops(void) -- Gitee From f4637dd5f0cdd09290b353faf074f1d04f729409 Mon Sep 17 00:00:00 2001 From: Xu Chenjiao Date: Mon, 13 May 2024 10:27:39 +0000 Subject: [PATCH 12/25] anolis: sw64: pciehp: add pcie hotplug driver support for C4 ANBZ: #4688 This driver is added to make hotplug work on C4, since PCIe hotplug is implemented on hardware. Signed-off-by: Xu Chenjiao Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/include/asm/uncore_io_junzhang.h | 7 + arch/sw_64/pci/pci.c | 4 +- drivers/irqchip/irq-sunway-cpu.c | 9 + drivers/pci/controller/pci-sunway.c | 17 + drivers/pci/hotplug/Kconfig | 9 + drivers/pci/hotplug/Makefile | 6 + drivers/pci/hotplug/sunway_pciehp.h | 215 ++++ drivers/pci/hotplug/sunway_pciehp_core.c | 278 +++++ drivers/pci/hotplug/sunway_pciehp_ctrl.c | 671 +++++++++++ drivers/pci/hotplug/sunway_pciehp_hpc.c | 1152 +++++++++++++++++++ drivers/pci/hotplug/sunway_pciehp_pci.c | 113 ++ 11 files changed, 2479 insertions(+), 2 deletions(-) create mode 100644 drivers/pci/hotplug/sunway_pciehp.h create mode 100644 drivers/pci/hotplug/sunway_pciehp_core.c create mode 100644 drivers/pci/hotplug/sunway_pciehp_ctrl.c create mode 100644 drivers/pci/hotplug/sunway_pciehp_hpc.c create mode 100644 drivers/pci/hotplug/sunway_pciehp_pci.c diff --git a/arch/sw_64/include/asm/uncore_io_junzhang.h b/arch/sw_64/include/asm/uncore_io_junzhang.h index 2f745fe35365..8c14890e5c1d 100644 --- a/arch/sw_64/include/asm/uncore_io_junzhang.h +++ b/arch/sw_64/include/asm/uncore_io_junzhang.h @@ -64,6 +64,7 @@ #define PME_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x8UL << 10) #define AER_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x8UL << 10) +#define HP_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x8UL << 10) #define PIUCONFIG0_INIT_VAL 0x38016 @@ -112,12 +113,15 @@ enum { SI_FAULT_STAT_EN = SPBU_BASE | 0x3180UL, SI_FAULT_INT_EN = SPBU_BASE | 0x3200UL, ADR_CTL = SPBU_BASE | 0x3600UL, + PIUH_CTRL = SPBU_BASE | 0x3680UL, MC_ONLINE = SPBU_BASE | 0x3780UL, CLK_CTL = SPBU_BASE | 0x3b80UL, CLU_LV2_SELH = SPBU_BASE | 0x3a00UL, CLU_LV2_SELL = SPBU_BASE | 0x3b00UL, PIU_TOP0_CONFIG = SPBU_BASE | 0x4c80UL, PIU_TOP1_CONFIG = SPBU_BASE | 0x4d00UL, + PIU_PHY_SRST_H = SPBU_BASE | 0x6280UL, + BUTTON_RST_N_PCIE0 = SPBU_BASE | 0x6a80UL, SOFT_INFO0 = SPBU_BASE | 0xa000UL, }; @@ -140,6 +144,8 @@ enum { PMEMSICONFIG = 0xa580UL, HPINTCONFIG = 0xa600UL, HPMSICONFIG = 0xa680UL, + HP_CTRL = 0xac80UL, + HP_WATCHOUT = 0xae00UL, DTBASEADDR = 0xb000UL, DTLB_FLUSHALL = 0xb080UL, DTLB_FLUSHDEV = 0xb100UL, @@ -165,6 +171,7 @@ enum { /* PIU IOR1 */ enum { PIUCONFIG1 = 0x0UL, + NEWLTSSMSTATE0 = 0x300UL, ERRENABLE = 0x880UL, RCDEBUGINF1 = 0xc80UL, DCACONTROL = 0x1a00UL, diff --git a/arch/sw_64/pci/pci.c b/arch/sw_64/pci/pci.c index 87930a62595a..77dbd2f4bf37 100644 --- a/arch/sw_64/pci/pci.c +++ b/arch/sw_64/pci/pci.c @@ -201,9 +201,9 @@ static void fixup_root_complex(struct pci_dev *dev) hose->self_busno = hose->busn_space->start; if (likely(bus->number == hose->self_busno)) { - if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE)) { + if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE_SUNWAY)) { /* Check Root Complex port again */ - dev->is_hotplug_bridge = 0; + dev->is_hotplug_bridge = 1; dev->current_state = PCI_D0; } diff --git a/drivers/irqchip/irq-sunway-cpu.c b/drivers/irqchip/irq-sunway-cpu.c index b8204fbdd6e7..250ddbd61cf4 100644 --- a/drivers/irqchip/irq-sunway-cpu.c +++ b/drivers/irqchip/irq-sunway-cpu.c @@ -71,6 +71,15 @@ static void handle_intx(unsigned int offset) } } + if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE_SUNWAY)) { + value = read_piu_ior0(hose->node, hose->index, HPINTCONFIG); + if (value >> 63) { + handle_irq(hose->service_irq); + write_piu_ior0(hose->node, hose->index, HPINTCONFIG, value); + } + + } + if (hose->iommu_enable) { value = read_piu_ior0(hose->node, hose->index, IOMMUEXCPT_STATUS); if (value >> 63) diff --git a/drivers/pci/controller/pci-sunway.c b/drivers/pci/controller/pci-sunway.c index 36e87a639bed..688793d1b136 100644 --- a/drivers/pci/controller/pci-sunway.c +++ b/drivers/pci/controller/pci-sunway.c @@ -33,6 +33,23 @@ void set_pcieport_service_irq(int node, int index) if (IS_ENABLED(CONFIG_PCIEAER)) write_piu_ior0(node, index, AERERRINTCONFIG, AER_ENABLE_INTD_CORE0); + +#ifdef CONFIG_UNCORE_JUNZHANG + if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE_SUNWAY)) + write_piu_ior0(node, index, + HPINTCONFIG, HP_ENABLE_INTD_CORE0); +#endif +} + +int pcibios_enable_device(struct pci_dev *dev, int bars) +{ + struct pci_bus *bus = dev->bus; + struct pci_controller *hose = pci_bus_to_pci_controller(bus); + + if (!is_guest_or_emul() && unlikely(bus->number == hose->self_busno)) + return 0; + else + return pci_enable_resources(dev, bars); } int chip_pcie_configure(struct pci_controller *hose) diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 48113b210cf9..16e38e1db976 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -114,6 +114,15 @@ config HOTPLUG_PCI_SHPC When in doubt, say N. +config HOTPLUG_PCI_PCIE_SUNWAY + bool "SUNWAY PCI Express Hotplug driver" + depends on SW64 && SUBARCH_C4 && !HOTPLUG_PCI_PCIE + help + Say Y here if you have a motherboard with a SUNWAY PCI Express Hotplug + controller. + + When in doubt, say N. + config HOTPLUG_PCI_POWERNV tristate "PowerPC PowerNV PCI Hotplug driver" depends on PPC_POWERNV && EEH diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 5196983220df..e7193995acde 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o # generic support. obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o +obj-$(CONFIG_HOTPLUG_PCI_PCIE_SUNWAY) += sunway_pciehp.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o @@ -70,3 +71,8 @@ shpchp-objs := shpchp_core.o \ shpchp_pci.o \ shpchp_sysfs.o \ shpchp_hpc.o + +sunway_pciehp-objs := sunway_pciehp_core.o \ + sunway_pciehp_ctrl.o \ + sunway_pciehp_pci.o \ + sunway_pciehp_hpc.o diff --git a/drivers/pci/hotplug/sunway_pciehp.h b/drivers/pci/hotplug/sunway_pciehp.h new file mode 100644 index 000000000000..5ef5e745543b --- /dev/null +++ b/drivers/pci/hotplug/sunway_pciehp.h @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Sunway PCI Express Hot Plug Controller Driver + */ +#ifndef _SUNWAYPCIEHP_H +#define _SUNWAYPCIEHP_H + +#include +#include +#include +#include +#include /* signal_pending() */ +#include +#include +#include + +#include "../pcie/portdrv.h" + +#define MY_NAME "sunway_pciehp" + +extern bool sunway_pciehp_poll_mode; +extern int sunway_pciehp_poll_time; + +/* + * Set CONFIG_DYNAMIC_DEBUG=y and boot with 'dyndbg="file sunway_pciehp* +p"' to + * enable debug messages. + */ +#define ctrl_dbg(ctrl, format, arg...) \ + pci_dbg(ctrl->pci_dev, format, ## arg) +#define ctrl_err(ctrl, format, arg...) \ + pci_err(ctrl->pci_dev, format, ## arg) +#define ctrl_info(ctrl, format, arg...) \ + pci_info(ctrl->pci_dev, format, ## arg) +#define ctrl_warn(ctrl, format, arg...) \ + pci_warn(ctrl->pci_dev, format, ## arg) + +#define SLOT_NAME_SIZE 10 + +struct saved_piu_space { + unsigned long epdmabar; + unsigned long msiaddr; + unsigned long iommuexcpt_ctrl; + unsigned long dtbaseaddr; + unsigned long intaconfig; + unsigned long intbconfig; + unsigned long intcconfig; + unsigned long intdconfig; + unsigned long pmeintconfig; + unsigned long aererrintconfig; + unsigned long hpintconfig; + unsigned int state_saved:1; +}; + +/** + * struct controller - PCIe hotplug controller + * @slot_cap: cached copy of the Slot Capabilities register + * @slot_ctrl: cached copy of the Slot Control register + * @ctrl_lock: serializes writes to the Slot Control register + * @cmd_started: jiffies when the Slot Control register was last written; + * the next write is allowed 1 second later, absent a Command Completed + * interrupt (PCIe r4.0, sec 6.7.3.2) + * @cmd_busy: flag set on Slot Control register write, cleared by IRQ handler + * on reception of a Command Completed event + * @queue: wait queue to wake up on reception of a Command Completed event, + * used for synchronous writes to the Slot Control register + * @pending_events: used by the IRQ handler to save events retrieved from the + * Slot Status register for later consumption by the IRQ thread + * @notification_enabled: whether the IRQ was requested successfully + * @power_fault_detected: whether a power fault was detected by the hardware + * that has not yet been cleared by the user + * @poll_thread: thread to poll for slot events if no IRQ is available, + * enabled with pciehp_poll_mode module parameter + * @state: current state machine position + * @state_lock: protects reads and writes of @state; + * protects scheduling, execution and cancellation of @button_work + * @button_work: work item to turn the slot on or off after 5 seconds + * in response to an Attention Button press + * @hotplug_slot: structure registered with the PCI hotplug core + * @reset_lock: prevents access to the Data Link Layer Link Active bit in the + * Link Status register and to the Presence Detect State bit in the Slot + * Status register during a slot reset which may cause them to flap + * @ist_running: flag to keep user request waiting while IRQ thread is running + * @request_result: result of last user request submitted to the IRQ thread + * @requester: wait queue to wake up on completion of user request, + * used for synchronous slot enable/disable request via sysfs + * + * PCIe hotplug has a 1:1 relationship between controller and slot, hence + * unlike other drivers, the two aren't represented by separate structures. + */ +struct controller { + struct pci_dev *pci_dev; + + u32 slot_cap; /* capabilities and quirks */ + unsigned int inband_presence_disabled:1; + u16 slot_ctrl; /* control register access */ + struct mutex ctrl_lock; + unsigned long cmd_started; + unsigned int cmd_busy:1; + wait_queue_head_t queue; + + atomic_t pending_events; /* event handling */ + unsigned int notification_enabled:1; + unsigned int power_fault_detected; + struct task_struct *poll_thread; + + u8 state; /* state machine */ + struct mutex state_lock; + struct delayed_work button_work; + + struct hotplug_slot hotplug_slot; /* hotplug core interface */ + struct rw_semaphore reset_lock; + unsigned int ist_running; + int request_result; + wait_queue_head_t requester; + + struct saved_piu_space saved_piu; +}; + +/** + * DOC: Slot state + * + * @OFF_STATE: slot is powered off, no subordinate devices are enumerated + * @BLINKINGON_STATE: slot will be powered on after the 5 second delay, + * Power Indicator is blinking + * @BLINKINGOFF_STATE: slot will be powered off after the 5 second delay, + * Power Indicator is blinking + * @POWERON_STATE: slot is currently powering on + * @POWEROFF_STATE: slot is currently powering off + * @ON_STATE: slot is powered on, subordinate devices have been enumerated + */ +#define OFF_STATE 0 +#define BLINKINGON_STATE 1 +#define BLINKINGOFF_STATE 2 +#define POWERON_STATE 3 +#define POWEROFF_STATE 4 +#define ON_STATE 5 + +/** + * DOC: Flags to request an action from the IRQ thread + * + * These are stored together with events read from the Slot Status register, + * hence must be greater than its 16-bit width. + * + * %DISABLE_SLOT: Disable the slot in response to a user request via sysfs or + * an Attention Button press after the 5 second delay + * %RERUN_ISR: Used by the IRQ handler to inform the IRQ thread that the + * hotplug port was inaccessible when the interrupt occurred, requiring + * that the IRQ handler is rerun by the IRQ thread after it has made the + * hotplug port accessible by runtime resuming its parents to D0 + */ +#define DISABLE_SLOT (1 << 16) +#define RERUN_ISR (1 << 17) +#define SW64_POLL_DISABLE_SLOT (1 << 18) +#define SW64_POLL_ENABLE_SLOT (1 << 19) + +#define ATTN_BUTTN(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_ABP) +#define POWER_CTRL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PCP) +#define MRL_SENS(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_MRLSP) +#define ATTN_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_AIP) +#define PWR_LED(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_PIP) +#define NO_CMD_CMPL(ctrl) ((ctrl)->slot_cap & PCI_EXP_SLTCAP_NCCS) +#define PSN(ctrl) (((ctrl)->slot_cap & PCI_EXP_SLTCAP_PSN) >> 19) + +#define HP_CTRL_FINISH 0x0 +#define HP_CTRL_INSERT 0x1 +#define HP_CTRL_REMOVE 0x2 + +void sunway_pciehp_request(struct controller *ctrl, int action); +void sunway_pciehp_handle_button_press(struct controller *ctrl); +void sunway_pciehp_handle_disable_request(struct controller *ctrl); +void sunway_pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events); +int sunway_pciehp_configure_device(struct controller *ctrl); +void sunway_pciehp_unconfigure_device(struct controller *ctrl, bool presence); +void sunway_pciehp_queue_pushbutton_work(struct work_struct *work); +struct controller *sunwayhpc_init(struct pci_dev *dev); +int sunway_pcie_init_notification(struct controller *ctrl); +void sunway_pcie_shutdown_notification(struct controller *ctrl); +void sunway_pcie_clear_hotplug_events(struct controller *ctrl); +int sunway_pciehp_power_on_slot(struct controller *ctrl); +void sunway_pciehp_power_off_slot(struct controller *ctrl); +void sunway_pciehp_get_power_status(struct controller *ctrl, u8 *status); + +#define INDICATOR_NOOP -1 /* Leave indicator unchanged */ +void sunway_pciehp_set_indicators(struct controller *ctrl, int pwr, int attn); + +void sunway_pciehp_get_latch_status(struct controller *ctrl, u8 *status); +int sunway_pciehp_query_power_fault(struct controller *ctrl); +int sunway_pciehp_card_present(struct controller *ctrl); +int sunway_pciehp_card_present_or_link_active(struct controller *ctrl); +int sunway_pciehp_check_link_status(struct controller *ctrl); +int sunway_pciehp_check_link_active(struct controller *ctrl); +void sunway_pciehp_release_ctrl(struct controller *ctrl); + +int sunway_pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot); +int sunway_pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot); +int sunway_pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe); +int sunway_pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status); +int sunway_pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, u8 status); +int sunway_pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, u8 *status); + +int sunway_pciehp_link_enable(struct controller *ctrl); +int sunway_pciehp_link_disable(struct controller *ctrl); +void sunway_pciehp_restore_rc_piu(struct controller *ctrl); + +static inline const char *slot_name(struct controller *ctrl) +{ + return hotplug_slot_name(&ctrl->hotplug_slot); +} + +static inline struct controller *to_ctrl(struct hotplug_slot *hotplug_slot) +{ + return container_of(hotplug_slot, struct controller, hotplug_slot); +} + +#endif /* _PCIEHP_H */ diff --git a/drivers/pci/hotplug/sunway_pciehp_core.c b/drivers/pci/hotplug/sunway_pciehp_core.c new file mode 100644 index 000000000000..e79a074d2557 --- /dev/null +++ b/drivers/pci/hotplug/sunway_pciehp_core.c @@ -0,0 +1,278 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sunway PCI Express Hot Plug Controller Driver + */ + +#define pr_fmt(fmt) "sunway_pciehp: " fmt +#define dev_fmt pr_fmt + +#include +#include +#include +#include +#include +#include + +#include "../pci.h" +#include "sunway_pciehp.h" + +/* Global variables */ +bool sunway_pciehp_poll_mode; +int sunway_pciehp_poll_time; + +#define DRIVER_VERSION "0.1" +#define DRIVER_DESC "Sunway PCI Express Hot Plug Controller Driver" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* + * not really modular, but the easiest way to keep compat with existing + * bootargs behaviour is to continue using module_param here. + */ +module_param(sunway_pciehp_poll_mode, bool, 0644); +module_param(sunway_pciehp_poll_time, int, 0644); +MODULE_PARM_DESC(sunway_pciehp_poll_mode, "Using polling mechanism for hot-plug events or not"); +MODULE_PARM_DESC(sunway_pciehp_poll_time, "Polling mechanism frequency, in seconds"); + +#define PCIE_MODULE_NAME "sunway_pciehp" + +static int set_attention_status(struct hotplug_slot *slot, u8 value); +static int get_power_status(struct hotplug_slot *slot, u8 *value); +static int get_latch_status(struct hotplug_slot *slot, u8 *value); +static int get_adapter_status(struct hotplug_slot *slot, u8 *value); + +static int init_slot(struct controller *ctrl) +{ + struct hotplug_slot_ops *ops; + char name[SLOT_NAME_SIZE]; + int retval; + + /* Setup hotplug slot ops */ + ops = kzalloc(sizeof(*ops), GFP_KERNEL); + if (!ops) + return -ENOMEM; + + ops->enable_slot = sunway_pciehp_sysfs_enable_slot; + ops->disable_slot = sunway_pciehp_sysfs_disable_slot; + ops->get_power_status = get_power_status; + ops->get_adapter_status = get_adapter_status; + ops->reset_slot = sunway_pciehp_reset_slot; + if (MRL_SENS(ctrl)) + ops->get_latch_status = get_latch_status; + if (ATTN_LED(ctrl)) { + ops->get_attention_status = sunway_pciehp_get_attention_status; + ops->set_attention_status = set_attention_status; + } else if (ctrl->pci_dev->hotplug_user_indicators) { + ops->get_attention_status = sunway_pciehp_get_raw_indicator_status; + ops->set_attention_status = sunway_pciehp_set_raw_indicator_status; + } + + /* register this slot with the hotplug pci core */ + ctrl->hotplug_slot.ops = ops; + snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl)); + + retval = pci_hp_initialize(&ctrl->hotplug_slot, + ctrl->pci_dev->subordinate, 0, name); + if (retval) { + ctrl_err(ctrl, "pci_hp_initialize failed: error %d\n", retval); + kfree(ops); + } + return retval; +} + +static void cleanup_slot(struct controller *ctrl) +{ + struct hotplug_slot *hotplug_slot = &ctrl->hotplug_slot; + + pci_hp_destroy(hotplug_slot); + kfree(hotplug_slot->ops); +} + +/* + * set_attention_status - Turns the Attention Indicator on, off or blinking + */ +static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl->pci_dev; + + if (status) + status <<= PCI_EXP_SLTCTL_ATTN_IND_SHIFT; + else + status = PCI_EXP_SLTCTL_ATTN_IND_OFF; + + pci_config_pm_runtime_get(pdev); + sunway_pciehp_set_indicators(ctrl, INDICATOR_NOOP, status); + pci_config_pm_runtime_put(pdev); + return 0; +} + +static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl->pci_dev; + + pci_config_pm_runtime_get(pdev); + sunway_pciehp_get_power_status(ctrl, value); + pci_config_pm_runtime_put(pdev); + return 0; +} + +static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl->pci_dev; + + pci_config_pm_runtime_get(pdev); + sunway_pciehp_get_latch_status(ctrl, value); + pci_config_pm_runtime_put(pdev); + return 0; +} + +static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl->pci_dev; + int ret; + + pci_config_pm_runtime_get(pdev); + ret = sunway_pciehp_card_present_or_link_active(ctrl); + pci_config_pm_runtime_put(pdev); + + if (ret < 0) + return ret; + + *value = ret; + return 0; +} + +/** + * sunway_pciehp_check_presence() - synthesize event if presence has changed + * + * On probe and resume, an explicit presence check is necessary to bring up an + * occupied slot or bring down an unoccupied slot. This can't be triggered by + * events in the Slot Status register, they may be stale and are therefore + * cleared. Secondly, sending an interrupt for "events that occur while + * interrupt generation is disabled [when] interrupt generation is subsequently + * enabled" is optional per PCIe r4.0, sec 6.7.3.4. + */ +static void sunway_pciehp_check_presence(struct controller *ctrl) +{ + int occupied; + + down_read(&ctrl->reset_lock); + mutex_lock(&ctrl->state_lock); + + occupied = sunway_pciehp_card_present_or_link_active(ctrl); + if ((occupied > 0 && (ctrl->state == OFF_STATE || + ctrl->state == BLINKINGON_STATE)) || + (!occupied && (ctrl->state == ON_STATE || + ctrl->state == BLINKINGOFF_STATE))) + sunway_pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); + + mutex_unlock(&ctrl->state_lock); + up_read(&ctrl->reset_lock); +} + +static int sunwayhp_init(struct pci_dev *dev) +{ + int rc; + struct controller *ctrl; + + if (!dev->subordinate) { + /* Can happen if we run out of bus numbers during probe */ + dev_err(&dev->dev, + "Hotplug bridge without secondary bus, ignoring\n"); + return -ENODEV; + } + + ctrl = sunwayhpc_init(dev); + if (!ctrl) { + dev_err(&dev->dev, "Controller initialization failed\n"); + return -ENODEV; + } + pci_set_drvdata(dev, ctrl); + + /* Setup the slot information structures */ + rc = init_slot(ctrl); + if (rc) { + if (rc == -EBUSY) + ctrl_warn(ctrl, "Slot already registered by another hotplug driver\n"); + else + ctrl_err(ctrl, "Slot initialization failed (%d)\n", rc); + goto err_out_release_ctlr; + } + + /* Enable events after we have setup the data structures */ + rc = sunway_pcie_init_notification(ctrl); + if (rc) { + ctrl_err(ctrl, "Notification initialization failed (%d)\n", rc); + goto err_out_free_ctrl_slot; + } + + /* Publish to user space */ + rc = pci_hp_add(&ctrl->hotplug_slot); + if (rc) { + ctrl_err(ctrl, "Publication to user space failed (%d)\n", rc); + goto err_out_shutdown_notification; + } + + sunway_pciehp_check_presence(ctrl); + + return 0; + +err_out_shutdown_notification: + sunway_pcie_shutdown_notification(ctrl); +err_out_free_ctrl_slot: + cleanup_slot(ctrl); +err_out_release_ctlr: + sunway_pciehp_release_ctrl(ctrl); + return -ENODEV; +} + +static void sunwayhp_remove(struct pci_dev *dev) +{ + struct controller *ctrl = pci_get_drvdata(dev); + + pci_hp_del(&ctrl->hotplug_slot); + sunway_pcie_shutdown_notification(ctrl); + cleanup_slot(ctrl); + sunway_pciehp_release_ctrl(ctrl); +} + +extern struct pci_controller *hose_head, **hose_tail; +static int __init sunway_pciehp_init(void) +{ + int retval; + struct pci_dev *pdev = NULL; + struct pci_controller *hose = NULL; + + pr_info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); + + for (hose = hose_head; hose; hose = hose->next) { + pdev = pci_get_device(PCI_VENDOR_ID_JN, PCI_DEVICE_ID_SW64_ROOT_BRIDGE, pdev); + + retval = sunwayhp_init(pdev); + } + + return retval; +} + +static void __exit sunway_pciehp_exit(void) +{ + struct pci_dev *pdev = NULL; + struct pci_controller *hose = NULL; + + pr_info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); + + for (hose = hose_head; hose; hose = hose->next) { + pdev = pci_get_device(PCI_VENDOR_ID_JN, PCI_DEVICE_ID_SW64_ROOT_BRIDGE, pdev); + + sunwayhp_remove(pdev); + } + +} + +module_init(sunway_pciehp_init); +module_exit(sunway_pciehp_exit); diff --git a/drivers/pci/hotplug/sunway_pciehp_ctrl.c b/drivers/pci/hotplug/sunway_pciehp_ctrl.c new file mode 100644 index 000000000000..c85ba8e684cc --- /dev/null +++ b/drivers/pci/hotplug/sunway_pciehp_ctrl.c @@ -0,0 +1,671 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sunway PCI Express Hot Plug Controller Driver + */ + +#define dev_fmt(fmt) "sunway_pciehp: " fmt + +#include +#include +#include +#include +#include +#include + +#include "sunway_pciehp.h" +#include "../pci.h" + +/* The following routines constitute the bulk of the + * hotplug controller logic + */ + +#define SAFE_REMOVAL true +#define SURPRISE_REMOVAL false + +static void set_slot_off(struct controller *ctrl) +{ + /* + * Turn off slot, turn on attention indicator, turn off power + * indicator + */ + if (POWER_CTRL(ctrl)) { + sunway_pciehp_power_off_slot(ctrl); + + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); + } + + sunway_pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + PCI_EXP_SLTCTL_ATTN_IND_ON); +} + +/** + * board_added - Called after a board has been added to the system. + * @ctrl: PCIe hotplug controller where board is added + * + * Turns power on for the board. + * Configures board. + */ +static int board_added(struct controller *ctrl) +{ + int retval = 0; + struct pci_bus *parent = ctrl->pci_dev->subordinate; + + if (POWER_CTRL(ctrl)) { + /* Power on slot */ + retval = sunway_pciehp_power_on_slot(ctrl); + if (retval) + return retval; + } + + sunway_pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, + INDICATOR_NOOP); + + /* Check link training status */ + retval = sunway_pciehp_check_link_status(ctrl); + if (retval) + goto err_exit; + + /* Check for a power fault */ + if (ctrl->power_fault_detected || sunway_pciehp_query_power_fault(ctrl)) { + ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl)); + retval = -EIO; + goto err_exit; + } + + retval = sunway_pciehp_configure_device(ctrl); + if (retval) { + if (retval != -EEXIST) { + ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", + pci_domain_nr(parent), parent->number); + goto err_exit; + } + } + + sunway_pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, + PCI_EXP_SLTCTL_ATTN_IND_OFF); + return 0; + +err_exit: + set_slot_off(ctrl); + return retval; +} + +/** + * remove_board - Turn off slot and Power Indicator + * @ctrl: PCIe hotplug controller where board is being removed + * @safe_removal: whether the board is safely removed (versus surprise removed) + */ +static void remove_board(struct controller *ctrl, bool safe_removal) +{ + sunway_pciehp_unconfigure_device(ctrl, safe_removal); + + if (POWER_CTRL(ctrl)) { + sunway_pciehp_power_off_slot(ctrl); + + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); + + /* Ignore link or presence changes caused by power off */ + atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC), + &ctrl->pending_events); + } + + /* turn off Green LED */ + sunway_pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + INDICATOR_NOOP); +} + +static int sunway_pciehp_enable_slot(struct controller *ctrl); +static int sunway_pciehp_disable_slot(struct controller *ctrl, bool safe_removal); + +void sunway_pciehp_request(struct controller *ctrl, int action) +{ + atomic_or(action, &ctrl->pending_events); + if (!sunway_pciehp_poll_mode) + irq_wake_thread(ctrl->pci_dev->irq, ctrl); +} + +void sunway_pciehp_queue_pushbutton_work(struct work_struct *work) +{ + struct controller *ctrl = container_of(work, struct controller, + button_work.work); + + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGOFF_STATE: + sunway_pciehp_request(ctrl, DISABLE_SLOT); + break; + case BLINKINGON_STATE: + sunway_pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); + break; + default: + break; + } + mutex_unlock(&ctrl->state_lock); +} + +void sunway_pciehp_handle_button_press(struct controller *ctrl) +{ + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case OFF_STATE: + case ON_STATE: + if (ctrl->state == ON_STATE) { + ctrl->state = BLINKINGOFF_STATE; + ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n", + slot_name(ctrl)); + } else { + ctrl->state = BLINKINGON_STATE; + ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n", + slot_name(ctrl)); + } + /* blink power indicator and turn off attention */ + sunway_pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK, + PCI_EXP_SLTCTL_ATTN_IND_OFF); + schedule_delayed_work(&ctrl->button_work, 5 * HZ); + break; + case BLINKINGOFF_STATE: + case BLINKINGON_STATE: + /* + * Cancel if we are still blinking; this means that we + * press the attention again before the 5 sec. limit + * expires to cancel hot-add or hot-remove + */ + ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl)); + cancel_delayed_work(&ctrl->button_work); + if (ctrl->state == BLINKINGOFF_STATE) { + ctrl->state = ON_STATE; + sunway_pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON, + PCI_EXP_SLTCTL_ATTN_IND_OFF); + } else { + ctrl->state = OFF_STATE; + sunway_pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + PCI_EXP_SLTCTL_ATTN_IND_OFF); + } + ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n", + slot_name(ctrl)); + break; + default: + ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n", + slot_name(ctrl), ctrl->state); + break; + } + mutex_unlock(&ctrl->state_lock); +} + +void sunway_pciehp_handle_disable_request(struct controller *ctrl) +{ + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGON_STATE: + case BLINKINGOFF_STATE: + cancel_delayed_work(&ctrl->button_work); + break; + } + ctrl->state = POWEROFF_STATE; + mutex_unlock(&ctrl->state_lock); + + ctrl->request_result = sunway_pciehp_disable_slot(ctrl, SAFE_REMOVAL); +} + +void sunway_pciehp_save_config_space(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl->pci_dev; + int i; + + if (!pdev->state_saved) { + for (i = 0; i < 16; i++) + pci_read_config_dword(pdev, i * 4, &pdev->saved_config_space[i]); + pdev->state_saved = true; + } +} + +void sunway_pciehp_save_rc_piu(struct controller *ctrl) +{ + struct pci_bus *bus = ctrl->pci_dev->bus; + struct pci_controller *hose = pci_bus_to_pci_controller(bus); + unsigned long node, rc_index; + + node = hose->node; + rc_index = hose->index; + + sunway_pciehp_save_config_space(ctrl); + + if (!ctrl->saved_piu.state_saved) { + ctrl->saved_piu.epdmabar = read_piu_ior0(node, rc_index, EPDMABAR); + ctrl->saved_piu.msiaddr = read_piu_ior0(node, rc_index, MSIADDR); + ctrl->saved_piu.iommuexcpt_ctrl = read_piu_ior0(node, rc_index, IOMMUEXCPT_CTRL); + ctrl->saved_piu.dtbaseaddr = read_piu_ior0(node, rc_index, DTBASEADDR); + + ctrl->saved_piu.intaconfig = read_piu_ior0(node, rc_index, INTACONFIG); + ctrl->saved_piu.intbconfig = read_piu_ior0(node, rc_index, INTBCONFIG); + ctrl->saved_piu.intcconfig = read_piu_ior0(node, rc_index, INTCCONFIG); + ctrl->saved_piu.intdconfig = read_piu_ior0(node, rc_index, INTDCONFIG); + ctrl->saved_piu.pmeintconfig = read_piu_ior0(node, rc_index, PMEINTCONFIG); + ctrl->saved_piu.aererrintconfig = read_piu_ior0(node, rc_index, AERERRINTCONFIG); + ctrl->saved_piu.hpintconfig = read_piu_ior0(node, rc_index, HPINTCONFIG); + + ctrl->saved_piu.state_saved = true; + } +} + +void sunway_pciehp_start(struct hotplug_slot *hotplug_slot) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl->pci_dev; + struct pci_bus *bus = pdev->bus; + struct pci_controller *hose = pci_bus_to_pci_controller(bus); + unsigned long piu_value; + unsigned long node, rc_index; + bool hardware_auto = true; + u16 slot_ctrl; + + node = hose->node; + rc_index = hose->index; + + switch (ctrl->state) { + case OFF_STATE: + if (sunway_pciehp_poll_mode) { + ctrl_dbg(ctrl, "%s: poll mode\n", __func__); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + /* poll mode */ + slot_ctrl &= ~PCI_EXP_SLTCTL_HPIE; + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); + + sunway_pciehp_request(ctrl, SW64_POLL_ENABLE_SLOT); + } else { + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + /* interrupt mode */ + if (hardware_auto) { + ctrl_dbg(ctrl, "%s: hardware auto enable\n", __func__); + slot_ctrl &= ~(PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_CCIE); + slot_ctrl |= (PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_DLLSCE); + } else { + ctrl_dbg(ctrl, "%s: hardware auto disable\n", __func__); + slot_ctrl &= ~(PCI_EXP_SLTCTL_ABPE | PCI_EXP_SLTCTL_MRLSCE); + slot_ctrl |= (PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_DLLSCE); + } + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); + + sunway_pciehp_set_indicators(ctrl, INDICATOR_NOOP, + PCI_EXP_SLTCTL_ATTN_IND_BLINK); + + write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_INSERT); + } + break; + case ON_STATE: + sunway_pciehp_save_rc_piu(ctrl); + if (sunway_pciehp_poll_mode) { + ctrl_dbg(ctrl, "%s: poll mode\n", __func__); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + /* poll mode */ + slot_ctrl &= ~PCI_EXP_SLTCTL_HPIE; + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); + + sunway_pciehp_request(ctrl, SW64_POLL_DISABLE_SLOT); + } else { + ctrl_dbg(ctrl, "%s: int mode\n", __func__); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + /* interrupt mode */ + slot_ctrl &= ~(PCI_EXP_SLTCTL_ABPE | PCI_EXP_SLTCTL_MRLSCE); + slot_ctrl |= (PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_DLLSCE); + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); + + sunway_pciehp_set_indicators(ctrl, INDICATOR_NOOP, + PCI_EXP_SLTCTL_ATTN_IND_BLINK); + sunway_pciehp_link_disable(ctrl); + + while (1) { + piu_value = read_piu_ior1(node, rc_index, NEWLTSSMSTATE0); + piu_value &= 0xff; + + if (piu_value == 0x19) + break; + + udelay(10); + } + + write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_REMOVE); + + sunway_pciehp_request(ctrl, DISABLE_SLOT); + } + break; + default: + break; + } +} + +static void pciehp_restore_config_space_range(struct pci_dev *pdev, + int start, int end) +{ + int index; + + for (index = end; index >= start; index--) { + pci_write_config_dword(pdev, 4 * index, + pdev->saved_config_space[index]); + } +} + +void sunway_pciehp_restore_config_space(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl->pci_dev; + + if (pdev->state_saved) { + pciehp_restore_config_space_range(pdev, 12, 15); + pciehp_restore_config_space_range(pdev, 9, 11); + pciehp_restore_config_space_range(pdev, 0, 8); + pdev->state_saved = false; + } +} + +void sunway_pciehp_restore_rc_piu(struct controller *ctrl) +{ + struct pci_bus *bus = ctrl->pci_dev->bus; + struct pci_controller *hose = pci_bus_to_pci_controller(bus); + unsigned long node, rc_index; + + node = hose->node; + rc_index = hose->index; + + sunway_pciehp_restore_config_space(ctrl); + + if (ctrl->saved_piu.state_saved) { + write_piu_ior0(node, rc_index, EPDMABAR, ctrl->saved_piu.epdmabar); + write_piu_ior0(node, rc_index, MSIADDR, ctrl->saved_piu.msiaddr); + write_piu_ior0(node, rc_index, IOMMUEXCPT_CTRL, ctrl->saved_piu.iommuexcpt_ctrl); + write_piu_ior0(node, rc_index, DTBASEADDR, ctrl->saved_piu.dtbaseaddr); + + write_piu_ior0(node, rc_index, INTACONFIG, ctrl->saved_piu.intaconfig); + write_piu_ior0(node, rc_index, INTBCONFIG, ctrl->saved_piu.intbconfig); + write_piu_ior0(node, rc_index, INTCCONFIG, ctrl->saved_piu.intcconfig); + write_piu_ior0(node, rc_index, INTDCONFIG, ctrl->saved_piu.intdconfig); + write_piu_ior0(node, rc_index, PMEINTCONFIG, ctrl->saved_piu.pmeintconfig); + write_piu_ior0(node, rc_index, AERERRINTCONFIG, ctrl->saved_piu.aererrintconfig); + write_piu_ior0(node, rc_index, HPINTCONFIG, ctrl->saved_piu.hpintconfig); + + ctrl->saved_piu.state_saved = false; + } +} + +void sunway_pciehp_end(struct controller *ctrl, bool insert) +{ + struct pci_dev *pdev = ctrl->pci_dev; + struct pci_bus *bus = pdev->bus; + struct pci_controller *hose = pci_bus_to_pci_controller(bus); + unsigned long piu_value; + unsigned long node, rc_index; + u16 slot_ctrl; + + node = hose->node; + rc_index = hose->index; + + if (insert) { + write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_FINISH); + } else { + sunway_pciehp_set_indicators(ctrl, INDICATOR_NOOP, + PCI_EXP_SLTCTL_ATTN_IND_OFF); + sunway_pciehp_link_enable(ctrl); + + mdelay(100); + + while (1) { + piu_value = read_piu_ior1(node, rc_index, NEWLTSSMSTATE0); + piu_value &= 0xff; + + if (piu_value == 0x0) + break; + + udelay(10); + } + + write_piu_ior0(node, rc_index, HPINTCONFIG, HP_ENABLE_INTD_CORE0); + + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + slot_ctrl |= (PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_HPIE | + PCI_EXP_SLTCTL_PCC | + PCI_EXP_SLTCTL_DLLSCE); + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); + + write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_FINISH); + } +} + +void sunway_pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) +{ + int present, link_active; + + /* + * If the slot is on and presence or link has changed, turn it off. + * Even if it's occupied again, we cannot assume the card is the same. + */ + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGOFF_STATE: + cancel_delayed_work(&ctrl->button_work); + fallthrough; + case ON_STATE: + ctrl->state = POWEROFF_STATE; + mutex_unlock(&ctrl->state_lock); + if (events & PCI_EXP_SLTSTA_DLLSC) + ctrl_info(ctrl, "Slot(%s): Link Down\n", + slot_name(ctrl)); + if (events & PCI_EXP_SLTSTA_PDC) + ctrl_info(ctrl, "Slot(%s): Card not present\n", + slot_name(ctrl)); + sunway_pciehp_disable_slot(ctrl, SURPRISE_REMOVAL); + break; + default: + mutex_unlock(&ctrl->state_lock); + break; + } + + /* Turn the slot on if it's occupied or link is up */ + mutex_lock(&ctrl->state_lock); + present = sunway_pciehp_card_present(ctrl); + link_active = sunway_pciehp_check_link_active(ctrl); + if (present <= 0 && link_active <= 0) { + sunway_pciehp_end(ctrl, false); + mutex_unlock(&ctrl->state_lock); + return; + } + + switch (ctrl->state) { + case BLINKINGON_STATE: + cancel_delayed_work(&ctrl->button_work); + fallthrough; + case OFF_STATE: + ctrl->state = POWERON_STATE; + mutex_unlock(&ctrl->state_lock); + if (present) + ctrl_info(ctrl, "Slot(%s): Card present\n", + slot_name(ctrl)); + if (link_active) + ctrl_info(ctrl, "Slot(%s): Link Up\n", + slot_name(ctrl)); + ctrl->request_result = sunway_pciehp_enable_slot(ctrl); + sunway_pciehp_end(ctrl, true); + break; + default: + mutex_unlock(&ctrl->state_lock); + break; + } +} + +static int __sunway_pciehp_enable_slot(struct controller *ctrl) +{ + u8 getstatus = 0; + + if (MRL_SENS(ctrl)) { + sunway_pciehp_get_latch_status(ctrl, &getstatus); + if (getstatus) { + ctrl_info(ctrl, "Slot(%s): Latch open\n", + slot_name(ctrl)); + return -ENODEV; + } + } + + if (POWER_CTRL(ctrl)) { + sunway_pciehp_get_power_status(ctrl, &getstatus); + if (getstatus) { + ctrl_info(ctrl, "Slot(%s): Already enabled\n", + slot_name(ctrl)); + return 0; + } + } + + return board_added(ctrl); +} + +static int sunway_pciehp_enable_slot(struct controller *ctrl) +{ + int ret; + + pm_runtime_get_sync(&ctrl->pci_dev->dev); + ret = __sunway_pciehp_enable_slot(ctrl); + if (ret && ATTN_BUTTN(ctrl)) + /* may be blinking */ + sunway_pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + INDICATOR_NOOP); + pm_runtime_put(&ctrl->pci_dev->dev); + + mutex_lock(&ctrl->state_lock); + ctrl->state = ret ? OFF_STATE : ON_STATE; + mutex_unlock(&ctrl->state_lock); + + return ret; +} + +static int __sunway_pciehp_disable_slot(struct controller *ctrl, bool safe_removal) +{ + u8 getstatus = 0; + + if (POWER_CTRL(ctrl)) { + sunway_pciehp_get_power_status(ctrl, &getstatus); + if (!getstatus) { + ctrl_info(ctrl, "Slot(%s): Already disabled\n", + slot_name(ctrl)); + return -EINVAL; + } + } + + remove_board(ctrl, safe_removal); + return 0; +} + +static int sunway_pciehp_disable_slot(struct controller *ctrl, bool safe_removal) +{ + int ret; + + pm_runtime_get_sync(&ctrl->pci_dev->dev); + ret = __sunway_pciehp_disable_slot(ctrl, safe_removal); + pm_runtime_put(&ctrl->pci_dev->dev); + + mutex_lock(&ctrl->state_lock); + ctrl->state = OFF_STATE; + mutex_unlock(&ctrl->state_lock); + + return ret; +} + +int sunway_pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGON_STATE: + case OFF_STATE: + mutex_unlock(&ctrl->state_lock); + /* + * The IRQ thread becomes a no-op if the user pulls out the + * card before the thread wakes up, so initialize to -ENODEV. + */ + ctrl->request_result = -ENODEV; + sunway_pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC); + + wait_event(ctrl->requester, + !atomic_read(&ctrl->pending_events) && + !ctrl->ist_running); + + sunway_pciehp_start(hotplug_slot); + return ctrl->request_result; + case POWERON_STATE: + ctrl_info(ctrl, "Slot(%s): Already in powering on state\n", + slot_name(ctrl)); + break; + case BLINKINGOFF_STATE: + case ON_STATE: + case POWEROFF_STATE: + ctrl_info(ctrl, "Slot(%s): Already enabled\n", + slot_name(ctrl)); + break; + default: + ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", + slot_name(ctrl), ctrl->state); + break; + } + mutex_unlock(&ctrl->state_lock); + + return -ENODEV; +} + +int sunway_pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + + mutex_lock(&ctrl->state_lock); + switch (ctrl->state) { + case BLINKINGOFF_STATE: + case ON_STATE: + sunway_pciehp_start(hotplug_slot); + + mutex_unlock(&ctrl->state_lock); + wait_event(ctrl->requester, + !atomic_read(&ctrl->pending_events) && + !ctrl->ist_running); + + return ctrl->request_result; + case POWEROFF_STATE: + ctrl_info(ctrl, "Slot(%s): Already in powering off state\n", + slot_name(ctrl)); + break; + case BLINKINGON_STATE: + case OFF_STATE: + case POWERON_STATE: + ctrl_info(ctrl, "Slot(%s): Already disabled\n", + slot_name(ctrl)); + break; + default: + ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n", + slot_name(ctrl), ctrl->state); + break; + } + mutex_unlock(&ctrl->state_lock); + + return -ENODEV; +} diff --git a/drivers/pci/hotplug/sunway_pciehp_hpc.c b/drivers/pci/hotplug/sunway_pciehp_hpc.c new file mode 100644 index 000000000000..d30ee235b029 --- /dev/null +++ b/drivers/pci/hotplug/sunway_pciehp_hpc.c @@ -0,0 +1,1152 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sunway PCI Express PCI Hot Plug Driver + */ + +#define dev_fmt(fmt) "sunway_pciehp: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../pci.h" +#include "sunway_pciehp.h" + +#include +#include + +static const struct dmi_system_id inband_presence_disabled_dmi_table[] = { + /* + * Match all Dell systems, as some Dell systems have inband + * presence disabled on NVMe slots (but don't support the bit to + * report it). Setting inband presence disabled should have no + * negative effect, except on broken hotplug slots that never + * assert presence detect--and those will still work, they will + * just have a bit of extra delay before being probed. + */ + { + .ident = "Dell System", + .matches = { + DMI_MATCH(DMI_OEM_STRING, "Dell System"), + }, + }, + {} +}; + +static inline struct pci_dev *ctrl_dev(struct controller *ctrl) +{ + return ctrl->pci_dev; +} + +static irqreturn_t sunway_pciehp_isr(int irq, void *dev_id); +static irqreturn_t sunway_pciehp_ist(int irq, void *dev_id); +static int sunway_pciehp_poll_start(void *data); + +static inline int sunway_pciehp_request_irq(struct controller *ctrl) +{ + int retval, irq = ctrl->pci_dev->irq; + + if (sunway_pciehp_poll_mode) { + ctrl->poll_thread = kthread_run(&sunway_pciehp_poll_start, ctrl, + "sunway_pciehp_poll_start-%s", + slot_name(ctrl)); + return PTR_ERR_OR_ZERO(ctrl->poll_thread); + } + + /* Installs the interrupt handler */ + retval = request_threaded_irq(irq, sunway_pciehp_isr, sunway_pciehp_ist, + IRQF_SHARED, MY_NAME, ctrl); + if (retval) + ctrl_err(ctrl, "Cannot get irq %d for the hotplug controller\n", + irq); + return retval; +} + +static inline void sunway_pciehp_free_irq(struct controller *ctrl) +{ + if (sunway_pciehp_poll_mode) + kthread_stop(ctrl->poll_thread); + else + free_irq(ctrl->pci_dev->irq, ctrl); +} + +static int pcie_poll_cmd(struct controller *ctrl, int timeout) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + + do { + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status == (u16) ~0) { + ctrl_info(ctrl, "%s: no response from device\n", + __func__); + return 0; + } + + if (slot_status & PCI_EXP_SLTSTA_CC) { + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_CC); + return 1; + } + msleep(10); + timeout -= 10; + } while (timeout >= 0); + return 0; /* timeout */ +} + +static void pcie_wait_cmd(struct controller *ctrl) +{ + unsigned int msecs = sunway_pciehp_poll_mode ? 2500 : 1000; + unsigned long duration = msecs_to_jiffies(msecs); + unsigned long cmd_timeout = ctrl->cmd_started + duration; + unsigned long now, timeout; + int rc; + + /* + * If the controller does not generate notifications for command + * completions, we never need to wait between writes. + */ + if (NO_CMD_CMPL(ctrl)) + return; + + if (!ctrl->cmd_busy) + return; + + /* + * Even if the command has already timed out, we want to call + * pcie_poll_cmd() so it can clear PCI_EXP_SLTSTA_CC. + */ + now = jiffies; + if (time_before_eq(cmd_timeout, now)) + timeout = 1; + else + timeout = cmd_timeout - now; + + if (ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE && + ctrl->slot_ctrl & PCI_EXP_SLTCTL_CCIE) + rc = wait_event_timeout(ctrl->queue, !ctrl->cmd_busy, timeout); + else + rc = pcie_poll_cmd(ctrl, jiffies_to_msecs(timeout)); + + if (!rc) + ctrl_info(ctrl, "Timeout on hotplug command %#06x (issued %u msec ago)\n", + ctrl->slot_ctrl, + jiffies_to_msecs(jiffies - ctrl->cmd_started)); +} + +#define CC_ERRATUM_MASK (PCI_EXP_SLTCTL_PCC | \ + PCI_EXP_SLTCTL_PIC | \ + PCI_EXP_SLTCTL_AIC | \ + PCI_EXP_SLTCTL_EIC) + +static void pcie_do_write_cmd(struct controller *ctrl, u16 cmd, + u16 mask, bool wait) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_ctrl_orig, slot_ctrl; + + mutex_lock(&ctrl->ctrl_lock); + + /* + * Always wait for any previous command that might still be in progress + */ + pcie_wait_cmd(ctrl); + + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + if (slot_ctrl == (u16) ~0) { + ctrl_info(ctrl, "%s: no response from device\n", __func__); + goto out; + } + + slot_ctrl_orig = slot_ctrl; + slot_ctrl &= ~mask; + slot_ctrl |= (cmd & mask); + ctrl->cmd_busy = 1; + smp_mb(); + ctrl->slot_ctrl = slot_ctrl; + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); + ctrl->cmd_started = jiffies; + + /* + * Controllers with the Intel CF118 and similar errata advertise + * Command Completed support, but they only set Command Completed + * if we change the "Control" bits for power, power indicator, + * attention indicator, or interlock. If we only change the + * "Enable" bits, they never set the Command Completed bit. + */ + if ((slot_ctrl_orig & CC_ERRATUM_MASK) == (slot_ctrl & CC_ERRATUM_MASK)) + ctrl->cmd_busy = 0; + + /* + * Optionally wait for the hardware to be ready for a new command, + * indicating completion of the above issued command. + */ + if (wait) + pcie_wait_cmd(ctrl); + +out: + mutex_unlock(&ctrl->ctrl_lock); +} + +/** + * pcie_write_cmd - Issue controller command + * @ctrl: controller to which the command is issued + * @cmd: command value written to slot control register + * @mask: bitmask of slot control register to be modified + */ +static void pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) +{ + pcie_do_write_cmd(ctrl, cmd, mask, true); +} + +/* Same as above without waiting for the hardware to latch */ +static void pcie_write_cmd_nowait(struct controller *ctrl, u16 cmd, u16 mask) +{ + pcie_do_write_cmd(ctrl, cmd, mask, false); +} + +/** + * sunway_pciehp_check_link_active() - Is the link active + * @ctrl: PCIe hotplug controller + * + * Check whether the downstream link is currently active. Note it is + * possible that the card is removed immediately after this so the + * caller may need to take it into account. + * + * If the hotplug controller itself is not available anymore returns + * %-ENODEV. + */ +int sunway_pciehp_check_link_active(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 lnk_status; + int ret; + + ret = pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); + if (ret == PCIBIOS_DEVICE_NOT_FOUND || lnk_status == (u16)~0) + return -ENODEV; + + ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); + ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); + + return ret; +} + +static bool pcie_wait_link_active(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + struct pci_bus *bus = pdev->bus; + struct pci_controller *hose = pci_bus_to_pci_controller(bus); + + if (pcie_wait_for_link(pdev, true)) { + pci_mark_rc_linkup(hose->node, hose->index); + sunway_pciehp_restore_rc_piu(ctrl); + return true; + } + + return false; +} + +static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) +{ + u32 l; + int count = 0; + int delay = 1000, step = 20; + bool found = false; + + do { + found = pci_bus_read_dev_vendor_id(bus, devfn, &l, 0); + count++; + + if (found) + break; + + msleep(step); + delay -= step; + } while (delay > 0); + + if (count > 1) + pr_debug("pci %04x:%02x:%02x.%d id reading try %d times with interval %d ms to get %08x\n", + pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), + PCI_FUNC(devfn), count, step, l); + + return found; +} + +static void pcie_wait_for_presence(struct pci_dev *pdev) +{ + int timeout = 1250; + u16 slot_status; + + do { + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_PDS) + return; + msleep(10); + timeout -= 10; + } while (timeout > 0); +} + +static bool pci_bus_check_linkup(struct pci_controller *hose) +{ + int count = 0; + int delay = 1000, step = 20; + bool linkup = false; + + do { + if (sw64_chip_init->pci_init.check_pci_linkup(hose->node, hose->index) == 0) { + udelay(10); + if (sw64_chip_init->pci_init.check_pci_linkup(hose->node, hose->index) == 0) + linkup = true; + } + count++; + + if (linkup) + break; + + msleep(step); + delay -= step; + } while (delay > 0); + + if (count > 1) + pr_debug("Node %ld RC %ld linkup reading try %d times with interval %d ms\n", + hose->node, hose->index, count, step); + + return linkup; +} + +int sunway_pciehp_check_link_status(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + struct pci_bus *bus = pdev->bus; + struct pci_controller *hose = pci_bus_to_pci_controller(bus); + bool found, linkup; + u16 lnk_status; + + if (!pcie_wait_link_active(ctrl)) { + ctrl_info(ctrl, "Slot(%s): No link\n", slot_name(ctrl)); + return -1; + } + + if (ctrl->inband_presence_disabled) + pcie_wait_for_presence(pdev); + + /* wait 1000ms and check RC linkup before read pci conf */ + msleep(1000); + linkup = pci_bus_check_linkup(hose); + + if (!linkup) + return -1; + + /* try read device id in 1s */ + found = pci_bus_check_dev(ctrl->pci_dev->subordinate, + PCI_DEVFN(0, 0)); + + /* ignore link or presence changes up to this point */ + if (found) + atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC), + &ctrl->pending_events); + + pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &lnk_status); + ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); + if ((lnk_status & PCI_EXP_LNKSTA_LT) || + !(lnk_status & PCI_EXP_LNKSTA_NLW)) { + ctrl_info(ctrl, "Slot(%s): Cannot train link: status %#06x\n", + slot_name(ctrl), lnk_status); + return -1; + } + + pcie_update_link_speed(ctrl->pci_dev->subordinate, lnk_status); + + if (!found) { + ctrl_info(ctrl, "Slot(%s): No device found\n", + slot_name(ctrl)); + return -1; + } + + return 0; +} + +static int __sunway_pciehp_link_set(struct controller *ctrl, bool enable) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 lnk_ctrl; + + pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &lnk_ctrl); + + if (enable) + lnk_ctrl &= ~PCI_EXP_LNKCTL_LD; + else + lnk_ctrl |= PCI_EXP_LNKCTL_LD; + + pcie_capability_write_word(pdev, PCI_EXP_LNKCTL, lnk_ctrl); + ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl); + + return 0; +} + +int sunway_pciehp_link_enable(struct controller *ctrl) +{ + return __sunway_pciehp_link_set(ctrl, true); +} + +int sunway_pciehp_link_disable(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + struct pci_bus *bus = pdev->bus; + struct pci_controller *hose = pci_bus_to_pci_controller(bus); + int ret; + + ret = __sunway_pciehp_link_set(ctrl, false); + pci_clear_rc_linkup(hose->node, hose->index); + + return ret; +} + +int sunway_pciehp_get_raw_indicator_status(struct hotplug_slot *hotplug_slot, + u8 *status) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_ctrl; + + pci_config_pm_runtime_get(pdev); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + pci_config_pm_runtime_put(pdev); + *status = (slot_ctrl & (PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC)) >> 6; + return 0; +} + +int sunway_pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_ctrl; + + pci_config_pm_runtime_get(pdev); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + pci_config_pm_runtime_put(pdev); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__, + pci_pcie_cap(ctrl->pci_dev) + PCI_EXP_SLTCTL, slot_ctrl); + + switch (slot_ctrl & PCI_EXP_SLTCTL_AIC) { + case PCI_EXP_SLTCTL_ATTN_IND_ON: + *status = 1; /* On */ + break; + case PCI_EXP_SLTCTL_ATTN_IND_BLINK: + *status = 2; /* Blink */ + break; + case PCI_EXP_SLTCTL_ATTN_IND_OFF: + *status = 0; /* Off */ + break; + default: + *status = 0xFF; + break; + } + + return 0; +} + +void sunway_pciehp_get_power_status(struct controller *ctrl, u8 *status) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_ctrl; + + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + + ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__, + pci_pcie_cap(ctrl->pci_dev) + PCI_EXP_SLTCTL, slot_ctrl); + + switch (slot_ctrl & PCI_EXP_SLTCTL_PCC) { + case PCI_EXP_SLTCTL_PWR_ON: + *status = 1; /* On */ + break; + case PCI_EXP_SLTCTL_PWR_OFF: + *status = 0; /* Off */ + break; + default: + *status = 0xFF; + break; + } +} + +void sunway_pciehp_get_latch_status(struct controller *ctrl, u8 *status) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); +} + +/** + * sunway_pciehp_card_present() - Is the card present + * @ctrl: PCIe hotplug controller + * + * Function checks whether the card is currently present in the slot and + * in that case returns true. Note it is possible that the card is + * removed immediately after the check so the caller may need to take + * this into account. + * + * It the hotplug controller itself is not available anymore returns + * %-ENODEV. + */ +int sunway_pciehp_card_present(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + int ret; + + ret = pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (ret == PCIBIOS_DEVICE_NOT_FOUND || slot_status == (u16)~0) + return -ENODEV; + + return !!(slot_status & PCI_EXP_SLTSTA_PDS); +} + +/** + * sunway_pciehp_card_present_or_link_active() - whether given slot is occupied + * @ctrl: PCIe hotplug controller + * + * Unlike pciehp_card_present(), which determines presence solely from the + * Presence Detect State bit, this helper also returns true if the Link Active + * bit is set. This is a concession to broken hotplug ports which hardwire + * Presence Detect State to zero, such as Wilocity's [1ae9:0200]. + * + * Returns: %1 if the slot is occupied and %0 if it is not. If the hotplug + * port is not present anymore returns %-ENODEV. + */ +int sunway_pciehp_card_present_or_link_active(struct controller *ctrl) +{ + int ret; + + ret = sunway_pciehp_card_present(ctrl); + if (ret) + return ret; + + return sunway_pciehp_check_link_active(ctrl); +} + +int sunway_pciehp_query_power_fault(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + return !!(slot_status & PCI_EXP_SLTSTA_PFD); +} + +int sunway_pciehp_set_raw_indicator_status(struct hotplug_slot *hotplug_slot, + u8 status) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl_dev(ctrl); + + pci_config_pm_runtime_get(pdev); + pcie_write_cmd_nowait(ctrl, status << 6, + PCI_EXP_SLTCTL_AIC | PCI_EXP_SLTCTL_PIC); + pci_config_pm_runtime_put(pdev); + return 0; +} + +/** + * sunway_pciehp_set_indicators() - set attention indicator, power indicator, or both + * @ctrl: PCIe hotplug controller + * @pwr: one of: + * PCI_EXP_SLTCTL_PWR_IND_ON + * PCI_EXP_SLTCTL_PWR_IND_BLINK + * PCI_EXP_SLTCTL_PWR_IND_OFF + * @attn: one of: + * PCI_EXP_SLTCTL_ATTN_IND_ON + * PCI_EXP_SLTCTL_ATTN_IND_BLINK + * PCI_EXP_SLTCTL_ATTN_IND_OFF + * + * Either @pwr or @attn can also be INDICATOR_NOOP to leave that indicator + * unchanged. + */ +void sunway_pciehp_set_indicators(struct controller *ctrl, int pwr, int attn) +{ + u16 cmd = 0, mask = 0; + + if (PWR_LED(ctrl) && pwr != INDICATOR_NOOP) { + cmd |= (pwr & PCI_EXP_SLTCTL_PIC); + mask |= PCI_EXP_SLTCTL_PIC; + } + + if (ATTN_LED(ctrl) && attn != INDICATOR_NOOP) { + cmd |= (attn & PCI_EXP_SLTCTL_AIC); + mask |= PCI_EXP_SLTCTL_AIC; + } + + if (cmd) { + pcie_write_cmd_nowait(ctrl, cmd, mask); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pci_dev) + PCI_EXP_SLTCTL, cmd); + } +} + +int sunway_pciehp_power_on_slot(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 slot_status; + int retval; + + /* Clear power-fault bit from previous power failures */ + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status); + if (slot_status & PCI_EXP_SLTSTA_PFD) + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PFD); + ctrl->power_fault_detected = 0; + + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_ON, PCI_EXP_SLTCTL_PCC); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pci_dev) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_ON); + + retval = sunway_pciehp_link_enable(ctrl); + if (retval) + ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__); + + return retval; +} + +void sunway_pciehp_power_off_slot(struct controller *ctrl) +{ + pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PWR_OFF, PCI_EXP_SLTCTL_PCC); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pci_dev) + PCI_EXP_SLTCTL, + PCI_EXP_SLTCTL_PWR_OFF); +} + +void sunway_pciehp_poll(struct controller *ctrl, u32 events) +{ + struct pci_dev *pdev = ctrl_dev(ctrl); + struct pci_bus *bus = pdev->bus; + struct pci_controller *hose = pci_bus_to_pci_controller(bus); + unsigned long piu_value; + unsigned long node, rc_index; + u16 slot_ctrl; + int i; + + node = hose->node; + rc_index = hose->index; + + if (events & SW64_POLL_DISABLE_SLOT) { + while (1) { + piu_value = read_piu_ior1(node, rc_index, NEWLTSSMSTATE0); + piu_value &= 0xff; + + if (piu_value == 0x19) + break; + + udelay(10); + } + + write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_REMOVE); + + while (1) { + piu_value = read_piu_ior0(node, rc_index, HP_WATCHOUT); + piu_value >>= 24; + + if (piu_value == 0x19) + break; + + udelay(10); + } + + sunway_pciehp_link_enable(ctrl); + + mdelay(100); + while (1) { + piu_value = read_piu_ior1(node, rc_index, NEWLTSSMSTATE0); + piu_value &= 0xff; + + if (piu_value == 0x0) + break; + + udelay(10); + } + + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + slot_ctrl &= ~(PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE | + PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_HPIE); + slot_ctrl |= (PCI_EXP_SLTCTL_ATTN_IND_OFF | + PCI_EXP_SLTCTL_PWR_IND_OFF | + PCI_EXP_SLTCTL_DLLSCE); + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); + + write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_FINISH); + + ctrl->state = OFF_STATE; + } + + if (events & SW64_POLL_ENABLE_SLOT) { + write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_INSERT); + + for (i = 0; i < 30; i++) { + if (pcie_wait_for_link(pdev, true)) { + pci_mark_rc_linkup(hose->node, hose->index); + sunway_pciehp_restore_rc_piu(ctrl); + + write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_FINISH); + + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); + slot_ctrl &= ~PCI_EXP_SLTCTL_PWR_OFF; + pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); + + ctrl->state = ON_STATE; + return; + } + + if (sunway_pciehp_query_power_fault(ctrl)) { + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PFD); + udelay(10); + if (sunway_pciehp_query_power_fault(ctrl)) { + ctrl_err(ctrl, "power fault\n"); + return; + } + } + } + + ctrl_err(ctrl, "insert failed\n"); + } +} + +static irqreturn_t sunway_pciehp_isr(int irq, void *dev_id) +{ + struct controller *ctrl = (struct controller *)dev_id; + struct pci_dev *pdev = ctrl_dev(ctrl); + struct device *parent = pdev->dev.parent; + u16 status, events = 0; + + /* + * Interrupts only occur in D3hot or shallower and only if enabled + * in the Slot Control register (PCIe r4.0, sec 6.7.3.4). + */ + if (pdev->current_state == PCI_D3cold || + (!(ctrl->slot_ctrl & PCI_EXP_SLTCTL_HPIE) && !sunway_pciehp_poll_mode)) + return IRQ_NONE; + + /* + * Keep the port accessible by holding a runtime PM ref on its parent. + * Defer resume of the parent to the IRQ thread if it's suspended. + * Mask the interrupt until then. + */ + if (parent) { + pm_runtime_get_noresume(parent); + if (!pm_runtime_active(parent)) { + pm_runtime_put(parent); + disable_irq_nosync(irq); + atomic_or(RERUN_ISR, &ctrl->pending_events); + return IRQ_WAKE_THREAD; + } + } + + if (sunway_pciehp_poll_mode) { + if (atomic_read(&ctrl->pending_events) & SW64_POLL_DISABLE_SLOT) { + sunway_pciehp_link_disable(ctrl); + goto out; + } else if (atomic_read(&ctrl->pending_events) & SW64_POLL_ENABLE_SLOT) + goto out; + + return IRQ_NONE; + } + +read_status: + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status); + if (status == (u16) ~0) { + ctrl_info(ctrl, "%s: no response from device\n", __func__); + if (parent) + pm_runtime_put(parent); + return IRQ_NONE; + } + + /* + * Slot Status contains plain status bits as well as event + * notification bits; right now we only want the event bits. + */ + status &= PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | + PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC | + PCI_EXP_SLTSTA_DLLSC; + + /* + * If we've already reported a power fault, don't report it again + * until we've done something to handle it. + */ + if (ctrl->power_fault_detected) + status &= ~PCI_EXP_SLTSTA_PFD; + + events |= status; + if (!events) { + if (parent) + pm_runtime_put(parent); + return IRQ_NONE; + } + + if (status) { + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events); + + /* + * In MSI mode, all event bits must be zero before the port + * will send a new interrupt (PCIe Base Spec r5.0 sec 6.7.3.4). + * So re-read the Slot Status register in case a bit was set + * between read and write. + */ + if (pci_dev_msi_enabled(pdev) && !sunway_pciehp_poll_mode) + goto read_status; + } + + ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events); + if (parent) + pm_runtime_put(parent); + + /* + * Command Completed notifications are not deferred to the + * IRQ thread because it may be waiting for their arrival. + */ + if (events & PCI_EXP_SLTSTA_CC) { + ctrl->cmd_busy = 0; + smp_mb(); + wake_up(&ctrl->queue); + + if (events == PCI_EXP_SLTSTA_CC) + return IRQ_HANDLED; + + events &= ~PCI_EXP_SLTSTA_CC; + } + +out: + if (pdev->ignore_hotplug) { + ctrl_dbg(ctrl, "ignoring hotplug event %#06x\n", events); + return IRQ_HANDLED; + } + + /* Save pending events for consumption by IRQ thread. */ + atomic_or(events, &ctrl->pending_events); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t sunway_pciehp_ist(int irq, void *dev_id) +{ + struct controller *ctrl = (struct controller *)dev_id; + struct pci_dev *pdev = ctrl_dev(ctrl); + irqreturn_t ret; + u32 events; + + ctrl->ist_running = true; + pci_config_pm_runtime_get(pdev); + + /* rerun sunway_pciehp_isr() if the port was inaccessible on interrupt */ + if (atomic_fetch_and(~RERUN_ISR, &ctrl->pending_events) & RERUN_ISR) { + ret = sunway_pciehp_isr(irq, dev_id); + enable_irq(irq); + if (ret != IRQ_WAKE_THREAD) + goto out; + } + + synchronize_hardirq(irq); + events = atomic_xchg(&ctrl->pending_events, 0); + if (!events) { + ret = IRQ_NONE; + goto out; + } + + if (sunway_pciehp_poll_mode) { + sunway_pciehp_poll(ctrl, events); + ret = IRQ_HANDLED; + goto out; + } + + /* Check Attention Button Pressed */ + if (events & PCI_EXP_SLTSTA_ABP) { + ctrl_info(ctrl, "Slot(%s): Attention button pressed\n", + slot_name(ctrl)); + sunway_pciehp_handle_button_press(ctrl); + } + + /* Check Power Fault Detected */ + if ((events & PCI_EXP_SLTSTA_PFD) && !ctrl->power_fault_detected) { + ctrl->power_fault_detected = 1; + ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl)); + sunway_pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF, + PCI_EXP_SLTCTL_ATTN_IND_ON); + } + + /* + * Disable requests have higher priority than Presence Detect Changed + * or Data Link Layer State Changed events. + */ + down_read(&ctrl->reset_lock); + + if (events & DISABLE_SLOT) + sunway_pciehp_handle_disable_request(ctrl); + else if (events & (PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC)) + sunway_pciehp_handle_presence_or_link_change(ctrl, events); + + up_read(&ctrl->reset_lock); + + ret = IRQ_HANDLED; +out: + pci_config_pm_runtime_put(pdev); + ctrl->ist_running = false; + wake_up(&ctrl->requester); + return ret; +} + +static int sunway_pciehp_poll_start(void *data) +{ + struct controller *ctrl = data; + + schedule_timeout_idle(10 * HZ); /* start with 10 sec delay */ + + while (!kthread_should_stop()) { + /* poll for interrupt events or user requests */ + while (sunway_pciehp_isr(IRQ_NOTCONNECTED, ctrl) == IRQ_WAKE_THREAD || + atomic_read(&ctrl->pending_events)) + sunway_pciehp_ist(IRQ_NOTCONNECTED, ctrl); + + if (sunway_pciehp_poll_time <= 0 || sunway_pciehp_poll_time > 60) + sunway_pciehp_poll_time = 2; /* clamp to sane value */ + + schedule_timeout_idle(sunway_pciehp_poll_time * HZ); + } + + return 0; +} + +static void pcie_enable_notification(struct controller *ctrl) +{ + u16 cmd, mask; + + /* + * TBD: Power fault detected software notification support. + * + * Power fault detected software notification is not enabled + * now, because it caused power fault detected interrupt storm + * on some machines. On those machines, power fault detected + * bit in the slot status register was set again immediately + * when it is cleared in the interrupt service routine, and + * next power fault detected interrupt was notified again. + */ + + /* + * Always enable link events: thus link-up and link-down shall + * always be treated as hotplug and unplug respectively. Enable + * presence detect only if Attention Button is not present. + */ + cmd = PCI_EXP_SLTCTL_DLLSCE; + if (ATTN_BUTTN(ctrl)) + cmd |= PCI_EXP_SLTCTL_ABPE; + else + cmd |= PCI_EXP_SLTCTL_PDCE; + if (!sunway_pciehp_poll_mode) + cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE; + + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_DLLSCE); + + pcie_write_cmd_nowait(ctrl, cmd, mask); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pci_dev) + PCI_EXP_SLTCTL, cmd); +} + +static void pcie_disable_notification(struct controller *ctrl) +{ + u16 mask; + + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE | + PCI_EXP_SLTCTL_DLLSCE); + pcie_write_cmd(ctrl, 0, mask); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pci_dev) + PCI_EXP_SLTCTL, 0); +} + +void sunway_pcie_clear_hotplug_events(struct controller *ctrl) +{ + pcie_capability_write_word(ctrl_dev(ctrl), PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_DLLSC); +} + +/* + * sunway_pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary + * bus reset of the bridge, but at the same time we want to ensure that it is + * not seen as a hot-unplug, followed by the hot-plug of the device. Thus, + * disable link state notification and presence detection change notification + * momentarily, if we see that they could interfere. Also, clear any spurious + * events after. + */ +int sunway_pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe) +{ + struct controller *ctrl = to_ctrl(hotplug_slot); + struct pci_dev *pdev = ctrl_dev(ctrl); + u16 stat_mask = 0, ctrl_mask = 0; + int rc; + + if (probe) + return 0; + + down_write(&ctrl->reset_lock); + + if (!ATTN_BUTTN(ctrl)) { + ctrl_mask |= PCI_EXP_SLTCTL_PDCE; + stat_mask |= PCI_EXP_SLTSTA_PDC; + } + ctrl_mask |= PCI_EXP_SLTCTL_DLLSCE; + stat_mask |= PCI_EXP_SLTSTA_DLLSC; + + pcie_write_cmd(ctrl, 0, ctrl_mask); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pci_dev) + PCI_EXP_SLTCTL, 0); + + rc = pci_bridge_secondary_bus_reset(ctrl->pci_dev); + + pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, stat_mask); + pcie_write_cmd_nowait(ctrl, ctrl_mask, ctrl_mask); + ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__, + pci_pcie_cap(ctrl->pci_dev) + PCI_EXP_SLTCTL, ctrl_mask); + + up_write(&ctrl->reset_lock); + return rc; +} + +int sunway_pcie_init_notification(struct controller *ctrl) +{ + if (sunway_pciehp_request_irq(ctrl)) + return -1; + pcie_enable_notification(ctrl); + ctrl->notification_enabled = 1; + return 0; +} + +void sunway_pcie_shutdown_notification(struct controller *ctrl) +{ + if (ctrl->notification_enabled) { + pcie_disable_notification(ctrl); + sunway_pciehp_free_irq(ctrl); + ctrl->notification_enabled = 0; + } +} + +static inline void dbg_ctrl(struct controller *ctrl) +{ + struct pci_dev *pdev = ctrl->pci_dev; + u16 reg16; + + ctrl_dbg(ctrl, "Slot Capabilities : 0x%08x\n", ctrl->slot_cap); + pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, ®16); + ctrl_dbg(ctrl, "Slot Status : 0x%04x\n", reg16); + pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, ®16); + ctrl_dbg(ctrl, "Slot Control : 0x%04x\n", reg16); +} + +#define FLAG(x, y) (((x) & (y)) ? '+' : '-') + +struct controller *sunwayhpc_init(struct pci_dev *dev) +{ + struct controller *ctrl; + u32 slot_cap, slot_cap2, link_cap; + u8 poweron; + struct pci_bus *subordinate = dev->subordinate; + + ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return NULL; + + ctrl->pci_dev = dev; + pcie_capability_read_dword(dev, PCI_EXP_SLTCAP, &slot_cap); + + if (dev->hotplug_user_indicators) + slot_cap &= ~(PCI_EXP_SLTCAP_AIP | PCI_EXP_SLTCAP_PIP); + + /* + * We assume no Thunderbolt controllers support Command Complete events, + * but some controllers falsely claim they do. + */ + if (dev->is_thunderbolt) + slot_cap |= PCI_EXP_SLTCAP_NCCS; + + ctrl->slot_cap = slot_cap; + mutex_init(&ctrl->ctrl_lock); + mutex_init(&ctrl->state_lock); + init_rwsem(&ctrl->reset_lock); + init_waitqueue_head(&ctrl->requester); + init_waitqueue_head(&ctrl->queue); + INIT_DELAYED_WORK(&ctrl->button_work, sunway_pciehp_queue_pushbutton_work); + dbg_ctrl(ctrl); + + down_read(&pci_bus_sem); + ctrl->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE; + up_read(&pci_bus_sem); + + pcie_capability_read_dword(dev, PCI_EXP_SLTCAP2, &slot_cap2); + if (slot_cap2 & PCI_EXP_SLTCAP2_IBPD) { + pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_IBPD_DISABLE, + PCI_EXP_SLTCTL_IBPD_DISABLE); + ctrl->inband_presence_disabled = 1; + } + + if (dmi_first_match(inband_presence_disabled_dmi_table)) + ctrl->inband_presence_disabled = 1; + + /* Check if Data Link Layer Link Active Reporting is implemented */ + pcie_capability_read_dword(dev, PCI_EXP_LNKCAP, &link_cap); + + /* Clear all remaining event bits in Slot Status register. */ + pcie_capability_write_word(dev, PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | + PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC | + PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC); + + ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c IbPresDis%c LLActRep%c\n", + (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, + FLAG(slot_cap, PCI_EXP_SLTCAP_ABP), + FLAG(slot_cap, PCI_EXP_SLTCAP_PCP), + FLAG(slot_cap, PCI_EXP_SLTCAP_MRLSP), + FLAG(slot_cap, PCI_EXP_SLTCAP_AIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_PIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_HPC), + FLAG(slot_cap, PCI_EXP_SLTCAP_HPS), + FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), + FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), + FLAG(slot_cap2, PCI_EXP_SLTCAP2_IBPD), + FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC)); + + /* + * If empty slot's power status is on, turn power off. The IRQ isn't + * requested yet, so avoid triggering a notification with this command. + */ + if (POWER_CTRL(ctrl)) { + sunway_pciehp_get_power_status(ctrl, &poweron); + if (!sunway_pciehp_card_present_or_link_active(ctrl) && poweron) { + pcie_disable_notification(ctrl); + sunway_pciehp_power_off_slot(ctrl); + } + } + + return ctrl; +} + +void sunway_pciehp_release_ctrl(struct controller *ctrl) +{ + cancel_delayed_work_sync(&ctrl->button_work); + kfree(ctrl); +} diff --git a/drivers/pci/hotplug/sunway_pciehp_pci.c b/drivers/pci/hotplug/sunway_pciehp_pci.c new file mode 100644 index 000000000000..f627ea054342 --- /dev/null +++ b/drivers/pci/hotplug/sunway_pciehp_pci.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Sunway PCI Express Hot Plug Controller Driver + */ + +#define dev_fmt(fmt) "sunway_pciehp: " fmt + +#include +#include +#include +#include +#include "../pci.h" +#include "sunway_pciehp.h" + +/** + * pciehp_configure_device() - enumerate PCI devices below a hotplug bridge + * @ctrl: PCIe hotplug controller + * + * Enumerate PCI devices below a hotplug bridge and add them to the system. + * Return 0 on success, %-EEXIST if the devices are already enumerated or + * %-ENODEV if enumeration failed. + */ +int sunway_pciehp_configure_device(struct controller *ctrl) +{ + struct pci_dev *dev; + struct pci_dev *bridge = ctrl->pci_dev; + struct pci_bus *parent = bridge->subordinate; + int num, ret = 0; + + pci_lock_rescan_remove(); + + dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); + if (dev) { + /* + * The device is already there. Either configured by the + * boot firmware or a previous hotplug event. + */ + ctrl_dbg(ctrl, "Device %s already exists at %04x:%02x:00, skipping hot-add\n", + pci_name(dev), pci_domain_nr(parent), parent->number); + pci_dev_put(dev); + ret = -EEXIST; + goto out; + } + + num = pci_scan_slot(parent, PCI_DEVFN(0, 0)); + if (num == 0) { + ctrl_err(ctrl, "No new device found\n"); + ret = -ENODEV; + goto out; + } + + for_each_pci_bridge(dev, parent) + pci_hp_add_bridge(dev); + + pci_assign_unassigned_bridge_resources(bridge); + pcie_bus_configure_settings(parent); + pci_bus_add_devices(parent); + + out: + pci_unlock_rescan_remove(); + return ret; +} + +/** + * pciehp_unconfigure_device() - remove PCI devices below a hotplug bridge + * @ctrl: PCIe hotplug controller + * @presence: whether the card is still present in the slot; + * true for safe removal via sysfs or an Attention Button press, + * false for surprise removal + * + * Unbind PCI devices below a hotplug bridge from their drivers and remove + * them from the system. Safely removed devices are quiesced. Surprise + * removed devices are marked as such to prevent further accesses. + */ +void sunway_pciehp_unconfigure_device(struct controller *ctrl, bool presence) +{ + struct pci_dev *dev, *temp; + struct pci_bus *parent = ctrl->pci_dev->subordinate; + u16 command; + + ctrl_dbg(ctrl, "%s: domain:bus:dev = %04x:%02x:00\n", + __func__, pci_domain_nr(parent), parent->number); + + if (!presence) + pci_walk_bus(parent, pci_dev_set_disconnected, NULL); + + pci_lock_rescan_remove(); + + /* + * Stopping an SR-IOV PF device removes all the associated VFs, + * which will update the bus->devices list and confuse the + * iterator. Therefore, iterate in reverse so we remove the VFs + * first, then the PF. We do the same in pci_stop_bus_device(). + */ + list_for_each_entry_safe_reverse(dev, temp, &parent->devices, + bus_list) { + pci_dev_get(dev); + pci_stop_and_remove_bus_device(dev); + /* + * Ensure that no new Requests will be generated from + * the device. + */ + if (presence) { + pci_read_config_word(dev, PCI_COMMAND, &command); + command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR); + command |= PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(dev, PCI_COMMAND, command); + } + pci_dev_put(dev); + } + + pci_unlock_rescan_remove(); +} -- Gitee From ed76b69ab048995e506b19d32b939e26c83cd698 Mon Sep 17 00:00:00 2001 From: Xu Yiwei Date: Tue, 14 May 2024 09:43:02 +0000 Subject: [PATCH 13/25] anolis: sw64: gpu: memset_io and memcpy_toio/fromio for iomem on AMD swsmu ANBZ: #4688 This commit complements commit cb7b5777c70b ("anolis: sw64: gpu: correct low-level mmio memset/memcpy direct calls"). Signed-off-by: Xu Yiwei Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index 12618a583e97..5aa0bdbde7bf 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -876,7 +876,11 @@ int smu_cmn_update_table(struct smu_context *smu, table_size = smu_table->tables[table_index].size; if (drv2smu) { +#if IS_ENABLED(CONFIG_SW64) + memcpy_toio(table->cpu_addr, table_data, table_size); +#else memcpy(table->cpu_addr, table_data, table_size); +#endif /* * Flush hdp cache: to guard the content seen by * GPU is consitent with CPU. @@ -894,7 +898,11 @@ int smu_cmn_update_table(struct smu_context *smu, if (!drv2smu) { amdgpu_asic_invalidate_hdp(adev, NULL); +#if IS_ENABLED(CONFIG_SW64) + memcpy_fromio(table_data, table->cpu_addr, table_size); +#else memcpy(table_data, table->cpu_addr, table_size); +#endif } return 0; @@ -950,7 +958,11 @@ int smu_cmn_get_metrics_table(struct smu_context *smu, } if (metrics_table) +#if IS_ENABLED(CONFIG_SW64) + memcpy_toio(metrics_table, smu_table->metrics_table, table_size); +#else memcpy(metrics_table, smu_table->metrics_table, table_size); +#endif return 0; } -- Gitee From a5ce61b422d0390530ab93c26e4ba81d93e2baf6 Mon Sep 17 00:00:00 2001 From: Zheng Chongzhen Date: Wed, 15 May 2024 16:07:57 +0000 Subject: [PATCH 14/25] anolis: sw64: pci: use readq/writeq to read/write RC and PIU IO registers ANBZ: #4688 Because kernel get all register base addresses of PCIe controller from firmware, use the base addresses to instead of macro. Remove functions read/write_rc_conf, read/write_piu_ior0 and read/write_piu_ior1, and use unified functions readq/writeq to access all PCI related IO registers. Signed-off-by: Zheng Chongzhen Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/include/asm/msi.h | 3 +- arch/sw_64/include/asm/sw64_init.h | 6 +- arch/sw_64/include/asm/sw64io.h | 60 -------------- arch/sw_64/kernel/chip_setup.c | 91 ++++++++++---------- arch/sw_64/kernel/reset.c | 10 +-- arch/sw_64/pci/pci-legacy.c | 10 +-- arch/sw_64/pci/pci.c | 15 ++-- drivers/irqchip/irq-sunway-cpu.c | 24 +++--- drivers/irqchip/irq-sunway-msi.c | 9 +- drivers/irqchip/irq-sunway-pci-intx.c | 71 ++++++++-------- drivers/pci/controller/pci-sunway.c | 101 ++++++++++++----------- drivers/pci/hotplug/sunway_pciehp_ctrl.c | 86 +++++++++---------- drivers/pci/hotplug/sunway_pciehp_hpc.c | 25 +++--- 13 files changed, 228 insertions(+), 283 deletions(-) diff --git a/arch/sw_64/include/asm/msi.h b/arch/sw_64/include/asm/msi.h index dbf6f81843be..e5d93040b2d3 100644 --- a/arch/sw_64/include/asm/msi.h +++ b/arch/sw_64/include/asm/msi.h @@ -48,8 +48,7 @@ struct sw64_msi_chip_data { unsigned long msi_config; unsigned long msiaddr; }; - unsigned long rc_node; - unsigned long rc_index; + struct pci_controller *hose; unsigned int msi_config_index; unsigned int dst_cpu; unsigned int vector; diff --git a/arch/sw_64/include/asm/sw64_init.h b/arch/sw_64/include/asm/sw64_init.h index 546be1a35250..2d553242487d 100644 --- a/arch/sw_64/include/asm/sw64_init.h +++ b/arch/sw_64/include/asm/sw64_init.h @@ -17,10 +17,8 @@ struct sw64_pci_init_ops { int (*map_irq)(const struct pci_dev *dev, u8 slot, u8 pin); unsigned long (*get_rc_enable)(unsigned long node); void (*hose_init)(struct pci_controller *hose); - void (*set_rc_piu)(unsigned long node, unsigned long index); - int (*check_pci_linkup)(unsigned long node, unsigned long index); - void (*set_intx)(unsigned long node, unsigned long index, - unsigned long int_conf); + void (*set_rc_piu)(struct pci_controller *hose); + int (*check_pci_linkup)(struct pci_controller *hose); }; diff --git a/arch/sw_64/include/asm/sw64io.h b/arch/sw_64/include/asm/sw64io.h index d52cd8cc86bf..e9c4a3be95ef 100644 --- a/arch/sw_64/include/asm/sw64io.h +++ b/arch/sw_64/include/asm/sw64io.h @@ -20,66 +20,6 @@ #define MK_PIU_IOR1(nid, idx) \ (SW64_PCI_IO_BASE((nid), (idx)) | PCI_IOR1_BASE) -static inline unsigned int -read_rc_conf(unsigned long node, unsigned long rc, - unsigned int offset) -{ - void __iomem *addr; - - addr = __va(MK_RC_CFG(node, rc) | offset); - return readl(addr); -} - -static inline void -write_rc_conf(unsigned long node, unsigned long rc, - unsigned int offset, unsigned int data) -{ - void __iomem *addr; - - addr = __va(MK_RC_CFG(node, rc) | offset); - writel(data, addr); -} - -static inline unsigned long -read_piu_ior0(unsigned long node, unsigned long rc, - unsigned int reg) -{ - void __iomem *addr; - - addr = __va(MK_PIU_IOR0(node, rc) + reg); - return readq(addr); -} - -static inline void -write_piu_ior0(unsigned long node, unsigned long rc, - unsigned int reg, unsigned long data) -{ - void __iomem *addr; - - addr = __va(MK_PIU_IOR0(node, rc) + reg); - writeq(data, addr); -} - -static inline unsigned long -read_piu_ior1(unsigned long node, unsigned long rc, - unsigned int reg) -{ - void __iomem *addr; - - addr = __va(MK_PIU_IOR1(node, rc) + reg); - return readq(addr); -} - -static inline void -write_piu_ior1(unsigned long node, unsigned long rc, - unsigned int reg, unsigned long data) -{ - void __iomem *addr; - - addr = __va(MK_PIU_IOR1(node, rc) + reg); - writeq(data, addr); -} - static inline unsigned long sw64_io_read(unsigned long node, unsigned long reg) { diff --git a/arch/sw_64/kernel/chip_setup.c b/arch/sw_64/kernel/chip_setup.c index 60373429a64e..c6374ae18138 100644 --- a/arch/sw_64/kernel/chip_setup.c +++ b/arch/sw_64/kernel/chip_setup.c @@ -78,38 +78,39 @@ static void pcie_save(void) { struct pci_controller *hose; struct piu_saved *piu_save; - unsigned long node, index; unsigned long i; + void __iomem *piu_ior0_base; + void __iomem *piu_ior1_base; for (hose = hose_head; hose; hose = hose->next) { - piu_save = kzalloc(sizeof(*piu_save), GFP_KERNEL); + piu_ior0_base = hose->piu_ior0_base; + piu_ior1_base = hose->piu_ior1_base; - node = hose->node; - index = hose->index; + piu_save = kzalloc(sizeof(*piu_save), GFP_KERNEL); hose->sysdata = piu_save; - piu_save->piuconfig0 = read_piu_ior0(node, index, PIUCONFIG0); - piu_save->piuconfig1 = read_piu_ior1(node, index, PIUCONFIG1); - piu_save->epdmabar = read_piu_ior0(node, index, EPDMABAR); - piu_save->msiaddr = read_piu_ior0(node, index, MSIADDR); + piu_save->piuconfig0 = readq(piu_ior0_base + PIUCONFIG0); + piu_save->piuconfig1 = readq(piu_ior1_base + PIUCONFIG1); + piu_save->epdmabar = readq(piu_ior0_base + EPDMABAR); + piu_save->msiaddr = readq(piu_ior1_base + MSIADDR); if (IS_ENABLED(CONFIG_UNCORE_XUELANG)) { for (i = 0; i < 256; i++) { - piu_save->msiconfig[i] = read_piu_ior0(node, index, - MSICONFIG0 + (i << 7)); + piu_save->msiconfig[i] = + readq(piu_ior0_base + MSICONFIG0 + (i << 7)); } } - piu_save->iommuexcpt_ctrl = read_piu_ior0(node, index, IOMMUEXCPT_CTRL); - piu_save->dtbaseaddr = read_piu_ior0(node, index, DTBASEADDR); + piu_save->iommuexcpt_ctrl = readq(piu_ior0_base + IOMMUEXCPT_CTRL); + piu_save->dtbaseaddr = readq(piu_ior0_base + DTBASEADDR); - piu_save->intaconfig = read_piu_ior0(node, index, INTACONFIG); - piu_save->intbconfig = read_piu_ior0(node, index, INTBCONFIG); - piu_save->intcconfig = read_piu_ior0(node, index, INTCCONFIG); - piu_save->intdconfig = read_piu_ior0(node, index, INTDCONFIG); - piu_save->pmeintconfig = read_piu_ior0(node, index, PMEINTCONFIG); - piu_save->aererrintconfig = read_piu_ior0(node, index, AERERRINTCONFIG); - piu_save->hpintconfig = read_piu_ior0(node, index, HPINTCONFIG); + piu_save->intaconfig = readq(piu_ior0_base + INTACONFIG); + piu_save->intbconfig = readq(piu_ior0_base + INTBCONFIG); + piu_save->intcconfig = readq(piu_ior0_base + INTCCONFIG); + piu_save->intdconfig = readq(piu_ior0_base + INTDCONFIG); + piu_save->pmeintconfig = readq(piu_ior0_base + PMEINTCONFIG); + piu_save->aererrintconfig = readq(piu_ior0_base + AERERRINTCONFIG); + piu_save->hpintconfig = readq(piu_ior0_base + HPINTCONFIG); } } @@ -118,53 +119,57 @@ static void pcie_restore(void) { struct pci_controller *hose; struct piu_saved *piu_save; - unsigned long node, index; u32 rc_misc_ctrl; unsigned int value; unsigned long i; + void __iomem *rc_config_space_base; + void __iomem *piu_ior0_base; + void __iomem *piu_ior1_base; for (hose = hose_head; hose; hose = hose->next) { - node = hose->node; - index = hose->index; + rc_config_space_base = hose->rc_config_space_base; + piu_ior0_base = hose->piu_ior0_base; + piu_ior1_base = hose->piu_ior1_base; piu_save = hose->sysdata; - write_piu_ior0(node, index, PIUCONFIG0, piu_save->piuconfig0); - write_piu_ior1(node, index, PIUCONFIG1, piu_save->piuconfig1); - write_piu_ior0(node, index, EPDMABAR, piu_save->epdmabar); - write_piu_ior0(node, index, MSIADDR, piu_save->msiaddr); + writeq(piu_save->piuconfig0, (piu_ior0_base + PIUCONFIG0)); + writeq(piu_save->piuconfig1, (piu_ior1_base + PIUCONFIG1)); + writeq(piu_save->epdmabar, (piu_ior0_base + EPDMABAR)); + writeq(piu_save->msiaddr, (piu_ior0_base + MSIADDR)); + if (IS_ENABLED(CONFIG_UNCORE_XUELANG)) { for (i = 0; i < 256; i++) { - write_piu_ior0(node, index, MSICONFIG0 + (i << 7), - piu_save->msiconfig[i]); + writeq(piu_save->msiconfig[i], + (piu_ior0_base + (MSICONFIG0 + (i << 7)))); } } - write_piu_ior0(node, index, IOMMUEXCPT_CTRL, piu_save->iommuexcpt_ctrl); - write_piu_ior0(node, index, DTBASEADDR, piu_save->dtbaseaddr); + writeq(piu_save->iommuexcpt_ctrl, (piu_ior0_base + IOMMUEXCPT_CTRL)); + writeq(piu_save->dtbaseaddr, (piu_ior0_base + DTBASEADDR)); - write_piu_ior0(node, index, INTACONFIG, piu_save->intaconfig); - write_piu_ior0(node, index, INTBCONFIG, piu_save->intbconfig); - write_piu_ior0(node, index, INTCCONFIG, piu_save->intcconfig); - write_piu_ior0(node, index, INTDCONFIG, piu_save->intdconfig); - write_piu_ior0(node, index, PMEINTCONFIG, piu_save->pmeintconfig); - write_piu_ior0(node, index, AERERRINTCONFIG, piu_save->aererrintconfig); - write_piu_ior0(node, index, HPINTCONFIG, piu_save->hpintconfig); + writeq(piu_save->intaconfig, (piu_ior0_base + INTACONFIG)); + writeq(piu_save->intbconfig, (piu_ior0_base + INTBCONFIG)); + writeq(piu_save->intcconfig, (piu_ior0_base + INTCCONFIG)); + writeq(piu_save->intdconfig, (piu_ior0_base + INTDCONFIG)); + writeq(piu_save->pmeintconfig, (piu_ior0_base + PMEINTCONFIG)); + writeq(piu_save->aererrintconfig, (piu_ior0_base + AERERRINTCONFIG)); + writeq(piu_save->hpintconfig, (piu_ior0_base + HPINTCONFIG)); /* Enable DBI_RO_WR_EN */ - rc_misc_ctrl = read_rc_conf(node, index, RC_MISC_CONTROL_1); - write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl | 0x1); + rc_misc_ctrl = readl(rc_config_space_base + RC_MISC_CONTROL_1); + writel((rc_misc_ctrl | 0x1), (rc_config_space_base + RC_MISC_CONTROL_1)); /* Fix up DEVICE_ID_VENDOR_ID register */ value = (PCI_DEVICE_ID_SW64_ROOT_BRIDGE << 16) | PCI_VENDOR_ID_JN; - write_rc_conf(node, index, RC_VENDOR_ID, value); + writel(value, (rc_config_space_base + RC_VENDOR_ID)); /* Set PCI-E root class code */ - value = read_rc_conf(node, index, RC_REVISION_ID); - write_rc_conf(node, index, RC_REVISION_ID, (PCI_CLASS_BRIDGE_HOST << 16) | value); + value = readl(rc_config_space_base + RC_REVISION_ID); + writel((PCI_CLASS_BRIDGE_HOST << 16) | value, (rc_config_space_base + RC_REVISION_ID)); /* Disable DBI_RO_WR_EN */ - write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl); + writel(rc_misc_ctrl, (rc_config_space_base + RC_MISC_CONTROL_1)); } } diff --git a/arch/sw_64/kernel/reset.c b/arch/sw_64/kernel/reset.c index 955339557a7a..3f1961ce85de 100644 --- a/arch/sw_64/kernel/reset.c +++ b/arch/sw_64/kernel/reset.c @@ -27,12 +27,10 @@ void fix_jm585_reset(void) 0x0585, NULL); if (pdev) { hose = pci_bus_to_pci_controller(pdev->bus); - val = read_rc_conf(hose->node, hose->index, - RC_PORT_LINK_CTL); - write_rc_conf(hose->node, hose->index, - RC_PORT_LINK_CTL, val | 0x8); - write_rc_conf(hose->node, hose->index, - RC_PORT_LINK_CTL, val); + val = readl(hose->rc_config_space_base + RC_PORT_LINK_CTL); + writel((val | 0x8), (hose->rc_config_space_base + RC_PORT_LINK_CTL)); + writel(val, (hose->rc_config_space_base + RC_PORT_LINK_CTL)); + } } diff --git a/arch/sw_64/pci/pci-legacy.c b/arch/sw_64/pci/pci-legacy.c index c968b3543dbe..52ba53a1fc0d 100644 --- a/arch/sw_64/pci/pci-legacy.c +++ b/arch/sw_64/pci/pci-legacy.c @@ -84,7 +84,7 @@ void __init common_init_pci(void) continue; hose->busn_space->start = last_bus; init_busnr = (0xff << 16) + ((last_bus + 1) << 8) + (last_bus); - write_rc_conf(hose->node, hose->index, RC_PRIMARY_BUS, init_busnr); + writel(init_busnr, (hose->rc_config_space_base + RC_PRIMARY_BUS)); offset = hose->mem_space->start - PCI_32BIT_MEMIO; if (is_in_host()) hose->first_busno = last_bus + 1; @@ -117,10 +117,10 @@ void __init common_init_pci(void) last_bus++; hose->last_busno = hose->busn_space->end = last_bus; - init_busnr = read_rc_conf(hose->node, hose->index, RC_PRIMARY_BUS); + init_busnr = readl(hose->rc_config_space_base + RC_PRIMARY_BUS); init_busnr &= ~(0xff << 16); init_busnr |= last_bus << 16; - write_rc_conf(hose->node, hose->index, RC_PRIMARY_BUS, init_busnr); + writel(init_busnr, (hose->rc_config_space_base + RC_PRIMARY_BUS)); pci_bus_update_busn_res_end(bus, last_bus); last_bus++; } @@ -228,9 +228,9 @@ sw64_init_host(unsigned long node, unsigned long index) sw64_chip_init->pci_init.hose_init(hose); if (sw64_chip_init->pci_init.set_rc_piu) - sw64_chip_init->pci_init.set_rc_piu(node, index); + sw64_chip_init->pci_init.set_rc_piu(hose); - ret = sw64_chip_init->pci_init.check_pci_linkup(node, index); + ret = sw64_chip_init->pci_init.check_pci_linkup(hose); if (ret == 0) { /* Root Complex downstream port is link up */ pci_mark_rc_linkup(node, index); // 8-bit per node diff --git a/arch/sw_64/pci/pci.c b/arch/sw_64/pci/pci.c index 77dbd2f4bf37..073781645f32 100644 --- a/arch/sw_64/pci/pci.c +++ b/arch/sw_64/pci/pci.c @@ -249,23 +249,22 @@ static void enable_sw_dca(struct pci_dev *dev) rc_index = hose->index; for (i = 0; i < 256; i++) { - dca_conf = read_piu_ior1(node, rc_index, DEVICEID0 + (i << 7)); + dca_conf = readq(hose->piu_ior1_base + DEVICEID0 + (i << 7)); if (dca_conf >> 63) continue; else { dca_conf = (1UL << 63) | (dev->bus->number << 8) | dev->devfn; pr_info("dca device index %d, dca_conf = %#lx\n", i, dca_conf); - write_piu_ior1(node, rc_index, - DEVICEID0 + (i << 7), dca_conf); + writeq(dca_conf, (hose->piu_ior1_base + DEVICEID0 + (i << 7))); break; } } - dca_ctl = read_piu_ior1(node, rc_index, DCACONTROL); + dca_ctl = readq(hose->piu_ior1_base + DCACONTROL); if (dca_ctl & 0x1) { dca_ctl = 0x2; - write_piu_ior1(node, rc_index, DCACONTROL, dca_ctl); + writeq(dca_ctl, (hose->piu_ior1_base + DCACONTROL)); pr_info("Node %ld RC %ld enable DCA 1.0\n", node, rc_index); } } @@ -342,7 +341,7 @@ void sw64_pci_root_bridge_prepare(struct pci_host_bridge *bridge) bridge->map_irq = sw64_pci_map_irq; init_busnr = (0xff << 16) + ((last_bus + 1) << 8) + (last_bus); - write_rc_conf(hose->node, hose->index, RC_PRIMARY_BUS, init_busnr); + writel(init_busnr, (hose->rc_config_space_base + RC_PRIMARY_BUS)); hose->first_busno = last_bus + (is_in_host() ? 1 : 0); @@ -400,10 +399,10 @@ void sw64_pci_root_bridge_scan_finish_up(struct pci_host_bridge *bridge) hose->last_busno = last_bus; hose->busn_space->end = last_bus; - init_busnr = read_rc_conf(hose->node, hose->index, RC_PRIMARY_BUS); + init_busnr = readl(hose->rc_config_space_base + RC_PRIMARY_BUS); init_busnr &= ~(0xff << 16); init_busnr |= last_bus << 16; - write_rc_conf(hose->node, hose->index, RC_PRIMARY_BUS, init_busnr); + writel(init_busnr, (hose->rc_config_space_base + RC_PRIMARY_BUS)); pci_bus_update_busn_res_end(bus, last_bus); last_bus++; diff --git a/drivers/irqchip/irq-sunway-cpu.c b/drivers/irqchip/irq-sunway-cpu.c index 250ddbd61cf4..23ad3b16af08 100644 --- a/drivers/irqchip/irq-sunway-cpu.c +++ b/drivers/irqchip/irq-sunway-cpu.c @@ -43,45 +43,49 @@ static void handle_intx(unsigned int offset) { struct pci_controller *hose; unsigned long value; + void __iomem *piu_ior0_base; hose = hose_head; + offset <<= 7; for (hose = hose_head; hose; hose = hose->next) { - value = read_piu_ior0(hose->node, hose->index, INTACONFIG + (offset << 7)); + piu_ior0_base = hose->piu_ior0_base; + + value = readq(piu_ior0_base + INTACONFIG + offset); if (value >> 63) { value = value & (~(1UL << 62)); - write_piu_ior0(hose->node, hose->index, INTACONFIG + (offset << 7), value); + writeq(value, (piu_ior0_base + INTACONFIG + offset)); handle_irq(hose->int_irq); value = value | (1UL << 62); - write_piu_ior0(hose->node, hose->index, INTACONFIG + (offset << 7), value); + writeq(value, (piu_ior0_base + INTACONFIG + offset)); } if (IS_ENABLED(CONFIG_PCIE_PME)) { - value = read_piu_ior0(hose->node, hose->index, PMEINTCONFIG); + value = readq(piu_ior0_base + PMEINTCONFIG); if (value >> 63) { handle_irq(hose->service_irq); - write_piu_ior0(hose->node, hose->index, PMEINTCONFIG, value); + writeq(value, (piu_ior0_base + PMEINTCONFIG)); } } if (IS_ENABLED(CONFIG_PCIEAER)) { - value = read_piu_ior0(hose->node, hose->index, AERERRINTCONFIG); + value = readq(piu_ior0_base + AERERRINTCONFIG); if (value >> 63) { handle_irq(hose->service_irq); - write_piu_ior0(hose->node, hose->index, AERERRINTCONFIG, value); + writeq(value, (piu_ior0_base + AERERRINTCONFIG)); } } if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE_SUNWAY)) { - value = read_piu_ior0(hose->node, hose->index, HPINTCONFIG); + value = readq(piu_ior0_base + HPINTCONFIG); if (value >> 63) { handle_irq(hose->service_irq); - write_piu_ior0(hose->node, hose->index, HPINTCONFIG, value); + writeq(value, (piu_ior0_base + HPINTCONFIG)); } } if (hose->iommu_enable) { - value = read_piu_ior0(hose->node, hose->index, IOMMUEXCPT_STATUS); + value = readq(piu_ior0_base + IOMMUEXCPT_STATUS); if (value >> 63) handle_irq(hose->int_irq); } diff --git a/drivers/irqchip/irq-sunway-msi.c b/drivers/irqchip/irq-sunway-msi.c index c231e504464b..6de689e5106f 100644 --- a/drivers/irqchip/irq-sunway-msi.c +++ b/drivers/irqchip/irq-sunway-msi.c @@ -93,8 +93,8 @@ static unsigned long set_piu_msi_config(struct pci_controller *hose, int cpu, phy_cpu = cpu_to_rcid(cpu); msi_config |= ((phy_cpu >> 5) << 6) | (phy_cpu & 0x1f); reg = MSICONFIG0 + (unsigned long)(msiconf_index << 7); - write_piu_ior0(hose->node, hose->index, reg, msi_config); - msi_config = read_piu_ior0(hose->node, hose->index, reg); + writeq(msi_config, (hose->piu_ior0_base + reg)); + msi_config = readq(hose->piu_ior0_base + reg); set_bit(msiconf_index, hose->piu_msiconfig); return msi_config; @@ -236,8 +236,7 @@ static int __assign_irq_vector(int virq, unsigned int nr_irqs, cdata->dst_cpu = cpu; cdata->vector = vector; - cdata->rc_index = hose->index; - cdata->rc_node = hose->node; + cdata->hose = hose; cdata->msi_config = msi_config; cdata->msi_config_index = msiconf_index; cdata->prev_cpu = cpu; @@ -463,7 +462,7 @@ void handle_pci_msi_interrupt(unsigned long type, unsigned long vector, unsigned irq_move_complete(cdata, cpu, vector_index + msi_index); piu_index = cdata->msi_config_index; value = cdata->msi_config | (1UL << 63); - write_piu_ior0(cdata->rc_node, cdata->rc_index, MSICONFIG0 + (piu_index << 7), value); + writeq(value, (cdata->hose->piu_ior0_base + MSICONFIG0 + (piu_index << 7))); spin_unlock(&cdata->cdata_lock); handle_irq(irq); diff --git a/drivers/irqchip/irq-sunway-pci-intx.c b/drivers/irqchip/irq-sunway-pci-intx.c index b2a563b0ef7e..434a0c1156de 100644 --- a/drivers/irqchip/irq-sunway-pci-intx.c +++ b/drivers/irqchip/irq-sunway-pci-intx.c @@ -19,33 +19,33 @@ static void unlock_legacy_lock(void) raw_spin_unlock(&legacy_lock); } -static void set_intx(unsigned long node, unsigned long index, unsigned long intx_conf) +static void set_intx(struct pci_controller *hose, unsigned long intx_conf) { + void __iomem *piu_ior0_base; + if (is_guest_or_emul()) return; - write_piu_ior0(node, index, INTACONFIG, intx_conf | (0x8UL << 10)); - write_piu_ior0(node, index, INTBCONFIG, intx_conf | (0x4UL << 10)); - write_piu_ior0(node, index, INTCCONFIG, intx_conf | (0x2UL << 10)); - write_piu_ior0(node, index, INTDCONFIG, intx_conf | (0x1UL << 10)); + piu_ior0_base = hose->piu_ior0_base; + + writeq(intx_conf | (0x8UL << 10), (piu_ior0_base + INTACONFIG)); + writeq(intx_conf | (0x4UL << 10), (piu_ior0_base + INTBCONFIG)); + writeq(intx_conf | (0x2UL << 10), (piu_ior0_base + INTCCONFIG)); + writeq(intx_conf | (0x1UL << 10), (piu_ior0_base + INTDCONFIG)); } static int __assign_piu_intx_config(struct pci_controller *hose, cpumask_t *targets) { unsigned long intx_conf; unsigned int cpu; - int node, index; int phy_cpu; - node = hose->node; - index = hose->index; - /* Use the last cpu in valid cpus to avoid core 0. */ cpu = cpumask_last(targets); phy_cpu = cpu_to_rcid(cpu); intx_conf = ((phy_cpu >> 5) << 6) | (phy_cpu & 0x1f); - set_intx(node, index, intx_conf); + set_intx(hose, intx_conf); return 0; } @@ -64,59 +64,59 @@ static int assign_piu_intx_config(struct pci_controller *hose, cpumask_t *target static void intx_irq_enable(struct irq_data *irq_data) { struct pci_controller *hose = irq_data->chip_data; - unsigned long intx_conf, node, index; + unsigned long intx_conf; + void __iomem *piu_ior0_base; if (is_guest_or_emul()) return; BUG_ON(!hose); - node = hose->node; - index = hose->index; + piu_ior0_base = hose->piu_ior0_base; - intx_conf = read_piu_ior0(node, index, INTACONFIG); + intx_conf = readq(piu_ior0_base + INTACONFIG); intx_conf |= PCI_INTX_ENABLE; - write_piu_ior0(node, index, INTACONFIG, intx_conf); + writeq(intx_conf, (piu_ior0_base + INTACONFIG)); - intx_conf = read_piu_ior0(node, index, INTBCONFIG); + intx_conf = readq(piu_ior0_base + INTBCONFIG); intx_conf |= PCI_INTX_ENABLE; - write_piu_ior0(node, index, INTBCONFIG, intx_conf); + writeq(intx_conf, (piu_ior0_base + INTBCONFIG)); - intx_conf = read_piu_ior0(node, index, INTCCONFIG); + intx_conf = readq(piu_ior0_base + INTCCONFIG); intx_conf |= PCI_INTX_ENABLE; - write_piu_ior0(node, index, INTCCONFIG, intx_conf); + writeq(intx_conf, (piu_ior0_base + INTCCONFIG)); - intx_conf = read_piu_ior0(node, index, INTDCONFIG); + intx_conf = readq(piu_ior0_base + INTDCONFIG); intx_conf |= PCI_INTX_ENABLE; - write_piu_ior0(node, index, INTDCONFIG, intx_conf); + writeq(intx_conf, (piu_ior0_base + INTDCONFIG)); } static void intx_irq_disable(struct irq_data *irq_data) { struct pci_controller *hose = irq_data->chip_data; - unsigned long intx_conf, node, index; + unsigned long intx_conf; + void __iomem *piu_ior0_base; if (is_guest_or_emul()) return; BUG_ON(!hose); - node = hose->node; - index = hose->index; + piu_ior0_base = hose->piu_ior0_base; - intx_conf = read_piu_ior0(node, index, INTACONFIG); + intx_conf = readq(piu_ior0_base + INTACONFIG); intx_conf &= PCI_INTX_DISABLE; - write_piu_ior0(node, index, INTACONFIG, intx_conf); + writeq(intx_conf, (piu_ior0_base + INTACONFIG)); - intx_conf = read_piu_ior0(node, index, INTBCONFIG); + intx_conf = readq(piu_ior0_base + INTBCONFIG); intx_conf &= PCI_INTX_DISABLE; - write_piu_ior0(node, index, INTBCONFIG, intx_conf); + writeq(intx_conf, (piu_ior0_base + INTBCONFIG)); - intx_conf = read_piu_ior0(node, index, INTCCONFIG); + intx_conf = readq(piu_ior0_base + INTCCONFIG); intx_conf &= PCI_INTX_DISABLE; - write_piu_ior0(node, index, INTCCONFIG, intx_conf); + writeq(intx_conf, (piu_ior0_base + INTCCONFIG)); - intx_conf = read_piu_ior0(node, index, INTDCONFIG); + intx_conf = readq(piu_ior0_base + INTDCONFIG); intx_conf &= PCI_INTX_DISABLE; - write_piu_ior0(node, index, INTDCONFIG, intx_conf); + writeq(intx_conf, (piu_ior0_base + INTDCONFIG)); } static int intx_set_affinity(struct irq_data *irq_data, @@ -149,14 +149,13 @@ static struct irq_chip sw64_intx_chip = { .flags = IRQCHIP_SKIP_SET_WAKE, }; -void __weak set_pcieport_service_irq(int node, int index) {} +void __weak set_pcieport_service_irq(struct pci_controller *hose) {} void setup_intx_irqs(struct pci_controller *hose) { - unsigned long irq, node, index, val_node; + unsigned long irq, node, val_node; node = hose->node; - index = hose->index; if (!node_online(node)) val_node = next_node_in(node, node_online_map); @@ -172,7 +171,7 @@ void setup_intx_irqs(struct pci_controller *hose) irq_set_chip_and_handler(irq + 1, &dummy_irq_chip, handle_level_irq); hose->service_irq = irq + 1; - set_pcieport_service_irq(node, index); + set_pcieport_service_irq(hose); } void __init sw64_init_irq(void) diff --git a/drivers/pci/controller/pci-sunway.c b/drivers/pci/controller/pci-sunway.c index 688793d1b136..6af0020ce1ee 100644 --- a/drivers/pci/controller/pci-sunway.c +++ b/drivers/pci/controller/pci-sunway.c @@ -24,20 +24,17 @@ void set_adr_int(int node) } #endif -void set_pcieport_service_irq(int node, int index) +void set_pcieport_service_irq(struct pci_controller *hose) { if (IS_ENABLED(CONFIG_PCIE_PME)) - write_piu_ior0(node, index, - PMEINTCONFIG, PME_ENABLE_INTD_CORE0); + writeq(PME_ENABLE_INTD_CORE0, (hose->piu_ior0_base + PMEINTCONFIG)); if (IS_ENABLED(CONFIG_PCIEAER)) - write_piu_ior0(node, index, - AERERRINTCONFIG, AER_ENABLE_INTD_CORE0); + writeq(AER_ENABLE_INTD_CORE0, (hose->piu_ior0_base + AERERRINTCONFIG)); #ifdef CONFIG_UNCORE_JUNZHANG if (IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE_SUNWAY)) - write_piu_ior0(node, index, - HPINTCONFIG, HP_ENABLE_INTD_CORE0); + writeq(HP_ENABLE_INTD_CORE0, (hose->piu_ior0_base + HPINTCONFIG)); #endif } @@ -59,17 +56,16 @@ int chip_pcie_configure(struct pci_controller *hose) struct list_head *next; unsigned int max_read_size, smallest_max_payload; int max_payloadsize; - unsigned long rc_index, node; unsigned long piuconfig0, value; unsigned int pcie_caps_offset; unsigned int rc_conf_value; u16 devctl, new_values; bool rc_ari_disabled = false, found = false; unsigned char bus_max_num; + void __iomem *rc_config_space_base; - node = hose->node; - rc_index = hose->index; - smallest_max_payload = read_rc_conf(node, rc_index, RC_EXP_DEVCAP); + rc_config_space_base = hose->rc_config_space_base; + smallest_max_payload = readl(rc_config_space_base + RC_EXP_DEVCAP); smallest_max_payload &= PCI_EXP_DEVCAP_PAYLOAD; bus_max_num = hose->busn_space->start; @@ -120,40 +116,40 @@ int chip_pcie_configure(struct pci_controller *hose) } if (rc_ari_disabled) { - rc_conf_value = read_rc_conf(node, rc_index, RC_EXP_DEVCTL2); + rc_conf_value = readl(rc_config_space_base + RC_EXP_DEVCTL2); rc_conf_value &= ~PCI_EXP_DEVCTL2_ARI; - write_rc_conf(node, rc_index, RC_EXP_DEVCTL2, rc_conf_value); + writel(rc_conf_value, (rc_config_space_base + RC_EXP_DEVCTL2)); } else { - rc_conf_value = read_rc_conf(node, rc_index, RC_EXP_DEVCTL2); + rc_conf_value = readl(rc_config_space_base + RC_EXP_DEVCTL2); rc_conf_value |= PCI_EXP_DEVCTL2_ARI; - write_rc_conf(node, rc_index, RC_EXP_DEVCTL2, rc_conf_value); + writel(rc_conf_value, (rc_config_space_base + RC_EXP_DEVCTL2)); } - rc_conf_value = read_rc_conf(node, rc_index, RC_EXP_DEVCAP); + rc_conf_value = readl(rc_config_space_base + RC_EXP_DEVCAP); rc_conf_value &= PCI_EXP_DEVCAP_PAYLOAD; max_payloadsize = rc_conf_value; if (max_payloadsize < smallest_max_payload) smallest_max_payload = max_payloadsize; max_read_size = 0x2; /* Limit to 512B */ - value = read_rc_conf(node, rc_index, RC_EXP_DEVCTL); + value = readl(rc_config_space_base + RC_EXP_DEVCTL); value &= ~(PCI_EXP_DEVCTL_PAYLOAD | PCI_EXP_DEVCTL_READRQ); value |= (max_read_size << 12) | (smallest_max_payload << 5); - write_rc_conf(node, rc_index, RC_EXP_DEVCTL, value); + writel(value, (rc_config_space_base + RC_EXP_DEVCTL)); new_values = (max_read_size << 12) | (smallest_max_payload << 5); - piuconfig0 = read_piu_ior0(node, rc_index, PIUCONFIG0); + piuconfig0 = readq(hose->piu_ior0_base + PIUCONFIG0); piuconfig0 &= ~(0x7fUL << 9); if (smallest_max_payload == 0x2) { piuconfig0 |= (0x20UL << 9); - write_piu_ior0(node, rc_index, PIUCONFIG0, piuconfig0); + writeq(piuconfig0, (hose->piu_ior0_base + PIUCONFIG0)); } else { piuconfig0 |= (0x40UL << 9); - write_piu_ior0(node, rc_index, PIUCONFIG0, piuconfig0); + writeq(piuconfig0, (hose->piu_ior0_base + PIUCONFIG0)); } pr_info("Node%ld RC%ld MPSS %luB, MRRS %luB, Piuconfig0 %#lx, ARI %s\n", - node, rc_index, (1UL << smallest_max_payload) << 7, + hose->node, hose->index, (1UL << smallest_max_payload) << 7, (1UL << max_read_size) << 7, piuconfig0, rc_ari_disabled ? "disabled" : "enabled"); @@ -192,69 +188,74 @@ int chip_pcie_configure(struct pci_controller *hose) return bus_max_num; } -static int check_pci_linkup(unsigned long node, unsigned long index) +static int check_pci_linkup(struct pci_controller *hose) { unsigned long rc_debug; if (is_guest_or_emul()) { - if (node == 0 && index == 0) + if (hose->node == 0 && hose->index == 0) return 0; else return 1; - } else { - rc_debug = read_piu_ior1(node, index, RCDEBUGINF1); } - return !(rc_debug == 0x111); + rc_debug = readq(hose->piu_ior1_base + RCDEBUGINF1); + + return !((rc_debug & 0x3fful) == 0x111); } -static void set_rc_piu(unsigned long node, unsigned long index) +static void set_rc_piu(struct pci_controller *hose) { unsigned int i __maybe_unused; unsigned int value; u32 rc_misc_ctrl; + void __iomem *rc_config_space_base; + void __iomem *piu_ior0_base; + void __iomem *piu_ior1_base; if (is_guest_or_emul()) return; + rc_config_space_base = hose->rc_config_space_base; + piu_ior0_base = hose->piu_ior0_base; + piu_ior1_base = hose->piu_ior1_base; + /* configure RC, set PCI-E root controller */ - write_rc_conf(node, index, RC_COMMAND, 0x00100007); - write_rc_conf(node, index, RC_PORT_LINK_CTL, 0x1f0020); - write_rc_conf(node, index, RC_EXP_DEVCTL, 0x2850); - write_rc_conf(node, index, RC_EXP_DEVCTL2, 0x6); + writel(0x00100007, (rc_config_space_base + RC_COMMAND)); + writel(0x1f0020, (rc_config_space_base + RC_PORT_LINK_CTL)); + writel(0x2850, (rc_config_space_base + RC_EXP_DEVCTL)); + writel(0x6, (rc_config_space_base + RC_EXP_DEVCTL2)); #ifdef CONFIG_UNCORE_XUELANG - write_rc_conf(node, index, RC_ORDER_RULE_CTL, 0x0100); + writel(0x0100, (rc_config_space_base + RC_ORDER_RULE_CTL)); #endif /* enable DBI_RO_WR_EN */ - rc_misc_ctrl = read_rc_conf(node, index, RC_MISC_CONTROL_1); - write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl | 0x1); + rc_misc_ctrl = readl(rc_config_space_base + RC_MISC_CONTROL_1); + writel(rc_misc_ctrl | 0x1, (rc_config_space_base + RC_MISC_CONTROL_1)); /* fix up DEVICE_ID_VENDOR_ID register */ value = (PCI_DEVICE_ID_SW64_ROOT_BRIDGE << 16) | PCI_VENDOR_ID_JN; - write_rc_conf(node, index, RC_VENDOR_ID, value); + writel(value, (rc_config_space_base + RC_VENDOR_ID)); /* set PCI-E root class code */ - value = read_rc_conf(node, index, RC_REVISION_ID); - write_rc_conf(node, index, RC_REVISION_ID, - (PCI_CLASS_BRIDGE_HOST << 16) | value); + value = readl(rc_config_space_base + RC_REVISION_ID); + writel((PCI_CLASS_BRIDGE_HOST << 16) | value, (rc_config_space_base + RC_REVISION_ID)); /* disable DBI_RO_WR_EN */ - write_rc_conf(node, index, RC_MISC_CONTROL_1, rc_misc_ctrl); + writel(rc_misc_ctrl, (rc_config_space_base + RC_MISC_CONTROL_1)); - write_rc_conf(node, index, RC_PRIMARY_BUS, 0xffffff); - write_piu_ior0(node, index, PIUCONFIG0, PIUCONFIG0_INIT_VAL); + writeq(PIUCONFIG0_INIT_VAL, (piu_ior0_base + PIUCONFIG0)); - write_piu_ior1(node, index, PIUCONFIG1, 0x2); - write_piu_ior1(node, index, ERRENABLE, -1); + writeq(0x2, (piu_ior1_base + PIUCONFIG1)); + writeq(-1, (piu_ior1_base + ERRENABLE)); /* set DMA offset value PCITODMA_OFFSET */ - write_piu_ior0(node, index, EPDMABAR, PCITODMA_OFFSET); + writeq(PCITODMA_OFFSET, (piu_ior0_base + EPDMABAR)); if (IS_ENABLED(CONFIG_PCI_MSI)) { - write_piu_ior0(node, index, MSIADDR, MSIX_MSG_ADDR); + writeq(MSIX_MSG_ADDR, (piu_ior0_base + MSIADDR)); #ifdef CONFIG_UNCORE_XUELANG for (i = 0; i < 256; i++) - write_piu_ior0(node, index, MSICONFIG0 + (i << 7), 0); + writeq(0, (piu_ior0_base + MSICONFIG0 + (i << 7))); #endif } } @@ -294,6 +295,8 @@ static void hose_init(struct pci_controller *hose) hose->dense_io_base = pci_io_base | PCI_LEGACY_IO; hose->ep_config_space_base = __va(pci_io_base | PCI_EP_CFG); hose->rc_config_space_base = __va(pci_io_base | PCI_RC_CFG); + hose->piu_ior0_base = __va(MK_PIU_IOR0(hose->node, hose->index)); + hose->piu_ior1_base = __va(MK_PIU_IOR1(hose->node, hose->index)); hose->mem_space->start = pci_io_base + PCI_32BIT_MEMIO; hose->mem_space->end = hose->mem_space->start + PCI_32BIT_MEMIO_SIZE - 1; @@ -706,8 +709,8 @@ static int sw64_pci_prepare_controller(struct pci_controller *hose, * 1. Root Complex enable * 2. Root Complex link up */ - set_rc_piu(hose->node, hose->index); - if (check_pci_linkup(hose->node, hose->index)) { + set_rc_piu(hose); + if (check_pci_linkup(hose)) { /** * Root Complex link up failed. * This usually means that no device on the slot. diff --git a/drivers/pci/hotplug/sunway_pciehp_ctrl.c b/drivers/pci/hotplug/sunway_pciehp_ctrl.c index c85ba8e684cc..52e79bfe7dd6 100644 --- a/drivers/pci/hotplug/sunway_pciehp_ctrl.c +++ b/drivers/pci/hotplug/sunway_pciehp_ctrl.c @@ -233,26 +233,25 @@ void sunway_pciehp_save_rc_piu(struct controller *ctrl) { struct pci_bus *bus = ctrl->pci_dev->bus; struct pci_controller *hose = pci_bus_to_pci_controller(bus); - unsigned long node, rc_index; + void __iomem *piu_ior0_base; - node = hose->node; - rc_index = hose->index; + piu_ior0_base = hose->piu_ior0_base; sunway_pciehp_save_config_space(ctrl); if (!ctrl->saved_piu.state_saved) { - ctrl->saved_piu.epdmabar = read_piu_ior0(node, rc_index, EPDMABAR); - ctrl->saved_piu.msiaddr = read_piu_ior0(node, rc_index, MSIADDR); - ctrl->saved_piu.iommuexcpt_ctrl = read_piu_ior0(node, rc_index, IOMMUEXCPT_CTRL); - ctrl->saved_piu.dtbaseaddr = read_piu_ior0(node, rc_index, DTBASEADDR); - - ctrl->saved_piu.intaconfig = read_piu_ior0(node, rc_index, INTACONFIG); - ctrl->saved_piu.intbconfig = read_piu_ior0(node, rc_index, INTBCONFIG); - ctrl->saved_piu.intcconfig = read_piu_ior0(node, rc_index, INTCCONFIG); - ctrl->saved_piu.intdconfig = read_piu_ior0(node, rc_index, INTDCONFIG); - ctrl->saved_piu.pmeintconfig = read_piu_ior0(node, rc_index, PMEINTCONFIG); - ctrl->saved_piu.aererrintconfig = read_piu_ior0(node, rc_index, AERERRINTCONFIG); - ctrl->saved_piu.hpintconfig = read_piu_ior0(node, rc_index, HPINTCONFIG); + ctrl->saved_piu.epdmabar = readq(piu_ior0_base + EPDMABAR); + ctrl->saved_piu.msiaddr = readq(piu_ior0_base + MSIADDR); + ctrl->saved_piu.iommuexcpt_ctrl = readq(piu_ior0_base + IOMMUEXCPT_CTRL); + ctrl->saved_piu.dtbaseaddr = readq(piu_ior0_base + DTBASEADDR); + + ctrl->saved_piu.intaconfig = readq(piu_ior0_base + INTACONFIG); + ctrl->saved_piu.intbconfig = readq(piu_ior0_base + INTBCONFIG); + ctrl->saved_piu.intcconfig = readq(piu_ior0_base + INTCCONFIG); + ctrl->saved_piu.intdconfig = readq(piu_ior0_base + INTDCONFIG); + ctrl->saved_piu.pmeintconfig = readq(piu_ior0_base + PMEINTCONFIG); + ctrl->saved_piu.aererrintconfig = readq(piu_ior0_base + AERERRINTCONFIG); + ctrl->saved_piu.hpintconfig = readq(piu_ior0_base + HPINTCONFIG); ctrl->saved_piu.state_saved = true; } @@ -265,12 +264,13 @@ void sunway_pciehp_start(struct hotplug_slot *hotplug_slot) struct pci_bus *bus = pdev->bus; struct pci_controller *hose = pci_bus_to_pci_controller(bus); unsigned long piu_value; - unsigned long node, rc_index; bool hardware_auto = true; u16 slot_ctrl; + void __iomem *piu_ior0_base; + void __iomem *piu_ior1_base; - node = hose->node; - rc_index = hose->index; + piu_ior0_base = hose->piu_ior0_base; + piu_ior1_base = hose->piu_ior1_base; switch (ctrl->state) { case OFF_STATE: @@ -308,7 +308,7 @@ void sunway_pciehp_start(struct hotplug_slot *hotplug_slot) sunway_pciehp_set_indicators(ctrl, INDICATOR_NOOP, PCI_EXP_SLTCTL_ATTN_IND_BLINK); - write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_INSERT); + writeq(HP_CTRL_INSERT, (piu_ior0_base + HP_CTRL)); } break; case ON_STATE: @@ -338,7 +338,7 @@ void sunway_pciehp_start(struct hotplug_slot *hotplug_slot) sunway_pciehp_link_disable(ctrl); while (1) { - piu_value = read_piu_ior1(node, rc_index, NEWLTSSMSTATE0); + piu_value = readq(piu_ior1_base + NEWLTSSMSTATE0); piu_value &= 0xff; if (piu_value == 0x19) @@ -347,7 +347,7 @@ void sunway_pciehp_start(struct hotplug_slot *hotplug_slot) udelay(10); } - write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_REMOVE); + writeq(HP_CTRL_REMOVE, (piu_ior0_base + HP_CTRL)); sunway_pciehp_request(ctrl, DISABLE_SLOT); } @@ -384,26 +384,25 @@ void sunway_pciehp_restore_rc_piu(struct controller *ctrl) { struct pci_bus *bus = ctrl->pci_dev->bus; struct pci_controller *hose = pci_bus_to_pci_controller(bus); - unsigned long node, rc_index; + void __iomem *piu_ior0_base; - node = hose->node; - rc_index = hose->index; + piu_ior0_base = hose->piu_ior0_base; sunway_pciehp_restore_config_space(ctrl); if (ctrl->saved_piu.state_saved) { - write_piu_ior0(node, rc_index, EPDMABAR, ctrl->saved_piu.epdmabar); - write_piu_ior0(node, rc_index, MSIADDR, ctrl->saved_piu.msiaddr); - write_piu_ior0(node, rc_index, IOMMUEXCPT_CTRL, ctrl->saved_piu.iommuexcpt_ctrl); - write_piu_ior0(node, rc_index, DTBASEADDR, ctrl->saved_piu.dtbaseaddr); - - write_piu_ior0(node, rc_index, INTACONFIG, ctrl->saved_piu.intaconfig); - write_piu_ior0(node, rc_index, INTBCONFIG, ctrl->saved_piu.intbconfig); - write_piu_ior0(node, rc_index, INTCCONFIG, ctrl->saved_piu.intcconfig); - write_piu_ior0(node, rc_index, INTDCONFIG, ctrl->saved_piu.intdconfig); - write_piu_ior0(node, rc_index, PMEINTCONFIG, ctrl->saved_piu.pmeintconfig); - write_piu_ior0(node, rc_index, AERERRINTCONFIG, ctrl->saved_piu.aererrintconfig); - write_piu_ior0(node, rc_index, HPINTCONFIG, ctrl->saved_piu.hpintconfig); + writeq(ctrl->saved_piu.epdmabar, (piu_ior0_base + EPDMABAR)); + writeq(ctrl->saved_piu.msiaddr, (piu_ior0_base + MSIADDR)); + writeq(ctrl->saved_piu.iommuexcpt_ctrl, (piu_ior0_base + IOMMUEXCPT_CTRL)); + writeq(ctrl->saved_piu.dtbaseaddr, (piu_ior0_base + DTBASEADDR)); + + writeq(ctrl->saved_piu.intaconfig, (piu_ior0_base + INTACONFIG)); + writeq(ctrl->saved_piu.intbconfig, (piu_ior0_base + INTBCONFIG)); + writeq(ctrl->saved_piu.intcconfig, (piu_ior0_base + INTCCONFIG)); + writeq(ctrl->saved_piu.intdconfig, (piu_ior0_base + INTDCONFIG)); + writeq(ctrl->saved_piu.pmeintconfig, (piu_ior0_base + PMEINTCONFIG)); + writeq(ctrl->saved_piu.aererrintconfig, (piu_ior0_base + AERERRINTCONFIG)); + writeq(ctrl->saved_piu.hpintconfig, (piu_ior0_base + HPINTCONFIG)); ctrl->saved_piu.state_saved = false; } @@ -415,14 +414,15 @@ void sunway_pciehp_end(struct controller *ctrl, bool insert) struct pci_bus *bus = pdev->bus; struct pci_controller *hose = pci_bus_to_pci_controller(bus); unsigned long piu_value; - unsigned long node, rc_index; u16 slot_ctrl; + void __iomem *piu_ior0_base; + void __iomem *piu_ior1_base; - node = hose->node; - rc_index = hose->index; + piu_ior0_base = hose->piu_ior0_base; + piu_ior1_base = hose->piu_ior1_base; if (insert) { - write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_FINISH); + writeq(HP_CTRL_FINISH, (piu_ior0_base + HP_CTRL)); } else { sunway_pciehp_set_indicators(ctrl, INDICATOR_NOOP, PCI_EXP_SLTCTL_ATTN_IND_OFF); @@ -431,7 +431,7 @@ void sunway_pciehp_end(struct controller *ctrl, bool insert) mdelay(100); while (1) { - piu_value = read_piu_ior1(node, rc_index, NEWLTSSMSTATE0); + piu_value = readq(piu_ior1_base + NEWLTSSMSTATE0); piu_value &= 0xff; if (piu_value == 0x0) @@ -440,7 +440,7 @@ void sunway_pciehp_end(struct controller *ctrl, bool insert) udelay(10); } - write_piu_ior0(node, rc_index, HPINTCONFIG, HP_ENABLE_INTD_CORE0); + writeq(HP_ENABLE_INTD_CORE0, (piu_ior0_base + HPINTCONFIG)); pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); slot_ctrl |= (PCI_EXP_SLTCTL_PFDE | @@ -451,7 +451,7 @@ void sunway_pciehp_end(struct controller *ctrl, bool insert) PCI_EXP_SLTCTL_DLLSCE); pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); - write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_FINISH); + writeq(HP_CTRL_FINISH, (piu_ior0_base + HP_CTRL)); } } diff --git a/drivers/pci/hotplug/sunway_pciehp_hpc.c b/drivers/pci/hotplug/sunway_pciehp_hpc.c index d30ee235b029..bd4556dbc3d4 100644 --- a/drivers/pci/hotplug/sunway_pciehp_hpc.c +++ b/drivers/pci/hotplug/sunway_pciehp_hpc.c @@ -304,9 +304,9 @@ static bool pci_bus_check_linkup(struct pci_controller *hose) bool linkup = false; do { - if (sw64_chip_init->pci_init.check_pci_linkup(hose->node, hose->index) == 0) { + if (sw64_chip_init->pci_init.check_pci_linkup(hose) == 0) { udelay(10); - if (sw64_chip_init->pci_init.check_pci_linkup(hose->node, hose->index) == 0) + if (sw64_chip_init->pci_init.check_pci_linkup(hose) == 0) linkup = true; } count++; @@ -634,16 +634,17 @@ void sunway_pciehp_poll(struct controller *ctrl, u32 events) struct pci_bus *bus = pdev->bus; struct pci_controller *hose = pci_bus_to_pci_controller(bus); unsigned long piu_value; - unsigned long node, rc_index; u16 slot_ctrl; int i; + void __iomem *piu_ior0_base; + void __iomem *piu_ior1_base; - node = hose->node; - rc_index = hose->index; + piu_ior0_base = hose->piu_ior0_base; + piu_ior1_base = hose->piu_ior1_base; if (events & SW64_POLL_DISABLE_SLOT) { while (1) { - piu_value = read_piu_ior1(node, rc_index, NEWLTSSMSTATE0); + piu_value = readq(piu_ior1_base + NEWLTSSMSTATE0); piu_value &= 0xff; if (piu_value == 0x19) @@ -652,10 +653,10 @@ void sunway_pciehp_poll(struct controller *ctrl, u32 events) udelay(10); } - write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_REMOVE); + writeq(HP_CTRL_REMOVE, (piu_ior0_base + HP_CTRL)); while (1) { - piu_value = read_piu_ior0(node, rc_index, HP_WATCHOUT); + piu_value = readq(piu_ior0_base + HP_WATCHOUT); piu_value >>= 24; if (piu_value == 0x19) @@ -668,7 +669,7 @@ void sunway_pciehp_poll(struct controller *ctrl, u32 events) mdelay(100); while (1) { - piu_value = read_piu_ior1(node, rc_index, NEWLTSSMSTATE0); + piu_value = readq(piu_ior1_base + NEWLTSSMSTATE0); piu_value &= 0xff; if (piu_value == 0x0) @@ -689,20 +690,20 @@ void sunway_pciehp_poll(struct controller *ctrl, u32 events) PCI_EXP_SLTCTL_DLLSCE); pcie_capability_write_word(pdev, PCI_EXP_SLTCTL, slot_ctrl); - write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_FINISH); + writeq(HP_CTRL_FINISH, (piu_ior0_base + HP_CTRL)); ctrl->state = OFF_STATE; } if (events & SW64_POLL_ENABLE_SLOT) { - write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_INSERT); + writeq(HP_CTRL_INSERT, (piu_ior0_base + HP_CTRL)); for (i = 0; i < 30; i++) { if (pcie_wait_for_link(pdev, true)) { pci_mark_rc_linkup(hose->node, hose->index); sunway_pciehp_restore_rc_piu(ctrl); - write_piu_ior0(node, rc_index, HP_CTRL, HP_CTRL_FINISH); + writeq(HP_CTRL_FINISH, (piu_ior0_base + HP_CTRL)); pcie_capability_read_word(pdev, PCI_EXP_SLTCTL, &slot_ctrl); slot_ctrl &= ~PCI_EXP_SLTCTL_PWR_OFF; -- Gitee From a132048279101919f6bae09d7bd8149fdb135c0b Mon Sep 17 00:00:00 2001 From: Gao Chen Date: Fri, 31 May 2024 17:54:14 +0800 Subject: [PATCH 15/25] anolis: sw64: fix a bug in gpio-sunway.c ANBZ: #4688 Change spin_unlock_irqrestore() and spin_lock_irqsave() into raw_spin_unlock_irqrestore() and raw_spin_lock_irqsave(). Signed-off-by: Gao Chen Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- drivers/gpio/gpio-sunway.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-sunway.c b/drivers/gpio/gpio-sunway.c index 0fd113ff6b11..8532e31edbec 100644 --- a/drivers/gpio/gpio-sunway.c +++ b/drivers/gpio/gpio-sunway.c @@ -231,11 +231,11 @@ static void sunway_irq_enable(struct irq_data *d) unsigned long flags; u32 val; - spin_lock_irqsave(&gc->bgpio_lock, flags); + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); val = sunway_read(gpio, GPIO_INTEN); val |= BIT(d->hwirq); sunway_write(gpio, GPIO_INTEN, val); - spin_unlock_irqrestore(&gc->bgpio_lock, flags); + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); } static void sunway_irq_disable(struct irq_data *d) @@ -246,11 +246,11 @@ static void sunway_irq_disable(struct irq_data *d) unsigned long flags; u32 val; - spin_lock_irqsave(&gc->bgpio_lock, flags); + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); val = sunway_read(gpio, GPIO_INTEN); val &= ~BIT(d->hwirq); sunway_write(gpio, GPIO_INTEN, val); - spin_unlock_irqrestore(&gc->bgpio_lock, flags); + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); } static int sunway_irq_reqres(struct irq_data *d) @@ -290,7 +290,7 @@ static int sunway_irq_set_type(struct irq_data *d, u32 type) IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) return -EINVAL; - spin_lock_irqsave(&gc->bgpio_lock, flags); + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); level = sunway_read(gpio, GPIO_INTTYPE_LEVEL); polarity = sunway_read(gpio, GPIO_INT_POLARITY); @@ -322,7 +322,7 @@ static int sunway_irq_set_type(struct irq_data *d, u32 type) sunway_write(gpio, GPIO_INTTYPE_LEVEL, level); if (type != IRQ_TYPE_EDGE_BOTH) sunway_write(gpio, GPIO_INT_POLARITY, polarity); - spin_unlock_irqrestore(&gc->bgpio_lock, flags); + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); return 0; } @@ -351,7 +351,7 @@ static int sunway_gpio_set_debounce(struct gpio_chip *gc, unsigned long flags, val_deb; unsigned long mask = BIT(offset); - spin_lock_irqsave(&gc->bgpio_lock, flags); + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); val_deb = sunway_read(gpio, GPIO_PORTA_DEBOUNCE); if (debounce) @@ -359,7 +359,7 @@ static int sunway_gpio_set_debounce(struct gpio_chip *gc, else sunway_write(gpio, GPIO_PORTA_DEBOUNCE, val_deb & ~mask); - spin_unlock_irqrestore(&gc->bgpio_lock, flags); + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); return 0; } @@ -763,7 +763,7 @@ static int sunway_gpio_suspend(struct device *dev) unsigned long flags; int i; - spin_lock_irqsave(&gc->bgpio_lock, flags); + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); for (i = 0; i < gpio->nr_ports; i++) { unsigned int offset; unsigned int idx = gpio->ports[i].idx; @@ -793,7 +793,7 @@ static int sunway_gpio_suspend(struct device *dev) 0xffffffff & ~ctx->wake_en); } } - spin_unlock_irqrestore(&gc->bgpio_lock, flags); + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); clk_disable_unprepare(gpio->clk); @@ -811,7 +811,7 @@ static int sunway_gpio_resume(struct device *dev) if (!IS_ERR(gpio->clk)) clk_prepare_enable(gpio->clk); - spin_lock_irqsave(&gc->bgpio_lock, flags); + raw_spin_lock_irqsave(&gc->bgpio_lock, flags); for (i = 0; i < gpio->nr_ports; i++) { unsigned int offset; unsigned int idx = gpio->ports[i].idx; @@ -840,7 +840,7 @@ static int sunway_gpio_resume(struct device *dev) sunway_write(gpio, GPIO_PORTA_EOI, 0xffffffff); } } - spin_unlock_irqrestore(&gc->bgpio_lock, flags); + raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags); return 0; } -- Gitee From 2f823caa07c98c7ae4cfa82eb4af4205f9c72f6c Mon Sep 17 00:00:00 2001 From: Jing Li Date: Thu, 16 May 2024 08:51:42 +0800 Subject: [PATCH 16/25] =?UTF-8?q?anolis:=20sw64:=20kconfig:=20set=20defaul?= =?UTF-8?q?t=20y=20for=20GPIO=20if=C2=A0ACPI=C2=A0enabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ANBZ: #4688 Set the driver of GPIO to default y, so that the GPIO is always available, which is used to signal ACPI events. Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/Kconfig | 1 + drivers/gpio/Kconfig | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/sw_64/Kconfig b/arch/sw_64/Kconfig index 5756c27234ef..a837be10df62 100644 --- a/arch/sw_64/Kconfig +++ b/arch/sw_64/Kconfig @@ -68,6 +68,7 @@ config SW64 select GENERIC_STRNCPY_FROM_USER select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL + select GPIOLIB if ACPI select HANDLE_DOMAIN_IRQ select HARDIRQS_SW_RESEND select HAVE_ARCH_AUDITSYSCALL diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 509f42e6ab6a..5ddd4a6badf8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -251,6 +251,7 @@ config GPIO_SUNWAY depends on SW64 select GPIO_GENERIC select GENERIC_IRQ_CHIP + default y if ACPI help Say Y or M here to build support for the Sunway GPIO block. -- Gitee From 3b7b049ccba5db0658e2d67d6b5dfc9f2cc46933 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Thu, 16 May 2024 09:00:55 +0800 Subject: [PATCH 17/25] anolis: sw64: kconfig: add I2C and SPI defconfig for junzhang ANBZ: #4688 The following configuration items are added and set to y: - CONFIG_I2C - CONFIG_I2C_DESIGNWARE_PLATFORM - CONFIG_SPI_CHIP - CONFIG_SPI_CHIP_MMIO Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/configs/junzhang_defconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/sw_64/configs/junzhang_defconfig b/arch/sw_64/configs/junzhang_defconfig index 5e8ab61e0977..e97e1fdfac4d 100644 --- a/arch/sw_64/configs/junzhang_defconfig +++ b/arch/sw_64/configs/junzhang_defconfig @@ -521,9 +521,13 @@ CONFIG_SERIAL_OF_PLATFORM=y CONFIG_VIRTIO_CONSOLE=y # CONFIG_HW_RANDOM is not set # CONFIG_I2C_COMPAT is not set +CONFIG_I2C=y CONFIG_I2C_CHARDEV=y +CONFIG_I2C_DESIGNWARE_PLATFORM=y CONFIG_I2C_MUX=y CONFIG_SPI=y +CONFIG_SPI_CHIP=y +CONFIG_SPI_CHIP_MMIO=y CONFIG_SPI_SPIDEV=y CONFIG_SENSORS_PVT=y CONFIG_SENSORS_LM75=y -- Gitee From 28b2041298a354878dc5b2ba4070cbc2002fad96 Mon Sep 17 00:00:00 2001 From: Zheng Chongzhen Date: Tue, 21 May 2024 11:20:04 +0000 Subject: [PATCH 18/25] anolis: sw64: ata: add ata_hrst_delay cmdline option ANBZ: #4688 The delay time added by commit ec745d2cf875 ("anolis: sw64: ahci: fix port reset") is insufficient. With this patch, it can set delay time for port reset via cmdline 'ata_hrst_delay='. For example, 'ata_hrst_delay=1000' means wait for 1000 ms. Signed-off-by: Zheng Chongzhen Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- drivers/ata/libata-sata.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index a701e1538482..3850c90537c0 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -517,6 +517,20 @@ int sata_set_spd(struct ata_link *link) } EXPORT_SYMBOL_GPL(sata_set_spd); +#ifdef CONFIG_SW64 +unsigned int port_reset_delay __read_mostly = 1; +static int __init port_reset_setup(char *str) +{ + int delay; + + get_option(&str, &delay); + if (delay > 0) + port_reset_delay = delay; + + return 0; +} +__setup("ata_hrst_delay=", port_reset_setup); +#endif /** * sata_link_hardreset - reset link via SATA phy reset * @link: link to reset @@ -580,7 +594,11 @@ int sata_link_hardreset(struct ata_link *link, const unsigned int *timing, /* Couldn't find anything in SATA I/II specs, but AHCI-1.1 * 10.4.2 says at least 1 ms. */ +#ifdef CONFIG_SW64 + ata_msleep(link->ap, port_reset_delay); +#else ata_msleep(link->ap, 1); +#endif /* bring link back */ rc = sata_link_resume(link, timing, deadline); -- Gitee From f21ded35e2de4301a0a9800b315de31772a38586 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Sun, 28 Apr 2024 09:03:52 +0800 Subject: [PATCH 19/25] anolis: sw64: dtb: disable built-in DTB for junzhang ANBZ: #4688 Starting from junzhang, we use DTB provided by firmware to pass some boot parameters that are dynamically generated by firmware and cannot be added to built-in DTB. Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sw_64/Kconfig b/arch/sw_64/Kconfig index a837be10df62..8a73f9ea9d2e 100644 --- a/arch/sw_64/Kconfig +++ b/arch/sw_64/Kconfig @@ -536,6 +536,7 @@ menu "Boot options" config BUILTIN_DTB bool "Embed DTB in kernel image" depends on OF + depends on SUBARCH_C3B default n help Embeds a device tree binary in the kernel image. -- Gitee From 1fe735aef8b39dbcf942655f301ec19e9b6983ac Mon Sep 17 00:00:00 2001 From: Jing Li Date: Mon, 29 Apr 2024 14:04:53 +0800 Subject: [PATCH 20/25] anolis: sw64: smp: support SMP initialization based on device tree ANBZ: #4688 When ACPI is disabled, we prefer to take core related information from device tree rather than I/O operations in kernel. Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/include/asm/smp.h | 6 ++ arch/sw_64/kernel/acpi.c | 8 +-- arch/sw_64/kernel/setup.c | 14 ++-- arch/sw_64/kernel/smp.c | 120 ++++++++++++++++++++++++++++++++++- 4 files changed, 133 insertions(+), 15 deletions(-) diff --git a/arch/sw_64/include/asm/smp.h b/arch/sw_64/include/asm/smp.h index 097f22b72c25..130c52c9e306 100644 --- a/arch/sw_64/include/asm/smp.h +++ b/arch/sw_64/include/asm/smp.h @@ -36,6 +36,8 @@ struct smp_rcb_struct { unsigned long init_done; }; +extern bool __init is_rcid_duplicate(int rcid); + #ifdef CONFIG_SMP /* SMP initialization hook for setup_arch */ void __init setup_smp(void); @@ -90,6 +92,9 @@ extern int get_thread_id_from_rcid(int rcid); extern int get_domain_id_from_rcid(int rcid); #else /* CONFIG_SMP */ + +static inline void __init setup_smp(void) { store_cpu_data(0); } + #define hard_smp_processor_id() 0 #define smp_call_function_on_cpu(func, info, wait, cpu) ({ 0; }) /* The map from sequential logical cpu number to hard cid. */ @@ -108,6 +113,7 @@ static inline void rcid_information_init(int core_version) { } static inline int get_core_id_from_rcid(int rcid) { return 0; } static inline int get_thread_id_from_rcid(int rcid) { return 0; } static inline int get_domain_id_from_rcid(int rcid) { return 0; } + #endif /* CONFIG_SMP */ #define NO_PROC_ID (-1) diff --git a/arch/sw_64/kernel/acpi.c b/arch/sw_64/kernel/acpi.c index 5be5d93018cc..990db687657e 100644 --- a/arch/sw_64/kernel/acpi.c +++ b/arch/sw_64/kernel/acpi.c @@ -226,7 +226,7 @@ int acpi_unmap_cpu(int cpu) EXPORT_SYMBOL(acpi_unmap_cpu); #endif /* CONFIG_ACPI_HOTPLUG_CPU */ -static bool __init is_rcid_duplicate(int rcid) +bool __init is_rcid_duplicate(int rcid) { int i; @@ -249,14 +249,14 @@ setup_rcid_and_core_mask(struct acpi_madt_sw_cintc *sw_cintc) * represents the maximum number of cores in the system. */ if (possible_cores >= nr_cpu_ids) { - pr_err(PREFIX "Max core num [%u] reached, core [0x%x] ignored\n", - nr_cpu_ids, rcid); + pr_err(PREFIX "Core [0x%x] exceeds max core num [%u]\n", + rcid, nr_cpu_ids); return -ENODEV; } /* The rcid of each core is unique */ if (is_rcid_duplicate(rcid)) { - pr_err(PREFIX "Duplicate core [0x%x] in MADT\n", rcid); + pr_err(PREFIX "Duplicate core [0x%x]\n", rcid); return -EINVAL; } diff --git a/arch/sw_64/kernel/setup.c b/arch/sw_64/kernel/setup.c index aceaecb5bf8e..411810dbf10d 100644 --- a/arch/sw_64/kernel/setup.c +++ b/arch/sw_64/kernel/setup.c @@ -688,7 +688,6 @@ setup_arch(char **cmdline_p) setup_cpu_info(); setup_run_mode(); setup_chip_ops(); - sw64_chip_init->early_init.setup_core_map(&core_start); if (is_guest_or_emul()) get_vt_smp_info(); @@ -730,13 +729,10 @@ setup_arch(char **cmdline_p) /* Parse the ACPI tables for possible boot-time configuration */ acpi_boot_table_init(); - if (acpi_disabled) { -#ifdef CONFIG_SMP - setup_smp(); -#else - store_cpu_data(0); -#endif - } + if (acpi_disabled) + device_tree_init(); + + setup_smp(); sw64_numa_init(); @@ -779,11 +775,9 @@ setup_arch(char **cmdline_p) #ifdef CONFIG_NUMA cpu_set_node(); #endif - device_tree_init(); } } - static int show_cpuinfo(struct seq_file *f, void *slot) { diff --git a/arch/sw_64/kernel/smp.c b/arch/sw_64/kernel/smp.c index 78d4082e1a58..03118ebc56ee 100644 --- a/arch/sw_64/kernel/smp.c +++ b/arch/sw_64/kernel/smp.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -220,6 +222,103 @@ void __init smp_rcb_init(struct smp_rcb_struct *smp_rcb_base_addr) mb(); } +static int __init sw64_of_core_version(const struct device_node *dn, + int *version) +{ + if (!dn || !version) + return -EINVAL; + + if (of_device_is_compatible(dn, "sw64,xuelang")) { + *version = CORE_VERSION_C3B; + return 0; + } + + if (of_device_is_compatible(dn, "sw64,junzhang")) { + *version = CORE_VERSION_C4; + return 0; + } + + return -EINVAL; +} + +static int __init fdt_setup_smp(void) +{ + struct device_node *dn = NULL; + u64 boot_flag_address; + u32 rcid, logical_core_id = 0; + int ret, i, version; + + /* Clean the map from logical core ID to physical core ID */ + for (i = 0; i < ARRAY_SIZE(__cpu_to_rcid); ++i) + set_rcid_map(i, -1); + + /* Clean core mask */ + init_cpu_possible(cpu_none_mask); + init_cpu_present(cpu_none_mask); + + while ((dn = of_find_node_by_type(dn, "cpu"))) { + if (!of_device_is_available(dn)) { + pr_info("OF: Core is not available\n"); + continue; + } + + ret = of_property_read_u32(dn, "reg", &rcid); + if (ret) { + pr_err("OF: Found core without rcid\n"); + return -ENODEV; + } + + if (logical_core_id >= nr_cpu_ids) { + pr_err("OF: Core [0x%x] exceeds max core num [%u]\n", + rcid, nr_cpu_ids); + return -ENODEV; + } + + if (is_rcid_duplicate(rcid)) { + pr_err("OF: Duplicate core [0x%x]\n", rcid); + return -EINVAL; + } + + ret = sw64_of_core_version(dn, &version); + if (ret) { + pr_err("OF: No valid core version found\n"); + return ret; + } + + ret = of_property_read_u64(dn, "sw64,boot_flag_address", + &boot_flag_address); + if (ret) { + pr_err("OF: No boot_flag_address found\n"); + return ret; + } + + set_rcid_map(logical_core_id, rcid); + set_cpu_possible(logical_core_id, true); + store_cpu_data(logical_core_id); + + if (!cpumask_test_cpu(logical_core_id, &cpu_offline)) + set_cpu_present(logical_core_id, true); + + rcid_information_init(version); + + smp_rcb_init(__va(boot_flag_address)); + + logical_core_id++; + } + + /* No valid cpu node found */ + if (!num_possible_cpus()) + return -EINVAL; + + /* It's time to update nr_cpu_ids */ + nr_cpu_ids = num_possible_cpus(); + + pr_info("OF: Detected %u possible CPU(s), %u CPU(s) are present\n", + nr_cpu_ids, num_present_cpus()); + + return 0; +} + /* * Called from setup_arch. Detect an SMP system and which processors * are present. @@ -228,10 +327,29 @@ void __init setup_smp(void) { int i = 0, num = 0; + /* First try SMP initialization via ACPI */ + if (!acpi_disabled) + return; + + /* Next try SMP initialization via device tree */ + if (!fdt_setup_smp()) + return; + + /* Fallback to legacy SMP initialization */ + + /* Clean the map from logical core ID to physical core ID */ + for (i = 0; i < ARRAY_SIZE(__cpu_to_rcid); ++i) + set_rcid_map(i, -1); + + /* Clean core mask */ init_cpu_possible(cpu_none_mask); + init_cpu_present(cpu_none_mask); + + /* Legacy core detect */ + sw64_chip_init->early_init.setup_core_map(&core_start); /* For unified kernel, NR_CPUS is the maximum possible value */ - for (; i < NR_CPUS; i++) { + for (i = 0; i < NR_CPUS; i++) { if (cpu_to_rcid(i) != -1) { set_cpu_possible(num, true); store_cpu_data(num); -- Gitee From e2e8c14ce399dae336a149bb9ab3af00d8607810 Mon Sep 17 00:00:00 2001 From: Jing Li Date: Mon, 29 Apr 2024 14:33:39 +0800 Subject: [PATCH 21/25] anolis: sw64: numa: support NUMA initialization based on device tree ANBZ: #4688 Make the generic function of_numa_init() work on sunway platform. Signed-off-by: Jing Li Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/sw_64/Kconfig b/arch/sw_64/Kconfig index 8a73f9ea9d2e..59c400211394 100644 --- a/arch/sw_64/Kconfig +++ b/arch/sw_64/Kconfig @@ -451,6 +451,7 @@ config NUMA bool "NUMA Support" depends on SMP && !FLATMEM select ACPI_NUMA if ACPI + select OF_NUMA help Say Y to compile the kernel to support NUMA (Non-Uniform Memory Access). This option is for configuring high-end multiprocessor -- Gitee From 572cc057b0d278f00c38b70c5a5a88b283b57c3d Mon Sep 17 00:00:00 2001 From: He Sheng Date: Wed, 17 Apr 2024 14:56:56 +0800 Subject: [PATCH 22/25] anolis: sw64: remove some unused module init/exit/license ANBZ: #4688 This patch cleans up some module related code, because these source will never be compiled into kernel modules. Signed-off-by: He Sheng Reviewed-by: Cui Wei Signed-off-by: Gu Zitao --- arch/sw_64/pci/msi.c | 1 - drivers/irqchip/irq-sunway-msi-vt.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/sw_64/pci/msi.c b/arch/sw_64/pci/msi.c index fc2c122c37ef..98dfb1c3b3e7 100644 --- a/arch/sw_64/pci/msi.c +++ b/arch/sw_64/pci/msi.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#include #include #include #include diff --git a/drivers/irqchip/irq-sunway-msi-vt.c b/drivers/irqchip/irq-sunway-msi-vt.c index df8c7d72671b..65d4ea559447 100644 --- a/drivers/irqchip/irq-sunway-msi-vt.c +++ b/drivers/irqchip/irq-sunway-msi-vt.c @@ -2,6 +2,7 @@ #include #include #include +#include static DEFINE_RAW_SPINLOCK(vector_lock); -- Gitee From 25d9fb3e1ace05d6ad16498535186078749f0891 Mon Sep 17 00:00:00 2001 From: Hang Xiaoqian Date: Thu, 9 May 2024 10:27:54 +0800 Subject: [PATCH 23/25] anolis: sw64: fix sw64_is_fake_mcount() of recordmcount ANBZ: #4688 The recordmcount may misidentify reloc type ELF_LITERAL_GOT as _mcount, which causes the original instruction to be replaced with NOP. Signed-off-by: Hang Xiaoqian Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- scripts/recordmcount.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 73558f7eb690..61f4975d6d36 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -499,6 +499,9 @@ static int sw64_is_fake_mcount(Elf64_Rel const *rp) Elf64_Addr current_r_offset = _w(rp->r_offset); int is_fake; + if (Elf_r_sym(rp) == 0) + return 1; + is_fake = (old_r_offset != ~(Elf64_Addr)0) && (current_r_offset - old_r_offset == SW64_FAKEMCOUNT_OFFSET); old_r_offset = current_r_offset; -- Gitee From 287efa9a2d960dc0432d0387e3cd094d1365c79b Mon Sep 17 00:00:00 2001 From: Zheng Chongzhen Date: Tue, 7 May 2024 15:24:51 +0000 Subject: [PATCH 24/25] anolis: sw64: modify some interrupt target core to logic 0 core ANBZ: #4688 Actually, the logic 0 core is the first online core, and its cid does not have to be zero. Signed-off-by: Zheng Chongzhen Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/include/asm/uncore_io_junzhang.h | 9 ++++++--- arch/sw_64/include/asm/uncore_io_xuelang.h | 6 ++++-- drivers/pci/controller/pci-sunway.c | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/arch/sw_64/include/asm/uncore_io_junzhang.h b/arch/sw_64/include/asm/uncore_io_junzhang.h index 8c14890e5c1d..770af3907b5c 100644 --- a/arch/sw_64/include/asm/uncore_io_junzhang.h +++ b/arch/sw_64/include/asm/uncore_io_junzhang.h @@ -62,9 +62,12 @@ #define LPC_FIRMWARE_IO (0x3UL << 28 | IO_BASE | LPC_BASE) #define PCI_VT_LEGACY_IO (IO_BASE | PCI_BASE | PCI_LEGACY_IO) -#define PME_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x8UL << 10) -#define AER_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x8UL << 10) -#define HP_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x8UL << 10) +#define CORE0_CID (rcid_to_domain_id(cpu_to_rcid(0)) << 7 | \ + rcid_to_thread_id(cpu_to_rcid(0)) << 6 | \ + rcid_to_core_id(cpu_to_rcid(0))) +#define PME_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x8UL << 10 | CORE0_CID) +#define AER_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x8UL << 10 | CORE0_CID) +#define HP_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x8UL << 10 | CORE0_CID) #define PIUCONFIG0_INIT_VAL 0x38016 diff --git a/arch/sw_64/include/asm/uncore_io_xuelang.h b/arch/sw_64/include/asm/uncore_io_xuelang.h index 695ce68dded9..7a8c9edc8bfb 100644 --- a/arch/sw_64/include/asm/uncore_io_xuelang.h +++ b/arch/sw_64/include/asm/uncore_io_xuelang.h @@ -66,8 +66,10 @@ #define DLI_PHY_CTL (0x10UL << 24) #define PCI_VT_LEGACY_IO (IO_BASE | PCI_BASE | PCI_LEGACY_IO) -#define PME_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x1UL << 10) -#define AER_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x1UL << 10) +#define CORE0_CID (rcid_to_domain_id(cpu_to_rcid(0)) << 6 | \ + rcid_to_core_id(cpu_to_rcid(0))) +#define PME_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x1UL << 10 | CORE0_CID) +#define AER_ENABLE_INTD_CORE0 (0x1UL << 62 | 0x1UL << 10 | CORE0_CID) #define PIUCONFIG0_INIT_VAL 0x38056 diff --git a/drivers/pci/controller/pci-sunway.c b/drivers/pci/controller/pci-sunway.c index 6af0020ce1ee..139c9515fe6f 100644 --- a/drivers/pci/controller/pci-sunway.c +++ b/drivers/pci/controller/pci-sunway.c @@ -19,7 +19,7 @@ void set_devint_wken(int node) #ifdef CONFIG_UNCORE_JUNZHANG void set_adr_int(int node) { - sw64_io_write(node, ADR_INT_CONFIG, (0x0 << 16 | 0x3f)); + sw64_io_write(node, ADR_INT_CONFIG, (CORE0_CID << 16 | 0x3f)); sw64_io_write(node, ADR_CTL, 0xc); } #endif -- Gitee From 48d999b889033d1b8ec76491be876cf4d097d24e Mon Sep 17 00:00:00 2001 From: Zhi Tongze Date: Wed, 8 May 2024 15:37:09 +0800 Subject: [PATCH 25/25] anolis: sw64: fix regs.pc on single-step with ptrace ANBZ: #4688 Previous patch had unexpectedly removed single step treatment from breakpoint handler, result in wrong pc after ptrace single step. Fixes: 1db0ae17cfe4 ("anolis: sw64: fix instruction fault handler") Signed-off-by: Zhi Tongze Reviewed-by: He Sheng Signed-off-by: Gu Zitao --- arch/sw_64/kernel/traps.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/sw_64/kernel/traps.c b/arch/sw_64/kernel/traps.c index 8dd522d09860..c9e956b01dc9 100644 --- a/arch/sw_64/kernel/traps.c +++ b/arch/sw_64/kernel/traps.c @@ -275,6 +275,8 @@ do_entIF(unsigned long inst_type, unsigned long va, struct pt_regs *regs) switch (type) { case IF_BREAKPOINT: /* gdb do pc-4 for sigtrap */ + if (ptrace_cancel_bpt(current)) + regs->pc -= 4; force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->pc); return; -- Gitee