From a1192a61ae1523d332313d2bbdb6108509eb2ff3 Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Mon, 17 Oct 2022 14:40:31 +0800 Subject: [PATCH 001/169] riscv: andes: add ae350 platform to SoC It's not a SoC though, just for simplicity. Signed-off-by: Yu Chien Peter Lin Reviewed-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig.socs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 29d78eefc8894f..a1630aacc4fa7e 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -1,5 +1,11 @@ menu "SoC selection" +config PLAT_AE350 + bool "Andes AE350 Platform" + select SIFIVE_PLIC + help + This enables support for Andes AE350 platform hardware. + config SOC_MICROCHIP_POLARFIRE bool "Microchip PolarFire SoCs" select MCHP_CLK_MPFS From 1aa093cd0735f850be20a72a30b4ed4685c6efc5 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Wed, 1 Feb 2023 11:32:06 +0800 Subject: [PATCH 002/169] riscv: andes: defconfig: add andes_defconfig and andes-support.config Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Makefile | 20 +++ arch/riscv/configs/amp.config | 11 ++ arch/riscv/configs/andes-support.config | 12 ++ arch/riscv/configs/andes_defconfig | 174 ++++++++++++++++++++++++ arch/riscv/configs/smp.config | 2 + 5 files changed, 219 insertions(+) create mode 100644 arch/riscv/configs/amp.config create mode 100644 arch/riscv/configs/andes-support.config create mode 100644 arch/riscv/configs/andes_defconfig create mode 100644 arch/riscv/configs/smp.config diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 3cb876f83187df..655085e64fe732 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -186,3 +186,23 @@ rv64_randconfig: PHONY += rv32_defconfig rv32_defconfig: $(Q)$(MAKE) -f $(srctree)/Makefile defconfig 32-bit.config + +PHONY += ae350_rv32_up_defconfig +ae350_rv32_up_defconfig: + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 32-bit.config + +PHONY += ae350_rv32_smp_defconfig +ae350_rv32_smp_defconfig: + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 32-bit.config smp.config + +PHONY += ae350_rv64_up_defconfig +ae350_rv64_up_defconfig: + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config + +PHONY += ae350_rv64_smp_defconfig +ae350_rv64_smp_defconfig: + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config smp.config + +PHONY += ae350_rv64_ax25_amp_defconfig +ae350_rv64_ax25_amp_defconfig: + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config amp.config diff --git a/arch/riscv/configs/amp.config b/arch/riscv/configs/amp.config new file mode 100644 index 00000000000000..11dbd76dfca949 --- /dev/null +++ b/arch/riscv/configs/amp.config @@ -0,0 +1,11 @@ +CONFIG_ATCSMU=y +# CONFIG_PMA is not set +CONFIG_AMP=y + +CONFIG_UIO=y +CONFIG_UIO_PDRV_GENIRQ=y + +CONFIG_REMOTEPROC=y +CONFIG_ANDES_REMOTEPROC=m +CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_VIRTIO=m diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config new file mode 100644 index 00000000000000..ff6179d4f90337 --- /dev/null +++ b/arch/riscv/configs/andes-support.config @@ -0,0 +1,12 @@ +CONFIG_DEFAULT_HOSTNAME="andes-test" +CONFIG_PLAT_AE350=y + +CONFIG_ANDES_CACHE=y +CONFIG_ANDES_DMA_NONCOHERENT=y +CONFIG_ANDES_PPMA=y + +CONFIG_FTMAC100=y +CONFIG_MMC_FTSDCG=y +CONFIG_ATCDMAC300G=y + +# CONFIG_RISCV_PMU is not set diff --git a/arch/riscv/configs/andes_defconfig b/arch/riscv/configs/andes_defconfig new file mode 100644 index 00000000000000..7a53b743ecc1a9 --- /dev/null +++ b/arch/riscv/configs/andes_defconfig @@ -0,0 +1,174 @@ +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_NO_HZ_IDLE=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y +CONFIG_PREEMPT=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_CGROUP_SCHED=y +CONFIG_CFS_BANDWIDTH=y +CONFIG_CGROUP_BPF=y +CONFIG_NAMESPACES=y +CONFIG_USER_NS=y +CONFIG_CHECKPOINT_RESTORE=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="rootfs-lite initramfs.txt.lite" +# CONFIG_RD_BZIP2 is not set +# CONFIG_RD_LZMA is not set +# CONFIG_RD_XZ is not set +# CONFIG_RD_LZO is not set +# CONFIG_RD_LZ4 is not set +# CONFIG_RD_ZSTD is not set +# CONFIG_INITRAMFS_PRESERVE_MTIME is not set +# CONFIG_SYSFS_SYSCALL is not set +CONFIG_EMBEDDED=y +CONFIG_PROFILING=y +CONFIG_SOC_VIRT=y +# CONFIG_RISCV_ISA_ZICBOM is not set +CONFIG_PM=y +CONFIG_CPU_IDLE=y +CONFIG_JUMP_LABEL=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +# CONFIG_INET_DIAG is not set +# CONFIG_WIRELESS is not set +# CONFIG_ETHTOOL_NETLINK is not set +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_VIRTIO_BLK=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_SCSI_VIRTIO=y +CONFIG_NETDEVICES=y +# CONFIG_NET_VENDOR_ALACRITECH is not set +# CONFIG_NET_VENDOR_AMAZON is not set +# CONFIG_NET_VENDOR_AQUANTIA is not set +# CONFIG_NET_VENDOR_ARC is not set +# CONFIG_NET_VENDOR_ASIX is not set +# CONFIG_NET_VENDOR_BROADCOM is not set +# CONFIG_NET_VENDOR_CADENCE is not set +# CONFIG_NET_VENDOR_CAVIUM is not set +# CONFIG_NET_VENDOR_CORTINA is not set +# CONFIG_NET_VENDOR_DAVICOM is not set +# CONFIG_NET_VENDOR_ENGLEDER is not set +# CONFIG_NET_VENDOR_EZCHIP is not set +# CONFIG_NET_VENDOR_FUNGIBLE is not set +# CONFIG_NET_VENDOR_GOOGLE is not set +# CONFIG_NET_VENDOR_HUAWEI is not set +# CONFIG_NET_VENDOR_INTEL is not set +# CONFIG_NET_VENDOR_WANGXUN is not set +# CONFIG_NET_VENDOR_LITEX is not set +# CONFIG_NET_VENDOR_MELLANOX is not set +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MICROSEMI is not set +# CONFIG_NET_VENDOR_MICROSOFT is not set +# CONFIG_NET_VENDOR_NI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NETRONOME is not set +# CONFIG_NET_VENDOR_PENSANDO is not set +# CONFIG_NET_VENDOR_QUALCOMM is not set +# CONFIG_NET_VENDOR_RENESAS is not set +# CONFIG_NET_VENDOR_ROCKER is not set +# CONFIG_NET_VENDOR_SAMSUNG is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SOLARFLARE is not set +# CONFIG_NET_VENDOR_SOCIONEXT is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SYNOPSYS is not set +# CONFIG_NET_VENDOR_VERTEXCOM is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +# CONFIG_NET_VENDOR_XILINX is not set +# CONFIG_WLAN is not set +CONFIG_INPUT_MOUSEDEV=y +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_VIRTIO_CONSOLE=y +CONFIG_HW_RANDOM=y +CONFIG_HW_RANDOM_VIRTIO=y +CONFIG_I2C=y +CONFIG_GPIOLIB=y +CONFIG_DEBUG_GPIO=y +CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_SIFIVE=y +CONFIG_POWER_SUPPLY=y +# CONFIG_HWMON is not set +CONFIG_FB=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_USB_SUPPORT is not set +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_CADENCE=y +CONFIG_DMADEVICES=y +CONFIG_SYNC_FILE=y +CONFIG_VIRTIO_BALLOON=y +CONFIG_VIRTIO_INPUT=y +CONFIG_VIRTIO_MMIO=y +# CONFIG_VHOST_MENU is not set +# CONFIG_IOMMU_SUPPORT is not set +CONFIG_EXT2_FS=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +CONFIG_EXPORTFS_BLOCK_OPS=y +CONFIG_FANOTIFY=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_HUGETLBFS=y +CONFIG_CONFIGFS_FS=y +# CONFIG_EFIVAR_FS is not set +# CONFIG_MISC_FILESYSTEMS is not set +CONFIG_NFS_FS=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_NFS_V4_2=y +CONFIG_NFS_USE_LEGACY_DNS=y +# CONFIG_NFS_DISABLE_UDP_SUPPORT is not set +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ISO8859_1=y +CONFIG_CRYPTO_DEV_VIRTIO=y +CONFIG_CRC_ITU_T=y +CONFIG_CRC7=y +CONFIG_XZ_DEC=y +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_INFO_DWARF4=y +CONFIG_GDB_SCRIPTS=y +CONFIG_READABLE_ASM=y +CONFIG_DEBUG_FS=y +CONFIG_DEBUG_PAGEALLOC=y +CONFIG_PTDUMP_DEBUGFS=y +CONFIG_SCHED_STACK_END_CHECK=y +CONFIG_DEBUG_VM=y +CONFIG_DEBUG_VM_PGFLAGS=y +CONFIG_DEBUG_MEMORY_INIT=y +CONFIG_SOFTLOCKUP_DETECTOR=y +CONFIG_WQ_WATCHDOG=y +CONFIG_DEBUG_TIMEKEEPING=y +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_DEBUG_RT_MUTEXES=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_MUTEXES=y +CONFIG_DEBUG_RWSEMS=y +CONFIG_DEBUG_LIST=y +CONFIG_DEBUG_PLIST=y +CONFIG_DEBUG_SG=y +# CONFIG_RCU_TRACE is not set +CONFIG_RCU_EQS_DEBUG=y +# CONFIG_FTRACE is not set +# CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_MEMTEST=y diff --git a/arch/riscv/configs/smp.config b/arch/riscv/configs/smp.config new file mode 100644 index 00000000000000..5d724af5288627 --- /dev/null +++ b/arch/riscv/configs/smp.config @@ -0,0 +1,2 @@ +CONFIG_SMP=y +CONFIG_HOTPLUG_CPU=y From 6f58fc1d83e0fa51212c7a26a974c02ac1ab5f34 Mon Sep 17 00:00:00 2001 From: Alan Kao Date: Wed, 16 Oct 2019 10:20:47 +0800 Subject: [PATCH 003/169] riscv: andes: Add Andes-specific relocation type Reformed from the following patches on RISCV-Linux-5.4: - (d7d722e5f46e) Andes-specific relocation type support - Reformed from the following patches: - https://git.andestech.com/alankao/linux/commit/3cb9939499bcf862ed78bfd650701846f2edb966 Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/include/uapi/asm/elf.h | 17 ++++++++++++++++ arch/riscv/kernel/module.c | 33 ++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/arch/riscv/include/uapi/asm/elf.h b/arch/riscv/include/uapi/asm/elf.h index d696d6610231dd..73f14235e52cdb 100644 --- a/arch/riscv/include/uapi/asm/elf.h +++ b/arch/riscv/include/uapi/asm/elf.h @@ -94,5 +94,22 @@ typedef union __riscv_fp_state elf_fpregset_t; #define R_RISCV_SET32 56 #define R_RISCV_32_PCREL 57 +/* Andes V5 */ +#define R_RISCV_ALIGN_BTB 240 +#define R_RISCV_10_PCREL 241 +#define R_RISCV_DATA 242 +#define R_RISCV_LALO_HI20 243 +#define R_RISCV_LALO_LO12_I 244 +#define R_RISCV_RELAX_ENTRY 245 +#define R_RISCV_LGP18S0 246 +#define R_RISCV_LGP17S1 247 +#define R_RISCV_LGP17S2 248 +#define R_RISCV_LGP17S3 249 +#define R_RISCV_SGP18S0 250 +#define R_RISCV_SGP17S1 251 +#define R_RISCV_SGP17S2 252 +#define R_RISCV_SGP17S3 253 +#define R_RISCV_RELAX_REGION_BEGIN 254 +#define R_RISCV_RELAX_REGION_END 255 #endif /* _UAPI_ASM_RISCV_ELF_H */ diff --git a/arch/riscv/kernel/module.c b/arch/riscv/kernel/module.c index a331001e33e669..12e678ca287016 100644 --- a/arch/riscv/kernel/module.c +++ b/arch/riscv/kernel/module.c @@ -296,6 +296,25 @@ static int apply_r_riscv_sub64_rela(struct module *me, u32 *location, return 0; } +static int apply_r_riscv_10_pcrel_rela(struct module *me, u32 *location, + Elf_Addr v) +{ + s64 offset = (void *)v - (void *)location; + u32 imm10 = (offset & 0x400) << (31 - 10); + u32 imm9_5 = (offset & 0x3e0) << (25 - 5); + u32 imm4_1 = (offset & 0x1e) << (8 - 1); + + *(u32 *) location = (*(u32 *) location & 0x41fff0ff) | + imm10 | imm9_5 | imm4_1; + return 0; +} + +static int apply_r_riscv_ignore_rela(struct module *me, u32 *location, + Elf_Addr v) +{ + return 0; +} + static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, Elf_Addr v) = { [R_RISCV_32] = apply_r_riscv_32_rela, @@ -321,6 +340,18 @@ static int (*reloc_handlers_rela[]) (struct module *me, u32 *location, [R_RISCV_SUB64] = apply_r_riscv_sub64_rela, }; +static int (*reloc_handlers_rela_nds(unsigned int type)) (struct module *me, + u32 *location, + Elf_Addr v) +{ + if (type == R_RISCV_10_PCREL) + return apply_r_riscv_10_pcrel_rela; + else if (type >= R_RISCV_ALIGN_BTB && type <= R_RISCV_RELAX_REGION_END) + return apply_r_riscv_ignore_rela; + else + return NULL; +} + int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) @@ -357,7 +388,7 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, if (type < ARRAY_SIZE(reloc_handlers_rela)) handler = reloc_handlers_rela[type]; else - handler = NULL; + handler = reloc_handlers_rela_nds(type); if (!handler) { pr_err("%s: Unknown relocation type %u\n", From 9e95b4ad193506fee513a846a3f531c132985c82 Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Tue, 2 Aug 2022 09:39:00 +0800 Subject: [PATCH 004/169] riscv: andes: add new readl_fixup() when driver probing Reformed from the following patches on RISCV-Linux-5.4: - (77573fb3af94) riscv: Add new readl_fixup() when driver probing - (21033a8b8069) riscv: Add shift_bits parameter on readl_fixup() - (86c6918870fe) riscv: Fix readl_fixup undefined problem Signed-off-by: Yu Chien Peter Lin Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/include/asm/uaccess.h | 2 ++ arch/riscv/kernel/riscv_ksyms.c | 1 + arch/riscv/lib/Makefile | 1 + arch/riscv/lib/driver_access.S | 44 ++++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+) create mode 100644 arch/riscv/lib/driver_access.S diff --git a/arch/riscv/include/asm/uaccess.h b/arch/riscv/include/asm/uaccess.h index ec0cab9fbddd0d..51d59423eb773b 100644 --- a/arch/riscv/include/asm/uaccess.h +++ b/arch/riscv/include/asm/uaccess.h @@ -285,6 +285,8 @@ do { \ }) +extern asmlinkage int readl_fixup(void __iomem *addr, unsigned int val, + unsigned int shift_bits); unsigned long __must_check __asm_copy_to_user(void __user *to, const void *from, unsigned long n); unsigned long __must_check __asm_copy_from_user(void *to, diff --git a/arch/riscv/kernel/riscv_ksyms.c b/arch/riscv/kernel/riscv_ksyms.c index 5ab1c7e1a6ed5d..f9f69bc14a1bb3 100644 --- a/arch/riscv/kernel/riscv_ksyms.c +++ b/arch/riscv/kernel/riscv_ksyms.c @@ -15,3 +15,4 @@ EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(__memset); EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(__memmove); +EXPORT_SYMBOL(readl_fixup); diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 25d5c9664e57e4..6c38bda9f479dd 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -3,6 +3,7 @@ lib-y += delay.o lib-y += memcpy.o lib-y += memset.o lib-y += memmove.o +lib-y += driver_access.o lib-$(CONFIG_MMU) += uaccess.o lib-$(CONFIG_64BIT) += tishift.o diff --git a/arch/riscv/lib/driver_access.S b/arch/riscv/lib/driver_access.S new file mode 100644 index 00000000000000..6fffafd7c79d51 --- /dev/null +++ b/arch/riscv/lib/driver_access.S @@ -0,0 +1,44 @@ +#include +#include +#include + + .altmacro + .macro fixup op reg addr lbl + LOCAL _epc +_epc: + \op \reg, \addr + .section __ex_table,"a" + .balign RISCV_SZPTR + RISCV_PTR _epc, \lbl + .previous + .endm + +/* + a0: driver register address + a1: correct value + a2: shift bits + if register value is not equal a1 + return 0 +*/ +ENTRY(readl_fixup) + + fixup lw t0, (a0), 2f + fence i, r + srl t0, t0, a2 + bne t0, a1, error + li a0, 1 + ret + +error: + li a0, 0 + ret + +ENDPROC(readl_fixup) + + .section .fixup,"ax" + .balign 4 + /* Fixup code for readl_fixup */ +2: + li a0, 0 + ret + .previous From 855a734cca132948779e5053105e8b8bd9c992b8 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 13 Dec 2022 18:42:17 +0800 Subject: [PATCH 005/169] soc: andes: add andes platform csr.h and proc.h and sbi.h Signed-off-by: Charles Ci-Jyun Wu --- include/soc/andes/csr.h | 150 +++++++++++++++++++++++++++++++++++++++ include/soc/andes/proc.h | 57 +++++++++++++++ include/soc/andes/sbi.h | 47 ++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 include/soc/andes/csr.h create mode 100644 include/soc/andes/proc.h create mode 100644 include/soc/andes/sbi.h diff --git a/include/soc/andes/csr.h b/include/soc/andes/csr.h new file mode 100644 index 00000000000000..b6569c0755c7fa --- /dev/null +++ b/include/soc/andes/csr.h @@ -0,0 +1,150 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ +#ifndef __SOC_ANDES_CSR_H +#define __SOC_ANDES_CSR_H + +#include +/* mdcm_cfg: Data Cache/Memory Configuration Register */ +#define MDCM_CFG_DEST_OFFSET 0 +#define MDCM_CFG_DWAY_OFFSET 3 +#define MDCM_CFG_DSZ_OFFSET 6 +#define MDCM_CFG_DLCK_OFFSET 9 +#define MDCM_CFG_DC_ECC_OFFSET 10 +#define MDCM_CFG_DLMB_OFFSET 12 +#define MDCM_CFG_DLMSZ_OFFSET 15 +#define MDCM_CFG_ULM_2BANK_OFFSET 20 +#define MDCM_CFG_DLM_ECC_OFFSET 21 + +#define MDCM_CFG_DEST_MASK (0x7 << MDCM_CFG_DEST_OFFSET) +#define MDCM_CFG_DWAY_MASK (0x7 << MDCM_CFG_DWAY_OFFSET) +#define MDCM_CFG_DSZ_MASK (0x7 << MDCM_CFG_DSZ_OFFSET) +#define MDCM_CFG_DLCK_MASK (0x1 << MDCM_CFG_DLCK_OFFSET) +#define MDCM_CFG_DC_ECC_MASK (0x3 << MDCM_CFG_DC_ECC_OFFSET) +#define MDCM_CFG_DLMB_MASK (0x7 << MDCM_CFG_DLMB_OFFSET) +#define MDCM_CFG_DLMSZ_MASK (0x1f << MDCM_CFG_DLMSZ_OFFSET) +#define MDCM_CFG_ULM_2BANK_MASK (0x1 << MDCM_CFG_ULM_2BANK_OFFSET) +#define MDCM_CFG_DLM_ECC_MASK (0x3 << MDCM_CFG_DLM_ECC_OFFSET) + +/* User mode control registers */ +#define CSR_UITB 0x800 +#define CSR_UCODE 0x801 +#define CSR_UDCAUSE 0x809 +#define CCTL_REG_UCCTLBEGINADDR_NUM 0x80b +#define CCTL_REG_UCCTLCOMMAND_NUM 0x80c +#define CSR_WFE 0x810 +#define CSR_SLEEPVALUE 0x811 +#define CSR_TXEVT 0x812 + +#define custom_csr_write(csr_num, val) csr_write(csr_num, val) +/* ucctlcommand */ +/* D-cache operation */ +#define CCTL_L1D_VA_INVAL 0 +#define CCTL_L1D_VA_WB 1 +#define CCTL_L1D_VA_WBINVAL 2 + +/* non-blocking & write around */ +#define MMISC_CTL_NON_BLOCKING_ENABLE (0x1 << MMISC_CTL_NON_BLOCKING_OFFSET) +#define MMISC_CTL_NON_BLOCKING_OFFSET 0x8 + +#define MCACHE_CTL_L1I_PREFETCH_OFFSET 9 +#define MCACHE_CTL_L1D_PREFETCH_OFFSET 10 +#define MCACHE_CTL_DC_WAROUND_OFFSET_1 13 +#define MCACHE_CTL_DC_WAROUND_OFFSET_2 14 +#define MCACHE_CTL_L1I_PREFETCH_EN (0x1 << MCACHE_CTL_L1I_PREFETCH_OFFSET) +#define MCACHE_CTL_L1D_PREFETCH_EN (0x1 << MCACHE_CTL_L1D_PREFETCH_OFFSET) +#define MCACHE_CTL_DC_WAROUND_1_EN (0x1 << MCACHE_CTL_DC_WAROUND_OFFSET_1) +#define MCACHE_CTL_DC_WAROUND_2_EN (0x1 << MCACHE_CTL_DC_WAROUND_OFFSET_2) +#define WRITE_AROUND_ENABLE (MCACHE_CTL_L1I_PREFETCH_EN | MCACHE_CTL_L1D_PREFETCH_EN | MCACHE_CTL_DC_WAROUND_1_EN) + +/* L1 I-cache , D-cache */ +#define CACHE_CTL_offIC_EN 0 /* Enable I-cache */ +#define CACHE_CTL_offDC_EN 1 /* Enable D-cache */ +#define CACHE_CTL_mskIC_EN (0x1 << CACHE_CTL_offIC_EN) +#define CACHE_CTL_mskDC_EN (0x1 << CACHE_CTL_offDC_EN) + +/* L2 cache */ +#define L2_CACHE_CTL_mskCEN 1 +/* L2 cache registers */ +#define L2C_REG_CFG_OFFSET 0 +#define L2C_REG_CTL_OFFSET 0x8 +#define L2C_HPM_C0_CTL_OFFSET 0x10 +#define L2C_HPM_C1_CTL_OFFSET 0x18 +#define L2C_HPM_C2_CTL_OFFSET 0x20 +#define L2C_HPM_C3_CTL_OFFSET 0x28 +#define L2C_REG_C0_CMD_OFFSET 0x40 +#define L2C_REG_C0_ACC_OFFSET 0x48 +#define L2C_REG_C1_CMD_OFFSET 0x50 +#define L2C_REG_C1_ACC_OFFSET 0x58 +#define L2C_REG_C2_CMD_OFFSET 0x60 +#define L2C_REG_C2_ACC_OFFSET 0x68 +#define L2C_REG_C3_CMD_OFFSET 0x70 +#define L2C_REG_C3_ACC_OFFSET 0x78 +#define L2C_REG_C0_STATUS_OFFSET 0x80 +#define L2C_REG_C0_HPM_OFFSET 0x200 + +/* L2 cache configuration register */ +#define V5_L2C_CFG_MAP_OFFSET 20 +#define V5_L2C_CFG_MAP_MASK (1UL << V5_L2C_CFG_MAP_OFFSET) + +/* L2 CCTL status */ +#define CCTL_L2_STATUS_IDLE 0 +#define CCTL_L2_STATUS_PROCESS 1 +#define CCTL_L2_STATUS_ILLEGAL 2 +/* L2 CCTL status cores mask */ +#define CCTL_L2_STATUS_C0_MASK 0xF +#define CCTL_L2_STATUS_C1_MASK 0xF0 +#define CCTL_L2_STATUS_C2_MASK 0xF00 +#define CCTL_L2_STATUS_C3_MASK 0xF000 + +/* L2 cache operation */ +#define CCTL_L2_PA_INVAL 0x8 +#define CCTL_L2_PA_WB 0x9 +#define CCTL_L2_PA_WBINVAL 0xA +#define CCTL_L2_WBINVAL_ALL 0x12 + +#define L2C_HPM_PER_CORE_OFFSET 0x8 +#ifndef __ASSEMBLER__ +extern u32 L2C_REG_PER_CORE_OFFSET; +extern u32 CCTL_L2_STATUS_PER_CORE_OFFSET; +#endif +#define L2C_REG_CN_CMD_OFFSET(n) \ + (L2C_REG_C0_CMD_OFFSET + (n * L2C_REG_PER_CORE_OFFSET)) +#define L2C_REG_CN_ACC_OFFSET(n) \ + (L2C_REG_C0_ACC_OFFSET + (n * L2C_REG_PER_CORE_OFFSET)) +#define CCTL_L2_STATUS_CN_MASK(n) \ + (CCTL_L2_STATUS_C0_MASK << (n * CCTL_L2_STATUS_PER_CORE_OFFSET)) +#define L2C_HPM_CN_CTL_OFFSET(n) \ + (L2C_HPM_C0_CTL_OFFSET + (n * L2C_HPM_PER_CORE_OFFSET)) +#define L2C_REG_CN_HPM_OFFSET(n) \ + (L2C_REG_C0_HPM_OFFSET + (n * L2C_HPM_PER_CORE_OFFSET)) + +/* Debug/Trace Registers (shared with Debug Mode) */ +#define CSR_SCONTEXT 0x7aa + +/* Supervisor trap registers */ +#define CSR_SLIE 0x9c4 +#define CSR_SLIP 0x9c5 +#define CSR_SDCAUSE 0x9c9 + +/* Supervisor counter registers */ +#define CSR_SCOUNTERINTEN 0x9cf +#define CSR_SCOUNTERMASK_M 0x9d1 +#define CSR_SCOUNTERMASK_S 0x9d2 +#define CSR_SCOUNTERMASK_U 0x9d3 +#define CSR_SCOUNTEROVF 0x9d4 +#define CSR_SCOUNTINHIBIT 0x9e0 +#define CSR_SHPMEVENT3 0x9e3 +#define CSR_SHPMEVENT4 0x9e4 +#define CSR_SHPMEVENT5 0x9e5 +#define CSR_SHPMEVENT6 0x9e6 + +/* Supervisor control registers */ +#define CSR_SCCTLDATA 0x9cd +#define CSR_SMISC_CTL 0x9d0 + +#define IRQ_HPM_OVF 18 +#define SLIP_PMOVI (_AC(0x1, UL) << IRQ_HPM_OVF) + +#endif /* !__SOC_ANDES_CSR_H */ diff --git a/include/soc/andes/proc.h b/include/soc/andes/proc.h new file mode 100644 index 00000000000000..9a96c26e6192cd --- /dev/null +++ b/include/soc/andes/proc.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ +#ifndef __SOC_ANDES_PROC_H +#define __SOC_ANDES_PROC_H + +#include +#include + +long sbi_andes_cpu_l1c_status(void); +void sbi_andes_cpu_icache_enable(void *info); +void sbi_andes_cpu_icache_disable(void *info); +void sbi_andes_cpu_dcache_enable(void *info); +void sbi_andes_cpu_dcache_disable(void *info); +uint32_t cpu_l2c_ctl_status(void); +void cpu_l2c_disable(void); + +long sbi_andes_get_non_blocking_status(void); +long sbi_andes_get_write_around_status(void); +void sbi_andes_enable_non_blocking_load_store(void); +void sbi_andes_disable_non_blocking_load_store(void); +void sbi_andes_enable_write_around(void); +void sbi_andes_disable_write_around(void); + +void sbi_andes_enable_l1i_cache(void); +void sbi_andes_disable_l1i_cache(void); +void sbi_andes_enable_l1d_cache(void); +void sbi_andes_disable_l1d_cache(void); + +void sbi_andes_set_mcache_ctl(unsigned long input); +void sbi_andes_set_mmisc_ctl(unsigned long input); + +void cpu_dma_inval_range(void *info); +void cpu_dma_wb_range(void *info); +void cpu_l2c_inval_range(unsigned long pa, unsigned long size); +void cpu_l2c_wb_range(unsigned long pa, unsigned long size); + +/* + * struct andesv5_cache_info + * The member of this struct is dupilcated to some content of struct cacheinfo + * to reduce the latence of searching dcache inforamtion in andesv5/cache.c. + * At current only dcache-line-size is needed. when the content of + * andesv5_cache_info has been initilized by function fill_cpu_cache_info(), + * member init_done is set as true + */ +struct andesv5_cache_info { + bool init_done; + int dcache_line_size; +}; + +struct range_info { + unsigned long start; + unsigned long end; + struct page *page; +}; +#endif /* !__SOC_ANDES_PROC_H */ diff --git a/include/soc/andes/sbi.h b/include/soc/andes/sbi.h new file mode 100644 index 00000000000000..7f6701cfeb3397 --- /dev/null +++ b/include/soc/andes/sbi.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ +#ifndef __SOC_ANDES_SBI_H +#define __SOC_ANDES_SBI_H + +#include +#include + +#define SBI_EXT_ANDES (SBI_EXT_VENDOR_START | ANDES_VENDOR_ID) + +enum sbi_ext_andes_fid { + + /* CCTL */ + SBI_EXT_ANDES_GET_MCACHE_CTL_STATUS = 0, + SBI_EXT_ANDES_GET_MMISC_CTL_STATUS, + SBI_EXT_ANDES_SET_MCACHE_CTL, + SBI_EXT_ANDES_SET_MMISC_CTL, + SBI_EXT_ANDES_ICACHE_OP, + SBI_EXT_ANDES_DCACHE_OP, + SBI_EXT_ANDES_L1CACHE_I_PREFETCH, + SBI_EXT_ANDES_L1CACHE_D_PREFETCH, + SBI_EXT_ANDES_NON_BLOCKING_LOAD_STORE, + SBI_EXT_ANDES_WRITE_AROUND, + + /* Trace */ + SBI_EXT_ANDES_TRIGGER, + SBI_EXT_ANDES_SET_PFM, + + /* CPU freq */ + SBI_EXT_ANDES_READ_POWERBRAKE, + SBI_EXT_ANDES_WRITE_POWERBRAKE, + + /* CPU idle (ATCSMU) */ + SBI_EXT_ANDES_SUSPEND_PREPARE, + SBI_EXT_ANDES_SUSPEND_MEM, + SBI_EXT_ANDES_SET_SUSPEND_MODE, + SBI_EXT_ANDES_ENTER_SUSPEND_MODE, + + /* Reset (ATCSMU) */ + SBI_EXT_ANDES_RESTART, + SBI_EXT_ANDES_SET_RESET_VEC, + + SBI_EXT_ANDES_DCACHE_WBINVAL_ALL, +}; +#endif /* !__SOC_ANDES_SBI_H */ From 9b1a034a598e73bbf37fcbf037cea441ceec5d4c Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Thu, 27 Oct 2022 16:41:48 +0800 Subject: [PATCH 006/169] riscv: andes: add Andes alternative ports Add required ports of the alternative scheme for Andes. Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig.erratas | 11 +++++++ arch/riscv/Kconfig.socs | 1 + arch/riscv/errata/Makefile | 1 + arch/riscv/errata/andes/Makefile | 1 + arch/riscv/errata/andes/errata.c | 44 ++++++++++++++++++++++++++ arch/riscv/include/asm/alternative.h | 3 ++ arch/riscv/include/asm/errata_list.h | 4 +++ arch/riscv/include/asm/vendorid_list.h | 1 + arch/riscv/kernel/alternative.c | 5 +++ 9 files changed, 71 insertions(+) create mode 100644 arch/riscv/errata/andes/Makefile create mode 100644 arch/riscv/errata/andes/errata.c diff --git a/arch/riscv/Kconfig.erratas b/arch/riscv/Kconfig.erratas index f3623df23b5fcd..0d91533b292457 100644 --- a/arch/riscv/Kconfig.erratas +++ b/arch/riscv/Kconfig.erratas @@ -1,5 +1,16 @@ menu "CPU errata selection" +config ERRATA_ANDES + bool "Andes errata" + depends on !XIP_KERNEL + select RISCV_ALTERNATIVE + help + All Andes errata Kconfig depend on this Kconfig. Disabling + this Kconfig will disable all Andes errata. Please say "Y" + here if your platform uses Andes CPU cores. + + Otherwise, please say "N" here to avoid unnecessary overhead. + config ERRATA_SIFIVE bool "SiFive errata" depends on !XIP_KERNEL diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index a1630aacc4fa7e..955e8ac7526396 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -3,6 +3,7 @@ menu "SoC selection" config PLAT_AE350 bool "Andes AE350 Platform" select SIFIVE_PLIC + select ERRATA_ANDES if !XIP_KERNEL help This enables support for Andes AE350 platform hardware. diff --git a/arch/riscv/errata/Makefile b/arch/riscv/errata/Makefile index a1055965fbee6a..6f1c693af92d16 100644 --- a/arch/riscv/errata/Makefile +++ b/arch/riscv/errata/Makefile @@ -1,2 +1,3 @@ +obj-$(CONFIG_ERRATA_ANDES) += andes/ obj-$(CONFIG_ERRATA_SIFIVE) += sifive/ obj-$(CONFIG_ERRATA_THEAD) += thead/ diff --git a/arch/riscv/errata/andes/Makefile b/arch/riscv/errata/andes/Makefile new file mode 100644 index 00000000000000..2d644e19caefcb --- /dev/null +++ b/arch/riscv/errata/andes/Makefile @@ -0,0 +1 @@ +obj-y += errata.o diff --git a/arch/riscv/errata/andes/errata.c b/arch/riscv/errata/andes/errata.c new file mode 100644 index 00000000000000..715ac4902050b4 --- /dev/null +++ b/arch/riscv/errata/andes/errata.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include + +static u32 __init_or_module andes_errata_probe(unsigned long archid, + unsigned long impid) +{ + u32 cpu_req_errata = 0; + + return cpu_req_errata; +} + +void __init_or_module andes_errata_patch_func(struct alt_entry *begin, + struct alt_entry *end, + unsigned long archid, + unsigned long impid, + unsigned int stage) +{ + struct alt_entry *alt; + u32 cpu_req_errata = andes_errata_probe(archid, impid); + u32 tmp; + + if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) + return; + + for (alt = begin; alt < end; alt++) { + if (alt->vendor_id != ANDES_VENDOR_ID) + continue; + if (alt->errata_id >= ERRATA_ANDES_NUMBER) + continue; + tmp = (1U << alt->errata_id); + if (cpu_req_errata & tmp) + patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len); + } +} diff --git a/arch/riscv/include/asm/alternative.h b/arch/riscv/include/asm/alternative.h index 6511dd73e812e2..6671679e8baff9 100644 --- a/arch/riscv/include/asm/alternative.h +++ b/arch/riscv/include/asm/alternative.h @@ -40,6 +40,9 @@ struct errata_checkfunc_id { bool (*func)(struct alt_entry *alt); }; +void andes_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, + unsigned long archid, unsigned long impid, + unsigned int stage); void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, unsigned long archid, unsigned long impid, unsigned int stage); diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index 7d2675bb71611c..88d49e0d66ca42 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -8,6 +8,10 @@ #include #include +#ifdef CONFIG_ERRATA_ANDES +#define ERRATA_ANDES_NUMBER 0 +#endif + #ifdef CONFIG_ERRATA_SIFIVE #define ERRATA_SIFIVE_CIP_453 0 #define ERRATA_SIFIVE_CIP_1200 1 diff --git a/arch/riscv/include/asm/vendorid_list.h b/arch/riscv/include/asm/vendorid_list.h index cb89af3f070471..2f2bb0c84f9a71 100644 --- a/arch/riscv/include/asm/vendorid_list.h +++ b/arch/riscv/include/asm/vendorid_list.h @@ -5,6 +5,7 @@ #ifndef ASM_VENDOR_LIST_H #define ASM_VENDOR_LIST_H +#define ANDES_VENDOR_ID 0x31e #define SIFIVE_VENDOR_ID 0x489 #define THEAD_VENDOR_ID 0x5b7 diff --git a/arch/riscv/kernel/alternative.c b/arch/riscv/kernel/alternative.c index a7d26a00beeadb..d9b31da4b80291 100644 --- a/arch/riscv/kernel/alternative.c +++ b/arch/riscv/kernel/alternative.c @@ -38,6 +38,11 @@ static void __init_or_module riscv_fill_cpu_mfr_info(struct cpu_manufacturer_inf #endif switch (cpu_mfr_info->vendor_id) { +#ifdef CONFIG_ERRATA_ANDES + case ANDES_VENDOR_ID: + cpu_mfr_info->patch_func = andes_errata_patch_func; + break; +#endif #ifdef CONFIG_ERRATA_SIFIVE case SIFIVE_VENDOR_ID: cpu_mfr_info->patch_func = sifive_errata_patch_func; From 16180fce065ade3dd82e34089a51b691e7a8fce6 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 1 Nov 2022 10:26:59 +0800 Subject: [PATCH 007/169] riscv: andes: legacy_mmu: apply errata legacy MMU patch Apply the Andes legacy MMU errata. We need to flush TLB immediately after a new PTE is allocated because the legacy MMU could retrieve stale data even if the PTE is updated. Note that legacy MMU only exists in Andes 2X-series CPU. Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig.erratas | 12 ++++++++ arch/riscv/errata/andes/errata.c | 43 ++++++++++++++++++++++++++-- arch/riscv/include/asm/errata_list.h | 8 +++++- arch/riscv/include/asm/pgalloc.h | 5 ++++ arch/riscv/include/asm/pgtable-64.h | 1 + arch/riscv/include/asm/pgtable.h | 2 ++ include/soc/andes/andes.h | 11 +++++++ 7 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 include/soc/andes/andes.h diff --git a/arch/riscv/Kconfig.erratas b/arch/riscv/Kconfig.erratas index 0d91533b292457..d52ed6a5fc1f64 100644 --- a/arch/riscv/Kconfig.erratas +++ b/arch/riscv/Kconfig.erratas @@ -11,6 +11,18 @@ config ERRATA_ANDES Otherwise, please say "N" here to avoid unnecessary overhead. +config ERRATA_ANDES_LEGACY_MMU + bool "Apply Andes errata legacy MMU" + depends on ERRATA_ANDES + default y + help + This will apply the Andes legacy MMU errata. We need to + flush TLB immediately after a new PTE is allocated because + the legacy MMU could retrieve stale data even if the PTE is + updated. Note that legacy MMU only exists in Andes 2X-series CPU. + + If you don't know what to do here, say "Y". + config ERRATA_SIFIVE bool "SiFive errata" depends on !XIP_KERNEL diff --git a/arch/riscv/errata/andes/errata.c b/arch/riscv/errata/andes/errata.c index 715ac4902050b4..d4ab8f51790d99 100644 --- a/arch/riscv/errata/andes/errata.c +++ b/arch/riscv/errata/andes/errata.c @@ -8,13 +8,47 @@ #include #include #include +#include #include #include +#include + +bool andes_legacy_mmu; +EXPORT_SYMBOL(andes_legacy_mmu); + +struct errata_info_t { + char name[ERRATA_STRING_LENGTH_MAX]; + bool (*check_func)(unsigned long arch_id, + unsigned long impid, + unsigned int stage); +}; + +static bool errata_legacy_mmu_check_func(unsigned long arch_id, + unsigned long impid, + unsigned int stage) +{ + /* legacy MMU only exists in 2X-series CPU.*/ + andes_legacy_mmu = (((arch_id & 0xF0) >> 4) == 0x2) ? true : false; + return andes_legacy_mmu; +} + +static struct errata_info_t errata_list[ERRATA_ANDES_NUMBER] = { + { + .name = "legacy_mmu", + .check_func = errata_legacy_mmu_check_func + }, +}; static u32 __init_or_module andes_errata_probe(unsigned long archid, - unsigned long impid) + unsigned long impid, + unsigned int stage) { u32 cpu_req_errata = 0; + int idx; + + for (idx = 0; idx < ERRATA_ANDES_NUMBER; idx++) + if (errata_list[idx].check_func(archid, impid, stage)) + cpu_req_errata |= (1U << idx); return cpu_req_errata; } @@ -26,17 +60,20 @@ void __init_or_module andes_errata_patch_func(struct alt_entry *begin, unsigned int stage) { struct alt_entry *alt; - u32 cpu_req_errata = andes_errata_probe(archid, impid); - u32 tmp; + u32 cpu_req_errata; + u32 tmp = 0; if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) return; + cpu_req_errata = andes_errata_probe(archid, impid, stage); + for (alt = begin; alt < end; alt++) { if (alt->vendor_id != ANDES_VENDOR_ID) continue; if (alt->errata_id >= ERRATA_ANDES_NUMBER) continue; + tmp = (1U << alt->errata_id); if (cpu_req_errata & tmp) patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len); diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index 88d49e0d66ca42..0ecdfc14fdbcf3 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -9,7 +9,8 @@ #include #ifdef CONFIG_ERRATA_ANDES -#define ERRATA_ANDES_NUMBER 0 +#define ERRATA_ANDES_LEGACY_MMU 0 +#define ERRATA_ANDES_NUMBER 1 #endif #ifdef CONFIG_ERRATA_SIFIVE @@ -48,6 +49,11 @@ asm(ALTERNATIVE("sfence.vma %0", "sfence.vma", SIFIVE_VENDOR_ID, \ ERRATA_SIFIVE_CIP_1200, CONFIG_ERRATA_SIFIVE_CIP_1200) \ : : "r" (addr) : "memory") +#define ALT_LEGACY_MMU_FLUSH_TLB() \ +asm volatile (ALTERNATIVE("nop", "sfence.vma", ANDES_VENDOR_ID, \ + ERRATA_ANDES_LEGACY_MMU, CONFIG_ERRATA_ANDES_LEGACY_MMU) \ + : : : "memory") + /* * _val is marked as "will be overwritten", so need to set it to 0 * in the default case. diff --git a/arch/riscv/include/asm/pgalloc.h b/arch/riscv/include/asm/pgalloc.h index 59dc12b5b7e8fd..3696c23a16dce1 100644 --- a/arch/riscv/include/asm/pgalloc.h +++ b/arch/riscv/include/asm/pgalloc.h @@ -9,6 +9,7 @@ #include #include +#include #ifdef CONFIG_MMU #define __HAVE_ARCH_PUD_ALLOC_ONE @@ -21,6 +22,7 @@ static inline void pmd_populate_kernel(struct mm_struct *mm, unsigned long pfn = virt_to_pfn(pte); set_pmd(pmd, __pmd((pfn << _PAGE_PFN_SHIFT) | _PAGE_TABLE)); + ALT_LEGACY_MMU_FLUSH_TLB(); } static inline void pmd_populate(struct mm_struct *mm, @@ -29,6 +31,7 @@ static inline void pmd_populate(struct mm_struct *mm, unsigned long pfn = virt_to_pfn(page_address(pte)); set_pmd(pmd, __pmd((pfn << _PAGE_PFN_SHIFT) | _PAGE_TABLE)); + ALT_LEGACY_MMU_FLUSH_TLB(); } #ifndef __PAGETABLE_PMD_FOLDED @@ -37,6 +40,7 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) unsigned long pfn = virt_to_pfn(pmd); set_pud(pud, __pud((pfn << _PAGE_PFN_SHIFT) | _PAGE_TABLE)); + ALT_LEGACY_MMU_FLUSH_TLB(); } static inline void p4d_populate(struct mm_struct *mm, p4d_t *p4d, pud_t *pud) @@ -143,6 +147,7 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); /* Copy kernel mappings */ sync_kernel_mappings(pgd); + ALT_LEGACY_MMU_FLUSH_TLB(); } return pgd; } diff --git a/arch/riscv/include/asm/pgtable-64.h b/arch/riscv/include/asm/pgtable-64.h index 42a042c0e13ed6..496f1fdb86d73b 100644 --- a/arch/riscv/include/asm/pgtable-64.h +++ b/arch/riscv/include/asm/pgtable-64.h @@ -165,6 +165,7 @@ static inline int pud_user(pud_t pud) static inline void set_pud(pud_t *pudp, pud_t pud) { *pudp = pud; + ALT_LEGACY_MMU_FLUSH_TLB(); } static inline void pud_clear(pud_t *pudp) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 59bb53da473dd6..20f18a1655965c 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -226,6 +226,7 @@ static inline int pmd_leaf(pmd_t pmd) static inline void set_pmd(pmd_t *pmdp, pmd_t pmd) { *pmdp = pmd; + ALT_LEGACY_MMU_FLUSH_TLB(); } static inline void pmd_clear(pmd_t *pmdp) @@ -443,6 +444,7 @@ static inline int pte_same(pte_t pte_a, pte_t pte_b) static inline void set_pte(pte_t *ptep, pte_t pteval) { *ptep = pteval; + ALT_LEGACY_MMU_FLUSH_TLB(); } void flush_icache_pte(pte_t pte); diff --git a/include/soc/andes/andes.h b/include/soc/andes/andes.h new file mode 100644 index 00000000000000..f089fbd3ba4737 --- /dev/null +++ b/include/soc/andes/andes.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#ifndef __SOC_ANDES_ANDES_H +#define __SOC_ANDES_ANDES_H + +extern bool andes_legacy_mmu; + +#endif /* !__SOC_ANDES_ANDES_H */ From 0c676152243acf66d472433c9fb800229c8af41c Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 13 Dec 2022 16:55:12 +0800 Subject: [PATCH 008/169] soc: andes: cache: add Andes cache support Signed-off-by: Charles Ci-Jyun Wu --- drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/andes/Kconfig | 9 + drivers/soc/andes/Makefile | 3 + drivers/soc/andes/cache.c | 529 +++++++++++++++++++++++++++++++++++++ 5 files changed, 543 insertions(+) create mode 100644 drivers/soc/andes/Kconfig create mode 100644 drivers/soc/andes/Makefile create mode 100644 drivers/soc/andes/cache.c diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index e461c071189b48..034bd60c3846fb 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -3,6 +3,7 @@ menu "SOC (System On Chip) specific Drivers" source "drivers/soc/actions/Kconfig" source "drivers/soc/amlogic/Kconfig" +source "drivers/soc/andes/Kconfig" source "drivers/soc/apple/Kconfig" source "drivers/soc/aspeed/Kconfig" source "drivers/soc/atmel/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 69ba6508cf2c17..538b6869cdac01 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_ARCH_ACTIONS) += actions/ +obj-$(CONFIG_PLAT_AE350) += andes/ obj-y += apple/ obj-y += aspeed/ obj-$(CONFIG_ARCH_AT91) += atmel/ diff --git a/drivers/soc/andes/Kconfig b/drivers/soc/andes/Kconfig new file mode 100644 index 00000000000000..0d222d83ca6ea5 --- /dev/null +++ b/drivers/soc/andes/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +menu "Andes AE350 platform drivers" + +config ANDES_CACHE + bool "Andes L1 & L2 cache controller" + depends on RISCV && PLAT_AE350 + help + Support for the Andes L1 & L2 cache controller on AE350 platforms. +endmenu diff --git a/drivers/soc/andes/Makefile b/drivers/soc/andes/Makefile new file mode 100644 index 00000000000000..016e271c9593c4 --- /dev/null +++ b/drivers/soc/andes/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_ANDES_CACHE) += cache.o diff --git a/drivers/soc/andes/cache.c b/drivers/soc/andes/cache.c new file mode 100644 index 00000000000000..5c26e9bc0be042 --- /dev/null +++ b/drivers/soc/andes/cache.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CACHE_LINE_SIZE 256 +#define EVSEL_MASK 0xff +#define SEL_PER_CTL 8 +#define SEL_OFF(id) (8 * (id % 8)) + +void __iomem *l2c_base; +/* default to offset of V0 memory map */ +u32 L2C_REG_PER_CORE_OFFSET = 0x10; +u32 CCTL_L2_STATUS_PER_CORE_OFFSET = 0x4; +u32 L2C_REG_STATUS_OFFSET = 0; + +DEFINE_PER_CPU(struct andesv5_cache_info, cpu_cache_info) = { + .init_done = 0, + .dcache_line_size = SZ_32 +}; + +static void fill_cpu_cache_info(struct andesv5_cache_info *cpu_ci) +{ + int ncpu = get_cpu(); + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(ncpu); + struct cacheinfo *this_leaf = this_cpu_ci->info_list; + unsigned int i = 0; + + for (; i < this_cpu_ci->num_leaves; i++, this_leaf++) { + if (this_leaf->type == CACHE_TYPE_DATA) { + cpu_ci->dcache_line_size = + this_leaf->coherency_line_size; + } + } + cpu_ci->init_done = true; + put_cpu(); +} + +static inline int get_cache_line_size(void) +{ + int ncpu = get_cpu(); + struct andesv5_cache_info *cpu_ci = &per_cpu(cpu_cache_info, ncpu); + + if (unlikely(cpu_ci->init_done == false)) + fill_cpu_cache_info(cpu_ci); + put_cpu(); + return cpu_ci->dcache_line_size; +} + +static uint32_t cpu_l2c_get_cctl_status(void) +{ + int mhartid = get_cpu(); + + put_cpu(); + return readl((void *)(l2c_base + L2C_REG_C0_STATUS_OFFSET + + mhartid * L2C_REG_STATUS_OFFSET)); +} + +void cpu_dcache_wb_range(unsigned long start, unsigned long end, int line_size, + struct page *page) +{ + int mhartid = get_cpu(); + unsigned long pa = page_to_phys(page); + + if (start & (~PAGE_MASK)) + pa += start & ~PAGE_MASK; + + while (end > start) { + custom_csr_write(CCTL_REG_UCCTLBEGINADDR_NUM, start); + custom_csr_write(CCTL_REG_UCCTLCOMMAND_NUM, CCTL_L1D_VA_WB); + + if (l2c_base) { + writel(pa, + (void *)(l2c_base + + L2C_REG_CN_ACC_OFFSET(mhartid))); + + writel(CCTL_L2_PA_WB, + (void *)(l2c_base + + L2C_REG_CN_CMD_OFFSET(mhartid))); + + while ((cpu_l2c_get_cctl_status() & + CCTL_L2_STATUS_CN_MASK(mhartid)) != + CCTL_L2_STATUS_IDLE) + ; + } + + start += line_size; + pa += line_size; + } + put_cpu(); +} + +void cpu_dcache_inval_range(unsigned long start, unsigned long end, + int line_size, struct page *page) +{ + int mhartid = get_cpu(); + unsigned long pa = page_to_phys(page); + + if (start & (~PAGE_MASK)) + pa += start & ~PAGE_MASK; + + while (end > start) { + custom_csr_write(CCTL_REG_UCCTLBEGINADDR_NUM, start); + custom_csr_write(CCTL_REG_UCCTLCOMMAND_NUM, CCTL_L1D_VA_INVAL); + + if (l2c_base) { + writel(pa, + (void *)(l2c_base + + L2C_REG_CN_ACC_OFFSET(mhartid))); + + writel(CCTL_L2_PA_INVAL, + (void *)(l2c_base + + L2C_REG_CN_CMD_OFFSET(mhartid))); + + while ((cpu_l2c_get_cctl_status() & + CCTL_L2_STATUS_CN_MASK(mhartid)) != + CCTL_L2_STATUS_IDLE) + ; + } + + start += line_size; + pa += line_size; + } + put_cpu(); +} +void cpu_dma_inval_range(void *info) +{ + unsigned long flags; + unsigned long line_size = get_cache_line_size(); + struct range_info *ri = info; + unsigned long start = ri->start; + unsigned long end = ri->end; + unsigned long old_start = start; + unsigned long old_end = end; + char cache_buf[2][MAX_CACHE_LINE_SIZE] = {0}; + + if (unlikely(start == end)) + return; + + start = start & (~(line_size - 1)); + end = ((end + line_size - 1) & (~(line_size - 1))); + + local_irq_save(flags); + if (unlikely(start != old_start)) + memcpy(&cache_buf[0][0], (void *)start, line_size); + + if (unlikely(end != old_end)) { + memcpy(&cache_buf[1][0], (void *)(old_end & (~(line_size - 1))), + line_size); + } + cpu_dcache_inval_range(start, end, line_size, ri->page); + if (unlikely(start != old_start)) { + memcpy((void *)start, &cache_buf[0][0], + (old_start & (line_size - 1))); + } + if (unlikely(end != old_end)) { + memcpy((void *)(old_end + 1), + &cache_buf[1][(old_end & (line_size - 1)) + 1], + end - old_end - 1); + } + local_irq_restore(flags); +} +EXPORT_SYMBOL(cpu_dma_inval_range); + +void cpu_dma_wb_range(void *info) +{ + unsigned long flags; + unsigned long line_size = get_cache_line_size(); + struct range_info *ri = info; + unsigned long start = ri->start; + unsigned long end = ri->end; + + local_irq_save(flags); + start = start & (~(line_size - 1)); + cpu_dcache_wb_range(start, end, line_size, ri->page); + local_irq_restore(flags); +} +EXPORT_SYMBOL(cpu_dma_wb_range); + +/* non-blocking load store */ +long sbi_andes_get_non_blocking_status(void) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MMISC_CTL_STATUS, 0, 0, + 0, 0, 0, 0); + return ret.value; +} +void sbi_andes_set_mcache_ctl(unsigned long input) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_MCACHE_CTL, input, 0, 0, 0, + 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_set_mmisc_ctl(unsigned long input) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_NON_BLOCKING_LOAD_STORE, input, + 0, 0, 0, 0, 0); + local_irq_restore(flags); +} + +/* write around */ +long sbi_andes_get_write_around_status(void) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MCACHE_CTL_STATUS, 0, + 0, 0, 0, 0, 0); + return ret.value; +} + +void sbi_andes_enable_non_blocking_load_store(void) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_WRITE_AROUND, 1, 0, 0, 0, 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_disable_non_blocking_load_store(void) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_WRITE_AROUND, 0, 0, 0, 0, 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_enable_write_around(void) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_MCACHE_CTL, 1, 0, 0, + 0, 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_disable_write_around(void) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_MMISC_CTL, 0, 0, 0, 0, 0, 0); + local_irq_restore(flags); +} + +/* L1 Cache Prefetch */ +void sbi_andes_enable_l1i_cache(void) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_L1CACHE_I_PREFETCH, 1, 0, 0, 0, + 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_disable_l1i_cache(void) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_L1CACHE_I_PREFETCH, 0, 0, 0, 0, + 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_enable_l1d_cache(void) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_L1CACHE_D_PREFETCH, 1, 0, 0, 0, + 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_disable_l1d_cache(void) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_L1CACHE_D_PREFETCH, 0, 0, 0, 0, + 0, 0); + local_irq_restore(flags); +} + +/* L1 Cache */ +long sbi_andes_cpu_l1c_status(void) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MCACHE_CTL_STATUS, 0, + 0, 0, 0, 0, 0); + return ret.value; +} + +void sbi_andes_cpu_icache_enable(void *info) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_ICACHE_OP, 1, 0, 0, 0, 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_cpu_icache_disable(void *info) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_ICACHE_OP, 0, 0, 0, 0, 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_cpu_dcache_enable(void *info) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_DCACHE_OP, 1, 0, 0, 0, 0, 0); + local_irq_restore(flags); +} + +void sbi_andes_cpu_dcache_disable(void *info) +{ + unsigned long flags; + + local_irq_save(flags); + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_DCACHE_OP, 0, 0, 0, 0, 0, 0); + local_irq_restore(flags); +} + +/* L2 Cache */ +uint32_t cpu_l2c_ctl_status(void) +{ + return readl((void *)(l2c_base + L2C_REG_CTL_OFFSET)); +} + +void cpu_l2c_disable(void) +{ +#ifdef CONFIG_SMP + int mhartid = get_cpu(); +#else + int mhartid = 0; +#endif + unsigned int val; + unsigned long flags; + + /* No l2 cache */ + if (!l2c_base) + return; + + /* l2 cache has disabled */ + if (!(cpu_l2c_ctl_status() & L2_CACHE_CTL_mskCEN)) + return; + + local_irq_save(flags); + + /* Disable L2 cache */ + val = readl((void *)(l2c_base + L2C_REG_CTL_OFFSET)); + val &= (~L2_CACHE_CTL_mskCEN); + + writel(val, (void *)(l2c_base + L2C_REG_CTL_OFFSET)); + while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid)) != + CCTL_L2_STATUS_IDLE) + ; + + /* L2 write-back and invalidate all */ + writel(CCTL_L2_WBINVAL_ALL, + (void *)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid))); + + while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid)) != + CCTL_L2_STATUS_IDLE) + ; + + local_irq_restore(flags); +#ifdef CONFIG_SMP + put_cpu(); +#endif +} + +#ifndef CONFIG_SMP +void cpu_l2c_inval_range(unsigned long pa, unsigned long size) +{ + unsigned long line_size = get_cache_line_size(); + unsigned long start = pa, end = pa + size; + unsigned long align_start, align_end; + + align_start = start & ~(line_size - 1); + align_end = (end + line_size - 1) & ~(line_size - 1); + + while (align_end > align_start) { + writel(align_start, (void *)(l2c_base + L2C_REG_C0_ACC_OFFSET)); + + writel(CCTL_L2_PA_INVAL, + (void *)(l2c_base + L2C_REG_C0_CMD_OFFSET)); + + while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_C0_MASK) != + CCTL_L2_STATUS_IDLE) + ; + + align_start += line_size; + } +} +EXPORT_SYMBOL(cpu_l2c_inval_range); + +void cpu_l2c_wb_range(unsigned long pa, unsigned long size) +{ + unsigned long line_size = get_cache_line_size(); + unsigned long start = pa, end = pa + size; + unsigned long align_start, align_end; + + align_start = start & ~(line_size - 1); + align_end = (end + line_size - 1) & ~(line_size - 1); + + while (align_end > align_start) { + writel(align_start, (void *)(l2c_base + L2C_REG_C0_ACC_OFFSET)); + + writel(CCTL_L2_PA_WB, + (void *)(l2c_base + L2C_REG_C0_CMD_OFFSET)); + + while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_C0_MASK) != + CCTL_L2_STATUS_IDLE) + ; + + align_start += line_size; + } +} +EXPORT_SYMBOL(cpu_l2c_wb_range); +#else /* !CONFIG_SMP */ +void cpu_l2c_inval_range(unsigned long pa, unsigned long size) +{ + int mhartid = get_cpu(); + unsigned long line_size = get_cache_line_size(); + unsigned long start = pa, end = pa + size; + unsigned long align_start, align_end; + + align_start = start & ~(line_size - 1); + align_end = (end + line_size - 1) & ~(line_size - 1); + + while (align_end > align_start) { + writel(align_start, + (void *)(l2c_base + L2C_REG_CN_ACC_OFFSET(mhartid))); + + writel(CCTL_L2_PA_INVAL, + (void *)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid))); + + while ((cpu_l2c_get_cctl_status() & + CCTL_L2_STATUS_CN_MASK(mhartid)) != + CCTL_L2_STATUS_IDLE) + ; + + align_start += line_size; + } + put_cpu(); +} +EXPORT_SYMBOL(cpu_l2c_inval_range); + +void cpu_l2c_wb_range(unsigned long pa, unsigned long size) +{ + int mhartid = get_cpu(); + unsigned long line_size = get_cache_line_size(); + unsigned long start = pa, end = pa + size; + unsigned long align_start, align_end; + + align_start = start & ~(line_size - 1); + align_end = (end + line_size - 1) & ~(line_size - 1); + + while (align_end > align_start) { + writel(align_start, + (void *)(l2c_base + L2C_REG_CN_ACC_OFFSET(mhartid))); + + writel(CCTL_L2_PA_WB, + (void *)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid))); + + while ((cpu_l2c_get_cctl_status() & + CCTL_L2_STATUS_CN_MASK(mhartid)) != + CCTL_L2_STATUS_IDLE) + ; + + align_start += line_size; + } + put_cpu(); +} +EXPORT_SYMBOL(cpu_l2c_wb_range); +#endif /* CONFIG_SMP */ + +int __init l2c_init(void) +{ + struct device_node *node; + u32 l2c_cfg = 0; + + node = of_find_compatible_node(NULL, NULL, "cache"); + + l2c_base = of_iomap(node, 0); + if (l2c_base) + l2c_cfg = *(u32 *)l2c_base; + + if (l2c_cfg & V5_L2C_CFG_MAP_MASK) { + L2C_REG_PER_CORE_OFFSET = 0x1000; + CCTL_L2_STATUS_PER_CORE_OFFSET = 0; + L2C_REG_STATUS_OFFSET = 0x1000; + } + + return 0; +} +arch_initcall(l2c_init) From e159bf6d7a23fc0dab929e76ebf0907a8716cbeb Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 7 Mar 2023 15:37:07 +0800 Subject: [PATCH 009/169] riscv: andes: Implement ioremap_wc for noncached memory remapping support In the RISCV-Linux-5.4 branch, ioremap_nocache() is the function that Andes uses to support noncache memory mapping. But the Linux kernel ioremap_nocache() has been deprecated. - (4bdc0d676a64) remove ioremap_nocache and devm_ioremap_nocache And the generic RISCV ioremap() flow has also changed. - (38af57825313) riscv: use the generic ioremap code Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/include/asm/io.h | 6 ++++++ arch/riscv/mm/Makefile | 3 +++ arch/riscv/mm/ioremap.c | 21 +++++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 arch/riscv/mm/ioremap.c diff --git a/arch/riscv/include/asm/io.h b/arch/riscv/include/asm/io.h index 42497d487a1746..76af2a7920b5ef 100644 --- a/arch/riscv/include/asm/io.h +++ b/arch/riscv/include/asm/io.h @@ -133,6 +133,12 @@ __io_writes_outs(outs, u64, q, __io_pbr(), __io_paw()) #define outsq(addr, buffer, count) __outsq(PCI_IOBASE + (addr), buffer, count) #endif +#ifdef CONFIG_MMU +extern void __iomem *ioremap_wc(phys_addr_t phys_addr, size_t size); +#define ioremap_wc ioremap_wc +#define ioremap_wt ioremap_wc +#endif /* CONFIG_MMU */ + #include #ifdef CONFIG_MMU diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile index d76aabf4b94d6a..3eeb632fd2e729 100644 --- a/arch/riscv/mm/Makefile +++ b/arch/riscv/mm/Makefile @@ -13,6 +13,9 @@ obj-y += extable.o obj-$(CONFIG_MMU) += fault.o pageattr.o obj-y += cacheflush.o obj-y += context.o +ifeq ($(CONFIG_ERRATA_ANDES),y) +obj-y += ioremap.o +endif ifeq ($(CONFIG_MMU),y) obj-$(CONFIG_SMP) += tlbflush.o diff --git a/arch/riscv/mm/ioremap.c b/arch/riscv/mm/ioremap.c new file mode 100644 index 00000000000000..6cdc8cac8ceb3e --- /dev/null +++ b/arch/riscv/mm/ioremap.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ +#include +#include +#include +#include + +void __iomem *ioremap_wc(phys_addr_t phys_addr, size_t size) +{ + void __iomem *ret; + pgprot_t prot; + + prot = pgprot_writecombine(PAGE_KERNEL); + + ret = ioremap_prot(phys_addr, size, pgprot_val(prot)); + + return ret; +} +EXPORT_SYMBOL(ioremap_wc); From b4314616bbcf5ffb64cbda08e267f42266cc3533 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Fri, 3 Mar 2023 13:38:52 +0800 Subject: [PATCH 010/169] riscv: andes: support physical address MSB Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig.erratas | 12 +++++++++++ arch/riscv/Kconfig.socs | 7 +++++++ arch/riscv/errata/andes/errata.c | 30 ++++++++++++++++++++++++++-- arch/riscv/include/asm/errata_list.h | 3 ++- arch/riscv/include/asm/pgtable.h | 20 ++++++++++++++++++- include/soc/andes/andes.h | 1 + 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/arch/riscv/Kconfig.erratas b/arch/riscv/Kconfig.erratas index d52ed6a5fc1f64..e6b7d22ca6a060 100644 --- a/arch/riscv/Kconfig.erratas +++ b/arch/riscv/Kconfig.erratas @@ -23,6 +23,18 @@ config ERRATA_ANDES_LEGACY_MMU If you don't know what to do here, say "Y". +config ERRATA_ANDES_PA_MSB + bool "Apply Andes errata physical address MSB" + depends on ERRATA_ANDES + select RISCV_ALTERNATIVE_EARLY + default y + help + This will apply the Andes physical address MSB errata. + Use the MSB of physical address to distinguish cacheable + memory for Andes 25 series CPUs. + + If you don't know what to do here, say "Y". + config ERRATA_SIFIVE bool "SiFive errata" depends on !XIP_KERNEL diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 955e8ac7526396..6329c077862d75 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -7,6 +7,13 @@ config PLAT_AE350 help This enables support for Andes AE350 platform hardware. +config ANDES_QEMU_SUPPORT + bool "Andes QEMU SUPPORT" + depends on SOC_SIFIVE + default n + help + Andes QEMU Support. + config SOC_MICROCHIP_POLARFIRE bool "Microchip PolarFire SoCs" select MCHP_CLK_MPFS diff --git a/arch/riscv/errata/andes/errata.c b/arch/riscv/errata/andes/errata.c index d4ab8f51790d99..7dfa60f886f575 100644 --- a/arch/riscv/errata/andes/errata.c +++ b/arch/riscv/errata/andes/errata.c @@ -16,6 +16,9 @@ bool andes_legacy_mmu; EXPORT_SYMBOL(andes_legacy_mmu); +phys_addr_t andes_pa_msb; +EXPORT_SYMBOL(andes_pa_msb); + struct errata_info_t { char name[ERRATA_STRING_LENGTH_MAX]; bool (*check_func)(unsigned long arch_id, @@ -23,11 +26,28 @@ struct errata_info_t { unsigned int stage); }; +static bool errata_msb_check_func(unsigned long arch_id, + unsigned long impid, + unsigned int stage) +{ + if (stage != RISCV_ALTERNATIVES_EARLY_BOOT) + return false; + + andes_pa_msb = 0; + + if (riscv_isa_extension_available(NULL, SVPBMT)) + return false; + + csr_write(satp, SATP_PPN); + andes_pa_msb = (csr_read(satp) + 1) >> 1; + return true; +} + static bool errata_legacy_mmu_check_func(unsigned long arch_id, unsigned long impid, unsigned int stage) { - /* legacy MMU only exists in 2X-series CPU.*/ + /* legacy MMU only exists in 2x-series CPU. */ andes_legacy_mmu = (((arch_id & 0xF0) >> 4) == 0x2) ? true : false; return andes_legacy_mmu; } @@ -37,6 +57,10 @@ static struct errata_info_t errata_list[ERRATA_ANDES_NUMBER] = { .name = "legacy_mmu", .check_func = errata_legacy_mmu_check_func }, + { + .name = "pa_msb", + .check_func = errata_msb_check_func + }, }; static u32 __init_or_module andes_errata_probe(unsigned long archid, @@ -63,8 +87,10 @@ void __init_or_module andes_errata_patch_func(struct alt_entry *begin, u32 cpu_req_errata; u32 tmp = 0; - if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) + if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) { + cpu_req_errata = errata_msb_check_func(archid, impid, stage); return; + } cpu_req_errata = andes_errata_probe(archid, impid, stage); diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index 0ecdfc14fdbcf3..a692e0db23cca0 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -10,7 +10,8 @@ #ifdef CONFIG_ERRATA_ANDES #define ERRATA_ANDES_LEGACY_MMU 0 -#define ERRATA_ANDES_NUMBER 1 +#define ERRATA_ANDES_PA_MSB 1 +#define ERRATA_ANDES_NUMBER 2 #endif #ifdef CONFIG_ERRATA_SIFIVE diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 20f18a1655965c..404cd17bb197c9 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -150,6 +150,14 @@ struct pt_alloc_ops { extern struct pt_alloc_ops pt_ops __initdata; #ifdef CONFIG_MMU +/* Noncacheable page prot for Andes MSB */ +extern phys_addr_t andes_pa_msb; +#ifdef CONFIG_ANDES_QEMU_SUPPORT +#define _PAGE_NONCACHEABLE 0 +#else +#define _PAGE_NONCACHEABLE ((!!andes_pa_msb) << 31) +#endif + /* Number of PGD entries that a user-mode program can use */ #define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE) @@ -283,7 +291,15 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot) ALT_THEAD_PMA(prot_val); - return __pte((pfn << _PAGE_PFN_SHIFT) | prot_val); + /* + * When PPMA is on and activated: pa_msb == 0; + * Otherwise: pa_msb != 0; + */ + if (andes_pa_msb && (prot_val & _PAGE_NONCACHEABLE)) + return __pte(((pfn | andes_pa_msb) << _PAGE_PFN_SHIFT) + | (prot_val & ~_PAGE_NONCACHEABLE)); + else + return __pte((pfn << _PAGE_PFN_SHIFT) | prot_val); } #define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot) @@ -542,6 +558,7 @@ static inline pgprot_t pgprot_noncached(pgprot_t _prot) prot &= ~_PAGE_MTMASK; prot |= _PAGE_IO; + prot |= _PAGE_NONCACHEABLE; return __pgprot(prot); } @@ -553,6 +570,7 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot) prot &= ~_PAGE_MTMASK; prot |= _PAGE_NOCACHE; + prot |= _PAGE_NONCACHEABLE; return __pgprot(prot); } diff --git a/include/soc/andes/andes.h b/include/soc/andes/andes.h index f089fbd3ba4737..25adf893d706bd 100644 --- a/include/soc/andes/andes.h +++ b/include/soc/andes/andes.h @@ -7,5 +7,6 @@ #define __SOC_ANDES_ANDES_H extern bool andes_legacy_mmu; +extern phys_addr_t andes_pa_msb; #endif /* !__SOC_ANDES_ANDES_H */ From 40aa955f287bb6023a9994fbee310e4d51b05311 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 7 Mar 2023 23:54:47 +0800 Subject: [PATCH 011/169] soc: andes: ppma: support Andes PPMA(Programmable Physical Memory Attribute) in Linux 6.1 Reformed from the following patches on RISCV-Linux-5.4: - (4e22888cadfb) riscv: Support PMA(Physical Memory Attribute) on linux5.4. - (7dadad5d6c31) FTLCD100: Fix ioremap_nocache() causing LCD kernel panic issue. - (496478d8ea29) Complete the programmable PMA implementation with IPI - (0d56a5b5b5a5) Fix compilation error for (884f011d) - (b4716ef9b379) andes: rename sbi calls of andes vendor extension - (313eb6af3350) riscv: fix compilation warnings - (a1ca7a07fb76) RISC-V: skip PMA setting for non-memory region Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/errata/andes/errata.c | 4 ++ arch/riscv/include/asm/io.h | 3 ++ arch/riscv/mm/ioremap.c | 11 +++++ drivers/soc/andes/Kconfig | 10 +++++ drivers/soc/andes/Makefile | 6 ++- drivers/soc/andes/andes_sbi.c | 31 +++++++++++++ drivers/soc/andes/ppma.c | 74 ++++++++++++++++++++++++++++++++ include/soc/andes/andes.h | 2 + include/soc/andes/ppma.h | 28 ++++++++++++ include/soc/andes/sbi.h | 12 ++++++ 10 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 drivers/soc/andes/andes_sbi.c create mode 100644 drivers/soc/andes/ppma.c create mode 100644 include/soc/andes/ppma.h diff --git a/arch/riscv/errata/andes/errata.c b/arch/riscv/errata/andes/errata.c index 7dfa60f886f575..edde226453023b 100644 --- a/arch/riscv/errata/andes/errata.c +++ b/arch/riscv/errata/andes/errata.c @@ -12,6 +12,7 @@ #include #include #include +#include bool andes_legacy_mmu; EXPORT_SYMBOL(andes_legacy_mmu); @@ -38,6 +39,9 @@ static bool errata_msb_check_func(unsigned long arch_id, if (riscv_isa_extension_available(NULL, SVPBMT)) return false; + if (andes_probe_ppma()) + return false; + csr_write(satp, SATP_PPN); andes_pa_msb = (csr_read(satp) + 1) >> 1; return true; diff --git a/arch/riscv/include/asm/io.h b/arch/riscv/include/asm/io.h index 76af2a7920b5ef..3f3ef8f6dae9c1 100644 --- a/arch/riscv/include/asm/io.h +++ b/arch/riscv/include/asm/io.h @@ -137,6 +137,9 @@ __io_writes_outs(outs, u64, q, __io_pbr(), __io_paw()) extern void __iomem *ioremap_wc(phys_addr_t phys_addr, size_t size); #define ioremap_wc ioremap_wc #define ioremap_wt ioremap_wc + +extern bool iounmap_allowed(void *addr); +#define iounmap_allowed iounmap_allowed #endif /* CONFIG_MMU */ #include diff --git a/arch/riscv/mm/ioremap.c b/arch/riscv/mm/ioremap.c index 6cdc8cac8ceb3e..31c3309c472618 100644 --- a/arch/riscv/mm/ioremap.c +++ b/arch/riscv/mm/ioremap.c @@ -6,6 +6,7 @@ #include #include #include +#include void __iomem *ioremap_wc(phys_addr_t phys_addr, size_t size) { @@ -16,6 +17,16 @@ void __iomem *ioremap_wc(phys_addr_t phys_addr, size_t size) ret = ioremap_prot(phys_addr, size, pgprot_val(prot)); + if (ret) + andes_set_ppma(phys_addr, ret, size); + return ret; } EXPORT_SYMBOL(ioremap_wc); + +bool iounmap_allowed(void *addr) +{ + andes_free_ppma(addr); + return true; +} +EXPORT_SYMBOL(iounmap_allowed); diff --git a/drivers/soc/andes/Kconfig b/drivers/soc/andes/Kconfig index 0d222d83ca6ea5..fc77350757d4b8 100644 --- a/drivers/soc/andes/Kconfig +++ b/drivers/soc/andes/Kconfig @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023 Andes Technology Corporation. + menu "Andes AE350 platform drivers" config ANDES_CACHE @@ -6,4 +8,12 @@ config ANDES_CACHE depends on RISCV && PLAT_AE350 help Support for the Andes L1 & L2 cache controller on AE350 platforms. + +config ANDES_PPMA + bool "Andes PPMA Support" + depends on RISCV && PLAT_AE350 + help + When calling memremap with MEMREMAP_WC flag, + Andes programmable physical memory attributes (PPMA) + can map memory as non-cacheable bufferable PMA region. endmenu diff --git a/drivers/soc/andes/Makefile b/drivers/soc/andes/Makefile index 016e271c9593c4..c72c495a46205e 100644 --- a/drivers/soc/andes/Makefile +++ b/drivers/soc/andes/Makefile @@ -1,3 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023 Andes Technology Corporation. + +obj-y += andes_sbi.o +obj-$(CONFIG_ANDES_CACHE) += cache.o +obj-$(CONFIG_ANDES_PPMA) += ppma.o -obj-$(CONFIG_ANDES_CACHE) += cache.o diff --git a/drivers/soc/andes/andes_sbi.c b/drivers/soc/andes/andes_sbi.c new file mode 100644 index 00000000000000..ca0744623aa1c5 --- /dev/null +++ b/drivers/soc/andes/andes_sbi.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#include + +void sbi_andes_set_ppma(void *arg) +{ + phys_addr_t phys_addr = ((struct ppma_arg_t *)arg)->phys_addr; + unsigned long va_addr = ((struct ppma_arg_t *)arg)->va_addr; + size_t size = ((struct ppma_arg_t *)arg)->size; + + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_PPMA, + phys_addr, va_addr, size, 0, 0, 0); +} + +void sbi_andes_free_ppma(unsigned long addr) +{ + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_FREE_PPMA, + addr, 0, 0, 0, 0, 0); +} + +long sbi_andes_probe_ppma(void) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_PROBE_PPMA, + 0, 0, 0, 0, 0, 0); + return ret.value; +} diff --git a/drivers/soc/andes/ppma.c b/drivers/soc/andes/ppma.c new file mode 100644 index 00000000000000..aac3f0f7253dd2 --- /dev/null +++ b/drivers/soc/andes/ppma.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct ppma_arg_t ppma_arg; + +long andes_probe_ppma(void) +{ + return sbi_andes_probe_ppma(); +} +EXPORT_SYMBOL(andes_probe_ppma); + +void andes_set_ppma(phys_addr_t phys_addr, void *va_addr, size_t size) +{ + int cpu_num = num_online_cpus(); + int id = smp_processor_id(); + int i, err; + bool within_memory = pfn_valid(phys_addr >> PAGE_SHIFT); + + /* + * PPMA/Non-cacheability setting only + * when the given PA is within main memory range, + * and the given size is power of 2 (NAPOT). + */ + if (!within_memory) + return; + + if ((size & (size - 1)) != 0) { + printk("The value of size is not power of 2\n"); + BUG(); + } + + /* + * FIXME: this is not protected. + */ + ppma_arg.phys_addr = phys_addr; + ppma_arg.va_addr = (unsigned long)va_addr; + ppma_arg.size = size; + + /* + * Send IPI + * FIXME: we need online CPU mask, not the number + */ + for (i = 0; i < cpu_num; i++) { + if (i == id) + continue; + + err = smp_call_function_single(i, sbi_andes_set_ppma, + (void *)&ppma_arg, true); + + if (err) { + pr_err("Core %d fails to set ppma\nError Code: %d\n", + i, err); + } + } + + sbi_andes_set_ppma(&ppma_arg); +} +EXPORT_SYMBOL(andes_set_ppma); + +void andes_free_ppma(void *addr) +{ + sbi_andes_free_ppma((unsigned long)addr); +} +EXPORT_SYMBOL(andes_free_ppma); diff --git a/include/soc/andes/andes.h b/include/soc/andes/andes.h index 25adf893d706bd..9a3ba34cd2db19 100644 --- a/include/soc/andes/andes.h +++ b/include/soc/andes/andes.h @@ -6,6 +6,8 @@ #ifndef __SOC_ANDES_ANDES_H #define __SOC_ANDES_ANDES_H +#include + extern bool andes_legacy_mmu; extern phys_addr_t andes_pa_msb; diff --git a/include/soc/andes/ppma.h b/include/soc/andes/ppma.h new file mode 100644 index 00000000000000..0d21bec9ca6547 --- /dev/null +++ b/include/soc/andes/ppma.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#ifndef __SOC_ANDES_PPMA_H +#define __SOC_ANDES_PPMA_H + +#include + +#ifdef CONFIG_ANDES_PPMA + +struct ppma_arg_t { + phys_addr_t phys_addr; + unsigned long va_addr; + size_t size; +}; + +extern struct ppma_arg_t ppma_arg; + +extern void andes_set_ppma(phys_addr_t phys_addr, + void *va_addr, + size_t size); +extern void andes_free_ppma(void *addr); +extern long andes_probe_ppma(void); + +#endif /* CONFIG_ANDES_PPMA */ +#endif /* !__SOC_ANDES_PPMA_H */ diff --git a/include/soc/andes/sbi.h b/include/soc/andes/sbi.h index 7f6701cfeb3397..d71232eb8774eb 100644 --- a/include/soc/andes/sbi.h +++ b/include/soc/andes/sbi.h @@ -7,6 +7,7 @@ #include #include +#include #define SBI_EXT_ANDES (SBI_EXT_VENDOR_START | ANDES_VENDOR_ID) @@ -42,6 +43,17 @@ enum sbi_ext_andes_fid { SBI_EXT_ANDES_RESTART, SBI_EXT_ANDES_SET_RESET_VEC, + /* Programmable physical memory attributes (PPMA) */ + SBI_EXT_ANDES_SET_PPMA, + SBI_EXT_ANDES_FREE_PPMA, + SBI_EXT_ANDES_PROBE_PPMA, + SBI_EXT_ANDES_DCACHE_WBINVAL_ALL, }; + +/* Programmable physical memory attributes (PPMA) */ +void sbi_andes_set_ppma(void *arg); +void sbi_andes_free_ppma(unsigned long addr); +long sbi_andes_probe_ppma(void); + #endif /* !__SOC_ANDES_SBI_H */ From 1348154997c55cc7015ff9df3c69475f69207dd8 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Wed, 8 Mar 2023 15:42:37 +0800 Subject: [PATCH 012/169] soc: andes: dma-noncoherent: add Andes dma-noncoherent Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig | 2 - arch/riscv/configs/andes-support.config | 1 + arch/riscv/mm/dma-noncoherent.c | 16 +++++ drivers/soc/andes/Kconfig | 11 ++++ drivers/soc/andes/Makefile | 5 +- drivers/soc/andes/dma-noncoherent.c | 23 +++++++ include/soc/andes/dma.h | 83 +++++++++++++++++++++++++ 7 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 drivers/soc/andes/dma-noncoherent.c create mode 100644 include/soc/andes/dma.h diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index d702359f8ab5e6..146055ec6bfa56 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -118,7 +118,6 @@ config RISCV select MODULES_USE_ELF_RELA if MODULES select MODULE_SECTIONS if MODULES select OF - select OF_DMA_DEFAULT_COHERENT select OF_EARLY_FLATTREE select OF_IRQ select PCI_DOMAINS_GENERIC if PCI @@ -230,7 +229,6 @@ config RISCV_DMA_NONCOHERENT select ARCH_HAS_SYNC_DMA_FOR_DEVICE select ARCH_HAS_SYNC_DMA_FOR_CPU select ARCH_HAS_SETUP_DMA_OPS - select DMA_DIRECT_REMAP config AS_HAS_INSN def_bool $(as-instr,.insn r 51$(comma) 0$(comma) 0$(comma) t0$(comma) t0$(comma) zero) diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index ff6179d4f90337..62a644d1720420 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -9,4 +9,5 @@ CONFIG_FTMAC100=y CONFIG_MMC_FTSDCG=y CONFIG_ATCDMAC300G=y +CONFIG_RISCV_ISA_ZICBOM=y # CONFIG_RISCV_PMU is not set diff --git a/arch/riscv/mm/dma-noncoherent.c b/arch/riscv/mm/dma-noncoherent.c index d919efab6ebad6..e7142dd85aca57 100644 --- a/arch/riscv/mm/dma-noncoherent.c +++ b/arch/riscv/mm/dma-noncoherent.c @@ -9,6 +9,7 @@ #include #include #include +#include static bool noncoherent_supported; @@ -19,12 +20,18 @@ void arch_sync_dma_for_device(phys_addr_t paddr, size_t size, switch (dir) { case DMA_TO_DEVICE: + if (!noncoherent_supported) + andes_cache_op(paddr, size, cpu_dma_wb_range); ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size); break; case DMA_FROM_DEVICE: + if (!noncoherent_supported) + andes_cache_op(paddr, size, cpu_dma_inval_range); ALT_CMO_OP(clean, vaddr, size, riscv_cbom_block_size); break; case DMA_BIDIRECTIONAL: + if (!noncoherent_supported) + andes_cache_op(paddr, size, cpu_dma_wb_range); ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size); break; default: @@ -42,6 +49,8 @@ void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size, break; case DMA_FROM_DEVICE: case DMA_BIDIRECTIONAL: + if (!noncoherent_supported) + andes_cache_op(paddr, size, cpu_dma_inval_range); ALT_CMO_OP(flush, vaddr, size, riscv_cbom_block_size); break; default: @@ -53,12 +62,19 @@ void arch_dma_prep_coherent(struct page *page, size_t size) { void *flush_addr = page_address(page); + if (!noncoherent_supported) + dma_flush_page(page, size); ALT_CMO_OP(flush, flush_addr, size, riscv_cbom_block_size); } void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, const struct iommu_ops *iommu, bool coherent) { + if (!noncoherent_supported) { + dev->dma_coherent = coherent; + return; + } + WARN_TAINT(!coherent && riscv_cbom_block_size > ARCH_DMA_MINALIGN, TAINT_CPU_OUT_OF_SPEC, "%s %s: ARCH_DMA_MINALIGN smaller than riscv,cbom-block-size (%d < %d)", diff --git a/drivers/soc/andes/Kconfig b/drivers/soc/andes/Kconfig index fc77350757d4b8..cb3b61364419f5 100644 --- a/drivers/soc/andes/Kconfig +++ b/drivers/soc/andes/Kconfig @@ -9,6 +9,17 @@ config ANDES_CACHE help Support for the Andes L1 & L2 cache controller on AE350 platforms. +config ANDES_DMA_NONCOHERENT + bool "Andes DMA noncoherent support" + select ARCH_HAS_SYNC_DMA_FOR_DEVICE + select ARCH_HAS_SYNC_DMA_FOR_CPU + select ARCH_HAS_SETUP_DMA_OPS + select ARCH_HAS_DMA_PREP_COHERENT + select ARCH_HAS_DMA_SET_UNCACHED + depends on RISCV && PLAT_AE350 + help + Andes DMA noncoherent support + config ANDES_PPMA bool "Andes PPMA Support" depends on RISCV && PLAT_AE350 diff --git a/drivers/soc/andes/Makefile b/drivers/soc/andes/Makefile index c72c495a46205e..bcdbcfd42dbfb5 100644 --- a/drivers/soc/andes/Makefile +++ b/drivers/soc/andes/Makefile @@ -2,6 +2,7 @@ # Copyright (C) 2023 Andes Technology Corporation. obj-y += andes_sbi.o -obj-$(CONFIG_ANDES_CACHE) += cache.o -obj-$(CONFIG_ANDES_PPMA) += ppma.o +obj-$(CONFIG_ANDES_CACHE) += cache.o +obj-$(CONFIG_ANDES_DMA_NONCOHERENT) += dma-noncoherent.o +obj-$(CONFIG_ANDES_PPMA) += ppma.o diff --git a/drivers/soc/andes/dma-noncoherent.c b/drivers/soc/andes/dma-noncoherent.c new file mode 100644 index 00000000000000..0d9fc903eb0496 --- /dev/null +++ b/drivers/soc/andes/dma-noncoherent.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Andes Technology Corporation + */ + +#include +#include + +void *arch_dma_set_uncached(void *addr, size_t size) +{ + phys_addr_t phys; + + phys = page_to_phys(virt_to_page(addr)); + addr = ioremap_wc(phys, size); + return addr; +} +EXPORT_SYMBOL(arch_dma_set_uncached); + +void arch_dma_clear_uncached(void *addr, size_t size) +{ + iounmap(addr); +} +EXPORT_SYMBOL(arch_dma_clear_uncached); diff --git a/include/soc/andes/dma.h b/include/soc/andes/dma.h new file mode 100644 index 00000000000000..0e803451a3b8fd --- /dev/null +++ b/include/soc/andes/dma.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#ifndef __SOC_ANDES_DMA_H +#define __SOC_ANDES_DMA_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline void dma_flush_page(struct page *page, size_t size) +{ + unsigned long k_d_vaddr; + struct range_info ri; + + /* + * Invalidate any data that might be lurking in the + * kernel direct-mapped region for device DMA. + */ + k_d_vaddr = (unsigned long)page_address(page); + memset((void *)k_d_vaddr, 0, size); + ri.start = k_d_vaddr; + ri.end = k_d_vaddr + size; + ri.page = page; + cpu_dma_wb_range((void *)&ri); + cpu_dma_inval_range((void *)&ri); +} + +static inline void andes_cache_op(phys_addr_t paddr, size_t size, + void (*fn)(void *ri)) +{ + struct page *page = pfn_to_page(paddr >> PAGE_SHIFT); + unsigned offset = paddr & ~PAGE_MASK; + size_t left = size; + unsigned long start; + struct range_info ri; + + do { + size_t len = left; + + if (PageHighMem(page)) { + void *addr; + + if (offset + len > PAGE_SIZE) { + if (offset >= PAGE_SIZE) { + page += offset >> PAGE_SHIFT; + offset &= ~PAGE_MASK; + } + len = PAGE_SIZE - offset; + } + + addr = kmap_atomic(page); + start = (unsigned long)(addr + offset); + ri.start = start; + ri.end = start + len; + ri.page = page; + fn((struct range_info *)&ri); + kunmap_atomic(addr); + } else { + start = (unsigned long)phys_to_virt(paddr); + ri.start = start; + ri.end = start + size; + ri.page = page; + fn((struct range_info *)&ri); + } + offset = 0; + page++; + left -= len; + } while (left); +} + +extern void *arch_dma_set_uncached(void *addr, size_t size); +extern void arch_dma_clear_uncached(void *addr, size_t size); + +#endif /* !__SOC_ANDES_DMA_H */ From 5768a167aa14813ad20b348ae1ab85a1dad11191 Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Thu, 14 Oct 2021 15:15:33 +0800 Subject: [PATCH 013/169] dmaengine: andes: atcdmac300g: Andes support DMA engine Reformed from the following patches on RISCV-Linux-5.4: - (b455101301e2) atcdmac300: support generic dma flow Signed-off-by: Rick Chen Signed-off-by: Yu Chien Peter Lin Signed-off-by: Charles Ci-Jyun Wu --- drivers/dma/Kconfig | 10 + drivers/dma/Makefile | 1 + drivers/dma/atcdmac300g.c | 1073 ++++++++++++++++++++++++++ drivers/dma/atcdmac300g.h | 375 +++++++++ include/linux/platform_data/dma-v5.h | 21 + 5 files changed, 1480 insertions(+) create mode 100644 drivers/dma/atcdmac300g.c create mode 100644 drivers/dma/atcdmac300g.h create mode 100644 include/linux/platform_data/dma-v5.h diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 81de833ccd0417..d66d72f432b294 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -93,6 +93,15 @@ config APPLE_ADMAC help Enable support for Audio DMA Controller found on Apple Silicon SoCs. +config ATCDMAC300G + tristate "Andes generic DMA support" + depends on PLAT_AE350 + select DMA_ENGINE + select DMATEST + help + Support the Andes ATCDMAC300 controller for dmaengine. + It has the capability for DMA_SLAVE and DMA_MEMCPY. + config AT_HDMAC tristate "Atmel AHB DMA support" depends on ARCH_AT91 @@ -798,6 +807,7 @@ config ASYNC_TX_DMA config DMATEST tristate "DMA Test client" depends on DMA_ENGINE + depends on ATCDMAC300G select DMA_ENGINE_RAID help Simple DMA test client. Say N unless you're debugging a diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 10f7d424100176..1d301dc94cc82f 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ obj-$(CONFIG_AMD_PTDMA) += ptdma/ obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o obj-$(CONFIG_AT_HDMAC) += at_hdmac.o +obj-$(CONFIG_ATCDMAC300G) += atcdmac300g.o obj-$(CONFIG_AT_XDMAC) += at_xdmac.o obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o obj-$(CONFIG_BCM_SBA_RAID) += bcm-sba-raid.o diff --git a/drivers/dma/atcdmac300g.c b/drivers/dma/atcdmac300g.c new file mode 100644 index 00000000000000..01b380a07d9f80 --- /dev/null +++ b/drivers/dma/atcdmac300g.c @@ -0,0 +1,1073 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for the Andestech ATCDMAC300 + * + * Copyright (C) 2021 Andestech Corporation + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dmaengine.h" +#include "atcdmac300g.h" + +#define V5_DMA_BUSWIDTHS\ + (BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) |\ + BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |\ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |\ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + +static unsigned int init_nr_desc_per_channel = 64; +module_param(init_nr_desc_per_channel, uint, 0644); +MODULE_PARM_DESC(init_nr_desc_per_channel, + "initial descriptors per channel (default: 64)"); + +static inline void v5_en_channel(struct v5_dma_chan *v5chan) +{ + setbl(CHEN, v5chan->ch_regs+CH_CTL_OFF); +} + +static inline void v5_dis_channel(struct v5_dma_chan *v5chan) +{ + clrbl(CHEN, v5chan->ch_regs+CH_CTL_OFF); +} + +static dma_cookie_t v5_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct v5_desc *desc = txd_to_v5_desc(tx); + struct v5_dma_chan *v5chan = to_v5_dma_chan(tx->chan); + dma_cookie_t cookie; + unsigned long flags; + + spin_lock_irqsave(&v5chan->lock, flags); + cookie = dma_cookie_assign(tx); + list_add_tail(&desc->desc_node, &v5chan->queue); + spin_unlock_irqrestore(&v5chan->lock, flags); + + return cookie; +} + +static struct v5_desc *v5_first_active(struct v5_dma_chan *v5chan) +{ + return list_first_entry(&v5chan->active_list, + struct v5_desc, desc_node); +} + +static struct v5_desc *v5_first_queued(struct v5_dma_chan *v5chan) +{ + return list_first_entry(&v5chan->queue, + struct v5_desc, desc_node); +} + +/** + * v5_alloc_descriptor - allocate and return an initialized descriptor + * @chan: the channel to allocate descriptors for + * @gfp_flags: GFP allocation flags + */ +static struct v5_desc *v5_alloc_descriptor(struct dma_chan *chan, + gfp_t gfp_flags) +{ + struct v5_desc *desc = NULL; + struct v5_dma *v5dma = to_v5_dma(chan->device); + dma_addr_t phys; + + desc = dma_pool_zalloc(v5dma->dma_desc_pool, gfp_flags, &phys); + if (desc) { + INIT_LIST_HEAD(&desc->tx_list); + dma_async_tx_descriptor_init(&desc->txd, chan); + /* txd.flags will be overwritten in prep functions */ + desc->txd.flags = DMA_CTRL_ACK; + desc->txd.tx_submit = v5_tx_submit; + desc->txd.phys = phys; + } + + return desc; +} + +/** + * v5_desc_get - get an unused descriptor from free_list + * @v5chan: channel we want a new descriptor for + */ +static struct v5_desc *v5_desc_get(struct v5_dma_chan *v5chan) +{ + struct v5_desc *desc, *_desc; + struct v5_desc *ret = NULL; + unsigned long flags; + unsigned int i = 0; + + spin_lock_irqsave(&v5chan->lock, flags); + list_for_each_entry_safe(desc, _desc, &v5chan->free_list, desc_node) { + i++; + if (async_tx_test_ack(&desc->txd)) { + list_del(&desc->desc_node); + ret = desc; + break; + } + dev_dbg(chan2dev(&v5chan->chan_common), + "desc %p not ACKed\n", desc); + } + spin_unlock_irqrestore(&v5chan->lock, flags); + dev_vdbg(chan2dev(&v5chan->chan_common), + "scanned %u descriptors on freelist\n", i); + if (!ret) { + ret = v5_alloc_descriptor(&v5chan->chan_common, GFP_ATOMIC); + if (ret) { + spin_lock_irqsave(&v5chan->lock, flags); + v5chan->descs_allocated++; + spin_unlock_irqrestore(&v5chan->lock, flags); + } else { + dev_err(chan2dev(&v5chan->chan_common), + "not enough descriptors available\n"); + } + } + + return ret; +} + +/** + * v5_desc_put - move a descriptor, including any children, to the free list + * @v5chan: channel we work on + * @desc: descriptor, at the head of a chain, to move to free list + */ +static void v5_desc_put(struct v5_dma_chan *v5chan, struct v5_desc *desc) +{ + if (desc) { + struct v5_desc *child; + unsigned long flags; + + spin_lock_irqsave(&v5chan->lock, flags); + list_for_each_entry(child, &desc->tx_list, desc_node) + dev_vdbg(chan2dev(&v5chan->chan_common), + "moving child desc %p to freelist\n", + child); + list_splice_init(&desc->tx_list, &v5chan->free_list); + dev_vdbg(chan2dev(&v5chan->chan_common), + "moving desc %p to freelist\n", desc); + list_add(&desc->desc_node, &v5chan->free_list); + spin_unlock_irqrestore(&v5chan->lock, flags); + } +} + +/** + * v5_desc_chain - build chain adding a descriptor + * @first: address of first descriptor of the chain + * @prev: address of previous descriptor of the chain + * @desc: descriptor to queue + */ +static void v5_desc_chain(struct v5_desc **first, struct v5_desc **prev, + struct v5_desc *desc) +{ + if (!(*first)) { + *first = desc; + } else { + (*prev)->lli.llPointerl = lower_32_bits(desc->txd.phys); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + (*prev)->lli.llPointerh = upper_32_bits(desc->txd.phys); +#endif + /* insert the descriptor to the ring */ + list_add_tail(&desc->desc_node, &(*first)->tx_list); + } + *prev = desc; +} + +/** + * v5_dostart - starts the DMA engine for real + * @v5chan: the channel we want to start + * @first: first descriptor in the list we want to begin with + */ +static void v5_dostart(struct v5_dma_chan *v5chan, struct v5_desc *first) +{ + if (v5_chan_is_enabled(v5chan)) { + dev_err(chan2dev(&v5chan->chan_common), + "BUG: Attempted to start non-idle channel\n"); + dev_err(chan2dev(&v5chan->chan_common), + " channel: 0x%x 0x%x 0x%x 0x%x 0x%x\n", + v5_channel_readl(v5chan, CH_CTL_OFF), + v5_channel_readl(v5chan, CH_SIZE_OFF), + v5_channel_readl(v5chan, CH_SRC_LOW_OFF), + v5_channel_readl(v5chan, CH_DST_LOW_OFF), + v5_channel_readl(v5chan, CH_LLP_LOW_OFF)); + return; + } + vdbg_dump_regs(v5chan); + v5_channel_writel(v5chan, CH_CTL_OFF, first->lli.ctrl); + v5_channel_writel(v5chan, CH_SIZE_OFF, first->lli.tranSize); + v5_channel_writel(v5chan, CH_SRC_LOW_OFF, first->lli.srcAddrl); + v5_channel_writel(v5chan, CH_DST_LOW_OFF, first->lli.dstAddrl); + v5_channel_writel(v5chan, CH_LLP_LOW_OFF, first->lli.llPointerl); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + v5_channel_writel(v5chan, CH_SRC_HIGH_OFF, first->lli.srcAddrh); + v5_channel_writel(v5chan, CH_DST_HIGH_OFF, first->lli.dstAddrh); + v5_channel_writel(v5chan, CH_LLP_HIGH_OFF, first->lli.llPointerh); +#endif + v5_en_channel(v5chan); +} + +/** + * v5_chain_complete - finish work for one transaction chain + * @v5chan: channel we work on + * @desc: descriptor at the head of the chain we want do complete + * @result: dma result + */ +static void v5_chain_complete(struct v5_dma_chan *v5chan, + struct v5_desc *desc, enum dmaengine_tx_result result) +{ + struct dma_async_tx_descriptor *txd = &desc->txd; + struct dmaengine_result res; + + res.result = result; + dev_vdbg(chan2dev(&v5chan->chan_common), + "descriptor %u complete\n", txd->cookie); + dma_cookie_complete(txd); + /* move children to free_list */ + list_splice_init(&desc->tx_list, &v5chan->free_list); + /* move myself to free_list */ + list_move(&desc->desc_node, &v5chan->free_list); + dma_descriptor_unmap(txd); + dmaengine_desc_get_callback_invoke(txd, &res); + dma_run_dependencies(txd); +} + +/** + * v5_complete_all - finish work for all transactions + * @v5chan: channel to complete transactions for + */ +static void v5_complete_all(struct v5_dma_chan *v5chan) +{ + struct v5_desc *desc, *_desc; + LIST_HEAD(list); + + dev_vdbg(chan2dev(&v5chan->chan_common), "complete all\n"); + if (!list_empty(&v5chan->queue)) + v5_dostart(v5chan, v5_first_queued(v5chan)); + /* empty active_list now it is completed */ + list_splice_init(&v5chan->active_list, &list); + /* empty queue list by moving descriptors (if any) to active_list */ + list_splice_init(&v5chan->queue, &v5chan->active_list); + + list_for_each_entry_safe(desc, _desc, &list, desc_node) + v5_chain_complete(v5chan, desc, DMA_TRANS_NOERROR); +} + +/** + * v5_advance_work - at the end of a transaction, move forward + * @v5chan: channel where the transaction ended + */ +static void v5_advance_work(struct v5_dma_chan *v5chan) +{ + dev_vdbg(chan2dev(&v5chan->chan_common), "advance_work\n"); + clear_bit(V5_IS_TC, &v5chan->status); + if (v5_chan_is_enabled(v5chan)) + return; + + if (list_empty(&v5chan->active_list) || + list_is_singular(&v5chan->active_list)) { + v5_complete_all(v5chan); + } else { + v5_chain_complete(v5chan, v5_first_active(v5chan), + DMA_TRANS_NOERROR); + /* advance work */ + v5_dostart(v5chan, v5_first_active(v5chan)); + } +} + +/** + * v5_handle_error - handle errors reported by DMA controller + * @v5chan: channel where error occurs + */ +static void v5_handle_error(struct v5_dma_chan *v5chan) +{ + struct v5_desc *bad_desc; + + clear_bit(V5_IS_ERR, &v5chan->status); + /* + * The descriptor currently at the head of the active list is + * broked. Since we don't have any way to report errors, we'll + * just have to scream loudly and try to carry on. + */ + bad_desc = v5_first_active(v5chan); + list_del_init(&bad_desc->desc_node); + + /* + * As we are stopped, take advantage + * to push queued descriptors in active_list + */ + list_splice_init(&v5chan->queue, v5chan->active_list.prev); + + /* Try to restart the controller */ + if (!list_empty(&v5chan->active_list)) + v5_dostart(v5chan, v5_first_active(v5chan)); + + /* + * KERN_CRITICAL may seem harsh, but since this only happens + * when someone submits a bad physical address in a + * descriptor, we should consider ourselves lucky that the + * controller flagged an error instead of scribbling over + * random memory locations. + */ + dev_crit(chan2dev(&v5chan->chan_common), + "Bad descriptor submitted for DMA!\n"); + dev_crit(chan2dev(&v5chan->chan_common), + " cookie: %d\n", bad_desc->txd.cookie); + /* Report the descriptor that dma abort */ + v5_chain_complete(v5chan, bad_desc, DMA_TRANS_ABORTED); +} + +static void v5_tasklet(unsigned long data) +{ + struct v5_dma_chan *v5chan = (struct v5_dma_chan *)data; + unsigned long flags; + + spin_lock_irqsave(&v5chan->lock, flags); + if (test_and_clear_bit(V5_IS_ERR, &v5chan->status)) + v5_handle_error(v5chan); + else + v5_advance_work(v5chan); + spin_unlock_irqrestore(&v5chan->lock, flags); +} + +static irqreturn_t v5_dma_interrupt(int irq, void *dev_id) +{ + struct v5_dma *v5dma = (struct v5_dma *)dev_id; + struct v5_dma_chan *v5chan; + int i; + u32 status; + int ret = IRQ_NONE; + + do { + status = v5_dma_readl(v5dma, INT_STA); + + dev_vdbg(v5dma->dma_common.dev, + "int: sta = 0x%08x\n", status); + for (i = 0; i < v5dma->dma_common.chancnt; i++) { + v5chan = &v5dma->chan[i]; + if (status & (V5_DMA_TC(i))) + set_bit(V5_IS_TC, &v5chan->status); + + if (status & (V5_DMA_ABT(i) | V5_DMA_ERR(i))) + set_bit(V5_IS_ERR, &v5chan->status); + + if (v5chan->status) { + tasklet_schedule(&v5chan->tasklet); + ret = IRQ_HANDLED; + } + } + v5_dma_writel(v5dma, INT_STA, status); + status = 0; + } while (status); + + return ret; +} + +static void v5_issue_pending(struct dma_chan *chan) +{ + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&v5chan->lock, flags); + if (list_empty(&v5chan->active_list)) + list_move(v5chan->queue.next, &v5chan->active_list); + v5_dostart(v5chan, v5_first_active(v5chan)); + spin_unlock_irqrestore(&v5chan->lock, flags); +} + +static inline unsigned int xfer_width(struct v5_dma *v5dma, + dma_addr_t src, dma_addr_t dst, size_t len) +{ + unsigned int width; + + if (!((src | dst | len) & 15)) + width = 4; + else if (!((src | dst | len) & 7)) + width = 3; + else if (!((src | dst | len) & 3)) + width = 2; + else if (!((src | dst | len) & 1)) + width = 1; + else + width = 0; + + width = width > v5dma->data_width ? + v5dma->data_width : width; + + return width; +} + +/** + * v5_prep_dma_memcpy - prepare a memcpy operation + * @chan: the channel to prepare operation on + * @dest: operation virtual destination address + * @src: operation virtual source address + * @len: operation length + * @flags: tx descriptor status flags + */ +static struct dma_async_tx_descriptor * +v5_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, + size_t len, unsigned long flags) +{ + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + struct v5_desc *desc; + unsigned int src_width; + unsigned int dst_width; + u32 src_maxburst; + u32 ctrl; + + struct v5_dma *v5dma = to_v5_dma(chan->device); + + desc = v5_desc_get(v5chan); + if (!desc) + goto err_desc_get; + + src_maxburst = DMAC_CSR_SIZE_1024; + src_width = dst_width = xfer_width(v5dma, src, dst, len); + convert_burst(&src_maxburst); + ctrl = v5_channel_readl(v5chan, CH_CTL_OFF); + ctrl &= ~(SRCWIDTH_MASK|SRCADDRCTRL_MASK|DSTWIDTH_MASK| + DSTADDRCTRL_MASK|SRC_HS|DST_HS|SRCREQSEL_MASK|DSTREQSEL_MASK); + ctrl = SBSIZE(src_maxburst); + ctrl |= SRC_WIDTH(src_width); + ctrl |= DST_WIDTH(dst_width); + ctrl |= (SRC_ADDR_MODE_INCR | DST_ADDR_MODE_INCR); + desc->lli.srcAddrl = lower_32_bits(src); + desc->lli.dstAddrl = lower_32_bits(dst); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + desc->lli.srcAddrh = upper_32_bits(src); + desc->lli.dstAddrh = upper_32_bits(dst); +#endif + desc->lli.ctrl = ctrl; + desc->lli.tranSize = (len >> src_width); + + return &desc->txd; + +err_desc_get: + v5_desc_put(v5chan, desc); + return NULL; +} + +/** + * v5_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction + * @chan: DMA channel + * @sgl: scatterlist to transfer to/from + * @sg_len: number of entries in @scatterlist + * @direction: DMA direction + * @flags: tx descriptor status flags + * @context: transaction context (ignored) + */ +static struct dma_async_tx_descriptor * +v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + struct v5_dma_slave *v5slave = chan->private; + struct dma_slave_config *sconfig = &v5chan->dma_sconfig; + struct v5_desc *first = NULL; + struct v5_desc *prev = NULL; + u32 ctrl; + dma_addr_t reg; + unsigned int reg_width; + unsigned int i; + struct scatterlist *sg; + size_t total_len = 0; + + dev_vdbg(chan2dev(chan), "prep_slave_sg (%d): %s f0x%lx\n", + sg_len, + direction == DMA_MEM_TO_DEV ? + "TO DEVICE" : "FROM DEVICE", flags); + if (unlikely(!v5slave || !sg_len)) { + dev_dbg(chan2dev(chan), "prep_slave_sg: sg length is zero!\n"); + return NULL; + } + ctrl = v5_channel_readl(v5chan, CH_CTL_OFF); + ctrl &= ~(SRCWIDTH_MASK|SRCADDRCTRL_MASK|DSTWIDTH_MASK| + DSTADDRCTRL_MASK|SRC_HS|DST_HS|SRCREQSEL_MASK|DSTREQSEL_MASK); + ctrl = SBSIZE(sconfig->src_maxburst); + reg_width = convert_buswidth(sconfig->src_addr_width); + ctrl |= SRC_WIDTH(reg_width); + reg_width = convert_buswidth(sconfig->dst_addr_width); + ctrl |= DST_WIDTH(reg_width); + + switch (direction) { + case DMA_MEM_TO_DEV: + ctrl |= DST_HS; + ctrl |= (SRC_ADDR_MODE_INCR | DST_ADDR_MODE_FIXED); + ctrl |= ((v5chan->req_num << DSTREQSEL) & DSTREQSEL_MASK); + reg = sconfig->dst_addr; + for_each_sg(sgl, sg, sg_len, i) { + struct v5_desc *desc; + u32 len; + dma_addr_t mem; + + desc = v5_desc_get(v5chan); + if (!desc) + goto err_desc_get; + + mem = sg_dma_address(sg); + len = sg_dma_len(sg); + if (unlikely(!len)) { + dev_dbg(chan2dev(chan), + "sg(%d) data length is zero\n", i); + goto err; + } + if (unlikely(mem & 3 || len & 3)) { + dev_dbg(chan2dev(chan), + "sg(%d) data length is not aligned\n", i); + goto err_desc_get; + } + desc->lli.srcAddrl = lower_32_bits(mem); + desc->lli.dstAddrl = lower_32_bits(reg); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + desc->lli.srcAddrh = upper_32_bits(mem); + desc->lli.dstAddrh = upper_32_bits(reg); +#endif + desc->lli.ctrl = ctrl; + desc->lli.tranSize = (len >> sconfig->src_maxburst); + v5_desc_chain(&first, &prev, desc); + total_len += len; + } + break; + + case DMA_DEV_TO_MEM: + ctrl |= SRC_HS; + ctrl |= (SRC_ADDR_MODE_FIXED | DST_ADDR_MODE_INCR); + ctrl |= ((v5chan->req_num << SRCREQSEL) & SRCREQSEL_MASK); + reg = sconfig->src_addr; + for_each_sg(sgl, sg, sg_len, i) { + struct v5_desc *desc; + u32 len; + u32 mem; + + desc = v5_desc_get(v5chan); + if (!desc) + goto err_desc_get; + mem = sg_dma_address(sg); + len = sg_dma_len(sg); + if (unlikely(!len)) { + dev_dbg(chan2dev(chan), + "sg(%d) data length is zero\n", i); + goto err; + } + if (unlikely(mem & 3 || len & 3)) { + dev_dbg(chan2dev(chan), + "sg(%d) data length is not aligned\n", i); + goto err; + } + desc->lli.srcAddrl = lower_32_bits(reg); + desc->lli.dstAddrl = lower_32_bits(mem); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + desc->lli.srcAddrh = upper_32_bits(reg); + desc->lli.dstAddrh = upper_32_bits(mem); +#endif + desc->lli.ctrl = ctrl; + desc->lli.tranSize = (len >> sconfig->src_maxburst); + v5_desc_chain(&first, &prev, desc); + total_len += len; + } + break; + default: + return NULL; + } + /* First descriptor of the chain embedds additional information */ + first->txd.cookie = -EBUSY; + first->total_len = total_len; + + /* first link descriptor of list is responsible of flags */ + first->txd.flags = flags; + + return &first->txd; + +err_desc_get: + dev_err(chan2dev(chan), "not enough descriptors available\n"); +err: + v5_desc_put(v5chan, first); + return NULL; +} + +static int v5_config(struct dma_chan *chan, + struct dma_slave_config *sconfig) +{ + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + + dev_vdbg(chan2dev(chan), "%s\n", __func__); + /* Check if it is chan is configured for slave transfers */ + if (!chan->private) + return -EINVAL; + + memcpy(&v5chan->dma_sconfig, sconfig, sizeof(*sconfig)); + convert_burst(&v5chan->dma_sconfig.src_maxburst); + convert_burst(&v5chan->dma_sconfig.dst_maxburst); + + return 0; +} + + +static int v5_terminate_all(struct dma_chan *chan) +{ + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + struct v5_desc *desc, *_desc; + unsigned long flags; + LIST_HEAD(list); + + dev_vdbg(chan2dev(chan), "%s\n", __func__); + /* + * This is only called when something went wrong elsewhere, so + * we don't really care about the data. Just disable the + * channel. + */ + spin_lock_irqsave(&v5chan->lock, flags); + + /* disabling channel: must also remove suspend state */ + v5_dis_channel(v5chan); + /* confirm that this channel is disabled */ + while (v5_chan_is_enabled(v5chan)) + cpu_relax(); + /* active_list entries will end up before queued entries */ + list_splice_init(&v5chan->queue, &list); + list_splice_init(&v5chan->active_list, &list); + + /* Flush all pending and queued descriptors */ + list_for_each_entry_safe(desc, _desc, &list, desc_node) + v5_chain_complete(v5chan, desc, DMA_TRANS_NOERROR); + + clear_bit(V5_IS_PAUSED, &v5chan->status); + spin_unlock_irqrestore(&v5chan->lock, flags); + + return 0; +} + +/** + * v5_tx_status - poll for transaction completion + * @chan: DMA channel + * @cookie: transaction identifier to check status of + * @txstate: if not %NULL updated with transaction state + */ +static enum dma_status +v5_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + enum dma_status ret; + + if (test_and_clear_bit(V5_IS_TC, &v5chan->status)) + return DMA_COMPLETE; + + if (test_and_clear_bit(V5_IS_ERR, &v5chan->status)) + return DMA_ERROR; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_COMPLETE) + return ret; + /* + * There's no point calculating the residue if there's + * no txstate to store the value. + */ + if (!txstate) + return DMA_ERROR; + + return ret; +} + +/** + * v5_alloc_chan_resources - allocate resources for DMA channel + * @chan: allocate descriptor resources for this channel + * @client: current client requesting the channel be ready for requests + * + * return - the number of allocated descriptors + */ +static int v5_alloc_chan_resources(struct dma_chan *chan) +{ + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + struct v5_dma *v5dma = to_v5_dma(chan->device); + struct v5_desc *desc; + unsigned long flags; + int i; + LIST_HEAD(tmp_list); + + dev_vdbg(chan2dev(chan), "alloc_chan_resources\n"); + + /* ASSERT: channel is idle */ + if (v5_chan_is_enabled(v5chan)) { + dev_dbg(chan2dev(chan), "DMA channel not idle ?\n"); + return -EIO; + } + + /* + * have we already been set up? + * reconfigure channel but no need to reallocate descriptors + */ + if (!list_empty(&v5chan->free_list)) + return v5chan->descs_allocated; + + /* Allocate initial pool of descriptors */ + for (i = 0; i < init_nr_desc_per_channel; i++) { + desc = v5_alloc_descriptor(chan, GFP_KERNEL); + if (!desc) { + dev_err(v5dma->dma_common.dev, + "Only %d initial descriptors\n", i); + break; + } + list_add_tail(&desc->desc_node, &tmp_list); + } + + spin_lock_irqsave(&v5chan->lock, flags); + v5chan->descs_allocated = i; + list_splice(&tmp_list, &v5chan->free_list); + dma_cookie_init(chan); + spin_unlock_irqrestore(&v5chan->lock, flags); + + dev_dbg(chan2dev(chan), + "alloc_chan_resources: allocated %d descriptors\n", + v5chan->descs_allocated); + + return v5chan->descs_allocated; +} + +/** + * v5_free_chan_resources - free all channel resources + * @chan: DMA channel + */ +static void v5_free_chan_resources(struct dma_chan *chan) +{ + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + struct v5_dma *v5dma = to_v5_dma(chan->device); + struct v5_desc *desc, *_desc; + LIST_HEAD(list); + + dev_dbg(chan2dev(chan), "free_chan_resources: (descs allocated=%u)\n", + v5chan->descs_allocated); + + /* ASSERT: channel is idle */ + WARN_ON(!list_empty(&v5chan->active_list)); + WARN_ON(!list_empty(&v5chan->queue)); + WARN_ON(v5_chan_is_enabled(v5chan)); + + list_for_each_entry_safe(desc, _desc, &v5chan->free_list, desc_node) { + dev_vdbg(chan2dev(chan), " freeing descriptor %p\n", desc); + list_del(&desc->desc_node); + /* free link descriptor */ + dma_pool_free(v5dma->dma_desc_pool, desc, desc->txd.phys); + } + list_splice_init(&v5chan->free_list, &list); + v5chan->descs_allocated = 0; + v5chan->status = 0; + kfree(chan->private); + chan->private = NULL; + dev_vdbg(chan2dev(chan), "free_chan_resources: done\n"); +} + +#ifdef CONFIG_OF +static bool v5_dma_filter(struct dma_chan *chan, void *slave) +{ + struct v5_dma_slave *atslave = slave; + + if (atslave->dma_dev == chan->device->dev) { + chan->private = atslave; + + return true; + } else + return false; +} +static struct dma_chan *v5_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *of_dma) +{ + struct dma_chan *chan; + + struct v5_dma_chan *v5chan; + struct v5_dma_slave *v5slave; + dma_cap_mask_t mask; + struct platform_device *dmac_pdev; + + dmac_pdev = of_find_device_by_node(dma_spec->np); + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + v5slave = kzalloc(sizeof(*v5slave), GFP_KERNEL); + if (!v5slave) + return NULL; + + v5slave->dma_dev = &dmac_pdev->dev; + chan = dma_request_channel(mask, v5_dma_filter, v5slave); + if (!chan) + return NULL; + + v5chan = to_v5_dma_chan(chan); + v5chan->req_num = dma_spec->args[0] & 0xff; + + return chan; +} +#else +static struct dma_chan *v5_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *of_dma) +{ + return NULL; +} +#endif + +static struct v5_dma_platform_data v5dma_config = { + .nr_channels = 8, +}; + +#if defined(CONFIG_OF) +static const struct of_device_id v5_dma_dt_ids[] = { + { + .compatible = "andestech,atcdmac300g", + .data = &v5dma_config, + }, { + /* sentinel */ + } +}; + +MODULE_DEVICE_TABLE(of, v5_dma_dt_ids); +#endif + +static const struct platform_device_id v5dma_devtypes[] = { + { + .name = "v5dma", + .driver_data = (unsigned long) &v5dma_config, + }, { + /* sentinel */ + } +}; + +static inline const struct v5_dma_platform_data * __init v5_dma_get_driver_data( + struct platform_device *pdev) +{ + if (pdev->dev.of_node) { + const struct of_device_id *match; + + match = of_match_node(v5_dma_dt_ids, pdev->dev.of_node); + if (match == NULL) + return NULL; + return match->data; + } + return (struct v5_dma_platform_data *) + platform_get_device_id(pdev)->driver_data; +} + +/** + * v5_dma_off - disable DMA controller + */ +static void v5_dma_off(struct v5_dma *v5dma) +{ + /* reset the DMA */ + setbl(CHEN, v5dma->regs+CTL); + /* disable all channels */ + clrbl(CHEN, v5dma->regs+CTL); + /* confirm that all channels are disabled */ + while (v5_dma_readl(v5dma, CH_EN) & v5dma->all_chan_mask) + cpu_relax(); +} + +static int __init v5_dma_probe(struct platform_device *pdev) +{ + struct resource *io; + struct v5_dma *v5dma; + size_t size; + int irq; + int err; + int i; + const struct v5_dma_platform_data *plat_dat; + + dma_cap_set(DMA_SLAVE, v5dma_config.cap_mask); + dma_cap_set(DMA_MEMCPY, v5dma_config.cap_mask); + + /* get DMA parameters from controller type */ + plat_dat = v5_dma_get_driver_data(pdev); + if (!plat_dat) + return -ENODEV; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!io) + return -EINVAL; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + size = sizeof(struct v5_dma); + size += plat_dat->nr_channels * sizeof(struct v5_dma_chan); + v5dma = kzalloc(size, GFP_KERNEL); + if (!v5dma) + return -ENOMEM; + + /* discover transaction capabilities */ + v5dma->dma_common.cap_mask = plat_dat->cap_mask; + v5dma->all_chan_mask = (1 << plat_dat->nr_channels) - 1; + size = resource_size(io); + if (!request_mem_region(io->start, size, pdev->dev.driver->name)) { + err = -EBUSY; + goto err_kfree; + } + + v5dma->regs = ioremap(io->start, size); + if (!v5dma->regs) { + err = -ENOMEM; + goto err_release_r; + } + v5dma->ctl = v5_dma_readl(v5dma, CFG); + v5dma->ch = (v5dma->ctl & CH_NUM); + v5dma->data_width = ((v5dma->ctl & DATA_WIDTH)>>DATA_WIDTH_OFF) + 2; + v5dma->ch = (plat_dat->nr_channels > v5dma->ch) ? + v5dma->ch : plat_dat->nr_channels; + + v5_dma_off(v5dma); + err = request_irq(irq, v5_dma_interrupt, 0, "v5_dmac", v5dma); + if (err) + goto err_irq; + + platform_set_drvdata(pdev, v5dma); + /* create a pool of consistent memory blocks for hardware descriptors */ + v5dma->dma_desc_pool = dma_pool_create("v5_desc_pool", + &pdev->dev, sizeof(struct v5_desc), + 4 /* word alignment */, 0); + if (!v5dma->dma_desc_pool) { + dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); + err = -ENOMEM; + goto err_desc_pool_create; + } + /* initialize channels related values */ + INIT_LIST_HEAD(&v5dma->dma_common.channels); + for (i = 0; i < v5dma->ch; i++) { + struct v5_dma_chan *v5chan = &v5dma->chan[i]; + + v5chan->chan_common.device = &v5dma->dma_common; + dma_cookie_init(&v5chan->chan_common); + list_add_tail(&v5chan->chan_common.device_node, + &v5dma->dma_common.channels); + v5chan->ch_regs = v5dma->regs + ch_regs(i); + spin_lock_init(&v5chan->lock); + v5chan->mask = 1 << i; + INIT_LIST_HEAD(&v5chan->active_list); + INIT_LIST_HEAD(&v5chan->queue); + INIT_LIST_HEAD(&v5chan->free_list); + tasklet_init(&v5chan->tasklet, v5_tasklet, + (unsigned long)v5chan); + v5_enable_chan_irq(v5dma, i); + } + /* set base routines */ + v5dma->dma_common.device_alloc_chan_resources = v5_alloc_chan_resources; + v5dma->dma_common.device_free_chan_resources = v5_free_chan_resources; + v5dma->dma_common.device_tx_status = v5_tx_status; + v5dma->dma_common.device_issue_pending = v5_issue_pending; + v5dma->dma_common.dev = &pdev->dev; + if (dma_has_cap(DMA_MEMCPY, v5dma->dma_common.cap_mask)) + v5dma->dma_common.device_prep_dma_memcpy = v5_prep_dma_memcpy; + + if (dma_has_cap(DMA_SLAVE, v5dma->dma_common.cap_mask)) { + v5dma->dma_common.device_prep_slave_sg = v5_prep_slave_sg; + v5dma->dma_common.device_config = v5_config; + v5dma->dma_common.device_terminate_all = v5_terminate_all; + v5dma->dma_common.src_addr_widths = V5_DMA_BUSWIDTHS; + v5dma->dma_common.dst_addr_widths = V5_DMA_BUSWIDTHS; + v5dma->dma_common.directions = BIT(DMA_DEV_TO_MEM) + | BIT(DMA_MEM_TO_DEV); + v5dma->dma_common.residue_granularity = + DMA_RESIDUE_GRANULARITY_BURST; + } + dev_info(&pdev->dev, "Atcdmac300 DMA Controller (%s,%s), %d channels\n", + dma_has_cap(DMA_MEMCPY, v5dma->dma_common.cap_mask) ? "cpy" : "", + dma_has_cap(DMA_SLAVE, v5dma->dma_common.cap_mask) ? "slave" : "", + v5dma->ch); + dma_async_device_register(&v5dma->dma_common); + + /* + * Do not return an error if the dmac node is not present in order to + * not break the existing way of requesting channel with + * dma_request_channel(). + */ + if (pdev->dev.of_node) { + err = of_dma_controller_register(pdev->dev.of_node, + v5_dma_xlate, v5dma); + if (err) { + dev_err(&pdev->dev, "could not register of_dma_controller\n"); + goto err_of_dma_controller_register; + } + } + + return 0; + +err_of_dma_controller_register: + dma_async_device_unregister(&v5dma->dma_common); + dma_pool_destroy(v5dma->dma_desc_pool); +err_desc_pool_create: + free_irq(platform_get_irq(pdev, 0), v5dma); +err_irq: + iounmap(v5dma->regs); + v5dma->regs = NULL; +err_release_r: + release_mem_region(io->start, size); +err_kfree: + kfree(v5dma); + return err; +} + +static int v5_dma_remove(struct platform_device *pdev) +{ + struct v5_dma *v5dma = platform_get_drvdata(pdev); + struct dma_chan *chan, *_chan; + struct resource *io; + + v5_dma_off(v5dma); + if (pdev->dev.of_node) + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&v5dma->dma_common); + dma_pool_destroy(v5dma->dma_desc_pool); + free_irq(platform_get_irq(pdev, 0), v5dma); + + list_for_each_entry_safe(chan, _chan, &v5dma->dma_common.channels, + device_node) { + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + + /* Disable interrupts */ + v5_disable_chan_irq(v5dma, chan->chan_id); + tasklet_kill(&v5chan->tasklet); + list_del(&chan->device_node); + } + + iounmap(v5dma->regs); + v5dma->regs = NULL; + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(io->start, resource_size(io)); + kfree(v5dma); + + return 0; +} + +static void v5_dma_shutdown(struct platform_device *pdev) +{ + v5_dma_off(platform_get_drvdata(pdev)); +} + +static struct platform_driver v5_dma_driver = { + .remove = v5_dma_remove, + .shutdown = v5_dma_shutdown, + .id_table = v5dma_devtypes, + .driver = { + .name = "v5dmac", + .of_match_table = of_match_ptr(v5_dma_dt_ids), + }, +}; + +static int __init v5_dma_init(void) +{ + return platform_driver_probe(&v5_dma_driver, v5_dma_probe); +} + +subsys_initcall(v5_dma_init); + +static void __exit v5_dma_exit(void) +{ + platform_driver_unregister(&v5_dma_driver); +} +module_exit(v5_dma_exit); +MODULE_DESCRIPTION("Andestech ATCDMAC300 Controller driver"); +MODULE_AUTHOR("Rick Chen "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:v5dmac"); diff --git a/drivers/dma/atcdmac300g.h b/drivers/dma/atcdmac300g.h new file mode 100644 index 00000000000000..849880ff40838c --- /dev/null +++ b/drivers/dma/atcdmac300g.h @@ -0,0 +1,375 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Header file for the Andestech DMA Controller driver + * + * Copyright (C) 2021 Andestech Corporation + */ +#ifndef ATCDMAC300G_H +#define ATCDMAC300G_H + +#include + +typedef u32 addr_t; + +static inline void setbl(addr_t bit, void __iomem *reg) +{ + writel(readl(reg) | (addr_t) bit, reg); +} + +static inline void clrbl(addr_t bit, void __iomem *reg) +{ + writel(readl(reg) & (~((addr_t) bit)), reg); +} + +#define DMAC_BASE (0) + +/* ID and Revision Register */ +#define ID_REV (DMAC_BASE + 0x00) +/* DMAC Configuration Register*/ +#define CFG (DMAC_BASE + 0x10) +#define CH_NUM GENMASK(3, 0) +#define DATA_WIDTH GENMASK(25, 24) +#define DATA_WIDTH_OFF 24 +#define REQSYNC 30 +#define CTL (DMAC_BASE + 0x20) +#define CH_ABT (DMAC_BASE + 0x24) +/* Interrupt Status Register */ +#define INT_STA (DMAC_BASE + 0x30) +#define TC_OFFSET 16 +#define ABT_OFFSET 8 +#define ERR_OFFSET 0 +#define V5_DMA_TC(x) (0x1 << (TC_OFFSET + (x))) +#define V5_DMA_ABT(x) (0x1 << (ABT_OFFSET + (x))) +#define V5_DMA_ERR(x) (0x1 << (ERR_OFFSET + (x))) +/* Channel Enable Register */ +#define CH_EN (DMAC_BASE + 0x34) +/* Channel Base Register */ +#define DMAC_CH_OFFSET 0x40 +#define CH_CTL_OFF 0x0 +#define CH_SIZE_OFF 0x4 +#define CH_SRC_LOW_OFF 0x8 +#define CH_SRC_HIGH_OFF 0xc +#define CH_DST_LOW_OFF 0x10 +#define CH_DST_HIGH_OFF 0x14 +#define CH_LLP_LOW_OFF 0x18 +#define CH_LLP_HIGH_OFF 0x1c +/* Channel x base addr */ +#define ch_regs(x) (DMAC_CH_OFFSET + (x) * 0x20) + + +/* Channel n Control Register */ +#define PRIORITY_SHIFT 29 +#define PRIORITY_LOW 0 +#define PRIORITY_HIGH 1 +#define DMAC_CSR_CHPRI_0 PRIORITY_LOW +#define DMAC_CSR_CHPRI_1 PRIORITY_LOW +#define DMAC_CSR_CHPRI_2 PRIORITY_HIGH +#define DMAC_CSR_CHPRI_3 PRIORITY_HIGH + +/* Maximum Buffer Transfer Size */ +#define ATC_BTSIZE_MAX 0xFFFFUL +/* Buffer Transfer Size */ +#define ATC_BTSIZE(x) (ATC_BTSIZE_MAX & (x)) +/* Source Chunk Transfer Size */ +#define ATC_SCSIZE_MASK (0x7 << 16) +#define ATC_SCSIZE(x) (ATC_SCSIZE_MASK & ((x) << 16)) +#define SBURST_SIZE_SHIFT 24 +#define SBURST_SIZE_MASK (0xf<ch_regs + name) + +#define v5_channel_writel(v5chan, name, val) \ + __raw_writel((val), (v5chan)->ch_regs + name) + +static inline struct v5_dma_chan *to_v5_dma_chan(struct dma_chan *dchan) +{ + return container_of(dchan, struct v5_dma_chan, chan_common); +} + +/* + * Note fls(0) = 0, fls(1) = 1, fls(0x8) = 4. + */ +static inline void convert_burst(u32 *maxburst) +{ + *maxburst = fls(*maxburst) - 1; +} + + +/* + * Fix sconfig's bus width + * 1 byte -> 0, 2 bytes -> 1, 4 bytes -> 2. + */ +static inline u8 convert_buswidth(enum dma_slave_buswidth addr_width) +{ + switch (addr_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + return 0; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + return 1; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + return 2; + case DMA_SLAVE_BUSWIDTH_8_BYTES: + return 3; + case DMA_SLAVE_BUSWIDTH_16_BYTES: + return 4; + case DMA_SLAVE_BUSWIDTH_32_BYTES: + return 5; + default: + /* fallback */ + return 0; + } +} + +struct v5_dma { + struct dma_device dma_common; + void __iomem *regs; + u32 ctl; + u8 data_width; + u8 ch; + u8 all_chan_mask; + struct dma_pool *dma_desc_pool; + struct v5_dma_chan chan[0]; +}; + +#define v5_dma_readl(v5_dma, name) \ + __raw_readl((v5_dma)->regs + name) +#define v5_dma_writel(v5_dma, name, val) \ + __raw_writel((val), (v5_dma)->regs + name) + +static inline struct v5_dma *to_v5_dma(struct dma_device *ddev) +{ + return container_of(ddev, struct v5_dma, dma_common); +} + +static struct device *chan2dev(struct dma_chan *chan) +{ + return &chan->dev->device; +} + +#if defined(VERBOSE_DEBUG) +static void vdbg_dump_regs(struct v5_dma_chan *v5chan) +{ + struct v5_dma *v5_dma = to_v5_dma(v5chan->chan_common.device); + + dev_err(chan2dev(&v5chan->chan_common), + " channel %d : sta = 0x%x, en = 0x%x\n", + v5chan->chan_common.chan_id, + v5_dma_readl(v5_dma, INT_STA), + v5_dma_readl(v5_dma, CH_EN)); + dev_err(chan2dev(&v5chan->chan_common), + " channel: s0x%x d0x%x ctrl0x%x:0x%x cfg0x%x l0x%x\n", + v5_channel_readl(v5chan, CH_CTL_OFF), + v5_channel_readl(v5chan, CH_SIZE_OFF)); +} +#else +static void vdbg_dump_regs(struct v5_dma_chan *v5chan) {} +#endif + +static void v5_setup_irq(struct v5_dma *v5dma, int chan_id, int on) +{ + + struct v5_dma_chan *v5chan = &v5dma->chan[chan_id]; + u32 chctl; + + chctl = v5_channel_readl(v5chan, CH_CTL_OFF); + if (on) + chctl &= ~(INTABTMASK|INTERRMASK|INTTCMASK); + else + chctl |= (INTABTMASK|INTERRMASK|INTTCMASK); + v5_channel_writel(v5chan, CH_CTL_OFF, chctl); +} + +static void v5_enable_chan_irq(struct v5_dma *v5dma, int chan_id) +{ + v5_setup_irq(v5dma, chan_id, 1); +} + +static void v5_disable_chan_irq(struct v5_dma *v5dma, int chan_id) +{ + v5_setup_irq(v5dma, chan_id, 0); +} + +/** + * v5_chan_is_enabled - test if given channel is enabled + * @v5chan: channel we want to test status + */ +static inline int v5_chan_is_enabled(struct v5_dma_chan *v5chan) +{ + struct v5_dma *v5dma = to_v5_dma(v5chan->chan_common.device); + + return !!(v5_dma_readl(v5dma, CH_EN) & v5chan->mask); +} + +/** + * v5_chan_is_paused - test channel pause/resume status + * @v5chan: channel we want to test status + */ +static inline int v5_chan_is_paused(struct v5_dma_chan *v5chan) +{ + return 0; +} +#endif /* ATCDMAC300G_H */ diff --git a/include/linux/platform_data/dma-v5.h b/include/linux/platform_data/dma-v5.h new file mode 100644 index 00000000000000..2aba0d1fdafd77 --- /dev/null +++ b/include/linux/platform_data/dma-v5.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Header file for the Andestech DMA Controller driver + * + * Copyright (C) 2021 Andestech Corporation + */ +#ifndef V5_DMA_H +#define V5_DMA_H + +#include + +struct v5_dma_platform_data { + unsigned int nr_channels; + dma_cap_mask_t cap_mask; +}; + +struct v5_dma_slave { + struct device *dma_dev; +}; + +#endif /* V5_DMA_H */ From 26c607870d9603043c4f7badbba6e3d03e9a0fc3 Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Thu, 14 Oct 2021 15:15:33 +0800 Subject: [PATCH 014/169] mmc: andes: ftsdc010g: add andes ftsdc010g Reformed from the following patches on RISCV-Linux-5.4: - (b455101301e2) atcdmac300: support generic dma flow - (peter) Enable MMC_FTSDC_DMA by default to disable PIO mode - (peter) ftsdc010g.c: fix missing NO_IRQ definition - (peter) ftsdc010g.c: Use dma_request_chan() directly for channel request - (52d263eb91e3) ftsdc010: Adjust tasklet_schedule position - (4b995530bb71) atcdmac300g: remove DMAEngine driver dependency Signed-off-by: Yu Chien Peter Lin --- drivers/mmc/host/Kconfig | 16 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/ftsdc010g.c | 1563 ++++++++++++++++++++++++++++++++++ drivers/mmc/host/ftsdc010g.h | 260 ++++++ 4 files changed, 1840 insertions(+) create mode 100644 drivers/mmc/host/ftsdc010g.c create mode 100644 drivers/mmc/host/ftsdc010g.h diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index d84bdb69f56b0f..1bd2b14d20d3e6 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -618,6 +618,22 @@ config MMC_DAVINCI If you have an DAVINCI board with a Multimedia Card slot, say Y or M here. If unsure, say N. +config MMC_FTSDCG + tristate "Andestech SDC Multimedia Card Interface support dmeengine" + depends on RISCV + depends on ATCDMAC300G + help + This selects the TI DAVINCI Multimedia card Interface. + If you have an DAVINCI board with a Multimedia Card slot, + say Y or M here. If unsure, say N. + +config MMC_FTSDC_DMA + bool "DMA mode" + depends on MMC_FTSDCG + default y + help + This selects the Andestech SDC work in DMA mode. + config MMC_SPI tristate "MMC/SD/SDIO over SPI" depends on SPI_MASTER diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 4e4ceb32c4b4b1..6bd63ffff1d6b7 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o +obj-$(CONFIG_MMC_FTSDCG) += ftsdc010g.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o obj-$(CONFIG_MMC_S3C) += s3cmci.o diff --git a/drivers/mmc/host/ftsdc010g.c b/drivers/mmc/host/ftsdc010g.c new file mode 100644 index 00000000000000..fa7a5e62e25f38 --- /dev/null +++ b/drivers/mmc/host/ftsdc010g.c @@ -0,0 +1,1563 @@ +// SPDX-License-Identifier: GPL-2.0 +/* drivers/mmc/host/ftsdc010.c + * Copyright (C) 2021 Andestech + * + * 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 "ftsdc010g.h" +#include "../core/core.h" +#include +#include + +#define DRIVER_NAME "ftsdc010g" +#define REG_READ(addr) readl((host->base + addr)) +#define REG_WRITE(data, addr) writel((data), (host->base + addr)) + +enum dbg_channels { + dbg_err = (1 << 0), + dbg_debug = (1 << 1), + dbg_info = (1 << 2), + dbg_irq = (1 << 3), + dbg_sg = (1 << 4), + dbg_dma = (1 << 5), + dbg_pio = (1 << 6), + dbg_fail = (1 << 7), + dbg_conf = (1 << 8), +}; + +static struct workqueue_struct *mywq; + +static const int dbgmap_err = dbg_fail; +static const int dbgmap_info = dbg_info | dbg_conf; +static const int dbgmap_debug = dbg_err | dbg_debug | dbg_info | dbg_conf; +#define dbg(host, channels, args...) \ +do { \ + if (dbgmap_err & channels) \ + dev_err(&host->pdev->dev, args); \ + else if (dbgmap_info & channels) \ + dev_info(&host->pdev->dev, args); \ + else if (dbgmap_debug & channels) \ + dev_dbg(&host->pdev->dev, args); \ +} while (0) +static void ftsdc_send_request(struct mmc_host *mmc); +#ifdef CONFIG_MMC_DEBUG +static void dbg_dumpregs(struct ftsdc_host *host, char *prefix) +{ + u32 con, cmdarg, r0, r1, r2, r3, rcmd, dcon, dtimer, + dlen, sta, clr, imask, pcon, ccon, bwidth, scon1, + scon2, ssta, fea; + + con = REG_READ(SDC_CMD_REG); + cmdarg = REG_READ(SDC_ARGU_REG); + r0 = REG_READ(SDC_RESPONSE0_REG); + r1 = REG_READ(SDC_RESPONSE1_REG); + r2 = REG_READ(SDC_RESPONSE2_REG); + r3 = REG_READ(SDC_RESPONSE3_REG); + rcmd = REG_READ(SDC_RSP_CMD_REG); + dcon = REG_READ(SDC_DATA_CTRL_REG); + dtimer = REG_READ(SDC_DATA_TIMER_REG); + dlen = REG_READ(SDC_DATA_LEN_REG); + sta = REG_READ(SDC_STATUS_REG); + clr = REG_READ(SDC_CLEAR_REG); + imask = REG_READ(SDC_INT_MASK_REG); + pcon = REG_READ(SDC_POWER_CTRL_REG); + ccon = REG_READ(SDC_CLOCK_CTRL_REG); + bwidth = REG_READ(SDC_BUS_WIDTH_REG); + scon1 = REG_READ(SDC_SDIO_CTRL1_REG); + scon2 = REG_READ(SDC_SDIO_CTRL2_REG); + ssta = REG_READ(SDC_SDIO_STATUS_REG); + fea = REG_READ(SDC_FEATURE_REG); + + dbg(host, dbg_debug, + "%s CON:[%08x] STA:[%08x] INT:[%08x] PWR:[%08x] CLK:[%08x]\n", + prefix, con, sta, imask, pcon, ccon); + dbg(host, dbg_debug, + "%s DCON:[%08x] DTIME:[%08x] DLEN:[%08x] DWIDTH:[%08x]\n", + prefix, dcon, dtimer, dlen, bwidth); + dbg(host, dbg_debug, + "%s R0:[%08x] R1:[%08x] R2:[%08x] R3:[%08x]\n", + prefix, r0, r1, r2, r3); + dbg(host, dbg_debug, + "%s SCON1:[%08x] SCON2:[%08x] SSTA:[%08x] FEA:[%08x]\n", + prefix, scon1, scon2, ssta, fea); +} + +static void prepare_dbgmsg(struct ftsdc_host *host, struct mmc_command *cmd, + int stop) +{ + snprintf(host->dbgmsg_cmd, 300, + "#%u%s op:%i arg:0x%08x flags:0x08%x retries:%u", + host->ccnt, (stop ? " (STOP)" : ""), + cmd->opcode, cmd->arg, cmd->flags, cmd->retries); + + if (cmd->data) { + snprintf(host->dbgmsg_dat, 300, + "#%u bsize:%u blocks:%u bytes:%u", + host->dcnt, cmd->data->blksz, + cmd->data->blocks, + cmd->data->blocks * cmd->data->blksz); + } else { + host->dbgmsg_dat[0] = '\0'; + } +} + +static void dbg_dumpcmd(struct ftsdc_host *host, + struct mmc_command *cmd, int fail) +{ + unsigned int dbglvl = fail ? dbg_fail : dbg_debug; + + if (!cmd) + return; + + if (cmd->error == 0) { + dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n", + host->dbgmsg_cmd, cmd->resp[0]); + } else { + dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n", + cmd->error, host->dbgmsg_cmd, host->status); + } + + if (!cmd->data) + return; + + if (cmd->data->error == 0) { + dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat); + } else { + dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n", + cmd->data->error, host->dbgmsg_dat, + REG_READ(SDC_DATA_LEN_REG)); + } +} +#else +static void dbg_dumpcmd(struct ftsdc_host *host, + struct mmc_command *cmd, int fail) +{ +} + +static void prepare_dbgmsg(struct ftsdc_host *host, + struct mmc_command *cmd, int stop) +{ +} + +static void dbg_dumpregs(struct ftsdc_host *host, char *prefix) +{ +} + +#endif /* CONFIG_MMC_DEBUG */ + +static inline bool ftsdc_dmaexist(struct ftsdc_host *host) +{ + return (host->dma.chan != NULL); +} + +static inline u32 enable_imask(struct ftsdc_host *host, u32 imask) +{ + u32 newmask; + + newmask = REG_READ(SDC_INT_MASK_REG); + newmask |= imask; + + REG_WRITE(newmask, SDC_INT_MASK_REG); + + return newmask; +} + +static inline u32 disable_imask(struct ftsdc_host *host, u32 imask) +{ + u32 newmask; + + newmask = REG_READ(SDC_INT_MASK_REG); + newmask &= ~imask; + + REG_WRITE(newmask, SDC_INT_MASK_REG); + + return newmask; +} + +static inline void clear_imask(struct ftsdc_host *host) +{ + u32 mask = REG_READ(SDC_INT_MASK_REG); + + /* preserve the SDIO IRQ mask state */ + mask &= (SDC_INT_MASK_REG_SDIO_INTR | SDC_INT_MASK_REG_CARD_CHANGE); + REG_WRITE(mask, SDC_INT_MASK_REG); +} + +static inline void get_data_buffer(struct ftsdc_host *host) +{ + struct scatterlist *sg; + + WARN_ON(host->buf_sgptr >= host->mrq->data->sg_len); + sg = &host->mrq->data->sg[host->buf_sgptr]; + host->buf_page = 0; + host->buf_bytes = sg->length; + host->buf_ptr = host->dodma ? + (u32 *)(unsigned long)sg->dma_address : sg_virt(sg); +#ifdef CONFIG_HIGHMEM + if (PageHighMem(sg_page(sg))) { + host->buf_page = sg_page(sg); + host->buf_offset = sg->offset; + if (!host->dodma) + host->buf_ptr = 0; + } +#endif + host->buf_sgptr++; +} + +static inline u32 cal_blksz(unsigned int blksz) +{ + u32 blksztwo = 0; + + while (blksz >>= 1) + blksztwo++; + + return blksztwo; +} + +/** + * ftsdc_enable_irq - enable IRQ, after having disabled it. + * @host: The device state. + * @more: True if more IRQs are expected from transfer. + * + * Enable the main IRQ if needed after it has been disabled. + * + * The IRQ can be one of the following states: + * - enable after data read/write + * - disable when handle data read/write + */ +static void ftsdc_enable_irq(struct ftsdc_host *host, bool enable) +{ + unsigned long flags; + + local_irq_save(flags); + host->irq_enabled = enable; + if (enable) + enable_irq(host->irq); + else + disable_irq(host->irq); + local_irq_restore(flags); +} + +static void do_pio_read(struct ftsdc_host *host) +{ + u32 fifo; + u32 fifo_words; + u32 *ptr = 0; + void *tptr = 0; + u32 status; + u32 retry = 0; + + WARN_ON(host->buf_bytes != 0); + while (host->buf_sgptr < host->mrq->data->sg_len) { + get_data_buffer(host); + tptr = host->buf_ptr; + while (host->buf_bytes) { + host->page_cnt = host->buf_bytes; + if (host->buf_page != 0) { + host->buf_ptr = kmap_atomic(host->buf_page); + tptr = host->buf_ptr; + tptr += host->buf_offset; + host->page_cnt = min((u32)(PAGE_SIZE-host->buf_offset), + host->buf_bytes); + host->buf_offset = 0; + } + host->buf_bytes -= host->page_cnt; + ptr = tptr; + while (host->page_cnt) { + status = REG_READ(SDC_STATUS_REG); + if (status & SDC_STATUS_REG_FIFO_OVERRUN) { + fifo = host->fifo_len > host->page_cnt ? + host->page_cnt : host->fifo_len; + dbg(host, dbg_pio, + "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", + fifo, host->page_cnt, + REG_READ(SDC_DATA_LEN_REG)); + host->page_cnt -= fifo; + host->buf_count += fifo; + fifo_words = fifo >> 2; + while (fifo_words--) + *ptr++ = REG_READ(SDC_DATA_WINDOW_REG); + if (fifo & 3) { + u32 n; + u32 data; + u8 *p; + + n = fifo & 3; + data = REG_READ(SDC_DATA_WINDOW_REG); + p = (u8 *)ptr; + while (n--) { + *p++ = data; + data >>= 8; + } + } + } else { + udelay(1); + if (++retry >= SDC_PIO_RETRY) { + host->mrq->data->error = -EIO; + goto err; + } + } + } + if (host->buf_page != 0) { + kunmap_atomic(host->buf_ptr); + host->buf_page++; + } + } + } +err: + host->buf_active = XFER_NONE; + host->complete_what = COMPLETION_FINALIZE; +} + +static void do_pio_write(struct ftsdc_host *host) +{ + u32 fifo; + u32 *ptr; + void *tptr; + u32 status; + u32 retry = 0; + + WARN_ON(host->buf_bytes != 0); + while (host->buf_sgptr < host->mrq->data->sg_len) { + get_data_buffer(host); + dbg(host, dbg_pio, + "pio_write(): new source: [%i]@[%p]\n", + host->buf_bytes, host->buf_ptr); + while (host->buf_bytes) { + host->page_cnt = host->buf_bytes; + tptr = host->buf_ptr; + if (host->buf_page != 0) { + host->buf_ptr = kmap_atomic(host->buf_page); + host->page_cnt = min((u32)(PAGE_SIZE-host->buf_offset), + host->page_cnt); + tptr = host->buf_ptr; + tptr += host->buf_offset; + host->buf_offset = 0; + } + host->buf_bytes -= host->page_cnt; + ptr = tptr; + while (host->page_cnt) { + status = REG_READ(SDC_STATUS_REG); + if (status & SDC_STATUS_REG_FIFO_UNDERRUN) { + fifo = host->fifo_len > host->page_cnt ? + host->page_cnt : host->fifo_len; + dbg(host, dbg_pio, + "pio_write(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", + fifo, host->buf_bytes, + REG_READ(SDC_DATA_LEN_REG)); + host->page_cnt -= fifo; + host->buf_count += fifo; + fifo = (fifo + 3) >> 2; + while (fifo--) { + REG_WRITE(*ptr, SDC_DATA_WINDOW_REG); + ptr++; + } + } else { + udelay(1); + if (++retry >= SDC_PIO_RETRY) { + host->mrq->data->error = -EIO; + goto err; + } + } + } + if (host->buf_page != 0) { + kunmap_atomic(host->buf_ptr); + host->buf_page++; + } + } + } + +err: + host->buf_active = XFER_NONE; + host->complete_what = COMPLETION_FINALIZE; +} + +static void ftsdc_dma_cleanup(struct ftsdc_host *host) +{ + struct mmc_data *data = host->mrq->data; + + if (data) + dma_unmap_sg(host->dma.chan->device->dev, + data->sg, data->sg_len, mmc_get_dma_dir(data)); +} + +static int ftsdc_configure_dma(struct ftsdc_host *host) +{ + host->dma.chan = dma_request_chan(&host->pdev->dev, + "rxtx"); + if (PTR_ERR(host->dma.chan) == -ENODEV) { + struct ftsdc_mmc_config *pdata = host->pdev->dev.platform_data; + dma_cap_mask_t mask; + + if (!pdata || !pdata->dma_filter) + return -ENODEV; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + host->dma.chan = dma_request_channel(mask, pdata->dma_filter, + pdata->dma_slave); + if (!host->dma.chan) + host->dma.chan = ERR_PTR(-ENODEV); + } + if (IS_ERR(host->dma.chan)) + return PTR_ERR(host->dma.chan); + + dev_info(&host->pdev->dev, "using %s for DMA transfers\n", + dma_chan_name(host->dma.chan)); + host->dma_conf.src_addr = host->mem->start + SDC_DATA_WINDOW_REG; + host->dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.src_maxburst = 4; + host->dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + host->dma_conf.dst_maxburst = 4; + + return 0; +} + + +static void ftsdc_dma_complete(void *arg, + const struct dmaengine_result *result) +{ + struct ftsdc_host *host = arg; + struct mmc_data *data = host->mrq->data; + + host->dma_finish = true; + data->error = result->result; + host->buf_active = XFER_NONE; + host->complete_what = COMPLETION_FINALIZE; + ftsdc_dma_cleanup(host); + ftsdc_enable_irq(host, true); + tasklet_schedule(&host->pio_tasklet); +} + +static u32 ftsdc_prepare_data_dma(struct ftsdc_host *host, + struct mmc_data *data) +{ + struct dma_chan *chan; + struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction slave_dirn; + unsigned int sglen; + + chan = host->dma.chan; + if (!chan) + return -ENODEV; + + if (data->flags & MMC_DATA_READ) { + host->dma_conf.direction = slave_dirn = DMA_DEV_TO_MEM; + host->dma_conf.src_addr = host->mem->start + + SDC_DATA_WINDOW_REG; + } else { + host->dma_conf.direction = slave_dirn = DMA_MEM_TO_DEV; + host->dma_conf.dst_addr = host->mem->start + + SDC_DATA_WINDOW_REG; + } + sglen = dma_map_sg(chan->device->dev, data->sg, + data->sg_len, mmc_get_dma_dir(data)); + dmaengine_slave_config(chan, &host->dma_conf); + desc = dmaengine_prep_slave_sg(chan, + data->sg, sglen, slave_dirn, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) + goto unmap_exit; + host->dma.data_desc = desc; + desc->callback_result = ftsdc_dma_complete; + desc->callback_param = host; + + return 0; + +unmap_exit: + dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data)); + + return -ENOMEM; +} + + +static void +ftsdc_submit_data_dma(struct ftsdc_host *host) +{ + struct dma_chan *chan = host->dma.chan; + struct dma_async_tx_descriptor *desc = host->dma.data_desc; + + if (chan) { + dmaengine_submit(desc); + dma_async_issue_pending(chan); + } +} + +static void ftsdc_work(struct work_struct *work) +{ + struct ftsdc_host *host = + container_of(work, struct ftsdc_host, work); + struct mmc_data *data = host->mrq->data; + + ftsdc_enable_irq(host, false); + if (host->dodma) { + get_data_buffer(host); + ftsdc_prepare_data_dma(host, data); + ftsdc_submit_data_dma(host); + } else { + if (host->buf_active == XFER_WRITE) + do_pio_write(host); + + if (host->buf_active == XFER_READ) + do_pio_read(host); + ftsdc_enable_irq(host, true); + tasklet_schedule(&host->pio_tasklet); + } +} + +static void finalize_request(struct ftsdc_host *host) +{ + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd; + u32 con; + int debug_as_failure = 0; + + if (host->complete_what != COMPLETION_FINALIZE) + return; + + if (!mrq) + return; + + cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + + if (cmd->data && (cmd->error == 0) && + (cmd->data->error == 0)) { + if (host->dodma && (!host->dma_finish)) { + dbg(host, dbg_dma, "DMA Missing (%d)!\n", + host->dma_finish); + return; + } + host->dodma = false; + } + + /* Read response from controller. */ + if (cmd->flags & MMC_RSP_136) { + cmd->resp[3] = REG_READ(SDC_RESPONSE0_REG); + cmd->resp[2] = REG_READ(SDC_RESPONSE1_REG); + cmd->resp[1] = REG_READ(SDC_RESPONSE2_REG); + cmd->resp[0] = REG_READ(SDC_RESPONSE3_REG); + } else if (cmd->flags & MMC_RSP_PRESENT) { + cmd->resp[0] = REG_READ(SDC_RESPONSE0_REG); + } + + if (cmd->error) + debug_as_failure = 1; + + if (cmd->data && cmd->data->error) + debug_as_failure = 1; + + dbg_dumpcmd(host, cmd, debug_as_failure); + + clear_imask(host); + con = REG_READ(SDC_STATUS_REG); + con &= ~SDC_CLEAR_REG_SDIO_INTR; + REG_WRITE(con, SDC_CLEAR_REG); + + if (cmd->data && cmd->error) + cmd->data->error = cmd->error; + + if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) { + host->cmd_is_stop = 1; + ftsdc_send_request(host->mmc); + return; + } + + /* If we have no data transfer we are finished here */ + if (!mrq->data) + goto request_done; + + /* Calculate the amount of bytes transfer if there was no error */ + if (mrq->data->error == 0) { + mrq->data->bytes_xfered = + (mrq->data->blocks * mrq->data->blksz); + } else { + mrq->data->bytes_xfered = 0; + } + +request_done: + host->complete_what = COMPLETION_NONE; + host->mrq = NULL; + + host->last_opcode = mrq->cmd->opcode; + mmc_request_done(host->mmc, mrq); +} + +static void pio_tasklet(unsigned long data) +{ + struct ftsdc_host *host = (struct ftsdc_host *) data; + + if (host->complete_what == COMPLETION_XFER_PROGRESS) { + queue_work(mywq, (struct work_struct *)&host->work); + return; + } + + if (host->complete_what == COMPLETION_FINALIZE) { + clear_imask(host); + if (host->buf_active != XFER_NONE) { + dbg(host, dbg_err, "unfinished %s buf_count:[%u] buf_bytes:[%u]\n", + (host->buf_active == XFER_READ) ? "read" : "write", + host->buf_count, host->buf_bytes); + + if (host->mrq->data) + host->mrq->data->error = -EINVAL; + } + finalize_request(host); + } +} + +static void ftsdc_send_command(struct ftsdc_host *host, + struct mmc_command *cmd) +{ + u32 ccon = 0; + u32 newmask = 0; + u32 scon; + + if (cmd->data) { + host->complete_what = COMPLETION_XFER_PROGRESS; + newmask |= SDC_INT_MASK_REG_RSP_TIMEOUT; + } else if (cmd->flags & MMC_RSP_PRESENT) { + host->complete_what = COMPLETION_RSPFIN; + newmask |= SDC_INT_MASK_REG_RSP_TIMEOUT; + } else { + host->complete_what = COMPLETION_CMDSENT; + newmask |= SDC_INT_MASK_REG_CMD_SEND; + } + + ccon |= cmd->opcode & SDC_CMD_REG_INDEX; + ccon |= SDC_CMD_REG_CMD_EN; + + if (cmd->flags & MMC_RSP_PRESENT) { + ccon |= SDC_CMD_REG_NEED_RSP; + newmask |= SDC_INT_MASK_REG_RSP_CRC_OK | + SDC_INT_MASK_REG_RSP_CRC_FAIL; + } + + if (cmd->flags & MMC_RSP_136) + ccon |= SDC_CMD_REG_LONG_RSP; + + /* applicatiion specific cmd must follow an MMC_APP_CMD. The + * value will be updated in finalize_request function + */ + if (host->last_opcode == MMC_APP_CMD) + ccon |= SDC_CMD_REG_APP_CMD; + + enable_imask(host, newmask); + REG_WRITE(cmd->arg, SDC_ARGU_REG); + + scon = REG_READ(SDC_SDIO_CTRL1_REG); + if (host->mmc->card != NULL && mmc_card_sdio(host->mmc->card)) + scon |= SDC_SDIO_CTRL1_REG_SDIO_ENABLE; + else + scon &= ~SDC_SDIO_CTRL1_REG_SDIO_ENABLE; + REG_WRITE(scon, SDC_SDIO_CTRL1_REG); + + dbg_dumpregs(host, ""); + dbg(host, dbg_debug, "CON[%x]\n", ccon); + + REG_WRITE(ccon, SDC_CMD_REG); +} + +static int ftsdc_setup_data(struct ftsdc_host *host, struct mmc_data *data) +{ + u32 dcon, newmask = 0; + + /* configure data transfer parameter */ + if (!data) + return 0; + if (host->mmc->card && host->mmc->card->type == (unsigned int)MMC_TYPE_SD) { + if (((data->blksz - 1) & data->blksz) != 0) { + pr_warn("%s: can't do non-power-of 2 (blksz %d)\n", + __func__, data->blksz); + return -EINVAL; + } + } + + if (data->blksz <= 2) { + /* We cannot deal with unaligned blocks with more than + * one block being transferred + */ + if (data->blocks > 1) { + pr_warn("%s: can't do non-word sized (blksz %d)\n", + __func__, data->blksz); + return -EINVAL; + } + } + + /* data length */ + dcon = data->blksz * data->blocks; + REG_WRITE(dcon, SDC_DATA_LEN_REG); + + /* write data control */ + dcon = 0; + dcon = cal_blksz(data->blksz); + + /* enable UNDERFUN will trigger interrupt immediatedly + * So setup it when rsp is received successfully + */ + if (data->flags & MMC_DATA_WRITE) { + dcon |= SDC_DATA_CTRL_REG_DATA_WRITE; + } else { + dcon &= ~SDC_DATA_CTRL_REG_DATA_WRITE; + newmask |= SDC_INT_MASK_REG_FIFO_OVERRUN; + } + + /* always reset fifo since last transfer may fail */ + dcon |= SDC_DATA_CTRL_REG_FIFO_RST; + + /* enable data transfer which will be pended until cmd is send */ + dcon |= SDC_DATA_CTRL_REG_DATA_EN; + if (ftsdc_dmaexist(host) && + ((data->blksz * data->blocks) & 0xf) == 0) { + newmask &= ~SDC_INT_MASK_REG_FIFO_OVERRUN; + dcon |= SDC_DATA_CTRL_REG_DMA_EN; + dcon |= SDC_DMA_TYPE_4; + host->dodma = true; + + } + REG_WRITE(dcon, SDC_DATA_CTRL_REG); + /* add to IMASK register */ + newmask |= SDC_INT_MASK_REG_DATA_CRC_FAIL | + SDC_INT_MASK_REG_DATA_TIMEOUT; + + enable_imask(host, newmask); + /* handle sdio */ + dcon = SDC_SDIO_CTRL1_REG_READ_WAIT_ENABLE + & REG_READ(SDC_SDIO_CTRL1_REG); + dcon |= data->blksz | data->blocks << 15; + if (data->blocks > 1) + dcon |= SDC_SDIO_CTRL1_REG_SDIO_BLK_MODE; + REG_WRITE(dcon, SDC_SDIO_CTRL1_REG); + + return 0; +} + +#define BOTH_DIR (MMC_DATA_WRITE | MMC_DATA_READ) + +static int ftsdc_prepare_buffer(struct ftsdc_host *host, struct mmc_data *data) +{ + int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; + + if ((!host->mrq) || (!host->mrq->data)) + return -EINVAL; + + WARN_ON((data->flags & BOTH_DIR) == BOTH_DIR); + host->buf_sgptr = 0; + host->buf_bytes = 0; + host->buf_count = 0; + host->buf_active = rw ? XFER_WRITE : XFER_READ; + if (host->dodma) + host->dma_finish = false; + + return 0; +} + +static irqreturn_t ftsdc_irq(int irq, void *dev_id) +{ + struct ftsdc_host *host = dev_id; + struct mmc_command *cmd; + u32 mci_status; + u32 mci_clear; + u32 mci_imsk; + unsigned long iflags; + + mci_status = REG_READ(SDC_STATUS_REG); + mci_imsk = REG_READ(SDC_INT_MASK_REG); + + dbg(host, dbg_debug, "irq: status:0x%08x, mask : %08x\n", + mci_status, mci_imsk); + + if (mci_status & SDC_STATUS_REG_SDIO_INTR) { + if (mci_imsk & SDC_INT_MASK_REG_SDIO_INTR) { + mci_clear = SDC_CLEAR_REG_SDIO_INTR; + REG_WRITE(mci_clear, SDC_CLEAR_REG); + + mmc_signal_sdio_irq(host->mmc); + return IRQ_HANDLED; + } + } + spin_lock_irqsave(&host->complete_lock, iflags); + mci_clear = 0; + if (mci_status & SDC_STATUS_REG_CARD_CHANGE) { + if ((mci_status & SDC_STATUS_REG_CARD_DETECT) + == SDC_CARD_INSERT) { + host->status = "card insert"; + } else { + host->status = "card remove"; + } + mmc_detect_change(host->mmc, msecs_to_jiffies(500)); + mci_clear |= SDC_CLEAR_REG_CARD_CHANGE; + dbg(host, dbg_irq, "%s\n", host->status); + + if (host->complete_what != COMPLETION_NONE) { + host->mrq->cmd->error = -EIO; + goto close_transfer; + } + + goto irq_out; + } + + if ((host->complete_what == COMPLETION_NONE) || + (host->complete_what == COMPLETION_FINALIZE)) { + host->status = "nothing to complete"; + mci_clear = -1u; + goto irq_out; + } + + if (!host->mrq) { + host->status = "no active mrq"; + clear_imask(host); + goto irq_out; + } + + cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd; + + if (!cmd) { + host->status = "no active cmd"; + clear_imask(host); + goto irq_out; + } + + if (mci_status & SDC_STATUS_REG_CMD_SEND) { + mci_clear |= SDC_CLEAR_REG_CMD_SEND; + + if (host->complete_what == COMPLETION_CMDSENT) { + host->status = "ok: command sent"; + goto close_transfer; + } else { + host->status = "error: command sent(status not match)"; + cmd->error = -EINVAL; + goto fail_transfer; + } + } + + /* handle error status */ + if (mci_status & SDC_STATUS_REG_RSP_TIMEOUT) { + dbg(host, dbg_err, "CMDSTAT: error RSP TIMEOUT\n"); + mci_clear |= SDC_CLEAR_REG_RSP_TIMEOUT; + cmd->error = -ETIMEDOUT; + host->status = "error: response timeout"; + goto fail_transfer; + } + + if (mci_status & SDC_STATUS_REG_RSP_CRC_FAIL) { + mci_clear |= SDC_CLEAR_REG_RSP_CRC_FAIL; + /* This is weird hack */ + if (cmd->flags & MMC_RSP_CRC) { + dbg(host, dbg_err, "CMDSTAT: error RSP CRC\n"); + cmd->error = -EILSEQ; + host->status = "error: RSP CRC failed"; + goto fail_transfer; + } else { + host->status = "R3 or R4 type command"; + goto close_transfer; + } + } + + if (mci_status & SDC_STATUS_REG_RSP_CRC_OK) { + mci_clear |= SDC_CLEAR_REG_RSP_CRC_OK; + + if (host->complete_what == COMPLETION_XFER_PROGRESS) { + REG_WRITE(mci_clear, SDC_CLEAR_REG); + + host->status = "RSP recv OK"; + if (!cmd->data) + goto close_transfer; + + if (host->dodma) { + tasklet_schedule(&host->pio_tasklet); + host->status = "dma access"; + goto irq_out; + } + + if (host->buf_active == XFER_WRITE) + enable_imask(host, + SDC_INT_MASK_REG_FIFO_UNDERRUN); + } else if (host->complete_what == COMPLETION_RSPFIN) { + goto close_transfer; + } + } + + /* handler data transfer */ + if (mci_status & SDC_STATUS_REG_DATA_TIMEOUT) { + dbg(host, dbg_err, "CMDSTAT: error DATA TIMEOUT\n"); + mci_clear |= SDC_CLEAR_REG_DATA_TIMEOUT; + cmd->error = -ETIMEDOUT; + host->status = "error: data timeout"; + goto fail_transfer; + } + + if (mci_status & SDC_STATUS_REG_DATA_CRC_FAIL) { + dbg(host, dbg_err, "CMDSTAT: error DATA CRC\n"); + mci_clear |= SDC_CLEAR_REG_DATA_CRC_FAIL; + cmd->error = -EILSEQ; + host->status = "error: data CRC fail"; + goto fail_transfer; + } + + if ((mci_status & SDC_STATUS_REG_FIFO_UNDERRUN) || + mci_status & SDC_STATUS_REG_FIFO_OVERRUN) { + + disable_imask(host, SDC_INT_MASK_REG_FIFO_OVERRUN | + SDC_INT_MASK_REG_FIFO_UNDERRUN); + + if (!host->dodma) { + if (host->buf_active == XFER_WRITE) { + tasklet_schedule(&host->pio_tasklet); + host->status = "pio tx"; + } else if (host->buf_active == XFER_READ) { + + tasklet_schedule(&host->pio_tasklet); + host->status = "pio rx"; + } + } + } + + goto irq_out; + +fail_transfer: + host->buf_active = XFER_NONE; + +close_transfer: + host->complete_what = COMPLETION_FINALIZE; + + clear_imask(host); + tasklet_schedule(&host->pio_tasklet); + +irq_out: + REG_WRITE(mci_clear, SDC_CLEAR_REG); + + dbg(host, dbg_debug, "irq: %s\n", host->status); + spin_unlock_irqrestore(&host->complete_lock, iflags); + return IRQ_HANDLED; +} + +static void ftsdc_send_request(struct mmc_host *mmc) +{ + struct ftsdc_host *host = mmc_priv(mmc); + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + + host->ccnt++; + prepare_dbgmsg(host, cmd, host->cmd_is_stop); + dbg(host, dbg_debug, "%s\n", host->dbgmsg_cmd); + + if (cmd->data) { + int res = ftsdc_setup_data(host, cmd->data); + + host->dcnt++; + + if (res) { + dbg(host, dbg_err, "setup data error %d\n", res); + cmd->error = res; + cmd->data->error = res; + + mmc_request_done(mmc, mrq); + return; + } + + res = ftsdc_prepare_buffer(host, cmd->data); + + if (res) { + dbg(host, dbg_err, "data prepare error %d\n", res); + cmd->error = res; + cmd->data->error = res; + + mmc_request_done(mmc, mrq); + return; + } + } + + /* Send command */ + ftsdc_send_command(host, cmd); +} + +static int ftsdc_get_cd(struct mmc_host *mmc) +{ + struct ftsdc_host *host = mmc_priv(mmc); + + u32 con = REG_READ(SDC_STATUS_REG); + dbg(host, dbg_debug, "get_cd status:%.8x\n\n", con); + + return (con & SDC_STATUS_REG_CARD_DETECT) ? 0 : 1; +} + +static void ftsdc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct ftsdc_host *host = mmc_priv(mmc); + + host->status = "mmc request"; + host->cmd_is_stop = 0; + host->mrq = mrq; + if (ftsdc_get_cd(mmc) == 0) { + dbg(host, dbg_err, "%s: no medium present\n", __func__); + host->mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + } else { + ftsdc_send_request(mmc); + } + dbg(host, dbg_debug, "send request\n"); +} + +static void ftsdc_set_clk(struct ftsdc_host *host, struct mmc_ios *ios) +{ + u32 clk_div = 0; + u32 con; + struct ftsdc_mmc_config *pdata = host->pdev->dev.platform_data; + u32 freq = pdata->max_freq; + + dbg(host, dbg_debug, "request clk : %u\n", ios->clock); + con = REG_READ(SDC_CLOCK_CTRL_REG); + if (ios->clock == 0) { + host->real_rate = 0; + con |= SDC_CLOCK_CTRL_REG_CLK_DIS; + } else { + clk_div = (freq / (ios->clock << 1)) - 1; + host->real_rate = freq / ((clk_div+1)<<1); + if (host->real_rate > ios->clock) { + ++clk_div; + host->real_rate = freq / ((clk_div+1)<<1); + } + if (clk_div > 127) + dbg(host, dbg_err, "%s: no match clock rate, %u\n", + __func__, ios->clock); + + con = (con & ~SDC_CLOCK_CTRL_REG_CLK_DIV) + | (clk_div & SDC_CLOCK_CTRL_REG_CLK_DIV); + con &= ~SDC_CLOCK_CTRL_REG_CLK_DIS; + } + + REG_WRITE(con, SDC_CLOCK_CTRL_REG); +} + +static void ftsdc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct ftsdc_host *host = mmc_priv(mmc); + u32 con; + + con = REG_READ(SDC_POWER_CTRL_REG); + switch (ios->power_mode) { + case MMC_POWER_ON: + case MMC_POWER_UP: + con |= SDC_POWER_CTRL_REG_POWER_ON; + break; + case MMC_POWER_OFF: + default: + con &= ~SDC_POWER_CTRL_REG_POWER_ON; + break; + } + + REG_WRITE(con, SDC_POWER_CTRL_REG); + + ftsdc_set_clk(host, ios); + + if ((ios->power_mode == MMC_POWER_ON) || + (ios->power_mode == MMC_POWER_UP)) { + dbg(host, dbg_debug, "running at %ukHz (requested: %ukHz).\n", + host->real_rate/1000, ios->clock/1000); + } else { + dbg(host, dbg_debug, "powered down.\n"); + } + + host->bus_width = ios->bus_width; + /* write bus configure */ + con = REG_READ(SDC_BUS_WIDTH_REG); + + con &= ~(SDC_BUS_WIDTH_REG_SINGLE_BUS | + SDC_BUS_WIDTH_REG_WIDE_4_BUS | + SDC_BUS_WIDTH_REG_WIDE_8_BUS); + if (host->bus_width == MMC_BUS_WIDTH_1) + con |= SDC_BUS_WIDTH_REG_SINGLE_BUS; + else if (host->bus_width == MMC_BUS_WIDTH_4) + con |= SDC_BUS_WIDTH_REG_WIDE_4_BUS; + else if (host->bus_width == MMC_BUS_WIDTH_8) + con |= SDC_BUS_WIDTH_REG_WIDE_8_BUS; + else + dbg(host, dbg_err, "set_ios: can't support bus mode"); + REG_WRITE(con, SDC_BUS_WIDTH_REG); + + /*set rsp and data timeout */ + con = -1; + REG_WRITE(con, SDC_DATA_TIMER_REG); + if (ios->power_mode == MMC_POWER_UP) + mmc_delay(250); +} + +static int ftsdc_get_ro(struct mmc_host *mmc) +{ + struct ftsdc_host *host = mmc_priv(mmc); + u32 con = REG_READ(SDC_STATUS_REG); + + dbg(host, dbg_debug, "get_ro status:%.8x\n", con); + + return (con & SDC_STATUS_REG_CARD_LOCK) ? 1 : 0; +} + + +static void ftsdc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct ftsdc_host *host = mmc_priv(mmc); + unsigned long flags; + u32 con; +#ifdef CONFIG_MMC_DEBUG + u32 ena; +#endif + + local_irq_save(flags); + + con = REG_READ(SDC_INT_MASK_REG); +#ifdef CONFIG_MMC_DEBUG + ena = (con & SDC_STATUS_REG_SDIO_INTR) ? 1:0; + if (ena == enable) + dbg(host, dbg_debug, "ena %.8x %.8x\n", ena, enable); +#endif + + con = enable ? (con | SDC_STATUS_REG_SDIO_INTR) + : (con & ~SDC_STATUS_REG_SDIO_INTR); + REG_WRITE(con, SDC_INT_MASK_REG); + +#ifdef CONFIG_MMC_DEBUG + //check and ensure data out to SD host controller + ena = (REG_READ(SDC_INT_MASK_REG) & SDC_STATUS_REG_SDIO_INTR) ? 1:0; + if (ena != enable) + dbg(host, dbg_debug, "ena %.8x %.8x\n", ena, enable); +#endif + + local_irq_restore(flags); +} + +static struct mmc_host_ops ftsdc_ops = { + .request = ftsdc_request, + .set_ios = ftsdc_set_ios, + .get_ro = ftsdc_get_ro, + .get_cd = ftsdc_get_cd, + .enable_sdio_irq = ftsdc_enable_sdio_irq, +}; + +#ifdef CONFIG_DEBUG_FS + +static int ftsdc_state_show(struct seq_file *seq, void *v) +{ + struct ftsdc_host *host = seq->private; + + seq_printf(seq, "Register base = 0x%08x\n", + (u32)((unsigned long)host->base)); + seq_printf(seq, "Clock rate = %u\n", host->real_rate); + seq_printf(seq, "host status = %s\n", host->status); + seq_printf(seq, "IRQ = %d\n", host->irq); + seq_printf(seq, "IRQ enabled = %d\n", host->irq_enabled); + seq_printf(seq, "complete what = %d\n", host->complete_what); + seq_printf(seq, "dma support = %d\n", ftsdc_dmaexist(host)); + seq_printf(seq, "use dma = %d\n", host->dodma); + + return 0; +} + +static int ftsdc_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, ftsdc_state_show, inode->i_private); +} + +static const struct file_operations ftsdc_fops_state = { + .owner = THIS_MODULE, + .open = ftsdc_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define DBG_REG(_r) { .addr = SDC_## _r ## _REG, .name = #_r } + +static struct ftsdc_reg { + unsigned short addr; + unsigned char *name; +} debug_regs[] = { + DBG_REG(CMD), + DBG_REG(ARGU), + DBG_REG(RESPONSE0), + DBG_REG(RESPONSE1), + DBG_REG(RESPONSE2), + DBG_REG(RESPONSE3), + DBG_REG(RSP_CMD), + DBG_REG(DATA_CTRL), + DBG_REG(DATA_TIMER), + DBG_REG(DATA_LEN), + DBG_REG(STATUS), + DBG_REG(CLEAR), + DBG_REG(INT_MASK), + DBG_REG(POWER_CTRL), + DBG_REG(CLOCK_CTRL), + DBG_REG(BUS_WIDTH), + DBG_REG(SDIO_CTRL1), + DBG_REG(SDIO_CTRL2), + DBG_REG(SDIO_STATUS), + DBG_REG(FEATURE), + DBG_REG(REVISION), + {} +}; + +static int ftsdc_regs_show(struct seq_file *seq, void *v) +{ + struct ftsdc_host *host = seq->private; + struct ftsdc_reg *rptr = debug_regs; + + for (; rptr->name; rptr++) + seq_printf(seq, "SDI%s\t=0x%08x\n", rptr->name, + REG_READ(rptr->addr)); + + return 0; +} + +static int ftsdc_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, ftsdc_regs_show, inode->i_private); +} + +static const struct file_operations ftsdc_fops_regs = { + .owner = THIS_MODULE, + .open = ftsdc_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void ftsdc_debugfs_attach(struct ftsdc_host *host) +{ + struct device *dev = &host->pdev->dev; + + host->debug_root = debugfs_create_dir(dev_name(dev), NULL); + if (IS_ERR(host->debug_root)) { + dev_err(dev, "failed to create debugfs root\n"); + return; + } + + host->debug_state = debugfs_create_file("state", 0444, + host->debug_root, host, + &ftsdc_fops_state); + + if (IS_ERR(host->debug_state)) + dev_err(dev, "failed to create debug state file\n"); + + host->debug_regs = debugfs_create_file("regs", 0444, + host->debug_root, host, + &ftsdc_fops_regs); + + if (IS_ERR(host->debug_regs)) + dev_err(dev, "failed to create debug regs file\n"); +} + +static void ftsdc_debugfs_remove(struct ftsdc_host *host) +{ + debugfs_remove(host->debug_regs); + debugfs_remove(host->debug_state); + debugfs_remove(host->debug_root); +} + +#else +static inline void ftsdc_debugfs_attach(struct ftsdc_host *host) { } +static inline void ftsdc_debugfs_remove(struct ftsdc_host *host) { } + +#endif /* CONFIG_DEBUG_FS */ + +enum { + MMC_CTLR_VERSION_1 = 0, + MMC_CTLR_VERSION_2, +}; + + +static struct platform_device_id ftsdc_mmc_devtype[] = { + { + .name = "ag101p", + .driver_data = MMC_CTLR_VERSION_1, + }, { + .name = "ae3xx", + .driver_data = MMC_CTLR_VERSION_2, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, ftsdc_mmc_devtype); + +static const struct of_device_id ftsdc_mmc_dt_ids[] = { + { + .compatible = "andestech,atfsdc010g", + .data = &ftsdc_mmc_devtype[MMC_CTLR_VERSION_2], + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ftsdc_mmc_dt_ids); + + +static struct ftsdc_mmc_config + *mmc_parse_pdata(struct platform_device *pdev) +{ + struct device_node *np; + struct ftsdc_mmc_config *pdata = pdev->dev.platform_data; + const struct of_device_id *match = + of_match_device(of_match_ptr(ftsdc_mmc_dt_ids), &pdev->dev); + u32 data; + + np = pdev->dev.of_node; + if (!np) + return pdata; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdev->dev.platform_data = (void *)pdata; + + if (!pdata) { + dev_err(&pdev->dev, "Failed to allocate memory for struct ftsdc_mmc_config\n"); + goto nodata; + } + + if (match) + pdev->id_entry = match->data; + + if (of_property_read_u32(np, "max-frequency", &pdata->max_freq)) + dev_info(&pdev->dev, "'max-frequency' property not specified, defaulting to 25MHz\n"); + + of_property_read_u32(np, "bus-width", &data); + switch (data) { + case 1: + case 4: + case 8: + pdata->wires = data; + break; + default: + pdata->wires = 1; + } +nodata: + return pdata; +} + +static int __init ftsdc_probe(struct platform_device *pdev) +{ + struct ftsdc_host *host; + struct mmc_host *mmc; + struct ftsdc_mmc_config *pdata = NULL; + struct resource *r, *mem = NULL; + int ret = -ENOMEM; + u32 con; + int irq = 0; + size_t mem_size; + + pdata = mmc_parse_pdata(pdev); + if (pdata == NULL) { + dev_err(&pdev->dev, "Couldn't get platform data\n"); + return -ENOENT; + } + ret = -ENODEV; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + + if (!r || irq < 0) + goto probe_out; + + ret = -EBUSY; + mem_size = resource_size(r); + mem = request_mem_region(r->start, mem_size, pdev->name); + + if (!mem) { + dev_err(&pdev->dev, + "failed to get io memory region resource.\n"); + goto probe_out; + } + ret = -ENOMEM; + mmc = mmc_alloc_host(sizeof(struct ftsdc_host), &pdev->dev); + if (!mmc) + goto probe_out; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdev = pdev; + mywq = create_workqueue("atcsdc_queue"); + if (mywq == NULL) + goto probe_free_host; + + spin_lock_init(&host->complete_lock); + tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host); + init_completion(&host->dma_complete); + INIT_WORK(&host->work, ftsdc_work); + host->complete_what = COMPLETION_NONE; + host->buf_active = XFER_NONE; + host->mem = mem; +#ifdef CONFIG_MMC_FTSDC_DMA + ret = ftsdc_configure_dma(host); + if (ret) + goto probe_free_host; +#endif + host->base = (void __iomem *) ioremap(mem->start, mem_size); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto probe_free_mem_region; + } + + host->irq = irq; + + ret = request_irq(host->irq, ftsdc_irq, 0, DRIVER_NAME, host); + if (ret) { + dev_err(&pdev->dev, "failed to request mci interrupt.\n"); + ret = -ENOENT; + goto probe_free_mem_region; + } + host->irq_enabled = true; + /* enable card change interruption */ + con = REG_READ(SDC_INT_MASK_REG); + con |= SDC_INT_MASK_REG_CARD_CHANGE; + REG_WRITE(con, SDC_INT_MASK_REG); + + con = REG_READ(SDC_BUS_WIDTH_REG); + mmc->ops = &ftsdc_ops; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + + if (con & SDC_WIDE_4_BUS_SUPPORT) + mmc->caps |= MMC_CAP_4_BIT_DATA; + else if (con & SDC_WIDE_8_BUS_SUPPORT) + mmc->caps |= MMC_CAP_8_BIT_DATA; + +#ifndef A320D_BUILDIN_SDC + mmc->caps |= MMC_CAP_SDIO_IRQ; +#endif + mmc->f_min = pdata->max_freq / (2 * 128); + mmc->f_max = pdata->max_freq / 2; + /* limit SDIO mode max size */ + mmc->max_req_size = 128 * 1024 * 1024 - 1; + mmc->max_blk_size = 2047; + mmc->max_req_size = (mmc->max_req_size + 1) / (mmc->max_blk_size + 1); + mmc->max_seg_size = mmc->max_req_size; + mmc->max_blk_count = (1<<17)-1; + /* set fifo length and default threshold half */ + con = REG_READ(SDC_FEATURE_REG); + host->fifo_len = (con & SDC_FEATURE_REG_FIFO_DEPTH) * sizeof(u32); + dbg(host, dbg_debug, + "probe: mapped mci_base:%p irq:%u.\n", + host->base, host->irq); + dbg_dumpregs(host, ""); + ret = mmc_add_host(mmc); + if (ret) { + dev_err(&pdev->dev, "failed to add mmc host.\n"); + goto probe_free_irq; + } + ftsdc_debugfs_attach(host); + platform_set_drvdata(pdev, mmc); + dev_info(&pdev->dev, "%s - using %s SDIO IRQ\n", mmc_hostname(mmc), + mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw"); + return 0; + + probe_free_irq: + free_irq(host->irq, host); + + probe_free_mem_region: + release_mem_region(host->mem->start, resource_size(host->mem)); + destroy_workqueue(mywq); + + probe_free_host: + if (!IS_ERR(host->dma.chan)) + dma_release_channel(host->dma.chan); + mmc_free_host(mmc); + + probe_out: + return ret; +} + +static void ftsdc_shutdown(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct ftsdc_host *host = mmc_priv(mmc); + + flush_workqueue(mywq); + destroy_workqueue(mywq); + + ftsdc_debugfs_remove(host); + mmc_remove_host(mmc); +} + +static int __exit ftsdc_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct ftsdc_host *host = mmc_priv(mmc); + + ftsdc_shutdown(pdev); + tasklet_disable(&host->pio_tasklet); + if (!IS_ERR(host->dma.chan)) + dma_release_channel(host->dma.chan); + free_irq(host->irq, host); + iounmap(host->base); + release_mem_region(host->mem->start, resource_size(host->mem)); + mmc_free_host(mmc); + + return 0; +} + +#ifdef CONFIG_PM +static int ftsdc_free_dma(struct ftsdc_host *host) +{ + if (!IS_ERR(host->dma.chan)) + dma_release_channel(host->dma.chan); + + return 0; +} + +static int ftsdc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct ftsdc_host *host = mmc_priv(mmc); + int ret = 0; + + if (mmc) + ftsdc_free_dma(host); + + return ret; + +} + +static int ftsdc_resume(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + int ret = 0; + + if (mmc) { + struct ftsdc_host *host = mmc_priv(mmc); + + ftsdc_configure_dma(host); + } + return ret; +} + +#else +#define ftsdc_suspend NULL +#define ftsdc_resume NULL +#endif + +static struct platform_driver ftsdc_driver = { + .driver = { + .name = "ftsdc010g", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ftsdc_mmc_dt_ids), + }, + .remove = __exit_p(ftsdc_remove), + .shutdown = ftsdc_shutdown, + .suspend = ftsdc_suspend, + .resume = ftsdc_resume, +}; + +module_platform_driver_probe(ftsdc_driver, ftsdc_probe); +MODULE_DESCRIPTION("Andestech Leopard MMC/SD Card Interface driver"); +MODULE_AUTHOR("Rick Chen "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/ftsdc010g.h b/drivers/mmc/host/ftsdc010g.h new file mode 100644 index 00000000000000..8aae18312c2694 --- /dev/null +++ b/drivers/mmc/host/ftsdc010g.h @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Andestech FTSDC010 Device Driver for dmaengine + * + * Copyright (C) 2021 Andestech + * + * All Rights Reserved + */ +#ifndef _FTSDC010_H_ +#define _FTSDC010_H_ + +#include + + +#define DELAY_FOR_DMA_READ + +/* used for dma timeout */ +#define SDC_TIMEOUT_BASE (HZ/2) + +/* used for pio retry times */ +#define SDC_PIO_RETRY 0x300000 + +/* sd controller register */ +#define SDC_CMD_REG 0x00000000 +#define SDC_ARGU_REG 0x00000004 +#define SDC_RESPONSE0_REG 0x00000008 +#define SDC_RESPONSE1_REG 0x0000000C +#define SDC_RESPONSE2_REG 0x00000010 +#define SDC_RESPONSE3_REG 0x00000014 +#define SDC_RSP_CMD_REG 0x00000018 +#define SDC_DATA_CTRL_REG 0x0000001C +#define SDC_DATA_TIMER_REG 0x00000020 +#define SDC_DATA_LEN_REG 0x00000024 +#define SDC_STATUS_REG 0x00000028 +#define SDC_CLEAR_REG 0x0000002C +#define SDC_INT_MASK_REG 0x00000030 +#define SDC_POWER_CTRL_REG 0x00000034 +#define SDC_CLOCK_CTRL_REG 0x00000038 +#define SDC_BUS_WIDTH_REG 0x0000003C +#define SDC_DATA_WINDOW_REG 0x00000040 + +#ifdef A320D_BUILDIN_SDC +#define SDC_FEATURE_REG 0x00000044 +#define SDC_REVISION_REG 0x00000048 +#else +#define SDC_MMC_INT_RSP_REG 0x00000044 +#define SDC_GP_OUTPUT_REG 0x00000048 +#define SDC_FEATURE_REG 0x0000009C +#define SDC_REVISION_REG 0x000000A0 +#endif + +#define SDC_SDIO_CTRL1_REG 0x0000006C +#define SDC_SDIO_CTRL2_REG 0x00000070 +#define SDC_SDIO_STATUS_REG 0x00000074 + +/* bit mapping of command register */ +#define SDC_CMD_REG_INDEX 0x0000003F +#define SDC_CMD_REG_NEED_RSP 0x00000040 +#define SDC_CMD_REG_LONG_RSP 0x00000080 +#define SDC_CMD_REG_APP_CMD 0x00000100 +#define SDC_CMD_REG_CMD_EN 0x00000200 +#define SDC_CMD_REG_SDC_RST 0x00000400 +#define SDC_CMD_MMC_INT_STOP 0x00000800 + +/* bit mapping of response command register */ +#define SDC_RSP_CMD_REG_INDEX 0x0000003F +#define SDC_RSP_CMD_REG_APP 0x00000040 + +/* bit mapping of data control register */ +#define SDC_DATA_CTRL_REG_BLK_SIZE 0x0000000F +#define SDC_DATA_CTRL_REG_DATA_WRITE 0x00000010 +#define SDC_DATA_CTRL_REG_DMA_EN 0x00000020 +#define SDC_DATA_CTRL_REG_DATA_EN 0x00000040 +#define SDC_DATA_CTRL_REG_FIFOTH 0x00000080 +#define SDC_DATA_CTRL_REG_DMA_TYPE 0x00000300 +#define SDC_DATA_CTRL_REG_FIFO_RST 0x00000400 +#define SDC_CPRM_DATA_CHANGE_ENDIAN_EN 0x00000800 +#define SDC_CPRM_DATA_SWAP_HL_EN 0x00001000 + +#define SDC_DMA_TYPE_1 0x00000000 +#define SDC_DMA_TYPE_4 0x00000100 +#define SDC_DMA_TYPE_8 0x00000200 + +/* bit mapping of status register */ +#define SDC_STATUS_REG_RSP_CRC_FAIL 0x00000001 +#define SDC_STATUS_REG_DATA_CRC_FAIL 0x00000002 +#define SDC_STATUS_REG_RSP_TIMEOUT 0x00000004 +#define SDC_STATUS_REG_DATA_TIMEOUT 0x00000008 +#define SDC_STATUS_REG_RSP_CRC_OK 0x00000010 +#define SDC_STATUS_REG_DATA_CRC_OK 0x00000020 +#define SDC_STATUS_REG_CMD_SEND 0x00000040 +#define SDC_STATUS_REG_DATA_END 0x00000080 +#define SDC_STATUS_REG_FIFO_UNDERRUN 0x00000100 +#define SDC_STATUS_REG_FIFO_OVERRUN 0x00000200 +#define SDC_STATUS_REG_CARD_CHANGE 0x00000400 +#define SDC_STATUS_REG_CARD_DETECT 0x00000800 +#define SDC_STATUS_REG_CARD_LOCK 0x00001000 +#define SDC_STATUS_REG_CP_READY 0x00002000 +#define SDC_STATUS_REG_CP_BUF_READY 0x00004000 +#define SDC_STATUS_REG_PLAIN_TEXT_READY 0x00008000 +#define SDC_STATUS_REG_SDIO_INTR 0x00010000 + +/* bit mapping of clear register */ +#define SDC_CLEAR_REG_RSP_CRC_FAIL 0x00000001 +#define SDC_CLEAR_REG_DATA_CRC_FAIL 0x00000002 +#define SDC_CLEAR_REG_RSP_TIMEOUT 0x00000004 +#define SDC_CLEAR_REG_DATA_TIMEOUT 0x00000008 +#define SDC_CLEAR_REG_RSP_CRC_OK 0x00000010 +#define SDC_CLEAR_REG_DATA_CRC_OK 0x00000020 +#define SDC_CLEAR_REG_CMD_SEND 0x00000040 +#define SDC_CLEAR_REG_DATA_END 0x00000080 +#define SDC_CLEAR_REG_CARD_CHANGE 0x00000400 +#define SDC_CLEAR_REG_SDIO_INTR 0x00010000 + +/* bit mapping of int_mask register */ +#define SDC_INT_MASK_REG_RSP_CRC_FAIL 0x00000001 +#define SDC_INT_MASK_REG_DATA_CRC_FAIL 0x00000002 +#define SDC_INT_MASK_REG_RSP_TIMEOUT 0x00000004 +#define SDC_INT_MASK_REG_DATA_TIMEOUT 0x00000008 +#define SDC_INT_MASK_REG_RSP_CRC_OK 0x00000010 +#define SDC_INT_MASK_REG_DATA_CRC_OK 0x00000020 +#define SDC_INT_MASK_REG_CMD_SEND 0x00000040 +#define SDC_INT_MASK_REG_DATA_END 0x00000080 +#define SDC_INT_MASK_REG_FIFO_UNDERRUN 0x00000100 +#define SDC_INT_MASK_REG_FIFO_OVERRUN 0x00000200 +#define SDC_INT_MASK_REG_CARD_CHANGE 0x00000400 +#define SDC_INT_MASK_REG_CARD_LOCK 0x00001000 +#define SDC_INT_MASK_REG_CP_READY 0x00002000 +#define SDC_INT_MASK_REG_CP_BUF_READY 0x00004000 +#define SDC_INT_MASK_REG_PLAIN_TEXT_READY 0x00008000 +#define SDC_INT_MASK_REG_SDIO_INTR 0x00010000 +#define SDC_CARD_INSERT 0x0 +#define SDC_CARD_REMOVE SDC_STATUS_REG_CARD_DETECT + +/* bit mapping of power control register */ +#define SDC_POWER_CTRL_REG_POWER_ON 0x00000010 +#define SDC_POWER_CTRL_REG_POWER_BITS 0x0000000F + +/* bit mapping of clock control register */ +#define SDC_CLOCK_CTRL_REG_CLK_DIV 0x0000007F +#define SDC_CLOCK_CTRL_REG_CARD_TYPE 0x00000080 +#define SDC_CLOCK_CTRL_REG_CLK_DIS 0x00000100 + +/* card type */ +#define SDC_CARD_TYPE_SD SDC_CLOCK_REG_CARD_TYPE +#define SDC_CARD_TYPE_MMC 0x0 + +/* bit mapping of bus width register */ +#define SDC_BUS_WIDTH_REG_SINGLE_BUS 0x00000001 +#define SDC_BUS_WIDTH_REG_WIDE_8_BUS 0x00000002 +#define SDC_BUS_WIDTH_REG_WIDE_4_BUS 0x00000004 +#define SDC_BUS_WIDTH_REG_WIDE_BUS_SUPPORT 0x00000018 +#define SDC_BUS_WIDTH_REG_CARD_DETECT 0x00000020 + +#define SDC_WIDE_4_BUS_SUPPORT 0x00000008 +#define SDC_WIDE_8_BUS_SUPPORT 0x00000010 + +/* bit mapping of feature register */ +#define SDC_FEATURE_REG_FIFO_DEPTH 0x000000FF +#define SDC_FEATURE_REG_CPRM_FUNCTION 0x00000100 + +/* bit mapping of sdio control register */ +#define SDC_SDIO_CTRL1_REG_SDIO_BLK_NO 0xFFFF8000 +#define SDC_SDIO_CTRL1_REG_SDIO_ENABLE 0x00004000 +#define SDC_SDIO_CTRL1_REG_READ_WAIT_ENABLE 0x00002000 +#define SDC_SDIO_CTRL1_REG_SDIO_BLK_MODE 0x00001000 +#define SDC_SDIO_CTRL1_REG_SDIO_BLK_SIZE 0x00000FFF + +/* bit mapping of sdio status register */ +#define SDC_SDIO_SDIO_STATUS_REG_FIFO_REMAIN_NO 0x00FE0000 +#define SDC_SDIO_SDIO_STATUS_REG_SDIO_BLK_CNT 0x0001FFFF + +enum ftsdc_waitfor { + COMPLETION_NONE, + COMPLETION_FINALIZE, + COMPLETION_CMDSENT, + COMPLETION_RSPFIN, + COMPLETION_XFER_PROGRESS, +}; + +struct ftsdc_dma { + struct dma_chan *chan; + struct dma_async_tx_descriptor *data_desc; +}; + +struct ftsdc_host { + struct platform_device *pdev; + struct mmc_host *mmc; + struct resource *mem; + struct clk *clk; + void __iomem *base; + int irq; + struct ftsdc_dma dma; + struct dma_chan *data_chan; + struct dma_slave_config dma_conf; + unsigned int real_rate; + bool irq_enabled; + /* bytes */ + unsigned int fifo_len; + /* keep last successful cmd to judge application specific command */ + unsigned int last_opcode; + struct mmc_request *mrq; + int cmd_is_stop; + spinlock_t complete_lock; + enum ftsdc_waitfor complete_what; + struct completion dma_complete; + bool dodma; + bool dma_finish; + /* keep next scallterlist buffer index */ + u32 buf_sgptr; + /* keep current total scallterlist buffer length */ + u32 buf_bytes; + /* keep real data size rw from sd */ + u32 buf_count; + /* keep current scallterlist buffer address */ + u32 *buf_ptr; + u32 page_cnt; + struct page *buf_page; + unsigned long buf_offset; +#define XFER_NONE 0 +#define XFER_READ 1 +#define XFER_WRITE 2 + /* keep current transfer mode */ + u32 buf_active; + int bus_width; + char dbgmsg_cmd[301]; + char dbgmsg_dat[301]; + char *status; + unsigned int ccnt, dcnt; + struct tasklet_struct pio_tasklet; + struct work_struct work; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debug_root; + struct dentry *debug_state; + struct dentry *debug_regs; +#endif +}; + +struct ftsdc_mmc_config { + /* get_cd()/get_wp() may sleep */ + int (*get_cd)(int module); + int (*get_ro)(int module); + + void (*set_power)(int module, bool on); + + /* wires == 0 is equivalent to wires == 4 (4-bit parallel) */ + u8 wires; + + u32 max_freq; + + /* any additional host capabilities: OR'd in to mmc->f_caps */ + u32 caps; + + /* Number of sg segments */ + u8 nr_sg; + void *dma_slave; + dma_filter_fn dma_filter; +}; +#endif From 8198e016ef966409b03bec57866e44ae362c6a0c Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Thu, 14 Oct 2021 15:15:33 +0800 Subject: [PATCH 015/169] mmc: andes: ftsdc010g: add new readl_fixup() when driver probing Reformed from the following patches on RISCV-Linux-5.4: - (86c6918870fe) riscv: Fix readl_fixup undefined problem - (b2f4eef97091) riscv: Add error return code when readl_fixup() failed Signed-off-by: Yu Chien Peter Lin --- drivers/mmc/host/ftsdc010g.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/mmc/host/ftsdc010g.c b/drivers/mmc/host/ftsdc010g.c index fa7a5e62e25f38..0f929775ace28f 100644 --- a/drivers/mmc/host/ftsdc010g.c +++ b/drivers/mmc/host/ftsdc010g.c @@ -1348,6 +1348,8 @@ static struct ftsdc_mmc_config static int __init ftsdc_probe(struct platform_device *pdev) { + int (*read_fixup)(void __iomem *addr, unsigned int val, + unsigned int shift_bits); struct ftsdc_host *host; struct mmc_host *mmc; struct ftsdc_mmc_config *pdata = NULL; @@ -1408,6 +1410,17 @@ static int __init ftsdc_probe(struct platform_device *pdev) goto probe_free_mem_region; } + /* Check revision register */ + read_fixup = symbol_get(readl_fixup); + ret = read_fixup(host->base + SDC_REVISION_REG, 0x00030107, 0); + symbol_put(readl_fixup); + if (!ret) { + dev_err(&pdev->dev, + "bitmap revision mismatch(ftsdc)\n"); + ret = -ENXIO; + goto probe_free_mem_region; + } + host->irq = irq; ret = request_irq(host->irq, ftsdc_irq, 0, DRIVER_NAME, host); From 3e0956d8d76047ef14e3f9bed88b226b8a10d5f0 Mon Sep 17 00:00:00 2001 From: Alan Kao Date: Tue, 3 Sep 2019 11:22:10 +0800 Subject: [PATCH 016/169] net: andes: ftmac100: Andes support for Faraday ATCMAC Reformed from the following patches on RISCV-Linux-5.4: - (604c07b887a1) Andes support for Faraday ATCMAC - (d61585d8b2a5) ftmac100.c: fix deprecated API - (7c0206d07793) driver/net/ethernet/faraday: Support 64bit mac driver. - (bb889b5a0d27) faraday/ftmac100.c: Solve NIC RPKT_LOST & NORXBUF issue - (86aca052be66) driver/net/ethernet/faraday: Add ftmac100_debug control. - (133384ba074a) faraday/ftmac100.c: Support FTMAC100 with suspend & resume functions. Signed-off-by: Alan Kao Signed-off-by: Yu Chien Peter Lin Signed-off-by: Charles Ci-Jyun Wu --- drivers/net/ethernet/faraday/Kconfig | 8 +- drivers/net/ethernet/faraday/Makefile | 2 +- drivers/net/ethernet/faraday/ftmac100.c | 234 +++++++++++++----- drivers/net/ethernet/faraday/ftmac100.h | 75 +++--- drivers/net/ethernet/faraday/ftmac100_debug.c | 193 +++++++++++++++ 5 files changed, 409 insertions(+), 103 deletions(-) create mode 100644 drivers/net/ethernet/faraday/ftmac100_debug.c diff --git a/drivers/net/ethernet/faraday/Kconfig b/drivers/net/ethernet/faraday/Kconfig index c699bd6bcbb938..1c12978ab8b026 100644 --- a/drivers/net/ethernet/faraday/Kconfig +++ b/drivers/net/ethernet/faraday/Kconfig @@ -6,7 +6,7 @@ config NET_VENDOR_FARADAY bool "Faraday devices" default y - depends on ARM || COMPILE_TEST + depends on ARM || RISCV || COMPILE_TEST help If you have a network (Ethernet) card belonging to this class, say Y. @@ -19,12 +19,12 @@ if NET_VENDOR_FARADAY config FTMAC100 tristate "Faraday FTMAC100 10/100 Ethernet support" - depends on ARM || COMPILE_TEST - depends on !64BIT || BROKEN + depends on ARM || RISCV || COMPILE_TEST select MII help This driver supports the FTMAC100 10/100 Ethernet controller - from Faraday. It is used on Faraday A320 and some other ARM SoC's. + from Faraday. It is used on Faraday A320, Andes AG101 and some + other ARM/RISCV SoC's. config FTGMAC100 tristate "Faraday FTGMAC100 Gigabit Ethernet support" diff --git a/drivers/net/ethernet/faraday/Makefile b/drivers/net/ethernet/faraday/Makefile index f16f58467868c5..950f34d5e86fd1 100644 --- a/drivers/net/ethernet/faraday/Makefile +++ b/drivers/net/ethernet/faraday/Makefile @@ -4,4 +4,4 @@ # obj-$(CONFIG_FTGMAC100) += ftgmac100.o -obj-$(CONFIG_FTMAC100) += ftmac100.o +obj-$(CONFIG_FTMAC100) += ftmac100.o ftmac100_debug.o diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index d95d782308280a..beb674241bef07 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -4,6 +4,19 @@ * * (C) Copyright 2009-2011 Faraday Technology * Po-Yu Chuang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -38,12 +51,29 @@ #error invalid RX_BUF_SIZE #endif +#define xprintk(...) + +#define FTMAC100_RX_DESC(priv, index) (&priv->descs->rxdes[index]) +#define FTMAC100_RX_DESC_EXT(priv, index) (&priv->descs->rxdes_ext[index]) +#define FTMAC100_TX_DESC(priv, index) (&priv->descs->txdes[index]) +#define FTMAC100_TX_DESC_EXT(priv, index) (&priv->descs->txdes_ext[index]) + +#define FTMAC100_CURRENT_RX_DESC_INDEX(priv) (priv->rx_pointer) +#define FTMAC100_CURRENT_TX_DESC_INDEX(priv) (priv->tx_pointer); +#define FTMAC100_CURRENT_CLEAN_TX_DESC_INDEX(priv) (priv->tx_clean_pointer); + +/* ftmac100_debug parameters */ +extern unsigned int FTMAC100_DEBUG; +extern unsigned int FTMAC100_INCR; + /****************************************************************************** * private data *****************************************************************************/ struct ftmac100_descs { struct ftmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; struct ftmac100_txdes txdes[TX_QUEUE_ENTRIES]; + struct ftmac100_rxdes_ext rxdes_ext[RX_QUEUE_ENTRIES]; + struct ftmac100_txdes_ext txdes_ext[TX_QUEUE_ENTRIES]; }; struct ftmac100 { @@ -69,7 +99,7 @@ struct ftmac100 { }; static int ftmac100_alloc_rx_page(struct ftmac100 *priv, - struct ftmac100_rxdes *rxdes, gfp_t gfp); + int index, gfp_t gfp); /****************************************************************************** * internal functions (hardware register access) @@ -96,11 +126,13 @@ static void ftmac100_disable_all_int(struct ftmac100 *priv) static void ftmac100_set_rx_ring_base(struct ftmac100 *priv, dma_addr_t addr) { + xprintk("%s: addr %p\n", __func__, (void *)addr); iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR); } static void ftmac100_set_tx_ring_base(struct ftmac100 *priv, dma_addr_t addr) { + xprintk("%s: addr %p\n", __func__, (void *)addr); iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR); } @@ -173,6 +205,19 @@ static int ftmac100_start_hw(struct ftmac100 *priv) iowrite32(FTMAC100_APTC_RXPOLL_CNT(1), priv->base + FTMAC100_OFFSET_APTC); + // Enable DMA Burst & RXFIFO threshold + iowrite32(FTMAC100_DBLAC_RX_THR_EN | /* Enable fifo threshold arb */ + FTMAC100_DBLAC_INCR16_EN | /* Enable INCR[4/8/16] DMA Burst, + this option solve RX RPKT_LOST issue */ + FTMAC100_DBLAC_RXFIFO_HTHR(6) | /* 6/8 of FIFO high threshold */ + FTMAC100_DBLAC_RXFIFO_LTHR(2), /* 2/8 of FIFO low threshold */ + priv->base + FTMAC100_OFFSET_DBLAC); + + // Pending interrupt until receive packets reach threshold + iowrite32(FTMAC100_ITC_RXINT_THR(1) | + FTMAC100_ITC_TXINT_THR(1), + priv->base + FTMAC100_OFFSET_ITC); + ftmac100_set_mac(priv, netdev->dev_addr); iowrite32(MACCR_ENABLE_ALL, priv->base + FTMAC100_OFFSET_MACCR); @@ -259,25 +304,22 @@ static void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes, dma_addr_t addr) { rxdes->rxdes2 = cpu_to_le32(addr); + rxdes->rxdes3 = cpu_to_le32(addr >> 32); } static dma_addr_t ftmac100_rxdes_get_dma_addr(struct ftmac100_rxdes *rxdes) { - return le32_to_cpu(rxdes->rxdes2); + return le32_to_cpu(rxdes->rxdes2) | (dma_addr_t)le32_to_cpu(rxdes->rxdes3) << 32; } -/* - * rxdes3 is not used by hardware. We use it to keep track of page. - * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). - */ -static void ftmac100_rxdes_set_page(struct ftmac100_rxdes *rxdes, struct page *page) +static void ftmac100_rxdes_set_page(struct ftmac100 *priv, int index, struct page *page) { - rxdes->rxdes3 = (unsigned int)page; + FTMAC100_RX_DESC_EXT(priv, index)->page = page; } -static struct page *ftmac100_rxdes_get_page(struct ftmac100_rxdes *rxdes) +static struct page *ftmac100_rxdes_get_page(struct ftmac100 *priv, int index) { - return (struct page *)rxdes->rxdes3; + return (struct page *)FTMAC100_RX_DESC_EXT(priv, index)->page; } /****************************************************************************** @@ -293,26 +335,23 @@ static void ftmac100_rx_pointer_advance(struct ftmac100 *priv) priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer); } -static struct ftmac100_rxdes *ftmac100_current_rxdes(struct ftmac100 *priv) -{ - return &priv->descs->rxdes[priv->rx_pointer]; -} - -static struct ftmac100_rxdes * +static int ftmac100_rx_locate_first_segment(struct ftmac100 *priv) { - struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv); + int index = FTMAC100_CURRENT_RX_DESC_INDEX(priv); + struct ftmac100_rxdes *rxdes = FTMAC100_RX_DESC(priv, index); while (!ftmac100_rxdes_owned_by_dma(rxdes)) { if (ftmac100_rxdes_first_segment(rxdes)) - return rxdes; + return index; ftmac100_rxdes_set_dma_own(rxdes); ftmac100_rx_pointer_advance(priv); - rxdes = ftmac100_current_rxdes(priv); + index = FTMAC100_CURRENT_RX_DESC_INDEX(priv); + rxdes = FTMAC100_RX_DESC(priv, index); } - return NULL; + return -1; } static bool ftmac100_rx_packet_error(struct ftmac100 *priv, @@ -363,9 +402,13 @@ static bool ftmac100_rx_packet_error(struct ftmac100 *priv, static void ftmac100_rx_drop_packet(struct ftmac100 *priv) { struct net_device *netdev = priv->netdev; - struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv); + struct ftmac100_rxdes *rxdes; + int index; bool done = false; + index = FTMAC100_CURRENT_RX_DESC_INDEX(priv); + rxdes = FTMAC100_RX_DESC(priv, index); + if (net_ratelimit()) netdev_dbg(netdev, "drop packet %p\n", rxdes); @@ -375,7 +418,8 @@ static void ftmac100_rx_drop_packet(struct ftmac100 *priv) ftmac100_rxdes_set_dma_own(rxdes); ftmac100_rx_pointer_advance(priv); - rxdes = ftmac100_current_rxdes(priv); + index = FTMAC100_CURRENT_RX_DESC_INDEX(priv); + rxdes = FTMAC100_RX_DESC(priv, index); } while (!done && !ftmac100_rxdes_owned_by_dma(rxdes)); netdev->stats.rx_dropped++; @@ -389,11 +433,13 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed) struct page *page; dma_addr_t map; int length; + int index; bool ret; - rxdes = ftmac100_rx_locate_first_segment(priv); - if (!rxdes) + index = ftmac100_rx_locate_first_segment(priv); + if (index < 0) return false; + rxdes = FTMAC100_RX_DESC(priv, index); if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) { ftmac100_rx_drop_packet(priv); @@ -424,7 +470,7 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed) dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); length = ftmac100_rxdes_frame_length(rxdes); - page = ftmac100_rxdes_get_page(rxdes); + page = ftmac100_rxdes_get_page(priv, index); skb_fill_page_desc(skb, 0, page, 0, length); skb->len += length; skb->data_len += length; @@ -437,7 +483,7 @@ static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed) /* Small frames are copied into linear part to free one page */ __pskb_pull_tail(skb, length); } - ftmac100_alloc_rx_page(priv, rxdes, GFP_ATOMIC); + ftmac100_alloc_rx_page(priv, index, GFP_ATOMIC); ftmac100_rx_pointer_advance(priv); @@ -520,25 +566,27 @@ static void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes, dma_addr_t addr) { txdes->txdes2 = cpu_to_le32(addr); + txdes->txdes3 = cpu_to_le32(addr >> 32); } static dma_addr_t ftmac100_txdes_get_dma_addr(struct ftmac100_txdes *txdes) { - return le32_to_cpu(txdes->txdes2); + return le32_to_cpu(txdes->txdes2) | (dma_addr_t)le32_to_cpu(txdes->txdes3) << 32; } -/* - * txdes3 is not used by hardware. We use it to keep track of socket buffer. - * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). - */ -static void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes, struct sk_buff *skb) +static void ftmac100_txdes_skb_reset(struct ftmac100 *priv, int index) +{ + FTMAC100_TX_DESC_EXT(priv, index)->skb = NULL; +} + +static void ftmac100_txdes_set_skb(struct ftmac100 *priv, int index, struct sk_buff *skb) { - txdes->txdes3 = (unsigned int)skb; + FTMAC100_TX_DESC_EXT(priv, index)->skb = skb; } -static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes) +static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100 *priv, int index) { - return (struct sk_buff *)txdes->txdes3; + return (struct sk_buff *)FTMAC100_TX_DESC_EXT(priv, index)->skb; } /****************************************************************************** @@ -559,32 +607,24 @@ static void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv) priv->tx_clean_pointer = ftmac100_next_tx_pointer(priv->tx_clean_pointer); } -static struct ftmac100_txdes *ftmac100_current_txdes(struct ftmac100 *priv) -{ - return &priv->descs->txdes[priv->tx_pointer]; -} - -static struct ftmac100_txdes *ftmac100_current_clean_txdes(struct ftmac100 *priv) -{ - return &priv->descs->txdes[priv->tx_clean_pointer]; -} - static bool ftmac100_tx_complete_packet(struct ftmac100 *priv) { struct net_device *netdev = priv->netdev; struct ftmac100_txdes *txdes; struct sk_buff *skb; dma_addr_t map; + int index; if (priv->tx_pending == 0) return false; - txdes = ftmac100_current_clean_txdes(priv); + index = FTMAC100_CURRENT_CLEAN_TX_DESC_INDEX(priv); + txdes = FTMAC100_TX_DESC(priv, index); if (ftmac100_txdes_owned_by_dma(txdes)) return false; - skb = ftmac100_txdes_get_skb(txdes); + skb = ftmac100_txdes_get_skb(priv, index); map = ftmac100_txdes_get_dma_addr(txdes); if (unlikely(ftmac100_txdes_excessive_collision(txdes) || @@ -603,6 +643,7 @@ static bool ftmac100_tx_complete_packet(struct ftmac100 *priv) dev_kfree_skb(skb); ftmac100_txdes_reset(txdes); + ftmac100_txdes_skb_reset(priv, index); ftmac100_tx_clean_pointer_advance(priv); @@ -620,18 +661,20 @@ static void ftmac100_tx_complete(struct ftmac100 *priv) ; } -static netdev_tx_t ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb, - dma_addr_t map) +static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb, + dma_addr_t map) { struct net_device *netdev = priv->netdev; struct ftmac100_txdes *txdes; unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; + int index; - txdes = ftmac100_current_txdes(priv); + index = FTMAC100_CURRENT_TX_DESC_INDEX(priv); + txdes = FTMAC100_TX_DESC(priv, index); ftmac100_tx_pointer_advance(priv); /* setup TX descriptor */ - ftmac100_txdes_set_skb(txdes, skb); + ftmac100_txdes_set_skb(priv, index, skb); ftmac100_txdes_set_dma_addr(txdes, map); ftmac100_txdes_set_first_segment(txdes); @@ -656,9 +699,10 @@ static netdev_tx_t ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb, * internal functions (buffer) *****************************************************************************/ static int ftmac100_alloc_rx_page(struct ftmac100 *priv, - struct ftmac100_rxdes *rxdes, gfp_t gfp) + int index, gfp_t gfp) { struct net_device *netdev = priv->netdev; + struct ftmac100_rxdes *rxdes = FTMAC100_RX_DESC(priv, index); struct page *page; dma_addr_t map; @@ -677,7 +721,7 @@ static int ftmac100_alloc_rx_page(struct ftmac100 *priv, return -ENOMEM; } - ftmac100_rxdes_set_page(rxdes, page); + ftmac100_rxdes_set_page(priv, index, page); ftmac100_rxdes_set_dma_addr(rxdes, map); ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE); ftmac100_rxdes_set_dma_own(rxdes); @@ -689,8 +733,8 @@ static void ftmac100_free_buffers(struct ftmac100 *priv) int i; for (i = 0; i < RX_QUEUE_ENTRIES; i++) { - struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; - struct page *page = ftmac100_rxdes_get_page(rxdes); + struct ftmac100_rxdes *rxdes = FTMAC100_RX_DESC(priv, i); + struct page *page = ftmac100_rxdes_get_page(priv, i); dma_addr_t map = ftmac100_rxdes_get_dma_addr(rxdes); if (!page) @@ -701,8 +745,8 @@ static void ftmac100_free_buffers(struct ftmac100 *priv) } for (i = 0; i < TX_QUEUE_ENTRIES; i++) { - struct ftmac100_txdes *txdes = &priv->descs->txdes[i]; - struct sk_buff *skb = ftmac100_txdes_get_skb(txdes); + struct ftmac100_txdes *txdes = FTMAC100_TX_DESC(priv, i); + struct sk_buff *skb = ftmac100_txdes_get_skb(priv, i); dma_addr_t map = ftmac100_txdes_get_dma_addr(txdes); if (!skb) @@ -722,7 +766,8 @@ static int ftmac100_alloc_buffers(struct ftmac100 *priv) priv->descs = dma_alloc_coherent(priv->dev, sizeof(struct ftmac100_descs), - &priv->descs_dma_addr, GFP_KERNEL); + &priv->descs_dma_addr, + GFP_KERNEL); if (!priv->descs) return -ENOMEM; @@ -730,9 +775,7 @@ static int ftmac100_alloc_buffers(struct ftmac100 *priv) ftmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]); for (i = 0; i < RX_QUEUE_ENTRIES; i++) { - struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; - - if (ftmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL)) + if (ftmac100_alloc_rx_page(priv, i, GFP_KERNEL)) goto err; } @@ -909,7 +952,7 @@ static int ftmac100_poll(struct napi_struct *napi, int budget) if (status & (FTMAC100_INT_NORXBUF | FTMAC100_INT_RPKT_LOST | FTMAC100_INT_AHB_ERR | FTMAC100_INT_PHYSTS_CHG)) { - if (net_ratelimit()) + if (net_ratelimit() && FTMAC100_DEBUG) netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status, status & FTMAC100_INT_NORXBUF ? "NORXBUF " : "", status & FTMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "", @@ -961,6 +1004,15 @@ static int ftmac100_open(struct net_device *netdev) goto err_irq; } + // set sysctl ip fragmentation parameters. + // sysctl -w net.ipv4.ipfrag_time + // sysctl -w net.ipv4.ipfrag_high_thresh + struct net *net; + + net = dev_net(netdev); + net->ipv4.fqdir->timeout = (5 * HZ); /* Decrease fragment timeout, 30 -> 5 */ + net->ipv4.fqdir->high_thresh = 8 * 1024 * 1024; /* Increase fragment buffer size, 4M -> 8M */ + priv->rx_pointer = 0; priv->tx_clean_pointer = 0; priv->tx_pointer = 0; @@ -999,7 +1051,7 @@ static int ftmac100_stop(struct net_device *netdev) return 0; } -static netdev_tx_t +static int ftmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct ftmac100 *priv = netdev_priv(netdev); @@ -1166,6 +1218,58 @@ static int ftmac100_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int ftmac100_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *netdev = platform_get_drvdata(pdev); + struct ftmac100 *priv = netdev_priv(netdev); + + if (!netif_running(netdev)) + return 0; + + pr_info("ftmac100_suspend() ...\n"); + + ftmac100_disable_all_int(priv); + + netif_stop_queue(netdev); + + napi_disable(&priv->napi); + + netif_tx_lock(netdev); + netif_device_detach(netdev); + netif_tx_unlock(netdev); + + ftmac100_stop_hw(priv); + + return 0; +} + +static int ftmac100_resume(struct platform_device *pdev) +{ + struct net_device *netdev = platform_get_drvdata(pdev); + struct ftmac100 *priv = netdev_priv(netdev); + + if (!netif_running(netdev)) + return 0; + + pr_info("ftmac100_resume() ...\n"); + + iowrite32(MACCR_ENABLE_ALL, priv->base + FTMAC100_OFFSET_MACCR); + + napi_enable(&priv->napi); + + netif_tx_lock(netdev); + netif_device_attach(netdev); + netif_tx_unlock(netdev); + + netif_start_queue(netdev); + + ftmac100_enable_all_int(priv); + + return 0; +} +#endif /* CONFIG_PM */ + static const struct of_device_id ftmac100_of_ids[] = { { .compatible = "andestech,atmac100" }, { } @@ -1178,6 +1282,10 @@ static struct platform_driver ftmac100_driver = { .name = DRV_NAME, .of_match_table = ftmac100_of_ids }, +#ifdef CONFIG_PM + .suspend = ftmac100_suspend, + .resume = ftmac100_resume, +#endif /* CONFIG_PM */ }; /****************************************************************************** diff --git a/drivers/net/ethernet/faraday/ftmac100.h b/drivers/net/ethernet/faraday/ftmac100.h index 8af32f9070f4c8..4269b0c1400a7e 100644 --- a/drivers/net/ethernet/faraday/ftmac100.h +++ b/drivers/net/ethernet/faraday/ftmac100.h @@ -4,6 +4,19 @@ * * (C) Copyright 2009-2011 Faraday Technology * Po-Yu Chuang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this progra. */ #ifndef __FTMAC100_H @@ -22,6 +35,8 @@ #define FTMAC100_OFFSET_ITC 0x28 #define FTMAC100_OFFSET_APTC 0x2c #define FTMAC100_OFFSET_DBLAC 0x30 +#define FTMAC100_OFFSET_TXR_BADR_H 0x40 +#define FTMAC100_OFFSET_RXR_BADR_H 0x44 #define FTMAC100_OFFSET_MACCR 0x88 #define FTMAC100_OFFSET_MACSR 0x8c #define FTMAC100_OFFSET_PHYCR 0x90 @@ -42,9 +57,7 @@ #define FTMAC100_OFFSET_RP 0xf4 #define FTMAC100_OFFSET_XP 0xf8 -/* - * Interrupt status register & interrupt mask register - */ +/* Interrupt status register & interrupt mask register */ #define FTMAC100_INT_RPKT_FINISH (1 << 0) #define FTMAC100_INT_NORXBUF (1 << 1) #define FTMAC100_INT_XPKT_FINISH (1 << 2) @@ -56,9 +69,7 @@ #define FTMAC100_INT_AHB_ERR (1 << 8) #define FTMAC100_INT_PHYSTS_CHG (1 << 9) -/* - * Interrupt timer control register - */ +/* Interrupt timer control register */ #define FTMAC100_ITC_RXINT_CNT(x) (((x) & 0xf) << 0) #define FTMAC100_ITC_RXINT_THR(x) (((x) & 0x7) << 4) #define FTMAC100_ITC_RXINT_TIME_SEL (1 << 7) @@ -66,17 +77,13 @@ #define FTMAC100_ITC_TXINT_THR(x) (((x) & 0x7) << 12) #define FTMAC100_ITC_TXINT_TIME_SEL (1 << 15) -/* - * Automatic polling timer control register - */ +/* Automatic polling timer control register */ #define FTMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0) #define FTMAC100_APTC_RXPOLL_TIME_SEL (1 << 4) #define FTMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8) #define FTMAC100_APTC_TXPOLL_TIME_SEL (1 << 12) -/* - * DMA burst length and arbitration control register - */ +/* DMA burst length and arbitration control register */ #define FTMAC100_DBLAC_INCR4_EN (1 << 0) #define FTMAC100_DBLAC_INCR8_EN (1 << 1) #define FTMAC100_DBLAC_INCR16_EN (1 << 2) @@ -84,9 +91,7 @@ #define FTMAC100_DBLAC_RXFIFO_HTHR(x) (((x) & 0x7) << 6) #define FTMAC100_DBLAC_RX_THR_EN (1 << 9) -/* - * MAC control register - */ +/* MAC control register */ #define FTMAC100_MACCR_XDMA_EN (1 << 0) #define FTMAC100_MACCR_RDMA_EN (1 << 1) #define FTMAC100_MACCR_SW_RST (1 << 2) @@ -104,28 +109,22 @@ #define FTMAC100_MACCR_RX_MULTIPKT (1 << 16) #define FTMAC100_MACCR_RX_BROADPKT (1 << 17) -/* - * PHY control register - */ +/* PHY control register */ #define FTMAC100_PHYCR_MIIRDATA 0xffff #define FTMAC100_PHYCR_PHYAD(x) (((x) & 0x1f) << 16) #define FTMAC100_PHYCR_REGAD(x) (((x) & 0x1f) << 21) #define FTMAC100_PHYCR_MIIRD (1 << 26) #define FTMAC100_PHYCR_MIIWR (1 << 27) -/* - * PHY write data register - */ +/* PHY write data register */ #define FTMAC100_PHYWDATA_MIIWDATA(x) ((x) & 0xffff) -/* - * Transmit descriptor, aligned to 16 bytes - */ +/* Transmit descriptor, aligned to 16 bytes */ struct ftmac100_txdes { - __le32 txdes0; - __le32 txdes1; - __le32 txdes2; /* TXBUF_BADR */ - unsigned int txdes3; /* not used by HW */ + unsigned int txdes0; + unsigned int txdes1; + unsigned int txdes2; /* TXBUF_BADR */ + unsigned int txdes3; /* TXBUF_BADR_H */ } __attribute__ ((aligned(16))); #define FTMAC100_TXDES0_TXPKT_LATECOL (1 << 0) @@ -139,14 +138,12 @@ struct ftmac100_txdes { #define FTMAC100_TXDES1_TXIC (1 << 30) #define FTMAC100_TXDES1_EDOTR (1 << 31) -/* - * Receive descriptor, aligned to 16 bytes - */ +/* Receive descriptor, aligned to 16 bytes */ struct ftmac100_rxdes { - __le32 rxdes0; - __le32 rxdes1; - __le32 rxdes2; /* RXBUF_BADR */ - unsigned int rxdes3; /* not used by HW */ + unsigned int rxdes0; + unsigned int rxdes1; + unsigned int rxdes2; /* RXBUF_BADR */ + unsigned int rxdes3; /* RXBUF_BADR_H */ } __attribute__ ((aligned(16))); #define FTMAC100_RXDES0_RFL 0x7ff @@ -164,4 +161,12 @@ struct ftmac100_rxdes { #define FTMAC100_RXDES1_RXBUF_SIZE(x) ((x) & 0x7ff) #define FTMAC100_RXDES1_EDORR (1 << 31) +struct ftmac100_txdes_ext { + void *skb; +}; + +struct ftmac100_rxdes_ext { + void *page; +}; + #endif /* __FTMAC100_H */ diff --git a/drivers/net/ethernet/faraday/ftmac100_debug.c b/drivers/net/ethernet/faraday/ftmac100_debug.c new file mode 100644 index 00000000000000..a2db0f2aa3384c --- /dev/null +++ b/drivers/net/ethernet/faraday/ftmac100_debug.c @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2009 Andes Technology Corporation + * Copyright (C) 2019 Andes Technology Corporation + * + * 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 + +#define INPUTLEN 32 + +/* 0: DISABLE + * 1: ENABLE + */ +unsigned int FTMAC100_DEBUG; + +/* 0: DISABLE + * 1: INCR4 + * 2: INCR8 + * 4: INCR16 + */ +unsigned int FTMAC100_INCR; + +#define ENTRY_NUMBER 2 + +struct entry_struct { + char *name; + int perm; + const struct proc_ops *p_ops; +}; + +static struct proc_dir_entry *proc_ftmac100_debug; + +#define DEBUG(enable, tagged, ...) \ + do { \ + if (enable) { \ + if (tagged) \ + printk("[ %30s() ] ", __func__); \ + printk(__VA_ARGS__); \ + } \ + } while (0) + +static int debug; +module_param(debug, int, 0); + +static ssize_t ftmac100_proc_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + int ret = 0; + char buf[128] = {0}; + + if (!strncmp(file->f_path.dentry->d_name.name, "debug", 5)) { + ret = sprintf(buf, "FTMAC100 debug info: %s\n", + (FTMAC100_DEBUG) ? "Enabled" : "Disabled"); + } else if (!strncmp(file->f_path.dentry->d_name.name, "incr", 4)) { + switch (FTMAC100_INCR) { + case 0: + ret = sprintf(buf, "FTMAC100 INCR: %s\n", "Disabled"); + break; + case 1: + ret = sprintf(buf, "FTMAC100 INCR: %d (INCR4)\n", + FTMAC100_INCR); + break; + case 2: + ret = sprintf(buf, "FTMAC100 INCR: %d (INCR8)\n", + FTMAC100_INCR); + break; + case 4: + ret = sprintf(buf, "FTMAC100 INCR: %d (INCR16)\n", + FTMAC100_INCR); + break; + } + } else + return -EFAULT; + + return simple_read_from_buffer(userbuf, count, ppos, buf, ret); +} + +static ssize_t ftmac100_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *ppos) +{ + unsigned long en; + char inbuf[INPUTLEN]; + + if (count > INPUTLEN - 1) + count = INPUTLEN - 1; + + if (copy_from_user(inbuf, buffer, count)) + return -EFAULT; + + inbuf[count] = '\0'; + + if (!sscanf(inbuf, "%lu", &en)) + return -EFAULT; + + if (!strncmp(file->f_path.dentry->d_name.name, "debug", 5)) { + FTMAC100_DEBUG = en; + } else if (!strncmp(file->f_path.dentry->d_name.name, "incr", 4)) { + switch (en) { + case 0: + case 1: + case 2: + case 4: + FTMAC100_INCR = en; + printk("Please restart eth0 interface to apply INCR\n"); + printk(" ~# ifconfig eth0 down\n"); + printk(" ~# udhcpc\n"); + break; + default: + printk("INCR value must be [0/1/2/4]\n"); + printk(" 0: INCR disabled\n"); + printk(" 1: INCR4\n"); + printk(" 2: INCR8\n"); + printk(" 4: INCR16\n"); + } + } else { + return -EFAULT; + } + + return count; +} + +static const struct proc_ops en_fops = { + .proc_open = simple_open, + .proc_read = ftmac100_proc_read, + .proc_write = ftmac100_proc_write, +}; + +static void create_seq_entry(struct entry_struct *e, mode_t mode, + struct proc_dir_entry *parent) +{ + struct proc_dir_entry *entry = + proc_create(e->name, mode, parent, e->p_ops); + + if (!entry) + printk(KERN_ERR "invalid %s register.\n", e->name); +} + +static void install_proc_table(struct entry_struct *table) +{ + int i; + + for (i = 0; i < ENTRY_NUMBER; table++, i++) + create_seq_entry(table, table->perm, proc_ftmac100_debug); +} + +static void remove_proc_table(struct entry_struct *table) +{ + int i; + + for (i = 0; i < ENTRY_NUMBER; table++, i++) + remove_proc_entry(table->name, proc_ftmac100_debug); +} + +struct entry_struct proc_table_ftmac100_debug[ENTRY_NUMBER] = { + {"debug", 0644, &en_fops}, + {"incr", 0644, &en_fops}, +}; + +static int __init init_ftmac100_debug(void) +{ + FTMAC100_DEBUG = 0; + FTMAC100_INCR = 4; + debug = 0; + DEBUG(debug, 1, "ftmac100_debug module registered\n"); + + proc_ftmac100_debug = proc_mkdir("ftmac100_debug", NULL); + if (!proc_ftmac100_debug) + return -ENOMEM; + + install_proc_table(proc_table_ftmac100_debug); + + return 0; +} + +static void __exit cleanup_ftmac100_debug(void) +{ + remove_proc_table(proc_table_ftmac100_debug); + remove_proc_entry("ftmac100_debug", NULL); + + DEBUG(debug, 1, "ftmac100_debug module unregistered\n"); +} + +module_init(init_ftmac100_debug); +module_exit(cleanup_ftmac100_debug); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ftmac100_debug Module"); From 5a6888d78373df25c76a38d6f935d506482de25d Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Tue, 2 Aug 2022 09:39:00 +0800 Subject: [PATCH 017/169] net: andes: ftmac100: add new readl_fixup() when driver probing Reformed from the following patches on RISCV-Linux-5.4: - (77573fb3af94) riscv: Add new readl_fixup() when driver probing - (2c7bf75454be) Modify readl_fixup check register to Revision register - (21033a8b8069) riscv: Add shift_bits parameter on readl_fixup() - (86c6918870fe) riscv: Fix readl_fixup undefined problem - (Create by Peter) fixup FTMAC100 riscv: Fix readl_fixup undefined problem Signed-off-by: Yu Chien Peter Lin Signed-off-by: Charles Ci-Jyun Wu --- drivers/net/ethernet/faraday/ftmac100.c | 17 +++++++++++++++-- drivers/net/ethernet/faraday/ftmac100.h | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index beb674241bef07..701f3e9ac6ac3c 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -34,6 +34,7 @@ #include #include "ftmac100.h" +#include #define DRV_NAME "ftmac100" @@ -990,6 +991,7 @@ static int ftmac100_poll(struct napi_struct *napi, int budget) static int ftmac100_open(struct net_device *netdev) { struct ftmac100 *priv = netdev_priv(netdev); + struct net *net; int err; err = ftmac100_alloc_buffers(priv); @@ -1007,7 +1009,6 @@ static int ftmac100_open(struct net_device *netdev) // set sysctl ip fragmentation parameters. // sysctl -w net.ipv4.ipfrag_time // sysctl -w net.ipv4.ipfrag_high_thresh - struct net *net; net = dev_net(netdev); net->ipv4.fqdir->timeout = (5 * HZ); /* Decrease fragment timeout, 30 -> 5 */ @@ -1103,11 +1104,13 @@ static const struct net_device_ops ftmac100_netdev_ops = { *****************************************************************************/ static int ftmac100_probe(struct platform_device *pdev) { + int (*read_fixup)(void __iomem *addr, unsigned int val, + unsigned int shift_bits); struct resource *res; int irq; struct net_device *netdev; struct ftmac100 *priv; - int err; + int err, ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -1161,6 +1164,16 @@ static int ftmac100_probe(struct platform_device *pdev) goto err_ioremap; } + /* Check feature register */ + read_fixup = symbol_get(readl_fixup); + ret = read_fixup(priv->base + FTMAC100_OFFSET_REVISION, 0x00010407, 0); + symbol_put(readl_fixup); + if (!ret) { + dev_err(&pdev->dev, "fail to read revision reg, bitmap not support ftmac100\n"); + err = -EIO; + goto err_ioremap; + } + priv->irq = irq; /* initialize struct mii_if_info */ diff --git a/drivers/net/ethernet/faraday/ftmac100.h b/drivers/net/ethernet/faraday/ftmac100.h index 4269b0c1400a7e..11fe1f1d586285 100644 --- a/drivers/net/ethernet/faraday/ftmac100.h +++ b/drivers/net/ethernet/faraday/ftmac100.h @@ -35,6 +35,7 @@ #define FTMAC100_OFFSET_ITC 0x28 #define FTMAC100_OFFSET_APTC 0x2c #define FTMAC100_OFFSET_DBLAC 0x30 +#define FTMAC100_OFFSET_REVISION 0x34 #define FTMAC100_OFFSET_TXR_BADR_H 0x40 #define FTMAC100_OFFSET_RXR_BADR_H 0x44 #define FTMAC100_OFFSET_MACCR 0x88 From 5c1393c7adb9ecd738edbac2ef6787dbf8bfe5d5 Mon Sep 17 00:00:00 2001 From: Dylan Jhong Date: Fri, 24 Feb 2023 17:52:23 +0800 Subject: [PATCH 018/169] RISC-V: mm: Support huge page in vmalloc_fault() Since RISC-V supports ioremap() with huge page (pud/pmd) mapping, However, vmalloc_fault() assumes that the vmalloc range is limited to pte mappings. To complete the vmalloc_fault() function by adding huge page support. Fixes: 310f541a027b ("riscv: Enable HAVE_ARCH_HUGE_VMAP for 64BIT") Signed-off-by: Dylan Jhong Reviewed-by: Alexandre Ghiti --- arch/riscv/mm/fault.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/riscv/mm/fault.c b/arch/riscv/mm/fault.c index 274bc6dd839fa7..e6337ae3bc8f0d 100644 --- a/arch/riscv/mm/fault.c +++ b/arch/riscv/mm/fault.c @@ -152,6 +152,8 @@ static inline void vmalloc_fault(struct pt_regs *regs, int code, unsigned long a no_context(regs, addr); return; } + if (pud_leaf(*pud_k)) + goto flush_tlb; /* * Since the vmalloc area is global, it is unnecessary @@ -162,6 +164,8 @@ static inline void vmalloc_fault(struct pt_regs *regs, int code, unsigned long a no_context(regs, addr); return; } + if (pmd_leaf(*pmd_k)) + goto flush_tlb; /* * Make sure the actual PTE exists as well to @@ -181,6 +185,7 @@ static inline void vmalloc_fault(struct pt_regs *regs, int code, unsigned long a * ordering constraint, not a cache flush; it is * necessary even after writing invalid entries. */ +flush_tlb: local_flush_tlb_page(addr); } From f558c5b6c2b75feda8ea9d617301241812f5802b Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Wed, 22 Mar 2023 09:30:31 +0800 Subject: [PATCH 019/169] riscv: andes: fix ex_table mismatch It has been changed exception table as relative after this commit bb1f85d6046f0db757ac52ed60a5eba5df394819 riscv: switch to relative exception tables Meanwhile struct exception_table_entry has been reconstruct. Hence driver_access.S shall be reused __ex_table sections in asm-extable.h , otherwise the ex_table searching will be out of control and crush the system. Signed-off-by: Rick Chen --- arch/riscv/lib/driver_access.S | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/arch/riscv/lib/driver_access.S b/arch/riscv/lib/driver_access.S index 6fffafd7c79d51..12d3a5eff05fd9 100644 --- a/arch/riscv/lib/driver_access.S +++ b/arch/riscv/lib/driver_access.S @@ -1,44 +1,22 @@ #include #include #include +#include - .altmacro .macro fixup op reg addr lbl - LOCAL _epc -_epc: +100: \op \reg, \addr - .section __ex_table,"a" - .balign RISCV_SZPTR - RISCV_PTR _epc, \lbl - .previous + _asm_extable 100b, \lbl .endm -/* - a0: driver register address - a1: correct value - a2: shift bits - if register value is not equal a1 - return 0 -*/ ENTRY(readl_fixup) - fixup lw t0, (a0), 2f - fence i, r + fence i,r srl t0, t0, a2 - bne t0, a1, error + bne t0, a1, 2f li a0, 1 ret - -error: - li a0, 0 - ret - -ENDPROC(readl_fixup) - - .section .fixup,"ax" - .balign 4 - /* Fixup code for readl_fixup */ 2: li a0, 0 ret - .previous +ENDPROC(readl_fixup) From 4e4da59e14e15c346a5811b9e37028a1b7b4f495 Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Tue, 11 Apr 2023 11:23:45 +0800 Subject: [PATCH 020/169] soc: andes: cache: Remove unused l2c functions Signed-off-by: Rick Chen --- drivers/soc/andes/cache.c | 155 -------------------------------------- include/soc/andes/proc.h | 4 - 2 files changed, 159 deletions(-) diff --git a/drivers/soc/andes/cache.c b/drivers/soc/andes/cache.c index 5c26e9bc0be042..2ed06292ba1606 100644 --- a/drivers/soc/andes/cache.c +++ b/drivers/soc/andes/cache.c @@ -352,161 +352,6 @@ void sbi_andes_cpu_dcache_disable(void *info) local_irq_restore(flags); } -/* L2 Cache */ -uint32_t cpu_l2c_ctl_status(void) -{ - return readl((void *)(l2c_base + L2C_REG_CTL_OFFSET)); -} - -void cpu_l2c_disable(void) -{ -#ifdef CONFIG_SMP - int mhartid = get_cpu(); -#else - int mhartid = 0; -#endif - unsigned int val; - unsigned long flags; - - /* No l2 cache */ - if (!l2c_base) - return; - - /* l2 cache has disabled */ - if (!(cpu_l2c_ctl_status() & L2_CACHE_CTL_mskCEN)) - return; - - local_irq_save(flags); - - /* Disable L2 cache */ - val = readl((void *)(l2c_base + L2C_REG_CTL_OFFSET)); - val &= (~L2_CACHE_CTL_mskCEN); - - writel(val, (void *)(l2c_base + L2C_REG_CTL_OFFSET)); - while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid)) != - CCTL_L2_STATUS_IDLE) - ; - - /* L2 write-back and invalidate all */ - writel(CCTL_L2_WBINVAL_ALL, - (void *)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid))); - - while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_CN_MASK(mhartid)) != - CCTL_L2_STATUS_IDLE) - ; - - local_irq_restore(flags); -#ifdef CONFIG_SMP - put_cpu(); -#endif -} - -#ifndef CONFIG_SMP -void cpu_l2c_inval_range(unsigned long pa, unsigned long size) -{ - unsigned long line_size = get_cache_line_size(); - unsigned long start = pa, end = pa + size; - unsigned long align_start, align_end; - - align_start = start & ~(line_size - 1); - align_end = (end + line_size - 1) & ~(line_size - 1); - - while (align_end > align_start) { - writel(align_start, (void *)(l2c_base + L2C_REG_C0_ACC_OFFSET)); - - writel(CCTL_L2_PA_INVAL, - (void *)(l2c_base + L2C_REG_C0_CMD_OFFSET)); - - while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_C0_MASK) != - CCTL_L2_STATUS_IDLE) - ; - - align_start += line_size; - } -} -EXPORT_SYMBOL(cpu_l2c_inval_range); - -void cpu_l2c_wb_range(unsigned long pa, unsigned long size) -{ - unsigned long line_size = get_cache_line_size(); - unsigned long start = pa, end = pa + size; - unsigned long align_start, align_end; - - align_start = start & ~(line_size - 1); - align_end = (end + line_size - 1) & ~(line_size - 1); - - while (align_end > align_start) { - writel(align_start, (void *)(l2c_base + L2C_REG_C0_ACC_OFFSET)); - - writel(CCTL_L2_PA_WB, - (void *)(l2c_base + L2C_REG_C0_CMD_OFFSET)); - - while ((cpu_l2c_get_cctl_status() & CCTL_L2_STATUS_C0_MASK) != - CCTL_L2_STATUS_IDLE) - ; - - align_start += line_size; - } -} -EXPORT_SYMBOL(cpu_l2c_wb_range); -#else /* !CONFIG_SMP */ -void cpu_l2c_inval_range(unsigned long pa, unsigned long size) -{ - int mhartid = get_cpu(); - unsigned long line_size = get_cache_line_size(); - unsigned long start = pa, end = pa + size; - unsigned long align_start, align_end; - - align_start = start & ~(line_size - 1); - align_end = (end + line_size - 1) & ~(line_size - 1); - - while (align_end > align_start) { - writel(align_start, - (void *)(l2c_base + L2C_REG_CN_ACC_OFFSET(mhartid))); - - writel(CCTL_L2_PA_INVAL, - (void *)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid))); - - while ((cpu_l2c_get_cctl_status() & - CCTL_L2_STATUS_CN_MASK(mhartid)) != - CCTL_L2_STATUS_IDLE) - ; - - align_start += line_size; - } - put_cpu(); -} -EXPORT_SYMBOL(cpu_l2c_inval_range); - -void cpu_l2c_wb_range(unsigned long pa, unsigned long size) -{ - int mhartid = get_cpu(); - unsigned long line_size = get_cache_line_size(); - unsigned long start = pa, end = pa + size; - unsigned long align_start, align_end; - - align_start = start & ~(line_size - 1); - align_end = (end + line_size - 1) & ~(line_size - 1); - - while (align_end > align_start) { - writel(align_start, - (void *)(l2c_base + L2C_REG_CN_ACC_OFFSET(mhartid))); - - writel(CCTL_L2_PA_WB, - (void *)(l2c_base + L2C_REG_CN_CMD_OFFSET(mhartid))); - - while ((cpu_l2c_get_cctl_status() & - CCTL_L2_STATUS_CN_MASK(mhartid)) != - CCTL_L2_STATUS_IDLE) - ; - - align_start += line_size; - } - put_cpu(); -} -EXPORT_SYMBOL(cpu_l2c_wb_range); -#endif /* CONFIG_SMP */ - int __init l2c_init(void) { struct device_node *node; diff --git a/include/soc/andes/proc.h b/include/soc/andes/proc.h index 9a96c26e6192cd..6c0a2db85dc1e4 100644 --- a/include/soc/andes/proc.h +++ b/include/soc/andes/proc.h @@ -13,8 +13,6 @@ void sbi_andes_cpu_icache_enable(void *info); void sbi_andes_cpu_icache_disable(void *info); void sbi_andes_cpu_dcache_enable(void *info); void sbi_andes_cpu_dcache_disable(void *info); -uint32_t cpu_l2c_ctl_status(void); -void cpu_l2c_disable(void); long sbi_andes_get_non_blocking_status(void); long sbi_andes_get_write_around_status(void); @@ -33,8 +31,6 @@ void sbi_andes_set_mmisc_ctl(unsigned long input); void cpu_dma_inval_range(void *info); void cpu_dma_wb_range(void *info); -void cpu_l2c_inval_range(unsigned long pa, unsigned long size); -void cpu_l2c_wb_range(unsigned long pa, unsigned long size); /* * struct andesv5_cache_info From 1ef886f7e026d7cba2999bcc94a679709f30ef7e Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Thu, 27 Apr 2023 13:54:49 +0800 Subject: [PATCH 021/169] soc: andes: cache: remove andes userspace cache and M-mode control functions This commit removes function declarations in include/soc/andes/proc.h that are only used in RISCV-Linux-5.4 arch/riscv/andesv5/cctl.c and arch/riscv/andesv5/sbi_proc_module.c. It also moves include/soc/andes/proc.h cache-related functions to their respective include files and removes the include/soc/andes/proc.h. For more details, see: [1] https://gitea.andestech.com/RD-SW/linux/issues/10 Signed-off-by: Charles Ci-Jyun Wu --- drivers/soc/andes/cache.c | 180 +++----------------------------------- include/soc/andes/dma.h | 23 ++++- include/soc/andes/proc.h | 53 ----------- 3 files changed, 33 insertions(+), 223 deletions(-) delete mode 100644 include/soc/andes/proc.h diff --git a/drivers/soc/andes/cache.c b/drivers/soc/andes/cache.c index 2ed06292ba1606..7d7772e3ecf30e 100644 --- a/drivers/soc/andes/cache.c +++ b/drivers/soc/andes/cache.c @@ -8,12 +8,12 @@ #include #include #include +#include #include #include -#include -#include #include #include +#include #define MAX_CACHE_LINE_SIZE 256 #define EVSEL_MASK 0xff @@ -21,10 +21,10 @@ #define SEL_OFF(id) (8 * (id % 8)) void __iomem *l2c_base; -/* default to offset of V0 memory map */ -u32 L2C_REG_PER_CORE_OFFSET = 0x10; -u32 CCTL_L2_STATUS_PER_CORE_OFFSET = 0x4; -u32 L2C_REG_STATUS_OFFSET = 0; + +u32 L2C_REG_PER_CORE_OFFSET; +u32 CCTL_L2_STATUS_PER_CORE_OFFSET; +u32 L2C_REG_STATUS_OFFSET; DEFINE_PER_CPU(struct andesv5_cache_info, cpu_cache_info) = { .init_done = 0, @@ -189,169 +189,6 @@ void cpu_dma_wb_range(void *info) } EXPORT_SYMBOL(cpu_dma_wb_range); -/* non-blocking load store */ -long sbi_andes_get_non_blocking_status(void) -{ - struct sbiret ret; - - ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MMISC_CTL_STATUS, 0, 0, - 0, 0, 0, 0); - return ret.value; -} -void sbi_andes_set_mcache_ctl(unsigned long input) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_MCACHE_CTL, input, 0, 0, 0, - 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_set_mmisc_ctl(unsigned long input) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_NON_BLOCKING_LOAD_STORE, input, - 0, 0, 0, 0, 0); - local_irq_restore(flags); -} - -/* write around */ -long sbi_andes_get_write_around_status(void) -{ - struct sbiret ret; - - ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MCACHE_CTL_STATUS, 0, - 0, 0, 0, 0, 0); - return ret.value; -} - -void sbi_andes_enable_non_blocking_load_store(void) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_WRITE_AROUND, 1, 0, 0, 0, 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_disable_non_blocking_load_store(void) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_WRITE_AROUND, 0, 0, 0, 0, 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_enable_write_around(void) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_MCACHE_CTL, 1, 0, 0, - 0, 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_disable_write_around(void) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_MMISC_CTL, 0, 0, 0, 0, 0, 0); - local_irq_restore(flags); -} - -/* L1 Cache Prefetch */ -void sbi_andes_enable_l1i_cache(void) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_L1CACHE_I_PREFETCH, 1, 0, 0, 0, - 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_disable_l1i_cache(void) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_L1CACHE_I_PREFETCH, 0, 0, 0, 0, - 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_enable_l1d_cache(void) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_L1CACHE_D_PREFETCH, 1, 0, 0, 0, - 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_disable_l1d_cache(void) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_L1CACHE_D_PREFETCH, 0, 0, 0, 0, - 0, 0); - local_irq_restore(flags); -} - -/* L1 Cache */ -long sbi_andes_cpu_l1c_status(void) -{ - struct sbiret ret; - - ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_GET_MCACHE_CTL_STATUS, 0, - 0, 0, 0, 0, 0); - return ret.value; -} - -void sbi_andes_cpu_icache_enable(void *info) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_ICACHE_OP, 1, 0, 0, 0, 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_cpu_icache_disable(void *info) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_ICACHE_OP, 0, 0, 0, 0, 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_cpu_dcache_enable(void *info) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_DCACHE_OP, 1, 0, 0, 0, 0, 0); - local_irq_restore(flags); -} - -void sbi_andes_cpu_dcache_disable(void *info) -{ - unsigned long flags; - - local_irq_save(flags); - sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_DCACHE_OP, 0, 0, 0, 0, 0, 0); - local_irq_restore(flags); -} - int __init l2c_init(void) { struct device_node *node; @@ -363,6 +200,11 @@ int __init l2c_init(void) if (l2c_base) l2c_cfg = *(u32 *)l2c_base; + /* default to offset of V0 memory map */ + L2C_REG_PER_CORE_OFFSET = 0x10; + CCTL_L2_STATUS_PER_CORE_OFFSET = 0x4; + L2C_REG_STATUS_OFFSET = 0; + if (l2c_cfg & V5_L2C_CFG_MAP_MASK) { L2C_REG_PER_CORE_OFFSET = 0x1000; CCTL_L2_STATUS_PER_CORE_OFFSET = 0; diff --git a/include/soc/andes/dma.h b/include/soc/andes/dma.h index 0e803451a3b8fd..176b426c2aa1b9 100644 --- a/include/soc/andes/dma.h +++ b/include/soc/andes/dma.h @@ -14,7 +14,28 @@ #include #include #include -#include + +/* + * struct andesv5_cache_info + * The member of this struct is dupilcated to some content of struct cacheinfo + * to reduce the latence of searching dcache inforamtion in andesv5/cache.c. + * At current only dcache-line-size is needed. when the content of + * andesv5_cache_info has been initilized by function fill_cpu_cache_info(), + * member init_done is set as true + */ +struct andesv5_cache_info { + bool init_done; + int dcache_line_size; +}; + +struct range_info { + unsigned long start; + unsigned long end; + struct page *page; +}; + +void cpu_dma_inval_range(void *info); +void cpu_dma_wb_range(void *info); static inline void dma_flush_page(struct page *page, size_t size) { diff --git a/include/soc/andes/proc.h b/include/soc/andes/proc.h deleted file mode 100644 index 6c0a2db85dc1e4..00000000000000 --- a/include/soc/andes/proc.h +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2023 Andes Technology Corporation. - */ -#ifndef __SOC_ANDES_PROC_H -#define __SOC_ANDES_PROC_H - -#include -#include - -long sbi_andes_cpu_l1c_status(void); -void sbi_andes_cpu_icache_enable(void *info); -void sbi_andes_cpu_icache_disable(void *info); -void sbi_andes_cpu_dcache_enable(void *info); -void sbi_andes_cpu_dcache_disable(void *info); - -long sbi_andes_get_non_blocking_status(void); -long sbi_andes_get_write_around_status(void); -void sbi_andes_enable_non_blocking_load_store(void); -void sbi_andes_disable_non_blocking_load_store(void); -void sbi_andes_enable_write_around(void); -void sbi_andes_disable_write_around(void); - -void sbi_andes_enable_l1i_cache(void); -void sbi_andes_disable_l1i_cache(void); -void sbi_andes_enable_l1d_cache(void); -void sbi_andes_disable_l1d_cache(void); - -void sbi_andes_set_mcache_ctl(unsigned long input); -void sbi_andes_set_mmisc_ctl(unsigned long input); - -void cpu_dma_inval_range(void *info); -void cpu_dma_wb_range(void *info); - -/* - * struct andesv5_cache_info - * The member of this struct is dupilcated to some content of struct cacheinfo - * to reduce the latence of searching dcache inforamtion in andesv5/cache.c. - * At current only dcache-line-size is needed. when the content of - * andesv5_cache_info has been initilized by function fill_cpu_cache_info(), - * member init_done is set as true - */ -struct andesv5_cache_info { - bool init_done; - int dcache_line_size; -}; - -struct range_info { - unsigned long start; - unsigned long end; - struct page *page; -}; -#endif /* !__SOC_ANDES_PROC_H */ From 83514c6a58d4c1b64e285bb3f2430478f491f377 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Thu, 13 Apr 2023 16:00:24 +0800 Subject: [PATCH 022/169] riscv: andes: defconfig: set RISCV SBI earlycon config to enable in andes_defconfig For more details, see: https://gitea.andestech.com/RD-SW/linux/issues/3 Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/configs/andes_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/configs/andes_defconfig b/arch/riscv/configs/andes_defconfig index 7a53b743ecc1a9..6dba44942ad30d 100644 --- a/arch/riscv/configs/andes_defconfig +++ b/arch/riscv/configs/andes_defconfig @@ -27,6 +27,7 @@ CONFIG_EMBEDDED=y CONFIG_PROFILING=y CONFIG_SOC_VIRT=y # CONFIG_RISCV_ISA_ZICBOM is not set +CONFIG_RISCV_SBI_V01=y CONFIG_PM=y CONFIG_CPU_IDLE=y CONFIG_JUMP_LABEL=y @@ -94,6 +95,7 @@ CONFIG_INPUT_MOUSEDEV=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y CONFIG_SERIAL_OF_PLATFORM=y +CONFIG_SERIAL_EARLYCON_RISCV_SBI=y CONFIG_VIRTIO_CONSOLE=y CONFIG_HW_RANDOM=y CONFIG_HW_RANDOM_VIRTIO=y From f1e04e60b1e5cd4a60665cc78be686577e2980c9 Mon Sep 17 00:00:00 2001 From: Alexandre Ghiti Date: Mon, 24 Apr 2023 11:23:13 +0200 Subject: [PATCH 023/169] riscv: Allow to downgrade paging mode from the command line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 2 early command line parameters that allow to downgrade satp mode (using the same naming as x86): - "no5lvl": use a 4-level page table (down from sv57 to sv48) - "no4lvl": use a 3-level page table (down from sv57/sv48 to sv39) Note that going through the device tree to get the kernel command line works with ACPI too since the efi stub creates a device tree anyway with the command line. In KASAN kernels, we can't use the libfdt that early in the boot process since we are not ready to execute instrumented functions. So instead of using the "generic" libfdt, we compile our own versions of those functions that are not instrumented and that are prefixed so that they do not conflict with the generic ones. We also need the non-instrumented versions of the string functions and the prefixed versions of memcpy/memmove. This is largely inspired by commit aacd149b6238 ("arm64: head: avoid relocating the kernel twice for KASLR") from which I removed compilation flags that were not relevant to RISC-V at the moment (LTO, SCS). Also note that we have to link with -z norelro to avoid ld.lld to throw a warning with the new .got sections, like in commit 311bea3cb9ee ("arm64: link with -z norelro for LLD or aarch64-elf"). Signed-off-by: Alexandre Ghiti Tested-by: Björn Töpel Reviewed-by: Björn Töpel Link: https://lore.kernel.org/r/20230424092313.178699-2-alexghiti@rivosinc.com Signed-off-by: Palmer Dabbelt --- .../admin-guide/kernel-parameters.txt | 5 +- arch/riscv/Makefile | 6 +- arch/riscv/kernel/Makefile | 2 + arch/riscv/kernel/pi/Makefile | 39 ++++++++++++ arch/riscv/kernel/pi/cmdline_early.c | 62 +++++++++++++++++++ arch/riscv/kernel/vmlinux.lds.S | 29 +++++++++ arch/riscv/lib/memcpy.S | 2 + arch/riscv/lib/memmove.S | 2 + arch/riscv/mm/init.c | 36 +++++++++-- 9 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 arch/riscv/kernel/pi/Makefile create mode 100644 arch/riscv/kernel/pi/cmdline_early.c diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 4ad60e127e0486..7f7599f1d7c42c 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3587,7 +3587,10 @@ emulation library even if a 387 maths coprocessor is present. - no5lvl [X86-64] Disable 5-level paging mode. Forces + no4lvl [RISCV] Disable 4-level and 5-level paging modes. Forces + kernel to use 3-level paging instead. + + no5lvl [X86-64,RISCV] Disable 5-level paging mode. Forces kernel to use 4-level paging instead. nofsgsbase [X86] Disables FSGSBASE instructions. diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 655085e64fe732..0e2704e10334a2 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -7,7 +7,11 @@ # OBJCOPYFLAGS := -O binary -LDFLAGS_vmlinux := +LDFLAGS_vmlinux := -z norelro +ifeq ($(CONFIG_RELOCATABLE),y) + LDFLAGS_vmlinux += -shared -Bsymbolic -z notext --emit-relocs + KBUILD_CFLAGS += -fPIE +endif ifeq ($(CONFIG_DYNAMIC_FTRACE),y) LDFLAGS_vmlinux := --no-relax KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index ab333cb792fd9c..bbc65d9c466fd4 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -89,3 +89,5 @@ obj-$(CONFIG_EFI) += efi.o obj-$(CONFIG_COMPAT) += compat_syscall_table.o obj-$(CONFIG_COMPAT) += compat_signal.o obj-$(CONFIG_COMPAT) += compat_vdso/ + +obj-$(CONFIG_64BIT) += pi/ diff --git a/arch/riscv/kernel/pi/Makefile b/arch/riscv/kernel/pi/Makefile new file mode 100644 index 00000000000000..5d7cb991f2b86c --- /dev/null +++ b/arch/riscv/kernel/pi/Makefile @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0 +# This file was copied from arm64/kernel/pi/Makefile. + +KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) -fpie \ + -Os -DDISABLE_BRANCH_PROFILING $(DISABLE_STACKLEAK_PLUGIN) \ + $(call cc-option,-mbranch-protection=none) \ + -I$(srctree)/scripts/dtc/libfdt -fno-stack-protector \ + -D__DISABLE_EXPORTS -ffreestanding \ + -fno-asynchronous-unwind-tables -fno-unwind-tables \ + $(call cc-option,-fno-addrsig) + +KBUILD_CFLAGS += -mcmodel=medany + +CFLAGS_cmdline_early.o += -D__NO_FORTIFY +CFLAGS_lib-fdt_ro.o += -D__NO_FORTIFY + +GCOV_PROFILE := n +KASAN_SANITIZE := n +KCSAN_SANITIZE := n +UBSAN_SANITIZE := n +KCOV_INSTRUMENT := n + +$(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ \ + --remove-section=.note.gnu.property \ + --prefix-alloc-sections=.init +$(obj)/%.pi.o: $(obj)/%.o FORCE + $(call if_changed,objcopy) + +$(obj)/lib-%.o: $(srctree)/lib/%.c FORCE + $(call if_changed_rule,cc_o_c) + +$(obj)/string.o: $(srctree)/lib/string.c FORCE + $(call if_changed_rule,cc_o_c) + +$(obj)/ctype.o: $(srctree)/lib/ctype.c FORCE + $(call if_changed_rule,cc_o_c) + +obj-y := cmdline_early.pi.o string.pi.o ctype.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o +extra-y := $(patsubst %.pi.o,%.o,$(obj-y)) diff --git a/arch/riscv/kernel/pi/cmdline_early.c b/arch/riscv/kernel/pi/cmdline_early.c new file mode 100644 index 00000000000000..05652d13c74623 --- /dev/null +++ b/arch/riscv/kernel/pi/cmdline_early.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include + +static char early_cmdline[COMMAND_LINE_SIZE]; + +/* + * Declare the functions that are exported (but prefixed) here so that LLVM + * does not complain it lacks the 'static' keyword (which, if added, makes + * LLVM complain because the function is actually unused in this file). + */ +u64 set_satp_mode_from_cmdline(uintptr_t dtb_pa); + +static char *get_early_cmdline(uintptr_t dtb_pa) +{ + const char *fdt_cmdline = NULL; + unsigned int fdt_cmdline_size = 0; + int chosen_node; + + if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) { + chosen_node = fdt_path_offset((void *)dtb_pa, "/chosen"); + if (chosen_node >= 0) { + fdt_cmdline = fdt_getprop((void *)dtb_pa, chosen_node, + "bootargs", NULL); + if (fdt_cmdline) { + fdt_cmdline_size = strlen(fdt_cmdline); + strscpy(early_cmdline, fdt_cmdline, + COMMAND_LINE_SIZE); + } + } + } + + if (IS_ENABLED(CONFIG_CMDLINE_EXTEND) || + IS_ENABLED(CONFIG_CMDLINE_FORCE) || + fdt_cmdline_size == 0 /* CONFIG_CMDLINE_FALLBACK */) { + strncat(early_cmdline, CONFIG_CMDLINE, + COMMAND_LINE_SIZE - fdt_cmdline_size); + } + + return early_cmdline; +} + +static u64 match_noXlvl(char *cmdline) +{ + if (strstr(cmdline, "no4lvl")) + return SATP_MODE_48; + else if (strstr(cmdline, "no5lvl")) + return SATP_MODE_57; + + return 0; +} + +u64 set_satp_mode_from_cmdline(uintptr_t dtb_pa) +{ + char *cmdline = get_early_cmdline(dtb_pa); + + return match_noXlvl(cmdline); +} diff --git a/arch/riscv/kernel/vmlinux.lds.S b/arch/riscv/kernel/vmlinux.lds.S index d478e063b8785d..60a30dfd9a25f5 100644 --- a/arch/riscv/kernel/vmlinux.lds.S +++ b/arch/riscv/kernel/vmlinux.lds.S @@ -88,6 +88,17 @@ SECTIONS /* Start of init data section */ __init_data_begin = .; INIT_DATA_SECTION(16) + + /* Those sections result from the compilation of kernel/pi/string.c */ + .init.pidata : { + *(.init.srodata.cst8*) + *(.init__bug_table*) + *(.init.sdata*) + } + + .init.bss : { + *(.init.bss) /* from the EFI stub */ + } .exit.data : { EXIT_DATA @@ -124,6 +135,24 @@ SECTIONS *(.sdata*) } + .rela.dyn : ALIGN(8) { + __rela_dyn_start = .; + *(.rela .rela*) + __rela_dyn_end = .; + } + + .got : { *(.got*) } + +#ifdef CONFIG_RELOCATABLE + .data.rel : { *(.data.rel*) } + .plt : { *(.plt) } + .dynamic : { *(.dynamic) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } +#endif + #ifdef CONFIG_EFI .pecoff_edata_padding : { BYTE(0); . = ALIGN(PECOFF_FILE_ALIGNMENT); } __pecoff_data_raw_size = ABSOLUTE(. - __pecoff_text_end); diff --git a/arch/riscv/lib/memcpy.S b/arch/riscv/lib/memcpy.S index 51ab716253fa3c..1a40d01a95439e 100644 --- a/arch/riscv/lib/memcpy.S +++ b/arch/riscv/lib/memcpy.S @@ -106,3 +106,5 @@ WEAK(memcpy) 6: ret END(__memcpy) +SYM_FUNC_ALIAS(__pi_memcpy, __memcpy) +SYM_FUNC_ALIAS(__pi___memcpy, __memcpy) diff --git a/arch/riscv/lib/memmove.S b/arch/riscv/lib/memmove.S index e0609e1f0864de..838ff2022fe32d 100644 --- a/arch/riscv/lib/memmove.S +++ b/arch/riscv/lib/memmove.S @@ -314,3 +314,5 @@ return_from_memmove: SYM_FUNC_END(memmove) SYM_FUNC_END(__memmove) +SYM_FUNC_ALIAS(__pi_memmove, __memmove) +SYM_FUNC_ALIAS(__pi___memmove, __memmove) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 7c4852af9e3f13..2708580c59014a 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -713,6 +713,8 @@ static __init pgprot_t pgprot_from_va(uintptr_t va) #endif /* CONFIG_STRICT_KERNEL_RWX */ #if defined(CONFIG_64BIT) && !defined(CONFIG_XIP_KERNEL) +u64 __pi_set_satp_mode_from_cmdline(uintptr_t dtb_pa); + static void __init disable_pgtable_l5(void) { pgtable_l5_enabled = false; @@ -727,17 +729,39 @@ static void __init disable_pgtable_l4(void) satp_mode = SATP_MODE_39; } +static int __init print_no4lvl(char *p) +{ + pr_info("Disabled 4-level and 5-level paging"); + return 0; +} +early_param("no4lvl", print_no4lvl); + +static int __init print_no5lvl(char *p) +{ + pr_info("Disabled 5-level paging"); + return 0; +} +early_param("no5lvl", print_no5lvl); + /* * There is a simple way to determine if 4-level is supported by the * underlying hardware: establish 1:1 mapping in 4-level page table mode * then read SATP to see if the configuration was taken into account * meaning sv48 is supported. */ -static __init void set_satp_mode(void) +static __init void set_satp_mode(uintptr_t dtb_pa) { u64 identity_satp, hw_satp; uintptr_t set_satp_mode_pmd = ((unsigned long)set_satp_mode) & PMD_MASK; - bool check_l4 = false; + u64 satp_mode_cmdline = __pi_set_satp_mode_from_cmdline(dtb_pa); + + if (satp_mode_cmdline == SATP_MODE_57) { + disable_pgtable_l5(); + } else if (satp_mode_cmdline == SATP_MODE_48) { + disable_pgtable_l5(); + disable_pgtable_l4(); + return; + } create_p4d_mapping(early_p4d, set_satp_mode_pmd, (uintptr_t)early_pud, @@ -756,7 +780,8 @@ static __init void set_satp_mode(void) retry: create_pgd_mapping(early_pg_dir, set_satp_mode_pmd, - check_l4 ? (uintptr_t)early_pud : (uintptr_t)early_p4d, + pgtable_l5_enabled ? + (uintptr_t)early_p4d : (uintptr_t)early_pud, PGDIR_SIZE, PAGE_TABLE); identity_satp = PFN_DOWN((uintptr_t)&early_pg_dir) | satp_mode; @@ -767,9 +792,8 @@ static __init void set_satp_mode(void) local_flush_tlb_all(); if (hw_satp != identity_satp) { - if (!check_l4) { + if (pgtable_l5_enabled) { disable_pgtable_l5(); - check_l4 = true; memset(early_pg_dir, 0, PAGE_SIZE); goto retry; } @@ -955,7 +979,7 @@ asmlinkage void __init setup_vm(uintptr_t dtb_pa) #endif #if defined(CONFIG_64BIT) && !defined(CONFIG_XIP_KERNEL) - set_satp_mode(); + set_satp_mode(dtb_pa); #endif kernel_map.va_pa_offset = PAGE_OFFSET - kernel_map.phys_addr; From bafbf134b36ee256fe703f528dbfd13d5fc3ef67 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Tue, 15 Nov 2022 10:51:33 +0000 Subject: [PATCH 024/169] riscv: Kconfig: Enable cpufreq kconfig menu Enable cpufreq kconfig menu for RISC-V. Signed-off-by: Lad Prabhakar Reviewed-by: Conor Dooley Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20221115105135.1180490-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 146055ec6bfa56..b49724166a8bb1 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -719,6 +719,8 @@ menu "CPU Power Management" source "drivers/cpuidle/Kconfig" +source "drivers/cpufreq/Kconfig" + endmenu # "CPU Power Management" source "arch/riscv/kvm/Kconfig" From a1ae71043d175289f49848c2236a14844d33cf9f Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Wed, 3 May 2023 14:45:46 +0800 Subject: [PATCH 025/169] riscv: andes: defconfig: enable CONFIG_CPU_FREQ for andes_defconfig Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/configs/andes_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/andes_defconfig b/arch/riscv/configs/andes_defconfig index 6dba44942ad30d..7a754e3a282c3c 100644 --- a/arch/riscv/configs/andes_defconfig +++ b/arch/riscv/configs/andes_defconfig @@ -30,6 +30,7 @@ CONFIG_SOC_VIRT=y CONFIG_RISCV_SBI_V01=y CONFIG_PM=y CONFIG_CPU_IDLE=y +CONFIG_CPU_FREQ=y CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y From 92fa49827966362c0db650a0559e604ecc470b32 Mon Sep 17 00:00:00 2001 From: Dylan Jhong Date: Fri, 28 Apr 2023 16:34:51 +0800 Subject: [PATCH 026/169] soc: andes: ppma: Support IPI in andes_free_ppma() Andes PMA is a per hart CSR. So when freeing PMA, it is necessary to use the IPI mechanism to let each hart free its own PMA settings through OpenSBI. Signed-off-by: Dylan Jhong --- drivers/soc/andes/andes_sbi.c | 4 ++-- drivers/soc/andes/ppma.c | 23 ++++++++++++++++++++++- include/soc/andes/sbi.h | 2 +- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/soc/andes/andes_sbi.c b/drivers/soc/andes/andes_sbi.c index ca0744623aa1c5..5492fa88bf0d3c 100644 --- a/drivers/soc/andes/andes_sbi.c +++ b/drivers/soc/andes/andes_sbi.c @@ -15,10 +15,10 @@ void sbi_andes_set_ppma(void *arg) phys_addr, va_addr, size, 0, 0, 0); } -void sbi_andes_free_ppma(unsigned long addr) +void sbi_andes_free_ppma(void *addr) { sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_FREE_PPMA, - addr, 0, 0, 0, 0, 0); + (unsigned long)addr, 0, 0, 0, 0, 0); } long sbi_andes_probe_ppma(void) diff --git a/drivers/soc/andes/ppma.c b/drivers/soc/andes/ppma.c index aac3f0f7253dd2..7987c020f0dd41 100644 --- a/drivers/soc/andes/ppma.c +++ b/drivers/soc/andes/ppma.c @@ -69,6 +69,27 @@ EXPORT_SYMBOL(andes_set_ppma); void andes_free_ppma(void *addr) { - sbi_andes_free_ppma((unsigned long)addr); + int cpu_num = num_online_cpus(); + int id = smp_processor_id(); + int i, err; + + /* + * Send IPI + * FIXME: we need online CPU mask, not the number + */ + for (i = 0; i < cpu_num; i++) { + if (i == id) + continue; + + err = smp_call_function_single(i, sbi_andes_free_ppma, + (void *)addr, true); + if (err) { + pr_err("Core %d fails to free ppma\n" + "Error Code: %d\n", i, err); + } + } + + sbi_andes_free_ppma(addr); + } EXPORT_SYMBOL(andes_free_ppma); diff --git a/include/soc/andes/sbi.h b/include/soc/andes/sbi.h index d71232eb8774eb..562d1661c423cb 100644 --- a/include/soc/andes/sbi.h +++ b/include/soc/andes/sbi.h @@ -53,7 +53,7 @@ enum sbi_ext_andes_fid { /* Programmable physical memory attributes (PPMA) */ void sbi_andes_set_ppma(void *arg); -void sbi_andes_free_ppma(unsigned long addr); +void sbi_andes_free_ppma(void *addr); long sbi_andes_probe_ppma(void); #endif /* !__SOC_ANDES_SBI_H */ From 168d9e0f084b5902fabb2f19f2525b3aca04d197 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Mon, 17 Apr 2023 15:19:13 +0800 Subject: [PATCH 027/169] soc: andes: injection: Andes support injection feature Reformed from the following patches on RISCV-Linux-5.4: - (87831dd706c4) Fix: inject feature - (62754f10ae3e) Fix injection feature - (42d499327dcc) Fix: inject print message - (6bb3adf3d930) hijack rename to inject Signed-off-by: Charles Ci-Jyun Wu --- drivers/soc/andes/Makefile | 1 + drivers/soc/andes/inject_init.c | 62 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 drivers/soc/andes/inject_init.c diff --git a/drivers/soc/andes/Makefile b/drivers/soc/andes/Makefile index bcdbcfd42dbfb5..d45b9594db09f3 100644 --- a/drivers/soc/andes/Makefile +++ b/drivers/soc/andes/Makefile @@ -2,6 +2,7 @@ # Copyright (C) 2023 Andes Technology Corporation. obj-y += andes_sbi.o +obj-y += inject_init.o obj-$(CONFIG_ANDES_CACHE) += cache.o obj-$(CONFIG_ANDES_DMA_NONCOHERENT) += dma-noncoherent.o obj-$(CONFIG_ANDES_PPMA) += ppma.o diff --git a/drivers/soc/andes/inject_init.c b/drivers/soc/andes/inject_init.c new file mode 100644 index 00000000000000..60b1beba2a71ec --- /dev/null +++ b/drivers/soc/andes/inject_init.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020 Andes Technology Corporation. + */ + +#include +#include +#include +#include +#include + +#ifdef CONFIG_PROC_FS + +// FIXME: make this FPGA-depedent +#define INJECT_START 0x100000 +#define INJECT_SIZE (0x200000 - INJECT_START) + +static void *inject; + +static int inject_show(struct seq_file *m, void *v) +{ + int i, pos; + unsigned char *p = (unsigned char *)inject; + + if (inject != NULL && p[0] == 0) { + /* do nothing */ + } else if (inject != NULL) { + for (i = 0, pos = 0; i < (INJECT_SIZE / PAGE_SIZE); i++, pos += PAGE_SIZE) + seq_write(m, (void *)&p[pos], PAGE_SIZE); + } else { + seq_printf(m, "#!/bin/sh\necho not found: ioremap failed.\n"); + } + return 0; +} + +static int inject_open(struct inode *inode, struct file *file) +{ + return single_open(file, inject_show, NULL); +} + +static const struct file_operations inject_fops = { + .open = inject_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init inject_init(void) +{ + unsigned char *p; + + inject = (void *)ioremap((phys_addr_t)INJECT_START, INJECT_SIZE); + p = (unsigned char *)inject; + + if (p[0] != '#' && p[0] != 0x79) + p[0] = 0; + + proc_create("inject", 0444, NULL, &inject_fops); + return 0; +} +arch_initcall(inject_init); +#endif /* CONFIG_PROC_FS */ From bc874ac425ed7b69da6f55b4221021af2733b383 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Mon, 17 Apr 2023 16:28:24 +0800 Subject: [PATCH 028/169] soc: andes: injection: upgrade struct file_operations to struct proc_ops Signed-off-by: Charles Ci-Jyun Wu --- drivers/soc/andes/inject_init.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/soc/andes/inject_init.c b/drivers/soc/andes/inject_init.c index 60b1beba2a71ec..43e2c6e38fbf10 100644 --- a/drivers/soc/andes/inject_init.c +++ b/drivers/soc/andes/inject_init.c @@ -38,11 +38,11 @@ static int inject_open(struct inode *inode, struct file *file) return single_open(file, inject_show, NULL); } -static const struct file_operations inject_fops = { - .open = inject_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, +static const struct proc_ops inject_fops = { + .proc_open = inject_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, }; static int __init inject_init(void) From 12f5269e08daf4c610ded99a86566456efd8789d Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Thu, 20 Apr 2023 17:50:46 +0800 Subject: [PATCH 029/169] riscv: andes: dsp: DSP support Reformed from the following patches on RISCV-Linux-5.4: -(15a3b9f259b0) Fix DSP condition during context switch -(43d9b4a32420) DSP support Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig | 6 +++ arch/riscv/include/asm/hwcap.h | 4 ++ arch/riscv/include/asm/processor.h | 3 ++ arch/riscv/include/asm/switch_to.h | 54 ++++++++++++++++++------ arch/riscv/include/uapi/asm/ptrace.h | 6 +++ arch/riscv/include/uapi/asm/sigcontext.h | 3 ++ arch/riscv/kernel/cpufeature.c | 3 ++ arch/riscv/kernel/process.c | 3 ++ arch/riscv/kernel/signal.c | 37 ++++++++++++++++ 9 files changed, 106 insertions(+), 13 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index b49724166a8bb1..edb7c9941422a7 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -470,6 +470,12 @@ config TOOLCHAIN_NEEDS_OLD_ISA_SPEC versions of clang and GCC to be passed to GAS, which has the same result as passing zicsr and zifencei to -march. +config DSP + bool "DSP support" + default y + help + Say N here if you want to disable DSP support. + config FPU bool "FPU support" default y diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h index b2252529007301..9da1eae9057bac 100644 --- a/arch/riscv/include/asm/hwcap.h +++ b/arch/riscv/include/asm/hwcap.h @@ -59,6 +59,7 @@ enum riscv_isa_ext_id { RISCV_ISA_EXT_ZIHINTPAUSE, RISCV_ISA_EXT_SSTC, RISCV_ISA_EXT_SVINVAL, + ANDES_ISA_EXT_DSP, RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX, }; @@ -71,6 +72,7 @@ enum riscv_isa_ext_key { RISCV_ISA_EXT_KEY_FPU, /* For 'F' and 'D' */ RISCV_ISA_EXT_KEY_ZIHINTPAUSE, RISCV_ISA_EXT_KEY_SVINVAL, + ANDES_ISA_EXT_KEY_DSP, RISCV_ISA_EXT_KEY_MAX, }; @@ -94,6 +96,8 @@ static __always_inline int riscv_isa_ext2key(int num) return RISCV_ISA_EXT_KEY_ZIHINTPAUSE; case RISCV_ISA_EXT_SVINVAL: return RISCV_ISA_EXT_KEY_SVINVAL; + case ANDES_ISA_EXT_DSP: + return ANDES_ISA_EXT_KEY_DSP; default: return -EINVAL; } diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h index 94a0590c69710b..a193477fb4e064 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -38,6 +38,9 @@ struct thread_struct { unsigned long sp; /* Kernel mode stack */ unsigned long s[12]; /* s[0]: frame pointer */ struct __riscv_d_ext_state fstate; +#ifdef CONFIG_DSP + struct __riscv_dsp_state dspstate; +#endif unsigned long bad_cause; }; diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h index 11463489fec677..12010f8bd8945f 100644 --- a/arch/riscv/include/asm/switch_to.h +++ b/arch/riscv/include/asm/switch_to.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -46,28 +47,55 @@ static inline void fstate_restore(struct task_struct *task, } } +static __always_inline bool has_fpu(void) +{ + return static_branch_likely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_FPU]); +} +#else +static __always_inline bool has_fpu(void) { return false; } +#define fstate_save(task, regs) do { } while (0) +#define fstate_restore(task, regs) do { } while (0) +#endif /* CONFIG_FPU */ + +#ifdef CONFIG_DSP +static inline void dspstate_save(struct task_struct *task) +{ + task->thread.dspstate.ucode = csr_read(CSR_UCODE); +} + +static inline void dspstate_restore(struct task_struct *task) +{ + csr_write(CSR_UCODE, task->thread.dspstate.ucode); +} + +static __always_inline bool has_dsp(void) +{ + return static_branch_likely(&riscv_isa_ext_keys[ANDES_ISA_EXT_KEY_DSP]); +} +#else +static __always_inline bool has_dsp(void) { return false; } +#define dspstate_save(task) do { } while (0) +#define dspstate_restore(task) do { } while (0) +#endif /* CONFIG_DSP */ + static inline void __switch_to_aux(struct task_struct *prev, struct task_struct *next) { +#ifdef CONFIG_FPU struct pt_regs *regs; - regs = task_pt_regs(prev); if (unlikely(regs->status & SR_SD)) fstate_save(prev, regs); fstate_restore(next, task_pt_regs(next)); +#endif /* CONFIG_FPU */ +#ifdef CONFIG_DSP + if (has_dsp()) { + dspstate_save(prev); + dspstate_restore(next); + } +#endif /* CONFIG_DSP */ } -static __always_inline bool has_fpu(void) -{ - return static_branch_likely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_FPU]); -} -#else -static __always_inline bool has_fpu(void) { return false; } -#define fstate_save(task, regs) do { } while (0) -#define fstate_restore(task, regs) do { } while (0) -#define __switch_to_aux(__prev, __next) do { } while (0) -#endif - extern struct task_struct *__switch_to(struct task_struct *, struct task_struct *); @@ -75,7 +103,7 @@ extern struct task_struct *__switch_to(struct task_struct *, do { \ struct task_struct *__prev = (prev); \ struct task_struct *__next = (next); \ - if (has_fpu()) \ + if (has_fpu() || has_dsp()) \ __switch_to_aux(__prev, __next); \ ((last) = __switch_to(__prev, __next)); \ } while (0) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 882547f6bd5c91..5e42d28845cb5a 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -51,6 +51,12 @@ struct user_regs_struct { unsigned long t6; }; +#ifdef CONFIG_DSP +struct __riscv_dsp_state { + unsigned long ucode; +}; +#endif + struct __riscv_f_ext_state { __u32 f[32]; __u32 fcsr; diff --git a/arch/riscv/include/uapi/asm/sigcontext.h b/arch/riscv/include/uapi/asm/sigcontext.h index 84f2dfcfdbce2e..94addf0067c5fe 100644 --- a/arch/riscv/include/uapi/asm/sigcontext.h +++ b/arch/riscv/include/uapi/asm/sigcontext.h @@ -17,6 +17,9 @@ struct sigcontext { struct user_regs_struct sc_regs; union __riscv_fp_state sc_fpregs; +#ifdef CONFIG_DSP + struct __riscv_dsp_state sc_dspregs; +#endif /* CONFIG_DSP */ }; #endif /* _UAPI_ASM_RISCV_SIGCONTEXT_H */ diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index fd1238df61497d..740d4761a794d8 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -206,6 +206,9 @@ void __init riscv_fill_hwcap(void) SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE); SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC); SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL); + + /* Andes vendor extensions: DSP */ + SET_ISA_EXT_MAP("xdsp", ANDES_ISA_EXT_DSP); } #undef SET_ISA_EXT_MAP } diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index 8955f2432c2d84..edd7a2571e01bd 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -148,6 +148,9 @@ void flush_thread(void) fstate_off(current, task_pt_regs(current)); memset(¤t->thread.fstate, 0, sizeof(current->thread.fstate)); #endif +#ifdef CONFIG_DSP + memset(¤t->thread.dspstate, 0, sizeof(current->thread.dspstate)); +#endif } int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index dee66c9290ccee..5a5aa23f3418a8 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -87,6 +87,39 @@ static long save_fp_state(struct pt_regs *regs, #define restore_fp_state(task, regs) (0) #endif +#ifdef CONFIG_DSP +static long restore_dsp_state(struct pt_regs *regs, + struct __riscv_dsp_state *sc_dspregs) +{ + long err; + + err = __copy_from_user(¤t->thread.dspstate.ucode, + &sc_dspregs->ucode, + sizeof(unsigned long)); + if (unlikely(err)) + return err; + dspstate_restore(current); + return 0; +} + +static long save_dsp_state(struct pt_regs *regs, + struct __riscv_dsp_state *sc_dspregs) +{ + long err; + + dspstate_save(current); + err = __copy_to_user(&sc_dspregs->ucode, + ¤t->thread.dspstate.ucode, + sizeof(unsigned long)); + if (unlikely(err)) + return err; + return 0; +} +#else +#define save_dsp_state(task, regs) (0) +#define restore_dsp_state(task, regs) (0) +#endif + static long restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { @@ -96,6 +129,8 @@ static long restore_sigcontext(struct pt_regs *regs, /* Restore the floating-point state. */ if (has_fpu()) err |= restore_fp_state(regs, &sc->sc_fpregs); + if (has_dsp()) + err |= restore_dsp_state(regs, &sc->sc_dspregs); return err; } @@ -151,6 +186,8 @@ static long setup_sigcontext(struct rt_sigframe __user *frame, /* Save the floating-point state. */ if (has_fpu()) err |= save_fp_state(regs, &sc->sc_fpregs); + if (has_dsp()) + err |= save_dsp_state(regs, &sc->sc_dspregs); return err; } From 6c6f1c2e7a20784ab1de3f2a8708434135fd1e6a Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Tue, 30 Nov 2021 16:22:24 +0800 Subject: [PATCH 030/169] rtc: andes: atcrtc100: Andes support for ATCRTC Reformed from the following patches on RISCV-Linux-5.4: - (d4bc1700f894) Andes support for ATCRTC - (4fe1359dc923) ATCRTC100: Fix atcrtc100.c init fail due to kernel API change. - (ae228236727f) ATCRTC100: Move request_irq() to the end of atc_rtc_probe(). Reformed from the following patches on v5.18.y_ae350-ax45mp: - peter (c95b6e78deea) rtc-atcrtc100:c: update rtc APIs and use ioremap. Signed-off-by: Yu Chien Peter Lin --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-atcrtc100.c | 381 ++++++++++++++++++++++++++++++++++++ 3 files changed, 392 insertions(+) create mode 100644 drivers/rtc/rtc-atcrtc100.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index bb63edb507da4b..7ce0e99dbef359 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1429,6 +1429,16 @@ config RTC_DRV_OMAP This driver can also be built as a module, if so, module will be called rtc-omap. +config RTC_DRV_ATCRTC100 + tristate "AE350 Real Time Clock" + depends on RISCV + help + If you say Y here you will get access to the real time clock + built into your AE350. + + To compile this driver as a module, choose M here. + The module will be called rtc-atcrtc100. + config RTC_DRV_S3C tristate "Samsung S3C series SoC RTC" depends on ARCH_EXYNOS || ARCH_S3C64XX || ARCH_S3C24XX || ARCH_S5PV210 || \ diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index aab22bc6343210..407af3c1310012 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o obj-$(CONFIG_RTC_DRV_ASPEED) += rtc-aspeed.o obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o +obj-$(CONFIG_RTC_DRV_ATCRTC100) += rtc-atcrtc100.o obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o obj-$(CONFIG_RTC_DRV_BD70528) += rtc-bd70528.o obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c new file mode 100644 index 00000000000000..f5ced02f648b2d --- /dev/null +++ b/drivers/rtc/rtc-atcrtc100.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2008-2017 Andes Technology Corporation + * Andes RTC Support + * + * Copyright (C) 2006, 2007, 2008 Paul Mundt + * Copyright (C) 2006 Jamie Lenehan + * Copyright (C) 2008 Angelo Castello + * Copyright (C) 2008 Roy Lee + * + * Based on the old arch/sh/kernel/cpu/rtc.c by: + * + * Copyright (C) 2000 Philipp Rumpf + * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka + * + * 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 +#include +#include + +#define DRV_NAME "atcrtc100" +#define RTC_REG(off) \ + (*(volatile unsigned int *)(rtc->regbase + (off))) +#define RTC_ID RTC_REG(0x00) /* ID and Revision */ +#define ID_OFF 12 +#define ID_MSK (0xfffff << ID_OFF) +#define ATCRTC100ID (0x03011 << ID_OFF) +#define RTC_RSV RTC_REG(0x4) /* Reserve */ + +#define RTC_CNT RTC_REG(0x10) /* Counter */ +#define RTC_ALM RTC_REG(0x14) /* Alarm */ +#define DAY_OFF 17 +#define DAY_MSK (0x7fff) +#define HOR_OFF 12 +#define HOR_MSK (0x1f) +#define MIN_OFF 6 +#define MIN_MSK (0x3f) +#define SEC_OFF 0 +#define SEC_MSK (0x3f) +#define RTC_SECOND \ + ((RTC_CNT >> SEC_OFF) & SEC_MSK) /* RTC sec */ +#define RTC_MINUTE \ + ((RTC_CNT >> MIN_OFF) & MIN_MSK) /* RTC min */ +#define RTC_HOUR \ + ((RTC_CNT >> HOR_OFF) & HOR_MSK) /* RTC hour */ +#define RTC_DAYS \ + ((RTC_CNT >> DAY_OFF) & DAY_MSK) /* RTC day */ +#define RTC_ALM_SECOND \ + ((RTC_ALM >> SEC_OFF) & SEC_MSK) /* RTC alarm sec */ +#define RTC_ALM_MINUTE \ + ((RTC_ALM >> MIN_OFF) & MIN_MSK) /* RTC alarm min */ +#define RTC_ALM_HOUR \ + ((RTC_ALM >> HOR_OFF) & HOR_MSK) /* RTC alarm hour */ +#define RTC_CR RTC_REG(0x18) /* Control */ +#define RTC_EN (0x1UL << 0) +#define ALARM_WAKEUP (0x1UL << 1) +#define ALARM_INT (0x1UL << 2) +#define DAY_INT (0x1UL << 3) +#define HOR_INT (0x1UL << 4) +#define MIN_INT (0x1UL << 5) +#define SEC_INT (0x1UL << 6) +#define HSEC_INT (0x1UL << 7) +#define RTC_STA RTC_REG(0x1c) /* Status */ +#define WD (0x1UL << 16) + +/* CHeck if day is configured as 15 */ +#define CHECK_DAY_15 0 + +struct atc_rtc { + void __iomem *regbase; + struct resource *res; + unsigned int alarm_irq; + unsigned int interrupt_irq; + struct rtc_device *rtc_dev; + spinlock_t lock; /* Protects this structure */ +}; + +struct atc_rtc rtc_platform_data; + +static irqreturn_t rtc_interrupt(int irq, void *dev_id) +{ + struct atc_rtc *rtc = dev_id; + + if (RTC_STA & SEC_INT) { + RTC_STA |= SEC_INT; + rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static irqreturn_t rtc_alarm(int irq, void *dev_id) +{ + struct atc_rtc *rtc = dev_id; + + if (RTC_STA & ALARM_INT) { + RTC_CR &= ~ALARM_INT; + RTC_STA |= ALARM_INT; + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +static int atc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct atc_rtc *rtc = dev_get_drvdata(dev); + + spin_lock_irq(&rtc->lock); + if (enabled) + RTC_CR |= ALARM_INT; + else + RTC_CR &= ~ALARM_INT; + spin_unlock_irq(&rtc->lock); + return 0; +} + +static int atc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct atc_rtc *rtc = dev_get_drvdata(dev); + unsigned long time = RTC_DAYS * 86400 + RTC_HOUR * 3600 + + RTC_MINUTE * 60 + RTC_SECOND; + + rtc_time64_to_tm(time, tm); + if (rtc_valid_tm(tm) < 0) { + dev_err(dev, "invalid date\n"); + rtc_time64_to_tm(0, tm); + } + return 0; +} + +static int atc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct atc_rtc *rtc = dev_get_drvdata(dev); + time64_t time = 0; + u32 cnt = 0; + + time = rtc_tm_to_time64(tm); + cnt |= ((div_s64_rem(time, 86400, (u32 *)&time) & DAY_MSK) << DAY_OFF); + cnt |= ((div_s64_rem(time, 3600, (u32 *)&time) & HOR_MSK) << HOR_OFF); + cnt |= ((div_s64_rem(time, 60, (u32 *)&time) & MIN_MSK) << MIN_OFF); + cnt |= ((time & SEC_MSK) << SEC_OFF); + + spin_lock_irq(&rtc->lock); + RTC_CNT = cnt; + spin_unlock_irq(&rtc->lock); + + /* synchronization progress of RTC register updates */ + while ((RTC_STA & WD) != WD) + continue; + + return 0; +} + +static int atc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct atc_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + + tm->tm_sec = RTC_ALM_SECOND; + tm->tm_min = RTC_ALM_MINUTE; + tm->tm_hour = RTC_ALM_HOUR; + wkalrm->enabled = (RTC_CR & ALARM_INT) ? 1 : 0; + return 0; +} + +static int atc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + u32 alm = 0; + struct atc_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + int err = rtc_valid_tm(tm); + + if (err < 0) { + dev_err(dev, "invalid alarm value\n"); + return err; + } + /* disable alarm interrupt and clear the alarm flag */ + RTC_CR &= ~ALARM_INT; + /* set alarm time */ + alm |= ((tm->tm_sec & SEC_MSK) << SEC_OFF); + alm |= ((tm->tm_min & MIN_MSK) << MIN_OFF); + alm |= ((tm->tm_hour & HOR_MSK) << HOR_OFF); + + spin_lock_irq(&rtc->lock); + RTC_ALM = alm; + + while ((RTC_STA & WD) != WD) + continue; + + if (wkalrm->enabled) + RTC_CR |= ALARM_INT; + spin_unlock_irq(&rtc->lock); + + return 0; +} + +const static struct rtc_class_ops rtc_ops = { + .alarm_irq_enable = atc_alarm_irq_enable, + .read_time = atc_rtc_read_time, + .set_time = atc_rtc_set_time, + .read_alarm = atc_rtc_read_alarm, + .set_alarm = atc_rtc_set_alarm, +}; + +static int atc_rtc_probe(struct platform_device *pdev) +{ + struct atc_rtc *rtc = &rtc_platform_data; + int ret = -ENOENT; + + spin_lock_init(&rtc->lock); + + rtc->alarm_irq = platform_get_irq(pdev, 1); + if (rtc->alarm_irq < 0) + goto err_exit; + + rtc->interrupt_irq = platform_get_irq(pdev, 0); + if (rtc->interrupt_irq < 0) + goto err_exit; + + rtc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!rtc->res) + goto err_exit; + + ret = -EBUSY; + + rtc->res = request_mem_region(rtc->res->start, + rtc->res->end - rtc->res->start + 1, + pdev->name); + if (!rtc->res) + goto err_request_region; + + ret = -EINVAL; + + rtc->regbase = (void __iomem *)ioremap(rtc->res->start, + rtc->res->end - rtc->res->start + 1); + if (!rtc->regbase) + goto err_ioremap1; + + if ((RTC_ID & ID_MSK) != ATCRTC100ID) + return -ENOENT; + +#if CHECK_DAY_15 + RTC_CNT = DAY_MSK << DAY_OFF; + while ((RTC_STA & WD) != WD) + continue; + + if (DAY_MSK << DAY_OFF != RTC_CNT) { + pr_err("rtc initialize fail\n"); + return -ENOENT; + } +#endif + platform_set_drvdata(pdev, rtc); + + if (of_property_read_bool(pdev->dev.of_node, "wakeup-source")) + device_init_wakeup(&pdev->dev, true); + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + rtc->rtc_dev->ops = &rtc_ops; + + if (IS_ERR(rtc->rtc_dev)) { + ret = PTR_ERR(rtc->rtc_dev); + goto err_unmap; + } + + rtc->rtc_dev->max_user_freq = 256; + rtc->rtc_dev->irq_freq = 1; + + ret = devm_rtc_register_device(rtc->rtc_dev); + if (ret) + goto err_unmap; + + ret = request_irq(rtc->alarm_irq, rtc_alarm, 0, + "RTC Alarm : atcrtc100", rtc); + if (ret) + goto err_exit; + + ret = request_irq(rtc->interrupt_irq, rtc_interrupt, + 0, "RTC Interrupt : atcrtc100", rtc); + if (ret) + goto err_interrupt_irq; + + RTC_CR |= RTC_EN; + + return 0; + +err_unmap: + iounmap(rtc->regbase); +err_ioremap1: + release_resource(rtc->res); +err_request_region: + free_irq(rtc->interrupt_irq, rtc); +err_interrupt_irq: + free_irq(rtc->alarm_irq, rtc); +err_exit: + return ret; +} + +static int atc_rtc_remove(struct platform_device *pdev) +{ + struct atc_rtc *rtc = platform_get_drvdata(pdev); + + /* + * Because generic rtc will not execute rtc_device_release() + * when call rtc_device_unregister(), + * rtc id will increase when unloading a rtc driver. + * This can work around to recycle rtc id. + * But if kernel fix this issue, it shell be removed away. + */ + rtc->rtc_dev->dev.release(&rtc->rtc_dev->dev); + RTC_CR &= ~(RTC_EN | SEC_INT | ALARM_INT); + free_irq(rtc->alarm_irq, rtc); + free_irq(rtc->interrupt_irq, rtc); + iounmap(rtc->regbase); + release_resource(rtc->res); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int atc_rtc_resume(struct device *dev) +{ + struct atc_rtc *rtc_dd = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc_dd->alarm_irq); + + return 0; +} + +static int atc_rtc_suspend(struct device *dev) +{ + struct atc_rtc *rtc_dd = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc_dd->alarm_irq); + + return 0; +} +#endif +static SIMPLE_DEV_PM_OPS(atc_rtc_pm_ops, atc_rtc_suspend, atc_rtc_resume); + +#ifdef CONFIG_OF +static const struct of_device_id atc_rtc_dt_match[] = { + {.compatible = "andestech,atcrtc100" }, + {}, +}; +MODULE_DEVICE_TABLE(of, atc_rtc_dt_match); +#endif + +static struct platform_driver atc_rtc_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atc_rtc_dt_match), + .pm = &atc_rtc_pm_ops, + }, + .probe = atc_rtc_probe, + .remove = atc_rtc_remove, +}; + +module_platform_driver(atc_rtc_platform_driver); +MODULE_DESCRIPTION("Andes ATCRTC100 driver"); +MODULE_AUTHOR("Paul Mundt , " + "Jamie Lenehan , " + "Angelo Castello , " + "Nick Hu "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); From 0baa8675e5c50d13c332f8190558c5f48e069a11 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Fri, 5 May 2023 08:37:19 +0800 Subject: [PATCH 031/169] rtc: andes: atcrtc100: add new readl_fixup() when driver probing Signed-off-by: Charles Ci-Jyun Wu --- drivers/rtc/rtc-atcrtc100.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c index f5ced02f648b2d..a9b9dce6d181a1 100644 --- a/drivers/rtc/rtc-atcrtc100.c +++ b/drivers/rtc/rtc-atcrtc100.c @@ -217,6 +217,8 @@ const static struct rtc_class_ops rtc_ops = { static int atc_rtc_probe(struct platform_device *pdev) { + int (*read_fixup)(void __iomem *addr, unsigned int val, + unsigned int shift_bits); struct atc_rtc *rtc = &rtc_platform_data; int ret = -ENOENT; @@ -249,6 +251,16 @@ static int atc_rtc_probe(struct platform_device *pdev) if (!rtc->regbase) goto err_ioremap1; + read_fixup = symbol_get(readl_fixup); + /* Check ID and Revision register 0x030110 */ + ret = read_fixup(rtc->regbase, 0x030110, 8); + symbol_put(readl_fixup); + if (!ret) { + dev_err(&pdev->dev, + "Failed to read the ID register, ATCRTC100 is not supported.\n"); + return -ENOENT; + } + if ((RTC_ID & ID_MSK) != ATCRTC100ID) return -ENOENT; From 56a9dbbad24a59e384ffbb84416067f469665757 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Thu, 11 May 2023 15:07:20 +0800 Subject: [PATCH 032/169] soc: andes: ppma: fix complier error when disable ppma Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/configs/amp.config | 2 +- arch/riscv/mm/ioremap.c | 6 ++++-- include/soc/andes/ppma.h | 24 ++++++++++++++++-------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/arch/riscv/configs/amp.config b/arch/riscv/configs/amp.config index 11dbd76dfca949..63ae179e09844c 100644 --- a/arch/riscv/configs/amp.config +++ b/arch/riscv/configs/amp.config @@ -1,5 +1,5 @@ CONFIG_ATCSMU=y -# CONFIG_PMA is not set +# CONFIG_ANDES_PPMA is not set CONFIG_AMP=y CONFIG_UIO=y diff --git a/arch/riscv/mm/ioremap.c b/arch/riscv/mm/ioremap.c index 31c3309c472618..646bd8ad4627f9 100644 --- a/arch/riscv/mm/ioremap.c +++ b/arch/riscv/mm/ioremap.c @@ -6,6 +6,7 @@ #include #include #include +#include #include void __iomem *ioremap_wc(phys_addr_t phys_addr, size_t size) @@ -17,7 +18,7 @@ void __iomem *ioremap_wc(phys_addr_t phys_addr, size_t size) ret = ioremap_prot(phys_addr, size, pgprot_val(prot)); - if (ret) + if (ret && !andes_pa_msb) andes_set_ppma(phys_addr, ret, size); return ret; @@ -26,7 +27,8 @@ EXPORT_SYMBOL(ioremap_wc); bool iounmap_allowed(void *addr) { - andes_free_ppma(addr); + if (!andes_pa_msb) + andes_free_ppma(addr); return true; } EXPORT_SYMBOL(iounmap_allowed); diff --git a/include/soc/andes/ppma.h b/include/soc/andes/ppma.h index 0d21bec9ca6547..6c93d6827085e4 100644 --- a/include/soc/andes/ppma.h +++ b/include/soc/andes/ppma.h @@ -8,8 +8,6 @@ #include -#ifdef CONFIG_ANDES_PPMA - struct ppma_arg_t { phys_addr_t phys_addr; unsigned long va_addr; @@ -18,11 +16,21 @@ struct ppma_arg_t { extern struct ppma_arg_t ppma_arg; -extern void andes_set_ppma(phys_addr_t phys_addr, - void *va_addr, - size_t size); -extern void andes_free_ppma(void *addr); -extern long andes_probe_ppma(void); - +#ifdef CONFIG_ANDES_PPMA +void andes_set_ppma(phys_addr_t phys_addr, + void *va_addr, + size_t size); +void andes_free_ppma(void *addr); +long andes_probe_ppma(void); +#else +static inline void andes_set_ppma(phys_addr_t phys_addr, + void *va_addr, + size_t size) {} +static inline void andes_free_ppma(void *addr) {} +static inline long andes_probe_ppma(void) +{ + return 0; +} #endif /* CONFIG_ANDES_PPMA */ + #endif /* !__SOC_ANDES_PPMA_H */ From 46eec2328e36887f2fae38f22a5c6d8842c38d64 Mon Sep 17 00:00:00 2001 From: Randolph Date: Tue, 9 May 2023 15:40:11 +0800 Subject: [PATCH 033/169] pwm: andes: atcpit100: add Andes's PWM module Add Andes's PWM module and its defconfig Signed-off-by: Randolph --- arch/riscv/configs/andes-support.config | 3 + drivers/pwm/Kconfig | 9 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-atcpit100.c | 289 ++++++++++++++++++++++++ 4 files changed, 302 insertions(+) create mode 100644 drivers/pwm/pwm-atcpit100.c diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 62a644d1720420..c50d8190cb3e85 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -11,3 +11,6 @@ CONFIG_ATCDMAC300G=y CONFIG_RISCV_ISA_ZICBOM=y # CONFIG_RISCV_PMU is not set + +CONFIG_PWM=y +CONFIG_PWM_ATCPIT100=y diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 60d13a949bc58c..72a710d00bb535 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -51,6 +51,15 @@ config PWM_AB8500 To compile this driver as a module, choose M here: the module will be called pwm-ab8500. +config PWM_ATCPIT100 + tristate "Andes AE350 PWM support" + depends on PLAT_AE350 + help + Generic PWM framework driver for Andes AE350 platform. + + To compile this driver as a module, choose M here. + The module will be called pwm-atcpwm. + config PWM_ATMEL tristate "Atmel PWM support" depends on ARCH_AT91 || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 7bf1a29f02b843..0adaaf3f98bdf0 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o +obj-$(CONFIG_PWM_ATCPIT100) += pwm-atcpit100.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o diff --git a/drivers/pwm/pwm-atcpit100.c b/drivers/pwm/pwm-atcpit100.c new file mode 100644 index 00000000000000..a216bfd03c4d56 --- /dev/null +++ b/drivers/pwm/pwm-atcpit100.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CLK_EXTERNAL 32768 +#define CLK_INTERNAL 60000000 +#define PRESCALE_MIN 0 +#define CHANNEL_ENABLE 0x1c +#define CHMODE_PWM 0x4 +#define DUTY_CYCLE_HIGH_MIN 0x00000000 +#define DUTY_CYCLE_HIGH_MAX 0x0000ffff +#define PERIOD_COUNT_MIN 0x00000000 +#define PERIOD_COUNT_MAX 0x0000ffff + +#define CHCLK_APB (1 << 3) +#define RELOAD(n) (0x24 + (n * 0x10)) +#define CHPWMEN(ch) ((1 << 3) << (4 * ch)) +#define CHANNEL_CONTROL(n) (0x20 + (n * 0x10)) + +#define PWM_READL(offset) \ + readl(ap->base + (offset)) + +#define PWM_WRITEL(val, offset) \ + writel((val), ap->base + (offset)) + +struct atcpit_pwmc { + struct pwm_chip chip; + void __iomem *base; + struct clk *clk; + u64 rate[2]; + u32 val; + bool en[2]; + enum pwm_polarity polarity; /* PWM polarity */ +}; + +static inline struct atcpit_pwmc *to_atcpit_pwmc(struct pwm_chip *_chip) +{ + return container_of(_chip, struct atcpit_pwmc, chip); +} + +static void atcpit_pwmc_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atcpit_pwmc *ap = to_atcpit_pwmc(chip); + unsigned int chan = pwm->hwpwm; + unsigned int value; + + value = PWM_READL(CHANNEL_ENABLE); + value &= ~(CHPWMEN(pwm->hwpwm)); + PWM_WRITEL(value, CHANNEL_ENABLE); + + pr_info("[pwm] channel:%d disable!\n", chan); + ap->en[chan] = 0; + pwm->state.enabled = 0; +} + +static int atcpit_pwmc_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + struct atcpit_pwmc *ap = to_atcpit_pwmc(chip); + u64 val, div, rate; + unsigned long prescale, pc, dc; + unsigned int value, chan = pwm->hwpwm; + + /* + * Find period count, duty count and prescale to suit duty_ns and + * period_ns. This is done according to formulas described below: + * + * period_ns = 10^9 * (PRESCALE + 1) * PC / PWM_CLK_RATE + * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE + * + * PC = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) + * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) + */ + + prescale = PRESCALE_MIN; + if (period_ns <= duty_ns) + return -EINVAL; + + if (ap->en[chan]) { + while (1) { + rate = ap->rate[pwm->hwpwm]; + div = 1000000000; + div = div * (1 + prescale); + val = rate * period_ns; + pc = div64_u64(val, div); + val = rate * duty_ns; + dc = div64_u64(val, div); + pc -= dc; + + /* If duty_ns or period_ns are not achievable then return */ + if (pc < PERIOD_COUNT_MIN || dc < DUTY_CYCLE_HIGH_MIN) { + atcpit_pwmc_disable(chip, pwm); + return -EINVAL; + } + + /* If pc and dc are in bounds, the calculation is done */ + if (pc <= PERIOD_COUNT_MAX && dc <= DUTY_CYCLE_HIGH_MAX) { + if (pc > 0) + pc--; + + if (ap->polarity == PWM_POLARITY_NORMAL) { + value = pc; + value |= ((dc) << 16); + ap->val = value; + } else { + value = dc; + value |= ((pc) << 16); + } + PWM_WRITEL(value, RELOAD(pwm->hwpwm)); + break; + } + + /* Otherwise recalculate pc and dc */ + if (pc > PERIOD_COUNT_MAX || dc > DUTY_CYCLE_HIGH_MAX) { + atcpit_pwmc_disable(chip, pwm); + return -EINVAL; + } + } + } + + return 0; +} + +static int atcpit_pwmc_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct atcpit_pwmc *ap = to_atcpit_pwmc(chip); + int ret; + unsigned int chan = pwm->hwpwm; + unsigned int value; + + value = PWM_READL(CHANNEL_CONTROL(chan)); + value |= (CHMODE_PWM); + + if (pwm->hwpwm) + value |= (CHCLK_APB); + + PWM_WRITEL(value, CHANNEL_CONTROL(chan)); + value = PWM_READL(CHANNEL_ENABLE); + value |= CHPWMEN(pwm->hwpwm); + PWM_WRITEL(value, CHANNEL_ENABLE); + ap->en[chan] = 1; + pwm->state.enabled = 1; + ret = atcpit_pwmc_config(chip, pwm, pwm_get_duty_cycle(pwm), + pwm_get_period(pwm)); + if (ret < 0) { + clk_disable_unprepare(ap->clk); + return ret; + } + pr_info("[pwm] channel:%d enable!\n", chan); + + return 0; +} + +static int atcpit_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int err; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + if (!state->enabled) { + if (pwm->state.enabled) + atcpit_pwmc_disable(chip, pwm); + + return 0; + } + + err = atcpit_pwmc_config(pwm->chip, pwm, state->duty_cycle, state->period); + if (err) + return err; + + if (!pwm->state.enabled) + err = atcpit_pwmc_enable(chip, pwm); + + return err; +} + +static const struct pwm_ops atcpit_pwm_ops = { + .free = atcpit_pwmc_disable, + .apply = atcpit_pwmc_apply, + .owner = THIS_MODULE, +}; + +static int atcpit_pwmc_probe(struct platform_device *pdev) +{ + struct atcpit_pwmc *ap; + struct resource *res; + int ret = 0; + + ap = devm_kzalloc(&pdev->dev, sizeof(*ap), GFP_KERNEL); + + if (ap == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, ap); + ap->chip.dev = &pdev->dev; + ap->chip.ops = &atcpit_pwm_ops; + ap->chip.base = -1; + ap->chip.npwm = 2; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ap->base = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(ap->base)) + return PTR_ERR(ap->base); + + ap->rate[0] = CLK_EXTERNAL; + ap->rate[1] = CLK_INTERNAL; + + if (IS_ERR(ap->clk)) { + dev_err(&pdev->dev, "failed to get clock: %ld\n", + PTR_ERR(ap->clk)); + return PTR_ERR(ap->clk); + } + + ret = pwmchip_add(&ap->chip); + if (ret < 0) + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + + return ret; +} + +static int atcpit_pwmc_remove(struct platform_device *pdev) +{ + struct atcpit_pwmc *ap = platform_get_drvdata(pdev); + + pwmchip_remove(&ap->chip); + + dev_dbg(&pdev->dev, "driver removed\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int atcpit_pwmc_suspend(struct device *dev) +{ + struct atcpit_pwmc *ap = dev_get_drvdata(dev); + + pr_info("[pwm] suspend!\n"); + + clk_disable_unprepare(ap->clk); + + return 0; +} + +static int atcpit_pwmc_resume(struct device *dev) +{ + struct atcpit_pwmc *ap = dev_get_drvdata(dev); + + pr_info("[pwm] resume!\n"); + + clk_prepare_enable(ap->clk); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(atcpit_pwmc_pm_ops, + atcpit_pwmc_suspend, atcpit_pwmc_resume); + +static const struct of_device_id atcpit_pwmc_dt[] = { + { .compatible = "andestech,atcpit100-pwm" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, atcpit_pwmc_dt); + +static struct platform_driver atcpit_pwmc_driver = { + .driver = { + .name = "atcpit100-pwm", + .of_match_table = atcpit_pwmc_dt, + .pm = &atcpit_pwmc_pm_ops, + }, + .probe = atcpit_pwmc_probe, + .remove = atcpit_pwmc_remove, +}; +module_platform_driver(atcpit_pwmc_driver); +MODULE_AUTHOR("Andes Technology Corporation "); +MODULE_DESCRIPTION("Andes atcpit100 PWM driver"); +MODULE_LICENSE("GPL"); From 5dfd1ab44d2bacd10fbb0272dad7305a420d1bd2 Mon Sep 17 00:00:00 2001 From: Randolph Date: Tue, 9 May 2023 17:12:01 +0800 Subject: [PATCH 034/169] i2c: andes: atciic100: add Andes's I2C module Add Andes's I2C module and the its defconfig Signed-off-by: Randolph --- arch/riscv/configs/andes-support.config | 6 + drivers/i2c/busses/Kconfig | 40 ++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-atciic100.c | 532 ++++++++++++++++++++++++ 4 files changed, 579 insertions(+) create mode 100644 drivers/i2c/busses/i2c-atciic100.c diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index c50d8190cb3e85..140b06592a78f8 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -14,3 +14,9 @@ CONFIG_RISCV_ISA_ZICBOM=y CONFIG_PWM=y CONFIG_PWM_ATCPIT100=y + +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_ATCIIC100=y +CONFIG_ATCIIC_IRQ=y diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index e50f9603d189e8..378d90ae7f6637 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -430,6 +430,46 @@ config I2C_AT91_SLAVE_EXPERIMENTAL - There are some mismtaches with a SAMA5D4 as slave and a SAMA5D2 as master. +config I2C_ATCIIC100 + tristate "Andes AE350 I2C interface" + depends on PLAT_AE350 + depends on I2C=y + help + This supports the use of the I2C interface on Andes AE350 platform. + + The default is using ATCIIC_PIO mode. + +choice + depends on I2C_ATCIIC100 + prompt "Transfer mode selection" + default ATCIIC_PIO + help + This supports the use of the I2C interface with PIO/IRO mode on Andes AE350 + processors. + + If unsure, select ATCIIC_PIO as default + +config ATCIIC_PIO + bool "Pio mode" + help + This supports the use of the I2C interface with PIO mode on Andes AE350 + processors. + +config ATCIIC_IRQ + bool "Irq mode" + help + This supports the use of the I2C interface with interrupt mode on Andes AE350 + processors. +endchoice + +config ATCIIC100_DEBUG + bool "atciic100 debugging" + default n + depends on I2C_ATCIIC100 != n + help + This is an option for use by developers; most people should + say N here. This enables atciic100 driver debugging. + config I2C_AU1550 tristate "Au1550/Au1200/Au1300 SMBus interface" depends on MIPS_ALCHEMY diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 784a803279d990..8b1219d0a7f032 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -43,6 +43,7 @@ i2c-at91-objs := i2c-at91-core.o i2c-at91-master.o ifeq ($(CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL),y) i2c-at91-objs += i2c-at91-slave.o endif +obj-$(CONFIG_I2C_ATCIIC100) += i2c-atciic100.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o diff --git a/drivers/i2c/busses/i2c-atciic100.c b/drivers/i2c/busses/i2c-atciic100.c new file mode 100644 index 00000000000000..0370d103a188cb --- /dev/null +++ b/drivers/i2c/busses/i2c-atciic100.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * I2C Support for Andes ATCIIC100 two-wire interface (TWI) + * 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. + * + * Copyright (C) 2016 Andes Technology Corporation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SUPPORT_AT24C128_BY_BOARD 0 + +#define DRV_NAME "atciic100" +#define EMPTY_STR + +/* ID and Revision Register */ +#define IDREV 0x00 +#define ID_OFF 12 +#define ID_MSK 0xfffff +#define ID 0x02021 + +/* Configuration Register */ +#define CFG 0x10 +#define FIFOSIZE_MASK 0x3 + +/* INterrupt Enable Register */ +#define INTEN 0x14 + +/* Status Register */ +#define STA 0x18 + +#define LINESDA (1 << 14) +#define LINESCL (1 << 13) +#define GENCALL (1 << 12) +#define BBUSY (1 << 11) +#define ACK (1 << 10) +#define COMPL (1 << 9) +#define BRECV (1 << 8) +#define BTRAN (1 << 7) +#define START (1 << 6) +#define STOP (1 << 5) +#define ALOSE (1 << 4) +#define ADDRHIT (1 << 3) +#define FIFOHAF (1 << 2) +#define FIFOFUL (1 << 1) +#define FIFOETY (1 << 0) +#define STAW1C (COMPL | BRECV | BTRAN | START | STOP | ALOSE | ADDRHIT) + +/* Address Register */ +#define ADR 0x1c + +/* Data Register */ +#define DAT 0x20 + +/* Control Register */ +#define CTL 0x24 +#define PSTART (1 << 12) +#define PADDR (1 << 11) +#define PDATA (1 << 10) +#define PSTOP (1 << 9) +#define DIR_RX (1 << 8) +#define DCNTMSK 0xff + +/* Command Register */ +#define CMD 0x28 +#define ISSUE_DATA 0x1 +#define RESPOND_ACK 0x2 +#define RESPOND_NACK 0x3 +#define CLEAR_FIFO 0x4 +#define RESET_IIC 0x5 + +/* Setup Register */ +#define SETUP 0x2c +#define MASTER (1 << 2) +#define ADDR10 (1 << 1) +#define IICEN (1 << 0) + +#define xget (buf[i++] = IIC(DAT)) +#define xput (IIC(DAT) = buf[i++]) + +#define XFER_TIMEOUT 0xf000 +#define MODE_PIO 0 +#define MODE_IRQ 1 +#ifdef CONFIG_ATCIIC_PIO +#define XFER_MODE MODE_PIO +#else +#define XFER_MODE MODE_IRQ +#endif + +#ifdef CONFIG_ATCIIC100_DEBUG +#define PRINTK printk +#else +#define PRINTK(x...) +#endif + +#define IIC(reg) (*(volatile u32 *)(iface->regs_base + reg)) + +#define xwait(c, b, eq, m) \ +do { \ +int cnt = c; \ +while ((IIC(STA)&b) eq b) \ +if (!(cnt--)) { \ + PRINTK(KERN_INFO "wait %s error\n", m); \ + return -EIO; \ +} \ +} while (0) + +#define xfer_wait(c, b, eq, m, act1, act2) \ +while (length--) { \ + act1; \ + xwait(c, b, eq, m); \ + act2; \ +} + +struct atciic { + int irq; + spinlock_t lock; + char read_write; + u8 fifolen; + u8 mode; + u8 command; + u8 *ptr; + int readNum; + int writeNum; + int cur_mode; + int addr_hit; + int cmpl; + int manual_stop; + int result; + struct i2c_adapter adap; + struct completion complete; + struct i2c_msg *pmsg; + int msg_num; + int cur_msg; + u16 saved_clkdiv; + u16 saved_control; + void __iomem *regs_base; + struct resource *res; +}; + +static u32 xrecev(struct atciic *iface) +{ + u32 status_wait = 0; + + if (iface->readNum >= iface->fifolen) { + IIC(INTEN) = (COMPL | FIFOFUL); + status_wait = FIFOFUL; + } else { + IIC(INTEN) = COMPL; + status_wait = COMPL; + } + return status_wait; +} + +static irqreturn_t atciic_irq(int irq, void *dev_id) +{ + struct atciic *iface = dev_id; + int i = 0; + unsigned long flags; + u32 status = IIC(STA); + u32 rleft = (IIC(CTL)&0xff); + char flen = iface->fifolen; + unsigned char *buf = iface->ptr; + + spin_lock_irqsave(&iface->lock, flags); + IIC(STA) = status; + if (status & ADDRHIT) + iface->addr_hit = 1; + + if (iface->pmsg->flags & I2C_M_RD) { + if (status & xrecev(iface)) { + flen = iface->readNum - rleft; + iface->readNum -= flen; + while (flen--) + xget; + xrecev(iface); + } + } else { + IIC(INTEN) = (COMPL | FIFOETY); + if (iface->writeNum <= flen) + flen = iface->writeNum; + + if (status & FIFOETY) { + iface->writeNum -= flen; + while (flen--) + xput; + } + } + iface->ptr += i; + spin_unlock_irqrestore(&iface->lock, flags); + if (status & COMPL) { + IIC(INTEN) = 0; + iface->cmpl = 1; + if (iface->addr_hit == 1) + complete(&iface->complete); + } + return IRQ_HANDLED; +} + +static int irq_transfer(struct atciic *iface) +{ + if (!wait_for_completion_timeout(&iface->complete, iface->adap.timeout)) { + iface->result = -EIO; + PRINTK(KERN_INFO + "%s(%s) fail, addrhit 0x%x, cmpl 0x%x, %s %d, int 0x%lx, status 0x%lx\n", + (iface->pmsg->flags & I2C_M_RD) ? "read" : "write", + iface->mode ? "irq" : "pio", + iface->addr_hit, + iface->cmpl, + (iface->pmsg->flags & I2C_M_RD) ? "readNum" : "writeNum", + (iface->pmsg->flags & I2C_M_RD) ? iface->readNum : iface->writeNum, + IIC(INTEN), IIC(STA)); + } + reinit_completion(&(iface->complete)); + return iface->result; +} + +static int pio_transfer(struct atciic *iface) +{ + int length = iface->pmsg->len; + unsigned char *buf = iface->pmsg->buf; + int i = 0; + + if (iface->pmsg->flags & I2C_M_RD) { + xfer_wait(XFER_TIMEOUT, FIFOETY, ==, "fifo empty", EMPTY_STR, xget); + xwait(XFER_TIMEOUT, COMPL, !=, "cmpl"); + } else { + xwait(XFER_TIMEOUT, ADDRHIT, !=, "addrhit"); + xfer_wait(XFER_TIMEOUT, FIFOFUL, ==, "fifofull", xput, EMPTY_STR); + xwait(XFER_TIMEOUT, COMPL, !=, "cmpl"); + } + return 0; +} + +static int atciic_transfer(struct atciic *iface) +{ + if (iface->mode == MODE_PIO) + return pio_transfer(iface); + else if (iface->mode == MODE_IRQ) + return irq_transfer(iface); + else + return -EIO; +} + +/* Adjust timeout depend on transaction time. */ +static void calculate_timeout(struct i2c_adapter *adap, int length) +{ + if (length > 4) + adap->timeout = 3 * HZ; + else + adap->timeout = HZ/8; +} + +/* Generic i2c master receive. */ +static int atciic_read(struct i2c_adapter *adap, unsigned char *buf, int length) +{ + int rc = 0; + struct atciic *iface = adap->algo_data; + + iface->readNum = length; + iface->result = 0; + IIC(CTL) &= ~0xff; + IIC(CTL) |= ((length & 0xff) | DIR_RX); + if (iface->mode == MODE_IRQ) + IIC(INTEN) = (FIFOFUL | COMPL); + IIC(CMD) = ISSUE_DATA; + rc = atciic_transfer(iface); + return rc; +} + +/* Generic i2c master transmit. */ +static int atciic_write(struct i2c_adapter *adap, unsigned char *buf, int length) +{ + int rc = 0; + struct atciic *iface = adap->algo_data; + + iface->result = 0; + iface->writeNum = length; + IIC(CTL) &= ~(DIR_RX | DCNTMSK); + IIC(CTL) |= (length&0xff); + if (iface->mode == MODE_IRQ) + IIC(INTEN) = (FIFOETY | COMPL); + /* Write cycle time issue */ + mdelay(5); + IIC(CMD) = ISSUE_DATA; + rc = atciic_transfer(iface); + return rc; +} + +/* Generic i2c master transfer entrypoint. */ +static int atciic_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num) +{ + int i, ret; + u16 rw; + int retry = adap->retries; + struct atciic *iface = adap->algo_data; + struct i2c_msg *msg; + + if ((pmsg->flags & I2C_M_RD) && (pmsg->len == 1)) + retry = 0; +xfer_start: + msg = pmsg; + dev_dbg(&adap->dev, "%s: processing %d messages:\n", __func__, num); + for (i = 0; i < num; i++) { + if (msg->len > 0x100) + return -EIO; + dev_dbg(&adap->dev, " #%d: %sing %d byte%s %s 0x%02x\n", i, + pmsg->flags & I2C_M_RD ? "read" : "writ", + pmsg->len, pmsg->len > 1 ? "s" : "", + pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr); + iface->addr_hit = 0; + iface->cmpl = 0; + iface->pmsg = msg; + rw = iface->pmsg->flags & I2C_M_RD; + iface->ptr = msg->buf; + IIC(INTEN) = 0; + IIC(ADR) = (msg->addr); + if (msg->len && msg->buf) { /* sanity check */ + calculate_timeout(adap, msg->len); + + if (msg->flags & I2C_M_RD) + ret = atciic_read(adap, msg->buf, msg->len); + else + ret = atciic_write(adap, msg->buf, msg->len); + + IIC(STA) = STAW1C; + if (ret) { + if (retry--) { + PRINTK(KERN_INFO "xfer %s(%s) retry\n", + rw ? "read" : "write", + iface->mode ? "irq" : "pio"); + goto xfer_start; + } else + return ret; + } + } + dev_dbg(&adap->dev, "transfer complete\n"); + msg++; /* next message */ + } + return i; +} + +/* Return list of supported functionality. */ +static u32 atciic_func(struct i2c_adapter *adapter) +{ + return (u32)(I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | I2C_FUNC_SMBUS_READ_I2C_BLOCK | + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_WORD_DATA); +} + +static struct i2c_algorithm atciic_algorithm = { + .master_xfer = atciic_xfer, + .functionality = atciic_func, +}; + +#if SUPPORT_AT24C128_BY_BOARD +#include + +static struct at24_platform_data at24c128 = { + .byte_len = 0x00020000, + .page_size = 64, + .flags = AT24_FLAG_ADDR16, +}; + +static struct i2c_board_info ae300_i2c_devices[] __initdata = { + { + I2C_BOARD_INFO("24c128", 0x50), + .platform_data = &at24c128, + }, +}; + +void __init ae300_add_device_i2c(struct i2c_board_info *devices, int nr_devices) +{ + i2c_register_board_info(0, devices, nr_devices); +} +#endif /* SUPPORT_AT24C128_BY_BOARD */ + +/* Main initialization routine. */ +static int atciic_probe(struct platform_device *pdev) +{ + int (*read_fixup)(void __iomem *addr, unsigned int val, + unsigned int shift_bits); + struct i2c_adapter *padap; + struct atciic *iface; + int rc, ret; + +#if SUPPORT_AT24C128_BY_BOARD + ae300_add_device_i2c(ae300_i2c_devices, ARRAY_SIZE(ae300_i2c_devices)); +#endif + + iface = kzalloc(sizeof(struct atciic), GFP_KERNEL); + if (!iface) { + rc = -ENOMEM; + goto out_error_nomem; + } + spin_lock_init(&(iface->lock)); + iface->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (iface->res == NULL) { + dev_err(&pdev->dev, "Cannot get IORESOURCE_MEM\n"); + rc = -ENOMEM; + goto out_error_get_res; + } + + if (!iface->res) { + rc = -ENOMEM; + goto out_error_get_res; + } + + iface->regs_base = devm_ioremap_resource(&pdev->dev, iface->res); + + if (iface->regs_base == NULL) { + dev_err(&pdev->dev, "Cannot map IO\n"); + rc = -ENXIO; + goto out_error_get_io; + } + + /* Check ID and Revision Register */ + read_fixup = symbol_get(readl_fixup); + ret = read_fixup(iface->regs_base, 0x020210, 8); + symbol_put(readl_fixup); + if (!ret) { + dev_err(&pdev->dev, "I2C version NOT match, bitmap not support atciic100\n"); + rc = -ENXIO; + goto out_error_get_io; + } + + iface->irq = platform_get_irq(pdev, 0); + if (iface->irq < 0) { + dev_err(&pdev->dev, "No IRQ specified\n"); + rc = -ENOENT; + goto out_error_get_io; + } + + rc = request_irq(iface->irq, atciic_irq, 0, pdev->name, iface); + if (rc) { + dev_err(&pdev->dev, "Can't get IRQ %d !\n", iface->irq); + rc = -ENODEV; + goto out_error_req_irq; + } + + iface->mode = XFER_MODE; + init_completion(&(iface->complete)); + padap = &iface->adap; + strscpy(padap->name, pdev->name, sizeof(padap->name)); + padap->algo = &atciic_algorithm; + padap->class = I2C_CLASS_HWMON; + padap->dev.parent = &pdev->dev; + padap->algo_data = iface; + padap->timeout = 3 * HZ; + padap->retries = 1; + rc = i2c_add_numbered_adapter(padap); + if (rc < 0) { + dev_err(&pdev->dev, "Can't add i2c adapter!\n"); + goto out_error_add_adapter; + } + + platform_set_drvdata(pdev, padap); + padap = platform_get_drvdata(pdev); + iface->fifolen = (1 << ((IIC(CFG) & FIFOSIZE_MASK) + 1)); + IIC(SETUP) |= (MASTER | IICEN); + + dev_info(&pdev->dev, "Andes i2c bus driver.\n"); + return 0; + +out_error_add_adapter: + free_irq(iface->irq, iface); +out_error_req_irq: +out_error_get_io: + release_mem_region(iface->res->start, resource_size(iface->res)); +out_error_get_res: + kfree(iface); +out_error_nomem: + return rc; +} + +static int atciic_remove(struct platform_device *pdev) +{ + struct i2c_adapter *adapter = platform_get_drvdata(pdev); + struct atciic *iface = adapter->algo_data; + + i2c_del_adapter(adapter); + platform_set_drvdata(pdev, NULL); + release_mem_region(iface->res->start, resource_size(iface->res)); + free_irq(iface->irq, iface); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id atc_iic_dt_match[] = { + { .compatible = "andestech,atciic100" }, + {}, +}; +MODULE_DEVICE_TABLE(of, atc_iic_dt_match); +#endif + +static struct platform_driver atciic_platform_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atc_iic_dt_match), + }, + .probe = atciic_probe, + .remove = atciic_remove, +}; + +module_platform_driver(atciic_platform_driver); +MODULE_AUTHOR("Rick Chen"); +MODULE_AUTHOR("Dylan Jhong"); +MODULE_DESCRIPTION("I2C driver for Andes atciic100"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:atciic100"); From 9b7b1eefa975674a9845611e40b558b3acec5af7 Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Wed, 26 Apr 2023 18:44:21 +0800 Subject: [PATCH 035/169] dmaengine: andes: atcdmac300g: support cyclic mode 1. Support cyclic mode 2. Fine-tune the DMAC driver to avoid potential risks. 3. Change the address aligned from 32 to 64 when creating the descriptor memory pool. Signed-off-by: CL Wang --- drivers/dma/atcdmac300g.c | 350 +++++++++++++++++++++++++++++++++----- drivers/dma/atcdmac300g.h | 58 ++++--- 2 files changed, 348 insertions(+), 60 deletions(-) diff --git a/drivers/dma/atcdmac300g.c b/drivers/dma/atcdmac300g.c index 01b380a07d9f80..20970fb1b75db4 100644 --- a/drivers/dma/atcdmac300g.c +++ b/drivers/dma/atcdmac300g.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "dmaengine.h" #include "atcdmac300g.h" @@ -39,6 +41,11 @@ static inline void v5_dis_channel(struct v5_dma_chan *v5chan) clrbl(CHEN, v5chan->ch_regs+CH_CTL_OFF); } +static inline void v5_abort_channel(struct v5_dma_chan *v5chan) +{ + setbl(v5chan->chan_id, v5chan->ch_regs + CH_ABT); +} + static dma_cookie_t v5_tx_submit(struct dma_async_tx_descriptor *tx) { struct v5_desc *desc = txd_to_v5_desc(tx); @@ -85,7 +92,10 @@ static struct v5_desc *v5_alloc_descriptor(struct dma_chan *chan, /* txd.flags will be overwritten in prep functions */ desc->txd.flags = DMA_CTRL_ACK; desc->txd.tx_submit = v5_tx_submit; - desc->txd.phys = phys; + if (v5dma->io_regs) + desc->txd.phys = phys | IOCP_MASK; + else + desc->txd.phys = phys; } return desc; @@ -155,7 +165,7 @@ static void v5_desc_put(struct v5_dma_chan *v5chan, struct v5_desc *desc) } } -/** +/* * v5_desc_chain - build chain adding a descriptor * @first: address of first descriptor of the chain * @prev: address of previous descriptor of the chain @@ -166,15 +176,22 @@ static void v5_desc_chain(struct v5_desc **first, struct v5_desc **prev, { if (!(*first)) { *first = desc; + desc->at = &desc->tx_list; } else { - (*prev)->lli.llPointerl = lower_32_bits(desc->txd.phys); + if ((*first)->cyclic == false) { + (*prev)->lli.llPointerl = lower_32_bits(desc->txd.phys); #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT - (*prev)->lli.llPointerh = upper_32_bits(desc->txd.phys); + (*prev)->lli.llPointerh = upper_32_bits(desc->txd.phys); #endif + } + /* insert the descriptor to the ring */ list_add_tail(&desc->desc_node, &(*first)->tx_list); } *prev = desc; + + desc->lli.llPointerh = 0; + desc->lli.llPointerl = 0; } /** @@ -262,19 +279,59 @@ static void v5_complete_all(struct v5_dma_chan *v5chan) */ static void v5_advance_work(struct v5_dma_chan *v5chan) { + struct v5_desc *v5desc = NULL; + dev_vdbg(chan2dev(&v5chan->chan_common), "advance_work\n"); clear_bit(V5_IS_TC, &v5chan->status); if (v5_chan_is_enabled(v5chan)) return; - if (list_empty(&v5chan->active_list) || - list_is_singular(&v5chan->active_list)) { - v5_complete_all(v5chan); + v5desc = v5_first_active(v5chan); + if (!v5desc->cyclic) { + if (list_empty(&v5chan->active_list) || + list_is_singular(&v5chan->active_list)) { + v5_complete_all(v5chan); + } else { + v5_chain_complete(v5chan, v5_first_active(v5chan), + DMA_TRANS_NOERROR); + /* advance work */ + v5_dostart(v5chan, v5_first_active(v5chan)); + } } else { - v5_chain_complete(v5chan, v5_first_active(v5chan), - DMA_TRANS_NOERROR); - /* advance work */ - v5_dostart(v5chan, v5_first_active(v5chan)); + /* For cyclic mode */ + struct dmaengine_result res; + + if (!list_empty(&v5chan->active_list)) { + res.result = DMA_TRANS_NOERROR; + + if (v5desc->num_sg == 1) { + /* Just repeat itself */ + v5_dostart(v5chan, v5_first_active(v5chan)); + } else { + struct v5_desc *next_tx; + unsigned long flags; + + /* do next SG and set the at pointer */ + v5desc->at = v5desc->at->next; + + spin_lock_irqsave(&v5chan->lock, flags); + if ((uintptr_t)v5desc->at == (uintptr_t)&v5desc->tx_list) { + next_tx = list_entry(v5desc->at, struct v5_desc, + tx_list); + } else { + next_tx = list_entry(v5desc->at, struct v5_desc, + desc_node); + } + spin_unlock_irqrestore(&v5chan->lock, flags); + + v5_dostart(v5chan, next_tx); + + dmaengine_desc_get_callback_invoke(&v5desc->txd, &res); + } + } else { + dev_vdbg(chan2dev(&v5chan->chan_common), + "active list is empty\n"); + } } } @@ -323,14 +380,11 @@ static void v5_handle_error(struct v5_dma_chan *v5chan) static void v5_tasklet(unsigned long data) { struct v5_dma_chan *v5chan = (struct v5_dma_chan *)data; - unsigned long flags; - spin_lock_irqsave(&v5chan->lock, flags); if (test_and_clear_bit(V5_IS_ERR, &v5chan->status)) v5_handle_error(v5chan); else v5_advance_work(v5chan); - spin_unlock_irqrestore(&v5chan->lock, flags); } static irqreturn_t v5_dma_interrupt(int irq, void *dev_id) @@ -346,6 +400,11 @@ static irqreturn_t v5_dma_interrupt(int irq, void *dev_id) dev_vdbg(v5dma->dma_common.dev, "int: sta = 0x%08x\n", status); + if (status == 0) + break; + + v5_dma_writel(v5dma, INT_STA, status); + for (i = 0; i < v5dma->dma_common.chancnt; i++) { v5chan = &v5dma->chan[i]; if (status & (V5_DMA_TC(i))) @@ -359,8 +418,6 @@ static irqreturn_t v5_dma_interrupt(int irq, void *dev_id) ret = IRQ_HANDLED; } } - v5_dma_writel(v5dma, INT_STA, status); - status = 0; } while (status); return ret; @@ -425,6 +482,10 @@ v5_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, if (!desc) goto err_desc_get; + if (v5dma->io_regs) { + dst |= IOCP_MASK; + src |= IOCP_MASK; + } src_maxburst = DMAC_CSR_SIZE_1024; src_width = dst_width = xfer_width(v5dma, src, dst, len); convert_burst(&src_maxburst); @@ -443,6 +504,8 @@ v5_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, #endif desc->lli.ctrl = ctrl; desc->lli.tranSize = (len >> src_width); + desc->num_sg = 1; + desc->total_len = len; return &desc->txd; @@ -465,6 +528,7 @@ v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_transfer_direction direction, unsigned long flags, void *context) { + struct v5_dma *v5dma = to_v5_dma(chan->device); struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); struct v5_dma_slave *v5slave = chan->private; struct dma_slave_config *sconfig = &v5chan->dma_sconfig; @@ -473,6 +537,7 @@ v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, u32 ctrl; dma_addr_t reg; unsigned int reg_width; + unsigned int src_reg_width; unsigned int i; struct scatterlist *sg; size_t total_len = 0; @@ -489,8 +554,8 @@ v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ctrl &= ~(SRCWIDTH_MASK|SRCADDRCTRL_MASK|DSTWIDTH_MASK| DSTADDRCTRL_MASK|SRC_HS|DST_HS|SRCREQSEL_MASK|DSTREQSEL_MASK); ctrl = SBSIZE(sconfig->src_maxburst); - reg_width = convert_buswidth(sconfig->src_addr_width); - ctrl |= SRC_WIDTH(reg_width); + src_reg_width = convert_buswidth(sconfig->src_addr_width); + ctrl |= SRC_WIDTH(src_reg_width); reg_width = convert_buswidth(sconfig->dst_addr_width); ctrl |= DST_WIDTH(reg_width); @@ -510,6 +575,10 @@ v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, goto err_desc_get; mem = sg_dma_address(sg); + + if (v5dma->io_regs) + mem |= IOCP_MASK; + len = sg_dma_len(sg); if (unlikely(!len)) { dev_dbg(chan2dev(chan), @@ -528,9 +597,10 @@ v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc->lli.dstAddrh = upper_32_bits(reg); #endif desc->lli.ctrl = ctrl; - desc->lli.tranSize = (len >> sconfig->src_maxburst); + desc->lli.tranSize = (len >> src_reg_width); v5_desc_chain(&first, &prev, desc); total_len += len; + desc->num_sg = 1; } break; @@ -542,12 +612,16 @@ v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, for_each_sg(sgl, sg, sg_len, i) { struct v5_desc *desc; u32 len; - u32 mem; + phys_addr_t mem; desc = v5_desc_get(v5chan); if (!desc) goto err_desc_get; mem = sg_dma_address(sg); + + if (v5dma->io_regs) + mem |= IOCP_MASK; + len = sg_dma_len(sg); if (unlikely(!len)) { dev_dbg(chan2dev(chan), @@ -566,9 +640,10 @@ v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, desc->lli.dstAddrh = upper_32_bits(mem); #endif desc->lli.ctrl = ctrl; - desc->lli.tranSize = (len >> sconfig->src_maxburst); + desc->lli.tranSize = (len >> src_reg_width); v5_desc_chain(&first, &prev, desc); total_len += len; + desc->num_sg = 1; } break; default: @@ -577,6 +652,7 @@ v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, /* First descriptor of the chain embedds additional information */ first->txd.cookie = -EBUSY; first->total_len = total_len; + first->cyclic = false; /* first link descriptor of list is responsible of flags */ first->txd.flags = flags; @@ -590,6 +666,167 @@ v5_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, return NULL; } +static struct dma_async_tx_descriptor * +v5_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, + size_t buf_len, size_t period_len, + enum dma_transfer_direction direction, unsigned long flags) +{ + + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + struct dma_slave_config *sconfig = &v5chan->dma_sconfig; + struct v5_desc *first = NULL; + struct v5_desc *prev = NULL; + u32 ctrl; + dma_addr_t reg; + unsigned int reg_width; + unsigned int i; + size_t total_len = 0; + struct v5_dma *v5dma = to_v5_dma(chan->device); + int period_index; + u32 src_maxburst; + + src_maxburst = DMAC_CSR_SIZE_1; + convert_burst(&src_maxburst); + sconfig->src_maxburst = src_maxburst; + + ctrl = v5_channel_readl(v5chan, CH_CTL_OFF); + ctrl &= ~(SRCWIDTH_MASK | SRCADDRCTRL_MASK | DSTWIDTH_MASK | + DSTADDRCTRL_MASK | SRC_HS | DST_HS | SRCREQSEL_MASK | + DSTREQSEL_MASK | INTABTMASK | INTERRMASK | INTTCMASK); + ctrl = SBSIZE(sconfig->src_maxburst); + reg_width = WIDTH_4; + ctrl |= SRC_WIDTH(reg_width); + ctrl |= DST_WIDTH(convert_buswidth(sconfig->dst_addr_width)); + + switch (direction) { + case DMA_MEM_TO_DEV: + ctrl |= DST_HS; + ctrl |= (SRC_ADDR_MODE_INCR | DST_ADDR_MODE_FIXED); + ctrl |= ((v5chan->req_num << DSTREQSEL) & DSTREQSEL_MASK); + reg = sconfig->dst_addr; + + for (period_index = 0; period_index < buf_len; period_index += period_len) { + struct v5_desc *desc; + u32 len; + dma_addr_t mem; + + desc = v5_desc_get(v5chan); + if (!desc) + goto err_desc_get; + + mem = buf_addr + period_index; + + if ((buf_len - period_index) > period_len) + len = period_len; + else + len = buf_len - period_index; + + if (unlikely(!len)) { + dev_dbg(chan2dev(chan), + "sg(%d) data length is zero\n", i); + goto err; + } + + if (unlikely(mem & 3 || len & 3)) { + dev_dbg(chan2dev(chan), + "sg(%d) data length is not aligned\n", i); + goto err_desc_get; + } + + if (v5dma->io_regs) + mem |= IOCP_MASK; + + desc->lli.srcAddrl = lower_32_bits(mem); + desc->lli.dstAddrl = lower_32_bits(reg); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + desc->lli.srcAddrh = upper_32_bits(mem); + desc->lli.dstAddrh = upper_32_bits(reg); +#endif + desc->lli.ctrl = ctrl; + desc->lli.tranSize = (len >> reg_width); + + desc->cyclic = true; + desc->num_sg = (buf_len + period_len - 1) / period_len; + desc->len = len; + desc->total_len = buf_len - period_index; + v5_desc_chain(&first, &prev, desc); + total_len += len; + } + break; + + case DMA_DEV_TO_MEM: + ctrl |= SRC_HS; + ctrl |= (SRC_ADDR_MODE_FIXED | DST_ADDR_MODE_INCR); + ctrl |= ((v5chan->req_num << SRCREQSEL) & SRCREQSEL_MASK); + reg = sconfig->src_addr; + + + for (period_index = 0; period_index < buf_len; + period_index += period_len) { + struct v5_desc *desc; + u32 len; + phys_addr_t mem; + + desc = v5_desc_get(v5chan); + if (!desc) + goto err_desc_get; + + mem = buf_addr + period_index; + + if (v5dma->io_regs) + mem |= IOCP_MASK; + + if ((buf_len - period_index) > period_len) + len = period_len; + else + len = buf_len - period_index; + + if (unlikely(!len)) { + dev_dbg(chan2dev(chan), + "sg(%d) data length is zero\n", i); + goto err; + } + + if (unlikely(mem & 3 || len & 3)) { + dev_dbg(chan2dev(chan), + "sg(%d) data length is not aligned\n", i); + goto err; + } + + desc->lli.srcAddrl = lower_32_bits(reg); + desc->lli.dstAddrl = lower_32_bits(mem); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + desc->lli.srcAddrh = upper_32_bits(reg); + desc->lli.dstAddrh = upper_32_bits(mem); +#endif + desc->lli.ctrl = ctrl; + desc->lli.tranSize = (len >> reg_width); + + desc->cyclic = true; + desc->num_sg = (buf_len + period_len - 1) / period_len; + desc->len = len; + desc->total_len = buf_len - period_index; + v5_desc_chain(&first, &prev, desc); + total_len += len; + } + break; + default: + return NULL; + } + + first->txd.flags = flags; + + return &first->txd; + +err_desc_get: + dev_err(chan2dev(chan), "Not enough descriptors available\n"); +err: + v5_desc_put(v5chan, first); + + + return NULL; +} + static int v5_config(struct dma_chan *chan, struct dma_slave_config *sconfig) { @@ -611,8 +848,6 @@ static int v5_config(struct dma_chan *chan, static int v5_terminate_all(struct dma_chan *chan) { struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); - struct v5_desc *desc, *_desc; - unsigned long flags; LIST_HEAD(list); dev_vdbg(chan2dev(chan), "%s\n", __func__); @@ -621,23 +856,18 @@ static int v5_terminate_all(struct dma_chan *chan) * we don't really care about the data. Just disable the * channel. */ - spin_lock_irqsave(&v5chan->lock, flags); /* disabling channel: must also remove suspend state */ + v5_abort_channel(v5chan); v5_dis_channel(v5chan); /* confirm that this channel is disabled */ while (v5_chan_is_enabled(v5chan)) cpu_relax(); /* active_list entries will end up before queued entries */ - list_splice_init(&v5chan->queue, &list); - list_splice_init(&v5chan->active_list, &list); - - /* Flush all pending and queued descriptors */ - list_for_each_entry_safe(desc, _desc, &list, desc_node) - v5_chain_complete(v5chan, desc, DMA_TRANS_NOERROR); + list_splice_init(&v5chan->queue, &v5chan->free_list); + list_splice_init(&v5chan->active_list, &v5chan->free_list); clear_bit(V5_IS_PAUSED, &v5chan->status); - spin_unlock_irqrestore(&v5chan->lock, flags); return 0; } @@ -653,25 +883,38 @@ v5_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct dma_tx_state *txstate) { - struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); - enum dma_status ret; - - if (test_and_clear_bit(V5_IS_TC, &v5chan->status)) - return DMA_COMPLETE; - - if (test_and_clear_bit(V5_IS_ERR, &v5chan->status)) - return DMA_ERROR; + struct v5_dma_chan *v5chan = to_v5_dma_chan(chan); + struct v5_desc *v5desc = NULL; + enum dma_status ret; + size_t residue = 0; ret = dma_cookie_status(chan, cookie, txstate); if (ret == DMA_COMPLETE) return ret; + /* * There's no point calculating the residue if there's * no txstate to store the value. */ if (!txstate) - return DMA_ERROR; + return ret; + + v5desc = v5_first_active(v5chan); + if (v5desc) { + if (v5desc->num_sg == 1) { + residue = v5desc->total_len; + } else { + struct v5_desc *v5desc_cur = NULL; + + if ((uintptr_t)v5desc->at == (uintptr_t)&v5desc->tx_list) + v5desc_cur = v5desc; + else + v5desc_cur = list_entry(v5desc->at, struct v5_desc, desc_node); + residue = v5desc_cur->total_len; + } + } + dma_set_residue(txstate, residue); return ret; } @@ -874,9 +1117,12 @@ static int __init v5_dma_probe(struct platform_device *pdev) int err; int i; const struct v5_dma_platform_data *plat_dat; + const __be32 *prop; + int len; dma_cap_set(DMA_SLAVE, v5dma_config.cap_mask); dma_cap_set(DMA_MEMCPY, v5dma_config.cap_mask); + dma_cap_set(DMA_CYCLIC, v5dma_config.cap_mask); /* get DMA parameters from controller type */ plat_dat = v5_dma_get_driver_data(pdev); @@ -897,6 +1143,16 @@ static int __init v5_dma_probe(struct platform_device *pdev) if (!v5dma) return -ENOMEM; + v5dma->io_regs = 0; + if (dev_is_dma_coherent(&pdev->dev)) { + u64 taddr; + + prop = of_get_property(pdev->dev.of_node, "iocp-address", &len); + if (prop) { + taddr = of_translate_address(pdev->dev.of_node, prop); + v5dma->io_regs = ioremap(taddr, 0x10); + } + } /* discover transaction capabilities */ v5dma->dma_common.cap_mask = plat_dat->cap_mask; v5dma->all_chan_mask = (1 << plat_dat->nr_channels) - 1; @@ -926,7 +1182,8 @@ static int __init v5_dma_probe(struct platform_device *pdev) /* create a pool of consistent memory blocks for hardware descriptors */ v5dma->dma_desc_pool = dma_pool_create("v5_desc_pool", &pdev->dev, sizeof(struct v5_desc), - 4 /* word alignment */, 0); + 64 /* 8 bytes aligned */, 4096); + if (!v5dma->dma_desc_pool) { dev_err(&pdev->dev, "No memory for descriptors dma pool\n"); err = -ENOMEM; @@ -950,6 +1207,8 @@ static int __init v5_dma_probe(struct platform_device *pdev) tasklet_init(&v5chan->tasklet, v5_tasklet, (unsigned long)v5chan); v5_enable_chan_irq(v5dma, i); + v5chan->device = v5dma; + v5chan->chan_id = i; } /* set base routines */ v5dma->dma_common.device_alloc_chan_resources = v5_alloc_chan_resources; @@ -960,6 +1219,10 @@ static int __init v5_dma_probe(struct platform_device *pdev) if (dma_has_cap(DMA_MEMCPY, v5dma->dma_common.cap_mask)) v5dma->dma_common.device_prep_dma_memcpy = v5_prep_dma_memcpy; + if (dma_has_cap(DMA_CYCLIC, v5dma->dma_common.cap_mask)) { + v5dma->dma_common.device_prep_dma_cyclic = v5_prep_dma_cyclic; + } + if (dma_has_cap(DMA_SLAVE, v5dma->dma_common.cap_mask)) { v5dma->dma_common.device_prep_slave_sg = v5_prep_slave_sg; v5dma->dma_common.device_config = v5_config; @@ -977,6 +1240,11 @@ static int __init v5_dma_probe(struct platform_device *pdev) v5dma->ch); dma_async_device_register(&v5dma->dma_common); + if (v5dma->io_regs) { + v5_dma_soc_writel(v5dma, CACHE_CTRL, IOCP_CACHE_DMAC0_AW | + IOCP_CACHE_DMAC0_AR | IOCP_CACHE_DMAC1_AW | + IOCP_CACHE_DMAC1_AW); + } /* * Do not return an error if the dmac node is not present in order to * not break the existing way of requesting channel with diff --git a/drivers/dma/atcdmac300g.h b/drivers/dma/atcdmac300g.h index 849880ff40838c..a5d876f7c57d42 100644 --- a/drivers/dma/atcdmac300g.h +++ b/drivers/dma/atcdmac300g.h @@ -148,6 +148,13 @@ static inline void clrbl(addr_t bit, void __iomem *reg) /* Channel Enable */ #define CHEN BIT(0) +#define CACHE_CTRL 0x184 +#define IOCP_MASK 0x4000000000 +#define IOCP_CACHE_DMAC0_AW 0xF +#define IOCP_CACHE_DMAC0_AR 0xF0 +#define IOCP_CACHE_DMAC1_AW 0xF00 +#define IOCP_CACHE_DMAC1_AR 0xF000 + /* Channel n Transfer Size Register */ /* total transfer size from source */ #define DMAC_TOT_SIZE_MASK 0xffffffff @@ -162,7 +169,7 @@ struct v5_lli { u32 llPointerh; }; -/** +/* * struct v5_desc - software descriptor * @v5_lli: hardware lli structure * @txd: support for the async_tx api @@ -172,17 +179,26 @@ struct v5_lli { */ struct v5_desc { /* FIRST values the hardware uses */ - struct v5_lli lli; + struct v5_lli lli; + /* THEN values for driver housekeeping */ - struct list_head tx_list; - struct dma_async_tx_descriptor txd; - struct list_head desc_node; - size_t len; - size_t total_len; + struct list_head tx_list; + struct dma_async_tx_descriptor txd; + struct list_head desc_node; + struct list_head *at; + size_t len; + + /* Total_len indicates that the remaining data in the descriptor + * has not been transferred in cyclic mode. + */ + size_t total_len; + /* Interleaved data */ - size_t boundary; - size_t dst_hole; - size_t src_hole; + size_t boundary; + size_t dst_hole; + size_t src_hole; + bool cyclic; + int num_sg; }; static inline struct v5_desc * @@ -203,7 +219,7 @@ enum v5_status { V5_IS_CYCLIC = 24, }; -/** +/* * struct v5_dma_chan - internal representation of an channel * @chan_common: common dmaengine channel object members * @device: parent device @@ -232,11 +248,13 @@ struct v5_dma_chan { struct tasklet_struct tasklet; struct dma_slave_config dma_sconfig; spinlock_t lock; + /* these other elements are all protected by lock */ - struct list_head active_list; - struct list_head queue; - struct list_head free_list; - unsigned int descs_allocated; + struct list_head active_list; + struct list_head queue; + struct list_head free_list; + unsigned int descs_allocated; + int chan_id; }; #define v5_channel_readl(v5chan, name) \ @@ -250,15 +268,12 @@ static inline struct v5_dma_chan *to_v5_dma_chan(struct dma_chan *dchan) return container_of(dchan, struct v5_dma_chan, chan_common); } -/* - * Note fls(0) = 0, fls(1) = 1, fls(0x8) = 4. - */ +/* Note fls(0) = 0, fls(1) = 1, fls(0x8) = 4. */ static inline void convert_burst(u32 *maxburst) { *maxburst = fls(*maxburst) - 1; } - /* * Fix sconfig's bus width * 1 byte -> 0, 2 bytes -> 1, 4 bytes -> 2. @@ -287,6 +302,7 @@ static inline u8 convert_buswidth(enum dma_slave_buswidth addr_width) struct v5_dma { struct dma_device dma_common; void __iomem *regs; + void __iomem *io_regs; u32 ctl; u8 data_width; u8 ch; @@ -299,6 +315,10 @@ struct v5_dma { __raw_readl((v5_dma)->regs + name) #define v5_dma_writel(v5_dma, name, val) \ __raw_writel((val), (v5_dma)->regs + name) +#define v5_dma_soc_readl(v5_dma, name) \ + __raw_readl((v5_dma)->io_regs + name) +#define v5_dma_soc_writel(v5_dma, name, val) \ + __raw_writel((val), (v5_dma)->io_regs + name) static inline struct v5_dma *to_v5_dma(struct dma_device *ddev) { From c469912b019d3290b86b7422b1a09fd364991f22 Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Tue, 2 May 2023 17:37:56 +0800 Subject: [PATCH 036/169] soc: andes: atcdmac300: Support for legacy DMA engine 1. Support for legacy DMA engine 2. Update Kconfig to enable the DMA engine driver 3. Combine the two configurations CONFIG_ATCDMAC300 and CONFIG_PLATFORM_AHBDMA into CONFIG_ATCDMAC300 to avoid compilation errors when only CONFIG_PLATFORM_AHBDMA is enabled. 4. Support for audio playback through legacy DMAC. signed-off-by: CL Wang --- arch/riscv/configs/andes-support.config | 3 + drivers/soc/andes/Kconfig | 9 + drivers/soc/andes/Makefile | 5 +- drivers/soc/andes/atcdmac300.c | 2456 +++++++++++++++++++++++ drivers/soc/andes/dmad_intc.c | 47 + include/soc/andes/atcdmac300.h | 537 +++++ include/soc/andes/dmad.h | 74 + 7 files changed, 3129 insertions(+), 2 deletions(-) create mode 100644 drivers/soc/andes/atcdmac300.c create mode 100644 drivers/soc/andes/dmad_intc.c create mode 100644 include/soc/andes/atcdmac300.h create mode 100644 include/soc/andes/dmad.h diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 140b06592a78f8..842d2d99bc8f72 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -7,7 +7,10 @@ CONFIG_ANDES_PPMA=y CONFIG_FTMAC100=y CONFIG_MMC_FTSDCG=y +CONFIG_MMC_FTSDC=y CONFIG_ATCDMAC300G=y +CONFIG_ATCDMAC300=y +CONFIG_PLATFORM_AHBDMA=y CONFIG_RISCV_ISA_ZICBOM=y # CONFIG_RISCV_PMU is not set diff --git a/drivers/soc/andes/Kconfig b/drivers/soc/andes/Kconfig index cb3b61364419f5..65038787976605 100644 --- a/drivers/soc/andes/Kconfig +++ b/drivers/soc/andes/Kconfig @@ -27,4 +27,13 @@ config ANDES_PPMA When calling memremap with MEMREMAP_WC flag, Andes programmable physical memory attributes (PPMA) can map memory as non-cacheable bufferable PMA region. + +config ATCDMAC300 + tristate "Andes legacy DMA support" + select PLATFORM_AHBDMA + +config PLATFORM_AHBDMA + bool "Platform AHB DMA support" + depends on ATCDMAC300 + endmenu diff --git a/drivers/soc/andes/Makefile b/drivers/soc/andes/Makefile index d45b9594db09f3..66e90c83c48c01 100644 --- a/drivers/soc/andes/Makefile +++ b/drivers/soc/andes/Makefile @@ -3,7 +3,8 @@ obj-y += andes_sbi.o obj-y += inject_init.o -obj-$(CONFIG_ANDES_CACHE) += cache.o +obj-$(CONFIG_ATCDMAC300) += atcdmac300.o dmad_intc.o +obj-$(CONFIG_ANDES_CACHE) += cache.o obj-$(CONFIG_ANDES_DMA_NONCOHERENT) += dma-noncoherent.o -obj-$(CONFIG_ANDES_PPMA) += ppma.o +obj-$(CONFIG_ANDES_PPMA) += ppma.o diff --git a/drivers/soc/andes/atcdmac300.c b/drivers/soc/andes/atcdmac300.c new file mode 100644 index 00000000000000..1089938e3943ad --- /dev/null +++ b/drivers/soc/andes/atcdmac300.c @@ -0,0 +1,2456 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Andes Technology Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +resource_size_t dmac_base; +struct at_dma_platform_data *pdata; + +static inline addr_t REG_READ(unsigned long r) +{ + return readl((void __iomem *)(unsigned long)r); +} + +static inline void REG_WRITE(addr_t d, unsigned long r) +{ + writel(d, (void __iomem *)(unsigned long)r); +} + +#if (defined(CONFIG_PLATFORM_AHBDMA)) +#define DMAD_AHB_MAX_CHANNELS DMAC_MAX_CHANNELS + +#define DMAD_DRB_POOL_SIZE 32 /* 128 */ + +static inline addr_t din(unsigned long r) +{ + return REG_READ(r); +} + +static inline void dout(addr_t d, unsigned long r) +{ + REG_WRITE(d, r); +} + +/* reg/io supplementals */ + +static void setbl(addr_t bit, unsigned long reg) +{ + REG_WRITE(REG_READ(reg) | (addr_t) ((addr_t) 1 << bit), reg); +} + +static inline void clrbl(addr_t bit, unsigned long reg) +{ + REG_WRITE(REG_READ(reg) & (~((addr_t) ((addr_t) 1 << bit))), reg); +} + +static inline addr_t getbl(addr_t bit, unsigned long reg) +{ + return REG_READ(reg) & (addr_t) ((addr_t) 1 << bit); +} + +/******************************************************************************/ + +enum DMAD_DRQ_FLAGS { + DMAD_DRQ_STATE_READY = 0x00000001, /* channel allocation status */ + DMAD_DRQ_STATE_ABORT = 0x00000002, /* abort drb alloc block-wait */ + DMAD_DRQ_DIR_A1_TO_A0 = 0x00000004, /* Transfer direction */ +}; + +#define DMAD_DRQ_DIR_MASK DMAD_DRQ_DIR_A1_TO_A0 + +/* DMA request queue, one instance per channel */ +typedef struct dmad_drq { + u32 state; /* enum DMAD_DRQ_STATE */ + + unsigned long channel_base; /* register base address */ + unsigned long enable_port; /* enable register */ + unsigned long src_port; /* source address register */ + unsigned long dst_port; /* dest address register */ + unsigned long cyc_port; /* size(cycle) register */ + + u32 flags; /* enum DMAD_CHREQ_FLAGS */ + + spinlock_t drb_pool_lock; + dmad_drb *drb_pool; /* drb pool */ + + unsigned long fre_head; /* free list head */ + unsigned long fre_tail; /* free list tail */ + + unsigned long rdy_head; /* ready list head */ + unsigned long rdy_tail; /* ready list tail */ + + unsigned long sbt_head; /* submitted list head */ + unsigned long sbt_tail; /* submitted list tail */ + + u32 data_width; /* dma transfer data width */ + + struct completion drb_alloc_sync; + + /* client supplied callback function, executed in interrupt context + * client private data to be passed to data argument of completion_cb(). + */ + void (*completion_cb)(int channel, u16 status, void *data); + void *completion_data; + + /* ring-mode fields are valid for DMAD_FLAGS_RING_MODE */ + dma_addr_t ring_base; /* ring buffer base address */ + int ring_size; /* size (of data width) */ + unsigned long ring_port; /* for setup/fetch hw_ptr */ + dmad_drb *ring_drb; + + addr_t dev_addr; /* device data port */ + + int periods; /* interrupts periods */ + int period_size; /* of dma data with */ + dma_addr_t period_bytes; /* Period size, in bytes */ + + /* ring_size - period_size * periods */ + dma_addr_t remnant_size; + + dma_addr_t sw_ptr; /* sw pointer */ + int sw_p_idx; /* current ring_ptr */ + dma_addr_t sw_p_off; /* offset to period base */ + +} dmad_drq; + +static inline void dmad_enable_channel(dmad_drq *drq) +{ + setbl(CHEN, drq->enable_port); +} + +static inline void dmad_disable_channel(dmad_drq *drq) +{ + clrbl(CHEN, drq->enable_port); +} + +static inline addr_t dmad_is_channel_enabled(dmad_drq *drq) +{ + return (addr_t) getbl(CHEN, drq->enable_port); +} + +/* system irq number (per channel, ahb) */ +static const unsigned int ahb_irqs[DMAD_AHB_MAX_CHANNELS] = { + DMA_IRQ0, DMA_IRQ1, DMA_IRQ2, DMA_IRQ3, + DMA_IRQ4, DMA_IRQ5, DMA_IRQ6, DMA_IRQ7, +}; + +/* Driver data structure, one instance per system */ +typedef struct DMAD_DATA_STRUCT { + /* Driver data initialization flag */ + + /* DMA queue pool access control object */ + spinlock_t drq_pool_lock; + + /* DMA queue base address, to ease alloc/free flow */ + dmad_drq *drq_pool; + /* DMA queue for AHB DMA channels */ + dmad_drq *ahb_drq_pool; + void *plat; +} DMAD_DATA; + +/* Driver data structure instance, one instance per system */ + +static DMAD_DATA dmad __aligned(8) = { + + .drq_pool_lock = __SPIN_LOCK_UNLOCKED(dmad.drq_pool_lock), + .drq_pool = 0, + .ahb_drq_pool = 0, + .plat = 0, +}; + +/** + * dmad_next_drb - static function + * @drb_pool : [in] The raw DRB pool of a DMA channel + * @node : [in] The node number to lookup its next node + * @drb : [out] The drb next to the "node" node number + * + * Lookup next DRB of the specified node number. "drb" is null if reaches end + * of the list. + */ +static inline void dmad_next_drb(dmad_drb *drb_pool, u32 node, dmad_drb **drb) +{ + if (likely(drb_pool[node].next != 0)) + *drb = &drb_pool[drb_pool[node].next]; + else + *drb = 0; +} + +/** + * dmad_prev_drb - static function + * @drb_pool : [in] The raw DRB pool of a DMA channel + * @node : [in] The node number to lookup its previous node + * @drb : [out] The drb previous to the "node" node number + * + * Lookup previous DRB of the specified node number. "drb" is null if reaches + * head-end of the list. + */ +static inline void dmad_prev_drb(dmad_drb *drb_pool, u32 node, dmad_drb **drb) +{ + if (unlikely(drb_pool[node].prev != 0)) + *drb = &drb_pool[drb_pool[node].prev]; + else + *drb = 0; +} + +/** + * dmad_detach_node - static function + * @drb_pool : [in] The raw DRB pool of a DMA channel + * @head : [in/out] Reference to the head node number + * @tail : [in/out] Reference to the tail node number + * @node : [in] The node to be dettached from the queue + * + * Detached a DRB specified by the node number from the queue. The head and + * tail records will be updated accordingly. + */ +static inline void dmad_detach_node(dmad_drb *drb_pool, unsigned long *head, + unsigned long *tail, u32 node) +{ + if (likely(drb_pool[node].prev != 0)) { + /* prev->next = this->next (= 0, if this is a tail) */ + drb_pool[drb_pool[node].prev].next = drb_pool[node].next; + } else { + /* this node is head, move head to next node + * (= 0, if this is the only one node) + */ + *head = drb_pool[node].next; + } + + if (unlikely(drb_pool[node].next != 0)) { + /* next->prev = this->prev (= 0, if this is a head) */ + drb_pool[drb_pool[node].next].prev = drb_pool[node].prev; + } else { + /* this node is tail, move tail to previous node + * (= 0, if this is the only one node) + */ + *tail = drb_pool[node].prev; + } + + drb_pool[node].prev = drb_pool[node].next = 0; +} + +/** + * dmad_detach_head - static function + * @drb_pool : [in] The raw DRB pool of a DMA channel + * @head : [in/out] Reference to the head node number + * @tail : [in/out] Reference to the tail node number + * @drb : [out] The detached head node; null if the queue is empty + * + * Detached a DRB from the head of the queue. The head and tail records will + * be updated accordingly. + */ +static inline void dmad_detach_head(dmad_drb *drb_pool, unsigned long *head, + unsigned long *tail, dmad_drb **drb) +{ + if (unlikely(*head == 0)) { + *drb = NULL; + return; + } + + *drb = &drb_pool[*head]; + + if (likely((*drb)->next != 0)) { + /* next->prev = this->prev (= 0, if this is a head) */ + drb_pool[(*drb)->next].prev = 0; + + /* prev->next = this->next (do nothing, if this is a head) */ + + /* head = this->next */ + *head = (*drb)->next; + } else { + /* head = tail = 0 */ + *head = 0; + *tail = 0; + } + + /* this->prev = this->next = 0 (do nothing, if save code size) */ + (*drb)->prev = (*drb)->next = 0; +} + +/** + * dmad_get_head - static function + * @drb_pool : [in] The raw DRB pool of a DMA channel + * @head : [in/out] Reference to the head node number + * @tail : [in/out] Reference to the tail node number + * @drb : [out] The head node; null if the queue is empty + * + * Get a DRB from the head of the queue. The head and tail records remain + * unchanged. + */ +static inline void dmad_get_head(dmad_drb *drb_pool, const unsigned long *head, + const unsigned long *tail, dmad_drb **drb) +{ + if (unlikely(*head == 0)) { + *drb = NULL; + return; + } + + *drb = &drb_pool[*head]; +} + +/** + * dmad_detach_tail - static function + * @drb_pool : [in] The raw DRB pool of a DMA channel + * @head : [in/out] Reference to the head node number + * @tail : [in/out] Reference to the tail node number + * @drb : [out] The tail node; null if the queue is empty + * + * Detached a DRB from the head of the queue. The head and tail records will + * be updated accordingly. + */ +static inline void dmad_detach_tail(dmad_drb *drb_pool, unsigned long *head, + unsigned long *tail, dmad_drb **drb) +{ + if (unlikely(*tail == 0)) { + *drb = NULL; + return; + } + + *drb = &drb_pool[*tail]; + + if (likely((*drb)->prev != 0)) { + /* prev->next = this->next (= 0, if this is a tail) */ + drb_pool[(*drb)->prev].next = 0; + + /* next->prev = this->prev (do nothing, if this is a tail) */ + + /* tail = this->prev */ + *tail = (*drb)->prev; + } else { + /* head = tail = 0 */ + *head = 0; + *tail = 0; + } + + /* this->next = this->prev = 0 (do nothing, if save code size) */ + (*drb)->prev = (*drb)->next = 0; +} + +/** + * dmad_get_tail - static function + * @drb_pool : [in] The raw DRB pool of a DMA channel + * @head : [in/out] Reference to the head node number + * @tail : [in/out] Reference to the tail node number + * @drb : [out] The tail node; null if the queue is empty + * + * Get a DRB from the tail of the queue. The head and tail records remain + * unchanged. + */ +static inline void dmad_get_tail(dmad_drb *drb_pool, unsigned long *head, + unsigned long *tail, dmad_drb **drb) +{ + if (unlikely(*tail == 0)) { + *drb = NULL; + return; + } + + *drb = &drb_pool[*tail]; +} + +/** + * dmad_attach_head - static function + * @drb_pool : [in] The raw DRB pool of a DMA channel + * @head : [in/out] Reference to the head node number + * @tail : [in/out] Reference to the tail node number + * @node : [in] The node to be attached + * + * Attach a DRB node to the head of the queue. The head and tail records will + * be updated accordingly. + */ +static inline void dmad_attach_head(dmad_drb *drb_pool, unsigned long *head, + unsigned long *tail, u32 node) +{ + if (likely(*head != 0)) { + /* head->prev = this */ + drb_pool[*head].prev = node; + + /* this->next = head */ + drb_pool[node].next = *head; + /* this->prev = 0 */ + drb_pool[node].prev = 0; + + /* head = node */ + *head = node; + } else { + /* head = tail = node */ + *head = *tail = node; + drb_pool[node].prev = drb_pool[node].next = 0; + } +} + +/** + * dmad_attach_head - static function + * @drb_pool : [in] The raw DRB pool of a DMA channel + * @head : [in/out] Reference to the head node number + * @tail : [in/out] Reference to the tail node number + * @node : [in] The node to be attached + * + * Attach a DRB node to the tail of the queue. The head and tail records will + * be updated accordingly. + */ +static inline void dmad_attach_tail(dmad_drb *drb_pool, unsigned long *head, + unsigned long *tail, u32 node) +{ + if (likely(*tail != 0)) { + /* tail->next = this */ + drb_pool[*tail].next = node; + + /* this->prev = tail */ + drb_pool[node].prev = *tail; + /* this->next = 0 */ + drb_pool[node].next = 0; + + /* tail = node */ + *tail = node; + } else { + /* head = tail = node */ + *head = *tail = node; + drb_pool[node].prev = drb_pool[node].next = 0; + } +} + +#ifdef CONFIG_PLATFORM_AHBDMA + +/** + * dmad_ahb_isr - AHB DMA interrupt service routine + * + * @irq : [in] The irq number + * @dev_id : [in] The identifier to identify the asserted channel + * + * This is the ISR that services all AHB DMA channels. + */ +static irqreturn_t dmad_ahb_isr(int irq, void *dev_id) +{ + dmad_drq *drq; + dmad_drb *drb, *drb_iter; + u32 channel = ((unsigned long)dev_id) - 1; + u8 tc_int = 0; + u8 err_int = 0; + u8 abt_int = 0; + u8 cpl_events = 1; + + dmad_dbg("%s() >> channel(%d)\n", __func__, channel); + + if (channel >= DMAD_AHB_MAX_CHANNELS) { + dmad_err("%s() invlaid channel number: %d!\n", __func__, + channel); + return IRQ_HANDLED; + } + + /* Fetch channel's DRQ struct (DMA Request Queue) */ + drq = (dmad_drq *) &dmad.ahb_drq_pool[channel]; + + /* Check DMA status register to get channel number */ + if (likely(getbl(channel + TC_OFFSET, (unsigned long)INT_STA))) { + /* Mark as TC int */ + tc_int = 1; + + /* DMAC INT TC status clear */ + setbl(channel + TC_OFFSET, (unsigned long)INT_STA); + + } else if (getbl(channel + ERR_OFFSET, (unsigned long)INT_STA)) { + /* Mark as ERR int */ + err_int = 1; + + /* DMAC INT ERR status clear */ + setbl(channel + ERR_OFFSET, (unsigned long)INT_STA); + + } else if (getbl(channel + ABT_OFFSET, (unsigned long)INT_STA)) { + /* Mark as ABT int */ + abt_int = 1; + + /* DMAC INT ABT status clear */ + setbl(channel + INT_STA, (unsigned long)INT_STA); + + } else { + dmad_err("%s() possible false-fired ahb dma int, channel %d status-reg: status(0x%08x)\n", + __func__, channel, din((unsigned long)INT_STA)); + + /* Stop DMA channel (make sure the channel will be stopped) */ + clrbl(CH_EN, drq->enable_port); + return IRQ_HANDLED; + } + + /* DMAC Stop DMA channel temporarily */ + dmad_disable_channel(drq); + + spin_lock(&drq->drb_pool_lock); + + /* Lookup/detach latest submitted DRB (DMA Request Block) from + * the DRQ (DMA Request Queue), so ISR could kick off next DRB + */ + dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb); + if (drb == NULL) { + spin_unlock(&drq->drb_pool_lock); + /* submitted list could be empty if client cancel all requests + * of the channel. + */ + return IRQ_HANDLED; + } + + /* release blocking of drb-allocation, if any ... */ + if (unlikely((drq->fre_head == 0) && + (drq->flags & DMAD_FLAGS_SLEEP_BLOCK))) { + complete_all(&drq->drb_alloc_sync); + } + + /* Process DRBs according to interrupt reason */ + if (tc_int) { + dmad_dbg("dma finish\n"); + dmad_dbg("finish drb(%d 0x%08x) addr0(0x%08llx) addr1 (0x%08llx) size(0x%08llx)\n", + drb->node, (u32) drb, drb->src_addr, drb->dst_addr, + drb->req_cycle); + + if (drb->req_cycle == 0) + cpl_events = 0; + + // Mark DRB state as completed + drb->state = DMAD_DRB_STATE_COMPLETED; + if (cpl_events && drb->sync) + complete_all(drb->sync); + + dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, + drb->node); + + // Check whether there are pending requests in the DRQ + if (drq->sbt_head != 0) { + // Lookup next DRB (DMA Request Block) + drb_iter = &drq->drb_pool[drq->sbt_head]; + + dmad_dbg("exec drb(%d 0x%08x) addr0(0x%08llx) addr1 (0x%08llx) size(0x%08llx)\n", + drb_iter->node, (u32) drb_iter, + drb_iter->src_addr, drb_iter->dst_addr, + drb_iter->req_cycle); + + // Kick-off DMA for next DRB + // - Source and destination address + if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) { + dout(drb_iter->addr1, + (unsigned long)drq->src_port); + dout(drb_iter->addr0, + (unsigned long)drq->dst_port); + } else { + dout(drb_iter->addr0, + (unsigned long)drq->src_port); + dout(drb_iter->addr1, + (unsigned long)drq->dst_port); + } + + /* - Transfer size (in units of source width) */ + dout(drb_iter->req_cycle, (unsigned long)drq->cyc_port); + + /* Kick off next request */ + dmad_enable_channel(drq); + + drb_iter->state = DMAD_DRB_STATE_EXECUTED; + + } else { + /* No pending requests, keep the DMA channel stopped */ + } + + } else { + dmad_err("%s() ahb dma channel %d error!\n", __func__, channel); + + /* Zero out src, dst, and size */ + dout(0, (unsigned long)drq->src_port); + dout(0, (unsigned long)drq->dst_port); + dout(0, (unsigned long)drq->cyc_port); + + /* Remove all pending requests in the queue */ + drb_iter = drb; + while (drb_iter) { + dmad_err("abort drb "); + + if (drb_iter->req_cycle == 0) + cpl_events = 0; + + /* Mark DRB state as abort */ + drb_iter->state = DMAD_DRB_STATE_ABORT; + + if (cpl_events && drb_iter->sync) + complete_all(drb_iter->sync); + + dmad_attach_tail(drq->drb_pool, &drq->fre_head, + &drq->fre_tail, drb_iter->node); + + /* Detach next submitted DRB (DMA Request Block) + * from the DRQ (DMA Request Queue) + */ + dmad_detach_head(drq->drb_pool, &drq->sbt_head, + &drq->sbt_tail, &drb_iter); + } + } + + spin_unlock(&drq->drb_pool_lock); + + /* dispatch interrupt-context level callbacks */ + if (cpl_events && drq->completion_cb) { + /* signal DMA driver that new node is available */ + drq->completion_cb(channel, tc_int, drq->completion_data); + } + + return IRQ_HANDLED; +} + +/** + * dmad_ahb_config_dir - prepare command reg according to tx direction + * @ch_req : [in] Reference to the DMA request descriptor structure + * @channel_cmds : [out] Reference to array of command words to be prepared with + * @return : none + * + * Prepare command registers according to transfer direction ... + * channel_cmd[0] DMAC_CSR + * channel_cmd[1] DMAC_CFG + * + * This function only serves as local helper. No protection wrappers. + */ +static void dmad_ahb_config_dir(dmad_chreq *ch_req, + unsigned long *channel_cmds) +{ + dmad_drq *drq = (dmad_drq *) ch_req->drq; + dmad_ahb_chreq *ahb_req = (dmad_ahb_chreq *) (&ch_req->ahb_req); + channel_control ch_ctl; + + dmad_dbg("%s() channel_cmds(0x%08lx)\n", __func__, channel_cmds[0]); + channel_cmds[0] &= ~(u32) (SRCWIDTH_MASK | SRCADDRCTRL_MASK | + DSTWIDTH_MASK | DSTADDRCTRL_MASK | SRC_HS | + DST_HS | SRCREQSEL_MASK | DSTREQSEL_MASK); + + if (ahb_req->tx_dir == 0) { + dmad_dbg("%s() addr0 --> addr1\n", __func__); + memcpy((u8 *) &ch_ctl.sWidth, (u8 *) &ahb_req->addr0_width, + 12); + memcpy((u8 *) &ch_ctl.dWidth, (u8 *) &ahb_req->addr1_width, + 12); + drq->flags &= ~(addr_t) DMAD_DRQ_DIR_A1_TO_A0; + } else { + dmad_dbg("%s() addr0 <-- addr1\n", __func__); + memcpy((u8 *) &ch_ctl.sWidth, (u8 *) &ahb_req->addr1_width, + 12); + memcpy((u8 *) &ch_ctl.dWidth, (u8 *) &ahb_req->addr0_width, + 12); + drq->flags |= (addr_t) DMAD_DRQ_DIR_A1_TO_A0; + } + + channel_cmds[0] |= (((ch_ctl.sWidth << SRCWIDTH) & SRCWIDTH_MASK) | + ((ch_ctl.sCtrl << SRCADDRCTRL) & SRCADDRCTRL_MASK) | + ((ch_ctl.dWidth << DSTWIDTH) & DSTWIDTH_MASK) | + ((ch_ctl.dCtrl << DSTADDRCTRL) & DSTADDRCTRL_MASK)); + drq->data_width = ch_ctl.sWidth; + if (likely(ahb_req->hw_handshake != 0)) { + if (ch_ctl.sReqn != DMAC_REQN_NONE) { + channel_cmds[0] |= + (SRC_HS | ((ch_ctl.sReqn << SRCREQSEL) & + SRCREQSEL_MASK)); + } + if (ch_ctl.dReqn != DMAC_REQN_NONE) { + channel_cmds[0] |= + (DST_HS | ((ch_ctl.dReqn << DSTREQSEL) & + DSTREQSEL_MASK)); + } + } + dmad_dbg("%s() channel_cmds(0x%08lx)\n", __func__, channel_cmds[0]); +} + +/** + * dmad_ahb_init - initialize a ahb dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : 0 if success, non-zero if any error + * + * Register AHB DMA ISR and performs hw initialization for the given DMA + * channel. + */ +static int dmad_ahb_init(dmad_chreq *ch_req) +{ + int err = 0; + dmad_drq *drq = (dmad_drq *) ch_req->drq; + dmad_ahb_chreq *ahb_req = (dmad_ahb_chreq *) (&ch_req->ahb_req); + u32 channel = (u32) ch_req->channel; + unsigned long channel_base = drq->channel_base; + addr_t channel_cmds[1]; + unsigned long lock_flags; + + /* register interrupt handler */ + err = request_irq(pdata->irqs[channel + 1], dmad_ahb_isr, 0, "AHB_DMA", + (void *)(unsigned long)(channel + 1)); + if (unlikely(err != 0)) { + dmad_err("unable to request IRQ %d for AHB DMA (error %d)\n", + ahb_irqs[channel], err); + return err; + } + spin_lock_irqsave(&dmad.drq_pool_lock, lock_flags); + + /* - INT TC/ERR/ABT status clear */ + setbl(channel + TC_OFFSET, (unsigned long)INT_STA); + setbl(channel + ABT_OFFSET, (unsigned long)INT_STA); + setbl(channel + ERR_OFFSET, (unsigned long)INT_STA); + + /* - SYNC */ + if (ahb_req->sync != (getbl(REQSYNC, CFG) >> REQSYNC)) { + dmad_err("sync configuration error !\n"); + return -EINVAL; + } + if (ahb_req->priority > PRIORITY_HIGH) + ahb_req->priority = PRIORITY_HIGH; + + channel_cmds[0] = (ahb_req->priority << PRIORITY_SHIFT); + channel_cmds[0] |= (ahb_req->burst_size << SBURST_SIZE_SHIFT) & + SBURST_SIZE_MASK; + + if (0 == + (ch_req->flags & (DMAD_FLAGS_RING_MODE | DMAD_FLAGS_BIDIRECTION))) + ahb_req->tx_dir = 0; + + dmad_ahb_config_dir(ch_req, (unsigned long *)channel_cmds); + dout(channel_cmds[0], (unsigned long)drq->enable_port); + + /* SRCADR and DESADR */ + dout(0, (unsigned long)drq->src_port); + dout(0, (unsigned long)drq->dst_port); + /* CYC (transfer size) */ + dout(0, (unsigned long)drq->cyc_port); + /* LLP */ + dout(0, (unsigned long)channel_base + CH_LLP_LOW_OFF); + + /* TOT_SIZE - not now */ + spin_unlock_irqrestore(&dmad.drq_pool_lock, lock_flags); + + return err; +} + +#endif /* CONFIG_PLATFORM_AHBDMA */ +/** + * dmad_channel_init - initialize given dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : 0 if success, non-zero if any error + * + * This function serves as the abstraction layer of dmad_ahb_init() + * and dmad_apb_init() functions. + */ +static int dmad_channel_init(dmad_chreq *ch_req) +{ + int err = 0; + + if (unlikely(ch_req == NULL)) + return -EFAULT; + + if (unlikely(ch_req->drq == NULL)) + return -EBADR; + + /* Initialize DMA controller */ + if (ch_req->controller == DMAD_DMAC_AHB_CORE) + err = dmad_ahb_init(ch_req); + return err; +} + +static inline void dmad_reset_channel(dmad_drq *drq) +{ + /* disable dma controller */ + dmad_disable_channel(drq); + + /* Source and destination address */ + dout(0, (unsigned long)drq->src_port); + dout(0, (unsigned long)drq->dst_port); + + /* Transfer size (in units of source width) */ + dout(0, (unsigned long)drq->cyc_port); +} + +/** + * dmad_channel_reset - reset given dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : 0 if success, non-zero if any error + * + * This function serves as the abstraction layer of dmad_ahb_reset() + * and dmad_apb_reset() functions. + */ +static int dmad_channel_reset(dmad_chreq *ch_req) +{ + u32 channel = (u32) ch_req->channel; + unsigned long lock_flags; + int err = 0; + + if (unlikely(ch_req == NULL)) + return -EFAULT; + + if (unlikely(ch_req->drq == NULL)) + return -EBADR; + + spin_lock_irqsave(&((dmad_drq *) ch_req->drq)->drb_pool_lock, + lock_flags); + + /* stop DMA channel */ + dmad_reset_channel((dmad_drq *) ch_req->drq); + + spin_unlock_irqrestore(&((dmad_drq *) ch_req->drq)->drb_pool_lock, + lock_flags); + + /* unregister interrupt handler */ + if (ch_req->controller == DMAD_DMAC_AHB_CORE) + free_irq(pdata->irqs[channel + 1], + (void *)(unsigned long)(channel + 1)); + + return err; +} + +/** + * dmad_channel_alloc - allocates and initialize a dma channel + * @ch_req : [in/out] Reference to the DMA request descriptor structure + * @return : 0 if success, non-zero if any error + * + * This function allocates a DMA channel according to client's request + * parameters. ISR and HW state will also be initialized accordingly. + */ +int dmad_channel_alloc(dmad_chreq *ch_req) +{ + dmad_drq *drq_iter = NULL; + dmad_drb *drb_iter; + int err = 0; + u32 i = 0; + + if (ch_req == NULL) { + dmad_err("%s() invalid argument!\n", __func__); + return -EFAULT; + } + + spin_lock(&dmad.drq_pool_lock); + + /* locate an available DMA channel */ + if (ch_req->controller == DMAD_DMAC_AHB_CORE) { + drq_iter = dmad.ahb_drq_pool; + + if ((ch_req->ahb_req.src_reqn != DMAC_REQN_NONE) || + (ch_req->ahb_req.dst_reqn != DMAC_REQN_NONE)) { + /* [2007-12-03] It looks current board have problem to + * do dma traffic for APB devices on DMAC channel 0/1. + * Redirect all APB devices to start from channel 2. + */ + + /* [todo] include USB controller ? */ + drq_iter = &dmad.ahb_drq_pool[2]; + for (i = 2; i < DMAD_AHB_MAX_CHANNELS; ++i, ++drq_iter) { + if (!(drq_iter->state & DMAD_DRQ_STATE_READY)) + break; + } + } else { + /* channel for other devices is free to allocate */ + for (i = 0; i < DMAD_AHB_MAX_CHANNELS; ++i, ++drq_iter) { + if (!(drq_iter->state & DMAD_DRQ_STATE_READY)) + break; + } + } + if (unlikely(i == DMAD_AHB_MAX_CHANNELS)) { + spin_unlock(&dmad.drq_pool_lock); + dmad_err("out of available channels (AHB DMAC)!\n"); + return -ENOSPC; + } + + dmad_dbg("allocated channel: %d (AHB DMAC)\n", i); + } + + if (drq_iter == NULL) { + spin_unlock(&dmad.drq_pool_lock); + dmad_err("%s() invalid argument!\n", __func__); + return -EFAULT; + } + + spin_unlock(&dmad.drq_pool_lock); + memset(drq_iter, 0, sizeof(dmad_drq)); + + /* Initialize DMA channel's DRB pool as list of free DRBs */ + drq_iter->drb_pool = + kmalloc_array(DMAD_DRB_POOL_SIZE, sizeof(dmad_drb), GFP_ATOMIC); + + if (drq_iter->drb_pool == NULL) { + dmad_err("%s() failed to allocate drb pool!\n", __func__); + return -ENOMEM; + } + + /* Allocate the DMA channel */ + drq_iter->state = DMAD_DRQ_STATE_READY; + drq_iter->flags = ch_req->flags; + + /* Initialize synchronization object for DMA queue access control */ + spin_lock_init(&drq_iter->drb_pool_lock); + + /* Initialize synchronization object for free drb notification */ + init_completion(&drq_iter->drb_alloc_sync); + + /* Record the channel number in client's struct */ + ch_req->channel = i; + /* Record the channel's queue handle in client's struct */ + ch_req->drq = drq_iter; + + if (ch_req->controller == DMAD_DMAC_AHB_CORE) { + drq_iter->channel_base = (unsigned long)DMAC_BASE_CH(i); + drq_iter->enable_port = (unsigned long)CH_CTL(i); + drq_iter->src_port = (unsigned long)CH_SRC_L(i); + drq_iter->dst_port = (unsigned long)CH_DST_L(i); + drq_iter->cyc_port = (unsigned long)CH_SIZE(i); + } + /* drb-0 is an invalid node - for node validation */ + drb_iter = &drq_iter->drb_pool[0]; + drb_iter->prev = 0; + drb_iter->next = 0; + drb_iter->node = 0; + ++drb_iter; + + /* init other drbs - link in order */ + for (i = 1; i < DMAD_DRB_POOL_SIZE; ++i, ++drb_iter) { + drb_iter->prev = i - 1; + drb_iter->next = i + 1; + drb_iter->node = i; + } + drq_iter->drb_pool[DMAD_DRB_POOL_SIZE - 1].next = 0; + + /* Initialize channel's DRB free-list, ready-list, and submitted-list */ + drq_iter->fre_head = 1; + drq_iter->fre_tail = DMAD_DRB_POOL_SIZE - 1; + drq_iter->rdy_head = drq_iter->rdy_tail = 0; + drq_iter->sbt_head = drq_iter->sbt_tail = 0; + + /* initialize ring buffer mode resources */ + if (ch_req->flags & DMAD_FLAGS_RING_MODE) { + int remnant = (int)ch_req->ring_size - + (int)ch_req->periods * (int)ch_req->period_size; + if (remnant == 0) { + drq_iter->periods = ch_req->periods; + } else if (remnant > 0) { + drq_iter->periods = ch_req->periods; // + 1; + } else { + dmad_err("%s() Error - buffer_size < periods * period _size!\n", + __func__); + err = -EFAULT; + goto _err_exit; + } + + drq_iter->ring_size = ch_req->ring_size; + drq_iter->period_size = ch_req->period_size; + drq_iter->remnant_size = (dma_addr_t) remnant; + + drq_iter->ring_base = (dma_addr_t) ch_req->ring_base; + drq_iter->dev_addr = (dma_addr_t) ch_req->dev_addr; + + if (ch_req->controller == DMAD_DMAC_AHB_CORE) { + if ((ch_req->ahb_req.ring_ctrl == DMAC_CSR_AD_DEC) || + (ch_req->ahb_req.dev_ctrl == DMAC_CSR_AD_DEC)) { + dmad_err("%s() Error - decremental address in DMA is not supported in ring mode currently!\n", + __func__); + err = -EFAULT; + goto _err_exit; + } + + if (ch_req->ahb_req.ring_ctrl == DMAC_CSR_AD_FIX) { + dmad_err("%s() Error - ring address control isfixed in ring DMA mode!\n", + __func__); + err = -EFAULT; + goto _err_exit; + } + + drq_iter->period_bytes = + DMAC_CYCLE_TO_BYTES(ch_req->period_size, + ch_req->ahb_req.ring_width); + + /* 0 - addr0 to addr1; 1 - addr1 to addr0 */ + if (ch_req->ahb_req.tx_dir == 0) + drq_iter->ring_port = + (unsigned long)drq_iter->src_port; + else + drq_iter->ring_port = + (unsigned long)drq_iter->dst_port; + } + + dmad_dbg + ("%s() ring: base(0x%08llx) port(0x%08lx) periods (0x%08x) period_size(0x%08x) period_bytes(0x%08llx)remnant_size(0x%08llx)\n", + __func__, drq_iter->ring_base, + drq_iter->ring_port, drq_iter->periods, + drq_iter->period_size, drq_iter->period_bytes, + drq_iter->remnant_size); + } + + drq_iter->completion_cb = ch_req->completion_cb; + drq_iter->completion_data = ch_req->completion_data; + + /* Initialize the channel && register isr */ + err = dmad_channel_init(ch_req); + +_err_exit: + + if (err != 0) { + spin_lock(&dmad.drq_pool_lock); + + kfree(drq_iter->drb_pool); + memset(drq_iter, 0, sizeof(dmad_drq)); + + ch_req->channel = -1; + ch_req->drq = (void *)0; + + spin_unlock(&dmad.drq_pool_lock); + + dmad_err("Failed to initialize APB DMA! Channel allocation aborted!\n"); + } + + return err; +} +EXPORT_SYMBOL_GPL(dmad_channel_alloc); + +/** + * dmad_channel_free - release a dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : 0 if success, non-zero if any error + * + * This function releases a DMA channel. The channel is available for future + * allocation after the invokation. + */ +int dmad_channel_free(dmad_chreq *ch_req) +{ + dmad_drq *drq; + + if (unlikely(ch_req == NULL)) { + dmad_err("null ch_req!\n"); + return -EFAULT; + } + + drq = (dmad_drq *) ch_req->drq; + + if (unlikely(drq == NULL)) { + dmad_err("null ch_req->drq!\n"); + return -EBADR; + } + if (unlikely((ch_req->channel < 0) || + ((drq->state & DMAD_DRQ_STATE_READY) == 0))) { + dmad_err("try to free a free channel!\n"); + return -EBADR; + } + + /* Stop/abort channel I/O + * (forced to shutdown and should be protected against isr) + */ + dmad_drain_requests(ch_req, 1); + dmad_channel_reset(ch_req); + + dmad_dbg("freed channel: %d\n", ch_req->channel); + + spin_lock(&dmad.drq_pool_lock); + + kfree(drq->drb_pool); + memset(drq, 0, sizeof(dmad_drq)); + + ch_req->drq = 0; + ch_req->channel = (u32) -1; + + spin_unlock(&dmad.drq_pool_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_channel_free); + +/** + * dmad_channel_enable - enable/disable a dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @enable : [in] 1 to enable the channel, 0 to disable + * @return : 0 if success, non-zero if any error + * + * Enable or disable the given DMA channel. + */ +int dmad_channel_enable(const dmad_chreq *ch_req, u8 enable) +{ + dmad_drq *drq; + unsigned long lock_flags; + + if (unlikely(ch_req == NULL)) + return -EFAULT; + + drq = (dmad_drq *) ch_req->drq; + + if (unlikely(drq == NULL)) + return -EBADR; + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + /* Enable/disable DMA channel */ + if (enable) + dmad_enable_channel(drq); + else + dmad_disable_channel(drq); + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_channel_enable); + +/** + * dmad_config_channel_dir - config dma channel transfer direction + * @ch_req : [in] Reference to the DMA request descriptor structure + * @dir : [in] DMAD_DRQ_DIR_A0_TO_A1 or DMAD_DRQ_DIR_A1_TO_A0 + * @return : 0 if success, non-zero if any error + * + * Reconfigure the channel transfer direction. This function works only if + * the channel was allocated with the DMAD_FLAGS_BIDIRECTION flags. Note + * that bi-direction mode and ring mode are mutual-exclusive from user's + * perspective. + */ +int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir) +{ + dmad_drq *drq; + addr_t channel_cmds[1]; + unsigned long lock_flags; + u8 cur_dir; + + if (unlikely(ch_req == NULL)) + return -EFAULT; + + drq = (dmad_drq *) ch_req->drq; + + if (unlikely(drq == NULL)) + return -EBADR; + + if (unlikely(!(ch_req->flags & DMAD_FLAGS_BIDIRECTION))) { + dmad_err("%s() Channel is not configured as bidirectional!\n", + __func__); + return -EFAULT; + } + + cur_dir = drq->flags & DMAD_DRQ_DIR_MASK; + if (dir == cur_dir) { + dmad_dbg("%s() cur_dir(%d) == dir(%d) skip reprogramming hw.\n", + __func__, cur_dir, dir); + return 0; + } + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + if (unlikely((drq->sbt_head != 0) /*||dmad_is_channel_enabled(drq) */)) { + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + dmad_err("%s() Cannot change direction while the channel has pending requests!\n", + __func__); + return -EFAULT; + } + + if (ch_req->controller == DMAD_DMAC_AHB_CORE) { + channel_cmds[0] = din((unsigned long)drq->enable_port); + ch_req->ahb_req.tx_dir = dir; + dmad_ahb_config_dir(ch_req, (unsigned long *)channel_cmds); + dout(channel_cmds[0], (unsigned long)drq->enable_port); + } + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_config_channel_dir); + +/** + * dmad_max_size_per_drb - return maximum transfer size per drb + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : The maximum transfer size per drb, in bytes. + * + * Calculate the maximum transfer size per drb according to the setting of + * data width during channel initialization. + * + * Return size is aligned to 4-byte boundary; this ensures the alignment + * requirement of dma starting address if the function was used in a loop to + * separate a large size dma transfer. + */ +u32 dmad_max_size_per_drb(dmad_chreq *ch_req) +{ + addr_t size = 0; + addr_t data_width = (addr_t) ((dmad_drq *) ch_req->drq)->data_width; + + if (ch_req->controller == DMAD_DMAC_AHB_CORE) { + size = DMAC_CYCLE_TO_BYTES(DMAC_TOT_SIZE_MASK & ((addr_t) ~3), + data_width); + } + + dmad_dbg("%s() - 0x%08x bytes\n", __func__, size); + + return size; +} +EXPORT_SYMBOL_GPL(dmad_max_size_per_drb); + +/** + * dmad_bytes_to_cycles - calculate drb transfer size, in cycles + * @ch_req : [in] Reference to the DMA request descriptor structure + * @byte_size : [in] The DMA transfer size to be converted, in bytes + * @return : The drb transfer size, in cycles. + * + * Calculate the drb transfer cycle according to the setting of channel data + * width and burst setting. + * + * AHB DMA : unit is number of "data width". + * APB DMA : unit is number of "data width * burst size" + * + * APB Note: According to specification, decrement addressing seems to regard + * the burst size setting. For code efficiency, + * dmad_make_req_cycles() does not take care of this case and might + * produce wrong result. + */ +u32 dmad_bytes_to_cycles(dmad_chreq *ch_req, u32 byte_size) +{ + addr_t cycle = 0; + addr_t data_width = (addr_t) ((dmad_drq *) ch_req->drq)->data_width; + + if (ch_req->controller == DMAD_DMAC_AHB_CORE) + cycle = DMAC_BYTES_TO_CYCLE(byte_size, data_width); + + dmad_dbg("%s() - 0x%08x bytes --> 0x%08x cycles\n", __func__, byte_size, + cycle); + return cycle; +} +EXPORT_SYMBOL_GPL(dmad_bytes_to_cycles); + +/** + * dmad_alloc_drb_internal - allocate a dma-request-block of a dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @drb : [out] Reference to a drb pointer to receive the allocated drb + * @return : 0 if success, non-zero if any error + * + * Allocates a DRB (DMA request block) of the given DMA channel. DRB is a + * single dma request which will be pushed into the submission queue of the + * given DMA channel. This is a lightweight internal version of + * dmad_alloc_drb() majorly for use in ring mode. Critical access to the + * drb pool should be protected before entering this function. + */ +static inline int dmad_alloc_drb_internal(dmad_drq *drq, dmad_drb **drb) +{ + /* Initialize drb ptr in case of fail allocation */ + *drb = NULL; + + if (unlikely(drq->fre_head == 0)) + return -EAGAIN; + + dmad_detach_head(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb); + + dmad_attach_tail(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, + (*drb)->node); + + (*drb)->state = DMAD_DRB_STATE_READY; + (*drb)->sync = 0; + + dmad_dbg("%s() drb(%d 0x%08x)\n", __func__, (*drb)->node, (u32) (*drb)); + + return 0; +} + +/** + * dmad_alloc_drb - allocate a dma-request-block of a dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @drb : [out] Reference to a drb pointer to receive the allocated drb + * @return : 0 if success, non-zero if any error + * + * Allocates a DRB (DMA request block) of the given DMA channel. DRB is a + * single dma request which will be pushed into the submission queue of the + * given DMA channel. + */ +int dmad_alloc_drb(dmad_chreq *ch_req, dmad_drb **drb) +{ + dmad_drq *drq; + unsigned long lock_flags; + + if (unlikely(ch_req == NULL)) { + dmad_err("null ch_req!\n"); + return -EFAULT; + } + + drq = (dmad_drq *) ch_req->drq; + + if (likely(drq == NULL)) { + dmad_err("null ch_req->drq!\n"); + return -EBADR; + } + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + /* Initialize drb ptr in case of fail allocation */ + *drb = NULL; + + if (unlikely(drq->fre_head == 0)) { + drq->state &= (u32) ~DMAD_DRQ_STATE_ABORT; + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + +_wait_for_free_drbs: + + /* Wait for free urbs */ + if (drq->flags & DMAD_FLAGS_SLEEP_BLOCK) { + int timeout = + wait_for_completion_interruptible_timeout( + &drq->drb_alloc_sync, + msecs_to_jiffies(6000)); + + /* reset sync object */ + reinit_completion(&drq->drb_alloc_sync); + + if (timeout < 0) { + dmad_err("%s() wait for completion error! (%d)\n", + __func__, timeout); + return timeout; + } + + } else if (drq->flags & DMAD_FLAGS_SPIN_BLOCK) { + u32 timeout = 0x00ffffff; + + while ((drq->fre_head == 0) && (--timeout != 0)) + ; + + if (timeout == 0) { + dmad_err("%s() polling wait for completion timeout!\n", + __func__); + return -EAGAIN; + } + + } else { + return -EAGAIN; + } + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + /* check whether all the requests of the channel has been + * abandoned or not + */ + if (unlikely(drq->state & DMAD_DRQ_STATE_ABORT)) { + dmad_dbg("%s() drb-allocation aborted due to cancel-request ...\n", + __func__); + drq->state &= (u32) ~DMAD_DRQ_STATE_ABORT; + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -ECANCELED; + } + + /* check again to avoid non-atomic operation between above + * two calls + */ + if (unlikely(drq->fre_head == 0)) { + dmad_dbg("%s() lost free drbs ... continue waiting...\n", + __func__); + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + goto _wait_for_free_drbs; + } + } + + dmad_detach_head(drq->drb_pool, &drq->fre_head, &drq->fre_tail, drb); + + dmad_attach_tail(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, + (*drb)->node); + + (*drb)->state = DMAD_DRB_STATE_READY; + (*drb)->sync = 0; + + dmad_dbg("%s() drb(%d 0x%08x)\n", __func__, (*drb)->node, (u32) (*drb)); + + drq->state &= (u32) ~DMAD_DRQ_STATE_ABORT; + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_alloc_drb); + +/** + * dmad_free_drb - free a dma-request-block of a dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @drb : [in] Reference to a drb to be freed + * @return : 0 if success, non-zero if any error + * + * Frees a DRB (DMA request block) of the given DMA channel. DRB is a + * single dma request which will be pushed into the submission queue of the + * given DMA channel. + */ +int dmad_free_drb(dmad_chreq *ch_req, dmad_drb *drb) +{ + dmad_drq *drq; + unsigned long lock_flags; + + if (unlikely(ch_req == NULL)) { + dmad_err("null ch_req!\n"); + return -EFAULT; + } + + drq = (dmad_drq *) ch_req->drq; + + if (unlikely(drq == NULL)) { + dmad_err("null ch_req->drq!\n"); + return -EBADR; + } + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + /**************************************************** + * Following code requires _safe_exit return path + */ + + if (unlikely((drq->rdy_head == 0) || (drb->node == 0) || + (drb->state != DMAD_DRB_STATE_READY) || + (drb->node >= DMAD_DRB_POOL_SIZE))) { + dmad_err("Ready-queue is empty or invalid node!\n"); + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -EBADR; + } + + dmad_detach_node(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, + drb->node); + dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, + drb->node); + + drb->state = DMAD_DRB_STATE_FREE; + drb->sync = 0; + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_free_drb); + +/** + * dmad_submit_request_internal - submit a dma-request-block to the dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @drb : [in] Reference to a drb to be submitted + * @keep_fired : [in] non-zero to kickoff dma even the channel has stopped due + * to finishing its previous request + * @return : 0 if success, non-zero if any error + * + * Submit a DRB (DMA request block) of the given DMA channel to submission + * queue. DRB is a single dma request which will be pushed into the + * submission queue of the given DMA channel. This is a lightweight internal + * version of dmad_alloc_drb() majorly for use in ring mode. Critical access to + * the drb pool should be protected before entering this function. + */ +static inline int dmad_submit_request_internal(dmad_drq *drq, dmad_drb *drb) +{ + if (drb->state == DMAD_DRB_STATE_READY) { + /* Detach user node from ready list */ + dmad_detach_node(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, + drb->node); + + dmad_attach_tail(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, + drb->node); + + drb->state = DMAD_DRB_STATE_SUBMITTED; + + dmad_dbg("%s() submit drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", + __func__, drb->node, (u32) drb, drb->src_addr, + drb->dst_addr, drb->req_cycle, drb->state); + } else { + dmad_dbg("%s() skip drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", + __func__, drb->node, (u32) drb, drb->src_addr, + drb->dst_addr, drb->req_cycle, drb->state); + } + + return 0; +} + +/** + * dmad_submit_request - submit a dma-request-block to the dma channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @drb : [in] Reference to a drb to be submitted + * @keep_fired : [in] non-zero to kickoff dma even the channel has stopped due + * to finishing its previous request + * @return : 0 if success, non-zero if any error + * + * Submit a DRB (DMA request block) of the given DMA channel to submission + * queue. DRB is a single dma request which will be pushed into the + * submission queue of the given DMA channel. + */ +int dmad_submit_request(dmad_chreq *ch_req, dmad_drb *drb, u8 keep_fired) +{ + dmad_drq *drq; + unsigned long lock_flags; + + if (unlikely(ch_req == NULL)) { + dmad_err("null ch_req!\n"); + return -EFAULT; + } + + drq = (dmad_drq *) ch_req->drq; + + if (unlikely(drq == NULL)) { + dmad_err("null ch_req->drq!\n"); + return -EBADR; + } + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + /****************************************************** + * Following code require _safe_exit return path + */ + + if (unlikely((drq->rdy_head == 0) || (drb->node == 0) || + (drb->node >= DMAD_DRB_POOL_SIZE))) { + dmad_err("node error\n"); + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -EBADR; + } + + /* Detach user node from ready list */ + dmad_detach_node(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, + drb->node); + + /* Queue DRB to the end of the submitted list */ + dmad_dbg("submit drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) sync(0x%08x) fire(%d)\n", + drb->node, (u32) drb, drb->src_addr, drb->dst_addr, + drb->req_cycle, (u32) drb->sync, keep_fired); + + /* Check if submission is performed to an empty queue */ + if (unlikely(keep_fired && (drq->sbt_head == 0))) { + /* DMA is not running, so kick off transmission */ + dmad_dbg("kickoff dma engine.\n"); + + dmad_attach_tail(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, + drb->node); + /* Source and destination address */ + if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) { + dout(drb->addr1, (unsigned long)drq->src_port); + dout(drb->addr0, (unsigned long)drq->dst_port); + } else { + dout(drb->addr0, (unsigned long)drq->src_port); + dout(drb->addr1, (unsigned long)drq->dst_port); + } + + /* Transfer size (in units of source width) */ + dout(drb->req_cycle, (unsigned long)drq->cyc_port); + + /* Enable DMA channel (Kick off transmission when client + * enable it's transfer state) + */ + dmad_enable_channel(drq); + drb->state = DMAD_DRB_STATE_EXECUTED; + } else { + /* DMA is already running, so only queue DRB to the end of the + * list + */ + dmad_attach_tail(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, + drb->node); + drb->state = DMAD_DRB_STATE_SUBMITTED; + } + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_submit_request); + +/** + * dmad_withdraw_request - cancel a submitted dma-request-block + * @ch_req : [in] Reference to the DMA request descriptor structure + * @drb : [in] Reference to a drb to be submitted + * @keep_fired : [in] non-zero to kickoff dma even the channel has stopped due + * to finishing its previous request + * @return : 0 if success, non-zero if any error + * + * Cancel a submitted DRB (DMA request block) of the given DMA channel in its + * submission queue. DRB is a single dma request which will be pushed into the + * submission queue of the given DMA channel. Cancellation fails if the DRB has + * already been kicked off. + */ +int dmad_withdraw_request(dmad_chreq *ch_req, dmad_drb *drb) +{ + dmad_drq *drq = 0; + unsigned long lock_flags; + + if (unlikely(ch_req == NULL)) { + dmad_err("null ch_req!\n"); + return -EFAULT; + } + + drq = (dmad_drq *) ch_req->drq; + + if (unlikely(drq == NULL)) { + dmad_err("null ch_req->drq!\n"); + return -EBADR; + } + + if (unlikely(drq->sbt_head == 0)) + return -EBADR; + + if (unlikely((drb->node == 0) || (drb->node >= DMAD_DRB_POOL_SIZE))) + return -EBADR; + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + if (unlikely((drq->sbt_head == 0) || (drb->node == 0) || + (drb->state != DMAD_DRB_STATE_SUBMITTED) || + (drb->node >= DMAD_DRB_POOL_SIZE))) { + dmad_err("Submitted-queue is empty or invalid node!\n"); + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -EBADR; + } + + dmad_dbg("cancel drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", + drb->node, (u32) drb, drb->src_addr, drb->dst_addr, + drb->req_cycle, drb->state); + + if (unlikely(drb->state == DMAD_DRB_STATE_EXECUTED)) { + dmad_dbg("Already running drb cannot be stopped currently!\n"); + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return 0; /*-EBADR; */ + } + + dmad_detach_node(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, + drb->node); + dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, + drb->node); + + drb->state = DMAD_DRB_STATE_FREE; + + if (drb->sync) + complete_all(drb->sync); + drb->sync = 0; + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_withdraw_request); + +/** + * dmad_kickoff_requests_internal - kickoff hw DMA transmission + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : 0 if success, non-zero if any error + * + * Kickoff hw DMA transmission of the given DMA channel. This function is + * valid for both ring & non-ring mode. This is a lightweight internal version + * of dmad_kickoff_requests() majorly for use in ring mode. Critical access to + * the drb pool should be protected before entering this function. + */ +static inline int dmad_kickoff_requests_internal(dmad_drq *drq) +{ + dmad_drb *drb; + + dmad_get_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb); + + if (!drb) { + dmad_err("%s() null drb!\n", __func__); + return -EBADR; + } + + dmad_dbg("%s() drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", + __func__, drb->node, (u32) drb, drb->src_addr, drb->dst_addr, + drb->req_cycle, drb->state); + + if (drb->state == DMAD_DRB_STATE_SUBMITTED) { + /* Transfer size (in units of source width) */ + dout(drb->req_cycle, (unsigned long)drq->cyc_port); + + /* Source and destination address */ + if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) { + dout(drb->addr1, (unsigned long)drq->src_port); + dout(drb->addr0, (unsigned long)drq->dst_port); + } else { + dout(drb->addr0, (unsigned long)drq->src_port); + dout(drb->addr1, (unsigned long)drq->dst_port); + } + + drb->state = DMAD_DRB_STATE_EXECUTED; + } + + /* Enable DMA channel */ + if (!dmad_is_channel_enabled(drq)) + dmad_enable_channel(drq); + + return 0; +} + +/** + * dmad_kickoff_requests - kickoff hw DMA transmission of the given DMA channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : 0 if success, non-zero if any error + * + * Kickoff hw DMA transmission of the given DMA channel. This function is + * valid for both ring & non-ring mode. + */ +int dmad_kickoff_requests(dmad_chreq *ch_req) +{ + dmad_drq *drq = 0; + dmad_drb *drb = 0; + unsigned long lock_flags; + dma_addr_t req_cycle; + + if (unlikely(ch_req == NULL)) { + dmad_err("null ch_req!\n"); + return -EFAULT; + } + + drq = (dmad_drq *) ch_req->drq; + + if (unlikely(drq == NULL)) { + dmad_err("null ch_req->drq!\n"); + return -EBADR; + } + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + dmad_get_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb); + + dmad_dbg("drq(0x%08x) channel_base(0x%08lx)\n", (u32) drq, + drq->channel_base); + dmad_dbg("kick off drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d) a1_to_a0(%d)\n", + (u32) drb->node, (u32) drb, drb->addr0, drb->addr1, + drb->req_cycle, drb->state, + drq->flags & DMAD_DRQ_DIR_A1_TO_A0); + + /* do nothing if no drbs are in the submission queue */ + if (unlikely((drb == 0) || (drb->state != DMAD_DRB_STATE_SUBMITTED))) { + dmad_dbg("%s() invalid drb(%d 0x%08x) or drb-state(%d)!\n", + __func__, drb->node, (u32) drb, + drb ? drb->state : 0xffffffff); + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return 0; + } + + req_cycle = drb->req_cycle; + + if (unlikely(req_cycle == 0)) { + dmad_dbg("%s() zero transfer size!\n", __func__); + goto _safe_exit; + } + + /* Transfer size (in units of source width) */ + dout(req_cycle, (unsigned long)drq->cyc_port); + + /* Source and destination address */ + if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) { + dout(drb->addr1, (unsigned long)drq->src_port); + dout(drb->addr0, (unsigned long)drq->dst_port); + } else { + dout(drb->addr0, (unsigned long)drq->src_port); + dout(drb->addr1, (unsigned long)drq->dst_port); + } + + drb->state = DMAD_DRB_STATE_EXECUTED; + + /* Enable DMA channel */ + dmad_enable_channel(drq); + +_safe_exit: + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_kickoff_requests); + +/** + * dmad_probe_hw_ptr_src - probe DMA source hw-address of the given channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : physical address of current HW source pointer + * + * Probe DMA source hw-address of the given channel. + */ +dma_addr_t dmad_probe_hw_ptr_src(dmad_chreq *ch_req) +{ + return (dma_addr_t) din(((dmad_drq *) ch_req->drq)->src_port); +} +EXPORT_SYMBOL_GPL(dmad_probe_hw_ptr_src); + +/** + * dmad_probe_hw_ptr_dst - probe DMA destination hw-address of the given channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : physical address of current HW destination pointer + * + * Probe DMA destination hw-address of the given channel. + */ +dma_addr_t dmad_probe_hw_ptr_dst(dmad_chreq *ch_req) +{ + return (dma_addr_t) din(((dmad_drq *) ch_req->drq)->dst_port); +} +EXPORT_SYMBOL_GPL(dmad_probe_hw_ptr_dst); + +/** + * dmad_update_ring - update DMA ring buffer base && size of the given channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @size : [in] The new ring buffer size, in unit of data width (cycles) + * @return : 0 if success, non-zero if any error + * + * Update DMA ring buffer size of the given channel. This function is valid + * only if the channel is initialized as ring buffer mode. + */ +int dmad_update_ring(dmad_chreq *ch_req) +{ + unsigned long lock_flags; + dmad_drq *drq = (dmad_drq *) ch_req->drq; + int remnant; + + if (unlikely(dmad_is_channel_enabled(drq))) { + dmad_err("%s() Error - dma channel should be disabled before updating ring size!\n", + __func__); + return -EFAULT; + } + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + /* todo: range checking */ + + remnant = (int)ch_req->ring_size - + (int)ch_req->periods * (int)ch_req->period_size; + if (remnant == 0) { + drq->periods = ch_req->periods; + } else if (remnant > 0) { + drq->periods = ch_req->periods; // + 1; + } else { + dmad_err("%s() Error - buffer_size < periods * period_size!\n", + __func__); + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -EFAULT; + } + + drq->ring_base = ch_req->ring_base; + drq->ring_size = ch_req->ring_size; + drq->period_size = ch_req->period_size; + drq->remnant_size = (dma_addr_t) remnant; + + if (ch_req->controller == DMAD_DMAC_AHB_CORE) { + drq->period_bytes = + DMAC_CYCLE_TO_BYTES(drq->period_size, drq->data_width); + } + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + dmad_dbg("%s() ring: base(0x%08llx) port(0x%08lx) periods(0x%08x) period_size(0x%08x) period_bytes(0x%08llx) remnant_size(0x%08llx)\n", + __func__, drq->ring_base, drq->ring_port, drq->periods, + drq->period_size, drq->period_bytes, drq->remnant_size); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_update_ring); + +/** + * dmad_update_ring_sw_ptr - update DMA ring buffer sw-pointer + * @ch_req : [in] Reference to the DMA request descriptor structure + * @sw_ptr : [in] The new sw-pointer for the hw-pointer to chase of + * @keep_fired : [in] non-zero to kickoff dma even the channel has stopped due + * to finishing its previous request + * @return : 0 if success, non-zero if any error + * + * Update DMA ring buffer sw-pointer of the given channel on the fly. This + * function is valid only if the channel is initialized as ring buffer mode. + * Uint of sw_ptr is in number of dma data width. + */ +int dmad_update_ring_sw_ptr(dmad_chreq *ch_req, dma_addr_t sw_ptr, + u8 keep_fired) +{ + dmad_drq *drq; + unsigned long lock_flags; + dma_addr_t hw_off = 0, ring_ptr; + dma_addr_t sw_p_off, ring_p_off, period_bytes; + dma_addr_t remnant_size; + int period_size; + int sw_p_idx, ring_p_idx, period, periods; + dmad_drb *drb = NULL; + + /*if (ch_req == NULL) { */ + /* dmad_dbg("%s() null ch_req!\n", __func__); */ + /* return -EFAULT; */ + /*} */ + + drq = (dmad_drq *) ch_req->drq; + + /*if (drq == NULL) { */ + /* dmad_dbg("%s() null ch_req->drq!\n", __func__); */ + /* return -EBADR; */ + /*} */ + + if (unlikely(sw_ptr > drq->ring_size)) { + // dmad_err("%s() Invalid ring buffer sw-pointer "); + return -EBADR; + } + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + periods = drq->periods; + period_size = drq->period_size; + period_bytes = drq->period_bytes; + remnant_size = drq->remnant_size; + + ring_ptr = drq->sw_ptr; + ring_p_idx = drq->sw_p_idx; + ring_p_off = drq->sw_p_off; + + sw_p_idx = div_u64(sw_ptr, period_size); + __iter_div_u64_rem(sw_ptr, period_size, &sw_p_off); + + if (remnant_size && (sw_p_idx == periods)) { + --sw_p_idx; + sw_p_off += period_size; + } + + dmad_dbg("%s() ring_ptr(0x%08llx) ring_p_idx(0x%08x) ring_p_off(0x%08llx)\n", + __func__, ring_ptr, ring_p_idx, ring_p_off); + dmad_dbg("%s() sw_ptr(0x%08llx) sw_p_idx(0x%08x) sw_p_off(0x%08llx)\n", + __func__, sw_ptr, sw_p_idx, sw_p_off); + + if (drq->ring_drb && (drq->ring_drb->state & + (DMAD_DRB_STATE_READY | DMAD_DRB_STATE_SUBMITTED | + DMAD_DRB_STATE_EXECUTED))) { + drb = drq->ring_drb; + } else { + /* alloc new drb if there is none yet at ring_ptr */ + if (dmad_alloc_drb_internal(drq, &drb) != 0) { + dmad_err("%s() drb allocation failed!\n", __func__); + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -ENOSPC; + } + drb->addr0 = ((dma_addr_t) ring_p_idx * period_bytes) + + drq->ring_base; + drb->addr1 = drq->dev_addr; + drb->req_cycle = 0; // redundent, though, no harm to performance + + dmad_dbg("init_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", + (u32) drb->node, (u32) drb, drb->src_addr, + drb->dst_addr, drb->req_cycle, drb->state); + + drq->ring_drb = drb; + } + + /* Following code-path has been optimized. The design flow is expanded + * below for reference. + * + * if (sw_ptr >= ring_ptr) + * if (sw_p_idx == ring_p_idx) + * ring_drb::req_cycle <- sw_p_off + * if (ring_drb::state == executed) + * hw_cycle <- sw_p_idx + * fi + * else + * ring_drb::req_cycle <- period_size + * if (ring_drb::state == executed) + * hw_cycle <- period_size + * fi + * for (i = ring_p_idx+1 ~sw_p_idx-1) + * new_drb::ring_addr <- i * period_bytes + ring_base + * new_drb::req_cycle <- period_size + * rof + * sw_drb::ring_addr <- sw_p_idx * period_bytes + ring_base + * sw_drb::req_cycle <- sw_p_off + * else + * // sw_ptr < ring_ptr + * ring_drb::req_cycle <- period_size + * if (ring_drb::state == executed) + * hw_cycle <- period_size + * fi + * for (i = ring_p_idx+1 ~idx_max) + * new_drb::ring_addr <- i * period_bytes + ring_base + * new_drb::req_cycle <- period_size + * rof + * for (i = 0 ~sw_p_idx-1) + * new_drb::ring_addr <- i * period_bytes + ring_base + * new_drb::req_cycle <- period_size + * rof + * sw_drb::ring_addr <- sw_p_idx * period_bytes + ring_base + * sw_drb::req_cycle <- sw_p_off + * fi + */ + if ((sw_ptr >= ring_ptr) && (sw_p_idx == ring_p_idx) && (sw_p_off != 0)) { + dmad_dbg("update ring drb\n"); + + /* update drb size at ring_ptr */ + drb->req_cycle = sw_p_off; + + dmad_dbg("ring_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", + (u32) drb->node, (u32) drb, drb->addr0, drb->addr1, + drb->req_cycle, drb->state); + + /* update hw dma size of this drb if it has been sent to the + * controller + */ + if (drb->state == DMAD_DRB_STATE_EXECUTED) { + dmad_disable_channel(drq); + + if (ch_req->controller == DMAD_DMAC_AHB_CORE) + hw_off = DMAC_BYTES_TO_CYCLE( + (addr_t)din((addr_t) drq->ring_port) + - (addr_t) drb->addr0, + drq->data_width); + + dmad_dbg("hw_off(0x%08x) sw_p_off(0x%08x)\n", + (u32) hw_off, (u32) sw_p_off); + + if (sw_p_off < hw_off) + dmad_err("%s() underrun! sw_p_off(0x%08x) < hw_off(0x%08x)\n", + __func__, (u32) sw_p_off, + (u32) hw_off); + else + dout(sw_p_off - hw_off, + (unsigned long)drq->cyc_port); + + dmad_enable_channel(drq); + + } else { + dmad_submit_request_internal(drq, drb); + } + + } else { + dmad_dbg("fulfill ring drb - sw_ptr(0x%08x) ring_ptr(0x%08x)\n", + (u32) sw_ptr, (u32) ring_ptr); + + /* fulfill last drb at ring_ptr */ + if (ring_p_idx == (periods - 1)) + drb->req_cycle = period_size + remnant_size; + else + drb->req_cycle = period_size; + + dmad_dbg("ring_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", + (u32) drb->node, (u32) drb, drb->addr0, drb->addr1, + drb->req_cycle, drb->state); + + if (drb->state == DMAD_DRB_STATE_EXECUTED) { + dmad_disable_channel(drq); + + if (ch_req->controller == DMAD_DMAC_AHB_CORE) + hw_off = DMAC_BYTES_TO_CYCLE((addr_t) + din((addr_t) drq->ring_port) + -(addr_t) drb->addr0, + drq->data_width); + + dmad_dbg("hw_off(0x%08x) period_size(0x%08x)\n", + (u32) hw_off, (u32) period_size); + + if (ring_p_idx == (periods - 1)) { + if (period_size < hw_off) + dmad_err("%s() illegal! period_size(0x%08x) + remnant_size(0x%08x) < hw_off(0x%08x)\n", + __func__, (u32) period_size, + (u32) remnant_size, (u32) hw_off); + else + dout(period_size + remnant_size - + hw_off, + (unsigned long)drq->cyc_port); + } else { + if (period_size < hw_off) + dmad_err("%s() illegal! period_size(0x%08x) < hw_off(0x%08x)\n", + __func__, (u32) period_size, + (u32) hw_off); + else + dout(period_size - hw_off, + (unsigned long)drq->cyc_port); + } + + dmad_enable_channel(drq); + + } else { + dmad_submit_request_internal(drq, drb); + } + + ++ring_p_idx; + + /* adjust sw_ptr period index ahead by one ring cycle */ + //if (sw_ptr < ring_ptr) { + if (sw_p_idx < ring_p_idx) + sw_p_idx += periods; + + /* allocate in-between (ring_ptr+1 to sw_ptr-1) + * full-cycle drbs + */ + for (period = ring_p_idx; period < sw_p_idx; ++period) { + if (dmad_alloc_drb_internal(drq, &drb) != 0) { + dmad_err("%s() drb allocation failed!\n", + __func__); + spin_unlock_irqrestore(&drq->drb_pool_lock, + lock_flags); + return -ENOSPC; + } + + drb->addr0 = + (dma_addr_t) (period % periods) * period_bytes + + drq->ring_base; + drb->addr1 = drq->dev_addr; + + if (period == (periods - 1)) + drb->req_cycle = period_size + remnant_size; + else + drb->req_cycle = period_size; + + dmad_dbg("inbtw_drb(%d 0x%08x) addr0(0x%08llx)addr1 (0x%08llx) size(0x%08llx) state(%d)\n", + (u32) drb->node, (u32) drb, drb->addr0, + drb->addr1, drb->req_cycle, drb->state); + + dmad_submit_request_internal(drq, drb); + } + + /* allocate drb right at sw_ptr */ + if (dmad_alloc_drb_internal(drq, &drb) != 0) { + dmad_err("%s() drb allocation failed!\n", __func__); + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -ENOSPC; + } + drb->addr0 = (dma_addr_t) (sw_p_idx % periods) * period_bytes + + drq->ring_base; + drb->addr1 = drq->dev_addr; + drb->req_cycle = sw_p_off; + + dmad_dbg("swptr_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", + (u32) drb->node, (u32) drb, drb->addr0, drb->addr1, + drb->req_cycle, drb->state); + + drq->ring_drb = drb; + + if (sw_p_off > 0) + dmad_submit_request_internal(drq, drb); + } + + __iter_div_u64_rem(sw_ptr, drq->ring_size, &drq->sw_ptr); + drq->sw_p_idx = sw_p_idx % periods; + drq->sw_p_off = sw_p_off; + + if (likely(keep_fired)) + dmad_kickoff_requests_internal(drq); + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + return 0; +} +EXPORT_SYMBOL_GPL(dmad_update_ring_sw_ptr); + +/** + * dmad_probe_ring_hw_ptr - probe DMA ring buffer position of the given channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @return : Ring buffer position of current HW ring buffer pointer + * + * Probe DMA ring buffer position of the given channel. The position is + * relative to the ring buffer base. This function is valid only if the + * channel is initialized as ring buffer mode. + */ +dma_addr_t dmad_probe_ring_hw_ptr(dmad_chreq *ch_req) +{ + dmad_drq *drq = (dmad_drq *) ch_req->drq; + dma_addr_t cycles = + (dma_addr_t) din(drq->ring_port) - (dma_addr_t) drq->ring_base; + + if (ch_req->controller == DMAD_DMAC_AHB_CORE) + cycles = DMAC_BYTES_TO_CYCLE(cycles, drq->data_width); + + return cycles; +} +EXPORT_SYMBOL_GPL(dmad_probe_ring_hw_ptr); + +/** + * dmad_channel_drain - cancel DMA transmission of the given DMA channel + * @controller : [in] One of the enum value of DMAD_DMAC_CORE + * @drq : [in] Reference to the DMA queue structure (dmad_drq) + * @shutdown : [in] Non-zero to force a immediate channel shutdown + * @return : 0 if success, non-zero if any error + * + * Stop the DMA transmission and cancel all submitted requests of the given + * DMA channel. This function drains a single channel and is the internal + * implementation of the interface routine dmad_drain_requests() and the + * module_exit function. + */ +static int dmad_channel_drain(u32 controller, dmad_drq *drq, u8 shutdown) +{ + dmad_drb *drb = 0; + unsigned long lock_flags; + + if (unlikely(drq == NULL)) { + dmad_err("null ch_req->drq!\n"); + return -EBADR; + } + + spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); + + /* Stop DMA channel if forced to shutdown immediately */ + if (shutdown) { + /* disable dma controller */ + dmad_reset_channel(drq); + + /* todo: more settings to stop DMA controller ?? */ + + /*if (drb->state == DMAD_DRB_STATE_EXECUTED) { */ + /*} */ + } + + /* Detach DRBs in submit queue */ + dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, &drb); + + while (drb) { + dmad_dbg("cancel sbt drb(%d 0x%08x) addr0(0x%08llx)addr1(0x%08llx) size(0x%08llx) state(%d)\n", + drb->node, (u32) drb, drb->src_addr, drb->dst_addr, + drb->req_cycle, (u32) drb->state); + + /* Mark DRB state as abort */ + drb->state = DMAD_DRB_STATE_ABORT; + + if (drb->sync) + complete_all(drb->sync); + + dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, + drb->node); + + dmad_detach_head(drq->drb_pool, &drq->sbt_head, &drq->sbt_tail, + &drb); + } + + /* Detach DRBs in ready queue */ + dmad_detach_head(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, &drb); + + while (drb) { + dmad_dbg("cancel rdy drb(%d 0x%08x) addr0(0x%08llx)addr1(0x%08llx) size(0x%08llx) state(%d)\n", + drb->node, (u32) drb, drb->src_addr, drb->dst_addr, + drb->req_cycle, (u32) drb->state); + + /* Mark DRB state as abort */ + drb->state = DMAD_DRB_STATE_ABORT; + + dmad_attach_tail(drq->drb_pool, &drq->fre_head, &drq->fre_tail, + drb->node); + + /* Detach next submitted DRB (DMA Request Block) from the + * DRQ (DMA Request Queue) + */ + dmad_detach_head(drq->drb_pool, &drq->rdy_head, &drq->rdy_tail, + &drb); + } + + drq->state |= DMAD_DRQ_STATE_ABORT; + + drq->ring_drb = NULL; + drq->sw_ptr = 0; + drq->sw_p_idx = 0; + drq->sw_p_off = 0; + + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + + if (/*(drq->fre_head == 0) && */(drq->flags & + DMAD_FLAGS_SLEEP_BLOCK)) { + complete_all(&drq->drb_alloc_sync); + } + + return 0; +} + +/** + * dmad_cancel_requests - cancel DMA transmission of the given DMA channel + * @ch_req : [in] Reference to the DMA request descriptor structure + * @shutdown : [in] Non-zero to force a immediate channel shutdown + * @return : 0 if success, non-zero if any error + * + * Stop the DMA transmission and cancel all submitted requests of the given + * DMA channel. + */ +int dmad_drain_requests(dmad_chreq *ch_req, u8 shutdown) +{ + if (ch_req == NULL) { + dmad_err("null ch_req!\n"); + return -EFAULT; + } + + return dmad_channel_drain(ch_req->controller, ch_req->drq, shutdown); +} +EXPORT_SYMBOL_GPL(dmad_drain_requests); + +/** + * dmad_probe_irq_source - probe DMA channel who asserts the shared sw-irq line + * @return : The channel number which asserts the shared sw-irq line + * + * Probe DMA channel who asserts the shared sw-irq line. + */ +int dmad_probe_irq_source_ahb(void) +{ + int channel; /* interrupt channel number */ + + /* todo: spin_lock */ + + /* - Check DMA status register to get channel number */ + for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel) { + if (getbl(channel + TC_OFFSET, (unsigned long)INT_STA)) + return channel; + } + + /* Perform DMA error checking if no valid channel was found who + * assert the finish signal. + */ + for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel) { + if (getbl(channel + ERR_OFFSET, (unsigned long)INT_STA)) + return channel; + if (getbl(channel + ABT_OFFSET, (unsigned long)INT_STA)) + return channel; + } + + /* todo: spin_unlock */ + + return -EFAULT; +} +EXPORT_SYMBOL_GPL(dmad_probe_irq_source_ahb); + +/** + * dmad_module_init - dma module-init function + * @return : 0 if success, non-zero if any error + */ +int dmad_module_init(void) +{ + int err = 0; + + /* clear device struct since the module may be load/unload many times */ + memset(&dmad, 0, sizeof(dmad) - 4); + dmad.drq_pool = + kmalloc_array(DMAD_AHB_MAX_CHANNELS, sizeof(dmad_drq), GFP_KERNEL); + if (dmad.drq_pool == NULL) { + dmad_err("%s() failed to allocate drb pool!\n", __func__); + return -ENOMEM; + } + memset(dmad.drq_pool, 0, DMAD_AHB_MAX_CHANNELS * sizeof(dmad_drq)); + spin_lock_init(&dmad.drq_pool_lock); + dmad.ahb_drq_pool = dmad.drq_pool; + dmad_dbg("DMA module init result: (%d)\n", err); + dmad_dbg(" AHB channels: %d\n; DRBs per channel: %d\n", + DMAC_MAX_CHANNELS, DMAD_DRB_POOL_SIZE); + + dmad_dbg("%s() return code (%d) <<\n", __func__, err); + return err; +} + +/** + * dmad_module_init - dma module clean up function + */ +int __exit dmad_module_exit(struct platform_device *pdev) +{ + dmad_drq *drq; + u32 channel; + struct at_dma_platform_data *pdata; + + pdata = dev_get_platdata(&pdev->dev); + + spin_lock(&dmad.drq_pool_lock); + + /* cancel existing requests and unregister interrupt handler */ + for (channel = 0; channel < DMAD_AHB_MAX_CHANNELS; ++channel) { + /* shutdown dma requests */ + drq = (dmad_drq *) &dmad.ahb_drq_pool[channel]; + + if ((drq->state & DMAD_DRQ_STATE_READY) != 0) + dmad_channel_drain(DMAD_DMAC_AHB_CORE, drq, 1); + + /* free registered irq handlers */ + free_irq(pdata->irqs[channel + 1], + (void *)(unsigned long)(channel + 1)); + } + + spin_unlock(&dmad.drq_pool_lock); + + kfree(dmad.drq_pool); + + memset(&dmad, 0, sizeof(dmad)); + + /* release I/O space */ + release_region((uintptr_t) pdata->dmac_regs, resource_size(pdata->io)); + dmad_dbg("DMA module unloaded!\n"); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id atcdma100_of_id_table[] = { + {.compatible = "andestech,atcdmac300" }, + { } +}; + +MODULE_DEVICE_TABLE(of, atcdma100_of_id_table); + +static struct at_dma_platform_data *at_dma_parse_dt(struct platform_device + *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct at_dma_platform_data *pdata; + + if (!np) { + dev_err(&pdev->dev, "Missing DT data\n"); + return NULL; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + if (of_property_read_u32(np, "dma-channels", &pdata->nr_channels)) + return NULL; + + return pdata; +} +#else +static inline struct at_dma_platform_data *at_dma_parse_dt(struct + platform_device + *pdev) +{ + return NULL; +} +#endif + +int get_irq(int channel) +{ + return pdata->irqs[channel + 1]; +} + +static int atcdma_probe(struct platform_device *pdev) +{ + struct resource *io = 0; + struct resource *mem = NULL; + int index; + + pdata = dev_get_platdata(&pdev->dev); + dmad.plat = pdev; + + if (!pdata) + pdata = at_dma_parse_dt(pdev); + pdev->dev.platform_data = pdata; + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pdata->io = io; + mem = request_mem_region(io->start, resource_size(io), pdev->name); + + if (!mem) { + dev_err(&pdev->dev, "failed to get io memory region resouce.\n"); + return -EINVAL; + } + + pdata->dmac_regs = + (void __iomem *)ioremap(mem->start, resource_size(io)); + dmac_base = (uintptr_t) pdata->dmac_regs; + + for (index = 0; index < DMAC_FTDMAC020_IRQ_COUNT + 1; index++) + pdata->irqs[index] = platform_get_irq(pdev, index); + + if (pdata->irqs[0] < 0) + return pdata->irqs[0]; + + intc_ftdmac020_init_irq(pdata->irqs[0]); + + return dmad_module_init(); +} + +static int __exit atcdma_remove(struct platform_device *pdev) +{ + return dmad_module_exit(pdev); +} + +static struct platform_driver atcdma100_driver = { + .probe = atcdma_probe, + .remove = __exit_p(atcdma_remove), + .driver = { + .name = "atcdmac100", + .of_match_table = of_match_ptr(atcdma100_of_id_table), + }, +}; + +static int __init atcdma_init(void) +{ + return platform_driver_register(&atcdma100_driver); +} + +subsys_initcall(atcdma_init); + +#endif /* CONFIG_PLATFORM_AHBDMA */ diff --git a/drivers/soc/andes/dmad_intc.c b/drivers/soc/andes/dmad_intc.c new file mode 100644 index 00000000000000..0e9d0f5499e569 --- /dev/null +++ b/drivers/soc/andes/dmad_intc.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Andes Technology Corporation + */ + +#include +#include +#include +#include + +#ifdef CONFIG_PLATFORM_AHBDMA + +void AHBDMA_irq_rounter(struct irq_desc *desc) +{ + int ahb_irq; + struct irq_desc *ahb_desc; + + raw_spin_lock(&desc->lock); + ahb_irq = dmad_probe_irq_source_ahb(); + + if (ahb_irq >= 0) { + ahb_irq = get_irq(ahb_irq); + ahb_desc = irq_to_desc(ahb_irq); + ahb_desc->irq_data.irq = ahb_irq; + raw_spin_unlock(&desc->lock); + ahb_desc->handle_irq(ahb_desc); + raw_spin_lock(&desc->lock); + } + desc->irq_data.chip->irq_unmask(&desc->irq_data); + desc->irq_data.chip->irq_eoi(&desc->irq_data); + raw_spin_unlock(&desc->lock); +} + +int intc_ftdmac020_init_irq(int irq) +{ + int i; + int ret; + /* Register all IRQ */ + for (i = DMA_IRQ0; i < DMA_IRQ0 + DMA_IRQ_COUNT; i++) { + // level trigger + ret = irq_set_chip(i, &dummy_irq_chip); + irq_set_handler(i, handle_simple_irq); + } + irq_set_chained_handler(irq, AHBDMA_irq_rounter); + return 0; +} +#endif /* CONFIG_PLATFORM_AHBDMA */ diff --git a/include/soc/andes/atcdmac300.h b/include/soc/andes/atcdmac300.h new file mode 100644 index 00000000000000..cbb81b4e606239 --- /dev/null +++ b/include/soc/andes/atcdmac300.h @@ -0,0 +1,537 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Andes Technology Corporation + * + */ + +#ifndef __NDS_DMAD_ATF_INC__ +#define __NDS_DMAD_ATF_INC__ + +/***************************************************************************** + * Configuration section + *****************************************************************************/ +/* Debug trace enable switch */ +#define DMAD_ERROR_TRACE 1 /* message for fatal errors */ +#define DMAD_DEBUG_TRACE 0 /* message for debug trace */ +typedef u32 addr_t; + +/* Device base address */ + +extern resource_size_t dmac_base; +#define DMAC_BASE (dmac_base) + +/* ID and Revision Register */ +#define ID_REV (DMAC_BASE + 0x00) +/* DMAC Configuration Register*/ +#define CFG (DMAC_BASE + 0x10) +#define REQSYNC 30 +#define CTL (DMAC_BASE + 0x20) +#define CH_ABT (DMAC_BASE + 0x24) +/* Interrupt Status Register */ +#define INT_STA (DMAC_BASE + 0x30) +#define TC_OFFSET 16 +#define ABT_OFFSET 8 +#define ERR_OFFSET 0 + + +#define CH_EN (DMAC_BASE + 0x34) + + + +#define DMAC_CH_OFFSET 0x40 +#define CH_CTL_OFF 0x0 +#define CH_SIZE_OFF 0x4 +#define CH_SRC_LOW_OFF 0x8 +#define CH_SRC_HIGH_OFF 0xc +#define CH_DST_LOW_OFF 0x10 +#define CH_DST_HIGH_OFF 0x14 +#define CH_LLP_LOW_OFF 0x18 +#define CH_LLP_HIGH_OFF 0x1c + + +#define DMAC_C0_BASE (DMAC_BASE + DMAC_CH_OFFSET) +#define DMAC_MAX_CHANNELS 8 +#define DMAC_BASE_CH(n) (DMAC_C0_BASE + n*0x20) + +/***** Channel n Control Register ******/ +#define CH_CTL(n) (DMAC_BASE_CH(n)+CH_CTL_OFF) +#define PRIORITY_SHIFT 29 +#define PRIORITY_LOW 0 +#define PRIORITY_HIGH 1 +#define DMAC_CSR_CHPRI_0 PRIORITY_LOW +#define DMAC_CSR_CHPRI_1 PRIORITY_LOW +#define DMAC_CSR_CHPRI_2 PRIORITY_HIGH +#define DMAC_CSR_CHPRI_3 PRIORITY_HIGH + + +#define SBURST_SIZE_SHIFT 24 +#define SBURST_SIZE_MASK (0xf<<24) +#define DMAC_CSR_SIZE_1 0x0 +#define DMAC_CSR_SIZE_2 0x1 +#define DMAC_CSR_SIZE_4 0x2 +#define DMAC_CSR_SIZE_8 0x3 +#define DMAC_CSR_SIZE_16 0x4 +#define DMAC_CSR_SIZE_32 0x5 +#define DMAC_CSR_SIZE_64 0x6 +#define DMAC_CSR_SIZE_128 0x7 +#define DMAC_CSR_SIZE_256 0x8 +#define DMAC_CSR_SIZE_512 0x9 +#define DMAC_CSR_SIZE_1024 0xa +/* Source transfer width */ +#define SRCWIDTH 21 +#define SRCWIDTH_MASK (0x7<> (width)) +#else +#define DMAC_CYCLE_TO_BYTES(cycle, width) 0 +#define DMAC_BYTES_TO_CYCLE(bytes, width) 0 +#endif /* CONFIG_PLATFORM_AHBDMA */ + + +/* Assignment of DMA hardware handshake ID */ +#define DMAC_REQN_SPITX 0 +#define DMAC_REQN_SPIRX 1 +#ifdef CONFIG_PLAT_AE350 +#define DMAC_REQN_I2SAC97TX 14 +#define DMAC_REQN_I2SAC97RX 15 +#else + +#define DMAC_REQN_I2SAC97TX 2 +#define DMAC_REQN_I2SAC97RX 3 +#endif +#define DMAC_REQN_UART1TX 4 +#define DMAC_REQN_UART1RX 5 +#define DMAC_REQN_UART2TX 6 +#define DMAC_REQN_UART2RX 7 +#define DMAC_REQN_I2C 8 +#define DMAC_REQN_SDC 9 +#define DMAC_REQN_NONE 16 + + +enum DMAD_DMAC_CORE { + DMAD_DMAC_AHB_CORE, + DMAD_DMAC_APB_CORE +}; + +enum DMAD_CHREG_FLAGS { + DMAD_FLAGS_NON_BLOCK = 0x00000000, + DMAD_FLAGS_SLEEP_BLOCK = 0x00000001, + DMAD_FLAGS_SPIN_BLOCK = 0x00000002, + DMAD_FLAGS_RING_MODE = 0x00000008, /* ring submission mode */ + DMAD_FLAGS_BIDIRECTION = 0x00000010, /* indicates both tx and rx */ +}; + +enum DMAD_CHDIR { + DMAD_DIR_A0_TO_A1 = 0, + DMAD_DIR_A1_TO_A0 = 1, +}; + +/* AHB Channel Request + * + * Notes for developers: + * These should be channel-only properties. Controller-specific properties + * should be separated as other driver structure or driver buildin-hardcode. + * If controller properties are embeded in this union, request for a channel + * may unexpectedly override the controller setting of the request of other + * channels. + */ +typedef struct dmad_ahb_chreq { + /* channel property */ + u32 sync; /* (in) different clock domain */ + u32 priority; /* (in) DMAC_CSR_CHPRI_xxx */ + u32 hw_handshake; /* (in) hardware handshaking on/off */ + u32 burst_size; /* (in) DMAC_CSR_SIZE_xxx */ + + /* source property */ + union { + u32 src_width; /* (in) DMAC_CSR_WIDTH_xxx */ + u32 addr0_width; /* (in) bi-direction mode alias */ + u32 ring_width; /* (in) ring-mode alias */ + }; + union { + u32 src_ctrl; /* (in) DMAC_CSR_AD_xxx */ + u32 addr0_ctrl; /* (in) bi-direction mode alias */ + u32 ring_ctrl; /* (in) ring-mode alias */ + }; + union { + u32 src_reqn; /* (in) DMAC_REQN_xxx */ + u32 addr0_reqn; /* (in) bi-direction mode alias */ + u32 ring_reqn; /* (in) ring-mode alias */ + }; + + /* destination property */ + union { + u32 dst_width; /* (in) DMAC_CSR_WIDTH_xxx */ + u32 addr1_width; /* (in) bi-direction mode alias */ + u32 dev_width; /* (in) ring-mode alias */ + }; + union { + u32 dst_ctrl; /* (in) DMAC_CSR_AD_xxx */ + u32 addr1_ctrl; /* (in) bi-direction mode alias */ + u32 dev_ctrl; /* (in) ring-mode alias */ + }; + union { + u32 dst_reqn; /* (in) DMAC_REQN_xxx */ + u32 addr1_reqn; /* (in) bi-direction mode alias */ + u32 dev_reqn; /* (in) ring-mode alias */ + }; + + /* (in) transfer direction, valid only if following flags were set ... + * DMAD_FLAGS_BIDIRECTION or + * DMAD_FLAGS_RING_MODE + * value: + * 0 (addr0 -> addr1, or ring-buff to device) + * 1 (addr0 <- addr1, or device to ring-buff) + */ + u32 tx_dir; + +} dmad_ahb_chreq; + +/* APB Channel Request + * + * Notes for developers: + * These should be channel-only properties. Controller-specific properties + * should be separated as other driver structure or driver buildin-hardcode. + * If controller properties are embeded in this union, request for a channel + * may unexpectedly override the controller setting of the request of other + * channels. + */ +typedef struct dmad_apb_chreq { + /* controller property (removed! should not exist in this struct) */ + + /* channel property */ + u32 burst_mode; /* (in) Burst mode (0/1) */ + u32 data_width; /* (in) APBBR_DATAWIDTH_xxx */ + + /* source property */ + union { + u32 src_ctrl; /* (in) APBBR_ADDRINC_xxx */ + u32 addr0_ctrl; /* (in) bi-direction mode alias */ + u32 ring_ctrl; /* (in) ring-mode alias */ + }; + union { + u32 src_reqn; /* (in) APBBR_REQN_xxx */ + u32 addr0_reqn; /* (in) bi-direction mode alias */ + u32 ring_reqn; /* (in) ring-mode alias */ + }; + + /* destination property */ + union { + u32 dst_ctrl; /* (in) APBBR_ADDRINC_xxx */ + u32 addr1_ctrl; /* (in) bi-direction mode alias */ + u32 dev_ctrl; /* (in) ring-mode alias */ + }; + union { + u32 dst_reqn; /* (in) APBBR_REQN_xxx */ + u32 addr1_reqn; /* (in) bi-direction mode alias */ + u32 dev_reqn; /* (in) ring-mode alias */ + }; + + /* (in) transfer direction, valid only if following flags were set ... + * DMAD_FLAGS_BIDIRECTION or + * DMAD_FLAGS_RING_MODE + * value: + * 0 (addr0 -> addr1, or ring-buff to device) + * 1 (addr0 <- addr1, or device to ring-buff) + */ + u32 tx_dir; + +} dmad_apb_chreq; + +/* Channel Request Descriptor */ +typedef struct dmad_chreq { + /* common fields */ + u32 controller; /* (in) enum DMAD_DMAC_CORE */ + u32 flags; /* (in) enum DMAD_CHREQ_FLAGS */ + + /********************************************************************** + * ring mode specific fields (valid only for DMAD_FLAGS_RING_MODE) + * note: + * - size fields are in unit of data width + * * for AHB, ring size is limited to 4K * data_width of data if + * hw-LLP is not used + * * for AHB, ring size is limited to 4K * data_width * LLP-count + * hw-if LLP is used + * * for APB, ring size is limited to 16M * data_width of data + * - currently sw ring mode dma supports only fixed or incremental + * src/dst addressing + * - ring_size shoule >= periods * period_size + */ + dma_addr_t ring_base; /* (in) ring buffer base (pa) */ + dma_addr_t ring_size; /* (in) unit of data width */ + dma_addr_t dev_addr; /* (in) device data port address */ + dma_addr_t periods; /* (in) number of ints per ring */ + dma_addr_t period_size; /* (in) size per int, data-width */ + + + /* channel-wise completion callback - called when hw-ptr catches sw-ptr + * (i.e., channel stops) + * + * completion_cb: (in) client supplied callback function, executed in + * interrupt context. + * completion_data: (in) client private data to be passed to data + * argument of completion_cb(). + */ + void (*completion_cb)(int channel, u16 status, void *data); + void *completion_data; + /*********************************************************************/ + + /* channel allocation output */ + u32 channel; /* (out) allocated channel */ + void *drq; /* (out) internal use (DMAD_DRQ *)*/ + + /* channel-alloc parameters (channel-wise properties) */ + union { +#ifdef CONFIG_PLATFORM_AHBDMA + dmad_ahb_chreq ahb_req; /* (in) for AHB DMA parameters */ +#endif +#ifdef CONFIG_PLATFORM_APBDMA + dmad_apb_chreq apb_req; /* (in) APB Bridge DMA params */ +#endif + }; + +} dmad_chreq; + +/* drb states are mutual exclusive */ +enum DMAD_DRB_STATE { + DMAD_DRB_STATE_FREE = 0, + DMAD_DRB_STATE_READY = 0x00000001, + DMAD_DRB_STATE_SUBMITTED = 0x00000002, + DMAD_DRB_STATE_EXECUTED = 0x00000004, + DMAD_DRB_STATE_COMPLETED = 0x00000008, + //DMAD_DRB_STATE_ERROR = 0x00000010, + DMAD_DRB_STATE_ABORT = 0x00000020, +}; + +/* DMA request block + * todo: replaced link with kernel struct list_head ?? + */ +typedef struct dmad_drb { + u32 prev; /* (internal) previous node */ + u32 next; /* (internal) next node */ + u32 node; /* (internal) this node */ + + u32 state; /* (out) DRB's current state */ + + union { + dma_addr_t src_addr; /* (in) source pa */ + dma_addr_t addr0; /* (in) bi-direction mode alias */ + }; + + union { + dma_addr_t dst_addr; /* (in) destination pa */ + dma_addr_t addr1; /* (in) bi-direction mode alias */ + }; + + /* (in) AHB DMA (22 bits): 0 ~ 4M-1, unit is "data width" + * APB DMA (24 bits): 0 ~ 16M-1, unit is "data width * burst size" + * => for safe without mistakes, use dmad_make_req_cycles() to + * compose this value if the addressing mode is incremental + * mode (not working yet for decremental mode). + */ + dma_addr_t req_cycle; + + /* (in) if non-null, this sync object will be signaled upon dma + * completion (for blocked-waiting dma completion) + */ + struct completion *sync; + +} dmad_drb; + + +/****************************************************************************** + * Debug Trace Mechanism + */ +#if (DMAD_ERROR_TRACE) +#define dmad_err(format, arg...) pr_err(format, ## arg) +#else +#define dmad_err(format, arg...) +#endif + +#if (DMAD_DEBUG_TRACE) +#define dmad_dbg(format, arg...) pr_err(format, ## arg) +#else +#define dmad_dbg(format, arg...) +#endif + +#if (defined(CONFIG_PLATFORM_AHBDMA) || defined(CONFIG_PLATFORM_APBDMA)) + +/****************************************************************************** + * DMAD Driver Interface + ****************************************************************************** + */ +extern int dmad_channel_alloc(dmad_chreq *ch_req); +extern int dmad_channel_free(dmad_chreq *ch_req); +extern int dmad_channel_enable(const dmad_chreq *ch_req, u8 enable); +extern u32 dmad_max_size_per_drb(dmad_chreq *ch_req); +extern u32 dmad_bytes_to_cycles(dmad_chreq *ch_req, u32 byte_size); + +extern int dmad_kickoff_requests(dmad_chreq *ch_req); +extern int dmad_drain_requests(dmad_chreq *ch_req, u8 shutdown); + +/* for performance reason, these two functions are platform-specific */ +#ifdef CONFIG_PLATFORM_AHBDMA +extern int dmad_probe_irq_source_ahb(void); +#endif +#ifdef CONFIG_PLATFORM_APBDMA +extern int dmad_probe_irq_source_apb(void); +#endif + +/* note: hw_ptr here is phyical address of dma source or destination */ +extern dma_addr_t dmad_probe_hw_ptr_src(dmad_chreq *ch_req); +extern dma_addr_t dmad_probe_hw_ptr_dst(dmad_chreq *ch_req); + +/***************************************************************************** + * routines only valid in discrete (non-ring) mode + */ +extern int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir); +extern int dmad_alloc_drb(dmad_chreq *ch_req, dmad_drb **drb); +extern int dmad_free_drb(dmad_chreq *ch_req, dmad_drb *drb); +extern int dmad_submit_request(dmad_chreq *ch_req, + dmad_drb *drb, u8 keep_fired); +extern int dmad_withdraw_request(dmad_chreq *ch_req, dmad_drb *drb); +/****************************************************************************/ + +/***************************************************************************** + * routines only valid in ring mode + * note: sw_ptr and hw_ptr are values offset from the ring buffer base + * unit of sw_ptr is data-width + * unit of hw_ptr returned is byte + */ +extern int dmad_update_ring(dmad_chreq *ch_req); +extern int dmad_update_ring_sw_ptr(dmad_chreq *ch_req, + dma_addr_t sw_ptr, u8 keep_fired); +extern dma_addr_t dmad_probe_ring_hw_ptr(dmad_chreq *ch_req); +/****************************************************************************/ + +#else /* CONFIG_PLATFORM_AHBDMA || CONFIG_PLATFORM_APBDMA */ + +static inline int dmad_channel_alloc(dmad_chreq *ch_req) { return -EFAULT; } +static inline int dmad_channel_free(dmad_chreq *ch_req) { return -EFAULT; } +static inline int dmad_channel_enable(const dmad_chreq *ch_req, u8 enable) + { return -EFAULT; } +static inline u32 dmad_max_size_per_drb(dmad_chreq *ch_req) { return 0; } +static inline u32 dmad_bytes_to_cycles(dmad_chreq *ch_req, u32 byte_size) + { return 0; } +static inline int dmad_kickoff_requests(dmad_chreq *ch_req) { return -EFAULT; } +static inline int dmad_drain_requests(dmad_chreq *ch_req, u8 shutdown) + { return -EFAULT; } +static inline int dmad_probe_irq_source_ahb(void) { return -EFAULT; } +static inline int dmad_probe_irq_source_apb(void) { return -EFAULT; } +static inline dma_addr_t dmad_probe_hw_ptr_src(dmad_chreq *ch_req) + { return (dma_addr_t)NULL; } +static inline dma_addr_t dmad_probe_hw_ptr_dst(dmad_chreq *ch_req) + { return (dma_addr_t)NULL; } +static inline int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir) + { return -EFAULT; } +static inline int dmad_alloc_drb(dmad_chreq *ch_req, dmad_drb **drb) + { return -EFAULT; } +static inline int dmad_free_drb(dmad_chreq *ch_req, dmad_drb *drb) + { return -EFAULT; } +static inline int dmad_submit_request(dmad_chreq *ch_req, + dmad_drb *drb, u8 keep_fired) { return -EFAULT; } +static inline int dmad_withdraw_request(dmad_chreq *ch_req, dmad_drb *drb) + { return -EFAULT; } +static inline int dmad_update_ring(dmad_chreq *ch_req) + { return -EFAULT; } +static inline int dmad_update_ring_sw_ptr(dmad_chreq *ch_req, + dma_addr_t sw_ptr, u8 keep_fired) { return -EFAULT; } +static inline dma_addr_t dmad_probe_ring_hw_ptr(dmad_chreq *ch_req) + { return (dma_addr_t)NULL; } + +#endif /* CONFIG_PLATFORM_AHBDMA || CONFIG_PLATFORM_APBDMA */ + +#endif /* __NDS_DMAD_ATF_INC__ */ + diff --git a/include/soc/andes/dmad.h b/include/soc/andes/dmad.h new file mode 100644 index 00000000000000..7f958772609528 --- /dev/null +++ b/include/soc/andes/dmad.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Andes Technology Corporation + * + */ + +#ifndef __NDS_DMAD_INC__ +#define __NDS_DMAD_INC__ + +#include + +#ifdef CONFIG_PLATFORM_AHBDMA +int intc_ftdmac020_init_irq(int irq); +#endif + +extern resource_size_t ahb_base; +extern resource_size_t pmu_base; + +#define AMERALD_PRODUCT_ID 0x41471000 +#define AMERALD_MASK 0xFFFFF000 + +/* DMAC */ +#define DMAC_FTDMAC020_IRQ_COUNT 8 +#define DMAC_FTDMAC020_IRQ0 64 +#define DMAC_FTDMAC020_IRQ1 65 +#define DMAC_FTDMAC020_IRQ2 66 +#define DMAC_FTDMAC020_IRQ3 67 +#define DMAC_FTDMAC020_IRQ4 68 +#define DMAC_FTDMAC020_IRQ5 69 +#define DMAC_FTDMAC020_IRQ6 70 +#define DMAC_FTDMAC020_IRQ7 71 + +/* APBBRG */ +#define APBBRG_FTAPBBRG020S_IRQ_COUNT 4 +#define APBBRG_FTAPBBRG020S_IRQ0 72 +#define APBBRG_FTAPBBRG020S_IRQ1 73 +#define APBBRG_FTAPBBRG020S_IRQ2 74 +#define APBBRG_FTAPBBRG020S_IRQ3 75 + + +/* Dma irq */ +#define DMA_IRQ_COUNT DMAC_FTDMAC020_IRQ_COUNT +#define DMA_IRQ0 DMAC_FTDMAC020_IRQ0 +#define DMA_IRQ1 DMAC_FTDMAC020_IRQ1 +#define DMA_IRQ2 DMAC_FTDMAC020_IRQ2 +#define DMA_IRQ3 DMAC_FTDMAC020_IRQ3 +#define DMA_IRQ4 DMAC_FTDMAC020_IRQ4 +#define DMA_IRQ5 DMAC_FTDMAC020_IRQ5 +#define DMA_IRQ6 DMAC_FTDMAC020_IRQ6 +#define DMA_IRQ7 DMAC_FTDMAC020_IRQ7 + + +struct at_dma_platform_data { + unsigned int nr_channels; + unsigned int irqs[DMA_IRQ_COUNT + 1]; /* include AHB and 8 channels */ + bool is_private; +#define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ +#define CHAN_ALLOCATION_DESCENDING 1 /* seven to zero */ + unsigned char chan_allocation_order; +#define CHAN_PRIORITY_ASCENDING 0 /* chan0 highest */ +#define CHAN_PRIORITY_DESCENDING 1 /* chan7 highest */ + unsigned char chan_priority; + unsigned short block_size; + unsigned char nr_masters; + unsigned char data_width[4]; + struct resource *io; + void __iomem *dmac_regs; + void __iomem *pmu_regs; + void __iomem *apb_regs; +}; + +extern int dmad_probe_irq_source_ahb(void); +extern int get_irq(int channel); +#endif /* __NDS_DMAD_INC__ */ From f80f552ddadcfe875739a3dab01476237287fac4 Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Tue, 9 May 2023 18:10:57 +0800 Subject: [PATCH 037/169] mmc: andes: ftsdc010: support legacy MMC driver Support for the old version of the MMC driver that uses the legacy DMAC driver to access the SD card. Signed-off-by: CL Wang --- drivers/mmc/host/Kconfig | 21 +- drivers/mmc/host/Makefile | 3 +- drivers/mmc/host/ftsdc010.c | 1649 +++++++++++++++++++++++++++++++++++ drivers/mmc/host/ftsdc010.h | 260 ++++++ 4 files changed, 1926 insertions(+), 7 deletions(-) create mode 100644 drivers/mmc/host/ftsdc010.c create mode 100644 drivers/mmc/host/ftsdc010.h diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 1bd2b14d20d3e6..53c323d5acf8c8 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -618,21 +618,30 @@ config MMC_DAVINCI If you have an DAVINCI board with a Multimedia Card slot, say Y or M here. If unsure, say N. +config MMC_FTSDC + tristate "Andes SDC Multimedia Card Interface support" + depends on RISCV + depends on ATCDMAC300 + help + This selects the Andes SDC multimedia card interface. + If you have an Andes platform with a multimedia card slot, + say Y or M here. If unsure, say N. + config MMC_FTSDCG - tristate "Andestech SDC Multimedia Card Interface support dmeengine" + tristate "Andestech SDC Multimedia Card Interface use dmaengine" depends on RISCV depends on ATCDMAC300G help - This selects the TI DAVINCI Multimedia card Interface. - If you have an DAVINCI board with a Multimedia Card slot, - say Y or M here. If unsure, say N. + This selects the Andes SDC multimedia card interface use dmaengine. + If you have an Andes platform with a multimedia card slot, + say Y or M here. If unsure, say N. config MMC_FTSDC_DMA bool "DMA mode" - depends on MMC_FTSDCG + depends on MMC_FTSDC || MMC_FTSDCG default y help - This selects the Andestech SDC work in DMA mode. + This selects the Andes SDC work in DMA mode. config MMC_SPI tristate "MMC/SD/SDIO over SPI" diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 6bd63ffff1d6b7..c93514492a09bf 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -32,7 +32,8 @@ obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o -obj-$(CONFIG_MMC_FTSDCG) += ftsdc010g.o +obj-$(CONFIG_MMC_FTSDC) += ftsdc010.o +obj-$(CONFIG_MMC_FTSDCG) += ftsdc010g.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o obj-$(CONFIG_MMC_S3C) += s3cmci.o diff --git a/drivers/mmc/host/ftsdc010.c b/drivers/mmc/host/ftsdc010.c new file mode 100644 index 00000000000000..ed43d596d61ad9 --- /dev/null +++ b/drivers/mmc/host/ftsdc010.c @@ -0,0 +1,1649 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2010 Andestech + * + * 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 "ftsdc010.h" +#include "../core/core.h" +#include +#include + +#define DRIVER_NAME "ftsdc010" +#define REG_READ(addr) readl((host->base + addr)) +#define REG_WRITE(data, addr) writel((data), (host->base + addr)) + +enum dbg_channels { + dbg_err = (1 << 0), + dbg_debug = (1 << 1), + dbg_info = (1 << 2), + dbg_irq = (1 << 3), + dbg_sg = (1 << 4), + dbg_dma = (1 << 5), + dbg_pio = (1 << 6), + dbg_fail = (1 << 7), + dbg_conf = (1 << 8), +}; + +static struct workqueue_struct *mywq; + +static const int dbgmap_err = dbg_fail; +static const int dbgmap_info = dbg_info | dbg_conf; +static const int dbgmap_debug = dbg_err | dbg_debug | dbg_info | dbg_conf; +#define dbg(host, channels, args...) \ +do { \ + if (dbgmap_err & channels) \ + dev_err(&host->pdev->dev, args); \ + else if (dbgmap_info & channels) \ + dev_info(&host->pdev->dev, args); \ + else if (dbgmap_debug & channels) \ + dev_dbg(&host->pdev->dev, args); \ +} while (0) +static void finalize_request(struct ftsdc_host *host); +static void ftsdc_send_request(struct mmc_host *mmc); +#ifdef CONFIG_MMC_DEBUG +static void dbg_dumpregs(struct ftsdc_host *host, char *prefix) +{ + u32 con, cmdarg, r0, r1, r2, r3, rcmd, dcon, dtimer, + dlen, sta, clr, imask, pcon, ccon, bwidth, scon1, scon2, ssta, fea; + + con = REG_READ(SDC_CMD_REG); + cmdarg = REG_READ(SDC_ARGU_REG); + r0 = REG_READ(SDC_RESPONSE0_REG); + r1 = REG_READ(SDC_RESPONSE1_REG); + r2 = REG_READ(SDC_RESPONSE2_REG); + r3 = REG_READ(SDC_RESPONSE3_REG); + rcmd = REG_READ(SDC_RSP_CMD_REG); + dcon = REG_READ(SDC_DATA_CTRL_REG); + dtimer = REG_READ(SDC_DATA_TIMER_REG); + dlen = REG_READ(SDC_DATA_LEN_REG); + sta = REG_READ(SDC_STATUS_REG); + clr = REG_READ(SDC_CLEAR_REG); + imask = REG_READ(SDC_INT_MASK_REG); + pcon = REG_READ(SDC_POWER_CTRL_REG); + ccon = REG_READ(SDC_CLOCK_CTRL_REG); + bwidth = REG_READ(SDC_BUS_WIDTH_REG); + scon1 = REG_READ(SDC_SDIO_CTRL1_REG); + scon2 = REG_READ(SDC_SDIO_CTRL2_REG); + ssta = REG_READ(SDC_SDIO_STATUS_REG); + fea = REG_READ(SDC_FEATURE_REG); + + dbg(host, dbg_debug, + "%s CON:[%08x] STA:[%08x] INT:[%08x], PWR:[%08x], CLK:[%08x]\n", + prefix, con, sta, imask, pcon, ccon); + + dbg(host, dbg_debug, "%s DCON:[%08x] DTIME:[%08x] DLEN:[%08x] DWIDTH:[%08x]\n", prefix, dcon, dtimer, dlen, bwidth); + + dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x] R2:[%08x] R3:[%08x]\n", prefix, r0, r1, r2, r3); + + dbg(host, dbg_debug, "%s SCON1:[%08x] SCON2:[%08x] SSTA:[%08x] FEA:[%08x]\n", prefix, scon1, scon2, ssta, fea); +} + +static void prepare_dbgmsg(struct ftsdc_host *host, struct mmc_command *cmd, + int stop) +{ + snprintf(host->dbgmsg_cmd, 300, + "#%u%s op:%i arg:0x%08x flags:0x08%x retries:%u", + host->ccnt, (stop ? " (STOP)" : ""), + cmd->opcode, cmd->arg, cmd->flags, cmd->retries); + + if (cmd->data) { + snprintf(host->dbgmsg_dat, 300, + "#%u bsize:%u blocks:%u bytes:%u", + host->dcnt, cmd->data->blksz, + cmd->data->blocks, + cmd->data->blocks * cmd->data->blksz); + } else { + host->dbgmsg_dat[0] = '\0'; + } +} + +static void dbg_dumpcmd(struct ftsdc_host *host, struct mmc_command *cmd, + int fail) +{ + unsigned int dbglvl = fail ? dbg_fail : dbg_debug; + + if (!cmd) + return; + + if (cmd->error == 0) { + dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n", + host->dbgmsg_cmd, cmd->resp[0]); + } else { + dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n", + cmd->error, host->dbgmsg_cmd, host->status); + } + + if (!cmd->data) + return; + + if (cmd->data->error == 0) { + dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat); + } else { + dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n", + cmd->data->error, host->dbgmsg_dat, + REG_READ(SDC_DATA_LEN_REG)); + } +} +#else +static void dbg_dumpcmd(struct ftsdc_host *host, + struct mmc_command *cmd, int fail) +{ +} + +static void prepare_dbgmsg(struct ftsdc_host *host, struct mmc_command *cmd, + int stop) +{ +} + +static void dbg_dumpregs(struct ftsdc_host *host, char *prefix) +{ +} + +#endif /* CONFIG_MMC_DEBUG */ + +static inline bool ftsdc_dmaexist(struct ftsdc_host *host) +{ + return (host->dma_req != NULL); +} + +static inline u32 enable_imask(struct ftsdc_host *host, u32 imask) +{ + u32 newmask; + +#ifdef CONFIG_MMC_DEBUG + if (imask & SDC_STATUS_REG_SDIO_INTR) + pr_err("\n*** E ***\n"); +#endif + newmask = REG_READ(SDC_INT_MASK_REG); + newmask |= imask; + + REG_WRITE(newmask, SDC_INT_MASK_REG); + + return newmask; +} + +static inline u32 disable_imask(struct ftsdc_host *host, u32 imask) +{ + u32 newmask; + +#ifdef CONFIG_MMC_DEBUG + if (imask & SDC_STATUS_REG_SDIO_INTR) + pr_err("\n*** D ***\n"); +#endif + newmask = REG_READ(SDC_INT_MASK_REG); + newmask &= ~imask; + + REG_WRITE(newmask, SDC_INT_MASK_REG); + + return newmask; +} + +static inline void clear_imask(struct ftsdc_host *host) +{ + u32 mask = REG_READ(SDC_INT_MASK_REG); + + /* preserve the SDIO IRQ mask state */ + mask &= (SDC_INT_MASK_REG_SDIO_INTR | SDC_INT_MASK_REG_CARD_CHANGE); + REG_WRITE(mask, SDC_INT_MASK_REG); +} + +static inline void get_data_buffer(struct ftsdc_host *host) +{ + struct scatterlist *sg; + + BUG_ON(host->buf_sgptr >= host->mrq->data->sg_len); + sg = &host->mrq->data->sg[host->buf_sgptr]; + host->buf_page = 0; + host->buf_bytes = sg->length; + host->buf_ptr = + host->dodma ? (u32 *) (unsigned long)sg->dma_address : sg_virt(sg); +#ifdef CONFIG_HIGHMEM + if (PageHighMem(sg_page(sg))) { + host->buf_page = sg_page(sg); + host->buf_offset = sg->offset; + if (!host->dodma) + host->buf_ptr = 0; + } +#endif + host->buf_sgptr++; +} + +static inline u32 cal_blksz(unsigned int blksz) +{ + u32 blksztwo = 0; + + while (blksz >>= 1) + blksztwo++; + + return blksztwo; +} + +/** + * ftsdc_enable_irq - enable IRQ, after having disabled it. + * @host: The device state. + * @more: True if more IRQs are expected from transfer. + * + * Enable the main IRQ if needed after it has been disabled. + * + * The IRQ can be one of the following states: + * - enable after data read/write + * - disable when handle data read/write + */ +static void ftsdc_enable_irq(struct ftsdc_host *host, bool enable) +{ + unsigned long flags; + + local_irq_save(flags); + + host->irq_enabled = enable; + + if (enable) + enable_irq(host->irq); + else + disable_irq(host->irq); + + local_irq_restore(flags); +} + +static void do_pio_read(struct ftsdc_host *host) +{ + u32 fifo; + u32 fifo_words; + u32 *ptr = 0; + void *tptr = 0; + u32 status; + u32 retry = 0; + + BUG_ON(host->buf_bytes != 0); + + while (host->buf_sgptr < host->mrq->data->sg_len) { + get_data_buffer(host); + tptr = host->buf_ptr; + while (host->buf_bytes) { + host->page_cnt = host->buf_bytes; + if (host->buf_page != 0) { + host->buf_ptr = kmap_atomic(host->buf_page); + tptr = host->buf_ptr; + tptr += host->buf_offset; + host->page_cnt = + min((u32) (PAGE_SIZE - host->buf_offset), + host->buf_bytes); + host->buf_offset = 0; + } + host->buf_bytes -= host->page_cnt; + ptr = tptr; + while (host->page_cnt) { + status = REG_READ(SDC_STATUS_REG); + if (status & SDC_STATUS_REG_FIFO_OVERRUN) { + fifo = host->fifo_len > host->page_cnt ? + host->page_cnt : host->fifo_len; + dbg(host, dbg_pio, + "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", + fifo, host->page_cnt, + REG_READ(SDC_DATA_LEN_REG)); + host->page_cnt -= fifo; + host->buf_count += fifo; + fifo_words = fifo >> 2; + while (fifo_words--) + *ptr++ = + REG_READ + (SDC_DATA_WINDOW_REG); + if (fifo & 3) { + u32 n = fifo & 3; + u32 data = REG_READ(SDC_DATA_WINDOW_REG); + u8 *p = (u8 *) ptr; + + while (n--) { + *p++ = data; + data >>= 8; + } + } + } else { + udelay(1); + if (++retry >= SDC_PIO_RETRY) { + host->mrq->data->error = -EIO; + goto err; + } + } + } + if (host->buf_page != 0) { + kunmap_atomic(host->buf_ptr); + host->buf_page++; + } + } + } +err: + + host->buf_active = XFER_NONE; + host->complete_what = COMPLETION_FINALIZE; +} + +static void do_pio_write(struct ftsdc_host *host) +{ + u32 fifo; + u32 *ptr; + void *tptr; + u32 status; + u32 retry = 0; + + BUG_ON(host->buf_bytes != 0); + while (host->buf_sgptr < host->mrq->data->sg_len) { + get_data_buffer(host); + dbg(host, dbg_pio, + "pio_write(): new source: [%i]@[%p]\n", + host->buf_bytes, host->buf_ptr); + while (host->buf_bytes) { + host->page_cnt = host->buf_bytes; + tptr = host->buf_ptr; + if (host->buf_page != 0) { + host->buf_ptr = kmap_atomic(host->buf_page); + host->page_cnt = + min((u32) (PAGE_SIZE - host->buf_offset), + host->page_cnt); + tptr = host->buf_ptr; + tptr += host->buf_offset; + host->buf_offset = 0; + } + host->buf_bytes -= host->page_cnt; + ptr = tptr; + while (host->page_cnt) { + status = REG_READ(SDC_STATUS_REG); + if (status & SDC_STATUS_REG_FIFO_UNDERRUN) { + fifo = host->fifo_len > host->page_cnt ? + host->page_cnt : host->fifo_len; + dbg(host, dbg_pio, + "pio_write(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n", + fifo, host->buf_bytes, + REG_READ(SDC_DATA_LEN_REG)); + host->page_cnt -= fifo; + host->buf_count += fifo; + fifo = (fifo + 3) >> 2; + while (fifo--) { + REG_WRITE(*ptr, + SDC_DATA_WINDOW_REG); + ptr++; + } + } else { + udelay(1); + if (++retry >= SDC_PIO_RETRY) { + host->mrq->data->error = -EIO; + goto err; + } + } + } + if (host->buf_page != 0) { + kunmap_atomic(host->buf_ptr); + host->buf_page++; + } + } + } +err: + host->buf_active = XFER_NONE; + host->complete_what = COMPLETION_FINALIZE; +} + +static void do_dma_access(struct ftsdc_host *host) +{ + int res; + unsigned long timeout; + dmad_chreq *req = host->dma_req; + dmad_drb *drb = 0; + + while (host->buf_sgptr < host->mrq->data->sg_len) { + + reinit_completion(&host->dma_complete); + get_data_buffer(host); + + dbg(host, dbg_dma, + "dma_%s(): new target: [%i]@[%p]\n", + host->buf_active == XFER_READ ? "read" : "write", + host->buf_bytes, host->buf_ptr); + + res = dmad_alloc_drb(req, &drb); + + if (res != 0 || (drb == 0)) { + dbg(host, dbg_err, + "%s() Failed to allocate dma request block!\n", + __func__); + host->mrq->data->error = -ENODEV; + goto err; + } + drb->addr0 = host->mem->start + SDC_DATA_WINDOW_REG; + drb->addr1 = (dma_addr_t) (unsigned long)host->buf_ptr; + drb->req_cycle = dmad_bytes_to_cycles(req, host->buf_bytes); + drb->sync = &host->dma_complete; + timeout = SDC_TIMEOUT_BASE * ((host->buf_bytes + 511) >> 9); + res = dmad_submit_request(req, drb, 1); + if (res != 0) { + dbg(host, dbg_err, + "%s() Failed to submit dma request block!\n", + __func__); + host->mrq->data->error = -ENODEV; + goto err; + } + dbg(host, dbg_err, "reach here!\n"); + if (wait_for_completion_timeout(&host->dma_complete, timeout) == + 0) { + dbg(host, dbg_err, "%s: read timeout\n", __func__); + host->mrq->data->error = -ETIMEDOUT; + goto err; + } + } + + host->dma_finish = true; +err: + host->buf_active = XFER_NONE; + host->complete_what = COMPLETION_FINALIZE; +} + +static void ftsdc_work(struct work_struct *work) +{ + struct ftsdc_host *host = container_of(work, struct ftsdc_host, work); + struct mmc_data *data = host->mrq->data; + + int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; + + ftsdc_enable_irq(host, false); + if (host->dodma) { + do_dma_access(host); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } else { + if (host->buf_active == XFER_WRITE) + do_pio_write(host); + + if (host->buf_active == XFER_READ) + do_pio_read(host); + } + + ftsdc_enable_irq(host, true); + tasklet_schedule(&host->pio_tasklet); +} + +static void pio_tasklet(unsigned long data) +{ + struct ftsdc_host *host = (struct ftsdc_host *)data; + + if (host->complete_what == COMPLETION_XFER_PROGRESS) { + queue_work(mywq, (struct work_struct *)&host->work); + return; + } + + if (host->complete_what == COMPLETION_FINALIZE) { + clear_imask(host); + if (host->buf_active != XFER_NONE) { + dbg(host, dbg_err, "unfinished %s - buf_count:[%u] buf_bytes:[%u]\n", + (host->buf_active == XFER_READ) ? "read" : "write", + host->buf_count, host->buf_bytes); + + if (host->mrq->data) + host->mrq->data->error = -EINVAL; + } + + finalize_request(host); + } +} + +static void finalize_request(struct ftsdc_host *host) +{ + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd; + u32 con; + int debug_as_failure = 0; + + if (host->complete_what != COMPLETION_FINALIZE) + return; + + if (!mrq) + return; + + cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + + if (cmd->data && (cmd->error == 0) && (cmd->data->error == 0)) { + if (host->dodma && (!host->dma_finish)) { + dbg(host, dbg_dma, "DMA Missing (%d)!\n", + host->dma_finish); + return; + } + host->dodma = false; + } + + /* Read response from controller. */ + if (cmd->flags & MMC_RSP_136) { + cmd->resp[3] = REG_READ(SDC_RESPONSE0_REG); + cmd->resp[2] = REG_READ(SDC_RESPONSE1_REG); + cmd->resp[1] = REG_READ(SDC_RESPONSE2_REG); + cmd->resp[0] = REG_READ(SDC_RESPONSE3_REG); + } else if (cmd->flags & MMC_RSP_PRESENT) { + cmd->resp[0] = REG_READ(SDC_RESPONSE0_REG); + } + + if (cmd->error) + debug_as_failure = 1; + + if (cmd->data && cmd->data->error) + debug_as_failure = 1; + + dbg_dumpcmd(host, cmd, debug_as_failure); + + clear_imask(host); + con = REG_READ(SDC_STATUS_REG); + con &= ~SDC_CLEAR_REG_SDIO_INTR; + REG_WRITE(con, SDC_CLEAR_REG); + + if (cmd->data && cmd->error) + cmd->data->error = cmd->error; + + if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) { + host->cmd_is_stop = 1; + ftsdc_send_request(host->mmc); + return; + } + + /* If we have no data transfer we are finished here */ + if (!mrq->data) + goto request_done; + + /* Calulate the amout of bytes transfer if there was no error */ + if (mrq->data->error == 0) { + mrq->data->bytes_xfered = + (mrq->data->blocks * mrq->data->blksz); + } else { + mrq->data->bytes_xfered = 0; + } + +request_done: + host->complete_what = COMPLETION_NONE; + host->mrq = NULL; + + host->last_opcode = mrq->cmd->opcode; + mmc_request_done(host->mmc, mrq); +} + +static void ftsdc_send_command(struct ftsdc_host *host, struct mmc_command *cmd) +{ + u32 ccon = 0; + u32 newmask = 0; + u32 scon; + + if (cmd->data) { + host->complete_what = COMPLETION_XFER_PROGRESS; + newmask |= SDC_INT_MASK_REG_RSP_TIMEOUT; + } else if (cmd->flags & MMC_RSP_PRESENT) { + host->complete_what = COMPLETION_RSPFIN; + newmask |= SDC_INT_MASK_REG_RSP_TIMEOUT; + } else { + host->complete_what = COMPLETION_CMDSENT; + newmask |= SDC_INT_MASK_REG_CMD_SEND; + } + + ccon |= cmd->opcode & SDC_CMD_REG_INDEX; + ccon |= SDC_CMD_REG_CMD_EN; + + if (cmd->flags & MMC_RSP_PRESENT) { + ccon |= SDC_CMD_REG_NEED_RSP; + newmask |= SDC_INT_MASK_REG_RSP_CRC_OK | + SDC_INT_MASK_REG_RSP_CRC_FAIL; + } + + if (cmd->flags & MMC_RSP_136) + ccon |= SDC_CMD_REG_LONG_RSP; + + /* applicatiion specific cmd must follow an MMC_APP_CMD. The + * value will be updated in finalize_request function + */ + if (host->last_opcode == MMC_APP_CMD) + ccon |= SDC_CMD_REG_APP_CMD; + + enable_imask(host, newmask); + REG_WRITE(cmd->arg, SDC_ARGU_REG); + + scon = REG_READ(SDC_SDIO_CTRL1_REG); + if (host->mmc->card != NULL && mmc_card_sdio(host->mmc->card)) + scon |= SDC_SDIO_CTRL1_REG_SDIO_ENABLE; + else + scon &= ~SDC_SDIO_CTRL1_REG_SDIO_ENABLE; + REG_WRITE(scon, SDC_SDIO_CTRL1_REG); + + dbg_dumpregs(host, ""); + dbg(host, dbg_debug, "CON[%x]\n", ccon); + + REG_WRITE(ccon, SDC_CMD_REG); +} + +static int ftsdc_setup_data(struct ftsdc_host *host, struct mmc_data *data) +{ + u32 dcon, newmask = 0; + + /* configure data transfer paramter */ + if (!data) + return 0; + if (host->mmc->card + && host->mmc->card->type == (unsigned int)MMC_TYPE_SD) { + if (((data->blksz - 1) & data->blksz) != 0) { + pr_warn + ("%s: can't do non-power-of 2 sized block transfers (blksz %d)\n", + __func__, data->blksz); + return -EINVAL; + } + } + + if (data->blksz <= 2) { + /* We cannot deal with unaligned blocks with more than + * one block being transfered. + */ + + if (data->blocks > 1) { + pr_warn("%s: can't do non-word sized block transfers (blksz %d)\n", + __func__, data->blksz); + return -EINVAL; + } + } + + /* data length */ + dcon = data->blksz * data->blocks; + REG_WRITE(dcon, SDC_DATA_LEN_REG); + + /* write data control */ + dcon = 0; + dcon = cal_blksz(data->blksz); + + /* enable UNDERFUN will trigger interrupt immediatedly + * So setup it when rsp is received successfully + */ + if (data->flags & MMC_DATA_WRITE) { + dcon |= SDC_DATA_CTRL_REG_DATA_WRITE; + } else { + dcon &= ~SDC_DATA_CTRL_REG_DATA_WRITE; + newmask |= SDC_INT_MASK_REG_FIFO_OVERRUN; + } + + /* always reset fifo since last transfer may fail */ + dcon |= SDC_DATA_CTRL_REG_FIFO_RST; + + /* enable data transfer which will be pended until cmd is send */ + dcon |= SDC_DATA_CTRL_REG_DATA_EN; + if (ftsdc_dmaexist(host) && ((data->blksz * data->blocks) & 0xf) == 0) { + newmask &= ~SDC_INT_MASK_REG_FIFO_OVERRUN; + dcon |= SDC_DATA_CTRL_REG_DMA_EN; + dcon |= SDC_DMA_TYPE_4; + host->dodma = true; + + } + REG_WRITE(dcon, SDC_DATA_CTRL_REG); + /* add to IMASK register */ + newmask |= SDC_INT_MASK_REG_DATA_CRC_FAIL | + SDC_INT_MASK_REG_DATA_TIMEOUT; + + enable_imask(host, newmask); + /* handle sdio */ + dcon = + SDC_SDIO_CTRL1_REG_READ_WAIT_ENABLE & REG_READ(SDC_SDIO_CTRL1_REG); + dcon |= data->blksz | data->blocks << 15; + if (data->blocks > 1) + dcon |= SDC_SDIO_CTRL1_REG_SDIO_BLK_MODE; + REG_WRITE(dcon, SDC_SDIO_CTRL1_REG); + + return 0; +} + +#define BOTH_DIR (MMC_DATA_WRITE | MMC_DATA_READ) + +static int ftsdc_prepare_buffer(struct ftsdc_host *host, struct mmc_data *data) +{ + int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; + + if ((!host->mrq) || (!host->mrq->data)) + return -EINVAL; + + BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); + host->buf_sgptr = 0; + host->buf_bytes = 0; + host->buf_count = 0; + host->buf_active = rw ? XFER_WRITE : XFER_READ; + if (host->dodma) { + u32 dma_len; + u32 drb_size; + + dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (dma_len == 0) + return -ENOMEM; + + dmad_config_channel_dir(host->dma_req, + rw ? DMAD_DIR_A1_TO_A0 : + DMAD_DIR_A0_TO_A1); + drb_size = dmad_max_size_per_drb(host->dma_req); + if (drb_size < (data->blksz & data->blocks)) + return -ENODEV; + + host->dma_finish = false; + } + return 0; +} + +static irqreturn_t ftsdc_irq(int irq, void *dev_id) +{ + struct ftsdc_host *host = dev_id; + struct mmc_command *cmd; + u32 mci_status; + u32 mci_clear; + u32 mci_imsk; + unsigned long iflags; + + mci_status = REG_READ(SDC_STATUS_REG); + mci_imsk = REG_READ(SDC_INT_MASK_REG); + + dbg(host, dbg_debug, "irq: status:0x%08x, mask : %08x\n", mci_status, + mci_imsk); + + if (mci_status & SDC_STATUS_REG_SDIO_INTR) { + if (mci_imsk & SDC_INT_MASK_REG_SDIO_INTR) { + mci_clear = SDC_CLEAR_REG_SDIO_INTR; + REG_WRITE(mci_clear, SDC_CLEAR_REG); + + mmc_signal_sdio_irq(host->mmc); + return IRQ_HANDLED; + } + } + + spin_lock_irqsave(&host->complete_lock, iflags); + + mci_status = REG_READ(SDC_STATUS_REG); + mci_clear = 0; + + if (mci_status & SDC_STATUS_REG_CARD_CHANGE) { + if ((mci_status & SDC_STATUS_REG_CARD_DETECT) + == SDC_CARD_INSERT) { + host->status = "card insert"; + mmc_detect_change(host->mmc, msecs_to_jiffies(500)); + } else { + host->status = "card remove"; + } + mci_clear |= SDC_CLEAR_REG_CARD_CHANGE; + dbg(host, dbg_irq, "%s\n", host->status); + + if (host->complete_what != COMPLETION_NONE) { + host->mrq->cmd->error = -EIO; + goto close_transfer; + } + + goto irq_out; + } + + if ((host->complete_what == COMPLETION_NONE) || + (host->complete_what == COMPLETION_FINALIZE)) { + host->status = "nothing to complete"; + mci_clear = -1u; + goto irq_out; + } + + if (!host->mrq) { + host->status = "no active mrq"; + clear_imask(host); + goto irq_out; + } + + cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd; + + if (!cmd) { + host->status = "no active cmd"; + clear_imask(host); + goto irq_out; + } + + if (mci_status & SDC_STATUS_REG_CMD_SEND) { + mci_clear |= SDC_CLEAR_REG_CMD_SEND; + + if (host->complete_what == COMPLETION_CMDSENT) { + host->status = "ok: command sent"; + goto close_transfer; + } else { + host->status = "error: command sent(status not match)"; + cmd->error = -EINVAL; + goto fail_transfer; + } + } + + /* handle error status */ + if (mci_status & SDC_STATUS_REG_RSP_TIMEOUT) { + dbg(host, dbg_err, "CMDSTAT: error RSP TIMEOUT\n"); + mci_clear |= SDC_CLEAR_REG_RSP_TIMEOUT; + cmd->error = -ETIMEDOUT; + host->status = "error: response timeout"; + goto fail_transfer; + } + + if (mci_status & SDC_STATUS_REG_RSP_CRC_FAIL) { + mci_clear |= SDC_CLEAR_REG_RSP_CRC_FAIL; + /* This is wierd hack */ + if (cmd->flags & MMC_RSP_CRC) { + dbg(host, dbg_err, "CMDSTAT: error RSP CRC\n"); + cmd->error = -EILSEQ; + host->status = "error: RSP CRC failed"; + goto fail_transfer; + } else { + host->status = "R3 or R4 type command"; + goto close_transfer; + } + } + + if (mci_status & SDC_STATUS_REG_RSP_CRC_OK) { + mci_clear |= SDC_CLEAR_REG_RSP_CRC_OK; + + if (host->complete_what == COMPLETION_XFER_PROGRESS) { + REG_WRITE(mci_clear, SDC_CLEAR_REG); + + host->status = "RSP recv OK"; + if (!cmd->data) + goto close_transfer; + + if (host->dodma) { + tasklet_schedule(&host->pio_tasklet); + host->status = "dma access"; + goto irq_out; + } + + if (host->buf_active == XFER_WRITE) + enable_imask(host, + SDC_INT_MASK_REG_FIFO_UNDERRUN); + } else if (host->complete_what == COMPLETION_RSPFIN) { + goto close_transfer; + } + } + + /* handler data transfer */ + if (mci_status & SDC_STATUS_REG_DATA_TIMEOUT) { + dbg(host, dbg_err, "CMDSTAT: error DATA TIMEOUT\n"); + mci_clear |= SDC_CLEAR_REG_DATA_TIMEOUT; + cmd->error = -ETIMEDOUT; + host->status = "error: data timeout"; + goto fail_transfer; + } + + if (mci_status & SDC_STATUS_REG_DATA_CRC_FAIL) { + dbg(host, dbg_err, "CMDSTAT: error DATA CRC\n"); + mci_clear |= SDC_CLEAR_REG_DATA_CRC_FAIL; + cmd->error = -EILSEQ; + host->status = "error: data CRC fail"; + goto fail_transfer; + } + + if ((mci_status & SDC_STATUS_REG_FIFO_UNDERRUN) || + mci_status & SDC_STATUS_REG_FIFO_OVERRUN) { + + disable_imask(host, SDC_INT_MASK_REG_FIFO_OVERRUN | + SDC_INT_MASK_REG_FIFO_UNDERRUN); + + if (!host->dodma) { + if (host->buf_active == XFER_WRITE) { + tasklet_schedule(&host->pio_tasklet); + host->status = "pio tx"; + } else if (host->buf_active == XFER_READ) { + + tasklet_schedule(&host->pio_tasklet); + host->status = "pio rx"; + } + } + } + + goto irq_out; + +fail_transfer: + host->buf_active = XFER_NONE; + +close_transfer: + host->complete_what = COMPLETION_FINALIZE; + + clear_imask(host); + tasklet_schedule(&host->pio_tasklet); + +irq_out: + REG_WRITE(mci_clear, SDC_CLEAR_REG); + + dbg(host, dbg_debug, "irq: %s\n", host->status); + spin_unlock_irqrestore(&host->complete_lock, iflags); + return IRQ_HANDLED; +} + +static void ftsdc_send_request(struct mmc_host *mmc) +{ + struct ftsdc_host *host = mmc_priv(mmc); + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + + host->ccnt++; + prepare_dbgmsg(host, cmd, host->cmd_is_stop); + dbg(host, dbg_debug, "%s\n", host->dbgmsg_cmd); + + if (cmd->data) { + int res = ftsdc_setup_data(host, cmd->data); + + host->dcnt++; + + if (res) { + dbg(host, dbg_err, "setup data error %d\n", res); + cmd->error = res; + cmd->data->error = res; + + mmc_request_done(mmc, mrq); + return; + } + + res = ftsdc_prepare_buffer(host, cmd->data); + + if (res) { + dbg(host, dbg_err, "data prepare error %d\n", res); + cmd->error = res; + cmd->data->error = res; + + mmc_request_done(mmc, mrq); + return; + } + } + + /* Send command */ + ftsdc_send_command(host, cmd); +} + +static int ftsdc_get_cd(struct mmc_host *mmc) +{ + struct ftsdc_host *host = mmc_priv(mmc); + u32 con = REG_READ(SDC_STATUS_REG); + + dbg(host, dbg_debug, "get_cd status:%.8x\n\n", con); + + return (con & SDC_STATUS_REG_CARD_DETECT) ? 0 : 1; +} + +static void ftsdc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct ftsdc_host *host = mmc_priv(mmc); + + host->status = "mmc request"; + host->cmd_is_stop = 0; + host->mrq = mrq; + if (ftsdc_get_cd(mmc) == 0) { + dbg(host, dbg_err, "%s: no medium present\n", __func__); + host->mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + } else { + ftsdc_send_request(mmc); + } + dbg(host, dbg_debug, "send request\n"); +} + +static void ftsdc_set_clk(struct ftsdc_host *host, struct mmc_ios *ios) +{ + u32 clk_div = 0; + u32 con; + struct ftsdc_mmc_config *pdata = host->pdev->dev.platform_data; + u32 freq = pdata->max_freq; + + dbg(host, dbg_debug, "request clk : %u\n", ios->clock); + con = REG_READ(SDC_CLOCK_CTRL_REG); + if (ios->clock == 0) { + host->real_rate = 0; + con |= SDC_CLOCK_CTRL_REG_CLK_DIS; + } else { + clk_div = (freq / (ios->clock << 1)) - 1; + host->real_rate = freq / ((clk_div + 1) << 1); + if (host->real_rate > ios->clock) { + ++clk_div; + host->real_rate = freq / ((clk_div + 1) << 1); + } + if (clk_div > 127) + dbg(host, dbg_err, "%s: no match clock rate, %u\n", + __func__, ios->clock); + + con = + (con & ~SDC_CLOCK_CTRL_REG_CLK_DIV) | (clk_div & + SDC_CLOCK_CTRL_REG_CLK_DIV); + con &= ~SDC_CLOCK_CTRL_REG_CLK_DIS; + } + + REG_WRITE(con, SDC_CLOCK_CTRL_REG); +} + +static void ftsdc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct ftsdc_host *host = mmc_priv(mmc); + u32 con; + + con = REG_READ(SDC_POWER_CTRL_REG); + switch (ios->power_mode) { + case MMC_POWER_ON: + case MMC_POWER_UP: + con |= SDC_POWER_CTRL_REG_POWER_ON; + break; + case MMC_POWER_OFF: + default: + con &= ~SDC_POWER_CTRL_REG_POWER_ON; + break; + } + + REG_WRITE(con, SDC_POWER_CTRL_REG); + + ftsdc_set_clk(host, ios); + + if ((ios->power_mode == MMC_POWER_ON) || + (ios->power_mode == MMC_POWER_UP)) { + dbg(host, dbg_debug, "running at %ukHz (requested: %ukHz).\n", + host->real_rate / 1000, ios->clock / 1000); + } else { + dbg(host, dbg_debug, "powered down.\n"); + } + + host->bus_width = ios->bus_width; + /* write bus configure */ + con = REG_READ(SDC_BUS_WIDTH_REG); + + con &= ~(SDC_BUS_WIDTH_REG_SINGLE_BUS | + SDC_BUS_WIDTH_REG_WIDE_4_BUS | SDC_BUS_WIDTH_REG_WIDE_8_BUS); + if (host->bus_width == MMC_BUS_WIDTH_1) + con |= SDC_BUS_WIDTH_REG_SINGLE_BUS; + else if (host->bus_width == MMC_BUS_WIDTH_4) + con |= SDC_BUS_WIDTH_REG_WIDE_4_BUS; + else if (host->bus_width == MMC_BUS_WIDTH_8) + con |= SDC_BUS_WIDTH_REG_WIDE_8_BUS; + else + dbg(host, dbg_err, "set_ios: can't support bus mode"); + + REG_WRITE(con, SDC_BUS_WIDTH_REG); + + /*set rsp and data timeout */ + con = -1; + REG_WRITE(con, SDC_DATA_TIMER_REG); + if (ios->power_mode == MMC_POWER_UP) + mmc_delay(250); +} + +static int ftsdc_get_ro(struct mmc_host *mmc) +{ + struct ftsdc_host *host = mmc_priv(mmc); + u32 con = REG_READ(SDC_STATUS_REG); + + dbg(host, dbg_debug, "get_ro status:%.8x\n", con); + + return (con & SDC_STATUS_REG_CARD_LOCK) ? 1 : 0; +} + +static void ftsdc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct ftsdc_host *host = mmc_priv(mmc); + unsigned long flags; + u32 con; +#ifdef CONFIG_MMC_DEBUG + u32 ena; +#endif + + local_irq_save(flags); + + con = REG_READ(SDC_INT_MASK_REG); +#ifdef CONFIG_MMC_DEBUG + ena = (con & SDC_STATUS_REG_SDIO_INTR) ? 1 : 0; + if (ena == enable) + pr_err("\n**ena == enable\n"); +#endif + + con = + enable ? (con | SDC_STATUS_REG_SDIO_INTR) : (con & + ~SDC_STATUS_REG_SDIO_INTR); + REG_WRITE(con, SDC_INT_MASK_REG); + +#ifdef CONFIG_MMC_DEBUG + //check and ensure data out to SD host controller + ena = (REG_READ(SDC_INT_MASK_REG) & SDC_STATUS_REG_SDIO_INTR) ? 1 : 0; + if (ena != enable) + pr_err("\n**ena != enable\n"); + +#endif + + local_irq_restore(flags); +} + +static struct mmc_host_ops ftsdc_ops = { + .request = ftsdc_request, + .set_ios = ftsdc_set_ios, + .get_ro = ftsdc_get_ro, + .get_cd = ftsdc_get_cd, + .enable_sdio_irq = ftsdc_enable_sdio_irq, +}; + +#ifdef CONFIG_DEBUG_FS + +static int ftsdc_state_show(struct seq_file *seq, void *v) +{ + struct ftsdc_host *host = seq->private; + + seq_printf(seq, "Register base = 0x%08x\n", + (u32) ((unsigned long)host->base)); + seq_printf(seq, "Clock rate = %u\n", host->real_rate); + seq_printf(seq, "host status = %s\n", host->status); + seq_printf(seq, "IRQ = %d\n", host->irq); + seq_printf(seq, "IRQ enabled = %d\n", host->irq_enabled); + seq_printf(seq, "complete what = %d\n", host->complete_what); + seq_printf(seq, "dma support = %d\n", ftsdc_dmaexist(host)); + seq_printf(seq, "use dma = %d\n", host->dodma); + + return 0; +} + +static int ftsdc_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, ftsdc_state_show, inode->i_private); +} + +static const struct file_operations ftsdc_fops_state = { + .owner = THIS_MODULE, + .open = ftsdc_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define DBG_REG(_r) { .addr = SDC_## _r ## _REG, .name = #_r } + +static struct ftsdc_reg { + unsigned short addr; + unsigned char *name; +} debug_regs[] = { + DBG_REG(CMD), + DBG_REG(ARGU), + DBG_REG(RESPONSE0), + DBG_REG(RESPONSE1), + DBG_REG(RESPONSE2), + DBG_REG(RESPONSE3), + DBG_REG(RSP_CMD), + DBG_REG(DATA_CTRL), + DBG_REG(DATA_TIMER), + DBG_REG(DATA_LEN), + DBG_REG(STATUS), + DBG_REG(CLEAR), + DBG_REG(INT_MASK), + DBG_REG(POWER_CTRL), + DBG_REG(CLOCK_CTRL), + DBG_REG(BUS_WIDTH), + DBG_REG(SDIO_CTRL1), + DBG_REG(SDIO_CTRL2), + DBG_REG(SDIO_STATUS), + DBG_REG(FEATURE), + DBG_REG(REVISION), + { } +}; + +static int ftsdc_regs_show(struct seq_file *seq, void *v) +{ + struct ftsdc_host *host = seq->private; + struct ftsdc_reg *rptr = debug_regs; + + for (; rptr->name; rptr++) + seq_printf(seq, "SDI%s\t=0x%08x\n", rptr->name, + REG_READ(rptr->addr)); + + return 0; +} + +static int ftsdc_regs_open(struct inode *inode, struct file *file) +{ + return single_open(file, ftsdc_regs_show, inode->i_private); +} + +static const struct file_operations ftsdc_fops_regs = { + .owner = THIS_MODULE, + .open = ftsdc_regs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void ftsdc_debugfs_attach(struct ftsdc_host *host) +{ + struct device *dev = &host->pdev->dev; + + host->debug_root = debugfs_create_dir(dev_name(dev), NULL); + if (IS_ERR(host->debug_root)) { + dev_err(dev, "failed to create debugfs root\n"); + return; + } + + host->debug_state = debugfs_create_file("state", 0444, + host->debug_root, host, + &ftsdc_fops_state); + + if (IS_ERR(host->debug_state)) + dev_err(dev, "failed to create debug state file\n"); + + host->debug_regs = debugfs_create_file("regs", 0444, + host->debug_root, host, + &ftsdc_fops_regs); + + if (IS_ERR(host->debug_regs)) + dev_err(dev, "failed to create debug regs file\n"); +} + +static void ftsdc_debugfs_remove(struct ftsdc_host *host) +{ + debugfs_remove(host->debug_regs); + debugfs_remove(host->debug_state); + debugfs_remove(host->debug_root); +} + +#else +static inline void ftsdc_debugfs_attach(struct ftsdc_host *host) +{ +} + +static inline void ftsdc_debugfs_remove(struct ftsdc_host *host) +{ +} + +#endif /* CONFIG_DEBUG_FS */ + +#if (defined(CONFIG_PLATFORM_AHBDMA) || defined(CONFIG_PLATFORM_APBDMA)) +static int ftsdc_alloc_dma(struct ftsdc_host *host) +{ + dmad_chreq *req = host->dma_req; + + req = kzalloc(sizeof(dmad_chreq), GFP_KERNEL); +#ifdef CONFIG_PLATFORM_APBDMA + req->apb_req.addr0_ctrl = APBBR_ADDRINC_FIXED; /* (in) APBBR_ADDRINC_xxx */ +/* for amerald */ +#if !defined(CONFIG_PLAT_AE3XX) + if ((inl(pmu_base) & AMERALD_MASK) == AMERALD_PRODUCT_ID) { + req->apb_req.addr0_reqn = APBBR_REQN_SDC_AMERALD; + } else +#endif + { + req->apb_req.addr0_reqn = APBBR_REQN_SDC; /* (in) APBBR_REQN_xxx (also used to help determine bus selection) */ + } + req->apb_req.addr1_ctrl = APBBR_ADDRINC_I4X; /* (in) APBBR_ADDRINC_xxx */ + req->apb_req.addr1_reqn = APBBR_REQN_NONE; /* (in) APBBR_REQN_xxx (also used to help determine bus selection) */ + req->apb_req.burst_mode = 1; /* (in) Burst mode (0: no burst 1-, 1: burst 4- data cycles per dma cycle) */ + req->apb_req.data_width = APBBR_DATAWIDTH_4; /* (in) APBBR_DATAWIDTH_4(word), APBBR_DATAWIDTH_2(half-word), APBBR_DATAWIDTH_1(byte) */ + req->apb_req.tx_dir = DMAD_DIR_A0_TO_A1; /* (in) DMAD_DIR_A0_TO_A1, DMAD_DIR_A1_TO_A0 */ + req->controller = DMAD_DMAC_APB_CORE; /* (in) DMAD_DMAC_AHB_CORE, DMAD_DMAC_APB_CORE */ + req->flags = DMAD_FLAGS_SLEEP_BLOCK | DMAD_FLAGS_BIDIRECTION; + + if (dmad_channel_alloc(req) == 0) { + dbg(host, dbg_debug, "%s: APB dma channel allocated (ch: %d)\n", + __func__, req->channel); + host->dma_req = req; + return 0; + } + + memset(req, 0, sizeof(dmad_chreq)); + dbg(host, dbg_info, "%s: APB dma channel allocation failed\n", + __func__); +#endif /* CONFIG_PLATFORM_APBDMA */ + +#ifdef CONFIG_PLATFORM_AHBDMA + req->ahb_req.sync = 1; /* (in) non-zero if src and dst have different clock domain */ + req->ahb_req.priority = DMAC_CSR_CHPRI_1; /* (in) DMAC_CSR_CHPRI_0 (lowest) ~ DMAC_CSR_CHPRI_3 (highest) */ + req->ahb_req.hw_handshake = 1; /* (in) non-zero to enable hardware handshake mode */ + req->ahb_req.burst_size = DMAC_CSR_SIZE_4; /* (in) DMAC_CSR_SIZE_1 ~ DMAC_CSR_SIZE_256 */ + req->ahb_req.addr0_width = DMAC_CSR_WIDTH_32; /* (in) DMAC_CSR_WIDTH_8, DMAC_CSR_WIDTH_16, or DMAC_CSR_WIDTH_32 */ + req->ahb_req.addr0_ctrl = DMAC_CSR_AD_FIX; /* (in) DMAC_CSR_AD_INC, DMAC_CSR_AD_DEC, or DMAC_CSR_AD_FIX */ + req->ahb_req.addr0_reqn = DMAC_REQN_SDC; /* (in) DMAC_REQN_xxx (also used to help determine channel number) */ + req->ahb_req.addr1_width = DMAC_CSR_WIDTH_32; /* (in) DMAC_CSR_WIDTH_8, DMAC_CSR_WIDTH_16, or DMAC_CSR_WIDTH_32 */ + req->ahb_req.addr1_ctrl = DMAC_CSR_AD_INC; /* (in) DMAC_CSR_AD_INC, DMAC_CSR_AD_DEC, or DMAC_CSR_AD_FIX */ + req->ahb_req.addr1_reqn = DMAC_REQN_NONE; /* (in) DMAC_REQN_xxx (also used to help determine channel number) */ + req->ahb_req.tx_dir = DMAD_DIR_A0_TO_A1; /* (in) DMAD_DIR_A0_TO_A1, DMAD_DIR_A1_TO_A0 */ + + req->controller = DMAD_DMAC_AHB_CORE; /* (in) DMAD_DMAC_AHB_CORE, DMAD_DMAC_APB_CORE */ + req->flags = DMAD_FLAGS_SLEEP_BLOCK | DMAD_FLAGS_BIDIRECTION; + + if (dmad_channel_alloc(req) == 0) { + dbg(host, dbg_debug, "%s: AHB dma channel allocated (ch: %d)\n", + __func__, req->channel); + host->dma_req = req; + return 0; + } + dbg(host, dbg_info, "%s: AHB dma channel allocation failed\n", + __func__); +#endif + + kfree(req); + return -ENODEV; + +} +#endif + +enum { + MMC_CTLR_VERSION_1 = 0, + MMC_CTLR_VERSION_2, +}; + +static struct platform_device_id ftsdc_mmc_devtype[] = { + { + .name = "ag101p", + .driver_data = MMC_CTLR_VERSION_1, + }, { + .name = "ae3xx", + .driver_data = MMC_CTLR_VERSION_2, + }, + { }, +}; + +MODULE_DEVICE_TABLE(platform, ftsdc_mmc_devtype); + +static const struct of_device_id ftsdc_mmc_dt_ids[] = { + { + .compatible = "andestech,atfsdc010", + .data = &ftsdc_mmc_devtype[MMC_CTLR_VERSION_1], + }, + { + .compatible = "andestech,atfsdc010", + .data = &ftsdc_mmc_devtype[MMC_CTLR_VERSION_2], + }, + { }, +}; + +MODULE_DEVICE_TABLE(of, ftsdc_mmc_dt_ids); + +static struct ftsdc_mmc_config +*mmc_parse_pdata(struct platform_device *pdev) +{ + struct device_node *np; + struct ftsdc_mmc_config *pdata = pdev->dev.platform_data; + const struct of_device_id *match = + of_match_device(of_match_ptr(ftsdc_mmc_dt_ids), &pdev->dev); + u32 data; + + np = pdev->dev.of_node; + if (!np) + return pdata; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdev->dev.platform_data = (void *)pdata; + + if (!pdata) { + dev_err(&pdev->dev, + "Failed to allocate memory for struct ftsdc_mmc_config\n"); + goto nodata; + } + + if (match) + pdev->id_entry = match->data; + + if (of_property_read_u32(np, "max-frequency", &pdata->max_freq)) + dev_info(&pdev->dev, + "'max-frequency' property not specified, defaulting to 25MHz\n"); + + of_property_read_u32(np, "bus-width", &data); + switch (data) { + case 1: + case 4: + case 8: + pdata->wires = data; + break; + default: + pdata->wires = 1; + + /* dev_info(&pdev->dev, "Unsupported buswidth, defaulting to 1 bit\n"); */ + } +nodata: + return pdata; +} + +static int __init ftsdc_probe(struct platform_device *pdev) +{ + int (*read_fixup)(void __iomem *addr, unsigned int val, + unsigned int shift_bits); + struct ftsdc_host *host; + struct mmc_host *mmc; + struct ftsdc_mmc_config *pdata = NULL; + struct resource *r, *mem = NULL; + int ret = -ENOMEM; + u32 con; + int irq = 0; + size_t mem_size; + + pdata = mmc_parse_pdata(pdev); + if (pdata == NULL) { + dev_err(&pdev->dev, "Couldn't get platform data\n"); + return -ENOENT; + } + ret = -ENODEV; + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + + if (!r || irq < 0) + goto probe_out; + + ret = -EBUSY; + mem_size = resource_size(r); + mem = request_mem_region(r->start, mem_size, pdev->name); + + if (!mem) { + dev_err(&pdev->dev, + "failed to get io memory region resouce.\n"); + goto probe_out; + } + ret = -ENOMEM; + mmc = mmc_alloc_host(sizeof(struct ftsdc_host), &pdev->dev); + if (!mmc) + goto probe_out; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->pdev = pdev; + mywq = create_workqueue("atcsdc_queue"); + if (mywq == NULL) + goto probe_free_host; + + spin_lock_init(&host->complete_lock); + tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long)host); + init_completion(&host->dma_complete); + INIT_WORK(&host->work, ftsdc_work); + + host->complete_what = COMPLETION_NONE; + host->buf_active = XFER_NONE; + + host->mem = mem; + host->base = (void __iomem *)ioremap(mem->start, mem_size); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto probe_free_mem_region; + } + + /* Check revision register */ + read_fixup = symbol_get(readl_fixup); + ret = read_fixup(host->base + SDC_REVISION_REG, 0x00030107, 0); + symbol_put(readl_fixup); + if (!ret) { + dev_err(&pdev->dev, + "failed to read revision reg, bitmap not support ftdsdc\n"); + ret = -ENXIO; + goto probe_free_mem_region; + } + + host->irq = irq; + + ret = request_irq(host->irq, ftsdc_irq, 0, DRIVER_NAME, host); + if (ret) { + dev_err(&pdev->dev, "failed to request mci interrupt.\n"); + ret = -ENOENT; + goto probe_free_mem_region; + } + host->irq_enabled = true; + /* enable card change interruption */ + con = REG_READ(SDC_INT_MASK_REG); + con |= SDC_INT_MASK_REG_CARD_CHANGE; + REG_WRITE(con, SDC_INT_MASK_REG); + + con = REG_READ(SDC_BUS_WIDTH_REG); + mmc->ops = &ftsdc_ops; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + + if (con & SDC_WIDE_4_BUS_SUPPORT) + mmc->caps |= MMC_CAP_4_BIT_DATA; + else if (con & SDC_WIDE_8_BUS_SUPPORT) + mmc->caps |= MMC_CAP_8_BIT_DATA; + +#if (defined(CONFIG_PLATFORM_AHBDMA) || defined(CONFIG_PLATFORM_APBDMA)) +#ifdef CONFIG_MMC_FTSDC_DMA + ftsdc_alloc_dma(host); +#endif +#endif + +#ifndef A320D_BUILDIN_SDC + mmc->caps |= MMC_CAP_SDIO_IRQ; +#endif + mmc->f_min = pdata->max_freq / (2 * 128); + mmc->f_max = pdata->max_freq / 2; + /* limit SDIO mode max size */ + mmc->max_req_size = 128 * 1024 * 1024 - 1; + mmc->max_blk_size = 2047; + mmc->max_req_size = (mmc->max_req_size + 1) / (mmc->max_blk_size + 1); + mmc->max_seg_size = mmc->max_req_size; + mmc->max_blk_count = (1 << 17) - 1; + + /* kernel default value. see Doc/block/biodocs.txt */ + /* 'struct mmc_host' has no member named 'max_phys_segs' + *'struct mmc_host' has no member named 'max_hw_segs' + */ + /* mmc->max_phys_segs = 128; + * mmc->max_hw_segs = 128; + */ + + /* set fifo lenght and default threshold half */ + con = REG_READ(SDC_FEATURE_REG); + host->fifo_len = (con & SDC_FEATURE_REG_FIFO_DEPTH) * sizeof(u32); + + dbg(host, dbg_debug, + "probe: mapped mci_base:%p irq:%u.\n", host->base, host->irq); + + dbg_dumpregs(host, ""); + ret = mmc_add_host(mmc); + if (ret) { + dev_err(&pdev->dev, "failed to add mmc host.\n"); + goto probe_free_irq; + } + ftsdc_debugfs_attach(host); + platform_set_drvdata(pdev, mmc); + dev_info(&pdev->dev, "%s - using %s SDIO IRQ\n", mmc_hostname(mmc), + mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw"); + + return 0; + +probe_free_irq: + free_irq(host->irq, host); + +probe_free_mem_region: + release_mem_region(host->mem->start, resource_size(host->mem)); + destroy_workqueue(mywq); + +probe_free_host: + mmc_free_host(mmc); + +probe_out: + return ret; +} + +static void ftsdc_shutdown(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct ftsdc_host *host = mmc_priv(mmc); + + flush_workqueue(mywq); + destroy_workqueue(mywq); + + ftsdc_debugfs_remove(host); + mmc_remove_host(mmc); +} + +static int __exit ftsdc_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct ftsdc_host *host = mmc_priv(mmc); + + ftsdc_shutdown(pdev); + + tasklet_disable(&host->pio_tasklet); + + if (ftsdc_dmaexist(host)) + kfree(host->dma_req); + + free_irq(host->irq, host); + + iounmap(host->base); + release_mem_region(host->mem->start, resource_size(host->mem)); + + mmc_free_host(mmc); + return 0; +} + +#ifdef CONFIG_PM +static int ftsdc_free_dma(struct ftsdc_host *host) +{ + dmad_channel_free(host->dma_req); + return 0; +} + +static int ftsdc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct ftsdc_host *host = mmc_priv(mmc); + int ret = 0; + + if (mmc) + ftsdc_free_dma(host); + + return ret; + +} + +static int ftsdc_resume(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + int ret = 0; + struct ftsdc_host *host = mmc_priv(mmc); + + if (mmc) { +#if (defined(CONFIG_PLATFORM_AHBDMA) || defined(CONFIG_PLATFORM_APBDMA)) + ftsdc_alloc_dma(host); +#endif + } + return ret; +} + +#else +#define ftsdc_suspend NULL +#define ftsdc_resume NULL +#endif + +static struct platform_driver ftsdc_driver = { + .driver = { + .name = "ftsdc010", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ftsdc_mmc_dt_ids), + }, + .remove = __exit_p(ftsdc_remove), + .shutdown = ftsdc_shutdown, + .suspend = ftsdc_suspend, + .resume = ftsdc_resume, +}; + +module_platform_driver_probe(ftsdc_driver, ftsdc_probe); +MODULE_DESCRIPTION("Andestech Leopard MMC/SD Card Interface driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/ftsdc010.h b/drivers/mmc/host/ftsdc010.h new file mode 100644 index 00000000000000..74342c14bf5faa --- /dev/null +++ b/drivers/mmc/host/ftsdc010.h @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Andes MMC/SD driver + * Andes FTSDC010 Device Driver + * + * Copyright (C) 2005 Andes Technology Corporation. + */ +#ifndef _FTSDC010_H_ +#define _FTSDC010_H_ + +#define DELAY_FOR_DMA_READ + +#ifdef SD_DEBUG +#define P_DEBUG(fmt, args...) pr_alert("SD: " fmt, ## args) +#else +#define P_DEBUG(a...) +#endif +#define P_DEBUGG(a...) + +/* + * Used for dma timeout. + * Unit is 500 ms. + */ +#define SDC_TIMEOUT_BASE (HZ / 2) + +/* Used for pio retry times */ +#define SDC_PIO_RETRY 0x300000 + +/* SD controller register */ +#define SDC_CMD_REG 0x00000000 +#define SDC_ARGU_REG 0x00000004 +#define SDC_RESPONSE0_REG 0x00000008 +#define SDC_RESPONSE1_REG 0x0000000C +#define SDC_RESPONSE2_REG 0x00000010 +#define SDC_RESPONSE3_REG 0x00000014 +#define SDC_RSP_CMD_REG 0x00000018 +#define SDC_DATA_CTRL_REG 0x0000001C +#define SDC_DATA_TIMER_REG 0x00000020 +#define SDC_DATA_LEN_REG 0x00000024 +#define SDC_STATUS_REG 0x00000028 +#define SDC_CLEAR_REG 0x0000002C +#define SDC_INT_MASK_REG 0x00000030 +#define SDC_POWER_CTRL_REG 0x00000034 +#define SDC_CLOCK_CTRL_REG 0x00000038 +#define SDC_BUS_WIDTH_REG 0x0000003C +#define SDC_DATA_WINDOW_REG 0x00000040 + +#ifdef A320D_BUILDIN_SDC +#define SDC_FEATURE_REG 0x00000044 +#define SDC_REVISION_REG 0x00000048 +#else +#define SDC_MMC_INT_RSP_REG 0x00000044 +#define SDC_GP_OUTPUT_REG 0x00000048 +#define SDC_FEATURE_REG 0x0000009C +#define SDC_REVISION_REG 0x000000A0 +#endif + +#define SDC_SDIO_CTRL1_REG 0x0000006C +#define SDC_SDIO_CTRL2_REG 0x00000070 +#define SDC_SDIO_STATUS_REG 0x00000074 + +/* bit mapping of command register */ +#define SDC_CMD_REG_INDEX 0x0000003F +#define SDC_CMD_REG_NEED_RSP 0x00000040 +#define SDC_CMD_REG_LONG_RSP 0x00000080 +#define SDC_CMD_REG_APP_CMD 0x00000100 +#define SDC_CMD_REG_CMD_EN 0x00000200 +#define SDC_CMD_REG_SDC_RST 0x00000400 +#define SDC_CMD_MMC_INT_STOP 0x00000800 + +/* bit mapping of response command register */ +#define SDC_RSP_CMD_REG_INDEX 0x0000003F +#define SDC_RSP_CMD_REG_APP 0x00000040 + +/* bit mapping of data control register */ +#define SDC_DATA_CTRL_REG_BLK_SIZE 0x0000000F +#define SDC_DATA_CTRL_REG_DATA_WRITE 0x00000010 +#define SDC_DATA_CTRL_REG_DMA_EN 0x00000020 +#define SDC_DATA_CTRL_REG_DATA_EN 0x00000040 +#define SDC_DATA_CTRL_REG_FIFOTH 0x00000080 +#define SDC_DATA_CTRL_REG_DMA_TYPE 0x00000300 +#define SDC_DATA_CTRL_REG_FIFO_RST 0x00000400 +#define SDC_CPRM_DATA_CHANGE_ENDIAN_EN 0x00000800 +#define SDC_CPRM_DATA_SWAP_HL_EN 0x00001000 + +#define SDC_DMA_TYPE_1 0x00000000 +#define SDC_DMA_TYPE_4 0x00000100 +#define SDC_DMA_TYPE_8 0x00000200 + +/* bit mapping of status register */ +#define SDC_STATUS_REG_RSP_CRC_FAIL 0x00000001 +#define SDC_STATUS_REG_DATA_CRC_FAIL 0x00000002 +#define SDC_STATUS_REG_RSP_TIMEOUT 0x00000004 +#define SDC_STATUS_REG_DATA_TIMEOUT 0x00000008 +#define SDC_STATUS_REG_RSP_CRC_OK 0x00000010 +#define SDC_STATUS_REG_DATA_CRC_OK 0x00000020 +#define SDC_STATUS_REG_CMD_SEND 0x00000040 +#define SDC_STATUS_REG_DATA_END 0x00000080 +#define SDC_STATUS_REG_FIFO_UNDERRUN 0x00000100 +#define SDC_STATUS_REG_FIFO_OVERRUN 0x00000200 +#define SDC_STATUS_REG_CARD_CHANGE 0x00000400 +#define SDC_STATUS_REG_CARD_DETECT 0x00000800 +#define SDC_STATUS_REG_CARD_LOCK 0x00001000 +#define SDC_STATUS_REG_CP_READY 0x00002000 +#define SDC_STATUS_REG_CP_BUF_READY 0x00004000 +#define SDC_STATUS_REG_PLAIN_TEXT_READY 0x00008000 +#define SDC_STATUS_REG_SDIO_INTR 0x00010000 + +/* bit mapping of clear register */ +#define SDC_CLEAR_REG_RSP_CRC_FAIL 0x00000001 +#define SDC_CLEAR_REG_DATA_CRC_FAIL 0x00000002 +#define SDC_CLEAR_REG_RSP_TIMEOUT 0x00000004 +#define SDC_CLEAR_REG_DATA_TIMEOUT 0x00000008 +#define SDC_CLEAR_REG_RSP_CRC_OK 0x00000010 +#define SDC_CLEAR_REG_DATA_CRC_OK 0x00000020 +#define SDC_CLEAR_REG_CMD_SEND 0x00000040 +#define SDC_CLEAR_REG_DATA_END 0x00000080 +#define SDC_CLEAR_REG_CARD_CHANGE 0x00000400 +#define SDC_CLEAR_REG_SDIO_INTR 0x00010000 + +/* bit mapping of int_mask register */ +#define SDC_INT_MASK_REG_RSP_CRC_FAIL 0x00000001 +#define SDC_INT_MASK_REG_DATA_CRC_FAIL 0x00000002 +#define SDC_INT_MASK_REG_RSP_TIMEOUT 0x00000004 +#define SDC_INT_MASK_REG_DATA_TIMEOUT 0x00000008 +#define SDC_INT_MASK_REG_RSP_CRC_OK 0x00000010 +#define SDC_INT_MASK_REG_DATA_CRC_OK 0x00000020 +#define SDC_INT_MASK_REG_CMD_SEND 0x00000040 +#define SDC_INT_MASK_REG_DATA_END 0x00000080 +#define SDC_INT_MASK_REG_FIFO_UNDERRUN 0x00000100 +#define SDC_INT_MASK_REG_FIFO_OVERRUN 0x00000200 +#define SDC_INT_MASK_REG_CARD_CHANGE 0x00000400 +#define SDC_INT_MASK_REG_CARD_LOCK 0x00001000 +#define SDC_INT_MASK_REG_CP_READY 0x00002000 +#define SDC_INT_MASK_REG_CP_BUF_READY 0x00004000 +#define SDC_INT_MASK_REG_PLAIN_TEXT_READY 0x00008000 +#define SDC_INT_MASK_REG_SDIO_INTR 0x00010000 + +#define SDC_CARD_INSERT 0x0 +#define SDC_CARD_REMOVE SDC_STATUS_REG_CARD_DETECT + +/* bit mapping of power control register */ +#define SDC_POWER_CTRL_REG_POWER_ON 0x00000010 +#define SDC_POWER_CTRL_REG_POWER_BITS 0x0000000F + +/* bit mapping of clock control register */ +#define SDC_CLOCK_CTRL_REG_CLK_DIV 0x0000007F +#define SDC_CLOCK_CTRL_REG_CARD_TYPE 0x00000080 +#define SDC_CLOCK_CTRL_REG_CLK_DIS 0x00000100 + +/* card type */ +#define SDC_CARD_TYPE_SD SDC_CLOCK_REG_CARD_TYPE +#define SDC_CARD_TYPE_MMC 0x0 + +/* bit mapping of bus width register */ +#define SDC_BUS_WIDTH_REG_SINGLE_BUS 0x00000001 +#define SDC_BUS_WIDTH_REG_WIDE_8_BUS 0x00000002 +#define SDC_BUS_WIDTH_REG_WIDE_4_BUS 0x00000004 +#define SDC_BUS_WIDTH_REG_WIDE_BUS_SUPPORT 0x00000018 +#define SDC_BUS_WIDTH_REG_CARD_DETECT 0x00000020 + +#define SDC_WIDE_4_BUS_SUPPORT 0x00000008 +#define SDC_WIDE_8_BUS_SUPPORT 0x00000010 + +/* bit mapping of feature register */ +#define SDC_FEATURE_REG_FIFO_DEPTH 0x000000FF +#define SDC_FEATURE_REG_CPRM_FUNCTION 0x00000100 + +/* bit mapping of sdio control register */ +#define SDC_SDIO_CTRL1_REG_SDIO_BLK_NO 0xFFFF8000 +#define SDC_SDIO_CTRL1_REG_SDIO_ENABLE 0x00004000 +#define SDC_SDIO_CTRL1_REG_READ_WAIT_ENABLE 0x00002000 +#define SDC_SDIO_CTRL1_REG_SDIO_BLK_MODE 0x00001000 +#define SDC_SDIO_CTRL1_REG_SDIO_BLK_SIZE 0x00000FFF + +/* bit mapping of sdio status register */ +#define SDC_SDIO_SDIO_STATUS_REG_FIFO_REMAIN_NO 0x00FE0000 +#define SDC_SDIO_SDIO_STATUS_REG_SDIO_BLK_CNT 0x0001FFFF + +enum ftsdc_waitfor { + COMPLETION_NONE, + COMPLETION_FINALIZE, + COMPLETION_CMDSENT, + COMPLETION_RSPFIN, + COMPLETION_XFER_PROGRESS, +}; + +struct ftsdc_host { + struct platform_device *pdev; + struct mmc_host *mmc; + struct resource *mem; + struct clk *clk; + void __iomem *base; + int irq; + + unsigned int real_rate; + bool irq_enabled; + unsigned int fifo_len; /* bytes */ + unsigned int last_opcode; /* keep last successful cmd to judge application specific command */ + + struct mmc_request *mrq; + int cmd_is_stop; + + spinlock_t complete_lock; + enum ftsdc_waitfor complete_what; + + struct completion dma_complete; + dmad_chreq *dma_req; + bool dodma; + bool dma_finish; + + u32 buf_sgptr; /* keep next scallterlist buffer index */ + u32 buf_bytes; /* keep current total scallterlist buffer length */ + u32 buf_count; /* keep real data size rw from sd */ + u32 *buf_ptr; /* keep current scallterlist buffer address */ + u32 page_cnt; + struct page *buf_page; + unsigned long buf_offset; +#define XFER_NONE 0 +#define XFER_READ 1 +#define XFER_WRITE 2 + u32 buf_active; /* keep current transfer mode */ + + int bus_width; + + char dbgmsg_cmd[301]; + char dbgmsg_dat[301]; + char *status; + + unsigned int ccnt, dcnt; + struct tasklet_struct pio_tasklet; + struct work_struct work; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debug_root; + struct dentry *debug_state; + struct dentry *debug_regs; +#endif +}; + +struct ftsdc_mmc_config { + /* get_cd() / get_wp() may sleep */ + int (*get_cd)(int module); + int (*get_ro)(int module); + + void (*set_power)(int module, bool on); + + /* wires == 0 is equivalent to wires == 4 (4-bit parallel) */ + u8 wires; + + u32 max_freq; + + /* any additional host capabilities: OR'd in to mmc->f_caps */ + u32 caps; + + /* Number of sg segments */ + u8 nr_sg; +}; + +#endif /* _FTSDC010_H_ */ From 4a8c16892e16325499472ab7992af90c9890d5ae Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Tue, 9 May 2023 18:28:56 +0800 Subject: [PATCH 038/169] sound: andes: ftssp010: Support for audio playback through legacy DMAC. 1. Support for audio playback through legacy DMAC. 2. Update kernel configuration to support the ALSA feature. Signed-off-by: CL Wang --- arch/riscv/configs/andes-support.config | 3 + arch/riscv/configs/andes_defconfig | 16 + sound/Kconfig | 2 + sound/Makefile | 2 +- sound/v5/FTSSP010_ALSA.c | 1467 +++++++++++++++++++++++ sound/v5/FTSSP010_UDA1345TS.h | 82 ++ sound/v5/FTSSP010_W83972D.h | 25 + sound/v5/FTSSP010_lib.c | 540 +++++++++ sound/v5/Kconfig | 23 + sound/v5/Makefile | 10 + 10 files changed, 2169 insertions(+), 1 deletion(-) create mode 100644 sound/v5/FTSSP010_ALSA.c create mode 100644 sound/v5/FTSSP010_UDA1345TS.h create mode 100644 sound/v5/FTSSP010_W83972D.h create mode 100644 sound/v5/FTSSP010_lib.c create mode 100644 sound/v5/Kconfig create mode 100644 sound/v5/Makefile diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 842d2d99bc8f72..d5adc060c4cea5 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -23,3 +23,6 @@ CONFIG_I2C_BOARDINFO=y CONFIG_I2C_CHARDEV=y CONFIG_I2C_ATCIIC100=y CONFIG_ATCIIC_IRQ=y + +CONFIG_SND_FTSSP010=y +CONFIG_SND_FTSSP010_AC97=y diff --git a/arch/riscv/configs/andes_defconfig b/arch/riscv/configs/andes_defconfig index 7a754e3a282c3c..fb9401afe72dfd 100644 --- a/arch/riscv/configs/andes_defconfig +++ b/arch/riscv/configs/andes_defconfig @@ -175,3 +175,19 @@ CONFIG_RCU_EQS_DEBUG=y # CONFIG_FTRACE is not set # CONFIG_RUNTIME_TESTING_MENU is not set CONFIG_MEMTEST=y + +CONFIG_SOUND=y +CONFIG_SOUND_OSS_CORE=y +CONFIG_SOUND_OSS_CORE_PRECLAIM=y +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_OSSEMUL=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +CONFIG_SND_PCM_TIMER=y +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_PROC_FS=y +CONFIG_SND_VERBOSE_PROCFS=y +CONFIG_SND_DRIVERS=y +CONFIG_SND_HDA_PREALLOC_SIZE=64 diff --git a/sound/Kconfig b/sound/Kconfig index 1903c35d799e11..6a31cb73d14bff 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -77,6 +77,8 @@ source "sound/mips/Kconfig" source "sound/sh/Kconfig" +source "sound/v5/Kconfig" + # the following will depend on the order of config. # here assuming USB is defined before ALSA source "sound/usb/Kconfig" diff --git a/sound/Makefile b/sound/Makefile index 04ef04b1168f39..f1ca85b3a29293 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SOUND) += soundcore.o obj-$(CONFIG_DMASOUND) += oss/dmasound/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ x86/ xen/ \ - virtio/ + v5/ virtio/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/v5/FTSSP010_ALSA.c b/sound/v5/FTSSP010_ALSA.c new file mode 100644 index 00000000000000..8822775a032fb2 --- /dev/null +++ b/sound/v5/FTSSP010_ALSA.c @@ -0,0 +1,1467 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Andes Technology Corporation + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "FTSSP010_UDA1345TS.h" +#include + +struct alc5630_data; + +#if (!defined(CONFIG_PLATFORM_AHBDMA)) +#warning needs ahb dma to wrok +#endif + +/* --------------------------------------------------------------------------- + * Define the debug level of FTSSP_DEBUG + */ +#define FTSSP_DEBUG 0 +#define FTSSP_DEBUG_VERBOSE 0 +#define FTSSP_PROC_FS 0 + +#undef VVDBG +#if (FTSSP_DEBUG_VERBOSE) +#define VVDBG(vvar...) +#else +#define VVDBG(vvar...) +#endif + +#undef ERR +#define ERR(vvar...) pr_err(vvar) + +#undef INFO +#define INFO(vvar...) pr_err(vvar) + +#if (FTSSP_DEBUG) +#undef DBG +#define DBG(vvar...) pr_err(vvar) +#else +#define DBG(vvar...) +#endif + +#if (FTSSP_DEBUG_VERBOSE) +#undef VDBG +#define VDBG(vvar...) pr_err(vvar) +#else +#define VDBG(vvar...) +#endif + +/* --------------------------------------------------------------------------- + * Preserved size of memory space for audio DMA ring + */ +#define FTSSP_HW_DMA_SIZE (512 * 1024) + +/* Buffer sizes reported to ALSA layer - AC97 mode */ + +/* ring size, exported to application */ +#define AC97_HW_BUFFER_BYTES_MAX (42 * 1024) +/* should not exceed AC97_HW_PERIOD_BYTES_MAX */ +#define AC97_HW_PERIOD_BYTES_MIN (2 * 1024) +/* AC97_HW_PERIOD_BYTES_MAX * AC97_HW_PERIODS_MAX <= AC97_HW_BUFFER_SIZE */ +#define AC97_HW_PERIOD_BYTES_MAX (8 * 1024) +/* 3 <= AC97_HW_PERIODS_MIN <= AC97_HW_PERIODS_MAX */ +#define AC97_HW_PERIODS_MIN 3 +/* AC97_HW_PERIOD_BYTES_MAX * AC97_HW_PERIODS_MAX <= AC97_HW_BUFFER_SIZE */ +#define AC97_HW_PERIODS_MAX 5 + +/* Driver internal dma buffer size, x2 for S16_LE(16-bits) to AC97 (20-bits), + * x6 for sampling rate converion from minimum 8k to AC97 48k. + * + * Note that AC97 mode cannot do playback and recording simultaneouly. So we + * use up all FTSSP_HW_DMA_SIZE of memory. + */ +#define AC97_HW_DMA_SIZE (AC97_HW_BUFFER_BYTES_MAX * 2 * 6) + +/* Buffer sizes reported to ALSA layer - I2S mode */ + +/* ring size, exported to application */ +#define I2S_HW_BUFFER_BYTES_MAX (256 * 1024) +/* should not exceed I2S_HW_PERIOD_BYTES_MAX */ +#define I2S_HW_PERIOD_BYTES_MIN (2 * 1024) +/* I2S_HW_PERIOD_BYTES_MAX * I2S_HW_PERIODS_MAX <= I2S_HW_BUFFER_SIZE */ +#define I2S_HW_PERIOD_BYTES_MAX (32 * 1024) +/* 3 <= I2S_HW_PERIODS_MIN <= I2S_HW_PERIODS_MAX */ +#define I2S_HW_PERIODS_MIN 3 +/* I2S_HW_PERIOD_BYTES_MAX * I2S_HW_PERIODS_MAX <= I2S_HW_BUFFER_SIZE */ +#define I2S_HW_PERIODS_MAX 8 + +/* Page-in size for playback and capture each. Note that I2S mode can do + * playback and recording simultaneouly, so this size should be less than or + * equal to FTSSP_HW_DMA_SIZE/2 + */ +#define I2S_HW_DMA_SIZE (I2S_HW_BUFFER_BYTES_MAX) + +/* --------------------------------------------------------------------------- + * Audio formats + */ +#define AC97_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) +#define AC97_CODEC_SAMPLE_RATES (SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_8000) + +#define AC97_CODEC_SAMPLE_RATE_MIN (8000) +#define AC97_CODEC_SAMPLE_RATE_MAX (48000) + +#define I2S_CODEC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) +#define I2S_CODEC_SAMPLE_RATES (SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_16000 | \ + SNDRV_PCM_RATE_11025 | \ + SNDRV_PCM_RATE_8000) +#define I2S_CODEC_SAMPLE_RATE_MIN (8000) +#define I2S_CODEC_SAMPLE_RATE_MAX (48000) + + +/* --------------------------------------------------------------------------- + * Configuration + */ +#if (CONFIG_PROC_FS == 0) +#undef FTSSP_PROC_FS +#define FTSSP_PROC_FS 0 +#else +#if (FTSSP_PROC_FS) +#include +#endif /* FTSSP_PROC_FS */ +#endif /* CONFIG_PROC_FS */ + +#define FTSSP_CARD_ID "ftssp010" +#define FTSSP_DRIVER_NAME "ftssp" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Faraday Technology Corp."); +MODULE_DESCRIPTION("FTSSP010 Linux 2.6 Driver"); + +static int cardno; +/* Driver mode */ +#ifdef CONFIG_SND_FTSSP010_AC97 +static int ac97 = 1; +#else +static int ac97; +#endif + +// ---------------------------------------------- +module_param(cardno, int, 0); +MODULE_PARM_DESC(cardno, "FTSSP No."); + +module_param(ac97, int, 0); +MODULE_PARM_DESC(ac97, "AC97 mode"); +// ---------------------------------------------- + +/* --------------------------------------------------------------------------- + * Structures + */ + +/* private data for card */ +typedef struct { + struct platform_device *pdev; + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *substream_tx; + struct snd_pcm_substream *substream_rx; +#if (FTSSP_PROC_FS) + struct snd_info_entry *info_buf_max; + struct snd_info_entry *info_period_min; + struct snd_info_entry *info_period_max; + struct snd_info_entry *info_periods_min; + struct snd_info_entry *info_periods_max; +#endif +} ftssp_chip; + +/* dma request descriptors */ +dmad_chreq dma_chreq_tx = { + .channel = -1, + .drq = NULL, +}; + +dmad_chreq dma_chreq_rx = { + .channel = -1, + .drq = NULL, +}; + +/* Holds ALSA card instance pointers */ +struct snd_card *ftssp_cards[SSP_FTSSP010_COUNT]; + +/* snd_pcm_hardware */ +static struct snd_pcm_hardware snd_ftssp_pcm_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED, + .formats = I2S_CODEC_FORMATS, + .rates = I2S_CODEC_SAMPLE_RATES, + .rate_min = I2S_CODEC_SAMPLE_RATE_MIN, + .rate_max = I2S_CODEC_SAMPLE_RATE_MAX, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = I2S_HW_BUFFER_BYTES_MAX, + .period_bytes_min = I2S_HW_PERIOD_BYTES_MIN, + .period_bytes_max = I2S_HW_PERIOD_BYTES_MAX, + .periods_min = I2S_HW_PERIODS_MIN, + .periods_max = I2S_HW_PERIODS_MAX, +}; + +/* private data for a substream (playback or capture) */ +/* function pointer for set up AHBDMA for this substream */ +typedef void (*start_t)(int cardno, unsigned int use_dma); +typedef void (*pmu_set_clocking_t)(unsigned int); +typedef void (*ftssp010_config_t)(int cardno, unsigned int is_stereo, + unsigned int speed, int use8bit); + +typedef struct { + u32 busy; + spinlock_t dma_lock; + unsigned long dma_area_va; + + int dma_width; + unsigned int tx_period; + unsigned int rx_period; + + start_t start; + pmu_set_clocking_t pmu_set_clocking; + ftssp010_config_t hw_config; +} ftssp_substream; + +static ftssp_substream ftssp010_substreams[2] = { + /* Playback substream */ + { + .busy = 0, + .start = ftssp010_start_tx, + .pmu_set_clocking = pmu_set_i2s_clocking, + .hw_config = ftssp010_config_tx, + }, + /* Capture substream */ + { + .busy = 0, + .start = ftssp010_start_rx, + .pmu_set_clocking = pmu_set_i2s_clocking, + .hw_config = ftssp010_config_rx, + } +}; + +/* (AC97 only) Convert 16 bits PCM data in user buffer to/from 20 bits PCM data + * (32 bits actaully in dma buffer) for AC97 codec. + */ +static int snd_ftssp_playback_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, void *usr_buf, + snd_pcm_uframes_t count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + ftssp_substream *ftssp010_substream = + (ftssp_substream *) runtime->private_data; + + u32 *dma_va = NULL; + u16 *usr_va = usr_buf; + int copy_words; + int pcm_data; + dmad_chreq *dma_chreq; + u32 sw_ptr; + + /* convert to frames */ + pos = bytes_to_frames(substream->runtime, pos); + count = bytes_to_frames(substream->runtime, count); + + /* frames_to_bytes(runtime, pos + count) * 2(bytes/per pcm) / + * 4(bytes per dma unit) + */ + sw_ptr = (u32)frames_to_bytes(runtime, pos + count) >> 1; + + switch (runtime->rate) { + case 8000: + sw_ptr *= 6; + dma_va = (unsigned int *)(ftssp010_substream->dma_area_va + + frames_to_bytes(runtime, pos * 6) * 2); + + VVDBG("%s: pos(0x%08x) count(0x%08x) next_pos(0x%08x)\n", + __func__, (u32)pos, (u32)count, (u32)(pos + count)); + VVDBG("%s: va base(0x%08x) range (0x%08x ~ 0x%08x)\n", + __func__, (u32)ftssp010_substream->dma_area_va, + (u32)dma_va, + (u32)dma_va + + (u32)2 * frames_to_bytes(runtime, count * 6)); + + if (runtime->channels == 1) { + while (count--) { + pcm_data = 0; + get_user(pcm_data, usr_va++); + dma_va[0] = (pcm_data & 0xffff) << 4; + dma_va[1] = dma_va[2] = dma_va[3] = + dma_va[4] = dma_va[5] = dma_va[0]; + //memcpy(&dma_va[1], &dma_va[0], 5 * 4 * 1); + dma_va += 6; + } + } else { // assume 2 channels + while (count--) { + pcm_data = 0; + get_user(pcm_data, usr_va++); + dma_va[0] = (pcm_data & 0xffff) << 4; + + pcm_data = 0; + get_user(pcm_data, usr_va++); + dma_va[1] = (pcm_data & 0xffff) << 4; + + dma_va[2] = dma_va[4] = dma_va[6] = + dma_va[8] = dma_va[10] = dma_va[0]; + dma_va[3] = dma_va[5] = dma_va[7] = + dma_va[9] = dma_va[11] = dma_va[0]; + //memcpy(&dma_va[2], &dma_va[0], 5 * 4 * 2); + dma_va += 12; + } + } + break; + + case 16000: + sw_ptr *= 3; + + dma_va = (unsigned int *)(ftssp010_substream->dma_area_va + + frames_to_bytes(runtime, pos * 3) * 2); + + VVDBG("%s: pos(0x%08x) count(0x%08x) next_pos(0x%08x)\n", + __func__, (u32)pos, (u32)count, (u32)(pos + count)); + VVDBG("%s: va base(0x%08x) range (0x%08x ~ 0x%08x)\n", + __func__, (u32)ftssp010_substream->dma_area_va, + (u32)dma_va, + (u32)dma_va + + (u32)2 * frames_to_bytes(runtime, count * 3)); + + if (runtime->channels == 1) { + while (count--) { + pcm_data = 0; + get_user(pcm_data, usr_va++); + dma_va[0] = (pcm_data & 0xffff) << 4; + dma_va[1] = dma_va[2] = dma_va[0]; + //memcpy(&dma_va[1], &dma_va[0], 2 * 4 * 1); + dma_va += 3; + } + } else { // assume 2 channels + while (count--) { + pcm_data = 0; + get_user(pcm_data, usr_va++); + dma_va[0] = (pcm_data & 0xffff) << 4; + + pcm_data = 0; + get_user(pcm_data, usr_va++); + dma_va[1] = (pcm_data & 0xffff) << 4; + + dma_va[2] = dma_va[4] = dma_va[0]; + dma_va[3] = dma_va[5] = dma_va[1]; + //memcpy(&dma_va[2], &dma_va[0], 2 * 4 * 2); + dma_va += 6; + } + } + break; + + case 48000: + default: + dma_va = (unsigned int *)(ftssp010_substream->dma_area_va + + frames_to_bytes(runtime, pos) * 2); + copy_words = 2 * frames_to_bytes(runtime, count) / sizeof(u32); + + VVDBG("%s: pos(0x%08x) count(0x%08x) next_pos(0x%08x)\n", + __func__, (u32)pos, (u32)count, (u32)(pos + count)); + VVDBG("%s: va base(0x%08x) range (0x%08x ~ 0x%08x)\n", + __func__, (u32)ftssp010_substream->dma_area_va, + (u32)dma_va, + (u32)dma_va + (u32)copy_words*4); + + while (copy_words--) { + get_user(pcm_data, usr_va++); + *dma_va++ = (pcm_data & 0xffff) << 4; + } + break; + } + + dma_chreq = &dma_chreq_tx; + + if (dmad_update_ring_sw_ptr(dma_chreq, sw_ptr, + (runtime->status->state == SNDRV_PCM_STATE_RUNNING) ? 1:0) != 0) { + ERR("%s: failed to update sw-pointer!\n", __func__); + return -ENODEV; + } + + return 0; +} + +static int snd_ftssp_capture_copy(struct snd_pcm_substream *substream, + int channel, snd_pcm_uframes_t pos, void *usr_buf, + snd_pcm_uframes_t count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + ftssp_substream *ftssp010_substream = + (ftssp_substream *) runtime->private_data; + + u32 *dma_va = NULL; + u16 *usr_va = usr_buf; + /* convert to frames */ + pos = bytes_to_frames(substream->runtime, pos); + count = bytes_to_frames(substream->runtime, count); + + switch (runtime->rate) { + case 8000: + dma_va = (unsigned int *)(ftssp010_substream->dma_area_va + + frames_to_bytes(runtime, pos * 6) * 2); + + VVDBG("%s: pos(0x%08x) count(0x%08x) next_pos(0x%08x)\n", + __func__, (u32)pos, (u32)count, (u32)(pos + count)); + VVDBG("%s: va base(0x%08x) range (0x%08x ~ 0x%08x)\n", + __func__, (u32)ftssp010_substream->dma_area_va, + (u32)dma_va, + (u32)dma_va + + (u32)2 * frames_to_bytes(runtime, count * 6)); + + if (runtime->channels == 1) { + while (count--) { + put_user((u16)(dma_va[0] >> 4), usr_va++); + dma_va += 6; + } + } else { + while (count--) { + put_user((u16)(dma_va[0] >> 4), usr_va); + usr_va++; + put_user((u16)(dma_va[0] >> 4), usr_va); + + /* [hw-limit] only slot-3 has valid data in + * recording mode -- check TAG_DATA_MONO + * defined in "FTSSP010_lib.c". Mask out + * one channel to avoid hi-freq noise. + */ + usr_va += 1; + dma_va += 12; + } + } + break; + + case 16000: + dma_va = (unsigned int *)(ftssp010_substream->dma_area_va + + frames_to_bytes(runtime, pos * 3) * 2); + + VVDBG("%s: pos(0x%08x) count(0x%08x) next_pos(0x%08x)\n", + __func__, (u32)pos, (u32)count, (u32)(pos + count)); + VVDBG("%s: va base(0x%08x) range (0x%08x ~ 0x%08x)\n", + __func__, (u32)ftssp010_substream->dma_area_va, + (u32)dma_va, + (u32)dma_va + + (u32)2 * frames_to_bytes(runtime, count * 3)); + + if (runtime->channels == 1) { + while (count--) { + put_user((u16)(dma_va[0] >> 4), usr_va++); + dma_va += 3; + } + } else { + while (count--) { + put_user((u16)(dma_va[0] >> 4), usr_va); + usr_va++; + put_user((u16)(dma_va[0] >> 4), usr_va); + + /* [hw-limit] only slot-3 has valid data in + * recording mode -- check TAG_DATA_MONO + * defined in "FTSSP010_lib.c". Mask out + * one channel to avoid hi-freq noise. + */ + usr_va++; + dma_va += 6; + } + } + break; + + case 48000: + default: + dma_va = (unsigned int *)(ftssp010_substream->dma_area_va + + frames_to_bytes(runtime, pos) * 2); + + VVDBG("%s: pos(0x%08x) count(0x%08x) next_pos(0x%08x)\n", + __func__, (u32)pos, (u32)count, (u32)(pos + count)); + VVDBG("%s: va base(0x%08x) range (0x%08x ~ 0x%08x)\n", + __func__, (u32)ftssp010_substream->dma_area_va, + (u32)dma_va, + (u32)dma_va + + (u32)2 * frames_to_bytes(runtime, count)); + + if (runtime->channels == 1) { + while (count--) { + put_user((u16)(dma_va[0] >> 4), usr_va++); + dma_va += 1; + } + } else { + while (count--) { + put_user((u16)(dma_va[0] >> 4), usr_va); + usr_va++; + put_user((u16)(dma_va[0] >> 4), usr_va); + + /* [hw-limit] only slot-3 has valid data in + * recording mode -- check TAG_DATA_MONO + * defined in "FTSSP010_lib.c". Mask out + * one channel to avoid hi-freq noise. + */ + usr_va += 1; + dma_va += 2; + } + } + break; + } + + return 0; +} + +/** + * These dma callbacks are called in interrupt context. + * @data: pointer to the chip-wide structure. + * TODO: use stream-specifc data + */ +__maybe_unused static void ftssp_dma_callback_tx(int ch, u16 int_status, void *data) +{ + ftssp_chip *chip = (ftssp_chip *)data; + + if (!ac97) { + /* in i2s mode, no indication to driver for user data length. + * For simplicity, just go ahead by one period + */ + + struct snd_pcm_runtime *runtime = chip->substream_tx->runtime; + ftssp_substream *ftssp010_substream = + (ftssp_substream *)runtime->private_data; + u32 sw_ptr; + u32 tx_period = ftssp010_substream->tx_period + 1; + + if (tx_period == runtime->periods) + sw_ptr = runtime->buffer_size; + else + sw_ptr = tx_period * runtime->period_size; + + sw_ptr = (u32)frames_to_bytes(runtime, sw_ptr) >> 1; + + if (dmad_update_ring_sw_ptr(&dma_chreq_tx, (u32)sw_ptr, 0)) + ERR("%s: failed to update sw-pointer!\n", __func__); + + ftssp010_substream->tx_period = tx_period % runtime->periods; + } + + snd_pcm_period_elapsed(chip->substream_tx); +} + +__maybe_unused static void ftssp_dma_callback_rx(int ch, u16 int_status, void *data) +{ + ftssp_chip *chip = (ftssp_chip *)data; + struct snd_pcm_runtime *runtime = chip->substream_rx->runtime; + ftssp_substream *ftssp010_substream = + (ftssp_substream *)runtime->private_data; + + u32 sw_ptr; + u32 rx_period = ftssp010_substream->rx_period + 1; + + if (rx_period == runtime->periods) + sw_ptr = runtime->buffer_size; + else + sw_ptr = rx_period * runtime->period_size; + + if (ac97) { + switch (runtime->rate) { + case 8000: + sw_ptr = sw_ptr * 6; + break; + case 16000: + sw_ptr = sw_ptr * 3; + break; + case 48000: + default: + break; + } + } + sw_ptr = (u32)frames_to_bytes(runtime, sw_ptr) >> 1; + + if (dmad_update_ring_sw_ptr(&dma_chreq_rx, (u32)sw_ptr, 0) != 0) + ERR("%s: failed to update sw-pointer!\n", __func__); + + ftssp010_substream->rx_period = rx_period % runtime->periods; + + snd_pcm_period_elapsed(chip->substream_rx); +} + +static inline int snd_ftssp_dma_ch_alloc(struct snd_pcm_substream *substream) +{ + dmad_chreq *ch_req __maybe_unused = 0; + +#ifdef CONFIG_PLATFORM_AHBDMA + if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ch_req = &dma_chreq_tx; + ch_req->completion_cb = ftssp_dma_callback_tx; + ch_req->ahb_req.tx_dir = DMAD_DIR_A0_TO_A1; +#if defined(CONFIG_PLAT_AG101P) + if ((inl(PMU_BASE) & AMERALD_MASK) == AMERALD_PRODUCT_ID) + ch_req->ahb_req.dev_reqn = DMAC_REQN_I2SAC97TX_AMERALD; + else +#endif + /*TX DST handshake mode addr0 --> addr1 + * tx_dir == 0, addr1 set handshake ID + */ + ch_req->ahb_req.dev_reqn = DMAC_REQN_I2SAC97TX; + } else { + ch_req = &dma_chreq_rx; + ch_req->completion_cb = ftssp_dma_callback_rx; + ch_req->ahb_req.tx_dir = DMAD_DIR_A1_TO_A0; +#if defined(CONFIG_PLAT_AG101P) + if ((inl(PMU_BASE) & AMERALD_MASK) == AMERALD_PRODUCT_ID) + ch_req->ahb_req.dev_reqn = DMAC_REQN_I2SAC97RX_AMERALD; + else +#endif + /*RX SRC handshake mode, addr1 --> addr0 + *tx_dir == 1, addr1 set handshake ID + */ + ch_req->ahb_req.dev_reqn = DMAC_REQN_I2SAC97RX; + } + + ch_req->controller = DMAD_DMAC_AHB_CORE; + ch_req->flags = DMAD_FLAGS_RING_MODE; + ch_req->ring_base = 0; + ch_req->dev_addr = (dma_addr_t)FTSSP010_DATA_PA(cardno); + ch_req->periods = 0; + ch_req->period_size = 0; + + ch_req->ahb_req.sync = 1; + ch_req->ahb_req.priority = DMAC_CSR_CHPRI_2; + ch_req->ahb_req.hw_handshake = 1; + ch_req->ahb_req.burst_size = DMAC_CSR_SIZE_1; + + if (ac97) { + ch_req->ahb_req.ring_width = DMAC_CSR_WIDTH_32; + ch_req->ahb_req.ring_ctrl = DMAC_CSR_AD_INC; + ch_req->ahb_req.ring_reqn = DMAC_REQN_NONE; + ch_req->ahb_req.dev_width = DMAC_CSR_WIDTH_32; + ch_req->ahb_req.dev_ctrl = DMAC_CSR_AD_FIX; + } else { + ch_req->ahb_req.ring_width = DMAC_CSR_WIDTH_16; + ch_req->ahb_req.ring_ctrl = DMAC_CSR_AD_INC; + ch_req->ahb_req.ring_reqn = DMAC_REQN_NONE; + ch_req->ahb_req.dev_width = DMAC_CSR_WIDTH_16; + ch_req->ahb_req.dev_ctrl = DMAC_CSR_AD_FIX; + } + + ch_req->completion_data = (void *)snd_pcm_substream_chip(substream); + + if (dmad_channel_alloc(ch_req) != 0) { + ERR("%s: AHBDMA channel allocation failed\n", __func__); + goto _err_exit; + } + + DBG("%s: AHBDMA channel allocated (ch: %d) ring_mode\n", + __func__, ch_req->channel); + + return 0; + +_err_exit: + +#endif /* CONFIG_PLATFORM_AHBDMA */ + + return -ENODEV; +} + +static inline ftssp_substream *ftssp010_substream_new(int stream_id) +{ + ftssp_substream *s = NULL; + + switch (stream_id) { + case SNDRV_PCM_STREAM_PLAYBACK: + s = &ftssp010_substreams[0]; + break; + case SNDRV_PCM_STREAM_CAPTURE: + s = &ftssp010_substreams[1]; + break; + default: + ERR("%s: wrong stream type (%d)\n", __func__, stream_id); + return NULL; + } + + if (s->busy) { + ERR("%s: device busy!\n", __func__); + return NULL; + } + s->busy = 1; + + spin_lock_init(&s->dma_lock); + + return s; +} + +static int snd_ftssp_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int stream_id = substream->pstr->stream; + + VDBG("%s, %s\n", __func__, + (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + "playback" : "capture"); + + /* Both playback and capture share a hardware description */ + runtime->hw = snd_ftssp_pcm_hw; + + /* Allocate & Initialize stream-specific data */ + runtime->private_data = ftssp010_substream_new(stream_id); + + if (runtime->private_data) + return snd_ftssp_dma_ch_alloc(substream); + else + return -EBUSY; +} + +static int snd_ftssp_pcm_close(struct snd_pcm_substream *substream) +{ + int stream_id = substream->pstr->stream; + ftssp_substream *ftssp010_substream = + (ftssp_substream *)substream->runtime->private_data; + + VDBG("%s, %s\n", __func__, + (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + "playback" : "capture"); + + if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) + dmad_channel_free(&dma_chreq_tx); + else + dmad_channel_free(&dma_chreq_rx); + + ftssp010_substream->busy = 0; + return 0; +} + +static int snd_ftssp_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + VDBG("%s, %s\n", __func__, + (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + "playback" : "capture"); + + if (ac97) + return snd_pcm_lib_malloc_pages(substream, AC97_HW_DMA_SIZE); + else + return snd_pcm_lib_malloc_pages(substream, I2S_HW_DMA_SIZE); +} + +static int snd_ftssp_pcm_hw_free(struct snd_pcm_substream *substream) +{ + VDBG("%s, %s\n", __func__, + (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + "playback" : "capture"); + + if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) + dmad_drain_requests(&dma_chreq_tx, 1); + else + dmad_drain_requests(&dma_chreq_rx, 1); + + return snd_pcm_lib_free_pages(substream); +} + +/* Prepare FTSSP010 AHBDMA for playback & capture */ +static int snd_ftssp_pcm_prepare(struct snd_pcm_substream *substream) +{ + ftssp_chip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + ftssp_substream *ftssp010_substream = + (ftssp_substream *)runtime->private_data; + + int stream_id = substream->pstr->stream; + dmad_chreq *dma_chreq; + unsigned int period_size, buffer_size; + + if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) + dma_chreq = &dma_chreq_tx; + else + dma_chreq = &dma_chreq_rx; + + period_size = frames_to_bytes(runtime, runtime->period_size); + buffer_size = frames_to_bytes(runtime, runtime->buffer_size); + + if (runtime->format != SNDRV_PCM_FORMAT_S16_LE) + return -ENODEV; + + if (ac97) { + switch (runtime->rate) { + case 8000: + period_size *= 12; + buffer_size *= 12; + break; + case 16000: + period_size *= 6; + buffer_size *= 6; + break; + case 48000: + default: + period_size *= 2; + buffer_size *= 2; + break; + } + + ftssp010_substream->dma_width = 4; + } else { + ftssp010_substream->dma_width = 2; + } + + dmad_drain_requests(dma_chreq, 1); + + dma_chreq->ring_base = (dma_addr_t)runtime->dma_addr; + dma_chreq->periods = (dma_addr_t)runtime->periods; + if (ac97) { + dma_chreq->period_size = (dma_addr_t)(period_size >> 2); + dma_chreq->ring_size = (dma_addr_t)(buffer_size >> 2); + } else { + dma_chreq->period_size = (dma_addr_t)(period_size >> 1); + dma_chreq->ring_size = (dma_addr_t)(buffer_size >> 1); + } + dmad_update_ring(dma_chreq); + + /* Set PMU, FTSSP010, and DMA */ + spin_lock(&ftssp010_substream->dma_lock); + + /* keep DMA buffer VA for copy() callback */ + // todo: support playback/capture simultaneously + ftssp010_substream->dma_area_va = (unsigned long)runtime->dma_area; + + if (ac97) { + ftssp010_substream->pmu_set_clocking(48000); + ftssp010_substream->hw_config(cardno, + runtime->channels > 1 ? 1 : 0, /* 1: stereo, 0: mono */ + runtime->rate, ftssp010_substream->dma_width); + } else { + ftssp010_substream->pmu_set_clocking(runtime->rate); + ftssp010_substream->hw_config(cardno, + runtime->channels > 1 ? 1 : 0, /* 1: stereo, 0: mono */ + runtime->rate, ftssp010_substream->dma_width); + } + + if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) { + ftssp010_substream->tx_period = 0; + chip->substream_tx = substream; + } else { + ftssp010_substream->rx_period = 0; + chip->substream_rx = substream; + } + + spin_unlock(&ftssp010_substream->dma_lock); + + return 0; +} + +static inline int snd_ftssp_start_play(ftssp_substream *ftssp010_substream, + struct snd_pcm_runtime *runtime) +{ + int err = 0; + + if (ac97) { + /* in ac97 mode, user data was fed to dma buffer through + * driver-provided copy callback + */ + err = dmad_kickoff_requests(&dma_chreq_tx); + if (err != 0) { + ERR("%s: failed to kickoff dma!\n", __func__); + return err; + } + } else { + /* in i2s mode, no indication to driver for user data length + * (except start threshold). For simplicity at start, just go + * ahead by one cycle + */ + + u32 sw_ptr = + (u32)frames_to_bytes(runtime, runtime->buffer_size) >> 1; + + err = dmad_update_ring_sw_ptr(&dma_chreq_tx, sw_ptr, 0); + if (err != 0) { + ERR("%s: failed to update sw-pointer!\n", __func__); + return err; + } + + err = dmad_kickoff_requests(&dma_chreq_tx); + if (err != 0) { + ERR("%s: failed to kickoff dma!\n", __func__); + return err; + } + } + ftssp010_substream->start(cardno, 1); + + return 0; +} + +static inline int snd_ftssp_start_record(ftssp_substream *ftssp010_substream, + struct snd_pcm_runtime *runtime) +{ + int err = 0; + u32 sw_ptr = (u32)frames_to_bytes(runtime, runtime->buffer_size); + + if (ac97) { + switch (runtime->rate) { + case 8000: + sw_ptr = (sw_ptr * 3); + break; + case 16000: + sw_ptr = (sw_ptr * 3) >> 1; + break; + case 48000: + default: + sw_ptr = sw_ptr >> 1; + break; + } + } else { + sw_ptr = sw_ptr >> 1; + } + + err = dmad_update_ring_sw_ptr(&dma_chreq_rx, sw_ptr, 0); + if (err != 0) { + ERR("%s: failed to update sw-pointer!\n", __func__); + return err; + } + + err = dmad_kickoff_requests(&dma_chreq_rx); + if (err != 0) { + ERR("%s: failed to kickoff dma!\n", __func__); + return err; + } + + ftssp010_substream->start(cardno, 1); + + return 0; +} + +/* Triggers AHBDMA for playback & capture */ +static int snd_ftssp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + ftssp_substream *ftssp010_substream = + (ftssp_substream *)substream->runtime->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + int err = 0; + int stream_id = substream->pstr->stream; + + /* note local interrupts are already disabled in the midlevel code */ + spin_lock(&ftssp010_substream->dma_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + + VDBG("%s: SNDRV_PCM_TRIGGER_START state(0x%08x)\n", + __func__, (u32)runtime->status->state); + + if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) { + err = snd_ftssp_start_play(ftssp010_substream, runtime); + } else { + err = snd_ftssp_start_record(ftssp010_substream, + runtime); + } + break; + + case SNDRV_PCM_TRIGGER_STOP: + + VDBG("%s: SNDRV_PCM_TRIGGER_STOP state(0x%08x)\n", + __func__, (u32)substream->runtime->status->state); + + if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) { + ftssp010_stop_tx(cardno); + dmad_drain_requests(&dma_chreq_tx, 1); + } else { + ftssp010_stop_rx(cardno); + dmad_drain_requests(&dma_chreq_rx, 1); + } + break; + default: + err = -EINVAL; + break; + } + + spin_unlock(&ftssp010_substream->dma_lock); + return err; +} + +// pcm middle-layer call this function within irq (snd_pcm_period_elapsed) or +// with local irq disabled (snd_pcm_lib_write1) +static snd_pcm_uframes_t snd_ftssp_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + u32 hw_ptr; + snd_pcm_uframes_t ret; + int stream_id = substream->pstr->stream; + + + /* Fetch DMA pointer, with spin lock */ + //spin_lock_irqsave(&ftssp010_substream->dma_lock, flags); + + if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) + hw_ptr = dmad_probe_ring_hw_ptr(&dma_chreq_tx); + else + hw_ptr = dmad_probe_ring_hw_ptr(&dma_chreq_rx); + + //spin_unlock_irqrestore(&ftssp010_substream->dma_lock, flags); + + if (ac97) { + ret = bytes_to_frames(runtime, hw_ptr << 1); + + switch (runtime->rate) { + case 8000: + ret = ret / 6; + break; + case 16000: + ret = ret / 3; + break; + case 48000: + default: + break; + } + } else { + ret = bytes_to_frames(runtime, hw_ptr << 1); + } + + + VVDBG("%s: hw_ptr(0x%08x) ret(0x%08x)\n", + (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ? "p" : "c", + (u32)hw_ptr, (u32)ret); + + /* ALSA requires return value 0 <= ret < buffer_size */ + if (ret >= runtime->buffer_size) + return 0; + return ret; +} + +/* For FTSSP010 driver, operations are shared among playback & capture */ +static struct snd_pcm_ops snd_ftssp_playback_ops = { + .open = snd_ftssp_pcm_open, + .close = snd_ftssp_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ftssp_pcm_hw_params, + .hw_free = snd_ftssp_pcm_hw_free, + .prepare = snd_ftssp_pcm_prepare, + .trigger = snd_ftssp_pcm_trigger, + .pointer = snd_ftssp_pcm_pointer, +}; + +static struct snd_pcm_ops snd_ftssp_capture_ops = { + .open = snd_ftssp_pcm_open, + .close = snd_ftssp_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_ftssp_pcm_hw_params, + .hw_free = snd_ftssp_pcm_hw_free, + .prepare = snd_ftssp_pcm_prepare, + .trigger = snd_ftssp_pcm_trigger, + .pointer = snd_ftssp_pcm_pointer, +}; + +/* ALSA PCM constructor */ +static int snd_ftssp_new_pcm(ftssp_chip *chip) +{ + struct snd_pcm *pcm; + int err; + + /* PCM device #0 with 1 playback and 1 capture */ + err = snd_pcm_new(chip->card, "ftssp_pcm", 0, 1, 1, &pcm); + if (err < 0) + return err; + + pcm->private_data = chip; + strcpy(pcm->name, "ftssp_pcm device"); + chip->pcm = pcm; + + /* set operators for playback and capture*/ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_ftssp_playback_ops); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_ftssp_capture_ops); + + /* Pre-allocate buffer, as suggested by ALSA driver document */ + // todo: support playback/capture simultaneously + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + &chip->pdev->dev, FTSSP_HW_DMA_SIZE, FTSSP_HW_DMA_SIZE); + + /* Force half-duplex (on A320D, or AC97 mode) */ + if (ac97) + pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; + + return 0; +} + +#if (FTSSP_PROC_FS) +static void snd_ftssp_buf_max_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + snd_iprintf(buffer, "%d\n", snd_ftssp_pcm_hw.buffer_bytes_max); +} + +static void snd_ftssp_buf_max_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + char tmp[128]; + char *ptr_e; + u32 val; + + if (buffer->size == 0) + return; + + memset(tmp, 0, 128); + snd_info_get_str(tmp, buffer->buffer, 127); + + val = simple_strtoul(tmp, &ptr_e, 10); + if (*ptr_e == 'k') + val *= 1024; + else if (*ptr_e == 'm') + val *= 1024 * 1024; + + if (ac97) { + if (val > AC97_HW_BUFFER_BYTES_MAX) + val = AC97_HW_BUFFER_BYTES_MAX; + } else { + if (val > I2S_HW_BUFFER_BYTES_MAX) + val = I2S_HW_BUFFER_BYTES_MAX; + } + + snd_ftssp_pcm_hw.buffer_bytes_max = (size_t)val; +} + +static void snd_ftssp_period_min_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + snd_iprintf(buffer, "%d\n", snd_ftssp_pcm_hw.period_bytes_min); +} + +static void snd_ftssp_period_min_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + char tmp[128]; + char *ptr_e; + u32 val; + + if (buffer->size == 0) + return; + + memset(tmp, 0, 128); + snd_info_get_str(tmp, buffer->buffer, 127); + + val = simple_strtoul(tmp, &ptr_e, 10); + if (*ptr_e == 'k') + val *= 1024; + else if (*ptr_e == 'm') + val *= 1024 * 1024; + + snd_ftssp_pcm_hw.period_bytes_min = (size_t)val; + + if ((val * snd_ftssp_pcm_hw.periods_max) > + snd_ftssp_pcm_hw.buffer_bytes_max) { + INFO("\nWarning: period_bytes(%d) * periods(%d) exceeds hw_buffer_size(%d).\n", + snd_ftssp_pcm_hw.period_bytes_min, + snd_ftssp_pcm_hw.periods_max, + snd_ftssp_pcm_hw.buffer_bytes_max); + INFO(" Unexpected access violation may occur!\n"); + } +} + +static void snd_ftssp_period_max_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + snd_iprintf(buffer, "%d\n", snd_ftssp_pcm_hw.period_bytes_max); +} + +static void snd_ftssp_period_max_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + char tmp[128]; + char *ptr_e; + u32 val; + + if (buffer->size == 0) + return; + + memset(tmp, 0, 128); + snd_info_get_str(tmp, buffer->buffer, 127); + + val = simple_strtoul(tmp, &ptr_e, 10); + if (*ptr_e == 'k') + val *= 1024; + else if (*ptr_e == 'm') + val *= 1024 * 1024; + + snd_ftssp_pcm_hw.period_bytes_max = (size_t)val; + + if ((val * snd_ftssp_pcm_hw.periods_max) > + snd_ftssp_pcm_hw.buffer_bytes_max) { + INFO("\nWarning: period_bytes(%d) * periods(%d) exceeds hw_buffer_size(%d).\n", + snd_ftssp_pcm_hw.period_bytes_max, + snd_ftssp_pcm_hw.periods_max, + snd_ftssp_pcm_hw.buffer_bytes_max); + INFO(" Unexpected access violation may occur!\n"); + } +} + +static void snd_ftssp_periods_min_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + snd_iprintf(buffer, "%d\n", snd_ftssp_pcm_hw.periods_min); +} + +static void snd_ftssp_periods_min_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + char tmp[128]; + char *ptr_e; + u32 val; + + if (buffer->size == 0) + return; + + memset(tmp, 0, 128); + snd_info_get_str(tmp, buffer->buffer, 127); + + val = simple_strtoul(tmp, &ptr_e, 10); + if (*ptr_e == 'k') + val *= 1024; + else if (*ptr_e == 'm') + val *= 1024 * 1024; + + snd_ftssp_pcm_hw.periods_min = (size_t)val; + + if ((val * snd_ftssp_pcm_hw.period_bytes_max) > + snd_ftssp_pcm_hw.buffer_bytes_max) { + INFO("\nWarning: period_bytes(%d) * periods(%d) exceeds hw_buffer_size(%d).\n", + snd_ftssp_pcm_hw.period_bytes_max, + snd_ftssp_pcm_hw.periods_min, + snd_ftssp_pcm_hw.buffer_bytes_max); + INFO(" Unexpected access violation may occur!\n"); + } +} + +static void snd_ftssp_periods_max_read(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + snd_iprintf(buffer, "%d\n", snd_ftssp_pcm_hw.periods_max); +} + +static void snd_ftssp_periods_max_write(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + char tmp[128]; + char *ptr_e; + u32 val; + + if (buffer->size == 0) + return; + + memset(tmp, 0, 128); + snd_info_get_str(tmp, buffer->buffer, 127); + + val = simple_strtoul(tmp, &ptr_e, 10); + if (*ptr_e == 'k') + val *= 1024; + else if (*ptr_e == 'm') + val *= 1024 * 1024; + + snd_ftssp_pcm_hw.periods_max = (size_t)val; + + if ((val * snd_ftssp_pcm_hw.period_bytes_max) > + snd_ftssp_pcm_hw.buffer_bytes_max) { + INFO("\nWarning: period_bytes(%d) * periods(%d) exceeds hw_buffer_size(%d).\n", + snd_ftssp_pcm_hw.period_bytes_max, + snd_ftssp_pcm_hw.periods_max, + snd_ftssp_pcm_hw.buffer_bytes_max); + INFO(" Unexpected access violation may occur!\n"); + } +} +#endif //FTSSP_PROC_FS + +static inline void ftssp_ac97_init(void) +{ + /* Change codec-dependent callbacks to AC97 */ + ftssp010_substreams[0].pmu_set_clocking = pmu_set_ac97_clocking; + ftssp010_substreams[0].hw_config = ftssp010_config_ac97_play; + ftssp010_substreams[1].pmu_set_clocking = pmu_set_ac97_clocking; + ftssp010_substreams[1].hw_config = ftssp010_config_ac97_rec; + + snd_ftssp_playback_ops.copy_user = snd_ftssp_playback_copy; + snd_ftssp_playback_ops.copy_kernel = snd_ftssp_playback_copy; + snd_ftssp_capture_ops.copy_user = snd_ftssp_capture_copy; + snd_ftssp_capture_ops.copy_kernel = snd_ftssp_capture_copy; + + snd_ftssp_pcm_hw.rates = AC97_CODEC_SAMPLE_RATES; + snd_ftssp_pcm_hw.rate_min = AC97_CODEC_SAMPLE_RATE_MIN; + snd_ftssp_pcm_hw.rate_max = AC97_CODEC_SAMPLE_RATE_MAX; + snd_ftssp_pcm_hw.formats = AC97_CODEC_FORMATS; + snd_ftssp_pcm_hw.buffer_bytes_max = AC97_HW_BUFFER_BYTES_MAX; + snd_ftssp_pcm_hw.period_bytes_min = AC97_HW_PERIOD_BYTES_MIN; + snd_ftssp_pcm_hw.period_bytes_max = AC97_HW_PERIOD_BYTES_MAX; + snd_ftssp_pcm_hw.periods_min = AC97_HW_PERIODS_MIN; + snd_ftssp_pcm_hw.periods_max = AC97_HW_PERIODS_MAX; +} + +static int ftssp_alsa_init(struct platform_device *pdev) +{ + ftssp_chip *chip; + int err; + + if (init_hw(cardno, ac97, NULL)) + return -EIO; + + if (ac97) + ftssp_ac97_init(); + + DBG("%s: FTSSP010 #%d (Physical Addr=0x%08X), mode: %s\n", + __func__, + cardno, SSP_FTSSP010_pa_base[cardno], + ac97 ? "ac97" : "i2s"); + + err = snd_card_new(&pdev->dev, SNDRV_DEFAULT_IDX1, FTSSP_CARD_ID, + THIS_MODULE, sizeof(ftssp_chip), &ftssp_cards[cardno]); + + if (err < 0) + return err; + + if (ac97) { + sprintf(ftssp_cards[cardno]->driver, FTSSP_DRIVER_NAME); + sprintf(ftssp_cards[cardno]->shortname, + FTSSP_DRIVER_NAME "_ac97"); + sprintf(ftssp_cards[cardno]->longname, + FTSSP_DRIVER_NAME "_ac97 controller"); + } else { + sprintf(ftssp_cards[cardno]->driver, FTSSP_DRIVER_NAME); + sprintf(ftssp_cards[cardno]->shortname, + FTSSP_DRIVER_NAME "_i2s"); + sprintf(ftssp_cards[cardno]->longname, + FTSSP_DRIVER_NAME "_i2s controller"); + } + + // PCM + chip = (ftssp_chip *)(ftssp_cards[cardno]->private_data); + chip->pdev = pdev; + chip->card = ftssp_cards[cardno]; + + err = snd_ftssp_new_pcm(chip); + if (err) { + ERR("%s, Can't new PCM devices\n", __func__); + return -ENODEV; + } + +#if (FTSSP_PROC_FS) + // new a proc entries subordinate to card->proc_root for debugging + // /proc/card#/buf_max + snd_card_proc_new(chip->card, "buf_max", &chip->info_buf_max); + if (chip->info_buf_max) { + chip->info_buf_max->c.text.read = snd_ftssp_buf_max_read; + chip->info_buf_max->c.text.write = snd_ftssp_buf_max_write; + } + // /proc/card#/period_min + snd_card_proc_new(chip->card, "period_size_min", + &chip->info_period_min); + if (chip->info_period_min) { + chip->info_period_min->c.text.read = snd_ftssp_period_min_read; + chip->info_period_min->c.text.write = + snd_ftssp_period_min_write; + } + // /proc/card#/period_max + snd_card_proc_new(chip->card, "period_size_max", + &chip->info_period_max); + if (chip->info_period_max) { + chip->info_period_max->c.text.read = snd_ftssp_period_max_read; + chip->info_period_max->c.text.write = + snd_ftssp_period_max_write; + } + // /proc/card#/periods_min + snd_card_proc_new(chip->card, "periods_min", &chip->info_periods_min); + if (chip->info_periods_min) { + chip->info_periods_min->c.text.read = + snd_ftssp_periods_min_read; + chip->info_periods_min->c.text.write = + snd_ftssp_periods_min_write; + } + // /proc/card#/periods_max + snd_card_proc_new(chip->card, "periods_max", &chip->info_periods_max); + if (chip->info_periods_max) { + chip->info_periods_max->c.text.read = + snd_ftssp_periods_max_read; + chip->info_periods_max->c.text.write = + snd_ftssp_periods_max_write; + } +#endif + + // Register the card to ALSA + err = snd_card_register(chip->card); + if (err == 0) + INFO("%s card registered!\n", FTSSP_CARD_ID); + + return err; +} + +static int ftssp_alsa_i2c_i2s_exit(void) +{ + DBG("%s, cleaning up\n", __func__); + + #ifndef CONFIG_SND_FTSSP010_AC97 + i2c_del_driver(&alc5630_driver); + #endif + + dmad_channel_free(&dma_chreq_tx); + dmad_channel_free(&dma_chreq_rx); + snd_card_free(ftssp_cards[cardno]); + + return 0; +} + + +static int atf_ac97_probe(struct platform_device *pdev) +{ + int (*read_fixup)(void __iomem *addr, unsigned int val, + unsigned int shift_bits); + struct resource *r, *mem = NULL; + size_t mem_size; + int ret; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ssp2_pbase = r->start; + mem_size = resource_size(r); + mem = request_mem_region(r->start, mem_size, pdev->name); + if (mem == NULL) { + ERR("%s: request_mem_region failed\n", __func__); + return -ENXIO; + } + + ssp2_vbase = (u32 *)ioremap(mem->start, mem_size); + + /* Check SSP revision register */ + read_fixup = symbol_get(readl_fixup); + ret = read_fixup((void __iomem *)(unsigned long)ssp2_vbase + 0x40, 0x00011506, 0); + symbol_put(readl_fixup); + if (!ret) { + ERR("%s: fail to read revision reg, bitmap no support ftssp\n", __func__); + return -ENXIO; + } + + return ftssp_alsa_init(pdev); +} + +static int atf_ac97_remove(struct platform_device *pdev) +{ + return ftssp_alsa_i2c_i2s_exit(); +} + +static const struct of_device_id atf_ac97_of_match[] = { + { .compatible = "andestech,atfac97", }, + {}, +}; + +static struct platform_driver atf_ac97_driver = { + .driver = { + .name = "atfssp", + .owner = THIS_MODULE, + .of_match_table = atf_ac97_of_match, + }, + .probe = atf_ac97_probe, + .remove = atf_ac97_remove, +}; +module_platform_driver(atf_ac97_driver); diff --git a/sound/v5/FTSSP010_UDA1345TS.h b/sound/v5/FTSSP010_UDA1345TS.h new file mode 100644 index 00000000000000..5be819c8c95434 --- /dev/null +++ b/sound/v5/FTSSP010_UDA1345TS.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2005 Andes Technology Corporation. + */ + +#include +#include +#include +#include + +/* Programming sequence: + * Suppose your playback format is 44.1KHz, 16 bit stereo + * PIO mode: + * pmu_set_i2s_clocking(44100); + * ftssp010_config(1, 44100, 0); + * ftssp010_start_tx(0); + * while(ftssp010_tx_fifo_not_full()) { + * Poke_your_PCM_data_to_FTSSP_data_port + * + * DMA mode: + * pmu_set_i2s_clocking(44100); + * ftssp010_config(1, 44100); + * + * pmu_set_i2s_dma_channel(ch); + * ftssp010_start_tx(1); + * + * ftssp010_stop_tx(); + */ +#define SSP_FTSSP010_COUNT 1 +#define PMU_BASE (pmu_base) +#define FTSSP010_DATA(x) ((u32 *)((uintptr_t)ssp2_vbase+0x18)) +#define FTSSP010_DATA_PA(x) ((u32 *)((uintptr_t)ssp2_pbase+0x18)) + +/* Initialize FTSSP010 to output to UDA1345TS via I2S */ +#define FTSSP010_CONTROL0(x) ((u32 *)((uintptr_t)ssp2_vbase+0x0)) +#define FTSSP010_CONTROL0_OPM_STEREO 0xC +#define FTSSP010_CONTROL0_OPM_MONO 0x8 + +#define FTSSP010_CONTROL1(x) ((u32 *)((uintptr_t)ssp2_vbase+0x4)) +#define FTSSP010_CONTROL2(x) ((u32 *)((uintptr_t)ssp2_vbase+0x8)) +#define FTSSP010_STATUS(x) ((u32 *)((uintptr_t)ssp2_vbase+0xC)) +#define FTSSP010_INT_CONTROL(x) ((u32 *)((uintptr_t)ssp2_vbase+0x10)) +#define FTSSP010_INT_STATUS(x) ((u32 *)((uintptr_t)ssp2_vbase+0x14)) +#define FTSSP010_DATA(x) ((u32 *)((uintptr_t)ssp2_vbase+0x18)) +#define FTSSP010_INFO(x) ((u32 *)((uintptr_t)ssp2_vbase+0x1C)) +#define FTSSP010_ACLINK_SLOT_VALID(x) ((u32 *)((uintptr_t)ssp2_vbase+0x20)) +#define FTSSP010_ACLINK_CMD(x) ((u32 *)((uintptr_t)ssp2_vbase+0x28)) +#define FTSSP010_ACLINK_CMD_DATA(x) ((u32 *)((uintptr_t)ssp2_vbase+0x2c)) + +extern resource_size_t ssp2_pbase; +extern u32 __iomem *ssp2_vbase; + +/* Drive PMU to generate I2S main clocking signal. Also configures PMU to set correct DMA REQ/ACK pair */ +extern void pmu_set_i2s_clocking(unsigned int speed); +/* Programs PMU to set I2S/AC97 DMA Channel, ch=0-7 */ +extern void pmu_set_i2s_dma_channel(unsigned int ch); + +/* Drive PMU to generate AC97 main clocking signal. Also configures PMU to set correct DMA REQ/ACK pair */ +extern void pmu_set_ac97_clocking(unsigned int speed); + +/* Returns FTSSP010 status */ +extern void ftssp010_set_int_control(int cardno, unsigned int val); +extern int ftssp010_get_status(int cardno); +extern unsigned int ftssp010_get_int_status(int cardno); +/* Polls FIFO full register */ +extern int ftssp010_tx_fifo_not_full(int cardno); +/* Configure FTSSP010 to a given sampling rate and channel number */ +extern void ftssp010_config_tx(int cardno, unsigned int is_stereo, unsigned int speed, int use8bit); +extern void ftssp010_config_rx(int cardno, unsigned int is_stereo, unsigned int speed, int use8bit); + +/* Configure FTSSP010 to a given sampling rate and channel number */ +extern void ftssp010_config_ac97_play(int cardno, unsigned int is_stereo, unsigned int speed, int use8bit); + +extern void ftssp010_config_ac97_rec(int cardno, unsigned int is_stereo, unsigned int speed, int use8bit); + +/* Initialize FTSSP010 to output to UDA1345TS via I2S */ +extern void ftssp010_start_tx(int cardno, unsigned int use_dma); +extern void ftssp010_start_rx(int cardno, unsigned int use_dma); +extern void ftssp010_stop_tx(int cardno); +extern void ftssp010_stop_rx(int cardno); +extern int init_hw(unsigned int cardno, unsigned int ac97, struct i2c_client *client); + diff --git a/sound/v5/FTSSP010_W83972D.h b/sound/v5/FTSSP010_W83972D.h new file mode 100644 index 00000000000000..35f484163d9533 --- /dev/null +++ b/sound/v5/FTSSP010_W83972D.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * linux/driver/mmc/ftsdc010.h - Andestech MMC/SD driver + * Andestech FTSSP010 Device Driver + * + * Andestech (C) 2005 Faraday Corp. (http://www.Andestech.com) + * + * All Rights Reserved + */ + + +/* Register Index for Winbond W83972D AC97 Codec */ +#define W83972D_RESET 0x0 +#define W83972D_STEREO_OUTPUT_CONTROL 0x2 +#define W83972D_MIC_VOLUME 0xE +#define W83972D_LINE_IN_VOLUME 0x10 +#define W83972D_AUX_INPUT_CONTROL 0x16 +#define W83972D_PCM_OUTPUT_CONTROL 0x18 +#define W83972D_RECORD_SELECT 0x1A +#define W83972D_RECORD_GAIN 0x1C +#define W83972D_RECORD_GAIN_MIC 0x1E +#define W83972D_EXT_AUDIO_CONTROL 0x2A +#define W83972D_DAC_SAMPLE_RATE_CONTROL 0x2C +#define W83972D_VER1 0x7C +#define W83972D_VER2 0x7E diff --git a/sound/v5/FTSSP010_lib.c b/sound/v5/FTSSP010_lib.c new file mode 100644 index 00000000000000..d2cedd282251c0 --- /dev/null +++ b/sound/v5/FTSSP010_lib.c @@ -0,0 +1,540 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Andes Technology Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include "FTSSP010_UDA1345TS.h" + +/* control0 register */ +#define NACK 0x40000000 +/* control2 register value */ +#define SSP_ACCRST 0x20 /* AC-Link Cold Reset Enable */ +#define SSP_ACWRST 0x10 /* AC-Link Warm Reset Enable */ +#define SSP_TXFCLR 0x8 /* TX FIFO Clear */ +#define SSP_RXFCLR 0x4 /* RX FIFO Clear */ +#define SSP_TXDOE 0x2 /* TX Data Output Enable */ +#define SSP_SSPEN 0x1 /* SSP Enable */ +#define SSP_TFVE 0x1f000 /* TX FIFO Valid Entries */ +#define SSP_RFVE 0x1f0 /* RX FIFO Valid Entries */ +#define SSP_FIFO_THOD 0xc400 +/* status register */ +#define WBUSY 0x08000000 +#define ACTXDATA 0x01000000 +#define SSP_BUSY 0x4 + +u32 __iomem *ssp2_vbase; +resource_size_t ssp2_pbase; + +#ifdef CONFIG_PLAT_AG101P +#define PMU_PDLLCR1 (pmu_base+0x34) +#define PMU_MFPSR (pmu_base+0x28) +#define PMU_I2SAC97_REQACKCFG (pmu_base+0xbc) +#define PMU_C4 (pmu_base+0xc4) +#endif +#define SSPCLK_TO_SCLKDIV(sspclk_div2, bps) ((sspclk_div2)/(bps)-1) + +// Each client has this additional data +struct alc5630_data { + struct i2c_client *client; + struct delayed_work work; + unsigned long gpio2_value; + struct mutex mtx; +}; + +static int i2s_alc5630_read(unsigned int raddr, char *data, struct i2c_client *client) +{ +#ifndef CONFIG_SND_FTSSP010_AC97 + struct i2c_adapter *adap = client->adapter; + int ret; +#endif + struct i2c_msg msg; + int i2c_value; + + //Reading ALC5630 register + msg.addr = raddr; + msg.flags = (client->flags & I2C_M_TEN) | I2C_M_RD; + msg.len = 1; + msg.buf = (char *)data; + + //ret = i2c_transfer(adap, &msg, 1); +#ifndef CONFIG_SND_FTSSP010_AC97 + ret = i2c_transfer(adap, &msg, 1); + + if (ret != 0) { + pr_err("i2c read failed\n"); + return -1; + } +#endif + + i2c_value = (data[0]&0xff) << 8 | (data[1]&0xff); + return i2c_value; +} + +static void i2s_alc5630_write(unsigned int raddr, unsigned int data, struct i2c_client *client) +{ +#ifndef CONFIG_SND_FTSSP010_AC97 + struct i2c_adapter *adap = client->adapter; + int ret; +#endif + struct i2c_msg msg; + char buf[3]; + + //Writing ALC5630 register + msg.addr = raddr; + msg.flags = (client->flags & I2C_M_TEN) | ~I2C_M_RD; + msg.len = 1; + buf[0] = (data >> 8) & 0xff; + buf[1] = data & 0xff; + msg.buf = (char *)buf; + //ret = i2c_transfer(adap, &msg, 1); +#ifndef CONFIG_SND_FTSSP010_AC97 + ret = i2c_transfer(adap, &msg, 1); + if (ret != 0) + pr_err("i2c write failed\n"); + +#endif +} + +static void i2s_al5630_slave_stereo_mode(struct i2c_client *client) +{ + i2s_alc5630_write(0x34, 0x8000, client); // codec slave mode + i2s_alc5630_write(0x0c, 0x1010, client); + i2s_alc5630_write(0x10, 0xee03, client); + i2s_alc5630_write(0x1c, 0x0748, client); + i2s_alc5630_write(0x62, 0x0000, client); +} + +//End ADD by river 2011.01.26 + + +/* Drive PMU to generate I2S main clocking signal. Also configures PMU to set correct DMA REQ/ACK pair */ +void pmu_set_i2s_clocking(unsigned int speed) +{ + unsigned int pmu_pdllcr1; /* PLL/DLL Control Register 1 */ + /* Configure PMU to generate I2S main clock */ + #ifdef CONFIG_PLAT_AG101 + pmu_pdllcr1 = ioread32(PMU_PDLLCR1)&0xfff0ffff; /* Bit 19-16 are relevent */ + #endif + + switch (speed) { + case 8000: + pmu_pdllcr1 |= 0x00000000; /* 2.048MHz x2 */ + break; + case 11025: + pmu_pdllcr1 |= 0x00010000; /* 2.8224MHz x2 */ + break; + case 16000: + pmu_pdllcr1 |= 0x00020000; /* 4.096MHz x2 */ + break; + case 22050: + pmu_pdllcr1 |= 0x00030000; /* 5.6448MHz x2 */ + break; + case 32000: + pmu_pdllcr1 |= 0x00040000; /* 8.192MHz x2 */ + break; + case 44100: + pmu_pdllcr1 |= 0x00050000; /* 11.2896Mhz x2 */ + break; + case 48000: + pmu_pdllcr1 |= 0x00060000; /* 12.2880MHz x2 */ + break; + default: + pr_err("%s: Unknown i2s speed %d\n", __func__, speed); + }; + + #ifdef CONFIG_PLAT_AG101 + iowrite32(pmu_pdllcr1, PMU_PDLLCR1); + /* Configure PMU to select I2S output (instead of AC97) */ + iowrite32(ioread32(PMU_MFPSR)&(~(1<<3)), PMU_MFPSR); /* clear bit 3 of MFPSR*/ + #endif +} + +/* Drive PMU to generate AC97 main clocking signal. Also configures PMU to set correct DMA REQ/ACK pair */ +void pmu_set_ac97_clocking(unsigned int speed) +{ + /* do nothing here */ +} + +/* Programs PMU to set I2S/AC97 DMA Channel, ch=0-7 */ +void pmu_set_i2s_dma_channel(unsigned int ch) +{ + #ifdef CONFIG_PLAT_AG101 + ch &= 0x7; + //iowrite32((ioread32(PMU_I2SAC97_REQACKCFG)&(~0x7))|ch, PMU_I2SAC97_REQACKCFG); + iowrite32(0xa, PMU_I2SAC97_REQACKCFG); + iowrite32(0xb, PMU_C4); + #endif +} + +static struct i2c_client *g_i2c_client; +void ftssp010_set_int_control(int cardno, unsigned int val) +{ + iowrite32(val, FTSSP010_INT_CONTROL(cardno)); +} + +unsigned int ftssp010_get_int_status(int cardno) +{ + return ioread32(FTSSP010_INT_STATUS(cardno)); +} + +int ftssp010_get_status(int cardno) +{ + return ioread32(FTSSP010_STATUS(cardno)); +} + +int ftssp010_tx_fifo_not_full(int cardno) +{ + return (ioread32(FTSSP010_STATUS(cardno)) & 0x2) == 0x2; +} + +int ftssp010_tx_fifo_vaild_entries(int cardno) +{ + return (ioread32(FTSSP010_STATUS(cardno))>>12) & 0x1f; +} + +#include "FTSSP010_W83972D.h" + +// AC97 codec tags +#define TAG_COMMAND 0xe000 +#define TAG_DATA 0x9800 /* Slot 3/4 */ +#define TAG_DATA_MONO 0x9000 /* Slot 3 */ +//#define TAG_DATA_LINE_IN 0x9000 /* Slot 3 */ + +void ftssp010_ac97_write_codec(unsigned int reg, unsigned int data) +{ + int cnt = 0x1000000; + + while ((ioread32(FTSSP010_STATUS(0)) & ACTXDATA) && cnt--) + ; + + if (!cnt) + pr_err("wait transfer buffer timeout\n"); + + iowrite32(0x0, FTSSP010_INT_CONTROL(0));/*Disable interrupts & DMA req */ + iowrite32(ioread32(FTSSP010_CONTROL2(0)) | (SSP_TXFCLR|SSP_RXFCLR), FTSSP010_CONTROL2(0)); + iowrite32(TAG_COMMAND, FTSSP010_ACLINK_SLOT_VALID(0)); + iowrite32(((reg<<16)|data), FTSSP010_ACLINK_CMD(0)); + iowrite32(ioread32(FTSSP010_CONTROL2(0)) | (SSP_SSPEN|SSP_TXDOE), FTSSP010_CONTROL2(0)); + cnt = 0x1000000; + while ((ioread32(FTSSP010_STATUS(0))&WBUSY) && cnt--) + ; + + if (!cnt) + pr_err("write ac97 timeout\n"); + iowrite32(ioread32(FTSSP010_CONTROL2(0)) & (~(SSP_SSPEN|SSP_TXDOE)), FTSSP010_CONTROL2(0)); +} + +/* Configure FTSSP010 to a given sampling rate and channel number + * for AC97 mode in playback mode + */ +int init_hw(unsigned int cardno, unsigned int ac97, struct i2c_client *client) +{ + int cnt = 0x1000000; + + g_i2c_client = client; + + if (ac97) { + // Clear FFMT + iowrite32(ioread32(FTSSP010_CONTROL0(cardno)) & 0xfff, FTSSP010_CONTROL0(cardno)); + // AC_link + iowrite32(ioread32(FTSSP010_CONTROL0(cardno)) | 0x4000, FTSSP010_CONTROL0(cardno)); + + iowrite32(0xc400, FTSSP010_INT_CONTROL(cardno)); + if ((ioread32(FTSSP010_INT_CONTROL(cardno))) != 0xc400) + return -EIO; + + iowrite32(0x20, FTSSP010_CONTROL2(cardno)); /* Cold Reset AC-Link */ + while (ioread32(FTSSP010_CONTROL2(cardno)) && cnt--) + ; + if (!cnt) + return -EIO; + + } else { + #ifdef CONFIG_PLAT_AG101 + iowrite32(ioread32(PMU_MFPSR)&(~(1<<3)), PMU_MFPSR); /* clear bit 3 of MFPSR*/ + iowrite32(0xa, PMU_I2SAC97_REQACKCFG); + iowrite32(0xb, PMU_C4); + #endif + + i2s_al5630_slave_stereo_mode(client); + iowrite32(0x311c, FTSSP010_CONTROL0(cardno)); /* I2S Master */ + iowrite32(0, FTSSP010_CONTROL1(cardno)); /* I2S Master */ + iowrite32(0xc400, FTSSP010_INT_CONTROL(cardno)); /* I2S Master */ + iowrite32(0x40, FTSSP010_CONTROL2(cardno)); /* Reset AC-Link */ + } + return 0; +} +static void _ftssp010_config_ac97(int cardno, unsigned int is_stereo, unsigned int speed, int is_rec) +{ + int cnt = 0x1000000; + /* Codec initialization */ + iowrite32(ioread32(FTSSP010_CONTROL0(cardno))|NACK, FTSSP010_CONTROL0(cardno)); + ftssp010_ac97_write_codec(W83972D_RESET, 0); + + if (is_rec) { /* Recording */ + /* Mute output */ + ftssp010_ac97_write_codec(W83972D_STEREO_OUTPUT_CONTROL, 0x8000); + /* Mute PCM */ + //ftssp010_ac97_write_codec(W83972D_PCM_OUTPUT_CONTROL, 0x8000); + + /* Register 0x10, Line-In/Mic Gain */ + //ftssp010_ac97_write_codec(W83972D_AUX_INPUT_CONTROL, 0x808); + /* FIXME: REC from line-in only */ + + /* Register 0x1A, Record Select=StereoMix */ + ftssp010_ac97_write_codec(W83972D_RECORD_SELECT, 0x0000 /*404*/); + ftssp010_ac97_write_codec(W83972D_MIC_VOLUME, 0x0040); + /* Register 0x1C, Record Gain=0db */ + ftssp010_ac97_write_codec(W83972D_RECORD_GAIN_MIC, 0x0808); + ftssp010_ac97_write_codec(W83972D_PCM_OUTPUT_CONTROL, 0x8000); + ftssp010_ac97_write_codec(W83972D_RECORD_GAIN, 0x0808); + } else { /* Playback */ + /* Register 0x10, Mute Line-In/Mic Gain */ + ftssp010_ac97_write_codec(W83972D_LINE_IN_VOLUME, 0x8000); + ftssp010_ac97_write_codec(W83972D_MIC_VOLUME, 0x8000); + + /* Register 0x1A, Mute Record Gains */ + ftssp010_ac97_write_codec(W83972D_RECORD_GAIN, 0x8000); + ftssp010_ac97_write_codec(W83972D_RECORD_GAIN_MIC, 0x8000); + + /* Output */ + ftssp010_ac97_write_codec(W83972D_STEREO_OUTPUT_CONTROL, 0); + ftssp010_ac97_write_codec(W83972D_PCM_OUTPUT_CONTROL, 0x808); + } + + ftssp010_ac97_write_codec(W83972D_EXT_AUDIO_CONTROL, 0x1); + ftssp010_ac97_write_codec(W83972D_DAC_SAMPLE_RATE_CONTROL, speed); + + iowrite32(ioread32(FTSSP010_CONTROL0(cardno))&~NACK, FTSSP010_CONTROL0(cardno)); + + /* Start data transfer */ +// if(is_rec) { +// iowrite32(TAG_DATA_LINE_IN, FTSSP010_ACLINK_SLOT_VALID(cardno)); +// } else { + if (is_stereo) + iowrite32(TAG_DATA, FTSSP010_ACLINK_SLOT_VALID(cardno)); + else + iowrite32(TAG_DATA_MONO, FTSSP010_ACLINK_SLOT_VALID(cardno)); +// } + while ((ioread32(FTSSP010_INT_STATUS(cardno)) & 0x3) && cnt--) + ; +} + +void ftssp010_config_ac97_play(int cardno, unsigned int is_stereo, unsigned int speed, int use8bit) +{ + _ftssp010_config_ac97(cardno, is_stereo, speed, 0); +} + +void ftssp010_config_ac97_rec(int cardno, unsigned int is_stereo, unsigned int speed, int use8bit) +{ + _ftssp010_config_ac97(cardno, is_stereo, speed, 1); +} + +/* + * Configure FTSSP010 to a given sampling rate and channel number + * for I2S mode + */ +void ftssp010_config(int cardno, unsigned int is_stereo, unsigned int speed, int width, int is_rec) +{ + int use8bit = (width == 1 ? 1 : 0); + unsigned int opm, bps = 2 * (use8bit ? 8 : 16); /* bits per 1 second audio data. */ + unsigned int fpclkdiv = 0; + struct alc5630_data *alc5630; + char data[3]; + + opm = is_stereo ? FTSSP010_CONTROL0_OPM_STEREO : FTSSP010_CONTROL0_OPM_MONO; + iowrite32(0x3100 | opm, FTSSP010_CONTROL0(cardno)); /* I2S Master */ + + /* configures CONTROL1 to use suitable clock divider. + * the I2S clock is generated from PMU. + */ + bps *= speed; + switch (speed) { + case 8000: /* SCLK : 256KHZ */ + i2s_alc5630_write(0x44, 0x3ea0, g_i2c_client); + i2s_alc5630_write(0x60, 0x3174, g_i2c_client); + i2s_alc5630_write(0x62, 0x1010, g_i2c_client); + fpclkdiv = 0xBB; + break; + case 11025: /* SCLK : 352.8KHZ */ + i2s_alc5630_write(0x44, 0x3ea0, g_i2c_client); + i2s_alc5630_write(0x60, 0x3174, g_i2c_client); + i2s_alc5630_write(0x62, 0x1010, g_i2c_client); + fpclkdiv = 0x88; + break; + case 16000: /* SCLK : 512KHZ */ + i2s_alc5630_write(0x44, 0x3ea0, g_i2c_client); + i2s_alc5630_write(0x60, 0x3174, g_i2c_client); + i2s_alc5630_write(0x62, 0x1010, g_i2c_client); + fpclkdiv = 0x5f; + break; + case 22050: /* SCLK : 705.6KHZ */ + i2s_alc5630_write(0x44, 0x3ea0, g_i2c_client); + i2s_alc5630_write(0x60, 0x3174, g_i2c_client); + i2s_alc5630_write(0x62, 0x1010, g_i2c_client); + fpclkdiv = 0x45; + break; + case 24000: /* SCLK : 768KHZ */ + i2s_alc5630_write(0x44, 0x3ea0, g_i2c_client); + i2s_alc5630_write(0x60, 0x3174, g_i2c_client); + i2s_alc5630_write(0x62, 0x1010, g_i2c_client); + fpclkdiv = 0x3e; + break; + case 32000: /* SCLK : 1024KHZ */ + i2s_alc5630_write(0x44, 0x3ea0, g_i2c_client); + i2s_alc5630_write(0x60, 0x3174, g_i2c_client); + i2s_alc5630_write(0x62, 0x1010, g_i2c_client); + fpclkdiv = 0x2f; + break; + case 44100: /* SCLK : 1.4112 MHZ */ /* 96 MHZ */ + i2s_alc5630_write(0x44, 0x3ea0, g_i2c_client); + i2s_alc5630_write(0x60, 0x3174, g_i2c_client); + i2s_alc5630_write(0x62, 0x1010, g_i2c_client); + fpclkdiv = 0x22; + break; + case 48000: /* SCLK : 1.536 MHZ */ + i2s_alc5630_write(0x44, 0x3ea0, g_i2c_client); + i2s_alc5630_write(0x60, 0x3174, g_i2c_client); + i2s_alc5630_write(0x62, 0x1010, g_i2c_client); + fpclkdiv = 0x1f; + break; + default: + pr_err("%s: unsupported speed %d\n", __func__, speed); + return; + }; + + if (!use8bit) + iowrite32(0xf0000|fpclkdiv, FTSSP010_CONTROL1(cardno)); /* 16bits */ + else + iowrite32(0x70000|fpclkdiv, FTSSP010_CONTROL1(cardno)); /* 8bits */ + + if (is_rec) + iowrite32(ioread32(FTSSP010_INT_CONTROL(cardno))&(~0x0f15), FTSSP010_INT_CONTROL(cardno)); /* Disable all interrupts */ + else + iowrite32(ioread32(FTSSP010_INT_CONTROL(cardno))&(~0xf02a), FTSSP010_INT_CONTROL(cardno)); /* Disable all interrupts */ + + iowrite32(0xc, FTSSP010_CONTROL2(cardno)); /* clear FIFOs */ + alc5630 = i2c_get_clientdata(g_i2c_client); + + if (is_rec) { + pr_err("%s for I2S mode in record.\n", __func__); + i2s_alc5630_write(0x0e, 0x0808, g_i2c_client); + i2s_alc5630_write(0x10, 0xee03, g_i2c_client); + i2s_alc5630_write(0x22, 0x0500, g_i2c_client); + i2s_alc5630_write(0x1c, 0x0748, g_i2c_client); + i2s_alc5630_write(0x14, 0x1f1f, g_i2c_client); + i2s_alc5630_write(0x12, 0xdfdf, g_i2c_client); + i2s_alc5630_write(0x26, 0x000f, g_i2c_client); + i2s_alc5630_write(0x3a, 0xffff, g_i2c_client); + i2s_alc5630_write(0x3c, 0xffff, g_i2c_client); + i2s_alc5630_write(0x3e, 0x80cf, g_i2c_client); + i2s_alc5630_write(0x44, 0x3ea0, g_i2c_client); + i2s_alc5630_write(0x42, 0x2000, g_i2c_client); + i2s_alc5630_write(0x40, 0x8c0a, g_i2c_client); + i2s_alc5630_write(0x02, 0x8080, g_i2c_client); + i2s_alc5630_write(0x04, 0x0000, g_i2c_client); + } else { + i2s_alc5630_write(0x0e, 0x0808, g_i2c_client); + i2s_alc5630_write(0x12, 0xcbcb, g_i2c_client); + i2s_alc5630_write(0x14, 0x7f7f, g_i2c_client); + i2s_alc5630_write(0x22, 0x0000, g_i2c_client); + i2s_alc5630_write(0x3e, 0x8000, g_i2c_client); + i2s_alc5630_write(0x40, 0x0c0a, g_i2c_client); + i2s_alc5630_write(0x42, 0x0000, g_i2c_client); + i2s_alc5630_write(0x26, 0x0000, g_i2c_client); + i2s_alc5630_write(0x3c, 0x2000, g_i2c_client); + i2s_alc5630_write(0x3a, 0x0002, g_i2c_client); + i2s_alc5630_write(0x3c, 0xa330, g_i2c_client); + i2s_alc5630_write(0x3a, 0xc843, g_i2c_client); + i2s_alc5630_write(0x3A, i2s_alc5630_read(0x3A, data, g_i2c_client)|0x0002, g_i2c_client); + i2s_alc5630_write(0x04, i2s_alc5630_read(0x04, data, g_i2c_client)|0x8080, g_i2c_client); + i2s_alc5630_write(0x3A, i2s_alc5630_read(0x3A, data, g_i2c_client)|0x0040, g_i2c_client); + i2s_alc5630_write(0x3c, i2s_alc5630_read(0x3C, data, g_i2c_client)|0x2000, g_i2c_client); + i2s_alc5630_write(0x3E, i2s_alc5630_read(0x3E, data, g_i2c_client)|0xfC00, g_i2c_client); + i2s_alc5630_write(0x5E, i2s_alc5630_read(0x5E, data, g_i2c_client)|0x0100, g_i2c_client); + i2s_alc5630_write(0x3A, i2s_alc5630_read(0x3A, data, g_i2c_client)|0x0200, g_i2c_client); + i2s_alc5630_write(0x3A, i2s_alc5630_read(0x3A, data, g_i2c_client)|0x0100, g_i2c_client); + i2s_alc5630_write(0x5E, i2s_alc5630_read(0x5E, data, g_i2c_client) & 0xfeff, g_i2c_client); + i2s_alc5630_write(0x1c, 0x0748, g_i2c_client); + i2s_alc5630_write(0x26, 0x000f, g_i2c_client); + + if (alc5630->gpio2_value == 0x0) + i2s_alc5630_write(0x3A, (i2s_alc5630_read(0x3A, data, g_i2c_client) & 0xFBFF)|0x0040, g_i2c_client); + else + i2s_alc5630_write(0x3A, i2s_alc5630_read(0x3A, data, g_i2c_client)|0x0440, g_i2c_client); + + i2s_alc5630_write(0x5E, i2s_alc5630_read(0x5E, data, g_i2c_client)|0x0020, g_i2c_client); + i2s_alc5630_write(0x5E, i2s_alc5630_read(0x5E, data, g_i2c_client)|0x00c0, g_i2c_client); + i2s_alc5630_write(0x04, i2s_alc5630_read(0x04, data, g_i2c_client) & 0x7f7f, g_i2c_client); + if (alc5630->gpio2_value == 0x0) + i2s_alc5630_write(0x02, 0x5F5F, g_i2c_client); + else + i2s_alc5630_write(0x02, 0x0000, g_i2c_client); + } +} + +void ftssp010_config_tx(int cardno, unsigned int is_stereo, unsigned int speed, int width) +{ + return ftssp010_config(cardno, is_stereo, speed, width, 0); +} + +void ftssp010_config_rx(int cardno, unsigned int is_stereo, unsigned int speed, int width) +{ + return ftssp010_config(cardno, is_stereo, speed, width, 1); +} + +/* Configures FTSSP010 to start TX. If use_dma being nonzero, + * FTSSP010 will use hardware handshake for DMA + */ +void ftssp010_start_tx(int cardno, unsigned int use_dma) +{ + unsigned int bogus = 0x800 * 3; + + if (use_dma) { + /* Enable H/W DMA Request and set TX DMA threshold to 12*/ + iowrite32(ioread32(FTSSP010_INT_CONTROL(cardno)) | 0xc422, FTSSP010_INT_CONTROL(cardno)); + } + + iowrite32(ioread32(FTSSP010_CONTROL2(cardno)) | (SSP_SSPEN|SSP_TXDOE), FTSSP010_CONTROL2(cardno)); + if (!use_dma) { + while (bogus > 0) { + while (!ftssp010_tx_fifo_not_full(cardno)) + udelay(50); + + iowrite32(0, FTSSP010_DATA(cardno)); + bogus--; + } + } +} + +/* Configures FTSSP010 to start RX. If use_dma being nonzero, + * FTSSP010 will use hardware handshake for DMA + */ +void ftssp010_start_rx(int cardno, unsigned int use_dma) +{ + if (use_dma) { + /* Enable H/W DMA Request and set RX DMA threshold to 2 */ + iowrite32(ioread32(FTSSP010_INT_CONTROL(cardno)) | 0xc411, FTSSP010_INT_CONTROL(cardno)); + } + + iowrite32(ioread32(FTSSP010_CONTROL2(cardno)) | 0x3, FTSSP010_CONTROL2(cardno)); +} + +void ftssp010_stop_tx(int cardno) +{ + iowrite32(ioread32(FTSSP010_INT_CONTROL(cardno)) & (~0x22), FTSSP010_INT_CONTROL(cardno)); + iowrite32(ioread32(FTSSP010_CONTROL2(0)) & (~(SSP_SSPEN|SSP_TXDOE)), FTSSP010_CONTROL2(0)); +} + +void ftssp010_stop_rx(int cardno) +{ + iowrite32(ioread32(FTSSP010_INT_CONTROL(cardno)) & (~0x11), FTSSP010_INT_CONTROL(cardno)); +} + diff --git a/sound/v5/Kconfig b/sound/v5/Kconfig new file mode 100644 index 00000000000000..b5b1b4d6da3f6c --- /dev/null +++ b/sound/v5/Kconfig @@ -0,0 +1,23 @@ +menu "ALSA RISCV devices" + depends on SND!=n && RISCV + +config SND_FTSSP010 + tristate "Faraday FTSSP010 audio Driver" + depends on SND && RISCV + depends on ATCDMAC300 + select SND_PCM +# select SND_AC97_CODEC + +choice + prompt "AC97/I2S/HDA selection" + depends on SND_FTSSP010 + default SND_FTSSP010_AC97 +config SND_FTSSP010_AC97 + bool "AC97" +config SND_FTSSP010_I2S + bool "I2S" +config SND_FTSSP010_HDA + bool "HDA" +endchoice +endmenu + diff --git a/sound/v5/Makefile b/sound/v5/Makefile new file mode 100644 index 00000000000000..716a62f760dd3f --- /dev/null +++ b/sound/v5/Makefile @@ -0,0 +1,10 @@ +ifeq ($(CONFIG_SND_FTSSP010_AC97),y) +snd-ftssp010-objs := FTSSP010_ALSA.o FTSSP010_lib.o +endif +ifeq ($(CONFIG_SND_FTSSP010_I2S),y) +snd-ftssp010-objs := FTSSP010_ALSA.o FTSSP010_lib.o +endif +ifeq ($(CONFIG_SND_FTSSP010_HDA),y) +snd-ftssp010-objs := FTSSP010_HDA.o FTSSP010_HDA_lib.o +endif +obj-$(CONFIG_SND_FTSSP010) += snd-ftssp010.o From 9ea821992e6f9050c541af4166c5c904c6264e55 Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Thu, 27 Apr 2023 15:51:47 +0800 Subject: [PATCH 039/169] fbdev: andes: ftlcdc100: Update kernel configuration to support the framebuffer feature. 1. Support framebuffer function. 2. Fix kernel panic problem when running "cat /dev/fb > /mnt/fb_img". 3. Source code is modified to match Linux coding style. 4. Update kernel configuration to support the ALSA feature. 5. Adjust the video buffer size to meet PPMA requirements. Signed-off-by: CL Wang --- arch/riscv/configs/andes-support.config | 5 + arch/riscv/configs/andes_defconfig | 4 + drivers/video/fbdev/Kconfig | 1 + drivers/video/fbdev/Makefile | 1 + drivers/video/fbdev/ftlcdc100/Kconfig | 74 ++ drivers/video/fbdev/ftlcdc100/Makefile | 1 + .../video/fbdev/ftlcdc100/faradayfb-main.c | 867 ++++++++++++++++++ drivers/video/fbdev/ftlcdc100/faradayfb.h | 217 +++++ drivers/video/fbdev/ftlcdc100/lcd-info.c | 266 ++++++ .../video/fbdev/ftlcdc100/pingpong-module.c | 614 +++++++++++++ 10 files changed, 2050 insertions(+) create mode 100644 drivers/video/fbdev/ftlcdc100/Kconfig create mode 100644 drivers/video/fbdev/ftlcdc100/Makefile create mode 100644 drivers/video/fbdev/ftlcdc100/faradayfb-main.c create mode 100644 drivers/video/fbdev/ftlcdc100/faradayfb.h create mode 100644 drivers/video/fbdev/ftlcdc100/lcd-info.c create mode 100644 drivers/video/fbdev/ftlcdc100/pingpong-module.c diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index d5adc060c4cea5..4db18a169178ec 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -24,5 +24,10 @@ CONFIG_I2C_CHARDEV=y CONFIG_I2C_ATCIIC100=y CONFIG_ATCIIC_IRQ=y +CONFIG_FB_FTLCDC100=y +CONFIG_PANEL_LW500AC9601=y +CONFIG_FFB_MODE_RGB=y +CONFIG_FFB_MODE_24BPP=y + CONFIG_SND_FTSSP010=y CONFIG_SND_FTSSP010_AC97=y diff --git a/arch/riscv/configs/andes_defconfig b/arch/riscv/configs/andes_defconfig index fb9401afe72dfd..71bbb5c5359c5f 100644 --- a/arch/riscv/configs/andes_defconfig +++ b/arch/riscv/configs/andes_defconfig @@ -176,6 +176,10 @@ CONFIG_RCU_EQS_DEBUG=y # CONFIG_RUNTIME_TESTING_MENU is not set CONFIG_MEMTEST=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y + CONFIG_SOUND=y CONFIG_SOUND_OSS_CORE=y CONFIG_SOUND_OSS_CORE_PRECLAIM=y diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index ff95f192249012..8a922bb1137912 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -2240,3 +2240,4 @@ config FB_SM712 source "drivers/video/fbdev/omap/Kconfig" source "drivers/video/fbdev/omap2/Kconfig" source "drivers/video/fbdev/mmp/Kconfig" +source "drivers/video/fbdev/ftlcdc100/Kconfig" diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index 7795c4126706fd..cc9bd386c376cf 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -115,6 +115,7 @@ obj-y += omap2/ obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o obj-$(CONFIG_FB_CARMINE) += carminefb.o obj-$(CONFIG_FB_MB862XX) += mb862xx/ +obj-$(CONFIG_FB_FTLCDC100) += ftlcdc100/ obj-$(CONFIG_FB_HYPERV) += hyperv_fb.o obj-$(CONFIG_FB_OPENCORES) += ocfb.o obj-$(CONFIG_FB_SM712) += sm712fb.o diff --git a/drivers/video/fbdev/ftlcdc100/Kconfig b/drivers/video/fbdev/ftlcdc100/Kconfig new file mode 100644 index 00000000000000..1393d530684d2b --- /dev/null +++ b/drivers/video/fbdev/ftlcdc100/Kconfig @@ -0,0 +1,74 @@ +config FB_FTLCDC100 + tristate "Faraday FTLCDC100 driver" + depends on FB + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + + choice + prompt "Default LCD Panel" + depends on FB_FTLCDC100 + default PANEL_AUA036QN01 + help + This option select a default panel setting for the LCD controller + + config PANEL_AUA036QN01 + bool "AU 3.5 inch LCD Panel" + + config PANEL_CH7013A + bool "Chrontel Digital PC to TV Encoder" + select I2C + select I2C_FARADAY + select CH7013A + + config PANEL_AUA070VW04 + bool "AU 7.0 inch LCD Panel" + + config PANEL_LW500AC9601 + bool "CHIMEI 5.0 inch LCD panel" + + endchoice + + # config FTLCD_OSD + # bool "Enable OSD (On Screen Display)" + # depends on FB_FTLCDC100 + # default n + # ---help--- + # This enables access to the OSD (On Screen Display) for Faraday + # FTLCDC100 LCD control. Disabling OSD will reduce the size of + # the kernel by approximately 6kb. + # + + choice + prompt "Default Color Mode" + depends on FB_FTLCDC100 + default FFB_MODE_RGB + help + This option select default color mode + + config FFB_MODE_RGB + bool "RGB Mode" + config FFB_MODE_YUV422 + bool "YUV422 Mode" + config FFB_MODE_YUV420 + bool "YUV420 Mode" + endchoice + + choice + prompt "Default BPP" + depends on FB_FTLCDC100 + default FFB_MODE_16BPP + help + This option select default BPP (bits-per-pixel) + + config FFB_MODE_8BPP + depends on FFB_MODE_RGB || FFB_MODE_YUV420 + bool "8 bits-per-pixel" + config FFB_MODE_16BPP + depends on FFB_MODE_RGB || FFB_MODE_YUV422 + bool "16 bits-per-pixel" + config FFB_MODE_24BPP + depends on FFB_MODE_RGB + bool "24 bits-per-pixel" + endchoice + diff --git a/drivers/video/fbdev/ftlcdc100/Makefile b/drivers/video/fbdev/ftlcdc100/Makefile new file mode 100644 index 00000000000000..3f206f6db0d76f --- /dev/null +++ b/drivers/video/fbdev/ftlcdc100/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FB_FTLCDC100) += faradayfb-main.o diff --git a/drivers/video/fbdev/ftlcdc100/faradayfb-main.c b/drivers/video/fbdev/ftlcdc100/faradayfb-main.c new file mode 100644 index 00000000000000..33ebf113277327 --- /dev/null +++ b/drivers/video/fbdev/ftlcdc100/faradayfb-main.c @@ -0,0 +1,867 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "faradayfb.h" +#include "lcd-info.c" +#include "pingpong-module.c" + +#define REG32(a) (*(unsigned int *)(a)) +static resource_size_t lcd_base; + +static u32 faradayfb_pseudo_palette[32]; +static inline void faradayfb_lcd_power(struct fb_info *info, int on) +{ + struct faradayfb_info *fbi = info->par; + LCD_Register *plcd = (LCD_Register *)fbi->io_base; + + if (on) + fbi->control |= (1UL << 11); + else + fbi->control &= ~(1UL << 11); + + plcd->Control = fbi->control; +} + +static void faradayfb_setup_gpio(struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + LCD_Register *plcd = (LCD_Register *)fbi->io_base; + + plcd->GPIO = 0x010000; +} + +static void faradayfb_enable_controller(struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + LCD_Register *plcd = (LCD_Register *)fbi->io_base; + + plcd->Timing0 = fbi->time0; + plcd->Timing1 = fbi->time1; + plcd->Timing2 = fbi->time2; + plcd->Control = fbi->control & ~0x01; + plcd->UPBase = fbi->screen_dma | fbi->frame420_size; + plcd->Control |= 0x01; + DEBUG(0, 1, "Time0 = 0x%08x\n", plcd->Timing0); + DEBUG(0, 1, "Time1 = 0x%08x\n", plcd->Timing1); + DEBUG(0, 1, "Time2 = 0x%08x\n", plcd->Timing2); + DEBUG(0, 1, "Control = 0x%08x\n", plcd->Control); + DEBUG(0, 1, "UPBase = 0x%08x\n", plcd->UPBase); +} + +static void faradayfb_disable_controller(struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + LCD_Register *plcd = (LCD_Register *)fbi->io_base; + DECLARE_WAITQUEUE(wait, current); + + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&fbi->ctrlr_wait, &wait); + fbi->control &= ~0x0001; + plcd->Control = fbi->control; + schedule_timeout(20 * HZ / 1000); + remove_wait_queue(&fbi->ctrlr_wait, &wait); +} + +static void faradayfb_enable_int(struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + LCD_Register *plcd = (LCD_Register *)fbi->io_base; + + plcd->INTREnable = fbi->int_mask; +} + +static void faradayfb_disable_int(struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + LCD_Register *plcd = (LCD_Register *)fbi->io_base; + + fbi->int_mask = plcd->INTREnable; + plcd->INTREnable = 0; + plcd->Status = 0x1e; +} + +static struct faradayfb_rgb def_rgb_8 = { + + .red = { .offset = 0, .length = 4 }, + .green = { .offset = 0, .length = 4 }, + .blue = { .offset = 0, .length = 4 }, + .transp = { .offset = 0, .length = 0 }, +}; + +static struct faradayfb_rgb def_rgb_16 = { + + .red = { .offset = 11, .length = 5, .msb_right = 0 }, + .green = { .offset = 5, .length = 6, .msb_right = 0 }, + .blue = { .offset = 0, .length = 5, .msb_right = 0 }, + .transp = { .offset = 15, .length = 0, .msb_right = 0 }, +}; + +static struct faradayfb_rgb def_rgb_24 = { + + .red = { .offset = 16, .length = 8, .msb_right = 0 }, + .green = { .offset = 8, .length = 8, .msb_right = 0 }, + .blue = { .offset = 0, .length = 8, .msb_right = 0 }, + .transp = { .offset = 0, .length = 0, .msb_right = 0 }, +}; + +static inline void faradayfb_schedule_work(struct fb_info *info, unsigned int state) +{ + struct faradayfb_info *fbi = info->par; + unsigned long flags; + + local_irq_save(flags); + + /* + * We need to handle two requests being made at the same time. + * There are two important cases: + * 1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE) + * We must perform the unblanking, which will do our REENABLE for us. + * 2. When we are blanking, but immediately unblank before we have + * blanked. We do the "REENABLE" thing here as well, just to be sure. + */ + if (fbi->task_state == C_ENABLE && state == C_REENABLE) + state = (unsigned int) -1; + + if (fbi->task_state == C_DISABLE && state == C_ENABLE) + state = C_REENABLE; + + if (state != (unsigned int) -1) { + + fbi->task_state = state; + schedule_work(&fbi->task); + } + + local_irq_restore(flags); +} + +static int faradayfb_setpalettereg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, unsigned int trans, + struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + + if (regno < fbi->palette_size) { + + fbi->palette_cpu[regno] = ((red >> 0) & (0x1fUL << 11)) | + ((green >> 5) & (0x3F << 5)) | + ((blue >> 11) & (0x1f << 0)); + + return 0; + } + + return 1; +} + +static int faradayfb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, + unsigned int blue, unsigned int trans, struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + int ret = 1; + /* + * If inverse mode was selected, invert all the colours + * rather than the register number. The register number + * is what you poke into the framebuffer to produce the + * colour you requested. + */ + if (fbi->cmap_inverse) { + + red = 0xffff - red; + green = 0xffff - green; + blue = 0xffff - blue; + } + + /* + * If greyscale is true, then we convert the RGB value + * to greyscale no mater what visual we are using. + */ + if (info->var.grayscale) + red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16; + + switch (info->fix.visual) { + + case FB_VISUAL_TRUECOLOR: + /* + * 12 or 16-bit True Colour. We encode the RGB value + * according to the RGB bitfield information. + */ + if (regno < 16) { + u32 col; + + red >>= (16 - info->var.red.length); + green >>= (16 - info->var.green.length); + blue >>= (16 - info->var.blue.length); + col = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset); + + switch (info->var.bits_per_pixel) { + + /* is the following code correct?? */ + case 16: + case 32: + ((u32 *)(info->pseudo_palette))[regno] = col; + ret = 0; + break; + } + } + break; + + case FB_VISUAL_STATIC_PSEUDOCOLOR: + case FB_VISUAL_PSEUDOCOLOR: + if (fbi->smode == FFB_MODE_RGB) + ret = faradayfb_setpalettereg(regno, red, green, blue, trans, info); + break; + } + + return ret; +} + +/* + * Round up in the following order: bits_per_pixel, xres, + * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale, + * bitfields, horizontal timing, vertical timing. + */ +static int faradayfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + int rgbidx; + + var->xres = min_t(unsigned int, var->xres, MIN_XRES); + var->yres = min_t(unsigned int, var->yres, MIN_YRES); + var->xres = max(var->xres, fbi->xres); + var->yres = max(var->yres, fbi->yres); + var->xres_virtual = max(var->xres_virtual, var->xres); + var->yres_virtual = max(var->yres_virtual, var->yres); + + DEBUG(0, 1, "var->bits_per_pixel = %d\n", var->bits_per_pixel); + + switch (var->bits_per_pixel) { + + case 1: + case 2: + case 4: + case 8: + rgbidx = RGB_8; + break; + + case 16: + rgbidx = RGB_16; + break; + + case 32: + rgbidx = RGB_24; + break; + + default: + return -EINVAL; + } + + /* + * Copy the RGB parameters for this display + * from the machine specific parameters. + */ + var->red = fbi->rgb[rgbidx]->red; + var->green = fbi->rgb[rgbidx]->green; + var->blue = fbi->rgb[rgbidx]->blue; + var->transp = fbi->rgb[rgbidx]->transp; + + DEBUG(0, 1, "RGBT length = %d:%d:%d:%d\n", + var->red.length, var->green.length, var->blue.length, var->transp.length); + + DEBUG(0, 1, "RGBT offset = %d:%d:%d:%d\n", + var->red.offset, var->green.offset, var->blue.offset, var->transp.offset); + + DEBUG(0, 1, "Leave\n"); + + return 0; +} + +/* + * Configures LCD Controller based on entries in var parameter. Settings are + * only written to the controller if changes were made. + */ +static int faradayfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + LCD_Register *plcd = (LCD_Register *)fbi->io_base; + + DEBUG(0, 1, "var: xres=%d hslen=%d lm=%d rm=%d\n", var->xres, var->hsync_len, var->left_margin, var->right_margin); + DEBUG(0, 1, "var: yres=%d vslen=%d um=%d bm=%d\n", var->yres, var->vsync_len, var->upper_margin, var->lower_margin); + + fbi->time0 = FARADAY_LCDTIME0_HFP(var->left_margin) + | FARADAY_LCDTIME0_HBP(var->right_margin) + | FARADAY_LCDTIME0_HW(var->hsync_len) + | FARADAY_LCDTIME0_PL(var->xres); + + fbi->time1 = FARADAY_LCDTIME1_VBP(var->upper_margin) + | FARADAY_LCDTIME1_VFP(var->lower_margin) + | FARADAY_LCDTIME1_VW(var->vsync_len) + | FARADAY_LCDTIME1_LF(var->yres); + + if ((plcd->Timing0 != fbi->time0) + || (plcd->Timing1 != fbi->time1) + || (plcd->Timing2 != fbi->time2) + || (plcd->Control != fbi->control)) { + + faradayfb_schedule_work(info, C_REENABLE); + } + + return 0; +} + +/* Set the user defined part of the display for the specified console */ +static int faradayfb_set_par(struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + struct fb_var_screeninfo *var = &info->var; + unsigned long palette_mem_size; + + if (var->bits_per_pixel == 16 || var->bits_per_pixel == 32) + info->fix.visual = FB_VISUAL_TRUECOLOR; + else if (!fbi->cmap_static) + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + else { + /* + * Some people have weird ideas about wanting static + * pseudocolor maps. I suspect their user space + * applications are broken. + */ + info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; + } + + info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; + + fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16; + + palette_mem_size = fbi->palette_size * sizeof(u32); + + DEBUG(0, 1, "info->fix.line_length = %d\n", info->fix.line_length); + DEBUG(0, 1, "palette_mem_size = 0x%08lx\n", (unsigned long) palette_mem_size); + + fbi->palette_cpu = (u32 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size); + + fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size; + + /* Set (any) board control register to handle new color depth */ + faradayfb_activate_var(var, info); + + DEBUG(0, 1, "Leave\n"); + + return 0; +} + +static irqreturn_t faradayfb_handle_irq(int irq, void *dev_id) +{ + struct fb_info *info = (struct fb_info *)dev_id; + struct faradayfb_info *fbi = info->par; + LCD_Register *plcd = (LCD_Register *)fbi->io_base; + u32 status = plcd->Interrupt; + + pr_err("%s, irq %d\n", __func__, irq); + + DEBUG(0, 1, "status 0x%x\n", status); + + return IRQ_HANDLED; +} + +/* + * This function must be called from task context only, since it will + * sleep when disabling the LCD controller, or if we get two contending + * processes trying to alter state. + */ +static void set_ctrlr_state(struct fb_info *info, unsigned int state) +{ + struct faradayfb_info *fbi = info->par; + unsigned int old_state; + + down(&fbi->ctrlr_sem); + + old_state = fbi->state; + + /* Hack around fbcon initialisation. */ + if (old_state == C_STARTUP && state == C_REENABLE) + state = C_ENABLE; + + switch (state) { + case C_DISABLE_PM: + /* Disable controller */ + if (old_state != C_DISABLE) { + + fbi->state = state; + faradayfb_disable_int(info); + faradayfb_lcd_power(info, 0); + faradayfb_disable_controller(info); + } + break; + + case C_DISABLE: + /* Disable controller */ + if (old_state != C_DISABLE) { + + fbi->state = state; + faradayfb_disable_int(info); + faradayfb_lcd_power(info, 0); + // faradayfb_disable_controller(info); + } + break; + + case C_REENABLE: + /* + * Re-enable the controller only if it was already + * enabled. This is so we reprogram the control + * registers. + */ + if (old_state == C_ENABLE) { + + faradayfb_disable_int(info); + faradayfb_lcd_power(info, 0); + faradayfb_disable_controller(info); + + faradayfb_setup_gpio(info); + faradayfb_lcd_power(info, 1); + faradayfb_enable_controller(info); + faradayfb_enable_int(info); + } + break; + + case C_ENABLE_PM: + /* + * Re-enable the controller after PM. This is not + * perfect - think about the case where we were doing + * a clock change, and we suspended half-way through. + */ + if (old_state != C_DISABLE_PM) { + fbi->state = C_ENABLE; + faradayfb_setup_gpio(info); + faradayfb_lcd_power(info, 1); + faradayfb_enable_controller(info); + faradayfb_enable_int(info); + } + break; + /* fall through */ + + case C_ENABLE: + /* + * Power up the LCD screen, enable controller, and + * turn on the backlight. + */ + if (old_state != C_ENABLE) { + + fbi->state = C_ENABLE; + faradayfb_setup_gpio(info); + faradayfb_lcd_power(info, 1); + faradayfb_enable_controller(info); + faradayfb_enable_int(info); + } + break; + } + up(&fbi->ctrlr_sem); +} + +/* + * Blank the display by setting all palette values to zero. Note, the + * 12 and 16 bpp modes don't really use the palette, so this will not + * blank the display in all modes. + */ +static int faradayfb_blank(int blank, struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + int i; + + switch (blank) { + case FB_BLANK_POWERDOWN: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + + if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR || + info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) { + + for (i = 0; i < fbi->palette_size; i++) + faradayfb_setpalettereg(i, 0, 0, 0, 0, info); + } + + faradayfb_schedule_work(info, C_DISABLE); + + break; + + case FB_BLANK_UNBLANK: + + if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR || + info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) + fb_set_cmap(&info->cmap, info); + + faradayfb_schedule_work(info, C_ENABLE); + + break; + } + + return 0; +} + +/* + * Our LCD controller task (which is called when we blank or unblank) + * via keventd. + */ +static void faradayfb_task(struct work_struct *dummy) +{ + struct faradayfb_info *fbi = container_of(dummy, struct faradayfb_info, task); + struct fb_info *info = fbi->info; + unsigned int state; + + state = xchg(&fbi->task_state, -1); + set_ctrlr_state(info, state); +} + +/* Fake monspecs to fill in fbinfo structure */ +static struct fb_monspecs monspecs = { + + .hfmin = 30000, + .hfmax = 70000, + .vfmin = 50, + .vfmax = 65, +}; + +static int ffb_get_mach_param(struct faradayfb_mach_info *inf, struct fb_info *info, unsigned long smode) +{ + struct faradayfb_info *fbi = info->par; + struct lcd_param *tparam; + unsigned long bpp_mode; + + tparam = get_lcd_time(inf->time0, inf->num_time0, smode); + if (!(tparam)) + return -1; + + fbi->time0 = tparam->value; + tparam = get_lcd_time(inf->time1, inf->num_time1, smode); + if (!(tparam)) + return -1; + + fbi->time1 = tparam->value; + tparam = get_lcd_time(inf->time2, inf->num_time2, smode); + if (!(tparam)) + return -1; + + fbi->time2 = tparam->value; + + switch (info->var.bits_per_pixel) { + case 1: + case 2: + case 4: + case 8: + bpp_mode = FFB_MODE_8BPP; + break; + + case 16: + bpp_mode = FFB_MODE_16BPP; + break; + + case 32: + bpp_mode = FFB_MODE_24BPP; + break; + + default: + DEBUG(1, 1, "Unsupported BPP, set default BPP to 16\n"); + bpp_mode = FFB_MODE_16BPP; + break; + } + + tparam = get_lcd_ctrl(inf->control, inf->num_control, smode | bpp_mode); + + if (!(tparam)) + return -1; + + fbi->control = tparam->value; + + fbi->xres = inf->xres; + fbi->yres = inf->yres; + + return 0; +} + +static int faradayfb_set_var(struct fb_info *info, struct faradayfb_mach_info *inf, unsigned long type) +{ + struct faradayfb_info *fbi = info->par; + unsigned long t_smode = 0; + + if (!type) + t_smode = fbi->smode; + else + t_smode = type; + + if (ffb_get_mach_param(inf, info, t_smode)) { + + DEBUG(1, 1, "Not support mode(%lx)\n", t_smode); + return -1; + } + + info->var.xres = fbi->xres; + info->var.xres_virtual = fbi->xres; + info->var.yres = fbi->yres; + info->var.yres_virtual = fbi->yres; + + info->var.upper_margin = FARADAY_LCDTIME1_GET_VBP(fbi->time1); + info->var.lower_margin = FARADAY_LCDTIME1_GET_VFP(fbi->time1); + info->var.vsync_len = FARADAY_LCDTIME1_GET_VW(fbi->time1); + info->var.left_margin = FARADAY_LCDTIME0_GET_HFP(fbi->time0); + info->var.right_margin = FARADAY_LCDTIME0_GET_HBP(fbi->time0); + info->var.hsync_len = FARADAY_LCDTIME0_GET_HW(fbi->time0); + + fbi->int_mask = 0; + + if (t_smode & FFB_MODE_YUV420) + fbi->frame420_size = (((info->var.xres * info->var.yres + 0xffff) & 0xffff0000) >> 16) << 2; + else + fbi->frame420_size = 0; + + return 0; +} + +static const struct fb_ops faradayfb_ops = { + + .owner = THIS_MODULE, + .fb_check_var = faradayfb_check_var, + .fb_set_par = faradayfb_set_par, + .fb_setcolreg = faradayfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = faradayfb_blank, + .fb_mmap = faradayfb_mmap, + .fb_ioctl = faradayfb_ioctl, +}; + +static struct fb_info *faradayfb_init_fbinfo(struct device *dev) +{ + struct faradayfb_mach_info *inf; + struct fb_info *info; + struct faradayfb_info *fbi; + + info = framebuffer_alloc(sizeof(struct faradayfb_info), dev); + if (!(info)) + return NULL; + + fbi = info->par; + fbi->info = info; + fbi->io_base = lcd_base; + strcpy(info->fix.id, FARADAYFB_MODULE_NAME); + + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.type_aux = 0; + info->fix.xpanstep = 0; + info->fix.ypanstep = 0; + info->fix.ywrapstep = 0; + info->fix.accel = FB_ACCEL_NONE; + + info->var.nonstd = 0; + info->var.activate = FB_ACTIVATE_NOW; + info->var.height = -1; + info->var.width = -1; + info->var.accel_flags = 0; + info->var.vmode = FB_VMODE_NONINTERLACED; + + info->fbops = &faradayfb_ops; + info->flags = FBINFO_DEFAULT; + info->monspecs = monspecs; + info->pseudo_palette = &faradayfb_pseudo_palette; + + fbi->rgb[RGB_8] = &def_rgb_8; + fbi->rgb[RGB_16] = &def_rgb_16; + fbi->rgb[RGB_24] = &def_rgb_24; + + inf = (struct faradayfb_mach_info *)dev->platform_data; + + /* + * People just don't seem to get this. We don't support + * anything but correct entries now, so panic if someone + * does something stupid. + */ + + fbi->xres = inf->xres; + fbi->yres = inf->yres; + fbi->max_bpp = 32; +#if defined(CONFIG_FFB_MODE_8BPP) + info->var.bits_per_pixel = min((u32)8, inf->max_bpp); +#elif defined(CONFIG_FFB_MODE_16BPP) + info->var.bits_per_pixel = min((u32)16, inf->max_bpp); +#elif defined(CONFIG_FFB_MODE_24BPP) + info->var.bits_per_pixel = min((u32)32, inf->max_bpp); + info->var.bits_per_pixel = 32; +#endif + + info->var.pixclock = inf->pixclock; + info->var.sync = inf->sync; + info->var.grayscale = inf->cmap_greyscale; + fbi->cmap_inverse = inf->cmap_inverse; + fbi->cmap_static = inf->cmap_static; + fbi->smode = FFB_DEFAULT_MODE; + if (faradayfb_set_var(info, inf, 0)) + goto err; + fbi->state = C_STARTUP; + fbi->task_state = (unsigned char) -1; + + /* The size of the palette should not be included in info->fix.smem_len. */ + info->fix.smem_len = faradayfb_cal_frame_buf_size(fbi) - PAGE_SIZE; + init_waitqueue_head(&fbi->ctrlr_wait); + INIT_WORK(&fbi->task, faradayfb_task); + sema_init(&fbi->ctrlr_sem, 1); + return info; +err: + kfree(fbi); + kfree(info); + + return NULL; +} + +static int faradayfb_probe(struct platform_device *pdev) +{ + struct fb_info *info = NULL; + struct faradayfb_info *fbi; + int irq = -EINVAL; + int ret = -ENODEV; + struct resource *io; + + io = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (io == NULL) { + dev_err(&pdev->dev, "Failed to get memory resource\n"); + goto err_exit; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, "Failed to get irq\n"); + goto err_exit; + } + + lcd_base = (resource_size_t)(unsigned long)devm_ioremap_resource(&pdev->dev, io); + pdev->dev.platform_data = &ffb_mach_info; + + info = faradayfb_init_fbinfo(&pdev->dev); + if (info == NULL) { + dev_err(&pdev->dev, "Failed to init info\n"); + goto err_exit; + } + + fbi = info->par; + + /* Initialize video memory */ + ret = faradayfb_map_video_memory(info); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to map video memory\n"); + goto err_free_mem; + } + + ret = -EINVAL; + if (request_irq(irq, faradayfb_handle_irq, 0, "LCD", info)) { + dev_err(&pdev->dev, "Failed to request irq\n"); + goto err_free_map; + } + + /* + * This makes sure that our colour bitfield + * descriptors are correctly initialised. + */ + faradayfb_check_var(&info->var, info); + faradayfb_set_par(info); + dev_set_drvdata(&pdev->dev, info); + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register framebuffer\n"); + goto err_free_irq; + } + faradayfb_clean_screen(info); + + return 0; + +err_free_irq: + dev_set_drvdata(&pdev->dev, NULL); + free_irq(irq, info); +err_free_map: + faradayfb_unmap_video_memory(info); +err_free_mem: + kfree(info->par); + kfree(info); +err_exit: + + return ret; +} + +/* Called when the device is being detached from the driver */ +static int faradayfb_remove(struct platform_device *pdev) +{ + struct fb_info *info = platform_get_drvdata(pdev); + int irq; + + set_ctrlr_state(info, C_DISABLE); + + irq = platform_get_irq(pdev, 0); + free_irq(irq, info); + + unregister_framebuffer(info); + faradayfb_unmap_video_memory(info); + dev_set_drvdata(&pdev->dev, NULL); + kfree(info->par); + kfree(info); + + return 0; +} + +#ifdef CONFIG_PM +static int faradayfb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct fb_info *info; + + info = platform_get_drvdata(pdev); + + set_ctrlr_state(info, C_DISABLE_PM); + + return 0; +} + +static int faradayfb_resume(struct platform_device *pdev) +{ + struct fb_info *info; + + info = platform_get_drvdata(pdev); + + set_ctrlr_state(info, C_ENABLE_PM); + + return 0; +} +#endif /* CONFIG_PM */ + +static const struct of_device_id atflcd_of_match[] = { + { .compatible = "andestech,atflcdc100", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, atflcd_of_match); +static struct platform_driver atflcd_of_driver = { + .probe = faradayfb_probe, + .remove = faradayfb_remove, + .driver = { + .name = "faraday-lcd", + .owner = THIS_MODULE, + .of_match_table = atflcd_of_match, + }, +#ifdef CONFIG_PM + .suspend = faradayfb_suspend, + .resume = faradayfb_resume, +#endif /* CONFIG_PM */ +}; + +module_platform_driver(atflcd_of_driver); +MODULE_DESCRIPTION("Faraday LCD driver"); +MODULE_AUTHOR("Francis Huang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/fbdev/ftlcdc100/faradayfb.h b/drivers/video/fbdev/ftlcdc100/faradayfb.h new file mode 100644 index 00000000000000..316ac467468722 --- /dev/null +++ b/drivers/video/fbdev/ftlcdc100/faradayfb.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __FARADAYFB_H +#define __FARADAYFB_H + +#define FARADAYFB_MODULE_NAME "faradayfb" +#define DEBUG(enabled, tagged, ...) \ + do { \ + if (enabled) { \ + if (tagged) \ + pr_err("[ %30s() ] ", __func__); \ + pr_err(__VA_ARGS__); \ + } \ + } while (0) + +#if defined(CONFIG_FFB_MODE_RGB) +# define DEFAULT_COLOR FFB_MODE_RGB +#elif defined(CONFIG_FFB_MODE_YUV422) +# define DEFAULT_COLOR FFB_MODE_YUV422 +#elif defined(CONFIG_FFB_MODE_YUV420) +# define DEFAULT_COLOR FFB_MODE_YUV420 +#endif + +#if defined(CONFIG_FFB_MODE_8BPP) +# define DEFAULT_BPP FFB_MODE_8BPP +#elif defined(CONFIG_FFB_MODE_16BPP) +# define DEFAULT_BPP FFB_MODE_16BPP +#elif defined(CONFIG_FFB_MODE_24BPP) +# define DEFAULT_BPP FFB_MODE_24BPP +#endif + +#define FFB_DEFAULT_MODE (DEFAULT_COLOR | DEFAULT_BPP) + +struct lcd_param { + + unsigned long value; + unsigned long flags; +}; + +inline struct lcd_param *get_lcd_time(struct lcd_param *array, unsigned long num_array, unsigned long type) +{ + int i; + + for (i = 0; i < num_array; i++) { + + struct lcd_param *r = &array[i]; + + if (r->flags & type & 0xff) + return r; + } + + return NULL; +} + +inline struct lcd_param *get_lcd_ctrl(struct lcd_param *array, unsigned long num_array, unsigned long type) +{ + int i; + + for (i = 0; i < num_array; i++) { + + struct lcd_param *r = &array[i]; + + if ((r->flags & type & 0xff) && (r->flags & type & 0xff00)) + return r; + } + + return NULL; +} + +enum { RGB_8, RGB_16, RGB_24, NR_RGB}; + +struct faradayfb_rgb { + + struct fb_bitfield red; + struct fb_bitfield green; + struct fb_bitfield blue; + struct fb_bitfield transp; +}; + +/* This structure describes the machine which we are running on. */ +struct faradayfb_mach_info { + + unsigned long num_time0; + struct lcd_param *time0; + + unsigned long num_time1; + struct lcd_param *time1; + + unsigned long num_time2; + struct lcd_param *time2; + + unsigned long num_control; + struct lcd_param *control; + + unsigned long pixclock; + + unsigned long xres; + unsigned long yres; + + unsigned int max_bpp; + unsigned int sync; + + unsigned int cmap_greyscale:1, + cmap_inverse:1, + cmap_static:1, + unused:29; +}; + +struct faradayfb_info { + + struct fb_info *info; + struct faradayfb_rgb *rgb[NR_RGB]; + + unsigned int xres; + unsigned int yres; + unsigned int max_bpp; + + /* + * These are the addresses we mapped + * the framebuffer memory region to. + */ + dma_addr_t map_dma; + unsigned char *map_cpu; + unsigned int map_size; + + unsigned char *screen_cpu; + dma_addr_t screen_dma; + u32 *palette_cpu; + dma_addr_t palette_dma; + unsigned int palette_size; + + unsigned int cmap_inverse:1, + cmap_static:1, + unused:30; + + unsigned long time0; + unsigned long time1; + unsigned long time2; + unsigned long control; + unsigned long int_mask; + unsigned long io_base; + + unsigned int state; + unsigned int task_state; + struct semaphore ctrlr_sem; + wait_queue_head_t ctrlr_wait; + struct work_struct task; + + unsigned long smode; + unsigned long frame420_size; +}; + +/* + * These are the actions for set_ctrlr_state + */ +#define C_DISABLE 0 +#define C_ENABLE 1 +#define C_DISABLE_CLKCHANGE 2 +#define C_ENABLE_CLKCHANGE 3 +#define C_REENABLE 4 +#define C_DISABLE_PM 5 +#define C_ENABLE_PM 6 +#define C_STARTUP 7 + +#define FARADAY_LCDTIME0_GET_HBP(x) ((((x) >> 24) & 0xFF) + 1) +#define FARADAY_LCDTIME0_GET_HFP(x) ((((x) >> 16) & 0xFF) + 1) +#define FARADAY_LCDTIME0_GET_HW(x) ((((x) >> 8) & 0xFF) + 1) +#define FARADAY_LCDTIME1_GET_VBP(x) ((((x) >> 24) & 0xFF)) +#define FARADAY_LCDTIME1_GET_VFP(x) ((((x) >> 16) & 0xFF)) +#define FARADAY_LCDTIME1_GET_VW(x) ((((x) >> 8) & 0xFF) + 1) + +#define FARADAY_LCDTIME0_HBP(x) ((((x) - 1) & 0xFF) << 24) +#define FARADAY_LCDTIME0_HFP(x) ((((x) - 1) & 0xFF) << 16) +#define FARADAY_LCDTIME0_HW(x) ((((x) - 1) & 0xFF) << 8) +#define FARADAY_LCDTIME0_PL(x) (((((x) >> 4) - 1) & 0x3F) << 2) +#define FARADAY_LCDTIME1_VBP(x) ((((x)) & 0xFF) << 24) +#define FARADAY_LCDTIME1_VFP(x) ((((x)) & 0xFF) << 16) +#define FARADAY_LCDTIME1_VW(x) ((((x) - 1) & 0xFF) << 8) +#define FARADAY_LCDTIME1_LF(x) ((((x) - 1) & 0x3FF)) + +#define FFB_MODE_RGB 0x00000001 +#define FFB_MODE_YUV420 0x00000002 +#define FFB_MODE_YUV422 0x00000004 + +#define FFB_MODE_8BPP 0x00000100 +#define FFB_MODE_16BPP 0x00000200 +#define FFB_MODE_24BPP 0x00000400 + +/* Minimum X and Y resolutions */ +#define MIN_XRES 64 +#define MIN_YRES 64 + +typedef struct LCDTag { + + u32 Timing0; /* 0x00 */ + u32 Timing1; /* 0x04 */ + u32 Timing2; /* 0x08 */ + u32 Timing3; /* 0x0C */ + u32 UPBase; /* 0x10 */ + u32 LPBase; /* 0x14 */ + u32 INTREnable; /* 0x18 */ + u32 Control; /* 0x1C */ + u32 Status; /* 0x20 */ + u32 Interrupt; /* 0x24 */ + u32 UPCurr; /* 0x28 */ + u32 LPCurr; /* 0x2C */ + u32 Reserved1[5]; /* 0x30~0x40 */ + u32 GPIO; /* 0x44 */ + u32 Reserved2[0x74 - 6]; /* 0x48~0x1FC */ + + u32 Palette[0x80]; /* 0x200~0x3FC */ + u32 TestReg[0x100]; /* 0x400~0x7FC */ + +} LCD_Register; + +#endif /* __FARADAYFB_H */ + diff --git a/drivers/video/fbdev/ftlcdc100/lcd-info.c b/drivers/video/fbdev/ftlcdc100/lcd-info.c new file mode 100644 index 00000000000000..364288ab7f9d1e --- /dev/null +++ b/drivers/video/fbdev/ftlcdc100/lcd-info.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +/* + * HBP : Horizontal Back Porch + * HFP : Horizontal Front Porch + * HSPW: Horizontal Sync. Pulse Width + * PPL : Pixels-per-line = 16(PPL+1) + */ +#define ENC_PARAM_TIME0(HBP, HFP, HSPW, PPL) \ + ((((HBP) - 1) << 24) | \ + (((HFP) - 1) << 16) | \ + (((HSPW) - 1) << 8) | \ + ((((PPL) >> 4) - 1) << 2)) + +/* + * HBP : Vertical Back Porch + * HFP : Vertical Front Porch + * HSPW: Vertical Sync. Pulse Width + * LPP : Lines-per-panel = LPP + 1 + */ +#define ENC_PARAM_TIME1(VBP, VFP, VSPW, LPP) \ + ((VBP << 24) | \ + ((VFP) << 16) | \ + (((VSPW) - 1) << 10) | \ + (((LPP) - 1))) + +/* + * PRA : Pixel Rate Adaptive + * IOE : Invert Panel Output Enable + * IPC : Invert Panel Clock (Test Chip Testing) + * IHS : Invert Horisontal Sync. + * IVS : Invert Versical Sync. + * PCD : Panel Clock Divisor + */ +#define ENC_PARAM_TIME2(PRA, IOE, IPC, IHS, IVS, PCD) \ + (((PRA) << 15) | \ + ((IOE) << 14) | \ + ((IPC) << 13) | \ + ((IHS) << 12) | \ + ((IVS) << 11) | \ + (((PCD) - 1))) + +/* + * Enable YCbCr + * Enable YCbCr420 + * FIFO threadhold + * Panel type, 0-6bit, 1-8bit + * LcdVComp, when to generate interrupt, 1: start of back_porch + * Power Enable + * Big Endian Pixel/Byte Ordering + * BGR + * TFT + * LCD bits per pixel + * Controller Enable + */ + +#define ENC_PARAM_CTRL(ENYUV, ENYUV420, FIFOTH, PTYPE, VCOMP, LCD_ON, ENDIAN, BGR, TFT, BPP, LCD_EN) \ + ((ENYUV << 18) | \ + (ENYUV420 << 17) | \ + (FIFOTH << 16) | \ + (PTYPE << 15) | \ + (VCOMP << 12) | \ + (LCD_ON << 11) | \ + (ENDIAN << 9) | \ + (BGR << 8) | \ + (TFT << 5) | \ + (BPP << 1) | \ + (LCD_EN)) + +static struct lcd_param control[] = { + + { + .value = ENC_PARAM_CTRL(0, 0, 1, 1, 0x3, 1, 0x0, 1, 1, 0x3, 1), + .flags = FFB_MODE_RGB | FFB_MODE_8BPP, + }, + { + .value = ENC_PARAM_CTRL(1, 1, 1, 1, 0x3, 1, 0x0, 0, 1, 0x3, 1), + .flags = FFB_MODE_YUV420 | FFB_MODE_8BPP, + }, + { + .value = ENC_PARAM_CTRL(1, 0, 1, 1, 0x3, 1, 0x0, 0, 1, 0x4, 1), + .flags = FFB_MODE_YUV422 | FFB_MODE_16BPP, + }, + { + .value = ENC_PARAM_CTRL(0, 0, 1, 1, 0x3, 1, 0x0, 1, 1, 0x4, 1), + .flags = FFB_MODE_RGB | FFB_MODE_16BPP, + }, + { + .value = ENC_PARAM_CTRL(0, 0, 1, 1, 0x3, 1, 0x0, 1, 1, 0x5, 1), + .flags = FFB_MODE_RGB | FFB_MODE_24BPP, + }, +}; + +#ifdef CONFIG_PANEL_AUA036QN01 + +static struct lcd_param time0[] = { + + { + .value = ENC_PARAM_TIME0(7, 6, 1, 320), + .flags = FFB_MODE_RGB | FFB_MODE_YUV420 | FFB_MODE_YUV422, + }, +}; + +static struct lcd_param time1[] = { + + { + .value = ENC_PARAM_TIME1(1, 1, 1, 240), + .flags = FFB_MODE_RGB | FFB_MODE_YUV420 | FFB_MODE_YUV422, + }, +}; + +static struct lcd_param time2[] = { + + { + .value = ENC_PARAM_TIME2(0, 0, 1, 1, 1, 0x7), + .flags = FFB_MODE_RGB | FFB_MODE_YUV420 | FFB_MODE_YUV422, + }, +}; + +static struct faradayfb_mach_info ffb_mach_info = { + + .pixclock = 171521, + .xres = 320, + .yres = 240, + .max_bpp = 24, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .num_time0 = ARRAY_SIZE(time0), + .time0 = time0, + .num_time1 = ARRAY_SIZE(time1), + .time1 = time1, + .num_time2 = ARRAY_SIZE(time2), + .time2 = time2, + .num_control = ARRAY_SIZE(control), + .control = control, +}; +#endif + +#ifdef CONFIG_PANEL_AUA070VW04 +static struct lcd_param time0[] = { + + { + .value = ENC_PARAM_TIME0(88, 40, 128, 800), + .flags = FFB_MODE_RGB | FFB_MODE_YUV420 | FFB_MODE_YUV422, + }, +}; + +static struct lcd_param time1[] = { + + { + .value = ENC_PARAM_TIME1(21, 1, 3, 480), + .flags = FFB_MODE_RGB | FFB_MODE_YUV420 | FFB_MODE_YUV422, + }, +}; + +static struct lcd_param time2[] = { + + { +// .value = ENC_PARAM_TIME2(0, 1, 1, 1, 1, 0x5), + .value = ENC_PARAM_TIME2(1, 0, 0, 1, 1, 0x5), + .flags = FFB_MODE_RGB | FFB_MODE_YUV420 | FFB_MODE_YUV422, + }, +}; + +static struct faradayfb_mach_info ffb_mach_info = { + + .pixclock = 171521, + .xres = 800, + .yres = 480, + .max_bpp = 24, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .num_time0 = ARRAY_SIZE(time0), + .time0 = time0, + .num_time1 = ARRAY_SIZE(time1), + .time1 = time1, + .num_time2 = ARRAY_SIZE(time2), + .time2 = time2, + .num_control = ARRAY_SIZE(control), + .control = control, +}; + +#endif + +#ifdef CONFIG_PANEL_LW500AC9601 +static struct lcd_param time0[] = { + { + .value = ENC_PARAM_TIME0(88, 40, 128, 800), + .flags = FFB_MODE_RGB | FFB_MODE_YUV420 | FFB_MODE_YUV422, + }, +}; + +static struct lcd_param time1[] = { + { + .value = ENC_PARAM_TIME1(33, 10, 2, 480), + .flags = FFB_MODE_RGB | FFB_MODE_YUV420 | FFB_MODE_YUV422, + }, +}; + +static struct lcd_param time2[] = { + { + .value = ENC_PARAM_TIME2(1, 0, 1, 1, 1, 0x3), + .flags = FFB_MODE_RGB | FFB_MODE_YUV420 | FFB_MODE_YUV422, + }, +}; + +static struct faradayfb_mach_info ffb_mach_info = { + .pixclock = 15000000, + .xres = 800, + .yres = 480, + .max_bpp = 24, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .num_time0 = ARRAY_SIZE(time0), + .time0 = time0, + .num_time1 = ARRAY_SIZE(time1), + .time1 = time1, + .num_time2 = ARRAY_SIZE(time2), + .time2 = time2, + .num_control = ARRAY_SIZE(control), + .control = control, +}; +#endif + +#ifdef CONFIG_PANEL_CH7013A +static struct lcd_param time0[] = { + + { + .value = ENC_PARAM_TIME0(88, 40, 128, 800), + .flags = FFB_MODE_RGB, + }, +}; + +static struct lcd_param time1[] = { + { + .value = ENC_PARAM_TIME1(23, 1, 4, 600), + .flags = FFB_MODE_RGB, + }, +}; +static struct lcd_param time2[] = { + + { + .value = 0x00003801, ENC_PARAM_TIME2(0, 0, 1, 1, 1, 0x2), + .flags = FFB_MODE_RGB, + }, +}; + +static struct faradayfb_mach_info ffb_mach_info = { + + .pixclock = 35500000, + .xres = 800, + .yres = 600, + .max_bpp = 24, + + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .num_time0 = ARRAY_SIZE(time0), + .time0 = time0, + .num_time1 = ARRAY_SIZE(time1), + .time1 = time1, + .num_time2 = ARRAY_SIZE(time2), + .time2 = time2, + .num_control = ARRAY_SIZE(control), + .control = control, +}; + +#endif /* CONFIG_CH7013A */ diff --git a/drivers/video/fbdev/ftlcdc100/pingpong-module.c b/drivers/video/fbdev/ftlcdc100/pingpong-module.c new file mode 100644 index 00000000000000..d575813dbfe66e --- /dev/null +++ b/drivers/video/fbdev/ftlcdc100/pingpong-module.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#include + +void *fmem_alloc(size_t size, dma_addr_t *dma_handle, bool has_dma_coherent) +{ + struct page *page; + void *cpu_addr = NULL; + + size = PAGE_ALIGN(size); + + page = alloc_pages(GFP_DMA, get_order(size)); + if (!(page)) + goto no_page; + + *dma_handle = page_to_phys(page); + + if (!has_dma_coherent) + cpu_addr = ioremap_wc(*dma_handle, size); + else + cpu_addr = ioremap(*dma_handle, size); + + if (cpu_addr) { + do { + SetPageReserved(page); + page++; + } while (size -= PAGE_SIZE); + } else { + __free_pages(page, get_order(size)); + } + +no_page: + return cpu_addr; +} + +void fmem_free(size_t size, void *cpu_addr, dma_addr_t handle) +{ + struct page *page = pfn_to_page(handle >> PAGE_SHIFT); + + iounmap(cpu_addr); + size = PAGE_ALIGN(size); + + do { + ClearPageReserved(page); + __free_page(page); + page++; + + } while (size -= PAGE_SIZE); +} + +static int faradayfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct faradayfb_info *fbi = info->par; + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; + int ret = -EINVAL; + + DEBUG(0, 1, "Enter\n"); + + if (off < info->fix.smem_len) { + + off += fbi->screen_dma & PAGE_MASK; + vma->vm_pgoff = off >> PAGE_SHIFT; + if (!info->device->dma_coherent) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + ret = remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + } else { + DEBUG(1, 1, "buffer mapping error !!\n"); + } + + DEBUG(0, 1, "Leave\n"); + + return ret; +} + +/* + * Allocates the DRAM memory for the frame buffer. This buffer is + * remapped into a non-cached, non-buffered, memory region to + * allow palette and pixel writes to occur without flushing the + * cache. Once this area is remapped, all virtual memory + * access to the video memory should occur at the new region. + */ +static int __init faradayfb_map_video_memory(struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + + bool dma_coherent = info->device->dma_coherent; + + /* The extra PAGE size is used for the palette. */ + fbi->map_size = PAGE_ALIGN(info->fix.smem_len) + PAGE_SIZE; + fbi->map_cpu = fmem_alloc(fbi->map_size, &fbi->map_dma, dma_coherent); + + if (fbi->map_cpu) { + + memset(fbi->map_cpu, 0x1d, fbi->map_size); + info->screen_base = fbi->map_cpu + PAGE_SIZE; + fbi->screen_dma = fbi->map_dma + PAGE_SIZE; + info->fix.smem_start = fbi->screen_dma; + } + + return fbi->map_cpu ? 0 : -ENOMEM; +} + +static inline void faradayfb_clean_screen(struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + int size = info->var.xres * info->var.yres; + + if (fbi->smode & FFB_MODE_YUV422) { + memset(fbi->map_cpu, 16, size * info->var.bits_per_pixel / 8); + } else if (fbi->smode & FFB_MODE_YUV420) { + + memset(fbi->map_cpu, 16, size); + memset(fbi->map_cpu + PAGE_SIZE + ((size + 0xffff) & 0xffff0000), 128, size / 4); + memset(fbi->map_cpu + PAGE_SIZE + ((size + 0xffff) & 0xffff0000) * 5 / 4, 128, size / 4); + } +} + +static inline void faradayfb_unmap_video_memory(struct fb_info *info) +{ + struct faradayfb_info *fbi = info->par; + + fmem_free(fbi->map_size, fbi->map_cpu, fbi->map_dma); +} + +#define FRAME_SIZE_RGB(xres, yres, mbpp) ((xres) * (yres) * (mbpp) / 8) +#define FRAME_SIZE_YUV422(xres, yres, mbpp) (((xres) * (yres) * (mbpp) / 8) * 2) +#define FRAME_SIZE_YUV420(xres, yres, mbpp) (((((xres) * (yres) * (mbpp) / 8) + 0xffff) & 0xffff0000) * 3 / 2) + +unsigned int nextPowerOf2(unsigned int n) +{ + n--; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + n++; + return n; +} + +static inline u32 faradayfb_cal_frame_buf_size(struct faradayfb_info *fbi) +{ + u32 size_rgb = FRAME_SIZE_RGB(fbi->xres, fbi->yres, fbi->max_bpp); + u32 size_yuv422 = FRAME_SIZE_YUV422(fbi->xres, fbi->yres, 8); + u32 size_yuv420 = FRAME_SIZE_YUV420(fbi->xres, fbi->yres, 8); + u32 buf_size; + u32 buf_size_power_of_2; + + /* PMA needs to allocate power of 2 memory size and the size should include the palette*/ + buf_size = max(size_rgb, max(size_yuv422, size_yuv420)); + buf_size_power_of_2 = nextPowerOf2(buf_size); + if ((buf_size_power_of_2 - buf_size) < PAGE_SIZE) + buf_size_power_of_2 = nextPowerOf2(buf_size + PAGE_SIZE); + + return buf_size_power_of_2; +} + +#ifdef CONFIG_FTLCD_OSD +/************************************ + * OSD + ***********************************/ +#define FOSD_SETPOS 0x46e1 +#define FOSD_SETDIM 0x46e2 +#define FOSD_SETSCAL 0x46e3 +#define FLCD_SET_TRANSPARENT 0x46e4 +#define FLCD_SET_STRING 0x46e5 +#define FOSD_ON 0x46e6 +#define FOSD_OFF 0x46e7 +#define FRREG 0x46e8 +#define FWREG 0x46e9 + +#define dig_alpha ((16 * 10) + (16 * 26) + (16 * 3)) + +struct fosd_string { + + unsigned int Str_row; + unsigned int display_mode; + unsigned int fg_color; + unsigned int bg_color; + unsigned char Str_OSD[30]; +}; + +struct fosd_data { + + unsigned int HPos; + unsigned int VPos; + unsigned int HDim; + unsigned int VDim; + unsigned int transparent_level; + unsigned int HScal; + unsigned int VScal; + struct fosd_string Str_Data[10]; +}; + +unsigned int OSD_Font[] = { + + /* 0 */ + 0x00, 0x00, 0x00, 0x3e, 0x63, 0x67, 0x6f, 0x7b, + 0x73, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + + /* 1 */ + 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x3f, 0x00, 0x00, 0x00, 0x00, + + /* 2 */ + 0x00, 0x00, 0x00, 0x3e, 0x63, 0x03, 0x06, 0x0c, + 0x18, 0x30, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, + + /* 3 */ + 0x00, 0x00, 0x00, 0x3E, 0x63, 0x03, 0x03, 0x1e, + 0x03, 0x03, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, + + /* 4 */ + 0x00, 0x00, 0x00, 0x06, 0x0e, 0x1e, 0x36, 0x66, + 0x7f, 0x06, 0x06, 0x0f, 0x00, 0x00, 0x00, 0x00, + + /* 5 */ + 0x00, 0x00, 0x00, 0x7f, 0x60, 0x60, 0x60, 0x7e, + 0x03, 0x03, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + + /* 6 */ + 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7e, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + + /* 7 */ + 0x00, 0x00, 0x00, 0x7f, 0x63, 0x03, 0x06, 0x0c, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + + /* 8 */ + 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x3e, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + + /* 9 */ + 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x3f, + 0x03, 0x03, 0x06, 0x3c, 0x00, 0x00, 0x00, 0x00, + + /* A */ + 0x00, 0x00, 0x00, 0x08, 0x1c, 0x36, 0x63, 0x63, + 0x7f, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + + /* B */ + 0x00, 0x00, 0x00, 0x7E, 0x33, 0x33, 0x33, 0x3E, + 0x33, 0x33, 0x33, 0x7E, 0x00, 0x00, 0x00, 0x00, + + /* C */ + 0x00, 0x00, 0x00, 0x1E, 0x33, 0x61, 0x60, 0x60, + 0x60, 0x61, 0x33, 0x1E, 0x00, 0x00, 0x00, 0x00, + + /* D */ + 0x00, 0x00, 0x00, 0x7c, 0x36, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00, + + /* E */ + 0x00, 0x00, 0x00, 0x7f, 0x33, 0x31, 0x34, 0x3c, + 0x34, 0x31, 0x33, 0x7f, 0x00, 0x00, 0x00, 0x00, + + /* F */ + 0x00, 0x00, 0x00, 0x7f, 0x33, 0x31, 0x34, 0x3c, + 0x34, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, + + /* G */ + 0x00, 0x00, 0x00, 0x1E, 0x33, 0x61, 0x60, 0x60, + 0x6F, 0x63, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00, + + /* H */ + 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x64, 0x7f, + 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + + /* I */ + 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + + /* J */ + 0x00, 0x00, 0x00, 0x0f, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + + /* K */ + 0x00, 0x00, 0x00, 0x73, 0x33, 0x36, 0x36, 0x3c, + 0x36, 0x36, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00, + + /* L */ + 0x00, 0x00, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x31, 0x33, 0x7f, 0x00, 0x00, 0x00, 0x00, + + /* M */ + 0x00, 0x00, 0x00, 0x63, 0x77, 0x7f, 0x6b, 0x63, + 0x63, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + + /* N */ + 0x00, 0x00, 0x00, 0x63, 0x73, 0x7b, 0x7f, 0x6f, + 0x67, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + + /* O */ + 0x00, 0x00, 0x00, 0x1c, 0x36, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + + /* P */ + 0x00, 0x00, 0x00, 0x7e, 0x33, 0x33, 0x33, 0x3e, + 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0x00, + + /* Q */ + 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x63, 0x63, + 0x6b, 0x6f, 0x3e, 0x06, 0x07, 0x00, 0x00, 0x00, + + /* R */ + 0x00, 0x00, 0x00, 0x7e, 0x33, 0x33, 0x33, 0x3e, + 0x36, 0x33, 0x33, 0x73, 0x00, 0x00, 0x00, 0x00, + + /* S */ + 0x00, 0x00, 0x00, 0x3e, 0x63, 0x63, 0x30, 0x1c, + 0x06, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + + /* T */ + 0x00, 0x00, 0x00, 0xFF, 0x99, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + + /* U */ + 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x63, 0x63, 0x3e, 0x00, 0x00, 0x00, 0x00, + + /* V */ + 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x63, 0x36, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x00, + + /* W */ + 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x63, 0x63, + 0x6b, 0x7f, 0x77, 0x63, 0x00, 0x00, 0x00, 0x00, + + /* X */ + 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x36, 0x1c, + 0x36, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, + + /* Y */ + 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x36, 0x1c, + 0x1c, 0x1c, 0x1c, 0x3e, 0x00, 0x00, 0x00, 0x00, + + /* Z */ + 0x00, 0x00, 0x00, 0x7f, 0x63, 0x46, 0x0c, 0x18, + 0x30, 0x61, 0x63, 0x7f, 0x00, 0x00, 0x00, 0x00, + + /* space */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* = */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, + 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* , */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, +}; + +void OSD_On(struct faradayfb_info *fbi) +{ + REG32(fbi->io_base + 0x34) &= 0xfffffffe; + REG32(fbi->io_base + 0x34) |= 1; +} + +void OSD_Off(struct faradayfb_info *fbi) +{ + REG32(fbi->io_base + 0x34) &= 0xfffffffe; +} + +void OSD_Pos(struct faradayfb_info *fbi, int HPos, int VPos) +{ + REG32(fbi->io_base + 0x38) = (HPos << 10) | VPos; +} + +void OSD_Dim(struct faradayfb_info *fbi, int HDim, int VDim) +{ + REG32(fbi->io_base + 0x34) &= 0x0000001f; + REG32(fbi->io_base + 0x34) |= ((HDim << 10) | (VDim << 5)); +} + +void OSD_transparent(struct faradayfb_info *fbi, int level) +{ + REG32(fbi->io_base + 0x40) &= 0xffffff00; + REG32(fbi->io_base + 0x40) |= (level << 6); +} + +void OSD_fg_color(struct faradayfb_info *fbi, int pal0, int pal1, int pal2, int pal3) +{ + REG32(fbi->io_base + 0x3c) = (pal0) | (pal1 << 8) | (pal2 << 16) | (pal3 << 24); +} + +void OSD_bg_color(struct faradayfb_info *fbi, int pal1, int pal2, int pal3) +{ + REG32(fbi->io_base + 0x40) &= 0x000000ff; + REG32(fbi->io_base + 0x40) |= (pal1 << 8) | (pal2 << 16) | (pal3 << 24); +} + +void OSD_Scal(struct faradayfb_info *fbi, int HScal, int VScal) +{ + REG32(fbi->io_base + 0x34) &= 0xffffffe1; + REG32(fbi->io_base + 0x34) |= (HScal << 3) | (VScal << 1); +} + +void OSD_putc(struct faradayfb_info *fbi, char c, int position, unsigned int value) +{ + if (c >= '0' && c <= '9') + REG32(fbi->io_base + 0xc000 + position * 4) = ((c - '0') << 4) | value; + + else if (c >= 'A' && c <= 'Z') + REG32(fbi->io_base + 0xc000 + position * 4) = ((c - 'A' + 10) << 4) | value; + + if (c == ' ') + REG32(fbi->io_base + 0xc000 + position * 4) = (('Z' - 'A' + 10 + 1) << 4) | value; + + if (c == '=') + REG32(fbi->io_base + 0xc000 + position * 4) = (('Z' - 'A' + 10 + 2) << 4) | value; + + if (c == ',') + REG32(fbi->io_base + 0xc000 + position * 4) = (('Z' - 'A' + 10 + 3) << 4) | value; +} + +void OSD_puts(struct faradayfb_info *fbi, char *str, int position, unsigned int value) +{ + int i; + + for (i = 0; i < strlen(str); i++) + OSD_putc(fbi, *(str + i), position + i, value); +} + +void OSD_String(struct faradayfb_info *fbi, int row, int mode, char *str, int fg_color, int bg_color) +{ + int i, j, x, y; + unsigned int value = fg_color | bg_color; + + /* 10 digit & 26 alphabet & ' ' & '=' & ',' */ + for (i = 0; i < dig_alpha; i++) { + + x = 0; + y = OSD_Font[i]; + + for (j = 0; j < 12; j++) { /* reorder */ + if (y & 1) + x |= 1; + y >>= 1; + x <<= 1; + } + + x >>= 1; + REG32(fbi->io_base + 0x8000 + i * 4) = x; + } + + OSD_puts(fbi, str, row, value); + + if (mode == 2) { /* YCbCr */ + + OSD_fg_color(fbi, 0x57, 0x88, 0x3B, 0xFF); + OSD_bg_color(fbi, 0x57, 0x88, 0x3B); + } else { + OSD_fg_color(fbi, 0x07, 0x38, 0xC0, 0xFF); + OSD_bg_color(fbi, 0x07, 0x38, 0xc0); + } +} +#endif /* CONFIG_FTLCD_OSD */ + +struct andesIO { + + unsigned long Regaddr; + unsigned long Regoffset; + unsigned long Regvalue; +}; + +static int faradayfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) +{ + int ret = 0; +#ifdef CONFIG_FTLCD_OSD + struct faradayfb_info *fbi = info->par; + int i; + struct fosd_data fosd; + + struct andesIO IOAccess; + unsigned long *Regaccess; +#endif + + DEBUG(0, 1, "Enter\n"); + + switch (cmd) { +#ifdef CONFIG_FTLCD_OSD + case FRREG: + + if (copy_from_user(&IOAccess, (struct andesIO *)arg, sizeof(struct andesIO))) { + + ret = -EFAULT; + break; + } + + Regaccess = (unsigned long *)(((IOAccess.Regaddr >> 4) | 0xF0000000UL) + IOAccess.Regoffset); + + IOAccess.Regvalue = *(Regaccess); + + if (copy_to_user((struct andesIO *)arg, &IOAccess, sizeof(struct andesIO))) { + ret = -EFAULT; + break; + } + + break; + + case FWREG: + + if (copy_from_user(&IOAccess, (struct andesIO *)arg, sizeof(struct andesIO))) { + ret = -EFAULT; + break; + } + + Regaccess = (unsigned long *)(((IOAccess.Regaddr >> 4) | 0xF0000000UL) + IOAccess.Regoffset); + *(Regaccess) = IOAccess.Regvalue; + + break; + + case FOSD_ON: + + DEBUG(1, 1, "FOSD_ON:\n"); + OSD_On(fbi); + break; + + case FOSD_OFF: + + DEBUG(1, 1, "FOSD_OFF:\n"); + OSD_Off(fbi); + break; + + case FOSD_SETPOS: + + DEBUG(1, 1, "FOSD_SETPOS:\n"); + + if (copy_from_user(&fosd, (unsigned int *)arg, 2 * sizeof(unsigned int))) { + + ret = -EFAULT; + break; + } + + OSD_Pos(fbi, fosd.HPos, fosd.VPos); + DEBUG(1, 1, "OSD_Pos = %d %d\n", fosd.HPos, fosd.VPos); + break; + + case FOSD_SETDIM: + + DEBUG(1, 1, "FOSD_SETDIM:\n"); + + if (copy_from_user(&fosd, (unsigned int *)arg, 4 * sizeof(unsigned int))) { + + ret = -EFAULT; + break; + } + + OSD_Dim(fbi, fosd.HDim, fosd.VDim); + DEBUG(1, 1, "OSD_Dim = %d %d\n", fosd.HDim, fosd.VDim); + break; + + case FOSD_SETSCAL: + + DEBUG(1, 1, "FOSD_SETSCAL:\n"); + + if (copy_from_user(&fosd, (unsigned int *)arg, 7 * sizeof(unsigned int))) { + ret = -EFAULT; + break; + } + + OSD_Scal(fbi, fosd.HScal, fosd.VScal); + break; + + case FLCD_SET_TRANSPARENT: + + DEBUG(1, 1, "FLCD_SET_TRANSPARENT:\n"); + + if (copy_from_user(&fosd, (unsigned int *)arg, 5 * sizeof(unsigned int))) { + + ret = -EFAULT; + break; + } + + OSD_transparent(fbi, fosd.transparent_level); + DEBUG(1, 1, "OSD_transparent = %d\n", fosd.transparent_level); + break; + + case FLCD_SET_STRING: + + DEBUG(1, 1, "FLCD_SET_STRING:\n"); + + if (copy_from_user(&fosd, (unsigned int *)arg, sizeof(struct fosd_data))) { + + ret = -EFAULT; + break; + } + + for (i = 0; i < fosd.VDim; i++) + + OSD_String(fbi, fosd.Str_Data[i].Str_row, + fosd.Str_Data[i].display_mode, + fosd.Str_Data[i].Str_OSD, + fosd.Str_Data[i].fg_color, + fosd.Str_Data[i].bg_color); + break; +#endif /* CONFIG_FTLCD_OSD */ + + default: + + DEBUG(1, 1, "IOCTL CMD(%08u) no define!\n", cmd); + ret = -EFAULT; + break; + } + + DEBUG(0, 1, "Leave\n"); + return ret; +} From 67a64172c4b25aca037c1c793d30c7a39e8aecce Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Fri, 19 May 2023 14:51:53 +0800 Subject: [PATCH 040/169] soc: andes: atcdmac300: fix channel pending when burn-in the dmac driver 1. Fixed the issue "Cannot change direction while the channel has pending" while burn-in the dmac driver. 2. Remove "raw_spin_lock / raw_spin_unlock" from legacy_dma_irq_router 3. Add a handle procedure to handle the status error while "DMAD_FLAGS_RING_MODE". 4. It is suggested in the Gitea code review: https://gitea.andestech.com/RD-SW/linux/pulls/17#issuecomment-73741 Signed-off-by: CL Wang --- drivers/mmc/host/ftsdc010.c | 22 +++-- drivers/soc/andes/atcdmac300.c | 176 ++++++++++++++++++++++----------- drivers/soc/andes/dmad_intc.c | 8 +- include/soc/andes/atcdmac300.h | 5 +- 4 files changed, 135 insertions(+), 76 deletions(-) diff --git a/drivers/mmc/host/ftsdc010.c b/drivers/mmc/host/ftsdc010.c index ed43d596d61ad9..220d041e1b63e1 100644 --- a/drivers/mmc/host/ftsdc010.c +++ b/drivers/mmc/host/ftsdc010.c @@ -401,12 +401,13 @@ static void do_pio_write(struct ftsdc_host *host) host->complete_what = COMPLETION_FINALIZE; } -static void do_dma_access(struct ftsdc_host *host) +static void do_dma_access(struct ftsdc_host *host, struct mmc_data *data) { int res; unsigned long timeout; dmad_chreq *req = host->dma_req; dmad_drb *drb = 0; + int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0; while (host->buf_sgptr < host->mrq->data->sg_len) { @@ -431,6 +432,15 @@ static void do_dma_access(struct ftsdc_host *host) drb->addr1 = (dma_addr_t) (unsigned long)host->buf_ptr; drb->req_cycle = dmad_bytes_to_cycles(req, host->buf_bytes); drb->sync = &host->dma_complete; + + res = dmad_config_channel_dir(host->dma_req, + rw ? DMAD_DIR_A1_TO_A0 : DMAD_DIR_A0_TO_A1, + drb); + if (res != 0) { + host->mrq->data->error = -ENODEV; + goto err; + } + timeout = SDC_TIMEOUT_BASE * ((host->buf_bytes + 511) >> 9); res = dmad_submit_request(req, drb, 1); if (res != 0) { @@ -464,7 +474,7 @@ static void ftsdc_work(struct work_struct *work) ftsdc_enable_irq(host, false); if (host->dodma) { - do_dma_access(host); + do_dma_access(host, data); dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); } else { @@ -722,20 +732,12 @@ static int ftsdc_prepare_buffer(struct ftsdc_host *host, struct mmc_data *data) host->buf_active = rw ? XFER_WRITE : XFER_READ; if (host->dodma) { u32 dma_len; - u32 drb_size; dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); if (dma_len == 0) return -ENOMEM; - dmad_config_channel_dir(host->dma_req, - rw ? DMAD_DIR_A1_TO_A0 : - DMAD_DIR_A0_TO_A1); - drb_size = dmad_max_size_per_drb(host->dma_req); - if (drb_size < (data->blksz & data->blocks)) - return -ENODEV; - host->dma_finish = false; } return 0; diff --git a/drivers/soc/andes/atcdmac300.c b/drivers/soc/andes/atcdmac300.c index 1089938e3943ad..1c704099156e4f 100644 --- a/drivers/soc/andes/atcdmac300.c +++ b/drivers/soc/andes/atcdmac300.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2018 Andes Technology Corporation - * */ #include @@ -41,12 +40,12 @@ static inline void REG_WRITE(addr_t d, unsigned long r) #define DMAD_DRB_POOL_SIZE 32 /* 128 */ -static inline addr_t din(unsigned long r) +static inline u32 din(unsigned long r) { return REG_READ(r); } -static inline void dout(addr_t d, unsigned long r) +static inline void dout(u32 d, unsigned long r) { REG_WRITE(d, r); } @@ -86,6 +85,8 @@ typedef struct dmad_drq { unsigned long enable_port; /* enable register */ unsigned long src_port; /* source address register */ unsigned long dst_port; /* dest address register */ + unsigned long src_port_h; /* source address register (high part) */ + unsigned long dst_port_h; /* dest address register (high part) */ unsigned long cyc_port; /* size(cycle) register */ u32 flags; /* enum DMAD_CHREQ_FLAGS */ @@ -127,10 +128,10 @@ typedef struct dmad_drq { /* ring_size - period_size * periods */ dma_addr_t remnant_size; - dma_addr_t sw_ptr; /* sw pointer */ + u64 sw_ptr; /* sw pointer */ int sw_p_idx; /* current ring_ptr */ dma_addr_t sw_p_off; /* offset to period base */ - + int channel; } dmad_drq; static inline void dmad_enable_channel(dmad_drq *drq) @@ -148,12 +149,6 @@ static inline addr_t dmad_is_channel_enabled(dmad_drq *drq) return (addr_t) getbl(CHEN, drq->enable_port); } -/* system irq number (per channel, ahb) */ -static const unsigned int ahb_irqs[DMAD_AHB_MAX_CHANNELS] = { - DMA_IRQ0, DMA_IRQ1, DMA_IRQ2, DMA_IRQ3, - DMA_IRQ4, DMA_IRQ5, DMA_IRQ6, DMA_IRQ7, -}; - /* Driver data structure, one instance per system */ typedef struct DMAD_DATA_STRUCT { /* Driver data initialization flag */ @@ -514,7 +509,7 @@ static irqreturn_t dmad_ahb_isr(int irq, void *dev_id) } /* Process DRBs according to interrupt reason */ - if (tc_int) { + if (tc_int || ((drq->flags & DMAD_FLAGS_RING_MODE) && err_int)) { dmad_dbg("dma finish\n"); dmad_dbg("finish drb(%d 0x%08x) addr0(0x%08llx) addr1 (0x%08llx) size(0x%08llx)\n", drb->node, (u32) drb, drb->src_addr, drb->dst_addr, @@ -544,20 +539,33 @@ static irqreturn_t dmad_ahb_isr(int irq, void *dev_id) // Kick-off DMA for next DRB // - Source and destination address if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) { - dout(drb_iter->addr1, - (unsigned long)drq->src_port); - dout(drb_iter->addr0, - (unsigned long)drq->dst_port); + dout(drb_iter->addr1, (unsigned long)drq->src_port); + dout(drb_iter->addr0, (unsigned long)drq->dst_port); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dout((drb_iter->addr1 >> 32), (unsigned long)drq->src_port_h); + dout((drb_iter->addr0 >> 32), (unsigned long)drq->dst_port_h); +#else + dout(0, (unsigned long)drq->src_port_h); + dout(0, (unsigned long)drq->dst_port_h); +#endif } else { - dout(drb_iter->addr0, - (unsigned long)drq->src_port); - dout(drb_iter->addr1, - (unsigned long)drq->dst_port); + dout(drb_iter->addr0, (unsigned long)drq->src_port); + dout(drb_iter->addr1, (unsigned long)drq->dst_port); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dout((drb_iter->addr0 >> 32), (unsigned long)drq->src_port_h); + dout((drb_iter->addr1 >> 32), (unsigned long)drq->dst_port_h); +#else + dout(0, (unsigned long)drq->src_port_h); + dout(0, (unsigned long)drq->dst_port_h); +#endif } /* - Transfer size (in units of source width) */ dout(drb_iter->req_cycle, (unsigned long)drq->cyc_port); + dout(drb->ch_control, (unsigned long)drq->enable_port); /* Kick off next request */ dmad_enable_channel(drq); @@ -633,7 +641,7 @@ static void dmad_ahb_config_dir(dmad_chreq *ch_req, dmad_dbg("%s() channel_cmds(0x%08lx)\n", __func__, channel_cmds[0]); channel_cmds[0] &= ~(u32) (SRCWIDTH_MASK | SRCADDRCTRL_MASK | DSTWIDTH_MASK | DSTADDRCTRL_MASK | SRC_HS | - DST_HS | SRCREQSEL_MASK | DSTREQSEL_MASK); + DST_HS | SRCREQSEL_MASK | DSTREQSEL_MASK | (1 << CHEN)); if (ahb_req->tx_dir == 0) { dmad_dbg("%s() addr0 --> addr1\n", __func__); @@ -693,8 +701,7 @@ static int dmad_ahb_init(dmad_chreq *ch_req) err = request_irq(pdata->irqs[channel + 1], dmad_ahb_isr, 0, "AHB_DMA", (void *)(unsigned long)(channel + 1)); if (unlikely(err != 0)) { - dmad_err("unable to request IRQ %d for AHB DMA (error %d)\n", - ahb_irqs[channel], err); + dmad_err("unable to request IRQ for channel:%d err:%d\n", channel, err); return err; } spin_lock_irqsave(&dmad.drq_pool_lock, lock_flags); @@ -725,11 +732,15 @@ static int dmad_ahb_init(dmad_chreq *ch_req) /* SRCADR and DESADR */ dout(0, (unsigned long)drq->src_port); + dout(0, (unsigned long)drq->src_port_h); + dout(0, (unsigned long)drq->dst_port); + dout(0, (unsigned long)drq->dst_port_h); /* CYC (transfer size) */ dout(0, (unsigned long)drq->cyc_port); /* LLP */ dout(0, (unsigned long)channel_base + CH_LLP_LOW_OFF); + dout(0, (unsigned long)channel_base + CH_LLP_HIGH_OFF); /* TOT_SIZE - not now */ spin_unlock_irqrestore(&dmad.drq_pool_lock, lock_flags); @@ -900,12 +911,15 @@ int dmad_channel_alloc(dmad_chreq *ch_req) /* Record the channel's queue handle in client's struct */ ch_req->drq = drq_iter; + drq_iter->channel = i; if (ch_req->controller == DMAD_DMAC_AHB_CORE) { drq_iter->channel_base = (unsigned long)DMAC_BASE_CH(i); drq_iter->enable_port = (unsigned long)CH_CTL(i); drq_iter->src_port = (unsigned long)CH_SRC_L(i); drq_iter->dst_port = (unsigned long)CH_DST_L(i); drq_iter->cyc_port = (unsigned long)CH_SIZE(i); + drq_iter->src_port_h = (unsigned long)CH_SRC_H(i); + drq_iter->dst_port_h = (unsigned long)CH_DST_H(i); } /* drb-0 is an invalid node - for node validation */ drb_iter = &drq_iter->drb_pool[0]; @@ -943,6 +957,13 @@ int dmad_channel_alloc(dmad_chreq *ch_req) goto _err_exit; } + drb_iter = &drq_iter->drb_pool[0]; + for (i = 0; i < DMAD_DRB_POOL_SIZE; ++i, ++drb_iter) { + err = dmad_config_channel_dir(ch_req, ch_req->ahb_req.tx_dir, drb_iter); + if (err != 0) + goto _err_exit; + } + drq_iter->ring_size = ch_req->ring_size; drq_iter->period_size = ch_req->period_size; drq_iter->remnant_size = (dma_addr_t) remnant; @@ -1110,52 +1131,25 @@ EXPORT_SYMBOL_GPL(dmad_channel_enable); * that bi-direction mode and ring mode are mutual-exclusive from user's * perspective. */ -int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir) +int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir, dmad_drb *drb) { dmad_drq *drq; addr_t channel_cmds[1]; - unsigned long lock_flags; - u8 cur_dir; if (unlikely(ch_req == NULL)) return -EFAULT; drq = (dmad_drq *) ch_req->drq; - if (unlikely(drq == NULL)) return -EBADR; - if (unlikely(!(ch_req->flags & DMAD_FLAGS_BIDIRECTION))) { - dmad_err("%s() Channel is not configured as bidirectional!\n", - __func__); - return -EFAULT; - } - - cur_dir = drq->flags & DMAD_DRQ_DIR_MASK; - if (dir == cur_dir) { - dmad_dbg("%s() cur_dir(%d) == dir(%d) skip reprogramming hw.\n", - __func__, cur_dir, dir); - return 0; - } - - spin_lock_irqsave(&drq->drb_pool_lock, lock_flags); - - if (unlikely((drq->sbt_head != 0) /*||dmad_is_channel_enabled(drq) */)) { - spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); - dmad_err("%s() Cannot change direction while the channel has pending requests!\n", - __func__); - return -EFAULT; - } - if (ch_req->controller == DMAD_DMAC_AHB_CORE) { channel_cmds[0] = din((unsigned long)drq->enable_port); ch_req->ahb_req.tx_dir = dir; dmad_ahb_config_dir(ch_req, (unsigned long *)channel_cmds); - dout(channel_cmds[0], (unsigned long)drq->enable_port); + drb->ch_control = channel_cmds[0]; } - spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); - return 0; } EXPORT_SYMBOL_GPL(dmad_config_channel_dir); @@ -1520,13 +1514,30 @@ int dmad_submit_request(dmad_chreq *ch_req, dmad_drb *drb, u8 keep_fired) if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) { dout(drb->addr1, (unsigned long)drq->src_port); dout(drb->addr0, (unsigned long)drq->dst_port); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dout((drb->addr1 >> 32), (unsigned long)drq->src_port_h); + dout((drb->addr0 >> 32), (unsigned long)drq->dst_port_h); +#else + dout(0, (unsigned long)drq->src_port_h); + dout(0, (unsigned long)drq->dst_port_h); +#endif } else { dout(drb->addr0, (unsigned long)drq->src_port); dout(drb->addr1, (unsigned long)drq->dst_port); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dout((drb->addr0 >> 32), (unsigned long)drq->src_port_h); + dout((drb->addr1 >> 32), (unsigned long)drq->dst_port_h); +#else + dout(0, (unsigned long)drq->src_port_h); + dout(0, (unsigned long)drq->dst_port_h); +#endif } /* Transfer size (in units of source width) */ dout(drb->req_cycle, (unsigned long)drq->cyc_port); + dout(drb->ch_control, (unsigned long)drq->enable_port); /* Enable DMA channel (Kick off transmission when client * enable it's transfer state) @@ -1648,17 +1659,32 @@ static inline int dmad_kickoff_requests_internal(dmad_drq *drq) drb->req_cycle, drb->state); if (drb->state == DMAD_DRB_STATE_SUBMITTED) { - /* Transfer size (in units of source width) */ - dout(drb->req_cycle, (unsigned long)drq->cyc_port); /* Source and destination address */ if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) { dout(drb->addr1, (unsigned long)drq->src_port); dout(drb->addr0, (unsigned long)drq->dst_port); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dout((drb->addr1 >> 32), (unsigned long)drq->src_port_h); + dout((drb->addr0 >> 32), (unsigned long)drq->dst_port_h); +#else + dout(0, (unsigned long)drq->src_port_h); + dout(0, (unsigned long)drq->dst_port_h); +#endif } else { dout(drb->addr0, (unsigned long)drq->src_port); dout(drb->addr1, (unsigned long)drq->dst_port); +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dout((drb->addr0 >> 32), (unsigned long)drq->src_port_h); + dout((drb->addr1 >> 32), (unsigned long)drq->dst_port_h); +#else + dout(0, (unsigned long)drq->src_port_h); + dout(0, (unsigned long)drq->dst_port_h); +#endif } + /* Transfer size (in units of source width) */ + dout(drb->req_cycle, (unsigned long)drq->cyc_port); + dout(drb->ch_control, (unsigned long)drq->enable_port); drb->state = DMAD_DRB_STATE_EXECUTED; } @@ -1724,18 +1750,35 @@ int dmad_kickoff_requests(dmad_chreq *ch_req) goto _safe_exit; } - /* Transfer size (in units of source width) */ - dout(req_cycle, (unsigned long)drq->cyc_port); - /* Source and destination address */ if (drq->flags & DMAD_DRQ_DIR_A1_TO_A0) { dout(drb->addr1, (unsigned long)drq->src_port); dout(drb->addr0, (unsigned long)drq->dst_port); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dout((drb->addr1 >> 32), (unsigned long)drq->src_port_h); + dout((drb->addr0 >> 32), (unsigned long)drq->dst_port_h); +#else + dout(0, (unsigned long)drq->src_port_h); + dout(0, (unsigned long)drq->dst_port_h); +#endif + } else { dout(drb->addr0, (unsigned long)drq->src_port); dout(drb->addr1, (unsigned long)drq->dst_port); + +#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT + dout((drb->addr0 >> 32), (unsigned long)drq->src_port_h); + dout((drb->addr1 >> 32), (unsigned long)drq->dst_port_h); +#else + dout(0, (unsigned long)drq->src_port_h); + dout(0, (unsigned long)drq->dst_port_h); +#endif } + /* Transfer size (in units of source width) */ + dout(req_cycle, (unsigned long)drq->cyc_port); + dout(drb->ch_control, (unsigned long)drq->enable_port); drb->state = DMAD_DRB_STATE_EXECUTED; /* Enable DMA channel */ @@ -1850,7 +1893,8 @@ int dmad_update_ring_sw_ptr(dmad_chreq *ch_req, dma_addr_t sw_ptr, dmad_drq *drq; unsigned long lock_flags; dma_addr_t hw_off = 0, ring_ptr; - dma_addr_t sw_p_off, ring_p_off, period_bytes; + u64 sw_p_off; + dma_addr_t ring_p_off, period_bytes; dma_addr_t remnant_size; int period_size; int sw_p_idx, ring_p_idx, period, periods; @@ -1912,6 +1956,11 @@ int dmad_update_ring_sw_ptr(dmad_chreq *ch_req, dma_addr_t sw_ptr, drq->ring_base; drb->addr1 = drq->dev_addr; drb->req_cycle = 0; // redundent, though, no harm to performance + if (dmad_config_channel_dir(ch_req, ch_req->ahb_req.tx_dir, drb) != 0) { + dmad_err("%s() dmad_config_channel_dir failed!\n", __func__); + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -ENOSPC; + } dmad_dbg("init_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", (u32) drb->node, (u32) drb, drb->src_addr, @@ -2077,6 +2126,12 @@ int dmad_update_ring_sw_ptr(dmad_chreq *ch_req, dma_addr_t sw_ptr, else drb->req_cycle = period_size; + if (dmad_config_channel_dir(ch_req, ch_req->ahb_req.tx_dir, drb) != 0) { + dmad_err("%s() dmad_config_channel_dir failed!\n", __func__); + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -ENOSPC; + } + dmad_dbg("inbtw_drb(%d 0x%08x) addr0(0x%08llx)addr1 (0x%08llx) size(0x%08llx) state(%d)\n", (u32) drb->node, (u32) drb, drb->addr0, drb->addr1, drb->req_cycle, drb->state); @@ -2094,6 +2149,11 @@ int dmad_update_ring_sw_ptr(dmad_chreq *ch_req, dma_addr_t sw_ptr, drq->ring_base; drb->addr1 = drq->dev_addr; drb->req_cycle = sw_p_off; + if (dmad_config_channel_dir(ch_req, ch_req->ahb_req.tx_dir, drb) != 0) { + dmad_err("%s() dmad_config_channel_dir failed!\n", __func__); + spin_unlock_irqrestore(&drq->drb_pool_lock, lock_flags); + return -ENOSPC; + } dmad_dbg("swptr_drb(%d 0x%08x) addr0(0x%08llx) addr1(0x%08llx) size(0x%08llx) state(%d)\n", (u32) drb->node, (u32) drb, drb->addr0, drb->addr1, diff --git a/drivers/soc/andes/dmad_intc.c b/drivers/soc/andes/dmad_intc.c index 0e9d0f5499e569..46c2fdadc9b461 100644 --- a/drivers/soc/andes/dmad_intc.c +++ b/drivers/soc/andes/dmad_intc.c @@ -10,25 +10,21 @@ #ifdef CONFIG_PLATFORM_AHBDMA -void AHBDMA_irq_rounter(struct irq_desc *desc) +static void legacy_dma_irq_router(struct irq_desc *desc) { int ahb_irq; struct irq_desc *ahb_desc; - raw_spin_lock(&desc->lock); ahb_irq = dmad_probe_irq_source_ahb(); if (ahb_irq >= 0) { ahb_irq = get_irq(ahb_irq); ahb_desc = irq_to_desc(ahb_irq); ahb_desc->irq_data.irq = ahb_irq; - raw_spin_unlock(&desc->lock); ahb_desc->handle_irq(ahb_desc); - raw_spin_lock(&desc->lock); } desc->irq_data.chip->irq_unmask(&desc->irq_data); desc->irq_data.chip->irq_eoi(&desc->irq_data); - raw_spin_unlock(&desc->lock); } int intc_ftdmac020_init_irq(int irq) @@ -41,7 +37,7 @@ int intc_ftdmac020_init_irq(int irq) ret = irq_set_chip(i, &dummy_irq_chip); irq_set_handler(i, handle_simple_irq); } - irq_set_chained_handler(irq, AHBDMA_irq_rounter); + irq_set_chained_handler(irq, legacy_dma_irq_router); return 0; } #endif /* CONFIG_PLATFORM_AHBDMA */ diff --git a/include/soc/andes/atcdmac300.h b/include/soc/andes/atcdmac300.h index cbb81b4e606239..39d501f6701c27 100644 --- a/include/soc/andes/atcdmac300.h +++ b/include/soc/andes/atcdmac300.h @@ -423,6 +423,7 @@ typedef struct dmad_drb { */ dma_addr_t req_cycle; + u32 ch_control; /* (in) if non-null, this sync object will be signaled upon dma * completion (for blocked-waiting dma completion) */ @@ -476,7 +477,7 @@ extern dma_addr_t dmad_probe_hw_ptr_dst(dmad_chreq *ch_req); /***************************************************************************** * routines only valid in discrete (non-ring) mode */ -extern int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir); +extern int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir, dmad_drb *drb); extern int dmad_alloc_drb(dmad_chreq *ch_req, dmad_drb **drb); extern int dmad_free_drb(dmad_chreq *ch_req, dmad_drb *drb); extern int dmad_submit_request(dmad_chreq *ch_req, @@ -514,7 +515,7 @@ static inline dma_addr_t dmad_probe_hw_ptr_src(dmad_chreq *ch_req) { return (dma_addr_t)NULL; } static inline dma_addr_t dmad_probe_hw_ptr_dst(dmad_chreq *ch_req) { return (dma_addr_t)NULL; } -static inline int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir) +static inline int dmad_config_channel_dir(dmad_chreq *ch_req, u8 dir, dmad_drb *drb) { return -EFAULT; } static inline int dmad_alloc_drb(dmad_chreq *ch_req, dmad_drb **drb) { return -EFAULT; } From 4fed16bd92681856dfa15d93268b803a12860e6f Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Fri, 12 May 2023 14:02:22 +0800 Subject: [PATCH 041/169] riscv: andes: defconfig: enable CONFIG_HZ_100 for andes_defconfig Signed-off-by: Hui Min Mina Chou --- arch/riscv/configs/andes_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/andes_defconfig b/arch/riscv/configs/andes_defconfig index 71bbb5c5359c5f..b5556e42daf365 100644 --- a/arch/riscv/configs/andes_defconfig +++ b/arch/riscv/configs/andes_defconfig @@ -1,6 +1,7 @@ CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_NO_HZ_IDLE=y +CONFIG_HZ_100=y CONFIG_HIGH_RES_TIMERS=y CONFIG_BPF_SYSCALL=y CONFIG_PREEMPT=y From 3ca374973aa2976f3b3f0795b2a3e64aff1ae437 Mon Sep 17 00:00:00 2001 From: Dylan Jhong Date: Fri, 19 May 2023 15:27:14 +0800 Subject: [PATCH 042/169] net: andes: ftmac100: use casting of u64 to avoid overflow Fix "-Wshift-count-overflow" compile error by casting to u64. Signed-off-by: Dylan Jhong Reviewed-on: https://gitea.andestech.com/RD-SW/linux/pulls/23 --- drivers/net/ethernet/faraday/ftmac100.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index 701f3e9ac6ac3c..22a2492052939b 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -305,12 +305,12 @@ static void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes, dma_addr_t addr) { rxdes->rxdes2 = cpu_to_le32(addr); - rxdes->rxdes3 = cpu_to_le32(addr >> 32); + rxdes->rxdes3 = cpu_to_le32((u64)addr >> 32); } static dma_addr_t ftmac100_rxdes_get_dma_addr(struct ftmac100_rxdes *rxdes) { - return le32_to_cpu(rxdes->rxdes2) | (dma_addr_t)le32_to_cpu(rxdes->rxdes3) << 32; + return le32_to_cpu(rxdes->rxdes2) | (u64)le32_to_cpu(rxdes->rxdes3) << 32; } static void ftmac100_rxdes_set_page(struct ftmac100 *priv, int index, struct page *page) @@ -567,12 +567,12 @@ static void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes, dma_addr_t addr) { txdes->txdes2 = cpu_to_le32(addr); - txdes->txdes3 = cpu_to_le32(addr >> 32); + txdes->txdes3 = cpu_to_le32((u64)addr >> 32); } static dma_addr_t ftmac100_txdes_get_dma_addr(struct ftmac100_txdes *txdes) { - return le32_to_cpu(txdes->txdes2) | (dma_addr_t)le32_to_cpu(txdes->txdes3) << 32; + return le32_to_cpu(txdes->txdes2) | (u64)le32_to_cpu(txdes->txdes3) << 32; } static void ftmac100_txdes_skb_reset(struct ftmac100 *priv, int index) From 61cc41708cab992bddea73d79af50adf20f171f0 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 23 May 2023 14:55:18 +0800 Subject: [PATCH 043/169] soc: andes: injection: injection script should adhere to the rules of the shell script It is suggested in the Gitea code review: https://gitea.andestech.com/RD-SW/linux/pulls/17#issuecomment-73617 Signed-off-by: Charles Ci-Jyun Wu --- drivers/soc/andes/inject_init.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/soc/andes/inject_init.c b/drivers/soc/andes/inject_init.c index 43e2c6e38fbf10..24408731f8fc39 100644 --- a/drivers/soc/andes/inject_init.c +++ b/drivers/soc/andes/inject_init.c @@ -52,10 +52,11 @@ static int __init inject_init(void) inject = (void *)ioremap((phys_addr_t)INJECT_START, INJECT_SIZE); p = (unsigned char *)inject; - if (p[0] != '#' && p[0] != 0x79) - p[0] = 0; + if (p[0] == '#' && p[1] == '!') + proc_create("inject", 0444, NULL, &inject_fops); + else + iounmap(inject); - proc_create("inject", 0444, NULL, &inject_fops); return 0; } arch_initcall(inject_init); From 85803cf8c4d516e16fe782a7c5024d711d1f0ea7 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 23 May 2023 16:15:30 +0800 Subject: [PATCH 044/169] rtc: andes: atcrtc100: change reads the RTC four times to one time It is suggested in the Gitea code review: https://gitea.andestech.com/RD-SW/linux/pulls/17#issuecomment-73729 Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/configs/andes-support.config | 3 +++ drivers/rtc/rtc-atcrtc100.c | 21 +++++++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 4db18a169178ec..8df4241ad3797a 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -31,3 +31,6 @@ CONFIG_FFB_MODE_24BPP=y CONFIG_SND_FTSSP010=y CONFIG_SND_FTSSP010_AC97=y + +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_ATCRTC100=y diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c index a9b9dce6d181a1..e3bdef0f75769b 100644 --- a/drivers/rtc/rtc-atcrtc100.c +++ b/drivers/rtc/rtc-atcrtc100.c @@ -49,14 +49,14 @@ #define MIN_MSK (0x3f) #define SEC_OFF 0 #define SEC_MSK (0x3f) -#define RTC_SECOND \ - ((RTC_CNT >> SEC_OFF) & SEC_MSK) /* RTC sec */ -#define RTC_MINUTE \ - ((RTC_CNT >> MIN_OFF) & MIN_MSK) /* RTC min */ -#define RTC_HOUR \ - ((RTC_CNT >> HOR_OFF) & HOR_MSK) /* RTC hour */ -#define RTC_DAYS \ - ((RTC_CNT >> DAY_OFF) & DAY_MSK) /* RTC day */ +#define RTC_SECOND(x) \ + ((x >> SEC_OFF) & SEC_MSK) /* RTC sec */ +#define RTC_MINUTE(x) \ + ((x >> MIN_OFF) & MIN_MSK) /* RTC min */ +#define RTC_HOUR(x) \ + ((x >> HOR_OFF) & HOR_MSK) /* RTC hour */ +#define RTC_DAYS(x) \ + ((x >> DAY_OFF) & DAY_MSK) /* RTC day */ #define RTC_ALM_SECOND \ ((RTC_ALM >> SEC_OFF) & SEC_MSK) /* RTC alarm sec */ #define RTC_ALM_MINUTE \ @@ -130,8 +130,9 @@ static int atc_alarm_irq_enable(struct device *dev, unsigned int enabled) static int atc_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct atc_rtc *rtc = dev_get_drvdata(dev); - unsigned long time = RTC_DAYS * 86400 + RTC_HOUR * 3600 - + RTC_MINUTE * 60 + RTC_SECOND; + unsigned long rtc_cnt = RTC_CNT; + unsigned long time = RTC_DAYS(rtc_cnt) * 86400 + RTC_HOUR(rtc_cnt) * 3600 + + RTC_MINUTE(rtc_cnt) * 60 + RTC_SECOND(rtc_cnt); rtc_time64_to_tm(time, tm); if (rtc_valid_tm(tm) < 0) { From c2be1a19e1c6315801a5beca07e2e3c92a0ec7aa Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 23 May 2023 16:51:16 +0800 Subject: [PATCH 045/169] fbdev: andes: ftlcdc100: change OSD_putc if...else to switch It is suggested in the Gitea code review: https://gitea.andestech.com/RD-SW/linux/pulls/17#issuecomment-73873 Signed-off-by: Charles Ci-Jyun Wu --- drivers/video/fbdev/ftlcdc100/pingpong-module.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/video/fbdev/ftlcdc100/pingpong-module.c b/drivers/video/fbdev/ftlcdc100/pingpong-module.c index d575813dbfe66e..83f4752da6117a 100644 --- a/drivers/video/fbdev/ftlcdc100/pingpong-module.c +++ b/drivers/video/fbdev/ftlcdc100/pingpong-module.c @@ -403,20 +403,27 @@ void OSD_Scal(struct faradayfb_info *fbi, int HScal, int VScal) void OSD_putc(struct faradayfb_info *fbi, char c, int position, unsigned int value) { - if (c >= '0' && c <= '9') + switch (c) { + case '0' ... '9': REG32(fbi->io_base + 0xc000 + position * 4) = ((c - '0') << 4) | value; + break; - else if (c >= 'A' && c <= 'Z') + case 'A' ... 'Z': REG32(fbi->io_base + 0xc000 + position * 4) = ((c - 'A' + 10) << 4) | value; + break; - if (c == ' ') + case ' ': REG32(fbi->io_base + 0xc000 + position * 4) = (('Z' - 'A' + 10 + 1) << 4) | value; + break; - if (c == '=') + case '=': REG32(fbi->io_base + 0xc000 + position * 4) = (('Z' - 'A' + 10 + 2) << 4) | value; + break; - if (c == ',') + case ',': REG32(fbi->io_base + 0xc000 + position * 4) = (('Z' - 'A' + 10 + 3) << 4) | value; + break; + } } void OSD_puts(struct faradayfb_info *fbi, char *str, int position, unsigned int value) From a63ba1b09b27536033a57dd339741d9419214fcd Mon Sep 17 00:00:00 2001 From: Cynthia Huang Date: Wed, 3 Nov 2021 14:59:38 +0800 Subject: [PATCH 046/169] scripts: andes: headers_install.sh : Add CONFIG_DSP in the leak ignore list. https://gitea.andestech.com/RD-SW/linux/issues/27 Reviewed-by: Leo Yu-Chi Liang Reviewed-by: Alan Kao --- scripts/headers_install.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/headers_install.sh b/scripts/headers_install.sh index 4041881746adef..19ace17fe17231 100755 --- a/scripts/headers_install.sh +++ b/scripts/headers_install.sh @@ -83,6 +83,8 @@ arch/nios2/include/uapi/asm/swab.h:CONFIG_NIOS2_CI_SWAB_SUPPORT arch/x86/include/uapi/asm/auxvec.h:CONFIG_IA32_EMULATION arch/x86/include/uapi/asm/auxvec.h:CONFIG_X86_64 arch/x86/include/uapi/asm/mman.h:CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS +arch/riscv/include/uapi/asm/ptrace.h:CONFIG_DSP +include/uapi/asm/sigcontext.h:CONFIG_DSP include/uapi/linux/atmdev.h:CONFIG_COMPAT include/uapi/linux/eventpoll.h:CONFIG_PM_SLEEP include/uapi/linux/hw_breakpoint.h:CONFIG_HAVE_MIXED_BREAKPOINTS_REGS From ea591b81a7ac88aaff4c6d2c23a526b6a600fe60 Mon Sep 17 00:00:00 2001 From: Leo Yu-Chi Liang Date: Fri, 26 May 2023 08:45:25 +0800 Subject: [PATCH 047/169] soc: andes: pm: Add Andes power management support Support kernel power management utility on Andes ae350 fpga board. Harts stay in wfi loop in kernel once we trigger light/deep sleep by "echo {standby, mem} > /sys/power/state" command, and are only woken up by the external interrupt sources. Reformed from the following commits in AST520 Linux kernel (branch: RISCV-Linux-5.4-ast-v5_2_0-branch) - (f4aa4295eea8e) riscv: Power management - (61f97ce561d6e) Fix code in suspend to ram. - (e454bc2828438) riscv: Power management - (72be906d0b095) ATCSMU: Support ATCSMU - (7eada24d2ef7f) irq-sifive-plic: Fix plic interrupt disabled issue after executing light/deep sleep. - (c6c47563cf80f) ATCSMU: Remove debug message. - (703f0b251425e) ATCSMU: Support atcsmu with SBI CALL v0.2 - (ef8bcb65af26d) atcsmu: Support 45-series CPU hotplug with deep sleep - (7660fb4930d63) atcsmu: Move atcsmu functions to OpenSBI. - (fbcc3ad40e1e1) atcsmu: Make sure hart0 is the primary cpu. - (d72880245a042) riscv: set suspend mode of main hart through andes SBI call - (3fff937eccbbe) riscv: update suspend mode functions - (ebc6ed2ab0d94) configs: Support SMU in qemu defconfig. Signed-off-by: Leo Yu-Chi Liang --- arch/riscv/Kconfig | 3 ++ drivers/irqchip/irq-sifive-plic.c | 39 ++++++++++++++ drivers/soc/andes/Makefile | 3 +- drivers/soc/andes/pm.c | 88 +++++++++++++++++++++++++++++++ include/soc/andes/smu.h | 13 +++++ 5 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 drivers/soc/andes/pm.c create mode 100644 include/soc/andes/smu.h diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index edb7c9941422a7..1e51a8d266ac55 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -715,6 +715,9 @@ config PORTABLE select OF select MMU +config ARCH_SUSPEND_POSSIBLE + def_bool RISCV_SBI + menu "Power management options" source "kernel/power/Kconfig" diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c index be5e19a86ac3b2..a4ec4e67816fad 100644 --- a/drivers/irqchip/irq-sifive-plic.c +++ b/drivers/irqchip/irq-sifive-plic.c @@ -32,6 +32,7 @@ #define MAX_DEVICES 1024 #define MAX_CONTEXTS 15872 +#define MAX_USE_REGS (MAX_DEVICES / 32) /* * Each interrupt source has a priority register associated with it. @@ -62,6 +63,9 @@ #define PLIC_QUIRK_EDGE_INTERRUPT 0 +unsigned int *wake_event; +void __iomem *plic_regs; + struct plic_priv { struct cpumask lmask; struct irq_domain *irqdomain; @@ -153,6 +157,35 @@ static void plic_irq_eoi(struct irq_data *d) } } +static int plic_set_wake(struct irq_data *d, unsigned int on) +{ + u32 offset = d->hwirq / 32; + int cpu; + + if (on) + __assign_bit(d->hwirq, (unsigned long *)wake_event, true); + else + __assign_bit(d->hwirq, (unsigned long *)wake_event, false); + + for_each_cpu(cpu, cpu_possible_mask) { + struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu); + + if (handler->present && on) { + unsigned int interrupt_enable[MAX_USE_REGS]; + u32 __iomem *reg = handler->enable_base + + (d->hwirq / 32) * sizeof(u32); + + interrupt_enable[offset] = readl(reg); + + __assign_bit(d->hwirq, + (unsigned long *)interrupt_enable, true); + + writel(interrupt_enable[offset], reg); + } + } + return 0; +} + #ifdef CONFIG_SMP static int plic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) @@ -203,6 +236,7 @@ static struct irq_chip plic_chip = { .irq_mask = plic_irq_mask, .irq_unmask = plic_irq_unmask, .irq_eoi = plic_irq_eoi, + .irq_set_wake = plic_set_wake, #ifdef CONFIG_SMP .irq_set_affinity = plic_set_affinity, #endif @@ -350,6 +384,10 @@ static int __init __plic_init(struct device_node *node, struct plic_priv *priv; struct plic_handler *handler; + wake_event = kzalloc((MAX_USE_REGS) * sizeof(u32), GFP_KERNEL); + if (WARN_ON(!wake_event)) + return -ENOMEM; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -357,6 +395,7 @@ static int __init __plic_init(struct device_node *node, priv->plic_quirks = plic_quirks; priv->regs = of_iomap(node, 0); + plic_regs = priv->regs; if (WARN_ON(!priv->regs)) { error = -EIO; goto out_free_priv; diff --git a/drivers/soc/andes/Makefile b/drivers/soc/andes/Makefile index 66e90c83c48c01..f37fa1e1c1f623 100644 --- a/drivers/soc/andes/Makefile +++ b/drivers/soc/andes/Makefile @@ -1,10 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright (C) 2023 Andes Technology Corporation. -obj-y += andes_sbi.o +obj-y += andes_sbi.o pm.o obj-y += inject_init.o obj-$(CONFIG_ATCDMAC300) += atcdmac300.o dmad_intc.o obj-$(CONFIG_ANDES_CACHE) += cache.o obj-$(CONFIG_ANDES_DMA_NONCOHERENT) += dma-noncoherent.o obj-$(CONFIG_ANDES_PPMA) += ppma.o - diff --git a/drivers/soc/andes/pm.c b/drivers/soc/andes/pm.c new file mode 100644 index 00000000000000..6c015380feee53 --- /dev/null +++ b/drivers/soc/andes/pm.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ +#include +#include +#include +#include + +#include + +extern void __iomem *plic_regs; +#define PLIC_PEND_OFF 0x1000 +#define MAX_DEVICES 1024 +#define MAX_USE_REGS (MAX_DEVICES / 32) + +int suspend_begin; + +static void andes_suspend_cpu(void) +{ + int i; + unsigned int wake; + u32 __iomem *reg = plic_regs + PLIC_PEND_OFF; + + while (true) { + __asm__ volatile("wfi"); + for (i = 0; i < MAX_USE_REGS; i++) { + if (wake_event[i]) { + wake = readl(reg + i); + if (wake_event[i] & wake) + goto wakeup; + } + } + } +wakeup: + return; +} + +static int andes_pm_enter(suspend_state_t state) +{ + pr_debug("%s:state:%d\n", __func__, state); + switch (state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + andes_suspend_cpu(); + break; + default: + return -EINVAL; + } + return 0; +} + +static int andes_pm_valid(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_ON: + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + return 1; + default: + return -EINVAL; + } +} + +static int andes_pm_begin(suspend_state_t state) +{ + suspend_begin = state; + return 0; +} + +static void andes_pm_end(void) +{ + suspend_begin = 0; +} + +static const struct platform_suspend_ops andes_pm_ops = { + .valid = andes_pm_valid, + .begin = andes_pm_begin, + .enter = andes_pm_enter, + .end = andes_pm_end, +}; + +static int __init andes_pm_init(void) +{ + suspend_set_ops(&andes_pm_ops); + return 0; +} +late_initcall(andes_pm_init); diff --git a/include/soc/andes/smu.h b/include/soc/andes/smu.h new file mode 100644 index 00000000000000..07d7953e0dcc70 --- /dev/null +++ b/include/soc/andes/smu.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#ifndef _ASM_ANDES_SMU_H +#define _ASM_ANDES_SMU_H + +#include + +extern unsigned int *wake_event; + +#endif From 6f9bc98a71e6b421b65423d1d18d2d16b4b7409e Mon Sep 17 00:00:00 2001 From: Leo Yu-Chi Liang Date: Fri, 26 May 2023 08:57:56 +0800 Subject: [PATCH 048/169] soc: andes: atcsmu: Add ATCSMU light/deep sleep support Support light/deep sleep in ATCSMU using kernel suspend feature. ATCSMU provides light sleep (clock gating) and deep sleep (power gating) feature for power management. Setup wakeup event: - TTY: - echo enabled > /sys/devices/platform/f0300000.serial/tty/ttyS0/power/wakeup - echo enabled > /sys/devices/platform/soc/f0300000.serial/tty/ttyS0/power/wakeup (Built-in DTB) - RTC: - echo enabled > /sys/devices/platform/f0600000.rtc/power/wakeup - echo enabled > /sys/devices/platform/soc/f0600000.rtc/power/wakeup (Built-in DTB) Light sleep (clock gating): - echo standby > /sys/power/state Deep sleep (power gating): - echo mem > /sys/power/state - echo 0 > /sys/devices/system/cpu/cpu[x]/online Reformed from the following commits from AST520 Linux kernel (branch: RISCV-Linux-5.4-ast-v5_2_0-branch) - (f4aa4295eea8e) riscv: Power management - (61f97ce561d6e) Fix code in suspend to ram. - (e454bc2828438) riscv: Power management - (72be906d0b095) ATCSMU: Support ATCSMU - (7eada24d2ef7f) irq-sifive-plic: Fix plic interrupt disabled issue after executing light/deep sleep. - (c6c47563cf80f) ATCSMU: Remove debug message. - (703f0b251425e) ATCSMU: Support atcsmu with SBI CALL v0.2 - (ef8bcb65af26d) atcsmu: Support 45-series CPU hotplug with deep sleep - (7660fb4930d63) atcsmu: Move atcsmu functions to OpenSBI. - (fbcc3ad40e1e1) atcsmu: Make sure hart0 is the primary cpu. - (d72880245a042) riscv: set suspend mode of main hart through andes SBI call - (3fff937eccbbe) riscv: update suspend mode functions - (ebc6ed2ab0d94) configs: Support SMU in qemu defconfig. Signed-off-by: Leo Yu-Chi Liang Execution Flow: =============== - echo standby > /sys/power/state - echo mem > /sys/power/state - (main hart) pm framework: andes_pm_enter -> atcsmu_{suspend2standby,suspend2ram} -> vendor SBI call: sbi_andes_set_suspend_mode -> vendor SBI call: sbi_andes_enter_suspend_mode -> OpenSBI: wfi (light/deep sleep) - (other hart) cpu hotplug: do_idle (after "suspend_disable_secondary_cpus") -> if (cpu_is_offline(cpu)) then "arch_cpu_idle_dead" -> vendor SBI call: sbi_andes_set_suspend_mode -> sret -> sbi_cpu_stop -> HSM SBI call: sbi_hsm_hart_stop -> ... -> ae350_enter_suspend_mode -> WFI --- arch/riscv/configs/amp.config | 1 - arch/riscv/configs/andes-support.config | 2 + arch/riscv/kernel/cpu-hotplug.c | 6 ++ drivers/soc/andes/Kconfig | 8 ++ drivers/soc/andes/Makefile | 1 + drivers/soc/andes/atcsmu.c | 135 ++++++++++++++++++++++++ drivers/soc/andes/pm.c | 15 ++- include/linux/cpu.h | 12 +++ include/soc/andes/smu.h | 38 +++++++ 9 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 drivers/soc/andes/atcsmu.c diff --git a/arch/riscv/configs/amp.config b/arch/riscv/configs/amp.config index 63ae179e09844c..513e8d24074b29 100644 --- a/arch/riscv/configs/amp.config +++ b/arch/riscv/configs/amp.config @@ -1,4 +1,3 @@ -CONFIG_ATCSMU=y # CONFIG_ANDES_PPMA is not set CONFIG_AMP=y diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 8df4241ad3797a..1297b84825f33b 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -34,3 +34,5 @@ CONFIG_SND_FTSSP010_AC97=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_ATCRTC100=y + +CONFIG_ANDES_ATCSMU=y diff --git a/arch/riscv/kernel/cpu-hotplug.c b/arch/riscv/kernel/cpu-hotplug.c index f7a832e3a1d1d4..068e783cb37c9e 100644 --- a/arch/riscv/kernel/cpu-hotplug.c +++ b/arch/riscv/kernel/cpu-hotplug.c @@ -15,6 +15,8 @@ #include #include +#include + bool cpu_has_hotplug(unsigned int cpu) { if (cpu_ops[cpu]->cpu_stop) @@ -77,6 +79,10 @@ void arch_cpu_idle_dead(void) (void)cpu_report_death(); +#ifdef CONFIG_ANDES_ATCSMU + atcsmu_set_suspend_mode(); +#endif + cpu_ops[smp_processor_id()]->cpu_stop(); /* It should never reach here */ BUG(); diff --git a/drivers/soc/andes/Kconfig b/drivers/soc/andes/Kconfig index 65038787976605..69147505c4b8a8 100644 --- a/drivers/soc/andes/Kconfig +++ b/drivers/soc/andes/Kconfig @@ -36,4 +36,12 @@ config PLATFORM_AHBDMA bool "Platform AHB DMA support" depends on ATCDMAC300 +config ANDES_ATCSMU + bool "Andes ATCSMU Support" + depends on RISCV && PLAT_AE350 + help + Andes System Management Unit (SMU) supports + light/sleep and hotplug utilities. ATCSMU + driver utilizes this hardware to implement + power management. endmenu diff --git a/drivers/soc/andes/Makefile b/drivers/soc/andes/Makefile index f37fa1e1c1f623..2e96ec5de77392 100644 --- a/drivers/soc/andes/Makefile +++ b/drivers/soc/andes/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_ATCDMAC300) += atcdmac300.o dmad_intc.o obj-$(CONFIG_ANDES_CACHE) += cache.o obj-$(CONFIG_ANDES_DMA_NONCOHERENT) += dma-noncoherent.o obj-$(CONFIG_ANDES_PPMA) += ppma.o +obj-$(CONFIG_ANDES_ATCSMU) += atcsmu.o diff --git a/drivers/soc/andes/atcsmu.c b/drivers/soc/andes/atcsmu.c new file mode 100644 index 00000000000000..90f024497fdcc4 --- /dev/null +++ b/drivers/soc/andes/atcsmu.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// define in drivers/soc/andes/pm.c +extern int suspend_begin; + +struct atcsmu atcsmu; +EXPORT_SYMBOL(atcsmu); + +void sbi_andes_set_suspend_mode(unsigned int suspend_mode) +{ + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_SUSPEND_MODE, suspend_mode, + 0, 0, 0, 0, 0); +} +EXPORT_SYMBOL(sbi_andes_set_suspend_mode); + +void sbi_andes_enter_suspend_mode(int main_core, unsigned int wake_event) +{ + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_ENTER_SUSPEND_MODE, main_core, + wake_event, 0, 0, 0, 0); +} +EXPORT_SYMBOL(sbi_andes_enter_suspend_mode); + +#ifdef CONFIG_ANDES_ATCSMU +/* main hart */ +void atcsmu_suspend2ram(void) +{ + sbi_andes_set_suspend_mode(DEEP_SLEEP_MODE); + sbi_andes_enter_suspend_mode(true, *wake_event); +} + +/* main hart */ +void atcsmu_suspend2standby(void) +{ + sbi_andes_set_suspend_mode(LIGHT_SLEEP_MODE); + sbi_andes_enter_suspend_mode(true, *wake_event); +} + +/* other harts or hotplugging hart */ +/* arch/riscv/kernel/cpu-hotplug.c: arch_cpu_idle_dead */ +void atcsmu_set_suspend_mode(void) +{ + if (suspend_begin == PM_SUSPEND_MEM) + sbi_andes_set_suspend_mode(DEEP_SLEEP_MODE); + else if (suspend_begin == PM_SUSPEND_STANDBY) + sbi_andes_set_suspend_mode(LIGHT_SLEEP_MODE); + else + sbi_andes_set_suspend_mode(CPUHOTPLUG_DEEP_SLEEP_MODE); +} +#endif + +static int atcsmu_probe(struct platform_device *pdev) +{ + struct atcsmu *smu = &atcsmu; + int ret = -ENOENT; + int pcs = 0; + + spin_lock_init(&smu->lock); + + smu->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!smu->res) + goto err_exit; + + ret = -EBUSY; + + smu->res = request_mem_region(smu->res->start, + smu->res->end - smu->res->start + 1, + pdev->name); + if (!smu->res) + goto err_exit; + + ret = -EINVAL; + + smu->base = + ioremap(smu->res->start, smu->res->end - smu->res->start + 1); + if (!smu->base) + goto err_ioremap; + + for (pcs = 0; pcs < MAX_PCS_SLOT; pcs++) + writel(0xffdfffff, (void *)(smu->base + PCSN_WE_OFF(pcs))); + + return 0; +err_ioremap: + release_resource(smu->res); +err_exit: + return ret; +} + +static int __exit atcsmu_remove(struct platform_device *pdev) +{ + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id atcsmu_of_id_table[] = { + { .compatible = "andestech,atcsmu" }, + {} +}; +MODULE_DEVICE_TABLE(of, atcsmu_of_id_table); +#endif + +static struct platform_driver atcsmu_driver = { + .probe = atcsmu_probe, + .remove = __exit_p(atcsmu_remove), + .driver = { + .name = "atcsmu", + .of_match_table = of_match_ptr(atcsmu_of_id_table), + }, +}; + +static int __init atcsmu_init(void) +{ + int ret = platform_driver_register(&atcsmu_driver); + + return ret; +} +subsys_initcall(atcsmu_init); diff --git a/drivers/soc/andes/pm.c b/drivers/soc/andes/pm.c index 6c015380feee53..4ffae1b4a96e00 100644 --- a/drivers/soc/andes/pm.c +++ b/drivers/soc/andes/pm.c @@ -16,6 +16,7 @@ extern void __iomem *plic_regs; int suspend_begin; +#ifndef CONFIG_ANDES_ATCSMU static void andes_suspend_cpu(void) { int i; @@ -35,19 +36,29 @@ static void andes_suspend_cpu(void) wakeup: return; } +#endif static int andes_pm_enter(suspend_state_t state) { pr_debug("%s:state:%d\n", __func__, state); switch (state) { case PM_SUSPEND_STANDBY: +#ifdef CONFIG_ANDES_ATCSMU + atcsmu_suspend2standby(); +#else + andes_suspend_cpu(); +#endif + return 0; case PM_SUSPEND_MEM: +#ifdef CONFIG_ANDES_ATCSMU + atcsmu_suspend2ram(); +#else andes_suspend_cpu(); - break; +#endif + return 0; default: return -EINVAL; } - return 0; } static int andes_pm_valid(suspend_state_t state) diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 008bfa68cfabc7..ca0af659235dc8 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -163,6 +163,18 @@ static inline int suspend_disable_secondary_cpus(void) if (IS_ENABLED(CONFIG_PM_SLEEP_SMP_NONZERO_CPU)) cpu = -1; + /* + * Kernel cpu_id is not mhartid. + * Make sure "primary cpu" corresponds to mhartid 0. + */ + + if (IS_ENABLED(CONFIG_ANDES_ATCSMU)) { + for_each_possible_cpu(cpu) { + if (cpuid_to_hartid_map(cpu) == 0) + break; + } + } + return freeze_secondary_cpus(cpu); } static inline void suspend_enable_secondary_cpus(void) diff --git a/include/soc/andes/smu.h b/include/soc/andes/smu.h index 07d7953e0dcc70..e63b6f518c6f88 100644 --- a/include/soc/andes/smu.h +++ b/include/soc/andes/smu.h @@ -7,7 +7,45 @@ #define _ASM_ANDES_SMU_H #include +#define MAX_PCS_SLOT 7 + +#define PCS0_WE_OFF 0x90 +#define PCS0_CTL_OFF 0x94 +#define PCS0_STATUS_OFF 0x98 + +/* + * PCS0 --> Always on power domain, includes the JTAG tap and + * DMI_AHB bus in NCEJDTM200 + * PCS1 --> Power domain for debug subsystem + * PCS2 --> Main power domain, includes the system bus and + * AHB, APB peripheral IPs + * PCS3 --> Power domain for Core0 and L2C + * PCSN --> Power domain for Core (N-3) + */ + +#define PCSN_WE_OFF(n) (n * 0x20 + PCS0_WE_OFF) +#define CN_PCS_WE_OFF(n) ((n + 3) * 0x20 + PCS0_WE_OFF) +#define CN_PCS_STATUS_OFF(n) ((n + 3) * 0x20 + PCS0_STATUS_OFF) +#define CN_PCS_CTL_OFF(n) ((n + 3) * 0x20 + PCS0_CTL_OFF) + +struct atcsmu { + void __iomem *base; + struct resource *res; + spinlock_t lock; +}; extern unsigned int *wake_event; +/* main hart (drivers/soc/andes/pm.c) */ +void atcsmu_suspend2standby(void); +void atcsmu_suspend2ram(void); + +/* other hart (arch/riscv/kernel/cpu-hotplug.c) */ +void atcsmu_set_suspend_mode(void); + +#define NORMAL_MODE 0 +#define LIGHT_SLEEP_MODE 1 +#define DEEP_SLEEP_MODE 2 +#define CPUHOTPLUG_DEEP_SLEEP_MODE 3 + #endif From 5105fba3f79462f42759e4ebafe427f2aac5099a Mon Sep 17 00:00:00 2001 From: Randolph Date: Thu, 18 May 2023 16:00:31 +0800 Subject: [PATCH 049/169] watchdog: andes: atcwdt200: Andes support for ATCWDT200 Reformed from the following patches on RISCV-Linux-5.4: - (ff422942995b) riscv: Porting Watch dog driver - Remove vendor SBI call sbi_andes_set_reset_vector(), directly program SMU reset vector, thus it needs CONFIG_ATCSMU as dependency - Add readl_fixup to work around dts probe when hardware is not present Signed-off-by: Yu Chien Peter Lin Signed-off-by: Randolph --- arch/riscv/configs/andes-support.config | 3 + drivers/watchdog/Kconfig | 13 + drivers/watchdog/Makefile | 1 + drivers/watchdog/atcwdt200_wdt.c | 318 ++++++++++++++++++++++++ include/soc/andes/smu.h | 11 + 5 files changed, 346 insertions(+) create mode 100644 drivers/watchdog/atcwdt200_wdt.c diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 1297b84825f33b..9e4eec02f27276 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -36,3 +36,6 @@ CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_ATCRTC100=y CONFIG_ANDES_ATCSMU=y +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_ATCWDT200_WATCHDOG=y diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b64bc49c7f30ed..31c8f616cfd936 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -47,6 +47,12 @@ config WATCHDOG_NOWAYOUT get killed. If you say Y here, the watchdog cannot be stopped once it has been started. +config WATCHDOG_DEBUG + bool "Hang in watch dog interrupt handler" + help + When system failed, it will hang in watch dog interrupt handler for + debugging. + config WATCHDOG_HANDLE_BOOT_ENABLED bool "Update boot-enabled watchdog until userspace takes over" default y @@ -473,6 +479,13 @@ config FTWDT010_WATCHDOG To compile this driver as a module, choose M here: the module will be called ftwdt010_wdt. +config ATCWDT200_WATCHDOG + tristate "ATCWDT200_WATCHDOG" + depends on RISCV && PLAT_AE350 && ANDES_ATCSMU + help + Support for Andes atcwdt200 watchdog. The driver needs SMU to + program reset vector before triggering software reset. + config IXP4XX_WATCHDOG tristate "IXP4xx Watchdog" depends on ARCH_IXP4XX diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index d41e5f830ae7f8..cee6c8e961b47f 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o +obj-$(CONFIG_ATCWDT200_WATCHDOG) += atcwdt200_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o diff --git a/drivers/watchdog/atcwdt200_wdt.c b/drivers/watchdog/atcwdt200_wdt.c new file mode 100644 index 00000000000000..fac85d6af54f33 --- /dev/null +++ b/drivers/watchdog/atcwdt200_wdt.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "atcwdt200" + +#define DEBUG(str, ...) \ +do { \ + if (debug) \ + pr_info(str, ##__VA_ARGS__); \ +} while (0) + +/* ID and Revision Register */ +#define IDREV (*(volatile unsigned int *)(wdt_base + 0x00)) +#define ID_OFF 12 +#define ID_MSK (0xfffff << ID_OFF) +#define ID (0x03002 << ID_OFF) + +/* Control Register */ +#define CTRL (*(volatile unsigned int *)(wdt_base + 0x10)) +#define RST_TIME_OFF 8 +#define RST_TIME_MSK (0x3 << RST_TIME_OFF) +#define RST_CLK_128 (0 << RST_TIME_OFF) +#define RST_CLK_256 (1 << RST_TIME_OFF) +#define RST_CLK_512 (2 << RST_TIME_OFF) +#define RST_CLK_1024 (3 << RST_TIME_OFF) +#define INT_TIME_OFF 4 +#define INT_TIME_MSK (0xf << INT_TIME_OFF) +#define INT_CLK_64 (0 << INT_TIME_OFF) +#define INT_CLK_256 (1 << INT_TIME_OFF) +#define INT_CLK_1024 (2 << INT_TIME_OFF) +#define INT_CLK_2048 (3 << INT_TIME_OFF) +#define INT_CLK_4096 (4 << INT_TIME_OFF) +#define INT_CLK_8192 (5 << INT_TIME_OFF) +#define INT_CLK_16384 (6 << INT_TIME_OFF) +#define INT_CLK_32768 (7 << INT_TIME_OFF) +#define RST_EN (1 << 3) +#define INT_EN (1 << 2) +#define CLK_PCLK (1 << 1) +#define WDT_EN (1 << 0) + +/* Restart Register */ +#define RESTART (*(volatile unsigned int *)(wdt_base + 0x14)) +#define RESTART_MAGIC 0xcafe + +/* Write Enable Register */ +#define WREN (*(volatile unsigned int *)(wdt_base + 0x18)) +#define WP_MAGIC 0x5aa5 + +/* Status Register */ +#define STATUS (*(volatile unsigned int *)(wdt_base + 0x1c)) +#define INT_EXPIRED (1 << 0) + +#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ + +static int expect_close; +static int debug; +static int timeout = TIMER_MARGIN; /* in seconds */ +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +static int wdt_panic; +module_param(wdt_panic, int, 0); +MODULE_PARM_DESC(wdt_panic, + "Watchdog action, set to 1 to panic, 0 to reboot (default=0)"); + +module_param(debug, int, 0); +module_param(timeout, int, 0); +static u32 wdt_freq; +static void __iomem *wdt_base; +extern struct atcsmu atcsmu; + +/* enable the WDT */ +void wdt_start(void) +{ +#ifdef CONFIG_WATCHDOG_DEBUG + WREN = WP_MAGIC; + CTRL = (INT_CLK_32768 | INT_EN | WDT_EN); +#else + WREN = WP_MAGIC; + CTRL = (INT_CLK_32768 | INT_EN | RST_CLK_128 | RST_EN | WDT_EN); +#endif +} + +int wdt_get_timeout(void) +{ + int time = (CTRL & INT_TIME_MSK) >> INT_TIME_OFF; + + if (time > 7) + return (time - 7) * 2; + else + return 1; +} + +/* set reset vector and enable WDT */ +static int wdtdog_open(struct inode *inode, struct file *file) +{ + int cpu_nums = num_online_cpus(); + struct atcsmu *smu = &atcsmu; + + for (int i = 0; i < cpu_nums; i++) { + writel((u32)FLASH_BASE, smu->base + SMU_HART_RESET_VEC_LO(i)); + writel((u64)FLASH_BASE >> 32, + smu->base + SMU_HART_RESET_VEC_HI(i)); + } + + for (int i = 0; i < cpu_nums; i++) { + u32 lo = readl(smu->base + SMU_HART_RESET_VEC_LO(i)); + u32 hi = readl(smu->base + SMU_HART_RESET_VEC_HI(i)); + + pr_info("hart%d reset vector (lo32) set to 0x%08x\n", i, lo); + pr_info("hart%d reset vector (hi32) set to 0x%08x\n", i, hi); + } + + wdt_start(); + DEBUG("Activating WDT..\n"); + return 0; +} + +/* + * disable WDT, NOWAY_OUT MEANS NEED MAGIC NUMBER + * OR just disable it if NOWAY_OUT not set + */ +static int wdtdog_release(struct inode *inode, struct file *file) +{ +#ifndef CONFIG_WATCHDOG_NOWAYOUT + /* + * Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT + */ + if (expect_close) { + WREN = WP_MAGIC; + CTRL = 0; + DEBUG("Deactivating WDT..\n"); + } +#endif + expect_close = 0; + return 0; +} + +/* clean the WDT */ +static ssize_t wdtdog_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + if (!len) + return 0; + + if (!nowayout) { + size_t i; + + for (i = 0; i < len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') { + expect_close = 42; + break; + } + } + } + WREN = WP_MAGIC; + RESTART = RESTART_MAGIC; + STATUS |= INT_EXPIRED; + return len; +} + +static long wdtdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + .identity = "FTWDT010 watchdog", + }; + + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_options, retval = -EINVAL; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, sizeof(ident)); + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_SETOPTIONS: + if (get_user(new_options, p)) + return -EFAULT; + + if (new_options & WDIOS_DISABLECARD) { + WREN = WP_MAGIC; + CTRL &= ~WDT_EN; + retval = 0; + } + + if (new_options & WDIOS_ENABLECARD) { + wdt_start(); + retval = 0; + } + return retval; + + case WDIOC_GETTIMEOUT: + timeout = wdt_get_timeout(); + return put_user(timeout, p); + + case WDIOC_KEEPALIVE: + WREN = WP_MAGIC; + RESTART = RESTART_MAGIC; + STATUS |= INT_EXPIRED; + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +static const struct file_operations wdtdog_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = wdtdog_write, + .unlocked_ioctl = wdtdog_ioctl, + .open = wdtdog_open, + .release = wdtdog_release, +}; + +static struct miscdevice atcwdt200_dog_miscdev = { + WATCHDOG_MINOR, + "watchdog", + &wdtdog_fops +}; + +#ifdef CONFIG_OF +static const struct of_device_id atcwdt200_dog_match[] = { + { .compatible = "andestech,atcwdt200" }, + {}, +}; +MODULE_DEVICE_TABLE(of, atcwdt200_dog_match); +#endif + +static int atcwdt200_dog_probe(struct platform_device *pdev) +{ + int (*read_fixup)(void __iomem *addr, unsigned int val, + unsigned int shift_bits); + int ret; + struct resource *res; + struct device_node *node; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(wdt_base)) + return PTR_ERR(wdt_base); + + /* check ID and Revision register */ + read_fixup = symbol_get(readl_fixup); + ret = read_fixup(wdt_base, 0x03002, 12); + symbol_put(readl_fixup); + if (!ret) { + dev_err(&pdev->dev, + "fail to read ID and Revision reg,\ + bitmap not support atcwdt200.\n"); + return -ENOENT; + } + + if ((IDREV & ID_MSK) != ID) + return -ENOENT; + + node = of_find_matching_node(NULL, atcwdt200_dog_match); + if (of_property_read_u32(node, "clock-frequency", &wdt_freq)) + panic("Can't read clock-frequency"); + + ret = misc_register(&atcwdt200_dog_miscdev); + if (ret) + return ret; + + dev_info(&pdev->dev, "ATCWDT200 watchdog timer driver.\n"); + + DEBUG("ATCWDT200 watchdog timer: timer timeout %d sec\n", timeout); + return 0; +} + +static int atcwdt200_dog_remove(struct platform_device *pdev) +{ + misc_deregister(&atcwdt200_dog_miscdev); + return 0; +} + +static struct platform_driver atcwdt200_dog_driver = { + .probe = atcwdt200_dog_probe, + .remove = atcwdt200_dog_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atcwdt200_dog_match), + }, +}; + +module_platform_driver(atcwdt200_dog_driver); +MODULE_DESCRIPTION("Andestech watchdog driver"); +MODULE_AUTHOR("Andestech"); +MODULE_LICENSE("GPL"); diff --git a/include/soc/andes/smu.h b/include/soc/andes/smu.h index e63b6f518c6f88..a7e4f6e3e23816 100644 --- a/include/soc/andes/smu.h +++ b/include/soc/andes/smu.h @@ -48,4 +48,15 @@ void atcsmu_set_suspend_mode(void); #define DEEP_SLEEP_MODE 2 #define CPUHOTPLUG_DEEP_SLEEP_MODE 3 +/* for watchdog */ +#define FLASH_BASE 0x80000000 +#define SMUCR_OFF 0x14 +#define SMUCR_RESET 0x3c +#define SMU_RESET_VEC_LO_OFF 0x50 +#define SMU_RESET_VEC_HI_OFF 0x60 +#define SMU_HART_RESET_VEC_LO(n) (SMU_RESET_VEC_LO_OFF + (n * 0x4)) +#define SMU_HART_RESET_VEC_HI(n) (SMU_RESET_VEC_HI_OFF + (n * 0x4)) +#define PCS_RESET 0x1 +#define RESET_CMD 0x1 + #endif From fb49f1f567ccc4da61006954fadc70512b74c256 Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Tue, 23 May 2023 16:48:08 +0800 Subject: [PATCH 050/169] gpio: andes: atcgpio100: Support Andes ATCGPIO100 Verify with poll, it will show messages as below: /tmp # ./poll GPIO[1], edge = falling GPIO[2], edge = rising GPIO[3], edge = both GPIO[4], edge = rising GPIO[5], edge = rising GPIO[1]: 1 GPIO[2]: 1 GPIO[3]: 1 GPIO[4]: 1 GPIO[5]: 1 GPIO[1]: 0 GPIO[2]: 1 GPIO[3]: 0 GPIO[3]: 1 GPIO[4]: 1 GPIO[5]: 1 Signed-off-by: Rick Chen --- drivers/gpio/Kconfig | 10 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-atcgpio100.c | 330 +++++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 drivers/gpio/gpio-atcgpio100.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3e8e5f4ffa59f2..74752164ea7d1e 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -293,6 +293,16 @@ config GPIO_FTGPIO010 Support for common GPIOs from the Faraday FTGPIO010 IP core, found in Cortina systems Gemini platforms, Moxa ART and others. +config GPIO_ATCGPIO100 + bool "Andes ATCGPIO100 GPIO support" + depends on RISCV && PLAT_AE350 && OF_GPIO + select GPIOLIB_IRQCHIP + select IRQ_DOMAIN + select GPIO_GENERIC + help + Support for common GPIOs from the Andes ATCGPIO100 IP core, + in the Andes AE350 platform. + config GPIO_GENERIC_PLATFORM tristate "Generic memory-mapped GPIO controller support (MMIO platform device)" select GPIO_GENERIC diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 29e3beb6548cb0..16fa3aeb7361a3 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_EXAR) += gpio-exar.o obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o +obj-$(CONFIG_GPIO_ATCGPIO100) += gpio-atcgpio100.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o diff --git a/drivers/gpio/gpio-atcgpio100.c b/drivers/gpio/gpio-atcgpio100.c new file mode 100644 index 00000000000000..34d56415f1361c --- /dev/null +++ b/drivers/gpio/gpio-atcgpio100.c @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Andes Technology Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPIO_DATA_OUT 0x24 +#define GPIO_DATA_IN 0x20 +#define PIN_DIR 0x28 +#define PIN_PULL_ENABLE 0x40 +#define PIN_PULL_TYPE 0x44 +#define INT_ENABLE 0x50 +#define INT_STATE 0x64 +#define INT_MODE(ch) (0x54 + ((ch / 8) << 2)) +#define HIGH_LEVEL 2 +#define LOW_LEVEL 3 +#define NEGATIVE_EDGE 5 +#define POSITIVE_EDGE 6 +#define DUAL_EDGE 7 +#define DEBOUNCE_ENABLE 0x70 + +#define ATCGPIO100_VIRTUAL_IRQ_BASE 0 + +struct atcgpio_priv { + struct gpio_chip gc; + struct irq_domain *domain; + void __iomem *base; + unsigned long virq_base; + spinlock_t lock; + unsigned int irq_parent; +}; + +#define GPIO_READL(offset, base) \ + readl((void __iomem *)base + (offset)) + +#define GPIO_WRITEL(val, offset, base) \ + writel((val), (void __iomem *)base + (offset)) + +static inline struct atcgpio_priv *atcgpio_irq_data_get_data(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + return gpiochip_get_data(chip); +} + +static int atcirq_to_gpio(unsigned long irq) +{ + return irq - (ATCGPIO100_VIRTUAL_IRQ_BASE ? gpio_to_irq(0) : 0); +} + +static int atcgpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct atcgpio_priv *priv; + unsigned long flags; + u32 val; + + priv = gpiochip_get_data(gc); + spin_lock_irqsave(&priv->lock, flags); + val = GPIO_READL(GPIO_DATA_IN, priv->base); + spin_unlock_irqrestore(&priv->lock, flags); + + return (val >> gpio & 1); +} + +static void atcgpio_set(struct gpio_chip *gc, unsigned int gpio, int data) +{ + unsigned long flags; + struct atcgpio_priv *priv; + unsigned long val; + + priv = gpiochip_get_data(gc); + spin_lock_irqsave(&priv->lock, flags); + if (data) + val = GPIO_READL(GPIO_DATA_OUT, priv->base) | (0x1UL << gpio); + else + val = GPIO_READL(GPIO_DATA_OUT, priv->base) & ~(0x1UL << gpio); + GPIO_WRITEL(val, GPIO_DATA_OUT, priv->base); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int atcgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long val; + unsigned long flags; + struct atcgpio_priv *priv; + + priv = gpiochip_get_data(gc); + spin_lock_irqsave(&priv->lock, flags); + val = GPIO_READL(PIN_DIR, priv->base) & ~(0x1UL << gpio); + GPIO_WRITEL(val, PIN_DIR, priv->base); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int atcgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int data) +{ + unsigned long val; + unsigned long flags; + struct atcgpio_priv *priv; + + priv = gpiochip_get_data(gc); + spin_lock_irqsave(&priv->lock, flags); + val = GPIO_READL(PIN_DIR, priv->base) | (0x1UL << gpio); + GPIO_WRITEL(val, PIN_DIR, priv->base); + gc->set(gc, gpio, data); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void atcgpio_irq_ack(struct irq_data *data) +{ + unsigned long flags; + struct atcgpio_priv *priv; + unsigned long irq; + + priv = atcgpio_irq_data_get_data(data); + irq = priv->virq_base ? data->irq : data->hwirq; + spin_lock_irqsave(&priv->lock, flags); + GPIO_WRITEL(0x1UL << atcirq_to_gpio(irq), INT_STATE, priv->base); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void atcgpio_irq_unmask(struct irq_data *data) +{ + unsigned long val; + unsigned long flags; + struct atcgpio_priv *priv; + unsigned long irq; + + priv = atcgpio_irq_data_get_data(data); + irq = priv->virq_base ? data->irq : data->hwirq; + spin_lock_irqsave(&priv->lock, flags); + val = GPIO_READL(INT_ENABLE, priv->base) | (0x1UL << atcirq_to_gpio(irq)); + GPIO_WRITEL(val, INT_ENABLE, priv->base); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static void atcgpio_irq_mask(struct irq_data *data) +{ + unsigned long val; + unsigned long flags; + struct atcgpio_priv *priv; + unsigned long irq; + + priv = atcgpio_irq_data_get_data(data); + irq = priv->virq_base ? data->irq : data->hwirq; + spin_lock_irqsave(&priv->lock, flags); + val = GPIO_READL(INT_ENABLE, priv->base) & ~(0x1UL << atcirq_to_gpio(irq)); + GPIO_WRITEL(val, INT_ENABLE, priv->base); + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int atcgpio_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct atcgpio_priv *priv = atcgpio_irq_data_get_data(data); + unsigned int irq = priv->virq_base ? data->irq : data->hwirq; + unsigned int gpio = atcirq_to_gpio(irq); + unsigned int ch = (gpio % 8); + unsigned int mode_off = (ch << 2); + unsigned int val; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + val = GPIO_READL(INT_MODE(gpio), priv->base); + val &= ~(7 << mode_off); + if (flow_type & IRQF_TRIGGER_RISING && flow_type & IRQF_TRIGGER_FALLING) + GPIO_WRITEL(val | DUAL_EDGE<base); + else if (flow_type & IRQF_TRIGGER_FALLING) + GPIO_WRITEL(val | NEGATIVE_EDGE<base); + else if (flow_type & IRQF_TRIGGER_RISING) + GPIO_WRITEL(val | POSITIVE_EDGE<base); + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static void gpio_irq_router(struct irq_desc *desc) +{ + unsigned long status; + struct atcgpio_priv *priv; + int i = 0; + + priv = irq_desc_get_handler_data(desc); + status = GPIO_READL(INT_STATE, priv->base); + status &= ~((1 << 22) | (1 << 25) | (1 << 26)); + while (status) { + if (status & 0x1UL) + generic_handle_irq(gpio_to_irq(i)); + status >>= 1; + i++; + } + desc->irq_data.chip->irq_eoi(&desc->irq_data); +} + +static const struct irq_chip atcgpio_irqchip = { + .name = "ATCGPIO100_irq", + .irq_ack = atcgpio_irq_ack, + .irq_mask = atcgpio_irq_mask, + .irq_unmask = atcgpio_irq_unmask, + .irq_set_type = atcgpio_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static int atcgpio100_gpio_probe(struct platform_device *pdev) +{ + int (*read_fixup)(void __iomem *addr, unsigned int val, + unsigned int shift_bits); + struct resource *res; + int ret; + struct atcgpio_priv *priv; + struct gpio_chip *gc; + struct gpio_irq_chip *girq; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to request atcgpio100 resource\n"); + return -ENXIO; + } + platform_set_drvdata(pdev, priv); + ret = platform_irq_count(pdev); + priv->irq_parent = platform_get_irq(pdev, 0); + if (priv->irq_parent < 0) { + dev_err(&pdev->dev, "failed to request atcgpio100 irq\n"); + return -ENXIO; + } + priv->virq_base = ATCGPIO100_VIRTUAL_IRQ_BASE; + priv->base = devm_ioremap_resource(&pdev->dev, res); + spin_lock_init(&priv->lock); + if (IS_ERR((void *)priv->base)) + return PTR_ERR((void *)priv->base); + + /* Check ID register */ + read_fixup = symbol_get(readl_fixup); + ret = read_fixup(priv->base, 0x020310, 8); + symbol_put(readl_fixup); + if (!ret) { + dev_err(&pdev->dev, + "Failed to read ID register, ATCGPIO100 is not supported.\n"); + return -ENXIO; + } + /* disable interrupt */ + GPIO_WRITEL(0x00000000UL, INT_ENABLE, priv->base); + /* clear interrupt */ + GPIO_WRITEL(0x0000FFFFUL, INT_STATE, priv->base); + /* enable de-bouncing */ + GPIO_WRITEL(0x0000FFFFUL, DEBOUNCE_ENABLE, priv->base); + /* enable interrupt */ + GPIO_WRITEL(0x0000FFFFUL, INT_ENABLE, priv->base); + gc = &priv->gc; + gc->owner = THIS_MODULE; + gc->parent = &pdev->dev; + gc->label = "atcgpio100"; + gc->base = 0; + gc->ngpio = 16; + gc->direction_output = atcgpio_dir_out; + gc->direction_input = atcgpio_dir_in; + gc->set = atcgpio_set; + gc->get = atcgpio_get; + girq = &priv->gc.irq; + gpio_irq_chip_set_chip(girq, &atcgpio_irqchip); + girq->parent_handler = gpio_irq_router; + girq->num_parents = 1; + girq->first = priv->virq_base; + girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, + sizeof(*girq->parents), GFP_KERNEL); + girq->parents[0] = priv->irq_parent; + if (!girq->parents) + return -ENOMEM; + + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_level_irq; + ret = devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); + if (ret) { + dev_err(&pdev->dev, "gpiochip_add error %d\n", ret); + return ret; + } + pr_info("ATCGPIO100 module inserted\n"); + + return 0; +} + +static int atcgpio100_gpio_remove(struct platform_device *pdev) +{ + struct atcgpio_priv *priv = platform_get_drvdata(pdev); + + /* disable interrupt */ + GPIO_WRITEL(0x00000000UL, INT_ENABLE, priv->base); + gpiochip_remove(&priv->gc); + pr_info("GPIO module removed\n"); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id atcgpio100_gpio_match[] = { + { + .compatible = "andestech,atcgpio100", + }, + { }, +}; +#endif + +static struct platform_driver atcgpio100_gpio_driver = { + .probe = atcgpio100_gpio_probe, + .driver = { + .name = "atcgpio100_gpio", + .of_match_table = of_match_ptr(atcgpio100_gpio_match), + }, + .remove = atcgpio100_gpio_remove, +}; + +module_platform_driver(atcgpio100_gpio_driver); +MODULE_DESCRIPTION("ATCGPIO100"); +MODULE_LICENSE("GPL"); From 2b6caa7c2c3fffa4164e521dd5f8aa2741f66e13 Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Tue, 23 May 2023 16:51:04 +0800 Subject: [PATCH 051/169] riscv: andes: defconfig: enable ATCGPIO100 for andes-support.config Signed-off-by: Rick Chen --- arch/riscv/configs/andes-support.config | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 9e4eec02f27276..98ff68802cca22 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -4,6 +4,8 @@ CONFIG_PLAT_AE350=y CONFIG_ANDES_CACHE=y CONFIG_ANDES_DMA_NONCOHERENT=y CONFIG_ANDES_PPMA=y +CONFIG_RISCV_ISA_ZICBOM=y +# CONFIG_RISCV_PMU is not set CONFIG_FTMAC100=y CONFIG_MMC_FTSDCG=y @@ -12,9 +14,6 @@ CONFIG_ATCDMAC300G=y CONFIG_ATCDMAC300=y CONFIG_PLATFORM_AHBDMA=y -CONFIG_RISCV_ISA_ZICBOM=y -# CONFIG_RISCV_PMU is not set - CONFIG_PWM=y CONFIG_PWM_ATCPIT100=y @@ -24,6 +23,8 @@ CONFIG_I2C_CHARDEV=y CONFIG_I2C_ATCIIC100=y CONFIG_ATCIIC_IRQ=y +CONFIG_GPIO_ATCGPIO100=y + CONFIG_FB_FTLCDC100=y CONFIG_PANEL_LW500AC9601=y CONFIG_FFB_MODE_RGB=y From 92043e47ce54d255cd6a4641ad907bc4ebe6ca45 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Tue, 23 May 2023 16:20:43 +0800 Subject: [PATCH 052/169] spi: andes: atcspi200: Support Andes ATCSPI200 Include new fix: spi: andes: atcspi200: fix SPI clock can't be set by device tree - ref: https://gitea.andestech.com/RD-SW/linux/pulls/31 Signed-off-by: CL Wang Signed-off-by: Randolph --- arch/riscv/configs/andes-support.config | 5 + drivers/mtd/spi-nor/macronix.c | 2 + drivers/spi/Kconfig | 5 + drivers/spi/Makefile | 1 + drivers/spi/spi-atcspi200.c | 566 ++++++++++++++++++++++++ 5 files changed, 579 insertions(+) create mode 100644 drivers/spi/spi-atcspi200.c diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 98ff68802cca22..398d1a7befc967 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -40,3 +40,8 @@ CONFIG_ANDES_ATCSMU=y CONFIG_WATCHDOG=y CONFIG_WATCHDOG_CORE=y CONFIG_ATCWDT200_WATCHDOG=y + +CONFIG_SPI=y +CONFIG_SPI_ATCSPI200=y +CONFIG_MTD=y +CONFIG_MTD_SPI_NOR=y diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index d81a4cb2812b35..b7aa812a10964e 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -100,6 +100,8 @@ static const struct flash_info macronix_nor_parts[] = { { "mx66u2g45g", INFO(0xc2253c, 0, 64 * 1024, 4096) NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, + { "mx25u1635e", INFO(0xc22535, 0, 64 * 1024, 32) + NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, }; static void macronix_nor_default_init(struct spi_nor *nor) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 15ea11ebcbe094..4af1d1e50c02ef 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -1112,6 +1112,11 @@ config SPI_AMD help Enables SPI controller driver for AMD SoC. +config SPI_ATCSPI200 + tristate "Andes ATCSPI200 SPI controller" + depends on HAS_IOMEM + help + This exposes the SPI controller IP from Andes. # # Add new SPI master controllers in alphabetical order above this line # diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 4b34e855c84125..f026d74596a803 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_SPI_ASPEED_SMC) += spi-aspeed-smc.o obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o obj-$(CONFIG_SPI_AT91_USART) += spi-at91-usart.o +obj-$(CONFIG_SPI_ATCSPI200) += spi-atcspi200.o obj-$(CONFIG_SPI_ATH79) += spi-ath79.o obj-$(CONFIG_SPI_AU1550) += spi-au1550.o obj-$(CONFIG_SPI_AXI_SPI_ENGINE) += spi-axi-spi-engine.o diff --git a/drivers/spi/spi-atcspi200.c b/drivers/spi/spi-atcspi200.c new file mode 100644 index 00000000000000..781f831ec3aea9 --- /dev/null +++ b/drivers/spi/spi-atcspi200.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPI_XFER_BEGIN (1 << 0) +#define SPI_XFER_END (1 << 1) +#define SPI_XFER_ONCE (SPI_XFER_BEGIN | SPI_XFER_END) +#define SPI_XFER_SHIFT 0 + +#define SPI_NAME "atcspi200" +#define SPI_MAX_HZ 50000000 +#define MAX_TRANSFER_LEN 512 +#define CHUNK_SIZE 1 +#define SPI_TIMEOUT 0x100000 +#define NSPI_MAX_CS_NUM 1 +#define DATA_LENGTH(x) ((x - 1) << 8) +#define ADDR_LENGTH(x) ((x - 1) << 16) + +/* SPI Transfer Control Register */ +#define ATCSPI200_TRANSFMT_OFFSET 24 +#define ATCSPI200_TRANSFMT_MASK (0x0F << ATCSPI200_TRANSFMT_OFFSET) +#define ATCSPI200_TRANSMODE_WR_SYNC (0 << ATCSPI200_TRANSFMT_OFFSET) +#define ATCSPI200_TRANSMODE_W_ONLY (1 << ATCSPI200_TRANSFMT_OFFSET) +#define ATCSPI200_TRANSMODE_R_ONLY (2 << ATCSPI200_TRANSFMT_OFFSET) +#define ATCSPI200_TRANSMODE_WR (3 << ATCSPI200_TRANSFMT_OFFSET) +#define ATCSPI200_TRANSMODE_NONEDATA (7 << ATCSPI200_TRANSFMT_OFFSET) +#define ATCSPI200_TRANSMODE_DMYREAD (9 << ATCSPI200_TRANSFMT_OFFSET) +#define ATCSPI200_TRANSCTRL_WRTRANCNT_OFFSET 12 +#define ATCSPI200_TRANSCTRL_WRTRANCNT_MASK (0x1FF << ATCSPI200_TRANSCTRL_WRTRANCNT_OFFSET) +#define ATCSPI200_TRANSCTRL_RDTRANCNT_OFFSET 0 +#define ATCSPI200_TRANSCTRL_RDTRANCNT_MASK (0x1FF << ATCSPI200_TRANSCTRL_RDTRANCNT_OFFSET) +#define CMD_EN (1 << 30) +#define ADDR_EN (1 << 29) +#define DUAL_QUAD(x) (x << 22) +#define DUMMY_CNT(x) ((x - 1) << 9) + +/* SPI Control Register */ +#define ATCSPI200_CTRL_TXFIFORST_MASK (1 << 2) +#define ATCSPI200_CTRL_RXFIFORST_MASK (1 << 1) +#define ATCSPI200_CTRL_SPIRST_MASK (1 << 0) + +/* SPI Transfer Format Register */ +#define ATCSPI200_TRANSFMT_CPHA_MASK (1UL << 0) +#define ATCSPI200_TRANSFMT_CPOL_MASK (1UL << 1) + +/* SPI Status Register */ +#define ATCSPI200_STATUS_TXEMPTY_OFFSET (1 << 22) +#define ATCSPI200_STATUS_RXEMPTY_OFFSET (1 << 14) +#define ATCSPI200_STATUS_TXNUM_LOWER_OFFSET (16) +#define ATCSPI200_STATUS_TXNUM_LOWER_MASK (0x1F << ATCSPI200_STATUS_TXNUM_LOWER_OFFSET) +#define ATCSPI200_STATUS_RXNUM_LOWER_OFFSET (8) +#define ATCSPI200_STATUS_RXNUM_LOWER_MASK (0x1F << ATCSPI200_STATUS_RXNUM_LOWER_OFFSET) +#define ATCSPI200_STATUS_SPIACTIVE_MASK (1 << 0) + +/* SPI Interface timing Setting */ +#define ATCSPI200_TIMING_SCLK_DIV_MASK 0xFF + +/* ATCSPI200 registers */ +#define SPI_IDREV 0x00 // ID and Revision Register +#define SPI_TRANSFMT 0x10 // SPI Transfer Format Register +#define SPI_DIRECTIO 0x14 // SPI Direct IO Control Register +#define SPI_TRANSCTRL 0x20 // SPI Transfer Control Register +#define SPI_CMD 0x24 // SPI Command Register +#define SPI_ADDR 0x28 // SPI Address Register +#define SPI_DATA 0x2C // SPI Data Register +#define SPI_CTRL 0x30 // SPI Control Register +#define SPI_STATUS 0x34 // SPI Status Register +#define SPI_INTR_EN 0x38 // SPI Interrupt Enable Register +#define SPI_INTR_ST 0x3C // SPI Interrupt Status Registe +#define SPI_TIMING 0x40 // SPI Interface timing Register + +struct atcspi200_spi { + void __iomem *regs; + struct clk *clk; + size_t trans_len; + size_t data_len; + size_t cmd_len; + u32 clk_rate; + u8 cmd_buf[16]; + u8 *din; + u8 *dout; + unsigned int addr; + unsigned int max_transfer_length; + unsigned int freq; + unsigned int mode; + unsigned int mtiming; + int timeout; + spinlock_t lock; +}; + +static void atcspi200_spi_write(struct atcspi200_spi *spi, int offset, u32 value) +{ + iowrite32(value, spi->regs + offset); +} + +static u32 atcspi200_spi_read(struct atcspi200_spi *spi, int offset) +{ + return ioread32(spi->regs + offset); +} + +static void atcspi200_polling_spiactive(struct atcspi200_spi *spi) +{ + int active; + + do { + active = atcspi200_spi_read(spi, SPI_STATUS); + } while (active & 1); +} + +static int atcspi200_spi_setup(struct atcspi200_spi *spi) +{ + unsigned int format_val; + u32 timing; + u8 div; + int ctrl_val = (ATCSPI200_CTRL_TXFIFORST_MASK | + ATCSPI200_CTRL_RXFIFORST_MASK | + ATCSPI200_CTRL_SPIRST_MASK); + + atcspi200_spi_write(spi, SPI_CTRL, ctrl_val); + + while (((atcspi200_spi_read(spi, SPI_CTRL)) & + (ATCSPI200_CTRL_TXFIFORST_MASK | + ATCSPI200_CTRL_RXFIFORST_MASK | + ATCSPI200_CTRL_SPIRST_MASK)) && + (spi->timeout--)) { + if (!spi->timeout) + return -EINVAL; + } + + spi->cmd_len = 0; + format_val = (DATA_LENGTH(8) | ADDR_LENGTH(3)); + format_val |= ATCSPI200_TRANSFMT_CPHA_MASK; + format_val |= ATCSPI200_TRANSFMT_CPOL_MASK; + atcspi200_spi_write(spi, SPI_TRANSFMT, format_val); + + timing = atcspi200_spi_read(spi, SPI_TIMING); + timing &= ~ATCSPI200_TIMING_SCLK_DIV_MASK; + + if (spi->freq >= spi->clk_rate) + div = ATCSPI200_TIMING_SCLK_DIV_MASK; + else { + for (div = 0; div < ATCSPI200_TIMING_SCLK_DIV_MASK; div++) { + if (spi->freq >= spi->clk_rate / (2 * (div + 1))) + break; + } + } + timing |= div; + atcspi200_spi_write(spi, SPI_TIMING, timing); + return 0; +} + +static int atcspi200_spi_stop(struct atcspi200_spi *spi) +{ + atcspi200_spi_write(spi, SPI_TIMING, spi->mtiming); + while ((atcspi200_spi_read(spi, SPI_STATUS) & + ATCSPI200_STATUS_SPIACTIVE_MASK) && + (spi->timeout--)) { + if (!spi->timeout) + return -EINVAL; + } + return 0; +} + +static int atcspi200_spi_start(struct atcspi200_spi *spi, struct spi_transfer *t) +{ + int i, trans_len = 0; + int tc = atcspi200_spi_read(spi, SPI_TRANSCTRL); + + atcspi200_polling_spiactive(spi); + + tc &= ~(ATCSPI200_TRANSCTRL_WRTRANCNT_MASK | ATCSPI200_TRANSCTRL_RDTRANCNT_MASK | + ATCSPI200_TRANSFMT_MASK); + + if ((spi->din) && (spi->cmd_len)) + tc |= ATCSPI200_TRANSMODE_WR; + else if (spi->din) + tc |= ATCSPI200_TRANSMODE_R_ONLY; + else + tc |= ATCSPI200_TRANSMODE_W_ONLY; + + if (spi->dout) + trans_len = spi->trans_len; + tc |= (spi->cmd_len+trans_len - 1) << ATCSPI200_TRANSCTRL_WRTRANCNT_OFFSET; + + if (spi->din) + tc |= (spi->trans_len - 1) << ATCSPI200_TRANSCTRL_RDTRANCNT_OFFSET; + + atcspi200_spi_write(spi, SPI_TRANSCTRL, tc); + + atcspi200_spi_write(spi, SPI_CMD, 1); + + for (i = 0; i < spi->cmd_len; i++) + atcspi200_spi_write(spi, SPI_DATA, spi->cmd_buf[i]); + + return 0; +} + +static void atcspi200_spi_tx(struct atcspi200_spi *spi, const u8 *dout) +{ + atcspi200_spi_write(spi, SPI_DATA, *dout); +} + +static int atcspi200_spi_rx(struct atcspi200_spi *spi, u8 *din, unsigned int bytes) +{ + u32 tmp_data = atcspi200_spi_read(spi, SPI_DATA); + *din = tmp_data; + return bytes; +} + +static int transfer_data(struct atcspi200_spi *spi, u8 *rx_buf, u8 *tx_buf, int num_blks) +{ + unsigned int event, rx_bytes; + int num_bytes, rf_cnt; + u8 *dout = tx_buf; + u8 *din = rx_buf; + + num_bytes = (spi->trans_len) % CHUNK_SIZE; + if (num_bytes == 0) + num_bytes = CHUNK_SIZE; + + while (num_blks) { + event = atcspi200_spi_read(spi, SPI_STATUS); + if ((event & ATCSPI200_STATUS_TXEMPTY_OFFSET) && (tx_buf)) { + atcspi200_spi_tx(spi, dout); + num_blks -= CHUNK_SIZE; + dout += CHUNK_SIZE; + } + if ((event & ATCSPI200_STATUS_RXNUM_LOWER_MASK) && (rx_buf)) { + rf_cnt = ((event & ATCSPI200_STATUS_RXNUM_LOWER_MASK) >> + ATCSPI200_STATUS_RXNUM_LOWER_OFFSET); + if (rf_cnt >= CHUNK_SIZE) + rx_bytes = CHUNK_SIZE; + else if (num_blks == 1 && rf_cnt == num_bytes) + rx_bytes = num_bytes; + else + continue; + + if (atcspi200_spi_rx(spi, din, rx_bytes) == rx_bytes) { + num_blks -= CHUNK_SIZE; + din = (unsigned char *)din + rx_bytes; + } + } + } + spi->data_len -= spi->trans_len; + spi->din = din; + spi->dout = dout; + return spi->data_len; + +} +static int atcspi200_spi_transfer(struct spi_device *atcspi200_spi, + struct spi_transfer *t, unsigned long flags) +{ + struct atcspi200_spi *spi = spi_master_get_devdata(atcspi200_spi->master); + u8 *cmd_buf = spi->cmd_buf; + size_t cmd_len = spi->cmd_len; + unsigned long data_len = t->len; + int trans_len, max_trans_len; + int num_chunks, num_blks; + int ret = 0; + + max_trans_len = spi->max_transfer_length; + switch (flags) { + case SPI_XFER_SHIFT: + memcpy(cmd_buf+spi->cmd_len, t->tx_buf, t->len); + spi->cmd_len += t->len; + return 0; + case SPI_XFER_BEGIN: + cmd_len = spi->cmd_len = data_len; + memcpy(cmd_buf, t->tx_buf, cmd_len); + return 0; + case SPI_XFER_END: + if (data_len == 0) + return 0; + + spi->data_len = data_len; + spi->dout = (u8 *)t->tx_buf; + spi->din = (u8 *)t->rx_buf; + break; + case SPI_XFER_BEGIN | SPI_XFER_END: + spi->data_len = 0; + spi->din = 0; + spi->dout = 0; + cmd_len = spi->cmd_len = data_len; + memcpy(cmd_buf, t->tx_buf, cmd_len); + t->tx_buf = 0; + data_len = 0; + spi->trans_len = 1; + atcspi200_spi_start(spi, t); + break; + } + num_chunks = DIV_ROUND_UP(data_len, max_trans_len); + while (num_chunks--) { + trans_len = min_t(size_t, data_len, max_trans_len); + spi->trans_len = trans_len; + num_blks = DIV_ROUND_UP(trans_len, CHUNK_SIZE); + atcspi200_spi_start(spi, t); + + data_len = transfer_data(spi, spi->din, spi->dout, num_blks); + if (data_len) { + spi->cmd_buf[1] += ((trans_len >> 16) & 0xff); + spi->cmd_buf[2] += ((trans_len >> 8) & 0xff); + spi->cmd_buf[3] += ((trans_len) & 0xff); + } + + ret = atcspi200_spi_stop(spi); + } + ret = atcspi200_spi_stop(spi); + + return ret; +} +static int atcspi200_spi_transfer_one_message(struct spi_master *master, struct spi_message *m) +{ + struct atcspi200_spi *spi = spi_master_get_devdata(master); + struct spi_transfer *t; + unsigned long spi_flags; + unsigned long flags; + int ret = 0; + + m->actual_length = 0; + + spi_flags = SPI_XFER_BEGIN; + list_for_each_entry(t, &m->transfers, transfer_list) { + if (list_is_first(&t->transfer_list, &m->transfers) && !(t->tx_buf)) { + dev_dbg(&master->dev, "missing tx buf\n"); + return -EINVAL; + } + if (!t->tx_buf && !t->rx_buf) + spi_flags |= SPI_XFER_ONCE; + + if (list_is_last(&t->transfer_list, &m->transfers)) + spi_flags |= SPI_XFER_END; + + spin_lock_irqsave(&spi->lock, flags); + ret = atcspi200_spi_transfer(m->spi, t, spi_flags); + spin_unlock_irqrestore(&spi->lock, flags); + if (ret) + break; + m->actual_length += t->len; + spi_flags = 0; + } + m->status = ret; + spi_finalize_current_message(master); + + return 0; +} + +static void atcspi200_set_qmode(struct atcspi200_spi *spi, const struct spi_mem_op *op) +{ + int tc = 0; + + tc |= (CMD_EN | ADDR_EN | DUAL_QUAD(2) | ATCSPI200_TRANSMODE_DMYREAD | DUMMY_CNT(4)); + + /* Set transfer length. */ + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_IN) + tc |= ((spi->trans_len - 1) << ATCSPI200_TRANSCTRL_RDTRANCNT_OFFSET); + else + tc |= ((spi->trans_len - 1) << ATCSPI200_TRANSCTRL_WRTRANCNT_OFFSET); + + } + + /* Set SPI_TRANSCTRL and address register. */ + atcspi200_spi_write(spi, SPI_TRANSCTRL, tc); + if (op->addr.nbytes) + atcspi200_spi_write(spi, SPI_ADDR, spi->addr); + + /* Write cmd to start SPI. */ + atcspi200_spi_write(spi, SPI_CMD, op->cmd.opcode); +} + +static int atcspi200_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) +{ + int ret, max_trans_len, data_len, trans_len, num_blks, num_chunks; + struct spi_device *atcspi200_spi = mem->spi; + struct atcspi200_spi *spi = spi_master_get_devdata(atcspi200_spi->master); + + if (op->cmd.opcode != SPINOR_OP_READ_1_1_4) + return -ENOTSUPP; + + /* Check spi status. */ + atcspi200_polling_spiactive(spi); + + max_trans_len = spi->max_transfer_length = MAX_TRANSFER_LEN; + data_len = spi->data_len = op->data.nbytes; + num_chunks = DIV_ROUND_UP(data_len, max_trans_len); + spi->addr = op->addr.val; + + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_IN) { + spi->din = op->data.buf.in; + spi->dout = 0; + } else { + spi->dout = (char *)op->data.buf.out; + spi->din = 0; + } + } + + while (num_chunks--) { + /* set transfer length and data information. */ + trans_len = min_t(size_t, data_len, max_trans_len); + spi->trans_len = trans_len; + num_blks = DIV_ROUND_UP(trans_len, CHUNK_SIZE); + atcspi200_set_qmode(spi, op); + /* Transfer data */ + data_len = transfer_data(spi, spi->din, spi->dout, num_blks); + + if (data_len) + spi->addr += max_trans_len; + + ret = atcspi200_spi_stop(spi); + } + ret = atcspi200_spi_stop(spi); + + return ret; +} + +static const struct spi_controller_mem_ops atcspi200_mem_ops = { + .exec_op = atcspi200_exec_mem_op, +}; + +static int atcspi200_spi_probe(struct platform_device *pdev) +{ + int (*read_fixup)(void __iomem *addr, unsigned int val, + unsigned int shift_bits); + struct resource *res; + struct spi_master *master; + struct atcspi200_spi *spi; + int ret; + u32 num_cs = NSPI_MAX_CS_NUM; + + master = spi_alloc_master(&pdev->dev, sizeof(struct atcspi200_spi)); + if (!master) { + dev_err(&pdev->dev, "spi_allooc_master error\n"); + return -ENOMEM; + } + + spi = spi_master_get_devdata(master); + spin_lock_init(&spi->lock); + platform_set_drvdata(pdev, master); + + /* get base addr */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spi->regs = devm_ioremap_resource(&pdev->dev, res); + + if (IS_ERR(spi->regs)) { + dev_err(&pdev->dev, "Unable to map IO resources\n"); + ret = PTR_ERR(spi->regs); + goto put_master; + } + + /* check ID and Revision register */ + read_fixup = symbol_get(readl_fixup); + ret = read_fixup(spi->regs, 0x020020, 8); + symbol_put(readl_fixup); + if (!ret) { + dev_err(&pdev->dev, + "Failed to read the ID register, ATCSPI200 is not supported.\n"); + goto put_master; + } + + spi->timeout = SPI_TIMEOUT; + spi->max_transfer_length = MAX_TRANSFER_LEN; + + /* get clock */ + spi->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(spi->clk)) { + dev_err(&pdev->dev, "Unable to find bus clock\n"); + ret = PTR_ERR(spi->clk); + goto put_master; + } + + /* Optional parameters */ + ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency", &master->max_speed_hz); + if (ret) { + master->max_speed_hz = SPI_MAX_HZ; /* 50MHz */ + spi->freq = SPI_MAX_HZ; + } + + /* Spin up the bus clock before hitting registers */ + ret = clk_prepare_enable(spi->clk); + if (ret) { + dev_err(&pdev->dev, "Unable to enable bus clock\n"); + goto put_master; + } + spi->clk_rate = clk_get_rate(spi->clk); + if (!spi->clk_rate) { + dev_err(&pdev->dev, "clk rate = 0\n"); + ret = -EINVAL; + goto put_master; + } + /* probe the number of CS lines */ + ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); + if (ret) { + dev_err(&pdev->dev, "could not find num-cs\n"); + ret = -ENXIO; + goto put_master; + } + if (num_cs > NSPI_MAX_CS_NUM) { + dev_warn(&pdev->dev, "unsupported number of cs (%i), reducing to 1\n", num_cs); + num_cs = NSPI_MAX_CS_NUM; + } + + /* Define our master */ + master->bus_num = pdev->id; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_QUAD; + master->dev.of_node = pdev->dev.of_node; + master->num_chipselect = num_cs; + master->transfer_one_message = atcspi200_spi_transfer_one_message; + master->mem_ops = &atcspi200_mem_ops; + + /* Configure the SPI master hardware */ + atcspi200_spi_setup(spi); + spi->mtiming = atcspi200_spi_read(spi, SPI_TIMING); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret < 0) { + dev_err(&pdev->dev, "spi_register_master failed\n"); + goto put_master; + } + dev_info(&pdev->dev, "Andes SPI atcspi200 driver.\n"); + + return 0; + +put_master: + spi_master_put(master); + + return ret; +} + +static const struct of_device_id atcspi200_spi_of_match[] = { + { .compatible = "andestech,atcspi200", }, + {} +}; + +MODULE_DEVICE_TABLE(of, atcspi200_spi_of_match); + +static struct platform_driver atcspi200_spi_driver = { + .probe = atcspi200_spi_probe, + .driver = { + .name = SPI_NAME, + .of_match_table = atcspi200_spi_of_match, + }, +}; +module_platform_driver(atcspi200_spi_driver); + +MODULE_DESCRIPTION("Andes SPI atcspi200 driver"); +MODULE_LICENSE("GPL"); From 81711ef1f297ce019a8aa895eb23fb21dd6292f0 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Thu, 25 May 2023 19:20:24 +0800 Subject: [PATCH 053/169] riscv: andes: defconfig: add sv39.config and sv48.config to support switch satp mode sv32: ae350_rv32_[up/smp]_defconfig sv39: ae350_rv64_[up/smp]_defconfig sv48: ae350_rv64_[up/smp]_sv48_defconfig Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Makefile | 14 +++++++++++--- arch/riscv/configs/sv39.config | 2 ++ arch/riscv/configs/sv48.config | 2 ++ 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 arch/riscv/configs/sv39.config create mode 100644 arch/riscv/configs/sv48.config diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 0e2704e10334a2..8d647d89741e75 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -201,12 +201,20 @@ ae350_rv32_smp_defconfig: PHONY += ae350_rv64_up_defconfig ae350_rv64_up_defconfig: - $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config sv39.config PHONY += ae350_rv64_smp_defconfig ae350_rv64_smp_defconfig: - $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config smp.config + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config smp.config sv39.config + +PHONY += ae350_rv64_up_sv48_defconfig +ae350_rv64_up_sv48_defconfig: + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config sv48.config + +PHONY += ae350_rv64_smp_sv48_defconfig +ae350_rv64_smp_sv48_defconfig: + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config smp.config sv48.config PHONY += ae350_rv64_ax25_amp_defconfig ae350_rv64_ax25_amp_defconfig: - $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config amp.config + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config amp.config sv39.config diff --git a/arch/riscv/configs/sv39.config b/arch/riscv/configs/sv39.config new file mode 100644 index 00000000000000..899c7e52cde3b5 --- /dev/null +++ b/arch/riscv/configs/sv39.config @@ -0,0 +1,2 @@ +CONFIG_CMDLINE="no4lvl" +CONFIG_CMDLINE_EXTEND=y diff --git a/arch/riscv/configs/sv48.config b/arch/riscv/configs/sv48.config new file mode 100644 index 00000000000000..2d0e26a5956bc4 --- /dev/null +++ b/arch/riscv/configs/sv48.config @@ -0,0 +1,2 @@ +CONFIG_CMDLINE="no5lvl" +CONFIG_CMDLINE_EXTEND=y From 2a3f313c0f945f9b3fd2f2e903dbd723b207aae1 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Fri, 9 Jun 2023 08:17:10 +0800 Subject: [PATCH 054/169] v3PR: add earlycon=sbi to extend cmdline --- arch/riscv/configs/sv39.config | 2 +- arch/riscv/configs/sv48.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/configs/sv39.config b/arch/riscv/configs/sv39.config index 899c7e52cde3b5..bf107b88654273 100644 --- a/arch/riscv/configs/sv39.config +++ b/arch/riscv/configs/sv39.config @@ -1,2 +1,2 @@ -CONFIG_CMDLINE="no4lvl" +CONFIG_CMDLINE="no4lvl earlycon=sbi" CONFIG_CMDLINE_EXTEND=y diff --git a/arch/riscv/configs/sv48.config b/arch/riscv/configs/sv48.config index 2d0e26a5956bc4..94136f06251d49 100644 --- a/arch/riscv/configs/sv48.config +++ b/arch/riscv/configs/sv48.config @@ -1,2 +1,2 @@ -CONFIG_CMDLINE="no5lvl" +CONFIG_CMDLINE="no5lvl earlycon=sbi" CONFIG_CMDLINE_EXTEND=y From 907992bec140d0e688f60428e6385f50f67c3f60 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 6 Jun 2023 15:27:18 +0800 Subject: [PATCH 055/169] riscv: andes: Kconfig: Disable CONFIG_STRICT_KERNEL_RWX https://gitea.andestech.com/RD-SW/linux/issues/30 https://gitea.andestech.com/RD-SW/linux_cicd/issues/10#issuecomment-75180 Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 1e51a8d266ac55..5ef60fd07a3f3d 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -29,12 +29,8 @@ config RISCV select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SET_DIRECT_MAP if MMU select ARCH_HAS_SET_MEMORY if MMU - select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL - select ARCH_HAS_STRICT_MODULE_RWX if MMU && !XIP_KERNEL select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAS_UBSAN_SANITIZE_ALL - select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX - select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT select ARCH_STACKWALK select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_DEBUG_PAGEALLOC if MMU From adde7c9c5cc148ce72051bea71fa5df8ba5ebd38 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Fri, 9 Jun 2023 08:16:09 +0800 Subject: [PATCH 056/169] v3PR: CONFIG_STRICT_KERNEL_RWX=n --- arch/riscv/Kconfig | 4 ++++ arch/riscv/configs/andes_defconfig | 1 + 2 files changed, 5 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 5ef60fd07a3f3d..1e51a8d266ac55 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -29,8 +29,12 @@ config RISCV select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SET_DIRECT_MAP if MMU select ARCH_HAS_SET_MEMORY if MMU + select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL + select ARCH_HAS_STRICT_MODULE_RWX if MMU && !XIP_KERNEL select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAS_UBSAN_SANITIZE_ALL + select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX + select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT select ARCH_STACKWALK select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_DEBUG_PAGEALLOC if MMU diff --git a/arch/riscv/configs/andes_defconfig b/arch/riscv/configs/andes_defconfig index b5556e42daf365..1adcc226b197ee 100644 --- a/arch/riscv/configs/andes_defconfig +++ b/arch/riscv/configs/andes_defconfig @@ -33,6 +33,7 @@ CONFIG_PM=y CONFIG_CPU_IDLE=y CONFIG_CPU_FREQ=y CONFIG_JUMP_LABEL=y +# CONFIG_STRICT_KERNEL_RWX is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y CONFIG_NET=y From 27530dbcb19204ef2e6058c9da91bb4bec3a260b Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Fri, 26 May 2023 16:39:12 +0800 Subject: [PATCH 057/169] riscv: andes: Add Andes-specific NORVC relocation type When the ".option norvc" directive is used, the assembler generates two custom relocation types defined by Andes: R_RISCV_NO_RVC_REGION_BEGIN and R_RISCV_NO_RVC_REGION_END. This relocation type marks the no rvc region, indicating to the linker that this region should not be optimized into RVC instructions. Signed-off-by: Hui Min Mina Chou --- arch/riscv/include/uapi/asm/elf.h | 4 +++- arch/riscv/kernel/module.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/uapi/asm/elf.h b/arch/riscv/include/uapi/asm/elf.h index 73f14235e52cdb..0ea1771c3c7b95 100644 --- a/arch/riscv/include/uapi/asm/elf.h +++ b/arch/riscv/include/uapi/asm/elf.h @@ -94,7 +94,9 @@ typedef union __riscv_fp_state elf_fpregset_t; #define R_RISCV_SET32 56 #define R_RISCV_32_PCREL 57 -/* Andes V5 */ +/* Relocation types numbered 200 and above are defined by Andes */ +#define R_RISCV_NO_RVC_REGION_BEGIN 237 +#define R_RISCV_NO_RVC_REGION_END 238 #define R_RISCV_ALIGN_BTB 240 #define R_RISCV_10_PCREL 241 #define R_RISCV_DATA 242 diff --git a/arch/riscv/kernel/module.c b/arch/riscv/kernel/module.c index 12e678ca287016..e053d2a339a141 100644 --- a/arch/riscv/kernel/module.c +++ b/arch/riscv/kernel/module.c @@ -346,7 +346,7 @@ static int (*reloc_handlers_rela_nds(unsigned int type)) (struct module *me, { if (type == R_RISCV_10_PCREL) return apply_r_riscv_10_pcrel_rela; - else if (type >= R_RISCV_ALIGN_BTB && type <= R_RISCV_RELAX_REGION_END) + else if (type >= R_RISCV_NO_RVC_REGION_BEGIN && type <= R_RISCV_RELAX_REGION_END) return apply_r_riscv_ignore_rela; else return NULL; From db22aa6f8c94e2c73dd8816a3e7621d5dba4096b Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Fri, 2 Jun 2023 17:29:47 +0800 Subject: [PATCH 058/169] fbdev: andes: ftlcdc100: Change the default LCD panel type from CONFIG_PANEL_LW500AC9601 to CONFIG_PANEL_AUA036QN01. 1. Change the default LCD panel type from CONFIG_PANEL_LW500AC9601 to CONFIG_PANEL_AUA036QN01. Signed-off-by: CL Wang --- arch/riscv/configs/andes-support.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 398d1a7befc967..1b676cc36ea197 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -26,7 +26,7 @@ CONFIG_ATCIIC_IRQ=y CONFIG_GPIO_ATCGPIO100=y CONFIG_FB_FTLCDC100=y -CONFIG_PANEL_LW500AC9601=y +CONFIG_PANEL_AUA036QN01=y CONFIG_FFB_MODE_RGB=y CONFIG_FFB_MODE_24BPP=y From ff772070f47fd68e5bcac801fa5414581ecb135a Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Tue, 27 Dec 2022 15:46:49 +0800 Subject: [PATCH 059/169] net: andes: ftmac100: allow setting mac address by device tree This patch allows setting mac address by specifying "mac-address" property: mac0: mac@e0100000 { compatible = "andestech,atmac100"; reg = <0x00000000 0xe0100000 0x00000000 0x00001000>; interrupts = <19 4>; interrupt-parent = <&plic0>; dma-coherent; mac-address = [de ed be ef 12 34]; }; [ 4.861903] ftmac100 e0100000.mac eth0: irq 3, mapped at (____ptrval____) [ 4.883422] ftmac100 e0100000.mac eth0: using MAC address given by device tree de:ad:be:ef:12:34 If such property is not provided, it will generate a random mac address: [ 4.863576] ftmac100 e0100000.mac eth0: irq 3, mapped at (____ptrval____) [ 4.885599] ftmac100 e0100000.mac eth0: generated random MAC address 56:e7:b3:97:e5:bb Signed-off-by: Yu Chien Peter Lin --- drivers/net/ethernet/faraday/ftmac100.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/faraday/ftmac100.c b/drivers/net/ethernet/faraday/ftmac100.c index 22a2492052939b..c7126267c9798c 100644 --- a/drivers/net/ethernet/faraday/ftmac100.c +++ b/drivers/net/ethernet/faraday/ftmac100.c @@ -32,6 +32,7 @@ #include #include #include +#include #include "ftmac100.h" #include @@ -1109,9 +1110,15 @@ static int ftmac100_probe(struct platform_device *pdev) struct resource *res; int irq; struct net_device *netdev; + struct device *dev; + struct device_node *mac_node; + u8 mac_addr[ETH_ALEN]; struct ftmac100 *priv; int err, ret; + dev = &pdev->dev; + mac_node = dev->of_node; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENXIO; @@ -1194,9 +1201,19 @@ static int ftmac100_probe(struct platform_device *pdev) netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base); if (!is_valid_ether_addr(netdev->dev_addr)) { - eth_hw_addr_random(netdev); - netdev_info(netdev, "generated random MAC address %pM\n", - netdev->dev_addr); + ret = of_get_mac_address(mac_node, mac_addr); + if (ret) { + /* generate random MAC address if it is not provided by + * device tree + */ + eth_hw_addr_random(netdev); + netdev_info(netdev, "generated random MAC address %pM\n", + netdev->dev_addr); + } else { + netdev_info(netdev, "using MAC address given by device tree %pM\n", + mac_addr); + dev_addr_set(netdev, mac_addr); + } } return 0; From 0e3a67faf3a0dba1dd77f8426e4ebace401dae72 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Wed, 31 May 2023 10:35:21 +0800 Subject: [PATCH 060/169] soc: andes: dma-noncoherent: select ARCH_HAS_DMA_CLEAR_UNCACHED to support driver free dma Signed-off-by: Charles Ci-Jyun Wu --- drivers/soc/andes/Kconfig | 1 + kernel/dma/direct.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/andes/Kconfig b/drivers/soc/andes/Kconfig index 69147505c4b8a8..50eeb678d7d4a0 100644 --- a/drivers/soc/andes/Kconfig +++ b/drivers/soc/andes/Kconfig @@ -16,6 +16,7 @@ config ANDES_DMA_NONCOHERENT select ARCH_HAS_SETUP_DMA_OPS select ARCH_HAS_DMA_PREP_COHERENT select ARCH_HAS_DMA_SET_UNCACHED + select ARCH_HAS_DMA_CLEAR_UNCACHED depends on RISCV && PLAT_AE350 help Andes DMA noncoherent support diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c index 63859a101ed831..8e6f7ea9dcf65f 100644 --- a/kernel/dma/direct.c +++ b/kernel/dma/direct.c @@ -352,7 +352,7 @@ void dma_direct_free(struct device *dev, size_t size, dma_free_from_pool(dev, cpu_addr, PAGE_ALIGN(size))) return; - if (is_vmalloc_addr(cpu_addr)) { + if (IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) && is_vmalloc_addr(cpu_addr)) { vunmap(cpu_addr); } else { if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_CLEAR_UNCACHED)) From d40850abd3c979daab80bcc194ed80d5760cc93d Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Mon, 17 Oct 2022 17:10:15 +0800 Subject: [PATCH 061/169] riscv: dts: andes: ae350: Add initial Andes ae350 device tree Signed-off-by: Yu Chien Peter Lin Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/boot/dts/Makefile | 1 + arch/riscv/boot/dts/andes/Makefile | 19 + .../andes/a25_c1_d_dsp_noncoherent_ae350.dts | 238 ++++++++++ .../boot/dts/andes/a25mp_c4_d_dsp_ae350.dts | 318 +++++++++++++ .../a27l2_c1_d_dsp_noncoherent_ae350.dts | 251 ++++++++++ .../andes/a45_c1_d_dsp_noncoherent_ae350.dts | 243 ++++++++++ .../boot/dts/andes/a45mp_c4_d_dsp_ae350.dts | 317 +++++++++++++ .../andes/ax25_c1_d_dsp_noncoherent_ae350.dts | 238 ++++++++++ .../boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts | 318 +++++++++++++ .../ax27l2_c1_d_dsp_noncoherent_ae350.dts | 251 ++++++++++ .../andes/ax45_c1_d_dsp_noncoherent_ae350.dts | 243 ++++++++++ .../boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts | 317 +++++++++++++ arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts | 429 ++++++++++++++++++ .../boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts | 261 +++++++++++ 14 files changed, 3444 insertions(+) create mode 100644 arch/riscv/boot/dts/andes/Makefile create mode 100644 arch/riscv/boot/dts/andes/a25_c1_d_dsp_noncoherent_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/a45_c1_d_dsp_noncoherent_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/ax25_c1_d_dsp_noncoherent_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/ax45_c1_d_dsp_noncoherent_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts create mode 100644 arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts diff --git a/arch/riscv/boot/dts/Makefile b/arch/riscv/boot/dts/Makefile index ff174996cdfd0a..6db0d821ba6172 100644 --- a/arch/riscv/boot/dts/Makefile +++ b/arch/riscv/boot/dts/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +subdir-y += andes subdir-y += sifive subdir-y += starfive subdir-$(CONFIG_SOC_CANAAN_K210_DTB_BUILTIN) += canaan diff --git a/arch/riscv/boot/dts/andes/Makefile b/arch/riscv/boot/dts/andes/Makefile new file mode 100644 index 00000000000000..f4e5195458e0a3 --- /dev/null +++ b/arch/riscv/boot/dts/andes/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# FPGA : BigOrca +dtb-$(CONFIG_PLAT_AE350) += a25_c1_d_dsp_noncoherent_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += a27l2_c1_d_dsp_noncoherent_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += a45_c1_d_dsp_noncoherent_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += ax25_c1_d_dsp_noncoherent_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += ax27l2_c1_d_dsp_noncoherent_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += ax45_c1_d_dsp_noncoherent_ae350.dtb + +# FPGA : VCU118 +dtb-$(CONFIG_PLAT_AE350) += a25mp_c4_d_dsp_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += a45mp_c4_d_dsp_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += ax25mp_c4_d_dsp_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += ax45mp_c4_d_dsp_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += ax45mp_c8_ae350.dtb + +# FPGA : VU19P +dtb-$(CONFIG_PLAT_AE350) += ax65mp_c2_d_dsp_ae350.dtb +obj-$(CONFIG_BUILTIN_DTB) += $(addsuffix .o, $(dtb-y)) diff --git a/arch/riscv/boot/dts/andes/a25_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/a25_c1_d_dsp_noncoherent_ae350.dts new file mode 100644 index 00000000000000..e6b60db8fae2d2 --- /dev/null +++ b/arch/riscv/boot/dts/andes/a25_c1_d_dsp_noncoherent_ae350.dts @@ -0,0 +1,238 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,a25"; + model = "andestech,a25"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x200>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x01>; + }; + }; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x40000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x01 0x0b 0x01 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x02>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x01 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x01>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x01 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x03>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0xe4e1c0>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x12c0000>; + reg-shift = <0x02>; + reg-offset = <0x20>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x02>; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x02>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + cap-sd-highspeed; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04 0x40 0x04 0x41 0x04 0x42 0x04 0x43 0x04 0x44 0x04 0x45 0x04 0x46 0x04 0x47 0x04>; + interrupt-parent = <0x02>; + dma-channels = <0x08>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x02>; + }; + + smc@e0400000 { + compatible = "andestech,atfsmc020"; + reg = <0x00 0xe0400000 0x00 0x1000>; + }; + + nor@88000000 { + compatible = "cfi-flash"; + reg = <0x00 0x88000000 0x00 0x1000>; + bank-width = <0x02>; + device-width = <0x02>; + }; + + snd@f0d00000 { + compatible = "andestech,atfac97"; + reg = <0x00 0xf0d00000 0x00 0x1000>; + interrupts = <0x11 0x04>; + interrupt-parent = <0x02>; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x02>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x03>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts new file mode 100644 index 00000000000000..49a56d0eb00f7a --- /dev/null +++ b/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts @@ -0,0 +1,318 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,a25"; + model = "andestech,a25"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x02>; + }; + }; + + cpu@1 { + device_type = "cpu"; + reg = <0x01>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x03>; + }; + }; + + cpu@2 { + device_type = "cpu"; + reg = <0x02>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x04>; + }; + }; + + cpu@3 { + device_type = "cpu"; + reg = <0x03>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x05>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x02>; + cache-size = <0x40000>; + reg = <0x00 0xe0500000 0x00 0x10000>; + andes,inst-prefetch = <0x03>; + andes,data-prefetch = <0x03>; + andes,tag-ram-ctl = <0x00 0x00>; + andes,data-ram-ctl = <0x00 0x00>; + phandle = <0x01>; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x40000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x02 0x0b 0x02 0x09 0x03 0x0b 0x03 0x09 0x04 0x0b 0x04 0x09 0x05 0x0b 0x05 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x06>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x02 0x03 0x03 0x03 0x04 0x03 0x05 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x04>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x02 0x07 0x03 0x07 0x04 0x07 0x05 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x08>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0xe4e1c0>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x12c0000>; + current-speed = <0x9600>; + reg-shift = <0x02>; + reg-offset = <0x20>; + reg-io-width = <0x04>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010g"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x06>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + cap-sd-highspeed; + dmas = <0x07 0x09>; + dma-names = "rxtx"; + dma-coherent; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300g"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x06>; + dma-channels = <0x08>; + #dma-cells = <0x01>; + dma-coherent; + phandle = <0x07>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x06>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x08>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts new file mode 100644 index 00000000000000..fbe1021235f188 --- /dev/null +++ b/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts @@ -0,0 +1,251 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,a25"; + model = "andestech,a25"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x80>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x02>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x02>; + cache-size = <0x80000>; + reg = <0x00 0xe0500000 0x00 0x1000>; + andes,inst-prefetch = <0x03>; + andes,data-prefetch = <0x03>; + andes,tag-ram-ctl = <0x00 0x00>; + andes,data-ram-ctl = <0x00 0x00>; + phandle = <0x01>; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x40000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x02 0x0b 0x02 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x03>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x02 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x01>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x02 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x04>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x03>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x03>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x03>; + clock-frequency = <0xe4e1c0>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x03>; + clock-frequency = <0x12c0000>; + reg-shift = <0x02>; + reg-offset = <0x20>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x03>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x03>; + wakeup-source; + }; + + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x03>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x03>; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x03>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + cap-sd-highspeed; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04 0x40 0x04 0x41 0x04 0x42 0x04 0x43 0x04 0x44 0x04 0x45 0x04 0x46 0x04 0x47 0x04>; + interrupt-parent = <0x03>; + dma-channels = <0x08>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x03>; + }; + + smc@e0400000 { + compatible = "andestech,atfsmc020"; + reg = <0x00 0xe0400000 0x00 0x1000>; + }; + + nor@88000000 { + compatible = "cfi-flash"; + reg = <0x00 0x88000000 0x00 0x1000>; + bank-width = <0x02>; + device-width = <0x02>; + }; + + snd@f0d00000 { + compatible = "andestech,atfac97"; + reg = <0x00 0xf0d00000 0x00 0x1000>; + interrupts = <0x11 0x04>; + interrupt-parent = <0x03>; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x03>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x04>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/a45_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/a45_c1_d_dsp_noncoherent_ae350.dts new file mode 100644 index 00000000000000..c8e79bd849e97a --- /dev/null +++ b/arch/riscv/boot/dts/andes/a45_c1_d_dsp_noncoherent_ae350.dts @@ -0,0 +1,243 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ae350"; + model = "andestech,a45"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x01>; + }; + }; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x40000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x01 0x0b 0x01 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x02>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x01 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x01>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x01 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x04>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x12c0000>; + reg-shift = <0x02>; + reg-offset = <0x20>; + no-loopback-test = <0x01>; + current-speed = <0x9600>; + reg-io-width = <0x04>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x02>; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010g"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x02>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + dma-names = "rxtx"; + dmas = <0x03 0x09>; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300g"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x02>; + dma-channels = <0x08>; + #dma-cells = <0x01>; + phandle = <0x03>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x02>; + }; + + smc@e0400000 { + compatible = "andestech,atfsmc020"; + reg = <0x00 0xe0400000 0x00 0x1000>; + }; + + nor@88000000 { + compatible = "cfi-flash"; + reg = <0x00 0x88000000 0x00 0x1000>; + bank-width = <0x02>; + device-width = <0x02>; + }; + + snd@f0d00000 { + compatible = "andestech,atfac97"; + reg = <0x00 0xf0d00000 0x00 0x1000>; + interrupts = <0x11 0x04>; + interrupt-parent = <0x02>; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x02>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x04>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts new file mode 100644 index 00000000000000..f4db227ca36f48 --- /dev/null +++ b/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts @@ -0,0 +1,317 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ae350"; + model = "andestech,a45"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x02>; + }; + }; + + cpu@1 { + device_type = "cpu"; + reg = <0x01>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x03>; + }; + }; + + cpu@2 { + device_type = "cpu"; + reg = <0x02>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x04>; + }; + }; + + cpu@3 { + device_type = "cpu"; + reg = <0x03>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv32i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv32"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x05>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x02>; + cache-size = <0x80000>; + reg = <0x00 0xe0500000 0x00 0x10000>; + andes,inst-prefetch = <0x03>; + andes,data-prefetch = <0x03>; + andes,tag-ram-ctl = <0x00 0x00>; + andes,data-ram-ctl = <0x00 0x00>; + phandle = <0x01>; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x80000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x02 0x0b 0x02 0x09 0x03 0x0b 0x03 0x09 0x04 0x0b 0x04 0x09 0x05 0x0b 0x05 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x06>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x02 0x03 0x03 0x03 0x04 0x03 0x05 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x04>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x02 0x07 0x03 0x07 0x04 0x07 0x05 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x08>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x12c0000>; + current-speed = <0x9600>; + reg-shift = <0x02>; + reg-offset = <0x20>; + reg-io-width = <0x04>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010g"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x06>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + dmas = <0x07 0x09>; + dma-names = "rxtx"; + dma-coherent; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300g"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x06>; + dma-channels = <0x08>; + #dma-cells = <0x01>; + dma-coherent; + phandle = <0x07>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x06>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x08>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/ax25_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/ax25_c1_d_dsp_noncoherent_ae350.dts new file mode 100644 index 00000000000000..2b9a934f27afbd --- /dev/null +++ b/arch/riscv/boot/dts/andes/ax25_c1_d_dsp_noncoherent_ae350.dts @@ -0,0 +1,238 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ax25"; + model = "andestech,ax25"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x200>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x01>; + }; + }; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x40000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x01 0x0b 0x01 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x02>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x01 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x01>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x01 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x03>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0xe4e1c0>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x12c0000>; + reg-shift = <0x02>; + reg-offset = <0x20>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x02>; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x02>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + cap-sd-highspeed; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04 0x40 0x04 0x41 0x04 0x42 0x04 0x43 0x04 0x44 0x04 0x45 0x04 0x46 0x04 0x47 0x04>; + interrupt-parent = <0x02>; + dma-channels = <0x08>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x02>; + }; + + smc@e0400000 { + compatible = "andestech,atfsmc020"; + reg = <0x00 0xe0400000 0x00 0x1000>; + }; + + nor@88000000 { + compatible = "cfi-flash"; + reg = <0x00 0x88000000 0x00 0x1000>; + bank-width = <0x02>; + device-width = <0x02>; + }; + + snd@f0d00000 { + compatible = "andestech,atfac97"; + reg = <0x00 0xf0d00000 0x00 0x1000>; + interrupts = <0x11 0x04>; + interrupt-parent = <0x02>; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x02>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x03>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts new file mode 100644 index 00000000000000..c2a1e319b5eadb --- /dev/null +++ b/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts @@ -0,0 +1,318 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ax25"; + model = "andestech,ax25"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x02>; + }; + }; + + cpu@1 { + device_type = "cpu"; + reg = <0x01>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x03>; + }; + }; + + cpu@2 { + device_type = "cpu"; + reg = <0x02>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x04>; + }; + }; + + cpu@3 { + device_type = "cpu"; + reg = <0x03>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x20>; + i-cache-block-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x20>; + d-cache-block-size = <0x20>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x05>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x02>; + cache-size = <0x40000>; + reg = <0x00 0xe0500000 0x00 0x10000>; + andes,inst-prefetch = <0x03>; + andes,data-prefetch = <0x03>; + andes,tag-ram-ctl = <0x00 0x00>; + andes,data-ram-ctl = <0x00 0x00>; + phandle = <0x01>; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x40000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x02 0x0b 0x02 0x09 0x03 0x0b 0x03 0x09 0x04 0x0b 0x04 0x09 0x05 0x0b 0x05 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x06>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x02 0x03 0x03 0x03 0x04 0x03 0x05 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x04>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x02 0x07 0x03 0x07 0x04 0x07 0x05 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x08>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0xe4e1c0>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x12c0000>; + current-speed = <0x9600>; + reg-shift = <0x02>; + reg-offset = <0x20>; + reg-io-width = <0x04>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010g"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x06>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + cap-sd-highspeed; + dmas = <0x07 0x09>; + dma-names = "rxtx"; + dma-coherent; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300g"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x06>; + dma-channels = <0x08>; + #dma-cells = <0x01>; + dma-coherent; + phandle = <0x07>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x06>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x08>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts new file mode 100644 index 00000000000000..69fca2a7236a52 --- /dev/null +++ b/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts @@ -0,0 +1,251 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ax25"; + model = "andestech,ax25"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x80>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x02>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x02>; + cache-size = <0x80000>; + reg = <0x00 0xe0500000 0x00 0x1000>; + andes,inst-prefetch = <0x03>; + andes,data-prefetch = <0x03>; + andes,tag-ram-ctl = <0x00 0x00>; + andes,data-ram-ctl = <0x00 0x00>; + phandle = <0x01>; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x40000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x02 0x0b 0x02 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x03>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x02 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x01>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x02 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x04>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x03>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x03>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x03>; + clock-frequency = <0xe4e1c0>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x03>; + clock-frequency = <0x12c0000>; + reg-shift = <0x02>; + reg-offset = <0x20>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x03>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x03>; + wakeup-source; + }; + + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x03>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x03>; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x03>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + cap-sd-highspeed; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04 0x40 0x04 0x41 0x04 0x42 0x04 0x43 0x04 0x44 0x04 0x45 0x04 0x46 0x04 0x47 0x04>; + interrupt-parent = <0x03>; + dma-channels = <0x08>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x03>; + }; + + smc@e0400000 { + compatible = "andestech,atfsmc020"; + reg = <0x00 0xe0400000 0x00 0x1000>; + }; + + nor@88000000 { + compatible = "cfi-flash"; + reg = <0x00 0x88000000 0x00 0x1000>; + bank-width = <0x02>; + device-width = <0x02>; + }; + + snd@f0d00000 { + compatible = "andestech,atfac97"; + reg = <0x00 0xf0d00000 0x00 0x1000>; + interrupts = <0x11 0x04>; + interrupt-parent = <0x03>; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x03>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x04>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/ax45_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/ax45_c1_d_dsp_noncoherent_ae350.dts new file mode 100644 index 00000000000000..989aa629f16dc5 --- /dev/null +++ b/arch/riscv/boot/dts/andes/ax45_c1_d_dsp_noncoherent_ae350.dts @@ -0,0 +1,243 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ae350"; + model = "andestech,ax45"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x01>; + }; + }; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x40000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x01 0x0b 0x01 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x02>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x01 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x01>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x01 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x04>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x3938700>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x02>; + clock-frequency = <0x12c0000>; + reg-shift = <0x02>; + reg-offset = <0x20>; + no-loopback-test = <0x01>; + current-speed = <0x9600>; + reg-io-width = <0x04>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x02>; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010g"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x02>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + dma-names = "rxtx"; + dmas = <0x03 0x09>; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300g"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x02>; + dma-channels = <0x08>; + #dma-cells = <0x01>; + phandle = <0x03>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x02>; + }; + + smc@e0400000 { + compatible = "andestech,atfsmc020"; + reg = <0x00 0xe0400000 0x00 0x1000>; + }; + + nor@88000000 { + compatible = "cfi-flash"; + reg = <0x00 0x88000000 0x00 0x1000>; + bank-width = <0x02>; + device-width = <0x02>; + }; + + snd@f0d00000 { + compatible = "andestech,atfac97"; + reg = <0x00 0xf0d00000 0x00 0x1000>; + interrupts = <0x11 0x04>; + interrupt-parent = <0x02>; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x02>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x04>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts new file mode 100644 index 00000000000000..23e97b8d22ae50 --- /dev/null +++ b/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts @@ -0,0 +1,317 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ae350"; + model = "andestech,ax45"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x02>; + }; + }; + + cpu@1 { + device_type = "cpu"; + reg = <0x01>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x03>; + }; + }; + + cpu@2 { + device_type = "cpu"; + reg = <0x02>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x04>; + }; + }; + + cpu@3 { + device_type = "cpu"; + reg = <0x03>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x05>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x02>; + cache-size = <0x80000>; + reg = <0x00 0xe0500000 0x00 0x10000>; + andes,inst-prefetch = <0x03>; + andes,data-prefetch = <0x03>; + andes,tag-ram-ctl = <0x00 0x00>; + andes,data-ram-ctl = <0x00 0x00>; + phandle = <0x01>; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x80000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x02 0x0b 0x02 0x09 0x03 0x0b 0x03 0x09 0x04 0x0b 0x04 0x09 0x05 0x0b 0x05 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x06>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x02 0x03 0x03 0x03 0x04 0x03 0x05 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x04>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x02 0x07 0x03 0x07 0x04 0x07 0x05 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x08>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x12c0000>; + current-speed = <0x9600>; + reg-shift = <0x02>; + reg-offset = <0x20>; + reg-io-width = <0x04>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010g"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x06>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + dmas = <0x07 0x09>; + dma-names = "rxtx"; + dma-coherent; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300g"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x06>; + dma-channels = <0x08>; + #dma-cells = <0x01>; + dma-coherent; + phandle = <0x07>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x06>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x08>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts b/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts new file mode 100644 index 00000000000000..3e4273b3786ddc --- /dev/null +++ b/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts @@ -0,0 +1,429 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ae350"; + model = "andestech,ax45"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0c2p0xv5-1p1"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv39"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x02>; + }; + }; + + cpu@1 { + device_type = "cpu"; + reg = <0x01>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0c2p0xv5-1p1"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv39"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x03>; + }; + }; + + cpu@2 { + device_type = "cpu"; + reg = <0x02>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0c2p0xv5-1p1"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv39"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x04>; + }; + }; + + cpu@3 { + device_type = "cpu"; + reg = <0x03>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0c2p0xv5-1p1"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv39"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x05>; + }; + }; + + cpu@4 { + device_type = "cpu"; + reg = <0x04>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0c2p0xv5-1p1"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv39"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x06>; + }; + }; + + cpu@5 { + device_type = "cpu"; + reg = <0x05>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0c2p0xv5-1p1"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv39"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x07>; + }; + }; + + cpu@6 { + device_type = "cpu"; + reg = <0x06>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0c2p0xv5-1p1"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv39"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x08>; + }; + }; + + cpu@7 { + device_type = "cpu"; + reg = <0x07>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0c2p0xv5-1p1"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv39"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x09>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x02>; + cache-size = <0x40000>; + reg = <0x00 0xe0500000 0x00 0x10000>; + andes,inst-prefetch = <0x03>; + andes,data-prefetch = <0x03>; + andes,tag-ram-ctl = <0x00 0x00>; + andes,data-ram-ctl = <0x00 0x00>; + phandle = <0x01>; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x80000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x02 0x0b 0x02 0x09 0x03 0x0b 0x03 0x09 0x04 0x0b 0x04 0x09 0x05 0x0b 0x05 0x09 0x06 0x0b 0x06 0x09 0x07 0x0b 0x07 0x09 0x08 0x0b 0x08 0x09 0x09 0x0b 0x09 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x0a>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x02 0x03 0x03 0x03 0x04 0x03 0x05 0x03 0x06 0x03 0x07 0x03 0x08 0x03 0x09 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x08>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x02 0x07 0x03 0x07 0x04 0x07 0x05 0x07 0x06 0x07 0x07 0x07 0x08 0x07 0x09 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x0c>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x0a>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x0a>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x0a>; + clock-frequency = <0x3938700>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x0a>; + clock-frequency = <0x12c0000>; + current-speed = <0x9600>; + reg-shift = <0x02>; + reg-offset = <0x20>; + reg-io-width = <0x04>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x0a>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x0a>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x0a>; + dma-coherent; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010g"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x0a>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + dmas = <0x0b 0x09>; + dma-names = "rxtx"; + dma-coherent; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300g"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x0a>; + dma-channels = <0x08>; + #dma-cells = <0x01>; + dma-coherent; + phandle = <0x0b>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x0a>; + dma-coherent; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x0a>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x0c>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; diff --git a/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts new file mode 100644 index 00000000000000..a5097ac7010879 --- /dev/null +++ b/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts @@ -0,0 +1,261 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ae350"; + model = "andestech,ax65"; + serial-number = "11339b63d"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdc_zba_zbb_zbc_zbkb_zbkc_zbkx_zbs_zicbom_zkn_zknd_zkne_zknh_zks_zksed_zksh_zkt_smepmp_sscofpmf_svinval_svnapot_svpbmt_xandes"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + riscv,cbom-block-size = <0x40>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x10000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x10000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x02>; + }; + }; + + cpu@1 { + device_type = "cpu"; + reg = <0x01>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64imafdc_zba_zbb_zbc_zbkb_zbkc_zbkx_zbs_zicbom_zkn_zknd_zkne_zknh_zks_zksed_zksh_zkt_smepmp_sscofpmf_svinval_svnapot_svpbmt_xandes"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + riscv,cbom-block-size = <0x40>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x10000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x10000>; + d-cache-sets = <0x100>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x03>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x02>; + cache-size = <0x200000>; + reg = <0x00 0xe0500000 0x00 0x10000>; + andes,inst-prefetch = <0x03>; + andes,data-prefetch = <0x03>; + andes,tag-ram-ctl = <0x00 0x00>; + andes,data-ram-ctl = <0x00 0x00>; + phandle = <0x01>; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x80000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x02 0x0b 0x02 0x09 0x03 0x0b 0x03 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x04>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x02 0x03 0x03 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x02>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x02 0x07 0x03 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x06>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x04>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x04>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x04>; + clock-frequency = <0x3938700>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x04>; + clock-frequency = <0x12c0000>; + current-speed = <0x9600>; + reg-shift = <0x02>; + reg-offset = <0x20>; + reg-io-width = <0x04>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x04>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x04>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x04>; + dma-coherent; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010g"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x04>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + dmas = <0x05 0x09>; + dma-names = "rxtx"; + dma-coherent; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300g"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x04>; + dma-channels = <0x08>; + #dma-cells = <0x01>; + dma-coherent; + phandle = <0x05>; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x04>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x06>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + + es100@f0900000 { + compatible = "andestech,es100"; + reg = <0x00 0xf0900000 0x00 0x1000>; + }; + }; +}; From 7137e6fd6e1535f548b12aa7731e18944067dc4b Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Fri, 9 Jun 2023 15:26:28 +0800 Subject: [PATCH 062/169] fbdev: andes: ftlcdc100: To support AUA036QN01, change the FFB mode from 24BPP to 16BPP. 1. To support AUA036QN01, change the FFB mode from 24BPP to 16BPP. Signed-off-by: CL Wang --- arch/riscv/configs/andes-support.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 1b676cc36ea197..30e0b70cd6dc5a 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -28,7 +28,7 @@ CONFIG_GPIO_ATCGPIO100=y CONFIG_FB_FTLCDC100=y CONFIG_PANEL_AUA036QN01=y CONFIG_FFB_MODE_RGB=y -CONFIG_FFB_MODE_24BPP=y +CONFIG_FFB_MODE_16BPP=y CONFIG_SND_FTSSP010=y CONFIG_SND_FTSSP010_AC97=y From 59457dadeb24938cf86b872d69d04a630d512954 Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Fri, 9 Jun 2023 15:30:15 +0800 Subject: [PATCH 063/169] i2c: andes: atciic100: Set the nr to -1 to dynamically assign bus IDs for multiple I2C masters. 1. Set the nr to -1 to dynamically assign bus IDs for multiple I2C masters. Signed-off-by: CL Wang --- drivers/i2c/busses/i2c-atciic100.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/i2c/busses/i2c-atciic100.c b/drivers/i2c/busses/i2c-atciic100.c index 0370d103a188cb..b7780cf5457e98 100644 --- a/drivers/i2c/busses/i2c-atciic100.c +++ b/drivers/i2c/busses/i2c-atciic100.c @@ -469,6 +469,8 @@ static int atciic_probe(struct platform_device *pdev) padap->algo_data = iface; padap->timeout = 3 * HZ; padap->retries = 1; + padap->nr = -1; + rc = i2c_add_numbered_adapter(padap); if (rc < 0) { dev_err(&pdev->dev, "Can't add i2c adapter!\n"); From 6bc26682335b1e1dd845d502bdd068d57a3b20cf Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Wed, 12 Oct 2022 01:18:41 +0200 Subject: [PATCH 064/169] drivers/perf: riscv_pmu_sbi: add support for PMU variant on T-Head C9xx cores With the T-HEAD C9XX cores being designed before or during the ratification to the SSCOFPMF extension, it implements functionality very similar but not equal to it. It implements overflow handling and also some privilege-mode filtering. While SSCOFPMF supports this for all modes, the C9XX only implements the filtering for M-mode and S-mode but not user-mode. So add some adaptions to allow the C9XX to still handle its PMU through the regular SBI PMU interface instead of defining new interfaces or drivers. To work properly, this requires a matching change in SBI, though the actual interface between kernel and SBI does not change. The main differences are a the overflow CSR and irq number. As the reading of the overflow-csr is in the hot-path during irq handling, use an errata and alternatives to not introduce new conditionals there. Reviewed-by: Andrew Jones Reviewed-by: Conor Dooley Signed-off-by: Heiko Stuebner Link: https://lore.kernel.org/all/20221011231841.2951264-2-heiko@sntech.de/ Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig.erratas | 13 +++++++++++ arch/riscv/errata/thead/errata.c | 19 ++++++++++++++++ arch/riscv/include/asm/errata_list.h | 16 ++++++++++++- drivers/perf/riscv_pmu_sbi.c | 34 ++++++++++++++++++++-------- 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/arch/riscv/Kconfig.erratas b/arch/riscv/Kconfig.erratas index e6b7d22ca6a060..fb2d33de3b3ac3 100644 --- a/arch/riscv/Kconfig.erratas +++ b/arch/riscv/Kconfig.erratas @@ -101,4 +101,17 @@ config ERRATA_THEAD_CMO If you don't know what to do here, say "Y". +config ERRATA_THEAD_PMU + bool "Apply T-Head PMU errata" + depends on ERRATA_THEAD && RISCV_PMU_SBI + default y + help + The T-Head C9xx cores implement a PMU overflow extension very + similar to the core SSCOFPMF extension. + + This will apply the overflow errata to handle the non-standard + behaviour via the regular SBI PMU driver and interface. + + If you don't know what to do here, say "Y". + endmenu # "CPU errata selection" diff --git a/arch/riscv/errata/thead/errata.c b/arch/riscv/errata/thead/errata.c index 32a34ed735098f..9d71fe3d35c77c 100644 --- a/arch/riscv/errata/thead/errata.c +++ b/arch/riscv/errata/thead/errata.c @@ -48,6 +48,22 @@ static bool errata_probe_cmo(unsigned int stage, return true; } +static bool errata_probe_pmu(unsigned int stage, + unsigned long arch_id, unsigned long impid) +{ + if (!IS_ENABLED(CONFIG_ERRATA_THEAD_PMU)) + return false; + + /* target-c9xx cores report arch_id and impid as 0 */ + if (arch_id != 0 || impid != 0) + return false; + + if (stage == RISCV_ALTERNATIVES_EARLY_BOOT) + return false; + + return true; +} + static u32 thead_errata_probe(unsigned int stage, unsigned long archid, unsigned long impid) { @@ -59,6 +75,9 @@ static u32 thead_errata_probe(unsigned int stage, if (errata_probe_cmo(stage, archid, impid)) cpu_req_errata |= BIT(ERRATA_THEAD_CMO); + if (errata_probe_pmu(stage, archid, impid)) + cpu_req_errata |= BIT(ERRATA_THEAD_PMU); + return cpu_req_errata; } diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index a692e0db23cca0..4fe856d1de070e 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -6,6 +6,7 @@ #define ASM_ERRATA_LIST_H #include +#include #include #ifdef CONFIG_ERRATA_ANDES @@ -23,7 +24,8 @@ #ifdef CONFIG_ERRATA_THEAD #define ERRATA_THEAD_PBMT 0 #define ERRATA_THEAD_CMO 1 -#define ERRATA_THEAD_NUMBER 2 +#define ERRATA_THEAD_PMU 2 +#define ERRATA_THEAD_NUMBER 3 #endif #define CPUFEATURE_SVPBMT 0 @@ -153,6 +155,18 @@ asm volatile(ALTERNATIVE_2( \ "r"((unsigned long)(_start) + (_size)) \ : "a0") +#define THEAD_C9XX_RV_IRQ_PMU 17 +#define THEAD_C9XX_CSR_SCOUNTEROF 0x5c5 + +#define ALT_SBI_PMU_OVERFLOW(__ovl) \ +asm volatile(ALTERNATIVE( \ + "csrr %0, " __stringify(CSR_SSCOUNTOVF), \ + "csrr %0, " __stringify(THEAD_C9XX_CSR_SCOUNTEROF), \ + THEAD_VENDOR_ID, ERRATA_THEAD_PMU, \ + CONFIG_ERRATA_THEAD_PMU) \ + : "=r" (__ovl) : \ + : "memory") + #endif /* __ASSEMBLY__ */ #endif diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c index 382fe5ee6100b5..6f9ca2c42ffe1f 100644 --- a/drivers/perf/riscv_pmu_sbi.c +++ b/drivers/perf/riscv_pmu_sbi.c @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -47,6 +48,8 @@ static const struct attribute_group *riscv_pmu_attr_groups[] = { * per_cpu in case of harts with different pmu counters */ static union sbi_pmu_ctr_info *pmu_ctr_list; +static bool riscv_pmu_use_irq; +static unsigned int riscv_pmu_irq_num; static unsigned int riscv_pmu_irq; struct sbi_pmu_event_data { @@ -580,7 +583,7 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev) fidx = find_first_bit(cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS); event = cpu_hw_evt->events[fidx]; if (!event) { - csr_clear(CSR_SIP, SIP_LCOFIP); + csr_clear(CSR_SIP, BIT(riscv_pmu_irq_num)); return IRQ_NONE; } @@ -588,13 +591,13 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev) pmu_sbi_stop_hw_ctrs(pmu); /* Overflow status register should only be read after counter are stopped */ - overflow = csr_read(CSR_SSCOUNTOVF); + ALT_SBI_PMU_OVERFLOW(overflow); /* * Overflow interrupt pending bit should only be cleared after stopping * all the counters to avoid any race condition. */ - csr_clear(CSR_SIP, SIP_LCOFIP); + csr_clear(CSR_SIP, BIT(riscv_pmu_irq_num)); /* No overflow bit is set */ if (!overflow) @@ -661,10 +664,10 @@ static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node) /* Stop all the counters so that they can be enabled from perf */ pmu_sbi_stop_all(pmu); - if (riscv_isa_extension_available(NULL, SSCOFPMF)) { + if (riscv_pmu_use_irq) { cpu_hw_evt->irq = riscv_pmu_irq; - csr_clear(CSR_IP, BIT(RV_IRQ_PMU)); - csr_set(CSR_IE, BIT(RV_IRQ_PMU)); + csr_clear(CSR_IP, BIT(riscv_pmu_irq_num)); + csr_set(CSR_IE, BIT(riscv_pmu_irq_num)); enable_percpu_irq(riscv_pmu_irq, IRQ_TYPE_NONE); } @@ -673,9 +676,9 @@ static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node) static int pmu_sbi_dying_cpu(unsigned int cpu, struct hlist_node *node) { - if (riscv_isa_extension_available(NULL, SSCOFPMF)) { + if (riscv_pmu_use_irq) { disable_percpu_irq(riscv_pmu_irq); - csr_clear(CSR_IE, BIT(RV_IRQ_PMU)); + csr_clear(CSR_IE, BIT(riscv_pmu_irq_num)); } /* Disable all counters access for user mode now */ @@ -691,7 +694,18 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde struct device_node *cpu, *child; struct irq_domain *domain = NULL; - if (!riscv_isa_extension_available(NULL, SSCOFPMF)) + if (riscv_isa_extension_available(NULL, SSCOFPMF)) { + riscv_pmu_irq_num = RV_IRQ_PMU; + riscv_pmu_use_irq = true; + } else if (IS_ENABLED(CONFIG_ERRATA_THEAD_PMU) && + riscv_cached_mvendorid(0) == THEAD_VENDOR_ID && + riscv_cached_marchid(0) == 0 && + riscv_cached_mimpid(0) == 0) { + riscv_pmu_irq_num = THEAD_C9XX_RV_IRQ_PMU; + riscv_pmu_use_irq = true; + } + + if (!riscv_pmu_use_irq) return -EOPNOTSUPP; for_each_of_cpu_node(cpu) { @@ -713,7 +727,7 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde return -ENODEV; } - riscv_pmu_irq = irq_create_mapping(domain, RV_IRQ_PMU); + riscv_pmu_irq = irq_create_mapping(domain, riscv_pmu_irq_num); if (!riscv_pmu_irq) { pr_err("Failed to map PMU interrupt for node\n"); return -ENODEV; From 4d02c541572c745525c9be081c3b1fa07f51f17e Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Wed, 12 Oct 2022 01:18:40 +0200 Subject: [PATCH 065/169] RISC-V: Cache SBI vendor values sbi_get_mvendorid(), sbi_get_marchid() and sbi_get_mimpid() might get called multiple times, though the values of these CSRs should not change during the runtime of a specific machine. Though the values can be different depending on which hart of the system they get called. So hook into the newly introduced cpuinfo struct to allow retrieving these cached values via new functions. Also use arch_initcall for the cpuinfo setup instead, as that now clearly is "architecture specific initialization" and also makes these information available slightly earlier. [caching vendor ids] Suggested-by: Atish Patra [using cpuinfo struct as cache] Suggested-by: Anup Patel Link: https://lore.kernel.org/all/20221011231841.2951264-2-heiko@sntech.de/ Signed-off-by: Heiko Stuebner Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/sbi.h | 5 +++++ arch/riscv/kernel/cpu.c | 30 +++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/arch/riscv/include/asm/sbi.h b/arch/riscv/include/asm/sbi.h index 9baddaee562387..8cb7c579aa21d9 100644 --- a/arch/riscv/include/asm/sbi.h +++ b/arch/riscv/include/asm/sbi.h @@ -327,4 +327,9 @@ int sbi_err_map_linux_errno(int err); static inline int sbi_remote_fence_i(const struct cpumask *cpu_mask) { return -1; } static inline void sbi_init(void) {} #endif /* CONFIG_RISCV_SBI */ + +unsigned long riscv_cached_mvendorid(unsigned int cpu_id); +unsigned long riscv_cached_marchid(unsigned int cpu_id); +unsigned long riscv_cached_mimpid(unsigned int cpu_id); + #endif /* _ASM_RISCV_SBI_H */ diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index 0f76181dc634db..ea60013702d94e 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -71,8 +71,6 @@ int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid) return -1; } -#ifdef CONFIG_PROC_FS - struct riscv_cpuinfo { unsigned long mvendorid; unsigned long marchid; @@ -80,6 +78,30 @@ struct riscv_cpuinfo { }; static DEFINE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo); +unsigned long riscv_cached_mvendorid(unsigned int cpu_id) +{ + struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id); + + return ci->mvendorid; +} +EXPORT_SYMBOL(riscv_cached_mvendorid); + +unsigned long riscv_cached_marchid(unsigned int cpu_id) +{ + struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id); + + return ci->marchid; +} +EXPORT_SYMBOL(riscv_cached_marchid); + +unsigned long riscv_cached_mimpid(unsigned int cpu_id) +{ + struct riscv_cpuinfo *ci = per_cpu_ptr(&riscv_cpuinfo, cpu_id); + + return ci->mimpid; +} +EXPORT_SYMBOL(riscv_cached_mimpid); + static int riscv_cpuinfo_starting(unsigned int cpu) { struct riscv_cpuinfo *ci = this_cpu_ptr(&riscv_cpuinfo); @@ -114,7 +136,9 @@ static int __init riscv_cpuinfo_init(void) return 0; } -device_initcall(riscv_cpuinfo_init); +arch_initcall(riscv_cpuinfo_init); + +#ifdef CONFIG_PROC_FS #define __RISCV_ISA_EXT_DATA(UPROP, EXTID) \ { \ From 652b40c54a3c906387e4abaad381ab55530331f0 Mon Sep 17 00:00:00 2001 From: Nikita Shubin Date: Mon, 15 Aug 2022 16:22:38 +0300 Subject: [PATCH 066/169] RISC-V: Create unique identification for SoC PMU Reformed from the following patches: - (e5884bfc7d89) perf tools riscv: Add support for get_cpuid_str function - (cf2c51cbd51c) perf arch events: riscv sbi firmware std event files - (419f297d4a1a) perf vendor events riscv: add Sifive U74 JSON file Signed-off-by: Locus Wei-Han Chen --- tools/perf/arch/riscv/util/Build | 1 + tools/perf/arch/riscv/util/header.c | 103 ++++++++++++++ .../arch/riscv/riscv-sbi-firmware.json | 134 ++++++++++++++++++ .../arch/riscv/sifive/u74/firmware.json | 68 +++++++++ .../arch/riscv/sifive/u74/instructions.json | 92 ++++++++++++ .../arch/riscv/sifive/u74/memory.json | 32 +++++ .../arch/riscv/sifive/u74/microarch.json | 57 ++++++++ 7 files changed, 487 insertions(+) create mode 100644 tools/perf/arch/riscv/util/header.c create mode 100644 tools/perf/pmu-events/arch/riscv/riscv-sbi-firmware.json create mode 100644 tools/perf/pmu-events/arch/riscv/sifive/u74/firmware.json create mode 100644 tools/perf/pmu-events/arch/riscv/sifive/u74/instructions.json create mode 100644 tools/perf/pmu-events/arch/riscv/sifive/u74/memory.json create mode 100644 tools/perf/pmu-events/arch/riscv/sifive/u74/microarch.json diff --git a/tools/perf/arch/riscv/util/Build b/tools/perf/arch/riscv/util/Build index 7d3050134ae0fd..603dbb5ae4dc9d 100644 --- a/tools/perf/arch/riscv/util/Build +++ b/tools/perf/arch/riscv/util/Build @@ -1,4 +1,5 @@ perf-y += perf_regs.o +perf-y += header.o perf-$(CONFIG_DWARF) += dwarf-regs.o perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o diff --git a/tools/perf/arch/riscv/util/header.c b/tools/perf/arch/riscv/util/header.c new file mode 100644 index 00000000000000..bdee502565bb6f --- /dev/null +++ b/tools/perf/arch/riscv/util/header.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Implementation of get_cpuid(). + * + * Author: Nikita Shubin + */ + +#include +#include +#include +#include +#include "../../util/debug.h" +#include "../../util/header.h" + +#define CPUINFO_MVEN "mvendorid" +#define CPUINFO_MARCH "marchid" +#define CPUINFO_MIMP "mimpid" +#define CPUINFO "/proc/cpuinfo" + +static char *_get_field(const char *line) +{ + char *line2, *nl; + + line2 = strrchr(line, ' '); + if (!line2) + return NULL; + + line2++; + nl = strrchr(line, '\n'); + if (!nl) + return NULL; + + return strndup(line2, nl - line2); +} + +static char *_get_cpuid(void) +{ + char *line = NULL; + char *mvendorid = NULL; + char *marchid = NULL; + char *mimpid = NULL; + char *cpuid = NULL; + int read; + unsigned long line_sz; + FILE *cpuinfo; + + cpuinfo = fopen(CPUINFO, "r"); + if (cpuinfo == NULL) + return cpuid; + + while ((read = getline(&line, &line_sz, cpuinfo)) != -1) { + if (!strncmp(line, CPUINFO_MVEN, strlen(CPUINFO_MVEN))) { + mvendorid = _get_field(line); + if (!mvendorid) + goto free; + } else if (!strncmp(line, CPUINFO_MARCH, strlen(CPUINFO_MARCH))) { + marchid = _get_field(line); + if (!marchid) + goto free; + } else if (!strncmp(line, CPUINFO_MIMP, strlen(CPUINFO_MIMP))) { + mimpid = _get_field(line); + if (!mimpid) + goto free; + + break; + } + } + + if (!mvendorid || !marchid || !mimpid) + goto free; + + if (asprintf(&cpuid, "%s-%s-%s", mvendorid, marchid, mimpid) < 0) + cpuid = NULL; + +free: + fclose(cpuinfo); + free(mvendorid); + free(marchid); + free(mimpid); + + return cpuid; +} + +int get_cpuid(char *buffer, size_t sz) +{ + char *cpuid = _get_cpuid(); + int ret = 0; + + if (sz < strlen(cpuid)) { + ret = -EINVAL; + goto free; + } + + scnprintf(buffer, sz, "%s", cpuid); +free: + free(cpuid); + return ret; +} + +char *get_cpuid_str(struct perf_pmu *pmu __maybe_unused) +{ + return _get_cpuid(); +} diff --git a/tools/perf/pmu-events/arch/riscv/riscv-sbi-firmware.json b/tools/perf/pmu-events/arch/riscv/riscv-sbi-firmware.json new file mode 100644 index 00000000000000..a9939823b14b5a --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/riscv-sbi-firmware.json @@ -0,0 +1,134 @@ +[ + { + "PublicDescription": "Misaligned load trap", + "ConfigCode": "0x8000000000000000", + "EventName": "FW_MISALIGNED_LOAD", + "BriefDescription": "Misaligned load trap event" + }, + { + "PublicDescription": "Misaligned store trap", + "ConfigCode": "0x8000000000000001", + "EventName": "FW_MISALIGNED_STORE", + "BriefDescription": "Misaligned store trap event" + }, + { + "PublicDescription": "Load access trap", + "ConfigCode": "0x8000000000000002", + "EventName": "FW_ACCESS_LOAD", + "BriefDescription": "Load access trap event" + }, + { + "PublicDescription": "Store access trap", + "ConfigCode": "0x8000000000000003", + "EventName": "FW_ACCESS_STORE", + "BriefDescription": "Store access trap event" + }, + { + "PublicDescription": "Illegal instruction trap", + "ConfigCode": "0x8000000000000004", + "EventName": "FW_ILLEGAL_INSN", + "BriefDescription": "Illegal instruction trap event" + }, + { + "PublicDescription": "Set timer event", + "ConfigCode": "0x8000000000000005", + "EventName": "FW_SET_TIMER", + "BriefDescription": "Set timer event" + }, + { + "PublicDescription": "Sent IPI to other HART event", + "ConfigCode": "0x8000000000000006", + "EventName": "FW_IPI_SENT", + "BriefDescription": "Sent IPI to other HART event" + }, + { + "PublicDescription": "Received IPI from other HART event", + "ConfigCode": "0x8000000000000007", + "EventName": "FW_IPI_RECEIVED", + "BriefDescription": "Received IPI from other HART event" + }, + { + "PublicDescription": "Sent FENCE.I request to other HART event", + "ConfigCode": "0x8000000000000008", + "EventName": "FW_FENCE_I_SENT", + "BriefDescription": "Sent FENCE.I request to other HART event" + }, + { + "PublicDescription": "Received FENCE.I request from other HART event", + "ConfigCode": "0x8000000000000009", + "EventName": "FW_FENCE_I_RECEIVED", + "BriefDescription": "Received FENCE.I request from other HART event" + }, + { + "PublicDescription": "Sent SFENCE.VMA request to other HART event", + "ConfigCode": "0x800000000000000a", + "EventName": "FW_SFENCE_VMA_SENT", + "BriefDescription": "Sent SFENCE.VMA request to other HART event" + }, + { + "PublicDescription": "Received SFENCE.VMA request from other HART event", + "ConfigCode": "0x800000000000000b", + "EventName": "FW_SFENCE_VMA_RECEIVED", + "BriefDescription": "Received SFENCE.VMA request from other HART event" + }, + { + "PublicDescription": "Sent SFENCE.VMA with ASID request to other HART event", + "ConfigCode": "0x800000000000000c", + "EventName": "FW_SFENCE_VMA_RECEIVED", + "BriefDescription": "Sent SFENCE.VMA with ASID request to other HART event" + }, + { + "PublicDescription": "Received SFENCE.VMA with ASID request from other HART event", + "ConfigCode": "0x800000000000000d", + "EventName": "FW_SFENCE_VMA_ASID_RECEIVED", + "BriefDescription": "Received SFENCE.VMA with ASID request from other HART event" + }, + { + "PublicDescription": "Sent HFENCE.GVMA request to other HART event", + "ConfigCode": "0x800000000000000e", + "EventName": "FW_HFENCE_GVMA_SENT", + "BriefDescription": "Sent HFENCE.GVMA request to other HART event" + }, + { + "PublicDescription": "Received HFENCE.GVMA request from other HART event", + "ConfigCode": "0x800000000000000f", + "EventName": "FW_HFENCE_GVMA_RECEIVED", + "BriefDescription": "Received HFENCE.GVMA request from other HART event" + }, + { + "PublicDescription": "Sent HFENCE.GVMA with VMID request to other HART event", + "ConfigCode": "0x8000000000000010", + "EventName": "FW_HFENCE_GVMA_VMID_SENT", + "BriefDescription": "Sent HFENCE.GVMA with VMID request to other HART event" + }, + { + "PublicDescription": "Received HFENCE.GVMA with VMID request from other HART event", + "ConfigCode": "0x8000000000000011", + "EventName": "FW_HFENCE_GVMA_VMID_RECEIVED", + "BriefDescription": "Received HFENCE.GVMA with VMID request from other HART event" + }, + { + "PublicDescription": "Sent HFENCE.VVMA request to other HART event", + "ConfigCode": "0x8000000000000012", + "EventName": "FW_HFENCE_VVMA_SENT", + "BriefDescription": "Sent HFENCE.VVMA request to other HART event" + }, + { + "PublicDescription": "Received HFENCE.VVMA request from other HART event", + "ConfigCode": "0x8000000000000013", + "EventName": "FW_HFENCE_VVMA_RECEIVED", + "BriefDescription": "Received HFENCE.VVMA request from other HART event" + }, + { + "PublicDescription": "Sent HFENCE.VVMA with ASID request to other HART event", + "ConfigCode": "0x8000000000000014", + "EventName": "FW_HFENCE_VVMA_ASID_SENT", + "BriefDescription": "Sent HFENCE.VVMA with ASID request to other HART event" + }, + { + "PublicDescription": "Received HFENCE.VVMA with ASID request from other HART event", + "ConfigCode": "0x8000000000000015", + "EventName": "FW_HFENCE_VVMA_ASID_RECEIVED", + "BriefDescription": "Received HFENCE.VVMA with ASID request from other HART event" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/sifive/u74/firmware.json b/tools/perf/pmu-events/arch/riscv/sifive/u74/firmware.json new file mode 100644 index 00000000000000..9b4a032186a7b1 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/sifive/u74/firmware.json @@ -0,0 +1,68 @@ +[ + { + "ArchStdEvent": "FW_MISALIGNED_LOAD" + }, + { + "ArchStdEvent": "FW_MISALIGNED_STORE" + }, + { + "ArchStdEvent": "FW_ACCESS_LOAD" + }, + { + "ArchStdEvent": "FW_ACCESS_STORE" + }, + { + "ArchStdEvent": "FW_ILLEGAL_INSN" + }, + { + "ArchStdEvent": "FW_SET_TIMER" + }, + { + "ArchStdEvent": "FW_IPI_SENT" + }, + { + "ArchStdEvent": "FW_IPI_RECEIVED" + }, + { + "ArchStdEvent": "FW_FENCE_I_SENT" + }, + { + "ArchStdEvent": "FW_FENCE_I_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_SENT" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/sifive/u74/instructions.json b/tools/perf/pmu-events/arch/riscv/sifive/u74/instructions.json new file mode 100644 index 00000000000000..5eab718c9256cc --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/sifive/u74/instructions.json @@ -0,0 +1,92 @@ +[ + { + "EventName": "EXCEPTION_TAKEN", + "EventCode": "0x0000100", + "BriefDescription": "Exception taken" + }, + { + "EventName": "INTEGER_LOAD_RETIRED", + "EventCode": "0x0000200", + "BriefDescription": "Integer load instruction retired" + }, + { + "EventName": "INTEGER_STORE_RETIRED", + "EventCode": "0x0000400", + "BriefDescription": "Integer store instruction retired" + }, + { + "EventName": "ATOMIC_MEMORY_RETIRED", + "EventCode": "0x0000800", + "BriefDescription": "Atomic memory operation retired" + }, + { + "EventName": "SYSTEM_INSTRUCTION_RETIRED", + "EventCode": "0x0001000", + "BriefDescription": "System instruction retired" + }, + { + "EventName": "INTEGER_ARITHMETIC_RETIRED", + "EventCode": "0x0002000", + "BriefDescription": "Integer arithmetic instruction retired" + }, + { + "EventName": "CONDITIONAL_BRANCH_RETIRED", + "EventCode": "0x0004000", + "BriefDescription": "Conditional branch retired" + }, + { + "EventName": "JAL_INSTRUCTION_RETIRED", + "EventCode": "0x0008000", + "BriefDescription": "JAL instruction retired" + }, + { + "EventName": "JALR_INSTRUCTION_RETIRED", + "EventCode": "0x0010000", + "BriefDescription": "JALR instruction retired" + }, + { + "EventName": "INTEGER_MULTIPLICATION_RETIRED", + "EventCode": "0x0020000", + "BriefDescription": "Integer multiplication instruction retired" + }, + { + "EventName": "INTEGER_DIVISION_RETIRED", + "EventCode": "0x0040000", + "BriefDescription": "Integer division instruction retired" + }, + { + "EventName": "FP_LOAD_RETIRED", + "EventCode": "0x0080000", + "BriefDescription": "Floating-point load instruction retired" + }, + { + "EventName": "FP_STORE_RETIRED", + "EventCode": "0x0100000", + "BriefDescription": "Floating-point store instruction retired" + }, + { + "EventName": "FP_ADDITION_RETIRED", + "EventCode": "0x0200000", + "BriefDescription": "Floating-point addition retired" + }, + { + "EventName": "FP_MULTIPLICATION_RETIRED", + "EventCode": "0x0400000", + "BriefDescription": "Floating-point multiplication retired" + }, + { + "EventName": "FP_FUSEDMADD_RETIRED", + "EventCode": "0x0800000", + "BriefDescription": "Floating-point fused multiply-add retired" + }, + { + "EventName": "FP_DIV_SQRT_RETIRED", + "EventCode": "0x1000000", + "BriefDescription": "Floating-point division or square-root retired" + }, + { + "EventName": "OTHER_FP_RETIRED", + "EventCode": "0x2000000", + "BriefDescription": "Other floating-point instruction retired" + } +] \ No newline at end of file diff --git a/tools/perf/pmu-events/arch/riscv/sifive/u74/memory.json b/tools/perf/pmu-events/arch/riscv/sifive/u74/memory.json new file mode 100644 index 00000000000000..be1a46312ac369 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/sifive/u74/memory.json @@ -0,0 +1,32 @@ +[ + { + "EventName": "ICACHE_RETIRED", + "EventCode": "0x0000102", + "BriefDescription": "Instruction cache miss" + }, + { + "EventName": "DCACHE_MISS_MMIO_ACCESSES", + "EventCode": "0x0000202", + "BriefDescription": "Data cache miss or memory-mapped I/O access" + }, + { + "EventName": "DCACHE_WRITEBACK", + "EventCode": "0x0000402", + "BriefDescription": "Data cache write-back" + }, + { + "EventName": "INST_TLB_MISS", + "EventCode": "0x0000802", + "BriefDescription": "Instruction TLB miss" + }, + { + "EventName": "DATA_TLB_MISS", + "EventCode": "0x0001002", + "BriefDescription": "Data TLB miss" + }, + { + "EventName": "UTLB_MISS", + "EventCode": "0x0002002", + "BriefDescription": "UTLB miss" + } +] \ No newline at end of file diff --git a/tools/perf/pmu-events/arch/riscv/sifive/u74/microarch.json b/tools/perf/pmu-events/arch/riscv/sifive/u74/microarch.json new file mode 100644 index 00000000000000..50ffa55418cb89 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/sifive/u74/microarch.json @@ -0,0 +1,57 @@ +[ + { + "EventName": "ADDRESSGEN_INTERLOCK", + "EventCode": "0x0000101", + "BriefDescription": "Address-generation interlock" + }, + { + "EventName": "LONGLAT_INTERLOCK", + "EventCode": "0x0000201", + "BriefDescription": "Long-latency interlock" + }, + { + "EventName": "CSR_READ_INTERLOCK", + "EventCode": "0x0000401", + "BriefDescription": "CSR read interlock" + }, + { + "EventName": "ICACHE_ITIM_BUSY", + "EventCode": "0x0000801", + "BriefDescription": "Instruction cache/ITIM busy" + }, + { + "EventName": "DCACHE_DTIM_BUSY", + "EventCode": "0x0001001", + "BriefDescription": "Data cache/DTIM busy" + }, + { + "EventName": "BRANCH_DIRECTION_MISPREDICTION", + "EventCode": "0x0002001", + "BriefDescription": "Branch direction misprediction" + }, + { + "EventName": "BRANCH_TARGET_MISPREDICTION", + "EventCode": "0x0004001", + "BriefDescription": "Branch/jump target misprediction" + }, + { + "EventName": "PIPE_FLUSH_CSR_WRITE", + "EventCode": "0x0008001", + "BriefDescription": "Pipeline flush from CSR write" + }, + { + "EventName": "PIPE_FLUSH_OTHER_EVENT", + "EventCode": "0x0010001", + "BriefDescription": "Pipeline flush from other event" + }, + { + "EventName": "INTEGER_MULTIPLICATION_INTERLOCK", + "EventCode": "0x0020001", + "BriefDescription": "Integer multiplication interlock" + }, + { + "EventName": "FP_INTERLOCK", + "EventCode": "0x0040001", + "BriefDescription": "Floating-point interlock" + } +] \ No newline at end of file From 2d380c0e9725c67cc1eb72f8e455aa9e9e17ef65 Mon Sep 17 00:00:00 2001 From: Mayuresh Chitale Date: Wed, 8 Feb 2023 13:13:14 +0530 Subject: [PATCH 067/169] drivers/perf: RISC-V: Allow programming custom firmware events Applications need to be able to program the SBI implementation specific or custom firmware events in addition to the standard firmware events. Remove a check in the driver that prohibits the programming of the custom firmware events. Signed-off-by: Mayuresh Chitale Reviewed-by: Andrew Jones Link: https://lore.kernel.org/r/20230208074314.3661406-1-mchitale@ventanamicro.com Signed-off-by: Palmer Dabbelt --- drivers/perf/riscv_pmu_sbi.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c index 6f9ca2c42ffe1f..f9961ae6983638 100644 --- a/drivers/perf/riscv_pmu_sbi.c +++ b/drivers/perf/riscv_pmu_sbi.c @@ -385,11 +385,8 @@ static int pmu_sbi_event_map(struct perf_event *event, u64 *econfig) bSoftware = config >> 63; raw_config_val = config & RISCV_PMU_RAW_EVENT_MASK; if (bSoftware) { - if (raw_config_val < SBI_PMU_FW_MAX) - ret = (raw_config_val & 0xFFFF) | - (SBI_PMU_EVENT_TYPE_FW << 16); - else - return -EINVAL; + ret = (raw_config_val & 0xFFFF) | + (SBI_PMU_EVENT_TYPE_FW << 16); } else { ret = RISCV_PMU_RAW_EVENT_IDX; *econfig = raw_config_val; From 036dcc18764da628ae09b152f49bb14e2a56a785 Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Mon, 9 Jan 2023 18:48:58 +0800 Subject: [PATCH 068/169] riscv: andes: add support for HPM variant on Andes With the Andes non-65 series cores being designed before or during the ratification to the SSCOFPMF extension, it implements functionality very similar but not equal to it. The main differences are a the overflow CSR and irq number. Signed-off-by: Locus Wei-Han Chen --- arch/riscv/Kconfig.erratas | 13 ++++++++++ arch/riscv/errata/andes/errata.c | 18 +++++++++++++ arch/riscv/include/asm/errata_list.h | 14 +++++++--- drivers/irqchip/irq-riscv-intc.c | 4 +++ drivers/perf/riscv_pmu_sbi.c | 38 ++++++++++++++++++++++++---- include/soc/andes/csr.h | 1 + include/soc/andes/sbi.h | 3 +++ 7 files changed, 83 insertions(+), 8 deletions(-) diff --git a/arch/riscv/Kconfig.erratas b/arch/riscv/Kconfig.erratas index fb2d33de3b3ac3..8ec1a9034cc6a1 100644 --- a/arch/riscv/Kconfig.erratas +++ b/arch/riscv/Kconfig.erratas @@ -35,6 +35,19 @@ config ERRATA_ANDES_PA_MSB If you don't know what to do here, say "Y". +config ERRATA_ANDES_HPM + bool "Apply Andes HPM errata" + depends on ERRATA_ANDES && RISCV_PMU_SBI + default y + help + The Andes cores implement a HPM overflow extension very + similar to the core SSCOFPMF extension. + + This will apply the overflow errata to handle the non-standard + behavious via the regular SBI PMU driver and interface. + + If you don't know what to do here, say "Y". + config ERRATA_SIFIVE bool "SiFive errata" depends on !XIP_KERNEL diff --git a/arch/riscv/errata/andes/errata.c b/arch/riscv/errata/andes/errata.c index edde226453023b..0442ccaf46e155 100644 --- a/arch/riscv/errata/andes/errata.c +++ b/arch/riscv/errata/andes/errata.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,19 @@ static bool errata_legacy_mmu_check_func(unsigned long arch_id, return andes_legacy_mmu; } +static bool errata_probe_hpm(unsigned long arch_id, + unsigned long impid, + unsigned int stage) +{ + struct sbiret result; + + result = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_HPM, 0,0,0,0,0,0); + if (result.value) + return true; + else + return false; +} + static struct errata_info_t errata_list[ERRATA_ANDES_NUMBER] = { { .name = "legacy_mmu", @@ -65,6 +79,10 @@ static struct errata_info_t errata_list[ERRATA_ANDES_NUMBER] = { .name = "pa_msb", .check_func = errata_msb_check_func }, + { + .name = "andes_hpm", + .check_func = errata_probe_hpm + }, }; static u32 __init_or_module andes_errata_probe(unsigned long archid, diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index 4fe856d1de070e..e5a9869fdbb251 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -12,9 +12,14 @@ #ifdef CONFIG_ERRATA_ANDES #define ERRATA_ANDES_LEGACY_MMU 0 #define ERRATA_ANDES_PA_MSB 1 -#define ERRATA_ANDES_NUMBER 2 +#define ERRATA_ANDES_HPM 2 +#define ERRATA_ANDES_NUMBER 3 #endif +/* Andes HPM */ +#define ANDES_RV_IRQ_HPM 18 +#define ANDES_CSR_SCOUNTEROVF 0x9d4 + #ifdef CONFIG_ERRATA_SIFIVE #define ERRATA_SIFIVE_CIP_453 0 #define ERRATA_SIFIVE_CIP_1200 1 @@ -159,11 +164,14 @@ asm volatile(ALTERNATIVE_2( \ #define THEAD_C9XX_CSR_SCOUNTEROF 0x5c5 #define ALT_SBI_PMU_OVERFLOW(__ovl) \ -asm volatile(ALTERNATIVE( \ +asm volatile(ALTERNATIVE_2( \ "csrr %0, " __stringify(CSR_SSCOUNTOVF), \ "csrr %0, " __stringify(THEAD_C9XX_CSR_SCOUNTEROF), \ THEAD_VENDOR_ID, ERRATA_THEAD_PMU, \ - CONFIG_ERRATA_THEAD_PMU) \ + CONFIG_ERRATA_THEAD_PMU, \ + "csrr %0, " __stringify(ANDES_CSR_SCOUNTEROVF), \ + ANDES_VENDOR_ID, ERRATA_ANDES_HPM, \ + CONFIG_ERRATA_ANDES_HPM) \ : "=r" (__ovl) : \ : "memory") diff --git a/drivers/irqchip/irq-riscv-intc.c b/drivers/irqchip/irq-riscv-intc.c index 4b66850978e6e8..99fec67ca82a8a 100644 --- a/drivers/irqchip/irq-riscv-intc.c +++ b/drivers/irqchip/irq-riscv-intc.c @@ -16,6 +16,8 @@ #include #include #include +#include +#include static struct irq_domain *intc_domain; @@ -23,6 +25,8 @@ static asmlinkage void riscv_intc_irq(struct pt_regs *regs) { unsigned long cause = regs->cause & ~CAUSE_IRQ_FLAG; + if (cause == IRQ_S_HPM) + cause = cause & ~(1 << 8); if (unlikely(cause >= BITS_PER_LONG)) panic("unexpected interrupt cause"); diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c index f9961ae6983638..7fbf8939867614 100644 --- a/drivers/perf/riscv_pmu_sbi.c +++ b/drivers/perf/riscv_pmu_sbi.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include @@ -51,6 +53,7 @@ static union sbi_pmu_ctr_info *pmu_ctr_list; static bool riscv_pmu_use_irq; static unsigned int riscv_pmu_irq_num; static unsigned int riscv_pmu_irq; +static DEFINE_STATIC_KEY_FALSE_RO(andes_hpm); struct sbi_pmu_event_data { union { @@ -580,7 +583,10 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev) fidx = find_first_bit(cpu_hw_evt->used_hw_ctrs, RISCV_MAX_COUNTERS); event = cpu_hw_evt->events[fidx]; if (!event) { - csr_clear(CSR_SIP, BIT(riscv_pmu_irq_num)); + if (static_branch_unlikely(&andes_hpm)) + csr_clear(CSR_SLIP, BIT(riscv_pmu_irq_num)); + else + csr_clear(CSR_SIP, BIT(riscv_pmu_irq_num)); return IRQ_NONE; } @@ -594,7 +600,10 @@ static irqreturn_t pmu_sbi_ovf_handler(int irq, void *dev) * Overflow interrupt pending bit should only be cleared after stopping * all the counters to avoid any race condition. */ - csr_clear(CSR_SIP, BIT(riscv_pmu_irq_num)); + if (static_branch_unlikely(&andes_hpm)) + csr_clear(CSR_SLIP, BIT(riscv_pmu_irq_num)); + else + csr_clear(CSR_SIP, BIT(riscv_pmu_irq_num)); /* No overflow bit is set */ if (!overflow) @@ -663,8 +672,13 @@ static int pmu_sbi_starting_cpu(unsigned int cpu, struct hlist_node *node) if (riscv_pmu_use_irq) { cpu_hw_evt->irq = riscv_pmu_irq; - csr_clear(CSR_IP, BIT(riscv_pmu_irq_num)); - csr_set(CSR_IE, BIT(riscv_pmu_irq_num)); + if (static_branch_unlikely(&andes_hpm)) { + csr_clear(CSR_SLIP, BIT(riscv_pmu_irq_num)); + csr_set(CSR_SLIE, BIT(riscv_pmu_irq_num)); + } else { + csr_clear(CSR_IP, BIT(riscv_pmu_irq_num)); + csr_set(CSR_IE, BIT(riscv_pmu_irq_num)); + } enable_percpu_irq(riscv_pmu_irq, IRQ_TYPE_NONE); } @@ -675,7 +689,10 @@ static int pmu_sbi_dying_cpu(unsigned int cpu, struct hlist_node *node) { if (riscv_pmu_use_irq) { disable_percpu_irq(riscv_pmu_irq); - csr_clear(CSR_IE, BIT(riscv_pmu_irq_num)); + if (static_branch_unlikely(&andes_hpm)) + csr_clear(CSR_SLIE, BIT(riscv_pmu_irq_num)); + else + csr_clear(CSR_IE, BIT(riscv_pmu_irq_num)); } /* Disable all counters access for user mode now */ @@ -690,6 +707,7 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde struct cpu_hw_events __percpu *hw_events = pmu->hw_events; struct device_node *cpu, *child; struct irq_domain *domain = NULL; + struct sbiret result; if (riscv_isa_extension_available(NULL, SSCOFPMF)) { riscv_pmu_irq_num = RV_IRQ_PMU; @@ -700,6 +718,16 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde riscv_cached_mimpid(0) == 0) { riscv_pmu_irq_num = THEAD_C9XX_RV_IRQ_PMU; riscv_pmu_use_irq = true; + } else if (IS_ENABLED(CONFIG_ERRATA_ANDES_HPM) && + riscv_cached_mvendorid(0) == ANDES_VENDOR_ID) { + + result = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_HPM, 0,0,0,0,0,0); + if (result.value) { + riscv_pmu_irq_num = ANDES_RV_IRQ_HPM; + riscv_pmu_use_irq = true; + static_branch_enable(&andes_hpm); + pr_info("Andes HPM is available\n"); + } } if (!riscv_pmu_use_irq) diff --git a/include/soc/andes/csr.h b/include/soc/andes/csr.h index b6569c0755c7fa..3907c76d033428 100644 --- a/include/soc/andes/csr.h +++ b/include/soc/andes/csr.h @@ -146,5 +146,6 @@ extern u32 CCTL_L2_STATUS_PER_CORE_OFFSET; #define IRQ_HPM_OVF 18 #define SLIP_PMOVI (_AC(0x1, UL) << IRQ_HPM_OVF) +#define IRQ_S_HPM 274 #endif /* !__SOC_ANDES_CSR_H */ diff --git a/include/soc/andes/sbi.h b/include/soc/andes/sbi.h index 562d1661c423cb..05ac45cc78bd96 100644 --- a/include/soc/andes/sbi.h +++ b/include/soc/andes/sbi.h @@ -49,6 +49,9 @@ enum sbi_ext_andes_fid { SBI_EXT_ANDES_PROBE_PPMA, SBI_EXT_ANDES_DCACHE_WBINVAL_ALL, + + /* HPM */ + SBI_EXT_ANDES_HPM, }; /* Programmable physical memory attributes (PPMA) */ From c1c239007d0e3d5307d20c01de5cac0bb6ed2de6 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Fri, 7 Apr 2023 15:19:26 +0800 Subject: [PATCH 069/169] riscv: andes: defconfig: add config and include for Andes riscv pmu Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig.erratas | 2 +- arch/riscv/configs/andes-support.config | 2 +- drivers/perf/riscv_pmu_sbi.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/Kconfig.erratas b/arch/riscv/Kconfig.erratas index 8ec1a9034cc6a1..efeb25ea342779 100644 --- a/arch/riscv/Kconfig.erratas +++ b/arch/riscv/Kconfig.erratas @@ -37,7 +37,7 @@ config ERRATA_ANDES_PA_MSB config ERRATA_ANDES_HPM bool "Apply Andes HPM errata" - depends on ERRATA_ANDES && RISCV_PMU_SBI + depends on ERRATA_ANDES default y help The Andes cores implement a HPM overflow extension very diff --git a/arch/riscv/configs/andes-support.config b/arch/riscv/configs/andes-support.config index 30e0b70cd6dc5a..6110ce28d31c39 100644 --- a/arch/riscv/configs/andes-support.config +++ b/arch/riscv/configs/andes-support.config @@ -5,7 +5,7 @@ CONFIG_ANDES_CACHE=y CONFIG_ANDES_DMA_NONCOHERENT=y CONFIG_ANDES_PPMA=y CONFIG_RISCV_ISA_ZICBOM=y -# CONFIG_RISCV_PMU is not set +CONFIG_RISCV_PMU=y CONFIG_FTMAC100=y CONFIG_MMC_FTSDCG=y diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c index 7fbf8939867614..c7f1588a3e7389 100644 --- a/drivers/perf/riscv_pmu_sbi.c +++ b/drivers/perf/riscv_pmu_sbi.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include From 3eedebd55c3afe6212ef9ec49379fcd41b1ae7d7 Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Fri, 19 May 2023 14:53:01 +0800 Subject: [PATCH 070/169] riscv: andes: Fix the error for 32-bit perf error message: util/genelf.h:47:2: error: #error "unsupported architecture" 47 | #error "unsupported architecture" Signed-off-by: Locus Wei-Han Chen --- tools/perf/util/genelf.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h index 6af062d1c4522c..3c5e5a252006e3 100644 --- a/tools/perf/util/genelf.h +++ b/tools/perf/util/genelf.h @@ -43,6 +43,9 @@ int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_ent #elif defined(__riscv) && __riscv_xlen == 64 #define GEN_ELF_ARCH EM_RISCV #define GEN_ELF_CLASS ELFCLASS64 +#elif defined(__riscv) && __riscv_xlen == 32 +#define GEN_ELF_ARCH EM_RISCV +#define GEN_ELF_CLASS ELFCLASS32 #else #error "unsupported architecture" #endif From e7b3e61c8bf3b12a4cc498266423a0a8f2d691fa Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Wed, 18 Jan 2023 13:16:53 +0800 Subject: [PATCH 071/169] riscv: andes: add Andes AX45 JSON file This patch add the Andes AX45 JSON file Signed-off-by: Locus Wei-Han Chen --- .../arch/riscv/andes/AX45/firmware.json | 68 +++++ .../arch/riscv/andes/AX45/riscvstd.json | 252 ++++++++++++++++++ tools/perf/pmu-events/arch/riscv/mapfile.csv | 18 ++ 3 files changed, 338 insertions(+) create mode 100644 tools/perf/pmu-events/arch/riscv/andes/AX45/firmware.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/AX45/riscvstd.json create mode 100644 tools/perf/pmu-events/arch/riscv/mapfile.csv diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX45/firmware.json b/tools/perf/pmu-events/arch/riscv/andes/AX45/firmware.json new file mode 100644 index 00000000000000..9b4a032186a7b1 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/AX45/firmware.json @@ -0,0 +1,68 @@ +[ + { + "ArchStdEvent": "FW_MISALIGNED_LOAD" + }, + { + "ArchStdEvent": "FW_MISALIGNED_STORE" + }, + { + "ArchStdEvent": "FW_ACCESS_LOAD" + }, + { + "ArchStdEvent": "FW_ACCESS_STORE" + }, + { + "ArchStdEvent": "FW_ILLEGAL_INSN" + }, + { + "ArchStdEvent": "FW_SET_TIMER" + }, + { + "ArchStdEvent": "FW_IPI_SENT" + }, + { + "ArchStdEvent": "FW_IPI_RECEIVED" + }, + { + "ArchStdEvent": "FW_FENCE_I_SENT" + }, + { + "ArchStdEvent": "FW_FENCE_I_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_SENT" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX45/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/AX45/riscvstd.json new file mode 100644 index 00000000000000..55cd56d9e527e8 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/AX45/riscvstd.json @@ -0,0 +1,252 @@ +[ + { + "EventCode": "0x10", + "EventName": "cycle_count", + "BriefDescription": "Cycle counts" + }, + { + "EventCode": "0x20", + "EventName": "inst_count", + "BriefDescription": "Retired instruction counts" + }, + { + "EventCode": "0x30", + "EventName": "int_load_inst", + "BriefDescription": "Integer load instructions" + }, + { + "EventCode": "0x40", + "EventName": "int_store_inst", + "BriefDescription": "Integer store instructions" + }, + { + "EventCode": "0x50", + "EventName": "atomic_mem_op", + "BriefDescription": "Atomic memory operation" + }, + { + "EventCode": "0x60", + "EventName": "sys_inst", + "BriefDescription": "System instructions" + }, + { + "EventCode": "0x70", + "EventName": "int_compute_inst", + "BriefDescription": "Integer computational instruction" + }, + { + "EventCode": "0x80", + "EventName": "condition_br", + "BriefDescription": "Conditional branch" + }, + { + "EventCode": "0x90", + "EventName": "taken_condition_br", + "BriefDescription": "Taken conditional branch" + }, + { + "EventCode": "0xA0", + "EventName": "jal_inst", + "BriefDescription": "JAL instruction" + }, + { + "EventCode": "0xB0", + "EventName": "jalr_inst", + "BriefDescription": "JALR instruction" + }, + { + "EventCode": "0xC0", + "EventName": "ret_inst", + "BriefDescription": "Return instruction" + }, + { + "EventCode": "0xD0", + "EventName": "control_trans_inst", + "BriefDescription": "Control transfer instruction" + }, + { + "EventCode": "0xE0", + "EventName": "ex9_inst", + "BriefDescription": "EX9 instruction" + }, + { + "EventCode": "0xF0", + "EventName": "int_mul_inst", + "BriefDescription": "Integer multiplication instruction" + }, + { + "EventCode": "0x100", + "EventName": "int_div_rem_inst", + "BriefDescription": "Integer division/remainder instruction" + }, + { + "EventCode": "0x110", + "EventName": "float_load_inst", + "BriefDescription": "Floating-point load instruction" + }, + { + "EventCode": "0x120", + "EventName": "float_store_inst", + "BriefDescription": "Floating-point store instruction" + }, + { + "EventCode": "0x130", + "EventName": "float_add_sub_inst", + "BriefDescription": "Floating-point addition/subtraction" + }, + { + "EventCode": "0x140", + "EventName": "float_mul_inst", + "BriefDescription": "Floating-point multiplication" + }, + { + "EventCode": "0x150", + "EventName": "float_fused_muladd_inst", + "BriefDescription": "Floating-point fused multiply-add" + }, + { + "EventCode": "0x160", + "EventName": "float_div_sqrt_inst", + "BriefDescription": "Floating-point division or square-root" + }, + { + "EventCode": "0x170", + "EventName": "other_float_inst", + "BriefDescription": "Other floating-point instruction" + }, + { + "EventCode": "0x01", + "EventName": "ilm_access", + "BriefDescription": "ILM access" + }, + { + "EventCode": "0x11", + "EventName": "dlm_access", + "BriefDescription": "DLM access" + }, + { + "EventCode": "0x21", + "EventName": "icache_access", + "BriefDescription": "ICACHE access" + }, + { + "EventCode": "0x31", + "EventName": "icache_miss", + "BriefDescription": "ICACHE miss" + }, + { + "EventCode": "0x41", + "EventName": "dcache_access", + "BriefDescription": "DCACHE access" + }, + { + "EventCode": "0x51", + "EventName": "dcache_miss", + "BriefDescription": "DCACHE miss" + }, + { + "EventCode": "0x61", + "EventName": "dcache_load_access", + "BriefDescription": "DCACHE load access" + }, + { + "EventCode": "0x71", + "EventName": "dcache_load_miss", + "BriefDescription": "DCACHE load miss" + }, + { + "EventCode": "0x81", + "EventName": "dcache_store_access", + "BriefDescription": "DCACHE store access" + }, + { + "EventCode": "0x91", + "EventName": "dcache_store_miss", + "BriefDescription": "DCACHE store miss" + }, + { + "EventCode": "0xA1", + "EventName": "dcache_wb", + "BriefDescription": "DCACHE writeback" + }, + { + "EventCode": "0xB1", + "EventName": "cycle_wait_icache_fill", + "BriefDescription": "Cycles waiting for ICACHE fill data" + }, + { + "EventCode": "0xC1", + "EventName": "cycle_wait_dcache_fill", + "BriefDescription": "Cycles waiting for DCACHE fill data" + }, + { + "EventCode": "0xD1", + "EventName": "uncached_ifetch_from_bus", + "BriefDescription": "Uncached ifetch data access from bus" + }, + { + "EventCode": "0xE1", + "EventName": "uncached_load_from_bus", + "BriefDescription": "Uncached load data access from bus" + }, + { + "EventCode": "0xF1", + "EventName": "cycle_wait_uncached_ifetch", + "BriefDescription": "Cycles waiting for uncached ifetch data from bus" + }, + { + "EventCode": "0x101", + "EventName": "cycle_wait_uncached_load", + "BriefDescription": "Cycles waiting for uncached load data from bus" + }, + { + "EventCode": "0x111", + "EventName": "main_itlb_access", + "BriefDescription": "Main ITLB access" + }, + { + "EventCode": "0x121", + "EventName": "main_itlb_miss", + "BriefDescription": "Main ITLB miss" + }, + { + "EventCode": "0x131", + "EventName": "main_dtlb_access", + "BriefDescription": "Main DTLB access" + }, + { + "EventCode": "0x141", + "EventName": "main_dtlb_miss", + "BriefDescription": "Main DTLB miss" + }, + { + "EventCode": "0x151", + "EventName": "cycle_wait_itlb_fill", + "BriefDescription": "Cycles waiting for Main ITLB fill data" + }, + { + "EventCode": "0x161", + "EventName": "pipe_stall_cycle_dtlb_miss", + "BriefDescription": "Pipeline stall cycles caused by Main DTLB miss" + }, + { + "EventCode": "0x02", + "EventName": "mispredict_condition_br", + "BriefDescription": "Misprediction of conditional branches" + }, + { + "EventCode": "0x12", + "EventName": "mispredict_take_condition_br", + "BriefDescription": "Misprediction of taken conditional branches" + }, + { + "EventCode": "0x22", + "EventName": "mispredict_target_ret_inst", + "BriefDescription": "Misprediction of targets of Return instructions" + }, + { + "EventCode": "0x32", + "EventName": "replay_las_sas", + "BriefDescription": "Replay for load-after-store or store-after-store cases" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/mapfile.csv b/tools/perf/pmu-events/arch/riscv/mapfile.csv new file mode 100644 index 00000000000000..5f0e9a8b08edec --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/mapfile.csv @@ -0,0 +1,18 @@ +# Format: +# MVENDORID-MARCHID-MIMPID,Version,JSON/file/pathname,Type +# +# where +# MVENDORID JEDEC code of the core provider +# MARCHID base microarchitecture of the hart +# MIMPID unique encoding of the version +# of the processor implementation +# Version could be used to track version of JSON file +# but currently unused. +# JSON/file/pathname is the path to JSON file, relative +# to tools/perf/pmu-events/arch/riscv/. +# Type is core, uncore etc +# +# +#MVENDORID-MARCHID-MIMPID,Version,Filename,EventType +0x489-0x8000000000000007-0x[[:xdigit:]]+,v1,sifive/u74,core +0x31e-0x8000000000008a45-0x[[:xdigit:]]+,V5,andes/AX45,core From 41e44a1768fe70f932a79d8c5852022e57a5f860 Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Thu, 11 May 2023 21:09:33 +0800 Subject: [PATCH 072/169] tools: perf: andes: Add Andes L2C events to JSON files We add 2 custom firmware events to count L2C accesses and access_miss, which can be monitored with following commands: $ perf stat -e r8000000000000016,r8000000000000017 or $ perf stat -e andes_l2c_accesses,andes_l2c_access_misses L2C events do not supported counter overflow (cannot sample with perf record). Signed-off-by: Yu Chien Peter Lin --- .../pmu-events/arch/riscv/andes/AX45/firmware.json | 6 ++++++ .../pmu-events/arch/riscv/riscv-sbi-firmware.json | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX45/firmware.json b/tools/perf/pmu-events/arch/riscv/andes/AX45/firmware.json index 9b4a032186a7b1..61e42c1813fa33 100644 --- a/tools/perf/pmu-events/arch/riscv/andes/AX45/firmware.json +++ b/tools/perf/pmu-events/arch/riscv/andes/AX45/firmware.json @@ -64,5 +64,11 @@ }, { "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESSES" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESS_MISSES" } ] diff --git a/tools/perf/pmu-events/arch/riscv/riscv-sbi-firmware.json b/tools/perf/pmu-events/arch/riscv/riscv-sbi-firmware.json index a9939823b14b5a..8d421db638c6f1 100644 --- a/tools/perf/pmu-events/arch/riscv/riscv-sbi-firmware.json +++ b/tools/perf/pmu-events/arch/riscv/riscv-sbi-firmware.json @@ -130,5 +130,17 @@ "ConfigCode": "0x8000000000000015", "EventName": "FW_HFENCE_VVMA_ASID_RECEIVED", "BriefDescription": "Received HFENCE.VVMA with ASID request from other HART event" + }, + { + "PublicDescription": "L2-Cache accesses", + "ConfigCode": "0x8000000000000016", + "EventName": "ANDES_L2C_ACCESSES", + "BriefDescription": "L2-Cache accesses" + }, + { + "PublicDescription": "L2-Cache access misses", + "ConfigCode": "0x8000000000000017", + "EventName": "ANDES_L2C_ACCESS_MISSES", + "BriefDescription": "L2-Cache access misses" } ] From 7eb47406fcc6b78a60bd0d31a19562bf6885e00d Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Tue, 6 Jun 2023 13:47:04 +0800 Subject: [PATCH 073/169] perf/core: Implement workaround to add pmu stop before unthrottling to prevent WARNING It is a temporary workaround for ast530. We will continue to investigate and resolve the root cause in the future. Currently, during the perf sampling, if the perf interrupt takes too long, perf framework will lower the perf_event_max_sample_rate. This will limit the number of samples per timer tick (max_samples_per_tick) and set hwc->interrupts to MAX_INTERRUPTS within the __perf_event_account_interrupt() function. Afterward, the perf framework will unthrottle the event in the timer interrupt handler, which triggers the driver's *_pmu_start() function. Most of the driver's *_pmu_start() functions will check the event->hw.state to determine whether this event has stopped. If the event has not stopped, a WARN_ON_ONCE() warning will be triggered. This is currently a workaround and may be reverted in the future. Reformed from the following: https://patchwork.kernel.org/project/linux-riscv/patch/20230602094841.1225-1-eric.lin@sifive.com/ Signed-off-by: Locus Wei-Han Chen --- kernel/events/core.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index 872d149b1959b3..4c2ca5b30d7036 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4127,6 +4127,11 @@ static void perf_adjust_freq_unthr_context(struct perf_event_context *ctx, if (hwc->interrupts == MAX_INTERRUPTS) { hwc->interrupts = 0; + + /* + * This is a workaround that may be reverted in the future. + */ + event->pmu->stop(event, 0); perf_log_throttle(event, 1); event->pmu->start(event, 0); } From a51596900d87a428ecf48ec7951d238476405b08 Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Tue, 6 Jun 2023 13:53:23 +0800 Subject: [PATCH 074/169] perf/core: Implement workaround to fix RCU stall issue when using perf record This patch is a workaround for now. As the root cause has not yet been identified, we will continue to search for the root cause and resolve it in the future. When using the 'perf record' command, an RCU (Read-Copy-Update) stall issue is encountered. The error log is shown as below: [ 52.012118 ] rcu: INFO: rcu_preempt detected stalls on CPUs/tasks: [ 52.030451 ] rcu: 3-...0: (2 ticks this GP) idle=366c/1/0x4000000000000002 softirq=378/378 fqs=2615 [ 52.057510 ] (detected by 1, t=5259 jiffies, g=1437, q=13 ncpus=4) [ 52.076009 ] Task dump for CPU 3: [ 52.085725 ] task:perf-exec state:R running task stack:0 pid:96 ppid:93 flags:0x0000000a [ 52.115443 ] Call Trace: [ 52.122860 ] [] __schedule+0x4cc/0x554 We have found that RCU stall is due to an excessive number of perf interrupts. Although the perf process itself seems to be functioning properly, but there is a potential issue with the Linux timer subsystem that causes the perf period to remain consistently at 1. As a result, overflow interrupts are triggered on every event, resulting in a large number of perf interrupts. We have discovered that modifying the parameters passed into perf_adjust_period() to calculate the difference of HPMcounter every time, instead of relying on the last period value, can effectively change the value of the period and prevent from the occurrence of RCU stall issues. Note again that this is only a workaround, not a final solution. This patch should be reverted once the root cause is found. Signed-off-by: Locus Wei-Han Chen --- kernel/events/core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 4c2ca5b30d7036..a28c9a28d21b53 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -9331,8 +9331,18 @@ __perf_event_account_interrupt(struct perf_event *event, int throttle) hwc->freq_time_stamp = now; - if (delta > 0 && delta < 2*TICK_NSEC) - perf_adjust_period(event, delta, hwc->last_period, true); + /* + * This is a workaround that "should" be reverted once the + * root cause is found. + */ + event->pmu->stop(event, PERF_EF_UPDATE); + u64 now_count = local64_read(&event->count); + s64 delta_count = now_count - hwc->freq_count_stamp; + hwc->freq_count_stamp = now_count; + + if ((delta > 0) && (delta < 2 * TICK_NSEC) && (delta_count > 0)) + perf_adjust_period(event, delta, delta_count, true); + event->pmu->start(event, delta_count > 0 ? PERF_EF_RELOAD : 0); } return ret; From 50041e0ca69bba95593ec4c1f046ccc4e66f0930 Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Thu, 8 Jun 2023 15:04:40 +0800 Subject: [PATCH 075/169] tool/perf: Adjust the sample frequency to 1000 This patch is a workaround for now. Due to the slower speed of our FPGA, we have temporarily set the clock frequency to 1000. It may be adjusted back in the future. Reformed from the following patches - (447a6013e9173) perf tools: Bump default sample freq to 4 kHz Signed-off-by: Locus Wei-Han Chen --- tools/perf/builtin-record.c | 7 ++++++- tools/perf/builtin-top.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 7314183cdcb6c0..cfad0a8b725b2a 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -3293,7 +3293,12 @@ static struct record record = { .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, - .freq = 4000, + /* + * Due to the slower speed of our FPGA, we have temporarily + * set the clock frequency to 1000. + * It may be reverted in the future. + */ + .freq = 1000, .target = { .uses_mmap = true, .default_per_cpu = true, diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index f9917848cdad0b..f34f729f7126f5 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -1419,7 +1419,7 @@ int cmd_top(int argc, const char **argv) .mmap_pages = UINT_MAX, .user_freq = UINT_MAX, .user_interval = ULLONG_MAX, - .freq = 4000, /* 4 KHz */ + .freq = 1000, /* 1 KHz */ .target = { .uses_mmap = true, }, From 803957f75ec7861d5e12d91b56ce5c7a470ed374 Mon Sep 17 00:00:00 2001 From: Eric Lin Date: Fri, 5 Jun 2020 13:32:55 +0800 Subject: [PATCH 076/169] tool: andes: perf: fix perf tools compile error on Linux5.4 when compiling perf tools in Linux 5.4, it will encounter compile error like this: bench/futex.h:37:10: error: 'SYS_futex' undeclared (first use in this function); did you mean 'SYS_tee'? syscall(SYS_futex, uaddr, op | opflags, val, timeout, uaddr2, val3) ^ Add require definition to cope with 32 to 64 bit time_t compatibility, including fstat clone3 and other affected syscalls. Signed-off-by: Eric Lin --- tools/arch/riscv/include/uapi/asm/unistd.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/arch/riscv/include/uapi/asm/unistd.h b/tools/arch/riscv/include/uapi/asm/unistd.h index f506cca520b06f..0b461204298688 100644 --- a/tools/arch/riscv/include/uapi/asm/unistd.h +++ b/tools/arch/riscv/include/uapi/asm/unistd.h @@ -20,6 +20,12 @@ #define __ARCH_WANT_SET_GET_RLIMIT #endif /* __LP64__ */ +#define __ARCH_WANT_NEW_STAT +#define __ARCH_WANT_STAT64 +#define __ARCH_WANT_SET_GET_RLIMIT +#define __ARCH_WANT_SYS_CLONE3 +#define __ARCH_WANT_TIME32_SYSCALLS + #include /* From 5ffbeadbc6156967d9bc211ec710fe38dae75682 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 9 Feb 2023 13:36:36 +0100 Subject: [PATCH 077/169] riscv: hwcap: Don't alphabetize ISA extension IDs While the comment above the ISA extension ID definitions says "Entries are sorted alphabetically.", this stopped being good advice with commit d8a3d8a75206 ("riscv: hwcap: make ISA extension ids can be used in asm"), as we now use macros instead of enums. Reshuffling defines is error-prone, so, since they don't need to be in any particular order, change the advice to just adding new extensions at the bottom. Also, take the opportunity to change spaces to tabs, merge three comments into one, and move the base and max defines into more logical locations wrt the ID definitions. Signed-off-by: Andrew Jones Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20230209123636.123537-1-ajones@ventanamicro.com Cc: stable@vger.kernel.org Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/hwcap.h | 44 ++++++++++++++++------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h index 9da1eae9057bac..f78062dfe3f2c1 100644 --- a/arch/riscv/include/asm/hwcap.h +++ b/arch/riscv/include/asm/hwcap.h @@ -37,31 +37,27 @@ extern unsigned long elf_hwcap; #define RISCV_ISA_EXT_u ('u' - 'a') /* - * Increse this to higher value as kernel support more ISA extensions. - */ -#define RISCV_ISA_EXT_MAX 64 -#define RISCV_ISA_EXT_NAME_LEN_MAX 32 - -/* The base ID for multi-letter ISA extensions */ -#define RISCV_ISA_EXT_BASE 26 - -/* - * This enum represent the logical ID for each multi-letter RISC-V ISA extension. - * The logical ID should start from RISCV_ISA_EXT_BASE and must not exceed - * RISCV_ISA_EXT_MAX. 0-25 range is reserved for single letter - * extensions while all the multi-letter extensions should define the next - * available logical extension id. + * These macros represent the logical IDs of each multi-letter RISC-V ISA + * extension and are used in the ISA bitmap. The logical IDs start from + * RISCV_ISA_EXT_BASE, which allows the 0-25 range to be reserved for single + * letter extensions. The maximum, RISCV_ISA_EXT_MAX, is defined in order + * to allocate the bitmap and may be increased when necessary. + * + * New extensions should just be added to the bottom, rather than added + * alphabetically, in order to avoid unnecessary shuffling. */ -enum riscv_isa_ext_id { - RISCV_ISA_EXT_SSCOFPMF = RISCV_ISA_EXT_BASE, - RISCV_ISA_EXT_SVPBMT, - RISCV_ISA_EXT_ZICBOM, - RISCV_ISA_EXT_ZIHINTPAUSE, - RISCV_ISA_EXT_SSTC, - RISCV_ISA_EXT_SVINVAL, - ANDES_ISA_EXT_DSP, - RISCV_ISA_EXT_ID_MAX = RISCV_ISA_EXT_MAX, -}; +#define RISCV_ISA_EXT_BASE 26 + +#define RISCV_ISA_EXT_SSCOFPMF 26 +#define RISCV_ISA_EXT_SSTC 27 +#define RISCV_ISA_EXT_SVINVAL 28 +#define RISCV_ISA_EXT_SVPBMT 29 +#define RISCV_ISA_EXT_ZBB 30 +#define RISCV_ISA_EXT_ZICBOM 31 +#define RISCV_ISA_EXT_ZIHINTPAUSE 32 +#define ANDES_ISA_EXT_DSP 63 +#define RISCV_ISA_EXT_MAX 64 +#define RISCV_ISA_EXT_NAME_LEN_MAX 32 /* * This enum represents the logical ID for each RISC-V ISA extension static From c61d97541f28ba846e5cac143571414662421344 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 1 Dec 2022 12:37:50 +0100 Subject: [PATCH 078/169] riscv: Support RISC-V Svnapot extension Dependency patches: e923f4625ed3 riscv: Apply a static assert to riscv_isa_ext_id 99e2266f2460 RISC-V: clarify ISA string ordering rules in cpu.c 80c200b34ee8 RISC-V: resort all extensions in consistent orders d8a3d8a75206 riscv: hwcap: make ISA extension ids can be used in asm Svnapot patches: 23ad288aaf15 riscv: mm: modify pte format for Svnapot 82a1a1f3bfb6 riscv: mm: support Svnapot in hugetlb page ce173474cf19 riscv: mm: support Svnapot in huge vmap Signed-off-by: Dylan Jhong --- arch/riscv/Kconfig | 21 +- arch/riscv/include/asm/hugetlb.h | 34 +++- arch/riscv/include/asm/hwcap.h | 6 + arch/riscv/include/asm/page.h | 5 - arch/riscv/include/asm/pgtable-64.h | 34 ++++ arch/riscv/include/asm/pgtable.h | 39 +++- arch/riscv/include/asm/vmalloc.h | 77 +++++++ arch/riscv/kernel/cpu.c | 54 +++-- arch/riscv/kernel/cpufeature.c | 7 +- arch/riscv/mm/hugetlbpage.c | 301 ++++++++++++++++++++++++++++ 10 files changed, 553 insertions(+), 25 deletions(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 1e51a8d266ac55..7cf18669b5fed9 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -44,7 +44,7 @@ config RISCV select ARCH_USE_QUEUED_RWLOCKS select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU select ARCH_WANT_FRAME_POINTERS - select ARCH_WANT_GENERAL_HUGETLB + select ARCH_WANT_GENERAL_HUGETLB if !RISCV_ISA_SVNAPOT select ARCH_WANT_HUGE_PMD_SHARE if 64BIT select ARCH_WANTS_THP_SWAP if HAVE_ARCH_TRANSPARENT_HUGEPAGE select BINFMT_FLAT_NO_DATA_START_OFFSET if !MMU @@ -392,6 +392,25 @@ config RISCV_ISA_C If you don't know what to do here, say Y. +config RISCV_ISA_SVNAPOT + bool "SVNAPOT extension support" + depends on 64BIT && MMU + default y + select RISCV_ALTERNATIVE + help + Allow kernel to detect the SVNAPOT ISA-extension dynamically at boot + time and enable its usage. + + The SVNAPOT extension is used to mark contiguous PTEs as a range + of contiguous virtual-to-physical translations for a naturally + aligned power-of-2 (NAPOT) granularity larger than the base 4KB page + size. When HUGETLBFS is also selected this option unconditionally + allocates some memory for each NAPOT page size supported by the kernel. + When optimizing for low memory consumption and for platforms without + the SVNAPOT extension, it may be better to say N here. + + If you don't know what to do here, say Y. + config RISCV_ISA_SVPBMT bool "SVPBMT extension support" depends on 64BIT && MMU diff --git a/arch/riscv/include/asm/hugetlb.h b/arch/riscv/include/asm/hugetlb.h index ec19d6afc89653..fe6f2300664162 100644 --- a/arch/riscv/include/asm/hugetlb.h +++ b/arch/riscv/include/asm/hugetlb.h @@ -2,7 +2,6 @@ #ifndef _ASM_RISCV_HUGETLB_H #define _ASM_RISCV_HUGETLB_H -#include #include static inline void arch_clear_hugepage_flags(struct page *page) @@ -11,4 +10,37 @@ static inline void arch_clear_hugepage_flags(struct page *page) } #define arch_clear_hugepage_flags arch_clear_hugepage_flags +#ifdef CONFIG_RISCV_ISA_SVNAPOT +#define __HAVE_ARCH_HUGE_PTE_CLEAR +void huge_pte_clear(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, unsigned long sz); + +#define __HAVE_ARCH_HUGE_SET_HUGE_PTE_AT +void set_huge_pte_at(struct mm_struct *mm, + unsigned long addr, pte_t *ptep, pte_t pte); + +#define __HAVE_ARCH_HUGE_PTEP_GET_AND_CLEAR +pte_t huge_ptep_get_and_clear(struct mm_struct *mm, + unsigned long addr, pte_t *ptep); + +#define __HAVE_ARCH_HUGE_PTEP_CLEAR_FLUSH +pte_t huge_ptep_clear_flush(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep); + +#define __HAVE_ARCH_HUGE_PTEP_SET_WRPROTECT +void huge_ptep_set_wrprotect(struct mm_struct *mm, + unsigned long addr, pte_t *ptep); + +#define __HAVE_ARCH_HUGE_PTEP_SET_ACCESS_FLAGS +int huge_ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, + pte_t pte, int dirty); + +pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags); +#define arch_make_huge_pte arch_make_huge_pte + +#endif /*CONFIG_RISCV_ISA_SVNAPOT*/ + +#include + #endif /* _ASM_RISCV_HUGETLB_H */ diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h index f78062dfe3f2c1..ef9c47f067218f 100644 --- a/arch/riscv/include/asm/hwcap.h +++ b/arch/riscv/include/asm/hwcap.h @@ -55,7 +55,9 @@ extern unsigned long elf_hwcap; #define RISCV_ISA_EXT_ZBB 30 #define RISCV_ISA_EXT_ZICBOM 31 #define RISCV_ISA_EXT_ZIHINTPAUSE 32 +#define RISCV_ISA_EXT_SVNAPOT 33 #define ANDES_ISA_EXT_DSP 63 + #define RISCV_ISA_EXT_MAX 64 #define RISCV_ISA_EXT_NAME_LEN_MAX 32 @@ -63,11 +65,13 @@ extern unsigned long elf_hwcap; * This enum represents the logical ID for each RISC-V ISA extension static * keys. We can use static key to optimize code path if some ISA extensions * are available. + * Entries are sorted alphabetically. */ enum riscv_isa_ext_key { RISCV_ISA_EXT_KEY_FPU, /* For 'F' and 'D' */ RISCV_ISA_EXT_KEY_ZIHINTPAUSE, RISCV_ISA_EXT_KEY_SVINVAL, + RISCV_ISA_EXT_KEY_SVNAPOT, ANDES_ISA_EXT_KEY_DSP, RISCV_ISA_EXT_KEY_MAX, }; @@ -92,6 +96,8 @@ static __always_inline int riscv_isa_ext2key(int num) return RISCV_ISA_EXT_KEY_ZIHINTPAUSE; case RISCV_ISA_EXT_SVINVAL: return RISCV_ISA_EXT_KEY_SVINVAL; + case RISCV_ISA_EXT_SVNAPOT: + return RISCV_ISA_EXT_KEY_SVNAPOT; case ANDES_ISA_EXT_DSP: return ANDES_ISA_EXT_KEY_DSP; default: diff --git a/arch/riscv/include/asm/page.h b/arch/riscv/include/asm/page.h index 86048c60f7002b..2a7d8312cc2e96 100644 --- a/arch/riscv/include/asm/page.h +++ b/arch/riscv/include/asm/page.h @@ -16,11 +16,6 @@ #define PAGE_SIZE (_AC(1, UL) << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE - 1)) -#ifdef CONFIG_64BIT -#define HUGE_MAX_HSTATE 2 -#else -#define HUGE_MAX_HSTATE 1 -#endif #define HPAGE_SHIFT PMD_SHIFT #define HPAGE_SIZE (_AC(1, UL) << HPAGE_SHIFT) #define HPAGE_MASK (~(HPAGE_SIZE - 1)) diff --git a/arch/riscv/include/asm/pgtable-64.h b/arch/riscv/include/asm/pgtable-64.h index 496f1fdb86d73b..b794725b71c99b 100644 --- a/arch/riscv/include/asm/pgtable-64.h +++ b/arch/riscv/include/asm/pgtable-64.h @@ -78,6 +78,40 @@ typedef struct { */ #define _PAGE_PFN_MASK GENMASK(53, 10) +/* + * [63] Svnapot definitions: + * 0 Svnapot disabled + * 1 Svnapot enabled + */ +#define _PAGE_NAPOT_SHIFT 63 +#define _PAGE_NAPOT BIT(_PAGE_NAPOT_SHIFT) +/* + * Only 64KB (order 4) napot ptes supported. + */ +#define NAPOT_CONT_ORDER_BASE 4 +enum napot_cont_order { + NAPOT_CONT64KB_ORDER = NAPOT_CONT_ORDER_BASE, + NAPOT_ORDER_MAX, +}; + +#define for_each_napot_order(order) \ + for (order = NAPOT_CONT_ORDER_BASE; order < NAPOT_ORDER_MAX; order++) +#define for_each_napot_order_rev(order) \ + for (order = NAPOT_ORDER_MAX - 1; \ + order >= NAPOT_CONT_ORDER_BASE; order--) +#define napot_cont_order(val) (__builtin_ctzl((val.pte >> _PAGE_PFN_SHIFT) << 1)) + +#define napot_cont_shift(order) ((order) + PAGE_SHIFT) +#define napot_cont_size(order) BIT(napot_cont_shift(order)) +#define napot_cont_mask(order) (~(napot_cont_size(order) - 1UL)) +#define napot_pte_num(order) BIT(order) + +#ifdef CONFIG_RISCV_ISA_SVNAPOT +#define HUGE_MAX_HSTATE (2 + (NAPOT_ORDER_MAX - NAPOT_CONT_ORDER_BASE)) +#else +#define HUGE_MAX_HSTATE 2 +#endif + /* * [62:61] Svpbmt Memory Type definitions: * diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 404cd17bb197c9..3afd20781e8505 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -276,10 +276,47 @@ static inline pte_t pud_pte(pud_t pud) return __pte(pud_val(pud)); } +#ifdef CONFIG_RISCV_ISA_SVNAPOT + +static __always_inline bool has_svnapot(void) +{ + return static_branch_likely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_SVNAPOT]); +} + +static inline unsigned long pte_napot(pte_t pte) +{ + return pte_val(pte) & _PAGE_NAPOT; +} + +static inline pte_t pte_mknapot(pte_t pte, unsigned int order) +{ + int pos = order - 1 + _PAGE_PFN_SHIFT; + unsigned long napot_bit = BIT(pos); + unsigned long napot_mask = ~GENMASK(pos, _PAGE_PFN_SHIFT); + + return __pte((pte_val(pte) & napot_mask) | napot_bit | _PAGE_NAPOT); +} + +#else + +static __always_inline bool has_svnapot(void) { return false; } + +static inline unsigned long pte_napot(pte_t pte) +{ + return 0; +} + +#endif /* CONFIG_RISCV_ISA_SVNAPOT */ + /* Yields the page frame number (PFN) of a page table entry */ static inline unsigned long pte_pfn(pte_t pte) { - return __page_val_to_pfn(pte_val(pte)); + unsigned long res = __page_val_to_pfn(pte_val(pte)); + + if (has_svnapot() && pte_napot(pte)) + res = res & (res - 1UL); + + return res; } #define pte_page(x) pfn_to_page(pte_pfn(x)) diff --git a/arch/riscv/include/asm/vmalloc.h b/arch/riscv/include/asm/vmalloc.h index ff9abc00d1394f..58d3e447f191cb 100644 --- a/arch/riscv/include/asm/vmalloc.h +++ b/arch/riscv/include/asm/vmalloc.h @@ -1,4 +1,81 @@ #ifndef _ASM_RISCV_VMALLOC_H #define _ASM_RISCV_VMALLOC_H +#ifdef CONFIG_HAVE_ARCH_HUGE_VMAP + +#define IOREMAP_MAX_ORDER (PUD_SHIFT) + +#define arch_vmap_pud_supported arch_vmap_pud_supported +static inline bool arch_vmap_pud_supported(pgprot_t prot) +{ + return true; +} + +#define arch_vmap_pmd_supported arch_vmap_pmd_supported +static inline bool arch_vmap_pmd_supported(pgprot_t prot) +{ + return true; +} + +#ifdef CONFIG_RISCV_ISA_SVNAPOT +#include + +#define arch_vmap_pte_range_map_size arch_vmap_pte_range_map_size +static inline unsigned long arch_vmap_pte_range_map_size(unsigned long addr, unsigned long end, + u64 pfn, unsigned int max_page_shift) +{ + unsigned long map_size = PAGE_SIZE; + unsigned long size, order; + + if (!has_svnapot()) + return map_size; + + for_each_napot_order_rev(order) { + if (napot_cont_shift(order) > max_page_shift) + continue; + + size = napot_cont_size(order); + if (end - addr < size) + continue; + + if (!IS_ALIGNED(addr, size)) + continue; + + if (!IS_ALIGNED(PFN_PHYS(pfn), size)) + continue; + + map_size = size; + break; + } + + return map_size; +} + +#define arch_vmap_pte_supported_shift arch_vmap_pte_supported_shift +static inline int arch_vmap_pte_supported_shift(unsigned long size) +{ + int shift = PAGE_SHIFT; + unsigned long order; + + if (!has_svnapot()) + return shift; + + WARN_ON_ONCE(size >= PMD_SIZE); + + for_each_napot_order_rev(order) { + if (napot_cont_size(order) > size) + continue; + + if (!IS_ALIGNED(size, napot_cont_size(order))) + continue; + + shift = napot_cont_shift(order); + break; + } + + return shift; +} + +#endif /* CONFIG_RISCV_ISA_SVNAPOT */ +#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */ #endif /* _ASM_RISCV_VMALLOC_H */ diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c index ea60013702d94e..84cd9afba7a051 100644 --- a/arch/riscv/kernel/cpu.c +++ b/arch/riscv/kernel/cpu.c @@ -145,30 +145,54 @@ arch_initcall(riscv_cpuinfo_init); .uprop = #UPROP, \ .isa_ext_id = EXTID, \ } + /* - * Here are the ordering rules of extension naming defined by RISC-V - * specification : - * 1. All extensions should be separated from other multi-letter extensions - * by an underscore. - * 2. The first letter following the 'Z' conventionally indicates the most + * The canonical order of ISA extension names in the ISA string is defined in + * chapter 27 of the unprivileged specification. + * + * Ordinarily, for in-kernel data structures, this order is unimportant but + * isa_ext_arr defines the order of the ISA string in /proc/cpuinfo. + * + * The specification uses vague wording, such as should, when it comes to + * ordering, so for our purposes the following rules apply: + * + * 1. All multi-letter extensions must be separated from other extensions by an + * underscore. + * + * 2. Additional standard extensions (starting with 'Z') must be sorted after + * single-letter extensions and before any higher-privileged extensions. + + * 3. The first letter following the 'Z' conventionally indicates the most * closely related alphabetical extension category, IMAFDQLCBKJTPVH. - * If multiple 'Z' extensions are named, they should be ordered first - * by category, then alphabetically within a category. - * 3. Standard supervisor-level extensions (starts with 'S') should be - * listed after standard unprivileged extensions. If multiple - * supervisor-level extensions are listed, they should be ordered + * If multiple 'Z' extensions are named, they must be ordered first by + * category, then alphabetically within a category. + * + * 3. Standard supervisor-level extensions (starting with 'S') must be listed + * after standard unprivileged extensions. If multiple supervisor-level + * extensions are listed, they must be ordered alphabetically. + * + * 4. Standard machine-level extensions (starting with 'Zxm') must be listed + * after any lower-privileged, standard extensions. If multiple + * machine-level extensions are listed, they must be ordered * alphabetically. - * 4. Non-standard extensions (starts with 'X') must be listed after all - * standard extensions. They must be separated from other multi-letter - * extensions by an underscore. + * + * 5. Non-standard extensions (starting with 'X') must be listed after all + * standard extensions. If multiple non-standard extensions are listed, they + * must be ordered alphabetically. + * + * An example string following the order is: + * rv64imadc_zifoo_zigoo_zafoo_sbar_scar_zxmbaz_xqux_xrux + * + * New entries to this struct should follow the ordering rules described above. */ static struct riscv_isa_ext_data isa_ext_arr[] = { + __RISCV_ISA_EXT_DATA(zicbom, RISCV_ISA_EXT_ZICBOM), + __RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE), __RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF), __RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC), __RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL), + __RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT), __RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT), - __RISCV_ISA_EXT_DATA(zicbom, RISCV_ISA_EXT_ZICBOM), - __RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE), __RISCV_ISA_EXT_DATA("", RISCV_ISA_EXT_MAX), }; diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 740d4761a794d8..58bf81a5ef665c 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -200,12 +200,14 @@ void __init riscv_fill_hwcap(void) this_hwcap |= isa2hwcap[(unsigned char)(*ext)]; set_bit(*ext - 'a', this_isa); } else { + /* sorted alphabetically */ SET_ISA_EXT_MAP("sscofpmf", RISCV_ISA_EXT_SSCOFPMF); + SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC); + SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL); + SET_ISA_EXT_MAP("svnapot", RISCV_ISA_EXT_SVNAPOT); SET_ISA_EXT_MAP("svpbmt", RISCV_ISA_EXT_SVPBMT); SET_ISA_EXT_MAP("zicbom", RISCV_ISA_EXT_ZICBOM); SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE); - SET_ISA_EXT_MAP("sstc", RISCV_ISA_EXT_SSTC); - SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL); /* Andes vendor extensions: DSP */ SET_ISA_EXT_MAP("xdsp", ANDES_ISA_EXT_DSP); @@ -288,6 +290,7 @@ static bool __init_or_module cpufeature_probe_zicbom(unsigned int stage) * This code may also be executed before kernel relocation, so we cannot use * addresses generated by the address-of operator as they won't be valid in * this context. + * Tests, unless otherwise required, are to be added in alphabetical order. */ static u32 __init_or_module cpufeature_probe(unsigned int stage) { diff --git a/arch/riscv/mm/hugetlbpage.c b/arch/riscv/mm/hugetlbpage.c index 932dadfdca544e..a163a3e0f0d46b 100644 --- a/arch/riscv/mm/hugetlbpage.c +++ b/arch/riscv/mm/hugetlbpage.c @@ -2,6 +2,305 @@ #include #include +#ifdef CONFIG_RISCV_ISA_SVNAPOT +pte_t *huge_pte_alloc(struct mm_struct *mm, + struct vm_area_struct *vma, + unsigned long addr, + unsigned long sz) +{ + unsigned long order; + pte_t *pte = NULL; + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + + pgd = pgd_offset(mm, addr); + p4d = p4d_alloc(mm, pgd, addr); + if (!p4d) + return NULL; + + pud = pud_alloc(mm, p4d, addr); + if (!pud) + return NULL; + + if (sz == PUD_SIZE) { + pte = (pte_t *)pud; + goto out; + } + + if (sz == PMD_SIZE) { + if (want_pmd_share(vma, addr) && pud_none(*pud)) + pte = huge_pmd_share(mm, vma, addr, pud); + else + pte = (pte_t *)pmd_alloc(mm, pud, addr); + goto out; + } + + pmd = pmd_alloc(mm, pud, addr); + if (!pmd) + return NULL; + + for_each_napot_order(order) { + if (napot_cont_size(order) == sz) { + pte = pte_alloc_map(mm, pmd, addr & napot_cont_mask(order)); + break; + } + } + +out: + WARN_ON_ONCE(pte && pte_present(*pte) && !pte_huge(*pte)); + return pte; +} + +pte_t *huge_pte_offset(struct mm_struct *mm, + unsigned long addr, + unsigned long sz) +{ + unsigned long order; + pte_t *pte = NULL; + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + + pgd = pgd_offset(mm, addr); + if (!pgd_present(*pgd)) + return NULL; + + p4d = p4d_offset(pgd, addr); + if (!p4d_present(*p4d)) + return NULL; + + pud = pud_offset(p4d, addr); + if (sz == PUD_SIZE) + /* must be pud huge, non-present or none */ + return (pte_t *)pud; + + if (!pud_present(*pud)) + return NULL; + + pmd = pmd_offset(pud, addr); + if (sz == PMD_SIZE) + /* must be pmd huge, non-present or none */ + return (pte_t *)pmd; + + if (!pmd_present(*pmd)) + return NULL; + + for_each_napot_order(order) { + if (napot_cont_size(order) == sz) { + pte = pte_offset_kernel(pmd, addr & napot_cont_mask(order)); + break; + } + } + return pte; +} + +static pte_t get_clear_contig(struct mm_struct *mm, + unsigned long addr, + pte_t *ptep, + unsigned long pte_num) +{ + pte_t orig_pte = ptep_get(ptep); + unsigned long i; + + for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++) { + pte_t pte = ptep_get_and_clear(mm, addr, ptep); + + if (pte_dirty(pte)) + orig_pte = pte_mkdirty(orig_pte); + + if (pte_young(pte)) + orig_pte = pte_mkyoung(orig_pte); + } + + return orig_pte; +} + +static pte_t get_clear_contig_flush(struct mm_struct *mm, + unsigned long addr, + pte_t *ptep, + unsigned long pte_num) +{ + pte_t orig_pte = get_clear_contig(mm, addr, ptep, pte_num); + struct vm_area_struct vma = TLB_FLUSH_VMA(mm, 0); + bool valid = !pte_none(orig_pte); + + if (valid) + flush_tlb_range(&vma, addr, addr + (PAGE_SIZE * pte_num)); + + return orig_pte; +} + +pte_t arch_make_huge_pte(pte_t entry, unsigned int shift, vm_flags_t flags) +{ + unsigned long order; + + for_each_napot_order(order) { + if (shift == napot_cont_shift(order)) { + entry = pte_mknapot(entry, order); + break; + } + } + if (order == NAPOT_ORDER_MAX) + entry = pte_mkhuge(entry); + + return entry; +} + +void set_huge_pte_at(struct mm_struct *mm, + unsigned long addr, + pte_t *ptep, + pte_t pte) +{ + int i, pte_num; + + if (!pte_napot(pte)) { + set_pte_at(mm, addr, ptep, pte); + return; + } + + pte_num = napot_pte_num(napot_cont_order(pte)); + for (i = 0; i < pte_num; i++, ptep++, addr += PAGE_SIZE) + set_pte_at(mm, addr, ptep, pte); +} + +int huge_ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long addr, + pte_t *ptep, + pte_t pte, + int dirty) +{ + struct mm_struct *mm = vma->vm_mm; + unsigned long order; + pte_t orig_pte; + int i, pte_num; + + if (!pte_napot(pte)) + return ptep_set_access_flags(vma, addr, ptep, pte, dirty); + + order = napot_cont_order(pte); + pte_num = napot_pte_num(order); + ptep = huge_pte_offset(mm, addr, napot_cont_size(order)); + orig_pte = get_clear_contig_flush(mm, addr, ptep, pte_num); + + if (pte_dirty(orig_pte)) + pte = pte_mkdirty(pte); + + if (pte_young(orig_pte)) + pte = pte_mkyoung(pte); + + for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++) + set_pte_at(mm, addr, ptep, pte); + + return true; +} + +pte_t huge_ptep_get_and_clear(struct mm_struct *mm, + unsigned long addr, + pte_t *ptep) +{ + pte_t orig_pte = ptep_get(ptep); + int pte_num; + + if (!pte_napot(orig_pte)) + return ptep_get_and_clear(mm, addr, ptep); + + pte_num = napot_pte_num(napot_cont_order(orig_pte)); + + return get_clear_contig(mm, addr, ptep, pte_num); +} + +void huge_ptep_set_wrprotect(struct mm_struct *mm, + unsigned long addr, + pte_t *ptep) +{ + pte_t pte = ptep_get(ptep); + unsigned long order; + int i, pte_num; + + if (!pte_napot(pte)) { + ptep_set_wrprotect(mm, addr, ptep); + return; + } + + order = napot_cont_order(pte); + pte_num = napot_pte_num(order); + ptep = huge_pte_offset(mm, addr, napot_cont_size(order)); + + for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++) + ptep_set_wrprotect(mm, addr, ptep); +} + +pte_t huge_ptep_clear_flush(struct vm_area_struct *vma, + unsigned long addr, + pte_t *ptep) +{ + pte_t pte = ptep_get(ptep); + int pte_num; + + if (!pte_napot(pte)) + return ptep_clear_flush(vma, addr, ptep); + + pte_num = napot_pte_num(napot_cont_order(pte)); + + return get_clear_contig_flush(vma->vm_mm, addr, ptep, pte_num); +} + +void huge_pte_clear(struct mm_struct *mm, + unsigned long addr, + pte_t *ptep, + unsigned long sz) +{ + pte_t pte = READ_ONCE(*ptep); + int i, pte_num; + + if (!pte_napot(pte)) { + pte_clear(mm, addr, ptep); + return; + } + + pte_num = napot_pte_num(napot_cont_order(pte)); + for (i = 0; i < pte_num; i++, addr += PAGE_SIZE, ptep++) + pte_clear(mm, addr, ptep); +} + +static __init bool is_napot_size(unsigned long size) +{ + unsigned long order; + + if (!has_svnapot()) + return false; + + for_each_napot_order(order) { + if (size == napot_cont_size(order)) + return true; + } + return false; +} + +static __init int napot_hugetlbpages_init(void) +{ + if (has_svnapot()) { + unsigned long order; + + for_each_napot_order(order) + hugetlb_add_hstate(order); + } + return 0; +} +arch_initcall(napot_hugetlbpages_init); + +#else + +static __init bool is_napot_size(unsigned long size) +{ + return false; +} + +#endif /*CONFIG_RISCV_ISA_SVNAPOT*/ + int pud_huge(pud_t pud) { return pud_leaf(pud); @@ -18,6 +317,8 @@ bool __init arch_hugetlb_valid_size(unsigned long size) return true; else if (IS_ENABLED(CONFIG_64BIT) && size == PUD_SIZE) return true; + else if (is_napot_size(size)) + return true; else return false; } From 32f7ba7b821837596985bf197cb693907fda405e Mon Sep 17 00:00:00 2001 From: Dylan Jhong Date: Mon, 19 Jun 2023 15:15:02 +0800 Subject: [PATCH 079/169] posix_types.h: Add workaround fix for RV32 datatype "__kernel_off_t" Please note that this patch is only a temporary workaround and SHOULD be reverted once we find a real solution. This patch adjusts RV32's "__kernel__off_t" datatype so that the "fcntl" failed test cases in the RV32 LTP are able to passed. In order to solve the problem of y2038, the glibc upstream of RISC-V used 64-bit time_t and off_t for both RV32 and RV64[*1]. However, no corresponding modification was made on the kernel side, which caused the test cases of LTP fnctl to fail. So, when glibc passes the "struct flock" parameter to the kernel through fnctl(), it will cause an error due to inconsistent size. The strcuture of flock are shown as below: struct flock in glibc: ------------------------------------------------------ struct flock { short int l_type; short int l_whence; __off_t l_start; __off_t l_len; __off64_t l_start; __off64_t l_len; <------ "__off64_t" in glibc is 64bit __pid_t l_pid; }; ------------------------------------------------------ struct flock in kernel 6.1: ------------------------------------------------------ struct flock { short l_type; short l_whence; __kernel_off_t l_start; __kernel_off_t l_len; <----- "__kernel_off_t" in kernel is 32bit __kernel_pid_t l_pid; __ARCH_FLOCK_EXTRA_SYSID __ARCH_FLOCK_PAD }; ------------------------------------------------------ We temporarily modify the "__kernel_off_t" of RV32 to 64bit to comply with the current glibc calling convention. However, since the current modification is in "include/uapi/asm-generic/posix_types.h", it may affect other architectures, so we should recheck the impact of this patch in the future. The patch is cherry-picked from AST520 RISCV-Linux-5.4, authored by Randolph.[*2][*3] [*1]: https://patchwork.ozlabs.org/project/glibc/patch/4e95f95966d8d7c6a8339160dc62d81c1f6a1bfb.1578824547.git.alistair.francis@wdc.com/#2360267 [*2]: 91a838fcc2 riscv: add default kenenl macro into to posix_types.h to avoid leak [*3]: e7c71ee264 riscv: change __kernel_off_t type as __kernel_loff_t in RV32 default Signed-off-by: Dylan Jhong --- include/uapi/asm-generic/posix_types.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/include/uapi/asm-generic/posix_types.h b/include/uapi/asm-generic/posix_types.h index b5f7594eee7ab2..5551ee85b28c27 100644 --- a/include/uapi/asm-generic/posix_types.h +++ b/include/uapi/asm-generic/posix_types.h @@ -84,11 +84,20 @@ typedef struct { /* * anything below here should be completely generic */ -typedef __kernel_long_t __kernel_off_t; typedef long long __kernel_loff_t; typedef __kernel_long_t __kernel_old_time_t; #ifndef __KERNEL__ typedef __kernel_long_t __kernel_time_t; +typedef __kernel_long_t __kernel_off_t; +#else +/* + * This is a workaround for RV32 and "SHOULD" be reverted in the future + */ +#if !defined(CONFIG_ARCH_32BIT_OFF_T) && __BITS_PER_LONG != 64 +typedef __kernel_loff_t __kernel_off_t; +#else +typedef __kernel_long_t __kernel_off_t; +#endif #endif typedef long long __kernel_time64_t; typedef __kernel_long_t __kernel_clock_t; From d3eb0939697c29338bab2134b923f038999b47be Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Fri, 2 Jun 2023 08:23:27 +0800 Subject: [PATCH 080/169] riscv: andes: trigger_module: Support Andes trigger_module Reformed from the following patches on RISCV-Linux-5.4: - (953a98950e1b) RISC-V: support trigger module for single step - (5b65b55bdcc0) Bug 18635 - Limitation about trigger module for icount type - (c2d003ee060d) Use scontext/svalue match to fire trigger - (d74ad2ce523b) warning: ISO C90 forbids mixed declarations and code - (c3699a445dd2) Fix unknown CSR error by using upstream musl toolchain - (b4716ef9b379) andes: rename sbi calls of andes vendor extension TIF_SINGLESETP define number workaround : During the system call switch, the current context's thread flag uses the 'andi' instruction with _TIF_SYSCALL_WORK for a bitwise check. Therefore, if TIF_SINGLESTEP exceeds 12, it will cause a compile error. So, we swap the values of the new TIF_SINGLESETP and OOM's TIF_MEMDIE since TIF_MEMDIE doesn't judge in this case. Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/include/asm/ptrace.h | 3 ++ arch/riscv/include/asm/thread_info.h | 16 ++++++++-- arch/riscv/kernel/entry.S | 6 ++++ arch/riscv/kernel/ptrace.c | 44 +++++++++++++++++++++++++++- drivers/soc/andes/andes_sbi.c | 14 +++++++-- include/soc/andes/csr.h | 5 +++- include/soc/andes/sbi.h | 3 ++ include/soc/andes/trigger_module.h | 19 ++++++++++++ 8 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 include/soc/andes/trigger_module.h diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h index 6ecd461129d222..1a2a54c7c932d9 100644 --- a/arch/riscv/include/asm/ptrace.h +++ b/arch/riscv/include/asm/ptrace.h @@ -59,6 +59,9 @@ struct pt_regs { #define REG_FMT "%08lx" #endif +/* Andes use trigger module to implement the ptrace single step. */ +#define arch_has_single_step() (1) + #define user_mode(regs) (((regs)->status & SR_PP) == 0) #define MAX_REG_OFFSET offsetof(struct pt_regs, orig_a0) diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h index f704c8dd57e040..64530c10e04cf3 100644 --- a/arch/riscv/include/asm/thread_info.h +++ b/arch/riscv/include/asm/thread_info.h @@ -94,7 +94,7 @@ struct thread_info { #define TIF_SIGPENDING 2 /* signal pending */ #define TIF_NEED_RESCHED 3 /* rescheduling necessary */ #define TIF_RESTORE_SIGMASK 4 /* restore signal mask in do_signal() */ -#define TIF_MEMDIE 5 /* is terminating due to OOM killer */ +#define TIF_MEMDIE 12 /* is terminating due to OOM killer */ #define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */ #define TIF_SYSCALL_AUDIT 7 /* syscall auditing */ #define TIF_SECCOMP 8 /* syscall secure computing */ @@ -102,6 +102,17 @@ struct thread_info { #define TIF_UPROBE 10 /* uprobe breakpoint or singlestep */ #define TIF_32BIT 11 /* compat-mode 32bit process */ +/* + * Andes trigger module support ptrace single step + * + * During the system call switch, the current context's thread flag + * uses the 'andi' instruction with _TIF_SYSCALL_WORK for a bitwise check. + * Therefore, if TIF_SINGLESTEP exceeds 12, it will cause a compile error. + * So, we swap the values of the new TIF_SINGLESETP and OOM's TIF_MEMDIE + * since TIF_MEMDIE doesn't judge in this case. + */ +#define TIF_SINGLESTEP 5 + #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) @@ -111,6 +122,7 @@ struct thread_info { #define _TIF_SECCOMP (1 << TIF_SECCOMP) #define _TIF_NOTIFY_SIGNAL (1 << TIF_NOTIFY_SIGNAL) #define _TIF_UPROBE (1 << TIF_UPROBE) +#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP) #define _TIF_WORK_MASK \ (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED | \ @@ -118,6 +130,6 @@ struct thread_info { #define _TIF_SYSCALL_WORK \ (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_TRACEPOINT | _TIF_SYSCALL_AUDIT | \ - _TIF_SECCOMP) + _TIF_SECCOMP | _TIF_SINGLESTEP) #endif /* _ASM_RISCV_THREAD_INFO_H */ diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 3221a9e5f3724c..f5a3c3de94e216 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -272,6 +272,12 @@ resume_userspace: call user_enter_callable #endif +.global bypass_singlestep +bypass_singlestep: + j 1f + /* Set trigger module for single step */ + call do_singlestep +1: /* Save unwound kernel stack pointer in thread_info */ addi s0, sp, PT_SIZE_ON_STACK REG_S s0, TASK_TI_KERNEL_SP(tp) diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 2ae8280ae4759d..c9f4bbc34185db 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -213,6 +215,7 @@ unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n) void ptrace_disable(struct task_struct *child) { clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + user_disable_single_step(child); } long arch_ptrace(struct task_struct *child, long request, @@ -229,6 +232,42 @@ long arch_ptrace(struct task_struct *child, long request, return ret; } + +extern void bypass_singlestep(void); +static void modify_bypass_to_nop(void) +{ + unsigned int nop = CNOP; + + copy_to_kernel_nofault(&bypass_singlestep, &nop, CNOP_SIZE); + smp_mb(); + flush_icache_range(&bypass_singlestep, &bypass_singlestep + CNOP_SIZE); +} + +void do_singlestep(void) +{ + if (test_thread_flag(TIF_SINGLESTEP)) { + sbi_andes_set_trigger(TRIGGER_TYPE_ICOUNT, ICOUNT, 1); + csr_write(CSR_SCONTEXT, 1); + } else { + csr_write(CSR_SCONTEXT, 0); + } +} + +static bool is_bypass_singlestep = true; +void user_enable_single_step(struct task_struct *child) +{ + set_tsk_thread_flag(child, TIF_SINGLESTEP); + if (is_bypass_singlestep) { + modify_bypass_to_nop(); + is_bypass_singlestep = false; + } +} + +void user_disable_single_step(struct task_struct *child) +{ + clear_tsk_thread_flag(child, TIF_SINGLESTEP); +} + /* * Allows PTRACE_SYSCALL to work. These are called from entry.S in * {handle,ret_from}_syscall. @@ -258,10 +297,13 @@ __visible int do_syscall_trace_enter(struct pt_regs *regs) __visible void do_syscall_trace_exit(struct pt_regs *regs) { + int step; + audit_syscall_exit(regs); + step = test_thread_flag(TIF_SINGLESTEP); if (test_thread_flag(TIF_SYSCALL_TRACE)) - ptrace_report_syscall_exit(regs, 0); + ptrace_report_syscall_exit(regs, step); #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) diff --git a/drivers/soc/andes/andes_sbi.c b/drivers/soc/andes/andes_sbi.c index 5492fa88bf0d3c..52520c99e2d598 100644 --- a/drivers/soc/andes/andes_sbi.c +++ b/drivers/soc/andes/andes_sbi.c @@ -12,13 +12,13 @@ void sbi_andes_set_ppma(void *arg) size_t size = ((struct ppma_arg_t *)arg)->size; sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_SET_PPMA, - phys_addr, va_addr, size, 0, 0, 0); + phys_addr, va_addr, size, 0, 0, 0); } void sbi_andes_free_ppma(void *addr) { sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_FREE_PPMA, - (unsigned long)addr, 0, 0, 0, 0, 0); + (unsigned long)addr, 0, 0, 0, 0, 0); } long sbi_andes_probe_ppma(void) @@ -26,6 +26,14 @@ long sbi_andes_probe_ppma(void) struct sbiret ret; ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_PROBE_PPMA, - 0, 0, 0, 0, 0, 0); + 0, 0, 0, 0, 0, 0); return ret.value; } + +/* trigger module support debug application with gdbserver */ +void sbi_andes_set_trigger(unsigned int type, uintptr_t data, int enable) +{ + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_TRIGGER, + type, data, enable, 0, 0, 0); +} +EXPORT_SYMBOL(sbi_andes_set_trigger); diff --git a/include/soc/andes/csr.h b/include/soc/andes/csr.h index 3907c76d033428..e86859befbdda7 100644 --- a/include/soc/andes/csr.h +++ b/include/soc/andes/csr.h @@ -120,7 +120,10 @@ extern u32 CCTL_L2_STATUS_PER_CORE_OFFSET; #define L2C_REG_CN_HPM_OFFSET(n) \ (L2C_REG_C0_HPM_OFFSET + (n * L2C_HPM_PER_CORE_OFFSET)) -/* Debug/Trace Registers (shared with Debug Mode) */ +/* + * Debug/Trace Registers (shared with Debug Mode). + * Andes trigger module support ptrace single step. + */ #define CSR_SCONTEXT 0x7aa /* Supervisor trap registers */ diff --git a/include/soc/andes/sbi.h b/include/soc/andes/sbi.h index 05ac45cc78bd96..47a51347e6a48e 100644 --- a/include/soc/andes/sbi.h +++ b/include/soc/andes/sbi.h @@ -59,4 +59,7 @@ void sbi_andes_set_ppma(void *arg); void sbi_andes_free_ppma(void *addr); long sbi_andes_probe_ppma(void); +/* Trigger module support debug application with gdbserver */ +void sbi_andes_set_trigger(unsigned int type, uintptr_t data, int enable); + #endif /* !__SOC_ANDES_SBI_H */ diff --git a/include/soc/andes/trigger_module.h b/include/soc/andes/trigger_module.h new file mode 100644 index 00000000000000..db98dce06b0813 --- /dev/null +++ b/include/soc/andes/trigger_module.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#ifndef __SOC_ANDES_TRIGGER_MODULE_H +#define __SOC_ANDES_TRIGGER_MODULE_H + +#include + +#define CNOP 0x0001 +#define CNOP_SIZE 0x2 +#define TRIGGER_TYPE_ICOUNT 3 +#define ICOUNT 1 + +extern void bypass_singlestep(void); +void do_singlestep(void); + +#endif /* !__SOC_ANDES_TRIGGER_MODULE_H */ From bc20b3d9ef3ebba9516a1d655b942d6ffa96018f Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Fri, 30 Jun 2023 14:27:01 +0800 Subject: [PATCH 081/169] riscv: dts: andes: Set the full set dts for each core Signed-off-by: Charles Ci-Jyun Wu --- .../dts/andes/a25_c1_d_dsp_noncoherent_ae350.dts | 13 ++++++++----- .../riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts | 8 ++++++++ .../andes/a27l2_c1_d_dsp_noncoherent_ae350.dts | 15 +++++++++------ .../riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts | 8 ++++++++ .../dts/andes/ax25_c1_d_dsp_noncoherent_ae350.dts | 13 ++++++++----- .../boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts | 8 ++++++++ .../andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts | 15 +++++++++------ .../boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts | 8 ++++++++ arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts | 8 ++++++++ .../boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts | 8 ++++++++ 10 files changed, 82 insertions(+), 22 deletions(-) diff --git a/arch/riscv/boot/dts/andes/a25_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/a25_c1_d_dsp_noncoherent_ae350.dts index e6b60db8fae2d2..9c231ad30cbbe1 100644 --- a/arch/riscv/boot/dts/andes/a25_c1_d_dsp_noncoherent_ae350.dts +++ b/arch/riscv/boot/dts/andes/a25_c1_d_dsp_noncoherent_ae350.dts @@ -91,7 +91,7 @@ compatible = "fixed-clock"; #clock-cells = <0x00>; clock-frequency = <0x5f5e100>; - phandle = <0x03>; + phandle = <0x04>; }; timer@f0400000 { @@ -167,22 +167,25 @@ }; mmc@f0e00000 { - compatible = "andestech,atfsdc010"; + compatible = "andestech,atfsdc010g"; reg = <0x00 0xf0e00000 0x00 0x1000>; interrupts = <0x12 0x04>; interrupt-parent = <0x02>; clock-freq-min-max = <0x61a80 0x5f5e100>; max-frequency = <0x5f5e100>; fifo-depth = <0x10>; - cap-sd-highspeed; + dma-names = "rxtx"; + dmas = <0x03 0x09>; }; dma@f0c00000 { - compatible = "andestech,atcdmac300"; + compatible = "andestech,atcdmac300g"; reg = <0x00 0xf0c00000 0x00 0x1000>; - interrupts = <0x0a 0x04 0x40 0x04 0x41 0x04 0x42 0x04 0x43 0x04 0x44 0x04 0x45 0x04 0x46 0x04 0x47 0x04>; + interrupts = <0x0a 0x04>; interrupt-parent = <0x02>; dma-channels = <0x08>; + #dma-cells = <0x01>; + phandle = <0x03>; }; lcd@e0200000 { diff --git a/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts index 49a56d0eb00f7a..83c97202ec750f 100644 --- a/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts @@ -245,6 +245,14 @@ wakeup-source; }; + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + mac@e0100000 { compatible = "andestech,atmac100"; reg = <0x00 0xe0100000 0x00 0x1000>; diff --git a/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts index fbe1021235f188..a60738c1e43dbc 100644 --- a/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts +++ b/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts @@ -180,22 +180,25 @@ }; mmc@f0e00000 { - compatible = "andestech,atfsdc010"; + compatible = "andestech,atfsdc010g"; reg = <0x00 0xf0e00000 0x00 0x1000>; interrupts = <0x12 0x04>; - interrupt-parent = <0x03>; + interrupt-parent = <0x02>; clock-freq-min-max = <0x61a80 0x5f5e100>; max-frequency = <0x5f5e100>; fifo-depth = <0x10>; - cap-sd-highspeed; + dma-names = "rxtx"; + dmas = <0x03 0x09>; }; dma@f0c00000 { - compatible = "andestech,atcdmac300"; + compatible = "andestech,atcdmac300g"; reg = <0x00 0xf0c00000 0x00 0x1000>; - interrupts = <0x0a 0x04 0x40 0x04 0x41 0x04 0x42 0x04 0x43 0x04 0x44 0x04 0x45 0x04 0x46 0x04 0x47 0x04>; - interrupt-parent = <0x03>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x02>; dma-channels = <0x08>; + #dma-cells = <0x01>; + phandle = <0x05>; }; lcd@e0200000 { diff --git a/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts index f4db227ca36f48..689c633b9026ad 100644 --- a/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts @@ -245,6 +245,14 @@ wakeup-source; }; + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + mac@e0100000 { compatible = "andestech,atmac100"; reg = <0x00 0xe0100000 0x00 0x1000>; diff --git a/arch/riscv/boot/dts/andes/ax25_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/ax25_c1_d_dsp_noncoherent_ae350.dts index 2b9a934f27afbd..7d31312b735a48 100644 --- a/arch/riscv/boot/dts/andes/ax25_c1_d_dsp_noncoherent_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax25_c1_d_dsp_noncoherent_ae350.dts @@ -91,7 +91,7 @@ compatible = "fixed-clock"; #clock-cells = <0x00>; clock-frequency = <0x5f5e100>; - phandle = <0x03>; + phandle = <0x04>; }; timer@f0400000 { @@ -167,22 +167,25 @@ }; mmc@f0e00000 { - compatible = "andestech,atfsdc010"; + compatible = "andestech,atfsdc010g"; reg = <0x00 0xf0e00000 0x00 0x1000>; interrupts = <0x12 0x04>; interrupt-parent = <0x02>; clock-freq-min-max = <0x61a80 0x5f5e100>; max-frequency = <0x5f5e100>; fifo-depth = <0x10>; - cap-sd-highspeed; + dma-names = "rxtx"; + dmas = <0x03 0x09>; }; dma@f0c00000 { - compatible = "andestech,atcdmac300"; + compatible = "andestech,atcdmac300g"; reg = <0x00 0xf0c00000 0x00 0x1000>; - interrupts = <0x0a 0x04 0x40 0x04 0x41 0x04 0x42 0x04 0x43 0x04 0x44 0x04 0x45 0x04 0x46 0x04 0x47 0x04>; + interrupts = <0x0a 0x04>; interrupt-parent = <0x02>; dma-channels = <0x08>; + #dma-cells = <0x01>; + phandle = <0x03>; }; lcd@e0200000 { diff --git a/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts index c2a1e319b5eadb..1a0f9bd6ebfbe0 100644 --- a/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts @@ -245,6 +245,14 @@ wakeup-source; }; + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + mac@e0100000 { compatible = "andestech,atmac100"; reg = <0x00 0xe0100000 0x00 0x1000>; diff --git a/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts index 69fca2a7236a52..f72ebc972366b3 100644 --- a/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts @@ -180,22 +180,25 @@ }; mmc@f0e00000 { - compatible = "andestech,atfsdc010"; + compatible = "andestech,atfsdc010g"; reg = <0x00 0xf0e00000 0x00 0x1000>; interrupts = <0x12 0x04>; - interrupt-parent = <0x03>; + interrupt-parent = <0x02>; clock-freq-min-max = <0x61a80 0x5f5e100>; max-frequency = <0x5f5e100>; fifo-depth = <0x10>; - cap-sd-highspeed; + dma-names = "rxtx"; + dmas = <0x03 0x09>; }; dma@f0c00000 { - compatible = "andestech,atcdmac300"; + compatible = "andestech,atcdmac300g"; reg = <0x00 0xf0c00000 0x00 0x1000>; - interrupts = <0x0a 0x04 0x40 0x04 0x41 0x04 0x42 0x04 0x43 0x04 0x44 0x04 0x45 0x04 0x46 0x04 0x47 0x04>; - interrupt-parent = <0x03>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x02>; dma-channels = <0x08>; + #dma-cells = <0x01>; + phandle = <0x05>; }; lcd@e0200000 { diff --git a/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts index 23e97b8d22ae50..96d7daa8c90fb1 100644 --- a/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts @@ -245,6 +245,14 @@ wakeup-source; }; + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + mac@e0100000 { compatible = "andestech,atmac100"; reg = <0x00 0xe0100000 0x00 0x1000>; diff --git a/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts b/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts index 3e4273b3786ddc..a7db6e1810afe5 100644 --- a/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts @@ -357,6 +357,14 @@ wakeup-source; }; + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + mac@e0100000 { compatible = "andestech,atmac100"; reg = <0x00 0xe0100000 0x00 0x1000>; diff --git a/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts index a5097ac7010879..1e3c6da3403599 100644 --- a/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts @@ -192,6 +192,14 @@ wakeup-source; }; + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + mac@e0100000 { compatible = "andestech,atmac100"; reg = <0x00 0xe0100000 0x00 0x1000>; From 127886ae621c44ba653c43f86543a3d091498e80 Mon Sep 17 00:00:00 2001 From: Alan Kao Date: Thu, 22 Oct 2020 09:53:30 +0800 Subject: [PATCH 082/169] riscv: andes: Support Andes supervisor detailed trap cause (sdcause) Reformed from the following patches on RISCV-Linux-5.4: - (edf0bb058123) Support sdcause - (7ed459de81c9) Fix sdcause read using wrong register Signed-off-by: Alan Kao --- arch/riscv/include/asm/ptrace.h | 3 +++ arch/riscv/kernel/asm-offsets.c | 1 + arch/riscv/kernel/entry.S | 4 ++++ arch/riscv/kernel/process.c | 4 ++-- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h index 1a2a54c7c932d9..fc526aaa23a725 100644 --- a/arch/riscv/include/asm/ptrace.h +++ b/arch/riscv/include/asm/ptrace.h @@ -49,6 +49,9 @@ struct pt_regs { unsigned long status; unsigned long badaddr; unsigned long cause; + + /* Andes supervisor detailed trap cause */ + unsigned long sdcause; /* a0 value before the syscall */ unsigned long orig_a0; }; diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index df9444397908d3..65042c283b40da 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -113,6 +113,7 @@ void asm_offsets(void) OFFSET(PT_STATUS, pt_regs, status); OFFSET(PT_BADADDR, pt_regs, badaddr); OFFSET(PT_CAUSE, pt_regs, cause); + OFFSET(PT_SDCAUSE, pt_regs, sdcause); OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index f5a3c3de94e216..6662a39d13de09 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -14,6 +14,8 @@ #include #include +#include + #if !IS_ENABLED(CONFIG_PREEMPTION) .set resume_kernel, restore_all #endif @@ -88,12 +90,14 @@ _save_context: csrr s3, CSR_TVAL csrr s4, CSR_CAUSE csrr s5, CSR_SCRATCH + csrr s6, CSR_SDCAUSE REG_S s0, PT_SP(sp) REG_S s1, PT_STATUS(sp) REG_S s2, PT_EPC(sp) REG_S s3, PT_BADADDR(sp) REG_S s4, PT_CAUSE(sp) REG_S s5, PT_TP(sp) + REG_S s6, PT_SDCAUSE(sp) /* * Set the scratch register to 0, so that if a recursive exception diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index edd7a2571e01bd..dffe2b3796702e 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -74,8 +74,8 @@ void __show_regs(struct pt_regs *regs) pr_cont(" t5 : " REG_FMT " t6 : " REG_FMT "\n", regs->t5, regs->t6); - pr_cont("status: " REG_FMT " badaddr: " REG_FMT " cause: " REG_FMT "\n", - regs->status, regs->badaddr, regs->cause); + pr_cont("status: " REG_FMT " badaddr: " REG_FMT " cause: " REG_FMT " sdcause: " REG_FMT "\n", + regs->status, regs->badaddr, regs->cause, regs->sdcause); } void show_regs(struct pt_regs *regs) { From cbdeb33ea4fb9fad11c55a8f98b95036f0d77e6f Mon Sep 17 00:00:00 2001 From: Randolph Date: Mon, 19 Jun 2023 15:28:58 +0800 Subject: [PATCH 083/169] riscv: amp: andes: add andes_remoteproc module andes_remoteproc module utilizes remote processor framework and interacts with the OpenAMP application to support ax25mp(dual core) + ax25(UP) AMP architecture. Reformed from the following patches on RISCV-Linux-5.4: - (7ee425cb1a) OpenAMP demo: close dir to prevent reopen failure (#34) - (5d26bcc3c7) dts: andes: openamp: Fix pause in use of reserved memory - (9312b3b664) riscv: amp: append CFLAGS - (13dfc42e1d) riscv: amp: fix typo - (e13e38939b) riscv: amp: fix warnings when compiling amp demos - (d0b633c0e8) riscv: app: add andes_remoteproc AMP module user applications - (0f3f718fa0) riscv: amp: add slave port support for AMP (ax25mp + ax25) - (5dd08ffa84) riscv: amp: add andes_remoteproc module and ax25mp AMP DTS and defconfig - (7e67617db4) riscv: amp: fix rpmsg driver override kfree static memory - (145a47e178) riscv: amp: back porting linux-5.13 rpmsg to support rpmsg_char control device - (b576157381) riscv: amp: back porting linux-5.8 remoteproc elf helpers to support remoteproc ELF64 loader Signed-off-by: Randolph Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Makefile | 2 +- arch/riscv/configs/amp.config | 2 + drivers/remoteproc/Kconfig | 11 + drivers/remoteproc/Makefile | 1 + drivers/remoteproc/andes_remoteproc.c | 463 ++++++++++++++++++ drivers/remoteproc/remoteproc_internal.h | 1 + samples/andes_remoteproc/App/Makefile | 29 ++ samples/andes_remoteproc/App/build_all.sh | 15 + samples/andes_remoteproc/App/echo_test.c | 189 +++++++ samples/andes_remoteproc/App/mat_mul_demo.c | 172 +++++++ samples/andes_remoteproc/App/proxy_app.c | 388 +++++++++++++++ samples/andes_remoteproc/App/proxy_app.h | 33 ++ .../andes_remoteproc/App/rpmsg_demo_ctrl.c | 133 +++++ .../andes_remoteproc/App/rpmsg_demo_ctrl.h | 33 ++ .../andes_remoteproc/Script/load_fw_echo.sh | 32 ++ .../Script/load_fw_mat_mul.sh | 32 ++ .../Script/load_fw_proxy_app.sh | 31 ++ 17 files changed, 1566 insertions(+), 1 deletion(-) create mode 100644 drivers/remoteproc/andes_remoteproc.c create mode 100644 samples/andes_remoteproc/App/Makefile create mode 100755 samples/andes_remoteproc/App/build_all.sh create mode 100644 samples/andes_remoteproc/App/echo_test.c create mode 100644 samples/andes_remoteproc/App/mat_mul_demo.c create mode 100644 samples/andes_remoteproc/App/proxy_app.c create mode 100644 samples/andes_remoteproc/App/proxy_app.h create mode 100644 samples/andes_remoteproc/App/rpmsg_demo_ctrl.c create mode 100644 samples/andes_remoteproc/App/rpmsg_demo_ctrl.h create mode 100755 samples/andes_remoteproc/Script/load_fw_echo.sh create mode 100755 samples/andes_remoteproc/Script/load_fw_mat_mul.sh create mode 100755 samples/andes_remoteproc/Script/load_fw_proxy_app.sh diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 8d647d89741e75..b9e17ba9976f9f 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -217,4 +217,4 @@ ae350_rv64_smp_sv48_defconfig: PHONY += ae350_rv64_ax25_amp_defconfig ae350_rv64_ax25_amp_defconfig: - $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config amp.config sv39.config + $(Q)$(MAKE) -f $(srctree)/Makefile andes_defconfig andes-support.config 64-bit.config smp.config amp.config sv39.config diff --git a/arch/riscv/configs/amp.config b/arch/riscv/configs/amp.config index 513e8d24074b29..41ff91faefc6e9 100644 --- a/arch/riscv/configs/amp.config +++ b/arch/riscv/configs/amp.config @@ -5,6 +5,8 @@ CONFIG_UIO=y CONFIG_UIO_PDRV_GENIRQ=y CONFIG_REMOTEPROC=y +CONFIG_REMOTEPROC_CDEV=y CONFIG_ANDES_REMOTEPROC=m CONFIG_RPMSG_CHAR=m +CONFIG_RPMSG_CTRL=m CONFIG_RPMSG_VIRTIO=m diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 1660197866531d..6a0b1d7bb15846 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -352,6 +352,17 @@ config TI_K3_R5_REMOTEPROC It's safe to say N here if you're not interested in utilizing a slave processor. +config ANDES_REMOTEPROC + tristate "Andes remoteproc support" + depends on REMOTEPROC + help + Say M here to support Andes remote processor subsystems + on various AE350 family of SoCs through the remote processor + framework. + + It's safe to say N here if you're not interested in utilizing + a slave processor. + endif # REMOTEPROC endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 5478c7cb9e07b8..c5c39487d6c9c6 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o +obj-$(CONFIG_ANDES_REMOTEPROC) += andes_remoteproc.o diff --git a/drivers/remoteproc/andes_remoteproc.c b/drivers/remoteproc/andes_remoteproc.c new file mode 100644 index 00000000000000..c4d07fed8536a0 --- /dev/null +++ b/drivers/remoteproc/andes_remoteproc.c @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "remoteproc_internal.h" +#include "remoteproc_elf_helpers.h" + +extern struct atcsmu atcsmu; + +#define DRV_NAME "andes_rproc_mbox" +#define MAX_NUM_VRINGS 2 +#define MBOX_SRC_OFF 0x80 +#define MBOX_SET (MBOX_SRC_OFF + 0x0) +#define MBOX_CLR (MBOX_SRC_OFF + 0x4) +#define MBOX_MASK 0x0000000f +#define MBOX_TX_BIT 0x1 +#define MBOX_RX_BIT 0x4 + +#define MBOX_MSG 0x3fffa000 +#define MBOX_MSG_SIZE 0x4 +#define MBOX_SET_MSG -1 +#define MBOX_SP_RUN 0 + +#define SP_RESET_VEC_OFF 0x58 +#define RESET_N_OFF 0x44 +#define SP_CORE_BIT 0x4 + +struct andes_rproc_pdata { + struct device *dev; + struct rproc *rproc; + int num_slave_ports; + u64 mp_slave_port[2]; + u64 sp_local_mem[2]; + int irq; + void __iomem *mbox_msg; +}; + +static void andes_rproc_kick(struct rproc *rproc, int vqid) +{ + struct device *dev = rproc->dev.parent; + unsigned long mask = 0; + + dev_dbg(dev, "KICK Firmware to start send messages vqid %d\n", vqid); + mask = MBOX_TX_BIT & MBOX_MASK; + writel(mask, atcsmu.base + MBOX_SET); +} + +static irqreturn_t andes_remoteproc_interrupt(int irq, void *dev_id) +{ + struct rproc *rproc = dev_id; + unsigned int mask = 0; + + dev_dbg(rproc->dev.parent, "KICK Linux because of pending message\n"); + mask = MBOX_RX_BIT & MBOX_MASK; + writel(mask, atcsmu.base + MBOX_CLR); + return IRQ_WAKE_THREAD; +} + +static irqreturn_t handle_event(int irq, void *dev_id) +{ + struct rproc *rproc = dev_id; + + return rproc_vq_interrupt(rproc, 0); +} + +static int andes_rproc_start(struct rproc *rproc) +{ + struct andes_rproc_pdata *local = rproc->priv; + unsigned int reset; + + /* + * MP set ELF entry point to the SP reset vector. + */ + writel(rproc->bootaddr, atcsmu.base + SP_RESET_VEC_OFF); + writel(MBOX_SET_MSG, local->mbox_msg); + + /* + * SP state must poweroff, before it poweron. + * SP set reset_n to 0, sp poweroff. + */ + reset = 0; + reset = readl(atcsmu.base + RESET_N_OFF); + if (reset & SP_CORE_BIT) { + reset = reset ^ SP_CORE_BIT; + writel(reset, atcsmu.base + RESET_N_OFF); + } + + /* + * SP set reset_n to 1, sp poweron + */ + reset = reset | SP_CORE_BIT; + writel(reset, atcsmu.base + RESET_N_OFF); + + return 0; +} + +static int andes_rproc_stop(struct rproc *rproc) +{ + struct andes_rproc_pdata *local = rproc->priv; + unsigned int msg; + + msg = readl(local->mbox_msg); + if (msg == MBOX_SP_RUN) { + writel(MBOX_SET_MSG, local->mbox_msg); + andes_rproc_kick(rproc, 1); + } + return 0; +} + +static int andes_reserved_mem_release(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + memunmap(mem->va); + return 0; +} + +static int andes_reserved_mem_alloc(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + void *va; + + va = memremap(mem->dma, mem->len, MEMREMAP_WC); + if (IS_ERR_OR_NULL(va)) + return -ENOMEM; + mem->va = va; + memset(mem->va, 0, mem->len); + return 0; +} + +static int andes_parse_reserved_mems(struct rproc *rproc) +{ + int i, num_mems; + struct device *dev = rproc->dev.parent; + struct device_node *mem_nodes = dev->of_node; + struct rproc_mem_entry *mem; + + num_mems = of_count_phandle_with_args(mem_nodes, "memory-region", NULL); + if (num_mems <= 0) { + dev_err(dev, "need reserved-memory to shared memory\n"); + return -EINVAL; + } + + for (i = 0; i < num_mems; i++) { + struct device_node *dt_node; + struct reserved_mem *rmem; + + dt_node = of_parse_phandle(mem_nodes, "memory-region", i); + + rmem = of_reserved_mem_lookup(dt_node); + if (!rmem) { + dev_err(dev, "unable to acquire memory-region\n"); + return -EINVAL; + } + + mem = rproc_mem_entry_init(dev, NULL, + (dma_addr_t)rmem->base, + rmem->size, rmem->base, + andes_reserved_mem_alloc, + andes_reserved_mem_release, + dt_node->name); + if (!mem) { + dev_err(dev, "unable to initialize memory-region %s\n", dt_node->name); + return -ENOMEM; + } + rproc_add_carveout(rproc, mem); + } + return 0; +} + +static int andes_slave_port_release(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + memunmap(mem->va); + return 0; +} + +static int andes_slave_port_alloc(struct rproc *rproc, + struct rproc_mem_entry *mem) +{ + void *va; + + va = memremap(mem->da, mem->len, MEMREMAP_WC); + if (IS_ERR_OR_NULL(va)) + return -ENOMEM; + + /* Update memory entry va */ + mem->va = va; + memset(mem->va, 0, mem->len); + return 0; +} + +static int andes_parse_slave_ports(struct rproc *rproc) +{ + struct device *dev = rproc->dev.parent; + struct andes_rproc_pdata *local = rproc->priv; + struct device_node *slave_port_nodes = dev->of_node; + int i, num_slave_ports; + + num_slave_ports = of_count_phandle_with_args(slave_port_nodes, + "slave_ports", NULL); + if (num_slave_ports <= 0) { + dev_err(dev, "need slave port to touch sp ILM/DLM\n"); + return -EINVAL; + } + + local->num_slave_ports = num_slave_ports; + + for (i = 0; i < num_slave_ports; i++) { + struct resource rsc; + resource_size_t size; + struct device_node *dt_node; + struct rproc_mem_entry *mem; + int ret; + + dt_node = of_parse_phandle(slave_port_nodes, "slave_ports", i); + + if (!dt_node) + return -EINVAL; + if (of_device_is_available(dt_node)) { + ret = of_address_to_resource(dt_node, 0, &rsc); + if (ret < 0) + return ret; + size = resource_size(&rsc); + mem = rproc_mem_entry_init(dev, NULL, rsc.start, + (int)size, rsc.start, + andes_slave_port_alloc, + andes_slave_port_release, + rsc.name); + if (!mem) + return -ENOMEM; + + local->mp_slave_port[i] = rsc.start; + local->sp_local_mem[i] = size & rsc.start; + rproc_add_carveout(rproc, mem); + } + } + return 0; +} + +static int andes_parse_fw(struct rproc *rproc, const struct firmware *fw) +{ + int ret; + + ret = andes_parse_slave_ports(rproc); + if (ret) + return ret; + + ret = andes_parse_reserved_mems(rproc); + if (ret) + return ret; + + ret = rproc_elf_load_rsc_table(rproc, fw); + if (ret == -EINVAL) { + dev_info(&rproc->dev, "no resource table found.\n"); + ret = 0; + } + return ret; +} + +int andes_rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw) +{ + struct device *dev = &rproc->dev; + struct andes_rproc_pdata *local = rproc->priv; + const void *ehdr, *phdr; + int i, j, ret = 0; + u16 phnum; + const u8 *elf_data = fw->data; + u8 class = fw_elf_get_class(fw); + u32 elf_phdr_get_size = elf_size_of_phdr(class); + + ehdr = elf_data; + phnum = elf_hdr_get_e_phnum(class, ehdr); + phdr = elf_data + elf_hdr_get_e_phoff(class, ehdr); + + /* go through the available ELF segments */ + for (i = 0; i < phnum; i++, phdr += elf_phdr_get_size) { + /* + * u64 da = elf_phdr_get_p_paddr(class, phdr); + * sp don't need pa, it just use va. + */ + + u64 da = elf_phdr_get_p_vaddr(class, phdr); + u64 memsz = elf_phdr_get_p_memsz(class, phdr); + u64 filesz = elf_phdr_get_p_filesz(class, phdr); + u64 offset = elf_phdr_get_p_offset(class, phdr); + u32 type = elf_phdr_get_p_type(class, phdr); + void *ptr; + + if (type != PT_LOAD) + continue; + + dev_dbg(dev, "phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n", + type, da, memsz, filesz); + + if (filesz > memsz) { + dev_err(dev, "bad phdr filesz 0x%llx memsz 0x%llx\n", + filesz, memsz); + ret = -EINVAL; + break; + } + + if (offset + filesz > fw->size) { + dev_err(dev, "truncated fw: need 0x%llx avail 0x%zx\n", + offset + filesz, fw->size); + ret = -EINVAL; + break; + } + + if (!rproc_u64_fit_in_size_t(memsz)) { + dev_err(dev, "size (%llx) does not fit in size_t type\n", + memsz); + ret = -EOVERFLOW; + break; + } + + for (j = 0; j < local->num_slave_ports; j++) { + if (da == local->sp_local_mem[j]) + da = local->mp_slave_port[j]; + } + + /* grab the kernel address for this device address */ + ptr = rproc_da_to_va(rproc, da, memsz, NULL); + if (!ptr) { + dev_err(dev, "bad phdr da 0x%llx mem 0x%llx\n", da, + memsz); + ret = -EINVAL; + break; + } + + /* put the segment where the remote processor expects it */ + if (filesz) + memcpy(ptr, elf_data + offset, filesz); + + /* + * Zero out remaining memory for this segment. + * + * This isn't strictly required since dma_alloc_coherent already + * did this for us. albeit harmless, we may consider removing + * this. + */ + if (memsz > filesz) + memset(ptr + filesz, 0, memsz - filesz); + } + + return ret; +} + +static struct rproc_ops andes_rproc_ops = { + .start = andes_rproc_start, + .stop = andes_rproc_stop, + .kick = andes_rproc_kick, + .parse_fw = andes_parse_fw, + .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, + .load = andes_rproc_elf_load_segments, + .sanity_check = rproc_elf_sanity_check, + .get_boot_addr = rproc_elf_get_boot_addr, +}; + +static int andes_remoteproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dev_node = dev->of_node; + struct rproc *rproc; + struct andes_rproc_pdata *local; + int ret = 0; + + rproc = rproc_alloc(dev, dev_node->name, &andes_rproc_ops, + NULL, sizeof(*local)); + if (!rproc) { + dev_err(&pdev->dev, "rproc allocation failed\n"); + return -ENOMEM; + } + + if (!atcsmu.base) { + dev_err(&pdev->dev, "atcsmu.base is NULL or 0x0\n"); + goto error; + } + + rproc->auto_boot = false; + local = rproc->priv; + local->rproc = rproc; + local->dev = dev; + + local->mbox_msg = ioremap_wc(MBOX_MSG, 4); + if (IS_ERR_OR_NULL(local->mbox_msg)) { + dev_err(&pdev->dev, "Failed to ioremap mbox_msg\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, rproc); + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret); + goto error; + } + + local->irq = platform_get_irq(pdev, 0); + if (local->irq <= 0) + return -EINVAL; + ret = devm_request_threaded_irq(dev, local->irq, + andes_remoteproc_interrupt, + handle_event, + 0, DRV_NAME, + local->rproc); + + ret = rproc_add(local->rproc); + if (ret) { + dev_err(&pdev->dev, "rproc registration failed\n"); + goto error; + } + return 0; +error: + rproc_free(rproc); + return ret; +} + +static int andes_remoteproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + struct andes_rproc_pdata *local = rproc->priv; + + if (atomic_read(&rproc->power) > 0) + rproc_shutdown(rproc); + + iounmap(local->mbox_msg); + + rproc_del(rproc); + of_reserved_mem_device_release(&pdev->dev); + rproc_free(rproc); + + return 0; +} + +/* Match table for OF platform binding */ +static const struct of_device_id andes_remoteproc_match[] = { + { .compatible = "andestech,andes_remoteproc", }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, andes_remoteproc_match); + +static struct platform_driver andes_remoteproc_driver = { + .probe = andes_remoteproc_probe, + .remove = andes_remoteproc_remove, + .driver = { + .name = "andes_remoteproc", + .of_match_table = andes_remoteproc_match, + }, +}; +module_platform_driver(andes_remoteproc_driver); + +MODULE_DESCRIPTION("Andes remote processor control driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index d4dbb8d1d80cc9..cd62cfc1199450 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -112,6 +112,7 @@ void rproc_free_vring(struct rproc_vring *rvring); int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); int rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i); +void *rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem); phys_addr_t rproc_va_to_pa(void *cpu_addr); int rproc_trigger_recovery(struct rproc *rproc); diff --git a/samples/andes_remoteproc/App/Makefile b/samples/andes_remoteproc/App/Makefile new file mode 100644 index 00000000000000..e96bcacc58eef4 --- /dev/null +++ b/samples/andes_remoteproc/App/Makefile @@ -0,0 +1,29 @@ +CC=riscv64-linux-gcc + +ECHO = echo_test +ECHO_OBJS = echo_test.o rpmsg_demo_ctrl.o + +MAT_MUL = mat_mul_demo +MAT_MUL_OBJS = mat_mul_demo.o rpmsg_demo_ctrl.o + +PROXY = proxy_app +PROXY_OBJS = proxy_app.o rpmsg_demo_ctrl.o + +CFLAGS += -Wall -Werror + +all: $(ECHO) $(MAT_MUL) $(PROXY) + +$(ECHO): $(ECHO_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ $(ECHO_OBJS) $(LDLIBS) + +$(MAT_MUL): $(MAT_MUL_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ $(MAT_MUL_OBJS) $(LDLIBS) -lpthread + +$(PROXY): $(PROXY_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ $(PROXY_OBJS) $(LDLIBS) + +clean: + rm -rf $(ECHO) $(MAT_MUL) $(PROXY) *.o + +%.o: %.c + $(CC) -c $(CFLAGS) -o $@ $< diff --git a/samples/andes_remoteproc/App/build_all.sh b/samples/andes_remoteproc/App/build_all.sh new file mode 100755 index 00000000000000..0321e15e8284d5 --- /dev/null +++ b/samples/andes_remoteproc/App/build_all.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +##################################### +# Build # +##################################### + +echo $PATH +export | grep riscv +echo $LDFLAGS + +make clean +make + +rm ../Script/echo_test ../Script/mat_mul_demo ../Script/proxy_app +cp echo_test mat_mul_demo proxy_app ../Script diff --git a/samples/andes_remoteproc/App/echo_test.c b/samples/andes_remoteproc/App/echo_test.c new file mode 100644 index 00000000000000..e56281b4fcc3e8 --- /dev/null +++ b/samples/andes_remoteproc/App/echo_test.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Test application that data integraty of inter processor + * communication from linux userspace to a remote software + * context. The application sends chunks of data to the + * remote processor. The remote side echoes the data back + * to application which then validates the data returned. + */ + +#include "rpmsg_demo_ctrl.h" + +#define RPMSG_GET_KFIFO_SIZE 1 +#define RPMSG_GET_AVAIL_DATA_SIZE 2 +#define RPMSG_GET_FREE_SPACE 3 +#define RPMSG_HEADER_LEN 16 +#define MAX_RPMSG_BUFF_SIZE (512 - RPMSG_HEADER_LEN) + +#define PAYLOAD_MIN_SIZE 1 +#define PAYLOAD_MAX_SIZE (MAX_RPMSG_BUFF_SIZE - 24) +#define NUM_PAYLOADS (PAYLOAD_MAX_SIZE/PAYLOAD_MIN_SIZE) + +static int charfd = -1, fd = -1, err_cnt; + +struct _payload { + unsigned long num; + unsigned long size; + char data[]; +}; + +struct _payload *i_payload; +struct _payload *r_payload; + +void send_shutdown(int fd) +{ + union { + unsigned int n[8]; + struct _payload sdown; + } umsg = { + .n = { + SHUTDOWN_MSG, SHUTDOWN_MSG, SHUTDOWN_MSG, SHUTDOWN_MSG, + SHUTDOWN_MSG, SHUTDOWN_MSG, SHUTDOWN_MSG, SHUTDOWN_MSG, + } + }; + + umsg.sdown.size = sizeof(umsg); + if (write(fd, &umsg, sizeof(umsg)) < 0) + perror("write SHUTDOWN_MSG\n"); +} + +int main(int argc, char *argv[]) +{ + int ret, i, j; + int size, bytes_rcvd, bytes_sent; + int opt; + char *rpmsg_dev = RPMSG_DEV; + int ntimes = 1; + char fpath[256]; + char rpmsg_char_name[16]; + struct rpmsg_endpoint_info eptinfo; + char ept_dev_name[16]; + char ept_dev_path[32]; + + while ((opt = getopt(argc, argv, "d:n:")) != -1) { + switch (opt) { + case 'd': + rpmsg_dev = optarg; + break; + case 'n': + ntimes = atoi(optarg); + break; + default: + printf("getopt return unsupported option: -%c\n", opt); + break; + } + } + + printf("\r\nEcho test start \r\n"); + printf("\r\nOpen rpmsg dev %s! \r\n", rpmsg_dev); + sprintf(fpath, "%s/devices/%s", RPMSG_BUS_SYS, rpmsg_dev); + if (access(fpath, F_OK)) { + fprintf(stderr, "Not able to access rpmsg device %s, %s\n", + fpath, strerror(errno)); + return -EINVAL; + } + + charfd = get_rpmsg_chrdev_fd(rpmsg_dev, rpmsg_char_name); + if (charfd < 0) + return charfd; + + strcpy(eptinfo.name, "rpmsg-openamp-demo-channel"); + eptinfo.src = 0; + eptinfo.dst = 0x400; + ret = rpmsg_create_ept(charfd, &eptinfo); + if (ret) { + printf("failed to create RPMsg endpoint.\n"); + return -EINVAL; + } + if (!get_rpmsg_ept_dev_name(rpmsg_char_name, eptinfo.name, + ept_dev_name)) + return -EINVAL; + sprintf(ept_dev_path, "/dev/%s", ept_dev_name); + fd = open(ept_dev_path, O_RDWR | O_NONBLOCK); + if (fd < 0) { + perror("Failed to open rpmsg device."); + close(charfd); + return -1; + } + + i_payload = (struct _payload *)malloc(2 * sizeof(unsigned long) + PAYLOAD_MAX_SIZE); + r_payload = (struct _payload *)malloc(2 * sizeof(unsigned long) + PAYLOAD_MAX_SIZE); + + if (i_payload == 0 || r_payload == 0) { + printf("ERROR: Failed to allocate memory for payload.\n"); + return -1; + } + + for (j = 0; j < ntimes; j++) { + printf("\r\n **********************************"); + printf("****\r\n"); + printf("\r\n Echo Test Round %d \r\n", j); + printf("\r\n **********************************"); + printf("****\r\n"); + for (i = 0, size = PAYLOAD_MIN_SIZE; i < NUM_PAYLOADS; + i++, size++) { + int k; + + i_payload->num = i; + i_payload->size = size; + + /* Mark the data buffer. */ + memset(&(i_payload->data[0]), 0xA5, size); + + printf("\r\n sending payload number"); + printf(" %ld of size %ld\r\n", i_payload->num, + (2 * sizeof(unsigned long)) + size); + + bytes_sent = write(fd, i_payload, + (2 * sizeof(unsigned long)) + size); + + if (bytes_sent <= 0) { + printf("\r\n Error sending data"); + printf(" .. \r\n"); + break; + } + printf("echo test: sent : %d\n", bytes_sent); + + r_payload->num = 0; + bytes_rcvd = read(fd, r_payload, + (2 * sizeof(unsigned long)) + PAYLOAD_MAX_SIZE); + while (bytes_rcvd <= 0) { + usleep(10000); + bytes_rcvd = read(fd, r_payload, + (2 * sizeof(unsigned long)) + PAYLOAD_MAX_SIZE); + } + printf(" received payload number "); + printf("%ld of size %d\r\n", r_payload->num, bytes_rcvd); + + /* Validate data buffer integrity. */ + for (k = 0; k < r_payload->size; k++) { + + if (r_payload->data[k] != 0xA5) { + printf(" \r\n Data corruption"); + printf(" at index %d \r\n", k); + err_cnt++; + break; + } + } + bytes_rcvd = read(fd, r_payload, + (2 * sizeof(unsigned long)) + PAYLOAD_MAX_SIZE); + + } + printf("\r\n **********************************"); + printf("****\r\n"); + printf("\r\n Echo Test Round %d Test Results: Error count = %d\r\n", + j, err_cnt); + printf("\r\n **********************************"); + printf("****\r\n"); + } + + /* send_shutdown(fd); */ + + free(i_payload); + free(r_payload); + + close(fd); + if (charfd >= 0) + close(charfd); + return 0; +} diff --git a/samples/andes_remoteproc/App/mat_mul_demo.c b/samples/andes_remoteproc/App/mat_mul_demo.c new file mode 100644 index 00000000000000..1a9e1048464c27 --- /dev/null +++ b/samples/andes_remoteproc/App/mat_mul_demo.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Sample demo application that showcases inter processor + * communication from linux userspace to a remote software + * context. The application generates random matrices and + * transmits them to the remote context over rpmsg. The + * remote application performs multiplication of matrices + * and transmits the results back to this application. + */ + +#include "rpmsg_demo_ctrl.h" + +#define MATRIX_SIZE 6 + +struct _matrix { + unsigned int size; + unsigned int elements[MATRIX_SIZE][MATRIX_SIZE]; +}; + +static int charfd = -1, fd = -1; +static struct _matrix i_matrix[2]; +static struct _matrix r_matrix; + +static void matrix_print(struct _matrix *m) +{ + int i, j; + + /* Generate two random matrices */ + printf("\r\nMaster : Linux : Printing results \r\n"); + + for (i = 0; i < m->size; ++i) { + for (j = 0; j < m->size; ++j) + printf(" %3d ", (unsigned int)m->elements[i][j]); + printf("\r\n"); + } +} + +static void generate_matrices(int num_matrices, + unsigned int matrix_size, void *p_data) +{ + int i, j, k; + struct _matrix *p_matrix = p_data; + time_t t; + unsigned long value; + + srand((unsigned int) time(&t)); + + for (i = 0; i < num_matrices; i++) { + /* Initialize workload */ + p_matrix[i].size = matrix_size; + + printf("\r\nMaster : Linux : Input matrix %d \r\n", i); + for (j = 0; j < matrix_size; j++) { + printf("\r\n"); + for (k = 0; k < matrix_size; k++) { + + value = (rand() & 0x7F); + value = value % 10; + p_matrix[i].elements[j][k] = value; + printf(" %3d ", + (unsigned int)p_matrix[i].elements[j][k]); + } + } + printf("\r\n"); + } +} + +/* + * Probably an overkill to memset(.., sizeof(struct _matrix)) as + * the firmware looks for SHUTDOWN_MSG in the first 32 bits. + */ +void send_shutdown(int fd) +{ + memset(i_matrix, SHUTDOWN_MSG, sizeof(struct _matrix)); + if (write(fd, i_matrix, sizeof(i_matrix)) < 0) + perror("write SHUTDOWN_MSG\n"); +} + +void matrix_mult(int ntimes) +{ + int i; + + for (i = 0; i < ntimes; i++) { + generate_matrices(2, 6, i_matrix); + + printf("%d: write rpmsg: %lu bytes\n", i, sizeof(i_matrix)); + ssize_t rc = write(fd, i_matrix, sizeof(i_matrix)); + + if (rc < 0) + fprintf(stderr, "write,errno = %ld, %d\n", rc, errno); + + puts("read results"); + do { + rc = read(fd, &r_matrix, sizeof(r_matrix)); + } while (rc < (int)sizeof(r_matrix)); + matrix_print(&r_matrix); + printf("End of Matrix multiplication demo Round %d\n", i); + } +} + +int main(int argc, char *argv[]) +{ + int ntimes = 1; + int opt, ret; + char *rpmsg_dev = RPMSG_DEV; + char rpmsg_char_name[16]; + char fpath[256]; + struct rpmsg_endpoint_info eptinfo; + char ept_dev_name[16]; + char ept_dev_path[32]; + + while ((opt = getopt(argc, argv, "d:n:")) != -1) { + switch (opt) { + case 'd': + rpmsg_dev = optarg; + break; + case 'n': + ntimes = atoi(optarg); + break; + default: + printf("getopt return unsupported option: -%c\n", opt); + break; + } + } + + printf("\r\nMatrix multiplication demo start\r\n"); + printf("\r\nOpen rpmsg dev %s!\r\n", rpmsg_dev); + sprintf(fpath, "%s/devices/%s", RPMSG_BUS_SYS, rpmsg_dev); + if (access(fpath, F_OK)) { + fprintf(stderr, "Not able to access rpmsg device %s, %s\n", + fpath, strerror(errno)); + return -EINVAL; + } + + charfd = get_rpmsg_chrdev_fd(rpmsg_dev, rpmsg_char_name); + if (charfd < 0) + return charfd; + strcpy(eptinfo.name, "rpmsg-openamp-demo-channel"); + eptinfo.src = 0; + eptinfo.dst = 0x400; + ret = rpmsg_create_ept(charfd, &eptinfo); + if (ret) { + fprintf(stderr, "rpmsg_create_ept %s\n", strerror(errno)); + return -EINVAL; + } + if (!get_rpmsg_ept_dev_name(rpmsg_char_name, eptinfo.name, + ept_dev_name)) + return -EINVAL; + sprintf(ept_dev_path, "/dev/%s", ept_dev_name); + + printf("open %s\n", ept_dev_path); + fd = open(ept_dev_path, O_RDWR | O_NONBLOCK); + if (fd < 0) { + perror(ept_dev_path); + close(charfd); + return -1; + } + + printf("%s:%d matrix_mult(%d)\n", __func__, __LINE__, ntimes); + matrix_mult(ntimes); + + /* send_shutdown(fd); */ + + close(fd); + if (charfd >= 0) + close(charfd); + + printf("\r\nQuitting application .. \r\n"); + printf("Matrix multiply application end \r\n"); + + return 0; +} diff --git a/samples/andes_remoteproc/App/proxy_app.c b/samples/andes_remoteproc/App/proxy_app.c new file mode 100644 index 00000000000000..c108052732663a --- /dev/null +++ b/samples/andes_remoteproc/App/proxy_app.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "proxy_app.h" + +//#define LPRINTF(format, ...) printf(format, ##__VA_ARGS__) +#define LPRINTF(format, ...) + +#define RPC_BUFF_SIZE 512 +#define PROXY_ENDPOINT 127 + +/* Initialization message ID */ +#define RPMG_INIT_MSG "init_msg" + +struct _proxy_data { + int active; + int rpmsg_proxy_fd; + struct _sys_rpc *rpc; + struct _sys_rpc *rpc_response; + char *firmware_path; +}; + +static struct _proxy_data *proxy; +char sbuf[512]; +int remote_id; + +int handle_open(struct _sys_rpc *rpc) +{ + int fd; + ssize_t bytes_written; + + /* Open remote fd */ + + fd = open(rpc->sys_call_args.data, rpc->sys_call_args.int_field1, + rpc->sys_call_args.int_field2); + + /* Construct rpc response */ + proxy->rpc_response->id = OPEN_SYSCALL_ID; + proxy->rpc_response->sys_call_args.int_field1 = fd; + proxy->rpc_response->sys_call_args.int_field2 = 0; /*not used*/ + proxy->rpc_response->sys_call_args.data_len = 0; /*not used*/ + + /* Transmit rpc response */ + bytes_written = write(proxy->rpmsg_proxy_fd, proxy->rpc_response, + sizeof(struct _sys_rpc)); + + return (bytes_written != sizeof(struct _sys_rpc)) ? -1 : 0; +} + +int handle_close(struct _sys_rpc *rpc) +{ + int retval; + ssize_t bytes_written; + + /* Close remote fd */ + retval = close(rpc->sys_call_args.int_field1); + + /* Construct rpc response */ + proxy->rpc_response->id = CLOSE_SYSCALL_ID; + proxy->rpc_response->sys_call_args.int_field1 = retval; + proxy->rpc_response->sys_call_args.int_field2 = 0; /*not used*/ + proxy->rpc_response->sys_call_args.data_len = 0; /*not used*/ + + /* Transmit rpc response */ + bytes_written = write(proxy->rpmsg_proxy_fd, proxy->rpc_response, + sizeof(struct _sys_rpc)); + + return (bytes_written != sizeof(struct _sys_rpc)) ? -1 : 0; +} + +int handle_read(struct _sys_rpc *rpc) +{ + ssize_t bytes_read, bytes_written; + size_t payload_size; + char *buff = proxy->rpc_response->sys_call_args.data; + + if (rpc->sys_call_args.int_field1 == 0) + /* + * * Perform read from fd for large size since this is a + * STD/I request + */ + bytes_read = read(rpc->sys_call_args.int_field1, buff, 512); + else + /* Perform read from fd */ + bytes_read = read(rpc->sys_call_args.int_field1, buff, + rpc->sys_call_args.int_field2); + + /* Construct rpc response */ + proxy->rpc_response->id = READ_SYSCALL_ID; + proxy->rpc_response->sys_call_args.int_field1 = bytes_read; + proxy->rpc_response->sys_call_args.int_field2 = 0; /* not used */ + proxy->rpc_response->sys_call_args.data_len = bytes_read; + + payload_size = sizeof(struct _sys_rpc) + + ((bytes_read > 0) ? bytes_read : 0); + + /* Transmit rpc response */ + LPRINTF("%s: %d, %d\n", __func__, proxy->rpc_response->id, + proxy->rpc_response->sys_call_args.int_field1); + bytes_written = write(proxy->rpmsg_proxy_fd, proxy->rpc_response, + payload_size); + + return (bytes_written != payload_size) ? -1 : 0; +} + +int handle_write(struct _sys_rpc *rpc) +{ + ssize_t bytes_written; + + /* Write to remote fd */ + bytes_written = write(rpc->sys_call_args.int_field1, + rpc->sys_call_args.data, + rpc->sys_call_args.int_field2); + + /* Construct rpc response */ + proxy->rpc_response->id = WRITE_SYSCALL_ID; + proxy->rpc_response->sys_call_args.int_field1 = bytes_written; + proxy->rpc_response->sys_call_args.int_field2 = 0; /*not used*/ + proxy->rpc_response->sys_call_args.data_len = 0; /*not used*/ + + /* Transmit rpc response */ + bytes_written = write(proxy->rpmsg_proxy_fd, proxy->rpc_response, + sizeof(struct _sys_rpc)); + + return (bytes_written != sizeof(struct _sys_rpc)) ? -1 : 0; +} + +int handle_rpc(struct _sys_rpc *rpc) +{ + int retval; + + /* Handle RPC */ + switch ((int)(rpc->id)) { + case OPEN_SYSCALL_ID: + { + retval = handle_open(rpc); + break; + } + case CLOSE_SYSCALL_ID: + { + retval = handle_close(rpc); + break; + } + case READ_SYSCALL_ID: + { + retval = handle_read(rpc); + break; + } + case WRITE_SYSCALL_ID: + { + retval = handle_write(rpc); + break; + } + case TERM_SYSCALL_ID: + { + proxy->active = 0; + retval = 0; + break; + } + default: + { + printf("\r\nMaster>Err:Invalid RPC sys call ID: %d! \r\n", rpc->id); + retval = -1; + break; + } + } + + return retval; +} + +/* write a string to an existing and writtable file */ +int file_write(char *path, char *str) +{ + int fd; + ssize_t bytes_written; + size_t str_sz; + + fd = open(path, O_WRONLY); + if (fd == -1) { + perror("Error"); + return -1; + } + str_sz = strlen(str); + bytes_written = write(fd, str, str_sz); + if (bytes_written != str_sz) { + if (bytes_written == -1) + perror("Error"); + close(fd); + return -1; + } + + if (-1 == close(fd)) { + perror("Error"); + return -1; + } + return 0; +} + +/* Stop remote CPU and Unload drivers */ +void stop_remote(void) +{ + system("modprobe -r rpmsg_char"); + sprintf(sbuf, + "/sys/class/remoteproc/remoteproc%u/state", + remote_id); + (void)file_write(sbuf, "stop"); +} + +void exit_action_handler(int signum) +{ + proxy->active = 0; +} + +void kill_action_handler(int signum) +{ + printf("\r\nMaster>RPC service killed !!\r\n"); + + /* Close proxy rpmsg device */ + close(proxy->rpmsg_proxy_fd); + + /* Free up resources */ + free(proxy->rpc); + free(proxy->rpc_response); + free(proxy); + + /* Stop remote cpu and unload drivers */ + stop_remote(); +} + +void display_help_msg(void) +{ + printf("\r\nLinux proxy application.\r\n"); + printf("-v Displays proxy application version.\n"); + printf("-c Whether to use RPMsg char driver.\n"); + printf("-f Accepts path of firmware to load on remote core.\n"); + printf("-r Which remoteproc instance\n"); + printf("-h Displays this help message.\n"); +} + +int main(int argc, char *argv[]) +{ + struct sigaction exit_action; + struct sigaction kill_action; + int bytes_rcvd; + int opt = 0, ret = 0; + char *rpmsg_dev = RPMSG_DEV; + char rpmsg_char_name[16]; + int rpmsg_char_fd = -1, ept_fd = -1; + struct rpmsg_endpoint_info eptinfo; + char ept_dev_name[16]; + char ept_dev_path[32]; + + /* Initialize signalling infrastructure */ + memset(&exit_action, 0, sizeof(struct sigaction)); + memset(&kill_action, 0, sizeof(struct sigaction)); + exit_action.sa_handler = exit_action_handler; + kill_action.sa_handler = kill_action_handler; + sigaction(SIGTERM, &exit_action, NULL); + sigaction(SIGINT, &exit_action, NULL); + sigaction(SIGKILL, &kill_action, NULL); + sigaction(SIGHUP, &kill_action, NULL); + + while ((opt = getopt(argc, argv, "vh:r:")) != -1) { + switch (opt) { + case 'r': + remote_id = atoi(optarg); + if (remote_id != 0 && remote_id != 1) { + display_help_msg(); + return -1; + } + break; + case 'v': + printf("\r\nLinux proxy application version 1.1\r\n"); + return 0; + case 'h': + display_help_msg(); + return 0; + default: + printf("getopt return unsupported option: -%c\n", opt); + break; + } + } + + rpmsg_char_fd = get_rpmsg_chrdev_fd(rpmsg_dev, rpmsg_char_name); + if (rpmsg_char_fd < 0) { + ret = rpmsg_char_fd; + goto error0; + } + + /* Create endpoint from rpmsg char driver */ + strcpy(eptinfo.name, "rpmsg-openamp-demo-channel"); + eptinfo.src = 0; + eptinfo.dst = 0x400; + ret = rpmsg_create_ept(rpmsg_char_fd, &eptinfo); + if (ret) { + printf("failed to create RPMsg endpoint.\n"); + goto error0; + } + if (!get_rpmsg_ept_dev_name(rpmsg_char_name, eptinfo.name, + ept_dev_name)) { + ret = -EINVAL; + goto error0; + } + sprintf(ept_dev_path, "/dev/%s", ept_dev_name); + ept_fd = open(ept_dev_path, O_RDWR | O_NONBLOCK); + if (ept_fd < 0) { + perror("Failed to open rpmsg device."); + ret = ept_fd; + goto error0; + } + /* Allocate memory for proxy data structure */ + proxy = malloc(sizeof(struct _proxy_data)); + if (proxy == NULL) { + fprintf(stderr, "\r\nMaster>Failed to allocate memory.\r\n"); + ret = -ENOMEM; + goto error0; + } + proxy->rpmsg_proxy_fd = ept_fd; + proxy->active = 1; + + /* Allocate memory for rpc payloads */ + proxy->rpc = malloc(RPC_BUFF_SIZE); + proxy->rpc_response = malloc(RPC_BUFF_SIZE); + if (proxy->rpc == NULL || proxy->rpc_response == NULL) { + fprintf(stderr, "\r\nMaster>Failed to allocate memory.\r\n"); + ret = -ENOMEM; + goto error0; + } + + /* RPC service starts */ + printf("\r\nMaster>RPC service started !!\r\n"); + /* Send init message to remote. + * This is required otherwise, remote doesn't know the host + * RPMsg endpoint + */ + ret = write(proxy->rpmsg_proxy_fd, RPMG_INIT_MSG, + sizeof(RPMG_INIT_MSG)); + if (ret < 0) { + printf("\r\nMaster>Failed to send init message.\r\n"); + goto error0; + } + + /* Block on read for rpc requests from remote context */ + do { + bytes_rcvd = read(proxy->rpmsg_proxy_fd, proxy->rpc, + RPC_BUFF_SIZE); + if (bytes_rcvd < 0 && errno != EAGAIN) { + perror("Failed to read ept"); + break; + } + /* Handle rpc */ + if (bytes_rcvd > 0 && handle_rpc(proxy->rpc)) { + printf("\nMaster>Err:Handling remote procedure call!\n"); + printf("\nrpc id %d\n", proxy->rpc->id); + printf("\nrpc int field1 %d\n", + proxy->rpc->sys_call_args.int_field1); + printf("\nrpc int field2 %d\n", + proxy->rpc->sys_call_args.int_field2); + break; + } + } while (proxy->active); + + printf("\r\nMaster>RPC service exiting !!\r\n"); + ret = 0; + + /* Close proxy rpmsg device */ + close(proxy->rpmsg_proxy_fd); + + /* Wait for other end to cleanup + * Otherwise, virtio_rpmsg_bus can post msg with no recipient + * warning as it can receive NS destroy after the rpmsg char + * module is removed. + */ + sleep(1); + + /* Free up resources */ + free(proxy->rpc); + free(proxy->rpc_response); + +error0: + if (ept_fd >= 0) + close(ept_fd); + if (rpmsg_char_fd >= 0) + close(rpmsg_char_fd); + free(proxy); + + stop_remote(); + + return ret; +} diff --git a/samples/andes_remoteproc/App/proxy_app.h b/samples/andes_remoteproc/App/proxy_app.h new file mode 100644 index 00000000000000..d4a6f7f0c29b4d --- /dev/null +++ b/samples/andes_remoteproc/App/proxy_app.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "rpmsg_demo_ctrl.h" + +/* System call definitions */ +#define OPEN_SYSCALL_ID 1 +#define CLOSE_SYSCALL_ID 2 +#define WRITE_SYSCALL_ID 3 +#define READ_SYSCALL_ID 4 +#define ACK_STATUS_ID 5 +#define TERM_SYSCALL_ID 6 + +#define FILE_NAME_LEN 50 + +struct _rpc_data { + struct rpmsg_channel *rpmsg_chnl; + struct rpmsg_endpoint *rp_ept; + void *rpc_lock; + void *sync_lock; + void *data; +}; + +struct _sys_call_args { + int32_t int_field1; + int32_t int_field2; + uint32_t data_len; + char data[0]; +}; + +/* System call rpc data structure */ +struct _sys_rpc { + uint32_t id; + struct _sys_call_args sys_call_args; +}; diff --git a/samples/andes_remoteproc/App/rpmsg_demo_ctrl.c b/samples/andes_remoteproc/App/rpmsg_demo_ctrl.c new file mode 100644 index 00000000000000..0facb060b4f60e --- /dev/null +++ b/samples/andes_remoteproc/App/rpmsg_demo_ctrl.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "rpmsg_demo_ctrl.h" + +int rpmsg_create_ept(int rpfd, struct rpmsg_endpoint_info *eptinfo) +{ + int ret; + + ret = ioctl(rpfd, RPMSG_CREATE_EPT_IOCTL, eptinfo); + if (ret) + perror("Failed to create endpoint.\n"); + return ret; +} + +char *get_rpmsg_ept_dev_name(const char *rpmsg_char_name, + const char *ept_name, + char *ept_dev_name) +{ + char sys_rpmsg_ept_name_path[64], svc_name[64]; + char dpath[256], fpath[256]; + int i, ept_name_len; + + FILE *fp; + + for (i = 0; i < 128; i++) { + sprintf(sys_rpmsg_ept_name_path, "%s/%s/rpmsg%d/name", + RPMSG_CLASS_SYS, rpmsg_char_name, i); + printf("checking %s\n", sys_rpmsg_ept_name_path); + if (access(sys_rpmsg_ept_name_path, F_OK) < 0) + continue; + fp = fopen(sys_rpmsg_ept_name_path, "r"); + if (!fp) { + printf("failed to open %s\n", sys_rpmsg_ept_name_path); + break; + } + fgets(svc_name, sizeof(svc_name), fp); + fclose(fp); + printf("svc_name: %s", svc_name); + ept_name_len = strlen(ept_name); + if (ept_name_len > sizeof(svc_name)) + ept_name_len = sizeof(svc_name); + if (!strncmp(svc_name, ept_name, ept_name_len)) { + sprintf(ept_dev_name, "rpmsg%d", i); + + /* + * Make file node under the /dev + */ + sprintf(dpath, "%s/%s/dev", + RPMSG_CLASS_SYS, ept_dev_name); + sprintf(fpath, "/dev/%s", ept_dev_name); + set_dev_node(dpath, fpath); + + return ept_dev_name; + } + } + + printf("Not able to RPMsg endpoint file for %s:%s.\n", + rpmsg_char_name, ept_name); + return NULL; +} + +int get_rpmsg_chrdev_fd(const char *rpmsg_dev_name, + char *rpmsg_ctrl_name) +{ + char dpath[512], fpath[512]; + char *rpmsg_ctrl_prefix = "rpmsg_ctrl"; + DIR *dir; + struct dirent *ent; + int fd; + + sprintf(dpath, "%s/devices/%s/rpmsg", RPMSG_BUS_SYS, rpmsg_dev_name); + dir = opendir(dpath); + if (dir == NULL) { + fprintf(stderr, "Failed to open dir %s\n", dpath); + return -EINVAL; + } + while ((ent = readdir(dir)) != NULL) { + if (!strncmp(ent->d_name, rpmsg_ctrl_prefix, + strlen(rpmsg_ctrl_prefix))) { + printf("Opening file %s.\n", ent->d_name); + sprintf(fpath, "/dev/%s", ent->d_name); + + /* + * Make file node under the /dev + */ + sprintf(dpath, "%s/devices/%s/rpmsg/%s/dev", + RPMSG_BUS_SYS, rpmsg_dev_name, ent->d_name); + set_dev_node(dpath, fpath); + + fd = open(fpath, O_RDWR | O_NONBLOCK); + if (fd < 0) { + fprintf(stderr, + "Failed to open rpmsg char dev %s,%s\n", + fpath, strerror(errno)); + closedir(dir); + return fd; + } + sprintf(rpmsg_ctrl_name, "%s", ent->d_name); + closedir(dir); + return fd; + } + } + + fprintf(stderr, "No rpmsg char dev file is found\n"); + return -EINVAL; +} + +void set_dev_node(char *path, char *dev_path) +{ + FILE *fp; + char dev[12]; + char *major, *minor; + int major_num, minor_num, ret; + + fp = fopen(path, "r"); + if (!fp) + printf("Failed to open %s\n", path); + + fgets(dev, sizeof(dev), fp); + fclose(fp); + + major = strtok(dev, ":"); + minor = strtok(NULL, ":"); + + major_num = atoi(major); + minor_num = atoi(minor); + printf("dev_path = %s, major = %d, minor = %d\n", + dev_path, major_num, minor_num); + + ret = mknod(dev_path, S_IFCHR, makedev(major_num, minor_num)); + + if (ret) + printf("mknod No.%d\n", ret); +} diff --git a/samples/andes_remoteproc/App/rpmsg_demo_ctrl.h b/samples/andes_remoteproc/App/rpmsg_demo_ctrl.h new file mode 100644 index 00000000000000..11afedbfd1e34c --- /dev/null +++ b/samples/andes_remoteproc/App/rpmsg_demo_ctrl.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __TEMP_H__ +#define __TEMP_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RPMSG_BUS_SYS "/sys/bus/rpmsg" +#define RPMSG_CLASS_SYS "/sys/class/rpmsg" +#define RPMSG_DEV "virtio0.rpmsg_ctrl.0.0" +#define SHUTDOWN_MSG 0xEF56A55A + +int rpmsg_create_ept(int rpfd, struct rpmsg_endpoint_info *eptinfo); +char *get_rpmsg_ept_dev_name(const char *rpmsg_char_name, + const char *ept_name, + char *ept_dev_name); +int get_rpmsg_chrdev_fd(const char *rpmsg_dev_name, + char *rpmsg_ctrl_name); +void set_dev_node(char *path, char *dev_path); + +#endif /* __TEMP_H__ */ diff --git a/samples/andes_remoteproc/Script/load_fw_echo.sh b/samples/andes_remoteproc/Script/load_fw_echo.sh new file mode 100755 index 00000000000000..84b365ba81cc34 --- /dev/null +++ b/samples/andes_remoteproc/Script/load_fw_echo.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +FW_PATH=$(pwd) +FW_NAME=rpmsg-echo.out + +APP_PATH=$(pwd) +APP_NAME=echo_test + +REMOTEPROC=remoteproc0 +RPMSG_CHRDEV=virtio0.rpmsg_ctrl.0.0 + +Load_and_Start_fw() +{ + echo "==== 1. Load_and_Start_fw start. ====" + echo -n $FW_PATH > /sys/module/firmware_class/parameters/path + echo -n $FW_NAME > /sys/class/remoteproc/$REMOTEPROC/firmware + echo -n start > /sys/class/remoteproc/$REMOTEPROC/state + echo "==== 1. Load_and_Start_fw done!! ====" +} + +Start_App() +{ + echo "==== 2. Start_App start. ====" + $APP_PATH/$APP_NAME -d $RPMSG_CHRDEV -n 1 + modprobe -r rpmsg_char + echo -n stop > /sys/class/remoteproc/$REMOTEPROC/state + modprobe rpmsg_char + echo "==== 2. Start_App done!! ====" +} + +Load_and_Start_fw +Start_App diff --git a/samples/andes_remoteproc/Script/load_fw_mat_mul.sh b/samples/andes_remoteproc/Script/load_fw_mat_mul.sh new file mode 100755 index 00000000000000..ad981c1e693862 --- /dev/null +++ b/samples/andes_remoteproc/Script/load_fw_mat_mul.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +FW_PATH=$(pwd) +FW_NAME=matrix_multiplyd.out + +APP_PATH=$(pwd) +APP_NAME=mat_mul_demo + +REMOTEPROC=remoteproc0 +RPMSG_CHRDEV=virtio0.rpmsg_ctrl.0.0 + +Load_and_Start_fw() +{ + echo "==== 1. Load_and_Start_fw start. ====" + echo -n $FW_PATH > /sys/module/firmware_class/parameters/path + echo -n $FW_NAME > /sys/class/remoteproc/$REMOTEPROC/firmware + echo -n start > /sys/class/remoteproc/$REMOTEPROC/state + echo "==== 1. Load_and_Start_fw done!! ====" +} + +Start_App() +{ + echo "==== 2. Start_App start. ====" + $APP_PATH/$APP_NAME -d $RPMSG_CHRDEV -n 1 + modprobe -r rpmsg_char + echo -n stop > /sys/class/remoteproc/$REMOTEPROC/state + modprobe rpmsg_char + echo "==== 2. Start_App done!! ====" +} + +Load_and_Start_fw +Start_App diff --git a/samples/andes_remoteproc/Script/load_fw_proxy_app.sh b/samples/andes_remoteproc/Script/load_fw_proxy_app.sh new file mode 100755 index 00000000000000..2ea4d4eecacf92 --- /dev/null +++ b/samples/andes_remoteproc/Script/load_fw_proxy_app.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +FW_PATH=$(pwd) +FW_NAME=rpc_demo.out + +APP_PATH=$(pwd) +APP_NAME=proxy_app + +REMOTEPROC=remoteproc0 +RPMSG_CHRDEV=virtio0.rpmsg_ctrl.0.0 + +Load_and_Start_fw() +{ + echo "==== 1. Load_and_Start_fw start. ====" + echo -n $FW_PATH > /sys/module/firmware_class/parameters/path + echo -n $FW_NAME > /sys/class/remoteproc/$REMOTEPROC/firmware + echo -n start > /sys/class/remoteproc/$REMOTEPROC/state + echo "==== 1. Load_and_Start_fw done!! ====" +} + +Start_App() +{ + echo "==== 2. Start_App start. ====" + $APP_PATH/$APP_NAME + modprobe rpmsg_char + rm remote.file + echo "==== 2. Start_App done!! ====" +} + +Load_and_Start_fw +Start_App From 5ba384e7f82ed1f8aad0a321120730c60aff965c Mon Sep 17 00:00:00 2001 From: Randolph Date: Fri, 9 Jun 2023 16:28:24 +0800 Subject: [PATCH 084/169] riscv: dts: andes: ae350: add amp device tree source for andes_remoteproc The AX25MP AMP DTS creates a node for andes_remoteproc module and reserved memory from main memory to create shared memory and vrings. --- arch/riscv/boot/dts/andes/Makefile | 1 + .../dts/andes/ax25mp_amp_c2_64_d_ae350.dts | 305 ++++++++++++++++++ 2 files changed, 306 insertions(+) create mode 100644 arch/riscv/boot/dts/andes/ax25mp_amp_c2_64_d_ae350.dts diff --git a/arch/riscv/boot/dts/andes/Makefile b/arch/riscv/boot/dts/andes/Makefile index f4e5195458e0a3..1cb9312dd28f46 100644 --- a/arch/riscv/boot/dts/andes/Makefile +++ b/arch/riscv/boot/dts/andes/Makefile @@ -6,6 +6,7 @@ dtb-$(CONFIG_PLAT_AE350) += a45_c1_d_dsp_noncoherent_ae350.dtb dtb-$(CONFIG_PLAT_AE350) += ax25_c1_d_dsp_noncoherent_ae350.dtb dtb-$(CONFIG_PLAT_AE350) += ax27l2_c1_d_dsp_noncoherent_ae350.dtb dtb-$(CONFIG_PLAT_AE350) += ax45_c1_d_dsp_noncoherent_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += ax25mp_amp_c2_64_d_ae350.dtb # FPGA : VCU118 dtb-$(CONFIG_PLAT_AE350) += a25mp_c4_d_dsp_ae350.dtb diff --git a/arch/riscv/boot/dts/andes/ax25mp_amp_c2_64_d_ae350.dts b/arch/riscv/boot/dts/andes/ax25mp_amp_c2_64_d_ae350.dts new file mode 100644 index 00000000000000..5d80968907dce3 --- /dev/null +++ b/arch/riscv/boot/dts/andes/ax25mp_amp_c2_64_d_ae350.dts @@ -0,0 +1,305 @@ +/dts-v1/; + +/ { + compatible = "andestech,ae350"; + #address-cells = <0x2>; + #size-cells = <0x2>; + dma-coherent; + + cpus { + #address-cells = <0x1>; + #size-cells = <0x0>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x0>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0xv5-0p0"; + riscv,priv-major = <0x1>; + riscv,priv-minor = <0xa>; + mmu-type = "riscv,sv39"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-line-size = <0x20>; + d-cache-size = <0x8000>; + d-cache-line-size = <0x20>; + next-level-cache = <0x1>; + + interrupt-controller { + #interrupt-cells = <0x1>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x2>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x2>; + cache-size = <0x40000>; + reg = <0x0 0xe0500000 0x0 0x40000>; + andes,inst-prefetch = <0x3>; + andes,data-prefetch = <0x3>; + andes,tag-ram-ctl = <0x0 0x0>; + andes,data-ram-ctl = <0x0 0x0>; + phandle = <0x1>; + }; + + memory@0 { + device_type = "memory"; + reg = <0x0 0x0 0x0 0x40000000>; + }; + + soc { + #address-cells = <0x2>; + #size-cells = <0x2>; + compatible = "andestech,riscv-ae350-soc", "simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + #address-cells = <0x2>; + #interrupt-cells = <0x2>; + interrupt-controller; + reg = <0x0 0xe4000000 0x0 0x2000000>; + riscv,ndev = <0x47>; + interrupts-extended = <0x2 0xb 0x2 0x9 0x3 0xb 0x3 0x9>; + phandle = <0x4>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + #address-cells = <0x2>; + #interrupt-cells = <0x2>; + interrupt-controller; + reg = <0x0 0xe6400000 0x0 0x400000>; + riscv,ndev = <0x2>; + interrupts-extended = <0x2 0x3 0x3 0x3>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x0 0xe6000000 0x0 0x100000>; + interrupts-extended = <0x2 0x7 0x3 0x7>; + }; + }; + + reserved-memory { + #address-cells = <0x2>; + #size-cells = <0x2>; + ranges; + + reserved_mem1:reserved_mem1@3fe00000 { + no-map; + reg = <0x0 0x3fe00000 0x0 0x1e4000>; + alignment = <0x100>; + }; + + vdev0buffer:vdev0buffer@3ffe4000 { + no-map; + reg = <0x0 0x3ffe4000 0x0 0xc000>; + alignment = <0x100>; + }; + vdev0vring1:vdev0vring0@3fff0000 { + no-map; + reg = <0x0 0x3fff0000 0x0 0x4000>; + alignment = <0x100>; + }; + vdev0vring0:vdev0vring1@3fff4000 { + no-map; + reg = <0x0 0x3fff4000 0x0 0x4000>; + alignment = <0x100>; + }; + + andes_rsc_table:andes_rsc_table@3fff8000 { + no-map; + reg = <0x0 0x3fff8000 0x0 0x2000>; + aligment = <0x400>; + }; + + reserved_mem2:reserved_mem2@3fffa000 { + no-map; + reg = <0x0 0x3fffa000 0x0 0x6000>; + alignment = <0x100>; + }; + }; + + slave_port_ilm:slave_port_ilm@a0000000 { + compatible = "andestech,slave_port_ilm"; + reg = <0x0 0xa0000000 0x0 0x200000>; + }; + + slave_port_dlm:slave_port_dlm@a0200000 { + compatible = "andestech,slave_port_dlm"; + reg = <0x0 0xa0200000 0x0 0x200000>; + }; + + andes_remoteproc { + compatible = "andestech,andes_remoteproc"; + memory-region = <&vdev0buffer>, <&vdev0vring0>, + <&vdev0vring1>, <&andes_rsc_table>; + slave_ports = <&slave_port_ilm>, <&slave_port_dlm>; + interrupts = <0x1e 0x4>; + interrupt-parent = <0x4>; + }; + + virt_100mhz { + #clock-cells = <0x0>; + compatible = "fixed-clock"; + clock-frequency = <0x5f5e100>; + phandle = <0x5>; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x0 0xf0100000 0x0 0x1000>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + interrupts = <0x0 0x4>; + reg = <0x0 0xf0500000 0x0 0x1000>; + clock-frequency = <0xe4e1c0>; + interrupt-parent = <0x4>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x0 0xf0400000 0x0 0x1000>; + clock-frequency = <0x3938700>; + interrupts = <0x3 0x4>; + interrupt-parent = <0x4>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550", "ns16550a"; + reg = <0x0 0xf0300000 0x0 0x1000>; + interrupts = <0x9 0x4>; + clock-frequency = <0x12c0000>; + reg-shift = <0x2>; + reg-offset = <0x20>; + no-loopback-test = <0x1>; + interrupt-parent = <0x4>; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x0 0xf0700000 0x0 0x1000>; + interrupts = <0x7 0x4>; + interrupt-parent = <0x4>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x0 0xf0600000 0x0 0x1000>; + interrupts = <0x1 0x4 0x2 0x4>; + interrupt-parent = <0x4>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x0 0xe0100000 0x0 0x1000>; + interrupts = <0x13 0x4>; + interrupt-parent = <0x4>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010"; + max-frequency = <0x5f5e100>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + fifo-depth = <0x10>; + reg = <0x0 0xf0e00000 0x0 0x1000>; + interrupts = <0x12 0x4>; + cap-sd-highspeed; + interrupt-parent = <0x4>; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300"; + reg = <0x0 0xf0c00000 0x0 0x1000>; + interrupts = <0xa 0x4 0x40 0x4 0x41 0x4 0x42 0x4 0x43 0x4 0x44 0x4 0x45 0x4 0x46 0x4 0x47 0x4>; + dma-channels = <0x8>; + interrupt-parent = <0x4>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x0 0xe0200000 0x0 0x1000>; + interrupts = <0x14 0x4>; + interrupt-parent = <0x4>; + }; + + smc@e0400000 { + compatible = "andestech,atfsmc020"; + reg = <0x0 0xe0400000 0x0 0x1000>; + }; + + snd@f0d00000 { + compatible = "andestech,atfac97"; + reg = <0x0 0xf0d00000 0x0 0x1000>; + interrupts = <0x11 0x4>; + interrupt-parent = <0x4>; + }; + + pmu { + device_type = "pmu"; + compatible = "riscv,andes-pmu"; + }; + + nor@0,0 { + compatible = "cfi-flash"; + reg = <0x0 0x88000000 0x0 0x1000>; + bank-width = <0x2>; + device-width = <0x1>; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x0 0xf0b00000 0x0 0x1000>; + #address-cells = <0x1>; + #size-cells = <0x0>; + num-cs = <0x1>; + clocks = <0x5>; + interrupts = <0x4 0x4>; + interrupt-parent = <0x4>; + + flash@0 { + compatible = "mx25u1635e", "jedec,spi-nor"; + spi-max-frequency = <0x2faf080>; + reg = <0x0>; + spi-cpol; + spi-cpha; + }; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x0 0xf0400000 0x0 0x1000>; + clock-frequency = <0x3938700>; + interrupts = <0x3 0x4>; + interrupt-parent = <0x4>; + pwm-cells = <0x2>; + }; + + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x0 0xf0a00000 0x0 0x1000>; + interrupts = <0x6 0x4>; + interrupt-parent = <0x4>; + }; + + aliases { + uart0 = "/serial@f0300000"; + spi0 = "/spi@f0b00000"; + i2c0 = "/i2c@f0a00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 earlycon=sbi debug loglevel=7"; + stdout-path = "uart0:38400n8"; + }; +}; From 3a9969b57dc0921cb2c51be23f19a5d71c8ba829 Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Wed, 28 Sep 2022 11:09:40 +0800 Subject: [PATCH 085/169] riscv: fpu: refine FPU save flow When Kernel first time run to arch_dup_task_struct(), it will check if sstatus.FS is dirty. If it is dirty, then it will do FPU save flow. But this field is floating currently. Meanwhile if the combination between platform(HW) and Kernel(SW) about FPU configuration is mismatch. eq: The platform is without FPU and Kernel is with FPU. Then Kernel may trigger illegal instruction here. Hence it shall check by has_fpu before do FPU save flow in arch_dup_task_struct(). Signed-off-by: Rick Chen --- arch/riscv/kernel/process.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index dffe2b3796702e..6146dd89eeb74d 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -155,7 +155,8 @@ void flush_thread(void) int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) { - fstate_save(src, task_pt_regs(src)); + if (has_fpu()) + fstate_save(src, task_pt_regs(src)); *dst = *src; return 0; } From b6de3a298bb0de3ebf5964fbb0db9fd55c3d784f Mon Sep 17 00:00:00 2001 From: Rick Chen Date: Thu, 29 Jun 2023 14:59:22 +0800 Subject: [PATCH 086/169] riscv: andes: Fix WARN_ON when PROVE_LOCK is enabled When Kernel execute patch function feature, it was expected to lock the text_mutex. By default PROVE_LOCK is not enabled, it will ignore the lock checking mechanism. As PROVE_LOCK is enabled, it will check if text_mutex is locked, otherwise there comes the WARN_ON messages during booting progress. Add to lock text_mutex can fix this issue. Signed-off-by: Rick Chen --- arch/riscv/errata/andes/errata.c | 7 ++++++- arch/riscv/kernel/cpufeature.c | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/riscv/errata/andes/errata.c b/arch/riscv/errata/andes/errata.c index 0442ccaf46e155..da28f685085ea1 100644 --- a/arch/riscv/errata/andes/errata.c +++ b/arch/riscv/errata/andes/errata.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include bool andes_legacy_mmu; EXPORT_SYMBOL(andes_legacy_mmu); @@ -123,7 +125,10 @@ void __init_or_module andes_errata_patch_func(struct alt_entry *begin, continue; tmp = (1U << alt->errata_id); - if (cpu_req_errata & tmp) + if (cpu_req_errata & tmp) { + mutex_lock(&text_mutex); patch_text_nosync(alt->old_ptr, alt->alt_ptr, alt->alt_len); + mutex_unlock(&text_mutex); + } } } diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 58bf81a5ef665c..64b7b70b4d8c14 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #define NUM_ALPHA_EXTS ('z' - 'a' + 1) From 59ff4d42e5804cfefcbb0e2002859ee4821e9e37 Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Tue, 18 Jul 2023 17:50:24 +0800 Subject: [PATCH 087/169] soc: andes: ppma:Replacing smp_processor_id() with get/put_cpu() for preemption control. When the debug preemptible kernel is enabled, check_preemption_disabled() is performed when smp_processor_id() is called. In this change, we replace smp_processor_id with get_cpu/put_cpu to ensure cpu_id consistency. Why get_cpu/put_cpu can ensure the consistency of the cpu_id, because get_cpu() will disable preemption so the process can send the IPI to the appropriate CPU before the context switch. #define get_cpu() ({ preempt_disable(); __smp_processor_id(); }) #define put_cpu() preempt_enable() For details, please refer to Bugzilla-Bug28287 http://e-andes.andestech.com/bugzilla5/show_bug.cgi?id=28287 Signed-off-by: CL Wang --- drivers/soc/andes/ppma.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/soc/andes/ppma.c b/drivers/soc/andes/ppma.c index 7987c020f0dd41..f752ecc2fe153a 100644 --- a/drivers/soc/andes/ppma.c +++ b/drivers/soc/andes/ppma.c @@ -22,7 +22,7 @@ EXPORT_SYMBOL(andes_probe_ppma); void andes_set_ppma(phys_addr_t phys_addr, void *va_addr, size_t size) { int cpu_num = num_online_cpus(); - int id = smp_processor_id(); + int id; int i, err; bool within_memory = pfn_valid(phys_addr >> PAGE_SHIFT); @@ -50,6 +50,7 @@ void andes_set_ppma(phys_addr_t phys_addr, void *va_addr, size_t size) * Send IPI * FIXME: we need online CPU mask, not the number */ + id = get_cpu(); for (i = 0; i < cpu_num; i++) { if (i == id) continue; @@ -64,19 +65,21 @@ void andes_set_ppma(phys_addr_t phys_addr, void *va_addr, size_t size) } sbi_andes_set_ppma(&ppma_arg); + put_cpu(); } EXPORT_SYMBOL(andes_set_ppma); void andes_free_ppma(void *addr) { int cpu_num = num_online_cpus(); - int id = smp_processor_id(); + int id; int i, err; /* * Send IPI * FIXME: we need online CPU mask, not the number */ + id = get_cpu(); for (i = 0; i < cpu_num; i++) { if (i == id) continue; @@ -90,6 +93,6 @@ void andes_free_ppma(void *addr) } sbi_andes_free_ppma(addr); - + put_cpu(); } EXPORT_SYMBOL(andes_free_ppma); From 0da0277e4a259fbad7611d20ba2d513335dab87f Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Thu, 6 Jul 2023 16:21:04 +0800 Subject: [PATCH 088/169] gpio: andes: atcgpio100: Support for the ngpios variable can be obtained from DTS. 1. Support for the ngpios variable can be obtained from DTS. 2. Start the virtual IRQ at 0. Signed-off-by: CL Wang --- drivers/gpio/gpio-atcgpio100.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-atcgpio100.c b/drivers/gpio/gpio-atcgpio100.c index 34d56415f1361c..d65532aab08de0 100644 --- a/drivers/gpio/gpio-atcgpio100.c +++ b/drivers/gpio/gpio-atcgpio100.c @@ -29,6 +29,7 @@ #define DEBOUNCE_ENABLE 0x70 #define ATCGPIO100_VIRTUAL_IRQ_BASE 0 +#define DEFAULT_PIN_NUMBER 16 struct atcgpio_priv { struct gpio_chip gc; @@ -222,6 +223,8 @@ static int atcgpio100_gpio_probe(struct platform_device *pdev) struct atcgpio_priv *priv; struct gpio_chip *gc; struct gpio_irq_chip *girq; + struct device_node *node; + u32 ngpios; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -254,6 +257,15 @@ static int atcgpio100_gpio_probe(struct platform_device *pdev) "Failed to read ID register, ATCGPIO100 is not supported.\n"); return -ENXIO; } + + node = pdev->dev.of_node; + if (!node) + return -EINVAL; + + ret = of_property_read_u32(node, "ngpios", &ngpios); + if (ret) + ngpios = DEFAULT_PIN_NUMBER; + /* disable interrupt */ GPIO_WRITEL(0x00000000UL, INT_ENABLE, priv->base); /* clear interrupt */ @@ -267,7 +279,7 @@ static int atcgpio100_gpio_probe(struct platform_device *pdev) gc->parent = &pdev->dev; gc->label = "atcgpio100"; gc->base = 0; - gc->ngpio = 16; + gc->ngpio = ngpios; gc->direction_output = atcgpio_dir_out; gc->direction_input = atcgpio_dir_in; gc->set = atcgpio_set; From 51379c9ec6762e4952b112155b55d40213e4b5a2 Mon Sep 17 00:00:00 2001 From: Alexandre Ghiti Date: Tue, 25 Jul 2023 15:22:46 +0200 Subject: [PATCH 089/169] riscv: Implement flush_cache_vmap() and add a call to flush_cache_vmap() in vmap_pfn() Andes PR : https://gitea.andestech.com/RD-SW/linux/pulls/52 When VMAP_STACK is enabled, the kernel stack will be obtained through vmalloc(). Normally, we rely on the logic in vmalloc_fault() to update stale page table entries covering the vmalloc space in a task's page tables when it first accesses the problematic region. Unfortunately, this is not sufficient when the kernel stack resides in the vmalloc region, because vmalloc_fault() is a C function that needs a stack to run. So we need to ensure that these page table entries are up to date before the MM switch. Here's our symptom: core 0: A speculative load lead the kernel stack load to the TLB before the corresponding kernel stack's page table is created. core 1: Create page table mapping of that kernel stack. core 0: After a context switch, the kernel attempts to use the stack region. However, even if the page table is correct, the stack address mapping in the TLB is invalid, leading to subsequent nested exceptions. The current workaround from upstream is to update the TLB in flush_cache_vmap(). The following are the patches we backported from upstream: Git repo : https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git Branch : mm-everything (b83cdc699b93) mm: Add a call to flush_cache_vmap() in vmap_pfn() (https://lkml.org/lkml/2023/8/9/857) Git repo : https://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux.git Branch : fixes (7e3811521dc3) riscv: Implement flush_cache_vmap() (http://lists.infradead.org/pipermail/linux-riscv/2023-July/037056.html) --- arch/riscv/include/asm/cacheflush.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index f6fbe7042f1c84..21eb0a600cf4dd 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h @@ -30,6 +30,10 @@ static inline void flush_dcache_page(struct page *page) #define flush_icache_user_page(vma, pg, addr, len) \ flush_icache_mm(vma->vm_mm, 0) +#ifdef CONFIG_64BIT +#define flush_cache_vmap(start, end) flush_tlb_kernel_range(start, end) +#endif + #ifndef CONFIG_SMP #define flush_icache_all() local_flush_icache_all() From 9e9d4a90b377b90ef9a7a7c4a5fa2125f44bd285 Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Tue, 8 Aug 2023 15:36:17 +0800 Subject: [PATCH 090/169] clocksource: andes: atcpit: Support Andes ATCPIT driver 1. The first version of the ATCPIT driver is migrated from ast520. Signed-off-by: CL Wang --- drivers/clocksource/Kconfig | 9 + drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-atcpit.c | 269 +++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) mode change 100644 => 100755 drivers/clocksource/Kconfig mode change 100644 => 100755 drivers/clocksource/Makefile create mode 100755 drivers/clocksource/timer-atcpit.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig old mode 100644 new mode 100755 index 4469e7f555e979..1f901f933d3e14 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -620,6 +620,15 @@ config GXP_TIMER Provides a driver for the timer control found on HPE GXP SOCs. This is required for all GXP SOCs. +config ATCPIT_TIMER + bool "ATCPIT timer driver" + select CLKSRC_MMIO + depends on HAS_IOMEM + select TIMER_PROBE + select TIMER_OF + help + This option enables support for Andes ATCPIT timer. + config RISCV_TIMER bool "Timer for the RISC-V platform" if COMPILE_TEST depends on GENERIC_SCHED_CLOCK && RISCV && RISCV_SBI diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile old mode 100644 new mode 100755 index 64ab547de97b9a..dffc7163c42843 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -79,6 +79,7 @@ obj-$(CONFIG_INGENIC_SYSOST) += ingenic-sysost.o obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o obj-$(CONFIG_X86_NUMACHIP) += numachip.o +obj-$(CONFIG_ATCPIT_TIMER) += timer-atcpit.o obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o obj-$(CONFIG_CLINT_TIMER) += timer-clint.o obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o diff --git a/drivers/clocksource/timer-atcpit.c b/drivers/clocksource/timer-atcpit.c new file mode 100755 index 00000000000000..d9b8e6e1b95f97 --- /dev/null +++ b/drivers/clocksource/timer-atcpit.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2005-2017 Andes Technology Corporation +/* + * Andestech ATCPIT100 Timer Device Driver Implementation + * Rick Chen, Andes Technology Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "timer-of.h" +#ifdef CONFIG_NDS32 +#include +#endif + +/* + * Definition of register offsets + */ + +/* ID and Revision Register */ +#define ID_REV 0x0 + +/* Configuration Register */ +#define CFG 0x10 + +/* Interrupt Enable Register */ +#define INT_EN 0x14 +#define CH_INT_EN(c, i) ((1<event_handler(evt); + + return IRQ_HANDLED; +} + +static struct timer_of to = { + .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE, + + .clkevt = { + .name = "atcpit100_tick", + .rating = 300, + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .set_state_shutdown = atcpit100_clkevt_shutdown, + .set_state_periodic = atcpit100_clkevt_set_periodic, + .set_state_oneshot = atcpit100_clkevt_set_oneshot, + .tick_resume = atcpit100_clkevt_shutdown, + .set_next_event = atcpit100_clkevt_next_event, + .cpumask = cpu_possible_mask, + }, + + .of_irq = { + .handler = atcpit100_timer_interrupt, + .flags = IRQF_TIMER | IRQF_IRQPOLL, + }, + + /* + * FIXME: we currently only support clocking using PCLK + * and using EXTCLK is not supported in the driver. + */ + .of_clk = { + .name = "PCLK", + } +}; + +static u64 notrace atcpit100_timer_sched_read(void) +{ + return ~readl(timer_of_base(&to) + CH1_CNT); +} + +#ifdef CONFIG_NDS32 +static void fill_vdso_need_info(struct device_node *node) +{ + struct resource timer_res; + of_address_to_resource(node, 0, &timer_res); + timer_info.mapping_base = (unsigned long)timer_res.start; + timer_info.cycle_count_down = true; + timer_info.cycle_count_reg_offset = CH1_CNT; +} +#endif + +static int atcpit100_timer_init(struct device_node *node) +{ + int ret; + u32 val; + void __iomem *base; + + ret = timer_of_init(node, &to); + if (ret) + return ret; + + base = timer_of_base(&to); + + sched_clock_register(atcpit100_timer_sched_read, 32, + timer_of_rate(&to)); + + ret = clocksource_mmio_init(base + CH1_CNT, + node->name, timer_of_rate(&to), 300, 32, + clocksource_mmio_readl_down); + + if (ret) { + pr_err("Failed to register clocksource\n"); + return ret; + } + + /* clear channel 0 timer0 interrupt */ + atcpit100_timer_clear_interrupt(base); + + clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), + TIMER_SYNC_TICKS, 0xffffffff); + atcpit100_ch0_tmr0_en(base); + atcpit100_ch1_tmr0_en(base); + atcpit100_clocksource_start(base); + atcpit100_clkevt_time_start(base); + + /* Enable channel 0 timer0 interrupt */ + val = readl(base + INT_EN); + writel(val | CH0INT0EN, base + INT_EN); + +#ifdef CONFIG_NDS32 + fill_vdso_need_info(node); +#endif + + return ret; +} + +TIMER_OF_DECLARE(atcpit100, "andestech,atcpit100", atcpit100_timer_init); From 6add1cbec8b7e262cdcdd61d1a9ad433aea807b8 Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Wed, 9 Aug 2023 10:17:54 +0800 Subject: [PATCH 091/169] clocksource: andes: atcpit: Refine ATCPIT driver 1. Create new common APIs to operate timer registers for different channels. 2. Separate the driver into clock source driver and clock event driver in order to reduce the maintenance effort. 3. Configurable via new add parameters from DTS. clock_src_ch = <0>; /* Specify a clock source driver channel (this parameter can be omitted) */ clock_evt_ch = <1>; /* Specify a clock event driver channel (this parameter can be omitted) */ pit_clk_src = <1>; /* 0:external clock 1:P clock */ 4. In DTS, a source clock must be specified for the driver, as shown below. pclk: clock-pclk { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <60000000>; clock-output-names = "pclk"; }; timer0: timer@f0400000 { ... clocks = <&pclk>; clock-names = "pit_clk"; } PS, The clock event driver does not support one-shot feature. Signed-off-by: CL Wang --- drivers/clocksource/timer-atcpit.c | 404 +++++++++++++++++------------ 1 file changed, 238 insertions(+), 166 deletions(-) diff --git a/drivers/clocksource/timer-atcpit.c b/drivers/clocksource/timer-atcpit.c index d9b8e6e1b95f97..c3b60304c515f8 100755 --- a/drivers/clocksource/timer-atcpit.c +++ b/drivers/clocksource/timer-atcpit.c @@ -1,9 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2005-2017 Andes Technology Corporation /* - * Andestech ATCPIT100 Timer Device Driver Implementation - * Rick Chen, Andes Technology Corporation - * + * Copyright (C) 2023 Andes Corporation */ #include @@ -11,259 +8,334 @@ #include #include #include -#include #include -#include -#include -#include #include #include #include #include #include "timer-of.h" -#ifdef CONFIG_NDS32 -#include -#endif - -/* - * Definition of register offsets - */ /* ID and Revision Register */ -#define ID_REV 0x0 +#define ID_REV 0x0 /* Configuration Register */ -#define CFG 0x10 +#define CFG 0x10 +#define NUM_PIT_CH_MSK 0x7 /* Interrupt Enable Register */ -#define INT_EN 0x14 -#define CH_INT_EN(c, i) ((1<base, pit_data->clock_evt_ch, delay); } -static int atcpit100_clkevt_set_periodic(struct clock_event_device *evt) +static inline void atcpit_clkevt_time_start(struct atcpit_data *pit_data) { - struct timer_of *to = to_timer_of(evt); - - atcpit100_clkevt_time_setup(timer_of_base(to), timer_of_period(to)); - atcpit100_clkevt_time_start(timer_of_base(to)); + atcpit_ch_en(pit_data->base, pit_data->clock_evt_ch, TIMER0, 1); +} - return 0; +static inline void atcpit_clkevt_time_stop(struct atcpit_data *pit_data) +{ + atcpit_ch_en(pit_data->base, pit_data->clock_evt_ch, TIMER0, 0); + atcpit_ch_clear_int(pit_data->base, pit_data->clock_evt_ch, TIMER0); } -static int atcpit100_clkevt_shutdown(struct clock_event_device *evt) + +static int atcpit_clkevt_set_periodic(struct clock_event_device *evt) { + struct atcpit_data *pit_data = NULL; struct timer_of *to = to_timer_of(evt); - atcpit100_clkevt_time_stop(timer_of_base(to)); + pit_data = to_atcpit_data_clkevt(evt); + + atcpit_clkevt_time_stop(pit_data); + atcpit_clkevt_time_setup(pit_data, timer_of_period(to)); + atcpit_clkevt_time_start(pit_data); return 0; } -static int atcpit100_clkevt_set_oneshot(struct clock_event_device *evt) + +static int atcpit_clkevt_shutdown(struct clock_event_device *evt) { - struct timer_of *to = to_timer_of(evt); - u32 val; + struct atcpit_data *pit_data = NULL; - writel(~0x0, timer_of_base(to) + CH0_REL); - val = readl(timer_of_base(to) + CH_EN); - writel(val | CH0TMR0EN, timer_of_base(to) + CH_EN); + pit_data = to_atcpit_data_clkevt(evt); + atcpit_clkevt_time_stop(pit_data); return 0; } -static irqreturn_t atcpit100_timer_interrupt(int irq, void *dev_id) +static irqreturn_t atcpit_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = (struct clock_event_device *)dev_id; - struct timer_of *to = to_timer_of(evt); + struct atcpit_data *pit_data = to_atcpit_data_clkevt(evt); - atcpit100_timer_clear_interrupt(timer_of_base(to)); + atcpit_ch_clear_int(pit_data->base, pit_data->clock_evt_ch, TIMER0); - evt->event_handler(evt); + if (evt->event_handler) + evt->event_handler(evt); return IRQ_HANDLED; } -static struct timer_of to = { - .flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE, - - .clkevt = { - .name = "atcpit100_tick", - .rating = 300, - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_state_shutdown = atcpit100_clkevt_shutdown, - .set_state_periodic = atcpit100_clkevt_set_periodic, - .set_state_oneshot = atcpit100_clkevt_set_oneshot, - .tick_resume = atcpit100_clkevt_shutdown, - .set_next_event = atcpit100_clkevt_next_event, - .cpumask = cpu_possible_mask, - }, - - .of_irq = { - .handler = atcpit100_timer_interrupt, - .flags = IRQF_TIMER | IRQF_IRQPOLL, - }, - - /* - * FIXME: we currently only support clocking using PCLK - * and using EXTCLK is not supported in the driver. - */ - .of_clk = { - .name = "PCLK", - } -}; - -static u64 notrace atcpit100_timer_sched_read(void) +static u64 atcpit_clksrc_read(struct clocksource *clksrc) { - return ~readl(timer_of_base(&to) + CH1_CNT); + struct atcpit_data *atcpit = NULL; + u64 src_cnt; + + atcpit = to_atcpit_data_clksrc(clksrc); + src_cnt = readl(atcpit->base + CH_CNT(atcpit->clock_src_ch)); + + return ~readl(atcpit->base + CH_CNT(atcpit->clock_src_ch)); } -#ifdef CONFIG_NDS32 -static void fill_vdso_need_info(struct device_node *node) +static int __init atcpit_clockevent_init(struct device_node *node, struct atcpit_data *pit_data) { - struct resource timer_res; - of_address_to_resource(node, 0, &timer_res); - timer_info.mapping_base = (unsigned long)timer_res.start; - timer_info.cycle_count_down = true; - timer_info.cycle_count_reg_offset = CH1_CNT; + unsigned long pit_clk_rate; + int ret = 0; + + pit_clk_rate = clk_get_rate(pit_data->src_clk); + if (!pit_clk_rate) { + pr_err("Invalid clock rate\n"); + return -EINVAL; + } + + pit_data->clkevt.name = "atcpit_timer"; + pit_data->clkevt.features = CLOCK_EVT_FEAT_PERIODIC; + pit_data->clkevt.set_state_shutdown = atcpit_clkevt_shutdown; + pit_data->clkevt.set_state_periodic = atcpit_clkevt_set_periodic; + pit_data->clkevt.tick_resume = atcpit_clkevt_shutdown; + pit_data->clkevt.rating = 200; + pit_data->irq = pit_data->irq; + pit_data->clkevt.cpumask = cpu_possible_mask; + + atcpit_ch_crtl(pit_data->base, pit_data->clock_evt_ch, pit_data->pit_clk_src, TIMER_32); + atcpit_ch_clear_int(pit_data->base, pit_data->clock_evt_ch, TIMER0); + atcpit_ch_int_en(pit_data->base, pit_data->clock_evt_ch, TIMER0, 1); + ret = request_irq(pit_data->irq, atcpit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "atcpit_timer", &pit_data->clkevt); + if (ret) { + pr_err("Unable to register interrupt for atcpit_timer\n"); + goto ERR_EXIT; + } + + clockevents_config_and_register(&pit_data->clkevt, pit_clk_rate, 0xf, 0xfffffffe); + +ERR_EXIT: + return ret; } -#endif -static int atcpit100_timer_init(struct device_node *node) + +static int __init atcpit_clocksource_init(struct device_node *node, struct atcpit_data *pit_data) { - int ret; - u32 val; - void __iomem *base; + unsigned long pit_clk_rate; + int ret = 0; - ret = timer_of_init(node, &to); - if (ret) - return ret; + pit_clk_rate = clk_get_rate(pit_data->src_clk); + if (!pit_clk_rate) { + pr_err("Invalid clock rate\n"); + return -EINVAL; + } - base = timer_of_base(&to); + pit_data->clksrc.mask = CLOCKSOURCE_MASK(32); + pit_data->clksrc.name = "atcpit_clocksource"; + pit_data->clksrc.rating = 200; + pit_data->clksrc.read = atcpit_clksrc_read; + pit_data->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS; - sched_clock_register(atcpit100_timer_sched_read, 32, - timer_of_rate(&to)); + atcpit_ch_crtl(pit_data->base, pit_data->clock_src_ch, pit_data->pit_clk_src, TIMER_32); + atcpit_ch_reload(pit_data->base, pit_data->clock_src_ch, 0xFFFFFFFF); + atcpit_ch_en(pit_data->base, pit_data->clock_src_ch, TIMER0, 1); - ret = clocksource_mmio_init(base + CH1_CNT, - node->name, timer_of_rate(&to), 300, 32, - clocksource_mmio_readl_down); + ret = clocksource_register_hz(&pit_data->clksrc, pit_clk_rate); - if (ret) { + if (ret) pr_err("Failed to register clocksource\n"); - return ret; + + return ret; +} + +static int __init atcpit_timer_init(struct device_node *node) +{ + struct atcpit_data *pit_data = NULL; + int ret = 0; + u32 val; + + pit_data = kzalloc(sizeof(*pit_data), GFP_KERNEL); + if (!pit_data) + return -ENOMEM; + + pit_data->clock_evt_ch = 0xFF; + pit_data->clock_src_ch = 0xFF; + + pit_data->base = of_iomap(node, 0); + if (!pit_data->base) { + pr_err("Invalid io addr\n"); + ret = -ENXIO; + goto ERR_EXIT; + } + + if (atcpit_check_pit_id(pit_data->base)) { + pr_err("Invalid pit id\n"); + goto ERR_EXIT; } + pit_data->pit_num_ch = atcpit_get_num_ch(pit_data->base); - /* clear channel 0 timer0 interrupt */ - atcpit100_timer_clear_interrupt(base); + pit_data->src_clk = of_clk_get(node, 0); + if (IS_ERR(pit_data->src_clk)) { + pr_err("Invalid src_clk\n"); + ret = PTR_ERR(pit_data->src_clk); + goto ERR_EXIT; + } - clockevents_config_and_register(&to.clkevt, timer_of_rate(&to), - TIMER_SYNC_TICKS, 0xffffffff); - atcpit100_ch0_tmr0_en(base); - atcpit100_ch1_tmr0_en(base); - atcpit100_clocksource_start(base); - atcpit100_clkevt_time_start(base); + pit_data->irq = irq_of_parse_and_map(node, 0); + if (!pit_data->irq) { + pr_err("Invalid irq\n"); + ret = -EINVAL; + goto ERR_EXIT; + } - /* Enable channel 0 timer0 interrupt */ - val = readl(base + INT_EN); - writel(val | CH0INT0EN, base + INT_EN); + ret = of_property_read_u32(node, "pit_clk_src", &val); + if (ret || val > PIT_CLK_SRC_PCLK) { + pr_err("Invalid pit clock source\n"); + goto ERR_EXIT; + } + pit_data->pit_clk_src = val; + + ret = of_property_read_u32(node, "clock_src_ch", &val); + if (!ret) { + if (val < pit_data->pit_num_ch) { + pit_data->clock_src_ch = val; + ret = atcpit_clocksource_init(node, pit_data); + if (ret) + goto ERR_EXIT; + } else { + pr_err("Invalid clock_src_ch:%d\n", val); + } + } -#ifdef CONFIG_NDS32 - fill_vdso_need_info(node); -#endif + ret = of_property_read_u32(node, "clock_evt_ch", &val); + if (!ret) { + if (val < pit_data->pit_num_ch) { + pit_data->clock_evt_ch = val; + ret = atcpit_clockevent_init(node, pit_data); + if (ret) + goto ERR_EXIT; + } else { + pr_err("Invalid clock_evt_ch:%d\n", val); + } + } + + return ret; +ERR_EXIT: + kfree(pit_data); return ret; } -TIMER_OF_DECLARE(atcpit100, "andestech,atcpit100", atcpit100_timer_init); +TIMER_OF_DECLARE(andes_atcpit, "andestech,atcpit100", atcpit_timer_init); From f4ee63c538baa9db1d98def92197dba10b7325f9 Mon Sep 17 00:00:00 2001 From: CL Chin-Long Wang Date: Wed, 16 Aug 2023 09:38:07 +0800 Subject: [PATCH 092/169] clocksource: andes: atcpit: Refine the source code of the atcpit driver. Please refer to the following link for the detailed reason for the change. https://gitea.andestech.com/RD-SW/linux/pulls/53 Signed-off-by: CL Wang --- drivers/clocksource/Kconfig | 6 ++-- drivers/clocksource/Makefile | 2 +- drivers/clocksource/timer-atcpit.c | 48 ++++++++++++++++++++---------- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 1f901f933d3e14..ca19d376f1aab6 100755 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -620,14 +620,14 @@ config GXP_TIMER Provides a driver for the timer control found on HPE GXP SOCs. This is required for all GXP SOCs. -config ATCPIT_TIMER - bool "ATCPIT timer driver" +config ATCPIT100_TIMER + bool "ATCPIT100 timer driver" select CLKSRC_MMIO depends on HAS_IOMEM select TIMER_PROBE select TIMER_OF help - This option enables support for Andes ATCPIT timer. + This option enables support for Andes ATCPIT100 timer. config RISCV_TIMER bool "Timer for the RISC-V platform" if COMPILE_TEST diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index dffc7163c42843..e6cecc08983e44 100755 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -79,7 +79,7 @@ obj-$(CONFIG_INGENIC_SYSOST) += ingenic-sysost.o obj-$(CONFIG_INGENIC_TIMER) += ingenic-timer.o obj-$(CONFIG_CLKSRC_ST_LPC) += clksrc_st_lpc.o obj-$(CONFIG_X86_NUMACHIP) += numachip.o -obj-$(CONFIG_ATCPIT_TIMER) += timer-atcpit.o +obj-$(CONFIG_ATCPIT100_TIMER) += timer-atcpit.o obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o obj-$(CONFIG_CLINT_TIMER) += timer-clint.o obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o diff --git a/drivers/clocksource/timer-atcpit.c b/drivers/clocksource/timer-atcpit.c index c3b60304c515f8..1fd6ce321230f5 100755 --- a/drivers/clocksource/timer-atcpit.c +++ b/drivers/clocksource/timer-atcpit.c @@ -17,6 +17,9 @@ /* ID and Revision Register */ #define ID_REV 0x0 +#define ATCPIT_ID 0x03031 +#define ATCPIT_ID_MSK 0xFFFFF000 +#define ATCPIT_ID_SFT 12 /* Configuration Register */ #define CFG 0x10 @@ -24,11 +27,11 @@ /* Interrupt Enable Register */ #define INT_EN 0x14 -#define CH_INT_EN(c, t) (t << (4 * c)) +#define CH_INT_EN(c, t) (t << (c * 4)) /* Interrupt Status Register */ #define INT_STA 0x18 -#define CH_INT_STA(c, t) (t << (4 * c)) +#define CH_INT_STA(c, t) (t << (c * 4)) /* Channel Enable Register */ #define CH_EN_REG 0x1C @@ -44,19 +47,19 @@ #define TIMER2 4 #define TIMER3 8 -/* Channel clock source , bit 3 , 0:External clock , 1:APB clock */ +/* Channel clock source, bit 3, 0:External clock, 1:APB clock */ #define PIT_CLK_SRC_PCLK 1 #define PIT_CLK_SRC_EXT 0 -/* Channel mode , bit 0~2 */ +/* Channel mode, bit 0~2 */ #define TIMER_32 0x1 #define TIMER_16 0x2 #define TIMER_8 0x3 -/* Channel 0 , 1 Reload Register */ +/* Channel 0, 1 Reload Register */ #define CH_REL(ch) (0x24 + (ch * 0x10)) -/* Channel 0 , 1 Counter Register */ +/* Channel 0, 1 Counter Register */ #define CH_CNT(ch) (0x28 + (ch * 0x10)) #define to_atcpit_data_clksrc(x) \ @@ -77,18 +80,22 @@ struct atcpit_data { u8 irq; }; -#define atcpit_check_pit_id(base) \ - ((readl(base + ID_REV) & 0xFFFFF000) != 0x3031000) -#define atcpit_get_num_ch(base) \ - (readl(base + CFG) & NUM_PIT_CH_MSK) +static inline u8 atcpit_check_pit_id(void __iomem *base) +{ + return ((readl(base + ID_REV) & ATCPIT_ID_MSK) >> ATCPIT_ID_SFT) == ATCPIT_ID; +} + +static inline u8 atcpit_get_num_ch(void __iomem *base) +{ + return readl(base + CFG) & NUM_PIT_CH_MSK; +} static inline void atcpit_ch_reload(void __iomem *base, u8 ch, u32 reload) { writel(reload, base + CH_REL(ch)); } -static void atcpit_ch_crtl(void __iomem *base, u8 ch, u8 ch_clksrc, - u8 ch_mode) +static void atcpit_ch_crtl(void __iomem *base, u8 ch, u8 ch_clksrc, u8 ch_mode) { u32 ch_ctrl; @@ -186,10 +193,8 @@ static irqreturn_t atcpit_timer_interrupt(int irq, void *dev_id) static u64 atcpit_clksrc_read(struct clocksource *clksrc) { struct atcpit_data *atcpit = NULL; - u64 src_cnt; atcpit = to_atcpit_data_clksrc(clksrc); - src_cnt = readl(atcpit->base + CH_CNT(atcpit->clock_src_ch)); return ~readl(atcpit->base + CH_CNT(atcpit->clock_src_ch)); } @@ -262,6 +267,7 @@ static int __init atcpit_clocksource_init(struct device_node *node, struct atcpi static int __init atcpit_timer_init(struct device_node *node) { + int (*read_fixup)(void __iomem *addr, unsigned int val, unsigned int shift_bits); struct atcpit_data *pit_data = NULL; int ret = 0; u32 val; @@ -280,10 +286,20 @@ static int __init atcpit_timer_init(struct device_node *node) goto ERR_EXIT; } - if (atcpit_check_pit_id(pit_data->base)) { - pr_err("Invalid pit id\n"); + /* Check ID register */ + read_fixup = symbol_get(readl_fixup); + if (read_fixup != NULL) { + ret = read_fixup(pit_data->base, ATCPIT_ID, 12); + symbol_put(readl_fixup); + } else { + ret = atcpit_check_pit_id(pit_data->base); + } + if (!ret) { + pr_err("Invalid ID register, ATCPIT is not supported\n"); + ret = -ENXIO; goto ERR_EXIT; } + pit_data->pit_num_ch = atcpit_get_num_ch(pit_data->base); pit_data->src_clk = of_clk_get(node, 0); From 76868e276c31826179504615fb01c32ea89d61a6 Mon Sep 17 00:00:00 2001 From: Khem Raj Date: Mon, 23 Jan 2023 13:04:10 -0800 Subject: [PATCH 093/169] perf cpumap: Make counter as unsigned ints These are loop counters which is inherently unsigned. Therefore make them unsigned. Moreover it also fixes alloc-size-larger-than error with gcc-13, where malloc can be called with (-1) due to tmp_len being an int type. Fixes | cpumap.c:366:20: error: argument 1 range [18446744065119617024, 18446744073709551612] exceeds maximum object size 9223372036854775807 [-Werror=alloc-size-larger-than=] | 366 | tmp_cpus = malloc(tmp_len * sizeof(struct perf_cpu)); | | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Signed-off-by: Khem Raj Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Arnaldo Carvalho de Melo Cc: Mark Rutland Cc: Alexander Shishkin Cc: Jiri Olsa Cc: Namhyung Kim Upstream-Status: Submitted [https://lore.kernel.org/linux-perf-users/20230123211310.127532-1-raj.khem@gmail.com/T/#u] --- tools/lib/perf/cpumap.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/lib/perf/cpumap.c b/tools/lib/perf/cpumap.c index 6cd0be7c1bb438..d960880dd903c6 100644 --- a/tools/lib/perf/cpumap.c +++ b/tools/lib/perf/cpumap.c @@ -351,8 +351,8 @@ struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, struct perf_cpu_map *other) { struct perf_cpu *tmp_cpus; - int tmp_len; - int i, j, k; + unsigned int tmp_len; + unsigned int i, j, k; struct perf_cpu_map *merged; if (perf_cpu_map__is_subset(orig, other)) @@ -369,7 +369,7 @@ struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, /* Standard merge algorithm from wikipedia */ i = j = k = 0; - while (i < orig->nr && j < other->nr) { + while (i < (unsigned int)orig->nr && j < (unsigned int)other->nr) { if (orig->map[i].cpu <= other->map[j].cpu) { if (orig->map[i].cpu == other->map[j].cpu) j++; @@ -378,10 +378,10 @@ struct perf_cpu_map *perf_cpu_map__merge(struct perf_cpu_map *orig, tmp_cpus[k++] = other->map[j++]; } - while (i < orig->nr) + while (i < (unsigned int)orig->nr) tmp_cpus[k++] = orig->map[i++]; - while (j < other->nr) + while (j < (unsigned int)other->nr) tmp_cpus[k++] = other->map[j++]; assert(k <= tmp_len); From 519ad78fdc3279fcc4201b91968ffc0c7d06cd52 Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Thu, 10 Aug 2023 18:09:54 +0800 Subject: [PATCH 094/169] riscv: andes: Add AX45MPV 4-core device tree Simply copy the ax45mp_c4_d_dsp_ae350.dts and add "v1p0" to riscv,isa of each CPU node. Signed-off-by: Yu Chien Peter Lin --- arch/riscv/boot/dts/andes/Makefile | 1 + .../boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts | 325 ++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts diff --git a/arch/riscv/boot/dts/andes/Makefile b/arch/riscv/boot/dts/andes/Makefile index 1cb9312dd28f46..c165e1ab3d0a25 100644 --- a/arch/riscv/boot/dts/andes/Makefile +++ b/arch/riscv/boot/dts/andes/Makefile @@ -13,6 +13,7 @@ dtb-$(CONFIG_PLAT_AE350) += a25mp_c4_d_dsp_ae350.dtb dtb-$(CONFIG_PLAT_AE350) += a45mp_c4_d_dsp_ae350.dtb dtb-$(CONFIG_PLAT_AE350) += ax25mp_c4_d_dsp_ae350.dtb dtb-$(CONFIG_PLAT_AE350) += ax45mp_c4_d_dsp_ae350.dtb +dtb-$(CONFIG_PLAT_AE350) += ax45mpv_c4_d_dsp_ae350.dtb dtb-$(CONFIG_PLAT_AE350) += ax45mp_c8_ae350.dtb # FPGA : VU19P diff --git a/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts new file mode 100644 index 00000000000000..c0d2903083f8c1 --- /dev/null +++ b/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts @@ -0,0 +1,325 @@ +/dts-v1/; + +/ { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,ae350"; + model = "andestech,ax45"; + + aliases { + uart0 = "/soc/serial@f0300000"; + spi0 = "/soc/spi@f0b00000"; + }; + + chosen { + bootargs = "console=ttyS0,38400n8 debug loglevel=7 earlycon=sbi"; + stdout-path = "uart0:38400n8"; + }; + + cpus { + #address-cells = <0x01>; + #size-cells = <0x00>; + timebase-frequency = <0x3938700>; + + cpu@0 { + device_type = "cpu"; + reg = <0x00>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0v1p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x02>; + }; + }; + + cpu@1 { + device_type = "cpu"; + reg = <0x01>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0v1p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x03>; + }; + }; + + cpu@2 { + device_type = "cpu"; + reg = <0x02>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0v1p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x04>; + }; + }; + + cpu@3 { + device_type = "cpu"; + reg = <0x03>; + status = "okay"; + compatible = "riscv"; + riscv,isa = "rv64i2p0m2p0a2p0f2p0d2p0c2p0v1p0xv5-1p1xdsp0p0"; + riscv,priv-major = <0x01>; + riscv,priv-minor = <0x0a>; + mmu-type = "riscv,sv48"; + clock-frequency = <0x3938700>; + i-cache-size = <0x8000>; + i-cache-sets = <0x100>; + i-cache-line-size = <0x40>; + i-cache-block-size = <0x40>; + d-cache-size = <0x8000>; + d-cache-sets = <0x80>; + d-cache-line-size = <0x40>; + d-cache-block-size = <0x40>; + next-level-cache = <0x01>; + + interrupt-controller { + #interrupt-cells = <0x01>; + interrupt-controller; + compatible = "riscv,cpu-intc"; + phandle = <0x05>; + }; + }; + }; + + l2-cache@e0500000 { + compatible = "cache"; + cache-level = <0x02>; + cache-size = <0x80000>; + reg = <0x00 0xe0500000 0x00 0x10000>; + andes,inst-prefetch = <0x03>; + andes,data-prefetch = <0x03>; + andes,tag-ram-ctl = <0x00 0x00>; + andes,data-ram-ctl = <0x00 0x00>; + phandle = <0x01>; + }; + + memory@0 { + reg = <0x00 0x00 0x00 0x80000000>; + device_type = "memory"; + }; + + soc { + #address-cells = <0x02>; + #size-cells = <0x02>; + compatible = "andestech,riscv-ae350-soc","simple-bus"; + ranges; + + interrupt-controller@e4000000 { + compatible = "riscv,plic0"; + reg = <0x00 0xe4000000 0x00 0x2000000>; + interrupts-extended = <0x02 0x0b 0x02 0x09 0x03 0x0b 0x03 0x09 0x04 0x0b 0x04 0x09 0x05 0x0b 0x05 0x09>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x47>; + phandle = <0x06>; + }; + + interrupt-controller@e6400000 { + compatible = "riscv,plic1"; + reg = <0x00 0xe6400000 0x00 0x400000>; + interrupts-extended = <0x02 0x03 0x03 0x03 0x04 0x03 0x05 0x03>; + interrupt-controller; + #address-cells = <0x02>; + #interrupt-cells = <0x02>; + riscv,ndev = <0x04>; + }; + + plmt0@e6000000 { + compatible = "riscv,plmt0"; + reg = <0x00 0xe6000000 0x00 0x100000>; + interrupts-extended = <0x02 0x07 0x03 0x07 0x04 0x07 0x05 0x07>; + }; + + virt_100mhz { + compatible = "fixed-clock"; + #clock-cells = <0x00>; + clock-frequency = <0x5f5e100>; + phandle = <0x08>; + }; + + timer@f0400000 { + compatible = "andestech,atcpit100"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + }; + + pwm@f0400000 { + compatible = "andestech,atcpit100-pwm"; + reg = <0x00 0xf0400000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + pwm-cells = <0x02>; + }; + + wdt@f0500000 { + compatible = "andestech,atcwdt200"; + reg = <0x00 0xf0500000 0x00 0x1000>; + interrupts = <0x03 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x3938700>; + }; + + serial@f0300000 { + compatible = "andestech,uart16550\0ns16550a"; + reg = <0x00 0xf0300000 0x00 0x1000>; + interrupts = <0x09 0x04>; + interrupt-parent = <0x06>; + clock-frequency = <0x12c0000>; + current-speed = <0x9600>; + reg-shift = <0x02>; + reg-offset = <0x20>; + reg-io-width = <0x04>; + no-loopback-test = <0x01>; + }; + + rtc@f0600000 { + compatible = "andestech,atcrtc100"; + reg = <0x00 0xf0600000 0x00 0x1000>; + interrupts = <0x01 0x04 0x02 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + gpio@f0700000 { + compatible = "andestech,atcgpio100"; + reg = <0x00 0xf0700000 0x00 0x1000>; + interrupts = <0x07 0x04>; + interrupt-parent = <0x06>; + wakeup-source; + }; + + i2c@f0a00000 { + compatible = "andestech,atciic100"; + reg = <0x00 0xf0a00000 0x00 0x1000>; + interrupts = <0x06 0x04>; + interrupt-parent = <0x02>; + wakeup-source; + }; + + mac@e0100000 { + compatible = "andestech,atmac100"; + reg = <0x00 0xe0100000 0x00 0x1000>; + interrupts = <0x13 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + smu@f0100000 { + compatible = "andestech,atcsmu"; + reg = <0x00 0xf0100000 0x00 0x1000>; + }; + + mmc@f0e00000 { + compatible = "andestech,atfsdc010g"; + reg = <0x00 0xf0e00000 0x00 0x1000>; + interrupts = <0x12 0x04>; + interrupt-parent = <0x06>; + clock-freq-min-max = <0x61a80 0x5f5e100>; + max-frequency = <0x5f5e100>; + fifo-depth = <0x10>; + dmas = <0x07 0x09>; + dma-names = "rxtx"; + dma-coherent; + }; + + dma@f0c00000 { + compatible = "andestech,atcdmac300g"; + reg = <0x00 0xf0c00000 0x00 0x1000>; + interrupts = <0x0a 0x04>; + interrupt-parent = <0x06>; + dma-channels = <0x08>; + #dma-cells = <0x01>; + dma-coherent; + phandle = <0x07>; + }; + + lcd@e0200000 { + compatible = "andestech,atflcdc100"; + reg = <0x00 0xe0200000 0x00 0x1000>; + interrupts = <0x14 0x04>; + interrupt-parent = <0x06>; + dma-coherent; + }; + + pmu { + compatible = "riscv,andes-pmu"; + device_type = "pmu"; + }; + + spi@f0b00000 { + compatible = "andestech,atcspi200"; + reg = <0x00 0xf0b00000 0x00 0x1000>; + interrupts = <0x04 0x04>; + interrupt-parent = <0x06>; + #address-cells = <0x01>; + #size-cells = <0x00>; + num-cs = <0x01>; + clocks = <0x08>; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x00>; + spi-max-frequency = <0x2faf080>; + spi-cpol; + spi-cpha; + }; + }; + }; +}; From 55f0a9ba91b3345283b248da22e1a8157b39d1ef Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Mon, 7 Aug 2023 14:59:09 +0800 Subject: [PATCH 095/169] riscv: Move Andes DSP context switch out of __switch_to_aux This is for easier renaming __switch_to_{aux => fpu}() and introducing vector context save/restore functions in the following patches. Signed-off-by: Yu Chien Peter Lin --- arch/riscv/include/asm/switch_to.h | 42 ++++++++++++++++-------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h index 12010f8bd8945f..5e54bcfb0baefc 100644 --- a/arch/riscv/include/asm/switch_to.h +++ b/arch/riscv/include/asm/switch_to.h @@ -47,6 +47,17 @@ static inline void fstate_restore(struct task_struct *task, } } +static inline void __switch_to_aux(struct task_struct *prev, + struct task_struct *next) +{ + struct pt_regs *regs; + + regs = task_pt_regs(prev); + if (unlikely(regs->status & SR_SD)) + fstate_save(prev, regs); + fstate_restore(next, task_pt_regs(next)); +} + static __always_inline bool has_fpu(void) { return static_branch_likely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_FPU]); @@ -55,6 +66,7 @@ static __always_inline bool has_fpu(void) static __always_inline bool has_fpu(void) { return false; } #define fstate_save(task, regs) do { } while (0) #define fstate_restore(task, regs) do { } while (0) +#define __switch_to_aux(__prev, __next) do { } while (0) #endif /* CONFIG_FPU */ #ifdef CONFIG_DSP @@ -68,6 +80,13 @@ static inline void dspstate_restore(struct task_struct *task) csr_write(CSR_UCODE, task->thread.dspstate.ucode); } +static inline void __switch_to_dsp(struct task_struct *prev, + struct task_struct *next) +{ + dspstate_save(prev); + dspstate_restore(next); +} + static __always_inline bool has_dsp(void) { return static_branch_likely(&riscv_isa_ext_keys[ANDES_ISA_EXT_KEY_DSP]); @@ -76,26 +95,9 @@ static __always_inline bool has_dsp(void) static __always_inline bool has_dsp(void) { return false; } #define dspstate_save(task) do { } while (0) #define dspstate_restore(task) do { } while (0) +#define __switch_to_dsp(__prev, __next) do { } while (0) #endif /* CONFIG_DSP */ -static inline void __switch_to_aux(struct task_struct *prev, - struct task_struct *next) -{ -#ifdef CONFIG_FPU - struct pt_regs *regs; - regs = task_pt_regs(prev); - if (unlikely(regs->status & SR_SD)) - fstate_save(prev, regs); - fstate_restore(next, task_pt_regs(next)); -#endif /* CONFIG_FPU */ -#ifdef CONFIG_DSP - if (has_dsp()) { - dspstate_save(prev); - dspstate_restore(next); - } -#endif /* CONFIG_DSP */ -} - extern struct task_struct *__switch_to(struct task_struct *, struct task_struct *); @@ -103,8 +105,10 @@ extern struct task_struct *__switch_to(struct task_struct *, do { \ struct task_struct *__prev = (prev); \ struct task_struct *__next = (next); \ - if (has_fpu() || has_dsp()) \ + if (has_fpu()) \ __switch_to_aux(__prev, __next); \ + if (has_dsp()) \ + __switch_to_dsp(__prev, __next); \ ((last) = __switch_to(__prev, __next)); \ } while (0) From d621f79c10a70766cf4632c526c75f8f48c59316 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Tue, 29 Nov 2022 15:34:45 +0100 Subject: [PATCH 096/169] RISC-V: Improve use of isa2hwcap[] Improve isa2hwcap[] by removing it from static storage, as riscv_fill_hwcap() is only called once, and by reducing its size from 256 bytes to 26. The latter improvement is possible because isa2hwcap[] will never be indexed with capital letters and we can precompute the offsets from 'a'. No functional change intended. Signed-off-by: Andrew Jones Reviewed-by: Conor Dooley Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/20221129143447.49714-2-ajones@ventanamicro.com Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/cpufeature.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 64b7b70b4d8c14..20086163bc7b9e 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -77,15 +77,15 @@ void __init riscv_fill_hwcap(void) const char *isa; char print_str[NUM_ALPHA_EXTS + 1]; int i, j, rc; - static unsigned long isa2hwcap[256] = {0}; + unsigned long isa2hwcap[26] = {0}; unsigned long hartid; - isa2hwcap['i'] = isa2hwcap['I'] = COMPAT_HWCAP_ISA_I; - isa2hwcap['m'] = isa2hwcap['M'] = COMPAT_HWCAP_ISA_M; - isa2hwcap['a'] = isa2hwcap['A'] = COMPAT_HWCAP_ISA_A; - isa2hwcap['f'] = isa2hwcap['F'] = COMPAT_HWCAP_ISA_F; - isa2hwcap['d'] = isa2hwcap['D'] = COMPAT_HWCAP_ISA_D; - isa2hwcap['c'] = isa2hwcap['C'] = COMPAT_HWCAP_ISA_C; + isa2hwcap['i' - 'a'] = COMPAT_HWCAP_ISA_I; + isa2hwcap['m' - 'a'] = COMPAT_HWCAP_ISA_M; + isa2hwcap['a' - 'a'] = COMPAT_HWCAP_ISA_A; + isa2hwcap['f' - 'a'] = COMPAT_HWCAP_ISA_F; + isa2hwcap['d' - 'a'] = COMPAT_HWCAP_ISA_D; + isa2hwcap['c' - 'a'] = COMPAT_HWCAP_ISA_C; elf_hwcap = 0; @@ -199,8 +199,10 @@ void __init riscv_fill_hwcap(void) if (unlikely(ext_err)) continue; if (!ext_long) { - this_hwcap |= isa2hwcap[(unsigned char)(*ext)]; - set_bit(*ext - 'a', this_isa); + int nr = *ext - 'a'; + + this_hwcap |= isa2hwcap[nr]; + set_bit(nr, this_isa); } else { /* sorted alphabetically */ SET_ISA_EXT_MAP("sscofpmf", RISCV_ISA_EXT_SSCOFPMF); From 3cb20fe09562bb5a231a4bf4e107e652b8652889 Mon Sep 17 00:00:00 2001 From: Guo Ren Date: Mon, 5 Jun 2023 11:06:58 +0000 Subject: [PATCH 097/169] riscv: Rename __switch_to_aux() -> fpu The name of __switch_to_aux() is not clear and rename it with the determine function: __switch_to_fpu(). Next we could add other regs' switch. Signed-off-by: Guo Ren Signed-off-by: Guo Ren Signed-off-by: Greentime Hu Reviewed-by: Anup Patel Reviewed-by: Palmer Dabbelt Signed-off-by: Andy Chiu Tested-by: Heiko Stuebner Reviewed-by: Heiko Stuebner Reviewed-by: Conor Dooley --- arch/riscv/include/asm/switch_to.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h index 5e54bcfb0baefc..0fad1419388f3a 100644 --- a/arch/riscv/include/asm/switch_to.h +++ b/arch/riscv/include/asm/switch_to.h @@ -47,7 +47,7 @@ static inline void fstate_restore(struct task_struct *task, } } -static inline void __switch_to_aux(struct task_struct *prev, +static inline void __switch_to_fpu(struct task_struct *prev, struct task_struct *next) { struct pt_regs *regs; @@ -66,7 +66,7 @@ static __always_inline bool has_fpu(void) static __always_inline bool has_fpu(void) { return false; } #define fstate_save(task, regs) do { } while (0) #define fstate_restore(task, regs) do { } while (0) -#define __switch_to_aux(__prev, __next) do { } while (0) +#define __switch_to_fpu(__prev, __next) do { } while (0) #endif /* CONFIG_FPU */ #ifdef CONFIG_DSP @@ -106,7 +106,7 @@ do { \ struct task_struct *__prev = (prev); \ struct task_struct *__next = (next); \ if (has_fpu()) \ - __switch_to_aux(__prev, __next); \ + __switch_to_fpu(__prev, __next); \ if (has_dsp()) \ __switch_to_dsp(__prev, __next); \ ((last) = __switch_to(__prev, __next)); \ From 7c9f4a47586d00e48a4a0edc67fd5fe304b809fd Mon Sep 17 00:00:00 2001 From: Guo Ren Date: Mon, 5 Jun 2023 11:06:59 +0000 Subject: [PATCH 098/169] riscv: Extending cpufeature.c to detect V-extension Add V-extension into riscv_isa_ext_keys array and detect it with isa string parsing. Signed-off-by: Guo Ren Signed-off-by: Guo Ren Signed-off-by: Greentime Hu Suggested-by: Vineet Gupta Co-developed-by: Andy Chiu Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Reviewed-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230605110724.21391-3-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/hwcap.h | 4 ++++ arch/riscv/include/asm/vector.h | 26 ++++++++++++++++++++++++++ arch/riscv/include/uapi/asm/hwcap.h | 1 + arch/riscv/kernel/cpufeature.c | 11 +++++++++++ 4 files changed, 42 insertions(+) create mode 100644 arch/riscv/include/asm/vector.h diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h index ef9c47f067218f..ddb9ecb20426af 100644 --- a/arch/riscv/include/asm/hwcap.h +++ b/arch/riscv/include/asm/hwcap.h @@ -35,6 +35,7 @@ extern unsigned long elf_hwcap; #define RISCV_ISA_EXT_m ('m' - 'a') #define RISCV_ISA_EXT_s ('s' - 'a') #define RISCV_ISA_EXT_u ('u' - 'a') +#define RISCV_ISA_EXT_v ('v' - 'a') /* * These macros represent the logical IDs of each multi-letter RISC-V ISA @@ -69,6 +70,7 @@ extern unsigned long elf_hwcap; */ enum riscv_isa_ext_key { RISCV_ISA_EXT_KEY_FPU, /* For 'F' and 'D' */ + RISCV_ISA_EXT_KEY_VECTOR, RISCV_ISA_EXT_KEY_ZIHINTPAUSE, RISCV_ISA_EXT_KEY_SVINVAL, RISCV_ISA_EXT_KEY_SVNAPOT, @@ -92,6 +94,8 @@ static __always_inline int riscv_isa_ext2key(int num) return RISCV_ISA_EXT_KEY_FPU; case RISCV_ISA_EXT_d: return RISCV_ISA_EXT_KEY_FPU; + case RISCV_ISA_EXT_v: + return RISCV_ISA_EXT_KEY_VECTOR; case RISCV_ISA_EXT_ZIHINTPAUSE: return RISCV_ISA_EXT_KEY_ZIHINTPAUSE; case RISCV_ISA_EXT_SVINVAL: diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h new file mode 100644 index 00000000000000..2adab02cbb0842 --- /dev/null +++ b/arch/riscv/include/asm/vector.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 SiFive + */ + +#ifndef __ASM_RISCV_VECTOR_H +#define __ASM_RISCV_VECTOR_H + +#include + +#ifdef CONFIG_RISCV_ISA_V + +#include + +static __always_inline bool has_vector(void) +{ + return static_branch_unlikely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_VECTOR]); +} + +#else /* ! CONFIG_RISCV_ISA_V */ + +static __always_inline bool has_vector(void) { return false; } + +#endif /* CONFIG_RISCV_ISA_V */ + +#endif /* ! __ASM_RISCV_VECTOR_H */ diff --git a/arch/riscv/include/uapi/asm/hwcap.h b/arch/riscv/include/uapi/asm/hwcap.h index 46dc3f5ee99f96..c52bb7bbbabe93 100644 --- a/arch/riscv/include/uapi/asm/hwcap.h +++ b/arch/riscv/include/uapi/asm/hwcap.h @@ -21,5 +21,6 @@ #define COMPAT_HWCAP_ISA_F (1 << ('F' - 'A')) #define COMPAT_HWCAP_ISA_D (1 << ('D' - 'A')) #define COMPAT_HWCAP_ISA_C (1 << ('C' - 'A')) +#define COMPAT_HWCAP_ISA_V (1 << ('V' - 'A')) #endif /* _UAPI_ASM_RISCV_HWCAP_H */ diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 20086163bc7b9e..08e0e10835406a 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -86,6 +86,7 @@ void __init riscv_fill_hwcap(void) isa2hwcap['f' - 'a'] = COMPAT_HWCAP_ISA_F; isa2hwcap['d' - 'a'] = COMPAT_HWCAP_ISA_D; isa2hwcap['c' - 'a'] = COMPAT_HWCAP_ISA_C; + isa2hwcap['v' - 'a'] = COMPAT_HWCAP_ISA_V; elf_hwcap = 0; @@ -242,6 +243,16 @@ void __init riscv_fill_hwcap(void) elf_hwcap &= ~COMPAT_HWCAP_ISA_F; } + if (elf_hwcap & COMPAT_HWCAP_ISA_V) { + /* + * ISA string in device tree might have 'v' flag, but + * CONFIG_RISCV_ISA_V is disabled in kernel. + * Clear V flag in elf_hwcap if CONFIG_RISCV_ISA_V is disabled. + */ + if (!IS_ENABLED(CONFIG_RISCV_ISA_V)) + elf_hwcap &= ~COMPAT_HWCAP_ISA_V; + } + memset(print_str, 0, sizeof(print_str)); for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++) if (riscv_isa[0] & BIT_MASK(i)) From de5445907cebcc649a7159f8697ac5ae0761c24d Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 5 Jun 2023 11:07:01 +0000 Subject: [PATCH 099/169] riscv: Add new csr defines related to vector extension Follow the riscv vector spec to add new csr numbers. Acked-by: Guo Ren Co-developed-by: Guo Ren Signed-off-by: Guo Ren Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Reviewed-by: Palmer Dabbelt Suggested-by: Vineet Gupta Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230605110724.21391-5-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/csr.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h index 0e571f6483d928..c3b87a7d1241ab 100644 --- a/arch/riscv/include/asm/csr.h +++ b/arch/riscv/include/asm/csr.h @@ -24,16 +24,24 @@ #define SR_FS_CLEAN _AC(0x00004000, UL) #define SR_FS_DIRTY _AC(0x00006000, UL) +#define SR_VS _AC(0x00000600, UL) /* Vector Status */ +#define SR_VS_OFF _AC(0x00000000, UL) +#define SR_VS_INITIAL _AC(0x00000200, UL) +#define SR_VS_CLEAN _AC(0x00000400, UL) +#define SR_VS_DIRTY _AC(0x00000600, UL) + #define SR_XS _AC(0x00018000, UL) /* Extension Status */ #define SR_XS_OFF _AC(0x00000000, UL) #define SR_XS_INITIAL _AC(0x00008000, UL) #define SR_XS_CLEAN _AC(0x00010000, UL) #define SR_XS_DIRTY _AC(0x00018000, UL) +#define SR_FS_VS (SR_FS | SR_VS) /* Vector and Floating-Point Unit */ + #ifndef CONFIG_64BIT -#define SR_SD _AC(0x80000000, UL) /* FS/XS dirty */ +#define SR_SD _AC(0x80000000, UL) /* FS/VS/XS dirty */ #else -#define SR_SD _AC(0x8000000000000000, UL) /* FS/XS dirty */ +#define SR_SD _AC(0x8000000000000000, UL) /* FS/VS/XS dirty */ #endif #ifdef CONFIG_64BIT @@ -297,6 +305,12 @@ #define CSR_MIMPID 0xf13 #define CSR_MHARTID 0xf14 +#define CSR_VSTART 0x8 +#define CSR_VCSR 0xf +#define CSR_VL 0xc20 +#define CSR_VTYPE 0xc21 +#define CSR_VLENB 0xc22 + #ifdef CONFIG_RISCV_M_MODE # define CSR_STATUS CSR_MSTATUS # define CSR_IE CSR_MIE From 4345ef2a0727efb82d1dba7363dc509f51fad9f9 Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 5 Jun 2023 11:07:02 +0000 Subject: [PATCH 100/169] riscv: Clear vector regfile on bootup clear vector registers on boot if kernel supports V. Signed-off-by: Greentime Hu Signed-off-by: Vineet Gupta Signed-off-by: Andy Chiu Acked-by: Conor Dooley Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Reviewed-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230605110724.21391-6-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/head.S | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index 4bf6c449d78b6a..3fd6a4bd9c3e7f 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -392,7 +392,7 @@ ENTRY(reset_regs) #ifdef CONFIG_FPU csrr t0, CSR_MISA andi t0, t0, (COMPAT_HWCAP_ISA_F | COMPAT_HWCAP_ISA_D) - beqz t0, .Lreset_regs_done + beqz t0, .Lreset_regs_done_fpu li t1, SR_FS csrs CSR_STATUS, t1 @@ -430,8 +430,31 @@ ENTRY(reset_regs) fmv.s.x f31, zero csrw fcsr, 0 /* note that the caller must clear SR_FS */ +.Lreset_regs_done_fpu: #endif /* CONFIG_FPU */ -.Lreset_regs_done: + +#ifdef CONFIG_RISCV_ISA_V + csrr t0, CSR_MISA + li t1, COMPAT_HWCAP_ISA_V + and t0, t0, t1 + beqz t0, .Lreset_regs_done_vector + + /* + * Clear vector registers and reset vcsr + * VLMAX has a defined value, VLEN is a constant, + * and this form of vsetvli is defined to set vl to VLMAX. + */ + li t1, SR_VS + csrs CSR_STATUS, t1 + csrs CSR_VCSR, x0 + vsetvli t1, x0, e8, m8, ta, ma + vmv.v.i v0, 0 + vmv.v.i v8, 0 + vmv.v.i v16, 0 + vmv.v.i v24, 0 + /* note that the caller must clear SR_VS */ +.Lreset_regs_done_vector: +#endif /* CONFIG_RISCV_ISA_V */ ret END(reset_regs) #endif /* CONFIG_RISCV_M_MODE */ From bc854a3d55b848b4b4ee4c151598bf017a977cd8 Mon Sep 17 00:00:00 2001 From: Guo Ren Date: Mon, 5 Jun 2023 11:07:03 +0000 Subject: [PATCH 101/169] riscv: Disable Vector Instructions for kernel itself Disable vector instructions execution for kernel mode at its entrances. This helps find illegal uses of vector in the kernel space, which is similar to the fpu. Signed-off-by: Guo Ren Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Co-developed-by: Han-Kuan Chen Signed-off-by: Han-Kuan Chen Co-developed-by: Greentime Hu Signed-off-by: Greentime Hu Signed-off-by: Vineet Gupta Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Reviewed-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230605110724.21391-7-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/entry.S | 6 +++--- arch/riscv/kernel/head.S | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 6662a39d13de09..e2aea3e24a8d68 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -79,10 +79,10 @@ _save_context: * Disable user-mode memory access as it should only be set in the * actual user copy routines. * - * Disable the FPU to detect illegal usage of floating point in kernel - * space. + * Disable the FPU/Vector to detect illegal usage of floating point + * or vector in kernel space. */ - li t0, SR_SUM | SR_FS + li t0, SR_SUM | SR_FS_VS REG_L s0, TASK_TI_USER_SP(tp) csrrc s1, CSR_STATUS, t0 diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index 3fd6a4bd9c3e7f..e16bb2185d5512 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -140,10 +140,10 @@ secondary_start_sbi: .option pop /* - * Disable FPU to detect illegal usage of - * floating point in kernel space + * Disable FPU & VECTOR to detect illegal usage of + * floating point or vector in kernel space */ - li t0, SR_FS + li t0, SR_FS_VS csrc CSR_STATUS, t0 /* Set trap vector to spin forever to help debug */ @@ -234,10 +234,10 @@ pmp_done: .option pop /* - * Disable FPU to detect illegal usage of - * floating point in kernel space + * Disable FPU & VECTOR to detect illegal usage of + * floating point or vector in kernel space */ - li t0, SR_FS + li t0, SR_FS_VS csrc CSR_STATUS, t0 #ifdef CONFIG_RISCV_BOOT_SPINWAIT From 34869e5c7e83bc96f09465a8742b8c36a79cdb24 Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 5 Jun 2023 11:07:04 +0000 Subject: [PATCH 102/169] riscv: Introduce Vector enable/disable helpers These are small and likely to be frequently called so implement as inline routines (vs. function call). Co-developed-by: Guo Ren Signed-off-by: Guo Ren Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Signed-off-by: Vineet Gupta Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Reviewed-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230605110724.21391-8-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/vector.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index 2adab02cbb0842..eb5834d078eb03 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -11,12 +11,23 @@ #ifdef CONFIG_RISCV_ISA_V #include +#include static __always_inline bool has_vector(void) { return static_branch_unlikely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_VECTOR]); } +static __always_inline void riscv_v_enable(void) +{ + csr_set(CSR_SSTATUS, SR_VS); +} + +static __always_inline void riscv_v_disable(void) +{ + csr_clear(CSR_SSTATUS, SR_VS); +} + #else /* ! CONFIG_RISCV_ISA_V */ static __always_inline bool has_vector(void) { return false; } From 3dac71f39e9ed140156de557ca3df7cbc1275f2a Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 5 Jun 2023 11:07:05 +0000 Subject: [PATCH 103/169] riscv: Introduce riscv_v_vsize to record size of Vector context This patch is used to detect the size of CPU vector registers and use riscv_v_vsize to save the size of all the vector registers. It assumes all harts has the same capabilities in a SMP system. If a core detects VLENB that is different from the boot core, then it warns and turns off V support for user space. Co-developed-by: Guo Ren Signed-off-by: Guo Ren Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Reviewed-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230605110724.21391-9-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/vector.h | 8 ++++++++ arch/riscv/kernel/Makefile | 1 + arch/riscv/kernel/cpufeature.c | 2 ++ arch/riscv/kernel/smpboot.c | 7 +++++++ arch/riscv/kernel/vector.c | 36 +++++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+) create mode 100644 arch/riscv/kernel/vector.c diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index eb5834d078eb03..f12a9027f41b7d 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -7,12 +7,16 @@ #define __ASM_RISCV_VECTOR_H #include +#include #ifdef CONFIG_RISCV_ISA_V #include #include +extern unsigned long riscv_v_vsize; +int riscv_v_setup_vsize(void); + static __always_inline bool has_vector(void) { return static_branch_unlikely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_VECTOR]); @@ -30,7 +34,11 @@ static __always_inline void riscv_v_disable(void) #else /* ! CONFIG_RISCV_ISA_V */ +struct pt_regs; + +static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; } static __always_inline bool has_vector(void) { return false; } +#define riscv_v_vsize (0) #endif /* CONFIG_RISCV_ISA_V */ diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile index bbc65d9c466fd4..8329f131a9011f 100644 --- a/arch/riscv/kernel/Makefile +++ b/arch/riscv/kernel/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_MMU) += vdso.o vdso/ obj-$(CONFIG_RISCV_M_MODE) += traps_misaligned.o obj-$(CONFIG_FPU) += fpu.o +obj-$(CONFIG_RISCV_ISA_V) += vector.o obj-$(CONFIG_SMP) += smpboot.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SMP) += cpu_ops.o diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index 08e0e10835406a..ec94c4dda31bf5 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -23,6 +23,7 @@ #include #include #include +#include #define NUM_ALPHA_EXTS ('z' - 'a' + 1) @@ -244,6 +245,7 @@ void __init riscv_fill_hwcap(void) } if (elf_hwcap & COMPAT_HWCAP_ISA_V) { + riscv_v_setup_vsize(); /* * ISA string in device tree might have 'v' flag, but * CONFIG_RISCV_ISA_V is disabled in kernel. diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c index ddb2afba6d2555..67ae124db5a4c0 100644 --- a/arch/riscv/kernel/smpboot.c +++ b/arch/riscv/kernel/smpboot.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include "head.h" @@ -169,6 +171,11 @@ asmlinkage __visible void smp_callin(void) numa_add_cpu(curr_cpuid); set_cpu_online(curr_cpuid, 1); + if (has_vector()) { + if (riscv_v_setup_vsize()) + elf_hwcap &= ~COMPAT_HWCAP_ISA_V; + } + /* * Remote TLB flushes are ignored while the CPU is offline, so emit * a local TLB flush right now just in case. diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c new file mode 100644 index 00000000000000..120f1ce9abf9ae --- /dev/null +++ b/arch/riscv/kernel/vector.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2023 SiFive + * Author: Andy Chiu + */ +#include + +#include +#include +#include +#include + +unsigned long riscv_v_vsize __read_mostly; +EXPORT_SYMBOL_GPL(riscv_v_vsize); + +int riscv_v_setup_vsize(void) +{ + unsigned long this_vsize; + + /* There are 32 vector registers with vlenb length. */ + riscv_v_enable(); + this_vsize = csr_read(CSR_VLENB) * 32; + riscv_v_disable(); + + if (!riscv_v_vsize) { + riscv_v_vsize = this_vsize; + return 0; + } + + if (riscv_v_vsize != this_vsize) { + WARN(1, "RISCV_ISA_V only supports one vlenb on SMP systems"); + return -EOPNOTSUPP; + } + + return 0; +} From b577e070b49b17610ef262d215a2eaf659ac3f1f Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 5 Jun 2023 11:07:06 +0000 Subject: [PATCH 104/169] riscv: Introduce struct/helpers to save/restore per-task Vector state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add vector state context struct to be added later in thread_struct. And prepare low-level helper functions to save/restore vector contexts. This include Vector Regfile and CSRs holding dynamic configuration state (vstart, vl, vtype, vcsr). The Vec Register width could be implementation defined, but same for all processes, so that is saved separately. This is not yet wired into final thread_struct - will be done when __switch_to actually starts doing this in later patches. Given the variable (and potentially large) size of regfile, they are saved in dynamically allocated memory, pointed to by datap pointer in __riscv_v_ext_state. Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Signed-off-by: Vineet Gupta Signed-off-by: Andy Chiu Acked-by: Conor Dooley Reviewed-by: Guo Ren Reviewed-by: Björn Töpel Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Reviewed-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230605110724.21391-10-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/vector.h | 95 ++++++++++++++++++++++++++++ arch/riscv/include/uapi/asm/ptrace.h | 17 +++++ 2 files changed, 112 insertions(+) diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index f12a9027f41b7d..cf8fa8140533f9 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -11,8 +11,10 @@ #ifdef CONFIG_RISCV_ISA_V +#include #include #include +#include extern unsigned long riscv_v_vsize; int riscv_v_setup_vsize(void); @@ -22,6 +24,26 @@ static __always_inline bool has_vector(void) return static_branch_unlikely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_VECTOR]); } +static inline void __riscv_v_vstate_clean(struct pt_regs *regs) +{ + regs->status = (regs->status & ~SR_VS) | SR_VS_CLEAN; +} + +static inline void riscv_v_vstate_off(struct pt_regs *regs) +{ + regs->status = (regs->status & ~SR_VS) | SR_VS_OFF; +} + +static inline void riscv_v_vstate_on(struct pt_regs *regs) +{ + regs->status = (regs->status & ~SR_VS) | SR_VS_INITIAL; +} + +static inline bool riscv_v_vstate_query(struct pt_regs *regs) +{ + return (regs->status & SR_VS) != 0; +} + static __always_inline void riscv_v_enable(void) { csr_set(CSR_SSTATUS, SR_VS); @@ -32,13 +54,86 @@ static __always_inline void riscv_v_disable(void) csr_clear(CSR_SSTATUS, SR_VS); } +static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest) +{ + asm volatile ( + "csrr %0, " __stringify(CSR_VSTART) "\n\t" + "csrr %1, " __stringify(CSR_VTYPE) "\n\t" + "csrr %2, " __stringify(CSR_VL) "\n\t" + "csrr %3, " __stringify(CSR_VCSR) "\n\t" + : "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl), + "=r" (dest->vcsr) : :); +} + +static __always_inline void __vstate_csr_restore(struct __riscv_v_ext_state *src) +{ + asm volatile ( + ".option push\n\t" + ".option arch, +v\n\t" + "vsetvl x0, %2, %1\n\t" + ".option pop\n\t" + "csrw " __stringify(CSR_VSTART) ", %0\n\t" + "csrw " __stringify(CSR_VCSR) ", %3\n\t" + : : "r" (src->vstart), "r" (src->vtype), "r" (src->vl), + "r" (src->vcsr) :); +} + +static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to, + void *datap) +{ + unsigned long vl; + + riscv_v_enable(); + __vstate_csr_save(save_to); + asm volatile ( + ".option push\n\t" + ".option arch, +v\n\t" + "vsetvli %0, x0, e8, m8, ta, ma\n\t" + "vse8.v v0, (%1)\n\t" + "add %1, %1, %0\n\t" + "vse8.v v8, (%1)\n\t" + "add %1, %1, %0\n\t" + "vse8.v v16, (%1)\n\t" + "add %1, %1, %0\n\t" + "vse8.v v24, (%1)\n\t" + ".option pop\n\t" + : "=&r" (vl) : "r" (datap) : "memory"); + riscv_v_disable(); +} + +static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_from, + void *datap) +{ + unsigned long vl; + + riscv_v_enable(); + asm volatile ( + ".option push\n\t" + ".option arch, +v\n\t" + "vsetvli %0, x0, e8, m8, ta, ma\n\t" + "vle8.v v0, (%1)\n\t" + "add %1, %1, %0\n\t" + "vle8.v v8, (%1)\n\t" + "add %1, %1, %0\n\t" + "vle8.v v16, (%1)\n\t" + "add %1, %1, %0\n\t" + "vle8.v v24, (%1)\n\t" + ".option pop\n\t" + : "=&r" (vl) : "r" (datap) : "memory"); + __vstate_csr_restore(restore_from); + riscv_v_disable(); +} + #else /* ! CONFIG_RISCV_ISA_V */ struct pt_regs; static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; } static __always_inline bool has_vector(void) { return false; } +static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; } #define riscv_v_vsize (0) +#define riscv_v_vstate_off(regs) do {} while (0) +#define riscv_v_vstate_on(regs) do {} while (0) #endif /* CONFIG_RISCV_ISA_V */ diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 5e42d28845cb5a..c4df2cf15d8377 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -83,6 +83,23 @@ union __riscv_fp_state { struct __riscv_q_ext_state q; }; +struct __riscv_v_ext_state { + unsigned long vstart; + unsigned long vl; + unsigned long vtype; + unsigned long vcsr; + void *datap; + /* + * In signal handler, datap will be set a correct user stack offset + * and vector registers will be copied to the address of datap + * pointer. + * + * In ptrace syscall, datap will be set to zero and the vector + * registers will be copied to the address right after this + * structure. + */ +}; + #endif /* __ASSEMBLY__ */ #endif /* _UAPI_ASM_RISCV_PTRACE_H */ From 86dc67b5db2723935c19dbc06bd6890d663ef871 Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 5 Jun 2023 11:07:07 +0000 Subject: [PATCH 105/169] riscv: Add task switch support for vector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds task switch support for vector. It also supports all lengths of vlen. Suggested-by: Andrew Waterman Co-developed-by: Nick Knight Signed-off-by: Nick Knight Co-developed-by: Guo Ren Signed-off-by: Guo Ren Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Co-developed-by: Ruinland Tsai Signed-off-by: Ruinland Tsai Signed-off-by: Greentime Hu Signed-off-by: Vineet Gupta Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Björn Töpel Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230605110724.21391-11-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/processor.h | 1 + arch/riscv/include/asm/switch_to.h | 3 +++ arch/riscv/include/asm/thread_info.h | 3 +++ arch/riscv/include/asm/vector.h | 38 ++++++++++++++++++++++++++++ arch/riscv/kernel/process.c | 19 ++++++++++++++ 5 files changed, 64 insertions(+) diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h index a193477fb4e064..3dc3c6dfbbf369 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -42,6 +42,7 @@ struct thread_struct { struct __riscv_dsp_state dspstate; #endif unsigned long bad_cause; + struct __riscv_v_ext_state vstate; }; /* Whitelist the fstate from the task_struct for hardened usercopy */ diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h index 0fad1419388f3a..aa47a29c34c9f7 100644 --- a/arch/riscv/include/asm/switch_to.h +++ b/arch/riscv/include/asm/switch_to.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -109,6 +110,8 @@ do { \ __switch_to_fpu(__prev, __next); \ if (has_dsp()) \ __switch_to_dsp(__prev, __next); \ + if (has_vector()) \ + __switch_to_vector(__prev, __next); \ ((last) = __switch_to(__prev, __next)); \ } while (0) diff --git a/arch/riscv/include/asm/thread_info.h b/arch/riscv/include/asm/thread_info.h index 64530c10e04cf3..bbe2c077942dcb 100644 --- a/arch/riscv/include/asm/thread_info.h +++ b/arch/riscv/include/asm/thread_info.h @@ -80,6 +80,9 @@ struct thread_info { .preempt_count = INIT_PREEMPT_COUNT, \ } +void arch_release_task_struct(struct task_struct *tsk); +int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); + #endif /* !__ASSEMBLY__ */ /* diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index cf8fa8140533f9..e9a19f6609e688 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -12,6 +12,9 @@ #ifdef CONFIG_RISCV_ISA_V #include +#include +#include +#include #include #include #include @@ -124,6 +127,38 @@ static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_ riscv_v_disable(); } +static inline void riscv_v_vstate_save(struct task_struct *task, + struct pt_regs *regs) +{ + if ((regs->status & SR_VS) == SR_VS_DIRTY) { + struct __riscv_v_ext_state *vstate = &task->thread.vstate; + + __riscv_v_vstate_save(vstate, vstate->datap); + __riscv_v_vstate_clean(regs); + } +} + +static inline void riscv_v_vstate_restore(struct task_struct *task, + struct pt_regs *regs) +{ + if ((regs->status & SR_VS) != SR_VS_OFF) { + struct __riscv_v_ext_state *vstate = &task->thread.vstate; + + __riscv_v_vstate_restore(vstate, vstate->datap); + __riscv_v_vstate_clean(regs); + } +} + +static inline void __switch_to_vector(struct task_struct *prev, + struct task_struct *next) +{ + struct pt_regs *regs; + + regs = task_pt_regs(prev); + riscv_v_vstate_save(prev, regs); + riscv_v_vstate_restore(next, task_pt_regs(next)); +} + #else /* ! CONFIG_RISCV_ISA_V */ struct pt_regs; @@ -132,6 +167,9 @@ static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; } static __always_inline bool has_vector(void) { return false; } static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; } #define riscv_v_vsize (0) +#define riscv_v_vstate_save(task, regs) do {} while (0) +#define riscv_v_vstate_restore(task, regs) do {} while (0) +#define __switch_to_vector(__prev, __next) do {} while (0) #define riscv_v_vstate_off(regs) do {} while (0) #define riscv_v_vstate_on(regs) do {} while (0) diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index 6146dd89eeb74d..6e359f3928a472 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -24,6 +24,7 @@ #include #include #include +#include register unsigned long gp_in_global __asm__("gp"); @@ -151,6 +152,19 @@ void flush_thread(void) #ifdef CONFIG_DSP memset(¤t->thread.dspstate, 0, sizeof(current->thread.dspstate)); #endif +#ifdef CONFIG_RISCV_ISA_V + /* Reset vector state */ + riscv_v_vstate_off(task_pt_regs(current)); + kfree(current->thread.vstate.datap); + memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); +#endif +} + +void arch_release_task_struct(struct task_struct *tsk) +{ + /* Free the vector context of datap. */ + if (has_vector()) + kfree(tsk->thread.vstate.datap); } int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) @@ -158,6 +172,9 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) if (has_fpu()) fstate_save(src, task_pt_regs(src)); *dst = *src; + /* clear entire V context, including datap for a new task */ + memset(&dst->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); + return 0; } @@ -183,6 +200,8 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args) p->thread.s[1] = (unsigned long)args->fn_arg; } else { *childregs = *(current_pt_regs()); + /* Turn off status.VS */ + riscv_v_vstate_off(childregs); if (usp) /* User fork */ childregs->sp = usp; if (clone_flags & CLONE_SETTLS) From a8a9d7ea74476ace1fc816e1222828297c8a519f Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Mon, 5 Jun 2023 11:07:08 +0000 Subject: [PATCH 106/169] riscv: Allocate user's vector context in the first-use trap Vector unit is disabled by default for all user processes. Thus, a process will take a trap (illegal instruction) into kernel at the first time when it uses Vector. Only after then, the kernel allocates V context and starts take care of the context for that user process. Suggested-by: Richard Henderson Link: https://lore.kernel.org/r/3923eeee-e4dc-0911-40bf-84c34aee962d@linaro.org Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20230605110724.21391-12-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/insn.h | 410 ++++++++++++++++++++++++++++++++ arch/riscv/include/asm/vector.h | 2 + arch/riscv/kernel/traps.c | 11 +- arch/riscv/kernel/vector.c | 95 ++++++++ 4 files changed, 516 insertions(+), 2 deletions(-) create mode 100644 arch/riscv/include/asm/insn.h diff --git a/arch/riscv/include/asm/insn.h b/arch/riscv/include/asm/insn.h new file mode 100644 index 00000000000000..4e1505cef8aa40 --- /dev/null +++ b/arch/riscv/include/asm/insn.h @@ -0,0 +1,410 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2020 SiFive + */ + +#ifndef _ASM_RISCV_INSN_H +#define _ASM_RISCV_INSN_H + +#include + +#define RV_INSN_FUNCT3_MASK GENMASK(14, 12) +#define RV_INSN_FUNCT3_OPOFF 12 +#define RV_INSN_OPCODE_MASK GENMASK(6, 0) +#define RV_INSN_OPCODE_OPOFF 0 +#define RV_INSN_FUNCT12_OPOFF 20 + +#define RV_ENCODE_FUNCT3(f_) (RVG_FUNCT3_##f_ << RV_INSN_FUNCT3_OPOFF) +#define RV_ENCODE_FUNCT12(f_) (RVG_FUNCT12_##f_ << RV_INSN_FUNCT12_OPOFF) + +/* The bit field of immediate value in I-type instruction */ +#define RV_I_IMM_SIGN_OPOFF 31 +#define RV_I_IMM_11_0_OPOFF 20 +#define RV_I_IMM_SIGN_OFF 12 +#define RV_I_IMM_11_0_OFF 0 +#define RV_I_IMM_11_0_MASK GENMASK(11, 0) + +/* The bit field of immediate value in J-type instruction */ +#define RV_J_IMM_SIGN_OPOFF 31 +#define RV_J_IMM_10_1_OPOFF 21 +#define RV_J_IMM_11_OPOFF 20 +#define RV_J_IMM_19_12_OPOFF 12 +#define RV_J_IMM_SIGN_OFF 20 +#define RV_J_IMM_10_1_OFF 1 +#define RV_J_IMM_11_OFF 11 +#define RV_J_IMM_19_12_OFF 12 +#define RV_J_IMM_10_1_MASK GENMASK(9, 0) +#define RV_J_IMM_11_MASK GENMASK(0, 0) +#define RV_J_IMM_19_12_MASK GENMASK(7, 0) + +/* + * U-type IMMs contain the upper 20bits [31:20] of an immediate with + * the rest filled in by zeros, so no shifting required. Similarly, + * bit31 contains the signed state, so no sign extension necessary. + */ +#define RV_U_IMM_SIGN_OPOFF 31 +#define RV_U_IMM_31_12_OPOFF 0 +#define RV_U_IMM_31_12_MASK GENMASK(31, 12) + +/* The bit field of immediate value in B-type instruction */ +#define RV_B_IMM_SIGN_OPOFF 31 +#define RV_B_IMM_10_5_OPOFF 25 +#define RV_B_IMM_4_1_OPOFF 8 +#define RV_B_IMM_11_OPOFF 7 +#define RV_B_IMM_SIGN_OFF 12 +#define RV_B_IMM_10_5_OFF 5 +#define RV_B_IMM_4_1_OFF 1 +#define RV_B_IMM_11_OFF 11 +#define RV_B_IMM_10_5_MASK GENMASK(5, 0) +#define RV_B_IMM_4_1_MASK GENMASK(3, 0) +#define RV_B_IMM_11_MASK GENMASK(0, 0) + +/* The register offset in RVG instruction */ +#define RVG_RS1_OPOFF 15 +#define RVG_RS2_OPOFF 20 +#define RVG_RD_OPOFF 7 +#define RVG_RD_MASK GENMASK(4, 0) + +/* The bit field of immediate value in RVC J instruction */ +#define RVC_J_IMM_SIGN_OPOFF 12 +#define RVC_J_IMM_4_OPOFF 11 +#define RVC_J_IMM_9_8_OPOFF 9 +#define RVC_J_IMM_10_OPOFF 8 +#define RVC_J_IMM_6_OPOFF 7 +#define RVC_J_IMM_7_OPOFF 6 +#define RVC_J_IMM_3_1_OPOFF 3 +#define RVC_J_IMM_5_OPOFF 2 +#define RVC_J_IMM_SIGN_OFF 11 +#define RVC_J_IMM_4_OFF 4 +#define RVC_J_IMM_9_8_OFF 8 +#define RVC_J_IMM_10_OFF 10 +#define RVC_J_IMM_6_OFF 6 +#define RVC_J_IMM_7_OFF 7 +#define RVC_J_IMM_3_1_OFF 1 +#define RVC_J_IMM_5_OFF 5 +#define RVC_J_IMM_4_MASK GENMASK(0, 0) +#define RVC_J_IMM_9_8_MASK GENMASK(1, 0) +#define RVC_J_IMM_10_MASK GENMASK(0, 0) +#define RVC_J_IMM_6_MASK GENMASK(0, 0) +#define RVC_J_IMM_7_MASK GENMASK(0, 0) +#define RVC_J_IMM_3_1_MASK GENMASK(2, 0) +#define RVC_J_IMM_5_MASK GENMASK(0, 0) + +/* The bit field of immediate value in RVC B instruction */ +#define RVC_B_IMM_SIGN_OPOFF 12 +#define RVC_B_IMM_4_3_OPOFF 10 +#define RVC_B_IMM_7_6_OPOFF 5 +#define RVC_B_IMM_2_1_OPOFF 3 +#define RVC_B_IMM_5_OPOFF 2 +#define RVC_B_IMM_SIGN_OFF 8 +#define RVC_B_IMM_4_3_OFF 3 +#define RVC_B_IMM_7_6_OFF 6 +#define RVC_B_IMM_2_1_OFF 1 +#define RVC_B_IMM_5_OFF 5 +#define RVC_B_IMM_4_3_MASK GENMASK(1, 0) +#define RVC_B_IMM_7_6_MASK GENMASK(1, 0) +#define RVC_B_IMM_2_1_MASK GENMASK(1, 0) +#define RVC_B_IMM_5_MASK GENMASK(0, 0) + +#define RVC_INSN_FUNCT4_MASK GENMASK(15, 12) +#define RVC_INSN_FUNCT4_OPOFF 12 +#define RVC_INSN_FUNCT3_MASK GENMASK(15, 13) +#define RVC_INSN_FUNCT3_OPOFF 13 +#define RVC_INSN_J_RS2_MASK GENMASK(6, 2) +#define RVC_INSN_OPCODE_MASK GENMASK(1, 0) +#define RVC_ENCODE_FUNCT3(f_) (RVC_FUNCT3_##f_ << RVC_INSN_FUNCT3_OPOFF) +#define RVC_ENCODE_FUNCT4(f_) (RVC_FUNCT4_##f_ << RVC_INSN_FUNCT4_OPOFF) + +/* The register offset in RVC op=C0 instruction */ +#define RVC_C0_RS1_OPOFF 7 +#define RVC_C0_RS2_OPOFF 2 +#define RVC_C0_RD_OPOFF 2 + +/* The register offset in RVC op=C1 instruction */ +#define RVC_C1_RS1_OPOFF 7 +#define RVC_C1_RS2_OPOFF 2 +#define RVC_C1_RD_OPOFF 7 + +/* The register offset in RVC op=C2 instruction */ +#define RVC_C2_RS1_OPOFF 7 +#define RVC_C2_RS2_OPOFF 2 +#define RVC_C2_RD_OPOFF 7 + +/* parts of opcode for RVG*/ +#define RVG_OPCODE_FENCE 0x0f +#define RVG_OPCODE_AUIPC 0x17 +#define RVG_OPCODE_BRANCH 0x63 +#define RVG_OPCODE_JALR 0x67 +#define RVG_OPCODE_JAL 0x6f +#define RVG_OPCODE_SYSTEM 0x73 +#define RVG_SYSTEM_CSR_OFF 20 +#define RVG_SYSTEM_CSR_MASK GENMASK(12, 0) + +/* parts of opcode for RVF, RVD and RVQ */ +#define RVFDQ_FL_FS_WIDTH_OFF 12 +#define RVFDQ_FL_FS_WIDTH_MASK GENMASK(3, 0) +#define RVFDQ_FL_FS_WIDTH_W 2 +#define RVFDQ_FL_FS_WIDTH_D 3 +#define RVFDQ_LS_FS_WIDTH_Q 4 +#define RVFDQ_OPCODE_FL 0x07 +#define RVFDQ_OPCODE_FS 0x27 + +/* parts of opcode for RVV */ +#define RVV_OPCODE_VECTOR 0x57 +#define RVV_VL_VS_WIDTH_8 0 +#define RVV_VL_VS_WIDTH_16 5 +#define RVV_VL_VS_WIDTH_32 6 +#define RVV_VL_VS_WIDTH_64 7 +#define RVV_OPCODE_VL RVFDQ_OPCODE_FL +#define RVV_OPCODE_VS RVFDQ_OPCODE_FS + +/* parts of opcode for RVC*/ +#define RVC_OPCODE_C0 0x0 +#define RVC_OPCODE_C1 0x1 +#define RVC_OPCODE_C2 0x2 + +/* parts of funct3 code for I, M, A extension*/ +#define RVG_FUNCT3_JALR 0x0 +#define RVG_FUNCT3_BEQ 0x0 +#define RVG_FUNCT3_BNE 0x1 +#define RVG_FUNCT3_BLT 0x4 +#define RVG_FUNCT3_BGE 0x5 +#define RVG_FUNCT3_BLTU 0x6 +#define RVG_FUNCT3_BGEU 0x7 + +/* parts of funct3 code for C extension*/ +#define RVC_FUNCT3_C_BEQZ 0x6 +#define RVC_FUNCT3_C_BNEZ 0x7 +#define RVC_FUNCT3_C_J 0x5 +#define RVC_FUNCT3_C_JAL 0x1 +#define RVC_FUNCT4_C_JR 0x8 +#define RVC_FUNCT4_C_JALR 0x9 +#define RVC_FUNCT4_C_EBREAK 0x9 + +#define RVG_FUNCT12_EBREAK 0x1 +#define RVG_FUNCT12_SRET 0x102 + +#define RVG_MATCH_AUIPC (RVG_OPCODE_AUIPC) +#define RVG_MATCH_JALR (RV_ENCODE_FUNCT3(JALR) | RVG_OPCODE_JALR) +#define RVG_MATCH_JAL (RVG_OPCODE_JAL) +#define RVG_MATCH_FENCE (RVG_OPCODE_FENCE) +#define RVG_MATCH_BEQ (RV_ENCODE_FUNCT3(BEQ) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BNE (RV_ENCODE_FUNCT3(BNE) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BLT (RV_ENCODE_FUNCT3(BLT) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BGE (RV_ENCODE_FUNCT3(BGE) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BLTU (RV_ENCODE_FUNCT3(BLTU) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_BGEU (RV_ENCODE_FUNCT3(BGEU) | RVG_OPCODE_BRANCH) +#define RVG_MATCH_EBREAK (RV_ENCODE_FUNCT12(EBREAK) | RVG_OPCODE_SYSTEM) +#define RVG_MATCH_SRET (RV_ENCODE_FUNCT12(SRET) | RVG_OPCODE_SYSTEM) +#define RVC_MATCH_C_BEQZ (RVC_ENCODE_FUNCT3(C_BEQZ) | RVC_OPCODE_C1) +#define RVC_MATCH_C_BNEZ (RVC_ENCODE_FUNCT3(C_BNEZ) | RVC_OPCODE_C1) +#define RVC_MATCH_C_J (RVC_ENCODE_FUNCT3(C_J) | RVC_OPCODE_C1) +#define RVC_MATCH_C_JAL (RVC_ENCODE_FUNCT3(C_JAL) | RVC_OPCODE_C1) +#define RVC_MATCH_C_JR (RVC_ENCODE_FUNCT4(C_JR) | RVC_OPCODE_C2) +#define RVC_MATCH_C_JALR (RVC_ENCODE_FUNCT4(C_JALR) | RVC_OPCODE_C2) +#define RVC_MATCH_C_EBREAK (RVC_ENCODE_FUNCT4(C_EBREAK) | RVC_OPCODE_C2) + +#define RVG_MASK_AUIPC (RV_INSN_OPCODE_MASK) +#define RVG_MASK_JALR (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_JAL (RV_INSN_OPCODE_MASK) +#define RVG_MASK_FENCE (RV_INSN_OPCODE_MASK) +#define RVC_MASK_C_JALR (RVC_INSN_FUNCT4_MASK | RVC_INSN_J_RS2_MASK | RVC_INSN_OPCODE_MASK) +#define RVC_MASK_C_JR (RVC_INSN_FUNCT4_MASK | RVC_INSN_J_RS2_MASK | RVC_INSN_OPCODE_MASK) +#define RVC_MASK_C_JAL (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK) +#define RVC_MASK_C_J (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK) +#define RVG_MASK_BEQ (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BNE (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BLT (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BGE (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BLTU (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVG_MASK_BGEU (RV_INSN_FUNCT3_MASK | RV_INSN_OPCODE_MASK) +#define RVC_MASK_C_BEQZ (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK) +#define RVC_MASK_C_BNEZ (RVC_INSN_FUNCT3_MASK | RVC_INSN_OPCODE_MASK) +#define RVC_MASK_C_EBREAK 0xffff +#define RVG_MASK_EBREAK 0xffffffff +#define RVG_MASK_SRET 0xffffffff + +#define __INSN_LENGTH_MASK _UL(0x3) +#define __INSN_LENGTH_GE_32 _UL(0x3) +#define __INSN_OPCODE_MASK _UL(0x7F) +#define __INSN_BRANCH_OPCODE _UL(RVG_OPCODE_BRANCH) + +#define __RISCV_INSN_FUNCS(name, mask, val) \ +static __always_inline bool riscv_insn_is_##name(u32 code) \ +{ \ + BUILD_BUG_ON(~(mask) & (val)); \ + return (code & (mask)) == (val); \ +} \ + +#if __riscv_xlen == 32 +/* C.JAL is an RV32C-only instruction */ +__RISCV_INSN_FUNCS(c_jal, RVC_MASK_C_JAL, RVC_MATCH_C_JAL) +#else +#define riscv_insn_is_c_jal(opcode) 0 +#endif +__RISCV_INSN_FUNCS(auipc, RVG_MASK_AUIPC, RVG_MATCH_AUIPC) +__RISCV_INSN_FUNCS(jalr, RVG_MASK_JALR, RVG_MATCH_JALR) +__RISCV_INSN_FUNCS(jal, RVG_MASK_JAL, RVG_MATCH_JAL) +__RISCV_INSN_FUNCS(c_jr, RVC_MASK_C_JR, RVC_MATCH_C_JR) +__RISCV_INSN_FUNCS(c_jalr, RVC_MASK_C_JALR, RVC_MATCH_C_JALR) +__RISCV_INSN_FUNCS(c_j, RVC_MASK_C_J, RVC_MATCH_C_J) +__RISCV_INSN_FUNCS(beq, RVG_MASK_BEQ, RVG_MATCH_BEQ) +__RISCV_INSN_FUNCS(bne, RVG_MASK_BNE, RVG_MATCH_BNE) +__RISCV_INSN_FUNCS(blt, RVG_MASK_BLT, RVG_MATCH_BLT) +__RISCV_INSN_FUNCS(bge, RVG_MASK_BGE, RVG_MATCH_BGE) +__RISCV_INSN_FUNCS(bltu, RVG_MASK_BLTU, RVG_MATCH_BLTU) +__RISCV_INSN_FUNCS(bgeu, RVG_MASK_BGEU, RVG_MATCH_BGEU) +__RISCV_INSN_FUNCS(c_beqz, RVC_MASK_C_BEQZ, RVC_MATCH_C_BEQZ) +__RISCV_INSN_FUNCS(c_bnez, RVC_MASK_C_BNEZ, RVC_MATCH_C_BNEZ) +__RISCV_INSN_FUNCS(c_ebreak, RVC_MASK_C_EBREAK, RVC_MATCH_C_EBREAK) +__RISCV_INSN_FUNCS(ebreak, RVG_MASK_EBREAK, RVG_MATCH_EBREAK) +__RISCV_INSN_FUNCS(sret, RVG_MASK_SRET, RVG_MATCH_SRET) +__RISCV_INSN_FUNCS(fence, RVG_MASK_FENCE, RVG_MATCH_FENCE); + +/* special case to catch _any_ system instruction */ +static __always_inline bool riscv_insn_is_system(u32 code) +{ + return (code & RV_INSN_OPCODE_MASK) == RVG_OPCODE_SYSTEM; +} + +/* special case to catch _any_ branch instruction */ +static __always_inline bool riscv_insn_is_branch(u32 code) +{ + return (code & RV_INSN_OPCODE_MASK) == RVG_OPCODE_BRANCH; +} + +#define RV_IMM_SIGN(x) (-(((x) >> 31) & 1)) +#define RVC_IMM_SIGN(x) (-(((x) >> 12) & 1)) +#define RV_X(X, s, mask) (((X) >> (s)) & (mask)) +#define RVC_X(X, s, mask) RV_X(X, s, mask) + +#define RV_EXTRACT_RD_REG(x) \ + ({typeof(x) x_ = (x); \ + (RV_X(x_, RVG_RD_OPOFF, RVG_RD_MASK)); }) + +#define RV_EXTRACT_UTYPE_IMM(x) \ + ({typeof(x) x_ = (x); \ + (RV_X(x_, RV_U_IMM_31_12_OPOFF, RV_U_IMM_31_12_MASK)); }) + +#define RV_EXTRACT_JTYPE_IMM(x) \ + ({typeof(x) x_ = (x); \ + (RV_X(x_, RV_J_IMM_10_1_OPOFF, RV_J_IMM_10_1_MASK) << RV_J_IMM_10_1_OFF) | \ + (RV_X(x_, RV_J_IMM_11_OPOFF, RV_J_IMM_11_MASK) << RV_J_IMM_11_OFF) | \ + (RV_X(x_, RV_J_IMM_19_12_OPOFF, RV_J_IMM_19_12_MASK) << RV_J_IMM_19_12_OFF) | \ + (RV_IMM_SIGN(x_) << RV_J_IMM_SIGN_OFF); }) + +#define RV_EXTRACT_ITYPE_IMM(x) \ + ({typeof(x) x_ = (x); \ + (RV_X(x_, RV_I_IMM_11_0_OPOFF, RV_I_IMM_11_0_MASK)) | \ + (RV_IMM_SIGN(x_) << RV_I_IMM_SIGN_OFF); }) + +#define RV_EXTRACT_BTYPE_IMM(x) \ + ({typeof(x) x_ = (x); \ + (RV_X(x_, RV_B_IMM_4_1_OPOFF, RV_B_IMM_4_1_MASK) << RV_B_IMM_4_1_OFF) | \ + (RV_X(x_, RV_B_IMM_10_5_OPOFF, RV_B_IMM_10_5_MASK) << RV_B_IMM_10_5_OFF) | \ + (RV_X(x_, RV_B_IMM_11_OPOFF, RV_B_IMM_11_MASK) << RV_B_IMM_11_OFF) | \ + (RV_IMM_SIGN(x_) << RV_B_IMM_SIGN_OFF); }) + +#define RVC_EXTRACT_JTYPE_IMM(x) \ + ({typeof(x) x_ = (x); \ + (RVC_X(x_, RVC_J_IMM_3_1_OPOFF, RVC_J_IMM_3_1_MASK) << RVC_J_IMM_3_1_OFF) | \ + (RVC_X(x_, RVC_J_IMM_4_OPOFF, RVC_J_IMM_4_MASK) << RVC_J_IMM_4_OFF) | \ + (RVC_X(x_, RVC_J_IMM_5_OPOFF, RVC_J_IMM_5_MASK) << RVC_J_IMM_5_OFF) | \ + (RVC_X(x_, RVC_J_IMM_6_OPOFF, RVC_J_IMM_6_MASK) << RVC_J_IMM_6_OFF) | \ + (RVC_X(x_, RVC_J_IMM_7_OPOFF, RVC_J_IMM_7_MASK) << RVC_J_IMM_7_OFF) | \ + (RVC_X(x_, RVC_J_IMM_9_8_OPOFF, RVC_J_IMM_9_8_MASK) << RVC_J_IMM_9_8_OFF) | \ + (RVC_X(x_, RVC_J_IMM_10_OPOFF, RVC_J_IMM_10_MASK) << RVC_J_IMM_10_OFF) | \ + (RVC_IMM_SIGN(x_) << RVC_J_IMM_SIGN_OFF); }) + +#define RVC_EXTRACT_BTYPE_IMM(x) \ + ({typeof(x) x_ = (x); \ + (RVC_X(x_, RVC_B_IMM_2_1_OPOFF, RVC_B_IMM_2_1_MASK) << RVC_B_IMM_2_1_OFF) | \ + (RVC_X(x_, RVC_B_IMM_4_3_OPOFF, RVC_B_IMM_4_3_MASK) << RVC_B_IMM_4_3_OFF) | \ + (RVC_X(x_, RVC_B_IMM_5_OPOFF, RVC_B_IMM_5_MASK) << RVC_B_IMM_5_OFF) | \ + (RVC_X(x_, RVC_B_IMM_7_6_OPOFF, RVC_B_IMM_7_6_MASK) << RVC_B_IMM_7_6_OFF) | \ + (RVC_IMM_SIGN(x_) << RVC_B_IMM_SIGN_OFF); }) + +#define RVG_EXTRACT_SYSTEM_CSR(x) \ + ({typeof(x) x_ = (x); RV_X(x_, RVG_SYSTEM_CSR_OFF, RVG_SYSTEM_CSR_MASK); }) + +#define RVFDQ_EXTRACT_FL_FS_WIDTH(x) \ + ({typeof(x) x_ = (x); RV_X(x_, RVFDQ_FL_FS_WIDTH_OFF, \ + RVFDQ_FL_FS_WIDTH_MASK); }) + +#define RVV_EXRACT_VL_VS_WIDTH(x) RVFDQ_EXTRACT_FL_FS_WIDTH(x) + +/* + * Get the immediate from a J-type instruction. + * + * @insn: instruction to process + * Return: immediate + */ +static inline s32 riscv_insn_extract_jtype_imm(u32 insn) +{ + return RV_EXTRACT_JTYPE_IMM(insn); +} + +/* + * Update a J-type instruction with an immediate value. + * + * @insn: pointer to the jtype instruction + * @imm: the immediate to insert into the instruction + */ +static inline void riscv_insn_insert_jtype_imm(u32 *insn, s32 imm) +{ + /* drop the old IMMs, all jal IMM bits sit at 31:12 */ + *insn &= ~GENMASK(31, 12); + *insn |= (RV_X(imm, RV_J_IMM_10_1_OFF, RV_J_IMM_10_1_MASK) << RV_J_IMM_10_1_OPOFF) | + (RV_X(imm, RV_J_IMM_11_OFF, RV_J_IMM_11_MASK) << RV_J_IMM_11_OPOFF) | + (RV_X(imm, RV_J_IMM_19_12_OFF, RV_J_IMM_19_12_MASK) << RV_J_IMM_19_12_OPOFF) | + (RV_X(imm, RV_J_IMM_SIGN_OFF, 1) << RV_J_IMM_SIGN_OPOFF); +} + +/* + * Put together one immediate from a U-type and I-type instruction pair. + * + * The U-type contains an upper immediate, meaning bits[31:12] with [11:0] + * being zero, while the I-type contains a 12bit immediate. + * Combined these can encode larger 32bit values and are used for example + * in auipc + jalr pairs to allow larger jumps. + * + * @utype_insn: instruction containing the upper immediate + * @itype_insn: instruction + * Return: combined immediate + */ +static inline s32 riscv_insn_extract_utype_itype_imm(u32 utype_insn, u32 itype_insn) +{ + s32 imm; + + imm = RV_EXTRACT_UTYPE_IMM(utype_insn); + imm += RV_EXTRACT_ITYPE_IMM(itype_insn); + + return imm; +} + +/* + * Update a set of two instructions (U-type + I-type) with an immediate value. + * + * Used for example in auipc+jalrs pairs the U-type instructions contains + * a 20bit upper immediate representing bits[31:12], while the I-type + * instruction contains a 12bit immediate representing bits[11:0]. + * + * This also takes into account that both separate immediates are + * considered as signed values, so if the I-type immediate becomes + * negative (BIT(11) set) the U-type part gets adjusted. + * + * @utype_insn: pointer to the utype instruction of the pair + * @itype_insn: pointer to the itype instruction of the pair + * @imm: the immediate to insert into the two instructions + */ +static inline void riscv_insn_insert_utype_itype_imm(u32 *utype_insn, u32 *itype_insn, s32 imm) +{ + /* drop possible old IMM values */ + *utype_insn &= ~(RV_U_IMM_31_12_MASK); + *itype_insn &= ~(RV_I_IMM_11_0_MASK << RV_I_IMM_11_0_OPOFF); + + /* add the adapted IMMs */ + *utype_insn |= (imm & RV_U_IMM_31_12_MASK) + ((imm & BIT(11)) << 1); + *itype_insn |= ((imm & RV_I_IMM_11_0_MASK) << RV_I_IMM_11_0_OPOFF); +} +#endif /* _ASM_RISCV_INSN_H */ diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index e9a19f6609e688..e9c829f4712ffd 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -21,6 +21,7 @@ extern unsigned long riscv_v_vsize; int riscv_v_setup_vsize(void); +bool riscv_v_first_use_handler(struct pt_regs *regs); static __always_inline bool has_vector(void) { @@ -165,6 +166,7 @@ struct pt_regs; static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; } static __always_inline bool has_vector(void) { return false; } +static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; } static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; } #define riscv_v_vsize (0) #define riscv_v_vstate_save(task, regs) do {} while (0) diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c index 5d07f6b3ca3273..1c27f6b0aa30a1 100644 --- a/arch/riscv/kernel/traps.c +++ b/arch/riscv/kernel/traps.c @@ -24,6 +24,7 @@ #include #include #include +#include int show_unhandled_signals = 1; @@ -112,8 +113,14 @@ DO_ERROR_INFO(do_trap_insn_misaligned, SIGBUS, BUS_ADRALN, "instruction address misaligned"); DO_ERROR_INFO(do_trap_insn_fault, SIGSEGV, SEGV_ACCERR, "instruction access fault"); -DO_ERROR_INFO(do_trap_insn_illegal, - SIGILL, ILL_ILLOPC, "illegal instruction"); + +asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *regs) +{ + if (!user_mode(regs) || !riscv_v_first_use_handler(regs)) + do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc, + "Oops - illegal instruction"); +} + DO_ERROR_INFO(do_trap_load_fault, SIGSEGV, SEGV_ACCERR, "load access fault"); #ifndef CONFIG_RISCV_M_MODE diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c index 120f1ce9abf9ae..9d81d1b2a7f398 100644 --- a/arch/riscv/kernel/vector.c +++ b/arch/riscv/kernel/vector.c @@ -4,10 +4,19 @@ * Author: Andy Chiu */ #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include #include unsigned long riscv_v_vsize __read_mostly; @@ -34,3 +43,89 @@ int riscv_v_setup_vsize(void) return 0; } + +static bool insn_is_vector(u32 insn_buf) +{ + u32 opcode = insn_buf & __INSN_OPCODE_MASK; + u32 width, csr; + + /* + * All V-related instructions, including CSR operations are 4-Byte. So, + * do not handle if the instruction length is not 4-Byte. + */ + if (unlikely(GET_INSN_LENGTH(insn_buf) != 4)) + return false; + + switch (opcode) { + case RVV_OPCODE_VECTOR: + return true; + case RVV_OPCODE_VL: + case RVV_OPCODE_VS: + width = RVV_EXRACT_VL_VS_WIDTH(insn_buf); + if (width == RVV_VL_VS_WIDTH_8 || width == RVV_VL_VS_WIDTH_16 || + width == RVV_VL_VS_WIDTH_32 || width == RVV_VL_VS_WIDTH_64) + return true; + + break; + case RVG_OPCODE_SYSTEM: + csr = RVG_EXTRACT_SYSTEM_CSR(insn_buf); + if ((csr >= CSR_VSTART && csr <= CSR_VCSR) || + (csr >= CSR_VL && csr <= CSR_VLENB)) + return true; + } + + return false; +} + +static int riscv_v_thread_zalloc(void) +{ + void *datap; + + datap = kzalloc(riscv_v_vsize, GFP_KERNEL); + if (!datap) + return -ENOMEM; + + current->thread.vstate.datap = datap; + memset(¤t->thread.vstate, 0, offsetof(struct __riscv_v_ext_state, + datap)); + return 0; +} + +bool riscv_v_first_use_handler(struct pt_regs *regs) +{ + u32 __user *epc = (u32 __user *)regs->epc; + u32 insn = (u32)regs->badaddr; + + /* Do not handle if V is not supported, or disabled */ + if (!(ELF_HWCAP & COMPAT_HWCAP_ISA_V)) + return false; + + /* If V has been enabled then it is not the first-use trap */ + if (riscv_v_vstate_query(regs)) + return false; + + /* Get the instruction */ + if (!insn) { + if (__get_user(insn, epc)) + return false; + } + + /* Filter out non-V instructions */ + if (!insn_is_vector(insn)) + return false; + + /* Sanity check. datap should be null by the time of the first-use trap */ + WARN_ON(current->thread.vstate.datap); + + /* + * Now we sure that this is a V instruction. And it executes in the + * context where VS has been off. So, try to allocate the user's V + * context and resume execution. + */ + if (riscv_v_thread_zalloc()) { + force_sig(SIGBUS); + return true; + } + riscv_v_vstate_on(regs); + return true; +} From 03544b0a3270ca2de612c832a3afa932fc918125 Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 5 Jun 2023 11:07:09 +0000 Subject: [PATCH 107/169] riscv: Add ptrace vector support This patch adds ptrace support for riscv vector. The vector registers will be saved in datap pointer of __riscv_v_ext_state. This pointer will be set right after the __riscv_v_ext_state data structure then it will be put in ubuf for ptrace system call to get or set. It will check if the datap got from ubuf is set to the correct address or not when the ptrace system call is trying to set the vector registers. Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230605110724.21391-13-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/uapi/asm/ptrace.h | 7 +++ arch/riscv/kernel/ptrace.c | 70 ++++++++++++++++++++++++++++ include/uapi/linux/elf.h | 1 + 3 files changed, 78 insertions(+) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index c4df2cf15d8377..9500b1f326986e 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -100,6 +100,13 @@ struct __riscv_v_ext_state { */ }; +/* + * According to spec: The number of bits in a single vector register, + * VLEN >= ELEN, which must be a power of 2, and must be no greater than + * 2^16 = 65536bits = 8192bytes + */ +#define RISCV_MAX_VLENB (8192) + #endif /* __ASSEMBLY__ */ #endif /* _UAPI_ASM_RISCV_PTRACE_H */ diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index c9f4bbc34185db..7308e22f552424 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -7,6 +7,7 @@ * Copied from arch/tile/kernel/ptrace.c */ +#include #include #include #include @@ -29,6 +30,9 @@ enum riscv_regset { #ifdef CONFIG_FPU REGSET_F, #endif +#ifdef CONFIG_RISCV_ISA_V + REGSET_V, +#endif }; static int riscv_gpr_get(struct task_struct *target, @@ -85,6 +89,61 @@ static int riscv_fpr_set(struct task_struct *target, } #endif +#ifdef CONFIG_RISCV_ISA_V +static int riscv_vr_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + struct __riscv_v_ext_state *vstate = &target->thread.vstate; + + if (!riscv_v_vstate_query(task_pt_regs(target))) + return -EINVAL; + + /* + * Ensure the vector registers have been saved to the memory before + * copying them to membuf. + */ + if (target == current) + riscv_v_vstate_save(current, task_pt_regs(current)); + + /* Copy vector header from vstate. */ + membuf_write(&to, vstate, offsetof(struct __riscv_v_ext_state, datap)); + membuf_zero(&to, sizeof(vstate->datap)); + + /* Copy all the vector registers from vstate. */ + return membuf_write(&to, vstate->datap, riscv_v_vsize); +} + +static int riscv_vr_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret, size; + struct __riscv_v_ext_state *vstate = &target->thread.vstate; + + if (!riscv_v_vstate_query(task_pt_regs(target))) + return -EINVAL; + + /* Copy rest of the vstate except datap */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate, 0, + offsetof(struct __riscv_v_ext_state, datap)); + if (unlikely(ret)) + return ret; + + /* Skip copy datap. */ + size = sizeof(vstate->datap); + count -= size; + ubuf += size; + + /* Copy all the vector registers. */ + pos = 0; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate->datap, + 0, riscv_v_vsize); + return ret; +} +#endif + static const struct user_regset riscv_user_regset[] = { [REGSET_X] = { .core_note_type = NT_PRSTATUS, @@ -104,6 +163,17 @@ static const struct user_regset riscv_user_regset[] = { .set = riscv_fpr_set, }, #endif +#ifdef CONFIG_RISCV_ISA_V + [REGSET_V] = { + .core_note_type = NT_RISCV_VECTOR, + .align = 16, + .n = ((32 * RISCV_MAX_VLENB) + + sizeof(struct __riscv_v_ext_state)) / sizeof(__u32), + .size = sizeof(__u32), + .regset_get = riscv_vr_get, + .set = riscv_vr_set, + }, +#endif }; static const struct user_regset_view riscv_user_native_view = { diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index c7b056af9ef0ad..5a5056c6a2a124 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -439,6 +439,7 @@ typedef struct elf64_shdr { #define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers */ #define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode */ #define NT_MIPS_MSA 0x802 /* MIPS SIMD registers */ +#define NT_RISCV_VECTOR 0x900 /* RISC-V vector registers */ #define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers */ #define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and status registers */ #define NT_LOONGARCH_LSX 0xa02 /* LoongArch Loongson SIMD Extension registers */ From 667eba08fe4f7f49641596462cd157c1bd0bc309 Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Mon, 5 Jun 2023 11:07:10 +0000 Subject: [PATCH 108/169] riscv: signal: check fp-reserved words unconditionally In order to let kernel/user locate and identify an extension context on the existing sigframe, we are going to utilize reserved space of fp and encode the information there. And since the sigcontext has already preserved a space for fp context w or w/o CONFIG_FPU, we move those reserved words checking/setting routine back into generic code. This commit also undone an additional logical change carried by the refactor commit 007f5c3589578 ("Refactor FPU code in signal setup/return procedures"). Originally we did not restore fp context if restoring of gpr have failed. And it was fine on the other side. In such way the kernel could keep the regfiles intact, and potentially react at the failing point of restore. Signed-off-by: Andy Chiu Acked-by: Conor Dooley Acked-by: Heiko Stuebner Tested-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230605110724.21391-14-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/signal.c | 66 +++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 5a5aa23f3418a8..4741f36a0145f4 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -39,26 +39,13 @@ static long restore_fp_state(struct pt_regs *regs, { long err; struct __riscv_d_ext_state __user *state = &sc_fpregs->d; - size_t i; err = __copy_from_user(¤t->thread.fstate, state, sizeof(*state)); if (unlikely(err)) return err; fstate_restore(current, regs); - - /* We support no other extension state at this time. */ - for (i = 0; i < ARRAY_SIZE(sc_fpregs->q.reserved); i++) { - u32 value; - - err = __get_user(value, &sc_fpregs->q.reserved[i]); - if (unlikely(err)) - break; - if (value != 0) - return -EINVAL; - } - - return err; + return 0; } static long save_fp_state(struct pt_regs *regs, @@ -66,20 +53,9 @@ static long save_fp_state(struct pt_regs *regs, { long err; struct __riscv_d_ext_state __user *state = &sc_fpregs->d; - size_t i; fstate_save(current, regs); err = __copy_to_user(state, ¤t->thread.fstate, sizeof(*state)); - if (unlikely(err)) - return err; - - /* We support no other extension state at this time. */ - for (i = 0; i < ARRAY_SIZE(sc_fpregs->q.reserved); i++) { - err = __put_user(0, &sc_fpregs->q.reserved[i]); - if (unlikely(err)) - break; - } - return err; } #else @@ -124,13 +100,37 @@ static long restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { long err; + size_t i; + /* sc_regs is structured the same as the start of pt_regs */ err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs)); + if (unlikely(err)) + return err; + /* Restore the floating-point state. */ - if (has_fpu()) - err |= restore_fp_state(regs, &sc->sc_fpregs); - if (has_dsp()) - err |= restore_dsp_state(regs, &sc->sc_dspregs); + if (has_fpu()) { + err = restore_fp_state(regs, &sc->sc_fpregs); + if (unlikely(err)) + return err; + } + + /* We support no other extension state at this time. */ + for (i = 0; i < ARRAY_SIZE(sc->sc_fpregs.q.reserved); i++) { + u32 value; + + err = __get_user(value, &sc->sc_fpregs.q.reserved[i]); + if (unlikely(err)) + break; + if (value != 0) + return -EINVAL; + } + + if (has_dsp()) { + err = restore_dsp_state(regs, &sc->sc_dspregs); + if (unlikely(err)) + return err; + } + return err; } @@ -181,13 +181,21 @@ static long setup_sigcontext(struct rt_sigframe __user *frame, { struct sigcontext __user *sc = &frame->uc.uc_mcontext; long err; + size_t i; + /* sc_regs is structured the same as the start of pt_regs */ err = __copy_to_user(&sc->sc_regs, regs, sizeof(sc->sc_regs)); /* Save the floating-point state. */ if (has_fpu()) err |= save_fp_state(regs, &sc->sc_fpregs); + /* We support no other extension state at this time. */ + for (i = 0; i < ARRAY_SIZE(sc->sc_fpregs.q.reserved); i++) + err |= __put_user(0, &sc->sc_fpregs.q.reserved[i]); + + /* Save the DSP state. */ if (has_dsp()) err |= save_dsp_state(regs, &sc->sc_dspregs); + return err; } From 99cbe63d6af0fbc448cd3dad4c012e42a2c986b4 Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 5 Jun 2023 11:07:11 +0000 Subject: [PATCH 109/169] riscv: signal: Add sigcontext save/restore for vector This patch facilitates the existing fp-reserved words for placement of the first extension's context header on the user's sigframe. A context header consists of a distinct magic word and the size, including the header itself, of an extension on the stack. Then, the frame is followed by the context of that extension, and then a header + context body for another extension if exists. If there is no more extension to come, then the frame must be ended with a null context header. A special case is rv64gc, where the kernel support no extensions requiring to expose additional regfile to the user. In such case the kernel would place the null context header right after the first reserved word of __riscv_q_ext_state when saving sigframe. And the kernel would check if all reserved words are zeros when a signal handler returns. __riscv_q_ext_state---->| |<-__riscv_extra_ext_header ~ ~ .reserved[0]--->|0 |<- .reserved <-------|magic |<- .hdr | |size |_______ end of sc_fpregs | |ext-bdy| | ~ ~ +)size ------->|magic |<- another context header |size | |ext-bdy| ~ ~ |magic:0|<- null context header |size:0 | The vector registers will be saved in datap pointer. The datap pointer will be allocated dynamically when the task needs in kernel space. On the other hand, datap pointer on the sigframe will be set right after the __riscv_v_ext_state data structure. Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Suggested-by: Vineet Gupta Suggested-by: Richard Henderson Co-developed-by: Andy Chiu Signed-off-by: Andy Chiu Acked-by: Conor Dooley Acked-by: Heiko Stuebner Tested-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230605110724.21391-15-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/uapi/asm/ptrace.h | 15 ++ arch/riscv/include/uapi/asm/sigcontext.h | 16 ++- arch/riscv/kernel/setup.c | 3 + arch/riscv/kernel/signal.c | 174 +++++++++++++++++++++-- 4 files changed, 193 insertions(+), 15 deletions(-) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 9500b1f326986e..9d5a0bdadd7762 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -77,6 +77,21 @@ struct __riscv_q_ext_state { __u32 reserved[3]; }; +struct __riscv_ctx_hdr { + __u32 magic; + __u32 size; +}; + +struct __riscv_extra_ext_header { + __u32 __padding[129] __attribute__((aligned(16))); + /* + * Reserved for expansion of sigcontext structure. Currently zeroed + * upon signal, and must be zero upon sigreturn. + */ + __u32 reserved; + struct __riscv_ctx_hdr hdr; +}; + union __riscv_fp_state { struct __riscv_f_ext_state f; struct __riscv_d_ext_state d; diff --git a/arch/riscv/include/uapi/asm/sigcontext.h b/arch/riscv/include/uapi/asm/sigcontext.h index 94addf0067c5fe..9038467e67e6a9 100644 --- a/arch/riscv/include/uapi/asm/sigcontext.h +++ b/arch/riscv/include/uapi/asm/sigcontext.h @@ -8,6 +8,17 @@ #include +/* The Magic number for signal context frame header. */ +#define RISCV_V_MAGIC 0x53465457 +#define END_MAGIC 0x0 + +/* The size of END signal context header. */ +#define END_HDR_SIZE 0x0 + +struct __sc_riscv_v_state { + struct __riscv_v_ext_state v_state; +} __attribute__((aligned(16))); + /* * Signal context structure * @@ -16,10 +27,13 @@ */ struct sigcontext { struct user_regs_struct sc_regs; - union __riscv_fp_state sc_fpregs; #ifdef CONFIG_DSP struct __riscv_dsp_state sc_dspregs; #endif /* CONFIG_DSP */ + union { + union __riscv_fp_state sc_fpregs; + struct __riscv_extra_ext_header sc_extdesc; + }; }; #endif /* _UAPI_ASM_RISCV_SIGCONTEXT_H */ diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index 2acf51c2356738..5424d76315022b 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -262,6 +262,8 @@ static void __init parse_dtb(void) #endif } +extern void __init init_rt_signal_env(void); + void __init setup_arch(char **cmdline_p) { parse_dtb(); @@ -295,6 +297,7 @@ void __init setup_arch(char **cmdline_p) riscv_init_cbom_blocksize(); riscv_fill_hwcap(); + init_rt_signal_env(); apply_boot_alternatives(); } diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 4741f36a0145f4..686a5e1b3c1439 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -18,10 +18,12 @@ #include #include #include +#include #include #include extern u32 __user_rt_sigreturn[2]; +static size_t riscv_v_sc_size __ro_after_init; #define DEBUG_SIG 0 @@ -94,14 +96,89 @@ static long save_dsp_state(struct pt_regs *regs, #else #define save_dsp_state(task, regs) (0) #define restore_dsp_state(task, regs) (0) +#endif /* CONFIG_DSP */ + +#ifdef CONFIG_RISCV_ISA_V + +static long save_v_state(struct pt_regs *regs, void __user **sc_vec) +{ + struct __riscv_ctx_hdr __user *hdr; + struct __sc_riscv_v_state __user *state; + void __user *datap; + long err; + + hdr = *sc_vec; + /* Place state to the user's signal context space after the hdr */ + state = (struct __sc_riscv_v_state __user *)(hdr + 1); + /* Point datap right after the end of __sc_riscv_v_state */ + datap = state + 1; + + /* datap is designed to be 16 byte aligned for better performance */ + WARN_ON(unlikely(!IS_ALIGNED((unsigned long)datap, 16))); + + riscv_v_vstate_save(current, regs); + /* Copy everything of vstate but datap. */ + err = __copy_to_user(&state->v_state, ¤t->thread.vstate, + offsetof(struct __riscv_v_ext_state, datap)); + /* Copy the pointer datap itself. */ + err |= __put_user(datap, &state->v_state.datap); + /* Copy the whole vector content to user space datap. */ + err |= __copy_to_user(datap, current->thread.vstate.datap, riscv_v_vsize); + /* Copy magic to the user space after saving all vector conetext */ + err |= __put_user(RISCV_V_MAGIC, &hdr->magic); + err |= __put_user(riscv_v_sc_size, &hdr->size); + if (unlikely(err)) + return err; + + /* Only progress the sv_vec if everything has done successfully */ + *sc_vec += riscv_v_sc_size; + return 0; +} + +/* + * Restore Vector extension context from the user's signal frame. This function + * assumes a valid extension header. So magic and size checking must be done by + * the caller. + */ +static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec) +{ + long err; + struct __sc_riscv_v_state __user *state = sc_vec; + void __user *datap; + + /* Copy everything of __sc_riscv_v_state except datap. */ + err = __copy_from_user(¤t->thread.vstate, &state->v_state, + offsetof(struct __riscv_v_ext_state, datap)); + if (unlikely(err)) + return err; + + /* Copy the pointer datap itself. */ + err = __get_user(datap, &state->v_state.datap); + if (unlikely(err)) + return err; + /* + * Copy the whole vector content from user space datap. Use + * copy_from_user to prevent information leak. + */ + err = copy_from_user(current->thread.vstate.datap, datap, riscv_v_vsize); + if (unlikely(err)) + return err; + + riscv_v_vstate_restore(current, regs); + + return err; +} +#else +#define save_v_state(task, regs) (0) +#define __restore_v_state(task, regs) (0) #endif static long restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) { + void __user *sc_ext_ptr = &sc->sc_extdesc.hdr; + __u32 rsvd; long err; - size_t i; - /* sc_regs is structured the same as the start of pt_regs */ err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs)); if (unlikely(err)) @@ -114,15 +191,40 @@ static long restore_sigcontext(struct pt_regs *regs, return err; } - /* We support no other extension state at this time. */ - for (i = 0; i < ARRAY_SIZE(sc->sc_fpregs.q.reserved); i++) { - u32 value; + /* Check the reserved word before extensions parsing */ + err = __get_user(rsvd, &sc->sc_extdesc.reserved); + if (unlikely(err)) + return err; + if (unlikely(rsvd)) + return -EINVAL; + + while (!err) { + __u32 magic, size; + struct __riscv_ctx_hdr __user *head = sc_ext_ptr; - err = __get_user(value, &sc->sc_fpregs.q.reserved[i]); + err |= __get_user(magic, &head->magic); + err |= __get_user(size, &head->size); if (unlikely(err)) + return err; + + sc_ext_ptr += sizeof(*head); + switch (magic) { + case END_MAGIC: + if (size != END_HDR_SIZE) + return -EINVAL; + + return 0; + case RISCV_V_MAGIC: + if (!has_vector() || !riscv_v_vstate_query(regs) || + size != riscv_v_sc_size) + return -EINVAL; + + err = __restore_v_state(regs, sc_ext_ptr); break; - if (value != 0) + default: return -EINVAL; + } + sc_ext_ptr = (void __user *)head + size; } if (has_dsp()) { @@ -134,19 +236,43 @@ static long restore_sigcontext(struct pt_regs *regs, return err; } +static size_t get_rt_frame_size(void) +{ + struct rt_sigframe __user *frame; + size_t frame_size; + size_t total_context_size = 0; + + frame_size = sizeof(*frame); + + if (has_vector() && riscv_v_vstate_query(task_pt_regs(current))) + total_context_size += riscv_v_sc_size; + /* + * Preserved a __riscv_ctx_hdr for END signal context header if an + * extension uses __riscv_extra_ext_header + */ + if (total_context_size) + total_context_size += sizeof(struct __riscv_ctx_hdr); + + frame_size += total_context_size; + + frame_size = round_up(frame_size, 16); + return frame_size; +} + SYSCALL_DEFINE0(rt_sigreturn) { struct pt_regs *regs = current_pt_regs(); struct rt_sigframe __user *frame; struct task_struct *task; sigset_t set; + size_t frame_size = get_rt_frame_size(); /* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; frame = (struct rt_sigframe __user *)regs->sp; - if (!access_ok(frame, sizeof(*frame))) + if (!access_ok(frame, frame_size)) goto badframe; if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) @@ -180,17 +306,22 @@ static long setup_sigcontext(struct rt_sigframe __user *frame, struct pt_regs *regs) { struct sigcontext __user *sc = &frame->uc.uc_mcontext; + struct __riscv_ctx_hdr __user *sc_ext_ptr = &sc->sc_extdesc.hdr; long err; - size_t i; /* sc_regs is structured the same as the start of pt_regs */ err = __copy_to_user(&sc->sc_regs, regs, sizeof(sc->sc_regs)); /* Save the floating-point state. */ if (has_fpu()) err |= save_fp_state(regs, &sc->sc_fpregs); - /* We support no other extension state at this time. */ - for (i = 0; i < ARRAY_SIZE(sc->sc_fpregs.q.reserved); i++) - err |= __put_user(0, &sc->sc_fpregs.q.reserved[i]); + /* Save the vector state. */ + if (has_vector() && riscv_v_vstate_query(regs)) + err |= save_v_state(regs, (void __user **)&sc_ext_ptr); + /* Write zero to fp-reserved space and check it on restore_sigcontext */ + err |= __put_user(0, &sc->sc_extdesc.reserved); + /* And put END __riscv_ctx_hdr at the end. */ + err |= __put_user(END_MAGIC, &sc_ext_ptr->magic); + err |= __put_user(END_HDR_SIZE, &sc_ext_ptr->size); /* Save the DSP state. */ if (has_dsp()) @@ -219,6 +350,13 @@ static inline void __user *get_sigframe(struct ksignal *ksig, /* Align the stack frame. */ sp &= ~0xfUL; + /* + * Fail if the size of the altstack is not large enough for the + * sigframe construction. + */ + if (current->sas_ss_size && sp < current->sas_ss_sp) + return (void __user __force *)-1UL; + return (void __user *)sp; } @@ -228,9 +366,10 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct rt_sigframe __user *frame; long err = 0; unsigned long __maybe_unused addr; + size_t frame_size = get_rt_frame_size(); - frame = get_sigframe(ksig, regs, sizeof(*frame)); - if (!access_ok(frame, sizeof(*frame))) + frame = get_sigframe(ksig, regs, frame_size); + if (!access_ok(frame, frame_size)) return -EFAULT; err |= copy_siginfo_to_user(&frame->info, &ksig->info); @@ -389,3 +528,10 @@ asmlinkage __visible void do_work_pending(struct pt_regs *regs, thread_info_flags = read_thread_flags(); } while (thread_info_flags & _TIF_WORK_MASK); } + +void init_rt_signal_env(void); +void __init init_rt_signal_env(void) +{ + riscv_v_sc_size = sizeof(struct __riscv_ctx_hdr) + + sizeof(struct __sc_riscv_v_state) + riscv_v_vsize; +} From d9f5d365bff69faaee629dcbe50ddb8b3cfc9df3 Mon Sep 17 00:00:00 2001 From: Vincent Chen Date: Mon, 5 Jun 2023 11:07:12 +0000 Subject: [PATCH 110/169] riscv: signal: Report signal frame size to userspace via auxv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vector register belongs to the signal context. They need to be stored and restored as entering and leaving the signal handler. According to the V-extension specification, the maximum length of the vector registers can be 2^16. Hence, if userspace refers to the MINSIGSTKSZ to create a sigframe, it may not be enough. To resolve this problem, this patch refers to the commit 94b07c1f8c39c ("arm64: signal: Report signal frame size to userspace via auxv") to enable userspace to know the minimum required sigframe size through the auxiliary vector and use it to allocate enough memory for signal context. Note that auxv always reports size of the sigframe as if V exists for all starting processes, whenever the kernel has CONFIG_RISCV_ISA_V. The reason is that users usually reference this value to allocate an alternative signal stack, and the user may use V anytime. So the user must reserve a space for V-context in sigframe in case that the signal handler invokes after the kernel allocating V. Signed-off-by: Greentime Hu Signed-off-by: Vincent Chen Signed-off-by: Andy Chiu Acked-by: Conor Dooley Reviewed-by: Björn Töpel Reviewed-by: Guo Ren Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230605110724.21391-16-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/elf.h | 9 +++++++++ arch/riscv/include/asm/processor.h | 2 ++ arch/riscv/include/uapi/asm/auxvec.h | 1 + arch/riscv/kernel/signal.c | 20 +++++++++++++++----- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h index e7acffdf21d266..c7eb40383453dd 100644 --- a/arch/riscv/include/asm/elf.h +++ b/arch/riscv/include/asm/elf.h @@ -103,6 +103,15 @@ do { \ get_cache_size(3, CACHE_TYPE_UNIFIED)); \ NEW_AUX_ENT(AT_L3_CACHEGEOMETRY, \ get_cache_geometry(3, CACHE_TYPE_UNIFIED)); \ + /* \ + * Should always be nonzero unless there's a kernel bug. \ + * If we haven't determined a sensible value to give to \ + * userspace, omit the entry: \ + */ \ + if (likely(signal_minsigstksz)) \ + NEW_AUX_ENT(AT_MINSIGSTKSZ, signal_minsigstksz); \ + else \ + NEW_AUX_ENT(AT_IGNORE, 0); \ } while (0) #define ARCH_HAS_SETUP_ADDITIONAL_PAGES struct linux_binprm; diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h index 3dc3c6dfbbf369..b166e5e50073b4 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -7,6 +7,7 @@ #define _ASM_RISCV_PROCESSOR_H #include +#include #include @@ -84,6 +85,7 @@ int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid); extern void riscv_fill_hwcap(void); extern int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); +extern unsigned long signal_minsigstksz __ro_after_init; #endif /* __ASSEMBLY__ */ #endif /* _ASM_RISCV_PROCESSOR_H */ diff --git a/arch/riscv/include/uapi/asm/auxvec.h b/arch/riscv/include/uapi/asm/auxvec.h index fb187a33ce5897..10aaa83db89ef7 100644 --- a/arch/riscv/include/uapi/asm/auxvec.h +++ b/arch/riscv/include/uapi/asm/auxvec.h @@ -35,5 +35,6 @@ /* entries in ARCH_DLINFO */ #define AT_VECTOR_SIZE_ARCH 9 +#define AT_MINSIGSTKSZ 51 #endif /* _UAPI_ASM_RISCV_AUXVEC_H */ diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 686a5e1b3c1439..bbe72425be5aca 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -22,6 +22,8 @@ #include #include +unsigned long signal_minsigstksz __ro_after_init; + extern u32 __user_rt_sigreturn[2]; static size_t riscv_v_sc_size __ro_after_init; @@ -236,7 +238,7 @@ static long restore_sigcontext(struct pt_regs *regs, return err; } -static size_t get_rt_frame_size(void) +static size_t get_rt_frame_size(bool cal_all) { struct rt_sigframe __user *frame; size_t frame_size; @@ -244,8 +246,10 @@ static size_t get_rt_frame_size(void) frame_size = sizeof(*frame); - if (has_vector() && riscv_v_vstate_query(task_pt_regs(current))) - total_context_size += riscv_v_sc_size; + if (has_vector()) { + if (cal_all || riscv_v_vstate_query(task_pt_regs(current))) + total_context_size += riscv_v_sc_size; + } /* * Preserved a __riscv_ctx_hdr for END signal context header if an * extension uses __riscv_extra_ext_header @@ -265,7 +269,7 @@ SYSCALL_DEFINE0(rt_sigreturn) struct rt_sigframe __user *frame; struct task_struct *task; sigset_t set; - size_t frame_size = get_rt_frame_size(); + size_t frame_size = get_rt_frame_size(false); /* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; @@ -366,7 +370,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct rt_sigframe __user *frame; long err = 0; unsigned long __maybe_unused addr; - size_t frame_size = get_rt_frame_size(); + size_t frame_size = get_rt_frame_size(false); frame = get_sigframe(ksig, regs, frame_size); if (!access_ok(frame, frame_size)) @@ -534,4 +538,10 @@ void __init init_rt_signal_env(void) { riscv_v_sc_size = sizeof(struct __riscv_ctx_hdr) + sizeof(struct __sc_riscv_v_state) + riscv_v_vsize; + /* + * Determine the stack space required for guaranteed signal delivery. + * The signal_minsigstksz will be populated into the AT_MINSIGSTKSZ entry + * in the auxiliary array at process startup. + */ + signal_minsigstksz = get_rt_frame_size(true); } From 3c95c0faaf14b1a6314aea164b530e71a02b733f Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Mon, 5 Jun 2023 11:07:13 +0000 Subject: [PATCH 111/169] riscv: signal: validate altstack to reflect Vector Some extensions, such as Vector, dynamically change footprint on a signal frame, so MINSIGSTKSZ is no longer accurate. For example, an RV64V implementation with vlen = 512 may occupy 2K + 40 + 12 Bytes of a signal frame with the upcoming support. And processes that do not execute any vector instructions do not need to reserve the extra sigframe. So we need a way to guard the allocation size of the sigframe at process runtime according to current status of V. Thus, provide the function sigaltstack_size_valid() to validate its size based on current allocation status of supported extensions. Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230605110724.21391-17-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/signal.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index bbe72425be5aca..9581b883f3eec6 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -545,3 +545,10 @@ void __init init_rt_signal_env(void) */ signal_minsigstksz = get_rt_frame_size(true); } + +#ifdef CONFIG_DYNAMIC_SIGFRAME +bool sigaltstack_size_valid(size_t ss_size) +{ + return ss_size > get_rt_frame_size(false); +} +#endif /* CONFIG_DYNAMIC_SIGFRAME */ From 02dcb6961a81d223643c2cc63e3c94c8acbfa9cb Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Mon, 5 Jun 2023 11:07:14 +0000 Subject: [PATCH 112/169] riscv: prevent stack corruption by reserving task_pt_regs(p) early Early function calls, such as setup_vm(), relocate_enable_mmu(), soc_early_init() etc, are free to operate on stack. However, PT_SIZE_ON_STACK bytes at the head of the kernel stack are purposedly reserved for the placement of per-task register context pointed by task_pt_regs(p). Those functions may corrupt task_pt_regs if we overlap the $sp with it. In fact, we had accidentally corrupted sstatus.VS in some tests, treating the kernel to save V context before V was actually allocated, resulting in a kernel panic. Thus, we should skip PT_SIZE_ON_STACK for $sp before making C function calls from the top-level assembly. Co-developed-by: ShihPo Hung Signed-off-by: ShihPo Hung Co-developed-by: Vincent Chen Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230605110724.21391-18-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/head.S | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S index e16bb2185d5512..11c3b94c4534f9 100644 --- a/arch/riscv/kernel/head.S +++ b/arch/riscv/kernel/head.S @@ -301,6 +301,7 @@ clear_bss_done: la tp, init_task la sp, init_thread_union + THREAD_SIZE XIP_FIXUP_OFFSET sp + addi sp, sp, -PT_SIZE_ON_STACK #ifdef CONFIG_BUILTIN_DTB la a0, __dtb_start XIP_FIXUP_OFFSET a0 @@ -318,6 +319,7 @@ clear_bss_done: /* Restore C environment */ la tp, init_task la sp, init_thread_union + THREAD_SIZE + addi sp, sp, -PT_SIZE_ON_STACK #ifdef CONFIG_KASAN call kasan_early_init From 3c544982cf5f90c07f0462a8afadd455b711349d Mon Sep 17 00:00:00 2001 From: Vincent Chen Date: Mon, 5 Jun 2023 11:07:15 +0000 Subject: [PATCH 113/169] riscv: kvm: Add V extension to KVM ISA Add V extension to KVM isa extension list to enable supporting of V extension on VCPUs. Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Anup Patel Acked-by: Anup Patel Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230605110724.21391-19-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/uapi/asm/kvm.h | 1 + arch/riscv/kvm/vcpu.c | 1 + 2 files changed, 2 insertions(+) diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h index 8985ff234c01cd..8f37444dd49265 100644 --- a/arch/riscv/include/uapi/asm/kvm.h +++ b/arch/riscv/include/uapi/asm/kvm.h @@ -102,6 +102,7 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_SVINVAL, KVM_RISCV_ISA_EXT_ZIHINTPAUSE, KVM_RISCV_ISA_EXT_ZICBOM, + KVM_RISCV_ISA_EXT_V, KVM_RISCV_ISA_EXT_MAX, }; diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index 5174ef54ad1d9e..0c293ddd980a62 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -56,6 +56,7 @@ static const unsigned long kvm_isa_ext_arr[] = { [KVM_RISCV_ISA_EXT_H] = RISCV_ISA_EXT_h, [KVM_RISCV_ISA_EXT_I] = RISCV_ISA_EXT_i, [KVM_RISCV_ISA_EXT_M] = RISCV_ISA_EXT_m, + [KVM_RISCV_ISA_EXT_V] = RISCV_ISA_EXT_v, KVM_ISA_EXT_ARR(SSTC), KVM_ISA_EXT_ARR(SVINVAL), From a90e22096043b4f42778fc9eb6fc126e98742eb5 Mon Sep 17 00:00:00 2001 From: Vincent Chen Date: Mon, 5 Jun 2023 11:07:16 +0000 Subject: [PATCH 114/169] riscv: KVM: Add vector lazy save/restore support This patch adds vector context save/restore for guest VCPUs. To reduce the impact on KVM performance, the implementation imitates the FP context switch mechanism to lazily store and restore the vector context only when the kernel enters/exits the in-kernel run loop and not during the KVM world switch. Signed-off-by: Vincent Chen Signed-off-by: Greentime Hu Signed-off-by: Andy Chiu Reviewed-by: Anup Patel Acked-by: Anup Patel Link: https://lore.kernel.org/r/20230605110724.21391-20-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/kvm_host.h | 1 + arch/riscv/include/asm/kvm_vcpu_vector.h | 82 ++++++++++ arch/riscv/include/uapi/asm/kvm.h | 7 + arch/riscv/kvm/Makefile | 1 + arch/riscv/kvm/vcpu.c | 24 ++- arch/riscv/kvm/vcpu_vector.c | 186 +++++++++++++++++++++++ 6 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 arch/riscv/include/asm/kvm_vcpu_vector.h create mode 100644 arch/riscv/kvm/vcpu_vector.c diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index dbbf43d5262348..3aefca8078e669 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -144,6 +144,7 @@ struct kvm_cpu_context { unsigned long sstatus; unsigned long hstatus; union __riscv_fp_state fp; + struct __riscv_v_ext_state vector; }; struct kvm_vcpu_csr { diff --git a/arch/riscv/include/asm/kvm_vcpu_vector.h b/arch/riscv/include/asm/kvm_vcpu_vector.h new file mode 100644 index 00000000000000..ff994fdd6d0d1c --- /dev/null +++ b/arch/riscv/include/asm/kvm_vcpu_vector.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 SiFive + * + * Authors: + * Vincent Chen + * Greentime Hu + */ + +#ifndef __KVM_VCPU_RISCV_VECTOR_H +#define __KVM_VCPU_RISCV_VECTOR_H + +#include + +#ifdef CONFIG_RISCV_ISA_V +#include +#include + +static __always_inline void __kvm_riscv_vector_save(struct kvm_cpu_context *context) +{ + __riscv_v_vstate_save(&context->vector, context->vector.datap); +} + +static __always_inline void __kvm_riscv_vector_restore(struct kvm_cpu_context *context) +{ + __riscv_v_vstate_restore(&context->vector, context->vector.datap); +} + +void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu); +void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx, + unsigned long *isa); +void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx, + unsigned long *isa); +void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx); +void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx); +int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu, + struct kvm_cpu_context *cntx); +void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu); +#else + +struct kvm_cpu_context; + +static inline void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu) +{ +} + +static inline void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx, + unsigned long *isa) +{ +} + +static inline void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx, + unsigned long *isa) +{ +} + +static inline void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx) +{ +} + +static inline void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx) +{ +} + +static inline int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu, + struct kvm_cpu_context *cntx) +{ + return 0; +} + +static inline void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu) +{ +} +#endif + +int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, + unsigned long rtype); +int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, + unsigned long rtype); +#endif diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h index 8f37444dd49265..85424a3474d3f5 100644 --- a/arch/riscv/include/uapi/asm/kvm.h +++ b/arch/riscv/include/uapi/asm/kvm.h @@ -150,6 +150,13 @@ enum KVM_RISCV_ISA_EXT_ID { /* ISA Extension registers are mapped as type 7 */ #define KVM_REG_RISCV_ISA_EXT (0x07 << KVM_REG_RISCV_TYPE_SHIFT) +/* V extension registers are mapped as type 9 */ +#define KVM_REG_RISCV_VECTOR (0x09 << KVM_REG_RISCV_TYPE_SHIFT) +#define KVM_REG_RISCV_VECTOR_CSR_REG(name) \ + (offsetof(struct __riscv_v_ext_state, name) / sizeof(unsigned long)) +#define KVM_REG_RISCV_VECTOR_REG(n) \ + ((n) + sizeof(struct __riscv_v_ext_state) / sizeof(unsigned long)) + #endif #endif /* __LINUX_KVM_RISCV_H */ diff --git a/arch/riscv/kvm/Makefile b/arch/riscv/kvm/Makefile index 019df9208bdd8d..b26bc605a26790 100644 --- a/arch/riscv/kvm/Makefile +++ b/arch/riscv/kvm/Makefile @@ -17,6 +17,7 @@ kvm-y += mmu.o kvm-y += vcpu.o kvm-y += vcpu_exit.o kvm-y += vcpu_fp.o +kvm-y += vcpu_vector.o kvm-y += vcpu_insn.o kvm-y += vcpu_switch.o kvm-y += vcpu_sbi.o diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index 0c293ddd980a62..d6475a0c989881 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include const struct _kvm_stats_desc kvm_vcpu_stats_desc[] = { KVM_GENERIC_VCPU_STATS(), @@ -133,6 +135,8 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu) kvm_riscv_vcpu_fp_reset(vcpu); + kvm_riscv_vcpu_vector_reset(vcpu); + kvm_riscv_vcpu_timer_reset(vcpu); WRITE_ONCE(vcpu->arch.irqs_pending, 0); @@ -183,6 +187,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) cntx->hstatus |= HSTATUS_SPVP; cntx->hstatus |= HSTATUS_SPV; + if (kvm_riscv_vcpu_alloc_vector_context(vcpu, cntx)) + return -ENOMEM; + /* By default, make CY, TM, and IR counters accessible in VU mode */ reset_csr->scounteren = 0x7; @@ -213,6 +220,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) /* Free unused pages pre-allocated for G-stage page table mappings */ kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache); + + /* Free vector context space for host and guest kernel */ + kvm_riscv_vcpu_free_vector_context(vcpu); } int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) @@ -561,7 +571,9 @@ static int kvm_riscv_vcpu_set_reg(struct kvm_vcpu *vcpu, KVM_REG_RISCV_FP_D); else if ((reg->id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_ISA_EXT) return kvm_riscv_vcpu_set_reg_isa_ext(vcpu, reg); - + else if ((reg->id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_VECTOR) + return kvm_riscv_vcpu_set_reg_vector(vcpu, reg, + KVM_REG_RISCV_VECTOR); return -EINVAL; } @@ -584,7 +596,9 @@ static int kvm_riscv_vcpu_get_reg(struct kvm_vcpu *vcpu, KVM_REG_RISCV_FP_D); else if ((reg->id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_ISA_EXT) return kvm_riscv_vcpu_get_reg_isa_ext(vcpu, reg); - + else if ((reg->id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_VECTOR) + return kvm_riscv_vcpu_get_reg_vector(vcpu, reg, + KVM_REG_RISCV_VECTOR); return -EINVAL; } @@ -847,6 +861,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) kvm_riscv_vcpu_host_fp_save(&vcpu->arch.host_context); kvm_riscv_vcpu_guest_fp_restore(&vcpu->arch.guest_context, vcpu->arch.isa); + kvm_riscv_vcpu_host_vector_save(&vcpu->arch.host_context); + kvm_riscv_vcpu_guest_vector_restore(&vcpu->arch.guest_context, + vcpu->arch.isa); vcpu->cpu = cpu; } @@ -862,6 +879,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) kvm_riscv_vcpu_host_fp_restore(&vcpu->arch.host_context); kvm_riscv_vcpu_timer_save(vcpu); + kvm_riscv_vcpu_guest_vector_save(&vcpu->arch.guest_context, + vcpu->arch.isa); + kvm_riscv_vcpu_host_vector_restore(&vcpu->arch.host_context); csr->vsstatus = csr_read(CSR_VSSTATUS); csr->vsie = csr_read(CSR_VSIE); diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c new file mode 100644 index 00000000000000..edd2eecbddc2ed --- /dev/null +++ b/arch/riscv/kvm/vcpu_vector.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 SiFive + * + * Authors: + * Vincent Chen + * Greentime Hu + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_RISCV_ISA_V +void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu) +{ + unsigned long *isa = vcpu->arch.isa; + struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; + + cntx->sstatus &= ~SR_VS; + if (riscv_isa_extension_available(isa, v)) { + cntx->sstatus |= SR_VS_INITIAL; + WARN_ON(!cntx->vector.datap); + memset(cntx->vector.datap, 0, riscv_v_vsize); + } else { + cntx->sstatus |= SR_VS_OFF; + } +} + +static void kvm_riscv_vcpu_vector_clean(struct kvm_cpu_context *cntx) +{ + cntx->sstatus &= ~SR_VS; + cntx->sstatus |= SR_VS_CLEAN; +} + +void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx, + unsigned long *isa) +{ + if ((cntx->sstatus & SR_VS) == SR_VS_DIRTY) { + if (riscv_isa_extension_available(isa, v)) + __kvm_riscv_vector_save(cntx); + kvm_riscv_vcpu_vector_clean(cntx); + } +} + +void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx, + unsigned long *isa) +{ + if ((cntx->sstatus & SR_VS) != SR_VS_OFF) { + if (riscv_isa_extension_available(isa, v)) + __kvm_riscv_vector_restore(cntx); + kvm_riscv_vcpu_vector_clean(cntx); + } +} + +void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx) +{ + /* No need to check host sstatus as it can be modified outside */ + if (riscv_isa_extension_available(NULL, v)) + __kvm_riscv_vector_save(cntx); +} + +void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx) +{ + if (riscv_isa_extension_available(NULL, v)) + __kvm_riscv_vector_restore(cntx); +} + +int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu, + struct kvm_cpu_context *cntx) +{ + cntx->vector.datap = kmalloc(riscv_v_vsize, GFP_KERNEL); + if (!cntx->vector.datap) + return -ENOMEM; + + vcpu->arch.host_context.vector.datap = kzalloc(riscv_v_vsize, GFP_KERNEL); + if (!vcpu->arch.host_context.vector.datap) + return -ENOMEM; + + return 0; +} + +void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu) +{ + kfree(vcpu->arch.guest_reset_context.vector.datap); + kfree(vcpu->arch.host_context.vector.datap); +} +#endif + +static void *kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu, + unsigned long reg_num, + size_t reg_size) +{ + struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; + void *reg_val; + size_t vlenb = riscv_v_vsize / 32; + + if (reg_num < KVM_REG_RISCV_VECTOR_REG(0)) { + if (reg_size != sizeof(unsigned long)) + return NULL; + switch (reg_num) { + case KVM_REG_RISCV_VECTOR_CSR_REG(vstart): + reg_val = &cntx->vector.vstart; + break; + case KVM_REG_RISCV_VECTOR_CSR_REG(vl): + reg_val = &cntx->vector.vl; + break; + case KVM_REG_RISCV_VECTOR_CSR_REG(vtype): + reg_val = &cntx->vector.vtype; + break; + case KVM_REG_RISCV_VECTOR_CSR_REG(vcsr): + reg_val = &cntx->vector.vcsr; + break; + case KVM_REG_RISCV_VECTOR_CSR_REG(datap): + default: + return NULL; + } + } else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) { + if (reg_size != vlenb) + return NULL; + reg_val = cntx->vector.datap + + (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb; + } else { + return NULL; + } + + return reg_val; +} + +int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, + unsigned long rtype) +{ + unsigned long *isa = vcpu->arch.isa; + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + rtype); + void *reg_val = NULL; + size_t reg_size = KVM_REG_SIZE(reg->id); + + if (rtype == KVM_REG_RISCV_VECTOR && + riscv_isa_extension_available(isa, v)) { + reg_val = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size); + } + + if (!reg_val) + return -EINVAL; + + if (copy_to_user(uaddr, reg_val, reg_size)) + return -EFAULT; + + return 0; +} + +int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu, + const struct kvm_one_reg *reg, + unsigned long rtype) +{ + unsigned long *isa = vcpu->arch.isa; + unsigned long __user *uaddr = + (unsigned long __user *)(unsigned long)reg->addr; + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | + KVM_REG_SIZE_MASK | + rtype); + void *reg_val = NULL; + size_t reg_size = KVM_REG_SIZE(reg->id); + + if (rtype == KVM_REG_RISCV_VECTOR && + riscv_isa_extension_available(isa, v)) { + reg_val = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size); + } + + if (!reg_val) + return -EINVAL; + + if (copy_from_user(reg_val, uaddr, reg_size)) + return -EFAULT; + + return 0; +} From 666aab628685096967d9b56adc1c20ac59e82a41 Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Mon, 5 Jun 2023 11:07:17 +0000 Subject: [PATCH 115/169] riscv: hwcap: change ELF_HWCAP to a function Using a function is flexible to represent ELF_HWCAP. So the kernel may encode hwcap reflecting supported hardware features just at the moment of the start of each program. This will be helpful when we introduce prctl/sysctl interface to control per-process availability of Vector extension in following patches. Programs started with V disabled should see V masked off in theirs ELF_HWCAP. Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20230605110724.21391-21-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/elf.h | 2 +- arch/riscv/include/asm/hwcap.h | 2 ++ arch/riscv/kernel/cpufeature.c | 5 +++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h index c7eb40383453dd..ceb9c02a802e83 100644 --- a/arch/riscv/include/asm/elf.h +++ b/arch/riscv/include/asm/elf.h @@ -64,7 +64,7 @@ extern bool compat_elf_check_arch(Elf32_Ehdr *hdr); * instruction set this CPU supports. This could be done in user space, * but it's not easy, and we've already done it here. */ -#define ELF_HWCAP (elf_hwcap) +#define ELF_HWCAP riscv_get_elf_hwcap() extern unsigned long elf_hwcap; /* diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h index ddb9ecb20426af..042f29612559d7 100644 --- a/arch/riscv/include/asm/hwcap.h +++ b/arch/riscv/include/asm/hwcap.h @@ -78,6 +78,8 @@ enum riscv_isa_ext_key { RISCV_ISA_EXT_KEY_MAX, }; +unsigned long riscv_get_elf_hwcap(void); + struct riscv_isa_ext_data { /* Name of the extension displayed to userspace via /proc/cpuinfo */ char uprop[RISCV_ISA_EXT_NAME_LEN_MAX]; diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index ec94c4dda31bf5..cfe530c2db7d12 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -274,6 +274,11 @@ void __init riscv_fill_hwcap(void) } } +unsigned long riscv_get_elf_hwcap(void) +{ + return (elf_hwcap & ((1UL << RISCV_ISA_EXT_BASE) - 1)); +} + #ifdef CONFIG_RISCV_ALTERNATIVE static bool __init_or_module cpufeature_probe_svpbmt(unsigned int stage) { From 54ec507ea2c6d45861aeacb8e45d7b45c678b1a5 Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Mon, 5 Jun 2023 11:07:18 +0000 Subject: [PATCH 116/169] riscv: Add prctl controls for userspace vector management This patch add two riscv-specific prctls, to allow usespace control the use of vector unit: * PR_RISCV_V_SET_CONTROL: control the permission to use Vector at next, or all following execve for a thread. Turning off a thread's Vector live is not possible since libraries may have registered ifunc that may execute Vector instructions. * PR_RISCV_V_GET_CONTROL: get the same permission setting for the current thread, and the setting for following execve(s). Signed-off-by: Andy Chiu Reviewed-by: Greentime Hu Reviewed-by: Vincent Chen Link: https://lore.kernel.org/r/20230605110724.21391-22-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/hwcap.h | 5 -- arch/riscv/include/asm/processor.h | 10 +++ arch/riscv/include/asm/vector.h | 4 + arch/riscv/kernel/cpufeature.c | 9 ++- arch/riscv/kernel/process.c | 1 + arch/riscv/kernel/vector.c | 114 +++++++++++++++++++++++++++++ arch/riscv/kvm/vcpu.c | 2 + include/uapi/linux/prctl.h | 15 ++++ kernel/sys.c | 12 +++ 9 files changed, 166 insertions(+), 6 deletions(-) diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h index 042f29612559d7..3eaccaa906604d 100644 --- a/arch/riscv/include/asm/hwcap.h +++ b/arch/riscv/include/asm/hwcap.h @@ -14,11 +14,6 @@ #ifndef __ASSEMBLY__ #include -/* - * This yields a mask that user programs can use to figure out what - * instruction set this cpu supports. - */ -#define ELF_HWCAP (elf_hwcap) enum { CAP_HWCAP = 1, diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h index b166e5e50073b4..c07345b6700839 100644 --- a/arch/riscv/include/asm/processor.h +++ b/arch/riscv/include/asm/processor.h @@ -43,6 +43,7 @@ struct thread_struct { struct __riscv_dsp_state dspstate; #endif unsigned long bad_cause; + unsigned long vstate_ctrl; struct __riscv_v_ext_state vstate; }; @@ -86,6 +87,15 @@ extern void riscv_fill_hwcap(void); extern int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); extern unsigned long signal_minsigstksz __ro_after_init; + +#ifdef CONFIG_RISCV_ISA_V +/* Userspace interface for PR_RISCV_V_{SET,GET}_VS prctl()s: */ +#define RISCV_V_SET_CONTROL(arg) riscv_v_vstate_ctrl_set_current(arg) +#define RISCV_V_GET_CONTROL() riscv_v_vstate_ctrl_get_current() +extern long riscv_v_vstate_ctrl_set_current(unsigned long arg); +extern long riscv_v_vstate_ctrl_get_current(void); +#endif /* CONFIG_RISCV_ISA_V */ + #endif /* __ASSEMBLY__ */ #endif /* _ASM_RISCV_PROCESSOR_H */ diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index e9c829f4712ffd..e7df4e0ac79387 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -160,6 +160,9 @@ static inline void __switch_to_vector(struct task_struct *prev, riscv_v_vstate_restore(next, task_pt_regs(next)); } +void riscv_v_vstate_ctrl_init(struct task_struct *tsk); +bool riscv_v_vstate_ctrl_user_allowed(void); + #else /* ! CONFIG_RISCV_ISA_V */ struct pt_regs; @@ -168,6 +171,7 @@ static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; } static __always_inline bool has_vector(void) { return false; } static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; } static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; } +static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; } #define riscv_v_vsize (0) #define riscv_v_vstate_save(task, regs) do {} while (0) #define riscv_v_vstate_restore(task, regs) do {} while (0) diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c index cfe530c2db7d12..dc98f0b503f78a 100644 --- a/arch/riscv/kernel/cpufeature.c +++ b/arch/riscv/kernel/cpufeature.c @@ -276,7 +276,14 @@ void __init riscv_fill_hwcap(void) unsigned long riscv_get_elf_hwcap(void) { - return (elf_hwcap & ((1UL << RISCV_ISA_EXT_BASE) - 1)); + unsigned long hwcap; + + hwcap = (elf_hwcap & ((1UL << RISCV_ISA_EXT_BASE) - 1)); + + if (!riscv_v_vstate_ctrl_user_allowed()) + hwcap &= ~COMPAT_HWCAP_ISA_V; + + return hwcap; } #ifdef CONFIG_RISCV_ALTERNATIVE diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index 6e359f3928a472..1dc6cc1974932c 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -154,6 +154,7 @@ void flush_thread(void) #endif #ifdef CONFIG_RISCV_ISA_V /* Reset vector state */ + riscv_v_vstate_ctrl_init(current); riscv_v_vstate_off(task_pt_regs(current)); kfree(current->thread.vstate.datap); memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c index 9d81d1b2a7f398..a7dec923016443 100644 --- a/arch/riscv/kernel/vector.c +++ b/arch/riscv/kernel/vector.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,8 @@ #include #include +static bool riscv_v_implicit_uacc = IS_ENABLED(CONFIG_RISCV_ISA_V_DEFAULT_ENABLE); + unsigned long riscv_v_vsize __read_mostly; EXPORT_SYMBOL_GPL(riscv_v_vsize); @@ -91,6 +94,43 @@ static int riscv_v_thread_zalloc(void) return 0; } +#define VSTATE_CTRL_GET_CUR(x) ((x) & PR_RISCV_V_VSTATE_CTRL_CUR_MASK) +#define VSTATE_CTRL_GET_NEXT(x) (((x) & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK) >> 2) +#define VSTATE_CTRL_MAKE_NEXT(x) (((x) << 2) & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK) +#define VSTATE_CTRL_GET_INHERIT(x) (!!((x) & PR_RISCV_V_VSTATE_CTRL_INHERIT)) +static inline int riscv_v_ctrl_get_cur(struct task_struct *tsk) +{ + return VSTATE_CTRL_GET_CUR(tsk->thread.vstate_ctrl); +} + +static inline int riscv_v_ctrl_get_next(struct task_struct *tsk) +{ + return VSTATE_CTRL_GET_NEXT(tsk->thread.vstate_ctrl); +} + +static inline bool riscv_v_ctrl_test_inherit(struct task_struct *tsk) +{ + return VSTATE_CTRL_GET_INHERIT(tsk->thread.vstate_ctrl); +} + +static inline void riscv_v_ctrl_set(struct task_struct *tsk, int cur, int nxt, + bool inherit) +{ + unsigned long ctrl; + + ctrl = cur & PR_RISCV_V_VSTATE_CTRL_CUR_MASK; + ctrl |= VSTATE_CTRL_MAKE_NEXT(nxt); + if (inherit) + ctrl |= PR_RISCV_V_VSTATE_CTRL_INHERIT; + tsk->thread.vstate_ctrl = ctrl; +} + +bool riscv_v_vstate_ctrl_user_allowed(void) +{ + return riscv_v_ctrl_get_cur(current) == PR_RISCV_V_VSTATE_CTRL_ON; +} +EXPORT_SYMBOL_GPL(riscv_v_vstate_ctrl_user_allowed); + bool riscv_v_first_use_handler(struct pt_regs *regs) { u32 __user *epc = (u32 __user *)regs->epc; @@ -129,3 +169,77 @@ bool riscv_v_first_use_handler(struct pt_regs *regs) riscv_v_vstate_on(regs); return true; } + +void riscv_v_vstate_ctrl_init(struct task_struct *tsk) +{ + bool inherit; + int cur, next; + + if (!has_vector()) + return; + + next = riscv_v_ctrl_get_next(tsk); + if (!next) { + if (riscv_v_implicit_uacc) + cur = PR_RISCV_V_VSTATE_CTRL_ON; + else + cur = PR_RISCV_V_VSTATE_CTRL_OFF; + } else { + cur = next; + } + /* Clear next mask if inherit-bit is not set */ + inherit = riscv_v_ctrl_test_inherit(tsk); + if (!inherit) + next = PR_RISCV_V_VSTATE_CTRL_DEFAULT; + + riscv_v_ctrl_set(tsk, cur, next, inherit); +} + +long riscv_v_vstate_ctrl_get_current(void) +{ + if (!has_vector()) + return -EINVAL; + + return current->thread.vstate_ctrl & PR_RISCV_V_VSTATE_CTRL_MASK; +} + +long riscv_v_vstate_ctrl_set_current(unsigned long arg) +{ + bool inherit; + int cur, next; + + if (!has_vector()) + return -EINVAL; + + if (arg & ~PR_RISCV_V_VSTATE_CTRL_MASK) + return -EINVAL; + + cur = VSTATE_CTRL_GET_CUR(arg); + switch (cur) { + case PR_RISCV_V_VSTATE_CTRL_OFF: + /* Do not allow user to turn off V if current is not off */ + if (riscv_v_ctrl_get_cur(current) != PR_RISCV_V_VSTATE_CTRL_OFF) + return -EPERM; + + break; + case PR_RISCV_V_VSTATE_CTRL_ON: + break; + case PR_RISCV_V_VSTATE_CTRL_DEFAULT: + cur = riscv_v_ctrl_get_cur(current); + break; + default: + return -EINVAL; + } + + next = VSTATE_CTRL_GET_NEXT(arg); + inherit = VSTATE_CTRL_GET_INHERIT(arg); + switch (next) { + case PR_RISCV_V_VSTATE_CTRL_DEFAULT: + case PR_RISCV_V_VSTATE_CTRL_OFF: + case PR_RISCV_V_VSTATE_CTRL_ON: + riscv_v_ctrl_set(current, cur, next, inherit); + return 0; + } + + return -EINVAL; +} diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index d6475a0c989881..e9be47cc1bf86d 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -84,6 +84,8 @@ static bool kvm_riscv_vcpu_isa_enable_allowed(unsigned long ext) switch (ext) { case KVM_RISCV_ISA_EXT_H: return false; + case KVM_RISCV_ISA_EXT_V: + return riscv_v_vstate_ctrl_user_allowed(); default: break; } diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index a5e06dcbba136d..4c401aac09695c 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -284,4 +284,19 @@ struct prctl_mm_map { #define PR_SET_VMA 0x53564d41 # define PR_SET_VMA_ANON_NAME 0 +#define PR_GET_AUXV 0x41555856 + +#define PR_SET_MEMORY_MERGE 67 +#define PR_GET_MEMORY_MERGE 68 + +#define PR_RISCV_V_SET_CONTROL 69 +#define PR_RISCV_V_GET_CONTROL 70 +# define PR_RISCV_V_VSTATE_CTRL_DEFAULT 0 +# define PR_RISCV_V_VSTATE_CTRL_OFF 1 +# define PR_RISCV_V_VSTATE_CTRL_ON 2 +# define PR_RISCV_V_VSTATE_CTRL_INHERIT (1 << 4) +# define PR_RISCV_V_VSTATE_CTRL_CUR_MASK 0x3 +# define PR_RISCV_V_VSTATE_CTRL_NEXT_MASK 0xc +# define PR_RISCV_V_VSTATE_CTRL_MASK 0x1f + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index c85e1abf7b7c79..0fb941f98caef5 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -139,6 +139,12 @@ #ifndef GET_TAGGED_ADDR_CTRL # define GET_TAGGED_ADDR_CTRL() (-EINVAL) #endif +#ifndef RISCV_V_SET_CONTROL +# define RISCV_V_SET_CONTROL(a) (-EINVAL) +#endif +#ifndef RISCV_V_GET_CONTROL +# define RISCV_V_GET_CONTROL() (-EINVAL) +#endif /* * this is where the system-wide overflow UID and GID are defined, for @@ -2639,6 +2645,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, case PR_SET_VMA: error = prctl_set_vma(arg2, arg3, arg4, arg5); break; + case PR_RISCV_V_SET_CONTROL: + error = RISCV_V_SET_CONTROL(arg2); + break; + case PR_RISCV_V_GET_CONTROL: + error = RISCV_V_GET_CONTROL(); + break; default: error = -EINVAL; break; From c3d728522bc66bdf820ba6d7b9522d2b7b0f63bb Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Mon, 5 Jun 2023 11:07:19 +0000 Subject: [PATCH 117/169] riscv: Add sysctl to set the default vector rule for new processes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support Vector extension, the series exports variable-length vector registers on the signal frame. However, this potentially breaks abi if processing vector registers is required in the signal handler for old binaries. For example, there is such need if user-level context switch is triggerred via signals[1]. For this reason, it is best to leave a decision to distro maintainers, where the enablement of userspace Vector for new launching programs can be controlled. Developers may also need the switch to experiment with. The parameter is configurable through sysctl interface so a distro may turn off Vector early at init script if the break really happens in the wild. The switch will only take effects on new execve() calls once set. This will not effect existing processes that do not call execve(), nor processes which has been set with a non-default vstate_ctrl by making explicit PR_RISCV_V_SET_CONTROL prctl() calls. Link: https://lore.kernel.org/all/87cz4048rp.fsf@all.your.base.are.belong.to.us/ Signed-off-by: Andy Chiu Reviewed-by: Greentime Hu Reviewed-by: Vincent Chen Reviewed-by: Björn Töpel Link: https://lore.kernel.org/r/20230605110724.21391-23-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --------------x----------------x--------------x---------------x-------- Linux v6.1 treats ctl_table.maxlen as an integer [1], so we need to convert the data type for proc_dobool(). [1] https://github.com/torvalds/linux/blob/v6.1/kernel/sysctl.c#L713-L714 --- arch/riscv/kernel/vector.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c index a7dec923016443..c7167207c1f663 100644 --- a/arch/riscv/kernel/vector.c +++ b/arch/riscv/kernel/vector.c @@ -180,7 +180,7 @@ void riscv_v_vstate_ctrl_init(struct task_struct *tsk) next = riscv_v_ctrl_get_next(tsk); if (!next) { - if (riscv_v_implicit_uacc) + if (READ_ONCE(riscv_v_implicit_uacc)) cur = PR_RISCV_V_VSTATE_CTRL_ON; else cur = PR_RISCV_V_VSTATE_CTRL_OFF; @@ -243,3 +243,34 @@ long riscv_v_vstate_ctrl_set_current(unsigned long arg) return -EINVAL; } + +#ifdef CONFIG_SYSCTL + +static struct ctl_table riscv_v_default_vstate_table[] = { + { + .procname = "riscv_v_default_allow", + .data = &riscv_v_implicit_uacc, + .maxlen = sizeof((int)riscv_v_implicit_uacc), + .mode = 0644, + .proc_handler = proc_dobool, + }, + { } +}; + +static int __init riscv_v_sysctl_init(void) +{ + if (has_vector()) + if (!register_sysctl("abi", riscv_v_default_vstate_table)) + return -EINVAL; + return 0; +} + +#else /* ! CONFIG_SYSCTL */ +static int __init riscv_v_sysctl_init(void) { return 0; } +#endif /* ! CONFIG_SYSCTL */ + +static int riscv_v_init(void) +{ + return riscv_v_sysctl_init(); +} +core_initcall(riscv_v_init); From 9409efcd2dc9ec120d47b94b636f1e87f19fabac Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Mon, 5 Jun 2023 11:07:20 +0000 Subject: [PATCH 118/169] riscv: detect assembler support for .option arch Some extensions use .option arch directive to selectively enable certain extensions in parts of its assembly code. For example, Zbb uses it to inform assmebler to emit bit manipulation instructions. However, supporting of this directive only exist on GNU assembler and has not landed on clang at the moment, making TOOLCHAIN_HAS_ZBB depend on AS_IS_GNU. While it is still under review at https://reviews.llvm.org/D123515, the upcoming Vector patch also requires this feature in assembler. Thus, provide Kconfig AS_HAS_OPTION_ARCH to detect such feature. Then TOOLCHAIN_HAS_XXX will be turned on automatically when the feature land. Suggested-by: Nathan Chancellor Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Reviewed-by: Nathan Chancellor Reviewed-by: Heiko Stuebner Tested-by: Heiko Stuebner Link: https://lore.kernel.org/r/20230605110724.21391-24-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 7cf18669b5fed9..261a99e21658cf 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -233,6 +233,12 @@ config RISCV_DMA_NONCOHERENT config AS_HAS_INSN def_bool $(as-instr,.insn r 51$(comma) 0$(comma) 0$(comma) t0$(comma) t0$(comma) zero) +config AS_HAS_OPTION_ARCH + # https://reviews.llvm.org/D123515 + def_bool y + depends on $(as-instr, .option arch$(comma) +m) + depends on !$(as-instr, .option arch$(comma) -i) + source "arch/riscv/Kconfig.socs" source "arch/riscv/Kconfig.erratas" From 0fe85bbbd02bc243af32cc61ad77030060151ccf Mon Sep 17 00:00:00 2001 From: Guo Ren Date: Mon, 5 Jun 2023 11:07:21 +0000 Subject: [PATCH 119/169] riscv: Enable Vector code to be built This patch adds configs for building Vector code. First it detects the reqired toolchain support for building the code. Then it provides an option setting whether Vector is implicitly enabled to userspace. Signed-off-by: Guo Ren Co-developed-by: Greentime Hu Signed-off-by: Greentime Hu Co-developed-by: Andy Chiu Signed-off-by: Andy Chiu Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20230605110724.21391-25-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/Kconfig | 31 +++++++++++++++++++++++++++++++ arch/riscv/Makefile | 6 +++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 261a99e21658cf..311210fe44f095 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -436,6 +436,37 @@ config RISCV_ISA_SVPBMT If you don't know what to do here, say Y. +config TOOLCHAIN_HAS_V + bool + default y + depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64iv) + depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32iv) + depends on LLD_VERSION >= 140000 || LD_VERSION >= 23800 + depends on AS_HAS_OPTION_ARCH + +config RISCV_ISA_V + bool "VECTOR extension support" + depends on TOOLCHAIN_HAS_V + depends on FPU + select DYNAMIC_SIGFRAME + default y + help + Say N here if you want to disable all vector related procedure + in the kernel. + + If you don't know what to do here, say Y. + +config RISCV_ISA_V_DEFAULT_ENABLE + bool "Enable userspace Vector by default" + depends on RISCV_ISA_V + default y + help + Say Y here if you want to enable Vector in userspace by default. + Otherwise, userspace has to make explicit prctl() call to enable + Vector, or enable it via the sysctl interface. + + If you don't know what to do here, say Y. + config TOOLCHAIN_HAS_ZICBOM bool default y diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index b9e17ba9976f9f..73e975cddd03f4 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -60,6 +60,7 @@ riscv-march-$(CONFIG_ARCH_RV32I) := rv32ima riscv-march-$(CONFIG_ARCH_RV64I) := rv64ima riscv-march-$(CONFIG_FPU) := $(riscv-march-y)fd riscv-march-$(CONFIG_RISCV_ISA_C) := $(riscv-march-y)c +riscv-march-$(CONFIG_RISCV_ISA_V) := $(riscv-march-y)v ifdef CONFIG_TOOLCHAIN_NEEDS_OLD_ISA_SPEC KBUILD_CFLAGS += -Wa,-misa-spec=2.2 @@ -74,7 +75,10 @@ riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZICBOM) := $(riscv-march-y)_zicbom # Check if the toolchain supports Zihintpause extension riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZIHINTPAUSE) := $(riscv-march-y)_zihintpause -KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y)) +# Remove F,D,V from isa string for all. Keep extensions between "fd" and "v" by +# matching non-v and non-multi-letter extensions out with the filter ([^v_]*) +KBUILD_CFLAGS += -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/') + KBUILD_AFLAGS += -march=$(riscv-march-y) KBUILD_CFLAGS += -mno-save-restore From 4ef186f701a1738ba685da5e114643feb3818d63 Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Mon, 5 Jun 2023 11:07:22 +0000 Subject: [PATCH 120/169] riscv: Add documentation for Vector This patch add a brief documentation of the userspace interface in regard to the RISC-V Vector extension. Signed-off-by: Andy Chiu Reviewed-by: Greentime Hu Reviewed-by: Vincent Chen Co-developed-by: Bagas Sanjaya Signed-off-by: Bagas Sanjaya Link: https://lore.kernel.org/r/20230605110724.21391-26-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- Documentation/riscv/index.rst | 1 + Documentation/riscv/vector.rst | 132 +++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 Documentation/riscv/vector.rst diff --git a/Documentation/riscv/index.rst b/Documentation/riscv/index.rst index 2e5b18fbb1451f..79a9eb006d7c06 100644 --- a/Documentation/riscv/index.rst +++ b/Documentation/riscv/index.rst @@ -9,6 +9,7 @@ RISC-V architecture vm-layout patch-acceptance uabi + vector features diff --git a/Documentation/riscv/vector.rst b/Documentation/riscv/vector.rst new file mode 100644 index 00000000000000..48f189d79e4134 --- /dev/null +++ b/Documentation/riscv/vector.rst @@ -0,0 +1,132 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================================= +Vector Extension Support for RISC-V Linux +========================================= + +This document briefly outlines the interface provided to userspace by Linux in +order to support the use of the RISC-V Vector Extension. + +1. prctl() Interface +--------------------- + +Two new prctl() calls are added to allow programs to manage the enablement +status for the use of Vector in userspace. The intended usage guideline for +these interfaces is to give init systems a way to modify the availability of V +for processes running under its domain. Calling thess interfaces is not +recommended in libraries routines because libraries should not override policies +configured from the parant process. Also, users must noted that these interfaces +are not portable to non-Linux, nor non-RISC-V environments, so it is discourage +to use in a portable code. To get the availability of V in an ELF program, +please read :c:macro:`COMPAT_HWCAP_ISA_V` bit of :c:macro:`ELF_HWCAP` in the +auxiliary vector. + +* prctl(PR_RISCV_V_SET_CONTROL, unsigned long arg) + + Sets the Vector enablement status of the calling thread, where the control + argument consists of two 2-bit enablement statuses and a bit for inheritance + mode. Other threads of the calling process are unaffected. + + Enablement status is a tri-state value each occupying 2-bit of space in + the control argument: + + * :c:macro:`PR_RISCV_V_VSTATE_CTRL_DEFAULT`: Use the system-wide default + enablement status on execve(). The system-wide default setting can be + controlled via sysctl interface (see sysctl section below). + + * :c:macro:`PR_RISCV_V_VSTATE_CTRL_ON`: Allow Vector to be run for the + thread. + + * :c:macro:`PR_RISCV_V_VSTATE_CTRL_OFF`: Disallow Vector. Executing Vector + instructions under such condition will trap and casuse the termination of the thread. + + arg: The control argument is a 5-bit value consisting of 3 parts, and + accessed by 3 masks respectively. + + The 3 masks, PR_RISCV_V_VSTATE_CTRL_CUR_MASK, + PR_RISCV_V_VSTATE_CTRL_NEXT_MASK, and PR_RISCV_V_VSTATE_CTRL_INHERIT + represents bit[1:0], bit[3:2], and bit[4]. bit[1:0] accounts for the + enablement status of current thread, and the setting at bit[3:2] takes place + at next execve(). bit[4] defines the inheritance mode of the setting in + bit[3:2]. + + * :c:macro:`PR_RISCV_V_VSTATE_CTRL_CUR_MASK`: bit[1:0]: Account for the + Vector enablement status for the calling thread. The calling thread is + not able to turn off Vector once it has been enabled. The prctl() call + fails with EPERM if the value in this mask is PR_RISCV_V_VSTATE_CTRL_OFF + but the current enablement status is not off. Setting + PR_RISCV_V_VSTATE_CTRL_DEFAULT here takes no effect but to set back + the original enablement status. + + * :c:macro:`PR_RISCV_V_VSTATE_CTRL_NEXT_MASK`: bit[3:2]: Account for the + Vector enablement setting for the calling thread at the next execve() + system call. If PR_RISCV_V_VSTATE_CTRL_DEFAULT is used in this mask, + then the enablement status will be decided by the system-wide + enablement status when execve() happen. + + * :c:macro:`PR_RISCV_V_VSTATE_CTRL_INHERIT`: bit[4]: the inheritance + mode for the setting at PR_RISCV_V_VSTATE_CTRL_NEXT_MASK. If the bit + is set then the following execve() will not clear the setting in both + PR_RISCV_V_VSTATE_CTRL_NEXT_MASK and PR_RISCV_V_VSTATE_CTRL_INHERIT. + This setting persists across changes in the system-wide default value. + + Return value: + * 0 on success; + * EINVAL: Vector not supported, invalid enablement status for current or + next mask; + * EPERM: Turning off Vector in PR_RISCV_V_VSTATE_CTRL_CUR_MASK if Vector + was enabled for the calling thread. + + On success: + * A valid setting for PR_RISCV_V_VSTATE_CTRL_CUR_MASK takes place + immediately. The enablement status specified in + PR_RISCV_V_VSTATE_CTRL_NEXT_MASK happens at the next execve() call, or + all following execve() calls if PR_RISCV_V_VSTATE_CTRL_INHERIT bit is + set. + * Every successful call overwrites a previous setting for the calling + thread. + +* prctl(PR_RISCV_V_GET_CONTROL) + + Gets the same Vector enablement status for the calling thread. Setting for + next execve() call and the inheritance bit are all OR-ed together. + + Note that ELF programs are able to get the availability of V for itself by + reading :c:macro:`COMPAT_HWCAP_ISA_V` bit of :c:macro:`ELF_HWCAP` in the + auxiliary vector. + + Return value: + * a nonnegative value on success; + * EINVAL: Vector not supported. + +2. System runtime configuration (sysctl) +----------------------------------------- + +To mitigate the ABI impact of expansion of the signal stack, a +policy mechanism is provided to the administrators, distro maintainers, and +developers to control the default Vector enablement status for userspace +processes in form of sysctl knob: + +* /proc/sys/abi/riscv_v_default_allow + + Writing the text representation of 0 or 1 to this file sets the default + system enablement status for new starting userspace programs. Valid values + are: + + * 0: Do not allow Vector code to be executed as the default for new processes. + * 1: Allow Vector code to be executed as the default for new processes. + + Reading this file returns the current system default enablement status. + + At every execve() call, a new enablement status of the new process is set to + the system default, unless: + + * PR_RISCV_V_VSTATE_CTRL_INHERIT is set for the calling process, and the + setting in PR_RISCV_V_VSTATE_CTRL_NEXT_MASK is not + PR_RISCV_V_VSTATE_CTRL_DEFAULT. Or, + + * The setting in PR_RISCV_V_VSTATE_CTRL_NEXT_MASK is not + PR_RISCV_V_VSTATE_CTRL_DEFAULT. + + Modifying the system default enablement status does not affect the enablement + status of any existing process of thread that do not make an execve() call. From df3898ee80a19167a21e75df64693a716976ab23 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Wed, 16 Aug 2023 15:54:48 +0000 Subject: [PATCH 121/169] RISC-V: Remove ptrace support for vectors We've found two bugs here: NT_RISCV_VECTOR steps on NT_RISCV_CSR (which is only for embedded), and we don't have vlenb in the core dumps. Given that we've have a pair of bugs croup up as part of the GDB review we've probably got other issues, so let's just cut this for 6.5 and get it right. Fixes: 0c59922c769a ("riscv: Add ptrace vector support") Signed-off-by: Palmer Dabbelt Reviewed-by: Maciej W. Rozycki Signed-off-by: Andy Chiu --- arch/riscv/kernel/ptrace.c | 69 -------------------------------------- include/uapi/linux/elf.h | 1 - 2 files changed, 70 deletions(-) diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index 7308e22f552424..a9e73c849c6b1e 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -30,9 +30,6 @@ enum riscv_regset { #ifdef CONFIG_FPU REGSET_F, #endif -#ifdef CONFIG_RISCV_ISA_V - REGSET_V, -#endif }; static int riscv_gpr_get(struct task_struct *target, @@ -89,61 +86,6 @@ static int riscv_fpr_set(struct task_struct *target, } #endif -#ifdef CONFIG_RISCV_ISA_V -static int riscv_vr_get(struct task_struct *target, - const struct user_regset *regset, - struct membuf to) -{ - struct __riscv_v_ext_state *vstate = &target->thread.vstate; - - if (!riscv_v_vstate_query(task_pt_regs(target))) - return -EINVAL; - - /* - * Ensure the vector registers have been saved to the memory before - * copying them to membuf. - */ - if (target == current) - riscv_v_vstate_save(current, task_pt_regs(current)); - - /* Copy vector header from vstate. */ - membuf_write(&to, vstate, offsetof(struct __riscv_v_ext_state, datap)); - membuf_zero(&to, sizeof(vstate->datap)); - - /* Copy all the vector registers from vstate. */ - return membuf_write(&to, vstate->datap, riscv_v_vsize); -} - -static int riscv_vr_set(struct task_struct *target, - const struct user_regset *regset, - unsigned int pos, unsigned int count, - const void *kbuf, const void __user *ubuf) -{ - int ret, size; - struct __riscv_v_ext_state *vstate = &target->thread.vstate; - - if (!riscv_v_vstate_query(task_pt_regs(target))) - return -EINVAL; - - /* Copy rest of the vstate except datap */ - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate, 0, - offsetof(struct __riscv_v_ext_state, datap)); - if (unlikely(ret)) - return ret; - - /* Skip copy datap. */ - size = sizeof(vstate->datap); - count -= size; - ubuf += size; - - /* Copy all the vector registers. */ - pos = 0; - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate->datap, - 0, riscv_v_vsize); - return ret; -} -#endif - static const struct user_regset riscv_user_regset[] = { [REGSET_X] = { .core_note_type = NT_PRSTATUS, @@ -163,17 +105,6 @@ static const struct user_regset riscv_user_regset[] = { .set = riscv_fpr_set, }, #endif -#ifdef CONFIG_RISCV_ISA_V - [REGSET_V] = { - .core_note_type = NT_RISCV_VECTOR, - .align = 16, - .n = ((32 * RISCV_MAX_VLENB) + - sizeof(struct __riscv_v_ext_state)) / sizeof(__u32), - .size = sizeof(__u32), - .regset_get = riscv_vr_get, - .set = riscv_vr_set, - }, -#endif }; static const struct user_regset_view riscv_user_native_view = { diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index 5a5056c6a2a124..c7b056af9ef0ad 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -439,7 +439,6 @@ typedef struct elf64_shdr { #define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers */ #define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode */ #define NT_MIPS_MSA 0x802 /* MIPS SIMD registers */ -#define NT_RISCV_VECTOR 0x900 /* RISC-V vector registers */ #define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers */ #define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and status registers */ #define NT_LOONGARCH_LSX 0xa02 /* LoongArch Loongson SIMD Extension registers */ From 6fcfe8e266c5956a3677f872f27d36274e5158ec Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Wed, 16 Aug 2023 15:54:49 +0000 Subject: [PATCH 122/169] RISC-V: vector: export VLENB csr in __sc_riscv_v_state VLENB is critical for callers of ptrace to reconstruct Vector register files from the register dump of NT_RISCV_VECTOR. Also, future systems may will have a writable VLENB, so add it now to potentially save future compatibility issue. Fixes: 0c59922c769a ("riscv: Add ptrace vector support") Signed-off-by: Andy Chiu --- arch/riscv/include/asm/vector.h | 3 ++- arch/riscv/include/uapi/asm/ptrace.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h index e7df4e0ac79387..1c4274250212f0 100644 --- a/arch/riscv/include/asm/vector.h +++ b/arch/riscv/include/asm/vector.h @@ -65,8 +65,9 @@ static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest) "csrr %1, " __stringify(CSR_VTYPE) "\n\t" "csrr %2, " __stringify(CSR_VL) "\n\t" "csrr %3, " __stringify(CSR_VCSR) "\n\t" + "csrr %4, " __stringify(CSR_VLENB) "\n\t" : "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl), - "=r" (dest->vcsr) : :); + "=r" (dest->vcsr), "=r" (dest->vlenb) : :); } static __always_inline void __vstate_csr_restore(struct __riscv_v_ext_state *src) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 9d5a0bdadd7762..595959acdd5482 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -103,6 +103,7 @@ struct __riscv_v_ext_state { unsigned long vl; unsigned long vtype; unsigned long vcsr; + unsigned long vlenb; void *datap; /* * In signal handler, datap will be set a correct user stack offset From 142a943a0bd579a77077b28a497815936ed8d1cf Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Tue, 27 Jun 2023 01:55:54 +0000 Subject: [PATCH 123/169] riscv: vector: clear V-reg in the first-use trap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there is no context switch happens after we enable V for a process, then we return to user space with whatever left on the CPU's V registers accessible to the process. The leaked data could belong to another process's V-context saved from last context switch, impacting process's confidentiality on the system. To prevent this from happening, we clear V registers by restoring zero'd V context after turining on V. Fixes: cd054837243b ("riscv: Allocate user's vector context in the first-use trap") Signed-off-by: Andy Chiu Reviewed-by: Björn Töpel Link: https://lore.kernel.org/r/20230627015556.12329-2-andy.chiu@sifive.com Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/vector.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c index c7167207c1f663..90067bb6e780ae 100644 --- a/arch/riscv/kernel/vector.c +++ b/arch/riscv/kernel/vector.c @@ -167,6 +167,7 @@ bool riscv_v_first_use_handler(struct pt_regs *regs) return true; } riscv_v_vstate_on(regs); + riscv_v_vstate_restore(current, regs); return true; } From bb00166fdbbc43fea23adf93ef4e7030eea47eec Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Mon, 31 Jul 2023 17:02:49 +0800 Subject: [PATCH 124/169] andes: perf: add andes event to JSON file Add andes event to JSON file for a25 ax25 a45 and ax65 Signed-off-by: Locus Wei-Han Chen --- .../arch/riscv/andes/A25/firmware.json | 74 +++++ .../arch/riscv/andes/A25/riscvstd.json | 257 +++++++++++++++++ .../arch/riscv/andes/A45/firmware.json | 74 +++++ .../arch/riscv/andes/A45/riscvstd.json | 267 ++++++++++++++++++ .../arch/riscv/andes/AX25/firmware.json | 74 +++++ .../arch/riscv/andes/AX25/riscvstd.json | 257 +++++++++++++++++ .../arch/riscv/andes/AX45/riscvstd.json | 25 +- .../arch/riscv/andes/AX65/firmware.json | 74 +++++ .../arch/riscv/andes/AX65/riscvstd.json | 262 +++++++++++++++++ tools/perf/pmu-events/arch/riscv/mapfile.csv | 4 + 10 files changed, 1363 insertions(+), 5 deletions(-) create mode 100644 tools/perf/pmu-events/arch/riscv/andes/A25/firmware.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/A45/firmware.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/A45/riscvstd.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/AX25/firmware.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/AX25/riscvstd.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/AX65/firmware.json create mode 100644 tools/perf/pmu-events/arch/riscv/andes/AX65/riscvstd.json diff --git a/tools/perf/pmu-events/arch/riscv/andes/A25/firmware.json b/tools/perf/pmu-events/arch/riscv/andes/A25/firmware.json new file mode 100644 index 00000000000000..61e42c1813fa33 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/A25/firmware.json @@ -0,0 +1,74 @@ +[ + { + "ArchStdEvent": "FW_MISALIGNED_LOAD" + }, + { + "ArchStdEvent": "FW_MISALIGNED_STORE" + }, + { + "ArchStdEvent": "FW_ACCESS_LOAD" + }, + { + "ArchStdEvent": "FW_ACCESS_STORE" + }, + { + "ArchStdEvent": "FW_ILLEGAL_INSN" + }, + { + "ArchStdEvent": "FW_SET_TIMER" + }, + { + "ArchStdEvent": "FW_IPI_SENT" + }, + { + "ArchStdEvent": "FW_IPI_RECEIVED" + }, + { + "ArchStdEvent": "FW_FENCE_I_SENT" + }, + { + "ArchStdEvent": "FW_FENCE_I_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_SENT" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESSES" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESS_MISSES" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json new file mode 100644 index 00000000000000..71cf1fc3f7b21c --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json @@ -0,0 +1,257 @@ +[ + { + "EventCode": "0x10", + "EventName": "cycle_count", + "BriefDescription": "Cycle counts" + }, + { + "EventCode": "0x20", + "EventName": "inst_count", + "BriefDescription": "Retired instruction counts" + }, + { + "EventCode": "0x30", + "EventName": "int_load_inst", + "BriefDescription": "Integer load instructions" + }, + { + "EventCode": "0x40", + "EventName": "int_store_inst", + "BriefDescription": "Integer store instructions" + }, + { + "EventCode": "0x50", + "EventName": "atomic_mem_op", + "BriefDescription": "Atomic memory operation" + }, + { + "EventCode": "0x60", + "EventName": "sys_inst", + "BriefDescription": "System instructions" + }, + { + "EventCode": "0x70", + "EventName": "int_compute_inst", + "BriefDescription": "Integer computational instruction" + }, + { + "EventCode": "0x80", + "EventName": "condition_br", + "BriefDescription": "Conditional branch" + }, + { + "EventCode": "0x90", + "EventName": "taken_condition_br", + "BriefDescription": "Taken conditional branch" + }, + { + "EventCode": "0xA0", + "EventName": "jal_inst", + "BriefDescription": "JAL instruction" + }, + { + "EventCode": "0xB0", + "EventName": "jalr_inst", + "BriefDescription": "JALR instruction" + }, + { + "EventCode": "0xC0", + "EventName": "ret_inst", + "BriefDescription": "Return instruction" + }, + { + "EventCode": "0xD0", + "EventName": "control_trans_inst", + "BriefDescription": "Control transfer instruction" + }, + { + "EventCode": "0xE0", + "EventName": "ex9_inst", + "BriefDescription": "EX9 instruction" + }, + { + "EventCode": "0xF0", + "EventName": "int_mul_inst", + "BriefDescription": "Integer multiplication instruction" + }, + { + "EventCode": "0x100", + "EventName": "int_div_rem_inst", + "BriefDescription": "Integer division/remainder instruction" + }, + { + "EventCode": "0x110", + "EventName": "float_load_inst", + "BriefDescription": "Floating-point load instruction" + }, + { + "EventCode": "0x120", + "EventName": "float_store_inst", + "BriefDescription": "Floating-point store instruction" + }, + { + "EventCode": "0x130", + "EventName": "float_add_sub_inst", + "BriefDescription": "Floating-point addition/subtraction" + }, + { + "EventCode": "0x140", + "EventName": "float_mul_inst", + "BriefDescription": "Floating-point multiplication" + }, + { + "EventCode": "0x150", + "EventName": "float_fused_muladd_inst", + "BriefDescription": "Floating-point fused multiply-add" + }, + { + "EventCode": "0x160", + "EventName": "float_div_sqrt_inst", + "BriefDescription": "Floating-point division or square-root" + }, + { + "EventCode": "0x170", + "EventName": "other_float_inst", + "BriefDescription": "Other floating-point instruction" + }, + { + "EventCode": "0x180", + "EventName": "integer_mul_and_sub_inst_count", + "BriefDescription": "Integer multiplication and add/sub instruction count" + }, + { + "EventCode": "0x01", + "EventName": "ilm_access", + "BriefDescription": "ILM access" + }, + { + "EventCode": "0x11", + "EventName": "dlm_access", + "BriefDescription": "DLM access" + }, + { + "EventCode": "0x21", + "EventName": "icache_access", + "BriefDescription": "ICACHE access" + }, + { + "EventCode": "0x31", + "EventName": "icache_miss", + "BriefDescription": "ICACHE miss" + }, + { + "EventCode": "0x41", + "EventName": "dcache_access", + "BriefDescription": "DCACHE access" + }, + { + "EventCode": "0x51", + "EventName": "dcache_miss", + "BriefDescription": "DCACHE miss" + }, + { + "EventCode": "0x61", + "EventName": "dcache_load_access", + "BriefDescription": "DCACHE load access" + }, + { + "EventCode": "0x71", + "EventName": "dcache_load_miss", + "BriefDescription": "DCACHE load miss" + }, + { + "EventCode": "0x81", + "EventName": "dcache_store_access", + "BriefDescription": "DCACHE store access" + }, + { + "EventCode": "0x91", + "EventName": "dcache_store_miss", + "BriefDescription": "DCACHE store miss" + }, + { + "EventCode": "0xA1", + "EventName": "dcache_wb", + "BriefDescription": "DCACHE writeback" + }, + { + "EventCode": "0xB1", + "EventName": "cycle_wait_icache_fill", + "BriefDescription": "Cycles waiting for ICACHE fill data" + }, + { + "EventCode": "0xC1", + "EventName": "cycle_wait_dcache_fill", + "BriefDescription": "Cycles waiting for DCACHE fill data" + }, + { + "EventCode": "0xD1", + "EventName": "uncached_ifetch_from_bus", + "BriefDescription": "Uncached ifetch data access from bus" + }, + { + "EventCode": "0xE1", + "EventName": "uncached_load_from_bus", + "BriefDescription": "Uncached load data access from bus" + }, + { + "EventCode": "0xF1", + "EventName": "cycle_wait_uncached_ifetch", + "BriefDescription": "Cycles waiting for uncached ifetch data from bus" + }, + { + "EventCode": "0x101", + "EventName": "cycle_wait_uncached_load", + "BriefDescription": "Cycles waiting for uncached load data from bus" + }, + { + "EventCode": "0x111", + "EventName": "main_itlb_access", + "BriefDescription": "Main ITLB access" + }, + { + "EventCode": "0x121", + "EventName": "main_itlb_miss", + "BriefDescription": "Main ITLB miss" + }, + { + "EventCode": "0x131", + "EventName": "main_dtlb_access", + "BriefDescription": "Main DTLB access" + }, + { + "EventCode": "0x141", + "EventName": "main_dtlb_miss", + "BriefDescription": "Main DTLB miss" + }, + { + "EventCode": "0x151", + "EventName": "cycle_wait_itlb_fill", + "BriefDescription": "Cycles waiting for Main ITLB fill data" + }, + { + "EventCode": "0x161", + "EventName": "pipe_stall_cycle_dtlb_miss", + "BriefDescription": "Pipeline stall cycles caused by Main DTLB miss" + }, + { + "EventCode": "0x02", + "EventName": "mispredict_condition_br", + "BriefDescription": "Misprediction of conditional branches" + }, + { + "EventCode": "0x12", + "EventName": "mispredict_take_condition_br", + "BriefDescription": "Misprediction of taken conditional branches" + }, + { + "EventCode": "0x22", + "EventName": "mispredict_target_ret_inst", + "BriefDescription": "Misprediction of targets of Return instructions" + }, + { + "EventCode": "0x32", + "EventName": "replay_load_store", + "BriefDescription": "Replay for load-after-store or store-after-store cases" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/A45/firmware.json b/tools/perf/pmu-events/arch/riscv/andes/A45/firmware.json new file mode 100644 index 00000000000000..61e42c1813fa33 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/A45/firmware.json @@ -0,0 +1,74 @@ +[ + { + "ArchStdEvent": "FW_MISALIGNED_LOAD" + }, + { + "ArchStdEvent": "FW_MISALIGNED_STORE" + }, + { + "ArchStdEvent": "FW_ACCESS_LOAD" + }, + { + "ArchStdEvent": "FW_ACCESS_STORE" + }, + { + "ArchStdEvent": "FW_ILLEGAL_INSN" + }, + { + "ArchStdEvent": "FW_SET_TIMER" + }, + { + "ArchStdEvent": "FW_IPI_SENT" + }, + { + "ArchStdEvent": "FW_IPI_RECEIVED" + }, + { + "ArchStdEvent": "FW_FENCE_I_SENT" + }, + { + "ArchStdEvent": "FW_FENCE_I_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_SENT" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESSES" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESS_MISSES" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/A45/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/A45/riscvstd.json new file mode 100644 index 00000000000000..def7e88cfc77ac --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/A45/riscvstd.json @@ -0,0 +1,267 @@ +[ + { + "EventCode": "0x10", + "EventName": "cycle_count", + "BriefDescription": "Cycle counts" + }, + { + "EventCode": "0x20", + "EventName": "inst_count", + "BriefDescription": "Retired instruction counts" + }, + { + "EventCode": "0x30", + "EventName": "int_load_inst", + "BriefDescription": "Integer load instructions" + }, + { + "EventCode": "0x40", + "EventName": "int_store_inst", + "BriefDescription": "Integer store instructions" + }, + { + "EventCode": "0x50", + "EventName": "atomic_mem_op", + "BriefDescription": "Atomic memory operation" + }, + { + "EventCode": "0x60", + "EventName": "sys_inst", + "BriefDescription": "System instructions" + }, + { + "EventCode": "0x70", + "EventName": "int_compute_inst", + "BriefDescription": "Integer computational instruction" + }, + { + "EventCode": "0x80", + "EventName": "condition_br", + "BriefDescription": "Conditional branch" + }, + { + "EventCode": "0x90", + "EventName": "taken_condition_br", + "BriefDescription": "Taken conditional branch" + }, + { + "EventCode": "0xA0", + "EventName": "jal_inst", + "BriefDescription": "JAL instruction" + }, + { + "EventCode": "0xB0", + "EventName": "jalr_inst", + "BriefDescription": "JALR instruction" + }, + { + "EventCode": "0xC0", + "EventName": "ret_inst", + "BriefDescription": "Return instruction" + }, + { + "EventCode": "0xD0", + "EventName": "control_trans_inst", + "BriefDescription": "Control transfer instruction" + }, + { + "EventCode": "0xE0", + "EventName": "ex9_inst", + "BriefDescription": "EX9 instruction" + }, + { + "EventCode": "0xF0", + "EventName": "int_mul_inst", + "BriefDescription": "Integer multiplication instruction" + }, + { + "EventCode": "0x100", + "EventName": "int_div_rem_inst", + "BriefDescription": "Integer division/remainder instruction" + }, + { + "EventCode": "0x110", + "EventName": "float_load_inst", + "BriefDescription": "Floating-point load instruction" + }, + { + "EventCode": "0x120", + "EventName": "float_store_inst", + "BriefDescription": "Floating-point store instruction" + }, + { + "EventCode": "0x130", + "EventName": "float_add_sub_inst", + "BriefDescription": "Floating-point addition/subtraction" + }, + { + "EventCode": "0x140", + "EventName": "float_mul_inst", + "BriefDescription": "Floating-point multiplication" + }, + { + "EventCode": "0x150", + "EventName": "float_fused_muladd_inst", + "BriefDescription": "Floating-point fused multiply-add" + }, + { + "EventCode": "0x160", + "EventName": "float_div_sqrt_inst", + "BriefDescription": "Floating-point division or square-root" + }, + { + "EventCode": "0x170", + "EventName": "other_float_inst", + "BriefDescription": "Other floating-point instruction" + }, + { + "EventCode": "0x180", + "EventName": "integer_mul_and_sub_inst_count", + "BriefDescription": "Integer multiplication and add/sub instruction count" + }, + { + "EventCode": "0x190", + "EventName": "op_count", + "BriefDescription": "Retired operation count" + }, + { + "EventCode": "0x01", + "EventName": "ilm_access", + "BriefDescription": "ILM access" + }, + { + "EventCode": "0x11", + "EventName": "dlm_access", + "BriefDescription": "DLM access" + }, + { + "EventCode": "0x21", + "EventName": "icache_access", + "BriefDescription": "ICACHE access" + }, + { + "EventCode": "0x31", + "EventName": "icache_miss", + "BriefDescription": "ICACHE miss" + }, + { + "EventCode": "0x41", + "EventName": "dcache_access", + "BriefDescription": "DCACHE access" + }, + { + "EventCode": "0x51", + "EventName": "dcache_miss", + "BriefDescription": "DCACHE miss" + }, + { + "EventCode": "0x61", + "EventName": "dcache_load_access", + "BriefDescription": "DCACHE load access" + }, + { + "EventCode": "0x71", + "EventName": "dcache_load_miss", + "BriefDescription": "DCACHE load miss" + }, + { + "EventCode": "0x81", + "EventName": "dcache_store_access", + "BriefDescription": "DCACHE store access" + }, + { + "EventCode": "0x91", + "EventName": "dcache_store_miss", + "BriefDescription": "DCACHE store miss" + }, + { + "EventCode": "0xA1", + "EventName": "dcache_wb", + "BriefDescription": "DCACHE writeback" + }, + { + "EventCode": "0xB1", + "EventName": "cycle_wait_icache_fill", + "BriefDescription": "Cycles waiting for ICACHE fill data" + }, + { + "EventCode": "0xC1", + "EventName": "cycle_wait_dcache_fill", + "BriefDescription": "Cycles waiting for DCACHE fill data" + }, + { + "EventCode": "0xD1", + "EventName": "uncached_ifetch_from_bus", + "BriefDescription": "Uncached ifetch data access from bus" + }, + { + "EventCode": "0xE1", + "EventName": "uncached_load_from_bus", + "BriefDescription": "Uncached load data access from bus" + }, + { + "EventCode": "0xF1", + "EventName": "cycle_wait_uncached_ifetch", + "BriefDescription": "Cycles waiting for uncached ifetch data from bus" + }, + { + "EventCode": "0x101", + "EventName": "cycle_wait_uncached_load", + "BriefDescription": "Cycles waiting for uncached load data from bus" + }, + { + "EventCode": "0x111", + "EventName": "main_itlb_access", + "BriefDescription": "Main ITLB access" + }, + { + "EventCode": "0x121", + "EventName": "main_itlb_miss", + "BriefDescription": "Main ITLB miss" + }, + { + "EventCode": "0x131", + "EventName": "main_dtlb_access", + "BriefDescription": "Main DTLB access" + }, + { + "EventCode": "0x141", + "EventName": "main_dtlb_miss", + "BriefDescription": "Main DTLB miss" + }, + { + "EventCode": "0x151", + "EventName": "cycle_wait_itlb_fill", + "BriefDescription": "Cycles waiting for Main ITLB fill data" + }, + { + "EventCode": "0x161", + "EventName": "pipe_stall_cycle_dtlb_miss", + "BriefDescription": "Pipeline stall cycles caused by Main DTLB miss" + }, + { + "EventCode": "0x171", + "EventName": "prefetch_bus_access", + "BriefDescription": "Hardware prefetch bus access" + }, + { + "EventCode": "0x181", + "EventName": "cycle_wait_for_operand", + "BriefDescription": "Cycles waiting for source operand ready in the integer register file scoreboard" + }, + { + "EventCode": "0x02", + "EventName": "mispredict_condition_br", + "BriefDescription": "Misprediction of conditional branches" + }, + { + "EventCode": "0x12", + "EventName": "mispredict_take_condition_br", + "BriefDescription": "Misprediction of taken conditional branches" + }, + { + "EventCode": "0x22", + "EventName": "mispredict_target_ret_inst", + "BriefDescription": "Misprediction of targets of Return instructions" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX25/firmware.json b/tools/perf/pmu-events/arch/riscv/andes/AX25/firmware.json new file mode 100644 index 00000000000000..61e42c1813fa33 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/AX25/firmware.json @@ -0,0 +1,74 @@ +[ + { + "ArchStdEvent": "FW_MISALIGNED_LOAD" + }, + { + "ArchStdEvent": "FW_MISALIGNED_STORE" + }, + { + "ArchStdEvent": "FW_ACCESS_LOAD" + }, + { + "ArchStdEvent": "FW_ACCESS_STORE" + }, + { + "ArchStdEvent": "FW_ILLEGAL_INSN" + }, + { + "ArchStdEvent": "FW_SET_TIMER" + }, + { + "ArchStdEvent": "FW_IPI_SENT" + }, + { + "ArchStdEvent": "FW_IPI_RECEIVED" + }, + { + "ArchStdEvent": "FW_FENCE_I_SENT" + }, + { + "ArchStdEvent": "FW_FENCE_I_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_SENT" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESSES" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESS_MISSES" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX25/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/AX25/riscvstd.json new file mode 100644 index 00000000000000..71cf1fc3f7b21c --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/AX25/riscvstd.json @@ -0,0 +1,257 @@ +[ + { + "EventCode": "0x10", + "EventName": "cycle_count", + "BriefDescription": "Cycle counts" + }, + { + "EventCode": "0x20", + "EventName": "inst_count", + "BriefDescription": "Retired instruction counts" + }, + { + "EventCode": "0x30", + "EventName": "int_load_inst", + "BriefDescription": "Integer load instructions" + }, + { + "EventCode": "0x40", + "EventName": "int_store_inst", + "BriefDescription": "Integer store instructions" + }, + { + "EventCode": "0x50", + "EventName": "atomic_mem_op", + "BriefDescription": "Atomic memory operation" + }, + { + "EventCode": "0x60", + "EventName": "sys_inst", + "BriefDescription": "System instructions" + }, + { + "EventCode": "0x70", + "EventName": "int_compute_inst", + "BriefDescription": "Integer computational instruction" + }, + { + "EventCode": "0x80", + "EventName": "condition_br", + "BriefDescription": "Conditional branch" + }, + { + "EventCode": "0x90", + "EventName": "taken_condition_br", + "BriefDescription": "Taken conditional branch" + }, + { + "EventCode": "0xA0", + "EventName": "jal_inst", + "BriefDescription": "JAL instruction" + }, + { + "EventCode": "0xB0", + "EventName": "jalr_inst", + "BriefDescription": "JALR instruction" + }, + { + "EventCode": "0xC0", + "EventName": "ret_inst", + "BriefDescription": "Return instruction" + }, + { + "EventCode": "0xD0", + "EventName": "control_trans_inst", + "BriefDescription": "Control transfer instruction" + }, + { + "EventCode": "0xE0", + "EventName": "ex9_inst", + "BriefDescription": "EX9 instruction" + }, + { + "EventCode": "0xF0", + "EventName": "int_mul_inst", + "BriefDescription": "Integer multiplication instruction" + }, + { + "EventCode": "0x100", + "EventName": "int_div_rem_inst", + "BriefDescription": "Integer division/remainder instruction" + }, + { + "EventCode": "0x110", + "EventName": "float_load_inst", + "BriefDescription": "Floating-point load instruction" + }, + { + "EventCode": "0x120", + "EventName": "float_store_inst", + "BriefDescription": "Floating-point store instruction" + }, + { + "EventCode": "0x130", + "EventName": "float_add_sub_inst", + "BriefDescription": "Floating-point addition/subtraction" + }, + { + "EventCode": "0x140", + "EventName": "float_mul_inst", + "BriefDescription": "Floating-point multiplication" + }, + { + "EventCode": "0x150", + "EventName": "float_fused_muladd_inst", + "BriefDescription": "Floating-point fused multiply-add" + }, + { + "EventCode": "0x160", + "EventName": "float_div_sqrt_inst", + "BriefDescription": "Floating-point division or square-root" + }, + { + "EventCode": "0x170", + "EventName": "other_float_inst", + "BriefDescription": "Other floating-point instruction" + }, + { + "EventCode": "0x180", + "EventName": "integer_mul_and_sub_inst_count", + "BriefDescription": "Integer multiplication and add/sub instruction count" + }, + { + "EventCode": "0x01", + "EventName": "ilm_access", + "BriefDescription": "ILM access" + }, + { + "EventCode": "0x11", + "EventName": "dlm_access", + "BriefDescription": "DLM access" + }, + { + "EventCode": "0x21", + "EventName": "icache_access", + "BriefDescription": "ICACHE access" + }, + { + "EventCode": "0x31", + "EventName": "icache_miss", + "BriefDescription": "ICACHE miss" + }, + { + "EventCode": "0x41", + "EventName": "dcache_access", + "BriefDescription": "DCACHE access" + }, + { + "EventCode": "0x51", + "EventName": "dcache_miss", + "BriefDescription": "DCACHE miss" + }, + { + "EventCode": "0x61", + "EventName": "dcache_load_access", + "BriefDescription": "DCACHE load access" + }, + { + "EventCode": "0x71", + "EventName": "dcache_load_miss", + "BriefDescription": "DCACHE load miss" + }, + { + "EventCode": "0x81", + "EventName": "dcache_store_access", + "BriefDescription": "DCACHE store access" + }, + { + "EventCode": "0x91", + "EventName": "dcache_store_miss", + "BriefDescription": "DCACHE store miss" + }, + { + "EventCode": "0xA1", + "EventName": "dcache_wb", + "BriefDescription": "DCACHE writeback" + }, + { + "EventCode": "0xB1", + "EventName": "cycle_wait_icache_fill", + "BriefDescription": "Cycles waiting for ICACHE fill data" + }, + { + "EventCode": "0xC1", + "EventName": "cycle_wait_dcache_fill", + "BriefDescription": "Cycles waiting for DCACHE fill data" + }, + { + "EventCode": "0xD1", + "EventName": "uncached_ifetch_from_bus", + "BriefDescription": "Uncached ifetch data access from bus" + }, + { + "EventCode": "0xE1", + "EventName": "uncached_load_from_bus", + "BriefDescription": "Uncached load data access from bus" + }, + { + "EventCode": "0xF1", + "EventName": "cycle_wait_uncached_ifetch", + "BriefDescription": "Cycles waiting for uncached ifetch data from bus" + }, + { + "EventCode": "0x101", + "EventName": "cycle_wait_uncached_load", + "BriefDescription": "Cycles waiting for uncached load data from bus" + }, + { + "EventCode": "0x111", + "EventName": "main_itlb_access", + "BriefDescription": "Main ITLB access" + }, + { + "EventCode": "0x121", + "EventName": "main_itlb_miss", + "BriefDescription": "Main ITLB miss" + }, + { + "EventCode": "0x131", + "EventName": "main_dtlb_access", + "BriefDescription": "Main DTLB access" + }, + { + "EventCode": "0x141", + "EventName": "main_dtlb_miss", + "BriefDescription": "Main DTLB miss" + }, + { + "EventCode": "0x151", + "EventName": "cycle_wait_itlb_fill", + "BriefDescription": "Cycles waiting for Main ITLB fill data" + }, + { + "EventCode": "0x161", + "EventName": "pipe_stall_cycle_dtlb_miss", + "BriefDescription": "Pipeline stall cycles caused by Main DTLB miss" + }, + { + "EventCode": "0x02", + "EventName": "mispredict_condition_br", + "BriefDescription": "Misprediction of conditional branches" + }, + { + "EventCode": "0x12", + "EventName": "mispredict_take_condition_br", + "BriefDescription": "Misprediction of taken conditional branches" + }, + { + "EventCode": "0x22", + "EventName": "mispredict_target_ret_inst", + "BriefDescription": "Misprediction of targets of Return instructions" + }, + { + "EventCode": "0x32", + "EventName": "replay_load_store", + "BriefDescription": "Replay for load-after-store or store-after-store cases" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX45/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/AX45/riscvstd.json index 55cd56d9e527e8..def7e88cfc77ac 100644 --- a/tools/perf/pmu-events/arch/riscv/andes/AX45/riscvstd.json +++ b/tools/perf/pmu-events/arch/riscv/andes/AX45/riscvstd.json @@ -115,6 +115,16 @@ "BriefDescription": "Other floating-point instruction" }, { + "EventCode": "0x180", + "EventName": "integer_mul_and_sub_inst_count", + "BriefDescription": "Integer multiplication and add/sub instruction count" + }, + { + "EventCode": "0x190", + "EventName": "op_count", + "BriefDescription": "Retired operation count" + }, + { "EventCode": "0x01", "EventName": "ilm_access", "BriefDescription": "ILM access" @@ -230,6 +240,16 @@ "BriefDescription": "Pipeline stall cycles caused by Main DTLB miss" }, { + "EventCode": "0x171", + "EventName": "prefetch_bus_access", + "BriefDescription": "Hardware prefetch bus access" + }, + { + "EventCode": "0x181", + "EventName": "cycle_wait_for_operand", + "BriefDescription": "Cycles waiting for source operand ready in the integer register file scoreboard" + }, + { "EventCode": "0x02", "EventName": "mispredict_condition_br", "BriefDescription": "Misprediction of conditional branches" @@ -243,10 +263,5 @@ "EventCode": "0x22", "EventName": "mispredict_target_ret_inst", "BriefDescription": "Misprediction of targets of Return instructions" - }, - { - "EventCode": "0x32", - "EventName": "replay_las_sas", - "BriefDescription": "Replay for load-after-store or store-after-store cases" } ] diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX65/firmware.json b/tools/perf/pmu-events/arch/riscv/andes/AX65/firmware.json new file mode 100644 index 00000000000000..61e42c1813fa33 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/AX65/firmware.json @@ -0,0 +1,74 @@ +[ + { + "ArchStdEvent": "FW_MISALIGNED_LOAD" + }, + { + "ArchStdEvent": "FW_MISALIGNED_STORE" + }, + { + "ArchStdEvent": "FW_ACCESS_LOAD" + }, + { + "ArchStdEvent": "FW_ACCESS_STORE" + }, + { + "ArchStdEvent": "FW_ILLEGAL_INSN" + }, + { + "ArchStdEvent": "FW_SET_TIMER" + }, + { + "ArchStdEvent": "FW_IPI_SENT" + }, + { + "ArchStdEvent": "FW_IPI_RECEIVED" + }, + { + "ArchStdEvent": "FW_FENCE_I_SENT" + }, + { + "ArchStdEvent": "FW_FENCE_I_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_SENT" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_SFENCE_VMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_GVMA_VMID_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_RECEIVED" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_SENT" + }, + { + "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESSES" + }, + { + "ArchStdEvent": "ANDES_L2C_ACCESS_MISSES" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX65/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/AX65/riscvstd.json new file mode 100644 index 00000000000000..916712a2e123d1 --- /dev/null +++ b/tools/perf/pmu-events/arch/riscv/andes/AX65/riscvstd.json @@ -0,0 +1,262 @@ +[ + { + "EventCode": "0x10", + "EventName": "cycle_count", + "BriefDescription": "Cycle counts" + }, + { + "EventCode": "0x20", + "EventName": "inst_count", + "BriefDescription": "Retired instruction counts" + }, + { + "EventCode": "0x30", + "EventName": "int_load_inst", + "BriefDescription": "Integer load instructions" + }, + { + "EventCode": "0x40", + "EventName": "int_store_inst", + "BriefDescription": "Integer store instructions" + }, + { + "EventCode": "0x50", + "EventName": "atomic_mem_op", + "BriefDescription": "Atomic memory operation" + }, + { + "EventCode": "0x60", + "EventName": "sys_inst", + "BriefDescription": "System instructions" + }, + { + "EventCode": "0x70", + "EventName": "int_compute_inst", + "BriefDescription": "Integer computational instruction" + }, + { + "EventCode": "0x80", + "EventName": "condition_br", + "BriefDescription": "Conditional branch" + }, + { + "EventCode": "0x90", + "EventName": "taken_condition_br", + "BriefDescription": "Taken conditional branch" + }, + { + "EventCode": "0xA0", + "EventName": "jal_inst", + "BriefDescription": "JAL instruction" + }, + { + "EventCode": "0xB0", + "EventName": "jalr_inst", + "BriefDescription": "JALR instruction" + }, + { + "EventCode": "0xC0", + "EventName": "ret_inst", + "BriefDescription": "Return instruction" + }, + { + "EventCode": "0xD0", + "EventName": "control_trans_inst", + "BriefDescription": "Control transfer instruction" + }, + { + "EventCode": "0xF0", + "EventName": "int_mul_inst", + "BriefDescription": "Integer multiplication instruction" + }, + { + "EventCode": "0x100", + "EventName": "int_div_rem_inst", + "BriefDescription": "Integer division/remainder instruction" + }, + { + "EventCode": "0x110", + "EventName": "float_load_inst", + "BriefDescription": "Floating-point load instruction" + }, + { + "EventCode": "0x120", + "EventName": "float_store_inst", + "BriefDescription": "Floating-point store instruction" + }, + { + "EventCode": "0x130", + "EventName": "float_add_sub_inst", + "BriefDescription": "Floating-point addition/subtraction" + }, + { + "EventCode": "0x140", + "EventName": "float_mul_inst", + "BriefDescription": "Floating-point multiplication" + }, + { + "EventCode": "0x150", + "EventName": "float_fused_muladd_inst", + "BriefDescription": "Floating-point fused multiply-add" + }, + { + "EventCode": "0x160", + "EventName": "float_div_sqrt_inst", + "BriefDescription": "Floating-point division or square-root" + }, + { + "EventCode": "0x170", + "EventName": "other_float_inst", + "BriefDescription": "Other floating-point instruction" + }, + { + "EventCode": "0x190", + "EventName": "op_count", + "BriefDescription": "Retired operation count" + }, + { + "EventCode": "0x21", + "EventName": "icache_access", + "BriefDescription": "ICACHE access" + }, + { + "EventCode": "0x31", + "EventName": "icache_miss", + "BriefDescription": "ICACHE miss" + }, + { + "EventCode": "0x41", + "EventName": "dcache_access", + "BriefDescription": "DCACHE access" + }, + { + "EventCode": "0x51", + "EventName": "dcache_miss", + "BriefDescription": "DCACHE miss" + }, + { + "EventCode": "0x61", + "EventName": "dcache_load_access", + "BriefDescription": "DCACHE load access" + }, + { + "EventCode": "0x71", + "EventName": "dcache_load_miss", + "BriefDescription": "DCACHE load miss" + }, + { + "EventCode": "0x81", + "EventName": "dcache_store_access", + "BriefDescription": "DCACHE store access" + }, + { + "EventCode": "0x91", + "EventName": "dcache_store_miss", + "BriefDescription": "DCACHE store miss" + }, + { + "EventCode": "0xA1", + "EventName": "dcache_wb", + "BriefDescription": "DCACHE writeback" + }, + { + "EventCode": "0xB1", + "EventName": "cycle_wait_icache_fill", + "BriefDescription": "Cycles waiting for ICACHE fill data" + }, + { + "EventCode": "0xC1", + "EventName": "cycle_wait_dcache_fill", + "BriefDescription": "Cycles waiting for DCACHE fill data" + }, + { + "EventCode": "0xD1", + "EventName": "uncached_ifetch_from_bus", + "BriefDescription": "Uncached ifetch data access from bus" + }, + { + "EventCode": "0xE1", + "EventName": "uncached_load_from_bus", + "BriefDescription": "Uncached load data access from bus" + }, + { + "EventCode": "0xF1", + "EventName": "cycle_wait_uncached_ifetch", + "BriefDescription": "Cycles waiting for uncached ifetch data from bus" + }, + { + "EventCode": "0x101", + "EventName": "cycle_wait_uncached_load", + "BriefDescription": "Cycles waiting for uncached load data from bus" + }, + { + "EventCode": "0x111", + "EventName": "main_itlb_access", + "BriefDescription": "Main ITLB access" + }, + { + "EventCode": "0x121", + "EventName": "main_itlb_miss", + "BriefDescription": "Main ITLB miss" + }, + { + "EventCode": "0x131", + "EventName": "main_dtlb_access", + "BriefDescription": "Main DTLB access" + }, + { + "EventCode": "0x141", + "EventName": "main_dtlb_miss", + "BriefDescription": "Main DTLB miss" + }, + { + "EventCode": "0x151", + "EventName": "cycle_wait_itlb_fill", + "BriefDescription": "Cycles waiting for Main ITLB fill data" + }, + { + "EventCode": "0x171", + "EventName": "prefetch_bus_access", + "BriefDescription": "Hardware prefetch bus access" + }, + { + "EventCode": "0x191", + "EventName": "l1_itlb_access", + "BriefDescription": "L1 ITLB access" + }, + { + "EventCode": "0x1A1", + "EventName": "l1_itlb_miss", + "BriefDescription": "L1 ITLB miss" + }, + { + "EventCode": "0x1B1", + "EventName": "l1_dtlb_access", + "BriefDescription": "L1 DTLB access" + }, + { + "EventCode": "0x1C1", + "EventName": "l1_dtlb_miss", + "BriefDescription": "L1 DTLB miss" + }, + { + "EventCode": "0x02", + "EventName": "mispredict_condition_br", + "BriefDescription": "Misprediction of conditional branches" + }, + { + "EventCode": "0x12", + "EventName": "mispredict_take_condition_br", + "BriefDescription": "Misprediction of taken conditional branches" + }, + { + "EventCode": "0x22", + "EventName": "mispredict_target_ret_inst", + "BriefDescription": "Misprediction of targets of Return instructions" + }, + { + "EventCode": "0x32", + "EventName": "replay_las_sas", + "BriefDescription": "Replay for load-after-store or store-after-store cases" + } +] diff --git a/tools/perf/pmu-events/arch/riscv/mapfile.csv b/tools/perf/pmu-events/arch/riscv/mapfile.csv index 5f0e9a8b08edec..631257eefbeb73 100644 --- a/tools/perf/pmu-events/arch/riscv/mapfile.csv +++ b/tools/perf/pmu-events/arch/riscv/mapfile.csv @@ -16,3 +16,7 @@ #MVENDORID-MARCHID-MIMPID,Version,Filename,EventType 0x489-0x8000000000000007-0x[[:xdigit:]]+,v1,sifive/u74,core 0x31e-0x8000000000008a45-0x[[:xdigit:]]+,V5,andes/AX45,core +0x31e-0x8000000000000a45-0x[[:xdigit:]]+,V5,andes/A45,core +0x31e-0x8000000000008a25-0x[[:xdigit:]]+,V5,andes/AX25,core +0x31e-0x8000000000000a25-0x[[:xdigit:]]+,V5,andes/A25,core +0x31e-0x8000000000008a65-0x[[:xdigit:]]+,V5,andes/AX65,core From b994db2bef7669f38bc17d520da8ceabab583419 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 1 Aug 2023 08:59:59 +0800 Subject: [PATCH 125/169] riscv: andes: defconfig: andes_defconfig default disable CONFIG_PREEMPT Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/configs/andes_defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/riscv/configs/andes_defconfig b/arch/riscv/configs/andes_defconfig index 1adcc226b197ee..8e32ed0d68167b 100644 --- a/arch/riscv/configs/andes_defconfig +++ b/arch/riscv/configs/andes_defconfig @@ -4,7 +4,6 @@ CONFIG_NO_HZ_IDLE=y CONFIG_HZ_100=y CONFIG_HIGH_RES_TIMERS=y CONFIG_BPF_SYSCALL=y -CONFIG_PREEMPT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y From 3e73d7ae7a7bbff44bed308ce2cc91fb2e29570a Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Thu, 3 Aug 2023 17:03:21 +0800 Subject: [PATCH 126/169] riscv: andes: Add RECOVER_UPSTREAM_CONFIG_RISCV to support upstream riscv make defconfig RECOVER_UPSTREAM_CONFIG_RISCV support for recovering the upstream "config RISCV," as setting in the arch/riscv/Kconfig. When selecting the PLAT_AE350 configuration, certain default enabled configurations from "config RISCV" are not needed. Therefore, we modified these configurations to be default-disabled and then re-enabled them under this specific configuration. Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig.socs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 6329c077862d75..2cb292540a9a4a 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -1,11 +1,31 @@ menu "SoC selection" +choice + prompt "Andes Linux for AE350 platform or upstream riscv" + default RECOVER_UPSTREAM_CONFIG_RISCV + help + Andes Platform use PLAT_AE350 for Andes full features support. + +config RECOVER_UPSTREAM_CONFIG_RISCV + bool "Andes Linux kernel recover upstream config RISCV setting" + select SIFIVE_PLIC + select OF_DMA_DEFAULT_COHERENT + select DMA_DIRECT_REMAP + help + This enables support for recovering the upstream "config RISCV", + as setting in the arch/riscv/Kconfig. When selecting the PLAT_AE350 + configuration, certain default enabled configurations from + "config RISCV" are not needed. Therefore, we modified these + configurations to be default-disabled and then re-enabled them + under this specific configuration. + config PLAT_AE350 bool "Andes AE350 Platform" select SIFIVE_PLIC select ERRATA_ANDES if !XIP_KERNEL help This enables support for Andes AE350 platform hardware. +endchoice config ANDES_QEMU_SUPPORT bool "Andes QEMU SUPPORT" From aa6c8cc19ca1d6ea2eb224c2f8fca05ad87f6f4e Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Thu, 3 Aug 2023 17:09:21 +0800 Subject: [PATCH 127/169] riscv: andes: fix make failure with riscv generic defconfig Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/include/asm/io.h | 2 ++ arch/riscv/include/asm/pgtable.h | 11 ++++++++++- arch/riscv/include/asm/ptrace.h | 2 ++ arch/riscv/kernel/asm-offsets.c | 3 +++ arch/riscv/kernel/entry.S | 4 ++++ arch/riscv/kernel/process.c | 2 ++ include/soc/andes/dma.h | 11 ++++++++++- include/soc/andes/sbi.h | 6 ++++++ 8 files changed, 39 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/io.h b/arch/riscv/include/asm/io.h index 3f3ef8f6dae9c1..95851da85b52f4 100644 --- a/arch/riscv/include/asm/io.h +++ b/arch/riscv/include/asm/io.h @@ -134,12 +134,14 @@ __io_writes_outs(outs, u64, q, __io_pbr(), __io_paw()) #endif #ifdef CONFIG_MMU +#ifdef CONFIG_PLAT_AE350 extern void __iomem *ioremap_wc(phys_addr_t phys_addr, size_t size); #define ioremap_wc ioremap_wc #define ioremap_wt ioremap_wc extern bool iounmap_allowed(void *addr); #define iounmap_allowed iounmap_allowed +#endif /* CONFIG_PLAT_AE350 */ #endif /* CONFIG_MMU */ #include diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 3afd20781e8505..a520d5124e5b64 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -150,6 +150,7 @@ struct pt_alloc_ops { extern struct pt_alloc_ops pt_ops __initdata; #ifdef CONFIG_MMU +#ifdef CONFIG_PLAT_AE350 /* Noncacheable page prot for Andes MSB */ extern phys_addr_t andes_pa_msb; #ifdef CONFIG_ANDES_QEMU_SUPPORT @@ -157,6 +158,7 @@ extern phys_addr_t andes_pa_msb; #else #define _PAGE_NONCACHEABLE ((!!andes_pa_msb) << 31) #endif +#endif /* CONFIG_PLAT_AE350 */ /* Number of PGD entries that a user-mode program can use */ #define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE) @@ -328,6 +330,7 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot) ALT_THEAD_PMA(prot_val); +#ifdef CONFIG_PLAT_AE350 /* * When PPMA is on and activated: pa_msb == 0; * Otherwise: pa_msb != 0; @@ -337,6 +340,9 @@ static inline pte_t pfn_pte(unsigned long pfn, pgprot_t prot) | (prot_val & ~_PAGE_NONCACHEABLE)); else return __pte((pfn << _PAGE_PFN_SHIFT) | prot_val); +#else + return __pte((pfn << _PAGE_PFN_SHIFT) | prot_val); +#endif /* !CONFIG_PLAT_AE350 */ } #define mk_pte(page, prot) pfn_pte(page_to_pfn(page), prot) @@ -595,7 +601,9 @@ static inline pgprot_t pgprot_noncached(pgprot_t _prot) prot &= ~_PAGE_MTMASK; prot |= _PAGE_IO; +#ifdef CONFIG_PLAT_AE350 prot |= _PAGE_NONCACHEABLE; +#endif return __pgprot(prot); } @@ -607,8 +615,9 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot) prot &= ~_PAGE_MTMASK; prot |= _PAGE_NOCACHE; +#ifdef CONFIG_PLAT_AE350 prot |= _PAGE_NONCACHEABLE; - +#endif return __pgprot(prot); } diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h index fc526aaa23a725..bcf24741a62cae 100644 --- a/arch/riscv/include/asm/ptrace.h +++ b/arch/riscv/include/asm/ptrace.h @@ -50,8 +50,10 @@ struct pt_regs { unsigned long badaddr; unsigned long cause; +#ifdef CONFIG_PLAT_AE350 /* Andes supervisor detailed trap cause */ unsigned long sdcause; +#endif /* a0 value before the syscall */ unsigned long orig_a0; }; diff --git a/arch/riscv/kernel/asm-offsets.c b/arch/riscv/kernel/asm-offsets.c index 65042c283b40da..bfef7dea4ab20b 100644 --- a/arch/riscv/kernel/asm-offsets.c +++ b/arch/riscv/kernel/asm-offsets.c @@ -113,7 +113,10 @@ void asm_offsets(void) OFFSET(PT_STATUS, pt_regs, status); OFFSET(PT_BADADDR, pt_regs, badaddr); OFFSET(PT_CAUSE, pt_regs, cause); + +#ifdef CONFIG_PLAT_AE350 OFFSET(PT_SDCAUSE, pt_regs, sdcause); +#endif OFFSET(SUSPEND_CONTEXT_REGS, suspend_context, regs); diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index e2aea3e24a8d68..9e295c79e83094 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -90,14 +90,18 @@ _save_context: csrr s3, CSR_TVAL csrr s4, CSR_CAUSE csrr s5, CSR_SCRATCH +#ifdef CONFIG_PLAT_AE350 csrr s6, CSR_SDCAUSE +#endif REG_S s0, PT_SP(sp) REG_S s1, PT_STATUS(sp) REG_S s2, PT_EPC(sp) REG_S s3, PT_BADADDR(sp) REG_S s4, PT_CAUSE(sp) REG_S s5, PT_TP(sp) +#ifdef CONFIG_PLAT_AE350 REG_S s6, PT_SDCAUSE(sp) +#endif /* * Set the scratch register to 0, so that if a recursive exception diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index 1dc6cc1974932c..e7bceecba16c34 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -75,8 +75,10 @@ void __show_regs(struct pt_regs *regs) pr_cont(" t5 : " REG_FMT " t6 : " REG_FMT "\n", regs->t5, regs->t6); +#ifdef CONFIG_PLAT_AE350 pr_cont("status: " REG_FMT " badaddr: " REG_FMT " cause: " REG_FMT " sdcause: " REG_FMT "\n", regs->status, regs->badaddr, regs->cause, regs->sdcause); +#endif } void show_regs(struct pt_regs *regs) { diff --git a/include/soc/andes/dma.h b/include/soc/andes/dma.h index 176b426c2aa1b9..ae724c1e60275c 100644 --- a/include/soc/andes/dma.h +++ b/include/soc/andes/dma.h @@ -34,6 +34,7 @@ struct range_info { struct page *page; }; +#ifdef CONFIG_PLAT_AE350 void cpu_dma_inval_range(void *info); void cpu_dma_wb_range(void *info); @@ -56,7 +57,7 @@ static inline void dma_flush_page(struct page *page, size_t size) } static inline void andes_cache_op(phys_addr_t paddr, size_t size, - void (*fn)(void *ri)) + void (*fn)(void *ri)) { struct page *page = pfn_to_page(paddr >> PAGE_SHIFT); unsigned offset = paddr & ~PAGE_MASK; @@ -101,4 +102,12 @@ static inline void andes_cache_op(phys_addr_t paddr, size_t size, extern void *arch_dma_set_uncached(void *addr, size_t size); extern void arch_dma_clear_uncached(void *addr, size_t size); +#else +static inline void cpu_dma_inval_range(void *info) {} +static inline void cpu_dma_wb_range(void *info) {} +static inline void dma_flush_page(struct page *page, size_t size) {} +static inline void andes_cache_op(phys_addr_t paddr, size_t size, + void (*fn)(void *ri)) {} +#endif /* !CONFIG_PLAT_AE350 */ + #endif /* !__SOC_ANDES_DMA_H */ diff --git a/include/soc/andes/sbi.h b/include/soc/andes/sbi.h index 47a51347e6a48e..4e716a74ed9feb 100644 --- a/include/soc/andes/sbi.h +++ b/include/soc/andes/sbi.h @@ -59,7 +59,13 @@ void sbi_andes_set_ppma(void *arg); void sbi_andes_free_ppma(void *addr); long sbi_andes_probe_ppma(void); +#ifdef CONFIG_PLAT_AE350 /* Trigger module support debug application with gdbserver */ void sbi_andes_set_trigger(unsigned int type, uintptr_t data, int enable); +#else +static inline void sbi_andes_set_trigger(unsigned int type, + uintptr_t data, + int enable) {} +#endif /* !CONFIG_PLAT_AE350 */ #endif /* !__SOC_ANDES_SBI_H */ From edece22e7d23514eeacd493b923e3d77b9133226 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Tue, 8 Aug 2023 14:32:50 +0800 Subject: [PATCH 128/169] cpufreq: andes-cpufreq: Andes processors PowerBrake driver support --- drivers/cpufreq/Kconfig | 4 + drivers/cpufreq/Kconfig.riscv | 11 ++ drivers/cpufreq/Makefile | 4 + drivers/cpufreq/andes-cpufreq.c | 187 ++++++++++++++++++++++++++++++++ drivers/soc/andes/andes_sbi.c | 18 +++ include/soc/andes/sbi.h | 4 + 6 files changed, 228 insertions(+) create mode 100644 drivers/cpufreq/Kconfig.riscv create mode 100644 drivers/cpufreq/andes-cpufreq.c diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 2a84fc63371e2e..98e39dfc1d4802 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -239,6 +239,10 @@ if PPC32 || PPC64 source "drivers/cpufreq/Kconfig.powerpc" endif +if RISCV +source "drivers/cpufreq/Kconfig.riscv" +endif + if IA64 config IA64_ACPI_CPUFREQ tristate "ACPI Processor P-States driver" diff --git a/drivers/cpufreq/Kconfig.riscv b/drivers/cpufreq/Kconfig.riscv new file mode 100644 index 00000000000000..4132fb3d91a755 --- /dev/null +++ b/drivers/cpufreq/Kconfig.riscv @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# RISC-V CPU Frequency scaling drivers +# + +config RISCV_ANDES_CPUFREQ_POWERBRAKE + bool "Andes RISC-V processor PowerBrake CPUFreq driver" + depends on PLAT_AE350 + default n + help + This adds CPUFreq driver for Andes RISC-V processor PowerBrake. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 49b98c62c5af59..4f03f9885505e4 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -105,6 +105,10 @@ obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o obj-$(CONFIG_POWERNV_CPUFREQ) += powernv-cpufreq.o +################################################################################## +# RISC-V platform drivers +obj-$(CONFIG_RISCV_ANDES_CPUFREQ_POWERBRAKE) += andes-cpufreq.o + ################################################################################## # Other platform drivers obj-$(CONFIG_BMIPS_CPUFREQ) += bmips-cpufreq.o diff --git a/drivers/cpufreq/andes-cpufreq.c b/drivers/cpufreq/andes-cpufreq.c new file mode 100644 index 00000000000000..fe2cc428a01bea --- /dev/null +++ b/drivers/cpufreq/andes-cpufreq.c @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define ANDES_PB_LV_MASK 0xF0 +#define ANDES_PB_LV_OFF 4 + +#define ANDES_PB_NUM_LV 16 +#define ANDES_PB_LOW_LV 15 +#define ANDES_PB_HIGH_LV 0 +#define ANDES_PB_PERIOD (policy->cpuinfo.max_freq / ANDES_PB_NUM_LV) + +void read_powerbrake(void *arg) +{ + long ret; + unsigned int *val = arg; + + ret = sbi_andes_read_powerbrake(); + *val = (ret & ANDES_PB_LV_MASK) >> ANDES_PB_LV_OFF; +} + +void write_powerbrake(void *arg) +{ + unsigned int *val = arg; + + sbi_andes_write_powerbrake(*val); +} + +static unsigned int andes_cpufreq_get(unsigned int cpu) +{ + unsigned int val, max; + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + + smp_call_function_single(cpu, read_powerbrake, &val, 1); + + max = (ANDES_PB_LOW_LV - val + 1) * ANDES_PB_PERIOD; + return max; +} + +static int andes_cpufreq_set_policy(struct cpufreq_policy *policy) +{ + int i; + unsigned int cpu, val; + + cpu = policy->cpu; + if (!policy) + return -EINVAL; + + if (policy->min == 0 && policy->max == 0) { + pr_err("Cannot set zero freq\n"); + return -EINVAL; + } + val = (policy->min + policy->max) / 2; + switch (policy->policy) { + case CPUFREQ_POLICY_POWERSAVE: + val = (val + policy->min) / 2; + break; + case CPUFREQ_POLICY_PERFORMANCE: + val = (val + policy->max) / 2; + break; + default: + pr_err("Not Support this governor\n"); + break; + } + + if (val < 0) { + pr_err("The freq is valid\n"); + return -EINVAL; + } + + /* + * PowerBrake register has 16 level, + * so we divide the xxxkHZ into 16 parts. + * + * EX: 100MHZ->100*1000kHZ + * | | |........| + * Mhz 0 6250 12500 16*6250 + */ + + // transfer MHZ to kHZ, and divided to 16 level + for (i = 1; i <= ANDES_PB_NUM_LV; i++) { + if (val <= i * ANDES_PB_PERIOD) { + val = ANDES_PB_LOW_LV - (i - 1); + break; + } + } + val = val << ANDES_PB_LV_OFF; + + return smp_call_function_single(cpu, write_powerbrake, &val, 1); +} + +static int andes_cpufreq_verify_policy(struct cpufreq_policy_data *policy) +{ + if (!policy) + return -EINVAL; + + cpufreq_verify_within_cpu_limits(policy); + + return 0; +} + +static int andes_get_policy(struct cpufreq_policy *policy) +{ + unsigned int val; + + if (!policy) + return -EINVAL; + + smp_call_function_single(policy->cpu, read_powerbrake, &val, 1); + + /* + * PowerBrake register has 16 level, + * so we divide the xxxkHZ into 16 parts. + * + * EX: 100MHZ->100*1000kHZ + * | | |........| + * Mhz 0 6250 12500 16*6250 + */ + policy->min = (ANDES_PB_LOW_LV - val) * ANDES_PB_PERIOD; + policy->max = (ANDES_PB_LOW_LV - val + 1) * ANDES_PB_PERIOD; + policy->policy = CPUFREQ_POLICY_PERFORMANCE; + return 0; +} + +static int andes_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + int ret; + u32 max_freq; + struct device_node *cpu; + + if (!policy) + return -EINVAL; + + cpu = of_get_cpu_node(policy->cpu, NULL); + + if (!cpu) + return -ENODEV; + + pr_debug("init cpufreq on CPU %d\n", policy->cpu); + if (of_property_read_u32(cpu, "clock-frequency", &max_freq)) { + pr_err("%s missing clock-frequency\n", cpu->name); + return -EINVAL; + } + of_node_put(cpu); + + policy->cpuinfo.min_freq = 0; + policy->cpuinfo.max_freq = max_freq / 1000; /* kHZ */ + ret = andes_get_policy(policy); + if (ret) + return ret; + + return 0; +} + +static struct cpufreq_driver andes_cpufreq_driver = { + .flags = CPUFREQ_CONST_LOOPS, + .verify = andes_cpufreq_verify_policy, + .setpolicy = andes_cpufreq_set_policy, + .get = andes_cpufreq_get, + .init = andes_cpufreq_cpu_init, + .name = "andes_cpufreq", +}; + +static int __init andes_cpufreq_init(void) +{ + return cpufreq_register_driver(&andes_cpufreq_driver); +} + +static void __exit andes_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&andes_cpufreq_driver); +} + +MODULE_DESCRIPTION("PowerBrake driver for Andes processors"); +MODULE_LICENSE("GPL"); +module_init(andes_cpufreq_init); +module_exit(andes_cpufreq_exit); diff --git a/drivers/soc/andes/andes_sbi.c b/drivers/soc/andes/andes_sbi.c index 52520c99e2d598..74da5aa16821a0 100644 --- a/drivers/soc/andes/andes_sbi.c +++ b/drivers/soc/andes/andes_sbi.c @@ -37,3 +37,21 @@ void sbi_andes_set_trigger(unsigned int type, uintptr_t data, int enable) type, data, enable, 0, 0, 0); } EXPORT_SYMBOL(sbi_andes_set_trigger); + +/* PowerBrake */ +void sbi_andes_write_powerbrake(unsigned int val) +{ + sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_WRITE_POWERBRAKE, + val, 0, 0, 0, 0, 0); +} +EXPORT_SYMBOL(sbi_andes_write_powerbrake); + +long sbi_andes_read_powerbrake(void) +{ + struct sbiret ret; + + ret = sbi_ecall(SBI_EXT_ANDES, SBI_EXT_ANDES_READ_POWERBRAKE, + 0, 0, 0, 0, 0, 0); + return ret.value; +} +EXPORT_SYMBOL(sbi_andes_read_powerbrake); diff --git a/include/soc/andes/sbi.h b/include/soc/andes/sbi.h index 4e716a74ed9feb..6dda406e139f88 100644 --- a/include/soc/andes/sbi.h +++ b/include/soc/andes/sbi.h @@ -68,4 +68,8 @@ static inline void sbi_andes_set_trigger(unsigned int type, int enable) {} #endif /* !CONFIG_PLAT_AE350 */ +/* PowerBrake */ +void sbi_andes_write_powerbrake(unsigned int val); +long sbi_andes_read_powerbrake(void); + #endif /* !__SOC_ANDES_SBI_H */ From afbbde8e24ff5a30f8058ea2424ddf4f132d9fb5 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Wed, 9 Aug 2023 12:34:01 +0800 Subject: [PATCH 129/169] riscv: andes: remove config ANDES_QEMU_SUPPORT The Andes dma-coherent support is checked in the following order: ZICBOM -> PPMA -> MSB However, the older version of QEMU does not support the ZICBOM and PPMA. When using a non-coherent setup in the Device Tree Source, the ANDES MSB takes effect. QEMU doesn't support the ANDES MSB, which results in system issues. The current, newer version of QEMU supports PPMA. As a result, the ANDES_QEMU_SUPPORT has been removed. Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/Kconfig.socs | 7 ------- arch/riscv/include/asm/pgtable.h | 4 ---- 2 files changed, 11 deletions(-) diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 2cb292540a9a4a..57c1e3112b7452 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -27,13 +27,6 @@ config PLAT_AE350 This enables support for Andes AE350 platform hardware. endchoice -config ANDES_QEMU_SUPPORT - bool "Andes QEMU SUPPORT" - depends on SOC_SIFIVE - default n - help - Andes QEMU Support. - config SOC_MICROCHIP_POLARFIRE bool "Microchip PolarFire SoCs" select MCHP_CLK_MPFS diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index a520d5124e5b64..7b232d442b8f7e 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -153,11 +153,7 @@ extern struct pt_alloc_ops pt_ops __initdata; #ifdef CONFIG_PLAT_AE350 /* Noncacheable page prot for Andes MSB */ extern phys_addr_t andes_pa_msb; -#ifdef CONFIG_ANDES_QEMU_SUPPORT -#define _PAGE_NONCACHEABLE 0 -#else #define _PAGE_NONCACHEABLE ((!!andes_pa_msb) << 31) -#endif #endif /* CONFIG_PLAT_AE350 */ /* Number of PGD entries that a user-mode program can use */ From f513b3560e5655c5801fdb8274f9e02efb7883e3 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Mon, 4 Sep 2023 15:52:49 +0800 Subject: [PATCH 130/169] gpio: andes: atcgpio100: Fix recursive deadlock when setting the direction of a GPIO. Please see the following link for details. https://es.andestech.com/scp/tasks.php?id=3717 Signed-off-by: CL Wang --- drivers/gpio/gpio-atcgpio100.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-atcgpio100.c b/drivers/gpio/gpio-atcgpio100.c index d65532aab08de0..f6c16bce53dffa 100644 --- a/drivers/gpio/gpio-atcgpio100.c +++ b/drivers/gpio/gpio-atcgpio100.c @@ -72,19 +72,25 @@ static int atcgpio_get(struct gpio_chip *gc, unsigned int gpio) return (val >> gpio & 1); } -static void atcgpio_set(struct gpio_chip *gc, unsigned int gpio, int data) +static void atcgpio_setgpio_val(struct atcgpio_priv *priv, unsigned int gpio, int data) { - unsigned long flags; - struct atcgpio_priv *priv; unsigned long val; - priv = gpiochip_get_data(gc); - spin_lock_irqsave(&priv->lock, flags); if (data) val = GPIO_READL(GPIO_DATA_OUT, priv->base) | (0x1UL << gpio); else val = GPIO_READL(GPIO_DATA_OUT, priv->base) & ~(0x1UL << gpio); GPIO_WRITEL(val, GPIO_DATA_OUT, priv->base); +} + +static void atcgpio_set(struct gpio_chip *gc, unsigned int gpio, int data) +{ + unsigned long flags; + struct atcgpio_priv *priv; + + priv = gpiochip_get_data(gc); + spin_lock_irqsave(&priv->lock, flags); + atcgpio_setgpio_val(priv, gpio, data); spin_unlock_irqrestore(&priv->lock, flags); } @@ -111,9 +117,9 @@ static int atcgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int data) priv = gpiochip_get_data(gc); spin_lock_irqsave(&priv->lock, flags); + atcgpio_setgpio_val(priv, gpio, data); val = GPIO_READL(PIN_DIR, priv->base) | (0x1UL << gpio); GPIO_WRITEL(val, PIN_DIR, priv->base); - gc->set(gc, gpio, data); spin_unlock_irqrestore(&priv->lock, flags); return 0; From 4f22a448af9f5d73d706a479b77a5d320dca475a Mon Sep 17 00:00:00 2001 From: CL Wang Date: Thu, 31 Aug 2023 14:06:07 +0800 Subject: [PATCH 131/169] dmaengine: andes: atcdmac300g: Fix some typos 1. "Andestech" to "Andes" 2. "Andes Corporation" to "Andes Technology Corporation" Signed-off-by: CL Wang --- drivers/dma/atcdmac300g.c | 4 ++-- drivers/dma/atcdmac300g.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/dma/atcdmac300g.c b/drivers/dma/atcdmac300g.c index 20970fb1b75db4..5c92d75bf4d8b9 100644 --- a/drivers/dma/atcdmac300g.c +++ b/drivers/dma/atcdmac300g.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Driver for the Andestech ATCDMAC300 + * Driver for the Andes ATCDMAC300 * - * Copyright (C) 2021 Andestech Corporation + * Copyright (C) 2021 Andes Technology Corporation * */ #include diff --git a/drivers/dma/atcdmac300g.h b/drivers/dma/atcdmac300g.h index a5d876f7c57d42..fdb70903cd2fdf 100644 --- a/drivers/dma/atcdmac300g.h +++ b/drivers/dma/atcdmac300g.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Header file for the Andestech DMA Controller driver + * Header file for the Andes DMA Controller driver * - * Copyright (C) 2021 Andestech Corporation + * Copyright (C) 2021 Andes Technology Corporation */ #ifndef ATCDMAC300G_H #define ATCDMAC300G_H From 6065d72a7a2bfd4ad5cc0092c32139bc77fe40d0 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Thu, 31 Aug 2023 14:14:59 +0800 Subject: [PATCH 132/169] clocksource: andes: atcpit: Refine the source code of the atcpit driver. Follow Tim's suggestion to refine the source code of the driver Please refer to the following link for the detailed reason for the change. Signed-off-by: CL Wang --- drivers/clocksource/timer-atcpit.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/clocksource/timer-atcpit.c b/drivers/clocksource/timer-atcpit.c index 1fd6ce321230f5..35b947c71cded2 100755 --- a/drivers/clocksource/timer-atcpit.c +++ b/drivers/clocksource/timer-atcpit.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2023 Andes Corporation + * Copyright (C) 2023 Andes Technology Corporation */ #include @@ -110,7 +110,7 @@ static void atcpit_ch_en(void __iomem *base, u8 ch, u8 timer_id, u8 en) u32 pit_en; pit_en = readl(base + CH_EN_REG); - if (en == 1) + if (en) pit_en |= CH_EN_TIME(ch, timer_id); else pit_en &= ~(CH_EN_TIME(ch, timer_id)); @@ -124,7 +124,7 @@ static void atcpit_ch_int_en(void __iomem *base, u8 ch, u8 timer_id, u8 en) pit_int_en = readl(base + INT_EN); - if (en == 1) + if (en) pit_int_en |= CH_INT_EN(ch, timer_id); else pit_int_en &= ~(CH_INT_EN(ch, timer_id)); @@ -155,7 +155,7 @@ static inline void atcpit_clkevt_time_stop(struct atcpit_data *pit_data) static int atcpit_clkevt_set_periodic(struct clock_event_device *evt) { - struct atcpit_data *pit_data = NULL; + struct atcpit_data *pit_data; struct timer_of *to = to_timer_of(evt); pit_data = to_atcpit_data_clkevt(evt); @@ -169,7 +169,7 @@ static int atcpit_clkevt_set_periodic(struct clock_event_device *evt) static int atcpit_clkevt_shutdown(struct clock_event_device *evt) { - struct atcpit_data *pit_data = NULL; + struct atcpit_data *pit_data; pit_data = to_atcpit_data_clkevt(evt); atcpit_clkevt_time_stop(pit_data); @@ -192,7 +192,7 @@ static irqreturn_t atcpit_timer_interrupt(int irq, void *dev_id) static u64 atcpit_clksrc_read(struct clocksource *clksrc) { - struct atcpit_data *atcpit = NULL; + struct atcpit_data *atcpit; atcpit = to_atcpit_data_clksrc(clksrc); @@ -268,7 +268,7 @@ static int __init atcpit_clocksource_init(struct device_node *node, struct atcpi static int __init atcpit_timer_init(struct device_node *node) { int (*read_fixup)(void __iomem *addr, unsigned int val, unsigned int shift_bits); - struct atcpit_data *pit_data = NULL; + struct atcpit_data *pit_data; int ret = 0; u32 val; From 832e38b47ab8697ec42922ac211c5e0a1f65ff18 Mon Sep 17 00:00:00 2001 From: Leo Yu-Chi Liang Date: Tue, 5 Sep 2023 11:20:50 +0800 Subject: [PATCH 133/169] andes: ftsdc010: Add another revision number According to Kavalan/James , the revision number for future Andes SDC IP will be changed to 32'h0003_0108. Therefore, for backward compatibility, we add another revision number to accommodate this change. Signed-off-by: Leo Yu-Chi Liang --- drivers/mmc/host/ftsdc010.c | 4 +++- drivers/mmc/host/ftsdc010g.c | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/ftsdc010.c b/drivers/mmc/host/ftsdc010.c index 220d041e1b63e1..3f913b396650f7 100644 --- a/drivers/mmc/host/ftsdc010.c +++ b/drivers/mmc/host/ftsdc010.c @@ -1416,6 +1416,7 @@ static int __init ftsdc_probe(struct platform_device *pdev) struct ftsdc_mmc_config *pdata = NULL; struct resource *r, *mem = NULL; int ret = -ENOMEM; + int ret2 = -ENOMEM; u32 con; int irq = 0; size_t mem_size; @@ -1471,8 +1472,9 @@ static int __init ftsdc_probe(struct platform_device *pdev) /* Check revision register */ read_fixup = symbol_get(readl_fixup); ret = read_fixup(host->base + SDC_REVISION_REG, 0x00030107, 0); + ret2 = read_fixup(host->base + SDC_REVISION_REG, 0x00030108, 0); symbol_put(readl_fixup); - if (!ret) { + if (!ret && !ret2) { dev_err(&pdev->dev, "failed to read revision reg, bitmap not support ftdsdc\n"); ret = -ENXIO; diff --git a/drivers/mmc/host/ftsdc010g.c b/drivers/mmc/host/ftsdc010g.c index 0f929775ace28f..0787429e688a11 100644 --- a/drivers/mmc/host/ftsdc010g.c +++ b/drivers/mmc/host/ftsdc010g.c @@ -1354,7 +1354,7 @@ static int __init ftsdc_probe(struct platform_device *pdev) struct mmc_host *mmc; struct ftsdc_mmc_config *pdata = NULL; struct resource *r, *mem = NULL; - int ret = -ENOMEM; + int ret = -ENOMEM, ret2 = -ENOMEM; u32 con; int irq = 0; size_t mem_size; @@ -1413,8 +1413,9 @@ static int __init ftsdc_probe(struct platform_device *pdev) /* Check revision register */ read_fixup = symbol_get(readl_fixup); ret = read_fixup(host->base + SDC_REVISION_REG, 0x00030107, 0); + ret2 = read_fixup(host->base + SDC_REVISION_REG, 0x00030108, 0); symbol_put(readl_fixup); - if (!ret) { + if (!ret && !ret2) { dev_err(&pdev->dev, "bitmap revision mismatch(ftsdc)\n"); ret = -ENXIO; From 6fd3028f925262b8ef28797543b266880ca51355 Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Wed, 13 Sep 2023 14:59:54 +0800 Subject: [PATCH 134/169] riscv: dts: andes: fix device node interrupt-parent 1. i2c 2. mmc 3. dma Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts | 2 +- .../riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts | 4 ++-- arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts | 2 +- arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts | 2 +- .../boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts | 4 ++-- arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts | 2 +- arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts | 2 +- arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts | 2 +- arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts index 83c97202ec750f..15cfc2f522409a 100644 --- a/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/a25mp_c4_d_dsp_ae350.dts @@ -249,7 +249,7 @@ compatible = "andestech,atciic100"; reg = <0x00 0xf0a00000 0x00 0x1000>; interrupts = <0x06 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x06>; wakeup-source; }; diff --git a/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts index a60738c1e43dbc..9eeba6602e47e1 100644 --- a/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts +++ b/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts @@ -183,7 +183,7 @@ compatible = "andestech,atfsdc010g"; reg = <0x00 0xf0e00000 0x00 0x1000>; interrupts = <0x12 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x03>; clock-freq-min-max = <0x61a80 0x5f5e100>; max-frequency = <0x5f5e100>; fifo-depth = <0x10>; @@ -195,7 +195,7 @@ compatible = "andestech,atcdmac300g"; reg = <0x00 0xf0c00000 0x00 0x1000>; interrupts = <0x0a 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x03>; dma-channels = <0x08>; #dma-cells = <0x01>; phandle = <0x05>; diff --git a/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts index 689c633b9026ad..983fa08fa2b3b4 100644 --- a/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts @@ -249,7 +249,7 @@ compatible = "andestech,atciic100"; reg = <0x00 0xf0a00000 0x00 0x1000>; interrupts = <0x06 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x06>; wakeup-source; }; diff --git a/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts index 1a0f9bd6ebfbe0..f6e5753d0509f3 100644 --- a/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts @@ -249,7 +249,7 @@ compatible = "andestech,atciic100"; reg = <0x00 0xf0a00000 0x00 0x1000>; interrupts = <0x06 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x06>; wakeup-source; }; diff --git a/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts index f72ebc972366b3..26041b04cd7f2f 100644 --- a/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts @@ -183,7 +183,7 @@ compatible = "andestech,atfsdc010g"; reg = <0x00 0xf0e00000 0x00 0x1000>; interrupts = <0x12 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x03>; clock-freq-min-max = <0x61a80 0x5f5e100>; max-frequency = <0x5f5e100>; fifo-depth = <0x10>; @@ -195,7 +195,7 @@ compatible = "andestech,atcdmac300g"; reg = <0x00 0xf0c00000 0x00 0x1000>; interrupts = <0x0a 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x03>; dma-channels = <0x08>; #dma-cells = <0x01>; phandle = <0x05>; diff --git a/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts index 96d7daa8c90fb1..56e023d9496882 100644 --- a/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts @@ -249,7 +249,7 @@ compatible = "andestech,atciic100"; reg = <0x00 0xf0a00000 0x00 0x1000>; interrupts = <0x06 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x06>; wakeup-source; }; diff --git a/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts b/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts index a7db6e1810afe5..55fcfb9c052a70 100644 --- a/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts @@ -361,7 +361,7 @@ compatible = "andestech,atciic100"; reg = <0x00 0xf0a00000 0x00 0x1000>; interrupts = <0x06 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x0a>; wakeup-source; }; diff --git a/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts index c0d2903083f8c1..92844924260c1d 100644 --- a/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts @@ -249,7 +249,7 @@ compatible = "andestech,atciic100"; reg = <0x00 0xf0a00000 0x00 0x1000>; interrupts = <0x06 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x06>; wakeup-source; }; diff --git a/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts index 1e3c6da3403599..50617d61462ccf 100644 --- a/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts @@ -196,7 +196,7 @@ compatible = "andestech,atciic100"; reg = <0x00 0xf0a00000 0x00 0x1000>; interrupts = <0x06 0x04>; - interrupt-parent = <0x02>; + interrupt-parent = <0x04>; wakeup-source; }; From 1e8a749fd45739823e5b4060494c805481fd9c66 Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Tue, 22 Aug 2023 16:49:03 +0000 Subject: [PATCH 135/169] riscv: signal: fix sigaltstack frame size checking The alternative stack checking in get_sigframe introduced by the Vector support is not needed and has a problem. It is not needed as we have already validate it at the beginning of the function if we are already on an altstack. If not, the size of an altstack is always validated at its allocation stage with sigaltstack_size_valid(). Besides, we must only regard the size of an altstack if the handler of a signal is registered with SA_ONSTACK. So, blindly checking overflow of an altstack if sas_ss_size not equals to zero will check against wrong signal handlers if only a subset of signals are registered with SA_ONSTACK. Fixes: 8ee0b41898fa ("riscv: signal: Add sigcontext save/restore for vector") Reported-by: Prashanth Swaminathan Signed-off-by: Andy Chiu --- arch/riscv/kernel/signal.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c index 9581b883f3eec6..08d5fc64f1952c 100644 --- a/arch/riscv/kernel/signal.c +++ b/arch/riscv/kernel/signal.c @@ -354,13 +354,6 @@ static inline void __user *get_sigframe(struct ksignal *ksig, /* Align the stack frame. */ sp &= ~0xfUL; - /* - * Fail if the size of the altstack is not large enough for the - * sigframe construction. - */ - if (current->sas_ss_size && sp < current->sas_ss_sp) - return (void __user __force *)-1UL; - return (void __user *)sp; } From a5a817c8c142b2844cae6497734922126b553944 Mon Sep 17 00:00:00 2001 From: Andy Chiu Date: Fri, 25 Aug 2023 05:02:46 +0000 Subject: [PATCH 136/169] RISC-V: Add ptrace support for vectors This patch add back the ptrace support with the following fix: - Define NT_RISCV_CSR and re-number NT_RISCV_VECTOR to prevent conflicting with gdb's NT_RISCV_CSR. - Use struct __riscv_v_regset_state to handle ptrace requests Since gdb does not directly include the note description header in Linux and has already defined NT_RISCV_CSR as 0x900, we decide to sync with gdb and renumber NT_RISCV_VECTOR to solve and prevent future conflicts. Fixes: 0c59922c769a ("riscv: Add ptrace vector support") Signed-off-by: Andy Chiu Reported-by: "kernelci.org bot" --- arch/riscv/include/uapi/asm/ptrace.h | 13 +++-- arch/riscv/kernel/ptrace.c | 79 ++++++++++++++++++++++++++++ include/uapi/linux/elf.h | 2 + 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h index 595959acdd5482..b0ffea269ced32 100644 --- a/arch/riscv/include/uapi/asm/ptrace.h +++ b/arch/riscv/include/uapi/asm/ptrace.h @@ -109,13 +109,18 @@ struct __riscv_v_ext_state { * In signal handler, datap will be set a correct user stack offset * and vector registers will be copied to the address of datap * pointer. - * - * In ptrace syscall, datap will be set to zero and the vector - * registers will be copied to the address right after this - * structure. */ }; +struct __riscv_v_regset_state { + unsigned long vstart; + unsigned long vl; + unsigned long vtype; + unsigned long vcsr; + unsigned long vlenb; + char vreg[]; +}; + /* * According to spec: The number of bits in a single vector register, * VLEN >= ELEN, which must be a power of 2, and must be no greater than diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c index a9e73c849c6b1e..3dee280eef5d8c 100644 --- a/arch/riscv/kernel/ptrace.c +++ b/arch/riscv/kernel/ptrace.c @@ -30,6 +30,9 @@ enum riscv_regset { #ifdef CONFIG_FPU REGSET_F, #endif +#ifdef CONFIG_RISCV_ISA_V + REGSET_V, +#endif }; static int riscv_gpr_get(struct task_struct *target, @@ -86,6 +89,71 @@ static int riscv_fpr_set(struct task_struct *target, } #endif +#ifdef CONFIG_RISCV_ISA_V +static int riscv_vr_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + struct __riscv_v_ext_state *vstate = &target->thread.vstate; + struct __riscv_v_regset_state ptrace_vstate; + + if (!riscv_v_vstate_query(task_pt_regs(target))) + return -EINVAL; + + /* + * Ensure the vector registers have been saved to the memory before + * copying them to membuf. + */ + if (target == current) + riscv_v_vstate_save(current, task_pt_regs(current)); + + ptrace_vstate.vstart = vstate->vstart; + ptrace_vstate.vl = vstate->vl; + ptrace_vstate.vtype = vstate->vtype; + ptrace_vstate.vcsr = vstate->vcsr; + ptrace_vstate.vlenb = vstate->vlenb; + + /* Copy vector header from vstate. */ + membuf_write(&to, &ptrace_vstate, sizeof(struct __riscv_v_regset_state)); + + /* Copy all the vector registers from vstate. */ + return membuf_write(&to, vstate->datap, riscv_v_vsize); +} + +static int riscv_vr_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret, size; + struct __riscv_v_ext_state *vstate = &target->thread.vstate; + struct __riscv_v_regset_state ptrace_vstate; + + if (!riscv_v_vstate_query(task_pt_regs(target))) + return -EINVAL; + + /* Copy rest of the vstate except datap */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ptrace_vstate, 0, + sizeof(struct __riscv_v_regset_state)); + if (unlikely(ret)) + return ret; + + if (vstate->vlenb != ptrace_vstate.vlenb) + return -EINVAL; + + vstate->vstart = ptrace_vstate.vstart; + vstate->vl = ptrace_vstate.vl; + vstate->vtype = ptrace_vstate.vtype; + vstate->vcsr = ptrace_vstate.vcsr; + + /* Copy all the vector registers. */ + pos = 0; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate->datap, + 0, riscv_v_vsize); + return ret; +} +#endif + static const struct user_regset riscv_user_regset[] = { [REGSET_X] = { .core_note_type = NT_PRSTATUS, @@ -105,6 +173,17 @@ static const struct user_regset riscv_user_regset[] = { .set = riscv_fpr_set, }, #endif +#ifdef CONFIG_RISCV_ISA_V + [REGSET_V] = { + .core_note_type = NT_RISCV_VECTOR, + .align = 16, + .n = ((32 * RISCV_MAX_VLENB) + + sizeof(struct __riscv_v_regset_state)) / sizeof(__u32), + .size = sizeof(__u32), + .regset_get = riscv_vr_get, + .set = riscv_vr_set, + }, +#endif }; static const struct user_regset_view riscv_user_native_view = { diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index c7b056af9ef0ad..cc73f4371ecd5e 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -439,6 +439,8 @@ typedef struct elf64_shdr { #define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers */ #define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode */ #define NT_MIPS_MSA 0x802 /* MIPS SIMD registers */ +#define NT_RISCV_CSR 0x900 /* RISC-V Control and Status Registers */ +#define NT_RISCV_VECTOR 0x901 /* RISC-V vector registers */ #define NT_LOONGARCH_CPUCFG 0xa00 /* LoongArch CPU config registers */ #define NT_LOONGARCH_CSR 0xa01 /* LoongArch control and status registers */ #define NT_LOONGARCH_LSX 0xa02 /* LoongArch Loongson SIMD Extension registers */ From f2c3d1881dc6e4c0d7c730cf723515c6cead3534 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Thu, 7 Sep 2023 10:27:24 +0800 Subject: [PATCH 137/169] input: andes: touchscreen: Add the touch screen driver from the ast520. Signed-off-by: CL Wang --- drivers/input/touchscreen/cpe_ts_ae350.c | 340 +++++++++++++++++++++++ drivers/input/touchscreen/cpe_ts_ae350.h | 108 +++++++ 2 files changed, 448 insertions(+) create mode 100755 drivers/input/touchscreen/cpe_ts_ae350.c create mode 100755 drivers/input/touchscreen/cpe_ts_ae350.h diff --git a/drivers/input/touchscreen/cpe_ts_ae350.c b/drivers/input/touchscreen/cpe_ts_ae350.c new file mode 100755 index 00000000000000..565827a8e14d05 --- /dev/null +++ b/drivers/input/touchscreen/cpe_ts_ae350.c @@ -0,0 +1,340 @@ +#include +#include +#include +#include +#include +#include "cpe_ts_ae350.h" +#include +#include +#include +#include +#include +#include + +#define REG32(a) (*(volatile unsigned int *)(a)) +#define ads_dbg( enabled, tagged, ...) \ + do{ \ + if( enabled){ \ + if( tagged) \ + printk( "[ %30s() ] ", __func__); \ + printk( __VA_ARGS__); \ + } \ + } while( 0) + +#define TS_POLL_DELAY ( 1 * 1000000) /* ns delay before the first sample */ +#define TS_POLL_PERIOD ( delay * 1000000) /* ns delay between samples */ +static int debug = 0; +static int delay = 25; +module_param(debug, int, 0); +module_param(delay, int, 0); + +struct ads7846 +{ + void __iomem * regs; + struct input_dev *input; + char phys[32]; + struct hrtimer timer; + int irq; + spinlock_t lock; + bool disabled; +}; + +struct ts_event +{ + int x; + int y; + int z1, z2; + int Rt; +}; + +struct ads7846 touchscreen; + +#define ADS_START ( 0x1UL << 7) +#define ADS_A2A1A0_d_y ( 0x1UL << 4) /* differential */ +#define ADS_A2A1A0_d_z1 ( 0x3UL << 4) /* differential */ +#define ADS_A2A1A0_d_z2 ( 0x4UL << 4) /* differential */ +#define ADS_A2A1A0_d_x ( 0x5UL << 4) /* differential */ +#define ADS_12_BIT ( 0x0UL << 3) +#define ADS_SER ( 0x1UL << 2) /* non-differential */ +#define ADS_DFR ( 0x0UL << 2) /* differential */ +#define ADS_PD10_PDOWN ( 0x0UL << 0) /* lowpower mode + penirq */ +#define ADS_PD10_ADC_ON ( 0x1UL << 0) /* ADC on */ +#define ADS_PD10_REF_ON ( 0x2UL << 0) /* vREF on + penirq */ +#define ADS_PD10_ALL_ON ( 0x3UL << 0) /* ADC + vREF on */ +#define MAX_12BIT ( ( 0x1UL << 12) - 1) + +#define READ_X ( ADS_A2A1A0_d_x | ADS_12_BIT | ADS_DFR) +#define READ_Y ( ADS_A2A1A0_d_y | ADS_12_BIT | ADS_DFR) +#define READ_Z1 ( ADS_A2A1A0_d_z1 | ADS_12_BIT | ADS_DFR) +#define READ_Z2 ( ADS_A2A1A0_d_z2 | ADS_12_BIT | ADS_DFR) + +static int +read_val(struct ads7846 *ts, unsigned long cmd) +{ + unsigned long data = 0; + ads_dbg(0, 1, "Queuing data: 0x%08lx\n", cmd << 16); + + while (!(REG32(ts->regs + STATUS) & TX_EMPTY)); + REG32(ts->regs + DATA) = (ADS_START | cmd) << 16; + REG32(ts->regs + CMD) = 1; + + while ((REG32(ts->regs + STATUS) & RX_EMPTY)); + data = (REG32(ts->regs + DATA) >> 3) & 0xFFF; + ads_dbg(0, 1, "CMD <%02lx> data: 0x%08lx( %ld)\n", cmd, data, data); + + return data; +} + +static int pendown(struct ads7846 *ts) +{ + return read_val(ts, READ_Z1) > 40; +} + +static void report(struct ads7846 *ts, struct ts_event *e) +{ + e->x = read_val(ts, READ_X); + e->y = read_val(ts, READ_Y); + e->z1 = read_val(ts, READ_Z1); + e->z2 = read_val(ts, READ_Z2); + ads_dbg(debug, 1, "x: %4d, y: %4d, z1: %4d, z2: %4d\n", e->x, e->y, e->z1, e->z2); +} + +#define FILTER_LIMIT 35 +static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) +{ + struct ads7846 *ts = container_of(handle, struct ads7846, timer); + struct ts_event e; + static int xp = 0, yp = 0; + + if (ts->disabled) + return HRTIMER_NORESTART; + + if (!pendown(ts)) + { + ads_dbg(debug, 1, "Release\n"); + input_report_key(ts->input, BTN_TOUCH, 0); + input_report_abs(ts->input, ABS_PRESSURE, 0); + input_sync(ts->input); + enable_irq(ts->irq); + return HRTIMER_NORESTART; + } + report(ts, &e); +#ifdef CONFIG_TOUCHSCREEN_CPE_TS_DEJITTER_AE350 + if (abs(xp - e.x) > FILTER_LIMIT || abs(yp - e.y) > FILTER_LIMIT) + { +#endif + input_report_key(ts->input, BTN_TOUCH, 1); + input_report_abs(ts->input, ABS_X, e.x); + input_report_abs(ts->input, ABS_Y, e.y); + input_report_abs(ts->input, ABS_PRESSURE, 50); + xp = e.x; + yp = e.y; +#ifdef CONFIG_TOUCHSCREEN_CPE_TS_DEJITTER_AE350 + } +#endif + input_sync(ts->input); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), HRTIMER_MODE_REL); + ads_dbg(0, 1, "Leave\n"); + return HRTIMER_NORESTART; +} + + +static irqreturn_t ads7846_irq(int irq, void *handle) +{ + struct ads7846 *ts = handle; + + if (ts->disabled) + return IRQ_HANDLED; + + if (!pendown(ts)) + return IRQ_HANDLED; + + disable_irq_nosync(irq); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), HRTIMER_MODE_REL); + + return IRQ_HANDLED; +} + +static int xspi_init_hw(struct at_ts_platdata *pdata) +{ + int id = REG32(pdata->regs + ID_REV); + + if((id&ID_MSK) !=(ID_ATCSPI200<regs + TIMING) |= (pdata->clk/(pdata->sclk<<1))-1; + REG32(pdata->regs + FORMAT) &= DATA_LEN_MSK; + REG32(pdata->regs + FORMAT) |= (23<irq); + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + + return 0; +} + +#ifdef CONFIG_PM +static int ads7846_suspend( struct platform_device *pdev, pm_message_t message) +{ + struct ads7846 *ts = platform_get_drvdata(pdev); + + spin_lock_irq(&ts->lock); + ts->disabled = true; + disable_irq(ts->irq); + spin_unlock_irq(&ts->lock); + + return 0; +} + +static int ads7846_resume( struct platform_device *pdev) +{ + struct ads7846 *ts = platform_get_drvdata(pdev); + + spin_lock_irq(&ts->lock); + enable_irq(ts->irq); + ts->disabled = false; + spin_unlock_irq(&ts->lock); + + return 0; +} +#else +#define ads7846_suspend NULL +#define ads7846_resume NULL +#endif + +static const struct of_device_id ads7846_dt_ids[] = { + { + .compatible = "andestech,atcts", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ads7846_dt_ids); + +#ifdef CONFIG_OF +static struct at_ts_platdata *ts_parse_dt(struct device *dev) +{ + struct at_ts_platdata *pdata; + struct device_node *np = dev->of_node; + + if (!np) + return ERR_PTR(-ENOENT); + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "failed to allocate platform data\n"); + return ERR_PTR(-ENOMEM); + } + + if (of_property_read_u32(np, "spi-max-frequency", &pdata->sclk)){ + dev_err(dev, "failed to get spi-max-frequency property\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(np, "clock-frequency", &pdata->clk)){ + dev_err(dev, "failed to get clock-frequency property\n"); + return ERR_PTR(-EINVAL); + } + printk("pdata->sclk %d , pdata->clk %d\n",pdata->sclk,pdata->clk); + + if (of_property_read_u32(np, "x-size", &pdata->x_max)) { + dev_err(dev, "failed to get x-size property\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(np, "y-size", &pdata->y_max)) { + dev_err(dev, "failed to get y-size property\n"); + return ERR_PTR(-EINVAL); + } + + return pdata; +} +#else +static struct at_ts_platdata *ts_parse_dt(struct device *dev) +{ + return ERR_PTR(-EINVAL); +} +#endif + +static int ads7846_probe(struct platform_device *pdev) +{ + struct ads7846 *ts = &touchscreen; + struct input_dev *input_dev; + int err = 0; + struct resource *r; + struct at_ts_platdata *pdata; + + pdata = ts_parse_dt(&pdev->dev); + pdev->dev.platform_data = (void *)pdata; + platform_set_drvdata(pdev, ts); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ts->regs = devm_ioremap_resource(&pdev->dev, r); + pdata->regs = (resource_size_t)ts->regs; + + err = xspi_init_hw(pdata); + if (err) + goto err_unmap; + + input_dev = input_allocate_device(); + if (!input_dev) + { + err = -ENOMEM; + goto err_free_mem; + } + ts->input = input_dev; + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = ads7846_timer; + input_dev->name = "ADS7846 Touchscreen"; + input_dev->phys = ts->phys; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + ts->irq = platform_get_irq(pdev, 0); + + if (request_irq(ts->irq, ads7846_irq, IRQF_TRIGGER_RISING, "touch screen", ts)) + goto err_free_mem; + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + spin_lock_init(&ts->lock); + + return 0; + +err_free_irq: + free_irq(ts->irq, ts); +err_free_mem: + input_free_device(input_dev); +err_unmap: + iounmap(ts->regs); + + return err; +} + +static struct platform_driver ads7846_driver = +{ + .driver = { + .name = "ads7846", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(ads7846_dt_ids), + }, + .remove = __exit_p(ads7846_remove), + .suspend = ads7846_suspend, + .resume = ads7846_resume, +}; + +module_platform_driver_probe(ads7846_driver, ads7846_probe); +MODULE_DESCRIPTION("ADS7846 TouchScreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/cpe_ts_ae350.h b/drivers/input/touchscreen/cpe_ts_ae350.h new file mode 100755 index 00000000000000..5ca9910a0968f6 --- /dev/null +++ b/drivers/input/touchscreen/cpe_ts_ae350.h @@ -0,0 +1,108 @@ +#ifndef SPI_ANDES_H +#define SPI_ANDES_H + +#define XILINX_SPI_NAME "andes-spi" + +struct at_ts_platdata { + resource_size_t regs; + unsigned int clk; + unsigned int sclk; + unsigned int x_max; + unsigned int y_max; +}; + +/* ID and Revision Register */ +#define ID_REV 0x0 +#define ID_OFF 12 +#define ID_MSK (0xfffff<<12) + +#define ID_ATCSPI200 0x02002 + +/* SPI Transfer Format Register */ +#define FORMAT 0x10 +#define DATA_LEN_OFF 8 +#define DATA_LEN_MSK (0x1f<<8) + +/* SPI Transfer Control Register */ +#define TCONTROL 0x20 +#define TRANS_MODE_OFF 24 +#define TRANS_MODE_MSK (0xf< Date: Thu, 7 Sep 2023 10:34:35 +0800 Subject: [PATCH 138/169] input: andes: touchscreen: Refine the driver 1. Refine the driver to fit the coding style rule 2. Remove REG32 and use ioread32 instead. 3. Fix the wrong data type of regs from resource_size_t to "void __iomem *" PS: This driver just passed the compilation but has not been tested yet. Signed-off-by: CL Wang --- drivers/input/touchscreen/Kconfig | 17 +++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/cpe_ts_ae350.c | 150 ++++++++++++----------- drivers/input/touchscreen/cpe_ts_ae350.h | 11 +- 4 files changed, 106 insertions(+), 73 deletions(-) diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index dc90a3ea51eed1..f3cac22338c0d1 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -89,6 +89,23 @@ config TOUCHSCREEN_AD7879_SPI To compile this driver as a module, choose M here: the module will be called ad7879-spi. +config TOUCHSCREEN_CPE_TS_AE350 + tristate "Touchscreen Driver for AE350/XC7" + help + This driver directly accesses SPI controller to communicate with + the ads7846 chip. + + Once we have a SPI controller driver, we can adopt to the SPI + framework that kernel provides. + +config TOUCHSCREEN_CPE_TS_DEJITTER_AE350 + bool "Dejitter Detection" + depends on TOUCHSCREEN_CPE_TS_AE350 + help + Say Y here to enable dejitter detection in AE350/Big Orca Touchscreen Driver. + + If unsure, say y. + config TOUCHSCREEN_ADC tristate "Generic ADC based resistive touchscreen" depends on IIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 557f84fd20755f..2686b68a4a3205 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -116,3 +116,4 @@ obj-$(CONFIG_TOUCHSCREEN_ROHM_BU21023) += rohm_bu21023.o obj-$(CONFIG_TOUCHSCREEN_RASPBERRYPI_FW) += raspberrypi-ts.o obj-$(CONFIG_TOUCHSCREEN_IQS5XX) += iqs5xx.o obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o +obj-$(CONFIG_TOUCHSCREEN_CPE_TS_AE350) += cpe_ts_ae350.o diff --git a/drivers/input/touchscreen/cpe_ts_ae350.c b/drivers/input/touchscreen/cpe_ts_ae350.c index 565827a8e14d05..1812c5753991fe 100755 --- a/drivers/input/touchscreen/cpe_ts_ae350.c +++ b/drivers/input/touchscreen/cpe_ts_ae350.c @@ -1,36 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes technology Corporation + */ + #include #include #include #include -#include -#include "cpe_ts_ae350.h" #include #include #include #include #include #include +#include +#include "cpe_ts_ae350.h" -#define REG32(a) (*(volatile unsigned int *)(a)) -#define ads_dbg( enabled, tagged, ...) \ - do{ \ - if( enabled){ \ - if( tagged) \ - printk( "[ %30s() ] ", __func__); \ - printk( __VA_ARGS__); \ +#define ads_dbg(enabled, tagged, ...) \ + do { \ + if (enabled) { \ + if (tagged) \ + pr_err("[ %30s() ] ", __func__); \ + pr_err(__VA_ARGS__); \ } \ - } while( 0) + } while (0) + +#define TS_POLL_DELAY (1 * 1000000) /* ns delay before the first sample */ +#define TS_POLL_PERIOD (delay * 1000000) /* ns delay between samples */ +#define read_reg(addr) ioread32(addr) +#define write_reg(val, addr) iowrite32(val, addr) -#define TS_POLL_DELAY ( 1 * 1000000) /* ns delay before the first sample */ -#define TS_POLL_PERIOD ( delay * 1000000) /* ns delay between samples */ -static int debug = 0; +static int debug; static int delay = 25; module_param(debug, int, 0); module_param(delay, int, 0); -struct ads7846 -{ - void __iomem * regs; +struct ads7846 { + void __iomem *regs; struct input_dev *input; char phys[32]; struct hrtimer timer; @@ -39,8 +45,7 @@ struct ads7846 bool disabled; }; -struct ts_event -{ +struct ts_event { int x; int y; int z1, z2; @@ -49,37 +54,42 @@ struct ts_event struct ads7846 touchscreen; -#define ADS_START ( 0x1UL << 7) -#define ADS_A2A1A0_d_y ( 0x1UL << 4) /* differential */ -#define ADS_A2A1A0_d_z1 ( 0x3UL << 4) /* differential */ -#define ADS_A2A1A0_d_z2 ( 0x4UL << 4) /* differential */ -#define ADS_A2A1A0_d_x ( 0x5UL << 4) /* differential */ -#define ADS_12_BIT ( 0x0UL << 3) -#define ADS_SER ( 0x1UL << 2) /* non-differential */ -#define ADS_DFR ( 0x0UL << 2) /* differential */ -#define ADS_PD10_PDOWN ( 0x0UL << 0) /* lowpower mode + penirq */ -#define ADS_PD10_ADC_ON ( 0x1UL << 0) /* ADC on */ -#define ADS_PD10_REF_ON ( 0x2UL << 0) /* vREF on + penirq */ -#define ADS_PD10_ALL_ON ( 0x3UL << 0) /* ADC + vREF on */ -#define MAX_12BIT ( ( 0x1UL << 12) - 1) - -#define READ_X ( ADS_A2A1A0_d_x | ADS_12_BIT | ADS_DFR) -#define READ_Y ( ADS_A2A1A0_d_y | ADS_12_BIT | ADS_DFR) -#define READ_Z1 ( ADS_A2A1A0_d_z1 | ADS_12_BIT | ADS_DFR) -#define READ_Z2 ( ADS_A2A1A0_d_z2 | ADS_12_BIT | ADS_DFR) +#define ADS_START (0x1UL << 7) +#define ADS_A2A1A0_d_y (0x1UL << 4) /* differential */ +#define ADS_A2A1A0_d_z1 (0x3UL << 4) /* differential */ +#define ADS_A2A1A0_d_z2 (0x4UL << 4) /* differential */ +#define ADS_A2A1A0_d_x (0x5UL << 4) /* differential */ +#define ADS_12_BIT (0x0UL << 3) +#define ADS_SER (0x1UL << 2) /* non-differential */ +#define ADS_DFR (0x0UL << 2) /* differential */ +#define ADS_PD10_PDOWN (0x0UL << 0) /* lowpower mode + penirq */ +#define ADS_PD10_ADC_ON (0x1UL << 0) /* ADC on */ +#define ADS_PD10_REF_ON (0x2UL << 0) /* vREF on + penirq */ +#define ADS_PD10_ALL_ON (0x3UL << 0) /* ADC + vREF on */ +#define MAX_12BIT ((0x1UL << 12) - 1) + +#define READ_X (ADS_A2A1A0_d_x | ADS_12_BIT | ADS_DFR) +#define READ_Y (ADS_A2A1A0_d_y | ADS_12_BIT | ADS_DFR) +#define READ_Z1 (ADS_A2A1A0_d_z1 | ADS_12_BIT | ADS_DFR) +#define READ_Z2 (ADS_A2A1A0_d_z2 | ADS_12_BIT | ADS_DFR) static int read_val(struct ads7846 *ts, unsigned long cmd) { unsigned long data = 0; + ads_dbg(0, 1, "Queuing data: 0x%08lx\n", cmd << 16); - while (!(REG32(ts->regs + STATUS) & TX_EMPTY)); - REG32(ts->regs + DATA) = (ADS_START | cmd) << 16; - REG32(ts->regs + CMD) = 1; + while (!(read_reg(ts->regs + STATUS) & TX_EMPTY)) + ; + + write_reg((ADS_START | cmd) << 16, ts->regs + DATA); + write_reg(1, ts->regs + CMD); - while ((REG32(ts->regs + STATUS) & RX_EMPTY)); - data = (REG32(ts->regs + DATA) >> 3) & 0xFFF; + while ((read_reg(ts->regs + STATUS) & RX_EMPTY)) + ; + + data = (read_reg(ts->regs + DATA) >> 3) & 0xFFF; ads_dbg(0, 1, "CMD <%02lx> data: 0x%08lx( %ld)\n", cmd, data, data); return data; @@ -104,24 +114,22 @@ static enum hrtimer_restart ads7846_timer(struct hrtimer *handle) { struct ads7846 *ts = container_of(handle, struct ads7846, timer); struct ts_event e; - static int xp = 0, yp = 0; + static int xp, yp; if (ts->disabled) return HRTIMER_NORESTART; - if (!pendown(ts)) - { + if (!pendown(ts)) { ads_dbg(debug, 1, "Release\n"); input_report_key(ts->input, BTN_TOUCH, 0); input_report_abs(ts->input, ABS_PRESSURE, 0); input_sync(ts->input); enable_irq(ts->irq); - return HRTIMER_NORESTART; + return HRTIMER_NORESTART; } report(ts, &e); #ifdef CONFIG_TOUCHSCREEN_CPE_TS_DEJITTER_AE350 - if (abs(xp - e.x) > FILTER_LIMIT || abs(yp - e.y) > FILTER_LIMIT) - { + if (abs(xp - e.x) > FILTER_LIMIT || abs(yp - e.y) > FILTER_LIMIT) { #endif input_report_key(ts->input, BTN_TOUCH, 1); input_report_abs(ts->input, ABS_X, e.x); @@ -157,17 +165,24 @@ static irqreturn_t ads7846_irq(int irq, void *handle) static int xspi_init_hw(struct at_ts_platdata *pdata) { - int id = REG32(pdata->regs + ID_REV); + int id = read_reg(pdata->regs + ID_REV); + unsigned int reg_val; - if((id&ID_MSK) !=(ID_ATCSPI200<regs + TIMING) |= (pdata->clk/(pdata->sclk<<1))-1; - REG32(pdata->regs + FORMAT) &= DATA_LEN_MSK; - REG32(pdata->regs + FORMAT) |= (23<regs + TIMING); + write_reg(reg_val | ((pdata->clk / (pdata->sclk << 1)) - 1), + pdata->regs + TIMING); + + reg_val = read_reg(pdata->regs + FORMAT); + write_reg(reg_val & DATA_LEN_MSK, (pdata->regs + FORMAT)); + + reg_val = read_reg(pdata->regs + FORMAT); + write_reg(reg_val | (23 << DATA_LEN_OFF), (pdata->regs + FORMAT)); return 0; } @@ -184,7 +199,7 @@ static int __exit ads7846_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int ads7846_suspend( struct platform_device *pdev, pm_message_t message) +static int ads7846_suspend(struct platform_device *pdev, pm_message_t message) { struct ads7846 *ts = platform_get_drvdata(pdev); @@ -196,7 +211,7 @@ static int ads7846_suspend( struct platform_device *pdev, pm_message_t message) return 0; } -static int ads7846_resume( struct platform_device *pdev) +static int ads7846_resume(struct platform_device *pdev) { struct ads7846 *ts = platform_get_drvdata(pdev); @@ -207,9 +222,6 @@ static int ads7846_resume( struct platform_device *pdev) return 0; } -#else -#define ads7846_suspend NULL -#define ads7846_resume NULL #endif static const struct of_device_id ads7846_dt_ids[] = { @@ -218,7 +230,7 @@ static const struct of_device_id ads7846_dt_ids[] = { }, {}, }; -MODULE_DEVICE_TABLE(of, ads7846_dt_ids); +MODULE_DEVICE_TABLE(of, c_dt_ids); #ifdef CONFIG_OF static struct at_ts_platdata *ts_parse_dt(struct device *dev) @@ -230,21 +242,18 @@ static struct at_ts_platdata *ts_parse_dt(struct device *dev) return ERR_PTR(-ENOENT); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - dev_err(dev, "failed to allocate platform data\n"); + if (!pdata) return ERR_PTR(-ENOMEM); - } - if (of_property_read_u32(np, "spi-max-frequency", &pdata->sclk)){ + if (of_property_read_u32(np, "spi-max-frequency", &pdata->sclk)) { dev_err(dev, "failed to get spi-max-frequency property\n"); return ERR_PTR(-EINVAL); } - if (of_property_read_u32(np, "clock-frequency", &pdata->clk)){ + if (of_property_read_u32(np, "clock-frequency", &pdata->clk)) { dev_err(dev, "failed to get clock-frequency property\n"); return ERR_PTR(-EINVAL); } - printk("pdata->sclk %d , pdata->clk %d\n",pdata->sclk,pdata->clk); if (of_property_read_u32(np, "x-size", &pdata->x_max)) { dev_err(dev, "failed to get x-size property\n"); @@ -278,15 +287,14 @@ static int ads7846_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ts); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ts->regs = devm_ioremap_resource(&pdev->dev, r); - pdata->regs = (resource_size_t)ts->regs; + pdata->regs = ts->regs; err = xspi_init_hw(pdata); if (err) goto err_unmap; input_dev = input_allocate_device(); - if (!input_dev) - { + if (!input_dev) { err = -ENOMEM; goto err_free_mem; } @@ -323,16 +331,18 @@ static int ads7846_probe(struct platform_device *pdev) return err; } -static struct platform_driver ads7846_driver = -{ +static struct platform_driver ads7846_driver = { .driver = { .name = "ads7846", .owner = THIS_MODULE, .of_match_table = of_match_ptr(ads7846_dt_ids), }, .remove = __exit_p(ads7846_remove), + +#ifdef CONFIG_PM .suspend = ads7846_suspend, .resume = ads7846_resume, +#endif }; module_platform_driver_probe(ads7846_driver, ads7846_probe); diff --git a/drivers/input/touchscreen/cpe_ts_ae350.h b/drivers/input/touchscreen/cpe_ts_ae350.h index 5ca9910a0968f6..68e0fa407782dd 100755 --- a/drivers/input/touchscreen/cpe_ts_ae350.h +++ b/drivers/input/touchscreen/cpe_ts_ae350.h @@ -1,10 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Andes technology Corporation + */ + #ifndef SPI_ANDES_H #define SPI_ANDES_H #define XILINX_SPI_NAME "andes-spi" struct at_ts_platdata { - resource_size_t regs; + void __iomem *regs; unsigned int clk; unsigned int sclk; unsigned int x_max; @@ -24,7 +29,7 @@ struct at_ts_platdata { #define DATA_LEN_MSK (0x1f<<8) /* SPI Transfer Control Register */ -#define TCONTROL 0x20 +#define TCONTROL 0x20 #define TRANS_MODE_OFF 24 #define TRANS_MODE_MSK (0xf< Date: Mon, 16 Oct 2023 16:30:10 +0800 Subject: [PATCH 139/169] riscv: andes: mm: Synchronize memory attributes for all mm in free_initmem() on RV32 platform. 1. Symptom: [ 44.486537] Unable to handle kernel paging request at virtual address c0800000 [ 44.509980] Oops [#1] [ 44.516975] Modules linked in: [ 44.526260] CPU: 0 PID: 1 Comm: swapper Not tainted 6.1.27-05153-g45f6a9286550-dirty #19 [ 44.550422] Hardware name: andestech,a45 (DT) [ 44.563473] epc : __memset+0x58/0xf4 [ 44.574353] ra : free_reserved_area+0xb0/0x1a4 [ 44.588144] epc : c05d4ca0 ra : c011f32c sp : c2c61f00 [ 44.603536] gp : c28a57c8 tp : c2c98000 t0 : c0800000 [ 44.618916] t1 : 07901b48 t2 : 0000000f s0 : c2c61f50 [ 44.634308] s1 : 00000001 a0 : c0800000 a1 : cccccccc [ 44.649696] a2 : 00001000 a3 : c0801000 a4 : 00000000 [ 44.665085] a5 : 02000000 a6 : c0800fff a7 : 00000c08 [ 44.680467] s2 : 000000cc s3 : ffffffff s4 : 00000000 [ 44.695846] s5 : c28a66cc s6 : c1eba000 s7 : c2125820 [ 44.711225] s8 : c0800000 s9 : c212583c s10: c28a6648 [ 44.726623] s11: fe03c7c0 t3 : acf917bf t4 : e0000000 [ 44.742009] t5 : c2ca0011 t6 : c2ca0016 [ 44.753789] status: 00000120 badaddr: c0800000 cause: 0000000f [ 44.771234] [] __memset+0x58/0xf4 [ 44.783895] [] free_initmem+0x80/0x88 [ 44.797599] [] kernel_init+0x3c/0x124 [ 44.811391] [] ret_from_exception+0x0/0x16 2. To reproduce the problem: a. Use the RV32 toolchain to build the system. b. Build in the SPI module and mtdpart module in the kernel Example: Enable the following configuration - CONFIG_SPI - CONFIG_MTD and CONFIG_MTD_SPI_NOR c. Enable the "Make kernel text and rodata read-only" option by using the following kernel config. - CONFIG_STRICT_KERNEL_RWX 3. Root cause: This problem occurs when the virtual address of the kernel paging request is mapped to a megapage on the RV32 platform. During system startup, free_initmem() calls set_kernel_memory() to change the memory attributes of the init section from RO to RW. It then calls free_initmem_default() to set the memory to POISON_FREE_INITMEM. If the system runs modprobe at boot time, it will trigger a fork/exec to create a new mm for the new process. If the modprobe was called before free_initmem(), it will cause a kernel oops because the memory attributes of the current mm were not changed by set_kernel_memory(). This is because the set_kernel_memory() changes the memory attributes of init_mm, but the pgd(satp) currently in use is another process's mm and it's memory attribute doesn't change. Thus, it causes a kernel oops because the memory region has an un-writable attribute. 4. The solution. A similar problem occurred on ARM platforms and was fixed in 08925c2f12 (ARM: 8464/1: Update all mm structures with section adjustments). This patch uses a similar approach to fix the problem on RV32 by synchronizing the memory attributes of the init section for all mm Reviewed-on: https://gitea.andestech.com/RD-SW/linux/pulls/60 Signed-off-by: CL Wang --- arch/riscv/include/asm/set_memory.h | 12 +++++++++ arch/riscv/kernel/setup.c | 42 +++++++++++++++++++++++++---- arch/riscv/mm/pageattr.c | 42 +++++++++++++++++------------ 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/arch/riscv/include/asm/set_memory.h b/arch/riscv/include/asm/set_memory.h index a2c14d4b3993ea..f2cae956828e3f 100644 --- a/arch/riscv/include/asm/set_memory.h +++ b/arch/riscv/include/asm/set_memory.h @@ -16,6 +16,10 @@ int set_memory_rw(unsigned long addr, int numpages); int set_memory_x(unsigned long addr, int numpages); int set_memory_nx(unsigned long addr, int numpages); int set_memory_rw_nx(unsigned long addr, int numpages); + +#if defined(CONFIG_32BIT) && defined(CONFIG_STRICT_KERNEL_RWX) +int set_memory_rw_nx_by_mm(unsigned long addr, int numpages, struct mm_struct *mm); +#endif static __always_inline int set_kernel_memory(char *startp, char *endp, int (*set_memory)(unsigned long start, int num_pages)) @@ -32,6 +36,14 @@ static inline int set_memory_rw(unsigned long addr, int numpages) { return 0; } static inline int set_memory_x(unsigned long addr, int numpages) { return 0; } static inline int set_memory_nx(unsigned long addr, int numpages) { return 0; } static inline int set_memory_rw_nx(unsigned long addr, int numpages) { return 0; } + +#if defined(CONFIG_32BIT) && defined(CONFIG_STRICT_KERNEL_RWX) +static inline int set_memory_rw_nx_by_mm(unsigned long addr, + int numpages, struct mm_struct *mm) +{ + return 0; +} +#endif static inline int set_kernel_memory(char *startp, char *endp, int (*set_memory)(unsigned long start, int num_pages)) diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c index 5424d76315022b..ba7feae93e24c0 100644 --- a/arch/riscv/kernel/setup.c +++ b/arch/riscv/kernel/setup.c @@ -319,13 +319,45 @@ static int __init topology_init(void) } subsys_initcall(topology_init); -void free_initmem(void) +#if defined(CONFIG_32BIT) && defined(CONFIG_STRICT_KERNEL_RWX) +static void set_kernel_mm_early(char *startp, char *endp, + int (*set_memory)(unsigned long start, + int num_pages, struct mm_struct *mm)) { - if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)) { - set_kernel_memory(lm_alias(__init_begin), lm_alias(__init_end), set_memory_rw_nx); - if (IS_ENABLED(CONFIG_64BIT)) - set_kernel_memory(__init_begin, __init_end, set_memory_nx); + struct task_struct *t, *s; + unsigned long start = (unsigned long)startp; + unsigned long end = (unsigned long)endp; + int num_pages = PAGE_ALIGN(end - start) >> PAGE_SHIFT; + + set_memory(start, num_pages, current->active_mm); + if (current->active_mm != &init_mm) + set_memory(start, num_pages, &init_mm); + + read_lock(&tasklist_lock); + for_each_process(t) { + if (t->flags & PF_KTHREAD) + continue; + for_each_thread(t, s) { + if (s->mm) + set_memory(start, num_pages, s->mm); + } } + read_unlock(&tasklist_lock); +} +#endif + +void free_initmem(void) +{ +#ifdef CONFIG_STRICT_KERNEL_RWX +#ifdef CONFIG_32BIT + set_kernel_mm_early(lm_alias(__init_begin), lm_alias(__init_end), + set_memory_rw_nx_by_mm); +#else + set_kernel_memory(lm_alias(__init_begin), lm_alias(__init_end), set_memory_rw_nx); +#endif + if (IS_ENABLED(CONFIG_64BIT)) + set_kernel_memory(__init_begin, __init_end, set_memory_nx); +#endif free_initmem_default(POISON_FREE_INITMEM); } diff --git a/arch/riscv/mm/pageattr.c b/arch/riscv/mm/pageattr.c index 9587e44874152f..d06d0735c3977c 100644 --- a/arch/riscv/mm/pageattr.c +++ b/arch/riscv/mm/pageattr.c @@ -259,7 +259,7 @@ static int split_linear_mapping(unsigned long start, unsigned long end) #endif /* CONFIG_64BIT */ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, - pgprot_t clear_mask) + pgprot_t clear_mask, struct mm_struct *mm) { int ret; unsigned long start = addr; @@ -274,7 +274,7 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, if (!numpages) return 0; - mmap_write_lock(&init_mm); + mmap_write_lock(mm); #ifdef CONFIG_64BIT /* @@ -298,7 +298,7 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, if (ret) goto unlock; - ret = walk_page_range_novma(&init_mm, lm_start, lm_end, + ret = walk_page_range_novma(mm, lm_start, lm_end, &pageattr_ops, NULL, &masks); if (ret) goto unlock; @@ -316,17 +316,17 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, if (ret) goto unlock; - ret = walk_page_range_novma(&init_mm, lm_start, lm_end, + ret = walk_page_range_novma(mm, lm_start, lm_end, &pageattr_ops, NULL, &masks); if (ret) goto unlock; } - ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, + ret = walk_page_range_novma(mm, start, end, &pageattr_ops, NULL, &masks); unlock: - mmap_write_unlock(&init_mm); + mmap_write_unlock(mm); /* * We can't use flush_tlb_kernel_range() here as we may have split a @@ -334,10 +334,10 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, */ flush_tlb_all(); #else - ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, + ret = walk_page_range_novma(mm, start, end, &pageattr_ops, NULL, &masks); - mmap_write_unlock(&init_mm); + mmap_write_unlock(mm); flush_tlb_kernel_range(start, end); #endif @@ -345,44 +345,52 @@ static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, return ret; } +#if defined(CONFIG_32BIT) && defined(CONFIG_STRICT_KERNEL_RWX) +int set_memory_rw_nx_by_mm(unsigned long addr, int numpages, struct mm_struct *mm) +{ + return __set_memory(addr, numpages, __pgprot(_PAGE_READ | _PAGE_WRITE), + __pgprot(_PAGE_EXEC), mm); +} +#endif + int set_memory_rw_nx(unsigned long addr, int numpages) { return __set_memory(addr, numpages, __pgprot(_PAGE_READ | _PAGE_WRITE), - __pgprot(_PAGE_EXEC)); + __pgprot(_PAGE_EXEC), &init_mm); } int set_memory_ro(unsigned long addr, int numpages) { return __set_memory(addr, numpages, __pgprot(_PAGE_READ), - __pgprot(_PAGE_WRITE)); + __pgprot(_PAGE_WRITE), &init_mm); } int set_memory_rw(unsigned long addr, int numpages) { return __set_memory(addr, numpages, __pgprot(_PAGE_READ | _PAGE_WRITE), - __pgprot(0)); + __pgprot(0), &init_mm); } int set_memory_x(unsigned long addr, int numpages) { - return __set_memory(addr, numpages, __pgprot(_PAGE_EXEC), __pgprot(0)); + return __set_memory(addr, numpages, __pgprot(_PAGE_EXEC), __pgprot(0), &init_mm); } int set_memory_nx(unsigned long addr, int numpages) { - return __set_memory(addr, numpages, __pgprot(0), __pgprot(_PAGE_EXEC)); + return __set_memory(addr, numpages, __pgprot(0), __pgprot(_PAGE_EXEC), &init_mm); } int set_direct_map_invalid_noflush(struct page *page) { return __set_memory((unsigned long)page_address(page), 1, - __pgprot(0), __pgprot(_PAGE_PRESENT)); + __pgprot(0), __pgprot(_PAGE_PRESENT), &init_mm); } int set_direct_map_default_noflush(struct page *page) { return __set_memory((unsigned long)page_address(page), 1, - PAGE_KERNEL, __pgprot(_PAGE_EXEC)); + PAGE_KERNEL, __pgprot(_PAGE_EXEC), &init_mm); } #ifdef CONFIG_DEBUG_PAGEALLOC @@ -393,10 +401,10 @@ void __kernel_map_pages(struct page *page, int numpages, int enable) if (enable) __set_memory((unsigned long)page_address(page), numpages, - __pgprot(_PAGE_PRESENT), __pgprot(0)); + __pgprot(_PAGE_PRESENT), __pgprot(0), &init_mm); else __set_memory((unsigned long)page_address(page), numpages, - __pgprot(0), __pgprot(_PAGE_PRESENT)); + __pgprot(0), __pgprot(_PAGE_PRESENT), &init_mm); } #endif From 707eaeb5a77b84e23af4c96aa02b6a8273cdb06b Mon Sep 17 00:00:00 2001 From: Charles Ci-Jyun Wu Date: Thu, 19 Oct 2023 12:13:43 +0800 Subject: [PATCH 140/169] riscv: andes: defconfig: enable CONFIG_PREEMPT allows failed LTP timeout cases to pass the tests Board: A25MP_4C >> Pass A27L2 >> Failed - timeout AX27L2 >> Failed case : futex_cmp_requeue01 QEMU: rv32 >> Failed case : thp01, cve-2011-0999 rv64 >> Pass Signed-off-by: Charles Ci-Jyun Wu --- arch/riscv/configs/andes_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/andes_defconfig b/arch/riscv/configs/andes_defconfig index 8e32ed0d68167b..1adcc226b197ee 100644 --- a/arch/riscv/configs/andes_defconfig +++ b/arch/riscv/configs/andes_defconfig @@ -4,6 +4,7 @@ CONFIG_NO_HZ_IDLE=y CONFIG_HZ_100=y CONFIG_HIGH_RES_TIMERS=y CONFIG_BPF_SYSCALL=y +CONFIG_PREEMPT=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_CGROUPS=y From bf71979468ec947a5cc57614489fac8dbd75a714 Mon Sep 17 00:00:00 2001 From: Leo Yu-Chi Liang Date: Wed, 18 Oct 2023 18:09:49 +0800 Subject: [PATCH 141/169] riscv: andes: a25mp: Add preempt disable/enable pair to prevent migration when flushing cache It is possible that migration occurs when sync-ing kernel page table mappings (i.e., sync_kernel_mappings). "sync_kernel_mappings" does two things, copy the page table of kernel mapping and then flush the data using sfence.vma on 25 series bitmaps. If the migration occurs between these two phases, (e.g., the page table modifying core and the data-flushing core are not the same), the page table data modification might not be sync-ed completely to L2 and memory. This could cause the page table walker to use stale data and does the VA-PA translation incorrectly. Therefore, we add preemption enable/disable pair when altering page table and flushing the modified page table data to avoid this issue. Fixes: Bugzilla 28230 ("[ASTv530_Beta] LTP failed on A25MP_4C") Signed-off-by: Leo Yu-Chi Liang --- arch/riscv/include/asm/pgalloc.h | 8 ++++++++ arch/riscv/include/asm/pgtable-64.h | 2 ++ arch/riscv/include/asm/pgtable.h | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/arch/riscv/include/asm/pgalloc.h b/arch/riscv/include/asm/pgalloc.h index 3696c23a16dce1..c6809b787ef7bc 100644 --- a/arch/riscv/include/asm/pgalloc.h +++ b/arch/riscv/include/asm/pgalloc.h @@ -21,8 +21,10 @@ static inline void pmd_populate_kernel(struct mm_struct *mm, { unsigned long pfn = virt_to_pfn(pte); + preempt_disable(); set_pmd(pmd, __pmd((pfn << _PAGE_PFN_SHIFT) | _PAGE_TABLE)); ALT_LEGACY_MMU_FLUSH_TLB(); + preempt_enable(); } static inline void pmd_populate(struct mm_struct *mm, @@ -30,8 +32,10 @@ static inline void pmd_populate(struct mm_struct *mm, { unsigned long pfn = virt_to_pfn(page_address(pte)); + preempt_disable(); set_pmd(pmd, __pmd((pfn << _PAGE_PFN_SHIFT) | _PAGE_TABLE)); ALT_LEGACY_MMU_FLUSH_TLB(); + preempt_enable(); } #ifndef __PAGETABLE_PMD_FOLDED @@ -39,8 +43,10 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) { unsigned long pfn = virt_to_pfn(pmd); + preempt_disable(); set_pud(pud, __pud((pfn << _PAGE_PFN_SHIFT) | _PAGE_TABLE)); ALT_LEGACY_MMU_FLUSH_TLB(); + preempt_enable(); } static inline void p4d_populate(struct mm_struct *mm, p4d_t *p4d, pud_t *pud) @@ -144,10 +150,12 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) pgd = (pgd_t *)__get_free_page(GFP_KERNEL); if (likely(pgd != NULL)) { + preempt_disable(); memset(pgd, 0, USER_PTRS_PER_PGD * sizeof(pgd_t)); /* Copy kernel mappings */ sync_kernel_mappings(pgd); ALT_LEGACY_MMU_FLUSH_TLB(); + preempt_enable(); } return pgd; } diff --git a/arch/riscv/include/asm/pgtable-64.h b/arch/riscv/include/asm/pgtable-64.h index b794725b71c99b..33e729fe37752f 100644 --- a/arch/riscv/include/asm/pgtable-64.h +++ b/arch/riscv/include/asm/pgtable-64.h @@ -198,8 +198,10 @@ static inline int pud_user(pud_t pud) static inline void set_pud(pud_t *pudp, pud_t pud) { + preempt_disable(); *pudp = pud; ALT_LEGACY_MMU_FLUSH_TLB(); + preempt_enable(); } static inline void pud_clear(pud_t *pudp) diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 7b232d442b8f7e..8e660dbd94ff9c 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -231,8 +231,10 @@ static inline int pmd_leaf(pmd_t pmd) static inline void set_pmd(pmd_t *pmdp, pmd_t pmd) { + preempt_disable(); *pmdp = pmd; ALT_LEGACY_MMU_FLUSH_TLB(); + preempt_enable(); } static inline void pmd_clear(pmd_t *pmdp) @@ -498,8 +500,10 @@ static inline int pte_same(pte_t pte_a, pte_t pte_b) */ static inline void set_pte(pte_t *ptep, pte_t pteval) { + preempt_disable(); *ptep = pteval; ALT_LEGACY_MMU_FLUSH_TLB(); + preempt_enable(); } void flush_icache_pte(pte_t pte); From 197c9d056afc1533d0e60bdf3385c1dce2c968f6 Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Wed, 25 Oct 2023 16:51:03 +0800 Subject: [PATCH 142/169] andes: perf: Fix event encoding of 0x32 on 25 and 65-series This patch fixes the encoding of 0x32, which represents a different event on the 25 and 65 series CPU. - 25 series: Replay for load-after-store or store-after-store cases - 45 series: No such event - 65 series: Replay for load with memory-order violations or correctable ECC error Fixes: 260bcf5c6c47 ("andes: perf: add andes event to JSON file") Signed-off-by: Yu Chien Peter Lin --- tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json | 2 +- tools/perf/pmu-events/arch/riscv/andes/AX25/riscvstd.json | 2 +- tools/perf/pmu-events/arch/riscv/andes/AX65/riscvstd.json | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json index 71cf1fc3f7b21c..cafc84b0a78706 100644 --- a/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json +++ b/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json @@ -251,7 +251,7 @@ }, { "EventCode": "0x32", - "EventName": "replay_load_store", + "EventName": "replay_las_sas", "BriefDescription": "Replay for load-after-store or store-after-store cases" } ] diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX25/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/AX25/riscvstd.json index 71cf1fc3f7b21c..cafc84b0a78706 100644 --- a/tools/perf/pmu-events/arch/riscv/andes/AX25/riscvstd.json +++ b/tools/perf/pmu-events/arch/riscv/andes/AX25/riscvstd.json @@ -251,7 +251,7 @@ }, { "EventCode": "0x32", - "EventName": "replay_load_store", + "EventName": "replay_las_sas", "BriefDescription": "Replay for load-after-store or store-after-store cases" } ] diff --git a/tools/perf/pmu-events/arch/riscv/andes/AX65/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/AX65/riscvstd.json index 916712a2e123d1..fa2ac3df5a8365 100644 --- a/tools/perf/pmu-events/arch/riscv/andes/AX65/riscvstd.json +++ b/tools/perf/pmu-events/arch/riscv/andes/AX65/riscvstd.json @@ -256,7 +256,7 @@ }, { "EventCode": "0x32", - "EventName": "replay_las_sas", - "BriefDescription": "Replay for load-after-store or store-after-store cases" + "EventName": "replay_load_order_ecc_err", + "BriefDescription": "Replay for load with memory-order violations or correctable ECC error" } ] From 1d8636748bed3ec3130e836770d7ef15319729f6 Mon Sep 17 00:00:00 2001 From: Yu Chien Peter Lin Date: Wed, 1 Nov 2023 11:02:39 +0800 Subject: [PATCH 143/169] andes: perf: Add 27-series event encoding Andes 27-series CPUs follow the same event encoding with 25-series, we also remove A{25|45} folders as they can share with json files with AX{25|45}. Signed-off-by: Yu Chien Peter Lin --- .../arch/riscv/andes/A25/firmware.json | 74 ----- .../arch/riscv/andes/A25/riscvstd.json | 257 ----------------- .../arch/riscv/andes/A45/firmware.json | 74 ----- .../arch/riscv/andes/A45/riscvstd.json | 267 ------------------ tools/perf/pmu-events/arch/riscv/mapfile.csv | 12 +- 5 files changed, 7 insertions(+), 677 deletions(-) delete mode 100644 tools/perf/pmu-events/arch/riscv/andes/A25/firmware.json delete mode 100644 tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json delete mode 100644 tools/perf/pmu-events/arch/riscv/andes/A45/firmware.json delete mode 100644 tools/perf/pmu-events/arch/riscv/andes/A45/riscvstd.json diff --git a/tools/perf/pmu-events/arch/riscv/andes/A25/firmware.json b/tools/perf/pmu-events/arch/riscv/andes/A25/firmware.json deleted file mode 100644 index 61e42c1813fa33..00000000000000 --- a/tools/perf/pmu-events/arch/riscv/andes/A25/firmware.json +++ /dev/null @@ -1,74 +0,0 @@ -[ - { - "ArchStdEvent": "FW_MISALIGNED_LOAD" - }, - { - "ArchStdEvent": "FW_MISALIGNED_STORE" - }, - { - "ArchStdEvent": "FW_ACCESS_LOAD" - }, - { - "ArchStdEvent": "FW_ACCESS_STORE" - }, - { - "ArchStdEvent": "FW_ILLEGAL_INSN" - }, - { - "ArchStdEvent": "FW_SET_TIMER" - }, - { - "ArchStdEvent": "FW_IPI_SENT" - }, - { - "ArchStdEvent": "FW_IPI_RECEIVED" - }, - { - "ArchStdEvent": "FW_FENCE_I_SENT" - }, - { - "ArchStdEvent": "FW_FENCE_I_RECEIVED" - }, - { - "ArchStdEvent": "FW_SFENCE_VMA_SENT" - }, - { - "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" - }, - { - "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" - }, - { - "ArchStdEvent": "FW_SFENCE_VMA_ASID_RECEIVED" - }, - { - "ArchStdEvent": "FW_HFENCE_GVMA_SENT" - }, - { - "ArchStdEvent": "FW_HFENCE_GVMA_RECEIVED" - }, - { - "ArchStdEvent": "FW_HFENCE_GVMA_VMID_SENT" - }, - { - "ArchStdEvent": "FW_HFENCE_GVMA_VMID_RECEIVED" - }, - { - "ArchStdEvent": "FW_HFENCE_VVMA_SENT" - }, - { - "ArchStdEvent": "FW_HFENCE_VVMA_RECEIVED" - }, - { - "ArchStdEvent": "FW_HFENCE_VVMA_ASID_SENT" - }, - { - "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" - }, - { - "ArchStdEvent": "ANDES_L2C_ACCESSES" - }, - { - "ArchStdEvent": "ANDES_L2C_ACCESS_MISSES" - } -] diff --git a/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json deleted file mode 100644 index cafc84b0a78706..00000000000000 --- a/tools/perf/pmu-events/arch/riscv/andes/A25/riscvstd.json +++ /dev/null @@ -1,257 +0,0 @@ -[ - { - "EventCode": "0x10", - "EventName": "cycle_count", - "BriefDescription": "Cycle counts" - }, - { - "EventCode": "0x20", - "EventName": "inst_count", - "BriefDescription": "Retired instruction counts" - }, - { - "EventCode": "0x30", - "EventName": "int_load_inst", - "BriefDescription": "Integer load instructions" - }, - { - "EventCode": "0x40", - "EventName": "int_store_inst", - "BriefDescription": "Integer store instructions" - }, - { - "EventCode": "0x50", - "EventName": "atomic_mem_op", - "BriefDescription": "Atomic memory operation" - }, - { - "EventCode": "0x60", - "EventName": "sys_inst", - "BriefDescription": "System instructions" - }, - { - "EventCode": "0x70", - "EventName": "int_compute_inst", - "BriefDescription": "Integer computational instruction" - }, - { - "EventCode": "0x80", - "EventName": "condition_br", - "BriefDescription": "Conditional branch" - }, - { - "EventCode": "0x90", - "EventName": "taken_condition_br", - "BriefDescription": "Taken conditional branch" - }, - { - "EventCode": "0xA0", - "EventName": "jal_inst", - "BriefDescription": "JAL instruction" - }, - { - "EventCode": "0xB0", - "EventName": "jalr_inst", - "BriefDescription": "JALR instruction" - }, - { - "EventCode": "0xC0", - "EventName": "ret_inst", - "BriefDescription": "Return instruction" - }, - { - "EventCode": "0xD0", - "EventName": "control_trans_inst", - "BriefDescription": "Control transfer instruction" - }, - { - "EventCode": "0xE0", - "EventName": "ex9_inst", - "BriefDescription": "EX9 instruction" - }, - { - "EventCode": "0xF0", - "EventName": "int_mul_inst", - "BriefDescription": "Integer multiplication instruction" - }, - { - "EventCode": "0x100", - "EventName": "int_div_rem_inst", - "BriefDescription": "Integer division/remainder instruction" - }, - { - "EventCode": "0x110", - "EventName": "float_load_inst", - "BriefDescription": "Floating-point load instruction" - }, - { - "EventCode": "0x120", - "EventName": "float_store_inst", - "BriefDescription": "Floating-point store instruction" - }, - { - "EventCode": "0x130", - "EventName": "float_add_sub_inst", - "BriefDescription": "Floating-point addition/subtraction" - }, - { - "EventCode": "0x140", - "EventName": "float_mul_inst", - "BriefDescription": "Floating-point multiplication" - }, - { - "EventCode": "0x150", - "EventName": "float_fused_muladd_inst", - "BriefDescription": "Floating-point fused multiply-add" - }, - { - "EventCode": "0x160", - "EventName": "float_div_sqrt_inst", - "BriefDescription": "Floating-point division or square-root" - }, - { - "EventCode": "0x170", - "EventName": "other_float_inst", - "BriefDescription": "Other floating-point instruction" - }, - { - "EventCode": "0x180", - "EventName": "integer_mul_and_sub_inst_count", - "BriefDescription": "Integer multiplication and add/sub instruction count" - }, - { - "EventCode": "0x01", - "EventName": "ilm_access", - "BriefDescription": "ILM access" - }, - { - "EventCode": "0x11", - "EventName": "dlm_access", - "BriefDescription": "DLM access" - }, - { - "EventCode": "0x21", - "EventName": "icache_access", - "BriefDescription": "ICACHE access" - }, - { - "EventCode": "0x31", - "EventName": "icache_miss", - "BriefDescription": "ICACHE miss" - }, - { - "EventCode": "0x41", - "EventName": "dcache_access", - "BriefDescription": "DCACHE access" - }, - { - "EventCode": "0x51", - "EventName": "dcache_miss", - "BriefDescription": "DCACHE miss" - }, - { - "EventCode": "0x61", - "EventName": "dcache_load_access", - "BriefDescription": "DCACHE load access" - }, - { - "EventCode": "0x71", - "EventName": "dcache_load_miss", - "BriefDescription": "DCACHE load miss" - }, - { - "EventCode": "0x81", - "EventName": "dcache_store_access", - "BriefDescription": "DCACHE store access" - }, - { - "EventCode": "0x91", - "EventName": "dcache_store_miss", - "BriefDescription": "DCACHE store miss" - }, - { - "EventCode": "0xA1", - "EventName": "dcache_wb", - "BriefDescription": "DCACHE writeback" - }, - { - "EventCode": "0xB1", - "EventName": "cycle_wait_icache_fill", - "BriefDescription": "Cycles waiting for ICACHE fill data" - }, - { - "EventCode": "0xC1", - "EventName": "cycle_wait_dcache_fill", - "BriefDescription": "Cycles waiting for DCACHE fill data" - }, - { - "EventCode": "0xD1", - "EventName": "uncached_ifetch_from_bus", - "BriefDescription": "Uncached ifetch data access from bus" - }, - { - "EventCode": "0xE1", - "EventName": "uncached_load_from_bus", - "BriefDescription": "Uncached load data access from bus" - }, - { - "EventCode": "0xF1", - "EventName": "cycle_wait_uncached_ifetch", - "BriefDescription": "Cycles waiting for uncached ifetch data from bus" - }, - { - "EventCode": "0x101", - "EventName": "cycle_wait_uncached_load", - "BriefDescription": "Cycles waiting for uncached load data from bus" - }, - { - "EventCode": "0x111", - "EventName": "main_itlb_access", - "BriefDescription": "Main ITLB access" - }, - { - "EventCode": "0x121", - "EventName": "main_itlb_miss", - "BriefDescription": "Main ITLB miss" - }, - { - "EventCode": "0x131", - "EventName": "main_dtlb_access", - "BriefDescription": "Main DTLB access" - }, - { - "EventCode": "0x141", - "EventName": "main_dtlb_miss", - "BriefDescription": "Main DTLB miss" - }, - { - "EventCode": "0x151", - "EventName": "cycle_wait_itlb_fill", - "BriefDescription": "Cycles waiting for Main ITLB fill data" - }, - { - "EventCode": "0x161", - "EventName": "pipe_stall_cycle_dtlb_miss", - "BriefDescription": "Pipeline stall cycles caused by Main DTLB miss" - }, - { - "EventCode": "0x02", - "EventName": "mispredict_condition_br", - "BriefDescription": "Misprediction of conditional branches" - }, - { - "EventCode": "0x12", - "EventName": "mispredict_take_condition_br", - "BriefDescription": "Misprediction of taken conditional branches" - }, - { - "EventCode": "0x22", - "EventName": "mispredict_target_ret_inst", - "BriefDescription": "Misprediction of targets of Return instructions" - }, - { - "EventCode": "0x32", - "EventName": "replay_las_sas", - "BriefDescription": "Replay for load-after-store or store-after-store cases" - } -] diff --git a/tools/perf/pmu-events/arch/riscv/andes/A45/firmware.json b/tools/perf/pmu-events/arch/riscv/andes/A45/firmware.json deleted file mode 100644 index 61e42c1813fa33..00000000000000 --- a/tools/perf/pmu-events/arch/riscv/andes/A45/firmware.json +++ /dev/null @@ -1,74 +0,0 @@ -[ - { - "ArchStdEvent": "FW_MISALIGNED_LOAD" - }, - { - "ArchStdEvent": "FW_MISALIGNED_STORE" - }, - { - "ArchStdEvent": "FW_ACCESS_LOAD" - }, - { - "ArchStdEvent": "FW_ACCESS_STORE" - }, - { - "ArchStdEvent": "FW_ILLEGAL_INSN" - }, - { - "ArchStdEvent": "FW_SET_TIMER" - }, - { - "ArchStdEvent": "FW_IPI_SENT" - }, - { - "ArchStdEvent": "FW_IPI_RECEIVED" - }, - { - "ArchStdEvent": "FW_FENCE_I_SENT" - }, - { - "ArchStdEvent": "FW_FENCE_I_RECEIVED" - }, - { - "ArchStdEvent": "FW_SFENCE_VMA_SENT" - }, - { - "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" - }, - { - "ArchStdEvent": "FW_SFENCE_VMA_RECEIVED" - }, - { - "ArchStdEvent": "FW_SFENCE_VMA_ASID_RECEIVED" - }, - { - "ArchStdEvent": "FW_HFENCE_GVMA_SENT" - }, - { - "ArchStdEvent": "FW_HFENCE_GVMA_RECEIVED" - }, - { - "ArchStdEvent": "FW_HFENCE_GVMA_VMID_SENT" - }, - { - "ArchStdEvent": "FW_HFENCE_GVMA_VMID_RECEIVED" - }, - { - "ArchStdEvent": "FW_HFENCE_VVMA_SENT" - }, - { - "ArchStdEvent": "FW_HFENCE_VVMA_RECEIVED" - }, - { - "ArchStdEvent": "FW_HFENCE_VVMA_ASID_SENT" - }, - { - "ArchStdEvent": "FW_HFENCE_VVMA_ASID_RECEIVED" - }, - { - "ArchStdEvent": "ANDES_L2C_ACCESSES" - }, - { - "ArchStdEvent": "ANDES_L2C_ACCESS_MISSES" - } -] diff --git a/tools/perf/pmu-events/arch/riscv/andes/A45/riscvstd.json b/tools/perf/pmu-events/arch/riscv/andes/A45/riscvstd.json deleted file mode 100644 index def7e88cfc77ac..00000000000000 --- a/tools/perf/pmu-events/arch/riscv/andes/A45/riscvstd.json +++ /dev/null @@ -1,267 +0,0 @@ -[ - { - "EventCode": "0x10", - "EventName": "cycle_count", - "BriefDescription": "Cycle counts" - }, - { - "EventCode": "0x20", - "EventName": "inst_count", - "BriefDescription": "Retired instruction counts" - }, - { - "EventCode": "0x30", - "EventName": "int_load_inst", - "BriefDescription": "Integer load instructions" - }, - { - "EventCode": "0x40", - "EventName": "int_store_inst", - "BriefDescription": "Integer store instructions" - }, - { - "EventCode": "0x50", - "EventName": "atomic_mem_op", - "BriefDescription": "Atomic memory operation" - }, - { - "EventCode": "0x60", - "EventName": "sys_inst", - "BriefDescription": "System instructions" - }, - { - "EventCode": "0x70", - "EventName": "int_compute_inst", - "BriefDescription": "Integer computational instruction" - }, - { - "EventCode": "0x80", - "EventName": "condition_br", - "BriefDescription": "Conditional branch" - }, - { - "EventCode": "0x90", - "EventName": "taken_condition_br", - "BriefDescription": "Taken conditional branch" - }, - { - "EventCode": "0xA0", - "EventName": "jal_inst", - "BriefDescription": "JAL instruction" - }, - { - "EventCode": "0xB0", - "EventName": "jalr_inst", - "BriefDescription": "JALR instruction" - }, - { - "EventCode": "0xC0", - "EventName": "ret_inst", - "BriefDescription": "Return instruction" - }, - { - "EventCode": "0xD0", - "EventName": "control_trans_inst", - "BriefDescription": "Control transfer instruction" - }, - { - "EventCode": "0xE0", - "EventName": "ex9_inst", - "BriefDescription": "EX9 instruction" - }, - { - "EventCode": "0xF0", - "EventName": "int_mul_inst", - "BriefDescription": "Integer multiplication instruction" - }, - { - "EventCode": "0x100", - "EventName": "int_div_rem_inst", - "BriefDescription": "Integer division/remainder instruction" - }, - { - "EventCode": "0x110", - "EventName": "float_load_inst", - "BriefDescription": "Floating-point load instruction" - }, - { - "EventCode": "0x120", - "EventName": "float_store_inst", - "BriefDescription": "Floating-point store instruction" - }, - { - "EventCode": "0x130", - "EventName": "float_add_sub_inst", - "BriefDescription": "Floating-point addition/subtraction" - }, - { - "EventCode": "0x140", - "EventName": "float_mul_inst", - "BriefDescription": "Floating-point multiplication" - }, - { - "EventCode": "0x150", - "EventName": "float_fused_muladd_inst", - "BriefDescription": "Floating-point fused multiply-add" - }, - { - "EventCode": "0x160", - "EventName": "float_div_sqrt_inst", - "BriefDescription": "Floating-point division or square-root" - }, - { - "EventCode": "0x170", - "EventName": "other_float_inst", - "BriefDescription": "Other floating-point instruction" - }, - { - "EventCode": "0x180", - "EventName": "integer_mul_and_sub_inst_count", - "BriefDescription": "Integer multiplication and add/sub instruction count" - }, - { - "EventCode": "0x190", - "EventName": "op_count", - "BriefDescription": "Retired operation count" - }, - { - "EventCode": "0x01", - "EventName": "ilm_access", - "BriefDescription": "ILM access" - }, - { - "EventCode": "0x11", - "EventName": "dlm_access", - "BriefDescription": "DLM access" - }, - { - "EventCode": "0x21", - "EventName": "icache_access", - "BriefDescription": "ICACHE access" - }, - { - "EventCode": "0x31", - "EventName": "icache_miss", - "BriefDescription": "ICACHE miss" - }, - { - "EventCode": "0x41", - "EventName": "dcache_access", - "BriefDescription": "DCACHE access" - }, - { - "EventCode": "0x51", - "EventName": "dcache_miss", - "BriefDescription": "DCACHE miss" - }, - { - "EventCode": "0x61", - "EventName": "dcache_load_access", - "BriefDescription": "DCACHE load access" - }, - { - "EventCode": "0x71", - "EventName": "dcache_load_miss", - "BriefDescription": "DCACHE load miss" - }, - { - "EventCode": "0x81", - "EventName": "dcache_store_access", - "BriefDescription": "DCACHE store access" - }, - { - "EventCode": "0x91", - "EventName": "dcache_store_miss", - "BriefDescription": "DCACHE store miss" - }, - { - "EventCode": "0xA1", - "EventName": "dcache_wb", - "BriefDescription": "DCACHE writeback" - }, - { - "EventCode": "0xB1", - "EventName": "cycle_wait_icache_fill", - "BriefDescription": "Cycles waiting for ICACHE fill data" - }, - { - "EventCode": "0xC1", - "EventName": "cycle_wait_dcache_fill", - "BriefDescription": "Cycles waiting for DCACHE fill data" - }, - { - "EventCode": "0xD1", - "EventName": "uncached_ifetch_from_bus", - "BriefDescription": "Uncached ifetch data access from bus" - }, - { - "EventCode": "0xE1", - "EventName": "uncached_load_from_bus", - "BriefDescription": "Uncached load data access from bus" - }, - { - "EventCode": "0xF1", - "EventName": "cycle_wait_uncached_ifetch", - "BriefDescription": "Cycles waiting for uncached ifetch data from bus" - }, - { - "EventCode": "0x101", - "EventName": "cycle_wait_uncached_load", - "BriefDescription": "Cycles waiting for uncached load data from bus" - }, - { - "EventCode": "0x111", - "EventName": "main_itlb_access", - "BriefDescription": "Main ITLB access" - }, - { - "EventCode": "0x121", - "EventName": "main_itlb_miss", - "BriefDescription": "Main ITLB miss" - }, - { - "EventCode": "0x131", - "EventName": "main_dtlb_access", - "BriefDescription": "Main DTLB access" - }, - { - "EventCode": "0x141", - "EventName": "main_dtlb_miss", - "BriefDescription": "Main DTLB miss" - }, - { - "EventCode": "0x151", - "EventName": "cycle_wait_itlb_fill", - "BriefDescription": "Cycles waiting for Main ITLB fill data" - }, - { - "EventCode": "0x161", - "EventName": "pipe_stall_cycle_dtlb_miss", - "BriefDescription": "Pipeline stall cycles caused by Main DTLB miss" - }, - { - "EventCode": "0x171", - "EventName": "prefetch_bus_access", - "BriefDescription": "Hardware prefetch bus access" - }, - { - "EventCode": "0x181", - "EventName": "cycle_wait_for_operand", - "BriefDescription": "Cycles waiting for source operand ready in the integer register file scoreboard" - }, - { - "EventCode": "0x02", - "EventName": "mispredict_condition_br", - "BriefDescription": "Misprediction of conditional branches" - }, - { - "EventCode": "0x12", - "EventName": "mispredict_take_condition_br", - "BriefDescription": "Misprediction of taken conditional branches" - }, - { - "EventCode": "0x22", - "EventName": "mispredict_target_ret_inst", - "BriefDescription": "Misprediction of targets of Return instructions" - } -] diff --git a/tools/perf/pmu-events/arch/riscv/mapfile.csv b/tools/perf/pmu-events/arch/riscv/mapfile.csv index 631257eefbeb73..b986ce9a587804 100644 --- a/tools/perf/pmu-events/arch/riscv/mapfile.csv +++ b/tools/perf/pmu-events/arch/riscv/mapfile.csv @@ -15,8 +15,10 @@ # #MVENDORID-MARCHID-MIMPID,Version,Filename,EventType 0x489-0x8000000000000007-0x[[:xdigit:]]+,v1,sifive/u74,core -0x31e-0x8000000000008a45-0x[[:xdigit:]]+,V5,andes/AX45,core -0x31e-0x8000000000000a45-0x[[:xdigit:]]+,V5,andes/A45,core -0x31e-0x8000000000008a25-0x[[:xdigit:]]+,V5,andes/AX25,core -0x31e-0x8000000000000a25-0x[[:xdigit:]]+,V5,andes/A25,core -0x31e-0x8000000000008a65-0x[[:xdigit:]]+,V5,andes/AX65,core +0x31e-0x8000000000008a25-0x[[:xdigit:]]+,v1,andes/AX25,core +0x31e-0x8000000000000a25-0x[[:xdigit:]]+,v1,andes/AX25,core +0x31e-0x8000000000008a27-0x[[:xdigit:]]+,v1,andes/AX25,core +0x31e-0x8000000000000a27-0x[[:xdigit:]]+,v1,andes/AX25,core +0x31e-0x8000000000008a45-0x[[:xdigit:]]+,v1,andes/AX45,core +0x31e-0x8000000000000a45-0x[[:xdigit:]]+,v1,andes/AX45,core +0x31e-0x8000000000008a65-0x[[:xdigit:]]+,v1,andes/AX65,core From 76a9ba4f4d8feece8d5a86dd52252324fc25e590 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 7 Jun 2023 15:13:35 -0700 Subject: [PATCH 144/169] scripts/gdb: fix SB_* constants parsing --0000000000009a0c9905fd9173ad Content-Transfer-Encoding: 8bit After f15afbd34d8f ("fs: fix undefined behavior in bit shift for SB_NOUSER") the constants were changed from plain integers which LX_VALUE() can parse to constants using the BIT() macro which causes the following: Reading symbols from build/linux-custom/vmlinux...done. Traceback (most recent call last): File "/home/fainelli/work/buildroot/output/arm64/build/linux-custom/vmlinux-gdb.py", line 25, in import linux.constants File "/home/fainelli/work/buildroot/output/arm64/build/linux-custom/scripts/gdb/linux/constants.py", line 5 LX_SB_RDONLY = ((((1UL))) << (0)) Use LX_GDBPARSED() which does not suffer from that issue. f15afbd34d8f ("fs: fix undefined behavior in bit shift for SB_NOUSER") Link: https://lkml.kernel.org/r/20230607221337.2781730-1-florian.fainelli@broadcom.com Signed-off-by: Florian Fainelli Acked-by: Christian Brauner Cc: Hao Ge Cc: Jan Kiszka Cc: Kieran Bingham Cc: Luis Chamberlain Cc: Pankaj Raghav Signed-off-by: Andrew Morton --- scripts/gdb/linux/constants.py.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in index 08f0587d15ea1e..0ff707bc189601 100644 --- a/scripts/gdb/linux/constants.py.in +++ b/scripts/gdb/linux/constants.py.in @@ -46,12 +46,12 @@ if IS_BUILTIN(CONFIG_COMMON_CLK): LX_GDBPARSED(CLK_GET_RATE_NOCACHE) /* linux/fs.h */ -LX_VALUE(SB_RDONLY) -LX_VALUE(SB_SYNCHRONOUS) -LX_VALUE(SB_MANDLOCK) -LX_VALUE(SB_DIRSYNC) -LX_VALUE(SB_NOATIME) -LX_VALUE(SB_NODIRATIME) +LX_GDBPARSED(SB_RDONLY) +LX_GDBPARSED(SB_SYNCHRONOUS) +LX_GDBPARSED(SB_MANDLOCK) +LX_GDBPARSED(SB_DIRSYNC) +LX_GDBPARSED(SB_NOATIME) +LX_GDBPARSED(SB_NODIRATIME) /* linux/htimer.h */ LX_GDBPARSED(hrtimer_resolution) From 87617c0694d3b955cfd63e430092e93dd1e9436a Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 25 Jul 2023 16:44:52 +0800 Subject: [PATCH 145/169] kselftest: Increase timeout value for andes platform Signed-off-by: Hui Min Mina Chou --- tools/testing/selftests/filesystems/epoll/settings | 1 + tools/testing/selftests/livepatch/settings | 2 +- tools/testing/selftests/pidfd/settings | 1 + tools/testing/selftests/rseq/settings | 2 +- tools/testing/selftests/sync/settings | 1 + tools/testing/selftests/timers/settings | 2 +- tools/testing/selftests/zram/settings | 1 + 7 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 tools/testing/selftests/filesystems/epoll/settings create mode 100644 tools/testing/selftests/pidfd/settings create mode 100644 tools/testing/selftests/sync/settings create mode 100644 tools/testing/selftests/zram/settings diff --git a/tools/testing/selftests/filesystems/epoll/settings b/tools/testing/selftests/filesystems/epoll/settings new file mode 100644 index 00000000000000..6091b45d226baf --- /dev/null +++ b/tools/testing/selftests/filesystems/epoll/settings @@ -0,0 +1 @@ +timeout=120 diff --git a/tools/testing/selftests/livepatch/settings b/tools/testing/selftests/livepatch/settings index e7b9417537fbc4..694d70710ff08a 100644 --- a/tools/testing/selftests/livepatch/settings +++ b/tools/testing/selftests/livepatch/settings @@ -1 +1 @@ -timeout=0 +timeout=300 diff --git a/tools/testing/selftests/pidfd/settings b/tools/testing/selftests/pidfd/settings new file mode 100644 index 00000000000000..ed8418e8217a09 --- /dev/null +++ b/tools/testing/selftests/pidfd/settings @@ -0,0 +1 @@ +timeout=3600 diff --git a/tools/testing/selftests/rseq/settings b/tools/testing/selftests/rseq/settings index e7b9417537fbc4..ed8418e8217a09 100644 --- a/tools/testing/selftests/rseq/settings +++ b/tools/testing/selftests/rseq/settings @@ -1 +1 @@ -timeout=0 +timeout=3600 diff --git a/tools/testing/selftests/sync/settings b/tools/testing/selftests/sync/settings new file mode 100644 index 00000000000000..694d70710ff08a --- /dev/null +++ b/tools/testing/selftests/sync/settings @@ -0,0 +1 @@ +timeout=300 diff --git a/tools/testing/selftests/timers/settings b/tools/testing/selftests/timers/settings index e7b9417537fbc4..694d70710ff08a 100644 --- a/tools/testing/selftests/timers/settings +++ b/tools/testing/selftests/timers/settings @@ -1 +1 @@ -timeout=0 +timeout=300 diff --git a/tools/testing/selftests/zram/settings b/tools/testing/selftests/zram/settings new file mode 100644 index 00000000000000..694d70710ff08a --- /dev/null +++ b/tools/testing/selftests/zram/settings @@ -0,0 +1 @@ +timeout=300 From 51c0cd266f40df8b9223dc48bb58f5a0abfe32f6 Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 25 Jul 2023 16:47:19 +0800 Subject: [PATCH 146/169] selftests/exec: Change the command interpreter of the shell script to bash. This testcase will create a long pathname to execute. However, in "sh" Shell, it will fail to open the file due to the filename being too long. Therefore, we will change it to use "bash" to execute the script. execveat fail log: /bin/sh: can't open '/dev/fd/6/mnt/exec/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...': File name too long Check success of execveat(6, 'mnt/exec/xxxxxxxxxxx...yyyyyyyyyyyyyyyyyyy', 0)... [FAIL] (child 370 exited with 2 not 127 nor 126) 1 tests failed Signed-off-by: Hui Min Mina Chou --- tools/testing/selftests/exec/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/exec/Makefile b/tools/testing/selftests/exec/Makefile index a0b8688b083694..21add85f454c39 100644 --- a/tools/testing/selftests/exec/Makefile +++ b/tools/testing/selftests/exec/Makefile @@ -20,7 +20,7 @@ include ../lib.mk $(OUTPUT)/subdir: mkdir -p $@ $(OUTPUT)/script: - echo '#!/bin/sh' > $@ + echo '#!/bin/bash' > $@ echo 'exit $$*' >> $@ chmod +x $@ $(OUTPUT)/execveat.symlink: $(OUTPUT)/execveat From 628e0f4f4c611818cac5f59b23dc242929811d22 Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 25 Jul 2023 17:41:34 +0800 Subject: [PATCH 147/169] selftests/lkdtm: Remove the testcase that causes the system to hang. Signed-off-by: Hui Min Mina Chou --- tools/testing/selftests/lkdtm/tests.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tools/testing/selftests/lkdtm/tests.txt b/tools/testing/selftests/lkdtm/tests.txt index 2f3a1b96da6e38..7cf1142af46e1f 100644 --- a/tools/testing/selftests/lkdtm/tests.txt +++ b/tools/testing/selftests/lkdtm/tests.txt @@ -32,18 +32,11 @@ SLAB_FREE_PAGE #HARDLOCKUP Hangs the system #SPINLOCKUP Hangs the system #HUNG_TASK Hangs the system -EXEC_DATA -EXEC_STACK EXEC_KMALLOC -EXEC_VMALLOC -EXEC_RODATA EXEC_USERSPACE EXEC_NULL ACCESS_USERSPACE ACCESS_NULL -WRITE_RO -WRITE_RO_AFTER_INIT -WRITE_KERN WRITE_OPD REFCOUNT_INC_OVERFLOW REFCOUNT_ADD_OVERFLOW From 29b619f06dd7c1c5bd666b32d3d2fe2bd4f27185 Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 25 Jul 2023 17:47:27 +0800 Subject: [PATCH 148/169] selftests: Remove some unsupported commands by Busybox selftests/kselftest/runner.sh: Busybox's timeout command does not support "--foreground" option [Drop] cdd61a27fb0af kselftest/runner.sh: Propagate SIGTERM to runner child (Upstream commit 9616cb34b08ec) selftests/firmware: Busybox's diff command does not support "-Z" option selftests/lkdtm: Busybox's comm does not support "--nocheck-order" option selftests: Busybox's modprobe command does not support "-n" option selftests/filesystems: Busybox does not support sudo command and already login as root Signed-off-by: Hui Min Mina Chou --- .../filesystems/fat/run_fat_tests.sh | 22 +++++++++---------- .../selftests/firmware/fw_filesystem.sh | 4 ++-- tools/testing/selftests/ir/ir_loopback.sh | 5 ----- tools/testing/selftests/kselftest/module.sh | 7 ------ tools/testing/selftests/kselftest/runner.sh | 3 +-- tools/testing/selftests/lkdtm/run.sh | 2 +- tools/testing/selftests/net/rtnetlink.sh | 4 ---- .../selftests/static_keys/test_static_keys.sh | 10 --------- .../testing/selftests/user/test_user_copy.sh | 4 ---- 9 files changed, 15 insertions(+), 46 deletions(-) diff --git a/tools/testing/selftests/filesystems/fat/run_fat_tests.sh b/tools/testing/selftests/filesystems/fat/run_fat_tests.sh index 7f35dc3d15dfa6..13e496b9d42a83 100755 --- a/tools/testing/selftests/filesystems/fat/run_fat_tests.sh +++ b/tools/testing/selftests/filesystems/fat/run_fat_tests.sh @@ -35,7 +35,7 @@ create_loopback() mount_image() { mkdir -p "${MNT_PATH}" - sudo mount -o loop "${IMG_PATH}" "${MNT_PATH}" + mount -o loop "${IMG_PATH}" "${MNT_PATH}" } rename_exchange_test() @@ -44,10 +44,10 @@ rename_exchange_test() local old_path="${MNT_PATH}/old_file" local new_path="${MNT_PATH}/new_file" - echo old | sudo tee "${old_path}" >/dev/null 2>&1 - echo new | sudo tee "${new_path}" >/dev/null 2>&1 - sudo "${rename_exchange}" "${old_path}" "${new_path}" >/dev/null 2>&1 - sudo sync -f "${MNT_PATH}" + echo old | tee "${old_path}" >/dev/null 2>&1 + echo new | tee "${new_path}" >/dev/null 2>&1 + "${rename_exchange}" "${old_path}" "${new_path}" >/dev/null 2>&1 + sync -f "${MNT_PATH}" grep new "${old_path}" >/dev/null 2>&1 grep old "${new_path}" >/dev/null 2>&1 } @@ -59,18 +59,18 @@ rename_exchange_subdir_test() local old_path="${MNT_PATH}/old_file" local new_path="${dir_path}/new_file" - sudo mkdir -p "${dir_path}" - echo old | sudo tee "${old_path}" >/dev/null 2>&1 - echo new | sudo tee "${new_path}" >/dev/null 2>&1 - sudo "${rename_exchange}" "${old_path}" "${new_path}" >/dev/null 2>&1 - sudo sync -f "${MNT_PATH}" + mkdir -p "${dir_path}" + echo old | tee "${old_path}" >/dev/null 2>&1 + echo new | tee "${new_path}" >/dev/null 2>&1 + "${rename_exchange}" "${old_path}" "${new_path}" >/dev/null 2>&1 + sync -f "${MNT_PATH}" grep new "${old_path}" >/dev/null 2>&1 grep old "${new_path}" >/dev/null 2>&1 } unmount_image() { - sudo umount "${MNT_PATH}" &> /dev/null + umount "${MNT_PATH}" &> /dev/null } create_loopback diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh index 1a99aea0549ec0..972b62809bd5fc 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -223,7 +223,7 @@ read_firmwares() # -Z required for now -- check for yourself, md5sum # on $FW and DIR/read_firmware will yield the same. Even # cmp agrees, so something is off. - if ! diff -q -Z "$fwfile" $DIR/read_firmware 2>/dev/null ; then + if ! diff -q "$fwfile" $DIR/read_firmware 2>/dev/null ; then echo "request #$i: firmware was not loaded" >&2 exit 1 fi @@ -264,7 +264,7 @@ read_firmwares_expect_nofile() for i in $(seq 0 3); do config_set_read_fw_idx $i # Ensures contents differ - if diff -q -Z "$FW" $DIR/read_firmware 2>/dev/null ; then + if diff -q "$FW" $DIR/read_firmware 2>/dev/null ; then echo "request $i: file was not expected to match" >&2 exit 1 fi diff --git a/tools/testing/selftests/ir/ir_loopback.sh b/tools/testing/selftests/ir/ir_loopback.sh index aff9299c941671..74ba71b809bf09 100755 --- a/tools/testing/selftests/ir/ir_loopback.sh +++ b/tools/testing/selftests/ir/ir_loopback.sh @@ -9,11 +9,6 @@ if [ $UID != 0 ]; then exit $ksft_skip fi -if ! /sbin/modprobe -q -n rc-loopback; then - echo "ir_loopback: module rc-loopback is not found in /lib/modules/`uname -r` [SKIP]" - exit $ksft_skip -fi - /sbin/modprobe rc-loopback if [ $? -ne 0 ]; then exit diff --git a/tools/testing/selftests/kselftest/module.sh b/tools/testing/selftests/kselftest/module.sh index fb4733faff1281..14b01f8a05fc10 100755 --- a/tools/testing/selftests/kselftest/module.sh +++ b/tools/testing/selftests/kselftest/module.sh @@ -22,7 +22,6 @@ modprobe="/sbin/modprobe" main() { parse_args "$@" assert_root - assert_have_module run_module } @@ -47,12 +46,6 @@ assert_root() { fi } -assert_have_module() { - if ! $modprobe -q -n $module; then - skip "module $module is not found" - fi -} - run_module() { if $modprobe -q $module $args; then $modprobe -q -r $module diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh index 1333ab1eda708e..36f317bf75b94a 100644 --- a/tools/testing/selftests/kselftest/runner.sh +++ b/tools/testing/selftests/kselftest/runner.sh @@ -35,8 +35,7 @@ tap_timeout() { # Make sure tests will time out if utility is available. if [ -x /usr/bin/timeout ] ; then - /usr/bin/timeout --foreground "$kselftest_timeout" \ - /usr/bin/timeout "$kselftest_timeout" $1 + /usr/bin/timeout "$kselftest_timeout" $1 else $1 fi diff --git a/tools/testing/selftests/lkdtm/run.sh b/tools/testing/selftests/lkdtm/run.sh index 95e904959207b3..2d602c226677b0 100755 --- a/tools/testing/selftests/lkdtm/run.sh +++ b/tools/testing/selftests/lkdtm/run.sh @@ -94,7 +94,7 @@ for i in $(seq 1 $repeat); do done # Record and dump the results -dmesg | comm --nocheck-order -13 "$DMESG" - > "$LOG" || true +dmesg | comm -13 "$DMESG" - > "$LOG" || true cat "$LOG" # Check for expected output diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index cafd14b1ed2ab9..924ff49a81190c 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -520,10 +520,6 @@ kci_test_encap_fou() return $ksft_skip fi - if ! /sbin/modprobe -q -n fou; then - echo "SKIP: module fou is not found" - return $ksft_skip - fi /sbin/modprobe -q fou ip -netns "$testns" fou add port 7777 ipproto 47 2>/dev/null if [ $? -ne 0 ];then diff --git a/tools/testing/selftests/static_keys/test_static_keys.sh b/tools/testing/selftests/static_keys/test_static_keys.sh index fc9f8cde7d4223..543fdc0d68445f 100755 --- a/tools/testing/selftests/static_keys/test_static_keys.sh +++ b/tools/testing/selftests/static_keys/test_static_keys.sh @@ -5,16 +5,6 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 -if ! /sbin/modprobe -q -n test_static_key_base; then - echo "static_key: module test_static_key_base is not found [SKIP]" - exit $ksft_skip -fi - -if ! /sbin/modprobe -q -n test_static_keys; then - echo "static_key: module test_static_keys is not found [SKIP]" - exit $ksft_skip -fi - if /sbin/modprobe -q test_static_key_base; then if /sbin/modprobe -q test_static_keys; then echo "static_key: ok" diff --git a/tools/testing/selftests/user/test_user_copy.sh b/tools/testing/selftests/user/test_user_copy.sh index f9b31a57439b75..a88bd763eb5b97 100755 --- a/tools/testing/selftests/user/test_user_copy.sh +++ b/tools/testing/selftests/user/test_user_copy.sh @@ -5,10 +5,6 @@ # Kselftest framework requirement - SKIP code is 4. ksft_skip=4 -if ! /sbin/modprobe -q -n test_user_copy; then - echo "user: module test_user_copy is not found [SKIP]" - exit $ksft_skip -fi if /sbin/modprobe -q test_user_copy; then /sbin/modprobe -q -r test_user_copy echo "user_copy: ok" From caed6e5264831d9e3d09fad58d851a87f14554d8 Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 18 Jul 2023 11:00:04 +0800 Subject: [PATCH 149/169] selftests/filesystems: Add six consecutive 'x' characters to mktemp In busybox, the mktemp requires that the generated filename be suffixed with at least six consecutive 'X' characters. Otherwise, it will return an "Invalid argument" error. Signed-off-by: Hui Min Mina Chou --- tools/testing/selftests/filesystems/fat/run_fat_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/filesystems/fat/run_fat_tests.sh b/tools/testing/selftests/filesystems/fat/run_fat_tests.sh index 13e496b9d42a83..400e01a6015387 100755 --- a/tools/testing/selftests/filesystems/fat/run_fat_tests.sh +++ b/tools/testing/selftests/filesystems/fat/run_fat_tests.sh @@ -12,7 +12,7 @@ set -u set -o pipefail BASE_DIR="$(dirname $0)" -TMP_DIR="$(mktemp -d /tmp/fat_tests_tmp.XXXX)" +TMP_DIR="$(mktemp -d /tmp/fat_tests_tmp.XXXXXX)" IMG_PATH="${TMP_DIR}/fat.img" MNT_PATH="${TMP_DIR}/mnt" From 247cfb8076ab55871592781bc4c1495b158d2b5d Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 26 Sep 2023 16:12:00 +0800 Subject: [PATCH 150/169] selftest/zram: Specify block size as 4096 when mkfs on /dev/zram In busybox, the default block size for mkfs.ext2 is 1024. This causes an error when mounting zram as it does not meet the requirements. Therefore, when running mkfs on /dev/zram, specify a block size of 4096. # ./zram.sh ... make ext2 filesystem on /dev/zram1 zram mkfs.ext2: OK mount /dev/zram1 [517.321021] EXT4-fs (zram1): bad block size 1024 [517.323146] EXT4-fs (zram1): bad block size 1024 [517.325233] EXT2-fs (zram1): error: bad blocksize 1024 mount: mounting /dev/zram1 on zram1 failed: Invalid argument FAIL mount /dev/zram1 failed Signed-off-by: Hui Min Mina Chou --- tools/testing/selftests/zram/zram_lib.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/zram/zram_lib.sh b/tools/testing/selftests/zram/zram_lib.sh index 21ec1966de76ca..91f295d67be0c0 100755 --- a/tools/testing/selftests/zram/zram_lib.sh +++ b/tools/testing/selftests/zram/zram_lib.sh @@ -244,7 +244,7 @@ zram_makefs() which mkfs.$fs > /dev/null 2>&1 || fs=ext2 echo "make $fs filesystem on /dev/zram$i" - mkfs.$fs /dev/zram$i > err.log 2>&1 + mkfs.$fs /dev/zram$i -b 4096 > err.log 2>&1 if [ $? -ne 0 ]; then cat err.log echo "FAIL failed to make $fs on /dev/zram$i" From f5ce58b29a57ce53fa373ab7a8592da25b39f03c Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 26 Sep 2023 17:01:26 +0800 Subject: [PATCH 151/169] selftests/kselftest_harness.h: Increase default timeout to 300 seconds Signed-off-by: Hui Min Mina Chou --- tools/testing/selftests/kselftest_harness.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h index 584687c3286ddc..1500235a401135 100644 --- a/tools/testing/selftests/kselftest_harness.h +++ b/tools/testing/selftests/kselftest_harness.h @@ -68,7 +68,7 @@ #include "kselftest.h" -#define TEST_TIMEOUT_DEFAULT 30 +#define TEST_TIMEOUT_DEFAULT 300 /* Utilities exposed to the test definitions */ #ifndef TH_LOG_STREAM From 1f9f2ce563e5c934703b4ffa6510894f63c700c3 Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 5 Sep 2023 17:42:45 +0800 Subject: [PATCH 152/169] Makefile: Exclude these test cases when kselftest-merge merges config Excluded test cases: * Build failed bpf seccomp alsa dma dmabuf-heaps * Not executed by default kselftest ia64 kmod locking media_tests nolibc ntb perf_events prctl ptp rcutorture safesetid sched uevent user_events watchdog wireguar * Used some network utilities that BusyBox does not support drivers net * No virtual machine vm Signed-off-by: Hui Min Mina Chou --- Makefile | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bc4adb561a7cfc..8ffa7f84330937 100644 --- a/Makefile +++ b/Makefile @@ -1450,7 +1450,31 @@ kselftest-%: headers FORCE PHONY += kselftest-merge kselftest-merge: $(if $(wildcard $(objtree)/.config),, $(error No .config exists, config your kernel first!)) - $(Q)find $(srctree)/tools/testing/selftests -name config | \ + find $(srctree)/tools/testing/selftests -name config \ + -not -path '$(srctree)/tools/testing/selftests/bpf/*' \ + -not -path '$(srctree)/tools/testing/selftests/seccomp/*' \ + -not -path '$(srctree)/tools/testing/selftests/alsa/*' \ + -not -path '$(srctree)/tools/testing/selftests/dma/*' \ + -not -path '$(srctree)/tools/testing/selftests/dmabuf-heaps/*' \ + -not -path '$(srctree)/tools/testing/selftests/drivers/*' \ + -not -path '$(srctree)/tools/testing/selftests/ia64/*' \ + -not -path '$(srctree)/tools/testing/selftests/kmod/*' \ + -not -path '$(srctree)/tools/testing/selftests/locking/*' \ + -not -path '$(srctree)/tools/testing/selftests/media_tests/*' \ + -not -path '$(srctree)/tools/testing/selftests/net/*' \ + -not -path '$(srctree)/tools/testing/selftests/nolibc/*' \ + -not -path '$(srctree)/tools/testing/selftests/ntb/*' \ + -not -path '$(srctree)/tools/testing/selftests/perf_events/*' \ + -not -path '$(srctree)/tools/testing/selftests/prctl/*' \ + -not -path '$(srctree)/tools/testing/selftests/ptp/*' \ + -not -path '$(srctree)/tools/testing/selftests/rcutorture/*' \ + -not -path '$(srctree)/tools/testing/selftests/safesetid/*' \ + -not -path '$(srctree)/tools/testing/selftests/sched/*' \ + -not -path '$(srctree)/tools/testing/selftests/uevent/*' \ + -not -path '$(srctree)/tools/testing/selftests/user_events/*' \ + -not -path '$(srctree)/tools/testing/selftests/vm/*' \ + -not -path '$(srctree)/tools/testing/selftests/watchdog/*' \ + -not -path '$(srctree)/tools/testing/selftests/wireguar/*' | \ xargs $(srctree)/scripts/kconfig/merge_config.sh -m $(objtree)/.config $(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig From 9c4df0025c2e79304e2bafdf92b62b498bc16f72 Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 3 Oct 2023 14:36:31 +0800 Subject: [PATCH 153/169] selftests/ptrace: Remove the vmaccess test case. This testcase 'tools/testing/selftests/ptrace/vmaccess.c' still can not pass on the newest v6.4 kernel. Thus, remove this test case. Link: https://patchwork.kernel.org/project/linux-kselftest/patch/20221128070454.1850273-1-limin100@huawei.com/ Signed-off-by: Hui Min Mina Chou --- tools/testing/selftests/ptrace/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/ptrace/Makefile b/tools/testing/selftests/ptrace/Makefile index 2f1f532c39dbcc..6ee155ffb57fcb 100644 --- a/tools/testing/selftests/ptrace/Makefile +++ b/tools/testing/selftests/ptrace/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only CFLAGS += -std=c99 -pthread -iquote../../../../include/uapi -Wall -TEST_GEN_PROGS := get_syscall_info peeksiginfo vmaccess +TEST_GEN_PROGS := get_syscall_info peeksiginfo include ../lib.mk From f76b5bf1736f4346581649f120b0e6d06ff41c02 Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Wed, 4 Oct 2023 14:37:54 +0800 Subject: [PATCH 154/169] selftests/Makefile: Add some test to SKIP_TARGETS Skipped test case: * Used some network utilities that BusyBox does not support drivers/dma-buf drivers/s390x/uvdevice drivers/net/bonding drivers/net/team net net/af_unix net/forwarding net/mptcp net/openvswitch * Causes the system to hang lkdtm breakpoints firmware * Need to add reserved memory for ramoops in DTS pstore Signed-off-by: Hui Min Mina Chou --- tools/testing/selftests/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index aae64eb2ae737f..93736dbd2e3da8 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -96,6 +96,7 @@ TARGETS_HOTPLUG += memory-hotplug # BPF since it has cutting edge build time dependencies which require # more effort to install. SKIP_TARGETS ?= bpf +SKIP_TARGETS += breakpoints drivers/dma-buf drivers/s390x/uvdevice drivers/net/bonding drivers/net/team firmware net net/af_unix net/forwarding net/mptcp net/openvswitch lkdtm pstore ifneq ($(SKIP_TARGETS),) TMP := $(filter-out $(SKIP_TARGETS), $(TARGETS)) override TARGETS := $(TMP) From 290b3277e8924582968d58244b13e16f6adc32f5 Mon Sep 17 00:00:00 2001 From: Hui Min Mina Chou Date: Tue, 14 Nov 2023 10:29:12 +0800 Subject: [PATCH 155/169] selftests: Enable some required kernel config selftests/zram: Enable CONFIG_CRYPTO_LZO and CONFIG_SWAP config selftests/syscall_user_dispatch: Enable CONFIG_SYSCALL_USER_DISPATCH config selftests/kvm: Enable CONFIG_VIRTUALIZATION config selftests/fpu:Enable CONFIG_RUNTIME_TESTING_MEN and CONFIG_TEST_FPU config selftests/firmware: Enable CONFIG_RUNTIME_TESTING_MENU config selftests/ftrace: Enable CONFIG_FTRACE_SYSCALL and CONFIG_SYNTH_EVENTS config Signed-off-by: Hui Min Mina Chou --- tools/testing/selftests/firmware/config | 1 + tools/testing/selftests/fpu/config | 2 ++ tools/testing/selftests/ftrace/config | 2 ++ tools/testing/selftests/kvm/config | 1 + tools/testing/selftests/syscall_user_dispatch/config | 1 + tools/testing/selftests/zram/config | 2 ++ 6 files changed, 9 insertions(+) create mode 100644 tools/testing/selftests/fpu/config diff --git a/tools/testing/selftests/firmware/config b/tools/testing/selftests/firmware/config index 6e402519b11737..f2d5fbc2e054c0 100644 --- a/tools/testing/selftests/firmware/config +++ b/tools/testing/selftests/firmware/config @@ -1,3 +1,4 @@ +CONFIG_RUNTIME_TESTING_MENU=y CONFIG_TEST_FIRMWARE=y CONFIG_FW_LOADER=y CONFIG_FW_LOADER_USER_HELPER=y diff --git a/tools/testing/selftests/fpu/config b/tools/testing/selftests/fpu/config new file mode 100644 index 00000000000000..5622621a3324c1 --- /dev/null +++ b/tools/testing/selftests/fpu/config @@ -0,0 +1,2 @@ +CONFIG_RUNTIME_TESTING_MENU=y +CONFIG_TEST_FPU=m diff --git a/tools/testing/selftests/ftrace/config b/tools/testing/selftests/ftrace/config index e59d985eeff0c8..f3d6a9553c4b9d 100644 --- a/tools/testing/selftests/ftrace/config +++ b/tools/testing/selftests/ftrace/config @@ -14,3 +14,5 @@ CONFIG_SAMPLES=y CONFIG_SAMPLE_FTRACE_DIRECT=m CONFIG_SAMPLE_TRACE_PRINTK=m CONFIG_KALLSYMS_ALL=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_SYNTH_EVENTS=y diff --git a/tools/testing/selftests/kvm/config b/tools/testing/selftests/kvm/config index 63ed533f73d6e8..ea6be969e0fc84 100644 --- a/tools/testing/selftests/kvm/config +++ b/tools/testing/selftests/kvm/config @@ -1,3 +1,4 @@ +CONFIG_VIRTUALIZATION=y CONFIG_KVM=y CONFIG_KVM_INTEL=y CONFIG_KVM_AMD=y diff --git a/tools/testing/selftests/syscall_user_dispatch/config b/tools/testing/selftests/syscall_user_dispatch/config index 039e303e59d7dc..7f4ee436956db5 100644 --- a/tools/testing/selftests/syscall_user_dispatch/config +++ b/tools/testing/selftests/syscall_user_dispatch/config @@ -1 +1,2 @@ CONFIG_GENERIC_ENTRY=y +CONFIG_SYSCALL_USER_DISPATCH=y diff --git a/tools/testing/selftests/zram/config b/tools/testing/selftests/zram/config index e0cc47e2c7e22c..70e0c49bb43975 100644 --- a/tools/testing/selftests/zram/config +++ b/tools/testing/selftests/zram/config @@ -1,2 +1,4 @@ CONFIG_ZSMALLOC=y CONFIG_ZRAM=m +CONFIG_CRYPTO_LZO=y +CONFIG_SWAP=y From b8be07ce9bdb9c8805b3f0c3825cec823b83d8ff Mon Sep 17 00:00:00 2001 From: Ben Zong-You Xie Date: Wed, 1 Nov 2023 13:34:46 +0800 Subject: [PATCH 156/169] drivers: soc: andes: Add the "print_detailed_cause" method When multiple events cause a trap to be taken with the same scause value, we need to know the sdcause value that records the detailed cause. The "print_detailed_cause" method is utilised to print out the detailed cause when the trap occurs. Signed-off-by: Ben Zong-You Xie --- arch/riscv/kernel/process.c | 5 ++ drivers/soc/andes/Makefile | 1 + drivers/soc/andes/dcause.c | 66 +++++++++++++++++++++++ include/soc/andes/dcause.h | 103 ++++++++++++++++++++++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 drivers/soc/andes/dcause.c create mode 100644 include/soc/andes/dcause.h diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c index e7bceecba16c34..014cfd3bb8db55 100644 --- a/arch/riscv/kernel/process.c +++ b/arch/riscv/kernel/process.c @@ -26,6 +26,10 @@ #include #include +#ifdef CONFIG_PLAT_AE350 +#include +#endif + register unsigned long gp_in_global __asm__("gp"); #if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK) @@ -78,6 +82,7 @@ void __show_regs(struct pt_regs *regs) #ifdef CONFIG_PLAT_AE350 pr_cont("status: " REG_FMT " badaddr: " REG_FMT " cause: " REG_FMT " sdcause: " REG_FMT "\n", regs->status, regs->badaddr, regs->cause, regs->sdcause); + print_detailed_cause(regs->cause, regs->sdcause); #endif } void show_regs(struct pt_regs *regs) diff --git a/drivers/soc/andes/Makefile b/drivers/soc/andes/Makefile index 2e96ec5de77392..0acc7edf88166b 100644 --- a/drivers/soc/andes/Makefile +++ b/drivers/soc/andes/Makefile @@ -3,6 +3,7 @@ obj-y += andes_sbi.o pm.o obj-y += inject_init.o +obj-y += dcause.o obj-$(CONFIG_ATCDMAC300) += atcdmac300.o dmad_intc.o obj-$(CONFIG_ANDES_CACHE) += cache.o obj-$(CONFIG_ANDES_DMA_NONCOHERENT) += dma-noncoherent.o diff --git a/drivers/soc/andes/dcause.c b/drivers/soc/andes/dcause.c new file mode 100644 index 00000000000000..45b0e97d63e7eb --- /dev/null +++ b/drivers/soc/andes/dcause.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#include +#include +#include + +void print_detailed_cause(long scause, unsigned long sdcause) +{ + if (scause >= 0) { + switch (scause) { + case EXC_INST_ACCESS: + pr_info("The detailed trap cause: %s\n", + sdcause_fetch_access[sdcause]); + break; + case EXC_INST_ILLEGAL: + pr_info("The detailed trap cause: %s\n", + sdcause_illegal_instruction[sdcause]); + break; + case EXC_LOAD_ACCESS: + pr_info("The detailed trap cause: %s\n", + sdcause_load_access[sdcause]); + break; + case EXC_STORE_ACCESS: + pr_info("The detailed trap cause: %s\n", + sdcause_store_access[sdcause]); + break; + case EXC_INST_PAGE_FAULT: + pr_info("The detailed trap cause: %s\n", + sdcause_fetch_page_fault[sdcause]); + break; + case EXC_LOAD_PAGE_FAULT: + pr_info("The detailed trap cause: %s\n", + sdcause_load_page_fault[sdcause]); + break; + case EXC_STORE_PAGE_FAULT: + pr_info("The detailed trap cause: %s\n", + sdcause_store_page_fault[sdcause]); + break; + default: + pr_info("The detailed trap cause: %s\n", + "sdcause does not support this exception"); + } + } else { + scause &= ~CAUSE_IRQ_FLAG; + switch (scause) { + case IRQ_IMPRECISE_ECC: + pr_info("The detailed trap cause: %s\n", + sdcause_imprecise_ECC[sdcause]); + break; + case IRQ_BUS_RW_TRANS: + pr_info("The detailed trap cause: %s\n", + sdcause_bus_rw_transaction[sdcause]); + break; + case IRQ_PMOVI: + pr_info("The detailed trap cause: %s\n", + "Performance monitor overflow interrupt"); + break; + default: + pr_info("The detailed trap cause: %s\n", + "sdcause does not support this interrupt"); + } + } +} diff --git a/include/soc/andes/dcause.h b/include/soc/andes/dcause.h new file mode 100644 index 00000000000000..96017434230de4 --- /dev/null +++ b/include/soc/andes/dcause.h @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Andes Technology Corporation. + */ + +#ifndef _ASM_RISCV_DCAUSE_H +#define _ASM_RISCV_DCAUSE_H + +#define IRQ_IMPRECISE_ECC 0x110 +#define IRQ_BUS_RW_TRANS 0x111 +#define IRQ_PMOVI 0x112 + +static const char *const sdcause_fetch_access[] = { + "Reserved", + "ECC/Parity error", + "PMP/Smepmp instruction access violation", + "Bus error", + "PMA empty hole access", + "PMA attribute inconsistency", + "PTW access device region", + "Instruction cache multiple-hit", +}; + +static const char *const sdcause_illegal_instruction[] = { + "Please parse stval CSR", + "FP diabled exception", + "ACE diabled exception", + "RVV disabled exception", + "CCTL filllock/unlock not supported", + "Reserved CCTL command", + "CCTL TLB command not supported", + "Reserverd for CCTL", + "CCTL BTB command not supported", + "CCTL L1D_VA_WB* and L1*_VA_INVAL not supported", + "CCTL ALL type (L1D_*_ALL) not supported", + "CCTL L1D_IX_WB* and L1*_IX_INVAL not supported", + "Reserved XCSR", +}; + +static const char *const sdcause_load_access[] = { + "Reserved", + "ECC/Parity error", + "PMP/Smepmp load access violation", + "Bus error", + "Misaligned address", + "PMA empty hole access", + "PMA attribute inconsistency", + "PMA NAMO exception", + "PTW access device region", + "Data cache multiple-hit", +}; + +static const char *const sdcause_store_access[] = { + "Reserved", + "ECC/Parity error", + "PMP/Smepmp store access violation", + "Bus error", + "Misaligned address", + "PMA empty hole access", + "PMA attribute inconsistency", + "PMA NAMO exception", + "PTW access device region", + "Data cache multiple-hit", + "CBO.Zero hits device region", +}; + +static const char *const sdcause_fetch_page_fault[] = { + "Reserved", + "ITLB multiple-hit", +}; + +static const char *const sdcause_load_page_fault[] = { + "Reserved", + "DTLB multiple-hit", +}; + +static const char *const sdcause_store_page_fault[] = { + "Reserved", + "Smepmp violation", + "DTLB multiple-hit", + "ITLB multiple-hit", +}; + +static const char *const sdcause_imprecise_ECC[] = { + "Reserved", + "LM slave port ECC/Parity error", + "Imprecise store ECC/Parity error", + "Imprecise load ECC/Parity error", +}; + +static const char *const sdcause_bus_rw_transaction[] = { + "Reserved", + "Bus read error", + "Bus write error", + "Read PMP/Smepmp check error", + "Write PMP/Smepmp check error", + "Read PMA check error", + "Write PMA check error", +}; + +void print_detailed_cause(long scause, unsigned long sdcause); + +#endif /* _ASM_RISCV_DCAUSE_H */ From 8a6c6c9d8aad6b3fe37ec9ff4d19135d948953e7 Mon Sep 17 00:00:00 2001 From: Ben Zong-You Xie Date: Tue, 5 Dec 2023 14:23:25 +0800 Subject: [PATCH 157/169] drivers: soc: andes: Register the L2-cache IRQ Register the L2-cache IRQ. The IRQ will first clear the error status, and then print the error messages according to the asynchronous error register and the error register. Signed-off-by: Ben Zong-You Xie --- .../a27l2_c1_d_dsp_noncoherent_ae350.dts | 2 + .../boot/dts/andes/a45mp_c4_d_dsp_ae350.dts | 2 + .../dts/andes/ax25mp_amp_c2_64_d_ae350.dts | 2 + .../boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts | 2 + .../ax27l2_c1_d_dsp_noncoherent_ae350.dts | 2 + .../boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts | 2 + arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts | 2 + .../boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts | 2 + .../boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts | 2 + drivers/soc/andes/cache.c | 70 +++++++- include/soc/andes/csr.h | 158 ++++++++++-------- 11 files changed, 174 insertions(+), 72 deletions(-) diff --git a/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts index 9eeba6602e47e1..f26289632f4121 100644 --- a/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts +++ b/arch/riscv/boot/dts/andes/a27l2_c1_d_dsp_noncoherent_ae350.dts @@ -59,6 +59,8 @@ andes,data-prefetch = <0x03>; andes,tag-ram-ctl = <0x00 0x00>; andes,data-ram-ctl = <0x00 0x00>; + interrupts = <0x10 0x04>; + interrupt-parent = <0x03>; phandle = <0x01>; }; diff --git a/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts index 983fa08fa2b3b4..a987aa103fdd8e 100644 --- a/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/a45mp_c4_d_dsp_ae350.dts @@ -143,6 +143,8 @@ andes,data-prefetch = <0x03>; andes,tag-ram-ctl = <0x00 0x00>; andes,data-ram-ctl = <0x00 0x00>; + interrupts = <0x10 0x04>; + interrupt-parent = <0x06>; phandle = <0x01>; }; diff --git a/arch/riscv/boot/dts/andes/ax25mp_amp_c2_64_d_ae350.dts b/arch/riscv/boot/dts/andes/ax25mp_amp_c2_64_d_ae350.dts index 5d80968907dce3..ad28f289d49abf 100644 --- a/arch/riscv/boot/dts/andes/ax25mp_amp_c2_64_d_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax25mp_amp_c2_64_d_ae350.dts @@ -45,6 +45,8 @@ andes,data-prefetch = <0x3>; andes,tag-ram-ctl = <0x0 0x0>; andes,data-ram-ctl = <0x0 0x0>; + interrupts = <0x10 0x4>; + interrupt-parent = <0x4>; phandle = <0x1>; }; diff --git a/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts index f6e5753d0509f3..6fe19af94f0d7d 100644 --- a/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax25mp_c4_d_dsp_ae350.dts @@ -143,6 +143,8 @@ andes,data-prefetch = <0x03>; andes,tag-ram-ctl = <0x00 0x00>; andes,data-ram-ctl = <0x00 0x00>; + interrupts = <0x10 0x04>; + interrupt-parent = <0x06>; phandle = <0x01>; }; diff --git a/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts b/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts index 26041b04cd7f2f..8172d1c145589f 100644 --- a/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax27l2_c1_d_dsp_noncoherent_ae350.dts @@ -59,6 +59,8 @@ andes,data-prefetch = <0x03>; andes,tag-ram-ctl = <0x00 0x00>; andes,data-ram-ctl = <0x00 0x00>; + interrupts = <0x10 0x04>; + interrupt-parent = <0x03>; phandle = <0x01>; }; diff --git a/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts index 56e023d9496882..3be8a68201d57b 100644 --- a/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax45mp_c4_d_dsp_ae350.dts @@ -143,6 +143,8 @@ andes,data-prefetch = <0x03>; andes,tag-ram-ctl = <0x00 0x00>; andes,data-ram-ctl = <0x00 0x00>; + interrupts = <0x10 0x04>; + interrupt-parent = <0x06>; phandle = <0x01>; }; diff --git a/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts b/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts index 55fcfb9c052a70..93e33d2c5d5754 100644 --- a/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax45mp_c8_ae350.dts @@ -255,6 +255,8 @@ andes,data-prefetch = <0x03>; andes,tag-ram-ctl = <0x00 0x00>; andes,data-ram-ctl = <0x00 0x00>; + interrupts = <0x10 0x04>; + interrupt-parent = <0x0a>; phandle = <0x01>; }; diff --git a/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts index 92844924260c1d..9cb59abdb12499 100644 --- a/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax45mpv_c4_d_dsp_ae350.dts @@ -143,6 +143,8 @@ andes,data-prefetch = <0x03>; andes,tag-ram-ctl = <0x00 0x00>; andes,data-ram-ctl = <0x00 0x00>; + interrupts = <0x10 0x04>; + interrupt-parent = <0x06>; phandle = <0x01>; }; diff --git a/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts b/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts index 50617d61462ccf..30501236f294a6 100644 --- a/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts +++ b/arch/riscv/boot/dts/andes/ax65mp_c2_d_dsp_ae350.dts @@ -90,6 +90,8 @@ andes,data-prefetch = <0x03>; andes,tag-ram-ctl = <0x00 0x00>; andes,data-ram-ctl = <0x00 0x00>; + interrupts = <0x10 0x04>; + interrupt-parent = <0x04>; phandle = <0x01>; }; diff --git a/drivers/soc/andes/cache.c b/drivers/soc/andes/cache.c index 7d7772e3ecf30e..1b088d4f6b8357 100644 --- a/drivers/soc/andes/cache.c +++ b/drivers/soc/andes/cache.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -189,10 +190,65 @@ void cpu_dma_wb_range(void *info) } EXPORT_SYMBOL(cpu_dma_wb_range); +static uint32_t get_l2c_async_err(void) +{ + return readl((void *)(l2c_base + L2C_REG_ASYNC_ERR_OFFSET)); +} + +static uint32_t get_l2c_err(void) +{ + return readl((void *)(l2c_base + L2C_REG_ERR_OFFSET)); +} + +static void l2c_print_err(u32 async_err_reg, u32 err_reg) +{ + if (async_err_reg) { + u32 err_type = err_reg & L2C_ERR_TYPE_MASK; + bool more_err = err_reg & L2C_ERR_MORERR_MASK; + + if (more_err) + pr_err_ratelimited("More errors occur due to L2 cache. Below is the first error type\n"); + + switch (err_type) { + case L2C_RAM_ERROR: + pr_err_ratelimited("L2C RAM error: CCTL operation encounters uncorrectable RAM errors\n"); + break; + case L2C_RELEASE_ERROR: + pr_err_ratelimited("L2C release error: D-cache writes back a line that is not in L2-cache\n"); + break; + case L2C_PROBE_ERROR: + pr_err_ratelimited("L2C probe error: CCTL operation probes D-cache when D-cache coherency is disabled\n"); + break; + case L2C_BUS_ERROR: + pr_err_ratelimited("L2C bus error: CCTL operation or writing back a line to L3 has bus errors\n"); + break; + default: + pr_err_ratelimited("L2C unknown error\n"); + break; + } + } else { + pr_err_ratelimited("L2C synchronous error\n"); + } +} + +static irqreturn_t l2c_irq(int irq, void *dev_id) +{ + u32 async_err_reg = get_l2c_async_err(); + u32 err_reg = get_l2c_err(); + + /* Clear the error status */ + writel(0x0, l2c_base + L2C_REG_ASYNC_ERR_OFFSET); + writel(0x0, l2c_base + L2C_REG_ERR_OFFSET); + + l2c_print_err(async_err_reg, err_reg); + return IRQ_HANDLED; +} + int __init l2c_init(void) { struct device_node *node; - u32 l2c_cfg = 0; + u32 l2c_cfg = 0, irq; + int error; node = of_find_compatible_node(NULL, NULL, "cache"); @@ -211,6 +267,18 @@ int __init l2c_init(void) L2C_REG_STATUS_OFFSET = 0x1000; } + irq = irq_of_parse_and_map(node, 0); + if (irq <= 0) { + pr_err("Failed to get L2C irq number\n"); + return irq; + } + + error = request_irq(irq, l2c_irq, 0, "L2C", NULL); + if (error) { + pr_err("Failed to register L2C irq\n"); + return error; + } + return 0; } arch_initcall(l2c_init) diff --git a/include/soc/andes/csr.h b/include/soc/andes/csr.h index e86859befbdda7..c9dbf21ec952de 100644 --- a/include/soc/andes/csr.h +++ b/include/soc/andes/csr.h @@ -6,6 +6,7 @@ #define __SOC_ANDES_CSR_H #include + /* mdcm_cfg: Data Cache/Memory Configuration Register */ #define MDCM_CFG_DEST_OFFSET 0 #define MDCM_CFG_DWAY_OFFSET 3 @@ -17,92 +18,107 @@ #define MDCM_CFG_ULM_2BANK_OFFSET 20 #define MDCM_CFG_DLM_ECC_OFFSET 21 -#define MDCM_CFG_DEST_MASK (0x7 << MDCM_CFG_DEST_OFFSET) -#define MDCM_CFG_DWAY_MASK (0x7 << MDCM_CFG_DWAY_OFFSET) -#define MDCM_CFG_DSZ_MASK (0x7 << MDCM_CFG_DSZ_OFFSET) -#define MDCM_CFG_DLCK_MASK (0x1 << MDCM_CFG_DLCK_OFFSET) -#define MDCM_CFG_DC_ECC_MASK (0x3 << MDCM_CFG_DC_ECC_OFFSET) -#define MDCM_CFG_DLMB_MASK (0x7 << MDCM_CFG_DLMB_OFFSET) -#define MDCM_CFG_DLMSZ_MASK (0x1f << MDCM_CFG_DLMSZ_OFFSET) -#define MDCM_CFG_ULM_2BANK_MASK (0x1 << MDCM_CFG_ULM_2BANK_OFFSET) -#define MDCM_CFG_DLM_ECC_MASK (0x3 << MDCM_CFG_DLM_ECC_OFFSET) +#define MDCM_CFG_DEST_MASK (0x7 << MDCM_CFG_DEST_OFFSET) +#define MDCM_CFG_DWAY_MASK (0x7 << MDCM_CFG_DWAY_OFFSET) +#define MDCM_CFG_DSZ_MASK (0x7 << MDCM_CFG_DSZ_OFFSET) +#define MDCM_CFG_DLCK_MASK (0x1 << MDCM_CFG_DLCK_OFFSET) +#define MDCM_CFG_DC_ECC_MASK (0x3 << MDCM_CFG_DC_ECC_OFFSET) +#define MDCM_CFG_DLMB_MASK (0x7 << MDCM_CFG_DLMB_OFFSET) +#define MDCM_CFG_DLMSZ_MASK (0x1f << MDCM_CFG_DLMSZ_OFFSET) +#define MDCM_CFG_ULM_2BANK_MASK (0x1 << MDCM_CFG_ULM_2BANK_OFFSET) +#define MDCM_CFG_DLM_ECC_MASK (0x3 << MDCM_CFG_DLM_ECC_OFFSET) /* User mode control registers */ -#define CSR_UITB 0x800 -#define CSR_UCODE 0x801 -#define CSR_UDCAUSE 0x809 -#define CCTL_REG_UCCTLBEGINADDR_NUM 0x80b -#define CCTL_REG_UCCTLCOMMAND_NUM 0x80c -#define CSR_WFE 0x810 -#define CSR_SLEEPVALUE 0x811 -#define CSR_TXEVT 0x812 - -#define custom_csr_write(csr_num, val) csr_write(csr_num, val) +#define CSR_UITB 0x800 +#define CSR_UCODE 0x801 +#define CSR_UDCAUSE 0x809 +#define CCTL_REG_UCCTLBEGINADDR_NUM 0x80b +#define CCTL_REG_UCCTLCOMMAND_NUM 0x80c +#define CSR_WFE 0x810 +#define CSR_SLEEPVALUE 0x811 +#define CSR_TXEVT 0x812 + +#define custom_csr_write(csr_num, val) csr_write(csr_num, val) /* ucctlcommand */ /* D-cache operation */ -#define CCTL_L1D_VA_INVAL 0 -#define CCTL_L1D_VA_WB 1 -#define CCTL_L1D_VA_WBINVAL 2 +#define CCTL_L1D_VA_INVAL 0 +#define CCTL_L1D_VA_WB 1 +#define CCTL_L1D_VA_WBINVAL 2 /* non-blocking & write around */ -#define MMISC_CTL_NON_BLOCKING_ENABLE (0x1 << MMISC_CTL_NON_BLOCKING_OFFSET) -#define MMISC_CTL_NON_BLOCKING_OFFSET 0x8 +#define MMISC_CTL_NON_BLOCKING_ENABLE (0x1 << MMISC_CTL_NON_BLOCKING_OFFSET) +#define MMISC_CTL_NON_BLOCKING_OFFSET 0x8 #define MCACHE_CTL_L1I_PREFETCH_OFFSET 9 #define MCACHE_CTL_L1D_PREFETCH_OFFSET 10 -#define MCACHE_CTL_DC_WAROUND_OFFSET_1 13 -#define MCACHE_CTL_DC_WAROUND_OFFSET_2 14 -#define MCACHE_CTL_L1I_PREFETCH_EN (0x1 << MCACHE_CTL_L1I_PREFETCH_OFFSET) -#define MCACHE_CTL_L1D_PREFETCH_EN (0x1 << MCACHE_CTL_L1D_PREFETCH_OFFSET) -#define MCACHE_CTL_DC_WAROUND_1_EN (0x1 << MCACHE_CTL_DC_WAROUND_OFFSET_1) -#define MCACHE_CTL_DC_WAROUND_2_EN (0x1 << MCACHE_CTL_DC_WAROUND_OFFSET_2) -#define WRITE_AROUND_ENABLE (MCACHE_CTL_L1I_PREFETCH_EN | MCACHE_CTL_L1D_PREFETCH_EN | MCACHE_CTL_DC_WAROUND_1_EN) +#define MCACHE_CTL_DC_WAROUND_OFFSET_1 13 +#define MCACHE_CTL_DC_WAROUND_OFFSET_2 14 +#define MCACHE_CTL_L1I_PREFETCH_EN (0x1 << MCACHE_CTL_L1I_PREFETCH_OFFSET) +#define MCACHE_CTL_L1D_PREFETCH_EN (0x1 << MCACHE_CTL_L1D_PREFETCH_OFFSET) +#define MCACHE_CTL_DC_WAROUND_1_EN (0x1 << MCACHE_CTL_DC_WAROUND_OFFSET_1) +#define MCACHE_CTL_DC_WAROUND_2_EN (0x1 << MCACHE_CTL_DC_WAROUND_OFFSET_2) +#define WRITE_AROUND_ENABLE (MCACHE_CTL_L1I_PREFETCH_EN | \ + MCACHE_CTL_L1D_PREFETCH_EN | \ + MCACHE_CTL_DC_WAROUND_1_EN) /* L1 I-cache , D-cache */ -#define CACHE_CTL_offIC_EN 0 /* Enable I-cache */ -#define CACHE_CTL_offDC_EN 1 /* Enable D-cache */ -#define CACHE_CTL_mskIC_EN (0x1 << CACHE_CTL_offIC_EN) -#define CACHE_CTL_mskDC_EN (0x1 << CACHE_CTL_offDC_EN) +#define CACHE_CTL_offIC_EN 0 /* Enable I-cache */ +#define CACHE_CTL_offDC_EN 1 /* Enable D-cache */ +#define CACHE_CTL_mskIC_EN (0x1 << CACHE_CTL_offIC_EN) +#define CACHE_CTL_mskDC_EN (0x1 << CACHE_CTL_offDC_EN) /* L2 cache */ -#define L2_CACHE_CTL_mskCEN 1 +#define L2_CACHE_CTL_mskCEN 1 /* L2 cache registers */ -#define L2C_REG_CFG_OFFSET 0 -#define L2C_REG_CTL_OFFSET 0x8 -#define L2C_HPM_C0_CTL_OFFSET 0x10 -#define L2C_HPM_C1_CTL_OFFSET 0x18 -#define L2C_HPM_C2_CTL_OFFSET 0x20 -#define L2C_HPM_C3_CTL_OFFSET 0x28 -#define L2C_REG_C0_CMD_OFFSET 0x40 -#define L2C_REG_C0_ACC_OFFSET 0x48 -#define L2C_REG_C1_CMD_OFFSET 0x50 -#define L2C_REG_C1_ACC_OFFSET 0x58 -#define L2C_REG_C2_CMD_OFFSET 0x60 -#define L2C_REG_C2_ACC_OFFSET 0x68 -#define L2C_REG_C3_CMD_OFFSET 0x70 -#define L2C_REG_C3_ACC_OFFSET 0x78 +#define L2C_REG_CFG_OFFSET 0 +#define L2C_REG_CTL_OFFSET 0x8 +#define L2C_HPM_C0_CTL_OFFSET 0x10 +#define L2C_HPM_C1_CTL_OFFSET 0x18 +#define L2C_HPM_C2_CTL_OFFSET 0x20 +#define L2C_HPM_C3_CTL_OFFSET 0x28 +#define L2C_REG_ASYNC_ERR_OFFSET 0x30 +#define L2C_REG_ERR_OFFSET 0x38 +#define L2C_REG_C0_CMD_OFFSET 0x40 +#define L2C_REG_C0_ACC_OFFSET 0x48 +#define L2C_REG_C1_CMD_OFFSET 0x50 +#define L2C_REG_C1_ACC_OFFSET 0x58 +#define L2C_REG_C2_CMD_OFFSET 0x60 +#define L2C_REG_C2_ACC_OFFSET 0x68 +#define L2C_REG_C3_CMD_OFFSET 0x70 +#define L2C_REG_C3_ACC_OFFSET 0x78 #define L2C_REG_C0_STATUS_OFFSET 0x80 -#define L2C_REG_C0_HPM_OFFSET 0x200 +#define L2C_REG_C0_HPM_OFFSET 0x200 /* L2 cache configuration register */ -#define V5_L2C_CFG_MAP_OFFSET 20 -#define V5_L2C_CFG_MAP_MASK (1UL << V5_L2C_CFG_MAP_OFFSET) +#define V5_L2C_CFG_MAP_OFFSET 20 +#define V5_L2C_CFG_MAP_MASK (1UL << V5_L2C_CFG_MAP_OFFSET) + +/* L2 cache error register */ +#define L2C_ERR_TYPE_OFFSET 27 +#define L2C_ERR_TYPE_MASK (0x7 << L2C_ERR_TYPE_OFFSET) +#define L2C_ERR_MORERR_OFFSET 30 +#define L2C_ERR_MORERR_MASK (0x1 << L2C_ERR_MORERR_OFFSET) +/* L2 cache error type */ +#define L2C_RAM_ERROR 0 +#define L2C_RELEASE_ERROR 1 +#define L2C_PROBE_ERROR 2 +#define L2C_BUS_ERROR 4 /* L2 CCTL status */ -#define CCTL_L2_STATUS_IDLE 0 -#define CCTL_L2_STATUS_PROCESS 1 -#define CCTL_L2_STATUS_ILLEGAL 2 +#define CCTL_L2_STATUS_IDLE 0 +#define CCTL_L2_STATUS_PROCESS 1 +#define CCTL_L2_STATUS_ILLEGAL 2 /* L2 CCTL status cores mask */ -#define CCTL_L2_STATUS_C0_MASK 0xF -#define CCTL_L2_STATUS_C1_MASK 0xF0 -#define CCTL_L2_STATUS_C2_MASK 0xF00 -#define CCTL_L2_STATUS_C3_MASK 0xF000 +#define CCTL_L2_STATUS_C0_MASK 0xF +#define CCTL_L2_STATUS_C1_MASK 0xF0 +#define CCTL_L2_STATUS_C2_MASK 0xF00 +#define CCTL_L2_STATUS_C3_MASK 0xF000 /* L2 cache operation */ -#define CCTL_L2_PA_INVAL 0x8 -#define CCTL_L2_PA_WB 0x9 -#define CCTL_L2_PA_WBINVAL 0xA -#define CCTL_L2_WBINVAL_ALL 0x12 +#define CCTL_L2_PA_INVAL 0x8 +#define CCTL_L2_PA_WB 0x9 +#define CCTL_L2_PA_WBINVAL 0xA +#define CCTL_L2_WBINVAL_ALL 0x12 #define L2C_HPM_PER_CORE_OFFSET 0x8 #ifndef __ASSEMBLER__ @@ -124,12 +140,12 @@ extern u32 CCTL_L2_STATUS_PER_CORE_OFFSET; * Debug/Trace Registers (shared with Debug Mode). * Andes trigger module support ptrace single step. */ -#define CSR_SCONTEXT 0x7aa +#define CSR_SCONTEXT 0x7aa /* Supervisor trap registers */ -#define CSR_SLIE 0x9c4 -#define CSR_SLIP 0x9c5 -#define CSR_SDCAUSE 0x9c9 +#define CSR_SLIE 0x9c4 +#define CSR_SLIP 0x9c5 +#define CSR_SDCAUSE 0x9c9 /* Supervisor counter registers */ #define CSR_SCOUNTERINTEN 0x9cf @@ -147,8 +163,8 @@ extern u32 CCTL_L2_STATUS_PER_CORE_OFFSET; #define CSR_SCCTLDATA 0x9cd #define CSR_SMISC_CTL 0x9d0 -#define IRQ_HPM_OVF 18 -#define SLIP_PMOVI (_AC(0x1, UL) << IRQ_HPM_OVF) -#define IRQ_S_HPM 274 +#define IRQ_HPM_OVF 18 +#define SLIP_PMOVI (_AC(0x1, UL) << IRQ_HPM_OVF) +#define IRQ_S_HPM 274 #endif /* !__SOC_ANDES_CSR_H */ From 556ae28a5b7a099a10118c13f0e8ec5b96d4571c Mon Sep 17 00:00:00 2001 From: Ben Zong-You Xie Date: Tue, 26 Dec 2023 11:05:20 +0800 Subject: [PATCH 158/169] drivers: soc: andes: Fix an error for print_detailed_cause() on imprecise exception (#101) To print error messages for imprecise exception, extract the field sdcause in the CSR sdcause instead of reading the whole register. Signed-off-by: Ben Zong-You Xie Reviewed-on: https://gitea.andestech.com/RD-SW/linux/pulls/101 Reviewed-by: Tim Shih-Ting OuYang Reviewed-by: Leo Yu-Chi Liang Reviewed-by: Dylan Dai-Rong Jhong Co-authored-by: Ben Zong-You Xie Co-committed-by: Ben Zong-You Xie --- drivers/soc/andes/dcause.c | 1 + include/soc/andes/dcause.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/soc/andes/dcause.c b/drivers/soc/andes/dcause.c index 45b0e97d63e7eb..446b139f45beac 100644 --- a/drivers/soc/andes/dcause.c +++ b/drivers/soc/andes/dcause.c @@ -45,6 +45,7 @@ void print_detailed_cause(long scause, unsigned long sdcause) } } else { scause &= ~CAUSE_IRQ_FLAG; + sdcause &= SDCAUSE_DCAUSE_MASK; switch (scause) { case IRQ_IMPRECISE_ECC: pr_info("The detailed trap cause: %s\n", diff --git a/include/soc/andes/dcause.h b/include/soc/andes/dcause.h index 96017434230de4..53adae365bd3ba 100644 --- a/include/soc/andes/dcause.h +++ b/include/soc/andes/dcause.h @@ -10,6 +10,8 @@ #define IRQ_BUS_RW_TRANS 0x111 #define IRQ_PMOVI 0x112 +#define SDCAUSE_DCAUSE_MASK 0x1f + static const char *const sdcause_fetch_access[] = { "Reserved", "ECC/Parity error", From 3188aa7de981466cf36888c0c39b187673518330 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Wed, 3 Jan 2024 17:48:40 +0800 Subject: [PATCH 159/169] IIC: andes: atciic100: Pass slave device DTS settings to the I2C framework for slave device initialization. Signed-off-by: CL Wang --- drivers/i2c/busses/i2c-atciic100.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-atciic100.c b/drivers/i2c/busses/i2c-atciic100.c index b7780cf5457e98..fe1031479d1e52 100644 --- a/drivers/i2c/busses/i2c-atciic100.c +++ b/drivers/i2c/busses/i2c-atciic100.c @@ -466,6 +466,7 @@ static int atciic_probe(struct platform_device *pdev) padap->algo = &atciic_algorithm; padap->class = I2C_CLASS_HWMON; padap->dev.parent = &pdev->dev; + padap->dev.of_node = pdev->dev.of_node; padap->algo_data = iface; padap->timeout = 3 * HZ; padap->retries = 1; From 6aa2342ce806030debdc9ebaf935b92daf07304c Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 29 Aug 2023 21:39:19 -0700 Subject: [PATCH 160/169] RISC-V: Provide pgtable_l5_enabled on rv32 A few of the other page table level helpers are defined on rv32, but not pgtable_l5_enabled. This adds the definition as a constant and converts pgtable_l4_enabled to a constant as well. Link: https://lore.kernel.org/r/20230830044129.11481-2-palmer@rivosinc.com Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/pgtable-32.h | 3 +++ arch/riscv/include/asm/pgtable.h | 1 - arch/riscv/mm/init.c | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/riscv/include/asm/pgtable-32.h b/arch/riscv/include/asm/pgtable-32.h index 59ba1fbaf78493..00f3369570a836 100644 --- a/arch/riscv/include/asm/pgtable-32.h +++ b/arch/riscv/include/asm/pgtable-32.h @@ -33,4 +33,7 @@ _PAGE_WRITE | _PAGE_EXEC | \ _PAGE_USER | _PAGE_GLOBAL)) +static const __maybe_unused int pgtable_l4_enabled; +static const __maybe_unused int pgtable_l5_enabled; + #endif /* _ASM_RISCV_PGTABLE_32_H */ diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 8e660dbd94ff9c..9b106cc466a8bd 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h @@ -884,7 +884,6 @@ extern uintptr_t _dtb_early_pa; #define dtb_early_pa _dtb_early_pa #endif /* CONFIG_XIP_KERNEL */ extern u64 satp_mode; -extern bool pgtable_l4_enabled; void paging_init(void); void misc_mem_init(void); diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 2708580c59014a..092900f8c080ce 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -44,10 +44,12 @@ u64 satp_mode __ro_after_init = SATP_MODE_32; #endif EXPORT_SYMBOL(satp_mode); +#ifdef CONFIG_64BIT bool pgtable_l4_enabled = IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_XIP_KERNEL); bool pgtable_l5_enabled = IS_ENABLED(CONFIG_64BIT) && !IS_ENABLED(CONFIG_XIP_KERNEL); EXPORT_SYMBOL(pgtable_l4_enabled); EXPORT_SYMBOL(pgtable_l5_enabled); +#endif phys_addr_t phys_ram_base __ro_after_init; EXPORT_SYMBOL(phys_ram_base); From b8b84a3a4705a5e3fc48a9fa32266230ad6c432a Mon Sep 17 00:00:00 2001 From: charles Date: Thu, 25 Jan 2024 17:19:14 +0800 Subject: [PATCH 161/169] riscv: andes: add GPL to readl_fixup() EXPORT_SYMBOL The commit e9b5e1f857b655deaab662e11b68fe09aa9c428e : modules: only allow symbol_get of EXPORT_SYMBOL_GPL modules Signed-off-by: charles --- arch/riscv/kernel/riscv_ksyms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kernel/riscv_ksyms.c b/arch/riscv/kernel/riscv_ksyms.c index f9f69bc14a1bb3..b65468bab0d331 100644 --- a/arch/riscv/kernel/riscv_ksyms.c +++ b/arch/riscv/kernel/riscv_ksyms.c @@ -15,4 +15,4 @@ EXPORT_SYMBOL(memmove); EXPORT_SYMBOL(__memset); EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(__memmove); -EXPORT_SYMBOL(readl_fixup); +EXPORT_SYMBOL_GPL(readl_fixup); From 678baca7e5b1ffee03cecedba85ee7f3ef6b750f Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Thu, 23 Nov 2023 14:00:02 +0800 Subject: [PATCH 162/169] spi: andes: atcspi200: Register the spi interrupt Register the interrupt but currently do nothing. The reason for this is that the ads7846 probe checks whether the SPI interrupt is registered Signed-off-by: Locus Wei-Han Chen --- drivers/spi/spi-atcspi200.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-atcspi200.c b/drivers/spi/spi-atcspi200.c index 781f831ec3aea9..40b2a9368df6d9 100644 --- a/drivers/spi/spi-atcspi200.c +++ b/drivers/spi/spi-atcspi200.c @@ -387,6 +387,11 @@ static void atcspi200_set_qmode(struct atcspi200_spi *spi, const struct spi_mem_ atcspi200_spi_write(spi, SPI_CMD, op->cmd.opcode); } +static irqreturn_t andes_spi_irq(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + static int atcspi200_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) { int ret, max_trans_len, data_len, trans_len, num_blks, num_chunks; @@ -444,7 +449,7 @@ static int atcspi200_spi_probe(struct platform_device *pdev) struct resource *res; struct spi_master *master; struct atcspi200_spi *spi; - int ret; + int ret, irq; u32 num_cs = NSPI_MAX_CS_NUM; master = spi_alloc_master(&pdev->dev, sizeof(struct atcspi200_spi)); @@ -488,6 +493,12 @@ static int atcspi200_spi_probe(struct platform_device *pdev) goto put_master; } + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; + goto put_master; + } + /* Optional parameters */ ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency", &master->max_speed_hz); if (ret) { @@ -531,6 +542,15 @@ static int atcspi200_spi_probe(struct platform_device *pdev) atcspi200_spi_setup(spi); spi->mtiming = atcspi200_spi_read(spi, SPI_TIMING); + ret = devm_request_irq(&pdev->dev, irq, andes_spi_irq, 0, + "andes-spi", spi); + + if (ret) + dev_err(&pdev->dev, "Unable to bind to Interrupt\n"); + + dev_info(&pdev->dev, "mapped; irq=%d, cs=%d\n", + irq, master->num_chipselect); + ret = devm_spi_register_master(&pdev->dev, master); if (ret < 0) { dev_err(&pdev->dev, "spi_register_master failed\n"); From eff0449cbfb9ad82509a1e3aab76122634494717 Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Thu, 23 Nov 2023 14:28:06 +0800 Subject: [PATCH 163/169] spi: andes: atcspi200: spi support ads7846 touchscreen Extend the SPI driver to eliminate the need for the cpe_ts_ae350 driver. Use the upstream ads7846.c and the SPI framework to operate the touchscreen currently. 1. Can accurately obtain the values of the x-axis and y-axis. 2. Since different devices require distinct environmental configurations, during the probe stage, different initialization settings are applied based on the compatible string. Signed-off-by: Locus Wei-Han Chen --- drivers/spi/spi-atcspi200.c | 89 ++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi-atcspi200.c b/drivers/spi/spi-atcspi200.c index 40b2a9368df6d9..271a986ec80c84 100644 --- a/drivers/spi/spi-atcspi200.c +++ b/drivers/spi/spi-atcspi200.c @@ -23,6 +23,7 @@ #define SPI_XFER_BEGIN (1 << 0) #define SPI_XFER_END (1 << 1) +#define SPI_XFER_DATA (1 << 2) #define SPI_XFER_ONCE (SPI_XFER_BEGIN | SPI_XFER_END) #define SPI_XFER_SHIFT 0 @@ -61,6 +62,8 @@ /* SPI Transfer Format Register */ #define ATCSPI200_TRANSFMT_CPHA_MASK (1UL << 0) #define ATCSPI200_TRANSFMT_CPOL_MASK (1UL << 1) +#define ATCSPI200_TRANSFMT_DATA_LEN_OFFSET (8) +#define ATCSPI200_TRANSFMT_DATA_LEN_MASK (0x1F << ATCSPI200_TRANSFMT_DATA_LEN_OFFSET) /* SPI Status Register */ #define ATCSPI200_STATUS_TXEMPTY_OFFSET (1 << 22) @@ -88,6 +91,11 @@ #define SPI_INTR_ST 0x3C // SPI Interrupt Status Registe #define SPI_TIMING 0x40 // SPI Interface timing Register +struct ts_buf { + u8 cmd; + __be16 data; +} __packed; + struct atcspi200_spi { void __iomem *regs; struct clk *clk; @@ -98,6 +106,8 @@ struct atcspi200_spi { u8 cmd_buf[16]; u8 *din; u8 *dout; + struct ts_buf *tx_buf; + struct ts_buf *rx_buf; unsigned int addr; unsigned int max_transfer_length; unsigned int freq; @@ -126,7 +136,7 @@ static void atcspi200_polling_spiactive(struct atcspi200_spi *spi) } while (active & 1); } -static int atcspi200_spi_setup(struct atcspi200_spi *spi) +static int spi_nor_setup(struct atcspi200_spi *spi) { unsigned int format_val; u32 timing; @@ -168,6 +178,32 @@ static int atcspi200_spi_setup(struct atcspi200_spi *spi) return 0; } +static int spi_ts_setup(struct atcspi200_spi *spi) +{ + unsigned int reg_val; + + reg_val = atcspi200_spi_read(spi, SPI_TIMING); + reg_val = reg_val | ((spi->clk_rate / (spi->freq << 1)) - 1); + atcspi200_spi_write(spi, SPI_TIMING, reg_val); + + reg_val = atcspi200_spi_read(spi, SPI_TRANSFMT); + atcspi200_spi_write(spi, SPI_TRANSFMT, + (reg_val & ATCSPI200_TRANSFMT_DATA_LEN_MASK) | + (0x17 << ATCSPI200_TRANSFMT_DATA_LEN_OFFSET)); + + return 0; +} + +static int atcspi200_spi_setup(struct atcspi200_spi *spi, const char *node_name) +{ + if (!strcmp(node_name, "jedec,spi-nor")) + spi_nor_setup(spi); + else if (!strcmp(node_name, "ti,ads7846")) + spi_ts_setup(spi); + + return 0; +} + static int atcspi200_spi_stop(struct atcspi200_spi *spi) { atcspi200_spi_write(spi, SPI_TIMING, spi->mtiming); @@ -266,6 +302,26 @@ static int transfer_data(struct atcspi200_spi *spi, u8 *rx_buf, u8 *tx_buf, int return spi->data_len; } + +static void read_val(struct atcspi200_spi *spi, struct spi_transfer *t) +{ + int i, trans_len = 0, tc = 0; + + atcspi200_spi_write(spi, SPI_TRANSCTRL, tc); + for (i = 0; i < 5; i++) { + atcspi200_polling_spiactive(spi); + + while (!(atcspi200_spi_read(spi, SPI_STATUS) & + ATCSPI200_STATUS_TXEMPTY_OFFSET)); + atcspi200_spi_write(spi, SPI_DATA, spi->tx_buf[i].cmd << 16); + atcspi200_spi_write(spi, SPI_CMD, 1); + + while ((atcspi200_spi_read(spi, SPI_STATUS) & + ATCSPI200_STATUS_RXEMPTY_OFFSET)); + spi->rx_buf[i].data = cpu_to_be16(atcspi200_spi_read(spi, SPI_DATA)); + } +} + static int atcspi200_spi_transfer(struct spi_device *atcspi200_spi, struct spi_transfer *t, unsigned long flags) { @@ -280,6 +336,8 @@ static int atcspi200_spi_transfer(struct spi_device *atcspi200_spi, max_trans_len = spi->max_transfer_length; switch (flags) { case SPI_XFER_SHIFT: + if (!t->tx_buf) + return 0; memcpy(cmd_buf+spi->cmd_len, t->tx_buf, t->len); spi->cmd_len += t->len; return 0; @@ -306,6 +364,11 @@ static int atcspi200_spi_transfer(struct spi_device *atcspi200_spi, spi->trans_len = 1; atcspi200_spi_start(spi, t); break; + case SPI_XFER_DATA: + spi->rx_buf = (struct ts_buf *)t->rx_buf; + spi->tx_buf = (struct ts_buf *)t->tx_buf; + read_val(spi, t); + return 0; } num_chunks = DIV_ROUND_UP(data_len, max_trans_len); while (num_chunks--) { @@ -339,16 +402,20 @@ static int atcspi200_spi_transfer_one_message(struct spi_master *master, struct spi_flags = SPI_XFER_BEGIN; list_for_each_entry(t, &m->transfers, transfer_list) { - if (list_is_first(&t->transfer_list, &m->transfers) && !(t->tx_buf)) { + if (list_is_first(&t->transfer_list, &m->transfers) && + !(t->tx_buf) && !(t->rx_buf)) { dev_dbg(&master->dev, "missing tx buf\n"); return -EINVAL; } if (!t->tx_buf && !t->rx_buf) spi_flags |= SPI_XFER_ONCE; - if (list_is_last(&t->transfer_list, &m->transfers)) - spi_flags |= SPI_XFER_END; - + if (list_is_last(&t->transfer_list, &m->transfers)) { + if (t->tx_buf && t->rx_buf) + spi_flags = SPI_XFER_DATA; + else + spi_flags |= SPI_XFER_END; + } spin_lock_irqsave(&spi->lock, flags); ret = atcspi200_spi_transfer(m->spi, t, spi_flags); spin_unlock_irqrestore(&spi->lock, flags); @@ -451,6 +518,8 @@ static int atcspi200_spi_probe(struct platform_device *pdev) struct atcspi200_spi *spi; int ret, irq; u32 num_cs = NSPI_MAX_CS_NUM; + struct device_node *child = of_get_next_child(pdev->dev.of_node, NULL); + const char *subnode; master = spi_alloc_master(&pdev->dev, sizeof(struct atcspi200_spi)); if (!master) { @@ -501,6 +570,7 @@ static int atcspi200_spi_probe(struct platform_device *pdev) /* Optional parameters */ ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency", &master->max_speed_hz); + spi->freq = master->max_speed_hz; if (ret) { master->max_speed_hz = SPI_MAX_HZ; /* 50MHz */ spi->freq = SPI_MAX_HZ; @@ -518,6 +588,7 @@ static int atcspi200_spi_probe(struct platform_device *pdev) ret = -EINVAL; goto put_master; } + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &spi->clk_rate); /* probe the number of CS lines */ ret = of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs); if (ret) { @@ -539,7 +610,13 @@ static int atcspi200_spi_probe(struct platform_device *pdev) master->mem_ops = &atcspi200_mem_ops; /* Configure the SPI master hardware */ - atcspi200_spi_setup(spi); + ret = of_property_read_string(child, "compatible", &subnode); + if (ret) { + dev_err(&pdev->dev, "could not find compatible string\n"); + ret = -ENXIO; + goto put_master; + } + atcspi200_spi_setup(spi, subnode); spi->mtiming = atcspi200_spi_read(spi, SPI_TIMING); ret = devm_request_irq(&pdev->dev, irq, andes_spi_irq, 0, From 9f08921ce30bc8ab8543bba8c740f8b6bfc50f24 Mon Sep 17 00:00:00 2001 From: Locus Wei-Han Chen Date: Tue, 26 Dec 2023 15:54:07 +0800 Subject: [PATCH 164/169] spi: andes: atcspi200: supports quad read write mode 1. Since the SFDP table doesn't define quad page program, a fixup hook has been added to enable its support. 2. Currently, the ATCSPI200 supports regular mode read and write, as well as quad read and write. Additionally, it utilizes the exec_op method to replace the one-transfer approach. Signed-off-by: Locus Wei-Han Chen --- drivers/mtd/spi-nor/macronix.c | 15 +++- drivers/spi/spi-atcspi200.c | 124 ++++++++++++++++++++++----------- 2 files changed, 97 insertions(+), 42 deletions(-) diff --git a/drivers/mtd/spi-nor/macronix.c b/drivers/mtd/spi-nor/macronix.c index b7aa812a10964e..8696a067b38993 100644 --- a/drivers/mtd/spi-nor/macronix.c +++ b/drivers/mtd/spi-nor/macronix.c @@ -28,6 +28,17 @@ mx25l25635_post_bfpt_fixups(struct spi_nor *nor, return 0; } +static void mx25u1635_late_init(struct spi_nor *nor) +{ + nor->params->hwcaps.mask |= SNOR_HWCAPS_PP_1_4_4; + spi_nor_set_pp_settings(&nor->params->page_programs[SNOR_CMD_PP_1_4_4], + SPINOR_OP_PP_1_4_4, SNOR_PROTO_1_4_4); +} + +static const struct spi_nor_fixups mx25u1635_fixups = { + .late_init = mx25u1635_late_init, +}; + static const struct spi_nor_fixups mx25l25635_fixups = { .post_bfpt = mx25l25635_post_bfpt_fixups, }; @@ -101,7 +112,9 @@ static const struct flash_info macronix_nor_parts[] = { NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) FIXUP_FLAGS(SPI_NOR_4B_OPCODES) }, { "mx25u1635e", INFO(0xc22535, 0, 64 * 1024, 32) - NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ) }, + PARSE_SFDP + .fixups = &mx25u1635_fixups, + }, }; static void macronix_nor_default_init(struct spi_nor *nor) diff --git a/drivers/spi/spi-atcspi200.c b/drivers/spi/spi-atcspi200.c index 271a986ec80c84..771293a0060264 100644 --- a/drivers/spi/spi-atcspi200.c +++ b/drivers/spi/spi-atcspi200.c @@ -49,10 +49,16 @@ #define ATCSPI200_TRANSCTRL_WRTRANCNT_MASK (0x1FF << ATCSPI200_TRANSCTRL_WRTRANCNT_OFFSET) #define ATCSPI200_TRANSCTRL_RDTRANCNT_OFFSET 0 #define ATCSPI200_TRANSCTRL_RDTRANCNT_MASK (0x1FF << ATCSPI200_TRANSCTRL_RDTRANCNT_OFFSET) -#define CMD_EN (1 << 30) -#define ADDR_EN (1 << 29) -#define DUAL_QUAD(x) (x << 22) -#define DUMMY_CNT(x) ((x - 1) << 9) +#define RDTRAN_CNT(x) ((x == 0) ? 0 : (x - 1) << 0) +#define DUMMY_CNT(x) ((x == 0) ? 0 : (x - 1) << 9) +#define WRTRAN_CNT(x) ((x == 0) ? 0 : (x - 1) << 12) +#define TOKEN_VAl(x) ((x == 0) ? 0 : (1 << 11)) +#define TOKEN_EN(x) ((x == 0) ? 0 : (1 << 21)) +#define DUAL_QUAD(x) ((x == 0) ? 0 : (x << 22)) +#define TRANSMODE(x) ((x == 0) ? 0 : (x << 24)) +#define ADDR_FMT(x) ((x == 0) ? 0 : (1 << 28)) +#define ADDR_EN(x) ((x == 0) ? 0 : (1 << 29)) +#define CMD_EN(x) ((x == 0) ? 0 : (1 << 30)) /* SPI Control Register */ #define ATCSPI200_CTRL_TXFIFORST_MASK (1 << 2) @@ -91,6 +97,11 @@ #define SPI_INTR_ST 0x3C // SPI Interrupt Status Registe #define SPI_TIMING 0x40 // SPI Interface timing Register +/* SPI transfer mode */ +#define REGULAR_MODE 0x1 +#define DUAL_MODE 0x2 +#define QUAD_MODE 0x4 + struct ts_buf { u8 cmd; __be16 data; @@ -115,6 +126,7 @@ struct atcspi200_spi { unsigned int mtiming; int timeout; spinlock_t lock; + struct mutex mutex_lock; }; static void atcspi200_spi_write(struct atcspi200_spi *spi, int offset, u32 value) @@ -268,12 +280,13 @@ static int transfer_data(struct atcspi200_spi *spi, u8 *rx_buf, u8 *tx_buf, int int num_bytes, rf_cnt; u8 *dout = tx_buf; u8 *din = rx_buf; + int timeout = spi->timeout; num_bytes = (spi->trans_len) % CHUNK_SIZE; if (num_bytes == 0) num_bytes = CHUNK_SIZE; - while (num_blks) { + while (num_blks && (timeout--)) { event = atcspi200_spi_read(spi, SPI_STATUS); if ((event & ATCSPI200_STATUS_TXEMPTY_OFFSET) && (tx_buf)) { atcspi200_spi_tx(spi, dout); @@ -430,28 +443,71 @@ static int atcspi200_spi_transfer_one_message(struct spi_master *master, struct return 0; } -static void atcspi200_set_qmode(struct atcspi200_spi *spi, const struct spi_mem_op *op) +static void atcspi200_set_transfer_ctl(struct atcspi200_spi *spi, + const struct spi_mem_op *op) { int tc = 0; - - tc |= (CMD_EN | ADDR_EN | DUAL_QUAD(2) | ATCSPI200_TRANSMODE_DMYREAD | DUMMY_CNT(4)); - - /* Set transfer length. */ + if (op->cmd.nbytes) + tc |= CMD_EN(op->cmd.nbytes); + if (op->addr.nbytes) { + tc |= ADDR_EN(op->addr.nbytes); + if (op->addr.buswidth > 1) + tc |= ADDR_FMT(1); + } if (op->data.nbytes) { - if (op->data.dir == SPI_MEM_DATA_IN) - tc |= ((spi->trans_len - 1) << ATCSPI200_TRANSCTRL_RDTRANCNT_OFFSET); - else - tc |= ((spi->trans_len - 1) << ATCSPI200_TRANSCTRL_WRTRANCNT_OFFSET); - + switch (op->data.buswidth) { + case REGULAR_MODE: + tc |= DUAL_QUAD(0); + break; + case DUAL_MODE: + tc |= DUAL_QUAD(1); + break; + case QUAD_MODE: + tc |= DUAL_QUAD(2); + break; + } + if (op->data.dir == SPI_MEM_DATA_IN) { /* rx */ + if (op->dummy.nbytes) { + tc |= ATCSPI200_TRANSMODE_DMYREAD; + if (tc & DUAL_QUAD(2)) { + tc |= DUMMY_CNT(2); + tc |= TOKEN_EN(1); + } + } else { + tc |= ATCSPI200_TRANSMODE_R_ONLY; + } + tc |= RDTRAN_CNT(op->data.nbytes); + } else { + tc |= ATCSPI200_TRANSMODE_W_ONLY; + tc |= WRTRAN_CNT(op->data.nbytes); + } + } else { + tc |= ATCSPI200_TRANSMODE_NONEDATA; } - - /* Set SPI_TRANSCTRL and address register. */ atcspi200_spi_write(spi, SPI_TRANSCTRL, tc); +} + +static int atcspi200_spi_setting(struct atcspi200_spi *spi, const struct spi_mem_op *op) +{ + /* Set SPI transfer ctl register */ + atcspi200_set_transfer_ctl(spi, op); + + /* Set SPI address register. */ if (op->addr.nbytes) - atcspi200_spi_write(spi, SPI_ADDR, spi->addr); + atcspi200_spi_write(spi, SPI_ADDR, op->addr.val); /* Write cmd to start SPI. */ atcspi200_spi_write(spi, SPI_CMD, op->cmd.opcode); + + return 0; +} + +static int atcspi200_nor_adjust_op_size(struct spi_mem *mem, + struct spi_mem_op *op) +{ + op->data.nbytes = min(op->data.nbytes, MAX_TRANSFER_LEN); + + return 0; } static irqreturn_t andes_spi_irq(int irq, void *dev_id) @@ -461,21 +517,14 @@ static irqreturn_t andes_spi_irq(int irq, void *dev_id) static int atcspi200_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *op) { - int ret, max_trans_len, data_len, trans_len, num_blks, num_chunks; + int ret; struct spi_device *atcspi200_spi = mem->spi; struct atcspi200_spi *spi = spi_master_get_devdata(atcspi200_spi->master); - if (op->cmd.opcode != SPINOR_OP_READ_1_1_4) - return -ENOTSUPP; - + mutex_lock(&spi->mutex_lock); /* Check spi status. */ atcspi200_polling_spiactive(spi); - max_trans_len = spi->max_transfer_length = MAX_TRANSFER_LEN; - data_len = spi->data_len = op->data.nbytes; - num_chunks = DIV_ROUND_UP(data_len, max_trans_len); - spi->addr = op->addr.val; - if (op->data.nbytes) { if (op->data.dir == SPI_MEM_DATA_IN) { spi->din = op->data.buf.in; @@ -485,28 +534,21 @@ static int atcspi200_exec_mem_op(struct spi_mem *mem, const struct spi_mem_op *o spi->din = 0; } } + /* set transfer length and data information. */ + ret = atcspi200_spi_setting(spi, op); + /* Transfer data */ - while (num_chunks--) { - /* set transfer length and data information. */ - trans_len = min_t(size_t, data_len, max_trans_len); - spi->trans_len = trans_len; - num_blks = DIV_ROUND_UP(trans_len, CHUNK_SIZE); - atcspi200_set_qmode(spi, op); - /* Transfer data */ - data_len = transfer_data(spi, spi->din, spi->dout, num_blks); - - if (data_len) - spi->addr += max_trans_len; + transfer_data(spi, spi->din, spi->dout, op->data.nbytes); - ret = atcspi200_spi_stop(spi); - } ret = atcspi200_spi_stop(spi); + mutex_unlock(&spi->mutex_lock); return ret; } static const struct spi_controller_mem_ops atcspi200_mem_ops = { .exec_op = atcspi200_exec_mem_op, + .adjust_op_size = atcspi200_nor_adjust_op_size, }; static int atcspi200_spi_probe(struct platform_device *pdev) @@ -603,7 +645,7 @@ static int atcspi200_spi_probe(struct platform_device *pdev) /* Define our master */ master->bus_num = pdev->id; - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_QUAD; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_QUAD | SPI_TX_QUAD; master->dev.of_node = pdev->dev.of_node; master->num_chipselect = num_cs; master->transfer_one_message = atcspi200_spi_transfer_one_message; From d7b0b8929eac22323ba52a44b24bc7985f2b85ad Mon Sep 17 00:00:00 2001 From: CL Wang Date: Wed, 20 Mar 2024 17:48:13 +0800 Subject: [PATCH 165/169] rtc: andes: atcrtc100: Use the native Linux IO APIs to read and write registers. 1. Use the native Linux IO APIs to read and write registers. 2. Change the register definition of hour from HOR_OFF to HOUR_OFF. 3. Change the register definition of WriteDone from WD to WRITE_DONE. 4. Update the copyright information. Signed off by: CL Wang --- drivers/rtc/rtc-atcrtc100.c | 86 ++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c index e3bdef0f75769b..f34ed71cf8f026 100644 --- a/drivers/rtc/rtc-atcrtc100.c +++ b/drivers/rtc/rtc-atcrtc100.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2008-2017 Andes Technology Corporation + * Copyright (C) 2008-2024 Andes Technology Corporation * Andes RTC Support * * Copyright (C) 2006, 2007, 2008 Paul Mundt @@ -30,21 +30,21 @@ #include #include -#define DRV_NAME "atcrtc100" -#define RTC_REG(off) \ - (*(volatile unsigned int *)(rtc->regbase + (off))) -#define RTC_ID RTC_REG(0x00) /* ID and Revision */ +#define DRV_NAME "atcrtc100" +#define RTC_READ32(offset) readl(rtc->regbase + offset) +#define RTC_WRITE32(val, offset) writel(val, rtc->regbase + offset) + +#define RTC_ID 0x00 /* ID and Revision */ #define ID_OFF 12 #define ID_MSK (0xfffff << ID_OFF) #define ATCRTC100ID (0x03011 << ID_OFF) -#define RTC_RSV RTC_REG(0x4) /* Reserve */ - -#define RTC_CNT RTC_REG(0x10) /* Counter */ -#define RTC_ALM RTC_REG(0x14) /* Alarm */ +#define RTC_RSV 0x4 /* Reserve */ +#define RTC_CNT 0x10 /* Counter */ +#define RTC_ALM 0x14 /* Alarm */ #define DAY_OFF 17 #define DAY_MSK (0x7fff) -#define HOR_OFF 12 -#define HOR_MSK (0x1f) +#define HOUR_OFF 12 +#define HOUR_MSK (0x1f) #define MIN_OFF 6 #define MIN_MSK (0x3f) #define SEC_OFF 0 @@ -54,29 +54,29 @@ #define RTC_MINUTE(x) \ ((x >> MIN_OFF) & MIN_MSK) /* RTC min */ #define RTC_HOUR(x) \ - ((x >> HOR_OFF) & HOR_MSK) /* RTC hour */ + ((x >> HOUR_OFF) & HOUR_MSK) /* RTC hour */ #define RTC_DAYS(x) \ ((x >> DAY_OFF) & DAY_MSK) /* RTC day */ #define RTC_ALM_SECOND \ - ((RTC_ALM >> SEC_OFF) & SEC_MSK) /* RTC alarm sec */ + ((RTC_READ32(RTC_ALM) >> SEC_OFF) & SEC_MSK) /* RTC alarm sec */ #define RTC_ALM_MINUTE \ - ((RTC_ALM >> MIN_OFF) & MIN_MSK) /* RTC alarm min */ + ((RTC_READ32(RTC_ALM) >> MIN_OFF) & MIN_MSK) /* RTC alarm min */ #define RTC_ALM_HOUR \ - ((RTC_ALM >> HOR_OFF) & HOR_MSK) /* RTC alarm hour */ -#define RTC_CR RTC_REG(0x18) /* Control */ + ((RTC_READ32(RTC_ALM) >> HOUR_OFF) & HOUR_MSK) /* RTC alarm hour */ +#define RTC_CR 0x18 /* Control */ #define RTC_EN (0x1UL << 0) #define ALARM_WAKEUP (0x1UL << 1) #define ALARM_INT (0x1UL << 2) #define DAY_INT (0x1UL << 3) -#define HOR_INT (0x1UL << 4) +#define HOUR_INT (0x1UL << 4) #define MIN_INT (0x1UL << 5) #define SEC_INT (0x1UL << 6) #define HSEC_INT (0x1UL << 7) -#define RTC_STA RTC_REG(0x1c) /* Status */ -#define WD (0x1UL << 16) +#define RTC_STA 0x1c /* Status */ +#define WRITE_DONE (0x1UL << 16) /* CHeck if day is configured as 15 */ -#define CHECK_DAY_15 0 +#define CHECK_DAY_15 0 struct atc_rtc { void __iomem *regbase; @@ -93,8 +93,8 @@ static irqreturn_t rtc_interrupt(int irq, void *dev_id) { struct atc_rtc *rtc = dev_id; - if (RTC_STA & SEC_INT) { - RTC_STA |= SEC_INT; + if (RTC_READ32(RTC_STA) & SEC_INT) { + RTC_WRITE32(RTC_READ32(RTC_STA) | SEC_INT, RTC_STA); rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); return IRQ_HANDLED; } @@ -105,9 +105,9 @@ static irqreturn_t rtc_alarm(int irq, void *dev_id) { struct atc_rtc *rtc = dev_id; - if (RTC_STA & ALARM_INT) { - RTC_CR &= ~ALARM_INT; - RTC_STA |= ALARM_INT; + if (RTC_READ32(RTC_STA) & ALARM_INT) { + RTC_WRITE32(RTC_READ32(RTC_CR) & ~ALARM_INT, RTC_CR); + RTC_WRITE32(RTC_READ32(RTC_STA) | ALARM_INT, RTC_STA); rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); return IRQ_HANDLED; } @@ -120,9 +120,9 @@ static int atc_alarm_irq_enable(struct device *dev, unsigned int enabled) spin_lock_irq(&rtc->lock); if (enabled) - RTC_CR |= ALARM_INT; + RTC_WRITE32(RTC_READ32(RTC_CR) | ALARM_INT, RTC_CR); else - RTC_CR &= ~ALARM_INT; + RTC_WRITE32(RTC_READ32(RTC_CR) & ~ALARM_INT, RTC_CR); spin_unlock_irq(&rtc->lock); return 0; } @@ -130,9 +130,9 @@ static int atc_alarm_irq_enable(struct device *dev, unsigned int enabled) static int atc_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct atc_rtc *rtc = dev_get_drvdata(dev); - unsigned long rtc_cnt = RTC_CNT; + unsigned long rtc_cnt = RTC_READ32(RTC_CNT); unsigned long time = RTC_DAYS(rtc_cnt) * 86400 + RTC_HOUR(rtc_cnt) * 3600 - + RTC_MINUTE(rtc_cnt) * 60 + RTC_SECOND(rtc_cnt); + + RTC_MINUTE(rtc_cnt) * 60 + RTC_SECOND(rtc_cnt); rtc_time64_to_tm(time, tm); if (rtc_valid_tm(tm) < 0) { @@ -150,16 +150,16 @@ static int atc_rtc_set_time(struct device *dev, struct rtc_time *tm) time = rtc_tm_to_time64(tm); cnt |= ((div_s64_rem(time, 86400, (u32 *)&time) & DAY_MSK) << DAY_OFF); - cnt |= ((div_s64_rem(time, 3600, (u32 *)&time) & HOR_MSK) << HOR_OFF); + cnt |= ((div_s64_rem(time, 3600, (u32 *)&time) & HOUR_MSK) << HOUR_OFF); cnt |= ((div_s64_rem(time, 60, (u32 *)&time) & MIN_MSK) << MIN_OFF); cnt |= ((time & SEC_MSK) << SEC_OFF); spin_lock_irq(&rtc->lock); - RTC_CNT = cnt; + RTC_WRITE32(cnt, RTC_CNT); spin_unlock_irq(&rtc->lock); /* synchronization progress of RTC register updates */ - while ((RTC_STA & WD) != WD) + while ((RTC_READ32(RTC_STA) & WRITE_DONE) != WRITE_DONE) continue; return 0; @@ -173,7 +173,7 @@ static int atc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) tm->tm_sec = RTC_ALM_SECOND; tm->tm_min = RTC_ALM_MINUTE; tm->tm_hour = RTC_ALM_HOUR; - wkalrm->enabled = (RTC_CR & ALARM_INT) ? 1 : 0; + wkalrm->enabled = (RTC_READ32(RTC_CR) & ALARM_INT) ? 1 : 0; return 0; } @@ -189,20 +189,20 @@ static int atc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) return err; } /* disable alarm interrupt and clear the alarm flag */ - RTC_CR &= ~ALARM_INT; + RTC_WRITE32(RTC_READ32(RTC_CR) & ~ALARM_INT, RTC_CR); /* set alarm time */ alm |= ((tm->tm_sec & SEC_MSK) << SEC_OFF); alm |= ((tm->tm_min & MIN_MSK) << MIN_OFF); - alm |= ((tm->tm_hour & HOR_MSK) << HOR_OFF); + alm |= ((tm->tm_hour & HOUR_MSK) << HOUR_OFF); spin_lock_irq(&rtc->lock); - RTC_ALM = alm; + RTC_WRITE32(alm, RTC_ALM); - while ((RTC_STA & WD) != WD) + while ((RTC_READ32(RTC_STA) & WRITE_DONE) != WRITE_DONE) continue; if (wkalrm->enabled) - RTC_CR |= ALARM_INT; + RTC_WRITE32(RTC_READ32(RTC_CR) | ALARM_INT, RTC_CR); spin_unlock_irq(&rtc->lock); return 0; @@ -262,15 +262,15 @@ static int atc_rtc_probe(struct platform_device *pdev) return -ENOENT; } - if ((RTC_ID & ID_MSK) != ATCRTC100ID) + if ((RTC_READ32(RTC_ID) & ID_MSK) != ATCRTC100ID) return -ENOENT; #if CHECK_DAY_15 RTC_CNT = DAY_MSK << DAY_OFF; - while ((RTC_STA & WD) != WD) + while ((RTC_READ32(RTC_STA) & WRITE_DONE) != WRITE_DONE) continue; - if (DAY_MSK << DAY_OFF != RTC_CNT) { + if (DAY_MSK << DAY_OFF != RTC_READ32(RTC_CNT)) { pr_err("rtc initialize fail\n"); return -ENOENT; } @@ -305,7 +305,7 @@ static int atc_rtc_probe(struct platform_device *pdev) if (ret) goto err_interrupt_irq; - RTC_CR |= RTC_EN; + RTC_WRITE32(RTC_READ32(RTC_CR) | RTC_EN, RTC_CR); return 0; @@ -333,7 +333,7 @@ static int atc_rtc_remove(struct platform_device *pdev) * But if kernel fix this issue, it shell be removed away. */ rtc->rtc_dev->dev.release(&rtc->rtc_dev->dev); - RTC_CR &= ~(RTC_EN | SEC_INT | ALARM_INT); + RTC_WRITE32(RTC_READ32(RTC_CR) & ~(RTC_EN | SEC_INT | ALARM_INT), RTC_CR); free_irq(rtc->alarm_irq, rtc); free_irq(rtc->interrupt_irq, rtc); iounmap(rtc->regbase); From fa241aae2f41caf2eae36fd9cbc3603dfb2faea9 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Wed, 20 Mar 2024 21:09:43 +0800 Subject: [PATCH 166/169] rtc: andes: atcrtc100: handle the situation where the size of the day is insufficient to represent the date. The Andes internal implementation of the RTC on FPGA cannot count more than 32 days because the day counter is 5 bits long, so use a variable to hold the date in the situation where the size of the day is insufficient to represent the date. Signed-off-by: CL Wang --- drivers/rtc/rtc-atcrtc100.c | 131 ++++++++++++++++++++++++++++-------- 1 file changed, 102 insertions(+), 29 deletions(-) diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c index f34ed71cf8f026..4a6e1ad272b463 100644 --- a/drivers/rtc/rtc-atcrtc100.c +++ b/drivers/rtc/rtc-atcrtc100.c @@ -75,8 +75,17 @@ #define RTC_STA 0x1c /* Status */ #define WRITE_DONE (0x1UL << 16) -/* CHeck if day is configured as 15 */ -#define CHECK_DAY_15 0 +#define ATCRTC_TIME_TO_SEC(D, H, M, S) (D * 86400LL + H * 3600 + M * 60 + S) + +/* + * WARNING: This variable is only intended to pass the LTP test. The Andes + * internal implementation of the RTC on FPGA cannot count more than 32 days + * because the day counter is 5 bits long. + * The real RTC hardware should support a sufficiently large counter, and this + * variable is automatically set to 0 if the day counter is sufficient to + * represent the date. + */ +time64_t total_offset_sec; struct atc_rtc { void __iomem *regbase; @@ -127,12 +136,37 @@ static int atc_alarm_irq_enable(struct device *dev, unsigned int enabled) return 0; } +/** + * This function reads the time from the RTC hardware + * @rtc: The structure of the atc_rtc. + * + * This function is called in an atomic operation, so don't add code + * to this function that will cause the process to sleep. + */ +static time64_t atc_rtc_read_rtc_time(struct atc_rtc *rtc) +{ + unsigned long rtc_cnt; + time64_t time; + + /* Check the progress of updating the RTC registers. */ + while ((RTC_READ32(RTC_STA) & WRITE_DONE) != WRITE_DONE) + continue; + + rtc_cnt = RTC_READ32(RTC_CNT); + time = ATCRTC_TIME_TO_SEC(RTC_DAYS(rtc_cnt), RTC_HOUR(rtc_cnt), + RTC_MINUTE(rtc_cnt), RTC_SECOND(rtc_cnt)); + + return time; +} + static int atc_rtc_read_time(struct device *dev, struct rtc_time *tm) { struct atc_rtc *rtc = dev_get_drvdata(dev); - unsigned long rtc_cnt = RTC_READ32(RTC_CNT); - unsigned long time = RTC_DAYS(rtc_cnt) * 86400 + RTC_HOUR(rtc_cnt) * 3600 - + RTC_MINUTE(rtc_cnt) * 60 + RTC_SECOND(rtc_cnt); + time64_t time; + + spin_lock_irq(&rtc->lock); + time = atc_rtc_read_rtc_time(rtc) + total_offset_sec; + spin_unlock_irq(&rtc->lock); rtc_time64_to_tm(time, tm); if (rtc_valid_tm(tm) < 0) { @@ -142,25 +176,74 @@ static int atc_rtc_read_time(struct device *dev, struct rtc_time *tm) return 0; } +/** + * This function write the time to the RTC hardware + * @rtc: The structure of the atc_rtc. + * @time: The time to set to the RTC + * + * This function is called in an atomic operation, so don't add code + * to this function that will cause the process to sleep. + */ +static void atc_rtc_set_rtc_time(struct atc_rtc *rtc, time64_t time) +{ + time64_t time_rem; + s32 rem; + u32 counter; + + counter = ((div_s64_rem(time, 86400, &rem) & DAY_MSK) + << DAY_OFF); + time_rem = rem; + counter |= ((div_s64_rem(time_rem, 3600, &rem) & HOUR_MSK) + << HOUR_OFF); + time_rem = rem; + counter |= ((div_s64_rem(time_rem, 60, &rem) & MIN_MSK) + << MIN_OFF); + counter |= ((rem & SEC_MSK) << SEC_OFF); + + RTC_WRITE32(counter, RTC_CNT); +} + static int atc_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct atc_rtc *rtc = dev_get_drvdata(dev); - time64_t time = 0; - u32 cnt = 0; + struct rtc_time tm_offset; + time64_t rtc_time; + time64_t sys_time; - time = rtc_tm_to_time64(tm); - cnt |= ((div_s64_rem(time, 86400, (u32 *)&time) & DAY_MSK) << DAY_OFF); - cnt |= ((div_s64_rem(time, 3600, (u32 *)&time) & HOUR_MSK) << HOUR_OFF); - cnt |= ((div_s64_rem(time, 60, (u32 *)&time) & MIN_MSK) << MIN_OFF); - cnt |= ((time & SEC_MSK) << SEC_OFF); + sys_time = rtc_tm_to_time64(tm); spin_lock_irq(&rtc->lock); - RTC_WRITE32(cnt, RTC_CNT); - spin_unlock_irq(&rtc->lock); + atc_rtc_set_rtc_time(rtc, sys_time); + + /* Ensure the size of day counter is sufficient to represent the date. */ + rtc_time = atc_rtc_read_rtc_time(rtc); + + if (rtc_time < sys_time) { + /* + * The day counter is not enough to represent the date, so + * we need to take an offset to improve the RTC hardware's ability + * to avoid getting the wrong date. + */ + dev_err(dev, "The size of day counter is insufficient for date representation, and the date may be incorrect when the system is restarted.\n"); + memcpy(&tm_offset, tm, sizeof(struct rtc_time)); + rtc_time = ATCRTC_TIME_TO_SEC(0, tm_offset.tm_hour, tm_offset.tm_min, + tm_offset.tm_sec); + /* + * Only the date (years, months, and days) is stored in the + * total_offset_sec, and the time (hours, minutes, and seconds) + * is stored directly in the hardware of the RTC. + */ + atc_rtc_set_rtc_time(rtc, rtc_time); + + tm_offset.tm_hour = 0; + tm_offset.tm_min = 0; + tm_offset.tm_sec = 0; + total_offset_sec = rtc_tm_to_time64(&tm_offset); + } else { + total_offset_sec = 0; + } - /* synchronization progress of RTC register updates */ - while ((RTC_READ32(RTC_STA) & WRITE_DONE) != WRITE_DONE) - continue; + spin_unlock_irq(&rtc->lock); return 0; } @@ -188,9 +271,9 @@ static int atc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) dev_err(dev, "invalid alarm value\n"); return err; } - /* disable alarm interrupt and clear the alarm flag */ + /* Disable alarm interrupt and clear the alarm flag */ RTC_WRITE32(RTC_READ32(RTC_CR) & ~ALARM_INT, RTC_CR); - /* set alarm time */ + /* Set alarm time */ alm |= ((tm->tm_sec & SEC_MSK) << SEC_OFF); alm |= ((tm->tm_min & MIN_MSK) << MIN_OFF); alm |= ((tm->tm_hour & HOUR_MSK) << HOUR_OFF); @@ -265,16 +348,6 @@ static int atc_rtc_probe(struct platform_device *pdev) if ((RTC_READ32(RTC_ID) & ID_MSK) != ATCRTC100ID) return -ENOENT; -#if CHECK_DAY_15 - RTC_CNT = DAY_MSK << DAY_OFF; - while ((RTC_READ32(RTC_STA) & WRITE_DONE) != WRITE_DONE) - continue; - - if (DAY_MSK << DAY_OFF != RTC_READ32(RTC_CNT)) { - pr_err("rtc initialize fail\n"); - return -ENOENT; - } -#endif platform_set_drvdata(pdev, rtc); if (of_property_read_bool(pdev->dev.of_node, "wakeup-source")) From 1b88112184d2ee0f3f1411a7a7ec05c0df8e4978 Mon Sep 17 00:00:00 2001 From: CL Wang Date: Thu, 28 Mar 2024 15:24:39 +0800 Subject: [PATCH 167/169] rtc: andes: atcrtc100: Support range_min and range_max to extend RTC range. The maximum day counter available for the ATCRTC100 hardware is 15 bits long and can count up to 32767 days. This means that the ATCRTC100 hardware can count up to about 89 years, so we set range_min to 2000 and range_max to the end of the year 2089. Signed-off-by: CL Wang --- drivers/rtc/rtc-atcrtc100.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/rtc/rtc-atcrtc100.c b/drivers/rtc/rtc-atcrtc100.c index 4a6e1ad272b463..d6742a95c5085e 100644 --- a/drivers/rtc/rtc-atcrtc100.c +++ b/drivers/rtc/rtc-atcrtc100.c @@ -77,6 +77,15 @@ #define ATCRTC_TIME_TO_SEC(D, H, M, S) (D * 86400LL + H * 3600 + M * 60 + S) +/* + * The maximum day counter available for the ATCRTC100 hardware is 15 bits + * long and can count up to 32767 days. This means that the ATCRTC100 hardware + * can count up to about 89 years, so we set range_min to 2000 and range_max + * to the end of the year 2089. + */ +#define ATCRTC_RTC_TIMESTAMP_END_2089 3786911999LL /* 2089-12-31 23:59:59 */ +#define ATCRTC_RTC_TIMESTAMP_BEGIN_2000 RTC_TIMESTAMP_BEGIN_2000 + /* * WARNING: This variable is only intended to pass the LTP test. The Andes * internal implementation of the RTC on FPGA cannot count more than 32 days @@ -355,6 +364,8 @@ static int atc_rtc_probe(struct platform_device *pdev) rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); rtc->rtc_dev->ops = &rtc_ops; + rtc->rtc_dev->range_min = ATCRTC_RTC_TIMESTAMP_BEGIN_2000; + rtc->rtc_dev->range_max = ATCRTC_RTC_TIMESTAMP_END_2089; if (IS_ERR(rtc->rtc_dev)) { ret = PTR_ERR(rtc->rtc_dev); From b57a6d328f24e65094a45f87bcd90419ff83a319 Mon Sep 17 00:00:00 2001 From: charles Date: Tue, 9 Apr 2024 10:38:21 +0800 Subject: [PATCH 168/169] drivers: soc: andes: fix cpu_dcache_[wb/inval]_range() failed on 64-bit physical address Bug 30361 - L2 cache operation does not support physical memory larger than 4 GB This issue stemmed from using writel for writing 64-bit physical addresses (PA) to the L2 CCTL access line register, as writel is limited to 32-bit operations. The solution involved replacing writel with writeq, enabling the correct handling of 64-bit PAs. Signed-off-by: charles --- drivers/soc/andes/cache.c | 67 +++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 30 deletions(-) diff --git a/drivers/soc/andes/cache.c b/drivers/soc/andes/cache.c index 1b088d4f6b8357..9d6ab5a4a61dd1 100644 --- a/drivers/soc/andes/cache.c +++ b/drivers/soc/andes/cache.c @@ -69,11 +69,42 @@ static uint32_t cpu_l2c_get_cctl_status(void) mhartid * L2C_REG_STATUS_OFFSET)); } +static void cpu_l2c_cctl(unsigned long long pa, int mhartid, unsigned long ops) +{ +#ifdef CONFIG_64BIT + writeq(pa, + (void *)(l2c_base + + L2C_REG_CN_ACC_OFFSET(mhartid))); +#else + /* + * Considering RV32 potential to use over 4G memory, + * the physical address is split into upper and lower 32 bits + * and stored in a 64-bits PA-type L2C CCTL access line register. + */ + writel((unsigned long)(pa & 0xFFFFFFFF), + (void *)(l2c_base + + L2C_REG_CN_ACC_OFFSET(mhartid))); + + writel((unsigned long)(pa >> 32), + (void *)(l2c_base + + L2C_REG_CN_ACC_OFFSET(mhartid)) + 0x4); +#endif /* !CONFIG_64BIT */ + + writel(ops, + (void *)(l2c_base + + L2C_REG_CN_CMD_OFFSET(mhartid))); + + while ((cpu_l2c_get_cctl_status() & + CCTL_L2_STATUS_CN_MASK(mhartid)) != + CCTL_L2_STATUS_IDLE) + ; +} + void cpu_dcache_wb_range(unsigned long start, unsigned long end, int line_size, struct page *page) { int mhartid = get_cpu(); - unsigned long pa = page_to_phys(page); + unsigned long long pa = page_to_phys(page); if (start & (~PAGE_MASK)) pa += start & ~PAGE_MASK; @@ -82,20 +113,8 @@ void cpu_dcache_wb_range(unsigned long start, unsigned long end, int line_size, custom_csr_write(CCTL_REG_UCCTLBEGINADDR_NUM, start); custom_csr_write(CCTL_REG_UCCTLCOMMAND_NUM, CCTL_L1D_VA_WB); - if (l2c_base) { - writel(pa, - (void *)(l2c_base + - L2C_REG_CN_ACC_OFFSET(mhartid))); - - writel(CCTL_L2_PA_WB, - (void *)(l2c_base + - L2C_REG_CN_CMD_OFFSET(mhartid))); - - while ((cpu_l2c_get_cctl_status() & - CCTL_L2_STATUS_CN_MASK(mhartid)) != - CCTL_L2_STATUS_IDLE) - ; - } + if (likely(l2c_base)) + cpu_l2c_cctl(pa, mhartid, CCTL_L2_PA_WB); start += line_size; pa += line_size; @@ -107,7 +126,7 @@ void cpu_dcache_inval_range(unsigned long start, unsigned long end, int line_size, struct page *page) { int mhartid = get_cpu(); - unsigned long pa = page_to_phys(page); + unsigned long long pa = page_to_phys(page); if (start & (~PAGE_MASK)) pa += start & ~PAGE_MASK; @@ -116,20 +135,8 @@ void cpu_dcache_inval_range(unsigned long start, unsigned long end, custom_csr_write(CCTL_REG_UCCTLBEGINADDR_NUM, start); custom_csr_write(CCTL_REG_UCCTLCOMMAND_NUM, CCTL_L1D_VA_INVAL); - if (l2c_base) { - writel(pa, - (void *)(l2c_base + - L2C_REG_CN_ACC_OFFSET(mhartid))); - - writel(CCTL_L2_PA_INVAL, - (void *)(l2c_base + - L2C_REG_CN_CMD_OFFSET(mhartid))); - - while ((cpu_l2c_get_cctl_status() & - CCTL_L2_STATUS_CN_MASK(mhartid)) != - CCTL_L2_STATUS_IDLE) - ; - } + if (likely(l2c_base)) + cpu_l2c_cctl(pa, mhartid, CCTL_L2_PA_INVAL); start += line_size; pa += line_size; From 1e42bbc89d20670057ceb306d4424fb4d6488475 Mon Sep 17 00:00:00 2001 From: Randolph Date: Tue, 26 Dec 2023 16:12:48 +0800 Subject: [PATCH 169/169] drivers: i2c: andes: using subsys_initcall (#136) fix bug30592 Because the i2c bus driver needs to bring up other drivers, it should use subsys_initcall. The procedure declared as subsys_initcall is guaranteed to be executed before the procedure declared as module_init. http://e-andes.andestech.com/bugzilla5/show_bug.cgi?id=30592 Test on Bigorca with Ax27L2 bitmap Reviewed-on: https://gitea.andestech.com/RD-SW/linux/pulls/136 Reviewed-by: Tim Shih-Ting OuYang Reviewed-by: CL Chin-Long Wang Signed-off-by: Randolph --- drivers/i2c/busses/i2c-atciic100.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-atciic100.c b/drivers/i2c/busses/i2c-atciic100.c index fe1031479d1e52..e4560f62eb4146 100644 --- a/drivers/i2c/busses/i2c-atciic100.c +++ b/drivers/i2c/busses/i2c-atciic100.c @@ -527,7 +527,19 @@ static struct platform_driver atciic_platform_driver = { .remove = atciic_remove, }; -module_platform_driver(atciic_platform_driver); +/* I2C may be needed to bring up other drivers */ +static int __init andes_i2c_init_driver(void) +{ + return platform_driver_register(&atciic_platform_driver); +} +subsys_initcall(andes_i2c_init_driver); + +static void __exit andes_i2c_exit_driver(void) +{ + platform_driver_unregister(&atciic_platform_driver); +} +module_exit(andes_i2c_exit_driver); + MODULE_AUTHOR("Rick Chen"); MODULE_AUTHOR("Dylan Jhong"); MODULE_DESCRIPTION("I2C driver for Andes atciic100");