diff --git a/Documentation/devicetree/bindings/pwm/loongson,ls7a-pwm.yaml b/Documentation/devicetree/bindings/pwm/loongson,ls7a-pwm.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5d64fb40a0d6207c3fec50e3fda44f18c8983e0d --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/loongson,ls7a-pwm.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/loongson,ls7a-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Loongson PWM Controller + +maintainers: + - Binbin Zhou + +description: + The Loongson PWM has one pulse width output signal and one pulse input + signal to be measured. + It can be found on Loongson-2K series cpus and Loongson LS7A bridge chips. + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - const: loongson,ls7a-pwm + - items: + - enum: + - loongson,ls2k0500-pwm + - loongson,ls2k1000-pwm + - loongson,ls2k2000-pwm + - const: loongson,ls7a-pwm + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + '#pwm-cells': + description: + The first cell must have a value of 0, which specifies the PWM output signal; + The second cell is the period in nanoseconds; + The third cell flag supported by this binding is PWM_POLARITY_INVERTED. + const: 3 + +required: + - compatible + - reg + - interrupts + - clocks + +additionalProperties: false + +examples: + - | + #include + #include + + pwm@1fe22000 { + compatible = "loongson,ls2k1000-pwm", "loongson,ls7a-pwm"; + reg = <0x1fe22000 0x10>; + interrupt-parent = <&liointc0>; + interrupts = <24 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LOONGSON2_APB_CLK>; + #pwm-cells = <3>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index c6a3ac61989c1402c7b16db4c5f41daee922dffe..5f7d8f6418002afacab16bf18ccb7e05273118bd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12438,6 +12438,13 @@ S: Maintained F: Documentation/devicetree/bindings/i2c/loongson,ls2x-i2c.yaml F: drivers/i2c/busses/i2c-ls2x.c +LOONGSON PWM DRIVER +M: Binbin Zhou +L: linux-pwm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/pwm/loongson,ls7a-pwm.yaml +F: drivers/pwm/pwm-loongson.c + LOONGSON-2 SOC SERIES CLOCK DRIVER M: Yinbo Zhu L: linux-clk@vger.kernel.org diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 4a021d86fc578d56b3151beafaabd009de2788f2..316bf567759db33d64fb94ebf7b45e7fcbdc0d49 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -184,6 +184,7 @@ config LOONGARCH select USE_PERCPU_NUMA_NODE_ID select USER_STACKTRACE_SUPPORT select ZONE_DMA32 + select CPUMASK_OFFSTACK if NR_CPUS > 256 config 32BIT bool @@ -447,14 +448,23 @@ config HOTPLUG_CPU Say N if you want to disable CPU hotplug. config NR_CPUS - int "Maximum number of CPUs (2-256)" - range 2 256 + int "Maximum number of CPUs (2-2048)" + range 2 2048 depends on SMP - default "64" + default "2048" help This allows you to specify the maximum number of CPUs which this kernel will support. +config SCHED_MC + def_bool y + prompt "Multi-core scheduler support" + depends on SMP + help + Multi-core scheduler support improves the CPU scheduler's decision + making when dealing with multi-core CPU chips at a cost of slightly + increased overhead in some places. If unsure say N here. + config NUMA bool "NUMA Support" select SMP diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 210ab156facdfe932a898986024ef808b5d031de..f0bcd1644e4b12b5962af709b7db6c70d7b3ec11 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -42,7 +42,6 @@ CONFIG_KEXEC=y CONFIG_CRASH_DUMP=y CONFIG_CMDLINE="vfio_iommu_type1.allow_unsafe_interrupts=1 nokaslr" CONFIG_CMDLINE_EXTEND=y -CONFIG_NR_CPUS=256 CONFIG_NUMA=y CONFIG_ARCH_IOREMAP=y CONFIG_CPU_HAS_LSX=y @@ -847,6 +846,7 @@ CONFIG_I40E_DCB=y CONFIG_I40EVF=m CONFIG_ICE=m CONFIG_FM10K=m +CONFIG_IGC=m # CONFIG_NET_VENDOR_MARVELL is not set CONFIG_MLX4_EN=m # CONFIG_MLX4_CORE_GEN2 is not set @@ -1179,6 +1179,7 @@ CONFIG_PINCTRL_LOONGSON2=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_AMDPT=m CONFIG_GPIO_LOONGSON_64BIT=y +CONFIG_GPIO_PCA953X=m CONFIG_GPIO_VIPERBOARD=m CONFIG_POWER_RESET=y CONFIG_SENSORS_AD7414=m @@ -1945,6 +1946,7 @@ CONFIG_NTB_TOOL=m CONFIG_NTB_PERF=m CONFIG_NTB_TRANSPORT=m CONFIG_PWM=y +CONFIG_PWM_LOONGSON=y CONFIG_POWERCAP=y CONFIG_USB4=m CONFIG_DAX=y diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h index cc232901e4dd2373f97eb38b3955936919526d2f..1a7dba6256eaf3d55e35e11fe627ede048d0c863 100644 --- a/arch/loongarch/include/asm/smp.h +++ b/arch/loongarch/include/asm/smp.h @@ -24,6 +24,7 @@ extern int num_processors; extern int disabled_cpus; extern cpumask_t cpu_sibling_map[]; extern cpumask_t cpu_core_map[]; +extern cpumask_t cpu_llc_shared_map[]; extern cpumask_t cpu_foreign_map[]; void loongson_smp_setup(void); diff --git a/arch/loongarch/include/asm/topology.h b/arch/loongarch/include/asm/topology.h index 379f5e4830ebd5a96f4e4d33c6b40dd03f9c7031..5c485999cbb2e8a34b271dae50684a313642dfb0 100644 --- a/arch/loongarch/include/asm/topology.h +++ b/arch/loongarch/include/asm/topology.h @@ -35,6 +35,15 @@ extern unsigned int __max_packages; #define topology_core_id(cpu) (cpu_data[cpu].core) #define topology_core_cpumask(cpu) (&cpu_core_map[cpu]) #define topology_sibling_cpumask(cpu) (&cpu_sibling_map[cpu]) + +/* + * return cpus that shares the last level cache. + */ +static inline const struct cpumask *cpu_coregroup_mask(int cpu) +{ + return &cpu_llc_shared_map[cpu]; +} + #else #define topology_max_packages() (1) #endif diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 5d02bf5126b7177c9f79a4d38bfe74f01e6a3bff..0201eb35df4c7faea545199cf76c21419d8c4fbd 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -49,6 +49,9 @@ EXPORT_SYMBOL(cpu_sibling_map); cpumask_t cpu_core_map[NR_CPUS] __read_mostly; EXPORT_SYMBOL(cpu_core_map); +cpumask_t cpu_llc_shared_map[NR_CPUS] __read_mostly; +EXPORT_SYMBOL(cpu_llc_shared_map); + static DECLARE_COMPLETION(cpu_starting); static DECLARE_COMPLETION(cpu_running); @@ -65,6 +68,10 @@ static cpumask_t cpu_sibling_setup_map; /* representing cpus for which core maps can be computed */ static cpumask_t cpu_core_setup_map; +/* representing cpus for which llc sibling maps can be computed */ +static cpumask_t cpu_llc_shared_setup_map; + + struct secondary_data cpuboot_data; static DEFINE_PER_CPU(int, cpu_state); @@ -103,6 +110,42 @@ static inline void set_cpu_core_map(int cpu) } } +static inline bool cpus_are_shared_llc(int cpua, int cpub) +{ + if (cpu_to_node(cpua) != cpu_to_node(cpub)) + return false; + + return true; +} + +static inline void set_cpu_llc_shared_map(int cpu) +{ + int i; + + cpumask_set_cpu(cpu, &cpu_llc_shared_setup_map); + + for_each_cpu(i, &cpu_llc_shared_setup_map) { + if (cpus_are_shared_llc(cpu, i)) { + cpumask_set_cpu(i, &cpu_llc_shared_map[cpu]); + cpumask_set_cpu(cpu, &cpu_llc_shared_map[i]); + } + } +} + +static inline void clear_cpu_llc_shared_map(int cpu) +{ + int i; + + for_each_cpu(i, &cpu_llc_shared_setup_map) { + if (cpus_are_shared_llc(cpu, i)) { + cpumask_clear_cpu(i, &cpu_llc_shared_map[cpu]); + cpumask_clear_cpu(cpu, &cpu_llc_shared_map[i]); + } + } + + cpumask_clear_cpu(cpu, &cpu_llc_shared_setup_map); +} + static inline void set_cpu_sibling_map(int cpu) { int i; @@ -401,6 +444,7 @@ int loongson_cpu_disable(void) #endif set_cpu_online(cpu, false); clear_cpu_sibling_map(cpu); + clear_cpu_llc_shared_map(cpu); calculate_cpu_foreign_map(); local_irq_save(flags); fixup_irqs(); @@ -523,6 +567,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus) loongson_prepare_cpus(max_cpus); set_cpu_sibling_map(0); set_cpu_core_map(0); + set_cpu_llc_shared_map(0); calculate_cpu_foreign_map(); #ifndef CONFIG_HOTPLUG_CPU init_cpu_present(cpu_possible_mask); @@ -564,6 +609,7 @@ asmlinkage void start_secondary(void) set_cpu_sibling_map(cpu); set_cpu_core_map(cpu); + set_cpu_llc_shared_map(cpu); notify_cpu_starting(cpu); diff --git a/arch/loongarch/mm/hugetlbpage.c b/arch/loongarch/mm/hugetlbpage.c index 62ddcea0aa14672cc4906f9706f0934d2735502b..aca52c42e94e8b993d63e1b930578815138f7628 100644 --- a/arch/loongarch/mm/hugetlbpage.c +++ b/arch/loongarch/mm/hugetlbpage.c @@ -47,7 +47,7 @@ pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr, pmd = pmd_offset(pud, addr); } } - return (pte_t *) pmd; + return pmd_none(pmdp_get(pmd)) ? NULL : (pte_t *) pmd; } int pmd_huge(pmd_t pmd) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index b1310e69d07dafde44289a010ea2eb1726330d0d..f5b44909c74289fb86aa7222183b9b7e9d7b3d04 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -10,6 +10,9 @@ #include #include #include +#ifdef CONFIG_LOONGARCH +#include +#endif #include #include #include @@ -1079,6 +1082,13 @@ static irqreturn_t nvme_irq(int irq, void *data) struct nvme_queue *nvmeq = data; DEFINE_IO_COMP_BATCH(iob); +#ifdef CONFIG_LOONGARCH + /* + * There is no guarantee of sequence between DMA + * and interrupts on the Loongson platform. + */ + udelay(30); +#endif if (nvme_poll_cq(nvmeq, &iob)) { if (!rq_list_empty(iob.req_list)) nvme_pci_complete_batch(&iob); diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 8ebcddf91f7b78582ab3b182879477a7be4f5d38..da57f4a2bde0fb368d89c80bfb70557bfb24deec 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -314,6 +314,18 @@ config PWM_KEEMBAY To compile this driver as a module, choose M here: the module will be called pwm-keembay. +config PWM_LOONGSON + tristate "Loongson PWM support" + depends on MACH_LOONGSON64 || COMPILE_TEST + depends on COMMON_CLK + help + Generic PWM framework driver for Loongson family. + It can be found on Loongson-2K series cpus and Loongson LS7A + bridge chips. + + To compile this driver as a module, choose M here: the module + will be called pwm-loongson. + config PWM_LP3943 tristate "TI/National Semiconductor LP3943 PWM support" depends on MFD_LP3943 diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index c822389c2a24c2dd76b106ebb64b4a215f522889..5d5b64c25b7f79d0aa33b3667f3bb7dc9407bb63 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_PWM_INTEL_LGM) += pwm-intel-lgm.o obj-$(CONFIG_PWM_IQS620A) += pwm-iqs620a.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_KEEMBAY) += pwm-keembay.o +obj-$(CONFIG_PWM_LOONGSON) += pwm-loongson.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o diff --git a/drivers/pwm/pwm-loongson.c b/drivers/pwm/pwm-loongson.c new file mode 100644 index 0000000000000000000000000000000000000000..8432a6ad1d106b6259d58785a6d7110fa778e390 --- /dev/null +++ b/drivers/pwm/pwm-loongson.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2017-2025 Loongson Technology Corporation Limited. + * + * Loongson PWM driver + * + * For Loongson's PWM IP block documentation please refer Chapter 11 of + * Reference Manual: https://loongson.github.io/LoongArch-Documentation/Loongson-7A1000-usermanual-EN.pdf + * + * Author: Juxin Gao + * Further cleanup and restructuring by: + * Binbin Zhou + * + * Limitations: + * - If both DUTY and PERIOD are set to 0, the output is a constant low signal. + * - When disabled the output is driven to 0 independent of the configured + * polarity. + * - If the register is reconfigured while PWM is running, it does not complete + * the currently running period. + * - Disabling the PWM stops the output immediately (without waiting for current + * period to complete first). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Loongson PWM registers */ +#define LOONGSON_PWM_REG_DUTY 0x4 /* Low Pulse Buffer Register */ +#define LOONGSON_PWM_REG_PERIOD 0x8 /* Pulse Period Buffer Register */ +#define LOONGSON_PWM_REG_CTRL 0xc /* Control Register */ + +/* Control register bits */ +#define LOONGSON_PWM_CTRL_REG_EN BIT(0) /* Counter Enable Bit */ +#define LOONGSON_PWM_CTRL_REG_OE BIT(3) /* Pulse Output Enable Control Bit, Valid Low */ +#define LOONGSON_PWM_CTRL_REG_SINGLE BIT(4) /* Single Pulse Control Bit */ +#define LOONGSON_PWM_CTRL_REG_INTE BIT(5) /* Interrupt Enable Bit */ +#define LOONGSON_PWM_CTRL_REG_INT BIT(6) /* Interrupt Bit */ +#define LOONGSON_PWM_CTRL_REG_RST BIT(7) /* Counter Reset Bit */ +#define LOONGSON_PWM_CTRL_REG_CAPTE BIT(8) /* Measurement Pulse Enable Bit */ +#define LOONGSON_PWM_CTRL_REG_INVERT BIT(9) /* Output flip-flop Enable Bit */ +#define LOONGSON_PWM_CTRL_REG_DZONE BIT(10) /* Anti-dead Zone Enable Bit */ + +/* default input clk frequency for the ACPI case */ +#define LOONGSON_PWM_FREQ_DEFAULT 50000 /* Hz */ + +struct pwm_loongson_ddata { + struct pwm_chip chip; + struct clk *clk; + void __iomem *base; + u64 clk_rate; +}; + +static inline __pure struct pwm_loongson_ddata *to_pwm_loongson_ddata(struct pwm_chip *chip) +{ + return container_of(chip, struct pwm_loongson_ddata, chip); +} + +static inline u32 pwm_loongson_readl(struct pwm_loongson_ddata *ddata, u32 offset) +{ + return readl(ddata->base + offset); +} + +static inline void pwm_loongson_writel(struct pwm_loongson_ddata *ddata, + u32 val, u32 offset) +{ + writel(val, ddata->base + offset); +} + +static int pwm_loongson_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, + enum pwm_polarity polarity) +{ + u16 val; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + + if (polarity == PWM_POLARITY_INVERSED) + /* Duty cycle defines LOW period of PWM */ + val |= LOONGSON_PWM_CTRL_REG_INVERT; + else + /* Duty cycle defines HIGH period of PWM */ + val &= ~LOONGSON_PWM_CTRL_REG_INVERT; + + pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); + + return 0; +} + +static void pwm_loongson_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + u32 val; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + val &= ~LOONGSON_PWM_CTRL_REG_EN; + pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); +} + +static int pwm_loongson_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + u32 val; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + val = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + val |= LOONGSON_PWM_CTRL_REG_EN; + pwm_loongson_writel(ddata, val, LOONGSON_PWM_REG_CTRL); + + return 0; +} + +static int pwm_loongson_config(struct pwm_chip *chip, struct pwm_device *pwm, + u64 duty_ns, u64 period_ns) +{ + u32 duty, period; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + /* duty = duty_ns * ddata->clk_rate / NSEC_PER_SEC */ + duty = mul_u64_u64_div_u64(duty_ns, ddata->clk_rate, NSEC_PER_SEC); + if (duty > U32_MAX) + duty = U32_MAX; + + /* period = period_ns * ddata->clk_rate / NSEC_PER_SEC */ + period = mul_u64_u64_div_u64(period_ns, ddata->clk_rate, NSEC_PER_SEC); + if (period > U32_MAX) + period = U32_MAX; + + pwm_loongson_writel(ddata, duty, LOONGSON_PWM_REG_DUTY); + pwm_loongson_writel(ddata, period, LOONGSON_PWM_REG_PERIOD); + + return 0; +} + +static int pwm_loongson_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int ret; + bool enabled = pwm->state.enabled; + + if (!state->enabled) { + if (enabled) + pwm_loongson_disable(chip, pwm); + return 0; + } + + ret = pwm_loongson_set_polarity(chip, pwm, state->polarity); + if (ret) + return ret; + + ret = pwm_loongson_config(chip, pwm, state->duty_cycle, state->period); + if (ret) + return ret; + + if (!enabled && state->enabled) + ret = pwm_loongson_enable(chip, pwm); + + return ret; +} + +static int pwm_loongson_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + u32 duty, period, ctrl; + struct pwm_loongson_ddata *ddata = to_pwm_loongson_ddata(chip); + + duty = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_DUTY); + period = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_PERIOD); + ctrl = pwm_loongson_readl(ddata, LOONGSON_PWM_REG_CTRL); + + /* duty & period have a max of 2^32, so we can't overflow */ + state->duty_cycle = DIV64_U64_ROUND_UP((u64)duty * NSEC_PER_SEC, ddata->clk_rate); + state->period = DIV64_U64_ROUND_UP((u64)period * NSEC_PER_SEC, ddata->clk_rate); + state->polarity = (ctrl & LOONGSON_PWM_CTRL_REG_INVERT) ? PWM_POLARITY_INVERSED : + PWM_POLARITY_NORMAL; + state->enabled = (ctrl & LOONGSON_PWM_CTRL_REG_EN) ? true : false; + + return 0; +} + +static const struct pwm_ops pwm_loongson_ops = { + .apply = pwm_loongson_apply, + .get_state = pwm_loongson_get_state, +}; + +static int pwm_loongson_probe(struct platform_device *pdev) +{ + int ret; + struct pwm_loongson_ddata *ddata; + struct device *dev = &pdev->dev; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + ddata->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ddata->base)) + return PTR_ERR(ddata->base); + + if (!has_acpi_companion(dev)) { + ddata->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(ddata->clk)) + return dev_err_probe(dev, PTR_ERR(ddata->clk), + "failed to get pwm clock\n"); + ddata->clk_rate = clk_get_rate(ddata->clk); + } else { + ddata->clk_rate = LOONGSON_PWM_FREQ_DEFAULT; + } + + /* This check is done to prevent an overflow in .apply */ + if (ddata->clk_rate > NSEC_PER_SEC) + return dev_err_probe(dev, -EINVAL, "PWM clock out of range\n"); + + ddata->chip.dev = dev; + ddata->chip.ops = &pwm_loongson_ops; + ddata->chip.npwm = 1; + platform_set_drvdata(pdev, ddata); + + ret = devm_pwmchip_add(dev, &ddata->chip); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); + + return 0; +} + +static int pwm_loongson_suspend(struct device *dev) +{ + struct pwm_loongson_ddata *ddata = dev_get_drvdata(dev); + struct pwm_device *pwm = &ddata->chip.pwms[0]; + + if (pwm->state.enabled) + return -EBUSY; + + clk_disable_unprepare(ddata->clk); + + return 0; +} + +static int pwm_loongson_resume(struct device *dev) +{ + struct pwm_loongson_ddata *ddata = dev_get_drvdata(dev); + + return clk_prepare_enable(ddata->clk); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(pwm_loongson_pm_ops, pwm_loongson_suspend, + pwm_loongson_resume); + +static const struct of_device_id pwm_loongson_of_ids[] = { + { .compatible = "loongson,ls7a-pwm" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, pwm_loongson_of_ids); + +static const struct acpi_device_id pwm_loongson_acpi_ids[] = { + { "LOON0006" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, pwm_loongson_acpi_ids); + +static struct platform_driver pwm_loongson_driver = { + .probe = pwm_loongson_probe, + .driver = { + .name = "loongson-pwm", + .pm = pm_ptr(&pwm_loongson_pm_ops), + .of_match_table = pwm_loongson_of_ids, + .acpi_match_table = pwm_loongson_acpi_ids, + }, +}; +module_platform_driver(pwm_loongson_driver); + +MODULE_DESCRIPTION("Loongson PWM driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited."); +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index d4b97f0a50131abac54b0a8652932e66de4acb3a..68cf8840d8c2784b9e058a2cbf6cbb7e7a632f38 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -3881,7 +3881,13 @@ static irqreturn_t megasas_isr_fusion(int irq, void *devp) instance->instancet->clear_intr(instance); return IRQ_HANDLED; } - +#ifdef CONFIG_LOONGARCH + /* + * There is no guarantee of sequence between DMA + * and interrupts on the Loongson platform. + */ + udelay(30); +#endif return complete_cmd_fusion(instance, irq_context->MSIxIndex, irq_context) ? IRQ_HANDLED : IRQ_NONE; } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 09aea6306ec3cd0b464d31e4ccddb72ef4c430ef..ada5e2264792901e7e4b9eef1022cd83570bd2d0 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -4283,6 +4283,14 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, } retry: + /* + * Deal with possible cpuset update races or zonelist updates to avoid + * infinite retries. + */ + if (check_retry_cpuset(cpuset_mems_cookie, ac) || + check_retry_zonelist(zonelist_iter_cookie)) + goto restart; + /* Ensure kswapd doesn't accidentally go to sleep as long as we loop */ if (alloc_flags & ALLOC_KSWAPD) wake_all_kswapds(order, gfp_mask, ac);