From 78c466f2fb2dbcfe56a3880b560aa5a3dedd4662 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 12 Mar 2020 17:40:53 +0800 Subject: [PATCH 001/130] BACKPORT: FROMLIST: MIPS: Add __cpu_full_name[] to make CPU names more human-readable In /proc/cpuinfo, we keep "cpu model" as is, since GCC should use it for -march=native. Besides, we add __cpu_full_name[] to describe the processor in a more human-readable manner. The full name is displayed as "model name" in cpuinfo, which is needed by some userspace tools such as "lscpu" and "gnome-system-monitor". The CPU frequency in "model name" is the default value (highest), and there is also a "CPU MHz" whose value can be changed by cpufreq. This is only used by Loongson now (ICT is dropped in cpu name, and cpu name can be overwritten by BIOS). Why drop ICT? At the beginning, Loongson is designed by ICT, but now all Loongson processors is designed by "Loongson Technology Corporation Limited" which is independent from ICT. We have search the code in https://codesearch.debian.net/, and the result is: 1, GCC searches the "cpu model" in cpuinfo, but it only matches "Loongson-2 V0.2" and so on, so drop "ICT" is comfortable; 2, Debian Installer searches the "cpu model" in cpuinfo and matches "ICT Loongson", but Yunqiang Su is modifying the code; 3, Valgrind searches the "cpu model" in cpuinfo and matches "ICT Loongson", and we are planning to fix it up. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1584006053-28887-1-git-send-email-chenhc@lemote.com/ [Kexy: Resolved minor conflicts in arch/mips/kernel/cpu-probe.c, arch/mips/loongson64/env.c, and arch/mips/loongson64/smp.c] Signed-off-by: Kexy Biscuit --- arch/mips/include/asm/cpu-info.h | 2 ++ .../include/asm/mach-loongson64/boot_param.h | 1 + arch/mips/include/asm/time.h | 2 ++ arch/mips/kernel/cpu-probe.c | 30 ++++++++++++++----- arch/mips/kernel/proc.c | 6 ++++ arch/mips/kernel/time.c | 2 ++ arch/mips/loongson64/env.c | 13 ++++++++ arch/mips/loongson64/smp.c | 1 + arch/mips/loongson64/smp.h | 1 + 9 files changed, 51 insertions(+), 7 deletions(-) diff --git a/arch/mips/include/asm/cpu-info.h b/arch/mips/include/asm/cpu-info.h index a600670d00e97..d2ea1476aaae8 100644 --- a/arch/mips/include/asm/cpu-info.h +++ b/arch/mips/include/asm/cpu-info.h @@ -125,7 +125,9 @@ extern void cpu_probe(void); extern void cpu_report(void); extern const char *__cpu_name[]; +extern const char *__cpu_full_name[]; #define cpu_name_string() __cpu_name[raw_smp_processor_id()] +#define cpu_full_name_string() __cpu_full_name[raw_smp_processor_id()] struct seq_file; struct notifier_block; diff --git a/arch/mips/include/asm/mach-loongson64/boot_param.h b/arch/mips/include/asm/mach-loongson64/boot_param.h index 9218b3ae33832..d1f064d885662 100644 --- a/arch/mips/include/asm/mach-loongson64/boot_param.h +++ b/arch/mips/include/asm/mach-loongson64/boot_param.h @@ -66,6 +66,7 @@ struct efi_cpuinfo_loongson { u16 reserved_cores_mask; u32 cpu_clock_freq; /* cpu_clock */ u32 nr_cpus; + char cpuname[64]; } __packed; #define MAX_UARTS 64 diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index e855a3611d922..3ba2bc06ae778 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h @@ -22,6 +22,8 @@ extern spinlock_t rtc_lock; */ extern void plat_time_init(void); +extern unsigned int mips_cpu_frequency; + /* * mips_hpt_frequency - must be set if you intend to use an R4k-compatible * counter as a timer interrupt source. diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index c7fee72ea6067..763095c43263a 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -1249,32 +1249,44 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu) switch (c->processor_id & PRID_REV_MASK) { case PRID_REV_LOONGSON2E: c->cputype = CPU_LOONGSON2EF; - __cpu_name[cpu] = "ICT Loongson-2"; + __cpu_name[cpu] = "Loongson-2"; set_elf_platform(cpu, "loongson2e"); set_isa(c, MIPS_CPU_ISA_III); c->fpu_msk31 |= FPU_CSR_CONDX; + __cpu_full_name[cpu] = "Loongson-2E"; break; case PRID_REV_LOONGSON2F: c->cputype = CPU_LOONGSON2EF; - __cpu_name[cpu] = "ICT Loongson-2"; + __cpu_name[cpu] = "Loongson-2"; set_elf_platform(cpu, "loongson2f"); set_isa(c, MIPS_CPU_ISA_III); c->fpu_msk31 |= FPU_CSR_CONDX; + __cpu_full_name[cpu] = "Loongson-2F"; break; case PRID_REV_LOONGSON3A_R1: c->cputype = CPU_LOONGSON64; - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R1); + __cpu_full_name[cpu] = "Loongson-3A R1 (Loongson-3A1000)"; c->ases |= (MIPS_ASE_LOONGSON_MMI | MIPS_ASE_LOONGSON_CAM | MIPS_ASE_LOONGSON_EXT); break; case PRID_REV_LOONGSON3B_R1: + c->cputype = CPU_LOONGSON64; + __cpu_name[cpu] = "Loongson-3"; + set_elf_platform(cpu, "loongson3b"); + set_isa(c, MIPS_CPU_ISA_M64R1); + __cpu_full_name[cpu] = "Loongson-3B R1 (Loongson-3B1000)"; + c->ases |= (MIPS_ASE_LOONGSON_MMI | MIPS_ASE_LOONGSON_CAM | + MIPS_ASE_LOONGSON_EXT); + break; case PRID_REV_LOONGSON3B_R2: c->cputype = CPU_LOONGSON64; - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3b"); set_isa(c, MIPS_CPU_ISA_M64R1); + __cpu_full_name[cpu] = "Loongson-3B R2 (Loongson-3B1500)"; c->ases |= (MIPS_ASE_LOONGSON_MMI | MIPS_ASE_LOONGSON_CAM | MIPS_ASE_LOONGSON_EXT); break; @@ -1702,15 +1714,17 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) switch (c->processor_id & PRID_REV_MASK) { case PRID_REV_LOONGSON3A_R2_0: case PRID_REV_LOONGSON3A_R2_1: - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R2); + __cpu_full_name[cpu] = "Loongson-3A R2 (Loongson-3A2000)"; break; case PRID_REV_LOONGSON3A_R3_0: case PRID_REV_LOONGSON3A_R3_1: - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R2); + __cpu_full_name[cpu] = "Loongson-3A R3 (Loongson-3A3000)"; break; } /* @@ -1729,9 +1743,10 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) LOONGSON_CONF6_INTIMER); break; case PRID_IMP_LOONGSON_64G: - __cpu_name[cpu] = "ICT Loongson-3"; + __cpu_name[cpu] = "Loongson-3"; set_elf_platform(cpu, "loongson3a"); set_isa(c, MIPS_CPU_ISA_M64R2); + __cpu_full_name[cpu] = "Loongson-3A R4 (Loongson-3A4000)"; decode_cpucfg(c); change_c0_config6(LOONGSON_CONF6_EXTIMER | LOONGSON_CONF6_INTIMER, LOONGSON_CONF6_INTIMER); @@ -1837,6 +1852,7 @@ EXPORT_SYMBOL(__ua_limit); #endif const char *__cpu_name[NR_CPUS]; +const char *__cpu_full_name[NR_CPUS]; const char *__elf_platform; const char *__elf_base_platform; diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c index 8eba5a1ed664c..23fe62a3cdde4 100644 --- a/arch/mips/kernel/proc.c +++ b/arch/mips/kernel/proc.c @@ -15,6 +15,7 @@ #include #include #include +#include unsigned int vced_count, vcei_count; @@ -63,6 +64,11 @@ static int show_cpuinfo(struct seq_file *m, void *v) seq_printf(m, fmt, __cpu_name[n], (version >> 4) & 0x0f, version & 0x0f, (fp_vers >> 4) & 0x0f, fp_vers & 0x0f); + if (__cpu_full_name[n]) + seq_printf(m, "model name\t\t: %s\n", __cpu_full_name[n]); + if (mips_cpu_frequency) + seq_printf(m, "CPU MHz\t\t\t: %u.%02u\n", + mips_cpu_frequency / 1000000, (mips_cpu_frequency / 10000) % 100); seq_printf(m, "BogoMIPS\t\t: %u.%02u\n", cpu_data[n].udelay_val / (500000/HZ), (cpu_data[n].udelay_val / (5000/HZ)) % 100); diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c index ed339d7979f3f..849c6be53b296 100644 --- a/arch/mips/kernel/time.c +++ b/arch/mips/kernel/time.c @@ -120,6 +120,8 @@ EXPORT_SYMBOL(perf_irq); * 2) calculate a couple of cached variables for later usage */ +unsigned int mips_cpu_frequency; +EXPORT_SYMBOL_GPL(mips_cpu_frequency); unsigned int mips_hpt_frequency; EXPORT_SYMBOL_GPL(mips_hpt_frequency); diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c index 09ff052698614..d526d1b14d3ac 100644 --- a/arch/mips/loongson64/env.c +++ b/arch/mips/loongson64/env.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ u32 cpu_clock_freq; EXPORT_SYMBOL(cpu_clock_freq); +char cpu_full_name[64]; struct efi_memory_map_loongson *loongson_memmap; struct loongson_system_configuration loongson_sysconf; @@ -58,6 +60,7 @@ void __init prom_dtb_init_env(void) void __init prom_lefi_init_env(void) { + char freq[12]; struct boot_params *boot_p; struct loongson_params *loongson_p; struct system_loongson *esys; @@ -152,6 +155,10 @@ void __init prom_lefi_init_env(void) loongson_sysconf.nr_nodes = (loongson_sysconf.nr_cpus + loongson_sysconf.cores_per_node - 1) / loongson_sysconf.cores_per_node; + if (!strncmp(ecpu->cpuname, "Loongson", 8)) + strncpy(cpu_full_name, ecpu->cpuname, sizeof(cpu_full_name)); + if (cpu_full_name[0] == 0) + strncpy(cpu_full_name, __cpu_full_name[0], sizeof(cpu_full_name)); loongson_sysconf.dma_mask_bits = eirq_source->dma_mask_bits; if (loongson_sysconf.dma_mask_bits < 32 || @@ -175,8 +182,14 @@ void __init prom_lefi_init_env(void) loongson_sysconf.workarounds |= esys->workarounds; + mips_cpu_frequency = cpu_clock_freq; pr_info("CpuClock = %u\n", cpu_clock_freq); + /* Append default cpu frequency with round-off */ + sprintf(freq, " @ %uMHz", (cpu_clock_freq + 500000) / 1000000); + strncat(cpu_full_name, freq, sizeof(cpu_full_name)); + __cpu_full_name[0] = cpu_full_name; + /* Read the ID of PCI host bridge to detect bridge type */ id = readl(HOST_BRIDGE_CONFIG_ADDR); vendor = id & 0xffff; diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c index 979993679d913..4b621981f3890 100644 --- a/arch/mips/loongson64/smp.c +++ b/arch/mips/loongson64/smp.c @@ -449,6 +449,7 @@ static void loongson3_init_secondary(void) initcount = core0_c0count[cpu] + i/2; write_c0_count(initcount); + __cpu_full_name[cpu] = cpu_full_name; } static void loongson3_smp_finish(void) diff --git a/arch/mips/loongson64/smp.h b/arch/mips/loongson64/smp.h index 957bde81e0e4e..6112d9d7dd226 100644 --- a/arch/mips/loongson64/smp.h +++ b/arch/mips/loongson64/smp.h @@ -3,6 +3,7 @@ #define __LOONGSON_SMP_H_ /* for Loongson-3 smp support */ +extern char cpu_full_name[64]; extern unsigned long long smp_group[4]; /* 4 groups(nodes) in maximum in numa case */ From 0683b36b0ae7c9a4b531f2f6b38e3c71a613d82c Mon Sep 17 00:00:00 2001 From: wangrui Date: Thu, 8 Apr 2021 19:30:59 +0800 Subject: [PATCH 002/130] BACKPORT: FROMLIST: MIPS: tlbex: Avoid access invalid address when pmd is modifying When user-space program accessing a virtual address and falls into TLB invalid exception handling. at almost the same time, if the pmd which that contains this virtual address is hit by THP scanning, and then a invalid address access may occurs in the tlb handler: CPU 0: (userspace) | CPU 1: (khugepaged) 1: | scan hit: set pmde to invalid_pmd_table | (by pmd_clear) 2: handle_tlbl(tlb invalid): | load pmde for huge page testing, | pmde doesn't contains _PAGE_HUGE | bit | 3: | collapsed: set pmde to huge page format 4: handle_tlbl(normal page case): | load pmde again as base address, | pmde doesn't contains an address, | access invalid address | This patch avoids the inconsistency of two memory loads by reusing the result of one load. Signed-off-by: wangrui Link: https://lore.kernel.org/all/20210212082058.40792-1-wangrui@loongson.cn/ Link: https://lore.kernel.org/all/CAAhV-H5a6f7SSaMfOJudFuqtzLqkDks_NkjsPe15gN3mJTGBDw@mail.gmail.com/ [Kexy: Resolved minor conflict in arch/mips/mm/tlbex.c] Signed-off-by: Kexy Biscuit --- arch/mips/mm/tlbex.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/arch/mips/mm/tlbex.c b/arch/mips/mm/tlbex.c index b4e1c783e6177..c595f16e1ef56 100644 --- a/arch/mips/mm/tlbex.c +++ b/arch/mips/mm/tlbex.c @@ -695,13 +695,12 @@ static void build_huge_tlb_write_entry(u32 **p, struct uasm_label **l, */ static void build_is_huge_pte(u32 **p, struct uasm_reloc **r, unsigned int tmp, - unsigned int pmd, int lid) + unsigned int pmde, int lid) { - UASM_i_LW(p, tmp, 0, pmd); if (use_bbit_insns()) { - uasm_il_bbit1(p, r, tmp, ilog2(_PAGE_HUGE), lid); + uasm_il_bbit1(p, r, pmde, ilog2(_PAGE_HUGE), lid); } else { - uasm_i_andi(p, tmp, tmp, _PAGE_HUGE); + uasm_i_andi(p, tmp, pmde, _PAGE_HUGE); uasm_il_bnez(p, r, tmp, lid); } } @@ -1061,7 +1060,6 @@ EXPORT_SYMBOL_GPL(build_update_entries); struct mips_huge_tlb_info { int huge_pte; int restore_scratch; - bool need_reload_pte; }; static struct mips_huge_tlb_info @@ -1076,7 +1074,6 @@ build_fast_tlb_refill_handler (u32 **p, struct uasm_label **l, rv.huge_pte = scratch; rv.restore_scratch = 0; - rv.need_reload_pte = false; if (check_for_high_segbits) { UASM_i_MFC0(p, tmp, C0_BADVADDR); @@ -1282,7 +1279,6 @@ static void build_r4000_tlb_refill_handler(void) } else { htlb_info.huge_pte = K0; htlb_info.restore_scratch = 0; - htlb_info.need_reload_pte = true; vmalloc_mode = refill_noscratch; /* * create the plain linear handler @@ -1307,11 +1303,14 @@ static void build_r4000_tlb_refill_handler(void) build_get_pgde32(&p, K0, K1); /* get pgd in K1 */ #endif + UASM_i_LW(&p, K0, 0, K1); /* get pmd entry in K0 */ #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT - build_is_huge_pte(&p, &r, K0, K1, label_tlb_huge_update); + build_is_huge_pte(&p, &r, K1, K0, label_tlb_huge_update); #endif - build_get_ptep(&p, K0, K1); + GET_CONTEXT(&p, K1); /* get context reg */ + build_adjust_context(&p, K1); + UASM_i_ADDU(&p, K1, K0, K1); /* add in offset */ build_update_entries(&p, K0, K1); build_tlb_write_entry(&p, &l, &r, tlb_random); uasm_l_leave(&l, p); @@ -1319,8 +1318,6 @@ static void build_r4000_tlb_refill_handler(void) } #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT uasm_l_tlb_huge_update(&l, p); - if (htlb_info.need_reload_pte) - UASM_i_LW(&p, htlb_info.huge_pte, 0, K1); build_huge_update_entries(&p, htlb_info.huge_pte, K1); build_huge_tlb_write_entry(&p, &l, &r, K0, tlb_random, htlb_info.restore_scratch); @@ -2019,20 +2016,20 @@ build_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l, build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */ #endif + UASM_i_LW(p, wr.r3, 0, wr.r2); /* get pmd entry in wr.r3 */ #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT /* - * For huge tlb entries, pmd doesn't contain an address but + * For huge tlb entries, pmde doesn't contain an address but * instead contains the tlb pte. Check the PAGE_HUGE bit and * see if we need to jump to huge tlb processing. */ - build_is_huge_pte(p, r, wr.r1, wr.r2, label_tlb_huge_update); + build_is_huge_pte(p, r, wr.r1, wr.r3, label_tlb_huge_update); #endif UASM_i_MFC0(p, wr.r1, C0_BADVADDR); - UASM_i_LW(p, wr.r2, 0, wr.r2); UASM_i_SRL(p, wr.r1, wr.r1, PAGE_SHIFT - PTE_T_LOG2); uasm_i_andi(p, wr.r1, wr.r1, (PTRS_PER_PTE - 1) << PTE_T_LOG2); - UASM_i_ADDU(p, wr.r2, wr.r2, wr.r1); + UASM_i_ADDU(p, wr.r2, wr.r3, wr.r1); #ifdef CONFIG_SMP uasm_l_smp_pgtable_change(l, *p); From 0411efaf31178401d88bd38e6cc99380e3528414 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 12 Mar 2020 17:41:57 +0800 Subject: [PATCH 003/130] FROMLIST: MIPS: Loongson: Add board_ebase_setup() support The EBase registers of old Loongson processor models before 3A2000 are 32bit and have no WG bit; those of newer models are 64bit and do have the WG bit. Unfortunately, dynamically allocated EBase addresses do not work well for the Loongson platform, because Loongson's memory layout is very limited below 0x20000000. The dynamically allocated EBase address above 0x20000000 is thus unmappable to a KSEG0/KSEG1 virtual address, but the cache error handler MUST be in KSEG1 (please see set_uncached_handler() in traps.c). Some might suggest that the cache error handler is hardly used so this is not a problem, but Loongson's MMIO configuration registers might be corrupted by set_uncached_handler(). To make Linux kernel on Loongson more robust, a board_ebase_setup() hook is added to ensure CKSEG0 is always used for EBase. This is also useful for configurations where firmware-provided EBase is not sane. Maybe this problem is present for all MIPSr2 processors, but it seems not all platforms have memory at physical address 0. So this patch only touches Loongson. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1584006117-28985-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/init.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/mips/loongson64/init.c b/arch/mips/loongson64/init.c index f25caa6aa9d30..36d3cf0f8b3ba 100644 --- a/arch/mips/loongson64/init.c +++ b/arch/mips/loongson64/init.c @@ -22,6 +22,16 @@ u32 node_id_offset; +static void __init mips_ebase_setup(void) +{ + ebase = CKSEG0; + + if (cpu_has_ebase_wg) + write_c0_ebase(ebase | MIPS_EBASE_WG); + + write_c0_ebase(ebase); +} + static void __init mips_nmi_setup(void) { void *base; @@ -143,6 +153,7 @@ void __init prom_init(void) setup_8250_early_printk_port(TO_UNCAC(LOONGSON_REG_BASE + 0x1e0), 0, 1024); register_smp_ops(&loongson3_smp_ops); + board_ebase_setup = mips_ebase_setup; board_nmi_handler_setup = mips_nmi_setup; } From 0170e51321d9a58895b12ae3f797e5ef163f0c65 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 19 Dec 2018 16:31:14 +0800 Subject: [PATCH 004/130] FROMLIST: scsi: lpfc: Switch memcpy_fromio() to __read32_copy() In commit bc73905abf770192 ("[SCSI] lpfc 8.3.16: SLI Additions, updates, and code cleanup"), lpfc_memcpy_to_slim() have switched memcpy_toio() to __write32_copy() in order to prevent unaligned 64 bit copy. Recently, we found that lpfc_memcpy_from_slim() have similar issues, so let it switch memcpy_fromio() to __read32_copy(). As maintainer says, it seems that we can hardly see a real "unaligned 64 bit copy", but this patch is still useful. Because in our tests we found that lpfc doesn't support 128 bit access, but some optimized memcpy() use 128 bit access (at lease on Loongson). Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1545208274-13736-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit --- drivers/scsi/lpfc/lpfc_compat.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_compat.h b/drivers/scsi/lpfc/lpfc_compat.h index 43cf46a3a71fe..0cd1e3c82d875 100644 --- a/drivers/scsi/lpfc/lpfc_compat.h +++ b/drivers/scsi/lpfc/lpfc_compat.h @@ -91,8 +91,8 @@ lpfc_memcpy_to_slim( void __iomem *dest, void *src, unsigned int bytes) static inline void lpfc_memcpy_from_slim( void *dest, void __iomem *src, unsigned int bytes) { - /* actually returns 1 byte past dest */ - memcpy_fromio( dest, src, bytes); + /* convert bytes in argument list to word count for copy function */ + __ioread32_copy(dest, src, bytes / sizeof(uint32_t)); } #endif /* __BIG_ENDIAN */ From 35e395c758ad74db12e2b6c5adddb416bf7a7981 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 24 Sep 2020 18:07:57 +0800 Subject: [PATCH 005/130] FROMLIST: MIPS: Crash kernel should be able to see old memories Kexec-tools use mem=X@Y to pass usable memories to crash kernel, but in commit a94e4f24ec836c8984f83959 ("MIPS: init: Drop boot_mem_map") all BIOS passed memories are removed by early_parse_mem(). I think this is reasonable for a normal kernel but not for a crash kernel, because a crash kernel should be able to see all old memories, even though it is not supposed to use them. Fixes: a94e4f24ec836c8984f83959 ("MIPS: init: Drop boot_mem_map") Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1600942079-18652-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit --- arch/mips/kernel/setup.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 3f45b72561db9..1a2bc9c489710 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -361,8 +361,10 @@ static int __init early_parse_mem(char *p) */ if (usermem == 0) { usermem = 1; +#ifndef CONFIG_CRASH_DUMP memblock_remove(memblock_start_of_DRAM(), memblock_end_of_DRAM() - memblock_start_of_DRAM()); +#endif } start = 0; size = memparse(p, &p); From 8851269f147ce2f884db9994eb07c3c8531fb461 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 24 Sep 2020 18:07:58 +0800 Subject: [PATCH 006/130] BACKPORT: FROMLIST: MIPS: Reserve extra memory for crash dump Traditionally, MIPS's contiguous low memory can be as less as 256M, so crashkernel=X@Y may be unable to large enough in some cases. Moreover, for the "multi numa node + sparse memory model" case, it is attempt to allocate section_mem_maps on every node. Thus, if the total memory of a node is more than 1GB, we reserve the top 128MB for the crash kernel. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1600942079-18652-2-git-send-email-chenhc@lemote.com/ [Kexy: Resolved minor conflict in arch/mips/kernel/setup.c] Signed-off-by: Kexy Biscuit --- arch/mips/kernel/setup.c | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 1a2bc9c489710..778d3909e2e66 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -58,6 +58,10 @@ EXPORT_SYMBOL(cpu_data); struct screen_info screen_info; #endif +#ifdef CONFIG_CRASH_DUMP +static phys_addr_t crashmem_start, crashmem_size; +#endif + /* * Setup information * @@ -376,6 +380,13 @@ static int __init early_parse_mem(char *p) else memblock_add(start, size); +#ifdef CONFIG_CRASH_DUMP + if (start && size) { + crashmem_start = start; + crashmem_size = size; + } +#endif + return 0; } early_param("mem", early_parse_mem); @@ -614,6 +625,48 @@ static void __init bootcmdline_init(void) bootcmdline_append(builtin_cmdline, COMMAND_LINE_SIZE); } +/* Traditionally, MIPS's contiguous low memory is 256M, so crashkernel=X@Y is + * unable to be large enough in some cases. Thus, if the total memory of a node + * is more than 1GB, we reserve the top 128MB for the crash kernel */ +static void reserve_crashm_region(int node, unsigned long s0, unsigned long e0) +{ +#ifdef CONFIG_KEXEC + if (crashk_res.start == crashk_res.end) + return; + + if ((e0 - s0) <= (SZ_1G >> PAGE_SHIFT)) + return; + + s0 = e0 - (SZ_128M >> PAGE_SHIFT); + + memblock_reserve(PFN_PHYS(s0), (e0 - s0) << PAGE_SHIFT); +#endif +} + +static void reserve_oldmem_region(int node, unsigned long s0, unsigned long e0) +{ +#ifdef CONFIG_CRASH_DUMP + unsigned long s1, e1; + + if (!is_kdump_kernel()) + return; + + if ((e0 - s0) > (SZ_1G >> PAGE_SHIFT)) + e0 = e0 - (SZ_128M >> PAGE_SHIFT); + + /* crashmem_start is crashk_res reserved by primary kernel */ + s1 = PFN_UP(crashmem_start); + e1 = PFN_DOWN(crashmem_start + crashmem_size); + + if (node == 0) { + memblock_reserve(PFN_PHYS(s0), (s1 - s0) << PAGE_SHIFT); + memblock_reserve(PFN_PHYS(e1), (e0 - e1) << PAGE_SHIFT); + } else { + memblock_reserve(PFN_PHYS(s0), (e0 - s0) << PAGE_SHIFT); + } +#endif +} + /* * arch_mem_init - initialize memory management subsystem * @@ -638,6 +691,9 @@ static void __init bootcmdline_init(void) */ static void __init arch_mem_init(char **cmdline_p) { + unsigned int node; + unsigned long start_pfn, end_pfn; + /* call board setup routine */ plat_mem_setup(); memblock_set_bottom_up(true); @@ -673,6 +729,12 @@ static void __init arch_mem_init(char **cmdline_p) mips_reserve_vmcore(); mips_parse_crashkernel(); + for_each_online_node(node) { + get_pfn_range_for_nid(node, &start_pfn, &end_pfn); + reserve_crashm_region(node, start_pfn, end_pfn); + reserve_oldmem_region(node, start_pfn, end_pfn); + } + device_tree_init(); /* From b544ea3ea88ec1c3567bab8366b34618b11a26e8 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Wed, 2 Apr 2014 12:13:15 +0200 Subject: [PATCH 007/130] BACKPORT: FROMLIST: MIPS syscall auditing patches this is the first cut of the MIPS auditing patches. MIPS doesn't quite fit into the existing pattern of other architectures and I'd appreciate your comments and maybe even an Acked-by. - MIPS syscalls return a success / error flag in register $7. If the flag is set then the return value in $2 is a *positive* error value. This means the existing AUDITSC_RESULT() macro does not work on MIPS and thus ptrace.c defines it's own version MIPS_AUDITSC_RESULT(). - Linux on MIPS extends the traditional syscall table used by older UNIX implementations. This is why 32-bit Linux syscalls are starting from 4000; the native 64-bit syscalls start from 5000 and the N32 compat ABI from 6000. The existing syscall bitmap is only large enough for at most 2048 syscalls, so I had to increase AUDIT_BITMASK_SIZE to 256 which provides enough space for 8192 syscalls. Because include/linux/audit.h and AUDIT_BITMASK_SIZE are exported to userspace I've used an #ifdef __mips__ for this. - I've introduced a flag __AUDIT_ARCH_ALT to indicate an alternative ABI. The name of the flag is intentionally very generic to make the name hopefully fit other architectures' eventual need as well. On MIPS it indicates the 3rd ABI known as N32. - To make matters worse, most MIPS processors can be configured to be big or little endian. Traditionally the the 64-bit little endian configuration is named mips64el, so I've changed references to MIPSEL64 in audit.h to MIPS64EL. - The code treats the little endian MIPS architecture as separate from big endian. Combined with the 3 ABIs that's 6 combinations. I tried to sort of follow the example set by ARM which explicitly lists the (rare) big endian architecture variant - but it doesn't seem to very useful so I wonder if this could be squashed to just the three ABIs without consideration of endianess? - Talking about flags; I've defined the the N32 architecture flags were defined #define AUDIT_ARCH_MIPS64_N32 (EM_MIPS|__AUDIT_ARCH_ALT) #define AUDIT_ARCH_MIPS64EL_N32 (EM_MIPS|__AUDIT_ARCH_ALT|__AUDIT_ARCH_LE N32 is a 32-bit ABI but one that only runs on 64-bit processors as it uses 64-bit registers for 64-bit integers. So I'm uncertain if the __AUDIT_ARCH_64BIT flags should be set or not. Thanks in advance, Signed-off-by: Ralf Baechle [Huacai: The original patch is from Ralf. I have maintained it for more than six years on Loongson platform, and it works perfectly. Most of the commit messages are written by Ralf.] Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1396433596-612624-2-git-send-email-manuel.lauss@gmail.com/ Fixes: 7a017721283d ("audit: Add CONFIG_HAVE_ARCH_AUDITSYSCALL") Fixes: f2d0801f00b7 ("MIPS: Add new AUDIT_ARCH token for the N32 ABI on MIPS64") Fixes: d1e63c947a6f ("MIPS: Factor o32 specific code into signal_o32.c") Link: https://lore.kernel.org/all/1584006083-28936-1-git-send-email-chenhc@lemote.com/ [Kexy: Resolved minor conflicts in arch/mips/Kconfig, arch/mips/include/asm/abi.h, arch/mips/include/uapi/asm/unistd.h, arch/mips/kernel/ptrace.c, arch/mips/kernel/signal.c, arch/mips/kernel/signal32.c, arch/mips/kernel/signal_n32.c, include/uapi/linux/audit.h, and init/Kconfig. In 7a017721283d ("audit: Add CONFIG_HAVE_ARCH_AUDITSYSCALL"), a new config option declaring audit syscall was added, so make MIPS in arch/mips/Kconfig to select it. It looks that CONFIG_AUDIT_ARCH is never used, removing the option. In f2d0801f00b7 ("MIPS: Add new AUDIT_ARCH token for the N32 ABI on MIPS64"), tokens for the N32 ABI on MIPS64 were added, so all related changes are not included or changed accordingly. In d1e63c947a6f ("MIPS: Factor o32 specific code into signal_o32.c"), struct mips_abi_32 was moved from arch/mips/kernel/signal32.c to arch/mips/kernel/signal_o32.c, so related changes are also moved. Incorporated Huacai's changes to arch/mips/kernel/audit-native.c, and kernel/auditsc.c.] Signed-off-by: Kexy Biscuit --- arch/mips/Kconfig | 9 +++ arch/mips/include/asm/abi.h | 1 + arch/mips/include/uapi/asm/unistd.h | 18 ++++-- arch/mips/kernel/Makefile | 4 ++ arch/mips/kernel/audit-n32.c | 58 +++++++++++++++++ arch/mips/kernel/audit-native.c | 97 +++++++++++++++++++++++++++++ arch/mips/kernel/audit-o32.c | 60 ++++++++++++++++++ arch/mips/kernel/ptrace.c | 7 +++ arch/mips/kernel/signal.c | 18 ++++++ arch/mips/kernel/signal_n32.c | 8 +++ arch/mips/kernel/signal_o32.c | 8 +++ include/uapi/linux/audit.h | 16 +++++ kernel/auditsc.c | 13 ++++ 13 files changed, 311 insertions(+), 6 deletions(-) create mode 100644 arch/mips/kernel/audit-n32.c create mode 100644 arch/mips/kernel/audit-native.c create mode 100644 arch/mips/kernel/audit-o32.c diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 91c3a502156b3..ce0df4e1d0814 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -102,6 +102,7 @@ config MIPS select TRACE_IRQFLAGS_SUPPORT select ARCH_HAS_ELFCORE_COMPAT select HAVE_ARCH_KCSAN if 64BIT + select HAVE_ARCH_AUDITSYSCALL config MIPS_FIXUP_BIGPHYS_ADDR bool @@ -1013,6 +1014,12 @@ config FW_ARC config ARCH_MAY_HAVE_PC_FDC bool +config AUDITSYSCALL_O32 + bool + +config AUDITSYSCALL_N32 + bool + config BOOT_RAW bool @@ -3087,6 +3094,7 @@ config MIPS32_O32 select ARCH_WANT_OLD_COMPAT_IPC select COMPAT select MIPS32_COMPAT + select AUDITSYSCALL_O32 if AUDITSYSCALL help Select this option if you want to run o32 binaries. These are pure 32-bit binaries as used by the 32-bit Linux/MIPS port. Most of @@ -3100,6 +3108,7 @@ config MIPS32_N32 select ARCH_WANT_COMPAT_IPC_PARSE_VERSION select COMPAT select MIPS32_COMPAT + select AUDITSYSCALL_N32 if AUDITSYSCALL help Select this option if you want to run n32 binaries. These are 64-bit binaries using 32-bit quantities for addressing and certain diff --git a/arch/mips/include/asm/abi.h b/arch/mips/include/asm/abi.h index dba7f4b6bebfa..6e717a4a13ceb 100644 --- a/arch/mips/include/asm/abi.h +++ b/arch/mips/include/asm/abi.h @@ -21,6 +21,7 @@ struct mips_abi { int (* const setup_rt_frame)(void *sig_return, struct ksignal *ksig, struct pt_regs *regs, sigset_t *set); const unsigned long restart; + const int audit_arch; unsigned off_sc_fpregs; unsigned off_sc_fpc_csr; diff --git a/arch/mips/include/uapi/asm/unistd.h b/arch/mips/include/uapi/asm/unistd.h index 4abe387549ad2..251d02e4111be 100644 --- a/arch/mips/include/uapi/asm/unistd.h +++ b/arch/mips/include/uapi/asm/unistd.h @@ -15,25 +15,31 @@ #include -#if _MIPS_SIM == _MIPS_SIM_ABI32 +#if (defined(__WANT_SYSCALL_NUMBERS) && \ + (__WANT_SYSCALL_NUMBERS == _MIPS_SIM_ABI32)) || \ + (!defined(__WANT_SYSCALL_NUMBERS) && _MIPS_SIM == _MIPS_SIM_ABI32) #define __NR_Linux 4000 #include -#endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ +#endif /* Want O32 || _MIPS_SIM == _MIPS_SIM_ABI32 */ -#if _MIPS_SIM == _MIPS_SIM_ABI64 +#if (defined(__WANT_SYSCALL_NUMBERS) && \ + (__WANT_SYSCALL_NUMBERS == _MIPS_SIM_ABI64)) || \ + (!defined(__WANT_SYSCALL_NUMBERS) && _MIPS_SIM == _MIPS_SIM_ABI64) #define __NR_Linux 5000 #include -#endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ +#endif /* Want N64 || _MIPS_SIM == _MIPS_SIM_ABI64 */ -#if _MIPS_SIM == _MIPS_SIM_NABI32 +#if (defined(__WANT_SYSCALL_NUMBERS) && \ + (__WANT_SYSCALL_NUMBERS == _MIPS_SIM_NABI32)) || \ + (!defined(__WANT_SYSCALL_NUMBERS) && _MIPS_SIM == _MIPS_SIM_NABI32) #define __NR_Linux 6000 #include -#endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ +#endif /* Want N32 || _MIPS_SIM == _MIPS_SIM_NABI32 */ #endif /* _UAPI_ASM_UNISTD_H */ diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile index 853a43ee4b446..e7c5ffd504d06 100644 --- a/arch/mips/kernel/Makefile +++ b/arch/mips/kernel/Makefile @@ -111,4 +111,8 @@ obj-$(CONFIG_MIPS_CPC) += mips-cpc.o obj-$(CONFIG_CPU_PM) += pm.o obj-$(CONFIG_MIPS_CPS_PM) += pm-cps.o +obj-$(CONFIG_AUDITSYSCALL_O32) += audit-o32.o +obj-$(CONFIG_AUDITSYSCALL_N32) += audit-n32.o +obj-$(CONFIG_AUDITSYSCALL) += audit-native.o + CPPFLAGS_vmlinux.lds := $(KBUILD_CFLAGS) diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c new file mode 100644 index 0000000000000..2248badc3acb6 --- /dev/null +++ b/arch/mips/kernel/audit-n32.c @@ -0,0 +1,58 @@ +#define __WANT_SYSCALL_NUMBERS _MIPS_SIM_NABI32 + +#include +#include +#include +#include + +static unsigned dir_class_n32[] = { +#include +~0U +}; + +static unsigned read_class_n32[] = { +#include +~0U +}; + +static unsigned write_class_n32[] = { +#include +~0U +}; + +static unsigned chattr_class_n32[] = { +#include +~0U +}; + +static unsigned signal_class_n32[] = { +#include +~0U +}; + +int audit_classify_syscall_n32(int abi, unsigned syscall) +{ + switch (syscall) { + case __NR_open: + return 2; + case __NR_openat: + return 3; + case __NR_execve: + return 5; + default: + return 0; + } +} + +static int __init audit_classes_n32_init(void) +{ + audit_register_class(AUDIT_CLASS_WRITE_N32, write_class_n32); + audit_register_class(AUDIT_CLASS_READ_N32, read_class_n32); + audit_register_class(AUDIT_CLASS_DIR_WRITE_N32, dir_class_n32); + audit_register_class(AUDIT_CLASS_CHATTR_N32, chattr_class_n32); + audit_register_class(AUDIT_CLASS_SIGNAL_N32, signal_class_n32); + + return 0; +} + +__initcall(audit_classes_n32_init); diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c new file mode 100644 index 0000000000000..09ae3dbcfecbc --- /dev/null +++ b/arch/mips/kernel/audit-native.c @@ -0,0 +1,97 @@ +#include +#include +#include +#include + +static unsigned dir_class[] = { +#include +~0U +}; + +static unsigned read_class[] = { +#include +~0U +}; + +static unsigned write_class[] = { +#include +~0U +}; + +static unsigned chattr_class[] = { +#include +~0U +}; + +static unsigned signal_class[] = { +#include +~0U +}; + + +/* + * Pretend to be a single architecture + */ +int audit_classify_arch(int arch) +{ + return 0; +} + +extern int audit_classify_syscall_o32(int abi, unsigned syscall); +extern int audit_classify_syscall_n32(int abi, unsigned syscall); + +int audit_classify_syscall(int abi, unsigned syscall) +{ + int res; + + switch (syscall) { + case __NR_open: + res = 2; + break; + + case __NR_openat: + res = 3; + break; + +#ifdef __NR_socketcall /* Only exists on O32 */ + case __NR_socketcall: + res = 4; + break; +#endif + case __NR_execve: + res = 5; + break; + default: +#ifdef CONFIG_AUDITSYSCALL_O32 + res = audit_classify_syscall_o32(abi, syscall); + if (res) + break; +#endif +#ifdef CONFIG_AUDITSYSCALL_N32 + res = audit_classify_syscall_n32(abi, syscall); + if (res) + break; +#endif + if (abi == AUDIT_ARCH_MIPS || abi == AUDIT_ARCH_MIPSEL) + res = 1; + else if (abi == AUDIT_ARCH_MIPS64 || abi == AUDIT_ARCH_MIPSEL64) + res = 0; + else if (abi == AUDIT_ARCH_MIPS64N32 || abi == AUDIT_ARCH_MIPSEL64N32) + res = 6; + } + + return res; +} + +static int __init audit_classes_init(void) +{ + audit_register_class(AUDIT_CLASS_WRITE, write_class); + audit_register_class(AUDIT_CLASS_READ, read_class); + audit_register_class(AUDIT_CLASS_DIR_WRITE, dir_class); + audit_register_class(AUDIT_CLASS_CHATTR, chattr_class); + audit_register_class(AUDIT_CLASS_SIGNAL, signal_class); + + return 0; +} + +__initcall(audit_classes_init); diff --git a/arch/mips/kernel/audit-o32.c b/arch/mips/kernel/audit-o32.c new file mode 100644 index 0000000000000..e8b9b50f7d267 --- /dev/null +++ b/arch/mips/kernel/audit-o32.c @@ -0,0 +1,60 @@ +#define __WANT_SYSCALL_NUMBERS _MIPS_SIM_ABI32 + +#include +#include +#include +#include + +static unsigned dir_class_o32[] = { +#include +~0U +}; + +static unsigned read_class_o32[] = { +#include +~0U +}; + +static unsigned write_class_o32[] = { +#include +~0U +}; + +static unsigned chattr_class_o32[] = { +#include +~0U +}; + +static unsigned signal_class_o32[] = { +#include +~0U +}; + +int audit_classify_syscall_o32(int abi, unsigned syscall) +{ + switch (syscall) { + case __NR_open: + return 2; + case __NR_openat: + return 3; + case __NR_socketcall: + return 4; + case __NR_execve: + return 5; + default: + return 0; + } +} + +static int __init audit_classes_o32_init(void) +{ + audit_register_class(AUDIT_CLASS_WRITE_32, write_class_o32); + audit_register_class(AUDIT_CLASS_READ_32, read_class_o32); + audit_register_class(AUDIT_CLASS_DIR_WRITE_32, dir_class_o32); + audit_register_class(AUDIT_CLASS_CHATTR_32, chattr_class_o32); + audit_register_class(AUDIT_CLASS_SIGNAL_32, signal_class_o32); + + return 0; +} + +__initcall(audit_classes_o32_init); diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 61503a36067e9..69169aa6bf26e 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -49,6 +50,12 @@ #define CREATE_TRACE_POINTS #include +/* + * The standard AUDITSC_RESULT() assumes errno values < 0 indicate error + * but MIPS passes an error flag instead. + */ +#define MIPS_AUDITSC_RESULT(x) ((x) ? AUDITSC_FAILURE : AUDITSC_SUCCESS) + unsigned long exception_ip(struct pt_regs *regs) { return exception_epc(regs); diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 479999b7f2de7..e3c3907105a13 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -8,6 +8,7 @@ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. * Copyright (C) 2014, Imagination Technologies Ltd. */ +#include #include #include #include @@ -810,6 +811,23 @@ struct mips_abi mips_abi = { .off_sc_used_math = offsetof(struct sigcontext, sc_used_math), .vdso = &vdso_image, +#ifdef CONFIG_64BIT +# ifdef __BIG_ENDIAN + .audit_arch = AUDIT_ARCH_MIPS64, +# elif defined(__LITTLE_ENDIAN) + .audit_arch = AUDIT_ARCH_MIPSEL64, +# else +# error "Neither big nor little endian ???" +# endif +#else +# ifdef __BIG_ENDIAN + .audit_arch = AUDIT_ARCH_MIPS, +# elif defined(__LITTLE_ENDIAN) + .audit_arch = AUDIT_ARCH_MIPSEL, +# else +# error "Neither big nor little endian ???" +# endif +#endif }; static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) diff --git a/arch/mips/kernel/signal_n32.c b/arch/mips/kernel/signal_n32.c index cfc77b69420a1..5bc4918d6fab2 100644 --- a/arch/mips/kernel/signal_n32.c +++ b/arch/mips/kernel/signal_n32.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2003 Broadcom Corporation */ +#include #include #include #include @@ -145,4 +146,11 @@ struct mips_abi mips_abi_n32 = { .off_sc_used_math = offsetof(struct sigcontext, sc_used_math), .vdso = &vdso_image_n32, +#ifdef __BIG_ENDIAN + .audit_arch = AUDIT_ARCH_MIPS64N32, +#elif defined(__LITTLE_ENDIAN) + .audit_arch = AUDIT_ARCH_MIPSEL64N32, +#else +# error "Neither big nor little endian ???" +#endif }; diff --git a/arch/mips/kernel/signal_o32.c b/arch/mips/kernel/signal_o32.c index 299a7a28ca33f..8e86ff609f529 100644 --- a/arch/mips/kernel/signal_o32.c +++ b/arch/mips/kernel/signal_o32.c @@ -8,6 +8,7 @@ * Copyright (C) 1999, 2000 Silicon Graphics, Inc. * Copyright (C) 2016, Imagination Technologies Ltd. */ +#include #include #include #include @@ -250,6 +251,13 @@ struct mips_abi mips_abi_32 = { .off_sc_used_math = offsetof(struct sigcontext32, sc_used_math), .vdso = &vdso_image_o32, +#ifdef __BIG_ENDIAN + .audit_arch = AUDIT_ARCH_MIPS, +#elif defined(__LITTLE_ENDIAN) + .audit_arch = AUDIT_ARCH_MIPSEL, +#else +# error "Neither big nor little endian ???" +#endif }; diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index d676ed2b246ec..747fd2789f964 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -185,7 +185,11 @@ * AUDIT_LIST commands must be implemented. */ #define AUDIT_MAX_FIELDS 64 #define AUDIT_MAX_KEY_LEN 256 +#ifdef __mips__ +#define AUDIT_BITMASK_SIZE 256 +#else #define AUDIT_BITMASK_SIZE 64 +#endif #define AUDIT_WORD(nr) ((__u32)((nr)/32)) #define AUDIT_BIT(nr) (1U << ((nr) - AUDIT_WORD(nr)*32)) @@ -201,6 +205,18 @@ #define AUDIT_CLASS_SIGNAL 8 #define AUDIT_CLASS_SIGNAL_32 9 +/* + * WARNING: Not officially assigned by upstream yet; the names of these + * constants might change breaking source compatibility. The values might + * change breaking binary compatibility. With the audit package being the + * only known user at this time the potencial problem is small + */ +#define AUDIT_CLASS_DIR_WRITE_N32 10 +#define AUDIT_CLASS_CHATTR_N32 11 +#define AUDIT_CLASS_READ_N32 12 +#define AUDIT_CLASS_WRITE_N32 13 +#define AUDIT_CLASS_SIGNAL_N32 14 + /* This bitmask is used to validate user input. It represents all bits that * are currently used in an audit field constant understood by the kernel. * If you are adding a new #define AUDIT_, please ensure that diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 6f0d6fb6523fa..9fa3a07f499a2 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -189,6 +189,19 @@ static int audit_match_perm(struct audit_context *ctx, int mask) return mask & AUDIT_PERM_EXEC; case AUDITSC_OPENAT2: return mask & ACC_MODE((u32)ctx->openat2.flags); +#ifdef CONFIG_MIPS + case 6: /* for N32 */ + if ((mask & AUDIT_PERM_WRITE) && + audit_match_class(AUDIT_CLASS_WRITE_N32, n)) + return 1; + if ((mask & AUDIT_PERM_READ) && + audit_match_class(AUDIT_CLASS_READ_N32, n)) + return 1; + if ((mask & AUDIT_PERM_ATTR) && + audit_match_class(AUDIT_CLASS_CHATTR_N32, n)) + return 1; + return 0; +#endif default: return 0; } From 94d6b7d34c3f006c062dde56572dfccbcaf4d919 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 6 Feb 2019 20:03:14 +0800 Subject: [PATCH 008/130] FROMLIST: MIPS: math-emu: Add madd/msub/nmadd/nmsub emulation for Loongson-3 Add madd.s/madd.d/msub.s/msub.d/nmadd.s/nmadd.d/nmsub.s/nmsub.d emulation for Loongson-3. MIPS R2 suggest these instructions be unfused, but Loongson-3 suggest these instructions be fused, which is similar to maddf/msubf in MIPS R6. Signed-off-by: Huacai Chen Signed-off-by: Pei Huang Link: https://lore.kernel.org/all/1549454594-9056-1-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit --- arch/mips/math-emu/cp1emu.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index 265bc57819dfb..a2cba79eba7a5 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -1451,6 +1451,7 @@ static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); } +#ifndef CONFIG_CPU_LOONGSON3 DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); @@ -1459,6 +1460,7 @@ DEF3OP(madd, dp, ieee754dp_mul, ieee754dp_add, ); DEF3OP(msub, dp, ieee754dp_mul, ieee754dp_sub, ); DEF3OP(nmadd, dp, ieee754dp_mul, ieee754dp_add, ieee754dp_neg); DEF3OP(nmsub, dp, ieee754dp_mul, ieee754dp_sub, ieee754dp_neg); +#endif static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, mips_instruction ir, void __user **fault_addr) @@ -1513,6 +1515,20 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; +#ifdef CONFIG_CPU_LOONGSON3 + case madd_s_op: + handler = ieee754sp_madd; + goto scoptop; + case msub_s_op: + handler = ieee754sp_msub; + goto scoptop; + case nmadd_s_op: + handler = ieee754sp_nmadd; + goto scoptop; + case nmsub_s_op: + handler = ieee754sp_nmsub; + goto scoptop; +#else case madd_s_op: if (cpu_has_mac2008_only) handler = ieee754sp_madd; @@ -1537,6 +1553,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, else handler = fpemu_sp_nmsub; goto scoptop; +#endif scoptop: SPFROMREG(fr, MIPSInst_FR(ir)); @@ -1621,6 +1638,20 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; +#ifdef CONFIG_CPU_LOONGSON3 + case madd_d_op: + handler = ieee754dp_madd; + goto dcoptop; + case msub_d_op: + handler = ieee754dp_msub; + goto dcoptop; + case nmadd_d_op: + handler = ieee754dp_nmadd; + goto dcoptop; + case nmsub_d_op: + handler = ieee754dp_nmsub; + goto dcoptop; +#else case madd_d_op: if (cpu_has_mac2008_only) handler = ieee754dp_madd; @@ -1645,6 +1676,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, else handler = fpemu_dp_nmsub; goto dcoptop; +#endif dcoptop: DPFROMREG(fr, MIPSInst_FR(ir)); From 16a01e9b438e73effd3d7f085375067af0b2be87 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 1 Nov 2020 11:33:56 +0800 Subject: [PATCH 009/130] BACKPORT: FROMLIST: mips/mm: Add NUMA balancing support NUMA balancing is available on nearly all architectures, but MIPS lacks it for a long time. In theory, the current NUMA balancing framework only need a "PROTNONE" page table bit and some APIs to manipulate it. So, it is time for us to add MIPS's NUMA balancing support (Only for 64bit now because NUMA balancing depends on huge page implicitly). Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1604201638-4001-1-git-send-email-chenhc@lemote.com/ [Kexy: Resolved minor conflicts in arch/mips/Kconfig, and arch/mips/include/asm/pgtable.h.] Signed-off-by: Kexy Biscuit --- arch/mips/Kconfig | 1 + arch/mips/include/asm/pgtable-64.h | 2 +- arch/mips/include/asm/pgtable-bits.h | 17 +++++++++++++++++ arch/mips/include/asm/pgtable.h | 18 ++++++++++++++++-- 4 files changed, 35 insertions(+), 3 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index ce0df4e1d0814..d675edd5d10be 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -16,6 +16,7 @@ config MIPS select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAS_UBSAN_SANITIZE_ALL select ARCH_HAS_GCOV_PROFILE_ALL + select ARCH_SUPPORTS_NUMA_BALANCING if 64BIT select ARCH_KEEP_MEMBLOCK select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_CMPXCHG_LOCKREF if 64BIT diff --git a/arch/mips/include/asm/pgtable-64.h b/arch/mips/include/asm/pgtable-64.h index 20ca48c1b6063..91ba82e605ecc 100644 --- a/arch/mips/include/asm/pgtable-64.h +++ b/arch/mips/include/asm/pgtable-64.h @@ -260,7 +260,7 @@ static inline int pmd_present(pmd_t pmd) { #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT if (unlikely(pmd_val(pmd) & _PAGE_HUGE)) - return pmd_val(pmd) & _PAGE_PRESENT; + return pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROTNONE); #endif return pmd_val(pmd) != (unsigned long) invalid_pte_table; diff --git a/arch/mips/include/asm/pgtable-bits.h b/arch/mips/include/asm/pgtable-bits.h index 421e78c30253c..a5302fdd39267 100644 --- a/arch/mips/include/asm/pgtable-bits.h +++ b/arch/mips/include/asm/pgtable-bits.h @@ -52,6 +52,9 @@ enum pgtable_bits { _PAGE_WRITE_SHIFT, _PAGE_ACCESSED_SHIFT, _PAGE_MODIFIED_SHIFT, +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) + _PAGE_PROTNONE_SHIFT, +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) _PAGE_SPECIAL_SHIFT, #endif @@ -84,6 +87,9 @@ enum pgtable_bits { _PAGE_WRITE_SHIFT, _PAGE_ACCESSED_SHIFT, _PAGE_MODIFIED_SHIFT, +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) + _PAGE_PROTNONE_SHIFT, +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) _PAGE_SPECIAL_SHIFT, #endif @@ -102,6 +108,9 @@ enum pgtable_bits { _PAGE_WRITE_SHIFT, _PAGE_ACCESSED_SHIFT, _PAGE_MODIFIED_SHIFT, +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) + _PAGE_PROTNONE_SHIFT, +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) _PAGE_SPECIAL_SHIFT, #endif @@ -131,6 +140,9 @@ enum pgtable_bits { #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) _PAGE_HUGE_SHIFT, #endif +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) + _PAGE_PROTNONE_SHIFT, +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) _PAGE_SPECIAL_SHIFT, #endif @@ -158,6 +170,11 @@ enum pgtable_bits { #if defined(CONFIG_MIPS_HUGE_TLB_SUPPORT) # define _PAGE_HUGE (1 << _PAGE_HUGE_SHIFT) #endif +#if defined(CONFIG_ARCH_SUPPORTS_NUMA_BALANCING) +# define _PAGE_PROTNONE (1 <<_PAGE_PROTNONE_SHIFT) +#else +# define _PAGE_PROTNONE 0 +#endif #if defined(CONFIG_ARCH_HAS_PTE_SPECIAL) # define _PAGE_SPECIAL (1 << _PAGE_SPECIAL_SHIFT) #else diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h index 430b208c01307..d44e51d5d9e9c 100644 --- a/arch/mips/include/asm/pgtable.h +++ b/arch/mips/include/asm/pgtable.h @@ -27,6 +27,8 @@ struct vm_area_struct; #define PAGE_SHARED vm_get_page_prot(VM_READ|VM_WRITE|VM_SHARED) +#define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_NO_READ | \ + _page_cachable_default) #define PAGE_KERNEL __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \ _PAGE_GLOBAL | _page_cachable_default) #define PAGE_KERNEL_NC __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \ @@ -160,7 +162,7 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt #else #define pte_none(pte) (!(pte_val(pte) & ~_PAGE_GLOBAL)) -#define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT) +#define pte_present(pte) (pte_val(pte) & (_PAGE_PRESENT | _PAGE_PROTNONE)) #define pte_no_exec(pte) (pte_val(pte) & _PAGE_NO_EXEC) /* @@ -730,7 +732,7 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) static inline pmd_t pmd_mkinvalid(pmd_t pmd) { - pmd_val(pmd) &= ~(_PAGE_PRESENT | _PAGE_VALID | _PAGE_DIRTY); + pmd_val(pmd) &= ~(_PAGE_PRESENT | _PAGE_VALID | _PAGE_PROTNONE | _PAGE_DIRTY); return pmd; } @@ -752,6 +754,18 @@ static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm, #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ +#ifdef CONFIG_NUMA_BALANCING +static inline long pte_protnone(pte_t pte) +{ + return (pte_val(pte) & _PAGE_PROTNONE); +} + +static inline long pmd_protnone(pmd_t pmd) +{ + return (pmd_val(pmd) & _PAGE_PROTNONE); +} +#endif /* CONFIG_NUMA_BALANCING */ + #ifdef _PAGE_HUGE #define pmd_leaf(pmd) ((pmd_val(pmd) & _PAGE_HUGE) != 0) #define pud_leaf(pud) ((pud_val(pud) & _PAGE_HUGE) != 0) From 7edc5e96ea9e855a51336ae0f5045cb94e2ce036 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 1 Nov 2020 11:33:58 +0800 Subject: [PATCH 010/130] FROMLIST: MIPS: Loongson64: Enlarge cross-package node distance NUMA node distances affect the NUMA balancing behaviors. The cost of cross-package memory access is very high, and our benchmarks show that 200 is a more appropriate value than 100 (for cross-package numa node distance) on Loongson64 platforms, so enlarge it. Signed-off-by: Huacai Chen Link: https://lore.kernel.org/all/1604201638-4001-3-git-send-email-chenhc@lemote.com/ Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/numa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/numa.c b/arch/mips/loongson64/numa.c index 8f61e93c0c5bc..d2c264317fe4d 100644 --- a/arch/mips/loongson64/numa.c +++ b/arch/mips/loongson64/numa.c @@ -62,7 +62,7 @@ static int __init compute_node_distance(int row, int col) else if (package_row == package_col) return 40; else - return 100; + return 200; } static void __init init_topology_matrix(void) From 22a31496d7acd77f1234512c4090444c95fc8495 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Thu, 19 Jan 2023 20:42:56 +0000 Subject: [PATCH 011/130] AOSCOS: wifi: rt2x00: Condition interface type getters with config options When compiling this driver for platforms with partial clk support (i.e. MIPS/Loongson64), it will report missing symbol for clk_get_sys. Conditioning interface type getters will help compiler to eliminate unsed code and thus exclude calls to clk symbols. Also hopefully reduce code size on other platforms. Signed-off-by: Jiaxun Yang Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/net/wireless/ralink/rt2x00/rt2x00.h | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00.h b/drivers/net/wireless/ralink/rt2x00/rt2x00.h index 07a6a5a9ce13a..5ccf6ddf283f9 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00.h +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00.h @@ -1174,23 +1174,27 @@ static inline bool rt2x00_intf(struct rt2x00_dev *rt2x00dev, static inline bool rt2x00_is_pci(struct rt2x00_dev *rt2x00dev) { - return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI) || - rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); + return IS_ENABLED(CONFIG_RT2X00_LIB_PCI) && + (rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCI) || + rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE)); } static inline bool rt2x00_is_pcie(struct rt2x00_dev *rt2x00dev) { - return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); + return IS_ENABLED(CONFIG_RT2X00_LIB_PCI) && + rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); } static inline bool rt2x00_is_usb(struct rt2x00_dev *rt2x00dev) { - return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); + return IS_ENABLED(CONFIG_RT2X00_LIB_USB) && + rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_USB); } static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev) { - return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); + return IS_ENABLED(CONFIG_RT2X00_LIB_SOC) && + rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); } /* Helpers for capability flags */ From d37c5bd3bb994fe5b884035b4b3eb075eecf988b Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 012/130] AOSCOS: tty: serial_core: Clear TTY_IO_ERROR if tty_port_open() return 0 After commit b3b57646186400d4f ("tty: serial_core: convert uart_open to use tty_port_open") and 761ed4a94582ab291a ("tty: serial_core: convert uart_close to use tty_port_close"), TTY_IO_ERROR is set by tty_port_close() unconditionally, but cleared by tty_port_open() only when tty_port_initialized() return 0. This is a bug: If we use "console=ttyS*" in kernel parameters, the corresponding serial port's port->console will be true. When somebody close the serial port, tty_port_close() will return without calling tty_port_set_initialized(port, 0). Then, when it is opened again, tty_port_open() will not clear TTY_IO_ERROR, and serial port becomes unavailable at last. So, this patch clear TTY_IO_ERROR in uart_open() if tty_port_open() return 0. Signed-off-by: Ce Sun Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/tty/serial/serial_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 8ff0efac6aa0d..ef0a27870e548 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -1999,6 +1999,8 @@ static int uart_open(struct tty_struct *tty, struct file *filp) int retval; retval = tty_port_open(&state->port, tty, filp); + if (!retval) + clear_bit(TTY_IO_ERROR, &tty->flags); if (retval > 0) retval = 0; From aa7603f9763150f5a499c5fefc9dda6ca60e88c8 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Fri, 29 Nov 2019 09:11:38 +0800 Subject: [PATCH 013/130] AOSCOS: MIPS: Loongson: Add constant timer support Partially implemented by commit 8267e78f020a ("MIPS: Tidy up CP0.Config6 bits definition"). [Mingcong Bai: Resolved merge conflicts in the following... arch/mips/include/asm/clocksource.h arch/mips/include/asm/cpu-features.h arch/mips/include/asm/cpu.h arch/mips/include/asm/mach-loongson64/loongson_regs.h arch/mips/include/asm/mipsregs.h arch/mips/include/asm/vdso/gettimeofday.h arch/mips/kernel/cpu-probe.c arch/mips/loongson64/constant_timer.c arch/mips/loongson64/loongson-3/Makefile => arch/mips/loongson64/Makefile arch/mips/loongson64/smp.c] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/include/asm/cpu-features.h | 4 + arch/mips/include/asm/cpu.h | 1 + .../asm/mach-loongson64/loongson_regs.h | 30 ++ arch/mips/include/asm/mipsregs.h | 1 + arch/mips/include/asm/time.h | 20 +- arch/mips/include/asm/vdso/clocksource.h | 3 +- arch/mips/include/asm/vdso/gettimeofday.h | 20 ++ arch/mips/kernel/cpu-probe.c | 16 ++ arch/mips/loongson64/Makefile | 1 + arch/mips/loongson64/constant_timer.c | 256 ++++++++++++++++++ arch/mips/loongson64/smp.c | 4 +- 11 files changed, 352 insertions(+), 4 deletions(-) create mode 100644 arch/mips/loongson64/constant_timer.c diff --git a/arch/mips/include/asm/cpu-features.h b/arch/mips/include/asm/cpu-features.h index 404390bb87eaf..e522efa1b53d0 100644 --- a/arch/mips/include/asm/cpu-features.h +++ b/arch/mips/include/asm/cpu-features.h @@ -659,6 +659,10 @@ # define cpu_has_mm_full __opt(MIPS_CPU_MM_FULL) #endif +#ifndef cpu_has_constant_timer +# define cpu_has_constant_timer __opt(MIPS_CPU_CONST_TIMER) +#endif + /* * Guest capabilities */ diff --git a/arch/mips/include/asm/cpu.h b/arch/mips/include/asm/cpu.h index ecb9854cb4324..3c1299b718e10 100644 --- a/arch/mips/include/asm/cpu.h +++ b/arch/mips/include/asm/cpu.h @@ -421,6 +421,7 @@ enum cpu_type_enum { #define MIPS_CPU_MAC_2008_ONLY BIT_ULL(60) /* CPU Only support MAC2008 Fused multiply-add instruction */ #define MIPS_CPU_FTLBPAREX BIT_ULL(61) /* CPU has FTLB parity exception */ #define MIPS_CPU_GSEXCEX BIT_ULL(62) /* CPU has GSExc exception */ +#define MIPS_CPU_CONST_TIMER BIT_ULL(63) /* CPU has constant timer */ /* * CPU ASE encodings diff --git a/arch/mips/include/asm/mach-loongson64/loongson_regs.h b/arch/mips/include/asm/mach-loongson64/loongson_regs.h index b5be7511f6cde..e557fe12da93b 100644 --- a/arch/mips/include/asm/mach-loongson64/loongson_regs.h +++ b/arch/mips/include/asm/mach-loongson64/loongson_regs.h @@ -131,6 +131,9 @@ static inline u32 read_cpucfg(u32 reg) #define LOONGSON_CFG7_GCCAEQRP BIT(0) #define LOONGSON_CFG7_UCAWINP BIT(1) +#define CSR_TO_RAW_ADDR(cpu, csr) ((0x900000003ff00000 | csr | \ + (((u64)cpu & 0xc) << 42)) | (((u64)cpu & 0x3) << 8)) + static inline bool cpu_has_csr(void) { if (cpu_has_cfg()) @@ -247,6 +250,12 @@ static inline void csr_writeq(u64 val, u32 reg) #define CSR_MAIL_SEND_BUF_SHIFT 32 #define CSR_MAIL_SEND_H32_MASK 0xFFFFFFFF00000000ULL +#define LOONGSON_CSR_TIMER_CFG 0x1060 +#define LOONGSON_CSR_TIMER_TICK 0x1070 +#define CONSTANT_TIMER_CFG_PERIODIC (_ULCAST_(1) << 62) +#define CONSTANT_TIMER_CFG_EN (_ULCAST_(1) << 61) +#define CONSTANT_TIMER_INITVAL_RESET (_ULCAST_(0xffff) << 48) + static inline u64 drdtime(void) { int rID = 0; @@ -265,4 +274,25 @@ static inline u64 drdtime(void) return val; } +static inline unsigned int calc_const_freq(void) +{ + unsigned int res; + unsigned int base_freq; + unsigned int cfm, cfd; + + res = read_cpucfg(LOONGSON_CFG2); + if (!(res & LOONGSON_CFG2_LLFTP)) + return 0; + + res = read_cpucfg(LOONGSON_CFG5); + cfm = res & 0xffff; + cfd = (res >> 16) & 0xffff; + base_freq = read_cpucfg(LOONGSON_CFG4); + + if (!base_freq || !cfm || !cfd) + return 0; + else + return (base_freq * cfm / cfd); +} + #endif diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 2d53704d9f246..78d9eb7ec4945 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -693,6 +693,7 @@ #define MTI_CONF6_SYND (_ULCAST_(1) << 13) /* Sleep state performance counter disable */ #define MTI_CONF6_SPCD (_ULCAST_(1) << 14) + /* proAptiv FTLB on/off bit */ #define MTI_CONF6_FTLBEN (_ULCAST_(1) << 15) /* Disable load/store bonding */ diff --git a/arch/mips/include/asm/time.h b/arch/mips/include/asm/time.h index 3ba2bc06ae778..8a91ad684fc87 100644 --- a/arch/mips/include/asm/time.h +++ b/arch/mips/include/asm/time.h @@ -42,11 +42,19 @@ extern int __weak get_c0_perfcount_int(void); */ extern unsigned int get_c0_compare_int(void); extern int r4k_clockevent_init(void); +#ifdef CONFIG_CPU_LOONGSON64 +extern int constant_clockevent_init(void); +#else +static inline int constant_clockevent_init(void) { return 0; } +#endif static inline int mips_clockevent_init(void) { #ifdef CONFIG_CEVT_R4K - return r4k_clockevent_init(); + if (!cpu_has_constant_timer) + return r4k_clockevent_init(); + else + return constant_clockevent_init(); #else return -ENXIO; #endif @@ -56,11 +64,19 @@ static inline int mips_clockevent_init(void) * Initialize the count register as a clocksource */ extern int init_r4k_clocksource(void); +#ifdef CONFIG_CPU_LOONGSON64 +extern int init_constant_clocksource(void); +#else +static inline int init_constant_clocksource(void) { return 0; } +#endif static inline int init_mips_clocksource(void) { #ifdef CONFIG_CSRC_R4K - return init_r4k_clocksource(); + if (!cpu_has_constant_timer) + return init_r4k_clocksource(); + else + return init_constant_clocksource(); #else return 0; #endif diff --git a/arch/mips/include/asm/vdso/clocksource.h b/arch/mips/include/asm/vdso/clocksource.h index 510e1671d8985..7fd43ca06eb11 100644 --- a/arch/mips/include/asm/vdso/clocksource.h +++ b/arch/mips/include/asm/vdso/clocksource.h @@ -4,6 +4,7 @@ #define VDSO_ARCH_CLOCKMODES \ VDSO_CLOCKMODE_R4K, \ - VDSO_CLOCKMODE_GIC + VDSO_CLOCKMODE_GIC, \ + VDSO_CLOCKMODE_CONST #endif /* __ASM_VDSOCLOCKSOURCE_H */ diff --git a/arch/mips/include/asm/vdso/gettimeofday.h b/arch/mips/include/asm/vdso/gettimeofday.h index 44a45f3fa4b01..c1b15b0f07689 100644 --- a/arch/mips/include/asm/vdso/gettimeofday.h +++ b/arch/mips/include/asm/vdso/gettimeofday.h @@ -183,6 +183,22 @@ static __always_inline u64 read_gic_count(const struct vdso_data *data) #endif +#ifdef CONFIG_CPU_LOONGSON64 +static __always_inline u64 read_const_count(void) +{ + unsigned long count; + + __asm__ __volatile__( + " .set push\n" + " .set mips32r2\n" + " rdhwr %0, $30\n" + " .set pop\n" + : "=r" (count)); + + return count; +} +#endif + static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, const struct vdso_data *vd) { @@ -193,6 +209,10 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, #ifdef CONFIG_CLKSRC_MIPS_GIC if (clock_mode == VDSO_CLOCKMODE_GIC) return read_gic_count(vd); +#endif +#ifdef CONFIG_CPU_LOONGSON64 + if (clock_mode == VDSO_CLOCKMODE_CONST) + return read_const_count(); #endif /* * Core checks mode already. So this raced against a concurrent diff --git a/arch/mips/kernel/cpu-probe.c b/arch/mips/kernel/cpu-probe.c index 763095c43263a..7f748be9f35de 100644 --- a/arch/mips/kernel/cpu-probe.c +++ b/arch/mips/kernel/cpu-probe.c @@ -33,6 +33,10 @@ #include +#ifdef CONFIG_CPU_LOONGSON64 +#include +#endif + /* Hardware capabilities */ unsigned int elf_hwcap __read_mostly; EXPORT_SYMBOL_GPL(elf_hwcap); @@ -1687,6 +1691,17 @@ static inline void decode_cpucfg(struct cpuinfo_mips *c) c->ases |= MIPS_ASE_LOONGSON_CAM; } +static void decode_loongson_cpucfg(struct cpuinfo_mips *c) +{ +#ifdef CONFIG_CPU_LOONGSON64 + unsigned int cpucfg; + + cpucfg = read_cpucfg(LOONGSON_CFG2); + if (cpucfg & LOONGSON_CFG2_LLFTP) + c->options |= MIPS_CPU_CONST_TIMER; +#endif +} + static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) { c->cputype = CPU_LOONGSON64; @@ -1748,6 +1763,7 @@ static inline void cpu_probe_loongson(struct cpuinfo_mips *c, unsigned int cpu) set_isa(c, MIPS_CPU_ISA_M64R2); __cpu_full_name[cpu] = "Loongson-3A R4 (Loongson-3A4000)"; decode_cpucfg(c); + decode_loongson_cpucfg(c); change_c0_config6(LOONGSON_CONF6_EXTIMER | LOONGSON_CONF6_INTIMER, LOONGSON_CONF6_INTIMER); break; diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index e806280bbb850..76d6251fb7472 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ + constant_timer.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/constant_timer.c b/arch/mips/loongson64/constant_timer.c new file mode 100644 index 0000000000000..b8bdbb2c47b51 --- /dev/null +++ b/arch/mips/loongson64/constant_timer.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int constant_timer_irq_installed; + +static DEFINE_SPINLOCK(state_lock); +DEFINE_PER_CPU(struct clock_event_device, constant_clockevent_device); + +static void constant_event_handler(struct clock_event_device *dev) +{ +} + +static irqreturn_t constant_timer_interrupt(int irq, void *data) +{ + int cpu = smp_processor_id(); + const int r2 = cpu_has_mips_r2_r6; + struct clock_event_device *cd; + + if ((cp0_perfcount_irq < 0) && perf_irq() == IRQ_HANDLED && !r2) + return IRQ_HANDLED; + + /* + * The same applies to performance counter interrupts. But with the + * above we now know that the reason we got here must be a timer + * interrupt. Being the paranoiacs we are we check anyway. + */ + if (!r2 || (read_c0_cause() & CAUSEF_TI)) { + /* Clear Count/Compare Interrupt */ + write_c0_compare(read_c0_compare()); + cd = &per_cpu(constant_clockevent_device, cpu); + cd->event_handler(cd); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static struct irqaction constant_timer_irqaction = { + .handler = constant_timer_interrupt, + .flags = IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED, + .name = "timer", +}; + +static int constant_set_state_oneshot(struct clock_event_device *evt) +{ + unsigned int raw_cpuid; + unsigned long cfg, addr; + + spin_lock(&state_lock); + + raw_cpuid = cpu_logical_map(smp_processor_id()); + addr = CSR_TO_RAW_ADDR(raw_cpuid, LOONGSON_CSR_TIMER_CFG); + + if (!cpu_has_csr()) + cfg = readq((void *)addr); + else + cfg = csr_readq(LOONGSON_CSR_TIMER_CFG); + + /* set timer type + * 1 : periodic interrupt + * 0 : non-periodic(oneshot) interrupt + */ + cfg |= CONSTANT_TIMER_CFG_EN; + cfg &= ~CONSTANT_TIMER_CFG_PERIODIC; + + if (!cpu_has_csr()) + writeq(cfg, (void *)addr); + else + csr_writeq(cfg, LOONGSON_CSR_TIMER_CFG); + + spin_unlock(&state_lock); + + return 0; +} + +static int constant_set_state_oneshot_stopped(struct clock_event_device *evt) +{ + return 0; +} + +static int constant_set_state_periodic(struct clock_event_device *evt) +{ + unsigned int period; + unsigned int raw_cpuid; + unsigned long cfg, addr; + + spin_lock(&state_lock); + + raw_cpuid = cpu_logical_map(smp_processor_id()); + addr = CSR_TO_RAW_ADDR(raw_cpuid, LOONGSON_CSR_TIMER_CFG); + + if (!cpu_has_csr()) + cfg = readq((void *)addr); + else + cfg = csr_readq(LOONGSON_CSR_TIMER_CFG); + + cfg &= CONSTANT_TIMER_INITVAL_RESET; + cfg |= (CONSTANT_TIMER_CFG_PERIODIC | CONSTANT_TIMER_CFG_EN); + + period = calc_const_freq(); + if (!period) + period = cpu_clock_freq; + + period = period / HZ; + + if (!cpu_has_csr()) + writeq(cfg | period, (void *)addr); + else + csr_writeq(cfg | period, LOONGSON_CSR_TIMER_CFG); + + spin_unlock(&state_lock); + + return 0; +} + +static int constant_set_state_shutdown(struct clock_event_device *evt) +{ + return 0; +} + +static int constant_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + unsigned long addr; + unsigned int raw_cpuid; + + raw_cpuid = cpu_logical_map(smp_processor_id()); + addr = CSR_TO_RAW_ADDR(raw_cpuid, LOONGSON_CSR_TIMER_CFG); + + writeq(delta | CONSTANT_TIMER_CFG_EN, (void *)addr); + + return 0; +} + +static int csr_constant_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + csr_writeq(delta | CONSTANT_TIMER_CFG_EN, LOONGSON_CSR_TIMER_CFG); + return 0; +} + +int constant_clockevent_init(void) +{ + unsigned int irq; + unsigned int config; + unsigned int const_freq; + unsigned long min_delta = 0x600; + unsigned long max_delta = (1UL << 48) - 1; + unsigned int cpu = smp_processor_id(); + struct clock_event_device *cd; + + config = read_c0_config6(); + config |= LOONGSON_CONF6_EXTIMER; + write_c0_config6(config); + + const_freq = calc_const_freq(); + if (!const_freq) + const_freq = cpu_clock_freq; + + irq = get_c0_compare_int(); + + cd = &per_cpu(constant_clockevent_device, cpu); + + cd->name = "Constant"; + cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_PERCPU; + + cd->rating = 320; + cd->irq = irq; + cd->cpumask = cpumask_of(cpu); + cd->set_state_oneshot = constant_set_state_oneshot; + cd->set_state_oneshot_stopped = constant_set_state_oneshot_stopped; + cd->set_state_periodic = constant_set_state_periodic; + cd->set_state_shutdown = constant_set_state_shutdown; + if (!cpu_has_csr()) + cd->set_next_event = constant_next_event; + else + cd->set_next_event = csr_constant_next_event; + + cd->event_handler = constant_event_handler; + + clockevents_config_and_register(cd, const_freq, min_delta, max_delta); + + if (constant_timer_irq_installed) + return 0; + + constant_timer_irq_installed = 1; + setup_irq(irq, &constant_timer_irqaction); + + return 0; +} + +static u64 read_const_counter(struct clocksource *clk) +{ + u64 count; + + __asm__ __volatile__( + " .set push\n" + " .set mips32r2\n" + " rdhwr %0, $30\n" + " .set pop\n" + : "=r" (count)); + + return count; +} + +static struct clocksource clocksource_const = { + .name = "Constant", + .rating = 400, + .read = read_const_counter, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, + .mult = 0, + .shift = 10, +}; + +static u64 read_sched_clock(void) +{ + u64 count; + + __asm__ __volatile__( + " .set push\n" + " .set mips32r2\n" + " rdhwr %0, $30\n" + " .set pop\n" + : "=r" (count)); + + return count; +} + +int __init init_constant_clocksource(void) +{ + int res; + unsigned long freq; + + freq = calc_const_freq(); + if (freq) + freq = cpu_clock_freq; + + clocksource_const.mult = + clocksource_hz2mult(freq, clocksource_const.shift); + + res = clocksource_register_hz(&clocksource_const, freq); + + sched_clock_register(read_sched_clock, 64, freq); + clocksource_const.archdata.vdso_clock_mode = VDSO_CLOCKMODE_CONST; + + return res; +} diff --git a/arch/mips/loongson64/smp.c b/arch/mips/loongson64/smp.c index 4b621981f3890..0a83e8aa925be 100644 --- a/arch/mips/loongson64/smp.c +++ b/arch/mips/loongson64/smp.c @@ -456,7 +456,9 @@ static void loongson3_smp_finish(void) { int cpu = smp_processor_id(); - write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); + if (!cpu_has_constant_timer) + write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); + local_irq_enable(); ipi_clear_buf(cpu); From 7333887fcf8811ad00d305a0b27e5b26fc8506a2 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 3 Dec 2024 16:52:14 +0800 Subject: [PATCH 014/130] AOSCOS: MIPS: loongson64: fix constant timer build on kernel versions >= v5.7-rc2 With commit 07d8350ede4c ("genirq: Remove setup_irq() and remove_irq()"), setup_irq() was removed in favour of request_irq(). This was done following commit cc2550b421aa ("clocksource: Replace setup_irq() by request_irq()") (v5.7-rc1). Follow the latter commit as a model to revise loongson64's constant_timer and use request_irq() instead. Also add error handling and output for cases of failure. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/constant_timer.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/arch/mips/loongson64/constant_timer.c b/arch/mips/loongson64/constant_timer.c index b8bdbb2c47b51..975fb93c4efa2 100644 --- a/arch/mips/loongson64/constant_timer.c +++ b/arch/mips/loongson64/constant_timer.c @@ -42,12 +42,6 @@ static irqreturn_t constant_timer_interrupt(int irq, void *data) return IRQ_NONE; } -static struct irqaction constant_timer_irqaction = { - .handler = constant_timer_interrupt, - .flags = IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED, - .name = "timer", -}; - static int constant_set_state_oneshot(struct clock_event_device *evt) { unsigned int raw_cpuid; @@ -155,6 +149,7 @@ int constant_clockevent_init(void) unsigned long max_delta = (1UL << 48) - 1; unsigned int cpu = smp_processor_id(); struct clock_event_device *cd; + int err; config = read_c0_config6(); config |= LOONGSON_CONF6_EXTIMER; @@ -192,7 +187,14 @@ int constant_clockevent_init(void) return 0; constant_timer_irq_installed = 1; - setup_irq(irq, &constant_timer_irqaction); + + err = request_irq(irq, constant_timer_interrupt, + IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED, + "timer", cd); + if (err) { + pr_err("loongson64: setup irq for constant_clock_event failed: %d\n", err); + return err; + } return 0; } From 310d24ab2c97e40d6700335cf41585da5551e28a Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 3 Dec 2024 16:56:36 +0800 Subject: [PATCH 015/130] AOSCOS: MIPS: loongson64: use generic vDSO clock mode storage for constant_timer Follow commit e1bdb22ebe53 ("mips: vdso: Use generic VDSO clock mode storage") (v5.7-rc1) and revise vdso_clock_mode setting in constant_timer code for loongson64 platforms. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/constant_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/constant_timer.c b/arch/mips/loongson64/constant_timer.c index 975fb93c4efa2..c1fda4a3ae165 100644 --- a/arch/mips/loongson64/constant_timer.c +++ b/arch/mips/loongson64/constant_timer.c @@ -252,7 +252,7 @@ int __init init_constant_clocksource(void) res = clocksource_register_hz(&clocksource_const, freq); sched_clock_register(read_sched_clock, 64, freq); - clocksource_const.archdata.vdso_clock_mode = VDSO_CLOCKMODE_CONST; + clocksource_const.vdso_clock_mode = VDSO_CLOCKMODE_CONST; return res; } From b832e5d24412f3902ac4ecdf4d3b81139ef3279e Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 12 Jul 2017 16:30:55 +0800 Subject: [PATCH 016/130] AOSCOS: MIPS: Loongson: Make CPUFreq usable for Loongson-3 Loongson-3A/3B support frequency scaling. But due to hardware limitation, Loongson-3A's frequency scaling is not independent for each core, we suggest enable Loongson-3A's CPUFreq only when there is one core online. Loongson-3B can adjust frequency independently for each core, so it can be always enabled. Each package has only one register (ChipConfig or FreqCtrl) to control frequency, so we need spinlocks to protect register access for multi- cores. However, we cannot use spinlock when a core becomes into "wait" status (frequency = 0), so we only enable "wait" when there is one core in a package online. arch/mips/kernel/smp.c is modified to guarantee udelay_val has the correct value while both CPU hotplug and CPUFreq are enabled. [Mingcong Bai: Resolved merge conflicts in the following... arch/mips/include/asm/mach-loongson64/loongson.h arch/mips/kernel/smp.c arch/mips/loongson64/Kconfig arch/mips/loongson64/clock.c arch/mips/loongson64/loongson-3/Makefile => arch/mips/loongson64/Makefile drivers/cpufreq/Kconfig drivers/cpufreq/Makefile Additionally, made a fork for drivers/cpufreq/loongson3_cpufreq.c => drivers/cpufreq/mips_loongson3_cpufreq.c (CONFIG_LOONGSON3_CPUFREQ => CONFIG_MIPS_LOONGSON3_CPUFREQ) Rename module to gs464_cpufreq to avoid confusion with loongson3_cpufreq (for LoongArch), also updated all function names, methods, and descriptions to reflect this change. Also make this driver a boolean (built-in or disabled), as `mips_clockevent_device' and `clockevents_update_freq' are not exposed as symbols (SGI IP30 clock module is also a non-tristate). ] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/Kconfig | 3 + arch/mips/include/asm/clock.h | 57 ++++++ .../include/asm/mach-loongson64/loongson.h | 5 + arch/mips/kernel/smp.c | 3 +- arch/mips/loongson64/Makefile | 2 +- arch/mips/loongson64/clock.c | 170 ++++++++++++++++ drivers/cpufreq/Kconfig | 12 ++ drivers/cpufreq/Makefile | 2 + drivers/cpufreq/gs464_cpufreq.c | 191 ++++++++++++++++++ 9 files changed, 443 insertions(+), 2 deletions(-) create mode 100644 arch/mips/include/asm/clock.h create mode 100644 arch/mips/loongson64/clock.c create mode 100644 drivers/cpufreq/gs464_cpufreq.c diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index d675edd5d10be..bbad673d533cf 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -494,6 +494,7 @@ config MACH_LOONGSON64 select CSRC_R4K select CEVT_R4K select FORCE_PCI + select HAVE_CLK select ISA select I8259 select IRQ_MIPS_CPU @@ -517,6 +518,8 @@ config MACH_LOONGSON64 select BUILTIN_DTB select PCI_HOST_GENERIC select HAVE_ARCH_NODEDATA_EXTENSION if NUMA + select CPU_SUPPORTS_CPUFREQ + select MIPS_EXTERNAL_TIMER help This enables the support of Loongson-2/3 family of machines. diff --git a/arch/mips/include/asm/clock.h b/arch/mips/include/asm/clock.h new file mode 100644 index 0000000000000..db72b78e0b2f4 --- /dev/null +++ b/arch/mips/include/asm/clock.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_MIPS_CLOCK_H +#define __ASM_MIPS_CLOCK_H + +#include +#include +#include +#include + +struct clk; + +struct clk_ops { + void (*init) (struct clk *clk); + void (*enable) (struct clk *clk); + void (*disable) (struct clk *clk); + void (*recalc) (struct clk *clk); + int (*set_rate) (struct clk *clk, unsigned long rate, int algo_id); + long (*round_rate) (struct clk *clk, unsigned long rate); +}; + +struct clk { + struct list_head node; + const char *name; + int id; + struct module *owner; + + struct clk *parent; + struct clk_ops *ops; + + struct kref kref; + + unsigned long rate; + unsigned long flags; +}; + +#define CLK_ALWAYS_ENABLED (1 << 0) +#define CLK_RATE_PROPAGATES (1 << 1) + +int clk_init(void); + +int __clk_enable(struct clk *); +void __clk_disable(struct clk *); + +void clk_recalc_rate(struct clk *); + +int clk_register(struct clk *); +void clk_unregister(struct clk *); + +struct clk *gs464_cpu_clk_get(int cpu); +struct clk *gs464_clk_get(struct device *dev, const char *id); +void gs464_propagate_rate(struct clk *clk); +unsigned long gs464_clk_get_rate(struct clk *clk); +int gs464_clk_set_rate(struct clk *clk, unsigned long rate); +long gs464_clk_round_rate(struct clk *clk, unsigned long rate); +int gs464_clock_init(void); + +#endif /* __ASM_MIPS_CLOCK_H */ diff --git a/arch/mips/include/asm/mach-loongson64/loongson.h b/arch/mips/include/asm/mach-loongson64/loongson.h index f7c3ab6d724e2..70399b6cd2aee 100644 --- a/arch/mips/include/asm/mach-loongson64/loongson.h +++ b/arch/mips/include/asm/mach-loongson64/loongson.h @@ -263,4 +263,9 @@ extern u64 loongson_freqctrl[MAX_PACKAGES]; #define LOONGSON_PCIMAP_WIN(WIN, ADDR) \ ((((ADDR)>>26) & LOONGSON_PCIMAP_PCIMAP_LO0) << ((WIN)*6)) +#ifdef CONFIG_CPU_SUPPORTS_CPUFREQ +#include +extern struct cpufreq_frequency_table gs464_clockmod_table[]; +#endif + #endif /* __ASM_MACH_LOONGSON64_LOONGSON_H */ diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 81f6c4f8fbc15..094453f4fc6e0 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -367,7 +367,8 @@ asmlinkage void start_secondary(void) */ calibrate_delay(); - cpu_data[cpu].udelay_val = loops_per_jiffy; + if (!cpu_data[cpu].udelay_val) + cpu_data[cpu].udelay_val = loops_per_jiffy; set_cpu_sibling_map(cpu); set_cpu_core_map(cpu); diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index 76d6251fb7472..d88c695355075 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ - constant_timer.o \ + clock.o constant_timer.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/clock.c b/arch/mips/loongson64/clock.c new file mode 100644 index 0000000000000..09d3c28a23abd --- /dev/null +++ b/arch/mips/loongson64/clock.c @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2008 - 2014 Lemote Inc. + * Author: Yan Hua, yanh@lemote.com + * Chen Huacai, chenhc@lemote.com + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include + +#include + +#include + +static LIST_HEAD(clock_list); +static DEFINE_SPINLOCK(clock_lock); +static DEFINE_MUTEX(clock_list_sem); + +/* Minimum CLK support */ +enum { + DC_ZERO, DC_12PT, DC_25PT, DC_37PT, DC_50PT, DC_62PT, + DC_75PT, DC_87PT, DC_DISABLE, DC_RESV +}; + +struct cpufreq_frequency_table gs464_clockmod_table[] = { + {0, DC_ZERO, CPUFREQ_ENTRY_INVALID}, + {0, DC_12PT, 0}, + {0, DC_25PT, 0}, + {0, DC_37PT, 0}, + {0, DC_50PT, 0}, + {0, DC_62PT, 0}, + {0, DC_75PT, 0}, + {0, DC_87PT, 0}, + {0, DC_DISABLE, 0}, + {0, DC_RESV, CPUFREQ_TABLE_END}, +}; + +static struct clk cpu_clks[NR_CPUS]; +static char clk_names[NR_CPUS][10]; + +struct clk *gs464_cpu_clk_get(int cpu) +{ + return &cpu_clks[cpu]; +} + +struct clk *gs464_clk_get(struct device *dev, const char *id) +{ + int i; + struct clk *clk; + + if (!id) + return NULL; + + for_each_possible_cpu(i) { + clk = &cpu_clks[i]; + if (strcmp(clk->name, id) == 0) + return clk; + } + + return NULL; +} + +void gs464_propagate_rate(struct clk *clk) +{ + struct clk *clkp; + + list_for_each_entry(clkp, &clock_list, node) { + if (likely(clkp->parent != clk)) + continue; + if (likely(clkp->ops && clkp->ops->recalc)) + clkp->ops->recalc(clkp); + if (unlikely(clkp->flags & CLK_RATE_PROPAGATES)) + gs464_propagate_rate(clkp); + } +} + +unsigned long gs464_clk_get_rate(struct clk *clk) +{ + if (!clk) + return 0; + + return (unsigned long)clk->rate; +} + +int gs464_clk_set_rate(struct clk *clk, unsigned long rate) +{ + int regval, ret = 0; + struct cpufreq_frequency_table *pos; + int cpu = clk - cpu_clks; + uint64_t core_id = cpu_core(&cpu_data[cpu]); + uint64_t package_id = cpu_data[cpu].package; + + if (likely(clk->ops && clk->ops->set_rate)) { + unsigned long flags; + + spin_lock_irqsave(&clock_lock, flags); + ret = clk->ops->set_rate(clk, rate, 0); + spin_unlock_irqrestore(&clock_lock, flags); + } + + if (unlikely(clk->flags & CLK_RATE_PROPAGATES)) + gs464_propagate_rate(clk); + + cpufreq_for_each_valid_entry(pos, gs464_clockmod_table) + if (rate == pos->frequency) + break; + if (rate != pos->frequency) + return -ENOTSUPP; + + clk->rate = rate; + + if ((read_c0_prid() & 0xf) == PRID_REV_LOONGSON3A_R1) { + regval = LOONGSON_CHIPCFG(package_id); + regval = (regval & ~0x7) | (pos->driver_data - 1); + LOONGSON_CHIPCFG(package_id) = regval; + } else { + regval = LOONGSON_FREQCTRL(package_id); + regval = (regval & ~(0x7 << (core_id*4))) | + ((pos->driver_data - 1) << (core_id*4)); + LOONGSON_FREQCTRL(package_id) = regval; + } + + return ret; +} + +long gs464_clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (likely(clk->ops && clk->ops->round_rate)) { + unsigned long flags, rounded; + + spin_lock_irqsave(&clock_lock, flags); + rounded = clk->ops->round_rate(clk, rate); + spin_unlock_irqrestore(&clock_lock, flags); + + return rounded; + } + + return rate; +} + +int gs464_clock_init(void) +{ + int i; + + for_each_possible_cpu(i) { + sprintf(clk_names[i], "cpu%d_clk", i); + cpu_clks[i].name = clk_names[i]; + cpu_clks[i].flags = CLK_ALWAYS_ENABLED | CLK_RATE_PROPAGATES; + cpu_clks[i].rate = cpu_clock_freq / 1000; + } + + /* clock table init */ + for (i = 1; + (gs464_clockmod_table[i].frequency != CPUFREQ_TABLE_END); + i++) + gs464_clockmod_table[i].frequency = ((cpu_clock_freq / 1000) * i) / 8; + + return 0; +} +arch_initcall(gs464_clock_init); + +MODULE_AUTHOR("Huacai Chen "); +MODULE_DESCRIPTION("CPUFreq driver for GS464 (MIPS-based Loongson-3) processors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index b14584bfdf3f5..393cb75809741 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -271,6 +271,18 @@ config LOONGSON2_CPUFREQ Loongson2F and its successors support this feature. If in doubt, say N. + +config GS464_CPUFREQ + bool "GS464 (MIPS-based Loongson-3) CPUFreq Driver" + depends on CPU_LOONGSON64 && MIPS + help + This option adds a CPUFreq driver for GS464 (MIPS-based + Loongson-3) processors which support software configurable CPU + frequency. + + For details, take a look at . + + If in doubt, say N. endif if LOONGARCH diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 076ea3ac1b56d..b6f271b5b40e9 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -105,6 +105,8 @@ obj-$(CONFIG_BMIPS_CPUFREQ) += bmips-cpufreq.o obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o obj-$(CONFIG_LOONGSON3_ACPI_CPUFREQ) += loongson3-acpi-cpufreq.o +obj-$(CONFIG_GS464_CPUFREQ) += gs464_cpufreq.o +obj-$(CONFIG_LOONGSON1_CPUFREQ) += loongson1-cpufreq.o obj-$(CONFIG_SH_CPU_FREQ) += sh-cpufreq.o obj-$(CONFIG_SPARC_US2E_CPUFREQ) += sparc-us2e-cpufreq.o obj-$(CONFIG_SPARC_US3_CPUFREQ) += sparc-us3-cpufreq.o diff --git a/drivers/cpufreq/gs464_cpufreq.c b/drivers/cpufreq/gs464_cpufreq.c new file mode 100644 index 0000000000000..91c91e616bffd --- /dev/null +++ b/drivers/cpufreq/gs464_cpufreq.c @@ -0,0 +1,191 @@ +/* + * CPUFreq driver for the loongson-3 processors + * + * All revisions of Loongson-3 processor support this feature. + * + * Copyright (C) 2008 - 2014 Lemote Inc. + * Author: Yan Hua, yanh@lemote.com + * Chen Huacai, chenhc@lemote.com + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static spinlock_t cpufreq_reg_lock[MAX_PACKAGES]; + +extern struct clk *gs464_cpu_clk_get(int cpu); + +static int gs464_cpu_freq_notifier(struct notifier_block *nb, + unsigned long val, void *data); + +static struct notifier_block gs464_cpufreq_notifier_block = { + .notifier_call = gs464_cpu_freq_notifier +}; + +#ifdef CONFIG_SMP +static int gs464_cpu_freq_notifier(struct notifier_block *nb, + unsigned long val, void *data) +{ + unsigned long cpu; + struct clock_event_device *cd; + struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; + + if (val != CPUFREQ_POSTCHANGE) + return 0; + + for_each_cpu(cpu, freqs->policy->cpus) { + cd = &per_cpu(mips_clockevent_device, cpu); + + if (cpu == smp_processor_id()) + clockevents_update_freq(cd, freqs->new * 1000 / 2); + else { + clockevents_calc_mult_shift(cd, freqs->new * 1000 / 2, 4); + cd->min_delta_ns = clockevent_delta2ns(cd->min_delta_ticks, cd); + cd->max_delta_ns = clockevent_delta2ns(cd->max_delta_ticks, cd); + } + } + + return 0; +} +#else +static int gs464_cpu_freq_notifier(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; + struct clock_event_device *cd = &per_cpu(mips_clockevent_device, 0); + + if (val == CPUFREQ_POSTCHANGE) + clockevents_update_freq(cd, freqs->new * 1000 / 2); + + return 0; +} +#endif + +static unsigned int gs464_cpufreq_get(unsigned int cpu) +{ + return gs464_clk_get_rate(gs464_cpu_clk_get(cpu)); +} + +/* + * Here we notify other drivers of the proposed change and the final change. + */ +static int gs464_cpufreq_target(struct cpufreq_policy *policy, + unsigned int index) +{ + unsigned int freq; + unsigned int cpu = policy->cpu; + unsigned int package = cpu_data[cpu].package; + + if (!cpu_online(cpu)) + return -ENODEV; + + freq = + ((cpu_clock_freq / 1000) * + gs464_clockmod_table[index].driver_data) / 8; + + /* setting the cpu frequency */ + spin_lock(&cpufreq_reg_lock[package]); + gs464_clk_set_rate(policy->clk, freq); + spin_unlock(&cpufreq_reg_lock[package]); + + return 0; +} + +static int gs464_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + if (!cpu_online(policy->cpu)) + return -ENODEV; + + policy->clk = gs464_cpu_clk_get(policy->cpu); + policy->cur = gs464_cpufreq_get(policy->cpu); + + policy->cpuinfo.transition_latency = 1000; + policy->freq_table = gs464_clockmod_table; + + /* Loongson-3A R1: all cores in a package share one clock */ + if ((read_c0_prid() & 0xf) == PRID_REV_LOONGSON3A_R1) + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + + return 0; +} + +static int gs464_cpufreq_exit(struct cpufreq_policy *policy) +{ + return 0; +} + +static struct cpufreq_driver gs464_cpufreq_driver = { + .name = "loongson3", + .init = gs464_cpufreq_cpu_init, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = gs464_cpufreq_target, + .get = gs464_cpufreq_get, + .exit = gs464_cpufreq_exit, + .attr = cpufreq_generic_attr, +}; + +static struct platform_device_id platform_device_ids[] = { + { + .name = "gs464_cpufreq", + }, + {} +}; + +MODULE_DEVICE_TABLE(platform, platform_device_ids); + +static struct platform_driver platform_driver = { + .driver = { + .name = "gs464_cpufreq", + .owner = THIS_MODULE, + }, + .id_table = platform_device_ids, +}; + +static int __init cpufreq_init(void) +{ + int i, ret; + + /* Register platform stuff */ + ret = platform_driver_register(&platform_driver); + if (ret) + return ret; + + pr_info("cpufreq: Loongson-3 CPU frequency driver.\n"); + + for (i = 0; i < MAX_PACKAGES; i++) + spin_lock_init(&cpufreq_reg_lock[i]); + + cpufreq_register_notifier(&gs464_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + + ret = cpufreq_register_driver(&gs464_cpufreq_driver); + + return ret; +} + +static void __exit cpufreq_exit(void) +{ + cpufreq_unregister_driver(&gs464_cpufreq_driver); + cpufreq_unregister_notifier(&gs464_cpufreq_notifier_block, + CPUFREQ_TRANSITION_NOTIFIER); + + platform_driver_unregister(&platform_driver); +} + +module_init(cpufreq_init); +module_exit(cpufreq_exit); + +MODULE_AUTHOR("Huacai Chen "); +MODULE_DESCRIPTION("CPUFreq driver for GS464 (MIPS-based Loongson-3) processors"); +MODULE_LICENSE("GPL"); From be8b83a64f27f5aa44b18cd8f49ffe37c47c52ec Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 27 Nov 2019 16:02:18 +0800 Subject: [PATCH 017/130] AOSCOS: MIPS: Loongson-3: Add CPUFreq BOOST support [Mingcong Bai: Resolve merge conflicts according to revisions made in "AOSCOS: MIPS: Loongson: Make CPUFreq usable for Loongson-3" and rename all `loongson3_' prefixes as `gs464_' per that commit.] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- .../asm/mach-loongson64/loongson_regs.h | 2 + .../asm/mach-loongson64/loongson_smc.h | 149 ++++++++++++++++++ drivers/cpufreq/gs464_cpufreq.c | 146 +++++++++++++++-- 3 files changed, 287 insertions(+), 10 deletions(-) create mode 100644 arch/mips/include/asm/mach-loongson64/loongson_smc.h diff --git a/arch/mips/include/asm/mach-loongson64/loongson_regs.h b/arch/mips/include/asm/mach-loongson64/loongson_regs.h index e557fe12da93b..e7412535e6408 100644 --- a/arch/mips/include/asm/mach-loongson64/loongson_regs.h +++ b/arch/mips/include/asm/mach-loongson64/loongson_regs.h @@ -228,7 +228,9 @@ static inline void csr_writeq(u64 val, u32 reg) #define LOONGSON_CSR_VENDOR 0x10 /* Vendor name string, should be "Loongson" */ #define LOONGSON_CSR_CPUNAME 0x20 /* Processor name string */ #define LOONGSON_CSR_NODECNT 0x408 +#define LOONGSON_CSR_SMCINT 0x420 #define LOONGSON_CSR_CPUTEMP 0x428 +#define LOONGSON_CSR_SMCMBX 0x51c /* PerCore CSR, only accessable by local cores */ #define LOONGSON_CSR_IPI_STATUS 0x1000 diff --git a/arch/mips/include/asm/mach-loongson64/loongson_smc.h b/arch/mips/include/asm/mach-loongson64/loongson_smc.h new file mode 100644 index 0000000000000..c5af9cd25aee0 --- /dev/null +++ b/arch/mips/include/asm/mach-loongson64/loongson_smc.h @@ -0,0 +1,149 @@ +#ifndef _LOONGSON_SMC_H_ +#define _LOONGSON_SMC_H_ + +union smc_message { + u32 value; + struct { + u32 arg : 24; + u8 cmd : 7; /* Return 0x7f if command failed */ + u8 complete : 1; + }; +}; + +/* Commands in SMC mailbox registers */ + +#define CMD_GET_VERSION 0x1 +/* Interface Version, input none, return version */ + +/* Features */ +#define CMD_GET_FEATURES 0x2 +/* Get features that SMC implemented, input index, output feature flags */ +#define CMD_GET_ENABLED_FEATURES 0x3 +/* Get currently enabled features, input index, output feature flags */ +#define CMD_SET_ENABLED_FEATURES 0x4 +/* Set features enabled state, input index and flags, output sucessfully enabled flags */ +struct feature_args { + u16 flags : 16; + u8 index : 8; +}; + +#define FEATURE_INDEX_GENERAL 0x0 +#define FEATURE_INDEX_ADVANCED 0x1 + +/* General Feature Flags */ +#define FEATURE_FREQ_SCALE (1 << 0) +#define FEATURE_VOLTAGE_SCALE (1 << 1) +#define FEATURE_BOOST (1 << 2) /* Enable Boost means set PLL from normal to higher */ +#define FEATURE_SENSORS (1 << 3) /* Temperature/Voltage sensors */ +#define FEATURE_FAN_CONTROL (1 << 4) + +/* Advanced Features Flags */ +#define FEATURE_ADJUST_DVFS_PARAM (1 << 0) /* Allow OS adjust FreqScale/VID of each level. */ + +/* Freqscale Related */ +#define CMD_GET_CPU_FREQUENCY 0x5 +/* Input CPUNum, output frequency, in MHz */ +#define CMD_GET_FREQ_LEVELS 0x6 +/* Input none, output freq levels */ +struct freq_level_args { + u8 min_level : 8; + u8 max_normal_level : 8; + u8 max_boost_level : 8; +}; + +#define CMD_GET_FREQ_INFO 0x7 +/* Input index and level, output info */ +#define CMD_SET_FREQ_INFO 0x8 +/* Input index and info, output none, available only with FEATURE_ADJUST_DVFS_PARAM */ +#define FREQ_INFO_INDEX_FREQ 0x0 /* Freq in MHz */ +struct freq_info_args { + u16 info : 16; + u8 index : 8; +}; + +#define CMD_SET_CPU_LEVEL 0x9 +/* Input cpu mask and level, output none */ +/* + * Note: This command return as completed only means + * SMC already knows the request, does not means the + * CPU freqency have changed. SMC should ensure constant + * counter frequency unchanged. + */ +struct freq_level_setting_args { + u16 cpumask : 16; + u8 level : 8; +}; + +/* TEMP Sensors */ +#define CMD_GET_SENSOR_NUM 0x10 +/* Input none, output Number of sensors in u4 */ + +#define CMD_GET_SENSOR_STATUS 0x11 +/* Input sensor_id and info_type, output info */ +#define SENSOR_INFO_TYPE_TEMP 0x0 +#define SENSOR_INFO_TYPE_VOLTAGE 0x1 +#define SENSOR_INFO_TYPE_FLAGS 0xf +#define SENSOR_FLAG_TEMP (1 << 0) +#define SENSOR_FLAG_VOLTAGE (1 << 1) +struct sensor_info_args { + union { + u16 val; + s16 temp; /* Signed 16bit, in Celsius */ + }; + u8 info_type : 4; + u8 sensor_id : 4; +}; + +/* Fan Control */ +#define CMD_GET_FAN_NUM 0x12 +/* Input none, output Number of fans in u4 */ + +#define CMD_GET_FAN_INFO 0x13 +/* Input sensor_id and info_type, output info */ +#define CMD_SET_FAN_INFO 0x14 +/* Input sensor_id and info_type info, output none */ +#define FAN_INFO_RPM 0x0 /* Return RPM, can not set */ +#define FAN_INFO_LEVEL 0x1 /* PWM Level, 0~255, only set with manual mode */ +#define FAN_INFO_FLAGS 0xf /* Determine Mode */ +#define FAN_FLAG_AUTO (1 << 0) +#define FAN_FLAG_MANUAL (1 << 1) + +struct fan_info_args { + u16 val; + u8 info_type : 4; + u8 fan_id : 4; +}; + +static inline int do_service_request(u8 cmd, void *arg) +{ + int retries; + union smc_message msg; + + msg.value = csr_readl(LOONGSON_CSR_SMCMBX); + if (!msg.complete) + return -1; + + msg.cmd = cmd; + msg.arg = *(u32 *)arg; + msg.complete = 0x0; + + csr_writel(msg.value, LOONGSON_CSR_SMCMBX); + csr_writel(csr_readl(LOONGSON_CSR_SMCINT) | 1<<10, LOONGSON_CSR_SMCINT); + + for (retries = 0; retries < 10000; retries++) { + msg.value = csr_readl(LOONGSON_CSR_SMCMBX); + if (msg.complete) + break; + + usleep_range(2, 5); + } + + if (!msg.complete || msg.cmd == 0x7f) + return -1; + + *(u32 *)arg = msg.arg; + + return 0; +} + +#endif diff --git a/drivers/cpufreq/gs464_cpufreq.c b/drivers/cpufreq/gs464_cpufreq.c index 91c91e616bffd..940a578b88456 100644 --- a/drivers/cpufreq/gs464_cpufreq.c +++ b/drivers/cpufreq/gs464_cpufreq.c @@ -21,9 +21,44 @@ #include #include +#include +#include +static int new_method = 0; +static int boost_supported = 0; +static struct mutex cpufreq_mutex[MAX_PACKAGES]; static spinlock_t cpufreq_reg_lock[MAX_PACKAGES]; +enum freq { + FREQ_LEV0, /* Reserved */ + FREQ_LEV1, FREQ_LEV2, FREQ_LEV3, FREQ_LEV4, + FREQ_LEV5, FREQ_LEV6, FREQ_LEV7, FREQ_LEV8, + FREQ_LEV9, FREQ_LEV10, FREQ_LEV11, FREQ_LEV12, + FREQ_LEV13, FREQ_LEV14, FREQ_LEV15, FREQ_LEV16, + FREQ_RESV +}; + +/* For Loongson-3A4000+, support boost */ +static struct cpufreq_frequency_table gs464_cpufreq_table[] = { + {0, FREQ_LEV0, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV1, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV2, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV3, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV4, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV5, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV6, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV7, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV8, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV9, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV10, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV11, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV12, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV13, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV14, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV15, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_LEV16, CPUFREQ_ENTRY_INVALID}, + {0, FREQ_RESV, CPUFREQ_TABLE_END}, +}; extern struct clk *gs464_cpu_clk_get(int cpu); static int gs464_cpu_freq_notifier(struct notifier_block *nb, @@ -41,6 +76,9 @@ static int gs464_cpu_freq_notifier(struct notifier_block *nb, struct clock_event_device *cd; struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; + if (freqs->flags & CPUFREQ_CONST_LOOPS) + return 0; + if (val != CPUFREQ_POSTCHANGE) return 0; @@ -65,6 +103,9 @@ static int gs464_cpu_freq_notifier(struct notifier_block *nb, struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data; struct clock_event_device *cd = &per_cpu(mips_clockevent_device, 0); + if (freqs->flags & CPUFREQ_CONST_LOOPS) + return 0; + if (val == CPUFREQ_POSTCHANGE) clockevents_update_freq(cd, freqs->new * 1000 / 2); @@ -77,6 +118,18 @@ static unsigned int gs464_cpufreq_get(unsigned int cpu) return gs464_clk_get_rate(gs464_cpu_clk_get(cpu)); } +static int gs464_cpufreq_set(struct cpufreq_policy *policy, int freq_level) +{ + uint32_t core_id = cpu_core(&cpu_data[policy->cpu]); + struct freq_level_setting_args args; + + args.level = freq_level; + args.cpumask = 1 << core_id; + do_service_request(CMD_SET_CPU_LEVEL, &args); + + return 0; +} + /* * Here we notify other drivers of the proposed change and the final change. */ @@ -95,9 +148,15 @@ static int gs464_cpufreq_target(struct cpufreq_policy *policy, gs464_clockmod_table[index].driver_data) / 8; /* setting the cpu frequency */ - spin_lock(&cpufreq_reg_lock[package]); - gs464_clk_set_rate(policy->clk, freq); - spin_unlock(&cpufreq_reg_lock[package]); + if (new_method) { + mutex_lock(&cpufreq_mutex[package]); + gs464_cpufreq_set(policy, index); + mutex_unlock(&cpufreq_mutex[package]); + } else { + spin_lock(&cpufreq_reg_lock[package]); + gs464_clk_set_rate(policy->clk, freq); + spin_unlock(&cpufreq_reg_lock[package]); + } return 0; } @@ -110,12 +169,17 @@ static int gs464_cpufreq_cpu_init(struct cpufreq_policy *policy) policy->clk = gs464_cpu_clk_get(policy->cpu); policy->cur = gs464_cpufreq_get(policy->cpu); - policy->cpuinfo.transition_latency = 1000; - policy->freq_table = gs464_clockmod_table; - - /* Loongson-3A R1: all cores in a package share one clock */ - if ((read_c0_prid() & 0xf) == PRID_REV_LOONGSON3A_R1) - cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + if (new_method) { + /* Loongson-3A R4: new method */ + policy->cpuinfo.transition_latency = 5000; + policy->freq_table = gs464_cpufreq_table; + } else { + policy->cpuinfo.transition_latency = 1000; + policy->freq_table = gs464_clockmod_table; + /* Loongson-3A R1: all cores in a package share one clock */ + if ((read_c0_prid() & PRID_REV_MASK) == PRID_REV_LOONGSON3A_R1) + cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu)); + } return 0; } @@ -152,6 +216,61 @@ static struct platform_driver platform_driver = { .id_table = platform_device_ids, }; +static int configure_cpufreq_info(void) +{ + int i, r, max_level; + struct feature_args args1; + struct freq_level_args args2; + struct freq_info_args args3; + + if (!cpu_has_csr()) + return -EPERM; + + if (cpu_has_constant_timer) + gs464_cpufreq_driver.flags |= CPUFREQ_CONST_LOOPS; + + args1.index = FEATURE_INDEX_GENERAL; + r = do_service_request(CMD_GET_FEATURES, &args1); + if (r < 0) + return -EPERM; + + if (args1.flags & FEATURE_FREQ_SCALE) + new_method = 1; + + if (args1.flags & FEATURE_BOOST) + boost_supported = 1; + + r = do_service_request(CMD_SET_ENABLED_FEATURES, &args1); + if (r < 0) { + new_method = 0; + boost_supported = 0; + return -EPERM; + } + + r = do_service_request(CMD_GET_FREQ_LEVELS, &args2); + if (r < 0) { + new_method = 0; + boost_supported = 0; + return -EPERM; + } + + if (boost_supported) + max_level = args2.max_boost_level; + else + max_level = args2.max_normal_level; + + for (i = args2.min_level; i <= max_level; i++) { + args3.info = i; + args3.index = FREQ_INFO_INDEX_FREQ; + do_service_request(CMD_GET_FREQ_INFO, &args3); + gs464_cpufreq_table[i].frequency = args3.info * 1000; + if (i > args2.max_normal_level) + gs464_cpufreq_table[i].flags = CPUFREQ_BOOST_FREQ; + } + + return 0; +} + static int __init cpufreq_init(void) { int i, ret; @@ -163,14 +282,21 @@ static int __init cpufreq_init(void) pr_info("cpufreq: Loongson-3 CPU frequency driver.\n"); - for (i = 0; i < MAX_PACKAGES; i++) + configure_cpufreq_info(); + + for (i = 0; i < MAX_PACKAGES; i++) { + mutex_init(&cpufreq_mutex[i]); spin_lock_init(&cpufreq_reg_lock[i]); + } cpufreq_register_notifier(&gs464_cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER); ret = cpufreq_register_driver(&gs464_cpufreq_driver); + if (boost_supported) + cpufreq_enable_boost_support(); + return ret; } From 13f1fad9a72e25a8c99e412df3acdf80ce4fc727 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 018/130] AOSCOS: MIPS: Loongson 3: Add basic EC operations Loongson-3 based laptops has a WPCE775L embedded controller. Add basic operations for future related drivers and board support. [Mingcong Bai: Moved the following files... arch/mips/loongson64/loongson-3/Makefile => arch/mips/loongson64/Makefile arch/mips/loongson64/loongson-3/ec_wpce775l.c => arch/mips/loongson64/ec_wpce775l.c] Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- .../include/asm/mach-loongson64/ec_wpce775l.h | 381 ++++++++++++++++++ arch/mips/loongson64/Makefile | 2 +- arch/mips/loongson64/ec_wpce775l.c | 249 ++++++++++++ drivers/input/serio/i8042.c | 2 +- 4 files changed, 632 insertions(+), 2 deletions(-) create mode 100644 arch/mips/include/asm/mach-loongson64/ec_wpce775l.h create mode 100644 arch/mips/loongson64/ec_wpce775l.c diff --git a/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h b/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h new file mode 100644 index 0000000000000..3d2ca3245a3e2 --- /dev/null +++ b/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h @@ -0,0 +1,381 @@ +/* + * EC (Embedded Controller) WPCE775L device driver header for Linux + * + * Copyright (C) 2011 Lemote Inc. + * Author : Wang Rui + * Author : Huang Wei + * Date : 2011-02-21 + * + * EC relative header file. All the EC registers should be defined here. + * + * 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 you option) and later version. + */ + +#ifndef __EC_WPCE775L_H__ +#define __EC_WPCE775L_H__ + +#define EC_VERSION "1.11" + +/* + * The following registers are determined by the EC index configureation. + * 1. fill the PORT_INDEX as EC register. + * 2. fill the PORT_DATA as EC register write data or get the data from it. + */ +/* base address for io access for Loongson3A+rs780e Notebook platform */ +#define EC_BASE_ADDR_PORT (0xb8000000) +#define SIO_INDEX_PORT 0x2E +#define SIO_DATA_PORT 0x2F + +/* + * EC delay time 500us for register and status access + * Unit : us + */ +#define EC_REG_DELAY 30000 +#define EC_CMD_TIMEOUT 0x1000 +#define EC_SEND_TIMEOUT 0xffff +#define EC_RECV_TIMEOUT 0xffff + +/* + * EC access port for with Host communication. + */ +#define EC_CMD_PORT 0x66 +#define EC_STS_PORT 0x66 +#define EC_DAT_PORT 0x62 + +/* + * ACPI legacy commands. + */ +#define CMD_READ_EC 0x80 /* Read EC command. */ +#define CMD_WRITE_EC 0x81 /* Write EC command. */ +#define CMD_GET_EVENT_NUM 0x84 /* Query EC command, for get SCI event number. */ + +/* + * ACPI OEM commands. + */ +#define CMD_RESET 0x4E /* Reset and poweroff the machine auto-clear: rd/wr */ +enum +{ + RESET_OFF = 0, + RESET_ON, + PWROFF_ON, + SUSPEND_ON, + STANDBY_ON +}; + +#define CMD_EC_VERSION 0x4F /* EC Version OEM command: 36 Bytes */ + +/* + * Used ACPI legacy command 80h to do active. + */ +/* >>> Read/Write temperature & fan index for ACPI 80h/81h command. */ +#define INDEX_TEMPERATURE_VALUE 0x1B /* Current CPU temperature value, Read and Write(81h command). */ +#define INDEX_FAN_MAXSPEED_LEVEL 0x5B /* Fan speed maxinum levels supported. Defaut is 6. */ +#define INDEX_FAN_SPEED_LEVEL 0x5C /* FAn speed level. [0,5] or [0x06, 0x38]*/ +#define INDEX_FAN_CTRLMOD 0x5D /* Fan control mode, 0 = by EC, 1 = by Host.*/ +enum +{ + FAN_CTRL_BYEC = 0, + FAN_CTRL_BYHOST +}; +#define INDEX_FAN_STSCTRL 0x5E /* Fan status/control, 0 = stop, 1 = run. */ +enum +{ + FAN_STSCTRL_OFF = 0, + FAN_STSCTRL_ON +}; +#define INDEX_FAN_ERRSTS 0x5F /* Fan error status, 0 = no error, 1 = has error. */ +enum +{ + FAN_ERRSTS_NO = 0, + FAN_ERRSTS_HAS +}; +#define INDEX_FAN_SPEED_LOW 0x08 /* Fan speed low byte.*/ +#define INDEX_FAN_SPEED_HIGH 0x09 /* Fan speed high byte. */ +/* <<< End Temp & Fan */ + +/* >>> Read/Write LCD backlight information/control index for ACPI 80h/81h command. */ +#define INDEX_BACKLIGHT_CTRLMODE 0x57 /* LCD backlight control mode: 0 = by EC, 1 = by HOST */ +enum +{ + BACKLIGHT_CTRL_BYEC = 0, + BACKLIGHT_CTRL_BYHOST +}; +#define INDEX_BACKLIGHT_STSCTRL 0x58 /* LCD backlight status or control: 0 = turn off, 1 = turn on */ +enum +{ + BACKLIGHT_OFF = 0, + BACKLIGHT_ON +}; +#define INDEX_DISPLAY_MAXBRIGHTNESS_LEVEL 0x59 /* LCD backlight brightness max level */ +#define INDEX_DISPLAY_BRIGHTNESS 0x5A /* 10 stages (0~9) LCD backlight brightness adjust */ +enum +{ + FLAG_DISPLAY_BRIGHTNESS_LEVEL_0 = 0, /* This level is backlight turn off. */ + FLAG_DISPLAY_BRIGHTNESS_LEVEL_1, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_2, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_3, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_4, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_5, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_6, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_7, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_8, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_9, + FLAG_DISPLAY_BRIGHTNESS_LEVEL_10 +}; +/* <<< End Backlight */ + +/* >>> Read battery(BQ3060) index for ACPI 80h command */ +/* + * The reported battery die temperature. + * The temperature is expressed in units of 0.25 seconds and is updated every 2.56 seconds. + * The equation to calculate reported pack temperature is: + * Temperature = 0.1 * (256 * TEMPH + TEMPL) Kelvin + * Temperature -= 273 Degrees Celsius + * The host sytem has read-only access to this register pair. + */ +#define INDEX_BATTERY_TEMP_LOW 0x20 /* Battery temperature low byte. */ +#define INDEX_BATTERY_TEMP_HIGH 0x21 /* Battery temperature high byte. */ +#define INDEX_BATTERY_VOL_LOW 0x22 /* Battery Voltage Low byte. */ +#define INDEX_BATTERY_VOL_HIGH 0x23 /* Battery Voltage High byte. */ +#define INDEX_BATTERY_CURRENT_LOW 0x24 /* Battery Current Low byte. */ +#define INDEX_BATTERY_CURRENT_HIGH 0x25 /* Battery Current High byte. */ +#define INDEX_BATTERY_AC_LOW 0x26 /* Battery AverageCurrent Low byte. */ +#define INDEX_BATTERY_AC_HIGH 0x27 /* Battery AverageCurrent High byte. */ +#define INDEX_BATTERY_CAPACITY 0x2A /* Battery RemainingCapacity percent. */ +#define INDEX_BATTERY_STATUS_LOW 0x2C /* Battery Status low byte. */ +enum +{ + BIT_BATTERY_STATUS_FD = 4, /* Battery Fully Discharged Notify. 1 = Fully Discharged */ + BIT_BATTERY_STATUS_FC, /* Battery Fully Charged Notify. 1 = Fully Charged. */ + BIT_BATTERY_STATUS_DSG, /* Battery Discharging mode. 0 = in charging mode, 1 = in discharging mode, + relaxation mode, or valid charge termination has occurred. */ + BIT_BATTERY_STATUS_INIT /* Battery Initialization. 1 = Initialization */ +}; +#define INDEX_BATTERY_STATUS_HIGH 0x2D /* Battery Status high byte. */ +enum +{ + BIT_BATTERT_STATUS_RTA = 0, /* Battery Remaining Time Alarm. <= 10min */ + BIT_BATTERY_STATUS_RCA, /* Battery Remaining Capacity Alarm. <= 430mAh */ + BIT_BATTERY_STATUS_TDA = 3, /* Battery Terminate Discharge Alarm. */ + BIT_BATTERY_STATUS_OTA, /* Battery Over Temperature Alarm. */ + BIT_BATTERY_STATUS_TCA = 6, /* Battery Terminate Charge Alarm. */ + BIT_BATTERY_STATUS_OCA /* Battery Over Charged Alarm. */ +}; +#define INDEX_BATTERY_RC_LOW 0x2E /* Battery RemainingCapacity Low byte. */ +#define INDEX_BATTERY_RC_HIGH 0x2F /* Battery RemainingCapacity High byte. */ +#define INDEX_BATTERY_ATTE_LOW 0x30 /* Battery AverageTimeToEmpty Low byte. */ +#define INDEX_BATTERY_ATTE_HIGH 0x31 /* Battery AverageTimeToEmpty High byte. */ +#define INDEX_BATTERY_ATTF_LOW 0x32 /* Battery AverageTimeToFull Low byte. */ +#define INDEX_BATTERY_ATTF_HIGH 0x33 /* Battery AverageTimeToFull High byte. */ +#define INDEX_BATTERY_FCC_LOW 0x34 /* Battery FullChargeCapacity Low byte. */ +#define INDEX_BATTERY_FCC_HIGH 0x35 /* Battery FullChargeCapacity High byte. */ +#define INDEX_BATTERY_CC_LOW 0x36 /* Battery ChargingCurrent Low byte. */ +#define INDEX_BATTERY_CC_HIGH 0x37 /* Battery ChargingCurrent High byte. */ +#define INDEX_BATTERY_CV_LOW 0x38 /* Battery ChargingVoltage Low byte. */ +#define INDEX_BATTERY_CV_HIGH 0x39 /* Battery ChargingVoltage High byte. */ +#define INDEX_BATTERY_CHGSTS_LOW 0x3A /* Battery ChargingStatus Low byte. */ +enum +{ + BIT_BATTERY_CHGSTS_XCHGLV = 0, /* 1 = Battery is depleted */ + BIT_BATTERY_CHGSTS_OC, /* 1 = Overcharge fault */ + BIT_BATTERY_CHGSTS_OCHGI, /* 1 = Overcharge current fault */ + BIT_BATTERY_CHGSTS_OCHGV, /* 1 = Overcharge voltage fault */ + BIT_BATTERY_CHGSTS_FCMTO, /* 1 = Fast-charge timeout fault */ + BIT_BATTERY_CHGSTS_PCMTO, /* 1 = Precharge timeout fault */ + BIT_BATTERY_CHGSTS_CB /* 1 = Cell balancing in progress */ +}; +#define INDEX_BATTERY_CHGSTS_HIGH 0x3B /* Battery ChargingStatus High byte. */ +enum +{ + BIT_BATTERY_CHGSTS_HTCHG = 0, /* 1 = Low temperature charging */ + BIT_BATTERY_CHGSTS_ST2CHG, /* 1 = Standard temperature charging 2 */ + BIT_BATTERY_CHGSTS_ST1CHG, /* 1 = Standard temperature charging 1 */ + BIT_BATTERY_CHGSTS_LTCHG, /* 1 = Low temperature charging */ + BIT_BATTERY_CHGSTS_PCHG = 5, /* 1 = Precharging conditions exist */ + BIT_BATTERY_CHGSTS_CHGSUSP, /* 1 = Charging suspended */ + BIT_BATTERY_CHGSTS_XCHG /* 1 = Charging disabled */ +}; +#define INDEX_BATTERY_CYCLECNT_LOW 0x3C /* Battery CycleCount Low byte. */ +#define INDEX_BATTERY_CYCLECNT_HIGH 0x3D /* Battery CycleCount High byte. */ + +/* Battery static information. */ +#define INDEX_BATTERY_DC_LOW 0x60 /* Battery DesignCapacity Low byte. */ +#define INDEX_BATTERY_DC_HIGH 0x61 /* Battery DesignCapacity High byte. */ +#define INDEX_BATTERY_DV_LOW 0x62 /* Battery DesignVoltage Low byte. */ +#define INDEX_BATTERY_DV_HIGH 0x63 /* Battery DesignVoltage High byte. */ +#define INDEX_BATTERY_MFD_LOW 0x64 /* Battery ManufactureDate Low byte. */ +#define INDEX_BATTERY_MFD_HIGH 0x65 /* Battery ManufactureDate High byte. */ +#define INDEX_BATTERY_SN_LOW 0x66 /* Battery SerialNumber Low byte. */ +#define INDEX_BATTERY_SN_HIGH 0x67 /* Battery SerialNumber High byte. */ +#define INDEX_BATTERY_MFN_LENG 0x68 /* Battery ManufacturerName string length. */ +#define INDEX_BATTERY_MFN_START 0x69 /* Battery ManufacturerName string start byte. */ + +#define INDEX_BATTERY_DEVNAME_LENG 0x74 /* Battery DeviceName string length. */ +#define INDEX_BATTERY_DEVNAME_START 0x75 /* Battery DeviceName string start byte. */ +#define INDEX_BATTERY_DEVCHEM_LENG 0x7C /* Battery DeviceChemitry string length. */ +#define INDEX_BATTERY_DEVCHEM_START 0x7D /* Battery DeviceChemitry string start byte. */ +#define INDEX_BATTERY_MFINFO_LENG 0x81 /* Battery ManufacturerInfo string length. */ +#define INDEX_BATTERY_MFINFO_START 0x82 /* Battery ManufacturerInfo string start byte. */ +#define INDEX_BATTERY_CELLCNT_START 0x95 /* Battery packaging fashion string start byte(=4). Unit: ASCII. */ +#define BATTERY_CELLCNT_LENG 4 /* Battery packaging fashion string size. */ +#define FLAG_BAT_CELL_3S1P "3S1P" + +#define BIT_BATTERY_CURRENT_PN 7 /* Battery current sign is positive or negative */ +#define BIT_BATTERY_CURRENT_PIN 0x07 /* Battery current sign is positive or negative.*/ +/* <<< End Battery */ + +#define MASK(x) (1 << x) + +#define INDEX_STOPCHG_STATUS 0xA1 /* Read currently stop charge status. */ +enum +{ + BIT_STOPCHG_FULLYCHG = 0, + BIT_STOPCHG_TIMEOUT, + BIT_STOPCHG_OVERTEMP, + BIT_STOPCHG_OVERVOLT, + BIT_STOPCHG_OVERCURRENT, + BIT_STOPCHG_TERMINATE +}; +#define INDEX_POWER_STATUS 0xA2 /* Read current power status. */ +enum +{ + BIT_POWER_PWRON = 0, /* Power-on start status, 1 = on power-on, 0 = power-on complete. */ + BIT_POWER_BATVL, /* Battery in very low status. */ + BIT_POWER_BATL, /* Battery in low status. */ + BIT_POWER_BATFCHG, /* Battery in fully charging status. */ + BIT_POWER_BATCHG, /* Battery in charging status. */ + BIT_POWER_TERMINATE, /* Battery in terminate charging status. */ + BIT_POWER_BATPRES, /* Battery present. */ + BIT_POWER_ACPRES /* AC present. */ +}; + +#define INDEX_DEVICE_STATUS 0xA3 /* Read Current Device Status */ +enum +{ + BIT_DEVICE_TP = 0, /* TouchPad status: 0 = close, 1 = open */ + BIT_DEVICE_WLAN, /* WLAN status: 0 = close, 1 = open */ + BIT_DEVICE_3G, /* 3G status: 0 = close, 1 = open */ + BIT_DEVICE_CAM, /* Camera status: 0 = close, 1 = open */ + BIT_DEVICE_MUTE, /* Mute status: 0 = close, 1 = open */ + BIT_DEVICE_LID, /* LID status: 0 = close, 1 = open */ + BIT_DEVICE_BKLIGHT, /* BackLight status: 0 = close, 1 = open */ + BIT_DEVICE_SIM /* SIM Card status: 0 = pull out, 1 = insert */ +}; + +#define INDEX_SHUTDOWN_ID 0xA4 /* Read Shutdown ID */ +enum +{ + BIT_SHUTDNID_S45 = 0, /* in S4 or S5 */ + BIT_SHUTDNID_BATDEAD, /* Battery Dead */ + BIT_SHUTDNID_OVERHEAT, /* Over Heat */ + BIT_SHUTDNID_SYSCMD, /* System command */ + BIT_SHUTDNID_LPRESSPWN, /* Long press power button */ + BIT_SHUTDNID_PWRUNDER9V,/* Batery voltage low under 9V */ + BIT_SHUTDNID_S3, /* Entry S3 state */ + BIT_SHUTDNID_S1 /* Entry S1 state */ +}; + +#define INDEX_SYSTEM_CFG 0xA5 /* Read System config */ +#define BIT_SYSCFG_TPSWITCH (1 << 0) /* TouchPad switch */ +#define BIT_SYSCFG_WLANPRES (1 << 1) /* WLAN present */ +#define BIT_SYSCFG_NB3GPRES (1 << 2) /* 3G present */ +#define BIT_SYSCFG_CAMERAPRES (1 << 3) /* Camera Present */ +#define BIT_SYSCFG_VOLCTRLEC (1 << 4) /* Volume control by EC */ +#define BIT_SYSCFG_BLCTRLEC (1 << 5) /* Backlight control by EC */ +#define BIT_SYSCFG_AUTOBRIGHT (1 << 7) /* Auto brightness */ + +#define INDEX_VOLUME_LEVEL 0xA6 /* Read Volume Level command */ +#define INDEX_VOLUME_MAXLEVEL 0xA7 /* Volume MaxLevel */ +#define VOLUME_MAX_LEVEL 0x0A /* Volume level max is 11 */ +enum +{ + FLAG_VOLUME_LEVEL_0 = 0, + FLAG_VOLUME_LEVEL_1, + FLAG_VOLUME_LEVEL_2, + FLAG_VOLUME_LEVEL_3, + FLAG_VOLUME_LEVEL_4, + FLAG_VOLUME_LEVEL_5, + FLAG_VOLUME_LEVEL_6, + FLAG_VOLUME_LEVEL_7, + FLAG_VOLUME_LEVEL_8, + FLAG_VOLUME_LEVEL_9, + FLAG_VOLUME_LEVEL_10 +}; + +/* Camera control */ +#define INDEX_CAM_STSCTRL 0xAA +enum +{ + CAM_STSCTRL_OFF = 0, + CAM_STSCTRL_ON +}; + +/* EC_SC input */ +/* EC Status query, by direct read 66h port. */ +#define EC_SMI_EVT (1 << 6) /* 1 = SMI event padding */ +#define EC_SCI_EVT (1 << 5) /* 1 = SCI event padding */ +#define EC_BURST (1 << 4) /* 1 = Controller is in burst mode */ +#define EC_CMD (1 << 3) /* 1 = Byte in data register is command */ + +#define EC_IBF (1 << 1) /* 1 = Input buffer full (data ready for ec) */ +#define EC_OBF (1 << 0) /* 1 = Output buffer full (data ready for host) */ + +/* SCI Event Number from EC */ +enum +{ + SCI_EVENT_NUM_WLAN = 0x21, /* 0x21, Fn+F1, Wlan is on or off */ + SCI_EVENT_NUM_3G, /* 0x22, Fn+F9 for 3G switch */ + SCI_EVENT_NUM_LID, /* 0x23, press the lid or not */ + SCI_EVENT_NUM_DISPLAY_TOGGLE, /* 0x24, Fn+F8 for display switch */ + SCI_EVENT_NUM_SLEEP, /* 0x25, Fn+ESC for entering sleep mode */ + SCI_EVENT_NUM_BRIGHTNESS_UP, /* 0x26, Fn+F3, LCD backlight brightness up adjust */ + SCI_EVENT_NUM_BRIGHTNESS_DN, /* 0x27, Fn+F2, LCD backlight brightness down adjust */ + SCI_EVENT_NUM_CAMERA, /* 0x28, Fn+F10, Camera is on or off */ + SCI_EVENT_NUM_TP, /* 0x29, Fn+F11, TouchPad is on */ + SCI_EVENT_NUM_AUDIO_MUTE, /* 0x2A, Fn+F4, Mute is on or off */ + SCI_EVENT_NUM_BLACK_SCREEN, /* 0x2B, Fn+F7, Black screen is on or off */ + SCI_EVENT_NUM_VOLUME_UP, /* 0x2C, Fn+F6, Volume up adjust */ + SCI_EVENT_NUM_VOLUME_DN, /* 0x2D, Fn+F5, Volume down adjust */ + SCI_EVENT_NUM_OVERTEMP, /* 0x2E, Over-temperature happened */ + SCI_EVENT_NUM_SIM, /* 0x2F, SIM Card Detect */ + SCI_EVENT_NUM_AC, /* 0x30, AC in/out */ + SCI_EVENT_NUM_BAT, /* 0x31, BAT in/out */ + SCI_EVENT_NUM_BATL, /* 0x32, Battery Low capacity alarm, < 10% */ + SCI_EVENT_NUM_BATVL, /* 0x33, Battery VeryLow capacity alarm, < 5% */ + SCI_EVENT_NUM_THROT, /* 0x34, CPU Throttling event alarm, CPU Temperature > 85 or < 80. */ + SCI_EVENT_NUM_POWER = 0x37, /* 0x37, Power button */ + SCI_EVENT_RESOLUTION_SETTING, /* 0x38, Resolution Setting */ + SCI_EVENT_MEDIA_RUN_PAUSE, /* 0x39, Media Play or Pause */ + SCI_EVENT_MEDIA_STOP, /* 0x3A, Media Stop */ + SCI_EVENT_MEDIA_LAST, /* 0x3B, Media Play last one */ + SCI_EVENT_MEDIA_NEXT, /* 0x3C, Media Play next one */ + SCI_EVENT_RECOVERY = 0x3D /* 0x3D, Recovery Event */ +}; + +#define SCI_EVENT_NUM_START SCI_EVENT_NUM_WLAN +#define SCI_EVENT_NUM_END SCI_EVENT_RECOVERY + +extern unsigned char app_access_ec_flag; + +typedef int (*sci_handler)(int status); + +/* The general ec index-io port read action */ +extern unsigned char ec_read(unsigned char index); +extern unsigned char ec_read_all(unsigned char command, unsigned char index); +extern unsigned char ec_read_noindex(unsigned char command); + +/* The general ec index-io port write action */ +extern int ec_write(unsigned char index, unsigned char data); +extern int ec_write_all(unsigned char command, unsigned char index, unsigned char data); +extern int ec_write_noindex(unsigned char command, unsigned char data); + +/* Query sequence of 62/66 port access routine. */ +extern int ec_query_seq(unsigned char command); +extern int ec_get_event_num(void); + +extern void clean_ec_event_status(void); + +#endif /* __EC_WPCE775L_H__ */ diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index d88c695355075..e22a7e9760e80 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -4,7 +4,7 @@ # obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ - clock.o constant_timer.o \ + clock.o constant_timer.o ec_wpce775l.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/ec_wpce775l.c b/arch/mips/loongson64/ec_wpce775l.c new file mode 100644 index 0000000000000..f407ddbe933a1 --- /dev/null +++ b/arch/mips/loongson64/ec_wpce775l.c @@ -0,0 +1,249 @@ +/* + * EC (Embedded Controller) WPCE775L device function for Linux. + * Author : Wang Rui + * Author : Huang Wei + * Date : 2011-02-21 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include + +#include + +/* This spinlock is dedicated for 62&66 ports and super io port access. */ +extern spinlock_t i8042_lock; +#define index_access_lock i8042_lock +DEFINE_SPINLOCK(port_access_lock); + +static int send_ec_command(unsigned char command) +{ + int timeout, ret = 0; + + timeout = EC_SEND_TIMEOUT; + while ((inb(EC_STS_PORT) & EC_IBF) && --timeout) + ; + if (!timeout) { + printk(KERN_ERR "Timeout while sending command 0x%02x to EC!\n", command); + ret = -1; + goto out; + } + + outb(command, EC_CMD_PORT); + +out: + return ret; +} + +static int send_ec_data(unsigned char data) +{ + int timeout, ret = 0; + + timeout = EC_SEND_TIMEOUT; + while ((inb(EC_STS_PORT) & EC_IBF) && --timeout) + ; + if (!timeout) { + printk(KERN_ERR "Timeout while sending data 0x%02x to EC!\n", data); + ret = -1; + goto out; + } + + outb(data, EC_DAT_PORT); + +out: + return ret; +} + +static unsigned char recv_ec_data(void) +{ + int timeout; + unsigned char data; + + timeout = EC_RECV_TIMEOUT; + while (!(inb(EC_STS_PORT) & EC_OBF) && --timeout) + ; + if (!timeout) { + printk(KERN_ERR "Timeout while receiving data from EC! status 0x%x.\n", inb(EC_STS_PORT)); + data = 0; + goto skip_data; + } + + data = inb(EC_DAT_PORT); + +skip_data: + return data; +} + +void clean_ec_event_status(void) +{ + unsigned long flags; + + spin_lock_irqsave(&port_access_lock, flags); + outl(0x404000, 0x810); + spin_unlock_irqrestore(&port_access_lock, flags); +} +EXPORT_SYMBOL(clean_ec_event_status); + +unsigned char ec_read(unsigned char index) +{ + unsigned char value = 0; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&index_access_lock, flags); + ret = send_ec_command(CMD_READ_EC); + if (ret < 0) { + printk(KERN_ERR "Send command fail!\n"); + value = 0; + goto out; + } + ret = send_ec_data(index); + if (ret < 0) { + printk(KERN_ERR "Send data fail!\n"); + value = 0; + goto out; + } + value = recv_ec_data(); +out: + spin_unlock_irqrestore(&index_access_lock, flags); + + return value; +} +EXPORT_SYMBOL(ec_read); + +unsigned char ec_read_all(unsigned char command, unsigned char index) +{ + unsigned char value = 0; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&index_access_lock, flags); + ret = send_ec_command(command); + if (ret < 0) { + printk(KERN_ERR "Send command fail!\n"); + goto out; + } + ret = send_ec_data(index); + if (ret < 0) { + printk(KERN_ERR "Send data fail!\n"); + goto out; + } + value = recv_ec_data(); +out: + spin_unlock_irqrestore(&index_access_lock, flags); + + return value; +} +EXPORT_SYMBOL(ec_read_all); + +unsigned char ec_read_noindex(unsigned char command) +{ + unsigned char value = 0; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&index_access_lock, flags); + ret = send_ec_command(command); + if (ret < 0) { + printk(KERN_ERR "Send command fail!\n"); + goto out; + } + value = recv_ec_data(); +out: + spin_unlock_irqrestore(&index_access_lock, flags); + + return value; +} +EXPORT_SYMBOL(ec_read_noindex); + +int ec_write(unsigned char index, unsigned char data) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&index_access_lock, flags); + ret = send_ec_command(CMD_WRITE_EC); + if (ret < 0) { + printk(KERN_ERR "Send command 0x81 fail!\n"); + goto out; + } + ret = send_ec_data(index); + if (ret < 0) { + printk(KERN_ERR "Send index 0x%x fail!\n", index); + goto out; + } + + ret = send_ec_data(data); + if (ret < 0) { + printk(KERN_ERR "Send data 0x%x fail!\n", data); + } +out: + spin_unlock_irqrestore(&index_access_lock, flags); + + return ret; +} +EXPORT_SYMBOL(ec_write); + +int ec_write_all(unsigned char command, unsigned char index, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&index_access_lock, flags); + send_ec_command(command); + send_ec_data(index); + send_ec_data(data); + spin_unlock_irqrestore(&index_access_lock, flags); + + return 0; +} +EXPORT_SYMBOL(ec_write_all); + +int ec_write_noindex(unsigned char command, unsigned char data) +{ + unsigned long flags; + + spin_lock_irqsave(&index_access_lock, flags); + send_ec_command(command); + send_ec_data(data); + spin_unlock_irqrestore(&index_access_lock, flags); + + return 0; +} +EXPORT_SYMBOL(ec_write_noindex); + +int ec_query_get_event_num(void) +{ + unsigned char value = 0; + unsigned long flags; + int ret = 0; + unsigned int timeout; + + spin_lock_irqsave(&index_access_lock, flags); + ret = send_ec_command(CMD_GET_EVENT_NUM); + if (ret < 0) { + printk(KERN_ERR "Send command fail!\n"); + goto out; + } + + /* check if the command is received by ec */ + timeout = EC_CMD_TIMEOUT; + while ((inb(EC_STS_PORT) & EC_IBF) && timeout--) + ; + if (timeout <= 0) { + printk(KERN_ERR "EC QUERY SEQ: deadable error : timeout...\n"); + ret = -EINVAL; + goto out; + } + + value = recv_ec_data(); +out: + spin_unlock_irqrestore(&index_access_lock, flags); + + return value; +} +EXPORT_SYMBOL(ec_query_get_event_num); diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 29340f8095bb2..7db53c74529c6 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -142,7 +142,7 @@ static struct fwnode_handle *i8042_kbd_fwnode; * i8042_lock protects serialization between i8042_command and * the interrupt handler. */ -static DEFINE_SPINLOCK(i8042_lock); +DEFINE_SPINLOCK(i8042_lock); /* * Writers to AUX and KBD ports as well as users issuing i8042_command From bf138f5223f7e469ac9123ea9d030dde61c42440 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 15:00:14 +0800 Subject: [PATCH 019/130] AOSCOS: MIPS: ec_wpce775l: add a missing prototype for ec_query_get_event_num() Add missing prototype for ec_query_get_event_num() to suppress a -Werror for "no previous prototype". Fixes: "AOSCOS: MIPS: Loongson 3: Add basic EC operations" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/include/asm/mach-loongson64/ec_wpce775l.h | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h b/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h index 3d2ca3245a3e2..fc60026bc6741 100644 --- a/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h +++ b/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h @@ -374,6 +374,7 @@ extern int ec_write_noindex(unsigned char command, unsigned char data); /* Query sequence of 62/66 port access routine. */ extern int ec_query_seq(unsigned char command); +extern int ec_query_get_event_num(void); extern int ec_get_event_num(void); extern void clean_ec_event_status(void); From 235dd58db9b2eb2554036d4e010b4dad5a65f3e1 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 020/130] AOSCOS: MIPS: Loongson 3: Add platform device drivers Platform device drivers include temperature sensor (EMC1412, TMP75, LM93), fan controllers (SB700/SB710/SB800 chipset, WPCE775L EC) and laptop driver (Hot key, battery, etc.) [Mingocng Bai: Moved the following file per upstream styling updates... arch/mips/loongson64/loongson-3/platform.c => arch/mips/loongson64/platform.c Also resolved merge conflicts in... drivers/platform/mips/Kconfig drivers/platform/mips/Makefile] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- .../asm/mach-loongson64/loongson_hwmon.h | 6 +- arch/mips/loongson64/Makefile | 1 + arch/mips/loongson64/platform.c | 67 + drivers/hwmon/lm93.c | 24 +- drivers/platform/mips/Kconfig | 16 + drivers/platform/mips/Makefile | 3 + drivers/platform/mips/emc1412.c | 454 +++++++ drivers/platform/mips/lemote3a-laptop.c | 1179 +++++++++++++++++ drivers/platform/mips/sbx00_fan.c | 767 +++++++++++ drivers/platform/mips/sd5075.c | 211 +++ drivers/platform/mips/tmp75.c | 24 + drivers/platform/mips/wpce_fan.c | 311 +++++ 12 files changed, 3060 insertions(+), 3 deletions(-) create mode 100644 arch/mips/loongson64/platform.c create mode 100644 drivers/platform/mips/emc1412.c create mode 100644 drivers/platform/mips/lemote3a-laptop.c create mode 100644 drivers/platform/mips/sbx00_fan.c create mode 100644 drivers/platform/mips/sd5075.c create mode 100644 drivers/platform/mips/tmp75.c create mode 100644 drivers/platform/mips/wpce_fan.c diff --git a/arch/mips/include/asm/mach-loongson64/loongson_hwmon.h b/arch/mips/include/asm/mach-loongson64/loongson_hwmon.h index 545f91f2ae16a..c1fa61ad483b9 100644 --- a/arch/mips/include/asm/mach-loongson64/loongson_hwmon.h +++ b/arch/mips/include/asm/mach-loongson64/loongson_hwmon.h @@ -36,7 +36,7 @@ struct temp_range { struct loongson_fan_policy { u8 type; - /* percent only used when type is CONSTANT_SPEED_POLICY */ + /* CONSTANT_SPEED_POLICY: actual perenct, STEP_SPEED_POLICY: maximum percent */ u8 percent; /* period between two check. (Unit: S) */ @@ -53,4 +53,8 @@ struct loongson_fan_policy { struct delayed_work work; }; +extern struct loongson_fan_policy kernel_helper_policy; +extern struct loongson_fan_policy step_speed_policy; +extern struct loongson_fan_policy constant_speed_policy; + #endif /* __LOONGSON_HWMON_H_*/ diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index e22a7e9760e80..cf305b96f1b86 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ clock.o constant_timer.o ec_wpce775l.o \ + platform.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/platform.c b/arch/mips/loongson64/platform.c new file mode 100644 index 0000000000000..a447eab0218d2 --- /dev/null +++ b/arch/mips/loongson64/platform.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2009 Lemote Inc. + * Author: Wu Zhangjin, wuzhangjin@gmail.com + * Xiang Yu, xiangy@lemote.com + * Chen Huacai, chenhc@lemote.com + */ + +#include +#include + +/* + * Kernel helper policy + * + * Fan is controlled by EC in laptop pruducts, but EC can not get the current + * cpu temperature which used for adjusting the current fan speed. + * + * So, kernel read the CPU temperature and notify it to EC per second, + * that's all! + */ +struct loongson_fan_policy kernel_helper_policy = { + .type = KERNEL_HELPER_POLICY, + .adjust_period = 1, + .depend_temp = loongson3_cpu_temp, +}; + +/* + * Policy at step mode + * + * up_step array | down_step array + * | + * [min, 50), 50% | (min, 45), 50% + * [50, 60), 60% | [45, 55), 60% + * [60, 70), 70% | [55, 65), 70% + * [70, 80), 80% | [65, 75), 80% + * [80, max), 100% | [75, max), 100% + * + */ +struct loongson_fan_policy step_speed_policy = { + .type = STEP_SPEED_POLICY, + .adjust_period = 1, + .depend_temp = loongson3_cpu_temp, + .up_step_num = 5, + .down_step_num = 5, + .up_step = { + {MIN_TEMP, 50, 50}, + { 50, 60, 60}, + { 60, 70, 70}, + { 70, 80, 80}, + { 80, MAX_TEMP, 100}, + }, + .down_step = { + {MIN_TEMP, 45, 50}, + { 45, 55, 60}, + { 55, 65, 70}, + { 65, 75, 80}, + { 75, MAX_TEMP, 100}, + }, +}; + +/* + * Constant speed policy + * + */ +struct loongson_fan_policy constant_speed_policy = { + .type = CONSTANT_SPEED_POLICY, +}; diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index 75bca805720e7..f4df8620ee145 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -2497,8 +2497,9 @@ ATTRIBUTE_GROUPS(lm93); static void lm93_init_client(struct i2c_client *client) { - int i; - u8 reg; + int i, nr; + u8 reg, ctl2, ctl4; + struct lm93_data *data = i2c_get_clientdata(client); /* configure VID pin input thresholds */ reg = lm93_read_byte(client, LM93_REG_GPI_VID_CTL); @@ -2529,6 +2530,25 @@ static void lm93_init_client(struct i2c_client *client) reg = lm93_read_byte(client, LM93_REG_CONFIG); lm93_write_byte(client, LM93_REG_CONFIG, reg | 0x01); + mutex_lock(&data->update_lock); + for (nr = 0; nr < 2; nr++) { + /* set manual mode */ + ctl2 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2)); + ctl2 |= 0x01; + lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2), ctl2); + /* set fanspeed 50% */ + ctl2 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2)); + ctl4 = lm93_read_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL4)); + ctl2 = (ctl2 & 0x0f) | LM93_PWM_TO_REG(0x80, (ctl4 & 0x07) ? + LM93_PWM_MAP_LO_FREQ : LM93_PWM_MAP_HI_FREQ) << 4; + /* save user commanded value */ + data->pwm_override[nr] = LM93_PWM_FROM_REG(ctl2 >> 4, + (ctl4 & 0x07) ? LM93_PWM_MAP_LO_FREQ : + LM93_PWM_MAP_HI_FREQ); + lm93_write_byte(client, LM93_REG_PWM_CTL(nr, LM93_PWM_CTL2), ctl2); + } + mutex_unlock(&data->update_lock); + /* spin until ready */ for (i = 0; i < 20; i++) { msleep(10); diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig index fb4ac4b08e891..59b6027eeadf5 100644 --- a/drivers/platform/mips/Kconfig +++ b/drivers/platform/mips/Kconfig @@ -37,4 +37,20 @@ config LS2K_RESET help Loongson-2K1000 Reset Controller driver. +config LEMOTE3A_LAPTOP + tristate "Lemote Loongson-3A Laptop Driver" + depends on LOONGSON_MACH3X + select BACKLIGHT_LCD_SUPPORT + select LCD_CLASS_DEVICE + select BACKLIGHT_CLASS_DEVICE + select POWER_SUPPLY + select HWMON + select VIDEO_OUTPUT_CONTROL + select INPUT_SPARSEKMAP + select INPUT_EVDEV + depends on INPUT + default y + help + Lemote Loongson-3A/2Gq family laptops driver. + endif # MIPS_PLATFORM_DEVICES diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile index 4c71444e453a6..4dcbbcfcf7a1e 100644 --- a/drivers/platform/mips/Makefile +++ b/drivers/platform/mips/Makefile @@ -2,3 +2,6 @@ obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o obj-$(CONFIG_RS780E_ACPI) += rs780e-acpi.o obj-$(CONFIG_LS2K_RESET) += ls2k-reset.o +obj-$(CONFIG_I2C_PIIX4) += emc1412.o sd5075.o tmp75.o +obj-$(CONFIG_CPU_HWMON) += wpce_fan.o sbx00_fan.o +obj-$(CONFIG_LEMOTE3A_LAPTOP) += lemote3a-laptop.o diff --git a/drivers/platform/mips/emc1412.c b/drivers/platform/mips/emc1412.c new file mode 100644 index 0000000000000..02f2284743ed3 --- /dev/null +++ b/drivers/platform/mips/emc1412.c @@ -0,0 +1,454 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_EMC1412_CLIENTS 4 +#define EMC1412_THERMAL_THRESHOLD 90000 + +#define EMC1412_TEMP_EXT_HI_REG 0x01 +#define EMC1412_TEMP_EXT_LO_REG 0x10 +#define EMC1412_TEMP_INT_HI_REG 0x00 +#define EMC1412_TEMP_INT_LO_REG 0x29 +#define EMC1412_THERM_LIMIT_EXT_REG 0x19 +#define EMC1412_THERM_LIMIT_INT_REG 0x20 +#define EMC1412_THERM_LIMIT_HYS_REG 0x21 + +struct i2c_client *emc1412_client[MAX_EMC1412_CLIENTS] = {NULL}; + +static struct device *emc1412_hwmon_dev; + +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t get_emc1412_label(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t get_emc1412_temp(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t get_emc1412_crit(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t set_emc1412_crit(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t get_emc1412_crit_hyst(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t set_emc1412_crit_hyst(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0); + +static struct attribute *emc1412_hwmon_attributes[] = +{ + &sensor_dev_attr_name.dev_attr.attr, + NULL +}; + +/* Hwmon device attribute group */ +static struct attribute_group emc1412_hwmon_attribute_group = +{ + .attrs = emc1412_hwmon_attributes, +}; + +/* Hwmon device get name */ +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "emc1412\n"); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_emc1412_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, get_emc1412_label, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, get_emc1412_crit, set_emc1412_crit, 1); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO | S_IWUSR, get_emc1412_crit_hyst, set_emc1412_crit_hyst, 1); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, get_emc1412_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, get_emc1412_label, NULL, 2); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR, get_emc1412_crit, set_emc1412_crit, 2); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO | S_IWUSR, get_emc1412_crit_hyst, set_emc1412_crit_hyst, 2); + +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, get_emc1412_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, get_emc1412_label, NULL, 3); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IRUGO | S_IWUSR, get_emc1412_crit, set_emc1412_crit, 3); +static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO | S_IWUSR, get_emc1412_crit_hyst, set_emc1412_crit_hyst, 3); + +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, get_emc1412_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, get_emc1412_label, NULL, 4); +static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO | S_IWUSR, get_emc1412_crit, set_emc1412_crit, 4); +static SENSOR_DEVICE_ATTR(temp4_crit_hyst, S_IRUGO | S_IWUSR, get_emc1412_crit_hyst, set_emc1412_crit_hyst, 4); + +static const struct attribute *emc1412_hwmon_temp[4][5] = { + { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + NULL + }, + + { + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + NULL + }, + + { + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, + NULL + }, + + { + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_crit_hyst.dev_attr.attr, + NULL + } +}; + +static int emc1412_probe(struct platform_device *dev) +{ + struct i2c_adapter *adapter = NULL; + struct i2c_board_info info; + int i = 0, r = 0, found = 0, id = dev->id - 1; + struct sensor_device *sdev = (struct sensor_device *)dev->dev.platform_data; + + memset(&info, 0, sizeof(struct i2c_board_info)); + + adapter = i2c_get_adapter(i++); + while (adapter) { + if (strncmp(adapter->name, "SMBus PIIX4", 11) == 0) { + found = 1; + break; + } + + adapter = i2c_get_adapter(i++); + } + + if (!found) + goto fail; + + info.addr = sdev->base_addr; + info.platform_data = dev->dev.platform_data; + strncpy(info.type, "emc1412", I2C_NAME_SIZE); + emc1412_client[id] = i2c_new_device(adapter, &info); + if (emc1412_client[id] == NULL) { + printk(KERN_ERR "failed to attach EMC1412 sensor\n"); + goto fail; + } + + r = sysfs_create_files(&emc1412_hwmon_dev->kobj, emc1412_hwmon_temp[id]); + if (r) + goto fail; + + i2c_smbus_write_byte_data(emc1412_client[id], EMC1412_THERM_LIMIT_EXT_REG, (EMC1412_THERMAL_THRESHOLD / 1000 + 10)); + i2c_smbus_write_byte_data(emc1412_client[id], EMC1412_THERM_LIMIT_INT_REG, (EMC1412_THERMAL_THRESHOLD / 1000 + 10)); + + printk(KERN_INFO "Success to attach EMC1412 sensor\n"); + + return 0; + +fail: + printk(KERN_ERR "Fail to found smbus controller attach EMC1412 sensor\n"); + + return r; +} + +static void emc1412_shutdown(struct platform_device *dev) +{ + int id = dev->id - 1; + + emc1412_client[id] = NULL; + msleep(15); /* Release I2C/SMBus resources */ +} + +/* + * emc1412 provide 2 temprature data + * Internal temprature: reg0.reg29 + * External temprature: reg1.reg10 + * reg0 & reg1 from 0 to 127 + * reg1 & reg10 between (0.125, 0.875) + * to avoid use float, temprature will mult 1000 + */ +int emc1412_internal_temp(int id) +{ + u8 reg; + int temp; + struct i2c_client *client; + + if (id < 0 || !(client = emc1412_client[id])) + return NOT_VALID_TEMP; + + reg = i2c_smbus_read_byte_data(client, EMC1412_TEMP_INT_LO_REG); + temp = i2c_smbus_read_byte_data(client, EMC1412_TEMP_INT_HI_REG) * 1000; + temp += (reg >> 5) * 125; + + return temp; +} + +int emc1412_external_temp(int id) +{ + u8 reg; + int temp; + struct i2c_client *client; + + /* not ready ??? */ + if (id < 0 || !(client = emc1412_client[id])) + return NOT_VALID_TEMP; + + reg = i2c_smbus_read_byte_data(client, EMC1412_TEMP_EXT_LO_REG); + temp = i2c_smbus_read_byte_data(client, EMC1412_TEMP_EXT_HI_REG) * 1000; + temp += (reg >> 5) * 125; + + return temp; +} + +static ssize_t get_emc1412_label(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + struct sensor_device *sdev = (struct sensor_device *)emc1412_client[id]->dev.platform_data; + + return sprintf(buf, "%s\n", sdev->label); +} + +static ssize_t get_emc1412_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + int value = emc1412_external_temp(id); + + return sprintf(buf, "%d\n", value); +} + +static ssize_t get_emc1412_crit(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + int ext_limit, int_limit, ave_limit; + int id = (to_sensor_dev_attr(attr))->index - 1; + + if (id < 0 || !(client = emc1412_client[id])) + return NOT_VALID_TEMP; + + ext_limit = i2c_smbus_read_byte_data(client, EMC1412_THERM_LIMIT_EXT_REG); + int_limit = i2c_smbus_read_byte_data(client, EMC1412_THERM_LIMIT_INT_REG); + ave_limit = (ext_limit + int_limit) / 2; + + return sprintf(buf, "%d\n", (ave_limit * 1000)); +} + +static ssize_t set_emc1412_crit(struct device *dev, + struct device_attribute *attr, const char *buf ,size_t count) +{ + unsigned long set_limit; + struct i2c_client *client; + int id = (to_sensor_dev_attr(attr))->index - 1; + + if (id < 0 || !(client = emc1412_client[id])) + return NOT_VALID_TEMP; + + if (kstrtoul(buf, 10, &set_limit)) + return -EINVAL; + + set_limit = clamp_val(set_limit, 0, 125000); + set_limit /= 1000; + i2c_smbus_write_byte_data(client, EMC1412_THERM_LIMIT_EXT_REG, set_limit); + i2c_smbus_write_byte_data(client, EMC1412_THERM_LIMIT_INT_REG, set_limit); + + return count; +} + +static ssize_t get_emc1412_crit_hyst(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client; + int ext_limit, int_limit, ave_limit, hyst; + int id = (to_sensor_dev_attr(attr))->index - 1; + + if (id < 0 || !(client = emc1412_client[id])) + return NOT_VALID_TEMP; + + ext_limit = i2c_smbus_read_byte_data(client, EMC1412_THERM_LIMIT_EXT_REG); + int_limit = i2c_smbus_read_byte_data(client, EMC1412_THERM_LIMIT_INT_REG); + ave_limit = (ext_limit + int_limit) / 2; + + hyst = i2c_smbus_read_byte_data(client, EMC1412_THERM_LIMIT_HYS_REG); + + return sprintf(buf, "%d\n", (ave_limit - hyst) * 1000); +} + +static ssize_t set_emc1412_crit_hyst(struct device *dev, + struct device_attribute *attr, const char *buf ,size_t count) +{ + unsigned long set_limit; + int ext_limit, int_limit, ave_limit, hyst; + struct i2c_client *client; + int id = (to_sensor_dev_attr(attr))->index - 1; + + if (id < 0 || !(client = emc1412_client[id])) + return NOT_VALID_TEMP; + + if (kstrtoul(buf, 10, &set_limit)) + return -EINVAL; + + ext_limit = i2c_smbus_read_byte_data(client, EMC1412_THERM_LIMIT_EXT_REG); + int_limit = i2c_smbus_read_byte_data(client, EMC1412_THERM_LIMIT_INT_REG); + ave_limit = (ext_limit + int_limit) / 2; + set_limit = clamp_val(set_limit, 0, 125000); + set_limit /= 1000; + hyst = ave_limit - set_limit; + + i2c_smbus_write_byte_data(client, EMC1412_THERM_LIMIT_HYS_REG, hyst); + + return count; +} + +static struct delayed_work thermal_work; + +static void do_thermal_timer(struct work_struct *work) +{ + int i, value, temp_max = 0; + + for (i=0; i temp_max) + temp_max = value; + } + + if (temp_max <= EMC1412_THERMAL_THRESHOLD) + schedule_delayed_work(&thermal_work, msecs_to_jiffies(5000)); + else { + pr_emerg("Power off due to high EMC1412 temp: %d\n", temp_max); + orderly_poweroff(true); + } +} + +int fixup_cpu_temp(int cpu, int cputemp) +{ + static int printed[MAX_PACKAGES] = {0, 0, 0, 0}; + int i, value, temp_min = 50000, temp_max = -20000; + + for (i=0; i temp_max) + temp_max = value; + } + for (i=0; i temp_max) + temp_max = value; + } + + if (temp_min > temp_max) { + printk_once("EMC1412: No valid reference.\n"); + return cputemp; + } + if (cputemp < 0 && temp_max < 2000) { + printk_once("EMC1412: No valid reference.\n"); + return cputemp; + } + + if (cputemp < temp_min - 5000) { + if(!printed[cpu]) { + printed[cpu] = 1; + printk("EMC1412: Original CPU#%d temperature too low, " + "fixup with reference: (%d -> %d).\n", + cpu, cputemp, temp_min - 5000); + } + return temp_min - 5000; + } + if (cputemp > temp_max + 15000) { + if(!printed[cpu]) { + printed[cpu] = 1; + printk("EMC1412: Original CPU#%d temperature too high, " + "fixup with reference: (%d -> %d).\n", + cpu, cputemp, temp_max + 10000); + } + return temp_max + 15000; + } + if(!printed[cpu]) { + printed[cpu] = 1; + printk("EMC1412: Original CPU#%d temperature is OK: (%d:%d:%d).\n", + cpu, cputemp, temp_min, temp_max); + } + + return cputemp; +} + +static struct platform_driver emc1412_driver = { + .probe = emc1412_probe, + .shutdown = emc1412_shutdown, + .driver = { + .name = "emc1412", + .owner = THIS_MODULE, + }, +}; + +static int __init emc1412_init(void) +{ + int ret; + + emc1412_hwmon_dev = hwmon_device_register(NULL); + if (IS_ERR(emc1412_hwmon_dev)) { + ret = -ENOMEM; + printk(KERN_ERR "hwmon_device_register fail!\n"); + goto fail_hwmon_device_register; + } + + ret = sysfs_create_group(&emc1412_hwmon_dev->kobj, + &emc1412_hwmon_attribute_group); + if (ret) { + printk(KERN_ERR "fail to create loongson hwmon!\n"); + goto fail_sysfs_create_group_hwmon; + } + + platform_driver_register(&emc1412_driver); + INIT_DEFERRABLE_WORK(&thermal_work, do_thermal_timer); + schedule_delayed_work(&thermal_work, msecs_to_jiffies(20000)); + + return 0; + +fail_sysfs_create_group_hwmon: + hwmon_device_unregister(emc1412_hwmon_dev); + +fail_hwmon_device_register: + return ret; +} + +static void __exit emc1412_exit(void) +{ + cancel_delayed_work_sync(&thermal_work); + platform_driver_unregister(&emc1412_driver); + sysfs_remove_group(&emc1412_hwmon_dev->kobj, + &emc1412_hwmon_attribute_group); + hwmon_device_unregister(emc1412_hwmon_dev); +} + +late_initcall(emc1412_init); +module_exit(emc1412_exit); + +MODULE_AUTHOR("Yu Xiang "); +MODULE_AUTHOR("Huacai Chen "); +MODULE_DESCRIPTION("EMC1412 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/mips/lemote3a-laptop.c b/drivers/platform/mips/lemote3a-laptop.c new file mode 100644 index 0000000000000..b06e7b6ae97c7 --- /dev/null +++ b/drivers/platform/mips/lemote3a-laptop.c @@ -0,0 +1,1179 @@ +/* + * Driver for Lemote Loongson-3A/2Gq Laptops with WPCE775l Embeded Controller + * + * Copyright (C) 2011 Lemote Inc. + * Author : Huang Wei + * : Wang Rui + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define KEY_TOUCHPAD_SW KEY_F21 +#define KEY_MODEM KEY_F24 + +/* Backlight */ +#define MAX_BRIGHTNESS 9 + +/* Power supply */ +#define BIT_BAT_POWER_ACIN (1 << 0) +enum +{ + APM_AC_OFFLINE = 0, + APM_AC_ONLINE, + APM_AC_BACKUP, + APM_AC_UNKNOWN = 0xff +}; +enum +{ + APM_BAT_STATUS_HIGH = 0, + APM_BAT_STATUS_LOW, + APM_BAT_STATUS_CRITICAL, + APM_BAT_STATUS_CHARGING, + APM_BAT_STATUS_NOT_PRESENT, + APM_BAT_STATUS_UNKNOWN = 0xff +}; + +enum /* bat_reg_flag */ +{ + BAT_REG_TEMP_FLAG = 1, + BAT_REG_VOLTAGE_FLAG, + BAT_REG_CURRENT_FLAG, + BAT_REG_AC_FLAG, + BAT_REG_RC_FLAG, + BAT_REG_FCC_FLAG, + BAT_REG_ATTE_FLAG, + BAT_REG_ATTF_FLAG, + BAT_REG_RSOC_FLAG, + BAT_REG_CYCLCNT_FLAG +}; + +/* Power info cached timeout */ +#define POWER_INFO_CACHED_TIMEOUT 100 /* jiffies */ + +/* SCI device */ +#define EC_SCI_DEV "sci" /* < 10 bytes. */ +#define SCI_IRQ_NUM 0x07 +#define GPIO_SIZE 256 + +const char *version = EC_VERSION; + +/* Power information structure */ +struct lemote3a_power_info +{ + /* AC insert or not */ + unsigned int ac_in; + /* Battery insert or not */ + unsigned int bat_in; + unsigned int health; + + /* Battery designed capacity */ + unsigned int design_capacity; + /* Battery designed voltage */ + unsigned int design_voltage; + /* Battery capacity after full charged */ + unsigned int full_charged_capacity; + /* Battery Manufacture Date */ + unsigned char manufacture_date[11]; + /* Battery Serial number */ + unsigned char serial_number[8]; + /* Battery Manufacturer Name, max 11 + 1(length) bytes */ + unsigned char manufacturer_name[12]; + /* Battery Device Name, max 7 + 1(length) bytes */ + unsigned char device_name[8]; + /* Battery Technology */ + unsigned int technology; + /* Battery cell count */ + unsigned char cell_count; + + /* Battery dynamic charge/discharge voltage */ + unsigned int voltage_now; + /* Battery dynamic charge/discharge average current */ + int current_now; + int current_sign; + int current_average; + /* Battery current remaining capacity */ + unsigned int remain_capacity; + /* Battery current remaining capacity percent */ + unsigned int remain_capacity_percent; + /* Battery current temperature */ + unsigned int temperature; + /* Battery current remaining time (AverageTimeToEmpty) */ + unsigned int remain_time; + /* Battery current full charging time (averageTimeToFull) */ + unsigned int fullchg_time; + /* Battery Status */ + unsigned int charge_status; + /* Battery current cycle count (CycleCount) */ + unsigned int cycle_count; +}; + +/* SCI device structure */ +struct sci_device +{ + /* The sci number get from ec */ + unsigned char number; + /* Sci count */ + unsigned char parameter; + /* Irq relative */ + unsigned char irq; + unsigned char irq_data; + /* Device name */ + unsigned char name[10]; +}; +/* SCI device event structure */ +struct sci_event +{ + int index; + sci_handler handler; +}; + +/* Platform driver init handler */ +static int __init lemote3a_laptop_init(void); +/* Platform driver exit handler */ +static void __exit lemote3a_laptop_exit(void); +/* Platform device suspend handler */ +static int lemote3a_laptop_suspend(struct platform_device * pdev, pm_message_t state); +/* Platform device resume handler */ +static int lemote3a_laptop_resume(struct platform_device * pdev); + +static ssize_t version_show(struct device_driver * driver, char * buf); + +/* Camera control misc device open handler */ +static int lemote3a_cam_misc_open(struct inode * inode, struct file * filp); +/* Camera control misc device release handler */ +static int lemote3a_cam_misc_release(struct inode * inode, struct file * filp); +/* Camera control misc device read handler */ +ssize_t lemote3a_cam_misc_read(struct file * filp, + char __user * buffer, size_t size, loff_t * offset); +/* Camera control misc device write handler */ +static ssize_t lemote3a_cam_misc_write(struct file * filp, + const char __user * buffer, size_t size, loff_t * offset); + +/* Backlight device set brightness handler */ +static int lemote3a_set_brightness(struct backlight_device * pdev); +/* Backlight device get brightness handler */ +static int lemote3a_get_brightness(struct backlight_device * pdev); + +/* >>>Power management operation */ +/* Update battery information handle function. */ +static void lemote3a_power_battery_info_update(unsigned char bat_reg_flag); +/* Clear battery static information. */ +static void lemote3a_power_info_battery_static_clear(void); +/* Get battery static information. */ +static void lemote3a_power_info_battery_static_update(void); +/* Update power_status value */ +static void lemote3a_power_info_power_status_update(void); +static void lemote3a_bat_get_string(unsigned char index, unsigned char *bat_string); +/* Power supply Battery get property handler */ +static int lemote3a_bat_get_property(struct power_supply * pws, + enum power_supply_property psp, union power_supply_propval * val); +/* Power supply AC get property handler */ +static int lemote3a_ac_get_property(struct power_supply * pws, + enum power_supply_property psp, union power_supply_propval * val); +/* <<props.max_brightness = ec_read(INDEX_DISPLAY_MAXBRIGHTNESS_LEVEL); + lemote3a_backlight_dev->props.brightness = ec_read(INDEX_DISPLAY_BRIGHTNESS); + backlight_update_status(lemote3a_backlight_dev); + /* Register backlight END */ + + /* Register power supply START */ + power_info = kzalloc(sizeof(struct lemote3a_power_info), GFP_KERNEL); + if (!power_info) { + printk(KERN_ERR "Lemote Laptop Platform Driver: Alloc memory for power_info failed!\n"); + ret = -ENOMEM; + goto fail_power_info_alloc; + } + + lemote3a_power_info_power_status_update(); + if (power_info->bat_in) { + /* Get battery static information. */ + lemote3a_power_info_battery_static_update(); + } + else { + printk(KERN_ERR "Lemote Laptop Platform Driver: The battery does not exist!!\n"); + } + lemote3a_bat = power_supply_register(NULL, &lemote3a_bat_desc, NULL); + if (IS_ERR(lemote3a_bat)) { + ret = -ENOMEM; + goto fail_bat_power_supply_register; + } + + lemote3a_ac = power_supply_register(NULL, &lemote3a_ac_desc, NULL); + if (IS_ERR(lemote3a_ac)) { + ret = -ENOMEM; + goto fail_ac_power_supply_register; + } + /* Register power supply END */ + + /* Hotkey device START */ + ret = lemote3a_hotkey_init(); + if (ret) { + printk(KERN_ERR "Lemote Laptop Platform Driver : Fail to register hotkey device.\n"); + goto fail_hotkey_init; + } + /* Hotkey device END */ + + /* SCI PCI Driver Init START */ + ret = sci_pci_driver_init(); + if (ret) { + printk(KERN_ERR "LS3ANB Driver : Fail to register sci pci driver.\n"); + goto fail_sci_pci_driver_init; + } + /* SCI PCI Driver Init END */ + + /* Camera control misc Device START */ + ret = misc_register(&lemote3a_cam_misc_dev); + if (ret) { + printk(KERN_ERR "Lemote Laptop Platform Driver : Fail to register camera control misc device.\n"); + goto fail_misc_register; + } + /* Camera control misc Device END */ + + /* Request control for backlight device START */ + ec_write(INDEX_BACKLIGHT_CTRLMODE, BACKLIGHT_CTRL_BYHOST); + /* Request control for backlight device END */ + + return 0; + +fail_misc_register: + sci_pci_driver_exit(); +fail_sci_pci_driver_init: + lemote3a_hotkey_exit(); +fail_hotkey_init: + power_supply_unregister(lemote3a_ac); +fail_ac_power_supply_register: + power_supply_unregister(lemote3a_bat); +fail_bat_power_supply_register: + kfree(power_info); +fail_power_info_alloc: + backlight_device_unregister(lemote3a_backlight_dev); +fail_backlight_device_register: + platform_driver_unregister(&platform_driver); + + return ret; +} + +/* Platform driver init handler */ +static int __init lemote3a_laptop_init(void) +{ + int ret; + + printk(KERN_INFO "Lemote Laptop Platform Driver version V%s\n", version); + + ret = platform_driver_register(&platform_driver); + if(ret) { + printk(KERN_ERR "Lemote Laptop Platform Driver: Fail to register lemote laptop platform driver.\n"); + return ret; + } + ret = driver_create_file(&platform_driver.driver, &driver_attr_version); + + return ret; +} + +/* Platform driver exit handler */ +static void __exit lemote3a_laptop_exit(void) +{ + free_irq(lemote3a_sci_device->irq, lemote3a_sci_device); + + /* Return control for backlight device START */ + ec_write(INDEX_BACKLIGHT_CTRLMODE, BACKLIGHT_CTRL_BYEC); + /* Return control for backlight device END */ + + /* Camera control misc device */ + misc_deregister(&lemote3a_cam_misc_dev); + + /* Hotkey & SCI device */ + sci_pci_driver_exit(); + lemote3a_hotkey_exit(); + + /* Power supply */ + power_supply_unregister(lemote3a_ac); + power_supply_unregister(lemote3a_bat); + kfree(power_info); + + /* Backlight */ + backlight_device_unregister(lemote3a_backlight_dev); + + /* Platform device & driver */ + platform_driver_unregister(&platform_driver); + + printk(KERN_INFO "Lemote Laptop Platform Driver : Unload Platform Specific Driver.\n"); +} + +#ifdef CONFIG_PM +/* Platform device suspend handler */ +static int lemote3a_laptop_suspend(struct platform_device * pdev, pm_message_t state) +{ + struct pci_dev *dev; + + dev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); + pci_disable_device(dev); + + return 0; +} + +/* Platform device resume handler */ +static int lemote3a_laptop_resume(struct platform_device * pdev) +{ + struct pci_dev *dev; + + dev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); + pci_enable_device(dev); + + /* Process LID event */ + lemote3a_sci_event_handler(SCI_EVENT_NUM_LID); + + /* + * Clear sci status: GPM9Status field in bit14 of + * EVENT_STATUS register for SB710, write 1 to clear + * + * Clear all SCI events when suspend + */ + clean_ec_event_status(); + + return 0; +} +#else +static int lemote3a_laptop_suspend(struct platform_device * pdev, pm_message_t state) +{ + return 0; +} + +static int lemote3a_laptop_resume(struct platform_device * pdev) +{ + return 0; +} +#endif /* CONFIG_PM */ + +static ssize_t version_show(struct device_driver * driver, char * buf) +{ + return sprintf(buf, "%s\n", version); +} + +/* Camera control misc device open handler */ +static int lemote3a_cam_misc_open(struct inode * inode, struct file * filp) +{ + return 0; +} + +/* Camera control misc device release handler */ +static int lemote3a_cam_misc_release(struct inode * inode, struct file * filp) +{ + return 0; +} + +/* Camera control misc device read handler */ +ssize_t lemote3a_cam_misc_read(struct file * filp, + char __user * buffer, size_t size, loff_t * offset) +{ + int ret = 0; + + if (0 != *offset) + return 0; + + ret = ec_read(INDEX_CAM_STSCTRL); + ret = sprintf(buffer, "%d\n", ret); + *offset = ret; + + return ret; +} + +/* Camera control misc device write handler */ +static ssize_t lemote3a_cam_misc_write(struct file * filp, + const char __user * buffer, size_t size, loff_t * offset) +{ + if (0 >= size) + return -EINVAL; + + if ('0' == buffer[0]) + ec_write(INDEX_CAM_STSCTRL, CAM_STSCTRL_OFF); + else + ec_write(INDEX_CAM_STSCTRL, CAM_STSCTRL_ON); + + return size; +} + +/* Backlight device set brightness handler */ +static int lemote3a_set_brightness(struct backlight_device * pdev) +{ + unsigned int level = 0; + + level = ((FB_BLANK_UNBLANK==pdev->props.fb_blank) && + (FB_BLANK_UNBLANK==pdev->props.power)) ? + pdev->props.brightness : 0; + + if (MAX_BRIGHTNESS < level) { + level = MAX_BRIGHTNESS; + } + else if (level < 0) { + level = 0; + } + + ec_write(INDEX_DISPLAY_BRIGHTNESS, level); + + return 0; +} + +/* Backlight device get brightness handler */ +static int lemote3a_get_brightness(struct backlight_device * pdev) +{ + /* Read level from ec */ + return ec_read(INDEX_DISPLAY_BRIGHTNESS); +} + +/* Update battery information handle function. */ +static void lemote3a_power_battery_info_update(unsigned char bat_reg_flag) +{ + short bat_info_value = 0; + + switch (bat_reg_flag) { + /* Update power_info->temperature value */ + case BAT_REG_TEMP_FLAG: + lemote3a_power_info_power_status_update(); + bat_info_value = (ec_read(INDEX_BATTERY_TEMP_HIGH) << 8) | ec_read(INDEX_BATTERY_TEMP_LOW); + power_info->temperature = (power_info->bat_in) ? (bat_info_value / 10 - 273) : 0; + break; + /* Update power_info->voltage value */ + case BAT_REG_VOLTAGE_FLAG: + lemote3a_power_info_power_status_update(); + bat_info_value = (ec_read(INDEX_BATTERY_VOL_HIGH) << 8) | ec_read(INDEX_BATTERY_VOL_LOW); + power_info->voltage_now = (power_info->bat_in) ? bat_info_value : 0; + break; + /* Update power_info->current_now value */ + case BAT_REG_CURRENT_FLAG: + lemote3a_power_info_power_status_update(); + bat_info_value = (ec_read(INDEX_BATTERY_CURRENT_HIGH) << 8) | ec_read(INDEX_BATTERY_CURRENT_LOW); + power_info->current_now = (power_info->bat_in) ? bat_info_value : 0; + break; + /* Update power_info->current_avg value */ + case BAT_REG_AC_FLAG: + lemote3a_power_info_power_status_update(); + bat_info_value = (ec_read(INDEX_BATTERY_AC_HIGH) << 8) | ec_read(INDEX_BATTERY_AC_LOW); + power_info->current_average = (power_info->bat_in) ? bat_info_value : 0; + break; + /* Update power_info->remain_capacity value */ + case BAT_REG_RC_FLAG: + power_info->remain_capacity = (ec_read(INDEX_BATTERY_RC_HIGH) << 8) | ec_read(INDEX_BATTERY_RC_LOW); + break; + /* Update power_info->full_charged_capacity value */ + case BAT_REG_FCC_FLAG: + power_info->full_charged_capacity = (ec_read(INDEX_BATTERY_FCC_HIGH) << 8) | ec_read(INDEX_BATTERY_FCC_LOW); + break; + /* Update power_info->remain_time value */ + case BAT_REG_ATTE_FLAG: + power_info->remain_time = (ec_read(INDEX_BATTERY_ATTE_HIGH) << 8) | ec_read(INDEX_BATTERY_ATTE_LOW); + break; + /* Update power_info->fullchg_time value */ + case BAT_REG_ATTF_FLAG: + power_info->fullchg_time = (ec_read(INDEX_BATTERY_ATTF_HIGH) << 8) | ec_read(INDEX_BATTERY_ATTF_LOW); + break; + /* Update power_info->curr_cap value */ + case BAT_REG_RSOC_FLAG: + power_info->remain_capacity_percent = ec_read(INDEX_BATTERY_CAPACITY); + break; + /* Update power_info->cycle_count value */ + case BAT_REG_CYCLCNT_FLAG: + power_info->cycle_count = (ec_read(INDEX_BATTERY_CYCLECNT_HIGH) << 8) | ec_read(INDEX_BATTERY_CYCLECNT_LOW); + break; + + default: + break; + } +} + +/* Clear battery static information. */ +static void lemote3a_power_info_battery_static_clear(void) +{ + strcpy(power_info->manufacturer_name, "Unknown"); + strcpy(power_info->device_name, "Unknown"); + power_info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + strcpy(power_info->serial_number, "Unknown"); + strcpy(power_info->manufacture_date, "Unknown"); + power_info->cell_count = 0; + power_info->design_capacity = 0; + power_info->design_voltage = 0; +} + +/* Get battery static information. */ +static void lemote3a_power_info_battery_static_update(void) +{ + unsigned int manufacture_date, bat_serial_number; + char device_chemistry[5]; + + manufacture_date = (ec_read(INDEX_BATTERY_MFD_HIGH) << 8) | ec_read(INDEX_BATTERY_MFD_LOW); + sprintf(power_info->manufacture_date, "%d-%d-%d", (manufacture_date >> 9) + 1980, + (manufacture_date & 0x01E0) >> 5, manufacture_date & 0x001F); + lemote3a_bat_get_string(INDEX_BATTERY_MFN_LENG, power_info->manufacturer_name); + lemote3a_bat_get_string(INDEX_BATTERY_DEVNAME_LENG, power_info->device_name); + lemote3a_bat_get_string(INDEX_BATTERY_DEVCHEM_LENG, device_chemistry); + if ((device_chemistry[2] == 'o') || (device_chemistry[2] == 'O')) { + power_info->technology = POWER_SUPPLY_TECHNOLOGY_LION; + } + else if (((device_chemistry[1] = 'h') && (device_chemistry[2] == 'm')) || + ((device_chemistry[1] = 'H') && (device_chemistry[2] == 'M'))) { + power_info->technology = POWER_SUPPLY_TECHNOLOGY_NiMH; + } + else if ((device_chemistry[2] == 'p') || (device_chemistry[2] == 'P')) { + power_info->technology = POWER_SUPPLY_TECHNOLOGY_LIPO; + } + else if ((device_chemistry[2] == 'f') || (device_chemistry[2] == 'F')) { + power_info->technology = POWER_SUPPLY_TECHNOLOGY_LiFe; + } + else if ((device_chemistry[2] == 'c') || (device_chemistry[2] == 'C')) { + power_info->technology = POWER_SUPPLY_TECHNOLOGY_NiCd; + } + else if (((device_chemistry[1] = 'n') && (device_chemistry[2] == 'm')) || + ((device_chemistry[1] = 'N') && (device_chemistry[2] == 'M'))) { + power_info->technology = POWER_SUPPLY_TECHNOLOGY_LiMn; + } + else { + power_info->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; + } + + bat_serial_number = (ec_read(INDEX_BATTERY_SN_HIGH) << 8) | ec_read(INDEX_BATTERY_SN_LOW); + snprintf(power_info->serial_number, 8, "%x", bat_serial_number); + + power_info->cell_count = ((ec_read(INDEX_BATTERY_CV_HIGH) << 8) | ec_read(INDEX_BATTERY_CV_LOW)) / 4200; + + power_info->design_capacity = (ec_read(INDEX_BATTERY_DC_HIGH) << 8) | ec_read(INDEX_BATTERY_DC_LOW); + power_info->design_voltage = (ec_read(INDEX_BATTERY_DV_HIGH) << 8) | ec_read(INDEX_BATTERY_DV_LOW); + power_info->full_charged_capacity = (ec_read(INDEX_BATTERY_FCC_HIGH) << 8) | ec_read(INDEX_BATTERY_FCC_LOW); + printk(KERN_INFO "LS3ANB Battery Information:\nManufacturerName: %s, DeviceName: %s, DeviceChemistry: %s\n", + power_info->manufacturer_name, power_info->device_name, device_chemistry); + printk(KERN_INFO "SerialNumber: %s, ManufactureDate: %s, CellNumber: %d\n", + power_info->serial_number, power_info->manufacture_date, power_info->cell_count); + printk(KERN_INFO "DesignCapacity: %dmAh, DesignVoltage: %dmV, FullChargeCapacity: %dmAh\n", + power_info->design_capacity, power_info->design_voltage, power_info->full_charged_capacity); +} + +/* Update power_status value */ +static void lemote3a_power_info_power_status_update(void) +{ + unsigned int power_status = 0; + + power_status = ec_read(INDEX_POWER_STATUS); + + power_info->ac_in = (power_status & MASK(BIT_POWER_ACPRES)) ? + APM_AC_ONLINE : APM_AC_OFFLINE; + + power_info->bat_in = (power_status & MASK(BIT_POWER_BATPRES)) ? 1 : 0; + if( power_info->bat_in && ((ec_read(INDEX_BATTERY_DC_LOW) | (ec_read(INDEX_BATTERY_DC_HIGH) << 8)) == 0) ) + power_info->bat_in = 0; + + power_info->health = (power_info->bat_in) ? POWER_SUPPLY_HEALTH_GOOD : + POWER_SUPPLY_HEALTH_UNKNOWN; + if (power_status & (MASK(BIT_POWER_BATL) | MASK(BIT_POWER_BATVL))) { + power_info->health = POWER_SUPPLY_HEALTH_DEAD; + } + + if (!power_info->bat_in) { + power_info->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; + } + else { + if (power_status & MASK(BIT_POWER_BATFCHG)) { + power_info->charge_status = POWER_SUPPLY_STATUS_FULL; + } + else if (power_status & MASK(BIT_POWER_BATCHG)) { + power_info->charge_status = POWER_SUPPLY_STATUS_CHARGING; + } + else if (power_status & MASK(BIT_POWER_TERMINATE)) { + power_info->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } + else { + power_info->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; + } + } +} + +/* Get battery static information string */ +static void lemote3a_bat_get_string(unsigned char index, unsigned char *bat_string) +{ + unsigned char length, i; + + length = ec_read(index); + for (i = 0; i < length; i++) { + *bat_string++ = ec_read(++index); + } + *bat_string = '\0'; +} + +/* Power supply Battery get property handler */ +static int lemote3a_bat_get_property(struct power_supply * pws, + enum power_supply_property psp, union power_supply_propval * val) +{ + switch (psp) { + /* Get battery static information. */ + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = power_info->design_voltage * 1000; /* mV -> uV */ + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = power_info->design_capacity * 1000; /* mAh -> uAh */ + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = power_info->device_name; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = power_info->manufacturer_name; + break; + case POWER_SUPPLY_PROP_SERIAL_NUMBER: + val->strval = power_info->serial_number; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = power_info->technology; + break; + + /* Get battery dynamic information. */ + case POWER_SUPPLY_PROP_STATUS: + lemote3a_power_info_power_status_update(); + val->intval = power_info->charge_status; + break; + case POWER_SUPPLY_PROP_PRESENT: + lemote3a_power_info_power_status_update(); + val->intval = power_info->bat_in; + break; + case POWER_SUPPLY_PROP_HEALTH: + lemote3a_power_info_power_status_update(); + val->intval = power_info->health; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + lemote3a_power_battery_info_update(BAT_REG_CURRENT_FLAG); + val->intval = power_info->current_now * 1000; /* mA -> uA */ + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + lemote3a_power_battery_info_update(BAT_REG_AC_FLAG); + val->intval = power_info->current_average * 1000; /* mA -> uA */ + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + lemote3a_power_battery_info_update(BAT_REG_VOLTAGE_FLAG); + val->intval = power_info->voltage_now * 1000; /* mV -> uV */ + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + lemote3a_power_battery_info_update(BAT_REG_RC_FLAG); + val->intval = power_info->remain_capacity * 1000; /* mAh -> uAh */ + break; + case POWER_SUPPLY_PROP_CAPACITY: + lemote3a_power_battery_info_update(BAT_REG_RSOC_FLAG); + val->intval = power_info->remain_capacity_percent; /* Percentage */ + break; + case POWER_SUPPLY_PROP_TEMP: + lemote3a_power_battery_info_update(BAT_REG_TEMP_FLAG); + val->intval = power_info->temperature; /* Celcius */ + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: + lemote3a_power_battery_info_update(BAT_REG_ATTE_FLAG); + if (power_info->remain_time == 0xFFFF) { + power_info->remain_time = 0; + } + val->intval = power_info->remain_time * 60; /* seconds */ + break; + case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: + lemote3a_power_battery_info_update(BAT_REG_ATTF_FLAG); + if (power_info->fullchg_time == 0xFFFF) { + power_info->fullchg_time = 0; + } + val->intval = power_info->fullchg_time * 60; /* seconds */ + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + lemote3a_power_battery_info_update(BAT_REG_FCC_FLAG); + val->intval = power_info->full_charged_capacity * 1000;/* mAh -> uAh */ + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + lemote3a_power_battery_info_update(BAT_REG_CYCLCNT_FLAG); + val->intval = power_info->cycle_count; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Power supply AC get property handler */ +static int lemote3a_ac_get_property(struct power_supply * pws, + enum power_supply_property psp, union power_supply_propval * val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + lemote3a_power_info_power_status_update(); + val->intval = power_info->ac_in; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* SCI device pci driver init handler */ +static int sci_pci_driver_init(void) +{ + int ret; + + ret = sci_pci_init(); + if (ret) { + printk(KERN_ERR "Lemote Laptop Platform Driver : Register pci driver error.\n"); + return ret; + } + + printk(KERN_INFO "Lemote Laptop Platform Driver : SCI event handler on WPCE775L Embedded Controller init.\n"); + + return ret; +} + +/* SCI device pci driver exit handler */ +static void sci_pci_driver_exit(void) +{ + printk(KERN_INFO "Lemote Laptop Platform Driver : SCI event handler on WPCE775L Embedded Controll exit.\n"); +} + +/* SCI device pci driver init */ +static int sci_pci_init(void) +{ + int ret = -EIO; + struct pci_dev *pdev; + + pdev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); + + /* Create the sci device */ + lemote3a_sci_device = kmalloc(sizeof(struct sci_device), GFP_KERNEL); + if (NULL == lemote3a_sci_device) { + printk(KERN_ERR "LS3ANB Drvier : Malloc memory for sci_device failed!\n"); + return -ENOMEM; + } + + /* Fill sci device */ + lemote3a_sci_device->irq = SCI_IRQ_NUM; + lemote3a_sci_device->irq_data = 0x00; + lemote3a_sci_device->number = 0x00; + lemote3a_sci_device->parameter = 0x00; + strcpy(lemote3a_sci_device->name, EC_SCI_DEV); + + /* Enable pci device and get the GPIO resources. */ + ret = pci_enable_device(pdev); + if (ret) { + printk(KERN_ERR "Lemote Laptop Platform Driver : Enable pci device failed!\n"); + ret = -ENODEV; + goto out_pdev; + } + + /* Clear sci status: GPM9Status field in bit14 of + * EVENT_STATUS register for SB710, write 1 to clear */ + clean_ec_event_status(); + + /* Alloc the interrupt for sci not pci */ + ret = request_irq(lemote3a_sci_device->irq, lemote3a_sci_int_routine, + IRQF_SHARED, lemote3a_sci_device->name, lemote3a_sci_device); + if (ret) { + printk(KERN_ERR "Lemote Laptop Platform Driver : Request irq %d failed!\n", lemote3a_sci_device->irq); + ret = -EFAULT; + goto out_irq; + } + + ret = 0; + printk(KERN_DEBUG "Lemote Laptop Platform Driver : PCI Init successful!\n"); + goto out; + +out_irq: + pci_disable_device(pdev); +out_pdev: + kfree(lemote3a_sci_device); +out: + return ret; +} + +/* SCI event routine handler */ +static irqreturn_t lemote3a_sci_int_routine(int irq, void * dev_id) +{ + int event; + + if (lemote3a_sci_device->irq != irq) { + return IRQ_NONE; + } + + event = ec_query_get_event_num(); + if ((SCI_EVENT_NUM_START > event) || (SCI_EVENT_NUM_END < event)) { + goto exit_event_action; + } + + /* Do event action */ + lemote3a_sci_event_handler(event); + + /* Clear sci status: GPM9Status field in bit14 of + * EVENT_STATUS register for SB710, write 1 to clear */ + clean_ec_event_status(); + + return IRQ_HANDLED; + +exit_event_action: + clean_ec_event_status(); + return IRQ_NONE; +} + +/* SCI device event handler */ +void lemote3a_sci_event_handler(int event) +{ + int status = 0; + struct key_entry * ke = NULL; + struct sci_event * sep = NULL; + + sep = (struct sci_event*)&(se[event]); + if (0 != sep->index) { + status = ec_read(sep->index); + } + if (NULL != sep->handler) { + status = sep->handler(status); + } + + ke = sparse_keymap_entry_from_scancode(lemote3a_hotkey_dev, event); + if (ke) { + if (SW_LID == ke->keycode) { + /* report LID event. */ + input_report_switch(lemote3a_hotkey_dev, SW_LID, status); + input_sync(lemote3a_hotkey_dev); + } + else { + sparse_keymap_report_entry(lemote3a_hotkey_dev, ke, 1, true); + } + } +} + +/* SCI device over temperature event handler */ +static int lemote3a_over_temp_handler(int status) +{ + return 0; +} + +/* SCI device Throttling the CPU event handler */ +static int lemote3a_throttling_CPU_handler(int status) +{ + return 0; +} + +/* SCI device AC event handler */ +static int lemote3a_ac_handler(int status) +{ + /* Report status changed */ + power_supply_changed(lemote3a_ac); + + return 0; +} + +/* SCI device Battery event handler */ +static int lemote3a_bat_handler(int status) +{ + /* Battery insert/pull-out to handle battery static information. */ + if (status & MASK(BIT_POWER_BATPRES)) { + /* If battery is insert, get battery static information. */ + lemote3a_power_info_battery_static_update(); + } + else { + /* Else if battery is pull-out, clear battery static information. */ + lemote3a_power_info_battery_static_clear(); + } + /* Report status changed */ + power_supply_changed(lemote3a_bat); + + return 0; +} + +/* SCI device Battery low event handler */ +static int lemote3a_bat_low_handler(int status) +{ + /* Report status changed */ + power_supply_changed(lemote3a_bat); + + return 0; +} + +/* SCI device Battery very low event handler */ +static int lemote3a_bat_very_low_handler(int status) +{ + /* Report status changed */ + power_supply_changed(lemote3a_bat); + + return 0; +} + +/* SCI device LID event handler */ +static int lemote3a_lid_handler(int status) +{ + if (status & BIT(BIT_DEVICE_LID)) { + return 1; + } + + return 0; +} + +/* Hotkey device init handler */ +static int lemote3a_hotkey_init(void) +{ + int ret; + + lemote3a_hotkey_dev = input_allocate_device(); + if (!lemote3a_hotkey_dev) { + return -ENOMEM; + } + + lemote3a_hotkey_dev->name = "Lemote Laptop Hotkeys"; + lemote3a_hotkey_dev->phys = "button/input0"; + lemote3a_hotkey_dev->id.bustype = BUS_HOST; + lemote3a_hotkey_dev->dev.parent = NULL; + + ret = sparse_keymap_setup(lemote3a_hotkey_dev, lemote3a_keymap, NULL); + if (ret) { + printk(KERN_ERR "Lemote Laptop Platform Driver : Fail to setup input device keymap\n"); + input_free_device(lemote3a_hotkey_dev); + return ret; + } + + ret = input_register_device(lemote3a_hotkey_dev); + if (ret) { + input_free_device(lemote3a_hotkey_dev); + return ret; + } + return 0; +} + +/* Hotkey device exit handler */ +static void lemote3a_hotkey_exit(void) +{ + if (lemote3a_hotkey_dev) { + input_unregister_device(lemote3a_hotkey_dev); + lemote3a_hotkey_dev = NULL; + } +} + +module_init(lemote3a_laptop_init); +module_exit(lemote3a_laptop_exit); + +MODULE_ALIAS("platform:wpce775l"); +MODULE_AUTHOR("Huang Wei ; Wang Rui "); +MODULE_DESCRIPTION("Lemote Loongson-3A/2Gq Laptop Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/mips/sbx00_fan.c b/drivers/platform/mips/sbx00_fan.c new file mode 100644 index 0000000000000..6331e09722a1f --- /dev/null +++ b/drivers/platform/mips/sbx00_fan.c @@ -0,0 +1,767 @@ +/* + * SB700/SB710/SB800 series South Bridge provide 4 Fan controllers, + * Loongson products almost use 1/2 of them. + * + * Fan1 & Fan2 usage: + * 1. configure gpio3 as gpio48 to Fanout mode + * 2. configure fan controll mode(depmod on HW design) + * 3. set pwm freqency + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* PM regs */ +#define PM_OPTION_0 0x60 +#define FAN0_EN (1 << 6) +#define FAN1_EN (1 << 2) +#define FAN2_EN (1 << 3) + +/* PM2 regs*/ +#define FAN_CNTR_REG 0 + +#define FAN_MODE0 0 +#define FAN_MODE1 1 +#define FAN_MODE2 2 +#define FAN_MODE3 3 +#define FAN_MODE_MASK 3 +#define FAN0_CNTR_SHIFT 0 +#define FAN1_CNTR_SHIFT 2 +#define FAN2_CNTR_SHIFT 4 +#define FAN3_CNTR_SHIFT 6 + +#define FAN_MISC0_REG 0x01 +#define FAN_MISC1_REG 0x0e +#define FAN_MISC2_REG 0x1b +#define AUTPMODE0 0 +#define LINEARMODE0 1 +#define FAN0POLARITY 2 +#define AUTPMODE1 0 +#define LINEARMODE1 1 +#define FAN1POLARITY 2 + +#define FREQDIV0_REG 0x02 +#define FREQDIV1_REG 0x0f +#define FREQDIV2_REG 0x1c +#define PWM_28KHZ 0 /* 28.64KHz */ +#define PWM_25KHZ 1 /* 25.78KHz */ +#define PWM_23KHZ 2 /* 23.44KHz */ +#define PWM_21KHZ 3 /* 21.48KHz */ +#define PWM_19KHZ 4 /* 19.83KHz */ +#define PWM_18KHZ 5 /* 18.41KHz */ + +#define LOWDUTY0 0x03 +#define LOWDUTY1 0x10 +#define LOWDUTY2 0x1d + +#define FAN0DETCONTROL 0x31 +#define FAN0SPEEDLIMLO 0x32 +#define FAN0SPEEDLIMHI 0x33 +#define FAN0SPEEDLO 0x34 +#define FAN0SPEEDHI 0x35 + +#define FAN1DETCONTROL 0x36 +#define FAN1SPEEDLIMLO 0x37 +#define FAN1SPEEDLIMHI 0x38 +#define FAN1SPEEDLO 0x39 +#define FAN1SPEEDHI 0x3A + +#define FAN2DETCONTROL 0x3B +#define FAN2SPEEDLIMLO 0x3C +#define FAN2SPEEDLIMHI 0x3D +#define FAN2SPEEDLO 0x3E +#define FAN2SPEEDHI 0x3F + +#define FANSTATUS 1 /* bit 0 */ +#define FANTOOSLOW 1 + +#define FANDET_EN 1 /* bit 0 */ +#define USEAVERAGE 2 /* bit 1 */ +#define ShutDown_EN 0x10 /* bit 4 */ + +#define MAX_SBX00_FANS 3 +int sbx00_fan_enable[MAX_SBX00_FANS]; +static enum fan_control_mode sbx00_fan_mode[MAX_SBX00_FANS]; +static struct loongson_fan_policy fan_policy[MAX_SBX00_FANS]; + +/* threshold = SpeedOfPWM(0)/SpeedOfPWM(255) */ +static int speed_percent_threshold[MAX_SBX00_FANS]; + +extern u8 pm_ioread(u8 reg); +extern u8 pm2_ioread(u8 reg); +extern void pm_iowrite(u8 reg, u8 val); +extern void pm2_iowrite(u8 reg, u8 val); + +/* up_temp & down_temp used in fan auto adjust */ +static u8 fan_up_temp[MAX_SBX00_FANS]; +static u8 fan_down_temp[MAX_SBX00_FANS]; +static u8 fan_up_temp_level[MAX_SBX00_FANS]; +static u8 fan_down_temp_level[MAX_SBX00_FANS]; + +static void fan1_adjust(struct work_struct *work); +static void fan2_adjust(struct work_struct *work); +static void fan3_adjust(struct work_struct *work); + +static struct device *sbx00_hwmon_dev; + +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf); +static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0); + +static struct attribute *sbx00_hwmon_attributes[] = +{ + &sensor_dev_attr_name.dev_attr.attr, + NULL +}; + +/* Hwmon device attribute group */ +static struct attribute_group sbx00_hwmon_attribute_group = +{ + .attrs = sbx00_hwmon_attributes, +}; + +/* Hwmon device get name */ +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "sbx00-fan\n"); +} + +/* low-level fucntions */ +static void sbx00_set_fan_level(u8 level, int id) +{ + if (id == 0) + pm2_iowrite(LOWDUTY0, level); + if (id == 1) + pm2_iowrite(LOWDUTY1, level); + if (id == 2) + pm2_iowrite(LOWDUTY2, level); +} + +static void get_up_temp(struct loongson_fan_policy *policy, + u8 current_temp, u8* up_temp, u8* up_temp_level) +{ + int i; + + for (i = 0; i < policy->up_step_num; i++) { + if (current_temp <= policy->up_step[i].low) { + *up_temp = policy->up_step[i].low; + *up_temp_level = i; + return; + } + } + + *up_temp = MAX_TEMP; + *up_temp_level = policy->up_step_num - 1; +} + +static void get_down_temp(struct loongson_fan_policy *policy, u8 current_temp, + u8* down_temp, u8* down_temp_level) +{ + int i; + + for (i = policy->down_step_num-1; i >= 0; i--) { + if (current_temp >= policy->down_step[i].high) { + *down_temp = policy->down_step[i].high; + *down_temp_level = i; + return; + } + } + + *down_temp = MIN_TEMP; + *down_temp_level = 0; +} + +static ssize_t get_fan_level(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t set_fan_level(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t get_fan_mode(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t set_fan_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t get_fan_speed(struct device *dev, + struct device_attribute *attr, char *buf); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, + get_fan_level, set_fan_level, 1); +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + get_fan_mode, set_fan_mode, 1); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan_speed, NULL, 1); + +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, + get_fan_level, set_fan_level, 2); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + get_fan_mode, set_fan_mode, 2); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan_speed, NULL, 2); + +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, + get_fan_level, set_fan_level, 3); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + get_fan_mode, set_fan_mode, 3); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan_speed, NULL, 3); + +static const struct attribute *sbx00_hwmon_fan[3][4] = { + { + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + NULL + } +}; + +static ssize_t get_fan_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 val = 0; + int id = (to_sensor_dev_attr(attr))->index - 1; + + if (id == 0) + val = pm2_ioread(LOWDUTY0); + if (id == 1) + val = pm2_ioread(LOWDUTY1); + if (id == 2) + val = pm2_ioread(LOWDUTY2); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t set_fan_level(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + u8 new_level; + int id = (to_sensor_dev_attr(attr))->index - 1; + + new_level = clamp_val(simple_strtoul(buf, NULL, 10), 0, 255); + if (sbx00_fan_mode[id] == FAN_MANUAL_MODE) { + if (id == 0) + pm2_iowrite(LOWDUTY0, new_level); + if (id == 1) + pm2_iowrite(LOWDUTY1, new_level); + if (id == 2) + pm2_iowrite(LOWDUTY2, new_level); + } + + return count; +} + +static ssize_t get_fan_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + return sprintf(buf, "%d\n", sbx00_fan_mode[id]); +} + +static void sbx00_fan_step_mode(get_temp_fun fun, + struct loongson_fan_policy *policy, int id) +{ + u8 current_temp; + int init_temp_level, target_fan_level, bias; + + current_temp = fun(0) / 1000; + get_up_temp(policy, current_temp, &fan_up_temp[id], + &fan_up_temp_level[id]); + get_down_temp(policy, current_temp, &fan_down_temp[id], + &fan_down_temp_level[id]); + + /* current speed is not sure, setting now */ + init_temp_level = (fan_up_temp_level[id] + fan_down_temp_level[id]) / 2; + target_fan_level = policy->up_step[init_temp_level].level * 255 / 100; + bias = (speed_percent_threshold[id] * 255) / 100; /* bias of pwm(0) */ + bias = bias * (255 - target_fan_level) / 255; /* bias of pwm(target) */ + target_fan_level = (target_fan_level > bias) ? target_fan_level - bias : 0; + target_fan_level = target_fan_level * fan_policy[id].percent / 100; + sbx00_set_fan_level(target_fan_level, id); + + schedule_delayed_work_on(0, &policy->work, policy->adjust_period * HZ); +} + +static void sbx00_fan_start_auto(int id) +{ + u8 level; + + switch (fan_policy[id].type) { + case CONSTANT_SPEED_POLICY: + level = fan_policy[id].percent * 255 / 100; + if (level > MAX_FAN_LEVEL) + level = MAX_FAN_LEVEL; + sbx00_set_fan_level(level, id); + break; + case STEP_SPEED_POLICY: + sbx00_fan_step_mode(fan_policy[id].depend_temp, &fan_policy[id], id); + break; + default: + printk(KERN_ERR "sbx00 fan not support fan policy id %d!\n", fan_policy[id].type); + sbx00_set_fan_level(MAX_FAN_LEVEL, id); + } + + return; +} + +static void sbx00_fan_stop_auto(int id) +{ + if ((fan_policy[id].type == STEP_SPEED_POLICY) && + (sbx00_fan_mode[id] == FAN_AUTO_MODE)) { + cancel_delayed_work(&fan_policy[id].work); + } +} + +static ssize_t set_fan_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + u8 new_mode; + int id = (to_sensor_dev_attr(attr))->index - 1; + + new_mode = clamp_val(simple_strtoul(buf, NULL, 10), + FAN_FULL_MODE, FAN_AUTO_MODE); + if (new_mode == sbx00_fan_mode[id]) + return count; + + switch (new_mode) { + case FAN_FULL_MODE: + sbx00_fan_stop_auto(id); + sbx00_set_fan_level(MAX_FAN_LEVEL, id); + break; + case FAN_MANUAL_MODE: + sbx00_fan_stop_auto(id); + break; + case FAN_AUTO_MODE: + sbx00_fan_start_auto(id); + break; + default: + break; + } + + sbx00_fan_mode[id] = new_mode; + + return count; +} + +static u32 sbx00_get_fan_speed(int id, bool warn) +{ + int speed_lo = 0, speed_hi = 0, count = 1; + + if (id == 0) { + speed_lo = pm2_ioread(FAN0SPEEDLO); + speed_hi = pm2_ioread(FAN0SPEEDHI); + } + if (id == 1) { + speed_lo = pm2_ioread(FAN1SPEEDLO); + speed_hi = pm2_ioread(FAN1SPEEDHI); + } + if (id == 2) { + speed_lo = pm2_ioread(FAN2SPEEDLO); + speed_hi = pm2_ioread(FAN2SPEEDHI); + } + + if (speed_lo & FANTOOSLOW) { + if (warn) + pr_warn("FanSpeed too slow!\n"); + return 0; + } + count = speed_hi << 8 | (speed_lo & ~FANSTATUS); + return 360000 / count; +} + +static ssize_t get_fan_speed(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + u32 val = sbx00_get_fan_speed(id, true); + + return sprintf(buf, "%d\n", val); +} + +static void sbx00_fan1_init(struct work_struct *work) +{ + u8 reg; + int ret; + int speed_low, speed_high; + + /* Configure gpio3 as FANOUT0 */ + reg = pm_ioread(PM_OPTION_0); + pm_iowrite(PM_OPTION_0, reg | FAN0_EN); + + /* setting FANOUT0 mode: + * without temperuature input, controlled by software + */ + reg = pm2_ioread(FAN_CNTR_REG); + reg &= ~(FAN_MODE_MASK << FAN0_CNTR_SHIFT); + reg |= FAN_MODE1 << FAN0_CNTR_SHIFT; + pm2_iowrite(FAN_CNTR_REG, reg); + + reg = pm2_ioread(FAN_MISC0_REG); + reg &= ~(1 << AUTPMODE0); /* Disable auto mode*/ + reg &= ~(1 << LINEARMODE0); /* Use step mode*/ + reg |= (1 << FAN0POLARITY); /* Active High */ + pm2_iowrite(FAN_MISC0_REG, reg); + + /* Set PWM freqency*/ + pm2_iowrite(FREQDIV0_REG, PWM_19KHZ); + + /* Enable Speed Detect */ + pm2_iowrite(FAN0DETCONTROL, FANDET_EN | USEAVERAGE); + pm2_iowrite(FAN0SPEEDLIMLO, 0xFF); /* Set limits register */ + pm2_iowrite(FAN0SPEEDLIMHI, 0xFF); + + sbx00_set_fan_level(255, 0); + msleep(250); + speed_high = sbx00_get_fan_speed(0, false); + sbx00_set_fan_level(0, 0); + msleep(5000); + speed_low = sbx00_get_fan_speed(0, false); + sbx00_set_fan_level(255, 0); + if (speed_high >= speed_low) + speed_percent_threshold[0] = speed_low * 100 / speed_high; + pr_info("SpeedPercentThreshold of FAN1 is %d%%\n", speed_percent_threshold[0]); + + /* force fans in auto mode first */ + work->func = fan1_adjust; + sbx00_fan_mode[0] = FAN_AUTO_MODE; + sbx00_fan_start_auto(0); + + ret = sysfs_create_files(&sbx00_hwmon_dev->kobj, sbx00_hwmon_fan[0]); + if (ret) + printk(KERN_ERR "fail to create sysfs files\n"); +} + +static void fan1_adjust(struct work_struct *work) +{ + u8 current_temp; + int target_fan_level, bias; + + current_temp = fan_policy[0].depend_temp(0) / 1000; + + if ((current_temp <= fan_up_temp[0]) && + (current_temp >= fan_down_temp[0])) + goto exit; + + if (current_temp > fan_up_temp[0]) + target_fan_level = fan_policy[0].up_step[fan_up_temp_level[0]].level * 255 / 100; + + if (current_temp < fan_down_temp[0]) + target_fan_level = fan_policy[0].down_step[fan_down_temp_level[0]].level * 255 / 100; + + bias = (speed_percent_threshold[0] * 255) / 100; + bias = bias * (255 - target_fan_level) / 255; + target_fan_level = (target_fan_level > bias) ? target_fan_level - bias : 0; + target_fan_level = target_fan_level * fan_policy[0].percent / 100; + sbx00_set_fan_level(target_fan_level, 0); + + get_up_temp(&fan_policy[0], current_temp, &fan_up_temp[0], &fan_up_temp_level[0]); + get_down_temp(&fan_policy[0], current_temp, &fan_down_temp[0], &fan_down_temp_level[0]); + +exit: + schedule_delayed_work_on(0, &fan_policy[0].work, fan_policy[0].adjust_period * HZ); +} + +static void sbx00_fan2_init(struct work_struct *work) +{ + u8 reg; + int ret; + int speed_low, speed_high; + + /* Configure gpio48 as FANOUT1 */ + reg = pm_ioread(PM_OPTION_0); + pm_iowrite(PM_OPTION_0, reg | FAN1_EN); + + /* setting FANOUT1 mode: + * without temperuature input, controlled by software + */ + reg = pm2_ioread(FAN_CNTR_REG); + reg &= ~(FAN_MODE_MASK << FAN1_CNTR_SHIFT); + reg |= FAN_MODE1 << FAN1_CNTR_SHIFT; + pm2_iowrite(FAN_CNTR_REG, reg); + + reg = pm2_ioread(FAN_MISC1_REG); + reg &= ~(1 << AUTPMODE1); /* Disable auto mode*/ + reg &= ~(1 << LINEARMODE1); /* Use step mode*/ + reg |= (1 << FAN1POLARITY); /* Active High */ + pm2_iowrite(FAN_MISC1_REG, reg); + + /* Set PWM freqency at 19.83KHz*/ + pm2_iowrite(FREQDIV1_REG, PWM_19KHZ); + + /* Enable Speed Detect */ + pm2_iowrite(FAN1DETCONTROL, FANDET_EN | USEAVERAGE); + pm2_iowrite(FAN1SPEEDLIMLO, 0xFF); /* Set limits register */ + pm2_iowrite(FAN1SPEEDLIMHI, 0xFF); + + sbx00_set_fan_level(255, 1); + msleep(250); + speed_high = sbx00_get_fan_speed(1, false); + sbx00_set_fan_level(0, 1); + msleep(5000); + speed_low = sbx00_get_fan_speed(1, false); + sbx00_set_fan_level(255, 1); + if (speed_high >= speed_low) + speed_percent_threshold[1] = speed_low * 100 / speed_high; + pr_info("SpeedPercentThreshold of FAN2 is %d%%\n", speed_percent_threshold[1]); + + /* force fans in auto mode first */ + work->func = fan2_adjust; + sbx00_fan_mode[1] = FAN_AUTO_MODE; + sbx00_fan_start_auto(1); + + ret = sysfs_create_files(&sbx00_hwmon_dev->kobj, sbx00_hwmon_fan[1]); + if (ret) + printk(KERN_ERR "fail to create sysfs files\n"); +} + +static void fan2_adjust(struct work_struct *work) +{ + u8 current_temp; + int target_fan_level, bias; + + current_temp = fan_policy[1].depend_temp(0) / 1000; + + if ((current_temp <= fan_up_temp[1]) && + (current_temp >= fan_down_temp[1])) + goto exit; + + if (current_temp > fan_up_temp[1]) + target_fan_level = fan_policy[1].up_step[fan_up_temp_level[1]].level * 255 / 100; + + if (current_temp < fan_down_temp[1]) + target_fan_level = fan_policy[1].down_step[fan_down_temp_level[1]].level * 255 / 100; + + bias = (speed_percent_threshold[1] * 255) / 100; + bias = bias * (255 - target_fan_level) / 255; + target_fan_level = (target_fan_level > bias) ? target_fan_level - bias : 0; + target_fan_level = target_fan_level * fan_policy[1].percent / 100; + sbx00_set_fan_level(target_fan_level, 1); + + get_up_temp(&fan_policy[1], current_temp, &fan_up_temp[1], &fan_up_temp_level[1]); + get_down_temp(&fan_policy[1], current_temp, &fan_down_temp[1], &fan_down_temp_level[1]); + +exit: + schedule_delayed_work_on(0, &fan_policy[1].work, fan_policy[1].adjust_period * HZ); +} + +static void sbx00_fan3_init(struct work_struct *work) +{ + u8 reg; + int ret; + int speed_low, speed_high; + + /* Configure gpio49 as FANOUT2 */ + reg = pm_ioread(PM_OPTION_0); + pm_iowrite(PM_OPTION_0, reg | FAN2_EN); + + /* setting FANOUT2 mode: + * without temperuature input, controlled by software + */ + reg = pm2_ioread(FAN_CNTR_REG); + reg &= ~(FAN_MODE_MASK << FAN2_CNTR_SHIFT); + reg |= FAN_MODE1 << FAN2_CNTR_SHIFT; + pm2_iowrite(FAN_CNTR_REG, reg); + + reg = pm2_ioread(FAN_MISC2_REG); + reg &= ~(1 << AUTPMODE1); /* Disable auto mode*/ + reg &= ~(1 << LINEARMODE1); /* Use step mode*/ + reg |= (1 << FAN1POLARITY); /* Active High */ + pm2_iowrite(FAN_MISC2_REG, reg); + + /* Set PWM freqency at 19.83KHz*/ + pm2_iowrite(FREQDIV2_REG, PWM_19KHZ); + + /* Enable Speed Detect */ + pm2_iowrite(FAN2DETCONTROL, FANDET_EN | USEAVERAGE); + pm2_iowrite(FAN2SPEEDLIMLO, 0xFF); /* Set limits register */ + pm2_iowrite(FAN2SPEEDLIMHI, 0xFF); + + sbx00_set_fan_level(255, 2); + msleep(250); + speed_high = sbx00_get_fan_speed(2, false); + sbx00_set_fan_level(0, 2); + msleep(5000); + speed_low = sbx00_get_fan_speed(2, false); + sbx00_set_fan_level(255, 2); + if (speed_high >= speed_low) + speed_percent_threshold[2] = speed_low * 100 / speed_high; + pr_info("SpeedPercentThreshold of FAN3 is %d%%\n", speed_percent_threshold[2]); + + /* force fans in auto mode first */ + work->func = fan3_adjust; + sbx00_fan_mode[2] = FAN_AUTO_MODE; + sbx00_fan_start_auto(2); + + ret = sysfs_create_files(&sbx00_hwmon_dev->kobj, sbx00_hwmon_fan[2]); + if (ret) + printk(KERN_ERR "fail to create sysfs files\n"); +} + +static void fan3_adjust(struct work_struct *work) +{ + u8 current_temp; + int target_fan_level, bias; + + current_temp = fan_policy[2].depend_temp(0) / 1000; + + if ((current_temp <= fan_up_temp[2]) && + (current_temp >= fan_down_temp[2])) + goto exit; + + if (current_temp > fan_up_temp[2]) + target_fan_level = fan_policy[2].up_step[fan_up_temp_level[2]].level * 255 / 100; + + if (current_temp < fan_down_temp[2]) + target_fan_level = fan_policy[2].down_step[fan_down_temp_level[2]].level * 255 / 100; + + bias = (speed_percent_threshold[2] * 255) / 100; + bias = bias * (255 - target_fan_level) / 255; + target_fan_level = (target_fan_level > bias) ? target_fan_level - bias : 0; + target_fan_level = target_fan_level * fan_policy[2].percent / 100; + sbx00_set_fan_level(target_fan_level, 2); + + get_up_temp(&fan_policy[2], current_temp, &fan_up_temp[2], &fan_up_temp_level[2]); + get_down_temp(&fan_policy[2], current_temp, &fan_down_temp[2], &fan_down_temp_level[2]); + +exit: + schedule_delayed_work_on(0, &fan_policy[2].work, fan_policy[2].adjust_period * HZ); +} + +static int sbx00_fan_probe(struct platform_device *dev) +{ + int id = dev->id - 1; + struct sensor_device *sdev = (struct sensor_device *)dev->dev.platform_data; + + /* get fan policy */ + switch (sdev->fan_policy) { + case KERNEL_HELPER_POLICY: + memcpy(&fan_policy[id], &kernel_helper_policy, sizeof(struct loongson_fan_policy)); + break; + case STEP_SPEED_POLICY: + memcpy(&fan_policy[id], &step_speed_policy, sizeof(struct loongson_fan_policy)); + fan_policy[id].percent = sdev->fan_percent; + if (fan_policy[id].percent == 0 || fan_policy[id].percent > 100) + fan_policy[id].percent = 100; + break; + case CONSTANT_SPEED_POLICY: + default: + memcpy(&fan_policy[id], &constant_speed_policy, sizeof(struct loongson_fan_policy)); + fan_policy[id].percent = sdev->fan_percent; + if (fan_policy[id].percent == 0 || fan_policy[id].percent > 100) + fan_policy[id].percent = 100; + break; + } + + if (id == 0) { + INIT_DEFERRABLE_WORK(&fan_policy[0].work, sbx00_fan1_init); + schedule_delayed_work_on(0, &fan_policy[0].work, 0); + } + if (id == 1) { + INIT_DEFERRABLE_WORK(&fan_policy[1].work, sbx00_fan2_init); + schedule_delayed_work_on(0, &fan_policy[1].work, 0); + } + if (id == 2) { + INIT_DEFERRABLE_WORK(&fan_policy[2].work, sbx00_fan3_init); + schedule_delayed_work_on(0, &fan_policy[2].work, 0); + } + + sbx00_fan_enable[id] = 1; + + return 0; +} + +static struct platform_driver sbx00_fan_driver = { + .probe = sbx00_fan_probe, + .driver = { + .name = "sbx00-fan", + .owner = THIS_MODULE, + }, +}; + +static int __init sbx00_fan_init(void) +{ + int ret; + + sbx00_hwmon_dev = hwmon_device_register(NULL); + if (IS_ERR(sbx00_hwmon_dev)) { + ret = -ENOMEM; + printk(KERN_ERR "hwmon_device_register fail!\n"); + goto fail_hwmon_device_register; + } + + ret = sysfs_create_group(&sbx00_hwmon_dev->kobj, + &sbx00_hwmon_attribute_group); + if (ret) { + printk(KERN_ERR "fail to create loongson hwmon!\n"); + goto fail_sysfs_create_group_hwmon; + } + + ret = platform_driver_register(&sbx00_fan_driver); + if (ret) { + printk(KERN_ERR "fail to register fan driver!\n"); + goto fail_register_fan; + } + + return 0; + +fail_register_fan: + sysfs_remove_group(&sbx00_hwmon_dev->kobj, + &sbx00_hwmon_attribute_group); + +fail_sysfs_create_group_hwmon: + hwmon_device_unregister(sbx00_hwmon_dev); + +fail_hwmon_device_register: + return ret; +} + +static void __exit sbx00_fan_exit(void) +{ + /* set fan at full speed before module exit */ + if (sbx00_fan_enable[0]) { + sbx00_fan_mode[0] = FAN_FULL_MODE; + sbx00_fan_stop_auto(0); + sbx00_set_fan_level(MAX_FAN_LEVEL, 0); + } + + if (sbx00_fan_enable[1]) { + sbx00_fan_mode[1] = FAN_FULL_MODE; + sbx00_fan_stop_auto(1); + sbx00_set_fan_level(MAX_FAN_LEVEL, 1); + } + + if (sbx00_fan_enable[2]) { + sbx00_fan_mode[2] = FAN_FULL_MODE; + sbx00_fan_stop_auto(2); + sbx00_set_fan_level(MAX_FAN_LEVEL, 2); + } + + platform_driver_unregister(&sbx00_fan_driver); + sysfs_remove_group(&sbx00_hwmon_dev->kobj, + &sbx00_hwmon_attribute_group); + hwmon_device_unregister(sbx00_hwmon_dev); +} + +late_initcall(sbx00_fan_init); +module_exit(sbx00_fan_exit); + +MODULE_AUTHOR("Yu Xiang "); +MODULE_AUTHOR("Huacai Chen "); +MODULE_DESCRIPTION("SB700/SB710/SB800 fan control driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/mips/sd5075.c b/drivers/platform/mips/sd5075.c new file mode 100644 index 0000000000000..be42c58822901 --- /dev/null +++ b/drivers/platform/mips/sd5075.c @@ -0,0 +1,211 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct i2c_client *sd5075_client = NULL; + +static struct device *sd5075_hwmon_dev; + +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t get_sd5075_label(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t get_sd5075_temp(struct device *dev, + struct device_attribute *attr, char *buf); + +static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0); + +static struct attribute *sd5075_hwmon_attributes[] = +{ + &sensor_dev_attr_name.dev_attr.attr, + NULL +}; + +/* Hwmon device attribute group */ +static struct attribute_group sd5075_hwmon_attribute_group = +{ + .attrs = sd5075_hwmon_attributes, +}; + +/* Hwmon device get name */ +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "sd5075\n"); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_sd5075_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, get_sd5075_label, NULL, 1); + +static const struct attribute *sd5075_hwmon_temp[1][3] = { + { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_label.dev_attr.attr, + NULL + } +}; + +#define BUS_MASK 0xffffffff00000000ul +#define ADDR_MASK 0x00000000fffffffful + +static int sd5075_probe(struct platform_device *dev) +{ + char i2c_name[16]; + struct i2c_board_info info; + struct i2c_adapter *adapter = NULL; + int found = 0, i = 0, r = 0, i2c_bus; + struct sensor_device *sdev = (struct sensor_device *)dev->dev.platform_data; + + memset(&info, 0, sizeof(struct i2c_board_info)); + + i2c_bus = ((sdev->base_addr & BUS_MASK) >> 32) - 1; + if (i2c_bus < 0) + sprintf(i2c_name, "SMBus PIIX4"); + else + sprintf(i2c_name, "LS2X I2C%d", i2c_bus); + + /* get i2c_adapter */ + adapter = i2c_get_adapter(i++); + while (adapter) { + if (strncmp(adapter->name, i2c_name, strlen(i2c_name)) == 0) { + found = 1; + break; + } + + adapter = i2c_get_adapter(i++); + } + + if (!found) + goto fail; + + info.addr = sdev->base_addr & ADDR_MASK; + info.platform_data = dev->dev.platform_data; + strncpy(info.type, "sd5075", I2C_NAME_SIZE); + sd5075_client = i2c_new_device(adapter, &info); + if (sd5075_client == NULL) { + pr_err("failed to attach sd5075 sensor\n"); + goto fail; + } + + r = sysfs_create_files(&sd5075_hwmon_dev->kobj, sd5075_hwmon_temp[0]); + if (r) + goto fail; + + /* set alert mode */ + i2c_smbus_write_byte_data(sd5075_client, 0x1, 0x80); + + printk(KERN_INFO "Success to attach sd5075 sensor\n"); + + return 0; +fail: + pr_err("Fail to found smbus controller attach sd5075 sensor\n"); + + return r; +} + +static void sd5075_shutdown(struct platform_device *dev) +{ + sd5075_client = NULL; + msleep(15); /* Release I2C/SMBus resources */ +} + +static ssize_t get_sd5075_label(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device *sdev = (struct sensor_device *)sd5075_client->dev.platform_data; + return sprintf(buf, "%s\n", sdev->label); +} + +int sd5075_internal_temp(int id) +{ + int reg_value = 0, temp; + + reg_value = i2c_smbus_read_word_swapped(sd5075_client, 0x0); + + if (reg_value & 0x8000) + { + reg_value |= 0xffff0000; + temp = (reg_value >> 4) * 1000 / 16; + + } + else + temp = (reg_value >> 4) * 1000 / 16; + + return temp; +} + +static ssize_t get_sd5075_temp(struct device *dev, struct device_attribute *attr, char *buf) { + + int id = (to_sensor_dev_attr(attr))->index -1; + int value = sd5075_internal_temp(id); + + return sprintf(buf, "%d\n", value); + +} + +static struct platform_driver sd5075_driver = { + .driver = { + .name = "sd5075", + .owner = THIS_MODULE, + }, + .probe = sd5075_probe, + .shutdown = sd5075_shutdown, +}; + +static int __init sd5075_init(void) +{ + int ret; + + sd5075_hwmon_dev = hwmon_device_register(NULL); + + if (IS_ERR(sd5075_hwmon_dev)) { + ret = -ENOMEM; + printk(KERN_ERR "hwmon_device_register fail!\n"); + goto fail_hwmon_device_register; + } + + ret = sysfs_create_group(&sd5075_hwmon_dev->kobj, + &sd5075_hwmon_attribute_group); + if (ret) { + printk(KERN_ERR "fail to create loongson hwmon!\n"); + goto fail_sysfs_create_group_hwmon; + } + + platform_driver_register(&sd5075_driver); + + return 0; + +fail_sysfs_create_group_hwmon: + hwmon_device_unregister(sd5075_hwmon_dev); + +fail_hwmon_device_register: + return ret; +} + +static void __exit sd5075_exit(void) +{ + platform_driver_unregister(&sd5075_driver); + sysfs_remove_group(&sd5075_hwmon_dev->kobj, + &sd5075_hwmon_attribute_group); + hwmon_device_unregister(sd5075_hwmon_dev); +} + +late_initcall(sd5075_init); +module_exit(sd5075_exit); + +MODULE_AUTHOR("Huacai Chen "); +MODULE_AUTHOR("Liangliang Huang "); +MODULE_DESCRIPTION("SD5075 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/mips/tmp75.c b/drivers/platform/mips/tmp75.c new file mode 100644 index 0000000000000..0582c8fa2e2b2 --- /dev/null +++ b/drivers/platform/mips/tmp75.c @@ -0,0 +1,24 @@ +#include + +static int __init tmp75_init(void) +{ + printk("====Please use device-tree for TMP75 Driver====\n"); + printk("You can put the tmp75 node under the i2c node:\n" + "tmp75@4e {\n" + " compatible = \"ti,tmp75\"\n" + " reg = <0x4e>\n" + "}\n"); + printk("==================Thank you!===================\n"); + return 0; +} + +static void __exit tmp75_exit(void) +{ +} + +late_initcall(tmp75_init); +module_exit(tmp75_exit); + +MODULE_AUTHOR("Hongbing Hu "); +MODULE_DESCRIPTION("TMP75 driver, based on the EMC1412 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/mips/wpce_fan.c b/drivers/platform/mips/wpce_fan.c new file mode 100644 index 0000000000000..ecef3158c59bc --- /dev/null +++ b/drivers/platform/mips/wpce_fan.c @@ -0,0 +1,311 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifdef MAX_FAN_LEVEL +#undef MAX_FAN_LEVEL +#endif +#define MAX_FAN_LEVEL 5 + +int fan_enable; +enum fan_control_mode fan_mode; +static struct loongson_fan_policy fan_policy; + +static struct device *wpce775l_hwmon_dev; + +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf); +static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0); + +static struct attribute *wpce775l_hwmon_attributes[] = +{ + &sensor_dev_attr_name.dev_attr.attr, + NULL +}; + +/* Hwmon device attribute group */ +static struct attribute_group wpce775l_hwmon_attribute_group = +{ + .attrs = wpce775l_hwmon_attributes, +}; + +/* Hwmon device get name */ +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "wpce775l-fan\n"); +} + +static ssize_t get_fan_level(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t set_fan_level(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t get_fan_mode(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t set_fan_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t get_fan_speed(struct device *dev, + struct device_attribute *attr, char *buf); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, + get_fan_level, set_fan_level, 1); +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + get_fan_mode, set_fan_mode, 1); +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan_speed, NULL, 1); + +static const struct attribute *hwmon_fan1[] = { + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + NULL +}; + +static struct workqueue_struct *notify_workqueue; +static void notify_temp(struct work_struct *work); +static DECLARE_DELAYED_WORK(notify_work, notify_temp); + +static int wpce_set_fan_level(u8 level) +{ + if (level > MAX_FAN_LEVEL) + level = MAX_FAN_LEVEL; + + ec_write(INDEX_FAN_SPEED_LEVEL, level); + return 0; +} + +static ssize_t get_fan_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 val; + + val = ec_read(INDEX_FAN_SPEED_LEVEL); + return sprintf(buf, "%d\n", val); +} + +static ssize_t set_fan_level(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + u8 new_level; + + new_level = clamp_val(simple_strtoul(buf, NULL, 10), 0, 255); + if (fan_mode == FAN_MANUAL_MODE) + wpce_set_fan_level(new_level); + + return count; +} + +static ssize_t get_fan_speed(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 val; + + val = (ec_read(INDEX_FAN_SPEED_HIGH) << 8) + + ec_read(INDEX_FAN_SPEED_LOW); + return sprintf(buf, "%d\n", val); +} + +static void notify_temp(struct work_struct *work) +{ + u8 temp; + + temp = fan_policy.depend_temp(0) / 1000; + + ec_write_noindex(0x4d, temp); + + queue_delayed_work(notify_workqueue, ¬ify_work, + fan_policy.adjust_period * HZ); +} + +static int notify_temp_to_EC(void) +{ + notify_workqueue = create_singlethread_workqueue("Temprature Notify"); + queue_delayed_work(notify_workqueue, ¬ify_work, HZ); + + return 0; +} + +static int kernel_control_fan(void) +{ + ec_write(INDEX_FAN_CTRLMOD,FAN_CTRL_BYHOST); + return 0; +} + +static int ec_control_fan(void) +{ + ec_write(INDEX_FAN_CTRLMOD,FAN_CTRL_BYEC); + return 0; +} + +static void fan_start_auto(void) +{ + ec_control_fan(); + + switch (fan_policy.type) { + case KERNEL_HELPER_POLICY: + notify_temp_to_EC(); + break; + default: + printk(KERN_ERR "wpce fan not support fan policy id %d!\n", fan_policy.type); + wpce_set_fan_level(MAX_FAN_LEVEL); + } + + return; +} + +static void fan_stop_auto(void) +{ + if ((fan_policy.type == KERNEL_HELPER_POLICY) && + (fan_mode == FAN_AUTO_MODE)) { + cancel_delayed_work(¬ify_work); + destroy_workqueue(notify_workqueue); + } + + kernel_control_fan(); +} + +static ssize_t set_fan_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + u8 new_mode; + + new_mode = clamp_val(simple_strtoul(buf, NULL, 10), + FAN_FULL_MODE, FAN_AUTO_MODE); + + if (new_mode == fan_mode) + return count; + + switch (new_mode) { + case FAN_FULL_MODE: + fan_stop_auto(); + wpce_set_fan_level(MAX_FAN_LEVEL); + break; + case FAN_MANUAL_MODE: + fan_stop_auto(); + break; + case FAN_AUTO_MODE: + fan_start_auto(); + break; + default: + break; + } + + fan_mode = new_mode; + + return count; +} + +static ssize_t get_fan_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", fan_mode); +} + +static int fan_probe(struct platform_device *dev) +{ + int ret; + struct sensor_device *sdev = (struct sensor_device *)dev->dev.platform_data; + + /* get fan policy */ + switch (sdev->fan_policy) { + case KERNEL_HELPER_POLICY: + memcpy(&fan_policy, &kernel_helper_policy, sizeof(fan_policy)); + break; + case STEP_SPEED_POLICY: + memcpy(&fan_policy, &step_speed_policy, sizeof(fan_policy)); + break; + case CONSTANT_SPEED_POLICY: + default: + memcpy(&fan_policy, &constant_speed_policy, sizeof(fan_policy)); + fan_policy.percent = sdev->fan_percent; + if (fan_policy.percent == 0 || fan_policy.percent > 100) + fan_policy.percent = 100; + break; + } + + /* force fan in auto mode first */ + fan_mode = FAN_AUTO_MODE; + fan_start_auto(); + + ret = sysfs_create_files(&wpce775l_hwmon_dev->kobj, hwmon_fan1); + if (ret) { + printk(KERN_ERR "fail to create sysfs files\n"); + return ret; + } + + return 0; +} + +static struct platform_driver fan_driver = { + .probe = fan_probe, + .driver = { + .name = "wpce-fan", + .owner = THIS_MODULE, + }, +}; + +static int __init wpce_fan_init(void) +{ + int ret; + + wpce775l_hwmon_dev = hwmon_device_register(NULL); + if (IS_ERR(wpce775l_hwmon_dev)) { + ret = -ENOMEM; + printk(KERN_ERR "hwmon_device_register fail!\n"); + goto fail_hwmon_device_register; + } + + ret = sysfs_create_group(&wpce775l_hwmon_dev->kobj, + &wpce775l_hwmon_attribute_group); + if (ret) { + printk(KERN_ERR "fail to create loongson hwmon!\n"); + goto fail_sysfs_create_group_hwmon; + } + + ret = platform_driver_register(&fan_driver); + if (ret) { + printk(KERN_ERR "fail to register fan driver!\n"); + goto fail_register_fan; + } + + return 0; + +fail_register_fan: + sysfs_remove_group(&wpce775l_hwmon_dev->kobj, + &wpce775l_hwmon_attribute_group); + +fail_sysfs_create_group_hwmon: + hwmon_device_unregister(wpce775l_hwmon_dev); + +fail_hwmon_device_register: + return ret; +} + +static void __exit wpce_fan_exit(void) +{ + /* set fan at full speed mode before module exit */ + if (fan_enable) + fan_mode = FAN_FULL_MODE; + fan_stop_auto(); + wpce_set_fan_level(MAX_FAN_LEVEL); + + platform_driver_unregister(&fan_driver); + sysfs_remove_group(&wpce775l_hwmon_dev->kobj, + &wpce775l_hwmon_attribute_group); + hwmon_device_unregister(wpce775l_hwmon_dev); +} + +late_initcall(wpce_fan_init); +module_exit(wpce_fan_exit); + +MODULE_AUTHOR("Yu Xiang "); +MODULE_AUTHOR("Huacai Chen "); +MODULE_DESCRIPTION("WPCE775L fan control driver"); +MODULE_LICENSE("GPL"); From 2cfaf40228d120e784a81d814629c553f90bf685 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 15:16:04 +0800 Subject: [PATCH 021/130] AOSCOS: platform: mips: rename dependency for LEMOTE3A_LAPTOP Rename LOONGSON_MACH3X => MACH_LOONGSON64 per commit 6fbde6b492df ("MIPS: Loongson64: Move files to the top-level directory"). Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig index 59b6027eeadf5..7ef9a47556d59 100644 --- a/drivers/platform/mips/Kconfig +++ b/drivers/platform/mips/Kconfig @@ -39,7 +39,7 @@ config LS2K_RESET config LEMOTE3A_LAPTOP tristate "Lemote Loongson-3A Laptop Driver" - depends on LOONGSON_MACH3X + depends on MACH_LOONGSON64 select BACKLIGHT_LCD_SUPPORT select LCD_CLASS_DEVICE select BACKLIGHT_CLASS_DEVICE From aba81875395cc4dd28cd30d5014c65eca05c1c15 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 15:42:39 +0800 Subject: [PATCH 022/130] AOSCOS: platform: sd5075: convert to i2c_new_client_device() function Follow commit 390fd0475af5 ("i2c: remove deprecated i2c_new_device API") and revise i2c_new_device() to i2c_new_client_device(), there is no change how the function is invoked. Fixes: "AOSCOS: MIPS: Loongson 3: Add platform device drivers" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/sd5075.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mips/sd5075.c b/drivers/platform/mips/sd5075.c index be42c58822901..02bb998210191 100644 --- a/drivers/platform/mips/sd5075.c +++ b/drivers/platform/mips/sd5075.c @@ -93,7 +93,7 @@ static int sd5075_probe(struct platform_device *dev) info.addr = sdev->base_addr & ADDR_MASK; info.platform_data = dev->dev.platform_data; strncpy(info.type, "sd5075", I2C_NAME_SIZE); - sd5075_client = i2c_new_device(adapter, &info); + sd5075_client = i2c_new_client_device(adapter, &info); if (sd5075_client == NULL) { pr_err("failed to attach sd5075 sensor\n"); goto fail; From 94174ffd58c4ece8867ce3b6ee8a7c9222f8e300 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 15:46:26 +0800 Subject: [PATCH 023/130] AOSCOS: platform: emc1412: convert to i2c_new_client_device() function Follow commit 390fd0475af5 ("i2c: remove deprecated i2c_new_device API") and revise i2c_new_device() to i2c_new_client_device(), there is no change how the function is invoked. Fixes: "AOSCOS: MIPS: Loongson 3: Add platform device drivers" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/emc1412.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mips/emc1412.c b/drivers/platform/mips/emc1412.c index 02f2284743ed3..3fb2dcd600106 100644 --- a/drivers/platform/mips/emc1412.c +++ b/drivers/platform/mips/emc1412.c @@ -143,7 +143,7 @@ static int emc1412_probe(struct platform_device *dev) info.addr = sdev->base_addr; info.platform_data = dev->dev.platform_data; strncpy(info.type, "emc1412", I2C_NAME_SIZE); - emc1412_client[id] = i2c_new_device(adapter, &info); + emc1412_client[id] = i2c_new_client_device(adapter, &info); if (emc1412_client[id] == NULL) { printk(KERN_ERR "failed to attach EMC1412 sensor\n"); goto fail; From 10884a032f4b543348645ffdeca109d7351a60ea Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 15:47:29 +0800 Subject: [PATCH 024/130] AOSCOS: platform: sd5075: mark non-prototyped functions as static Fix build error with -Werror due to missing prototypes. Fixes: "AOSCOS: MIPS: Loongson 3: Add platform device drivers" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/sd5075.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mips/sd5075.c b/drivers/platform/mips/sd5075.c index 02bb998210191..27e9fb359b9f6 100644 --- a/drivers/platform/mips/sd5075.c +++ b/drivers/platform/mips/sd5075.c @@ -128,7 +128,7 @@ static ssize_t get_sd5075_label(struct device *dev, return sprintf(buf, "%s\n", sdev->label); } -int sd5075_internal_temp(int id) +static int sd5075_internal_temp(int id) { int reg_value = 0, temp; From 321db7e8283216c8e114b5c716007508119cdb96 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 15:49:04 +0800 Subject: [PATCH 025/130] AOSCOS: platform: emc1412: mark non-prototyped functions as static Fix build error with -Werror due to missing prototypes. Fixes: "AOSCOS: MIPS: Loongson 3: Add platform device drivers" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/emc1412.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/mips/emc1412.c b/drivers/platform/mips/emc1412.c index 3fb2dcd600106..bf2fa4b815c8f 100644 --- a/drivers/platform/mips/emc1412.c +++ b/drivers/platform/mips/emc1412.c @@ -182,7 +182,7 @@ static void emc1412_shutdown(struct platform_device *dev) * reg1 & reg10 between (0.125, 0.875) * to avoid use float, temprature will mult 1000 */ -int emc1412_internal_temp(int id) +static int emc1412_internal_temp(int id) { u8 reg; int temp; @@ -198,7 +198,7 @@ int emc1412_internal_temp(int id) return temp; } -int emc1412_external_temp(int id) +static int emc1412_external_temp(int id) { u8 reg; int temp; @@ -336,7 +336,7 @@ static void do_thermal_timer(struct work_struct *work) } } -int fixup_cpu_temp(int cpu, int cputemp) +static int fixup_cpu_temp(int cpu, int cputemp) { static int printed[MAX_PACKAGES] = {0, 0, 0, 0}; int i, value, temp_min = 50000, temp_max = -20000; From 2b80bae5b5db57aa69b36456b60e03a359dc50b0 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 15:49:40 +0800 Subject: [PATCH 026/130] AOSCOS: platform: emc1412: drop unused fixup_cpu_temp() function This function was not used anywhere and triggered -Werror=unused-function. Fixes: "AOSCOS: MIPS: Loongson 3: Add platform device drivers" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/emc1412.c | 60 --------------------------------- 1 file changed, 60 deletions(-) diff --git a/drivers/platform/mips/emc1412.c b/drivers/platform/mips/emc1412.c index bf2fa4b815c8f..6524baddd6ca2 100644 --- a/drivers/platform/mips/emc1412.c +++ b/drivers/platform/mips/emc1412.c @@ -336,66 +336,6 @@ static void do_thermal_timer(struct work_struct *work) } } -static int fixup_cpu_temp(int cpu, int cputemp) -{ - static int printed[MAX_PACKAGES] = {0, 0, 0, 0}; - int i, value, temp_min = 50000, temp_max = -20000; - - for (i=0; i temp_max) - temp_max = value; - } - for (i=0; i temp_max) - temp_max = value; - } - - if (temp_min > temp_max) { - printk_once("EMC1412: No valid reference.\n"); - return cputemp; - } - if (cputemp < 0 && temp_max < 2000) { - printk_once("EMC1412: No valid reference.\n"); - return cputemp; - } - - if (cputemp < temp_min - 5000) { - if(!printed[cpu]) { - printed[cpu] = 1; - printk("EMC1412: Original CPU#%d temperature too low, " - "fixup with reference: (%d -> %d).\n", - cpu, cputemp, temp_min - 5000); - } - return temp_min - 5000; - } - if (cputemp > temp_max + 15000) { - if(!printed[cpu]) { - printed[cpu] = 1; - printk("EMC1412: Original CPU#%d temperature too high, " - "fixup with reference: (%d -> %d).\n", - cpu, cputemp, temp_max + 10000); - } - return temp_max + 15000; - } - if(!printed[cpu]) { - printed[cpu] = 1; - printk("EMC1412: Original CPU#%d temperature is OK: (%d:%d:%d).\n", - cpu, cputemp, temp_min, temp_max); - } - - return cputemp; -} - static struct platform_driver emc1412_driver = { .probe = emc1412_probe, .shutdown = emc1412_shutdown, From 2b7125aaf4e84b5d623b2c54752e17af60884a0c Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 15:56:33 +0800 Subject: [PATCH 027/130] AOSCOS: platform: emc1412: drop unused emc1412_internal_temp() function This function was not used anywhere and triggered -Werror=unused-function. Fixes: "AOSCOS: MIPS: Loongson 3: Add platform device drivers" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/emc1412.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/drivers/platform/mips/emc1412.c b/drivers/platform/mips/emc1412.c index 6524baddd6ca2..fc1a26cd34fe0 100644 --- a/drivers/platform/mips/emc1412.c +++ b/drivers/platform/mips/emc1412.c @@ -174,30 +174,6 @@ static void emc1412_shutdown(struct platform_device *dev) msleep(15); /* Release I2C/SMBus resources */ } -/* - * emc1412 provide 2 temprature data - * Internal temprature: reg0.reg29 - * External temprature: reg1.reg10 - * reg0 & reg1 from 0 to 127 - * reg1 & reg10 between (0.125, 0.875) - * to avoid use float, temprature will mult 1000 - */ -static int emc1412_internal_temp(int id) -{ - u8 reg; - int temp; - struct i2c_client *client; - - if (id < 0 || !(client = emc1412_client[id])) - return NOT_VALID_TEMP; - - reg = i2c_smbus_read_byte_data(client, EMC1412_TEMP_INT_LO_REG); - temp = i2c_smbus_read_byte_data(client, EMC1412_TEMP_INT_HI_REG) * 1000; - temp += (reg >> 5) * 125; - - return temp; -} - static int emc1412_external_temp(int id) { u8 reg; From cb08ad1ac79cd8dd7680eb1c96eb6e2d3082640d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 16:14:17 +0800 Subject: [PATCH 028/130] AOSCOS: platform: lemote3a-laptop: drop fb_blank state from backlight restoration logic Follow commit 4551978bb50a ("backlight: Remove fb_blank from struct backlight_properties") and remove fb_blank from the ternary operator in backlight restoration function, as the unblank state has already been set as the default since this commit. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/lemote3a-laptop.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/mips/lemote3a-laptop.c b/drivers/platform/mips/lemote3a-laptop.c index b06e7b6ae97c7..6e6b0c46aca4c 100644 --- a/drivers/platform/mips/lemote3a-laptop.c +++ b/drivers/platform/mips/lemote3a-laptop.c @@ -625,9 +625,8 @@ static int lemote3a_set_brightness(struct backlight_device * pdev) { unsigned int level = 0; - level = ((FB_BLANK_UNBLANK==pdev->props.fb_blank) && - (FB_BLANK_UNBLANK==pdev->props.power)) ? - pdev->props.brightness : 0; + level = (FB_BLANK_UNBLANK==pdev->props.power) ? + pdev->props.brightness : 0; if (MAX_BRIGHTNESS < level) { level = MAX_BRIGHTNESS; From 0ef5f55fedb126f803659304a705e928e257920e Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 16:45:30 +0800 Subject: [PATCH 029/130] AOSCOS: platform: lemote3a-laptop: fix pci_enable_device() usage pci_enable_device() ignores return values. Follow other use cases in the kernel tree and feed it to an int err, printing to kernel output when an error occurs. Fixes: "AOSCOS: MIPS: Loongson 3: Add platform device drivers" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/lemote3a-laptop.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/platform/mips/lemote3a-laptop.c b/drivers/platform/mips/lemote3a-laptop.c index 6e6b0c46aca4c..a8d339a3e30b7 100644 --- a/drivers/platform/mips/lemote3a-laptop.c +++ b/drivers/platform/mips/lemote3a-laptop.c @@ -542,10 +542,16 @@ static int lemote3a_laptop_suspend(struct platform_device * pdev, pm_message_t s /* Platform device resume handler */ static int lemote3a_laptop_resume(struct platform_device * pdev) { + int err; struct pci_dev *dev; dev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL); - pci_enable_device(dev); + err = pci_enable_device(dev); + if (err) { + dev_err(&pdev->dev, "lemote3a-laptop: cannot enable ATI device: %d\n", + err); + return err; + } /* Process LID event */ lemote3a_sci_event_handler(SCI_EVENT_NUM_LID); From f440520c54516937d871349a4c8699de1ca422a0 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 16:47:07 +0800 Subject: [PATCH 030/130] AOSCOS: platform: sbx00_fan: add missing definitions for pm{,2}_* functions These functions were declared as prototypes but were never defined, copy definitions from the rs780e-acpi driver to fix build. Fixes: "AOSCOS: MIPS: Loongson 3: Add platform device drivers" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/sbx00_fan.c | 42 ++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/drivers/platform/mips/sbx00_fan.c b/drivers/platform/mips/sbx00_fan.c index 6331e09722a1f..51c2017ebe1d6 100644 --- a/drivers/platform/mips/sbx00_fan.c +++ b/drivers/platform/mips/sbx00_fan.c @@ -97,10 +97,44 @@ static struct loongson_fan_policy fan_policy[MAX_SBX00_FANS]; /* threshold = SpeedOfPWM(0)/SpeedOfPWM(255) */ static int speed_percent_threshold[MAX_SBX00_FANS]; -extern u8 pm_ioread(u8 reg); -extern u8 pm2_ioread(u8 reg); -extern void pm_iowrite(u8 reg, u8 val); -extern void pm2_iowrite(u8 reg, u8 val); +/* from rs780-acpi */ +#define PM_INDEX 0xCD6 +#define PM_DATA 0xCD7 +#define PM2_INDEX 0xCD0 +#define PM2_DATA 0xCD1 + +static void pmio_write_index(u16 index, u8 reg, u8 value) +{ + outb(reg, index); + outb(value, index + 1); +} + +static u8 pmio_read_index(u16 index, u8 reg) +{ + outb(reg, index); + return inb(index + 1); +} + +static void pm_iowrite(u8 reg, u8 value) +{ + pmio_write_index(PM_INDEX, reg, value); +} + +static u8 pm_ioread(u8 reg) +{ + return pmio_read_index(PM_INDEX, reg); +} + +static void pm2_iowrite(u8 reg, u8 value) +{ + pmio_write_index(PM2_INDEX, reg, value); +} + +static u8 pm2_ioread(u8 reg) +{ + return pmio_read_index(PM2_INDEX, reg); +} + /* up_temp & down_temp used in fan auto adjust */ static u8 fan_up_temp[MAX_SBX00_FANS]; From ff474aa2eb549660082550b2712563015d21c77d Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 9 Nov 2017 10:31:55 +0800 Subject: [PATCH 031/130] AOSCOS: MIPS: Loongson: Add ACPI Power Button driver [Mingcong Bai: arch/mips/loongson64/loongson-3/acpi_init.c was moved to drivers/platform/mips/rs780e-acpi.c with commit 0cfd2440aa03 ("MIPS: Loongson64: Make RS780E ACPI as a platform driver" and was partially modified with commit 68557c59a550 ("MIPS: Loongson64: Implement PM suspend for LEFI firmware"), merged accordingly.] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/rs780e-acpi.c | 73 +++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/drivers/platform/mips/rs780e-acpi.c b/drivers/platform/mips/rs780e-acpi.c index bb0e8ae0eefd8..dba6b7ec80067 100644 --- a/drivers/platform/mips/rs780e-acpi.c +++ b/drivers/platform/mips/rs780e-acpi.c @@ -1,10 +1,13 @@ // SPDX-License-Identifier: GPL-2.0 #include #include +#include +#include #include #include #include #include +#include static unsigned long acpi_iobase; @@ -20,6 +23,9 @@ static unsigned long acpi_iobase; #define PM2_INDEX 0xCD0 #define PM2_DATA 0xCD1 +static int acpi_irq; +static struct input_dev *button; + static void pmio_write_index(u16 index, u8 reg, u8 value) { outb(reg, index); @@ -69,6 +75,68 @@ static void acpi_hw_clear_status(void) outl(inl(ACPI_GPE0_BLK), ACPI_GPE0_BLK); } +static irqreturn_t acpi_int_routine(int irq, void *dev_id) +{ + u16 value; + + /* PMStatus: Check PwrBtnStatus */ + value = inw(ACPI_PM_EVT_BLK); + if (value & (1 << 8)) { + outw(1 << 8, ACPI_PM_EVT_BLK); + pr_info("Power Button pressed...\n"); + input_report_key(button, KEY_POWER, 1); + input_sync(button); + input_report_key(button, KEY_POWER, 0); + input_sync(button); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int __init power_button_init(void) +{ + int ret; + struct pci_dev *dev; + + dev = pci_get_bus_and_slot(0, 0); + switch (dev->vendor) { + case PCI_VENDOR_ID_AMD: + case PCI_VENDOR_ID_ATI: + acpi_irq = 7; + break; + default: + return -ENODEV; + } + + button = input_allocate_device(); + if (!button) + return -ENOMEM; + + button->name = "ACPI Power Button"; + button->phys = "acpi/button/input0"; + button->id.bustype = BUS_HOST; + button->dev.parent = NULL; + input_set_capability(button, EV_KEY, KEY_POWER); + + ret = request_irq(acpi_irq, acpi_int_routine, IRQF_SHARED, "acpi", acpi_int_routine); + if (ret) { + pr_err("ACPI Power Button Driver: Request irq %d failed!\n", acpi_irq); + return -EFAULT; + } + + ret = input_register_device(button); + if (ret) { + input_free_device(button); + return ret; + } + + pr_info("ACPI Power Button Driver: Init successful!\n"); + + return 0; +} +device_initcall(power_button_init); + static void acpi_registers_setup(void) { u32 value; @@ -130,6 +198,11 @@ static void acpi_registers_setup(void) value = pm2_ioread(0xf8); value |= ((1 << 5) | (1 << 1)); pm2_iowrite(0xf8, value); + + /* PMEnable: Enable PwrBtn */ + value = inw(ACPI_PM_EVT_BLK + 2); + value |= 1 << 8; + outw(value, ACPI_PM_EVT_BLK + 2); } static int rs780e_acpi_probe(struct platform_device *pdev) From 13d0218566825ae765cbf0bd8f8d0abe828ba90d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 17:36:26 +0800 Subject: [PATCH 032/130] AOSCOS: platform: rs780e-acpi: deprecate pci_get_bus_and_slot() The commit 5cf0c37a71da ("PCI: Remove pci_get_bus_and_slot() function") removed pci_get_bus_and_slot() in favour of pci_get_domain_bus_and_slot(), which added an extra first argument for setting the PCI domain (which defaulted to 0 with pci_get_bus_and_slot(). Add 0 as the first parameter to complete the transition. Fixes: "AOSCOS: MIPS: Loongson: Add ACPI Power Button driver" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/rs780e-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mips/rs780e-acpi.c b/drivers/platform/mips/rs780e-acpi.c index dba6b7ec80067..7d3d23459a071 100644 --- a/drivers/platform/mips/rs780e-acpi.c +++ b/drivers/platform/mips/rs780e-acpi.c @@ -99,7 +99,7 @@ static int __init power_button_init(void) int ret; struct pci_dev *dev; - dev = pci_get_bus_and_slot(0, 0); + dev = pci_get_domain_bus_and_slot(0, 0, 0); switch (dev->vendor) { case PCI_VENDOR_ID_AMD: case PCI_VENDOR_ID_ATI: From 5b76d6a6139392ba58de222577a6d65082be58db Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 033/130] AOSCOS: MIPS: Loongson: Add the multifunction keys (Fnkey) support. 1, Add special function keys keycode to keycode mapping table. Fn+F2: DisplaySwitch, keycode 227. Fn+F5: BrightnessUp, keycode 225. Fn+F6: BrightnessDown, keycode 224. 2, Fixup some special keys (Fn+F2, Fn+F5, Fn+F6) on A1201 laptop: do not generate release events issue. Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/input/keyboard/Kconfig | 11 ++++++++++ drivers/input/keyboard/atkbd.c | 21 +++++++++++++++++-- drivers/input/keyboard/lmps2atkbd.h | 32 +++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 drivers/input/keyboard/lmps2atkbd.h diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index bd943d9676854..c41dfa25a3648 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -156,6 +156,17 @@ config KEYBOARD_ATKBD_RDI_KEYCODES right-hand column will be interpreted as the key shown in the left-hand column. +config KEYBOARD_ATKBD_LEMOTE_KEYCODES + bool "Use Lemote laptop (A1201) keyboard scancodes" + depends on MACH_LOONGSON && KEYBOARD_ATKBD + default y + help + Say Y here if you want to use an AT or PS/2 keyboard, and your + keyboard uses keycodes that are specific to Lemote laptop (A1201) + keyboards. + + Say N if you use a standard keyboard. + config KEYBOARD_QT1050 tristate "Microchip AT42QT1050 Touch Sensor Chip" depends on I2C diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index c229bd6b3f7f2..c9f387e61d850 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -77,7 +77,13 @@ MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard conne static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = { -#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES +#if defined(CONFIG_KEYBOARD_ATKBD_LEMOTE_KEYCODES) + +/* XXX: need a more general approach */ + +#include "lmps2atkbd.h" /* include the keyboard scancodes */ + +#elif defined(CONFIG_KEYBOARD_ATKBD_HP_KEYCODES) /* XXX: need a more general approach */ @@ -389,7 +395,7 @@ static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code if (atkbd->set == 3) { if (atkbd->emul == 1) code |= 0x100; - } else { + } else { code = (code & 0x7f) | ((code & 0x80) << 1); if (atkbd->emul == 1) code |= 0x80; @@ -1017,6 +1023,14 @@ static void atkbd_apply_forced_release_keylist(struct atkbd* atkbd, __set_bit(keys[i], atkbd->force_release_mask); } +/* + * Most special keys (Fn+F?) on LS2GQ laptops do not generate release + * events so we have to do it ourselves. + */ +static unsigned int atkbd_lemote_laptop_forced_release_keys[] = { + 0x61, 0x6F, 0x76, -1U +}; + /* * Most special keys (Fn+F?) on Dell laptops do not generate release * events so we have to do it ourselves. @@ -1950,6 +1964,9 @@ static int __init atkbd_init(void) { dmi_check_system(atkbd_dmi_quirk_table); + atkbd_platform_fixup = atkbd_apply_forced_release_keylist; + atkbd_platform_fixup_data = atkbd_lemote_laptop_forced_release_keys; + return serio_register_driver(&atkbd_drv); } diff --git a/drivers/input/keyboard/lmps2atkbd.h b/drivers/input/keyboard/lmps2atkbd.h new file mode 100644 index 0000000000000..95d4ac5365c0d --- /dev/null +++ b/drivers/input/keyboard/lmps2atkbd.h @@ -0,0 +1,32 @@ +/* + * drivers/input/keyboard/lmps2atkbd.h + * + * Copyright (c) 2012 Huang Wei + * + * Lemote PS/2 AT-compatible Keyboard, found in Lemote Laptops (A1201) + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +/* These are offset for escaped keycodes: */ + 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117, + 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0, + 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183, + 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185, + 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12,225, + 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 85,224, + 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0,227, + 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99, + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125, + 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127, + 159, 0,115, 0,164, 0, 0,116,158, 0,172,166, 0, 0, 0,142, + 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0, + 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112, + 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0, + + 0, 0, 0, 65, 99, From 4b22eb2a7cc6501c1ab5deb60da103307768c658 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 17:54:18 +0800 Subject: [PATCH 034/130] AOSCOS: input: atkbd: correct dependency for KEYBOARD_ATKBD_LEMOTE_KEYCODES Rename dependency MACH_LOONGSON => MACH_LOONGSON64 per commit 30ad29bb4888 ("MIPS: Loongson: Naming style cleanup and rework") from the v4.2 cycle. Fixes: "AOSCOS: MIPS: Loongson: Add the multifunction keys (Fnkey) support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/input/keyboard/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index c41dfa25a3648..3f12e94931d62 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -158,7 +158,7 @@ config KEYBOARD_ATKBD_RDI_KEYCODES config KEYBOARD_ATKBD_LEMOTE_KEYCODES bool "Use Lemote laptop (A1201) keyboard scancodes" - depends on MACH_LOONGSON && KEYBOARD_ATKBD + depends on MACH_LOONGSON64 && KEYBOARD_ATKBD default y help Say Y here if you want to use an AT or PS/2 keyboard, and your From 50f11d9e5bcb093f7ca4eea37b060a5ebce7b3fc Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 4 Dec 2024 17:54:37 +0800 Subject: [PATCH 035/130] AOSCOS: input: atkbd: disable KEYBOARD_ATKBD_LEMOTE_KEYCODES by default This option sets a special keycode to workaround issues with certain multimedia Fn key combos on the Lemote A1201 laptop but does not use DMI to match device models. Disable this option by default until we adopt DMI detect-and-match to this particular option. Fixes: "AOSCOS: MIPS: Loongson: Add the multifunction keys (Fnkey) support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/input/keyboard/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 3f12e94931d62..15a7d7e7c62c9 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -159,7 +159,7 @@ config KEYBOARD_ATKBD_RDI_KEYCODES config KEYBOARD_ATKBD_LEMOTE_KEYCODES bool "Use Lemote laptop (A1201) keyboard scancodes" depends on MACH_LOONGSON64 && KEYBOARD_ATKBD - default y + default n help Say Y here if you want to use an AT or PS/2 keyboard, and your keyboard uses keycodes that are specific to Lemote laptop (A1201) From 73dcff7cf3211d3e9ac09473e32c6ee6fc7d1113 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 5 Dec 2024 13:51:25 +0800 Subject: [PATCH 036/130] AOSCOS: MIPS: workarounds: declare function prototypes Add prototype declaration for gpio_lvds_off(), turn_off_lvds(), and turn_on_lvds(), fixing build errors due to missing function prototypes. Fixes: "AOSCOS: MIPS: workaround A1004/A1101/A1201/A1205 display issues" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/include/asm/mach-loongson64/workarounds.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/mips/include/asm/mach-loongson64/workarounds.h b/arch/mips/include/asm/mach-loongson64/workarounds.h index 17b71172a0971..d5d6b131a7fa3 100644 --- a/arch/mips/include/asm/mach-loongson64/workarounds.h +++ b/arch/mips/include/asm/mach-loongson64/workarounds.h @@ -5,4 +5,8 @@ #define WORKAROUND_CPUFREQ 0x00000001 #define WORKAROUND_CPUHOTPLUG 0x00000002 +void gpio_lvds_off(void); +void turn_off_lvds(void); +void turn_on_lvds(void); + #endif From 649c38cb6cb24f933d7f69581e15629478603e89 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 5 Dec 2024 13:54:12 +0800 Subject: [PATCH 037/130] AOSCOS: MIPS: loongson64: add missing includes for the loongson_system_configuration struct Add missing includes for loongson.h and workarounds.h to fix build for A1004/A1101/A1201/A1205 workaround code. Fixes: "AOSCOS: MIPS: workaround A1004/A1101/A1201/A1205 display issues" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/platform.c | 2 ++ arch/mips/loongson64/workarounds.c | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 arch/mips/loongson64/workarounds.c diff --git a/arch/mips/loongson64/platform.c b/arch/mips/loongson64/platform.c index a447eab0218d2..56a0299b297db 100644 --- a/arch/mips/loongson64/platform.c +++ b/arch/mips/loongson64/platform.c @@ -8,6 +8,8 @@ #include #include +#include +#include /* * Kernel helper policy diff --git a/arch/mips/loongson64/workarounds.c b/arch/mips/loongson64/workarounds.c new file mode 100644 index 0000000000000..117e069144916 --- /dev/null +++ b/arch/mips/loongson64/workarounds.c @@ -0,0 +1,2 @@ +#include +#include From e2d85dad7a4c359ed18ce4b6bc4099671b64cb44 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 038/130] AOSCOS: GPIO: Reset usb One-Way-Import chip for TMCS Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- .../include/asm/mach-loongson64/workarounds.h | 1 + arch/mips/loongson64/workarounds.c | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/arch/mips/include/asm/mach-loongson64/workarounds.h b/arch/mips/include/asm/mach-loongson64/workarounds.h index d5d6b131a7fa3..296a97fdd0c00 100644 --- a/arch/mips/include/asm/mach-loongson64/workarounds.h +++ b/arch/mips/include/asm/mach-loongson64/workarounds.h @@ -4,6 +4,7 @@ #define WORKAROUND_CPUFREQ 0x00000001 #define WORKAROUND_CPUHOTPLUG 0x00000002 +#define WORKAROUND_USB_TMCS 0x00000010 void gpio_lvds_off(void); void turn_off_lvds(void); diff --git a/arch/mips/loongson64/workarounds.c b/arch/mips/loongson64/workarounds.c index 117e069144916..7eea4553b1fa1 100644 --- a/arch/mips/loongson64/workarounds.c +++ b/arch/mips/loongson64/workarounds.c @@ -1,2 +1,30 @@ #include #include + +static int __init usb_fix_for_tmcs(void) +{ + if (loongson_sysconf.workarounds & WORKAROUND_USB_TMCS) { + gpio_request(13, "gpio13"); + gpio_request(12, "gpio12"); + gpio_request(11, "gpio11"); + gpio_request(9, "gpio9"); + gpio_request(8, "gpio8"); + + gpio_direction_output(11, 1); + msleep(1000); + gpio_direction_output(13, 0); + gpio_direction_output(12, 0); + gpio_direction_output(9, 0); + gpio_direction_output(8, 0); + + gpio_set_value(8, 1); + gpio_set_value(13, 1); + msleep(1000); + gpio_set_value(9, 1); + gpio_set_value(12, 1); + } + + return 0; +} + +late_initcall(usb_fix_for_tmcs); From 5a79133d57fcc22c05cf05f898a31462f681b450 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 039/130] AOSCOS: MIPS: Loongson 3: Add EC resources accessing and programming support Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/Makefile | 1 + drivers/platform/mips/ec_rom.c | 179 +++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 drivers/platform/mips/ec_rom.c diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile index 4dcbbcfcf7a1e..5929b5ad7b6cd 100644 --- a/drivers/platform/mips/Makefile +++ b/drivers/platform/mips/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_LS2K_RESET) += ls2k-reset.o obj-$(CONFIG_I2C_PIIX4) += emc1412.o sd5075.o tmp75.o obj-$(CONFIG_CPU_HWMON) += wpce_fan.o sbx00_fan.o obj-$(CONFIG_LEMOTE3A_LAPTOP) += lemote3a-laptop.o +obj-m += ec_rom.o diff --git a/drivers/platform/mips/ec_rom.c b/drivers/platform/mips/ec_rom.c new file mode 100644 index 0000000000000..b7b1624191f7a --- /dev/null +++ b/drivers/platform/mips/ec_rom.c @@ -0,0 +1,179 @@ +/* + * EC(Embedded Controller) WPCE775L misc device driver on Linux + * Author : Huang Wei + * Date : 2010-06-28 + * + * NOTE : + * 1, The EC resources accessing and programming are supported. + */ + +/*******************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* for ioremap(offset, size) */ + +#include "ec_wpce775l.h" + +/*******************************************************************/ + +/* the register operation access struct */ +struct ec_reg { + u32 addr; /* the address is EC flash address and ACPI command */ + u8 index; /* the index is ACPI command's index */ + u16 val; /* the val is EC flash data and EC space value */ + u8 flag; /* Different access methods. */ +}; + +const char *version = EC_VERSION; + +/* Ec misc device name */ +#define EC_MISC_DEV "ec_misc" + +/* Ec misc device minor number */ +#define ECMISC_MINOR_DEV MISC_DYNAMIC_MINOR + +#define EC_IOC_MAGIC 'E' + +/* misc ioctl operations */ +#define IOCTL_RDREG _IOR(EC_IOC_MAGIC, 1, int) +#define IOCTL_WRREG _IOW(EC_IOC_MAGIC, 2, int) + +/* ioctl */ +static int misc_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) +{ + void __user *ptr = (void __user *)arg; + struct ec_reg *ecreg = (struct ec_reg *)(filp->private_data); + int ret = 0; + + switch (cmd) { + case IOCTL_RDREG: + ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg)); + if (ret) { + printk(KERN_ERR "ACPI command read : copy from user error.\n"); + return -EFAULT; + } + + if (ecreg->flag == 0) { + /* has index read ACPI command */ + ecreg->val = (unsigned short)ec_read_all((unsigned char)ecreg->addr, ecreg->index); + } else if (ecreg->flag == 1) { + /* no index read ACPI command */ + ecreg->val = (unsigned short)ec_read_noindex((unsigned char)ecreg->addr); + } + ret = copy_to_user(ptr, ecreg, sizeof(struct ec_reg)); + if (ret) { + printk(KERN_ERR "ACPI command read : copy to user error.\n"); + return -EFAULT; + } + break; + case IOCTL_WRREG: + ret = copy_from_user(ecreg, ptr, sizeof(struct ec_reg)); + if (ret) { + printk(KERN_ERR "WCB and ACPI command write : copy from user error.\n"); + return -EFAULT; + } + + if (ecreg->flag == 1) { + /* write no index command */ + ec_write_noindex((unsigned char)ecreg->addr, (unsigned char)ecreg->val); + } else { + /* write has index command */ + ec_write_all((unsigned char)ecreg->addr, (unsigned char)ecreg->index, (unsigned char)ecreg->val); + } + break; + + default : + break; + } + + return 0; +} + +static long misc_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + return misc_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); +} + +static int misc_open(struct inode * inode, struct file * filp) +{ + struct ec_reg *ecreg = NULL; + ecreg = kmalloc(sizeof(struct ec_reg), GFP_KERNEL); + if (ecreg) { + filp->private_data = ecreg; + } + + return ecreg ? 0 : -ENOMEM; +} + +static int misc_release(struct inode * inode, struct file * filp) +{ + struct ec_reg *ecreg = (struct ec_reg *)(filp->private_data); + + filp->private_data = NULL; + kfree(ecreg); + + return 0; +} + +static struct file_operations ecmisc_fops = { + .owner = THIS_MODULE, + .open = misc_open, + .release = misc_release, + .read = NULL, + .write = NULL, +#ifdef CONFIG_64BIT + .compat_ioctl = misc_compat_ioctl, +#else + .ioctl = misc_ioctl, +#endif +}; + +/*********************************************************/ + +static struct miscdevice ecmisc_device = { + .minor = ECMISC_MINOR_DEV, + .name = EC_MISC_DEV, + .fops = &ecmisc_fops +}; + +static int __init ecmisc_init(void) +{ + int ret; + + ret = misc_register(&ecmisc_device); + printk(KERN_INFO "EC misc device init V%s.\n", version); + + return ret; +} + +static void __exit ecmisc_exit(void) +{ + misc_deregister(&ecmisc_device); + printk(KERN_INFO "EC misc device exit.\n"); +} + +module_init(ecmisc_init); +module_exit(ecmisc_exit); + +MODULE_AUTHOR("Huang Wei "); +MODULE_DESCRIPTION("WPCE775L resources misc Management"); +MODULE_LICENSE("GPL"); From 1dde164175e94a53797e7c00def67a71fa769622 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 040/130] AOSCOS: MIPS: Loongson: Add PMON read/write in OS support PMON is the firmware(BIOS) of Loongson. Usage: modprobe mtd modprobe mtdblock modprobe pmon_flash dd if=pmon.bin of=/dev/mtdblcok0 Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/Makefile | 4 ++ drivers/platform/mips/pmon_flash.c | 100 +++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 drivers/platform/mips/pmon_flash.c diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile index 5929b5ad7b6cd..98eef2e092264 100644 --- a/drivers/platform/mips/Makefile +++ b/drivers/platform/mips/Makefile @@ -6,3 +6,7 @@ obj-$(CONFIG_I2C_PIIX4) += emc1412.o sd5075.o tmp75.o obj-$(CONFIG_CPU_HWMON) += wpce_fan.o sbx00_fan.o obj-$(CONFIG_LEMOTE3A_LAPTOP) += lemote3a-laptop.o obj-m += ec_rom.o + +ifdef CONFIG_MTD +obj-m += pmon_flash.o +endif diff --git a/drivers/platform/mips/pmon_flash.c b/drivers/platform/mips/pmon_flash.c new file mode 100644 index 0000000000000..8fd5f4bf8b1bb --- /dev/null +++ b/drivers/platform/mips/pmon_flash.c @@ -0,0 +1,100 @@ +/* + * Copyright www.lemote.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FLASH_PHYS_ADDR 0x1fc00000 +#define FLASH_SIZE 0x100000 + +#define FLASH_PARTITION0_ADDR 0x00000000 +#define FLASH_PARTITION0_SIZE 0x00100000 + +struct map_info flash_map = { + .name = "flash device", + .size = FLASH_SIZE, + .bankwidth = 1, +}; + +struct mtd_partition flash_parts[] = { + { + .name = "Bootloader", + .offset = FLASH_PARTITION0_ADDR, + .size = FLASH_PARTITION0_SIZE + }, +}; + +#define PARTITION_COUNT ARRAY_SIZE(flash_parts) + +static struct mtd_info *mymtd; + +int __init init_flash(void) +{ + printk(KERN_NOTICE "Flash flash device: %x at %x\n", + FLASH_SIZE, FLASH_PHYS_ADDR); + + flash_map.phys = FLASH_PHYS_ADDR; + flash_map.virt = ioremap(FLASH_PHYS_ADDR, + FLASH_SIZE); + + if (!flash_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&flash_map); + + mymtd = do_map_probe("jedec_probe", &flash_map); + if (mymtd) { + mtd_device_register(mymtd, flash_parts, PARTITION_COUNT); + printk(KERN_NOTICE "pmon flash device initialized\n"); + return 0; + } + + iounmap((void *)flash_map.virt); + return -ENXIO; +} + +static void __exit cleanup_flash(void) +{ + if (mymtd) { + mtd_device_unregister(mymtd); + map_destroy(mymtd); + } + if (flash_map.virt) { + iounmap((void *)flash_map.virt); + flash_map.virt = 0; + } +} + +module_init(init_flash); +module_exit(cleanup_flash); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yanhua"); +MODULE_DESCRIPTION("MTD map driver for pmon programming module"); From b9cd4b6986b4182475202a00e465323c906f1d2d Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 5 Dec 2024 16:19:39 +0800 Subject: [PATCH 041/130] AOSCOS: platform: pmon_flash: mark init_flash() function as static Suppress a missing prototypes warning during build. Fixes: "AOSCOS: MIPS: Loongson: Add PMON read/write in OS support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/pmon_flash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mips/pmon_flash.c b/drivers/platform/mips/pmon_flash.c index 8fd5f4bf8b1bb..6c5a3c4342b29 100644 --- a/drivers/platform/mips/pmon_flash.c +++ b/drivers/platform/mips/pmon_flash.c @@ -53,7 +53,7 @@ struct mtd_partition flash_parts[] = { static struct mtd_info *mymtd; -int __init init_flash(void) +static int __init init_flash(void) { printk(KERN_NOTICE "Flash flash device: %x at %x\n", FLASH_SIZE, FLASH_PHYS_ADDR); From dad2f4cf68dbb9dac2da5117a410d57c298726ca Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 042/130] AOSCOS: MIPS: Loongson: AT24c04 support for Loongson-3 Signed-off-by: Binbin Zhou Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/Makefile | 2 +- drivers/platform/mips/at24c04.c | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/mips/at24c04.c diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile index 98eef2e092264..4dde0988fdadb 100644 --- a/drivers/platform/mips/Makefile +++ b/drivers/platform/mips/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_LS2K_RESET) += ls2k-reset.o obj-$(CONFIG_I2C_PIIX4) += emc1412.o sd5075.o tmp75.o obj-$(CONFIG_CPU_HWMON) += wpce_fan.o sbx00_fan.o obj-$(CONFIG_LEMOTE3A_LAPTOP) += lemote3a-laptop.o -obj-m += ec_rom.o +obj-m += ec_rom.o at24c04.o ifdef CONFIG_MTD obj-m += pmon_flash.o diff --git a/drivers/platform/mips/at24c04.c b/drivers/platform/mips/at24c04.c new file mode 100644 index 0000000000000..7ba3d1c8692e1 --- /dev/null +++ b/drivers/platform/mips/at24c04.c @@ -0,0 +1,26 @@ +#include + +static int __init at24c04_init(void) +{ + printk("====Please use device-tree for AT24 Driver====\n"); + printk("You can put the at24 node under the i2c node:\n" + "eeprom@51 {\n" + " compatible = \"atmel,24c04\"\n" + " reg = <0x51>\n" + " size = <512>\n" + " pagesize = <16>\n" + "}\n"); + printk("==================Thank you!==================\n"); + return 0; +} + +static void __exit at24c04_exit(void) +{ +} + +module_init(at24c04_init); +module_exit(at24c04_exit); + +MODULE_AUTHOR("Binbin Zhou "); +MODULE_DESCRIPTION("AT24c04 driver, based on the at24 driver"); +MODULE_LICENSE("GPL"); From bf16d1231d426d543105b33124da95d276dd987d Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 043/130] AOSCOS: Add ioremap.h for loongson platform Add loongson specific plat_ioremap to utilize uncached acceleration feature. Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit --- .../include/asm/mach-loongson64/ioremap.h | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 arch/mips/include/asm/mach-loongson64/ioremap.h diff --git a/arch/mips/include/asm/mach-loongson64/ioremap.h b/arch/mips/include/asm/mach-loongson64/ioremap.h new file mode 100644 index 0000000000000..155dafe2ef346 --- /dev/null +++ b/arch/mips/include/asm/mach-loongson64/ioremap.h @@ -0,0 +1,44 @@ +/* + * include/asm-mips/mach-loongson64/ioremap.h + * + * 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. + */ +#ifndef __ASM_MACH_LOONGSON_IOREMAP_H +#define __ASM_MACH_LOONGSON_IOREMAP_H + +#include + +/* + * Allow physical addresses to be fixed up to help peripherals located + * outside the low 32-bit range -- generic pass-through version. + */ +static inline phys_addr_t fixup_bigphys_addr(phys_addr_t phys_addr, phys_addr_t size) +{ + return phys_addr; +} + +/* Add support for uncached accelerate */ +static inline void __iomem *plat_ioremap(phys_addr_t offset, unsigned long size, + unsigned long flags) +{ +#ifdef CONFIG_64BIT +#define LOONGSON_UNCACHED_ACCEL_BASE _AC(0xb800000000000000, UL) + u64 base = LOONGSON_UNCACHED_ACCEL_BASE; + + if (flags == _CACHE_UNCACHED_ACCELERATED && + offset + size < XKSEG - base) { + return (void __iomem *)(unsigned long) (base + offset); + } +#endif + return NULL; +} + +static inline int plat_iounmap(const volatile void __iomem *addr) +{ + return 0; +} + +#endif /* __ASM_MACH_LOONGSON_IOREMAP_H */ From edb695a03c9edfda731c8af67039b1e890e2c4bf Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 044/130] AOSCOS: Fix touchpad status error after STR/STD Signed-off-by: Rui Wang Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/input/mouse/sentelic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c index 2716d2ba386a1..ed3ad58fd393b 100644 --- a/drivers/input/mouse/sentelic.c +++ b/drivers/input/mouse/sentelic.c @@ -899,8 +899,8 @@ static int fsp_activate_protocol(struct psmouse *psmouse) pad->flags |= FSPDRV_FLAG_EN_OPC; /* Enable on-pad vertical and horizontal scrolling */ - fsp_onpad_vscr(psmouse, true); - fsp_onpad_hscr(psmouse, true); + fsp_onpad_vscr(psmouse, pad->vscroll); + fsp_onpad_hscr(psmouse, pad->hscroll); } else { /* Enable absolute coordinates output for Cx/Dx hardware */ if (fsp_reg_write(psmouse, FSP_REG_SWC1, From ad7e630b7cecfbf027c305e67cce60e495c8cfa8 Mon Sep 17 00:00:00 2001 From: Yao Wang Date: Fri, 5 Jan 2018 11:28:18 +0800 Subject: [PATCH 045/130] AOSCOS: GPIO: Add NCT6102 GPIO driver support [Mingcong Bai: Resolved minor merge conflicts in drivers/gpio/Kconfig and drivers/gpio/Makefile.] Signed-off-by: Yao Wang Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpio/Kconfig | 7 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-nct6102.c | 191 ++++++++++++++++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 drivers/gpio/gpio-nct6102.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f068798efa63b..f209015152671 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -482,6 +482,13 @@ config GPIO_MXS select GPIO_GENERIC select GENERIC_IRQ_CHIP +config GPIO_NCT6102 + bool "NCT6102 GPIO driver" + def_bool y + depends on CPU_LOONGSON3 + help + Say yes here to support the NCT6102 GPIO device + config GPIO_OCTEON tristate "Cavium OCTEON GPIO" depends on CAVIUM_OCTEON_SOC diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 8fcc69932b04a..3bf93c81f2b96 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -116,6 +116,7 @@ obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o +obj-$(CONFIG_GPIO_NCT6102) += gpio-nct6102.o obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o obj-$(CONFIG_GPIO_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o diff --git a/drivers/gpio/gpio-nct6102.c b/drivers/gpio/gpio-nct6102.c new file mode 100644 index 0000000000000..25efc588c7612 --- /dev/null +++ b/drivers/gpio/gpio-nct6102.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define NCT6102_GPIO_BASE 80 + +#define EFIR_ADDR 0x2E +#define EFDR_ADDR 0x2F + +#define SIO_NCT6102_ID 0xc450 +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_LDSEL 0x7 /* Logical device select */ +#define SIO_REG_MFS 0x1C /* Multi Function Selection */ +#define SIO_GP2X_DISABLE 0x02 /* Disable GP2x */ +#define SIO_DEV_GPIO 0x7 /* Logical device 7(GPIO) */ +#define SIO_GPIO_DESC_BASE 0xE0 /* GPIO description base address */ +#define SIO_GPIO_DATA_BASE 0xE1 /* GPIO data base address*/ +#define SIO_GPIO_ENABLE 0x30 /* GPIO enable register */ + +#define REG_ADDR_OFFSET(x) ((x / 8) * 4) +#define REG_VALUE(x) (x % 8) + +static DEFINE_SPINLOCK(gpio_lock); + +/* + read/write_gbl() is used to read/write Global register. + */ +static unsigned char read_gbl(u8 gbl_reg) +{ + u8 ret = 0; + + /* Enter the Extended Function Mode */ + outb(0x87, EFIR_ADDR); + outb(0x87, EFIR_ADDR); + /* Read Global Reg */ + outb(gbl_reg, EFIR_ADDR); + ret = inb(EFDR_ADDR); + /* Exit the Extended Function Mode */ + outb(0xAA, EFIR_ADDR); + + return ret; +} + +static void write_gbl(u8 val, u8 gbl_reg) +{ + outb(0x87, EFIR_ADDR); + outb(0x87, EFIR_ADDR); + outb(gbl_reg, EFIR_ADDR); + outb(val, EFDR_ADDR); + outb(0xAA, EFIR_ADDR); +} + +/* + read/write_dev() is used to read/write Logical Device register. + */ +static unsigned char read_dev(u8 dev_num, u8 dev_reg) +{ + u8 ret = 0; + + /* Enter the Extended Function Mode */ + outb(0x87, EFIR_ADDR); + outb(0x87, EFIR_ADDR); + /* Point to Logical Device Number Reg */ + outb(SIO_REG_LDSEL, EFIR_ADDR); + /* Select Logical Device x */ + outb(dev_num, EFDR_ADDR); + /* Select Logical Device Reg */ + outb(dev_reg, EFIR_ADDR); + /* Read Logica Device Reg */ + ret = inb(EFDR_ADDR); + /* Exit the Extended Function Mode */ + outb(0xAA, EFIR_ADDR); + + return ret; +} + +static void write_dev(u8 val, u8 dev_num, u8 dev_reg) +{ + outb(0x87, EFIR_ADDR); + outb(0x87, EFIR_ADDR); + outb(SIO_REG_LDSEL, EFIR_ADDR); + outb(dev_num, EFDR_ADDR); + outb(dev_reg, EFIR_ADDR); + outb(val, EFDR_ADDR); + outb(0xAA, EFIR_ADDR); +} + +/* Detect chip */ +static u16 nct6102_chip_detect(void) +{ + u16 tmp; + + tmp = (read_gbl(SIO_REG_DEVID) << 8) | read_gbl(SIO_REG_DEVID + 1); + + return tmp & 0xFFF0; +} + +static int nct6102_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + u8 tmp; + + spin_lock(&gpio_lock); + tmp = read_dev(SIO_DEV_GPIO, SIO_GPIO_DATA_BASE + REG_ADDR_OFFSET(offset)); + tmp &= 1 << REG_VALUE(offset); + spin_unlock(&gpio_lock); + + return tmp; +} + +static void nct6102_gpio_set_value(struct gpio_chip *chip, unsigned offset, int value) +{ + u8 tmp; + + spin_lock(&gpio_lock); + tmp = read_dev(SIO_DEV_GPIO, SIO_GPIO_DATA_BASE + REG_ADDR_OFFSET(offset)); + if(value) + tmp |= 1 << REG_VALUE(offset); + else + tmp &= ~(1 << REG_VALUE(offset)); + write_dev(tmp, SIO_DEV_GPIO, SIO_GPIO_DATA_BASE + REG_ADDR_OFFSET(offset)); + spin_unlock(&gpio_lock); +} + +static int nct6102_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + u8 tmp; + + spin_lock(&gpio_lock); + tmp = read_dev(SIO_DEV_GPIO, SIO_GPIO_DESC_BASE + REG_ADDR_OFFSET(offset)); + tmp |= 1 << REG_VALUE(offset); + write_dev(tmp, SIO_DEV_GPIO, SIO_GPIO_DESC_BASE + REG_ADDR_OFFSET(offset)); + spin_unlock(&gpio_lock); + + return 0; +} + +static int nct6102_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int level) +{ + u8 tmp; + + nct6102_gpio_set_value(chip, offset, level); + spin_lock(&gpio_lock); + tmp = read_dev(SIO_DEV_GPIO, SIO_GPIO_DESC_BASE + REG_ADDR_OFFSET(offset)); + tmp &= ~(1 << REG_VALUE(offset)); + write_dev(tmp, SIO_DEV_GPIO, SIO_GPIO_DESC_BASE + REG_ADDR_OFFSET(offset)); + spin_unlock(&gpio_lock); + + return 0; +} + +static int nct6102_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + u8 val; + + val = read_dev(SIO_DEV_GPIO, SIO_GPIO_ENABLE); + + if (val & 1 << (offset / 8)) + return 0; + + return -EINVAL; +} + +static struct gpio_chip nct6102_chip = { + .label ="nct6102-gpio-chip", + .request = nct6102_gpio_request, + .direction_input = nct6102_gpio_direction_input, + .direction_output = nct6102_gpio_direction_output, + .get = nct6102_gpio_get_value, + .set = nct6102_gpio_set_value, + .base = NCT6102_GPIO_BASE, + .ngpio = 64, +}; + +static int __init nct6102_gpio_setup(void) +{ + int ret; + u16 dev_id; + + /* Detect device */ + dev_id = nct6102_chip_detect(); + if (dev_id != SIO_NCT6102_ID) + return 0; + + return gpiochip_add(&nct6102_chip); +} +subsys_initcall(nct6102_gpio_setup); From f29ff5a55d1d72925114c75f56d38b3e360cd3c3 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 9 Dec 2024 17:24:26 +0800 Subject: [PATCH 046/130] AOSCOS: gpio: use CPU_LOONGSON64 for GPIO_NCT6102 Per commit 30ad29bb4888 ("MIPS: Loongson: Naming style cleanup and rework"), rename the CPU_LOONGSON3 dependency as CPU_LOONGSON64. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f209015152671..e48ade3456fd4 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -485,7 +485,7 @@ config GPIO_MXS config GPIO_NCT6102 bool "NCT6102 GPIO driver" def_bool y - depends on CPU_LOONGSON3 + depends on CPU_LOONGSON64 help Say yes here to support the NCT6102 GPIO device From 50059ec445ab49777bc4dd697dca8609ad97b51a Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 9 Dec 2024 17:25:34 +0800 Subject: [PATCH 047/130] AOSCOS: gpio: gpio-nct6102: add a missing include to This driver was missing an include to , the reference to which was likely forked out at some point after the original driver code was committed back in 2018. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpio/gpio-nct6102.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-nct6102.c b/drivers/gpio/gpio-nct6102.c index 25efc588c7612..547b61008390d 100644 --- a/drivers/gpio/gpio-nct6102.c +++ b/drivers/gpio/gpio-nct6102.c @@ -6,6 +6,7 @@ #include #include #include +#include #define NCT6102_GPIO_BASE 80 From cd1d86f79bd745bac54486acc5577e46844a6b37 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 9 Dec 2024 17:30:53 +0800 Subject: [PATCH 048/130] AOSCOS: gpio: gpio-nct6102: remove unused write_gbl() function Fix an error thrown by `-Werror=unused-function'. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpio/gpio-nct6102.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/gpio/gpio-nct6102.c b/drivers/gpio/gpio-nct6102.c index 547b61008390d..c376f9fd6d076 100644 --- a/drivers/gpio/gpio-nct6102.c +++ b/drivers/gpio/gpio-nct6102.c @@ -47,15 +47,6 @@ static unsigned char read_gbl(u8 gbl_reg) return ret; } -static void write_gbl(u8 val, u8 gbl_reg) -{ - outb(0x87, EFIR_ADDR); - outb(0x87, EFIR_ADDR); - outb(gbl_reg, EFIR_ADDR); - outb(val, EFDR_ADDR); - outb(0xAA, EFIR_ADDR); -} - /* read/write_dev() is used to read/write Logical Device register. */ From 0f466371cbc7eb2f7b3d228b0c79b51a6d518b96 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 9 Dec 2024 17:31:56 +0800 Subject: [PATCH 049/130] AOSCOS: gpio: gpio-nct6102: drop an unused variable in nct6102_gpio_setup() The integer-type variable `ret' was never used in the function, causing an error with `-Werror=unused-variable'. Remove this unused variable. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpio/gpio-nct6102.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-nct6102.c b/drivers/gpio/gpio-nct6102.c index c376f9fd6d076..1bb29ea5f5f2d 100644 --- a/drivers/gpio/gpio-nct6102.c +++ b/drivers/gpio/gpio-nct6102.c @@ -170,7 +170,6 @@ static struct gpio_chip nct6102_chip = { static int __init nct6102_gpio_setup(void) { - int ret; u16 dev_id; /* Detect device */ From 456d8cdc972d54b5a46634adc513bf479e13abc9 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 9 Dec 2024 17:34:17 +0800 Subject: [PATCH 050/130] AOSCOS: gpio: gpio-nct6102: revise gpiochip_add() as gpiochip_add_data() Per commit 3ff1180a39fb ("gpiolib: Remove data-less gpiochip_add() function"), upstream maintainer advise against adding GPIO devices without "driver-private data associated with the chip" but also recommended just using NULL if none was specified. Do the latter as recommended and revise the gpiochip_add() function as gpiochip_add_data() with NULL as the second argument. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpio/gpio-nct6102.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-nct6102.c b/drivers/gpio/gpio-nct6102.c index 1bb29ea5f5f2d..143f0a139e212 100644 --- a/drivers/gpio/gpio-nct6102.c +++ b/drivers/gpio/gpio-nct6102.c @@ -177,6 +177,6 @@ static int __init nct6102_gpio_setup(void) if (dev_id != SIO_NCT6102_ID) return 0; - return gpiochip_add(&nct6102_chip); + return gpiochip_add_data(&nct6102_chip, NULL); } subsys_initcall(nct6102_gpio_setup); From a8659b6c69ea909da6c8b9a513162187b75c9d4a Mon Sep 17 00:00:00 2001 From: Yao Wang Date: Fri, 22 Nov 2019 19:21:01 +0800 Subject: [PATCH 051/130] AOSCOS: hwmon: Add NCT7511 driver support This driver is base on nct7802y. The nct7802y driver is incompatible with nct7511y, so add a nct7511 driver. [Mingcong Bai: Resolved minor merge conflicts in drivers/hwmon/Kconfig and drivers/hwmon/Makefile.] Signed-off-by: Yao Wang Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/nct7511.c | 801 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 813 insertions(+) create mode 100644 drivers/hwmon/nct7511.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 515bf6a00c4b2..d78b187bb3d83 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1596,6 +1596,17 @@ config SENSORS_NCT6775_I2C This driver can also be built as a module. If so, the module will be called nct6775-i2c. +config SENSORS_NCT7511 + tristate "Nuvoton NCT7511Y" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Nuvoton NCT7511Y + hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called nct7511. + config SENSORS_NCT7802 tristate "Nuvoton NCT7802Y" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 065767eec3cde..5f0e08bdc1631 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -166,6 +166,7 @@ obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o nct6775-objs := nct6775-platform.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o obj-$(CONFIG_SENSORS_NCT6775_I2C) += nct6775-i2c.o +obj-$(CONFIG_SENSORS_NCT7511) += nct7511.o obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o diff --git a/drivers/hwmon/nct7511.c b/drivers/hwmon/nct7511.c new file mode 100644 index 0000000000000..a945ffc2ab3d5 --- /dev/null +++ b/drivers/hwmon/nct7511.c @@ -0,0 +1,801 @@ +/* + * nct7511 - Driver for Nuvoton NCT7511Y + * + * Copyright (C) 2014 Guenter Roeck + * Copyright (C) 2019 Ainux + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRVNAME "nct7511" + +static const u8 REG_VOLTAGE[5] = { 0x09, 0x0a, 0x0c, 0x0d, 0x0e }; + +static const u8 REG_VOLTAGE_LIMIT_LSB[2][5] = { + { 0x40, 0x00, 0x42, 0x44, 0x46 }, + { 0x3f, 0x00, 0x41, 0x43, 0x45 }, +}; + +static const u8 REG_VOLTAGE_LIMIT_MSB[5] = { 0x48, 0x00, 0x47, 0x47, 0x48 }; + +static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = { + { 0, 0, 4, 0, 4 }, + { 2, 0, 6, 2, 6 }, +}; + +#define REG_RTD1_MSB 0x01 +#define REG_RTD2_MSB 0x02 +#define REG_LTD_MSB 0x04 +#define REG_TEMP_LSB 0x05 +#define REG_FANCOUNT_MSB 0x10 +#define REG_FANCOUNT_LSB 0x13 +#define REG_DFAS 0x17 +#define REG_LAS 0x18 +#define REG_HAS 0x19 +#define REG_FAS 0x1a +#define REG_CAS 0x1b +#define REG_START 0x21 +#define REG_MODE 0x22 /* 7.2.32 Mode Selection Register */ +#define REG_FAN_ENABLE 0x24 +#define REG_VMON_ENABLE 0x25 +#define REG_RTD1_THL 0x30 +#define REG_RTD1_TLL 0x31 +#define REG_RTD2_THL 0x32 +#define REG_RTD2_TLL 0x33 +#define REG_LTD_THL 0x36 +#define REG_LTD_TLL 0x37 +#define REG_RTD1_CRIT 0x3a +#define REG_RTD2_CRIT 0x3b +#define REG_LTD_CRIT 0x3d +#define REG_FANLIMIT_LSB 0x49 +#define REG_FANLIMIT_MSB 0x4c +#define REG_FANOUTPUT_CRTTYPE 0x5e +#define REG_PWM(x) (0x60 + (x)) +#define REG_SMARTFAN_EN(x) (0x64 + (x) / 2) +#define SMARTFAN_EN_SHIFT(x) ((x) % 2 * 4) +#define REG_VENDOR_ID 0xfd +#define REG_CHIP_ID 0xfe +#define REG_DEVICE_ID 0xff + +/* + * Data structures and manipulation thereof + */ + +struct nct7511_data { + struct regmap *regmap; + struct mutex access_lock; /* for multi-byte read and write operations */ +}; + +static ssize_t show_temp_label(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + + switch (sattr->index) { + case 0: + return sprintf(buf, "%s\n", "RTD1 Temperature"); + break; + case 1: + return sprintf(buf, "%s\n", "RTD2 Temperature"); + break; + case 2: + return sprintf(buf, "%s\n", "LTD Temperature"); + break; + default: + return -EOPNOTSUPP; + } +} + +static ssize_t show_pwm_mode(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct7511_data *data = dev_get_drvdata(dev); + unsigned int regval; + int ret; + + if (sattr->index > 1) + return sprintf(buf, "1\n"); + + ret = regmap_read(data->regmap, REG_FANOUTPUT_CRTTYPE, ®val); + if (ret < 0) + return ret; + return sprintf(buf, "%u\n", regval & (1 << sattr->index)); +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct nct7511_data *data = dev_get_drvdata(dev); + unsigned int val; + int ret; + + if (!attr->index) + return sprintf(buf, "255\n"); + + ret = regmap_read(data->regmap, attr->index, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct nct7511_data *data = dev_get_drvdata(dev); + int err; + u8 val; + + err = kstrtou8(buf, 0, &val); + if (err < 0) + return err; + + err = regmap_write(data->regmap, attr->index, val); + return err ? : count; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nct7511_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + unsigned int reg, enabled; + int ret; + + ret = regmap_read(data->regmap, REG_SMARTFAN_EN(sattr->index), ®); + if (ret < 0) + return ret; + enabled = reg >> SMARTFAN_EN_SHIFT(sattr->index) & 1; + return sprintf(buf, "%u\n", enabled); +} + +static ssize_t store_pwm_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct nct7511_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + u8 val; + int ret; + + ret = kstrtou8(buf, 0, &val); + if (ret < 0) + return ret; + if (val > 1) + return -EINVAL; + ret = regmap_update_bits(data->regmap, REG_SMARTFAN_EN(sattr->index), + 1 << SMARTFAN_EN_SHIFT(sattr->index), + val << SMARTFAN_EN_SHIFT(sattr->index)); + return ret ? : count; +} + +static int nct7511_read_temp(struct nct7511_data *data, + u8 reg_temp, u8 reg_temp_low, int *temp) +{ + unsigned int t1, t2 = 0; + int err; + + *temp = 0; + + mutex_lock(&data->access_lock); + err = regmap_read(data->regmap, reg_temp, &t1); + if (err < 0) + goto abort; + t1 <<= 8; + if (reg_temp_low) { /* 11 bit data */ + err = regmap_read(data->regmap, reg_temp_low, &t2); + if (err < 0) + goto abort; + } + t1 |= t2 & 0xe0; + *temp = (s16)t1 / 32 * 125; +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static int nct7511_read_fan(struct nct7511_data *data, u8 reg_fan) +{ + unsigned int f1, f2; + int ret; + + mutex_lock(&data->access_lock); + ret = regmap_read(data->regmap, reg_fan, &f1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_FANCOUNT_LSB, &f2); + if (ret < 0) + goto abort; + ret = (f1 << 5) | (f2 >> 3); + /* convert fan count to rpm */ + if (ret == 0x1fff) /* maximum value, assume fan is stopped */ + ret = 0; + else if (ret) + ret = DIV_ROUND_CLOSEST(1350000U, ret); +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7511_read_fan_min(struct nct7511_data *data, u8 reg_fan_low, + u8 reg_fan_high) +{ + unsigned int f1, f2; + int ret; + + mutex_lock(&data->access_lock); + ret = regmap_read(data->regmap, reg_fan_low, &f1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, reg_fan_high, &f2); + if (ret < 0) + goto abort; + ret = f1 | ((f2 & 0xf8) << 5); + /* convert fan count to rpm */ + if (ret == 0x1fff) /* maximum value, assume no limit */ + ret = 0; + else if (ret) + ret = DIV_ROUND_CLOSEST(1350000U, ret); + else + ret = 1350000U; +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7511_write_fan_min(struct nct7511_data *data, u8 reg_fan_low, + u8 reg_fan_high, unsigned long limit) +{ + int err; + + if (limit) + limit = DIV_ROUND_CLOSEST(1350000U, limit); + else + limit = 0x1fff; + limit = clamp_val(limit, 0, 0x1fff); + + mutex_lock(&data->access_lock); + err = regmap_write(data->regmap, reg_fan_low, limit & 0xff); + if (err < 0) + goto abort; + + err = regmap_write(data->regmap, reg_fan_high, (limit & 0x1f00) >> 5); +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct7511_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int err, temp; + + err = nct7511_read_temp(data, sattr->nr, sattr->index, &temp); + if (err < 0) + return err; + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t store_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7511_data *data = dev_get_drvdata(dev); + int nr = sattr->nr; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); + + err = regmap_write(data->regmap, nr, val & 0xff); + return err ? : count; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct7511_data *data = dev_get_drvdata(dev); + int speed; + + speed = nct7511_read_fan(data, sattr->index); + if (speed < 0) + return speed; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7511_data *data = dev_get_drvdata(dev); + int speed; + + speed = nct7511_read_fan_min(data, sattr->nr, sattr->index); + if (speed < 0) + return speed; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t store_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7511_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + err = nct7511_write_fan_min(data, sattr->nr, sattr->index, val); + return err ? : count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct7511_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int bit = sattr->index; + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, sattr->nr, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", !!(val & (1 << bit))); +} + +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, + show_temp_label, NULL, 0); +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, REG_RTD1_MSB, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, REG_RTD1_TLL, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, REG_RTD1_THL, 0); +static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, REG_RTD1_CRIT, 0); + +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, + show_temp_label, NULL, 1); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, REG_RTD2_MSB, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, REG_RTD2_TLL, 0); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, REG_RTD2_THL, 0); +static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, REG_RTD2_CRIT, 0); + +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, + show_temp_label, NULL, 2); +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, REG_LTD_MSB, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, REG_LTD_TLL, 0); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, REG_LTD_THL, 0); +static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, REG_LTD_CRIT, 0); + +static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_alarm, NULL, + REG_LAS, 0); +static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_alarm, NULL, + REG_LAS, 1); +static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_alarm, NULL, + REG_LAS, 2); + +static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_alarm, NULL, + REG_HAS, 0); +static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_alarm, NULL, + REG_HAS, 1); +static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_alarm, NULL, + REG_HAS, 2); + +static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, + REG_CAS, 0); +static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, + REG_CAS, 1); +static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, + REG_CAS, 2); + +static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_alarm, NULL, REG_DFAS, 0); +static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_alarm, NULL, REG_DFAS, 1); +static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_alarm, NULL, REG_DFAS, 2); + +static struct attribute *nct7511_temp_attrs[] = { + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + + &sensor_dev_attr_temp2_label.dev_attr.attr, /* 9 */ + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + + &sensor_dev_attr_temp3_label.dev_attr.attr, /* 18 */ + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + + NULL +}; + +static umode_t nct7511_temp_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7511_data *data = dev_get_drvdata(dev); + unsigned int reg; + int err; + + err = regmap_read(data->regmap, REG_MODE, ®); + if (err < 0) + return 0; + + if (index < 10 && (reg & 0x03) != 0x01) /* RD1 */ + return 0; + + if (index >= 10 && index < 20 && (reg & 0x0c) != 0x04) /* RD2 */ + return 0; + + if (index >= 30 && index < 38 && (reg & 0x40) != 0x40) /* LTD */ + return 0; + + return attr->mode; +} + +static const struct attribute_group nct7511_temp_group = { + .attrs = nct7511_temp_attrs, + .is_visible = nct7511_temp_is_visible, +}; + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, REG_FANCOUNT_MSB); +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, REG_FANLIMIT_LSB, REG_FANLIMIT_MSB); +static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, show_alarm, NULL, REG_FAS, 0); + +/* 7.2.42 Fan Control Output Type */ +static SENSOR_DEVICE_ATTR(pwm1_mode, S_IRUGO, show_pwm_mode, NULL, 0); + +/* 7.2.44 Fan Control Output Value */ +static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, store_pwm, + REG_PWM(0)); + +/* 7.2.46... Temperature to Fan mapping Relationships Register */ +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + store_pwm_enable, 0); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + store_pwm_enable, 1); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + store_pwm_enable, 2); + +static struct attribute *nct7511_fan_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + + NULL +}; + +static umode_t nct7511_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7511_data *data = dev_get_drvdata(dev); + int fan = index / 4; /* 4 attributes per fan */ + unsigned int reg; + int err; + + err = regmap_read(data->regmap, REG_FAN_ENABLE, ®); + if (err < 0 || !(reg & (1 << fan))) + return 0; + + return attr->mode; +} + +static const struct attribute_group nct7511_fan_group = { + .attrs = nct7511_fan_attrs, + .is_visible = nct7511_fan_is_visible, +}; + +static struct attribute *nct7511_pwm_attrs[] = { + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_mode.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct7511_pwm_group = { + .attrs = nct7511_pwm_attrs, +}; + +/* 7.2.63... 0x80-0x83, 0x84 Temperature (X-axis) transition */ +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x80, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x81, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x82, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point4_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x83, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point5_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x84, 0); + +/* 7.2.63... 0x85-0x88 PWM (Y-axis) transition */ +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x85); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x86); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x87); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x88); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm, NULL, 0); + +/* 7.2.63 Table 2 X-axis Transition Point 1 Register */ +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x90, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x91, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x92, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point4_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x93, 0); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point5_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0x94, 0); + +/* 7.2.63 Table 2 Y-axis Transition Point 1 Register */ +static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x95); +static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x96); +static SENSOR_DEVICE_ATTR(pwm2_auto_point3_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x97); +static SENSOR_DEVICE_ATTR(pwm2_auto_point4_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0x98); +static SENSOR_DEVICE_ATTR(pwm2_auto_point5_pwm, S_IRUGO, show_pwm, NULL, 0); + +/* 7.2.63 Table 3 X-axis Transition Point 1 Register */ +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA0, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA1, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA2, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point4_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA3, 0); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point5_temp, S_IRUGO | S_IWUSR, + show_temp, store_temp, 0xA4, 0); + +/* 7.2.63 Table 3 Y-axis Transition Point 1 Register */ +static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0xA5); +static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0xA6); +static SENSOR_DEVICE_ATTR(pwm3_auto_point3_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0xA7); +static SENSOR_DEVICE_ATTR(pwm3_auto_point4_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0xA8); +static SENSOR_DEVICE_ATTR(pwm3_auto_point5_pwm, S_IRUGO, show_pwm, NULL, 0); + +static struct attribute *nct7511_auto_point_attrs[] = { + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, + + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, + + &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point5_temp.dev_attr.attr, + + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point5_pwm.dev_attr.attr, + + &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point5_temp.dev_attr.attr, + + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point5_pwm.dev_attr.attr, + + NULL +}; + +static const struct attribute_group nct7511_auto_point_group = { + .attrs = nct7511_auto_point_attrs, +}; + +static const struct attribute_group *nct7511_groups[] = { + &nct7511_temp_group, + &nct7511_fan_group, + &nct7511_pwm_group, + &nct7511_auto_point_group, + NULL +}; + +static int nct7511_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + int reg; + + reg = i2c_smbus_read_byte_data(client, REG_VENDOR_ID); + if (reg != 0x50) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_CHIP_ID); + if (reg != 0xc3) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_DEVICE_ID); + /* Device id range is 2xh(x = 0,1,2...) */ + if (reg < 0x20 || reg > 0x2f) + return -ENODEV; + + /* Also validate lower bits of temperature registers */ + reg = i2c_smbus_read_byte_data(client, REG_TEMP_LSB); + if (reg < 0 || (reg & 0x1f)) + return -ENODEV; + + strlcpy(info->type, "nct7511", I2C_NAME_SIZE); + return 0; +} + +static bool nct7511_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return (reg <= 0x20) || + (reg >= REG_PWM(0) && reg <= REG_PWM(2)); +} + +static const struct regmap_config nct7511_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = nct7511_regmap_is_volatile, +}; + +static int nct7511_init_chip(struct nct7511_data *data) +{ + int err; + + /* Enable ADC */ + err = regmap_update_bits(data->regmap, REG_START, 0x01, 0x01); + if (err) + return err; + + /* Enable local temperature sensor */ + err = regmap_update_bits(data->regmap, REG_MODE, 0x40, 0x40); + if (err) + return err; + + /* Enable Vcore and VCC voltage monitoring */ + return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03); +} + +static int nct7511_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct nct7511_data *data; + struct device *hwmon_dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &nct7511_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + mutex_init(&data->access_lock); + + ret = nct7511_init_chip(data); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + nct7511_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const unsigned short nct7511_address_list[] = { + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END +}; + +static const struct i2c_device_id nct7511_idtable[] = { + { "nct7511", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct7511_idtable); + +static struct i2c_driver nct7511_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .detect = nct7511_detect, + .probe = nct7511_probe, + .id_table = nct7511_idtable, + .address_list = nct7511_address_list, +}; + +module_i2c_driver(nct7511_driver); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_AUTHOR("Ainux "); +MODULE_DESCRIPTION("NCT7511Y Hardware Monitoring Driver"); +MODULE_LICENSE("GPL v2"); From 7e57a2fe0885364c1fc744e717a4835d97b0507a Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 9 Dec 2024 17:58:37 +0800 Subject: [PATCH 052/130] AOSCOS: hwmon: nct7511: replace deprecated strlcpy() with strscpy() The string function strlcpy() was removed in commit d26270061ae6 ("string: Remove strlcpy()") and per deprecated.rst: strlcpy() reads the entire source buffer first (since the return value is meant to match that of strlen()). This read may exceed the destination size limit. This is both inefficient and can lead to linear read overflows if a source string is not NUL-terminated. The safe replacement is strscpy(), though care must be given to any cases where the return value of strlcpy() is used, since strscpy() will return negative errno values when it truncates. Replace calls to strlcpy() with strscpy(). Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/hwmon/nct7511.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/nct7511.c b/drivers/hwmon/nct7511.c index a945ffc2ab3d5..e5006ecd83465 100644 --- a/drivers/hwmon/nct7511.c +++ b/drivers/hwmon/nct7511.c @@ -709,7 +709,7 @@ static int nct7511_detect(struct i2c_client *client, if (reg < 0 || (reg & 0x1f)) return -ENODEV; - strlcpy(info->type, "nct7511", I2C_NAME_SIZE); + strscpy(info->type, "nct7511", I2C_NAME_SIZE); return 0; } From 450aeb48e6b64ff8020ab458e3a516a8fd7e9c2b Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 9 Dec 2024 18:04:57 +0800 Subject: [PATCH 053/130] AOSCOS: hwmon: nct7511: revise .probe() in struct i2c_driver Since commit 03c835f498b5 ("i2c: Switch .probe() to not take an id parameter"), the `.probe()' member function only takes an i2c_client as its sole parameter. Remove the second `i2c_device_id' parameter. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/hwmon/nct7511.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hwmon/nct7511.c b/drivers/hwmon/nct7511.c index e5006ecd83465..808755e2c37d5 100644 --- a/drivers/hwmon/nct7511.c +++ b/drivers/hwmon/nct7511.c @@ -744,8 +744,7 @@ static int nct7511_init_chip(struct nct7511_data *data) return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03); } -static int nct7511_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int nct7511_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct nct7511_data *data; From 44065b9861a1029025d1eff9a5357ff63cdad537 Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 054/130] AOSCOS: snd/hda/patch_conexant: add proc_widget_hook Export non-std/vendor defined node 0x27 [Mingcong Bai: Resolved a minor merge conflict in sound/pci/hda/patch_conexant.c.] Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit --- sound/pci/hda/patch_conexant.c | 74 ++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index b3208b068dd80..84ee9a4a98d4d 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1142,6 +1142,78 @@ static void add_cx5051_fake_mutes(struct hda_codec *codec) spec->gen.dac_min_mute = true; } +#ifdef CONFIG_PROC_FS +static void cxt5066_proc_hook(struct snd_info_buffer *buffer, + struct hda_codec *codec, hda_nid_t nid) +{ + if (nid == codec->core.afg) { + unsigned int res; + const struct { + unsigned int val; + const char *desc; + } speaker_power_map[] = { + { 0x10, "2.00 1.00 N/A N/A (Unit:W)" }, + { 0x14, "1.80 0.90 N/A N/A (Unit:W)" }, + { 0x1c, "1.50 1.00 N/A N/A (Unit:W)" }, + { 0x20, "1.40 1.00 N/A N/A (Unit:W)" }, + { 0x24, "1.20 1.00 N/A N/A (Unit:W)" }, + { 0x28, "1.00 0.50 N/A N/A (Unit:W)" }, + { 0x2c, "0.80 0.40 N/A N/A (Unit:W)" }, + { 0x30, "0.60 0.30 N/A N/A (Unit:W)" }, + { 0x34, "0.50 0.25 2.00 1.00 (Unit:W)" }, + { 0x38, "0.40 0.20 1.60 0.80 (Unit:W)" }, + { 0x3c, "0.25 0.13 1.00 0.50 (Unit:W)" }, + { -1, "???" } + }; + int i; + + snd_iprintf(buffer, "Node 0x27 [Vendor Defined Widget]:\n"); + + snd_hda_codec_exec_verb(codec, 0x27a2000, &res); + snd_iprintf(buffer, " PC beep: %s\n", + res == 0x0 ? "independent mode(default)" : + res == 0x2 ? "mixed mode" : "???"); + + snd_hda_codec_exec_verb(codec, 0x27a7000, &res); + snd_iprintf(buffer, " Class D: %s\n", + res == 0x0 ? "stereo mode(default)" : + res == 0x10 ? "mono mode" : "???"); + + snd_hda_codec_exec_verb(codec, 0x27bf000, &res); + for (i = 0; + speaker_power_map[i].val != -1 && + speaker_power_map[i].val != res; + i++) {} + snd_iprintf(buffer, " Speaker power: %s\n", + speaker_power_map[i].desc); + + snd_hda_codec_exec_verb(codec, 0x01f1f00, &res); + snd_iprintf(buffer, " GS Mark: %s\n", + res == 0x0 ? "Disabled" : + res == 0x1 ? "Enabled" : "???"); + + snd_hda_codec_exec_verb(codec, 0x27a9000, &res); + if (res > 0x0 && res <= 0x3f) + snd_iprintf(buffer, " High pass filter: %dHz\n", res*30); + else + snd_iprintf(buffer, " High pass filter: %s\n", + res == 0x0 ? "120Hz(default)" : "???"); + + snd_hda_codec_exec_verb(codec, 0x27aa008, &res); + if (res > 0x0 && res <= 0x3f) + snd_iprintf(buffer, " Low pass filter: %dHz\n", res*15); + else + snd_iprintf(buffer, " Low pass filter: %s\n", + res == 0x0 ? "Disabled" : "???"); + + snd_hda_codec_exec_verb(codec, 0x27b4100, &res); + snd_iprintf(buffer, " Adjust 3.3V LDO voltage: 0x%x\n", res); + } +} +#else +#define cxt5066_proc_hook NULL +#endif + static int patch_conexant_auto(struct hda_codec *codec) { struct conexant_spec *spec; @@ -1165,6 +1237,8 @@ static int patch_conexant_auto(struct hda_codec *codec) break; } + codec->proc_widget_hook = cxt5066_proc_hook; + cx_auto_parse_eapd(codec); spec->gen.own_eapd_ctl = 1; From 34bb5a52e9d08e7643ad3860d2e3bd0fc88d5c6e Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 055/130] AOSCOS: snd-hda-codec: new symbol snd_hda_codec_exec_verb snd_hda_codec_exec_verb() wraps&exports codec_exec_verb() Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: chenj Signed-off-by: Kexy Biscuit --- include/sound/hda_codec.h | 2 ++ sound/pci/hda/hda_codec.c | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 5497dc9c396a5..940f5be340ff3 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -371,6 +371,8 @@ struct hda_verb { u32 param; }; +int snd_hda_codec_exec_verb(struct hda_codec *codec, unsigned int cmd, + unsigned int *res); void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 33af707a65ab1..2031360133790 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -68,6 +68,24 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, return err; } +int snd_hda_codec_exec_verb(struct hda_codec *codec, unsigned int raw_cmd, + unsigned int *res) +{ + unsigned cmd; + unsigned int _res; + int r; + + cmd = (u32)codec->addr << 28; + cmd |= raw_cmd; + + r = codec_exec_verb(&codec->core, cmd, 0, (res || codec->bus->core.sync_write) ? &_res : NULL); + if (res) + *res = _res; + + return r; +} +EXPORT_SYMBOL_GPL(snd_hda_codec_exec_verb); + /** * snd_hda_sequence_write - sequence writes * @codec: the HDA codec From 913c90104990d3456ac5f8589431b1b96343f86d Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 056/130] AOSCOS: snd/hda/conexant: add support for raw verbs [Mingcong Bai: Resolved a minor merge conflict in sound/pci/hda/patch_conexant.c.] Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit --- sound/pci/hda/patch_conexant.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 84ee9a4a98d4d..bcfe286a123ba 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -43,6 +43,9 @@ struct conexant_spec { unsigned int gpio_mute_led_mask; unsigned int gpio_mic_led_mask; bool is_cx8070_sn6140; + + const unsigned int *raw_init_verbs[5]; + unsigned int num_raw_init_verbs; }; @@ -187,7 +190,13 @@ static void cx_fixup_headset_recog(struct hda_codec *codec) static int cx_auto_init(struct hda_codec *codec) { + int i, j; struct conexant_spec *spec = codec->spec; + + for (i = 0; i < spec->num_raw_init_verbs; i++) + for (j = 0; spec->raw_init_verbs[i][j] != -1; j++) + snd_hda_codec_exec_verb(codec, spec->raw_init_verbs[i][j], NULL); + snd_hda_gen_init(codec); if (!spec->dynamic_eapd) cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); From 2b2aad7d3ee65b02e89300e6f6b31f698b77e9fc Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 057/130] AOSCOS: snd/hda/conexant: Add support for lemote A1205 Lemote A1205 is a all in one product. Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- sound/pci/hda/patch_conexant.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index bcfe286a123ba..9abb64c9c467e 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -299,6 +299,7 @@ enum { CXT_FIXUP_STEREO_DMIC, CXT_PINCFG_LENOVO_NOTEBOOK, CXT_FIXUP_INC_MIC_BOOST, + CXT_FIXUP_SET_RAW_VERBS, CXT_FIXUP_HEADPHONE_MIC_PIN, CXT_FIXUP_HEADPHONE_MIC, CXT_FIXUP_GPIO1, @@ -325,6 +326,12 @@ enum { /* for hda_fixup_thinkpad_acpi() */ #include "thinkpad_helper.c" +static unsigned int cxt5066_raw_init_verbs_lemote_aio_a1205[] = { + 0x273f01c, /* Set speaker power to 1.5W@8ohm */ + 0x2729003, /* Set High pass filter to 90Hz */ + -1 /* end */ +}; + static void cxt_fixup_stereo_dmic(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -358,6 +365,19 @@ static void cxt5066_increase_mic_boost(struct hda_codec *codec, (0 << AC_AMPCAP_MUTE_SHIFT)); } +static void cxt5066_set_raw_verbs(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct conexant_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + spec->raw_init_verbs[spec->num_raw_init_verbs] = + cxt5066_raw_init_verbs_lemote_aio_a1205; + spec->num_raw_init_verbs++; +} + static void cxt_update_headset_mode(struct hda_codec *codec) { /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */ @@ -860,6 +880,8 @@ static const struct hda_fixup cxt_fixups[] = { }, [CXT_PINCFG_LEMOTE_A1205] = { .type = HDA_FIXUP_PINS, + .chained = true, + .chain_id = CXT_FIXUP_SET_RAW_VERBS, .v.pins = cxt_pincfg_lemote, }, [CXT_PINCFG_COMPAQ_CQ60] = { @@ -887,6 +909,10 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = cxt5066_increase_mic_boost, }, + [CXT_FIXUP_SET_RAW_VERBS] = { + .type = HDA_FIXUP_FUNC, + .v.func = cxt5066_set_raw_verbs, + }, [CXT_FIXUP_HEADPHONE_MIC_PIN] = { .type = HDA_FIXUP_PINS, .chained = true, From 27d8ee9c1256361307fcb989f1c0bcf7d19567d0 Mon Sep 17 00:00:00 2001 From: xiangy Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 058/130] AOSCOS: add 3g support and ppp config support Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Signed-off-by: xiangy Signed-off-by: Kexy Biscuit --- drivers/usb/serial/option.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 04f511adc0025..955ded694fd09 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2059,6 +2059,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ALINK_VENDOR_ID, ALINK_PRODUCT_3GU, 0xff, 0xff, 0xff) }, { USB_DEVICE(ALINK_VENDOR_ID, SIMCOM_PRODUCT_SIM7100E), .driver_info = RSVD(5) | RSVD(6) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x19f5, 0x9909, 0xff, 0xff, 0xff)}, { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */ .driver_info = RSVD(7) }, From 7953fa4376f8af49a7b4b302f83c8347c998284d Mon Sep 17 00:00:00 2001 From: Xiang Yu Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 059/130] AOSCOS: add rear mic support for CX20631 CX20631 Node1b's default config value should be 0x01A190F0 according to CX20631 datesheet, but the true value is 0x018xxxxx, this cause PortC can't work as rear mic, so rewrite to default value Signed-off-by: Icenowy Zheng Signed-off-by: Xiang Yu Signed-off-by: Kexy Biscuit --- sound/pci/hda/patch_conexant.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 9abb64c9c467e..b5c31cb47c478 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1298,6 +1298,13 @@ static int patch_conexant_auto(struct hda_codec *codec) snd_hda_pick_fixup(codec, cxt5051_fixup_models, cxt5051_fixups, cxt_fixups); break; + /* CX20631 node1b's default value is not same as datasheet, cause rear mic not work */ + case 0x14f15097: + snd_hda_codec_set_pincfg(codec, 0x1b, 0x01a190f0); + codec->pin_amp_workaround = 1; + snd_hda_pick_fixup(codec, cxt5066_fixup_models, + cxt5066_fixups, cxt_fixups); + break; case 0x14f15098: codec->pin_amp_workaround = 1; spec->gen.mixer_nid = 0x22; From 40b9b8acce332dcb58a86b02b22b6b75c3ab73cc Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 060/130] AOSCOS: add rear mic support for CX20641 CX20641 Node1b's default config value should be 0x01A190F0 according to CX20641 datesheet, but the true value is 0x018xxxxx, this cause PortC can't work as rear mic, so rewrite to default value Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- sound/pci/hda/patch_conexant.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index b5c31cb47c478..37f1d5a1dea4c 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -1298,8 +1298,9 @@ static int patch_conexant_auto(struct hda_codec *codec) snd_hda_pick_fixup(codec, cxt5051_fixup_models, cxt5051_fixups, cxt_fixups); break; - /* CX20631 node1b's default value is not same as datasheet, cause rear mic not work */ + /* CX20631/CX20641 node1b's default value is not same as datasheet, cause rear mic not work */ case 0x14f15097: + case 0x14f150a1: snd_hda_codec_set_pincfg(codec, 0x1b, 0x01a190f0); codec->pin_amp_workaround = 1; snd_hda_pick_fixup(codec, cxt5066_fixup_models, From 6bbf3e75fb7c1e563d1b3ee4bf2f83eac83aff0e Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 061/130] AOSCOS: Change some KERN_ERR to KERN_INFO 1, In radeon graphics card. 2, In i8042.c, SB710 has no i8042 controller so selftest timeout. Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/radeon/radeon_atombios.c | 2 +- drivers/gpu/drm/radeon/rs690.c | 2 +- drivers/input/serio/i8042.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index c025ce6eb3167..1850a7c53cdbc 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1317,7 +1317,7 @@ bool radeon_atombios_sideport_present(struct radeon_device *rdev) return true; break; default: - DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev); + DRM_INFO("Unsupported IGP table: %d %d\n", frev, crev); break; } } diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index 016eb4992803d..3189a6ddb6c9f 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -112,7 +112,7 @@ void rs690_pm_info(struct radeon_device *rdev) rdev->pm.igp_system_mclk.full = dfixed_const(200); rdev->pm.igp_ht_link_clk.full = dfixed_const(1000); rdev->pm.igp_ht_link_width.full = dfixed_const(8); - DRM_ERROR("No integrated system info for your GPU, using safe default\n"); + DRM_INFO("No integrated system info for your GPU, using safe default\n"); break; } } else { diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 7db53c74529c6..897e4c9930995 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -959,7 +959,7 @@ static int i8042_controller_selftest(void) do { if (i8042_command(¶m, I8042_CMD_CTL_TEST)) { - pr_err("i8042 controller selftest timeout\n"); + pr_info("i8042 controller selftest timeout\n"); return -ENODEV; } From 7614354b409e300bbab8657d37111e9be0cf2dcb Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 062/130] AOSCOS: E1000E: Detect and recover weird rx hang bug Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/net/ethernet/intel/e1000e/e1000.h | 6 ++ drivers/net/ethernet/intel/e1000e/netdev.c | 76 ++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index ba9c19e6994c9..0e56b992dd3c5 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -228,6 +228,7 @@ struct e1000_adapter { bool detect_tx_hung; bool tx_hang_recheck; + bool rx_hang_recheck; u8 tx_timeout_factor; u32 tx_int_delay; @@ -270,6 +271,10 @@ struct e1000_adapter { u32 alloc_rx_buff_failed; u32 rx_dma_failed; u32 rx_hwtstamp_cleared; + u32 rdh_old; + u32 rdt_old; + u32 rx_ntu_old; + u32 rx_ntc_old; unsigned int rx_ps_pages; u16 rx_ps_bsize0; @@ -315,6 +320,7 @@ struct e1000_adapter { struct work_struct print_hang_task; int phy_hang_count; + int weird_hang_count; u16 tx_ring_count; u16 rx_ring_count; diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 721c098f2bb1b..d62a7e4a887e4 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -637,6 +637,69 @@ static void e1000e_update_tdt_wa(struct e1000_ring *tx_ring, unsigned int i) } } +static void e1000_check_82574_weird_hang(struct e1000_ring *rx_ring, bool ps) +{ + u32 i, rdh, rdt, staterr, next_staterr; + struct e1000_adapter *adapter = rx_ring->adapter; + + if (adapter->link_speed != SPEED_1000) + return; + + i = rx_ring->next_to_clean; + rdh = readl(rx_ring->head); + rdt = readl(rx_ring->tail); + if (ps) { + union e1000_rx_desc_packet_split *rx_desc = E1000_RX_DESC_PS(*rx_ring, i); + union e1000_rx_desc_packet_split *next_rxd = E1000_RX_DESC_PS(*rx_ring, (i+1)%rx_ring->count); + staterr = le32_to_cpu(rx_desc->wb.middle.status_error); + next_staterr = le32_to_cpu(next_rxd->wb.middle.status_error); + } + else { + union e1000_rx_desc_extended *rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); + union e1000_rx_desc_extended *next_rxd = E1000_RX_DESC_EXT(*rx_ring, (i+1)%rx_ring->count); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); + next_staterr = le32_to_cpu(next_rxd->wb.upper.status_error); + } + + /* Rx Hung */ + if (e1000_desc_unused(rx_ring)==0 && rdh==rdt && !(staterr & E1000_RXD_STAT_DD)) { + if (!adapter->rx_hang_recheck) { /* Check Twice*/ + adapter->rx_hang_recheck = true; + adapter->rdh_old = rdh; + adapter->rdt_old = rdt; + adapter->rx_ntu_old = rx_ring->next_to_use; + adapter->rx_ntc_old = rx_ring->next_to_clean; + } + else { + adapter->rx_hang_recheck = false; + if (rdh==adapter->rdh_old && rdt==adapter->rdt_old + && rx_ring->next_to_use==adapter->rx_ntu_old + && rx_ring->next_to_clean==adapter->rx_ntc_old) { + adapter->weird_hang_count++; + e_err("Detected the %dth Weird Rx Hang:\n" + " RDH <%x>\n" + " RDT <%x>\n" + " next_to_use <%x>\n" + " next_to_clean <%x>\n" + " rx_desc status <%x>\n", + adapter->weird_hang_count, + rdh, rdt, + rx_ring->next_to_use, + rx_ring->next_to_clean, + staterr); + + if (next_staterr & E1000_RXD_STAT_DD) { /* Really hang, light recover */ + e_err("Recover by skipping rx_desc...\n"); + rx_ring->next_to_clean = (i+1) % rx_ring->count; + } + else { /* Really hang, heavy recover */ + e_err("Recover by resetting device...\n"); + schedule_work(&adapter->reset_task); + } + } + } + } +} /** * e1000_alloc_rx_buffers - Replace used receive buffers * @rx_ring: Rx descriptor ring @@ -926,6 +989,8 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring, int *work_done, bool cleaned = false; unsigned int total_rx_bytes = 0, total_rx_packets = 0; + e1000_check_82574_weird_hang(rx_ring, false); + i = rx_ring->next_to_clean; rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); staterr = le32_to_cpu(rx_desc->wb.upper.status_error); @@ -1324,6 +1389,8 @@ static bool e1000_clean_rx_irq_ps(struct e1000_ring *rx_ring, int *work_done, bool cleaned = false; unsigned int total_rx_bytes = 0, total_rx_packets = 0; + e1000_check_82574_weird_hang(rx_ring, true); + i = rx_ring->next_to_clean; rx_desc = E1000_RX_DESC_PS(*rx_ring, i); staterr = le32_to_cpu(rx_desc->wb.middle.status_error); @@ -1518,6 +1585,8 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done, unsigned int total_rx_bytes = 0, total_rx_packets = 0; struct skb_shared_info *shinfo; + e1000_check_82574_weird_hang(rx_ring, false); + i = rx_ring->next_to_clean; rx_desc = E1000_RX_DESC_EXT(*rx_ring, i); staterr = le32_to_cpu(rx_desc->wb.upper.status_error); @@ -4681,6 +4750,7 @@ int e1000e_open(struct net_device *netdev) e1000_irq_enable(adapter); adapter->tx_hang_recheck = false; + adapter->rx_hang_recheck = false; hw->mac.get_link_status = true; pm_runtime_put(&pdev->dev); @@ -7689,6 +7759,12 @@ static int e1000_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (pci_dev_run_wake(pdev)) pm_runtime_put_noidle(&pdev->dev); + adapter->rdh_old = 0; + adapter->rdt_old = 0; + adapter->rx_ntu_old = 0; + adapter->rx_ntc_old = 0; + adapter->weird_hang_count = 0; + return 0; err_register: From be40df73615c7603a9f9c1b06b915a611245df07 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 063/130] AOSCOS: Retry to configure USB device if needed [Mingcong Bai: Resolved a minor merge conflict in drivers/usb/core/message.c.] Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/usb/core/message.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 077dfe48d01c1..295d763d95e81 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1996,7 +1996,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) struct usb_host_config *cp = NULL; struct usb_interface **new_interfaces = NULL; struct usb_hcd *hcd = bus_to_hcd(dev->bus); - int n, nintf; + int n, nintf, retried = 0; if (dev->authorized == 0 || configuration == -1) configuration = 0; @@ -2145,6 +2145,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) } kfree(new_interfaces); +retry: ret = usb_control_msg_send(dev, 0, USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, USB_CTRL_SET_TIMEOUT, GFP_NOIO); @@ -2153,6 +2154,12 @@ int usb_set_configuration(struct usb_device *dev, int configuration) * All the old state is gone, so what else can we do? * The device is probably useless now anyway. */ + if (!retried) { + retried = 1; + printk("Retry to configure %d-%s!\n", dev->bus->busnum, dev->devpath); + goto retry; + } + usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL); for (i = 0; i < nintf; ++i) { usb_disable_interface(dev, cp->interface[i], true); From c573d2c936719fadf99a1dc8e390bc5b4adc1883 Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 064/130] AOSCOS: platform: export psmouse::touchpad led device [Mingcong Bai: Resolved a minor merge conflict in drivers/platform/mips/Kconfig.] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit --- .../include/asm/mach-loongson64/ec_wpce775l.h | 7 +++ drivers/platform/mips/Kconfig | 1 + drivers/platform/mips/lemote3a-laptop.c | 44 +++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h b/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h index fc60026bc6741..f1c686ef7a54b 100644 --- a/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h +++ b/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h @@ -127,6 +127,13 @@ enum }; /* <<< End Backlight */ +#define INDEX_TOUCHPAD_ENABLE_LED 0x56 +enum +{ + TP_EN_LED_OFF, + TP_EN_LED_ON +}; + /* >>> Read battery(BQ3060) index for ACPI 80h command */ /* * The reported battery die temperature. diff --git a/drivers/platform/mips/Kconfig b/drivers/platform/mips/Kconfig index 7ef9a47556d59..a5a80e0a399ff 100644 --- a/drivers/platform/mips/Kconfig +++ b/drivers/platform/mips/Kconfig @@ -49,6 +49,7 @@ config LEMOTE3A_LAPTOP select INPUT_SPARSEKMAP select INPUT_EVDEV depends on INPUT + select LEDS_CLASS default y help Lemote Loongson-3A/2Gq family laptops driver. diff --git a/drivers/platform/mips/lemote3a-laptop.c b/drivers/platform/mips/lemote3a-laptop.c index a8d339a3e30b7..b6f585b0f7d35 100644 --- a/drivers/platform/mips/lemote3a-laptop.c +++ b/drivers/platform/mips/lemote3a-laptop.c @@ -19,12 +19,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -218,6 +220,9 @@ static int lemote3a_bat_very_low_handler(int status); /* SCI device LID event handler */ static int lemote3a_lid_handler(int status); +static void lemote3a_tp_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness); + /* Hotkey device init handler */ static int lemote3a_hotkey_init(void); /* Hotkey device exit handler */ @@ -386,6 +391,23 @@ static const struct key_entry lemote3a_keymap[] = {KE_END, 0 } }; +/* Touchpad EN/DIS led*/ +static struct led_classdev lemote3a_tp_led = { + .name = "psmouse::touchpad", + .brightness_set = lemote3a_tp_led_set, + .flags = LED_CORE_SUSPENDRESUME, +}; + +static int tp_led_shutdown_notify(struct notifier_block *unused1, + unsigned long unused2, void *unused3) +{ + lemote3a_tp_led_set(&lemote3a_tp_led, LED_OFF); + return NOTIFY_DONE; +} + +static struct notifier_block tp_led_nb = { + .notifier_call = tp_led_shutdown_notify, +}; static int wpce775l_probe(struct platform_device *dev) { @@ -432,6 +454,13 @@ static int wpce775l_probe(struct platform_device *dev) } /* Register power supply END */ + /* Touchpad enable/disable LED */ + ret = led_classdev_register(NULL, &lemote3a_tp_led); + if (ret == 0) + register_reboot_notifier(&tp_led_nb); + else + goto fail_tp_led_register; + /* Hotkey device START */ ret = lemote3a_hotkey_init(); if (ret) { @@ -467,6 +496,8 @@ static int wpce775l_probe(struct platform_device *dev) fail_sci_pci_driver_init: lemote3a_hotkey_exit(); fail_hotkey_init: + led_classdev_unregister(&lemote3a_tp_led); +fail_tp_led_register: power_supply_unregister(lemote3a_ac); fail_ac_power_supply_register: power_supply_unregister(lemote3a_bat); @@ -513,6 +544,10 @@ static void __exit lemote3a_laptop_exit(void) sci_pci_driver_exit(); lemote3a_hotkey_exit(); + /* Touchpad enable/disable LED */ + unregister_reboot_notifier(&tp_led_nb); + led_classdev_unregister(&lemote3a_tp_led); + /* Power supply */ power_supply_unregister(lemote3a_ac); power_supply_unregister(lemote3a_bat); @@ -1136,6 +1171,15 @@ static int lemote3a_lid_handler(int status) return 0; } +/* Set touchpad en/dis led */ +static void lemote3a_tp_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + int val = brightness ? TP_EN_LED_ON : TP_EN_LED_OFF; + + ec_write(INDEX_TOUCHPAD_ENABLE_LED, val); +} + /* Hotkey device init handler */ static int lemote3a_hotkey_init(void) { From 598ff5efcdf5150f8de3ec743d1bfca35ebbc525 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Fri, 21 Apr 2017 15:38:39 +0800 Subject: [PATCH 065/130] AOSCOS: Loongson: Add data destory and healthy led control Signed-off-by: Binbin Zhou Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- .../include/asm/mach-loongson64/ec_wpce775l.h | 11 ++++ drivers/platform/mips/lemote3a-laptop.c | 50 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h b/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h index f1c686ef7a54b..e1f8577a6ed61 100644 --- a/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h +++ b/arch/mips/include/asm/mach-loongson64/ec_wpce775l.h @@ -320,6 +320,17 @@ enum CAM_STSCTRL_ON }; +/* data destroy led control */ +#define INDEX_DATA_DESTROY 0xB0 +enum +{ + DATA_DESTROY_OFF = 0, + DATA_DESTROY_ON +}; + +/* The led of board healthy */ +#define INDEX_BOARD_HEALTHY 0xB1 + /* EC_SC input */ /* EC Status query, by direct read 66h port. */ #define EC_SMI_EVT (1 << 6) /* 1 = SMI event padding */ diff --git a/drivers/platform/mips/lemote3a-laptop.c b/drivers/platform/mips/lemote3a-laptop.c index b6f585b0f7d35..dbb37e376ee80 100644 --- a/drivers/platform/mips/lemote3a-laptop.c +++ b/drivers/platform/mips/lemote3a-laptop.c @@ -160,6 +160,7 @@ static int lemote3a_laptop_suspend(struct platform_device * pdev, pm_message_t s static int lemote3a_laptop_resume(struct platform_device * pdev); static ssize_t version_show(struct device_driver * driver, char * buf); +static ssize_t data_destroy_store(struct device_driver *driver, const char *buf, size_t count); /* Camera control misc device open handler */ static int lemote3a_cam_misc_open(struct inode * inode, struct file * filp); @@ -229,12 +230,16 @@ static int lemote3a_hotkey_init(void); static void lemote3a_hotkey_exit(void); extern int ec_query_get_event_num(void); +static int lemote3a_healthy_led_init(void); + static int wpce775l_probe(struct platform_device *dev); +static void wpce775l_shutdown(struct platform_device *dev); /* Platform driver object */ static struct platform_driver platform_driver = { .probe = wpce775l_probe, + .shutdown = wpce775l_shutdown, .driver = { .name = "wpce775l", @@ -246,6 +251,7 @@ static struct platform_driver platform_driver = #endif /* CONFIG_PM */ }; static DRIVER_ATTR_RO(version); +static DRIVER_ATTR_WO(data_destroy); /* Camera control misc device object file operations */ static const struct file_operations lemote3a_cam_misc_fops = @@ -409,6 +415,8 @@ static struct notifier_block tp_led_nb = { .notifier_call = tp_led_shutdown_notify, }; +static struct delayed_work healthy_led_control; + static int wpce775l_probe(struct platform_device *dev) { int ret; @@ -485,6 +493,8 @@ static int wpce775l_probe(struct platform_device *dev) } /* Camera control misc Device END */ + ret = lemote3a_healthy_led_init(); + /* Request control for backlight device START */ ec_write(INDEX_BACKLIGHT_CTRLMODE, BACKLIGHT_CTRL_BYHOST); /* Request control for backlight device END */ @@ -511,6 +521,11 @@ static int wpce775l_probe(struct platform_device *dev) return ret; } +static void wpce775l_shutdown(struct platform_device *dev) +{ + cancel_delayed_work_sync(&healthy_led_control); +} + /* Platform driver init handler */ static int __init lemote3a_laptop_init(void) { @@ -523,7 +538,9 @@ static int __init lemote3a_laptop_init(void) printk(KERN_ERR "Lemote Laptop Platform Driver: Fail to register lemote laptop platform driver.\n"); return ret; } + ret = driver_create_file(&platform_driver.driver, &driver_attr_version); + ret = driver_create_file(&platform_driver.driver, &driver_attr_data_destroy); return ret; } @@ -618,6 +635,22 @@ static ssize_t version_show(struct device_driver * driver, char * buf) return sprintf(buf, "%s\n", version); } +/* the sysctl for the led of data destroy */ +static ssize_t data_destroy_store(struct device_driver *driver, const char *buf, size_t count) +{ + int value; + + if (!sscanf(buf, "%d", &value)) + return -EINVAL; + + if ('0' == buf[0]) + ec_write(INDEX_DATA_DESTROY, DATA_DESTROY_OFF); + else + ec_write(INDEX_DATA_DESTROY, DATA_DESTROY_ON); + + return count; +} + /* Camera control misc device open handler */ static int lemote3a_cam_misc_open(struct inode * inode, struct file * filp) { @@ -1180,6 +1213,23 @@ static void lemote3a_tp_led_set(struct led_classdev *led_cdev, ec_write(INDEX_TOUCHPAD_ENABLE_LED, val); } +static void do_healthy_led_timer(struct work_struct *work) +{ + static unsigned char times_count = 0; + + ec_write(INDEX_BOARD_HEALTHY, times_count); + schedule_delayed_work(&healthy_led_control, msecs_to_jiffies(1000)); + times_count++; +} + +static int lemote3a_healthy_led_init(void) +{ + INIT_DELAYED_WORK(&healthy_led_control, do_healthy_led_timer); + schedule_delayed_work(&healthy_led_control, msecs_to_jiffies(1000)); + + return 0; +} + /* Hotkey device init handler */ static int lemote3a_hotkey_init(void) { From 5da95c9edd21b6f7ab6820aeaa5885ce7c8f747c Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Feb 2018 14:06:27 +0800 Subject: [PATCH 066/130] AOSCOS: writeback: Limit maximum dirty data for Loongson-3 Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- mm/page-writeback.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/mm/page-writeback.c b/mm/page-writeback.c index e632ec9b64210..d9cd4058f8d44 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -2335,6 +2336,38 @@ static struct ctl_table vm_page_writeback_sysctls[] = { */ void __init page_writeback_init(void) { +#ifdef CONFIG_CPU_LOONGSON3 + uint64_t x, d; + unsigned long ratio = dirty_background_ratio; + unsigned long avail = global_dirtyable_memory(); + unsigned long limit = 2*800*1024*1024UL / PAGE_SIZE; + + if (avail*ratio > limit*100) { + dirty_background_ratio = 0; + vm_dirty_ratio = 0; + dirty_background_bytes = limit * PAGE_SIZE; + vm_dirty_bytes = 2 * dirty_background_bytes; + } + dirty_expire_interval = 10 * 100; + + avail = avail * PAGE_SIZE / SZ_1G; + + switch (avail) { + case 0 ... 89: + d = avail / 2; + break; + case 90 ... 269: + x = avail / 4; + x = x*x*x*x*x*x*x*x*x*x; + d = fls64(x) - 1; + break; + default: + /* 270 ... ULONG_MAX */ + d = vm_swappiness; + } + vm_swappiness -= d; +#endif + BUG_ON(wb_domain_init(&global_wb_domain, GFP_KERNEL)); cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mm/writeback:online", From aec5bc7beed7db4526bd488f8646d309980b3aae Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 10 Dec 2024 17:02:04 +0800 Subject: [PATCH 067/130] AOSCOS: mm: writeback: replace CPU_LOONGSON3 with CPU_LOONGSON64 Per commit 30ad29bb4888 ("MIPS: Loongson: Naming style cleanup and rework"), rename the CPU_LOONGSON3 definition as CPU_LOONGSON64. Fixes: "writeback: Limit maximum dirty data for Loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- mm/page-writeback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/page-writeback.c b/mm/page-writeback.c index d9cd4058f8d44..71cc3ba716c52 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -2336,7 +2336,7 @@ static struct ctl_table vm_page_writeback_sysctls[] = { */ void __init page_writeback_init(void) { -#ifdef CONFIG_CPU_LOONGSON3 +#ifdef CONFIG_CPU_LOONGSON64 uint64_t x, d; unsigned long ratio = dirty_background_ratio; unsigned long avail = global_dirtyable_memory(); From 60d66388397b2e08ae2bdf700a6990ac1c25bc6a Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 068/130] AOSCOS: drm/radeon: recover the GPU if it fails at resume [Mingcong Bai: Resolved a minor merge conflict in drivers/gpu/drm/radeon/radeon_device.c.] Signed-off-by: Huacai Chen Signed-off-by: Binbin Zhou Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/radeon/radeon.h | 2 ++ drivers/gpu/drm/radeon/radeon_device.c | 32 +++++++++++++++++++++++-- drivers/gpu/drm/radeon/radeon_irq_kms.c | 5 ++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index e0a02b357ce72..d4f1271e6324e 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -2407,6 +2407,8 @@ struct radeon_device { struct delayed_work hotplug_work; struct work_struct dp_work; struct work_struct audio_work; + int need_recover; + struct delayed_work recover_work; int num_crtc; /* number of crtcs */ struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */ bool has_uvd; diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 32851632643db..4fdb17226974a 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1645,7 +1645,28 @@ int radeon_suspend_kms(struct drm_device *dev, bool suspend, return 0; } -/* +void radeon_recover_callback(struct work_struct *work) +{ + int resched; + struct radeon_device *rdev = container_of(to_delayed_work(work), + struct radeon_device, recover_work); + + printk("Radeon GPU Recover...\n"); + down_write(&rdev->exclusive_lock); + radeon_save_bios_scratch_regs(rdev); + resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); + radeon_pm_suspend(rdev); + radeon_suspend(rdev); + radeon_resume(rdev); + radeon_restore_bios_scratch_regs(rdev); + radeon_ib_ring_tests(rdev); + radeon_pm_resume(rdev); + drm_helper_resume_force_mode(rdev->ddev); + ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); + up_write(&rdev->exclusive_lock); +} + +/** * radeon_resume_kms - initiate device resume * * Bring the hw back to operating state (all asics). @@ -1680,8 +1701,10 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) radeon_resume(rdev); r = radeon_ib_ring_tests(rdev); - if (r) + if (r) { + rdev->need_recover = 1; DRM_ERROR("ib ring test failed (%d).\n", r); + } if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { /* do dpm late init */ @@ -1754,6 +1777,11 @@ int radeon_resume_kms(struct drm_device *dev, bool resume, bool fbcon) console_unlock(); } + + if (rdev->need_recover) { + rdev->need_recover = 0; + schedule_delayed_work_on(0, &rdev->recover_work, msecs_to_jiffies(2000)); + } return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 9961251b44ba0..e5d5ea218579f 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -307,6 +307,8 @@ static bool radeon_msi_ok(struct radeon_device *rdev) return true; } +extern void radeon_recover_callback(struct work_struct *work); + /** * radeon_irq_kms_init - init driver interrupt info * @@ -344,6 +346,9 @@ int radeon_irq_kms_init(struct radeon_device *rdev) INIT_WORK(&rdev->dp_work, radeon_dp_work_func); INIT_WORK(&rdev->audio_work, r600_audio_update_hdmi); + rdev->need_recover = 0; + INIT_DEFERRABLE_WORK(&rdev->recover_work, radeon_recover_callback); + rdev->irq.installed = true; r = radeon_irq_install(rdev, rdev->pdev->irq); if (r) { From 7a7994cd9ac99a2fa3c0f82e3b3c377428920cfd Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 10 Dec 2024 17:55:36 +0800 Subject: [PATCH 069/130] AOSCOS: drm: radeon: declare prototype for radeon_recover_callback() Declare radeon_recover_callback() in radeon_device.h to clean up function reference and avoid build errors with -Werror=missing-prototype. Fixes: "AOSCOS: drm/radeon: recover the GPU if it fails at resume" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/radeon/radeon_device.h | 2 ++ drivers/gpu/drm/radeon/radeon_irq_kms.c | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/radeon/radeon_device.h b/drivers/gpu/drm/radeon/radeon_device.h index 31a0ae295cc8f..e7f6d468b5674 100644 --- a/drivers/gpu/drm/radeon/radeon_device.h +++ b/drivers/gpu/drm/radeon/radeon_device.h @@ -29,4 +29,6 @@ bool radeon_device_is_virtual(void); +void radeon_recover_callback(struct work_struct *work); + #endif /* __RADEON_DEVICE_H__ */ diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index e5d5ea218579f..d4ad1fa826454 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -40,6 +40,7 @@ #include "radeon_kms.h" #include "radeon_reg.h" +#include "radeon_device.h" #define RADEON_WAIT_IDLE_TIMEOUT 200 @@ -307,8 +308,6 @@ static bool radeon_msi_ok(struct radeon_device *rdev) return true; } -extern void radeon_recover_callback(struct work_struct *work); - /** * radeon_irq_kms_init - init driver interrupt info * From cc0203e415436d9b32f1ce04b475c343279b827b Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 10 Dec 2024 18:06:52 +0800 Subject: [PATCH 070/130] AOSCOS: Revert "drm/ttm: remove ttm_bo_(un)lock_delayed_workqueue" This reverts commit cd3a8a596214e6a338a22104936c40e62bdea2b6. This commit is needed to build "AOSCOS: drm/radeon: recover the GPU if it fails at resume", which relies on the removed `ttm_bo_{un,}lock_delayed_workqueue()' functions. Partially revert this commit for the radeon and ttm modules (leaving out amdgpu, as it was not the target of the original patch). Fixes: "AOSCOS: drm/radeon: recover the GPU if it fails at resume" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/radeon/radeon_device.c | 5 +++++ drivers/gpu/drm/radeon/radeon_pm.c | 4 +++- drivers/gpu/drm/ttm/ttm_bo.c | 14 ++++++++++++++ include/drm/ttm/ttm_bo.h | 16 ++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 4fdb17226974a..578c70be27a09 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1801,6 +1801,7 @@ int radeon_gpu_reset(struct radeon_device *rdev) bool saved = false; int i, r; + int resched; down_write(&rdev->exclusive_lock); @@ -1812,6 +1813,8 @@ int radeon_gpu_reset(struct radeon_device *rdev) atomic_inc(&rdev->gpu_reset_counter); radeon_save_bios_scratch_regs(rdev); + /* block TTM */ + resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); radeon_suspend(rdev); radeon_hpd_fini(rdev); @@ -1870,6 +1873,8 @@ int radeon_gpu_reset(struct radeon_device *rdev) /* reset hpd state */ radeon_hpd_init(rdev); + ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); + rdev->in_reset = true; rdev->needs_reset = false; diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 66fe9fb920452..b7d74746bd1af 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1853,10 +1853,11 @@ static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish static void radeon_dynpm_idle_work_handler(struct work_struct *work) { struct radeon_device *rdev; - + int resched; rdev = container_of(work, struct radeon_device, pm.dynpm_idle_work.work); + resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); mutex_lock(&rdev->pm.mutex); if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { int not_processed = 0; @@ -1907,6 +1908,7 @@ static void radeon_dynpm_idle_work_handler(struct work_struct *work) msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); } mutex_unlock(&rdev->pm.mutex); + ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); } /* diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index b3e5185835c37..4c02f9c1d2304 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -395,6 +395,20 @@ void ttm_bo_put(struct ttm_buffer_object *bo) } EXPORT_SYMBOL(ttm_bo_put); +int ttm_bo_lock_delayed_workqueue(struct ttm_device *bdev) +{ + return cancel_delayed_work_sync(&bdev->wq); +} +EXPORT_SYMBOL(ttm_bo_lock_delayed_workqueue); + +void ttm_bo_unlock_delayed_workqueue(struct ttm_device *bdev, int resched) +{ + if (resched) + schedule_delayed_work(&bdev->wq, + ((HZ / 100) < 1) ? 1 : HZ / 100); +} +EXPORT_SYMBOL(ttm_bo_unlock_delayed_workqueue); + static int ttm_bo_bounce_temp_buffer(struct ttm_buffer_object *bo, struct ttm_resource **mem, struct ttm_operation_ctx *ctx, diff --git a/include/drm/ttm/ttm_bo.h b/include/drm/ttm/ttm_bo.h index 0223a41a64b24..0db017e3be8c3 100644 --- a/include/drm/ttm/ttm_bo.h +++ b/include/drm/ttm/ttm_bo.h @@ -316,6 +316,22 @@ static inline void ttm_bo_move_null(struct ttm_buffer_object *bo, ttm_bo_assign_mem(bo, new_mem); } +/** + * ttm_bo_lock_delayed_workqueue + * + * Prevent the delayed workqueue from running. + * Returns + * True if the workqueue was queued at the time + */ +int ttm_bo_lock_delayed_workqueue(struct ttm_device *bdev); + +/** + * ttm_bo_unlock_delayed_workqueue + * + * Allows the delayed workqueue to run. + */ +void ttm_bo_unlock_delayed_workqueue(struct ttm_device *bdev, int resched); + /** * ttm_bo_unreserve * From 7c7ce71ea8107d89e6b1b1391ad33b9c460580c2 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 10:36:24 +0800 Subject: [PATCH 071/130] AOSCOS: drm: ttm: introduce struct delayed_work member dwork to struct ttm_device Commit 9bff18d13473 ("drm/ttm: use per BO cleanup workers") revised the member struct `wq' for struct `ttm_device' from type `struct delayed_work' to `struct workqueue_struct'. This broke the ttm_bo_{un,}lock_delayed_workqueue() functions introduced in "AOSCOS: Revert "drm/ttm: remove ttm_bo_(un)lock_delayed_workqueue"", as they expect a `struct delayed_work' type as the first argument - which is now revised as `struct workqueue_struct'. Introduce a `struct delayed_work dwork' member to fix build (but would it still work as expected?). Fixes: "AOSCOS: Revert "drm/ttm: remove ttm_bo_(un)lock_delayed_workqueue"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/ttm/ttm_bo.c | 4 ++-- include/drm/ttm/ttm_device.h | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 4c02f9c1d2304..d07fe37f7dda0 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -397,14 +397,14 @@ EXPORT_SYMBOL(ttm_bo_put); int ttm_bo_lock_delayed_workqueue(struct ttm_device *bdev) { - return cancel_delayed_work_sync(&bdev->wq); + return cancel_delayed_work_sync(&bdev->dwork); } EXPORT_SYMBOL(ttm_bo_lock_delayed_workqueue); void ttm_bo_unlock_delayed_workqueue(struct ttm_device *bdev, int resched) { if (resched) - schedule_delayed_work(&bdev->wq, + schedule_delayed_work(&bdev->dwork, ((HZ / 100) < 1) ? 1 : HZ / 100); } EXPORT_SYMBOL(ttm_bo_unlock_delayed_workqueue); diff --git a/include/drm/ttm/ttm_device.h b/include/drm/ttm/ttm_device.h index c22f30535c848..1116429c20007 100644 --- a/include/drm/ttm/ttm_device.h +++ b/include/drm/ttm/ttm_device.h @@ -266,6 +266,11 @@ struct ttm_device { * @wq: Work queue structure for the delayed delete workqueue. */ struct workqueue_struct *wq; + + /** + * @dwork: Delayed work in the workqueue. + */ + struct delayed_work dwork; }; int ttm_global_swapout(struct ttm_operation_ctx *ctx, gfp_t gfp_flags); From a89a978a2e65a8ffd02c9bcc9115e20a4e865167 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 072/130] AOSCOS: drm/radeon: Fix hibernation for JUNIPER on Loongson Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/radeon/evergreen.c | 3 +++ drivers/gpu/drm/radeon/radeon_pm.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index ca2cc4c6a5ba5..1709ff97bd478 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1009,6 +1009,9 @@ static void evergreen_init_golden_registers(struct radeon_device *rdev) (const u32)ARRAY_SIZE(cypress_mgcg_init)); break; case CHIP_JUNIPER: + if (rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_ATI) + break; /* Fix hibernation for Loongson */ + radeon_program_register_sequence(rdev, evergreen_golden_registers, (const u32)ARRAY_SIZE(evergreen_golden_registers)); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index b7d74746bd1af..efa94e6e367a6 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1519,6 +1519,7 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_RS780: case CHIP_RS880: case CHIP_RV770: + case CHIP_JUNIPER: /* DPM requires the RLC, RV770+ dGPU requires SMC */ if (!rdev->rlc_fw) rdev->pm.pm_method = PM_METHOD_PROFILE; @@ -1536,7 +1537,6 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_RV740: case CHIP_CEDAR: case CHIP_REDWOOD: - case CHIP_JUNIPER: case CHIP_CYPRESS: case CHIP_HEMLOCK: case CHIP_PALM: From 0ae917cdf3293ab9e35cbd994b2c691f266abf44 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 11:20:38 +0800 Subject: [PATCH 073/130] AOSCOS: drm: radeon: limit MIPS Loongson-3 workarounds for Juniper Guard MIPS Loongson-3 specific hacks around CONFIG_MACH_LOONGSON64. Fixes: "AOSCOS: drm/radeon: Fix hibernation for JUNIPER on Loongson" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/radeon/evergreen.c | 2 ++ drivers/gpu/drm/radeon/radeon_pm.c | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 1709ff97bd478..d2e57e5bf8bb2 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1009,8 +1009,10 @@ static void evergreen_init_golden_registers(struct radeon_device *rdev) (const u32)ARRAY_SIZE(cypress_mgcg_init)); break; case CHIP_JUNIPER: +#ifdef CONFIG_MACH_LOONGSON64 if (rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_ATI) break; /* Fix hibernation for Loongson */ +#endif radeon_program_register_sequence(rdev, evergreen_golden_registers, diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index efa94e6e367a6..adba64c6242ea 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1519,7 +1519,9 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_RS780: case CHIP_RS880: case CHIP_RV770: +#ifdef CONFIG_MACH_LOONGSON64 case CHIP_JUNIPER: +#endif /* DPM requires the RLC, RV770+ dGPU requires SMC */ if (!rdev->rlc_fw) rdev->pm.pm_method = PM_METHOD_PROFILE; @@ -1537,6 +1539,9 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_RV740: case CHIP_CEDAR: case CHIP_REDWOOD: +#ifndef CONFIG_MACH_LOONGSON64 + case CHIP_JUNIPER: +#endif case CHIP_CYPRESS: case CHIP_HEMLOCK: case CHIP_PALM: From 23b8397571fe13f7e62ddd7ab98550db5709a5d3 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sat, 6 May 2017 09:28:30 +0800 Subject: [PATCH 074/130] AOSCOS: drm/radeon: Use high performance profile Signed-off-by: Ce Sun Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/radeon/radeon_pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index adba64c6242ea..e6a30b252327f 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1353,7 +1353,7 @@ static int radeon_pm_init_old(struct radeon_device *rdev) { int ret; - rdev->pm.profile = PM_PROFILE_DEFAULT; + rdev->pm.profile = PM_PROFILE_AUTO; rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; rdev->pm.dynpm_can_upclock = true; From 20a3dd316a08d6dd81c2c6f31c07af724758a325 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 1 Aug 2018 14:31:19 +0800 Subject: [PATCH 075/130] AOSCOS: drm/radeon: Reintroduce radeon_gart_restore() This revert commit a3eb06dbca08e3fdad7039021ae0 ("drm/radeon: Remove radeon_gart_restore()") and update code for the latest kernel. We do this because kexec/kdump need to do this, otherwise it boot fails. Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/radeon/cik.c | 1 + drivers/gpu/drm/radeon/evergreen.c | 1 + drivers/gpu/drm/radeon/ni.c | 1 + drivers/gpu/drm/radeon/r100.c | 1 + drivers/gpu/drm/radeon/r300.c | 1 + drivers/gpu/drm/radeon/r600.c | 1 + drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_gart.c | 24 ++++++++++++++++++++++++ drivers/gpu/drm/radeon/rs400.c | 1 + drivers/gpu/drm/radeon/rs600.c | 1 + drivers/gpu/drm/radeon/rv770.c | 1 + drivers/gpu/drm/radeon/si.c | 1 + 12 files changed, 35 insertions(+) diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 581060fd632c9..811d9f1591f8d 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -5435,6 +5435,7 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev) r = radeon_gart_table_vram_pin(rdev); if (r) return r; + radeon_gart_restore(rdev); /* Setup TLB control */ WREG32(MC_VM_MX_L1_TLB_CNTL, (0xA << 7) | diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index d2e57e5bf8bb2..34382024c887c 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -2413,6 +2413,7 @@ static int evergreen_pcie_gart_enable(struct radeon_device *rdev) r = radeon_gart_table_vram_pin(rdev); if (r) return r; + radeon_gart_restore(rdev); /* Setup L2 cache */ WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 4cd89fd6e9a22..23d9983e7a388 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -1269,6 +1269,7 @@ static int cayman_pcie_gart_enable(struct radeon_device *rdev) r = radeon_gart_table_vram_pin(rdev); if (r) return r; + radeon_gart_restore(rdev); /* Setup TLB control */ WREG32(MC_VM_MX_L1_TLB_CNTL, (0xA << 7) | diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 54cbfac3605fb..1f5d7e31f9112 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -671,6 +671,7 @@ int r100_pci_gart_enable(struct radeon_device *rdev) { uint32_t tmp; + radeon_gart_restore(rdev); /* discard memory request outside of configured range */ tmp = RREG32(RADEON_AIC_CNTL) | RADEON_DIS_OUT_OF_PCI_GART_ACCESS; WREG32(RADEON_AIC_CNTL, tmp); diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 430a4263ccf7a..dc63ea470d529 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -160,6 +160,7 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev) r = radeon_gart_table_vram_pin(rdev); if (r) return r; + radeon_gart_restore(rdev); /* discard memory request outside of configured range */ tmp = RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD; WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp); diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index c7a9956a410d9..6ef6bd73a2862 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -1137,6 +1137,7 @@ static int r600_pcie_gart_enable(struct radeon_device *rdev) r = radeon_gart_table_vram_pin(rdev); if (r) return r; + radeon_gart_restore(rdev); /* Setup L2 cache */ WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index d4f1271e6324e..29dedac455e08 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -627,6 +627,7 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, int pages, struct page **pagelist, dma_addr_t *dma_addr, uint32_t flags); +void radeon_gart_restore(struct radeon_device *rdev); /* diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index 4bb242437ff60..b34b8b9066947 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -317,6 +317,30 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned int offset, return 0; } +/** + * radeon_gart_restore - bind all pages in the gart page table + * + * @rdev: radeon_device pointer + * + * Binds all pages in the gart page table (all asics). + * Used to rebuild the gart table on device startup or resume. + */ +void radeon_gart_restore(struct radeon_device *rdev) +{ + int i; + uint64_t page_entry; + + if (!rdev->gart.ptr) + return; + + for (i = 0; i < rdev->gart.num_gpu_pages; i++) { + page_entry = rdev->gart.pages_entry[i]; + radeon_gart_set_page(rdev, i, page_entry); + } + mb(); + radeon_gart_tlb_flush(rdev); +} + /** * radeon_gart_init - init the driver info for managing the gart * diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index 4f93fe468ec7f..f9360d9b06bcc 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -112,6 +112,7 @@ int rs400_gart_enable(struct radeon_device *rdev) uint32_t size_reg; uint32_t tmp; + radeon_gart_restore(rdev); tmp = RREG32_MC(RS690_AIC_CTRL_SCRATCH); tmp |= RS690_DIS_OUT_OF_PCI_GART_ACCESS; WREG32_MC(RS690_AIC_CTRL_SCRATCH, tmp); diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index fa4cc2a185dd0..8113b1106e823 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -572,6 +572,7 @@ static int rs600_gart_enable(struct radeon_device *rdev) r = radeon_gart_table_vram_pin(rdev); if (r) return r; + radeon_gart_restore(rdev); /* Enable bus master */ tmp = RREG32(RADEON_BUS_CNTL) & ~RS600_BUS_MASTER_DIS; WREG32(RADEON_BUS_CNTL, tmp); diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 7d4b0bf591090..c7317aff4cd64 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -903,6 +903,7 @@ static int rv770_pcie_gart_enable(struct radeon_device *rdev) r = radeon_gart_table_vram_pin(rdev); if (r) return r; + radeon_gart_restore(rdev); /* Setup L2 cache */ WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | ENABLE_L2_FRAGMENT_PROCESSING | ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 5bf7e40bf3549..0298963265fc5 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -4292,6 +4292,7 @@ static int si_pcie_gart_enable(struct radeon_device *rdev) r = radeon_gart_table_vram_pin(rdev); if (r) return r; + radeon_gart_restore(rdev); /* Setup TLB control */ WREG32(MC_VM_MX_L1_TLB_CNTL, (0xA << 7) | From 09b91e96de97e29795553df38003eb27189eecd8 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Wed, 1 Aug 2018 14:39:20 +0800 Subject: [PATCH 076/130] AOSCOS: drm/radeon: Modify GART TLB setting to fix kdump failure After commit 0986c1a55ca64b44ee12 ("drm/radeon: stop poisoning the GART TLB") kdump fails to boot the second kernel, so partially revert to the old behavior. Cc: stable@vger.kernel.org Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/gpu/drm/radeon/rs600.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 8113b1106e823..fedc636bda104 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -655,6 +655,9 @@ uint64_t rs600_gart_get_page_entry(uint64_t addr, uint32_t flags) addr |= R600_PTE_WRITEABLE; if (flags & RADEON_GART_PAGE_SNOOP) addr |= R600_PTE_SNOOPED; +#ifdef CONFIG_CRASH_DUMP + addr |= R600_PTE_VALID | R600_PTE_READABLE | R600_PTE_WRITEABLE; +#endif return addr; } From 767befe4a6449886ac41d31867b670e44c1ff80d Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Fri, 3 Nov 2017 09:15:11 +0800 Subject: [PATCH 077/130] AOSCOS: sm750fb: change default screen resolution Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/staging/sm750fb/sm750.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index 79bcd5bd49380..fc27480f504f0 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -34,7 +34,7 @@ static int g_hwcursor = 1; static int g_noaccel; static int g_nomtrr; static const char *g_fbmode[] = {NULL, NULL}; -static const char *g_def_fbmode = "1024x768-32@60"; +static const char *g_def_fbmode = "800x600-32@60"; static char *g_settings; static int g_dualview; static char *g_option; From 7e4ebd91a41293779ed854a6076695d76efc1385 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 078/130] AOSCOS: sm750fb: Disable hw_cursor to avoid screen corruption Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/staging/sm750fb/sm750.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index fc27480f504f0..22032a8753dde 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -30,7 +30,7 @@ */ /* common var for all device */ -static int g_hwcursor = 1; +static int g_hwcursor; static int g_noaccel; static int g_nomtrr; static const char *g_fbmode[] = {NULL, NULL}; @@ -880,7 +880,7 @@ static void sm750fb_setup(struct sm750_dev *sm750_dev, char *src) sm750_dev->initParm.resetMemory = 1; /* defaultly turn g_hwcursor on for both view */ - g_hwcursor = 3; + g_hwcursor = 0; if (!src || !*src) { dev_warn(&sm750_dev->pdev->dev, "no specific g_option.\n"); From 2d6fefe2b497b72d18c7f641651b68e284aefe0d Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 079/130] AOSCOS: Input: i8042 - Make i8042_bypass_aux_irq_test as a module parameter The original i8042_bypass_aux_irq_test is a variable which cannot be changed without kernel rebuild. Some buggy platform fails to detect the existance of AUX if i8042_bypass_aux_irq_test isn't set. So make this variable as a module parameter and set the default value to true. Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/input/serio/i8042.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 897e4c9930995..76ce5c5a424eb 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -131,7 +131,9 @@ MODULE_PARM_DESC(unmask_kbd_data, "Unconditional enable (may reveal sensitive da #endif static bool i8042_present; -static bool i8042_bypass_aux_irq_test; +static bool i8042_bypass_aux_irq_test = true; +module_param_named(bypass_aux_test, i8042_bypass_aux_irq_test, bool, 0600); +MODULE_PARM_DESC(debug, "Turn i8042 aux irq test off"); static char i8042_kbd_firmware_id[128]; static char i8042_aux_firmware_id[128]; static struct fwnode_handle *i8042_kbd_fwnode; From 92d0f67af09d209cf33e34db34f0052801fe0d30 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 080/130] AOSCOS: IGB: Detect and recover weird rx hang bug Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/net/ethernet/intel/igb/igb.h | 7 +++ drivers/net/ethernet/intel/igb/igb_main.c | 69 +++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index a2b759531cb7b..ff88e897aad2f 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -374,6 +374,13 @@ struct igb_q_vector { struct rcu_head rcu; /* to avoid race with update stats on free */ char name[IFNAMSIZ + 9]; + u32 rdh_old; + u32 rdt_old; + u32 rx_ntu_old; + u32 rx_ntc_old; + int weird_hang_count; + bool weird_hang_recheck; + /* for dynamic allocation of rings associated with this q_vector */ struct igb_ring ring[] ____cacheline_internodealigned_in_smp; }; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index c38be2880efcf..6a66cb516163a 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -4221,6 +4221,15 @@ static int __igb_open(struct net_device *netdev, bool resuming) hw->mac.get_link_status = 1; schedule_work(&adapter->watchdog_task); + for (i = 0; i < adapter->num_q_vectors; i++) { + adapter->q_vector[i]->rdh_old = 0; + adapter->q_vector[i]->rdt_old = 0; + adapter->q_vector[i]->rx_ntu_old = 0; + adapter->q_vector[i]->rx_ntc_old = 0; + adapter->q_vector[i]->weird_hang_count = 0; + adapter->q_vector[i]->weird_hang_recheck = false; + } + return 0; err_set_queues: @@ -8895,6 +8904,64 @@ static void igb_put_rx_buffer(struct igb_ring *rx_ring, rx_buffer->page = NULL; } +static void igb_check_weird_hang(struct igb_ring *rx_ring) +{ + u32 i, rdh, rdt, staterr; + union e1000_adv_rx_desc *rx_desc, *next_rxd; + struct igb_q_vector *q_vector = rx_ring->q_vector; + struct e1000_hw *hw = &q_vector->adapter->hw; + + if (q_vector->adapter->link_speed != SPEED_1000) + return; + + i = rx_ring->next_to_clean; + rdh = rd32(E1000_RDH(rx_ring->reg_idx)); + rdt = rd32(E1000_RDT(rx_ring->reg_idx)); + rx_desc = IGB_RX_DESC(rx_ring, i); + staterr = le32_to_cpu(rx_desc->wb.upper.status_error); + next_rxd = IGB_RX_DESC(rx_ring, (i+1)%rx_ring->count); + + /* Weird Hung */ + if (igb_desc_unused(rx_ring)==0 && rdh==rdt && !igb_test_staterr(rx_desc, E1000_RXD_STAT_DD)) { + if (!q_vector->weird_hang_recheck) { /* Check Twice*/ + q_vector->weird_hang_recheck = true; + q_vector->rdh_old = rdh; + q_vector->rdt_old = rdt; + q_vector->rx_ntu_old = rx_ring->next_to_use; + q_vector->rx_ntc_old = rx_ring->next_to_clean; + } + else { + q_vector->weird_hang_recheck = false; + if (rdh==q_vector->rdh_old && rdt==q_vector->rdt_old + && rx_ring->next_to_use==q_vector->rx_ntu_old + && rx_ring->next_to_clean==q_vector->rx_ntc_old) { + q_vector->weird_hang_count++; + dev_err(rx_ring->dev, + "Detected the %dth Weird Rx Hang:\n" + " RDH <%x>\n" + " RDT <%x>\n" + " next_to_use <%x>\n" + " next_to_clean <%x>\n" + " rx_desc status <%x>\n", + q_vector->weird_hang_count, + rdh, rdt, + rx_ring->next_to_use, + rx_ring->next_to_clean, + staterr); + + if (igb_test_staterr(next_rxd, E1000_RXD_STAT_DD)) { /* Really hang, light recover */ + dev_err(rx_ring->dev, "Recover by skipping rx_desc...\n"); + rx_ring->next_to_clean = (i+1) % rx_ring->count; + } + else { /* Really hang, heavy recover */ + dev_err(rx_ring->dev, "Recover by resetting device...\n"); + schedule_work(&q_vector->adapter->reset_task); + } + } + } + } +} + static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) { unsigned int total_bytes = 0, total_packets = 0; @@ -8915,6 +8982,8 @@ static int igb_clean_rx_irq(struct igb_q_vector *q_vector, const int budget) #endif xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq); + igb_check_weird_hang(rx_ring); + while (likely(total_packets < budget)) { union e1000_adv_rx_desc *rx_desc; struct igb_rx_buffer *rx_buffer; From 8d5ff3847bc7576856b1777cd6684a8a4a0bca9a Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 081/130] AOSCOS: Revert "staging: sb105x: delete the driver" This reverts commit aa98b772b7d32d91ec2a7f8779adfc31f0aaaf24. Lemote use this driver for MIPS/Loongson, so let me maintain it! [Mingcong Bai: Resolved minor merge conflicts in drivers/staging/Kconfig and drivers/staging/Makefile.] Signed-off-by: Mingcong Bai Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/sb105x/Kconfig | 9 + drivers/staging/sb105x/Makefile | 3 + drivers/staging/sb105x/sb_mp_register.h | 295 +++ drivers/staging/sb105x/sb_pci_mp.c | 3189 +++++++++++++++++++++++ drivers/staging/sb105x/sb_pci_mp.h | 291 +++ drivers/staging/sb105x/sb_ser_core.h | 368 +++ 8 files changed, 4158 insertions(+) create mode 100644 drivers/staging/sb105x/Kconfig create mode 100644 drivers/staging/sb105x/Makefile create mode 100644 drivers/staging/sb105x/sb_mp_register.h create mode 100644 drivers/staging/sb105x/sb_pci_mp.c create mode 100644 drivers/staging/sb105x/sb_pci_mp.h create mode 100644 drivers/staging/sb105x/sb_ser_core.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index f9aef39cac2e9..4398fb36b23c6 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -58,6 +58,8 @@ source "drivers/staging/board/Kconfig" source "drivers/staging/gdm724x/Kconfig" +source "drivers/staging/sb105x/Kconfig" + source "drivers/staging/fbtft/Kconfig" source "drivers/staging/most/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index ffa70dda481d3..8f89455d72216 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_USB_EMXX) += emxx_udc/ obj-$(CONFIG_MFD_NVEC) += nvec/ obj-$(CONFIG_STAGING_BOARD) += board/ obj-$(CONFIG_LTE_GDM724X) += gdm724x/ +obj-$(CONFIG_SB105X) += sb105x/ obj-$(CONFIG_FB_TFT) += fbtft/ obj-$(CONFIG_MOST) += most/ obj-$(CONFIG_KS7010) += ks7010/ diff --git a/drivers/staging/sb105x/Kconfig b/drivers/staging/sb105x/Kconfig new file mode 100644 index 0000000000000..245e7847a3542 --- /dev/null +++ b/drivers/staging/sb105x/Kconfig @@ -0,0 +1,9 @@ +config SB105X + tristate "SystemBase PCI Multiport UART" + select SERIAL_CORE + depends on PCI && X86 && TTY && BROKEN + help + A driver for the SystemBase Multi-2/PCI serial card + + To compile this driver a module, choose M here: the module + will be called "sb105x". diff --git a/drivers/staging/sb105x/Makefile b/drivers/staging/sb105x/Makefile new file mode 100644 index 0000000000000..b1bf3779acaed --- /dev/null +++ b/drivers/staging/sb105x/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_SB105X) += sb105x.o + +sb105x-y := sb_pci_mp.o diff --git a/drivers/staging/sb105x/sb_mp_register.h b/drivers/staging/sb105x/sb_mp_register.h new file mode 100644 index 0000000000000..276c1bbcc18dd --- /dev/null +++ b/drivers/staging/sb105x/sb_mp_register.h @@ -0,0 +1,295 @@ + +/* + * SB105X_UART.h + * + * Copyright (C) 2008 systembase + * + * UART registers. + * + * 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. + */ + +#ifndef UART_SB105X_H +#define UART_SB105X_H + +/* + * option register + */ + +/* Device Information Register */ +#define MP_OPTR_DIR0 0x04 /* port0 ~ port8 */ +#define MP_OPTR_DIR1 0x05 /* port8 ~ port15 */ +#define MP_OPTR_DIR2 0x06 /* port16 ~ port23 */ +#define MP_OPTR_DIR3 0x07 /* port24 ~ port31 */ + +#define DIR_UART_16C550 0 +#define DIR_UART_16C1050 1 +#define DIR_UART_16C1050A 2 + +#define DIR_CLK_1843200 0x0 /* input clock 1843200 Hz */ +#define DIR_CLK_3686400 0x1 /* input clock 3686400 Hz */ +#define DIR_CLK_7372800 0x2 /* input clock 7372800 Hz */ +#define DIR_CLK_14745600 0x3 /* input clock 14745600 Hz */ +#define DIR_CLK_29491200 0x4 /* input clock 29491200 Hz */ +#define DIR_CLK_58985400 0x5 /* input clock 58985400 Hz */ + +/* Interface Information Register */ +#define MP_OPTR_IIR0 0x08 /* port0 ~ port8 */ +#define MP_OPTR_IIR1 0x09 /* port8 ~ port15 */ +#define MP_OPTR_IIR2 0x0A /* port16 ~ port23 */ +#define MP_OPTR_IIR3 0x0B /* port24 ~ port31 */ + +#define IIR_RS232 0x00 /* RS232 type */ +#define IIR_RS422 0x10 /* RS422 type */ +#define IIR_RS485 0x20 /* RS485 type */ +#define IIR_TYPE_MASK 0x30 + +/* Interrupt Mask Register */ +#define MP_OPTR_IMR0 0x0C /* port0 ~ port8 */ +#define MP_OPTR_IMR1 0x0D /* port8 ~ port15 */ +#define MP_OPTR_IMR2 0x0E /* port16 ~ port23 */ +#define MP_OPTR_IMR3 0x0F /* port24 ~ port31 */ + +/* Interrupt Poll Register */ +#define MP_OPTR_IPR0 0x10 /* port0 ~ port8 */ +#define MP_OPTR_IPR1 0x11 /* port8 ~ port15 */ +#define MP_OPTR_IPR2 0x12 /* port16 ~ port23 */ +#define MP_OPTR_IPR3 0x13 /* port24 ~ port31 */ + +/* General Purpose Output Control Register */ +#define MP_OPTR_GPOCR 0x20 + +/* General Purpose Output Data Register */ +#define MP_OPTR_GPODR 0x21 + +/* Parallel Additional Function Register */ +#define MP_OPTR_PAFR 0x23 + +/* + * systembase 16c105x UART register + */ + +#define PAGE_0 0 +#define PAGE_1 1 +#define PAGE_2 2 +#define PAGE_3 3 +#define PAGE_4 4 + +/* + * ****************************************************************** + * * DLAB=0 =============== Page 0 Registers * + * ****************************************************************** + */ + +#define SB105X_RX 0 /* In: Receive buffer */ +#define SB105X_TX 0 /* Out: Transmit buffer */ + +#define SB105X_IER 1 /* Out: Interrupt Enable Register */ + +#define SB105X_IER_CTSI 0x80 /* CTS# Interrupt Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_RTSI 0x40 /* RTS# Interrupt Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_XOI 0x20 /* Xoff Interrupt Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_SME 0x10 /* Sleep Mode Enable (Requires EFR[4] = 1) */ +#define SB105X_IER_MSI 0x08 /* Enable Modem status interrupt */ +#define SB105X_IER_RLSI 0x04 /* Enable receiver line status interrupt */ +#define SB105X_IER_THRI 0x02 /* Enable Transmitter holding register int. */ +#define SB105X_IER_RDI 0x01 /* Enable receiver data interrupt */ + +#define SB105X_ISR 2 /* In: Interrupt ID Register */ + +#define SB105X_ISR_NOINT 0x01 /* No interrupts pending */ +#define SB105X_ISR_RLSI 0x06 /* Receiver line status interrupt (Priority = 1)*/ +#define SB105X_ISR_RDAI 0x0c /* Receive Data Available interrupt */ +#define SB105X_ISR_CTII 0x04 /* Character Timeout Indication interrupt */ +#define SB105X_ISR_THRI 0x02 /* Transmitter holding register empty */ +#define SB105X_ISR_MSI 0x00 /* Modem status interrupt */ +#define SB105X_ISR_RXCI 0x10 /* Receive Xoff or Special Character interrupt */ +#define SB105X_ISR_RCSI 0x20 /* RTS#, CTS# status interrupt during Auto RTS/CTS flow control */ + +#define SB105X_FCR 2 /* Out: FIFO Control Register */ + +#define SB105X_FCR_FEN 0x01 /* FIFO Enable */ +#define SB105X_FCR_RXFR 0x02 /* RX FIFO Reset */ +#define SB105X_FCR_TXFR 0x04 /* TX FIFO Reset */ +#define SB105X_FCR_DMS 0x08 /* DMA Mode Select */ + +#define SB105X_FCR_RTR08 0x00 /* Receive Trigger Level set at 8 */ +#define SB105X_FCR_RTR16 0x40 /* Receive Trigger Level set at 16 */ +#define SB105X_FCR_RTR56 0x80 /* Receive Trigger Level set at 56 */ +#define SB105X_FCR_RTR60 0xc0 /* Receive Trigger Level set at 60 */ +#define SB105X_FCR_TTR08 0x00 /* Transmit Trigger Level set at 8 */ +#define SB105X_FCR_TTR16 0x10 /* Transmit Trigger Level set at 16 */ +#define SB105X_FCR_TTR32 0x20 /* Transmit Trigger Level set at 32 */ +#define SB105X_FCR_TTR56 0x30 /* Transmit Trigger Level set at 56 */ + +#define SB105X_LCR 3 /* Out: Line Control Register */ +/* + * * Note: if the word length is 5 bits (SB105X_LCR_WLEN5), then setting + * * SB105X_LCR_STOP will select 1.5 stop bits, not 2 stop bits. + */ +#define SB105X_LCR_DLAB 0x80 /* Divisor Latch Enable */ +#define SB105X_LCR_SBC 0x40 /* Break Enable*/ +#define SB105X_LCR_SPAR 0x20 /* Set Stick parity */ +#define SB105X_LCR_EPAR 0x10 /* Even parity select */ +#define SB105X_LCR_PAREN 0x08 /* Parity Enable */ +#define SB105X_LCR_STOP 0x04 /* Stop bits: 0->1 bit, 1->2 bits, 1 and SB105X_LCR_WLEN5 -> 1.5 bit */ +#define SB105X_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define SB105X_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define SB105X_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define SB105X_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + +#define SB105X_LCR_BF 0xBF + +#define SB105X_MCR 4 /* Out: Modem Control Register */ +#define SB105X_MCR_CPS 0x80 /* Clock Prescaler Select */ +#define SB105X_MCR_P2S 0x40 /* Page 2 Select /Xoff Re-Transmit Access Enable */ +#define SB105X_MCR_XOA 0x20 /* Xon Any Enable */ +#define SB105X_MCR_ILB 0x10 /* Internal Loopback Enable */ +#define SB105X_MCR_OUT2 0x08 /* Out2/Interrupt Output Enable*/ +#define SB105X_MCR_OUT1 0x04 /* Out1/Interrupt Output Enable */ +#define SB105X_MCR_RTS 0x02 /* RTS# Output */ +#define SB105X_MCR_DTR 0x01 /* DTR# Output */ + +#define SB105X_LSR 5 /* In: Line Status Register */ +#define SB105X_LSR_RFEI 0x80 /* Receive FIFO data error Indicator */ +#define SB105X_LSR_TEMI 0x40 /* THR and TSR Empty Indicator */ +#define SB105X_LSR_THRE 0x20 /* THR Empty Indicator */ +#define SB105X_LSR_BII 0x10 /* Break interrupt indicator */ +#define SB105X_LSR_FEI 0x08 /* Frame error indicator */ +#define SB105X_LSR_PEI 0x04 /* Parity error indicator */ +#define SB105X_LSR_OEI 0x02 /* Overrun error indicator */ +#define SB105X_LSR_RDRI 0x01 /* Receive data ready Indicator*/ + +#define SB105X_MSR 6 /* In: Modem Status Register */ +#define SB105X_MSR_DCD 0x80 /* Data Carrier Detect */ +#define SB105X_MSR_RI 0x40 /* Ring Indicator */ +#define SB105X_MSR_DSR 0x20 /* Data Set Ready */ +#define SB105X_MSR_CTS 0x10 /* Clear to Send */ +#define SB105X_MSR_DDCD 0x08 /* Delta DCD */ +#define SB105X_MSR_DRI 0x04 /* Delta ring indicator */ +#define SB105X_MSR_DDSR 0x02 /* Delta DSR */ +#define SB105X_MSR_DCTS 0x01 /* Delta CTS */ + +#define SB105XA_MDR 6 /* Out: Multi Drop mode Register */ +#define SB105XA_MDR_NPS 0x08 /* 9th Bit Polarity Select */ +#define SB105XA_MDR_AME 0x02 /* Auto Multi-drop Enable */ +#define SB105XA_MDR_MDE 0x01 /* Multi Drop Enable */ + +#define SB105X_SPR 7 /* I/O: Scratch Register */ + +/* + * DLAB=1 + */ +#define SB105X_DLL 0 /* Out: Divisor Latch Low */ +#define SB105X_DLM 1 /* Out: Divisor Latch High */ + +/* + * ****************************************************************** + * * DLAB(LCR[7]) = 0 , MCR[6] = 1 ============= Page 2 Registers * + * ****************************************************************** + */ +#define SB105X_GICR 1 /* Global Interrupt Control Register */ +#define SB105X_GICR_GIM 0x01 /* Global Interrupt Mask */ + +#define SB105X_GISR 2 /* Global Interrupt Status Register */ +#define SB105X_GISR_MGICR0 0x80 /* Mirror the content of GICR[0] */ +#define SB105X_GISR_CS3IS 0x08 /* SB105X of CS3# Interrupt Status */ +#define SB105X_GISR_CS2IS 0x04 /* SB105X of CS2# Interrupt Status */ +#define SB105X_GISR_CS1IS 0x02 /* SB105X of CS1# Interrupt Status */ +#define SB105X_GISR_CS0IS 0x01 /* SB105X of CS0# Interrupt Status */ + +#define SB105X_TFCR 5 /* Transmit FIFO Count Register */ + +#define SB105X_RFCR 6 /* Receive FIFO Count Register */ + +#define SB105X_FSR 7 /* Flow Control Status Register */ +#define SB105X_FSR_THFS 0x20 /* Transmit Hardware Flow Control Status */ +#define SB105X_FSR_TSFS 0x10 /* Transmit Software Flow Control Status */ +#define SB105X_FSR_RHFS 0x02 /* Receive Hardware Flow Control Status */ +#define SB105X_FSR_RSFS 0x01 /* Receive Software Flow Control Status */ + +/* + * ****************************************************************** + * * LCR = 0xBF, PSR[0] = 0 ============= Page 3 Registers * + * ****************************************************************** + */ + +#define SB105X_PSR 0 /* Page Select Register */ +#define SB105X_PSR_P3KEY 0xA4 /* Page 3 Select Key */ +#define SB105X_PSR_P4KEY 0xA5 /* Page 5 Select Key */ + +#define SB105X_ATR 1 /* Auto Toggle Control Register */ +#define SB105X_ATR_RPS 0x80 /* RXEN Polarity Select */ +#define SB105X_ATR_RCMS 0x40 /* RXEN Control Mode Select */ +#define SB105X_ATR_TPS 0x20 /* TXEN Polarity Select */ +#define SB105X_ATR_TCMS 0x10 /* TXEN Control Mode Select */ +#define SB105X_ATR_ATDIS 0x00 /* Auto Toggle is disabled */ +#define SB105X_ATR_ART 0x01 /* RTS#/TXEN pin operates as TXEN */ +#define SB105X_ATR_ADT 0x02 /* DTR#/TXEN pin operates as TXEN */ +#define SB105X_ATR_A80 0x03 /* only in 80 pin use */ + +#define SB105X_EFR 2 /* (Auto) Enhanced Feature Register */ +#define SB105X_EFR_ACTS 0x80 /* Auto-CTS Flow Control Enable */ +#define SB105X_EFR_ARTS 0x40 /* Auto-RTS Flow Control Enable */ +#define SB105X_EFR_SCD 0x20 /* Special Character Detect */ +#define SB105X_EFR_EFBEN 0x10 /* Enhanced Function Bits Enable */ + +#define SB105X_XON1 4 /* Xon1 Character Register */ +#define SB105X_XON2 5 /* Xon2 Character Register */ +#define SB105X_XOFF1 6 /* Xoff1 Character Register */ +#define SB105X_XOFF2 7 /* Xoff2 Character Register */ + +/* + * ****************************************************************** + * * LCR = 0xBF, PSR[0] = 1 ============ Page 4 Registers * + * ****************************************************************** + */ + +#define SB105X_AFR 1 /* Additional Feature Register */ +#define SB105X_AFR_GIPS 0x20 /* Global Interrupt Polarity Select */ +#define SB105X_AFR_GIEN 0x10 /* Global Interrupt Enable */ +#define SB105X_AFR_AFEN 0x01 /* 256-byte FIFO Enable */ + +#define SB105X_XRCR 2 /* Xoff Re-transmit Count Register */ +#define SB105X_XRCR_NRC1 0x00 /* Transmits Xoff Character whenever the number of received data is 1 during XOFF status */ +#define SB105X_XRCR_NRC4 0x01 /* Transmits Xoff Character whenever the number of received data is 4 during XOFF status */ +#define SB105X_XRCR_NRC8 0x02 /* Transmits Xoff Character whenever the number of received data is 8 during XOFF status */ +#define SB105X_XRCR_NRC16 0x03 /* Transmits Xoff Character whenever the number of received data is 16 during XOFF status */ + +#define SB105X_TTR 4 /* Transmit FIFO Trigger Level Register */ +#define SB105X_RTR 5 /* Receive FIFO Trigger Level Register */ +#define SB105X_FUR 6 /* Flow Control Upper Threshold Register */ +#define SB105X_FLR 7 /* Flow Control Lower Threshold Register */ + + +/* page 0 */ + +#define SB105X_GET_CHAR(port) inb((port)->iobase + SB105X_RX) +#define SB105X_GET_IER(port) inb((port)->iobase + SB105X_IER) +#define SB105X_GET_ISR(port) inb((port)->iobase + SB105X_ISR) +#define SB105X_GET_LCR(port) inb((port)->iobase + SB105X_LCR) +#define SB105X_GET_MCR(port) inb((port)->iobase + SB105X_MCR) +#define SB105X_GET_LSR(port) inb((port)->iobase + SB105X_LSR) +#define SB105X_GET_MSR(port) inb((port)->iobase + SB105X_MSR) +#define SB105X_GET_SPR(port) inb((port)->iobase + SB105X_SPR) + +#define SB105X_PUT_CHAR(port,v) outb((v),(port)->iobase + SB105X_TX ) +#define SB105X_PUT_IER(port,v) outb((v),(port)->iobase + SB105X_IER ) +#define SB105X_PUT_FCR(port,v) outb((v),(port)->iobase + SB105X_FCR ) +#define SB105X_PUT_LCR(port,v) outb((v),(port)->iobase + SB105X_LCR ) +#define SB105X_PUT_MCR(port,v) outb((v),(port)->iobase + SB105X_MCR ) +#define SB105X_PUT_SPR(port,v) outb((v),(port)->iobase + SB105X_SPR ) + + +/* page 1 */ +#define SB105X_GET_REG(port,reg) inb((port)->iobase + (reg)) +#define SB105X_PUT_REG(port,reg,v) outb((v),(port)->iobase + (reg)) + +/* page 2 */ + +#define SB105X_PUT_PSR(port,v) outb((v),(port)->iobase + SB105X_PSR ) + +#endif diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c new file mode 100644 index 0000000000000..c9d6ee3903adc --- /dev/null +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -0,0 +1,3189 @@ +#include "sb_pci_mp.h" +#include +#include + +extern struct parport *parport_pc_probe_port(unsigned long base_lo, + unsigned long base_hi, + int irq, int dma, + struct device *dev, + int irqflags); + +static struct mp_device_t mp_devs[MAX_MP_DEV]; +static int mp_nrpcibrds = sizeof(mp_pciboards)/sizeof(mppcibrd_t); +static int NR_BOARD=0; +static int NR_PORTS=0; +static struct mp_port multi_ports[MAX_MP_PORT]; +static struct irq_info irq_lists[NR_IRQS]; + +static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset); +static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value); +static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset); +static int sb1054_get_register(struct sb_uart_port *port, int page, int reg); +static int sb1054_set_register(struct sb_uart_port *port, int page, int reg, int value); +static void SendATCommand(struct mp_port *mtpt); +static int set_deep_fifo(struct sb_uart_port *port, int status); +static int get_deep_fifo(struct sb_uart_port *port); +static int get_device_type(int arg); +static int set_auto_rts(struct sb_uart_port *port, int status); +static void mp_stop(struct tty_struct *tty); +static void __mp_start(struct tty_struct *tty); +static void mp_start(struct tty_struct *tty); +static void mp_tasklet_action(unsigned long data); +static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear); +static int mp_startup(struct sb_uart_state *state, int init_hw); +static void mp_shutdown(struct sb_uart_state *state); +static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios); + +static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c); +static int mp_put_char(struct tty_struct *tty, unsigned char ch); + +static void mp_put_chars(struct tty_struct *tty); +static int mp_write(struct tty_struct *tty, const unsigned char *buf, int count); +static int mp_write_room(struct tty_struct *tty); +static int mp_chars_in_buffer(struct tty_struct *tty); +static void mp_flush_buffer(struct tty_struct *tty); +static void mp_send_xchar(struct tty_struct *tty, char ch); +static void mp_throttle(struct tty_struct *tty); +static void mp_unthrottle(struct tty_struct *tty); +static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo); +static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo); +static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value); + +static int mp_tiocmget(struct tty_struct *tty); +static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); +static int mp_break_ctl(struct tty_struct *tty, int break_state); +static int mp_do_autoconfig(struct sb_uart_state *state); +static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg); +static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt); +static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); +static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios); +static void mp_close(struct tty_struct *tty, struct file *filp); +static void mp_wait_until_sent(struct tty_struct *tty, int timeout); +static void mp_hangup(struct tty_struct *tty); +static void mp_update_termios(struct sb_uart_state *state); +static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state); +static struct sb_uart_state *uart_get(struct uart_driver *drv, int line); +static int mp_open(struct tty_struct *tty, struct file *filp); +static const char *mp_type(struct sb_uart_port *port); +static void mp_change_pm(struct sb_uart_state *state, int pm_state); +static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port); +static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port); +static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state); +static int mp_register_driver(struct uart_driver *drv); +static void mp_unregister_driver(struct uart_driver *drv); +static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port); +static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port); +static void autoconfig(struct mp_port *mtpt, unsigned int probeflags); +static void autoconfig_irq(struct mp_port *mtpt); +static void multi_stop_tx(struct sb_uart_port *port); +static void multi_start_tx(struct sb_uart_port *port); +static void multi_stop_rx(struct sb_uart_port *port); +static void multi_enable_ms(struct sb_uart_port *port); +static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ); +static _INLINE_ void transmit_chars(struct mp_port *mtpt); +static _INLINE_ void check_modem_status(struct mp_port *mtpt); +static inline void multi_handle_port(struct mp_port *mtpt); +static irqreturn_t multi_interrupt(int irq, void *dev_id); +static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt); +static int serial_link_irq_chain(struct mp_port *mtpt); +static void serial_unlink_irq_chain(struct mp_port *mtpt); +static void multi_timeout(unsigned long data); +static unsigned int multi_tx_empty(struct sb_uart_port *port); +static unsigned int multi_get_mctrl(struct sb_uart_port *port); +static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl); +static void multi_break_ctl(struct sb_uart_port *port, int break_state); +static int multi_startup(struct sb_uart_port *port); +static void multi_shutdown(struct sb_uart_port *port); +static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud); +static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old); +static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate); +static void multi_release_std_resource(struct mp_port *mtpt); +static void multi_release_port(struct sb_uart_port *port); +static int multi_request_port(struct sb_uart_port *port); +static void multi_config_port(struct sb_uart_port *port, int flags); +static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser); +static const char *multi_type(struct sb_uart_port *port); +static void __init multi_init_ports(void); +static void __init multi_register_ports(struct uart_driver *drv); +static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd); + +static int deep[256]; +static int deep_count; +static int fcr_arr[256]; +static int fcr_count; +static int ttr[256]; +static int ttr_count; +static int rtr[256]; +static int rtr_count; + +module_param_array(deep,int,&deep_count,0); +module_param_array(fcr_arr,int,&fcr_count,0); +module_param_array(ttr,int,&ttr_count,0); +module_param_array(rtr,int,&rtr_count,0); + +static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset) +{ + return inb(mtpt->port.iobase + offset); +} + +static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value) +{ + outb(value, mtpt->port.iobase + offset); +} + +static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset) +{ + return inb(mtpt->option_base_addr + offset); +} + +static int sb1053a_get_interface(struct mp_port *mtpt, int port_num) +{ + unsigned long option_base_addr = mtpt->option_base_addr; + unsigned int interface = 0; + + switch (port_num) + { + case 0: + case 1: + /* set GPO[1:0] = 00 */ + outb(0x00, option_base_addr + MP_OPTR_GPODR); + break; + case 2: + case 3: + /* set GPO[1:0] = 01 */ + outb(0x01, option_base_addr + MP_OPTR_GPODR); + break; + case 4: + case 5: + /* set GPO[1:0] = 10 */ + outb(0x02, option_base_addr + MP_OPTR_GPODR); + break; + default: + break; + } + + port_num &= 0x1; + + /* get interface */ + interface = inb(option_base_addr + MP_OPTR_IIR0 + port_num); + + /* set GPO[1:0] = 11 */ + outb(0x03, option_base_addr + MP_OPTR_GPODR); + + return (interface); +} + +static int sb1054_get_register(struct sb_uart_port *port, int page, int reg) +{ + int ret = 0; + unsigned int lcr = 0; + unsigned int mcr = 0; + unsigned int tmp = 0; + + if( page <= 0) + { + printk(" page 0 can not use this function\n"); + return -1; + } + + switch(page) + { + case 1: + lcr = SB105X_GET_LCR(port); + tmp = lcr | SB105X_LCR_DLAB; + SB105X_PUT_LCR(port, tmp); + + tmp = SB105X_GET_LCR(port); + + ret = SB105X_GET_REG(port,reg); + SB105X_PUT_LCR(port,lcr); + break; + case 2: + mcr = SB105X_GET_MCR(port); + tmp = mcr | SB105X_MCR_P2S; + SB105X_PUT_MCR(port,tmp); + + ret = SB105X_GET_REG(port,reg); + + SB105X_PUT_MCR(port,mcr); + break; + case 3: + lcr = SB105X_GET_LCR(port); + tmp = lcr | SB105X_LCR_BF; + SB105X_PUT_LCR(port,tmp); + SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P3KEY); + + ret = SB105X_GET_REG(port,reg); + + SB105X_PUT_LCR(port,lcr); + break; + case 4: + lcr = SB105X_GET_LCR(port); + tmp = lcr | SB105X_LCR_BF; + SB105X_PUT_LCR(port,tmp); + SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P4KEY); + + ret = SB105X_GET_REG(port,reg); + + SB105X_PUT_LCR(port,lcr); + break; + default: + printk(" error invalid page number \n"); + return -1; + } + + return ret; +} + +static int sb1054_set_register(struct sb_uart_port *port, int page, int reg, int value) +{ + int lcr = 0; + int mcr = 0; + int ret = 0; + + if( page <= 0) + { + printk(" page 0 can not use this function\n"); + return -1; + } + switch(page) + { + case 1: + lcr = SB105X_GET_LCR(port); + SB105X_PUT_LCR(port, lcr | SB105X_LCR_DLAB); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_LCR(port, lcr); + ret = 1; + break; + case 2: + mcr = SB105X_GET_MCR(port); + SB105X_PUT_MCR(port, mcr | SB105X_MCR_P2S); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_MCR(port, mcr); + ret = 1; + break; + case 3: + lcr = SB105X_GET_LCR(port); + SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF); + SB105X_PUT_PSR(port, SB105X_PSR_P3KEY); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_LCR(port, lcr); + ret = 1; + break; + case 4: + lcr = SB105X_GET_LCR(port); + SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF); + SB105X_PUT_PSR(port, SB105X_PSR_P4KEY); + + SB105X_PUT_REG(port,reg,value); + + SB105X_PUT_LCR(port, lcr); + ret = 1; + break; + default: + printk(" error invalid page number \n"); + return -1; + } + + return ret; +} + +static int set_multidrop_mode(struct sb_uart_port *port, unsigned int mode) +{ + int mdr = SB105XA_MDR_NPS; + + if (mode & MDMODE_ENABLE) + { + mdr |= SB105XA_MDR_MDE; + } + + if (1) //(mode & MDMODE_AUTO) + { + int efr = 0; + mdr |= SB105XA_MDR_AME; + efr = sb1054_get_register(port, PAGE_3, SB105X_EFR); + efr |= SB105X_EFR_SCD; + sb1054_set_register(port, PAGE_3, SB105X_EFR, efr); + } + + sb1054_set_register(port, PAGE_1, SB105XA_MDR, mdr); + port->mdmode &= ~0x6; + port->mdmode |= mode; + printk("[%d] multidrop init: %x\n", port->line, port->mdmode); + + return 0; +} + +static int get_multidrop_addr(struct sb_uart_port *port) +{ + return sb1054_get_register(port, PAGE_3, SB105X_XOFF2); +} + +static int set_multidrop_addr(struct sb_uart_port *port, unsigned int addr) +{ + sb1054_set_register(port, PAGE_3, SB105X_XOFF2, addr); + + return 0; +} + +static void SendATCommand(struct mp_port *mtpt) +{ + // a t cr lf + unsigned char ch[] = {0x61,0x74,0x0d,0x0a,0x0}; + unsigned char lineControl; + unsigned char i=0; + unsigned char Divisor = 0xc; + + lineControl = serial_inp(mtpt,UART_LCR); + serial_outp(mtpt,UART_LCR,(lineControl | UART_LCR_DLAB)); + serial_outp(mtpt,UART_DLL,(Divisor & 0xff)); + serial_outp(mtpt,UART_DLM,(Divisor & 0xff00)>>8); //baudrate is 4800 + + + serial_outp(mtpt,UART_LCR,lineControl); + serial_outp(mtpt,UART_LCR,0x03); // N-8-1 + serial_outp(mtpt,UART_FCR,7); + serial_outp(mtpt,UART_MCR,0x3); + while(ch[i]){ + while((serial_inp(mtpt,UART_LSR) & 0x60) !=0x60){ + ; + } + serial_outp(mtpt,0,ch[i++]); + } + + +}// end of SendATCommand() + +static int set_deep_fifo(struct sb_uart_port *port, int status) +{ + int afr_status = 0; + afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); + + if(status == ENABLE) + { + afr_status |= SB105X_AFR_AFEN; + } + else + { + afr_status &= ~SB105X_AFR_AFEN; + } + + sb1054_set_register(port,PAGE_4,SB105X_AFR,afr_status); + sb1054_set_register(port,PAGE_4,SB105X_TTR,ttr[port->line]); + sb1054_set_register(port,PAGE_4,SB105X_RTR,rtr[port->line]); + afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); + + return afr_status; +} + +static int get_device_type(int arg) +{ + int ret; + ret = inb(mp_devs[arg].option_reg_addr+MP_OPTR_DIR0); + ret = (ret & 0xf0) >> 4; + switch (ret) + { + case DIR_UART_16C550: + return PORT_16C55X; + case DIR_UART_16C1050: + return PORT_16C105X; + case DIR_UART_16C1050A: + /* + if (mtpt->port.line < 2) + { + return PORT_16C105XA; + } + else + { + if (mtpt->device->device_id & 0x50) + { + return PORT_16C55X; + } + else + { + return PORT_16C105X; + } + }*/ + return PORT_16C105XA; + default: + return PORT_UNKNOWN; + } + +} +static int get_deep_fifo(struct sb_uart_port *port) +{ + int afr_status = 0; + afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR); + return afr_status; +} + +static int set_auto_rts(struct sb_uart_port *port, int status) +{ + int atr_status = 0; + +#if 0 + int efr_status = 0; + + efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR); + if(status == ENABLE) + efr_status |= SB105X_EFR_ARTS; + else + efr_status &= ~SB105X_EFR_ARTS; + sb1054_set_register(port,PAGE_3,SB105X_EFR,efr_status); + efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR); +#endif + +//ATR + atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR); + switch(status) + { + case RS422PTP: + atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_A80); + break; + case RS422MD: + atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); + break; + case RS485NE: + atr_status = (SB105X_ATR_RCMS) | (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); + break; + case RS485ECHO: + atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80); + break; + } + + sb1054_set_register(port,PAGE_3,SB105X_ATR,atr_status); + atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR); + + return atr_status; +} + +static void mp_stop(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_tx(port); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void __mp_start(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + + if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && + !tty->stopped && !tty->hw_stopped) + port->ops->start_tx(port); +} + +static void mp_start(struct tty_struct *tty) +{ + __mp_start(tty); +} + +static void mp_tasklet_action(unsigned long data) +{ + struct sb_uart_state *state = (struct sb_uart_state *)data; + struct tty_struct *tty; + + printk("tasklet is called!\n"); + tty = state->info->tty; + tty_wakeup(tty); +} + +static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear) +{ + unsigned int old; + + old = port->mctrl; + port->mctrl = (old & ~clear) | set; + if (old != port->mctrl) + port->ops->set_mctrl(port, port->mctrl); +} + +#define uart_set_mctrl(port,set) mp_update_mctrl(port,set,0) +#define uart_clear_mctrl(port,clear) mp_update_mctrl(port,0,clear) + +static int mp_startup(struct sb_uart_state *state, int init_hw) +{ + struct sb_uart_info *info = state->info; + struct sb_uart_port *port = state->port; + unsigned long page; + int retval = 0; + + if (info->flags & UIF_INITIALIZED) + return 0; + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (port->type == PORT_UNKNOWN) + return 0; + + if (!info->xmit.buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + info->xmit.buf = (unsigned char *) page; + + uart_circ_clear(&info->xmit); + } + + retval = port->ops->startup(port); + if (retval == 0) { + if (init_hw) { + mp_change_speed(state, NULL); + + if (info->tty && (info->tty->termios.c_cflag & CBAUD)) + uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); + } + + info->flags |= UIF_INITIALIZED; + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + } + + if (retval && capable(CAP_SYS_ADMIN)) + retval = 0; + + return retval; +} + +static void mp_shutdown(struct sb_uart_state *state) +{ + struct sb_uart_info *info = state->info; + struct sb_uart_port *port = state->port; + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + if (info->flags & UIF_INITIALIZED) { + info->flags &= ~UIF_INITIALIZED; + + if (!info->tty || (info->tty->termios.c_cflag & HUPCL)) + uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); + + wake_up_interruptible(&info->delta_msr_wait); + + port->ops->shutdown(port); + + synchronize_irq(port->irq); + } + tasklet_kill(&info->tlet); + + if (info->xmit.buf) { + free_page((unsigned long)info->xmit.buf); + info->xmit.buf = NULL; + } +} + +static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios) +{ + struct tty_struct *tty = state->info->tty; + struct sb_uart_port *port = state->port; + + if (!tty || port->type == PORT_UNKNOWN) + return; + + if (tty->termios.c_cflag & CRTSCTS) + state->info->flags |= UIF_CTS_FLOW; + else + state->info->flags &= ~UIF_CTS_FLOW; + + if (tty->termios.c_cflag & CLOCAL) + state->info->flags &= ~UIF_CHECK_CD; + else + state->info->flags |= UIF_CHECK_CD; + + port->ops->set_termios(port, &tty->termios, old_termios); +} + +static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c) +{ + unsigned long flags; + int ret = 0; + + if (!circ->buf) + return 0; + + spin_lock_irqsave(&port->lock, flags); + if (uart_circ_chars_free(circ) != 0) { + circ->buf[circ->head] = c; + circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); + ret = 1; + } + spin_unlock_irqrestore(&port->lock, flags); + return ret; +} + +static int mp_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct sb_uart_state *state = tty->driver_data; + + return __mp_put_char(state->port, &state->info->xmit, ch); +} + +static void mp_put_chars(struct tty_struct *tty) +{ + mp_start(tty); +} + +static int mp_write(struct tty_struct *tty, const unsigned char *buf, int count) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port; + struct circ_buf *circ; + int c, ret = 0; + + if (!state || !state->info) { + return -EL3HLT; + } + + port = state->port; + circ = &state->info->xmit; + + if (!circ->buf) + return 0; + + while (1) { + c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + memcpy(circ->buf + circ->head, buf, c); + + circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); + buf += c; + count -= c; + ret += c; + } + mp_start(tty); + return ret; +} + +static int mp_write_room(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + return uart_circ_chars_free(&state->info->xmit); +} + +static int mp_chars_in_buffer(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + return uart_circ_chars_pending(&state->info->xmit); +} + +static void mp_flush_buffer(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port; + unsigned long flags; + + if (!state || !state->info) { + return; + } + + port = state->port; + spin_lock_irqsave(&port->lock, flags); + uart_circ_clear(&state->info->xmit); + spin_unlock_irqrestore(&port->lock, flags); + wake_up_interruptible(&tty->write_wait); + tty_wakeup(tty); +} + +static void mp_send_xchar(struct tty_struct *tty, char ch) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long flags; + + if (port->ops->send_xchar) + port->ops->send_xchar(port, ch); + else { + port->x_char = ch; + if (ch) { + spin_lock_irqsave(&port->lock, flags); + port->ops->start_tx(port); + spin_unlock_irqrestore(&port->lock, flags); + } + } +} + +static void mp_throttle(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + if (I_IXOFF(tty)) + mp_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios.c_cflag & CRTSCTS) + uart_clear_mctrl(state->port, TIOCM_RTS); +} + +static void mp_unthrottle(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + + if (I_IXOFF(tty)) { + if (port->x_char) + port->x_char = 0; + else + mp_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios.c_cflag & CRTSCTS) + uart_set_mctrl(port, TIOCM_RTS); +} + +static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo) +{ + struct sb_uart_port *port = state->port; + struct serial_struct tmp; + + memset(&tmp, 0, sizeof(tmp)); + tmp.type = port->type; + tmp.line = port->line; + tmp.port = port->iobase; + if (HIGH_BITS_OFFSET) + tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET; + tmp.irq = port->irq; + tmp.flags = port->flags; + tmp.xmit_fifo_size = port->fifosize; + tmp.baud_base = port->uartclk / 16; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait == USF_CLOSING_WAIT_NONE ? + ASYNC_CLOSING_WAIT_NONE : + state->closing_wait; + tmp.custom_divisor = port->custom_divisor; + tmp.hub6 = port->hub6; + tmp.io_type = port->iotype; + tmp.iomem_reg_shift = port->regshift; + tmp.iomem_base = (void *)port->mapbase; + + if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo) +{ + struct serial_struct new_serial; + struct sb_uart_port *port = state->port; + unsigned long new_port; + unsigned int change_irq, change_port, closing_wait; + unsigned int old_custom_divisor; + unsigned int old_flags, new_flags; + int retval = 0; + + if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) + return -EFAULT; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + new_serial.irq = irq_canonicalize(new_serial.irq); + + closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? + USF_CLOSING_WAIT_NONE : new_serial.closing_wait; + MP_STATE_LOCK(state); + + change_irq = new_serial.irq != port->irq; + change_port = new_port != port->iobase || + (unsigned long)new_serial.iomem_base != port->mapbase || + new_serial.hub6 != port->hub6 || + new_serial.io_type != port->iotype || + new_serial.iomem_reg_shift != port->regshift || + new_serial.type != port->type; + old_flags = port->flags; + new_flags = new_serial.flags; + old_custom_divisor = port->custom_divisor; + + if (!capable(CAP_SYS_ADMIN)) { + retval = -EPERM; + if (change_irq || change_port || + (new_serial.baud_base != port->uartclk / 16) || + (new_serial.close_delay != state->close_delay) || + (closing_wait != state->closing_wait) || + (new_serial.xmit_fifo_size != port->fifosize) || + (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0)) + goto exit; + port->flags = ((port->flags & ~UPF_USR_MASK) | + (new_flags & UPF_USR_MASK)); + port->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + if (port->ops->verify_port) + retval = port->ops->verify_port(port, &new_serial); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)) + retval = -EINVAL; + + if (retval) + goto exit; + + if (change_port || change_irq) { + retval = -EBUSY; + + if (uart_users(state) > 1) + goto exit; + + mp_shutdown(state); + } + + if (change_port) { + unsigned long old_iobase, old_mapbase; + unsigned int old_type, old_iotype, old_hub6, old_shift; + + old_iobase = port->iobase; + old_mapbase = port->mapbase; + old_type = port->type; + old_hub6 = port->hub6; + old_iotype = port->iotype; + old_shift = port->regshift; + + if (old_type != PORT_UNKNOWN) + port->ops->release_port(port); + + port->iobase = new_port; + port->type = new_serial.type; + port->hub6 = new_serial.hub6; + port->iotype = new_serial.io_type; + port->regshift = new_serial.iomem_reg_shift; + port->mapbase = (unsigned long)new_serial.iomem_base; + + if (port->type != PORT_UNKNOWN) { + retval = port->ops->request_port(port); + } else { + retval = 0; + } + + if (retval && old_type != PORT_UNKNOWN) { + port->iobase = old_iobase; + port->type = old_type; + port->hub6 = old_hub6; + port->iotype = old_iotype; + port->regshift = old_shift; + port->mapbase = old_mapbase; + retval = port->ops->request_port(port); + if (retval) + port->type = PORT_UNKNOWN; + + retval = -EBUSY; + } + } + + port->irq = new_serial.irq; + port->uartclk = new_serial.baud_base * 16; + port->flags = (port->flags & ~UPF_CHANGE_MASK) | + (new_flags & UPF_CHANGE_MASK); + port->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay; + state->closing_wait = closing_wait; + port->fifosize = new_serial.xmit_fifo_size; + if (state->info->tty) + state->info->tty->low_latency = + (port->flags & UPF_LOW_LATENCY) ? 1 : 0; + +check_and_exit: + retval = 0; + if (port->type == PORT_UNKNOWN) + goto exit; + if (state->info->flags & UIF_INITIALIZED) { + if (((old_flags ^ port->flags) & UPF_SPD_MASK) || + old_custom_divisor != port->custom_divisor) { + if (port->flags & UPF_SPD_MASK) { + printk(KERN_NOTICE + "%s sets custom speed on ttyMP%d. This " + "is deprecated.\n", current->comm, + port->line); + } + mp_change_speed(state, NULL); + } + } else + retval = mp_startup(state, 1); +exit: + MP_STATE_UNLOCK(state); + return retval; +} + + +static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value) +{ + struct sb_uart_port *port = state->port; + unsigned int result; + + result = port->ops->tx_empty(port); + + if (port->x_char || + ((uart_circ_chars_pending(&state->info->xmit) > 0) && + !state->info->tty->stopped && !state->info->tty->hw_stopped)) + result &= ~TIOCSER_TEMT; + + return put_user(result, value); +} + +static int mp_tiocmget(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + int result = -EIO; + + MP_STATE_LOCK(state); + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + result = port->mctrl; + spin_lock_irq(&port->lock); + result |= port->ops->get_mctrl(port); + spin_unlock_irq(&port->lock); + } + MP_STATE_UNLOCK(state); + return result; +} + +static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + int ret = -EIO; + + + MP_STATE_LOCK(state); + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + mp_update_mctrl(port, set, clear); + ret = 0; + } + MP_STATE_UNLOCK(state); + + return ret; +} + +static int mp_break_ctl(struct tty_struct *tty, int break_state) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + + MP_STATE_LOCK(state); + + if (port->type != PORT_UNKNOWN) + port->ops->break_ctl(port, break_state); + + MP_STATE_UNLOCK(state); + return 0; +} + +static int mp_do_autoconfig(struct sb_uart_state *state) +{ + struct sb_uart_port *port = state->port; + int flags, ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (mutex_lock_interruptible(&state->mutex)) + return -ERESTARTSYS; + ret = -EBUSY; + if (uart_users(state) == 1) { + mp_shutdown(state); + + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + + port->ops->config_port(port, flags); + + ret = mp_startup(state, 1); + } + MP_STATE_UNLOCK(state); + return ret; +} + +static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg) +{ + struct sb_uart_port *port = state->port; + DECLARE_WAITQUEUE(wait, current); + struct sb_uart_icount cprev, cnow; + int ret; + + spin_lock_irq(&port->lock); + memcpy(&cprev, &port->icount, sizeof(struct sb_uart_icount)); + + port->ops->enable_ms(port); + spin_unlock_irq(&port->lock); + + add_wait_queue(&state->info->delta_msr_wait, &wait); + for (;;) { + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount)); + spin_unlock_irq(&port->lock); + + set_current_state(TASK_INTERRUPTIBLE); + + if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) { + ret = 0; + break; + } + + schedule(); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + cprev = cnow; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&state->info->delta_msr_wait, &wait); + + return ret; +} + +static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt) +{ + struct serial_icounter_struct icount = {}; + struct sb_uart_icount cnow; + struct sb_uart_port *port = state->port; + + spin_lock_irq(&port->lock); + memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount)); + spin_unlock_irq(&port->lock); + + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0; +} + +static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) +{ + struct sb_uart_state *state = tty->driver_data; + struct mp_port *info = (struct mp_port *)state->port; + int ret = -ENOIOCTLCMD; + + + switch (cmd) { + case TIOCSMULTIDROP: + /* set multi-drop mode enable or disable, and default operation mode is H/W mode */ + if (info->port.type == PORT_16C105XA) + { + //arg &= ~0x6; + //state->port->mdmode = 0; + return set_multidrop_mode((struct sb_uart_port *)info, (unsigned int)arg); + } + ret = -ENOTSUPP; + break; + case GETDEEPFIFO: + ret = get_deep_fifo(state->port); + return ret; + case SETDEEPFIFO: + ret = set_deep_fifo(state->port,arg); + deep[state->port->line] = arg; + return ret; + case SETTTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_set_register(state->port,PAGE_4,SB105X_TTR,arg); + ttr[state->port->line] = arg; + } + return ret; + case SETRTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_set_register(state->port,PAGE_4,SB105X_RTR,arg); + rtr[state->port->line] = arg; + } + return ret; + case GETTTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_get_register(state->port,PAGE_4,SB105X_TTR); + } + return ret; + case GETRTR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_get_register(state->port,PAGE_4,SB105X_RTR); + } + return ret; + + case SETFCR: + if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){ + ret = sb1054_set_register(state->port,PAGE_1,SB105X_FCR,arg); + } + else{ + serial_out(info,2,arg); + } + + return ret; + case TIOCSMDADDR: + /* set multi-drop address */ + if (info->port.type == PORT_16C105XA) + { + state->port->mdmode |= MDMODE_ADDR; + return set_multidrop_addr((struct sb_uart_port *)info, (unsigned int)arg); + } + ret = -ENOTSUPP; + break; + + case TIOCGMDADDR: + /* set multi-drop address */ + if ((info->port.type == PORT_16C105XA) && (state->port->mdmode & MDMODE_ADDR)) + { + return get_multidrop_addr((struct sb_uart_port *)info); + } + ret = -ENOTSUPP; + break; + + case TIOCSENDADDR: + /* send address in multi-drop mode */ + if ((info->port.type == PORT_16C105XA) + && (state->port->mdmode & (MDMODE_ENABLE))) + { + if (mp_chars_in_buffer(tty) > 0) + { + tty_wait_until_sent(tty, 0); + } + //while ((serial_in(info, UART_LSR) & 0x60) != 0x60); + //while (sb1054_get_register(state->port, PAGE_2, SB105X_TFCR) != 0); + while ((serial_in(info, UART_LSR) & 0x60) != 0x60); + serial_out(info, UART_SCR, (int)arg); + } + break; + + case TIOCGSERIAL: + ret = mp_get_info(state, (struct serial_struct *)arg); + break; + + case TIOCSSERIAL: + ret = mp_set_info(state, (struct serial_struct *)arg); + break; + + case TIOCSERCONFIG: + ret = mp_do_autoconfig(state); + break; + + case TIOCSERGWILD: /* obsolete */ + case TIOCSERSWILD: /* obsolete */ + ret = 0; + break; + /* for Multiport */ + case TIOCGNUMOFPORT: /* Get number of ports */ + return NR_PORTS; + case TIOCGGETDEVID: + return mp_devs[arg].device_id; + case TIOCGGETREV: + return mp_devs[arg].revision; + case TIOCGGETNRPORTS: + return mp_devs[arg].nr_ports; + case TIOCGGETBDNO: + return NR_BOARD; + case TIOCGGETINTERFACE: + if (mp_devs[arg].revision == 0xc0) + { + /* for SB16C1053APCI */ + return (sb1053a_get_interface(info, info->port.line)); + } + else + { + return (inb(mp_devs[arg].option_reg_addr+MP_OPTR_IIR0+(state->port->line/8))); + } + case TIOCGGETPORTTYPE: + ret = get_device_type(arg); + return ret; + case TIOCSMULTIECHO: /* set to multi-drop mode(RS422) or echo mode(RS485)*/ + outb( ( inb(info->interface_config_addr) & ~0x03 ) | 0x01 , + info->interface_config_addr); + return 0; + case TIOCSPTPNOECHO: /* set to multi-drop mode(RS422) or echo mode(RS485) */ + outb( ( inb(info->interface_config_addr) & ~0x03 ) , + info->interface_config_addr); + return 0; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + if (tty->flags & (1 << TTY_IO_ERROR)) { + ret = -EIO; + goto out; + } + + switch (cmd) { + case TIOCMIWAIT: + ret = mp_wait_modem_status(state, arg); + break; + + case TIOCGICOUNT: + ret = mp_get_count(state, (struct serial_icounter_struct *)arg); + break; + } + + if (ret != -ENOIOCTLCMD) + goto out; + + MP_STATE_LOCK(state); + switch (cmd) { + case TIOCSERGETLSR: /* Get line status register */ + ret = mp_get_lsr_info(state, (unsigned int *)arg); + break; + + default: { + struct sb_uart_port *port = state->port; + if (port->ops->ioctl) + ret = port->ops->ioctl(port, cmd, arg); + break; + } + } + + MP_STATE_UNLOCK(state); +out: + return ret; +} + +static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios) +{ + struct sb_uart_state *state = tty->driver_data; + unsigned long flags; + unsigned int cflag = tty->termios.c_cflag; + +#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + if ((cflag ^ old_termios->c_cflag) == 0 && + RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0) + return; + + mp_change_speed(state, old_termios); + + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) + uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR); + + if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + unsigned int mask = TIOCM_DTR; + if (!(cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) + mask |= TIOCM_RTS; + uart_set_mctrl(state->port, mask); + } + + if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) { + spin_lock_irqsave(&state->port->lock, flags); + tty->hw_stopped = 0; + __mp_start(tty); + spin_unlock_irqrestore(&state->port->lock, flags); + } + + if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) { + spin_lock_irqsave(&state->port->lock, flags); + if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) { + tty->hw_stopped = 1; + state->port->ops->stop_tx(state->port); + } + spin_unlock_irqrestore(&state->port->lock, flags); + } +} + +static void mp_close(struct tty_struct *tty, struct file *filp) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port; + + printk("mp_close!\n"); + if (!state || !state->port) + return; + + port = state->port; + + printk("close1 %d\n", __LINE__); + MP_STATE_LOCK(state); + + printk("close2 %d\n", __LINE__); + if (tty_hung_up_p(filp)) + goto done; + + printk("close3 %d\n", __LINE__); + if ((tty->count == 1) && (state->count != 1)) { + printk("mp_close: bad serial port count; tty->count is 1, " + "state->count is %d\n", state->count); + state->count = 1; + } + printk("close4 %d\n", __LINE__); + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttyMP%d: %d\n", + port->line, state->count); + state->count = 0; + } + if (state->count) + goto done; + + tty->closing = 1; + + printk("close5 %d\n", __LINE__); + if (state->closing_wait != USF_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, state->closing_wait); + + printk("close6 %d\n", __LINE__); + if (state->info->flags & UIF_INITIALIZED) { + unsigned long flags; + spin_lock_irqsave(&port->lock, flags); + port->ops->stop_rx(port); + spin_unlock_irqrestore(&port->lock, flags); + mp_wait_until_sent(tty, port->timeout); + } + printk("close7 %d\n", __LINE__); + + mp_shutdown(state); + printk("close8 %d\n", __LINE__); + mp_flush_buffer(tty); + tty_ldisc_flush(tty); + tty->closing = 0; + state->info->tty = NULL; + if (state->info->blocked_open) + { + if (state->close_delay) + { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(state->close_delay); + } + } + else + { + mp_change_pm(state, 3); + } + printk("close8 %d\n", __LINE__); + + state->info->flags &= ~UIF_NORMAL_ACTIVE; + wake_up_interruptible(&state->info->open_wait); + +done: + printk("close done\n"); + MP_STATE_UNLOCK(state); + module_put(THIS_MODULE); +} + +static void mp_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct sb_uart_state *state = tty->driver_data; + struct sb_uart_port *port = state->port; + unsigned long char_time, expire; + + if (port->type == PORT_UNKNOWN || port->fifosize == 0) + return; + + char_time = (port->timeout - HZ/50) / port->fifosize; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + + if (timeout == 0 || timeout > 2 * port->timeout) + timeout = 2 * port->timeout; + + expire = jiffies + timeout; + + while (!port->ops->tx_empty(port)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (time_after(jiffies, expire)) + break; + } + set_current_state(TASK_RUNNING); /* might not be needed */ +} + +static void mp_hangup(struct tty_struct *tty) +{ + struct sb_uart_state *state = tty->driver_data; + + MP_STATE_LOCK(state); + if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { + mp_flush_buffer(tty); + mp_shutdown(state); + state->count = 0; + state->info->flags &= ~UIF_NORMAL_ACTIVE; + state->info->tty = NULL; + wake_up_interruptible(&state->info->open_wait); + wake_up_interruptible(&state->info->delta_msr_wait); + } + MP_STATE_UNLOCK(state); +} + +static void mp_update_termios(struct sb_uart_state *state) +{ + struct tty_struct *tty = state->info->tty; + struct sb_uart_port *port = state->port; + + if (!(tty->flags & (1 << TTY_IO_ERROR))) { + mp_change_speed(state, NULL); + + if (tty->termios.c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); + } +} + +static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state) +{ + DECLARE_WAITQUEUE(wait, current); + struct sb_uart_info *info = state->info; + struct sb_uart_port *port = state->port; + unsigned int mctrl; + + info->blocked_open++; + state->count--; + + add_wait_queue(&info->open_wait, &wait); + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + if (tty_hung_up_p(filp) || info->tty == NULL) + break; + + if (!(info->flags & UIF_INITIALIZED)) + break; + + if ((filp->f_flags & O_NONBLOCK) || + (info->tty->termios.c_cflag & CLOCAL) || + (info->tty->flags & (1 << TTY_IO_ERROR))) { + break; + } + + if (info->tty->termios.c_cflag & CBAUD) + uart_set_mctrl(port, TIOCM_DTR); + + spin_lock_irq(&port->lock); + port->ops->enable_ms(port); + mctrl = port->ops->get_mctrl(port); + spin_unlock_irq(&port->lock); + if (mctrl & TIOCM_CAR) + break; + + MP_STATE_UNLOCK(state); + schedule(); + MP_STATE_LOCK(state); + + if (signal_pending(current)) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + + state->count++; + info->blocked_open--; + + if (signal_pending(current)) + return -ERESTARTSYS; + + if (!info->tty || tty_hung_up_p(filp)) + return -EAGAIN; + + return 0; +} + +static struct sb_uart_state *uart_get(struct uart_driver *drv, int line) +{ + struct sb_uart_state *state; + + MP_MUTEX_LOCK(mp_mutex); + state = drv->state + line; + if (mutex_lock_interruptible(&state->mutex)) { + state = ERR_PTR(-ERESTARTSYS); + goto out; + } + state->count++; + if (!state->port) { + state->count--; + MP_STATE_UNLOCK(state); + state = ERR_PTR(-ENXIO); + goto out; + } + + if (!state->info) { + state->info = kmalloc(sizeof(struct sb_uart_info), GFP_KERNEL); + if (state->info) { + memset(state->info, 0, sizeof(struct sb_uart_info)); + init_waitqueue_head(&state->info->open_wait); + init_waitqueue_head(&state->info->delta_msr_wait); + + state->port->info = state->info; + + tasklet_init(&state->info->tlet, mp_tasklet_action, + (unsigned long)state); + } else { + state->count--; + MP_STATE_UNLOCK(state); + state = ERR_PTR(-ENOMEM); + } + } + +out: + MP_MUTEX_UNLOCK(mp_mutex); + return state; +} + +static int mp_open(struct tty_struct *tty, struct file *filp) +{ + struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; + struct sb_uart_state *state; + int retval; + int line = tty->index; + struct mp_port *mtpt; + + retval = -ENODEV; + if (line >= tty->driver->num) + goto fail; + + state = uart_get(drv, line); + + if (IS_ERR(state)) { + retval = PTR_ERR(state); + goto fail; + } + + mtpt = (struct mp_port *)state->port; + + tty->driver_data = state; + tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; + tty->alt_speed = 0; + state->info->tty = tty; + + if (tty_hung_up_p(filp)) { + retval = -EAGAIN; + state->count--; + MP_STATE_UNLOCK(state); + goto fail; + } + + if (state->count == 1) + mp_change_pm(state, 0); + + retval = mp_startup(state, 0); + + if (retval == 0) + retval = mp_block_til_ready(filp, state); + MP_STATE_UNLOCK(state); + + if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { + state->info->flags |= UIF_NORMAL_ACTIVE; + + mp_update_termios(state); + } + + uart_clear_mctrl(state->port, TIOCM_RTS); + try_module_get(THIS_MODULE); +fail: + return retval; +} + + +static const char *mp_type(struct sb_uart_port *port) +{ + const char *str = NULL; + + if (port->ops->type) + str = port->ops->type(port); + + if (!str) + str = "unknown"; + + return str; +} + +static void mp_change_pm(struct sb_uart_state *state, int pm_state) +{ + struct sb_uart_port *port = state->port; + if (port->ops->pm) + port->ops->pm(port, pm_state, state->pm_state); + state->pm_state = pm_state; +} + +static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port) +{ + char address[64]; + + switch (port->iotype) { + case UPIO_PORT: + snprintf(address, sizeof(address),"I/O 0x%x", port->iobase); + break; + case UPIO_HUB6: + snprintf(address, sizeof(address),"I/O 0x%x offset 0x%x", port->iobase, port->hub6); + break; + case UPIO_MEM: + snprintf(address, sizeof(address),"MMIO 0x%lx", port->mapbase); + break; + default: + snprintf(address, sizeof(address),"*unknown*" ); + strlcpy(address, "*unknown*", sizeof(address)); + break; + } + + printk( "%s%d at %s (irq = %d) is a %s\n", + drv->dev_name, port->line, address, port->irq, mp_type(port)); + +} + +static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port) +{ + unsigned int flags; + + + if (!port->iobase && !port->mapbase && !port->membase) + { + DPRINTK("%s error \n",__FUNCTION__); + return; + } + flags = UART_CONFIG_TYPE; + if (port->flags & UPF_AUTO_IRQ) + flags |= UART_CONFIG_IRQ; + if (port->flags & UPF_BOOT_AUTOCONF) { + port->type = PORT_UNKNOWN; + port->ops->config_port(port, flags); + } + + if (port->type != PORT_UNKNOWN) { + unsigned long flags; + + mp_report_port(drv, port); + + spin_lock_irqsave(&port->lock, flags); + port->ops->set_mctrl(port, 0); + spin_unlock_irqrestore(&port->lock, flags); + + mp_change_pm(state, 3); + } +} + +static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state) +{ + struct sb_uart_port *port = state->port; + struct sb_uart_info *info = state->info; + + if (info && info->tty) + tty_hangup(info->tty); + + MP_STATE_LOCK(state); + + state->info = NULL; + + if (port->type != PORT_UNKNOWN) + port->ops->release_port(port); + + port->type = PORT_UNKNOWN; + + if (info) { + tasklet_kill(&info->tlet); + kfree(info); + } + + MP_STATE_UNLOCK(state); +} +static struct tty_operations mp_ops = { + .open = mp_open, + .close = mp_close, + .write = mp_write, + .put_char = mp_put_char, + .flush_chars = mp_put_chars, + .write_room = mp_write_room, + .chars_in_buffer= mp_chars_in_buffer, + .flush_buffer = mp_flush_buffer, + .ioctl = mp_ioctl, + .throttle = mp_throttle, + .unthrottle = mp_unthrottle, + .send_xchar = mp_send_xchar, + .set_termios = mp_set_termios, + .stop = mp_stop, + .start = mp_start, + .hangup = mp_hangup, + .break_ctl = mp_break_ctl, + .wait_until_sent= mp_wait_until_sent, +#ifdef CONFIG_PROC_FS + .proc_fops = NULL, +#endif + .tiocmget = mp_tiocmget, + .tiocmset = mp_tiocmset, +}; + +static int mp_register_driver(struct uart_driver *drv) +{ + struct tty_driver *normal = NULL; + int i, retval; + + drv->state = kmalloc(sizeof(struct sb_uart_state) * drv->nr, GFP_KERNEL); + retval = -ENOMEM; + if (!drv->state) + { + printk("SB PCI Error: Kernel memory allocation error!\n"); + goto out; + } + memset(drv->state, 0, sizeof(struct sb_uart_state) * drv->nr); + + normal = alloc_tty_driver(drv->nr); + if (!normal) + { + printk("SB PCI Error: tty allocation error!\n"); + goto out; + } + + drv->tty_driver = normal; + + normal->owner = drv->owner; + normal->magic = TTY_DRIVER_MAGIC; + normal->driver_name = drv->driver_name; + normal->name = drv->dev_name; + normal->major = drv->major; + normal->minor_start = drv->minor; + + normal->num = MAX_MP_PORT ; + + normal->type = TTY_DRIVER_TYPE_SERIAL; + normal->subtype = SERIAL_TYPE_NORMAL; + normal->init_termios = tty_std_termios; + normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + normal->driver_state = drv; + + tty_set_operations(normal, &mp_ops); + +for (i = 0; i < drv->nr; i++) { + struct sb_uart_state *state = drv->state + i; + + state->close_delay = 500; + state->closing_wait = 30000; + + mutex_init(&state->mutex); + } + + retval = tty_register_driver(normal); +out: + if (retval < 0) { + printk("Register tty driver Fail!\n"); + put_tty_driver(normal); + kfree(drv->state); + } + + return retval; +} + +void mp_unregister_driver(struct uart_driver *drv) +{ + struct tty_driver *normal = NULL; + + normal = drv->tty_driver; + + if (!normal) + { + return; + } + + tty_unregister_driver(normal); + put_tty_driver(normal); + drv->tty_driver = NULL; + + + kfree(drv->state); + +} + +static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port) +{ + struct sb_uart_state *state; + int ret = 0; + + + if (port->line >= drv->nr) + return -EINVAL; + + state = drv->state + port->line; + + MP_MUTEX_LOCK(mp_mutex); + if (state->port) { + ret = -EINVAL; + goto out; + } + + state->port = port; + + spin_lock_init(&port->lock); + port->cons = drv->cons; + port->info = state->info; + + mp_configure_port(drv, state, port); + + tty_register_device(drv->tty_driver, port->line, port->dev); + +out: + MP_MUTEX_UNLOCK(mp_mutex); + + + return ret; +} + +static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port) +{ + struct sb_uart_state *state = drv->state + port->line; + + if (state->port != port) + printk(KERN_ALERT "Removing wrong port: %p != %p\n", + state->port, port); + + MP_MUTEX_LOCK(mp_mutex); + + tty_unregister_device(drv->tty_driver, port->line); + + mp_unconfigure_port(drv, state); + state->port = NULL; + MP_MUTEX_UNLOCK(mp_mutex); + + return 0; +} + +static void autoconfig(struct mp_port *mtpt, unsigned int probeflags) +{ + unsigned char status1, scratch, scratch2, scratch3; + unsigned char save_lcr, save_mcr; + unsigned long flags; + + unsigned char u_type; + unsigned char b_ret = 0; + + if (!mtpt->port.iobase && !mtpt->port.mapbase && !mtpt->port.membase) + return; + + DEBUG_AUTOCONF("ttyMP%d: autoconf (0x%04x, 0x%p): ", + mtpt->port.line, mtpt->port.iobase, mtpt->port.membase); + + spin_lock_irqsave(&mtpt->port.lock, flags); + + if (!(mtpt->port.flags & UPF_BUGGY_UART)) { + scratch = serial_inp(mtpt, UART_IER); + serial_outp(mtpt, UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(mtpt, UART_IER) & 0x0f; + serial_outp(mtpt, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(mtpt, UART_IER) & 0x0F; + serial_outp(mtpt, UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0F) { + DEBUG_AUTOCONF("IER test failed (%02x, %02x) ", + scratch2, scratch3); + goto out; + } + } + + save_mcr = serial_in(mtpt, UART_MCR); + save_lcr = serial_in(mtpt, UART_LCR); + + if (!(mtpt->port.flags & UPF_SKIP_TEST)) { + serial_outp(mtpt, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(mtpt, UART_MSR) & 0xF0; + serial_outp(mtpt, UART_MCR, save_mcr); + if (status1 != 0x90) { + DEBUG_AUTOCONF("LOOP test failed (%02x) ", + status1); + goto out; + } + } + + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, 0); + serial_outp(mtpt, UART_LCR, 0); + + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(mtpt, UART_IIR) >> 6; + + DEBUG_AUTOCONF("iir=%d ", scratch); + if(mtpt->device->nr_ports >= 8) + b_ret = read_option_register(mtpt,(MP_OPTR_DIR0 + ((mtpt->port.line)/8))); + else + b_ret = read_option_register(mtpt,MP_OPTR_DIR0); + u_type = (b_ret & 0xf0) >> 4; + if(mtpt->port.type == PORT_UNKNOWN ) + { + switch (u_type) + { + case DIR_UART_16C550: + mtpt->port.type = PORT_16C55X; + break; + case DIR_UART_16C1050: + mtpt->port.type = PORT_16C105X; + break; + case DIR_UART_16C1050A: + if (mtpt->port.line < 2) + { + mtpt->port.type = PORT_16C105XA; + } + else + { + if (mtpt->device->device_id & 0x50) + { + mtpt->port.type = PORT_16C55X; + } + else + { + mtpt->port.type = PORT_16C105X; + } + } + break; + default: + mtpt->port.type = PORT_UNKNOWN; + break; + } + } + + if(mtpt->port.type == PORT_UNKNOWN ) + { +printk("unknow2\n"); + switch (scratch) { + case 0: + case 1: + mtpt->port.type = PORT_UNKNOWN; + break; + case 2: + case 3: + mtpt->port.type = PORT_16C55X; + break; + } + } + + serial_outp(mtpt, UART_LCR, save_lcr); + + mtpt->port.fifosize = uart_config[mtpt->port.type].dfl_xmit_fifo_size; + mtpt->capabilities = uart_config[mtpt->port.type].flags; + + if (mtpt->port.type == PORT_UNKNOWN) + goto out; + serial_outp(mtpt, UART_MCR, save_mcr); + serial_outp(mtpt, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(mtpt, UART_FCR, 0); + (void)serial_in(mtpt, UART_RX); + serial_outp(mtpt, UART_IER, 0); + +out: + spin_unlock_irqrestore(&mtpt->port.lock, flags); + DEBUG_AUTOCONF("type=%s\n", uart_config[mtpt->port.type].name); +} + +static void autoconfig_irq(struct mp_port *mtpt) +{ + unsigned char save_mcr, save_ier; + unsigned long irqs; + int irq; + + /* forget possible initially masked and pending IRQ */ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(mtpt, UART_MCR); + save_ier = serial_inp(mtpt, UART_IER); + serial_outp(mtpt, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(mtpt, UART_MCR, 0); + serial_outp(mtpt, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + + serial_outp(mtpt, UART_IER, 0x0f); /* enable all intrs */ + (void)serial_inp(mtpt, UART_LSR); + (void)serial_inp(mtpt, UART_RX); + (void)serial_inp(mtpt, UART_IIR); + (void)serial_inp(mtpt, UART_MSR); + serial_outp(mtpt, UART_TX, 0xFF); + irq = probe_irq_off(irqs); + + serial_outp(mtpt, UART_MCR, save_mcr); + serial_outp(mtpt, UART_IER, save_ier); + + mtpt->port.irq = (irq > 0) ? irq : 0; +} + +static void multi_stop_tx(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + if (mtpt->ier & UART_IER_THRI) { + mtpt->ier &= ~UART_IER_THRI; + serial_out(mtpt, UART_IER, mtpt->ier); + } + + tasklet_schedule(&port->info->tlet); +} + +static void multi_start_tx(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + if (!(mtpt->ier & UART_IER_THRI)) { + mtpt->ier |= UART_IER_THRI; + serial_out(mtpt, UART_IER, mtpt->ier); + } +} + +static void multi_stop_rx(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + mtpt->ier &= ~UART_IER_RLSI; + mtpt->port.read_status_mask &= ~UART_LSR_DR; + serial_out(mtpt, UART_IER, mtpt->ier); +} + +static void multi_enable_ms(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + + mtpt->ier |= UART_IER_MSI; + serial_out(mtpt, UART_IER, mtpt->ier); +} + + +static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ) +{ + struct tty_struct *tty = mtpt->port.info->tty; + unsigned char lsr = *status; + int max_count = 256; + unsigned char ch; + char flag; + + //lsr &= mtpt->port.read_status_mask; + + do { + if ((lsr & UART_LSR_PE) && (mtpt->port.mdmode & MDMODE_ENABLE)) + { + ch = serial_inp(mtpt, UART_RX); + } + else if (lsr & UART_LSR_SPECIAL) + { + flag = 0; + ch = serial_inp(mtpt, UART_RX); + + if (lsr & UART_LSR_BI) + { + + mtpt->port.icount.brk++; + flag = TTY_BREAK; + + if (sb_uart_handle_break(&mtpt->port)) + goto ignore_char; + } + if (lsr & UART_LSR_PE) + { + mtpt->port.icount.parity++; + flag = TTY_PARITY; + } + if (lsr & UART_LSR_FE) + { + mtpt->port.icount.frame++; + flag = TTY_FRAME; + } + if (lsr & UART_LSR_OE) + { + mtpt->port.icount.overrun++; + flag = TTY_OVERRUN; + } + tty_insert_flip_char(tty, ch, flag); + } + else + { + ch = serial_inp(mtpt, UART_RX); + tty_insert_flip_char(tty, ch, 0); + } +ignore_char: + lsr = serial_inp(mtpt, UART_LSR); + } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); + + tty_flip_buffer_push(tty); +} + + + + +static _INLINE_ void transmit_chars(struct mp_port *mtpt) +{ + struct circ_buf *xmit = &mtpt->port.info->xmit; + int count; + + if (mtpt->port.x_char) { + serial_outp(mtpt, UART_TX, mtpt->port.x_char); + mtpt->port.icount.tx++; + mtpt->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&mtpt->port)) { + multi_stop_tx(&mtpt->port); + return; + } + + count = uart_circ_chars_pending(xmit); + + if(count > mtpt->port.fifosize) + { + count = mtpt->port.fifosize; + } + + printk("[%d] mdmode: %x\n", mtpt->port.line, mtpt->port.mdmode); + do { +#if 0 + /* check multi-drop mode */ + if ((mtpt->port.mdmode & (MDMODE_ENABLE | MDMODE_ADDR)) == (MDMODE_ENABLE | MDMODE_ADDR)) + { + printk("send address\n"); + /* send multi-drop address */ + serial_out(mtpt, UART_SCR, xmit->buf[xmit->tail]); + } + else +#endif + { + serial_out(mtpt, UART_TX, xmit->buf[xmit->tail]); + } + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + mtpt->port.icount.tx++; + } while (--count > 0); +} + + + +static _INLINE_ void check_modem_status(struct mp_port *mtpt) +{ + int status; + + status = serial_in(mtpt, UART_MSR); + + if ((status & UART_MSR_ANY_DELTA) == 0) + return; + + if (status & UART_MSR_TERI) + mtpt->port.icount.rng++; + if (status & UART_MSR_DDSR) + mtpt->port.icount.dsr++; + if (status & UART_MSR_DDCD) + sb_uart_handle_dcd_change(&mtpt->port, status & UART_MSR_DCD); + if (status & UART_MSR_DCTS) + sb_uart_handle_cts_change(&mtpt->port, status & UART_MSR_CTS); + + wake_up_interruptible(&mtpt->port.info->delta_msr_wait); +} + +static inline void multi_handle_port(struct mp_port *mtpt) +{ + unsigned int status = serial_inp(mtpt, UART_LSR); + + //printk("lsr: %x\n", status); + + if ((status & UART_LSR_DR) || (status & UART_LSR_SPECIAL)) + receive_chars(mtpt, &status); + check_modem_status(mtpt); + if (status & UART_LSR_THRE) + { + if ((mtpt->port.type == PORT_16C105X) + || (mtpt->port.type == PORT_16C105XA)) + transmit_chars(mtpt); + else + { + if (mtpt->interface >= RS485NE) + uart_set_mctrl(&mtpt->port, TIOCM_RTS); + + transmit_chars(mtpt); + + + if (mtpt->interface >= RS485NE) + { + while((status=serial_in(mtpt,UART_LSR) &0x60)!=0x60); + uart_clear_mctrl(&mtpt->port, TIOCM_RTS); + } + } + } +} + + + +static irqreturn_t multi_interrupt(int irq, void *dev_id) +{ + struct irq_info *iinfo = dev_id; + struct list_head *lhead, *end = NULL; + int pass_counter = 0; + + + spin_lock(&iinfo->lock); + + lhead = iinfo->head; + do { + struct mp_port *mtpt; + unsigned int iir; + + mtpt = list_entry(lhead, struct mp_port, list); + + iir = serial_in(mtpt, UART_IIR); + printk("interrupt! port %d, iir 0x%x\n", mtpt->port.line, iir); //wlee + if (!(iir & UART_IIR_NO_INT)) + { + printk("interrupt handle\n"); + spin_lock(&mtpt->port.lock); + multi_handle_port(mtpt); + spin_unlock(&mtpt->port.lock); + + end = NULL; + } else if (end == NULL) + end = lhead; + + lhead = lhead->next; + if (lhead == iinfo->head && pass_counter++ > PASS_LIMIT) + { + printk(KERN_ERR "multi: too much work for " + "irq%d\n", irq); + printk( "multi: too much work for " + "irq%d\n", irq); + break; + } + } while (lhead != end); + + spin_unlock(&iinfo->lock); + + + return IRQ_HANDLED; +} + +static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt) +{ + spin_lock_irq(&i->lock); + + if (!list_empty(i->head)) { + if (i->head == &mtpt->list) + i->head = i->head->next; + list_del(&mtpt->list); + } else { + i->head = NULL; + } + + spin_unlock_irq(&i->lock); +} + +static int serial_link_irq_chain(struct mp_port *mtpt) +{ + struct irq_info *i = irq_lists + mtpt->port.irq; + int ret, irq_flags = mtpt->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; + spin_lock_irq(&i->lock); + + if (i->head) { + list_add(&mtpt->list, i->head); + spin_unlock_irq(&i->lock); + + ret = 0; + } else { + INIT_LIST_HEAD(&mtpt->list); + i->head = &mtpt->list; + spin_unlock_irq(&i->lock); + + ret = request_irq(mtpt->port.irq, multi_interrupt, + irq_flags, "serial", i); + if (ret < 0) + serial_do_unlink(i, mtpt); + } + + return ret; +} + + + + +static void serial_unlink_irq_chain(struct mp_port *mtpt) +{ + struct irq_info *i = irq_lists + mtpt->port.irq; + + if (list_empty(i->head)) + { + free_irq(mtpt->port.irq, i); + } + serial_do_unlink(i, mtpt); +} + +static void multi_timeout(unsigned long data) +{ + struct mp_port *mtpt = (struct mp_port *)data; + + + spin_lock(&mtpt->port.lock); + multi_handle_port(mtpt); + spin_unlock(&mtpt->port.lock); + + mod_timer(&mtpt->timer, jiffies+1 ); +} + +static unsigned int multi_tx_empty(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&mtpt->port.lock, flags); + ret = serial_in(mtpt, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&mtpt->port.lock, flags); + + return ret; +} + + +static unsigned int multi_get_mctrl(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned char status; + unsigned int ret; + + status = serial_in(mtpt, UART_MSR); + + ret = 0; + if (status & UART_MSR_DCD) + ret |= TIOCM_CAR; + if (status & UART_MSR_RI) + ret |= TIOCM_RNG; + if (status & UART_MSR_DSR) + ret |= TIOCM_DSR; + if (status & UART_MSR_CTS) + ret |= TIOCM_CTS; + return ret; +} + +static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned char mcr = 0; + + mctrl &= 0xff; + + if (mctrl & TIOCM_RTS) + mcr |= UART_MCR_RTS; + if (mctrl & TIOCM_DTR) + mcr |= UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + mcr |= UART_MCR_OUT2; + if (mctrl & TIOCM_LOOP) + mcr |= UART_MCR_LOOP; + + + serial_out(mtpt, UART_MCR, mcr); +} + + +static void multi_break_ctl(struct sb_uart_port *port, int break_state) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + + spin_lock_irqsave(&mtpt->port.lock, flags); + if (break_state == -1) + mtpt->lcr |= UART_LCR_SBC; + else + mtpt->lcr &= ~UART_LCR_SBC; + serial_out(mtpt, UART_LCR, mtpt->lcr); + spin_unlock_irqrestore(&mtpt->port.lock, flags); +} + + + +static int multi_startup(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + int retval; + + mtpt->capabilities = uart_config[mtpt->port.type].flags; + mtpt->mcr = 0; + + if (mtpt->capabilities & UART_CLEAR_FIFO) { + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(mtpt, UART_FCR, 0); + } + + (void) serial_inp(mtpt, UART_LSR); + (void) serial_inp(mtpt, UART_RX); + (void) serial_inp(mtpt, UART_IIR); + (void) serial_inp(mtpt, UART_MSR); + //test-wlee 9-bit disable + serial_outp(mtpt, UART_MSR, 0); + + + if (!(mtpt->port.flags & UPF_BUGGY_UART) && + (serial_inp(mtpt, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", mtpt->port.line); + //return -ENODEV; + } + + if ((!is_real_interrupt(mtpt->port.irq)) || (mtpt->poll_type==TYPE_POLL)) { + unsigned int timeout = mtpt->port.timeout; + + timeout = timeout > 6 ? (timeout / 2 - 2) : 1; + + mtpt->timer.data = (unsigned long)mtpt; + mod_timer(&mtpt->timer, jiffies + timeout); + } + else + { + retval = serial_link_irq_chain(mtpt); + if (retval) + return retval; + } + + serial_outp(mtpt, UART_LCR, UART_LCR_WLEN8); + + spin_lock_irqsave(&mtpt->port.lock, flags); + if ((is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_INTERRUPT)) + mtpt->port.mctrl |= TIOCM_OUT2; + + multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); + spin_unlock_irqrestore(&mtpt->port.lock, flags); + + + mtpt->ier = UART_IER_RLSI | UART_IER_RDI; + serial_outp(mtpt, UART_IER, mtpt->ier); + + (void) serial_inp(mtpt, UART_LSR); + (void) serial_inp(mtpt, UART_RX); + (void) serial_inp(mtpt, UART_IIR); + (void) serial_inp(mtpt, UART_MSR); + + return 0; +} + + + +static void multi_shutdown(struct sb_uart_port *port) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned long flags; + + + mtpt->ier = 0; + serial_outp(mtpt, UART_IER, 0); + + spin_lock_irqsave(&mtpt->port.lock, flags); + mtpt->port.mctrl &= ~TIOCM_OUT2; + + multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); + spin_unlock_irqrestore(&mtpt->port.lock, flags); + + serial_out(mtpt, UART_LCR, serial_inp(mtpt, UART_LCR) & ~UART_LCR_SBC); + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_outp(mtpt, UART_FCR, 0); + + + (void) serial_in(mtpt, UART_RX); + + if ((!is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_POLL)) + { + del_timer_sync(&mtpt->timer); + } + else + { + serial_unlink_irq_chain(mtpt); + } +} + + + +static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud) +{ + unsigned int quot; + + if ((port->flags & UPF_MAGIC_MULTIPLIER) && + baud == (port->uartclk/4)) + quot = 0x8001; + else if ((port->flags & UPF_MAGIC_MULTIPLIER) && + baud == (port->uartclk/8)) + quot = 0x8002; + else + quot = sb_uart_get_divisor(port, baud); + + return quot; +} + + + + +static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old) +{ + struct mp_port *mtpt = (struct mp_port *)port; + unsigned char cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + + switch (termios->c_cflag & CSIZE) { + case CS5: + cval = 0x00; + break; + case CS6: + cval = 0x01; + break; + case CS7: + cval = 0x02; + break; + default: + case CS8: + cval = 0x03; + break; + } + + if (termios->c_cflag & CSTOPB) + cval |= 0x04; + if (termios->c_cflag & PARENB) + cval |= UART_LCR_PARITY; + if (!(termios->c_cflag & PARODD)) + cval |= UART_LCR_EPAR; + +#ifdef CMSPAR + if (termios->c_cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif + + baud = sb_uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); + quot = multi_get_divisor(port, baud); + + if (mtpt->capabilities & UART_USE_FIFO) { + //if (baud < 2400) + // fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; + //else + // fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + + // fcr = UART_FCR_ENABLE_FIFO | 0x90; + fcr = fcr_arr[mtpt->port.line]; + } + + spin_lock_irqsave(&mtpt->port.lock, flags); + + sb_uart_update_timeout(port, termios->c_cflag, baud); + + mtpt->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; + if (termios->c_iflag & INPCK) + mtpt->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE; + if (termios->c_iflag & (BRKINT | PARMRK)) + mtpt->port.read_status_mask |= UART_LSR_BI; + + mtpt->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + mtpt->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (termios->c_iflag & IGNBRK) { + mtpt->port.ignore_status_mask |= UART_LSR_BI; + if (termios->c_iflag & IGNPAR) + mtpt->port.ignore_status_mask |= UART_LSR_OE; + } + + if ((termios->c_cflag & CREAD) == 0) + mtpt->port.ignore_status_mask |= UART_LSR_DR; + + mtpt->ier &= ~UART_IER_MSI; + if (UART_ENABLE_MS(&mtpt->port, termios->c_cflag)) + mtpt->ier |= UART_IER_MSI; + + serial_out(mtpt, UART_IER, mtpt->ier); + + if (mtpt->capabilities & UART_STARTECH) { + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, + termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0); + } + + serial_outp(mtpt, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */ + + serial_outp(mtpt, UART_DLL, quot & 0xff); /* LS of divisor */ + serial_outp(mtpt, UART_DLM, quot >> 8); /* MS of divisor */ + + serial_outp(mtpt, UART_LCR, cval); /* reset DLAB */ + mtpt->lcr = cval; /* Save LCR */ + + if (fcr & UART_FCR_ENABLE_FIFO) { + /* emulated UARTs (Lucent Venus 167x) need two steps */ + serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO); + } + + serial_outp(mtpt, UART_FCR, fcr); /* set fcr */ + + + if ((mtpt->port.type == PORT_16C105X) + || (mtpt->port.type == PORT_16C105XA)) + { + if(deep[mtpt->port.line]!=0) + set_deep_fifo(port, ENABLE); + + if (mtpt->interface != RS232) + set_auto_rts(port,mtpt->interface); + + } + else + { + if (mtpt->interface >= RS485NE) + { + uart_clear_mctrl(&mtpt->port, TIOCM_RTS); + } + } + + if(mtpt->device->device_id == PCI_DEVICE_ID_MP4M) + { + SendATCommand(mtpt); + printk("SendATCommand\n"); + } + multi_set_mctrl(&mtpt->port, mtpt->port.mctrl); + spin_unlock_irqrestore(&mtpt->port.lock, flags); +} + +static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate) +{ + struct mp_port *mtpt = (struct mp_port *)port; + if (state) { + if (mtpt->capabilities & UART_STARTECH) { + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, UART_EFR_ECB); + serial_outp(mtpt, UART_LCR, 0); + serial_outp(mtpt, UART_IER, UART_IERX_SLEEP); + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, 0); + serial_outp(mtpt, UART_LCR, 0); + } + + if (mtpt->pm) + mtpt->pm(port, state, oldstate); + } + else + { + if (mtpt->capabilities & UART_STARTECH) { + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, UART_EFR_ECB); + serial_outp(mtpt, UART_LCR, 0); + serial_outp(mtpt, UART_IER, 0); + serial_outp(mtpt, UART_LCR, 0xBF); + serial_outp(mtpt, UART_EFR, 0); + serial_outp(mtpt, UART_LCR, 0); + } + + if (mtpt->pm) + mtpt->pm(port, state, oldstate); + } +} + +static void multi_release_std_resource(struct mp_port *mtpt) +{ + unsigned int size = 8 << mtpt->port.regshift; + + switch (mtpt->port.iotype) { + case UPIO_MEM: + if (!mtpt->port.mapbase) + break; + + if (mtpt->port.flags & UPF_IOREMAP) { + iounmap(mtpt->port.membase); + mtpt->port.membase = NULL; + } + + release_mem_region(mtpt->port.mapbase, size); + break; + + case UPIO_HUB6: + case UPIO_PORT: + release_region(mtpt->port.iobase,size); + break; + } +} + +static void multi_release_port(struct sb_uart_port *port) +{ +} + +static int multi_request_port(struct sb_uart_port *port) +{ + return 0; +} + +static void multi_config_port(struct sb_uart_port *port, int flags) +{ + struct mp_port *mtpt = (struct mp_port *)port; + int probeflags = PROBE_ANY; + + if (flags & UART_CONFIG_TYPE) + autoconfig(mtpt, probeflags); + if (mtpt->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ) + autoconfig_irq(mtpt); + + if (mtpt->port.type == PORT_UNKNOWN) + multi_release_std_resource(mtpt); +} + +static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser) +{ + if (ser->irq >= NR_IRQS || ser->irq < 0 || + ser->baud_base < 9600 || ser->type < PORT_UNKNOWN || + ser->type == PORT_STARTECH) + return -EINVAL; + return 0; +} + +static const char *multi_type(struct sb_uart_port *port) +{ + int type = port->type; + + if (type >= ARRAY_SIZE(uart_config)) + type = 0; + return uart_config[type].name; +} + +static struct sb_uart_ops multi_pops = { + .tx_empty = multi_tx_empty, + .set_mctrl = multi_set_mctrl, + .get_mctrl = multi_get_mctrl, + .stop_tx = multi_stop_tx, + .start_tx = multi_start_tx, + .stop_rx = multi_stop_rx, + .enable_ms = multi_enable_ms, + .break_ctl = multi_break_ctl, + .startup = multi_startup, + .shutdown = multi_shutdown, + .set_termios = multi_set_termios, + .pm = multi_pm, + .type = multi_type, + .release_port = multi_release_port, + .request_port = multi_request_port, + .config_port = multi_config_port, + .verify_port = multi_verify_port, +}; + +static struct uart_driver multi_reg = { + .owner = THIS_MODULE, + .driver_name = "goldel_tulip", + .dev_name = "ttyMP", + .major = SB_TTY_MP_MAJOR, + .minor = 0, + .nr = MAX_MP_PORT, + .cons = NULL, +}; + +static void __init multi_init_ports(void) +{ + struct mp_port *mtpt; + static int first = 1; + int i,j,k; + unsigned char osc; + unsigned char b_ret = 0; + static struct mp_device_t *sbdev; + + if (!first) + return; + first = 0; + + mtpt = multi_ports; + + for (k=0;knr_ports; i++, mtpt++) + { + mtpt->device = sbdev; + mtpt->port.iobase = sbdev->uart_access_addr + 8*i; + mtpt->port.irq = sbdev->irq; + if ( ((sbdev->device_id == PCI_DEVICE_ID_MP4)&&(sbdev->revision==0x91))) + mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i; + else if (sbdev->revision == 0xc0) + mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + (i & 0x1); + else + mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i/8; + + mtpt->option_base_addr = sbdev->option_reg_addr; + + mtpt->poll_type = sbdev->poll_type; + + mtpt->port.uartclk = BASE_BAUD * 16; + + /* get input clock information */ + osc = inb(sbdev->option_reg_addr + MP_OPTR_DIR0 + i/8) & 0x0F; + if (osc==0x0f) + osc = 0; + for(j=0;jport.uartclk *= 2; + mtpt->port.flags |= STD_COM_FLAGS | UPF_SHARE_IRQ ; + mtpt->port.iotype = UPIO_PORT; + mtpt->port.ops = &multi_pops; + + if (sbdev->revision == 0xc0) + { + /* for SB16C1053APCI */ + b_ret = sb1053a_get_interface(mtpt, i); + } + else + { + b_ret = read_option_register(mtpt,(MP_OPTR_IIR0 + i/8)); + printk("IIR_RET = %x\n",b_ret); + } + + /* default to RS232 */ + mtpt->interface = RS232; + if (IIR_RS422 == (b_ret & IIR_TYPE_MASK)) + mtpt->interface = RS422PTP; + if (IIR_RS485 == (b_ret & IIR_TYPE_MASK)) + mtpt->interface = RS485NE; + } + } +} + +static void __init multi_register_ports(struct uart_driver *drv) +{ + int i; + + multi_init_ports(); + + for (i = 0; i < NR_PORTS; i++) { + struct mp_port *mtpt = &multi_ports[i]; + + mtpt->port.line = i; + mtpt->port.ops = &multi_pops; + init_timer(&mtpt->timer); + mtpt->timer.function = multi_timeout; + mp_add_one_port(drv, &mtpt->port); + } +} + +/** + * pci_remap_base - remap BAR value of pci device + * + * PARAMETERS + * pcidev - pci_dev structure address + * offset - BAR offset PCI_BASE_ADDRESS_0 ~ PCI_BASE_ADDRESS_4 + * address - address to be changed BAR value + * size - size of address space + * + * RETURNS + * If this function performs successful, it returns 0. Otherwise, It returns -1. + */ +static int pci_remap_base(struct pci_dev *pcidev, unsigned int offset, + unsigned int address, unsigned int size) +{ +#if 0 + struct resource *root; + unsigned index = (offset - 0x10) >> 2; +#endif + + pci_write_config_dword(pcidev, offset, address); +#if 0 + root = pcidev->resource[index].parent; + release_resource(&pcidev->resource[index]); + address &= ~0x1; + pcidev->resource[index].start = address; + pcidev->resource[index].end = address + size - 1; + + if (request_resource(root, &pcidev->resource[index]) != NULL) + { + printk(KERN_ERR "pci remap conflict!! 0x%x\n", address); + return (-1); + } +#endif + + return (0); +} + +static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd) +{ + static struct mp_device_t *sbdev = mp_devs; + unsigned long addr = 0; + int j; + struct resource *ret = NULL; + + sbdev->device_id = brd.device_id; + pci_read_config_byte(pcidev, PCI_CLASS_REVISION, &(sbdev->revision)); + sbdev->name = brd.name; + sbdev->uart_access_addr = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; + + /* check revision. The SB16C1053APCI's option i/o address is BAR4 */ + if (sbdev->revision == 0xc0) + { + /* SB16C1053APCI */ + sbdev->option_reg_addr = pcidev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK; + } + else + { + sbdev->option_reg_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; + } +#if 1 + if (sbdev->revision == 0xc0) + { + outb(0x00, sbdev->option_reg_addr + MP_OPTR_GPOCR); + inb(sbdev->option_reg_addr + MP_OPTR_GPOCR); + outb(0x83, sbdev->option_reg_addr + MP_OPTR_GPOCR); + } +#endif + + sbdev->irq = pcidev->irq; + + if ((brd.device_id & 0x0800) || !(brd.device_id &0xff00)) + { + sbdev->poll_type = TYPE_INTERRUPT; + } + else + { + sbdev->poll_type = TYPE_POLL; + } + + /* codes which is specific to each board*/ + switch(brd.device_id){ + case PCI_DEVICE_ID_MP1 : + case PCIE_DEVICE_ID_MP1 : + case PCIE_DEVICE_ID_MP1E : + case PCIE_DEVICE_ID_GT_MP1 : + sbdev->nr_ports = 1; + break; + case PCI_DEVICE_ID_MP2 : + case PCIE_DEVICE_ID_MP2 : + case PCIE_DEVICE_ID_GT_MP2 : + case PCIE_DEVICE_ID_MP2B : + case PCIE_DEVICE_ID_MP2E : + sbdev->nr_ports = 2; + + /* serial base address remap */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + } + break; + case PCI_DEVICE_ID_MP4 : + case PCI_DEVICE_ID_MP4A : + case PCIE_DEVICE_ID_MP4 : + case PCI_DEVICE_ID_GT_MP4 : + case PCI_DEVICE_ID_GT_MP4A : + case PCIE_DEVICE_ID_GT_MP4 : + case PCI_DEVICE_ID_MP4M : + case PCIE_DEVICE_ID_MP4B : + sbdev->nr_ports = 4; + + if(sbdev->revision == 0x91){ + sbdev->reserved_addr[0] = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK; + outb(0x03 , sbdev->reserved_addr[0] + 0x01); + outb(0x03 , sbdev->reserved_addr[0] + 0x02); + outb(0x01 , sbdev->reserved_addr[0] + 0x20); + outb(0x00 , sbdev->reserved_addr[0] + 0x21); + request_region(sbdev->reserved_addr[0], 32, sbdev->name); + sbdev->uart_access_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK; + sbdev->option_reg_addr = pcidev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK; + } + + /* SB16C1053APCI */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 8); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 24, 8); + } + break; + case PCI_DEVICE_ID_MP6 : + case PCI_DEVICE_ID_MP6A : + case PCI_DEVICE_ID_GT_MP6 : + case PCI_DEVICE_ID_GT_MP6A : + sbdev->nr_ports = 6; + + /* SB16C1053APCI */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 16); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 32, 16); + } + break; + case PCI_DEVICE_ID_MP8 : + case PCIE_DEVICE_ID_MP8 : + case PCI_DEVICE_ID_GT_MP8 : + case PCIE_DEVICE_ID_GT_MP8 : + case PCIE_DEVICE_ID_MP8B : + sbdev->nr_ports = 8; + break; + case PCI_DEVICE_ID_MP32 : + case PCIE_DEVICE_ID_MP32 : + case PCI_DEVICE_ID_GT_MP32 : + case PCIE_DEVICE_ID_GT_MP32 : + { + int portnum_hex=0; + portnum_hex = inb(sbdev->option_reg_addr); + sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16); + } + break; +#ifdef CONFIG_PARPORT_PC + case PCI_DEVICE_ID_MP2S1P : + sbdev->nr_ports = 2; + + /* SB16C1053APCI */ + if (sbdev->revision == 0xc0) + { + int prev_port_addr = 0; + + pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr); + pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8); + } + + /* add PC compatible parallel port */ + parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); + break; + case PCI_DEVICE_ID_MP1P : + /* add PC compatible parallel port */ + parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0); + break; +#endif + } + + ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name); + + if (sbdev->revision == 0xc0) + { + ret = request_region(sbdev->option_reg_addr, 0x40, sbdev->name); + } + else + { + ret = request_region(sbdev->option_reg_addr, 0x20, sbdev->name); + } + + + NR_BOARD++; + NR_PORTS += sbdev->nr_ports; + + /* Enable PCI interrupt */ + addr = sbdev->option_reg_addr + MP_OPTR_IMR0; + for(j=0; j < (sbdev->nr_ports/8)+1; j++) + { + if (sbdev->poll_type == TYPE_INTERRUPT) + { + outb(0xff,addr +j); + } + } + sbdev++; + + return 0; +} + +static int __init multi_init(void) +{ + int ret, i; + struct pci_dev *dev = NULL; + + if(fcr_count==0) + { + for(i=0;i<256;i++) + { + fcr_arr[i] = 0x01; + + } + } + if(deep_count==0) + { + for(i=0;i<256;i++) + { + deep[i] = 1; + + } + } + if(rtr_count==0) + { + for(i=0;i<256;i++) + { + rtr[i] = 0x10; + } + } + if(ttr_count==0) + { + for(i=0;i<256;i++) + { + ttr[i] = 0x38; + } + } + + +printk("MULTI INIT\n"); + for( i=0; i< mp_nrpcibrds; i++) + { + + while( (dev = pci_get_device(mp_pciboards[i].vendor_id, mp_pciboards[i].device_id, dev) ) ) + + { +printk("FOUND~~~\n"); +// Cent OS bug fix +// if (mp_pciboards[i].device_id & 0x0800) + { + int status; + pci_disable_device(dev); + status = pci_enable_device(dev); + + if (status != 0) + { + printk("Multiport Board Enable Fail !\n\n"); + status = -ENXIO; + return status; + } + } + + init_mp_dev(dev, mp_pciboards[i]); + } + } + + for (i = 0; i < NR_IRQS; i++) + spin_lock_init(&irq_lists[i].lock); + + ret = mp_register_driver(&multi_reg); + + if (ret >= 0) + multi_register_ports(&multi_reg); + + return ret; +} + +static void __exit multi_exit(void) +{ + int i; + + for (i = 0; i < NR_PORTS; i++) + mp_remove_one_port(&multi_reg, &multi_ports[i].port); + + mp_unregister_driver(&multi_reg); +} + +module_init(multi_init); +module_exit(multi_exit); + +MODULE_DESCRIPTION("SystemBase Multiport PCI/PCIe CORE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/sb105x/sb_pci_mp.h b/drivers/staging/sb105x/sb_pci_mp.h new file mode 100644 index 0000000000000..80ae4ab046031 --- /dev/null +++ b/drivers/staging/sb105x/sb_pci_mp.h @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +#include +#include +#include + + +#define MP_TERMIOS ktermios + +#include "sb_mp_register.h" +#include "sb_ser_core.h" + +#define DRIVER_VERSION "1.1" +#define DRIVER_DATE "2012/01/05" +#define DRIVER_AUTHOR "SYSTEMBASE" +#define DRIVER_DESC "SystemBase PCI/PCIe Multiport Core" + +#define SB_TTY_MP_MAJOR 54 +#define PCI_VENDOR_ID_MULTIPORT 0x14A1 + +#define PCI_DEVICE_ID_MP1 0x4d01 +#define PCI_DEVICE_ID_MP2 0x4d02 +#define PCI_DEVICE_ID_MP4 0x4d04 +#define PCI_DEVICE_ID_MP4A 0x4d54 +#define PCI_DEVICE_ID_MP6 0x4d06 +#define PCI_DEVICE_ID_MP6A 0x4d56 +#define PCI_DEVICE_ID_MP8 0x4d08 +#define PCI_DEVICE_ID_MP32 0x4d32 +/* Parallel port */ +#define PCI_DEVICE_ID_MP1P 0x4301 +#define PCI_DEVICE_ID_MP2S1P 0x4303 + +#define PCIE_DEVICE_ID_MP1 0x4501 +#define PCIE_DEVICE_ID_MP2 0x4502 +#define PCIE_DEVICE_ID_MP4 0x4504 +#define PCIE_DEVICE_ID_MP8 0x4508 +#define PCIE_DEVICE_ID_MP32 0x4532 + +#define PCIE_DEVICE_ID_MP1E 0x4e01 +#define PCIE_DEVICE_ID_MP2E 0x4e02 +#define PCIE_DEVICE_ID_MP2B 0x4b02 +#define PCIE_DEVICE_ID_MP4B 0x4b04 +#define PCIE_DEVICE_ID_MP8B 0x4b08 + +#define PCI_DEVICE_ID_GT_MP4 0x0004 +#define PCI_DEVICE_ID_GT_MP4A 0x0054 +#define PCI_DEVICE_ID_GT_MP6 0x0006 +#define PCI_DEVICE_ID_GT_MP6A 0x0056 +#define PCI_DEVICE_ID_GT_MP8 0x0008 +#define PCI_DEVICE_ID_GT_MP32 0x0032 + +#define PCIE_DEVICE_ID_GT_MP1 0x1501 +#define PCIE_DEVICE_ID_GT_MP2 0x1502 +#define PCIE_DEVICE_ID_GT_MP4 0x1504 +#define PCIE_DEVICE_ID_GT_MP8 0x1508 +#define PCIE_DEVICE_ID_GT_MP32 0x1532 + +#define PCI_DEVICE_ID_MP4M 0x4604 //modem + +#define MAX_MP_DEV 8 +#define BD_MAX_PORT 32 /* Max serial port in one board */ +#define MAX_MP_PORT 256 /* Max serial port in one PC */ + +#define PORT_16C105XA 3 +#define PORT_16C105X 2 +#define PORT_16C55X 1 + +#define ENABLE 1 +#define DISABLE 0 + +/* ioctls */ +#define TIOCGNUMOFPORT 0x545F +#define TIOCSMULTIECHO 0x5440 +#define TIOCSPTPNOECHO 0x5441 + +#define TIOCGOPTIONREG 0x5461 +#define TIOCGDISABLEIRQ 0x5462 +#define TIOCGENABLEIRQ 0x5463 +#define TIOCGSOFTRESET 0x5464 +#define TIOCGSOFTRESETR 0x5465 +#define TIOCGREGINFO 0x5466 +#define TIOCGGETLSR 0x5467 +#define TIOCGGETDEVID 0x5468 +#define TIOCGGETBDNO 0x5469 +#define TIOCGGETINTERFACE 0x546A +#define TIOCGGETREV 0x546B +#define TIOCGGETNRPORTS 0x546C +#define TIOCGGETPORTTYPE 0x546D +#define GETDEEPFIFO 0x54AA +#define SETDEEPFIFO 0x54AB +#define SETFCR 0x54BA +#define SETTTR 0x54B1 +#define SETRTR 0x54B2 +#define GETTTR 0x54B3 +#define GETRTR 0x54B4 + +/* multi-drop mode related ioctl commands */ +#define TIOCSMULTIDROP 0x5470 +#define TIOCSMDADDR 0x5471 +#define TIOCGMDADDR 0x5472 +#define TIOCSENDADDR 0x5473 + + +/* serial interface */ +#define RS232 1 +#define RS422PTP 2 +#define RS422MD 3 +#define RS485NE 4 +#define RS485ECHO 5 + +#define serial_inp(up, offset) serial_in(up, offset) +#define serial_outp(up, offset, value) serial_out(up, offset, value) + +#define PASS_LIMIT 256 +#define is_real_interrupt(irq) ((irq) != 0) + +#define PROBE_ANY (~0) + +static DEFINE_MUTEX(mp_mutex); +#define MP_MUTEX_LOCK(x) mutex_lock(&(x)) +#define MP_MUTEX_UNLOCK(x) mutex_unlock(&(x)) +#define MP_STATE_LOCK(x) mutex_lock(&((x)->mutex)) +#define MP_STATE_UNLOCK(x) mutex_unlock(&((x)->mutex)) + + +#define UART_LSR_SPECIAL 0x1E + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) +#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0)) + + +//#define MP_DEBUG 1 +#undef MP_DEBUG + +#ifdef MP_DEBUG +#define DPRINTK(x...) printk(x) +#else +#define DPRINTK(x...) do { } while (0) +#endif + +#ifdef MP_DEBUG +#define DEBUG_AUTOCONF(fmt...) printk(fmt) +#else +#define DEBUG_AUTOCONF(fmt...) do { } while (0) +#endif + +#ifdef MP_DEBUG +#define DEBUG_INTR(fmt...) printk(fmt) +#else +#define DEBUG_INTR(fmt...) do { } while (0) +#endif + +#if defined(__i386__) && defined(CONFIG_M486) +#define SERIAL_INLINE +#endif +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +#define TYPE_POLL 1 +#define TYPE_INTERRUPT 2 + + +struct mp_device_t { + unsigned short device_id; + unsigned char revision; + char *name; + unsigned long uart_access_addr; + unsigned long option_reg_addr; + unsigned long reserved_addr[4]; + int irq; + int nr_ports; + int poll_type; +}; + +typedef struct mppcibrd { + char *name; + unsigned short vendor_id; + unsigned short device_id; +} mppcibrd_t; + +static mppcibrd_t mp_pciboards[] = { + + { "Multi-1 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1} , + { "Multi-2 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2} , + { "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4} , + { "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4A} , + { "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6} , + { "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6A} , + { "Multi-8 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP8} , + { "Multi-32 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP32} , + + { "Multi-1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1P} , + { "Multi-2S1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2S1P} , + + { "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4} , + { "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4A} , + { "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6} , + { "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6A} , + { "Multi-8(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP8} , + { "Multi-32(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP32} , + + { "Multi-1 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1} , + { "Multi-2 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2} , + { "Multi-4 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4} , + { "Multi-8 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8} , + { "Multi-32 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP32} , + + { "Multi-1 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1E} , + { "Multi-2 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2E} , + { "Multi-2 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2B} , + { "Multi-4 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4B} , + { "Multi-8 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8B} , + + { "Multi-1(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP1} , + { "Multi-2(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP2} , + { "Multi-4(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP4} , + { "Multi-8(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP8} , + { "Multi-32(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP32} , + + { "Multi-4M PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4M} , +}; + +struct mp_port { + struct sb_uart_port port; + + struct timer_list timer; /* "no irq" timer */ + struct list_head list; /* ports on this IRQ */ + unsigned int capabilities; /* port capabilities */ + unsigned short rev; + unsigned char acr; + unsigned char ier; + unsigned char lcr; + unsigned char mcr; + unsigned char mcr_mask; /* mask of user bits */ + unsigned char mcr_force; /* mask of forced bits */ + unsigned char lsr_break_flag; + + void (*pm)(struct sb_uart_port *port, + unsigned int state, unsigned int old); + struct mp_device_t *device; + unsigned long interface_config_addr; + unsigned long option_base_addr; + unsigned char interface; + unsigned char poll_type; +}; + +struct irq_info { + spinlock_t lock; + struct list_head *head; +}; + +struct sb105x_uart_config { + char *name; + int dfl_xmit_fifo_size; + int flags; +}; + +static const struct sb105x_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "SB16C1050", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, + { "SB16C1050A", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, +}; + + + diff --git a/drivers/staging/sb105x/sb_ser_core.h b/drivers/staging/sb105x/sb_ser_core.h new file mode 100644 index 0000000000000..c8fb991732997 --- /dev/null +++ b/drivers/staging/sb105x/sb_ser_core.h @@ -0,0 +1,368 @@ +#include + +#define UART_CONFIG_TYPE (1 << 0) +#define UART_CONFIG_IRQ (1 << 1) +#define UPIO_PORT (0) +#define UPIO_HUB6 (1) +#define UPIO_MEM (2) +#define UPIO_MEM32 (3) +#define UPIO_AU (4) /* Au1x00 type IO */ +#define UPIO_TSI (5) /* Tsi108/109 type IO */ +#define UPF_FOURPORT (1 << 1) +#define UPF_SAK (1 << 2) +#define UPF_SPD_MASK (0x1030) +#define UPF_SPD_HI (0x0010) +#define UPF_SPD_VHI (0x0020) +#define UPF_SPD_CUST (0x0030) +#define UPF_SPD_SHI (0x1000) +#define UPF_SPD_WARP (0x1010) +#define UPF_SKIP_TEST (1 << 6) +#define UPF_AUTO_IRQ (1 << 7) +#define UPF_HARDPPS_CD (1 << 11) +#define UPF_LOW_LATENCY (1 << 13) +#define UPF_BUGGY_UART (1 << 14) +#define UPF_MAGIC_MULTIPLIER (1 << 16) +#define UPF_CONS_FLOW (1 << 23) +#define UPF_SHARE_IRQ (1 << 24) +#define UPF_BOOT_AUTOCONF (1 << 28) +#define UPF_DEAD (1 << 30) +#define UPF_IOREMAP (1 << 31) +#define UPF_CHANGE_MASK (0x17fff) +#define UPF_USR_MASK (UPF_SPD_MASK|UPF_LOW_LATENCY) +#define USF_CLOSING_WAIT_INF (0) +#define USF_CLOSING_WAIT_NONE (~0U) + +#define UART_XMIT_SIZE PAGE_SIZE + +#define UIF_CHECK_CD (1 << 25) +#define UIF_CTS_FLOW (1 << 26) +#define UIF_NORMAL_ACTIVE (1 << 29) +#define UIF_INITIALIZED (1 << 31) +#define UIF_SUSPENDED (1 << 30) + +#define WAKEUP_CHARS 256 + +#define uart_circ_empty(circ) ((circ)->head == (circ)->tail) +#define uart_circ_clear(circ) ((circ)->head = (circ)->tail = 0) + +#define uart_circ_chars_pending(circ) \ + (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE)) + +#define uart_circ_chars_free(circ) \ + (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) + +#define uart_tx_stopped(port) \ + ((port)->info->tty->stopped || (port)->info->tty->hw_stopped) + +#define UART_ENABLE_MS(port,cflag) ((port)->flags & UPF_HARDPPS_CD || \ + (cflag) & CRTSCTS || \ + !((cflag) & CLOCAL)) + + +struct sb_uart_port; +struct sb_uart_info; +struct serial_struct; +struct device; + +struct sb_uart_ops { + unsigned int (*tx_empty)(struct sb_uart_port *); + void (*set_mctrl)(struct sb_uart_port *, unsigned int mctrl); + unsigned int (*get_mctrl)(struct sb_uart_port *); + void (*stop_tx)(struct sb_uart_port *); + void (*start_tx)(struct sb_uart_port *); + void (*send_xchar)(struct sb_uart_port *, char ch); + void (*stop_rx)(struct sb_uart_port *); + void (*enable_ms)(struct sb_uart_port *); + void (*break_ctl)(struct sb_uart_port *, int ctl); + int (*startup)(struct sb_uart_port *); + void (*shutdown)(struct sb_uart_port *); + void (*set_termios)(struct sb_uart_port *, struct MP_TERMIOS *new, + struct MP_TERMIOS *old); + void (*pm)(struct sb_uart_port *, unsigned int state, + unsigned int oldstate); + int (*set_wake)(struct sb_uart_port *, unsigned int state); + + const char *(*type)(struct sb_uart_port *); + + void (*release_port)(struct sb_uart_port *); + + int (*request_port)(struct sb_uart_port *); + void (*config_port)(struct sb_uart_port *, int); + int (*verify_port)(struct sb_uart_port *, struct serial_struct *); + int (*ioctl)(struct sb_uart_port *, unsigned int, unsigned long); +}; + + +struct sb_uart_icount { + __u32 cts; + __u32 dsr; + __u32 rng; + __u32 dcd; + __u32 rx; + __u32 tx; + __u32 frame; + __u32 overrun; + __u32 parity; + __u32 brk; + __u32 buf_overrun; +}; +typedef unsigned int upf_t; + +struct sb_uart_port { + spinlock_t lock; /* port lock */ + unsigned int iobase; /* in/out[bwl] */ + unsigned char __iomem *membase; /* read/write[bwl] */ + unsigned int irq; /* irq number */ + unsigned int uartclk; /* base uart clock */ + unsigned int fifosize; /* tx fifo size */ + unsigned char x_char; /* xon/xoff char */ + unsigned char regshift; /* reg offset shift */ + unsigned char iotype; /* io access style */ + unsigned char unused1; + + + unsigned int read_status_mask; /* driver specific */ + unsigned int ignore_status_mask; /* driver specific */ + struct sb_uart_info *info; /* pointer to parent info */ + struct sb_uart_icount icount; /* statistics */ + + struct console *cons; /* struct console, if any */ +#ifdef CONFIG_SERIAL_CORE_CONSOLE + unsigned long sysrq; /* sysrq timeout */ +#endif + + upf_t flags; + + unsigned int mctrl; /* current modem ctrl settings */ + unsigned int timeout; /* character-based timeout */ + unsigned int type; /* port type */ + const struct sb_uart_ops *ops; + unsigned int custom_divisor; + unsigned int line; /* port index */ + unsigned long mapbase; /* for ioremap */ + struct device *dev; /* parent device */ + unsigned char hub6; /* this should be in the 8250 driver */ + unsigned char unused[3]; +}; + +#define mdmode unused[2] +#define MDMODE_ADDR 0x1 +#define MDMODE_ENABLE 0x2 +#define MDMODE_AUTO 0x4 +#define MDMODE_ADDRSEND 0x8 + +struct sb_uart_state { + unsigned int close_delay; /* msec */ + unsigned int closing_wait; /* msec */ + + + int count; + int pm_state; + struct sb_uart_info *info; + struct sb_uart_port *port; + + struct mutex mutex; +}; + +typedef unsigned int uif_t; + +struct sb_uart_info { + struct tty_struct *tty; + struct circ_buf xmit; + uif_t flags; + + int blocked_open; + + struct tasklet_struct tlet; + + wait_queue_head_t open_wait; + wait_queue_head_t delta_msr_wait; +}; + + +struct module; +struct tty_driver; + +struct uart_driver { + struct module *owner; + const char *driver_name; + const char *dev_name; + int major; + int minor; + int nr; + struct console *cons; + + struct sb_uart_state *state; + struct tty_driver *tty_driver; +}; + +void sb_uart_write_wakeup(struct sb_uart_port *port) +{ + struct sb_uart_info *info = port->info; + tasklet_schedule(&info->tlet); +} + +void sb_uart_update_timeout(struct sb_uart_port *port, unsigned int cflag, + unsigned int baud) +{ + unsigned int bits; + + switch (cflag & CSIZE) + { + case CS5: + bits = 7; + break; + + case CS6: + bits = 8; + break; + + case CS7: + bits = 9; + break; + + default: + bits = 10; + break; + } + + if (cflag & CSTOPB) + { + bits++; + } + + if (cflag & PARENB) + { + bits++; + } + + bits = bits * port->fifosize; + + port->timeout = (HZ * bits) / baud + HZ/50; +} +unsigned int sb_uart_get_baud_rate(struct sb_uart_port *port, struct MP_TERMIOS *termios, + struct MP_TERMIOS *old, unsigned int min, + unsigned int max) +{ + unsigned int try, baud, altbaud = 38400; + upf_t flags = port->flags & UPF_SPD_MASK; + + if (flags == UPF_SPD_HI) + altbaud = 57600; + if (flags == UPF_SPD_VHI) + altbaud = 115200; + if (flags == UPF_SPD_SHI) + altbaud = 230400; + if (flags == UPF_SPD_WARP) + altbaud = 460800; + + for (try = 0; try < 2; try++) { + + switch (termios->c_cflag & (CBAUD | CBAUDEX)) + { + case B921600 : baud = 921600; break; + case B460800 : baud = 460800; break; + case B230400 : baud = 230400; break; + case B115200 : baud = 115200; break; + case B57600 : baud = 57600; break; + case B38400 : baud = 38400; break; + case B19200 : baud = 19200; break; + case B9600 : baud = 9600; break; + case B4800 : baud = 4800; break; + case B2400 : baud = 2400; break; + case B1800 : baud = 1800; break; + case B1200 : baud = 1200; break; + case B600 : baud = 600; break; + case B300 : baud = 300; break; + case B200 : baud = 200; break; + case B150 : baud = 150; break; + case B134 : baud = 134; break; + case B110 : baud = 110; break; + case B75 : baud = 75; break; + case B50 : baud = 50; break; + default : baud = 9600; break; + } + + if (baud == 38400) + baud = altbaud; + + if (baud == 0) + baud = 9600; + + if (baud >= min && baud <= max) + return baud; + + termios->c_cflag &= ~CBAUD; + if (old) { + termios->c_cflag |= old->c_cflag & CBAUD; + old = NULL; + continue; + } + + termios->c_cflag |= B9600; + } + + return 0; +} +unsigned int sb_uart_get_divisor(struct sb_uart_port *port, unsigned int baud) +{ + unsigned int quot; + + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) + quot = port->custom_divisor; + else + quot = (port->uartclk + (8 * baud)) / (16 * baud); + + return quot; +} + + + +static inline int sb_uart_handle_break(struct sb_uart_port *port) +{ + struct sb_uart_info *info = port->info; + + if (port->flags & UPF_SAK) + do_SAK(info->tty); + return 0; +} + +static inline void sb_uart_handle_dcd_change(struct sb_uart_port *port, unsigned int status) +{ + struct sb_uart_info *info = port->info; + + port->icount.dcd++; + + if (info->flags & UIF_CHECK_CD) { + if (status) + wake_up_interruptible(&info->open_wait); + else if (info->tty) + tty_hangup(info->tty); + } +} + +static inline void sb_uart_handle_cts_change(struct sb_uart_port *port, unsigned int status) +{ + struct sb_uart_info *info = port->info; + struct tty_struct *tty = info->tty; + + port->icount.cts++; + + if (info->flags & UIF_CTS_FLOW) { + if (tty->hw_stopped) { + if (status) { + tty->hw_stopped = 0; + port->ops->start_tx(port); + sb_uart_write_wakeup(port); + } + } else { + if (!status) { + tty->hw_stopped = 1; + port->ops->stop_tx(port); + } + } + } +} + + + From 09489ed87715c70e142264b43ac97fbf2666ce42 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 082/130] AOSCOS: Staging: sb105x: Fix build and add MIPS support [Mingcong Bai: Resolved merge conflicts in arch/mips/include/asm/serial.h and drivers/staging/sb105x/Kconfig.] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/include/asm/serial.h | 19 +++++++++++ drivers/staging/sb105x/Kconfig | 2 +- drivers/staging/sb105x/sb_pci_mp.c | 54 ++++++++++-------------------- drivers/staging/sb105x/sb_pci_mp.h | 3 +- 4 files changed, 38 insertions(+), 40 deletions(-) create mode 100644 arch/mips/include/asm/serial.h diff --git a/arch/mips/include/asm/serial.h b/arch/mips/include/asm/serial.h new file mode 100644 index 0000000000000..a780ee51a7188 --- /dev/null +++ b/arch/mips/include/asm/serial.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2017 MIPS Tech, LLC + */ +#ifndef __ASM__SERIAL_H +#define __ASM__SERIAL_H + +#ifdef CONFIG_MIPS_GENERIC +/* + * Generic kernels cannot know a correct value for all platforms at + * compile time. Set it to 0 to prevent 8250_early using it + */ +#define BASE_BAUD 0 +#else +#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) +#include +#endif + +#endif /* __ASM__SERIAL_H */ diff --git a/drivers/staging/sb105x/Kconfig b/drivers/staging/sb105x/Kconfig index 245e7847a3542..c5f741e71c14b 100644 --- a/drivers/staging/sb105x/Kconfig +++ b/drivers/staging/sb105x/Kconfig @@ -1,7 +1,7 @@ config SB105X tristate "SystemBase PCI Multiport UART" select SERIAL_CORE - depends on PCI && X86 && TTY && BROKEN + depends on PCI && (X86 || MIPS) && TTY help A driver for the SystemBase Multi-2/PCI serial card diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index c9d6ee3903adc..18e91bc61ba16 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -1,6 +1,7 @@ #include "sb_pci_mp.h" #include #include +#include extern struct parport *parport_pc_probe_port(unsigned long base_lo, unsigned long base_hi, @@ -87,7 +88,7 @@ static irqreturn_t multi_interrupt(int irq, void *dev_id); static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt); static int serial_link_irq_chain(struct mp_port *mtpt); static void serial_unlink_irq_chain(struct mp_port *mtpt); -static void multi_timeout(unsigned long data); +static void multi_timeout(struct timer_list *t); static unsigned int multi_tx_empty(struct sb_uart_port *port); static unsigned int multi_get_mctrl(struct sb_uart_port *port); static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl); @@ -315,7 +316,6 @@ static int set_multidrop_mode(struct sb_uart_port *port, unsigned int mode) sb1054_set_register(port, PAGE_1, SB105XA_MDR, mdr); port->mdmode &= ~0x6; port->mdmode |= mode; - printk("[%d] multidrop init: %x\n", port->line, port->mdmode); return 0; } @@ -494,7 +494,6 @@ static void mp_tasklet_action(unsigned long data) struct sb_uart_state *state = (struct sb_uart_state *)data; struct tty_struct *tty; - printk("tasklet is called!\n"); tty = state->info->tty; tty_wakeup(tty); } @@ -897,9 +896,6 @@ static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinf state->close_delay = new_serial.close_delay; state->closing_wait = closing_wait; port->fifosize = new_serial.xmit_fifo_size; - if (state->info->tty) - state->info->tty->low_latency = - (port->flags & UPF_LOW_LATENCY) ? 1 : 0; check_and_exit: retval = 0; @@ -1315,26 +1311,21 @@ static void mp_close(struct tty_struct *tty, struct file *filp) struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port; - printk("mp_close!\n"); if (!state || !state->port) return; port = state->port; - printk("close1 %d\n", __LINE__); MP_STATE_LOCK(state); - printk("close2 %d\n", __LINE__); if (tty_hung_up_p(filp)) goto done; - printk("close3 %d\n", __LINE__); if ((tty->count == 1) && (state->count != 1)) { printk("mp_close: bad serial port count; tty->count is 1, " "state->count is %d\n", state->count); state->count = 1; } - printk("close4 %d\n", __LINE__); if (--state->count < 0) { printk("rs_close: bad serial port count for ttyMP%d: %d\n", port->line, state->count); @@ -1345,11 +1336,9 @@ static void mp_close(struct tty_struct *tty, struct file *filp) tty->closing = 1; - printk("close5 %d\n", __LINE__); if (state->closing_wait != USF_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, state->closing_wait); - printk("close6 %d\n", __LINE__); if (state->info->flags & UIF_INITIALIZED) { unsigned long flags; spin_lock_irqsave(&port->lock, flags); @@ -1357,10 +1346,8 @@ static void mp_close(struct tty_struct *tty, struct file *filp) spin_unlock_irqrestore(&port->lock, flags); mp_wait_until_sent(tty, port->timeout); } - printk("close7 %d\n", __LINE__); mp_shutdown(state); - printk("close8 %d\n", __LINE__); mp_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; @@ -1377,13 +1364,11 @@ static void mp_close(struct tty_struct *tty, struct file *filp) { mp_change_pm(state, 3); } - printk("close8 %d\n", __LINE__); state->info->flags &= ~UIF_NORMAL_ACTIVE; wake_up_interruptible(&state->info->open_wait); done: - printk("close done\n"); MP_STATE_UNLOCK(state); module_put(THIS_MODULE); } @@ -1571,8 +1556,6 @@ static int mp_open(struct tty_struct *tty, struct file *filp) mtpt = (struct mp_port *)state->port; tty->driver_data = state; - tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; - tty->alt_speed = 0; state->info->tty = tty; if (tty_hung_up_p(filp)) { @@ -1705,7 +1688,7 @@ static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *s MP_STATE_UNLOCK(state); } -static struct tty_operations mp_ops = { +static struct tty_operations sb_mp_ops = { .open = mp_open, .close = mp_close, .write = mp_write, @@ -1725,7 +1708,7 @@ static struct tty_operations mp_ops = { .break_ctl = mp_break_ctl, .wait_until_sent= mp_wait_until_sent, #ifdef CONFIG_PROC_FS - .proc_fops = NULL, + .proc_show = NULL, #endif .tiocmget = mp_tiocmget, .tiocmset = mp_tiocmset, @@ -1770,7 +1753,7 @@ static int mp_register_driver(struct uart_driver *drv) normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; normal->driver_state = drv; - tty_set_operations(normal, &mp_ops); + tty_set_operations(normal, &sb_mp_ops); for (i = 0; i < drv->nr; i++) { struct sb_uart_state *state = drv->state + i; @@ -1837,6 +1820,7 @@ static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port) mp_configure_port(drv, state, port); + tty_port_link_device(&tt_port[port->line], drv->tty_driver, port->line); tty_register_device(drv->tty_driver, port->line, port->dev); out: @@ -2070,7 +2054,6 @@ static void multi_enable_ms(struct sb_uart_port *port) static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ) { - struct tty_struct *tty = mtpt->port.info->tty; unsigned char lsr = *status; int max_count = 256; unsigned char ch; @@ -2112,18 +2095,18 @@ static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status ) mtpt->port.icount.overrun++; flag = TTY_OVERRUN; } - tty_insert_flip_char(tty, ch, flag); + tty_insert_flip_char(&tt_port[mtpt->port.line], ch, flag); } else { ch = serial_inp(mtpt, UART_RX); - tty_insert_flip_char(tty, ch, 0); + tty_insert_flip_char(&tt_port[mtpt->port.line], ch, 0); } ignore_char: lsr = serial_inp(mtpt, UART_LSR); } while ((lsr & UART_LSR_DR) && (max_count-- > 0)); - tty_flip_buffer_push(tty); + tty_flip_buffer_push(&tt_port[mtpt->port.line]); } @@ -2152,7 +2135,6 @@ static _INLINE_ void transmit_chars(struct mp_port *mtpt) count = mtpt->port.fifosize; } - printk("[%d] mdmode: %x\n", mtpt->port.line, mtpt->port.mdmode); do { #if 0 /* check multi-drop mode */ @@ -2245,10 +2227,8 @@ static irqreturn_t multi_interrupt(int irq, void *dev_id) mtpt = list_entry(lhead, struct mp_port, list); iir = serial_in(mtpt, UART_IIR); - printk("interrupt! port %d, iir 0x%x\n", mtpt->port.line, iir); //wlee if (!(iir & UART_IIR_NO_INT)) { - printk("interrupt handle\n"); spin_lock(&mtpt->port.lock); multi_handle_port(mtpt); spin_unlock(&mtpt->port.lock); @@ -2328,10 +2308,9 @@ static void serial_unlink_irq_chain(struct mp_port *mtpt) serial_do_unlink(i, mtpt); } -static void multi_timeout(unsigned long data) +static void multi_timeout(struct timer_list *t) { - struct mp_port *mtpt = (struct mp_port *)data; - + struct mp_port *mtpt = from_timer(mtpt, t, timer); spin_lock(&mtpt->port.lock); multi_handle_port(mtpt); @@ -2448,7 +2427,6 @@ static int multi_startup(struct sb_uart_port *port) timeout = timeout > 6 ? (timeout / 2 - 2) : 1; - mtpt->timer.data = (unsigned long)mtpt; mod_timer(&mtpt->timer, jiffies + timeout); } else @@ -2845,7 +2823,6 @@ static void __init multi_init_ports(void) else { b_ret = read_option_register(mtpt,(MP_OPTR_IIR0 + i/8)); - printk("IIR_RET = %x\n",b_ret); } /* default to RS232 */ @@ -2869,8 +2846,8 @@ static void __init multi_register_ports(struct uart_driver *drv) mtpt->port.line = i; mtpt->port.ops = &multi_pops; - init_timer(&mtpt->timer); - mtpt->timer.function = multi_timeout; + timer_setup(&mtpt->timer, multi_timeout, 0); + tty_port_init(&tt_port[i]); mp_add_one_port(drv, &mtpt->port); } } @@ -3146,7 +3123,10 @@ printk("FOUND~~~\n"); // if (mp_pciboards[i].device_id & 0x0800) { int status; - pci_disable_device(dev); + + if (pci_is_enabled(dev)) + pci_disable_device(dev); + status = pci_enable_device(dev); if (status != 0) diff --git a/drivers/staging/sb105x/sb_pci_mp.h b/drivers/staging/sb105x/sb_pci_mp.h index 80ae4ab046031..0920beb31c15e 100644 --- a/drivers/staging/sb105x/sb_pci_mp.h +++ b/drivers/staging/sb105x/sb_pci_mp.h @@ -287,5 +287,4 @@ static const struct sb105x_uart_config uart_config[] = { { "SB16C1050A", 128, UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH }, }; - - +static struct tty_port tt_port[MAX_MP_PORT]; From 2066da1aad837f9aa163d36e454b5926b3d710e7 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 13:49:12 +0800 Subject: [PATCH 083/130] AOSCOS: staging: sb105x: add missing function prototypes A few functions in drivers/staging/sb105x/sb_ser_core.h were missing function prototype declarations. Mark them as static to suppress a couple of `-Werror=missing-prototype' errors. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_ser_core.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/sb105x/sb_ser_core.h b/drivers/staging/sb105x/sb_ser_core.h index c8fb991732997..8835415e1d3df 100644 --- a/drivers/staging/sb105x/sb_ser_core.h +++ b/drivers/staging/sb105x/sb_ser_core.h @@ -196,13 +196,13 @@ struct uart_driver { struct tty_driver *tty_driver; }; -void sb_uart_write_wakeup(struct sb_uart_port *port) +static void sb_uart_write_wakeup(struct sb_uart_port *port) { struct sb_uart_info *info = port->info; tasklet_schedule(&info->tlet); } -void sb_uart_update_timeout(struct sb_uart_port *port, unsigned int cflag, +static void sb_uart_update_timeout(struct sb_uart_port *port, unsigned int cflag, unsigned int baud) { unsigned int bits; @@ -240,7 +240,7 @@ void sb_uart_update_timeout(struct sb_uart_port *port, unsigned int cflag, port->timeout = (HZ * bits) / baud + HZ/50; } -unsigned int sb_uart_get_baud_rate(struct sb_uart_port *port, struct MP_TERMIOS *termios, +static unsigned int sb_uart_get_baud_rate(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old, unsigned int min, unsigned int max) { @@ -304,7 +304,7 @@ unsigned int sb_uart_get_baud_rate(struct sb_uart_port *port, struct MP_TERMIOS return 0; } -unsigned int sb_uart_get_divisor(struct sb_uart_port *port, unsigned int baud) +static unsigned int sb_uart_get_divisor(struct sb_uart_port *port, unsigned int baud) { unsigned int quot; From 2ea407a78f2f24ff869ff2d28d175902e3574444 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 14:44:52 +0800 Subject: [PATCH 084/130] AOSCOS: staging: sb105x: adapt to tty_struct changes Per commit 6e94dbc7a4e4 ("tty: cumulate and document tty_struct::flow* members"), struct members `stopped' and `tco_stopped' were grouped under `struct flow.'. Revise driver code accordingly. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 4 ++-- drivers/staging/sb105x/sb_ser_core.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index 18e91bc61ba16..e333245ec9c5d 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -480,7 +480,7 @@ static void __mp_start(struct tty_struct *tty) struct sb_uart_port *port = state->port; if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && - !tty->stopped && !tty->hw_stopped) + !tty->flow.stopped && !tty->hw_stopped) port->ops->start_tx(port); } @@ -929,7 +929,7 @@ static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value) if (port->x_char || ((uart_circ_chars_pending(&state->info->xmit) > 0) && - !state->info->tty->stopped && !state->info->tty->hw_stopped)) + !state->info->tty->flow.stopped && !state->info->tty->hw_stopped)) result &= ~TIOCSER_TEMT; return put_user(result, value); diff --git a/drivers/staging/sb105x/sb_ser_core.h b/drivers/staging/sb105x/sb_ser_core.h index 8835415e1d3df..d8664e723b4a3 100644 --- a/drivers/staging/sb105x/sb_ser_core.h +++ b/drivers/staging/sb105x/sb_ser_core.h @@ -52,7 +52,7 @@ (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE)) #define uart_tx_stopped(port) \ - ((port)->info->tty->stopped || (port)->info->tty->hw_stopped) + ((port)->info->tty->flow.stopped || (port)->info->tty->hw_stopped) #define UART_ENABLE_MS(port,cflag) ((port)->flags & UPF_HARDPPS_CD || \ (cflag) & CRTSCTS || \ From 375690e2b8caba5b78afc3e508db6478d229e057 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 14:52:57 +0800 Subject: [PATCH 085/130] AOSCOS: staging: sb105x: rename state to __state in task_struct Per commit 2f064a59a11f ("sched: Change task_struct::state"), the member `state' of `struct task_struct' was renamed into `__state'. Revise driver code accordingly. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index e333245ec9c5d..963f3091a5c95 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -1051,7 +1051,7 @@ static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg) cprev = cnow; } - current->state = TASK_RUNNING; + current->__state = TASK_RUNNING; remove_wait_queue(&state->info->delta_msr_wait, &wait); return ret; From a1007d7c563d269aecc64131a8264a3c7ba14d7f Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 14:58:57 +0800 Subject: [PATCH 086/130] AOSCOS: staging: sb105x: replace deprecated strlcpy() with strscpy() The string function strlcpy() was removed in commit d26270061ae6 ("string: Remove strlcpy()") and per deprecated.rst: strlcpy() reads the entire source buffer first (since the return value is meant to match that of strlen()). This read may exceed the destination size limit. This is both inefficient and can lead to linear read overflows if a source string is not NUL-terminated. The safe replacement is strscpy(), though care must be given to any cases where the return value of strlcpy() is used, since strscpy() will return negative errno values when it truncates. Replace calls to strlcpy() with strscpy(). Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index 963f3091a5c95..cd98d3b0183da 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -1624,7 +1624,7 @@ static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port * break; default: snprintf(address, sizeof(address),"*unknown*" ); - strlcpy(address, "*unknown*", sizeof(address)); + strscpy(address, "*unknown*", sizeof(address)); break; } From 0e0ac3a16f295560f787e6f4ffd471a422a906a0 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 15:21:26 +0800 Subject: [PATCH 087/130] AOSCOS: staging: sb105x: replace alloc_tty_driver() with tty_alloc_driver() Per commit 39b7b42be4a8 ("tty: stop using alloc_tty_driver"), all instances of `alloc_tty_driver()' were revised into `tty_alloc_driver()' with an extra `flags' argument (merged from the `flags' member of `struct tty_driver'. The `tty_alloc_driver()' function was then removed in commit 56ec5880a28e ("tty: drop alloc_tty_driver"). Follow this chain of changes and revise the driver code. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index cd98d3b0183da..ccf02cfeb2fcc 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -1728,12 +1728,10 @@ static int mp_register_driver(struct uart_driver *drv) } memset(drv->state, 0, sizeof(struct sb_uart_state) * drv->nr); - normal = alloc_tty_driver(drv->nr); - if (!normal) - { - printk("SB PCI Error: tty allocation error!\n"); - goto out; - } + normal = tty_alloc_driver(drv->nr, + TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV); + if (IS_ERR(normal)) + return PTR_ERR(normal); drv->tty_driver = normal; @@ -1750,7 +1748,6 @@ static int mp_register_driver(struct uart_driver *drv) normal->subtype = SERIAL_TYPE_NORMAL; normal->init_termios = tty_std_termios; normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; normal->driver_state = drv; tty_set_operations(normal, &sb_mp_ops); From db2bb64aaa9189da5673ea8b2a89bc3ed5a23c54 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 15:37:23 +0800 Subject: [PATCH 088/130] AOSCOS: staging: sb105x: replace put_tty_driver() with tty_driver_kref_put() Per commit 9f90a4ddef4e ("tty: drop put_tty_driver"), "put_tty_driver() is an alias for tty_driver_kref_put()." Rename all instances of `put_tty_driver()' to `tty_driver_kref_put()'. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index ccf02cfeb2fcc..c5e1c39e7a7a2 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -1765,7 +1765,7 @@ for (i = 0; i < drv->nr; i++) { out: if (retval < 0) { printk("Register tty driver Fail!\n"); - put_tty_driver(normal); + tty_driver_kref_put(normal); kfree(drv->state); } @@ -1784,7 +1784,7 @@ void mp_unregister_driver(struct uart_driver *drv) } tty_unregister_driver(normal); - put_tty_driver(normal); + tty_driver_kref_put(normal); drv->tty_driver = NULL; From 9ac083f46dac64c75ba9a4ecaa4c3b21bc37823c Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 15:47:49 +0800 Subject: [PATCH 089/130] AOSCOS: MIPS: serial: drop STD_FLAGS Per commit b1d984cf7d6b ("crisv10: use flags from tty_port"), the flags defined in STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) were not tested in other code and therefore needlessly initialised. Drop them to prevent build errors. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/include/asm/serial.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/mips/include/asm/serial.h b/arch/mips/include/asm/serial.h index a780ee51a7188..2777148dbfc5a 100644 --- a/arch/mips/include/asm/serial.h +++ b/arch/mips/include/asm/serial.h @@ -12,7 +12,6 @@ */ #define BASE_BAUD 0 #else -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) #include #endif From a750f5fe4d07c9e1989daf4b4a97ee67e2e9bdd8 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 16:02:01 +0800 Subject: [PATCH 090/130] AOSCOS: staging: sb105x: drop upstream-removed STD_COM_FLAGS Per commit 5691e03593cb ("tty: frv, remove unused serial macros"), the `STD_COM_FLAGS' macro was not used anywhere and was then removed. Follow this commit and remove the aforementioned macro. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index c5e1c39e7a7a2..32f60de269ccb 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -2808,7 +2808,7 @@ static void __init multi_init_ports(void) osc = 0; for(j=0;jport.uartclk *= 2; - mtpt->port.flags |= STD_COM_FLAGS | UPF_SHARE_IRQ ; + mtpt->port.flags |= UPF_SHARE_IRQ ; mtpt->port.iotype = UPIO_PORT; mtpt->port.ops = &multi_pops; From 75986f8d2af0849b9bc833d25767cdbd208e9f83 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 16:19:05 +0800 Subject: [PATCH 091/130] AOSCOS: staging: sb105x: drop TTY_DRIVER_MAGIC assignment Per commit 5052df99d3bc ("tty: remove TTY_DRIVER_MAGIC"), the magic number "shoudl not need" to be defined. Let's see how it goes. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index 32f60de269ccb..841e9b15486b0 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -1736,7 +1736,6 @@ static int mp_register_driver(struct uart_driver *drv) drv->tty_driver = normal; normal->owner = drv->owner; - normal->magic = TTY_DRIVER_MAGIC; normal->driver_name = drv->driver_name; normal->name = drv->dev_name; normal->major = drv->major; From 1eff80a226b72de49e1aadf681fd957b70fa1049 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 16:58:16 +0800 Subject: [PATCH 092/130] AOSCOS: staging: sb105x: convert old ktermios to a const Per commit a8c11c152034 ("tty: Make ->set_termios() old ktermios const"), the "old ktermios" would get discarded and there is therefore no point to modify its value assignment. Convert all relevant functions prototypes to adapt to this change. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 12 ++++++------ drivers/staging/sb105x/sb_ser_core.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index 841e9b15486b0..0d7e216fad94a 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -33,7 +33,7 @@ static void mp_tasklet_action(unsigned long data); static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear); static int mp_startup(struct sb_uart_state *state, int init_hw); static void mp_shutdown(struct sb_uart_state *state); -static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios); +static void mp_change_speed(struct sb_uart_state *state, const struct MP_TERMIOS *old_termios); static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c); static int mp_put_char(struct tty_struct *tty, unsigned char ch); @@ -57,7 +57,7 @@ static int mp_do_autoconfig(struct sb_uart_state *state); static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg); static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt); static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg); -static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios); +static void mp_set_termios(struct tty_struct *tty, const struct MP_TERMIOS *old_termios); static void mp_close(struct tty_struct *tty, struct file *filp); static void mp_wait_until_sent(struct tty_struct *tty, int timeout); static void mp_hangup(struct tty_struct *tty); @@ -96,7 +96,7 @@ static void multi_break_ctl(struct sb_uart_port *port, int break_state); static int multi_startup(struct sb_uart_port *port); static void multi_shutdown(struct sb_uart_port *port); static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud); -static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old); +static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, const struct MP_TERMIOS *old); static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate); static void multi_release_std_resource(struct mp_port *mtpt); static void multi_release_port(struct sb_uart_port *port); @@ -586,7 +586,7 @@ static void mp_shutdown(struct sb_uart_state *state) } } -static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios) +static void mp_change_speed(struct sb_uart_state *state, const struct MP_TERMIOS *old_termios) { struct tty_struct *tty = state->info->tty; struct sb_uart_port *port = state->port; @@ -1264,7 +1264,7 @@ static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) return ret; } -static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios) +static void mp_set_termios(struct tty_struct *tty, const struct MP_TERMIOS *old_termios) { struct sb_uart_state *state = tty->driver_data; unsigned long flags; @@ -2510,7 +2510,7 @@ static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int ba -static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old) +static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, const struct MP_TERMIOS *old) { struct mp_port *mtpt = (struct mp_port *)port; unsigned char cval, fcr = 0; diff --git a/drivers/staging/sb105x/sb_ser_core.h b/drivers/staging/sb105x/sb_ser_core.h index d8664e723b4a3..978b4b26ddbb1 100644 --- a/drivers/staging/sb105x/sb_ser_core.h +++ b/drivers/staging/sb105x/sb_ser_core.h @@ -77,7 +77,7 @@ struct sb_uart_ops { int (*startup)(struct sb_uart_port *); void (*shutdown)(struct sb_uart_port *); void (*set_termios)(struct sb_uart_port *, struct MP_TERMIOS *new, - struct MP_TERMIOS *old); + const struct MP_TERMIOS *old); void (*pm)(struct sb_uart_port *, unsigned int state, unsigned int oldstate); int (*set_wake)(struct sb_uart_port *, unsigned int state); @@ -241,7 +241,7 @@ static void sb_uart_update_timeout(struct sb_uart_port *port, unsigned int cflag port->timeout = (HZ * bits) / baud + HZ/50; } static unsigned int sb_uart_get_baud_rate(struct sb_uart_port *port, struct MP_TERMIOS *termios, - struct MP_TERMIOS *old, unsigned int min, + const struct MP_TERMIOS *old, unsigned int min, unsigned int max) { unsigned int try, baud, altbaud = 38400; From c251a8d9d9e2cd2b994ce6ee5e21411e04869ef8 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 17:16:44 +0800 Subject: [PATCH 093/130] AOSCOS: staging: sb105x: revise type of mp_write() as ssize_t Per commit 95713967ba52 ("tty: make tty_operations::write()'s count size_t"), member function `write()' in `struct tty_operations' was revised from type `int' to type `ssize_t'. Revise driver code accordingly. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index 0d7e216fad94a..f61451fabdcd0 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -39,7 +39,7 @@ static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ static int mp_put_char(struct tty_struct *tty, unsigned char ch); static void mp_put_chars(struct tty_struct *tty); -static int mp_write(struct tty_struct *tty, const unsigned char *buf, int count); +static ssize_t mp_write(struct tty_struct *tty, const unsigned char *buf, long unsigned int count); static int mp_write_room(struct tty_struct *tty); static int mp_chars_in_buffer(struct tty_struct *tty); static void mp_flush_buffer(struct tty_struct *tty); @@ -637,7 +637,7 @@ static void mp_put_chars(struct tty_struct *tty) mp_start(tty); } -static int mp_write(struct tty_struct *tty, const unsigned char *buf, int count) +static ssize_t mp_write(struct tty_struct *tty, const unsigned char *buf, long unsigned int count) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port; From 6ac1514427fc6aca9b232b13e002f6330b84b033 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 17:20:33 +0800 Subject: [PATCH 094/130] AOSCOS: staging: sb105x: revise type of mp_write_room() as unsigned int Per commit 03b3b1a2405c ("tty: make tty_operations::write_room return uint"), member function `write_room()' in `struct tty_operations' was revised from type `int' to type `unsigned int'. Revise driver code accordingly. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index f61451fabdcd0..593835f0ad11c 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -40,7 +40,7 @@ static int mp_put_char(struct tty_struct *tty, unsigned char ch); static void mp_put_chars(struct tty_struct *tty); static ssize_t mp_write(struct tty_struct *tty, const unsigned char *buf, long unsigned int count); -static int mp_write_room(struct tty_struct *tty); +static unsigned int mp_write_room(struct tty_struct *tty); static int mp_chars_in_buffer(struct tty_struct *tty); static void mp_flush_buffer(struct tty_struct *tty); static void mp_send_xchar(struct tty_struct *tty, char ch); @@ -671,7 +671,7 @@ static ssize_t mp_write(struct tty_struct *tty, const unsigned char *buf, long u return ret; } -static int mp_write_room(struct tty_struct *tty) +static unsigned int mp_write_room(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; From 75eeb831a77e80968ed3f20a4b7e4a964fe63783 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 17:22:27 +0800 Subject: [PATCH 095/130] AOSCOS: staging: sb105x: revise type of mp_chars_in_buffer() as unsigned int Per commit fff4ef17a940 ("tty: make tty_operations::chars_in_buffer return uint"), member function `chars_in_buffer()' in `struct tty_operations' was revised from type `int' to type `unsigned int'. Revise driver code accordingly. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index 593835f0ad11c..8712e74230648 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -41,7 +41,7 @@ static int mp_put_char(struct tty_struct *tty, unsigned char ch); static void mp_put_chars(struct tty_struct *tty); static ssize_t mp_write(struct tty_struct *tty, const unsigned char *buf, long unsigned int count); static unsigned int mp_write_room(struct tty_struct *tty); -static int mp_chars_in_buffer(struct tty_struct *tty); +static unsigned int mp_chars_in_buffer(struct tty_struct *tty); static void mp_flush_buffer(struct tty_struct *tty); static void mp_send_xchar(struct tty_struct *tty, char ch); static void mp_throttle(struct tty_struct *tty); @@ -678,7 +678,7 @@ static unsigned int mp_write_room(struct tty_struct *tty) return uart_circ_chars_free(&state->info->xmit); } -static int mp_chars_in_buffer(struct tty_struct *tty) +static unsigned int mp_chars_in_buffer(struct tty_struct *tty) { struct sb_uart_state *state = tty->driver_data; From a57477aa3e1aea4623335ccf05467dddb158349e Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 17:25:55 +0800 Subject: [PATCH 096/130] AOSCOS: staging: sb105x: revise type of second argument of mp_send_xchar() as u8 Per commit 3a00da027946 ("tty: make tty_operations::send_xchar accept u8 char"), the type of the second argument of member function `mp_send_xchar()' in `struct tty_operations' was revised from type `char' to type `u8'. Revise driver code accordingly. Fixes: "AOSCOS: Revert "staging: sb105x: delete the driver"" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/staging/sb105x/sb_pci_mp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c index 8712e74230648..19098bbfa113e 100644 --- a/drivers/staging/sb105x/sb_pci_mp.c +++ b/drivers/staging/sb105x/sb_pci_mp.c @@ -43,7 +43,7 @@ static ssize_t mp_write(struct tty_struct *tty, const unsigned char *buf, long u static unsigned int mp_write_room(struct tty_struct *tty); static unsigned int mp_chars_in_buffer(struct tty_struct *tty); static void mp_flush_buffer(struct tty_struct *tty); -static void mp_send_xchar(struct tty_struct *tty, char ch); +static void mp_send_xchar(struct tty_struct *tty, u8 ch); static void mp_throttle(struct tty_struct *tty); static void mp_unthrottle(struct tty_struct *tty); static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo); @@ -703,7 +703,7 @@ static void mp_flush_buffer(struct tty_struct *tty) tty_wakeup(tty); } -static void mp_send_xchar(struct tty_struct *tty, char ch) +static void mp_send_xchar(struct tty_struct *tty, u8 ch) { struct sb_uart_state *state = tty->driver_data; struct sb_uart_port *port = state->port; From 794015adfb582d10f1d9f9ca81f58b6d6c55c6e5 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sun, 28 May 2017 09:29:33 +0800 Subject: [PATCH 097/130] AOSCOS: 8250_pci: Add a new PLX9050 serial port card support [Mingcong Bai: Resolved merge conflicts in drivers/tty/serial/8250/8250_pci.c, applied parts of the patch to drivers/tty/serial/8250/8250_pcilib.c per commit 0348386dab37 ("serial: 8250_pci: Add serial8250_pci_setup_port definition in 8250_pcilib.c").] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/tty/serial/8250/8250_pci.c | 19 +++++++++++++++++++ drivers/tty/serial/8250/8250_pcilib.c | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index bbd7914ddc9ad..e648ce7d8eaa5 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -258,6 +258,12 @@ static int pci_plx9050_init(struct pci_dev *dev) * deep FIFOs */ irq_config = 0x5b; + + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_9050) && + (dev->subsystem_vendor == PCI_VENDOR_ID_PLX) && + (dev->subsystem_device == PCI_DEVICE_ID_PLX_9050)) + irq_config = 0x53; /* * enable/disable interrupts */ @@ -2246,6 +2252,15 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .setup = pci_default_setup, .exit = pci_plx9050_exit, }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9050, + .subvendor = PCI_VENDOR_ID_PLX, + .subdevice = PCI_DEVICE_ID_PLX_9050, + .init = pci_plx9050_init, + .setup = pci_default_setup, + .exit = pci_plx9050_exit, + }, { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050, @@ -4349,6 +4364,10 @@ static const struct pci_device_id serial_pci_tbl[] = { PCI_SUBDEVICE_ID_UNKNOWN_0x1584, 0, 0, pbn_b2_4_115200 }, /* Unknown card - subdevice 0x1588 */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_PLX, + PCI_DEVICE_ID_PLX_9050, 0, 0, + pbn_b2_8_921600 }, { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_VENDOR_ID_PLX, PCI_SUBDEVICE_ID_UNKNOWN_0x1588, 0, 0, diff --git a/drivers/tty/serial/8250/8250_pcilib.c b/drivers/tty/serial/8250/8250_pcilib.c index d234e9194feb5..18a04874ec468 100644 --- a/drivers/tty/serial/8250/8250_pcilib.c +++ b/drivers/tty/serial/8250/8250_pcilib.c @@ -18,6 +18,12 @@ int serial8250_pci_setup_port(struct pci_dev *dev, struct uart_8250_port *port, if (bar >= PCI_STD_NUM_BARS) return -EINVAL; + if ((dev->vendor == PCI_VENDOR_ID_PLX) && + (dev->device == PCI_DEVICE_ID_PLX_9050) && + (dev->subsystem_vendor == PCI_VENDOR_ID_PLX) && + (dev->subsystem_device == PCI_DEVICE_ID_PLX_9050)) + offset += 0x80; + if (pci_resource_flags(dev, bar) & IORESOURCE_MEM) { if (!pcim_iomap(dev, bar, 0) && !pcim_iomap_table(dev)) return -ENOMEM; From 2e5a683050acff2cd960a5a239f3214e99c5abcf Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Tue, 18 Jul 2017 16:41:48 +0800 Subject: [PATCH 098/130] AOSCOS: HID: Add some usb-ids ILITEK touch screen driver Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/hid/hid-ids.h | 12 ++++++++++- drivers/hid/hid-multitouch.c | 42 +++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index f16940f3d93d4..ff515e454589c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -643,7 +643,17 @@ #define USB_DEVICE_ID_IDEACOM_IDC6680 0x6680 #define USB_VENDOR_ID_ILITEK 0x222a -#define USB_DEVICE_ID_ILITEK_MULTITOUCH 0x0001 +#define USB_DEVICE_ID_ILITEK_MULTITOUCH01 0x0001 +#define USB_DEVICE_ID_ILITEK_MULTITOUCH02 0x0002 +#define USB_DEVICE_ID_ILITEK_MULTITOUCH06 0x0006 +#define USB_DEVICE_ID_ILITEK_MULTITOUCH10 0x0010 +#define USB_DEVICE_ID_ILITEK_MULTITOUCH15 0x0015 +#define USB_DEVICE_ID_ILITEK_MULTITOUCH1C 0x001C +#define USB_DEVICE_ID_ILITEK_MULTITOUCH1F 0x001F +#define USB_DEVICE_ID_ILITEK_MULTITOUCH24 0x0024 +#define USB_DEVICE_ID_ILITEK_MULTITOUCH41 0x0041 +#define USB_DEVICE_ID_ILITEK_MULTITOUCH88 0x0088 +#define USB_DEVICE_ID_ILITEK_MULTITOUCH94 0x0094 #define USB_VENDOR_ID_INTEL_0 0x8086 #define USB_VENDOR_ID_INTEL_1 0x8087 diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index bf9cad7112592..d3c1b174d6544 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -2101,7 +2101,47 @@ static const struct hid_device_id mt_devices[] = { /* Ilitek dual touch panel */ { .driver_data = MT_CLS_NSMU, MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, - USB_DEVICE_ID_ILITEK_MULTITOUCH) }, + USB_DEVICE_ID_ILITEK_MULTITOUCH01) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH02) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH06) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH10) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH15) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH1C) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH1F) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH24) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH41) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH88) }, + + { .driver_data = MT_CLS_NSMU, + MT_USB_DEVICE(USB_VENDOR_ID_ILITEK, + USB_DEVICE_ID_ILITEK_MULTITOUCH94) }, /* LG Melfas panel */ { .driver_data = MT_CLS_LG, From 3dd11a86bb2b1e71091073e6fed4566eef371c27 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sat, 24 Nov 2018 15:28:13 +0800 Subject: [PATCH 099/130] AOSCOS: USB: OHCI: Fix ohci_resume() for hibernation During hibernation restore, some OHCI controllers (such as in AMD RS780 and Loongson LS7A) may generate RHSC interrupt. Once RHSC interrupt occurs, it will be NOT really handled (just return IRQ_NOTMINE) by ohci_irq() because of OHCI_RH_HALTED at that time, until HCR command done. So we turn off MIE before OHCI reset to prevent from possible interrupt so that to reset safely. MIE will be enabled later in ohci_rh_resume(), and as a result this patch is also suitable for controllers without this trouble. Signed-off-by: Huacai Chen Signed-off-by: Jianmin Lv Signed-off-by: Kexy Biscuit --- drivers/usb/host/ohci-hcd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 5cec7640e913c..67335c98f717e 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1138,8 +1138,10 @@ int ohci_resume(struct usb_hcd *hcd, bool hibernated) set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); /* Make sure resume from hibernation re-enumerates everything */ - if (hibernated) + if (hibernated) { + ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable); ohci_usb_reset(ohci); + } /* See if the controller is already running or has been reset */ ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); From 186cb85b5da6e53a2f1e8d059a9d294483a910f0 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Mon, 3 Jun 2019 10:16:02 +0800 Subject: [PATCH 100/130] AOSCOS: USB: XHCI: Fix device lost problems on ETRON controller [Mingcong Bai: Resolved merge conflicts in drivers/usb/host/xhci-ring.c and drivers/usb/host/xhci.h. Also renamed BIT_ULL index for ETRON_HOST to 49, working around the reserved bit for XHCI_RENESAS_FW_QUIRK.] Signed-off-by: Huacai Chen Signed-off-by: Liangliang Huang Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/usb/host/xhci-hub.c | 7 +++++++ drivers/usb/host/xhci-mem.c | 19 +++++++++++++++++++ drivers/usb/host/xhci-ring.c | 20 ++++++++++++++++++-- drivers/usb/host/xhci.c | 13 +++++++++++++ drivers/usb/host/xhci.h | 1 + 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 0df5d807a77e8..26487a660729e 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -361,6 +361,9 @@ static void xhci_usb3_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, } desc->u.ss.DeviceRemovable = cpu_to_le16(port_removable); + + if (xhci->quirks & XHCI_ETRON_HOST) + desc->bPwrOn2PwrGood = 255; } static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, @@ -889,6 +892,10 @@ static void xhci_hub_report_usb3_link_state(struct xhci_hcd *xhci, if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && (pls == USB_SS_PORT_LS_COMP_MOD)) pls |= USB_PORT_STAT_CONNECTION; + + if ((xhci->quirks & XHCI_ETRON_HOST) && + (pls == USB_SS_PORT_LS_COMP_MOD)) + pls |= USB_PORT_STAT_CONNECTION; } /* update status field */ diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 54c47463c215c..0ad216fcd2178 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1409,9 +1409,15 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, unsigned int mult; unsigned int avg_trb_len; unsigned int err_count = 0; + u8 reg8; + unsigned long flags; + struct usb_hcd *hcd; + struct xhci_virt_ep *virt_ep; + hcd = xhci_to_hcd(xhci); ep_index = xhci_get_endpoint_index(&ep->desc); ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, ep_index); + virt_ep = &virt_dev->eps[ep_index]; endpoint_type = xhci_get_endpoint_type(ep); if (!endpoint_type) @@ -1464,6 +1470,19 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, if ((xhci->hci_version > 0x100) && HCC2_LEC(xhci->hcc_params2)) mult = 0; + if (xhci->quirks & XHCI_ETRON_HOST) { + if (udev->speed == USB_SPEED_LOW && usb_endpoint_is_int_in(&ep->desc)) { + spin_lock_irqsave(&xhci->lock, flags); + reg8 = readb(hcd->regs + 0x4300); + reg8 |= 0x04; + writeb(reg8, hcd->regs + 0x4300); + spin_unlock_irqrestore(&xhci->lock, flags); + + max_packet = 9; + virt_ep->ep_state |= EP_EJ188_FIX; + } + } + /* Set up the endpoint ring */ virt_dev->eps[ep_index].new_ring = xhci_ring_alloc(xhci, 2, 1, ring_type, max_packet, mem_flags); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 50f5880114004..4d387eb5409b5 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2548,6 +2548,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, struct xhci_slot_ctx *slot_ctx; u32 trb_comp_code; u32 remaining, requested, ep_trb_len; + union xhci_trb *cur_trb; slot_ctx = xhci_get_slot_ctx(xhci, ep->vdev->out_ctx); trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); @@ -2595,9 +2596,15 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, break; } - if (ep_trb == td->last_trb) + if (ep_trb == td->last_trb) { td->urb->actual_length = requested - remaining; - else + if ((xhci->quirks & XHCI_ETRON_HOST) && (ep->ep_state & EP_EJ188_FIX)) { + cur_trb = ep_ring->dequeue; + td->urb->actual_length = + TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - + EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); + } + } else td->urb->actual_length = sum_trb_lengths(xhci, ep_ring, ep_trb) + ep_trb_len - remaining; @@ -3660,7 +3667,9 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, int sent_len, ret; u32 field, length_field, remainder; u64 addr, send_addr; + struct xhci_virt_ep *ep; + ep = &xhci->devs[slot_id]->eps[ep_index]; ring = xhci_urb_to_transfer_ring(xhci, urb); if (!ring) return -EINVAL; @@ -3762,6 +3771,13 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); + if ((xhci->quirks & XHCI_ETRON_HOST) && + (ep->ep_state & EP_EJ188_FIX)) { + length_field = TRB_LEN(9) | + TRB_TD_SIZE(remainder) | + TRB_INTR_TARGET(0); + } + queue_trb(xhci, ring, more_trbs_coming | need_zero_pkt, lower_32_bits(send_addr), upper_32_bits(send_addr), diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3bd70e6ad64ba..dee9965e2f045 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2898,6 +2898,8 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_input_control_ctx *ctrl_ctx; struct xhci_slot_ctx *slot_ctx; struct xhci_command *command; + u8 reg8; + unsigned long flags; ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); if (ret <= 0) @@ -2981,6 +2983,17 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_debugfs_create_endpoint(xhci, virt_dev, i); } command_cleanup: + if (xhci->quirks & XHCI_ETRON_HOST) { + spin_lock_irqsave(&xhci->lock, flags); + hcd = xhci_to_hcd(xhci); + reg8 = readb(hcd->regs + 0x4300); + if (reg8 & 0x04) { + reg8 &= ~0x04; + writeb(reg8, hcd->regs + 0x4300); + } + spin_unlock_irqrestore(&xhci->lock, flags); + } + kfree(command->completion); kfree(command); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 4bbd12db7239a..eefcfd40848fc 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -697,6 +697,7 @@ struct xhci_virt_ep { #define EP_SOFT_CLEAR_TOGGLE (1 << 7) /* usb_hub_clear_tt_buffer is in progress */ #define EP_CLEARING_TT (1 << 8) +#define EP_EJ188_FIX (1 << 9) /* ---- Related to URB cancellation ---- */ struct list_head cancelled_td_list; struct xhci_hcd *xhci; From 559dce9a3c53133c6933bcfe9ef59c272de8d69a Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Mon, 16 Apr 2018 15:13:09 +0800 Subject: [PATCH 101/130] AOSCOS: Loongson: Add LS7A pwm driver support Signed-off-by: Ce Sun Signed-off-by: Huacai Chen Signed-off-by: Kexy Biscuit --- drivers/platform/mips/Makefile | 2 +- drivers/platform/mips/ls7a_fan.c | 598 +++++++++++++++++++++++++++++++ 2 files changed, 599 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/mips/ls7a_fan.c diff --git a/drivers/platform/mips/Makefile b/drivers/platform/mips/Makefile index 4dde0988fdadb..a3e9e6b2f38fb 100644 --- a/drivers/platform/mips/Makefile +++ b/drivers/platform/mips/Makefile @@ -3,7 +3,7 @@ obj-$(CONFIG_CPU_HWMON) += cpu_hwmon.o obj-$(CONFIG_RS780E_ACPI) += rs780e-acpi.o obj-$(CONFIG_LS2K_RESET) += ls2k-reset.o obj-$(CONFIG_I2C_PIIX4) += emc1412.o sd5075.o tmp75.o -obj-$(CONFIG_CPU_HWMON) += wpce_fan.o sbx00_fan.o +obj-$(CONFIG_CPU_HWMON) += wpce_fan.o ls7a_fan.o sbx00_fan.o obj-$(CONFIG_LEMOTE3A_LAPTOP) += lemote3a-laptop.o obj-m += ec_rom.o at24c04.o diff --git a/drivers/platform/mips/ls7a_fan.c b/drivers/platform/mips/ls7a_fan.c new file mode 100644 index 0000000000000..cc188d4d05762 --- /dev/null +++ b/drivers/platform/mips/ls7a_fan.c @@ -0,0 +1,598 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LS7A_PWM_REG_BASE (void *)TO_UNCAC(LS7A_MISC_REG_BASE + 0x20000) + +#define LS7A_PWM0_LOW 0x004 +#define LS7A_PWM0_FULL 0x008 +#define LS7A_PWM0_CTRL 0x00c + +#define LS7A_PWM1_LOW 0x104 +#define LS7A_PWM1_FULL 0x108 +#define LS7A_PWM1_CTRL 0x10c + +#define LS7A_PWM2_LOW 0x204 +#define LS7A_PWM2_FULL 0x208 +#define LS7A_PWM2_CTRL 0x20c + +#define LS7A_PWM3_LOW 0x304 +#define LS7A_PWM3_FULL 0x308 +#define LS7A_PWM3_CTRL 0x30c + +#define MAX_LS7A_FANS 4 + +static struct device *pwm_hwmon_dev; +static enum fan_control_mode ls7a_fan_mode[MAX_LS7A_FANS]; +static struct loongson_fan_policy fan_policy[MAX_LS7A_FANS]; + +/* up_temp & down_temp used in fan auto adjust */ +static u8 fan_up_temp[MAX_LS7A_FANS]; +static u8 fan_down_temp[MAX_LS7A_FANS]; +static u8 fan_up_temp_level[MAX_LS7A_FANS]; +static u8 fan_down_temp_level[MAX_LS7A_FANS]; + +static void fan1_adjust(struct work_struct *work); +static void fan2_adjust(struct work_struct *work); +static void fan3_adjust(struct work_struct *work); +static void fan4_adjust(struct work_struct *work); + +#define pwm_read(addr) readl(LS7A_PWM_REG_BASE + (addr)) + +#define pwm_write(val, addr) \ + do { \ + writel(val, LS7A_PWM_REG_BASE + (addr));\ + readl(LS7A_PWM_REG_BASE + (addr)); \ + } while (0) + +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf); +static SENSOR_DEVICE_ATTR(name, S_IRUGO, get_hwmon_name, NULL, 0); + +static struct attribute *pwm_hwmon_attributes[] = +{ + &sensor_dev_attr_name.dev_attr.attr, + NULL +}; + +/* Hwmon device attribute group */ +static struct attribute_group pwm_hwmon_attribute_group = +{ + .attrs = pwm_hwmon_attributes, +}; + +/* Hwmon device get name */ +static ssize_t get_hwmon_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "ls7a-fan\n"); +} + +static ssize_t get_fan_level(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t set_fan_level(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t get_fan_mode(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t set_fan_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, + get_fan_level, set_fan_level, 1); +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + get_fan_mode, set_fan_mode, 1); + +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, + get_fan_level, set_fan_level, 2); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, + get_fan_mode, set_fan_mode, 2); + +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, + get_fan_level, set_fan_level, 3); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, + get_fan_mode, set_fan_mode, 3); + +static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, + get_fan_level, set_fan_level, 4); +static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, + get_fan_mode, set_fan_mode, 4); + +static const struct attribute *ls7a_hwmon_fan[4][3] = { + { + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + NULL + }, + { + &sensor_dev_attr_pwm4.dev_attr.attr, + &sensor_dev_attr_pwm4_enable.dev_attr.attr, + NULL + } +}; + +static u32 ls7a_get_fan_level(int id) +{ + unsigned long low = 0, full = 0; + + if (id == 0) { + low = pwm_read(LS7A_PWM0_LOW); + full = pwm_read(LS7A_PWM0_FULL); + } + if (id == 1) { + low = pwm_read(LS7A_PWM1_LOW); + full = pwm_read(LS7A_PWM1_FULL); + } + if (id == 2) { + low = pwm_read(LS7A_PWM2_LOW); + full = pwm_read(LS7A_PWM2_FULL); + } + if (id == 3) { + low = pwm_read(LS7A_PWM3_LOW); + full = pwm_read(LS7A_PWM3_FULL); + } + + return 255 * (full - low) / full; +} + +static void ls7a_set_fan_level(u8 level, int id) +{ + if (id == 0){ + pwm_write(255, LS7A_PWM0_FULL); + pwm_write(255 - level, LS7A_PWM0_LOW); + } + if (id == 1){ + pwm_write(255, LS7A_PWM1_FULL); + pwm_write(255 - level, LS7A_PWM1_LOW); + } + if (id == 2){ + pwm_write(255, LS7A_PWM2_FULL); + pwm_write(255 - level, LS7A_PWM2_LOW); + } + if (id == 3){ + pwm_write(255, LS7A_PWM3_FULL); + pwm_write(255 - level, LS7A_PWM3_LOW); + } + +} + +static void get_up_temp(struct loongson_fan_policy *policy, + u8 current_temp, u8* up_temp, u8* up_temp_level) +{ + int i; + + for (i = 0; i < policy->up_step_num; i++) { + if (current_temp <= policy->up_step[i].low) { + *up_temp = policy->up_step[i].low; + *up_temp_level = i; + return; + } + } + + *up_temp = MAX_TEMP; + *up_temp_level = policy->up_step_num - 1; +} + +static void get_down_temp(struct loongson_fan_policy *policy, u8 current_temp, + u8* down_temp, u8* down_temp_level) +{ + int i; + + for (i = policy->down_step_num-1; i >= 0; i--) { + if (current_temp >= policy->down_step[i].high) { + *down_temp = policy->down_step[i].high; + *down_temp_level = i; + return; + } + } + + *down_temp = MIN_TEMP; + *down_temp_level = 0; +} + + +static ssize_t get_fan_level(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + u32 val = ls7a_get_fan_level(id); + + return sprintf(buf, "%d\n", val); +} + +static ssize_t set_fan_level(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + u8 new_speed; + int id = (to_sensor_dev_attr(attr))->index - 1; + + if(!ls7a_fan_mode[id]) + return count; + + new_speed = clamp_val(simple_strtoul(buf, NULL, 10), 0, 255); + + ls7a_set_fan_level(new_speed, id); + + return count; +} + +static ssize_t get_fan_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + return sprintf(buf, "%d\n", ls7a_fan_mode[id]); +} + +static void ls7a_fan_step_mode(get_temp_fun fun, + struct loongson_fan_policy *policy, int id) +{ + u8 current_temp; + int init_temp_level, target_fan_level; + + current_temp = fun(0) / 1000; + get_up_temp(policy, current_temp, &fan_up_temp[id], + &fan_up_temp_level[id]); + get_down_temp(policy, current_temp, &fan_down_temp[id], + &fan_down_temp_level[id]); + + /* current speed is not sure, setting now */ + init_temp_level = (fan_up_temp_level[id] + fan_down_temp_level[id]) / 2; + target_fan_level = policy->up_step[init_temp_level].level * 255 / 100; + ls7a_set_fan_level(target_fan_level * policy->percent / 100, id); + + schedule_delayed_work_on(0, &policy->work, policy->adjust_period * HZ); +} + +static void ls7a_fan_start_auto(int id) +{ + u8 level; + + switch (fan_policy[id].type) { + case CONSTANT_SPEED_POLICY: + level = fan_policy[id].percent * 255 / 100; + if (level > MAX_FAN_LEVEL) + level = MAX_FAN_LEVEL; + ls7a_set_fan_level(level, id); + break; + case STEP_SPEED_POLICY: + ls7a_fan_step_mode(fan_policy[id].depend_temp, &fan_policy[id], id); + break; + default: + printk(KERN_ERR "ls7a fan not support fan policy id %d!\n", fan_policy[id].type); + ls7a_set_fan_level(MAX_FAN_LEVEL, id); + } + + return; +} + +static void ls7a_fan_stop_auto(int id) +{ + if ((fan_policy[id].type == STEP_SPEED_POLICY) && + (ls7a_fan_mode[id] == FAN_AUTO_MODE)) { + cancel_delayed_work(&fan_policy[id].work); + } +} + + +static ssize_t set_fan_mode(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int id = (to_sensor_dev_attr(attr))->index - 1; + u8 new_mode; + + new_mode = clamp_val(simple_strtoul(buf, NULL, 10), + FAN_FULL_MODE, FAN_AUTO_MODE); + if (new_mode == ls7a_fan_mode[id]) + return count; + + switch (new_mode) { + case FAN_FULL_MODE: + ls7a_fan_stop_auto(id); + ls7a_set_fan_level(MAX_FAN_LEVEL, id); + break; + case FAN_MANUAL_MODE: + ls7a_fan_stop_auto(id); + break; + case FAN_AUTO_MODE: + ls7a_fan_start_auto(id); + break; + default: + break; + } + + ls7a_fan_mode[id] = new_mode; + + return count; +} + +static void fan1_adjust(struct work_struct *work) +{ + u8 current_temp; + int target_fan_level; + + current_temp = fan_policy[0].depend_temp(0) / 1000; + + if ((current_temp <= fan_up_temp[0]) && + (current_temp >= fan_down_temp[0])) + goto exit; + + if (current_temp > fan_up_temp[0]) + target_fan_level = fan_policy[0].up_step[fan_up_temp_level[0]].level * 255 / 100; + + if (current_temp < fan_down_temp[0]) + target_fan_level = fan_policy[0].down_step[fan_down_temp_level[0]].level * 255 / 100; + + ls7a_set_fan_level(target_fan_level * fan_policy[0].percent / 100, 0); + + get_up_temp(&fan_policy[0], current_temp, &fan_up_temp[0], &fan_up_temp_level[0]); + get_down_temp(&fan_policy[0], current_temp, &fan_down_temp[0], &fan_down_temp_level[0]); + +exit: + schedule_delayed_work_on(0, &fan_policy[0].work, fan_policy[0].adjust_period * HZ); +} + +static void ls7a_fan1_init(void) +{ + int ret; + + pwm_write(1, LS7A_PWM0_CTRL); + pwm_write(255, LS7A_PWM0_FULL); /* Full = 255, so Low + Pwm = 255 */ + + INIT_DEFERRABLE_WORK(&fan_policy[0].work, fan1_adjust); + + /* force fans in auto mode first */ + ls7a_fan_mode[0] = FAN_AUTO_MODE; + ls7a_fan_start_auto(0); + + ret = sysfs_create_files(&pwm_hwmon_dev->kobj, ls7a_hwmon_fan[0]); + if (ret) + printk(KERN_ERR "fail to create sysfs files\n"); +} + +static void fan2_adjust(struct work_struct *work) +{ + u8 current_temp; + int target_fan_level; + + current_temp = fan_policy[1].depend_temp(0) / 1000; + + if ((current_temp <= fan_up_temp[1]) && + (current_temp >= fan_down_temp[1])) + goto exit; + + if (current_temp > fan_up_temp[1]) + target_fan_level = fan_policy[1].up_step[fan_up_temp_level[1]].level * 255 / 100; + + if (current_temp < fan_down_temp[1]) + target_fan_level = fan_policy[1].down_step[fan_down_temp_level[1]].level * 255 / 100; + + ls7a_set_fan_level(target_fan_level * fan_policy[1].percent / 100, 1); + + get_up_temp(&fan_policy[1], current_temp, &fan_up_temp[1], &fan_up_temp_level[1]); + get_down_temp(&fan_policy[1], current_temp, &fan_down_temp[1], &fan_down_temp_level[1]); + +exit: + schedule_delayed_work_on(0, &fan_policy[1].work, fan_policy[1].adjust_period * HZ); +} + +static void ls7a_fan2_init(void) +{ + int ret; + + pwm_write(1, LS7A_PWM1_CTRL); + pwm_write(255, LS7A_PWM1_FULL); /* Full = 255, so Low + Pwm = 255 */ + + INIT_DEFERRABLE_WORK(&fan_policy[1].work, fan2_adjust); + + /* force fans in auto mode first */ + ls7a_fan_mode[1] = FAN_AUTO_MODE; + ls7a_fan_start_auto(1); + + ret = sysfs_create_files(&pwm_hwmon_dev->kobj, ls7a_hwmon_fan[1]); + if (ret) + printk(KERN_ERR "fail to create sysfs files\n"); +} + +static void fan3_adjust(struct work_struct *work) +{ + u8 current_temp; + int target_fan_level; + + current_temp = fan_policy[2].depend_temp(0) / 1000; + + if ((current_temp <= fan_up_temp[2]) && + (current_temp >= fan_down_temp[2])) + goto exit; + + if (current_temp > fan_up_temp[2]) + target_fan_level = fan_policy[2].up_step[fan_up_temp_level[2]].level * 255 / 100; + + if (current_temp < fan_down_temp[2]) + target_fan_level = fan_policy[2].down_step[fan_down_temp_level[2]].level * 255 / 100; + + ls7a_set_fan_level(target_fan_level * fan_policy[2].percent / 100, 2); + + get_up_temp(&fan_policy[2], current_temp, &fan_up_temp[2], &fan_up_temp_level[2]); + get_down_temp(&fan_policy[2], current_temp, &fan_down_temp[2], &fan_down_temp_level[2]); + +exit: + schedule_delayed_work_on(0, &fan_policy[2].work, fan_policy[2].adjust_period * HZ); +} + +static void ls7a_fan3_init(void) +{ + int ret; + + pwm_write(1, LS7A_PWM2_CTRL); + pwm_write(255, LS7A_PWM2_FULL); /* Full = 255, so Low + Pwm = 255 */ + + INIT_DEFERRABLE_WORK(&fan_policy[2].work, fan3_adjust); + + /* force fans in auto mode first */ + ls7a_fan_mode[2] = FAN_AUTO_MODE; + ls7a_fan_start_auto(2); + + ret = sysfs_create_files(&pwm_hwmon_dev->kobj, ls7a_hwmon_fan[2]); + if (ret) + printk(KERN_ERR "fail to create sysfs files\n"); +} + +static void fan4_adjust(struct work_struct *work) +{ + u8 current_temp; + int target_fan_level; + + current_temp = fan_policy[3].depend_temp(0) / 1000; + + if ((current_temp <= fan_up_temp[3]) && + (current_temp >= fan_down_temp[3])) + goto exit; + + if (current_temp > fan_up_temp[3]) + target_fan_level = fan_policy[3].up_step[fan_up_temp_level[3]].level * 255 / 100; + + if (current_temp < fan_down_temp[3]) + target_fan_level = fan_policy[3].down_step[fan_down_temp_level[3]].level * 255 / 100; + + ls7a_set_fan_level(target_fan_level * fan_policy[3].percent / 100, 3); + + get_up_temp(&fan_policy[3], current_temp, &fan_up_temp[3], &fan_up_temp_level[3]); + get_down_temp(&fan_policy[3], current_temp, &fan_down_temp[3], &fan_down_temp_level[3]); + +exit: + schedule_delayed_work_on(0, &fan_policy[3].work, fan_policy[3].adjust_period * HZ); +} + +static void ls7a_fan4_init(void) +{ + int ret; + + pwm_write(1, LS7A_PWM3_CTRL); + pwm_write(255, LS7A_PWM3_FULL); /* Full = 255, so Low + Pwm = 255 */ + + INIT_DEFERRABLE_WORK(&fan_policy[3].work, fan4_adjust); + + /* force fans in auto mode first */ + ls7a_fan_mode[3] = FAN_AUTO_MODE; + ls7a_fan_start_auto(3); + + ret = sysfs_create_files(&pwm_hwmon_dev->kobj, ls7a_hwmon_fan[3]); + if (ret) + printk(KERN_ERR "fail to create sysfs files\n"); +} + +static int ls7a_fan_probe(struct platform_device *dev) +{ + int id = dev->id - 1; + struct sensor_device *sdev = (struct sensor_device *)dev->dev.platform_data; + + /* get fan policy */ + switch (sdev->fan_policy) { + case STEP_SPEED_POLICY: + memcpy(&fan_policy[id], &step_speed_policy, sizeof(struct loongson_fan_policy)); + fan_policy[id].percent = sdev->fan_percent; + if (fan_policy[id].percent == 0 || fan_policy[id].percent > 100) + fan_policy[id].percent = 100; + break; + case CONSTANT_SPEED_POLICY: + default: + memcpy(&fan_policy[id], &constant_speed_policy, sizeof(struct loongson_fan_policy)); + fan_policy[id].percent = sdev->fan_percent; + if (fan_policy[id].percent == 0 || fan_policy[id].percent > 100) + fan_policy[id].percent = 100; + break; + } + + if (id == 0) + ls7a_fan1_init(); + if (id == 1) + ls7a_fan2_init(); + if (id == 2) + ls7a_fan3_init(); + if (id == 3) + ls7a_fan4_init(); + + return 0; +} + + +static struct platform_driver pwm_fan_driver = { + .probe = ls7a_fan_probe, + .driver = { + .name = "ls7a-fan", + .owner = THIS_MODULE, + }, +}; + + +static int ls7a_fan_init(void) +{ + int ret; + + pwm_hwmon_dev = hwmon_device_register(NULL); + if (IS_ERR(pwm_hwmon_dev)) { + ret = -ENOMEM; + printk(KERN_ERR "hwmon_device_register fail!\n"); + goto fail_hwmon_device_register; + } + + ret = sysfs_create_group(&pwm_hwmon_dev->kobj, + &pwm_hwmon_attribute_group); + if (ret) { + printk(KERN_ERR "fail to create loongson hwmon!\n"); + goto fail_sysfs_create_group_hwmon; + } + + ret = platform_driver_register(&pwm_fan_driver); + if (ret) { + printk(KERN_ERR "fail to register fan driver!\n"); + goto fail_register_fan; + } + + return 0; + +fail_register_fan: + sysfs_remove_group(&pwm_hwmon_dev->kobj, + &pwm_hwmon_attribute_group); + +fail_sysfs_create_group_hwmon: + hwmon_device_unregister(pwm_hwmon_dev); + +fail_hwmon_device_register: + return ret; +} + +static void ls7a_fan_exit(void) +{ + platform_driver_unregister(&pwm_fan_driver); + sysfs_remove_group(&pwm_hwmon_dev->kobj, + &pwm_hwmon_attribute_group); + hwmon_device_unregister(pwm_hwmon_dev); +} + +late_initcall(ls7a_fan_init); +module_exit(ls7a_fan_exit); + +MODULE_AUTHOR("Sun Ce "); +MODULE_AUTHOR("Huacai Chen "); +MODULE_DESCRIPTION("LS7A fan control driver"); +MODULE_LICENSE("GPL"); From c28388119c6d836fd9e7a3bd3eecda8a0e05ab94 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 11 Dec 2024 23:29:38 +0800 Subject: [PATCH 102/130] AOSCOS: MIPS: ls7a_fan: use register addresses in loongson.h Original driver code expects register definition in loongson-pch.h (where LS7A register definitions were never merged upstream), which are now revised and stored in loongson.h. Miscellaneous register base (LS7A_MISC_REG_BASE) starts at: LOONGSON_PCILO0_BASE + 0x80000 According to legacy loongson-pch.h from Lemote. Revise definition for LS7A_PWM_REG_BASE to be: LOONGSON_PCILO0_BASE (LS7A_PCH_REG_BASE in loongson-pch.h) + 0x80000 = LS7A_MISC_REG_BASE LS7A_MISC_REG_BASE + 0x2000 = LS7A_PWM_REG_BASE Fixes: "AOSCOS: MIPS: ls7a_fan: use register addresses in loongson.h" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/platform/mips/ls7a_fan.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/platform/mips/ls7a_fan.c b/drivers/platform/mips/ls7a_fan.c index cc188d4d05762..e9cf2dded39e4 100644 --- a/drivers/platform/mips/ls7a_fan.c +++ b/drivers/platform/mips/ls7a_fan.c @@ -9,10 +9,16 @@ #include #include -#include +#include #include -#define LS7A_PWM_REG_BASE (void *)TO_UNCAC(LS7A_MISC_REG_BASE + 0x20000) +/** + * Miscellaneous register base (LS7A_MISC_REG_BASE) starts at + * LOONGSON_PCILO0_BASE + 0x80000, according to legacy loongson-pch.h + * from Lemote. + */ +#define LS7A_PWM_REG_BASE (void *)TO_UNCAC(LOONGSON_PCILO0_BASE + \ + 0x80000 + 0x20000) #define LS7A_PWM0_LOW 0x004 #define LS7A_PWM0_FULL 0x008 From 8aff50ef8c0530cddd0ea9effd3410f3763fd6a2 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 00:29:07 +0800 Subject: [PATCH 103/130] AOSCOS: MIPS: math-emu: replace CPU_LOONGSON3 conditions with CPU_LOONGSON64 Per commit 30ad29bb4888 ("MIPS: Loongson: Naming style cleanup and rework"), rename the CPU_LOONGSON3 definition as CPU_LOONGSON64. Fixes: "FROMLIST: MIPS: math-emu: Add madd/msub/nmadd/nmsub emulation for Loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/math-emu/cp1emu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index a2cba79eba7a5..0c618683e57cf 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -1451,7 +1451,7 @@ static union ieee754sp fpemu_sp_rsqrt(union ieee754sp s) return ieee754sp_div(ieee754sp_one(0), ieee754sp_sqrt(s)); } -#ifndef CONFIG_CPU_LOONGSON3 +#ifndef CONFIG_CPU_LOONGSON64 DEF3OP(madd, sp, ieee754sp_mul, ieee754sp_add, ); DEF3OP(msub, sp, ieee754sp_mul, ieee754sp_sub, ); DEF3OP(nmadd, sp, ieee754sp_mul, ieee754sp_add, ieee754sp_neg); @@ -1515,7 +1515,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; -#ifdef CONFIG_CPU_LOONGSON3 +#ifdef CONFIG_CPU_LOONGSON64 case madd_s_op: handler = ieee754sp_madd; goto scoptop; @@ -1638,7 +1638,7 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx, } break; -#ifdef CONFIG_CPU_LOONGSON3 +#ifdef CONFIG_CPU_LOONGSON64 case madd_d_op: handler = ieee754dp_madd; goto dcoptop; From 376c82e90dee692f619fbd91bd010d699e71e596 Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 104/130] AOSCOS: Optimize clear_page Signed-off-by: chenj Signed-off-by: Kexy Biscuit --- arch/mips/include/asm/uasm.h | 5 +++++ arch/mips/include/uapi/asm/inst.h | 7 +++++++ arch/mips/mm/page.c | 9 +++++++++ arch/mips/mm/uasm-mips.c | 24 ++++++++++++++++++++++++ arch/mips/mm/uasm.c | 12 +++++++++++- 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/arch/mips/include/asm/uasm.h b/arch/mips/include/asm/uasm.h index 296bcf31abb57..06612a106ab4d 100644 --- a/arch/mips/include/asm/uasm.h +++ b/arch/mips/include/asm/uasm.h @@ -42,6 +42,10 @@ void uasm_i##op(u32 **buf, unsigned int a, signed int b, unsigned int c) #define Ip_s3s1s2(op) \ void uasm_i##op(u32 **buf, int a, int b, int c) +#define Ip_u4u2u1s3(op) \ +void uasm_i##op(u32 **buf, unsigned int a, unsigned int b, signed int c, \ + unsigned int d) + #define Ip_u2u1s3(op) \ void uasm_i##op(u32 **buf, unsigned int a, unsigned int b, signed int c) @@ -159,6 +163,7 @@ Ip_u2s3u1(_sd); Ip_u3u1u2(_seleqz); Ip_u3u1u2(_selnez); Ip_u2s3u1(_sh); +Ip_u4u2u1s3(_gssq); Ip_u2u1u3(_sll); Ip_u3u2u1(_sllv); Ip_s3s1s2(_slt); diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h index c29dbc8c1d491..6d41c8bbbdec2 100644 --- a/arch/mips/include/uapi/asm/inst.h +++ b/arch/mips/include/uapi/asm/inst.h @@ -137,6 +137,13 @@ enum ddivu_op { ddivu_dmodu_op = 0x3, }; +/* + * func field of spec opcode. + */ +enum swc2_op { + gssq_op = 0x20, +}; + /* * rt field of bcond opcodes. */ diff --git a/arch/mips/mm/page.c b/arch/mips/mm/page.c index d3b4459d0fe85..4dbecc10e7a01 100644 --- a/arch/mips/mm/page.c +++ b/arch/mips/mm/page.c @@ -218,6 +218,11 @@ static void set_prefetch_parameters(void) else if (cpu_has_cache_cdex_p) cache_line_size = cpu_dcache_line_size(); } + +#ifdef CONFIG_CPU_LOONGSON64 + clear_word_size = 16; +#endif + /* * Too much unrolling will overflow the available space in * clear_space_array / copy_page_array. @@ -232,11 +237,15 @@ static void set_prefetch_parameters(void) static void build_clear_store(u32 **buf, int off) { +#ifdef CONFIG_CPU_LOONGSON64 + uasm_i_gssq(buf, ZERO, ZERO, off, A0); +#else if (cpu_has_64bit_gp_regs || cpu_has_64bit_zero_reg) { uasm_i_sd(buf, ZERO, off, A0); } else { uasm_i_sw(buf, ZERO, off, A0); } +#endif } static inline void build_clear_pref(u32 **buf, int off) diff --git a/arch/mips/mm/uasm-mips.c b/arch/mips/mm/uasm-mips.c index e15c6700cd088..7e44ea365795b 100644 --- a/arch/mips/mm/uasm-mips.c +++ b/arch/mips/mm/uasm-mips.c @@ -27,6 +27,10 @@ #define RT_SH 16 #define SCIMM_MASK 0xfffff #define SCIMM_SH 6 +#define RZ_MASK 0x1f +#define RZ_SH 0 +#define RC_MASK 0x1ff +#define RC_SH 6 /* This macro sets the non-variable bits of an instruction. */ #define M(a, b, c, d, e, f) \ @@ -203,6 +207,7 @@ static const struct insn insn_table[insn_invalid] = { [insn_xor] = {M(spec_op, 0, 0, 0, 0, xor_op), RS | RT | RD}, [insn_xori] = {M(xori_op, 0, 0, 0, 0, 0), RS | RT | UIMM}, [insn_yield] = {M(spec3_op, 0, 0, 0, 0, yield_op), RS | RD}, + [insn_gssq] = {M(swc2_op, 0, 0, 0, 0, gssq_op), RT | RS | RZ | RC}, }; #undef M @@ -225,6 +230,21 @@ static inline u32 build_jimm(u32 arg) return (arg >> 2) & JIMM_MASK; } +static inline u32 build_rz(u32 arg) +{ + WARN(arg & ~RZ_MASK, KERN_WARNING "Micro-assembler field overflow\n"); + + return (arg & RZ_MASK) << RZ_SH; +} + +static inline u32 build_rc(s32 arg) +{ + WARN((arg >> 4) > 0xff || + (arg >> 4) < -0x100, KERN_WARNING "Micro-assembler field overflow\n"); + + return ((arg >> 4) & RC_MASK) << RC_SH; +} + /* * The order of opcode arguments is implicitly left to right, * starting with RS and ending with FUNC or IMM. @@ -252,6 +272,10 @@ static void build_insn(u32 **buf, enum opcode opc, ...) op |= build_rd(va_arg(ap, u32)); if (ip->fields & RE) op |= build_re(va_arg(ap, u32)); + if (ip->fields & RZ) + op |= build_rz(va_arg(ap, u32)); + if (ip->fields & RC) + op |= build_rc(va_arg(ap, s32)); if (ip->fields & SIMM) op |= build_simm(va_arg(ap, s32)); if (ip->fields & UIMM) diff --git a/arch/mips/mm/uasm.c b/arch/mips/mm/uasm.c index 125140979d62c..2a9ca7de43760 100644 --- a/arch/mips/mm/uasm.c +++ b/arch/mips/mm/uasm.c @@ -26,6 +26,8 @@ enum fields { SET = 0x200, SCIMM = 0x400, SIMM9 = 0x800, + RZ = 0x1000, + RC = 0x2000 }; #define OP_MASK 0x3f @@ -65,7 +67,7 @@ enum opcode { insn_sllv, insn_slt, insn_slti, insn_sltiu, insn_sltu, insn_sra, insn_srav, insn_srl, insn_srlv, insn_subu, insn_sw, insn_sync, insn_syscall, insn_tlbp, insn_tlbr, insn_tlbwi, insn_tlbwr, insn_wait, - insn_wsbh, insn_xor, insn_xori, insn_yield, + insn_wsbh, insn_xor, insn_xori, insn_yield, insn_gssq, insn_invalid /* insn_invalid must be last */ }; @@ -198,6 +200,13 @@ Ip_u2s3u1(op) \ } \ UASM_EXPORT_SYMBOL(uasm_i##op); +#define I_u4u2u1s3(op) \ +Ip_u4u2u1s3(op) \ +{ \ + build_insn(buf, insn##op, d, b, a, c); \ +} \ +UASM_EXPORT_SYMBOL(uasm_i##op); + #define I_u2u1s3(op) \ Ip_u2u1s3(op) \ { \ @@ -356,6 +365,7 @@ I_u2s3u1(_sd) I_u3u1u2(_seleqz) I_u3u1u2(_selnez) I_u2s3u1(_sh) +I_u4u2u1s3(_gssq) I_u2u1u3(_sll) I_u3u2u1(_sllv) I_s3s1s2(_slt) From d5f80cd405efd9038061a3cc0f56aaec4353af05 Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 105/130] AOSCOS: memset optimization for loongson-3 [Mingcong Bai: Resolved a minor conflict in arch/mips/loongson64/Makefile.] Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/Makefile | 2 +- arch/mips/loongson64/loongson3-memset.S | 206 ++++++++++++++++++++++++ 2 files changed, 207 insertions(+), 1 deletion(-) create mode 100644 arch/mips/loongson64/loongson3-memset.S diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index cf305b96f1b86..6fb3ba9f60541 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ clock.o constant_timer.o ec_wpce775l.o \ - platform.o \ + platform.o loongson3-memset.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/loongson3-memset.S b/arch/mips/loongson64/loongson3-memset.S new file mode 100644 index 0000000000000..227b2d67082e3 --- /dev/null +++ b/arch/mips/loongson64/loongson3-memset.S @@ -0,0 +1,206 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1998, 1999, 2000 by Ralf Baechle + * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2007 by Maciej W. Rozycki + * Copyright (C) 2011, 2012 MIPS Technologies, Inc. + */ +#include +#include +#include +#include + +#define LONG_S_L sdl +#define LONG_S_R sdr + +#define STORSIZE 16 +#define STORMASK 15 + +#define EX(insn,reg,addr,handler) \ +9: insn reg, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + +#define EX_GSSQ(reg, addr, handler) \ + .set push; \ + .set arch=loongson3a; \ +9: gssq reg, reg, addr; \ + .set pop; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + + .macro f_fill128 dst, offset, val, fixup + EX_GSSQ(\val, (\offset + 0 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 1 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 2 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 3 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 4 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 5 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 6 * STORSIZE)(\dst), \fixup) + EX_GSSQ(\val, (\offset + 7 * STORSIZE)(\dst), \fixup) + .endm + +/* + * memset(void *s, int c, size_t n) + * + * a0: start of area to clear + * a1: char to fill with + * a2: size of area to clear + */ + .set noreorder + .align 5 +LEAF(memset) +EXPORT_SYMBOL(memset) + beqz a1, 1f + move v0, a0 /* result */ + + andi a1, 0xff /* spread fillword */ + LONG_SLL t1, a1, 8 + or a1, t1 + LONG_SLL t1, a1, 16 + or a1, t1 + LONG_SLL t1, a1, 32 + or a1, t1 +1: + +FEXPORT(__bzero) +EXPORT_SYMBOL(__bzero) + sltiu t0, a2, STORSIZE /* very small region? */ + bnez t0, .Lsmall_memset + andi t0, a0, STORMASK /* aligned? */ + + .set noat + li AT, STORSIZE + beqz t0, 1f + PTR_SUBU t0, AT /* alignment in bytes */ + .set at + + EX(LONG_S_R, a1, (a0), .Lfirst_fixup) /* make word/dword 8B aligned */ + .set push + .set arch=mips64r2 + PTR_ADDIU t1, a0, 8 + dins t1, zero, 0, 3 + .set pop + EX(LONG_S, a1, (t1), .Lsecond_fixup) /* May double copy 8B */ + + PTR_SUBU a0, t0 /* long align ptr */ + PTR_ADDU a2, t0 /* correct size */ + +1: ori t1, a2, 0x7f /* # of full blocks */ + xori t1, 0x7f + beqz t1, .Lmemset_partial /* no block to fill */ + andi t0, a2, 0x80-STORSIZE + + PTR_ADDU t1, a0 /* end address */ + .set reorder +1: PTR_ADDIU a0, 128 + f_fill128 a0, -128, a1, .Lfwd_fixup + bne t1, a0, 1b + .set noreorder + +.Lmemset_partial: + PTR_LA t1, 2f /* where to start */ + .set noat + LONG_SRL AT, t0, 2 + PTR_SUBU t1, AT + .set at + jr t1 + PTR_ADDU a0, t0 /* dest ptr */ + + .set push + .set noreorder + .set nomacro + f_fill128 a0, -128, a1, .Lpartial_fixup /* ... but first do 16Bs ... */ +2: .set pop + andi a2, STORMASK /* At most 15B to go */ + + beqz a2, 1f + PTR_ADDU a0, a2 /* What's left */ + .set push + .set arch=mips64r2 + PTR_ADDI t1, a0, -8 + dins t1, zero, 0, 3 + .set pop + EX(LONG_S, a1, (t1), .Lnotlast_fixup) /* May double copy 8B */ + EX(LONG_S_L, a1, -1(a0), .Llast_fixup) +1: jr ra + move a2, zero + +.Lsmall_memset: + andi t1, a2, 7 + beq t1, a2, 1f + LONG_SLL t1, 2 + + EX(LONG_S_R, a1, (a0), .Lfirst_fixup) + EX(LONG_S_L, a1, 7(a0), .Lsmall_memset_fixup) + +1: PTR_LA t0, 2f + PTR_SUBU t1, t0, t1 + jr t1 + PTR_ADDU a0, a2 + + EX(sb, a1, -7(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -6(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -5(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -4(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -3(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -2(a0), .Lsmall_memset_partial_fixup) + EX(sb, a1, -1(a0), .Lsmall_memset_partial_fixup) + +2: jr ra /* done */ + move a2, zero + END(memset) + +.Lsmall_memset_fixup: + PTR_ADDIU t0, a0, 8 + .set push + .set arch=mips64r2 + dins t0, zero, 0, 3 + .set pop + LONG_ADDU a2, a0 + jr ra + LONG_SUBU a2, t0 + +.Lsmall_memset_partial_fixup: + PTR_L t0, TI_TASK($28) + LONG_L t0, THREAD_BUADDR(t0) + jr ra + LONG_SUBU a2, a0, t0 + +.Lfirst_fixup: + jr ra + nop + +.Lsecond_fixup: + LONG_ADDU a2, a0 + jr ra + LONG_SUBU a2, t1 + +.Lfwd_fixup: + PTR_L t0, TI_TASK($28) + andi a2, 0x7f + LONG_L t0, THREAD_BUADDR(t0) + LONG_ADDU a2, t1 + jr ra + LONG_SUBU a2, t0 + +.Lpartial_fixup: + PTR_L t0, TI_TASK($28) + andi a2, STORMASK + LONG_L t0, THREAD_BUADDR(t0) + LONG_ADDU a2, a0 + jr ra + LONG_SUBU a2, t0 + +.Llast_fixup: + jr ra + andi a2, 0x7 + +.Lnotlast_fixup: + jr ra + PTR_SUBU a2, a0, t1 From 25650a39049b13e7315eb56e028d6aad1b560eee Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 01:27:08 +0800 Subject: [PATCH 106/130] AOSCOS: MIPS: loongson3-memset: replace with Per commit 9259e15b3f27 ("mips: replace #include with for . Follow the commit and revise loongson3-memset.S accordingly. Fixes: "AOSCOS: memset optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/loongson3-memset.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/loongson3-memset.S b/arch/mips/loongson64/loongson3-memset.S index 227b2d67082e3..0a35c013f6df4 100644 --- a/arch/mips/loongson64/loongson3-memset.S +++ b/arch/mips/loongson64/loongson3-memset.S @@ -8,9 +8,9 @@ * Copyright (C) 2007 by Maciej W. Rozycki * Copyright (C) 2011, 2012 MIPS Technologies, Inc. */ +#include #include #include -#include #include #define LONG_S_L sdl From aacb47d71ad9b85cba43e88e42096f14cbb409e8 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 13:30:55 +0800 Subject: [PATCH 107/130] AOSCOS: MIPS: loongson3-memset: use PTR_WD to fix build Per commit fa62f39dc7e2 ("MIPS: Fix build error due to PTR used in more places"), `PTR' was renamed to `PTR_WD' to avoid definition conflicts. Follow this commit to fix build. Fixes: "AOSCOS: memset optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/loongson3-memset.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/loongson3-memset.S b/arch/mips/loongson64/loongson3-memset.S index 0a35c013f6df4..55c3b8d7bc458 100644 --- a/arch/mips/loongson64/loongson3-memset.S +++ b/arch/mips/loongson64/loongson3-memset.S @@ -22,7 +22,7 @@ #define EX(insn,reg,addr,handler) \ 9: insn reg, addr; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous #define EX_GSSQ(reg, addr, handler) \ @@ -31,7 +31,7 @@ 9: gssq reg, reg, addr; \ .set pop; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous .macro f_fill128 dst, offset, val, fixup From 7b668710c1d5072b7ec5e75af111e6e9d12e8c23 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 13:33:50 +0800 Subject: [PATCH 108/130] AOSCOS: MIPS: lib: exclude generic memset.o if CPU_LOONGSON64 is set In "AOSCOS: memset optimization for loongson-3", a Loongson-3-specific memset implementation was introduced, with its own set of definitions for `bzero' and `memset' routines. This resulted in multiple definitions for the aforementioned assembly routines, causing vmlinux to fail to link. Exclude generic memset.o if `CPU_LOONGSON64' is set to avoid this error. Fixes: "AOSCOS: memset optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/lib/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile index 5d5b993cbc2bf..b5532bab9337b 100644 --- a/arch/mips/lib/Makefile +++ b/arch/mips/lib/Makefile @@ -10,6 +10,7 @@ lib-y += bitops.o csum_partial.o delay.o memcpy.o memset.o \ obj-y += iomap_copy.o obj-$(CONFIG_PCI) += iomap-pci.o lib-$(CONFIG_GENERIC_CSUM) := $(filter-out csum_partial.o, $(lib-y)) +lib-$(CONFIG_CPU_LOONGSON64) := $(filter-out memset.o, $(lib-y)) obj-$(CONFIG_CPU_GENERIC_DUMP_TLB) += dump_tlb.o obj-$(CONFIG_CPU_R3000) += r3k_dump_tlb.o From bdad755f0c99f4c0434bd80493ba95e9789ace53 Mon Sep 17 00:00:00 2001 From: chenj Date: Thu, 1 Dec 2016 09:51:35 +0800 Subject: [PATCH 109/130] AOSCOS: memcpy optimization for loongson-3 [Mingcong Bai: Resolved a minor merge conflict in arch/mips/loongson64/Makefile.] Signed-off-by: Mingcong Bai Signed-off-by: chenj Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/Makefile | 1 + arch/mips/loongson64/loongson3-memcpy.S | 506 ++++++++++++++++++++++++ 2 files changed, 507 insertions(+) create mode 100644 arch/mips/loongson64/loongson3-memcpy.S diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index 6fb3ba9f60541..2155e9532901d 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ setup.o init.o env.o time.o reset.o \ clock.o constant_timer.o ec_wpce775l.o \ platform.o loongson3-memset.o \ + loongson3-memcpy.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S new file mode 100644 index 0000000000000..28b785363351c --- /dev/null +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -0,0 +1,506 @@ +/* + ============================================================================ + Name : memcpy.S + Author : Heiher + Chen Jie + Version : 20140307 + Copyright : GPLv2 + Description : The memcpy for Loongson 3. + ============================================================================ + */ + +#define LINUX_KERNEL +#ifndef LINUX_KERNEL +#include +#include + +#define EXC(inst_reg,addr,handler) \ + inst_reg, addr; +#define EXCQ(inst,reg1,reg2,addr,handler) \ + inst reg1, reg2, addr; + +#if _MIPS_SIM == _ABI64 +#define CONFIG_64BIT +#else +#define CONFIG_32BIT +#endif + +#define FEXPORT(symbol) + +#else /* LINUX_KERNEL */ + +#include +#include +#include +#include + +#define EXC(inst_reg,addr,handler) \ +9: inst_reg, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + +#define EXCQ(inst,reg1,reg2,addr,handler) \ +9: inst reg1, reg2, addr; \ + .section __ex_table,"a"; \ + PTR 9b, handler; \ + .previous + +#endif + +#define dst a0 +#define src a1 +#define len a2 +#define rem t8 + +/* + * 64bit ABI vs 32bit ABI + */ +#ifdef CONFIG_64BIT +#define ADDU daddu +#define ADDI daddi +#define SUBU dsubu +#define SLL dsll +#define SRL dsrl +#define PTR_LA dla + +#define LOAD ld + +/* + * As we are sharing code base with the mips32 tree (which use the o32 ABI + * register definitions). We need to redefine the register definitions from + * the n64 ABI register naming to the o32 ABI register naming. + */ +#undef t0 +#undef t1 +#undef t2 +#undef t3 +#define t0 $8 +#define t1 $9 +#define t2 $10 +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 + +#else + +#define ADDU addu +#define ADDI addi +#define SUBU subu +#define SLL sll +#define SRL srl +#define PTR_LA la + +#define LOAD lw +#endif /* CONFIG_64BIT */ + +#define LDFIRST ldr +#define LDREST ldl + +#define SDFIRST sdr +#define SDREST sdl + +#define LWFIRST lwr +#define LWREST lwl + +#define SWFIRST swr +#define SWREST swl + +/* void * memcpy (void *s1, const void *s2, size_t n); */ + .text + .align 5 + .set noreorder + .set noat + .set arch=loongson3a + +LEAF(memcpy) /* a0=dst a1=src a2=len */ +EXPORT_SYMBOL(memcpy) + move v0, dst +.L__memcpy: +FEXPORT(__copy_user) +EXPORT_SYMBOL(__copy_user) + /* if less then 0x28 bytes */ + sltu t2, a2, 0x28 + andi t0, dst, 0xf + bnez t2, .L_memcpy_less + andi t1, src, 0xf + + beqz t0, 1f + ADDI rem, t0, -0x10 + + /* upgrade */ +EXC( LDFIRST t3, 0(src), .Ll_exc) + sltu t4, t0, 0x8 +EXC( LDREST t3, 7(src), .Ll_exc_copy) + SUBU src, rem +EXC( SDFIRST t3, 0(dst), .Ls_exc) + SUBU dst, rem + beqz t4, 1f + ADDU len, rem +EXC( LDFIRST t3, -8(src), .Ll_exc_a8) +EXC( LDREST t3, -1(src), .Ll_exc_copy_a8) +EXC( sd t3, -8(dst), .Ls_exc_p8) + +1: andi t7, src, 0x7 + beq t0, t1, .L_memcpy_16_16 + nop + bnez t7, .L_memcpy_16_4_2_1 + +.L_memcpy_16_8: + SRL t0, len, 6 # 64B per iteration + beqz t0, 2f + and rem, len, 0x3f + .align 4 +1: +EXC( ld t4, (16 * 0)(src), .Ll_exc) +EXC( ld t7, (8 + 16 * 0)(src), .Ll_exc_copy) +EXC( ld t2, (16 * 1)(src), .Ll_exc_copy) +EXC( ld t3, (8 + 16 * 1)(src), .Ll_exc_copy) +EXC( ld t0, (16 * 2)(src), .Ll_exc_copy) +EXC( ld t1, (8 + 16 * 2)(src), .Ll_exc_copy) + ADDI len, -16 * 4 +EXCQ( gssq, t7, t4, (16 * 0)(dst), .Ls_exc_p64) +EXC( ld t4, (16 * 3)(src), .Ll_exc_copy) +EXC( ld t7, (8 + 16 * 3)(src), .Ll_exc_copy) + ADDU src, 16 * 4 + ADDU dst, 16 * 4 +EXCQ( gssq, t3, t2, (-16 * 3)(dst), .Ls_exc_p48) +EXCQ( gssq, t1, t0, (-16 * 2)(dst), .Ls_exc_p32) +EXCQ( gssq, t7, t4, (-16 * 1)(dst), .Ls_exc_p16) + bne len, rem, 1b + nop + beqz len, .Ldone +2: sltu t0, len, 32 + bnez t0, 3f + and rem, len, 0xf +EXC( ld t2, (16 * 0)(src), .Ll_exc) +EXC( ld t3, (8 + 16 * 0)(src), .Ll_exc_copy) +EXC( ld t0, (16 * 1)(src), .Ll_exc_copy) +EXC( ld t1, (8 + 16 * 1)(src), .Ll_exc_copy) + ADDI len, -16 * 2 + ADDU src, 16 * 2 +EXCQ( gssq, t3, t2, (16 * 0)(dst), .Ls_exc_p32) +EXCQ( gssq, t1, t0, (16 * 1)(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 32 +3: /* copy less than 32B */ + beq rem, len, .L_memcpy_1_15B_8B_aligned + nop +EXC( ld t2, 0(src), .Ll_exc) +EXC( ld t3, 8(src), .Ll_exc_copy) + ADDI len, -16 + ADDU src, 16 +EXCQ( gssq, t3, t2, 0(dst), .Ls_exc_p16) + bnez len, .L_memcpy_1_15B_8B_aligned + ADDU dst, 16 + + jr ra + nop + +.L_memcpy_16_16: + SRL t0, len, 6 # 64B per iteration + beqz t0, 2f + and rem, len, 0x3f + .align 4 +1: +EXCQ( gslq, t7, t4, (16 * 0)(src), .Ll_exc) +EXCQ( gslq, t3, t2, (16 * 1)(src), .Ll_exc_copy) +EXCQ( gslq, t1, t0, (16 * 2)(src), .Ll_exc_copy) + ADDI len, -16 * 4 +EXCQ( gssq, t7, t4, (16 * 0)(dst), .Ls_exc_p64) +EXCQ( gslq, t7, t4, (16 * 3)(src), .Ll_exc_copy) + ADDU src, 16 * 4 + ADDU dst, 16 * 4 +EXCQ( gssq, t3, t2, (-16 * 3)(dst), .Ls_exc_p48) +EXCQ( gssq, t1, t0, (-16 * 2)(dst), .Ls_exc_p32) +EXCQ( gssq, t7, t4, (-16 * 1)(dst), .Ls_exc_p16) + bne len, rem, 1b + nop + beqz len, .Ldone +2: sltu t0, len, 32 + bnez t0, 3f + and rem, len, 0xf +EXCQ( gslq, t3, t2, (16 * 0)(src), .Ll_exc) +EXCQ( gslq, t1, t0, (16 * 1)(src), .Ll_exc_copy) + ADDI len, -16 * 2 + ADDU src, 32 +EXCQ( gssq, t3, t2, (16 * 0)(dst), .Ls_exc_p32) +EXCQ( gssq, t1, t0, (16 * 1)(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 32 +3: /* copy less than 32B */ + beq rem, len, .L_memcpy_1_15B_8B_aligned + nop +EXCQ( gslq, t3, t2, 0(src), .Ll_exc) + ADDI len, -16 + ADDU src, 16 +EXCQ( gssq, t3, t2, 0(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 16 +/* + * copy 1 - 15B, src & dst are 8B aligned + */ +.L_memcpy_1_15B_8B_aligned: + sltu t0, len, 0x9 + bnez t0, 1f + nop +EXC( ld t1, (src), .Ll_exc) +EXC( sd t1, (dst), .Ls_exc) +1: ADDU src, len + ADDU dst, len +EXC( LDREST t1, -1(src), .Ll_exc_copy_len) +EXC( SDREST t1, -1(dst), .Ls_exc) +.Ldone: + jr ra + move len, zero + +.L_memcpy_16_4_2_1: + SRL t0, len, 5 # 32B per iteration + beqz t0, 2f + and rem, len, 0x1f +1: +EXC( LDFIRST t4, 0(src), .Ll_exc) +EXC( LDFIRST t7, 8(src), .Ll_exc_copy) + ADDI len, -16 * 2 +EXC( LDREST t4, 7(src), .Ll_exc_copy) +EXC( LDREST t7, 15(src), .Ll_exc_copy) +EXC( LDFIRST t2, 16(src), .Ll_exc_copy) +EXC( LDFIRST t3, 24(src), .Ll_exc_copy) +EXC( LDREST t2, 23(src), .Ll_exc_copy) +EXC( LDREST t3, 31(src), .Ll_exc_copy) + ADDU src, 16 * 2 +EXCQ( gssq, t7, t4, (16 * 0)(dst), .Ls_exc_p32) +EXCQ( gssq, t3, t2, (16 * 1)(dst), .Ls_exc_p16) + bne len, rem, 1b + ADDU dst, 16 * 2 + beqz len, .Ldone +2: and rem, len, 0xf + beq rem, len, .L_memcpy_less + nop +EXC( LDFIRST t0, 0(src), .Ll_exc) +EXC( LDFIRST t1, 8(src), .Ll_exc_copy) + ADDI len, -16 +EXC( LDREST t0, 7(src), .Ll_exc_copy) +EXC( LDREST t1, 15(src), .Ll_exc_copy) + ADDU src, 16 +EXCQ( gssq, t1, t0, 0(dst), .Ls_exc_p16) + beqz len, .Ldone + ADDU dst, 16 + +.L_memcpy_less: + andi t0, len, 0x7 + beq t0, len, 2f + andi t4, len, 0x3 + + .set reorder + SUBU t1, len, t0 + ADDU dst, t1 + ADDU src, t1 + .set at=t2 + PTR_LA t3, 1f + .set noat + SLL t2, t1, 0x1 /* 4 * 4B instructions move 8B data*/ + SUBU t3, t2 + jr t3 + .set noreorder + +EXC( LDFIRST t2, (-8 * 6)(src), .Ll_exc_a48) +EXC( LDREST t2, (-8 * 6 + 7)(src), .Ll_exc_copy_a48) +EXC( SDFIRST t2, (-8 * 6)(dst), .Ls_exc) +EXC( SDREST t2, (-8 * 6 + 7)(dst), .Ls_exc_a8) + +EXC( LDFIRST t3, (-8 * 5)(src), .Ll_exc_a40) +EXC( LDREST t3, (-8 * 5 + 7)(src), .Ll_exc_copy_a40) +EXC( SDFIRST t3, (-8 * 5)(dst), .Ls_exc_a8) +EXC( SDREST t3, (-8 * 5 + 7)(dst), .Ls_exc_a16) + +EXC( LDFIRST t1, (-8 * 4)(src), .Ll_exc_a32) +EXC( LDREST t1, (-8 * 4 + 7)(src), .Ll_exc_copy_a32) +EXC( SDFIRST t1, (-8 * 4)(dst), .Ls_exc_a16) +EXC( SDREST t1, (-8 * 4 + 7)(dst), .Ls_exc_a24) + +EXC( LDFIRST t2, (-8 * 3)(src), .Ll_exc_a24) +EXC( LDREST t2, (-8 * 3 + 7)(src), .Ll_exc_copy_a24) +EXC( SDFIRST t2, (-8 * 3)(dst), .Ls_exc_a24) +EXC( SDREST t2, (-8 * 3 + 7)(dst), .Ls_exc_a32) + +EXC( LDFIRST t3, (-8 * 2)(src), .Ll_exc_a16) +EXC( LDREST t3, (-8 * 2 + 7)(src), .Ll_exc_copy_a16) +EXC( SDFIRST t3, (-8 * 2)(dst), .Ls_exc_a32) +EXC( SDREST t3, (-8 * 2 + 7)(dst), .Ls_exc_a40) + +EXC( LDFIRST t1, (-8 * 1)(src), .Ll_exc_a8) +EXC( LDREST t1, (-8 * 1 + 7)(src), .Ll_exc_copy_a8) +EXC( SDFIRST t1, (-8 * 1)(dst), .Ls_exc_a40) +EXC( SDREST t1, (-8 * 1 + 7)(dst), .Ls_exc_a48) +1: beqz t0, .Ldone + ADDU src, t0 + ADDU dst, t0 +EXC( LDFIRST t2, -8(src), .Ll_exc_a8) +EXC( LDREST t2, -1(src), .Ll_exc_copy_a8) +EXC( SDFIRST t2, -8(dst), .Ls_exc) +EXC( SDREST t2, -1(dst), .Ls_exc) + jr ra + move len, zero + +2: + beq t4, len, 3f + nop +EXC( LWFIRST t2, (src), .Ll_exc) +EXC( LWREST t2, 3(src), .Ll_exc_copy) + ADDU src, len +EXC( SWFIRST t2, (dst), .Ls_exc) +EXC( SWREST t2, 3(dst), .Ls_exc) + beqz t4, .Ldone + ADDU dst, len + +EXC( LWFIRST t1, -4(src), .Ll_exc_a4) +EXC( LWREST t1, -1(src), .Ll_exc_copy_a4) +EXC( SWFIRST t1, -4(dst), .Ls_exc) +EXC( SWREST t1, -1(dst), .Ls_exc) + jr ra + move len, zero + +3: + beqz len, .Ldone + ADDU t0, src, len +1: +EXC( lb t2, (src), .Ll_exc) + ADDU src, 1 +EXC( sb t2, (dst), .Ls_exc_p1) + bne t0, src, 1b + ADDU dst, 1 + + jr ra + move len, zero + + END(memcpy) + +#ifdef LINUX_KERNEL + +#define LEXC_a(n) \ +.Ll_exc_copy_a ## n: \ + ADDI src, -n; \ + b .Ll_exc_copy; \ + ADDI dst, -n; \ +.Ll_exc_a ## n: \ + ADDI src, -n; \ + b .Ll_exc; \ + ADDI dst, -n; + +.Ll_exc_copy_len: + SUBU src, len + b .Ll_exc_copy + SUBU dst, len + +LEXC_a(4) +LEXC_a(8) +LEXC_a(16) +LEXC_a(24) +LEXC_a(32) +LEXC_a(40) +LEXC_a(48) + +.Ll_exc_copy: + /* + * Copy bytes from src until faulting load address (or until a + * lb faults) + * + * When reached by a faulting LDFIRST/LDREST, THREAD_BUADDR($28) + * may be more than a byte beyond the last address. + * Hence, the lb below may get an exception. + * + * Assumes src < THREAD_BUADDR($28) + */ + LOAD t0, TI_TASK($28) + nop + LOAD t0, THREAD_BUADDR(t0) +1: +EXC( lb t1, 0(src), .Ll_exc) + ADDU src, 1 + sb t1, 0(dst) # can't fault -- we're copy_from_user + bne src, t0, 1b + ADDU dst, 1 +.Ll_exc: + LOAD t0, TI_TASK($28) + nop + LOAD t0, THREAD_BUADDR(t0) # t0 is just past last good address + nop + SUBU len, AT, t0 # len number of uncopied bytes + jr ra + nop + +#define SEXC_p(n) \ +.Ls_exc_p ## n: \ + jr ra; \ + ADDU len, n; + +#define SEXC_a(n) \ +.Ls_exc_a ## n: \ + jr ra; \ + ADDI len, -n; + +SEXC_p(1) +SEXC_p(8) +SEXC_p(16) +SEXC_p(32) +SEXC_p(48) +SEXC_p(64) +SEXC_a(48) +SEXC_a(40) +SEXC_a(32) +SEXC_a(24) +SEXC_a(16) +SEXC_a(8) + +.Ls_exc: + jr ra + nop + + .align 5 +LEAF(memmove) +EXPORT_SYMBOL(memmove) + ADDU t0, a0, a2 + ADDU t1, a1, a2 + sltu t0, a1, t0 # dst + len <= src -> memcpy + sltu t1, a0, t1 # dst >= src + len -> memcpy + and t0, t1 + beqz t0, .L__memcpy + move v0, a0 /* return value */ + beqz a2, .Lr_out + END(memmove) + + /* fall through to __rmemcpy */ +LEAF(__rmemcpy) /* a0=dst a1=src a2=len */ + sltu t0, a1, a0 + beqz t0, .Lr_end_bytes_up # src >= dst + nop + ADDU a0, a2 # dst = dst + len + ADDU a1, a2 # src = src + len + +.Lr_end_bytes: + lb t0, -1(a1) + ADDI a2, -0x1 + sb t0, -1(a0) + ADDI a1, -0x1 + bnez a2, .Lr_end_bytes + ADDI a0, -0x1 + +.Lr_out: + jr ra + move a2, zero + +.Lr_end_bytes_up: + lb t0, (a1) + ADDI a2, -0x1 + sb t0, (a0) + ADDU a1, 0x1 + bnez a2, .Lr_end_bytes_up + ADDU a0, 0x1 + + jr ra + move a2, zero + END(__rmemcpy) +#endif From 5d8996294eb7d4c1104c539e865b5978123e4457 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:03:01 +0800 Subject: [PATCH 110/130] AOSCOS: MIPS: loongson3-memcpy: use PTR_WD to fix build Per commit fa62f39dc7e2 ("MIPS: Fix build error due to PTR used in more places"), `PTR' was renamed to `PTR_WD' to avoid definition conflicts. Follow this commit to fix build. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/loongson3-memcpy.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S index 28b785363351c..4c0b6b8474f04 100644 --- a/arch/mips/loongson64/loongson3-memcpy.S +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -37,13 +37,13 @@ #define EXC(inst_reg,addr,handler) \ 9: inst_reg, addr; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous #define EXCQ(inst,reg1,reg2,addr,handler) \ 9: inst reg1, reg2, addr; \ .section __ex_table,"a"; \ - PTR 9b, handler; \ + PTR_WD 9b, handler; \ .previous #endif From c70dd5bb8edc89b395e43094e165d0be7fac321b Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:04:20 +0800 Subject: [PATCH 111/130] AOSCOS: MIPS: loongson3-memcpy: replace with Per commit 9259e15b3f27 ("mips: replace #include with for . Follow the commit and revise loongson3-memcpy.S accordingly. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/loongson3-memcpy.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S index 4c0b6b8474f04..2021e75d5dd2e 100644 --- a/arch/mips/loongson64/loongson3-memcpy.S +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -29,9 +29,9 @@ #else /* LINUX_KERNEL */ +#include #include #include -#include #include #define EXC(inst_reg,addr,handler) \ From 94458bb27d96efb2fe8992817769195f09100cc6 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:09:34 +0800 Subject: [PATCH 112/130] AOSCOS: MIPS: mark CPU_LOONGSON64 as HAVE_PLAT_MEMCPY loongson3-memcpy is a platform-specific memcpy implementation, add HAVE_PLAT_MEMCPY under CPU_LOONGSON64. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index bbad673d533cf..0307b74cfe427 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -1295,6 +1295,7 @@ config CPU_LOONGSON64 select GPIOLIB select SWIOTLB select HAVE_KVM + select HAVE_PLAT_MEMCPY help The Loongson GSx64(GS264/GS464/GS464E/GS464V) series of processor cores implements the MIPS64R2 instruction set with many extensions, From 58401b13a6eeba1fd263bbe611fdde3bc8708fe3 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:10:57 +0800 Subject: [PATCH 113/130] AOSCOS: MIPS: loongson3-memcpy: adapt to RAW_COPY_USER Since commit 2260ea86c0e7 ("mips: switch to RAW_COPY_USER"), it is required to export `__raw_copy_from_user' and `__raw_copy_from_user', which are then referenced by certain kernel modules. Export these symbols under `__memcpy', also remove the export for the now- unused `__copy_user' symbol. Fixes: "AOSCOS: memcpy optimization for loongson-3" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/loongson64/loongson3-memcpy.S | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/loongson3-memcpy.S b/arch/mips/loongson64/loongson3-memcpy.S index 2021e75d5dd2e..c5dd2e2866c3c 100644 --- a/arch/mips/loongson64/loongson3-memcpy.S +++ b/arch/mips/loongson64/loongson3-memcpy.S @@ -119,8 +119,10 @@ LEAF(memcpy) /* a0=dst a1=src a2=len */ EXPORT_SYMBOL(memcpy) move v0, dst .L__memcpy: -FEXPORT(__copy_user) -EXPORT_SYMBOL(__copy_user) +FEXPORT(__raw_copy_from_user) +EXPORT_SYMBOL(__raw_copy_from_user) +FEXPORT(__raw_copy_to_user) +EXPORT_SYMBOL(__raw_copy_to_user) /* if less then 0x28 bytes */ sltu t2, a2, 0x28 andi t0, dst, 0xf From 3811a644690e51e30a1f27b721424f865d313455 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 14:45:22 +0800 Subject: [PATCH 114/130] AOSCOS: spi: spi-loongson: allow building on MACH_LOONGSON32/64 LS2K/LS7A chipsets are also found on some MIPS-based Loongson hardware, allow building for 32-bit (2K) and 64-bit (3A/B) hardware. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/spi/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 170c4c3cc7cb7..85250eb86b24b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -525,12 +525,12 @@ config SPI_LM70_LLP config SPI_LOONGSON_CORE tristate - depends on LOONGARCH || COMPILE_TEST + depends on LOONGARCH || MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST config SPI_LOONGSON_PCI tristate "Loongson SPI Controller PCI Driver Support" select SPI_LOONGSON_CORE - depends on PCI && (LOONGARCH || COMPILE_TEST) + depends on PCI && (LOONGARCH || MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST) help This bus driver supports the Loongson SPI hardware controller in the Loongson platforms and supports to use PCI framework to @@ -541,7 +541,7 @@ config SPI_LOONGSON_PCI config SPI_LOONGSON_PLATFORM tristate "Loongson SPI Controller Platform Driver Support" select SPI_LOONGSON_CORE - depends on OF && (LOONGARCH || COMPILE_TEST) + depends on OF && (LOONGARCH || MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST) help This bus driver supports the Loongson SPI hardware controller in the Loongson platforms and supports to use DTS framework to From bb076f64964a8b7a9e5d2767dd022f14c2fd8627 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 16:05:51 +0800 Subject: [PATCH 115/130] AOSCOS: MIPS: select ARCH_FORCE_MAX_ORDER >= 11 if NUMA_BALANCING is selected This should suppress an assertion failure during build. Further investigation should be conducted when possible. Fixes: "BACKPORT: FROMLIST: mips/mm: Add NUMA balancing support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 0307b74cfe427..c19befe38588e 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -2118,9 +2118,9 @@ endchoice config ARCH_FORCE_MAX_ORDER int "Maximum zone order" - default "13" if MIPS_HUGE_TLB_SUPPORT && PAGE_SIZE_64KB - default "12" if MIPS_HUGE_TLB_SUPPORT && PAGE_SIZE_32KB - default "11" if MIPS_HUGE_TLB_SUPPORT && PAGE_SIZE_16KB + default "13" if (NUMA_BALANCING || MIPS_HUGE_TLB_SUPPORT) && PAGE_SIZE_64KB + default "12" if (NUMA_BALANCING || MIPS_HUGE_TLB_SUPPORT) && PAGE_SIZE_32KB + default "11" if (NUMA_BALANCING || MIPS_HUGE_TLB_SUPPORT) && PAGE_SIZE_16KB default "10" help The kernel memory allocator divides physically contiguous memory From dec763a69aedec7f7a4622a8650da2525f2c7a3f Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Thu, 12 Dec 2024 16:07:57 +0800 Subject: [PATCH 116/130] AOSCOS: init: make NUMA_BALANCING depend on TRANSPARENT_HUGEPAGE if MIPS This should suppress an assertion failure during build. Further investigation should be conducted when possible. Fixes: "BACKPORT: FROMLIST: mips/mm: Add NUMA balancing support" Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- init/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/init/Kconfig b/init/Kconfig index 578b0a68a1042..50798796b6163 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -911,6 +911,7 @@ config NUMA_BALANCING depends on ARCH_SUPPORTS_NUMA_BALANCING depends on !ARCH_WANT_NUMA_VARIABLE_LOCALITY depends on SMP && NUMA && MIGRATION && !PREEMPT_RT + depends on TRANSPARENT_HUGEPAGE || !MIPS help This option adds support for automatic NUMA aware memory/task placement. The mechanism is quite primitive and is based on migrating memory when From 8059993ce4818f2a209e7e7011f4c6db6d403068 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sat, 23 Jun 2018 14:14:24 +0800 Subject: [PATCH 117/130] AOSCOS: parport: Add support for the WCH384 4S/1P multi-IO card WCH384 4S/1P is a PCI-E card with 1 LPT and 4 DB9 COM ports detected as Serial controller: Device 1c00:3450 (rev 10) (prog-if 05 [16850]) [Mingcong Bai: Resolved minor merge conflicts in drivers/tty/serial/8250/8250_pci.c.] Signed-off-by: Huacai Chen Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- drivers/parport/parport_serial.c | 10 ++++++++++ drivers/tty/serial/8250/8250_pci.c | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c index 3644997a83425..d09d328196090 100644 --- a/drivers/parport/parport_serial.c +++ b/drivers/parport/parport_serial.c @@ -60,6 +60,7 @@ enum parport_pc_pci_cards { wch_ch353_2s1p, wch_ch382_0s1p, wch_ch382_2s1p, + wch_ch384_4s1p, brainboxes_5s1p, sunix_4008a, sunix_5069a, @@ -157,6 +158,7 @@ static struct parport_pc_pci cards[] = { /* wch_ch353_2s1p*/ { 1, { { 2, -1}, } }, /* wch_ch382_0s1p*/ { 1, { { 2, -1}, } }, /* wch_ch382_2s1p*/ { 1, { { 2, -1}, } }, + /* wch_ch384_4s1p*/ { 1, { { 2, -1}, } }, /* brainboxes_5s1p */ { 1, { { 3, -1 }, } }, /* sunix_4008a */ { 1, { { 1, 2 }, } }, /* sunix_5069a */ { 1, { { 1, 2 }, } }, @@ -270,6 +272,7 @@ static struct pci_device_id parport_serial_pci_tbl[] = { { 0x4348, 0x7053, 0x4348, 0x3253, 0, 0, wch_ch353_2s1p}, { 0x1c00, 0x3050, 0x1c00, 0x3050, 0, 0, wch_ch382_0s1p}, { 0x1c00, 0x3250, 0x1c00, 0x3250, 0, 0, wch_ch382_2s1p}, + { 0x1c00, 0x3450, 0x1c00, 0x3450, 0, 0, wch_ch384_4s1p}, /* BrainBoxes PX272/PX306 MIO card */ { PCI_VENDOR_ID_INTASHIELD, 0x4100, @@ -558,6 +561,13 @@ static struct pciserial_board pci_parport_serial_boards[] = { .uart_offset = 8, .first_offset = 0xC0, }, + [wch_ch384_4s1p] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + .first_offset = 0xC0, + }, [brainboxes_5s1p] = { .flags = FL_BASE2, .num_ports = 5, diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index e648ce7d8eaa5..94decedf572fa 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1961,6 +1961,7 @@ pci_moxa_setup(struct serial_private *priv, #define PCIE_VENDOR_ID_WCH 0x1c00 #define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250 #define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470 +#define PCIE_DEVICE_ID_WCH_CH384_4S1P 0x3450 #define PCIE_DEVICE_ID_WCH_CH384_8S 0x3853 #define PCIE_DEVICE_ID_WCH_CH382_2S 0x3253 @@ -2753,6 +2754,14 @@ static struct pci_serial_quirk pci_serial_quirks[] = { .subdevice = PCI_ANY_ID, .init = pci_wch_ch38x_init, .exit = pci_wch_ch38x_exit, + .setup = pci_wch_ch38x_setup, + }, + /* WCH CH384 4S1P card (16850 clone) */ + { + .vendor = PCIE_VENDOR_ID_WCH, + .device = PCIE_DEVICE_ID_WCH_CH384_4S1P, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, .setup = pci_wch_ch38x_setup, }, /* @@ -3829,6 +3838,7 @@ static const struct pci_device_id blacklist[] = { { PCI_DEVICE(0x4348, 0x5053), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), }, /* WCH CH382 2S1P */ { PCI_DEVICE(0x1c00, 0x3250), 0, 0, REPORT_CONFIG(PARPORT_SERIAL), }, + { PCI_DEVICE(0x1c00, 0x3450), }, /* WCH CH384 4S1P */ /* Intel platforms with MID UART */ { PCI_VDEVICE(INTEL, 0x081b), REPORT_8250_CONFIG(MID), }, From 29c911c23468dc58fca98a7af5f51f677bb03170 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 10 Dec 2024 16:36:25 +0800 Subject: [PATCH 118/130] AOSCOS: MIPS: audit: declare function prototypes for audit_classify_syscall_*() Functions `audit_classify_syscall_n32()', `audit_classify_syscall_o32()` were never declared in the previous code and causes build errors due to `-Werror=missing-prototype'. Create function prototype declarations in `audit-{n,o}32.h' and reference them in each of the respective sources. Also remove the extern references as they are no longer needed. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/kernel/audit-n32.c | 1 + arch/mips/kernel/audit-n32.h | 5 +++++ arch/mips/kernel/audit-native.c | 5 ++--- arch/mips/kernel/audit-o32.c | 1 + arch/mips/kernel/audit-o32.h | 5 +++++ 5 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 arch/mips/kernel/audit-n32.h create mode 100644 arch/mips/kernel/audit-o32.h diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c index 2248badc3acb6..2e9ac29588c81 100644 --- a/arch/mips/kernel/audit-n32.c +++ b/arch/mips/kernel/audit-n32.c @@ -4,6 +4,7 @@ #include #include #include +#include "audit-n32.h" static unsigned dir_class_n32[] = { #include diff --git a/arch/mips/kernel/audit-n32.h b/arch/mips/kernel/audit-n32.h new file mode 100644 index 0000000000000..781de2e9ad66b --- /dev/null +++ b/arch/mips/kernel/audit-n32.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef CONFIG_AUDITSYSCALL_N32 +int audit_classify_syscall_n32(int abi, unsigned syscall); +#endif /* CONFIG_AUDITSYSCALL_N32 */ diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c index 09ae3dbcfecbc..88e283f8df385 100644 --- a/arch/mips/kernel/audit-native.c +++ b/arch/mips/kernel/audit-native.c @@ -2,6 +2,8 @@ #include #include #include +#include "audit-n32.h" +#include "audit-o32.h" static unsigned dir_class[] = { #include @@ -37,9 +39,6 @@ int audit_classify_arch(int arch) return 0; } -extern int audit_classify_syscall_o32(int abi, unsigned syscall); -extern int audit_classify_syscall_n32(int abi, unsigned syscall); - int audit_classify_syscall(int abi, unsigned syscall) { int res; diff --git a/arch/mips/kernel/audit-o32.c b/arch/mips/kernel/audit-o32.c index e8b9b50f7d267..6bf302c04e5d3 100644 --- a/arch/mips/kernel/audit-o32.c +++ b/arch/mips/kernel/audit-o32.c @@ -4,6 +4,7 @@ #include #include #include +#include "audit-o32.h" static unsigned dir_class_o32[] = { #include diff --git a/arch/mips/kernel/audit-o32.h b/arch/mips/kernel/audit-o32.h new file mode 100644 index 0000000000000..8a782fdd1c6e7 --- /dev/null +++ b/arch/mips/kernel/audit-o32.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifdef CONFIG_AUDITSYSCALL_O32 +int audit_classify_syscall_o32(int abi, unsigned syscall); +#endif /* CONFIG_AUDITSYSCALL_O32 */ From a52fdd45e55a31c5ca290f187fcb5460a8874475 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 10 Dec 2024 16:39:13 +0800 Subject: [PATCH 119/130] AOSCOS: MIPS: audit: replace magic audit syscall class numbers with macros Follow commit 42f355ef59a2 ("audit: replace magic audit syscall class numbers with macros") and replace all magic numbers in MIPS audit code. Signed-off-by: Mingcong Bai Signed-off-by: Kexy Biscuit --- arch/mips/kernel/audit-n32.c | 6 +++--- arch/mips/kernel/audit-native.c | 8 ++++---- arch/mips/kernel/audit-o32.c | 10 +++++----- include/linux/audit_arch.h | 3 +++ kernel/auditsc.c | 2 +- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c index 2e9ac29588c81..8d114bf854254 100644 --- a/arch/mips/kernel/audit-n32.c +++ b/arch/mips/kernel/audit-n32.c @@ -35,11 +35,11 @@ int audit_classify_syscall_n32(int abi, unsigned syscall) { switch (syscall) { case __NR_open: - return 2; + return AUDITSC_OPEN; case __NR_openat: - return 3; + return AUDITSC_OPENAT; case __NR_execve: - return 5; + return AUDITSC_EXECVE; default: return 0; } diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c index 88e283f8df385..4a0fecd0e4867 100644 --- a/arch/mips/kernel/audit-native.c +++ b/arch/mips/kernel/audit-native.c @@ -45,20 +45,20 @@ int audit_classify_syscall(int abi, unsigned syscall) switch (syscall) { case __NR_open: - res = 2; + res = AUDITSC_OPEN; break; case __NR_openat: - res = 3; + res = AUDITSC_OPEN; break; #ifdef __NR_socketcall /* Only exists on O32 */ case __NR_socketcall: - res = 4; + res = AUDITSC_SOCKETCALL; break; #endif case __NR_execve: - res = 5; + res = AUDITSC_EXECVE; break; default: #ifdef CONFIG_AUDITSYSCALL_O32 diff --git a/arch/mips/kernel/audit-o32.c b/arch/mips/kernel/audit-o32.c index 6bf302c04e5d3..818f91ce14acf 100644 --- a/arch/mips/kernel/audit-o32.c +++ b/arch/mips/kernel/audit-o32.c @@ -35,15 +35,15 @@ int audit_classify_syscall_o32(int abi, unsigned syscall) { switch (syscall) { case __NR_open: - return 2; + return AUDITSC_OPEN; case __NR_openat: - return 3; + return AUDITSC_OPENAT; case __NR_socketcall: - return 4; + return AUDITSC_SOCKETCALL; case __NR_execve: - return 5; + return AUDITSC_EXECVE; default: - return 0; + return AUDITSC_NATIVE; } } diff --git a/include/linux/audit_arch.h b/include/linux/audit_arch.h index 0e34d673ef171..0436757bfc33e 100644 --- a/include/linux/audit_arch.h +++ b/include/linux/audit_arch.h @@ -17,6 +17,9 @@ enum auditsc_class_t { AUDITSC_SOCKETCALL, AUDITSC_EXECVE, AUDITSC_OPENAT2, +#ifdef CONFIG_MIPS + AUDITSC_MIPS_N32, +#endif AUDITSC_NVALS /* count */ }; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 9fa3a07f499a2..ba2b1882a4c94 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -190,7 +190,7 @@ static int audit_match_perm(struct audit_context *ctx, int mask) case AUDITSC_OPENAT2: return mask & ACC_MODE((u32)ctx->openat2.flags); #ifdef CONFIG_MIPS - case 6: /* for N32 */ + case AUDITSC_MIPS_N32: /* for N32 */ if ((mask & AUDIT_PERM_WRITE) && audit_match_class(AUDIT_CLASS_WRITE_N32, n)) return 1; From d64f47dfbffe2ac75823f147959fb95eda889e65 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Tue, 17 Dec 2024 00:11:51 +0800 Subject: [PATCH 120/130] AOSCOS: MIPS: audit: clean up the last remnants of magic numbers Follow commit 42f355ef59a2 ("audit: replace magic audit syscall class numbers with macros") and replace all magic numbers in MIPS audit code. Signed-off-by: Mingcong Bai --- arch/mips/Kconfig | 4 ++++ arch/mips/kernel/audit-n32.c | 2 +- arch/mips/kernel/audit-native.c | 33 +++++++-------------------------- 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index c19befe38588e..4929379af0c5e 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -104,6 +104,10 @@ config MIPS select ARCH_HAS_ELFCORE_COMPAT select HAVE_ARCH_KCSAN if 64BIT select HAVE_ARCH_AUDITSYSCALL + select AUDIT_ARCH + +config AUDIT_ARCH + bool config MIPS_FIXUP_BIGPHYS_ADDR bool diff --git a/arch/mips/kernel/audit-n32.c b/arch/mips/kernel/audit-n32.c index 8d114bf854254..76c95ff924f83 100644 --- a/arch/mips/kernel/audit-n32.c +++ b/arch/mips/kernel/audit-n32.c @@ -41,7 +41,7 @@ int audit_classify_syscall_n32(int abi, unsigned syscall) case __NR_execve: return AUDITSC_EXECVE; default: - return 0; + return AUDITSC_NATIVE; } } diff --git a/arch/mips/kernel/audit-native.c b/arch/mips/kernel/audit-native.c index 4a0fecd0e4867..763f0e6b3636a 100644 --- a/arch/mips/kernel/audit-native.c +++ b/arch/mips/kernel/audit-native.c @@ -41,45 +41,26 @@ int audit_classify_arch(int arch) int audit_classify_syscall(int abi, unsigned syscall) { - int res; - switch (syscall) { case __NR_open: - res = AUDITSC_OPEN; - break; - + return AUDITSC_OPEN; case __NR_openat: - res = AUDITSC_OPEN; - break; - + return AUDITSC_OPEN; #ifdef __NR_socketcall /* Only exists on O32 */ case __NR_socketcall: - res = AUDITSC_SOCKETCALL; - break; + return AUDITSC_SOCKETCALL; #endif case __NR_execve: - res = AUDITSC_EXECVE; - break; + return AUDITSC_EXECVE; default: #ifdef CONFIG_AUDITSYSCALL_O32 - res = audit_classify_syscall_o32(abi, syscall); - if (res) - break; + return audit_classify_syscall_o32(abi, syscall); #endif #ifdef CONFIG_AUDITSYSCALL_N32 - res = audit_classify_syscall_n32(abi, syscall); - if (res) - break; + return audit_classify_syscall_n32(abi, syscall); #endif - if (abi == AUDIT_ARCH_MIPS || abi == AUDIT_ARCH_MIPSEL) - res = 1; - else if (abi == AUDIT_ARCH_MIPS64 || abi == AUDIT_ARCH_MIPSEL64) - res = 0; - else if (abi == AUDIT_ARCH_MIPS64N32 || abi == AUDIT_ARCH_MIPSEL64N32) - res = 6; + return AUDITSC_NATIVE; } - - return res; } static int __init audit_classes_init(void) From 3d9826791b88e6abee19a09be4c97929fac35d7f Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Wed, 12 Jun 2024 09:38:19 +0100 Subject: [PATCH 121/130] BACKPORT: MIPS: Implement ieee754 NAN2008 emulation mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement ieee754 NAN2008 emulation mode. When this mode is enabled, kernel will accept ELF file compiled for both NaN 2008 and NaN legacy, but if hardware does not have capability to match ELF's NaN mode, __own_fpu will fail for corresponding thread and fpuemu will then kick in. This mode trade performance for correctness, while maintaining support for both NaN mode regardless of hardware capability. It is useful for multilib installation that have both types of binary exist in system. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- Documentation/admin-guide/kernel-parameters.txt | 4 +++- arch/mips/include/asm/fpu.h | 15 +++++++++++++++ arch/mips/kernel/elf.c | 4 ++++ arch/mips/kernel/fpu-probe.c | 9 ++++++++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 9ce75836fc73c..dd2b51fd67929 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1933,7 +1933,7 @@ for the device. By default it is set to false (0). ieee754= [MIPS] Select IEEE Std 754 conformance mode - Format: { strict | legacy | 2008 | relaxed } + Format: { strict | legacy | 2008 | relaxed | emulated } Default: strict Choose which programs will be accepted for execution @@ -1953,6 +1953,8 @@ by the FPU relaxed accept any binaries regardless of whether supported by the FPU + emulated accept any binaries but enable FPU emulator + if binary mode is unsupported by the FPU. The FPU emulator is always able to support both NaN encodings, so if no FPU hardware is present or it has diff --git a/arch/mips/include/asm/fpu.h b/arch/mips/include/asm/fpu.h index 86310d6e10352..bc5ac9887d097 100644 --- a/arch/mips/include/asm/fpu.h +++ b/arch/mips/include/asm/fpu.h @@ -129,6 +129,18 @@ static inline int __own_fpu(void) if (ret) return ret; + if (current->thread.fpu.fcr31 & FPU_CSR_NAN2008) { + if (!cpu_has_nan_2008) { + ret = SIGFPE; + goto failed; + } + } else { + if (!cpu_has_nan_legacy) { + ret = SIGFPE; + goto failed; + } + } + KSTK_STATUS(current) |= ST0_CU1; if (mode == FPU_64BIT || mode == FPU_HYBRID) KSTK_STATUS(current) |= ST0_FR; @@ -137,6 +149,9 @@ static inline int __own_fpu(void) set_thread_flag(TIF_USEDFPU); return 0; +failed: + __disable_fpu(); + return ret; } static inline int own_fpu_inatomic(int restore) diff --git a/arch/mips/kernel/elf.c b/arch/mips/kernel/elf.c index 7aa2c2360ff60..f0e7fe85a42a7 100644 --- a/arch/mips/kernel/elf.c +++ b/arch/mips/kernel/elf.c @@ -318,6 +318,10 @@ void mips_set_personality_nan(struct arch_elf_state *state) t->thread.fpu.fcr31 = c->fpu_csr31; switch (state->nan_2008) { case 0: + if (!(c->fpu_msk31 & FPU_CSR_NAN2008)) + t->thread.fpu.fcr31 &= ~FPU_CSR_NAN2008; + if (!(c->fpu_msk31 & FPU_CSR_ABS2008)) + t->thread.fpu.fcr31 &= ~FPU_CSR_ABS2008; break; case 1: if (!(c->fpu_msk31 & FPU_CSR_NAN2008)) diff --git a/arch/mips/kernel/fpu-probe.c b/arch/mips/kernel/fpu-probe.c index e689d6a832342..6bf3f19b1c335 100644 --- a/arch/mips/kernel/fpu-probe.c +++ b/arch/mips/kernel/fpu-probe.c @@ -144,7 +144,7 @@ static void cpu_set_fpu_2008(struct cpuinfo_mips *c) * IEEE 754 conformance mode to use. Affects the NaN encoding and the * ABS.fmt/NEG.fmt execution mode. */ -static enum { STRICT, LEGACY, STD2008, RELAXED } ieee754 = STRICT; +static enum { STRICT, EMULATED, LEGACY, STD2008, RELAXED } ieee754 = STRICT; /* * Set the IEEE 754 NaN encodings and the ABS.fmt/NEG.fmt execution modes @@ -160,6 +160,7 @@ static void cpu_set_nofpu_2008(struct cpuinfo_mips *c) switch (ieee754) { case STRICT: + case EMULATED: if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 | MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | MIPS_CPU_ISA_M32R5 | MIPS_CPU_ISA_M64R5 | @@ -204,6 +205,10 @@ static void cpu_set_nan_2008(struct cpuinfo_mips *c) mips_use_nan_legacy = !cpu_has_nan_2008; mips_use_nan_2008 = !!cpu_has_nan_2008; break; + case EMULATED: + /* Pretend ABS2008/NAN2008 options are dynamic */ + c->fpu_msk31 &= ~(FPU_CSR_NAN2008 | FPU_CSR_ABS2008); + fallthrough; case RELAXED: mips_use_nan_legacy = true; mips_use_nan_2008 = true; @@ -226,6 +231,8 @@ static int __init ieee754_setup(char *s) return -1; else if (!strcmp(s, "strict")) ieee754 = STRICT; + else if (!strcmp(s, "emulated")) + ieee754 = EMULATED; else if (!strcmp(s, "legacy")) ieee754 = LEGACY; else if (!strcmp(s, "2008")) From 74dccc5d1546805f7db5d27a18352d2b5fb1a3b4 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Tue, 7 May 2024 16:22:01 +0100 Subject: [PATCH 122/130] MIPS: Loongson64: Implement PM suspend for LEFI firmware Implement PM suspend for LEFI firmware. Entering STR (Suspend to RAM) is as simple as save our context then go to a firmware vector. Wake is a little bit treaky as we need to setup some CP0 status first, which can be done with smp_slave_setup. Signed-off-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/loongson64/Makefile | 2 +- arch/mips/loongson64/pm.c | 88 +++++++--------------------------- arch/mips/loongson64/sleeper.S | 17 +++++++ 3 files changed, 36 insertions(+), 71 deletions(-) create mode 100644 arch/mips/loongson64/sleeper.S diff --git a/arch/mips/loongson64/Makefile b/arch/mips/loongson64/Makefile index 2155e9532901d..402cc006a5bfc 100644 --- a/arch/mips/loongson64/Makefile +++ b/arch/mips/loongson64/Makefile @@ -11,7 +11,7 @@ obj-$(CONFIG_MACH_LOONGSON64) += cop2-ex.o dma.o \ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_NUMA) += numa.o obj-$(CONFIG_RS780_HPET) += hpet.o -obj-$(CONFIG_SUSPEND) += pm.o +obj-$(CONFIG_SUSPEND) += pm.o sleeper.o obj-$(CONFIG_PCI_QUIRKS) += vbios_quirk.o obj-$(CONFIG_CPU_LOONGSON3_CPUCFG_EMULATION) += cpucfg-emul.o obj-$(CONFIG_SYSFS) += boardinfo.o diff --git a/arch/mips/loongson64/pm.c b/arch/mips/loongson64/pm.c index 7c8556f097812..5f0604af8f136 100644 --- a/arch/mips/loongson64/pm.c +++ b/arch/mips/loongson64/pm.c @@ -6,98 +6,46 @@ * Author: Wu Zhangjin */ #include -#include #include -#include #include #include -static unsigned int __maybe_unused cached_master_mask; /* i8259A */ -static unsigned int __maybe_unused cached_slave_mask; -static unsigned int __maybe_unused cached_bonito_irq_mask; /* bonito */ +asmlinkage void loongson_lefi_sleep(unsigned long sleep_addr); -void arch_suspend_disable_irqs(void) +static int lefi_pm_enter(suspend_state_t state) { - /* disable all mips events */ - local_irq_disable(); - -#ifdef CONFIG_I8259 - /* disable all events of i8259A */ - cached_slave_mask = inb(PIC_SLAVE_IMR); - cached_master_mask = inb(PIC_MASTER_IMR); - - outb(0xff, PIC_SLAVE_IMR); - inb(PIC_SLAVE_IMR); - outb(0xff, PIC_MASTER_IMR); - inb(PIC_MASTER_IMR); -#endif - /* disable all events of bonito */ - cached_bonito_irq_mask = LOONGSON_INTEN; - LOONGSON_INTENCLR = 0xffff; - (void)LOONGSON_INTENCLR; -} - -void arch_suspend_enable_irqs(void) -{ - /* enable all mips events */ - local_irq_enable(); -#ifdef CONFIG_I8259 - /* only enable the cached events of i8259A */ - outb(cached_slave_mask, PIC_SLAVE_IMR); - outb(cached_master_mask, PIC_MASTER_IMR); -#endif - /* enable all cached events of bonito */ - LOONGSON_INTENSET = cached_bonito_irq_mask; - (void)LOONGSON_INTENSET; -} - -/* - * Setup the board-specific events for waking up loongson from wait mode - */ -void __weak setup_wakeup_events(void) -{ -} - -void __weak mach_suspend(void) -{ -} - -void __weak mach_resume(void) -{ -} - -static int loongson_pm_enter(suspend_state_t state) -{ - mach_suspend(); - - mach_resume(); - - return 0; + switch (state) { + case PM_SUSPEND_MEM: + pm_set_suspend_via_firmware(); + loongson_lefi_sleep(loongson_sysconf.suspend_addr); + pm_set_resume_via_firmware(); + return 0; + default: + return -EINVAL; + } } -static int loongson_pm_valid_state(suspend_state_t state) +static int lefi_pm_valid_state(suspend_state_t state) { switch (state) { - case PM_SUSPEND_ON: - case PM_SUSPEND_STANDBY: case PM_SUSPEND_MEM: - return 1; - + return !!loongson_sysconf.suspend_addr; default: return 0; } } -static const struct platform_suspend_ops loongson_pm_ops = { - .valid = loongson_pm_valid_state, - .enter = loongson_pm_enter, +static const struct platform_suspend_ops lefi_pm_ops = { + .valid = lefi_pm_valid_state, + .enter = lefi_pm_enter, }; static int __init loongson_pm_init(void) { - suspend_set_ops(&loongson_pm_ops); + if (loongson_sysconf.fw_interface == LOONGSON_LEFI) + suspend_set_ops(&lefi_pm_ops); return 0; } diff --git a/arch/mips/loongson64/sleeper.S b/arch/mips/loongson64/sleeper.S new file mode 100644 index 0000000000000..04874b9bf430b --- /dev/null +++ b/arch/mips/loongson64/sleeper.S @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024, Jiaxun Yang + * Loongson EFI firmware sleeper routine + */ + +#include +#include + +#include + +LEAF(loongson_lefi_sleep) + SUSPEND_SAVE + jalr a0 + smp_slave_setup + RESUME_RESTORE_REGS_RETURN +END(loongson_lefi_sleep) From fae1747a0e1106a553f0f259fffe67283b1c11fc Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Fri, 14 Jun 2024 16:40:17 +0100 Subject: [PATCH 123/130] MIPS: Loongson64: sleeper: Pass ra and sp as arguments Some firmware implementations require restoring ra and sp to be passed as arguments. Passing them as necessary. Fixes: 68557c59a550 ("MIPS: Loongson64: Implement PM suspend for LEFI firmware") Signed-off-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/loongson64/sleeper.S | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/mips/loongson64/sleeper.S b/arch/mips/loongson64/sleeper.S index 04874b9bf430b..cf16877409e2f 100644 --- a/arch/mips/loongson64/sleeper.S +++ b/arch/mips/loongson64/sleeper.S @@ -11,7 +11,11 @@ LEAF(loongson_lefi_sleep) SUSPEND_SAVE - jalr a0 - smp_slave_setup + move t9, a0 + PTR_LA a0, wake + move a1, sp + jalr t9 +wake: + smp_slave_setup RESUME_RESTORE_REGS_RETURN END(loongson_lefi_sleep) From 9acb4f0b59a3d380830f83fe831ac18210beb65e Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Tue, 7 May 2024 16:21:59 +0100 Subject: [PATCH 124/130] MIPS: asm/pm.h: Use platform agnostic macros Use platform agnostic macros so it can be compiled on all MIPS platforms. Signed-off-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/pm.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/mips/include/asm/pm.h b/arch/mips/include/asm/pm.h index 10bb7b6407383..7ecd4dfe38461 100644 --- a/arch/mips/include/asm/pm.h +++ b/arch/mips/include/asm/pm.h @@ -17,7 +17,7 @@ /* Save CPU state to stack for suspend to RAM */ .macro SUSPEND_SAVE_REGS - subu sp, PT_SIZE + PTR_SUBU sp, PT_SIZE /* Call preserved GPRs */ LONG_S $16, PT_R16(sp) LONG_S $17, PT_R17(sp) @@ -56,13 +56,13 @@ LONG_L $31, PT_R31(sp) /* Pop and return */ jr ra - addiu sp, PT_SIZE + PTR_ADDIU sp, PT_SIZE .set pop .endm /* Get address of static suspend state into t1 */ .macro LA_STATIC_SUSPEND - la t1, mips_static_suspend_state + PTR_LA t1, mips_static_suspend_state .endm /* Save important CPU state for early restoration to global data */ @@ -72,11 +72,11 @@ * Segment configuration is saved in global data where it can be easily * reloaded without depending on the segment configuration. */ - mfc0 k0, CP0_PAGEMASK, 2 /* SegCtl0 */ + mfc0 k0, CP0_SEGCTL0 LONG_S k0, SSS_SEGCTL0(t1) - mfc0 k0, CP0_PAGEMASK, 3 /* SegCtl1 */ + mfc0 k0, CP0_SEGCTL1 LONG_S k0, SSS_SEGCTL1(t1) - mfc0 k0, CP0_PAGEMASK, 4 /* SegCtl2 */ + mfc0 k0, CP0_SEGCTL2 LONG_S k0, SSS_SEGCTL2(t1) #endif /* save stack pointer (pointing to GPRs) */ @@ -92,11 +92,11 @@ * segments. */ LONG_L k0, SSS_SEGCTL0(t1) - mtc0 k0, CP0_PAGEMASK, 2 /* SegCtl0 */ + mtc0 k0, CP0_SEGCTL0 LONG_L k0, SSS_SEGCTL1(t1) - mtc0 k0, CP0_PAGEMASK, 3 /* SegCtl1 */ + mtc0 k0, CP0_SEGCTL1 LONG_L k0, SSS_SEGCTL2(t1) - mtc0 k0, CP0_PAGEMASK, 4 /* SegCtl2 */ + mtc0 k0, CP0_SEGCTL2 tlbw_use_hazard #endif /* restore stack pointer (pointing to GPRs) */ @@ -105,10 +105,10 @@ /* flush caches to make sure context has reached memory */ .macro SUSPEND_CACHE_FLUSH - .extern __wback_cache_all + .extern __flush_cache_all .set push .set noreorder - la t1, __wback_cache_all + PTR_LA t1, __flush_cache_all LONG_L t0, 0(t1) jalr t0 nop From 8372e76dddba07710e249789c06fee69a8b440ed Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Tue, 7 May 2024 16:22:00 +0100 Subject: [PATCH 125/130] MIPS: select CPU_PM with SUSPEND Functionalities provided by CPU_PM are essential for suspend to work on all platforms. Signed-off-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 4929379af0c5e..84b673e713c05 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -30,7 +30,7 @@ config MIPS select BUILDTIME_TABLE_SORT select CLONE_BACKWARDS select CPU_NO_EFFICIENT_FFS if (TARGET_ISA_REV < 1) - select CPU_PM if CPU_IDLE + select CPU_PM if CPU_IDLE || SUSPEND select GENERIC_ATOMIC64 if !64BIT select GENERIC_CMOS_UPDATE select GENERIC_CPU_AUTOPROBE From 5fcb7e941fbecdb1716f34a13f8cf60cf5c73428 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 23 Dec 2024 15:31:01 +0800 Subject: [PATCH 126/130] DEEPIN: pci: limit CFG0/1 mode detection to CONFIG_LOONGARCH Commit 620c10b247c7 ("PCI: Check if the pci controller can use both CFG0 and CFG1 mode to access configuration space") broke PCIe resource probing on Loongson 3A4000. Limit the CFG0 mode detection to CONFIG_LOONGARCH to make sure that MIPS platforms are not affected. Fixes: 620c10b247c7 ("PCI: Check if the pci controller can use both CFG0 and CFG1 mode to access configuration space") Signed-off-by: Mingcong Bai --- drivers/pci/controller/pci-loongson.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/controller/pci-loongson.c b/drivers/pci/controller/pci-loongson.c index d5495c59a6929..af97aee9bd101 100644 --- a/drivers/pci/controller/pci-loongson.c +++ b/drivers/pci/controller/pci-loongson.c @@ -484,7 +484,9 @@ static int loongson_pci_probe(struct platform_device *pdev) } if (priv->data->flags & FLAG_CFG1) { +#ifdef CONFIG_LOONGARCH if (priv->cfg0_base) +#endif num = 1; regs = platform_get_resource(pdev, IORESOURCE_MEM, num); if (!regs) From e12ff80907f27c416f8f69c0569202462bf48c8a Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Mon, 23 Dec 2024 15:38:20 +0800 Subject: [PATCH 127/130] DEEPIN: mips: add deepin_loongson3_desktop_defconfig This will serve as the default kernel configuration for MIPS-based Loongson 3 targets. Signed-off-by: Mingcong Bai --- .../deepin_loongson3_desktop_defconfig | 406 ++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 arch/mips/configs/deepin_loongson3_desktop_defconfig diff --git a/arch/mips/configs/deepin_loongson3_desktop_defconfig b/arch/mips/configs/deepin_loongson3_desktop_defconfig new file mode 100644 index 0000000000000..7ea3575c96ca6 --- /dev/null +++ b/arch/mips/configs/deepin_loongson3_desktop_defconfig @@ -0,0 +1,406 @@ +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_BUILD_SALT="-loongson3-desktop-hwe" +CONFIG_KERNEL_ZSTD=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y +CONFIG_BPF_JIT=y +CONFIG_PREEMPT=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_MEMCG=y +CONFIG_BLK_CGROUP=y +CONFIG_CGROUP_PIDS=y +CONFIG_CGROUP_FREEZER=y +CONFIG_CPUSETS=y +CONFIG_CGROUP_DEVICE=y +CONFIG_CGROUP_CPUACCT=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_SCHED_AUTOGROUP=y +CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_EXPERT=y +CONFIG_PERF_EVENTS=y +CONFIG_KEXEC=y +CONFIG_MACH_LOONGSON64=y +CONFIG_ARCH_FORCE_MAX_ORDER=11 +CONFIG_CPU_HAS_MSA=y +CONFIG_NUMA=y +CONFIG_NR_CPUS=16 +CONFIG_HZ_256=y +CONFIG_MIPS32_O32=y +CONFIG_MIPS32_N32=y +CONFIG_VIRTUALIZATION=y +CONFIG_KVM=m +CONFIG_KPROBES=y +CONFIG_MODULES=y +CONFIG_MODULE_FORCE_LOAD=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_MQ_IOSCHED_DEADLINE=m +CONFIG_IOSCHED_BFQ=y +CONFIG_BINFMT_MISC=m +CONFIG_KSM=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_XFRM_USER=y +CONFIG_NET_KEY=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_NETFILTER=y +CONFIG_BRIDGE_NETFILTER=m +CONFIG_NETFILTER_NETLINK_LOG=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_TABLES=m +CONFIG_NFT_CT=m +CONFIG_NFT_NAT=m +CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_IP_SET=m +CONFIG_IP_VS=m +CONFIG_NF_TABLES_IPV4=y +CONFIG_NF_TABLES_ARP=y +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_RPFILTER=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_NAT=m +CONFIG_IP_NF_TARGET_MASQUERADE=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_SECURITY=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_NF_TABLES_IPV6=y +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_RPFILTER=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_IP6_NF_SECURITY=m +CONFIG_IP6_NF_NAT=m +CONFIG_IP6_NF_TARGET_MASQUERADE=m +CONFIG_NF_TABLES_BRIDGE=m +CONFIG_BRIDGE_NF_EBTABLES=m +CONFIG_BRIDGE_EBT_T_FILTER=m +CONFIG_BRIDGE_EBT_T_NAT=m +CONFIG_IP_SCTP=m +CONFIG_L2TP=m +CONFIG_BRIDGE=m +CONFIG_VSOCKETS=m +CONFIG_VIRTIO_VSOCKETS=m +CONFIG_CFG80211=m +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=m +CONFIG_RFKILL=m +CONFIG_RFKILL_INPUT=y +CONFIG_NET_9P=m +CONFIG_NET_9P_VIRTIO=m +CONFIG_PCIEPORTBUS=y +# CONFIG_PCIEASPM is not set +CONFIG_HOTPLUG_PCI=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_MTD=m +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_VIRTIO_BLK=y +CONFIG_BLK_DEV_NVME=y +CONFIG_NVME_MULTIPATH=y +CONFIG_NVME_HWMON=y +CONFIG_RAID_ATTRS=m +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_CHR_DEV_SG=y +CONFIG_CHR_DEV_SCH=m +CONFIG_SCSI_CONSTANTS=y +CONFIG_SCSI_LOGGING=y +CONFIG_SCSI_SPI_ATTRS=m +CONFIG_SCSI_FC_ATTRS=m +CONFIG_ISCSI_TCP=m +CONFIG_MEGARAID_NEWGEN=y +CONFIG_MEGARAID_MM=y +CONFIG_MEGARAID_MAILBOX=y +CONFIG_MEGARAID_LEGACY=y +CONFIG_MEGARAID_SAS=y +CONFIG_SCSI_VIRTIO=y +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_PATA_ATIIXP=y +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +CONFIG_MD_LINEAR=m +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID10=m +CONFIG_MD_RAID456=m +CONFIG_MD_MULTIPATH=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m +CONFIG_TARGET_CORE=m +CONFIG_TCM_IBLOCK=m +CONFIG_TCM_FILEIO=m +CONFIG_TCM_PSCSI=m +CONFIG_LOOPBACK_TARGET=m +CONFIG_ISCSI_TARGET=m +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +CONFIG_TUN=m +CONFIG_VETH=m +CONFIG_VIRTIO_NET=m +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_VENDOR_ADAPTEC is not set +# CONFIG_NET_VENDOR_ALTEON is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_ATHEROS is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CIRRUS is not set +# CONFIG_NET_VENDOR_CISCO is not set +# CONFIG_NET_VENDOR_DEC is not set +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_I825XX is not set +CONFIG_E1000=y +CONFIG_E1000E=y +CONFIG_IGB=y +CONFIG_IXGBE=y +# CONFIG_NET_VENDOR_MARVELL is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MYRI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NVIDIA is not set +# CONFIG_NET_VENDOR_OKI is not set +# CONFIG_NET_VENDOR_QLOGIC is not set +# CONFIG_NET_VENDOR_BROCADE is not set +# CONFIG_NET_VENDOR_RDC is not set +CONFIG_8139CP=m +CONFIG_8139TOO=m +CONFIG_R8169=y +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SILAN is not set +# CONFIG_NET_VENDOR_SIS is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SUN is not set +# CONFIG_NET_VENDOR_TEHUTI is not set +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_TOSHIBA is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_FILTER=y +CONFIG_PPP_MPPE=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPPOE=m +CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_ATH9K=m +CONFIG_HOSTAP=m +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_KEYBOARD_XTKBD=m +CONFIG_MOUSE_PS2_SENTELIC=y +CONFIG_MOUSE_SERIAL=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=m +CONFIG_SERIO_SERPORT=m +CONFIG_SERIO_RAW=m +CONFIG_LEGACY_PTY_COUNT=16 +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=16 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_RSA=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_NONSTANDARD=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_PIIX4=y +CONFIG_GPIO_LOONGSON=y +CONFIG_SENSORS_LM75=m +CONFIG_SENSORS_LM93=m +CONFIG_SENSORS_W83627HF=m +CONFIG_MEDIA_SUPPORT=m +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +CONFIG_DRM=y +CONFIG_DRM_RADEON=m +CONFIG_DRM_AMDGPU=m +CONFIG_DRM_AMDGPU_SI=y +CONFIG_DRM_AMDGPU_CIK=y +CONFIG_DRM_AMDGPU_USERPTR=y +CONFIG_DRM_AMD_ACP=y +CONFIG_DRM_AMD_DC_SI=y +CONFIG_DRM_AST=m +CONFIG_DRM_QXL=y +CONFIG_DRM_VIRTIO_GPU=y +# CONFIG_DRM_ARISE is not set +CONFIG_FB=y +CONFIG_FB_RADEON=y +CONFIG_LCD_PLATFORM=m +# CONFIG_VGA_CONSOLE is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=m +CONFIG_SND_SEQUENCER=m +CONFIG_SND_SEQ_DUMMY=m +# CONFIG_SND_ISA is not set +CONFIG_SND_HDA_INTEL=m +CONFIG_SND_HDA_PATCH_LOADER=y +CONFIG_SND_HDA_CODEC_REALTEK=m +CONFIG_SND_HDA_CODEC_SIGMATEL=m +CONFIG_SND_HDA_CODEC_HDMI=m +CONFIG_SND_HDA_CODEC_CONEXANT=m +# CONFIG_SND_USB is not set +CONFIG_HIDRAW=y +CONFIG_HID_A4TECH=m +CONFIG_HID_SUNPLUS=m +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y +CONFIG_USB=y +CONFIG_USB_MON=y +CONFIG_USB_XHCI_HCD=m +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_OHCI_HCD=y +CONFIG_USB_UHCI_HCD=m +CONFIG_USB_STORAGE=m +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_OPTION=m +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_CMOS=y +CONFIG_RTC_DRV_LOONGSON=y +CONFIG_RTC_DRV_GOLDFISH=y +CONFIG_DMADEVICES=y +CONFIG_VIRTIO_PCI=y +CONFIG_VIRTIO_BALLOON=m +CONFIG_VIRTIO_INPUT=y +CONFIG_VIRTIO_MMIO=y +CONFIG_VHOST_NET=m +CONFIG_VHOST_SCSI=m +CONFIG_VHOST_VSOCK=m +CONFIG_GOLDFISH=y +CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_USERSPACE=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_XFS_FS=y +CONFIG_XFS_POSIX_ACL=y +CONFIG_BTRFS_FS=y +CONFIG_BTRFS_FS_POSIX_ACL=y +CONFIG_QUOTA=y +CONFIG_QFMT_V1=m +CONFIG_QFMT_V2=m +CONFIG_AUTOFS_FS=y +CONFIG_FUSE_FS=m +CONFIG_VIRTIO_FS=m +CONFIG_FSCACHE=m +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=936 +CONFIG_FAT_DEFAULT_IOCHARSET="gb2312" +CONFIG_PROC_KCORE=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=y +CONFIG_CRAMFS=m +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_XATTR=y +CONFIG_NFS_FS=m +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=m +CONFIG_NFSD=m +CONFIG_NFSD_V3_ACL=y +CONFIG_NFSD_V4=y +CONFIG_CIFS=m +CONFIG_9P_FS=m +CONFIG_9P_FSCACHE=y +CONFIG_9P_FS_POSIX_ACL=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_936=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_UTF8=y +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_PATH=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_DEFLATE=m +CONFIG_PRINTK_TIME=y +CONFIG_STRIP_ASM_SYMS=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_FS=y +# CONFIG_SCHED_DEBUG is not set +CONFIG_FUNCTION_TRACER=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ieee754=relaxed" From a3ec9496b156862ffc1ca0b8a857030182e77eb0 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Fri, 2 Feb 2024 18:21:40 +0000 Subject: [PATCH 128/130] MIPS: Probe toolchain support of -msym32 msym32 is not supported by LLVM toolchain. Workaround by probe toolchain support of msym32 for KBUILD_SYM32 feature. Link: https://github.com/ClangBuiltLinux/linux/issues/1544 Signed-off-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/Makefile b/arch/mips/Makefile index f49807e1f19bc..0888074f4dfef 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -299,7 +299,7 @@ drivers-$(CONFIG_PCI) += arch/mips/pci/ ifdef CONFIG_64BIT ifndef KBUILD_SYM32 ifeq ($(shell expr $(load-y) \< 0xffffffff80000000), 0) - KBUILD_SYM32 = y + KBUILD_SYM32 = $(call cc-option-yn, -msym32) endif endif From 36bca459118d734da7a64817ad624e3860a414d6 Mon Sep 17 00:00:00 2001 From: Jiaxun Yang Date: Fri, 2 Feb 2024 18:21:47 +0000 Subject: [PATCH 129/130] MIPS: mipsregs: Set proper ISA level for virt extensions c994a3ec7ecc ("MIPS: set mips32r5 for virt extensions") setted some instructions in virt extensions to ISA level mips32r5. However TLB related vz instructions was leftover, also this shouldn't be done to a R5 or R6 kernel buid. Reorg macros to set ISA level as needed when _ASM_SET_VIRT is called. Signed-off-by: Jiaxun Yang Signed-off-by: Thomas Bogendoerfer --- arch/mips/include/asm/mipsregs.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/arch/mips/include/asm/mipsregs.h b/arch/mips/include/asm/mipsregs.h index 78d9eb7ec4945..879ab0ad156da 100644 --- a/arch/mips/include/asm/mipsregs.h +++ b/arch/mips/include/asm/mipsregs.h @@ -2079,7 +2079,14 @@ do { \ _ASM_INSN_IF_MIPS(0x4200000c) \ _ASM_INSN32_IF_MM(0x0000517c) #else /* !TOOLCHAIN_SUPPORTS_VIRT */ -#define _ASM_SET_VIRT ".set\tvirt\n\t" +#if MIPS_ISA_REV >= 5 +#define _ASM_SET_VIRT_ISA +#elif defined(CONFIG_64BIT) +#define _ASM_SET_VIRT_ISA ".set\tmips64r5\n\t" +#else +#define _ASM_SET_VIRT_ISA ".set\tmips32r5\n\t" +#endif +#define _ASM_SET_VIRT _ASM_SET_VIRT_ISA ".set\tvirt\n\t" #define _ASM_SET_MFGC0 _ASM_SET_VIRT #define _ASM_SET_DMFGC0 _ASM_SET_VIRT #define _ASM_SET_MTGC0 _ASM_SET_VIRT @@ -2100,7 +2107,6 @@ do { \ ({ int __res; \ __asm__ __volatile__( \ ".set\tpush\n\t" \ - ".set\tmips32r5\n\t" \ _ASM_SET_MFGC0 \ "mfgc0\t%0, " #source ", %1\n\t" \ _ASM_UNSET_MFGC0 \ @@ -2114,7 +2120,6 @@ do { \ ({ unsigned long long __res; \ __asm__ __volatile__( \ ".set\tpush\n\t" \ - ".set\tmips64r5\n\t" \ _ASM_SET_DMFGC0 \ "dmfgc0\t%0, " #source ", %1\n\t" \ _ASM_UNSET_DMFGC0 \ @@ -2128,7 +2133,6 @@ do { \ do { \ __asm__ __volatile__( \ ".set\tpush\n\t" \ - ".set\tmips32r5\n\t" \ _ASM_SET_MTGC0 \ "mtgc0\t%z0, " #register ", %1\n\t" \ _ASM_UNSET_MTGC0 \ @@ -2141,7 +2145,6 @@ do { \ do { \ __asm__ __volatile__( \ ".set\tpush\n\t" \ - ".set\tmips64r5\n\t" \ _ASM_SET_DMTGC0 \ "dmtgc0\t%z0, " #register ", %1\n\t" \ _ASM_UNSET_DMTGC0 \ From cfd4a44784b2b8f7dd201ada12ac56204aa2ecd3 Mon Sep 17 00:00:00 2001 From: Mingcong Bai Date: Wed, 25 Dec 2024 15:06:30 +0800 Subject: [PATCH 130/130] DEEPIN: MIPS: loongson64: use strlen to calculate string length Suppress a Clang warning regarding potential buffer overflow: arch/mips/loongson64/env.c:190:31: error: the value of the size argument in 'strncat' is too large, might lead to a buffer overflow [-Werror,-Wstrncat-size] 190 | strncat(cpu_full_name, freq, sizeof(cpu_full_name)); | ^~~~~~~~~~~~~~~~~~~~~ arch/mips/loongson64/env.c:190:31: note: change the argument to be the free space in the destination buffer minus the terminating null byte 190 | strncat(cpu_full_name, freq, sizeof(cpu_full_name)); | ^~~~~~~~~~~~~~~~~~~~~ | sizeof(cpu_full_name) - strlen(cpu_full_name) - 1 1 error generated. Fixes: "BACKPORT: FROMLIST: MIPS: Add __cpu_full_name[] to make CPU names more human-readable" Signed-off-by: Mingcong Bai --- arch/mips/loongson64/env.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/loongson64/env.c b/arch/mips/loongson64/env.c index d526d1b14d3ac..5e2e8571305b0 100644 --- a/arch/mips/loongson64/env.c +++ b/arch/mips/loongson64/env.c @@ -187,7 +187,7 @@ void __init prom_lefi_init_env(void) /* Append default cpu frequency with round-off */ sprintf(freq, " @ %uMHz", (cpu_clock_freq + 500000) / 1000000); - strncat(cpu_full_name, freq, sizeof(cpu_full_name)); + strncat(cpu_full_name, freq, strlen(freq)); __cpu_full_name[0] = cpu_full_name; /* Read the ID of PCI host bridge to detect bridge type */