diff --git a/Documentation/devicetree/bindings/misc/phytium,snoop-ctrl.yaml b/Documentation/devicetree/bindings/misc/phytium,snoop-ctrl.yaml new file mode 100644 index 0000000000000000000000000000000000000000..db6f165a7dc5e3e22380c10eb6011a164b216add --- /dev/null +++ b/Documentation/devicetree/bindings/misc/phytium,snoop-ctrl.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/phytium,snoop-ctrl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium BMC Snoop Controller + +maintainers: + - Lan Hengyu + +description: + The snoop controller allows the BMC to listen on and record the data + bytes written by the Host to the target I/O ports. + +properties: + compatible: + items: + - enum: + - phytium,snoop-ctrl + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + snoop-ports: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Ports to snoop + +required: + - compatible + - interrupts + - snoop-ports + +examples: + - | + snoop: snoop@28010000 { + compatible = "phytium,snoop-ctrl"; + reg = <0x0 0x28010000 0x1000>; + interrupts = ; + + snoop-ports = <0x80>; + }; diff --git a/Documentation/devicetree/bindings/pci/phytium,phytium-pcie-ep.txt b/Documentation/devicetree/bindings/pci/phytium,phytium-pcie-ep.txt new file mode 100644 index 0000000000000000000000000000000000000000..0067be87b18f7645d1a66c61ed194ae92a167ba1 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/phytium,phytium-pcie-ep.txt @@ -0,0 +1,22 @@ +* Phytium PCIe endpoint controller + +Required properties: +- compatible: Should contain "phytium,pcie-ep-1.0" or + "phytium,pcie-ep-2.0" +- reg: Should contain the controller register base address, AXI interface + region base address and hpb register base address respectively. +- reg-names: Must be "reg", "mem" and "hpb" respectively. +- max-outbound-regions: Set to maximum number of outbound regions. +- max-functions: Maximum number of functions that can be configured (default 1). + +Example: + +ep0: ep@0x29030000 { + compatible = "phytium,pcie-ep-1.0"; + reg = <0x0 0x29030000 0x0 0x10000>, + <0x11 0x00000000 0x1 0x00000000>, + <0x0 0x29101000 0x0 0x1000>; + reg-names = "reg", "mem", "hpb"; + max-outbound-regions = <3>; + max-functions = /bits/ 8 <1>; +}; diff --git a/Documentation/scheduler/sched-energy.rst b/Documentation/scheduler/sched-energy.rst index fc853c8cc346df35b90ee90dbd0501b6ac80cfd8..e3bc17afa700055354ad537162222e04ea862968 100644 --- a/Documentation/scheduler/sched-energy.rst +++ b/Documentation/scheduler/sched-energy.rst @@ -359,33 +359,9 @@ in milli-Watts or in an 'abstract scale'. 6.3 - Energy Model complexity ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The task wake-up path is very latency-sensitive. When the EM of a platform is -too complex (too many CPUs, too many performance domains, too many performance -states, ...), the cost of using it in the wake-up path can become prohibitive. -The energy-aware wake-up algorithm has a complexity of: - - C = Nd * (Nc + Ns) - -with: Nd the number of performance domains; Nc the number of CPUs; and Ns the -total number of OPPs (ex: for two perf. domains with 4 OPPs each, Ns = 8). - -A complexity check is performed at the root domain level, when scheduling -domains are built. EAS will not start on a root domain if its C happens to be -higher than the completely arbitrary EM_MAX_COMPLEXITY threshold (2048 at the -time of writing). - -If you really want to use EAS but the complexity of your platform's Energy -Model is too high to be used with a single root domain, you're left with only -two possible options: - - 1. split your system into separate, smaller, root domains using exclusive - cpusets and enable EAS locally on each of them. This option has the - benefit to work out of the box but the drawback of preventing load - balance between root domains, which can result in an unbalanced system - overall; - 2. submit patches to reduce the complexity of the EAS wake-up algorithm, - hence enabling it to cope with larger EMs in reasonable time. - +EAS does not impose any complexity limit on the number of PDs/OPPs/CPUs but +restricts the number of CPUs to EM_MAX_NUM_CPUS to prevent overflows during +the energy estimation. 6.4 - Schedutil governor ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/MAINTAINERS b/MAINTAINERS index d265d1a06cec38b5f4ee839c6b86afd3f969774b..f92fd53bf357fd680068cc229d6c6c2a223c690c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17446,6 +17446,7 @@ F: Documentation/devicetree/bindings/ipmi/phytium,bt-bmc.yaml F: Documentation/devicetree/bindings/ipmi/phytium,kcs-bmc.yaml F: Documentation/devicetree/bindings/leds/phytnet_led.yaml F: Documentation/devicetree/bindings/mailbox/phytium,mbox.yaml +F: Documentation/devicetree/bindings/misc/phytium,snoop-ctrl.yaml F: Documentation/devicetree/bindings/mmc/phytium,mci.yaml F: Documentation/devicetree/bindings/mmc/phytium,sdci.yaml F: Documentation/devicetree/bindings/mtd/phytium,nfc.yaml @@ -17463,9 +17464,13 @@ F: Documentation/devicetree/bindings/w1/phytium,w1.yaml F: arch/arm64/boot/dts/phytium/* F: arch/arm64/include/asm/ras.h F: arch/arm64/kernel/ras.c +F: drivers/acpi/phytium_base_ctrl.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 +F: drivers/cpufreq/phytium_pstate.c +F: drivers/devfreq/phytium_dmu.c +F: drivers/devfreq/phytium_noc.c F: drivers/dma/phytium/phytium* F: drivers/edac/phytium_edac.c F: drivers/gpio/gpio-phytium* @@ -17484,6 +17489,7 @@ F: drivers/mailbox/phytium_mailbox.c 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/mmc/host/phytium-mci* F: drivers/mmc/host/phytium-sdci.* F: drivers/mtd/nand/raw/phytium_nand* diff --git a/arch/arm64/boot/dts/phytium/pe2201.dtsi b/arch/arm64/boot/dts/phytium/pe2201.dtsi index 82512bcb9f1f0d9672ab52e6dba610edcfcefbe9..ed4b674b317ccbc25260670d3ef80610467f10b8 100644 --- a/arch/arm64/boot/dts/phytium/pe2201.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2201.dtsi @@ -245,7 +245,7 @@ sgpio: sgpio@2807d000 { }; macb0: ethernet@32010000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , , @@ -258,7 +258,7 @@ macb0: ethernet@32010000 { }; macb1: ethernet@32012000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , , diff --git a/arch/arm64/boot/dts/phytium/pe2202.dtsi b/arch/arm64/boot/dts/phytium/pe2202.dtsi index 7ece269cfd8932128af4f5408a221a5fe5e0297e..4e6446da32901c573073749efa08ffc73c67d046 100644 --- a/arch/arm64/boot/dts/phytium/pe2202.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2202.dtsi @@ -176,7 +176,7 @@ sata1: sata@32014000 { }; macb0: ethernet@3200c000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200c000 0x0 0x2000>; interrupts = , , @@ -193,7 +193,7 @@ macb0: ethernet@3200c000 { }; macb1: ethernet@3200e000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200e000 0x0 0x2000>; interrupts = , , @@ -206,7 +206,7 @@ macb1: ethernet@3200e000 { }; macb2: ethernet@32010000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , , @@ -220,7 +220,7 @@ macb2: ethernet@32010000 { }; macb3: ethernet@32012000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , , diff --git a/arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts b/arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts index fe9288f6a175e222f39ceaef4f1bdabf89fadf10..2513b7b5e21ab1bfe978c3d8444039720027d6b8 100644 --- a/arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts +++ b/arch/arm64/boot/dts/phytium/pe2204-miniitx-board.dts @@ -225,7 +225,7 @@ &macb0 { }; &macb2 { - phy-mode = "rgmii"; + phy-mode = "rgmii-id"; use-mii; status = "okay"; }; diff --git a/arch/arm64/boot/dts/phytium/pe2204.dtsi b/arch/arm64/boot/dts/phytium/pe2204.dtsi index 5d8f2d6fffee3f6292e2af81e742f086690cb5c6..228264b282ee3985002e963df92ff70bd41eaa81 100644 --- a/arch/arm64/boot/dts/phytium/pe2204.dtsi +++ b/arch/arm64/boot/dts/phytium/pe2204.dtsi @@ -211,7 +211,7 @@ sata1: sata@32014000 { }; macb0: ethernet@3200c000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200c000 0x0 0x2000>; interrupts = , , @@ -229,7 +229,7 @@ macb0: ethernet@3200c000 { }; macb1: ethernet@3200e000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x3200e000 0x0 0x2000>; interrupts = , , @@ -242,7 +242,7 @@ macb1: ethernet@3200e000 { }; macb2: ethernet@32010000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32010000 0x0 0x2000>; interrupts = , , @@ -255,7 +255,7 @@ macb2: ethernet@32010000 { }; macb3: ethernet@32012000 { - compatible = "cdns,phytium-gem"; + compatible = "cdns,phytium-gem-1.0"; reg = <0x0 0x32012000 0x0 0x2000>; interrupts = , , diff --git a/arch/arm64/boot/dts/phytium/pe220x.dtsi b/arch/arm64/boot/dts/phytium/pe220x.dtsi index ad6a1846315d34b630b67a51632046a02d5a2451..40d952a1a49b5fd31dc28b0697ea93062b805038 100644 --- a/arch/arm64/boot/dts/phytium/pe220x.dtsi +++ b/arch/arm64/boot/dts/phytium/pe220x.dtsi @@ -278,6 +278,13 @@ ddma1: dma-controller@28004000 { dma-channels = <8>; }; + snoop: snoop@28010000 { + compatible = "phytium,snoop-ctrl"; + reg = <0x0 0x28010000 0x0 0x1000>; + interrupts = ; + snoop-ports = <0x80>; + }; + lpc: lpc@28010000 { compatible = "simple-mfd", "syscon"; reg = <0x0 0x28010000 0x0 0x1000>; diff --git a/arch/arm64/configs/phytium_defconfig b/arch/arm64/configs/phytium_defconfig index 8dddafbf058e6113b3a54ad774f3f751d9c71cf5..3cb88a2efebd3cb0f9acdd509031b7135b6247f5 100644 --- a/arch/arm64/configs/phytium_defconfig +++ b/arch/arm64/configs/phytium_defconfig @@ -381,6 +381,7 @@ CONFIG_SERIAL_8250_DW=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +CONFIG_SERIAL_PHYTIUM_PCI=y CONFIG_SERIAL_XILINX_PS_UART=y CONFIG_SERIAL_XILINX_PS_UART_CONSOLE=y CONFIG_SERIAL_FSL_LPUART=y @@ -836,6 +837,8 @@ CONFIG_DEVFREQ_GOV_PERFORMANCE=y CONFIG_DEVFREQ_GOV_POWERSAVE=y CONFIG_DEVFREQ_GOV_USERSPACE=y CONFIG_DEVFREQ_GOV_PASSIVE=m +CONFIG_ARM_PHYTIUM_NOC_DEVFREQ=y +CONFIG_ARM_PHYTIUM_DMU_DEVFREQ=y CONFIG_EXTCON_PTN5150=m CONFIG_EXTCON_USB_GPIO=y CONFIG_EXTCON_USBC_CROS_EC=y diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h index a51c5ae081ea1c22dfe4657f935c00c9c6ae721d..9cf44f567f2469d78193b15bde36a0e9bce4136d 100644 --- a/arch/arm64/include/asm/cputype.h +++ b/arch/arm64/include/asm/cputype.h @@ -149,7 +149,7 @@ #define MICROSOFT_CPU_PART_AZURE_COBALT_100 0xD49 /* Based on r0p0 of ARM Neoverse N2 */ -#define PHYTIUM_CPU_PART_FTC310 0x660 +#define PHYTIUM_CPU_PART_FTC303 0x303 #define PHYTIUM_CPU_PART_FTC660 0x660 #define PHYTIUM_CPU_PART_FTC661 0x661 #define PHYTIUM_CPU_PART_FTC662 0x662 @@ -223,7 +223,7 @@ #define MIDR_APPLE_M2_BLIZZARD_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD_MAX) #define MIDR_APPLE_M2_AVALANCHE_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE_MAX) #define MIDR_AMPERE1 MIDR_CPU_MODEL(ARM_CPU_IMP_AMPERE, AMPERE_CPU_PART_AMPERE1) -#define MIDR_PHYTIUM_FTC310 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC310) +#define MIDR_PHYTIUM_FTC303 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC303) #define MIDR_PHYTIUM_FTC660 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC660) #define MIDR_PHYTIUM_FTC661 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC661) #define MIDR_PHYTIUM_PS17064 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC662) diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c index dba8fcec7f33d6581848bbfd61b9e65f3413bcaa..31deddea5cfad3eafcdb77507b402a716f25485f 100644 --- a/arch/arm64/kernel/acpi.c +++ b/arch/arm64/kernel/acpi.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -517,3 +518,35 @@ 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/cpufeature.c b/arch/arm64/kernel/cpufeature.c index cc55214d4d3a29ea99fb6e7f8f54312c6a1fb00a..6355818501db73dd1ea6180e87792c340b9acad6 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1686,7 +1686,7 @@ static bool unmap_kernel_at_el0(const struct arm64_cpu_capabilities *entry, MIDR_ALL_VERSIONS(MIDR_CORTEX_A73), MIDR_ALL_VERSIONS(MIDR_HISI_TSV110), MIDR_ALL_VERSIONS(MIDR_NVIDIA_CARMEL), - MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC310), + MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC303), MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC660), MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC661), MIDR_ALL_VERSIONS(MIDR_PHYTIUM_PS17064), diff --git a/arch/arm64/kernel/proton-pack.c b/arch/arm64/kernel/proton-pack.c index 19b1fa4acddea5a20b5dce7dd5541ded72550d0b..acce23aca11ad426a35c86aae52ecb466ef2c4f7 100644 --- a/arch/arm64/kernel/proton-pack.c +++ b/arch/arm64/kernel/proton-pack.c @@ -160,7 +160,7 @@ static enum mitigation_state spectre_v2_get_cpu_hw_mitigation_state(void) MIDR_ALL_VERSIONS(MIDR_CORTEX_A55), MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53), MIDR_ALL_VERSIONS(MIDR_HISI_TSV110), - MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC310), + MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC303), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_2XX_SILVER), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_3XX_SILVER), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_SILVER), @@ -471,7 +471,7 @@ static enum mitigation_state spectre_v4_get_cpu_hw_mitigation_state(void) MIDR_ALL_VERSIONS(MIDR_CORTEX_A53), MIDR_ALL_VERSIONS(MIDR_CORTEX_A55), MIDR_ALL_VERSIONS(MIDR_BRAHMA_B53), - MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC310), + MIDR_ALL_VERSIONS(MIDR_PHYTIUM_FTC303), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_3XX_SILVER), MIDR_ALL_VERSIONS(MIDR_QCOM_KRYO_4XX_SILVER), { /* sentinel */ }, diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c index c583d1f335f8c72655105a5b2e38345ff0ac4b5d..80ba3351582fe50d37ec6bef942dcb8a942ce06e 100644 --- a/arch/arm64/kernel/setup.c +++ b/arch/arm64/kernel/setup.c @@ -403,7 +403,7 @@ static int __init topology_init(void) { int i; - for_each_possible_cpu(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); @@ -454,3 +454,24 @@ 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 14365ef842440225c2da6f289afa2e3315988f48..82c9c82ee07562b55f575a68641c39cd40996620 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -522,16 +522,14 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) { u64 hwid = processor->arm_mpidr; - if (!(processor->flags & ACPI_MADT_ENABLED)) { - 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; @@ -754,7 +752,13 @@ void __init smp_prepare_cpus(unsigned int max_cpus) if (err) continue; - set_cpu_present(cpu, true); + if (acpi_disabled) { + set_cpu_present(cpu, true); + } else { + if ((cpu_madt_gicc[cpu].flags & ACPI_MADT_ENABLED)) + set_cpu_present(cpu, true); + } + numa_store_cpu_info(cpu); } } diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index cee82b473dc50921c31e6dcfc0b573cdcf0962e9..48e7d00bacf09ae346b27346f4735f245ff65dda 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -45,6 +45,13 @@ 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 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index eaa09bf52f17609ffbb24fef7d7fccf12afe4024..e483aa370c2ef10a53f818c4f26738496880058d 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -42,6 +42,7 @@ 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/ec.c b/drivers/acpi/ec.c index 115994dfefec1e9db5fc15256ffbf6a124b74e4a..e5d77ee100b55c6336c341bc7d946e99eb76d1a6 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -31,6 +31,9 @@ #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" @@ -273,7 +276,12 @@ static bool acpi_ec_flushed(struct acpi_ec *ec) static inline u8 acpi_ec_read_status(struct acpi_ec *ec) { - u8 x = inb(ec->command_addr); + u8 x; + + if (phytium_check_cpu() == true) + x = base_ctrl_readb(ec->command_addr); + else + 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", @@ -288,7 +296,12 @@ static inline u8 acpi_ec_read_status(struct acpi_ec *ec) static inline u8 acpi_ec_read_data(struct acpi_ec *ec) { - u8 x = inb(ec->data_addr); + u8 x; + + if (phytium_check_cpu() == true) + x = base_ctrl_readb(ec->data_addr); + else + x = inb(ec->data_addr); ec->timestamp = jiffies; ec_dbg_raw("EC_DATA(R) = 0x%2.2x", x); @@ -298,14 +311,24 @@ 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); - outb(command, ec->command_addr); + + if (phytium_check_cpu() == true) + base_ctrl_writeb(ec->command_addr, command); + else + 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); - outb(data, ec->data_addr); + + if (phytium_check_cpu() == true) + base_ctrl_writeb(ec->data_addr, data); + else + outb(data, ec->data_addr); + ec->timestamp = jiffies; } @@ -371,6 +394,17 @@ 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 * -------------------------------------------------------------------------- */ @@ -415,9 +449,12 @@ 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 - enable_irq(ec->irq); - + else { + if (!phytium_check_cpu()) + enable_irq(ec->irq); + else if ((irq_to_desc(ec->irq))->depth > 0) + enable_irq(ec->irq); + } ec_dbg_drv("Polling disabled"); } } @@ -496,6 +533,10 @@ 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); } @@ -503,6 +544,11 @@ 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 } /* @@ -2117,6 +2163,10 @@ 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; } @@ -2130,7 +2180,7 @@ EXPORT_SYMBOL_GPL(acpi_ec_mark_gpe_for_wake); void acpi_ec_set_gpe_wake_mask(u8 action) { - if (pm_suspend_no_platform() && first_ec && !ec_no_wakeup) + if (!phytium_check_cpu() && pm_suspend_no_platform() && first_ec && !ec_no_wakeup) acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action); } diff --git a/drivers/acpi/phytium_base_ctrl.c b/drivers/acpi/phytium_base_ctrl.c new file mode 100644 index 0000000000000000000000000000000000000000..94bb4c78b3d30a389dc3bab060419bb04c6bcfb5 --- /dev/null +++ b/drivers/acpi/phytium_base_ctrl.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * base_ctrl driver for Phytium. + * + * Copyright (C) 2021-2024, Phytium Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "phytium_base_ctrl.h" + +#define BASE_CTRL_DRIVER_VERSION "1.1.0" + +static struct phytium_base_ctrl *boot_base_ctrl; + +int phytium_base_ctrl_irq(void) +{ + if (!boot_base_ctrl) + return -ENODEV; + + return boot_base_ctrl->irq; +} +EXPORT_SYMBOL(phytium_base_ctrl_irq); + +int base_ctrl_read_int_status(void) +{ + unsigned long flags; + u16 value; + + if (!boot_base_ctrl) + return 0; + + spin_lock_irqsave(&boot_base_ctrl->lock, flags); + value = readw(boot_base_ctrl->base + boot_base_ctrl->int_status_reg); + spin_unlock_irqrestore(&boot_base_ctrl->lock, flags); + + return value; +} +EXPORT_SYMBOL(base_ctrl_read_int_status); + +void base_ctrl_write_int_clear(int val) +{ + unsigned long flags; + + if (!boot_base_ctrl) + return; + + spin_lock_irqsave(&boot_base_ctrl->lock, flags); + writew(val, boot_base_ctrl->base + boot_base_ctrl->int_clear_reg); + spin_unlock_irqrestore(&boot_base_ctrl->lock, flags); +} +EXPORT_SYMBOL(base_ctrl_write_int_clear); + +bool phytium_check_cpu(void) +{ +#ifdef CONFIG_ARCH_PHYTIUM + if (read_cpuid_implementor() == ARM_CPU_IMP_PHYTIUM) + return true; +#endif + return false; +} +EXPORT_SYMBOL(phytium_check_cpu); + +u8 base_ctrl_readb(unsigned long offset) +{ + unsigned long flags; + u8 value; + + if (!boot_base_ctrl) + return 1; + + spin_lock_irqsave(&boot_base_ctrl->lock, flags); + value = readb(boot_base_ctrl->base + offset); + spin_unlock_irqrestore(&boot_base_ctrl->lock, flags); + return value; +} +EXPORT_SYMBOL(base_ctrl_readb); + +u32 base_ctrl_readl(unsigned long offset) +{ + unsigned long flags; + u32 value; + + if (!boot_base_ctrl) + return 1; + + spin_lock_irqsave(&boot_base_ctrl->lock, flags); + value = readl(boot_base_ctrl->base + offset); + spin_unlock_irqrestore(&boot_base_ctrl->lock, flags); + + return value; +} +EXPORT_SYMBOL(base_ctrl_readl); + +int base_ctrl_writeb(unsigned long offset, u8 value) +{ + unsigned long flags; + + if (!boot_base_ctrl) + return 0; + + spin_lock_irqsave(&boot_base_ctrl->lock, flags); + writeb(value, boot_base_ctrl->base + offset); + spin_unlock_irqrestore(&boot_base_ctrl->lock, flags); + + return 0; +} +EXPORT_SYMBOL(base_ctrl_writeb); + +int base_ctrl_writel(unsigned long offset, u32 value) +{ + unsigned long flags; + + if (!boot_base_ctrl) + return 0; + + spin_lock_irqsave(&boot_base_ctrl->lock, flags); + writel(value, boot_base_ctrl->base + offset); + spin_unlock_irqrestore(&boot_base_ctrl->lock, flags); + + return 0; +} +EXPORT_SYMBOL(base_ctrl_writel); + +static int phytium_base_ctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct phytium_base_ctrl *base_ctrl; + int error = -1; + + base_ctrl = devm_kzalloc(dev, sizeof(*base_ctrl), GFP_KERNEL); + if (!base_ctrl) + return -ENOMEM; + + base_ctrl->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + base_ctrl->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base_ctrl->base)) { + dev_err(&pdev->dev, "region map failed\n"); + return PTR_ERR(base_ctrl->base); + } + + base_ctrl->irq = platform_get_irq(pdev, 0); + if (base_ctrl->irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return base_ctrl->irq; + } + + error = device_property_read_u32(&pdev->dev, "int_state", + &base_ctrl->int_status_reg); + if (error) + base_ctrl->int_status_reg = base_ctrl_INT_STATE; + + error = device_property_read_u32(&pdev->dev, "clr_int", + &base_ctrl->int_clear_reg); + if (error) + base_ctrl->int_clear_reg = base_ctrl_CLR_INT; + + spin_lock_init(&base_ctrl->lock); + boot_base_ctrl = base_ctrl; + platform_set_drvdata(pdev, base_ctrl); + + return 0; +} + +static const struct acpi_device_id base_ctrl_acpi_match[] = { + { "PHYT0007", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, base_ctrl_acpi_match); + +static struct platform_driver phytium_base_ctrl_driver = { + .probe = phytium_base_ctrl_probe, + .driver = { + .name = "phytium_base_ctrl", + .acpi_match_table = ACPI_PTR(base_ctrl_acpi_match), + }, +}; + +module_platform_driver(phytium_base_ctrl_driver); + +MODULE_AUTHOR("Li Yuze "); +MODULE_DESCRIPTION("Phytium base_ctrl driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(BASE_CTRL_DRIVER_VERSION); diff --git a/drivers/acpi/phytium_base_ctrl.h b/drivers/acpi/phytium_base_ctrl.h new file mode 100644 index 0000000000000000000000000000000000000000..017f21e5ac6af0c36bf25736d129a805383b1ea8 --- /dev/null +++ b/drivers/acpi/phytium_base_ctrl.h @@ -0,0 +1,27 @@ +/* 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/char/ipmi/bt_bmc_phytium.c b/drivers/char/ipmi/bt_bmc_phytium.c index 1d4a50c14fa3001315a8e3291b5c221fbc157a62..69567e7fe7886aae7e0f7b2d5ca462ac847b03ec 100644 --- a/drivers/char/ipmi/bt_bmc_phytium.c +++ b/drivers/char/ipmi/bt_bmc_phytium.c @@ -72,6 +72,8 @@ #define BT_BMC_BUFFER_SIZE 256 +#define BT_BMC_DRIVER_VERSION "1.1.1" + struct bt_bmc { struct device dev; struct miscdevice miscdev; @@ -531,3 +533,4 @@ module_platform_driver(bt_bmc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Cheng Quan "); MODULE_DESCRIPTION("Phytium device interface to the KCS BMC device"); +MODULE_VERSION(KCS_BMC_DRIVER_VERSION); diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index c5cecbd89ba9cea7ad5361623463f00b98365685..0274a0e1a1b08440e62ccb515f3d300d808046f9 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -159,6 +159,28 @@ config ARM_OMAP2PLUS_CPUFREQ depends on ARCH_OMAP2PLUS default ARCH_OMAP2PLUS +config ARM_PHYTIUM_PSTATE + bool "Phytium P-state driver" + depends on ACPI_PROCESSOR + select ACPI_CPPC_LIB + default m + help + This adds the P-state driver for Phytium SoCs. + +config PHYT_PSTATE_DEFAULT_MODE + int "Phytium Processor P-state default mode" + depends on ARM_PHYTIUM_PSTATE + default 1 if ARM_PHYTIUM_PSTATE + range 1 3 + help + Select the default mode the phytium-pstate driver will use on + supported hardware. + The value set has the following meanings: + 1 -> Disabled + 2 -> Passive + 3 -> Active (EPP) + + config ARM_QCOM_CPUFREQ_NVMEM tristate "Qualcomm nvmem based CPUFreq" depends on ARCH_QCOM diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index ef8510774913113b19f3ee9ee738bc228154d188..14b280ce27d249a8430e127a213d5c543eba52ca 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ_HW) += mediatek-cpufreq-hw.o obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o +obj-$(CONFIG_ARM_PHYTIUM_PSTATE) += phytium_pstate.o obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o obj-$(CONFIG_ARM_QCOM_CPUFREQ_HW) += qcom-cpufreq-hw.o diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index df445b44e9ec0b59d566f2f1d6226e196f2d1781..ad4b7bf331ffdd020126aa5a82464331d3507470 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -86,7 +86,7 @@ static void cpufreq_governor_limits(struct cpufreq_policy *policy); static int cpufreq_set_policy(struct cpufreq_policy *policy, struct cpufreq_governor *new_gov, unsigned int new_pol); -static bool cpufreq_boost_supported(void); +bool cpufreq_boost_supported(void); /* * Two notifier lists: the "policy" list is involved in the @@ -1224,7 +1224,6 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy) cmp = &policy->kobj_unregister; up_write(&policy->rwsem); kobject_put(kobj); - /* * We need to make sure that the underlying kobj is * actually not referenced anymore by anybody before we @@ -2794,10 +2793,14 @@ int cpufreq_boost_trigger_state(int state) return ret; } -static bool cpufreq_boost_supported(void) +bool cpufreq_boost_supported(void) { + if (!cpufreq_driver) + return 0; + return cpufreq_driver->set_boost; } +EXPORT_SYMBOL_GPL(cpufreq_boost_supported); static int create_boost_sysfs_file(void) { @@ -2834,6 +2837,9 @@ EXPORT_SYMBOL_GPL(cpufreq_enable_boost_support); int cpufreq_boost_enabled(void) { + if (!cpufreq_driver) + return 0; + return cpufreq_driver->boost_enabled; } EXPORT_SYMBOL_GPL(cpufreq_boost_enabled); diff --git a/drivers/cpufreq/phytium_pstate.c b/drivers/cpufreq/phytium_pstate.c new file mode 100644 index 0000000000000000000000000000000000000000..54a6a9731bfc8f57d71e9ffb52010820d2a7a10c --- /dev/null +++ b/drivers/cpufreq/phytium_pstate.c @@ -0,0 +1,997 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * phytium_pstate.c - Phytium Processor P-state Frequency Driver + * + * Copyright (C) 2024,Phytium Technology Co.,Ltd. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define KHZ_PER_MHZ 1000 +#define REFERENCE_PERF 50 + +/* + * Available EPP Performance Levels. From large to small, + * the lower the power consumption. + */ +#define PHYT_CPPC_EPP_PERFORMANCE 0x00 +#define PHYT_CPPC_EPP_BALANCE_PERFORMANCE 0x40 +#define PHYT_CPPC_EPP_BALANCE_POWERSAVE 0x80 +#define PHYT_CPPC_EPP_POWERSAVE 0xc0 + +#define PHYT_PSTATE_TRANSITION_LATENCY 20000 +#define PHYT_PSTATE_TRANSITION_DELAY 1000 + +/* AMU Register Encoding. */ +#define PHYT_SYS_AMEVCNTR0_EL0(op2) sys_reg(3, 3, 15, 9, (op2)) +#define PHYT_SYS_AMEVCNTR0_CORE_EL0 PHYT_SYS_AMEVCNTR0_EL0(0) +#define PHYT_SYS_AMEVCNTR0_CONST_EL0 PHYT_SYS_AMEVCNTR0_EL0(1) + +#define read_corecnt() read_sysreg_s(PHYT_SYS_AMEVCNTR0_CORE_EL0) +#define read_constcnt() read_sysreg_s(PHYT_SYS_AMEVCNTR0_CONST_EL0) + +struct cppc_req_cached { + u32 max_perf_cached; + u32 min_perf_cached; + u32 desired_perf_cached; + u32 epp_cached; +}; + +struct phyt_cpudata { + int cpu; + u32 policy; + bool boost_supported; + + /* EPP feature related attributes */ + s16 epp_policy; + + struct cppc_req_cached req_cached; + bool suspended; + + /* CPC related data structure */ + struct cppc_perf_caps perf_caps; + struct cppc_perf_ctrls perf_ctrls; + struct cppc_perf_fb_ctrs perf_fb_ctrs; +}; + +/* + * enum phyt_pstate_mode - driver working mode of phytium pstate + */ +enum phyt_pstate_mode { + PHYT_PSTATE_UNDEFINED = 0, + PHYT_PSTATE_DISABLE, + PHYT_PSTATE_PASSIVE, + PHYT_PSTATE_ACTIVE, + PHYT_PSTATE_MAX, +}; + +static const char * const phyt_pstate_mode_string[] = { + [PHYT_PSTATE_UNDEFINED] = "undefined", + [PHYT_PSTATE_DISABLE] = "disable", + [PHYT_PSTATE_PASSIVE] = "passive", + [PHYT_PSTATE_ACTIVE] = "active", + NULL, +}; + +static struct cpufreq_driver *current_pstate_driver; +static struct cpufreq_driver phyt_pstate_driver; +static struct cpufreq_driver phyt_cpufreq_driver; +static int driver_state = PHYT_PSTATE_UNDEFINED; + +/* + * Phytium Energy Preference Performance (EPP) + * display strings corresponding to EPP index in the + * energy_perf_strings[] + * index String + *------------------------------------- + * 0 performance + * 1 balance_performance + * 2 balance_power + * 3 power + */ +enum energy_perf_value_index { + EPP_INDEX_PERFORMANCE = 0, + EPP_INDEX_BALANCE_PERFORMANCE, + EPP_INDEX_BALANCE_POWERSAVE, + EPP_INDEX_POWERSAVE, +}; + +static const char * const energy_perf_strings[] = { + [EPP_INDEX_PERFORMANCE] = "performance", + [EPP_INDEX_BALANCE_PERFORMANCE] = "balance_performance", + [EPP_INDEX_BALANCE_POWERSAVE] = "balance_power", + [EPP_INDEX_POWERSAVE] = "power", + NULL +}; + +static unsigned int epp_values[] = { + [EPP_INDEX_PERFORMANCE] = PHYT_CPPC_EPP_PERFORMANCE, + [EPP_INDEX_BALANCE_PERFORMANCE] = PHYT_CPPC_EPP_BALANCE_PERFORMANCE, + [EPP_INDEX_BALANCE_POWERSAVE] = PHYT_CPPC_EPP_BALANCE_POWERSAVE, + [EPP_INDEX_POWERSAVE] = PHYT_CPPC_EPP_POWERSAVE, +}; + +typedef int (*cppc_mode_transition_fn)(int); + +static inline int get_mode_idx_from_str(const char *str, size_t size) +{ + int i; + + for (i = 0; i < PHYT_PSTATE_MAX; i++) { + if (!strncmp(str, phyt_pstate_mode_string[i], size)) + return i; + } + return -EINVAL; +} + +static DEFINE_MUTEX(phyt_pstate_limits_lock); +static DEFINE_MUTEX(phyt_pstate_driver_lock); + +static s16 phyt_pstate_get_epp(struct phyt_cpudata *cpudata) +{ + u64 epp; + int ret; + + + ret = cppc_get_epp_perf(cpudata->cpu, &epp); + if (ret < 0) { + pr_debug("Could not retrieve energy perf value (%d)\n", ret); + return -EIO; + } + + return (s16)(epp & 0xff); +} + +static int phyt_pstate_get_energy_pref_index(struct phyt_cpudata *cpudata) +{ + s16 epp; + int index = -EINVAL; + + epp = phyt_pstate_get_epp(cpudata); + if (epp < 0) + return epp; + + switch (epp) { + case PHYT_CPPC_EPP_PERFORMANCE: + index = EPP_INDEX_PERFORMANCE; + break; + case PHYT_CPPC_EPP_BALANCE_PERFORMANCE: + index = EPP_INDEX_BALANCE_PERFORMANCE; + break; + case PHYT_CPPC_EPP_BALANCE_POWERSAVE: + index = EPP_INDEX_BALANCE_POWERSAVE; + break; + case PHYT_CPPC_EPP_POWERSAVE: + index = EPP_INDEX_POWERSAVE; + break; + default: + break; + } + + return index; +} + +static int phyt_pstate_set_epp(struct phyt_cpudata *cpudata, u32 epp) +{ + int ret; + struct cppc_perf_ctrls perf_ctrls; + struct cppc_req_cached req_epp_cached = cpudata->req_cached; + + perf_ctrls.energy_perf = epp; + ret = cppc_set_epp_perf(cpudata->cpu, &perf_ctrls, 1); + if (ret) { + pr_debug("failed to set energy perf value (%d)\n", ret); + return ret; + } + req_epp_cached.epp_cached = epp; + + return ret; +} + +static int phyt_pstate_set_energy_pref_index(struct phyt_cpudata *cpudata, + int pref_index) +{ + int epp = -EINVAL; + int ret; + + if (!pref_index) { + pr_debug("EPP pref_index is invalid\n"); + return -EINVAL; + } + + if (epp == -EINVAL) + epp = epp_values[pref_index]; + + if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { + pr_debug("EPP cannot be set under performance policy\n"); + return -EBUSY; + } + + ret = phyt_pstate_set_epp(cpudata, epp); + + return ret; +} + +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(6,6,58)// in v6.6.58, cppc_acpi already export these API below +unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int perf) +{ + s64 retval, offset = 0; + u64 mul, div; + + mul = caps->nominal_freq - caps->lowest_freq; + mul *= KHZ_PER_MHZ; + div = caps->nominal_perf - caps->lowest_perf; + offset = caps->nominal_freq * KHZ_PER_MHZ - + div64_u64(caps->nominal_perf * mul, div); + + retval = offset + div64_u64(perf * mul, div); + if (retval >= 0) + return retval; + return 0; +} + +unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq) +{ + s64 retval, offset = 0; + u64 mul, div; + + mul = caps->nominal_perf - caps->lowest_perf; + div = caps->nominal_freq - caps->lowest_freq; + /* + * We don't need to convert to kHz for computing offset and can + * directly use nominal_freq and lowest_freq as the div64_u64 + * will remove the frequency unit. + */ + offset = caps->nominal_perf - + div64_u64(caps->nominal_freq * mul, div); + /* But we need it for computing the perf level. */ + div *= KHZ_PER_MHZ; + + retval = offset + div64_u64(freq * mul, div); + if (retval >= 0) + return retval; + return 0; +} +#endif + +static int phyt_pstate_init_perf(struct phyt_cpudata *cpudata) +{ + int ret; + + ret = cppc_get_perf_caps(cpudata->cpu, &cpudata->perf_caps); + if (ret) + return ret; + + ret = cppc_get_auto_sel_caps(cpudata->cpu, &cpudata->perf_caps); + if (ret) { + pr_warn("failed to get auto_sel, ret: %d\n", ret); + return 0; + } + + ret = cppc_set_auto_sel(cpudata->cpu, + (driver_state == PHYT_PSTATE_PASSIVE) ? 0 : 1); + if (ret) + pr_warn("failed to set auto_sel, ret: %d\n", ret); + + return ret; +} + +static int phyt_cpufreq_verify(struct cpufreq_policy_data *policy) +{ + cpufreq_verify_within_cpu_limits(policy); + + return 0; +} + +static int phyt_cpufreq_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + unsigned int cpu = policy->cpu; + struct cpufreq_freqs freqs; + u32 desired_perf; + int ret = 0; + + desired_perf = cppc_khz_to_perf(&cpudata->perf_caps, target_freq); + /* Return if it is exactly the same perf. */ + if (desired_perf == cpudata->perf_ctrls.desired_perf) + return ret; + + cpudata->perf_ctrls.desired_perf = desired_perf; + freqs.old = policy->cur; + freqs.new = target_freq; + + cpufreq_freq_transition_begin(policy, &freqs); + ret = cppc_set_perf(cpu, &cpudata->perf_ctrls); + cpufreq_freq_transition_end(policy, &freqs, ret != 0); + + if (ret) + pr_debug("Failed to set target on CPU:%d. ret:%d\n", + cpu, ret); + + pr_debug("Set target on CPU:%d, target:%u\n", cpu, target_freq); + return ret; +} + +static int phyt_cpufreq_set_boost(struct cpufreq_policy *policy, int state) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + int ret; + + if (!cpudata->boost_supported) { + pr_err("Boost mode is not supported by this processor or SBIOS\n"); + return -EINVAL; + } + + if (state) + policy->cpuinfo.max_freq = cppc_perf_to_khz(&cpudata->perf_caps, + cpudata->perf_caps.highest_perf); + else + policy->cpuinfo.max_freq = cppc_perf_to_khz(&cpudata->perf_caps, + cpudata->perf_caps.nominal_perf); + + policy->max = policy->cpuinfo.max_freq; + + ret = freq_qos_update_request(policy->max_freq_req, policy->max); + if (ret < 0) + return ret; + + return 0; +} + +static void phyt_pstate_boost_init(struct phyt_cpudata *cpudata) +{ + u32 highest_perf, nominal_perf; + + highest_perf = cpudata->perf_caps.highest_perf; + nominal_perf = cpudata->perf_caps.nominal_perf; + + if (highest_perf <= nominal_perf) + return; + + cpudata->boost_supported = true; + current_pstate_driver->boost_enabled = false; +} + +static int phyt_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + struct device *dev; + struct phyt_cpudata *cpudata; + struct cppc_perf_caps *caps; + int ret; + + dev = get_cpu_device(policy->cpu); + if (!dev) + return -ENODEV; + + cpudata = kzalloc(sizeof(*cpudata), GFP_KERNEL); + if (!cpudata) + return -ENOMEM; + + cpudata->cpu = policy->cpu; + ret = phyt_pstate_init_perf(cpudata); + if (ret) + goto err; + + caps = &cpudata->perf_caps; + + policy->cpuinfo.transition_latency = PHYT_PSTATE_TRANSITION_LATENCY; + policy->transition_delay_us = PHYT_PSTATE_TRANSITION_DELAY; + policy->min = cppc_perf_to_khz(caps, caps->lowest_perf); + policy->max = cppc_perf_to_khz(caps, caps->nominal_perf); + policy->cpuinfo.min_freq = policy->min; + policy->cpuinfo.max_freq = policy->max; + policy->fast_switch_possible = false; + policy->driver_data = cpudata; + + /* It will be updated by governor */ + policy->cur = policy->cpuinfo.max_freq; + + phyt_pstate_boost_init(cpudata); + + return 0; +err: + kfree(cpudata); + return ret; +} + +static int phyt_cpufreq_cpu_exit(struct cpufreq_policy *policy) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + + kfree(cpudata); + + return 0; +} + +/* Sysfs attributes */ + +static ssize_t show_energy_performance_available_preferences( + struct cpufreq_policy *policy, char *buf) +{ + int i = 0; + int offset = 0; + struct phyt_cpudata *cpudata = policy->driver_data; + + if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) + return sysfs_emit_at(buf, offset, "%s\n", + energy_perf_strings[EPP_INDEX_PERFORMANCE]); + + while (energy_perf_strings[i] != NULL) + offset += sysfs_emit_at(buf, offset, "%s ", energy_perf_strings[i++]); + + offset += sysfs_emit_at(buf, offset, "\n"); + + return offset; +} + +static ssize_t store_energy_performance_preference( + struct cpufreq_policy *policy, const char *buf, size_t count) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + char str_preference[21]; + ssize_t ret; + + ret = sscanf(buf, "%20s", str_preference); + if (ret != 1) + return -EINVAL; + + ret = match_string(energy_perf_strings, -1, str_preference); + if (ret < 0) + return -EINVAL; + + mutex_lock(&phyt_pstate_limits_lock); + ret = phyt_pstate_set_energy_pref_index(cpudata, ret); + mutex_unlock(&phyt_pstate_limits_lock); + + return ret ?: count; +} + +static ssize_t show_energy_performance_preference( + struct cpufreq_policy *policy, char *buf) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + int preference; + + preference = phyt_pstate_get_energy_pref_index(cpudata); + if (preference < 0) + return preference; + + return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]); +} + +static void phyt_pstate_driver_cleanup(void) +{ + int cpu; + + for_each_present_cpu(cpu) + cppc_set_auto_sel(cpu, 0); + + driver_state = PHYT_PSTATE_DISABLE; + current_pstate_driver = NULL; +} + +static int phyt_pstate_register_driver(int mode) +{ + int ret; + + if (mode == PHYT_PSTATE_PASSIVE) + current_pstate_driver = &phyt_cpufreq_driver; + else if (mode == PHYT_PSTATE_ACTIVE) + current_pstate_driver = &phyt_pstate_driver; + else + return -EINVAL; + + driver_state = mode; + ret = cpufreq_register_driver(current_pstate_driver); + if (ret) { + phyt_pstate_driver_cleanup(); + return ret; + } + return 0; +} + +static int phyt_pstate_unregister_driver(int dummy) +{ + cpufreq_unregister_driver(current_pstate_driver); + phyt_pstate_driver_cleanup(); + + return 0; +} + +static int phyt_pstate_change_driver_mode(int mode) +{ + int ret; + + driver_state = mode; + + ret = phyt_pstate_unregister_driver(0); + if (ret) + return ret; + + ret = phyt_pstate_register_driver(mode); + if (ret) + return ret; + + return 0; +} + +static cppc_mode_transition_fn mode_state_machine[PHYT_PSTATE_MAX][PHYT_PSTATE_MAX] = { + [PHYT_PSTATE_DISABLE] = { + [PHYT_PSTATE_DISABLE] = NULL, + [PHYT_PSTATE_PASSIVE] = phyt_pstate_register_driver, + [PHYT_PSTATE_ACTIVE] = phyt_pstate_register_driver, + }, + [PHYT_PSTATE_PASSIVE] = { + [PHYT_PSTATE_DISABLE] = phyt_pstate_unregister_driver, + [PHYT_PSTATE_PASSIVE] = NULL, + [PHYT_PSTATE_ACTIVE] = phyt_pstate_change_driver_mode, + }, + [PHYT_PSTATE_ACTIVE] = { + [PHYT_PSTATE_DISABLE] = phyt_pstate_unregister_driver, + [PHYT_PSTATE_PASSIVE] = phyt_pstate_change_driver_mode, + [PHYT_PSTATE_ACTIVE] = NULL, + } +}; + +static ssize_t phyt_pstate_show_status(char *buf) +{ + if (!current_pstate_driver) + return sysfs_emit(buf, "disable\n"); + + return sysfs_emit(buf, "%s\n", phyt_pstate_mode_string[driver_state]); +} + +static int phyt_pstate_update_status(const char *buf, size_t size) +{ + int mode_idx; + + if (size > strlen("passive") || size < strlen("active")) + return -EINVAL; + + mode_idx = get_mode_idx_from_str(buf, size); + + if (mode_idx < 0 || mode_idx >= PHYT_PSTATE_MAX) + return -EINVAL; + + if (mode_state_machine[driver_state][mode_idx]) + return mode_state_machine[driver_state][mode_idx](mode_idx); + + return 0; +} + +static ssize_t status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + + mutex_lock(&phyt_pstate_driver_lock); + ret = phyt_pstate_show_status(buf); + mutex_unlock(&phyt_pstate_driver_lock); + + return ret; +} + +static ssize_t status_store(struct device *a, struct device_attribute *b, + const char *buf, size_t count) +{ + char *p = memchr(buf, '\n', count); + int ret; + + mutex_lock(&phyt_pstate_driver_lock); + ret = phyt_pstate_update_status(buf, p ? p - buf : count); + mutex_unlock(&phyt_pstate_driver_lock); + + return ret < 0 ? ret : count; +} + +cpufreq_freq_attr_rw(energy_performance_preference); +cpufreq_freq_attr_ro(energy_performance_available_preferences); +static DEVICE_ATTR_RW(status); + +static struct freq_attr *phyt_cpufreq_attr[] = { + NULL, +}; + +static struct freq_attr *phyt_pstate_attr[] = { + &energy_performance_preference, + &energy_performance_available_preferences, + NULL, +}; + +static struct attribute *pstate_global_attributes[] = { + &dev_attr_status.attr, + NULL +}; + +static const struct attribute_group phyt_pstate_global_attr_group = { + .name = "phyt_pstate", + .attrs = pstate_global_attributes, +}; + +static int phyt_pstate_cpu_init(struct cpufreq_policy *policy) +{ + struct phyt_cpudata *cpudata; + struct device *dev; + struct cppc_perf_caps *caps; + struct cppc_perf_ctrls perf_ctrls; + int ret; + + dev = get_cpu_device(policy->cpu); + if (!dev) + return -ENODEV; + + cpudata = kzalloc(sizeof(*cpudata), GFP_KERNEL); + if (!cpudata) + return -ENOMEM; + + cpudata->cpu = policy->cpu; + ret = phyt_pstate_init_perf(cpudata); + if (ret) + goto err; + + cpudata->epp_policy = 0; + cpudata->req_cached.epp_cached = phyt_pstate_get_epp(cpudata); + + caps = &cpudata->perf_caps; + + policy->cpuinfo.transition_latency = PHYT_PSTATE_TRANSITION_LATENCY; + policy->transition_delay_us = PHYT_PSTATE_TRANSITION_DELAY; + policy->min = cppc_perf_to_khz(caps, caps->lowest_perf); + policy->max = cppc_perf_to_khz(caps, caps->nominal_perf); + policy->cpuinfo.min_freq = policy->min; + policy->cpuinfo.max_freq = policy->max; + policy->fast_switch_possible = false; + policy->driver_data = cpudata; + policy->policy = CPUFREQ_POLICY_POWERSAVE; + + /* It will be updated by governor */ + policy->cur = policy->cpuinfo.max_freq; + + perf_ctrls.max_perf = caps->nominal_perf; + perf_ctrls.min_perf = caps->lowest_perf; + ret = cppc_set_perf(cpudata->cpu, &perf_ctrls); + if (ret) + pr_debug("Failed to limit freq on CPU:%d. ret:%d\n", + cpudata->cpu, ret); + + phyt_pstate_boost_init(cpudata); + + return 0; +err: + kfree(cpudata); + return ret; +} + +static int phyt_pstate_cpu_exit(struct cpufreq_policy *policy) +{ + pr_debug("CPU %d exiting\n", policy->cpu); + return 0; +} + +static void phyt_pstate_update_limit(struct cpufreq_policy *policy) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + struct cppc_perf_ctrls perf_ctrls; + struct cppc_req_cached req_epp_cached = cpudata->req_cached; + + u32 min_perf, max_perf; + s16 epp; + + max_perf = cppc_khz_to_perf(&cpudata->perf_caps, policy->max); + min_perf = cppc_khz_to_perf(&cpudata->perf_caps, policy->min); + + if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { + min_perf = max_perf; + policy->min = policy->max; + } + + perf_ctrls.max_perf = max_perf; + perf_ctrls.min_perf = min_perf; + perf_ctrls.desired_perf = 0; + cppc_set_perf(cpudata->cpu, &perf_ctrls); + + req_epp_cached.max_perf_cached = max_perf; + req_epp_cached.min_perf_cached = min_perf; + req_epp_cached.desired_perf_cached = 0; + + cpudata->epp_policy = cpudata->policy; + + /* Get epp value */ + epp = phyt_pstate_get_epp(cpudata); + if (epp < 0) + return; + + if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) + epp = 0; + + req_epp_cached.epp_cached = epp; + phyt_pstate_set_epp(cpudata, epp); +} + +static int phyt_pstate_set_policy(struct cpufreq_policy *policy) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + + if (!policy->cpuinfo.max_freq) + return -ENODEV; + + cpudata->policy = policy->policy; + + phyt_pstate_update_limit(policy); + + return 0; +} + +static void update_cpu_perf(void *val) +{ + u64 prev_core_cnt, prev_const_cnt, core_cnt, const_cnt; + u64 delta_core_cnt, delta_const_cnt; + u64 perf; + + prev_core_cnt = read_corecnt(); + prev_const_cnt = read_constcnt(); + udelay(2); + core_cnt = read_corecnt(); + const_cnt = read_constcnt(); + + delta_core_cnt = core_cnt - prev_core_cnt; + delta_const_cnt = const_cnt - prev_const_cnt; + + /* + * delta_core_cnt + * perf = ----------------- * reference_perf + * delta_const_cnt + */ + perf = div64_u64(100 * delta_core_cnt, delta_const_cnt); + perf = div64_u64(REFERENCE_PERF * perf, 100); + + *(u64 *)val = perf; +} + +static inline int read_counters_on_cpu(int cpu, smp_call_func_t func, u64 *val) +{ + if (WARN_ON_ONCE(irqs_disabled())) + return -EPERM; + + smp_call_function_single(cpu, func, val, 1); + return 0; +} + +static unsigned int phyt_pstate_get_rate(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + struct phyt_cpudata *cpudata = policy->driver_data; + u64 scale, cur_freq; + + read_counters_on_cpu(cpu, update_cpu_perf, &scale); + + cur_freq = cppc_perf_to_khz(&cpudata->perf_caps, scale); + + cpufreq_cpu_put(policy); + return cur_freq; +} + +static void phyt_pstate_epp_reenable(struct phyt_cpudata *cpudata) +{ + struct cppc_perf_ctrls perf_ctrls; + struct cppc_req_cached req_epp_cached = cpudata->req_cached; + u64 max_perf, min_perf; + + max_perf = req_epp_cached.max_perf_cached; + min_perf = req_epp_cached.min_perf_cached; + + /* Set the maximum and minimum perf level to the cached value. */ + perf_ctrls.max_perf = max_perf; + perf_ctrls.min_perf = min_perf; + perf_ctrls.desired_perf = 0; + cppc_set_perf(cpudata->cpu, &perf_ctrls); + + /* Re-enable auto_sel and write epp levels cached before suspend. */ + perf_ctrls.energy_perf = req_epp_cached.epp_cached; + cppc_set_epp_perf(cpudata->cpu, &perf_ctrls, 1); +} + +static int phyt_pstate_cpu_online(struct cpufreq_policy *policy) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + + pr_debug("Phyt CPU Core %d going online\n", cpudata->cpu); + + if (driver_state == PHYT_PSTATE_ACTIVE) + phyt_pstate_epp_reenable(cpudata); + + cpudata->suspended = false; + return 0; +} + +static int phyt_pstate_cpu_offline(struct cpufreq_policy *policy) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + + pr_debug("Phyt CPU Core %d going offline\n", cpudata->cpu); + + if (cpudata->suspended) + return 0; + + return 0; +} + +static int phyt_pstate_verify_policy(struct cpufreq_policy_data *policy) +{ + cpufreq_verify_within_cpu_limits(policy); + + return 0; +} + +static int phyt_pstate_suspend(struct cpufreq_policy *policy) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + + cpudata->suspended = true; + + return 0; +} + +static int phyt_pstate_resume(struct cpufreq_policy *policy) +{ + struct phyt_cpudata *cpudata = policy->driver_data; + int ret; + + if (cpudata->suspended) { + if (driver_state == PHYT_PSTATE_ACTIVE) { + mutex_lock(&phyt_pstate_limits_lock); + /* enable phyt pstate from suspend state*/ + phyt_pstate_epp_reenable(cpudata); + mutex_unlock(&phyt_pstate_limits_lock); + } else { + cpudata->perf_ctrls.desired_perf = policy->cur; + ret = cppc_set_perf(policy->cpu, &cpudata->perf_ctrls); + } + cpudata->suspended = false; + } + + return 0; +} + +static struct cpufreq_driver phyt_pstate_driver = { + .flags = CPUFREQ_CONST_LOOPS, + .verify = phyt_pstate_verify_policy, + .setpolicy = phyt_pstate_set_policy, + .get = phyt_pstate_get_rate, + .init = phyt_pstate_cpu_init, + .exit = phyt_pstate_cpu_exit, + .offline = phyt_pstate_cpu_offline, + .online = phyt_pstate_cpu_online, + .suspend = phyt_pstate_suspend, + .resume = phyt_pstate_resume, + .set_boost = phyt_cpufreq_set_boost, + .name = "phyt-pstate", + .attr = phyt_pstate_attr, +}; + +static struct cpufreq_driver phyt_cpufreq_driver = { + .flags = CPUFREQ_CONST_LOOPS | CPUFREQ_NEED_UPDATE_LIMITS, + .verify = phyt_cpufreq_verify, + .target = phyt_cpufreq_target, + .get = phyt_pstate_get_rate, + .init = phyt_cpufreq_cpu_init, + .exit = phyt_cpufreq_cpu_exit, + .suspend = phyt_pstate_suspend, + .resume = phyt_pstate_resume, + .set_boost = phyt_cpufreq_set_boost, + .name = "phyt-cpufreq", + .attr = phyt_cpufreq_attr, +}; + +static int __init phyt_pstate_set_driver(int mode_idx) +{ + if (mode_idx >= PHYT_PSTATE_DISABLE && mode_idx < PHYT_PSTATE_MAX) { + driver_state = mode_idx; + if (driver_state == PHYT_PSTATE_DISABLE) + pr_info("driver is explicitly disabled\n"); + + if (driver_state == PHYT_PSTATE_ACTIVE) + current_pstate_driver = &phyt_pstate_driver; + + if (driver_state == PHYT_PSTATE_PASSIVE) + current_pstate_driver = &phyt_cpufreq_driver; + + return 0; + } + + return -EINVAL; +} + +static int __init phyt_pstate_init(void) +{ + struct device *dev_root; + int ret; + + if (!acpi_cpc_valid()) { + pr_warn_once("_CPC_method is not available\n"); + return -ENODEV; + } + + /* don't keep reloading if cpufreq_driver exists */ + if (cpufreq_get_current_driver()) + return -EEXIST; + + switch (driver_state) { + case PHYT_PSTATE_UNDEFINED: + ret = phyt_pstate_set_driver(CONFIG_PHYT_PSTATE_DEFAULT_MODE); + if (ret) + return ret; + break; + case PHYT_PSTATE_DISABLE: + return -ENODEV; + case PHYT_PSTATE_PASSIVE: + case PHYT_PSTATE_ACTIVE: + break; + default: + return -EINVAL; + } + + ret = cpufreq_register_driver(current_pstate_driver); + if (ret) + pr_err("failed to register with return %d\n", ret); + + dev_root = bus_get_dev_root(&cpu_subsys); + if (dev_root) { + ret = sysfs_create_group(&dev_root->kobj, + &phyt_pstate_global_attr_group); + put_device(dev_root); + if (ret) { + pr_err("sysfs export failed %d\n", ret); + goto global_attr_free; + } + } + + return ret; + +global_attr_free: + cpufreq_unregister_driver(current_pstate_driver); + return ret; +} +device_initcall(phyt_pstate_init); + +static int __init phyt_pstate_param(char *str) +{ + size_t size; + int mode_idx; + + if (!str) + return -EINVAL; + + size = strlen(str); + mode_idx = get_mode_idx_from_str(str, size); + + return phyt_pstate_set_driver(mode_idx); +} +early_param("phyt_pstate", phyt_pstate_param); + +MODULE_AUTHOR("Li Jiayi "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium Processor P-state Frequency Driver"); diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 3c4862a752b5a3b8186f34fab44325d1f8d78bb8..c90e8fa9d7335c8e71c146245ed50cc9ef6706c0 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -150,6 +150,24 @@ config ARM_SUN8I_A33_MBUS_DEVFREQ This adds the DEVFREQ driver for the MBUS controller in some Allwinner sun8i (A33 through H3) and sun50i (A64 and H5) SoCs. +config ARM_PHYTIUM_NOC_DEVFREQ + tristate "ARM PHYTIUM NOC DEVFREQ Driver" + depends on ARCH_PHYTIUM || COMPILE_TEST + depends on ACPI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for Phytium Net On Chip. + It adjusts frequency for noc based on load bandwidth obtained from register. + +config ARM_PHYTIUM_DMU_DEVFREQ + tristate "ARM PHYTIUM DMU DEVFREQ Driver" + depends on ARCH_PHYTIUM || COMPILE_TEST + depends on ACPI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for Phytium DDR Memory Unit. + It adjusts frequency for dmu based on load bandwidth obtained from register. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index bf40d04928d03d5dbd0c0267ef2269332c496535..c57455f9b481889015749eca2cc315f26c33ed81 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -12,6 +12,8 @@ obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o obj-$(CONFIG_ARM_IMX_BUS_DEVFREQ) += imx-bus.o obj-$(CONFIG_ARM_IMX8M_DDRC_DEVFREQ) += imx8m-ddrc.o obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ) += mtk-cci-devfreq.o +obj-$(CONFIG_ARM_PHYTIUM_DMU_DEVFREQ) += phytium_dmu.o +obj-$(CONFIG_ARM_PHYTIUM_NOC_DEVFREQ) += phytium_noc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ) += sun8i-a33-mbus.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o diff --git a/drivers/devfreq/phytium_dmu.c b/drivers/devfreq/phytium_dmu.c new file mode 100644 index 0000000000000000000000000000000000000000..1fc23b486962b3e6c1fe58891958fdc94881c118 --- /dev/null +++ b/drivers/devfreq/phytium_dmu.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + *phytium_dmu.c - Phytium Processor dmu Frequency Driver + * + *Copyright (C) 2024,Phytium Technology Co.,Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG + +#define DEVICE_TYPE 9 //DMU ID + +#define UPDATE_INTERVAL_MS 10 + +#define DMUFREQ_DRIVER_VERSION "1.0.0" + +struct phytium_dmufreq { + struct device *dev; + + struct devfreq *devfreq; + struct devfreq_dev_profile profile; + struct devfreq_simple_ondemand_data ondemand_data; + + unsigned long rate, target_rate; + unsigned long bandwidth; + + struct timer_list sampling; + struct work_struct work; + + unsigned int freq_count; + unsigned long freq_table[]; +}; + +static ktime_t stop; + +static int phytium_dmu_set_freq(struct device *dev, unsigned long freq) +{ + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object args[4]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + unsigned long long ret; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = freq; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + args[3].type = ACPI_TYPE_INTEGER; + args[3].integer.value = 0; + + status = acpi_evaluate_integer(handle, "PSCF", &arg_list, &ret); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PSCF method\n"); + return -EIO; + } + + return 0; +} + +static int phytium_dmu_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + unsigned long old_freq = priv->rate; + unsigned long target_rate; + struct dev_pm_opp *opp; + int ret; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + target_rate = dev_pm_opp_get_freq(opp); + + dev_pm_opp_put(opp); + + if (target_rate == old_freq) + return 0; + + dev_dbg(dev, "target_rate = %lu\n", target_rate); + /* + * Read back the clk rate to verify switch was correct and so that + * we can report it on all error paths. + */ + ret = phytium_dmu_set_freq(dev, target_rate); + if (ret) { + dev_warn(dev, "failed to set DRAM frequency: %lu\n", target_rate); + return ret; + } + priv->rate = target_rate; + + return ret; + +} + +static int phytium_dmu_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + + *freq = priv->rate; + + return 0; +} + +static int phytium_read_perf_counter(struct device *dev) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *package, *elements; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + + status = acpi_evaluate_object(handle, "PDMU", NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PDMU method\n"); + return -EIO; + } + + package = buffer.pointer; + + elements = package->package.elements; + + return elements[0].integer.value + elements[1].integer.value; +} + +static void sampling_timer_callback(struct timer_list *t) +{ + 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); + struct device *dev = priv->dev; + static unsigned long load_counter; + static int count; + unsigned long current_load; + + current_load = phytium_read_perf_counter(dev); + + load_counter += current_load; + count += 1; + + if (ktime_after(ktime_get(), stop)) { + priv->bandwidth = (load_counter / count) / 2; + 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); + + stat->busy_time = priv->bandwidth; + stat->total_time = (75000000 * priv->rate) / priv->freq_table[0]; + dev_dbg(dev, "busy_time = %lu, total_time = %lu\n", + stat->busy_time, stat->total_time); + + stat->current_frequency = priv->rate; + + return 0; +} + +static int phytium_dmu_get_freq_info(struct device *dev) +{ + struct phytium_dmufreq *priv = dev_get_drvdata(dev); + + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + int i; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + priv->freq_count = element->integer.value; + + for (i = 0; i < priv->freq_count; i++) { + element = &package->package.elements[i+2]; + priv->freq_table[i] = element->integer.value; + dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); + } + + return 0; + +} + +static int get_freq_count(struct device *dev) +{ + int freq_count = -1; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method, status = %d\n", status); + return -EIO; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + freq_count = element->integer.value; + dev_dbg(dev, "freq_count = %d\n", freq_count); + + return freq_count; +} + +static int phytium_dmufreq_probe(struct platform_device *pdev) +{ + struct phytium_dmufreq *priv; + struct device *dev = &pdev->dev; + const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + int i, ret; + unsigned int max_state = get_freq_count(dev); + + if (max_state <= 0) + return max_state; + + dev->init_name = "dmufreq"; + + priv = kzalloc(sizeof(struct phytium_dmufreq) + + max_state * sizeof(unsigned long), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + ret = phytium_dmu_get_freq_info(dev); + if (ret) { + dev_err(dev, "failed to get ddr frequency info\n"); + return -EIO; + } + + priv->profile.initial_freq = priv->freq_table[0]; + priv->profile.polling_ms = 100; + priv->profile.timer = DEVFREQ_TIMER_DELAYED; + priv->profile.target = phytium_dmu_target; + priv->profile.get_cur_freq = phytium_dmu_get_cur_freq; + priv->profile.get_dev_status = phytium_dmu_get_dev_status; + priv->profile.freq_table = priv->freq_table; + priv->rate = priv->profile.initial_freq; + priv->profile.max_state = priv->freq_count; + priv->ondemand_data.upthreshold = 80; + priv->ondemand_data.downdifferential = 10; + + for (i = 0; i < max_state; ++i) { + ret = dev_pm_opp_add(dev, priv->freq_table[i], 0); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + goto err; + } + } + + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, + gov, &priv->ondemand_data); + if (IS_ERR(priv->devfreq)) { + ret = PTR_ERR(priv->devfreq); + dev_err(dev, "failed to add devfreq device: %d\n", ret); + goto err; + } + + 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; +} + +static int phytium_dmufreq_remove(struct platform_device *pdev) +{ + struct phytium_dmufreq *priv = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + 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; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_dmufreq_acpi_ids[] = { + {"PHYT0063"}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, phytium_dmufreq_acpi_ids); +#else +#define phytium_dmu_acpi_ids NULL +#endif + +static struct platform_driver phytium_dmufreq_driver = { + .probe = phytium_dmufreq_probe, + .remove = phytium_dmufreq_remove, + .driver = { + .name = "phytium_dmufreq", + .acpi_match_table = ACPI_PTR(phytium_dmufreq_acpi_ids), + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(phytium_dmufreq_driver); + +MODULE_DESCRIPTION("Phytium DDR Memory Unit frequency driver"); +MODULE_AUTHOR("Li Jiayi "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DMUFREQ_DRIVER_VERSION); diff --git a/drivers/devfreq/phytium_noc.c b/drivers/devfreq/phytium_noc.c new file mode 100644 index 0000000000000000000000000000000000000000..9658d9506197f158a8db8a5bd0c3d5384b6fe652 --- /dev/null +++ b/drivers/devfreq/phytium_noc.c @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + *phytium_noc.c - Phytium Processor noc Frequency Driver + * + *Copyright (C) 2024,Phytium Technology Co.,Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MINI_SIZE 0x400 +#define CNT_ENABLE 0x000 +#define WORK_STATE 0X004 +#define CLR_EN 0X010 +#define SNAPSHOT_EN 0X014 +#define INT_CTRL_CLR 0x024 +#define WR_NOLAST_HANDSHARK_NUM 0x44 + +#define DEBUG +#define DEVICE_TYPE 7 + +#define NOCFREQ_DRIVER_VERSION "1.0.0" + +struct phytium_nocfreq { + struct device *dev; + + struct devfreq *devfreq; + struct devfreq_dev_profile profile; + struct devfreq_simple_ondemand_data ondemand_data; + + void __iomem *reg_noc; + struct mutex lock; + + unsigned long rate, target_rate; + unsigned int freq_count; + unsigned long freq_table[]; +}; + +static u32 phytium_nocfreq_get_peak_bw(struct phytium_nocfreq *priv) +{ + /*Returns the peak number of dmu read/write commands on the axi bus.*/ + unsigned long peak_bw, bw_0, bw_1, bw_2, bw_3; + + bw_0 = readl_relaxed(priv->reg_noc + WR_NOLAST_HANDSHARK_NUM); + bw_1 = readl_relaxed(priv->reg_noc + MINI_SIZE*1 + WR_NOLAST_HANDSHARK_NUM); + bw_2 = readl_relaxed(priv->reg_noc + MINI_SIZE*2 + WR_NOLAST_HANDSHARK_NUM); + bw_3 = readl_relaxed(priv->reg_noc + MINI_SIZE*3 + WR_NOLAST_HANDSHARK_NUM); + + peak_bw = bw_0; + if (bw_1 > peak_bw) + peak_bw = bw_1; + if (bw_2 > peak_bw) + peak_bw = bw_2; + if (bw_3 > peak_bw) + peak_bw = bw_3; + + return peak_bw; +} + +static void phytium_nocfreq_restart_handshark_counters(struct phytium_nocfreq *priv) +{ + + /*clear interrupt*/ + + writel_relaxed(0x80000000, priv->reg_noc + INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*1 + INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*2 + INT_CTRL_CLR); + writel_relaxed(0x80000000, priv->reg_noc + MINI_SIZE*3 + INT_CTRL_CLR); + + /*clear counters*/ + writel_relaxed(0x1, priv->reg_noc + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + CLR_EN); +} + + +static int phytium_noc_set_freq(struct device *dev, unsigned long freq) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object args[4]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + unsigned long long ret; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = freq; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + args[3].type = ACPI_TYPE_INTEGER; + args[3].integer.value = 0; + + mutex_lock(&priv->lock); + status = acpi_evaluate_integer(handle, "PSCF", &arg_list, &ret); + mutex_unlock(&priv->lock); + + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PSCF method\n"); + return -EIO; + } + + if (ret) { + dev_err(dev, "Failed to set the freq to %lu\n", freq); + return -EIO; + } + dev_dbg(dev, "set target_freq = %lu khz\n", freq); + return 0; +} + +static int phytium_noc_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + unsigned long old_freq = priv->rate; + unsigned long target_rate; + struct dev_pm_opp *opp; + int ret; + + opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + target_rate = dev_pm_opp_get_freq(opp); + dev_pm_opp_put(opp); + + if (target_rate == old_freq) + return 0; + /* + * Read back the clk rate to verify switch was correct and so that + * we can report it on all error paths. + */ + ret = phytium_noc_set_freq(dev, target_rate); + + if (ret) { + dev_warn(dev, "failed to set noc frequency: %d\n", ret); + *freq = old_freq; + } + priv->rate = target_rate; + return ret; + +} + +static int phytium_noc_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object args[3]; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + unsigned long long ret; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + mutex_lock(&priv->lock); + status = acpi_evaluate_integer(handle, "PGCF", &arg_list, &ret); + mutex_unlock(&priv->lock); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCF method\n"); + return -EIO; + } + + if (ret < 0) { + dev_err(dev, "Failed to get the freq\n"); + return -EIO; + } + *freq = ret; + + return 0; +} + +static int phytium_noc_get_freq_info(struct device *dev, u32 flags) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + int i; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = flags; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + if (!buffer.length) { + dev_err(dev, "buffer is NULL\n"); + return -EINVAL; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + priv->freq_count = element->integer.value; + + for (i = 0; i < priv->freq_count; i++) { + element = &package->package.elements[i+2]; + priv->freq_table[i] = element->integer.value; + dev_dbg(dev, "freq_table[%d] = %llu\n", i, element->integer.value); + } + + return 0; + +} + +static int get_freq_count(struct device *dev) +{ + int freq_count = -1; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object args[3], *package, *element; + struct acpi_object_list arg_list = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + + args[0].type = ACPI_TYPE_INTEGER; + args[0].integer.value = DEVICE_TYPE; + args[1].type = ACPI_TYPE_INTEGER; + args[1].integer.value = 0; + args[2].type = ACPI_TYPE_INTEGER; + args[2].integer.value = 0; + + status = acpi_evaluate_object(handle, "PGCL", &arg_list, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "No PGCL method\n"); + return -EIO; + } + if (!buffer.length) { + dev_err(dev, "buffer is NULL\n"); + return -EINVAL; + } + + package = buffer.pointer; + + element = &package->package.elements[1]; + freq_count = element->integer.value; + dev_dbg(dev, "freq_count = %d\n", freq_count); + + return freq_count; +} + +static int phytium_noc_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct phytium_nocfreq *priv = dev_get_drvdata(dev); + unsigned int val; + + writel_relaxed(0x1, priv->reg_noc + SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + SNAPSHOT_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + SNAPSHOT_EN); + + val = DIV_ROUND_CLOSEST(priv->rate * 100, priv->profile.initial_freq); + stat->busy_time = phytium_nocfreq_get_peak_bw(priv); + stat->total_time = 320000 * val; + stat->current_frequency = priv->rate; + + phytium_nocfreq_restart_handshark_counters(priv); + dev_dbg(dev, "Using %lu/%lu (%lu%%) at %lu KHz\n", + stat->busy_time, stat->total_time, + DIV_ROUND_CLOSEST(stat->busy_time * 100, stat->total_time), + stat->current_frequency); + + return 0; +} + + +static int phytium_nocfreq_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phytium_nocfreq *priv; + const char *gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + int i, ret; + unsigned int max_state; + struct resource *mem; + + max_state = get_freq_count(dev); + dev->init_name = "nocfreq"; + + priv = devm_kzalloc(dev, struct_size(priv, freq_table, max_state), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + platform_set_drvdata(pdev, priv); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource"); + return -EINVAL; + } + + priv->reg_noc = devm_ioremap_resource(&pdev->dev, mem); + if (!priv->reg_noc) { + dev_err(dev, "NOC region map failed\n"); + return PTR_ERR(priv->reg_noc); + } + + ret = phytium_noc_get_freq_info(dev, DEVICE_TYPE); + if (ret) { + dev_err(dev, "failed to get noc frequency info\n"); + return -EIO; + } + + priv->profile.initial_freq = priv->freq_table[0]; + priv->profile.polling_ms = 100; + priv->profile.target = phytium_noc_target; + priv->profile.get_cur_freq = phytium_noc_get_cur_freq; + priv->profile.get_dev_status = phytium_noc_get_dev_status; + priv->profile.freq_table = priv->freq_table; + priv->profile.max_state = priv->freq_count; + priv->rate = priv->freq_table[0]; + priv->ondemand_data.upthreshold = 80; + priv->ondemand_data.downdifferential = 10; + priv->profile.max_state = priv->freq_count; + + for (i = 0; i < max_state; ++i) { + ret = dev_pm_opp_add(dev, priv->freq_table[i], 0); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + goto err; + } + } + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, + gov, &priv->ondemand_data); + if (IS_ERR(priv->devfreq)) { + ret = PTR_ERR(priv->devfreq); + dev_err(dev, "failed to add devfreq device: %d\n", ret); + goto err; + } + + ret = phytium_noc_set_freq(dev, priv->profile.initial_freq); + if (ret) + dev_warn(dev, "failed to init noc frequency: %d\n", ret); + + 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); + return 0; + +err: + dev_pm_opp_of_remove_table(dev); + kfree(priv); + return ret; +} + +static int phytium_nocfreq_remove(struct platform_device *pdev) +{ + struct phytium_nocfreq *priv = platform_get_drvdata(pdev); + unsigned long initial_freq = priv->profile.initial_freq; + struct device *dev = &pdev->dev; + int ret; + + 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); + + writel_relaxed(0x1, priv->reg_noc + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*1 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*2 + CLR_EN); + writel_relaxed(0x1, priv->reg_noc + MINI_SIZE*3 + CLR_EN); + + ret = phytium_noc_set_freq(dev, initial_freq); + 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; +} + +static const struct acpi_device_id phytium_noc_acpi_ids[] = { + {"PHYT0047"}, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, phytium_noc_acpi_ids); + +static struct platform_driver phytium_nocfreq_driver = { + .probe = phytium_nocfreq_probe, + .remove = phytium_nocfreq_remove, + .driver = { + .name = "phytium_nocfreq", + .acpi_match_table = ACPI_PTR(phytium_noc_acpi_ids), + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(phytium_nocfreq_driver); + +MODULE_DESCRIPTION("Phytium NOC Controller frequency driver"); +MODULE_AUTHOR("Li Jiayi "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(NOCFREQ_DRIVER_VERSION); diff --git a/drivers/dma/phytium/phytium-ddmac.c b/drivers/dma/phytium/phytium-ddmac.c index 768236d9947ab6efdce0cf653d9d233283862abc..32de9cccc96b2e1909d7940e1ff8c1305759ee56 100644 --- a/drivers/dma/phytium/phytium-ddmac.c +++ b/drivers/dma/phytium/phytium-ddmac.c @@ -29,6 +29,7 @@ #include #include "phytium-ddmac.h" +#define DDMA_DRIVER_VERSION "1.1.1" static inline struct phytium_ddma_device *to_ddma_device(struct dma_chan *chan) { return container_of(chan->device, struct phytium_ddma_device, dma_dev); @@ -955,3 +956,4 @@ module_exit(phytium_ddma_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium DDMA Controller platform driver"); MODULE_AUTHOR("Huang Jie "); +MODULE_VERSION(DDMA_DRIVER_VERSION); diff --git a/drivers/edac/phytium_edac.c b/drivers/edac/phytium_edac.c index af6a729816f4afb8212c85f7ee3db1ae0e78c18d..49230b89c01ff6003ae94d0dcfb386bdf86ec8f6 100644 --- a/drivers/edac/phytium_edac.c +++ b/drivers/edac/phytium_edac.c @@ -38,6 +38,8 @@ #define MAX_ERR_GROUP 3 +#define EDAC_DRIVER_VERSION "1.1.1" + struct phytium_edac { struct device *dev; void __iomem **ras_base; @@ -479,3 +481,4 @@ module_platform_driver(phytium_edac_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Huangjie "); +MODULE_VERSION(EDAC_DRIVER_VERSION); diff --git a/drivers/gpio/gpio-phytium-core.c b/drivers/gpio/gpio-phytium-core.c index 8ea7cafc4c479ae9f72005bbe738e8f03ad8f172..1bda08c8567f4c798927202be3bd14f09442197b 100644 --- a/drivers/gpio/gpio-phytium-core.c +++ b/drivers/gpio/gpio-phytium-core.c @@ -129,14 +129,13 @@ int phytium_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, return -EINVAL; ddr = gpio->regs + GPIO_SWPORTA_DDR + (loc.port * GPIO_PORT_STRIDE); + phytium_gpio_set(gc, offset, value); raw_spin_lock_irqsave(&gpio->lock, flags); writel(readl(ddr) | BIT(loc.offset), ddr); raw_spin_unlock_irqrestore(&gpio->lock, flags); - phytium_gpio_set(gc, offset, value); - return 0; } EXPORT_SYMBOL_GPL(phytium_gpio_direction_output); @@ -272,6 +271,8 @@ void phytium_gpio_irq_enable(struct irq_data *d) unsigned long flags; u32 val; + if (gpio->is_resuming) + return; /* Only port A can provide interrupt source */ if (irqd_to_hwirq(d) >= gpio->ngpio[0]) return; @@ -326,19 +327,36 @@ void phytium_gpio_irq_handler(struct irq_desc *desc) struct irq_chip *irqchip = irq_desc_get_chip(desc); unsigned long pending; int offset; + int index = -1; + unsigned int index_found = 0; chained_irq_enter(irqchip, desc); pending = readl(gpio->regs + GPIO_INTSTATUS); + + if (gc->irq.num_parents > 1) { + for (index = 0 ; index < gc->irq.num_parents; index++) { + if (gc->irq.parents[index] == desc->irq_data.irq) { + index_found = 1; + break; + } + } + if (index_found == 0) { + pr_err("Can't find index for this gpio interrupt.\n"); + index = -1; + } + } + if (pending) { for_each_set_bit(offset, &pending, gpio->ngpio[0]) { - int gpio_irq = irq_find_mapping(gc->irq.domain, - offset); - generic_handle_irq(gpio_irq); - - if ((irq_get_trigger_type(gpio_irq) & - IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) - phytium_gpio_toggle_trigger(gpio, offset); + if (index == -1 || offset == index) { + int gpio_irq = irq_find_mapping(gc->irq.domain, + offset); + generic_handle_irq(gpio_irq); + if ((irq_get_trigger_type(gpio_irq) & + IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) + phytium_gpio_toggle_trigger(gpio, offset); + } } } diff --git a/drivers/gpio/gpio-phytium-core.h b/drivers/gpio/gpio-phytium-core.h index 3d8c6e7b355cec690754abd14d0a5bc4bfd2de79..381fc841de70a1213984f89553df552dbc293bae 100644 --- a/drivers/gpio/gpio-phytium-core.h +++ b/drivers/gpio/gpio-phytium-core.h @@ -63,6 +63,7 @@ struct phytium_gpio { struct gpio_chip gc; unsigned int ngpio[2]; int irq[32]; + int is_resuming; #ifdef CONFIG_PM_SLEEP struct phytium_gpio_ctx ctx; #endif diff --git a/drivers/gpio/gpio-phytium-pci.c b/drivers/gpio/gpio-phytium-pci.c index e194e3582f2fa961a892b2197b92aa1ec02c2c0b..3e4c1edd97cb04d620b317ccb7bcdba735d8f325 100644 --- a/drivers/gpio/gpio-phytium-pci.c +++ b/drivers/gpio/gpio-phytium-pci.c @@ -85,6 +85,7 @@ 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, @@ -132,11 +133,13 @@ static int phytium_gpio_pci_suspend(struct device *dev) gpio->ctx.ext_portb = readl(gpio->regs + GPIO_EXT_PORTB); gpio->ctx.inten = readl(gpio->regs + GPIO_INTEN); + gpio->is_resuming = 1; gpio->ctx.intmask = readl(gpio->regs + GPIO_INTMASK); gpio->ctx.inttype_level = readl(gpio->regs + GPIO_INTTYPE_LEVEL); gpio->ctx.int_polarity = readl(gpio->regs + GPIO_INT_POLARITY); gpio->ctx.debounce = readl(gpio->regs + GPIO_DEBOUNCE); + writel(0, gpio->regs + GPIO_INTEN); raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; @@ -157,7 +160,6 @@ static int phytium_gpio_pci_resume(struct device *dev) writel(gpio->ctx.swportb_ddr, gpio->regs + GPIO_SWPORTB_DDR); writel(gpio->ctx.ext_portb, gpio->regs + GPIO_EXT_PORTB); - writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); writel(gpio->ctx.intmask, gpio->regs + GPIO_INTMASK); writel(gpio->ctx.inttype_level, gpio->regs + GPIO_INTTYPE_LEVEL); writel(gpio->ctx.int_polarity, gpio->regs + GPIO_INT_POLARITY); @@ -165,6 +167,9 @@ static int phytium_gpio_pci_resume(struct device *dev) writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); + gpio->is_resuming = 0; + raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; diff --git a/drivers/gpio/gpio-phytium-platform.c b/drivers/gpio/gpio-phytium-platform.c index 2f808b89e4ed57a670cc9e9ec6f75fb465193506..4774bb1f239ef7dc5334bbe48b8494fe6065628b 100644 --- a/drivers/gpio/gpio-phytium-platform.c +++ b/drivers/gpio/gpio-phytium-platform.c @@ -105,6 +105,7 @@ 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; @@ -149,11 +150,13 @@ static int phytium_gpio_suspend(struct device *dev) gpio->ctx.ext_portb = readl(gpio->regs + GPIO_EXT_PORTB); gpio->ctx.inten = readl(gpio->regs + GPIO_INTEN); + gpio->is_resuming = 1; gpio->ctx.intmask = readl(gpio->regs + GPIO_INTMASK); gpio->ctx.inttype_level = readl(gpio->regs + GPIO_INTTYPE_LEVEL); gpio->ctx.int_polarity = readl(gpio->regs + GPIO_INT_POLARITY); gpio->ctx.debounce = readl(gpio->regs + GPIO_DEBOUNCE); + writel(0, gpio->regs + GPIO_INTEN); raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; @@ -174,7 +177,6 @@ static int phytium_gpio_resume(struct device *dev) writel(gpio->ctx.swportb_ddr, gpio->regs + GPIO_SWPORTB_DDR); writel(gpio->ctx.ext_portb, gpio->regs + GPIO_EXT_PORTB); - writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); writel(gpio->ctx.intmask, gpio->regs + GPIO_INTMASK); writel(gpio->ctx.inttype_level, gpio->regs + GPIO_INTTYPE_LEVEL); writel(gpio->ctx.int_polarity, gpio->regs + GPIO_INT_POLARITY); @@ -182,6 +184,9 @@ static int phytium_gpio_resume(struct device *dev) writel(0xffffffff, gpio->regs + GPIO_PORTA_EOI); + writel(gpio->ctx.inten, gpio->regs + GPIO_INTEN); + gpio->is_resuming = 0; + raw_spin_unlock_irqrestore(&gpio->lock, flags); return 0; diff --git a/drivers/gpu/drm/phytium/Makefile b/drivers/gpu/drm/phytium/Makefile index 2dc7ea1118cd4cbf141215a7f377e258ae2593ba..2e730a02f1666b4b98936af4b64e3f19d44989ef 100644 --- a/drivers/gpu/drm/phytium/Makefile +++ b/drivers/gpu/drm/phytium/Makefile @@ -16,3 +16,8 @@ phytium-dc-drm-y := phytium_display_drv.o \ obj-$(CONFIG_DRM_PHYTIUM) += phytium-dc-drm.o CFLAGS_REMOVE_phytium_crtc.o += -mgeneral-regs-only +ifeq ($(ARCH), x86) + FPU_CFLAGS += -mhard-float + FPU_CFLAGS += -msse -msse2 + CFLAGS_phytium_crtc.o += $(FPU_CFLAGS) +endif \ No newline at end of file diff --git a/drivers/gpu/drm/phytium/pe220x_dc.c b/drivers/gpu/drm/phytium/pe220x_dc.c index b465dbb615f16fe947564316141186cc0fe4e100..cfdecad43561b3943a04ce3d782e211f1099f449 100644 --- a/drivers/gpu/drm/phytium/pe220x_dc.c +++ b/drivers/gpu/drm/phytium/pe220x_dc.c @@ -7,7 +7,9 @@ #include #include +#if defined(__arm__) || defined(__aarch64__) #include +#endif #include #include "phytium_display_drv.h" #include "pe220x_reg.h" @@ -56,6 +58,10 @@ static const unsigned int pe220x_primary_formats[] = { DRM_FORMAT_NV21, }; +static const unsigned int pe220x_bmc_primary_formats[] = { + DRM_FORMAT_XRGB8888, +}; + static uint64_t pe220x_primary_formats_modifiers[] = { DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID @@ -109,49 +115,76 @@ void pe220x_dc_hw_reset(struct drm_crtc *crtc) int config = 0; int phys_pipe = phytium_crtc->phys_pipe; - /* disable pixel clock for bmc mode */ - if (phys_pipe == 0) - pe220x_dc_hw_disable(crtc); - config = phytium_readl_reg(priv, 0, PE220X_DC_CLOCK_CONTROL); - config &= (~(DC0_CORE_RESET | DC1_CORE_RESET | AXI_RESET | AHB_RESET)); - if (phys_pipe == 0) { - phytium_writel_reg(priv, config | DC0_CORE_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET | AHB_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config | DC0_CORE_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); + if (priv->info.bmc_mode) { + pe220x_dc_hw_disable(crtc); + config &= (~(DC0_CORE_RESET | DC1_CORE_RESET | AHB_RESET)); + if (phys_pipe == 0) { + phytium_writel_reg(priv, config | DC0_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET | AHB_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + } else { + phytium_writel_reg(priv, config | DC1_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET | AHB_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + } + } else { - phytium_writel_reg(priv, config | DC1_CORE_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET | AHB_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config | DC1_CORE_RESET, - 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); - phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL); - udelay(20); + config &= (~(DC0_CORE_RESET | DC1_CORE_RESET | AXI_RESET | AHB_RESET)); + if (phys_pipe == 0) { + phytium_writel_reg(priv, config | DC0_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET | AHB_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET | AXI_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC0_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + } else { + phytium_writel_reg(priv, config | DC1_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET | AHB_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET | AXI_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config | DC1_CORE_RESET, + 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + phytium_writel_reg(priv, config, 0, PE220X_DC_CLOCK_CONTROL); + udelay(20); + } } } @@ -216,6 +249,15 @@ void pe220x_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers, *format_count = ARRAY_SIZE(pe220x_primary_formats); } +void pe220x_dc_bmc_hw_plane_get_primary_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count) +{ + *format_modifiers = pe220x_primary_formats_modifiers; + *formats = pe220x_bmc_primary_formats; + *format_count = ARRAY_SIZE(pe220x_bmc_primary_formats); +} + void pe220x_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers, const uint32_t **formats, uint32_t *format_count) diff --git a/drivers/gpu/drm/phytium/pe220x_dc.h b/drivers/gpu/drm/phytium/pe220x_dc.h index 5840795cbae61fa97140e5167d2bda960a5fdc65..8902d11348502db9d72f35731424105a7255396b 100644 --- a/drivers/gpu/drm/phytium/pe220x_dc.h +++ b/drivers/gpu/drm/phytium/pe220x_dc.h @@ -9,8 +9,8 @@ #define __PE220X_DC_H__ #define PE220X_DC_PIX_CLOCK_MAX (594000) -#define PE220X_DC_HDISPLAY_MAX 3840 -#define PE220X_DC_VDISPLAY_MAX 2160 +#define PE220X_DC_HDISPLAY_MAX 1920 +#define PE220X_DC_VDISPLAY_MAX 1080 #define PE220X_DC_ADDRESS_MASK 0x7f extern void pe220x_dc_hw_vram_init(struct phytium_display_private *priv, @@ -22,6 +22,9 @@ extern int pe220x_dc_hw_fb_format_check(const struct drm_mode_fb_cmd2 *mode_cmd, extern void pe220x_dc_hw_plane_get_primary_format(const uint64_t **format_modifiers, const uint32_t **formats, uint32_t *format_count); +extern void pe220x_dc_bmc_hw_plane_get_primary_format(const uint64_t **format_modifiers, + const uint32_t **formats, + uint32_t *format_count); extern void pe220x_dc_hw_plane_get_cursor_format(const uint64_t **format_modifiers, const uint32_t **formats, uint32_t *format_count); diff --git a/drivers/gpu/drm/phytium/pe220x_dp.c b/drivers/gpu/drm/phytium/pe220x_dp.c index c41446f06551cff05ead7891daabc08ff0658898..528dd429eaf5c173eab8eb650d9c1d1ddc08da4d 100644 --- a/drivers/gpu/drm/phytium/pe220x_dp.c +++ b/drivers/gpu/drm/phytium/pe220x_dp.c @@ -4,7 +4,6 @@ * * Copyright (c) 2021-2024 Phytium Technology Co., Ltd. */ - #include #include #include diff --git a/drivers/gpu/drm/phytium/phytium_crtc.c b/drivers/gpu/drm/phytium/phytium_crtc.c index 8adc1c6e10dc839a4fca999ea1b68492cd9a9295..b0c1f49806815654bb2e9ca3f540ada07e252781 100644 --- a/drivers/gpu/drm/phytium/phytium_crtc.c +++ b/drivers/gpu/drm/phytium/phytium_crtc.c @@ -6,7 +6,11 @@ #include #include +#if defined(__arm__) || defined(__aarch64__) #include +#elif defined(__x86_64) +#include +#endif #include #include "phytium_display_drv.h" #include "phytium_crtc.h" @@ -276,14 +280,22 @@ static void phytium_dc_scaling_config(struct drm_crtc *crtc, memset(&kernel_info_width, 0, sizeof(struct filter_blit_array)); kernel_info_width.kernelStates = tmp; memset(kernel_info_width.kernelStates, 0, KERNELSTATES); +#if defined(__arm__) || defined(__aarch64__) kernel_neon_begin(); +#elif defined(__x86_64__) + kernel_fpu_begin(); +#endif dc_calculate_sync_table(FRAMEBUFFER_HORIZONTAL_FILTER_TAP, phytium_crtc->src_width, phytium_crtc->dst_width, &kernel_info_width); memset(kernelStates, 0, sizeof(kernelStates)); memcpy(kernelStates, kernel_info_width.kernelStates + 1, KERNELSTATES - 4); +#if defined(__arm__) || defined(__aarch64__) kernel_neon_end(); +#elif defined(__x86_64__) + kernel_fpu_end(); +#endif phytium_writel_reg(priv, HORI_FILTER_INDEX, group_offset, PHYTIUM_DC_FRAMEBUFFER_HORI_FILTER_INDEX); for (i = 0; i < 128; i++) { @@ -294,12 +306,20 @@ static void phytium_dc_scaling_config(struct drm_crtc *crtc, memset(&kernel_info_width, 0, sizeof(struct filter_blit_array)); kernel_info_width.kernelStates = tmp; memset(kernel_info_width.kernelStates, 0, KERNELSTATES); +#if defined(__arm__) || defined(__aarch64__) kernel_neon_begin(); +#elif defined(__x86_64__) + kernel_fpu_begin(); +#endif dc_calculate_sync_table(FRAMEBUFFER_FILTER_TAP, phytium_crtc->src_height, phytium_crtc->dst_height, &kernel_info_width); memset(kernelStates, 0, sizeof(kernelStates)); memcpy(kernelStates, kernel_info_width.kernelStates + 1, KERNELSTATES - 4); +#if defined(__arm__) || defined(__aarch64__) kernel_neon_end(); +#elif defined(__x86_64__) + kernel_fpu_end(); +#endif phytium_writel_reg(priv, VERT_FILTER_INDEX, group_offset, PHYTIUM_DC_FRAMEBUFFER_VERT_FILTER_INDEX); for (i = 0; i < 128; i++) @@ -571,7 +591,8 @@ phytium_crtc_atomic_enable(struct drm_crtc *crtc, else config &= (~FRAMEBUFFER_SCALE_ENABLE); - config |= FRAMEBUFFER_GAMMA_ENABLE; + if (!priv->info.bmc_mode) + config |= FRAMEBUFFER_GAMMA_ENABLE; if (crtc->state->gamma_lut) phytium_crtc_gamma_set(crtc); @@ -748,6 +769,7 @@ int phytium_crtc_init(struct drm_device *dev, int phys_pipe) struct phytium_crtc_state *phytium_crtc_state; struct phytium_plane *phytium_primary_plane = NULL; struct phytium_plane *phytium_cursor_plane = NULL; + struct drm_plane *cursor_base = NULL; struct phytium_display_private *priv = dev->dev_private; int ret; @@ -790,16 +812,21 @@ int phytium_crtc_init(struct drm_device *dev, int phys_pipe) goto failed_create_primary; } - phytium_cursor_plane = phytium_cursor_plane_create(dev, phys_pipe); - if (IS_ERR(phytium_cursor_plane)) { - ret = PTR_ERR(phytium_cursor_plane); - DRM_ERROR("create cursor plane failed, phys_pipe(%d)\n", phys_pipe); - goto failed_create_cursor; + if (priv->info.bmc_mode) { + cursor_base = NULL; + } else { + phytium_cursor_plane = phytium_cursor_plane_create(dev, phys_pipe); + if (IS_ERR(phytium_cursor_plane)) { + ret = PTR_ERR(phytium_cursor_plane); + DRM_ERROR("create cursor plane failed, phys_pipe(%d)\n", phys_pipe); + goto failed_create_cursor; + } + cursor_base = &phytium_cursor_plane->base; } ret = drm_crtc_init_with_planes(dev, &phytium_crtc->base, &phytium_primary_plane->base, - &phytium_cursor_plane->base, + cursor_base, &phytium_crtc_funcs, "phys_pipe %d", phys_pipe); diff --git a/drivers/gpu/drm/phytium/phytium_display_drv.c b/drivers/gpu/drm/phytium/phytium_display_drv.c index c311c1c977ccf2edb8d9b811fc90adfd08393eb0..6807f91793013d334de0fff295ef5a4ad335c2ae 100644 --- a/drivers/gpu/drm/phytium/phytium_display_drv.c +++ b/drivers/gpu/drm/phytium/phytium_display_drv.c @@ -289,8 +289,11 @@ static void phytium_display_unload(struct drm_device *dev) * The device specific ioctl range is 0x40 to 0x79. */ #define DRM_PHYTIUM_VRAM_TYPE_DEVICE 0x0 +#define DRM_PHYTIUM_BMC_DEVICE 0x1 #define DRM_IOCTL_PHYTIUM_VRAM_TYPE_DEVICE DRM_IO(DRM_COMMAND_BASE\ + DRM_PHYTIUM_VRAM_TYPE_DEVICE) +#define DRM_IOCTL_PHYTIUM_IS_BMC_DEVICE DRM_IO(DRM_COMMAND_BASE\ + + DRM_PHYTIUM_BMC_DEVICE) static int phytium_ioctl_check_vram_device(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -300,10 +303,20 @@ static int phytium_ioctl_check_vram_device(struct drm_device *dev, void *data, return ((priv->support_memory_type == MEMORY_TYPE_VRAM_DEVICE) ? 1 : 0); } +static int phytium_ioctl_check_bmc_device(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct phytium_display_private *priv = dev->dev_private; + + return priv->info.bmc_mode ? 1 : 0; +} + static const struct drm_ioctl_desc phytium_ioctls[] = { /* for test, none so far */ DRM_IOCTL_DEF_DRV(PHYTIUM_VRAM_TYPE_DEVICE, phytium_ioctl_check_vram_device, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(PHYTIUM_IS_BMC_DEVICE, phytium_ioctl_check_bmc_device, + DRM_AUTH|DRM_UNLOCKED), }; static const struct file_operations phytium_drm_driver_fops = { @@ -447,5 +460,6 @@ module_init(phytium_display_init); module_exit(phytium_display_exit); MODULE_LICENSE("GPL"); +MODULE_VERSION(DC_DRIVER_VERSION); MODULE_AUTHOR("Yang Xun "); MODULE_DESCRIPTION("Phytium Display Controller"); diff --git a/drivers/gpu/drm/phytium/phytium_display_drv.h b/drivers/gpu/drm/phytium/phytium_display_drv.h index 587614f5b2efb7e160d5228216763d8a176e3ea8..accfd65fe27bbb7223d3a4a64bb00d2816c6f881 100644 --- a/drivers/gpu/drm/phytium/phytium_display_drv.h +++ b/drivers/gpu/drm/phytium/phytium_display_drv.h @@ -21,6 +21,7 @@ #define DRV_DATE "20201220" #define DRV_MAJOR 1 #define DRV_MINOR 1 +#define DC_DRIVER_VERSION "1.0.0" /* come from GPU */ #define DRM_FORMAT_MOD_VENDOR_PHYTIUM 0x92 @@ -66,6 +67,8 @@ struct phytium_device_info { unsigned char num_pipes; unsigned char total_pipes; unsigned char edp_mask; + bool bmc_mode; + unsigned char reserve[2]; unsigned int crtc_clock_max; unsigned int hdisplay_max; unsigned int vdisplay_max; diff --git a/drivers/gpu/drm/phytium/phytium_dp.c b/drivers/gpu/drm/phytium/phytium_dp.c index b9a07b74b8ad56ed3eb84ef51527e4d73d8383ba..78eac4fde18388504951f292ce28d9fdd316a3ad 100644 --- a/drivers/gpu/drm/phytium/phytium_dp.c +++ b/drivers/gpu/drm/phytium/phytium_dp.c @@ -2222,13 +2222,13 @@ phytium_encoder_mode_valid(struct drm_encoder *encoder, const struct drm_display case 8: break; default: - pr_info("not support bpc(%d)\n", display_info->bpc); + DRM_DEBUG_KMS("not support bpc(%d)\n", display_info->bpc); display_info->bpc = 8; break; } if ((display_info->color_formats & DRM_COLOR_FORMAT_RGB444) == 0) { - pr_info("not support color_format(%d)\n", display_info->color_formats); + 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_fbdev.c b/drivers/gpu/drm/phytium/phytium_fbdev.c index 8c424fc35e0c137afd254be391c7825bc4ea800b..da288131327b068dc33b062238dfee1ce888a299 100644 --- a/drivers/gpu/drm/phytium/phytium_fbdev.c +++ b/drivers/gpu/drm/phytium/phytium_fbdev.c @@ -16,14 +16,6 @@ #define PHYTIUM_MAX_CONNECTOR 1 #define helper_to_drm_private(x) container_of(x, struct phytium_display_private, fbdev_helper) -static void phytium_fbdev_destroy(struct fb_info *info) -{ - struct drm_fb_helper *helper = info->par; - struct phytium_display_private *priv = helper_to_drm_private(helper); - - phytium_gem_free_object(&priv->fbdev_phytium_gem->base); -} - static int phytium_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma) { struct drm_fb_helper *helper = info->par; @@ -35,7 +27,6 @@ static int phytium_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma) static const struct fb_ops phytium_fbdev_ops = { .owner = THIS_MODULE, .fb_mmap = phytium_fbdev_mmap, - .fb_destroy = phytium_fbdev_destroy, DRM_FB_HELPER_DEFAULT_OPS, __FB_DEFAULT_IOMEM_OPS_RDWR, __FB_DEFAULT_IOMEM_OPS_DRAW, diff --git a/drivers/gpu/drm/phytium/phytium_gem.c b/drivers/gpu/drm/phytium/phytium_gem.c index 7d962076f7d7f87282f3d4fd640220b310be4c31..8234b6b6e8cf128d5165a435cf7af06f0c851b0f 100644 --- a/drivers/gpu/drm/phytium/phytium_gem.c +++ b/drivers/gpu/drm/phytium/phytium_gem.c @@ -95,7 +95,7 @@ phytium_gem_prime_get_sg_table(struct drm_gem_object *obj) DRM_ERROR("failed to allocate sg\n"); goto sgt_free; } - page = phys_to_page(phytium_gem_obj->phys_addr); + page = pfn_to_page(__phys_to_pfn(phytium_gem_obj->phys_addr)); sg_set_page(sgt->sgl, page, PAGE_ALIGN(phytium_gem_obj->size), 0); } else if (phytium_gem_obj->memory_type == MEMORY_TYPE_SYSTEM_UNIFIED) { ret = dma_get_sgtable_attrs(dev->dev, sgt, phytium_gem_obj->vaddr, @@ -449,7 +449,7 @@ struct phytium_gem_object *phytium_gem_create_object(struct drm_device *dev, uns DRM_ERROR("fail to allocate carveout memory with size %lx\n", size); goto failed_dma_alloc; } - page = phys_to_page(phytium_gem_obj->phys_addr); + page = pfn_to_page(__phys_to_pfn(phytium_gem_obj->phys_addr)); phytium_gem_obj->iova = dma_map_page(dev->dev, page, 0, size, DMA_TO_DEVICE); if (dma_mapping_error(dev->dev, phytium_gem_obj->iova)) { DRM_ERROR("fail to dma map carveout memory with size %lx\n", size); diff --git a/drivers/gpu/drm/phytium/phytium_pci.c b/drivers/gpu/drm/phytium/phytium_pci.c index 80a1db064ee4bc7a7bf82c5b2f65c911c4943673..a8742a8ea5366a122043fc5124410a6c82cbed47 100644 --- a/drivers/gpu/drm/phytium/phytium_pci.c +++ b/drivers/gpu/drm/phytium/phytium_pci.c @@ -256,6 +256,12 @@ static int phytium_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e struct phytium_display_private *priv = NULL; struct drm_device *dev = NULL; int ret = 0; + struct phytium_device_info *phytium_info = (struct phytium_device_info *)ent->driver_data; + + if (phytium_info) { + if (phytium_info->platform_mask & BIT(PHYTIUM_PLATFORM_PE220X)) + phytium_display_drm_driver.name = "pe220x"; + } ret = phytium_remove_conflicting_framebuffers(pdev); if (ret) { @@ -403,6 +409,7 @@ static const struct phytium_device_info px210_info = { .address_mask = PX210_DC_ADDRESS_MASK, .backlight_max = PX210_DP_BACKLIGHT_MAX, .backlight_min = PX210_DP_BACKLIGHT_MIN, + .bmc_mode = false, }; static const struct phytium_device_info pe220x_info = { @@ -414,6 +421,7 @@ static const struct phytium_device_info pe220x_info = { .address_mask = PE220X_DC_ADDRESS_MASK, .backlight_max = PE220X_DP_BACKLIGHT_MAX, .backlight_min = PE220X_DP_BACKLIGHT_MIN, + .bmc_mode = true, }; static const struct pci_device_id phytium_display_pci_ids[] = { diff --git a/drivers/gpu/drm/phytium/phytium_plane.c b/drivers/gpu/drm/phytium/phytium_plane.c index bc28ad8d45c032dabf3ba2d811dbd0496db2e094..228c637b87da4a66e55aa4cac953f3717cb387fa 100644 --- a/drivers/gpu/drm/phytium/phytium_plane.c +++ b/drivers/gpu/drm/phytium/phytium_plane.c @@ -580,7 +580,12 @@ struct phytium_plane *phytium_primary_plane_create(struct drm_device *dev, int p phytium_plane->dc_hw_update_primary_hi_addr = px210_dc_hw_update_primary_hi_addr; phytium_plane->dc_hw_update_cursor_hi_addr = NULL; } else if (IS_PE220X(priv)) { - phytium_plane->dc_hw_plane_get_format = pe220x_dc_hw_plane_get_primary_format; + if (priv->info.bmc_mode) + phytium_plane->dc_hw_plane_get_format = + pe220x_dc_bmc_hw_plane_get_primary_format; + else + phytium_plane->dc_hw_plane_get_format = + pe220x_dc_hw_plane_get_primary_format; phytium_plane->dc_hw_update_dcreq = NULL; phytium_plane->dc_hw_update_primary_hi_addr = pe220x_dc_hw_update_primary_hi_addr; phytium_plane->dc_hw_update_cursor_hi_addr = NULL; diff --git a/drivers/gpu/drm/phytium/phytium_platform.c b/drivers/gpu/drm/phytium/phytium_platform.c index 32c4a3efeb02aebaa3f6b48571955a6a43d25cbf..69dedbb75a853a420d604a36382594ab83ebbf7d 100644 --- a/drivers/gpu/drm/phytium/phytium_platform.c +++ b/drivers/gpu/drm/phytium/phytium_platform.c @@ -136,14 +136,16 @@ phytium_platform_private_init(struct platform_device *pdev) goto failed; } priv->edp_bl_en = gpiod_get(&pdev->dev, "edp-bl-en", GPIOD_OUT_HIGH); - if (!priv->edp_bl_en) { + if (IS_ERR(priv->edp_bl_en)) { dev_err(&pdev->dev, "Failed to get edp_en gpio\n"); + priv->edp_bl_en = NULL; goto failed; } priv->edp_power_en = gpiod_get(&pdev->dev, "edp-power-en", GPIOD_OUT_HIGH); - if (!priv->edp_power_en) { + if (IS_ERR(priv->edp_power_en)) { dev_err(&pdev->dev, "Failed to get edp_pwr_en gpio\n"); - goto failed; + priv->edp_power_en = NULL; + goto failed_gpio_power_init; } // set GPIO pin output gpiod_direction_output(priv->edp_power_en, 0); @@ -176,14 +178,16 @@ phytium_platform_private_init(struct platform_device *pdev) goto failed; } priv->edp_bl_en = gpiod_get(&pdev->dev, "edp-bl-en", GPIOD_OUT_HIGH); - if (!priv->edp_bl_en) { + if (IS_ERR(priv->edp_bl_en)) { dev_err(&pdev->dev, "Failed to get edp_en gpio\n"); + priv->edp_bl_en = NULL; goto failed; } priv->edp_power_en = gpiod_get(&pdev->dev, "edp-power-en", GPIOD_OUT_HIGH); - if (!priv->edp_power_en) { + if (IS_ERR(priv->edp_power_en)) { dev_err(&pdev->dev, "Failed to get edp_pwr_en gpio\n"); - goto failed; + priv->edp_power_en = NULL; + goto failed_gpio_power_init; } // set GPIO pin output gpiod_direction_output(priv->edp_power_en, 0); @@ -219,6 +223,8 @@ phytium_platform_private_init(struct platform_device *pdev) return priv; +failed_gpio_power_init: + gpiod_put(priv->edp_bl_en); failed: devm_kfree(&pdev->dev, platform_priv); exit: @@ -231,6 +237,11 @@ static void phytium_platform_private_fini(struct platform_device *pdev) struct phytium_display_private *priv = dev->dev_private; struct phytium_platform_private *platform_priv = to_platform_priv(priv); + if (priv->edp_power_en) + gpiod_put(priv->edp_power_en); + if (priv->edp_bl_en) + gpiod_put(priv->edp_bl_en); + devm_kfree(&pdev->dev, platform_priv); } @@ -238,8 +249,23 @@ static int phytium_platform_probe(struct platform_device *pdev) { struct phytium_display_private *priv = NULL; struct drm_device *dev = NULL; + struct phytium_device_info *phytium_info = NULL; int ret = 0; + if (pdev->dev.of_node) { + phytium_info = (struct phytium_device_info *)of_device_get_match_data(&pdev->dev); + if (phytium_info) { + if (phytium_info->platform_mask & BIT(PHYTIUM_PLATFORM_PE220X)) + phytium_display_drm_driver.name = "pe220x"; + } + } else if (has_acpi_companion(&pdev->dev)) { + phytium_info = (struct phytium_device_info *)acpi_device_get_match_data(&pdev->dev); + if (phytium_info) { + if (phytium_info->platform_mask & BIT(PHYTIUM_PLATFORM_PE220X)) + phytium_display_drm_driver.name = "pe220x"; + } + } + dev = drm_dev_alloc(&phytium_display_drm_driver, &pdev->dev); if (IS_ERR(dev)) { DRM_ERROR("failed to allocate drm_device\n"); @@ -333,6 +359,7 @@ static const struct phytium_device_info pe220x_info = { .address_mask = PE220X_DC_ADDRESS_MASK, .backlight_max = PE220X_DP_BACKLIGHT_MAX, .backlight_min = PE220X_DP_BACKLIGHT_MIN, + .bmc_mode = false, }; static const struct of_device_id display_of_match[] = { diff --git a/drivers/gpu/drm/phytium/px210_dc.c b/drivers/gpu/drm/phytium/px210_dc.c index 84fd44a130f07910e324365c911744a203bcf6d5..6b416fc7737e3615d5ad19826f1f237e04acc468 100644 --- a/drivers/gpu/drm/phytium/px210_dc.c +++ b/drivers/gpu/drm/phytium/px210_dc.c @@ -6,7 +6,9 @@ #include #include +#if defined(__arm__) || defined(__aarch64__) #include +#endif #include #include "phytium_display_drv.h" #include "px210_reg.h" diff --git a/drivers/hwmon/tacho-phytium.c b/drivers/hwmon/tacho-phytium.c index 00a773014f9c205566d932981ce081e5ced8a37d..f61ed5ac6013e8d3f64533e4f64d14c390243a83 100644 --- a/drivers/hwmon/tacho-phytium.c +++ b/drivers/hwmon/tacho-phytium.c @@ -56,6 +56,9 @@ #define TIMER_START_VALUE_REG 0x38 #define TIMER_INT_CLR_MASK GENMASK(5, 0) +#define TIMER_TACHO_DEFAULT_FREQ 0x2FAF080 + +#define TACHO_DRIVER_VERSION "1.1.1" enum tacho_modes { tacho_mode = 1, @@ -236,41 +239,24 @@ static const struct attribute_group *capture_groups[] = { static int phytium_tacho_get_work_mode(struct phytium_tacho *tacho) { - struct device_node *nc = tacho->dev->of_node; struct fwnode_handle *fwn = tacho->dev->fwnode; - if (of_property_read_bool(nc, "tacho")) - return tacho_mode; - else if (has_acpi_companion(tacho->dev) && fwnode_property_read_bool( - fwn, "tacho")) + if (fwnode_property_read_bool(fwn, "tacho")) return tacho_mode; - if (of_property_read_bool(nc, "capture")) - return capture_mode; - else if (has_acpi_companion(tacho->dev) && fwnode_property_read_bool( - fwn, "capture")) + if (fwnode_property_read_bool(fwn, "capture")) return capture_mode; return tacho_mode; } static int phytium_tacho_get_edge_mode(struct phytium_tacho *tacho) { - struct device_node *nc = tacho->dev->of_node; struct fwnode_handle *fwn = tacho->dev->fwnode; - if (of_property_read_bool(nc, "up")) + if (fwnode_property_read_bool(fwn, "up")) return rising_edge; - else if (has_acpi_companion(tacho->dev) && fwnode_property_read_bool( - fwn, "up")) - return rising_edge; - if (of_property_read_bool(nc, "down")) - return falling_edge; - else if (has_acpi_companion(tacho->dev) && fwnode_property_read_bool( - fwn, "down")) + if (fwnode_property_read_bool(fwn, "down")) return falling_edge; - if (of_property_read_bool(nc, "double")) - return double_edge; - else if (has_acpi_companion(tacho->dev) && fwnode_property_read_bool( - fwn, "double")) + if (fwnode_property_read_bool(fwn, "double")) return double_edge; return rising_edge; } @@ -278,15 +264,10 @@ static int phytium_tacho_get_edge_mode(struct phytium_tacho *tacho) static int phytium_tacho_get_debounce(struct phytium_tacho *tacho) { u32 value; - struct device_node *nc = tacho->dev->of_node; struct fwnode_handle *fwn = tacho->dev->fwnode; - if (!of_property_read_u32(nc, "debounce-level", &value)) + if (!fwnode_property_read_u32(fwn, "debounce-level", &value)) return value; - if (has_acpi_companion(tacho->dev)) { - if (!fwnode_property_read_u32(fwn, "debounce-level", &value)) - return value; - } return 0; } @@ -303,6 +284,7 @@ static int phytium_tacho_probe(struct platform_device *pdev) struct resource *res; struct phytium_tacho *tacho; int ret; + u32 fre; tacho = devm_kzalloc(dev, sizeof(*tacho), GFP_KERNEL); if (!tacho) @@ -320,24 +302,19 @@ static int phytium_tacho_probe(struct platform_device *pdev) return PTR_ERR(tacho->base); } + tacho->freq = TIMER_TACHO_DEFAULT_FREQ; if (!has_acpi_companion(tacho->dev)) { tacho->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(tacho->clk)) - return PTR_ERR(tacho->clk); - ret = clk_prepare_enable(tacho->clk); - if (ret) - return ret; - tacho->freq = clk_get_rate(tacho->clk); + if (IS_ERR(tacho->clk) || clk_prepare_enable(tacho->clk)) + dev_err(&pdev->dev, "Tacho get clocks failed\n"); + else + tacho->freq = clk_get_rate(tacho->clk); } else { - u32 fre; - - ret = clk_prepare_enable(tacho->clk); - if (ret) - return ret; - fwnode_property_read_u32(tacho->dev->fwnode, "clock-frequency", &fre); - tacho->freq = fre; + if (fwnode_property_read_u32(tacho->dev->fwnode, "clock-frequency", &fre)) + dev_err(&pdev->dev, "Tacho get clock-frequency failed\n"); + else + tacho->freq = fre; } - tacho->irq = platform_get_irq(pdev, 0); if (tacho->irq < 0) { dev_err(&pdev->dev, "no irq resource?\n"); @@ -414,3 +391,4 @@ module_platform_driver(phytium_tacho_driver); MODULE_AUTHOR("Zhang Yiqun "); MODULE_DESCRIPTION("Phytium tachometer driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(TACHO_DRIVER_VERSION); diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 96b78a36f377a0f100623853d05d516d6163d134..36e48488f7b78c3d7b1ef75b51bf6612db2e7278 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -94,7 +94,7 @@ obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi-core.o i2c-pasemi-pci.o obj-$(CONFIG_I2C_APPLE) += i2c-pasemi-core.o i2c-pasemi-platform.o obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o obj-$(CONFIG_I2C_PHYTIUM_CORE) += i2c-phytium-core.o -i2c-phytium-core-objs := i2c-phytium-common.o i2c-phytium-master.o i2c-phytium-slave.o +i2c-phytium-core-objs := i2c-phytium-common.o i2c-phytium-mix.o obj-$(CONFIG_I2C_PHYTIUM_PCI) += i2c-phytium-pci.o obj-$(CONFIG_I2C_PHYTIUM_PLATFORM) += i2c-phytium-platform.o obj-$(CONFIG_I2C_PNX) += i2c-pnx.o diff --git a/drivers/i2c/busses/i2c-phytium-common.c b/drivers/i2c/busses/i2c-phytium-common.c index 2c866fa8e83c1a9789488b4f2448d4b2f5ee1c31..b008e6a10bb7c2f49d1d1e6b1f2c733b42f16d7d 100644 --- a/drivers/i2c/busses/i2c-phytium-common.c +++ b/drivers/i2c/busses/i2c-phytium-common.c @@ -2,11 +2,6 @@ /* * Phytium I2C adapter driver. * - * Derived from Synopysys I2C driver. - * Copyright (C) 2006 Texas Instruments. - * Copyright (C) 2007 MontaVista Software Inc. - * Copyright (C) 2009 Provigent Ltd. - * * Copyright (c) 2021-2024 Phytium Technology Co., Ltd. */ #include @@ -20,6 +15,7 @@ #include #include #include +#include #include "i2c-phytium-core.h" @@ -96,22 +92,7 @@ int i2c_phytium_set_sda_hold(struct phytium_i2c_dev *dev) void __i2c_phytium_disable(struct phytium_i2c_dev *dev) { - int timeout = 100; - - do { - __i2c_phytium_disable_nowait(dev); - if ((phytium_readl(dev, IC_ENABLE_STATUS) & 1) == 0) - return; - - /* - * Wait 10 times the signaling period of the highest I2C - * transfer supported by the driver (for 400KHz this is - * 25us). - */ - usleep_range(25, 250); - } while (timeout--); - - dev_warn(dev->dev, "timeout in disabling adapter\n"); + __i2c_phytium_disable_nowait(dev); } unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev) @@ -134,23 +115,12 @@ int i2c_phytium_prepare_clk(struct phytium_i2c_dev *dev, bool prepare) } EXPORT_SYMBOL_GPL(i2c_phytium_prepare_clk); -int i2c_phytium_wait_bus_not_busy(struct phytium_i2c_dev *dev) +int i2c_phytium_check_bus_not_busy(struct phytium_i2c_dev *dev) { - int timeout = 20; /* 20 ms */ - - while (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) { - if (timeout <= 0) { - dev_warn(dev->dev, "timeout waiting for bus ready\n"); - i2c_recover_bus(&dev->adapter); - - if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) - return -ETIMEDOUT; - return 0; - } - timeout--; - usleep_range(1000, 1100); - } - + if (dev->slave_state != SLAVE_STATE_IDLE) + return -EAGAIN; + if (phytium_readl(dev, IC_STATUS) & IC_STATUS_ACTIVITY) + return -ETIMEDOUT; return 0; } diff --git a/drivers/i2c/busses/i2c-phytium-core.h b/drivers/i2c/busses/i2c-phytium-core.h index 7423ccb1b0e896f73d10a25494cbc5f8b06a4950..62900e11153241357f3adc83c86859311af522da 100644 --- a/drivers/i2c/busses/i2c-phytium-core.h +++ b/drivers/i2c/busses/i2c-phytium-core.h @@ -2,11 +2,6 @@ /* * Phytium I2C adapter driver. * - * Derived from Synopysys I2C driver. - * Copyright (C) 2006 Texas Instruments. - * Copyright (C) 2007 MontaVista Software Inc. - * Copyright (C) 2009 Provigent Ltd. - * * Copyright (c) 2021-2024 Phytium Technology Co., Ltd. */ @@ -14,8 +9,13 @@ #include #include +#include + +#define I2C_PHYTIUM_DRV_VERSION "1.0.0" + #define IC_DEFAULT_FUNCTIONALITY (I2C_FUNC_I2C | \ I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_QUICK | \ I2C_FUNC_SMBUS_BYTE_DATA | \ I2C_FUNC_SMBUS_WORD_DATA | \ I2C_FUNC_SMBUS_BLOCK_DATA | \ @@ -93,7 +93,6 @@ #define IC_INTR_SMBALERT_IN_N 0x20000 #define IC_INTR_DEFAULT_MASK (IC_INTR_RX_FULL | \ - IC_INTR_TX_ABRT | \ IC_INTR_STOP_DET) #define IC_INTR_MASTER_MASK (IC_INTR_DEFAULT_MASK | \ IC_INTR_TX_EMPTY) @@ -104,7 +103,8 @@ #define IC_INTR_SMBUS_MASK (IC_INTR_MASTER_MASK | \ IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | \ IC_INTR_SMBCLK_TMO_LOW_TIMEOUT | \ - IC_INTR_SMBSDA_LOW_TIMEOUT) + IC_INTR_SMBSDA_LOW_TIMEOUT | \ + IC_INTR_SMBALERT_IN_N) #define IC_STATUS_ACTIVITY 0x1 #define IC_STATUS_TFE BIT(2) @@ -115,6 +115,15 @@ #define IC_SDA_HOLD_RX_MASK GENMASK(23, IC_SDA_HOLD_RX_SHIFT) #define IC_ERR_TX_ABRT 0x1 +#define IC_ERR_SMBLK_READ_ZERO BIT(31) + +#define IC_ENABLE_EN BIT(0) +#define IC_ENABLE_ALERT_EN BIT(5) +#define IC_ENABLE_RXDATA_ADVANCE_EN BIT(11) +#define IC_ENABLE_MASK (IC_ENABLE_EN | \ + IC_ENABLE_ALERT_EN | \ + IC_ENABLE_RXDATA_ADVANCE_EN) +#define IC_DISABLE_MASK IC_ENABLE_ALERT_EN #define IC_TAR_10BITADDR_MASTER BIT(12) @@ -131,6 +140,15 @@ #define PHYTIUM_IC_MASTER 0 #define PHYTIUM_IC_SLAVE 1 +#if IS_ENABLED(CONFIG_I2C_SLAVE) +enum i2c_slave_state { + SLAVE_STATE_IDLE, + SLAVE_STATE_RECV, + SLAVE_STATE_SEND, + SLAVE_STATE_REQUEST, + SLAVE_STATE_RESPONSE +}; +#endif #define ABRT_7B_ADDR_NOACK 0 #define ABRT_10ADDR1_NOACK 1 #define ABRT_10ADDR2_NOACK 2 @@ -178,6 +196,12 @@ struct phytium_i2c_dev { struct clk *clk; struct reset_control *rst; int mode; + + u32 capability; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + enum i2c_slave_state slave_state; +#endif + spinlock_t i2c_lock; struct i2c_client *slave; u32 (*get_clk_rate_khz)(struct phytium_i2c_dev *dev); @@ -220,6 +244,7 @@ struct phytium_i2c_dev { u16 hs_lcnt; bool pm_disabled; + bool first_time_init_master; void (*disable)(struct phytium_i2c_dev *dev); void (*disable_int)(struct phytium_i2c_dev *dev); int (*init)(struct phytium_i2c_dev *dev); @@ -233,7 +258,7 @@ u32 phytium_readl(struct phytium_i2c_dev *dev, int offset); void phytium_writel(struct phytium_i2c_dev *dev, u32 b, int offset); unsigned long i2c_phytium_clk_rate(struct phytium_i2c_dev *dev); int i2c_phytium_prepare_clk(struct phytium_i2c_dev *dev, bool prepare); -int i2c_phytium_wait_bus_not_busy(struct phytium_i2c_dev *dev); +int i2c_phytium_check_bus_not_busy(struct phytium_i2c_dev *dev); int i2c_phytium_handle_tx_abort(struct phytium_i2c_dev *dev); u32 i2c_phytium_func(struct i2c_adapter *adap); void i2c_phytium_disable(struct phytium_i2c_dev *dev); @@ -244,16 +269,14 @@ u32 i2c_phytium_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); static inline void __i2c_phytium_enable(struct phytium_i2c_dev *dev) { - phytium_writel(dev, 1, IC_ENABLE); + phytium_writel(dev, IC_ENABLE_MASK, IC_ENABLE); } static inline void __i2c_phytium_disable_nowait(struct phytium_i2c_dev *dev) { - phytium_writel(dev, 0, IC_ENABLE); + phytium_writel(dev, IC_DISABLE_MASK, IC_ENABLE); } void __i2c_phytium_disable(struct phytium_i2c_dev *dev); - +void i2c_phytium_configure_master(struct phytium_i2c_dev *dev); extern int i2c_phytium_probe(struct phytium_i2c_dev *dev); - -extern int i2c_phytium_probe_slave(struct phytium_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-phytium-master.c b/drivers/i2c/busses/i2c-phytium-mix.c similarity index 51% rename from drivers/i2c/busses/i2c-phytium-master.c rename to drivers/i2c/busses/i2c-phytium-mix.c index ca745b146b5eee385b6928e5d7e29259cdfcc358..748042c9feb6b6edb849e0ee212a3385d95d3093 100644 --- a/drivers/i2c/busses/i2c-phytium-master.c +++ b/drivers/i2c/busses/i2c-phytium-mix.c @@ -2,13 +2,9 @@ /* * Phytium I2C adapter driver. * - * Derived from Synopysys I2C driver. - * Copyright (C) 2006 Texas Instruments. - * Copyright (C) 2007 MontaVista Software Inc. - * Copyright (C) 2009 Provigent Ltd. - * * Copyright (c) 2021-2024 Phytium Technology Co., Ltd. */ +#include #include #include #include @@ -22,6 +18,78 @@ #include "i2c-phytium-core.h" +#define I2C_TRANS_READ_CMD BIT(8) +#define I2C_TRANS_STOP_CMD BIT(9) +#define I2C_TRANS_RESTART_CMD BIT(10) +#define I2C_QUICK_CMD_BIT_SET BIT(13) +#define DEFAULT_TIMEOUT (DEFAULT_CLOCK_FREQUENCY / 1000 * 35) + +static int i2c_phytium_recover_controller(struct phytium_i2c_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->i2c_lock, flags); + + reset_control_reset(dev->rst); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->slave_state = SLAVE_STATE_IDLE; + dev->status = STATUS_IDLE; + if (dev->slave) + phytium_writel(dev, dev->slave->addr, IC_SAR); +#endif + + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} + +static int i2c_phytium_init_slave(struct phytium_i2c_dev *dev) +{ + /* Disable the adapter. */ + __i2c_phytium_disable(dev); + + /* Write SDA hold time if supported */ + if (dev->sda_hold_time) + phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); + + /* Configure Tx/Rx FIFO threshold levels. */ + phytium_writel(dev, 0, IC_TX_TL); + phytium_writel(dev, 0, IC_RX_TL); + dev->mode = PHYTIUM_IC_SLAVE; + + /* Configure the I2C slave. */ + dev->slave_cfg = IC_CON_RX_FIFO_FULL_HLD_CTRL | IC_CON_RESTART_EN | + IC_CON_STOP_DET_IFADDRESSED; + phytium_writel(dev, dev->slave_cfg, IC_CON); + phytium_writel(dev, IC_INTR_SLAVE_MASK, IC_INTR_MASK); + + return 0; +} + +void i2c_phytium_configure_master(struct phytium_i2c_dev *dev) +{ + struct i2c_timings *t = &dev->timings; + + dev->functionality = I2C_FUNC_10BIT_ADDR | IC_DEFAULT_FUNCTIONALITY | + I2C_FUNC_SLAVE; + + dev->master_cfg = + IC_CON_MASTER | IC_CON_SLAVE_DISABLE | IC_CON_RESTART_EN; + + dev->mode = PHYTIUM_IC_MASTER; + + switch (t->bus_freq_hz) { + case 100000: + dev->master_cfg |= IC_CON_SPEED_STD; + break; + case 3400000: + dev->master_cfg |= IC_CON_SPEED_HIGH; + break; + default: + dev->master_cfg |= IC_CON_SPEED_FAST; + } +} +EXPORT_SYMBOL_GPL(i2c_phytium_configure_master); + static int i2c_phytium_init_master(struct phytium_i2c_dev *dev) { /* Disable the adapter */ @@ -40,7 +108,7 @@ static int i2c_phytium_init_master(struct phytium_i2c_dev *dev) phytium_writel(dev, dev->hs_hcnt, IC_HS_SCL_HCNT); phytium_writel(dev, dev->hs_lcnt, IC_HS_SCL_LCNT); } - + dev->mode = PHYTIUM_IC_MASTER; /* Write SDA hold time if supported */ if (dev->sda_hold_time) phytium_writel(dev, dev->sda_hold_time, IC_SDA_HOLD); @@ -50,11 +118,29 @@ static int i2c_phytium_init_master(struct phytium_i2c_dev *dev) phytium_writel(dev, 0, IC_RX_TL); /* Configure the I2C master */ + if (dev->first_time_init_master == false) { + i2c_phytium_configure_master(dev); + dev->first_time_init_master = true; + } phytium_writel(dev, dev->master_cfg, IC_CON); return 0; } +#if IS_ENABLED(CONFIG_I2C_SLAVE) +static void i2c_phytium_change_mode(int target_mode, struct phytium_i2c_dev *dev) +{ + if (target_mode == PHYTIUM_IC_MASTER) { + dev->disable_int(dev); + dev->disable(dev); + i2c_phytium_init_master(dev); + } else { + i2c_phytium_init_slave(dev); + __i2c_phytium_enable(dev); + } +} +#endif + static void i2c_phytium_xfer_init(struct phytium_i2c_dev *dev) { struct i2c_msg *msgs = dev->msgs; @@ -63,6 +149,9 @@ static void i2c_phytium_xfer_init(struct phytium_i2c_dev *dev) /* Disable the adapter */ __i2c_phytium_disable(dev); + if ((dev->capability & I2C_FUNC_SMBUS_QUICK) && (msgs[0].len == 0)) + ic_tar = I2C_QUICK_CMD_BIT_SET; + /* If the slave address is 10-bit address, enable 10BITADDR */ ic_con = phytium_readl(dev, IC_CON); if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { @@ -104,23 +193,26 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) u8 *buf = dev->tx_buf; bool need_restart = false; - intr_mask = IC_INTR_MASTER_MASK; + intr_mask = IC_INTR_SMBUS_MASK; for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { u32 flags = msgs[dev->msg_write_idx].flags; if (msgs[dev->msg_write_idx].addr != addr) { - dev_err(dev->dev, - "%s: invalid target address\n", __func__); dev->msg_err = -EINVAL; break; } + /* If the register dose not support smbus block read function, + * then use the operation of program code + */ + if (!(dev->capability & I2C_FUNC_SMBUS_BLOCK_DATA) && + (flags & I2C_M_RECV_LEN)) + msgs[dev->msg_write_idx].len = 1 + I2C_SMBUS_BLOCK_MAX; if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { /* new i2c_msg */ buf = msgs[dev->msg_write_idx].buf; buf_len = msgs[dev->msg_write_idx].len; - if ((dev->master_cfg & IC_CON_RESTART_EN) && (dev->msg_write_idx > 0)) need_restart = true; @@ -128,16 +220,14 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) tx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_TXFLR); rx_limit = dev->tx_fifo_depth - phytium_readl(dev, IC_RXFLR); - while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { u32 cmd = 0; - if (dev->msg_write_idx == dev->msgs_num - 1 && - buf_len == 1 && !(flags & I2C_M_RECV_LEN)) - cmd |= BIT(9); + if (dev->msg_write_idx == dev->msgs_num - 1 && buf_len == 1) + cmd |= I2C_TRANS_STOP_CMD; if (need_restart) { - cmd |= BIT(10); + cmd |= I2C_TRANS_RESTART_CMD; need_restart = false; } @@ -146,7 +236,7 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) if (dev->rx_outstanding >= dev->rx_fifo_depth) break; - phytium_writel(dev, cmd | 0x100, IC_DATA_CMD); + phytium_writel(dev, cmd | I2C_TRANS_READ_CMD, IC_DATA_CMD); rx_limit--; dev->rx_outstanding++; } else { @@ -164,22 +254,30 @@ static void i2c_phytium_xfer_msg(struct phytium_i2c_dev *dev) * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop * the transaction here. */ - if (buf_len > 0 || flags & I2C_M_RECV_LEN) { + if (buf_len > 0) { /* more bytes to be written */ dev->status |= STATUS_WRITE_IN_PROGRESS; break; } - dev->status &= ~STATUS_WRITE_IN_PROGRESS; } + /*This is use for quick cmd*/ + if ((dev->capability & I2C_FUNC_SMBUS_QUICK) && (msgs[0].len == 0)) { + if (msgs[0].flags == I2C_M_RD) + phytium_writel(dev, I2C_TRANS_STOP_CMD | I2C_TRANS_READ_CMD, + IC_DATA_CMD); + else + phytium_writel(dev, I2C_TRANS_STOP_CMD, IC_DATA_CMD); + } + if (dev->msg_write_idx == dev->msgs_num) intr_mask &= ~IC_INTR_TX_EMPTY; if (dev->msg_err) intr_mask = 0; - phytium_writel(dev, intr_mask, IC_INTR_MASK); + phytium_writel(dev, intr_mask, IC_INTR_MASK); } static u8 i2c_phytium_recv_len(struct phytium_i2c_dev *dev, u8 len) @@ -191,10 +289,17 @@ static u8 i2c_phytium_recv_len(struct phytium_i2c_dev *dev, u8 len) * Adjust the buffer length and mask the flag * after receiving the first byte. */ + if (len > I2C_SMBUS_BLOCK_MAX) + len = 0; + len += (flags & I2C_CLIENT_PEC) ? 2 : 1; dev->tx_buf_len = len - min_t(u8, len, dev->rx_outstanding); - msgs[dev->msg_read_idx].len = len; - msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; + + /* require adding one byte to trigger the i2c_phytium_xfer finishing the transaction. */ + if (dev->tx_buf_len == 0) { + dev->tx_buf_len = 1; + len = dev->rx_outstanding + 1; + } return len; } @@ -226,9 +331,19 @@ static void i2c_phytium_read(struct phytium_i2c_dev *dev) *buf = phytium_readl(dev, IC_DATA_CMD); /* Ensure length byte is a valid value */ - if (flags & I2C_M_RECV_LEN && - *buf <= I2C_SMBUS_BLOCK_MAX && *buf > 0) { + if (flags & I2C_M_RECV_LEN) { + msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; len = i2c_phytium_recv_len(dev, *buf); + if (*buf > I2C_SMBUS_BLOCK_MAX || *buf == 0) { + dev_err(dev->dev, + "The value %d is out of the SMBus range [1~32]\n", + *buf); + *buf = 0; + dev->cmd_err = IC_ERR_SMBLK_READ_ZERO; + } else { + msgs[dev->msg_read_idx].len = *buf + + ((flags & I2C_CLIENT_PEC) ? 2 : 1); + } } buf++; dev->rx_outstanding--; @@ -240,21 +355,28 @@ static void i2c_phytium_read(struct phytium_i2c_dev *dev) dev->rx_buf = buf; return; } - dev->status &= ~STATUS_READ_IN_PROGRESS; } } -static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], int num) +static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], + int num) { struct phytium_i2c_dev *dev = i2c_get_adapdata(adapter); int ret; + unsigned long flags; dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); - pm_runtime_get_sync(dev->dev); + ret = pm_runtime_get_sync(dev->dev); + if (ret < 0) { + dev_err(dev->dev, "pm runtime get sync err.\n"); + goto pm_exit; + } + spin_lock_irqsave(&dev->i2c_lock, flags); reinit_completion(&dev->cmd_complete); + dev->msgs = msgs; dev->msgs_num = num; dev->cmd_err = 0; @@ -265,22 +387,30 @@ static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], dev->abort_source = 0; dev->rx_outstanding = 0; - ret = i2c_phytium_wait_bus_not_busy(dev); + ret = i2c_phytium_check_bus_not_busy(dev); if (ret < 0) goto done; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_cfg) + i2c_phytium_change_mode(PHYTIUM_IC_MASTER, dev); +#endif + /* Start the transfers */ i2c_phytium_xfer_init(dev); - + spin_unlock_irqrestore(&dev->i2c_lock, flags); /* Wait for tx to complete */ - if (!wait_for_completion_timeout(&dev->cmd_complete, adapter->timeout)) { + if (!wait_for_completion_timeout(&dev->cmd_complete, + adapter->timeout)) { dev_err(dev->dev, "controller timed out\n"); - i2c_recover_bus(&dev->adapter); - i2c_phytium_init_master(dev); + i2c_phytium_recover_controller(dev); + spin_lock_irqsave(&dev->i2c_lock, flags); + if (!dev->slave_cfg) + i2c_phytium_init_master(dev); ret = -ETIMEDOUT; goto done; } - + spin_lock_irqsave(&dev->i2c_lock, flags); __i2c_phytium_disable_nowait(dev); if (dev->msg_err) { @@ -295,29 +425,99 @@ static int i2c_phytium_xfer(struct i2c_adapter *adapter, struct i2c_msg msgs[], /* We have got an error */ if (dev->cmd_err == IC_ERR_TX_ABRT) { + spin_unlock_irqrestore(&dev->i2c_lock, flags); ret = i2c_phytium_handle_tx_abort(dev); + spin_lock_irqsave(&dev->i2c_lock, flags); goto done; } - if (dev->status) - dev_err(dev->dev, "transfer terminated early.\n"); - ret = -EIO; done: +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->slave_cfg) + i2c_phytium_change_mode(PHYTIUM_IC_SLAVE, dev); + dev->status = STATUS_IDLE; + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_unlock_irqrestore(&dev->i2c_lock, flags); + +pm_exit: + pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); return ret; } +#if IS_ENABLED(CONFIG_I2C_SLAVE) +static int i2c_phytium_reg_slave(struct i2c_client *slave) +{ + int ret = 0; + unsigned long flags; + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + + spin_lock_irqsave(&dev->i2c_lock, flags); + + if (dev->slave) { + ret = -EBUSY; + goto abort; + } + if (slave->flags & I2C_CLIENT_TEN) { + ret = -EAFNOSUPPORT; + goto abort; + } + pm_runtime_get_sync(dev->dev); + + /* + * Set slave address in the IC_SAR register, + * the address to which the i2c responds. + */ + i2c_phytium_init_slave(dev); + phytium_writel(dev, slave->addr, IC_SAR); + dev->slave = slave; + dev->mode = PHYTIUM_IC_SLAVE; + __i2c_phytium_enable(dev); + + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + dev->abort_source = 0; + dev->rx_outstanding = 0; + +abort: + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return ret; +} + +static int i2c_phytium_unreg_slave(struct i2c_client *slave) +{ + unsigned long flags; + struct phytium_i2c_dev *dev = i2c_get_adapdata(slave->adapter); + + spin_lock_irqsave(&dev->i2c_lock, flags); + + dev->disable_int(dev); + dev->disable(dev); + dev->slave = NULL; + pm_runtime_put(dev->dev); + + dev->mode = PHYTIUM_IC_MASTER; + i2c_phytium_init_master(dev); + spin_unlock_irqrestore(&dev->i2c_lock, flags); + return 0; +} +#endif + static const struct i2c_algorithm i2c_phytium_algo = { .master_xfer = i2c_phytium_xfer, .functionality = i2c_phytium_func, -}; - -static const struct i2c_adapter_quirks i2c_phytium_quirks = { - .flags = I2C_AQ_NO_ZERO_LEN, +#if IS_ENABLED(CONFIG_I2C_SLAVE) + .reg_slave = i2c_phytium_reg_slave, + .unreg_slave = i2c_phytium_unreg_slave, +#endif }; static u32 i2c_phytium_read_clear_intrbits(struct phytium_i2c_dev *dev) @@ -335,7 +535,8 @@ static u32 i2c_phytium_read_clear_intrbits(struct phytium_i2c_dev *dev) if (stat & IC_INTR_RD_REQ) phytium_readl(dev, IC_CLR_RD_REQ); if (stat & IC_INTR_TX_ABRT) { - dev->abort_source = phytium_readl(dev, IC_TX_ABRT_SOURCE); + if (dev->mode == PHYTIUM_IC_MASTER) + dev->abort_source = phytium_readl(dev, IC_TX_ABRT_SOURCE); phytium_readl(dev, IC_CLR_TX_ABRT); } if (stat & IC_INTR_RX_DONE) @@ -362,12 +563,14 @@ static u32 i2c_phytium_read_clear_intrbits(struct phytium_i2c_dev *dev) static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) { - u32 stat; + u32 stat, raw_stat; + raw_stat = phytium_readl(dev, IC_RAW_INTR_STAT); stat = i2c_phytium_read_clear_intrbits(dev); /* SMBus interrupt */ - if (stat & (IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | IC_INTR_SMBCLK_TMO_LOW_TIMEOUT)) { + if (stat & + (IC_INTR_SMBCLK_EXT_LOW_TIMEOUT | IC_INTR_SMBCLK_TMO_LOW_TIMEOUT)) { phytium_writel(dev, phytium_readl(dev, IC_ENABLE) & (~BIT(6)), IC_ENABLE); phytium_writel(dev, phytium_readl(dev, IC_ENABLE) | BIT(4), @@ -384,15 +587,25 @@ static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) if (stat & IC_INTR_SMBALERT_IN_N && dev->ara) i2c_handle_smbus_alert(dev->ara); - if (stat & IC_INTR_TX_ABRT) { - dev->cmd_err |= IC_ERR_TX_ABRT; - dev->status = STATUS_IDLE; - - /* Anytime TX_ABRT is set, the contents of the tx/rx - * buffers are flushed. Make sure to skip them. - */ - phytium_writel(dev, 0, IC_INTR_MASK); - goto abort; + if (raw_stat & IC_INTR_TX_ABRT) { + if (stat & IC_INTR_TX_ABRT) { + dev->cmd_err |= IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + + /* Anytime TX_ABRT is set, the contents of the tx/rx + * buffers are flushed. Make sure to skip them. + */ + phytium_writel(dev, 0, IC_INTR_MASK); + goto abort; + } else if (stat & IC_INTR_STOP_DET) { + dev->cmd_err |= IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + phytium_readl(dev, IC_CLR_TX_ABRT); + phytium_writel(dev, 0, IC_INTR_MASK); + + complete(&dev->cmd_complete); + return 0; + } } if (stat & IC_INTR_RX_FULL) @@ -402,10 +615,9 @@ static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) i2c_phytium_xfer_msg(dev); abort: - if ((stat & (IC_INTR_TX_ABRT | IC_INTR_STOP_DET)) || - dev->msg_err) + if ((stat & IC_INTR_TX_ABRT) || (stat & IC_INTR_STOP_DET) || dev->msg_err) { complete(&dev->cmd_complete); - else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { + } else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { /* Workaround to trigger pending interrupt */ stat = phytium_readl(dev, IC_INTR_MASK); i2c_phytium_disable_int(dev); @@ -415,6 +627,93 @@ static int i2c_phytium_irq_handler_master(struct phytium_i2c_dev *dev) return 0; } +static int i2c_phytium_irq_handler_slave(struct phytium_i2c_dev *dev) +{ + u32 raw_stat, stat, enabled; + u8 val, slave_activity; + + enabled = phytium_readl(dev, IC_ENABLE); + raw_stat = phytium_readl(dev, IC_RAW_INTR_STAT); + slave_activity = + ((phytium_readl(dev, IC_STATUS) & IC_STATUS_SLAVE_ACTIVITY) >> + 6); + + if (!enabled || !(raw_stat & ~IC_INTR_ACTIVITY) || !dev->slave) + return 0; + + stat = i2c_phytium_read_clear_intrbits(dev); + + if (stat & IC_INTR_RX_FULL) { + if (dev->status != STATUS_WRITE_IN_PROGRESS) { + dev->status = STATUS_WRITE_IN_PROGRESS; + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, + &val); + dev->slave_state = SLAVE_STATE_RECV; + } + do { + val = phytium_readl(dev, IC_DATA_CMD); + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, + &val); + val = phytium_readl(dev, IC_STATUS); + } while (val & BIT(3)); + } + + if (stat & IC_INTR_RD_REQ) { + if (slave_activity) { + phytium_readl(dev, IC_CLR_RD_REQ); + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_REQUESTED, &val); + dev->status |= STATUS_READ_IN_PROGRESS; + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + dev->slave_state = SLAVE_STATE_SEND; + + } else { + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_PROCESSED, &val); + } + phytium_writel(dev, val, IC_DATA_CMD); + } + } + + if (stat & IC_INTR_STOP_DET) { + i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->status = STATUS_IDLE; + dev->slave_state = SLAVE_STATE_IDLE; +#endif + } + + return 1; +} + +static irqreturn_t i2c_phytium_isr(int this_irq, void *dev_id) +{ + struct phytium_i2c_dev *dev = dev_id; + u32 stat, enabled; + + spin_lock(&dev->i2c_lock); + +#if IS_ENABLED(CONFIG_I2C_SLAVE) + if (dev->mode == PHYTIUM_IC_SLAVE) { + i2c_phytium_irq_handler_slave(dev); + spin_unlock(&dev->i2c_lock); + return IRQ_HANDLED; + } +#endif + enabled = phytium_readl(dev, IC_ENABLE); + stat = phytium_readl(dev, IC_RAW_INTR_STAT); + if (!enabled || !(stat & ~IC_INTR_ACTIVITY)) { + spin_unlock(&dev->i2c_lock); + return IRQ_NONE; + } + + i2c_phytium_irq_handler_master(dev); + spin_unlock(&dev->i2c_lock); + return IRQ_HANDLED; +} + static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) { const char *mode_str, *fp_str = ""; @@ -430,24 +729,21 @@ static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) /* Calculate SCL timing parameters for standard mode if not set */ if (!dev->ss_hcnt || !dev->ss_lcnt) { ic_clk = i2c_phytium_clk_rate(dev); - dev->ss_hcnt = - i2c_phytium_scl_hcnt(ic_clk, - 4000, /* tHD;STA = tHIGH = 4.0 us */ - sda_falling_time, - 0, /* 0: DW default, 1: Ideal */ - 0); /* No offset */ + dev->ss_hcnt = i2c_phytium_scl_hcnt( + ic_clk, 4000, /* tHD;STA = tHIGH = 4.0 us */ + sda_falling_time, 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ dev->ss_lcnt = - i2c_phytium_scl_lcnt(ic_clk, - 4700, /* tLOW = 4.7 us */ - scl_falling_time, - 0); /* No offset */ + i2c_phytium_scl_lcnt(ic_clk, 4700, /* tLOW = 4.7 us */ + scl_falling_time, + 0); /* No offset */ } - dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", - dev->ss_hcnt, dev->ss_lcnt); + dev_dbg(dev->dev, "Standard Mode HCNT:LCNT = %d:%d\n", dev->ss_hcnt, + dev->ss_lcnt); /* * Set SCL timing parameters for fast mode or fast mode plus. Only - * difference is the timing parameter values since the registers are - * the same. + * difference is the timing parameter values since the registers + * are the same. */ if (t->bus_freq_hz == 1000000) { /* @@ -466,20 +762,17 @@ static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) */ if (!dev->fs_hcnt || !dev->fs_lcnt) { ic_clk = i2c_phytium_clk_rate(dev); - dev->fs_hcnt = - i2c_phytium_scl_hcnt(ic_clk, - 600, /* tHD;STA = tHIGH = 0.6 us */ - sda_falling_time, - 0, /* 0: DW default, 1: Ideal */ - 0); /* No offset */ + dev->fs_hcnt = i2c_phytium_scl_hcnt( + ic_clk, 600, /* tHD;STA = tHIGH = 0.6 us */ + sda_falling_time, 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ dev->fs_lcnt = - i2c_phytium_scl_lcnt(ic_clk, - 1300, /* tLOW = 1.3 us */ - scl_falling_time, - 0); /* No offset */ + i2c_phytium_scl_lcnt(ic_clk, 1300, /* tLOW = 1.3 us */ + scl_falling_time, + 0); /* No offset */ } - dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", - fp_str, dev->fs_hcnt, dev->fs_lcnt); + dev_dbg(dev->dev, "Fast Mode%s HCNT:LCNT = %d:%d\n", fp_str, + dev->fs_hcnt, dev->fs_lcnt); if (dev->hs_hcnt && dev->hs_lcnt) dev_dbg(dev->dev, "High Speed Mode HCNT:LCNT = %d:%d\n", @@ -505,19 +798,18 @@ static int i2c_phytium_set_timings_master(struct phytium_i2c_dev *dev) return ret; } -static irqreturn_t i2c_phytium_isr(int this_irq, void *dev_id) +static int i2c_phytium_enable_smbus_alert(struct phytium_i2c_dev *i2c_dev) { - struct phytium_i2c_dev *dev = dev_id; - u32 stat, enabled; + struct i2c_adapter *adap = &i2c_dev->adapter; + struct i2c_smbus_alert_setup setup; - enabled = phytium_readl(dev, IC_ENABLE); - stat = phytium_readl(dev, IC_RAW_INTR_STAT); - if (!enabled || !(stat & ~IC_INTR_ACTIVITY)) - return IRQ_NONE; + setup.irq = 0; + i2c_dev->ara = i2c_new_smbus_alert_device(adap, &setup); - i2c_phytium_irq_handler_master(dev); + if (IS_ERR(i2c_dev->ara)) + return PTR_ERR(i2c_dev->ara); - return IRQ_HANDLED; + return 0; } int i2c_phytium_probe(struct phytium_i2c_dev *dev) @@ -525,13 +817,21 @@ int i2c_phytium_probe(struct phytium_i2c_dev *dev) struct i2c_adapter *adapter = &dev->adapter; unsigned long irq_flags; int ret; + u32 alert = 0; init_completion(&dev->cmd_complete); + if (dev->mode == PHYTIUM_IC_MASTER) { + snprintf(adapter->name, sizeof(adapter->name), + "Phytium I2C Adapter"); + dev->init = i2c_phytium_init_master; + } else if (dev->mode == PHYTIUM_IC_SLAVE) { + snprintf(adapter->name, sizeof(adapter->name), + "Phytium I2C Slave Adapter"); + dev->init = i2c_phytium_init_slave; + } - dev->init = i2c_phytium_init_master; dev->disable = i2c_phytium_disable; dev->disable_int = i2c_phytium_disable_int; - ret = i2c_phytium_set_timings_master(dev); if (ret) return ret; @@ -540,16 +840,14 @@ int i2c_phytium_probe(struct phytium_i2c_dev *dev) if (ret) return ret; - /* XXX: should be initialized in firmware, remove it in future */ -#define DEFAULT_TIMEOUT (DEFAULT_CLOCK_FREQUENCY / 1000 * 35) - phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_MEXT); - phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_TIMEOUT); - phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBDAT_STUCK_TIMEOUT); + if (dev->mode == PHYTIUM_IC_MASTER) { + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_MEXT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBCLK_LOW_TIMEOUT); + phytium_writel(dev, DEFAULT_TIMEOUT, IC_SMBDAT_STUCK_TIMEOUT); + } - snprintf(adapter->name, sizeof(adapter->name), "Phytium I2C adapter"); adapter->retries = 3; adapter->algo = &i2c_phytium_algo; - adapter->quirks = &i2c_phytium_quirks; adapter->dev.parent = dev->dev; i2c_set_adapdata(adapter, dev); @@ -559,7 +857,8 @@ int i2c_phytium_probe(struct phytium_i2c_dev *dev) ret = devm_request_irq(dev->dev, dev->irq, i2c_phytium_isr, irq_flags, dev_name(dev->dev), dev); if (ret) { - dev_err(dev->dev, "failed to request irq %i: %d\n", dev->irq, ret); + dev_err(dev->dev, "failed to request irq %i: %d\n", dev->irq, + ret); return ret; } @@ -573,11 +872,27 @@ int i2c_phytium_probe(struct phytium_i2c_dev *dev) ret = i2c_add_numbered_adapter(adapter); if (ret) dev_err(dev->dev, "fail to add adapter: %d\n", ret); + + if (of_property_read_bool(dev->dev->of_node, "smbus-alert")) + alert = 1; + else if (has_acpi_companion(dev->dev)) + fwnode_property_read_u32_array(dev->dev->fwnode, "smbus_alert", &alert, 1); + + if (alert) { + ret = i2c_phytium_enable_smbus_alert(dev); + if (ret) { + dev_err(dev->dev, + "failed to enable SMBus alert protocol (%d)\n", ret); + } + } + pm_runtime_put_noidle(dev->dev); return ret; } EXPORT_SYMBOL_GPL(i2c_phytium_probe); -MODULE_DESCRIPTION("Phytium I2C bus master adapter"); +MODULE_AUTHOR("Wu Jinyong "); +MODULE_DESCRIPTION("Phytium I2C bus controller adapter"); MODULE_LICENSE("GPL"); +MODULE_VERSION(I2C_PHYTIUM_DRV_VERSION); diff --git a/drivers/i2c/busses/i2c-phytium-pci.c b/drivers/i2c/busses/i2c-phytium-pci.c index c63abddd09b13f5220a4946c74d3de4943dfdb98..91d8cabcd96b95dc805504e40e42f56d7f6bf504 100644 --- a/drivers/i2c/busses/i2c-phytium-pci.c +++ b/drivers/i2c/busses/i2c-phytium-pci.c @@ -183,6 +183,10 @@ static int i2c_phytium_pci_probe(struct pci_dev *pdev, dev->irq = pdev->irq; dev->flags |= controller->flags; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_lock_init(&dev->i2c_lock); dev->functionality = controller->functionality | IC_DEFAULT_FUNCTIONALITY; dev->master_cfg = controller->bus_cfg; if (controller->scl_sda_cfg) { @@ -205,6 +209,8 @@ static int i2c_phytium_pci_probe(struct pci_dev *pdev, ACPI_COMPANION_SET(&adapter->dev, ACPI_COMPANION(&pdev->dev)); adapter->nr = controller->bus_num; + dev->capability = 0; + dev->first_time_init_master = true; ret = i2c_phytium_probe(dev); if (ret) goto out; diff --git a/drivers/i2c/busses/i2c-phytium-platform.c b/drivers/i2c/busses/i2c-phytium-platform.c index 6f4de15570008c96f01ff5298813d981e2cb64a7..084135a71895141d07150a3c7c4fefab5025bef2 100644 --- a/drivers/i2c/busses/i2c-phytium-platform.c +++ b/drivers/i2c/busses/i2c-phytium-platform.c @@ -2,11 +2,6 @@ /* * Phytium I2C adapter driver. * - * Derived from Synopysys I2C driver. - * Copyright (C) 2006 Texas Instruments. - * Copyright (C) 2007 MontaVista Software Inc. - * Copyright (C) 2009 Provigent Ltd. - * * Copyright (c) 2021-2024 Phytium Technology Co., Ltd. */ @@ -119,29 +114,6 @@ static inline int phytium_i2c_acpi_configure(struct platform_device *pdev) } #endif -static void i2c_phytium_configure_master(struct phytium_i2c_dev *dev) -{ - struct i2c_timings *t = &dev->timings; - - dev->functionality = I2C_FUNC_10BIT_ADDR | IC_DEFAULT_FUNCTIONALITY; - - dev->master_cfg = IC_CON_MASTER | IC_CON_SLAVE_DISABLE | - IC_CON_RESTART_EN; - - dev->mode = PHYTIUM_IC_MASTER; - - switch (t->bus_freq_hz) { - case 100000: - dev->master_cfg |= IC_CON_SPEED_STD; - break; - case 3400000: - dev->master_cfg |= IC_CON_SPEED_HIGH; - break; - default: - dev->master_cfg |= IC_CON_SPEED_FAST; - } -} - static void i2c_phytium_configure_slave(struct phytium_i2c_dev *dev) { dev->functionality = I2C_FUNC_SLAVE | IC_DEFAULT_FUNCTIONALITY; @@ -164,7 +136,6 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) 0, 100000, 400000, 1000000, 3400000 }; - irq = platform_get_irq(pdev, 0); if (irq < 0) return irq; @@ -180,6 +151,11 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->irq = irq; + dev->first_time_init_master = false; +#if IS_ENABLED(CONFIG_I2C_SLAVE) + dev->slave_state = SLAVE_STATE_IDLE; +#endif + spin_lock_init(&dev->i2c_lock); platform_set_drvdata(pdev, dev); dev->rst = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); @@ -231,11 +207,12 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) goto exit_reset; } - if (i2c_detect_slave_mode(&pdev->dev)) + if (i2c_detect_slave_mode(&pdev->dev)) { i2c_phytium_configure_slave(dev); - else + } else { + dev->first_time_init_master = true; i2c_phytium_configure_master(dev); - + } dev->clk = devm_clk_get(&pdev->dev, NULL); if (!i2c_phytium_prepare_clk(dev, true)) { u64 clk_khz; @@ -257,6 +234,9 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) adap->class = I2C_CLASS_DEPRECATED; ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev)); adap->dev.of_node = pdev->dev.of_node; + adap->dev.fwnode = pdev->dev.fwnode; + + dev->capability = 0; dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_SMART_PREPARE | @@ -272,10 +252,7 @@ static int phytium_i2c_plat_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - if (dev->mode == PHYTIUM_IC_SLAVE) - ret = i2c_phytium_probe_slave(dev); - else - ret = i2c_phytium_probe(dev); + ret = i2c_phytium_probe(dev); if (ret) goto exit_probe; diff --git a/drivers/i2c/busses/i2c-phytium-slave.c b/drivers/i2c/busses/i2c-phytium-slave.c index 2f0076561b642005215e6fe28161b9783ce8d73f..3d6f3c393218f5bc2e5dd67ae6070df233a1226f 100644 --- a/drivers/i2c/busses/i2c-phytium-slave.c +++ b/drivers/i2c/busses/i2c-phytium-slave.c @@ -146,62 +146,44 @@ static int i2c_phytium_irq_handler_slave(struct phytium_i2c_dev *dev) stat = phytium_readl(dev, IC_INTR_STAT); enabled = phytium_readl(dev, IC_ENABLE); raw_stat = phytium_readl(dev, IC_RAW_INTR_STAT); - slave_activity = ((phytium_readl(dev, IC_STATUS) & - IC_STATUS_SLAVE_ACTIVITY) >> 6); + slave_activity = + ((phytium_readl(dev, IC_STATUS) & IC_STATUS_SLAVE_ACTIVITY) >> 6); if (!enabled || !(raw_stat & ~IC_INTR_ACTIVITY) || !dev->slave) return 0; - dev_dbg(dev->dev, - "%#x STATUS SLAVE_ACTIVITY=%#x : RAW_INTR_STAT=%#x : INTR_STAT=%#x\n", - enabled, slave_activity, raw_stat, stat); + stat = i2c_phytium_read_clear_intrbits_slave(dev); - if ((stat & IC_INTR_RX_FULL) && (stat & IC_INTR_STOP_DET)) - i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val); + if (stat & IC_INTR_RX_FULL) { + if (dev->status != STATUS_WRITE_IN_PROGRESS) { + dev->status = STATUS_WRITE_IN_PROGRESS; + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val); + } + do { + val = phytium_readl(dev, IC_DATA_CMD); + i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, &val); + val = phytium_readl(dev, IC_STATUS); + } while (val & BIT(3)); + } if (stat & IC_INTR_RD_REQ) { if (slave_activity) { - if (stat & IC_INTR_RX_FULL) { - val = phytium_readl(dev, IC_DATA_CMD); - - if (!i2c_slave_event(dev->slave, - I2C_SLAVE_WRITE_RECEIVED, - &val)) { - dev_vdbg(dev->dev, "Byte %X acked!", - val); - } - phytium_readl(dev, IC_CLR_RD_REQ); - stat = i2c_phytium_read_clear_intrbits_slave(dev); + phytium_readl(dev, IC_CLR_RD_REQ); + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + i2c_slave_event(dev->slave, I2C_SLAVE_READ_REQUESTED, &val); + dev->status |= STATUS_READ_IN_PROGRESS; + dev->status &= ~STATUS_WRITE_IN_PROGRESS; } else { - phytium_readl(dev, IC_CLR_RD_REQ); - phytium_readl(dev, IC_CLR_RX_UNDER); - stat = i2c_phytium_read_clear_intrbits_slave(dev); + i2c_slave_event(dev->slave, + I2C_SLAVE_READ_PROCESSED, &val); } - if (!i2c_slave_event(dev->slave, - I2C_SLAVE_READ_REQUESTED, - &val)) - phytium_writel(dev, val, IC_DATA_CMD); + phytium_writel(dev, val, IC_DATA_CMD); } } - if (stat & IC_INTR_RX_DONE) { - if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED, - &val)) - phytium_readl(dev, IC_CLR_RX_DONE); - - i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); - stat = i2c_phytium_read_clear_intrbits_slave(dev); - return 1; - } - - if (stat & IC_INTR_RX_FULL) { - val = phytium_readl(dev, IC_DATA_CMD); - if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED, - &val)) - dev_vdbg(dev->dev, "Byte %X acked!", val); - } else { + if (stat & IC_INTR_STOP_DET) { i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val); - stat = i2c_phytium_read_clear_intrbits_slave(dev); + dev->status = STATUS_IDLE; } return 1; @@ -212,7 +194,6 @@ static irqreturn_t i2c_phytium_isr_slave(int this_irq, void *dev_id) struct phytium_i2c_dev *dev = dev_id; int ret; - i2c_phytium_read_clear_intrbits_slave(dev); ret = i2c_phytium_irq_handler_slave(dev); if (ret > 0) complete(&dev->cmd_complete); @@ -237,12 +218,14 @@ int i2c_phytium_probe_slave(struct phytium_i2c_dev *dev) dev->disable = i2c_phytium_disable; dev->disable_int = i2c_phytium_disable_int; + dev->status = STATUS_IDLE; + ret = dev->init(dev); if (ret) return ret; snprintf(adap->name, sizeof(adap->name), - "Synopsys DesignWare I2C Slave adapter"); + "Phytium I2C Slave adapter"); adap->retries = 3; adap->algo = &i2c_phytium_algo; adap->dev.parent = dev->dev; diff --git a/drivers/i3c/i3c_master_acpi.c b/drivers/i3c/i3c_master_acpi.c index 9346d882aa5529d644a91d3fc0928a8d1c62cccc..d1c6189dc84ae3449272e328a1e3a415a7a86033 100644 --- a/drivers/i3c/i3c_master_acpi.c +++ b/drivers/i3c/i3c_master_acpi.c @@ -25,8 +25,9 @@ static int i3c_master_acpi_parse_val(union acpi_object *store_elements, char *me if (package_count == 2) { acpi_elements = acpi_elements[i].package.elements; - if (acpi_elements[0].type == ACPI_TYPE_STRING) { - if (!strcmp(acpi_elements[0].string.pointer, method)) { + if ((acpi_elements[0].type == ACPI_TYPE_STRING) && + (!strcmp(acpi_elements[0].string.pointer, method))) { + if (acpi_elements[1].type == ACPI_TYPE_INTEGER) { *value = acpi_elements[1].integer.value; ret = 0; break; diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c index 826f2877d554623c41152526e7c04b8146ce5430..a72fdea49116e3a35e8d1c1abf21484d90381462 100644 --- a/drivers/i3c/master.c +++ b/drivers/i3c/master.c @@ -19,7 +19,7 @@ #include #include -#ifdef CONFIG_ACPI +#if defined(CONFIG_ACPI) && defined(CONFIG_ARCH_PHYTIUM) #include "i3c_master_acpi.h" #endif #include "internals.h" @@ -2252,7 +2252,7 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master) return 0; } -#ifdef CONFIG_ACPI +#if defined(CONFIG_ACPI) && defined(CONFIG_ARCH_PHYTIUM) static int i3c_acpi_master_add_i2c_boardinfo(struct i3c_master_controller *master, struct acpi_device *adev, struct i3c_acpi_lookup *look_up) { @@ -2364,7 +2364,6 @@ static int i3c_acpi_master_get_slave_info(struct i3c_master_controller *master, return ret; } - static bool i3c_master_check_cpu_workaround(void) { u32 cpu_implementor; @@ -2916,7 +2915,7 @@ int i3c_master_register(struct i3c_master_controller *master, ret = of_populate_i3c_bus(master); if (ret) goto err_put_dev; -#ifdef CONFIG_ACPI +#if defined(CONFIG_ACPI) && defined(CONFIG_ARCH_PHYTIUM) i3c_acpi_register_devices(master); #endif list_for_each_entry(i2cbi, &master->boardinfo.i2c, node) { diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h index 64590b86eb37ff3079ed8b5c8dd4c2ad76180206..8dfb23df58f1212fbfd0abc2fb5dbb5b772fc4a6 100644 --- a/drivers/input/serio/i8042-io.h +++ b/drivers/input/serio/i8042-io.h @@ -2,7 +2,9 @@ #ifndef _I8042_IO_H #define _I8042_IO_H - +#ifdef CONFIG_ARCH_PHYTIUM +#include "../../acpi/phytium_base_ctrl.h" +#endif /* * Names. */ @@ -42,22 +44,34 @@ extern int of_i8042_aux_irq; static inline int i8042_read_data(void) { - return inb(I8042_DATA_REG); + if (phytium_check_cpu() == true) + return base_ctrl_readb(I8042_DATA_REG); + else + return inb(I8042_DATA_REG); } static inline int i8042_read_status(void) { - return inb(I8042_STATUS_REG); + if (phytium_check_cpu() == true) + return base_ctrl_readb(I8042_STATUS_REG); + else + return inb(I8042_STATUS_REG); } static inline void i8042_write_data(int val) { - outb(val, I8042_DATA_REG); + if (phytium_check_cpu() == true) + base_ctrl_writeb(I8042_DATA_REG, val); + else + outb(val, I8042_DATA_REG); } static inline void i8042_write_command(int val) { - outb(val, I8042_COMMAND_REG); + if (phytium_check_cpu() == true) + base_ctrl_writeb(I8042_COMMAND_REG, val); + else + 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 29340f8095bb25e838e73767cab61701adfd5b6b..a821c241d2f15b617742bee9cae9e18236f25985 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -536,6 +536,9 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id) spin_lock_irqsave(&i8042_lock, flags); + if (phytium_check_cpu() == true) + base_ctrl_write_int_clear(0x0); + str = i8042_read_status(); if (unlikely(~str & I8042_STR_OBF)) { spin_unlock_irqrestore(&i8042_lock, flags); @@ -1457,7 +1460,7 @@ static int i8042_setup_aux(void) int error; int i; - if (i8042_check_aux()) + if (!phytium_check_cpu() && i8042_check_aux()) return -ENODEV; if (i8042_nomux || i8042_check_mux()) { @@ -1474,10 +1477,12 @@ static int i8042_setup_aux(void) aux_enable = i8042_enable_mux_ports; } - error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED, - "i8042", i8042_platform_device); - if (error) - goto err_free_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 = aux_enable(); if (error) @@ -1501,8 +1506,17 @@ static int i8042_setup_kbd(void) if (error) return error; - error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED, - "i8042", i8042_platform_device); + 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); + } if (error) goto err_free_port; @@ -1619,14 +1633,15 @@ static int __init i8042_init(void) dbg_init(); - err = i8042_platform_init(); - if (err) - return (err == -ENODEV) ? 0 : err; - - err = i8042_controller_check(); - if (err) - goto err_platform_exit; + if (!phytium_check_cpu()) { + err = i8042_platform_init(); + if (err) + return (err == -ENODEV) ? 0 : err; + err = i8042_controller_check(); + if (err) + goto err_platform_exit; + } /* Set this before creating the dev to allow i8042_command to work right away */ i8042_present = true; diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig index d57c5adf932e3676ee9b790c582fd2f4ca8118fe..237557f925e420d7a20145b136c6404fe223175c 100644 --- a/drivers/iommu/Kconfig +++ b/drivers/iommu/Kconfig @@ -461,6 +461,12 @@ config QCOM_IOMMU help Support for IOMMU on certain Qualcomm SoCs. +config SMMU_BYPASS_DEV + bool "SMMU bypass streams for some specific devices" + help + according smmu.bypassdev cmdline, SMMU performs attribute + transformation only,with no address translation. + config HYPERV_IOMMU bool "Hyper-V IRQ Handling" depends on HYPERV && X86 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 b02ea0fe0f42998176418ea3bfc795992a2d4c7a..847510eb3810b04a3a3bc50b8c306451ef253cb3 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -41,6 +41,42 @@ module_param(disable_msipolling, bool, 0444); MODULE_PARM_DESC(disable_msipolling, "Disable MSI-based polling for CMD_SYNC completion."); +#ifdef CONFIG_SMMU_BYPASS_DEV +struct smmu_bypass_device { + unsigned short vendor; + unsigned short device; +}; +#define MAX_CMDLINE_SMMU_BYPASS_DEV 16 + +static struct smmu_bypass_device smmu_bypass_devices[MAX_CMDLINE_SMMU_BYPASS_DEV]; +static int smmu_bypass_devices_num; + +static int __init arm_smmu_bypass_dev_setup(char *str) +{ + unsigned short vendor; + unsigned short device; + int ret; + + if (!str) + return -EINVAL; + + ret = sscanf(str, "%hx:%hx", &vendor, &device); + if (ret != 2) + return -EINVAL; + + if (smmu_bypass_devices_num >= MAX_CMDLINE_SMMU_BYPASS_DEV) + return -ERANGE; + + smmu_bypass_devices[smmu_bypass_devices_num].vendor = vendor; + smmu_bypass_devices[smmu_bypass_devices_num].device = device; + smmu_bypass_devices_num++; + + return 0; +} + +__setup("smmu.bypassdev=", arm_smmu_bypass_dev_setup); +#endif + enum arm_smmu_msi_index { EVTQ_MSI_INDEX, GERROR_MSI_INDEX, @@ -2859,6 +2895,30 @@ static void arm_smmu_remove_dev_pasid(struct device *dev, ioasid_t pasid) arm_smmu_sva_remove_dev_pasid(domain, dev, pasid); } +#ifdef CONFIG_SMMU_BYPASS_DEV +static int arm_smmu_device_domain_type(struct device *dev, unsigned int *type) +{ + int i; + struct pci_dev *pdev; + + if (!dev_is_pci(dev)) + return -ERANGE; + + pdev = to_pci_dev(dev); + for (i = 0; i < smmu_bypass_devices_num; i++) { + if ((smmu_bypass_devices[i].vendor == pdev->vendor) + && (smmu_bypass_devices[i].device == pdev->device)) { + dev_info(dev, "device 0x%x:0x%x uses identity mapping.", + pdev->vendor, pdev->device); + *type = IOMMU_DOMAIN_IDENTITY; + return 0; + } + } + + return -ERANGE; +} +#endif + static struct iommu_ops arm_smmu_ops = { .capable = arm_smmu_capable, .domain_alloc = arm_smmu_domain_alloc, @@ -2874,6 +2934,9 @@ static struct iommu_ops arm_smmu_ops = { .def_domain_type = arm_smmu_def_domain_type, .pgsize_bitmap = -1UL, /* Restricted during device attach */ .owner = THIS_MODULE, +#ifdef CONFIG_SMMU_BYPASS_DEV + .device_domain_type = arm_smmu_device_domain_type, +#endif .default_domain_ops = &(const struct iommu_domain_ops) { .attach_dev = arm_smmu_attach_dev, .map_pages = arm_smmu_map_pages, @@ -3003,12 +3066,59 @@ static int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu) return 0; } +#ifdef CONFIG_SMMU_BYPASS_DEV +static void arm_smmu_install_bypass_ste_for_dev(struct arm_smmu_device *smmu, + u32 sid) +{ + u64 val; + __le64 *step = arm_smmu_get_step_for_sid(smmu, sid); + + if (!step) + return; + + val = STRTAB_STE_0_V; + val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); + step[0] = cpu_to_le64(val); + step[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, + STRTAB_STE_1_SHCFG_INCOMING)); + step[2] = 0; +} + +static int arm_smmu_prepare_init_l2_strtab(struct device *dev, void *data) +{ + u32 sid; + int ret; + unsigned int type; + struct pci_dev *pdev; + struct arm_smmu_device *smmu = (struct arm_smmu_device *)data; + + if (arm_smmu_device_domain_type(dev, &type)) + return 0; + + pdev = to_pci_dev(dev); + sid = PCI_DEVID(pdev->bus->number, pdev->devfn); + if (!arm_smmu_sid_in_range(smmu, sid)) + return -ERANGE; + + ret = arm_smmu_init_l2_strtab(smmu, sid); + if (ret) + return ret; + + arm_smmu_install_bypass_ste_for_dev(smmu, sid); + + return 0; +} +#endif + static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu) { void *strtab; u64 reg; u32 size, l1size; struct arm_smmu_strtab_cfg *cfg = &smmu->strtab_cfg; +#ifdef CONFIG_SMMU_BYPASS_DEV + int ret; +#endif /* Calculate the L1 size, capped to the SIDSIZE. */ size = STRTAB_L1_SZ_SHIFT - (ilog2(STRTAB_L1_DESC_DWORDS) + 3); @@ -3038,7 +3148,19 @@ static int arm_smmu_init_strtab_2lvl(struct arm_smmu_device *smmu) reg |= FIELD_PREP(STRTAB_BASE_CFG_SPLIT, STRTAB_SPLIT); cfg->strtab_base_cfg = reg; +#ifdef CONFIG_SMMU_BYPASS_DEV + ret = arm_smmu_init_l1_strtab(smmu); + if (ret) + return ret; + + if (smmu_bypass_devices_num) { + ret = bus_for_each_dev(&pci_bus_type, NULL, (void *)smmu, + arm_smmu_prepare_init_l2_strtab); + } + return ret; +#else return arm_smmu_init_l1_strtab(smmu); +#endif } static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index a998f13adfb85e4fb3831f0331ab2557c3aee14e..1ea52c6ac2d07a8ee31b10a961de60928643cf61 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1773,14 +1773,30 @@ iommu_group_alloc_default_domain(struct iommu_group *group, int req_type) list_first_entry(&group->devices, struct group_device, list) ->dev->bus; struct iommu_domain *dom; +#ifdef CONFIG_SMMU_BYPASS_DEV + struct device *dev = + list_first_entry(&group->devices, struct group_device, list)->dev; + const struct iommu_ops *ops = dev_iommu_ops(dev); + unsigned int type = iommu_def_domain_type; +#endif lockdep_assert_held(&group->mutex); if (req_type) return __iommu_group_alloc_default_domain(bus, group, req_type); +#ifdef CONFIG_SMMU_BYPASS_DEV + /* direct allocate required default domain type for some specific devices. */ + if (ops->device_domain_type != NULL) { + if (ops->device_domain_type(dev, &type)) + type = iommu_def_domain_type; + } + + dom = __iommu_group_alloc_default_domain(bus, group, type); +#else /* The driver gave no guidance on what type to use, try the default */ dom = __iommu_group_alloc_default_domain(bus, group, iommu_def_domain_type); +#endif if (dom) return dom; diff --git a/drivers/media/platform/phytium/phytium_jpeg_core.c b/drivers/media/platform/phytium/phytium_jpeg_core.c index 014af0162c49f7e413f4a2b4f62aae77a1994671..ea4c7084df95b88afaf8e1f37aad2071b5f4afd1 100644 --- a/drivers/media/platform/phytium/phytium_jpeg_core.c +++ b/drivers/media/platform/phytium/phytium_jpeg_core.c @@ -71,7 +71,13 @@ static u32 phytium_jpeg_header[PHYTIUM_JPEG_HEADER_SIZE] = { static char yuv_mode_str[YUV_MODE_STR_LEN] = { "yuv444" }; module_param_string(yuv_mode, yuv_mode_str, sizeof(yuv_mode_str), 0444); -MODULE_PARM_DESC(yuv_mode, "Users select one mode from such modes as 'yuv444', or 'yuv422', or 'yuv420'. If no mode is set, the driver adapts defaults mode 'yuv444'."); +MODULE_PARM_DESC(yuv_mode, "Users select one mode from such modes as\n" + " \t\t'yuv444', or 'yuv422', or 'yuv420'. If no mode is set,\n" + " \t\tthe driver adapts defaults mode 'yuv444'."); + +/* The below global variables are used to filter same log-print lines */ +static bool first_invalid = true; +static bool cur_non_zero = true; static u32 phytium_jpeg_read(struct phytium_jpeg_dev *jpeg_dev, u32 reg) { @@ -166,18 +172,19 @@ static void phytium_jpeg_off(struct phytium_jpeg_dev *jpeg_dev) u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; - if (!test_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status)) { - dev_info(jpeg_dev->dev, "JPEG Engine is already off.\n"); - return; - } - /* disable all interrupt */ phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, disable_all_interrupt); /* clear all interrupt */ phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + /* disable JPEG engine */ phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, TRANSINFO_ENABLE_ENGINE, 0); + if (!test_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status)) { + dev_info(jpeg_dev->dev, "JPEG Engine is already off.\n"); + return; + } + clear_bit(VIDEO_CLOCKS_ON, &jpeg_dev->status); /* wait 50 ms */ mdelay(50); @@ -218,21 +225,27 @@ static void phytium_jpeg_get_resolution(struct phytium_jpeg_dev *jpeg_dev) if (width * height != 0) { detected_timings->width = width; detected_timings->height = height; + jpeg_dev->v4l2_input_status = 0; + cur_non_zero = true; + } else { + /* filter some repeated log-print lines */ + first_invalid = cur_non_zero; + cur_non_zero = false; } - jpeg_dev->v4l2_input_status = 0; - /* * Resolution is changed will trigger an interrupt, resolution detecting * also is disable during process interrupt. So re-enable. */ phytium_jpeg_enable_source_detecting(jpeg_dev); - dev_info(jpeg_dev->dev, "Change resolution: %uX%u\n", width, height); + + if (cur_non_zero == true || first_invalid == true) + dev_info(jpeg_dev->dev, "Change resolution: %uX%u\n", width, height); } static void phytium_jpeg_set_resolution(struct phytium_jpeg_dev *jpeg_dev) { - struct v4l2_bt_timings *active_timings = &jpeg_dev->active_timings; + struct v4l2_bt_timings *active_timings = &jpeg_dev->active_timings; int i; int src_addrs[OCM_BUF_NUM]; /* @@ -479,6 +492,9 @@ static int phytium_jpeg_query_dv_timings(struct file *file, void *priv, struct v4l2_dv_timings *timings) { int ret; + u32 source_info; + u32 width; + u32 height; struct phytium_jpeg_dev *jpeg_dev = video_drvdata(file); /* @@ -500,6 +516,15 @@ static int phytium_jpeg_query_dv_timings(struct file *file, void *priv, timings->type = V4L2_DV_BT_656_1120; timings->bt = jpeg_dev->detected_timings; + /* Get resolution from SRC_VGA_INFO_REG */ + source_info = phytium_jpeg_read(jpeg_dev, SRC_VGA_INFO_REG); + width = (source_info & SRC_HOR_PIXELS) >> SRC_WIDTH_SHIFT; + height = (source_info & SRC_VER_PIXELS) >> SRC_HEIGHT_SHIFT; + + /* Check if that the current resolution is zero. */ + if (width == 0 || height == 0) + jpeg_dev->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL; + return jpeg_dev->v4l2_input_status ? -ENOLINK : 0; } @@ -738,10 +763,11 @@ static int phytium_jpeg_start_frame(struct phytium_jpeg_dev *jpeg_dev) unsigned long status; struct phytium_jpeg_buffer *jpeg_buf; - if (jpeg_dev->v4l2_input_status) { - dev_err(jpeg_dev->dev, "No signal; needn't start frame\n"); + /* JPEG Engine shouldn't be enable to compress in the case no signal is input JPEG Engine. + * V4L2_IN_ST_NO_SIGNAL + */ + if (jpeg_dev->v4l2_input_status) return 0; - } spin_lock_irqsave(&jpeg_dev->hw_lock, status); jpeg_buf = list_first_entry_or_null(&jpeg_dev->buffers, @@ -855,7 +881,7 @@ static int phytium_jpeg_buf_prepare(struct vb2_buffer *vb) static inline struct phytium_jpeg_buffer * phytium_vb2buf_to_dstbuf(struct vb2_v4l2_buffer *buf) { - return container_of(buf, struct phytium_jpeg_buffer, vb); + return container_of(buf, struct phytium_jpeg_buffer, vb); } static void phytium_jpeg_buf_queue(struct vb2_buffer *vb) @@ -914,7 +940,12 @@ static irqreturn_t phytium_jpeg_irq(int irq, void *arg) u32 frame_size; if (test_bit(VIDEO_POWEROFF, &jpeg_dev->status)) { - dev_info(jpeg_dev->dev, "jpeg engine is requested to poweroff\n"); + dev_info(jpeg_dev->dev, "jpeg engine is requested to poweroff 0x%x\n", + phytium_jpeg_read(jpeg_dev, INT_STATUS_CTRL_REG)); + /* Disable interruption */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + /* clear all interruption of the hardware's buffers */ + phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, INT_JPEG_ENCODE_COMPLETE, 1); return IRQ_HANDLED; } @@ -1065,7 +1096,7 @@ static irqreturn_t phytium_jpeg_timer31_irq(int irq, void *arg) /* clear timer interrupt status */ writel(0x8, jpeg_dev->timer31_addr + 0x2c); - /* clear JPEG Engine's poweroff status */ + /* clear JPEG Engine's poweroff status */ clear_bit(VIDEO_POWEROFF, &jpeg_dev->status); dev_info(jpeg_dev->dev, "timer31 set jpeg status 0x%lx\n", jpeg_dev->status); @@ -1105,13 +1136,23 @@ static irqreturn_t phytium_jpeg_timer30_irq(int irq, void *arg) struct phytium_jpeg_dev *jpeg_dev = arg; struct arm_smccc_res res; + u32 disable_all_interrupt = 0; + u32 clear_all_interrupt = INT_FIFO_OVERFLOW | INT_OCM_BUF_OVERFLOW | + INT_JPEG_ENCODE_COMPLETE | INT_VIDEO_FORMAT_CHANGE; + /* disable timer interrupt */ writel(0, jpeg_dev->timer30_addr); /* clear timer interrupt status */ writel(0x8, jpeg_dev->timer30_addr + 0x2c); - /* Disable interruption */ - phytium_jpeg_update(jpeg_dev, INT_STATUS_CTRL_REG, STS_VE_JPEG_CODE_COMP_EN, 0); + /* disable all interrupts */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, disable_all_interrupt); + udelay(5); + /* clear all interrupts */ + phytium_jpeg_write(jpeg_dev, INT_STATUS_CTRL_REG, clear_all_interrupt); + + /* disable JPEG engine */ + phytium_jpeg_update(jpeg_dev, TRANSFORM_INFO_REG, 0, 0); /* call SE to poweroff JPEG Engine */ arm_smccc_smc(0xc300fff4, 0x9, 0x2, 0x80000020, 0, 0, 0, 0, &res); @@ -1201,12 +1242,44 @@ static int phytium_jpeg_init(struct phytium_jpeg_dev *jpeg_dev) } +/* The function is provided for user space adjusts the sampling mode. */ +static int phytium_jpeg_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct phytium_jpeg_dev *jpeg_dev = container_of(ctrl->handler, + struct phytium_jpeg_dev, + ctrl_handler); + if (ctrl->id != V4L2_CID_JPEG_CHROMA_SUBSAMPLING) + return -EINVAL; + + switch (ctrl->val) { + case V4L2_JPEG_CHROMA_SUBSAMPLING_420: + strscpy(yuv_mode_str, "yuv420", sizeof(yuv_mode_str)); + break; + case V4L2_JPEG_CHROMA_SUBSAMPLING_422: + strscpy(yuv_mode_str, "yuv422", sizeof(yuv_mode_str)); + break; + default: + strscpy(yuv_mode_str, "yuv444", sizeof(yuv_mode_str)); + } + phytium_jpeg_set_yuv_mode(jpeg_dev); + dev_info(jpeg_dev->dev, "current sample mode is %s\n", yuv_mode_str); + return 0; +} + +static const struct v4l2_ctrl_ops phytium_jpeg_ctrl_ops = { + .s_ctrl = phytium_jpeg_set_ctrl, +}; + + static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) { struct v4l2_device *v4l2_dev = &jpeg_dev->v4l2_dev; struct vb2_queue *dst_vq = &jpeg_dev->queue; struct video_device *vdev = &jpeg_dev->vdev; + const u64 mask = ~(BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_444) | + BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_422) | + BIT(V4L2_JPEG_CHROMA_SUBSAMPLING_420)); int ret; jpeg_dev->pix_fmt.pixelformat = V4L2_PIX_FMT_JPEG; @@ -1222,6 +1295,20 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) } /* Register how many v4l2 controls to a handler */ + v4l2_ctrl_handler_init(&jpeg_dev->ctrl_handler, 1); + v4l2_ctrl_new_std_menu(&jpeg_dev->ctrl_handler, &phytium_jpeg_ctrl_ops, + V4L2_CID_JPEG_CHROMA_SUBSAMPLING, + V4L2_JPEG_CHROMA_SUBSAMPLING_420, mask, + V4L2_JPEG_CHROMA_SUBSAMPLING_444); + + if (jpeg_dev->ctrl_handler.error) { + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); + dev_err(jpeg_dev->dev, "Failed to init v4l2 controls:%d\n", + jpeg_dev->ctrl_handler.error); + goto err_v4l2_register; + } + v4l2_dev->ctrl_handler = &jpeg_dev->ctrl_handler; + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF; dst_vq->dev = v4l2_dev->dev; @@ -1234,6 +1321,7 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) dst_vq->min_buffers_needed = CAPTURE_BUF_NUMBER; ret = vb2_queue_init(dst_vq); if (ret) { + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); dev_err(jpeg_dev->dev, "Failed to init vb2 queue\n"); goto err_v4l2_register; } @@ -1244,7 +1332,7 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) V4L2_CAP_STREAMING; vdev->v4l2_dev = v4l2_dev; strscpy(vdev->name, PHYTIUM_JPEG_NAME, sizeof(vdev->name)); - vdev->vfl_type = VFL_TYPE_VIDEO; + vdev->vfl_type = VFL_TYPE_VIDEO; /* The newest kernel using VFL_TYPE_VIDEO */ vdev->vfl_dir = VFL_DIR_RX; vdev->release = video_device_release_empty; vdev->ioctl_ops = &phytium_jpeg_ioctl_ops; @@ -1263,7 +1351,7 @@ static int phytium_jpeg_setup_video(struct phytium_jpeg_dev *jpeg_dev) err_video_register: vb2_queue_release(dst_vq); - + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); err_v4l2_register: v4l2_device_unregister(v4l2_dev); return ret; @@ -1352,10 +1440,14 @@ static int phytium_jpeg_remove(struct platform_device *pdev) phytium_jpeg_off(jpeg_dev); + phytium_jpeg_write(jpeg_dev, TRANSFORM_INFO_REG, 0); + video_unregister_device(&jpeg_dev->vdev); vb2_queue_release(&jpeg_dev->queue); + v4l2_ctrl_handler_free(&jpeg_dev->ctrl_handler); + v4l2_device_unregister(v4l2_dev); of_reserved_mem_device_release(dev); @@ -1375,5 +1467,6 @@ static struct platform_driver phytium_jpeg_driver = { module_platform_driver(phytium_jpeg_driver); MODULE_DESCRIPTION("Phytium JPEG Encoder driver"); +MODULE_VERSION(JPEG_DRIVER_VERSION); MODULE_AUTHOR("Wang Min "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/phytium/phytium_jpeg_core.h b/drivers/media/platform/phytium/phytium_jpeg_core.h index 27e32b2d3c435cc9c77250c1fbc36200cd2d5fa1..111cfefb979fb368bf82c07a038f40a24d07c401 100644 --- a/drivers/media/platform/phytium/phytium_jpeg_core.h +++ b/drivers/media/platform/phytium/phytium_jpeg_core.h @@ -36,6 +36,7 @@ #include #define PHYTIUM_JPEG_NAME "phytium-jpeg" +#define JPEG_DRIVER_VERSION "1.0.0" #define MAX_FRAME_RATE 60 #define MAX_HEIGHT 1080 #define MAX_WIDTH 1920 @@ -45,7 +46,7 @@ #define MAX_PIXEL_CLOCK (1920 * 1080 * 60) /* 1920 x 1080 x 60Hz */ #define SOURCE_RESOLUTION_DETECT_TIMEOUT msecs_to_jiffies(500) -#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(0) +#define RESOLUTION_CHANGE_DELAY msecs_to_jiffies(250) #define INVALID_RESOLUTION_DELAY msecs_to_jiffies(250) #define STOP_TIMEOUT msecs_to_jiffies(1000) @@ -128,6 +129,7 @@ struct phytium_jpeg_dev { unsigned int frame_rate; void __iomem *timer30_addr; void __iomem *timer31_addr; + struct v4l2_ctrl_handler ctrl_handler; }; struct phytium_jpeg_config { diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index cadd4a820c03364ae1c3afd32278dcdcace8cb61..a36a8868bb5bc165c0a963fc5f4f3367d287421b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -104,6 +104,17 @@ config PHANTOM If you choose to build module, its name will be phantom. If unsure, say N here. +config PHYTIUM_SNOOP_CTRL + tristate "Phytium snoop controller support" + depends on ARCH_PHYTIUM && REGMAP && MFD_SYSCON + default n + help + Say Y here if you want BMC to snoop 80h port. + + Provides a driver to control the phytium snoop interface which + allows the BMC to listen on and save the data written by the + host to an arbitrary I/O port. + config TIFM_CORE tristate "TI Flash Media interface support" depends on PCI diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index f2a4d1ff65d46a2a014b6e40ed737d26a68a25d0..16480d807ba21ea63acc7418e1669d4bad083469 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_LKDTM) += lkdtm/ obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o +obj-$(CONFIG_PHYTIUM_SNOOP_CTRL) += phytium-snoop-ctrl.o obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o diff --git a/drivers/misc/phytium-snoop-ctrl.c b/drivers/misc/phytium-snoop-ctrl.c new file mode 100644 index 0000000000000000000000000000000000000000..aedb15bbfa5f5c3abbe0815451853736c51d0475 --- /dev/null +++ b/drivers/misc/phytium-snoop-ctrl.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Provides a simple driver to control the PHYTIUM snoop ctrl interface which + * allows the BMC to listen on and save the data written by + * the host to an arbitrary I/O port. + * + * Typically used by the BMC to "watch" host boot progress via port + * 0x80 writes made by the BIOS during the boot process. + * + * Copyright (c) 2019-2023, Phytium Technology Co., Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "phytium-snoop-ctrl" + +#define NUM_SNOOP_CHANNELS 2 +#define SNOOP_FIFO_SIZE 2048 + +#define snp_enable_reg 0x150 +#define snp_enable_reg_snp1_en BIT(0) +#define snp_enable_reg_snp1_int_en BIT(1) +#define snp_enable_reg_snp2_en BIT(2) +#define snp_enable_reg_snp2_int_en BIT(3) + +#define snp_status_reg 0x154 +#define snp_status_reg_snp1_int BIT(0) +#define snp_status_reg_snp2_int BIT(1) + +#define snp_addr_reg 0x158 +#define snp_addr_reg_snp1_addr GENMASK(15, 0) +#define snp_addr_reg_snp1_shift 0 +#define snp_addr_reg_snp2_addr GENMASK(31, 16) +#define snp_addr_reg_snp2_shift 16 + +#define snp_data_reg 0x15c +#define snp_data_reg_snp1_data_reg GENMASK(7, 0) +#define snp_data_reg_snp1_shift 0 +#define snp_data_reg_snp2_data_reg GENMASK(15, 8) +#define snp_data_reg_snp2_shift 8 + +#define SNOOP_DRIVER_VERSION "1.1.1" + +struct phytium_snoop_ctrl_channel { + struct kfifo fifo; + wait_queue_head_t wq; + struct miscdevice miscdev; +}; + +struct phytium_snoop_ctrl { + struct regmap *regmap; + int irq; + struct phytium_snoop_ctrl_channel chan[NUM_SNOOP_CHANNELS]; +}; + +static struct phytium_snoop_ctrl_channel *snoop_file_to_chan(struct file *file) +{ + return container_of(file->private_data, + struct phytium_snoop_ctrl_channel, + miscdev); +} + +static ssize_t snoop_file_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct phytium_snoop_ctrl_channel *chan = snoop_file_to_chan(file); + unsigned int copied; + int ret = 0; + + if (kfifo_is_empty(&chan->fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(chan->wq, + !kfifo_is_empty(&chan->fifo)); + if (ret == -ERESTARTSYS) + return -EINTR; + } + ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); + + return ret ? ret : copied; +} + +static unsigned int snoop_file_poll(struct file *file, + struct poll_table_struct *pt) +{ + struct phytium_snoop_ctrl_channel *chan = snoop_file_to_chan(file); + + poll_wait(file, &chan->wq, pt); + return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0; +} + +static const struct file_operations snoop_fops = { + .owner = THIS_MODULE, + .read = snoop_file_read, + .poll = snoop_file_poll, + .llseek = noop_llseek, +}; + +/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ +static void put_fifo_with_discard(struct phytium_snoop_ctrl_channel *chan, u8 val) +{ + if (!kfifo_initialized(&chan->fifo)) + return; + if (kfifo_is_full(&chan->fifo)) + kfifo_skip(&chan->fifo); + kfifo_put(&chan->fifo, val); + wake_up_interruptible(&chan->wq); +} + +static irqreturn_t phytium_snoop_ctrl_irq(int irq, void *arg) +{ + struct phytium_snoop_ctrl *snoop_ctrl = arg; + u32 reg, data; + + if (regmap_read(snoop_ctrl->regmap, snp_status_reg, ®)) + return IRQ_NONE; + + /* Check if one of the snoop channels is interrupting */ + reg &= (snp_status_reg_snp1_int | snp_status_reg_snp2_int); + if (!reg) + return IRQ_NONE; + + /* Ack pending IRQs */ + regmap_write(snoop_ctrl->regmap, snp_status_reg, reg); + + /* Read and save most recent snoop'ed data byte to FIFO */ + regmap_read(snoop_ctrl->regmap, snp_data_reg, &data); + + if (reg & snp_status_reg_snp1_int) { + u8 val = (data & snp_data_reg_snp1_data_reg) >> snp_data_reg_snp1_shift; + + put_fifo_with_discard(&snoop_ctrl->chan[0], val); + } + if (reg & snp_status_reg_snp2_int) { + u8 val = (data & snp_data_reg_snp2_data_reg) >> snp_data_reg_snp2_shift; + + put_fifo_with_discard(&snoop_ctrl->chan[1], val); + } + + return IRQ_HANDLED; +} + +static int phytium_snoop_ctrl_config_irq(struct phytium_snoop_ctrl *snoop_ctrl, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + snoop_ctrl->irq = platform_get_irq(pdev, 0); + if (!snoop_ctrl->irq) + return -ENODEV; + + rc = devm_request_irq(dev, snoop_ctrl->irq, + phytium_snoop_ctrl_irq, IRQF_SHARED, + DEVICE_NAME, snoop_ctrl); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", snoop_ctrl->irq); + snoop_ctrl->irq = 0; + return rc; + } + + return 0; +} + +static int phytium_enable_snoop(struct phytium_snoop_ctrl *snoop_ctrl, + struct device *dev, + int channel, u16 snoop_port) +{ + int rc = 0; + u32 snp_enable_reg_en, snp_addr_reg_mask, snp_addr_reg_shift; + + init_waitqueue_head(&snoop_ctrl->chan[channel].wq); + /* Create FIFO datastructure */ + rc = kfifo_alloc(&snoop_ctrl->chan[channel].fifo, + SNOOP_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + + snoop_ctrl->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; + snoop_ctrl->chan[channel].miscdev.name = + devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); + snoop_ctrl->chan[channel].miscdev.fops = &snoop_fops; + snoop_ctrl->chan[channel].miscdev.parent = dev; + rc = misc_register(&snoop_ctrl->chan[channel].miscdev); + if (rc) + return rc; + + /* Enable snoop channel at requested port */ + switch (channel) { + case 0: + snp_enable_reg_en = snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en; + snp_addr_reg_mask = snp_addr_reg_snp1_addr; + snp_addr_reg_shift = snp_addr_reg_snp1_shift; + break; + case 1: + snp_enable_reg_en = snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en; + snp_addr_reg_mask = snp_addr_reg_snp2_addr; + snp_addr_reg_shift = snp_addr_reg_snp2_shift; + break; + default: + return -EINVAL; + } + + regmap_update_bits(snoop_ctrl->regmap, + snp_enable_reg, snp_enable_reg_en, snp_enable_reg_en); + regmap_update_bits(snoop_ctrl->regmap, snp_addr_reg, snp_addr_reg_mask, + snoop_port << snp_addr_reg_shift); + return rc; +} + +static void phytium_disable_snoop(struct phytium_snoop_ctrl *snoop_ctrl, + int channel) +{ + switch (channel) { + case 0: + regmap_update_bits(snoop_ctrl->regmap, snp_enable_reg, + snp_enable_reg_snp1_en | snp_enable_reg_snp1_int_en, + 0); + break; + case 1: + regmap_update_bits(snoop_ctrl->regmap, snp_enable_reg, + snp_enable_reg_snp2_en | snp_enable_reg_snp2_int_en, + 0); + break; + default: + return; + } + + kfifo_free(&snoop_ctrl->chan[channel].fifo); + misc_deregister(&snoop_ctrl->chan[channel].miscdev); +} + +static int phytium_snoop_ctrl_probe(struct platform_device *pdev) +{ + struct phytium_snoop_ctrl *snoop_ctrl; + struct device *dev; + u32 port; + int rc; + + dev = &pdev->dev; + + snoop_ctrl = devm_kzalloc(dev, sizeof(*snoop_ctrl), GFP_KERNEL); + if (!snoop_ctrl) + return -ENOMEM; + + snoop_ctrl->regmap = syscon_node_to_regmap( + pdev->dev.of_node); + if (IS_ERR(snoop_ctrl->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + dev_set_drvdata(&pdev->dev, snoop_ctrl); + + rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); + if (rc) { + dev_err(dev, "no snoop ports configured\n"); + return -ENODEV; + } + + rc = phytium_snoop_ctrl_config_irq(snoop_ctrl, pdev); + if (rc) + return rc; + + rc = phytium_enable_snoop(snoop_ctrl, dev, 0, port); + if (rc) + return rc; + + /* Configuration of 2nd snoop channel port is optional */ + if (of_property_read_u32_index(dev->of_node, "snoop-ports", + 1, &port) == 0) { + rc = phytium_enable_snoop(snoop_ctrl, dev, 1, port); + if (rc) + phytium_disable_snoop(snoop_ctrl, 0); + } + + return rc; +} + +static int phytium_snoop_ctrl_remove(struct platform_device *pdev) +{ + struct phytium_snoop_ctrl *snoop_ctrl = dev_get_drvdata(&pdev->dev); + + /* Disable both snoop channels */ + phytium_disable_snoop(snoop_ctrl, 0); + phytium_disable_snoop(snoop_ctrl, 1); + + return 0; +} + + +static const struct of_device_id phytium_snoop_ctrl_match[] = { + { .compatible = "phytium,snoop-ctrl"}, + { }, +}; + +static struct platform_driver phytium_snoop_ctrl_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = phytium_snoop_ctrl_match, + }, + .probe = phytium_snoop_ctrl_probe, + .remove = phytium_snoop_ctrl_remove, +}; + +module_platform_driver(phytium_snoop_ctrl_driver); + +MODULE_DEVICE_TABLE(of, phytium_snoop_ctrl_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lan Hengyu lanhengyu1395@phytium.com.cn"); +MODULE_DESCRIPTION("Driver to control Phytium snoop controller functionality"); +MODULE_VERSION(SNOOP_DRIVER_VERSION); diff --git a/drivers/mmc/host/phytium-mci.c b/drivers/mmc/host/phytium-mci.c index b511d893be2e3962cc3cd3843d2ab4075970f065..8be02664b49a111aa735631775fe6c8a5a80b020 100644 --- a/drivers/mmc/host/phytium-mci.c +++ b/drivers/mmc/host/phytium-mci.c @@ -44,7 +44,7 @@ static const u32 data_ints_mask = MCI_INT_MASK_DTO | MCI_INT_MASK_DCRC | MCI_INT MCI_INT_MASK_SBE_BCI; static const u32 cmd_err_ints_mask = MCI_INT_MASK_RTO | MCI_INT_MASK_RCRC | MCI_INT_MASK_RE | MCI_INT_MASK_DCRC | MCI_INT_MASK_DRTO | - MCI_MASKED_INTS_SBE_BCI; + MCI_MASKED_INTS_SBE_BCI | MCI_MASKED_INTS_EBE; static const u32 dmac_ints_mask = MCI_DMAC_INT_ENA_FBE | MCI_DMAC_INT_ENA_DU | MCI_DMAC_INT_ENA_NIS | MCI_DMAC_INT_ENA_AIS; @@ -62,6 +62,7 @@ static void phytium_mci_init_adma_table(struct phytium_mci_host *host, struct phytium_mci_dma *dma); static void phytium_mci_init_hw(struct phytium_mci_host *host); static int phytium_mci_get_cd(struct mmc_host *mmc); +static int phytium_mci_get_ro(struct mmc_host *mmc); static int phytium_mci_err_irq(struct phytium_mci_host *host, u32 dmac_events, u32 events); static void sdr_set_bits(void __iomem *reg, u32 bs) @@ -151,10 +152,17 @@ static void phytium_mci_send_cmd(struct phytium_mci_host *host, u32 cmd, u32 arg static void phytium_mci_update_cmd11(struct phytium_mci_host *host, u32 cmd) { + int rc; + u32 data; + writel(MCI_CMD_START | cmd, host->base + MCI_CMD); - while (readl(host->base + MCI_CMD) & MCI_CMD_START) - cpu_relax(); + rc = readl_relaxed_poll_timeout(host->base + MCI_CMD, + data, + !(data & MCI_CMD_START), + 0, 100 * 1000); + if (rc == -ETIMEDOUT) + pr_debug("%s %d, timeout mci_cmd: 0x%08x\n", __func__, __LINE__, data); } static void phytium_mci_set_clk(struct phytium_mci_host *host, struct mmc_ios *ios) @@ -179,7 +187,7 @@ static void phytium_mci_set_clk(struct phytium_mci_host *host, struct mmc_ios *i host->clk_rate, ios->clock); if (ios->clock >= 25000000) - tmp_ext_reg = 0x202; + tmp_ext_reg = 0x102; else if (ios->clock == 400000) tmp_ext_reg = 0x502; else @@ -227,8 +235,26 @@ static void phytium_mci_set_clk(struct phytium_mci_host *host, struct mmc_ios *i } } - dev_dbg(host->dev, "UHS_REG_EXT ext: %x, CLKDIV: %x\n", - readl(host->base + MCI_UHS_REG_EXT), readl(host->base + MCI_CLKDIV)); + if (!(readl(host->base + MCI_CLKDIV) & 0xff00) && + (ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_MMC_HS400 || + ios->timing == MMC_TIMING_UHS_DDR50)) { + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_CRC_SERIAL_DATA); + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_DRV_SHIFT_EN); + } else { + sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_CRC_SERIAL_DATA); + sdr_clr_bits(host->base + MCI_CNTRL, MCI_CNTRL_DRV_SHIFT_EN); + } + + if (div >= 2) + writel(((2 * (div & 0xff)) & 0xffff), host->base + MCI_CLK_DIVIDER); + + dev_dbg(host->dev, "UHS_REG_EXT ext: %x, CLKDIV: %x MCI_CLK_DIVIDER %x %x\n", + readl(host->base + MCI_UHS_REG_EXT), readl(host->base + MCI_CLKDIV), + readl(host->base + MCI_CLK_DIVIDER), ((2 * (div & 0xff)) & 0xffff)); + + if (cur_cmd_index == SD_SWITCH_VOLTAGE) + msleep(40); sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); @@ -1248,6 +1274,7 @@ static void phytium_mci_init_hw(struct phytium_mci_host *host) sdr_set_bits(host->base + MCI_CLKENA, MCI_CLKENA_CCLK_ENABLE); sdr_set_bits(host->base + MCI_UHS_REG_EXT, MCI_EXT_CLK_ENABLE); sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_VOLT); + sdr_clr_bits(host->base + MCI_EMMC_DDR_REG, MCI_EMMC_DDR_CYCLE); phytium_mci_reset_hw(host); @@ -1350,8 +1377,13 @@ static void phytium_mci_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct phytium_mci_host *host = mmc_priv(mmc); - if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50) + if (ios->timing == MMC_TIMING_MMC_DDR52 || + ios->timing == MMC_TIMING_MMC_HS400 || + ios->timing == MMC_TIMING_UHS_DDR50) { sdr_set_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_DDR); + sdr_set_bits(host->base + MCI_CNTRL, MCI_CNTRL_START_BIT_MODE); + sdr_clr_bits(host->base + MCI_EMMC_DDR_REG, MCI_EMMC_DDR_CYCLE); + } else sdr_clr_bits(host->base + MCI_UHS_REG, MCI_UHS_REG_DDR); @@ -1402,6 +1434,20 @@ static int phytium_mci_get_cd(struct mmc_host *mmc) return 1; } +static int phytium_mci_get_ro(struct mmc_host *mmc) +{ + struct phytium_mci_host *host = mmc_priv(mmc); + u32 status; + + status = readl(host->base + MCI_CARD_WRTPRT); + + dev_dbg(host->dev, "mci_get_ro status %d\n", status); + if ((status & 0x1) == 0x1) + return 1; + + return 0; +} + static int phytium_mci_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios) { struct phytium_mci_host *host = mmc_priv(mmc); @@ -1495,6 +1541,7 @@ static struct mmc_host_ops phytium_mci_ops = { .request = phytium_mci_ops_request, .set_ios = phytium_mci_ops_set_ios, .get_cd = phytium_mci_get_cd, + .get_ro = phytium_mci_get_ro, .enable_sdio_irq = phytium_mci_enable_sdio_irq, .ack_sdio_irq = phytium_mci_ack_sdio_irq, .card_busy = phytium_mci_card_busy, diff --git a/drivers/mmc/host/phytium-mci.h b/drivers/mmc/host/phytium-mci.h index 90f51b9ee135d7911d5beb100da51a896765dd87..9d3e86a4d5dfd4b01250237ec0bb8f040e25c8d7 100644 --- a/drivers/mmc/host/phytium-mci.h +++ b/drivers/mmc/host/phytium-mci.h @@ -104,6 +104,7 @@ #define MCI_UHS_REG_EXT 0x108 /* the UHS register extension */ #define MCI_EMMC_DDR_REG 0x10C /* the EMMC DDR reg */ #define MCI_ENABLE_SHIFT 0x110 /* the enable phase shift reg */ +#define MCI_CLK_DIVIDER 0x114 /* CLK DIVIDER */ #define MCI_DATA 0x200 /* the data FIFO access */ /* Command register defines */ @@ -132,12 +133,14 @@ #define MCI_CNTRL_CONTROLLER_RESET (0x1 << 0) /* RW */ #define MCI_CNTRL_FIFO_RESET (0x1 << 1) /* RW */ #define MCI_CNTRL_DMA_RESET (0x1 << 2) /* RW */ -#define MCI_CNTRL_RES (0x1 << 3) /* */ +#define MCI_CNTRL_DRV_SHIFT_EN (0x1 << 3) /* */ #define MCI_CNTRL_INT_ENABLE (0x1 << 4) /* RW */ #define MCI_CNTRL_DMA_ENABLE (0x1 << 5) /* RW */ #define MCI_CNTRL_READ_WAIT (0x1 << 6) /* RW */ #define MCI_CNTRL_SEND_IRQ_RESPONSE (0x1 << 7) /* RW */ #define MCI_CNTRL_ABORT_READ_DATA (0x1 << 8) /* RW */ +#define MCI_CNTRL_START_BIT_MODE (0x1 << 9) /* RW */ +#define MCI_CNTRL_CRC_SERIAL_DATA (0x1 << 10) /* RW */ #define MCI_CNTRL_ENDIAN (0x1 << 11) /* RW */ //#define MCI_CNTRL_CARD_VOLTAGE_A (0xF << 16) /* RW */ //#define MCI_CNTRL_CARD_VOLTAGE_B (0xF << 20) /* RW */ diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index e098ae937ce88a4deb7e1f3fee659146f4661358..e910b432f7787ac5be6055f4bfe997b702050042 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -75,6 +75,15 @@ config MTD_PHYSMAP_OF physically into the CPU's memory. The mapping description here is taken from OF device tree. +config MTD_PHYSMAP_ACPI + bool "Memory device in physical memory map based on ACPI description" + depends on MTD_PHYSMAP + help + This provides a 'mapping' driver which allows the NOR Flash, ROM + and RAM driver code to communicate with chips which are mapped + physically into the CPU's memory. The mapping description here is + taken from ACPI table. + config MTD_PHYSMAP_BT1_ROM bool "Baikal-T1 Boot ROMs OF-based physical memory map handling" depends on MTD_PHYSMAP_OF diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c index 9be71b045b259e258ce9fe4651ac8198c38fbbdb..4ceff03d3b5fe47dcb41f48b28016486daa14f5f 100644 --- a/drivers/mtd/maps/physmap-core.c +++ b/drivers/mtd/maps/physmap-core.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "physmap-bt1-rom.h" #include "physmap-gemini.h" @@ -409,12 +410,22 @@ static int physmap_flash_of_init(struct platform_device *dev) } #endif /* IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) */ +#if IS_ENABLED(CONFIG_MTD_PHYSMAP_ACPI) +static const struct acpi_device_id physmap_flash_ids[] = { + { "ACPI0018" }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, physmap_flash_ids); +#else /* IS_ENABLED(CONFIG_MTD_PHYSMAP_ACPI) */ +#define physmap_flash_ids NULL +#endif /* IS_ENABLED(CONFIG_MTD_PHYSMAP_ACPI) */ + static const char * const rom_probe_types[] = { "cfi_probe", "jedec_probe", "qinfo_probe", "map_rom", }; static const char * const part_probe_types[] = { - "cmdlinepart", "RedBoot", "afs", NULL + "cmdlinepart", "RedBoot", "afs", "acpipart", NULL }; static int physmap_flash_pdata_init(struct platform_device *dev) @@ -424,6 +435,18 @@ static int physmap_flash_pdata_init(struct platform_device *dev) unsigned int i; int err; +#ifdef CONFIG_MTD_PHYSMAP_ACPI + u32 bankwidth; + + fwnode_property_read_u32(dev->dev.fwnode, "bank-width", &bankwidth); + + struct physmap_flash_data acpi_data = { + .width = bankwidth, + }; + + dev->dev.platform_data = &acpi_data; +#endif + physmap_data = dev_get_platdata(&dev->dev); if (!physmap_data) return -EINVAL; @@ -454,7 +477,7 @@ static int physmap_flash_probe(struct platform_device *dev) int err = 0; int i; - if (!dev->dev.of_node && !dev_get_platdata(&dev->dev)) + if (!dev->dev.of_node && !dev->dev.fwnode && !dev_get_platdata(&dev->dev)) return -EINVAL; info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL); @@ -598,6 +621,9 @@ static int physmap_flash_probe(struct platform_device *dev) spin_lock_init(&info->vpp_lock); mtd_set_of_node(info->cmtd, dev->dev.of_node); + + mtd_set_fwnode(info->cmtd, dev->dev.fwnode); + err = mtd_device_parse_register(info->cmtd, info->part_types, NULL, info->parts, info->nparts); if (err) @@ -631,6 +657,7 @@ static struct platform_driver physmap_flash_driver = { .driver = { .name = "physmap-flash", .of_match_table = of_flash_match, + .acpi_match_table = physmap_flash_ids, }, }; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 17a44b31c05f5ccab4d7cbefdbc6b78909cc2e61..2dc8e4ca42fad889b6b5f740662629f34bd5c30e 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -583,7 +583,7 @@ static int mtd_part_acpi_parse(struct mtd_info *master, struct mtd_part_parser *parser; struct acpi_device *adev; struct fwnode_handle *child; - const char *compat = NULL; + const char *compat = ""; const char *fixed = "acpi-partitions"; int ret, err = 0; int compare = 1; @@ -597,6 +597,7 @@ static int mtd_part_acpi_parse(struct mtd_info *master, compare = strcmp(compat, fixed); } + //all child node device_for_each_child_node(dev, child) { if (compat && !compare) { parser = mtd_part_parser_get(fixed); diff --git a/drivers/mtd/nand/raw/phytium_nand.c b/drivers/mtd/nand/raw/phytium_nand.c index 2763455f3739bba4188e5fafeeeae61b8f2b61a3..efb6064df51c72d5806c13c8dcf9af61be670659 100644 --- a/drivers/mtd/nand/raw/phytium_nand.c +++ b/drivers/mtd/nand/raw/phytium_nand.c @@ -1271,8 +1271,10 @@ static int phytium_nand_page_read_hwecc(struct mtd_info *mtd, struct nand_chip * cond_delay(nfc_op->cle_ale_delay_ns); ret = phytium_nfc_wait_op(chip, nfc_op->rdy_timeout_ms); - if (ret) + if (ret) { + kfree(nfc_op); return ret; + } cond_delay(nfc_op->rdy_delay_ns * 1000); @@ -1483,6 +1485,7 @@ static int phytium_nand_page_write_hwecc(struct mtd_info *mtd, struct nand_chip cond_delay(nfc_op->rdy_delay_ns * 1000); out: + kfree(nfc_op); return ret; } @@ -2022,6 +2025,8 @@ int phytium_nand_init(struct phytium_nfc *nfc) nfc->controller.ops = &phytium_nand_controller_ops; INIT_LIST_HEAD(&nfc->chips); + spin_lock_init(&nfc->spinlock); + /* Init the controller and then probe the chips */ ret = phytium_nfc_init(nfc); if (ret) @@ -2031,8 +2036,6 @@ int phytium_nand_init(struct phytium_nfc *nfc) if (ret) goto out; - spin_lock_init(&nfc->spinlock); - out: return ret; } diff --git a/drivers/mtd/nand/raw/phytium_nand_pci.c b/drivers/mtd/nand/raw/phytium_nand_pci.c index 62e64adea5454fcd1472b65829549f749a7c73e3..a24a37d6e8e9c5764150488ec2a31b3b97ff0377 100644 --- a/drivers/mtd/nand/raw/phytium_nand_pci.c +++ b/drivers/mtd/nand/raw/phytium_nand_pci.c @@ -11,6 +11,7 @@ #include "phytium_nand.h" #define DRV_NAME "phytium_nand_pci" +#define DRV_VERSION "1.0.0" static struct mtd_partition partition_info[] = { { @@ -148,3 +149,4 @@ module_pci_driver(phytium_pci_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("PCI driver for Phytium NAND controller"); MODULE_AUTHOR("Zhu Mingshuai "); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/mtd/nand/raw/phytium_nand_plat.c b/drivers/mtd/nand/raw/phytium_nand_plat.c index 411a53a667c96a24c30ff29509e37c34a36dc213..5e0bd67c8a3338b5cfa929865c9d33808c0f52de 100644 --- a/drivers/mtd/nand/raw/phytium_nand_plat.c +++ b/drivers/mtd/nand/raw/phytium_nand_plat.c @@ -20,6 +20,7 @@ #include "phytium_nand.h" #define DRV_NAME "phytium_nand_plat" +#define DRV_VERSION "1.0.0" static int phytium_nfc_plat_probe(struct platform_device *pdev) { @@ -136,3 +137,4 @@ module_platform_driver(phytium_nfc_plat_driver) MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium NAND controller Platform driver"); MODULE_AUTHOR("Zhu Mingshuai "); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/mtd/parsers/acpipart_core.c b/drivers/mtd/parsers/acpipart_core.c index dde80407a60583764d3a86b87587904a3f44d006..06060738a353f3dc9f9ed04cde06c9e309fe3134 100644 --- a/drivers/mtd/parsers/acpipart_core.c +++ b/drivers/mtd/parsers/acpipart_core.c @@ -118,7 +118,7 @@ MODULE_DEVICE_TABLE(acpi, parse_acpipart_match_table); static struct mtd_part_parser acpipart_parser = { .parse_fn = parse_acpi_fixed_partitions, - .name = "acpi-fixed-partitions", + .name = "acpi-partitions", .acpi_match_table = ACPI_PTR(parse_acpipart_match_table), }; diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 0810e27d986788f3452b5ff33cbd5bc027bc0955..fcca6ce65fbeafd0f42f22778794cafcbe49c268 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -2791,6 +2791,10 @@ static void spi_nor_no_sfdp_init_params(struct spi_nor *nor) spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_4], 0, 8, SPINOR_OP_READ_1_1_4, SNOR_PROTO_1_1_4); + params->hwcaps.mask |= SNOR_HWCAPS_READ_1_4_4; + spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_4_4], + 0, 8, SPINOR_OP_READ_1_4_4, + SNOR_PROTO_1_4_4); } if (no_sfdp_flags & SPI_NOR_OCTAL_READ) { diff --git a/drivers/net/can/phytium/phytium_can.c b/drivers/net/can/phytium/phytium_can.c index 52564c4aaa60e3be438e5754fc0e49487a67bf0b..8f7cf64a9c9ee14f64cba00a10e5018a2a44233a 100644 --- a/drivers/net/can/phytium/phytium_can.c +++ b/drivers/net/can/phytium/phytium_can.c @@ -814,18 +814,18 @@ static int phytium_can_set_bittiming(struct net_device *dev) /* Setting Time Segment 1 in BTR Register */ btr |= (bt->prop_seg - 1) << 2; - btr |= (bt->phase_seg1 - 1) << 5; + btr |= (bt->phase_seg1 - 1) << 8; /* Setting Time Segment 2 in BTR Register */ - btr |= (bt->phase_seg2 - 1) << 8; + btr |= (bt->phase_seg2 - 1) << 5; /* Setting Synchronous jump width in BTR Register */ btr |= (bt->sjw - 1); dbtr = (dbt->brp - 1) << 16; dbtr |= (dbt->prop_seg - 1) << 2; - dbtr |= (dbt->phase_seg1 - 1) << 5; - dbtr |= (dbt->phase_seg2 - 1) << 8; + dbtr |= (dbt->phase_seg1 - 1) << 8; + dbtr |= (dbt->phase_seg2 - 1) << 5; dbtr |= (dbt->sjw - 1); if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 8a677582efeeb483bb100036e5df64d16dc66505..18e28ac80f5f613df533591890e1e0905cef44c8 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -57,7 +57,7 @@ struct sifive_fu540_macb_mgmt { #define RX_BUFFER_MULTIPLE 64 /* bytes */ #define DEFAULT_RX_RING_SIZE 512 /* must be power of 2 */ -#define MIN_RX_RING_SIZE 64 +#define MIN_RX_RING_SIZE 128 #define MAX_RX_RING_SIZE 8192 #define RX_RING_BYTES(bp) (macb_dma_desc_get_size(bp) \ * (bp)->rx_ring_size) @@ -112,6 +112,8 @@ struct sifive_fu540_macb_mgmt { static void macb_tx_unmap(struct macb *bp, struct macb_tx_skb *tx_skb, int budget); +static void macb_set_addr(struct macb *bp, struct macb_dma_desc *desc, + dma_addr_t addr); /* DMA buffer descriptor might be different size * depends on hardware configuration: @@ -744,6 +746,7 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, struct macb *bp = netdev_priv(ndev); struct macb_tx_skb *tx_skb; struct macb_queue *queue; + struct macb_dma_desc *tx_desc = NULL; unsigned int q; u32 ctrl; int i; @@ -763,12 +766,18 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, /* Tx clean */ for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + spin_lock(&queue->tx_ptr_lock); for (i = 0; i < bp->tx_ring_size; i++) { tx_skb = macb_tx_skb(queue, i); /* free unsent skb buffers */ if (tx_skb) macb_tx_unmap(bp, tx_skb, 0); + + tx_desc = macb_tx_desc(queue, i); + macb_set_addr(bp, tx_desc, 0); + tx_desc->ctrl &= ~MACB_BIT(TX_USED); } + spin_unlock(&queue->tx_ptr_lock); } netif_tx_stop_all_queues(ndev); @@ -809,7 +818,21 @@ static void phytium_gem1p0_sel_clk(struct macb *bp, int speed) gem_writel(bp, RX_CLK_SEL3_0, 0x0); /*0x1c78*/ gem_writel(bp, RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ } else if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { - if (speed == SPEED_1000) { + if (speed == SPEED_2500) { + gem_writel(bp, DIV_SEL0_LN, 0x1); /*0x1c08*/ + gem_writel(bp, DIV_SEL1_LN, 0x2); /*0x1c0c*/ + gem_writel(bp, PMA_XCVR_POWER_STATE, 0x1); /*0x1c10*/ + gem_writel(bp, TX_CLK_SEL0, 0x0); /*0x1c20*/ + gem_writel(bp, TX_CLK_SEL1, 0x1); /*0x1c24*/ + gem_writel(bp, TX_CLK_SEL2, 0x1); /*0x1c28*/ + gem_writel(bp, TX_CLK_SEL3, 0x1); /*0x1c2c*/ + gem_writel(bp, RX_CLK_SEL0, 0x1); /*0x1c30*/ + gem_writel(bp, RX_CLK_SEL1, 0x0); /*0x1c34*/ + gem_writel(bp, TX_CLK_SEL3_0, 0x0); /*0x1c70*/ + gem_writel(bp, TX_CLK_SEL4_0, 0x0); /*0x1c74*/ + gem_writel(bp, RX_CLK_SEL3_0, 0x0); /*0x1c78*/ + gem_writel(bp, RX_CLK_SEL4_0, 0x0); /*0x1c7c*/ + } else if (speed == SPEED_1000) { gem_writel(bp, SRC_SEL_LN, 0x1); /*0x1c04*/ gem_writel(bp, DIV_SEL0_LN, 0x4); /*0x1c08*/ gem_writel(bp, DIV_SEL1_LN, 0x8); /*0x1c0c*/ @@ -981,10 +1004,16 @@ static void macb_mac_link_up(struct phylink_config *config, if (speed == SPEED_2500) { u32 network_ctrl; + u32 pcsctrl, old_pcsctrl; network_ctrl = macb_readl(bp, NCR); network_ctrl |= MACB_BIT(2PT5G); macb_writel(bp, NCR, network_ctrl); + + old_pcsctrl = gem_readl(bp, PCSCNTRL); + pcsctrl = old_pcsctrl & ~GEM_BIT(PCSAUTONEG); + if (old_pcsctrl != pcsctrl) + gem_writel(bp, PCSCNTRL, pcsctrl); } if (bp->phy_interface == PHY_INTERFACE_MODE_10GBASER || @@ -1127,11 +1156,11 @@ static int macb_mii_probe(struct net_device *dev) bp->phylink_config.type = PHYLINK_NETDEV; bp->phylink_config.mac_managed_pm = true; - if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII) { + if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII || + bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { bp->phylink_config.poll_fixed_state = true; bp->phylink_config.get_fixed_state = macb_get_pcs_fixed_state; - } else if (bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX || - bp->phy_interface == PHY_INTERFACE_MODE_USXGMII) { + } else if (bp->phy_interface == PHY_INTERFACE_MODE_USXGMII) { bp->phylink_config.poll_fixed_state = true; bp->phylink_config.get_fixed_state = macb_get_usx_pcs_fixed_state; } @@ -1167,12 +1196,6 @@ static int macb_mii_probe(struct net_device *dev) } } - if (bp->phy_interface == PHY_INTERFACE_MODE_SGMII || - bp->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - bp->phylink_config.poll_fixed_state = true; - bp->phylink_config.get_fixed_state = macb_get_pcs_fixed_state; - } - bp->phylink = phylink_create(&bp->phylink_config, bp->pdev->dev.fwnode, bp->phy_interface, &macb_phylink_ops); if (IS_ERR(bp->phylink)) { @@ -3191,6 +3214,10 @@ static void macb_init_hw(struct macb *bp) macb_reset_hw(bp); macb_set_hwaddr(bp); + config = macb_readl(bp, NCR); + config |= MACB_BIT(MPE); + macb_writel(bp, NCR, config); + config = macb_mdc_clk_div(bp); config |= MACB_BF(RBOF, NET_IP_ALIGN); /* Make eth data aligned */ config |= MACB_BIT(DRFCS); /* Discard Rx FCS */ @@ -5846,8 +5873,16 @@ static int macb_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); if (!pm_runtime_suspended(&pdev->dev)) { - macb_clks_disable(bp->pclk, bp->hclk, bp->tx_clk, - bp->rx_clk, bp->tsu_clk); + if (__clk_is_enabled(bp->pclk)) + clk_disable_unprepare(bp->pclk); + if (__clk_is_enabled(bp->hclk)) + clk_disable_unprepare(bp->hclk); + if (__clk_is_enabled(bp->tx_clk)) + clk_disable_unprepare(bp->tx_clk); + if (__clk_is_enabled(bp->rx_clk)) + clk_disable_unprepare(bp->rx_clk); + if (__clk_is_enabled(bp->tsu_clk)) + clk_disable_unprepare(bp->tsu_clk); pm_runtime_set_suspended(&pdev->dev); } phylink_destroy(bp->phylink); @@ -6021,9 +6056,6 @@ static int __maybe_unused macb_resume(struct device *dev) macb_set_rx_mode(netdev); macb_restore_features(bp); rtnl_lock(); - if (!device_may_wakeup(&bp->dev->dev)) - phy_init(bp->sgmii_phy); - phylink_start(bp->phylink); rtnl_unlock(); @@ -6039,10 +6071,21 @@ static int __maybe_unused macb_runtime_suspend(struct device *dev) struct net_device *netdev = dev_get_drvdata(dev); struct macb *bp = netdev_priv(netdev); - if (!(device_may_wakeup(dev))) - macb_clks_disable(bp->pclk, bp->hclk, bp->tx_clk, bp->rx_clk, bp->tsu_clk); - else if (!(bp->caps & MACB_CAPS_NEED_TSUCLK)) - macb_clks_disable(NULL, NULL, NULL, NULL, bp->tsu_clk); + if (!(device_may_wakeup(dev))) { + if (__clk_is_enabled(bp->pclk)) + clk_disable_unprepare(bp->pclk); + if (__clk_is_enabled(bp->hclk)) + clk_disable_unprepare(bp->hclk); + if (__clk_is_enabled(bp->tx_clk)) + clk_disable_unprepare(bp->tx_clk); + if (__clk_is_enabled(bp->rx_clk)) + clk_disable_unprepare(bp->rx_clk); + if (__clk_is_enabled(bp->tsu_clk)) + clk_disable_unprepare(bp->tsu_clk); + } else if (!(bp->caps & MACB_CAPS_NEED_TSUCLK)) { + if (__clk_is_enabled(bp->tsu_clk)) + clk_disable_unprepare(bp->tsu_clk); + } return 0; } diff --git a/drivers/net/ethernet/intel/ice/ice_lag.c b/drivers/net/ethernet/intel/ice/ice_lag.c index 4e675c7c199fa1f41b339144651250ce33759e67..b460ea4f5fc8f39b05c3c077a372a19fcd608fec 100644 --- a/drivers/net/ethernet/intel/ice/ice_lag.c +++ b/drivers/net/ethernet/intel/ice/ice_lag.c @@ -1806,9 +1806,7 @@ static int ice_create_lag_recipe(struct ice_hw *hw, u16 *rid, new_rcp->content.act_ctrl_fwd_priority = prio; new_rcp->content.rid = *rid | ICE_AQ_RECIPE_ID_IS_ROOT; new_rcp->recipe_indx = *rid; - bitmap_zero((unsigned long *)new_rcp->recipe_bitmap, - ICE_MAX_NUM_RECIPES); - set_bit(*rid, (unsigned long *)new_rcp->recipe_bitmap); + put_unaligned_le64(BIT_ULL(*rid), new_rcp->recipe_bitmap); err = ice_aq_add_recipe(hw, new_rcp, 1, NULL); if (err) diff --git a/drivers/net/ethernet/phytium/phytmac.h b/drivers/net/ethernet/phytium/phytmac.h index b90e1551499ef77b1a228d3adf29327ad3be7ae5..df4ec49a0851222571c52642bc1d282bc8e4ce8d 100644 --- a/drivers/net/ethernet/phytium/phytmac.h +++ b/drivers/net/ethernet/phytium/phytmac.h @@ -13,7 +13,7 @@ #define PHYTMAC_DRV_NAME "phytium-mac" #define PHYTMAC_DRV_DESC "PHYTIUM Ethernet Driver" - +#define PHYTMAC_DRIVER_VERSION "1.0.5" #define PHYTMAC_DEFAULT_MSG_ENABLE \ (NETIF_MSG_DRV | \ NETIF_MSG_PROBE | \ @@ -38,7 +38,6 @@ #define MIN_TX_RING_SIZE 64 #define MIN_RX_RING_SIZE 64 #define DEFAULT_TX_DESC_MIN_FREE 64 -#define DEFAULT_RX_DESC_MIN_FREE 64 #define MEMORY_SIZE 4096 #define MHU_SIZE 0x20 @@ -308,6 +307,13 @@ struct phytmac_tx_ts { u32 ts_2; }; +struct phytmac_rx_buffer { + dma_addr_t addr; + struct page *page; + __u16 page_offset; + __u16 pagecnt_bias; +}; + struct phytmac_queue { struct phytmac *pdata; int irq; @@ -329,8 +335,9 @@ struct phytmac_queue { dma_addr_t rx_ring_addr; unsigned int rx_head; unsigned int rx_tail; + unsigned int rx_next_to_alloc; struct phytmac_dma_desc *rx_ring; - struct sk_buff **rx_skb; + struct phytmac_rx_buffer *rx_buffer_info; struct napi_struct rx_napi; struct phytmac_queue_stats stats; @@ -499,18 +506,17 @@ struct phytmac_hw_if { struct packet_info *packet); void (*init_rx_map)(struct phytmac_queue *queue, u32 index); unsigned int (*rx_map)(struct phytmac_queue *queue, u32 index, dma_addr_t addr); - unsigned int (*rx_clean)(struct phytmac_queue *queue, u32 cleaned_count); void (*transmit)(struct phytmac_queue *queue); void (*restart)(struct phytmac *pdata); int (*tx_complete)(const struct phytmac_dma_desc *desc); - int (*rx_complete)(const struct phytmac_dma_desc *desc); + bool (*rx_complete)(const struct phytmac_dma_desc *desc); int (*get_rx_pkt_len)(struct phytmac *pdata, const struct phytmac_dma_desc *desc); - dma_addr_t (*get_desc_addr)(const struct phytmac_dma_desc *desc); bool (*rx_checksum)(const struct phytmac_dma_desc *desc); void (*set_desc_rxused)(struct phytmac_dma_desc *desc); bool (*rx_single_buffer)(const struct phytmac_dma_desc *desc); bool (*rx_pkt_start)(const struct phytmac_dma_desc *desc); bool (*rx_pkt_end)(const struct phytmac_dma_desc *desc); + unsigned int (*zero_rx_desc_addr)(struct phytmac_dma_desc *desc); void (*clear_rx_desc)(struct phytmac_queue *queue, int begin, int end); void (*clear_tx_desc)(struct phytmac_queue *queue); /* ptp */ @@ -574,6 +580,19 @@ struct phytmac_hw_if { #define PHYTMAC_READ_DATA0(pdata) PHYTMAC_MHU_READ(pdata, PHYTMAC_MHU_CPP_DATA0) #define PHYTMAC_TIMEOUT 1000000000 /* in usecs */ +#define PHYTMAC_GFP_FLAGS \ + (GFP_ATOMIC | __GFP_NOWARN | GFP_DMA | __GFP_DMA32) +#define PHYTMAC_RX_DMA_ATTR \ + (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING) +#define PHYTMAC_SKB_PAD (NET_SKB_PAD) + +#define PHYTMAC_RXBUFFER_2048 2048 +#define PHYTMAC_MAX_FRAME_BUILD_SKB \ + (SKB_WITH_OVERHEAD(PHYTMAC_RXBUFFER_2048) - PHYTMAC_SKB_PAD) + +#define PHYTMAC_RX_PAGE_ORDER 0 +#define PHYTMAC_RX_PAGE_SIZE (PAGE_SIZE << PHYTMAC_RX_PAGE_ORDER) + struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, unsigned int index); inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, @@ -588,4 +607,5 @@ int phytmac_drv_resume(struct phytmac *pdata); struct phytmac *phytmac_alloc_pdata(struct device *dev); void phytmac_free_pdata(struct phytmac *pdata); int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size); +void phytmac_set_bios_wol_enable(struct phytmac *pdata, u32 wol); #endif diff --git a/drivers/net/ethernet/phytium/phytmac_ethtool.c b/drivers/net/ethernet/phytium/phytmac_ethtool.c index 592d2d9dc6d4b27b87594738e5ce2ab09ce20ded..e1698fa10b0955c0983bdd861427b740f2ff0f2e 100644 --- a/drivers/net/ethernet/phytium/phytmac_ethtool.c +++ b/drivers/net/ethernet/phytium/phytmac_ethtool.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include "phytmac.h" #include "phytmac_v1.h" #include "phytmac_v2.h" @@ -87,23 +89,23 @@ static void phytmac_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol { struct phytmac *pdata = netdev_priv(ndev); + wol->wolopts = 0; phylink_ethtool_get_wol(pdata->phylink, wol); + wol->supported = WAKE_MAGIC | WAKE_ARP | + WAKE_UCAST | WAKE_MCAST; + if (pdata->wol & PHYTMAC_WAKE_MAGIC) { wol->wolopts |= WAKE_MAGIC; - wol->supported |= WAKE_MAGIC; } if (pdata->wol & PHYTMAC_WAKE_ARP) { wol->wolopts |= WAKE_ARP; - wol->supported |= WAKE_ARP; } if (pdata->wol & PHYTMAC_WAKE_UCAST) { wol->wolopts |= WAKE_UCAST; - wol->supported |= WAKE_UCAST; } if (pdata->wol & PHYTMAC_WAKE_MCAST) { wol->wolopts |= WAKE_MCAST; - wol->supported |= WAKE_MCAST; } } @@ -114,7 +116,8 @@ static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) ret = phylink_ethtool_set_wol(pdata->phylink, wol); - if (!ret || ret != -EOPNOTSUPP) + /* Don't manage WoL on MAC, if PHY set_wol() fails */ + if (ret && ret != -EOPNOTSUPP) return ret; pdata->wol = 0; @@ -129,6 +132,7 @@ static int phytmac_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) pdata->wol |= PHYTMAC_WAKE_MCAST; device_set_wakeup_enable(pdata->dev, pdata->wol ? 1 : 0); + phytmac_set_bios_wol_enable(pdata, pdata->wol ? 1 : 0); return 0; } @@ -382,43 +386,32 @@ static int phytmac_get_link_ksettings(struct net_device *ndev, u32 advertising = 0; if (!ndev->phydev) { + kset->base.port = PORT_FIBRE; + kset->base.transceiver = XCVR_INTERNAL; + kset->base.duplex = pdata->duplex; + kset->base.speed = pdata->speed; + if (pdata->phy_interface == PHY_INTERFACE_MODE_USXGMII || pdata->phy_interface == PHY_INTERFACE_MODE_10GBASER) { supported = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; advertising = ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_10000; } else if (pdata->phy_interface == PHY_INTERFACE_MODE_2500BASEX) { - supported = SUPPORTED_2500baseX_Full | SUPPORTED_Pause; - advertising = ADVERTISED_2500baseX_Full | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_2500; + supported = SUPPORTED_2500baseX_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_2500baseX_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; } else if (pdata->phy_interface == PHY_INTERFACE_MODE_1000BASEX) { - supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full - | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE - | SUPPORTED_Pause; - advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full - | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE - | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_100; + supported = SUPPORTED_1000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; } else if (pdata->phy_interface == PHY_INTERFACE_MODE_SGMII) { - supported = SUPPORTED_1000baseT_Full | SUPPORTED_100baseT_Full - | SUPPORTED_10baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause; - advertising = ADVERTISED_1000baseT_Full | ADVERTISED_100baseT_Full - | ADVERTISED_10baseT_Full | ADVERTISED_FIBRE | ADVERTISED_Pause; - kset->base.port = PORT_FIBRE; - kset->base.transceiver = XCVR_INTERNAL; - kset->base.duplex = DUPLEX_FULL; - kset->base.speed = SPEED_1000; + supported = SUPPORTED_1000baseT_Full + | SUPPORTED_FIBRE | SUPPORTED_Pause; + advertising = ADVERTISED_1000baseT_Full + | ADVERTISED_FIBRE | ADVERTISED_Pause; } ethtool_convert_legacy_u32_to_link_mode(kset->link_modes.supported, @@ -438,7 +431,7 @@ static int phytmac_set_link_ksettings(struct net_device *ndev, int ret = 0; if (!ndev->phydev) { - netdev_err(ndev, "fixed link interface not supported set link\n"); + netdev_err(ndev, "Without a PHY, setting link is not supported\n"); ret = -EOPNOTSUPP; } else { phy_ethtool_set_link_ksettings(ndev, kset); @@ -513,6 +506,19 @@ static inline void phytmac_set_msglevel(struct net_device *ndev, u32 level) pdata->msg_enable = level; } +static void phytmac_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *drvinfo) +{ + struct phytmac *pdata = netdev_priv(ndev); + + strscpy(drvinfo->driver, PHYTMAC_DRV_NAME, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, PHYTMAC_DRIVER_VERSION, sizeof(drvinfo->version)); + + if (pdata->platdev) + strscpy(drvinfo->bus_info, pdata->platdev->name, sizeof(drvinfo->bus_info)); + else if (pdata->pcidev) + strscpy(drvinfo->bus_info, pci_name(pdata->pcidev), sizeof(drvinfo->bus_info)); +} + static const struct ethtool_ops phytmac_ethtool_ops = { .get_regs_len = phytmac_get_regs_len, .get_regs = phytmac_get_regs, @@ -535,6 +541,7 @@ static const struct ethtool_ops phytmac_ethtool_ops = { .set_channels = phytmac_set_channels, .get_wol = phytmac_get_wol, .set_wol = phytmac_set_wol, + .get_drvinfo = phytmac_get_drvinfo, }; void phytmac_set_ethtool_ops(struct net_device *ndev) diff --git a/drivers/net/ethernet/phytium/phytmac_main.c b/drivers/net/ethernet/phytium/phytmac_main.c index c172103a97362803d1d7db79e43718fbd3c3e90f..fd29c647e2775bc4b614ec8ceb48bc4afc60e15d 100644 --- a/drivers/net/ethernet/phytium/phytmac_main.c +++ b/drivers/net/ethernet/phytium/phytmac_main.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "phytmac.h" #include "phytmac_ptp.h" @@ -46,6 +48,7 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); #define RX_BUFFER_MULTIPLE 64 /* bytes */ #define MAX_MTU 3072 #define RING_ADDR_INTERVAL 128 +#define MAX_RING_ADDR_ALLOC_TIMES 3 #define RX_RING_BYTES(pdata) (sizeof(struct phytmac_dma_desc) \ * (pdata)->rx_ring_size) @@ -60,6 +63,23 @@ MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); * space in the SRAM (16KB) even when there is. */ +static int phytmac_queue_phyaddr_check(struct phytmac *pdata, dma_addr_t ring_base_addr, + int offset) +{ + u32 bus_addr_high; + int i; + + /* Check the high address of the DMA ring. */ + bus_addr_high = upper_32_bits(ring_base_addr); + for (i = 1; i < pdata->queues_num; i++) { + ring_base_addr += offset; + if (bus_addr_high != upper_32_bits(ring_base_addr)) + return -EFAULT; + } + + return 0; +} + static int phytmac_change_mtu(struct net_device *ndev, int new_mtu) { if (netif_running(ndev)) @@ -91,6 +111,8 @@ static int phytmac_set_mac_address(struct net_device *netdev, void *addr) hw_if->set_mac_address(pdata, saddr->sa_data); + phytmac_set_bios_wol_enable(pdata, pdata->wol ? 1 : 0); + return 0; } @@ -176,7 +198,6 @@ static int phytmac_mdio_write_c45(struct mii_bus *bus, int mii_id, int devad, in return 0; } - static inline int hash_bit_value(int bitnr, __u8 *addr) { if (addr[bitnr / 8] & (1 << (bitnr % 8))) @@ -276,15 +297,12 @@ static struct net_device_stats *phytmac_get_stats(struct net_device *dev) return nstat; } -static int phytmac_calc_rx_buf_len(struct phytmac *pdata, u32 mtu) +static inline int phytmac_calc_rx_buf_len(void) { - unsigned int size = mtu + ETH_HLEN + ETH_FCS_LEN; - int rx_buf_len = roundup(size, RX_BUFFER_MULTIPLE); - - netdev_dbg(pdata->ndev, "mtu [%u] rx_buffer_size [%u]\n", - mtu, rx_buf_len); - - return rx_buf_len; +#if (PAGE_SIZE < 8192) + return rounddown(PHYTMAC_MAX_FRAME_BUILD_SKB, RX_BUFFER_MULTIPLE); +#endif + return rounddown(PHYTMAC_RXBUFFER_2048, RX_BUFFER_MULTIPLE); } inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, @@ -293,12 +311,6 @@ inline struct phytmac_dma_desc *phytmac_get_rx_desc(struct phytmac_queue *queue, return &queue->rx_ring[index & (queue->pdata->rx_ring_size - 1)]; } -struct sk_buff *phytmac_get_rx_skb(struct phytmac_queue *queue, - unsigned int index) -{ - return queue->rx_skb[index & (queue->pdata->rx_ring_size - 1)]; -} - struct phytmac_tx_skb *phytmac_get_tx_skb(struct phytmac_queue *queue, unsigned int index) { @@ -311,6 +323,49 @@ inline struct phytmac_dma_desc *phytmac_get_tx_desc(struct phytmac_queue *queue, return &queue->tx_ring[index & (queue->pdata->tx_ring_size - 1)]; } +static void phytmac_rx_unmap(struct phytmac_queue *queue) +{ + struct phytmac_rx_buffer *rx_buffer_info; + struct phytmac *pdata = queue->pdata; + int i; + + if (queue->rx_buffer_info) { + /* Free all the Rx ring sk_buffs */ + i = queue->rx_tail; + + while (i != queue->rx_next_to_alloc) { + rx_buffer_info = &queue->rx_buffer_info[i]; + + /* Invalidate cache lines that may have been written to by + * device so that we avoid corrupting memory. + */ + dma_sync_single_range_for_cpu(pdata->dev, + rx_buffer_info->addr, + rx_buffer_info->page_offset, + pdata->rx_buffer_len, + DMA_FROM_DEVICE); + + /* free resources associated with mapping */ + dma_unmap_page_attrs(pdata->dev, + rx_buffer_info->addr, + PHYTMAC_RX_PAGE_SIZE, + DMA_FROM_DEVICE, + PHYTMAC_RX_DMA_ATTR); + + __page_frag_cache_drain(rx_buffer_info->page, + rx_buffer_info->pagecnt_bias); + + i++; + if (i == pdata->rx_ring_size) + i = 0; + } + + queue->rx_tail = 0; + queue->rx_head = 0; + queue->rx_next_to_alloc = 0; + } +} + static int phytmac_free_tx_resource(struct phytmac *pdata) { struct phytmac_queue *queue; @@ -345,14 +400,10 @@ static int phytmac_free_tx_resource(struct phytmac *pdata) static int phytmac_free_rx_resource(struct phytmac *pdata) { struct phytmac_queue *queue; - struct sk_buff *skb; - struct phytmac_dma_desc *desc; - struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_dma_desc *rx_ring_base = NULL; dma_addr_t rx_ring_base_addr; - dma_addr_t addr; unsigned int q; - int size, i; + int size; queue = pdata->queues; if (queue->rx_ring) { @@ -361,25 +412,15 @@ static int phytmac_free_rx_resource(struct phytmac *pdata) } for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - if (queue->rx_skb) { - for (i = 0; i < pdata->rx_ring_size; i++) { - skb = phytmac_get_rx_skb(queue, i); - if (skb) { - desc = &queue->rx_ring[i]; - addr = hw_if->get_desc_addr(desc); - dma_unmap_single(pdata->dev, addr, pdata->rx_buffer_len, - DMA_FROM_DEVICE); - dev_kfree_skb_any(skb); - skb = NULL; - } - } - - kfree(queue->rx_skb); - queue->rx_skb = NULL; - } + phytmac_rx_unmap(queue); if (queue->rx_ring) queue->rx_ring = NULL; + + if (queue->rx_buffer_info) { + vfree(queue->rx_buffer_info); + queue->rx_buffer_info = NULL; + } } if (rx_ring_base) { @@ -397,26 +438,44 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) struct phytmac_dma_desc *tx_ring_base; dma_addr_t tx_ring_base_addr; unsigned int q; - int size; + int tx_offset; + int tx_size; + int size = 0; + int ret, i; + + tx_offset = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; + tx_offset = ALIGN(tx_offset, 4096); + tx_size = pdata->queues_num * tx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) + goto err; - size = pdata->queues_num * (TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + - RING_ADDR_INTERVAL); - tx_ring_base = dma_alloc_coherent(pdata->dev, size, - &tx_ring_base_addr, GFP_KERNEL); - if (!tx_ring_base) - goto err; + tx_ring_base = dma_alloc_coherent(pdata->dev, tx_size, + &tx_ring_base_addr, GFP_KERNEL); + if (!tx_ring_base) + continue; + + ret = phytmac_queue_phyaddr_check(pdata, tx_ring_base_addr, + tx_offset); + if (ret) { + dma_free_coherent(pdata->dev, tx_size, tx_ring_base, + tx_ring_base_addr); + continue; + } else { + break; + } + } for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - size = TX_RING_BYTES(pdata) + pdata->tx_bd_prefetch + RING_ADDR_INTERVAL; - queue->tx_ring = (void *)tx_ring_base + q * size; - queue->tx_ring_addr = tx_ring_base_addr + q * size; + queue->tx_ring = (void *)tx_ring_base + q * tx_offset; + queue->tx_ring_addr = tx_ring_base_addr + q * tx_offset; if (!queue->tx_ring) goto err; if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "Allocated TX ring for queue %u of %d bytes at %08lx\n", - q, size, (unsigned long)queue->tx_ring_addr); + q, tx_offset, (unsigned long)queue->tx_ring_addr); size = pdata->tx_ring_size * sizeof(struct phytmac_tx_skb); queue->tx_skb = kzalloc(size, GFP_KERNEL); @@ -428,7 +487,6 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) "Allocated %d TX struct tx_skb entries at %p\n", pdata->tx_ring_size, queue->tx_skb); } - tx_ring_base = NULL; return 0; err: @@ -440,46 +498,53 @@ static int phytmac_alloc_tx_resource(struct phytmac *pdata) static int phytmac_alloc_rx_resource(struct phytmac *pdata) { struct phytmac_queue *queue; - struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_dma_desc *rx_ring_base; dma_addr_t rx_ring_base_addr; + int rx_offset; + int rx_size; unsigned int q; - int size; - int i; + int size = 0; + int ret, i; + + rx_offset = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; + rx_offset = ALIGN(rx_offset, 4096); + rx_size = pdata->queues_num * rx_offset; + for (i = 0; i < MAX_RING_ADDR_ALLOC_TIMES + 1; i++) { + if (i == MAX_RING_ADDR_ALLOC_TIMES) + goto err; - size = pdata->queues_num * (RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + - RING_ADDR_INTERVAL); - rx_ring_base = dma_alloc_coherent(pdata->dev, size, - &rx_ring_base_addr, GFP_KERNEL); - if (!rx_ring_base) - goto err; + rx_ring_base = dma_alloc_coherent(pdata->dev, rx_size, + &rx_ring_base_addr, GFP_KERNEL); + if (!rx_ring_base) + continue; + + ret = phytmac_queue_phyaddr_check(pdata, rx_ring_base_addr, + rx_offset); + if (ret) { + dma_free_coherent(pdata->dev, rx_size, rx_ring_base, + rx_ring_base_addr); + continue; + } else { + break; + } + } for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) { - size = RX_RING_BYTES(pdata) + pdata->rx_bd_prefetch + RING_ADDR_INTERVAL; - queue->rx_ring = (void *)rx_ring_base + q * size; - queue->rx_ring_addr = rx_ring_base_addr + q * size; + queue->rx_ring = (void *)rx_ring_base + q * rx_offset; + queue->rx_ring_addr = rx_ring_base_addr + q * rx_offset; if (!queue->rx_ring) goto err; if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "Allocated RX ring for queue %u of %d bytes at %08lx\n", - q, size, (unsigned long)queue->rx_ring_addr); - - for (i = 0; i < pdata->rx_ring_size; i++) - hw_if->init_rx_map(queue, i); + q, rx_offset, (unsigned long)queue->rx_ring_addr); - size = pdata->rx_ring_size * sizeof(struct sk_buff *); - queue->rx_skb = kzalloc(size, GFP_KERNEL); - if (!queue->rx_skb) + size = pdata->rx_ring_size * sizeof(struct phytmac_rx_buffer); + queue->rx_buffer_info = vzalloc(size); + if (!queue->rx_buffer_info) goto err; - - if (netif_msg_drv(pdata)) - netdev_info(pdata->ndev, - "Allocated %d RX struct sk_buff entries at %p\n", - pdata->rx_ring_size, queue->rx_skb); } - rx_ring_base = NULL; return 0; err: @@ -490,10 +555,9 @@ static int phytmac_alloc_rx_resource(struct phytmac *pdata) static int phytmac_alloc_resource(struct phytmac *pdata) { - struct net_device *ndev = pdata->ndev; int ret; - pdata->rx_buffer_len = phytmac_calc_rx_buf_len(pdata, ndev->mtu); + pdata->rx_buffer_len = phytmac_calc_rx_buf_len(); if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "alloc resource, rx_buffer_len = %d\n", @@ -604,30 +668,221 @@ static void phytmac_dump_pkt(struct phytmac *pdata, struct sk_buff *skb, bool tx skb->data, skb->len, true); } +static bool phytmac_alloc_mapped_page(struct phytmac *pdata, + struct phytmac_rx_buffer *bi) +{ + struct page *page = bi->page; + dma_addr_t dma; + + /* since we are recycling buffers we should seldom need to alloc */ + if (likely(page)) + return true; + + /* alloc new page for storage */ + page = __dev_alloc_pages(PHYTMAC_GFP_FLAGS, PHYTMAC_RX_PAGE_ORDER); + if (unlikely(!page)) { + netdev_err(pdata->ndev, "rx alloc page failed\n"); + return false; + } + + /* map page for use */ + dma = dma_map_page_attrs(pdata->dev, page, 0, + PHYTMAC_RX_PAGE_SIZE, + DMA_FROM_DEVICE, PHYTMAC_RX_DMA_ATTR); + if (dma_mapping_error(pdata->dev, dma)) { + __free_pages(page, PHYTMAC_RX_PAGE_ORDER); + return false; + } + + bi->addr = dma; + bi->page = page; + bi->page_offset = PHYTMAC_SKB_PAD; + bi->pagecnt_bias = 1; + + return true; +} + +static bool phytmac_can_reuse_rx_page(struct phytmac_rx_buffer *rx_buffer) +{ + unsigned int pagecnt_bias = rx_buffer->pagecnt_bias; + struct page *page = rx_buffer->page; + + /* avoid re-using remote and pfmemalloc pages */ + if (!dev_page_is_reusable(page)) + return false; + +#if (PAGE_SIZE < 8192) + /* if we are only owner of page we can reuse it */ + if (unlikely((page_ref_count(page) - pagecnt_bias) > 1)) + return false; +#else +#define PHYTMAC_LAST_OFFSET \ + (SKB_WITH_OVERHEAD(PAGE_SIZE) - PHYTMAC_RXBUFFER_2048) + + if (rx_buffer->page_offset > PHYTMAC_LAST_OFFSET) + return false; +#endif + + /* If we have drained the page fragment pool we need to update + * the pagecnt_bias and page count so that we fully restock the + * number of references the driver holds. + */ + if (unlikely(!pagecnt_bias)) { + page_ref_add(page, USHRT_MAX); + rx_buffer->pagecnt_bias = USHRT_MAX; + } + + return true; +} + +static void phytmac_reuse_rx_page(struct phytmac_queue *queue, + struct phytmac_rx_buffer *old_buff) +{ + struct phytmac_rx_buffer *new_buff; + struct phytmac *pdata = queue->pdata; + u16 nta = queue->rx_next_to_alloc; + + new_buff = &queue->rx_buffer_info[nta & (pdata->rx_ring_size - 1)]; + + /* update, and store next to alloc */ + nta++; + queue->rx_next_to_alloc = (nta < pdata->rx_ring_size) ? nta : 0; + + /* Transfer page from old buffer to new buffer. + * Move each member individually to avoid possible store + * forwarding stalls. + */ + new_buff->addr = old_buff->addr; + new_buff->page = old_buff->page; + new_buff->page_offset = old_buff->page_offset; + new_buff->pagecnt_bias = old_buff->pagecnt_bias; +} + +static struct phytmac_rx_buffer *phytmac_get_rx_buffer(struct phytmac_queue *queue, + unsigned int index, + const unsigned int size) +{ + struct phytmac_rx_buffer *rx_buffer; + struct phytmac *pdata = queue->pdata; + + rx_buffer = &queue->rx_buffer_info[index & (pdata->rx_ring_size - 1)]; + prefetchw(rx_buffer->page); + + /* we are reusing so sync this buffer for CPU use */ + dma_sync_single_range_for_cpu(pdata->dev, + rx_buffer->addr, + rx_buffer->page_offset, + size, + DMA_FROM_DEVICE); + + rx_buffer->pagecnt_bias--; + + return rx_buffer; +} + +static void phytmac_put_rx_buffer(struct phytmac_queue *queue, + struct phytmac_rx_buffer *rx_buffer) +{ + struct phytmac *pdata = queue->pdata; + + if (phytmac_can_reuse_rx_page(rx_buffer)) { + /* hand second half of page back to the ring */ + phytmac_reuse_rx_page(queue, rx_buffer); + } else { + dma_unmap_page_attrs(pdata->dev, rx_buffer->addr, + PHYTMAC_RX_PAGE_SIZE, + DMA_FROM_DEVICE, PHYTMAC_RX_DMA_ATTR); + __page_frag_cache_drain(rx_buffer->page, + rx_buffer->pagecnt_bias); + } + + /* clear contents of rx_buffer */ + rx_buffer->page = NULL; +} + +static void phytmac_add_rx_frag(struct phytmac_queue *queue, + struct phytmac_rx_buffer *rx_buffer, + struct sk_buff *skb, + unsigned int size) +{ + unsigned int truesize; + +#if (PAGE_SIZE < 8192) + truesize = PHYTMAC_RX_PAGE_SIZE / 2; +#else + truesize = SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + size); +#endif + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page, + rx_buffer->page_offset, size, truesize); +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif +} + +static struct sk_buff *phytmac_build_skb(struct phytmac_rx_buffer *rx_buffer, + unsigned int size) +{ + struct sk_buff *skb; + unsigned int truesize; + void *va; + +#if (PAGE_SIZE < 8192) + truesize = PHYTMAC_RX_PAGE_SIZE / 2; +#else + truesize = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + + SKB_DATA_ALIGN(PHYTMAC_SKB_PAD + size); +#endif + + va = page_address(rx_buffer->page) + rx_buffer->page_offset; + /* prefetch first cache line of first page */ + prefetch(va); + + /* build an skb around the page buffer */ + skb = build_skb(va - PHYTMAC_SKB_PAD, truesize); + if (unlikely(!skb)) + return NULL; + + /* update pointers within the skb to store the data */ + skb_reserve(skb, PHYTMAC_SKB_PAD); + __skb_put(skb, size); + + /* update buffer offset */ +#if (PAGE_SIZE < 8192) + rx_buffer->page_offset ^= truesize; +#else + rx_buffer->page_offset += truesize; +#endif + + return skb; +} + static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phytmac_dma_desc *desc) { struct phytmac *pdata = queue->pdata; struct phytmac_hw_if *hw_if = pdata->hw_if; - struct sk_buff *skb; + struct phytmac_rx_buffer *rx_buffer; + struct sk_buff *skb = NULL; unsigned int len; - dma_addr_t addr; - skb = phytmac_get_rx_skb(queue, queue->rx_tail); + len = hw_if->get_rx_pkt_len(pdata, desc); + rx_buffer = phytmac_get_rx_buffer(queue, queue->rx_tail, len); + hw_if->zero_rx_desc_addr(desc); + + skb = phytmac_build_skb(rx_buffer, len); if (unlikely(!skb)) { netdev_err(pdata->ndev, - "inconsistent Rx descriptor chain\n"); + "rx single build skb failed\n"); pdata->ndev->stats.rx_dropped++; queue->stats.rx_dropped++; + rx_buffer->pagecnt_bias++; return NULL; } - queue->rx_skb[queue->rx_tail & (pdata->rx_ring_size - 1)] = NULL; - len = hw_if->get_rx_pkt_len(pdata, desc); - addr = hw_if->get_desc_addr(desc); + phytmac_put_rx_buffer(queue, rx_buffer); - skb_put(skb, len); - dma_unmap_single(pdata->dev, addr, - pdata->rx_buffer_len, DMA_FROM_DEVICE); skb->protocol = eth_type_trans(skb, pdata->ndev); skb_checksum_none_assert(skb); @@ -643,64 +898,67 @@ static struct sk_buff *phytmac_rx_single(struct phytmac_queue *queue, struct phy } static struct sk_buff *phytmac_rx_frame(struct phytmac_queue *queue, - unsigned int first_frag, unsigned int last_frag, int len) + unsigned int first_frag, unsigned int last_frag, + unsigned int total_len) { - unsigned int offset = 0; unsigned int frag = 0; - unsigned int entry = 0; - dma_addr_t addr = 0; struct sk_buff *skb; struct phytmac_dma_desc *desc; struct phytmac *pdata = queue->pdata; struct phytmac_hw_if *hw_if = pdata->hw_if; unsigned int frag_len = pdata->rx_buffer_len; + unsigned int offset = frag_len; + struct phytmac_rx_buffer *rx_buffer; if (netif_msg_drv(pdata)) netdev_info(pdata->ndev, "rx frame %u - %u (len %u)\n", - first_frag, last_frag, len); + first_frag, last_frag, total_len); + + desc = phytmac_get_rx_desc(queue, first_frag); + rx_buffer = phytmac_get_rx_buffer(queue, first_frag, frag_len); + hw_if->zero_rx_desc_addr(desc); - skb = netdev_alloc_skb(pdata->ndev, len); - if (!skb) { + skb = phytmac_build_skb(rx_buffer, frag_len); + if (unlikely(!skb)) { + netdev_err(pdata->ndev, "rx frame build skb failed\n"); pdata->ndev->stats.rx_dropped++; - netdev_err(pdata->ndev, "rx frame alloc skb failed\n"); + queue->stats.rx_dropped++; + rx_buffer->pagecnt_bias++; return NULL; } - skb_checksum_none_assert(skb); + phytmac_put_rx_buffer(queue, rx_buffer); - if (pdata->ndev->features & NETIF_F_RXCSUM && - !(pdata->ndev->flags & IFF_PROMISC) && - hw_if->rx_checksum(phytmac_get_rx_desc(queue, last_frag))) - skb->ip_summed = CHECKSUM_UNNECESSARY; - - skb_put(skb, len); + for (frag = first_frag + 1; ; frag++) { + desc = phytmac_get_rx_desc(queue, frag); + rx_buffer = phytmac_get_rx_buffer(queue, frag, frag_len); + hw_if->zero_rx_desc_addr(desc); - for (frag = first_frag; ; frag++) { - if (offset + frag_len > len) { + if (offset + frag_len > total_len) { if (unlikely(frag != last_frag)) { dev_kfree_skb_any(skb); + phytmac_put_rx_buffer(queue, rx_buffer); return NULL; } - frag_len = len - offset; + frag_len = total_len - offset; } - desc = phytmac_get_rx_desc(queue, frag); - addr = hw_if->get_desc_addr(desc); - dma_sync_single_for_cpu(pdata->dev, addr, frag_len, - DMA_FROM_DEVICE); - - entry = frag & (pdata->rx_ring_size - 1); - skb_copy_to_linear_data_offset(skb, offset, queue->rx_skb[entry]->data, frag_len); - - offset += pdata->rx_buffer_len; + phytmac_add_rx_frag(queue, rx_buffer, skb, frag_len); + phytmac_put_rx_buffer(queue, rx_buffer); - dma_sync_single_for_device(pdata->dev, addr, frag_len, - DMA_FROM_DEVICE); + offset += frag_len; if (frag == last_frag) break; } + skb_checksum_none_assert(skb); + + if (pdata->ndev->features & NETIF_F_RXCSUM && + !(pdata->ndev->flags & IFF_PROMISC) && + hw_if->rx_checksum(phytmac_get_rx_desc(queue, last_frag))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + skb->protocol = eth_type_trans(skb, pdata->ndev); if (netif_msg_pktdata(pdata)) phytmac_dump_pkt(pdata, skb, false); @@ -716,10 +974,13 @@ static struct sk_buff *phytmac_rx_mbuffer(struct phytmac_queue *queue) struct sk_buff *skb = NULL; unsigned int rx_tail = 0; int first_frag = -1; - int len; + unsigned int len; for (rx_tail = queue->rx_tail; ; rx_tail++) { desc = phytmac_get_rx_desc(queue, rx_tail); + if (!hw_if->rx_complete(desc)) + return NULL; + if (hw_if->rx_pkt_start(desc)) { if (first_frag != -1) hw_if->clear_rx_desc(queue, first_frag, rx_tail); @@ -743,50 +1004,35 @@ static void phytmac_rx_clean(struct phytmac_queue *queue) struct phytmac *pdata = queue->pdata; struct phytmac_hw_if *hw_if = pdata->hw_if; unsigned int index, space; - dma_addr_t paddr; - struct sk_buff *skb; - unsigned int rx_unclean = 0; + struct phytmac_rx_buffer *rx_buf_info; space = CIRC_SPACE(queue->rx_head, queue->rx_tail, pdata->rx_ring_size); - if (space < DEFAULT_RX_DESC_MIN_FREE) - return; - - index = queue->rx_head & (pdata->rx_ring_size - 1); while (space > 0) { - if (!queue->rx_skb[index]) { - skb = netdev_alloc_skb(pdata->ndev, pdata->rx_buffer_len); - if (unlikely(!skb)) { - netdev_err(pdata->ndev, "rx clean alloc skb failed\n"); - break; - } + index = queue->rx_head & (pdata->rx_ring_size - 1); + rx_buf_info = &queue->rx_buffer_info[index]; - paddr = dma_map_single(pdata->dev, skb->data, - pdata->rx_buffer_len, DMA_FROM_DEVICE); - if (dma_mapping_error(pdata->dev, paddr)) { - dev_kfree_skb(skb); - break; - } + if (!phytmac_alloc_mapped_page(pdata, rx_buf_info)) + break; + /* sync the buffer for use by the device */ + dma_sync_single_range_for_device(pdata->dev, rx_buf_info->addr, + rx_buf_info->page_offset, + pdata->rx_buffer_len, + DMA_FROM_DEVICE); - queue->rx_skb[index] = skb; + hw_if->rx_map(queue, index, rx_buf_info->addr + rx_buf_info->page_offset); - hw_if->rx_map(queue, index, paddr); - } + queue->rx_head++; + if (queue->rx_head >= pdata->rx_ring_size) + queue->rx_head &= (pdata->rx_ring_size - 1); - index = (index + 1) & (pdata->rx_ring_size - 1); - rx_unclean++; space--; } + queue->rx_next_to_alloc = queue->rx_head; /* make newly descriptor to hardware */ wmb(); - hw_if->rx_clean(queue, rx_unclean); - /* make newly descriptor to hardware */ - wmb(); - queue->rx_head += rx_unclean; - if (queue->rx_head >= pdata->rx_ring_size) - queue->rx_head &= (pdata->rx_ring_size - 1); } static int phytmac_rx(struct phytmac_queue *queue, struct napi_struct *napi, @@ -880,7 +1126,7 @@ static int phytmac_maybe_wake_tx_queue(struct phytmac_queue *queue) { struct phytmac *pdata = queue->pdata; int space = CIRC_CNT(queue->tx_tail, queue->tx_head, - pdata->tx_ring_size); + pdata->tx_ring_size); return (space <= (3 * pdata->tx_ring_size / 4)) ? 1 : 0; } @@ -1155,7 +1401,7 @@ static unsigned int phytmac_tx_map(struct phytmac *pdata, { dma_addr_t mapping; struct phytmac_hw_if *hw_if = pdata->hw_if; - unsigned int len, i, tx_tail = queue->tx_tail; + unsigned int len, i, tx_tail; struct phytmac_tx_skb *tx_skb = NULL; unsigned int offset, size, count = 0; unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags; @@ -1249,14 +1495,18 @@ static inline void phytmac_init_ring(struct phytmac *pdata) struct phytmac_hw_if *hw_if = pdata->hw_if; struct phytmac_queue *queue; unsigned int q = 0; + int i; for (queue = pdata->queues; q < pdata->queues_num; ++q) { queue->tx_head = 0; queue->tx_tail = 0; hw_if->clear_tx_desc(queue); + for (i = 0; i < pdata->rx_ring_size; i++) + hw_if->init_rx_map(queue, i); queue->rx_head = 0; queue->rx_tail = 0; + queue->rx_next_to_alloc = 0; phytmac_rx_clean(queue); ++queue; } @@ -1375,7 +1625,7 @@ static const struct phylink_pcs_ops phytmac_pcs_phylink_ops = { }; static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, - phy_interface_t interface) + phy_interface_t interface) { struct phytmac *pdata = netdev_priv(to_net_dev(config->dev)); @@ -1391,7 +1641,6 @@ static struct phylink_pcs *phytmac_mac_select_pcs(struct phylink_config *config, return &pdata->phylink_pcs; } - static void phytmac_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { @@ -1479,6 +1728,9 @@ static void phytmac_mac_link_up(struct phylink_config *config, pdata->pause = rx_pause; } + pdata->speed = speed; + pdata->duplex = duplex; + phytmac_init_ring(pdata); for (q = 0, queue = pdata->queues; q < pdata->queues_num; ++q, ++queue) @@ -1597,11 +1849,12 @@ static void phytmac_validate(struct phylink_config *config, if (state->interface == PHY_INTERFACE_MODE_5GBASER) phylink_set(mask, 5000baseT_Full); - if (state->interface == PHY_INTERFACE_MODE_1000BASEX || - state->interface == PHY_INTERFACE_MODE_SGMII || + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + phylink_set(mask, 1000baseX_Full); + + if (state->interface == PHY_INTERFACE_MODE_SGMII || phy_interface_mode_is_rgmii(state->interface)) { phylink_set(mask, 1000baseT_Full); - phylink_set(mask, 1000baseX_Full); phylink_set(mask, 1000baseT_Half); phylink_set(mask, 10baseT_Half); phylink_set(mask, 10baseT_Full); @@ -1685,12 +1938,6 @@ static int phytmac_open(struct net_device *ndev) hw_if->reset_hw(pdata); - ret = phytmac_get_mac_address(pdata); - if (ret) { - netdev_err(ndev, "phytmac get mac address failed\n"); - goto reset_hw; - } - ret = netif_set_real_num_tx_queues(ndev, pdata->queues_num); if (ret) { netdev_err(ndev, "error setting real tx queue number\n"); @@ -1716,17 +1963,17 @@ static int phytmac_open(struct net_device *ndev) ++queue; } - phytmac_init_ring(pdata); hw_if->init_hw(pdata); ret = phytmac_phylink_connect(pdata); if (ret) { - netdev_err(ndev, "phylink connet failed,(error %d)\n", + netdev_err(ndev, "phylink connect failed,(error %d)\n", ret); goto reset_hw; } phylink_start(pdata->phylink); + phytmac_set_bios_wol_enable(pdata, pdata->wol ? 1 : 0); netif_tx_start_all_queues(pdata->ndev); @@ -1796,7 +2043,7 @@ static int phytmac_close(struct net_device *ndev) static int phytmac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct phytmac *pdata = netdev_priv(dev); - int ret; + int ret = -EOPNOTSUPP; if (!netif_running(dev)) return -EINVAL; @@ -1877,6 +2124,37 @@ static netdev_features_t phytmac_features_check(struct sk_buff *skb, return features; } +void phytmac_set_bios_wol_enable(struct phytmac *pdata, u32 wol) +{ + struct net_device *ndev = pdata->ndev; + + if (ndev->phydev) { +#ifdef CONFIG_ACPI + if (has_acpi_companion(pdata->dev)) { + acpi_handle handle = ACPI_HANDLE(pdata->dev); + + if (acpi_has_method(handle, "PWOL")) { + union acpi_object args[] = { + { .type = ACPI_TYPE_INTEGER, }, + }; + struct acpi_object_list arg_input = { + .pointer = args, + .count = ARRAY_SIZE(args), + }; + acpi_status status; + + /* Set the input parameters */ + args[0].integer.value = wol; + + status = acpi_evaluate_object(handle, "PWOL", &arg_input, NULL); + if (ACPI_FAILURE(status)) + netdev_err(ndev, "The PWOL method failed to be executed.\n"); + } + } +#endif + } +} + int phytmac_reset_ringsize(struct phytmac *pdata, u32 rx_size, u32 tx_size) { int ret = 0; @@ -1968,7 +2246,6 @@ static int phytmac_init(struct phytmac *pdata) ndev->netdev_ops = &phytmac_netdev_ops; phytmac_set_ethtool_ops(ndev); - eth_hw_addr_random(pdata->ndev); if (ndev->hw_features & NETIF_F_NTUPLE) { INIT_LIST_HEAD(&pdata->rx_fs_list.list); @@ -2076,6 +2353,12 @@ int phytmac_drv_probe(struct phytmac *pdata) goto err_phylink_init; } + ret = phytmac_get_mac_address(pdata); + if (ret) { + netdev_err(ndev, "phytmac get mac address failed\n"); + goto err_phylink_init; + } + if (netif_msg_probe(pdata)) dev_dbg(pdata->dev, "probe successfully! Phytium %s at 0x%08lx irq %d (%pM)\n", "MAC", ndev->base_addr, ndev->irq, ndev->dev_addr); @@ -2241,4 +2524,5 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); MODULE_ALIAS("platform:phytmac"); +MODULE_VERSION(PHYTMAC_DRIVER_VERSION); diff --git a/drivers/net/ethernet/phytium/phytmac_pci.c b/drivers/net/ethernet/phytium/phytmac_pci.c index fd21bf80f1388350d2692f11ebacadc97c701e10..60bd296d8f0b6c95c1cb3b210f719efb1241773f 100644 --- a/drivers/net/ethernet/phytium/phytmac_pci.c +++ b/drivers/net/ethernet/phytium/phytmac_pci.c @@ -225,7 +225,7 @@ struct phytmac_data phytmac_1000basex = { .use_mii = false, .speed = 1000, .duplex = true, - .interface = PHY_INTERFACE_MODE_SGMII, + .interface = PHY_INTERFACE_MODE_1000BASEX, .properties = fl_properties[0], }; @@ -316,3 +316,4 @@ module_pci_driver(phytmac_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium NIC PCI wrapper"); +MODULE_VERSION(PHYTMAC_DRIVER_VERSION); diff --git a/drivers/net/ethernet/phytium/phytmac_platform.c b/drivers/net/ethernet/phytium/phytmac_platform.c index 305ff5866e2fe0fdc71b0a347275b95e674f4308..9390056fdc7a95a86917608ba97561ca8e362c49 100644 --- a/drivers/net/ethernet/phytium/phytmac_platform.c +++ b/drivers/net/ethernet/phytium/phytmac_platform.c @@ -253,3 +253,4 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium Ethernet driver"); MODULE_AUTHOR("Wenting Song"); MODULE_ALIAS("platform:phytmac"); +MODULE_VERSION(PHYTMAC_DRIVER_VERSION); diff --git a/drivers/net/ethernet/phytium/phytmac_v1.c b/drivers/net/ethernet/phytium/phytmac_v1.c index ec95c6c79b06681158396f16ab3f0d024ad558d9..72a6eeaec3563dd67c0cc9bb853ca65ee71ba95e 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.c +++ b/drivers/net/ethernet/phytium/phytmac_v1.c @@ -182,7 +182,7 @@ static int phytmac_mac_linkup(struct phytmac *pdata, phy_interface_t interface, config = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG); - config &= ~(PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD)); + config &= ~(PHYTMAC_BIT(SPEED) | PHYTMAC_BIT(FD) | PHYTMAC_BIT(GM_EN)); if (speed == SPEED_100) config |= PHYTMAC_BIT(SPEED); @@ -414,7 +414,7 @@ static int phytmac_init_hw(struct phytmac *pdata) PHYTMAC_WRITE(pdata, PHYTMAC_DCONFIG, dmaconfig); if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) - PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, 0x80000001); + PHYTMAC_WRITE(pdata, PHYTMAC_TAIL_ENABLE, PHYTMAC_BIT(TXTAIL_ENABLE)); if (phy_interface_mode_is_8023z(pdata->phy_interface)) phytmac_pcs_software_reset(pdata, 1); @@ -489,6 +489,15 @@ static int phytmac_set_wake(struct phytmac *pdata, int wake) value |= PHYTMAC_BIT(MCAST); PHYTMAC_WRITE(pdata, PHYTMAC_WOL, value); + if (wake) { + PHYTMAC_WRITE(pdata, PHYTMAC_IE, PHYTMAC_BIT(WOL_RECEIVE_ENABLE)); + value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG) | PHYTMAC_BIT(IGNORE_RX_FCS); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + } else { + PHYTMAC_WRITE(pdata, PHYTMAC_ID, PHYTMAC_BIT(WOL_RECEIVE_DISABLE)); + value = PHYTMAC_READ(pdata, PHYTMAC_NCONFIG) & ~PHYTMAC_BIT(IGNORE_RX_FCS); + PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, value); + } return 0; } @@ -918,23 +927,23 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, addr |= PHYTMAC_BIT(RX_WRAP); desc->desc1 = 0; desc->desc2 = upper_32_bits(addr); - desc->desc0 = lower_32_bits(addr) | PHYTMAC_BIT(RX_USED); + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 = lower_32_bits(addr); + } else { + desc->desc1 = 0; + /* Make newly descriptor to hardware */ + dma_wmb(); + desc->desc0 &= ~PHYTMAC_BIT(RX_USED); } + return 0; } -static unsigned int phytmac_rx_clean_desc(struct phytmac_queue *queue, u32 count) +static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) { - struct phytmac_dma_desc *desc; - u32 index = queue->rx_head + count - 1; - - while (count) { - desc = phytmac_get_rx_desc(queue, index); - desc->desc0 &= ~PHYTMAC_BIT(RX_USED); - dma_wmb(); - index--; - count--; - } + desc->desc2 = 0; + desc->desc0 = PHYTMAC_BIT(RX_USED); return 0; } @@ -945,7 +954,7 @@ static void phytmac_tx_start(struct phytmac_queue *queue) if (pdata->capacities & PHYTMAC_CAPS_TAILPTR) PHYTMAC_WRITE(pdata, PHYTMAC_TAILPTR(queue->index), - BIT(31) | queue->tx_tail); + PHYTMAC_BIT(TXTAIL_UPDATE) | queue->tx_tail); if (pdata->capacities & PHYTMAC_CAPS_START) PHYTMAC_WRITE(pdata, PHYTMAC_NCTRL, @@ -972,9 +981,19 @@ static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) return PHYTMAC_GET_BITS(desc->desc1, TX_USED); } -static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) { - return (desc->desc0 & PHYTMAC_BIT(RX_USED)) != 0; + dma_addr_t addr; + bool used; + + used = desc->desc0 & PHYTMAC_BIT(RX_USED); + addr = ((u64)(desc->desc2) << 32); + addr |= desc->desc0 & 0xfffffff8; + + if (used != 0 && addr != 0) + return true; + else + return false; } static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) @@ -985,16 +1004,6 @@ static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_de return desc->desc1 & PHYTMAC_FRAME_MASK; } -static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) -{ - dma_addr_t addr = 0; - - addr = ((u64)(desc->desc2) << 32); - - addr |= (desc->desc0 & 0xfffffffc); - return addr; -} - static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; @@ -1033,7 +1042,7 @@ static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int en if (begin > end) tmp = end + queue->pdata->rx_ring_size; - for (frag = begin; frag != end; frag++) { + for (frag = begin; frag != tmp; frag++) { desc = phytmac_get_rx_desc(queue, frag); desc->desc0 &= ~PHYTMAC_BIT(RX_USED); } @@ -1069,12 +1078,15 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod config |= PHYTMAC_BIT(SGMII_EN) | PHYTMAC_BIT(PCS_EN); if (state->speed == SPEED_1000) config |= PHYTMAC_BIT(GM_EN); - else if (state->speed == SPEED_2500) - config |= PHYTMAC_BIT(2PT5G); + else if (state->speed == SPEED_2500) { + ctrl |= PHYTMAC_BIT(2PT5G); + config |= PHYTMAC_BIT(GM_EN); + } } else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) { config |= PHYTMAC_BIT(PCS_EN) | PHYTMAC_BIT(GM_EN); } else if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { - config |= PHYTMAC_BIT(2PT5G) | PHYTMAC_BIT(PCS_EN); + ctrl |= PHYTMAC_BIT(2PT5G); + config |= PHYTMAC_BIT(PCS_EN) | PHYTMAC_BIT(GM_EN); } else if (state->interface == PHY_INTERFACE_MODE_10GBASER || state->interface == PHY_INTERFACE_MODE_USXGMII || state->interface == PHY_INTERFACE_MODE_5GBASER) { @@ -1102,11 +1114,17 @@ static void phytmac_mac_interface_config(struct phytmac *pdata, unsigned int mod if (old_config ^ config) PHYTMAC_WRITE(pdata, PHYTMAC_NCONFIG, config); - /* Disable AN for SGMII fixed link configuration, enable otherwise.*/ - if (state->interface == PHY_INTERFACE_MODE_SGMII) - phytmac_enable_autoneg(pdata, mode == MLO_AN_FIXED ? 0 : 1); + /* Disable AN for SGMII fixed link or speed equal to 2.5G, enable otherwise.*/ + if (state->interface == PHY_INTERFACE_MODE_SGMII) { + if (state->speed == SPEED_2500 || mode == MLO_AN_FIXED) + phytmac_enable_autoneg(pdata, 0); + else + phytmac_enable_autoneg(pdata, 1); + } if (state->interface == PHY_INTERFACE_MODE_1000BASEX) phytmac_enable_autoneg(pdata, 1); + if (state->interface == PHY_INTERFACE_MODE_2500BASEX) + phytmac_enable_autoneg(pdata, 0); } static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, @@ -1374,16 +1392,15 @@ struct phytmac_hw_if phytmac_1p0_hw = { .tx_complete = phytmac_tx_complete, .rx_complete = phytmac_rx_complete, .get_rx_pkt_len = phytmac_rx_pkt_len, - .get_desc_addr = phytmac_get_desc_addr, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, - .rx_clean = phytmac_rx_clean_desc, .rx_checksum = phytmac_rx_checksum, .rx_single_buffer = phytmac_rx_single_buffer, .rx_pkt_start = phytmac_rx_sof, .rx_pkt_end = phytmac_rx_eof, .clear_rx_desc = phytmac_clear_rx_desc, .clear_tx_desc = phytmac_clear_tx_desc, + .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, /* ptp */ .init_ts_hw = phytmac_ptp_init_hw, .set_time = phytmac_set_time, diff --git a/drivers/net/ethernet/phytium/phytmac_v1.h b/drivers/net/ethernet/phytium/phytmac_v1.h index d8de2c26cab4b2543167db3a8f5bb6672bb85ec5..32bb1294910963b71d34d0b2a354d8fbf179d99a 100644 --- a/drivers/net/ethernet/phytium/phytmac_v1.h +++ b/drivers/net/ethernet/phytium/phytmac_v1.h @@ -140,6 +140,8 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_DBW_128 4 #define PHYTMAC_RCO_EN_INDEX 24 /* Receive checksum offload enable */ #define PHYTMAC_RCO_EN_WIDTH 1 +#define PHYTMAC_IGNORE_RX_FCS_INDEX 26 +#define PHYTMAC_IGNORE_RX_FCS_WIDTH 1 #define PHYTMAC_SGMII_EN_INDEX 27 /* Sgmii mode enable */ #define PHYTMAC_SGMII_EN_WIDTH 1 @@ -356,6 +358,22 @@ extern struct phytmac_hw_if phytmac_1p0_hw; #define PHYTMAC_VLAN_ID_INDEX 10 #define PHYTMAC_VLAN_ID_WIDTH 1 +/* Bitfields in TAILPTR */ +#define PHYTMAC_TXTAIL_UPDATE_INDEX 31 /* Update tx tail */ +#define PHYTMAC_TXTAIL_UPDATE_WIDTH 1 + +/* Bitfields in TAIL_ENABLE */ +#define PHYTMAC_TXTAIL_ENABLE_INDEX 0 /* Enable tx tail */ +#define PHYTMAC_TXTAIL_ENABLE_WIDTH 1 + +/* Bitfields in INT ENABLE */ +#define PHYTMAC_WOL_RECEIVE_ENABLE_INDEX 28 /* Enable wol_event_recieve */ +#define PHYTMAC_WOL_RECEIVE_ENABLE_WIDTH 1 + +/* Bitfields in INT DISABLE */ +#define PHYTMAC_WOL_RECEIVE_DISABLE_INDEX 28 /* Disable wol_event_recieve */ +#define PHYTMAC_WOL_RECEIVE_DISABLE_WIDTH 1 + #define PHYTMAC_TSEC_WIDTH (PHYTMAC_SECH_WIDTH + PHYTMAC_SECL_WIDTH) #define SEC_MAX_VAL (((u64)1 << PHYTMAC_TSEC_WIDTH) - 1) #define NSEC_MAX_VAL ((1 << PHYTMAC_NSEC_WIDTH) - 1) diff --git a/drivers/net/ethernet/phytium/phytmac_v2.c b/drivers/net/ethernet/phytium/phytmac_v2.c index df142aa6797f687d35ddb0355fe9c0e57d21a8a4..25f1ba6c1d6ef428f7b521386ebf0eb1f882118c 100644 --- a/drivers/net/ethernet/phytium/phytmac_v2.c +++ b/drivers/net/ethernet/phytium/phytmac_v2.c @@ -842,6 +842,7 @@ static int phytmac_pcs_linkdown(struct phytmac *pdata) static unsigned int phytmac_pcs_get_link(struct phytmac *pdata, phy_interface_t interface) { if (interface == PHY_INTERFACE_MODE_SGMII || + interface == PHY_INTERFACE_MODE_1000BASEX || interface == PHY_INTERFACE_MODE_2500BASEX) return PHYTMAC_READ_BITS(pdata, PHYTMAC_NETWORK_STATUS, LINK); else if (interface == PHY_INTERFACE_MODE_USXGMII || @@ -933,14 +934,32 @@ static unsigned int phytmac_rx_map_desc(struct phytmac_queue *queue, u32 index, return 0; } +static unsigned int phytmac_zero_rx_desc_addr(struct phytmac_dma_desc *desc) +{ + desc->desc2 = 0; + desc->desc0 = PHYTMAC_BIT(RXUSED); + + return 0; +} + static int phytmac_tx_complete(const struct phytmac_dma_desc *desc) { return PHYTMAC_GET_BITS(desc->desc1, TXUSED); } -static int phytmac_rx_complete(const struct phytmac_dma_desc *desc) +static bool phytmac_rx_complete(const struct phytmac_dma_desc *desc) { - return PHYTMAC_GET_BITS(desc->desc0, RXUSED); + dma_addr_t addr; + bool used; + + used = PHYTMAC_GET_BITS(desc->desc0, RXUSED); + addr = ((u64)(desc->desc2) << 32); + addr |= desc->desc0 & 0xfffffff8; + + if (used != 0 && addr != 0) + return true; + else + return false; } static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_desc *desc) @@ -951,16 +970,6 @@ static int phytmac_rx_pkt_len(struct phytmac *pdata, const struct phytmac_dma_de return desc->desc1 & PHYTMAC_RXFRMLEN_MASK; } -static dma_addr_t phytmac_get_desc_addr(const struct phytmac_dma_desc *desc) -{ - dma_addr_t addr = 0; - - addr = ((u64)(desc->desc2) << 32); - - addr |= (desc->desc0 & 0xfffffffc); - return addr; -} - static bool phytmac_rx_checksum(const struct phytmac_dma_desc *desc) { u32 value = desc->desc1; @@ -999,7 +1008,7 @@ static void phytmac_clear_rx_desc(struct phytmac_queue *queue, int begin, int en if (begin > end) tmp = end + queue->pdata->rx_ring_size; - for (frag = begin; frag != end; frag++) { + for (frag = begin; frag != tmp; frag++) { desc = phytmac_get_rx_desc(queue, frag); desc->desc0 &= ~PHYTMAC_BIT(RXUSED); } @@ -1228,7 +1237,6 @@ struct phytmac_hw_if phytmac_2p0_hw = { .tx_complete = phytmac_tx_complete, .rx_complete = phytmac_rx_complete, .get_rx_pkt_len = phytmac_rx_pkt_len, - .get_desc_addr = phytmac_get_desc_addr, .init_rx_map = phytmac_init_rx_map_desc, .rx_map = phytmac_rx_map_desc, .rx_checksum = phytmac_rx_checksum, @@ -1237,6 +1245,7 @@ struct phytmac_hw_if phytmac_2p0_hw = { .rx_pkt_end = phytmac_rx_eof, .clear_rx_desc = phytmac_clear_rx_desc, .clear_tx_desc = phytmac_clear_tx_desc, + .zero_rx_desc_addr = phytmac_zero_rx_desc_addr, /* ptp */ .init_ts_hw = phytmac_ptp_init_hw, diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index ec2a3d16b1a2dab0d37bde44aed34d1acc6fb37a..312fb7728e8f9a22557e32dfab16aad1234a8cdc 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -268,6 +268,15 @@ static struct phy_driver genphy_driver; static LIST_HEAD(phy_fixup_list); static DEFINE_MUTEX(phy_fixup_lock); +static bool phy_drv_wol_enabled(struct phy_device *phydev) +{ + struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; + + phy_ethtool_get_wol(phydev, &wol); + + return wol.wolopts != 0; +} + static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) { struct device_driver *drv = phydev->mdio.dev.driver; @@ -277,6 +286,12 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) if (!drv || !phydrv->suspend) return false; + /* If the PHY on the mido bus is not attached but has WOL enabled + * we cannot suspend the PHY. + */ + if (!netdev && phy_drv_wol_enabled(phydev)) + return false; + /* PHY not attached? May suspend if the PHY has not already been * suspended as part of a prior call to phy_disconnect() -> * phy_detach() -> phy_suspend() because the parent netdev might be the @@ -1860,7 +1875,6 @@ EXPORT_SYMBOL(phy_detach); int phy_suspend(struct phy_device *phydev) { - struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; struct net_device *netdev = phydev->attached_dev; struct phy_driver *phydrv = phydev->drv; int ret; @@ -1868,8 +1882,8 @@ int phy_suspend(struct phy_device *phydev) if (phydev->suspended) return 0; - phy_ethtool_get_wol(phydev, &wol); - phydev->wol_enabled = wol.wolopts || (netdev && netdev->wol_enabled); + phydev->wol_enabled = phy_drv_wol_enabled(phydev) || + (netdev && netdev->wol_enabled); /* If the device has WOL enabled, we cannot suspend the PHY */ if (phydev->wol_enabled && !(phydrv->flags & PHY_ALWAYS_CALL_SUSPEND)) return -EBUSY; diff --git a/drivers/pci/controller/pcie-phytium-ep.c b/drivers/pci/controller/pcie-phytium-ep.c index e25c7cdfb4e49144bd453c99b514f69a504bd81a..2f75ee1d2aeeda89b7ec4c044547299fff9af18f 100644 --- a/drivers/pci/controller/pcie-phytium-ep.c +++ b/drivers/pci/controller/pcie-phytium-ep.c @@ -19,6 +19,8 @@ #include "pcie-phytium-ep.h" #include "pcie-phytium-register.h" +#define PHYTIUM_PCIE_EP_DRIVER_VERSION "1.1.2" + #define PHYTIUM_PCIE_EP_IRQ_PCI_ADDR_NONE 0x0 #define PHYTIUM_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x1 @@ -323,6 +325,18 @@ static int phytium_pcie_ep_start(struct pci_epc *epc) return 0; } +static const struct pci_epc_features phytium_pcie_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = false, +}; + +static const struct pci_epc_features* +phytium_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no) +{ + return &phytium_pcie_epc_features; +} + static const struct pci_epc_ops phytium_pcie_epc_ops = { .write_header = phytium_pcie_ep_write_header, .set_bar = phytium_pcie_ep_set_bar, @@ -333,14 +347,34 @@ static const struct pci_epc_ops phytium_pcie_epc_ops = { .get_msi = phytium_pcie_ep_get_msi, .raise_irq = phytium_pcie_ep_raise_irq, .start = phytium_pcie_ep_start, + .get_features = phytium_pcie_ep_get_features, }; +static const struct phytium_pcie_ep_config pcie_ep_1p0_config = +{ + .hpb_perf_base_limit_offs = 0xA30, +}; +static const struct phytium_pcie_ep_config pcie_ep_2p0_config = +{ + .hpb_perf_base_limit_offs = 0xA40, +}; + +static const struct of_device_id phytium_pcie_ep_of_match[] = { + { .compatible = "phytium,pcie-ep-1.0", + .data = &pcie_ep_1p0_config }, + { .compatible = "phytium,pcie-ep-2.0", + .data = &pcie_ep_2p0_config }, + { }, +}; static int phytium_pcie_ep_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + const struct of_device_id *match = NULL; struct phytium_pcie_ep *priv = NULL; + const struct phytium_pcie_ep_config *pcie_ep_config = + &pcie_ep_2p0_config; struct resource *res; struct device_node *np = dev->of_node; struct pci_epc *epc; @@ -350,6 +384,13 @@ static int phytium_pcie_ep_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + match = of_match_node(phytium_pcie_ep_of_match, pdev->dev.of_node); + if (match && match->data) { + pcie_ep_config = match->data; + } + priv->hpb_perf_base_limit_offs = + pcie_ep_config->hpb_perf_base_limit_offs; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); priv->reg_base = devm_ioremap_resource(dev, res); if (IS_ERR(priv->reg_base)) { @@ -421,13 +462,13 @@ static int phytium_pcie_ep_probe(struct platform_device *pdev) & C0_PREF_BASE_MASK) << C0_PREF_BASE_SHIFT; value |= (((lower_32_bits(priv->mem_res->end) >> C0_PREF_VALUE_SHIFT) & C0_PREF_LIMIT_MASK) << C0_PREF_LIMIT_SHIFT); - phytium_hpb_writel(priv, PHYTIUM_HPB_C0_PREF_BASE_LIMIT, value); + phytium_hpb_writel(priv, priv->hpb_perf_base_limit_offs, value); value = ((upper_32_bits(priv->mem_res->start) >> C0_PREF_UP32_VALUE_SHIFT) & C0_PREF_BASE_UP32_MASK) << C0_PREF_BASE_UP32_SHIFT; value |= (((upper_32_bits(priv->mem_res->end) >> C0_PREF_UP32_VALUE_SHIFT) & C0_PREF_LIMIT_UP32_MASK) << C0_PREF_LIMIT_UP32_SHIFT); - phytium_hpb_writel(priv, PHYTIUM_HPB_C0_PREF_BASE_LIMIT_UP32, value); + phytium_hpb_writel(priv, priv->hpb_perf_base_limit_offs + 0x04, value); dev_dbg(dev, "exit %s successful\n", __func__); return 0; @@ -448,11 +489,6 @@ static int phytium_pcie_ep_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id phytium_pcie_ep_of_match[] = { - { .compatible = "phytium,pd2008-pcie-ep" }, - { }, -}; - static struct platform_driver phytium_pcie_ep_driver = { .driver = { .name = "phytium-pcie-ep", @@ -461,9 +497,10 @@ static struct platform_driver phytium_pcie_ep_driver = { .probe = phytium_pcie_ep_probe, .remove = phytium_pcie_ep_remove, }; - +MODULE_DEVICE_TABLE(of, phytium_pcie_ep_of_match); module_platform_driver(phytium_pcie_ep_driver); MODULE_LICENSE("GPL"); +MODULE_VERSION(PHYTIUM_PCIE_EP_DRIVER_VERSION); MODULE_AUTHOR("Yang Xun "); MODULE_DESCRIPTION("Phytium PCIe Controller Endpoint driver"); diff --git a/drivers/pci/controller/pcie-phytium-ep.h b/drivers/pci/controller/pcie-phytium-ep.h index 10cece4fc542282885c6c9de61522dd8aff9d3d1..57fbe521400cb0d498f5cc90885638c2f657b8b8 100644 --- a/drivers/pci/controller/pcie-phytium-ep.h +++ b/drivers/pci/controller/pcie-phytium-ep.h @@ -8,13 +8,20 @@ #ifndef __PCIE_PHYTIUM_EP_H__ #define __PCIE_PHYTIUM_EP_H__ +#include #include "pcie-phytium-register.h" #define IRQ_MAPPING_SIZE 0x1000 + +struct phytium_pcie_ep_config { + u32 hpb_perf_base_limit_offs; +}; + struct phytium_pcie_ep { void __iomem *reg_base; struct resource *mem_res; void __iomem *hpb_base; + u32 hpb_perf_base_limit_offs; unsigned int max_regions; unsigned long ob_region_map; phys_addr_t *ob_addr; diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 25dbe85c4217585505510aeb19cadb7f7f491bdb..f74c83f0335077507b45e0ec1037cd482f857f32 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -900,6 +900,10 @@ int pci_iov_init(struct pci_dev *dev) if (!pci_is_pcie(dev)) return -ENODEV; + if ((dev->vendor == PCI_VENDOR_ID_PHYTIUM) + && (dev->device == PCI_DEVICE_ID_PHYTIUM_PE220X)) + return -ENODEV; + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); if (pos) return sriov_init(dev, pos); diff --git a/drivers/perf/phytium/phytium_ddr_pmu.c b/drivers/perf/phytium/phytium_ddr_pmu.c index a31bf29c874b14df4032fc4eb05ab220267c3e66..8fd6f997c4b117dcc07f36a265ccad05fa2a7a6a 100644 --- a/drivers/perf/phytium/phytium_ddr_pmu.c +++ b/drivers/perf/phytium/phytium_ddr_pmu.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -30,32 +31,60 @@ #undef pr_fmt #define pr_fmt(fmt) "phytium_ddr_pmu: " fmt -#define PHYTIUM_DDR_MAX_COUNTERS 8 -#define DDR_START_TIMER 0x000 -#define DDR_STOP_TIMER 0x004 -#define DDR_CLEAR_EVENT 0x008 -#define DDR_SET_TIMER_L 0x00c -#define DDR_SET_TIMER_H 0x010 -#define DDR_TRIG_MODE 0x014 -#define DDR_NOW_STATE 0x0e0 -#define DDR_EVENT_CYCLES 0x0e4 -#define DDR_TPOINT_END_L 0x0e4 -#define DDR_TPOINT_END_H 0x0e8 -#define DDR_STATE_STOP 0x0ec -#define DDR_EVENT_RXREQ 0x100 -#define DDR_EVENT_RXDAT 0x104 -#define DDR_EVENT_TXDAT 0x108 -#define DDR_EVENT_RXREQ_RNS 0x10c -#define DDR_EVENT_RXREQ_WNSP 0x110 -#define DDR_EVENT_RXREQ_WNSF 0x114 -#define DDR_EVENT_BANDWIDTH 0x200 -#define DDR_W_DATA_BASE 0x200 -#define DDR_CLK_FRE 0xe00 -#define DDR_DATA_WIDTH 0xe04 +#define PHYTIUM_DDR_MAX_COUNTERS 8 +#define DDR_PERF_DRIVER_VERSION "1.3.0" + +#define DDR_START_TIMER 0x000 +#define DDR_STOP_TIMER 0x004 +#define DDR_CLEAR_EVENT 0x008 +#define DDR_SET_TIMER_L 0x00c +#define DDR_SET_TIMER_H 0x010 +#define DDR_TRIG_MODE 0x014 +#define DDR_NOW_STATE 0x0e0 +#define DDR_EVENT_CYCLES 0x0e4 +#define DDR_TPOINT_END_L 0x0e4 +#define DDR_TPOINT_END_H 0x0e8 +#define DDR_STATE_STOP 0x0ec +#define DDR_EVENT_RXREQ 0x100 +#define DDR_EVENT_RXDAT 0x104 +#define DDR_EVENT_TXDAT 0x108 +#define DDR_EVENT_RXREQ_RNS 0x10c +#define DDR_EVENT_RXREQ_WNSP 0x110 +#define DDR_EVENT_RXREQ_WNSF 0x114 +#define DDR_EVENT_BANDWIDTH 0x200 +#define DDR_W_DATA_BASE 0x200 +#define DDR_CLK_FRE 0xe00 +#define DDR_DATA_WIDTH 0xe04 + +#define DDR_PMU_OFL_STOP_TYPE_VAL 0x10 + +#define SOC_ID_PS230XX 0x8 +#define SOC_ID_PS240XX 0x6 +#define MIDR_PSXX 0x700F8620 #define to_phytium_ddr_pmu(p) (container_of(p, struct phytium_ddr_pmu, pmu)) +enum { + PS230XX = 0x01, + PS240XX = 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 { @@ -66,15 +95,17 @@ struct phytium_ddr_pmu_hwevents { struct phytium_ddr_pmu { struct device *dev; void __iomem *base; - void __iomem *csr_base; + void __iomem *cfg_base; + void __iomem *irq_reg; struct pmu pmu; struct phytium_ddr_pmu_hwevents pmu_events; u32 die_id; u32 ddr_id; u32 pmu_id; - int bit_idx; + int irq_bit; int on_cpu; int irq; + int soc_version; struct hlist_node node; }; @@ -87,20 +118,20 @@ 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) + struct device_attribute *attr, + char *buf) { struct dev_ext_attribute *eattr; eattr = container_of(attr, struct dev_ext_attribute, attr); - return sprintf(buf, "%s\n", (char *)eattr->var); } ssize_t phytium_ddr_pmu_event_sysfs_show(struct device *dev, - struct device_attribute *attr, - char *page) + struct device_attribute *attr, + char *page) { struct dev_ext_attribute *eattr; @@ -110,7 +141,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)); @@ -118,22 +149,21 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, return cpumap_print_to_pagebuf(true, buf, cpumask_of(ddr_pmu->on_cpu)); } -#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ - (&((struct dev_ext_attribute[]){ \ - { __ATTR(_name, 0444, _func, NULL), (void *)_config } })[0] \ - .attr.attr) +#define PHYTIUM_PMU_ATTR(_name, _func, _config) \ + (&((struct dev_ext_attribute[]){ \ + { __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) +#define PHYTIUM_DDR_PMU_FORMAT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_format_sysfs_show, \ + (void *)_config) -#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ - PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ - (unsigned long)_config) +#define PHYTIUM_DDR_PMU_EVENT_ATTR(_name, _config) \ + PHYTIUM_PMU_ATTR(_name, phytium_ddr_pmu_event_sysfs_show, \ + (unsigned long)_config) static struct attribute *phytium_ddr_pmu_format_attr[] = { PHYTIUM_DDR_PMU_FORMAT_ATTR(event, "config:0-2"), - PHYTIUM_DDR_PMU_FORMAT_ATTR(timer, "config1:0-31"), NULL, }; @@ -177,11 +207,6 @@ static const struct attribute_group *phytium_ddr_pmu_attr_groups[] = { NULL, }; -static u32 phytium_ddr_pmu_get_event_timer(struct perf_event *event) -{ - return FIELD_GET(GENMASK(31, 0), event->attr.config1); -} - static u64 phytium_ddr_pmu_read_counter(struct phytium_ddr_pmu *ddr_pmu, struct hw_perf_event *hwc) { @@ -221,18 +246,24 @@ static void phytium_ddr_pmu_enable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - val = readl(ddr_pmu->csr_base); + if (ddr_pmu->soc_version == PS240XX) + return; + + val = readl(ddr_pmu->cfg_base); val |= 0xF; - writel(val, ddr_pmu->csr_base); + writel(val, ddr_pmu->cfg_base); } static void phytium_ddr_pmu_disable_clk(struct phytium_ddr_pmu *ddr_pmu) { u32 val; - val = readl(ddr_pmu->csr_base); + if (ddr_pmu->soc_version == PS240XX) + return; + + val = readl(ddr_pmu->cfg_base); val &= ~(0xF); - writel(val, ddr_pmu->csr_base); + writel(val, ddr_pmu->cfg_base); } static void phytium_ddr_pmu_clear_all_counters(struct phytium_ddr_pmu *ddr_pmu) @@ -250,29 +281,6 @@ static void phytium_ddr_pmu_stop_all_counters(struct phytium_ddr_pmu *ddr_pmu) writel(0x1, ddr_pmu->base + DDR_STOP_TIMER); } -static void phytium_ddr_pmu_set_timer(struct phytium_ddr_pmu *ddr_pmu, - u32 th_val) -{ - u32 val; - - val = readl(ddr_pmu->base + DDR_SET_TIMER_L); - val = readl(ddr_pmu->base + DDR_SET_TIMER_H); - - writel(th_val, ddr_pmu->base + DDR_SET_TIMER_L); - writel(0, ddr_pmu->base + DDR_SET_TIMER_H); -} - -static void phytium_ddr_pmu_reset_timer(struct phytium_ddr_pmu *ddr_pmu) -{ - u32 val; - - val = readl(ddr_pmu->base + DDR_SET_TIMER_L); - val = readl(ddr_pmu->base + DDR_SET_TIMER_H); - - writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_L); - writel(0xFFFFFFFF, ddr_pmu->base + DDR_SET_TIMER_H); -} - static unsigned long phytium_ddr_pmu_get_stop_state(struct phytium_ddr_pmu *ddr_pmu) { @@ -287,7 +295,7 @@ phytium_ddr_pmu_get_irq_flag(struct phytium_ddr_pmu *ddr_pmu) { unsigned long val; - val = (unsigned long)readl(ddr_pmu->csr_base + 4); + val = (unsigned long)readl(ddr_pmu->irq_reg); return val; } @@ -322,7 +330,6 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct phytium_ddr_pmu *ddr_pmu; - u32 event_timer; if (event->attr.type != event->pmu->type) return -ENOENT; @@ -343,10 +350,6 @@ int phytium_ddr_pmu_event_init(struct perf_event *event) if (ddr_pmu->on_cpu == -1) return -EINVAL; - event_timer = phytium_ddr_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_ddr_pmu_set_timer(ddr_pmu, event_timer); - hwc->idx = -1; hwc->config_base = event->attr.config; @@ -406,17 +409,12 @@ 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; - u32 event_timer; 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); - event_timer = phytium_ddr_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_ddr_pmu_reset_timer(ddr_pmu); - perf_event_update_userpage(event); ddr_pmu->pmu_events.hw_events[hwc->idx] = NULL; } @@ -443,6 +441,12 @@ void phytium_ddr_pmu_disable(struct pmu *pmu) phytium_ddr_pmu_stop_all_counters(ddr_pmu); } +void phytium_ddr_pmu_reset(struct phytium_ddr_pmu *ddr_pmu) +{ + phytium_ddr_pmu_disable_clk(ddr_pmu); + phytium_ddr_pmu_clear_all_counters(ddr_pmu); +} + static const struct acpi_device_id phytium_ddr_pmu_acpi_match[] = { { "PHYT0043", @@ -456,14 +460,13 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) struct phytium_ddr_pmu *ddr_pmu = dev_id; struct perf_event *event; unsigned long overflown, stop_state; - int idx; unsigned long *used_mask = ddr_pmu->pmu_events.used_mask; - + int idx; int event_added = bitmap_weight(used_mask, PHYTIUM_DDR_MAX_COUNTERS); overflown = phytium_ddr_pmu_get_irq_flag(ddr_pmu); - if (!test_bit(ddr_pmu->bit_idx, &overflown)) + if (!test_bit(ddr_pmu->irq_bit, &overflown)) return IRQ_NONE; stop_state = phytium_ddr_pmu_get_stop_state(ddr_pmu); @@ -475,8 +478,10 @@ static irqreturn_t phytium_ddr_pmu_overflow_handler(int irq, void *dev_id) continue; phytium_ddr_pmu_event_update(event); } + phytium_ddr_pmu_clear_all_counters(ddr_pmu); - phytium_ddr_pmu_start_all_counters(ddr_pmu); + if ((stop_state & DDR_PMU_OFL_STOP_TYPE_VAL) == 0) + phytium_ddr_pmu_start_all_counters(ddr_pmu); return IRQ_HANDLED; } @@ -516,7 +521,13 @@ static int phytium_ddr_pmu_init_irq(struct phytium_ddr_pmu *ddr_pmu, static int phytium_ddr_pmu_init_data(struct platform_device *pdev, struct phytium_ddr_pmu *ddr_pmu) { - struct resource *res, *clkres; + 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)) { @@ -536,7 +547,7 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - ddr_pmu->bit_idx = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; + ddr_pmu->irq_bit = ddr_pmu->ddr_id * 2 + ddr_pmu->pmu_id; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ddr_pmu->base = devm_ioremap_resource(&pdev->dev, res); @@ -552,15 +563,29 @@ static int phytium_ddr_pmu_init_data(struct platform_device *pdev, dev_err(&pdev->dev, "failed for get ddr_pmu clk resource.\n"); return -EINVAL; } + ddr_pmu->cfg_base = devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); + if (IS_ERR(ddr_pmu->cfg_base)) { + dev_err(&pdev->dev, "ioremap failed for ddr_pmu clk resource\n"); + return PTR_ERR(ddr_pmu->cfg_base); + } - ddr_pmu->csr_base = - devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); - if (IS_ERR(ddr_pmu->csr_base)) { - dev_err(&pdev->dev, - "ioremap failed for ddr_pmu csr resource\n"); - return PTR_ERR(ddr_pmu->csr_base); + if (ddr_pmu->soc_version == PS240XX) { + irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!irqres) { + dev_err(&pdev->dev, "failed for get ddr_pmu irq resource.\n"); + return -EINVAL; + } + ddr_pmu->irq_reg = devm_ioremap(&pdev->dev, irqres->start, resource_size(irqres)); + if (IS_ERR(ddr_pmu->irq_reg)) { + dev_err(&pdev->dev, "ioremap failed for ddr_pmu irq resource\n"); + return PTR_ERR(ddr_pmu->irq_reg); + } + } else { + ddr_pmu->irq_reg = ddr_pmu->cfg_base + 0x4; } + phytium_ddr_pmu_reset(ddr_pmu); + return 0; } @@ -599,8 +624,8 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) if (ret) return ret; - ret = cpuhp_state_add_instance( - phytium_ddr_pmu_hp_state, &ddr_pmu->node); + ret = cpuhp_state_add_instance(phytium_ddr_pmu_hp_state, + &ddr_pmu->node); if (ret) { dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); return ret; @@ -622,22 +647,19 @@ static int phytium_ddr_pmu_probe(struct platform_device *pdev) .stop = phytium_ddr_pmu_event_stop, .read = phytium_ddr_pmu_event_update, .attr_groups = phytium_ddr_pmu_attr_groups, - .capabilities = PERF_PMU_CAP_NO_EXCLUDE, }; ret = perf_pmu_register(&ddr_pmu->pmu, name, -1); if (ret) { dev_err(ddr_pmu->dev, "DDR PMU register failed!\n"); - cpuhp_state_remove_instance_nocalls( - phytium_ddr_pmu_hp_state, + cpuhp_state_remove_instance_nocalls(phytium_ddr_pmu_hp_state, &ddr_pmu->node); } phytium_ddr_pmu_enable_clk(ddr_pmu); - pr_info("Phytium DDR PMU: "); - pr_info(" die_id = %d ddr_id = %d pmu_id = %d.\n", ddr_pmu->die_id, - ddr_pmu->ddr_id, ddr_pmu->pmu_id); + pr_info("die%d_ddr%d_pmu%d on cpu%d.\n", ddr_pmu->die_id, + ddr_pmu->ddr_id, ddr_pmu->pmu_id, ddr_pmu->on_cpu); return ret; } @@ -649,8 +671,8 @@ static int phytium_ddr_pmu_remove(struct platform_device *pdev) phytium_ddr_pmu_disable_clk(ddr_pmu); perf_pmu_unregister(&ddr_pmu->pmu); - cpuhp_state_remove_instance_nocalls( - phytium_ddr_pmu_hp_state, &ddr_pmu->node); + cpuhp_state_remove_instance_nocalls(phytium_ddr_pmu_hp_state, + &ddr_pmu->node); return 0; } @@ -750,4 +772,6 @@ module_exit(phytium_ddr_pmu_module_exit); MODULE_DESCRIPTION("Phytium DDR PMU driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(DDR_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); +MODULE_AUTHOR("Tan Rui "); diff --git a/drivers/perf/phytium/phytium_pcie_pmu.c b/drivers/perf/phytium/phytium_pcie_pmu.c index e1dfbf11e6514fe89bf9eea52974d29a64b12fa9..80a931e4e4aee3c5584479a400892132d1889ae1 100644 --- a/drivers/perf/phytium/phytium_pcie_pmu.c +++ b/drivers/perf/phytium/phytium_pcie_pmu.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -31,49 +32,72 @@ #define pr_fmt(fmt) "phytium_pcie_pmu: " fmt #define PHYTIUM_PCIE_MAX_COUNTERS 18 - -#define PCIE_START_TIMER 0x000 -#define PCIE_STOP_TIMER 0x004 -#define PCIE_CLEAR_EVENT 0x008 -#define PCIE_SET_TIMER_L 0x00c -#define PCIE_SET_TIMER_H 0x010 -#define PCIE_TRIG_MODE 0x014 - -#define PCIE_NOW_STATE 0x0e0 -#define PCIE_EVENT_CYCLES 0x0e4 -#define PCIE_TPOINT_END_L 0x0e4 -#define PCIE_TPOINT_END_H 0x0e8 -#define PCIE_STATE_STOP 0x0ec - -#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_RDELAY_TIME 0x300 -#define PCIE_RDELAY_TIME_BASE 0x300 - -#define PCIE_EVENT_WDELAY_TIME 0x700 -#define PCIE_WDELAY_TIME_BASE 0x700 - -#define PCIE_CLK_FRE 0xe00 -#define PCIE_DATA_WIDTH 0xe04 +#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_EVENT_CYCLES 0x0e4 +#define PCIE_TPOINT_END_L 0x0e4 +#define PCIE_TPOINT_END_H 0x0e8 +#define PCIE_STATE_STOP 0x0ec + +#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_RDELAY_TIME 0x300 +#define PCIE_RDELAY_TIME_BASE 0x300 + +#define PCIE_EVENT_WDELAY_TIME 0x700 +#define PCIE_WDELAY_TIME_BASE 0x700 + +#define PCIE_DATA_WIDTH 0xe04 + +#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 to_phytium_pcie_pmu(p) (container_of(p, struct phytium_pcie_pmu, pmu)) +enum { + PS230XX = 0x1, + PS240XX = 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; struct phytium_pcie_pmu_hwevents { @@ -84,18 +108,21 @@ struct phytium_pcie_pmu_hwevents { struct phytium_pcie_pmu { struct device *dev; void __iomem *base; - void __iomem *csr_base; + void __iomem *cfg_base; void __iomem *irq_reg; struct pmu pmu; struct phytium_pcie_pmu_hwevents pmu_events; u32 die_id; + u32 pcie_id; u32 pmu_id; int on_cpu; int irq; + int irq_bit; struct hlist_node node; int ctrler_id; int real_ctrler; u32 clk_bits; + u32 soc_version; }; #define GET_PCIE_EVENTID(hwc) (hwc->config_base & 0x1F) @@ -158,7 +185,6 @@ static ssize_t cpumask_show(struct device *dev, struct device_attribute *attr, static struct attribute *phytium_pcie_pmu_format_attr[] = { PHYTIUM_PCIE_PMU_FORMAT_ATTR(event, "config:0-4"), PHYTIUM_PCIE_PMU_FORMAT_ATTR(ctrler, "config:8-10"), - PHYTIUM_PCIE_PMU_FORMAT_ATTR(timer, "config1:0-31"), NULL, }; @@ -217,11 +243,6 @@ static u32 phytium_pcie_pmu_get_event_ctrler(struct perf_event *event) return FIELD_GET(GENMASK(10, 8), event->attr.config); } -static u32 phytium_pcie_pmu_get_event_timer(struct perf_event *event) -{ - return FIELD_GET(GENMASK(31, 0), event->attr.config1); -} - static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, struct hw_perf_event *hwc) { @@ -231,12 +252,16 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, u64 val64 = 0; int i; u32 counter_offset = pcie_counter_reg_offset[idx]; + u32 rdelay_num = 127; if (!EVENT_VALID(idx)) { dev_err(pcie_pmu->dev, "Unsupported event index:%d!\n", idx); return 0; } + if (pcie_pmu->soc_version == PS240XX && pcie_pmu->pmu_id == 3) + rdelay_num = 63; + switch (idx) { case 0: cycle_l = readl(pcie_pmu->base + counter_offset); @@ -251,7 +276,7 @@ static u64 phytium_pcie_pmu_read_counter(struct phytium_pcie_pmu *pcie_pmu, } break; case 16: - for (i = 0; i <= 127; i = i + 2) { + for (i = 0; i <= rdelay_num; i = i + 2) { rdelay_l = readl(pcie_pmu->base + counter_offset + 4 * i); rdelay_h = readl(pcie_pmu->base + counter_offset + @@ -279,38 +304,40 @@ static void phytium_pcie_pmu_enable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; - val = readl(pcie_pmu->csr_base); + val = readl(pcie_pmu->cfg_base); val |= (pcie_pmu->clk_bits); - writel(val, pcie_pmu->csr_base); + writel(val, pcie_pmu->cfg_base); } static void phytium_pcie_pmu_disable_clk(struct phytium_pcie_pmu *pcie_pmu) { u32 val; - val = readl(pcie_pmu->csr_base); + val = readl(pcie_pmu->cfg_base); val &= ~(pcie_pmu->clk_bits); - writel(val, pcie_pmu->csr_base); + writel(val, pcie_pmu->cfg_base); } static void phytium_pcie_pmu_select_ctrler(struct phytium_pcie_pmu *pcie_pmu) { - u32 val, offset = 0; - - if (pcie_pmu->pmu_id != 2) - offset = 0xc; + u32 val, offset; + u32 mask = 0xfffffffc; - val = readl(pcie_pmu->csr_base + offset); - - if (pcie_pmu->pmu_id == 2) { - val &= 0xffffffcf; - val |= pcie_pmu->real_ctrler; + if (pcie_pmu->soc_version == PS230XX) { + if (pcie_pmu->pmu_id == 2) { + mask = 0xffffffcf; + offset = 0x0; + } else + offset = 0xc; } else { - val &= 0xfffffffc; - val |= pcie_pmu->real_ctrler; + offset = 0x170; } - writel(val, pcie_pmu->csr_base + offset); + val = readl(pcie_pmu->cfg_base + offset); + val &= mask; + val |= pcie_pmu->real_ctrler; + writel(val, pcie_pmu->cfg_base + offset); + } static void @@ -331,29 +358,6 @@ phytium_pcie_pmu_stop_all_counters(struct phytium_pcie_pmu *pcie_pmu) writel(0x1, pcie_pmu->base + PCIE_STOP_TIMER); } -static void phytium_pcie_pmu_set_timer(struct phytium_pcie_pmu *pcie_pmu, - u32 th_val) -{ - u32 val; - - val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); - val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); - - writel(th_val, pcie_pmu->base + PCIE_SET_TIMER_L); - writel(0, pcie_pmu->base + PCIE_SET_TIMER_H); -} - -static void phytium_pcie_pmu_reset_timer(struct phytium_pcie_pmu *pcie_pmu) -{ - u32 val; - - val = readl(pcie_pmu->base + PCIE_SET_TIMER_L); - val = readl(pcie_pmu->base + PCIE_SET_TIMER_H); - - writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_L); - writel(0xFFFFFFFF, pcie_pmu->base + PCIE_SET_TIMER_H); -} - static unsigned long phytium_pcie_pmu_get_stop_state(struct phytium_pcie_pmu *pcie_pmu) { @@ -403,7 +407,7 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; struct phytium_pcie_pmu *pcie_pmu; - u32 event_ctrler, event_timer; + u32 event_ctrler; if (event->attr.type != event->pmu->type) return -ENOENT; @@ -424,58 +428,84 @@ int phytium_pcie_pmu_event_init(struct perf_event *event) if (pcie_pmu->on_cpu == -1) return -EINVAL; - event_timer = phytium_pcie_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_pcie_pmu_set_timer(pcie_pmu, event_timer); - - event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); - switch (pcie_pmu->pmu_id) { - case 0: - if (event_ctrler != 0) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu0!\n", - event_ctrler); - return -EINVAL; + if (pcie_pmu->soc_version == PS240XX) { + event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); + if (pcie_pmu->pmu_id == 2) { + if (event_ctrler == 0) + event_ctrler = 2; + else if ((event_ctrler < 2) || (event_ctrler > 3)) { + dev_warn(pcie_pmu->dev, "Wrong ctrler id(%d) for pcie-pmu2!\n", + event_ctrler); + return -EINVAL; + } + if (pcie_pmu->ctrler_id != event_ctrler) { + pcie_pmu->ctrler_id = event_ctrler; + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + phytium_pcie_pmu_select_ctrler(pcie_pmu); + } + } else { + if (event_ctrler != 0) { + dev_warn(pcie_pmu->dev, "Don't need to set ctrler id(%d) for pcie-pmu%d!\n", + event_ctrler, pcie_pmu->pmu_id); + return -EINVAL; + } + pcie_pmu->ctrler_id = pcie_pmu->pmu_id; + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; } - break; - case 1: - if ((event_ctrler < 1) || (event_ctrler > 3)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu1!\n", - event_ctrler); + } else { + event_ctrler = phytium_pcie_pmu_get_event_ctrler(event); + switch (pcie_pmu->pmu_id) { + case 0: + if (event_ctrler != 0) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu0!\n", + event_ctrler); + return -EINVAL; + } + break; + case 1: + if (event_ctrler == 0) + event_ctrler = 1; + else if ((event_ctrler < 1) || (event_ctrler > 3)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu1!\n", + event_ctrler); + return -EINVAL; + } + break; + case 2: + if (event_ctrler == 0) + event_ctrler = 4; + else if ((event_ctrler < 4) || (event_ctrler > 7)) { + dev_warn(pcie_pmu->dev, + "Wrong ctrler id(%d) for pcie-pmu2!\n", + event_ctrler); + return -EINVAL; + } + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); return -EINVAL; } - break; - case 2: - if ((event_ctrler < 4) || (event_ctrler > 7)) { - dev_warn(pcie_pmu->dev, - "Wrong ctrler id(%d) for pcie-pmu2!\n", - event_ctrler); + + pcie_pmu->ctrler_id = event_ctrler; + switch (pcie_pmu->pmu_id) { + case 0: + case 1: + pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; + break; + case 2: + pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; + break; + default: + dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", + pcie_pmu->pmu_id); return -EINVAL; } - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - return -EINVAL; + phytium_pcie_pmu_select_ctrler(pcie_pmu); } - pcie_pmu->ctrler_id = event_ctrler; - switch (pcie_pmu->pmu_id) { - case 0: - case 1: - pcie_pmu->real_ctrler = pcie_pmu->ctrler_id; - break; - case 2: - pcie_pmu->real_ctrler = (pcie_pmu->ctrler_id - 4) * 16; - break; - default: - dev_err(pcie_pmu->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - return -EINVAL; - } - phytium_pcie_pmu_select_ctrler(pcie_pmu); - hwc->idx = -1; hwc->config_base = event->attr.config; @@ -534,17 +564,12 @@ 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; - u32 event_timer; 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); - event_timer = phytium_pcie_pmu_get_event_timer(event); - if (event_timer != 0) - phytium_pcie_pmu_reset_timer(pcie_pmu); - perf_event_update_userpage(event); pcie_pmu->pmu_events.hw_events[hwc->idx] = NULL; } @@ -571,6 +596,12 @@ void phytium_pcie_pmu_disable(struct pmu *pmu) phytium_pcie_pmu_stop_all_counters(pcie_pmu); } +void phytium_pcie_pmu_reset(struct phytium_pcie_pmu *pcie_pmu) +{ + phytium_pcie_pmu_disable_clk(pcie_pmu); + phytium_pcie_pmu_clear_all_counters(pcie_pmu); +} + static const struct acpi_device_id phytium_pcie_pmu_acpi_match[] = { { "PHYT0044", @@ -590,7 +621,7 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) overflown = phytium_pcie_pmu_get_irq_flag(pcie_pmu); - if (!test_bit(pcie_pmu->pmu_id + 4, &overflown)) + if (!test_bit(pcie_pmu->irq_bit, &overflown)) return IRQ_NONE; stop_state = phytium_pcie_pmu_get_stop_state(pcie_pmu); @@ -603,7 +634,8 @@ static irqreturn_t phytium_pcie_pmu_overflow_handler(int irq, void *dev_id) phytium_pcie_pmu_event_update(event); } phytium_pcie_pmu_clear_all_counters(pcie_pmu); - phytium_pcie_pmu_start_all_counters(pcie_pmu); + if ((stop_state & PCIE_PMU_OFL_STOP_TYPE_VAL) == 0) + phytium_pcie_pmu_start_all_counters(pcie_pmu); return IRQ_HANDLED; } @@ -639,9 +671,14 @@ static int phytium_pcie_pmu_init_irq(struct phytium_pcie_pmu *pcie_pmu, } static int phytium_pcie_pmu_init_data(struct platform_device *pdev, - struct phytium_pcie_pmu *pcie_pmu) + 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)) { @@ -655,20 +692,46 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - switch (pcie_pmu->pmu_id) { - case 0: - pcie_pmu->clk_bits = 0x1; - break; - case 1: - pcie_pmu->clk_bits = 0xe; - break; - case 2: - pcie_pmu->clk_bits = 0xf; - break; - default: - dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", - pcie_pmu->pmu_id); - break; + if (pcie_pmu->soc_version == PS230XX) { + switch (pcie_pmu->pmu_id) { + case 0: + pcie_pmu->clk_bits = 0x1; + break; + case 1: + pcie_pmu->clk_bits = 0xe; + break; + case 2: + pcie_pmu->clk_bits = 0xf; + break; + default: + dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", pcie_pmu->pmu_id); + break; + } + + pcie_pmu->irq_bit = pcie_pmu->pmu_id + 4; + } else { + if (device_property_read_u32(&pdev->dev, "phytium,pcie-id", &pcie_pmu->pcie_id)) { + dev_err(&pdev->dev, "Can not read phytium,pcie-id!\n"); + return -EINVAL; + } + + switch (pcie_pmu->pmu_id) { + case 0: + case 3: + pcie_pmu->clk_bits = 0x1; + break; + case 1: + pcie_pmu->clk_bits = 0x2; + break; + case 2: + pcie_pmu->clk_bits = 0xc; + break; + default: + dev_err(&pdev->dev, "Unsupported pmu id:%d!\n", pcie_pmu->pmu_id); + break; + } + + pcie_pmu->irq_bit = pcie_pmu->pcie_id * 4 + pcie_pmu->pmu_id + 16; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -684,12 +747,12 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - pcie_pmu->csr_base = + pcie_pmu->cfg_base = devm_ioremap(&pdev->dev, clkres->start, resource_size(clkres)); - if (IS_ERR(pcie_pmu->csr_base)) { + if (IS_ERR(pcie_pmu->cfg_base)) { dev_err(&pdev->dev, "ioremap failed for pcie_pmu csr resource\n"); - return PTR_ERR(pcie_pmu->csr_base); + return PTR_ERR(pcie_pmu->cfg_base); } irqres = platform_get_resource(pdev, IORESOURCE_MEM, 2); @@ -707,11 +770,13 @@ static int phytium_pcie_pmu_init_data(struct platform_device *pdev, return PTR_ERR(pcie_pmu->irq_reg); } + phytium_pcie_pmu_reset(pcie_pmu); + return 0; } static int phytium_pcie_pmu_dev_probe(struct platform_device *pdev, - struct phytium_pcie_pmu *pcie_pmu) + struct phytium_pcie_pmu *pcie_pmu) { int ret; @@ -752,8 +817,12 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) return ret; } - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", - pcie_pmu->die_id, pcie_pmu->pmu_id); + if (pcie_pmu->soc_version == PS230XX) + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie_pmu%u", + pcie_pmu->die_id, pcie_pmu->pmu_id); + else + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "phyt%u_pcie%u_pmu%u", + pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id); pcie_pmu->pmu = (struct pmu){ .name = name, .module = THIS_MODULE, @@ -767,7 +836,6 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) .stop = phytium_pcie_pmu_event_stop, .read = phytium_pcie_pmu_event_update, .attr_groups = phytium_pcie_pmu_attr_groups, - .capabilities = PERF_PMU_CAP_NO_EXCLUDE, }; ret = perf_pmu_register(&pcie_pmu->pmu, name, -1); @@ -780,9 +848,12 @@ static int phytium_pcie_pmu_probe(struct platform_device *pdev) phytium_pcie_pmu_enable_clk(pcie_pmu); - pr_info("Phytium PCIe PMU: "); - pr_info("die_id = %d pmu_id = %d.\n", pcie_pmu->die_id, - pcie_pmu->pmu_id); + if (pcie_pmu->soc_version == PS230XX) + pr_info("die%d_pcie_pmu%d on cpu%d.\n", + pcie_pmu->die_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); + else + pr_info("die%d_pcie%d_pmu%d on cpu%d.\n", + pcie_pmu->die_id, pcie_pmu->pcie_id, pcie_pmu->pmu_id, pcie_pmu->on_cpu); return ret; } @@ -824,6 +895,7 @@ int phytium_pcie_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) pcie_pmu->on_cpu = cpu; WARN_ON(irq_set_affinity_hint(pcie_pmu->irq, cpumask_of(cpu))); } + return 0; } @@ -870,8 +942,8 @@ static int __init phytium_pcie_pmu_module_init(void) phytium_pcie_pmu_hp_state = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, - "perf/phytium/pciepmu:online", - phytium_pcie_pmu_online_cpu, phytium_pcie_pmu_offline_cpu); + "perf/phytium/pciepmu:online", phytium_pcie_pmu_online_cpu, + phytium_pcie_pmu_offline_cpu); if (phytium_pcie_pmu_hp_state < 0) { pr_err("PCIE PMU: setup hotplug, ret = %d\n", phytium_pcie_pmu_hp_state); @@ -896,4 +968,6 @@ module_exit(phytium_pcie_pmu_module_exit); MODULE_DESCRIPTION("Phytium PCIe PMU driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(PCIE_PERF_DRIVER_VERSION); MODULE_AUTHOR("Hu Xianghua "); +MODULE_AUTHOR("Tan Rui "); diff --git a/drivers/pwm/pwm-phytium.c b/drivers/pwm/pwm-phytium.c index 98ae864a82cd60588c8964e3e27f2a76ad08b1f7..a754c1628b1f6a6b351d88f7c2be0a8bdf444c22 100644 --- a/drivers/pwm/pwm-phytium.c +++ b/drivers/pwm/pwm-phytium.c @@ -47,6 +47,8 @@ #define PWM_N(x) ((0x400)*(x)) #define MAX_PARAMETER 2 +#define PWM_DRIVER_VERSION "1.1.1" + struct phytium_pwm_state { int rst; int cntmod; @@ -588,3 +590,4 @@ module_platform_driver(pwm_phytium_driver); MODULE_DESCRIPTION("Phytium SoC PWM driver"); MODULE_AUTHOR("Yang Liu "); MODULE_LICENSE("GPL"); +MODULE_VERSION(PWM_DRIVER_VERSION); diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c index edd7430d4c052bf31c122bfac684e8edfdad4de4..6e33632f8e42fb3dc73cdcf0b4fc8bb086188672 100644 --- a/drivers/spi/spi-mem.c +++ b/drivers/spi/spi-mem.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "internals.h" @@ -142,6 +143,24 @@ static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx) static bool spi_mem_check_buswidth(struct spi_mem *mem, const struct spi_mem_op *op) { + u32 proto; + + if (op->data.dir == SPI_MEM_DATA_OUT) + proto = mem->spi->tx_proto; + else if (op->data.dir == SPI_MEM_DATA_IN) + proto = mem->spi->rx_proto; + + if (op->data.dir != SPI_MEM_NO_DATA) { + if (op->cmd.buswidth > spi_nor_get_protocol_inst_nbits(proto)) + return false; + if (op->addr.buswidth > spi_nor_get_protocol_addr_nbits(proto)) + return false; + if (op->dummy.buswidth > spi_nor_get_protocol_addr_nbits(proto)) + return false; + if (op->data.buswidth > spi_nor_get_protocol_data_nbits(proto)) + return false; + } + if (spi_check_buswidth_req(mem, op->cmd.buswidth, true)) return false; diff --git a/drivers/spi/spi-phytium-pci.c b/drivers/spi/spi-phytium-pci.c index 6ef8acfe5c85643c8a0281e63f12985fad049e36..985aa0ed5bf7d3af6561764de83d09c77be55b40 100644 --- a/drivers/spi/spi-phytium-pci.c +++ b/drivers/spi/spi-phytium-pci.c @@ -26,6 +26,7 @@ #include "spi-phytium.h" #define DRIVER_NAME "phytium_spi_pci" +#define DRIVER_VERSION "1.0.0" static int phytium_spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) @@ -109,6 +110,7 @@ static const struct pci_device_id phytium_device_pci_tbl[] = { { PCI_VDEVICE(PHYTIUM, 0xdc2c) }, {}, }; +MODULE_DEVICE_TABLE(pci, phytium_device_pci_tbl); static struct pci_driver phytium_spi_pci_driver = { .name = DRIVER_NAME, @@ -125,3 +127,4 @@ module_pci_driver(phytium_spi_pci_driver); MODULE_AUTHOR("Yiqun Zhang "); MODULE_DESCRIPTION("PCI Driver for Phytium SPI controller core"); MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/spi/spi-phytium-plat.c b/drivers/spi/spi-phytium-plat.c index 9b82c5c016092c4bb80cde8cd15685db91a11c9b..178bd90dc1a050c365ac71c25b2ee40fe9bdb3b5 100644 --- a/drivers/spi/spi-phytium-plat.c +++ b/drivers/spi/spi-phytium-plat.c @@ -29,6 +29,9 @@ #include "spi-phytium.h" #define DRIVER_NAME "phytium_spi" +#define DRIVER_VERSION "1.0.0" + +#define SPI_PHYTIUM_DEFAULT_CLK_RATE 50000000 struct phytium_spi_clk { struct phytium_spi fts; @@ -37,12 +40,14 @@ struct phytium_spi_clk { static int phytium_spi_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct phytium_spi_clk *ftsc; struct phytium_spi *fts; struct resource *mem; int ret; int num_cs; - int global_cs; + int global_cs = 0; + u32 clk_rate = SPI_PHYTIUM_DEFAULT_CLK_RATE; ftsc = devm_kzalloc(&pdev->dev, sizeof(struct phytium_spi_clk), GFP_KERNEL); @@ -81,7 +86,8 @@ static int phytium_spi_probe(struct platform_device *pdev) fts->max_freq = clk_get_rate(ftsc->clk); } else if (has_acpi_companion(&pdev->dev)) { - fts->max_freq = 48000000; + fwnode_property_read_u32(dev->fwnode, "spi-clock", &clk_rate); + fts->max_freq = clk_rate; } fts->bus_num = pdev->id; @@ -169,3 +175,4 @@ module_platform_driver(phytium_spi_driver); MODULE_AUTHOR("Yiqun Zhang "); MODULE_DESCRIPTION("Platform Driver for Phytium SPI controller core"); MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/spi/spi-phytium-qspi.c b/drivers/spi/spi-phytium-qspi.c index 4022d565a98d03bdfcd28dd97e813ad66ea13ca8..745f0afc0ccce604e7967b5d1e3966aa3ea83c96 100644 --- a/drivers/spi/spi-phytium-qspi.c +++ b/drivers/spi/spi-phytium-qspi.c @@ -21,8 +21,14 @@ #include #include +#define DRIVER_VERSION "1.0.2" + +#define PHYTIUM_CPU_PART_FTC862 0x862 + +#define MIDR_PHYTIUM_FTC862 MIDR_CPU_MODEL(ARM_CPU_IMP_PHYTIUM, PHYTIUM_CPU_PART_FTC862) #define QSPI_FLASH_CAP_REG 0x00 +#define QSPI_FLASH_CAP_NUM_SHIFT_NEW 16 #define QSPI_FLASH_CAP_NUM_SHIFT 3 #define QSPI_FLASH_CAP_NUM_MASK (0x3 << QSPI_FLASH_CAP_NUM_SHIFT) #define QSPI_FLASH_CAP_CAP_SHIFT 0 @@ -146,6 +152,10 @@ #define XFER_PROTO_2_2_2 0x5 #define XFER_PROTO_4_4_4 0x6 +#define WR_CFG_NODIR_VALUE 0x5000000 + +#define QSPI_DEFAULT_CLK 500000000 + struct phytium_qspi_flash { u32 cs; u32 clk_div; @@ -250,6 +260,44 @@ static int phytium_spi_nor_protocol_encode(const struct spi_mem_op *op, u32 *cod return ret; } +static int phytium_qspi_flash_capacity_encode_new(u32 size, + u32 *cap, int i) +{ + int ret = 0; + + switch (size) { + case SZ_4M: + *cap |= (0x0 >> (4 * i)); + break; + case SZ_8M: + *cap |= (0x1 >> (4 * i)); + break; + case SZ_16M: + *cap |= (0x2 >> (4 * i)); + break; + case SZ_32M: + *cap |= (0x3 >> (4 * i)); + break; + case SZ_64M: + *cap |= (0x4 >> (4 * i)); + break; + case SZ_128M: + *cap |= (0x5 >> (4 * i)); + break; + case SZ_256M: + *cap |= (0x6 >> (4 * i)); + break; + case SZ_512M: + *cap |= (0x7 >> (4 * i)); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static int phytium_qspi_flash_capacity_encode(u32 size, u32 *cap) { int ret = 0; @@ -622,6 +670,8 @@ static int phytium_qspi_probe(struct platform_device *pdev) struct spi_mem *mem; struct spi_nor *nor; const char **reg_name_array; + bool new_capacity = false; + u32 clk_rate = QSPI_DEFAULT_CLK; ctrl = spi_alloc_master(dev, sizeof(*qspi)); if (!ctrl) @@ -701,13 +751,19 @@ static int phytium_qspi_probe(struct platform_device *pdev) goto probe_clk_failed; } } else if (has_acpi_companion(dev)) { - qspi->clk_rate = 50000000; + fwnode_property_read_u32(dev->fwnode, "spi-clock", &clk_rate); + qspi->clk_rate = clk_rate; } qspi->nodirmap = device_property_present(dev, "no-direct-mapping"); ctrl->mem_ops = qspi->nodirmap ? &phytium_qspi_mem_ops_nodirmap : &phytium_qspi_mem_ops; + if ((read_cpuid_id() & MIDR_CPU_MODEL_MASK) == MIDR_PHYTIUM_FTC862) { + dev_warn(dev, "capacity register(0x00) is the latest design\n"); + new_capacity = true; + } + qspi->dev = dev; platform_set_drvdata(pdev, qspi); @@ -734,25 +790,38 @@ static int phytium_qspi_probe(struct platform_device *pdev) } } } + if (!new_capacity) { + for (i = 1; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) { + if (qspi->flash[i].size != qspi->flash[0].size) { + dev_err(dev, "Flashes are of different sizes.\n"); + ret = -EINVAL; + goto probe_setup_failed; + } + } - for (i = 1; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) { - if (qspi->flash[i].size != qspi->flash[0].size) { - dev_err(dev, "Flashes are of different sizes.\n"); - ret = -EINVAL; + ret = phytium_qspi_flash_capacity_encode(qspi->flash[0].size, + &qspi->flash_cap); + if (ret) { + dev_err(dev, "Flash size is invalid.\n"); goto probe_setup_failed; } - } - ret = phytium_qspi_flash_capacity_encode(qspi->flash[0].size, - &qspi->flash_cap); - if (ret) { - dev_err(dev, "Flash size is invalid.\n"); - goto probe_setup_failed; + qspi->flash_cap |= (qspi->fnum - 1) << QSPI_FLASH_CAP_NUM_SHIFT; + } else { + for (i = 0; qspi->fnum > i && i < PHYTIUM_QSPI_MAX_NORCHIP; i++) { + ret = phytium_qspi_flash_capacity_encode_new(qspi->flash[i].size, + &qspi->flash_cap, i); + if (ret) { + dev_err(dev, "Flash size is invalid.\n"); + goto probe_setup_failed; + } + } + qspi->flash_cap |= (qspi->fnum - 1) << QSPI_FLASH_CAP_NUM_SHIFT_NEW; } - qspi->flash_cap |= qspi->fnum << QSPI_FLASH_CAP_NUM_SHIFT; - writel_relaxed(qspi->flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + } else { + writel_relaxed(WR_CFG_NODIR_VALUE, qspi->io_base + QSPI_WR_CFG_REG); } return 0; @@ -803,10 +872,15 @@ static int __maybe_unused phytium_qspi_resume(struct device *dev) { struct phytium_qspi *qspi = dev_get_drvdata(dev); - /* set rd_cfg reg, wr_cfg reg and flash_capacity reg after resume */ - writel_relaxed(qspi->rd_cfg_reg, qspi->io_base + QSPI_RD_CFG_REG); - writel_relaxed(qspi->wr_cfg_reg, qspi->io_base + QSPI_WR_CFG_REG); - writel_relaxed(qspi->flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + if (!qspi->nodirmap) { + /* set rd_cfg reg and flash_capacity reg after resume */ + writel_relaxed(qspi->rd_cfg_reg, qspi->io_base + QSPI_RD_CFG_REG); + writel_relaxed(qspi->wr_cfg_reg, qspi->io_base + QSPI_WR_CFG_REG); + writel_relaxed(qspi->flash_cap, qspi->io_base + QSPI_FLASH_CAP_REG); + } else { + writel_relaxed(WR_CFG_NODIR_VALUE, qspi->io_base + QSPI_WR_CFG_REG); + } + return pm_runtime_force_resume(dev); } @@ -841,3 +915,4 @@ module_platform_driver(phytium_qspi_driver); MODULE_AUTHOR("Chen Baozi "); MODULE_DESCRIPTION("Phytium Quad SPI driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 5c57c7378ee708b58723eb763570cfb31e77a5f3..74c2ea9b81bb3e4169f48d61a6ebb0311841430e 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #define CREATE_TRACE_POINTS @@ -2320,6 +2321,18 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, if (!of_property_read_u32(nc, "spi-max-frequency", &value)) spi->max_speed_hz = value; + if (!of_property_read_u32(nc, "spi-rx-proto", &value)) + spi->rx_proto = value; + if (!of_property_read_u32(nc, "spi-tx-proto", &value)) + spi->tx_proto = value; + + if (spi->rx_proto != SNOR_PROTO_1_1_4 && + spi->rx_proto != SNOR_PROTO_1_4_4 && + spi->rx_proto != SNOR_PROTO_1_1_2) + spi->rx_proto = SNOR_PROTO_1_1_1; + if (spi->tx_proto != SNOR_PROTO_1_1_4) + spi->tx_proto = SNOR_PROTO_1_1_1; + /* Device CS delays */ of_spi_parse_dt_cs_delay(nc, &spi->cs_setup, "spi-cs-setup-delay-ns"); of_spi_parse_dt_cs_delay(nc, &spi->cs_hold, "spi-cs-hold-delay-ns"); @@ -2640,6 +2653,8 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, struct acpi_spi_lookup lookup = {}; struct spi_device *spi; int ret; + int rx_val = 1, tx_val = 1; + int rx_proto = 1, tx_proto = 1; if (!ctlr && index == -1) return ERR_PTR(-EINVAL); @@ -2654,6 +2669,52 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, acpi_spi_add_resource, &lookup); acpi_dev_free_resource_list(&resource_list); + if (!fwnode_property_read_u32(&adev->fwnode, "spi-tx-bus-width", &tx_val)) { + switch (tx_val) { + case 0: + lookup.mode |= SPI_NO_TX; + break; + case 1: + break; + case 2: + lookup.mode |= SPI_TX_DUAL; + break; + case 4: + lookup.mode |= SPI_TX_QUAD; + break; + case 8: + lookup.mode |= SPI_TX_OCTAL; + break; + default: + dev_warn(&lookup.ctlr->dev, "spi-tx-bus-width %d not supported\n", tx_val); + break; + } + } + if (!fwnode_property_read_u32(&adev->fwnode, "spi-rx-bus-width", &rx_val)) { + switch (rx_val) { + case 0: + lookup.mode |= SPI_NO_RX; + break; + case 1: + break; + case 2: + lookup.mode |= SPI_RX_DUAL; + break; + case 4: + lookup.mode |= SPI_RX_QUAD; + break; + case 8: + lookup.mode |= SPI_RX_OCTAL; + break; + default: + dev_warn(&lookup.ctlr->dev, "spi-rx-bus-width %d not supported\n", rx_val); + break; + } + } + + fwnode_property_read_u32(&adev->fwnode, "spi-rx-proto", &rx_proto); + fwnode_property_read_u32(&adev->fwnode, "spi-tx-proto", &tx_proto); + if (ret < 0) /* Found SPI in _CRS but it points to another controller */ return ERR_PTR(ret); @@ -2682,6 +2743,18 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, spi->bits_per_word = lookup.bits_per_word; spi_set_chipselect(spi, 0, lookup.chip_select); + if (rx_proto) + spi->rx_proto = rx_proto; + if (tx_proto) + spi->tx_proto = tx_proto; + + if (spi->rx_proto != SNOR_PROTO_1_1_4 && + spi->rx_proto != SNOR_PROTO_1_4_4 && + spi->rx_proto != SNOR_PROTO_1_1_2) + spi->rx_proto = SNOR_PROTO_1_1_1; + if (spi->tx_proto != SNOR_PROTO_1_1_4) + spi->tx_proto = SNOR_PROTO_1_1_1; + return spi; } EXPORT_SYMBOL_GPL(acpi_spi_device_alloc); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index d7ac7eef680e124d91be635118e50f69207670e4..4aa2ad410541439e900dd8ca69994ed9535ffeae 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -18,9 +18,10 @@ #include #include #include +#include #include #include - +#include #define CREATE_TRACE_POINTS #include "thermal_trace.h" @@ -341,6 +342,49 @@ static void handle_critical_trips(struct thermal_zone_device *tz, tz->ops->critical(tz); } +static int thermal_boost_enable(struct thermal_zone_device *tz) +{ + enum thermal_trend trend = get_tz_trend(tz, 0); + struct thermal_trip trip; + + if (!tz->overheated) + return -EPERM; + if (trend == THERMAL_TREND_RAISING) + return -EBUSY; + + __thermal_zone_get_trip(tz, 0, &trip); + + if ((tz->temperature + (trip.temperature >> 2)) < trip.temperature) { + mutex_lock(&tz->lock); + tz->overheated = false; + if (tz->boost_polling) { + tz->boost_polling = false; + thermal_zone_device_set_polling(tz, 0); + } + mutex_unlock(&tz->lock); + cpufreq_boost_trigger_state(1); + return 0; + } + return -EBUSY; +} + +static void thermal_boost_disable(struct thermal_zone_device *tz) +{ + cpufreq_boost_trigger_state(0); + + /* + * If no workqueue for monitoring is running - start one with + * 1000 ms monitoring period + * If workqueue already running - do not change its period and only + * test if target CPU has cooled down + */ + if (tz->mode != THERMAL_DEVICE_ENABLED) { + tz->boost_polling = true; + thermal_zone_device_set_polling(tz, 1000); + } + tz->overheated = true; +} + static void handle_thermal_trip(struct thermal_zone_device *tz, int trip_id) { struct thermal_trip trip; @@ -420,6 +464,11 @@ void __thermal_zone_device_update(struct thermal_zone_device *tz, update_temperature(tz); + if (cpufreq_boost_supported() && cpufreq_boost_enabled()) + if ((tz->temperature + (tz->trips[0].temperature >> 2)) + >= tz->trips[0].temperature) + thermal_boost_disable(tz); + __thermal_zone_set_trips(tz); tz->notify_event = event; @@ -526,6 +575,9 @@ static void thermal_zone_device_check(struct work_struct *work) struct thermal_zone_device *tz = container_of(work, struct thermal_zone_device, poll_queue.work); + if (cpufreq_boost_supported()) + if (!thermal_boost_enable(tz)) + return; thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); } diff --git a/drivers/tty/serial/phytium-uart.c b/drivers/tty/serial/phytium-uart.c index c21cf9021608dfc06f8bf4d117ac73a4311f8b36..3b80854fa05f6b2949550b8aa2f03fef875d0fa9 100644 --- a/drivers/tty/serial/phytium-uart.c +++ b/drivers/tty/serial/phytium-uart.c @@ -23,7 +23,7 @@ #include #define DRV_NAME "phytium_uart" - +#define PHYT_UART_DRV_VERSION "1.1.0" #define REG_DR 0x00 #define REG_FR 0x18 #define REG_IBRD 0x24 @@ -924,4 +924,5 @@ module_exit(phytium_uart_exit); MODULE_AUTHOR("Chen Baozi "); MODULE_DESCRIPTION("Phytium PCI serial port driver"); +MODULE_VERSION(PHYT_UART_DRV_VERSION); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 9420d7a565f4682f0368d95ffcd332b0f27d1f94..7aa8dc4ca3a101fe69e6cc4ee276a7f31b0d9436 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -114,7 +114,7 @@ static const struct xhci_plat_priv xhci_plat_brcm = { }; static const struct xhci_plat_priv xhci_plat_phytium_pe220x = { - .quirks = XHCI_RESET_ON_RESUME, + .quirks = XHCI_RESET_ON_RESUME | XHCI_S1_SUSPEND_WAKEUP, }; static const struct of_device_id usb_xhci_of_match[] = { diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 6e38b6b480e0981df0b9faf62cc169eee60c878c..4587b652ce9da06499d57c307eb8c38d2371b5c6 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3154,6 +3154,12 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) goto out; } + if (status & STS_WAKEUP) { + status |= STS_WAKEUP; + writel(status, &xhci->op_regs->status); + ret = IRQ_HANDLED; + } + if (!(status & STS_EINT)) goto out; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f005ce1f91ca2a7cb892fc4759d3c723c8b0f60b..5fa6c8909c32fa071d0043157cde7f536d47705f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -936,6 +936,18 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) return -ETIMEDOUT; } } + + if (xhci->quirks & XHCI_S1_SUSPEND_WAKEUP) { + if (device_may_wakeup(xhci_to_hcd(xhci)->self.controller) && do_wakeup) { + if (enable_irq_wake(hcd->irq)) + xhci_err(xhci, "failed to enable irq wakes\n"); + + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + if (xhci->shared_hcd) + set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); + } + } + spin_unlock_irq(&xhci->lock); /* @@ -982,6 +994,13 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) time_before(jiffies, xhci->usb3_rhub.bus_state.next_statechange)) msleep(100); + if (xhci->quirks & XHCI_S1_SUSPEND_WAKEUP) { + if (device_may_wakeup(xhci_to_hcd(xhci)->self.controller)) { + if (disable_irq_wake(hcd->irq)) + xhci_err(xhci, "failed to disable irq wakes\n"); + } + } + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); if (xhci->shared_hcd) set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 38a64838bc0da53752f742fa4d32d25f1f42876b..e80d040cd677051dde65a955e3531c87266cfa5b 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -159,6 +159,8 @@ struct xhci_op_regs { /* USBSTS - USB status - status bitmasks */ /* HC not running - set to 1 when run/stop bit is cleared. */ #define STS_HALT XHCI_STS_HALT +/* event wakeup interrupt */ +#define STS_WAKEUP (1 << 1) /* serious error, e.g. PCI parity error. The HC will clear the run/stop bit. */ #define STS_FATAL (1 << 2) /* event interrupt - clear this prior to clearing any IP flags in IR set*/ @@ -1661,6 +1663,7 @@ struct xhci_hcd { #define XHCI_WRITE_64_HI_LO BIT_ULL(47) #define XHCI_CDNS_SCTX_QUIRK BIT_ULL(48) #define XHCI_SLOWDOWN_QUIRK BIT_ULL(49) +#define XHCI_S1_SUSPEND_WAKEUP BIT_ULL(50) unsigned int num_active_eps; unsigned int limit_active_eps; diff --git a/drivers/usb/phytium/core.c b/drivers/usb/phytium/core.c index c0182c0770e5ef5f5baa7d7b1709b844ca8578db..ed2d1d661c2d31cc8cb08dfa916ff9932c496192 100644 --- a/drivers/usb/phytium/core.c +++ b/drivers/usb/phytium/core.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include "core.h" int phytium_core_reset(struct phytium_cusb *config, bool skip_wait) diff --git a/drivers/usb/phytium/core.h b/drivers/usb/phytium/core.h index f563672ccaa9963859ff3e1bf8ce62276d3c0bce..0237476bea664ebb60131645a496b5379e6989ba 100644 --- a/drivers/usb/phytium/core.h +++ b/drivers/usb/phytium/core.h @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __PHYTIUM_CORE_H__ #define __PHYTIUM_CORE_H__ diff --git a/drivers/usb/phytium/dma.c b/drivers/usb/phytium/dma.c index acc0674cf5d38bda23e10b2e155723f832de582f..3048a89de81e796021135f4e77bb5036795e6899 100644 --- a/drivers/usb/phytium/dma.c +++ b/drivers/usb/phytium/dma.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include #include "core.h" diff --git a/drivers/usb/phytium/dma.h b/drivers/usb/phytium/dma.h index f2c25e6006e707a2af7ed0051316dc3302c6c430..cecc38c99fb727fde68473b26b2b5c01a3d4bef8 100644 --- a/drivers/usb/phytium/dma.h +++ b/drivers/usb/phytium/dma.h @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __PHYTIUM_DMA_H__ #define __PHYTIUM_DMA_H__ diff --git a/drivers/usb/phytium/gadget.c b/drivers/usb/phytium/gadget.c index e5e1625d785f89f44153f12985c3e22680bef55a..db33e6a1926efcdcebb109c52a4cc22c40a12116 100644 --- a/drivers/usb/phytium/gadget.c +++ b/drivers/usb/phytium/gadget.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include "gadget.h" #include "dma.h" diff --git a/drivers/usb/phytium/gadget.h b/drivers/usb/phytium/gadget.h index d87b55ade7a70b29189402e8164d833e6340730b..6d7bd4b32859b44e10792825baf5af3b02ff2a31 100644 --- a/drivers/usb/phytium/gadget.h +++ b/drivers/usb/phytium/gadget.h @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __PHYTIUM_GADGET_H_ #define __PHYTIUM_GADGET_H_ diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c index 8d58eaa99dd56bfa755a1174f435c89ad54a146d..8572c58f41e9a81573dafb08a68a0cf05c00b6b2 100644 --- a/drivers/usb/phytium/host.c +++ b/drivers/usb/phytium/host.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include #include @@ -1317,7 +1321,7 @@ static int hc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) usb_endpoint_dir_in(host_ep_desc)); if (req->epNum > MAX_INSTANCE_EP_NUM) { pr_err("Not enough endpoint resource for remap\n"); - dump_ep_remap_pool(priv, usb_endpoint_dir_in(host_ep_desc)); + dump_ep_remap_pool(priv, usb_endpoint_num(host_ep_desc)); req->epNum = MAX_INSTANCE_EP_NUM; } @@ -2049,12 +2053,18 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, if (usb_endpoint_xfer_control(&desc) || usb_endpoint_xfer_bulk(&desc)) { if (desc.bInterval == 0) return interval; + + if (usb_endpoint_xfer_bulk(&desc)) { + interval = 1; + return interval; + } + interval = fls(desc.bInterval) - 1; interval = clamp_val(interval, 0, 15); interval = 1 << interval; if ((1 << interval) != desc.bInterval) pr_debug("rounding to %d microframes, desc %d microframes\n", - 1 << interval, desc.bInterval); + 1 << interval, desc.bInterval); break; } @@ -2063,16 +2073,15 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = 1 << interval; if (interval != desc.bInterval - 1) pr_debug("rounding to %d %sframes\n", 1 << interval, - speed == USB_SPEED_FULL ? "" : "micro"); + speed == USB_SPEED_FULL ? "" : "micro"); } break; case USB_SPEED_FULL: if (usb_endpoint_xfer_isoc(&desc)) { - interval = clamp_val(desc.bInterval, 1, 16) - 1; + interval = clamp_val(desc.bInterval, 1, 16); if (interval != desc.bInterval - 1) pr_debug("rounding to %d %sframes\n", 1 << interval, - speed == USB_SPEED_FULL ? "" : "micro"); - interval += 3; + speed == USB_SPEED_FULL ? "" : "micro"); break; } fallthrough; @@ -2082,7 +2091,7 @@ unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc, interval = clamp_val(interval, 3, 10); if ((1 << interval) != desc.bInterval * 8) pr_debug("rounding to %d microframes, desc %d microframes\n", - 1 << interval, desc.bInterval); + 1 << interval, desc.bInterval); } } diff --git a/drivers/usb/phytium/host_api.h b/drivers/usb/phytium/host_api.h index b99d2b4980fbe09ab31f8591b0cd528f6516d6f5..6003954eb1b1777d74cc587c79c303bd7d3c9f8a 100644 --- a/drivers/usb/phytium/host_api.h +++ b/drivers/usb/phytium/host_api.h @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __PHYTIUM_HOST_API_H_ #define __PHYTIUM_HOST_API_H_ diff --git a/drivers/usb/phytium/hw-regs.h b/drivers/usb/phytium/hw-regs.h index 8da9f8e9b92537f20c2be1c232849f331f32baf7..8200ac3cf7b7fb852df679e16c2e22d3f7300a3d 100644 --- a/drivers/usb/phytium/hw-regs.h +++ b/drivers/usb/phytium/hw-regs.h @@ -1,4 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #ifndef __LINUX_PHYTIUM_HW_REGS #define __LINUX_PHYTIUM_HW_REGS diff --git a/drivers/usb/phytium/pci.c b/drivers/usb/phytium/pci.c index b4d675effb88b56a36f41e2a94e5b78f4eff5c17..21f373cc4f12f2f90938148744fc433a20a34ca1 100644 --- a/drivers/usb/phytium/pci.c +++ b/drivers/usb/phytium/pci.c @@ -1,5 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include #include diff --git a/drivers/usb/phytium/platform.c b/drivers/usb/phytium/platform.c index 48f35b19ade72ac5b518b2d3bdc7070b6388d0a1..c61b544baebd0515d0a9c243961ef3c055b5758d 100644 --- a/drivers/usb/phytium/platform.c +++ b/drivers/usb/phytium/platform.c @@ -1,4 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 + +/* + * Copyright (c) 2022, Phytium Technology Co., Ltd. + */ + #include #include //#include @@ -11,6 +16,7 @@ #include "core.h" #include "hw-regs.h" +#define PHYTIUM_OTG_V1_VERSION "1.0.2" #define PHYTIUM_OTG_USB_LOADED 3 #define USB2_2_BASE_ADDRESS 0x31800000 @@ -215,3 +221,4 @@ module_platform_driver(phytium_otg_driver); MODULE_AUTHOR("Chen Zhenhua "); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Phytium usb platform wrapper"); +MODULE_VERSION(PHYTIUM_OTG_V1_VERSION); diff --git a/drivers/w1/masters/phytium_w1.c b/drivers/w1/masters/phytium_w1.c index 0cb62258daec91f8cfc6eb2b13667fbe4c298494..0d1e5522a57a819c05b343a7742036c8da356f65 100644 --- a/drivers/w1/masters/phytium_w1.c +++ b/drivers/w1/masters/phytium_w1.c @@ -56,6 +56,8 @@ #define PHY_W1M_MAX_USER 4 +#define W1_DRIVER_VERSION "1.1.1" + static DECLARE_WAIT_QUEUE_HEAD(w1m_wait_queue); struct w1m_data { @@ -641,3 +643,4 @@ module_platform_driver(phytium_w1m_driver); MODULE_AUTHOR("Zhu Mingshuai "); MODULE_DESCRIPTION("Phytium w1 bus master driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(W1_DRIVER_VERSION); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 9ca4211c063f397bad7ad7e5f89c1b8e3bf283ff..0393a2f2baf79e3279c0931d62dd0f03ffeb19b9 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -797,6 +797,7 @@ ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf); #ifdef CONFIG_CPU_FREQ int cpufreq_boost_trigger_state(int state); int cpufreq_boost_enabled(void); +bool cpufreq_boost_supported(void); int cpufreq_enable_boost_support(void); bool policy_has_boost_freq(struct cpufreq_policy *policy); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index b6ef263e85c061495d3e506859c8d15f39f46836..7c6e71c47039ed0628700cb07c14859ceb3709e9 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -294,6 +294,12 @@ struct iommu_ops { const struct iommu_domain_ops *default_domain_ops; unsigned long pgsize_bitmap; struct module *owner; + +#ifdef CONFIG_SMMU_BYPASS_DEV +#ifndef __GENKSYMS__ + int (*device_domain_type)(struct device *dev, unsigned int *type); +#endif +#endif }; /** diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 914a9f974baaae5010d473d0e5d8c6e79f88e191..a22816384ade2801d5d216ddd18e7938055fc01d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -458,6 +458,12 @@ static inline void mtd_set_pairing_scheme(struct mtd_info *mtd, mtd->pairing = pairing; } +static inline void mtd_set_fwnode(struct mtd_info *mtd, + struct fwnode_handle *fwnode) +{ + mtd->dev.fwnode = fwnode; +} + static inline void mtd_set_of_node(struct mtd_info *mtd, struct device_node *np) { diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 8520a5c682a038ae29a4ff534caec485447a908a..4c01b8cf24051c05a26267339cc5b603a85dd509 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -3222,5 +3222,6 @@ #define PCI_VENDOR_ID_NCUBE 0x10ff #define PCI_VENDOR_ID_PHYTIUM 0x1db7 +#define PCI_DEVICE_ID_PHYTIUM_PE220X 0xdc3e #endif /* _LINUX_PCI_IDS_H */ diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index e5baf43bcfbb65a0fad95ef940a0ee0b699a719a..5a3bbe31948ec52be6d1944cf97770741a668137 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -208,6 +208,8 @@ struct spi_device { */ #define SPI_MODE_KERNEL_MASK (~(BIT(29) - 1)) u32 mode; + u32 rx_proto; + u32 tx_proto; int irq; void *controller_state; void *controller_data; diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 2e9d18ba465314e8bd80151e465fe357efbcf4fc..155f023b6cdfdaa73b079c3ca029259e2084b8c7 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -176,6 +176,8 @@ struct thermal_zone_device { int prev_low_trip; int prev_high_trip; atomic_t need_update; + bool overheated; + bool boost_polling; struct thermal_zone_device_ops *ops; struct thermal_zone_params *tzp; struct thermal_governor *governor; diff --git a/kernel/sched/topology.c b/kernel/sched/topology.c index 3a13cecf177402a15b7fc8d3447909165ee9cc65..1b05b652f5f898d78e81b796dc1dc137894fe4e6 100644 --- a/kernel/sched/topology.c +++ b/kernel/sched/topology.c @@ -348,32 +348,13 @@ static void sched_energy_set(bool has_eas) * 1. an Energy Model (EM) is available; * 2. the SD_ASYM_CPUCAPACITY flag is set in the sched_domain hierarchy. * 3. no SMT is detected. - * 4. the EM complexity is low enough to keep scheduling overheads low; - * 5. schedutil is driving the frequency of all CPUs of the rd; - * 6. frequency invariance support is present; - * - * The complexity of the Energy Model is defined as: - * - * C = nr_pd * (nr_cpus + nr_ps) - * - * with parameters defined as: - * - nr_pd: the number of performance domains - * - nr_cpus: the number of CPUs - * - nr_ps: the sum of the number of performance states of all performance - * domains (for example, on a system with 2 performance domains, - * with 10 performance states each, nr_ps = 2 * 10 = 20). - * - * It is generally not a good idea to use such a model in the wake-up path on - * very complex platforms because of the associated scheduling overheads. The - * arbitrary constraint below prevents that. It makes EAS usable up to 16 CPUs - * with per-CPU DVFS and less than 8 performance states each, for example. + * 4. schedutil is driving the frequency of all CPUs of the rd; + * 5. frequency invariance support is present; */ -#define EM_MAX_COMPLEXITY 2048 - extern struct cpufreq_governor schedutil_gov; static bool build_perf_domains(const struct cpumask *cpu_map) { - int i, nr_pd = 0, nr_ps = 0, nr_cpus = cpumask_weight(cpu_map); + int i; struct perf_domain *pd = NULL, *tmp; int cpu = cpumask_first(cpu_map); struct root_domain *rd = cpu_rq(cpu)->rd; @@ -431,20 +412,6 @@ static bool build_perf_domains(const struct cpumask *cpu_map) goto free; tmp->next = pd; pd = tmp; - - /* - * Count performance domains and performance states for the - * complexity check. - */ - nr_pd++; - nr_ps += em_pd_nr_perf_states(pd->em_pd); - } - - /* Bail out if the Energy Model complexity is too high. */ - if (nr_pd * (nr_ps + nr_cpus) > EM_MAX_COMPLEXITY) { - WARN(1, "rd %*pbl: Failed to start EAS, EM complexity is too high\n", - cpumask_pr_args(cpu_map)); - goto free; } perf_domain_debug(cpu_map, pd); diff --git a/sound/pci/hda/hda_phytium.c b/sound/pci/hda/hda_phytium.c index 86cd17120b0d529ebb9e30b7873ccc7589248008..18e5faac712f67aa5a6d338cf01903a8818a734c 100644 --- a/sound/pci/hda/hda_phytium.c +++ b/sound/pci/hda/hda_phytium.c @@ -477,7 +477,7 @@ static int azx_resume(struct device *dev) snd_hdac_bus_exit_link_reset(bus); usleep_range(1000, 1200); - azx_init_chip(chip, 0); + azx_init_chip(chip, 1); snd_power_change_state(card, SNDRV_CTL_POWER_D0); @@ -967,6 +967,7 @@ static int hda_ft_probe(struct platform_device *pdev) return err; } +#define codec_power_count(codec) codec->core.dev.power.usage_count.counter /* number of codec slots for each chipset: 0 = default slots (i.e. 4) */ static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = { [AZX_DRIVER_FT] = 4, @@ -979,6 +980,7 @@ static int azx_probe_continue(struct azx *chip) int dev = chip->dev_index; int err; struct hdac_bus *bus = azx_bus(chip); + struct hda_codec *codec; hda->probe_continued = 1; @@ -1011,6 +1013,14 @@ static int azx_probe_continue(struct azx *chip) if (azx_has_pm_runtime(chip)) pm_runtime_put_noidle(hddev); + + list_for_each_codec(codec, &chip->bus) { + if (codec_power_count(codec) > 0) { + pm_runtime_mark_last_busy(&codec->core.dev); + pm_runtime_put_autosuspend(&codec->core.dev); + } + } + return err; out_free: diff --git a/sound/soc/codecs/es8336.c b/sound/soc/codecs/es8336.c index 2d666ab2831c1a9f06251374f04ebbacc202dfd1..a7ae3d1cba24ebdb20dc87153c6c0d050e5d186b 100644 --- a/sound/soc/codecs/es8336.c +++ b/sound/soc/codecs/es8336.c @@ -30,6 +30,8 @@ #include #include #include "es8336.h" + +#define ES8336_V1_VERSION "1.0.0" #define ES8336_MUTE (1 << 5) static struct snd_soc_component *es8336_component; @@ -826,31 +828,14 @@ static int es8336_suspend(struct snd_soc_component *component) static int es8336_resume(struct snd_soc_component *component) { - struct es8336_priv *es8336 = snd_soc_component_get_drvdata(component); + struct regmap *regmap = dev_get_regmap(component->dev, NULL); int ret; - es8336_reset(component); /* UPDATED BY DAVID,15-3-5 */ - ret = snd_soc_component_read(component, ES8336_CLKMGR_ADCDIV2_REG05); - if (!ret) { - es8336_init_regs(component); - snd_soc_component_write(component, ES8336_GPIO_SEL_REG4D, 0x02); - /* max debance time, enable interrupt, low active */ - snd_soc_component_write(component, ES8336_GPIO_DEBUNCE_INT_REG4E, 0xf3); - /* es8336_set_bias_level(component, SND_SOC_BIAS_OFF); */ - snd_soc_component_write(component, ES8336_CPHP_OUTEN_REG17, 0x00); - snd_soc_component_write(component, ES8336_DAC_PDN_REG2F, 0x11); - snd_soc_component_write(component, ES8336_CPHP_LDOCTL_REG1B, 0x03); - snd_soc_component_write(component, ES8336_CPHP_PDN2_REG1A, 0x22); - snd_soc_component_write(component, ES8336_CPHP_PDN1_REG19, 0x06); - snd_soc_component_write(component, ES8336_HPMIX_SWITCH_REG14, 0x00); - snd_soc_component_write(component, ES8336_HPMIX_PDN_REG15, 0x33); - snd_soc_component_write(component, ES8336_HPMIX_VOL_REG16, 0x00); - if (!es8336->hp_inserted) - snd_soc_component_write(component, ES8336_SYS_PDN_REG0D, 0x3F); - snd_soc_component_write(component, ES8336_SYS_LP1_REG0E, 0xFF); - snd_soc_component_write(component, ES8336_SYS_LP2_REG0F, 0xFF); - snd_soc_component_write(component, ES8336_CLKMGR_CLKSW_REG01, 0xF3); - snd_soc_component_update_bits(component, ES8336_ADC_PDN_LINSEL_REG22, 0xC0, 0xC0); + regcache_mark_dirty(regmap); + ret = regcache_sync(regmap); + if (ret) { + dev_err(component->dev, "unable to sync regcache\n"); + return ret; } return 0; } @@ -931,6 +916,8 @@ const struct regmap_config es8336_regmap_config = { .cache_type = REGCACHE_RBTREE, .reg_defaults = es8336_reg_defaults, .num_reg_defaults = ARRAY_SIZE(es8336_reg_defaults), + .use_single_read = true, + .use_single_write = true, }; static const struct snd_soc_component_driver soc_component_dev_es8336 = { @@ -1071,3 +1058,4 @@ static struct i2c_driver es8336_i2c_driver = { module_i2c_driver(es8336_i2c_driver); MODULE_DESCRIPTION("ASoC es8336 driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(ES8336_V1_VERSION); diff --git a/sound/soc/codecs/es8388.c b/sound/soc/codecs/es8388.c index 11959e97e61bcf1c1465080c893618c99a6ceb5b..57ef3c747a0360889e77be6a211ca6ba0a7cb868 100644 --- a/sound/soc/codecs/es8388.c +++ b/sound/soc/codecs/es8388.c @@ -23,6 +23,8 @@ #include #include +#define ES8388_V1_VERSION "1.0.0" + static const unsigned int rates_12288[] = { 8000, 12000, 16000, 24000, 32000, 48000, 96000, }; @@ -817,3 +819,4 @@ module_i2c_driver(es8388_i2c_driver); MODULE_DESCRIPTION("ASoC ES8388 driver"); MODULE_AUTHOR("Yiqun Zhang "); MODULE_LICENSE("GPL"); +MODULE_VERSION(ES8388_V1_VERSION); diff --git a/sound/soc/phytium/local.h b/sound/soc/phytium/local.h index 8cc80d1d1040e1f0e5ea6179e63a115c5d048f46..54c4d2a6de2871a06de33b768bb2c0860301edcf 100644 --- a/sound/soc/phytium/local.h +++ b/sound/soc/phytium/local.h @@ -81,6 +81,36 @@ #define DMA_STS 0x0008 +#define I2S_HEADPHONE_ENABLE 1 +#define I2S_HEADPHONE_DISABLE 0 + +#define I2S_GPIO(x) BIT(x) +#define I2S_GPIO_BASE 0xD00 + +/* I2S GPIO registers */ +#define I2S_GPIO_SWPORTA_DR 0x00 /* WR Port A Output Data Register */ + #define I2S_HEADPHONE_FRONT (!I2S_GPIO(1)) + #define I2S_HEADPHONE_REAR I2S_GPIO(1) +#define I2S_GPIO_SWPORTA_DDR 0x04 /* WR Port A Data Direction Register */ + #define I2S_GPIO_INPUT(x) (!I2S_GPIO(x)) + #define I2S_GPIO_OUTPUT(x) I2S_GPIO(x) +#define I2S_GPIO_EXT_PORTA 0x08 /* RO Port A Input Data Register */ +#define I2S_GPIO_SWPORTB_DR 0x0c /* WR Port B Output Data Register */ +#define I2S_GPIO_SWPORTB_DDR 0x10 /* WR Port B Data Direction Register */ +#define I2S_GPIO_EXT_PORTB 0x14 /* RO Port B Input Data Register */ + +#define I2S_GPIO_INTEN 0x18 /* WR Port A Interrput Enable Register */ +#define I2S_GPIO_INTMASK 0x1c /* WR Port A Interrupt Mask Register */ +#define I2S_GPIO_INTTYPE_LEVEL 0x20 /* WR Port A Interrupt Level Register */ + #define I2S_GPIO_LEVEL(x) (!I2S_GPIO(x)) + #define I2S_GPIO_EDGE(x) I2S_GPIO(x) +#define I2S_GPIO_INT_POLARITY 0x24 /* WR Port A Interrupt Polarity Register */ + #define I2S_GPIO_DOWN(x) (!I2S_GPIO(x)) + #define I2S_GPIO_UP(x) I2S_GPIO(x) +#define I2S_GPIO_INTSTATUS 0x28 /* RO Port A Interrupt Status Register */ +#define I2S_GPIO_DEBOUNCE 0x34 /* WR Debounce Enable Register */ +#define I2S_GPIO_PORTA_EOI 0x38 /* WO Port A Clear Interrupt Register */ + /* max number of fragments - we may use more if allocating more pages for BDL */ #define BDL_SIZE 4096 #define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) @@ -281,7 +311,10 @@ struct i2s_phytium { u32 paddr; void __iomem *regs; void __iomem *regs_db; + void __iomem *regs_gpio; int irq_id; + int gpio_irq_id; + bool detect; /* for pending irqs */ struct work_struct irq_pending_work; @@ -289,6 +322,7 @@ struct i2s_phytium { /* sync probing */ struct completion probe_wait; struct work_struct probe_work; + struct delayed_work i2s_gpio_work; /* extra flags */ unsigned int pcie:1; diff --git a/sound/soc/phytium/phytium_i2s.c b/sound/soc/phytium/phytium_i2s.c index 9119cdae1c24909a20eade6d96a737c0e0c62598..ace159cd51159a6ef8da22a8b8933afdfa0947b8 100644 --- a/sound/soc/phytium/phytium_i2s.c +++ b/sound/soc/phytium/phytium_i2s.c @@ -27,9 +27,11 @@ #include #include #include +#include #include #include "local.h" +#define PHYTIUM_I2S_V1_VERSION "1.0.0" #define NUM_CAPTURE 1 #define NUM_PLAYBACK 1 @@ -46,11 +48,25 @@ #define EIGHT_CHANNEL_SUPPORT 8 /* up to 7.1 */ struct pdata_px210_mfd { - struct device *dev; + struct device *dev; char *name; int clk_base; }; +static struct snd_soc_jack hs_jack; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "FrontIn", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Front", + .mask = SND_JACK_HEADPHONE, + }, +}; + static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val) { writel(val, io_base + reg); @@ -151,11 +167,86 @@ irqreturn_t azx_i2s_interrupt(int irq, void *dev_id) return IRQ_RETVAL(handled); } +int azx_i2s_enable_gpio(struct i2s_phytium *i2s) +{ + u32 val; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_SWPORTA_DDR, + (I2S_GPIO_INPUT(0) | I2S_GPIO_OUTPUT(1))); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_SWPORTA_DDR); + if (val != (I2S_GPIO_INPUT(0) | I2S_GPIO_OUTPUT(1))) + goto enable_err; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INTMASK, !I2S_GPIO(0)); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INTMASK); + if (val != !I2S_GPIO(0)) + goto enable_err; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INTTYPE_LEVEL, I2S_GPIO_EDGE(0)); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INTTYPE_LEVEL); + if (val != I2S_GPIO_EDGE(0)) + goto enable_err; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INT_POLARITY, I2S_GPIO_DOWN(0)); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INT_POLARITY); + if (val != I2S_GPIO_DOWN(0)) + goto enable_err; + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INTEN, I2S_GPIO(0)); + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INTEN); + if (val != I2S_GPIO(0)) + goto enable_err; + + return 0; +enable_err: + return -EBUSY; +} + +static void i2s_gpio_jack_work(struct work_struct *work) +{ + struct i2s_phytium *i2s = container_of(work, struct i2s_phytium, i2s_gpio_work.work); + u32 val; + + val = i2s_read_reg(i2s->regs_gpio, I2S_GPIO_INT_POLARITY); + if (val == 1) { + snd_soc_jack_report(&hs_jack, I2S_HEADPHONE_DISABLE, SND_JACK_HEADSET); + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_SWPORTA_DR, I2S_HEADPHONE_REAR); + } else { + snd_soc_jack_report(&hs_jack, I2S_HEADPHONE_ENABLE, SND_JACK_HEADSET); + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_SWPORTA_DR, I2S_HEADPHONE_FRONT); + } + val = ~val; + val &= 0x1; + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INT_POLARITY, val); +} + +irqreturn_t azx_i2s_gpio_interrupt(int irq, void *dev_id) +{ + struct azx *chip = dev_id; + struct i2s_phytium *i2s = container_of(chip, struct i2s_phytium, chip); + bool handled = true; + + queue_delayed_work(system_power_efficient_wq, &i2s->i2s_gpio_work, + msecs_to_jiffies(100)); + + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_PORTA_EOI, I2S_GPIO(0)); + return IRQ_RETVAL(handled); +} + static int azx_acquire_irq(struct azx *chip, int do_disconnect) { struct i2sc_bus *bus = azx_bus(chip); struct i2s_phytium *i2s = container_of(chip, struct i2s_phytium, chip); int err; + u32 status; + + status = i2s_read_reg(i2s->regs_db, DMA_STS); + if (status & 0x1) + i2s_write_reg(i2s->regs_db, DMA_STS, 0x1); + if (status & 0x100) + i2s_write_reg(i2s->regs_db, DMA_STS, 0x100); + + i2s_write_reg(i2s->regs_db, DMA_MASK_INT, 0x0); err = devm_request_irq(i2s->dev, i2s->irq_id, azx_i2s_interrupt, IRQF_SHARED, "phytium i2s", chip); @@ -165,6 +256,26 @@ static int azx_acquire_irq(struct azx *chip, int do_disconnect) return err; } + if (i2s->detect) { + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_PORTA_EOI, I2S_GPIO(0)); + i2s_write_reg(i2s->regs_gpio, I2S_GPIO_INTMASK, 0x1); + + err = devm_request_irq(i2s->dev, i2s->gpio_irq_id, azx_i2s_gpio_interrupt, + IRQF_SHARED, + "phytium i2s gpio", chip); + if (err < 0) { + dev_err(i2s->dev, "failed to request gpio irq\n"); + return err; + } + + err = azx_i2s_enable_gpio(i2s); + if (err < 0) { + dev_err(i2s->dev, "failed to enable gpio irq\n"); + return err; + } + + } + bus->irq = i2s->irq_id; return 0; @@ -370,6 +481,10 @@ static int phytium_i2s_resume(struct snd_soc_component *component) } i2s_write_reg(dev->regs, CLK_CFG0, dev->cfg); i2s_write_reg(dev->regs, CLK_CFG1, 0xf); + + if (dev->detect) + azx_i2s_enable_gpio(dev); + return 0; } #else @@ -377,6 +492,21 @@ static int phytium_i2s_resume(struct snd_soc_component *component) #define phytium_i2s_resume NULL #endif +static int phytium_i2s_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_card *card = component->card; + int ret; + + ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, + &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); + if (ret < 0) { + dev_err(component->dev, "Cannot create jack\n"); + return ret; + } + return 0; +} + static struct snd_soc_dai_driver phytium_i2s_dai = { .playback = { .stream_name = "i2s-Playback", @@ -967,6 +1097,7 @@ static const struct snd_soc_component_driver phytium_i2s_component = { .pointer = phytium_pcm_pointer, .suspend = phytium_i2s_suspend, .resume = phytium_i2s_resume, + .probe = phytium_i2s_component_probe, .legacy_dai_naming = 1, }; @@ -1265,6 +1396,8 @@ static int i2s_phytium_create(struct platform_device *pdev, } INIT_WORK(&i2s->probe_work, azx_probe_work); + if (i2s->detect) + INIT_DELAYED_WORK(&i2s->i2s_gpio_work, i2s_gpio_jack_work); *rchip = chip; return 0; } @@ -1324,17 +1457,28 @@ static int phytium_i2s_probe(struct platform_device *pdev) i2s->paddr = res->start; i2s->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(i2s->regs)) + return PTR_ERR(i2s->regs); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); i2s->regs_db = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(i2s->regs)) - return PTR_ERR(i2s->regs); + if (IS_ERR(i2s->regs_db)) + return PTR_ERR(i2s->regs_db); i2s->irq_id = platform_get_irq(pdev, 0); if (i2s->irq_id < 0) return i2s->irq_id; + i2s->gpio_irq_id = platform_get_irq(pdev, 1); + i2s->detect = true; + + if (i2s->gpio_irq_id < 0) + i2s->detect = false; + else + i2s->regs_gpio = i2s->regs + I2S_GPIO_BASE; + i2s->i2s_reg_comp1 = I2S_COMP_PARAM_1; i2s->i2s_reg_comp2 = I2S_COMP_PARAM_2; @@ -1431,3 +1575,4 @@ module_platform_driver(phytium_i2s_driver); MODULE_DESCRIPTION("Phytium I2S Driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Zhang Yiqun "); +MODULE_VERSION(PHYTIUM_I2S_V1_VERSION); diff --git a/sound/soc/phytium/pmdk_es8336.c b/sound/soc/phytium/pmdk_es8336.c index 258aa64035b891a1a651282fa671131814984bda..1008533b286df2ec2bc5683b0c1b44640b60780c 100644 --- a/sound/soc/phytium/pmdk_es8336.c +++ b/sound/soc/phytium/pmdk_es8336.c @@ -8,6 +8,7 @@ #include #include +#define PMDK_ES8336_VERSION "1.0.0" /* PMDK widgets */ static const struct snd_soc_dapm_widget pmdk_es8336_dapm_widgets[] = { @@ -95,3 +96,4 @@ module_platform_driver(pmdk_sound_driver); MODULE_AUTHOR("Zhang Yiqun "); MODULE_DESCRIPTION("ALSA SoC PMDK ES8336"); MODULE_LICENSE("GPL"); +MODULE_VERSION(PMDK_ES8336_VERSION); diff --git a/sound/soc/phytium/pmdk_es8388.c b/sound/soc/phytium/pmdk_es8388.c index dcee078b17e62bdb1f89f8cf61cd1ad18fe580b7..066fa46ed43658e1ffd2d0649b3cab6d69090102 100644 --- a/sound/soc/phytium/pmdk_es8388.c +++ b/sound/soc/phytium/pmdk_es8388.c @@ -9,6 +9,8 @@ #include #include +#define PMDK_ES8388_VERSION "1.0.0" + static struct snd_soc_jack hs_jack; /* Headset jack detection DAPM pins */ @@ -167,3 +169,4 @@ module_platform_driver(pmdk_sound_driver); MODULE_AUTHOR("Zhang Yiqun"); MODULE_DESCRIPTION("ALSA SoC PMDK ES8388"); MODULE_LICENSE("GPL"); +MODULE_VERSION(PMDK_ES8388_VERSION);