diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index d83a3f47e20074c0126dbe5df0415237a6eda6b9..4d31800dced6f0b0633b37826d756bc1968f7eab 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3989,6 +3989,14 @@ parameter, xsave area per process might occupy more memory on xsaves enabled systems. + arm64_cpu0_hotplug [ARM64] Turn on arm64_cpu0_hotpluggable when + CONFIG_ARM64_BOOTPARAM_HOTPLUG_CPU0 is off. + Some features depend on CPU0. Known dependency is: + MegaRAID Tri-Mode SAS3508 may block the reboot process + after offline CPU0. + If the dependencies are under your control, you can + turn on arm64_cpu0_hotplug. + nps_mtm_hs_ctr= [KNL,ARC] This parameter sets the maximum duration, in cycles, each HW thread of the CTOP can run diff --git a/Documentation/arch/arm64/cpu-hotplug.rst b/Documentation/arch/arm64/cpu-hotplug.rst new file mode 100644 index 0000000000000000000000000000000000000000..76ba8d932c72264521cdbb4d3e6937525a1d6d87 --- /dev/null +++ b/Documentation/arch/arm64/cpu-hotplug.rst @@ -0,0 +1,79 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. _cpuhp_index: + +==================== +CPU Hotplug and ACPI +==================== + +CPU hotplug in the arm64 world is commonly used to describe the kernel taking +CPUs online/offline using PSCI. This document is about ACPI firmware allowing +CPUs that were not available during boot to be added to the system later. + +``possible`` and ``present`` refer to the state of the CPU as seen by linux. + + +CPU Hotplug on physical systems - CPUs not present at boot +---------------------------------------------------------- + +Physical systems need to mark a CPU that is ``possible`` but not ``present`` as +being ``present``. An example would be a dual socket machine, where the package +in one of the sockets can be replaced while the system is running. + +This is not supported. + +In the arm64 world CPUs are not a single device but a slice of the system. +There are no systems that support the physical addition (or removal) of CPUs +while the system is running, and ACPI is not able to sufficiently describe +them. + +e.g. New CPUs come with new caches, but the platform's cache toplogy is +described in a static table, the PPTT. How caches are shared between CPUs is +not discoverable, and must be described by firmware. + +e.g. The GIC redistributor for each CPU must be accessed by the driver during +boot to discover the system wide supported features. ACPI's MADT GICC +structures can describe a redistributor associated with a disabled CPU, but +can't describe whether the redistributor is accessible, only that it is not +'always on'. + +arm64's ACPI tables assume that everything described is ``present``. + + +CPU Hotplug on virtual systems - CPUs not enabled at boot +--------------------------------------------------------- + +Virtual systems have the advantage that all the properties the system will +ever have can be described at boot. There are no power-domain considerations +as such devices are emulated. + +CPU Hotplug on virtual systems is supported. It is distinct from physical +CPU Hotplug as all resources are described as ``present``, but CPUs may be +marked as disabled by firmware. Only the CPU's online/offline behaviour is +influenced by firmware. An example is where a virtual machine boots with a +single CPU, and additional CPUs are added once a cloud orchestrator deploys +the workload. + +For a virtual machine, the VMM (e.g. Qemu) plays the part of firmware. + +Virtual hotplug is implemented as a firmware policy affecting which CPUs can be +brought online. Firmware can enforce its policy via PSCI's return codes. e.g. +``DENIED``. + +The ACPI tables must describe all the resources of the virtual machine. CPUs +that firmware wishes to disable either from boot (or later) should not be +``enabled`` in the MADT GICC structures, but should have the ``online capable`` +bit set, to indicate they can be enabled later. The boot CPU must be marked as +``enabled``. The 'always on' GICR structure must be used to describe the +redistributors. + +CPUs described as ``online capable`` but not ``enabled`` can be set to enabled +by the DSDT's Processor object's _STA method. On virtual systems the _STA method +must always report the CPU as ``present``. Changes to the firmware policy can +be notified to the OS via device-check or eject-request. + +CPUs described as ``enabled`` in the static table, should not have their _STA +modified dynamically by firmware. Soft-restart features such as kexec will +re-read the static properties of the system from these static tables, and +may malfunction if these no longer describe the running system. Linux will +re-discover the dynamic properties of the system from the _STA method later +during boot. diff --git a/Documentation/arch/arm64/index.rst b/Documentation/arch/arm64/index.rst index d08e924204bf15a6528e4b20ff046e74e11c04aa..78544de0a8a9e8b833c1fca80439999247137244 100644 --- a/Documentation/arch/arm64/index.rst +++ b/Documentation/arch/arm64/index.rst @@ -13,6 +13,7 @@ ARM64 Architecture asymmetric-32bit booting cpu-feature-registers + cpu-hotplug elf_hwcaps hugetlbpage kdump diff --git a/Documentation/virt/kvm/arm/pvsched.rst b/Documentation/virt/kvm/arm/pvsched.rst new file mode 100644 index 0000000000000000000000000000000000000000..8f7112a8a9cd91a5cd69dc0ff6d47c946f0960b4 --- /dev/null +++ b/Documentation/virt/kvm/arm/pvsched.rst @@ -0,0 +1,58 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Paravirtualized sched support for arm64 +======================================= + +KVM/arm64 provides some hypervisor service calls to support a paravirtualized +sched. + +Some SMCCC compatible hypercalls are defined: + +* PV_SCHED_FEATURES: 0xC5000090 +* PV_SCHED_IPA_INIT: 0xC5000091 +* PV_SCHED_IPA_RELEASE: 0xC5000092 + +The existence of the PV_SCHED hypercall should be probed using the SMCCC 1.1 +ARCH_FEATURES mechanism before calling it. + +PV_SCHED_FEATURES + ============= ======== ========== + Function ID: (uint32) 0xC5000090 + PV_call_id: (uint32) The function to query for support. + Return value: (int64) NOT_SUPPORTED (-1) or SUCCESS (0) if the relevant + PV-sched feature is supported by the hypervisor. + ============= ======== ========== + +PV_SCHED_IPA_INIT + ============= ======== ========== + Function ID: (uint32) 0xC5000091 + Return value: (int64) NOT_SUPPORTED (-1) or SUCCESS (0) if the IPA of + this vCPU's PV data structure is shared to the + hypervisor. + ============= ======== ========== + +PV_SCHED_IPA_RELEASE + ============= ======== ========== + Function ID: (uint32) 0xC5000092 + Return value: (int64) NOT_SUPPORTED (-1) or SUCCESS (0) if the IPA of + this vCPU's PV data structure is released. + ============= ======== ========== + +PV sched state +-------------- + +The structure pointed to by the PV_SCHED_IPA hypercall is as follows: + ++-----------+-------------+-------------+-----------------------------------+ +| Field | Byte Length | Byte Offset | Description | ++===========+=============+=============+===================================+ +| preempted | 4 | 0 | Indicates that the vCPU that owns | +| | | | this struct is running or not. | +| | | | Non-zero values mean the vCPU has | +| | | | been preempted. Zero means the | +| | | | vCPU is not preempted. | ++-----------+-------------+-------------+-----------------------------------+ + +The preempted field will be updated to 0 by the hypervisor prior to scheduling +a vCPU. When the vCPU is scheduled out, the preempted field will be updated +to 1 by the hypervisor. diff --git a/MAINTAINERS b/MAINTAINERS index 3df7cd72785c816f82cc3dd5d900605f648151e0..e70c8cd744bfd5a77a4149dfbaaa5d3f09b6e3d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17429,6 +17429,9 @@ ARM/PHYTIUM SOC SUPPORT M: Wang Yinfeng S: Maintained W: https://www.phytium.com.cn +F: arch/arm64/include/asm/pvsched-abi.h +F: arch/arm64/kernel/paravirt-spinlocks.c +F: Documentation/arch/arm64/cpu-hotplug.rst F: Documentation/devicetree/bindings/dma/phytium,ddma.yaml F: Documentation/devicetree/bindings/dma/phytium,gdma.yaml F: Documentation/devicetree/bindings/edac/phytium-pe220x-edac.txt @@ -17474,10 +17477,13 @@ F: Documentation/devicetree/bindings/usb/phytium,usb2.yaml F: Documentation/devicetree/bindings/usb/phytium.role-sw.yaml F: Documentation/devicetree/bindings/w1/phytium,w1.yaml F: Documentation/devicetree/bindings/staging/phytium-npu.yaml +F: Documentation/virt/kvm/arm/pvsched.rst F: arch/arm64/boot/dts/phytium/* +F: arch/arm64/include/asm/phytium_cputype.h +F: arch/arm64/include/asm/phyt.h F: arch/arm64/include/asm/ras.h F: arch/arm64/kernel/ras.c -F: drivers/acpi/phytium_base_ctrl.c +F: drivers/bus/phytium_pio.c F: drivers/char/hw_random/phytium-rng.c F: drivers/char/ipmi/bt_bmc_phytium.c F: drivers/char/ipmi/kcs_bmc_phytium.c @@ -17504,6 +17510,7 @@ F: drivers/media/platform/phytium-jpeg/phytium_jpeg* F: drivers/mfd/phytium_px210_i2s_lsd.c F: drivers/mfd/phytium_px210_i2s_mmd.c F: drivers/misc/phytium-snoop-ctrl.c +F: drivers/misc/phytium_pwrmon.c F: drivers/mmc/host/phytium-mci* F: drivers/mmc/host/phytium-mci-plat-v2.c F: drivers/mmc/host/phytium-mci-v2* diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index e30dc74b6dcdb6153c67ff577ffed9017f352be7..94423a385da1b3999d6d599be7d7795937405fb3 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -134,6 +134,7 @@ config ARM64 select GENERIC_ARCH_TOPOLOGY select GENERIC_CLOCKEVENTS_BROADCAST select GENERIC_CPU_AUTOPROBE + select GENERIC_CPU_DEVICES select GENERIC_CPU_VULNERABILITIES select GENERIC_EARLY_IOREMAP select GENERIC_IDLE_POLL_SETUP @@ -1477,6 +1478,34 @@ config HOTPLUG_CPU Say Y here to experiment with turning CPUs off and on. CPUs can be controlled through /sys/devices/system/cpu. +config ARM64_BOOTPARAM_HOTPLUG_CPU0 + bool "Set default setting of arm64_cpu0_hotpluggable" + default n + depends on HOTPLUG_CPU + help + Set whether default state of arm64_cpu0_hotpluggable is on or off. + + Say Y here to set arm64_cpu0_hotpluggable on by default. If this + switch is turned on, there is no need to give arm64_cpu0_hotplug + kernel parameter and arm64_cpu0_hotpluggable is on by default. + + Please note: there may be some CPU0 dependencies if you want + to enable the CPU0 hotplug feature either by this switch or by + arm64_cpu0_hotplug kernel parameter. + + For example: + We found the following issue related to CPU0 dependency: + 1. echo 0 > /sys/devices/system/cpu/cpu0/online + 2. reboot + MegaRAID Tri-Mode SAS3508 may block the reboot process. + + Please make sure the dependencies are under your control before + you enable this feature. + + Say N if you don't want to enable CPU0 hotplug feature by default. + You still can set arm64_cpu0_hotpluggable on at boot by kernel + parameter arm64_cpu0_hotplug. + config ARM64_ERR_RECOV bool "Support arm64 RAS error recovery" depends on ACPI_APEI_SEA && MEMORY_FAILURE @@ -1538,6 +1567,17 @@ config PARAVIRT under a hypervisor, potentially improving performance significantly over full virtualization. +config PARAVIRT_SCHED + bool "Paravirtualization layer for sched" + depends on PARAVIRT + help + This supports the vCPU preemption check to enhance lock performance on + overcommitted hosts (more runnable vCPUs than physical CPUs in the + system) as doing busy waits for preempted vCPUs will hurt system + performance far worse than early yielding. + + If you are unsure how to answer this question, answer Y. + config PARAVIRT_TIME_ACCOUNTING bool "Paravirtual steal time accounting" select PARAVIRT diff --git a/arch/arm64/boot/dts/phytium/pe2201-demo-ddr4.dts b/arch/arm64/boot/dts/phytium/pe2201-demo-ddr4.dts index 7e8e51fc4180c3afd792780ec931a96ce47f4bc9..38f61d2aa8d8d66085294d5a537f3968cded4d9f 100644 --- a/arch/arm64/boot/dts/phytium/pe2201-demo-ddr4.dts +++ b/arch/arm64/boot/dts/phytium/pe2201-demo-ddr4.dts @@ -18,7 +18,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0 { + memory@00 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2202-chillipi-edu-board.dts b/arch/arm64/boot/dts/phytium/pe2202-chillipi-edu-board.dts index e358e7c2393d304467b9031bfb8e8e40a4f7a95b..0ab55a50ffd179c72884e3dbce2ae0505d99d492 100644 --- a/arch/arm64/boot/dts/phytium/pe2202-chillipi-edu-board.dts +++ b/arch/arm64/boot/dts/phytium/pe2202-chillipi-edu-board.dts @@ -25,7 +25,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0{ + memory@00{ device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2202-demo-ddr4-local.dts b/arch/arm64/boot/dts/phytium/pe2202-demo-ddr4-local.dts index 0684e91f7b83784cbde989f3ed64a17b620588e3..79c0262ed3a3fddf5ec189f021f7eb5a73505291 100644 --- a/arch/arm64/boot/dts/phytium/pe2202-demo-ddr4-local.dts +++ b/arch/arm64/boot/dts/phytium/pe2202-demo-ddr4-local.dts @@ -18,7 +18,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0 { + memory@00 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2202-demo-ddr4.dts b/arch/arm64/boot/dts/phytium/pe2202-demo-ddr4.dts index f9dacea116950f721124720ce7d404df91991506..d139f6850ef7965cc7f612f6d971c89c8f6f0692 100644 --- a/arch/arm64/boot/dts/phytium/pe2202-demo-ddr4.dts +++ b/arch/arm64/boot/dts/phytium/pe2202-demo-ddr4.dts @@ -19,7 +19,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0 { + memory@00 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2202-miniitx-board.dts b/arch/arm64/boot/dts/phytium/pe2202-miniitx-board.dts index 7d06f07cc0fc704672f605393cc5a32883c718bc..6f38e532a2758cc53420238fea27ef14830cc9d6 100644 --- a/arch/arm64/boot/dts/phytium/pe2202-miniitx-board.dts +++ b/arch/arm64/boot/dts/phytium/pe2202-miniitx-board.dts @@ -25,7 +25,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0{ + memory@00{ device_type = "memory"; reg = <0x0 0x80000000 0x1 0x00000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2202-power-board.dts b/arch/arm64/boot/dts/phytium/pe2202-power-board.dts index b0294d02d9505c8cd30d8e6648b425afc0e33a0d..1726a84fa7b6f787f0ec4caee9d398ce012aec77 100644 --- a/arch/arm64/boot/dts/phytium/pe2202-power-board.dts +++ b/arch/arm64/boot/dts/phytium/pe2202-power-board.dts @@ -25,7 +25,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0{ + memory@00{ device_type = "memory"; reg = <0x0 0x80000000 0x1 0x00000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2202.dtsi b/arch/arm64/boot/dts/phytium/pe2202.dtsi index 5d42cccc6cb23e5ebb3e8362570315f50d4d3b02..c5d94059e5165c4d39ce82078f4a0e6309a95ede 100644 --- a/arch/arm64/boot/dts/phytium/pe2202.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2202.dtsi @@ -114,6 +114,7 @@ i2s0: i2s@28009000 { interrupts = ; clocks = <&sysclk_600mhz>; clock-names = "i2s_clk"; + dai-name = "phytium-i2s-lsd"; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/phytium/pe2204-come-board.dts b/arch/arm64/boot/dts/phytium/pe2204-come-board.dts index e87b2607822485bb3d9b9a5d30cfc7a7149ec021..1ce5c0034d9274a3e76bc15e0e866370c962f83f 100644 --- a/arch/arm64/boot/dts/phytium/pe2204-come-board.dts +++ b/arch/arm64/boot/dts/phytium/pe2204-come-board.dts @@ -25,7 +25,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0{ + memory@00{ device_type = "memory"; reg = <0x0 0x80000000 0x2 0x00000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2204-demo-ddr4-local.dts b/arch/arm64/boot/dts/phytium/pe2204-demo-ddr4-local.dts index 402d07735b863c5c9363254390d52abd6609af30..65fb64e87d090f907889a3a235c998858d0b9c48 100644 --- a/arch/arm64/boot/dts/phytium/pe2204-demo-ddr4-local.dts +++ b/arch/arm64/boot/dts/phytium/pe2204-demo-ddr4-local.dts @@ -18,7 +18,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0 { + memory@00 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2204-demo-ddr4.dts b/arch/arm64/boot/dts/phytium/pe2204-demo-ddr4.dts index c27bf0dec9b58a53465758fd69d15b47c3cb114f..59ae3e653072e13af0bf630e75c6b6a6070764dc 100644 --- a/arch/arm64/boot/dts/phytium/pe2204-demo-ddr4.dts +++ b/arch/arm64/boot/dts/phytium/pe2204-demo-ddr4.dts @@ -19,7 +19,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0 { + memory@00 { device_type = "memory"; reg = <0x0 0x80000000 0x0 0x80000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2204-edu-board.dts b/arch/arm64/boot/dts/phytium/pe2204-edu-board.dts index 7a47cbc375a2f7c9ca414bb964e692a0a16395f8..96c84e5eabe7c2603eb09440c946d288806d4948 100644 --- a/arch/arm64/boot/dts/phytium/pe2204-edu-board.dts +++ b/arch/arm64/boot/dts/phytium/pe2204-edu-board.dts @@ -25,7 +25,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0{ + memory@00{ device_type = "memory"; reg = <0x0 0x80000000 0x2 0x00000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2204-hanwei-board.dts b/arch/arm64/boot/dts/phytium/pe2204-hanwei-board.dts index d9e1eae59d982aa021686de696736316637ebf00..02b635dd48c2e2b9548222bbdf47b25ce191de2b 100644 --- a/arch/arm64/boot/dts/phytium/pe2204-hanwei-board.dts +++ b/arch/arm64/boot/dts/phytium/pe2204-hanwei-board.dts @@ -25,7 +25,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0{ + memory@00{ device_type = "memory"; reg = <0x0 0x80000000 0x2 0x00000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts b/arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts index 2513b7b5e21ab1bfe978c3d8444039720027d6b8..a806f116108f995f0f94e678fb8a208ef829dfb3 100644 --- a/arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts +++ b/arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts @@ -32,7 +32,7 @@ aliases { serial8 = &mio15; }; - memory@0{ + memory@00{ device_type = "memory"; reg = <0x0 0x80000000 0x2 0x00000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2204-vpx-board.dts b/arch/arm64/boot/dts/phytium/pe2204-vpx-board.dts index 22c3c4395b9b0acb2d1addd8b575371c9944d09e..c3a6950a46d0645ce42335c83d10f0c191b3adcc 100644 --- a/arch/arm64/boot/dts/phytium/pe2204-vpx-board.dts +++ b/arch/arm64/boot/dts/phytium/pe2204-vpx-board.dts @@ -25,7 +25,7 @@ chosen { stdout-path = "serial1:115200n8"; }; - memory@0{ + memory@00{ device_type = "memory"; reg = <0x0 0x80000000 0x2 0x00000000>; }; diff --git a/arch/arm64/boot/dts/phytium/pe2204.dtsi b/arch/arm64/boot/dts/phytium/pe2204.dtsi index 228264b282ee3985002e963df92ff70bd41eaa81..16bc4889784c25c9a621e7b924730c98fcfd538a 100644 --- a/arch/arm64/boot/dts/phytium/pe2204.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2204.dtsi @@ -149,6 +149,7 @@ i2s0: i2s@28009000 { interrupts = ; clocks = <&sysclk_600mhz>; clock-names = "i2s_clk"; + dai-name = "phytium-i2s-lsd"; status = "disabled"; }; diff --git a/arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts b/arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts index 4f064b4e13c3f0c8319d5b83a87abe674db0f317..3c360d402e3f04631ebe602f76941d902d157e72 100644 --- a/arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts +++ b/arch/arm64/boot/dts/phytium/phytiumpi_firefly.dts @@ -26,7 +26,7 @@ aliases { serial6 = &mio15; }; - memory@0{ + memory@00{ device_type = "memory"; reg = <0x0 0x80000000 0x2 0x00000000>; }; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 6f24e05668617e2072e933848ca32d3a8adc7777..e9279fa1001a8164d8a45c72f68d039312314140 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -78,6 +78,8 @@ CONFIG_ARM64_VA_BITS_48=y CONFIG_SCHED_MC=y CONFIG_SCHED_SMT=y CONFIG_NUMA=y +CONFIG_PARAVIRT=y +CONFIG_PARAVIRT_SCHED=y CONFIG_KEXEC=y CONFIG_KEXEC_FILE=y CONFIG_CRASH_DUMP=y diff --git a/arch/arm64/configs/phytium_defconfig b/arch/arm64/configs/phytium_defconfig index 21c153d16238d58ba9371fa31f66d9c9020d0b5b..84078331524e899a6e486d6994193c81640b8eb7 100644 --- a/arch/arm64/configs/phytium_defconfig +++ b/arch/arm64/configs/phytium_defconfig @@ -27,9 +27,11 @@ CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_CPUACCT=y CONFIG_CGROUP_PERF=y CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y CONFIG_USER_NS=y CONFIG_SCHED_AUTOGROUP=y CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y CONFIG_KALLSYMS_ALL=y CONFIG_PROFILING=y CONFIG_KEXEC=y @@ -190,6 +192,7 @@ CONFIG_FW_LOADER_USER_HELPER=y CONFIG_FW_LOADER_COMPRESS=y CONFIG_FW_LOADER_COMPRESS_ZSTD=y CONFIG_BRCMSTB_GISB_ARB=y +CONFIG_PHYTIUM_PIO=y CONFIG_VEXPRESS_CONFIG=y CONFIG_MHI_BUS_PCI_GENERIC=m CONFIG_ARM_SCMI_PROTOCOL=y @@ -293,6 +296,7 @@ CONFIG_NGBE=y CONFIG_AQUANTIA_PHY=y CONFIG_BROADCOM_PHY=m CONFIG_BCM54140_PHY=m +CONFIG_LINKYUM_PHY=y CONFIG_MARVELL_PHY=m CONFIG_MARVELL_10G_PHY=y CONFIG_MICREL_PHY=y @@ -433,6 +437,7 @@ CONFIG_PINCTRL=y CONFIG_PINCTRL_MAX77620=y CONFIG_PINCTRL_RK805=m CONFIG_PINCTRL_SINGLE=y +CONFIG_GPIO_SYSFS=y CONFIG_GPIO_ALTERA=m CONFIG_GPIO_DWAPB=y CONFIG_GPIO_MB86S7X=y @@ -531,6 +536,7 @@ CONFIG_RC_DECODERS=y CONFIG_RC_DEVICES=y CONFIG_IR_GPIO_CIR=m CONFIG_MEDIA_SUPPORT=m +CONFIG_MEDIA_SUPPORT_FILTER=y CONFIG_MEDIA_CAMERA_SUPPORT=y CONFIG_MEDIA_ANALOG_TV_SUPPORT=y CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y @@ -606,6 +612,7 @@ CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y CONFIG_SND_ALOOP=m +CONFIG_SND_HDA_INTEL=y CONFIG_SND_HDA_PHYTIUM=y CONFIG_SND_HDA_CODEC_REALTEK=y CONFIG_SND_HDA_CODEC_HDMI=y @@ -624,6 +631,7 @@ CONFIG_SND_SOC_IMX_AUDMUX=m CONFIG_SND_SOC_PHYTIUM_I2S=y CONFIG_SND_PMDK_ES8388=y CONFIG_SND_PMDK_ES8336=y +CONFIG_SND_PMDK_MA1026=y CONFIG_SND_SOC_PHYTIUM_I2S_V2=y CONFIG_SND_SOC_PHYTIUM_MACHINE_V2=y CONFIG_SND_PMDK_DP=y @@ -674,6 +682,19 @@ CONFIG_SND_SOC_LPASS_TX_MACRO=m CONFIG_SND_SIMPLE_CARD=y CONFIG_SND_AUDIO_GRAPH_CARD=m CONFIG_SND_AUDIO_GRAPH_CARD2=m +CONFIG_HID_A4TECH=y +CONFIG_HID_APPLE=y +CONFIG_HID_BELKIN=y +CONFIG_HID_CHERRY=y +CONFIG_HID_CHICONY=y +CONFIG_HID_CYPRESS=y +CONFIG_HID_EZKEY=y +CONFIG_HID_ITE=y +CONFIG_HID_KENSINGTON=y +CONFIG_HID_LOGITECH=y +CONFIG_HID_REDRAGON=y +CONFIG_HID_MICROSOFT=y +CONFIG_HID_MONTEREY=y CONFIG_HID_MULTITOUCH=m CONFIG_I2C_HID_ACPI=m CONFIG_I2C_HID_OF=m @@ -706,7 +727,7 @@ CONFIG_USB_SERIAL_CP210X=m CONFIG_USB_SERIAL_FTDI_SIO=m CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_HSIC_USB3503=y -CONFIG_USB_ONBOARD_HUB=m +CONFIG_USB_ONBOARD_HUB=y CONFIG_NOP_USB_XCEIV=y CONFIG_USB_ULPI=y CONFIG_USB_GADGET=y @@ -820,7 +841,7 @@ CONFIG_STAGING=y CONFIG_STAGING_MEDIA=y CONFIG_VIDEO_MAX96712=m CONFIG_PHYTIUM_NPU=y -CONFIG_NPU_PLATFORM=y +CONFIG_PHYTIUM_NPU_PLATFORM=y CONFIG_CHROME_PLATFORMS=y CONFIG_CROS_EC=y CONFIG_CROS_EC_I2C=y @@ -941,6 +962,7 @@ CONFIG_HUGETLBFS=y CONFIG_EFIVAR_FS=y CONFIG_UBIFS_FS=m CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_XATTR=y CONFIG_SQUASHFS_LZO=y CONFIG_SQUASHFS_XZ=y CONFIG_NFS_FS=y @@ -953,6 +975,7 @@ CONFIG_NFSD_V2=y CONFIG_NFSD_V2_ACL=y CONFIG_NFSD_V3_ACL=y CONFIG_NFSD_V4=y +CONFIG_CIFS=y CONFIG_9P_FS=y CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_ISO8859_1=y @@ -984,11 +1007,9 @@ CONFIG_CRYPTO_DEV_HISI_ZIP=m CONFIG_CRYPTO_DEV_HISI_HPRE=m CONFIG_CRYPTO_DEV_HISI_TRNG=m CONFIG_CRYPTO_DEV_AMLOGIC_GXL=m -CONFIG_INDIRECT_PIO=y CONFIG_DMA_RESTRICTED_POOL=y CONFIG_CMA_SIZE_MBYTES=32 CONFIG_PRINTK_TIME=y -CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y CONFIG_DEBUG_INFO_REDUCED=y CONFIG_MAGIC_SYSRQ=y diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index e749838b9c5d4e03517f0e9261f3c6740555845c..887bd0d992bb2346878fe470ab92a348fa7f564e 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -38,7 +38,6 @@ struct cpuinfo_32bit { }; struct cpuinfo_arm64 { - struct cpu cpu; struct kobject kobj; u64 reg_ctr; u64 reg_cntfrq; diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index 9cf44f567f2469d78193b15bde36a0e9bce4136d..a3097136f91e1babcba4d4dbb90c2689b4894570 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -155,6 +155,9 @@ #define PHYTIUM_CPU_PART_FTC662 0x662 #define PHYTIUM_CPU_PART_FTC663 0x663 #define PHYTIUM_CPU_PART_FTC664 0x664 +#define PHYTIUM_CPU_PART_FTC862 0x862 + +#define PHYTIUM_CPU_SOCID_PS24080 0x6 #define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) #define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57) @@ -229,6 +232,8 @@ #define MIDR_PHYTIUM_PS17064 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC662) #define MIDR_PHYTIUM_FTC663 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC663) #define MIDR_PHYTIUM_FTC664 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC664) +#define MIDR_PHYTIUM_FTC862 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC862) + #define MIDR_AMPERE1A MIDR_CPU_MODEL(ARM_CPU_IMP_AMPERE, AMPERE_CPU_PART_AMPERE1A) #define MIDR_MICROSOFT_AZURE_COBALT_100 MIDR_CPU_MODEL(ARM_CPU_IMP_MICROSOFT, MICROSOFT_CPU_PART_AZURE_COBALT_100) diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index af06ccb7ee343304535c30b8855d6de8d8b1f4fc..038b4de4c8b39f04ee825b907854d48b5691a6d6 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -589,6 +589,13 @@ struct kvm_vcpu_arch { gpa_t base; } steal; +#ifdef CONFIG_PARAVIRT_SCHED + /* Guest PV sched state */ + struct { + gpa_t base; + } pvsched; +#endif + /* Per-vcpu CCSIDR override or NULL */ u32 *ccsidr; }; @@ -1038,6 +1045,33 @@ static inline bool kvm_arm_is_pvtime_enabled(struct kvm_vcpu_arch *vcpu_arch) return (vcpu_arch->steal.base != INVALID_GPA); } +#ifdef CONFIG_PARAVIRT_SCHED +long kvm_hypercall_pvsched_features(struct kvm_vcpu *vcpu); +void kvm_update_pvsched_preempted(struct kvm_vcpu *vcpu, u32 preempted); + +static inline void kvm_arm_pvsched_vcpu_init(struct kvm_vcpu_arch *vcpu_arch) +{ + vcpu_arch->pvsched.base = INVALID_GPA; +} + +static inline bool kvm_arm_is_pvsched_enabled(struct kvm_vcpu_arch *vcpu_arch) +{ + return (vcpu_arch->pvsched.base != INVALID_GPA); +} +#else +static inline long kvm_hypercall_pvsched_features(struct kvm_vcpu *vcpu) +{ + return 0; +} +static inline void kvm_update_pvsched_preempted(struct kvm_vcpu *vcpu, + u32 preempted) {} +static inline void kvm_arm_pvsched_vcpu_init(struct kvm_vcpu_arch *vcpu_arch) {} +static inline bool kvm_arm_is_pvsched_enabled(struct kvm_vcpu_arch *vcpu_arch) +{ + return false; +} +#endif + void kvm_set_sei_esr(struct kvm_vcpu *vcpu, u64 syndrome); struct kvm_vcpu *kvm_mpidr_to_vcpu(struct kvm *kvm, unsigned long mpidr); diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h index d3e354bb8351d7aa9762155e18cb49cffee938e0..de0b8845df7a14d5f76526e031bf62b47efe9182 100644 --- a/arch/arm64/include/asm/kvm_pgtable.h +++ b/arch/arm64/include/asm/kvm_pgtable.h @@ -169,6 +169,7 @@ enum kvm_pgtable_stage2_flags { * @KVM_PGTABLE_PROT_W: Write permission. * @KVM_PGTABLE_PROT_R: Read permission. * @KVM_PGTABLE_PROT_DEVICE: Device attributes. + * @KVM_PGTABLE_PROT_NORMAL_NC: Normal noncacheable attributes. * @KVM_PGTABLE_PROT_SW0: Software bit 0. * @KVM_PGTABLE_PROT_SW1: Software bit 1. * @KVM_PGTABLE_PROT_SW2: Software bit 2. @@ -180,6 +181,7 @@ enum kvm_pgtable_prot { KVM_PGTABLE_PROT_R = BIT(2), KVM_PGTABLE_PROT_DEVICE = BIT(3), + KVM_PGTABLE_PROT_NORMAL_NC = BIT(4), KVM_PGTABLE_PROT_SW0 = BIT(55), KVM_PGTABLE_PROT_SW1 = BIT(56), diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index fde4186cc3870894aa21f736f1bbeefdd63499c3..c247e5f29d5af58a48af51cf8bee191b8155fd07 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -147,6 +147,7 @@ * Memory types for Stage-2 translation */ #define MT_S2_NORMAL 0xf +#define MT_S2_NORMAL_NC 0x5 #define MT_S2_DEVICE_nGnRE 0x1 /* @@ -154,6 +155,7 @@ * Stage-2 enforces Normal-WB and Device-nGnRE */ #define MT_S2_FWB_NORMAL 6 +#define MT_S2_FWB_NORMAL_NC 5 #define MT_S2_FWB_DEVICE_nGnRE 1 #ifdef CONFIG_ARM64_4K_PAGES diff --git a/arch/arm64/include/asm/paravirt.h b/arch/arm64/include/asm/paravirt.h index 9aa193e0e8f28d9309bc18013230e714152f4f93..5ccead71bf8786fd9301c6a911c719fe5076b3d5 100644 --- a/arch/arm64/include/asm/paravirt.h +++ b/arch/arm64/include/asm/paravirt.h @@ -20,9 +20,22 @@ static inline u64 paravirt_steal_clock(int cpu) int __init pv_time_init(void); +#ifdef CONFIG_PARAVIRT_SCHED +int __init pv_sched_init(void); + +__visible bool __native_vcpu_is_preempted(int cpu); +DECLARE_STATIC_CALL(pv_vcpu_preempted, __native_vcpu_is_preempted); + +static inline bool pv_vcpu_is_preempted(int cpu) +{ + return static_call(pv_vcpu_preempted)(cpu); +} +#endif /* CONFIG_PARAVIRT_SCHED */ + #else #define pv_time_init() do {} while (0) +#define pv_sched_init() do {} while (0) #endif // CONFIG_PARAVIRT diff --git a/arch/arm64/include/asm/phytium_cputype.h b/arch/arm64/include/asm/phytium_cputype.h new file mode 100644 index 0000000000000000000000000000000000000000..5873ecff16a60dc846d5dd9a4d5e206a3f8f581f --- /dev/null +++ b/arch/arm64/include/asm/phytium_cputype.h @@ -0,0 +1,241 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Phytium Technology Co., Ltd. + */ +#ifndef __ASM_PHYTIUM_CPUTYPE_H +#define __ASM_PHYTIUM_CPUTYPE_H + +#include +#include + +#define SOC_ID_PE1702 0x1 +#define SOC_ID_PS17064 0x2 +#define SOC_ID_PD1904 0x3 +#define SOC_ID_PS20064 0x4 + +#define SOC_ID_PD2008 0x5 +#define SOC_ID_PS21064 0x6 +#define SOC_ID_PE220X 0x7 +#define SOC_ID_PS23064 0x8 +#define SOC_ID_PD2308 0x9 +#define SOC_ID_PS2480 0xa +#define SOC_ID_PD2408 0xb + +#define SYS_REG_VAL_PS24080 0x6 +#define SYS_REG_VAL_PS23064 0x8 + +#define SMCCC_SUCCESS 0 +#define SMCCC_FAILURE (-1) +#define FUNC_ID_GET_CPU_VERSION 0xc2000002 + +enum phyt_soc_type { + PE1702 = 1, + PS17064, + PD1904, + PS20064, + PD2008, + PS21064, + PE220X, + PS23064, + PD2308, + PS24080, + PD2408, + PS15016, + UNKNOWN_SOC +}; + +extern enum phyt_soc_type phyt_soc_type_t; + +static enum phyt_soc_type do_smccc_res(struct arm_smccc_res res) +{ + unsigned long smc_cpu_ver = res.a1; + + smc_cpu_ver = (smc_cpu_ver >> 8); + switch (smc_cpu_ver) { + case SOC_ID_PE1702: + return PE1702; + case SOC_ID_PS17064: + return PS17064; + case SOC_ID_PD1904: + return PD1904; + case SOC_ID_PS20064: + return PS20064; + case SOC_ID_PD2008: + return PD2008; + case SOC_ID_PS21064: + return PS21064; + case SOC_ID_PE220X: + return PE220X; + case SOC_ID_PS23064: + return PS23064; + case SOC_ID_PD2308: + return PD2308; + case SOC_ID_PS2480: + return PS24080; + case SOC_ID_PD2408: + return PD2408; + default: + return UNKNOWN_SOC; + } +} + +static enum phyt_soc_type do_read_sysreg(void) +{ + u32 aidr_reg_val = read_sysreg(aidr_el1); + + switch (aidr_reg_val) { + case SYS_REG_VAL_PS24080: + return PS24080; + case SYS_REG_VAL_PS23064: + return PS23064; + default: + return UNKNOWN_SOC; + } +} + +static enum phyt_soc_type do_read_mpidr(void) +{ + u32 part_id = read_cpuid_part_number(); + + switch (part_id) { + case PHYTIUM_CPU_PART_FTC303: + return PE220X; + case PHYTIUM_CPU_PART_FTC660: + return PS15016; + case PHYTIUM_CPU_PART_FTC661: + return PE1702; + case PHYTIUM_CPU_PART_FTC662: + return PS17064; + case PHYTIUM_CPU_PART_FTC663: + return PD1904; + case PHYTIUM_CPU_PART_FTC862: + return PD2408; + default: + return UNKNOWN_SOC; + } +} + +static inline bool is_phytium_soc(void) +{ + if (read_cpuid_implementor() == ARM_CPU_IMP_PHYTIUM) + return true; + return false; +} + +static inline enum phyt_soc_type phyt_read_soc_type(void) +{ + enum phyt_soc_type ctype; + struct arm_smccc_res res; + + if (!is_phytium_soc()) + return UNKNOWN_SOC; + + arm_smccc_smc(FUNC_ID_GET_CPU_VERSION, 0, 0, 0, 0, 0, 0, 0, &res); + switch (res.a0) { + case SMCCC_SUCCESS: + ctype = do_smccc_res(res); + if (ctype != UNKNOWN_SOC) + break; + fallthrough; + case SMCCC_FAILURE: + ctype = do_read_sysreg(); + if (ctype != UNKNOWN_SOC) + break; + fallthrough; + default: + ctype = do_read_mpidr(); + break; + } + return ctype; +} + +static inline void phyt_soc_type_init(void) +{ + enum phyt_soc_type ctype = phyt_read_soc_type(); + + switch (ctype) { + case PE1702: + phyt_soc_type_t = PE1702; + break; + case PS17064: + phyt_soc_type_t = PS17064; + break; + case PD1904: + phyt_soc_type_t = PD1904; + break; + case PS20064: + phyt_soc_type_t = PS20064; + break; + case PD2008: + phyt_soc_type_t = PD2008; + break; + case PS21064: + phyt_soc_type_t = PS21064; + break; + case PE220X: + phyt_soc_type_t = PE220X; + break; + case PS23064: + phyt_soc_type_t = PS23064; + break; + case PD2308: + phyt_soc_type_t = PD2308; + break; + case PS24080: + phyt_soc_type_t = PS24080; + break; + case PD2408: + phyt_soc_type_t = PD2408; + break; + case PS15016: + phyt_soc_type_t = PS15016; + break; + default: + phyt_soc_type_t = UNKNOWN_SOC; + break; + } +} + +static inline bool is_pd2408(void) +{ + + if (phyt_soc_type_t == PD2408) + return true; + return false; +} + +static inline bool is_ps23064(void) +{ + + if (phyt_soc_type_t == PS23064) + return true; + return false; +} + +static inline bool is_ps24080(void) +{ + + if (phyt_soc_type_t == PS24080) + return true; + return false; +} + +static inline bool is_pd2308(void) +{ + + if (phyt_soc_type_t == PD2308) + return true; + return false; +} + +static inline bool is_pe220x(void) +{ + + if (phyt_soc_type_t == PE220X) + return true; + return false; +} + +#endif + + diff --git a/arch/arm64/include/asm/pvsched-abi.h b/arch/arm64/include/asm/pvsched-abi.h new file mode 100644 index 0000000000000000000000000000000000000000..cc8e27dd266aeddb30cb51b7df0779083f9ebdca --- /dev/null +++ b/arch/arm64/include/asm/pvsched-abi.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2019 Huawei Technologies Co., Ltd + * Author: Zengruan Ye + */ + +#ifndef __ASM_PVSCHED_ABI_H +#define __ASM_PVSCHED_ABI_H + +#ifdef CONFIG_PARAVIRT_SCHED +struct pvsched_vcpu_state { + __le32 preempted; + /* Structure must be 64 byte aligned, pad to that size */ + u8 padding[60]; +} __packed; +#endif /* CONFIG_PARAVIRT_SCHED */ + +#endif diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h index 0525c0b089edf7b1551bc1e4a47f167c969727e9..378aea19e840896b1e186a5fcc984536e5a02c27 100644 --- a/arch/arm64/include/asm/spinlock.h +++ b/arch/arm64/include/asm/spinlock.h @@ -7,6 +7,7 @@ #include #include +#include /* See include/linux/spinlock.h */ #define smp_mb__after_spinlock() smp_mb() @@ -19,9 +20,16 @@ * https://lore.kernel.org/lkml/20200110100612.GC2827@hirez.programming.kicks-ass.net */ #define vcpu_is_preempted vcpu_is_preempted +#if defined(CONFIG_PARAVIRT) && defined(CONFIG_PARAVIRT_SCHED) +static inline bool vcpu_is_preempted(int cpu) +{ + return pv_vcpu_is_preempted(cpu); +} +#else static inline bool vcpu_is_preempted(int cpu) { return false; } +#endif /* CONFIG_PARAVIRT && CONFIG_PARAVIRT_SCHED */ #endif /* __ASM_SPINLOCK_H */ diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index b73baaf8ae47beb58dd95cf804c7eef2253117d5..1b7fb0ea9e98e257fe05369f85bdeb636fa6eeb1 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -423,6 +423,9 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma, (end - start) >= (MAX_TLBI_OPS * stride)) || pages >= MAX_TLBI_RANGE_PAGES) { flush_tlb_mm(vma->vm_mm); +#ifdef CONFIG_ARCH_PHYTIUM + mmu_notifier_arch_invalidate_secondary_tlbs(vma->vm_mm, start, end); +#endif return; } diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index f56da806d3d606bbabfc28d50e905dbd175a7724..2f8fbfd9c3af053944437e3bd84fd64890878b01 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -57,7 +57,7 @@ obj-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_ARM64_ERR_RECOV) += ras.o obj-$(CONFIG_ACPI_NUMA) += acpi_numa.o obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o -obj-$(CONFIG_PARAVIRT) += paravirt.o +obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt-spinlocks.o obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o pi/ obj-$(CONFIG_IRQ_PIPELINE) += irq_pipeline.o obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index 31deddea5cfad3eafcdb77507b402a716f25485f..dba8fcec7f33d6581848bbfd61b9e65f3413bcaa 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -29,7 +29,6 @@ #include #include -#include #include #include #include @@ -518,35 +517,3 @@ int acpi_ffh_address_space_arch_handler(acpi_integer *value, void *region_contex return ret; } #endif /* CONFIG_ACPI_FFH */ - -int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, - int *pcpu) -{ - int cpu, nid; - - cpu = acpi_map_cpuid(physid, acpi_id); - if (cpu < 0) { - pr_info("Unable to map GICC to logical cpu number\n"); - return cpu; - } - nid = acpi_get_node(handle); - if (nid != NUMA_NO_NODE) { - set_cpu_numa_node(cpu, nid); - numa_add_cpu(cpu); - } - - *pcpu = cpu; - set_cpu_present(cpu, true); - - return 0; -} -EXPORT_SYMBOL(acpi_map_cpu); - -int acpi_unmap_cpu(int cpu) -{ - set_cpu_present(cpu, false); - numa_clear_node(cpu); - - return 0; -} -EXPORT_SYMBOL(acpi_unmap_cpu); diff --git a/arch/arm64/kernel/paravirt-spinlocks.c b/arch/arm64/kernel/paravirt-spinlocks.c new file mode 100644 index 0000000000000000000000000000000000000000..f402e7e6c30186a3c93f7d6d707b219b3792b85d --- /dev/null +++ b/arch/arm64/kernel/paravirt-spinlocks.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright(c) 2019 Huawei Technologies Co., Ltd + * Author: Zengruan Ye + */ + +#ifdef CONFIG_PARAVIRT_SCHED +#include +#include +#include + +__visible bool __native_vcpu_is_preempted(int cpu) +{ + return false; +} + +DEFINE_STATIC_CALL(pv_vcpu_preempted, __native_vcpu_is_preempted); +#endif /* CONFIG_PARAVIRT_SCHED */ diff --git a/arch/arm64/kernel/paravirt.c b/arch/arm64/kernel/paravirt.c index aa718d6a9274ab06e78b6dc28f08de97a517a3ec..8a5fcb0d99f24faa02f4787aac54926e77ac683c 100644 --- a/arch/arm64/kernel/paravirt.c +++ b/arch/arm64/kernel/paravirt.c @@ -22,6 +22,7 @@ #include #include +#include #include struct static_key paravirt_steal_enabled; @@ -174,3 +175,111 @@ int __init pv_time_init(void) return 0; } + +#ifdef CONFIG_PARAVIRT_SCHED +DEFINE_PER_CPU(struct pvsched_vcpu_state, pvsched_vcpu_region) __aligned(64); +EXPORT_PER_CPU_SYMBOL(pvsched_vcpu_region); + +static bool kvm_vcpu_is_preempted(int cpu) +{ + struct pvsched_vcpu_state *reg; + u32 preempted; + + reg = &per_cpu(pvsched_vcpu_region, cpu); + if (!reg) { + pr_warn_once("PV sched enabled but not configured for cpu %d\n", + cpu); + return false; + } + + preempted = le32_to_cpu(READ_ONCE(reg->preempted)); + + return !!preempted; +} + +static int pvsched_vcpu_state_dying_cpu(unsigned int cpu) +{ + struct pvsched_vcpu_state *reg; + struct arm_smccc_res res; + + reg = this_cpu_ptr(&pvsched_vcpu_region); + if (!reg) + return -EFAULT; + + arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_SCHED_IPA_RELEASE, &res); + memset(reg, 0, sizeof(*reg)); + + return 0; +} + +static int init_pvsched_vcpu_state(unsigned int cpu) +{ + struct pvsched_vcpu_state *reg; + struct arm_smccc_res res; + + reg = this_cpu_ptr(&pvsched_vcpu_region); + if (!reg) + return -EFAULT; + + /* Pass the memory address to host via hypercall */ + arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_SCHED_IPA_INIT, + virt_to_phys(reg), &res); + + return 0; +} + +static int kvm_arm_init_pvsched(void) +{ + int ret; + + ret = cpuhp_setup_state(CPUHP_AP_ARM_KVM_PVSCHED_STARTING, + "hypervisor/arm/pvsched:starting", + init_pvsched_vcpu_state, + pvsched_vcpu_state_dying_cpu); + + if (ret < 0) { + pr_warn("PV sched init failed\n"); + return ret; + } + + return 0; +} + +static bool has_kvm_pvsched(void) +{ + struct arm_smccc_res res; + + /* To detect the presence of PV sched support we require SMCCC 1.1+ */ + if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE) + return false; + + arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, + ARM_SMCCC_HV_PV_SCHED_FEATURES, &res); + + return (res.a0 == SMCCC_RET_SUCCESS); +} + +int __init pv_sched_init(void) +{ + int ret; + + if (is_hyp_mode_available()) + return 0; + + if (!has_kvm_pvsched()) { + pr_warn("PV sched is not available\n"); + return 0; + } + + ret = kvm_arm_init_pvsched(); + if (ret) + return ret; + + static_call_update(pv_vcpu_preempted, kvm_vcpu_is_preempted); + pr_info("using PV sched preempted\n"); + + return 0; +} +early_initcall(pv_sched_init); +#endif /* CONFIG_PARAVIRT_SCHED */ + diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index 29a8e444db836a70a17fecf2794f358a5f8f3003..48ff9dc8126a9810eee8a19913e7bad7fe4125f4 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -40,8 +40,12 @@ static int cpu_psci_cpu_boot(unsigned int cpu) { phys_addr_t pa_secondary_entry = __pa_symbol(secondary_entry); int err = psci_ops.cpu_on(cpu_logical_map(cpu), pa_secondary_entry); - if (err) - pr_err("failed to boot CPU%d (%d)\n", cpu, err); + if (err) { + if (err != -EPERM) + pr_err("failed to boot CPU%d (%d)\n", cpu, err); + else + pr_err("psci feedback boot CPU%d result(%d) undefined\n", cpu, err); + } return err; } diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index 80ba3351582fe50d37ec6bef942dcb8a942ce06e..fca316e33d269da702a40ae3309252a2a83200dd 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -54,9 +54,28 @@ #include #include +#ifdef CONFIG_ARCH_PHYTIUM +#include +enum phyt_soc_type phyt_soc_type_t; +EXPORT_SYMBOL(phyt_soc_type_t); +#endif + static int num_standard_resources; static struct resource *standard_resources; +#ifdef CONFIG_ARM64_BOOTPARAM_HOTPLUG_CPU0 +static int arm64_cpu0_hotpluggable = 1; +#else +static int arm64_cpu0_hotpluggable; +static int __init arm64_enable_cpu0_hotplug(char *str) +{ + arm64_cpu0_hotpluggable = 1; + return 1; +} + +__setup("arm64_cpu0_hotplug", arm64_enable_cpu0_hotplug); +#endif + phys_addr_t __fdt_pointer __initdata; u64 mmu_enabled_at_boot __initdata; @@ -342,6 +361,9 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p) FW_BUG "Booted with MMU enabled!"); } +#ifdef CONFIG_ARCH_PHYTIUM + phyt_soc_type_init(); +#endif arm64_memblock_init(); paging_init(); @@ -393,25 +415,20 @@ static inline bool cpu_can_disable(unsigned int cpu) #ifdef CONFIG_HOTPLUG_CPU const struct cpu_operations *ops = get_cpu_ops(cpu); - if (ops && ops->cpu_can_disable) - return ops->cpu_can_disable(cpu); + if (ops && ops->cpu_can_disable) { + if (cpu == 0) + return ops->cpu_can_disable(0) && arm64_cpu0_hotpluggable; + else + return ops->cpu_can_disable(cpu); + } #endif return false; } -static int __init topology_init(void) +bool arch_cpu_is_hotpluggable(int num) { - int i; - - for_each_present_cpu(i) { - struct cpu *cpu = &per_cpu(cpu_data.cpu, i); - cpu->hotpluggable = cpu_can_disable(i); - register_cpu(cpu, i); - } - - return 0; + return cpu_can_disable(num); } -subsys_initcall(topology_init); static void dump_kernel_offset(void) { @@ -454,24 +471,3 @@ static int __init check_mmu_enabled_at_boot(void) return 0; } device_initcall_sync(check_mmu_enabled_at_boot); - -#ifdef CONFIG_HOTPLUG_CPU - -int arch_register_cpu(int num) -{ - struct cpu *cpu = &per_cpu(cpu_data.cpu, num); - - cpu->hotpluggable = 1; - return register_cpu(cpu, num); -} -EXPORT_SYMBOL(arch_register_cpu); - -void arch_unregister_cpu(int num) -{ - struct cpu *cpu = &per_cpu(cpu_data.cpu, num); - - unregister_cpu(cpu); -} -EXPORT_SYMBOL(arch_unregister_cpu); - -#endif diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 72f89ed27fae58d1fa1d549c5a5d88bb19e2fe92..435033c95f9ba6ac565f194b1114eab155b0b7e4 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -124,7 +124,8 @@ int __cpu_up(unsigned int cpu, struct task_struct *idle) /* Now bring the CPU into our world */ ret = boot_secondary(cpu, idle); if (ret) { - pr_err("CPU%u: failed to boot: %d\n", cpu, ret); + if (ret != -EPERM) + pr_err("CPU%u: failed to boot: %d\n", cpu, ret); return ret; } @@ -523,14 +524,16 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) { u64 hwid = processor->arm_mpidr; + if (!acpi_gicc_is_usable(processor)) { + pr_debug("skipping disabled CPU entry with 0x%llx MPIDR\n", hwid); + return; + } + if (hwid & ~MPIDR_HWID_BITMASK || hwid == INVALID_HWID) { pr_err("skipping CPU entry with invalid MPIDR 0x%llx\n", hwid); return; } - if (!(processor->flags & ACPI_MADT_ENABLED)) - pr_debug("disabled CPU entry with 0x%llx MPIDR\n", hwid); - if (is_mpidr_duplicate(cpu_count, hwid)) { pr_err("duplicate CPU MPIDR 0x%llx in MADT\n", hwid); return; @@ -753,13 +756,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) if (err) continue; - if (acpi_disabled) { - set_cpu_present(cpu, true); - } else { - if ((cpu_madt_gicc[cpu].flags & ACPI_MADT_ENABLED)) - set_cpu_present(cpu, true); - } - + set_cpu_present(cpu, true); numa_store_cpu_info(cpu); } } diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index c0c050e53157d9908c91fd781aa1b5d3271e4092..3e16e6c364c63b47ed74dca9184e0dee6a70d6f3 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -10,7 +10,7 @@ include $(srctree)/virt/kvm/Makefile.kvm obj-$(CONFIG_KVM) += kvm.o obj-$(CONFIG_KVM) += hyp/ -kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \ +kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o pvsched.o \ inject_fault.o va_layout.o handle_exit.o \ guest.o debug.o reset.o sys_regs.o stacktrace.o \ vgic-sys-reg-v3.o fpsimd.o pkvm.o \ diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 18413d869cca1a5e5ac2418b6dee9dcf1fe543a9..865320250efea52c6bfb5f5fbe7851ae2111f210 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -386,6 +386,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) kvm_arm_pvtime_vcpu_init(&vcpu->arch); + kvm_arm_pvsched_vcpu_init(&vcpu->arch); + vcpu->arch.hw_mmu = &vcpu->kvm->arch.mmu; err = kvm_vgic_vcpu_init(vcpu); @@ -465,6 +467,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) if (!cpumask_test_cpu(cpu, vcpu->kvm->arch.supported_cpus)) vcpu_set_on_unsupported_cpu(vcpu); + + if (kvm_arm_is_pvsched_enabled(&vcpu->arch)) + kvm_update_pvsched_preempted(vcpu, 0); } void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) @@ -480,6 +485,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) vcpu_clear_on_unsupported_cpu(vcpu); vcpu->cpu = -1; + + if (kvm_arm_is_pvsched_enabled(&vcpu->arch)) + kvm_update_pvsched_preempted(vcpu, 1); } static void __kvm_arm_vcpu_power_off(struct kvm_vcpu *vcpu) @@ -1335,6 +1343,8 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu, spin_unlock(&vcpu->arch.mp_state_lock); + kvm_arm_pvsched_vcpu_init(&vcpu->arch); + return 0; } diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index ca0bf0b92ca09ec0c9e96c97f32100bc0f9d30b2..1af8912ce92baeb66e6bad3dc7e42c1941478cdd 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -695,15 +695,29 @@ void kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot prot, kvm_pte_t *ptep) { - bool device = prot & KVM_PGTABLE_PROT_DEVICE; - kvm_pte_t attr = device ? KVM_S2_MEMATTR(pgt, DEVICE_nGnRE) : - KVM_S2_MEMATTR(pgt, NORMAL); + kvm_pte_t attr; u32 sh = KVM_PTE_LEAF_ATTR_LO_S2_SH_IS; + switch (prot & (KVM_PGTABLE_PROT_DEVICE | + KVM_PGTABLE_PROT_NORMAL_NC)) { + case KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_NORMAL_NC: + return -EINVAL; + case KVM_PGTABLE_PROT_DEVICE: + if (prot & KVM_PGTABLE_PROT_X) + return -EINVAL; + attr = KVM_S2_MEMATTR(pgt, DEVICE_nGnRE); + break; + case KVM_PGTABLE_PROT_NORMAL_NC: + if (prot & KVM_PGTABLE_PROT_X) + return -EINVAL; + attr = KVM_S2_MEMATTR(pgt, NORMAL_NC); + break; + default: + attr = KVM_S2_MEMATTR(pgt, NORMAL); + } + if (!(prot & KVM_PGTABLE_PROT_X)) attr |= KVM_PTE_LEAF_ATTR_HI_S2_XN; - else if (device) - return -EINVAL; if (prot & KVM_PGTABLE_PROT_R) attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R; @@ -1161,6 +1175,20 @@ struct stage2_attr_data { u32 level; }; +static inline bool post_migration_huge(kvm_pte_t pte, kvm_pte_t old_pte, + u32 level) +{ + /* + * Based on pagesize and PTE permissions(read, write and execute), + * determine whether it is a huge pagesize VM live migration. + */ + return (kvm_granule_size(level) > PAGE_SIZE + && (pte & KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R) + && (pte & KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W) + && stage2_pte_executable(pte) + && !stage2_pte_executable(old_pte)); +} + static int stage2_attr_walker(const struct kvm_pgtable_visit_ctx *ctx, enum kvm_pgtable_walk_flags visit) { @@ -1182,6 +1210,16 @@ static int stage2_attr_walker(const struct kvm_pgtable_visit_ctx *ctx, * set on the next access instead. */ if (data->pte != pte) { + /* + * if it's a huge page VM after live migration, other + * vCPUs do not need to refresh icache because of the + * CMO mechanism. + */ + if (read_cpuid_implementor() == ARM_CPU_IMP_PHYTIUM) { + if (post_migration_huge(pte, ctx->old, ctx->level)) + goto set_pte; + } + /* * Invalidate instruction cache before updating the guest * stage-2 PTE if we are going to add executable permission. @@ -1191,6 +1229,7 @@ static int stage2_attr_walker(const struct kvm_pgtable_visit_ctx *ctx, mm_ops->icache_inval_pou(kvm_pte_follow(pte, mm_ops), kvm_granule_size(ctx->level)); +set_pte: if (!stage2_try_set_pte(ctx, pte)) return -EAGAIN; } diff --git a/arch/arm64/kvm/hypercalls.c b/arch/arm64/kvm/hypercalls.c index 7fb4df0456dea53f9cdeccdd28c2ff47a8ce6ff3..4c9fc5df5142b1a8b531a57fe059609701dea393 100644 --- a/arch/arm64/kvm/hypercalls.c +++ b/arch/arm64/kvm/hypercalls.c @@ -332,6 +332,11 @@ int kvm_smccc_call_handler(struct kvm_vcpu *vcpu) &smccc_feat->std_hyp_bmap)) val[0] = SMCCC_RET_SUCCESS; break; +#ifdef CONFIG_PARAVIRT_SCHED + case ARM_SMCCC_HV_PV_SCHED_FEATURES: + val[0] = SMCCC_RET_SUCCESS; + break; +#endif /* CONFIG_PARAVIRT_SCHED */ } break; case ARM_SMCCC_HV_PV_TIME_FEATURES: @@ -342,6 +347,22 @@ int kvm_smccc_call_handler(struct kvm_vcpu *vcpu) if (gpa != INVALID_GPA) val[0] = gpa; break; +#ifdef CONFIG_PARAVIRT_SCHED + case ARM_SMCCC_HV_PV_SCHED_FEATURES: + val[0] = kvm_hypercall_pvsched_features(vcpu); + break; + case ARM_SMCCC_HV_PV_SCHED_IPA_INIT: + gpa = smccc_get_arg1(vcpu); + if (gpa != INVALID_GPA) { + vcpu->arch.pvsched.base = gpa; + val[0] = SMCCC_RET_SUCCESS; + } + break; + case ARM_SMCCC_HV_PV_SCHED_IPA_RELEASE: + vcpu->arch.pvsched.base = INVALID_GPA; + val[0] = SMCCC_RET_SUCCESS; + break; +#endif /* CONFIG_PARAVIRT_SCHED */ case ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID: val[0] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0; val[1] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1; diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index 482280fe22d7c59d2539b9f0e758cfc95afad0ff..6876400b342c4085af798f6709e9b6c9d348f3f0 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -1398,7 +1398,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, int ret = 0; bool write_fault, writable, force_pte = false; bool exec_fault, mte_allowed; - bool device = false; + bool device = false, vfio_allow_any_uc = false; unsigned long mmu_seq; struct kvm *kvm = vcpu->kvm; struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache; @@ -1490,6 +1490,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, gfn = fault_ipa >> PAGE_SHIFT; mte_allowed = kvm_vma_mte_allowed(vma); + vfio_allow_any_uc = vma->vm_flags & VM_ALLOW_ANY_UNCACHED; + /* Don't use the VMA after the unlock -- it may have vanished */ vma = NULL; @@ -1576,10 +1578,15 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa, if (exec_fault) prot |= KVM_PGTABLE_PROT_X; - if (device) - prot |= KVM_PGTABLE_PROT_DEVICE; - else if (cpus_have_const_cap(ARM64_HAS_CACHE_DIC)) + if (device) { + if (vfio_allow_any_uc) + prot |= KVM_PGTABLE_PROT_NORMAL_NC; + else + prot |= KVM_PGTABLE_PROT_DEVICE; + } else if (cpus_have_final_cap(ARM64_HAS_CACHE_DIC)) { prot |= KVM_PGTABLE_PROT_X; + } + /* * Under the premise of getting a FSC_PERM fault, we just need to relax @@ -2147,8 +2154,20 @@ void kvm_toggle_cache(struct kvm_vcpu *vcpu, bool was_enabled) * If switching it off, need to clean the caches. * Clean + invalidate does the trick always. */ - if (now_enabled != was_enabled) - stage2_flush_vm(vcpu->kvm); + if (now_enabled != was_enabled) { + /* + * Due to Phytium CPU's cache consistency support, + * just flush dcache on one vcpu not all vcpus in the VM. + * This can reduce the number of flush dcaches and + * improve the efficiency of SMP multi-core startup, + * especially for the large VM with hugepages. + */ + if (read_cpuid_implementor() == ARM_CPU_IMP_PHYTIUM) { + if (vcpu->vcpu_id == 0) + stage2_flush_vm(vcpu->kvm); + } else + stage2_flush_vm(vcpu->kvm); + } /* Caches are now on, stop trapping VM ops (until a S/W op) */ if (now_enabled) diff --git a/arch/arm64/kvm/pvsched.c b/arch/arm64/kvm/pvsched.c new file mode 100644 index 0000000000000000000000000000000000000000..6371dc1374523f91e1c859c8f5299cf590c7a965 --- /dev/null +++ b/arch/arm64/kvm/pvsched.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright(c) 2019 Huawei Technologies Co., Ltd + * Author: Zengruan Ye + */ + +#ifdef CONFIG_PARAVIRT_SCHED +#include +#include + +#include +#include + +void kvm_update_pvsched_preempted(struct kvm_vcpu *vcpu, u32 preempted) +{ + struct kvm *kvm = vcpu->kvm; + u64 base = vcpu->arch.pvsched.base; + u64 offset = offsetof(struct pvsched_vcpu_state, preempted); + int idx; + + if (base == INVALID_GPA) + return; + + /* + * This function is called from atomic context, so we need to + * disable page faults. + */ + pagefault_disable(); + + idx = srcu_read_lock(&kvm->srcu); + kvm_put_guest(kvm, base + offset, cpu_to_le32(preempted)); + srcu_read_unlock(&kvm->srcu, idx); + + pagefault_enable(); +} + +long kvm_hypercall_pvsched_features(struct kvm_vcpu *vcpu) +{ + u32 feature = smccc_get_arg1(vcpu); + long val = SMCCC_RET_NOT_SUPPORTED; + + switch (feature) { + case ARM_SMCCC_HV_PV_SCHED_FEATURES: + case ARM_SMCCC_HV_PV_SCHED_IPA_INIT: + case ARM_SMCCC_HV_PV_SCHED_IPA_RELEASE: + val = SMCCC_RET_SUCCESS; + break; + } + + return val; +} +#endif /* CONFIG_PARAVIRT_SCHED */ diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 370a1a7bd369dc44f9d5b6fe8c2006e007d091c9..543fa3c630d86f0a1a2456934ef5aab373e229a0 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -1253,7 +1253,6 @@ static int arm64_check_features(struct kvm_vcpu *vcpu, const struct arm64_ftr_reg *ftr_reg; const struct arm64_ftr_bits *ftrp = NULL; u32 id = reg_to_encoding(rd); - u64 writable_mask = rd->val; u64 limit = rd->reset(vcpu, rd); u64 mask = 0; @@ -1273,15 +1272,11 @@ static int arm64_check_features(struct kvm_vcpu *vcpu, for (; ftrp && ftrp->width; ftrp++) { s64 f_val, f_lim, safe_val; - u64 ftr_mask; - ftr_mask = arm64_ftr_mask(ftrp); - if ((ftr_mask & writable_mask) != ftr_mask) - continue; + mask |= arm64_ftr_mask(ftrp); f_val = arm64_ftr_value(ftrp, val); f_lim = arm64_ftr_value(ftrp, limit); - mask |= ftr_mask; if (f_val == f_lim) safe_val = f_val; diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c index 3cb101e8cb29baca75d3fd25287c9dfe932f7677..17ebc794a775dcd45f2387bbec62658ca1160d33 100644 --- a/arch/arm64/mm/dma-mapping.c +++ b/arch/arm64/mm/dma-mapping.c @@ -13,6 +13,10 @@ #include #include +#ifdef CONFIG_PSWIOTLB +#include +#endif + void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, enum dma_data_direction dir) { @@ -61,5 +65,9 @@ void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, if (iommu) iommu_setup_dma_ops(dev, dma_base, dma_base + size - 1); +#ifdef CONFIG_PSWIOTLB + pswiotlb_setup_dma_ops(dev); +#endif + xen_setup_dma_ops(dev); } diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 9fd8644a9a4c6a679f81d35da9c91fff00b48b54..5533e33074daf6f20ae16efe40c2c82298acd634 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -5,6 +5,7 @@ config LOONGARCH select ACPI select ACPI_GENERIC_GSI if ACPI select ACPI_MCFG if ACPI + select ACPI_HOTPLUG_CPU if ACPI_PROCESSOR && HOTPLUG_CPU select ACPI_PPTT if ACPI select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI select ARCH_BINFMT_ELF_STATE @@ -72,6 +73,7 @@ config LOONGARCH select GENERIC_CLOCKEVENTS select GENERIC_CMOS_UPDATE select GENERIC_CPU_AUTOPROBE + select GENERIC_CPU_DEVICES select GENERIC_ENTRY select GENERIC_GETTIMEOFDAY select GENERIC_IOREMAP if !ARCH_IOREMAP diff --git a/arch/loongarch/kernel/topology.c b/arch/loongarch/kernel/topology.c index 3fd1660066983b247d1493864a9437e831282e3e..75d5c51a7cd3def4227a3777625abecca37c8ede 100644 --- a/arch/loongarch/kernel/topology.c +++ b/arch/loongarch/kernel/topology.c @@ -10,47 +10,9 @@ #include -static DEFINE_PER_CPU(struct cpu, cpu_devices); - #ifdef CONFIG_HOTPLUG_CPU -int arch_register_cpu(int cpu) +bool arch_cpu_is_hotpluggable(int cpu) { - int ret; - struct cpu *c = &per_cpu(cpu_devices, cpu); - - c->hotpluggable = 1; - ret = register_cpu(c, cpu); - if (ret < 0) - pr_warn("register_cpu %d failed (%d)\n", cpu, ret); - - return ret; -} -EXPORT_SYMBOL(arch_register_cpu); - -void arch_unregister_cpu(int cpu) -{ - struct cpu *c = &per_cpu(cpu_devices, cpu); - - c->hotpluggable = 0; - unregister_cpu(c); + return !io_master(cpu); } -EXPORT_SYMBOL(arch_unregister_cpu); #endif - -static int __init topology_init(void) -{ - int i, ret; - - for_each_present_cpu(i) { - struct cpu *c = &per_cpu(cpu_devices, i); - - c->hotpluggable = !io_master(i); - ret = register_cpu(c, i); - if (ret < 0) - pr_warn("topology_init: register_cpu %d failed (%d)\n", i, ret); - } - - return 0; -} - -subsys_initcall(topology_init); diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 1304992232adbef36287d19ceb71e27d2860b29e..bcc8146115f91cbe5c6b8e5627207891be6b65d8 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -70,6 +70,7 @@ config RISCV select GENERIC_ARCH_TOPOLOGY select GENERIC_ATOMIC64 if !64BIT select GENERIC_CLOCKEVENTS_BROADCAST if SMP + select GENERIC_CPU_DEVICES select GENERIC_EARLY_IOREMAP select GENERIC_ENTRY select GENERIC_GETTIMEOFDAY if HAVE_GENERIC_VDSO diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index ddadee6621f0dacff5f4c1c3e52aba7110b835d0..1480791e50d68caa4800f0cf540c3887eb949b39 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -55,7 +55,6 @@ atomic_t hart_lottery __section(".sdata") #endif ; unsigned long boot_cpu_hartid; -static DEFINE_PER_CPU(struct cpu, cpu_devices); /* * Place kernel memory regions on the resource tree so that @@ -313,23 +312,10 @@ void __init setup_arch(char **cmdline_p) riscv_set_dma_cache_alignment(); } -static int __init topology_init(void) +bool arch_cpu_is_hotpluggable(int cpu) { - int i, ret; - - for_each_possible_cpu(i) { - struct cpu *cpu = &per_cpu(cpu_devices, i); - - cpu->hotpluggable = cpu_has_hotplug(i); - ret = register_cpu(cpu, i); - if (unlikely(ret)) - pr_warn("Warning: %s: register_cpu %d failed (%d)\n", - __func__, i, ret); - } - - return 0; + return cpu_has_hotplug(cpu); } -subsys_initcall(topology_init); void free_initmem(void) { diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 75f4260b59d1acf0808a67a0c9f006898120c1d0..5d4fd53b704ac8342674261ef569bb7b2203c3e3 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -62,6 +62,8 @@ config X86 # select ACPI_LEGACY_TABLES_LOOKUP if ACPI select ACPI_SYSTEM_POWER_STATES_SUPPORT if ACPI + select ACPI_HOTPLUG_CPU if ACPI_PROCESSOR && HOTPLUG_CPU + select ACPI_HOTPLUG_IGNORE_OSC if ACPI && HOTPLUG_CPU select ARCH_32BIT_OFF_T if X86_32 select ARCH_CLOCKSOURCE_INIT select ARCH_CONFIGURES_CPU_MITIGATIONS @@ -150,6 +152,7 @@ config X86 select GENERIC_CLOCKEVENTS_MIN_ADJUST select GENERIC_CMOS_UPDATE select GENERIC_CPU_AUTOPROBE + select GENERIC_CPU_DEVICES select GENERIC_CPU_VULNERABILITIES select GENERIC_EARLY_IOREMAP select GENERIC_ENTRY diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index 25050d953eee02d71cb50f585f2f7dcc7482c011..91867a6a9f8e41c2d7019af81d8b265cd98aa6b1 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -23,10 +23,6 @@ static inline void prefill_possible_map(void) {} #endif /* CONFIG_SMP */ -struct x86_cpu { - struct cpu cpu; -}; - #ifdef CONFIG_HOTPLUG_CPU extern void soft_restart_cpu(void); #endif diff --git a/arch/x86/kernel/cpu/intel_epb.c b/arch/x86/kernel/cpu/intel_epb.c index e4c3ba91321c95ad601d8e29f7439ab803c315a9..f18d35fe27a928690ed1d6c6d96a2468002c5690 100644 --- a/arch/x86/kernel/cpu/intel_epb.c +++ b/arch/x86/kernel/cpu/intel_epb.c @@ -237,4 +237,4 @@ static __init int intel_epb_init(void) cpuhp_remove_state(CPUHP_AP_X86_INTEL_EPB_ONLINE); return ret; } -subsys_initcall(intel_epb_init); +late_initcall(intel_epb_init); diff --git a/arch/x86/kernel/topology.c b/arch/x86/kernel/topology.c index 0bab0313003362ef7549bb820bb8c2b6b59259f9..d42c28b8bfd80c15c8b1814f60143cf72b79b541 100644 --- a/arch/x86/kernel/topology.c +++ b/arch/x86/kernel/topology.c @@ -35,38 +35,9 @@ #include #include -static DEFINE_PER_CPU(struct x86_cpu, cpu_devices); - #ifdef CONFIG_HOTPLUG_CPU -int arch_register_cpu(int cpu) +bool arch_cpu_is_hotpluggable(int cpu) { - struct x86_cpu *xc = per_cpu_ptr(&cpu_devices, cpu); - - xc->cpu.hotpluggable = cpu > 0; - return register_cpu(&xc->cpu, cpu); -} -EXPORT_SYMBOL(arch_register_cpu); - -void arch_unregister_cpu(int num) -{ - unregister_cpu(&per_cpu(cpu_devices, num).cpu); -} -EXPORT_SYMBOL(arch_unregister_cpu); -#else /* CONFIG_HOTPLUG_CPU */ - -int __init arch_register_cpu(int num) -{ - return register_cpu(&per_cpu(cpu_devices, num).cpu, num); + return cpu > 0; } #endif /* CONFIG_HOTPLUG_CPU */ - -static int __init topology_init(void) -{ - int i; - - for_each_present_cpu(i) - arch_register_cpu(i); - - return 0; -} -subsys_initcall(topology_init); diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 48e7d00bacf09ae346b27346f4735f245ff65dda..c4746869d67ba0854e8869511476f7cda589d3ba 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -45,13 +45,6 @@ if ACPI config ACPI_LEGACY_TABLES_LOOKUP bool -config PHYTIUM_BASE_CTRL - bool "Phytium base ctrl driver" - default y - help - This driver provides interface functions for reading and - writing phytium base controller device address. - config ARCH_MIGHT_HAVE_ACPI_PDC bool @@ -316,7 +309,15 @@ config ACPI_HOTPLUG_CPU bool depends on ACPI_PROCESSOR && HOTPLUG_CPU select ACPI_CONTAINER - default y + +config ACPI_HOTPLUG_IGNORE_OSC + bool + depends on ACPI_HOTPLUG_CPU + help + Ignore whether firmware acknowledged support for toggling the CPU + present bit in _STA. Some architectures predate the _OSC bits, so + firmware doesn't know to do this. + config ACPI_PROCESSOR_AGGREGATOR tristate "Processor Aggregator" diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index e483aa370c2ef10a53f818c4f26738496880058d..eaa09bf52f17609ffbb24fef7d7fccf12afe4024 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -42,7 +42,6 @@ acpi-y += resource.o acpi-y += acpi_processor.o acpi-y += processor_core.o acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o -acpi-$(CONFIG_PHYTIUM_BASE_CTRL) += phytium_base_ctrl.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-$(CONFIG_PCI) += pci_root.o pci_link.o pci_irq.o diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 7053f1b9fc1ddc3b6b7b2374534878a07a557216..1f886245aa0b282ce9ab65d400ee8b8a5288d101 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -182,32 +182,30 @@ static void __init acpi_pcc_cpufreq_init(void) static void __init acpi_pcc_cpufreq_init(void) {} #endif /* CONFIG_X86 */ -/* Initialization */ -#ifdef CONFIG_ACPI_HOTPLUG_CPU -int __weak acpi_map_cpu(acpi_handle handle, - phys_cpuid_t physid, u32 acpi_id, int *pcpu) +static bool acpi_processor_hotplug_present_supported(void) { - return -ENODEV; -} + if (!IS_ENABLED(CONFIG_ACPI_HOTPLUG_CPU)) + return false; -int __weak acpi_unmap_cpu(int cpu) -{ - return -ENODEV; -} + /* x86 systems pre-date the _OSC bit */ + if (IS_ENABLED(CONFIG_ACPI_HOTPLUG_IGNORE_OSC)) + return true; -int __weak arch_register_cpu(int cpu) -{ - return -ENODEV; + return osc_sb_hotplug_present_support_acked; } -void __weak arch_unregister_cpu(int cpu) {} - -static int acpi_processor_hotadd_init(struct acpi_processor *pr) +/* Initialization */ +static int acpi_processor_make_present(struct acpi_processor *pr) { unsigned long long sta; acpi_status status; int ret; + if (!acpi_processor_hotplug_present_supported()) { + pr_err_once("Changing CPU present bit is not supported\n"); + return -ENODEV; + } + if (invalid_phys_cpuid(pr->phys_id)) return -ENODEV; @@ -241,12 +239,32 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) cpu_maps_update_done(); return ret; } -#else -static inline int acpi_processor_hotadd_init(struct acpi_processor *pr) + +static int acpi_processor_make_enabled(struct acpi_processor *pr) { - return -ENODEV; + unsigned long long sta; + acpi_status status; + bool present, enabled; + + if (!acpi_has_method(pr->handle, "_STA")) + return arch_register_cpu(pr->id); + + status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) + return -ENODEV; + + present = sta & ACPI_STA_DEVICE_PRESENT; + enabled = sta & ACPI_STA_DEVICE_ENABLED; + + if (cpu_online(pr->id) && (!present || !enabled)) { + pr_err_once(FW_BUG "CPU %u online, disabled!\n", pr->id); + add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); + } else if (!present || !enabled) { + return -ENODEV; + } + + return arch_register_cpu(pr->id); } -#endif /* CONFIG_ACPI_HOTPLUG_CPU */ static int acpi_processor_get_info(struct acpi_device *device) { @@ -332,6 +350,18 @@ static int acpi_processor_get_info(struct acpi_device *device) cpufreq_add_device("acpi-cpufreq"); } + /* + * Register CPUs that are present. get_cpu_device() is used to skip + * duplicate CPU descriptions from firmware. + */ + if (!invalid_logical_cpuid(pr->id) && cpu_present(pr->id) && + !get_cpu_device(pr->id)) { + int ret = acpi_processor_make_enabled(pr); + + if (ret) + return ret; + } + /* * Extra Processor objects may be enumerated on MP systems with * less than the max # of CPUs. They should be ignored _iff @@ -341,7 +371,7 @@ static int acpi_processor_get_info(struct acpi_device *device) * because cpuid <-> apicid mapping is persistent now. */ if (invalid_logical_cpuid(pr->id) || !cpu_present(pr->id)) { - int ret = acpi_processor_hotadd_init(pr); + int ret = acpi_processor_make_present(pr); if (ret) return ret; @@ -468,14 +498,15 @@ static int acpi_processor_add(struct acpi_device *device, return result; } -#ifdef CONFIG_ACPI_HOTPLUG_CPU /* Removal */ -static void acpi_processor_remove(struct acpi_device *device) +static void acpi_processor_make_not_present(struct acpi_device *device) { struct acpi_processor *pr; - if (!device || !acpi_driver_data(device)) + if (!IS_ENABLED(CONFIG_ACPI_HOTPLUG_CPU)) { + pr_err_once("Changing CPU present bit is not supported"); return; + } pr = acpi_driver_data(device); if (pr->id >= nr_cpu_ids) @@ -512,7 +543,32 @@ static void acpi_processor_remove(struct acpi_device *device) free_cpumask_var(pr->throttling.shared_cpu_map); kfree(pr); } -#endif /* CONFIG_ACPI_HOTPLUG_CPU */ + +static void acpi_processor_post_eject(struct acpi_device *device) +{ + struct acpi_processor *pr; + unsigned long long sta; + acpi_status status; + + if (!IS_ENABLED(CONFIG_HOTPLUG_CPU) || !device) + return; + + pr = acpi_driver_data(device); + if (!pr || pr->id >= nr_cpu_ids || invalid_phys_cpuid(pr->phys_id)) + return; + + status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) + return; + + if (cpu_present(pr->id) && !(sta & ACPI_STA_DEVICE_PRESENT)) { + acpi_processor_make_not_present(device); + return; + } + + if (cpu_present(pr->id) && !(sta & ACPI_STA_DEVICE_ENABLED)) + arch_unregister_cpu(pr->id); +} #ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC bool __init processor_physically_present(acpi_handle handle) @@ -637,9 +693,7 @@ static const struct acpi_device_id processor_device_ids[] = { static struct acpi_scan_handler processor_handler = { .ids = processor_device_ids, .attach = acpi_processor_add, -#ifdef CONFIG_ACPI_HOTPLUG_CPU - .detach = acpi_processor_remove, -#endif + .post_eject = acpi_processor_post_eject, .hotplug = { .enabled = true, }, @@ -788,6 +842,25 @@ void __init acpi_processor_init(void) acpi_pcc_cpufreq_init(); } +static int __init acpi_processor_register_missing_cpus(void) +{ + int cpu; + + if (acpi_disabled) + return 0; + + for_each_online_cpu(cpu) { + if (!get_cpu_device(cpu)) { + pr_err_once(FW_BUG "CPU %u has no ACPI namespace description!\n", cpu); + add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); + arch_register_cpu(cpu); + } + } + + return 0; +} +subsys_initcall_sync(acpi_processor_register_missing_cpus); + #ifdef CONFIG_ACPI_PROCESSOR_CSTATE /** * acpi_processor_claim_cst_control - Request _CST control from the platform. diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 2a7aa5e8784ae887e01fe48ac2ee521acb17e4cf..73580a6b7132b7d7553c8fdecac9d8fd9a4e7f5e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -714,6 +714,12 @@ static bool ghes_do_proc(struct ghes *ghes, } else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) { queued = ghes_handle_arm_hw_error(gdata, sev, sync); + } else if (guid_equal(sec_type, &CPER_SEC_PHYT_ERR)) { + struct cper_sec_phyt_err *phyt = acpi_hest_get_payload(gdata); + int sec_sev; + + sec_sev = ghes_severity(gdata->error_severity); + log_phyt_err_event(phyt, sec_sev); } else { void *err = acpi_hest_get_payload(gdata); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a4aa53b7e2bb30789165145c4d1ccdea214809a3..0d2e1de143efbae90bf6733fb84322ef51001c96 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -298,6 +298,13 @@ EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed); bool osc_sb_cppc2_support_acked; +/* + * ACPI 6.? Proposed Operating System Capabilities for modifying CPU + * present/enable. + */ +bool osc_sb_hotplug_enabled_support_acked; +bool osc_sb_hotplug_present_support_acked; + static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48"; static void acpi_bus_osc_negotiate_platform_control(void) { @@ -346,6 +353,11 @@ static void acpi_bus_osc_negotiate_platform_control(void) if (!ghes_disable) capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_APEI_SUPPORT; + + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_ENABLED_SUPPORT; + if (IS_ENABLED(CONFIG_ACPI_HOTPLUG_CPU)) + capbuf[OSC_SUPPORT_DWORD] |= OSC_SB_HOTPLUG_PRESENT_SUPPORT; + if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle))) return; @@ -383,6 +395,10 @@ static void acpi_bus_osc_negotiate_platform_control(void) capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT; osc_cpc_flexible_adr_space_confirmed = capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_CPC_FLEXIBLE_ADR_SPACE; + osc_sb_hotplug_enabled_support_acked = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_HOTPLUG_ENABLED_SUPPORT; + osc_sb_hotplug_present_support_acked = + capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_HOTPLUG_PRESENT_SUPPORT; } kfree(context.ret.pointer); diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 3b4d048c49417303bf5c4451e94e6d9f3dd77e56..e3c80f3b3b573a77232bd89ec8cd59dd0812aa7d 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -313,7 +313,7 @@ int acpi_bus_init_power(struct acpi_device *device) return -EINVAL; device->power.state = ACPI_STATE_UNKNOWN; - if (!acpi_device_is_present(device)) { + if (!acpi_dev_ready_for_enumeration(device)) { device->flags.initialized = false; return -ENXIO; } diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index 6ed5e9e56be2f4ee6d35273cc6400101fa9f1131..eae6016ad2825d0c2ff5d95dd2ba9046876bb979 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -141,7 +141,7 @@ static int create_pnp_modalias(const struct acpi_device *acpi_dev, char *modalia struct acpi_hardware_id *id; /* Avoid unnecessarily loading modules for non present devices. */ - if (!acpi_device_is_present(acpi_dev)) + if (!acpi_dev_ready_for_enumeration(acpi_dev)) return 0; /* diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e5d77ee100b55c6336c341bc7d946e99eb76d1a6..115994dfefec1e9db5fc15256ffbf6a124b74e4a 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -31,9 +31,6 @@ #include #include "internal.h" -#ifdef CONFIG_ARCH_PHYTIUM -#include "phytium_base_ctrl.h" -#endif #define ACPI_EC_CLASS "embedded_controller" #define ACPI_EC_DEVICE_NAME "Embedded Controller" @@ -276,12 +273,7 @@ static bool acpi_ec_flushed(struct acpi_ec *ec) static inline u8 acpi_ec_read_status(struct acpi_ec *ec) { - u8 x; - - if (phytium_check_cpu() == true) - x = base_ctrl_readb(ec->command_addr); - else - x = inb(ec->command_addr); + u8 x = inb(ec->command_addr); ec_dbg_raw("EC_SC(R) = 0x%2.2x " "SCI_EVT=%d BURST=%d CMD=%d IBF=%d OBF=%d", @@ -296,12 +288,7 @@ static inline u8 acpi_ec_read_status(struct acpi_ec *ec) static inline u8 acpi_ec_read_data(struct acpi_ec *ec) { - u8 x; - - if (phytium_check_cpu() == true) - x = base_ctrl_readb(ec->data_addr); - else - x = inb(ec->data_addr); + u8 x = inb(ec->data_addr); ec->timestamp = jiffies; ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x); @@ -311,24 +298,14 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec) static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) { ec_dbg_raw("EC_SC(W) = 0x%2.2x", command); - - if (phytium_check_cpu() == true) - base_ctrl_writeb(ec->command_addr, command); - else - outb(command, ec->command_addr); - + outb(command, ec->command_addr); ec->timestamp = jiffies; } static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) { ec_dbg_raw("EC_DATA(W) = 0x%2.2x", data); - - if (phytium_check_cpu() == true) - base_ctrl_writeb(ec->data_addr, data); - else - outb(data, ec->data_addr); - + outb(data, ec->data_addr); ec->timestamp = jiffies; } @@ -394,17 +371,6 @@ static inline void acpi_ec_disable_gpe(struct acpi_ec *ec, bool close) } } -#ifdef CONFIG_ARCH_PHYTIUM -static void hwreduce_acpi_enable(struct acpi_ec *ec) -{ - base_ctrl_writeb(ec->command_addr, 0x86); -} -static void hwreduce_acpi_disable(struct acpi_ec *ec) -{ - base_ctrl_writeb(ec->command_addr, 0x87); -} -#endif - /* -------------------------------------------------------------------------- * Transaction Management * -------------------------------------------------------------------------- */ @@ -449,12 +415,9 @@ static void acpi_ec_unmask_events(struct acpi_ec *ec) clear_bit(EC_FLAGS_EVENTS_MASKED, &ec->flags); if (ec->gpe >= 0) acpi_ec_enable_gpe(ec, false); - else { - if (!phytium_check_cpu()) - enable_irq(ec->irq); - else if ((irq_to_desc(ec->irq))->depth > 0) - enable_irq(ec->irq); - } + else + enable_irq(ec->irq); + ec_dbg_drv("Polling disabled"); } } @@ -533,10 +496,6 @@ static inline void __acpi_ec_enable_event(struct acpi_ec *ec) * Unconditionally invoke this once after enabling the event * handling mechanism to detect the pending events. */ -#ifdef CONFIG_ARCH_PHYTIUM - if (phytium_check_cpu()) - hwreduce_acpi_enable(ec); -#endif advance_transaction(ec, false); } @@ -544,11 +503,6 @@ static inline void __acpi_ec_disable_event(struct acpi_ec *ec) { if (test_and_clear_bit(EC_FLAGS_QUERY_ENABLED, &ec->flags)) ec_log_drv("event blocked"); - -#ifdef CONFIG_ARCH_PHYTIUM - if (phytium_check_cpu()) - hwreduce_acpi_disable(ec); -#endif } /* @@ -2163,10 +2117,6 @@ static int acpi_ec_resume(struct device *dev) struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); -#ifdef CONFIG_ARCH_PHYTIUM - if (phytium_check_cpu()) - hwreduce_acpi_enable(ec); -#endif acpi_ec_enable_event(ec); return 0; } @@ -2180,7 +2130,7 @@ EXPORT_SYMBOL_GPL(acpi_ec_mark_gpe_for_wake); void acpi_ec_set_gpe_wake_mask(u8 action) { - if (!phytium_check_cpu() && pm_suspend_no_platform() && first_ec && !ec_no_wakeup) + if (pm_suspend_no_platform() && first_ec && !ec_no_wakeup) acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action); } diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 3642bd38603b1a18f55efc3c50433fe3687cb784..dec9532b35c7ee36389b9149c8d69cd4e8cce545 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -119,7 +119,6 @@ int acpi_device_setup_files(struct acpi_device *dev); void acpi_device_remove_files(struct acpi_device *dev); void acpi_device_add_finalize(struct acpi_device *device); void acpi_free_pnp_ids(struct acpi_device_pnp *pnp); -bool acpi_device_is_present(const struct acpi_device *adev); bool acpi_device_is_battery(struct acpi_device *adev); bool acpi_device_is_first_physical_node(struct acpi_device *adev, const struct device *dev); diff --git a/drivers/acpi/phytium_base_ctrl.h b/drivers/acpi/phytium_base_ctrl.h deleted file mode 100644 index 017f21e5ac6af0c36bf25736d129a805383b1ea8..0000000000000000000000000000000000000000 --- a/drivers/acpi/phytium_base_ctrl.h +++ /dev/null @@ -1,27 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * acpi/phytium_base_ctrl.h - * - * Copyright (C) 2021-2024, Phytium Technology Co., Ltd. - */ - -#define base_ctrl_INT_STATE 0x7FFFFC4 -#define base_ctrl_CLR_INT 0x7FFFFC0 - -struct phytium_base_ctrl { - struct device *dev; - void __iomem *base; - int irq; - spinlock_t lock; - u32 int_status_reg; - u32 int_clear_reg; -}; - -int phytium_base_ctrl_irq(void); -u8 base_ctrl_readb(unsigned long offset); -u32 base_ctrl_readl(unsigned long offset); -bool phytium_check_cpu(void); -int base_ctrl_writeb(unsigned long offset, u8 value); -int base_ctrl_writel(unsigned long offset, u32 value); -int base_ctrl_read_int_status(void); -void base_ctrl_write_int_clear(int val); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 7dd6dbaa98c34a171909452f8de1edfbcb0c70fc..b203cfe2855023eb1a664874d55fabcd932a1a06 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -90,7 +90,7 @@ static int map_gicc_mpidr(struct acpi_subtable_header *entry, struct acpi_madt_generic_interrupt *gicc = container_of(entry, struct acpi_madt_generic_interrupt, header); - if (!(gicc->flags & ACPI_MADT_ENABLED)) + if (!acpi_gicc_is_usable(gicc)) return -ENODEV; /* device_declaration means Device object in DSDT, in the diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 4d958a165da058c3599da352bcb397b2eb914a84..a0b03160fdedd087dbe7542e16f37078ae900f73 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1423,7 +1423,7 @@ static bool acpi_fwnode_device_is_available(const struct fwnode_handle *fwnode) if (!is_acpi_device_node(fwnode)) return false; - return acpi_device_is_present(to_acpi_device_node(fwnode)); + return acpi_dev_ready_for_enumeration(to_acpi_device_node(fwnode)); } static const void * diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index c0c5c5c58ae1e7ec093dfbf22c4b3f8bc04ed5af..6dbe4579445f150afde588197ee315441c812bcb 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -244,11 +244,85 @@ static int acpi_scan_try_to_offline(struct acpi_device *device) return 0; } +/** + * acpi_bus_trim_one() - Detach scan handlers and drivers from ACPI device + * objects. + * @adev: Root of the ACPI namespace scope to walk. + * @eject: Pointer to a bool that indicates if this was due to an + * eject-request. + * + * Must be called under acpi_scan_lock. + * If @eject points to true, clearing the device enumeration is deferred until + * acpi_bus_post_eject() is called. + */ +static int acpi_bus_trim_one(struct acpi_device *adev, void *eject) +{ + struct acpi_scan_handler *handler = adev->handler; + bool is_eject = *(bool *)eject; + + acpi_dev_for_each_child_reverse(adev, acpi_bus_trim_one, eject); + + adev->flags.match_driver = false; + if (handler) { + if (handler->detach) + handler->detach(adev); + } else { + device_release_driver(&adev->dev); + } + /* + * Most likely, the device is going away, so put it into D3cold before + * that. + */ + acpi_device_set_power(adev, ACPI_STATE_D3_COLD); + adev->flags.initialized = false; + + /* For eject this is deferred to acpi_bus_post_eject() */ + if (!is_eject) { + adev->handler = NULL; + acpi_device_clear_enumerated(adev); + } + + return 0; +} + +/** + * acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects. + * @adev: Root of the ACPI namespace scope to walk. + * + * Must be called under acpi_scan_lock. + */ +void acpi_bus_trim(struct acpi_device *adev) +{ + bool eject = false; + + acpi_bus_trim_one(adev, &eject); +} +EXPORT_SYMBOL_GPL(acpi_bus_trim); + +static int acpi_bus_post_eject(struct acpi_device *adev, void *not_used) +{ + struct acpi_scan_handler *handler = adev->handler; + + acpi_dev_for_each_child_reverse(adev, acpi_bus_post_eject, NULL); + + if (handler) { + if (handler->post_eject) + handler->post_eject(adev); + + adev->handler = NULL; + } + + acpi_device_clear_enumerated(adev); + + return 0; +} + static int acpi_scan_hot_remove(struct acpi_device *device) { acpi_handle handle = device->handle; unsigned long long sta; acpi_status status; + bool eject = true; if (device->handler && device->handler->hotplug.demand_offline) { if (!acpi_scan_is_offline(device, true)) @@ -261,7 +335,7 @@ static int acpi_scan_hot_remove(struct acpi_device *device) acpi_handle_debug(handle, "Ejecting\n"); - acpi_bus_trim(device); + acpi_bus_trim_one(device, &eject); acpi_evaluate_lck(handle, 0); /* @@ -284,15 +358,17 @@ static int acpi_scan_hot_remove(struct acpi_device *device) } else if (sta & ACPI_STA_DEVICE_ENABLED) { acpi_handle_warn(handle, "Eject incomplete - status 0x%llx\n", sta); + } else { + acpi_bus_post_eject(device, NULL); } return 0; } -static int acpi_scan_device_not_present(struct acpi_device *adev) +static int acpi_scan_device_not_enumerated(struct acpi_device *adev) { if (!acpi_device_enumerated(adev)) { - dev_warn(&adev->dev, "Still not present\n"); + dev_warn(&adev->dev, "Still not enumerated\n"); return -EALREADY; } acpi_bus_trim(adev); @@ -304,7 +380,7 @@ static int acpi_scan_device_check(struct acpi_device *adev) int error; acpi_bus_get_status(adev); - if (adev->status.present || adev->status.functional) { + if (acpi_dev_ready_for_enumeration(adev)) { /* * This function is only called for device objects for which * matching scan handlers exist. The only situation in which @@ -323,7 +399,7 @@ static int acpi_scan_device_check(struct acpi_device *adev) return error; } } else { - error = acpi_scan_device_not_present(adev); + error = acpi_scan_device_not_enumerated(adev); } return error; } @@ -334,8 +410,8 @@ static int acpi_scan_bus_check(struct acpi_device *adev, void *not_used) int error; acpi_bus_get_status(adev); - if (!(adev->status.present || adev->status.functional)) { - acpi_scan_device_not_present(adev); + if (!acpi_dev_ready_for_enumeration(adev)) { + acpi_scan_device_not_enumerated(adev); return 0; } if (handler && handler->hotplug.scan_dependent) @@ -1910,11 +1986,6 @@ static bool acpi_device_should_be_hidden(acpi_handle handle) return true; } -bool acpi_device_is_present(const struct acpi_device *adev) -{ - return adev->status.present || adev->status.functional; -} - static bool acpi_scan_handler_matching(struct acpi_scan_handler *handler, const char *idstr, const struct acpi_device_id **matchid) @@ -2379,16 +2450,25 @@ EXPORT_SYMBOL_GPL(acpi_dev_clear_dependencies); * acpi_dev_ready_for_enumeration - Check if the ACPI device is ready for enumeration * @device: Pointer to the &struct acpi_device to check * - * Check if the device is present and has no unmet dependencies. + * Check if the device is functional or enabled and has no unmet dependencies. * - * Return true if the device is ready for enumeratino. Otherwise, return false. + * Return true if the device is ready for enumeration. Otherwise, return false. */ bool acpi_dev_ready_for_enumeration(const struct acpi_device *device) { if (device->flags.honor_deps && device->dep_unmet) return false; - return acpi_device_is_present(device); + /* + * ACPI 6.5's 6.3.7 "_STA (Device Status)" allows firmware to return + * (!present && functional) for certain types of devices that should be + * enumerated. Note that the enabled bit can't be sert until the present + * bit is set. + */ + if (device->status.present) + return device->status.enabled; + else + return device->status.functional; } EXPORT_SYMBOL_GPL(acpi_dev_ready_for_enumeration); @@ -2507,44 +2587,6 @@ int acpi_bus_scan(acpi_handle handle) } EXPORT_SYMBOL(acpi_bus_scan); -static int acpi_bus_trim_one(struct acpi_device *adev, void *not_used) -{ - struct acpi_scan_handler *handler = adev->handler; - - acpi_dev_for_each_child_reverse(adev, acpi_bus_trim_one, NULL); - - adev->flags.match_driver = false; - if (handler) { - if (handler->detach) - handler->detach(adev); - - adev->handler = NULL; - } else { - device_release_driver(&adev->dev); - } - /* - * Most likely, the device is going away, so put it into D3cold before - * that. - */ - acpi_device_set_power(adev, ACPI_STATE_D3_COLD); - adev->flags.initialized = false; - acpi_device_clear_enumerated(adev); - - return 0; -} - -/** - * acpi_bus_trim - Detach scan handlers and drivers from ACPI device objects. - * @adev: Root of the ACPI namespace scope to walk. - * - * Must be called under acpi_scan_lock. - */ -void acpi_bus_trim(struct acpi_device *adev) -{ - acpi_bus_trim_one(adev, NULL); -} -EXPORT_SYMBOL_GPL(acpi_bus_trim); - int acpi_bus_register_early_device(int type) { struct acpi_device *device = NULL; diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index b741b5ba82bd6e53c216e9338cec8bfdd0dd3e27..9ccb7daee78eb5a316fddfc3f7a42b7c7198be33 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -220,20 +220,34 @@ static DECLARE_WORK(update_topology_flags_work, update_topology_flags_workfn); static DEVICE_ATTR_RO(cpu_capacity); -static int register_cpu_capacity_sysctl(void) +static int cpu_capacity_sysctl_add(unsigned int cpu) { - int i; - struct device *cpu; + struct device *cpu_dev = get_cpu_device(cpu); - for_each_possible_cpu(i) { - cpu = get_cpu_device(i); - if (!cpu) { - pr_err("%s: too early to get CPU%d device!\n", - __func__, i); - continue; - } - device_create_file(cpu, &dev_attr_cpu_capacity); - } + if (!cpu_dev) + return -ENOENT; + + device_create_file(cpu_dev, &dev_attr_cpu_capacity); + + return 0; +} + +static int cpu_capacity_sysctl_remove(unsigned int cpu) +{ + struct device *cpu_dev = get_cpu_device(cpu); + + if (!cpu_dev) + return -ENOENT; + + device_remove_file(cpu_dev, &dev_attr_cpu_capacity); + + return 0; +} + +static int register_cpu_capacity_sysctl(void) +{ + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "topology/cpu-capacity", + cpu_capacity_sysctl_add, cpu_capacity_sysctl_remove); return 0; } diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index ef427ee787a99bce2a66e7cb93d1d13046374fcf..738d66612229d62f62237db7daa4e65244f034d2 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -95,6 +95,7 @@ void unregister_cpu(struct cpu *cpu) { int logical_cpu = cpu->dev.id; + set_cpu_enabled(logical_cpu, false); unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); device_unregister(&cpu->dev); @@ -273,6 +274,13 @@ static ssize_t print_cpus_offline(struct device *dev, } static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL); +static ssize_t print_cpus_enabled(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(cpu_enabled_mask)); +} +static DEVICE_ATTR(enabled, 0444, print_cpus_enabled, NULL); + static ssize_t print_cpus_isolated(struct device *dev, struct device_attribute *attr, char *buf) { @@ -413,6 +421,7 @@ int register_cpu(struct cpu *cpu, int num) register_cpu_under_node(num, cpu_to_node(num)); dev_pm_qos_expose_latency_limit(&cpu->dev, PM_QOS_RESUME_LATENCY_NO_CONSTRAINT); + set_cpu_enabled(num, true); return 0; } @@ -494,6 +503,7 @@ static struct attribute *cpu_root_attrs[] = { &cpu_attrs[2].attr.attr, &dev_attr_kernel_max.attr, &dev_attr_offline.attr, + &dev_attr_enabled.attr, &dev_attr_isolated.attr, #ifdef CONFIG_NO_HZ_FULL &dev_attr_nohz_full.attr, @@ -525,19 +535,46 @@ bool cpu_is_hotpluggable(unsigned int cpu) EXPORT_SYMBOL_GPL(cpu_is_hotpluggable); #ifdef CONFIG_GENERIC_CPU_DEVICES -static DEFINE_PER_CPU(struct cpu, cpu_devices); -#endif +DEFINE_PER_CPU(struct cpu, cpu_devices); + +bool __weak arch_cpu_is_hotpluggable(int cpu) +{ + return false; +} + +int __weak arch_register_cpu(int cpu) +{ + struct cpu *c = &per_cpu(cpu_devices, cpu); + + c->hotpluggable = arch_cpu_is_hotpluggable(cpu); + + return register_cpu(c, cpu); +} + +#ifdef CONFIG_HOTPLUG_CPU +void __weak arch_unregister_cpu(int num) +{ + unregister_cpu(&per_cpu(cpu_devices, num)); +} +#endif /* CONFIG_HOTPLUG_CPU */ +#endif /* CONFIG_GENERIC_CPU_DEVICES */ static void __init cpu_dev_register_generic(void) { -#ifdef CONFIG_GENERIC_CPU_DEVICES - int i; + int i, ret; - for_each_possible_cpu(i) { - if (register_cpu(&per_cpu(cpu_devices, i), i)) - panic("Failed to register CPU device"); + /* + * When ACPI is enabled, CPUs are registered via + * acpi_processor_get_info(). + */ + if (!IS_ENABLED(CONFIG_GENERIC_CPU_DEVICES) || !acpi_disabled) + return; + + for_each_present_cpu(i) { + ret = arch_register_cpu(i); + if (ret) + pr_warn("register_cpu %d failed (%d)\n", i, ret); } -#endif } #ifdef CONFIG_GENERIC_CPU_VULNERABILITIES diff --git a/drivers/base/init.c b/drivers/base/init.c index 397eb9880cecb8ed39914880bf64e879a8c94b32..c4954835128cfedaef237fd356ce371115673b5d 100644 --- a/drivers/base/init.c +++ b/drivers/base/init.c @@ -35,8 +35,8 @@ void __init driver_init(void) of_core_init(); platform_bus_init(); auxiliary_bus_init(); - cpu_dev_init(); memory_dev_init(); node_dev_init(); + cpu_dev_init(); container_dev_init(); } diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index fab27506d945e91d4c16bb705398beb462f91c8b..e34e7ab4c2ea14e146b0f456dd45609c3bc1d4f2 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -142,6 +142,15 @@ config OMAP_OCP2SCP OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via OCP2SCP. +config PHYTIUM_PIO + bool "Support for ISA I/O space on Phytium SoCs" + depends on (ARM64 && ARCH_PHYTIUM) + depends on HAS_IOMEM + select INDIRECT_PIO if ARM64 + help + Driver to enable I/O access to devices attached to the Legacy IO ports + on the Phytium SoCs. + config QCOM_EBI2 bool "Qualcomm External Bus Interface 2 (EBI2)" depends on HAS_IOMEM diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index d90eed189a65b0e7ddb7731e125f641d692a6e6d..89aec3e3aea3adceae5f0c8c36b5897e25694fca 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -6,6 +6,7 @@ # Interconnect bus drivers for ARM platforms obj-$(CONFIG_ARM_CCI) += arm-cci.o obj-$(CONFIG_ARM_INTEGRATOR_LM) += arm-integrator-lm.o +obj-$(CONFIG_PHYTIUM_PIO) += phytium_pio.o obj-$(CONFIG_HISILICON_LPC) += hisi_lpc.o obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o obj-$(CONFIG_MOXTET) += moxtet.o diff --git a/drivers/bus/phytium_pio.c b/drivers/bus/phytium_pio.c new file mode 100644 index 0000000000000000000000000000000000000000..6dd42798f3d66f87df798d043e587802a79ba826 --- /dev/null +++ b/drivers/bus/phytium_pio.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium PIO driver + * + * Copyright (C) 2024 Phytium Technology Co., Ltd. All Rights Reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "phytium_pio.h" + +#define PHYT_PIO_DRIVER_NAME "phytium-pio" +#define PHYT_PIO_DRV_VER "1.1.0" + +static struct phytium_pio *ppio; + +bool check_cpu_type(void) +{ + if (read_cpuid_implementor() == ARM_CPU_IMP_PHYTIUM) + return true; + return false; +} +EXPORT_SYMBOL(check_cpu_type); + +void phytium_pio_clear_interrupt(u32 val) +{ + unsigned long flags; + + if (!ppio) + return; + spin_lock_irqsave(&ppio->lock, flags); + writew(val, ppio->membase + PHYTIUM_PIO_CLR_INT); + spin_unlock_irqrestore(&ppio->lock, flags); +} +EXPORT_SYMBOL(phytium_pio_clear_interrupt); + +int phytium_pio_get_irq(void) +{ + if (!ppio) + return -EINVAL; + return ppio->irq; +} +EXPORT_SYMBOL(phytium_pio_get_irq); + +/* + * phytium_pio_in - input the data from the target I/O port + * @hostdata: pointer to the device information relevant to PIO controller + * @addr: the target I/O port address + * @dwidth: the data width required reading from the target I/O port + * + * Return the data read back on success, -ERRNO otherwise. + */ +static u32 phytium_pio_in(void *hostdata, unsigned long addr, size_t dwidth) +{ + struct phytium_pio *pio = hostdata; + struct logic_pio_hwaddr *range = pio->range; + __le32 rd_data = 0; + unsigned long flags; + + spin_lock_irqsave(&pio->lock, flags); + switch (dwidth) { + case 1: + rd_data = readb(pio->membase + addr - range->io_start); + break; + case 2: + rd_data = readw(pio->membase + addr - range->io_start); + break; + case 4: + rd_data = readl(pio->membase + addr - range->io_start); + break; + default: + break; + } + spin_unlock_irqrestore(&pio->lock, flags); + return le32_to_cpu(rd_data); +} + +/* + * phytium_pio_out - output the data to the target I/O port + * @hostdata: pointer to the device information relevant to PIO controller + * @addr: the target I/O port address + * @val: the data to be output + * @dwidth: the data width required writing to the target I/O port + */ +static void phytium_pio_out(void *hostdata, unsigned long addr, u32 val, + size_t dwidth) +{ + struct phytium_pio *pio = hostdata; + struct logic_pio_hwaddr *range = pio->range; + unsigned long flags; + + spin_lock_irqsave(&pio->lock, flags); + switch (dwidth) { + case 1: + writeb(val, pio->membase + addr - range->io_start); + break; + case 2: + writew(val, pio->membase + addr - range->io_start); + break; + case 4: + writel(val, pio->membase + addr - range->io_start); + break; + default: + break; + } + spin_unlock_irqrestore(&pio->lock, flags); +} + +/* + * phytium_pio_ins - input the data in the buffer in multiple operations + * @hostdata: pointer to the device information relevant to PIO controller + * @pio: the target I/O port address + * @buffer: the buffer to store the data + * @dwidth: the data width required reading from the target I/O port + * @count: the number of data to be read + * + * When success, the data read back is stored in buffer pointed by buffer. + * Return 0 on success, -ERRNO otherwise. + */ +static u32 phytium_pio_ins(void *hostdata, unsigned long addr, void *buffer, + size_t dwidth, unsigned int count) +{ + struct phytium_pio *pio = hostdata; + unsigned char *buf = buffer; + + if (!pio || !buf || !count || !dwidth || dwidth > 4) + return -EINVAL; + + do { + *(u32 *)buf = phytium_pio_in(pio, addr, dwidth); + buf += dwidth; + addr += dwidth; + } while (--count); + + return 0; +} + +/* + * phytium_pio_outs - output the data in the buffer in multiple operations + * @hostdata: pointer to the device information relevant to PIO controller + * @addr: the target I/O port address + * @buffer: the buffer to store the data + * @dwidth: the data width required writing to the target I/O port + * @count: the number of data to be written + */ +static void phytium_pio_outs(void *hostdata, unsigned long addr, + const void *buffer, size_t dwidth, unsigned int count) +{ + struct phytium_pio *pio = hostdata; + const unsigned char *buf = buffer; + + if (!pio || !buf || !count || !dwidth || dwidth > 4) + return; + + do { + phytium_pio_out(pio, addr, *(u32 *)buf, dwidth); + buf += dwidth; + addr += dwidth; + } while (--count); +} + +static const struct logic_pio_host_ops phytium_pio_ops = { + .in = phytium_pio_in, + .ins = phytium_pio_ins, + .out = phytium_pio_out, + .outs = phytium_pio_outs, +}; + +static int phytium_pio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct logic_pio_hwaddr *range; + struct phytium_pio *pio; + int ret; + + pio = devm_kzalloc(dev, sizeof(*pio), GFP_KERNEL); + if (!pio) + return -ENOMEM; + pio->membase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR_OR_NULL(pio->membase)) + return PTR_ERR(pio->membase); + + pio->irq = platform_get_irq(pdev, 0); + if (pio->irq < 0) { + dev_err(dev, "no irq resource?\n"); + return pio->irq; + } + + ret = device_property_read_u32(&pdev->dev, "int_state", + &pio->int_status_reg); + if (ret) + pio->int_status_reg = PHYTIUM_PIO_INT_STATE; + + ret = device_property_read_u32(&pdev->dev, "clr_int", + &pio->int_clear_reg); + if (ret) + pio->int_clear_reg = PHYTIUM_PIO_CLR_INT; + + range = devm_kzalloc(dev, sizeof(*range), GFP_KERNEL); + if (!range) + return -ENOMEM; + range->fwnode = dev_fwnode(dev); + range->flags = LOGIC_PIO_INDIRECT; + range->size = PIO_INDIRECT_SIZE; + range->hostdata = pio; + range->ops = &phytium_pio_ops; + pio->range = range; + ppio = pio; + ret = logic_pio_register_range(range); + if (ret) { + dev_err(dev, "Failed to register logic pio range (%d)!\n", ret); + goto out; + } + spin_lock_init(&pio->lock); + dev_set_drvdata(dev, pio); +out: + return ret; +} + +static int phytium_pio_remove(struct platform_device *pdev) +{ + struct logic_pio_hwaddr *range; + + range = find_io_range_by_fwnode(pdev->dev.fwnode); + if (!range) + return -EINVAL; + + logic_pio_unregister_range(range); + + return 0; +} + +static const struct acpi_device_id phytium_pio_acpi_match[] = { + { "PHYT0007", }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, phytium_pio_acpi_match); + +static struct platform_driver phytium_pio_driver = { + .driver = { + .name = PHYT_PIO_DRIVER_NAME, + .acpi_match_table = ACPI_PTR(phytium_pio_acpi_match), + }, + .probe = phytium_pio_probe, + .remove = phytium_pio_remove, +}; + + +static int __init phytium_pio_init(void) +{ + return platform_driver_register(&phytium_pio_driver); +} +arch_initcall(phytium_pio_init); + +MODULE_DESCRIPTION("Phytium pio driver"); +MODULE_VERSION(PHYT_PIO_DRV_VER); +MODULE_LICENSE("GPL"); + diff --git a/drivers/bus/phytium_pio.h b/drivers/bus/phytium_pio.h new file mode 100644 index 0000000000000000000000000000000000000000..5e226876276deb90136b90e4104346558c7583c9 --- /dev/null +++ b/drivers/bus/phytium_pio.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * bus/phytium_pio.h + * + * Copyright (C) 2021-2025, Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_PIO_H_ +#define _PHYTIUM_PIO_H_ + +#include +#include + +#define PHYTIUM_PIO_INT_STATE 0x7FFFFC4 +#define PHYTIUM_PIO_CLR_INT 0x7FFFFC0 +#define LEGACY_ISA_SIZE 0x400 + +struct phytium_pio { + void __iomem *membase; + int irq; + spinlock_t lock; + u32 int_status_reg; + u32 int_clear_reg; + struct logic_pio_hwaddr *range; +}; + +bool check_cpu_type(void); +int phytium_pio_get_irq(void); +void phytium_pio_clear_interrupt(u32 val); + +#endif + + diff --git a/drivers/char/ipmi/bt_bmc_phytium.c b/drivers/char/ipmi/bt_bmc_phytium.c index 69567e7fe7886aae7e0f7b2d5ca462ac847b03ec..795847c472f46b5ee1130250e652132f36f8dfc1 100644 --- a/drivers/char/ipmi/bt_bmc_phytium.c +++ b/drivers/char/ipmi/bt_bmc_phytium.c @@ -427,6 +427,10 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc, if (!bt_bmc->irq) return -ENODEV; + rc = regmap_update_bits(bt_bmc->map, BT_CSR1, + BT_CSR1_IRQ_H2B | BT_CSR1_IRQ_HWRST, + 0); + rc = devm_request_irq(dev, bt_bmc->irq, bt_bmc_irq, IRQF_SHARED, DEVICE_NAME, bt_bmc); if (rc < 0) { @@ -481,6 +485,10 @@ static int bt_bmc_probe(struct platform_device *pdev) return rc; } + regmap_update_bits(bt_bmc->map, LPC_HICR0, LPC_HICR0_LPC3E, 0); + regmap_update_bits(bt_bmc->map, LPC_HICR4, LPC_BTENABLE, 0); + regmap_update_bits(bt_bmc->map, LPC_HICR2, LPC_HICR2_IBFIF3, 0); + bt_bmc_config_irq(bt_bmc, pdev); if (bt_bmc->irq) { diff --git a/drivers/char/ipmi/kcs_bmc_phytium.c b/drivers/char/ipmi/kcs_bmc_phytium.c index 32a8b3f8a0e9bb2e2923906c168ed48c0a061572..e07b3ebe2a5c86962b42945a599717f2241a8ece 100644 --- a/drivers/char/ipmi/kcs_bmc_phytium.c +++ b/drivers/char/ipmi/kcs_bmc_phytium.c @@ -121,6 +121,10 @@ static u8 phytium_kcs_inb(struct kcs_bmc_device *kcs_bmc, u32 reg) rc = regmap_read(priv->map, reg, &val); WARN(rc != 0, "regmap_read() failed: %d\n", rc); + if (reg == LPC_IDR1 || reg == LPC_IDR2 || + reg == LPC_IDR3 || reg == LPC_IDR4) + rc = regmap_read(priv->map, reg, &val); + return rc == 0 ? (u8) val : 0; } @@ -129,6 +133,10 @@ static void phytium_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data) struct phytium_kcs_bmc *priv = to_phytium_kcs_bmc(kcs_bmc); int rc; + if (reg == LPC_ODR1 || reg == LPC_ODR2 || + reg == LPC_ODR3 || reg == LPC_ODR4) + regmap_write(priv->map, reg, data); + rc = regmap_write(priv->map, reg, data); WARN(rc != 0, "regmap_write() failed: %d\n", rc); } @@ -380,14 +388,15 @@ static int phytium_kcs_probe(struct platform_device *pdev) if (rc) return rc; + phytium_kcs_enable_channel(kcs_bmc, false); + phytium_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0); + rc = phytium_kcs_config_irq(kcs_bmc, pdev); if (rc) return rc; platform_set_drvdata(pdev, priv); - phytium_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0); - phytium_kcs_enable_channel(kcs_bmc, true); rc = kcs_bmc_add_device(&priv->kcs_bmc); diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c index 46062b2a533695b0d01649166ff816efe4407b16..44ddf5dd07f67f1335517e98f800c6e6dd737127 100644 --- a/drivers/devfreq/phytium_dmu.c +++ b/drivers/devfreq/phytium_dmu.c @@ -25,8 +25,6 @@ #define DEVICE_TYPE 9 //DMU ID -#define UPDATE_INTERVAL_MS 10 - #define DMU_PMU_STRIDE 0x80000 #define AXI_MONITOR2_L 0x084 @@ -41,7 +39,7 @@ #define DDR_PMU_NOTICE_START 0x0 #define DDR_PMU_NOTICE_STOP 0x1 -#define DMUFREQ_DRIVER_VERSION "1.0.0" +#define DMUFREQ_DRIVER_VERSION "1.0.2" struct phytium_dmufreq { struct device *dev; @@ -52,6 +50,7 @@ struct phytium_dmufreq { unsigned long rate, target_rate; unsigned long bandwidth; + unsigned long single_threshold_value; int max_count; int cnt; @@ -60,9 +59,6 @@ struct phytium_dmufreq { unsigned long *read_bw; unsigned long *write_bw; - struct timer_list sampling; - struct work_struct work; - struct notifier_block nb; /*dmu to pmu operation status identification 0: not operable, 1: operable*/ @@ -91,7 +87,6 @@ static inline unsigned long dmu_read32(struct phytium_dmufreq *priv, int dmu, return readl_relaxed(priv->base[dmu] + offest); } -#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) BLOCKING_NOTIFIER_HEAD(dmu_pmu_notifier_chain); EXPORT_SYMBOL(dmu_pmu_notifier_chain); @@ -116,9 +111,7 @@ static int dmu_pmu_notifier_call(struct notifier_block *nb, unsigned long event, return NOTIFY_OK; } -#endif -static ktime_t stop; static int phytium_dmu_set_freq(struct device *dev, unsigned long freq) { @@ -262,7 +255,7 @@ struct acpi_result phytium_read_threshold_value(struct device *dev) status = acpi_evaluate_integer(handle, "BAND", NULL, &single_threshold_value); if (ACPI_FAILURE(status)) { - dev_err(dev, "Failed to evaluate BAND method: ACPI status 0x%x\n", status); + WARN_ONCE(1, "Failed to evaluate BAND method: ACPI status 0x%x\n", status); result.status = -EIO; result.value = 0; return result; @@ -277,8 +270,9 @@ static u64 phytium_dmufreq_get_real_bw(struct phytium_dmufreq *priv) { unsigned long peak_bw = 0; unsigned long sum_peak_bw = 0; + int i; - for (int i = 0; i < priv->max_count; i++) { + for (i = 0; i < priv->max_count; i++) { priv->read_bw[i] = dmu_read32(priv, i, AXI_MONITOR2_L); priv->write_bw[i] = dmu_read32(priv, i, AXI_MONITOR3_L); @@ -293,66 +287,36 @@ static u64 phytium_dmufreq_get_real_bw(struct phytium_dmufreq *priv) return peak_bw; } -static void sampling_timer_callback(struct timer_list *t) +static void polling_handle(struct phytium_dmufreq *priv) { - struct phytium_dmufreq *priv = from_timer(priv, t, sampling); - - schedule_work(&priv->work); -} - -static void sampling_work_handle(struct work_struct *work) -{ - struct phytium_dmufreq *priv = container_of(work, struct phytium_dmufreq, work); - static unsigned long load_counter; - static int count; - unsigned long current_load; + int i; /*if the pmu_reg is not active, return the last busy time(pmu_reg not work)*/ if (!priv->pmu_active) { priv->bandwidth = priv->last_bust_time; - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); return; } if (priv->cnt > 0) { - for (int i = 0; i < priv->max_count ; i++) { + for (i = 0; i < priv->max_count ; i++) { dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); dmu_write32(priv, i, TIMER_STOP, 0x1); } - current_load = phytium_dmufreq_get_real_bw(priv); - load_counter += current_load; - count += 1; + priv->bandwidth = phytium_dmufreq_get_real_bw(priv); } priv->cnt = 1; - if (ktime_after(ktime_get(), stop)) { - priv->bandwidth = div64_u64(load_counter, count); - load_counter = 0; - count = 0; - stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); - } else - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); } static int phytium_dmu_get_dev_status(struct device *dev, struct devfreq_dev_status *stat) { struct phytium_dmufreq *priv = dev_get_drvdata(dev); - struct acpi_result result; - unsigned long long single_threshold_value; - result = phytium_read_threshold_value(dev); - if (result.status) { - dev_err(dev, "Failed to get threshold value\n"); - return -EINVAL; - } - single_threshold_value = result.value; - single_threshold_value = (single_threshold_value * 1024 * 1024) / 100; + polling_handle(priv); + priv->last_bust_time = stat->busy_time = priv->bandwidth; + stat->total_time = (priv->single_threshold_value * priv->rate) / priv->freq_table[0]; - stat->busy_time = priv->bandwidth; - stat->total_time = (single_threshold_value * priv->rate) / priv->freq_table[0]; - priv->last_bust_time = priv->bandwidth; dev_dbg(dev, "busy_time = %lu, total_time = %lu,single_threshold_value = %llu\n", - stat->busy_time, stat->total_time, single_threshold_value); + stat->busy_time, stat->total_time, priv->single_threshold_value); stat->current_frequency = priv->rate; return 0; @@ -441,9 +405,6 @@ static __maybe_unused int phytium_dmufreq_suspend(struct device *dev) dev_dbg(dev, "DMU is being suspended\n"); - del_timer_sync(&priv->sampling); - flush_work(&priv->work); - ret = devfreq_suspend_device(priv->devfreq); if (ret < 0) { dev_err(dev, "failed to suspend the devfreq devices\n"); @@ -466,11 +427,6 @@ static __maybe_unused int phytium_dmufreq_resume(struct device *dev) return ret; } - if (!timer_pending(&priv->sampling)) - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); - else - dev_warn(dev, "Sampling timer already active ,skipping reinitialization\n"); - return 0; } @@ -487,6 +443,9 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) struct acpi_result result; struct resource *res; + if (max_state <= 0) + return -EINVAL; + result = phytium_dmufreq_state(dev); if (result.value == 0) { dev_err(dev, "DMUFREQ is not enabled\n"); @@ -505,8 +464,13 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) priv->max_count = result.value; - if (max_state <= 0) - return max_state; + result = phytium_read_threshold_value(dev); + if (result.status) { + dev_err(dev, "Failed to get threshold value\n"); + return -EINVAL; + } + priv->single_threshold_value = result.value; + priv->single_threshold_value = (priv->single_threshold_value * 1024 * 1024) / 10; dev->init_name = "dmufreq"; @@ -519,7 +483,6 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, priv); -#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) /* Register the notifier */ priv->nb.notifier_call = dmu_pmu_notifier_call; ret = blocking_notifier_chain_register(&dmu_pmu_notifier_chain, &priv->nb); @@ -527,14 +490,17 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) dev_err(dev, "Failed to register notifier\n"); return ret; } -#endif /* Get the base address of the DMU PMU */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - for (int i = 0; i < priv->max_count; i++) { - priv->base[i] = ioremap(res->start + i * DMU_PMU_STRIDE, resource_size(res)); - if (!priv->base[i]) - return -ENOMEM; + for (i = 0; i < priv->max_count; i++) { + resource_size_t offset = res->start + i * DMU_PMU_STRIDE; + + priv->base[i] = devm_ioremap(&pdev->dev, offset, resource_size(res)); + if (IS_ERR(priv->base[i])) { + dev_err(dev, "Ioremap failed for dmu base resource\n"); + return PTR_ERR(priv->base); + } } ret = phytium_dmu_get_freq_info(dev); @@ -575,25 +541,19 @@ static int phytium_dmufreq_probe(struct platform_device *pdev) /*Enable PMU*/ if (priv->pmu_active) { - for (int i = 0; i < priv->max_count; i++) { + for (i = 0; i < priv->max_count; i++) { dmu_write32(priv, i, AXI_MONITOR_EN, 0x101); dmu_write32(priv, i, CLEAR_EVENT, 0x1); dmu_write32(priv, i, TIMER_START, 0x1); } } - INIT_WORK(&priv->work, sampling_work_handle); - timer_setup(&priv->sampling, sampling_timer_callback, 0); - stop = ktime_add_ms(ktime_get(), priv->profile.polling_ms); - mod_timer(&priv->sampling, jiffies + msecs_to_jiffies(UPDATE_INTERVAL_MS)); - priv->dev = dev; return 0; err: dev_pm_opp_of_remove_table(dev); - kfree(priv); return ret; } @@ -601,26 +561,20 @@ static int phytium_dmufreq_remove(struct platform_device *pdev) { struct phytium_dmufreq *priv = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; + int i; - for (int i = 0; i < priv->max_count; i++) { - dmu_write32(priv, i, AXI_MONITOR_EN, 0x0); + for (i = 0; i < priv->max_count; i++) { dmu_write32(priv, i, TIMER_STOP, 0x1); + dmu_write32(priv, i, AXI_MONITOR_EN, 0x0); } -#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) /*Unregister the notifier*/ blocking_notifier_chain_unregister(&dmu_pmu_notifier_chain, &priv->nb); -#endif if (!priv->devfreq) return 0; - flush_work(&priv->work); - del_timer_sync(&priv->sampling); - dev_pm_opp_remove_all_dynamic(dev); - kfree(priv); - return 0; } @@ -635,11 +589,9 @@ MODULE_DEVICE_TABLE(acpi, phytium_dmufreq_acpi_ids); #define phytium_dmu_acpi_ids NULL #endif -#if IS_ENABLED(CONFIG_PHYT_DMU_PMU_PD2408) struct notifier_block nb = { .notifier_call = dmu_pmu_notifier_call, }; -#endif static struct platform_driver phytium_dmufreq_driver = { .probe = phytium_dmufreq_probe, @@ -655,5 +607,6 @@ module_platform_driver(phytium_dmufreq_driver); MODULE_DESCRIPTION("Phytium DDR Memory Unit frequency driver"); MODULE_AUTHOR("Li Jiayi "); +MODULE_AUTHOR("Li Mingzhe "); MODULE_LICENSE("GPL"); MODULE_VERSION(DMUFREQ_DRIVER_VERSION); diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c index 9658d9506197f158a8db8a5bd0c3d5384b6fe652..9225313eb0dc2d668be6e4f848944c9d9dfd23dd 100644 --- a/drivers/devfreq/phytium_noc.c +++ b/drivers/devfreq/phytium_noc.c @@ -14,6 +14,7 @@ #include #include #include +#include #define MINI_SIZE 0x400 #define CNT_ENABLE 0x000 @@ -26,7 +27,7 @@ #define DEBUG #define DEVICE_TYPE 7 -#define NOCFREQ_DRIVER_VERSION "1.0.0" +#define NOCFREQ_DRIVER_VERSION "1.0.2" struct phytium_nocfreq { struct device *dev; @@ -38,7 +39,7 @@ struct phytium_nocfreq { void __iomem *reg_noc; struct mutex lock; - unsigned long rate, target_rate; + unsigned long rate, target_rate, suspend_freq; unsigned int freq_count; unsigned long freq_table[]; }; @@ -297,6 +298,74 @@ static int phytium_noc_get_dev_status(struct device *dev, return 0; } +static __maybe_unused int phytium_nocfreq_suspend(struct device *dev) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(dev, "NOCfreq is being suspended\n"); + + ret = phytium_noc_get_cur_freq(dev, &priv->suspend_freq); + if (ret) + dev_warn(dev, "failed to get suspend freq\n"); + else + dev_info(dev, "saved suspend freq = %lu\n", priv->suspend_freq); + + ret = devfreq_suspend_device(priv->devfreq); + if (ret < 0) { + dev_err(dev, "failed to suspend the devfreq devices\n"); + return ret; + } + priv->devfreq->stop_polling = true; + + writel_relaxed(0x0, priv->reg_noc + CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*1+CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*2+CNT_ENABLE); + writel_relaxed(0x0, priv->reg_noc + MINI_SIZE*3+CNT_ENABLE); + + return ret; +} + +static __maybe_unused int phytium_nocfreq_resume(struct device *dev) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + int ret = 0; + + dev_dbg(dev, "NOCfreq is being resumed\n"); + + ret = devfreq_resume_device(priv->devfreq); + if (ret < 0) { + dev_err(dev, "failed to resume the devfreq devices\n"); + return ret; + } + if (!delayed_work_pending(&priv->devfreq->work) && priv->devfreq->profile->polling_ms) { + dev_info(dev, "Polling work not pending, manually restarting polling\n"); + priv->devfreq->stop_polling = true; + } + writel_relaxed(0x02, priv->reg_noc + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*1 + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*2 + WORK_STATE); + writel_relaxed(0x02, priv->reg_noc + MINI_SIZE*3 + WORK_STATE); + + writel_relaxed(0x3f, priv->reg_noc + CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*1+CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*2+CNT_ENABLE); + writel_relaxed(0x3f, priv->reg_noc + MINI_SIZE*3+CNT_ENABLE); + + if (priv->suspend_freq) { + ret = phytium_noc_set_freq(dev, priv->suspend_freq); + if (ret < 0) + dev_warn(dev, "failed to restore suspend freq %lu\n", priv->suspend_freq); + else { + dev_info(dev, "restored suspend freq = %lu\n", priv->suspend_freq); + priv->rate = priv->suspend_freq; + } + } + return ret; +} + +static SIMPLE_DEV_PM_OPS(phytium_nocfreq_pm, phytium_nocfreq_suspend, + phytium_nocfreq_resume); static int phytium_nocfreq_probe(struct platform_device *pdev) { @@ -379,7 +448,6 @@ static int phytium_nocfreq_probe(struct platform_device *pdev) err: dev_pm_opp_of_remove_table(dev); - kfree(priv); return ret; } @@ -404,13 +472,10 @@ static int phytium_nocfreq_remove(struct platform_device *pdev) if (ret) dev_warn(dev, "failed to restore NOC frequency: %d\n", ret); - iounmap(priv->reg_noc); - if (!priv->devfreq) return 0; dev_pm_opp_remove_all_dynamic(dev); - kfree(priv); return 0; } @@ -427,6 +492,7 @@ static struct platform_driver phytium_nocfreq_driver = { .remove = phytium_nocfreq_remove, .driver = { .name = "phytium_nocfreq", + .pm = &phytium_nocfreq_pm, .acpi_match_table = ACPI_PTR(phytium_noc_acpi_ids), .suppress_bind_attrs = true, }, diff --git a/drivers/edac/phytium_edac.c b/drivers/edac/phytium_edac.c index 00a5134f02d60c667edc7208eaf015b25da3531c..e01b7dee8c5ba5c9895b5cff7d253f5a92eed235 100644 --- a/drivers/edac/phytium_edac.c +++ b/drivers/edac/phytium_edac.c @@ -14,6 +14,7 @@ #include #include #include "edac_module.h" +#include #define EDAC_MOD_STR "phytium_edac" @@ -36,21 +37,21 @@ #define CORRECTED_ERROR 0 #define UNCORRECTED_ERROR 1 -#define MAX_ERR_GROUP 3 +#define EDAC_DRIVER_VERSION "1.1.2" -#define EDAC_DRIVER_VERSION "1.1.1" +struct ras_error_info { + u32 index; + u32 error_type; + const char *error_str; +}; struct phytium_edac { struct device *dev; void __iomem **ras_base; struct dentry *dfs; struct edac_device_ctl_info *edac_dev; -}; - -struct ras_error_info { - u32 index; - u32 error_type; - const char *error_str; + int num_err_group; + const struct ras_error_info **error_info; }; /* error severity definition */ @@ -128,8 +129,182 @@ static const struct ras_error_info pe220x_ras_peu_error[] = { { 5, CORRECTED_ERROR, "axi_r_rsp_error" }, }; +/* pd2208 error */ +static const struct ras_error_info pd2208_ras_err[] = { + {0, CORRECTED_ERROR, "lmu0_ras_ecc_corrected_err"}, + {1, UNCORRECTED_ERROR, "lmu0_ras_ecc_uncorrected_err"}, + {2, CORRECTED_ERROR, "lmu1_ras_ecc_corrected_err"}, + {3, UNCORRECTED_ERROR, "lmu1_ras_ecc_uncorrected_err"}, + {4, CORRECTED_ERROR, "sram_corrected_err"}, + {5, UNCORRECTED_ERROR, "sram_uncorrected_err"}, + {6, UNCORRECTED_ERROR, "qspi_ras_addr_err"}, + {7, UNCORRECTED_ERROR, "qspi_ras_pstrb_err"}, + {8, UNCORRECTED_ERROR, "intreq_err"}, + {9, UNCORRECTED_ERROR, "gic_axim_err"}, + {10, UNCORRECTED_ERROR, "gic_ecc_fatal"}, + {11, UNCORRECTED_ERROR, "lsd_lbc_ras_err"}, + {12, UNCORRECTED_ERROR, "nEXTERRIRQ_cluster0"}, + {13, UNCORRECTED_ERROR, "nINTERRIRQ_cluster0"}, + {14, UNCORRECTED_ERROR, "nEXTERRIRQ_cluster1"}, + {15, UNCORRECTED_ERROR, "nINTERRIRQ_cluster1"}, + {16, UNCORRECTED_ERROR, "nEXTERRIRQ_cluster2"}, + {17, UNCORRECTED_ERROR, "nINTERRIRQ_cluster2"}, + {18, UNCORRECTED_ERROR, "nEXTERRIRQ_cluster3"}, + {19, UNCORRECTED_ERROR, "nINTERRIRQ_cluster3"}, + {20, CORRECTED_ERROR, "lbc_ecc_corrected_err"}, + {21, UNCORRECTED_ERROR, "lbc_ecc_uncorrected_err"}, +}; + +static const struct ras_error_info pd2208_ras_sram_err[] = { + {0, CORRECTED_ERROR, "scp_sram_corrected_err"}, + {1, UNCORRECTED_ERROR, "scp_sram_uncorrected_err"}, + {2, CORRECTED_ERROR, "scp_sharemem_corrected_err"}, + {3, UNCORRECTED_ERROR, "scp_sharemem_uncorrected_err"}, + {4, CORRECTED_ERROR, "wr_cmd_buf_corrected_err"}, + {5, UNCORRECTED_ERROR, "wr_cmd_buf_uncorrected_err"}, + {6, CORRECTED_ERROR, "rd_dat_buf_corrected_err"}, + {7, UNCORRECTED_ERROR, "rd_dat_buf_uncorrected_err"}, + {8, CORRECTED_ERROR, "wr_cmd_buf_corrected_err"}, + {9, UNCORRECTED_ERROR, "wr_cmd_buf_uncorrected_err"}, + {10, CORRECTED_ERROR, "wr_dat_buf_corrected_err"}, + {11, UNCORRECTED_ERROR, "wr_dat_buf_uncorrected_err"}, + {12, CORRECTED_ERROR, "drtrch0_corrected_err"}, + {13, UNCORRECTED_ERROR, "drtrch0_uncorrected_err"}, + {14, CORRECTED_ERROR, "drtrch0_corrected_err"}, + {15, UNCORRECTED_ERROR, "drtrch0_uncorrected_err"}, + {16, CORRECTED_ERROR, "dmac_corrected_err"}, + {17, UNCORRECTED_ERROR, "dmac_uncorrected_err"}, + {18, CORRECTED_ERROR, "rmram0_corrected_err"}, + {19, UNCORRECTED_ERROR, "rmram0_uncorrected_err"}, + {20, CORRECTED_ERROR, "rmram1_corrected_err"}, + {21, UNCORRECTED_ERROR, "rmram1_uncorrected_err"}, + {22, CORRECTED_ERROR, "rmram2_corrected_err"}, + {23, UNCORRECTED_ERROR, "rmram2_uncorrected_err"}, + {24, CORRECTED_ERROR, "rmram3_corrected_err"}, + {25, UNCORRECTED_ERROR, "rmram3_uncorrected_err"}, + {26, CORRECTED_ERROR, "gmactx0_corrected_err"}, + {27, UNCORRECTED_ERROR, "gmactx0_uncorrected_err"}, + {28, CORRECTED_ERROR, "gmactx1_corrected_err"}, + {29, UNCORRECTED_ERROR, "gmactx1_uncorrected_err"}, + {30, CORRECTED_ERROR, "gmactx2_corrected_err"}, + {31, UNCORRECTED_ERROR, "gmactx2_uncorrected_err"}, + {32, CORRECTED_ERROR, "gmactx3_corrected_err"}, + {33, UNCORRECTED_ERROR, "gmactx3_uncorrected_err"}, +}; + +static const struct ras_error_info pd2208_ras_peu_sram0_err[] = { + {0, CORRECTED_ERROR, "c0p2a_corrected_err"}, + {1, UNCORRECTED_ERROR, "c0p2a_uncorrected_err"}, + {2, CORRECTED_ERROR, "c0a2p_corrected_err"}, + {3, UNCORRECTED_ERROR, "c0a2p_uncorrected_err"}, + {4, CORRECTED_ERROR, "c0rxbuf0_corrected_err"}, + {5, UNCORRECTED_ERROR, "c0rxbuf0_uncorrected_err"}, + {6, CORRECTED_ERROR, "c0rxbuf1_corrected_err"}, + {7, UNCORRECTED_ERROR, "c0rxbuf1 _uncorrected_err"}, + {8, CORRECTED_ERROR, "c0rxbuf2_corrected_err"}, + {9, UNCORRECTED_ERROR, "c0rxbuf2 _uncorrected_err"}, + {10, CORRECTED_ERROR, "c0rxbuf3_corrected_err"}, + {11, UNCORRECTED_ERROR, "c0rxbuf3 _uncorrected_err"}, + {12, CORRECTED_ERROR, "c0txbuf0_corrected_err"}, + {13, UNCORRECTED_ERROR, "c0txbuf0_uncorrected_err"}, + {14, CORRECTED_ERROR, "c0txbuf1_corrected_err"}, + {15, UNCORRECTED_ERROR, "c0txbuf1 _uncorrected_err"}, + {16, CORRECTED_ERROR, "c0txbuf2_corrected_err"}, + {17, UNCORRECTED_ERROR, "c0txbuf2 _uncorrected_err"}, + {18, CORRECTED_ERROR, "c0txbuf3_corrected_err"}, + {19, UNCORRECTED_ERROR, "c0txbuf3 _uncorrected_err"}, + {20, CORRECTED_ERROR, "c1p2a_corrected_err"}, + {21, UNCORRECTED_ERROR, "c1p2a_uncorrected_err"}, + {22, CORRECTED_ERROR, "c1a2p_corrected_err"}, + {23, UNCORRECTED_ERROR, "c1a2p_uncorrected_err"}, + {24, CORRECTED_ERROR, "c1rxbuf0_corrected_err"}, + {25, UNCORRECTED_ERROR, "c1rxbuf0_uncorrected_err"}, + {26, CORRECTED_ERROR, "c1rxbuf1_corrected_err"}, + {27, UNCORRECTED_ERROR, "c1rxbuf1 _uncorrected_err"}, + {28, CORRECTED_ERROR, "c1rxbuf2_corrected_err"}, + {29, UNCORRECTED_ERROR, "c1rxbuf2 _uncorrected_err"}, + {30, CORRECTED_ERROR, "c1rxbuf3_corrected_err"}, + {31, UNCORRECTED_ERROR, "c1rxbuf3 _uncorrected_err"}, + {32, CORRECTED_ERROR, "c1txbuf0_corrected_err"}, + {33, UNCORRECTED_ERROR, "c1txbuf0_uncorrected_err"}, + {34, CORRECTED_ERROR, "c1txbuf1_corrected_err"}, + {35, UNCORRECTED_ERROR, "c1txbuf1 _uncorrected_err"}, + {36, CORRECTED_ERROR, "c1txbuf2_corrected_err"}, + {37, UNCORRECTED_ERROR, "c1txbuf2 _uncorrected_err"}, + {38, CORRECTED_ERROR, "c1txbuf3_corrected_err"}, + {39, UNCORRECTED_ERROR, "c1txbuf3 _uncorrected_err"}, + {40, CORRECTED_ERROR, "c2p2a_corrected_err"}, + {41, UNCORRECTED_ERROR, "c2p2a_uncorrected_err"}, + {42, CORRECTED_ERROR, "c2a2p_corrected_err"}, + {43, UNCORRECTED_ERROR, "c2a2p_uncorrected_err"}, + {44, CORRECTED_ERROR, "c2rxbuf0_corrected_err"}, + {45, UNCORRECTED_ERROR, "c2rxbuf0_uncorrected_err"}, + {46, CORRECTED_ERROR, "c2rxbuf1_corrected_err"}, + {47, UNCORRECTED_ERROR, "c2rxbuf1 _uncorrected_err"}, + {48, CORRECTED_ERROR, "c2rxbuf2_corrected_err"}, + {49, UNCORRECTED_ERROR, "c2rxbuf2 _uncorrected_err"}, + {50, CORRECTED_ERROR, "c2rxbuf3_corrected_err"}, + {51, UNCORRECTED_ERROR, "c2rxbuf3 _uncorrected_err"}, + {52, CORRECTED_ERROR, "c2txbuf0_corrected_err"}, + {53, UNCORRECTED_ERROR, "c2txbuf0_uncorrected_err"}, + {54, CORRECTED_ERROR, "c2txbuf1_corrected_err"}, + {55, UNCORRECTED_ERROR, "c2txbuf1 _uncorrected_err"}, +}; + +static const struct ras_error_info pd2208_ras_peu_sram1_err[] = { + {0, CORRECTED_ERROR, "c2txbuf2_corrected_err"}, + {1, UNCORRECTED_ERROR, "c2txbuf2_uncorrected_err"}, + {2, CORRECTED_ERROR, "c2txbuf3_corrected_err"}, + {3, UNCORRECTED_ERROR, "c2txbuf3_uncorrected_err"}, + {4, CORRECTED_ERROR, "phy0_sram0_corrected_err"}, + {5, UNCORRECTED_ERROR, "phy0_sram0_uncorrected_err"}, + {6, CORRECTED_ERROR, "phy0_sram1_corrected_err"}, + {7, UNCORRECTED_ERROR, "phy0_sram1 _uncorrected_err"}, + {8, CORRECTED_ERROR, "phy0_sram2_corrected_err"}, + {9, UNCORRECTED_ERROR, "phy0_sram2 _uncorrected_err"}, + {10, CORRECTED_ERROR, "phy0_sram3_corrected_err"}, + {11, UNCORRECTED_ERROR, "phy0_sram3 _uncorrected_err"}, + {12, CORRECTED_ERROR, "phy1_sram0_corrected_err"}, + {13, UNCORRECTED_ERROR, "phy1_sram0_uncorrected_err"}, + {14, CORRECTED_ERROR, "mac0_rxdpram_corrected_err"}, + {15, UNCORRECTED_ERROR, "mac0_rxdpram _uncorrected_err"}, + {16, CORRECTED_ERROR, "mac0_txdpram_corrected_err"}, + {17, UNCORRECTED_ERROR, "mac0_txdpram _uncorrected_err"}, + {18, CORRECTED_ERROR, "mac1_rxdpram_corrected_err"}, + {19, UNCORRECTED_ERROR, "mac1_rxdpram _uncorrected_err"}, + {20, CORRECTED_ERROR, "mac1_txdpram_corrected_err"}, + {21, UNCORRECTED_ERROR, "mac1_txdpram _uncorrected_err"}, +}; + +static const struct ras_error_info pd2208_ras_peu_base_err[] = { + {0, UNCORRECTED_ERROR, "pio_rd_addr_error"}, + {1, UNCORRECTED_ERROR, "pio_rd_timeout"}, + {2, UNCORRECTED_ERROR, "pio_wr_addr_error"}, + {3, UNCORRECTED_ERROR, "pio_wr_timeout"}, + {4, CORRECTED_ERROR, "axi_b_rsp_error"}, + {5, UNCORRECTED_ERROR, "axi_r_rsp_error"}, + {6, UNCORRECTED_ERROR, "mac0_asf_trans_to_err"}, + {7, UNCORRECTED_ERROR, "mac0_asf_protocol_err"}, + {8, UNCORRECTED_ERROR, "mac0_asf_nonfatal_int"}, + {9, UNCORRECTED_ERROR, "mac0_asf_fatal_int"}, + {10, UNCORRECTED_ERROR, "mac1_asf_trans_to_err"}, + {11, UNCORRECTED_ERROR, "mac1_asf_protocol_err"}, + {12, UNCORRECTED_ERROR, "mac1_asf_nonfatal_int"}, + {13, UNCORRECTED_ERROR, "mac1_asf_fatal_int"}, +}; + static const struct ras_error_info *pe220x_ras_error[] = { - pe220x_ras_soc_error, pe220x_ras_peu_psu_error, pe220x_ras_peu_error + pe220x_ras_soc_error, + pe220x_ras_peu_psu_error, + pe220x_ras_peu_error, +}; + +static const struct ras_error_info *pd2208_ras_error[] = { + pd2208_ras_err, + pd2208_ras_sram_err, + pd2208_ras_peu_sram0_err, + pd2208_ras_peu_sram1_err, + pd2208_ras_peu_base_err, }; static inline unsigned int get_error_num(const struct phytium_edac *edac, @@ -146,11 +321,11 @@ static inline void phytium_ras_setup(const struct phytium_edac *edac) { u64 val = 0; unsigned int i = 0; + /* * enable error report and generate interrupt for corrected error event - * first error record owned by node present the node configuration */ - for (i = 0; i < MAX_ERR_GROUP; i++) { + for (i = 0; i < edac->num_err_group; i++) { val = readq(edac->ras_base[i] + ERR_CTLR(0)); val |= CTLR_ED | CTLR_UI | CTLR_CFI; writeq(val, edac->ras_base[i] + ERR_CTLR(0)); @@ -190,7 +365,7 @@ static ssize_t phytium_edac_inject_ctrl_write(struct file *filp, goto out; res = kstrtouint(tmp, 0, &error_group); - if (res || error_group >= MAX_ERR_GROUP) { + if (res || error_group >= edac->num_err_group) { dev_err(edac->dev, "invalid error group parameters"); goto out; } @@ -207,10 +382,10 @@ static ssize_t phytium_edac_inject_ctrl_write(struct file *filp, goto out; } - dev_dbg(edac->dev, "inject group%d, error_id: %d\n", + dev_dbg(edac->dev, "inject group: %d, error_id: %d\n", error_group, error_id); - if (pe220x_ras_error[error_group][error_id].error_type + if (edac->error_info[error_group][error_id].error_type == CORRECTED_ERROR) { writeq(MISC0_CEC(0xFF), edac->ras_base[error_group] + ERR_MISC0(error_id)); @@ -295,7 +470,7 @@ static int get_error_id(struct phytium_edac *edac, int *error_id, int err_id = 0; /* Iterate over the ras node to check error status */ - for (i = 0; i < MAX_ERR_GROUP; i++) { + for (i = 0; i < edac->num_err_group; i++) { error_num = get_error_num(edac, i); error_bit = readq(edac->ras_base[i] + ERR_GSR); for (err_id = 0; err_id < error_num; err_id++) { @@ -311,7 +486,7 @@ static int get_error_id(struct phytium_edac *edac, int *error_id, } } - if (i >= MAX_ERR_GROUP) { + if (i >= edac->num_err_group) { ret = -1; dev_warn(edac->dev, "no error detect.\n"); } @@ -323,7 +498,12 @@ static void phytium_edac_error_report(struct phytium_edac *edac, const int error_id, const int error_group) { const struct ras_error_info *err_info = - pe220x_ras_error[error_group]; + edac->error_info[error_group]; + + /* ignore pe220x soc_err id 40~43 */ + if ((err_info == pe220x_ras_soc_error) && + (error_id >= 40) && (error_id <= 43)) + return; if (err_info[error_id].error_type == UNCORRECTED_ERROR) { edac_printk(KERN_CRIT, EDAC_MOD_STR, "uncorrected error: %s\n", @@ -398,14 +578,23 @@ static int phytium_edac_probe(struct platform_device *pdev) edac->dev = &pdev->dev; platform_set_drvdata(pdev, edac); - edac->ras_base = devm_kcalloc(&pdev->dev, 3, + edac->error_info = + (const struct ras_error_info **)of_device_get_match_data(&pdev->dev); + + edac->num_err_group = of_address_count(pdev->dev.of_node); + if (edac->num_err_group <= 0) { + dev_err(&pdev->dev, "can't get error group count"); + goto out; + } + + edac->ras_base = devm_kcalloc(&pdev->dev, edac->num_err_group, sizeof(*edac->ras_base), GFP_KERNEL); if (!edac->ras_base) { return -ENOMEM; goto out; } - for (i = 0; i < MAX_ERR_GROUP; i++) { + for (i = 0; i < edac->num_err_group; i++) { res = platform_get_resource(pdev, IORESOURCE_MEM, i); edac->ras_base[i] = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(edac->ras_base[i])) { @@ -463,7 +652,10 @@ static int phytium_edac_remove(struct platform_device *pdev) } static const struct of_device_id phytium_edac_of_match[] = { - { .compatible = "phytium,pe220x-edac" }, + { .compatible = "phytium,pe220x-edac", + .data = pe220x_ras_error }, + { .compatible = "phytium,pd2208-edac", + .data = pd2208_ras_error }, {}, }; MODULE_DEVICE_TABLE(of, phytium_edac_of_match); diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index 35c37f667781c7071c714aef274e68dbddca026b..9f203034e110e0bbc367ec2a0c8217ebdf98bc71 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -26,6 +26,10 @@ #include #include "cper_cxl.h" +#ifndef PHYT_ERR_OTHER +#define PHYT_ERR_OTHER 0xff +#endif + /* * CPER record ID need to be unique even after reboot, because record * ID is used as index for ERST storage, while CPER records from @@ -502,6 +506,36 @@ static void cper_print_fw_err(const char *pfx, print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, buf, length, true); } +static const char * const phyt_err_type_strs[] = { + "Core", + "L3C", + "LSD", + "SMMU", + "GIC", + "DDR", + "PCIE", + "C2C", +}; + +static void cper_print_phyt_err(const char *pfx, + const struct cper_sec_phyt_err *phyt) +{ + if (phyt->type == PHYT_ERR_OTHER) + pr_err("%sPhytium Error Record Type: %s\n", pfx, "Other"); + else + pr_err("%sPhytium Error Record Type: %s\n", pfx, + phyt->type < ARRAY_SIZE(phyt_err_type_strs) ? + phyt_err_type_strs[phyt->type] : "unknown"); + pr_err("%sSubtype: %d\n", pfx, phyt->subtype); + pr_err("%sID: 0x%x\n", pfx, phyt->id); + pr_err("%sError Status: 0x%x\n", pfx, phyt->error_status); + pr_err("%sPhysical Address: 0x%llx\n", pfx, phyt->phys_address); + pr_err("%sMISC0: 0x%llx\n", pfx, phyt->misc0); + pr_err("%sMISC1: 0x%llx\n", pfx, phyt->misc1); + pr_err("%sMISC2: 0x%llx\n", pfx, phyt->misc2); + pr_err("%sMISC3: 0x%llx\n", pfx, phyt->misc3); +} + static void cper_print_tstamp(const char *pfx, struct acpi_hest_generic_data_v300 *gdata) { @@ -607,6 +641,14 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata cper_print_prot_err(newpfx, prot_err); else goto err_section_too_small; + } else if (guid_equal(sec_type, &CPER_SEC_PHYT_ERR)) { + struct cper_sec_phyt_err *phyt_err = acpi_hest_get_payload(gdata); + + pr_err("%ssection_type: Phytium Error Record\n", newpfx); + if (gdata->error_data_length >= sizeof(*phyt_err)) + cper_print_phyt_err(newpfx, phyt_err); + else + goto err_section_too_small; } else { const void *err = acpi_hest_get_payload(gdata); diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c index 2328ca58bba61fdb677ac20a1a7447882cd0cf22..eb5fde61d08be923dcb4fe867112364f649f220b 100644 --- a/drivers/firmware/psci/psci.c +++ b/drivers/firmware/psci/psci.c @@ -28,7 +28,9 @@ #include #include #include - +#ifdef CONFIG_ARCH_PHYTIUM +#include +#endif /* * While a 64-bit OS can make calls with SMC32 calling conventions, for some * calls it is necessary to use SMC64 to pass or return 64-bit values. @@ -495,6 +497,31 @@ int psci_cpu_suspend_enter(u32 state) } #endif +#ifdef CONFIG_ARCH_PHYTIUM +static int phytium_psci_system_suspend(unsigned long state) +{ + phys_addr_t pa_cpu_resume = __pa_symbol(cpu_resume); + + if (state == PM_SUSPEND_STANDBY) + return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), + pa_cpu_resume, 0x544600c2, 0); + else + return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), + pa_cpu_resume, 0, 0); +} + +static int phytium_psci_system_suspend_enter(suspend_state_t state) +{ + return cpu_suspend(state, phytium_psci_system_suspend); +} + + +static const struct platform_suspend_ops phytium_psci_suspend_ops = { + .valid = phytium_suspend_valid_mem, + .enter = phytium_psci_system_suspend_enter, +}; +#endif + static int psci_system_suspend(unsigned long unused) { int err; @@ -535,7 +562,14 @@ static void __init psci_init_system_suspend(void) ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND)); if (ret != PSCI_RET_NOT_SUPPORTED) +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pd2408()) + suspend_set_ops(&phytium_psci_suspend_ops); + else + suspend_set_ops(&psci_suspend_ops); +#else suspend_set_ops(&psci_suspend_ops); +#endif } static void __init psci_init_cpu_suspend(void) diff --git a/drivers/gpio/gpio-phytium-core.c b/drivers/gpio/gpio-phytium-core.c index 33928c4af8eb5ebea3821a5d8ce16ba237ded7c8..d6fed899649ce6719cdd4b92ae06c0d51d04cf8f 100644 --- a/drivers/gpio/gpio-phytium-core.c +++ b/drivers/gpio/gpio-phytium-core.c @@ -385,6 +385,7 @@ int phytium_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) struct phytium_gpio *gpio = gpiochip_get_data(gc); struct phytium_gpio_ctx *ctx = &gpio->ctx; irq_hw_number_t bit = irqd_to_hwirq(d); + unsigned long flags; int ret; if (gpio->irq[bit]) @@ -395,6 +396,8 @@ int phytium_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) if (ret < 0) dev_err(gc->parent, "set gpio irq wake failed!\n"); + raw_spin_lock_irqsave(&gpio->lock, flags); + if (enable) { ctx->wake_en |= BIT(bit); if (gpio->is_resuming == 1) { @@ -404,6 +407,8 @@ int phytium_gpio_irq_set_wake(struct irq_data *d, unsigned int enable) } else ctx->wake_en &= ~BIT(bit); + raw_spin_unlock_irqrestore(&gpio->lock, flags); + return 0; } EXPORT_SYMBOL_GPL(phytium_gpio_irq_set_wake); diff --git a/drivers/gpio/gpio-phytium-core.h b/drivers/gpio/gpio-phytium-core.h index 739ddc18a0c779e338fbd330276a34d73dc8adcf..8af0e4808f03b4b9ab157d68c3af6303b5950f6f 100644 --- a/drivers/gpio/gpio-phytium-core.h +++ b/drivers/gpio/gpio-phytium-core.h @@ -32,8 +32,9 @@ #define NGPIO_DEFAULT 8 #define NGPIO_MAX 32 #define GPIO_PORT_STRIDE (GPIO_EXT_PORTB - GPIO_EXT_PORTA) +#define GPIO_CLEAR_IRQ 0xffffffff -#define PHYTIUM_GPIO_DRIVER_VERSION "1.1.1" +#define PHYTIUM_GPIO_DRIVER_VERSION "1.1.2" struct pin_loc { unsigned int port; diff --git a/drivers/gpio/gpio-phytium-pci.c b/drivers/gpio/gpio-phytium-pci.c index be4670936b104abc802b9266c52b6d35050f097c..e3074342d1e5b2172698ed83ebbc15824c42560b 100644 --- a/drivers/gpio/gpio-phytium-pci.c +++ b/drivers/gpio/gpio-phytium-pci.c @@ -74,7 +74,7 @@ static int phytium_gpio_pci_probe(struct pci_dev *pdev, const struct pci_device_ raw_spin_lock_init(&gpio->lock); writel(0, gpio->regs + GPIO_INTEN); - writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + writel(GPIO_CLEAR_IRQ, gpio->regs + GPIO_PORTA_EOI); gpio->gc.base = -1; gpio->gc.get_direction = phytium_gpio_get_direction; @@ -90,7 +90,6 @@ static int phytium_gpio_pci_probe(struct pci_dev *pdev, const struct pci_device_ girq = &gpio->gc.irq; girq->handler = handle_bad_irq; girq->default_type = IRQ_TYPE_NONE; - gpio->is_resuming = 0; girq->num_parents = 1; girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, @@ -171,7 +170,7 @@ static int phytium_gpio_pci_resume(struct device *dev) writel(gpio->ctx.int_polarity, gpio->regs + GPIO_INT_POLARITY); writel(gpio->ctx.debounce, gpio->regs + GPIO_DEBOUNCE); - writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + writel(GPIO_CLEAR_IRQ, gpio->regs + GPIO_PORTA_EOI); writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); gpio->is_resuming = 0; diff --git a/drivers/gpio/gpio-phytium-platform.c b/drivers/gpio/gpio-phytium-platform.c index 364136fb1d9b277f72fa7684ba2c9d801226e925..f3e121341cabd7bba510734e1dc263368395be64 100644 --- a/drivers/gpio/gpio-phytium-platform.c +++ b/drivers/gpio/gpio-phytium-platform.c @@ -93,7 +93,7 @@ static int phytium_gpio_probe(struct platform_device *pdev) raw_spin_lock_init(&gpio->lock); writel(0, gpio->regs + GPIO_INTEN); - writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + writel(GPIO_CLEAR_IRQ, gpio->regs + GPIO_PORTA_EOI); gpio->gc.base = -1; gpio->gc.get_direction = phytium_gpio_get_direction; @@ -109,7 +109,6 @@ static int phytium_gpio_probe(struct platform_device *pdev) girq = &gpio->gc.irq; girq->handler = handle_bad_irq; girq->default_type = IRQ_TYPE_NONE; - gpio->is_resuming = 0; for (irq_count = 0; irq_count < platform_irq_count(pdev); irq_count++) { gpio->irq[irq_count] = -ENXIO; @@ -187,7 +186,7 @@ static int phytium_gpio_resume(struct device *dev) writel(gpio->ctx.int_polarity, gpio->regs + GPIO_INT_POLARITY); writel(gpio->ctx.debounce, gpio->regs + GPIO_DEBOUNCE); - writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + writel(GPIO_CLEAR_IRQ, gpio->regs + GPIO_PORTA_EOI); writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); gpio->is_resuming = 0; diff --git a/drivers/gpu/drm/phytium/phytium_dp.c b/drivers/gpu/drm/phytium/phytium_dp.c index da3757541d7bb3d5d8a734207b710c9a9faf4fb0..b9277c2fb23d89b7af9f54828e40a832c804410a 100644 --- a/drivers/gpu/drm/phytium/phytium_dp.c +++ b/drivers/gpu/drm/phytium/phytium_dp.c @@ -1896,8 +1896,11 @@ void phytium_dp_hpd_work_func(struct work_struct *work) drm_dbg_kms(dev, "running encoder hotplug work functions\n"); drm_connector_list_iter_begin(dev, &conn_iter); drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->force) + if (connector->force) { + connector->force = 0; + changed = true; continue; + } old_status = connector->status; connector->status = drm_helper_probe_detect(connector, NULL, false); if (old_status != connector->status) { @@ -2227,6 +2230,9 @@ phytium_encoder_mode_valid(struct drm_encoder *encoder, const struct drm_display break; } + if (phytium_dp->connector.force == DRM_FORCE_ON) + return MODE_OK; + if ((display_info->color_formats & DRM_COLOR_FORMAT_RGB444) == 0) { DRM_DEBUG_KMS("not support color_format(%d)\n", display_info->color_formats); display_info->color_formats = DRM_COLOR_FORMAT_RGB444; diff --git a/drivers/gpu/drm/phytium/phytium_gem.c b/drivers/gpu/drm/phytium/phytium_gem.c index 8234b6b6e8cf128d5165a435cf7af06f0c851b0f..30e60e64bb43ab62d7b9ddd187db791dbe52adc5 100644 --- a/drivers/gpu/drm/phytium/phytium_gem.c +++ b/drivers/gpu/drm/phytium/phytium_gem.c @@ -432,6 +432,8 @@ struct phytium_gem_object *phytium_gem_create_object(struct drm_device *dev, uns goto failed_object_init; } + phytium_gem_obj->base.funcs = &phytium_drm_gem_object_funcs; + if (priv->support_memory_type & (MEMORY_TYPE_VRAM_WC | MEMORY_TYPE_VRAM_DEVICE)) { ret = phytium_memory_pool_alloc(priv, &phytium_gem_obj->vaddr, &phytium_gem_obj->phys_addr, size); @@ -475,8 +477,6 @@ struct phytium_gem_object *phytium_gem_create_object(struct drm_device *dev, uns goto failed_dma_alloc; } - phytium_gem_obj->base.funcs = &phytium_drm_gem_object_funcs; - phytium_gem_obj->size = size; list_add_tail(&phytium_gem_obj->list, &priv->gem_list_head); DRM_DEBUG_KMS("phytium_gem_obj iova:0x%pa size:0x%lx\n", diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c index 1d00ba706f27ea215d0916e5f929d23a1ee58681..1eb8bedd151abe6f33e88ceeddd4e647bc29f559 100644 --- a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-common.c @@ -20,7 +20,7 @@ #include "i2c-phyt-core.h" #define FT_LOG_LINE_MAX_LEN 400 -#define FT_LOG_MAX_SIZE 8192 +#define FT_LOG_MAX_SIZE 32768 static void i2c_phyt_send_msg(struct i2c_phyt_dev *dev, struct phyt_msg_info *set_msg, bool complete_flag); @@ -32,6 +32,11 @@ static char *i2c_ft_abort_sources[] = { [FT_I2C_INT_ERR] = "Interrupt error", [FT_I2C_BLOCK_SIZE] = "smbus block error", [FT_I2C_INVALID_ADDR] = "slave address invalid", + [FT_I2C_TRANS_PACKET_FAIL] = "Packet transfer fail", + [FT_I2C_PACKET_NOT_START] = "Packet not start", + [FT_I2C_PARA_ERR] = "Para error", + [FT_I2C_INTR_XFER_INIT] = "Intr mode xfer init", + [FT_I2C_TRANS_WAIT] = "Trans wait", [FT_I2C_CHECK_STATUS_ERR] = "Uncomplete status", }; @@ -183,7 +188,7 @@ int i2c_phyt_malloc_log_mem(struct i2c_phyt_dev *dev) reg = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_DEBUG); phy_addr = ((reg & FT_I2C_LOG_ADDR_MASK) >> FT_I2C_LOG_ADDR_LOW_SHIFT) << FT_I2C_LOG_ADDR_SHIFT; - dev->log_size = ((reg & FT_I2C_LOG_SIZE_MASK) >> FT_I2C_LOG_SIZE_LOW_SHIFT) * 1024; + dev->log_size = ((reg & FT_I2C_LOG_SIZE_MASK) >> FT_I2C_LOG_SIZE_LOW_SHIFT) * 4096; dev->log_addr = devm_ioremap(dev->dev, phy_addr, dev->log_size); @@ -293,6 +298,7 @@ void i2c_phyt_default_cfg(struct i2c_phyt_dev *dev, struct phyt_msg_info i2c_mng_msg; memset(&i2c_mng_msg, 0, sizeof(i2c_mng_msg)); + memset(dev->rx_shmem_addr, 0xFF, sizeof(struct phyt_msg_info)); i2c_phyt_common_set_cmd(dev, &i2c_mng_msg, PHYTI2C_MSG_CMD_DEFAULT, PHYTI2C_MSG_CMD_DEFAULT_RESUME); @@ -370,9 +376,12 @@ int i2c_phyt_check_status(struct i2c_phyt_dev *dev, if (msg->head.status0 != FT_I2C_MSG_COMPLETE_UNKNOW) { if (!dev->mng.is_need_check || ((msg->head.status0 == FT_I2C_MSG_COMPLETE_OK) && - (msg->head.status1 == FT_I2C_SUCCESS))) { + ((msg->head.status1 == FT_I2C_SUCCESS) || + (msg->head.status1 == FT_I2C_TRANS_WAIT)))) { dev->abort_source = 0; result = FT_I2C_SUCCESS; + if (msg->head.status1 == FT_I2C_TRANS_WAIT) + dev->flags = FT_I2C_TRANS_WAIT; } else { i2c_phyt_show_log(dev); dev->abort_source = 1 << msg->head.status1; diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h index 8c44e505868e297118f3ea9fff91496df95769a7..7197992b5872fa5040360f6dc89b2c81d33fae88 100644 --- a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-core.h @@ -14,7 +14,7 @@ #include #include -#define I2C_PHYTIUM_V2_DRV_VERSION "1.0.2" +#define I2C_PHYTIUM_V2_DRV_VERSION "1.0.3" #define FT_I2C_MSG_UNIT_SIZE 10 #define FT_I2C_DATA_RESV_LEN 2 @@ -38,9 +38,9 @@ #define FT_I2C_REGFILE_HEARTBIT_VAL BIT(2) #define FT_I2C_LOG_SIZE_LOW_SHIFT 4 #define FT_I2C_LOG_SIZE_MASK GENMASK(7, FT_I2C_LOG_SIZE_LOW_SHIFT) -#define FT_I2C_LOG_ADDR_SHIFT 10 +#define FT_I2C_LOG_ADDR_SHIFT 12 #define FT_I2C_LOG_ADDR_LOW_SHIFT 8 -#define FT_I2C_LOG_ADDR_MASK GENMASK(29, FT_I2C_LOG_ADDR_LOW_SHIFT) +#define FT_I2C_LOG_ADDR_MASK GENMASK(27, FT_I2C_LOG_ADDR_LOW_SHIFT) #define FT_I2C_LOG_ADDR_LOCK_VAL BIT(31) #define FT_I2C_LOG_ADDR_LOG_FLAG BIT(3) #define FT_I2C_LOG_ADDR_LOG_ALIVE BIT(1) @@ -267,6 +267,10 @@ enum phyti2c_status_code { FT_I2C_BLOCK_SIZE, FT_I2C_INVALID_ADDR, FT_I2C_TRANS_PACKET_FAIL, + FT_I2C_PACKET_NOT_START, + FT_I2C_PARA_ERR, + FT_I2C_INTR_XFER_INIT, + FT_I2C_TRANS_WAIT, /*The RV result must put above*/ FT_I2C_RUNNING, FT_I2C_CHECK_STATUS_ERR @@ -389,6 +393,7 @@ struct i2c_phyt_dev { u32 master_cfg; u32 slave_cfg; u32 functionality; + spinlock_t i2c_lock; u8 real_index[FT_I2C_SINGLE_FRAME_CNT]; struct i2c_timings timings; @@ -509,4 +514,5 @@ void i2c_phyt_notify_rv(struct i2c_phyt_dev *dev, bool need_check); int i2c_phyt_check_status(struct i2c_phyt_dev *dev, struct phyt_msg_info *msg); void i2c_phyt_set_int_interrupt(struct i2c_phyt_dev *dev, u32 is_enable, u32 intr_mask); +void i2c_phyt_trig_rv_intr(struct i2c_phyt_dev *dev); #endif diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c index 8b205ae7458c5e15506671e1ee2c0112e6937646..a4bf85dfcfff7ee9a1b8b7e10122926968fbe403 100644 --- a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-master.c @@ -345,9 +345,11 @@ static int i2c_phyt_master_xfer(struct i2c_adapter *adapter, { struct i2c_phyt_dev *dev = i2c_get_adapdata(adapter); int ret; + unsigned long flags; pm_runtime_get_sync(dev->dev); + spin_lock_irqsave(&dev->i2c_lock, flags); dev->msgs = msgs; dev->msgs_num = num; dev->msg_err = 0; @@ -356,12 +358,13 @@ static int i2c_phyt_master_xfer(struct i2c_adapter *adapter, dev->mng.tx_cmd_cnt = 0; dev->mng.cur_cmd_cnt = 0; dev->mng.is_last_frame = false; + dev->flags = ~FT_I2C_TRANS_WAIT; i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_HEAD, 0); i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_TAIL, 0); i2c_phyt_master_xfer_single_frame(dev); - + spin_unlock_irqrestore(&dev->i2c_lock, flags); ret = i2c_phyt_check_result(dev); if (!ret) { ret = num; @@ -369,7 +372,8 @@ static int i2c_phyt_master_xfer(struct i2c_adapter *adapter, if (dev->abort_source != FT_I2C_SUCCESS) { i2c_phyt_handle_tx_abort(dev); if ((dev->abort_source & BIT(FT_I2C_TIMEOUT)) || - (dev->abort_source & BIT(FT_I2C_TRANS_PACKET_FAIL))) { + (dev->abort_source & BIT(FT_I2C_TRANS_PACKET_FAIL)) || + (dev->abort_source & BIT(FT_I2C_INTR_XFER_INIT))) { i2c_phyt_set_module_en( dev, FT_I2C_ADAPTER_MODULE_RESET); i2c_phyt_check_result(dev); @@ -447,10 +451,12 @@ static irqreturn_t i2c_phyt_master_regfile_isr(int this_irq, void *dev_id) u32 stat, head, tail; int ret; + spin_lock(&dev->i2c_lock); stat = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_RV2AP_INTR_STAT); if (!(stat & FT_I2C_RV2AP_INTR_BIT4)) { dev_warn(dev->dev, "unexpect i2c_phyt regfile intr, stat:%#x\n", stat); + spin_unlock(&dev->i2c_lock); return IRQ_NONE; } @@ -458,6 +464,7 @@ static irqreturn_t i2c_phyt_master_regfile_isr(int this_irq, void *dev_id) if (!dev->mng.tx_ring_cnt) { dev_err(dev->dev, "tx_ring_cnt is zero\n"); + spin_unlock(&dev->i2c_lock); return IRQ_NONE; } head = i2c_phyt_read_reg(dev, FT_I2C_REGFILE_TX_HEAD) % dev->mng.tx_ring_cnt; @@ -465,10 +472,10 @@ static irqreturn_t i2c_phyt_master_regfile_isr(int this_irq, void *dev_id) do { tail++; tail %= dev->mng.tx_ring_cnt; - if (dev->complete_flag) { - if (rx_msg->head.cmd_type == PHYTI2C_MSG_CMD_REPORT) - goto done; + if (rx_msg->head.cmd_type == PHYTI2C_MSG_CMD_REPORT) + i2c_phyt_master_isr_handle(dev); + if (dev->complete_flag) { ret = i2c_phyt_master_handle(dev); if (ret == FT_I2C_RUNNING) continue; @@ -478,12 +485,17 @@ static irqreturn_t i2c_phyt_master_regfile_isr(int this_irq, void *dev_id) i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_HEAD, 0); i2c_phyt_write_reg(dev, FT_I2C_REGFILE_TX_TAIL, 0); complete(&dev->cmd_complete); + spin_unlock(&dev->i2c_lock); return IRQ_HANDLED; } } while (tail != head); -done: - i2c_phyt_master_isr_handle(dev); + if (dev->flags == FT_I2C_TRANS_WAIT) { + dev->flags = ~FT_I2C_TRANS_WAIT; + i2c_phyt_trig_rv_intr(dev); + } + + spin_unlock(&dev->i2c_lock); return IRQ_HANDLED; } diff --git a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-platform.c b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-platform.c index 45ad303f2875a6ff835360f09da15844424b0edd..4ff35cbf69e7f85eb96d0f54ebfa7ecff87594cf 100644 --- a/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-platform.c +++ b/drivers/i2c/busses/phytium_i2c_v2/i2c-phyt-platform.c @@ -305,6 +305,7 @@ static int i2c_phyt_plat_probe(struct platform_device *pdev) dev->mng.tx_ring_cnt * sizeof(struct phyt_msg_info); dev->dev = &pdev->dev; + spin_lock_init(&dev->i2c_lock); platform_set_drvdata(pdev, dev); ret = i2c_phyt_malloc_log_mem(dev); diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h index 8dfb23df58f1212fbfd0abc2fb5dbb5b772fc4a6..64590b86eb37ff3079ed8b5c8dd4c2ad76180206 100644 --- a/drivers/input/serio/i8042-io.h +++ b/drivers/input/serio/i8042-io.h @@ -2,9 +2,7 @@ #ifndef _I8042_IO_H #define _I8042_IO_H -#ifdef CONFIG_ARCH_PHYTIUM -#include "../../acpi/phytium_base_ctrl.h" -#endif + /* * Names. */ @@ -44,34 +42,22 @@ extern int of_i8042_aux_irq; static inline int i8042_read_data(void) { - if (phytium_check_cpu() == true) - return base_ctrl_readb(I8042_DATA_REG); - else - return inb(I8042_DATA_REG); + return inb(I8042_DATA_REG); } static inline int i8042_read_status(void) { - if (phytium_check_cpu() == true) - return base_ctrl_readb(I8042_STATUS_REG); - else - return inb(I8042_STATUS_REG); + return inb(I8042_STATUS_REG); } static inline void i8042_write_data(int val) { - if (phytium_check_cpu() == true) - base_ctrl_writeb(I8042_DATA_REG, val); - else - outb(val, I8042_DATA_REG); + outb(val, I8042_DATA_REG); } static inline void i8042_write_command(int val) { - if (phytium_check_cpu() == true) - base_ctrl_writeb(I8042_COMMAND_REG, val); - else - outb(val, I8042_COMMAND_REG); + outb(val, I8042_COMMAND_REG); } static inline int i8042_platform_init(void) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index feff507a3810eb46c4ce8b6867e5c05f0a02a446..f7b09c0befa242bf4c30820daead3c5faa6715f6 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -22,7 +22,9 @@ #include #include #include - +#if defined(CONFIG_PHYTIUM_PIO) +#include "../../bus/phytium_pio.h" +#endif #include MODULE_AUTHOR("Vojtech Pavlik "); @@ -535,10 +537,10 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) int ret = 1; spin_lock_irqsave(&i8042_lock, flags); - - if (phytium_check_cpu() == true) - base_ctrl_write_int_clear(0x0); - +#if defined(CONFIG_PHYTIUM_PIO) + if (check_cpu_type() == true) + phytium_pio_clear_interrupt(0x0); +#endif str = i8042_read_status(); if (unlikely(~str & I8042_STR_OBF)) { spin_unlock_irqrestore(&i8042_lock, flags); @@ -1460,7 +1462,7 @@ static int i8042_setup_aux(void) int error; int i; - if (!phytium_check_cpu() && i8042_check_aux()) + if (i8042_check_aux()) return -ENODEV; if (i8042_nomux || i8042_check_mux()) { @@ -1477,19 +1479,16 @@ static int i8042_setup_aux(void) aux_enable = i8042_enable_mux_ports; } - if (!phytium_check_cpu()) { - error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, - "i8042", i8042_platform_device); - if (error) - goto err_free_ports; - } + error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, + "i8042", i8042_platform_device); + if (error) + goto err_free_ports; error = aux_enable(); if (error) goto err_free_irq; - if (!phytium_check_cpu()) - i8042_aux_irq_registered = true; + i8042_aux_irq_registered = true; return 0; err_free_irq: @@ -1499,25 +1498,53 @@ static int i8042_setup_aux(void) return error; } -static int i8042_setup_kbd(void) +#if defined(CONFIG_PHYTIUM_PIO) +static int phytium_i8042_setup_kbd(void) { int error; + int kbd_irq_num; + if (i8042_nokbd) { + i8042_register_ports(); + return 0; + } error = i8042_create_kbd_port(); if (error) return error; - if (phytium_check_cpu() == true) { - error = phytium_base_ctrl_irq(); - if (error < 0) - goto err_free_port; - - error = devm_request_irq(&i8042_platform_device->dev, error, - i8042_interrupt, IRQF_SHARED, "i8042", i8042_platform_device); - } else { - error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, - "i8042", i8042_platform_device); + kbd_irq_num = phytium_pio_get_irq(); + if (kbd_irq_num < 0) { + error = -EINVAL; + goto err_free_port; } + error = devm_request_irq(&i8042_platform_device->dev, kbd_irq_num, + i8042_interrupt, IRQF_SHARED, "i8042", i8042_platform_device); + if (error) + goto err_free_port; + error = i8042_enable_kbd_port(); + if (error) + goto err_free_port; + + i8042_register_ports(); + return 0; + + err_free_port: + i8042_free_kbd_port(); + i8042_controller_reset(false); + + return error; +} +#endif +static int i8042_setup_kbd(void) +{ + int error; + + error = i8042_create_kbd_port(); + if (error) + return error; + + error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, + "i8042", i8042_platform_device); if (error) goto err_free_port; @@ -1576,7 +1603,10 @@ static int i8042_probe(struct platform_device *dev) if (i8042_dritek) i8042_dritek_enable(); #endif - +#if defined(CONFIG_PHYTIUM_PIO) + if (check_cpu_type() == true) + return phytium_i8042_setup_kbd(); +#endif if (!i8042_noaux) { error = i8042_setup_aux(); if (error && error != -ENODEV && error != -EBUSY) @@ -1633,8 +1663,8 @@ static int __init i8042_init(void) int err; dbg_init(); - - if (!phytium_check_cpu()) { +#if defined(CONFIG_PHYTIUM_PIO) + if (!check_cpu_type()) { err = i8042_platform_init(); if (err) return (err == -ENODEV) ? 0 : err; @@ -1643,6 +1673,16 @@ static int __init i8042_init(void) if (err) goto err_platform_exit; } +#else + err = i8042_platform_init(); + if (err) + return (err == -ENODEV) ? 0 : err; + + err = i8042_controller_check(); + if (err) + goto err_platform_exit; + +#endif /* Set this before creating the dev to allow i8042_command to work right away */ i8042_present = true; diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index aeb888096456acc92026068bf38f520383d9cdd4..d4888c7ef32958c1a51badc1d398b855b063678f 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -1613,6 +1613,17 @@ static irqreturn_t arm_smmu_evtq_thread(int irq, void *dev) ret = arm_smmu_handle_evt(smmu, evt); if (!ret || !__ratelimit(&rs)) continue; +#ifdef CONFIG_ARCH_PHYTIUM + if (read_cpuid_id() == MIDR_PHYTIUM_FTC862 && + read_sysreg_s(SYS_AIDR_EL1) == PHYTIUM_CPU_SOCID_PS24080) { + u8 type = FIELD_GET(EVTQ_0_ID, evt[0]); + u64 addr = FIELD_GET(EVTQ_2_ADDR, evt[2]); + + if (type == EVT_ID_TRANSLATION_FAULT && + addr == TRANSLATE_INVALID_ADDR) + continue; + } +#endif dev_info(smmu->dev, "event 0x%02x received:\n", id); for (i = 0; i < ARRAY_SIZE(evt); ++i) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index 07f2b0941d1d1451a1d7b9b3affad200bd6acdd0..88c9cbee4dc354b24ffa4fed4346e125593b7e64 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -426,6 +426,8 @@ #define MSI_IOVA_BASE 0x8000000 #define MSI_IOVA_LENGTH 0x100000 +#define TRANSLATE_INVALID_ADDR 0x0 + enum pri_resp { PRI_RESP_DENY = 0, PRI_RESP_FAIL = 1, diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 04a757e4c6f1f75989bc572920206413f91c135f..b97bb1509da9ad7d1dbe23e32e1286050348e799 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -2387,12 +2387,25 @@ gic_acpi_parse_madt_gicc(union acpi_subtable_headers *header, (struct acpi_madt_generic_interrupt *)header; u32 reg = readl_relaxed(acpi_data.dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK; u32 size = reg == GIC_PIDR2_ARCH_GICv4 ? SZ_64K * 4 : SZ_64K * 2; + int cpu = get_cpu_for_acpi_id(gicc->uid); void __iomem *redist_base; - /* GICC entry which has !ACPI_MADT_ENABLED is not unusable so skip */ - if (!(gicc->flags & ACPI_MADT_ENABLED)) + if (!acpi_gicc_is_usable(gicc)) return 0; + /* + * Capable but disabled CPUs can be brought online later. What about + * the redistributor? ACPI doesn't want to say! + * Virtual hotplug systems can use the MADT's "always-on" GICR entries. + * Otherwise, prevent such CPUs from being brought online. + */ + if (!(gicc->flags & ACPI_MADT_ENABLED)) { + pr_warn_once("CPU %u inaccessible: can't brought\n", cpu); + set_cpu_present(cpu, false); + set_cpu_possible(cpu, false); + return 0; + } + redist_base = ioremap(gicc->gicr_base_address, size); if (!redist_base) return -ENOMEM; @@ -2438,21 +2451,15 @@ static int __init gic_acpi_match_gicc(union acpi_subtable_headers *header, /* * If GICC is enabled and has valid gicr base address, then it means - * GICR base is presented via GICC + * GICR base is presented via GICC. The redistributor is only known to + * be accessible if the GICC is marked as enabled. If this bit is not + * set, we'd need to add the redistributor at runtime, which isn't + * supported. */ - if ((gicc->flags & ACPI_MADT_ENABLED) && gicc->gicr_base_address) { + if (gicc->flags & ACPI_MADT_ENABLED && gicc->gicr_base_address) acpi_data.enabled_rdists++; - return 0; - } - /* - * It's perfectly valid firmware can pass disabled GICC entry, driver - * should not treat as errors, skip the entry instead of probe fail. - */ - if (!(gicc->flags & ACPI_MADT_ENABLED)) - return 0; - - return -ENODEV; + return 0; } static int __init gic_acpi_count_gicr_regions(void) @@ -2508,8 +2515,7 @@ static int __init gic_acpi_parse_virt_madt_gicc(union acpi_subtable_headers *hea int maint_irq_mode; static int first_madt = true; - /* Skip unusable CPUs */ - if (!(gicc->flags & ACPI_MADT_ENABLED)) + if (!acpi_gicc_is_usable(gicc)) return 0; maint_irq_mode = (gicc->flags & ACPI_MADT_VGIC_IRQ_MODE) ? diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index a36a8868bb5bc165c0a963fc5f4f3367d287421b..08e90e564b41cd732a40684980648f737a63ce59 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -573,6 +573,17 @@ config TPS6594_PFSM This driver can also be built as a module. If so, the module will be called tps6594-pfsm. +config PHYTIUM_POWER_MONITOR + tristate "Phytium power monitor support" + depends on ARCH_PHYTIUM + default m + help + Select this option will enable the power monitor driver in + Phytium platform, allowing user to obtain power information + in SoC through procfs, such as power consumption, voltage. + + If unsure, say N. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 16480d807ba21ea63acc7418e1669d4bad083469..25fb543d122e1682a914c5c0cdabcc5f71642c3f 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -68,3 +68,4 @@ obj-$(CONFIG_TMR_MANAGER) += xilinx_tmr_manager.o obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o obj-$(CONFIG_TPS6594_ESM) += tps6594-esm.o obj-$(CONFIG_TPS6594_PFSM) += tps6594-pfsm.o +obj-$(CONFIG_PHYTIUM_POWER_MONITOR) += phytium_pwrmon.o diff --git a/drivers/misc/phytium-snoop-ctrl.c b/drivers/misc/phytium-snoop-ctrl.c index aedb15bbfa5f5c3abbe0815451853736c51d0475..8812299ebfb8a05105c51f10591232d5a21919a5 100644 --- a/drivers/misc/phytium-snoop-ctrl.c +++ b/drivers/misc/phytium-snoop-ctrl.c @@ -275,6 +275,12 @@ static int phytium_snoop_ctrl_probe(struct platform_device *pdev) return -ENODEV; } + regmap_update_bits(snoop_ctrl->regmap, snp_enable_reg, + snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en, 0); + + regmap_update_bits(snoop_ctrl->regmap, snp_enable_reg, + snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en, 0); + rc = phytium_snoop_ctrl_config_irq(snoop_ctrl, pdev); if (rc) return rc; diff --git a/drivers/misc/phytium_pwrmon.c b/drivers/misc/phytium_pwrmon.c new file mode 100644 index 0000000000000000000000000000000000000000..5ccac223a67900bbcc15d1ffb101d14b3155beff --- /dev/null +++ b/drivers/misc/phytium_pwrmon.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Phytium SoC Power Monitor Driver + * + * This driver provides a simple interface to read power monitor + * data from Phytium SoC. + * + * Copyright (c) 2021-2025 Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +#undef pr_fmt +#define pr_fmt(fmt) "phytium_pwrmon: " fmt + +#define ACPI_METHOD_PKG "PGET" +#define DEFAULT_BUFFER_SIZE (256) + +static struct proc_dir_entry *pwrmon_dir; +static struct proc_dir_entry *proc_entry; + +struct buffer_handler { + char *buf; + size_t sz; + ssize_t len; +}; + +struct pwrmon_drv_data { + struct platform_device *pdev; + struct kmem_cache *bh_cache; +}; + +/** + * get_buffer_handler - Get a buffer handler from kmem_cache + * @drv_data: Pointer to pwrmon_drv_data structure + * + * This function allocates a buffer handler from kmem_cache and initializes it. + * The buffer handler contains a buffer, and the size of the buffer is set to + * DEFAULT_BUFFER_SIZE. The buffer is allocated using kmalloc. + * + * Return: Pointer to buffer handler or NULL on failure + */ +static inline struct buffer_handler *get_buffer_handler(struct pwrmon_drv_data *drv_data) +{ + struct buffer_handler *bh; + + if (!drv_data || !drv_data->bh_cache) + return NULL; + + bh = kmem_cache_zalloc(drv_data->bh_cache, GFP_KERNEL); + if (bh) { + bh->buf = kmalloc(DEFAULT_BUFFER_SIZE, GFP_KERNEL); + if (bh->buf) { + bh->sz = DEFAULT_BUFFER_SIZE; + bh->len = 0; + } else { + kmem_cache_free(drv_data->bh_cache, bh); + bh = NULL; + } + } + return bh; +} + +/** + * put_buffer_handler - Put a buffer handler back to kmem_cache + * @drv_data: Pointer to pwrmon_drv_data structure + * @bh: Pointer to buffer handler + * + * This function puts a buffer handler back to kmem_cache. It first checks if + * the parameters are valid, and then frees the buffer in the buffer handler + * using kfree, and finally frees the buffer handler itself using + * kmem_cache_free. + * + * Return: None + */ +static inline void put_buffer_handler(struct pwrmon_drv_data *drv_data, struct buffer_handler *bh) +{ + if (!bh || !drv_data || !drv_data->bh_cache) + return; + + kfree(bh->buf); + kmem_cache_free(drv_data->bh_cache, bh); +} + +/** + * free_buffer_handler - Free a kmem_cache used to allocate buffer handler + * @drv_data: Pointer to pwrmon_drv_data structure + * + * This function frees a kmem_cache used to allocate buffer handler, and then + * sets the pointer of the cache to NULL. It first checks if the parameters are + * valid, i.e., if the pointer to pwrmon_drv_data structure is not NULL and if + * the pointer to the cache is not NULL. + * + * Return: None + */ +static inline void free_buffer_handler(struct pwrmon_drv_data *drv_data) +{ + if (!drv_data || !drv_data->bh_cache) + return; + + kmem_cache_destroy(drv_data->bh_cache); + drv_data->bh_cache = NULL; +} + +/** + * shrink_buffer_handler - Shrink the buffer handler cache + * @drv_data: Pointer to pwrmon_drv_data structure + * + * This function shrinks the buffer handler cache to release unused memory. + * It checks if the input parameters are valid before proceeding with the + * shrink operation. If either the driver data or the buffer handler cache + * pointer is NULL, the function returns immediately without performing any + * operation. + * + * Return: None + */ +static inline void shrink_buffer_handler(struct pwrmon_drv_data *drv_data) +{ + if (!drv_data || !drv_data->bh_cache) + return; + + kmem_cache_shrink(drv_data->bh_cache); +} + +/** + * init_buffer_handler - Initialize a buffer handler cache + * @drv_data: Pointer to pwrmon_drv_data structure + * + * This function creates a buffer handler cache to store buffer handlers. + * The cache is created with the name "buffer_handler" and the size of a + * buffer handler structure. The cache is created with the SLAB_HWCACHE_ALIGN + * flag to ensure that the cache is aligned to the hardware cache size. + * The cache is created with the NULL constructor and the NULL destructor. + * + * Return: 0 if the cache is created successfully, -ENOMEM if the cache + * cannot be created. + */ +static inline int init_buffer_handler(struct pwrmon_drv_data *drv_data) +{ + if (!drv_data) + return -EINVAL; + + drv_data->bh_cache = kmem_cache_create("buffer_handler", + sizeof(struct buffer_handler), 0, + SLAB_HWCACHE_ALIGN, NULL); + + return drv_data->bh_cache ? 0 : -ENOMEM; +} + +/** + * enlarge_buffer - Enlarge the buffer size of a buffer handler + * @bh: Pointer to buffer_handler structure + * + * This function doubles the size of the buffer in the given buffer handler + * structure. If the buffer handler structure does not have a buffer allocated, + * it allocates a new buffer with the default size. + * + * Return: 0 if the buffer is enlarged successfully, -ENOMEM if memory cannot + * be allocated. + */ +static int enlarge_buffer(struct buffer_handler *bh) +{ + size_t next_sz; + char *new_buf; + + if (!bh || !bh->buf) + return -EFAULT; + + next_sz = bh->sz ? bh->sz * 2 : DEFAULT_BUFFER_SIZE; + new_buf = krealloc(bh->buf, next_sz, GFP_KERNEL); + if (!new_buf) + return -ENOMEM; + + bh->buf = new_buf; + bh->sz = next_sz; + return 0; +} + +static inline struct platform_device *get_pdev(struct inode *inode) +{ + return pde_data(inode); +} + +static inline struct pwrmon_drv_data *get_drv_data(struct inode *node) +{ + struct platform_device *pdev = get_pdev(node); + + return pdev ? platform_get_drvdata(pdev) : NULL; +} + +/** + * prepare_pwrmon_data_acpi - Prepare power monitor data in ACPI mode. + * @pdev: The platform device associated with the power monitor. + * @bh: The buffer handler structure to store the prepared data. + * + * This function evaluates the ACPI method _PGET to get the power monitor data, + * and stores the data in the buffer handler structure. + * + * Return: 0 if the data is prepared successfully, -ENOMEM if memory cannot be + * allocated, -EINVAL if the ACPI handle is invalid or the method evaluation + * fails. + */ +static int prepare_pwrmon_data_acpi(struct platform_device *pdev, struct buffer_handler *bh) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *package; + int ret = 0, i, j; + size_t remaining; + + if (!ACPI_HANDLE(&pdev->dev)) { + pr_err("Invalid ACPI handle\n"); + return -EINVAL; + } + + status = acpi_evaluate_object(ACPI_HANDLE(&pdev->dev), ACPI_METHOD_PKG, NULL, &buffer); + if (ACPI_FAILURE(status)) { + pr_err("ACPI method %s evaluation failed: %d\n", ACPI_METHOD_PKG, status); + return -EINVAL; + } + + package = buffer.pointer; + if (!package || package->type != ACPI_TYPE_PACKAGE) { + pr_err("ACPI method %s did not return a valid package\n", ACPI_METHOD_PKG); + bh->len = 0; + ret = -EINVAL; + goto free_buffer; + } + + for (i = 0; i < package->package.count; i++) { + union acpi_object *element = &package->package.elements[i]; + + if (element->type != ACPI_TYPE_PACKAGE || element->package.count < 2) { + pr_warn_ratelimited("Element %d has %d elements, expected at least 2\n", + i, element->package.count); + continue; + } + for (j = 0; j < element->package.count; j++) { + if (unlikely(element->package.elements[j].type != ACPI_TYPE_INTEGER)) { + pr_warn_ratelimited("Element [%d, %d] is not an integer\n", i, j); + continue; + } else { + /* + * We first check if the buffer is going to overflow. + * If so, we enlarge it. + */ + remaining = bh->sz - bh->len; + if (remaining < 23) { + ret = enlarge_buffer(bh); + if (ret < 0) { + pr_err("Failed to enlarge buffer\n"); + ret = -ENOMEM; + goto free_buffer; + } + } + + /* + * We do not check for overflow here, because we have already + * reserved enough space above. + */ + remaining = bh->sz - bh->len; + ret = scnprintf(bh->buf + bh->len, remaining, "%lld%s%s", + element->package.elements[j].integer.value, + j == 0 ? ": " : " ", + j == element->package.count - 1 ? "\n" : ""); + if (ret < 0) { + pr_err("Write error\n"); + goto free_buffer; + } + + bh->len += ret; + } + } + } + + ret = 0; +free_buffer: + kfree(buffer.pointer); + return ret; +} + + +/** + * prepare_pwrmon_data_of - Prepare power monitor data from Device Tree + * @pdev: Pointer to platform device structure + * @bh: Pointer to buffer handler structure + * + * This function is not implemented yet. + * + * Return: -EOPNOTSUPP if the method is not supported. + */ +static int prepare_pwrmon_data_of(struct platform_device *pdev, struct buffer_handler *bh) +{ + /* TODO: Implement Device Tree support by reading from regmap */ + return -EOPNOTSUPP; +} + +/** + * prepare_pwrmon_data - Prepare power monitor data in the corresponding format. + * @inode: Pointer to inode structure associated with the power monitor device. + * @file: Pointer to file structure associated with the power monitor device. + * + * This function prepares the power monitor data in the corresponding format + * (ACPI or Device Tree) and stores it in the buffer handler structure. + * + * Return: 0 if the data is prepared successfully, -EINVAL if the platform device + * or the buffer handler is invalid, -EOPNOTSUPP if the method is not supported. + */ +static int prepare_pwrmon_data(struct inode *inode, struct file *file) +{ + struct platform_device *pdev = get_pdev(inode); + struct buffer_handler *bh = file->private_data; + + if (!pdev || !bh) + return -EINVAL; + + if (ACPI_HANDLE(&pdev->dev)) + return prepare_pwrmon_data_acpi(pdev, bh); + else if (pdev->dev.of_node) + return prepare_pwrmon_data_of(pdev, bh); + + return -EOPNOTSUPP; +} + +/** + * pwrmon_pm_suspend - Suspend power monitor device + * @dev: Pointer to the device structure + * + * This function is called during the suspend phase of the power management + * process. It attempts to shrink the buffer handler associated with the + * power monitor device to release unused memory. If the driver data is + * not available, it returns an error. The function is intended to be + * expanded with additional suspend operations, such as suspending I/O + * mappings, if needed. + * + * Return: 0 on success, -EINVAL if driver data is not available. + */ + +static int pwrmon_pm_suspend(struct device *dev) +{ + struct pwrmon_drv_data *drv_data = dev_get_drvdata(dev); + + if (!drv_data) + return -EINVAL; + + /* Try to shrink the buffer handler */ + shrink_buffer_handler(drv_data); + + /* TODO: suspend some iomap */ + return 0; +} + +/** + * pwrmon_pm_resume - Resume power monitor device + * @dev: Pointer to the device structure + * + * This function is called during the resume phase of the power management + * process. It attempts to resume I/O mappings associated with the + * power monitor device. If the driver data is not available, it returns + * an error. + * + * Return: 0 on success, -EINVAL if driver data is not available. + */ +static int pwrmon_pm_resume(struct device *dev) +{ + struct pwrmon_drv_data *drv_data = dev_get_drvdata(dev); + + if (!drv_data) + return -EINVAL; + + /* TODO: resume some iomap */ + return 0; +} + +static const struct dev_pm_ops pwrmon_pm_ops = { + .suspend = pwrmon_pm_suspend, + .resume = pwrmon_pm_resume, +}; + +/** + * pwrmon_proc_open - Open a power monitor device for reading + * @inode: Pointer to the inode structure + * @file: Pointer to the file structure + * + * This function is called when the /proc interface is opened for reading. + * It tries to allocate a buffer handler and prepare the power monitor + * data for the user space to read. If the driver data is not available, + * it returns -EINVAL. If the buffer handler cannot be allocated, it + * returns -ENOMEM. If the power monitor data preparation fails, it + * returns -EFAULT. + * + * Return: 0 on success, -EINVAL if driver data is not available, + * -ENOMEM if buffer handler allocation fails, -EFAULT if power monitor + * data preparation fails. + */ +static int pwrmon_proc_open(struct inode *inode, struct file *file) +{ + struct pwrmon_drv_data *drv_data = get_drv_data(inode); + struct buffer_handler *bh; + + if (!drv_data) + return -EINVAL; + + bh = get_buffer_handler(drv_data); + if (!bh) + return -ENOMEM; + + file->private_data = bh; + + if (prepare_pwrmon_data(inode, file)) { + file->private_data = NULL; + put_buffer_handler(drv_data, bh); + return -EFAULT; + } + + return 0; +} + +/** + * pwrmon_proc_release - Release a power monitor device after reading + * @inode: Pointer to the inode structure + * @file: Pointer to the file structure + * + * This function is called when the /proc interface is closed after reading. + * It releases the buffer handler associated with the power monitor device. + * + * Return: 0 on success, -EINVAL if driver data is not available. + */ +static int pwrmon_proc_release(struct inode *inode, struct file *file) +{ + put_buffer_handler(get_drv_data(inode), file->private_data); + return 0; +} + +/** + * pwrmon_proc_read - Read a power monitor device through the /proc interface + * @file: Pointer to the file structure + * @ubuf: Pointer to the user-space buffer + * @count: Size of the user-space buffer + * @ppos: Pointer to the file offset + * + * This function is called when the /proc interface is read. It returns the + * power monitor data stored in the buffer handler associated with the power + * monitor device. + * + * Return: The number of bytes read, -EINVAL if no buffer handler is available. + */ +static ssize_t pwrmon_proc_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) +{ + struct buffer_handler *bh = file->private_data; + + return bh ? simple_read_from_buffer(ubuf, count, ppos, bh->buf, bh->len) : -EINVAL; +} + +static const struct proc_ops pwrmon_ops = { + .proc_open = pwrmon_proc_open, + .proc_read = pwrmon_proc_read, + .proc_release = pwrmon_proc_release, +}; + +/** + * pwrmon_probe - Probe the power monitor device + * @pdev: Pointer to the platform device structure + * + * This function is called when the power monitor device is probed. It allocates + * the driver data structure, initializes the buffer handler, and sets up the + * /proc interface. + * + * Return: 0 on success, error code on failure. + */ +static int pwrmon_probe(struct platform_device *pdev) +{ + struct pwrmon_drv_data *drv_data; + + if (!pdev) + return -EINVAL; + + drv_data = devm_kzalloc(&pdev->dev, sizeof(struct pwrmon_drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + drv_data->pdev = pdev; + + + if (init_buffer_handler(drv_data)) { + pr_err("Failed to init buffer handler\n"); + devm_kfree(&pdev->dev, drv_data); + return -ENOMEM; + } + + platform_set_drvdata(pdev, drv_data); + + pwrmon_dir = proc_mkdir("phytium_pwrmon", NULL); + if (!pwrmon_dir) { + pr_err("Failed to create /proc/phytium_pwrmon dir\n"); + goto dir_create_error; + } + + proc_entry = proc_create_data("data", 0444, pwrmon_dir, &pwrmon_ops, pdev); + if (!proc_entry) { + pr_err("Failed to create /proc/phytium_pwrmon/data entry\n"); + goto entry_create_error; + } + + pr_notice("Phytium power monitor driver loaded\n"); + return 0; + +entry_create_error: + if (pwrmon_dir) + proc_remove(pwrmon_dir); +dir_create_error: + free_buffer_handler(drv_data); + return -ENOMEM; +} + +/** + * pwrmon_remove - Remove a power monitor device + * @pdev: Pointer to the platform device structure + * + * This function is called when the power monitor device is removed. It removes + * the /proc interface entries associated with the power monitor device and + * releases the buffer handler. + * + * Return: 0 on success. + */ +static int pwrmon_remove(struct platform_device *pdev) +{ + if (proc_entry) + proc_remove(proc_entry); + + if (pwrmon_dir) + proc_remove(pwrmon_dir); + + free_buffer_handler(platform_get_drvdata(pdev)); + platform_set_drvdata(pdev, NULL); + pr_notice("Phytium power monitor driver unloaded\n"); + return 0; +} + +static const struct acpi_device_id acpi_ids[] = { + { "PHYT800B", 0 }, + { "", 0 } +}; +MODULE_DEVICE_TABLE(acpi, acpi_ids); + +static struct platform_driver pwrmon_driver = { + .probe = pwrmon_probe, + .remove = pwrmon_remove, + .driver = { + .name = "pwrmon_driver", + .acpi_match_table = ACPI_PTR(acpi_ids), + .pm = &pwrmon_pm_ops, + }, +}; + +module_platform_driver(pwrmon_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huang Shaobo "); +MODULE_DESCRIPTION("Phytium power monitor driver"); diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 0391a41ea5a1e79e77b3816080818eef6484f91d..f656e817c515a966d3a12820c67d276436e44061 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -88,7 +88,7 @@ struct sifive_fu540_macb_mgmt { #define GEM_MAX_TX_LEN (unsigned int)(0x3FC0) #define GEM_MTU_MIN_SIZE ETH_MIN_MTU -#define MACB_NETIF_LSO NETIF_F_TSO +#define MACB_NETIF_LSO (NETIF_F_TSO | NETIF_F_TSO6) #define MACB_WOL_HAS_MAGIC_PACKET (0x1 << 0) #define MACB_WOL_ENABLED (0x1 << 1) @@ -989,6 +989,9 @@ static void macb_mac_link_up(struct phylink_config *config, macb_set_tx_clk(bp, speed); + bp->speed = speed; + bp->duplex = duplex; + /* Initialize rings & buffers as clearing MACB_BIT(TE) in link down * cleared the pipeline and control registers. */ @@ -3775,8 +3778,13 @@ static int macb_get_link_ksettings(struct net_device *netdev, supported); ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.advertising, advertising); - kset->base.speed = bp->speed; - kset->base.duplex = bp->duplex; + if (netif_carrier_ok(netdev)) { + kset->base.speed = bp->speed; + kset->base.duplex = bp->duplex; + } else { + kset->base.speed = SPEED_UNKNOWN; + kset->base.duplex = DUPLEX_UNKNOWN; + } } else { phylink_ethtool_ksettings_get(bp->phylink, kset); } @@ -4809,7 +4817,6 @@ static const struct macb_usrio_config macb_default_usrio = { .refclk = MACB_BIT(CLKEN), }; -#if defined(CONFIG_OF) /* 1518 rounded up */ #define AT91ETHER_MAX_RBUFF_SZ 0x600 /* max number of receive buffers */ @@ -5526,6 +5533,7 @@ static const struct macb_config phytium_gem2p0_config = { .usrio = &macb_default_usrio, }; +#if defined(CONFIG_OF) static const struct of_device_id macb_dt_ids[] = { { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config }, { .compatible = "cdns,macb" }, diff --git a/drivers/net/ethernet/phytium/Kconfig b/drivers/net/ethernet/phytium/Kconfig index 14a77adbfdc12fac3a7ca84fb7b10afb154f9dc9..e68ca5d82181e8a895c4430b11cf805a17543c4f 100644 --- a/drivers/net/ethernet/phytium/Kconfig +++ b/drivers/net/ethernet/phytium/Kconfig @@ -5,6 +5,7 @@ config NET_VENDOR_PHYTIUM bool "Phytium devices" + depends on ARCH_PHYTIUM depends on HAS_IOMEM default y help diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index ee22cda030e94a3548ffc3008bb6c4745dcf4d5d..c3feca3d9611a0940f80e5b053e11a99770bebc6 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -17,7 +17,7 @@ #define PHYTMAC_PCI_DRV_NAME "phytmac_pci" #define PHYTMAC_PLAT_DRV_NAME "phytmac_platform" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" -#define PHYTMAC_DRIVER_VERSION "1.0.47" +#define PHYTMAC_DRIVER_VERSION "1.0.50" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index bed3d6e4d040cec4b5c1b85a4141073e8d87ff53..86e8c7b5c45553739a6c7ca95d81b957d3fd1e29 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -385,8 +385,13 @@ static int phytmac_get_link_ksettings(struct net_device *ndev, if (!ndev->phydev) { kset->base.port = PORT_FIBRE; kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = pdata->duplex; - kset->base.speed = pdata->speed; + if (netif_carrier_ok(ndev)) { + kset->base.duplex = pdata->duplex; + kset->base.speed = pdata->speed; + } else { + kset->base.duplex = DUPLEX_UNKNOWN; + kset->base.speed = SPEED_UNKNOWN; + } if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index 9ed5ec09523b3ee6ef99dbdae62e105c202bb911..ccdb2be4ab0bb20655b558ba30de9db773084bc8 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -739,7 +739,8 @@ static bool phytmac_alloc_mapped_page(struct phytmac *pdata, bi->addr = dma; bi->page = page; bi->page_offset = PHYTMAC_SKB_PAD; - bi->pagecnt_bias = 1; + page_ref_add(page, USHRT_MAX - 1); + bi->pagecnt_bias = USHRT_MAX; return true; } @@ -769,8 +770,8 @@ static bool phytmac_can_reuse_rx_page(struct phytmac_rx_buffer *rx_buffer) * the pagecnt_bias and page count so that we fully restock the * number of references the driver holds. */ - if (unlikely(!pagecnt_bias)) { - page_ref_add(page, USHRT_MAX); + if (unlikely(pagecnt_bias == 1)) { + page_ref_add(page, USHRT_MAX - 1); rx_buffer->pagecnt_bias = USHRT_MAX; } @@ -2364,7 +2365,7 @@ static netdev_features_t phytmac_features_check(struct sk_buff *skb, hdrlen = skb_transport_offset(skb); if (!IS_ALIGNED(skb_headlen(skb) - hdrlen, PHYTMAC_TX_LEN_ALIGN)) - return features & ~NETIF_F_TSO; + return features & ~(NETIF_F_TSO | NETIF_F_TSO6); nr_frags = skb_shinfo(skb)->nr_frags; /* No need to check last fragment */ @@ -2373,7 +2374,7 @@ static netdev_features_t phytmac_features_check(struct sk_buff *skb, const skb_frag_t *frag = &skb_shinfo(skb)->frags[f]; if (!IS_ALIGNED(skb_frag_size(frag), PHYTMAC_TX_LEN_ALIGN)) - return features & ~NETIF_F_TSO; + return features & ~(NETIF_F_TSO | NETIF_F_TSO6); } return features; } @@ -2663,7 +2664,7 @@ void phytmac_default_config(struct phytmac *pdata) ndev->hw_features = NETIF_F_SG; if (pdata->capacities & PHYTMAC_CAPS_LSO) - ndev->hw_features |= NETIF_F_TSO; + ndev->hw_features |= NETIF_F_TSO | NETIF_F_TSO6; if (pdata->use_ncsi) { ndev->hw_features &= ~(NETIF_F_HW_CSUM | NETIF_F_RXCSUM); diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index 1afff67d625d99b450f1e4dcc63508f98a1d9db0..0df1771ca3972f82338183e9b9c93c9b329fa5d5 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -25,10 +25,11 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, u32 tx_head, tx_tail, ring_size; struct phytmac_msg_info msg; struct phytmac_msg_info msg_rx; + unsigned long flags; u32 retry = 0; int ret = 0; - spin_lock(&pdata->msg_ring.msg_lock); + spin_lock_irqsave(&pdata->msg_ring.msg_lock, flags); tx_head = PHYTMAC_READ(pdata, PHYTMAC_TX_MSG_HEAD) & 0xff; tx_tail = phytmac_v2_tx_ring_wrap(pdata, pdata->msg_ring.tx_msg_wr_tail); pdata->msg_ring.tx_msg_rd_tail = tx_tail; @@ -42,8 +43,8 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, netdev_err(pdata->ndev, "Time out waiting for Tx msg ring free, tx_tail:0x%x, tx_head:0x%x", tx_tail, tx_head); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } } @@ -58,8 +59,8 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, } else if (len > PHYTMAC_MSG_PARA_LEN) { netdev_err(pdata->ndev, "Tx msg para len %d is greater than the max len %d", len, PHYTMAC_MSG_PARA_LEN); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } if (netif_msg_hw(pdata)) { @@ -80,8 +81,8 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, retry++; if (retry >= PHYTMAC_RETRY_TIMES) { netdev_err(pdata->ndev, "Msg process time out!"); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } } @@ -90,12 +91,13 @@ static int phytmac_v2_msg_send(struct phytmac *pdata, u16 cmd_id, if (!(msg_rx.status0 & PHYTMAC_CMD_PRC_SUCCESS)) { netdev_err(pdata->ndev, "Msg process error, cmdid:%d, subid:%d, status0:%d, tail:%d", msg.cmd_type, msg.cmd_subid, msg.status0, tx_tail); - spin_unlock(&pdata->msg_ring.msg_lock); - return -EINVAL; + ret = -EINVAL; + goto err_out; } } - spin_unlock(&pdata->msg_ring.msg_lock); +err_out: + spin_unlock_irqrestore(&pdata->msg_ring.msg_lock, flags); return ret; } @@ -192,7 +194,6 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) struct phytmac_dma_info dma; struct phytmac_eth_info eth; u32 ptrconfig = 0; - u8 mdc; if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) ptrconfig |= PHYTMAC_BIT(TXTAIL_EN); @@ -255,10 +256,6 @@ static int phytmac_v2_init_hw(struct phytmac *pdata) dma.hw_dma_cap |= HW_DMA_CAP_DDW128; phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)&dma, sizeof(dma), 0); - cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; - mdc = PHYTMAC_CLK_DIV96; - phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 0); - memset(ð, 0, sizeof(eth)); cmd_subid = PHYTMAC_MSG_CMD_SET_ETH_MATCH; eth.index = 0; @@ -703,8 +700,13 @@ static int phytmac_v2_enable_txcsum(struct phytmac *pdata, int enable) static int phytmac_v2_enable_mdio(struct phytmac *pdata, int enable) { u16 cmd_id, cmd_subid; + u8 mdc; cmd_id = PHYTMAC_MSG_CMD_SET; + cmd_subid = PHYTMAC_MSG_CMD_SET_MDC; + mdc = PHYTMAC_CLK_DIV96; + phytmac_v2_msg_send(pdata, cmd_id, cmd_subid, (void *)(&mdc), sizeof(mdc), 0); + if (enable) cmd_subid = PHYTMAC_MSG_CMD_SET_ENABLE_MDIO; else diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 107880d13d219f8282c98f1ee6d5b5eec2c8a71f..3305eaad313e3486f2bef042e9086a18971a8950 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -189,6 +189,11 @@ config ICPLUS_PHY help Currently supports the IP175C and IP1001 PHYs. +config LINKYUM_PHY + tristate "LINKYUM PHYs" + help + Currently supports the LY1211A LY1211S LY1241A LY1241BPHYs. + config LXT_PHY tristate "Intel LXT PHYs" help diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index c945ed9bd14b3928d6aa161b925a075fd90e5e9b..215d06d054bffe0e9a61639f4575730b095749fc 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_DP83TD510_PHY) += dp83td510.o obj-$(CONFIG_FIXED_PHY) += fixed_phy.o obj-$(CONFIG_ICPLUS_PHY) += icplus.o obj-$(CONFIG_INTEL_XWAY_PHY) += intel-xway.o +obj-$(CONFIG_LINKYUM_PHY) += linkyum.o obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_MARVELL_10G_PHY) += marvell10g.o diff --git a/drivers/net/phy/linkyum.c b/drivers/net/phy/linkyum.c new file mode 100644 index 0000000000000000000000000000000000000000..2393de565b08dc79b37388c8cd9d14e3d606a004 --- /dev/null +++ b/drivers/net/phy/linkyum.c @@ -0,0 +1,1816 @@ +/**************************************************************************** + * Copyright (c) 2023 Shanghai Linkyum Microelectronics Co. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +***************************************************************************** + * drivers/net/phy/linkyum.c + * Description : Driver for LY1211A LY1211S LY1241A LY1241BPHYs. + * Version : V0.7 Fixed an issue where the network cannot link + * V0.8 Follow link to switch Page automatically in mode 2 + * V0.9 Adapts to ly1211a wol functions and 1000M utp optimise + * V0.10 There is a probability that the optimized phy will fail to link and add ly1210 driver + * V1.0 Optimize 125M clkout and add 1y1211 cfg function + * V1.1 Optimized adaptation 1y1241 cfg function + * V1.2 Support LY1211V3 cfg opt +*****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LINUX_VERSION_CODE +#include +#else +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) +#endif + + +/* WOL Enable Flag: + * disable by default to enable system WOL feature of phy + * please define this phy to 1 otherwise, define it to 0. + */ +#define LINKYUM_PHY_WOL_FEATURE_ENABLE 0 +#define LINKYUM_PHY_WOL_PASSWD_ENABLE 0 + +#define LINKYUM_PHY_MODE_SET_ENABLE 0 +#define LINKYUM_PHY_RXC_DELAY_SET_ENABLE 0 +#define LINKYUM_PHY_RXC_DELAY_VAL 0x00 +#define LINKYUM_PHY_CLK_OUT_125M_ENABLE 1 +#define LINKYUM_PHY_LINK_OPT_ENABLE 0 + + +/* Config Enable Flag */ +#define LINKYUM_PHY_LY1211_CFG_ENABLE 0 +#define LINKYUM_PHY_LY1211V2_CFG_ENABLE 0 +#define LINKYUM_PHY_LY1211V3_CFG_ENABLE 0 +#define LINKYUM_PHY_LY1241V2_CFG_ENABLE 0 + +#if LINKYUM_PHY_LY1211_CFG_ENABLE || LINKYUM_PHY_LY1211V2_CFG_ENABLE || \ + LINKYUM_PHY_LY1211V3_CFG_ENABLE || LINKYUM_PHY_LY1241V2_CFG_ENABLE +#include "linkyum.h" +#endif + +#define LYPHY_GLB_DISABLE 0 +#define LYPHY_GLB_ENABLE 1 +#define LYPHY_LINK_DOWN 0 +#define LYPHY_LINK_UP 1 +/* Mask used for ID comparisons */ +#define LINKYUM_PHY_ID_MASK 0xffffffff + +/* LY1211 PHY IDs */ +#define LY1210_PHY_ID 0xADB40400 +/* LY1211 PHY IDs */ +#define LY1211_PHY_ID 0xADB40412 +/* LY1141 PHY IDs */ +#define LY1241_PHY_ID 0xADB40411 + +/*LY1211 PHY LED */ +#define LY1211_EXTREG_LED0 0x1E33 // 0 +#define LY1211_EXTREG_LED1 0x1E34 // 00101111 +#define LY1211_EXTREG_LED2 0x1E35 // 0x40 +/*LY1241 PHY BX LED */ +#define LY1241_EXTREG_LEDCTRL 0x0621 +#define LY1241_EXTREG_LED0_1 0x0700 +#define LY1241_EXTREG_LED0_2 0x0701 +#define LY1241_EXTREG_LED1_1 0x0702 +#define LY1241_EXTREG_LED1_2 0x0703 +#define LY1241_EXTREG_LED2_1 0x0706 +#define LY1241_EXTREG_LED2_2 0x0707 +#define LY1241_EXTREG_LED3_1 0x0708 +#define LY1241_EXTREG_LED3_2 0x0709 +#define LY1241_EXTREG_LED4_1 0x070C +#define LY1241_EXTREG_LED4_2 0x070D +#define LY1241_EXTREG_LED5_1 0x070E +#define LY1241_EXTREG_LED5_2 0x070F +#define LY1241_EXTREG_LED6_1 0x0712 +#define LY1241_EXTREG_LED6_2 0x0713 +#define LY1241_EXTREG_LED7_1 0x0714 +#define LY1241_EXTREG_LED7_2 0x0715 + +/* PHY MODE OPSREG*/ +#define LY1211_EXTREG_GET_PORT_PHY_MODE 0x062B +#define LY1211_EXTREG_PHY_MODE_MASK 0x0070 +/* Magic Packet MAC address registers */ +#define LINKYUM_MAGIC_PACKET_MAC_ADDR 0x0229 +/* Magic Packet MAC Passwd registers */ +#define LINKYUM_MAGIC_PACKET_PASSWD_ADDR 0x022F +#define LINKYUM_PHY_WOL_PULSE_MODE_SET 0x062a + +/* Magic Packet MAC Passwd Val*/ +#define LINKYUM_MAGIC_PACKET_PASSWD1 0x11 +#define LINKYUM_MAGIC_PACKET_PASSWD2 0x22 +#define LINKYUM_MAGIC_PACKET_PASSWD3 0x33 +#define LINKYUM_MAGIC_PACKET_PASSWD4 0x44 +#define LINKYUM_MAGIC_PACKET_PASSWD5 0x55 +#define LINKYUM_MAGIC_PACKET_PASSWD6 0x66 + +/* Linyum wol config register */ +#define LINKYUM_WOL_CFG_REG0 0x0220 +#define LINKYUM_WOL_CFG_REG1 0x0221 +#define LINKYUM_WOL_CFG_REG2 0x0222 +#define LINKYUM_WOL_STA_REG 0x0223 +/* 8 PHY MODE */ +#define LY1211_EXTREG_PHY_MODE_UTP_TO_RGMII 0x00 +#define LY1211_EXTREG_PHY_MODE_FIBER_TO_RGMII 0x10 +#define LY1211_EXTREG_PHY_MODE_UTP_OR_FIBER_TO_RGMII 0x20 +#define LY1211_EXTREG_PHY_MODE_UTP_TO_SGMII 0x30 +#define LY1211_EXTREG_PHY_MODE_SGMII_PHY_TO_RGMII_MAC 0x40 +#define LY1211_EXTREG_PHY_MODE_SGMII_MAC_TO_RGMII_PHY 0x50 +#define LY1211_EXTREG_PHY_MODE_UTP_TO_FIBER_AUTO 0x60 +#define LY1211_EXTREG_PHY_MODE_UTP_TO_FIBER_FORCE 0x70 + +/* PHY EXTRW OPSREG */ +#define LY1211_EXTREG_ADDR 0x0E +#define LY1211_EXTREG_DATA 0x0D +/* PHY PAGE SPACE */ +#define LYPHY_REG_UTP_SPACE 0 +#define LYPHY_REG_FIBER_SPACE 1 + +/* PHY PAGE SELECT */ +#define LY1211_EXTREG_PHY_MODE_PAGE_SELECT 0x0016 +#define LYPHY_REG_UTP_SPACE_SETADDR 0x0000 +#define LYPHY_REG_FIBER_SPACE_SETADDR 0x0100 +//utp +#define UTP_REG_PAUSE_CAP 0x0400 /* Can pause */ +#define UTP_REG_PAUSE_ASYM 0x0800 /* Can pause asymetrically */ +//fiber +#define FIBER_REG_PAUSE_CAP 0x0080 /* Can pause */ +#define FIBER_REG_PAUSE_ASYM 0x0100 /* Can pause asymetrically */ + +/* specific status register */ +#define LINKYUM_SPEC_REG 0x0011 + +/* Interrupt Enable Register */ +#define LINKYUM_INTR_REG 0x0017 +/* Phy Patch Register */ +#define LY_CFG_INIT 0xC460 +#define LY_CFG_WAIT 0xC45C +#define LY_CFG_EPHY_INIT 0xC413 +#define LY_CFG_ADDR 0xC800 +#define LY_CFG_ENABLE 0xC45D +#define LY_CFG_ENABLE_ACK 0xC45E + +/* WOL TYPE */ +#define LINKYUM_WOL_TYPE BIT(0) +/* WOL Pulse Width */ +#define LINKYUM_WOL_WIDTH1 BIT(1) +#define LINKYUM_WOL_WIDTH2 BIT(2) +/* WOL dest addr check enable */ +#define LINKYUM_WOL_SECURE_CHECK BIT(5) +/* WOL crc check enable */ +#define LINKYUM_WOL_CRC_CHECK BIT(4) +/* WOL dest addr check enable */ +#define LINKYUM_WOL_DESTADDR_CHECK BIT(5) +/* WOL Event Interrupt Enable */ +#define LINKYUM_WOL_INTR_EN BIT(2) +/* WOL Enable */ +#define LINKYUM_WOL_EN BIT(7) + +#define LINKYUM_WOL_RESTARTANEG BIT(9) +/* GET PHY MODE */ +#define LYPHY_MODE_CURR lyphy_get_port_type(phydev) + +enum linkyum_port_type_e +{ + LYPHY_PORT_TYPE_UTP, + LYPHY_PORT_TYPE_FIBER, + LYPHY_PORT_TYPE_COMBO, + LYPHY_PORT_TYPE_EXT +}; +enum linkyum_wol_type_e +{ + LYPHY_WOL_TYPE_LEVEL, + LYPHY_WOL_TYPE_PULSE, + LYPHY_WOL_TYPE_EXT +}; + +enum linkyum_wol_width_e +{ + LYPHY_WOL_WIDTH_84MS, + LYPHY_WOL_WIDTH_168MS, + LYPHY_WOL_WIDTH_336MS, + LYPHY_WOL_WIDTH_672MS, + LYPHY_WOL_WIDTH_EXT +}; + +typedef struct linkyum_wol_cfg_s +{ + int wolen; + int type; + int width; + int secure; + int checkcrc; + int checkdst; +}linkyum_wol_cfg_t; + +#if (KERNEL_VERSION(5, 5, 0) > LINUX_VERSION_CODE) +static inline void phy_lock_mdio_bus(struct phy_device *phydev) +{ +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + mutex_lock(&phydev->bus->mdio_lock); +#else + mutex_lock(&phydev->mdio.bus->mdio_lock); +#endif +} + +static inline void phy_unlock_mdio_bus(struct phy_device *phydev) +{ +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + mutex_unlock(&phydev->bus->mdio_lock); +#else + mutex_unlock(&phydev->mdio.bus->mdio_lock); +#endif +} +#endif + +#if (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE) +static inline int __phy_read(struct phy_device *phydev, u32 regnum) +{ +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + struct mii_bus *bus = phydev->bus; + int addr = phydev->addr; + return bus->read(bus, phydev->addr, regnum); +#else + struct mii_bus *bus = phydev->mdio.bus; + int addr = phydev->mdio.addr; +#endif + return bus->read(bus, addr, regnum); +} + +static inline int __phy_write(struct phy_device *phydev, u32 regnum, u16 val) +{ +#if (KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE) + struct mii_bus *bus = phydev->bus; + int addr = phydev->addr; +#else + struct mii_bus *bus = phydev->mdio.bus; + int addr = phydev->mdio.addr; +#endif + return bus->write(bus, addr, regnum, val); +} +#endif + +#if (KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE) && (KERNEL_VERSION(4, 16, 0) > LINUX_VERSION_CODE) +static int genphy_read_mmd_unsupported(struct phy_device *phdev, int devad, u16 regnum) +{ + return -EOPNOTSUPP; +} + +static int genphy_write_mmd_unsupported(struct phy_device *phdev, int devnum, + u16 regnum, u16 val) +{ + return -EOPNOTSUPP; +} +#endif + +static int ly1241_phy_read(struct phy_device *phydev, u32 regnum) +{ + int ret, val, oldval = 0; + + phy_lock_mdio_bus(phydev); + + ret = __phy_read(phydev, LY1211_EXTREG_ADDR); + if (ret < 0) + goto err_handle; + oldval = ret; + + ret = __phy_read(phydev, regnum); + if (ret < 0) + goto err_handle; + val = ret; + + ret = __phy_write(phydev, LY1211_EXTREG_ADDR, oldval); + if (ret < 0) + goto err_handle; + ret = val; + +err_handle: + phy_unlock_mdio_bus(phydev); + return ret; +} + +// static int ly1241_phy_write(struct phy_device *phydev, u32 regnum, u16 val) +// { +// int ret, oldval = 0; + +// phy_lock_mdio_bus(phydev); + +// ret = __phy_read(phydev, LY1211_EXTREG_ADDR); +// if (ret < 0) +// goto err_handle; +// oldval = ret; + +// ret = __phy_write(phydev, regnum, val); +// if(ret<0) +// goto err_handle; + +// ret = __phy_write(phydev, LY1211_EXTREG_ADDR, oldval); +// if (ret < 0) +// goto err_handle; + +// err_handle: +// phy_unlock_mdio_bus(phydev); +// return ret; +// } + +static int ly1211_phy_ext_read(struct phy_device *phydev, u32 regnum) +{ + int ret, val, oldpage = 0, oldval = 0; + + phy_lock_mdio_bus(phydev); + + ret = __phy_read(phydev, LY1211_EXTREG_ADDR); + if (ret < 0) + goto err_handle; + oldval = ret; + + /* Force change to utp page */ + ret = __phy_read(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT);//get old page + if (ret < 0) + goto err_handle; + oldpage = ret; + + ret = __phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, LYPHY_REG_UTP_SPACE_SETADDR); + if (ret < 0) + goto err_handle; + + /* Default utp ext rw */ + ret = __phy_write(phydev, LY1211_EXTREG_ADDR, regnum); + if (ret < 0) + goto err_handle; + + ret = __phy_read(phydev, LY1211_EXTREG_DATA); + if (ret < 0) + goto err_handle; + val = ret; + + /* Recover to old page */ + ret = __phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, oldpage); + if (ret < 0) + goto err_handle; + + ret = __phy_write(phydev, LY1211_EXTREG_ADDR, oldval); + if (ret < 0) + goto err_handle; + ret = val; + +err_handle: + phy_unlock_mdio_bus(phydev); + return ret; +} + +static int ly1211_phy_ext_write(struct phy_device *phydev, u32 regnum, u16 val) +{ + int ret, oldpage = 0, oldval = 0; + + phy_lock_mdio_bus(phydev); + + ret = __phy_read(phydev, LY1211_EXTREG_ADDR); + if (ret < 0) + goto err_handle; + oldval = ret; + + /* Force change to utp page */ + ret = __phy_read(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT); //get old page + if (ret < 0) + goto err_handle; + oldpage = ret; + + ret = __phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, LYPHY_REG_UTP_SPACE_SETADDR); + if (ret < 0) + goto err_handle; + + /* Default utp ext rw */ + ret = __phy_write(phydev, LY1211_EXTREG_ADDR, regnum); + if (ret < 0) + goto err_handle; + + ret = __phy_write(phydev, LY1211_EXTREG_DATA, val); + if (ret < 0) + goto err_handle; + + /* Recover to old page */ + ret = __phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, oldpage); + if (ret < 0) + goto err_handle; + + ret = __phy_write(phydev, LY1211_EXTREG_ADDR, oldval); + if (ret < 0) + goto err_handle; + +err_handle: + phy_unlock_mdio_bus(phydev); + return ret; + +} + +static int linkyum_phy_select_reg_page(struct phy_device *phydev, int space) +{ + int ret; + if (space == LYPHY_REG_UTP_SPACE) + ret = phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, LYPHY_REG_UTP_SPACE_SETADDR); + else + ret = phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, LYPHY_REG_FIBER_SPACE_SETADDR); + return ret; +} + +static int linkyum_phy_get_reg_page(struct phy_device *phydev) +{ + return phy_read(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT); +} + +static int linkyum_phy_ext_read(struct phy_device *phydev, u32 regnum) +{ + return ly1211_phy_ext_read(phydev, regnum); +} + + +static int linkyum_phy_ext_write(struct phy_device *phydev, u32 regnum, u16 val) +{ + return ly1211_phy_ext_write(phydev, regnum, val); +} + +static int lyphy_page_read(struct phy_device *phydev, int page, u32 regnum) +{ + int ret, val, oldpage = 0, oldval = 0; + + phy_lock_mdio_bus(phydev); + + ret = __phy_read(phydev, LY1211_EXTREG_ADDR); + if (ret < 0) + goto err_handle; + oldval = ret; + + ret = __phy_read(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT); + if (ret < 0) + goto err_handle; + oldpage = ret; + + //Select page + ret = __phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, (page << 8)); + if (ret < 0) + goto err_handle; + + ret = __phy_read(phydev, regnum); + if (ret < 0) + goto err_handle; + val = ret; + + /* Recover to old page */ + ret = __phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, oldpage); + if (ret < 0) + goto err_handle; + + ret = __phy_write(phydev, LY1211_EXTREG_ADDR, oldval); + if (ret < 0) + goto err_handle; + ret = val; + +err_handle: + phy_unlock_mdio_bus(phydev); + return ret; +} + +static int lyphy_page_write(struct phy_device *phydev, int page, u32 regnum, u16 value) +{ + int ret, oldpage = 0, oldval = 0; + + phy_lock_mdio_bus(phydev); + + ret = __phy_read(phydev, LY1211_EXTREG_ADDR); + if (ret < 0) + goto err_handle; + oldval = ret; + + ret = __phy_read(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT); + if (ret < 0) + goto err_handle; + oldpage = ret; + + //Select page + ret = __phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, (page << 8)); + if(ret<0) + goto err_handle; + + ret = __phy_write(phydev, regnum, value); + if(ret<0) + goto err_handle; + + /* Recover to old page */ + ret = __phy_write(phydev, LY1211_EXTREG_PHY_MODE_PAGE_SELECT, oldpage); + if (ret < 0) + goto err_handle; + + ret = __phy_write(phydev, LY1211_EXTREG_ADDR, oldval); + if (ret < 0) + goto err_handle; + +err_handle: + phy_unlock_mdio_bus(phydev); + return ret; +} + +//get port type +static int lyphy_get_port_type(struct phy_device *phydev) +{ + int ret, mode; + + ret = linkyum_phy_ext_read(phydev, LY1211_EXTREG_GET_PORT_PHY_MODE); + if (ret < 0) + return ret; + ret &= LY1211_EXTREG_PHY_MODE_MASK; + + if (ret == LY1211_EXTREG_PHY_MODE_UTP_TO_RGMII || + ret == LY1211_EXTREG_PHY_MODE_UTP_TO_SGMII) { + mode = LYPHY_PORT_TYPE_UTP; + } else if (ret == LY1211_EXTREG_PHY_MODE_FIBER_TO_RGMII || + ret == LY1211_EXTREG_PHY_MODE_SGMII_PHY_TO_RGMII_MAC || + ret == LY1211_EXTREG_PHY_MODE_SGMII_MAC_TO_RGMII_PHY) { + mode = LYPHY_PORT_TYPE_FIBER; + } else { + mode = LYPHY_PORT_TYPE_COMBO; + } + + return mode; +} + +static int ly1121_led_init(struct phy_device *phydev) +{ + int ret; + + ret = linkyum_phy_ext_write(phydev, LY1211_EXTREG_LED0, 0x00); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1211_EXTREG_LED1, 0x2F); + if (ret < 0) + return ret; + + return linkyum_phy_ext_write(phydev, LY1211_EXTREG_LED2, 0x40); +} + +static int ly1241_led_init(struct phy_device *phydev) +{ + int ret; + // set led put low level + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LEDCTRL, 0x04); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_read(phydev, LY1241_EXTREG_LED0_1); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_read(phydev, LY1241_EXTREG_LED0_2); + if (ret < 0) + return ret; + + + // set led 0 1 + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED0_1, 0x00); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED0_2, 0x08); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED1_1, 0xF0); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED1_2, 0x0E); + if (ret < 0) + return ret; + + // set led 2 3 + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED2_1, 0x00); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED2_2, 0x18); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED3_1, 0xF0); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED3_2, 0x1E); + if (ret < 0) + return ret; + + // set led 4 + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED4_1, 0x00); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED4_2, 0x28); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED5_1, 0xFE); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED5_2, 0x2E); + if (ret < 0) + return ret; + + // set led 6 + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED6_2, 0x00); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED6_2, 0x38); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED7_1, 0xF0); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY1241_EXTREG_LED7_2, 0x3E); + if (ret < 0) + return ret; + + return ret; +} + + +static int lyphy_restart_aneg(struct phy_device *phydev) +{ + int ret, ctl; + + ctl = lyphy_page_read(phydev, LYPHY_REG_FIBER_SPACE, MII_BMCR); + if (ctl < 0) + return ctl; + ctl |= BMCR_ANENABLE; + ret = lyphy_page_write(phydev, LYPHY_REG_FIBER_SPACE, MII_BMCR, ctl); + if (ret < 0) + return ret; + + return 0; +} + +int ly1211_config_aneg(struct phy_device *phydev) +{ + int ret, phymode, oldpage = 0; + + phymode = LYPHY_MODE_CURR; + + if (phymode == LYPHY_PORT_TYPE_UTP || phymode == LYPHY_PORT_TYPE_COMBO) { + oldpage = linkyum_phy_get_reg_page(phydev); + if (oldpage < 0) + return oldpage; + ret = linkyum_phy_select_reg_page(phydev, LYPHY_REG_UTP_SPACE); + if (ret < 0) + return ret; + ret = genphy_config_aneg(phydev); + if (ret < 0) + return ret; + ret = linkyum_phy_select_reg_page(phydev, oldpage); + if (ret < 0) + return ret; + } + + if (phymode == LYPHY_PORT_TYPE_FIBER || phymode == LYPHY_PORT_TYPE_COMBO) { + oldpage = linkyum_phy_get_reg_page(phydev); + if (oldpage < 0) + return oldpage; + ret = linkyum_phy_select_reg_page(phydev, LYPHY_REG_FIBER_SPACE); + if (ret < 0) + return ret; + if (AUTONEG_ENABLE != phydev->autoneg) + return genphy_setup_forced(phydev); + ret = lyphy_restart_aneg(phydev); + if (ret < 0) + return ret; + ret = linkyum_phy_select_reg_page(phydev, oldpage); + if (ret < 0) + return ret; + } + return 0; +} + +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) +int ly1211_aneg_done(struct phy_device *phydev) +{ + int val = 0; + + val = phy_read(phydev, 0x16); + + if (val == LYPHY_REG_FIBER_SPACE_SETADDR) { + val = phy_read(phydev, 0x1); + val = phy_read(phydev, 0x1); + return (val < 0) ? val : (val & BMSR_LSTATUS); + } + + return genphy_aneg_done(phydev); +} +#endif + +#if (LINKYUM_PHY_WOL_FEATURE_ENABLE) +static void linkyum_get_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + int val = 0; + wol->supported = WAKE_MAGIC; + wol->wolopts = 0; + + val = linkyum_phy_ext_read(phydev, LINKYUM_WOL_CFG_REG1); + if (val < 0) + return; + + if (val & LINKYUM_WOL_EN) + wol->wolopts |= WAKE_MAGIC; + + return; +} + +static int linkyum_wol_en_cfg(struct phy_device *phydev, linkyum_wol_cfg_t wol_cfg) +{ + int ret, val0,val1; + + val0 = linkyum_phy_ext_read(phydev, LINKYUM_WOL_CFG_REG0); + if (val0 < 0) + return val0; + val1 = linkyum_phy_ext_read(phydev, LINKYUM_WOL_CFG_REG1); + if (val1 < 0) + return val1; + if (wol_cfg.wolen) { + val1 |= LINKYUM_WOL_EN; + if (wol_cfg.type == LYPHY_WOL_TYPE_LEVEL) { + val0 |= LINKYUM_WOL_TYPE; + } else if (wol_cfg.type == LYPHY_WOL_TYPE_PULSE) { + ret = linkyum_phy_ext_write(phydev, LINKYUM_PHY_WOL_PULSE_MODE_SET, 0x04);//set int pin pulse + if (ret < 0) + return ret; + val0 &= ~LINKYUM_WOL_TYPE; + if (wol_cfg.width == LYPHY_WOL_WIDTH_84MS) { + val0 &= ~LINKYUM_WOL_WIDTH1; + val0 &= ~LINKYUM_WOL_WIDTH2; + } else if (wol_cfg.width == LYPHY_WOL_WIDTH_168MS) { + val0 |= LINKYUM_WOL_WIDTH1; + val0 &= ~LINKYUM_WOL_WIDTH2; + } else if (wol_cfg.width == LYPHY_WOL_WIDTH_336MS) { + val0 &= ~LINKYUM_WOL_WIDTH1; + val0 |= LINKYUM_WOL_WIDTH2; + } else if (wol_cfg.width == LYPHY_WOL_WIDTH_672MS) { + val0 |= LINKYUM_WOL_WIDTH1; + val0 |= LINKYUM_WOL_WIDTH2; + } + } + if (wol_cfg.secure == LYPHY_GLB_ENABLE) + val1 |= LINKYUM_WOL_SECURE_CHECK; + else + val1 &= ~LINKYUM_WOL_SECURE_CHECK; + if (wol_cfg.checkcrc == LYPHY_GLB_ENABLE) + val0 |= LINKYUM_WOL_CRC_CHECK; + else + val0 &= ~LINKYUM_WOL_CRC_CHECK; + if (wol_cfg.checkdst == LYPHY_GLB_ENABLE) + val0 |= LINKYUM_WOL_DESTADDR_CHECK; + else + val0 &= ~LINKYUM_WOL_DESTADDR_CHECK; + } else { + val1 &= ~LINKYUM_WOL_EN; + } + + ret = linkyum_phy_ext_write(phydev, LINKYUM_WOL_CFG_REG0, val0); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LINKYUM_WOL_CFG_REG1, val1); + if (ret < 0) + return ret; + return 0; +} + +static int linkyum_set_wol(struct phy_device *phydev, struct ethtool_wolinfo *wol) +{ + int ret, val, i, phymode; + linkyum_wol_cfg_t wol_cfg; + + phymode = LYPHY_MODE_CURR; + memset(&wol_cfg,0,sizeof(linkyum_wol_cfg_t)); + + if (wol->wolopts & WAKE_MAGIC) { + if (phymode == LYPHY_PORT_TYPE_UTP || phymode == LYPHY_PORT_TYPE_COMBO) { + /* Enable the WOL interrupt */ + val = lyphy_page_read(phydev, LYPHY_REG_UTP_SPACE, LINKYUM_INTR_REG); + val |= LINKYUM_WOL_INTR_EN; + ret = lyphy_page_write(phydev, LYPHY_REG_UTP_SPACE, LINKYUM_INTR_REG, val); + if (ret < 0) + return ret; + } + if (phymode == LYPHY_PORT_TYPE_FIBER || phymode == LYPHY_PORT_TYPE_COMBO) { + /* Enable the WOL interrupt */ + val = lyphy_page_read(phydev, LYPHY_REG_FIBER_SPACE, LINKYUM_INTR_REG); + val |= LINKYUM_WOL_INTR_EN; + ret = lyphy_page_write(phydev, LYPHY_REG_FIBER_SPACE, LINKYUM_INTR_REG, val); + if (ret < 0) + return ret; + } + /* Set the WOL config */ + wol_cfg.wolen = LYPHY_GLB_ENABLE; + wol_cfg.type = LYPHY_WOL_TYPE_PULSE; + wol_cfg.width = LYPHY_WOL_WIDTH_672MS; + wol_cfg.checkdst = LYPHY_GLB_ENABLE; + wol_cfg.checkcrc = LYPHY_GLB_ENABLE; + ret = linkyum_wol_en_cfg(phydev, wol_cfg); + if (ret < 0) + return ret; + + /* Store the device address for the magic packet */ + for(i = 0; i < 6; ++i) { + ret = linkyum_phy_ext_write(phydev, LINKYUM_MAGIC_PACKET_MAC_ADDR - i, + ((phydev->attached_dev->dev_addr[i]))); + if (ret < 0) + return ret; + } +#if LINKYUM_PHY_WOL_PASSWD_ENABLE + /* Set passwd for the magic packet */ + ret = linkyum_phy_ext_write(phydev, LINKYUM_MAGIC_PACKET_PASSWD_ADDR, LINKYUM_MAGIC_PACKET_PASSWD1); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LINKYUM_MAGIC_PACKET_PASSWD_ADDR - 1, LINKYUM_MAGIC_PACKET_PASSWD2); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LINKYUM_MAGIC_PACKET_PASSWD_ADDR - 2, LINKYUM_MAGIC_PACKET_PASSWD3); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LINKYUM_MAGIC_PACKET_PASSWD_ADDR - 3, LINKYUM_MAGIC_PACKET_PASSWD4); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LINKYUM_MAGIC_PACKET_PASSWD_ADDR - 4, LINKYUM_MAGIC_PACKET_PASSWD5); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LINKYUM_MAGIC_PACKET_PASSWD_ADDR - 5, LINKYUM_MAGIC_PACKET_PASSWD6); + if (ret < 0) + return ret; +#endif + } else { + wol_cfg.wolen = LYPHY_GLB_DISABLE; + wol_cfg.type = LYPHY_WOL_TYPE_EXT; + wol_cfg.width = LYPHY_WOL_WIDTH_EXT; + wol_cfg.checkdst = LYPHY_GLB_DISABLE; + wol_cfg.checkcrc = LYPHY_GLB_DISABLE; + ret = linkyum_wol_en_cfg(phydev, wol_cfg); + if (ret < 0) + return ret; + } + + if (val == LY1211_EXTREG_PHY_MODE_UTP_TO_SGMII) { + val = lyphy_page_read(phydev, LYPHY_REG_UTP_SPACE, MII_BMCR); + val |= LINKYUM_WOL_RESTARTANEG; + ret = lyphy_page_write(phydev, LYPHY_REG_UTP_SPACE, MII_BMCR, val); + if (ret < 0) + return ret; + } + + return 0; +} +#endif +static int ly1211_rxc_init(struct phy_device *phydev) +{ + int ret; + + ret = (linkyum_phy_ext_read(phydev, LY1211_EXTREG_GET_PORT_PHY_MODE) & + LY1211_EXTREG_PHY_MODE_MASK); + if (ret < 0) + return ret; + + if ((ret == LY1211_EXTREG_PHY_MODE_UTP_TO_SGMII) || + (ret == LY1211_EXTREG_PHY_MODE_UTP_TO_FIBER_AUTO) || + (ret == LY1211_EXTREG_PHY_MODE_UTP_TO_FIBER_FORCE)) + return 0; + + // Init rxc and enable rxc + if (ret == LY1211_EXTREG_PHY_MODE_UTP_TO_RGMII) { + ret = phy_read(phydev, 0x11); + if ((ret & 0x4) == 0x0) { + ret = linkyum_phy_ext_write(phydev,0x1E0C, 0x17); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev,0x1E58, 0x00); + if (ret < 0) + return ret; + } + } + + +#if LINKYUM_PHY_RXC_DELAY_SET_ENABLE + // Init rxc delay + ret = linkyum_phy_ext_write(phydev,0x0282, LINKYUM_PHY_RXC_DELAY_VAL); + if (ret < 0) + return ret; +#endif + + return ret; +} +static int ly1211_config_opt(struct phy_device *phydev) +{ + int ret; + //100M utp optimise + ret = linkyum_phy_ext_write(phydev, 0x0149, 0x84); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_write(phydev, 0x014A, 0x86); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_write(phydev, 0x023C, 0x81); + if (ret < 0) + return ret; + + //1000M utp optimise + ret = linkyum_phy_ext_write(phydev, 0x0184, 0x85); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_write(phydev, 0x0185, 0x86); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_write(phydev, 0x0186, 0x85); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_write(phydev, 0x0187, 0x86); + if (ret < 0) + return ret; + return ret; +} + +#if LINKYUM_PHY_CLK_OUT_125M_ENABLE +static int ly1211_clkout_init(struct phy_device *phydev) +{ + int ret; + + ret = linkyum_phy_ext_write(phydev, 0x0272 , 0x49); + if (ret < 0) + return ret; + + return ret; +} +#endif + +#if LINKYUM_PHY_MODE_SET_ENABLE +//set mode +static int phy_mode_set(struct phy_device *phydev, u16 phyMode) +{ + int ret, num = 0; + + ret = linkyum_phy_ext_read(phydev, 0xC417); + if (ret < 0) + return ret; + + ret = (ret & 0xF0) | (0x8 | phyMode); + + ret = linkyum_phy_ext_write(phydev, 0xC417, ret); + if (ret < 0) + return ret; + + while ((linkyum_phy_ext_read(phydev, 0xC415) & 0x07) != phyMode) { + msleep(10); + if(++num == 5) { + printk("Phy Mode Set Time Out!\r\n"); + break; + } + } + + while (linkyum_phy_ext_read(phydev, 0xC413) != 0) { + msleep(10); + if(++num == 10) { + printk("Phy Mode Set Time Out!\r\n"); + break; + } + } + + return 0; +} +#endif + +#if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE) +static int genphy_config_init(struct phy_device *phydev) +{ + int val; + u32 features; + + features = (SUPPORTED_TP | SUPPORTED_MII + | SUPPORTED_AUI | SUPPORTED_FIBRE | + SUPPORTED_BNC | SUPPORTED_Pause | SUPPORTED_Asym_Pause); + + /* Do we support autonegotiation? */ + val = phy_read(phydev, MII_BMSR); + if (val < 0) + return val; + + if (val & BMSR_ANEGCAPABLE) + features |= SUPPORTED_Autoneg; + + if (val & BMSR_100FULL) + features |= SUPPORTED_100baseT_Full; + if (val & BMSR_100HALF) + features |= SUPPORTED_100baseT_Half; + if (val & BMSR_10FULL) + features |= SUPPORTED_10baseT_Full; + if (val & BMSR_10HALF) + features |= SUPPORTED_10baseT_Half; + + if (val & BMSR_ESTATEN) { + val = phy_read(phydev, MII_ESTATUS); + if (val < 0) + return val; + + if (val & ESTATUS_1000_TFULL) + features |= SUPPORTED_1000baseT_Full; + if (val & ESTATUS_1000_THALF) + features |= SUPPORTED_1000baseT_Half; + } + + phydev->supported &= features; + phydev->advertising &= features; + + return 0; +} +#endif + +int ly1241_power_updown_init(struct phy_device *phydev) +{ + int ret = 0, num = 0; + + ret = linkyum_phy_ext_read(phydev, 0xC419);//version + if (ret < 0 || ret > 0x02) + return ret; + + ret = linkyum_phy_ext_write(phydev, 0xC464, 0x01);//powerdown + if (ret < 0) + return ret; + + while (!(linkyum_phy_ext_read(phydev, 0xC465))) { + msleep(10); + if(++num == 5) { + printk("Phy powerdown Set Time Out!\r\n"); + break; + } + } + + ret = linkyum_phy_ext_write(phydev, 0x060E, 0x00); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_read(phydev, 0x20A4); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, 0x20A4, ret | 0x04); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_read(phydev, 0x20A4); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, 0x20A4, ret & ~(0x04)); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_write(phydev, 0x060E, 0x01); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_write(phydev, 0xC464, 0x00);//powerup + if (ret < 0) + return ret; + + msleep(10); + + return ret; +} + +#if LINKYUM_PHY_LY1241V2_CFG_ENABLE +static int ly1241phy_load_cfg(struct phy_device *phydev) +{ + int i, ver, ret; + + // get version + ret = linkyum_phy_ext_read(phydev, 0xC419); + if (ret < 0) + return ret; + ver = ret; + + // disable startup patch and restart + ret = linkyum_phy_ext_write(phydev, LY_CFG_INIT, 0x85); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY_CFG_WAIT, 0x01); + if (ret < 0) + return ret; + ret = linkyum_phy_ext_write(phydev, LY_CFG_EPHY_INIT, 0x85); + if (ret < 0) + return ret; + + // init exp write, addr auto inc for fast load cfg + ret = phy_write(phydev, 0x16, 0x01); + if (ret < 0) + return ret; + + ret = phy_write(phydev, 0x0E, LY_CFG_ADDR); + if (ret < 0) + return ret; + +#if LINKYUM_PHY_LY1241V2_CFG_ENABLE + for(i = 0; i < sizeof(LINKYUM_LY1241V2_CFG) / sizeof(LINKYUM_LY1241V2_CFG[0]); i++) { + ret = phy_write(phydev, 0x0D, LINKYUM_LY1241V2_CFG[i]); + if (ret < 0) + return ret; + } +#endif + + ret = phy_write(phydev, 0x16, 0x00); + if (ret < 0) + return ret; + + ret = linkyum_phy_ext_write(phydev, LY_CFG_WAIT, 0x00); + if (ret < 0) + return ret; + + i = 0; + + // check + while (!((linkyum_phy_ext_read(phydev, LY_CFG_ENABLE) == 0x85) && + (linkyum_phy_ext_read(phydev, LY_CFG_ENABLE_ACK) == 0x85))) { + msleep(10); + if(++i == 5) { + printk("Phy LY1241 CFG Load Time Out!\r\n"); + break; + } + } + + printk("Phy LY1241 CFG VERSION : 0x%x\r\n", linkyum_phy_ext_read(phydev, 0xC461)); + + return 0; +} +#endif + +int ly1241_config_init(struct phy_device *phydev) +{ + int ret; + +#if LINKYUM_PHY_LY1241V2_CFG_ENABLE + ret = ly1241phy_load_cfg(phydev); + if (ret < 0) + return ret; +#endif + +#if (KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE) + ret = genphy_config_init(phydev); +#else + ret = genphy_read_abilities(phydev); +#endif + if (ret < 0) + return ret; + + +#if (KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE) + phydev->supported |= SUPPORTED_1000baseT_Full; + phydev->advertising |= SUPPORTED_1000baseT_Full; +#else + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported, ESTATUS_1000_TFULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->advertising, ESTATUS_1000_TFULL); +#endif + +#if LINKYUM_PHY_LINK_OPT_ENABLE + ret = ly1241_power_updown_init(phydev); + if (ret < 0) + return ret; +#endif + + return ly1241_led_init(phydev); +} + +int ly1210_power_updown_init(struct phy_device *phydev) +{ + int ret = 0, num = 0; + + ret = ly1211_phy_ext_read(phydev, 0xC419);//version + if (ret < 0 || ret > 0x02) + return ret; + + ret = ly1211_phy_ext_write(phydev, 0xC464, 0x01);//powerdown + if (ret < 0) + return ret; + + while (!(ly1211_phy_ext_read(phydev, 0xC465))) { + msleep(10); + if(++num == 5) { + printk("Phy powerdown Set Time Out!\r\n"); + break; + } + } + + ret = ly1211_phy_ext_write(phydev, 0x060E, 0x00); + if (ret < 0) + return ret; + + ret = ly1211_phy_ext_read(phydev, 0x20A4); + if (ret < 0) + return ret; + ret = ly1211_phy_ext_write(phydev, 0x20A4, ret | 0x20); + if (ret < 0) + return ret; + + ret = ly1211_phy_ext_read(phydev, 0x20A4); + if (ret < 0) + return ret; + ret = ly1211_phy_ext_write(phydev, 0x20A4, ret & ~(0x20)); + if (ret < 0) + return ret; + + ret = ly1211_phy_ext_write(phydev, 0x060E, 0x01); + if (ret < 0) + return ret; + + ret = ly1211_phy_ext_write(phydev, 0xC464, 0x00);//powerup + if (ret < 0) + return ret; + + msleep(10); + + return ret; +} + + +int ly1210_config_init(struct phy_device *phydev) +{ + int ret = 0; +#if LINKYUM_PHY_LINK_OPT_ENABLE + ret = ly1210_power_updown_init(phydev); + if (ret < 0) + return ret; +#endif + + return ret; +} + + +int ly1211_power_updown_init(struct phy_device *phydev) +{ + int ret = 0, num = 0, temp = 0; + + ret = ly1211_phy_ext_read(phydev, 0xC419);//version + if (ret < 0 || ret > 0x02) + return ret; + + ret = phy_read(phydev, 0x1C); + if (ret < 0) + return ret; + temp = ret; + + ret = phy_write(phydev, 0x1C, 0x4000);//powerdown + if (ret < 0) + return ret; + + while (ly1211_phy_ext_read(phydev, 0x40) || ly1211_phy_ext_read(phydev, 0x256)) { + msleep(10); + if(++num == 5) { + printk("Phy powerdown Set Time Out!\r\n"); + break; + } + } + + ret = ly1211_phy_ext_write(phydev, 0x060E, 0x00); + if (ret < 0) + return ret; + + ret = ly1211_phy_ext_read(phydev, 0x20A4); + if (ret < 0) + return ret; + ret = ly1211_phy_ext_write(phydev, 0x20A4, ret | 0x04); + if (ret < 0) + return ret; + + ret = ly1211_phy_ext_read(phydev, 0x20A4); + if (ret < 0) + return ret; + ret = ly1211_phy_ext_write(phydev, 0x20A4, ret & ~(0x04)); + if (ret < 0) + return ret; + + ret = ly1211_phy_ext_write(phydev, 0x060E, 0x01); + if (ret < 0) + return ret; + + ret = phy_write(phydev, 0x1C, temp);//powerup + if (ret < 0) + return ret; + + while (ly1211_phy_ext_read(phydev, 0x40) || ly1211_phy_ext_read(phydev, 0x256)) { + msleep(10); + if(++num == 5) { + printk("Phy powerup Set Time Out!\r\n"); + break; + } + } + + return ret; +} + +#if LINKYUM_PHY_LY1211_CFG_ENABLE || LINKYUM_PHY_LY1211V2_CFG_ENABLE || LINKYUM_PHY_LY1211V3_CFG_ENABLE +static int ly1211phy_load_cfg(struct phy_device *phydev) +{ + int i, ver, ret; + + // get version + ret = ly1211_phy_ext_read(phydev, 0xC419); + if (ret < 0) + return ret; + ver = ret; + + // disable startup patch and restart + ret = ly1211_phy_ext_write(phydev, LY_CFG_INIT, 0x85); + if (ret < 0) + return ret; + ret = ly1211_phy_ext_write(phydev, LY_CFG_WAIT, 0x01); + if (ret < 0) + return ret; + ret = ly1211_phy_ext_write(phydev, LY_CFG_EPHY_INIT, 0x85); + if (ret < 0) + return ret; + + // init exp write, addr auto inc for fast load cfg + ret = phy_write(phydev, 0x16, 0x01); + if (ret < 0) + return ret; + + ret = phy_write(phydev, 0x0E, LY_CFG_ADDR); + if (ret < 0) + return ret; + +#if LINKYUM_PHY_LY1211_CFG_ENABLE + if (ver == 0x01) { + for(i = 0; i < sizeof(LINKYUM_LY1211_CFG) / sizeof(LINKYUM_LY1211_CFG[0]); i++) { + ret = phy_write(phydev, 0x0D, LINKYUM_LY1211_CFG[i]); + if (ret < 0) + return ret; + } + } +#endif + +#if LINKYUM_PHY_LY1211V2_CFG_ENABLE + if (ver == 0x02) { + for(i = 0; i < sizeof(LINKYUM_LY1211V2_CFG) / sizeof(LINKYUM_LY1211V2_CFG[0]); i++) { + ret = phy_write(phydev, 0x0D, LINKYUM_LY1211V2_CFG[i]); + if (ret < 0) + return ret; + } + } +#endif + +#if LINKYUM_PHY_LY1211V3_CFG_ENABLE + if (ver == 0x03) { + for(i = 0; i < sizeof(LINKYUM_LY1211V3_CFG) / sizeof(LINKYUM_LY1211V3_CFG[0]); i++) { + ret = phy_write(phydev, 0x0D, LINKYUM_LY1211V3_CFG[i]); + if (ret < 0) + return ret; + } + } +#endif + + ret = phy_write(phydev, 0x16, 0x00); + if (ret < 0) + return ret; + + ret = ly1211_phy_ext_write(phydev, LY_CFG_WAIT, 0x00); + if (ret < 0) + return ret; + + i = 0; + + // check + while (!((linkyum_phy_ext_read(phydev, LY_CFG_ENABLE) == 0x85) && + (linkyum_phy_ext_read(phydev, LY_CFG_ENABLE_ACK) == 0x85))) { + msleep(10); + if(++i == 5) { + printk("Phy LY1211 CFG Load Time Out!\r\n"); + break; + } + } + + printk("Phy LY1211 CFG VERSION : 0x%x\r\n", linkyum_phy_ext_read(phydev, 0xC461)); + + return 0; +} +#endif + +int ly1211_config_init(struct phy_device *phydev) +{ + int ret, phymode; + +#if LINKYUM_PHY_LY1211_CFG_ENABLE || LINKYUM_PHY_LY1211V2_CFG_ENABLE || LINKYUM_PHY_LY1211V3_CFG_ENABLE + ret = ly1211phy_load_cfg(phydev); + if (ret < 0) + return ret; +#endif + +#if LINKYUM_PHY_WOL_FEATURE_ENABLE + struct ethtool_wolinfo wol; +#endif + +#if LINKYUM_PHY_MODE_SET_ENABLE + ret = phy_mode_set(phydev, 0x0); + if (ret < 0) + return ret; +#endif + phymode = LYPHY_MODE_CURR; + + if (phymode == LYPHY_PORT_TYPE_UTP || phymode == LYPHY_PORT_TYPE_COMBO) { + linkyum_phy_select_reg_page(phydev, LYPHY_REG_UTP_SPACE); +#if (KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE) + ret = genphy_config_init(phydev); +#else + ret = genphy_read_abilities(phydev); +#endif + if (ret < 0) + return ret; + } else { + linkyum_phy_select_reg_page(phydev, LYPHY_REG_FIBER_SPACE); +#if (KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE) + ret = genphy_config_init(phydev); + if (ret < 0) + return ret; +#else + ret = genphy_read_abilities(phydev); + if (ret < 0) + return ret; +#endif + +#if (KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE) + phydev->supported |= SUPPORTED_1000baseT_Full; + phydev->advertising |= SUPPORTED_1000baseT_Full; +#else + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->supported, ESTATUS_1000_TFULL); + linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, + phydev->advertising, ESTATUS_1000_TFULL); +#endif + } + +#if LINKYUM_PHY_LINK_OPT_ENABLE + ret = ly1211_power_updown_init(phydev); + if (ret < 0) + return ret; +#endif + + ret = ly1211_rxc_init(phydev); + if (ret < 0) + return ret; + + ret = ly1211_config_opt(phydev); + if (ret < 0) + return ret; + +#if LINKYUM_PHY_CLK_OUT_125M_ENABLE + ret = ly1211_clkout_init(phydev); + if (ret < 0) + return ret; +#endif + +#if LINKYUM_PHY_WOL_FEATURE_ENABLE + wol.wolopts = 0; + wol.supported = WAKE_MAGIC; + wol.wolopts |= WAKE_MAGIC; + linkyum_set_wol(phydev, &wol); +#endif + + return ly1121_led_init(phydev); +} + +static int ly1211_update_link(struct phy_device *phydev) +{ + int ret, val, phymode; + + phymode = LYPHY_MODE_CURR; + + if (phymode == LYPHY_PORT_TYPE_UTP || phymode == LYPHY_PORT_TYPE_COMBO) { + /* Do a fake read */ + ret = lyphy_page_read(phydev, LYPHY_REG_UTP_SPACE, MII_BMSR); + if (ret < 0) + return ret; + + /* Read link and autonegotiation status */ + ret = lyphy_page_read(phydev, LYPHY_REG_UTP_SPACE, MII_BMSR); + if (ret < 0) + return ret; + if ((ret & BMSR_LSTATUS) == 0) { + phydev->link = LYPHY_LINK_DOWN; + } else { + phydev->link = LYPHY_LINK_UP; + + val = linkyum_phy_ext_read(phydev, LY1211_EXTREG_GET_PORT_PHY_MODE) & LY1211_EXTREG_PHY_MODE_MASK; + if (val < 0) + return val; + if (val == LY1211_EXTREG_PHY_MODE_UTP_OR_FIBER_TO_RGMII) { + linkyum_phy_select_reg_page(phydev, LYPHY_REG_UTP_SPACE); + } + return LYPHY_REG_UTP_SPACE; + } + } + + if (phymode == LYPHY_PORT_TYPE_FIBER || phymode == LYPHY_PORT_TYPE_COMBO) { + /* Do a fake read */ + ret = lyphy_page_read(phydev, LYPHY_REG_FIBER_SPACE, MII_BMSR); + if (ret < 0) + return ret; + + /* Read link and autonegotiation status */ + ret = lyphy_page_read(phydev, LYPHY_REG_FIBER_SPACE, MII_BMSR); + if (ret < 0) + return ret; + + if ((ret & BMSR_LSTATUS) == 0) { + phydev->link = LYPHY_LINK_DOWN; + return LYPHY_REG_UTP_SPACE; + } else { + val = linkyum_phy_ext_read(phydev, LY1211_EXTREG_GET_PORT_PHY_MODE) & LY1211_EXTREG_PHY_MODE_MASK; + if (val < 0) + return val; + if (val != LY1211_EXTREG_PHY_MODE_SGMII_MAC_TO_RGMII_PHY) { + if (val == LY1211_EXTREG_PHY_MODE_UTP_OR_FIBER_TO_RGMII) { + linkyum_phy_select_reg_page(phydev, LYPHY_REG_FIBER_SPACE); + } + phydev->link = LYPHY_LINK_UP; + } else { + ret = lyphy_page_read(phydev, LYPHY_REG_FIBER_SPACE, 0x1C); + if ((ret & 0x8000) == 0) { + phydev->link = LYPHY_LINK_DOWN; + } else { + phydev->link = LYPHY_LINK_UP; + } + } + return LYPHY_REG_FIBER_SPACE; + } + } + return LYPHY_REG_UTP_SPACE; +} + +static int ly1211_read_status(struct phy_device *phydev) +{ + int val, ret, lpa, page; + + /* Update the link, but return if there was an error */ + ret = ly1211_update_link(phydev); + if (ret < 0) + return ret; + page = ret; + + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + phydev->pause = 0; + phydev->asym_pause = 0; + + val = lyphy_page_read(phydev, page, LINKYUM_SPEC_REG); + if (val < 0) + return val; + + lpa = lyphy_page_read(phydev, page, MII_LPA); + if (lpa < 0) + return lpa; + + if (val & 0x02) + phydev->duplex = DUPLEX_FULL; + if ((val & 0x18) == 0x10) + phydev->speed = SPEED_1000; + if ((val & 0x18) == 0x08) + phydev->speed = SPEED_100; + if (phydev->duplex == DUPLEX_FULL) { + if(page == LYPHY_REG_UTP_SPACE) { + phydev->pause = lpa & UTP_REG_PAUSE_CAP ? 1 : 0; + phydev->asym_pause = lpa & UTP_REG_PAUSE_ASYM ? 1 : 0; + } else { + phydev->pause = lpa & FIBER_REG_PAUSE_CAP ? 1 : 0; + phydev->asym_pause = lpa & FIBER_REG_PAUSE_ASYM ? 1 : 0; + } + } + return 0; +} + + +static int ly1241_update_link(struct phy_device *phydev) +{ + int ret; + + /* Do a fake read */ + ret = ly1241_phy_read(phydev, MII_BMSR); + if (ret < 0) + return ret; + + /* Read link and autonegotiation status */ + ret = ly1241_phy_read(phydev, MII_BMSR); + if (ret < 0) + return ret; + + if ((ret & BMSR_LSTATUS) == 0) + phydev->link = LYPHY_LINK_DOWN; + else + phydev->link = LYPHY_LINK_UP; + + return 0; +} + +static int ly1241_read_status(struct phy_device *phydev) +{ + int val, ret, lpa; + + /* Update the link, but return if there was an error */ + ret = ly1241_update_link(phydev); + if (ret < 0) + return ret; + + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + phydev->pause = 0; + phydev->asym_pause = 0; + + val = ly1241_phy_read(phydev, LINKYUM_SPEC_REG); + if (val < 0) + return val; + + lpa = ly1241_phy_read(phydev, MII_LPA); + if (lpa < 0) + return lpa; + + if (val & 0x02) + phydev->duplex = DUPLEX_FULL; + if ((val & 0x18) == 0x10) + phydev->speed = SPEED_1000; + if ((val & 0x18) == 0x08) + phydev->speed = SPEED_100; + if (phydev->duplex == DUPLEX_FULL) { + phydev->pause = lpa & UTP_REG_PAUSE_CAP ? 1 : 0; + phydev->asym_pause = lpa & UTP_REG_PAUSE_ASYM ? 1 : 0; + } + return 0; +} + +static int ly1210_read_status(struct phy_device *phydev) +{ + int val, ret, lpa; + + /* Update the link, but return if there was an error */ + ret = ly1241_update_link(phydev); + if (ret < 0) + return ret; + + phydev->speed = SPEED_10; + phydev->duplex = DUPLEX_HALF; + phydev->pause = 0; + phydev->asym_pause = 0; + + val = ly1241_phy_read(phydev, LINKYUM_SPEC_REG); + if (val < 0) + return val; + + lpa = ly1241_phy_read(phydev, MII_LPA); + if (lpa < 0) + return lpa; + + if (val & 0x02) + phydev->duplex = DUPLEX_FULL; + // if ((val & 0x18) == 0x10) + // phydev->speed = SPEED_1000; + if ((val & 0x18) == 0x08) + phydev->speed = SPEED_100; + if (phydev->duplex == DUPLEX_FULL) { + phydev->pause = lpa & UTP_REG_PAUSE_CAP ? 1 : 0; + phydev->asym_pause = lpa & UTP_REG_PAUSE_ASYM ? 1 : 0; + } + return 0; +} + +static struct phy_driver ly_phy_drivers[] = { + { + .phy_id = LY1210_PHY_ID, + .phy_id_mask = LINKYUM_PHY_ID_MASK, + .name = "LY1210A 100M Ethernet", + .features = PHY_BASIC_FEATURES, + .flags = PHY_POLL, + .config_init = ly1210_config_init, + .read_status = ly1210_read_status, +#if (KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE) + .write_mmd = genphy_write_mmd_unsupported, + .read_mmd = genphy_read_mmd_unsupported, +#endif + }, + + { + .phy_id = LY1211_PHY_ID, + .phy_id_mask = LINKYUM_PHY_ID_MASK, + .name = "LY1211A_LY1211S Gigabit Ethernet", + .features = PHY_GBIT_FEATURES, + .flags = PHY_POLL, + .config_init = ly1211_config_init, + .config_aneg = ly1211_config_aneg, +#if (KERNEL_VERSION(3, 14, 79) < LINUX_VERSION_CODE) + .aneg_done = ly1211_aneg_done, +#endif + .read_status = ly1211_read_status, +#if (KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE) + .write_mmd = genphy_write_mmd_unsupported, + .read_mmd = genphy_read_mmd_unsupported, +#endif + .suspend = genphy_suspend, + .resume = genphy_resume, +#if LINKYUM_PHY_WOL_FEATURE_ENABLE + .get_wol = &linkyum_get_wol, + .set_wol = &linkyum_set_wol, +#endif + }, + + { + .phy_id = LY1241_PHY_ID, + .phy_id_mask = LINKYUM_PHY_ID_MASK, + .name = "LY1241A_LY1241B Gigabit Ethernet", + .features = PHY_GBIT_FEATURES, + .flags = PHY_POLL, + .config_init = ly1241_config_init, + .config_aneg = genphy_config_aneg, + .read_status = ly1241_read_status, +#if (KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE) + .write_mmd = genphy_write_mmd_unsupported, + .read_mmd = genphy_read_mmd_unsupported, +#endif + .suspend = genphy_suspend, + .resume = genphy_resume, + }, +}; + +#if (KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE) +static int ly_phy_drivers_register(struct phy_driver *phy_drvs, int size) +{ + int i, j; + int ret; + + for (i = 0; i < size; i++) { + ret = phy_driver_register(&phy_drvs[i]); + if (ret) + goto err; + } + + return 0; + +err: + for (j = 0; j < i; j++) + phy_driver_unregister(&phy_drvs[j]); + + return ret; +} + +static void ly_phy_drivers_unregister(struct phy_driver *phy_drvs, int size) +{ + int i; + + for (i = 0; i < size; i++) + phy_driver_unregister(&phy_drvs[i]); +} + +static int __init ly_phy_init(void) +{ + return ly_phy_drivers_register(ly_phy_drivers, ARRAY_SIZE(ly_phy_drivers)); +} + +static void __exit ly_phy_exit(void) +{ + ly_phy_drivers_unregister(ly_phy_drivers, ARRAY_SIZE(ly_phy_drivers)); +} + +module_init(ly_phy_init); +module_exit(ly_phy_exit); +#else +/* for linux 4.x */ +module_phy_driver(ly_phy_drivers); +#endif + +static struct mdio_device_id __maybe_unused linkyum_phy_tbl[] = { + { LY1210_PHY_ID, LINKYUM_PHY_ID_MASK }, + { LY1211_PHY_ID, LINKYUM_PHY_ID_MASK }, + { LY1241_PHY_ID, LINKYUM_PHY_ID_MASK }, + {}, +}; + +MODULE_DEVICE_TABLE(mdio, linkyum_phy_tbl); + +MODULE_DESCRIPTION("Linkyum PHY driver"); +MODULE_AUTHOR("Huxl"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 25dcaa49ab8be9d2ea75ae46a38a4bf3b69dca3a..6a7635be65c330534eea0b17937397f01a8d39f6 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -632,7 +632,8 @@ static bool mdiobus_prevent_c45_scan(struct mii_bus *bus) continue; oui = phydev->phy_id >> 10; - if (oui == MICREL_OUI) + /* skip c45 scan when linkyum phy */ + if (oui == MICREL_OUI || oui == 0x2B6D01) return true; } return false; diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index d0763fc364980484e092fec69b79609728fb2579..e800d35e431da9f5c4b448c506e4f8c9918f9308 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -1558,6 +1558,8 @@ static int yt8521_read_status(struct phy_device *phydev) ytxxxx_adjust_status(phydev, val_utp, 1); } else { link_utp = 0; + phydev->speed = SPEED_UNKNOWN; + phydev->duplex = DUPLEX_UNKNOWN; } if (link_utp) { diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index dcdbfcf404ddf449481055f16d9a4d05b06a9530..4b105878aa044498ee3df614b3c8ab5c88031a57 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -20,6 +20,9 @@ #include #include #include "pciehp.h" +#ifdef CONFIG_ARCH_PHYTIUM +#include "../pci.h" +#endif /* The following routines constitute the bulk of the hotplug controller logic @@ -282,6 +285,10 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) if (link_active) ctrl_info(ctrl, "Slot(%s): Link Up\n", slot_name(ctrl)); +#ifdef CONFIG_ARCH_PHYTIUM + if (present && link_active) + phytium_clear_ctrl_prot(ctrl->pcie->port, PHYTIUM_PCIE_HOTPLUG); +#endif ctrl->request_result = pciehp_enable_slot(ctrl); break; default: diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 00d8fbbc525a3daa4b4cd0919fb24d07693936cc..c5506bd0a2332fe90ebfa1d248d1248dc9c9825f 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -36,6 +36,9 @@ #ifdef CONFIG_PSWIOTLB #include #endif +#ifdef CONFIG_ARCH_PHYTIUM +#include +#endif DEFINE_MUTEX(pci_slot_mutex); @@ -4534,15 +4537,6 @@ void __weak pcibios_set_master(struct pci_dev *dev) */ void pci_set_master(struct pci_dev *dev) { -#ifdef CONFIG_PSWIOTLB - if ((pswiotlb_force_disable != true) && - is_phytium_ps_socs()) { - dev->dev.can_use_pswiotlb = pswiotlb_is_dev_in_passthroughlist(dev); - dev_info(&dev->dev, "The device %s use pswiotlb because vendor 0x%04x %s in pswiotlb passthroughlist\n", - dev->dev.can_use_pswiotlb ? "would" : "would NOT", - dev->vendor, dev->dev.can_use_pswiotlb ? "is NOT" : "is"); - } -#endif __pci_set_master(dev, true); pcibios_set_master(dev); } @@ -5257,6 +5251,10 @@ void pci_reset_secondary_bus(struct pci_dev *dev) ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl); + +#ifdef CONFIG_ARCH_PHYTIUM + phytium_clear_ctrl_prot(dev, PHYTIUM_PCIE_HOTRESET); +#endif } void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) @@ -5273,6 +5271,17 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) */ int pci_bridge_secondary_bus_reset(struct pci_dev *dev) { +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pd2308()) { + int ret = 0; + + pci_save_state(dev); + pcibios_reset_secondary_bus(dev); + ret = pci_bridge_wait_for_secondary_bus(dev, "bus reset"); + pci_restore_state(dev); + return ret; + } +#endif pcibios_reset_secondary_bus(dev); return pci_bridge_wait_for_secondary_bus(dev, "bus reset"); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d5e9010a135a14a7874664847ab4df334bd31a1f..2fe6edc95d6ec5d115a86761457695be5162a527 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -832,4 +832,46 @@ static inline pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) (PCI_CONF1_ADDRESS(bus, dev, func, reg) | \ PCI_CONF1_EXT_REG(reg)) +#ifdef CONFIG_ARCH_PHYTIUM +#include + +#define PHYTIUM_PCIE_HOTRESET 0 +#define PHYTIUM_PCIE_HOTPLUG 1 +#define PHYTIUM_PCI_VENDOR_ID 0x1DB7 +#define PHYTIUM_PCI_CTRL_ID 0x0100 +#define PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID 0xC2000020 + +static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) +{ + int socket; + u8 bus = pdev->bus->number; + u8 device = PCI_SLOT(pdev->devfn); + u8 function = PCI_FUNC(pdev->devfn); + u16 vendor_id = pdev->vendor; + u16 device_id = pdev->device; + struct arm_smccc_res res; + u32 arg; + + if (vendor_id != PHYTIUM_PCI_VENDOR_ID || + device_id != PHYTIUM_PCI_CTRL_ID || + pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) + return; + + socket = dev_to_node(&pdev->dev); + if (socket < 0) { + pci_err(pdev, "Cannot find socket, stop clean pcie protection\n"); + return; + } + + arg = (socket << 16) | (bus << 8) | (device << 3) | function; + arm_smccc_smc(PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID, arg, op, 0, 0, 0, 0, 0, &res); + if (res.a0 != 0) + pci_err(pdev, "Error: Firmware call PCIE protection clear Failed: %d, sbdf: 0x%x\n", + (int)res.a0, arg); + else + pci_info(pdev, "%s : Clear pcie protection successfully\n", + op ? "HotPlug" : "HotReset"); +} +#endif + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 399b1def4ccd8d0e1fbe007a31d2eb2de5ac5d60..86f4aec816c3fe75273a3abe45dc21450e4f8157 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5159,6 +5159,10 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_ZHAOXIN, PCI_ANY_ID, pci_quirk_zhaoxin_pcie_ports_acs }, /* Wangxun nics */ { PCI_VENDOR_ID_WANGXUN, PCI_ANY_ID, pci_quirk_wangxun_nic_acs }, + /* Phytium Technology */ + { 0x10b5, PCI_ANY_ID, pci_quirk_xgene_acs }, + { 0x17cd, PCI_ANY_ID, pci_quirk_xgene_acs }, + { 0x1db7, PCI_ANY_ID, pci_quirk_xgene_acs }, { 0 } }; diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index 8fd6f997c4b117dcc07f36a265ccad05fa2a7a6a..2443262f232f199bd6d68a7ff8116887a19bafd2 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -31,9 +32,8 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt - #define PHYTIUM_DDR_MAX_COUNTERS 8 -#define DDR_PERF_DRIVER_VERSION "1.3.0" +#define DDR_PERF_DRIVER_VERSION "1.3.3" #define DDR_START_TIMER 0x000 #define DDR_STOP_TIMER 0x004 @@ -57,34 +57,17 @@ #define DDR_CLK_FRE 0xe00 #define DDR_DATA_WIDTH 0xe04 -#define DDR_PMU_OFL_STOP_TYPE_VAL 0x10 +#define DDR_PMU_OFL_STOP_TYPE_VAL 0x10 -#define SOC_ID_PS230XX 0x8 -#define SOC_ID_PS240XX 0x6 -#define MIDR_PSXX 0x700F8620 +#define PBFVER_FUNC_ID 0x82000001 #define to_phytium_ddr_pmu(p) (container_of(p, struct phytium_ddr_pmu, pmu)) enum { - PS230XX = 0x01, - PS240XX = 0x02, + DDRV1P0 = 0x01, + DDRV1P5 = 0x02, }; -static inline int phytium_socs_type(void) -{ - unsigned int soc_id, cpu_id; - - soc_id = read_sysreg_s(SYS_AIDR_EL1); - cpu_id = read_cpuid_id(); - - if ((soc_id == SOC_ID_PS230XX) && (cpu_id == MIDR_PSXX)) - return PS230XX; - else if ((soc_id == SOC_ID_PS240XX) && (cpu_id == MIDR_PSXX)) - return PS240XX; - else - return 0; -} - static int phytium_ddr_pmu_hp_state; struct phytium_ddr_pmu_hwevents { @@ -105,7 +88,7 @@ struct phytium_ddr_pmu { int irq_bit; int on_cpu; int irq; - int soc_version; + int ver; struct hlist_node node; }; @@ -118,7 +101,6 @@ static const u32 ddr_counter_reg_offset[] = { DDR_EVENT_RXREQ_WNSF, DDR_EVENT_BANDWIDTH }; - ssize_t phytium_ddr_pmu_format_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -126,6 +108,7 @@ ssize_t phytium_ddr_pmu_format_sysfs_show(struct device *dev, struct dev_ext_attribute *eattr; eattr = container_of(attr, struct dev_ext_attribute, attr); + return sprintf(buf, "%s\n", (char *)eattr->var); } @@ -141,7 +124,7 @@ ssize_t phytium_ddr_pmu_event_sysfs_show(struct device *dev, } static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(dev_get_drvdata(dev)); @@ -151,12 +134,12 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, #define PHYTIUM_PMU_ATTR(_name, _func, _config) \ (&((struct dev_ext_attribute[]){ \ - { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ - .attr.attr) + { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ + .attr.attr) #define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ - (void *)_config) + (void *)_config) #define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ @@ -173,13 +156,13 @@ static const struct attribute_group phytium_ddr_pmu_format_group = { }; static struct attribute *phytium_ddr_pmu_events_attr[] = { - PHYTIUM_DDR_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_DDR_PMU_EVENT_ATTR(ddr_cycles, 0x00), PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq, 0x01), PHYTIUM_DDR_PMU_EVENT_ATTR(rxdat, 0x02), PHYTIUM_DDR_PMU_EVENT_ATTR(txdat, 0x03), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_RNS, 0x04), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_WNSP, 0x05), - PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_WNSF, 0x06), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_rns, 0x04), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_wnsp, 0x05), + PHYTIUM_DDR_PMU_EVENT_ATTR(rxreq_wnsf, 0x06), PHYTIUM_DDR_PMU_EVENT_ATTR(bandwidth, 0x07), NULL, }; @@ -246,7 +229,7 @@ static void phytium_ddr_pmu_enable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - if (ddr_pmu->soc_version == PS240XX) + if (ddr_pmu->ver == DDRV1P5) return; val = readl(ddr_pmu->cfg_base); @@ -258,7 +241,7 @@ static void phytium_ddr_pmu_disable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - if (ddr_pmu->soc_version == PS240XX) + if (ddr_pmu->ver == DDRV1P5) return; val = readl(ddr_pmu->cfg_base); @@ -408,11 +391,9 @@ void phytium_ddr_pmu_event_del(struct perf_event *event, int flags) { struct phytium_ddr_pmu *ddr_pmu = to_phytium_ddr_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; - unsigned long val; phytium_ddr_pmu_event_stop(event, PERF_EF_UPDATE); - val = phytium_ddr_pmu_get_irq_flag(ddr_pmu); - val = phytium_ddr_pmu_get_stop_state(ddr_pmu); + phytium_ddr_pmu_unmark_event(ddr_pmu, hwc->idx); perf_event_update_userpage(event); @@ -448,9 +429,8 @@ void phytium_ddr_pmu_reset(struct phytium_ddr_pmu *ddr_pmu) } static const struct acpi_device_id phytium_ddr_pmu_acpi_match[] = { - { - "PHYT0043", - }, + { "PHYT0043", }, + { "PHYT0067", }, {}, }; MODULE_DEVICE_TABLE(acpi, phytium_ddr_pmu_acpi_match); @@ -494,6 +474,48 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) return IRQ_NONE; } +static int phytium_verify_pbf_version(struct platform_device *pdev) +{ + struct arm_smccc_res res; + unsigned long major_ver, minor_ver; + + arm_smccc_smc(PBFVER_FUNC_ID, 0, 0, 0, 0, 0, 0, 0, &res); + if (res.a0 <= 0) { + dev_warn(&pdev->dev, "Can not recognize PBF Firmware version!\n"); + return -EINVAL; + } + + minor_ver = res.a0 & 0xFFFF; + major_ver = (res.a0 >> 16) & 0xFFFF; + + if (major_ver < 1 || (major_ver == 1 && minor_ver < 20)) { + dev_err(&pdev->dev, + "Driver load failed, Please upgrade PBF Firmware version to 1.20 or later!\n"); + return -EINVAL; + } + + return 0; + +} + +static int phytium_ddr_pmu_version(struct platform_device *pdev, + struct phytium_ddr_pmu *ddr_pmu) +{ + struct acpi_device *acpi_dev; + + acpi_dev = ACPI_COMPANION(&pdev->dev); + if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0043")) { + ddr_pmu->ver = DDRV1P0; + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0067")) { + ddr_pmu->ver = DDRV1P5; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + } + + return 0; +} + static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, struct platform_device *pdev) { @@ -523,12 +545,6 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, { struct resource *res, *clkres, *irqres; - ddr_pmu->soc_version = phytium_socs_type(); - if (ddr_pmu->soc_version == 0) { - dev_err(&pdev->dev, "The DDR PMU driver can't be installed in this SoC!\n"); - return -EINVAL; - } - if (device_property_read_u32(&pdev->dev, "phytium,die-id", &ddr_pmu->die_id)) { dev_err(&pdev->dev, "Can not read phytium,die-id!\n"); @@ -569,7 +585,7 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, return PTR_ERR(ddr_pmu->cfg_base); } - if (ddr_pmu->soc_version == PS240XX) { + if (ddr_pmu->ver == DDRV1P5) { irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); if (!irqres) { dev_err(&pdev->dev, "failed for get ddr_pmu irq resource.\n"); @@ -594,6 +610,16 @@ static int phytium_ddr_pmu_dev_probe(struct platform_device *pdev, { int ret; + ret = phytium_ddr_pmu_version(pdev, ddr_pmu); + if (ret) + return ret; + + if (ddr_pmu->ver == DDRV1P0) { + ret = phytium_verify_pbf_version(pdev); + if (ret) + return ret; + } + ret = phytium_ddr_pmu_init_data(pdev, ddr_pmu); if (ret) return ret; diff --git a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c index df873b018c0e4897a18f40ac7285298a253e2b73..b4a9ff59b202ba3850e073437e67a4b1886ee104 100644 --- a/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c +++ b/drivers/perf/phytium/phytium_dmu_pmu_pd2408.c @@ -31,7 +31,7 @@ #undef pr_fmt #define pr_fmt(fmt) "pd2408_dmu_pmu: " fmt -#define DMU_PERF_DRIVER_VERSION "1.0.0" +#define DMU_PERF_DRIVER_VERSION "1.0.2" #define DMU_PMU_TIMER_START 0x0 #define DMU_PMU_TIMER_STOP 0x4 @@ -151,7 +151,7 @@ static const struct attribute_group pd2408_dmu_pmu_format_group = { }; static struct attribute *pd2408_dmu_pmu_events_attr[] = { - PHYTIUM_DMU_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_DMU_PMU_EVENT_ATTR(dmu_axi_cycles, 0x00), PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_flux, 0x01), PHYTIUM_DMU_PMU_EVENT_ATTR(axi_read_flux, 0x02), PHYTIUM_DMU_PMU_EVENT_ATTR(axi_write_cmd, 0x03), @@ -652,11 +652,13 @@ int pd2408_dmu_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) struct pd2408_dmu_pmu *dmu_pmu = hlist_entry_safe(node, struct pd2408_dmu_pmu, node); unsigned int target; + cpumask_t available_cpus; if (dmu_pmu->on_cpu != cpu) return 0; - target = cpumask_last(cpu_online_mask); + cpumask_andnot(&available_cpus, cpu_online_mask, cpumask_of(cpu)); + target = cpumask_last(&available_cpus); if (target >= nr_cpu_ids) { dev_err(dmu_pmu->dev, "offline cpu%d with no target to migrate.\n", diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index 80a931e4e4aee3c5584479a400892132d1889ae1..8f67636b0b99978274c81b82d8d3a36c46f9a697 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -31,72 +31,53 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_pcie_pmu: " fmt +#define PCIE_PERF_DRIVER_VERSION "1.3.1" + #define PHYTIUM_PCIE_MAX_COUNTERS 18 -#define PCIE_PERF_DRIVER_VERSION "1.3.0" -#define PCIE_START_TIMER 0x000 -#define PCIE_STOP_TIMER 0x004 -#define PCIE_CLEAR_EVENT 0x008 +#define PCIE_START_TIMER 0x000 +#define PCIE_STOP_TIMER 0x004 +#define PCIE_CLEAR_EVENT 0x008 -#define PCIE_EVENT_CYCLES 0x0e4 -#define PCIE_TPOINT_END_L 0x0e4 +#define PCIE_EVENT_CYCLES 0x0e4 +#define PCIE_TPOINT_END_L 0x0e4 #define PCIE_TPOINT_END_H 0x0e8 -#define PCIE_STATE_STOP 0x0ec +#define PCIE_STATE_STOP 0x0ec -#define PCIE_EVENT_AW 0x100 -#define PCIE_EVENT_W_LAST 0x104 -#define PCIE_EVENT_B 0x108 +#define PCIE_EVENT_AW 0x100 +#define PCIE_EVENT_W_LAST 0x104 +#define PCIE_EVENT_B 0x108 #define PCIE_EVENT_AR 0x10c -#define PCIE_EVENT_R_LAST 0x110 -#define PCIE_EVENT_R_FULL 0x114 -#define PCIE_EVENT_R_ERR 0x118 -#define PCIE_EVENT_W_ERR 0x11c -#define PCIE_EVENT_DELAY_RD 0x120 -#define PCIE_EVENT_DELAY_WR 0x124 -#define PCIE_EVENT_RD_MAX 0x128 -#define PCIE_EVENT_RD_MIN 0x12c -#define PCIE_EVENT_WR_MAX 0x130 -#define PCIE_EVENT_WR_MIN 0x134 - -#define PCIE_EVENT_W_DATA 0x200 -#define PCIE_W_DATA_BASE 0x200 +#define PCIE_EVENT_R_LAST 0x110 +#define PCIE_EVENT_R_FULL 0x114 +#define PCIE_EVENT_R_ERR 0x118 +#define PCIE_EVENT_W_ERR 0x11c +#define PCIE_EVENT_DELAY_RD 0x120 +#define PCIE_EVENT_DELAY_WR 0x124 +#define PCIE_EVENT_RD_MAX 0x128 +#define PCIE_EVENT_RD_MIN 0x12c +#define PCIE_EVENT_WR_MAX 0x130 +#define PCIE_EVENT_WR_MIN 0x134 + +#define PCIE_EVENT_W_DATA 0x200 +#define PCIE_W_DATA_BASE 0x200 #define PCIE_EVENT_RDELAY_TIME 0x300 -#define PCIE_RDELAY_TIME_BASE 0x300 - -#define PCIE_EVENT_WDELAY_TIME 0x700 -#define PCIE_WDELAY_TIME_BASE 0x700 +#define PCIE_RDELAY_TIME_BASE 0x300 -#define PCIE_DATA_WIDTH 0xe04 +#define PCIE_EVENT_WDELAY_TIME 0x700 +#define PCIE_WDELAY_TIME_BASE 0x700 -#define PCIE_PMU_OFL_STOP_TYPE_VAL 0x10 - -#define SYS_AIDR_EL1 sys_reg(3, 1, 0, 0, 7) -#define SOC_ID_PS230XX 0x8 -#define SOC_ID_PS240XX 0x6 -#define MIDR_PSXX 0x700f8620 +#define PCIE_DATA_WIDTH 0xe04 +#define PCIE_PMU_OFL_STOP_TYPE_VAL 0x10 #define to_phytium_pcie_pmu(p) (container_of(p, struct phytium_pcie_pmu, pmu)) enum { - PS230XX = 0x1, - PS240XX = 0x2, + PCIEV1P0 = 0x1, + PCIEV1P5 = 0x2, }; -static inline int phytium_socs_type(void) -{ - unsigned int soc_id, cpu_id; - - soc_id = read_sysreg_s(SYS_AIDR_EL1); - cpu_id = read_cpuid_id(); - - if ((soc_id == SOC_ID_PS230XX) && (cpu_id == MIDR_PSXX)) - return PS230XX; - else if ((soc_id == SOC_ID_PS240XX) && (cpu_id == MIDR_PSXX)) - return PS240XX; - else - return 0; -} static int phytium_pcie_pmu_hp_state; @@ -121,8 +102,8 @@ struct phytium_pcie_pmu { struct hlist_node node; int ctrler_id; int real_ctrler; + int ver; u32 clk_bits; - u32 soc_version; }; #define GET_PCIE_EVENTID(hwc) (hwc->config_base & 0x1F) @@ -194,7 +175,7 @@ static const struct attribute_group phytium_pcie_pmu_format_group = { }; static struct attribute *phytium_pcie_pmu_events_attr[] = { - PHYTIUM_PCIE_PMU_EVENT_ATTR(cycles, 0x00), + PHYTIUM_PCIE_PMU_EVENT_ATTR(pcie_cycles, 0x00), PHYTIUM_PCIE_PMU_EVENT_ATTR(aw, 0x01), PHYTIUM_PCIE_PMU_EVENT_ATTR(w_last, 0x02), PHYTIUM_PCIE_PMU_EVENT_ATTR(b, 0x03), @@ -259,7 +240,7 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, return 0; } - if (pcie_pmu->soc_version == PS240XX && pcie_pmu->pmu_id == 3) + if (pcie_pmu->ver == PCIEV1P5 && pcie_pmu->pmu_id == 3) rdelay_num = 63; switch (idx) { @@ -323,7 +304,7 @@ static void phytium_pcie_pmu_select_ctrler(struct phytium_pcie_pmu *pcie_pmu) u32 val, offset; u32 mask = 0xfffffffc; - if (pcie_pmu->soc_version == PS230XX) { + if (pcie_pmu->ver == PCIEV1P0) { if (pcie_pmu->pmu_id == 2) { mask = 0xffffffcf; offset = 0x0; @@ -428,7 +409,7 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) if (pcie_pmu->on_cpu == -1) return -EINVAL; - if (pcie_pmu->soc_version == PS240XX) { + if (pcie_pmu->ver == PCIEV1P5) { event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); if (pcie_pmu->pmu_id == 2) { if (event_ctrler == 0) @@ -563,11 +544,9 @@ void phytium_pcie_pmu_event_del(struct perf_event *event, int flags) { struct phytium_pcie_pmu *pcie_pmu = to_phytium_pcie_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; - unsigned long val; phytium_pcie_pmu_event_stop(event, PERF_EF_UPDATE); - val = phytium_pcie_pmu_get_irq_flag(pcie_pmu); - val = phytium_pcie_pmu_get_stop_state(pcie_pmu); + phytium_pcie_pmu_unmark_event(pcie_pmu, hwc->idx); perf_event_update_userpage(event); @@ -603,9 +582,8 @@ void phytium_pcie_pmu_reset(struct phytium_pcie_pmu *pcie_pmu) } static const struct acpi_device_id phytium_pcie_pmu_acpi_match[] = { - { - "PHYT0044", - }, + { "PHYT0044", }, + { "PHYT0068", }, {}, }; MODULE_DEVICE_TABLE(acpi, phytium_pcie_pmu_acpi_match); @@ -646,6 +624,25 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) return IRQ_NONE; } +static int phytium_pcie_pmu_version(struct platform_device *pdev, + struct phytium_pcie_pmu *pcie_pmu) +{ + struct acpi_device *acpi_dev; + + acpi_dev = ACPI_COMPANION(&pdev->dev); + if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0044")) { + pcie_pmu->ver = PCIEV1P0; + } else if (!strcmp(acpi_device_hid(acpi_dev), "PHYT0068")) { + pcie_pmu->ver = PCIEV1P5; + } else { + dev_err(&pdev->dev, "The current driver does not support this device.\n"); + return -ENODEV; + + } + + return 0; +} + static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, struct platform_device *pdev) { @@ -674,11 +671,6 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, struct phytium_pcie_pmu *pcie_pmu) { struct resource *res, *clkres, *irqres; - pcie_pmu->soc_version = phytium_socs_type(); - if (pcie_pmu->soc_version == 0) { - dev_err(&pdev->dev, "The PCIe PMU driver can't be installed in this SoC.\n"); - return -EINVAL; - } if (device_property_read_u32(&pdev->dev, "phytium,die-id", &pcie_pmu->die_id)) { @@ -692,7 +684,7 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - if (pcie_pmu->soc_version == PS230XX) { + if (pcie_pmu->ver == PCIEV1P0) { switch (pcie_pmu->pmu_id) { case 0: pcie_pmu->clk_bits = 0x1; @@ -780,6 +772,10 @@ static int phytium_pcie_pmu_dev_probe(struct platform_device *pdev, { int ret; + ret = phytium_pcie_pmu_version(pdev, pcie_pmu); + if (ret) + return ret; + ret = phytium_pcie_pmu_init_data(pdev, pcie_pmu); if (ret) return ret; @@ -817,7 +813,7 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) return ret; } - if (pcie_pmu->soc_version == PS230XX) + if (pcie_pmu->ver == PCIEV1P0) name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", pcie_pmu->die_id, pcie_pmu->pmu_id); else @@ -848,7 +844,7 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) phytium_pcie_pmu_enable_clk(pcie_pmu); - if (pcie_pmu->soc_version == PS230XX) + if (pcie_pmu->ver == PCIEV1P0) pr_info("die%d_pcie_pmu%d on cpu%d.\n", pcie_pmu->die_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); else diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c index e8094b931738c1ac57632feb802e735e9c749d17..c590e0b22f0aa11cc9e57bd0ee62e177a4ea4538 100644 --- a/drivers/ras/ras.c +++ b/drivers/ras/ras.c @@ -70,6 +70,16 @@ void log_arm_hw_error(struct cper_sec_proc_arm *err, const u8 sev) ven_err_data, vsei_len, sev, cpu); } +void log_phyt_err_event(struct cper_sec_phyt_err *err, const u8 sev) +{ + u32 phyt_len; + u8 *phyt; + + phyt_len = sizeof(struct cper_sec_phyt_err); + phyt = (u8 *)err; + trace_phyt_err_event(phyt_len, phyt, sev); +} + static int __init ras_init(void) { int rc = 0; @@ -87,6 +97,7 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(extlog_mem_event); EXPORT_TRACEPOINT_SYMBOL_GPL(mc_event); EXPORT_TRACEPOINT_SYMBOL_GPL(non_standard_event); EXPORT_TRACEPOINT_SYMBOL_GPL(arm_event); +EXPORT_TRACEPOINT_SYMBOL_GPL(phyt_err_event); static int __init parse_ras_param(char *str) { diff --git a/drivers/spi/spi-phytium-plat-v2.c b/drivers/spi/spi-phytium-plat-v2.c index 9801406d61e9322847c5d59a2f407418606db7b1..b346df744d90f8a5e28106216327123054354ea6 100644 --- a/drivers/spi/spi-phytium-plat-v2.c +++ b/drivers/spi/spi-phytium-plat-v2.c @@ -26,7 +26,7 @@ #include "spi-phytium.h" #define DRIVER_NAME_PHYT "phytium_spi_2.0" -#define DRIVER_VERSION "1.0.7" +#define DRIVER_VERSION "1.0.8" #define PHYTIUM_CPU_PART_FTC872 0x872 @@ -130,7 +130,7 @@ static int spi_phyt_probe(struct platform_device *pdev) int ret; int num_cs; int cs_gpio; - int global_cs = 0; + int global_cs = 1; int i; u32 clk_rate = SPI_DEFAULT_CLK; diff --git a/drivers/spi/spi-phytium-v2.c b/drivers/spi/spi-phytium-v2.c index dbf834f0e13c4002a2f33de9879a3e7453a10e07..c05b2457759cd61120075f81083c7f07fc1e1d88 100644 --- a/drivers/spi/spi-phytium-v2.c +++ b/drivers/spi/spi-phytium-v2.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include #include @@ -25,10 +27,6 @@ #include #include "spi-phytium.h" -#define MCP251x_READ 0x03 -#define MCP251x_READ_RXB0 0x90 -#define MCP251x_READ_RXB1 0x94 - static inline void spi_phyt_enable_chip(struct phytium_spi *fts, u8 enable) { u8 val = enable ? 1 : 2; @@ -99,13 +97,6 @@ static void spi_phyt_set_cs(struct spi_device *spi, bool enable) u32 origin; u16 cs; - if (fts->tx || fts->rx) - return; - - if (fts->msg->cmd_id == PHYTSPI_MSG_CMD_DATA && - fts->msg->cmd_subid == PHYTSPI_MSG_CMD_DATA_TX) - return; - if (chip && chip->cs_control) chip->cs_control(!enable); @@ -145,8 +136,13 @@ static int spi_phyt_transfer_one(struct spi_master *master, { struct phytium_spi *fts = spi_master_get_devdata(master); struct chip_data *chip = spi_get_ctldata(spi); + struct spi_mem *mem = spi_get_drvdata(spi); + struct spi_nor *nor; int ret; + if (mem) + nor = spi_mem_get_drvdata(mem); + fts->tx = (void *)transfer->tx_buf; fts->tx_end = fts->tx + transfer->len; fts->rx = transfer->rx_buf; @@ -162,7 +158,7 @@ static int spi_phyt_transfer_one(struct spi_master *master, chip->tmode = TMOD_TO; } - if (fts->tx && fts->len == 1) { + if (mem == nor->spimem && fts->tx && fts->len == 1) { if ((*(u8 *)fts->tx == SPINOR_OP_WREN) && fts->spi_write_flag == 0) { spi_phytium_write_pre(fts, spi->chip_select, transfer->bits_per_word, spi->mode, @@ -262,11 +258,7 @@ static int spi_phyt_transfer_one(struct spi_master *master, } fts->flash_erase = 0; } else { - fts->flags = 1; - if (fts->spi_write_flag == 0 && *(u8 *)(fts->tx) != MCP251x_READ - && *(u8 *)(fts->tx) != MCP251x_READ_RXB0 - && *(u8 *)(fts->tx) != MCP251x_READ_RXB1) - fts->flags = 3; + fts->flags = 0; ret = spi_phytium_write(fts, spi->chip_select, transfer->bits_per_word, spi->mode, chip->tmode, fts->flags, fts->spi_write_flag); diff --git a/drivers/staging/phytium-npu/Kconfig b/drivers/staging/phytium-npu/Kconfig index 6f15304154139bbea2a9cf5415b5c33e67c26113..8e03300195f59fdcb1a528f1a14d0434103fb193 100644 --- a/drivers/staging/phytium-npu/Kconfig +++ b/drivers/staging/phytium-npu/Kconfig @@ -1,37 +1,34 @@ # SPDX-License-Identifier: GPL-2.0 -menuconfig PHYTIUM_NPU - tristate "Phytium neural network processing unit" - depends on ARCH_PHYTIUM - help - Say Y here if your want support for Phytium NPU controller framework. - This is common support for devices that embed the Phytium NPU IP. +config PHYTIUM_NPU + tristate "Phytium neural network processing unit" + help + Say Y here if your want support for Phytium NPU controller framework. + This is common support for devices that embed the Phytium NPU IP. - To compile this driver as a module, choose M here: the module will be - called phytium_npu. + To compile this driver as a module, choose M here: the module will be + called phytium_npu. if PHYTIUM_NPU -choice - prompt "Select the Phytium NPU Hardware" - default NPU_PLATFORM - help - You need to select a suitable NPU controller in the current board. - This option allows you to decide which Phytium NPU will be built - in system. - If in doubt, select 'N' - config NPU_PLATFORM - tristate "driver runs on platform" - help - Say Y here if you want to support for IO Mapped Phytium NPU controller. - This support is for devices that have the Phytium NPU controller IP. - To compile this driver as a module, choose M here: the module will be - called phytium_npu_platform. - config NPU_PCI - tristate "driver runs on PCI hardware" - help - Say Y here if you want to support for Phytium NPU controller connected - to the PCI bus. This support is for devices that have the Phytium NPU - controller IP embedded into a PCI device. - To compile this driver as a module, choose M here: the module will be - called phytium_npu_pci. -endchoice +config PHYTIUM_NPU_PLATFORM + tristate "Phytium NPU platform support" + depends on ARCH_PHYTIUM && (ACPI || OF) + depends on PHYTIUM_NPU + help + Say Y here if you want to support for IO Mapped Phytium NPU controller. + This support is for devices that have the Phytium NPU controller IP. + + To compile this driver as a module, choose M here: the module will be + called phytium_npu_platform. + +config PHYTIUM_NPU_PCI + tristate "Phytium NPU PCI support" + depends on PCI + depends on PHYTIUM_NPU + help + Say Y here if you want to support for Phytium NPU controller connected + to the PCI bus. This support is for devices that have the Phytium NPU + controller IP embedded into a PCI device. + + To compile this driver as a module, choose M here: the module will be + called phytium_npu_pci. endif diff --git a/drivers/staging/phytium-npu/Makefile b/drivers/staging/phytium-npu/Makefile index ecbcb639754a5e17653232ee87224f06050a98d6..ce7a5f0f03c9650ca3989addf1a3017636602f0e 100644 --- a/drivers/staging/phytium-npu/Makefile +++ b/drivers/staging/phytium-npu/Makefile @@ -1,21 +1,14 @@ # SPDX-License-Identifier: GPL-2.0 -ccflags-y += -I$(src)/include/ -ccflags-y += -I$(srctree)/drivers/staging/phytium-npu/include/ - -ifdef CONFIG_NPU_PLATFORM - ifeq ($(CONFIG_NPU_PLATFORM),m) - EXTRA_CFLAGS += -DPHYTIUM_NPU_PLATFORM - endif - - ifeq ($(CONFIG_NPU_PLATFORM),y) - EXTRA_CFLAGS += -DPHYTIUM_NPU_PLATFORM - endif -endif obj-$(CONFIG_PHYTIUM_NPU) += phytium_npu.o -phytium_npu-y := phytium_npu_common.o phytium_npu_dev.o \ -phytium_npu_dev_mmu.o phytium_npu_mm_mmu.o phytium_npu_workqueue.o \ -phytium_npu_session.o phytium_npu_uapi.o phytium_npu_debug.o - -obj-$(CONFIG_NPU_PLATFORM) += phytium_npu_platform.o -obj-$(CONFIG_NPU_PCI) += phytium_npu_pci.o +phytium_npu-y := phytium_npu_common.o \ + phytium_npu_dev.o \ + phytium_npu_dev_mmu.o \ + phytium_npu_mm_mmu.o \ + phytium_npu_workqueue.o \ + phytium_npu_session.o \ + phytium_npu_uapi.o \ + phytium_npu_debug.o + +obj-$(CONFIG_PHYTIUM_NPU_PLATFORM) += phytium_npu_platform.o +obj-$(CONFIG_PHYTIUM_NPU_PCI) += phytium_npu_pci.o diff --git a/drivers/staging/phytium-npu/Makefile.testing b/drivers/staging/phytium-npu/Makefile.testing deleted file mode 100644 index da579693a6becfec5304e5e4f888ec3ce0ac0be4..0000000000000000000000000000000000000000 --- a/drivers/staging/phytium-npu/Makefile.testing +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -export CONFIG_PHYTIUM_NPU := y -export CONFIG_NPU_PLATFORM := y -export CONFIG_PHYTIUM_NPU_DMA_HEAP := y -include Makefile -ARCH := arm64 -CROSS_COMPILE := aarch64-linux-gnu- - -#------------------------------------ -# for internal use -#------------------------------------ - -ifeq ($(KERNELRELEASE),) - -#KDIR ?= /lib/modules/$(shell uname -r)/build -KSRC := /home/chengquan/code/phytium-linux-kernel-master/ -PWD := $(shell pwd) - -# kernel warning levels: as high as possible: -# W=12 and W=123 seem to warn about linux kernel headers, so use W=1. -KWARN := W=1 - -ifneq (,$(shell which sparse)) -# C=1: use 'sparse' for extra warnings, if it is avail. -SPARSE := C=1 -endif - -all: modules -modules: - $(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE)-C $(KSRC) M=$(shell pwd) modules - -clean: - $(MAKE) -C $(KDIR) M=$(PWD) clean -check: - cd $(KDIR); scripts/checkpatch.pl -f `find $(PWD)/include -name "*\.[ch]"` - -endif # KERNELRELEASE diff --git a/drivers/staging/phytium-npu/include/phytium_npu.h b/drivers/staging/phytium-npu/phytium_npu.h similarity index 100% rename from drivers/staging/phytium-npu/include/phytium_npu.h rename to drivers/staging/phytium-npu/phytium_npu.h diff --git a/drivers/staging/phytium-npu/phytium_npu_common.c b/drivers/staging/phytium-npu/phytium_npu_common.c index ec8e07dabcb0b35a4c8c63b822df261c931083ab..193c420ab2379aa672bf15e9a061725061f37dfb 100644 --- a/drivers/staging/phytium-npu/phytium_npu_common.c +++ b/drivers/staging/phytium-npu/phytium_npu_common.c @@ -18,11 +18,7 @@ #include #include #include -#ifdef PHYTIUM_NPU_PLATFORM -#include "phytium_npu_leopard_reg.h" -#else -#include "phytium_npu_reg.h" -#endif +#include "phytium_npu_pd2408_reg.h" struct phytium_npu_dev *gnpu_dev; #define AP_CPPC2_STAT 0x0 @@ -397,13 +393,12 @@ static int phytium_npu_response_stream(struct phytium_npu_session *sess, } else { size = MAX_NPU_USER_RSP_SIZE; } + nustream_rsp = kzalloc(alloc_size, GFP_KERNEL); + if (!nustream_rsp) + return -ENOMEM; if (nustream->estream.stype & NPU_COMPUTEFLAG_NOTIFY) { if (!nstream->rsp) { - nustream_rsp = kzalloc(alloc_size, GFP_KERNEL); - if (!nustream_rsp) - return -ENOMEM; - nstream->rsp = nustream_rsp; INIT_LIST_HEAD(&nustream_rsp->stream_rsp_list_entry); } diff --git a/drivers/staging/phytium-npu/phytium_npu_debug.c b/drivers/staging/phytium-npu/phytium_npu_debug.c index 3c21aec6798ffa1591ff62eddc74e7026a5286b9..0f2d42486447e5f1b2a9dd814f7eaecbcdaadebc 100644 --- a/drivers/staging/phytium-npu/phytium_npu_debug.c +++ b/drivers/staging/phytium-npu/phytium_npu_debug.c @@ -9,11 +9,7 @@ #include "phytium_npu.h" #include "phytium_npu_mmu.h" #include "linux/phytium_npu_dma_buf_heap.h" -#ifdef PHYTIUM_NPU_PLATFORM -#include "phytium_npu_leopard_reg.h" -#else -#include "phytium_npu_reg.h" -#endif +#include "phytium_npu_pd2408_reg.h" #define DEBUG_BAND_COUNT_RESET 0x3fffffff #define DEBUG_BAND_EN 0x1 diff --git a/drivers/staging/phytium-npu/phytium_npu_dev.c b/drivers/staging/phytium-npu/phytium_npu_dev.c index 3c13262db1744b9b2d839536a3a949fed15eeb19..04ae5fa37b9f4a69a65246aa11abe682a8c880ed 100644 --- a/drivers/staging/phytium-npu/phytium_npu_dev.c +++ b/drivers/staging/phytium-npu/phytium_npu_dev.c @@ -7,11 +7,8 @@ #include #include "phytium_npu_mmu.h" #include "phytium_npu.h" -#ifdef PHYTIUM_NPU_PLATFORM -#include "phytium_npu_leopard_reg.h" -#else -#include "phytium_npu_reg.h" -#endif +#include "phytium_npu_pd2408_reg.h" + #define POLL_PERIOD 100 #define POLL_TIMEOUT 100000 diff --git a/drivers/staging/phytium-npu/phytium_npu_dev_mmu.c b/drivers/staging/phytium-npu/phytium_npu_dev_mmu.c index 39f544750f790ee2e1f1f139817d56bcd5dfb1a8..542b9753ded79370fa18e77b9855d7c17c977f6a 100644 --- a/drivers/staging/phytium-npu/phytium_npu_dev_mmu.c +++ b/drivers/staging/phytium-npu/phytium_npu_dev_mmu.c @@ -6,11 +6,7 @@ #include "phytium_npu_mmu.h" #include "phytium_npu.h" #include -#ifdef PHYTIUM_NPU_PLATFORM -#include "phytium_npu_leopard_reg.h" -#else -#include "phytium_npu_reg.h" -#endif +#include "phytium_npu_pd2408_reg.h" int pg_size[6] = {4096, 16384, 65536, 262144, 1048576, 2097152}; diff --git a/drivers/staging/phytium-npu/include/phytium_npu_mmu.h b/drivers/staging/phytium-npu/phytium_npu_mmu.h similarity index 100% rename from drivers/staging/phytium-npu/include/phytium_npu_mmu.h rename to drivers/staging/phytium-npu/phytium_npu_mmu.h diff --git a/drivers/staging/phytium-npu/phytium_npu_pci.c b/drivers/staging/phytium-npu/phytium_npu_pci.c index c1ffeb78fb0155888e937a0186b86acf4ab82acb..6e4796786ce52e402b7469276b972ee2dfdf1c6a 100644 --- a/drivers/staging/phytium-npu/phytium_npu_pci.c +++ b/drivers/staging/phytium-npu/phytium_npu_pci.c @@ -11,7 +11,7 @@ #include #include #include "phytium_npu.h" -#include "phytium_npu_reg.h" +#include "phytium_npu_px210_reg.h" #define PHYTIUM_PCI_VENDOR_ID 0x1DB7 #define PHYTIUM_PCI_DEVICE_ID 0xDC24 @@ -48,8 +48,10 @@ static int phytium_npu_pci_probe(struct pci_dev *pci_dev, { struct phytium_npu_dev *npu_dev; struct device *dev = &pci_dev->dev; - void __iomem *reg_addr; - int ret, irq, bar; + void __iomem *reg_addr = NULL; + int ret = 0; + int irq = 0; + int bar = 0; dev_info(dev, "probed a NPU device, pci_dev: %x:%x\n", pci_dev->vendor, pci_dev->device); diff --git a/drivers/staging/phytium-npu/include/phytium_npu_leopard_reg.h b/drivers/staging/phytium-npu/phytium_npu_pd2408_reg.h similarity index 98% rename from drivers/staging/phytium-npu/include/phytium_npu_leopard_reg.h rename to drivers/staging/phytium-npu/phytium_npu_pd2408_reg.h index fd3f04158004a26fb6275b1cda4bd37b3f87f0e0..0f3b24889a2625b8e49ec7e595701023257ffcf7 100644 --- a/drivers/staging/phytium-npu/include/phytium_npu_leopard_reg.h +++ b/drivers/staging/phytium-npu/phytium_npu_pd2408_reg.h @@ -2,8 +2,8 @@ /* * Copyright (C) 2023 Phytium Technology Co., Ltd. */ -#ifndef __PHYTIUM_NPU_LEOPARD_NEW_REG_H__ -#define __PHYTIUM_NPU_LEOPARD_NEW_REG_H__ +#ifndef __PHYTIUM_NPU_PD2408_REG_H__ +#define __PHYTIUM_NPU_PD2408_REG_H__ #define NPU_SYS_CLK_STATUS (0x0888U) #define NPU_SYS_MWDT (0x08C8U) @@ -237,4 +237,4 @@ #define NPU_PRE_WR_256 0x00200000 #define NPU_PRELOAD_CFG (NPU_PRE_CBUF | NPU_PRE_RD_256 | NPU_PRE_WR_256) -#endif /* __PHYTIUM_NPU_LEOPARD_REG_H__ */ +#endif /* __PHYTIUM_NPU_PD2408_REG_H__ */ diff --git a/drivers/staging/phytium-npu/phytium_npu_platform.c b/drivers/staging/phytium-npu/phytium_npu_platform.c index fbb4a8b85863468a30b3ffc2daa59aa2be0d6d51..408bb040e8f9312396a1ef0975f07da38cafbea6 100644 --- a/drivers/staging/phytium-npu/phytium_npu_platform.c +++ b/drivers/staging/phytium-npu/phytium_npu_platform.c @@ -15,7 +15,7 @@ #include #include #include "phytium_npu.h" -#include "phytium_npu_leopard_reg.h" +#include "phytium_npu_pd2408_reg.h" static irqreturn_t phytium_npu_thread_irq(int irq, void *dev_id) { diff --git a/drivers/staging/phytium-npu/include/phytium_npu_reg.h b/drivers/staging/phytium-npu/phytium_npu_px210_reg.h similarity index 98% rename from drivers/staging/phytium-npu/include/phytium_npu_reg.h rename to drivers/staging/phytium-npu/phytium_npu_px210_reg.h index 3663c68ae5fa0e7b3114c4de728ec392a06ca79a..f527379fe221791da176c7e2fab0d6db79da22a5 100644 --- a/drivers/staging/phytium-npu/include/phytium_npu_reg.h +++ b/drivers/staging/phytium-npu/phytium_npu_px210_reg.h @@ -2,8 +2,8 @@ /* * Copyright (C) 2023 Phytium Technology Co., Ltd. */ -#ifndef __PHYTIUM_NPU_X100_REG_H__ -#define __PHYTIUM_NPU_X100_REG_H__ +#ifndef __PHYTIUM_NPU_PX210_REG_H__ +#define __PHYTIUM_NPU_PX210_REG_H__ #define NPU_SYS_CLK_STATUS (0x0008U) #define NPU_SYS_MWDT (0x0048U) @@ -238,4 +238,4 @@ #define NPU_PRE_WR_256 0x00200000 #define NPU_PRELOAD_CFG (NPU_PRE_CBUF | NPU_PRE_RD_256 | NPU_PRE_WR_256) -#endif /* __PHYTIUM_NPU_LEOPARD_REG_H__ */ +#endif /* __PHYTIUM_NPU_PX210_REG_H__ */ diff --git a/drivers/staging/phytium-npu/phytium_npu_uapi.c b/drivers/staging/phytium-npu/phytium_npu_uapi.c index 0fc968a38d63aa05d9649fbba1dda5ddb9333e1d..4edd2954397c59f899bd9e04a766c34f2382946d 100644 --- a/drivers/staging/phytium-npu/phytium_npu_uapi.c +++ b/drivers/staging/phytium-npu/phytium_npu_uapi.c @@ -12,8 +12,8 @@ #include #include #include -#include "include/phytium_npu.h" -#include "include/phytium_npu_uapi.h" +#include "phytium_npu.h" +#include "phytium_npu_uapi.h" static int phytium_npu_open(struct inode *inode, struct file *file) { @@ -326,7 +326,7 @@ static long phytium_npu_ioctl(struct file *file, unsigned int cmd, unsigned long { struct phytium_npu_session *sess = file->private_data; struct phytium_npu_dev *npu = sess->npu_dev; - int retval; + int retval = 0; if (!sess) return -EINVAL; diff --git a/drivers/staging/phytium-npu/include/phytium_npu_uapi.h b/drivers/staging/phytium-npu/phytium_npu_uapi.h similarity index 100% rename from drivers/staging/phytium-npu/include/phytium_npu_uapi.h rename to drivers/staging/phytium-npu/phytium_npu_uapi.h diff --git a/drivers/staging/phytium-npu/phytium_npu_workqueue.c b/drivers/staging/phytium-npu/phytium_npu_workqueue.c index 1d7301949b54998bb175b581e4bf93a725be8566..ba9e069e971da3983ae066a20d7de6d63b57f9e6 100644 --- a/drivers/staging/phytium-npu/phytium_npu_workqueue.c +++ b/drivers/staging/phytium-npu/phytium_npu_workqueue.c @@ -9,11 +9,7 @@ #include #include "phytium_npu.h" #include "phytium_npu_uapi.h" -#ifdef PHYTIUM_NPU_PLATFORM -#include "phytium_npu_leopard_reg.h" -#else -#include "phytium_npu_reg.h" -#endif +#include "phytium_npu_pd2408_reg.h" static void wake(struct phytium_npu_dev *npu) { diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 20254651523b0914317a8133d022a88260028a7b..47af8dceaaeac4f4b927682daa40cdf06177f481 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -21,10 +21,38 @@ #include #include +#ifdef CONFIG_ARCH_PHYTIUM +#include +#endif + #include "xhci.h" #include "xhci-plat.h" #include "xhci-mvebu.h" +#ifdef CONFIG_ARCH_PHYTIUM +static struct workqueue_struct *xhci_wq; +static struct workqueue_struct *get_xhci_wq(void) +{ + return xhci_wq; +} + +static int xhci_generic_plat_probe(struct platform_device *pdev); + +static void xhci_delay_work_func(struct work_struct *work) +{ + struct xhci_hcd *xhci; + struct usb_hcd *hcd; + struct platform_device *pdev; + + xhci = container_of(to_delayed_work(work), struct xhci_hcd, xhci_delay_wq); + hcd = xhci_to_hcd(xhci); + pdev = to_platform_device(hcd->self.controller); + + xhci_plat_remove(pdev); + xhci_generic_plat_probe(pdev); +} +#endif + static struct hc_driver __read_mostly xhci_plat_hc_driver; static int xhci_plat_setup(struct usb_hcd *hcd); @@ -101,7 +129,8 @@ static int xhci_plat_start(struct usb_hcd *hcd) } static const struct xhci_plat_priv xhci_plat_phytium_pe220x = { - .quirks = XHCI_RESET_ON_RESUME | XHCI_S1_SUSPEND_WAKEUP, + .quirks = XHCI_RESET_ON_RESUME | XHCI_S1_SUSPEND_WAKEUP | + XHCI_BROKEN_STREAMS, }; #ifdef CONFIG_OF @@ -184,8 +213,17 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s ret = -ENOMEM; goto disable_runtime; } - +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pe220x() || is_pd2408()) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hcd->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + } else { + hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + } +#else hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); +#endif + if (IS_ERR(hcd->regs)) { ret = PTR_ERR(hcd->regs); goto put_hcd; @@ -320,7 +358,8 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s } usb3_hcd = xhci_get_usb3_hcd(xhci); - if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4) + if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4 && + !(xhci->quirks & XHCI_BROKEN_STREAMS)) usb3_hcd->can_do_streams = 1; if (xhci->shared_hcd) { @@ -332,6 +371,13 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s device_enable_async_suspend(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pe220x() || is_pd2408()) { + INIT_DELAYED_WORK(&xhci->xhci_delay_wq, xhci_delay_work_func); + xhci->get_xhci_wq = get_xhci_wq; + } +#endif + /* * Prevent runtime pm from being on as default, users should enable * runtime pm using power/control in sysfs. @@ -584,6 +630,13 @@ MODULE_ALIAS("platform:xhci-hcd"); static int __init xhci_plat_init(void) { +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pe220x() || is_pd2408()) { + xhci_wq = create_workqueue("xhci_wq"); + if (!xhci_wq) + return -ENOMEM; + } +#endif xhci_init_driver(&xhci_plat_hc_driver, &xhci_plat_overrides); return platform_driver_register(&usb_generic_xhci_driver); } @@ -591,6 +644,10 @@ module_init(xhci_plat_init); static void __exit xhci_plat_exit(void) { +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pe220x() || is_pd2408()) + destroy_workqueue(xhci_wq); +#endif platform_driver_unregister(&usb_generic_xhci_driver); } module_exit(xhci_plat_exit); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 4587b652ce9da06499d57c307eb8c38d2371b5c6..903094cb0384f3c8f3c819f5321fffd17436a876 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1298,6 +1298,13 @@ void xhci_hc_died(struct xhci_hcd *xhci) /* inform usb core hc died if PCI remove isn't already handling it */ if (!(xhci->xhc_state & XHCI_STATE_REMOVING)) usb_hc_died(xhci_to_hcd(xhci)); + +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pe220x() || is_pd2408()) { + if (xhci->get_xhci_wq && (xhci->quirks & XHCI_S1_SUSPEND_WAKEUP)) + mod_delayed_work(xhci->get_xhci_wq(), &xhci->xhci_delay_wq, 1000); + } +#endif } static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, @@ -2255,6 +2262,13 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, td->start_seg, td->first_trb)); return 0; } +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pd2408() || is_pe220x()) { + xhci_clear_hub_tt_buffer(xhci, td, ep); + xhci_handle_halted_endpoint(xhci, ep, td, EP_SOFT_RESET); + return 0; + } +#endif /* endpoint not halted, don't reset it */ break; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3376f1f38dcc914358393b447c47b695787e264c..c0e4dc7c179d889f1f6847360fb7908a95288254 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -18,6 +18,9 @@ #include #include #include +#ifdef CONFIG_ARCH_PHYTIUM +#include +#endif /* Code sharing between pci-quirks and xhci hcd */ #include "xhci-ext-caps.h" @@ -1562,6 +1565,10 @@ struct xhci_hcd { struct list_head cmd_list; unsigned int cmd_ring_reserved_trbs; struct delayed_work cmd_timer; +#ifdef CONFIG_ARCH_PHYTIUM + struct delayed_work xhci_delay_wq; + struct workqueue_struct* (*get_xhci_wq)(void); +#endif struct completion cmd_ring_stop_completion; struct xhci_command *current_cmd; diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c index 8572c58f41e9a81573dafb08a68a0cf05c00b6b2..25cad697b9b013fd15e3559cd30f0dbeb9cb7a1b 100644 --- a/drivers/usb/phytium/host.c +++ b/drivers/usb/phytium/host.c @@ -26,6 +26,7 @@ #define HOST_ESHUTDOWN 4 #define HOST_EP_NUM 16 +#define FORCE_PORT_RESET 0x60 static void dump_ep_remap_pool(struct HOST_CTRL *priv, bool dirIn) { @@ -2334,6 +2335,7 @@ static void HubPortReset(struct HOST_CTRL *priv, uint8_t on) phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | 0 | 0x04); + phytium_write8(&priv->regs->portctrl, FORCE_PORT_RESET); priv->portStatus |= USB_PORT_STAT_RESET; priv->portStatus &= ~USB_PORT_STAT_ENABLE; priv->port_resetting = 0; diff --git a/drivers/usb/phytium_usb_v2/host.c b/drivers/usb/phytium_usb_v2/host.c index 28151f1faf542f125dc2aacc9cbb7293f8f8d43c..40e066bad534c09c03cd54b0c656f1049cde415e 100644 --- a/drivers/usb/phytium_usb_v2/host.c +++ b/drivers/usb/phytium_usb_v2/host.c @@ -115,10 +115,10 @@ static void host_stop(void *data) struct phytium_usb *phytium_usb = (struct phytium_usb *)data; if (phytium_usb) { - phytium_usb_otg_host_off((void *)phytium_usb); kfree(phytium_usb->host_plat_data); platform_device_unregister(phytium_usb->host_dev); phytium_usb->host_dev = NULL; + phytium_usb_otg_host_off((void *)phytium_usb); } } diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index a8f259bc2f4d0c392e06fd879049c72789385c5b..2df176edc642542d05fdebad76332bec36878e0e 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -1882,8 +1882,25 @@ int vfio_pci_core_mmap(struct vfio_device *core_vdev, struct vm_area_struct *vma /* * See remap_pfn_range(), called from vfio_pci_fault() but we can't * change vm_flags within the fault handler. Set them now. + * + * VM_ALLOW_ANY_UNCACHED: The VMA flag is implemented for ARM64, + * allowing KVM stage 2 device mapping attributes to use Normal-NC + * rather than DEVICE_nGnRE, which allows guest mappings + * supporting write-combining attributes (WC). ARM does not + * architecturally guarantee this is safe, and indeed some MMIO + * regions like the GICv2 VCPU interface can trigger uncontained + * faults if Normal-NC is used. + * + * To safely use VFIO in KVM the platform must guarantee full + * safety in the guest where no action taken against a MMIO + * mapping can trigger an uncontained failure. The assumption is + * that most VFIO PCI platforms support this for both mapping types, + * at least in common flows, based on some expectations of how + * PCI IP is integrated. Hence VM_ALLOW_ANY_UNCACHED is set in + * the VMA flags. */ - vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP); + vm_flags_set(vma, VM_ALLOW_ANY_UNCACHED | VM_IO | VM_PFNMAP | + VM_DONTEXPAND | VM_DONTDUMP); vma->vm_ops = &vfio_pci_mmap_ops; return 0; diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c index c444082e0477860bd5a74bf0d38a8634309d36f7..5ac9c3b2bd1285c1eb598be9507393c8b4e219d4 100644 --- a/drivers/watchdog/dw_wdt.c +++ b/drivers/watchdog/dw_wdt.c @@ -84,7 +84,7 @@ struct dw_wdt { void __iomem *regs; struct clk *clk; struct clk *pclk; - u64 rate; + u32 rate; enum dw_wdt_rmod rmod; struct dw_wdt_timeout timeouts[DW_WDT_NUM_TOPS]; struct watchdog_device wdd; @@ -595,7 +595,7 @@ static int dw_wdt_drv_probe(struct platform_device *pdev) * are not available, so watchdog rate get from * clock-frequency property given in _DSD object. */ - device_property_read_u64(dev, "clock-frequency", + device_property_read_u32(dev, "clock-frequency", &dw_wdt->rate); if (dw_wdt->rate == 0) return -EINVAL; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index d9c20ae23b63267738b11d601b1114eb546aa44e..a23b352e13fea44c8816402654992da0de91ee66 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -127,6 +127,7 @@ struct acpi_scan_handler { bool (*match)(const char *idstr, const struct acpi_device_id **matchid); int (*attach)(struct acpi_device *dev, const struct acpi_device_id *id); void (*detach)(struct acpi_device *dev); + void (*post_eject)(struct acpi_device *dev); void (*bind)(struct device *phys_dev); void (*unbind)(struct device *phys_dev); struct acpi_hotplug_profile hotplug; diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 3751ae69432f1285533a61db8e0c31d42ed5fa31..c433a079d8e11cc7a71ff0036096b5e67d425aaf 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -1046,6 +1046,7 @@ struct acpi_madt_generic_interrupt { /* ACPI_MADT_ENABLED (1) Processor is usable if set */ #define ACPI_MADT_PERFORMANCE_IRQ_MODE (1<<1) /* 01: Performance Interrupt Mode */ #define ACPI_MADT_VGIC_IRQ_MODE (1<<2) /* 02: VGIC Maintenance Interrupt mode */ +#define ACPI_MADT_GICC_CPU_CAPABLE (1<<3) /* 03: CPU is online capable */ /* 12: Generic Distributor (ACPI 5.0 + ACPI 6.0 changes) */ diff --git a/include/linux/acpi.h b/include/linux/acpi.h index cb1a465f1c88ca2d36e8826c77b5294cacc6bacf..1504c720c327635219b55f18d0dcccc78ac40918 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -256,6 +256,11 @@ acpi_table_parse_cedt(enum acpi_cedt_type id, int acpi_parse_mcfg (struct acpi_table_header *header); void acpi_table_print_madt_entry (struct acpi_subtable_header *madt); +static inline bool acpi_gicc_is_usable(struct acpi_madt_generic_interrupt *gicc) +{ + return gicc->flags & (ACPI_MADT_ENABLED | ACPI_MADT_GICC_CPU_CAPABLE); +} + /* the following numa functions are architecture-dependent */ void acpi_numa_slit_init (struct acpi_table_slit *slit); @@ -316,12 +321,10 @@ static inline int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu, } #endif -#ifdef CONFIG_ACPI_HOTPLUG_CPU /* Arch dependent functions for cpu hotplug support */ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, int *pcpu); int acpi_unmap_cpu(int cpu); -#endif /* CONFIG_ACPI_HOTPLUG_CPU */ #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr); @@ -588,12 +591,16 @@ acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context); #define OSC_SB_NATIVE_USB4_SUPPORT 0x00040000 #define OSC_SB_PRM_SUPPORT 0x00200000 #define OSC_SB_FFH_OPR_SUPPORT 0x00400000 +#define OSC_SB_HOTPLUG_ENABLED_SUPPORT 0x00800000 +#define OSC_SB_HOTPLUG_PRESENT_SUPPORT 0x01000000 extern bool osc_sb_apei_support_acked; extern bool osc_pc_lpi_support_confirmed; extern bool osc_sb_native_usb4_support_confirmed; extern bool osc_sb_cppc2_support_acked; extern bool osc_cpc_flexible_adr_space_confirmed; +extern bool osc_sb_hotplug_enabled_support_acked; +extern bool osc_sb_hotplug_present_support_acked; /* USB4 Capabilities */ #define OSC_USB_USB3_TUNNELING 0x00000001 diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 374ff338755ca271d634a0dd7058fe72def2da4d..9aba481603524d991ca2bdd61fe03aa8dda22d06 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -551,5 +551,26 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, method; \ }) +#ifdef CONFIG_PARAVIRT_SCHED +/* Paravirtualised sched calls */ +#define ARM_SMCCC_HV_PV_SCHED_FEATURES \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_STANDARD_HYP, \ + 0x90) + +#define ARM_SMCCC_HV_PV_SCHED_IPA_INIT \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_STANDARD_HYP, \ + 0x91) + +#define ARM_SMCCC_HV_PV_SCHED_IPA_RELEASE \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \ + ARM_SMCCC_SMC_64, \ + ARM_SMCCC_OWNER_STANDARD_HYP, \ + 0x92) +#endif /* CONFIG_PARAVIRT_SCHED */ + #endif /*__ASSEMBLY__*/ #endif /*__LINUX_ARM_SMCCC_H*/ diff --git a/include/linux/cper.h b/include/linux/cper.h index c1a7dc3251215a5ba0e982568a746ff5b04602d1..a053d52c70122070cda3efd84c4595a3b7f1791c 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -198,6 +198,11 @@ enum { GUID_INIT(0x036F84E1, 0x7F37, 0x428c, 0xA7, 0x9E, 0x57, 0x5F, \ 0xDF, 0xAA, 0x84, 0xEC) +/* Phytium Error Record */ +#define CPER_SEC_PHYT_ERR \ + GUID_INIT(0x253C8E21, 0xB0B5, 0x4B1A, 0x8C, 0x45, 0x5A, 0x0F, \ + 0x5C, 0x06, 0x69, 0x8D) + #define CPER_PROC_VALID_TYPE 0x0001 #define CPER_PROC_VALID_ISA 0x0002 #define CPER_PROC_VALID_ERROR_TYPE 0x0004 @@ -550,6 +555,19 @@ struct cper_sec_fw_err_rec_ref { guid_t record_identifier_guid; }; +/* Phytium Error Record, Phytium RAS V1.1 */ +struct cper_sec_phyt_err { + u8 type; + u8 subtype; + u16 id; + u32 error_status; + u64 phys_address; + u64 misc0; + u64 misc1; + u64 misc2; + u64 misc3; +}; + /* Reset to default packing */ #pragma pack() diff --git a/include/linux/cpu.h b/include/linux/cpu.h index a7d91a167a8b6452534b2509872cecfc3b708de4..75f0344bd3b944c28643cc91e8bb95a73c155e9c 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -82,6 +82,7 @@ extern __printf(4, 5) struct device *cpu_device_create(struct device *parent, void *drvdata, const struct attribute_group **groups, const char *fmt, ...); +extern bool arch_cpu_is_hotpluggable(int cpu); extern int arch_register_cpu(int cpu); extern void arch_unregister_cpu(int cpu); #ifdef CONFIG_HOTPLUG_CPU @@ -90,6 +91,10 @@ extern ssize_t arch_cpu_probe(const char *, size_t); extern ssize_t arch_cpu_release(const char *, size_t); #endif +#ifdef CONFIG_GENERIC_CPU_DEVICES +DECLARE_PER_CPU(struct cpu, cpu_devices); +#endif + /* * These states are not related to the core CPU hotplug mechanism. They are * used by various (sub)architectures to track internal state diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 2f979c03d7fc2be8f15c432154179804e778717e..18fb6ceea65dbf41ac5488a33f254cfbac62882c 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -190,6 +190,7 @@ enum cpuhp_state { CPUHP_AP_DUMMY_TIMER_STARTING, CPUHP_AP_ARM_XEN_STARTING, CPUHP_AP_ARM_XEN_RUNSTATE_STARTING, + CPUHP_AP_ARM_KVM_PVSCHED_STARTING, CPUHP_AP_ARM_CORESIGHT_STARTING, CPUHP_AP_ARM_CORESIGHT_CTI_STARTING, CPUHP_AP_ARM64_ISNDEP_STARTING, diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index dbdbf1451cadd753363af69b04c9cc111ee4c020..4c26ede3b87d311283306c014b072c1ac233affa 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -92,6 +92,7 @@ static inline void set_nr_cpu_ids(unsigned int nr) * * cpu_possible_mask- has bit 'cpu' set iff cpu is populatable * cpu_present_mask - has bit 'cpu' set iff cpu is populated + * cpu_enabled_mask - has bit 'cpu' set iff cpu can be brought online * cpu_online_mask - has bit 'cpu' set iff cpu available to scheduler * cpu_active_mask - has bit 'cpu' set iff cpu available to migration * @@ -124,11 +125,13 @@ static inline void set_nr_cpu_ids(unsigned int nr) extern struct cpumask __cpu_possible_mask; extern struct cpumask __cpu_online_mask; +extern struct cpumask __cpu_enabled_mask; extern struct cpumask __cpu_present_mask; extern struct cpumask __cpu_active_mask; extern struct cpumask __cpu_dying_mask; #define cpu_possible_mask ((const struct cpumask *)&__cpu_possible_mask) #define cpu_online_mask ((const struct cpumask *)&__cpu_online_mask) +#define cpu_enabled_mask ((const struct cpumask *)&__cpu_enabled_mask) #define cpu_present_mask ((const struct cpumask *)&__cpu_present_mask) #define cpu_active_mask ((const struct cpumask *)&__cpu_active_mask) #define cpu_dying_mask ((const struct cpumask *)&__cpu_dying_mask) @@ -973,6 +976,7 @@ extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS); #else #define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask) #define for_each_online_cpu(cpu) for_each_cpu((cpu), cpu_online_mask) +#define for_each_enabled_cpu(cpu) for_each_cpu((cpu), cpu_enabled_mask) #define for_each_present_cpu(cpu) for_each_cpu((cpu), cpu_present_mask) #endif @@ -995,6 +999,15 @@ set_cpu_possible(unsigned int cpu, bool possible) cpumask_clear_cpu(cpu, &__cpu_possible_mask); } +static inline void +set_cpu_enabled(unsigned int cpu, bool can_be_onlined) +{ + if (can_be_onlined) + cpumask_set_cpu(cpu, &__cpu_enabled_mask); + else + cpumask_clear_cpu(cpu, &__cpu_enabled_mask); +} + static inline void set_cpu_present(unsigned int cpu, bool present) { @@ -1074,6 +1087,7 @@ static __always_inline unsigned int num_online_cpus(void) return raw_atomic_read(&__num_online_cpus); } #define num_possible_cpus() cpumask_weight(cpu_possible_mask) +#define num_enabled_cpus() cpumask_weight(cpu_enabled_mask) #define num_present_cpus() cpumask_weight(cpu_present_mask) #define num_active_cpus() cpumask_weight(cpu_active_mask) @@ -1082,6 +1096,11 @@ static inline bool cpu_online(unsigned int cpu) return cpumask_test_cpu(cpu, cpu_online_mask); } +static inline bool cpu_enabled(unsigned int cpu) +{ + return cpumask_test_cpu(cpu, cpu_enabled_mask); +} + static inline bool cpu_possible(unsigned int cpu) { return cpumask_test_cpu(cpu, cpu_possible_mask); @@ -1106,6 +1125,7 @@ static inline bool cpu_dying(unsigned int cpu) #define num_online_cpus() 1U #define num_possible_cpus() 1U +#define num_enabled_cpus() 1U #define num_present_cpus() 1U #define num_active_cpus() 1U @@ -1119,6 +1139,11 @@ static inline bool cpu_possible(unsigned int cpu) return cpu == 0; } +static inline bool cpu_enabled(unsigned int cpu) +{ + return cpu == 0; +} + static inline bool cpu_present(unsigned int cpu) { return cpu == 0; diff --git a/include/linux/device.h b/include/linux/device.h index 2c5e240eb71a65bfe739b2afa3468b7ccea8d344..b2ddba6725f879249b5a2b753bd1fee04bff1567 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -656,7 +656,10 @@ struct device_physical_location { * @dma_io_tlb_lock: Protects changes to the list of active pools. * @dma_uses_io_tlb: %true if device has used the software IO TLB. * @dma_p_io_tlb_mem: Phytium Software IO TLB allocator. Not for driver use. + * @orig_dma_ops: Original DMA mapping operations for this device. + * @local_node: NUMA node this device is really belong to. * @dma_uses_p_io_tlb: %true if device has used the Phytium software IO TLB. + * @can_use_pswiotlb: %true if device can use the Phytium software IO TLB. * @archdata: For arch-specific additions. * @of_node: Associated device tree node. * @fwnode: Associated device node supplied by platform firmware. @@ -765,8 +768,16 @@ struct device { #endif #ifdef CONFIG_PSWIOTLB struct p_io_tlb_mem *dma_p_io_tlb_mem; - bool dma_uses_p_io_tlb; - bool can_use_pswiotlb; +#ifdef CONFIG_DMA_OPS + const struct dma_map_ops *orig_dma_ops; +#endif + struct { +#ifdef CONFIG_NUMA + int local_node; +#endif + bool dma_uses_p_io_tlb; + bool can_use_pswiotlb; + }; #endif #ifdef CONFIG_SWIOTLB_DYNAMIC struct list_head dma_io_tlb_pools; @@ -781,9 +792,6 @@ struct device { #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ -#ifdef CONFIG_PSWIOTLB - int local_node; /* NUMA node this device is really belong to */ -#endif #endif dev_t devt; /* dev_t, creates the sysfs "dev" */ u32 id; /* device instance */ diff --git a/include/linux/mm.h b/include/linux/mm.h index a4b4be542811748ed1ca2811dbf99e1d55a1a955..8171ffbc4efef9d7e7928f01a0c6352d0b41474d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -398,6 +398,20 @@ extern unsigned int kobjsize(const void *objp); # define VM_UFFD_MINOR VM_NONE #endif /* CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */ +/* + * This flag is used to connect VFIO to arch specific KVM code. It + * indicates that the memory under this VMA is safe for use with any + * non-cachable memory type inside KVM. Some VFIO devices, on some + * platforms, are thought to be unsafe and can cause machine crashes + * if KVM does not lock down the memory type. + */ +#ifdef CONFIG_64BIT +#define VM_ALLOW_ANY_UNCACHED_BIT 39 +#define VM_ALLOW_ANY_UNCACHED BIT(VM_ALLOW_ANY_UNCACHED_BIT) +#else +#define VM_ALLOW_ANY_UNCACHED VM_NONE +#endif + /* Bits set in the VMA until the stack is in its final location */ #define VM_STACK_INCOMPLETE_SETUP (VM_RAND_READ | VM_SEQ_READ | VM_STACK_EARLY) diff --git a/include/linux/pswiotlb.h b/include/linux/pswiotlb.h index 26b2a9d7f5a1c6fda8fa594278e8330b18f4b67c..510cff3ad34204390e84781f31412479d7b1ffcd 100644 --- a/include/linux/pswiotlb.h +++ b/include/linux/pswiotlb.h @@ -79,6 +79,7 @@ dma_addr_t pswiotlb_map(struct device *dev, int nid, phys_addr_t phys, void pswiotlb_store_local_node(struct pci_dev *dev, struct pci_bus *bus); void iommu_dma_unmap_sg_pswiotlb(struct device *dev, struct scatterlist *sg, unsigned long iova, size_t mapped, int nents, enum dma_data_direction dir, unsigned long attrs); +void pswiotlb_setup_dma_ops(struct device *dev); #ifdef CONFIG_PSWIOTLB struct pswiotlb_passthroughlist { struct list_head node; diff --git a/include/linux/ras.h b/include/linux/ras.h index f695e7e760ddea96c9aa2cc05eee2b026c2da654..d138d537a51a2cebdc979530492143d2a7a004db 100644 --- a/include/linux/ras.h +++ b/include/linux/ras.h @@ -25,6 +25,7 @@ void log_non_standard_event(const guid_t *sec_type, const guid_t *fru_id, const char *fru_text, const u8 sev, const u8 *err, const u32 len); void log_arm_hw_error(struct cper_sec_proc_arm *err, const u8 sev); +void log_phyt_err_event(struct cper_sec_phyt_err *err, const u8 sev); #else static inline void log_non_standard_event(const guid_t *sec_type, @@ -33,6 +34,8 @@ log_non_standard_event(const guid_t *sec_type, { return; } static inline void log_arm_hw_error(struct cper_sec_proc_arm *err, const u8 sev) { return; } +static inline void +log_phyt_err_event(struct cper_sec_phyt_err *err, const u8 sev) { return; } #endif #if defined(CONFIG_ARM) || defined(CONFIG_ARM64) diff --git a/include/linux/suspend.h b/include/linux/suspend.h index ef503088942d997ac5ff1c3395ad7c56f8c8c7f7..6b51f5665de82fa75950964ee61f26674ff3ceec 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h @@ -212,6 +212,9 @@ extern suspend_state_t mem_sleep_default; */ extern void suspend_set_ops(const struct platform_suspend_ops *ops); extern int suspend_valid_only_mem(suspend_state_t state); +#ifdef CONFIG_ARCH_PHYTIUM +extern int phytium_suspend_valid_mem(suspend_state_t state); +#endif extern unsigned int pm_suspend_global_flags; diff --git a/include/ras/ras_event.h b/include/ras/ras_event.h index 417c7fb994013c26cfc682f9d5bc43ddcf524597..ca05802a868a7ee729a0532fe05eac47358194da 100644 --- a/include/ras/ras_event.h +++ b/include/ras/ras_event.h @@ -460,6 +460,39 @@ TRACE_EVENT(memory_failure_event, ) ); #endif /* CONFIG_MEMORY_FAILURE */ + +/* + * Phytium Error Section Report + * + * This event is Phytium Error Record event. + * + */ +#define PHYTERR "Phytium Error Record" +TRACE_EVENT(phyt_err_event, + + TP_PROTO(u32 phyt_len, + u8 *phyt, + u8 sev), + + TP_ARGS(phyt_len, phyt, sev), + + TP_STRUCT__entry( + __field(u32, phyt_len) + __dynamic_array(u8, buf, phyt_len) + __field(u8, sev) + ), + + TP_fast_assign( + __entry->phyt_len = phyt_len; + memcpy(__get_dynamic_array(buf), phyt, phyt_len); + __entry->sev = sev; + ), + + TP_printk("%s: %s", + PHYTERR, + __print_hex(__get_dynamic_array(buf), __entry->phyt_len)) +); + #endif /* _TRACE_HW_EVENT_MC_H */ /* This part must be outside protection */ diff --git a/kernel/cpu.c b/kernel/cpu.c index 0c72b94ed076a3a88863d6434402d3bece016f00..d007c6a74cd8a71070ac0dd93287fba7dab0b81e 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -3131,6 +3131,9 @@ EXPORT_SYMBOL(__cpu_possible_mask); struct cpumask __cpu_online_mask __read_mostly; EXPORT_SYMBOL(__cpu_online_mask); +struct cpumask __cpu_enabled_mask __read_mostly; +EXPORT_SYMBOL(__cpu_enabled_mask); + struct cpumask __cpu_present_mask __read_mostly; EXPORT_SYMBOL(__cpu_present_mask); diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index 2e392fc6c22ca0d75cd4d06b837fbe3465586658..b0953cf247055007a8b6dbd63f03d7cb953a0838 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -158,13 +158,6 @@ dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page, if (WARN_ON_ONCE(!dev->dma_mask)) return DMA_MAPPING_ERROR; -#ifdef CONFIG_PSWIOTLB - if (check_if_pswiotlb_is_applicable(dev) && - !pswiotlb_bypass_is_needed(dev, 0, dir)) { - addr = pswiotlb_dma_map_page_distribute(dev, page, offset, size, dir, attrs); - return addr; - } -#endif if (dma_map_direct(dev, ops) || arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size)) addr = dma_direct_map_page(dev, page, offset, size, dir, attrs); @@ -182,12 +175,7 @@ void dma_unmap_page_attrs(struct device *dev, dma_addr_t addr, size_t size, const struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!valid_dma_direction(dir)); -#ifdef CONFIG_PSWIOTLB - if (check_if_pswiotlb_is_applicable(dev)) { - pswiotlb_dma_unmap_page_attrs_distribute(dev, addr, size, dir, attrs); - return; - } -#endif + if (dma_map_direct(dev, ops) || arch_dma_unmap_page_direct(dev, addr + size)) dma_direct_unmap_page(dev, addr, size, dir, attrs); @@ -206,13 +194,7 @@ static int __dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, if (WARN_ON_ONCE(!dev->dma_mask)) return 0; -#ifdef CONFIG_PSWIOTLB - if (check_if_pswiotlb_is_applicable(dev) && - !pswiotlb_bypass_is_needed(dev, nents, dir)) { - ents = pswiotlb_dma_map_sg_attrs_distribute(dev, sg, nents, dir, attrs); - return ents; - } -#endif + if (dma_map_direct(dev, ops) || arch_dma_map_sg_direct(dev, sg, nents)) ents = dma_direct_map_sg(dev, sg, nents, dir, attrs); @@ -306,12 +288,7 @@ void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, BUG_ON(!valid_dma_direction(dir)); debug_dma_unmap_sg(dev, sg, nents, dir); -#ifdef CONFIG_PSWIOTLB - if (check_if_pswiotlb_is_applicable(dev)) { - pswiotlb_dma_unmap_sg_attrs_distribute(dev, sg, nents, dir, attrs); - return; - } -#endif + if (dma_map_direct(dev, ops) || arch_dma_unmap_sg_direct(dev, sg, nents)) dma_direct_unmap_sg(dev, sg, nents, dir, attrs); @@ -358,12 +335,7 @@ void dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr, size_t size, const struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!valid_dma_direction(dir)); -#ifdef CONFIG_PSWIOTLB - if (check_if_pswiotlb_is_applicable(dev)) { - pswiotlb_dma_sync_single_for_cpu_distribute(dev, addr, size, dir); - return; - } -#endif + if (dma_map_direct(dev, ops)) dma_direct_sync_single_for_cpu(dev, addr, size, dir); else if (ops->sync_single_for_cpu) @@ -377,12 +349,7 @@ void dma_sync_single_for_device(struct device *dev, dma_addr_t addr, const struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!valid_dma_direction(dir)); -#ifdef CONFIG_PSWIOTLB - if (check_if_pswiotlb_is_applicable(dev)) { - pswiotlb_dma_sync_single_for_device_distribute(dev, addr, size, dir); - return; - } -#endif + if (dma_map_direct(dev, ops)) dma_direct_sync_single_for_device(dev, addr, size, dir); else if (ops->sync_single_for_device) @@ -396,12 +363,7 @@ void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, const struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!valid_dma_direction(dir)); -#ifdef CONFIG_PSWIOTLB - if (check_if_pswiotlb_is_applicable(dev)) { - pswiotlb_dma_sync_sg_for_cpu_distribute(dev, sg, nelems, dir); - return; - } -#endif + if (dma_map_direct(dev, ops)) dma_direct_sync_sg_for_cpu(dev, sg, nelems, dir); else if (ops->sync_sg_for_cpu) @@ -415,12 +377,7 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, const struct dma_map_ops *ops = get_dma_ops(dev); BUG_ON(!valid_dma_direction(dir)); -#ifdef CONFIG_PSWIOTLB - if (check_if_pswiotlb_is_applicable(dev)) { - pswiotlb_dma_sync_sg_for_device_distribute(dev, sg, nelems, dir); - return; - } -#endif + if (dma_map_direct(dev, ops)) dma_direct_sync_sg_for_device(dev, sg, nelems, dir); else if (ops->sync_sg_for_device) @@ -554,9 +511,6 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle, if (WARN_ON_ONCE(flag & __GFP_COMP)) return NULL; -#ifdef CONFIG_PSWIOTLB - check_if_pswiotlb_is_applicable(dev); -#endif if (dma_alloc_from_dev_coherent(dev, size, dma_handle, &cpu_addr)) return cpu_addr; diff --git a/kernel/dma/phytium/pswiotlb-dma.h b/kernel/dma/phytium/pswiotlb-dma.h index 0f159a3891749f38fbf8ce43bb2a69f5b9de05aa..8d355e8baa3ac6ab67574fdba7019fb27949d4de 100644 --- a/kernel/dma/phytium/pswiotlb-dma.h +++ b/kernel/dma/phytium/pswiotlb-dma.h @@ -13,6 +13,69 @@ #include extern bool pswiotlb_force_disable; +struct pswiotlb_dma_map_ops { + unsigned int flags; + + void *(*alloc)(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t gfp, + unsigned long attrs); + void (*free)(struct device *dev, size_t size, void *vaddr, + dma_addr_t dma_handle, unsigned long attrs); + struct page *(*alloc_pages)(struct device *dev, size_t size, + dma_addr_t *dma_handle, enum dma_data_direction dir, + gfp_t gfp); + void (*free_pages)(struct device *dev, size_t size, struct page *vaddr, + dma_addr_t dma_handle, enum dma_data_direction dir); + struct sg_table *(*alloc_noncontiguous)(struct device *dev, size_t size, + enum dma_data_direction dir, gfp_t gfp, + unsigned long attrs); + void (*free_noncontiguous)(struct device *dev, size_t size, + struct sg_table *sgt, enum dma_data_direction dir); + int (*mmap)(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size, unsigned long attrs); + + int (*get_sgtable)(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, size_t size, + unsigned long attrs); + + dma_addr_t (*map_page)(struct device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction dir, unsigned long attrs); + void (*unmap_page)(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir, + unsigned long attrs); + /* + * map_sg should return a negative error code on error. See + * dma_map_sgtable() for a list of appropriate error codes + * and their meanings. + */ + int (*map_sg)(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction dir, unsigned long attrs); + void (*unmap_sg)(struct device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction dir, unsigned long attrs); + dma_addr_t (*map_resource)(struct device *dev, phys_addr_t phys_addr, + size_t size, enum dma_data_direction dir, + unsigned long attrs); + void (*unmap_resource)(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir, + unsigned long attrs); + void (*sync_single_for_cpu)(struct device *dev, dma_addr_t dma_handle, + size_t size, enum dma_data_direction dir); + void (*sync_single_for_device)(struct device *dev, + dma_addr_t dma_handle, size_t size, + enum dma_data_direction dir); + void (*sync_sg_for_cpu)(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir); + void (*sync_sg_for_device)(struct device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction dir); + void (*cache_sync)(struct device *dev, void *vaddr, size_t size, + enum dma_data_direction direction); + int (*dma_supported)(struct device *dev, u64 mask); + u64 (*get_required_mask)(struct device *dev); + size_t (*max_mapping_size)(struct device *dev); + size_t (*opt_mapping_size)(void); + unsigned long (*get_merge_boundary)(struct device *dev); +}; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_PSWIOTLB) void pswiotlb_dma_direct_sync_sg_for_device(struct device *dev, @@ -46,7 +109,7 @@ static inline void pswiotlb_dma_direct_sync_sg_for_cpu(struct device *dev, #ifdef CONFIG_PSWIOTLB int pswiotlb_dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents, enum dma_data_direction dir, unsigned long attrs); -dma_addr_t pswiotlb_dma_map_page_distribute(struct device *dev, struct page *page, +dma_addr_t pswiotlb_dma_map_page_attrs_distribute(struct device *dev, struct page *page, size_t offset, size_t size, enum dma_data_direction dir, unsigned long attrs); void pswiotlb_dma_unmap_page_attrs_distribute(struct device *dev, dma_addr_t addr, @@ -80,6 +143,8 @@ void pswiotlb_iommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl, int nelems, enum dma_data_direction dir); void pswiotlb_iommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, int nelems, enum dma_data_direction dir); +void pswiotlb_iommu_dma_free(struct device *dev, size_t size, void *cpu_addr, + dma_addr_t handle, unsigned long attrs); static inline bool check_if_pswiotlb_is_applicable(struct device *dev) { @@ -212,7 +277,7 @@ static inline int pswiotlb_dma_direct_map_sg(struct device *dev, struct scatterl return 0; } -static inline dma_addr_t pswiotlb_dma_map_page_distribute(struct device *dev, +static inline dma_addr_t pswiotlb_dma_map_page_attrs_distribute(struct device *dev, struct page *page, size_t offset, size_t size, enum dma_data_direction dir, unsigned long attrs) { @@ -304,6 +369,11 @@ static inline void pswiotlb_iommu_dma_sync_sg_for_device(struct device *dev, { } +static inline void pswiotlb_iommu_dma_free(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t handle, unsigned long attrs) +{ +} + static inline bool check_if_pswiotlb_is_applicable(struct device *dev) { return false; diff --git a/kernel/dma/phytium/pswiotlb-iommu.c b/kernel/dma/phytium/pswiotlb-iommu.c index bc8954afff899351c203d8db93ada23e352f49d6..e19ae0c2df6b30a44583576c510db61a374433e7 100644 --- a/kernel/dma/phytium/pswiotlb-iommu.c +++ b/kernel/dma/phytium/pswiotlb-iommu.c @@ -116,16 +116,24 @@ struct iova_fq { * The following functions are ported from * ./drivers/iommu/dma-iommu.c * ./drivers/iommu/iommu.c + * static inline bool fq_full(struct iova_fq *fq); + * static void fq_flush_iotlb(struct iommu_dma_cookie *cookie); + * static inline unsigned int fq_ring_add(struct iova_fq *fq); + * static void fq_ring_free(struct iommu_dma_cookie *cookie, struct iova_fq *fq); * static size_t iommu_pgsize(struct iommu_domain *domain, unsigned long iova, * phys_addr_t paddr, size_t size, size_t *count); * static int __iommu_map_pages(struct iommu_domain *domain, unsigned long iova, - * phys_addr_t paddr, size_t size, int prot, - * gfp_t gfp, size_t *mapped); + * phys_addr_t paddr, size_t size, int prot, gfp_t gfp, size_t *mapped); * static int __iommu_map(struct iommu_domain *domain, unsigned long iova, * phys_addr_t paddr, size_t size, int prot, gfp_t gfp); + * static bool dev_use_swiotlb(struct device *dev, size_t size, + * enum dma_data_direction dir); * static bool dev_is_untrusted(struct device *dev); * static int dma_info_to_prot(enum dma_data_direction dir, bool coherent, * unsigned long attrs); + * static void queue_iova(struct iommu_dma_cookie *cookie, + * unsigned long pfn, unsigned long pages, + * struct list_head *freelist); * static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain, * size_t size, u64 dma_limit, struct device *dev); * static void iommu_dma_free_iova(struct iommu_dma_cookie *cookie, diff --git a/kernel/dma/phytium/pswiotlb-mapping.c b/kernel/dma/phytium/pswiotlb-mapping.c index 65674b7bdeab7e3fcf53517ef86d08c164858492..7d518b5646c7dff7195ab82af8599ee2efbb4201 100644 --- a/kernel/dma/phytium/pswiotlb-mapping.c +++ b/kernel/dma/phytium/pswiotlb-mapping.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include "../debug.h" #include "../direct.h" #include "pswiotlb-dma.h" @@ -22,8 +24,18 @@ * ./drivers/dma/mapping.c * static bool dma_go_direct(struct device *dev, dma_addr_t mask, * const struct dma_map_ops *ops); + * static inline bool dma_alloc_direct(struct device *dev, + * const struct dma_map_ops *ops); * static inline bool dma_map_direct(struct device *dev, * const struct dma_map_ops *ops); + * static struct page *__dma_alloc_pages(struct device *dev, size_t size, + * dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp); + * static struct sg_table *alloc_single_sgt(struct device *dev, size_t size, + * enum dma_data_direction dir, gfp_t gfp); + * static void __dma_free_pages(struct device *dev, size_t size, struct page *page, + * dma_addr_t dma_handle, enum dma_data_direction dir); + * static void free_single_sgt(struct device *dev, size_t size, + * struct sg_table *sgt, enum dma_data_direction dir); */ static bool dma_go_direct(struct device *dev, dma_addr_t mask, @@ -39,23 +51,38 @@ static bool dma_go_direct(struct device *dev, dma_addr_t mask, return false; } +static inline bool dma_alloc_direct(struct device *dev, + const struct dma_map_ops *ops) +{ + return dma_go_direct(dev, dev->coherent_dma_mask, ops); +} + static inline bool dma_map_direct(struct device *dev, const struct dma_map_ops *ops) { return dma_go_direct(dev, *dev->dma_mask, ops); } -dma_addr_t pswiotlb_dma_map_page_distribute(struct device *dev, struct page *page, + +dma_addr_t pswiotlb_dma_map_page_attrs_distribute(struct device *dev, struct page *page, size_t offset, size_t size, enum dma_data_direction dir, unsigned long attrs) { - const struct dma_map_ops *ops = get_dma_ops(dev); + const struct dma_map_ops *ops = dev->orig_dma_ops; dma_addr_t addr; if (dma_map_direct(dev, ops) || - arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size)) - addr = pswiotlb_dma_direct_map_page(dev, page, offset, size, dir, attrs); - else - addr = pswiotlb_iommu_dma_map_page(dev, page, offset, size, dir, attrs); + arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size)) { + if (!pswiotlb_bypass_is_needed(dev, 0, dir)) + addr = pswiotlb_dma_direct_map_page(dev, page, offset, size, dir, attrs); + else + addr = dma_direct_map_page(dev, page, offset, size, dir, attrs); + } else { + if (!pswiotlb_bypass_is_needed(dev, 0, dir)) + addr = pswiotlb_iommu_dma_map_page(dev, page, offset, size, dir, attrs); + else + addr = ops->map_page(dev, page, offset, size, dir, attrs); + } + kmsan_handle_dma(page, offset, size, dir); debug_dma_map_page(dev, page, offset, size, dir, addr, attrs); return addr; @@ -64,7 +91,7 @@ dma_addr_t pswiotlb_dma_map_page_distribute(struct device *dev, struct page *pag void pswiotlb_dma_unmap_page_attrs_distribute(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir, unsigned long attrs) { - const struct dma_map_ops *ops = get_dma_ops(dev); + const struct dma_map_ops *ops = dev->orig_dma_ops; if (dma_map_direct(dev, ops) || arch_dma_unmap_page_direct(dev, addr + size)) @@ -77,14 +104,21 @@ void pswiotlb_dma_unmap_page_attrs_distribute(struct device *dev, dma_addr_t add int pswiotlb_dma_map_sg_attrs_distribute(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir, unsigned long attrs) { - const struct dma_map_ops *ops = get_dma_ops(dev); + const struct dma_map_ops *ops = dev->orig_dma_ops; int ents; if (dma_map_direct(dev, ops) || - arch_dma_map_sg_direct(dev, sg, nents)) - ents = pswiotlb_dma_direct_map_sg(dev, sg, nents, dir, attrs); - else - ents = pswiotlb_iommu_dma_map_sg(dev, sg, nents, dir, attrs); + arch_dma_map_sg_direct(dev, sg, nents)) { + if (!pswiotlb_bypass_is_needed(dev, nents, dir)) + ents = pswiotlb_dma_direct_map_sg(dev, sg, nents, dir, attrs); + else + ents = dma_direct_map_sg(dev, sg, nents, dir, attrs); + } else { + if (!pswiotlb_bypass_is_needed(dev, nents, dir)) + ents = pswiotlb_iommu_dma_map_sg(dev, sg, nents, dir, attrs); + else + ents = ops->map_sg(dev, sg, nents, dir, attrs); + } if (ents > 0) debug_dma_map_sg(dev, sg, nents, ents, dir, attrs); @@ -99,7 +133,7 @@ void pswiotlb_dma_unmap_sg_attrs_distribute(struct device *dev, struct scatterli int nents, enum dma_data_direction dir, unsigned long attrs) { - const struct dma_map_ops *ops = get_dma_ops(dev); + const struct dma_map_ops *ops = dev->orig_dma_ops; if (dma_map_direct(dev, ops) || arch_dma_unmap_sg_direct(dev, sg, nents)) @@ -111,7 +145,7 @@ void pswiotlb_dma_unmap_sg_attrs_distribute(struct device *dev, struct scatterli void pswiotlb_dma_sync_single_for_cpu_distribute(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir) { - const struct dma_map_ops *ops = get_dma_ops(dev); + const struct dma_map_ops *ops = dev->orig_dma_ops; if (dma_map_direct(dev, ops)) pswiotlb_dma_direct_sync_single_for_cpu(dev, addr, size, dir); @@ -123,7 +157,7 @@ void pswiotlb_dma_sync_single_for_cpu_distribute(struct device *dev, dma_addr_t void pswiotlb_dma_sync_single_for_device_distribute(struct device *dev, dma_addr_t addr, size_t size, enum dma_data_direction dir) { - const struct dma_map_ops *ops = get_dma_ops(dev); + const struct dma_map_ops *ops = dev->orig_dma_ops; if (dma_map_direct(dev, ops)) pswiotlb_dma_direct_sync_single_for_device(dev, addr, size, dir); @@ -135,7 +169,7 @@ void pswiotlb_dma_sync_single_for_device_distribute(struct device *dev, dma_addr void pswiotlb_dma_sync_sg_for_cpu_distribute(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction dir) { - const struct dma_map_ops *ops = get_dma_ops(dev); + const struct dma_map_ops *ops = dev->orig_dma_ops; if (dma_map_direct(dev, ops)) pswiotlb_dma_direct_sync_sg_for_cpu(dev, sg, nelems, dir); @@ -147,7 +181,7 @@ void pswiotlb_dma_sync_sg_for_cpu_distribute(struct device *dev, struct scatterl void pswiotlb_dma_sync_sg_for_device_distribute(struct device *dev, struct scatterlist *sg, int nelems, enum dma_data_direction dir) { - const struct dma_map_ops *ops = get_dma_ops(dev); + const struct dma_map_ops *ops = dev->orig_dma_ops; if (dma_map_direct(dev, ops)) pswiotlb_dma_direct_sync_sg_for_device(dev, sg, nelems, dir); @@ -155,3 +189,272 @@ void pswiotlb_dma_sync_sg_for_device_distribute(struct device *dev, struct scatt pswiotlb_iommu_dma_sync_sg_for_device(dev, sg, nelems, dir); debug_dma_sync_sg_for_device(dev, sg, nelems, dir); } + +static dma_addr_t pswiotlb_dma_map_resource_distribute(struct device *dev, phys_addr_t phys, + size_t size, enum dma_data_direction dir, unsigned long attrs) +{ + return dma_direct_map_resource(dev, phys, size, dir, attrs); +} + +static struct page *__dma_alloc_pages(struct device *dev, size_t size, + dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp) +{ + const struct dma_map_ops *ops = get_dma_ops(dev); + + if (WARN_ON_ONCE(!dev->coherent_dma_mask)) + return NULL; + if (WARN_ON_ONCE(gfp & (__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM))) + return NULL; + if (WARN_ON_ONCE(gfp & __GFP_COMP)) + return NULL; + + size = PAGE_ALIGN(size); + if (dma_alloc_direct(dev, ops)) + return dma_direct_alloc_pages(dev, size, dma_handle, dir, gfp); + if (!ops->alloc_pages) + return NULL; + return ops->alloc_pages(dev, size, dma_handle, dir, gfp); +} + +static struct sg_table *alloc_single_sgt(struct device *dev, size_t size, + enum dma_data_direction dir, gfp_t gfp) +{ + struct sg_table *sgt; + struct page *page; + + sgt = kmalloc(sizeof(*sgt), gfp); + if (!sgt) + return NULL; + if (sg_alloc_table(sgt, 1, gfp)) + goto out_free_sgt; + page = __dma_alloc_pages(dev, size, &sgt->sgl->dma_address, dir, gfp); + if (!page) + goto out_free_table; + sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0); + sg_dma_len(sgt->sgl) = sgt->sgl->length; + return sgt; +out_free_table: + sg_free_table(sgt); +out_free_sgt: + kfree(sgt); + return NULL; +} + +static struct sg_table *pswiotlb_dma_alloc_noncontiguous_distribute(struct device *dev, + size_t size, enum dma_data_direction dir, gfp_t gfp, + unsigned long attrs) +{ + return alloc_single_sgt(dev, size, dir, gfp); +} + +static void __dma_free_pages(struct device *dev, size_t size, struct page *page, + dma_addr_t dma_handle, enum dma_data_direction dir) +{ + const struct dma_map_ops *ops = get_dma_ops(dev); + + size = PAGE_ALIGN(size); + if (dma_alloc_direct(dev, ops)) + dma_direct_free_pages(dev, size, page, dma_handle, dir); + else if (ops->free_pages) + ops->free_pages(dev, size, page, dma_handle, dir); +} + +static void free_single_sgt(struct device *dev, size_t size, + struct sg_table *sgt, enum dma_data_direction dir) +{ + __dma_free_pages(dev, size, sg_page(sgt->sgl), sgt->sgl->dma_address, + dir); + sg_free_table(sgt); + kfree(sgt); +} + +static void pswiotlb_dma_free_noncontiguous_distribute(struct device *dev, size_t size, + struct sg_table *sgt, enum dma_data_direction dir) +{ + free_single_sgt(dev, size, sgt, dir); +} + +static int pswiotlb_dma_get_sgtable_distribute(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, size_t size, + unsigned long attrs) +{ + return dma_direct_get_sgtable(dev, sgt, cpu_addr, dma_addr, + size, attrs); +} + +static int pswiotlb_dma_mmap_distribute(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size, + unsigned long attrs) +{ + return dma_direct_mmap(dev, vma, cpu_addr, dma_addr, size, + attrs); +} + +static u64 pswiotlb_dma_get_required_mask_distribute(struct device *dev) +{ + return dma_direct_get_required_mask(dev); +} + +static void *pswiotlb_dma_alloc_distribute(struct device *dev, size_t size, + dma_addr_t *handle, gfp_t gfp, unsigned long attrs) +{ + const struct dma_map_ops *ops = dev->orig_dma_ops; + void *cpu_addr; + + check_if_pswiotlb_is_applicable(dev); + + if (dma_alloc_direct(dev, ops)) + cpu_addr = dma_direct_alloc(dev, size, handle, gfp, attrs); + else + cpu_addr = ops->alloc(dev, size, handle, gfp, attrs); + + return cpu_addr; +} + +static void pswiotlb_dma_free_distribute(struct device *dev, size_t size, + void *cpu_addr, dma_addr_t handle, unsigned long attrs) +{ + const struct dma_map_ops *ops = dev->orig_dma_ops; + + if (dma_alloc_direct(dev, ops)) + dma_direct_free(dev, size, cpu_addr, handle, attrs); + else + ops->free(dev, size, cpu_addr, handle, attrs); +} + +static struct page *pswiotlb_dma_common_alloc_pages_distribute(struct device *dev, size_t size, + dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp) +{ + return dma_direct_alloc_pages(dev, size, dma_handle, dir, gfp); +} + +static void pswiotlb_dma_common_free_pages_distribute(struct device *dev, size_t size, + struct page *page, dma_addr_t dma_handle, enum dma_data_direction dir) +{ + dma_direct_free_pages(dev, size, page, dma_handle, dir); +} + +static int pswiotlb_dma_supported_distribute(struct device *dev, u64 mask) +{ + return dma_direct_supported(dev, mask); +} + +static size_t pswiotlb_dma_max_mapping_size_distribute(struct device *dev) +{ + const struct dma_map_ops *ops = dev->orig_dma_ops; + + if (dma_map_direct(dev, ops)) + return dma_direct_max_mapping_size(dev); + else + return SIZE_MAX; +} + +static size_t pswiotlb_iommu_dma_opt_mapping_size(void) +{ + if (iommu_default_passthrough()) + return SIZE_MAX; + else + return iova_rcache_range(); +} + +static size_t pswiotlb_dma_opt_mapping_size_distribute(void) +{ + size_t size; + + size = pswiotlb_iommu_dma_opt_mapping_size(); + + return min(SIZE_MAX, size); +} + +static unsigned long pswiotlb_dma_get_merge_boundary_distribute(struct device *dev) +{ + return 0; /* can't merge */ +} + +static const struct dma_map_ops pswiotlb_noiommu_dma_ops = { + .flags = DMA_F_PCI_P2PDMA_SUPPORTED, + .alloc = pswiotlb_dma_alloc_distribute, + .free = pswiotlb_dma_free_distribute, + .alloc_pages = pswiotlb_dma_common_alloc_pages_distribute, + .free_pages = pswiotlb_dma_common_free_pages_distribute, + .alloc_noncontiguous = pswiotlb_dma_alloc_noncontiguous_distribute, + .free_noncontiguous = pswiotlb_dma_free_noncontiguous_distribute, + .mmap = pswiotlb_dma_mmap_distribute, + .get_sgtable = pswiotlb_dma_get_sgtable_distribute, + .map_page = pswiotlb_dma_map_page_attrs_distribute, + .unmap_page = pswiotlb_dma_unmap_page_attrs_distribute, + .map_sg = pswiotlb_dma_map_sg_attrs_distribute, + .unmap_sg = pswiotlb_dma_unmap_sg_attrs_distribute, + .sync_single_for_cpu = pswiotlb_dma_sync_single_for_cpu_distribute, + .sync_single_for_device = pswiotlb_dma_sync_single_for_device_distribute, + .sync_sg_for_cpu = pswiotlb_dma_sync_sg_for_cpu_distribute, + .sync_sg_for_device = pswiotlb_dma_sync_sg_for_device_distribute, + .map_resource = pswiotlb_dma_map_resource_distribute, + .unmap_resource = NULL, + .get_merge_boundary = pswiotlb_dma_get_merge_boundary_distribute, + .get_required_mask = pswiotlb_dma_get_required_mask_distribute, + .dma_supported = pswiotlb_dma_supported_distribute, + .max_mapping_size = pswiotlb_dma_max_mapping_size_distribute, + .opt_mapping_size = pswiotlb_dma_opt_mapping_size_distribute, +}; +struct pswiotlb_dma_map_ops *pswiotlb_clone_orig_dma_ops(struct device *dev, + const struct dma_map_ops *ops) +{ + struct pswiotlb_dma_map_ops *new_dma_ops = kmalloc(sizeof(struct pswiotlb_dma_map_ops), + GFP_KERNEL); + if (!new_dma_ops) + return NULL; + + memcpy(new_dma_ops, ops, sizeof(struct pswiotlb_dma_map_ops)); + + return new_dma_ops; +} + +void pswiotlb_setup_dma_ops(struct device *dev) +{ + const struct dma_map_ops *orig_ops = get_dma_ops(dev); + struct pswiotlb_dma_map_ops *new_ops; + struct pci_dev *pdev; + + if (dev && dev_is_pci(dev) && (pswiotlb_force_disable != true) && + is_phytium_ps_socs()) { + pdev = to_pci_dev(dev); + pdev->dev.can_use_pswiotlb = pswiotlb_is_dev_in_passthroughlist(pdev); + dev_info(&pdev->dev, "The device %s use pswiotlb because vendor 0x%04x %s in pswiotlb passthroughlist\n", + pdev->dev.can_use_pswiotlb ? "would" : "would NOT", + pdev->vendor, pdev->dev.can_use_pswiotlb ? "is NOT" : "is"); + } + + if (check_if_pswiotlb_is_applicable(dev)) { + if (!orig_ops) + set_dma_ops(dev, &pswiotlb_noiommu_dma_ops); + else { + new_ops = pswiotlb_clone_orig_dma_ops(dev, orig_ops); + if (!new_ops) { + dev_warn(dev, "Failed to clone dma ops, pswiotlb is NOT applicable\n"); + return; + } + + dev->orig_dma_ops = get_dma_ops(dev); + new_ops->alloc = pswiotlb_dma_alloc_distribute; + new_ops->map_page = pswiotlb_dma_map_page_attrs_distribute; + new_ops->unmap_page = pswiotlb_dma_unmap_page_attrs_distribute; + new_ops->map_sg = pswiotlb_dma_map_sg_attrs_distribute; + new_ops->unmap_sg = pswiotlb_dma_unmap_sg_attrs_distribute; + new_ops->sync_single_for_cpu = + pswiotlb_dma_sync_single_for_cpu_distribute; + new_ops->sync_single_for_device = + pswiotlb_dma_sync_single_for_device_distribute; + new_ops->sync_sg_for_cpu = + pswiotlb_dma_sync_sg_for_cpu_distribute; + new_ops->sync_sg_for_device = + pswiotlb_dma_sync_sg_for_device_distribute; + new_ops->max_mapping_size = + pswiotlb_dma_max_mapping_size_distribute; + new_ops->opt_mapping_size = + pswiotlb_dma_opt_mapping_size_distribute; + + set_dma_ops(dev, (const struct dma_map_ops *)new_ops); + } + } +} diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c index 3aae526cc4aacaf582321d64dd4c8bdbecb18b08..7e855ad243aa8a3a9476175b75bde36509657696 100644 --- a/kernel/power/suspend.c +++ b/kernel/power/suspend.c @@ -248,6 +248,22 @@ int suspend_valid_only_mem(suspend_state_t state) } EXPORT_SYMBOL_GPL(suspend_valid_only_mem); +#ifdef CONFIG_ARCH_PHYTIUM +int phytium_suspend_valid_mem(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_MEM: + return state == PM_SUSPEND_MEM; + case PM_SUSPEND_STANDBY: + return state == PM_SUSPEND_STANDBY; + default: + pr_err("no valid state support\n"); + return 0; + } +} +EXPORT_SYMBOL_GPL(phytium_suspend_valid_mem); +#endif + static bool sleep_state_supported(suspend_state_t state) { return state == PM_SUSPEND_TO_IDLE || diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 1bb1b8b532122240abbca9c65adc675ddacb10d9..88cdabd76949627802ff0957515053297bef7c27 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -8635,7 +8635,7 @@ int __sched __cond_resched(void) { check_inband_stage(); - if (should_resched(0)) { + if (should_resched(0) && !irqs_disabled()) { preempt_schedule_common(); return 1; } diff --git a/lib/logic_pio.c b/lib/logic_pio.c index 2ea564a400644274e8bfbe98ab0ddef616f1836d..8884b22f3a7d3b820cc01128c32a787da5ca2c87 100644 --- a/lib/logic_pio.c +++ b/lib/logic_pio.c @@ -16,6 +16,14 @@ #include #include +#ifdef CONFIG_PHYTIUM_PIO +#include "../drivers/bus/phytium_pio.h" +#endif + +__weak bool check_cpu_type(void) +{ + return false; +} /* The unique hardware address list */ static LIST_HEAD(io_range_list); static DEFINE_MUTEX(io_range_mutex); @@ -152,7 +160,7 @@ static struct logic_pio_hwaddr *find_io_range(unsigned long pio) } rcu_read_unlock(); - if (!found_range) + if (!found_range && !check_cpu_type()) pr_err("PIO entry token 0x%lx invalid\n", pio); return found_range; @@ -234,6 +242,8 @@ type logic_in##bwl(unsigned long addr) \ { \ type ret = (type)~0; \ \ + if (check_cpu_type() == true) \ + addr += MMIO_UPPER_LIMIT; \ if (addr < MMIO_UPPER_LIMIT) { \ ret = _in##bwl(addr); \ } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ @@ -242,14 +252,20 @@ type logic_in##bwl(unsigned long addr) \ if (entry) \ ret = entry->ops->in(entry->hostdata, \ addr, sizeof(type)); \ - else \ - WARN_ON_ONCE(1); \ + else { \ + if (check_cpu_type() == true) \ + ret = 1; \ + else \ + WARN_ON_ONCE(1); \ + } \ } \ return ret; \ } \ \ void logic_out##bwl(type value, unsigned long addr) \ { \ + if (check_cpu_type() == true) \ + addr += MMIO_UPPER_LIMIT; \ if (addr < MMIO_UPPER_LIMIT) { \ _out##bwl(value, addr); \ } else if (addr >= MMIO_UPPER_LIMIT && addr < IO_SPACE_LIMIT) { \ @@ -259,7 +275,8 @@ void logic_out##bwl(type value, unsigned long addr) \ entry->ops->out(entry->hostdata, \ addr, value, sizeof(type)); \ else \ - WARN_ON_ONCE(1); \ + if (!check_cpu_type()) \ + WARN_ON_ONCE(1); \ } \ } \ \ diff --git a/mm/show_mem.c b/mm/show_mem.c index b896e54e3a26cf5f2e40241db1db556efb411671..ab4f147bc0c7515d5796bf72e9b9651b46408653 100644 --- a/mm/show_mem.c +++ b/mm/show_mem.c @@ -14,6 +14,9 @@ #include #include #include +#ifdef CONFIG_ARCH_PHYTIUM +#include +#endif #include "internal.h" #include "swap.h" @@ -78,6 +81,17 @@ EXPORT_SYMBOL_GPL(si_mem_available); void si_meminfo(struct sysinfo *val) { val->totalram = totalram_pages(); +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pd2408()) { + int nid; + unsigned long total_pages = 0; + + for_each_node_state(nid, N_MEMORY) { + total_pages += node_present_pages(nid); + } + val->totalram = total_pages; + } +#endif val->sharedram = global_node_page_state(NR_SHMEM); val->freeram = global_zone_page_state(NR_FREE_PAGES); val->bufferram = nr_blockdev_pages(); @@ -100,6 +114,10 @@ void si_meminfo_node(struct sysinfo *val, int nid) for (zone_type = 0; zone_type < MAX_NR_ZONES; zone_type++) managed_pages += zone_managed_pages(&pgdat->node_zones[zone_type]); val->totalram = managed_pages; +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pd2408()) + val->totalram = node_present_pages(nid); +#endif val->sharedram = node_page_state(pgdat, NR_SHMEM); val->freeram = sum_zone_node_page_state(nid, NR_FREE_PAGES); #ifdef CONFIG_HIGHMEM diff --git a/sound/pci/hda/hda_phytium.c b/sound/pci/hda/hda_phytium.c index 443c78af3f1043b4dcd53efae111285cb95288ae..1e9789a18cfec3b9a39b56be77e7a9237699ceff 100644 --- a/sound/pci/hda/hda_phytium.c +++ b/sound/pci/hda/hda_phytium.c @@ -848,6 +848,7 @@ static int azx_first_init(struct azx *chip) struct resource *res; const struct acpi_device_id *match; + device_enable_async_suspend(hddev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); hda->regs = devm_ioremap_resource(hddev, res); if (IS_ERR(hda->regs)) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 6e16cc2ccebf4f54b17fe464373c640e7e89d089..5486ea1df2f30fe4f01661e9878a2d30e8092f00 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -1135,6 +1135,10 @@ config SND_SOC_LOCHNAGAR_SC This driver support the sound card functionality of the Cirrus Logic Lochnagar audio development board. +config SND_SOC_MA1026 + tristate "Cubiclattice MA1026 CODEC" + depends on I2C + config SND_SOC_MADERA tristate default y if SND_SOC_CS47L15=y diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index a2f54c2ca7d444061284e430be0bafd62c534147..791be6458d5849f94be39f10d43923d9925fa15d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -141,6 +141,7 @@ snd-soc-lpass-rx-macro-objs := lpass-rx-macro.o snd-soc-lpass-tx-macro-objs := lpass-tx-macro.o snd-soc-lpass-wsa-macro-objs := lpass-wsa-macro.o snd-soc-lpass-va-macro-objs := lpass-va-macro.o +snd-soc-ma1026-objs := ma1026.o snd-soc-madera-objs := madera.o snd-soc-max9759-objs := max9759.o snd-soc-max9768-objs := max9768.o @@ -526,6 +527,7 @@ obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o +obj-$(CONFIG_SND_SOC_MA1026) += snd-soc-ma1026.o obj-$(CONFIG_SND_SOC_MADERA) += snd-soc-madera.o obj-$(CONFIG_SND_SOC_MAX9759) += snd-soc-max9759.o obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o diff --git a/sound/soc/codecs/ma1026.c b/sound/soc/codecs/ma1026.c new file mode 100755 index 0000000000000000000000000000000000000000..3c35b0e3586099a16a5b09612d4b369e60e476b9 --- /dev/null +++ b/sound/soc/codecs/ma1026.c @@ -0,0 +1,1052 @@ +//SPDX-License-Identifier:GPL-2.0 +/* + * ma1026 ALSA SoC audio driver + * + * Copyright (c) Cubiclattice semiconductor.co.,Ltd + * Copyright (c) 2022-2024 PhytiumTechnology co.,"Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ma1026.h" + +#define TEMP_DEBUG 0 +#define MA_DBG(fmt...) printk(fmt) + +#define MA1026_LRCK (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT) +#define MA1026_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + #define snd_soc_write_ma1026(codec, addr, value) \ + snd_soc_write(codec, addr, value); + #define snd_soc_update_ma1026(component, reg, mask, val) \ + snd_soc_update_bits(component, reg, mask, val) +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 00, 0)) + #define snd_soc_write_ma1026(codec, addr, value) \ + snd_soc_component_write(codec, addr, value); + #define snd_soc_update_ma1026(component, reg, mask, val) \ + snd_soc_component_update_bits(component, reg, mask, val) +#else + #define snd_soc_write_ma1026(codec, addr, value) \ + snd_soc_component_write(codec, addr, value); + #define snd_soc_update_ma1026(component, reg, mask, val) \ + snd_soc_component_update_bits(component, reg, mask, val) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + #define snd_soc_read_ma1026(codec, addr) \ + snd_soc_read(codec, addr); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 00, 0)) + #define snd_soc_read_ma1026(codec, addr) \ + snd_soc_component_read32(codec, addr); +#else + #define snd_soc_read_ma1026(codec, addr) \ + snd_soc_component_read(codec, addr); +#endif + +struct sp_config { + u8 spc, mmcc, spfs; + u32 lrckfreq; +}; + +struct ma1026_priv{ + struct gpio_desc *gpiod_spk_ctl; + struct sp_config config; + struct regmap *regmap; + struct clk *mclk; + unsigned int sysclk; + struct snd_pcm_hw_constraint_list *sysclk_constraints; + int shutdwn_delay; + bool hp_mute; + bool hp_inserted; + long force_muted; + long playback_path; + long capture_path; +}; + +struct snd_pcm_constr_list { + int count; + u32 *list; +}; + +struct ma1026_mclk_div { + u32 mclk, lrckfreq; + u8 mmcc; +}; + +static struct ma1026_mclk_div ma1026_mclk_coeffs[] = { + /* MCLK, LRCK, MMCC[5:0] */ + {5644800, 11025, 0x30}, + {5644800, 22050, 0x20}, + {5644800, 44100, 0x10}, + + {6144000, 8000, 0x38}, + {6144000, 12000, 0x30}, + {6144000, 16000, 0x28}, + {6144000, 24000, 0x20}, + {6144000, 32000, 0x18}, + {6144000, 48000, 0x10}, +}; + +struct ma1026_priv *globe_ma1026; +static struct snd_soc_component *ma1026_codec; + +struct workqueue_struct *spk_monitor_wq; +struct delayed_work spk_delay_work; + + +static unsigned int rates_12288[] = { + 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12288 = { + .count = ARRAY_SIZE(rates_12288), + .list = rates_12288, +}; + +static unsigned int rates_112896[] = { + 8000, 11025, 22050, 44100, +}; + +static struct snd_pcm_hw_constraint_list constraints_112896 = { + .count = ARRAY_SIZE(rates_112896), + .list = rates_112896, +}; + +static unsigned int rates_12[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, + 48000, 88235, 96000, +}; + +static struct snd_pcm_hw_constraint_list constraints_12 = { + .count = ARRAY_SIZE(rates_12), + .list = rates_12, +}; + +static bool ma1026_volatile_reg(struct device *dev, unsigned int reg) +{ + if (reg <= 255) + return true; + else + return false; +} + +static bool ma1026_readable_reg(struct device *dev, unsigned int reg) +{ + if (reg <= 255) + return true; + else + return false; +} + +static const struct reg_default ma1026_reg_defaults[] = { + {1, 0xF1}, {2, 0xDF}, {3, 0x3F}, {4, 0x60}, + {5, 0x77}, {6, 0x04}, {7, 0x00}, {8, 0x15}, + {9, 0x06}, {10, 0x00}, {11, 0x00}, {12, 0x00}, + {13, 0x00}, {14, 0x00}, {15, 0x00}, {16, 0x00}, + {17, 0x00}, {18, 0x02}, {19, 0x02}, {20, 0x00}, + {21, 0x00}, {22, 0x00}, {23, 0x7F}, {24, 0x03}, + {25, 0x00}, {26, 0x3F}, {27, 0xE1}, {28, 0x00}, + {29, 0x00}, {30, 0x08}, {31, 0x3F}, {32, 0x3F}, + {33, 0x00}, {34, 0x00}, {35, 0x00}, {36, 0x00}, + {37, 0x3F}, {38, 0x3F}, {94, 0x00}, +}; + +static struct regmap_config ma1026_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MA1026_MAX_REGISTER, + .reg_defaults = ma1026_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ma1026_reg_defaults), + .volatile_reg = ma1026_volatile_reg, + .readable_reg = ma1026_readable_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int ma1026_set_bias_level( +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct snd_soc_codec *codec, +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 00, 0)) + struct snd_soc_component *codec, +#else + struct snd_soc_component *codec, +#endif + enum snd_soc_bias_level level) +{ + MA_DBG("ma1026" "set ma1026 bias level level = %d \n", level); + if (level == SND_SOC_BIAS_ON) { + snd_soc_component_update_bits(codec, MA1026_DMMCC, + MA1026_MCLKDIS, 0); + snd_soc_component_update_bits(codec, MA1026_PWRCTRL1, + MA1026_PDN, 0); + } + return 0; +} + +static DECLARE_TLV_DB_SCALE(micpga_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(ipdig_tlv, -9600, 100, 0); +static DECLARE_TLV_DB_SCALE(adc_20db_tlv, 0, 2000, 0); + +static const char * const ma1026_pgaa[] = {"Line 1", "Mic 1"}; +static const char * const ma1026_pgab[] = {"Line 2", "Mic 2"}; + +static const struct soc_enum pgaa_enum = + SOC_ENUM_SINGLE(MA1026_ADCIPC, 3, ARRAY_SIZE(ma1026_pgaa), ma1026_pgaa); + +static const struct soc_enum pgab_enum = + SOC_ENUM_SINGLE(MA1026_ADCIPC, 7, ARRAY_SIZE(ma1026_pgab), ma1026_pgab); + +static const char * const ma1026_ip_swap[] = { + "Stereo", + "Mono 1", + "Mono 2", + "Swap 1/2" +}; + +static const struct soc_enum ip_swap_enum = + SOC_ENUM_SINGLE(MA1026_MIOPC, 6, + ARRAY_SIZE(ma1026_ip_swap), ma1026_ip_swap); + +static const char * const ma1026_ng_delay[] = { + "50ms", + "100ms", + "150ms", + "200ms" +}; + +static const struct soc_enum ng_delay_enum = + SOC_ENUM_SINGLE(MA1026_NGCAB, 0, + ARRAY_SIZE(ma1026_ng_delay), ma1026_ng_delay); + +static const struct snd_kcontrol_new pgaa_mux = + SOC_DAPM_ENUM("Left Analog Input Capture Mux", pgaa_enum); + +static const struct snd_kcontrol_new pgab_mux = + SOC_DAPM_ENUM("Right Analog Input Capture Mux", pgab_enum); + +static const struct snd_kcontrol_new input_left_mixer[] = { + SOC_DAPM_SINGLE("ADC Left Input", MA1026_PWRCTRL1, 5, 1, 1), + SOC_DAPM_SINGLE("DMIC Left Input", MA1026_PWRCTRL1, 4, 1, 1), +}; + +static const struct snd_kcontrol_new input_right_mixer[] = { + SOC_DAPM_SINGLE("ADC Right Input", MA1026_PWRCTRL1, 7, 1, 1), + SOC_DAPM_SINGLE("DMIC Right Input", MA1026_PWRCTRL1, 6, 1, 1), +}; + +//static DECLARE_TLV_DB_SCALE(hpd_tlv, -10200, 50, 0); +static DECLARE_TLV_DB_SCALE(hpa_tlv, -5400, 200, 0); + +static const unsigned int limiter_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 2, TLV_DB_SCALE_ITEM(-3000, 600, 0), + 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0), +}; + +static const char * const ma1026_playback_path_mode[] = { + "OPEN SPK", + "CLOSE SPK", + "OPEN HP A", + "CLOSE HP A", + "OPEN HP B", + "CLOSE HP B", + "OPEN HP", + "CLOSE HP" +}; + +static const char * const ma1026_capture_path_mode[] = { + "OPEN DIGITAL MIC", + "CLOSE DIGITAL MIC", + "OPEN MIC A", + "CLOSE MIC A", + "OPEN MIC B", + "CLOSE MIC B" +}; + +static const char * const ma1026_mute_mode[] = { + "MUTE OFF", + "MUTE ON", +}; + +static SOC_ENUM_SINGLE_DECL(ma1026_playback_path_type, + 0, 0, ma1026_playback_path_mode); + +static SOC_ENUM_SINGLE_DECL(ma1026_capture_path_type, + 0, 0, ma1026_capture_path_mode); + +static SOC_ENUM_SINGLE_DECL(ma1026_mute_type, + 0, 0, ma1026_mute_mode); + +static int ma1026_playback_path_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(component); + + MA_DBG("ma1026 %s : playback_path %ld\n", + __func__, ma1026->playback_path); + + ucontrol->value.integer.value[0] = ma1026->playback_path; + + return 0; +} + +static int ma1026_playback_path_config(struct snd_soc_component *component, + long int pre_path, long int target_path) +{ + int reg = 0; + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(component); + + ma1026->playback_path = target_path; + + switch (ma1026->playback_path) { + case OPEN_SPK_E: + break; + + case CLOSE_SPK: + break; + + case OPEN_HP_A: + snd_soc_update_ma1026(component, MA1026_MICBSVDDA, + MA1026_MICBSVDDA_HPA, 1<<5); + snd_soc_update_ma1026(component, MA1026_PWRCTRL3, + MA1026_PDN_HP, 0); + break; + + case CLOSE_HP_A: + snd_soc_update_ma1026(component, MA1026_MICBSVDDA, + MA1026_MICBSVDDA_HPA, 0<<5); + reg = snd_soc_read_ma1026(component, MA1026_MICBSVDDA); + MA_DBG("ma1026" "%s : ma1026_MICBSVDDA = %x\n", __func__, reg); + if ((reg&MA1026_MICBSVDDA_HPB) == 0) + snd_soc_update_ma1026(component, MA1026_PWRCTRL3, + MA1026_PDN_HP, 1); + break; + + case OPEN_HP_B: + snd_soc_update_ma1026(component, MA1026_MICBSVDDA, + MA1026_MICBSVDDA_HPB, 1<<6); + snd_soc_update_ma1026(component, MA1026_PWRCTRL3, + MA1026_PDN_HP, 0); + break; + + case CLOSE_HP_B: + snd_soc_update_ma1026(component, MA1026_MICBSVDDA, + MA1026_MICBSVDDA_HPB, 0<<6); + reg = snd_soc_read_ma1026(component, MA1026_MICBSVDDA); + MA_DBG("ma1026" "%s : ma1026_MICBSVDDA = %x\n", __func__, reg); + if ((reg&MA1026_MICBSVDDA_HPA) == 0) + snd_soc_update_ma1026(component, MA1026_PWRCTRL3, + MA1026_PDN_HP, 1); + break; + + case OPEN_HP: + snd_soc_update_ma1026(component, MA1026_MICBSVDDA, + MA1026_MICBSVDDA_HPA, 1<<5); + snd_soc_update_ma1026(component, MA1026_MICBSVDDA, + MA1026_MICBSVDDA_HPB, 1<<6); + break; + + case CLOSE_HP: + snd_soc_update_ma1026(component, MA1026_MICBSVDDA, + MA1026_MICBSVDDA_HPB, 0<<6); + snd_soc_update_ma1026(component, MA1026_MICBSVDDA, + MA1026_MICBSVDDA_HPB, 0<<5); + snd_soc_update_ma1026(component, MA1026_PWRCTRL3, + MA1026_PDN_HP, 1); + break; + } + + return 0; +} + +static int ma1026_playback_path_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(component); + static int isSet; + + if ((ma1026->playback_path == ucontrol->value.integer.value[0]) && + (isSet == 1)) { + MA_DBG("ma1026" "%s : playback_path is not changed!\n", + __func__); + return 0; + } + isSet = 1; + return ma1026_playback_path_config(component, ma1026->playback_path, + ucontrol->value.integer.value[0]); +} + +static int ma1026_capture_path_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(component); + + MA_DBG("ma1026 %s : capture_path %ld\n", __func__, + ma1026->capture_path); + + ucontrol->value.integer.value[0] = ma1026->capture_path; + return 0; +} + +static int ma1026_capture_path_config(struct snd_soc_component *component, + long pre_path, long target_path) +{ + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(component); + + ma1026->capture_path = target_path; + + switch (ma1026->capture_path) { + case OPEN_DIGITAL_MIC: + snd_soc_update_ma1026(component, MA1026_PWRCTRL1, + MA1026_PDN_DMICB, 0<<6); + snd_soc_update_ma1026(component, MA1026_PWRCTRL1, + MA1026_PDN_DMICB, 0<<4); + snd_soc_update_ma1026(component, MA1026_PWRCTRL1, + MA1026_PDN_ADCB, 1<<7); + snd_soc_update_ma1026(component, MA1026_PWRCTRL1, + MA1026_PDN_ADCA, 1<<5); + break; + + case CLOSE_DIGITAL_MIC: + snd_soc_update_ma1026(component, MA1026_PWRCTRL1, + MA1026_PDN_DMICB, 0<<6); + snd_soc_update_ma1026(component, MA1026_PWRCTRL1, + MA1026_PDN_DMICB, 0<<4); + snd_soc_update_ma1026(component, MA1026_PWRCTRL1, + MA1026_PDN_ADCB, 0<<7); + snd_soc_update_ma1026(component, MA1026_PWRCTRL1, + MA1026_PDN_ADCA, 0<<5); + break; + + case SELECT_MIC_A: + snd_soc_update_ma1026(component, MA1026_ADCIPC, + MA1026_ADCIPC_MICB_LINEB, 1<<7); + break; + + case SELECT_LINE_A: + snd_soc_update_ma1026(component, MA1026_ADCIPC, + MA1026_ADCIPC_MICB_LINEB, 0<<7); + break; + + case SELECT_MIC_B: + snd_soc_update_ma1026(component, MA1026_ADCIPC, + MA1026_ADCIPC_MICA_LINEA, 1<<3); + break; + + case SELECT_LINE_B: + snd_soc_update_ma1026(component, MA1026_ADCIPC, + MA1026_ADCIPC_MICA_LINEA, 0<<3); + break; + + default: + break; + } + + return 0; +} + +static int ma1026_capture_path_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(component); + static int isSet; + + if ((ma1026->capture_path == ucontrol->value.integer.value[0]) && + (isSet == 1)) { + MA_DBG("ma1026" "%s : capture_path is not changed!\n", + __func__); + return 0; + } + isSet = 1; + return ma1026_capture_path_config(component, ma1026->capture_path, + ucontrol->value.integer.value[0]); +} + +static int ma1026_mute_path_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(component); + + MA_DBG("ma1026 %s : force_muted %ld\n", __func__, ma1026->force_muted); + + ucontrol->value.integer.value[0] = ma1026->force_muted; + return 0; +} + +static int ma1026_muted_path_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(component); + + if (!ucontrol->value.integer.value[0]) { + ma1026->force_muted = 0; + snd_soc_update_ma1026(component, MA1026_HPDC, 1<<1, 0<<1); + snd_soc_update_ma1026(component, MA1026_HPDC, 1<<0, 0<<0); + } else { + ma1026->force_muted = 1; + snd_soc_update_ma1026(component, MA1026_HPDC, 1<<1, 1<<1); + snd_soc_update_ma1026(component, MA1026_HPDC, 1<<0, 1<<0); + } + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1); + +static const struct snd_kcontrol_new ma1026_snd_controls[] = { + /* range 0-30 */ + SOC_DOUBLE_R_S_TLV("HP_A_PlaybackVolume", + MA1026_HPAAVOL, MA1026_HPBAVOL, 1, + -27, 3, 5, 0, hpa_tlv), + + SOC_DOUBLE_R("HP Analog Playback Mute", MA1026_HPAAVOL, + MA1026_HPBAVOL, 7, 1, 1), + + /* range 0-24 */ + SOC_DOUBLE_R_TLV("Input PGA Analog Volume", + MA1026_MICPGAAVOL, MA1026_MICPGABVOL, 0, + 0x18, 0, micpga_tlv), + + /* range 0-108 */ + SOC_DOUBLE_R_S_TLV("Input Path Digital Volume", + MA1026_IPADVOL, MA1026_IPBDVOL, 0x00, + -96, 12, 7, 0, ipdig_tlv), + + SOC_DOUBLE_R("MIC Preamp Switch", MA1026_MICPGAAVOL, + MA1026_MICPGABVOL, 6, 1, 1), + + SOC_SINGLE_TLV("ADC A 20db", + MA1026_ADCIPC, 2, + 0x01, 1, adc_20db_tlv), + + SOC_SINGLE_TLV("ADC B 20db", + MA1026_ADCIPC, 6, + 0x01, 1, adc_20db_tlv), + + SOC_DOUBLE("Input Path Digital Mute", MA1026_ADCIPC, 0, 4, 1, 1), + SOC_DOUBLE("HP Digital Playback left Mute", MA1026_HPDC, 0, 1, 1, 1), + SOC_SINGLE("PGA Soft Ramp Switch", MA1026_MIOPC, 3, 1, 0), + SOC_SINGLE("Analog 0 Cross Switch", MA1026_MIOPC, 2, 1, 0), + SOC_SINGLE("Digital Soft Ramp Switch", MA1026_MIOPC, 1, 1, 0), + SOC_SINGLE("Analog Output Soft Ramp Switch", MA1026_MIOPC, 0, 1, 0), + + SOC_DOUBLE("ADC Signal Polarity Switch", MA1026_ADCIPC, 1, 5, 1, 0), + + SOC_SINGLE("HP Limiter Attack Rate", MA1026_LIMATTRATEHP, 0, 0x3F, 0), + SOC_SINGLE("HP Limiter Release Rate", MA1026_LIMRLSRATEHP, 0, 0x3F, 0), + SOC_SINGLE("HP Limiter Switch", MA1026_LIMRLSRATEHP, 7, 1, 0), + SOC_SINGLE("HP Limiter All Channels Switch", + MA1026_LIMRLSRATEHP, 6, 1, 0), + + SOC_SINGLE_TLV("HP Limiter Max Threshold Vol", MA1026_LMAXMINHP, + 5, 7, 1, limiter_tlv), + SOC_SINGLE_TLV("HP Limiter Min Threshold Vol", MA1026_LMAXMINHP, + 2, 7, 1, limiter_tlv), + + SOC_SINGLE("ALC Attack Rate Volume", MA1026_ALCARATE, 0, 0x3F, 0), + SOC_SINGLE("ALC Release Rate Volume", MA1026_ALCRRATE, 0, 0x3F, 0), + SOC_DOUBLE("ALC Switch", MA1026_ALCARATE, 6, 7, 1, 0), + + SOC_SINGLE_TLV("ALC Max Threshold Volume", MA1026_ALCMINMAX, + 5, 7, 0, limiter_tlv), + SOC_SINGLE_TLV("ALC Min Threshold Volume", MA1026_ALCMINMAX, + 2, 7, 0, limiter_tlv), + + SOC_DOUBLE("NG Enable Switch", MA1026_NGCAB, 6, 7, 1, 0), + SOC_SINGLE("NG Boost Switch", MA1026_NGCAB, 5, 1, 0), + SOC_SINGLE("NG Threshold", MA1026_NGCAB, 2, 7, 0), + SOC_SINGLE("HP Amp CTRL", MA1026_PWRCTRL3, 0, 1, 0), + + SOC_ENUM("NG Delay", ng_delay_enum), + + SOC_DOUBLE_R_TLV("IP2ASP Volume", MA1026_IPA2ASPA, MA1026_IPB2ASPB, + 0, 0x3F, 1, attn_tlv), + SOC_DOUBLE_R_TLV("ASP2ASP Volume", MA1026_ASPA2ASPA, MA1026_ASPB2ASPB, + 0, 0x3F, 1, attn_tlv), + SOC_DOUBLE_R_TLV("IP2HP Volume", MA1026_IPA2HPA, MA1026_IPB2HPB, + 0, 0x3F, 1, attn_tlv), + SOC_DOUBLE_R_TLV("ASP2HP Volume", MA1026_ASPA2HPA, MA1026_ASPB2HPB, + 0, 0x3F, 1, attn_tlv), + + SOC_ENUM("IP Digital Swap/Mono Select", ip_swap_enum), + SOC_ENUM_EXT("Playback Opt", ma1026_playback_path_type, + ma1026_playback_path_get, ma1026_playback_path_put), + SOC_ENUM_EXT("Capture MIC Opt", ma1026_capture_path_type, + ma1026_capture_path_get, ma1026_capture_path_put), + SOC_ENUM_EXT("Mute", ma1026_mute_type, + ma1026_mute_path_get, ma1026_muted_path_put), +}; + +static int ma1026_init_regs( +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct snd_soc_codec *codec +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + struct snd_soc_component *codec +#endif +) +{ + MA_DBG("ma1026" " init all regs...\n"); +#if TEMP_DEBUG + snd_soc_write_ma1026(codec, 0x00, 0x68); + snd_soc_write_ma1026(codec, ma1026_PWRCTRL1, 0x00); + snd_soc_update_ma1026(codec, ma1026_PWRCTRL1, ma1026_PDN_LDO, 1<<3); + snd_soc_update_ma1026(codec, ma1026_PWRCTRL1, ma1026_PDN, 0); + + /* Set the main clk division */ + snd_soc_update_ma1026(codec, ma1026_DMMCC, ma1026_FREQ_DIV, 2<<1); + snd_soc_update_ma1026(codec, ma1026_DMMCC, ma1026_MCLKDIS, 0); + + /* Open I2S input/output */ + snd_soc_update_ma1026(codec, ma1026_PWRCTRL2, ma1026_PDN_ASP_SDO, 0); + snd_soc_update_ma1026(codec, ma1026_PWRCTRL2, ma1026_PDN_ASP_SDI, 0); +#endif + snd_soc_write_ma1026(codec, 0x00, 0x68); + snd_soc_write_ma1026(codec, 0x0A, 0x88); + snd_soc_write_ma1026(codec, 0x01, 0x00); + snd_soc_write_ma1026(codec, 0x01, 0x08); + snd_soc_write_ma1026(codec, 0x05, 0x65); + snd_soc_write_ma1026(codec, 0x02, 0x1F); + snd_soc_write_ma1026(codec, 0x06, 0x04); + snd_soc_write_ma1026(codec, 0x02, 0x13); + snd_soc_write_ma1026(codec, 0x12, 0x02); + snd_soc_write_ma1026(codec, 0x13, 0x02); + + snd_soc_write_ma1026(codec, 0x21, 0x00); + snd_soc_write_ma1026(codec, 0x22, 0x00); + snd_soc_write_ma1026(codec, 0x23, 0x00); + snd_soc_write_ma1026(codec, 0x24, 0x00); + snd_soc_write_ma1026(codec, 0x03, 0x1c); + + snd_soc_write_ma1026(codec, 0x2c, 0xc1); + + return 0; +} + +static int ma1026_probe( +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct snd_soc_codec *codec +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + struct snd_soc_component *codec +#endif +) +{ + int ret = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct ma1026_priv *ma1026 = snd_soc_codec_get_drvdata(codec); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(codec); +#endif + ma1026_codec = codec; + + MA_DBG("ma1026" " alsa start ....\n"); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + codec->control_data = ma1026->regmap; +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + snd_soc_component_init_regmap(codec, ma1026->regmap); +#endif + + ma1026_set_bias_level(codec, SND_SOC_BIAS_ON); + + ma1026_init_regs(codec); + + ma1026->mclk = devm_clk_get(codec->dev, "mclk"); + if (PTR_ERR(ma1026->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + MA_DBG("ma1026" "alsa driver finish.... \n"); + return ret; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) +static int ma1026_remove( + struct snd_soc_codec *codec +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) +static void ma1026_remove( + struct snd_soc_component *codec +#endif +) +{ + ma1026_set_bias_level(codec, SND_SOC_BIAS_OFF); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + return 0; +#endif +} + +static int ma1026_suspend( +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct snd_soc_codec *codec +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + struct snd_soc_component *codec +#endif + ) +{ + MA_DBG("ma1026" " suspend ...\n"); + ma1026_set_bias_level(codec, SND_SOC_BIAS_OFF); + return 0; +} + +static int ma1026_resume( +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct snd_soc_codec *codec +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + struct snd_soc_component *codec +#endif + ) +{ + MA_DBG("ma1026" " resume ...\n"); + ma1026_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + return 0; +} + +static const struct snd_soc_dapm_widget dit_widgets[] = { + SND_SOC_DAPM_OUTPUT("HPOUTA"), + SND_SOC_DAPM_OUTPUT("HPOUTB"), + + SND_SOC_DAPM_INPUT("LINEIN1"), + SND_SOC_DAPM_INPUT("LINEIN2"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + + SND_SOC_DAPM_DAC("HP Amp", NULL, MA1026_PWRCTRL3, 0, 0), + + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Left Capture", SND_SOC_NOPM, + 0, 0, input_left_mixer, ARRAY_SIZE(input_left_mixer)), + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Right Capture", SND_SOC_NOPM, + 0, 0, input_right_mixer, ARRAY_SIZE(input_right_mixer)), + + SND_SOC_DAPM_ADC("DMIC Left", NULL, MA1026_PWRCTRL1, 6, 0), + SND_SOC_DAPM_ADC("DMIC Right", NULL, MA1026_PWRCTRL1, 4, 0), +}; + +static const struct snd_soc_dapm_route dit_routes[] = { + {"HPOUTA", NULL, "ASP Playback"}, + {"HPOUTA", NULL, "ASP Playback"}, +}; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) +static struct snd_soc_codec_driver soc_codec_dev_ma1026 = { +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) +static struct snd_soc_component_driver soc_codec_dev_ma1026 = { +#endif + .probe = ma1026_probe, + .remove = ma1026_remove, + .suspend = ma1026_suspend, + .resume = ma1026_resume, + .set_bias_level = ma1026_set_bias_level, + .dapm_widgets = dit_widgets, + .num_dapm_widgets = ARRAY_SIZE(dit_widgets), + .dapm_routes = dit_routes, + .num_dapm_routes = ARRAY_SIZE(dit_routes), + .controls = ma1026_snd_controls, + .num_controls = ARRAY_SIZE(ma1026_snd_controls), +}; + +static int ma1026_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + + if (playback) + msleep(50); + + return 0; +} + + +static int ma1026_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct snd_soc_codec *codec = dai->codec; + struct ma1026_priv *priv = snd_soc_codec_get_drvdata(codec); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + struct snd_soc_component *codec = dai->component; + struct ma1026_priv *priv = snd_soc_component_get_drvdata(codec); +#endif + int lrckfreq = params_rate(params); + int i, index = -1; + if (priv->config.mmcc & MA1026_MS_MASTER) { + + for (i = 0; i < ARRAY_SIZE(ma1026_mclk_coeffs); i++) { + if (ma1026_mclk_coeffs[i].lrckfreq == lrckfreq) + index = i; + } + + if (index < 0) { + MA_DBG("ma1026" " ma1026_pcm_hw_params, mclk = %d\n", index); + return -EINVAL; + } + + dev_dbg(codec->dev, "DAI[ ]: lrckfreq %u, MMCC[5:0] = %x\n", + lrckfreq, ma1026_mclk_coeffs[index].mmcc); + + priv->config.mmcc &= 0xC0; + priv->config.mmcc |= ma1026_mclk_coeffs[index].mmcc; + priv->config.spc &= 0xFC; + } else { + priv->config.spc &= 0xFC; + priv->config.spc |= MA1026_MCK_SCLK_64FS; + } + priv->config.lrckfreq = lrckfreq; + + snd_soc_write_ma1026(codec, MA1026_ASPMMCC, priv->config.mmcc); + + return 0; +} + +static int ma1026_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct snd_soc_codec *codec = codec_dai->codec; + struct ma1026_priv *priv = snd_soc_codec_get_drvdata(codec); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + struct snd_soc_component *codec = codec_dai->component; + struct ma1026_priv *priv = snd_soc_component_get_drvdata(codec); +#endif + u8 spc, mmcc; + + spc = snd_soc_read_ma1026(codec, MA1026_ASPC); + mmcc = snd_soc_read_ma1026(codec, MA1026_ASPMMCC); + + if (fmt == SND_SOC_DAIFMT_CBM_CFM) + mmcc |= MA1026_MS_MASTER; + else + mmcc &= ~MA1026_MS_MASTER; + + if (fmt == SND_SOC_DAIFMT_I2S) + spc &= ~MA1026_SPDIF_PCM; + else + spc |= MA1026_SPDIF_PCM; + + if (spc & MA1026_SPDIF_PCM) + spc &= ~MA1026_PCM_BIT_ORDER; + + priv->config.spc = spc; + priv->config.mmcc = mmcc; + + return 0; +} + +static int ma1026_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct snd_soc_codec *codec = codec_dai->codec; + struct ma1026_priv *ma1026 = snd_soc_codec_get_drvdata(codec); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + struct snd_soc_component *codec = codec_dai->component; + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(codec); +#endif + MA_DBG("ma1026" " ma1026_set_dai_sysclk, freq = %d\n", freq); + + switch (freq) { + case 11289600: + case 18432000: + case 22579200: + case 36864000: + ma1026->sysclk_constraints = &constraints_112896; + ma1026->sysclk = freq; + return 0; + case 12288000: + case 19200000: + case 16934400: + case 24576000: + case 33868800: + ma1026->sysclk_constraints = &constraints_12288; + ma1026->sysclk = freq; + return 0; + case 12000000: + ma1026->sysclk_constraints = &constraints_12; + ma1026->sysclk = freq; + return 0; + } + return -EINVAL; +} + +static int ma1026_mute(struct snd_soc_dai *dai, int mute +#if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 00, 0)) + , int stream +#endif +) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 00, 0)) + struct snd_soc_codec *codec = dai->codec; + struct ma1026_priv *ma1026 = snd_soc_codec_get_drvdata(codec); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 00, 0)) + struct snd_soc_component *codec = dai->component; + struct ma1026_priv *ma1026 = snd_soc_component_get_drvdata(codec); +#endif + + MA_DBG("ma1026" " ma1026_mute, mute = %d\n", mute); + if (ma1026->force_muted) + return 0; + + if (mute) { + snd_soc_update_ma1026(codec, MA1026_HPDC, 1<<1, 1<<1); + snd_soc_update_ma1026(codec, MA1026_HPDC, 1<<0, 1<<0); + } else { + snd_soc_update_ma1026(codec, MA1026_HPDC, 1<<1, 0<<1); + snd_soc_update_ma1026(codec, MA1026_HPDC, 1<<0, 0<<0); + } + return 0; +} + +#define ma1026_pcm_shutdown NULL + +static struct snd_soc_dai_ops ma1026_ops = { + .startup = ma1026_pcm_startup, + .hw_params = ma1026_pcm_hw_params, + .set_fmt = ma1026_set_dai_fmt, + .set_sysclk = ma1026_set_dai_sysclk, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 00, 0)) + .digital_mute = ma1026_mute, +#else + .mute_stream = ma1026_mute, +#endif +}; + +static struct snd_soc_dai_driver ma1026_dai = { + .name = "ma1026-asp", + .playback = { + .stream_name = "ASP Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MA1026_LRCK, + .formats = MA1026_FORMATS, + }, + .capture = { + .stream_name = "ASP Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MA1026_LRCK, + .formats = MA1026_FORMATS, + }, + .ops = &ma1026_ops, + .symmetric_rate = 1, +}; + +static int ma1026_i2c_probe(struct i2c_client *i2c_client) +{ + struct ma1026_priv *ma1026; + int ret; + + MA_DBG("ma1026" " run driver .... \n"); + + ma1026 = devm_kzalloc(&i2c_client->dev, sizeof(struct ma1026_priv), + GFP_KERNEL); + if (!ma1026) { + MA_DBG("ma1026 " "could not allocate codec\n"); + return -ENOMEM; + } + memset(ma1026, 0x00, sizeof(struct ma1026_priv)); + ma1026->regmap = devm_regmap_init_i2c(i2c_client, &ma1026_regmap); + if (IS_ERR(ma1026->regmap)) { + ret = PTR_ERR(ma1026->regmap); + MA_DBG("ma1026 ""regmap_init() failed: %d\n", ret); + return ret; + } + + i2c_set_clientdata(i2c_client, ma1026); + + globe_ma1026 = ma1026; + + ma1026->hp_mute = 1; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)) + ret = snd_soc_register_codec(&i2c_client->dev, + &soc_codec_dev_ma1026, &ma1026_dai, 1); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(4, 19, 0)) + ret = devm_snd_soc_register_component(&i2c_client->dev, + &soc_codec_dev_ma1026, &ma1026_dai, 1); +#endif + + if (ret < 0) { + MA_DBG("ma1026 ""%s() register codec error %d\n", + __func__, ret); + return ret; + } + + MA_DBG("ma1026" " load end ret=%d\n", ret); + + return 0; +} + +static void ma1026_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_component(&client->dev); +} + +static void ma1026_i2c_shutdown(struct i2c_client *client) +{ + if (ma1026_codec != NULL) { + msleep(20); + ma1026_set_bias_level(ma1026_codec, SND_SOC_BIAS_OFF); + } +} + +static const struct i2c_device_id ma1026_id[] = { + {"ma1026", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ma1026_id); + +static const struct of_device_id ma1026_of_match[] = { + {.compatible = "Phytium, ma1026", }, + {} +}; + +MODULE_DEVICE_TABLE(of, ma1026_of_match); + +static const struct acpi_device_id ma1026_acpi_match[] = { + { "MAMA1026", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, ma1026_acpi_match); + +static struct i2c_driver ma1026_i2c_driver = { + .driver = { + .name = "ma1026", + .of_match_table = ma1026_of_match, + .acpi_match_table = ma1026_acpi_match, + }, + .probe = ma1026_i2c_probe, + .remove = ma1026_i2c_remove, + .shutdown = ma1026_i2c_shutdown, + .id_table = ma1026_id, +}; +module_i2c_driver(ma1026_i2c_driver); + +MODULE_DESCRIPTION("Cubiclattice ma1026 driver"); +MODULE_AUTHOR("Cubiclattice Ltd"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/ma1026.h b/sound/soc/codecs/ma1026.h new file mode 100644 index 0000000000000000000000000000000000000000..05aeb15fb3dc1aeb3b93615c7626926741776e53 --- /dev/null +++ b/sound/soc/codecs/ma1026.h @@ -0,0 +1,127 @@ +#ifndef __MA1026_H__ +#define __MA1026_H__ + +/* I2C Registers */ +#define MA1026_PWRCTRL1 0x01 /* Power Ctrl 1. */ +#define MA1026_PWRCTRL2 0x02 /* Power Ctrl 2. */ +#define MA1026_PWRCTRL3 0x03 /* Power Ctrl 3. */ +#define MA1026_CPFCHC 0x04 /* Charge Pump Freq. Class H Ctrl. */ +#define MA1026_MICBSVDDA 0x05 /* MIC Bias & VDDA Voltage*/ +#define MA1026_DMMCC 0x06 /* Digital MIC & Master Clock Ctrl. */ +#define MA1026_ASPC 0x07 /* Audio Serial Port (ASP) Control. */ +#define MA1026_ASPMMCC 0x08 /* ASP Master Mode Control. */ +#define MA1026_MIOPC 0x09 /* Input & Output Path Control. */ +#define MA1026_ADCIPC 0x0A /* ADC/IP Control. */ +#define MA1026_MICPGAAVOL 0x0B /* MIC 1 PreAmp, PGAA Vol. */ +#define MA1026_MICPGABVOL 0x0C /* MIC 2 PreAmp, PGAB Vol. */ +#define MA1026_IPADVOL 0x0D /* Input Path A Digital Volume. */ +#define MA1026_IPBDVOL 0x0E /* Input Path B Digital Volume. */ +#define MA1026_HPDC 0x0F /* HP Digital Control. */ +#define MA1026_HPADVOL 0x10 /* HP A Digital Vol. */ +#define MA1026_HPBDVOL 0x11 /* HP B Digital Vol. */ +#define MA1026_HPAAVOL 0x12 /* HP A Analog Volume. */ +#define MA1026_HPBAVOL 0x13 /* HP B Analog Volume. */ +#define MA1026_ANAINVOL 0x14 /* Analog Input Path Advisory Vol. */ +#define MA1026_ASPINVOL 0x15 /* ASP Input Path Advisory Vol. */ +#define MA1026_LIMATTRATEHP 0x16 /* Limit Attack Rate HP. */ +#define MA1026_LIMRLSRATEHP 0x17 /* Limit Release Rate HP. */ +#define MA1026_LMAXMINHP 0x18 /* Limit Thresholds HP. */ +#define MA1026_ALCARATE 0x19 /* ALC Enable, Attack Rate AB. */ +#define MA1026_ALCRRATE 0x1A /* ALC Release Rate AB. */ +#define MA1026_ALCMINMAX 0x1B /* ALC Thresholds AB. */ +#define MA1026_NGCAB 0x1C /* Noise Gate Ctrl AB. */ +#define MA1026_ALCNG 0x1D /* ALC & Noise Gate Ctrl. */ +#define MA1026_MIXERCTL 0x1E /* Mixer Control. */ +#define MA1026_IPA2HPA 0x1F /* HP Left Mixer */ +#define MA1026_IPB2HPB 0x20 /* HP Right Mixer */ +#define MA1026_ASPA2HPA 0x21 /* HP Left Mixer */ +#define MA1026_ASPB2HPB 0x22 /* HP Right Mixer */ +#define MA1026_IPA2ASPA 0x23 /* ASP Left Mixer */ +#define MA1026_IPB2ASPB 0x24 /* ASP Right Mixer */ +#define MA1026_ASPA2ASPA 0x25 /* ASP Left Mixer */ +#define MA1026_ASPB2ASPB 0x26 /* ASP Right Mixer */ +#define MA1026_IM 0x5E /* Interrupt Mask */ +#define MA1026_IS 0x60 /* Interrupt Status */ +#define MA1026_MAX_REGISTER 0xff /* Total Registers */ +/* Bitfield Definitions */ + +/* MA1026_PWRCTRL1 */ +#define MA1026_PDN_ADCB (1 << 7) +#define MA1026_PDN_DMICB (1 << 6) +#define MA1026_PDN_ADCA (1 << 5) +#define MA1026_PDN_DMICA (1 << 4) +#define MA1026_PDN_LDO (1 << 3) +#define MA1026_PDN (1 << 0) + +/* MA1026_PWRCTRL2 */ +#define MA1026_PDN_MICBS2 (1 << 7) +#define MA1026_PDN_MICBS1 (1 << 6) +#define MA1026_PDN_ASP_SDO (1 << 3) +#define MA1026_PDN_ASP_SDI (1 << 2) + +/* MA1026_PWRCTRL3 */ +#define MA1026_PDN_HP (1 << 0) + +#define MA1026_CP_MASK (0xF0) + +/* MA1026_ASPC */ +#define MA1026_SP_3ST (1 << 7) +#define MA1026_SPDIF_PCM (1 << 6) +#define MA1026_PCM_BIT_ORDER (1 << 3) +#define MA1026_MCK_SCLK_64FS (0 << 0) +#define MA1026_MCK_SCLK_MCLK (2 << 0) +#define MA1026_MCK_SCLK_PREMCLK (3 << 0) + +/* MA1026_ASPMMCC */ +#define MA1026_MS_MASTER (1 << 7) + + +/* MA1026_DMMCC */ +#define MA1026_MCLKDIS (1 << 0) +#define MA1026_FREQ_DIV (2 << 1) +/* IS1, IM1 */ +#define MA1026_MIC_SDET (1 << 6) + +/* Analog Softramp */ +#define MA1026_ANLGOSFT (1 << 0) + +/* MA1026 MCLK 0 from EXT CLK; 1 from PLL OUTPUT*/ +#define MA1026_CLKID 0 + +/*MA1026_MICBSVDDA */ +#define MA1026_MICBSVDDA_HPA (1<<5) +#define MA1026_MICBSVDDA_HPB (1<<6) + +/*MA1026_ADCIPC */ +#define MA1026_ADCIPC_MICB_LINEB (1<<7) +#define MA1026_ADCIPC_MICA_LINEA (1<<3) + +enum output +{ + OPEN_SPK_E, + CLOSE_SPK, + + OPEN_HP_A, + CLOSE_HP_A, + + OPEN_HP_B, + CLOSE_HP_B, + + OPEN_HP, + CLOSE_HP, +} ; +enum input +{ + OPEN_DIGITAL_MIC, + CLOSE_DIGITAL_MIC, + + SELECT_MIC_A, + SELECT_LINE_A, + + SELECT_MIC_B, + SELECT_LINE_B, + +}; + + +#endif /* __MA1026_H__ */ diff --git a/sound/soc/codecs/phytium-codec-v2.c b/sound/soc/codecs/phytium-codec-v2.c index f117e76ed3bd5cba41af6f7fb1650528b2dbef37..c558e35b7e1575f93c152729a12188859e5be8fe 100644 --- a/sound/soc/codecs/phytium-codec-v2.c +++ b/sound/soc/codecs/phytium-codec-v2.c @@ -2,7 +2,7 @@ /* * Phytium CODEC ALSA SoC Audio driver * - * Copyright (C) 2024, Phytium Technology Co., Ltd. + * Copyright (C) 2023-2024, Phytium Technology Co., Ltd. * */ @@ -32,7 +32,7 @@ #include "phytium-codec-v2.h" -#define PHYT_CODEC_V2_VERSION "1.1.0" +#define PHYT_CODEC_V2_VERSION "1.1.1" #define PHYTIUM_RATES (SNDRV_PCM_RATE_192000 | \ SNDRV_PCM_RATE_96000 | \ SNDRV_PCM_RATE_88200 | \ @@ -146,6 +146,7 @@ int phyt_codec_msg_set_cmd(struct phytium_codec *priv) return -EINVAL; } } else if (ans_msg->complete != PHYTCODEC_COMPLETE_SUCCESS) { + phyt_codec_show_status(ans_msg->status); dev_err(priv->dev, "receive msg; error code:%d\n", ans_msg->complete); ret = -EINVAL; @@ -184,7 +185,7 @@ static int phyt_pm_cmd(struct phytium_codec *priv, struct phytcodec_cmd *msg = priv->sharemem_base; uint16_t total_regs_len; uint8_t *regs; - int ret = 0, i; + int ret = 0, i = 0, cnt = 1; memset(msg, 0, sizeof(struct phytcodec_cmd)); @@ -194,6 +195,7 @@ static int phyt_pm_cmd(struct phytium_codec *priv, msg->cmd_subid = cmd; msg->complete = 0; msg->cmd_para.phytcodec_reg.cnt = 0; + if (cmd == PHYTCODEC_MSG_CMD_SET_RESUME) { memcpy(msg->cmd_para.phytcodec_reg.regs, priv->regs, REG_SH_LEN); phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_MASK, 0x0); @@ -202,15 +204,24 @@ static int phyt_pm_cmd(struct phytium_codec *priv, ret = phyt_codec_msg_set_cmd(priv); if (ret < 0) { dev_err(priv->dev, "set cmd_subid 0x%x failed\n", cmd); - ret = -EINVAL; - goto error; + return -EINVAL; } total_regs_len = msg->cmd_para.phytcodec_reg.total_regs_len; + if (total_regs_len % REG_SH_LEN == 0) + cnt = total_regs_len / REG_SH_LEN; + else + cnt = total_regs_len / REG_SH_LEN + 1; - if (cmd == PHYTCODEC_MSG_CMD_SET_SUSPEND || cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) { + if (cmd == PHYTCODEC_MSG_CMD_SET_SUSPEND) { regs = kmalloc(total_regs_len, GFP_KERNEL); priv->regs = regs; - while (total_regs_len > REG_SH_LEN * msg->cmd_para.phytcodec_reg.cnt) { + + for (i = 1; i < cnt; i++) { + if (msg->cmd_para.phytcodec_reg.cnt != i) { + dev_err(priv->dev, "error phytcodec_reg.cnt\n"); + ret = -EINVAL; + goto error; + } memcpy(regs, msg->cmd_para.phytcodec_reg.regs, REG_SH_LEN); regs += REG_SH_LEN; msg->complete = 0; @@ -223,15 +234,14 @@ static int phyt_pm_cmd(struct phytium_codec *priv, } memcpy(regs, msg->cmd_para.phytcodec_reg.regs, total_regs_len - REG_SH_LEN * (msg->cmd_para.phytcodec_reg.cnt - 1)); - if (cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) { - dev_dbg(priv->dev, "all codec registers:\n"); - for (i = 0; i < total_regs_len; i++) - dev_dbg(priv->dev, "0x%02x-0x%02x\n", i, priv->regs[i]); - kfree(priv->regs); - } } else if (cmd == PHYTCODEC_MSG_CMD_SET_RESUME) { regs = priv->regs; - while (total_regs_len > REG_SH_LEN * msg->cmd_para.phytcodec_reg.cnt) { + for (i = 1; i < cnt; i++) { + if (msg->cmd_para.phytcodec_reg.cnt != i) { + dev_err(priv->dev, "error phytcodec_reg.cnt\n"); + ret = -EINVAL; + goto error; + } regs += REG_SH_LEN; memcpy(msg->cmd_para.phytcodec_reg.regs, regs, REG_SH_LEN); msg->complete = 0; @@ -243,28 +253,73 @@ static int phyt_pm_cmd(struct phytium_codec *priv, } } kfree(priv->regs); + priv->regs = NULL; } + return ret; + error: + kfree(priv->regs); + priv->regs = NULL; return ret; } static int phyt_get_cmd(struct phytium_codec *priv, unsigned int cmd) { struct phytcodec_cmd *msg = priv->sharemem_base; - int ret = 0; + int ret = 0, i = 0, cnt = 1; + uint16_t total_regs_len; + uint8_t *regs; msg->reserved = 0; msg->seq = 0; msg->cmd_id = PHYTCODEC_MSG_CMD_GET; msg->cmd_subid = cmd; msg->complete = 0; + if (cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) + msg->cmd_para.phytcodec_reg.cnt = 0; ret = phyt_codec_msg_set_cmd(priv); if (ret < 0) { dev_err(priv->dev, "get cmd_subid 0x%x failed\n", cmd); - ret = -EINVAL; + return -EINVAL; } + total_regs_len = msg->cmd_para.phytcodec_reg.total_regs_len; + if (cmd == PHYTCODEC_MSG_CMD_GET_ALL_REGS) { + if (total_regs_len % REG_SH_LEN == 0) + cnt = total_regs_len / REG_SH_LEN; + else + cnt = total_regs_len / REG_SH_LEN + 1; + regs = kmalloc(total_regs_len, GFP_KERNEL); + priv->regs = regs; + for (i = 1; i < cnt; i++) { + if (msg->cmd_para.phytcodec_reg.cnt != i) { + dev_err(priv->dev, "error phytcodec_reg.cnt\n"); + ret = -EINVAL; + goto error; + } + memcpy(regs, msg->cmd_para.phytcodec_reg.regs, REG_SH_LEN); + regs += REG_SH_LEN; + msg->complete = 0; + ret = phyt_codec_msg_set_cmd(priv); + if (ret < 0) { + dev_err(priv->dev, "set cmd_subid 0x%x failed\n", cmd); + ret = -EINVAL; + goto error; + } + } + memcpy(regs, msg->cmd_para.phytcodec_reg.regs, + total_regs_len - REG_SH_LEN * (msg->cmd_para.phytcodec_reg.cnt - 1)); + for (i = 0; i < total_regs_len; i++) + dev_info(priv->dev, "0x%02x-0x%02x\n", i, priv->regs[i]); + + kfree(priv->regs); + priv->regs = NULL; + } return ret; +error: + kfree(priv->regs); + priv->regs = NULL; + return -EINVAL; } static int phyt_probe(struct snd_soc_component *component) @@ -402,19 +457,19 @@ static int phyt_hw_params(struct snd_pcm_substream *substream, priv->channels = params_channels(params); switch (params_width(params)) { - case 16: + case PHYTCODEC_FORMAT_S16: wl = 3; break; - case 18: + case PHYTCODEC_FORMAT_S18: wl = 2; break; - case 20: + case PHYTCODEC_FORMAT_S20: wl = 1; break; - case 24: + case PHYTCODEC_FORMAT_S24: wl = 0; break; - case 32: + case PHYTCODEC_FORMAT_S32: wl = 4; break; default: @@ -552,7 +607,7 @@ static int phyt_get_one_reg(struct phytium_codec *priv, uint8_t arg1, uint8_t ar msg->cmd_para.rw_data.addr = arg1; msg->cmd_para.rw_data.reg = arg2; ret = phyt_get_cmd(priv, PHYTCODEC_MSG_CMD_GET_ONE_REG); - dev_dbg(priv->dev, "val: 0x%x\n", msg->cmd_para.rw_data.val); + dev_info(priv->dev, "val: 0x%x\n", msg->cmd_para.rw_data.val); return ret; } @@ -687,7 +742,7 @@ static ssize_t debug_store(struct device *dev, struct device_attribute *da, dev_err(dev, "dump command requires one argument\n"); goto error; } - phyt_pm_cmd(priv, PHYTCODEC_MSG_CMD_GET_ALL_REGS); + phyt_get_cmd(priv, PHYTCODEC_MSG_CMD_GET_ALL_REGS); } else if (strcmp(cmd, "help") == 0) { dev_info(dev, "Available commands:\n" "dump all regs: echo \"dump\" > debug\n" @@ -732,6 +787,27 @@ static int phyt_get_channels(struct phytium_codec *priv) return channels; } +static void phyt_codec_init(struct phytium_codec *priv) +{ + phyt_disable_debug(priv); + phyt_disable_alive(priv); + priv->debug_enabled = false; + priv->alive_enabled = false; + priv->heartbeat = phyt_heartbeat; + priv->timer.expires = jiffies + msecs_to_jiffies(10000); + timer_setup(&priv->timer, phyt_timer_handle, 0); + add_timer(&priv->timer); + + if (sysfs_create_group(&priv->dev->kobj, &phyt_codec_device_group)) + dev_warn(priv->dev, "failed to create sysfs\n"); + + phyt_dai.playback.channels_max = phyt_get_channels(priv); + phyt_dai.capture.channels_max = phyt_dai.playback.channels_max; + + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_MASK, 0x0); + phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_ENABLE, 0x1); +} + static int phyt_codec_probe(struct platform_device *pdev) { struct phytium_codec *priv; @@ -774,23 +850,7 @@ static int phyt_codec_probe(struct platform_device *pdev) goto failed_regmap_init; } - phyt_disable_debug(priv); - phyt_disable_alive(priv); - priv->debug_enabled = false; - priv->alive_enabled = false; - priv->heartbeat = phyt_heartbeat; - priv->timer.expires = jiffies + msecs_to_jiffies(10000); - timer_setup(&priv->timer, phyt_timer_handle, 0); - add_timer(&priv->timer); - - if (sysfs_create_group(&pdev->dev.kobj, &phyt_codec_device_group)) - dev_warn(dev, "failed to create sysfs\n"); - - phyt_dai.playback.channels_max = phyt_get_channels(priv); - phyt_dai.capture.channels_max = phyt_dai.playback.channels_max; - - phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_MASK, 0x0); - phyt_writel_reg(priv->regfile_base, PHYTIUM_CODEC_INT_ENABLE, 0x1); + phyt_codec_init(priv); ret = devm_snd_soc_register_component(dev, &phyt_component_driver, &phyt_dai, 1); diff --git a/sound/soc/codecs/phytium-codec-v2.h b/sound/soc/codecs/phytium-codec-v2.h index b699c7276e91f4a3077d1b6a61f0318328c95877..f0515d0cfabbb3e17272f12ea3bd912720b67576 100644 --- a/sound/soc/codecs/phytium-codec-v2.h +++ b/sound/soc/codecs/phytium-codec-v2.h @@ -2,7 +2,7 @@ * * Phytium CODEC ASoC driver * - * Copyright (C) 2024, Phytium Technology Co., Ltd. + * Copyright (C) 2023-2024, Phytium Technology Co., Ltd. * */ @@ -43,6 +43,12 @@ /****************register end *****************/ +#define PHYTCODEC_FORMAT_S16 16 +#define PHYTCODEC_FORMAT_S18 18 +#define PHYTCODEC_FORMAT_S20 20 +#define PHYTCODEC_FORMAT_S24 24 +#define PHYTCODEC_FORMAT_S32 32 + #define PHYTIUM_CODEC_LSD_ID 0x701 enum phytcodec_msg_cmd_id { diff --git a/sound/soc/phytium/Kconfig b/sound/soc/phytium/Kconfig index c567699bcb568e4500cdf97cb53d9c9671ff1a43..20ddf8e26ba54b481439e3b108b25f0689e6f984 100644 --- a/sound/soc/phytium/Kconfig +++ b/sound/soc/phytium/Kconfig @@ -26,6 +26,14 @@ config SND_PMDK_ES8336 Say Y if you want to add Phytium machine support for ES8336 codecs. +config SND_PMDK_MA1026 + tristate "Phytium machine support with MA1026" + depends on I2C && SND_SOC_PHYTIUM_I2S + select SND_SOC_MA1026 + help + Say Y if you want to add Phytium machine support for + MA1026 codecs. + config SND_SOC_PHYTIUM_I2S_V2 tristate "Phytium I2S V2 Device Driver" help diff --git a/sound/soc/phytium/Makefile b/sound/soc/phytium/Makefile index b3e38c874da6629f9d1d37aec884521d0c8ca071..f00fa8a35c60307118090c6ca0a72a1b8ebb4dc1 100644 --- a/sound/soc/phytium/Makefile +++ b/sound/soc/phytium/Makefile @@ -18,3 +18,6 @@ obj-$(CONFIG_SND_SOC_PHYTIUM_I2S_V2) += snd-soc-phytium-i2s-v2.o snd-soc-phytium-machine-v2-objs := phytium-machine-v2.o obj-$(CONFIG_SND_SOC_PHYTIUM_MACHINE_V2) += snd-soc-phytium-machine-v2.o + +snd-soc-pmdk-ma1026-objs :=pmdk_ma1026.o +obj-$(CONFIG_SND_PMDK_MA1026) += snd-soc-pmdk-ma1026.o diff --git a/sound/soc/phytium/phytium-i2s-v2.c b/sound/soc/phytium/phytium-i2s-v2.c index 1817bf3ce2970e88db8d07292d4f382cb5f15ece..8df71b5d763610efce308111589b2b4f4ea5cc02 100644 --- a/sound/soc/phytium/phytium-i2s-v2.c +++ b/sound/soc/phytium/phytium-i2s-v2.c @@ -2,7 +2,7 @@ /* * Phytium I2S ASoC driver * - * Copyright (C) 2024, Phytium Technology Co., Ltd. + * Copyright (C) 2023-2024, Phytium Technology Co., Ltd. * */ @@ -30,7 +30,7 @@ #include #include "phytium-i2s-v2.h" -#define PHYT_I2S_V2_VERSION "1.0.5" +#define PHYT_I2S_V2_VERSION "1.0.6" static struct snd_soc_jack hs_jack; static irqreturn_t phyt_i2s_gpio_interrupt(int irq, void *dev_id); @@ -157,7 +157,7 @@ static int phyt_pcm_hw_params(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); - int ret; + int ret = 0; uint32_t format_val; switch (params_format(hw_params)) { @@ -185,10 +185,8 @@ static int phyt_pcm_hw_params(struct snd_soc_component *component, priv->pcm_config[DIRECTION_CAPTURE].format_val = format_val; ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); - if (ret < 0) - return ret; - return 0; + return ret; } static void phyt_bdl_entry_setup(dma_addr_t addr, uint32_t **pbdl, @@ -273,7 +271,7 @@ static int phyt_pcm_prepare(struct snd_soc_component *component, else config = CAPTRUE_ADDRESS_OFFSET; phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_DEV_ADDR(direction), - E2000_LSD_I2S_BASE + config); + LSD_I2S_BASE + config); phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_LVI(direction), frags - 1); phyt_writel_reg(priv->dma_reg_base, PHYTIUM_DMA_CHALX_CBL(direction), pcm_config->buffer_size); @@ -361,7 +359,6 @@ static snd_pcm_uframes_t phyt_pcm_pointer(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct phytium_i2s *priv = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0)); - uint32_t pos = 0; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -372,10 +369,10 @@ static snd_pcm_uframes_t phyt_pcm_pointer(struct snd_soc_component *component, return bytes_to_frames(substream->runtime, pos); } -int phyt_i2s_msg_set_cmd(struct phytium_i2s *priv, bool is_gpio) +static int phyt_i2s_msg_set_cmd(struct phytium_i2s *priv, bool is_gpio) { struct phyti2s_cmd *ans_msg; - int timeout = 40, ret = 0; + int timeout = 100, ret = 0; if (is_gpio) { phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_AP2RV_INT_STATE, @@ -690,11 +687,8 @@ static void phyt_i2s_gpio_jack_work(struct work_struct *work) int ret = 0; u32 unplug = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_HPDET); - if (IS_ERR_OR_NULL(hs_jack.card->snd_card)) { - dev_warn(priv->dev, "sound card %s not binded", - hs_jack.card->name); + if (!hs_jack.card->snd_card) return; - } if (unplug & 0x1) { if (hs_jack.jack) { @@ -842,7 +836,7 @@ void phyt_i2s_show_log(struct phytium_i2s *priv) { u32 reg, len; u8 *plog; - int i; + int i, cur_len; if (!priv->log_addr) return; @@ -853,12 +847,12 @@ void phyt_i2s_show_log(struct phytium_i2s *priv) len = strnlen((char *)priv->log_addr, LOG_SIZE_MAX); dev_info(priv->dev, "log len :%d, addr:0x%llx, size:%d\n", len, (u64)priv->log_addr, priv->log_size); - if (len > LOG_LINE_MAX_LEN) { - for (i = 0; i < len; i += LOG_LINE_MAX_LEN) - dev_info(priv->dev, "(DEV)%.*s\n", LOG_LINE_MAX_LEN, &plog[i]); - } else { - dev_info(priv->dev, "(DDR)%.*s\n", LOG_LINE_MAX_LEN, &plog[i]); + + for (i = 0; i < len; i += LOG_LINE_MAX_LEN) { + cur_len = (((len - i) < LOG_LINE_MAX_LEN) ? (len - i) : LOG_LINE_MAX_LEN); + dev_info(priv->dev, "(DEV)%.*s\n", cur_len, &plog[i]); } + for (i = 0; i < priv->log_size; i++) plog[i] = 0; } @@ -866,12 +860,13 @@ void phyt_i2s_show_log(struct phytium_i2s *priv) phyt_writel_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG, reg); } -int phyt_i2s_alloc_log_mem(struct phytium_i2s *priv) +int phyt_i2s_init_log(struct phytium_i2s *priv) { u32 reg; u64 phy_addr; reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + reg = reg | DEBUG_ENABLE; phy_addr = ((reg & ADDR_MASK) >> ADDR_LOW_SHIFT) << ADDR_SHIFT; priv->log_size = ((reg & LOG_SIZE_MASK) >> LOG_SIZE_LOW_SHIFT) * 1024; @@ -889,6 +884,8 @@ static ssize_t phyt_i2s_debug_show(struct device *dev, struct phytium_i2s *priv = dev_get_drvdata(dev); u32 debug_reg = phyt_readl_reg(priv->regfile_base, PHYTIUM_REGFILE_DEBUG); + dev_info(dev, "echo debug(1)/alive(0) enable(1)/disable(0) > debug\n"); + return sprintf(buf, "%x\n", debug_reg); } @@ -903,8 +900,6 @@ static ssize_t phyt_i2s_debug_store(struct device *dev, int ret; long value; - dev_info(dev, "echo debug(1)/alive(0) enable(1)/disable(0) > debug\n"); - p = kmalloc(size, GFP_KERNEL); if (p == NULL) return -EINVAL; @@ -1013,10 +1008,10 @@ static int phyt_i2s_probe(struct platform_device *pdev) goto failed_ioremap_res2; } - ret = phyt_i2s_alloc_log_mem(priv); + ret = phyt_i2s_init_log(priv); if (ret != 0) { - dev_err(&pdev->dev, "failed to alloc log mem\n"); - goto failed_alloc_log_mem; + dev_err(&pdev->dev, "failed to init log\n"); + goto failed_init_log; } status = readl(priv->dma_reg_base + PHYTIUM_DMA_STS); @@ -1057,7 +1052,7 @@ static int phyt_i2s_probe(struct platform_device *pdev) if (pdev->dev.of_node) { ret = device_property_read_string(&pdev->dev, "dai-name", &dai_driver->name); if (ret < 0) { - dev_err(&pdev->dev, "missing dai-name property %d\n", ret); + dev_err(&pdev->dev, "missing dai-name property from device tree\n"); goto failed_get_dai_name; } clk = devm_clk_get(&pdev->dev, NULL); @@ -1103,7 +1098,7 @@ static int phyt_i2s_probe(struct platform_device *pdev) failed_get_dai_name: failed_request_irq: failed_disable_gpioint: -failed_alloc_log_mem: +failed_init_log: failed_ioremap_res2: failed_ioremap_res1: failed_ioremap_res0: diff --git a/sound/soc/phytium/phytium-i2s-v2.h b/sound/soc/phytium/phytium-i2s-v2.h index 5db90b103cd2a7e22db505449665b2d1bd7fa726..e37644d19960a6c49552fdc35e8ea63529a35de8 100644 --- a/sound/soc/phytium/phytium-i2s-v2.h +++ b/sound/soc/phytium/phytium-i2s-v2.h @@ -2,7 +2,7 @@ * * Phytium I2S ASoC driver * - * Copyright (C) 2024, Phytium Technology Co., Ltd. + * Copyright (C) 2023-2024, Phytium Technology Co., Ltd. * */ @@ -174,7 +174,7 @@ struct phyti2s_cmd { #define PHYTIUM_DMA_BDLPU(x) (0x40 * x + 0x0040) #define PHYTIUM_DMA_BDLPL(x) (0x40 * x + 0x0044) #define PHYTIUM_DMA_CHALX_DEV_ADDR(x) (0x40 * x + 0x0048) - #define E2000_LSD_I2S_BASE 0x28009000 + #define LSD_I2S_BASE 0x18009000 #define PLAYBACK_ADDRESS_OFFSET 0x1c8 #define CAPTRUE_ADDRESS_OFFSET 0x1c0 #define PHYTIUM_DMA_CHALX_LVI(x) (0x40 * x + 0x004c) diff --git a/sound/soc/phytium/phytium-machine-v2.c b/sound/soc/phytium/phytium-machine-v2.c index cf29c368b2219a9514e05c84010dd8df8553e7d3..d02ae022e5591d240c35327ec29458a2c847f37b 100644 --- a/sound/soc/phytium/phytium-machine-v2.c +++ b/sound/soc/phytium/phytium-machine-v2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2024, Phytium Techonology Co., Ltd. + * Copyright (c) 2023-2024, Phytium Techonology Co., Ltd. */ #include diff --git a/sound/soc/phytium/phytium_i2s.c b/sound/soc/phytium/phytium_i2s.c index 07254f15c8475768787e336e8ac372571814bc8f..5edd754021c4598b9dd44b13f6262bb4d22bcfdc 100644 --- a/sound/soc/phytium/phytium_i2s.c +++ b/sound/soc/phytium/phytium_i2s.c @@ -520,8 +520,7 @@ static struct snd_soc_dai_driver phytium_i2s_dai = { .formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, + SNDRV_PCM_FMTBIT_S24_LE, }, .capture = { .stream_name = "i2s-Capture", @@ -538,7 +537,7 @@ static struct snd_soc_dai_driver phytium_i2s_dai = { .symmetric_rate = 1, }; -static const struct snd_pcm_hardware phytium_pcm_hardware = { +static struct snd_pcm_hardware phytium_pcm_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | @@ -552,8 +551,7 @@ static const struct snd_pcm_hardware phytium_pcm_hardware = { .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE | - SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE), + SNDRV_PCM_FMTBIT_S24_LE ), .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 4096*16, @@ -627,6 +625,9 @@ static int phytium_pcm_open(struct snd_soc_component *component, if (azx_dev == NULL) return -EBUSY; + if (!dev->i2s_dp) + phytium_pcm_hardware.formats |= SNDRV_PCM_FMTBIT_S32_LE; + snd_soc_set_runtime_hwparams(substream, &phytium_pcm_hardware); snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); @@ -1505,7 +1506,11 @@ static int phytium_i2s_probe(struct platform_device *pdev) i2s->clk_base = pdata->clk_base; i2s->pcie = 1; } else if (pdev->dev.of_node) { - device_property_read_string(&pdev->dev, "dai-name", &dai_drv->name); + ret = device_property_read_string(&pdev->dev, "dai-name", &dai_drv->name); + if (ret < 0) { + dev_err(&pdev->dev, "missing dai-name property from device tree\n"); + goto failed_get_dai_name; + } i2s->pdev = &pdev->dev; clk = devm_clk_get(&pdev->dev, NULL); i2s->clk_base = clk_get_rate(clk); @@ -1525,6 +1530,14 @@ static int phytium_i2s_probe(struct platform_device *pdev) } } + if (strstr(dai_drv->name, "dp")) + i2s->i2s_dp = 1; + else + i2s->i2s_dp = 0; + + if (!i2s->i2s_dp) + dai_drv->playback.formats |= SNDRV_PCM_FMTBIT_S32_LE; + ret = devm_snd_soc_register_component(&pdev->dev, &phytium_i2s_component, dai_drv, 1); if (ret != 0) diff --git a/sound/soc/phytium/pmdk_ma1026.c b/sound/soc/phytium/pmdk_ma1026.c new file mode 100644 index 0000000000000000000000000000000000000000..a63b0fad687de0950b508b1e783e5770fadbafd9 --- /dev/null +++ b/sound/soc/phytium/pmdk_ma1026.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021-2024 Phytium Technology Co.,Ltd. + */ + +#include +#include +#include +#include +#include + +/* PMDK widgets */ +static const struct snd_soc_dapm_widget pmdk_ma1026_dapm_widgets[] = { + SND_SOC_DAPM_HP("HP", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), + SND_SOC_DAPM_MIC("Mic In", NULL), +}; + +/* PMDK control */ +static const struct snd_kcontrol_new pmdk_ma1026_controls[] = { + SOC_DAPM_PIN_SWITCH("HP"), + SOC_DAPM_PIN_SWITCH("Int Mic"), + SOC_DAPM_PIN_SWITCH("Mic In"), +}; + +/* PMDK connections */ +static const struct snd_soc_dapm_route pmdk_ma1026_audio_map[] = { + {"DMIC", NULL, "Int Mic"}, + {"MIC1", NULL, "Mic In"}, + {"MIC2", NULL, "Mic In"}, + + {"HP", NULL, "HPOL"}, + {"HP", NULL, "HPOR"}, +}; + +#define PMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ + SND_SOC_DAIFMT_CBS_CFS) + +SND_SOC_DAILINK_DEFS(pmdk_ma1026, + DAILINK_COMP_ARRAY(COMP_CPU("phytium-i2s-lsd")), + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-MAMA1026:00", "ma1026-asp")), + DAILINK_COMP_ARRAY(COMP_PLATFORM("snd-soc-dummy"))); + +static struct snd_soc_dai_link pmdk_dai[] = { + { + .name = "MA1026 ASP", + .stream_name = "MA1026 ASP", + .dai_fmt = PMDK_DAI_FMT, + SND_SOC_DAILINK_REG(pmdk_ma1026), + }, +}; + +static struct snd_soc_card pmdk = { + .name = "PMDK-I2S", + .owner = THIS_MODULE, + .dai_link = pmdk_dai, + .num_links = ARRAY_SIZE(pmdk_dai), +}; + +static int pmdk_sound_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &pmdk; + struct device *dev = &pdev->dev; + + card->dev = dev; + return devm_snd_soc_register_card(&pdev->dev, card); +} + +static const struct acpi_device_id pmdk_sound_acpi_match[] = { + { "PHYT8013", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, pmdk_sound_acpi_match); + +static const struct of_device_id phytium_ma1026_of_match[] = { + { .compatible = "phytium,audio-ma1026",}, + {}, +}; + +static struct platform_driver pmdk_sound_driver = { + .probe = pmdk_sound_probe, + .driver = { + .name = "pmdk_ma1026", + .acpi_match_table = pmdk_sound_acpi_match, + .of_match_table = phytium_ma1026_of_match, +#ifdef CONFIG_PM + .pm = &snd_soc_pm_ops, +#endif + }, +}; + +module_platform_driver(pmdk_sound_driver); + +MODULE_AUTHOR("Shi Guangyuan "); +MODULE_DESCRIPTION("ALSA SoC PMDK MA1026"); +MODULE_LICENSE("GPL");