riscv64: backport staging patches, refresh drivers and config
authorZoltan HERPAI <[email protected]>
Wed, 15 May 2019 09:39:30 +0000 (11:39 +0200)
committerZoltan HERPAI <[email protected]>
Wed, 15 May 2019 09:40:54 +0000 (11:40 +0200)
Signed-off-by: Zoltan HERPAI <[email protected]>
57 files changed:
target/linux/riscv64/patches-4.19/0001-Extract-FPU-context-operations-from-entry.S.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0002-Refactor-FPU-code-in-signal-setup-return-procedures.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0003-Cleanup-ISA-string-setting.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0004-Allow-to-disable-FPU-support.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0005-Auto-detect-whether-a-FPU-exists.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0006-RISC-V-Build-tishift-only-on-64-bit.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0007-RISC-V-Use-swiotlb-on-RV64-only.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0008-lib-Add-umoddi3-and-udivmoddi4-of-GCC-library-routin.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0009-RISC-V-Select-GENERIC_LIB_UMODDI3-on-RV32.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0010-RISC-V-Avoid-corrupting-the-upper-32-bit-of-phys_add.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0011-RISC-V-No-need-to-pass-scause-as-arg-to-do_IRQ.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0012-RISC-V-Don-t-set-cacheinfo.-physical_line_partition-.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0013-RISC-V-Filter-ISA-and-MMU-values-in-cpuinfo.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0014-RISC-V-Comment-on-the-TLB-flush-in-smp_callin.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0015-RISC-V-Disable-preemption-before-enabling-interrupts.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0016-RISC-V-Provide-a-cleaner-raw_smp_processor_id.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0017-RISC-V-Rename-riscv_of_processor_hart-to-riscv_of_pr.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0018-RISC-V-Rename-im_okay_therefore_i_am-to-found_boot_c.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0019-RISC-V-Use-mmgrab.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0020-RISC-V-Use-WRITE_ONCE-instead-of-direct-access.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0021-RISC-V-Add-logical-CPU-indexing-for-RISC-V.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0022-RISC-V-Use-Linux-logical-CPU-number-instead-of-harti.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0023-RISC-V-Show-CPU-ID-and-Hart-ID-separately-in-proc-cp.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0024-RISC-V-Show-IPI-stats.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0026-RISC-V-Mask-out-the-F-extension-on-systems-without-D.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0027-RISC-V-Add-FP-register-ptrace-support-for-gdb.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0028-RISC-V-Add-futex-support.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0029-RISC-V-remove-the-unused-return_to_handler-export.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0030-riscv-move-GCC-version-check-for-ARCH_SUPPORTS_INT12.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0031-RISC-V-Cosmetic-menuconfig-changes.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0032-RISC-V-properly-determine-hardware-caps.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0033-Move-EM_RISCV-into-elf-em.h.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0034-Revert-RISC-V-Select-GENERIC_LIB_UMODDI3-on-RV32.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0035-Revert-lib-Add-umoddi3-and-udivmoddi4-of-GCC-library.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0036-RISC-V-refresh-defconfig.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0037-RISC-V-defconfig-Enable-printk-timestamps.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0038-riscv-fix-spacing-in-struct-pt_regs.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0039-riscv-add-missing-vdso_install-target.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0040-RISC-V-lib-Fix-build-error-for-64-bit.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0100-riscv-add-S-and-U-modes-to-ISA-string.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0101-002-clk-sifive-prci.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0101-003-gemgxl-mgmt-implement-clock-switch-for-GEM-tx_cl.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0101-004-spi-sifive.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0101-005-spi-is25wp256d.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0101-006-uart-sifive-serial-driver.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0101-007-gpio-sifive-support-GPIO-on-SiFive-SoCs.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0101-008-RISC-V-Networking-fix-Hack.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0101-010-pwm-sifive-add-a-driver-for-SiFive-SoC-PWM.patch [new file with mode: 0644]
target/linux/riscv64/patches-4.19/0101-011-sifive-u500-otp-driver-for-SiFive-U500-OTP-contr.patch [new file with mode: 0644]
target/linux/riscv64/patches/0009-RISC-V-Networking-fix-Hack.patch [deleted file]
target/linux/riscv64/patches/0010-pcie-microsemi-added-support-for-the-Vera-board-root.patch [deleted file]
target/linux/riscv64/patches/002-clk-sifive-prci.patch [deleted file]
target/linux/riscv64/patches/003-clk-gemgxl.patch [deleted file]
target/linux/riscv64/patches/004-spi-sifive.patch [deleted file]
target/linux/riscv64/patches/005-spi-is25wp256d.patch [deleted file]
target/linux/riscv64/patches/006-uart-sifive-serial-driver.patch [deleted file]
target/linux/riscv64/patches/007-gpio-sifive-support-GPIO-on-SiFive-SoCs.patch [deleted file]

diff --git a/target/linux/riscv64/patches-4.19/0001-Extract-FPU-context-operations-from-entry.S.patch b/target/linux/riscv64/patches-4.19/0001-Extract-FPU-context-operations-from-entry.S.patch
new file mode 100644 (file)
index 0000000..4a5550f
--- /dev/null
@@ -0,0 +1,247 @@
+From e68ad867f77e1a839ad496ffcbd88b9c96769b5b Mon Sep 17 00:00:00 2001
+From: Alan Kao <[email protected]>
+Date: Tue, 9 Oct 2018 10:18:30 +0800
+Subject: [PATCH 01/41] Extract FPU context operations from entry.S
+
+We move __fstate_save and __fstate_restore to a new source
+file, fpu.S.
+
+Signed-off-by: Alan Kao <[email protected]>
+Cc: Greentime Hu <[email protected]>
+Cc: Vincent Chen <[email protected]>
+Cc: Zong Li <[email protected]>
+Cc: Nick Hu <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/Makefile |   1 +
+ arch/riscv/kernel/entry.S  |  87 -------------------------------------
+ arch/riscv/kernel/fpu.S    | 106 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 107 insertions(+), 87 deletions(-)
+ create mode 100644 arch/riscv/kernel/fpu.S
+
+diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
+index e1274fc..bd433efd 100644
+--- a/arch/riscv/kernel/Makefile
++++ b/arch/riscv/kernel/Makefile
+@@ -13,6 +13,7 @@ extra-y += vmlinux.lds
+ obj-y += cpu.o
+ obj-y += cpufeature.o
+ obj-y += entry.o
++obj-y += fpu.o
+ obj-y += irq.o
+ obj-y += process.o
+ obj-y += ptrace.o
+diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
+index fa2c08e..59c02e2 100644
+--- a/arch/riscv/kernel/entry.S
++++ b/arch/riscv/kernel/entry.S
+@@ -357,93 +357,6 @@ ENTRY(__switch_to)
+       ret
+ ENDPROC(__switch_to)
+-ENTRY(__fstate_save)
+-      li  a2,  TASK_THREAD_F0
+-      add a0, a0, a2
+-      li t1, SR_FS
+-      csrs sstatus, t1
+-      frcsr t0
+-      fsd f0,  TASK_THREAD_F0_F0(a0)
+-      fsd f1,  TASK_THREAD_F1_F0(a0)
+-      fsd f2,  TASK_THREAD_F2_F0(a0)
+-      fsd f3,  TASK_THREAD_F3_F0(a0)
+-      fsd f4,  TASK_THREAD_F4_F0(a0)
+-      fsd f5,  TASK_THREAD_F5_F0(a0)
+-      fsd f6,  TASK_THREAD_F6_F0(a0)
+-      fsd f7,  TASK_THREAD_F7_F0(a0)
+-      fsd f8,  TASK_THREAD_F8_F0(a0)
+-      fsd f9,  TASK_THREAD_F9_F0(a0)
+-      fsd f10, TASK_THREAD_F10_F0(a0)
+-      fsd f11, TASK_THREAD_F11_F0(a0)
+-      fsd f12, TASK_THREAD_F12_F0(a0)
+-      fsd f13, TASK_THREAD_F13_F0(a0)
+-      fsd f14, TASK_THREAD_F14_F0(a0)
+-      fsd f15, TASK_THREAD_F15_F0(a0)
+-      fsd f16, TASK_THREAD_F16_F0(a0)
+-      fsd f17, TASK_THREAD_F17_F0(a0)
+-      fsd f18, TASK_THREAD_F18_F0(a0)
+-      fsd f19, TASK_THREAD_F19_F0(a0)
+-      fsd f20, TASK_THREAD_F20_F0(a0)
+-      fsd f21, TASK_THREAD_F21_F0(a0)
+-      fsd f22, TASK_THREAD_F22_F0(a0)
+-      fsd f23, TASK_THREAD_F23_F0(a0)
+-      fsd f24, TASK_THREAD_F24_F0(a0)
+-      fsd f25, TASK_THREAD_F25_F0(a0)
+-      fsd f26, TASK_THREAD_F26_F0(a0)
+-      fsd f27, TASK_THREAD_F27_F0(a0)
+-      fsd f28, TASK_THREAD_F28_F0(a0)
+-      fsd f29, TASK_THREAD_F29_F0(a0)
+-      fsd f30, TASK_THREAD_F30_F0(a0)
+-      fsd f31, TASK_THREAD_F31_F0(a0)
+-      sw t0, TASK_THREAD_FCSR_F0(a0)
+-      csrc sstatus, t1
+-      ret
+-ENDPROC(__fstate_save)
+-
+-ENTRY(__fstate_restore)
+-      li  a2,  TASK_THREAD_F0
+-      add a0, a0, a2
+-      li t1, SR_FS
+-      lw t0, TASK_THREAD_FCSR_F0(a0)
+-      csrs sstatus, t1
+-      fld f0,  TASK_THREAD_F0_F0(a0)
+-      fld f1,  TASK_THREAD_F1_F0(a0)
+-      fld f2,  TASK_THREAD_F2_F0(a0)
+-      fld f3,  TASK_THREAD_F3_F0(a0)
+-      fld f4,  TASK_THREAD_F4_F0(a0)
+-      fld f5,  TASK_THREAD_F5_F0(a0)
+-      fld f6,  TASK_THREAD_F6_F0(a0)
+-      fld f7,  TASK_THREAD_F7_F0(a0)
+-      fld f8,  TASK_THREAD_F8_F0(a0)
+-      fld f9,  TASK_THREAD_F9_F0(a0)
+-      fld f10, TASK_THREAD_F10_F0(a0)
+-      fld f11, TASK_THREAD_F11_F0(a0)
+-      fld f12, TASK_THREAD_F12_F0(a0)
+-      fld f13, TASK_THREAD_F13_F0(a0)
+-      fld f14, TASK_THREAD_F14_F0(a0)
+-      fld f15, TASK_THREAD_F15_F0(a0)
+-      fld f16, TASK_THREAD_F16_F0(a0)
+-      fld f17, TASK_THREAD_F17_F0(a0)
+-      fld f18, TASK_THREAD_F18_F0(a0)
+-      fld f19, TASK_THREAD_F19_F0(a0)
+-      fld f20, TASK_THREAD_F20_F0(a0)
+-      fld f21, TASK_THREAD_F21_F0(a0)
+-      fld f22, TASK_THREAD_F22_F0(a0)
+-      fld f23, TASK_THREAD_F23_F0(a0)
+-      fld f24, TASK_THREAD_F24_F0(a0)
+-      fld f25, TASK_THREAD_F25_F0(a0)
+-      fld f26, TASK_THREAD_F26_F0(a0)
+-      fld f27, TASK_THREAD_F27_F0(a0)
+-      fld f28, TASK_THREAD_F28_F0(a0)
+-      fld f29, TASK_THREAD_F29_F0(a0)
+-      fld f30, TASK_THREAD_F30_F0(a0)
+-      fld f31, TASK_THREAD_F31_F0(a0)
+-      fscsr t0
+-      csrc sstatus, t1
+-      ret
+-ENDPROC(__fstate_restore)
+-
+-
+       .section ".rodata"
+       /* Exception vector table */
+ ENTRY(excp_vect_table)
+diff --git a/arch/riscv/kernel/fpu.S b/arch/riscv/kernel/fpu.S
+new file mode 100644
+index 0000000..1defb06
+--- /dev/null
++++ b/arch/riscv/kernel/fpu.S
+@@ -0,0 +1,106 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2012 Regents of the University of California
++ * Copyright (C) 2017 SiFive
++ *
++ *   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, version 2.
++ *
++ *   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.
++ */
++
++#include <linux/linkage.h>
++
++#include <asm/asm.h>
++#include <asm/csr.h>
++#include <asm/asm-offsets.h>
++
++ENTRY(__fstate_save)
++      li  a2,  TASK_THREAD_F0
++      add a0, a0, a2
++      li t1, SR_FS
++      csrs sstatus, t1
++      frcsr t0
++      fsd f0,  TASK_THREAD_F0_F0(a0)
++      fsd f1,  TASK_THREAD_F1_F0(a0)
++      fsd f2,  TASK_THREAD_F2_F0(a0)
++      fsd f3,  TASK_THREAD_F3_F0(a0)
++      fsd f4,  TASK_THREAD_F4_F0(a0)
++      fsd f5,  TASK_THREAD_F5_F0(a0)
++      fsd f6,  TASK_THREAD_F6_F0(a0)
++      fsd f7,  TASK_THREAD_F7_F0(a0)
++      fsd f8,  TASK_THREAD_F8_F0(a0)
++      fsd f9,  TASK_THREAD_F9_F0(a0)
++      fsd f10, TASK_THREAD_F10_F0(a0)
++      fsd f11, TASK_THREAD_F11_F0(a0)
++      fsd f12, TASK_THREAD_F12_F0(a0)
++      fsd f13, TASK_THREAD_F13_F0(a0)
++      fsd f14, TASK_THREAD_F14_F0(a0)
++      fsd f15, TASK_THREAD_F15_F0(a0)
++      fsd f16, TASK_THREAD_F16_F0(a0)
++      fsd f17, TASK_THREAD_F17_F0(a0)
++      fsd f18, TASK_THREAD_F18_F0(a0)
++      fsd f19, TASK_THREAD_F19_F0(a0)
++      fsd f20, TASK_THREAD_F20_F0(a0)
++      fsd f21, TASK_THREAD_F21_F0(a0)
++      fsd f22, TASK_THREAD_F22_F0(a0)
++      fsd f23, TASK_THREAD_F23_F0(a0)
++      fsd f24, TASK_THREAD_F24_F0(a0)
++      fsd f25, TASK_THREAD_F25_F0(a0)
++      fsd f26, TASK_THREAD_F26_F0(a0)
++      fsd f27, TASK_THREAD_F27_F0(a0)
++      fsd f28, TASK_THREAD_F28_F0(a0)
++      fsd f29, TASK_THREAD_F29_F0(a0)
++      fsd f30, TASK_THREAD_F30_F0(a0)
++      fsd f31, TASK_THREAD_F31_F0(a0)
++      sw t0, TASK_THREAD_FCSR_F0(a0)
++      csrc sstatus, t1
++      ret
++ENDPROC(__fstate_save)
++
++ENTRY(__fstate_restore)
++      li  a2,  TASK_THREAD_F0
++      add a0, a0, a2
++      li t1, SR_FS
++      lw t0, TASK_THREAD_FCSR_F0(a0)
++      csrs sstatus, t1
++      fld f0,  TASK_THREAD_F0_F0(a0)
++      fld f1,  TASK_THREAD_F1_F0(a0)
++      fld f2,  TASK_THREAD_F2_F0(a0)
++      fld f3,  TASK_THREAD_F3_F0(a0)
++      fld f4,  TASK_THREAD_F4_F0(a0)
++      fld f5,  TASK_THREAD_F5_F0(a0)
++      fld f6,  TASK_THREAD_F6_F0(a0)
++      fld f7,  TASK_THREAD_F7_F0(a0)
++      fld f8,  TASK_THREAD_F8_F0(a0)
++      fld f9,  TASK_THREAD_F9_F0(a0)
++      fld f10, TASK_THREAD_F10_F0(a0)
++      fld f11, TASK_THREAD_F11_F0(a0)
++      fld f12, TASK_THREAD_F12_F0(a0)
++      fld f13, TASK_THREAD_F13_F0(a0)
++      fld f14, TASK_THREAD_F14_F0(a0)
++      fld f15, TASK_THREAD_F15_F0(a0)
++      fld f16, TASK_THREAD_F16_F0(a0)
++      fld f17, TASK_THREAD_F17_F0(a0)
++      fld f18, TASK_THREAD_F18_F0(a0)
++      fld f19, TASK_THREAD_F19_F0(a0)
++      fld f20, TASK_THREAD_F20_F0(a0)
++      fld f21, TASK_THREAD_F21_F0(a0)
++      fld f22, TASK_THREAD_F22_F0(a0)
++      fld f23, TASK_THREAD_F23_F0(a0)
++      fld f24, TASK_THREAD_F24_F0(a0)
++      fld f25, TASK_THREAD_F25_F0(a0)
++      fld f26, TASK_THREAD_F26_F0(a0)
++      fld f27, TASK_THREAD_F27_F0(a0)
++      fld f28, TASK_THREAD_F28_F0(a0)
++      fld f29, TASK_THREAD_F29_F0(a0)
++      fld f30, TASK_THREAD_F30_F0(a0)
++      fld f31, TASK_THREAD_F31_F0(a0)
++      fscsr t0
++      csrc sstatus, t1
++      ret
++ENDPROC(__fstate_restore)
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0002-Refactor-FPU-code-in-signal-setup-return-procedures.patch b/target/linux/riscv64/patches-4.19/0002-Refactor-FPU-code-in-signal-setup-return-procedures.patch
new file mode 100644 (file)
index 0000000..942af4d
--- /dev/null
@@ -0,0 +1,129 @@
+From 007f5c35895786fdc797f13313b9493fe5d5e655 Mon Sep 17 00:00:00 2001
+From: Alan Kao <[email protected]>
+Date: Tue, 9 Oct 2018 10:18:31 +0800
+Subject: [PATCH 02/41] Refactor FPU code in signal setup/return procedures
+
+FPU-related logic is separated from normal signal handling path in
+this patch.  Kernel can easily be configured to exclude those procedures
+for no-FPU systems.
+
+Signed-off-by: Alan Kao <[email protected]>
+Cc: Greentime Hu <[email protected]>
+Cc: Vincent Chen <[email protected]>
+Cc: Zong Li <[email protected]>
+Cc: Nick Hu <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/signal.c | 68 ++++++++++++++++++++++++++++------------------
+ 1 file changed, 41 insertions(+), 27 deletions(-)
+
+diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c
+index 718d0c9..6a18b98 100644
+--- a/arch/riscv/kernel/signal.c
++++ b/arch/riscv/kernel/signal.c
+@@ -37,45 +37,63 @@ struct rt_sigframe {
+       struct ucontext uc;
+ };
+-static long restore_d_state(struct pt_regs *regs,
+-      struct __riscv_d_ext_state __user *state)
++static long restore_fp_state(struct pt_regs *regs,
++                           union __riscv_fp_state *sc_fpregs)
+ {
+       long err;
++      struct __riscv_d_ext_state __user *state = &sc_fpregs->d;
++      size_t i;
++
+       err = __copy_from_user(&current->thread.fstate, state, sizeof(*state));
+-      if (likely(!err))
+-              fstate_restore(current, regs);
++      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;
+ }
+-static long save_d_state(struct pt_regs *regs,
+-      struct __riscv_d_ext_state __user *state)
++static long save_fp_state(struct pt_regs *regs,
++                        union __riscv_fp_state *sc_fpregs)
+ {
++      long err;
++      struct __riscv_d_ext_state __user *state = &sc_fpregs->d;
++      size_t i;
++
+       fstate_save(current, regs);
+-      return __copy_to_user(state, &current->thread.fstate, sizeof(*state));
++      err = __copy_to_user(state, &current->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;
+ }
+ 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. */
+-      err = restore_d_state(regs, &sc->sc_fpregs.d);
+-      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;
+-      }
++      err |= restore_fp_state(regs, &sc->sc_fpregs);
+       return err;
+ }
+@@ -124,14 +142,10 @@ 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. */
+-      err |= save_d_state(regs, &sc->sc_fpregs.d);
+-      /* 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]);
++      err |= save_fp_state(regs, &sc->sc_fpregs);
+       return err;
+ }
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0003-Cleanup-ISA-string-setting.patch b/target/linux/riscv64/patches-4.19/0003-Cleanup-ISA-string-setting.patch
new file mode 100644 (file)
index 0000000..ef452aa
--- /dev/null
@@ -0,0 +1,65 @@
+From e8be5302330281bd9f77834600f63e8cc4560d3d Mon Sep 17 00:00:00 2001
+From: Alan Kao <[email protected]>
+Date: Tue, 9 Oct 2018 10:18:32 +0800
+Subject: [PATCH 03/41] Cleanup ISA string setting
+
+This patch cleanup the MARCH string passing to both compiler and
+assembler.  Note that the CFLAGS should not contain "fd" before we
+have mechnisms like kernel_fpu_begin/end in other architectures.
+
+Signed-off-by: Alan Kao <[email protected]>
+Cc: Greentime Hu <[email protected]>
+Cc: Vincent Chen <[email protected]>
+Cc: Zong Li <[email protected]>
+Cc: Nick Hu <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/Makefile | 19 ++++++++-----------
+ 1 file changed, 8 insertions(+), 11 deletions(-)
+
+diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
+index 61ec424..01393e1 100644
+--- a/arch/riscv/Makefile
++++ b/arch/riscv/Makefile
+@@ -28,7 +28,6 @@ ifeq ($(CONFIG_ARCH_RV64I),y)
+       
+       KBUILD_CFLAGS   += $(call cc-ifversion, -ge, 0500, -DCONFIG_ARCH_SUPPORTS_INT128)
+-      KBUILD_MARCH = rv64im
+       KBUILD_LDFLAGS += -melf64lriscv
+ else
+       BITS := 32
+@@ -36,22 +35,20 @@ else
+       KBUILD_CFLAGS += -mabi=ilp32
+       KBUILD_AFLAGS += -mabi=ilp32
+-      KBUILD_MARCH = rv32im
+       KBUILD_LDFLAGS += -melf32lriscv
+ endif
+ KBUILD_CFLAGS += -Wall
+-ifeq ($(CONFIG_RISCV_ISA_A),y)
+-      KBUILD_ARCH_A = a
+-endif
+-ifeq ($(CONFIG_RISCV_ISA_C),y)
+-      KBUILD_ARCH_C = c
+-endif
+-
+-KBUILD_AFLAGS += -march=$(KBUILD_MARCH)$(KBUILD_ARCH_A)fd$(KBUILD_ARCH_C)
++# ISA string setting
++riscv-march-$(CONFIG_ARCH_RV32I)      := rv32im
++riscv-march-$(CONFIG_ARCH_RV64I)      := rv64im
++riscv-march-$(CONFIG_RISCV_ISA_A)     := $(riscv-march-y)a
++riscv-march-y                         := $(riscv-march-y)fd
++riscv-march-$(CONFIG_RISCV_ISA_C)     := $(riscv-march-y)c
++KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y))
++KBUILD_AFLAGS += -march=$(riscv-march-y)
+-KBUILD_CFLAGS += -march=$(KBUILD_MARCH)$(KBUILD_ARCH_A)$(KBUILD_ARCH_C)
+ KBUILD_CFLAGS += -mno-save-restore
+ KBUILD_CFLAGS += -DCONFIG_PAGE_OFFSET=$(CONFIG_PAGE_OFFSET)
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0004-Allow-to-disable-FPU-support.patch b/target/linux/riscv64/patches-4.19/0004-Allow-to-disable-FPU-support.patch
new file mode 100644 (file)
index 0000000..e4bf664
--- /dev/null
@@ -0,0 +1,160 @@
+From 9671f7061433e2c58b9894093eada1898595b85d Mon Sep 17 00:00:00 2001
+From: Alan Kao <[email protected]>
+Date: Tue, 9 Oct 2018 10:18:33 +0800
+Subject: [PATCH 04/41] Allow to disable FPU support
+
+FPU codes have been separated from common part in previous patches.
+This patch add the CONFIG_FPU option and some stubs, so that a no-FPU
+configuration is allowed.
+
+Signed-off-by: Alan Kao <[email protected]>
+Cc: Greentime Hu <[email protected]>
+Cc: Vincent Chen <[email protected]>
+Cc: Zong Li <[email protected]>
+Cc: Nick Hu <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/Kconfig                 |  9 +++++++++
+ arch/riscv/Makefile                |  2 +-
+ arch/riscv/include/asm/switch_to.h | 10 ++++++++++
+ arch/riscv/kernel/Makefile         |  2 +-
+ arch/riscv/kernel/process.c        |  4 +++-
+ arch/riscv/kernel/signal.c         |  5 +++++
+ 6 files changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index a344980..a63f9db 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -208,6 +208,15 @@ config RISCV_BASE_PMU
+ endmenu
++config FPU
++      bool "FPU support"
++      default y
++      help
++        Say N here if you want to disable all floating-point related procedure
++        in the kernel.
++
++        If you don't know what to do here, say Y.
++
+ endmenu
+ menu "Kernel type"
+diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
+index 01393e1..901770f 100644
+--- a/arch/riscv/Makefile
++++ b/arch/riscv/Makefile
+@@ -44,7 +44,7 @@ KBUILD_CFLAGS += -Wall
+ riscv-march-$(CONFIG_ARCH_RV32I)      := rv32im
+ riscv-march-$(CONFIG_ARCH_RV64I)      := rv64im
+ riscv-march-$(CONFIG_RISCV_ISA_A)     := $(riscv-march-y)a
+-riscv-march-y                         := $(riscv-march-y)fd
++riscv-march-$(CONFIG_FPU)             := $(riscv-march-y)fd
+ riscv-march-$(CONFIG_RISCV_ISA_C)     := $(riscv-march-y)c
+ KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y))
+ KBUILD_AFLAGS += -march=$(riscv-march-y)
+diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h
+index dd6b05b..093050b 100644
+--- a/arch/riscv/include/asm/switch_to.h
++++ b/arch/riscv/include/asm/switch_to.h
+@@ -18,6 +18,7 @@
+ #include <asm/ptrace.h>
+ #include <asm/csr.h>
++#ifdef CONFIG_FPU
+ extern void __fstate_save(struct task_struct *save_to);
+ extern void __fstate_restore(struct task_struct *restore_from);
+@@ -55,6 +56,15 @@ static inline void __switch_to_aux(struct task_struct *prev,
+       fstate_restore(next, task_pt_regs(next));
+ }
++#define DEFAULT_SSTATUS (SR_SPIE | SR_FS_INITIAL)
++
++#else
++#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 DEFAULT_SSTATUS (SR_SPIE | SR_FS_OFF)
++#endif
++
+ extern struct task_struct *__switch_to(struct task_struct *,
+                                      struct task_struct *);
+diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
+index bd433efd..f13f7f2 100644
+--- a/arch/riscv/kernel/Makefile
++++ b/arch/riscv/kernel/Makefile
+@@ -13,7 +13,6 @@ extra-y += vmlinux.lds
+ obj-y += cpu.o
+ obj-y += cpufeature.o
+ obj-y += entry.o
+-obj-y += fpu.o
+ obj-y += irq.o
+ obj-y += process.o
+ obj-y += ptrace.o
+@@ -32,6 +31,7 @@ obj-y        += vdso/
+ CFLAGS_setup.o := -mcmodel=medany
++obj-$(CONFIG_FPU)             += fpu.o
+ obj-$(CONFIG_SMP)             += smpboot.o
+ obj-$(CONFIG_SMP)             += smp.o
+ obj-$(CONFIG_MODULES)         += module.o
+diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
+index d7c6ca7..07d5156 100644
+--- a/arch/riscv/kernel/process.c
++++ b/arch/riscv/kernel/process.c
+@@ -76,7 +76,7 @@ void show_regs(struct pt_regs *regs)
+ void start_thread(struct pt_regs *regs, unsigned long pc,
+       unsigned long sp)
+ {
+-      regs->sstatus = SR_SPIE /* User mode, irqs on */ | SR_FS_INITIAL;
++      regs->sstatus = DEFAULT_SSTATUS;
+       regs->sepc = pc;
+       regs->sp = sp;
+       set_fs(USER_DS);
+@@ -84,12 +84,14 @@ void start_thread(struct pt_regs *regs, unsigned long pc,
+ void flush_thread(void)
+ {
++#ifdef CONFIG_FPU
+       /*
+        * Reset FPU context
+        *      frm: round to nearest, ties to even (IEEE default)
+        *      fflags: accrued exceptions cleared
+        */
+       memset(&current->thread.fstate, 0, sizeof(current->thread.fstate));
++#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 6a18b98..2450b82 100644
+--- a/arch/riscv/kernel/signal.c
++++ b/arch/riscv/kernel/signal.c
+@@ -37,6 +37,7 @@ struct rt_sigframe {
+       struct ucontext uc;
+ };
++#ifdef CONFIG_FPU
+ static long restore_fp_state(struct pt_regs *regs,
+                            union __riscv_fp_state *sc_fpregs)
+ {
+@@ -85,6 +86,10 @@ static long save_fp_state(struct pt_regs *regs,
+       return err;
+ }
++#else
++#define save_fp_state(task, regs) (0)
++#define restore_fp_state(task, regs) (0)
++#endif
+ static long restore_sigcontext(struct pt_regs *regs,
+       struct sigcontext __user *sc)
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0005-Auto-detect-whether-a-FPU-exists.patch b/target/linux/riscv64/patches-4.19/0005-Auto-detect-whether-a-FPU-exists.patch
new file mode 100644 (file)
index 0000000..98709b5
--- /dev/null
@@ -0,0 +1,123 @@
+From 9411ec60c23d868124d9c1b1d491937aebe07afa Mon Sep 17 00:00:00 2001
+From: Alan Kao <[email protected]>
+Date: Tue, 9 Oct 2018 10:18:34 +0800
+Subject: [PATCH 05/41] Auto-detect whether a FPU exists
+
+We expect that a kernel with CONFIG_FPU=y can still support no-FPU
+machines. To do so, the kernel should first examine the existence of a
+FPU, then do nothing if a FPU does exist; otherwise, it should
+disable/bypass all FPU-related functions.
+
+In this patch, a new global variable, has_fpu, is created and determined
+when parsing the hardware capability from device tree during booting.
+This variable is used in those FPU-related functions.
+
+Signed-off-by: Alan Kao <[email protected]>
+Cc: Greentime Hu <[email protected]>
+Cc: Vincent Chen <[email protected]>
+Cc: Zong Li <[email protected]>
+Cc: Nick Hu <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/include/asm/switch_to.h | 8 ++++----
+ arch/riscv/kernel/cpufeature.c     | 8 ++++++++
+ arch/riscv/kernel/process.c        | 4 +++-
+ arch/riscv/kernel/signal.c         | 6 ++++--
+ 4 files changed, 19 insertions(+), 7 deletions(-)
+
+diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h
+index 093050b..7335590 100644
+--- a/arch/riscv/include/asm/switch_to.h
++++ b/arch/riscv/include/asm/switch_to.h
+@@ -56,13 +56,12 @@ static inline void __switch_to_aux(struct task_struct *prev,
+       fstate_restore(next, task_pt_regs(next));
+ }
+-#define DEFAULT_SSTATUS (SR_SPIE | SR_FS_INITIAL)
+-
++extern bool has_fpu;
+ #else
++#define has_fpu 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 DEFAULT_SSTATUS (SR_SPIE | SR_FS_OFF)
+ #endif
+ extern struct task_struct *__switch_to(struct task_struct *,
+@@ -72,7 +71,8 @@ extern struct task_struct *__switch_to(struct task_struct *,
+ do {                                                  \
+       struct task_struct *__prev = (prev);            \
+       struct task_struct *__next = (next);            \
+-      __switch_to_aux(__prev, __next);                \
++      if (has_fpu)                                    \
++              __switch_to_aux(__prev, __next);        \
+       ((last) = __switch_to(__prev, __next));         \
+ } while (0)
+diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
+index 17011a8..46942e6 100644
+--- a/arch/riscv/kernel/cpufeature.c
++++ b/arch/riscv/kernel/cpufeature.c
+@@ -22,6 +22,9 @@
+ #include <asm/hwcap.h>
+ unsigned long elf_hwcap __read_mostly;
++#ifdef CONFIG_FPU
++bool has_fpu __read_mostly;
++#endif
+ void riscv_fill_hwcap(void)
+ {
+@@ -58,4 +61,9 @@ void riscv_fill_hwcap(void)
+               elf_hwcap |= isa2hwcap[(unsigned char)(isa[i])];
+       pr_info("elf_hwcap is 0x%lx", elf_hwcap);
++
++#ifdef CONFIG_FPU
++      if (elf_hwcap & (COMPAT_HWCAP_ISA_F | COMPAT_HWCAP_ISA_D))
++              has_fpu = true;
++#endif
+ }
+diff --git a/arch/riscv/kernel/process.c b/arch/riscv/kernel/process.c
+index 07d5156..bef1999 100644
+--- a/arch/riscv/kernel/process.c
++++ b/arch/riscv/kernel/process.c
+@@ -76,7 +76,9 @@ void show_regs(struct pt_regs *regs)
+ void start_thread(struct pt_regs *regs, unsigned long pc,
+       unsigned long sp)
+ {
+-      regs->sstatus = DEFAULT_SSTATUS;
++      regs->sstatus = SR_SPIE;
++      if (has_fpu)
++              regs->sstatus |= SR_FS_INITIAL;
+       regs->sepc = pc;
+       regs->sp = sp;
+       set_fs(USER_DS);
+diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c
+index 2450b82..f9b5e7e 100644
+--- a/arch/riscv/kernel/signal.c
++++ b/arch/riscv/kernel/signal.c
+@@ -98,7 +98,8 @@ static long restore_sigcontext(struct pt_regs *regs,
+       /* sc_regs is structured the same as the start of pt_regs */
+       err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs));
+       /* Restore the floating-point state. */
+-      err |= restore_fp_state(regs, &sc->sc_fpregs);
++      if (has_fpu)
++              err |= restore_fp_state(regs, &sc->sc_fpregs);
+       return err;
+ }
+@@ -150,7 +151,8 @@ static long setup_sigcontext(struct rt_sigframe __user *frame,
+       /* 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. */
+-      err |= save_fp_state(regs, &sc->sc_fpregs);
++      if (has_fpu)
++              err |= save_fp_state(regs, &sc->sc_fpregs);
+       return err;
+ }
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0006-RISC-V-Build-tishift-only-on-64-bit.patch b/target/linux/riscv64/patches-4.19/0006-RISC-V-Build-tishift-only-on-64-bit.patch
new file mode 100644 (file)
index 0000000..8b64199
--- /dev/null
@@ -0,0 +1,30 @@
+From 7f47c73b355f300cf162f3a664e43d557d2cb30d Mon Sep 17 00:00:00 2001
+From: Zong Li <[email protected]>
+Date: Tue, 2 Oct 2018 16:52:27 +0800
+Subject: [PATCH 06/41] RISC-V: Build tishift only on 64-bit
+
+Only RV64 supports 128 integer size.
+
+Signed-off-by: Zong Li <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/lib/Makefile | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
+index 445ec84..5739bd0 100644
+--- a/arch/riscv/lib/Makefile
++++ b/arch/riscv/lib/Makefile
+@@ -2,6 +2,7 @@ lib-y  += delay.o
+ lib-y += memcpy.o
+ lib-y += memset.o
+ lib-y += uaccess.o
+-lib-y += tishift.o
++
++lib-(CONFIG_64BIT) += tishift.o
+ lib-$(CONFIG_32BIT) += udivdi3.o
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0007-RISC-V-Use-swiotlb-on-RV64-only.patch b/target/linux/riscv64/patches-4.19/0007-RISC-V-Use-swiotlb-on-RV64-only.patch
new file mode 100644 (file)
index 0000000..6833d01
--- /dev/null
@@ -0,0 +1,32 @@
+From 51858aaf9bea3ddf166bf9d252a1fc351260b497 Mon Sep 17 00:00:00 2001
+From: Zong Li <[email protected]>
+Date: Tue, 2 Oct 2018 16:52:28 +0800
+Subject: [PATCH 07/41] RISC-V: Use swiotlb on RV64 only
+
+Only RV64 supports swiotlb. On RV32, it don't select the SWIOTLB.
+
+Signed-off-by: Zong Li <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/setup.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c
+index b2d26d9..c946198 100644
+--- a/arch/riscv/kernel/setup.c
++++ b/arch/riscv/kernel/setup.c
+@@ -227,7 +227,10 @@ void __init setup_arch(char **cmdline_p)
+       setup_bootmem();
+       paging_init();
+       unflatten_device_tree();
++
++#ifdef CONFIG_SWIOTLB
+       swiotlb_init(1);
++#endif
+ #ifdef CONFIG_SMP
+       setup_smp();
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0008-lib-Add-umoddi3-and-udivmoddi4-of-GCC-library-routin.patch b/target/linux/riscv64/patches-4.19/0008-lib-Add-umoddi3-and-udivmoddi4-of-GCC-library-routin.patch
new file mode 100644 (file)
index 0000000..f115f72
--- /dev/null
@@ -0,0 +1,407 @@
+From 6315730e9eab7de5fa9864bb13a352713f48aef1 Mon Sep 17 00:00:00 2001
+From: Zong Li <[email protected]>
+Date: Tue, 2 Oct 2018 16:52:29 +0800
+Subject: [PATCH 08/41] lib: Add umoddi3 and udivmoddi4 of GCC library routines
+
+Add umoddi3 and udivmoddi4 support for 32-bit.
+
+The RV32 need the umoddi3 to do modulo when the operands are long long
+type, like other libraries implementation such as ucmpdi2, lshrdi3 and
+so on.
+
+I encounter the undefined reference 'umoddi3' when I use the in
+house dma driver, although it is in house driver, but I think that
+umoddi3 is a common function for RV32.
+
+The udivmoddi4 and umoddi3 are copies from libgcc in gcc. There are other
+functions use the udivmoddi4 in libgcc, so I separate the umoddi3 and
+udivmoddi4 for flexible extension in the future.
+
+Signed-off-by: Zong Li <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ lib/Kconfig      |   3 +
+ lib/Makefile     |   1 +
+ lib/udivmoddi4.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ lib/umoddi3.c    |  32 ++++++
+ 4 files changed, 346 insertions(+)
+ create mode 100644 lib/udivmoddi4.c
+ create mode 100644 lib/umoddi3.c
+
+diff --git a/lib/Kconfig b/lib/Kconfig
+index a3928d4..d82f206 100644
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -621,3 +621,6 @@ config GENERIC_LIB_CMPDI2
+ config GENERIC_LIB_UCMPDI2
+       bool
++
++config GENERIC_LIB_UMODDI3
++      bool
+diff --git a/lib/Makefile b/lib/Makefile
+index 4238764..56a8d9c 100644
+--- a/lib/Makefile
++++ b/lib/Makefile
+@@ -270,3 +270,4 @@ obj-$(CONFIG_GENERIC_LIB_LSHRDI3) += lshrdi3.o
+ obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
+ obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
+ obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
++obj-$(CONFIG_GENERIC_LIB_UMODDI3) += umoddi3.o udivmoddi4.o
+diff --git a/lib/udivmoddi4.c b/lib/udivmoddi4.c
+new file mode 100644
+index 0000000..c08bc8a
+--- /dev/null
++++ b/lib/udivmoddi4.c
+@@ -0,0 +1,310 @@
++// SPDX-License-Identifier: GPL-2.0
++
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * 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; if not, see the file COPYING, or write
++ * to the Free Software Foundation, Inc.
++ */
++
++#include <linux/libgcc.h>
++
++#define count_leading_zeros(COUNT, X)   ((COUNT) = __builtin_clz(X))
++
++#define W_TYPE_SIZE 32
++
++#define __ll_B ((unsigned long) 1 << (W_TYPE_SIZE / 2))
++#define __ll_lowpart(t) ((unsigned long) (t) & (__ll_B - 1))
++#define __ll_highpart(t) ((unsigned long) (t) >> (W_TYPE_SIZE / 2))
++
++/* If we still don't have umul_ppmm, define it using plain C. */
++#if !defined(umul_ppmm)
++#define umul_ppmm(w1, w0, u, v)                                               \
++      do {                                                            \
++              unsigned long __x0, __x1, __x2, __x3;                   \
++              unsigned short __ul, __vl, __uh, __vh;                  \
++                                                                      \
++              __ul = __ll_lowpart(u);                                 \
++              __uh = __ll_highpart(u);                                \
++              __vl = __ll_lowpart(v);                                 \
++              __vh = __ll_highpart(v);                                \
++                                                                      \
++              __x0 = (unsigned long) __ul * __vl;                     \
++              __x1 = (unsigned long) __ul * __vh;                     \
++              __x2 = (unsigned long) __uh * __vl;                     \
++              __x3 = (unsigned long) __uh * __vh;                     \
++                                                                      \
++              __x1 += __ll_highpart(__x0);                            \
++              __x1 += __x2;                                           \
++              if (__x1 < __x2)                                        \
++                      __x3 += __ll_B;                                 \
++                                                                      \
++              (w1) = __x3 + __ll_highpart(__x1);                      \
++              (w0) = __ll_lowpart(__x1) * __ll_B + __ll_lowpart(__x0);\
++      } while (0)
++#endif
++
++#if !defined(sub_ddmmss)
++#define sub_ddmmss(sh, sl, ah, al, bh, bl)                            \
++      do {                                                            \
++              unsigned long __x;                                      \
++              __x = (al) - (bl);                                      \
++              (sh) = (ah) - (bh) - (__x > (al));                      \
++              (sl) = __x;                                             \
++      } while (0)
++#endif
++
++/* Define this unconditionally, so it can be used for debugging. */
++#define __udiv_qrnnd_c(q, r, n1, n0, d)                                       \
++      do {                                                            \
++              unsigned long __d1, __d0, __q1, __q0;                   \
++              unsigned long __r1, __r0, __m;                          \
++              __d1 = __ll_highpart(d);                                \
++              __d0 = __ll_lowpart(d);                         \
++                                                                      \
++              __r1 = (n1) % __d1;                                     \
++              __q1 = (n1) / __d1;                                     \
++              __m = (unsigned long) __q1 * __d0;                      \
++              __r1 = __r1 * __ll_B | __ll_highpart(n0);               \
++              if (__r1 < __m) {                                       \
++                      __q1--, __r1 += (d);                            \
++                      if (__r1 >= (d))                                \
++                              if (__r1 < __m)                         \
++                                      __q1--, __r1 += (d);            \
++              }                                                       \
++              __r1 -= __m;                                            \
++                                                                      \
++              __r0 = __r1 % __d1;                                     \
++              __q0 = __r1 / __d1;                                     \
++              __m = (unsigned long) __q0 * __d0;                      \
++              __r0 = __r0 * __ll_B | __ll_lowpart(n0);                \
++              if (__r0 < __m) {                                       \
++                      __q0--, __r0 += (d);                            \
++                      if (__r0 >= (d))                                \
++                              if (__r0 < __m)                         \
++                                      __q0--, __r0 += (d);            \
++              }                                                       \
++              __r0 -= __m;                                            \
++                                                                      \
++              (q) = (unsigned long) __q1 * __ll_B | __q0;             \
++              (r) = __r0;                                             \
++      } while (0)
++
++/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */
++#if !defined(udiv_qrnnd)
++#define UDIV_NEEDS_NORMALIZATION 1
++#define udiv_qrnnd __udiv_qrnnd_c
++#endif
++
++unsigned long long __udivmoddi4(unsigned long long u, unsigned long long v,
++                              unsigned long long *rp)
++{
++      const DWunion nn = {.ll = u };
++      const DWunion dd = {.ll = v };
++      DWunion rr, ww;
++      unsigned long d0, d1, n0, n1, n2;
++      unsigned long q0 = 0, q1 = 0;
++      unsigned long b, bm;
++
++      d0 = dd.s.low;
++      d1 = dd.s.high;
++      n0 = nn.s.low;
++      n1 = nn.s.high;
++
++#if !UDIV_NEEDS_NORMALIZATION
++
++      if (d1 == 0) {
++              if (d0 > n1) {
++                      /* 0q = nn / 0D */
++
++                      udiv_qrnnd(q0, n0, n1, n0, d0);
++                      q1 = 0;
++
++                      /* Remainder in n0. */
++              } else {
++                      /* qq = NN / 0d */
++
++                      if (d0 == 0)
++                              /* Divide intentionally by zero. */
++                              d0 = 1 / d0;
++
++                      udiv_qrnnd(q1, n1, 0, n1, d0);
++                      udiv_qrnnd(q0, n0, n1, n0, d0);
++
++                      /* Remainder in n0. */
++              }
++
++              if (rp != 0) {
++                      rr.s.low = n0;
++                      rr.s.high = 0;
++                      *rp = rr.ll;
++              }
++
++#else /* UDIV_NEEDS_NORMALIZATION */
++
++      if (d1 == 0) {
++              if (d0 > n1) {
++                      /* 0q = nn / 0D */
++
++                      count_leading_zeros(bm, d0);
++
++                      if (bm != 0) {
++                              /*
++                               * Normalize, i.e. make the most significant bit
++                               * of the denominator set.
++                               */
++
++                              d0 = d0 << bm;
++                              n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm));
++                              n0 = n0 << bm;
++                      }
++
++                      udiv_qrnnd(q0, n0, n1, n0, d0);
++                      q1 = 0;
++
++                      /* Remainder in n0 >> bm. */
++              } else {
++                      /* qq = NN / 0d */
++
++                      if (d0 == 0)
++                              /* Divide intentionally by zero. */
++                              d0 = 1 / d0;
++
++                      count_leading_zeros(bm, d0);
++
++                      if (bm == 0) {
++                              /*
++                               * From (n1 >= d0) /\ (the most significant bit
++                               * of d0 is set), conclude (the most significant
++                               * bit of n1 is set) /\ (theleading quotient
++                               * digit q1 = 1).
++                               *
++                               * This special case is necessary, not an
++                               * optimization. (Shifts counts of W_TYPE_SIZE
++                               * are undefined.)
++                               */
++
++                              n1 -= d0;
++                              q1 = 1;
++                      } else {
++                              /* Normalize. */
++
++                              b = W_TYPE_SIZE - bm;
++
++                              d0 = d0 << bm;
++                              n2 = n1 >> b;
++                              n1 = (n1 << bm) | (n0 >> b);
++                              n0 = n0 << bm;
++
++                              udiv_qrnnd(q1, n1, n2, n1, d0);
++                      }
++
++                      /* n1 != d0... */
++
++                      udiv_qrnnd(q0, n0, n1, n0, d0);
++
++                      /* Remainder in n0 >> bm. */
++              }
++
++              if (rp != 0) {
++                      rr.s.low = n0 >> bm;
++                      rr.s.high = 0;
++                      *rp = rr.ll;
++              }
++
++#endif /* UDIV_NEEDS_NORMALIZATION */
++
++      } else {
++              if (d1 > n1) {
++                      /* 00 = nn / DD */
++
++                      q0 = 0;
++                      q1 = 0;
++
++                      /* Remainder in n1n0. */
++                      if (rp != 0) {
++                              rr.s.low = n0;
++                              rr.s.high = n1;
++                              *rp = rr.ll;
++                      }
++              } else {
++                      /* 0q = NN / dd */
++
++                      count_leading_zeros(bm, d1);
++                      if (bm == 0) {
++                              /*
++                               * From (n1 >= d1) /\ (the most significant bit
++                               * of d1 is set), conclude (the most significant
++                               * bit of n1 is set) /\ (the quotient digit q0 =
++                               * 0 or 1).
++                               *
++                               * This special case is necessary, not an
++                               * optimization.
++                               */
++
++                              /*
++                               * The condition on the next line takes
++                               * advantage of that n1 >= d1 (true due to
++                               * program flow).
++                               */
++                              if (n1 > d1 || n0 >= d0) {
++                                      q0 = 1;
++                                      sub_ddmmss(n1, n0, n1, n0, d1, d0);
++                              } else {
++                                      q0 = 0;
++                              }
++
++                              q1 = 0;
++
++                              if (rp != 0) {
++                                      rr.s.low = n0;
++                                      rr.s.high = n1;
++                                      *rp = rr.ll;
++                              }
++                      } else {
++                              unsigned long m1, m0;
++                              /* Normalize. */
++
++                              b = W_TYPE_SIZE - bm;
++
++                              d1 = (d1 << bm) | (d0 >> b);
++                              d0 = d0 << bm;
++                              n2 = n1 >> b;
++                              n1 = (n1 << bm) | (n0 >> b);
++                              n0 = n0 << bm;
++
++                              udiv_qrnnd(q0, n1, n2, n1, d1);
++                              umul_ppmm(m1, m0, q0, d0);
++
++                              if (m1 > n1 || (m1 == n1 && m0 > n0)) {
++                                      q0--;
++                                      sub_ddmmss(m1, m0, m1, m0, d1, d0);
++                              }
++
++                              q1 = 0;
++
++                              /* Remainder in (n1n0 - m1m0) >> bm. */
++                              if (rp != 0) {
++                                      sub_ddmmss(n1, n0, n1, n0, m1, m0);
++                                      rr.s.low = (n1 << b) | (n0 >> bm);
++                                      rr.s.high = n1 >> bm;
++                                      *rp = rr.ll;
++                              }
++                      }
++              }
++      }
++
++      ww.s.low = q0;
++      ww.s.high = q1;
++
++      return ww.ll;
++}
+diff --git a/lib/umoddi3.c b/lib/umoddi3.c
+new file mode 100644
+index 0000000..d7bbf0f
+--- /dev/null
++++ b/lib/umoddi3.c
+@@ -0,0 +1,32 @@
++// SPDX-License-Identifier: GPL-2.0
++
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * 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; if not, see the file COPYING, or write
++ * to the Free Software Foundation, Inc.
++ */
++
++#include <linux/module.h>
++#include <linux/libgcc.h>
++
++extern unsigned long long __udivmoddi4(unsigned long long u,
++                                     unsigned long long v,
++                                     unsigned long long *rp);
++
++unsigned long long __umoddi3(unsigned long long u, unsigned long long v)
++{
++      unsigned long long w;
++      (void)__udivmoddi4(u, v, &w);
++      return w;
++}
++EXPORT_SYMBOL(__umoddi3);
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0009-RISC-V-Select-GENERIC_LIB_UMODDI3-on-RV32.patch b/target/linux/riscv64/patches-4.19/0009-RISC-V-Select-GENERIC_LIB_UMODDI3-on-RV32.patch
new file mode 100644 (file)
index 0000000..bc570eb
--- /dev/null
@@ -0,0 +1,28 @@
+From 757331db921428295948fed5e7377a436e66d34e Mon Sep 17 00:00:00 2001
+From: Zong Li <[email protected]>
+Date: Tue, 2 Oct 2018 16:52:30 +0800
+Subject: [PATCH 09/41] RISC-V: Select GENERIC_LIB_UMODDI3 on RV32
+
+On 32-bit, it need to use __umoddi3 by some drivers.
+
+Signed-off-by: Zong Li <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index a344980..dc262fa 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -108,6 +108,7 @@ config ARCH_RV32I
+       select GENERIC_LIB_ASHRDI3
+       select GENERIC_LIB_LSHRDI3
+       select GENERIC_LIB_UCMPDI2
++      select GENERIC_LIB_UMODDI3
+ config ARCH_RV64I
+       bool "RV64I"
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0010-RISC-V-Avoid-corrupting-the-upper-32-bit-of-phys_add.patch b/target/linux/riscv64/patches-4.19/0010-RISC-V-Avoid-corrupting-the-upper-32-bit-of-phys_add.patch
new file mode 100644 (file)
index 0000000..6bb58c4
--- /dev/null
@@ -0,0 +1,34 @@
+From 827a438156e4c423b6875a092e272933952a2910 Mon Sep 17 00:00:00 2001
+From: Vincent Chen <[email protected]>
+Date: Tue, 2 Oct 2018 16:52:31 +0800
+Subject: [PATCH 10/41] RISC-V: Avoid corrupting the upper 32-bit of
+ phys_addr_t in ioremap
+
+For 32bit, the upper 32-bit of phys_addr_t will be flushed to zero
+after AND with PAGE_MASK because the data type of PAGE_MASK is
+unsigned long. To fix this problem, the page alignment is done by
+subtracting the page offset instead of AND with PAGE_MASK.
+
+Signed-off-by: Vincent Chen <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/mm/ioremap.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/riscv/mm/ioremap.c b/arch/riscv/mm/ioremap.c
+index 70ef272..bd2f2db 100644
+--- a/arch/riscv/mm/ioremap.c
++++ b/arch/riscv/mm/ioremap.c
+@@ -42,7 +42,7 @@ static void __iomem *__ioremap_caller(phys_addr_t addr, size_t size,
+       /* Page-align mappings */
+       offset = addr & (~PAGE_MASK);
+-      addr &= PAGE_MASK;
++      addr -= offset;
+       size = PAGE_ALIGN(size + offset);
+       area = get_vm_area_caller(size, VM_IOREMAP, caller);
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0011-RISC-V-No-need-to-pass-scause-as-arg-to-do_IRQ.patch b/target/linux/riscv64/patches-4.19/0011-RISC-V-No-need-to-pass-scause-as-arg-to-do_IRQ.patch
new file mode 100644 (file)
index 0000000..c915356
--- /dev/null
@@ -0,0 +1,50 @@
+From 1ed4237ab616a05225e11d07bf42d5474deec905 Mon Sep 17 00:00:00 2001
+From: Anup Patel <[email protected]>
+Date: Tue, 2 Oct 2018 12:14:54 -0700
+Subject: [PATCH 11/41] RISC-V: No need to pass scause as arg to do_IRQ()
+
+The scause is already part of pt_regs so no need to pass
+scause as separate arg to do_IRQ().
+
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Anup Patel <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/entry.S | 1 -
+ arch/riscv/kernel/irq.c   | 4 ++--
+ 2 files changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S
+index fa2c08e..6eaacfa 100644
+--- a/arch/riscv/kernel/entry.S
++++ b/arch/riscv/kernel/entry.S
+@@ -168,7 +168,6 @@ ENTRY(handle_exception)
+       /* Handle interrupts */
+       move a0, sp /* pt_regs */
+-      move a1, s4 /* scause */
+       tail do_IRQ
+ 1:
+       /* Exceptions run with interrupts enabled */
+diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c
+index 0cfac48..ca45933 100644
+--- a/arch/riscv/kernel/irq.c
++++ b/arch/riscv/kernel/irq.c
+@@ -24,12 +24,12 @@
+  */
+ #define INTERRUPT_CAUSE_FLAG  (1UL << (__riscv_xlen - 1))
+-asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs, unsigned long cause)
++asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs)
+ {
+       struct pt_regs *old_regs = set_irq_regs(regs);
+       irq_enter();
+-      switch (cause & ~INTERRUPT_CAUSE_FLAG) {
++      switch (regs->scause & ~INTERRUPT_CAUSE_FLAG) {
+       case INTERRUPT_CAUSE_TIMER:
+               riscv_timer_interrupt();
+               break;
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0012-RISC-V-Don-t-set-cacheinfo.-physical_line_partition-.patch b/target/linux/riscv64/patches-4.19/0012-RISC-V-Don-t-set-cacheinfo.-physical_line_partition-.patch
new file mode 100644 (file)
index 0000000..7463de2
--- /dev/null
@@ -0,0 +1,40 @@
+From 566d6c428eadf9dc06df8b2195dff58d9a97c9e6 Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Tue, 2 Oct 2018 12:14:55 -0700
+Subject: [PATCH 12/41] RISC-V: Don't set
+ cacheinfo.{physical_line_partition,attributes}
+
+These are just hard coded in the RISC-V port, which doesn't make any
+sense.  We should probably be setting these from device tree entries
+when they exist, but for now I think it's saner to just leave them all
+as their default values.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Reviewed-by: Jeremy Linton <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/cacheinfo.c | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/arch/riscv/kernel/cacheinfo.c b/arch/riscv/kernel/cacheinfo.c
+index 0bc86e5..cb35ffd 100644
+--- a/arch/riscv/kernel/cacheinfo.c
++++ b/arch/riscv/kernel/cacheinfo.c
+@@ -22,13 +22,6 @@ static void ci_leaf_init(struct cacheinfo *this_leaf,
+ {
+       this_leaf->level = level;
+       this_leaf->type = type;
+-      /* not a sector cache */
+-      this_leaf->physical_line_partition = 1;
+-      /* TODO: Add to DTS */
+-      this_leaf->attributes =
+-              CACHE_WRITE_BACK
+-              | CACHE_READ_ALLOCATE
+-              | CACHE_WRITE_ALLOCATE;
+ }
+ static int __init_cache_level(unsigned int cpu)
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0013-RISC-V-Filter-ISA-and-MMU-values-in-cpuinfo.patch b/target/linux/riscv64/patches-4.19/0013-RISC-V-Filter-ISA-and-MMU-values-in-cpuinfo.patch
new file mode 100644 (file)
index 0000000..19c12e0
--- /dev/null
@@ -0,0 +1,107 @@
+From 19ccf29bb18f08a4583aa899a8cc8c11e5ea85a6 Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Tue, 2 Oct 2018 12:14:56 -0700
+Subject: [PATCH 13/41] RISC-V: Filter ISA and MMU values in cpuinfo
+
+We shouldn't be directly passing device tree values to userspace, both
+because there could be mistakes in device trees and because the kernel
+doesn't support arbitrary ISAs.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+[Atish: checkpatch fix and code comment formatting update]
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/cpu.c | 68 ++++++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 61 insertions(+), 7 deletions(-)
+
+diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
+index ca6c81e..1c0bf66 100644
+--- a/arch/riscv/kernel/cpu.c
++++ b/arch/riscv/kernel/cpu.c
+@@ -58,6 +58,63 @@ int riscv_of_processor_hart(struct device_node *node)
+ #ifdef CONFIG_PROC_FS
++static void print_isa(struct seq_file *f, const char *orig_isa)
++{
++      static const char *ext = "mafdc";
++      const char *isa = orig_isa;
++      const char *e;
++
++      /*
++       * Linux doesn't support rv32e or rv128i, and we only support booting
++       * kernels on harts with the same ISA that the kernel is compiled for.
++       */
++#if defined(CONFIG_32BIT)
++      if (strncmp(isa, "rv32i", 5) != 0)
++              return;
++#elif defined(CONFIG_64BIT)
++      if (strncmp(isa, "rv64i", 5) != 0)
++              return;
++#endif
++
++      /* Print the base ISA, as we already know it's legal. */
++      seq_puts(f, "isa\t: ");
++      seq_write(f, isa, 5);
++      isa += 5;
++
++      /*
++       * Check the rest of the ISA string for valid extensions, printing those
++       * we find.  RISC-V ISA strings define an order, so we only print the
++       * extension bits when they're in order.
++       */
++      for (e = ext; *e != '\0'; ++e) {
++              if (isa[0] == e[0]) {
++                      seq_write(f, isa, 1);
++                      isa++;
++              }
++      }
++
++      /*
++       * If we were given an unsupported ISA in the device tree then print
++       * a bit of info describing what went wrong.
++       */
++      if (isa[0] != '\0')
++              pr_info("unsupported ISA \"%s\" in device tree", orig_isa);
++}
++
++static void print_mmu(struct seq_file *f, const char *mmu_type)
++{
++#if defined(CONFIG_32BIT)
++      if (strcmp(mmu_type, "riscv,sv32") != 0)
++              return;
++#elif defined(CONFIG_64BIT)
++      if (strcmp(mmu_type, "riscv,sv39") != 0 &&
++          strcmp(mmu_type, "riscv,sv48") != 0)
++              return;
++#endif
++
++      seq_printf(f, "mmu\t: %s\n", mmu_type+6);
++}
++
+ static void *c_start(struct seq_file *m, loff_t *pos)
+ {
+       *pos = cpumask_next(*pos - 1, cpu_online_mask);
+@@ -83,13 +140,10 @@ static int c_show(struct seq_file *m, void *v)
+       const char *compat, *isa, *mmu;
+       seq_printf(m, "hart\t: %lu\n", hart_id);
+-      if (!of_property_read_string(node, "riscv,isa", &isa)
+-          && isa[0] == 'r'
+-          && isa[1] == 'v')
+-              seq_printf(m, "isa\t: %s\n", isa);
+-      if (!of_property_read_string(node, "mmu-type", &mmu)
+-          && !strncmp(mmu, "riscv,", 6))
+-              seq_printf(m, "mmu\t: %s\n", mmu+6);
++      if (!of_property_read_string(node, "riscv,isa", &isa))
++              print_isa(m, isa);
++      if (!of_property_read_string(node, "mmu-type", &mmu))
++              print_mmu(m, mmu);
+       if (!of_property_read_string(node, "compatible", &compat)
+           && strcmp(compat, "riscv"))
+               seq_printf(m, "uarch\t: %s\n", compat);
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0014-RISC-V-Comment-on-the-TLB-flush-in-smp_callin.patch b/target/linux/riscv64/patches-4.19/0014-RISC-V-Comment-on-the-TLB-flush-in-smp_callin.patch
new file mode 100644 (file)
index 0000000..e327975
--- /dev/null
@@ -0,0 +1,34 @@
+From b18d6f05252d6b3f725c08d8831a46b003df5b6b Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Tue, 2 Oct 2018 12:14:57 -0700
+Subject: [PATCH 14/41] RISC-V: Comment on the TLB flush in smp_callin()
+
+This isn't readily apparent from reading the code.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+[Atish: code comment formatting update]
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/smpboot.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
+index 56abab6..712e9ca 100644
+--- a/arch/riscv/kernel/smpboot.c
++++ b/arch/riscv/kernel/smpboot.c
+@@ -106,6 +106,10 @@ asmlinkage void __init smp_callin(void)
+       trap_init();
+       notify_cpu_starting(smp_processor_id());
+       set_cpu_online(smp_processor_id(), 1);
++      /*
++       * Remote TLB flushes are ignored while the CPU is offline, so emit
++       * a local TLB flush right now just in case.
++       */
+       local_flush_tlb_all();
+       local_irq_enable();
+       preempt_disable();
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0015-RISC-V-Disable-preemption-before-enabling-interrupts.patch b/target/linux/riscv64/patches-4.19/0015-RISC-V-Disable-preemption-before-enabling-interrupts.patch
new file mode 100644 (file)
index 0000000..fe0d8b7
--- /dev/null
@@ -0,0 +1,38 @@
+From 6db170ff4c088caaf7806c00b29a55f6df07d7b6 Mon Sep 17 00:00:00 2001
+From: Atish Patra <[email protected]>
+Date: Tue, 2 Oct 2018 12:14:58 -0700
+Subject: [PATCH 15/41] RISC-V: Disable preemption before enabling interrupts
+
+Currently, irq is enabled before preemption disabling happens.
+If the scheduler fired right here and cpu is scheduled then it
+may blow up.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+[Atish: Commit text and code comment formatting update]
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/smpboot.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
+index 712e9ca..670749e 100644
+--- a/arch/riscv/kernel/smpboot.c
++++ b/arch/riscv/kernel/smpboot.c
+@@ -111,7 +111,11 @@ asmlinkage void __init smp_callin(void)
+        * a local TLB flush right now just in case.
+        */
+       local_flush_tlb_all();
+-      local_irq_enable();
++      /*
++       * Disable preemption before enabling interrupts, so we don't try to
++       * schedule a CPU that hasn't actually started yet.
++       */
+       preempt_disable();
++      local_irq_enable();
+       cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
+ }
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0016-RISC-V-Provide-a-cleaner-raw_smp_processor_id.patch b/target/linux/riscv64/patches-4.19/0016-RISC-V-Provide-a-cleaner-raw_smp_processor_id.patch
new file mode 100644 (file)
index 0000000..8f9c856
--- /dev/null
@@ -0,0 +1,55 @@
+From 9639a44394b9859a5576cb36630105733a552bd6 Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Tue, 2 Oct 2018 12:14:59 -0700
+Subject: [PATCH 16/41] RISC-V: Provide a cleaner raw_smp_processor_id()
+
+I'm not sure how I managed to miss this the first time, but this is much
+better.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+[Atish: code comment formatting and other fixes]
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/include/asm/smp.h | 14 ++++----------
+ 1 file changed, 4 insertions(+), 10 deletions(-)
+
+diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
+index 3601684..85d7619 100644
+--- a/arch/riscv/include/asm/smp.h
++++ b/arch/riscv/include/asm/smp.h
+@@ -14,13 +14,9 @@
+ #ifndef _ASM_RISCV_SMP_H
+ #define _ASM_RISCV_SMP_H
+-/* This both needs asm-offsets.h and is used when generating it. */
+-#ifndef GENERATING_ASM_OFFSETS
+-#include <asm/asm-offsets.h>
+-#endif
+-
+ #include <linux/cpumask.h>
+ #include <linux/irqreturn.h>
++#include <linux/thread_info.h>
+ #ifdef CONFIG_SMP
+@@ -34,12 +30,10 @@ void arch_send_call_function_ipi_mask(struct cpumask *mask);
+ void arch_send_call_function_single_ipi(int cpu);
+ /*
+- * This is particularly ugly: it appears we can't actually get the definition
+- * of task_struct here, but we need access to the CPU this task is running on.
+- * Instead of using C we're using asm-offsets.h to get the current processor
+- * ID.
++ * Obtains the hart ID of the currently executing task.  This relies on
++ * THREAD_INFO_IN_TASK, but we define that unconditionally.
+  */
+-#define raw_smp_processor_id() (*((int*)((char*)get_current() + TASK_TI_CPU)))
++#define raw_smp_processor_id() (current_thread_info()->cpu)
+ #endif /* CONFIG_SMP */
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0017-RISC-V-Rename-riscv_of_processor_hart-to-riscv_of_pr.patch b/target/linux/riscv64/patches-4.19/0017-RISC-V-Rename-riscv_of_processor_hart-to-riscv_of_pr.patch
new file mode 100644 (file)
index 0000000..197bde3
--- /dev/null
@@ -0,0 +1,97 @@
+From b2f8cfa7ac34202e5fd9551b6507fcd424634c1b Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Tue, 2 Oct 2018 12:15:00 -0700
+Subject: [PATCH 17/41] RISC-V: Rename riscv_of_processor_hart to
+ riscv_of_processor_hartid
+
+It's a bit confusing exactly what this function does: it actually
+returns the hartid of an OF processor node, failing with -1 on invalid
+nodes.  I've changed the name to _hartid() in order to make that a bit
+more clear, as well as adding a comment.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+[Atish: code comment formatting update]
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/include/asm/processor.h | 2 +-
+ arch/riscv/kernel/cpu.c            | 7 +++++--
+ arch/riscv/kernel/smpboot.c        | 2 +-
+ drivers/clocksource/riscv_timer.c  | 2 +-
+ drivers/irqchip/irq-sifive-plic.c  | 2 +-
+ 5 files changed, 9 insertions(+), 6 deletions(-)
+
+diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h
+index 3fe4af8..50de774 100644
+--- a/arch/riscv/include/asm/processor.h
++++ b/arch/riscv/include/asm/processor.h
+@@ -88,7 +88,7 @@ static inline void wait_for_interrupt(void)
+ }
+ struct device_node;
+-extern int riscv_of_processor_hart(struct device_node *node);
++int riscv_of_processor_hartid(struct device_node *node);
+ extern void riscv_fill_hwcap(void);
+diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
+index 1c0bf66..4723e23 100644
+--- a/arch/riscv/kernel/cpu.c
++++ b/arch/riscv/kernel/cpu.c
+@@ -15,8 +15,11 @@
+ #include <linux/seq_file.h>
+ #include <linux/of.h>
+-/* Return -1 if not a valid hart */
+-int riscv_of_processor_hart(struct device_node *node)
++/*
++ * Returns the hart ID of the given device tree node, or -1 if the device tree
++ * node isn't a RISC-V hart.
++ */
++int riscv_of_processor_hartid(struct device_node *node)
+ {
+       const char *isa, *status;
+       u32 hart;
+diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
+index 670749e..cfb0b02 100644
+--- a/arch/riscv/kernel/smpboot.c
++++ b/arch/riscv/kernel/smpboot.c
+@@ -53,7 +53,7 @@ void __init setup_smp(void)
+       int hart, im_okay_therefore_i_am = 0;
+       while ((dn = of_find_node_by_type(dn, "cpu"))) {
+-              hart = riscv_of_processor_hart(dn);
++              hart = riscv_of_processor_hartid(dn);
+               if (hart >= 0) {
+                       set_cpu_possible(hart, true);
+                       set_cpu_present(hart, true);
+diff --git a/drivers/clocksource/riscv_timer.c b/drivers/clocksource/riscv_timer.c
+index 4e8b347..ad7453f 100644
+--- a/drivers/clocksource/riscv_timer.c
++++ b/drivers/clocksource/riscv_timer.c
+@@ -84,7 +84,7 @@ void riscv_timer_interrupt(void)
+ static int __init riscv_timer_init_dt(struct device_node *n)
+ {
+-      int cpu_id = riscv_of_processor_hart(n), error;
++      int cpu_id = riscv_of_processor_hartid(n), error;
+       struct clocksource *cs;
+       if (cpu_id != smp_processor_id())
+diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
+index 532e9d6..c55eaa3 100644
+--- a/drivers/irqchip/irq-sifive-plic.c
++++ b/drivers/irqchip/irq-sifive-plic.c
+@@ -176,7 +176,7 @@ static int plic_find_hart_id(struct device_node *node)
+ {
+       for (; node; node = node->parent) {
+               if (of_device_is_compatible(node, "riscv"))
+-                      return riscv_of_processor_hart(node);
++                      return riscv_of_processor_hartid(node);
+       }
+       return -1;
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0018-RISC-V-Rename-im_okay_therefore_i_am-to-found_boot_c.patch b/target/linux/riscv64/patches-4.19/0018-RISC-V-Rename-im_okay_therefore_i_am-to-found_boot_c.patch
new file mode 100644 (file)
index 0000000..9d1886c
--- /dev/null
@@ -0,0 +1,49 @@
+From 177fae4515889e2407810c5167a5227da8b37cce Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Tue, 2 Oct 2018 12:15:01 -0700
+Subject: [PATCH 18/41] RISC-V: Rename im_okay_therefore_i_am to found_boot_cpu
+
+The old name was a bit odd.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/smpboot.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
+index cfb0b02..4a23260 100644
+--- a/arch/riscv/kernel/smpboot.c
++++ b/arch/riscv/kernel/smpboot.c
+@@ -50,7 +50,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
+ void __init setup_smp(void)
+ {
+       struct device_node *dn = NULL;
+-      int hart, im_okay_therefore_i_am = 0;
++      int hart;
++      bool found_boot_cpu = false;
+       while ((dn = of_find_node_by_type(dn, "cpu"))) {
+               hart = riscv_of_processor_hartid(dn);
+@@ -58,13 +59,13 @@ void __init setup_smp(void)
+                       set_cpu_possible(hart, true);
+                       set_cpu_present(hart, true);
+                       if (hart == smp_processor_id()) {
+-                              BUG_ON(im_okay_therefore_i_am);
+-                              im_okay_therefore_i_am = 1;
++                              BUG_ON(found_boot_cpu);
++                              found_boot_cpu = true;
+                       }
+               }
+       }
+-      BUG_ON(!im_okay_therefore_i_am);
++      BUG_ON(!found_boot_cpu);
+ }
+ int __cpu_up(unsigned int cpu, struct task_struct *tidle)
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0019-RISC-V-Use-mmgrab.patch b/target/linux/riscv64/patches-4.19/0019-RISC-V-Use-mmgrab.patch
new file mode 100644 (file)
index 0000000..c6425cc
--- /dev/null
@@ -0,0 +1,40 @@
+From 46373cb442c56d2f8a4c8b3f777c89d20546c9d5 Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Tue, 2 Oct 2018 12:15:02 -0700
+Subject: [PATCH 19/41] RISC-V: Use mmgrab()
+
+commit f1f1007644ff ("mm: add new mmgrab() helper") added a
+helper that we missed out on.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Atish Patra <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/smpboot.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
+index 4a23260..17e7483 100644
+--- a/arch/riscv/kernel/smpboot.c
++++ b/arch/riscv/kernel/smpboot.c
+@@ -30,6 +30,7 @@
+ #include <linux/irq.h>
+ #include <linux/of.h>
+ #include <linux/sched/task_stack.h>
++#include <linux/sched/mm.h>
+ #include <asm/irq.h>
+ #include <asm/mmu_context.h>
+ #include <asm/tlbflush.h>
+@@ -101,7 +102,7 @@ asmlinkage void __init smp_callin(void)
+       struct mm_struct *mm = &init_mm;
+       /* All kernel threads share the same mm context.  */
+-      atomic_inc(&mm->mm_count);
++      mmgrab(mm);
+       current->active_mm = mm;
+       trap_init();
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0020-RISC-V-Use-WRITE_ONCE-instead-of-direct-access.patch b/target/linux/riscv64/patches-4.19/0020-RISC-V-Use-WRITE_ONCE-instead-of-direct-access.patch
new file mode 100644 (file)
index 0000000..ec7ad2d
--- /dev/null
@@ -0,0 +1,35 @@
+From a37d56fc401108f39dec9ba83ed923a453937a26 Mon Sep 17 00:00:00 2001
+From: Atish Patra <[email protected]>
+Date: Tue, 2 Oct 2018 12:15:03 -0700
+Subject: [PATCH 20/41] RISC-V: Use WRITE_ONCE instead of direct access
+
+The secondary harts spin on couple of per cpu variables until both of
+these are non-zero so it's not necessary to have any ordering here.
+However, WRITE_ONCE should be used to avoid tearing.
+
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/smpboot.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
+index 17e7483..1e47861 100644
+--- a/arch/riscv/kernel/smpboot.c
++++ b/arch/riscv/kernel/smpboot.c
+@@ -81,8 +81,9 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle)
+        * the spinning harts that they can continue the boot process.
+        */
+       smp_mb();
+-      __cpu_up_stack_pointer[cpu] = task_stack_page(tidle) + THREAD_SIZE;
+-      __cpu_up_task_pointer[cpu] = tidle;
++      WRITE_ONCE(__cpu_up_stack_pointer[cpu],
++                task_stack_page(tidle) + THREAD_SIZE);
++      WRITE_ONCE(__cpu_up_task_pointer[cpu], tidle);
+       while (!cpu_online(cpu))
+               cpu_relax();
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0021-RISC-V-Add-logical-CPU-indexing-for-RISC-V.patch b/target/linux/riscv64/patches-4.19/0021-RISC-V-Add-logical-CPU-indexing-for-RISC-V.patch
new file mode 100644 (file)
index 0000000..84bb8dd
--- /dev/null
@@ -0,0 +1,121 @@
+From 6825c7a80f1863b975a00042abe140ea24813af2 Mon Sep 17 00:00:00 2001
+From: Atish Patra <[email protected]>
+Date: Tue, 2 Oct 2018 12:15:04 -0700
+Subject: [PATCH 21/41] RISC-V: Add logical CPU indexing for RISC-V
+
+Currently, both Linux CPU id and hart id are same.
+This is not recommended as it will lead to discontinuous CPU
+indexing in Linux. Moreover, kdump kernel will run from CPU0
+which would be absent if we follow existing scheme.
+
+Implement a logical mapping between Linux CPU id and hart
+id to decouple these two. Always mark the boot processor as
+CPU0 and all other CPUs get the logical CPU id based on their
+booting order.
+
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Anup Patel <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/include/asm/smp.h | 24 +++++++++++++++++++++++-
+ arch/riscv/kernel/setup.c    |  4 ++++
+ arch/riscv/kernel/smp.c      | 19 +++++++++++++++++++
+ 3 files changed, 46 insertions(+), 1 deletion(-)
+
+diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
+index 85d7619..47fd61df 100644
+--- a/arch/riscv/include/asm/smp.h
++++ b/arch/riscv/include/asm/smp.h
+@@ -18,6 +18,13 @@
+ #include <linux/irqreturn.h>
+ #include <linux/thread_info.h>
++#define INVALID_HARTID ULONG_MAX
++/*
++ * Mapping between linux logical cpu index and hartid.
++ */
++extern unsigned long __cpuid_to_hartid_map[NR_CPUS];
++#define cpuid_to_hartid_map(cpu)    __cpuid_to_hartid_map[cpu]
++
+ #ifdef CONFIG_SMP
+ /* SMP initialization hook for setup_arch */
+@@ -29,12 +36,27 @@ void arch_send_call_function_ipi_mask(struct cpumask *mask);
+ /* Hook for the generic smp_call_function_single() routine. */
+ void arch_send_call_function_single_ipi(int cpu);
++int riscv_hartid_to_cpuid(int hartid);
++void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out);
++
+ /*
+  * Obtains the hart ID of the currently executing task.  This relies on
+  * THREAD_INFO_IN_TASK, but we define that unconditionally.
+  */
+ #define raw_smp_processor_id() (current_thread_info()->cpu)
+-#endif /* CONFIG_SMP */
++#else
++
++static inline int riscv_hartid_to_cpuid(int hartid)
++{
++      return 0;
++}
++static inline void riscv_cpuid_to_hartid_mask(const struct cpumask *in,
++                                            struct cpumask *out)
++{
++      cpumask_set_cpu(cpuid_to_hartid_map(0), out);
++}
++
++#endif /* CONFIG_SMP */
+ #endif /* _ASM_RISCV_SMP_H */
+diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c
+index b2d26d9..d5d8611 100644
+--- a/arch/riscv/kernel/setup.c
++++ b/arch/riscv/kernel/setup.c
+@@ -82,6 +82,10 @@ EXPORT_SYMBOL(empty_zero_page);
+ /* The lucky hart to first increment this variable will boot the other cores */
+ atomic_t hart_lottery;
++unsigned long __cpuid_to_hartid_map[NR_CPUS] = {
++      [0 ... NR_CPUS-1] = INVALID_HARTID
++};
++
+ #ifdef CONFIG_BLK_DEV_INITRD
+ static void __init setup_initrd(void)
+ {
+diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c
+index 906fe21..0bd4893 100644
+--- a/arch/riscv/kernel/smp.c
++++ b/arch/riscv/kernel/smp.c
+@@ -38,7 +38,26 @@ enum ipi_message_type {
+       IPI_MAX
+ };
++int riscv_hartid_to_cpuid(int hartid)
++{
++      int i = -1;
++
++      for (i = 0; i < NR_CPUS; i++)
++              if (cpuid_to_hartid_map(i) == hartid)
++                      return i;
++
++      pr_err("Couldn't find cpu id for hartid [%d]\n", hartid);
++      BUG();
++      return i;
++}
++void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out)
++{
++      int cpu;
++
++      for_each_cpu(cpu, in)
++              cpumask_set_cpu(cpuid_to_hartid_map(cpu), out);
++}
+ /* Unsupported */
+ int setup_profiling_timer(unsigned int multiplier)
+ {
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0022-RISC-V-Use-Linux-logical-CPU-number-instead-of-harti.patch b/target/linux/riscv64/patches-4.19/0022-RISC-V-Use-Linux-logical-CPU-number-instead-of-harti.patch
new file mode 100644 (file)
index 0000000..5ca9433
--- /dev/null
@@ -0,0 +1,322 @@
+From f99fb607fb2bc0d4ce6b9adb764c65e37f40a92b Mon Sep 17 00:00:00 2001
+From: Atish Patra <[email protected]>
+Date: Tue, 2 Oct 2018 12:15:05 -0700
+Subject: [PATCH 22/41] RISC-V: Use Linux logical CPU number instead of hartid
+
+Setup the cpu_logical_map during boot. Moreover, every SBI call
+and PLIC context are based on the physical hartid. Use the logical
+CPU to hartid mapping to pass correct hartid to respective functions.
+
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Anup Patel <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/include/asm/tlbflush.h | 16 +++++++++++++---
+ arch/riscv/kernel/cpu.c           |  8 +++++---
+ arch/riscv/kernel/head.S          |  4 +++-
+ arch/riscv/kernel/setup.c         |  6 ++++++
+ arch/riscv/kernel/smp.c           | 24 +++++++++++++++---------
+ arch/riscv/kernel/smpboot.c       | 25 ++++++++++++++++---------
+ drivers/clocksource/riscv_timer.c | 12 ++++++++----
+ drivers/irqchip/irq-sifive-plic.c |  8 +++++---
+ 8 files changed, 71 insertions(+), 32 deletions(-)
+
+diff --git a/arch/riscv/include/asm/tlbflush.h b/arch/riscv/include/asm/tlbflush.h
+index 85c2d8b..54fee0c 100644
+--- a/arch/riscv/include/asm/tlbflush.h
++++ b/arch/riscv/include/asm/tlbflush.h
+@@ -16,6 +16,7 @@
+ #define _ASM_RISCV_TLBFLUSH_H
+ #include <linux/mm_types.h>
++#include <asm/smp.h>
+ /*
+  * Flush entire local TLB.  'sfence.vma' implicitly fences with the instruction
+@@ -49,13 +50,22 @@ static inline void flush_tlb_range(struct vm_area_struct *vma,
+ #include <asm/sbi.h>
++static inline void remote_sfence_vma(struct cpumask *cmask, unsigned long start,
++                                   unsigned long size)
++{
++      struct cpumask hmask;
++
++      cpumask_clear(&hmask);
++      riscv_cpuid_to_hartid_mask(cmask, &hmask);
++      sbi_remote_sfence_vma(hmask.bits, start, size);
++}
++
+ #define flush_tlb_all() sbi_remote_sfence_vma(NULL, 0, -1)
+ #define flush_tlb_page(vma, addr) flush_tlb_range(vma, addr, 0)
+ #define flush_tlb_range(vma, start, end) \
+-      sbi_remote_sfence_vma(mm_cpumask((vma)->vm_mm)->bits, \
+-                            start, (end) - (start))
++      remote_sfence_vma(mm_cpumask((vma)->vm_mm), start, (end) - (start))
+ #define flush_tlb_mm(mm) \
+-      sbi_remote_sfence_vma(mm_cpumask(mm)->bits, 0, -1)
++      remote_sfence_vma(mm_cpumask(mm), 0, -1)
+ #endif /* CONFIG_SMP */
+diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
+index 4723e23..cccc6f6 100644
+--- a/arch/riscv/kernel/cpu.c
++++ b/arch/riscv/kernel/cpu.c
+@@ -14,6 +14,7 @@
+ #include <linux/init.h>
+ #include <linux/seq_file.h>
+ #include <linux/of.h>
++#include <asm/smp.h>
+ /*
+  * Returns the hart ID of the given device tree node, or -1 if the device tree
+@@ -138,11 +139,12 @@ static void c_stop(struct seq_file *m, void *v)
+ static int c_show(struct seq_file *m, void *v)
+ {
+-      unsigned long hart_id = (unsigned long)v - 1;
+-      struct device_node *node = of_get_cpu_node(hart_id, NULL);
++      unsigned long cpu_id = (unsigned long)v - 1;
++      struct device_node *node = of_get_cpu_node(cpuid_to_hartid_map(cpu_id),
++                                                 NULL);
+       const char *compat, *isa, *mmu;
+-      seq_printf(m, "hart\t: %lu\n", hart_id);
++      seq_printf(m, "hart\t: %lu\n", cpu_id);
+       if (!of_property_read_string(node, "riscv,isa", &isa))
+               print_isa(m, isa);
+       if (!of_property_read_string(node, "mmu-type", &mmu))
+diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S
+index c4d2c63..711190d 100644
+--- a/arch/riscv/kernel/head.S
++++ b/arch/riscv/kernel/head.S
+@@ -47,6 +47,8 @@ ENTRY(_start)
+       /* Save hart ID and DTB physical address */
+       mv s0, a0
+       mv s1, a1
++      la a2, boot_cpu_hartid
++      REG_S a0, (a2)
+       /* Initialize page tables and relocate to virtual addresses */
+       la sp, init_thread_union + THREAD_SIZE
+@@ -55,7 +57,7 @@ ENTRY(_start)
+       /* Restore C environment */
+       la tp, init_task
+-      sw s0, TASK_TI_CPU(tp)
++      sw zero, TASK_TI_CPU(tp)
+       la sp, init_thread_union
+       li a0, ASM_THREAD_SIZE
+diff --git a/arch/riscv/kernel/setup.c b/arch/riscv/kernel/setup.c
+index d5d8611..5e9e6f9 100644
+--- a/arch/riscv/kernel/setup.c
++++ b/arch/riscv/kernel/setup.c
+@@ -81,11 +81,17 @@ EXPORT_SYMBOL(empty_zero_page);
+ /* The lucky hart to first increment this variable will boot the other cores */
+ atomic_t hart_lottery;
++unsigned long boot_cpu_hartid;
+ unsigned long __cpuid_to_hartid_map[NR_CPUS] = {
+       [0 ... NR_CPUS-1] = INVALID_HARTID
+ };
++void __init smp_setup_processor_id(void)
++{
++      cpuid_to_hartid_map(0) = boot_cpu_hartid;
++}
++
+ #ifdef CONFIG_BLK_DEV_INITRD
+ static void __init setup_initrd(void)
+ {
+diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c
+index 0bd4893..4eac009 100644
+--- a/arch/riscv/kernel/smp.c
++++ b/arch/riscv/kernel/smp.c
+@@ -97,14 +97,18 @@ void riscv_software_interrupt(void)
+ static void
+ send_ipi_message(const struct cpumask *to_whom, enum ipi_message_type operation)
+ {
+-      int i;
++      int cpuid, hartid;
++      struct cpumask hartid_mask;
++      cpumask_clear(&hartid_mask);
+       mb();
+-      for_each_cpu(i, to_whom)
+-              set_bit(operation, &ipi_data[i].bits);
+-
++      for_each_cpu(cpuid, to_whom) {
++              set_bit(operation, &ipi_data[cpuid].bits);
++              hartid = cpuid_to_hartid_map(cpuid);
++              cpumask_set_cpu(hartid, &hartid_mask);
++      }
+       mb();
+-      sbi_send_ipi(cpumask_bits(to_whom));
++      sbi_send_ipi(cpumask_bits(&hartid_mask));
+ }
+ void arch_send_call_function_ipi_mask(struct cpumask *mask)
+@@ -146,7 +150,7 @@ void smp_send_reschedule(int cpu)
+ void flush_icache_mm(struct mm_struct *mm, bool local)
+ {
+       unsigned int cpu;
+-      cpumask_t others, *mask;
++      cpumask_t others, hmask, *mask;
+       preempt_disable();
+@@ -164,9 +168,11 @@ void flush_icache_mm(struct mm_struct *mm, bool local)
+        */
+       cpumask_andnot(&others, mm_cpumask(mm), cpumask_of(cpu));
+       local |= cpumask_empty(&others);
+-      if (mm != current->active_mm || !local)
+-              sbi_remote_fence_i(others.bits);
+-      else {
++      if (mm != current->active_mm || !local) {
++              cpumask_clear(&hmask);
++              riscv_cpuid_to_hartid_mask(&others, &hmask);
++              sbi_remote_fence_i(hmask.bits);
++      } else {
+               /*
+                * It's assumed that at least one strongly ordered operation is
+                * performed on this hart between setting a hart's cpumask bit
+diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
+index 1e47861..18cda0e 100644
+--- a/arch/riscv/kernel/smpboot.c
++++ b/arch/riscv/kernel/smpboot.c
+@@ -53,17 +53,23 @@ void __init setup_smp(void)
+       struct device_node *dn = NULL;
+       int hart;
+       bool found_boot_cpu = false;
++      int cpuid = 1;
+       while ((dn = of_find_node_by_type(dn, "cpu"))) {
+               hart = riscv_of_processor_hartid(dn);
+-              if (hart >= 0) {
+-                      set_cpu_possible(hart, true);
+-                      set_cpu_present(hart, true);
+-                      if (hart == smp_processor_id()) {
+-                              BUG_ON(found_boot_cpu);
+-                              found_boot_cpu = true;
+-                      }
++              if (hart < 0)
++                      continue;
++
++              if (hart == cpuid_to_hartid_map(0)) {
++                      BUG_ON(found_boot_cpu);
++                      found_boot_cpu = 1;
++                      continue;
+               }
++
++              cpuid_to_hartid_map(cpuid) = hart;
++              set_cpu_possible(cpuid, true);
++              set_cpu_present(cpuid, true);
++              cpuid++;
+       }
+       BUG_ON(!found_boot_cpu);
+@@ -71,6 +77,7 @@ void __init setup_smp(void)
+ int __cpu_up(unsigned int cpu, struct task_struct *tidle)
+ {
++      int hartid = cpuid_to_hartid_map(cpu);
+       tidle->thread_info.cpu = cpu;
+       /*
+@@ -81,9 +88,9 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle)
+        * the spinning harts that they can continue the boot process.
+        */
+       smp_mb();
+-      WRITE_ONCE(__cpu_up_stack_pointer[cpu],
++      WRITE_ONCE(__cpu_up_stack_pointer[hartid],
+                 task_stack_page(tidle) + THREAD_SIZE);
+-      WRITE_ONCE(__cpu_up_task_pointer[cpu], tidle);
++      WRITE_ONCE(__cpu_up_task_pointer[hartid], tidle);
+       while (!cpu_online(cpu))
+               cpu_relax();
+diff --git a/drivers/clocksource/riscv_timer.c b/drivers/clocksource/riscv_timer.c
+index ad7453f..084e97d 100644
+--- a/drivers/clocksource/riscv_timer.c
++++ b/drivers/clocksource/riscv_timer.c
+@@ -8,6 +8,7 @@
+ #include <linux/cpu.h>
+ #include <linux/delay.h>
+ #include <linux/irq.h>
++#include <asm/smp.h>
+ #include <asm/sbi.h>
+ /*
+@@ -84,13 +85,16 @@ void riscv_timer_interrupt(void)
+ static int __init riscv_timer_init_dt(struct device_node *n)
+ {
+-      int cpu_id = riscv_of_processor_hartid(n), error;
++      int cpuid, hartid, error;
+       struct clocksource *cs;
+-      if (cpu_id != smp_processor_id())
++      hartid = riscv_of_processor_hartid(n);
++      cpuid = riscv_hartid_to_cpuid(hartid);
++
++      if (cpuid != smp_processor_id())
+               return 0;
+-      cs = per_cpu_ptr(&riscv_clocksource, cpu_id);
++      cs = per_cpu_ptr(&riscv_clocksource, cpuid);
+       clocksource_register_hz(cs, riscv_timebase);
+       error = cpuhp_setup_state(CPUHP_AP_RISCV_TIMER_STARTING,
+@@ -98,7 +102,7 @@ static int __init riscv_timer_init_dt(struct device_node *n)
+                        riscv_timer_starting_cpu, riscv_timer_dying_cpu);
+       if (error)
+               pr_err("RISCV timer register failed [%d] for cpu = [%d]\n",
+-                     error, cpu_id);
++                     error, cpuid);
+       return error;
+ }
+diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
+index c55eaa3..357e9da 100644
+--- a/drivers/irqchip/irq-sifive-plic.c
++++ b/drivers/irqchip/irq-sifive-plic.c
+@@ -15,6 +15,7 @@
+ #include <linux/of_irq.h>
+ #include <linux/platform_device.h>
+ #include <linux/spinlock.h>
++#include <asm/smp.h>
+ /*
+  * This driver implements a version of the RISC-V PLIC with the actual layout
+@@ -218,7 +219,7 @@ static int __init plic_init(struct device_node *node,
+               struct of_phandle_args parent;
+               struct plic_handler *handler;
+               irq_hw_number_t hwirq;
+-              int cpu;
++              int cpu, hartid;
+               if (of_irq_parse_one(node, i, &parent)) {
+                       pr_err("failed to parse parent for context %d.\n", i);
+@@ -229,12 +230,13 @@ static int __init plic_init(struct device_node *node,
+               if (parent.args[0] == -1)
+                       continue;
+-              cpu = plic_find_hart_id(parent.np);
+-              if (cpu < 0) {
++              hartid = plic_find_hart_id(parent.np);
++              if (hartid < 0) {
+                       pr_warn("failed to parse hart ID for context %d.\n", i);
+                       continue;
+               }
++              cpu = riscv_hartid_to_cpuid(hartid);
+               handler = per_cpu_ptr(&plic_handlers, cpu);
+               handler->present = true;
+               handler->ctxid = i;
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0023-RISC-V-Show-CPU-ID-and-Hart-ID-separately-in-proc-cp.patch b/target/linux/riscv64/patches-4.19/0023-RISC-V-Show-CPU-ID-and-Hart-ID-separately-in-proc-cp.patch
new file mode 100644 (file)
index 0000000..2ef4e11
--- /dev/null
@@ -0,0 +1,89 @@
+From 4b26d22fdff1e39647cc5952b01d329e83dedfe1 Mon Sep 17 00:00:00 2001
+From: Anup Patel <[email protected]>
+Date: Tue, 2 Oct 2018 12:15:06 -0700
+Subject: [PATCH 23/41] RISC-V: Show CPU ID and Hart ID separately in
+ /proc/cpuinfo
+
+Currently, /proc/cpuinfo show logical CPU ID as Hart ID which
+is in-correct. This patch shows CPU ID and Hart ID separately
+in /proc/cpuinfo using cpuid_to_hardid_map().
+
+With this patch, contents of /proc/cpuinfo looks as follows:
+processor      : 0
+hart           : 1
+isa            : rv64imafdc
+mmu            : sv48
+
+processor      : 1
+hart           : 0
+isa            : rv64imafdc
+mmu            : sv48
+
+processor      : 2
+hart           : 2
+isa            : rv64imafdc
+mmu            : sv48
+
+processor      : 3
+hart           : 3
+isa            : rv64imafdc
+mmu            : sv48
+
+Signed-off-by: Anup Patel <[email protected]>
+Signed-off-by: Atish Patra <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/cpu.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
+index cccc6f6..3a5a2ee 100644
+--- a/arch/riscv/kernel/cpu.c
++++ b/arch/riscv/kernel/cpu.c
+@@ -81,7 +81,7 @@ static void print_isa(struct seq_file *f, const char *orig_isa)
+ #endif
+       /* Print the base ISA, as we already know it's legal. */
+-      seq_puts(f, "isa\t: ");
++      seq_puts(f, "isa\t\t: ");
+       seq_write(f, isa, 5);
+       isa += 5;
+@@ -96,6 +96,7 @@ static void print_isa(struct seq_file *f, const char *orig_isa)
+                       isa++;
+               }
+       }
++      seq_puts(f, "\n");
+       /*
+        * If we were given an unsupported ISA in the device tree then print
+@@ -116,7 +117,7 @@ static void print_mmu(struct seq_file *f, const char *mmu_type)
+               return;
+ #endif
+-      seq_printf(f, "mmu\t: %s\n", mmu_type+6);
++      seq_printf(f, "mmu\t\t: %s\n", mmu_type+6);
+ }
+ static void *c_start(struct seq_file *m, loff_t *pos)
+@@ -144,14 +145,15 @@ static int c_show(struct seq_file *m, void *v)
+                                                  NULL);
+       const char *compat, *isa, *mmu;
+-      seq_printf(m, "hart\t: %lu\n", cpu_id);
++      seq_printf(m, "processor\t: %lu\n", cpu_id);
++      seq_printf(m, "hart\t\t: %lu\n", cpuid_to_hartid_map(cpu_id));
+       if (!of_property_read_string(node, "riscv,isa", &isa))
+               print_isa(m, isa);
+       if (!of_property_read_string(node, "mmu-type", &mmu))
+               print_mmu(m, mmu);
+       if (!of_property_read_string(node, "compatible", &compat)
+           && strcmp(compat, "riscv"))
+-              seq_printf(m, "uarch\t: %s\n", compat);
++              seq_printf(m, "uarch\t\t: %s\n", compat);
+       seq_puts(m, "\n");
+       return 0;
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0024-RISC-V-Show-IPI-stats.patch b/target/linux/riscv64/patches-4.19/0024-RISC-V-Show-IPI-stats.patch
new file mode 100644 (file)
index 0000000..460d373
--- /dev/null
@@ -0,0 +1,178 @@
+From 8b20d2db0a6d2761e0fc156eb74f7a55b92b3147 Mon Sep 17 00:00:00 2001
+From: Anup Patel <[email protected]>
+Date: Tue, 2 Oct 2018 12:15:07 -0700
+Subject: [PATCH 24/41] RISC-V: Show IPI stats
+
+This patch provides arch_show_interrupts() implementation to
+show IPI stats via /proc/interrupts.
+
+Now the contents of /proc/interrupts" will look like below:
+           CPU0       CPU1       CPU2       CPU3
+  8:         17          7          6         14  SiFive PLIC   8  virtio0
+ 10:         10         10          9         11  SiFive PLIC  10  ttyS0
+IPI0:       170        673        251         79  Rescheduling interrupts
+IPI1:         1         12         27          1  Function call interrupts
+
+Signed-off-by: Anup Patel <[email protected]>
+[Atish - Fixed checkpatch errors]
+Signed-off-by: Atish Patra <[email protected]>
+Reviewed-by: Palmer Dabbelt <[email protected]>
+
+Changes since v2:
+ - Remove use of IPI_CALL_WAKEUP because it's being removed
+
+Changes since v1:
+ - Add stub inline show_ipi_stats() function for !CONFIG_SMP
+ - Make ipi_names[] dynamically sized at compile time
+ - Minor beautification of ipi_names[] using tabs
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/include/asm/smp.h |  9 +++++++++
+ arch/riscv/kernel/irq.c      |  8 ++++++++
+ arch/riscv/kernel/smp.c      | 39 ++++++++++++++++++++++++++++++++-------
+ 3 files changed, 49 insertions(+), 7 deletions(-)
+
+diff --git a/arch/riscv/include/asm/smp.h b/arch/riscv/include/asm/smp.h
+index 47fd61df..41aa73b 100644
+--- a/arch/riscv/include/asm/smp.h
++++ b/arch/riscv/include/asm/smp.h
+@@ -25,8 +25,13 @@
+ extern unsigned long __cpuid_to_hartid_map[NR_CPUS];
+ #define cpuid_to_hartid_map(cpu)    __cpuid_to_hartid_map[cpu]
++struct seq_file;
++
+ #ifdef CONFIG_SMP
++/* print IPI stats */
++void show_ipi_stats(struct seq_file *p, int prec);
++
+ /* SMP initialization hook for setup_arch */
+ void __init setup_smp(void);
+@@ -47,6 +52,10 @@ void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out);
+ #else
++static inline void show_ipi_stats(struct seq_file *p, int prec)
++{
++}
++
+ static inline int riscv_hartid_to_cpuid(int hartid)
+ {
+       return 0;
+diff --git a/arch/riscv/kernel/irq.c b/arch/riscv/kernel/irq.c
+index ca45933..48e6b7d 100644
+--- a/arch/riscv/kernel/irq.c
++++ b/arch/riscv/kernel/irq.c
+@@ -8,6 +8,8 @@
+ #include <linux/interrupt.h>
+ #include <linux/irqchip.h>
+ #include <linux/irqdomain.h>
++#include <linux/seq_file.h>
++#include <asm/smp.h>
+ /*
+  * Possible interrupt causes:
+@@ -24,6 +26,12 @@
+  */
+ #define INTERRUPT_CAUSE_FLAG  (1UL << (__riscv_xlen - 1))
++int arch_show_interrupts(struct seq_file *p, int prec)
++{
++      show_ipi_stats(p, prec);
++      return 0;
++}
++
+ asmlinkage void __irq_entry do_IRQ(struct pt_regs *regs)
+ {
+       struct pt_regs *old_regs = set_irq_regs(regs);
+diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c
+index 4eac009..57b1383 100644
+--- a/arch/riscv/kernel/smp.c
++++ b/arch/riscv/kernel/smp.c
+@@ -22,22 +22,24 @@
+ #include <linux/interrupt.h>
+ #include <linux/smp.h>
+ #include <linux/sched.h>
++#include <linux/seq_file.h>
+ #include <asm/sbi.h>
+ #include <asm/tlbflush.h>
+ #include <asm/cacheflush.h>
+-/* A collection of single bit ipi messages.  */
+-static struct {
+-      unsigned long bits ____cacheline_aligned;
+-} ipi_data[NR_CPUS] __cacheline_aligned;
+-
+ enum ipi_message_type {
+       IPI_RESCHEDULE,
+       IPI_CALL_FUNC,
+       IPI_MAX
+ };
++/* A collection of single bit ipi messages.  */
++static struct {
++      unsigned long stats[IPI_MAX] ____cacheline_aligned;
++      unsigned long bits ____cacheline_aligned;
++} ipi_data[NR_CPUS] __cacheline_aligned;
++
+ int riscv_hartid_to_cpuid(int hartid)
+ {
+       int i = -1;
+@@ -67,6 +69,7 @@ int setup_profiling_timer(unsigned int multiplier)
+ void riscv_software_interrupt(void)
+ {
+       unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits;
++      unsigned long *stats = ipi_data[smp_processor_id()].stats;
+       /* Clear pending IPI */
+       csr_clear(sip, SIE_SSIE);
+@@ -81,11 +84,15 @@ void riscv_software_interrupt(void)
+               if (ops == 0)
+                       return;
+-              if (ops & (1 << IPI_RESCHEDULE))
++              if (ops & (1 << IPI_RESCHEDULE)) {
++                      stats[IPI_RESCHEDULE]++;
+                       scheduler_ipi();
++              }
+-              if (ops & (1 << IPI_CALL_FUNC))
++              if (ops & (1 << IPI_CALL_FUNC)) {
++                      stats[IPI_CALL_FUNC]++;
+                       generic_smp_call_function_interrupt();
++              }
+               BUG_ON((ops >> IPI_MAX) != 0);
+@@ -111,6 +118,24 @@ send_ipi_message(const struct cpumask *to_whom, enum ipi_message_type operation)
+       sbi_send_ipi(cpumask_bits(&hartid_mask));
+ }
++static const char * const ipi_names[] = {
++      [IPI_RESCHEDULE]        = "Rescheduling interrupts",
++      [IPI_CALL_FUNC]         = "Function call interrupts",
++};
++
++void show_ipi_stats(struct seq_file *p, int prec)
++{
++      unsigned int cpu, i;
++
++      for (i = 0; i < IPI_MAX; i++) {
++              seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i,
++                         prec >= 4 ? " " : "");
++              for_each_online_cpu(cpu)
++                      seq_printf(p, "%10lu ", ipi_data[cpu].stats[i]);
++              seq_printf(p, " %s\n", ipi_names[i]);
++      }
++}
++
+ void arch_send_call_function_ipi_mask(struct cpumask *mask)
+ {
+       send_ipi_message(mask, IPI_CALL_FUNC);
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0026-RISC-V-Mask-out-the-F-extension-on-systems-without-D.patch b/target/linux/riscv64/patches-4.19/0026-RISC-V-Mask-out-the-F-extension-on-systems-without-D.patch
new file mode 100644 (file)
index 0000000..f646196
--- /dev/null
@@ -0,0 +1,43 @@
+From 86e581e310785782e2025a076dc9a3f5138e5bf3 Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Mon, 27 Aug 2018 14:42:53 -0700
+Subject: [PATCH 26/41] RISC-V: Mask out the F extension on systems without D
+
+The RISC-V Linux port doesn't support systems that have the F extension
+but don't have the D extension -- we actually don't support systems
+without D either, but Alan's patch set is rectifying that soon.  For now
+I think we can leave this in a semi-broken state and just wait for
+Alan's patch set to get merged for proper non-FPU support -- the patch
+set is starting to look good, so doing something in-between doesn't seem
+like it's worth the work.
+
+I don't think it's worth fretting about support for systems with F but
+not D for now: our glibc ABIs are IMAC and IMAFDC so they probably won't
+end up being popular.  We can always extend this in the future.
+
+CC: Alan Kao <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/cpufeature.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
+index 17011a8..652d102 100644
+--- a/arch/riscv/kernel/cpufeature.c
++++ b/arch/riscv/kernel/cpufeature.c
+@@ -57,5 +57,12 @@ void riscv_fill_hwcap(void)
+       for (i = 0; i < strlen(isa); ++i)
+               elf_hwcap |= isa2hwcap[(unsigned char)(isa[i])];
++      /* We don't support systems with F but without D, so mask those out
++       * here. */
++      if ((elf_hwcap & COMPAT_HWCAP_ISA_F) && !(elf_hwcap & COMPAT_HWCAP_ISA_D)) {
++              pr_info("This kernel does not support systems with F but not D");
++              elf_hwcap &= ~COMPAT_HWCAP_ISA_F;
++      }
++
+       pr_info("elf_hwcap is 0x%lx", elf_hwcap);
+ }
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0027-RISC-V-Add-FP-register-ptrace-support-for-gdb.patch b/target/linux/riscv64/patches-4.19/0027-RISC-V-Add-FP-register-ptrace-support-for-gdb.patch
new file mode 100644 (file)
index 0000000..0c0e523
--- /dev/null
@@ -0,0 +1,112 @@
+From b8c8a9590e4fde82f8c3ee06a521763e6f21e9c8 Mon Sep 17 00:00:00 2001
+From: Jim Wilson <[email protected]>
+Date: Wed, 17 Oct 2018 17:59:05 -0700
+Subject: [PATCH 27/41] RISC-V: Add FP register ptrace support for gdb.
+
+Add a variable and a macro to describe FP registers, assuming only D is
+supported.  FP code is conditional on CONFIG_FPU.  The FP regs and FCSR
+are copied separately to avoid copying struct padding.  Tested by hand and
+with the gdb testsuite.
+
+Signed-off-by: Jim Wilson <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/include/uapi/asm/elf.h |  3 +++
+ arch/riscv/kernel/ptrace.c        | 52 +++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 55 insertions(+)
+
+diff --git a/arch/riscv/include/uapi/asm/elf.h b/arch/riscv/include/uapi/asm/elf.h
+index 1e0dfc3..644a00c 100644
+--- a/arch/riscv/include/uapi/asm/elf.h
++++ b/arch/riscv/include/uapi/asm/elf.h
+@@ -19,7 +19,10 @@ typedef unsigned long elf_greg_t;
+ typedef struct user_regs_struct elf_gregset_t;
+ #define ELF_NGREG (sizeof(elf_gregset_t) / sizeof(elf_greg_t))
++/* We don't support f without d, or q.  */
++typedef __u64 elf_fpreg_t;
+ typedef union __riscv_fp_state elf_fpregset_t;
++#define ELF_NFPREG (sizeof(struct __riscv_d_ext_state) / sizeof(elf_fpreg_t))
+ #if __riscv_xlen == 64
+ #define ELF_RISCV_R_SYM(r_info)               ELF64_R_SYM(r_info)
+diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
+index 9f82a7e..60f1e02 100644
+--- a/arch/riscv/kernel/ptrace.c
++++ b/arch/riscv/kernel/ptrace.c
+@@ -28,6 +28,9 @@
+ enum riscv_regset {
+       REGSET_X,
++#ifdef CONFIG_FPU
++      REGSET_F,
++#endif
+ };
+ static int riscv_gpr_get(struct task_struct *target,
+@@ -54,6 +57,45 @@ static int riscv_gpr_set(struct task_struct *target,
+       return ret;
+ }
++#ifdef CONFIG_FPU
++static int riscv_fpr_get(struct task_struct *target,
++                       const struct user_regset *regset,
++                       unsigned int pos, unsigned int count,
++                       void *kbuf, void __user *ubuf)
++{
++      int ret;
++      struct __riscv_d_ext_state *fstate = &target->thread.fstate;
++
++      ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0,
++                                offsetof(struct __riscv_d_ext_state, fcsr));
++      if (!ret) {
++              ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, fstate, 0,
++                                        offsetof(struct __riscv_d_ext_state, fcsr) +
++                                        sizeof(fstate->fcsr));
++      }
++
++      return ret;
++}
++
++static int riscv_fpr_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;
++      struct __riscv_d_ext_state *fstate = &target->thread.fstate;
++
++      ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0,
++                               offsetof(struct __riscv_d_ext_state, fcsr));
++      if (!ret) {
++              ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, fstate, 0,
++                                       offsetof(struct __riscv_d_ext_state, fcsr) +
++                                       sizeof(fstate->fcsr));
++      }
++
++      return ret;
++}
++#endif
+ static const struct user_regset riscv_user_regset[] = {
+       [REGSET_X] = {
+@@ -64,6 +106,16 @@ static const struct user_regset riscv_user_regset[] = {
+               .get = &riscv_gpr_get,
+               .set = &riscv_gpr_set,
+       },
++#ifdef CONFIG_FPU
++      [REGSET_F] = {
++              .core_note_type = NT_PRFPREG,
++              .n = ELF_NFPREG,
++              .size = sizeof(elf_fpreg_t),
++              .align = sizeof(elf_fpreg_t),
++              .get = &riscv_fpr_get,
++              .set = &riscv_fpr_set,
++      },
++#endif
+ };
+ static const struct user_regset_view riscv_user_native_view = {
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0028-RISC-V-Add-futex-support.patch b/target/linux/riscv64/patches-4.19/0028-RISC-V-Add-futex-support.patch
new file mode 100644 (file)
index 0000000..8d1ce58
--- /dev/null
@@ -0,0 +1,200 @@
+From b90edb33010bcfb9a0d74681be2cdd52300f1e69 Mon Sep 17 00:00:00 2001
+From: Jim Wilson <[email protected]>
+Date: Tue, 16 Oct 2018 14:42:59 -0700
+Subject: [PATCH 28/41] RISC-V: Add futex support.
+
+Here is an attempt to add the missing futex support.  I started with the MIPS
+version of futex.h and modified it until I got it working.  I tested it on
+a HiFive Unleashed running Fedora Core 29 using the fc29 4.15 version of the
+kernel.  This was tested against the glibc testsuite, where it fixes 14 nptl
+related testsuite failures.  That unfortunately only tests the cmpxchg support,
+so I also used the testcase at the end of
+
+    https://lwn.net/Articles/148830/
+
+which tests the atomic_op functionality, except that it doesn't verify that
+the operations are atomic, which they obviously are.  This testcase runs
+successfully with the patch and fails without it.
+
+I'm not a kernel expert, so there could be details I got wrong here.  I wasn't
+sure about the memory model support, so I used aqrl which seemed safest, and
+didn't add fences which seemed unnecessary.  I'm not sure about the copyright
+statements, I left in Ralf Baechle's line because I started with his code.
+Checkpatch reports some style problems, but it is the same style as the MIPS
+futex.h, and the uses of ENOSYS appear correct even though it complains about
+them.  I don't know if any of that matters.
+
+This patch was tested on qemu with the glibc nptl/tst-cond-except
+testcase, and the wake_op testcase from above.
+
+Signed-off-by: Jim Wilson <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/Kconfig             |   1 +
+ arch/riscv/include/asm/Kbuild  |   1 -
+ arch/riscv/include/asm/futex.h | 128 +++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 129 insertions(+), 1 deletion(-)
+ create mode 100644 arch/riscv/include/asm/futex.h
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index a344980..093361e 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -31,6 +31,7 @@ config RISCV
+       select HAVE_MEMBLOCK
+       select HAVE_MEMBLOCK_NODE_MAP
+       select HAVE_DMA_CONTIGUOUS
++      select HAVE_FUTEX_CMPXCHG if FUTEX
+       select HAVE_GENERIC_DMA_COHERENT
+       select HAVE_PERF_EVENTS
+       select IRQ_DOMAIN
+diff --git a/arch/riscv/include/asm/Kbuild b/arch/riscv/include/asm/Kbuild
+index efdbe31..6a646d9 100644
+--- a/arch/riscv/include/asm/Kbuild
++++ b/arch/riscv/include/asm/Kbuild
+@@ -13,7 +13,6 @@ generic-y += errno.h
+ generic-y += exec.h
+ generic-y += fb.h
+ generic-y += fcntl.h
+-generic-y += futex.h
+ generic-y += hardirq.h
+ generic-y += hash.h
+ generic-y += hw_irq.h
+diff --git a/arch/riscv/include/asm/futex.h b/arch/riscv/include/asm/futex.h
+new file mode 100644
+index 0000000..3b19eba
+--- /dev/null
++++ b/arch/riscv/include/asm/futex.h
+@@ -0,0 +1,128 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2006  Ralf Baechle ([email protected])
++ * Copyright (c) 2018  Jim Wilson ([email protected])
++ */
++
++#ifndef _ASM_FUTEX_H
++#define _ASM_FUTEX_H
++
++#ifndef CONFIG_RISCV_ISA_A
++/*
++ * Use the generic interrupt disabling versions if the A extension
++ * is not supported.
++ */
++#ifdef CONFIG_SMP
++#error "Can't support generic futex calls without A extension on SMP"
++#endif
++#include <asm-generic/futex.h>
++
++#else /* CONFIG_RISCV_ISA_A */
++
++#include <linux/futex.h>
++#include <linux/uaccess.h>
++#include <linux/errno.h>
++#include <asm/asm.h>
++
++#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg)    \
++{                                                             \
++      uintptr_t tmp;                                          \
++      __enable_user_access();                                 \
++      __asm__ __volatile__ (                                  \
++      "1:     " insn "                                \n"     \
++      "2:                                             \n"     \
++      "       .section .fixup,\"ax\"                  \n"     \
++      "       .balign 4                               \n"     \
++      "3:     li %[r],%[e]                            \n"     \
++      "       jump 2b,%[t]                            \n"     \
++      "       .previous                               \n"     \
++      "       .section __ex_table,\"a\"               \n"     \
++      "       .balign " RISCV_SZPTR "                 \n"     \
++      "       " RISCV_PTR " 1b, 3b                    \n"     \
++      "       .previous                               \n"     \
++      : [r] "+r" (ret), [ov] "=&r" (oldval),                  \
++        [u] "+m" (*uaddr), [t] "=&r" (tmp)                    \
++      : [op] "Jr" (oparg), [e] "i" (-EFAULT)                  \
++      : "memory");                                            \
++      __disable_user_access();                                \
++}
++
++static inline int
++arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
++{
++      int oldval = 0, ret = 0;
++
++      pagefault_disable();
++
++      switch (op) {
++      case FUTEX_OP_SET:
++              __futex_atomic_op("amoswap.w.aqrl %[ov],%z[op],%[u]",
++                                ret, oldval, uaddr, oparg);
++              break;
++      case FUTEX_OP_ADD:
++              __futex_atomic_op("amoadd.w.aqrl %[ov],%z[op],%[u]",
++                                ret, oldval, uaddr, oparg);
++              break;
++      case FUTEX_OP_OR:
++              __futex_atomic_op("amoor.w.aqrl %[ov],%z[op],%[u]",
++                                ret, oldval, uaddr, oparg);
++              break;
++      case FUTEX_OP_ANDN:
++              __futex_atomic_op("amoand.w.aqrl %[ov],%z[op],%[u]",
++                                ret, oldval, uaddr, ~oparg);
++              break;
++      case FUTEX_OP_XOR:
++              __futex_atomic_op("amoxor.w.aqrl %[ov],%z[op],%[u]",
++                                ret, oldval, uaddr, oparg);
++              break;
++      default:
++              ret = -ENOSYS;
++      }
++
++      pagefault_enable();
++
++      if (!ret)
++              *oval = oldval;
++
++      return ret;
++}
++
++static inline int
++futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
++                            u32 oldval, u32 newval)
++{
++      int ret = 0;
++      u32 val;
++      uintptr_t tmp;
++
++      if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
++              return -EFAULT;
++
++      __enable_user_access();
++      __asm__ __volatile__ (
++      "1:     lr.w.aqrl %[v],%[u]                     \n"
++      "       bne %[v],%z[ov],3f                      \n"
++      "2:     sc.w.aqrl %[t],%z[nv],%[u]              \n"
++      "       bnez %[t],1b                            \n"
++      "3:                                             \n"
++      "       .section .fixup,\"ax\"                  \n"
++      "       .balign 4                               \n"
++      "4:     li %[r],%[e]                            \n"
++      "       jump 3b,%[t]                            \n"
++      "       .previous                               \n"
++      "       .section __ex_table,\"a\"               \n"
++      "       .balign " RISCV_SZPTR "                 \n"
++      "       " RISCV_PTR " 1b, 4b                    \n"
++      "       " RISCV_PTR " 2b, 4b                    \n"
++      "       .previous                               \n"
++      : [r] "+r" (ret), [v] "=&r" (val), [u] "+m" (*uaddr), [t] "=&r" (tmp)
++      : [ov] "Jr" (oldval), [nv] "Jr" (newval), [e] "i" (-EFAULT)
++      : "memory");
++      __disable_user_access();
++
++      *uval = val;
++      return ret;
++}
++
++#endif /* CONFIG_RISCV_ISA_A */
++#endif /* _ASM_FUTEX_H */
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0029-RISC-V-remove-the-unused-return_to_handler-export.patch b/target/linux/riscv64/patches-4.19/0029-RISC-V-remove-the-unused-return_to_handler-export.patch
new file mode 100644 (file)
index 0000000..6498e47
--- /dev/null
@@ -0,0 +1,29 @@
+From f31b8de98853091e86a6391f9cd7948a2397287e Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <[email protected]>
+Date: Fri, 21 Sep 2018 08:43:21 +0200
+Subject: [PATCH 29/41] RISC-V: remove the unused return_to_handler export
+
+This export is not only not needed, but also breaks symbol versioning
+due to being an undeclared assembly export.
+
+Signed-off-by: Christoph Hellwig <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/mcount.S | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/arch/riscv/kernel/mcount.S b/arch/riscv/kernel/mcount.S
+index 5721624..8a5593f 100644
+--- a/arch/riscv/kernel/mcount.S
++++ b/arch/riscv/kernel/mcount.S
+@@ -75,7 +75,6 @@ ENTRY(return_to_handler)
+       RESTORE_RET_ABI_STATE
+       jalr    a1
+ ENDPROC(return_to_handler)
+-EXPORT_SYMBOL(return_to_handler)
+ #endif
+ #ifndef CONFIG_DYNAMIC_FTRACE
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0030-riscv-move-GCC-version-check-for-ARCH_SUPPORTS_INT12.patch b/target/linux/riscv64/patches-4.19/0030-riscv-move-GCC-version-check-for-ARCH_SUPPORTS_INT12.patch
new file mode 100644 (file)
index 0000000..2166e9b
--- /dev/null
@@ -0,0 +1,43 @@
+From ee5928843a93c7d246bbe17c5eed95918ed0ddb1 Mon Sep 17 00:00:00 2001
+From: Masahiro Yamada <[email protected]>
+Date: Fri, 24 Aug 2018 17:33:53 +0900
+Subject: [PATCH 30/41] riscv: move GCC version check for ARCH_SUPPORTS_INT128
+ to Kconfig
+
+This becomes much neater in Kconfig.
+
+Signed-off-by: Masahiro Yamada <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/Kconfig  | 1 +
+ arch/riscv/Makefile | 2 --
+ 2 files changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index 093361e..bb80d3e 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -113,6 +113,7 @@ config ARCH_RV32I
+ config ARCH_RV64I
+       bool "RV64I"
+       select 64BIT
++      select ARCH_SUPPORTS_INT128 if GCC_VERSION >= 50000
+       select HAVE_FUNCTION_TRACER
+       select HAVE_FUNCTION_GRAPH_TRACER
+       select HAVE_FTRACE_MCOUNT_RECORD
+diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
+index 61ec424..33700e4 100644
+--- a/arch/riscv/Makefile
++++ b/arch/riscv/Makefile
+@@ -25,8 +25,6 @@ ifeq ($(CONFIG_ARCH_RV64I),y)
+       KBUILD_CFLAGS += -mabi=lp64
+       KBUILD_AFLAGS += -mabi=lp64
+-      
+-      KBUILD_CFLAGS   += $(call cc-ifversion, -ge, 0500, -DCONFIG_ARCH_SUPPORTS_INT128)
+       KBUILD_MARCH = rv64im
+       KBUILD_LDFLAGS += -melf64lriscv
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0031-RISC-V-Cosmetic-menuconfig-changes.patch b/target/linux/riscv64/patches-4.19/0031-RISC-V-Cosmetic-menuconfig-changes.patch
new file mode 100644 (file)
index 0000000..d324ca5
--- /dev/null
@@ -0,0 +1,119 @@
+From aef53f97b505ff94190ce3a06dcd0ded6cf5c0ca Mon Sep 17 00:00:00 2001
+From: Nick Kossifidis <[email protected]>
+Date: Thu, 20 Sep 2018 01:48:15 +0300
+Subject: [PATCH 31/41] RISC-V: Cosmetic menuconfig changes
+
+* Move the built-in cmdline configuration on a new menu entry "Boot
+  options", it doesn't make much sense to be part of the debuging menu.
+
+* Rename "Kernel Type" menu to "Kernel features" to be more consistent with
+  what other architectures are using, plus "type" is a bit misleading here.
+
+Signed-off-by: Nick Kossifidis <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/Kconfig       | 40 +++++++++++++++++++++++++++++++++++++++-
+ arch/riscv/Kconfig.debug | 35 -----------------------------------
+ 2 files changed, 39 insertions(+), 36 deletions(-)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index bb80d3e..6c8329b 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -212,12 +212,50 @@ endmenu
+ endmenu
+-menu "Kernel type"
++menu "Kernel features"
+ source "kernel/Kconfig.hz"
+ endmenu
++menu "Boot options"
++
++config CMDLINE_BOOL
++      bool "Built-in kernel command line"
++      help
++        For most platforms, it is firmware or second stage bootloader
++        that by default specifies the kernel command line options.
++        However, it might be necessary or advantageous to either override
++        the default kernel command line or add a few extra options to it.
++        For such cases, this option allows hardcoding command line options
++        directly into the kernel.
++
++        For that, choose 'Y' here and fill in the extra boot parameters
++        in CONFIG_CMDLINE.
++
++        The built-in options will be concatenated to the default command
++        line if CMDLINE_FORCE is set to 'N'. Otherwise, the default
++        command line will be ignored and replaced by the built-in string.
++
++config CMDLINE
++      string "Built-in kernel command string"
++      depends on CMDLINE_BOOL
++      default ""
++      help
++        Supply command-line options at build time by entering them here.
++
++config CMDLINE_FORCE
++      bool "Built-in command line overrides bootloader arguments"
++      depends on CMDLINE_BOOL
++      help
++        Set this option to 'Y' to have the kernel ignore the bootloader
++        or firmware command line.  Instead, the built-in command line
++        will be used exclusively.
++
++        If you don't know what to do here, say N.
++
++endmenu
++
+ menu "Bus support"
+ config PCI
+diff --git a/arch/riscv/Kconfig.debug b/arch/riscv/Kconfig.debug
+index 3224ff6..c5a72f1 100644
+--- a/arch/riscv/Kconfig.debug
++++ b/arch/riscv/Kconfig.debug
+@@ -1,37 +1,2 @@
+-
+-config CMDLINE_BOOL
+-      bool "Built-in kernel command line"
+-      help
+-        For most platforms, it is firmware or second stage bootloader
+-        that by default specifies the kernel command line options.
+-        However, it might be necessary or advantageous to either override
+-        the default kernel command line or add a few extra options to it.
+-        For such cases, this option allows hardcoding command line options
+-        directly into the kernel.
+-
+-        For that, choose 'Y' here and fill in the extra boot parameters
+-        in CONFIG_CMDLINE.
+-
+-        The built-in options will be concatenated to the default command
+-        line if CMDLINE_FORCE is set to 'N'. Otherwise, the default
+-        command line will be ignored and replaced by the built-in string.
+-
+-config CMDLINE
+-      string "Built-in kernel command string"
+-      depends on CMDLINE_BOOL
+-      default ""
+-      help
+-        Supply command-line options at build time by entering them here.
+-
+-config CMDLINE_FORCE
+-      bool "Built-in command line overrides bootloader arguments"
+-      depends on CMDLINE_BOOL
+-      help
+-        Set this option to 'Y' to have the kernel ignore the bootloader
+-        or firmware command line.  Instead, the built-in command line
+-        will be used exclusively.
+-
+-        If you don't know what to do here, say N.
+-
+ config EARLY_PRINTK
+       def_bool y
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0032-RISC-V-properly-determine-hardware-caps.patch b/target/linux/riscv64/patches-4.19/0032-RISC-V-properly-determine-hardware-caps.patch
new file mode 100644 (file)
index 0000000..107aa03
--- /dev/null
@@ -0,0 +1,45 @@
+From a18d90df2e83a9891caa85a52ec3921d95915084 Mon Sep 17 00:00:00 2001
+From: Andreas Schwab <[email protected]>
+Date: Tue, 23 Oct 2018 09:33:47 +0200
+Subject: [PATCH 32/41] RISC-V: properly determine hardware caps
+
+On the Hifive-U platform, cpu 0 is a masked cpu with less capabilities
+than the other cpus.  Ignore it for the purpose of determining the
+hardware capabilities of the system.
+
+Signed-off-by: Andreas Schwab <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/kernel/cpufeature.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
+index 5493f32..0339087 100644
+--- a/arch/riscv/kernel/cpufeature.c
++++ b/arch/riscv/kernel/cpufeature.c
+@@ -28,7 +28,7 @@ bool has_fpu __read_mostly;
+ void riscv_fill_hwcap(void)
+ {
+-      struct device_node *node;
++      struct device_node *node = NULL;
+       const char *isa;
+       size_t i;
+       static unsigned long isa2hwcap[256] = {0};
+@@ -44,9 +44,11 @@ void riscv_fill_hwcap(void)
+       /*
+        * We don't support running Linux on hertergenous ISA systems.  For
+-       * now, we just check the ISA of the first processor.
++       * now, we just check the ISA of the first "okay" processor.
+        */
+-      node = of_find_node_by_type(NULL, "cpu");
++      while ((node = of_find_node_by_type(node, "cpu")))
++              if (riscv_of_processor_hartid(node) >= 0)
++                      break;
+       if (!node) {
+               pr_warning("Unable to find \"cpu\" devicetree entry");
+               return;
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0033-Move-EM_RISCV-into-elf-em.h.patch b/target/linux/riscv64/patches-4.19/0033-Move-EM_RISCV-into-elf-em.h.patch
new file mode 100644 (file)
index 0000000..093b844
--- /dev/null
@@ -0,0 +1,47 @@
+From 4047c266cca93ba2bc24ca746f2ae267e2d6a9ca Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Mon, 25 Jun 2018 13:23:12 -0700
+Subject: [PATCH 33/41] Move EM_RISCV into elf-em.h
+
+This should never have been inside our arch port to begin with, it's
+just a relic from when we were maintaining out of tree patches.
+
+Reviewed-by: Kees Cook <[email protected]>
+Reviewed-by: Paul Walmsley <[email protected]>
+Reviewed-by: Christoph Hellwig <[email protected]>
+Tested-by: David Abdurachmanov <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/include/asm/elf.h | 3 ---
+ include/uapi/linux/elf-em.h  | 1 +
+ 2 files changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/arch/riscv/include/asm/elf.h b/arch/riscv/include/asm/elf.h
+index a1ef503d..697fc23 100644
+--- a/arch/riscv/include/asm/elf.h
++++ b/arch/riscv/include/asm/elf.h
+@@ -16,9 +16,6 @@
+ #include <asm/auxvec.h>
+ #include <asm/byteorder.h>
+-/* TODO: Move definition into include/uapi/linux/elf-em.h */
+-#define EM_RISCV      0xF3
+-
+ /*
+  * These are used to set parameters in the core dumps.
+  */
+diff --git a/include/uapi/linux/elf-em.h b/include/uapi/linux/elf-em.h
+index 31aa101..93722e6 100644
+--- a/include/uapi/linux/elf-em.h
++++ b/include/uapi/linux/elf-em.h
+@@ -41,6 +41,7 @@
+ #define EM_TILEPRO    188     /* Tilera TILEPro */
+ #define EM_MICROBLAZE 189     /* Xilinx MicroBlaze */
+ #define EM_TILEGX     191     /* Tilera TILE-Gx */
++#define EM_RISCV      243     /* RISC-V */
+ #define EM_BPF                247     /* Linux BPF - in-kernel virtual machine */
+ #define EM_FRV                0x5441  /* Fujitsu FR-V */
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0034-Revert-RISC-V-Select-GENERIC_LIB_UMODDI3-on-RV32.patch b/target/linux/riscv64/patches-4.19/0034-Revert-RISC-V-Select-GENERIC_LIB_UMODDI3-on-RV32.patch
new file mode 100644 (file)
index 0000000..6f0c287
--- /dev/null
@@ -0,0 +1,30 @@
+From 4f404f5ce99d06ffb33ea17995c953456b2d4362 Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Fri, 26 Oct 2018 12:37:43 -0700
+Subject: [PATCH 34/41] Revert "RISC-V: Select GENERIC_LIB_UMODDI3 on RV32"
+
+I'm removing the generic 64-bit divide support, which means this will no
+longer work.
+
+This reverts commit 757331db921428295948fed5e7377a436e66d34e.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/Kconfig | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
+index fe45134..4198759 100644
+--- a/arch/riscv/Kconfig
++++ b/arch/riscv/Kconfig
+@@ -109,7 +109,6 @@ config ARCH_RV32I
+       select GENERIC_LIB_ASHRDI3
+       select GENERIC_LIB_LSHRDI3
+       select GENERIC_LIB_UCMPDI2
+-      select GENERIC_LIB_UMODDI3
+ config ARCH_RV64I
+       bool "RV64I"
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0035-Revert-lib-Add-umoddi3-and-udivmoddi4-of-GCC-library.patch b/target/linux/riscv64/patches-4.19/0035-Revert-lib-Add-umoddi3-and-udivmoddi4-of-GCC-library.patch
new file mode 100644 (file)
index 0000000..9087843
--- /dev/null
@@ -0,0 +1,397 @@
+From 9d93e875aa5e097a580a5128b9c9590eaf572427 Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Fri, 26 Oct 2018 12:38:11 -0700
+Subject: [PATCH 35/41] Revert "lib: Add umoddi3 and udivmoddi4 of GCC library
+ routines"
+
+We don't want 64-bit divide in the kernel.
+
+This reverts commit 6315730e9eab7de5fa9864bb13a352713f48aef1.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ lib/Kconfig      |   3 -
+ lib/Makefile     |   1 -
+ lib/udivmoddi4.c | 310 -------------------------------------------------------
+ lib/umoddi3.c    |  32 ------
+ 4 files changed, 346 deletions(-)
+ delete mode 100644 lib/udivmoddi4.c
+ delete mode 100644 lib/umoddi3.c
+
+diff --git a/lib/Kconfig b/lib/Kconfig
+index d82f206..a3928d4 100644
+--- a/lib/Kconfig
++++ b/lib/Kconfig
+@@ -621,6 +621,3 @@ config GENERIC_LIB_CMPDI2
+ config GENERIC_LIB_UCMPDI2
+       bool
+-
+-config GENERIC_LIB_UMODDI3
+-      bool
+diff --git a/lib/Makefile b/lib/Makefile
+index 56a8d9c..4238764 100644
+--- a/lib/Makefile
++++ b/lib/Makefile
+@@ -270,4 +270,3 @@ obj-$(CONFIG_GENERIC_LIB_LSHRDI3) += lshrdi3.o
+ obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
+ obj-$(CONFIG_GENERIC_LIB_CMPDI2) += cmpdi2.o
+ obj-$(CONFIG_GENERIC_LIB_UCMPDI2) += ucmpdi2.o
+-obj-$(CONFIG_GENERIC_LIB_UMODDI3) += umoddi3.o udivmoddi4.o
+diff --git a/lib/udivmoddi4.c b/lib/udivmoddi4.c
+deleted file mode 100644
+index c08bc8a..0000000
+--- a/lib/udivmoddi4.c
++++ /dev/null
+@@ -1,310 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-
+-/*
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * 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; if not, see the file COPYING, or write
+- * to the Free Software Foundation, Inc.
+- */
+-
+-#include <linux/libgcc.h>
+-
+-#define count_leading_zeros(COUNT, X)   ((COUNT) = __builtin_clz(X))
+-
+-#define W_TYPE_SIZE 32
+-
+-#define __ll_B ((unsigned long) 1 << (W_TYPE_SIZE / 2))
+-#define __ll_lowpart(t) ((unsigned long) (t) & (__ll_B - 1))
+-#define __ll_highpart(t) ((unsigned long) (t) >> (W_TYPE_SIZE / 2))
+-
+-/* If we still don't have umul_ppmm, define it using plain C. */
+-#if !defined(umul_ppmm)
+-#define umul_ppmm(w1, w0, u, v)                                               \
+-      do {                                                            \
+-              unsigned long __x0, __x1, __x2, __x3;                   \
+-              unsigned short __ul, __vl, __uh, __vh;                  \
+-                                                                      \
+-              __ul = __ll_lowpart(u);                                 \
+-              __uh = __ll_highpart(u);                                \
+-              __vl = __ll_lowpart(v);                                 \
+-              __vh = __ll_highpart(v);                                \
+-                                                                      \
+-              __x0 = (unsigned long) __ul * __vl;                     \
+-              __x1 = (unsigned long) __ul * __vh;                     \
+-              __x2 = (unsigned long) __uh * __vl;                     \
+-              __x3 = (unsigned long) __uh * __vh;                     \
+-                                                                      \
+-              __x1 += __ll_highpart(__x0);                            \
+-              __x1 += __x2;                                           \
+-              if (__x1 < __x2)                                        \
+-                      __x3 += __ll_B;                                 \
+-                                                                      \
+-              (w1) = __x3 + __ll_highpart(__x1);                      \
+-              (w0) = __ll_lowpart(__x1) * __ll_B + __ll_lowpart(__x0);\
+-      } while (0)
+-#endif
+-
+-#if !defined(sub_ddmmss)
+-#define sub_ddmmss(sh, sl, ah, al, bh, bl)                            \
+-      do {                                                            \
+-              unsigned long __x;                                      \
+-              __x = (al) - (bl);                                      \
+-              (sh) = (ah) - (bh) - (__x > (al));                      \
+-              (sl) = __x;                                             \
+-      } while (0)
+-#endif
+-
+-/* Define this unconditionally, so it can be used for debugging. */
+-#define __udiv_qrnnd_c(q, r, n1, n0, d)                                       \
+-      do {                                                            \
+-              unsigned long __d1, __d0, __q1, __q0;                   \
+-              unsigned long __r1, __r0, __m;                          \
+-              __d1 = __ll_highpart(d);                                \
+-              __d0 = __ll_lowpart(d);                         \
+-                                                                      \
+-              __r1 = (n1) % __d1;                                     \
+-              __q1 = (n1) / __d1;                                     \
+-              __m = (unsigned long) __q1 * __d0;                      \
+-              __r1 = __r1 * __ll_B | __ll_highpart(n0);               \
+-              if (__r1 < __m) {                                       \
+-                      __q1--, __r1 += (d);                            \
+-                      if (__r1 >= (d))                                \
+-                              if (__r1 < __m)                         \
+-                                      __q1--, __r1 += (d);            \
+-              }                                                       \
+-              __r1 -= __m;                                            \
+-                                                                      \
+-              __r0 = __r1 % __d1;                                     \
+-              __q0 = __r1 / __d1;                                     \
+-              __m = (unsigned long) __q0 * __d0;                      \
+-              __r0 = __r0 * __ll_B | __ll_lowpart(n0);                \
+-              if (__r0 < __m) {                                       \
+-                      __q0--, __r0 += (d);                            \
+-                      if (__r0 >= (d))                                \
+-                              if (__r0 < __m)                         \
+-                                      __q0--, __r0 += (d);            \
+-              }                                                       \
+-              __r0 -= __m;                                            \
+-                                                                      \
+-              (q) = (unsigned long) __q1 * __ll_B | __q0;             \
+-              (r) = __r0;                                             \
+-      } while (0)
+-
+-/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c. */
+-#if !defined(udiv_qrnnd)
+-#define UDIV_NEEDS_NORMALIZATION 1
+-#define udiv_qrnnd __udiv_qrnnd_c
+-#endif
+-
+-unsigned long long __udivmoddi4(unsigned long long u, unsigned long long v,
+-                              unsigned long long *rp)
+-{
+-      const DWunion nn = {.ll = u };
+-      const DWunion dd = {.ll = v };
+-      DWunion rr, ww;
+-      unsigned long d0, d1, n0, n1, n2;
+-      unsigned long q0 = 0, q1 = 0;
+-      unsigned long b, bm;
+-
+-      d0 = dd.s.low;
+-      d1 = dd.s.high;
+-      n0 = nn.s.low;
+-      n1 = nn.s.high;
+-
+-#if !UDIV_NEEDS_NORMALIZATION
+-
+-      if (d1 == 0) {
+-              if (d0 > n1) {
+-                      /* 0q = nn / 0D */
+-
+-                      udiv_qrnnd(q0, n0, n1, n0, d0);
+-                      q1 = 0;
+-
+-                      /* Remainder in n0. */
+-              } else {
+-                      /* qq = NN / 0d */
+-
+-                      if (d0 == 0)
+-                              /* Divide intentionally by zero. */
+-                              d0 = 1 / d0;
+-
+-                      udiv_qrnnd(q1, n1, 0, n1, d0);
+-                      udiv_qrnnd(q0, n0, n1, n0, d0);
+-
+-                      /* Remainder in n0. */
+-              }
+-
+-              if (rp != 0) {
+-                      rr.s.low = n0;
+-                      rr.s.high = 0;
+-                      *rp = rr.ll;
+-              }
+-
+-#else /* UDIV_NEEDS_NORMALIZATION */
+-
+-      if (d1 == 0) {
+-              if (d0 > n1) {
+-                      /* 0q = nn / 0D */
+-
+-                      count_leading_zeros(bm, d0);
+-
+-                      if (bm != 0) {
+-                              /*
+-                               * Normalize, i.e. make the most significant bit
+-                               * of the denominator set.
+-                               */
+-
+-                              d0 = d0 << bm;
+-                              n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm));
+-                              n0 = n0 << bm;
+-                      }
+-
+-                      udiv_qrnnd(q0, n0, n1, n0, d0);
+-                      q1 = 0;
+-
+-                      /* Remainder in n0 >> bm. */
+-              } else {
+-                      /* qq = NN / 0d */
+-
+-                      if (d0 == 0)
+-                              /* Divide intentionally by zero. */
+-                              d0 = 1 / d0;
+-
+-                      count_leading_zeros(bm, d0);
+-
+-                      if (bm == 0) {
+-                              /*
+-                               * From (n1 >= d0) /\ (the most significant bit
+-                               * of d0 is set), conclude (the most significant
+-                               * bit of n1 is set) /\ (theleading quotient
+-                               * digit q1 = 1).
+-                               *
+-                               * This special case is necessary, not an
+-                               * optimization. (Shifts counts of W_TYPE_SIZE
+-                               * are undefined.)
+-                               */
+-
+-                              n1 -= d0;
+-                              q1 = 1;
+-                      } else {
+-                              /* Normalize. */
+-
+-                              b = W_TYPE_SIZE - bm;
+-
+-                              d0 = d0 << bm;
+-                              n2 = n1 >> b;
+-                              n1 = (n1 << bm) | (n0 >> b);
+-                              n0 = n0 << bm;
+-
+-                              udiv_qrnnd(q1, n1, n2, n1, d0);
+-                      }
+-
+-                      /* n1 != d0... */
+-
+-                      udiv_qrnnd(q0, n0, n1, n0, d0);
+-
+-                      /* Remainder in n0 >> bm. */
+-              }
+-
+-              if (rp != 0) {
+-                      rr.s.low = n0 >> bm;
+-                      rr.s.high = 0;
+-                      *rp = rr.ll;
+-              }
+-
+-#endif /* UDIV_NEEDS_NORMALIZATION */
+-
+-      } else {
+-              if (d1 > n1) {
+-                      /* 00 = nn / DD */
+-
+-                      q0 = 0;
+-                      q1 = 0;
+-
+-                      /* Remainder in n1n0. */
+-                      if (rp != 0) {
+-                              rr.s.low = n0;
+-                              rr.s.high = n1;
+-                              *rp = rr.ll;
+-                      }
+-              } else {
+-                      /* 0q = NN / dd */
+-
+-                      count_leading_zeros(bm, d1);
+-                      if (bm == 0) {
+-                              /*
+-                               * From (n1 >= d1) /\ (the most significant bit
+-                               * of d1 is set), conclude (the most significant
+-                               * bit of n1 is set) /\ (the quotient digit q0 =
+-                               * 0 or 1).
+-                               *
+-                               * This special case is necessary, not an
+-                               * optimization.
+-                               */
+-
+-                              /*
+-                               * The condition on the next line takes
+-                               * advantage of that n1 >= d1 (true due to
+-                               * program flow).
+-                               */
+-                              if (n1 > d1 || n0 >= d0) {
+-                                      q0 = 1;
+-                                      sub_ddmmss(n1, n0, n1, n0, d1, d0);
+-                              } else {
+-                                      q0 = 0;
+-                              }
+-
+-                              q1 = 0;
+-
+-                              if (rp != 0) {
+-                                      rr.s.low = n0;
+-                                      rr.s.high = n1;
+-                                      *rp = rr.ll;
+-                              }
+-                      } else {
+-                              unsigned long m1, m0;
+-                              /* Normalize. */
+-
+-                              b = W_TYPE_SIZE - bm;
+-
+-                              d1 = (d1 << bm) | (d0 >> b);
+-                              d0 = d0 << bm;
+-                              n2 = n1 >> b;
+-                              n1 = (n1 << bm) | (n0 >> b);
+-                              n0 = n0 << bm;
+-
+-                              udiv_qrnnd(q0, n1, n2, n1, d1);
+-                              umul_ppmm(m1, m0, q0, d0);
+-
+-                              if (m1 > n1 || (m1 == n1 && m0 > n0)) {
+-                                      q0--;
+-                                      sub_ddmmss(m1, m0, m1, m0, d1, d0);
+-                              }
+-
+-                              q1 = 0;
+-
+-                              /* Remainder in (n1n0 - m1m0) >> bm. */
+-                              if (rp != 0) {
+-                                      sub_ddmmss(n1, n0, n1, n0, m1, m0);
+-                                      rr.s.low = (n1 << b) | (n0 >> bm);
+-                                      rr.s.high = n1 >> bm;
+-                                      *rp = rr.ll;
+-                              }
+-                      }
+-              }
+-      }
+-
+-      ww.s.low = q0;
+-      ww.s.high = q1;
+-
+-      return ww.ll;
+-}
+diff --git a/lib/umoddi3.c b/lib/umoddi3.c
+deleted file mode 100644
+index d7bbf0f..0000000
+--- a/lib/umoddi3.c
++++ /dev/null
+@@ -1,32 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0
+-
+-/*
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * 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; if not, see the file COPYING, or write
+- * to the Free Software Foundation, Inc.
+- */
+-
+-#include <linux/module.h>
+-#include <linux/libgcc.h>
+-
+-extern unsigned long long __udivmoddi4(unsigned long long u,
+-                                     unsigned long long v,
+-                                     unsigned long long *rp);
+-
+-unsigned long long __umoddi3(unsigned long long u, unsigned long long v)
+-{
+-      unsigned long long w;
+-      (void)__udivmoddi4(u, v, &w);
+-      return w;
+-}
+-EXPORT_SYMBOL(__umoddi3);
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0036-RISC-V-refresh-defconfig.patch b/target/linux/riscv64/patches-4.19/0036-RISC-V-refresh-defconfig.patch
new file mode 100644 (file)
index 0000000..135cf67
--- /dev/null
@@ -0,0 +1,64 @@
+From 8e5e5a02706aaf939589b792813b8c349cad55e9 Mon Sep 17 00:00:00 2001
+From: Anup Patel <[email protected]>
+Date: Thu, 1 Nov 2018 10:40:32 +0530
+Subject: [PATCH 36/41] RISC-V: refresh defconfig
+
+This patch updates defconfig using savedefconfig on Linux-4.19.  It is
+intended to have no functional change.
+
+Signed-off-by: Anup Patel <[email protected]>
+Reviewed-by: Palmer Dabbelt <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/configs/defconfig | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
+index 36473d7..07fa9ea 100644
+--- a/arch/riscv/configs/defconfig
++++ b/arch/riscv/configs/defconfig
+@@ -1,6 +1,3 @@
+-CONFIG_SMP=y
+-CONFIG_PCI=y
+-CONFIG_PCIE_XILINX=y
+ CONFIG_SYSVIPC=y
+ CONFIG_POSIX_MQUEUE=y
+ CONFIG_IKCONFIG=y
+@@ -11,10 +8,15 @@ 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_EXPERT=y
+-CONFIG_CHECKPOINT_RESTORE=y
+ CONFIG_BPF_SYSCALL=y
++CONFIG_SMP=y
++CONFIG_PCI=y
++CONFIG_PCIE_XILINX=y
++CONFIG_MODULES=y
++CONFIG_MODULE_UNLOAD=y
+ CONFIG_NET=y
+ CONFIG_PACKET=y
+ CONFIG_UNIX=y
+@@ -59,6 +61,7 @@ CONFIG_USB_OHCI_HCD_PLATFORM=y
+ CONFIG_USB_STORAGE=y
+ CONFIG_USB_UAS=y
+ CONFIG_VIRTIO_MMIO=y
++CONFIG_SIFIVE_PLIC=y
+ CONFIG_RAS=y
+ CONFIG_EXT4_FS=y
+ CONFIG_EXT4_FS_POSIX_ACL=y
+@@ -72,8 +75,5 @@ CONFIG_NFS_V4=y
+ CONFIG_NFS_V4_1=y
+ CONFIG_NFS_V4_2=y
+ CONFIG_ROOT_NFS=y
+-# CONFIG_RCU_TRACE is not set
+ CONFIG_CRYPTO_USER_API_HASH=y
+-CONFIG_MODULES=y
+-CONFIG_MODULE_UNLOAD=y
+-CONFIG_SIFIVE_PLIC=y
++# CONFIG_RCU_TRACE is not set
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0037-RISC-V-defconfig-Enable-printk-timestamps.patch b/target/linux/riscv64/patches-4.19/0037-RISC-V-defconfig-Enable-printk-timestamps.patch
new file mode 100644 (file)
index 0000000..fff11d1
--- /dev/null
@@ -0,0 +1,33 @@
+From bbe098ce00a42eeff9fd94f6a0e12aada8ee1ef3 Mon Sep 17 00:00:00 2001
+From: Anup Patel <[email protected]>
+Date: Thu, 1 Nov 2018 10:40:33 +0530
+Subject: [PATCH 37/41] RISC-V: defconfig: Enable printk timestamps
+
+The printk timestamps are very useful information to visually see
+where kernel is spending time during boot. It also helps us see
+the timing of hotplug events at runtime.
+
+This patch enables printk timestamps in RISC-V defconfig so that
+we have it enabled by default (similar to other architectures
+such as x86_64, arm64, etc).
+
+Signed-off-by: Anup Patel <[email protected]>
+Acked-by: Olof Johansson <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/configs/defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
+index 07fa9ea..ef4f15d 100644
+--- a/arch/riscv/configs/defconfig
++++ b/arch/riscv/configs/defconfig
+@@ -76,4 +76,5 @@ CONFIG_NFS_V4_1=y
+ CONFIG_NFS_V4_2=y
+ CONFIG_ROOT_NFS=y
+ CONFIG_CRYPTO_USER_API_HASH=y
++CONFIG_PRINTK_TIME=y
+ # CONFIG_RCU_TRACE is not set
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0038-riscv-fix-spacing-in-struct-pt_regs.patch b/target/linux/riscv64/patches-4.19/0038-riscv-fix-spacing-in-struct-pt_regs.patch
new file mode 100644 (file)
index 0000000..5008608
--- /dev/null
@@ -0,0 +1,31 @@
+From 004049175b3577e684db4d4bcc1999d019640c0d Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <[email protected]>
+Date: Mon, 5 Nov 2018 15:40:04 +0100
+Subject: [PATCH 38/41] riscv: fix spacing in struct pt_regs
+
+Replace 8 spaces with tab to match styling.
+
+Signed-off-by: David Abdurachmanov <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/include/asm/ptrace.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h
+index 2c5df94..bbe1862 100644
+--- a/arch/riscv/include/asm/ptrace.h
++++ b/arch/riscv/include/asm/ptrace.h
+@@ -56,8 +56,8 @@ struct pt_regs {
+       unsigned long sstatus;
+       unsigned long sbadaddr;
+       unsigned long scause;
+-        /* a0 value before the syscall */
+-        unsigned long orig_a0;
++      /* a0 value before the syscall */
++      unsigned long orig_a0;
+ };
+ #ifdef CONFIG_64BIT
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0039-riscv-add-missing-vdso_install-target.patch b/target/linux/riscv64/patches-4.19/0039-riscv-add-missing-vdso_install-target.patch
new file mode 100644 (file)
index 0000000..e283a97
--- /dev/null
@@ -0,0 +1,30 @@
+From 6823875abd0a0644163997423015e929198b646f Mon Sep 17 00:00:00 2001
+From: David Abdurachmanov <[email protected]>
+Date: Mon, 5 Nov 2018 15:35:37 +0100
+Subject: [PATCH 39/41] riscv: add missing vdso_install target
+
+Building kernel 4.20 for Fedora as RPM fails, because riscv is missing
+vdso_install target in arch/riscv/Makefile.
+
+Signed-off-by: David Abdurachmanov <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/Makefile | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
+index d1014619..4af153a 100644
+--- a/arch/riscv/Makefile
++++ b/arch/riscv/Makefile
+@@ -77,4 +77,8 @@ core-y += arch/riscv/kernel/ arch/riscv/mm/
+ libs-y += arch/riscv/lib/
++PHONY += vdso_install
++vdso_install:
++      $(Q)$(MAKE) $(build)=arch/riscv/kernel/vdso $@
++
+ all: vmlinux
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0040-RISC-V-lib-Fix-build-error-for-64-bit.patch b/target/linux/riscv64/patches-4.19/0040-RISC-V-lib-Fix-build-error-for-64-bit.patch
new file mode 100644 (file)
index 0000000..1411dae
--- /dev/null
@@ -0,0 +1,34 @@
+From 69f76e0109e02647945322f57cf17c3bc99b4646 Mon Sep 17 00:00:00 2001
+From: Olof Johansson <[email protected]>
+Date: Tue, 30 Oct 2018 23:47:07 -0700
+Subject: [PATCH 40/41] RISC-V: lib: Fix build error for 64-bit
+
+Fixes the following build error from tinyconfig:
+
+riscv64-unknown-linux-gnu-ld: kernel/sched/fair.o: in function `.L8':
+fair.c:(.text+0x70): undefined reference to `__lshrti3'
+riscv64-unknown-linux-gnu-ld: kernel/time/clocksource.o: in function `.L0 ':
+clocksource.c:(.text+0x334): undefined reference to `__lshrti3'
+
+Fixes: 7f47c73b355f ("RISC-V: Build tishift only on 64-bit")
+Signed-off-by: Olof Johansson <[email protected]>
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ arch/riscv/lib/Makefile | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
+index 5739bd0..4e2e600 100644
+--- a/arch/riscv/lib/Makefile
++++ b/arch/riscv/lib/Makefile
+@@ -3,6 +3,6 @@ lib-y  += memcpy.o
+ lib-y += memset.o
+ lib-y += uaccess.o
+-lib-(CONFIG_64BIT) += tishift.o
++lib-$(CONFIG_64BIT) += tishift.o
+ lib-$(CONFIG_32BIT) += udivdi3.o
+-- 
+2.1.4
+
diff --git a/target/linux/riscv64/patches-4.19/0100-riscv-add-S-and-U-modes-to-ISA-string.patch b/target/linux/riscv64/patches-4.19/0100-riscv-add-S-and-U-modes-to-ISA-string.patch
new file mode 100644 (file)
index 0000000..eb9540c
--- /dev/null
@@ -0,0 +1,21 @@
+---
+ arch/riscv/kernel/cpu.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/riscv/kernel/cpu.c b/arch/riscv/kernel/cpu.c
+index 3a5a2ee31547..4029c7e6872b 100644
+--- a/arch/riscv/kernel/cpu.c
++++ b/arch/riscv/kernel/cpu.c
+@@ -64,7 +64,7 @@ int riscv_of_processor_hartid(struct device_node *node)
+ static void print_isa(struct seq_file *f, const char *orig_isa)
+ {
+-      static const char *ext = "mafdc";
++      static const char *ext = "mafdcsu";
+       const char *isa = orig_isa;
+       const char *e;
+-- 
+2.19.1
+
+  
\ No newline at end of file
diff --git a/target/linux/riscv64/patches-4.19/0101-002-clk-sifive-prci.patch b/target/linux/riscv64/patches-4.19/0101-002-clk-sifive-prci.patch
new file mode 100644 (file)
index 0000000..11e2725
--- /dev/null
@@ -0,0 +1,509 @@
+From 7f45b80bb9675e9ace37bc1c4fd8f0351dfd9de9 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <[email protected]>
+Date: Tue, 13 Feb 2018 19:39:41 -0800
+Subject: [PATCH 1/5] u54-prci: driver for core U54 clocks
+
+---
+ drivers/clk/sifive/Kconfig    |   4 +
+ drivers/clk/sifive/Makefile   |   1 +
+ drivers/clk/sifive/u54-prci.c | 314 ++++++++++++++++++++++++++++++++++
+ 3 files changed, 319 insertions(+)
+ create mode 100644 drivers/clk/sifive/Kconfig
+ create mode 100644 drivers/clk/sifive/Makefile
+ create mode 100644 drivers/clk/sifive/u54-prci.c
+
+diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
+new file mode 100644
+index 0000000000000..a562e0c6dc67e
+--- /dev/null
++++ b/drivers/clk/sifive/Kconfig
+@@ -0,0 +1,4 @@
++config CLK_U54_PRCI
++      bool "PRCI driver for U54 SoCs"
++      ---help---
++        Supports Power Reset Clock interface found in U540 SoCs
+diff --git a/drivers/clk/sifive/Makefile b/drivers/clk/sifive/Makefile
+new file mode 100644
+index 0000000000000..0c2b175b5846d
+--- /dev/null
++++ b/drivers/clk/sifive/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_CLK_U54_PRCI)    += u54-prci.o
+diff --git a/drivers/clk/sifive/u54-prci.c b/drivers/clk/sifive/u54-prci.c
+new file mode 100644
+index 0000000000000..edc4b7818e710
+--- /dev/null
++++ b/drivers/clk/sifive/u54-prci.c
+@@ -0,0 +1,314 @@
++/*
++ * 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.
++ *
++ * 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.
++ *
++ * Copyright (C) 2018 SiFive, Inc.
++ */
++
++#include <linux/clkdev.h>
++#include <linux/clk-provider.h>
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++#include <linux/log2.h>
++
++#define CORE_CLOCK 0
++#define GEMTX_CLOCK 1
++#define PRCI_CLOCKS 2
++
++#define MIN_REF 7000000UL
++#define MAX_REF 200000000UL
++#define MAX_PARENT 600000000UL
++#define MAX_VCO 4800000000UL
++#define MAX_DIV 64
++#define MAX_R 64UL
++
++#define PLL_LOCK 0x80000000U
++#define NAME_LEN 40
++
++struct sifive_u54_prci_driver;
++
++struct sifive_u54_prci_pll {
++      struct clk_hw hw;
++      struct sifive_u54_prci_driver *driver;
++      char name[NAME_LEN];
++      u32 freq;
++      u32 glcm;
++};
++
++struct sifive_u54_prci_driver {
++      struct clk_onecell_data table;
++      struct clk *clks[PRCI_CLOCKS];
++      struct sifive_u54_prci_pll plls[PRCI_CLOCKS];
++      void __iomem *reg;
++};
++
++#define to_sifive_u54_prci_pll(hw) container_of(hw, struct sifive_u54_prci_pll, hw)
++
++struct sifive_u54_pll_cfg {
++      unsigned long r, f, q, a;
++};
++
++static struct sifive_u54_pll_cfg sifive_u54_pll_cfg(u32 reg)
++{
++      struct sifive_u54_pll_cfg cfg;
++      cfg.r = (reg >>  0) & 0x3f;
++      cfg.f = (reg >>  6) & 0x1ff;
++      cfg.q = (reg >> 15) & 0x7;
++      cfg.a = (reg >> 18) & 0x7;
++      return cfg;
++}
++
++static u32 sifive_u54_pll_reg(struct sifive_u54_pll_cfg cfg)
++{
++      u32 reg = 0;
++      reg |= (cfg.r & 0x3f)  << 0;
++      reg |= (cfg.f & 0x1ff) << 6;
++      reg |= (cfg.q & 0x7)   << 15;
++      reg |= (cfg.a & 0x7)   << 18;
++      reg |= 1<<25; // internal feedback
++      return reg;
++}
++
++static unsigned long sifive_u54_pll_rate(struct sifive_u54_pll_cfg cfg, unsigned long parent)
++{
++      return (parent*2*(cfg.f+1) / (cfg.r+1)) >> cfg.q;
++}
++
++static struct sifive_u54_pll_cfg sifive_u54_pll_configure(unsigned long target, unsigned long parent)
++{
++      struct sifive_u54_pll_cfg cfg;
++      unsigned long scale, ratio, best_delta, filter;
++      u32 max_r, best_r, best_f, r;
++
++      /* Confirm input frequency is within bounds */
++      if (WARN_ON(parent > MAX_PARENT)) { parent = MAX_PARENT; }
++      if (WARN_ON(parent < MIN_REF))    { parent = MIN_REF; }
++
++      /* Calculate the Q shift and target VCO */
++      scale = MAX_VCO / target;
++      if (scale <= 1) {
++              cfg.q = 1;
++              target = MAX_VCO;
++      } else if (scale > MAX_DIV) {
++              cfg.q = ilog2(MAX_DIV);
++              target = MAX_VCO/2;
++      } else {
++              cfg.q = ilog2(scale);
++              target = target << cfg.q;
++      }
++
++      /* Precalcualte the target ratio */
++      ratio = (target << 20) / parent;
++
++      /* Placeholder values */
++      best_r = 0;
++      best_f = 0;
++      best_delta = MAX_VCO;
++
++      /* Consider all values for R which land within [MIN_REF, MAX_REF]; prefer smaller R */
++      max_r = min(MAX_R, parent / MIN_REF);
++      for (r = DIV_ROUND_UP(parent, MAX_REF); r <= max_r; ++r) {
++              /* What is the best F we can pick in this case? */
++              u32 f = (ratio*r + (1<<20)) >> 21;
++              unsigned long ref = parent / r;
++              unsigned long vco = ref * f * 2;
++              unsigned long delta;
++
++              /* Ensure rounding didn't take us out of range */
++              if (vco > target) --f;
++              if (vco < MAX_VCO/2) ++f;
++              vco = ref * f * 2;
++
++              delta = abs(target - vco);
++              if (delta < best_delta) {
++                      best_delta = delta;
++                      best_r = r;
++                      best_f = f;
++              }
++      }
++
++      cfg.r = best_r - 1;
++      cfg.f = best_f - 1;
++
++      /* Pick the best PLL jitter filter */
++      filter = parent / best_r;
++      BUG_ON(filter < 7000000);
++      if (filter < 11000000) {
++              cfg.a = 1;
++      } else if (filter < 18000000) {
++              cfg.a = 2;
++      } else if (filter < 30000000) {
++              cfg.a = 3;
++      } else if (filter < 50000000) {
++              cfg.a = 4;
++      } else if (filter < 80000000) {
++              cfg.a = 5;
++      } else if (filter < 130000000) {
++              cfg.a = 6;
++      } else {
++              BUG_ON (filter > 200000000);
++              cfg.a = 7;
++      }
++
++      return cfg;
++}
++
++static unsigned long sifive_u54_prci_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
++{
++      struct sifive_u54_prci_pll *pll = to_sifive_u54_prci_pll(hw);
++      struct sifive_u54_prci_driver *driver = pll->driver;
++
++      u32 reg = ioread32(driver->reg + pll->freq);
++      struct sifive_u54_pll_cfg cfg = sifive_u54_pll_cfg(reg);
++
++      return sifive_u54_pll_rate(cfg, parent_rate);
++}
++
++static long sifive_u54_prci_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate)
++{
++      struct sifive_u54_pll_cfg cfg = sifive_u54_pll_configure(rate, *parent_rate);
++      return sifive_u54_pll_rate(cfg, *parent_rate);
++}
++
++static int sifive_u54_prci_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
++{
++      struct sifive_u54_prci_pll *pll = to_sifive_u54_prci_pll(hw);
++      struct sifive_u54_prci_driver *driver = pll->driver;
++
++      struct sifive_u54_pll_cfg cfg = sifive_u54_pll_configure(rate, parent_rate);
++      u32 reg = sifive_u54_pll_reg(cfg);
++
++      /* Switch to reg clock and reconfigure PLL */
++      iowrite32(1, driver->reg + pll->glcm);
++      iowrite32(reg, driver->reg + pll->freq);
++
++      /* Wait for lock and switch back to PLL */
++      while (!(ioread32(driver->reg + pll->freq) & PLL_LOCK));
++      iowrite32(0, driver->reg + pll->glcm);
++
++      return 0;
++}
++
++static const struct clk_ops sifive_u54_prci_ops_rw = {
++      .recalc_rate = sifive_u54_prci_recalc_rate,
++      .round_rate = sifive_u54_prci_round_rate,
++      .set_rate = sifive_u54_prci_set_rate,
++};
++
++static const struct clk_ops sifive_u54_prci_ops_ro = {
++      .recalc_rate = sifive_u54_prci_recalc_rate,
++};
++
++static ssize_t sifive_u54_pll_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++      struct sifive_u54_prci_driver *driver = dev_get_drvdata(dev);
++      return sprintf(buf, "%ld", clk_get_rate(driver->clks[0]));
++}
++
++static ssize_t sifive_u54_pll_rate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
++{
++      struct sifive_u54_prci_driver *driver = dev_get_drvdata(dev);
++      unsigned long rate;
++      char *endp;
++
++      rate = simple_strtoul(buf, &endp, 0);
++      if (*endp != 0 && *endp != '\n')
++              return -EINVAL;
++
++      clk_set_rate(driver->clks[0], rate);
++      return count;
++}
++
++static DEVICE_ATTR(rate, 0644, sifive_u54_pll_show, sifive_u54_pll_rate_store);
++
++static int sifive_u54_prci_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct clk_init_data init;
++      struct sifive_u54_prci_driver *driver;
++      struct resource *res;
++      const char *parent;
++      int i;
++
++      parent = of_clk_get_parent_name(dev->of_node, 0);
++      if (!parent) {
++              dev_err(dev, "No OF parent clocks found\n");
++              return -EINVAL;
++      }
++
++      driver = devm_kzalloc(dev, sizeof(*driver), GFP_KERNEL);
++      if (!driver) {
++              dev_err(dev, "Out of memory\n");
++              return -ENOMEM;
++      }
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      driver->reg = devm_ioremap_resource(dev, res);
++      if (IS_ERR(driver->reg))
++              return PTR_ERR(driver->reg);
++
++      /* Link the data structure */
++      driver->table.clk_num = PRCI_CLOCKS;
++      driver->table.clks = &driver->clks[0];
++      dev_set_drvdata(dev, driver);
++
++      /* Describe the clocks */
++      snprintf(driver->plls[CORE_CLOCK].name, NAME_LEN, "%s.core", dev->of_node->name);
++      driver->plls[CORE_CLOCK].freq = 0x4;
++      driver->plls[CORE_CLOCK].glcm = 0x24;
++      snprintf(driver->plls[GEMTX_CLOCK].name, NAME_LEN, "%s.gemtx", dev->of_node->name);
++      driver->plls[GEMTX_CLOCK].freq = 0x1c;
++      driver->plls[GEMTX_CLOCK].glcm = 0; /* None; cannot be set_rate */
++
++      /* Export the clocks */
++      for (i = 0; i < PRCI_CLOCKS; ++i) {
++              init.name = &driver->plls[i].name[0];
++              init.ops = driver->plls[i].glcm ? &sifive_u54_prci_ops_rw : &sifive_u54_prci_ops_ro;
++              init.num_parents = 1;
++              init.parent_names = &parent;
++              init.flags = 0;
++
++              driver->plls[i].driver = driver;
++              driver->plls[i].hw.init = &init;
++
++              driver->clks[i] = devm_clk_register(dev, &driver->plls[i].hw);
++              if (IS_ERR(driver->clks[i])) {
++                      dev_err(dev, "Failed to register clock %d, %ld\n", i, PTR_ERR(driver->clks[i]));
++                      return PTR_ERR(driver->clks[i]);
++              }
++      }
++
++      of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &driver->table);
++      device_create_file(dev, &dev_attr_rate);
++      dev_info(dev, "Registered U54 core clocks\n");
++
++      return 0;
++}
++
++static const struct of_device_id sifive_u54_prci_of_match[] = {
++      { .compatible = "sifive,aloeprci0", },
++      {}
++};
++
++static struct platform_driver sifive_u54_prci_driver = {
++      .driver = {
++              .name = "sifive-u54-prci",
++              .of_match_table = sifive_u54_prci_of_match,
++      },
++      .probe = sifive_u54_prci_probe,
++};
++
++static int __init sifive_u54_prci_init(void)
++{
++      return platform_driver_register(&sifive_u54_prci_driver);
++}
++core_initcall(sifive_u54_prci_init);
+
+From 9b47a41c087008233d3024cc3f5005984a6c504c Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Wed, 21 Feb 2018 10:06:48 -0800
+Subject: [PATCH 2/5] Fix some overflow warnings
+
+---
+ drivers/clk/sifive/u54-prci.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/clk/sifive/u54-prci.c b/drivers/clk/sifive/u54-prci.c
+index edc4b7818e710..b8a93d1ebc2db 100644
+--- a/drivers/clk/sifive/u54-prci.c
++++ b/drivers/clk/sifive/u54-prci.c
+@@ -24,15 +24,15 @@
+ #define GEMTX_CLOCK 1
+ #define PRCI_CLOCKS 2
+-#define MIN_REF 7000000UL
+-#define MAX_REF 200000000UL
+-#define MAX_PARENT 600000000UL
+-#define MAX_VCO 4800000000UL
+-#define MAX_DIV 64
+-#define MAX_R 64UL
+-
+-#define PLL_LOCK 0x80000000U
+-#define NAME_LEN 40
++#define MIN_REF 7000000ULL
++#define MAX_REF 200000000ULL
++#define MAX_PARENT 600000000ULL
++#define MAX_VCO 4800000000ULL
++#define MAX_DIV 64ULL
++#define MAX_R 64ULL
++
++#define PLL_LOCK 0x80000000ULL
++#define NAME_LEN 40ULL
+ struct sifive_u54_prci_driver;
+
+From 031060d77cfbd86d39dfb8863e4fa4f95b435b1b Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Wed, 21 Feb 2018 13:00:16 -0800
+Subject: [PATCH 3/5] Include the sifive kconfig
+
+---
+ drivers/clk/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
+index 721572a8c4296..5b4bb0a23ffd3 100644
+--- a/drivers/clk/Kconfig
++++ b/drivers/clk/Kconfig
+@@ -288,6 +288,7 @@ source "drivers/clk/mvebu/Kconfig"
+ source "drivers/clk/qcom/Kconfig"
+ source "drivers/clk/renesas/Kconfig"
+ source "drivers/clk/samsung/Kconfig"
++source "drivers/clk/sifive/Kconfig"
+ source "drivers/clk/sprd/Kconfig"
+ source "drivers/clk/sunxi-ng/Kconfig"
+ source "drivers/clk/tegra/Kconfig"
+
+From 9f8ca54f7c62aeb3245d8a70150d15e25ffb4f15 Mon Sep 17 00:00:00 2001
+From: Palmer Dabbelt <[email protected]>
+Date: Wed, 21 Feb 2018 13:40:10 -0800
+Subject: [PATCH 4/5] Only show the U64 clock driver on RISC-V
+
+This will probably only be on a RISC-V system, and it doesn't build on
+32-bit systems without 64-bit divides which is a headache.
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ drivers/clk/sifive/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
+index a562e0c6dc67e..c324161700271 100644
+--- a/drivers/clk/sifive/Kconfig
++++ b/drivers/clk/sifive/Kconfig
+@@ -1,4 +1,5 @@
+ config CLK_U54_PRCI
+       bool "PRCI driver for U54 SoCs"
++      depends on RISCV
+       ---help---
+         Supports Power Reset Clock interface found in U540 SoCs
+
+From d79106eca2349e212c4737c5646002bd864b1593 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <[email protected]>
+Date: Tue, 13 Feb 2018 19:39:41 -0800
+Subject: [PATCH 5/5] u54-prci: driver for core U54 clocks
+
+---
+ .../bindings/clock/sifive,u54-prci.txt        | 44 +++++++++++++++++++
+ 1 file changed, 44 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/sifive,u54-prci.txt
+
+diff --git a/Documentation/devicetree/bindings/clock/sifive,u54-prci.txt b/Documentation/devicetree/bindings/clock/sifive,u54-prci.txt
+new file mode 100644
+index 0000000000000..88682c5eaebc6
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/sifive,u54-prci.txt
+@@ -0,0 +1,44 @@
++SiFive U54 SoC clocks
++
++This binding uses the common clock binding:
++    Documentation/devicetree/bindings/clock/clock-bindings.txt
++
++The U54 PRCI controller generates clocks for the U54 SoC. There is
++a core PLL that sets the processor frequency and PLLs for ethernet
++and DDR. It takes an input clock from the board, typically an oscillator
++or crystal.
++
++Required properties:
++- compatible: Should be "sifive,aloeprci0"
++- #clock-cells:       Should be <1>
++- reg:                Specifies base physical address and size of the registers
++- clocks:     phandles to the parent clock used as input
++
++Example:
++
++      refclk: refclk {
++              #clock-cells = <0>;
++              compatible = "fixed-clock";
++              clock-frequency = <33333333>;
++              clock-output-names = "xtal";
++      };
++
++      u54: prci@10000000 {
++              compatible = "sifive,aloeprci0";
++              reg = <0x0 0x10000000 0x0 0x1000>;
++              clocks = <&refclk>;
++              #clock-cells = <1>;
++      };
++
++      tlclk: tlclk {
++              compatible = "fixed-factor-clock";
++              clocks = <&u54 0>; /* Core frequency */
++              #clock-cells = <0>;
++              clock-div = <2>;
++              clock-mult = <1>;
++      };
++
++      ethernet@10090000 {
++              ...
++              clocks = <&prci 1>; /* TX clock */
++      };
+diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
+index ae40cbe770f05..017d8d12551b6 100644
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -89,6 +89,7 @@ obj-$(CONFIG_COMMON_CLK_QCOM)                += qcom/
+ obj-y                                 += renesas/
+ obj-$(CONFIG_ARCH_ROCKCHIP)           += rockchip/
+ obj-$(CONFIG_COMMON_CLK_SAMSUNG)      += samsung/
++obj-y                                 += sifive/
+ obj-$(CONFIG_ARCH_SIRF)                       += sirf/
+ obj-$(CONFIG_ARCH_SOCFPGA)            += socfpga/
+ obj-$(CONFIG_PLAT_SPEAR)              += spear/
diff --git a/target/linux/riscv64/patches-4.19/0101-003-gemgxl-mgmt-implement-clock-switch-for-GEM-tx_cl.patch b/target/linux/riscv64/patches-4.19/0101-003-gemgxl-mgmt-implement-clock-switch-for-GEM-tx_cl.patch
new file mode 100644 (file)
index 0000000..5cdcfda
--- /dev/null
@@ -0,0 +1,203 @@
+From 16736520d300998f5bddcabce9a8c99de4824778 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <[email protected]>
+Date: Tue, 6 Feb 2018 11:03:07 -0800
+Subject: [PATCH] gemgxl-mgmt: implement clock switch for GEM tx_clk
+
+XXX Should be changed to move this into the network device drivers
+as a management IP block
+---
+ .../bindings/clock/sifive,gemgxl-mgmt.txt     |  26 ++++
+ drivers/clk/sifive/Kconfig                    |   5 +
+ drivers/clk/sifive/Makefile                   |   1 +
+ drivers/clk/sifive/gemgxl-mgmt.c              | 129 ++++++++++++++++++
+ 4 files changed, 161 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt
+ create mode 100644 drivers/clk/sifive/gemgxl-mgmt.c
+
+diff --git a/Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt b/Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt
+new file mode 100644
+index 0000000000000..349489e33edaa
+--- /dev/null
++++ b/Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt
+@@ -0,0 +1,26 @@
++TX clock switch for GEMGXL in U540 SoCs
++
++This binding uses the common clock binding:
++    Documentation/devicetree/bindings/clock/clock-bindings.txt
++
++The U54 includes a clock mux to control the ethernet TX frequenecy. It
++switches between the local TX clock (125MHz) and PHY TX clocks. This is
++necessary to toggle between 1Gb and 100/10Mb speeds.
++
++Required properties:
++- compatible: Should be "sifive,cadencegemgxlmgmt0"
++- #clock-cells:       Should be <0>
++- reg:                Specifies base physical address and size of the registers
++
++Example:
++
++      mgmt: cadence-gemgxl-mgmt@100a00000 {
++              compatible = "sifive,cadencegemgxlmgmt0";
++              #clock-cells = <0>;
++              reg = <0x0 0x100a0000 0x0 0x1000>;
++      };
++
++      ethernet@10090000 {
++              ...
++              clocks = <&mgmt>; /* TX clock */
++      };
+diff --git a/drivers/clk/sifive/Makefile b/drivers/clk/sifive/Makefile
+index 74d58a4c07567..bf70be89b862c 100644
+--- a/drivers/clk/sifive/Makefile
++++ b/drivers/clk/sifive/Makefile
+@@ -1 +1,2 @@
+ obj-$(CONFIG_CLK_SIFIVE_FU540_PRCI)   += fu540-prci.o
++obj-$(CONFIG_CLK_GEMGXL_MGMT) += gemgxl-mgmt.o
+diff --git a/drivers/clk/sifive/gemgxl-mgmt.c b/drivers/clk/sifive/gemgxl-mgmt.c
+new file mode 100644
+index 0000000000000..00b07580fe3ce
+--- /dev/null
++++ b/drivers/clk/sifive/gemgxl-mgmt.c
+@@ -0,0 +1,129 @@
++/*
++ * 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.
++ *
++ * 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.
++ *
++ * Copyright (C) 2018 SiFive, Inc.
++ */
++
++#include <linux/clkdev.h>
++#include <linux/clk-provider.h>
++#include <linux/err.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++
++struct sifive_gemgxl_mgmt {
++      void __iomem *reg;
++      unsigned long rate;
++      struct clk_hw hw;
++};
++
++#define to_sifive_gemgxl_mgmt(mgmt) container_of(mgmt, struct sifive_gemgxl_mgmt, hw)
++
++static unsigned long sifive_gemgxl_mgmt_recalc_rate(struct clk_hw *hw,
++                                    unsigned long parent_rate)
++{
++      struct sifive_gemgxl_mgmt *mgmt = to_sifive_gemgxl_mgmt(hw);
++      return mgmt->rate;
++}
++
++static long sifive_gemgxl_mgmt_round_rate(struct clk_hw *hw, unsigned long rate,
++              unsigned long *parent_rate)
++{
++      if (WARN_ON(rate < 2500000)) {
++              return 2500000;
++      } else if (rate == 2500000) {
++              return 2500000;
++      } else if (WARN_ON(rate < 13750000)) {
++              return 2500000;
++      } else if (WARN_ON(rate < 25000000)) {
++              return 25000000;
++      } else if (rate == 25000000) {
++              return 25000000;
++      } else if (WARN_ON(rate < 75000000)) {
++              return 25000000;
++      } else if (WARN_ON(rate < 125000000)) {
++              return 125000000;
++      } else if (rate == 125000000) {
++              return 125000000;
++      } else {
++              WARN_ON(rate > 125000000);
++              return 125000000;
++      }
++}
++
++static int sifive_gemgxl_mgmt_set_rate(struct clk_hw *hw, unsigned long rate,
++              unsigned long parent_rate)
++{
++      struct sifive_gemgxl_mgmt *mgmt = to_sifive_gemgxl_mgmt(hw);
++      rate = sifive_gemgxl_mgmt_round_rate(hw, rate, &parent_rate);
++      iowrite32(rate != 125000000, mgmt->reg);
++      mgmt->rate = rate;
++      return 0;
++}
++
++static const struct clk_ops sifive_gemgxl_mgmt_ops = {
++      .recalc_rate = sifive_gemgxl_mgmt_recalc_rate,
++      .round_rate = sifive_gemgxl_mgmt_round_rate,
++      .set_rate = sifive_gemgxl_mgmt_set_rate,
++};
++
++static int sifive_gemgxl_mgmt_probe(struct platform_device *pdev)
++{
++      struct clk_init_data init;
++      struct sifive_gemgxl_mgmt *mgmt;
++      struct resource *res;
++      struct clk *clk;
++
++      mgmt = devm_kzalloc(&pdev->dev, sizeof(*mgmt), GFP_KERNEL);
++      if (!mgmt)
++              return -ENOMEM;
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      mgmt->reg = devm_ioremap_resource(&pdev->dev, res);
++      if (IS_ERR(mgmt->reg))
++              return PTR_ERR(mgmt->reg);
++
++      init.name = pdev->dev.of_node->name;
++      init.ops = &sifive_gemgxl_mgmt_ops;
++      init.flags = 0;
++      init.num_parents = 0;
++
++      mgmt->rate = 0;
++      mgmt->hw.init = &init;
++
++      clk = clk_register(NULL, &mgmt->hw);
++      if (IS_ERR(clk))
++              return PTR_ERR(clk);
++
++      of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
++
++      dev_info(&pdev->dev, "Registered clock switch '%s'\n", init.name);
++
++      return 0;
++}
++
++static const struct of_device_id sifive_gemgxl_mgmt_of_match[] = {
++      { .compatible = "sifive,cadencegemgxlmgmt0", },
++      {}
++};
++
++static struct platform_driver sifive_gemgxl_mgmt_driver = {
++      .driver = {
++              .name = "sifive-gemgxl-mgmt",
++              .of_match_table = sifive_gemgxl_mgmt_of_match,
++      },
++      .probe = sifive_gemgxl_mgmt_probe,
++};
++
++static int __init sifive_gemgxl_mgmt_init(void)
++{
++      return platform_driver_register(&sifive_gemgxl_mgmt_driver);
++}
++core_initcall(sifive_gemgxl_mgmt_init);
+diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
+index 8db4a3eb47820..b6028b46e2d4a 100644
+--- a/drivers/clk/sifive/Kconfig
++++ b/drivers/clk/sifive/Kconfig
+@@ -3,3 +3,8 @@
+       depends on RISCV
+       ---help---
+         Supports Power Reset Clock interface found in U540 SoCs
++
++config CLK_GEMGXL_MGMT
++      bool "TX clock switch for GEMGXL in U540 SoCs"
++      ---help---
++        Supports clock muxing between 10/100Mbit and 1Gbit TX clock on U5
diff --git a/target/linux/riscv64/patches-4.19/0101-004-spi-sifive.patch b/target/linux/riscv64/patches-4.19/0101-004-spi-sifive.patch
new file mode 100644 (file)
index 0000000..78f6c31
--- /dev/null
@@ -0,0 +1,509 @@
+From 1e23d75040e34fa26cacf715acb23a4344825bc2 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <[email protected]>
+Date: Mon, 5 Feb 2018 17:42:32 -0800
+Subject: [PATCH] spi-sifive: support SiFive SPI controller in Quad-Mode
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ .../devicetree/bindings/spi/spi-sifive.txt    |  29 ++
+ drivers/spi/Kconfig                           |   7 +
+ drivers/spi/Makefile                          |   1 +
+ drivers/spi/spi-sifive.c                      | 423 ++++++++++++++++++
+ 4 files changed, 460 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/spi/spi-sifive.txt
+ create mode 100644 drivers/spi/spi-sifive.c
+
+diff --git a/Documentation/devicetree/bindings/spi/spi-sifive.txt b/Documentation/devicetree/bindings/spi/spi-sifive.txt
+new file mode 100644
+index 0000000000000..945543244601b
+--- /dev/null
++++ b/Documentation/devicetree/bindings/spi/spi-sifive.txt
+@@ -0,0 +1,29 @@
++SiFive SPI controller Device Tree Bindings
++-------------------------------------------------
++
++Required properties:
++- compatible          : Should be "sifive,spi0"
++- reg                 : Physical base address and size of SPI registers map
++                        A second (optional) range can indicate memory mapped flash
++- interrupts          : Must contain one entry
++- interrupt-parent    : Must be core interrupt controller
++- clocks              : Must reference the frequency given to the controller
++- #address-cells      : Must be '1', indicating which CS to use
++- #size-cells         : Must be '0'
++
++Optional properties:
++- sifive,buffer-size  : Depth of hardware queues; defaults to 8
++- sifive,bits-per-word        : Maximum bits per word; defaults to 8
++
++Example:
++      spi: spi@10040000 {
++              compatible = "sifive,spi0";
++              reg = <0x0 0x10040000 0x0 0x1000 0x0 0x20000000 0x0 0x10000000>;
++              interrupt-parent = <&plic>;
++              interrupts = <51>;
++              clocks = <&tlclk>;
++              #address-cells = <1>;
++              #size-cells = <0>;
++              sifive,buffer-size = <8>;
++              sifive,bits-per-word = <8>;
++      };
+diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
+index ad5d68e1dab7c..f77cd28a84392 100644
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -736,6 +736,13 @@ config SPI_ZYNQMP_GQSPI
+       help
+         Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
++config SPI_SIFIVE
++        tristate "SiFive SPI controller"
++        depends on HAS_IOMEM
++      select SPI_BITBANG
++        help
++          This exposes the SPI controller IP from SiFive.
++
+ #
+ # Add new SPI master controllers in alphabetical order above this line
+ #
+diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
+index cb1f4378b87c0..5c8e8866b16f8 100644
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -106,6 +106,7 @@ obj-$(CONFIG_SPI_XILINX)           += spi-xilinx.o
+ obj-$(CONFIG_SPI_XLP)                 += spi-xlp.o
+ obj-$(CONFIG_SPI_XTENSA_XTFPGA)               += spi-xtensa-xtfpga.o
+ obj-$(CONFIG_SPI_ZYNQMP_GQSPI)                += spi-zynqmp-gqspi.o
++obj-$(CONFIG_SPI_SIFIVE)              += spi-sifive.o
+ # SPI slave protocol handlers
+ obj-$(CONFIG_SPI_SLAVE_TIME)          += spi-slave-time.o
+diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
+new file mode 100644
+index 0000000000000..208566a9888d7
+--- /dev/null
++++ b/drivers/spi/spi-sifive.c
+@@ -0,0 +1,423 @@
++/*
++ * SiFive SPI controller driver (master mode only)
++ *
++ * Author: SiFive, Inc.
++ *    [email protected]
++ *
++ * 2018 (c) SiFive Software, Inc.
++
++ * 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 <linux/clk.h>
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++#include <linux/io.h>
++#include <linux/log2.h>
++
++#define SIFIVE_SPI_MAX_CS     32
++
++#define SIFIVE_SPI_NAME "sifive_spi"
++
++#define SIFIVE_SPI_DEFAULT_DEPTH 8
++#define SIFIVE_SPI_DEFAULT_BITS       8
++
++#define XSPI_SCDR_OFFSET      0x000   /* Serial Clock Divisor Register */
++#define XSPI_SCD_SCALE_MASK   0xFFF
++
++#define XSPI_SCMR_OFFSET        0x004   /* Serial Clock Mode Register */
++#define XSPI_SCM_CPHA         1
++#define XSPI_SCM_CPOL         2
++#define XSPI_SCM_MODE_MASK     (XSPI_SCM_CPHA | XSPI_SCM_CPOL)
++
++#define XSPI_CSIDR_OFFSET       0x010
++#define XSPI_CSDR_OFFSET        0x014
++#define XSPI_CSMR_OFFSET        0x018
++#define XSPI_CSM_MODE_AUTO      0
++#define XSPI_CSM_MODE_HOLD      2
++#define XSPI_CSM_MODE_OFF       3
++
++#define XSPI_DC0R_OFFSET        0x028
++#define XSPI_CS_TO_SCK_MASK     0xFF
++#define XSPI_SCK_TO_CS_MASK     (0xFF << 16)
++#define XSPI_DC1R_OFFSET        0x02C
++#define XSPI_MIN_CS_IATIME_MASK 0xFF
++#define XSPI_MAX_IF_DELAY_MASK  (0xFF << 16)
++
++#define XSPI_FFR_OFFSET         0x040
++#define XSPI_FF_SINGLE          0
++#define XSPI_FF_DUAL            1
++#define XSPI_FF_QUAD            2
++#define XSPI_FF_SPI_MASK        0x3
++#define XSPI_FF_LSB_FIRST       4
++#define XSPI_FF_TX_DIR          8
++#define XSPI_FF_BPF_MASK        (0xFF << 16)
++
++#define XSPI_TXDR_OFFSET      0x048   /* Data Transmit Register */
++#define XSPI_TXD_FIFO_FULL      (8U << 28)
++#define XSPI_RXDR_OFFSET      0x04C   /* Data Receive Register */
++#define XSPI_RXD_FIFO_EMPTY     (8U << 28)
++#define XSPI_DATA_MASK          0xFF
++#define XSPI_DATA_SHIFT         8
++
++#define XSPI_TXWMR_OFFSET       0x050   /* TX FIFO Watermark Register */
++#define XSPI_RXWMR_OFFSET       0x054   /* RX FIFO Watermark Register */
++
++#define XSPI_FCTRL_OFFSET     0x60
++
++#define XSPI_IPR_OFFSET               0x074   /* Interrupt Pendings Register */
++#define XSPI_IER_OFFSET               0x070   /* Interrupt Enable Register */
++#define XSPI_TXWM_INTR          0x1
++#define XSPI_RXWM_INTR          0x2
++
++struct sifive_spi {
++      void __iomem    *regs;          /* virt. address of the control registers */
++      struct clk      *clk;           /* bus clock */
++      int             irq;            /* watermark irq */
++      int             buffer_size;    /* buffer size in words */
++      u32             cs_inactive;    /* Level of the CS pins when inactive*/
++      struct completion done;         /* Wake-up from interrupt */
++};
++
++static void sifive_spi_write(struct sifive_spi *spi, int offset, u32 value)
++{
++      iowrite32(value, spi->regs + offset);
++}
++
++static u32 sifive_spi_read(struct sifive_spi *spi, int offset)
++{
++      return ioread32(spi->regs + offset);
++}
++
++static void sifive_spi_init(struct sifive_spi *spi)
++{
++      /* Watermark interrupts are disabled by default */
++      sifive_spi_write(spi, XSPI_IER_OFFSET, 0);
++
++      /* Default watermark FIFO threshold values */
++      sifive_spi_write(spi, XSPI_TXWMR_OFFSET, 1);
++      sifive_spi_write(spi, XSPI_RXWMR_OFFSET, 0);
++
++      /* Set CS/SCK Delays and Inactive Time to defaults */
++
++      /* Exit specialized memory-mapped SPI flash mode */
++      sifive_spi_write(spi, XSPI_FCTRL_OFFSET, 0);
++}
++
++static void sifive_spi_prep_device(struct sifive_spi *spi, struct spi_device *device)
++{
++      u32 cr;
++
++      /* Update the chip select polarity */
++      if (device->mode & SPI_CS_HIGH)
++              spi->cs_inactive &= ~BIT(device->chip_select);
++      else
++              spi->cs_inactive |= BIT(device->chip_select);
++      sifive_spi_write(spi, XSPI_CSDR_OFFSET, spi->cs_inactive);
++
++      /* Select the correct device */
++      sifive_spi_write(spi, XSPI_CSIDR_OFFSET, device->chip_select);
++
++      /* Switch clock mode bits */
++      cr = sifive_spi_read(spi, XSPI_SCMR_OFFSET) & ~XSPI_SCM_MODE_MASK;
++      if (device->mode & SPI_CPHA)
++              cr |= XSPI_SCM_CPHA;
++      if (device->mode & SPI_CPOL)
++              cr |= XSPI_SCM_CPOL;
++      sifive_spi_write(spi, XSPI_SCMR_OFFSET, cr);
++}
++
++static int sifive_spi_prep_transfer(struct sifive_spi *spi, struct spi_device *device, struct spi_transfer *t)
++{
++      u32 hz, scale, cr;
++      int mode;
++
++      /* Calculate and program the clock rate */
++      hz = t->speed_hz ? t->speed_hz : device->max_speed_hz;
++      scale = (DIV_ROUND_UP(clk_get_rate(spi->clk) >> 1, hz) - 1) & XSPI_SCD_SCALE_MASK;
++      sifive_spi_write(spi, XSPI_SCDR_OFFSET, scale);
++
++      /* Modify the SPI protocol mode */
++      cr = sifive_spi_read(spi, XSPI_FFR_OFFSET);
++
++      /* LSB first? */
++      cr &= ~XSPI_FF_LSB_FIRST;
++      if (device->mode & SPI_LSB_FIRST)
++              cr |= XSPI_FF_LSB_FIRST;
++
++      /* SINGLE/DUAL/QUAD? */
++      mode = max((int)t->rx_nbits, (int)t->tx_nbits);
++      cr &= ~XSPI_FF_SPI_MASK;
++      switch (mode) {
++              case SPI_NBITS_QUAD: cr |= XSPI_FF_QUAD;   break;
++              case SPI_NBITS_DUAL: cr |= XSPI_FF_DUAL;   break;
++              default:             cr |= XSPI_FF_SINGLE; break;
++      }
++
++      /* SPI direction */
++      cr &= ~XSPI_FF_TX_DIR;
++      if (!t->rx_buf)
++              cr |= XSPI_FF_TX_DIR;
++
++      sifive_spi_write(spi, XSPI_FFR_OFFSET, cr);
++
++      /* We will want to poll if the time we need to wait is less than the context switching time.
++       * Let's call that threshold 5us. The operation will take:
++       *    (8/mode) * buffer_size / hz <= 5 * 10^-6
++       *    1600000 * buffer_size <= hz * mode
++       */
++      return 1600000 * spi->buffer_size <= hz * mode;
++}
++
++static void sifive_spi_tx(struct sifive_spi *spi, const u8* tx_ptr)
++{
++      BUG_ON((sifive_spi_read(spi, XSPI_TXDR_OFFSET) & XSPI_TXD_FIFO_FULL) != 0);
++      sifive_spi_write(spi, XSPI_TXDR_OFFSET, *tx_ptr & XSPI_DATA_MASK);
++}
++
++static void sifive_spi_rx(struct sifive_spi *spi, u8* rx_ptr)
++{
++        u32 data = sifive_spi_read(spi, XSPI_RXDR_OFFSET);
++        BUG_ON((data & XSPI_RXD_FIFO_EMPTY) != 0);
++        *rx_ptr = data & XSPI_DATA_MASK;
++}
++
++static void sifive_spi_wait(struct sifive_spi *spi, int bit, int poll)
++{
++      if (poll) {
++              u32 cr;
++              do cr = sifive_spi_read(spi, XSPI_IPR_OFFSET);
++              while (!(cr & bit));
++      } else {
++              reinit_completion(&spi->done);
++              sifive_spi_write(spi, XSPI_IER_OFFSET, bit);
++              wait_for_completion(&spi->done);
++      }
++}
++
++static void sifive_spi_execute(struct sifive_spi *spi, struct spi_transfer *t, int poll)
++{
++      int remaining_words = t->len;
++      const u8* tx_ptr = t->tx_buf;
++      u8* rx_ptr = t->rx_buf;
++
++      while (remaining_words) {
++              int n_words, tx_words, rx_words;
++              n_words = min(remaining_words, spi->buffer_size);
++
++              /* Enqueue n_words for transmission */
++              for (tx_words = 0; tx_words < n_words; ++tx_words)
++                      sifive_spi_tx(spi, tx_ptr++);
++
++              if (rx_ptr) {
++                      /* Wait for transmission + reception to complete */
++                      sifive_spi_write(spi, XSPI_RXWMR_OFFSET, n_words-1);
++                      sifive_spi_wait(spi, XSPI_RXWM_INTR, poll);
++
++                      /* Read out all the data from the RX FIFO */
++                      for (rx_words = 0; rx_words < n_words; ++rx_words)
++                              sifive_spi_rx(spi, rx_ptr++);
++              } else {
++                      /* Wait for transmission to complete */
++                      sifive_spi_wait(spi, XSPI_TXWM_INTR, poll);
++              }
++
++              remaining_words -= n_words;
++      }
++}
++
++static int sifive_spi_transfer_one(struct spi_master *master, struct spi_device *device, struct spi_transfer *t)
++{
++      struct sifive_spi *spi = spi_master_get_devdata(master);
++      int poll;
++
++      sifive_spi_prep_device(spi, device);
++      poll = sifive_spi_prep_transfer(spi, device, t);
++      sifive_spi_execute(spi, t, poll);
++
++      return 0;
++}
++
++static void sifive_spi_set_cs(struct spi_device *device, bool is_high)
++{
++      struct sifive_spi *spi = spi_master_get_devdata(device->master);
++
++      /* Reverse polarity is handled by SCMR/CPOL. Not inverted CS. */
++      if (device->mode & SPI_CS_HIGH)
++              is_high = !is_high;
++
++      sifive_spi_write(spi, XSPI_CSMR_OFFSET, is_high ? XSPI_CSM_MODE_AUTO : XSPI_CSM_MODE_HOLD);
++}
++
++static irqreturn_t sifive_spi_irq(int irq, void *dev_id)
++{
++      struct sifive_spi *spi = dev_id;
++      u32 ip;
++
++      ip = sifive_spi_read(spi, XSPI_IPR_OFFSET) & (XSPI_TXWM_INTR | XSPI_RXWM_INTR);
++      if (ip != 0) {
++              /* Disable interrupts until next transfer */
++              sifive_spi_write(spi, XSPI_IER_OFFSET, 0);
++              complete(&spi->done);
++              return IRQ_HANDLED;
++      }
++
++      return IRQ_NONE;
++}
++
++static int sifive_spi_probe(struct platform_device *pdev)
++{
++      struct sifive_spi *spi;
++      struct resource *res;
++      int ret, num_cs;
++      u32 cs_bits, buffer_size, bits_per_word;
++      struct spi_master *master;
++
++      master = spi_alloc_master(&pdev->dev, sizeof(struct sifive_spi));
++      if (!master) {
++              dev_err(&pdev->dev, "out of memory\n");
++              return -ENOMEM;
++      }
++
++      spi = spi_master_get_devdata(master);
++      init_completion(&spi->done);
++      platform_set_drvdata(pdev, master);
++
++      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;
++      }
++
++      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;
++      }
++
++      spi->irq = platform_get_irq(pdev, 0);
++      if (spi->irq < 0) {
++              dev_err(&pdev->dev, "Unable to find interrupt\n");
++              ret = spi->irq;
++              goto put_master;
++      }
++
++      /* Optional parameters */
++      ret = of_property_read_u32(pdev->dev.of_node, "sifive,buffer-size", &buffer_size);
++      if (ret < 0)
++              spi->buffer_size = SIFIVE_SPI_DEFAULT_DEPTH;
++      else
++              spi->buffer_size = buffer_size;
++
++      ret = of_property_read_u32(pdev->dev.of_node, "sifive,bits-per-word", &bits_per_word);
++      if (ret < 0)
++              bits_per_word = SIFIVE_SPI_DEFAULT_BITS;
++
++      /* 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;
++      }
++
++      /* probe the number of CS lines */
++      spi->cs_inactive = sifive_spi_read(spi, XSPI_CSDR_OFFSET);
++      sifive_spi_write(spi, XSPI_CSDR_OFFSET, 0xffffffffU);
++      cs_bits = sifive_spi_read(spi, XSPI_CSDR_OFFSET);
++      sifive_spi_write(spi, XSPI_CSDR_OFFSET, spi->cs_inactive);
++      if (!cs_bits) {
++              dev_err(&pdev->dev, "Could not auto probe CS lines\n");
++              ret = -EINVAL;
++              goto put_master;
++      }
++
++      num_cs = ilog2(cs_bits) + 1;
++      if (num_cs > SIFIVE_SPI_MAX_CS) {
++              dev_err(&pdev->dev, "Invalid number of spi slaves\n");
++              ret = -EINVAL;
++              goto put_master;
++      }
++
++      /* Define our master */
++      master->bus_num = pdev->id;
++      master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH |
++                          SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD;
++      master->flags = SPI_CONTROLLER_MUST_TX | SPI_MASTER_GPIO_SS;
++      master->dev.of_node = pdev->dev.of_node;
++      master->bits_per_word_mask = SPI_BPW_MASK(bits_per_word);
++      master->num_chipselect = num_cs;
++      master->transfer_one = sifive_spi_transfer_one;
++      master->set_cs = sifive_spi_set_cs;
++
++      /* If mmc_spi sees a dma_mask, it starts using dma mapped buffers.
++       * Probably it should rely on the SPI core auto mapping instead.
++       */
++      pdev->dev.dma_mask = 0;
++
++      /* Configure the SPI master hardware */
++      sifive_spi_init(spi);
++
++      /* Register for SPI Interrupt */
++      ret = devm_request_irq(&pdev->dev, spi->irq, sifive_spi_irq, 0,
++                              dev_name(&pdev->dev), spi);
++      if (ret) {
++              dev_err(&pdev->dev, "Unable to bind to interrupt\n");
++              goto put_master;
++      }
++
++      dev_info(&pdev->dev, "mapped; irq=%d, cs=%d\n",
++              spi->irq, master->num_chipselect);
++
++      ret = devm_spi_register_master(&pdev->dev, master);
++      if (ret < 0) {
++              dev_err(&pdev->dev, "spi_register_master failed\n");
++              goto put_master;
++      }
++
++      return 0;
++
++put_master:
++      spi_master_put(master);
++
++      return ret;
++}
++
++static int sifive_spi_remove(struct platform_device *pdev)
++{
++      struct spi_master *master = platform_get_drvdata(pdev);
++      struct sifive_spi *spi = spi_master_get_devdata(master);
++
++      /* Disable all the interrupts just in case */
++      sifive_spi_write(spi, XSPI_IER_OFFSET, 0);
++      spi_master_put(master);
++
++      return 0;
++}
++
++static const struct of_device_id sifive_spi_of_match[] = {
++      { .compatible = "sifive,spi0", },
++      {}
++};
++MODULE_DEVICE_TABLE(of, sifive_spi_of_match);
++
++static struct platform_driver sifive_spi_driver = {
++      .probe = sifive_spi_probe,
++      .remove = sifive_spi_remove,
++      .driver = {
++              .name = SIFIVE_SPI_NAME,
++              .of_match_table = sifive_spi_of_match,
++      },
++};
++module_platform_driver(sifive_spi_driver);
++
++MODULE_AUTHOR("SiFive, Inc. <[email protected]>");
++MODULE_DESCRIPTION("SiFive SPI driver");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/riscv64/patches-4.19/0101-005-spi-is25wp256d.patch b/target/linux/riscv64/patches-4.19/0101-005-spi-is25wp256d.patch
new file mode 100644 (file)
index 0000000..68851b4
--- /dev/null
@@ -0,0 +1,110 @@
+From c6e4a154bd008655dd69a850275d5cb082a7304b Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <[email protected]>
+Date: Mon, 5 Feb 2018 17:44:19 -0800
+Subject: [PATCH] spi-nor: add support for is25wp{32,64,128,256}
+
+Signed-off-by: Palmer Dabbelt <[email protected]>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 47 ++++++++++++++++++++++++++++++++++-
+ include/linux/mtd/spi-nor.h   |  2 ++
+ 2 files changed, 48 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
+index d9c368c441948..e9a3557a3c237 100644
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -1072,6 +1072,9 @@ static const struct flash_info spi_nor_ids[] = {
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+       { "is25wp128",  INFO(0x9d7018, 0, 64 * 1024, 256,
+                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
++      { "is25wp256d", INFO(0x9d7019, 0, 32 * 1024, 1024,
++                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES)
++      },
+       /* Macronix */
+       { "mx25l512e",   INFO(0xc22010, 0, 64 * 1024,   1, SECT_4K) },
+@@ -1515,6 +1518,45 @@ static int macronix_quad_enable(struct spi_nor *nor)
+       return 0;
+ }
++/**
++ * issi_unlock() - clear BP[0123] write-protection.
++ * @nor:      pointer to a 'struct spi_nor'
++ *
++ * Bits [2345] of the Status Register are BP[0123].
++ * ISSI chips use a different block protection scheme than other chips.
++ * Just disable the write-protect unilaterally.
++ *
++ * Return: 0 on success, -errno otherwise.
++ */
++static int issi_unlock(struct spi_nor *nor)
++{
++      int ret, val;
++      u8 mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3;
++
++      val = read_sr(nor);
++      if (val < 0)
++              return val;
++      if (!(val & mask))
++              return 0;
++
++      write_enable(nor);
++
++      write_sr(nor, val & ~mask);
++
++      ret = spi_nor_wait_till_ready(nor);
++      if (ret)
++              return ret;
++
++      ret = read_sr(nor);
++      if (ret > 0 && !(ret & mask)) {
++              dev_info(nor->dev, "ISSI Block Protection Bits cleared\n");
++              return 0;
++      } else {
++              dev_err(nor->dev, "ISSI Block Protection Bits not cleared\n");
++              return -EINVAL;
++      }
++}
++
+ /*
+  * Write status Register and configuration register with 2 bytes
+  * The first byte will be written to the status register, while the
+@@ -2747,6 +2789,9 @@ static int spi_nor_init(struct spi_nor *nor)
+               spi_nor_wait_till_ready(nor);
+       }
++      if (JEDEC_MFR(nor->info) == SNOR_MFR_ISSI)
++              issi_unlock(nor);
++
+       if (nor->quad_enable) {
+               err = nor->quad_enable(nor);
+               if (err) {
+@@ -2926,7 +2971,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
+       if (ret)
+               return ret;
+-      if (nor->addr_width) {
++      if (nor->addr_width && JEDEC_MFR(info) != SNOR_MFR_ISSI) {
+               /* already configured from SFDP */
+       } else if (info->addr_width) {
+               nor->addr_width = info->addr_width;
+diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
+index e60da0d34cc14..da422a37d3837 100644
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -23,6 +23,7 @@
+ #define SNOR_MFR_ATMEL                CFI_MFR_ATMEL
+ #define SNOR_MFR_GIGADEVICE   0xc8
+ #define SNOR_MFR_INTEL                CFI_MFR_INTEL
++#define SNOR_MFR_ISSI         0x9d
+ #define SNOR_MFR_MICRON               CFI_MFR_ST /* ST Micro <--> Micron */
+ #define SNOR_MFR_MACRONIX     CFI_MFR_MACRONIX
+ #define SNOR_MFR_SPANSION     CFI_MFR_AMD
+@@ -121,6 +122,7 @@
+ #define SR_BP0                        BIT(2)  /* Block protect 0 */
+ #define SR_BP1                        BIT(3)  /* Block protect 1 */
+ #define SR_BP2                        BIT(4)  /* Block protect 2 */
++#define SR_BP3                        BIT(5)  /* Block protect 3 (on ISSI chips) */
+ #define SR_TB                 BIT(5)  /* Top/Bottom protect */
+ #define SR_SRWD                       BIT(7)  /* SR write protect */
+ /* Spansion/Cypress specific status bits */
diff --git a/target/linux/riscv64/patches-4.19/0101-006-uart-sifive-serial-driver.patch b/target/linux/riscv64/patches-4.19/0101-006-uart-sifive-serial-driver.patch
new file mode 100644 (file)
index 0000000..9779f9f
--- /dev/null
@@ -0,0 +1,1128 @@
+From 6f1c41357974f377c4707a9b77125dd9cc9c2873 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <[email protected]>
+Date: Fri, 16 Mar 2018 11:34:26 -0700
+Subject: [PATCH] serial/sifive: initial driver from Paul Walmsley
+
+---
+ drivers/tty/serial/Kconfig       |   23 +
+ drivers/tty/serial/Makefile      |    1 +
+ drivers/tty/serial/sifive.c      | 1051 ++++++++++++++++++++++++++++++
+ include/uapi/linux/serial_core.h |    3 +
+ 4 files changed, 1078 insertions(+)
+ create mode 100644 drivers/tty/serial/sifive.c
+
+diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
+index df8bd0c7b97db..94a6de4b7dec4 100644
+--- a/drivers/tty/serial/Kconfig
++++ b/drivers/tty/serial/Kconfig
+@@ -1060,6 +1060,29 @@ config SERIAL_OMAP_CONSOLE
+         your boot loader about how to pass options to the kernel at
+         boot time.)
++config SERIAL_SIFIVE
++      tristate "SiFive UART support"
++      depends on OF
++      select SERIAL_CORE
++      help
++        If you have a SiFive Freedom U500 or similar SoC, enable this to
++        support the SiFive UART.
++
++config SERIAL_SIFIVE_CONSOLE
++      bool "Console on SiFive UART"
++      depends on SERIAL_SIFIVE=y
++      select SERIAL_CORE_CONSOLE
++      help
++        Select this option if you would like to use a SiFive UART as the
++        system console.
++
++        Even if you say Y here, the currently visible virtual console
++        (/dev/tty0) will still be used as the system console by default, but
++        you can alter that using a kernel command line option such as
++        "console=ttySIx". (Try "man bootparam" or see the documentation of
++        your boot loader about how to pass options to the kernel at
++        boot time.)
++
+ config SERIAL_LANTIQ
+       bool "Lantiq serial driver"
+       depends on LANTIQ
+diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
+index daac675612dff..7e906d3c04558 100644
+--- a/drivers/tty/serial/Makefile
++++ b/drivers/tty/serial/Makefile
+@@ -89,6 +89,7 @@ obj-$(CONFIG_SERIAL_MVEBU_UART)      += mvebu-uart.o
+ obj-$(CONFIG_SERIAL_PIC32)    += pic32_uart.o
+ obj-$(CONFIG_SERIAL_MPS2_UART)        += mps2-uart.o
+ obj-$(CONFIG_SERIAL_OWL)      += owl-uart.o
++obj-$(CONFIG_SERIAL_SIFIVE)   += sifive.o
+ # GPIOLIB helpers for modem control lines
+ obj-$(CONFIG_SERIAL_MCTRL_GPIO)       += serial_mctrl_gpio.o
+diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c
+new file mode 100644
+index 0000000000000..588fb31cc94d0
+--- /dev/null
++++ b/drivers/tty/serial/sifive.c
+@@ -0,0 +1,1051 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * SiFive UART driver
++ * Copyright (C) 2018 Paul Walmsley <[email protected]>
++ *
++ * Based partially on drivers/tty/serial/pxa.c, drivers/pwm/pwm-sifive.c,
++ * and drivers/tty/serial/omap-serial.c
++ *
++ * See Chapter 19 "Universal Asynchronous Receiver/Transmitter (UART)" of
++ * SiFive FE310-G000 v2p3.
++ *
++ * The SiFive UART design is not 8250-compatible.  The following common
++ * features are not supported:
++ * - Word lengths other than 8 bits
++ * - Break handling
++ * - Parity
++ * - Flow control
++ * - Modem signals (DSR, RI, etc.)
++ * On the other hand, the design is free from the baggage of the classical 8250
++ * programming model.
++ *
++ * 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.
++ */
++
++/* XXX Magic SYSRQ support - is it possible to implement? */
++/* XXX ignore_status_mask */
++/* XXX Ensure operations are spinlocked that need to be spinlocked */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/console.h>
++#include <linux/serial_reg.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <linux/serial_core.h>
++#include <linux/irq.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++
++/*
++ * Register offsets
++ */
++
++/* TXDATA */
++#define SIFIVE_SERIAL_TXDATA_OFFS             0x0
++#define SIFIVE_SERIAL_TXDATA_FULL_SHIFT               31
++#define SIFIVE_SERIAL_TXDATA_FULL_MASK                (1 << SIFIVE_SERIAL_TXDATA_FULL_SHIFT)
++#define SIFIVE_SERIAL_TXDATA_DATA_SHIFT               0
++#define SIFIVE_SERIAL_TXDATA_DATA_MASK                (0xff << SIFIVE_SERIAL_TXDATA_DATA_SHIFT)
++
++/* RXDATA */
++#define SIFIVE_SERIAL_RXDATA_OFFS             0x4
++#define SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT      31
++#define SIFIVE_SERIAL_RXDATA_EMPTY_MASK               (1 << SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT)
++#define SIFIVE_SERIAL_RXDATA_DATA_SHIFT               0
++#define SIFIVE_SERIAL_RXDATA_DATA_MASK                (0xff << SIFIVE_SERIAL_RXDATA_DATA_SHIFT)
++
++/* TXCTRL */
++#define SIFIVE_SERIAL_TXCTRL_OFFS             0x8
++#define SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT      16
++#define SIFIVE_SERIAL_TXCTRL_TXCNT_MASK               (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT)
++#define SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT      1
++#define SIFIVE_SERIAL_TXCTRL_NSTOP_MASK               (1 << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT)
++#define SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT               0
++#define SIFIVE_SERIAL_TXCTRL_TXEN_MASK                (1 << SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT)
++
++/* RXCTRL */
++#define SIFIVE_SERIAL_RXCTRL_OFFS             0xC
++#define SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT      16
++#define SIFIVE_SERIAL_RXCTRL_RXCNT_MASK               (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT)
++#define SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT               0
++#define SIFIVE_SERIAL_RXCTRL_RXEN_MASK                (1 << SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT)
++
++/* IE */
++#define SIFIVE_SERIAL_IE_OFFS                 0x10
++#define SIFIVE_SERIAL_IE_RXWM_SHIFT           1
++#define SIFIVE_SERIAL_IE_RXWM_MASK            (1 << SIFIVE_SERIAL_IE_RXWM_SHIFT)
++#define SIFIVE_SERIAL_IE_TXWM_SHIFT           0
++#define SIFIVE_SERIAL_IE_TXWM_MASK            (1 << SIFIVE_SERIAL_IE_TXWM_SHIFT)
++
++/* IP */
++#define SIFIVE_SERIAL_IP_OFFS                 0x14
++#define SIFIVE_SERIAL_IP_RXWM_SHIFT           1
++#define SIFIVE_SERIAL_IP_RXWM_MASK            (1 << SIFIVE_SERIAL_IP_RXWM_SHIFT)
++#define SIFIVE_SERIAL_IP_TXWM_SHIFT           0
++#define SIFIVE_SERIAL_IP_TXWM_MASK            (1 << SIFIVE_SERIAL_IP_TXWM_SHIFT)
++
++/* DIV */
++#define SIFIVE_SERIAL_DIV_OFFS                        0x18
++#define SIFIVE_SERIAL_DIV_DIV_SHIFT           0
++#define SIFIVE_SERIAL_DIV_DIV_MASK            (0xffff << SIFIVE_SERIAL_IP_DIV_SHIFT)
++
++/*
++ * Config macros
++ */
++
++/* SIFIVE_SERIAL_MAX_PORTS: maximum number of UARTs possible on a device */
++/* XXX Move to Kconfig? */
++#define SIFIVE_SERIAL_MAX_PORTS                       10
++
++/* SIFIVE_SERIAL_NAME: our driver's name that we pass to the operating system */
++#define SIFIVE_SERIAL_NAME                    "sifive-serial"
++
++/* SIFIVE_TTY_PREFIX: tty name prefix for SiFive serial ports */
++#define SIFIVE_TTY_PREFIX                     "ttySI"
++
++/*
++ *
++ */
++
++/**
++ * sifive_serial_port - driver-specific data extension to struct uart_port
++ * @port: struct uart_port embedded in this struct
++ * @dev: struct device *
++ * @ier: shadowed copy of the interrupt enable register
++ * @clkin_rate: input clock to the UART IP block.
++ * @bit_rate: UART serial line rate (e.g., 115200 bps)
++ * @clk_notifier: clock rate change notifier for upstream clock changes
++ */
++struct sifive_serial_port {
++      struct uart_port        port;
++      struct device           *dev;
++      unsigned char           ier;
++      unsigned long           clkin_rate;
++      unsigned long           bit_rate;
++      struct clk              *clk;
++      struct notifier_block   clk_notifier;
++};
++
++/*
++ * Structure container-of macros
++ */
++
++#define port_to_sifive_serial_port(p) (container_of((p), \
++                                                  struct sifive_serial_port, \
++                                                  port))
++
++#define notifier_to_sifive_serial_port(nb) (container_of((nb), \
++                                                       struct sifive_serial_port, \
++                                                       clk_notifier))
++
++/*
++ * Forward declarations
++ */
++static void sifive_serial_stop_tx(struct uart_port *port);
++
++/*
++ * Internal functions
++ */
++
++/**
++ * sifive_serial_early_write() - write to a UART register (early)
++ * @port: pointer to a struct uart_port record
++ * @offs: register address offset from the IP block base address
++ * @v: value to write to the register
++ *
++ * Given a pointer @port to a struct uart_port record, write the value @v to the
++ * IP block register address offset @offs.  This function is intended for early
++ * console use.
++ */
++static void sifive_serial_early_write(struct uart_port *port, u16 offs, u32 v)
++{
++      writel(v, port->membase + offs);
++}
++
++/**
++ * sifive_serial_early_read() - read from a UART register (early)
++ * @port: pointer to a struct uart_port record
++ * @offs: register address offset from the IP block base address
++ *
++ * Given a pointer @port to a struct uart_port record, read the contents of the
++ * IP block register located at offset @offs from the IP block base and return
++ * it.  This function is intended for early console use.
++ *
++ * Returns: the register value read from the UART.
++ */
++static u32 sifive_serial_early_read(struct uart_port *port, u16 offs)
++{
++      return readl(port->membase + offs);
++}
++
++/**
++ * sifive_serial_write() - write to a UART register
++ * @ssp: pointer to a struct sifive_serial_port record
++ * @offs: register address offset from the IP block base address
++ * @v: value to write to the register
++ *
++ * Write the value @v to the IP block register located at offset @offs from the
++ * IP block base, given a pointer @ssp to a struct sifive_serial_port record.
++ */
++static void sifive_serial_write(struct sifive_serial_port *ssp, u16 offs, u32 v)
++{
++      sifive_serial_early_write(&ssp->port, offs, v);
++}
++
++/**
++ * sifive_serial_read() - read from a UART register
++ * @ssp: pointer to a struct sifive_serial_port record
++ * @offs: register address offset from the IP block base address
++ *
++ * Read the contents of the IP block register located at offset @offs from the
++ * IP block base, given a pointer @ssp to a struct sifive_serial_port record.
++ *
++ * Returns: the value of the UART register
++ */
++static u32 sifive_serial_read(struct sifive_serial_port *ssp, u16 offs)
++{
++      return sifive_serial_early_read(&ssp->port, offs);
++}
++
++/**
++ * sifive_serial_is_txfifo_full() - is the TXFIFO full?
++ * @ssp: pointer to a struct sifive_serial_port
++ *
++ * Read the transmit FIFO "full" bit, returning a non-zero value if the
++ * TX FIFO is full, or zero if space remains.  Intended to be used to prevent
++ * writes to the TX FIFO when it's full.
++ *
++ * Returns: SIFIVE_SERIAL_TXDATA_FULL_MASK (non-zero) if the transmit FIFO
++ * is full, or 0 if space remains.
++ */
++static int sifive_serial_is_txfifo_full(struct sifive_serial_port *ssp)
++{
++      return sifive_serial_read(ssp, SIFIVE_SERIAL_TXDATA_OFFS) &
++              SIFIVE_SERIAL_TXDATA_FULL_MASK;
++}
++
++/**
++ * sifive_serial_transmit_char() - enqueue a byte to transmit onto the TX FIFO
++ * @ssp: pointer to a struct sifive_serial_port
++ * @ch: character to transmit
++ *
++ * Enqueue a byte @ch onto the transmit FIFO, given a pointer @ssp to the
++ * struct sifive_serial_port * to transmit on.  Caller should first check to
++ * ensure that the TXFIFO has space; see sifive_serial_is_txfifo_full().
++ */
++static void sifive_serial_transmit_char(struct sifive_serial_port *ssp, int ch)
++{
++      sifive_serial_write(ssp, SIFIVE_SERIAL_TXDATA_OFFS, ch);
++}
++
++/**
++ * sifive_serial_transmit_chars() - enqueue multiple bytes onto the TX FIFO
++ * @ssp: pointer to a struct sifive_serial_port
++ *
++ * Transfer up to a TX FIFO size's worth of characters from the Linux serial
++ * transmit buffer to the SiFive UART TX FIFO.
++ */
++static void sifive_serial_transmit_chars(struct sifive_serial_port *ssp)
++{
++      struct circ_buf *xmit = &ssp->port.state->xmit;
++      int count;
++
++      if (ssp->port.x_char) {
++              sifive_serial_transmit_char(ssp, ssp->port.x_char);
++              ssp->port.icount.tx++;
++              ssp->port.x_char = 0;
++              return;
++      }
++      if (uart_circ_empty(xmit) || uart_tx_stopped(&ssp->port)) {
++              sifive_serial_stop_tx(&ssp->port);
++              return;
++      }
++      count = ssp->port.fifosize;
++      do {
++              sifive_serial_transmit_char(ssp, xmit->buf[xmit->tail]);
++              xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
++              ssp->port.icount.tx++;
++              if (uart_circ_empty(xmit))
++                      break;
++      } while (--count > 0);
++
++      if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
++              uart_write_wakeup(&ssp->port);
++
++      if (uart_circ_empty(xmit))
++              sifive_serial_stop_tx(&ssp->port);
++}
++
++/**
++ * sifive_serial_enable_txwm() - enable transmit watermark interrupts
++ * @ssp: pointer to a struct sifive_serial_port
++ *
++ * Enable interrupt generation when the transmit FIFO watermark is reached
++ * on the UART referred to by @ssp.
++ */
++static void sifive_serial_enable_txwm(struct sifive_serial_port *ssp)
++{
++      if (ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK)
++              return;
++
++      ssp->ier |= SIFIVE_SERIAL_IE_TXWM_MASK;
++      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ssp->ier);
++}
++
++/**
++ * sifive_serial_enable_rxwm() - enable receive watermark interrupts
++ * @ssp: pointer to a struct sifive_serial_port
++ *
++ * Enable interrupt generation when the receive FIFO watermark is reached
++ * on the UART referred to by @ssp.
++ */
++static void sifive_serial_enable_rxwm(struct sifive_serial_port *ssp)
++{
++      if (ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK)
++              return;
++
++      ssp->ier |= SIFIVE_SERIAL_IE_RXWM_MASK;
++      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ssp->ier);
++}
++
++/**
++ * sifive_serial_disable_txwm() - disable transmit watermark interrupts
++ * @ssp: pointer to a struct sifive_serial_port
++ *
++ * Disable interrupt generation when the transmit FIFO watermark is reached
++ * on the UART referred to by @ssp.
++ */
++static void sifive_serial_disable_txwm(struct sifive_serial_port *ssp)
++{
++      if (!(ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK))
++              return;
++
++      ssp->ier &= ~SIFIVE_SERIAL_IE_TXWM_MASK;
++      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ssp->ier);
++}
++
++/**
++ * sifive_serial_disable_rxwm() - disable receive watermark interrupts
++ * @ssp: pointer to a struct sifive_serial_port
++ *
++ * Disable interrupt generation when the receive FIFO watermark is reached
++ * on the UART referred to by @ssp.
++ */
++static void sifive_serial_disable_rxwm(struct sifive_serial_port *ssp)
++{
++      if (!(ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK))
++              return;
++
++      ssp->ier &= ~SIFIVE_SERIAL_IE_RXWM_MASK;
++      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ssp->ier);
++}
++
++/**
++ * sifive_serial_receive_char() - receive a byte from the UART
++ * @ssp: pointer to a struct sifive_serial_port
++ * @is_empty: char pointer to return whether the RX FIFO is empty
++ *
++ * Try to read a byte from the SiFive UART RX FIFO, referenced by
++ * @ssp, and to return it.  Also returns the RX FIFO empty bit in
++ * the char pointed to by @ch.  The caller must pass the byte back to the
++ * Linux serial layer if needed.
++ *
++ * Returns: the byte read from the UART RX FIFO.
++ */
++static char sifive_serial_receive_char(struct sifive_serial_port *ssp,
++                                     char *is_empty)
++{
++      u32 v;
++      u8 ch;
++
++      v = sifive_serial_read(ssp, SIFIVE_SERIAL_RXDATA_OFFS);
++
++      if (!is_empty)
++              WARN_ON(1);
++      else
++              *is_empty = (v & SIFIVE_SERIAL_RXDATA_EMPTY_MASK) >>
++                      SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT;
++
++      ch = (v & SIFIVE_SERIAL_RXDATA_DATA_MASK) >>
++              SIFIVE_SERIAL_RXDATA_DATA_SHIFT;
++
++      return ch;
++}
++
++/**
++ * sifive_serial_receive_chars() - receive multiple bytes from the UART
++ * @ssp: pointer to a struct sifive_serial_port
++ *
++ * Receive up to an RX FIFO's worth of bytes from the SiFive UART referred
++ * to by @ssp and pass them up to the Linux serial layer.
++ */
++static void sifive_serial_receive_chars(struct sifive_serial_port *ssp)
++{
++      unsigned char ch;
++      char is_empty;
++      int c;
++
++      for (c = ssp->port.fifosize; c > 0; --c) {
++              ch = sifive_serial_receive_char(ssp, &is_empty);
++              if (is_empty) break;
++
++              ssp->port.icount.rx++;
++              uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL);
++      }
++}
++
++/**
++ * sifive_serial_update_div() - calculate the divisor setting by the line rate
++ * @ssp: pointer to a struct sifive_serial_port
++ *
++ * Calculate the appropriate value of the clock divisor for the UART
++ * referred to by @ssp and the target line rate referred to by @bps, and
++ * return it.
++ */
++static void sifive_serial_update_div(struct sifive_serial_port *ssp)
++{
++      u16 div = DIV_ROUND_UP(ssp->clkin_rate, ssp->bit_rate) - 1;
++      /* XXX check for div out of spec */
++      sifive_serial_write(ssp, SIFIVE_SERIAL_DIV_OFFS, div);
++}
++
++/**
++ * sifive_serial_update_bit_rate() - set the UART "baud rate"
++ * @ssp: pointer to a struct sifive_serial_port
++ * @rate: new target bit rate
++ *
++ * Calculate the UART divisor value for the target bit rate @rate for the
++ * SiFive UART described by @ssp and program it into the UART.  There may
++ * be some error between the target bit rate and the actual bit rate implemented
++ * by the UART due to clock ratio granularity.
++ */
++static void sifive_serial_update_bit_rate(struct sifive_serial_port *ssp,
++                                        unsigned int rate)
++{
++      if (ssp->bit_rate == rate)
++              return;
++
++      ssp->bit_rate = rate;
++      sifive_serial_update_div(ssp);
++}
++
++/**
++ * sifive_serial_set_stop_bits() - set the number of stop bits
++ * @ssp: pointer to a struct sifive_serial_port
++ * @nstop: 1 or 2 (stop bits)
++ *
++ * Program the SiFive UART referred to by @ssp to use @nstop stop bits.
++ */
++static void sifive_serial_set_stop_bits(struct sifive_serial_port *ssp,
++                                      char nstop)
++{
++      u32 v;
++
++      if (nstop < 1 || nstop > 2) {
++              WARN_ON(1);
++              return;
++      }
++
++      v = sifive_serial_read(ssp, SIFIVE_SERIAL_TXCTRL_OFFS);
++      v &= ~SIFIVE_SERIAL_TXCTRL_NSTOP_MASK;
++      v |= (nstop-1) << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT;
++      sifive_serial_write(ssp, SIFIVE_SERIAL_TXCTRL_OFFS, v);
++}
++
++/**
++ * sifive_serial_wait_for_xmitr() - wait for an empty slot on the TX FIFO
++ * @ssp: pointer to a struct sifive_serial_port
++ *
++ * Delay while the UART TX FIFO referred to by @ssp is marked as full.
++ *
++ * XXX Probably should use a timeout/bailout.
++ */
++static inline void sifive_serial_wait_for_xmitr(struct sifive_serial_port *ssp)
++{
++      while (sifive_serial_is_txfifo_full(ssp))
++              udelay(1); /* XXX Should vary by bps rate */
++}
++
++/*
++ * Linux serial API functions
++ */
++
++static void sifive_serial_stop_tx(struct uart_port *port)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++      sifive_serial_disable_txwm(ssp);
++}
++
++static void sifive_serial_stop_rx(struct uart_port *port)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++      sifive_serial_disable_rxwm(ssp);
++}
++
++static void sifive_serial_start_tx(struct uart_port *port)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++      sifive_serial_enable_txwm(ssp);
++}
++
++static irqreturn_t sifive_serial_irq(int irq, void *dev_id)
++{
++      struct sifive_serial_port *ssp = dev_id;
++      irqreturn_t r = IRQ_NONE;
++      int c = ssp->port.fifosize;
++      u32 ip;
++
++      spin_lock(&ssp->port.lock);
++
++      do {
++              ip = sifive_serial_read(ssp, SIFIVE_SERIAL_IP_OFFS);
++              if (!ip)
++                      break;
++
++              r = IRQ_HANDLED;
++
++              if (ip & SIFIVE_SERIAL_IP_RXWM_MASK)
++                      sifive_serial_receive_chars(ssp);
++              if (ip & SIFIVE_SERIAL_IP_TXWM_MASK)
++                      sifive_serial_transmit_chars(ssp);
++      } while (c--);
++
++      spin_unlock(&ssp->port.lock);
++
++      tty_flip_buffer_push(&ssp->port.state->port);
++
++      return r;
++}
++
++static unsigned int sifive_serial_tx_empty(struct uart_port *port)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++      return !sifive_serial_is_txfifo_full(ssp);
++}
++
++static unsigned int sifive_serial_get_mctrl(struct uart_port *port)
++{
++      return 0; /* XXX -EINVAL? */
++}
++
++static void sifive_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++      // dev_err(port->dev, "set_mctrl not supported\n");
++}
++
++static void sifive_serial_break_ctl(struct uart_port *port, int break_state)
++{
++      dev_err(port->dev, "sending break not supported\n");
++}
++
++static int sifive_serial_startup(struct uart_port *port)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++      sifive_serial_enable_rxwm(ssp);
++
++      return 0;
++}
++
++static void sifive_serial_shutdown(struct uart_port *port)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++      sifive_serial_disable_rxwm(ssp);
++      sifive_serial_disable_txwm(ssp);
++}
++
++/**
++ * sifive_serial_clk_notifier() - clock post-rate-change notifier
++ * @nb: pointer to the struct notifier_block, from the notifier code
++ * @event: event mask from the notifier code
++ * @data: pointer to the struct clk_notifier_data from the notifier code
++ *
++ * On the H5U SoC, the UART IP block is derived from the CPU clock source
++ * after a synchronous divide-by-two divider, so any CPU clock rate change
++ * requires the UART baud rate to be updated.  This presumably could corrupt any
++ * serial word currently being transmitted or received.  It would probably
++ * be better to stop receives and transmits, then complete the baud rate
++ * change, then re-enable them.
++ */
++static int sifive_serial_clk_notifier(struct notifier_block *nb,
++                                    unsigned long event, void *data)
++{
++      struct clk_notifier_data *cnd = data;
++      struct sifive_serial_port *ssp = notifier_to_sifive_serial_port(nb);
++
++      if (event == POST_RATE_CHANGE && ssp->clkin_rate != cnd->new_rate) {
++              ssp->clkin_rate = cnd->new_rate;
++              sifive_serial_update_div(ssp);
++      }
++
++      return NOTIFY_OK;
++}
++
++static void sifive_serial_set_termios(struct uart_port *port,
++                                    struct ktermios *termios,
++                                    struct ktermios *old)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++      int rate;
++      char nstop;
++
++      if ((termios->c_cflag & CSIZE) != CS8) {
++              dev_err(ssp->port.dev, "only 8-bit words supported\n");
++              return;
++      }
++
++      /* Set number of stop bits */
++      nstop = (termios->c_cflag & CSTOPB) ? 2 : 1;
++      sifive_serial_set_stop_bits(ssp, nstop);
++
++      /* Set line rate */
++      rate = uart_get_baud_rate(port, termios, old, 0, ssp->clkin_rate / 16);
++      sifive_serial_update_bit_rate(ssp, rate);
++
++      /* XXX Enable FIFOs with watermark 1 */
++
++#if 0
++      spin_lock_irqsave(&ssp->port.lock, flags);
++#endif
++
++      /*
++       * Update the per-port timeout.
++       */
++      uart_update_timeout(port, termios->c_cflag, rate);
++
++      /* XXX */
++      ssp->port.read_status_mask = 0;
++      if (termios->c_iflag & INPCK) {
++              dev_err(ssp->port.dev, "INPCK flag not supported\n");
++              return;
++      }
++      if (termios->c_iflag & (BRKINT | PARMRK)) {
++              dev_err(ssp->port.dev, "BRKINT/PARMRK flag not supported\n");
++              return;
++      }
++
++#if 0
++      /*
++       * ignore all characters if CREAD is not set
++       */
++      if ((termios->c_cflag & CREAD) == 0)
++              ssp->port.ignore_status_mask |= UART_LSR_DR;
++#endif
++
++      /* XXX enable interrupts */
++}
++
++static void sifive_serial_release_port(struct uart_port *port)
++{
++}
++
++static int sifive_serial_request_port(struct uart_port *port)
++{
++      return 0;
++}
++
++static void sifive_serial_config_port(struct uart_port *port, int flags)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++      ssp->port.type = PORT_SIFIVE_H5U;
++}
++
++static int sifive_serial_verify_port(struct uart_port *port,
++                                   struct serial_struct *ser)
++{
++      return -EINVAL;
++}
++
++static const char *sifive_serial_type(struct uart_port *port)
++{
++      return port->type == PORT_SIFIVE_H5U ? SIFIVE_SERIAL_NAME : NULL;
++}
++
++/*
++ * Polling support
++ */
++
++#ifdef CONFIG_CONSOLE_POLL
++
++static void sifive_serial_poll_put_char(struct uart_port *port,
++                                      unsigned char ch)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++      sifive_serial_wait_for_xmitr(ssp);
++      sifive_serial_write(ssp, SIFIVE_SERIAL_TXDATA_OFFS, ch);
++}
++
++static int sifive_serial_poll_get_char(struct uart_port *port)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++      char is_empty, ch;
++
++      ch = sifive_serial_receive_char(ssp, &is_empty);
++      if (is_empty)
++              return NO_POLL_CHAR;
++
++      return ch;
++}
++
++#endif /* CONFIG_CONSOLE_POLL */
++
++/*
++ * Earlyconsole support
++ */
++
++#ifdef CONFIG_SERIAL_EARLYCON
++static void early_sifive_serial_putc(struct uart_port *port, int c)
++{
++      while (sifive_serial_early_read(port, SIFIVE_SERIAL_TXDATA_OFFS) &
++             SIFIVE_SERIAL_TXDATA_FULL_MASK)
++              cpu_relax();
++
++      sifive_serial_early_write(port, SIFIVE_SERIAL_TXDATA_OFFS, c);
++}
++
++void early_sifive_serial_write(struct console *console, const char *s,
++                             unsigned int count)
++{
++      struct earlycon_device *device = console->data;
++      struct uart_port *port = &device->port;
++
++      uart_console_write(port, s, count, early_sifive_serial_putc);
++}
++
++static int __init early_sifive_serial_setup(struct earlycon_device *device,
++                                          const char *options)
++{
++      struct uart_port *port = &device->port;
++
++      if (!(port->membase || port->iobase))
++              return -ENODEV;
++
++      device->con->write = early_sifive_serial_write;
++      return 0;
++}
++
++OF_EARLYCON_DECLARE(sifive, "sifive,freedom-uart", early_sifive_serial_setup);
++#endif /* CONFIG_SERIAL_EARLYCON */
++
++/*
++ * Linux console interface
++ */
++
++#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE
++
++static struct sifive_serial_port *sifive_serial_console_ports[SIFIVE_SERIAL_MAX_PORTS];
++
++static void sifive_serial_console_putchar(struct uart_port *port, int ch)
++{
++      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++      sifive_serial_wait_for_xmitr(ssp);
++      sifive_serial_transmit_char(ssp, ch);
++}
++
++static void sifive_serial_console_write(struct console *co, const char *s,
++                                      unsigned int count)
++{
++      struct sifive_serial_port *ssp = sifive_serial_console_ports[co->index];
++      unsigned long flags;
++      unsigned int ier;
++      int locked = 1;
++
++      if (!ssp) return;
++
++      local_irq_save(flags);
++      if (ssp->port.sysrq)
++              locked = 0;
++      else if (oops_in_progress)
++              locked = spin_trylock(&ssp->port.lock);
++      else
++              spin_lock(&ssp->port.lock);
++
++      ier = sifive_serial_read(ssp, SIFIVE_SERIAL_IE_OFFS);
++      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, 0);
++
++      uart_console_write(&ssp->port, s, count, sifive_serial_console_putchar);
++
++      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ier);
++
++      if (locked)
++              spin_unlock(&ssp->port.lock);
++      local_irq_restore(flags);
++}
++
++static int __init sifive_serial_console_setup(struct console *co, char *options)
++{
++      struct sifive_serial_port *ssp;
++      int baud = 115200;
++      int bits = 8;
++      int parity = 'n';
++      int flow = 'n';
++
++      ssp = sifive_serial_console_ports[co->index];
++      if (!ssp)
++              return -ENODEV;
++
++      if (options)
++              uart_parse_options(options, &baud, &parity, &bits, &flow);
++
++      return uart_set_options(&ssp->port, co, baud, parity, bits, flow);
++}
++
++static struct uart_driver sifive_serial_uart_driver;
++
++static struct console sifive_serial_console = {
++      .name           = SIFIVE_TTY_PREFIX,
++      .write          = sifive_serial_console_write,
++      .device         = uart_console_device,
++      .setup          = sifive_serial_console_setup,
++      .flags          = CON_PRINTBUFFER,
++      .index          = -1,
++      .data           = &sifive_serial_uart_driver,
++};
++
++static void sifive_serial_add_console_port(struct sifive_serial_port *ssp)
++{
++      sifive_serial_console_ports[ssp->port.line] = ssp;
++}
++
++static void sifive_serial_remove_console_port(struct sifive_serial_port *ssp)
++{
++      sifive_serial_console_ports[ssp->port.line] = 0;
++}
++
++#define SIFIVE_SERIAL_CONSOLE (&sifive_serial_console)
++
++#else
++
++#define SIFIVE_SERIAL_CONSOLE NULL
++
++static inline void sifive_serial_add_console_port(struct sifive_serial_port *ssp)
++{}
++static void sifive_serial_remove_console_port(struct sifive_serial_port *ssp)
++{}
++
++#endif
++
++static const struct uart_ops sifive_serial_uops = {
++      .tx_empty       = sifive_serial_tx_empty,
++      .set_mctrl      = sifive_serial_set_mctrl,
++      .get_mctrl      = sifive_serial_get_mctrl,
++      .stop_tx        = sifive_serial_stop_tx,
++      .start_tx       = sifive_serial_start_tx,
++      .stop_rx        = sifive_serial_stop_rx,
++      .break_ctl      = sifive_serial_break_ctl,
++      .startup        = sifive_serial_startup,
++      .shutdown       = sifive_serial_shutdown,
++      .set_termios    = sifive_serial_set_termios,
++      .type           = sifive_serial_type,
++      .release_port   = sifive_serial_release_port,
++      .request_port   = sifive_serial_request_port,
++      .config_port    = sifive_serial_config_port,
++      .verify_port    = sifive_serial_verify_port,
++#ifdef CONFIG_CONSOLE_POLL
++      .poll_put_char  = sifive_serial_poll_put_char,
++      .poll_get_char  = sifive_serial_poll_get_char,
++#endif
++};
++
++static struct uart_driver sifive_serial_uart_driver = {
++      .owner          = THIS_MODULE,
++      .driver_name    = SIFIVE_SERIAL_NAME,
++      .dev_name       = "ttySI",
++      .nr             = SIFIVE_SERIAL_MAX_PORTS,
++      .cons           = SIFIVE_SERIAL_CONSOLE,
++};
++
++static int sifive_serial_probe(struct platform_device *pdev)
++{
++      struct sifive_serial_port *ssp;
++      struct resource *mem;
++      struct clk *clk;
++      void __iomem *base;
++      int irq, id, r;
++
++      irq = platform_get_irq(pdev, 0);
++      if (irq < 0) {
++              dev_err(&pdev->dev, "could not acquire interrupt\n");
++              return -EPROBE_DEFER;
++      }
++
++      mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      base = devm_ioremap_resource(&pdev->dev, mem);
++      if (IS_ERR(base)) {
++              dev_err(&pdev->dev, "could not acquire device memory\n");
++              return PTR_ERR(base);
++      }
++
++      clk = devm_clk_get(&pdev->dev, NULL);
++      if (IS_ERR(clk)) {
++              dev_err(&pdev->dev, "unable to find controller clock\n");
++              return PTR_ERR(clk);
++      }
++
++      id = of_alias_get_id(pdev->dev.of_node, "serial");
++      if (id < 0) {
++              dev_err(&pdev->dev, "missing aliases entry\n");
++              return id;
++      }
++
++#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE
++      if (id > SIFIVE_SERIAL_MAX_PORTS) {
++              dev_err(&pdev->dev, "too many UARTs (%d)\n", id);
++              return -EINVAL;
++      }
++#endif
++
++      ssp = devm_kzalloc(&pdev->dev, sizeof(*ssp), GFP_KERNEL);
++      if (!ssp)
++              return -ENOMEM;
++
++      ssp->port.dev = &pdev->dev;
++      ssp->port.type = PORT_SIFIVE_H5U;
++      ssp->port.iotype = UPIO_MEM;
++      ssp->port.irq = irq;
++      ssp->port.fifosize = 8;
++      ssp->port.ops = &sifive_serial_uops;
++      ssp->port.line = id;
++      ssp->port.mapbase = mem->start;
++      ssp->port.membase = base;
++      ssp->dev = &pdev->dev;
++      ssp->clk = clk;
++      ssp->clk_notifier.notifier_call = sifive_serial_clk_notifier;
++
++      r = clk_notifier_register(ssp->clk, &ssp->clk_notifier);
++      if (r) {
++              dev_err(&pdev->dev, "could not register clock notifier: %d\n",
++                      r);
++              goto probe_out1;
++      }
++
++      /* Setup clock divider */
++      ssp->clkin_rate = clk_get_rate(ssp->clk);
++      ssp->bit_rate = 115200;
++      sifive_serial_update_div(ssp);
++
++      platform_set_drvdata(pdev, ssp);
++
++      /* Enable transmits and set the watermark level to 1 */
++      sifive_serial_write(ssp, SIFIVE_SERIAL_TXCTRL_OFFS,
++                          (1 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT) |
++                          SIFIVE_SERIAL_TXCTRL_TXEN_MASK);
++
++      /* Enable receives and set the watermark level to 0 */
++      sifive_serial_write(ssp, SIFIVE_SERIAL_RXCTRL_OFFS,
++                          (0 << SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT) |
++                          SIFIVE_SERIAL_RXCTRL_RXEN_MASK);
++
++      r = request_irq(ssp->port.irq, sifive_serial_irq, ssp->port.irqflags,
++                      dev_name(&pdev->dev), ssp);
++      if (r) {
++              dev_err(&pdev->dev, "could not attach interrupt: %d\n", r);
++              goto probe_out2;
++      }
++
++      r = uart_add_one_port(&sifive_serial_uart_driver, &ssp->port);
++      if (r != 0) {
++              dev_err(&pdev->dev, "could not add uart: %d\n", r);
++              goto probe_out3;
++      }
++
++      sifive_serial_add_console_port(ssp);
++
++      return 0;
++
++probe_out3:
++      free_irq(ssp->port.irq, ssp);
++probe_out2:
++      clk_notifier_unregister(ssp->clk, &ssp->clk_notifier);
++probe_out1:
++      return r;
++}
++
++static int sifive_serial_remove(struct platform_device *dev)
++{
++      struct sifive_serial_port *ssp = platform_get_drvdata(dev);
++
++      sifive_serial_remove_console_port(ssp);
++      uart_remove_one_port(&sifive_serial_uart_driver, &ssp->port);
++      free_irq(ssp->port.irq, ssp);
++      clk_notifier_unregister(ssp->clk, &ssp->clk_notifier);
++
++      return 0;
++}
++
++static const struct of_device_id sifive_serial_of_match[] = {
++      { .compatible = "sifive,uart0" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, sifive_serial_match);
++
++static struct platform_driver sifive_serial_platform_driver = {
++      .probe          = sifive_serial_probe,
++      .remove         = sifive_serial_remove,
++      .driver         = {
++              .name   = SIFIVE_SERIAL_NAME,
++              .of_match_table = of_match_ptr(sifive_serial_of_match),
++      },
++};
++
++static int __init sifive_serial_init(void)
++{
++      struct tty_driver *tty_drv;
++      int r;
++
++      r = uart_register_driver(&sifive_serial_uart_driver);
++      if (r) goto init_out1;
++
++      tty_drv = sifive_serial_uart_driver.tty_driver;
++      if (!tty_drv) goto init_out2;
++
++      /* change default terminal settings for SiFive uarts */
++      tty_drv->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
++      tty_drv->init_termios.c_ispeed = 115200;
++      tty_drv->init_termios.c_ospeed = 115200;
++
++      r = platform_driver_register(&sifive_serial_platform_driver);
++      if (r) goto init_out2;
++
++      return 0;
++
++init_out2:
++      uart_unregister_driver(&sifive_serial_uart_driver);
++init_out1:
++      return r;
++}
++
++static void __exit sifive_serial_exit(void)
++{
++      platform_driver_unregister(&sifive_serial_platform_driver);
++      uart_unregister_driver(&sifive_serial_uart_driver);
++}
++
++module_init(sifive_serial_init);
++module_exit(sifive_serial_exit);
++
++MODULE_DESCRIPTION("SiFive UART serial driver");
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Paul Walmsley <[email protected]>");
+diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
+index dce5f9dae1210..86973c3854145 100644
+--- a/include/uapi/linux/serial_core.h
++++ b/include/uapi/linux/serial_core.h
+@@ -281,4 +281,7 @@
+ /* MediaTek BTIF */
+ #define PORT_MTK_BTIF 117
++/* SiFive UART */
++#define PORT_SIFIVE_H5U       118
++
+ #endif /* _UAPILINUX_SERIAL_CORE_H */
diff --git a/target/linux/riscv64/patches-4.19/0101-007-gpio-sifive-support-GPIO-on-SiFive-SoCs.patch b/target/linux/riscv64/patches-4.19/0101-007-gpio-sifive-support-GPIO-on-SiFive-SoCs.patch
new file mode 100644 (file)
index 0000000..ce41ece
--- /dev/null
@@ -0,0 +1,409 @@
+From 28447771a2dddf9c083e1eefa5848d03e83496c7 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <[email protected]>
+Date: Wed, 21 Feb 2018 15:43:02 -0800
+Subject: [PATCH 07/11] gpio-sifive: support GPIO on SiFive SoCs
+
+---
+ .../devicetree/bindings/gpio/gpio-sifive.txt       |  28 ++
+ drivers/gpio/Kconfig                               |   7 +
+ drivers/gpio/Makefile                              |   1 +
+ drivers/gpio/gpio-sifive.c                         | 322 +++++++++++++++++++++
+ 4 files changed, 358 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/gpio/gpio-sifive.txt
+ create mode 100644 drivers/gpio/gpio-sifive.c
+
+diff --git a/Documentation/devicetree/bindings/gpio/gpio-sifive.txt b/Documentation/devicetree/bindings/gpio/gpio-sifive.txt
+new file mode 100644
+index 00000000..bf41eed8
+--- /dev/null
++++ b/Documentation/devicetree/bindings/gpio/gpio-sifive.txt
+@@ -0,0 +1,28 @@
++SiFive GPIO controller bindings
++
++Required properties:
++- compatible:
++  - "sifive,gpio0"
++- reg: Physical base address and length of the controller's registers.
++- #gpio-cells : Should be 2
++  - The first cell is the gpio offset number.
++  - The second cell indicates the polarity of the GPIO
++- gpio-controller : Marks the device node as a GPIO controller.
++- interrupt-controller: Mark the device node as an interrupt controller
++- #interrupt-cells : Should be 2.
++  - The first cell is the GPIO offset number within the GPIO controller.
++  - The second cell is the edge/level to use for interrupt generation.
++- interrupts: Specify the interrupts, one per GPIO
++
++Example:
++
++gpio: gpio@10060000 {
++      compatible = "sifive,gpio0";
++      interrupt-parent = <&plic>;
++      interrupts = <7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22>;
++      reg = <0x0 0x10060000 0x0 0x1000>;
++      gpio-controller;
++      #gpio-cells = <2>;
++      interrupt-controller;
++      #interrupt-cells = <2>;
++};
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+index 4f52c3a8..7755f49e 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -439,6 +439,13 @@ config GPIO_REG
+         A 32-bit single register GPIO fixed in/out implementation.  This
+         can be used to represent any register as a set of GPIO signals.
++config GPIO_SIFIVE
++      bool "SiFive GPIO support"
++      depends on OF_GPIO
++      select GPIOLIB_IRQCHIP
++      help
++        Say yes here to support the GPIO device on SiFive SoCs.
++
+ config GPIO_SPEAR_SPICS
+       bool "ST SPEAr13xx SPI Chip Select as GPIO support"
+       depends on PLAT_SPEAR
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
+index c256aff6..244a3696 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -111,6 +111,7 @@ obj-$(CONFIG_GPIO_REG)             += gpio-reg.o
+ obj-$(CONFIG_ARCH_SA1100)     += gpio-sa1100.o
+ obj-$(CONFIG_GPIO_SCH)                += gpio-sch.o
+ obj-$(CONFIG_GPIO_SCH311X)    += gpio-sch311x.o
++obj-$(CONFIG_GPIO_SIFIVE)     += gpio-sifive.o
+ obj-$(CONFIG_GPIO_SODAVILLE)  += gpio-sodaville.o
+ obj-$(CONFIG_GPIO_SPEAR_SPICS)        += gpio-spear-spics.o
+ obj-$(CONFIG_GPIO_SPRD)               += gpio-sprd.o
+diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
+new file mode 100644
+index 00000000..6482ebbc
+--- /dev/null
++++ b/drivers/gpio/gpio-sifive.c
+@@ -0,0 +1,322 @@
++/*
++ * SiFive GPIO driver
++ *
++ * Copyright (C) 2018 SiFive, Inc.
++ *
++ * 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 <linux/bitops.h>
++#include <linux/device.h>
++#include <linux/errno.h>
++#include <linux/of_irq.h>
++#include <linux/gpio/driver.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/init.h>
++#include <linux/of.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++
++#define GPIO_INPUT_VAL        0x00
++#define GPIO_INPUT_EN 0x04
++#define GPIO_OUTPUT_EN        0x08
++#define GPIO_OUTPUT_VAL       0x0C
++#define GPIO_RISE_IE  0x18
++#define GPIO_RISE_IP  0x1C
++#define GPIO_FALL_IE  0x20
++#define GPIO_FALL_IP  0x24
++#define GPIO_HIGH_IE  0x28
++#define GPIO_HIGH_IP  0x2C
++#define GPIO_LOW_IE   0x30
++#define GPIO_LOW_IP   0x34
++#define GPIO_OUTPUT_XOR       0x40
++
++#define MAX_GPIO      32
++
++struct sifive_gpio {
++      raw_spinlock_t          lock;
++      void __iomem            *base;
++      struct gpio_chip        gc;
++      unsigned long           enabled;
++      unsigned                trigger[MAX_GPIO];
++      unsigned int            irq_parent[MAX_GPIO];
++      struct sifive_gpio      *self_ptr[MAX_GPIO];
++};
++
++static void sifive_assign_bit(void __iomem *ptr, int offset, int value)
++{
++      // It's frustrating that we are not allowed to use the device atomics
++      // which are GUARANTEED to be supported by this device on RISC-V
++      u32 bit = BIT(offset), old = ioread32(ptr);
++      if (value)
++              iowrite32(old | bit, ptr);
++      else
++              iowrite32(old & ~bit, ptr);
++}
++
++static int sifive_direction_input(struct gpio_chip *gc, unsigned offset)
++{
++      struct sifive_gpio *chip = gpiochip_get_data(gc);
++      unsigned long flags;
++
++      if (offset >= gc->ngpio)
++              return -EINVAL;
++
++      raw_spin_lock_irqsave(&chip->lock, flags);
++      sifive_assign_bit(chip->base + GPIO_OUTPUT_EN, offset, 0);
++      sifive_assign_bit(chip->base + GPIO_INPUT_EN,  offset, 1);
++      raw_spin_unlock_irqrestore(&chip->lock, flags);
++
++      return 0;
++}
++
++static int sifive_direction_output(struct gpio_chip *gc, unsigned offset, int value)
++{
++      struct sifive_gpio *chip = gpiochip_get_data(gc);
++      unsigned long flags;
++
++      if (offset >= gc->ngpio)
++              return -EINVAL;
++
++      raw_spin_lock_irqsave(&chip->lock, flags);
++      sifive_assign_bit(chip->base + GPIO_INPUT_EN,   offset, 0);
++      sifive_assign_bit(chip->base + GPIO_OUTPUT_VAL, offset, value);
++      sifive_assign_bit(chip->base + GPIO_OUTPUT_EN,  offset, 1);
++      raw_spin_unlock_irqrestore(&chip->lock, flags);
++
++      return 0;
++}
++
++static int sifive_get_direction(struct gpio_chip *gc, unsigned offset)
++{
++      struct sifive_gpio *chip = gpiochip_get_data(gc);
++
++      if (offset >= gc->ngpio)
++              return -EINVAL;
++
++      return !(ioread32(chip->base + GPIO_OUTPUT_EN) & BIT(offset));
++}
++
++static int sifive_get_value(struct gpio_chip *gc, unsigned offset)
++{
++      struct sifive_gpio *chip = gpiochip_get_data(gc);
++
++      if (offset >= gc->ngpio)
++              return -EINVAL;
++
++      return !!(ioread32(chip->base + GPIO_INPUT_VAL) & BIT(offset));
++}
++
++static void sifive_set_value(struct gpio_chip *gc, unsigned offset, int value)
++{
++      struct sifive_gpio *chip = gpiochip_get_data(gc);
++      unsigned long flags;
++
++      if (offset >= gc->ngpio)
++              return;
++
++      raw_spin_lock_irqsave(&chip->lock, flags);
++      sifive_assign_bit(chip->base + GPIO_OUTPUT_VAL, offset, value);
++      raw_spin_unlock_irqrestore(&chip->lock, flags);
++}
++
++static void sifive_set_ie(struct sifive_gpio *chip, int offset)
++{
++      unsigned long flags;
++      unsigned trigger;
++
++      raw_spin_lock_irqsave(&chip->lock, flags);
++      trigger = (chip->enabled & BIT(offset)) ? chip->trigger[offset] : 0;
++      sifive_assign_bit(chip->base + GPIO_RISE_IE, offset, trigger & IRQ_TYPE_EDGE_RISING);
++      sifive_assign_bit(chip->base + GPIO_FALL_IE, offset, trigger & IRQ_TYPE_EDGE_FALLING);
++      sifive_assign_bit(chip->base + GPIO_HIGH_IE, offset, trigger & IRQ_TYPE_LEVEL_HIGH);
++      sifive_assign_bit(chip->base + GPIO_LOW_IE,  offset, trigger & IRQ_TYPE_LEVEL_LOW);
++      raw_spin_unlock_irqrestore(&chip->lock, flags);
++}
++
++static int sifive_irq_set_type(struct irq_data *d, unsigned trigger)
++{
++      struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
++      struct sifive_gpio *chip = gpiochip_get_data(gc);
++      int offset = irqd_to_hwirq(d);
++
++      if (offset < 0 || offset >= gc->ngpio)
++              return -EINVAL;
++
++      chip->trigger[offset] = trigger;
++      sifive_set_ie(chip, offset);
++      return 0;
++}
++
++/* chained_irq_{enter,exit} already mask the parent */
++static void sifive_irq_mask(struct irq_data *d) { }
++static void sifive_irq_unmask(struct irq_data *d) { }
++
++static void sifive_irq_enable(struct irq_data *d)
++{
++      struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
++      struct sifive_gpio *chip = gpiochip_get_data(gc);
++      int offset = irqd_to_hwirq(d) % MAX_GPIO; // must not fail
++      u32 bit = BIT(offset);
++
++      /* Switch to input */
++      sifive_direction_input(gc, offset);
++
++      /* Clear any sticky pending interrupts */
++      iowrite32(bit, chip->base + GPIO_RISE_IP);
++      iowrite32(bit, chip->base + GPIO_FALL_IP);
++      iowrite32(bit, chip->base + GPIO_HIGH_IP);
++      iowrite32(bit, chip->base + GPIO_LOW_IP);
++
++      /* Enable interrupts */
++      assign_bit(offset, &chip->enabled, 1);
++      sifive_set_ie(chip, offset);
++}
++
++static void sifive_irq_disable(struct irq_data *d)
++{
++      struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
++      struct sifive_gpio *chip = gpiochip_get_data(gc);
++      int offset = irqd_to_hwirq(d) % MAX_GPIO; // must not fail
++
++      assign_bit(offset, &chip->enabled, 0);
++      sifive_set_ie(chip, offset);
++}
++
++static struct irq_chip sifive_irqchip = {
++      .name           = "sifive-gpio",
++      .irq_set_type   = sifive_irq_set_type,
++      .irq_mask       = sifive_irq_mask,
++      .irq_unmask     = sifive_irq_unmask,
++      .irq_enable     = sifive_irq_enable,
++      .irq_disable    = sifive_irq_disable,
++};
++
++static void sifive_irq_handler(struct irq_desc *desc)
++{
++      struct irq_chip *irqchip = irq_desc_get_chip(desc);
++      struct sifive_gpio **self_ptr = irq_desc_get_handler_data(desc);
++      struct sifive_gpio *chip = *self_ptr;
++      int offset = self_ptr - &chip->self_ptr[0];
++      u32 bit = BIT(offset);
++
++      chained_irq_enter(irqchip, desc);
++
++      /* Re-arm the edge triggers so don't miss the next one */
++      iowrite32(bit, chip->base + GPIO_RISE_IP);
++      iowrite32(bit, chip->base + GPIO_FALL_IP);
++
++      generic_handle_irq(irq_find_mapping(chip->gc.irq.domain, offset));
++
++      /* Re-arm the level triggers after handling to prevent spurious refire */
++      iowrite32(bit, chip->base + GPIO_HIGH_IP);
++      iowrite32(bit, chip->base + GPIO_LOW_IP);
++
++      chained_irq_exit(irqchip, desc);
++}
++
++static int sifive_gpio_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct device_node *node = pdev->dev.of_node;
++      struct sifive_gpio *chip;
++      struct resource *res;
++      int gpio, irq, ret, ngpio;
++
++      chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
++      if (!chip) {
++              dev_err(dev, "out of memory\n");
++              return -ENOMEM;
++      }
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      chip->base = devm_ioremap_resource(dev, res);
++      if (IS_ERR(chip->base)) {
++              dev_err(dev, "failed to allocate device memory\n");
++              return PTR_ERR(chip->base);
++      }
++
++      ngpio = of_irq_count(node);
++      if (ngpio >= MAX_GPIO) {
++              dev_err(dev, "too many interrupts\n");
++              return -EINVAL;
++      }
++
++      raw_spin_lock_init(&chip->lock);
++      chip->gc.direction_input = sifive_direction_input;
++      chip->gc.direction_output = sifive_direction_output;
++      chip->gc.get_direction = sifive_get_direction;
++      chip->gc.get = sifive_get_value;
++      chip->gc.set = sifive_set_value;
++      chip->gc.base = -1;
++      chip->gc.ngpio = ngpio;
++      chip->gc.label = dev_name(dev);
++      chip->gc.parent = dev;
++      chip->gc.owner = THIS_MODULE;
++
++      ret = gpiochip_add_data(&chip->gc, chip);
++      if (ret)
++              return ret;
++
++      /* Disable all GPIO interrupts before enabling parent interrupts */
++      iowrite32(0, chip->base + GPIO_RISE_IE);
++      iowrite32(0, chip->base + GPIO_FALL_IE);
++      iowrite32(0, chip->base + GPIO_HIGH_IE);
++      iowrite32(0, chip->base + GPIO_LOW_IE);
++      chip->enabled = 0;
++
++      ret = gpiochip_irqchip_add(&chip->gc, &sifive_irqchip, 0, handle_simple_irq, IRQ_TYPE_NONE);
++      if (ret) {
++              dev_err(dev, "could not add irqchip\n");
++              gpiochip_remove(&chip->gc);
++              return ret;
++      }
++
++      chip->gc.irq.num_parents = ngpio;
++      chip->gc.irq.parents = &chip->irq_parent[0];
++      chip->gc.irq.map = &chip->irq_parent[0];
++
++      for (gpio = 0; gpio < ngpio; ++gpio) {
++              irq = platform_get_irq(pdev, gpio);
++              if (irq < 0) {
++                      dev_err(dev, "invalid IRQ\n");
++                      gpiochip_remove(&chip->gc);
++                      return -ENODEV;
++              }
++
++              chip->irq_parent[gpio] = irq;
++              chip->self_ptr[gpio] = chip;
++              chip->trigger[gpio] = IRQ_TYPE_LEVEL_HIGH;
++      }
++
++      for (gpio = 0; gpio < ngpio; ++gpio) {
++              irq = chip->irq_parent[gpio];
++              irq_set_chained_handler_and_data(irq, sifive_irq_handler, &chip->self_ptr[gpio]);
++              irq_set_parent(irq_find_mapping(chip->gc.irq.domain, gpio), irq);
++      }
++
++      platform_set_drvdata(pdev, chip);
++      dev_info(dev, "SiFive GPIO chip registered %d GPIOs\n", ngpio);
++
++      return 0;
++}
++
++static const struct of_device_id sifive_gpio_match[] = {
++      {
++              .compatible = "sifive,gpio0",
++      },
++      { },
++};
++
++static struct platform_driver sifive_gpio_driver = {
++      .probe          = sifive_gpio_probe,
++      .driver = {
++              .name   = "sifive_gpio",
++              .of_match_table = of_match_ptr(sifive_gpio_match),
++      },
++};
++builtin_platform_driver(sifive_gpio_driver)
+-- 
+2.7.4
+
diff --git a/target/linux/riscv64/patches-4.19/0101-008-RISC-V-Networking-fix-Hack.patch b/target/linux/riscv64/patches-4.19/0101-008-RISC-V-Networking-fix-Hack.patch
new file mode 100644 (file)
index 0000000..35009a2
--- /dev/null
@@ -0,0 +1,39 @@
+From aa230e7dc2ab01db5b630f427e57297ffc25c884 Mon Sep 17 00:00:00 2001
+From: Atish Patra <[email protected]>
+Date: Fri, 7 Sep 2018 10:22:27 -0700
+Subject: [PATCH 09/11] RISC-V: Networking fix Hack
+
+It looks like that kernel driver now supports reseting the
+signal one additional time. As it had been  already reset
+twice in FSBL, PHY gets into incorrect state causing below error.
+
+----------------------------------------------------------------------
+macb 10090000.ethernet (unnamed net_device) (uninitialized): Could not attach to PHY
+macb: probe of 10090000.ethernet failed with error -110
+----------------------------------------------------------------------
+
+This patch is just a temporary fix until we have a fix a FSBL.
+It is just a **HACK** and **NOT TO BE MERGED** into mainline.
+
+Signed-off-by: Atish Patra <[email protected]>
+---
+ drivers/net/phy/mdio_bus.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
+index 98f4b1f7..02c31f83 100644
+--- a/drivers/net/phy/mdio_bus.c
++++ b/drivers/net/phy/mdio_bus.c
+@@ -64,9 +64,6 @@ static int mdiobus_register_gpiod(struct mdio_device *mdiodev)
+       mdiodev->reset = gpiod;
+-      /* Assert the reset signal again */
+-      mdio_device_reset(mdiodev, 1);
+-
+       return 0;
+ }
+-- 
+2.7.4
+
diff --git a/target/linux/riscv64/patches-4.19/0101-010-pwm-sifive-add-a-driver-for-SiFive-SoC-PWM.patch b/target/linux/riscv64/patches-4.19/0101-010-pwm-sifive-add-a-driver-for-SiFive-SoC-PWM.patch
new file mode 100644 (file)
index 0000000..fd4210b
--- /dev/null
@@ -0,0 +1,339 @@
+From d6e84658098c6f37875a9e4b151706c6b541dc0b Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <[email protected]>
+Date: Fri, 2 Mar 2018 14:53:51 -0800
+Subject: [PATCH] pwm-sifive: add a driver for SiFive SoC PWM
+
+---
+ .../devicetree/bindings/pwm/pwm-sifive.txt    |  28 ++
+ drivers/pwm/Kconfig                           |  10 +
+ drivers/pwm/Makefile                          |   1 +
+ drivers/pwm/pwm-sifive.c                      | 252 ++++++++++++++++++
+ 4 files changed, 291 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pwm/pwm-sifive.txt
+ create mode 100644 drivers/pwm/pwm-sifive.c
+
+diff --git a/Documentation/devicetree/bindings/pwm/pwm-sifive.txt b/Documentation/devicetree/bindings/pwm/pwm-sifive.txt
+new file mode 100644
+index 0000000000000..7cea20db58e55
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pwm/pwm-sifive.txt
+@@ -0,0 +1,28 @@
++SiFive PWM controller
++
++Unlike most other PWM controllers, the SiFive PWM controller currently only
++supports one period for all channels in the PWM. This is set globally in DTS.
++The period also has significant restrictions on the values it can achieve,
++which the driver rounds to the nearest achievable frequency.
++
++Required properties:
++- compatible: should be "sifive,pwm0"
++- reg: physical base address and length of the controller's registers
++- clocks: The frequency the controller runs at
++- #pwm-cells: Should be 2.
++  The first cell is the PWM channel number
++  The second cell is the PWM polarity
++- sifive,approx-period: the driver will get as close to this period as it can
++- interrupts: one interrupt per PWM channel (currently unused in the driver)
++
++Examples:
++
++pwm:  pwm@10020000 {
++      compatible = "sifive,pwm0";
++      reg = <0x0 0x10020000 0x0 0x1000>;
++      clocks = <&tlclk>;
++      interrupt-parent = <&plic>;
++      interrupts = <42 43 44 45>;
++      #pwm-cells = <2>;
++      sifive,approx-period = <1000000>;
++};
+diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
+index 763ee50ea57d5..c2037d28446fa 100644
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -387,6 +387,16 @@ config PWM_SAMSUNG
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-samsung.
++config PWM_SIFIVE
++      tristate "SiFive PWM support"
++      depends on OF
++      depends on COMMON_CLK
++      help
++        Generic PWM framework driver for SiFive SoCs.
++
++        To compile this driver as a module, choose M here: the module
++        will be called pwm-sifive.
++
+ config PWM_SPEAR
+       tristate "STMicroelectronics SPEAr PWM support"
+       depends on PLAT_SPEAR
+diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
+index 0258a745f30c9..17c5eb90cb058 100644
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -38,6 +38,7 @@ obj-$(CONFIG_PWM_RCAR)               += pwm-rcar.o
+ obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
+ obj-$(CONFIG_PWM_ROCKCHIP)    += pwm-rockchip.o
+ obj-$(CONFIG_PWM_SAMSUNG)     += pwm-samsung.o
++obj-$(CONFIG_PWM_SIFIVE)      += pwm-sifive.o
+ obj-$(CONFIG_PWM_SPEAR)               += pwm-spear.o
+ obj-$(CONFIG_PWM_STI)         += pwm-sti.o
+ obj-$(CONFIG_PWM_STM32)               += pwm-stm32.o
+diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
+new file mode 100644
+index 0000000000000..93bc0844d23a9
+--- /dev/null
++++ b/drivers/pwm/pwm-sifive.c
+@@ -0,0 +1,252 @@
++/*
++ * Copyright (C) 2018 SiFive, Inc
++ *
++ * 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 <dt-bindings/pwm/pwm.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++#include <linux/slab.h>
++#include <linux/clk.h>
++#include <linux/io.h>
++
++#define MAX_PWM                       4
++
++/* Register offsets */
++#define REG_PWMCFG            0x0
++#define REG_PWMCOUNT          0x8
++#define REG_PWMS              0x10
++#define       REG_PWMCMP0             0x20
++
++/* PWMCFG fields */
++#define BIT_PWM_SCALE         0
++#define BIT_PWM_STICKY                8
++#define BIT_PWM_ZERO_ZMP      9
++#define BIT_PWM_DEGLITCH      10
++#define BIT_PWM_EN_ALWAYS     12
++#define BIT_PWM_EN_ONCE               13
++#define BIT_PWM0_CENTER               16
++#define BIT_PWM0_GANG         24
++#define BIT_PWM0_IP           28
++
++#define SIZE_PWMCMP           4
++#define MASK_PWM_SCALE                0xf
++
++struct sifive_pwm_device {
++      struct pwm_chip         chip;
++      struct notifier_block   notifier;
++      struct clk              *clk;
++      void __iomem            *regs;
++      int                     irq;
++      unsigned int            approx_period;
++      unsigned int            real_period;
++};
++
++static inline struct sifive_pwm_device *chip_to_sifive(struct pwm_chip *c)
++{
++      return container_of(c, struct sifive_pwm_device, chip);
++}
++
++static inline struct sifive_pwm_device *notifier_to_sifive(struct notifier_block *nb)
++{
++      return container_of(nb, struct sifive_pwm_device, notifier);
++}
++
++static int sifive_pwm_apply(struct pwm_chip *chip, struct pwm_device *dev, struct pwm_state *state)
++{
++      struct sifive_pwm_device *pwm = chip_to_sifive(chip);
++      unsigned int duty_cycle;
++      u32 frac;
++
++      duty_cycle = state->duty_cycle;
++      if (!state->enabled) duty_cycle = 0;
++      if (state->polarity == PWM_POLARITY_NORMAL) duty_cycle = state->period - duty_cycle;
++
++      frac = ((u64)duty_cycle << 16) / state->period;
++      frac = min(frac, 0xFFFFU);
++
++      iowrite32(frac, pwm->regs + REG_PWMCMP0 + (dev->hwpwm * SIZE_PWMCMP));
++
++      if (state->enabled) {
++              state->period = pwm->real_period;
++              state->duty_cycle = ((u64)frac * pwm->real_period) >> 16;
++              if (state->polarity == PWM_POLARITY_NORMAL)
++                      state->duty_cycle = state->period - state->duty_cycle;
++      }
++
++      return 0;
++}
++
++static void sifive_pwm_get_state(struct pwm_chip *chip, struct pwm_device *dev, struct pwm_state *state)
++{
++      struct sifive_pwm_device *pwm = chip_to_sifive(chip);
++      unsigned long duty;
++
++      duty = ioread32(pwm->regs + REG_PWMCMP0 + (dev->hwpwm * SIZE_PWMCMP));
++
++      state->period     = pwm->real_period;
++      state->duty_cycle = ((u64)duty * pwm->real_period) >> 16;
++      state->polarity   = PWM_POLARITY_INVERSED;
++      state->enabled    = duty > 0;
++}
++
++static const struct pwm_ops sifive_pwm_ops = {
++      .get_state      = sifive_pwm_get_state,
++      .apply          = sifive_pwm_apply,
++      .owner          = THIS_MODULE,
++};
++
++static struct pwm_device *sifive_pwm_xlate(struct pwm_chip *chip, const struct of_phandle_args *args)
++{
++      struct sifive_pwm_device *pwm = chip_to_sifive(chip);
++      struct pwm_device *dev;
++
++      if (args->args[0] >= chip->npwm)
++              return ERR_PTR(-EINVAL);
++
++      dev = pwm_request_from_chip(chip, args->args[0], NULL);
++      if (IS_ERR(dev))
++              return dev;
++
++      /* The period cannot be changed on a per-PWM basis */
++      dev->args.period   = pwm->real_period;
++      dev->args.polarity = PWM_POLARITY_NORMAL;
++      if (args->args[1] & PWM_POLARITY_INVERTED)
++              dev->args.polarity = PWM_POLARITY_INVERSED;
++
++      return dev;
++}
++
++static void sifive_pwm_update_clock(struct sifive_pwm_device *pwm, unsigned long rate)
++{
++      /* (1 << (16+scale)) * 10^9/rate = real_period */
++      unsigned long scalePow = (pwm->approx_period * (u64)rate) / 1000000000;
++      int scale = ilog2(scalePow) - 16;
++
++      scale = clamp(scale, 0, 0xf);
++      iowrite32((1 << BIT_PWM_EN_ALWAYS) | (scale << BIT_PWM_SCALE), pwm->regs + REG_PWMCFG);
++
++      pwm->real_period = (1000000000ULL << (16 + scale)) / rate;
++}
++
++static int sifive_pwm_clock_notifier(struct notifier_block *nb, unsigned long event, void *data)
++{
++      struct clk_notifier_data *ndata = data;
++      struct sifive_pwm_device *pwm = notifier_to_sifive(nb);
++
++      if (event == POST_RATE_CHANGE)
++              sifive_pwm_update_clock(pwm, ndata->new_rate);
++
++      return NOTIFY_OK;
++}
++
++static int sifive_pwm_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct device_node *node = pdev->dev.of_node;
++      struct sifive_pwm_device *pwm;
++      struct pwm_chip *chip;
++      struct resource *res;
++      int ret;
++
++      pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
++      if (!pwm) {
++              dev_err(dev, "Out of memory\n");
++              return -ENOMEM;
++      }
++
++      chip = &pwm->chip;
++      chip->dev = dev;
++      chip->ops = &sifive_pwm_ops;
++      chip->of_xlate = sifive_pwm_xlate;
++      chip->of_pwm_n_cells = 2;
++      chip->base = -1;
++
++      ret = of_property_read_u32(node, "sifive,npwm", &chip->npwm);
++      if (ret < 0 || chip->npwm > MAX_PWM) chip->npwm = MAX_PWM;
++
++      ret = of_property_read_u32(node, "sifive,approx-period", &pwm->approx_period);
++      if (ret < 0) {
++              dev_err(dev, "Unable to read sifive,approx-period from DTS\n");
++              return -ENOENT;
++      }
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      pwm->regs = devm_ioremap_resource(dev, res);
++      if (IS_ERR(pwm->regs)) {
++              dev_err(dev, "Unable to map IO resources\n");
++              return PTR_ERR(pwm->regs);
++      }
++
++      pwm->clk = devm_clk_get(dev, NULL);
++      if (IS_ERR(pwm->clk)) {
++              dev_err(dev, "Unable to find controller clock\n");
++              return PTR_ERR(pwm->clk);
++      }
++
++      pwm->irq = platform_get_irq(pdev, 0);
++      if (pwm->irq < 0) {
++              dev_err(dev, "Unable to find interrupt\n");
++              return pwm->irq;
++      }
++
++      /* Watch for changes to underlying clock frequency */
++      pwm->notifier.notifier_call = sifive_pwm_clock_notifier;
++      clk_notifier_register(pwm->clk, &pwm->notifier);
++
++      /* Initialize PWM config */
++      sifive_pwm_update_clock(pwm, clk_get_rate(pwm->clk));
++
++      /* No interrupt handler needed yet */
++/*
++      ret = devm_request_irq(dev, pwm->irq, sifive_pwm_irq, 0, dev_name(dev), pwm);
++      if (ret) {
++              dev_err(dev, "Unable to bind interrupt\n");
++              return ret;
++      }
++*/
++
++      ret = pwmchip_add(chip);
++      if (ret < 0) {
++              dev_err(dev, "cannot register PWM: %d\n", ret);
++              clk_notifier_unregister(pwm->clk, &pwm->notifier);
++              return ret;
++      }
++
++      platform_set_drvdata(pdev, pwm);
++      dev_info(dev, "SiFive PWM chip registered %d PWMs\n", chip->npwm);
++
++      return 0;
++}
++
++static int sifive_pwm_remove(struct platform_device *dev)
++{
++      struct sifive_pwm_device *pwm = platform_get_drvdata(dev);
++      struct pwm_chip *chip = &pwm->chip;
++
++      clk_notifier_unregister(pwm->clk, &pwm->notifier);
++      return pwmchip_remove(chip);
++}
++
++static const struct of_device_id sifive_pwm_of_match[] = {
++      { .compatible = "sifive,pwm0" },
++      {},
++};
++MODULE_DEVICE_TABLE(of, sifive_pwm_of_match);
++
++static struct platform_driver sifive_pwm_driver = {
++      .probe = sifive_pwm_probe,
++      .remove = sifive_pwm_remove,
++      .driver = {
++              .name = "pwm-sifivem",
++              .of_match_table = of_match_ptr(sifive_pwm_of_match),
++      },
++};
++module_platform_driver(sifive_pwm_driver);
++
++MODULE_DESCRIPTION("SiFive PWM driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/riscv64/patches-4.19/0101-011-sifive-u500-otp-driver-for-SiFive-U500-OTP-contr.patch b/target/linux/riscv64/patches-4.19/0101-011-sifive-u500-otp-driver-for-SiFive-U500-OTP-contr.patch
new file mode 100644 (file)
index 0000000..14fb51e
--- /dev/null
@@ -0,0 +1,409 @@
+From 33cd56c2ab57ff984a4d7fbe378b27ef13b87458 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <[email protected]>
+Date: Mon, 26 Feb 2018 16:49:49 -0800
+Subject: [PATCH] sifive-u500-otp: driver for SiFive U500 OTP controller
+
+---
+ .../bindings/nvmem/sifive-u500-otp.txt        |  19 +
+ drivers/nvmem/Kconfig                         |  10 +
+ drivers/nvmem/Makefile                        |   2 +
+ drivers/nvmem/sifive-u500-otp.c               | 330 ++++++++++++++++++
+ 4 files changed, 361 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/nvmem/sifive-u500-otp.txt
+ create mode 100644 drivers/nvmem/sifive-u500-otp.c
+
+diff --git a/Documentation/devicetree/bindings/nvmem/sifive-u500-otp.txt b/Documentation/devicetree/bindings/nvmem/sifive-u500-otp.txt
+new file mode 100644
+index 0000000000000..fc59123719660
+--- /dev/null
++++ b/Documentation/devicetree/bindings/nvmem/sifive-u500-otp.txt
+@@ -0,0 +1,19 @@
++= SiFive U500 OTP controller device tree bindings =
++
++This binding represents the OTP controller found in U500 SoCs.
++
++!!! EXERCISE EXTREME CAUTION USING THIS DEVICE  !!!
++
++OTP bits can only be programmed once from 1 to 0. They are contained in the
++chip itself. Incorrectly modifying OTP contents may require a new chip.
++
++Required properties:
++- compatible: should be "sifive,ememoryotp0"
++- reg: Should contain registers location and length
++
++For example:
++
++      otp: sifive,ememoryotp0 {
++              compatible = "sifive,ememoryotp0";
++              reg = <0x0 0x10070000 0x0 0x1000>;
++      };
+diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig
+index ff505af064ba6..e0ea3eca483d6 100644
+--- a/drivers/nvmem/Kconfig
++++ b/drivers/nvmem/Kconfig
+@@ -113,6 +113,16 @@ config NVMEM_BCM_OCOTP
+         This driver can also be built as a module. If so, the module
+         will be called nvmem-bcm-ocotp.
++config NVMEM_SIFIVE_U500_OTP
++      tristate "SiFive U500 OTP Controller support"
++      depends on HAS_IOMEM
++      help
++        Say y here to enable read/write access to the SiFive U500
++        OTP controller.
++
++        This driver can also be built as a module. If so, the module
++        will be called nvmem-sifive-u500-otp.
++
+ config NVMEM_SUNXI_SID
+       tristate "Allwinner SoCs SID support"
+       depends on ARCH_SUNXI
+diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile
+index e54dcfa6565a9..d4c64fbbec7f2 100644
+--- a/drivers/nvmem/Makefile
++++ b/drivers/nvmem/Makefile
+@@ -9,6 +9,8 @@ nvmem_core-y                   := core.o
+ # Devices
+ obj-$(CONFIG_NVMEM_BCM_OCOTP) += nvmem-bcm-ocotp.o
+ nvmem-bcm-ocotp-y             := bcm-ocotp.o
++obj-$(CONFIG_NVMEM_SIFIVE_U500_OTP)   += nvmem-sifive-u500-otp.o
++nvmem-sifive-u500-otp-y               := sifive-u500-otp.o
+ obj-$(CONFIG_NVMEM_IMX_IIM)   += nvmem-imx-iim.o
+ nvmem-imx-iim-y                       := imx-iim.o
+ obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
+diff --git a/drivers/nvmem/sifive-u500-otp.c b/drivers/nvmem/sifive-u500-otp.c
+new file mode 100644
+index 0000000000000..8c0d0ccea29e0
+--- /dev/null
++++ b/drivers/nvmem/sifive-u500-otp.c
+@@ -0,0 +1,330 @@
++/*
++ * Copyright (C) 2018 SiFive, Inc.
++ *
++ * 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 version 2.
++ *
++ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
++ * kind, whether express or implied; without even the implied warranty
++ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/nvmem-provider.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++
++/* Register offsets */
++#define EMEMORYOTP_REG_GET(reg)               ioread32(otp->base + reg)
++#define EMEMORYOTP_REG_SET(reg, val)  iowrite32(val, otp->base + reg)
++
++#define EMEMORYOTP_PA                 0x00
++#define EMEMORYOTP_PAIO               0x04
++#define EMEMORYOTP_PAS                0x08
++#define EMEMORYOTP_PCE                0x0C
++#define EMEMORYOTP_PCLK               0x10
++#define EMEMORYOTP_PDIN               0x14
++#define EMEMORYOTP_PDOUT              0x18
++#define EMEMORYOTP_PDSTB              0x1C
++#define EMEMORYOTP_PPROG              0x20
++#define EMEMORYOTP_PTC                0x24
++#define EMEMORYOTP_PTM                0x28
++#define EMEMORYOTP_PTM_REP            0x2C
++#define EMEMORYOTP_PTR                0x30
++#define EMEMORYOTP_PTRIM              0x34
++#define EMEMORYOTP_PWE                0x38
++
++/* Timing delays (in us)
++   MIN indicates that there is no maximum.
++   TYP indicates that there is a maximum
++   that should not be exceeded.
++   When the minimums are < 1us, I just put 1us.
++*/
++
++#define EMEMORYOTP_MIN_TVDS      1
++#define EMEMORYOTP_MIN_TSAS      2
++#define EMEMORYOTP_MIN_TTAS      50
++#define EMEMORYOTP_MIN_TTAH      1
++#define EMEMORYOTP_MIN_TASH      1
++#define EMEMORYOTP_MIN_TMS       1
++#define EMEMORYOTP_MIN_TCS       10
++#define EMEMORYOTP_MIN_TMH       1
++#define EMEMORYOTP_MIN_TAS       50
++
++#define EMEMORYOTP_MAX_TCD       1
++#define EMEMORYOTP_MIN_TKH       1
++
++// Note: This has an upper limit of 100.
++#define EMEMORYOTP_MIN_TCSP      10
++#define EMEMORYOTP_TYP_TCSP      11
++
++// This has an upper limit of 20.
++#define EMEMORYOTP_MIN_TPPS      5
++#define EMEMORYOTP_TYP_TPPS      6
++
++// This has an upper limit of 20.
++#define EMEMORYOTP_MIN_TPPH      1
++#define EMEMORYOTP_TYP_TPPH      2
++
++// This has upper limit of 100.
++#define EMEMORYOTP_MIN_TPPR      5
++#define EMEMORYOTP_TYP_TPPR      6
++
++// This has upper limit of 20
++#define EMEMORYOTP_MIN_TPW       10
++#define EMEMORYOTP_TYP_TPW       11
++
++#define EMEMORYOTP_MIN_TASP      1
++#define EMEMORYOTP_MIN_TDSP      1
++
++#define EMEMORYOTP_MIN_TAHP      1
++#define EMEMORYOTP_MIN_TDHP      1
++// This has a max of 5!
++#define EMEMORYOTP_MIN_TPWI      1
++#define EMEMORYOTP_TYP_TPWI      2
++
++struct sifive_u500_otp {
++      void __iomem            *base;
++      raw_spinlock_t          lock;
++      struct nvmem_config     config;
++};
++
++static void sifive_u500_otp_power_up_sequence(struct sifive_u500_otp *otp)
++{
++      // Probably don't need to do this, since
++      // all the other stuff has been happening.
++      // But it is on the wave form.
++      udelay(EMEMORYOTP_MIN_TVDS);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PDSTB, 1);
++      udelay(EMEMORYOTP_MIN_TSAS);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PTRIM, 1);
++      udelay(EMEMORYOTP_MIN_TTAS);
++}
++
++static void sifive_u500_otp_power_down_sequence(struct sifive_u500_otp *otp)
++{
++      udelay(EMEMORYOTP_MIN_TTAH);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PTRIM, 0);
++      udelay(EMEMORYOTP_MIN_TASH);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PDSTB, 0);
++      // No delay indicated after this
++}
++
++static void sifive_u500_otp_begin_read(struct sifive_u500_otp *otp)
++{
++      // Initialize
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PCLK, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PA, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PDIN, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PWE, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PTM, 0);
++      udelay(EMEMORYOTP_MIN_TMS);
++
++      // Enable chip select
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PCE, 1);
++      udelay(EMEMORYOTP_MIN_TCS);
++}
++
++static void sifive_u500_otp_exit_read(struct sifive_u500_otp *otp)
++{
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PCLK, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PA, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PDIN, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PWE, 0);
++      // Disable chip select
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PCE, 0);
++      // Wait before changing PTM
++      udelay(EMEMORYOTP_MIN_TMH);
++}
++
++static unsigned int sifive_u500_otp_read(struct sifive_u500_otp *otp, int address)
++{
++      unsigned int read_value;
++      int delay;
++
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PA, address);
++      // Toggle clock
++      udelay(EMEMORYOTP_MIN_TAS);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PCLK, 1);
++
++      // Insert delay until data is ready.
++      // There are lots of delays
++      // on the chart, but I think this is the most relevant.
++      delay = max(EMEMORYOTP_MAX_TCD, EMEMORYOTP_MIN_TKH);
++      udelay(delay);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PCLK, 0);
++      read_value = EMEMORYOTP_REG_GET(EMEMORYOTP_PDOUT);
++
++      // Could check here for things like TCYC < TAH + TCD
++      return read_value;
++}
++
++static void sifive_u500_otp_pgm_entry(struct sifive_u500_otp *otp)
++{
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PCLK, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PA, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PAS, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PAIO, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PDIN, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PWE, 0);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PTM, 2);
++      udelay(EMEMORYOTP_MIN_TMS);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PCE, 1);
++      udelay(EMEMORYOTP_TYP_TCSP);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PPROG, 1);
++      udelay(EMEMORYOTP_TYP_TPPS);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PTRIM, 1);
++}
++
++static void sifive_u500_otp_pgm_exit(struct sifive_u500_otp *otp)
++{
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PWE, 0);
++      udelay(EMEMORYOTP_TYP_TPPH);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PPROG, 0);
++      udelay(EMEMORYOTP_TYP_TPPR);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PCE, 0);
++      udelay(EMEMORYOTP_MIN_TMH);
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PTM, 0);
++}
++
++static void sifive_u500_otp_pgm_access(struct sifive_u500_otp *otp, int address, unsigned int write_data)
++{
++      int i, pas, delay;
++
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PA, address);
++      for (pas = 0; pas < 2; pas ++) {
++              EMEMORYOTP_REG_SET(EMEMORYOTP_PAS, pas);
++              for (i = 0; i < 32; i++) {
++                      EMEMORYOTP_REG_SET(EMEMORYOTP_PAIO, i);
++                      EMEMORYOTP_REG_SET(EMEMORYOTP_PDIN, (write_data >> i) & 1);
++                      delay = max(EMEMORYOTP_MIN_TASP, EMEMORYOTP_MIN_TDSP);
++                      udelay(delay);
++                      EMEMORYOTP_REG_SET(EMEMORYOTP_PWE, 1);
++                      udelay(EMEMORYOTP_TYP_TPW);
++                      EMEMORYOTP_REG_SET(EMEMORYOTP_PWE, 0);
++                      delay = max(EMEMORYOTP_MIN_TAHP, EMEMORYOTP_MIN_TDHP);
++                      delay = max(delay, EMEMORYOTP_TYP_TPWI);
++                      udelay(delay);
++              }
++      }
++      EMEMORYOTP_REG_SET(EMEMORYOTP_PAS, 0);
++}
++
++static int sifive_u500_otp_reg_read(void *context, unsigned int offset, void *val, size_t bytes)
++{
++      struct sifive_u500_otp *otp = context;
++      unsigned long flags;
++      u32 *buf = val;
++      int i;
++
++      raw_spin_lock_irqsave(&otp->lock, flags);
++      sifive_u500_otp_power_up_sequence(otp);
++      sifive_u500_otp_begin_read(otp);
++
++      for (i = 0; i < bytes/4; ++i)
++              buf[i] = sifive_u500_otp_read(otp, i+offset/4);
++
++      sifive_u500_otp_exit_read(otp);
++      sifive_u500_otp_power_down_sequence(otp);
++      raw_spin_unlock_irqrestore(&otp->lock, flags);
++
++      return 0;
++}
++
++static int sifive_u500_otp_reg_write(void *context, unsigned int offset, void *val, size_t bytes)
++{
++      struct sifive_u500_otp *otp = context;
++      unsigned long flags;
++      u32 *buf = val;
++      int i;
++
++      if (offset % 4 || bytes % 4)
++              return -EINVAL;
++
++      raw_spin_lock_irqsave(&otp->lock, flags);
++      sifive_u500_otp_power_up_sequence(otp);
++      sifive_u500_otp_pgm_entry(otp);
++
++      for (i = 0; i < bytes/4; ++i)
++              sifive_u500_otp_pgm_access(otp, i+offset/4, buf[i]);
++
++      sifive_u500_otp_pgm_exit(otp);
++      sifive_u500_otp_power_down_sequence(otp);
++      raw_spin_unlock_irqrestore(&otp->lock, flags);
++
++      return 0;
++}
++
++static int sifive_u500_otp_probe(struct platform_device *pdev)
++{
++      struct device *dev = &pdev->dev;
++      struct resource *res;
++      struct sifive_u500_otp *otp;
++      struct nvmem_device *nvmem;
++
++      otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL);
++      if (!otp) {
++              dev_err(dev, "out of memory\n");
++              return -ENOMEM;
++      }
++
++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++      otp->base = devm_ioremap_resource(dev, res);
++      if (IS_ERR(otp->base)) {
++              dev_err(dev, "unable to map device memory\n");
++              return PTR_ERR(otp->base);
++      }
++
++      otp->config.dev       = dev;
++      otp->config.name      = "sifive-u500-otp";
++      otp->config.read_only = false;
++      otp->config.root_only = false; // true; /* writes permanently modify the SoC! */
++      otp->config.reg_read  = sifive_u500_otp_reg_read;
++      otp->config.reg_write = sifive_u500_otp_reg_write;
++      otp->config.size      = 16384; /* in bytes */
++      otp->config.word_size = 4;
++      otp->config.stride    = 4;
++      otp->config.priv      = otp;
++
++      raw_spin_lock_init(&otp->lock);
++      nvmem = nvmem_register(&otp->config);
++      if (IS_ERR(nvmem)) {
++              dev_err(dev, "error registering nvmem config\n");
++              return PTR_ERR(nvmem);
++      }
++
++      platform_set_drvdata(pdev, nvmem);
++      return 0;
++}
++
++static int sifive_u500_otp_remove(struct platform_device *pdev)
++{
++      struct nvmem_device *nvmem = platform_get_drvdata(pdev);
++
++      return nvmem_unregister(nvmem);
++}
++
++static const struct of_device_id sifive_u500_otp_dt_ids[] = {
++      { .compatible = "sifive,ememoryotp0" },
++      { },
++};
++MODULE_DEVICE_TABLE(of, sifive_u500_otp_dt_ids);
++
++static struct platform_driver sifive_u500_otp_driver = {
++      .probe  = sifive_u500_otp_probe,
++      .remove = sifive_u500_otp_remove,
++      .driver = {
++              .name   = "sifive-u500-otp",
++              .of_match_table = sifive_u500_otp_dt_ids,
++      },
++};
++module_platform_driver(sifive_u500_otp_driver);
++
++MODULE_DESCRIPTION("SiFive U500 OTP driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/riscv64/patches/0009-RISC-V-Networking-fix-Hack.patch b/target/linux/riscv64/patches/0009-RISC-V-Networking-fix-Hack.patch
deleted file mode 100644 (file)
index 35009a2..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-From aa230e7dc2ab01db5b630f427e57297ffc25c884 Mon Sep 17 00:00:00 2001
-From: Atish Patra <[email protected]>
-Date: Fri, 7 Sep 2018 10:22:27 -0700
-Subject: [PATCH 09/11] RISC-V: Networking fix Hack
-
-It looks like that kernel driver now supports reseting the
-signal one additional time. As it had been  already reset
-twice in FSBL, PHY gets into incorrect state causing below error.
-
-----------------------------------------------------------------------
-macb 10090000.ethernet (unnamed net_device) (uninitialized): Could not attach to PHY
-macb: probe of 10090000.ethernet failed with error -110
-----------------------------------------------------------------------
-
-This patch is just a temporary fix until we have a fix a FSBL.
-It is just a **HACK** and **NOT TO BE MERGED** into mainline.
-
-Signed-off-by: Atish Patra <[email protected]>
----
- drivers/net/phy/mdio_bus.c | 3 ---
- 1 file changed, 3 deletions(-)
-
-diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
-index 98f4b1f7..02c31f83 100644
---- a/drivers/net/phy/mdio_bus.c
-+++ b/drivers/net/phy/mdio_bus.c
-@@ -64,9 +64,6 @@ static int mdiobus_register_gpiod(struct mdio_device *mdiodev)
-       mdiodev->reset = gpiod;
--      /* Assert the reset signal again */
--      mdio_device_reset(mdiodev, 1);
--
-       return 0;
- }
--- 
-2.7.4
-
diff --git a/target/linux/riscv64/patches/0010-pcie-microsemi-added-support-for-the-Vera-board-root.patch b/target/linux/riscv64/patches/0010-pcie-microsemi-added-support-for-the-Vera-board-root.patch
deleted file mode 100644 (file)
index 47c75ea..0000000
+++ /dev/null
@@ -1,804 +0,0 @@
-From 1fd6c8fbc07bec3c18771738ce4b2aad3ed228f2 Mon Sep 17 00:00:00 2001
-From: "Wesley W. Terpstra" <[email protected]>
-Date: Wed, 25 Apr 2018 12:10:15 -0700
-Subject: [PATCH 10/11] pcie-microsemi: added support for the Vera-board root
- complex
-
----
- drivers/pci/controller/Kconfig          |   6 +
- drivers/pci/controller/Makefile         |   1 +
- drivers/pci/controller/pcie-microsemi.c | 754 ++++++++++++++++++++++++++++++++
- 3 files changed, 761 insertions(+)
- create mode 100644 drivers/pci/controller/pcie-microsemi.c
-
-diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
-index 028b2874..4458c119 100644
---- a/drivers/pci/controller/Kconfig
-+++ b/drivers/pci/controller/Kconfig
-@@ -278,5 +278,11 @@ config VMD
-         To compile this driver as a module, choose M here: the
-         module will be called vmd.
-+config PCIE_MICROSEMI
-+      bool "Microsemi AXI PCIe host bridge support"
-+      depends on OF || COMPILE_TEST
-+      help
-+        Say 'Y' here if you want kernel to support the Microsemi AXI PCIe
-+        Host Bridge driver.
- source "drivers/pci/controller/dwc/Kconfig"
- endmenu
-diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
-index d56a5074..c3b76ff2 100644
---- a/drivers/pci/controller/Makefile
-+++ b/drivers/pci/controller/Makefile
-@@ -28,6 +28,7 @@ obj-$(CONFIG_PCIE_ROCKCHIP_HOST) += pcie-rockchip-host.o
- obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
- obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
- obj-$(CONFIG_PCIE_TANGO_SMP8759) += pcie-tango.o
-+obj-$(CONFIG_PCIE_MICROSEMI) += pcie-microsemi.o
- obj-$(CONFIG_VMD) += vmd.o
- # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW
- obj-y                         += dwc/
-diff --git a/drivers/pci/controller/pcie-microsemi.c b/drivers/pci/controller/pcie-microsemi.c
-new file mode 100644
-index 00000000..9e2abca2
---- /dev/null
-+++ b/drivers/pci/controller/pcie-microsemi.c
-@@ -0,0 +1,754 @@
-+/*
-+ * PCIe host controller driver for Microsemi AXI PCIe Bridge
-+ *
-+ * Copyright (c) 2018 - Microsemi.
-+ * Author: Badal Nilawar <[email protected]>
-+ *
-+ * Based on the Xilinx, Altera PCIe driver
-+ *
-+ *
-+ * This program is free software: you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation, either version 2 of the License, or
-+ * (at your option) any later version.
-+ */
-+
-+#include <linux/interrupt.h>
-+#include <linux/irq.h>
-+#include <linux/irqdomain.h>
-+#include <linux/kernel.h>
-+#include <linux/init.h>
-+#include <linux/msi.h>
-+#include <linux/of_address.h>
-+#include <linux/of_pci.h>
-+#include <linux/of_platform.h>
-+#include <linux/of_irq.h>
-+#include <linux/pci.h>
-+#include <linux/platform_device.h>
-+#include "../pci.h"
-+
-+/* ECAM definitions */
-+#define ECAM_BUS_NUM_SHIFT            20
-+#define ECAM_DEV_NUM_SHIFT            12
-+
-+/* Number of MSI IRQs */
-+#define MICROSEMI_NUM_MSI_IRQS                32
-+
-+/* PCIe Bridge Phy and Controller Phy offsets */
-+#define PCIE0_BRIDGE_PHY_ADDR_OFFSET                  0x03004000u
-+#define PCIE0_CRTL_PHY_ADDR_OFFSET                    0x03006000u
-+
-+#define PCIE0_BRIDGE_ADDR             0x03004000u
-+#define PCIE0_CRTL_ADDR                       0x03006000u
-+
-+#define PCIE1_BRIDGE_ADDR             0x00008000u
-+#define PCIE1_CRTL_ADDR                       0x0000A000u
-+
-+/* PCIe LTSSM State reg */
-+#define LTSSM_STATE           0x5c
-+
-+/* PCIe LTSSM L0 state */
-+#define LTSSM_L0_STATE                0x10
-+
-+/* PCIe Controller Phy Regs */
-+#define SEC_ERROR_INT         0x28
-+#define SEC_ERROR_INT_MASK    0x2c
-+#define DED_ERROR_INT         0x30
-+#define DED_ERROR_INT_MASK    0x34
-+#define ECC_CONTROL           0x38
-+#define PCIE_EVENT_INT                0x14c
-+
-+/* PCIe Bridge Phy Regs */
-+#define IMASK_LOCAL           0x180
-+#define ISTATUS_LOCAL         0x184
-+#define IMASK_HOST            0x188
-+#define ISTATUS_HOST          0x18c
-+#define       ISTATUS_MSI             0x194
-+#define       PCIE_PCI_IDS_DW1        0x9c
-+
-+/* PCIe AXI slave table init defines */
-+#define ATR0_AXI4_SLV0_SRCADDR_PARAM  0x800u
-+#define ATR0_AXI4_SLV0_SRC_ADDR               0x804u
-+#define ATR0_AXI4_SLV0_TRSL_ADDR_LSB  0x808u
-+#define ATR0_AXI4_SLV0_TRSL_ADDR_UDW  0x80cu
-+#define ATR0_AXI4_SLV0_TRSL_PARAM     0x810u
-+
-+#define ATR1_AXI4_SLV0_SRCADDR_PARAM  0x820u
-+#define ATR1_AXI4_SLV0_SRC_ADDR               0x824u
-+#define ATR1_AXI4_SLV0_TRSL_ADDR_LSB  0x828u
-+#define ATR1_AXI4_SLV0_TRSL_ADDR_UDW  0x82cu
-+#define ATR1_AXI4_SLV0_TRSL_PARAM     0x830u
-+
-+/* PCIe Master table init defines */
-+#define ATR0_PCIE_WIN0_SRCADDR_PARAM  0x600u
-+
-+/* Translated ID */
-+#define  PCIE_TX_RX_INTERFACE         0x00000000u
-+#define  PCIE_CONFIG_INTERFACE                0x00000001u
-+
-+/* PCIe Config space MSI capability structure */
-+#define PCIE_ENABLE_MSI                       0x10000000u
-+
-+/* MSI definitions */
-+#define MSI_MSG_ADDR                  0x190u
-+#define MSI_ENABLE                    (0x01u << 16)
-+#define MSI_ENABLE_MULTI              (MICROSEMI_NUM_MSI_IRQS << 20)
-+
-+/* MSI Capability Structure  */
-+#define MSI_CAP_CTRL                  0xE0u
-+#define MSI_MSG_ADDR_OFFSET           0xE4u
-+#define MSI_MSG_UPPER_ADDR_OFFEST     0xE8u
-+#define MSI_MSG_DATA                  0xF0u
-+
-+
-+
-+/******************************************************************************/
-+/*Enable PCIe local*/
-+#define PCIE_LOCAL_INT_ENABLE         0xF000000u
-+
-+/******************************************************************************/
-+/* Clear PCIe interrupt events */
-+#define PCIE_EVENT_INT_DATA           0x00070007u
-+#define PCIE_ECC_DISABLE              0x0F000000u
-+#define PCIE_SEC_ERROR_INT_CLEAR      0x0000FFFFu
-+#define PCIE_DED_ERROR_INT_CLEAR      0x0000FFFFu
-+#define PCIE_ISTATUS_CLEAR            0xFFFFFFFFu
-+#define PCIE_CLEAR                    0x00000000u
-+#define PCIE_SET                      0x00000001u
-+
-+#define ROOT_PORT_ENABLE              0x00000001u
-+
-+#define NULL_POINTER                  0x00000000u
-+
-+/*****************************************************************************/
-+/* PCIe Controller 0 */
-+#define PF_PCIE_CTRL_0                 0u
-+/* PCIe Controller 1 */
-+#define PF_PCIE_CTRL_1                 1u
-+
-+/* It indicates that the ATR table is enabled */
-+#define PF_PCIE_ATR_TABLE_ENABLE       1u
-+/* It indicates that the the ATR table is disabled */
-+#define PF_PCIE_ATR_TABLE_DISABLE      0u
-+
-+
-+/**
-+ * struct microsemi_pcie_port - PCIe port information
-+ * @reg_base: IO Mapped Register Base
-+ * @irq: Interrupt number
-+ * @root_busno: Root Bus number
-+ * @dev: Device pointer
-+ * @msi_domain: MSI IRQ domain pointer
-+ * @leg_domain: Legacy IRQ domain pointer
-+ * @resources: Bus Resources
-+ */
-+struct microsemi_pcie_port {
-+      struct platform_device  *pdev;
-+      void __iomem *reg_base;
-+      void __iomem *reg_base_apb;
-+      void __iomem *reg_bridge_apb;
-+      void __iomem *reg_ctrl_apb;
-+      u32 irq;
-+      u8 root_busno;
-+      struct device *dev;
-+      struct irq_domain *msi_domain;
-+      struct irq_domain *leg_domain;
-+      struct list_head resources;
-+};
-+
-+static DECLARE_BITMAP(msi_irq_in_use, MICROSEMI_NUM_MSI_IRQS);
-+
-+static inline u32 pcie_read(struct microsemi_pcie_port *port, u32 reg)
-+{
-+      return readl(port->reg_base + reg);
-+}
-+
-+static inline void pcie_write(struct microsemi_pcie_port *port,
-+                              u32 val, u32 reg)
-+{
-+      writel(val, port->reg_base + reg);
-+}
-+
-+static inline bool microsemi_pcie_link_up(struct microsemi_pcie_port *port)
-+{
-+      return (readl(port->reg_ctrl_apb + LTSSM_STATE)
-+              & LTSSM_L0_STATE) ? 1 : 0;
-+}
-+
-+/**
-+ * microsemi_pcie_valid_device - Check if a valid device is present on bus
-+ * @bus: PCI Bus structure
-+ * @devfn: device/function
-+ *
-+ * Return: 'true' on success and 'false' if invalid device is found
-+ */
-+static bool microsemi_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
-+{
-+      struct microsemi_pcie_port *port = bus->sysdata;
-+
-+      /* Check if link is up when trying to access downstream ports */
-+      if (bus->number != port->root_busno)
-+              if (!microsemi_pcie_link_up(port))
-+                      return false;
-+
-+      /* Only one device down on each root port */
-+      if (bus->number == port->root_busno && devfn > 0)
-+              return false;
-+
-+      return true;
-+}
-+
-+/**
-+ * microsemi_pcie_map_bus - Get configuration base
-+ * @bus: PCI Bus structure
-+ * @devfn: Device/function
-+ * @where: Offset from base
-+ *
-+ * Return: Base address of the configuration space needed to be
-+ *       accessed.
-+ */
-+static void __iomem *microsemi_pcie_map_bus(struct pci_bus *bus,
-+                                       unsigned int devfn, int where)
-+{
-+      struct microsemi_pcie_port *port = bus->sysdata;
-+      int relbus;
-+
-+
-+      if (!microsemi_pcie_valid_device(bus, devfn))
-+              return NULL;
-+
-+      relbus = (bus->number << ECAM_BUS_NUM_SHIFT) |
-+               (devfn << ECAM_DEV_NUM_SHIFT);
-+
-+
-+      return port->reg_base + relbus + where;
-+}
-+
-+/* PCIe operations */
-+static struct pci_ops microsemi_pcie_ops = {
-+      .map_bus = microsemi_pcie_map_bus,
-+      .read   = pci_generic_config_read,
-+      .write  = pci_generic_config_write,
-+};
-+
-+/* MSI functions */
-+
-+/**
-+ * microsemi_pcie_destroy_msi - Free MSI number
-+ * @irq: IRQ to be freed
-+ */
-+static void microsemi_pcie_destroy_msi(unsigned int irq)
-+{
-+      struct msi_desc *msi;
-+      struct microsemi_pcie_port *port;
-+      struct irq_data *d = irq_get_irq_data(irq);
-+      irq_hw_number_t hwirq = irqd_to_hwirq(d);
-+
-+      if (!test_bit(hwirq, msi_irq_in_use)) {
-+              msi = irq_get_msi_desc(irq);
-+              port = msi_desc_to_pci_sysdata(msi);
-+              dev_err(port->dev, "Trying to free unused MSI#%d\n", irq);
-+      } else {
-+              clear_bit(hwirq, msi_irq_in_use);
-+      }
-+}
-+
-+/**
-+ * microsemi_pcie_assign_msi - Allocate MSI number
-+ *
-+ * Return: A valid IRQ on success and error value on failure.
-+ */
-+static int microsemi_pcie_assign_msi(void)
-+{
-+      int pos;
-+
-+      pos = find_first_zero_bit(msi_irq_in_use, MICROSEMI_NUM_MSI_IRQS);
-+      if (pos < MICROSEMI_NUM_MSI_IRQS)
-+              set_bit(pos, msi_irq_in_use);
-+      else
-+              return -ENOSPC;
-+
-+      return pos;
-+}
-+
-+/**
-+ * microsemi_msi_teardown_irq - Destroy the MSI
-+ * @chip: MSI Chip descriptor
-+ * @irq: MSI IRQ to destroy
-+ */
-+static void microsemi_msi_teardown_irq(struct msi_controller *chip,
-+                                  unsigned int irq)
-+{
-+      microsemi_pcie_destroy_msi(irq);
-+      irq_dispose_mapping(irq);
-+}
-+
-+/**
-+ * microsemi_pcie_msi_setup_irq - Setup MSI request
-+ * @chip: MSI chip pointer
-+ * @pdev: PCIe device pointer
-+ * @desc: MSI descriptor pointer
-+ *
-+ * Return: '0' on success and error value on failure
-+ */
-+static int microsemi_pcie_msi_setup_irq(struct msi_controller *chip,
-+                                   struct pci_dev *pdev,
-+                                   struct msi_desc *desc)
-+{
-+      struct microsemi_pcie_port *port = pdev->bus->sysdata;
-+      unsigned int irq;
-+      int hwirq;
-+      struct msi_msg msg;
-+
-+      hwirq = microsemi_pcie_assign_msi();
-+      if (hwirq < 0)
-+              return hwirq;
-+
-+      irq = irq_create_mapping(port->msi_domain, hwirq);
-+      if (!irq)
-+              return -EINVAL;
-+
-+      irq_set_msi_desc(irq, desc);
-+
-+      msg.address_hi = 0;
-+      msg.address_lo = MSI_MSG_ADDR;
-+      msg.data = hwirq;
-+
-+      pci_write_msi_msg(irq, &msg);
-+
-+      return 0;
-+}
-+
-+/* MSI Chip Descriptor */
-+static struct msi_controller microsemi_pcie_msi_chip = {
-+      .setup_irq = microsemi_pcie_msi_setup_irq,
-+      .teardown_irq = microsemi_msi_teardown_irq,
-+};
-+
-+/* HW Interrupt Chip Descriptor */
-+static struct irq_chip microsemi_msi_irq_chip = {
-+      .name = "Microsemi PCIe MSI",
-+      .irq_enable = pci_msi_unmask_irq,
-+      .irq_disable = pci_msi_mask_irq,
-+      .irq_mask = pci_msi_mask_irq,
-+      .irq_unmask = pci_msi_unmask_irq,
-+};
-+
-+/**
-+ * microsemi_pcie_msi_map - Set the handler for the MSI and mark IRQ as valid
-+ * @domain: IRQ domain
-+ * @irq: Virtual IRQ number
-+ * @hwirq: HW interrupt number
-+ *
-+ * Return: Always returns 0.
-+ */
-+static int microsemi_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
-+                             irq_hw_number_t hwirq)
-+{
-+      irq_set_chip_and_handler(irq,
-+                              &microsemi_msi_irq_chip, handle_simple_irq);
-+      irq_set_chip_data(irq, domain->host_data);
-+
-+      return 0;
-+}
-+
-+/* IRQ Domain operations */
-+static const struct irq_domain_ops msi_domain_ops = {
-+      .map = microsemi_pcie_msi_map,
-+};
-+
-+/**
-+ * microsemi_pcie_enable_msi - Enable MSI support
-+ * @port: PCIe port information
-+ */
-+static void microsemi_pcie_enable_msi(struct microsemi_pcie_port *port)
-+{
-+      u32 cap_ctrl;
-+
-+      cap_ctrl = pcie_read(port, MSI_CAP_CTRL);
-+
-+      pcie_write(port, cap_ctrl | MSI_ENABLE_MULTI | MSI_ENABLE, MSI_CAP_CTRL);
-+      pcie_write(port, MSI_MSG_ADDR, MSI_MSG_ADDR_OFFSET);
-+}
-+
-+/* INTx Functions */
-+
-+/**
-+ * microsemi_pcie_intx_map - Set the handler for the INTx and mark IRQ as valid
-+ * @domain: IRQ domain
-+ * @irq: Virtual IRQ number
-+ * @hwirq: HW interrupt number
-+ *
-+ * Return: Always returns 0.
-+ */
-+static int microsemi_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
-+                              irq_hw_number_t hwirq)
-+{
-+      irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
-+      irq_set_chip_data(irq, domain->host_data);
-+
-+      return 0;
-+}
-+
-+/* INTx IRQ Domain operations */
-+static const struct irq_domain_ops intx_domain_ops = {
-+      .map = microsemi_pcie_intx_map,
-+      .xlate = pci_irqd_intx_xlate,
-+};
-+
-+/* PCIe HW Functions */
-+
-+/**
-+ * microsemi_pcie_intr_handler - Interrupt Service Handler
-+ * @irq: IRQ number
-+ * @data: PCIe port information
-+ *
-+ * Return: IRQ_HANDLED on success and IRQ_NONE on failure
-+ */
-+static irqreturn_t microsemi_pcie_intr_handler(int irq, void *data)
-+{
-+      struct microsemi_pcie_port *port = (struct microsemi_pcie_port *)data;
-+      struct device *dev = port->dev;
-+      unsigned long status;
-+      unsigned long msi;
-+      u32 bit;
-+      u32 virq;
-+
-+
-+      status = readl(port->reg_bridge_apb + ISTATUS_LOCAL);
-+
-+      status = (status >> 24) & 0x0f;
-+      for_each_set_bit(bit, &status, PCI_NUM_INTX) {
-+              /* clear interrupts */
-+              writel(1 << (bit+24),
-+                      port->reg_bridge_apb + ISTATUS_LOCAL);
-+
-+              virq = irq_find_mapping(port->leg_domain, bit);
-+
-+              if (virq)
-+                      generic_handle_irq(virq);
-+              else
-+                      dev_err(dev, "unexpected IRQ, INT%d\n", bit);
-+      }
-+
-+      status = readl(port->reg_bridge_apb + ISTATUS_LOCAL);
-+      if ((status & 0x10000000) == 0x10000000) {
-+              writel((1 << 28), port->reg_bridge_apb + ISTATUS_LOCAL);
-+              msi = readl(port->reg_bridge_apb + ISTATUS_MSI);
-+              for_each_set_bit(bit, &msi, MICROSEMI_NUM_MSI_IRQS) {
-+              /* clear interrupts */
-+                      writel((1 << bit),
-+                              port->reg_bridge_apb + ISTATUS_MSI);
-+                      virq = irq_find_mapping(port->msi_domain, bit);
-+                      if (virq)
-+                              generic_handle_irq(virq);
-+                      else
-+                              dev_err(dev, "unexpected IRQ, INT%d\n", bit);
-+              }
-+      }
-+
-+      return IRQ_HANDLED;
-+}
-+
-+/**
-+ * microsemi_pcie_init_irq_domain - Initialize IRQ domain
-+ * @port: PCIe port information
-+ *
-+ * Return: '0' on success and error value on failure
-+ */
-+static int microsemi_pcie_init_irq_domain(struct microsemi_pcie_port *port)
-+{
-+      struct device *dev = port->dev;
-+      struct device_node *node = dev->of_node;
-+      struct device_node *pcie_intc_node;
-+
-+      /* Setup INTx */
-+      pcie_intc_node = of_get_next_child(node, NULL);
-+      if (!pcie_intc_node) {
-+              dev_err(dev, "No PCIe Intc node found\n");
-+              return -ENODEV;
-+      }
-+      dev_info(dev, "Intc node foundi %s\n", pcie_intc_node->name);
-+
-+      port->leg_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
-+                                               &intx_domain_ops,
-+                                               port);
-+      if (!port->leg_domain) {
-+              dev_err(dev, "Failed to get a INTx IRQ domain\n");
-+              return -ENODEV;
-+      }
-+
-+      /* Setup MSI */
-+      if (IS_ENABLED(CONFIG_PCI_MSI)) {
-+              port->msi_domain = irq_domain_add_linear(node,
-+                                              MICROSEMI_NUM_MSI_IRQS,
-+                                              &msi_domain_ops,
-+                                              &microsemi_pcie_msi_chip);
-+              if (!port->msi_domain) {
-+                      dev_err(dev, "Failed to get a MSI IRQ domain\n");
-+                      return -ENODEV;
-+              }
-+              microsemi_pcie_enable_msi(port);
-+      }
-+      return 0;
-+}
-+
-+/**
-+ * microsemi_pcie_init_port - Parse Device tree, Initialize hardware
-+ * @port: PCIe port information
-+ *
-+ * Return: '0' on success and error value on failure
-+ */
-+static int microsemi_pcie_init_port(struct microsemi_pcie_port *port)
-+{
-+      struct device *dev = port->dev;
-+      struct device_node *node = dev->of_node;
-+      struct of_pci_range_parser parser;
-+      struct of_pci_range range;
-+      struct resource regs;
-+      struct resource regs1;
-+      resource_size_t size;
-+      u32 bit, pf_bridge_id = 1;
-+      const char *type;
-+      int err;
-+
-+      type = of_get_property(node, "device_type", NULL);
-+      if (!type || strcmp(type, "pci")) {
-+              dev_err(dev, "invalid \"device_type\" %s\n", type);
-+              return -EINVAL;
-+      }
-+
-+      err = of_address_to_resource(node, 0, &regs);
-+      if (err) {
-+              dev_err(dev, "missing \"reg\" property\n");
-+              return err;
-+      }
-+
-+      port->reg_base = devm_pci_remap_cfg_resource(dev, &regs);
-+      if (IS_ERR(port->reg_base))
-+              return PTR_ERR(port->reg_base);
-+
-+
-+      err = of_address_to_resource(node, 1, &regs1);
-+      if (err) {
-+              dev_err(dev, "missing \"reg\" property\n");
-+              return err;
-+      }
-+
-+
-+      port->reg_base_apb = devm_ioremap_resource(dev, &regs1);
-+      if (IS_ERR(port->reg_base_apb))
-+              return PTR_ERR(port->reg_base_apb);
-+
-+      if (pf_bridge_id == 0) {
-+              port->reg_bridge_apb =  port->reg_base_apb + PCIE0_BRIDGE_ADDR;
-+              port->reg_ctrl_apb = port->reg_base_apb + PCIE0_CRTL_ADDR;
-+      } else {
-+              port->reg_bridge_apb =  port->reg_base_apb + PCIE1_BRIDGE_ADDR;
-+              port->reg_ctrl_apb = port->reg_base_apb + PCIE1_CRTL_ADDR;
-+      }
-+
-+      port->irq = irq_of_parse_and_map(node, 0);
-+
-+      err = devm_request_irq(dev, port->irq, microsemi_pcie_intr_handler,
-+                             IRQF_SHARED | IRQF_NO_THREAD,
-+                             "microsemi-pcie", port);
-+      if (err) {
-+              dev_err(dev, "unable to request irq %d\n", port->irq);
-+              return err;
-+      }
-+
-+
-+      /* Clear and Disable interrupts */
-+
-+      writel(0x0f000000, port->reg_ctrl_apb + ECC_CONTROL);
-+      writel(0x00070007, port->reg_ctrl_apb + PCIE_EVENT_INT);
-+      writel(0x0000ffff, port->reg_ctrl_apb + SEC_ERROR_INT);
-+      writel(0x0000ffff, port->reg_ctrl_apb + SEC_ERROR_INT_MASK);
-+      writel(0x0000ffff, port->reg_ctrl_apb + DED_ERROR_INT);
-+      writel(0x0000ffff, port->reg_ctrl_apb + DED_ERROR_INT_MASK);
-+
-+      writel(0x00000000, port->reg_bridge_apb + IMASK_LOCAL);
-+      writel(0xffffffff, port->reg_bridge_apb + ISTATUS_LOCAL);
-+      writel(0x00000000, port->reg_bridge_apb + IMASK_HOST);
-+      writel(0xffffffff, port->reg_bridge_apb + ISTATUS_HOST);
-+
-+      dev_info(dev, "interrupt disabled\n");
-+
-+      /* Configure Address Translation Table 0 for pcie config space */
-+
-+      writel(PCIE_CONFIG_INTERFACE,
-+              port->reg_bridge_apb + ATR0_AXI4_SLV0_TRSL_PARAM);
-+
-+      size = resource_size(&regs);
-+
-+      bit = find_first_bit((const unsigned long *)&size, 64) - 1;
-+
-+      writel((u32)regs.start | bit << 1 | 0x01,
-+              port->reg_bridge_apb + ATR0_AXI4_SLV0_SRCADDR_PARAM);
-+
-+//    writel((u32)(regs.start >> 32),
-+//            port->reg_bridge_apb + ATR0_AXI4_SLV0_SRC_ADDR);
-+
-+      writel((u32)regs.start,
-+              port->reg_bridge_apb + ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
-+
-+//    writel((u32)(regs.start >> 32),
-+//            port->reg_bridge_apb + ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
-+
-+
-+      if (of_pci_range_parser_init(&parser, node)) {
-+              dev_err(dev, "missing \"ranges\" property\n");
-+              return -EINVAL;
-+      }
-+
-+
-+      for_each_of_pci_range(&parser, &range) {
-+              switch (range.flags & IORESOURCE_TYPE_BITS) {
-+              case IORESOURCE_MEM:
-+
-+              size = range.size;
-+              bit = find_first_bit((const unsigned long *)&size, 64) - 1;
-+
-+              /* Configure Address Translation Table 1 for pcie mem space */
-+
-+              writel(PCIE_TX_RX_INTERFACE,
-+                      port->reg_bridge_apb + ATR1_AXI4_SLV0_TRSL_PARAM);
-+
-+              writel((u32)range.cpu_addr | bit << 1 | 0x01,
-+                      port->reg_bridge_apb + ATR1_AXI4_SLV0_SRCADDR_PARAM);
-+
-+//            writel((u32)(range.cpu_addr >> 32),
-+//                    port->reg_bridge_apb + ATR1_AXI4_SLV0_SRC_ADDR);
-+
-+              writel((u32)range.pci_addr,
-+                      port->reg_bridge_apb + ATR1_AXI4_SLV0_TRSL_ADDR_LSB);
-+
-+//            writel((u32)(range.pci_addr >> 32),
-+//                    port->reg_bridge_apb + ATR1_AXI4_SLV0_TRSL_ADDR_UDW);
-+
-+              break;
-+              }
-+
-+      }
-+
-+
-+      writel(readl(port->reg_bridge_apb + ATR0_PCIE_WIN0_SRCADDR_PARAM)
-+              | 0x3E,
-+              port->reg_bridge_apb + ATR0_PCIE_WIN0_SRCADDR_PARAM);
-+
-+      writel(0, port->reg_bridge_apb + 0x604);
-+
-+      writel((readl(port->reg_bridge_apb + PCIE_PCI_IDS_DW1) & 0xffff)
-+              | (PCI_CLASS_BRIDGE_PCI << 16),
-+              port->reg_bridge_apb + PCIE_PCI_IDS_DW1);
-+
-+      pcie_write(port, 0x00ff0100, 0x18);
-+
-+      /* Enable interrupts */
-+      writel(PCIE_ENABLE_MSI | PCIE_LOCAL_INT_ENABLE,
-+              port->reg_bridge_apb + IMASK_LOCAL);
-+
-+
-+      return 0;
-+}
-+
-+/**
-+ * microsemi_pcie_probe - Probe function
-+ * @pdev: Platform device pointer
-+ *
-+ * Return: '0' on success and error value on failure
-+ */
-+static int microsemi_pcie_probe(struct platform_device *pdev)
-+{
-+      struct device *dev = &pdev->dev;
-+      struct microsemi_pcie_port *port;
-+      struct pci_bus *bus, *child;
-+      struct pci_host_bridge *bridge;
-+      int err;
-+      resource_size_t iobase = 0;
-+      LIST_HEAD(res);
-+
-+      pr_err("%s In \n", __func__);
-+      if (!dev->of_node)
-+              return -ENODEV;
-+
-+      bridge = devm_pci_alloc_host_bridge(dev, sizeof(*port));
-+      if (!bridge)
-+              return -ENODEV;
-+
-+      port = pci_host_bridge_priv(bridge);
-+
-+      port->dev = dev;
-+      port->pdev = pdev;
-+
-+      err = microsemi_pcie_init_port(port);
-+      if (err) {
-+              dev_err(dev, "Pcie port initialization failed\n");
-+              return err;
-+      }
-+
-+
-+      err = microsemi_pcie_init_irq_domain(port);
-+      if (err) {
-+              dev_err(dev, "Failed creating IRQ Domain\n");
-+              return err;
-+      }
-+
-+      err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, &res,
-+                                             &iobase);
-+      if (err) {
-+              dev_err(dev, "Getting bridge resources failed\n");
-+              return err;
-+      }
-+
-+      err = devm_request_pci_bus_resources(dev, &res);
-+      if (err)
-+              goto error;
-+
-+
-+      list_splice_init(&res, &bridge->windows);
-+      bridge->dev.parent = dev;
-+      bridge->sysdata = port;
-+      bridge->busnr = 0;
-+      bridge->ops = &microsemi_pcie_ops;
-+      bridge->map_irq = of_irq_parse_and_map_pci;
-+      bridge->swizzle_irq = pci_common_swizzle;
-+
-+#ifdef CONFIG_PCI_MSI
-+      microsemi_pcie_msi_chip.dev = dev;
-+      bridge->msi = &microsemi_pcie_msi_chip;
-+#endif
-+      err = pci_scan_root_bus_bridge(bridge);
-+      dev_info(dev, "pci_scan_root_bus_bridge done\n");
-+      if (err < 0)
-+              goto error;
-+
-+      bus = bridge->bus;
-+
-+      pci_assign_unassigned_bus_resources(bus);
-+      list_for_each_entry(child, &bus->children, node)
-+              pcie_bus_configure_settings(child);
-+      pci_bus_add_devices(bus);
-+
-+      return 0;
-+
-+error:
-+      pci_free_resource_list(&res);
-+      return err;
-+}
-+
-+static const struct of_device_id microsemi_pcie_of_match[] = {
-+      { .compatible = "ms-pf,axi-pcie-host", },
-+      {}
-+};
-+
-+static struct platform_driver microsemi_pcie_driver = {
-+      .driver = {
-+              .name = "microsemi-pcie",
-+              .of_match_table = microsemi_pcie_of_match,
-+              .suppress_bind_attrs = true,
-+      },
-+      .probe = microsemi_pcie_probe,
-+};
-+builtin_platform_driver(microsemi_pcie_driver);
--- 
-2.7.4
-
diff --git a/target/linux/riscv64/patches/002-clk-sifive-prci.patch b/target/linux/riscv64/patches/002-clk-sifive-prci.patch
deleted file mode 100644 (file)
index 11e2725..0000000
+++ /dev/null
@@ -1,509 +0,0 @@
-From 7f45b80bb9675e9ace37bc1c4fd8f0351dfd9de9 Mon Sep 17 00:00:00 2001
-From: "Wesley W. Terpstra" <[email protected]>
-Date: Tue, 13 Feb 2018 19:39:41 -0800
-Subject: [PATCH 1/5] u54-prci: driver for core U54 clocks
-
----
- drivers/clk/sifive/Kconfig    |   4 +
- drivers/clk/sifive/Makefile   |   1 +
- drivers/clk/sifive/u54-prci.c | 314 ++++++++++++++++++++++++++++++++++
- 3 files changed, 319 insertions(+)
- create mode 100644 drivers/clk/sifive/Kconfig
- create mode 100644 drivers/clk/sifive/Makefile
- create mode 100644 drivers/clk/sifive/u54-prci.c
-
-diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
-new file mode 100644
-index 0000000000000..a562e0c6dc67e
---- /dev/null
-+++ b/drivers/clk/sifive/Kconfig
-@@ -0,0 +1,4 @@
-+config CLK_U54_PRCI
-+      bool "PRCI driver for U54 SoCs"
-+      ---help---
-+        Supports Power Reset Clock interface found in U540 SoCs
-diff --git a/drivers/clk/sifive/Makefile b/drivers/clk/sifive/Makefile
-new file mode 100644
-index 0000000000000..0c2b175b5846d
---- /dev/null
-+++ b/drivers/clk/sifive/Makefile
-@@ -0,0 +1 @@
-+obj-$(CONFIG_CLK_U54_PRCI)    += u54-prci.o
-diff --git a/drivers/clk/sifive/u54-prci.c b/drivers/clk/sifive/u54-prci.c
-new file mode 100644
-index 0000000000000..edc4b7818e710
---- /dev/null
-+++ b/drivers/clk/sifive/u54-prci.c
-@@ -0,0 +1,314 @@
-+/*
-+ * 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.
-+ *
-+ * 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.
-+ *
-+ * Copyright (C) 2018 SiFive, Inc.
-+ */
-+
-+#include <linux/clkdev.h>
-+#include <linux/clk-provider.h>
-+#include <linux/clk.h>
-+#include <linux/err.h>
-+#include <linux/of.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+#include <linux/log2.h>
-+
-+#define CORE_CLOCK 0
-+#define GEMTX_CLOCK 1
-+#define PRCI_CLOCKS 2
-+
-+#define MIN_REF 7000000UL
-+#define MAX_REF 200000000UL
-+#define MAX_PARENT 600000000UL
-+#define MAX_VCO 4800000000UL
-+#define MAX_DIV 64
-+#define MAX_R 64UL
-+
-+#define PLL_LOCK 0x80000000U
-+#define NAME_LEN 40
-+
-+struct sifive_u54_prci_driver;
-+
-+struct sifive_u54_prci_pll {
-+      struct clk_hw hw;
-+      struct sifive_u54_prci_driver *driver;
-+      char name[NAME_LEN];
-+      u32 freq;
-+      u32 glcm;
-+};
-+
-+struct sifive_u54_prci_driver {
-+      struct clk_onecell_data table;
-+      struct clk *clks[PRCI_CLOCKS];
-+      struct sifive_u54_prci_pll plls[PRCI_CLOCKS];
-+      void __iomem *reg;
-+};
-+
-+#define to_sifive_u54_prci_pll(hw) container_of(hw, struct sifive_u54_prci_pll, hw)
-+
-+struct sifive_u54_pll_cfg {
-+      unsigned long r, f, q, a;
-+};
-+
-+static struct sifive_u54_pll_cfg sifive_u54_pll_cfg(u32 reg)
-+{
-+      struct sifive_u54_pll_cfg cfg;
-+      cfg.r = (reg >>  0) & 0x3f;
-+      cfg.f = (reg >>  6) & 0x1ff;
-+      cfg.q = (reg >> 15) & 0x7;
-+      cfg.a = (reg >> 18) & 0x7;
-+      return cfg;
-+}
-+
-+static u32 sifive_u54_pll_reg(struct sifive_u54_pll_cfg cfg)
-+{
-+      u32 reg = 0;
-+      reg |= (cfg.r & 0x3f)  << 0;
-+      reg |= (cfg.f & 0x1ff) << 6;
-+      reg |= (cfg.q & 0x7)   << 15;
-+      reg |= (cfg.a & 0x7)   << 18;
-+      reg |= 1<<25; // internal feedback
-+      return reg;
-+}
-+
-+static unsigned long sifive_u54_pll_rate(struct sifive_u54_pll_cfg cfg, unsigned long parent)
-+{
-+      return (parent*2*(cfg.f+1) / (cfg.r+1)) >> cfg.q;
-+}
-+
-+static struct sifive_u54_pll_cfg sifive_u54_pll_configure(unsigned long target, unsigned long parent)
-+{
-+      struct sifive_u54_pll_cfg cfg;
-+      unsigned long scale, ratio, best_delta, filter;
-+      u32 max_r, best_r, best_f, r;
-+
-+      /* Confirm input frequency is within bounds */
-+      if (WARN_ON(parent > MAX_PARENT)) { parent = MAX_PARENT; }
-+      if (WARN_ON(parent < MIN_REF))    { parent = MIN_REF; }
-+
-+      /* Calculate the Q shift and target VCO */
-+      scale = MAX_VCO / target;
-+      if (scale <= 1) {
-+              cfg.q = 1;
-+              target = MAX_VCO;
-+      } else if (scale > MAX_DIV) {
-+              cfg.q = ilog2(MAX_DIV);
-+              target = MAX_VCO/2;
-+      } else {
-+              cfg.q = ilog2(scale);
-+              target = target << cfg.q;
-+      }
-+
-+      /* Precalcualte the target ratio */
-+      ratio = (target << 20) / parent;
-+
-+      /* Placeholder values */
-+      best_r = 0;
-+      best_f = 0;
-+      best_delta = MAX_VCO;
-+
-+      /* Consider all values for R which land within [MIN_REF, MAX_REF]; prefer smaller R */
-+      max_r = min(MAX_R, parent / MIN_REF);
-+      for (r = DIV_ROUND_UP(parent, MAX_REF); r <= max_r; ++r) {
-+              /* What is the best F we can pick in this case? */
-+              u32 f = (ratio*r + (1<<20)) >> 21;
-+              unsigned long ref = parent / r;
-+              unsigned long vco = ref * f * 2;
-+              unsigned long delta;
-+
-+              /* Ensure rounding didn't take us out of range */
-+              if (vco > target) --f;
-+              if (vco < MAX_VCO/2) ++f;
-+              vco = ref * f * 2;
-+
-+              delta = abs(target - vco);
-+              if (delta < best_delta) {
-+                      best_delta = delta;
-+                      best_r = r;
-+                      best_f = f;
-+              }
-+      }
-+
-+      cfg.r = best_r - 1;
-+      cfg.f = best_f - 1;
-+
-+      /* Pick the best PLL jitter filter */
-+      filter = parent / best_r;
-+      BUG_ON(filter < 7000000);
-+      if (filter < 11000000) {
-+              cfg.a = 1;
-+      } else if (filter < 18000000) {
-+              cfg.a = 2;
-+      } else if (filter < 30000000) {
-+              cfg.a = 3;
-+      } else if (filter < 50000000) {
-+              cfg.a = 4;
-+      } else if (filter < 80000000) {
-+              cfg.a = 5;
-+      } else if (filter < 130000000) {
-+              cfg.a = 6;
-+      } else {
-+              BUG_ON (filter > 200000000);
-+              cfg.a = 7;
-+      }
-+
-+      return cfg;
-+}
-+
-+static unsigned long sifive_u54_prci_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
-+{
-+      struct sifive_u54_prci_pll *pll = to_sifive_u54_prci_pll(hw);
-+      struct sifive_u54_prci_driver *driver = pll->driver;
-+
-+      u32 reg = ioread32(driver->reg + pll->freq);
-+      struct sifive_u54_pll_cfg cfg = sifive_u54_pll_cfg(reg);
-+
-+      return sifive_u54_pll_rate(cfg, parent_rate);
-+}
-+
-+static long sifive_u54_prci_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate)
-+{
-+      struct sifive_u54_pll_cfg cfg = sifive_u54_pll_configure(rate, *parent_rate);
-+      return sifive_u54_pll_rate(cfg, *parent_rate);
-+}
-+
-+static int sifive_u54_prci_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
-+{
-+      struct sifive_u54_prci_pll *pll = to_sifive_u54_prci_pll(hw);
-+      struct sifive_u54_prci_driver *driver = pll->driver;
-+
-+      struct sifive_u54_pll_cfg cfg = sifive_u54_pll_configure(rate, parent_rate);
-+      u32 reg = sifive_u54_pll_reg(cfg);
-+
-+      /* Switch to reg clock and reconfigure PLL */
-+      iowrite32(1, driver->reg + pll->glcm);
-+      iowrite32(reg, driver->reg + pll->freq);
-+
-+      /* Wait for lock and switch back to PLL */
-+      while (!(ioread32(driver->reg + pll->freq) & PLL_LOCK));
-+      iowrite32(0, driver->reg + pll->glcm);
-+
-+      return 0;
-+}
-+
-+static const struct clk_ops sifive_u54_prci_ops_rw = {
-+      .recalc_rate = sifive_u54_prci_recalc_rate,
-+      .round_rate = sifive_u54_prci_round_rate,
-+      .set_rate = sifive_u54_prci_set_rate,
-+};
-+
-+static const struct clk_ops sifive_u54_prci_ops_ro = {
-+      .recalc_rate = sifive_u54_prci_recalc_rate,
-+};
-+
-+static ssize_t sifive_u54_pll_show(struct device *dev, struct device_attribute *attr, char *buf)
-+{
-+      struct sifive_u54_prci_driver *driver = dev_get_drvdata(dev);
-+      return sprintf(buf, "%ld", clk_get_rate(driver->clks[0]));
-+}
-+
-+static ssize_t sifive_u54_pll_rate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-+{
-+      struct sifive_u54_prci_driver *driver = dev_get_drvdata(dev);
-+      unsigned long rate;
-+      char *endp;
-+
-+      rate = simple_strtoul(buf, &endp, 0);
-+      if (*endp != 0 && *endp != '\n')
-+              return -EINVAL;
-+
-+      clk_set_rate(driver->clks[0], rate);
-+      return count;
-+}
-+
-+static DEVICE_ATTR(rate, 0644, sifive_u54_pll_show, sifive_u54_pll_rate_store);
-+
-+static int sifive_u54_prci_probe(struct platform_device *pdev)
-+{
-+      struct device *dev = &pdev->dev;
-+      struct clk_init_data init;
-+      struct sifive_u54_prci_driver *driver;
-+      struct resource *res;
-+      const char *parent;
-+      int i;
-+
-+      parent = of_clk_get_parent_name(dev->of_node, 0);
-+      if (!parent) {
-+              dev_err(dev, "No OF parent clocks found\n");
-+              return -EINVAL;
-+      }
-+
-+      driver = devm_kzalloc(dev, sizeof(*driver), GFP_KERNEL);
-+      if (!driver) {
-+              dev_err(dev, "Out of memory\n");
-+              return -ENOMEM;
-+      }
-+
-+      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+      driver->reg = devm_ioremap_resource(dev, res);
-+      if (IS_ERR(driver->reg))
-+              return PTR_ERR(driver->reg);
-+
-+      /* Link the data structure */
-+      driver->table.clk_num = PRCI_CLOCKS;
-+      driver->table.clks = &driver->clks[0];
-+      dev_set_drvdata(dev, driver);
-+
-+      /* Describe the clocks */
-+      snprintf(driver->plls[CORE_CLOCK].name, NAME_LEN, "%s.core", dev->of_node->name);
-+      driver->plls[CORE_CLOCK].freq = 0x4;
-+      driver->plls[CORE_CLOCK].glcm = 0x24;
-+      snprintf(driver->plls[GEMTX_CLOCK].name, NAME_LEN, "%s.gemtx", dev->of_node->name);
-+      driver->plls[GEMTX_CLOCK].freq = 0x1c;
-+      driver->plls[GEMTX_CLOCK].glcm = 0; /* None; cannot be set_rate */
-+
-+      /* Export the clocks */
-+      for (i = 0; i < PRCI_CLOCKS; ++i) {
-+              init.name = &driver->plls[i].name[0];
-+              init.ops = driver->plls[i].glcm ? &sifive_u54_prci_ops_rw : &sifive_u54_prci_ops_ro;
-+              init.num_parents = 1;
-+              init.parent_names = &parent;
-+              init.flags = 0;
-+
-+              driver->plls[i].driver = driver;
-+              driver->plls[i].hw.init = &init;
-+
-+              driver->clks[i] = devm_clk_register(dev, &driver->plls[i].hw);
-+              if (IS_ERR(driver->clks[i])) {
-+                      dev_err(dev, "Failed to register clock %d, %ld\n", i, PTR_ERR(driver->clks[i]));
-+                      return PTR_ERR(driver->clks[i]);
-+              }
-+      }
-+
-+      of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &driver->table);
-+      device_create_file(dev, &dev_attr_rate);
-+      dev_info(dev, "Registered U54 core clocks\n");
-+
-+      return 0;
-+}
-+
-+static const struct of_device_id sifive_u54_prci_of_match[] = {
-+      { .compatible = "sifive,aloeprci0", },
-+      {}
-+};
-+
-+static struct platform_driver sifive_u54_prci_driver = {
-+      .driver = {
-+              .name = "sifive-u54-prci",
-+              .of_match_table = sifive_u54_prci_of_match,
-+      },
-+      .probe = sifive_u54_prci_probe,
-+};
-+
-+static int __init sifive_u54_prci_init(void)
-+{
-+      return platform_driver_register(&sifive_u54_prci_driver);
-+}
-+core_initcall(sifive_u54_prci_init);
-
-From 9b47a41c087008233d3024cc3f5005984a6c504c Mon Sep 17 00:00:00 2001
-From: Palmer Dabbelt <[email protected]>
-Date: Wed, 21 Feb 2018 10:06:48 -0800
-Subject: [PATCH 2/5] Fix some overflow warnings
-
----
- drivers/clk/sifive/u54-prci.c | 18 +++++++++---------
- 1 file changed, 9 insertions(+), 9 deletions(-)
-
-diff --git a/drivers/clk/sifive/u54-prci.c b/drivers/clk/sifive/u54-prci.c
-index edc4b7818e710..b8a93d1ebc2db 100644
---- a/drivers/clk/sifive/u54-prci.c
-+++ b/drivers/clk/sifive/u54-prci.c
-@@ -24,15 +24,15 @@
- #define GEMTX_CLOCK 1
- #define PRCI_CLOCKS 2
--#define MIN_REF 7000000UL
--#define MAX_REF 200000000UL
--#define MAX_PARENT 600000000UL
--#define MAX_VCO 4800000000UL
--#define MAX_DIV 64
--#define MAX_R 64UL
--
--#define PLL_LOCK 0x80000000U
--#define NAME_LEN 40
-+#define MIN_REF 7000000ULL
-+#define MAX_REF 200000000ULL
-+#define MAX_PARENT 600000000ULL
-+#define MAX_VCO 4800000000ULL
-+#define MAX_DIV 64ULL
-+#define MAX_R 64ULL
-+
-+#define PLL_LOCK 0x80000000ULL
-+#define NAME_LEN 40ULL
- struct sifive_u54_prci_driver;
-
-From 031060d77cfbd86d39dfb8863e4fa4f95b435b1b Mon Sep 17 00:00:00 2001
-From: Palmer Dabbelt <[email protected]>
-Date: Wed, 21 Feb 2018 13:00:16 -0800
-Subject: [PATCH 3/5] Include the sifive kconfig
-
----
- drivers/clk/Kconfig | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
-index 721572a8c4296..5b4bb0a23ffd3 100644
---- a/drivers/clk/Kconfig
-+++ b/drivers/clk/Kconfig
-@@ -288,6 +288,7 @@ source "drivers/clk/mvebu/Kconfig"
- source "drivers/clk/qcom/Kconfig"
- source "drivers/clk/renesas/Kconfig"
- source "drivers/clk/samsung/Kconfig"
-+source "drivers/clk/sifive/Kconfig"
- source "drivers/clk/sprd/Kconfig"
- source "drivers/clk/sunxi-ng/Kconfig"
- source "drivers/clk/tegra/Kconfig"
-
-From 9f8ca54f7c62aeb3245d8a70150d15e25ffb4f15 Mon Sep 17 00:00:00 2001
-From: Palmer Dabbelt <[email protected]>
-Date: Wed, 21 Feb 2018 13:40:10 -0800
-Subject: [PATCH 4/5] Only show the U64 clock driver on RISC-V
-
-This will probably only be on a RISC-V system, and it doesn't build on
-32-bit systems without 64-bit divides which is a headache.
-
-Signed-off-by: Palmer Dabbelt <[email protected]>
----
- drivers/clk/sifive/Kconfig | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
-index a562e0c6dc67e..c324161700271 100644
---- a/drivers/clk/sifive/Kconfig
-+++ b/drivers/clk/sifive/Kconfig
-@@ -1,4 +1,5 @@
- config CLK_U54_PRCI
-       bool "PRCI driver for U54 SoCs"
-+      depends on RISCV
-       ---help---
-         Supports Power Reset Clock interface found in U540 SoCs
-
-From d79106eca2349e212c4737c5646002bd864b1593 Mon Sep 17 00:00:00 2001
-From: "Wesley W. Terpstra" <[email protected]>
-Date: Tue, 13 Feb 2018 19:39:41 -0800
-Subject: [PATCH 5/5] u54-prci: driver for core U54 clocks
-
----
- .../bindings/clock/sifive,u54-prci.txt        | 44 +++++++++++++++++++
- 1 file changed, 44 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/clock/sifive,u54-prci.txt
-
-diff --git a/Documentation/devicetree/bindings/clock/sifive,u54-prci.txt b/Documentation/devicetree/bindings/clock/sifive,u54-prci.txt
-new file mode 100644
-index 0000000000000..88682c5eaebc6
---- /dev/null
-+++ b/Documentation/devicetree/bindings/clock/sifive,u54-prci.txt
-@@ -0,0 +1,44 @@
-+SiFive U54 SoC clocks
-+
-+This binding uses the common clock binding:
-+    Documentation/devicetree/bindings/clock/clock-bindings.txt
-+
-+The U54 PRCI controller generates clocks for the U54 SoC. There is
-+a core PLL that sets the processor frequency and PLLs for ethernet
-+and DDR. It takes an input clock from the board, typically an oscillator
-+or crystal.
-+
-+Required properties:
-+- compatible: Should be "sifive,aloeprci0"
-+- #clock-cells:       Should be <1>
-+- reg:                Specifies base physical address and size of the registers
-+- clocks:     phandles to the parent clock used as input
-+
-+Example:
-+
-+      refclk: refclk {
-+              #clock-cells = <0>;
-+              compatible = "fixed-clock";
-+              clock-frequency = <33333333>;
-+              clock-output-names = "xtal";
-+      };
-+
-+      u54: prci@10000000 {
-+              compatible = "sifive,aloeprci0";
-+              reg = <0x0 0x10000000 0x0 0x1000>;
-+              clocks = <&refclk>;
-+              #clock-cells = <1>;
-+      };
-+
-+      tlclk: tlclk {
-+              compatible = "fixed-factor-clock";
-+              clocks = <&u54 0>; /* Core frequency */
-+              #clock-cells = <0>;
-+              clock-div = <2>;
-+              clock-mult = <1>;
-+      };
-+
-+      ethernet@10090000 {
-+              ...
-+              clocks = <&prci 1>; /* TX clock */
-+      };
-diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
-index ae40cbe770f05..017d8d12551b6 100644
---- a/drivers/clk/Makefile
-+++ b/drivers/clk/Makefile
-@@ -89,6 +89,7 @@ obj-$(CONFIG_COMMON_CLK_QCOM)                += qcom/
- obj-y                                 += renesas/
- obj-$(CONFIG_ARCH_ROCKCHIP)           += rockchip/
- obj-$(CONFIG_COMMON_CLK_SAMSUNG)      += samsung/
-+obj-y                                 += sifive/
- obj-$(CONFIG_ARCH_SIRF)                       += sirf/
- obj-$(CONFIG_ARCH_SOCFPGA)            += socfpga/
- obj-$(CONFIG_PLAT_SPEAR)              += spear/
diff --git a/target/linux/riscv64/patches/003-clk-gemgxl.patch b/target/linux/riscv64/patches/003-clk-gemgxl.patch
deleted file mode 100644 (file)
index c44befc..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-From 13317fd60728d24988fb8f5682bfaafe401b3a15 Mon Sep 17 00:00:00 2001
-From: "Wesley W. Terpstra" <[email protected]>
-Date: Tue, 6 Feb 2018 11:03:07 -0800
-Subject: [PATCH] gemgxl-mgmt: implement clock switch for GEM tx_clk
-
-Signed-off-by: Palmer Dabbelt <[email protected]>
----
-
-diff --git a/Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt b/Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt
-new file mode 100644
-index 0000000000000..349489e33edaa
---- /dev/null
-+++ b/Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt
-@@ -0,0 +1,26 @@
-+TX clock switch for GEMGXL in U540 SoCs
-+
-+This binding uses the common clock binding:
-+    Documentation/devicetree/bindings/clock/clock-bindings.txt
-+
-+The U54 includes a clock mux to control the ethernet TX frequenecy. It
-+switches between the local TX clock (125MHz) and PHY TX clocks. This is
-+necessary to toggle between 1Gb and 100/10Mb speeds.
-+
-+Required properties:
-+- compatible: Should be "sifive,cadencegemgxlmgmt0"
-+- #clock-cells:       Should be <0>
-+- reg:                Specifies base physical address and size of the registers
-+
-+Example:
-+
-+      mgmt: cadence-gemgxl-mgmt@100a00000 {
-+              compatible = "sifive,cadencegemgxlmgmt0";
-+              #clock-cells = <0>;
-+              reg = <0x0 0x100a0000 0x0 0x1000>;
-+      };
-+
-+      ethernet@10090000 {
-+              ...
-+              clocks = <&mgmt>; /* TX clock */
-+      };
-diff --git a/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
-index 0000000000000..284bffb121ebd
---- a/drivers/clk/sifive/Kconfig
-+++ b/drivers/clk/sifive/Kconfig
-@@ -0,0 +1,9 @@
-+config CLK_U54_PRCI
-+      bool "PRCI driver for U54 SoCs"
-+      ---help---
-+        Supports Power Reset Clock interface found in U540 SoCs
-+
-+config CLK_GEMGXL_MGMT
-+      bool "TX clock switch for GEMGXL in U540 SoCs"
-+      ---help---
-+        Supports clock muxing between 10/100Mbit and 1Gbit TX clock on U540 SoCs
-diff --git a/drivers/clk/sifive/Makefile b/drivers/clk/sifive/Makefile
-index 0000000000000..7784d2ee0f446
---- a/drivers/clk/sifive/Makefile
-+++ b/drivers/clk/sifive/Makefile
-@@ -0,0 +1,2 @@
-+obj-$(CONFIG_CLK_U54_PRCI)    += u54-prci.o
-+obj-$(CONFIG_CLK_GEMGXL_MGMT) += gemgxl-mgmt.o
-diff --git a/drivers/clk/sifive/gemgxl-mgmt.c b/drivers/clk/sifive/gemgxl-mgmt.c
-new file mode 100644
-index 0000000000000..00b07580fe3ce
---- /dev/null
-+++ b/drivers/clk/sifive/gemgxl-mgmt.c
-@@ -0,0 +1,129 @@
-+/*
-+ * 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.
-+ *
-+ * 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.
-+ *
-+ * Copyright (C) 2018 SiFive, Inc.
-+ */
-+
-+#include <linux/clkdev.h>
-+#include <linux/clk-provider.h>
-+#include <linux/err.h>
-+#include <linux/of.h>
-+#include <linux/platform_device.h>
-+#include <linux/slab.h>
-+
-+struct sifive_gemgxl_mgmt {
-+      void __iomem *reg;
-+      unsigned long rate;
-+      struct clk_hw hw;
-+};
-+
-+#define to_sifive_gemgxl_mgmt(mgmt) container_of(mgmt, struct sifive_gemgxl_mgmt, hw)
-+
-+static unsigned long sifive_gemgxl_mgmt_recalc_rate(struct clk_hw *hw,
-+                                    unsigned long parent_rate)
-+{
-+      struct sifive_gemgxl_mgmt *mgmt = to_sifive_gemgxl_mgmt(hw);
-+      return mgmt->rate;
-+}
-+
-+static long sifive_gemgxl_mgmt_round_rate(struct clk_hw *hw, unsigned long rate,
-+              unsigned long *parent_rate)
-+{
-+      if (WARN_ON(rate < 2500000)) {
-+              return 2500000;
-+      } else if (rate == 2500000) {
-+              return 2500000;
-+      } else if (WARN_ON(rate < 13750000)) {
-+              return 2500000;
-+      } else if (WARN_ON(rate < 25000000)) {
-+              return 25000000;
-+      } else if (rate == 25000000) {
-+              return 25000000;
-+      } else if (WARN_ON(rate < 75000000)) {
-+              return 25000000;
-+      } else if (WARN_ON(rate < 125000000)) {
-+              return 125000000;
-+      } else if (rate == 125000000) {
-+              return 125000000;
-+      } else {
-+              WARN_ON(rate > 125000000);
-+              return 125000000;
-+      }
-+}
-+
-+static int sifive_gemgxl_mgmt_set_rate(struct clk_hw *hw, unsigned long rate,
-+              unsigned long parent_rate)
-+{
-+      struct sifive_gemgxl_mgmt *mgmt = to_sifive_gemgxl_mgmt(hw);
-+      rate = sifive_gemgxl_mgmt_round_rate(hw, rate, &parent_rate);
-+      iowrite32(rate != 125000000, mgmt->reg);
-+      mgmt->rate = rate;
-+      return 0;
-+}
-+
-+static const struct clk_ops sifive_gemgxl_mgmt_ops = {
-+      .recalc_rate = sifive_gemgxl_mgmt_recalc_rate,
-+      .round_rate = sifive_gemgxl_mgmt_round_rate,
-+      .set_rate = sifive_gemgxl_mgmt_set_rate,
-+};
-+
-+static int sifive_gemgxl_mgmt_probe(struct platform_device *pdev)
-+{
-+      struct clk_init_data init;
-+      struct sifive_gemgxl_mgmt *mgmt;
-+      struct resource *res;
-+      struct clk *clk;
-+
-+      mgmt = devm_kzalloc(&pdev->dev, sizeof(*mgmt), GFP_KERNEL);
-+      if (!mgmt)
-+              return -ENOMEM;
-+
-+      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+      mgmt->reg = devm_ioremap_resource(&pdev->dev, res);
-+      if (IS_ERR(mgmt->reg))
-+              return PTR_ERR(mgmt->reg);
-+
-+      init.name = pdev->dev.of_node->name;
-+      init.ops = &sifive_gemgxl_mgmt_ops;
-+      init.flags = 0;
-+      init.num_parents = 0;
-+
-+      mgmt->rate = 0;
-+      mgmt->hw.init = &init;
-+
-+      clk = clk_register(NULL, &mgmt->hw);
-+      if (IS_ERR(clk))
-+              return PTR_ERR(clk);
-+
-+      of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
-+
-+      dev_info(&pdev->dev, "Registered clock switch '%s'\n", init.name);
-+
-+      return 0;
-+}
-+
-+static const struct of_device_id sifive_gemgxl_mgmt_of_match[] = {
-+      { .compatible = "sifive,cadencegemgxlmgmt0", },
-+      {}
-+};
-+
-+static struct platform_driver sifive_gemgxl_mgmt_driver = {
-+      .driver = {
-+              .name = "sifive-gemgxl-mgmt",
-+              .of_match_table = sifive_gemgxl_mgmt_of_match,
-+      },
-+      .probe = sifive_gemgxl_mgmt_probe,
-+};
-+
-+static int __init sifive_gemgxl_mgmt_init(void)
-+{
-+      return platform_driver_register(&sifive_gemgxl_mgmt_driver);
-+}
-+core_initcall(sifive_gemgxl_mgmt_init);
diff --git a/target/linux/riscv64/patches/004-spi-sifive.patch b/target/linux/riscv64/patches/004-spi-sifive.patch
deleted file mode 100644 (file)
index 78f6c31..0000000
+++ /dev/null
@@ -1,509 +0,0 @@
-From 1e23d75040e34fa26cacf715acb23a4344825bc2 Mon Sep 17 00:00:00 2001
-From: "Wesley W. Terpstra" <[email protected]>
-Date: Mon, 5 Feb 2018 17:42:32 -0800
-Subject: [PATCH] spi-sifive: support SiFive SPI controller in Quad-Mode
-
-Signed-off-by: Palmer Dabbelt <[email protected]>
----
- .../devicetree/bindings/spi/spi-sifive.txt    |  29 ++
- drivers/spi/Kconfig                           |   7 +
- drivers/spi/Makefile                          |   1 +
- drivers/spi/spi-sifive.c                      | 423 ++++++++++++++++++
- 4 files changed, 460 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/spi/spi-sifive.txt
- create mode 100644 drivers/spi/spi-sifive.c
-
-diff --git a/Documentation/devicetree/bindings/spi/spi-sifive.txt b/Documentation/devicetree/bindings/spi/spi-sifive.txt
-new file mode 100644
-index 0000000000000..945543244601b
---- /dev/null
-+++ b/Documentation/devicetree/bindings/spi/spi-sifive.txt
-@@ -0,0 +1,29 @@
-+SiFive SPI controller Device Tree Bindings
-+-------------------------------------------------
-+
-+Required properties:
-+- compatible          : Should be "sifive,spi0"
-+- reg                 : Physical base address and size of SPI registers map
-+                        A second (optional) range can indicate memory mapped flash
-+- interrupts          : Must contain one entry
-+- interrupt-parent    : Must be core interrupt controller
-+- clocks              : Must reference the frequency given to the controller
-+- #address-cells      : Must be '1', indicating which CS to use
-+- #size-cells         : Must be '0'
-+
-+Optional properties:
-+- sifive,buffer-size  : Depth of hardware queues; defaults to 8
-+- sifive,bits-per-word        : Maximum bits per word; defaults to 8
-+
-+Example:
-+      spi: spi@10040000 {
-+              compatible = "sifive,spi0";
-+              reg = <0x0 0x10040000 0x0 0x1000 0x0 0x20000000 0x0 0x10000000>;
-+              interrupt-parent = <&plic>;
-+              interrupts = <51>;
-+              clocks = <&tlclk>;
-+              #address-cells = <1>;
-+              #size-cells = <0>;
-+              sifive,buffer-size = <8>;
-+              sifive,bits-per-word = <8>;
-+      };
-diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
-index ad5d68e1dab7c..f77cd28a84392 100644
---- a/drivers/spi/Kconfig
-+++ b/drivers/spi/Kconfig
-@@ -736,6 +736,13 @@ config SPI_ZYNQMP_GQSPI
-       help
-         Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC.
-+config SPI_SIFIVE
-+        tristate "SiFive SPI controller"
-+        depends on HAS_IOMEM
-+      select SPI_BITBANG
-+        help
-+          This exposes the SPI controller IP from SiFive.
-+
- #
- # Add new SPI master controllers in alphabetical order above this line
- #
-diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
-index cb1f4378b87c0..5c8e8866b16f8 100644
---- a/drivers/spi/Makefile
-+++ b/drivers/spi/Makefile
-@@ -106,6 +106,7 @@ obj-$(CONFIG_SPI_XILINX)           += spi-xilinx.o
- obj-$(CONFIG_SPI_XLP)                 += spi-xlp.o
- obj-$(CONFIG_SPI_XTENSA_XTFPGA)               += spi-xtensa-xtfpga.o
- obj-$(CONFIG_SPI_ZYNQMP_GQSPI)                += spi-zynqmp-gqspi.o
-+obj-$(CONFIG_SPI_SIFIVE)              += spi-sifive.o
- # SPI slave protocol handlers
- obj-$(CONFIG_SPI_SLAVE_TIME)          += spi-slave-time.o
-diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
-new file mode 100644
-index 0000000000000..208566a9888d7
---- /dev/null
-+++ b/drivers/spi/spi-sifive.c
-@@ -0,0 +1,423 @@
-+/*
-+ * SiFive SPI controller driver (master mode only)
-+ *
-+ * Author: SiFive, Inc.
-+ *    [email protected]
-+ *
-+ * 2018 (c) SiFive Software, Inc.
-+
-+ * 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 <linux/clk.h>
-+#include <linux/module.h>
-+#include <linux/interrupt.h>
-+#include <linux/of.h>
-+#include <linux/platform_device.h>
-+#include <linux/spi/spi.h>
-+#include <linux/io.h>
-+#include <linux/log2.h>
-+
-+#define SIFIVE_SPI_MAX_CS     32
-+
-+#define SIFIVE_SPI_NAME "sifive_spi"
-+
-+#define SIFIVE_SPI_DEFAULT_DEPTH 8
-+#define SIFIVE_SPI_DEFAULT_BITS       8
-+
-+#define XSPI_SCDR_OFFSET      0x000   /* Serial Clock Divisor Register */
-+#define XSPI_SCD_SCALE_MASK   0xFFF
-+
-+#define XSPI_SCMR_OFFSET        0x004   /* Serial Clock Mode Register */
-+#define XSPI_SCM_CPHA         1
-+#define XSPI_SCM_CPOL         2
-+#define XSPI_SCM_MODE_MASK     (XSPI_SCM_CPHA | XSPI_SCM_CPOL)
-+
-+#define XSPI_CSIDR_OFFSET       0x010
-+#define XSPI_CSDR_OFFSET        0x014
-+#define XSPI_CSMR_OFFSET        0x018
-+#define XSPI_CSM_MODE_AUTO      0
-+#define XSPI_CSM_MODE_HOLD      2
-+#define XSPI_CSM_MODE_OFF       3
-+
-+#define XSPI_DC0R_OFFSET        0x028
-+#define XSPI_CS_TO_SCK_MASK     0xFF
-+#define XSPI_SCK_TO_CS_MASK     (0xFF << 16)
-+#define XSPI_DC1R_OFFSET        0x02C
-+#define XSPI_MIN_CS_IATIME_MASK 0xFF
-+#define XSPI_MAX_IF_DELAY_MASK  (0xFF << 16)
-+
-+#define XSPI_FFR_OFFSET         0x040
-+#define XSPI_FF_SINGLE          0
-+#define XSPI_FF_DUAL            1
-+#define XSPI_FF_QUAD            2
-+#define XSPI_FF_SPI_MASK        0x3
-+#define XSPI_FF_LSB_FIRST       4
-+#define XSPI_FF_TX_DIR          8
-+#define XSPI_FF_BPF_MASK        (0xFF << 16)
-+
-+#define XSPI_TXDR_OFFSET      0x048   /* Data Transmit Register */
-+#define XSPI_TXD_FIFO_FULL      (8U << 28)
-+#define XSPI_RXDR_OFFSET      0x04C   /* Data Receive Register */
-+#define XSPI_RXD_FIFO_EMPTY     (8U << 28)
-+#define XSPI_DATA_MASK          0xFF
-+#define XSPI_DATA_SHIFT         8
-+
-+#define XSPI_TXWMR_OFFSET       0x050   /* TX FIFO Watermark Register */
-+#define XSPI_RXWMR_OFFSET       0x054   /* RX FIFO Watermark Register */
-+
-+#define XSPI_FCTRL_OFFSET     0x60
-+
-+#define XSPI_IPR_OFFSET               0x074   /* Interrupt Pendings Register */
-+#define XSPI_IER_OFFSET               0x070   /* Interrupt Enable Register */
-+#define XSPI_TXWM_INTR          0x1
-+#define XSPI_RXWM_INTR          0x2
-+
-+struct sifive_spi {
-+      void __iomem    *regs;          /* virt. address of the control registers */
-+      struct clk      *clk;           /* bus clock */
-+      int             irq;            /* watermark irq */
-+      int             buffer_size;    /* buffer size in words */
-+      u32             cs_inactive;    /* Level of the CS pins when inactive*/
-+      struct completion done;         /* Wake-up from interrupt */
-+};
-+
-+static void sifive_spi_write(struct sifive_spi *spi, int offset, u32 value)
-+{
-+      iowrite32(value, spi->regs + offset);
-+}
-+
-+static u32 sifive_spi_read(struct sifive_spi *spi, int offset)
-+{
-+      return ioread32(spi->regs + offset);
-+}
-+
-+static void sifive_spi_init(struct sifive_spi *spi)
-+{
-+      /* Watermark interrupts are disabled by default */
-+      sifive_spi_write(spi, XSPI_IER_OFFSET, 0);
-+
-+      /* Default watermark FIFO threshold values */
-+      sifive_spi_write(spi, XSPI_TXWMR_OFFSET, 1);
-+      sifive_spi_write(spi, XSPI_RXWMR_OFFSET, 0);
-+
-+      /* Set CS/SCK Delays and Inactive Time to defaults */
-+
-+      /* Exit specialized memory-mapped SPI flash mode */
-+      sifive_spi_write(spi, XSPI_FCTRL_OFFSET, 0);
-+}
-+
-+static void sifive_spi_prep_device(struct sifive_spi *spi, struct spi_device *device)
-+{
-+      u32 cr;
-+
-+      /* Update the chip select polarity */
-+      if (device->mode & SPI_CS_HIGH)
-+              spi->cs_inactive &= ~BIT(device->chip_select);
-+      else
-+              spi->cs_inactive |= BIT(device->chip_select);
-+      sifive_spi_write(spi, XSPI_CSDR_OFFSET, spi->cs_inactive);
-+
-+      /* Select the correct device */
-+      sifive_spi_write(spi, XSPI_CSIDR_OFFSET, device->chip_select);
-+
-+      /* Switch clock mode bits */
-+      cr = sifive_spi_read(spi, XSPI_SCMR_OFFSET) & ~XSPI_SCM_MODE_MASK;
-+      if (device->mode & SPI_CPHA)
-+              cr |= XSPI_SCM_CPHA;
-+      if (device->mode & SPI_CPOL)
-+              cr |= XSPI_SCM_CPOL;
-+      sifive_spi_write(spi, XSPI_SCMR_OFFSET, cr);
-+}
-+
-+static int sifive_spi_prep_transfer(struct sifive_spi *spi, struct spi_device *device, struct spi_transfer *t)
-+{
-+      u32 hz, scale, cr;
-+      int mode;
-+
-+      /* Calculate and program the clock rate */
-+      hz = t->speed_hz ? t->speed_hz : device->max_speed_hz;
-+      scale = (DIV_ROUND_UP(clk_get_rate(spi->clk) >> 1, hz) - 1) & XSPI_SCD_SCALE_MASK;
-+      sifive_spi_write(spi, XSPI_SCDR_OFFSET, scale);
-+
-+      /* Modify the SPI protocol mode */
-+      cr = sifive_spi_read(spi, XSPI_FFR_OFFSET);
-+
-+      /* LSB first? */
-+      cr &= ~XSPI_FF_LSB_FIRST;
-+      if (device->mode & SPI_LSB_FIRST)
-+              cr |= XSPI_FF_LSB_FIRST;
-+
-+      /* SINGLE/DUAL/QUAD? */
-+      mode = max((int)t->rx_nbits, (int)t->tx_nbits);
-+      cr &= ~XSPI_FF_SPI_MASK;
-+      switch (mode) {
-+              case SPI_NBITS_QUAD: cr |= XSPI_FF_QUAD;   break;
-+              case SPI_NBITS_DUAL: cr |= XSPI_FF_DUAL;   break;
-+              default:             cr |= XSPI_FF_SINGLE; break;
-+      }
-+
-+      /* SPI direction */
-+      cr &= ~XSPI_FF_TX_DIR;
-+      if (!t->rx_buf)
-+              cr |= XSPI_FF_TX_DIR;
-+
-+      sifive_spi_write(spi, XSPI_FFR_OFFSET, cr);
-+
-+      /* We will want to poll if the time we need to wait is less than the context switching time.
-+       * Let's call that threshold 5us. The operation will take:
-+       *    (8/mode) * buffer_size / hz <= 5 * 10^-6
-+       *    1600000 * buffer_size <= hz * mode
-+       */
-+      return 1600000 * spi->buffer_size <= hz * mode;
-+}
-+
-+static void sifive_spi_tx(struct sifive_spi *spi, const u8* tx_ptr)
-+{
-+      BUG_ON((sifive_spi_read(spi, XSPI_TXDR_OFFSET) & XSPI_TXD_FIFO_FULL) != 0);
-+      sifive_spi_write(spi, XSPI_TXDR_OFFSET, *tx_ptr & XSPI_DATA_MASK);
-+}
-+
-+static void sifive_spi_rx(struct sifive_spi *spi, u8* rx_ptr)
-+{
-+        u32 data = sifive_spi_read(spi, XSPI_RXDR_OFFSET);
-+        BUG_ON((data & XSPI_RXD_FIFO_EMPTY) != 0);
-+        *rx_ptr = data & XSPI_DATA_MASK;
-+}
-+
-+static void sifive_spi_wait(struct sifive_spi *spi, int bit, int poll)
-+{
-+      if (poll) {
-+              u32 cr;
-+              do cr = sifive_spi_read(spi, XSPI_IPR_OFFSET);
-+              while (!(cr & bit));
-+      } else {
-+              reinit_completion(&spi->done);
-+              sifive_spi_write(spi, XSPI_IER_OFFSET, bit);
-+              wait_for_completion(&spi->done);
-+      }
-+}
-+
-+static void sifive_spi_execute(struct sifive_spi *spi, struct spi_transfer *t, int poll)
-+{
-+      int remaining_words = t->len;
-+      const u8* tx_ptr = t->tx_buf;
-+      u8* rx_ptr = t->rx_buf;
-+
-+      while (remaining_words) {
-+              int n_words, tx_words, rx_words;
-+              n_words = min(remaining_words, spi->buffer_size);
-+
-+              /* Enqueue n_words for transmission */
-+              for (tx_words = 0; tx_words < n_words; ++tx_words)
-+                      sifive_spi_tx(spi, tx_ptr++);
-+
-+              if (rx_ptr) {
-+                      /* Wait for transmission + reception to complete */
-+                      sifive_spi_write(spi, XSPI_RXWMR_OFFSET, n_words-1);
-+                      sifive_spi_wait(spi, XSPI_RXWM_INTR, poll);
-+
-+                      /* Read out all the data from the RX FIFO */
-+                      for (rx_words = 0; rx_words < n_words; ++rx_words)
-+                              sifive_spi_rx(spi, rx_ptr++);
-+              } else {
-+                      /* Wait for transmission to complete */
-+                      sifive_spi_wait(spi, XSPI_TXWM_INTR, poll);
-+              }
-+
-+              remaining_words -= n_words;
-+      }
-+}
-+
-+static int sifive_spi_transfer_one(struct spi_master *master, struct spi_device *device, struct spi_transfer *t)
-+{
-+      struct sifive_spi *spi = spi_master_get_devdata(master);
-+      int poll;
-+
-+      sifive_spi_prep_device(spi, device);
-+      poll = sifive_spi_prep_transfer(spi, device, t);
-+      sifive_spi_execute(spi, t, poll);
-+
-+      return 0;
-+}
-+
-+static void sifive_spi_set_cs(struct spi_device *device, bool is_high)
-+{
-+      struct sifive_spi *spi = spi_master_get_devdata(device->master);
-+
-+      /* Reverse polarity is handled by SCMR/CPOL. Not inverted CS. */
-+      if (device->mode & SPI_CS_HIGH)
-+              is_high = !is_high;
-+
-+      sifive_spi_write(spi, XSPI_CSMR_OFFSET, is_high ? XSPI_CSM_MODE_AUTO : XSPI_CSM_MODE_HOLD);
-+}
-+
-+static irqreturn_t sifive_spi_irq(int irq, void *dev_id)
-+{
-+      struct sifive_spi *spi = dev_id;
-+      u32 ip;
-+
-+      ip = sifive_spi_read(spi, XSPI_IPR_OFFSET) & (XSPI_TXWM_INTR | XSPI_RXWM_INTR);
-+      if (ip != 0) {
-+              /* Disable interrupts until next transfer */
-+              sifive_spi_write(spi, XSPI_IER_OFFSET, 0);
-+              complete(&spi->done);
-+              return IRQ_HANDLED;
-+      }
-+
-+      return IRQ_NONE;
-+}
-+
-+static int sifive_spi_probe(struct platform_device *pdev)
-+{
-+      struct sifive_spi *spi;
-+      struct resource *res;
-+      int ret, num_cs;
-+      u32 cs_bits, buffer_size, bits_per_word;
-+      struct spi_master *master;
-+
-+      master = spi_alloc_master(&pdev->dev, sizeof(struct sifive_spi));
-+      if (!master) {
-+              dev_err(&pdev->dev, "out of memory\n");
-+              return -ENOMEM;
-+      }
-+
-+      spi = spi_master_get_devdata(master);
-+      init_completion(&spi->done);
-+      platform_set_drvdata(pdev, master);
-+
-+      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;
-+      }
-+
-+      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;
-+      }
-+
-+      spi->irq = platform_get_irq(pdev, 0);
-+      if (spi->irq < 0) {
-+              dev_err(&pdev->dev, "Unable to find interrupt\n");
-+              ret = spi->irq;
-+              goto put_master;
-+      }
-+
-+      /* Optional parameters */
-+      ret = of_property_read_u32(pdev->dev.of_node, "sifive,buffer-size", &buffer_size);
-+      if (ret < 0)
-+              spi->buffer_size = SIFIVE_SPI_DEFAULT_DEPTH;
-+      else
-+              spi->buffer_size = buffer_size;
-+
-+      ret = of_property_read_u32(pdev->dev.of_node, "sifive,bits-per-word", &bits_per_word);
-+      if (ret < 0)
-+              bits_per_word = SIFIVE_SPI_DEFAULT_BITS;
-+
-+      /* 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;
-+      }
-+
-+      /* probe the number of CS lines */
-+      spi->cs_inactive = sifive_spi_read(spi, XSPI_CSDR_OFFSET);
-+      sifive_spi_write(spi, XSPI_CSDR_OFFSET, 0xffffffffU);
-+      cs_bits = sifive_spi_read(spi, XSPI_CSDR_OFFSET);
-+      sifive_spi_write(spi, XSPI_CSDR_OFFSET, spi->cs_inactive);
-+      if (!cs_bits) {
-+              dev_err(&pdev->dev, "Could not auto probe CS lines\n");
-+              ret = -EINVAL;
-+              goto put_master;
-+      }
-+
-+      num_cs = ilog2(cs_bits) + 1;
-+      if (num_cs > SIFIVE_SPI_MAX_CS) {
-+              dev_err(&pdev->dev, "Invalid number of spi slaves\n");
-+              ret = -EINVAL;
-+              goto put_master;
-+      }
-+
-+      /* Define our master */
-+      master->bus_num = pdev->id;
-+      master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH |
-+                          SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD;
-+      master->flags = SPI_CONTROLLER_MUST_TX | SPI_MASTER_GPIO_SS;
-+      master->dev.of_node = pdev->dev.of_node;
-+      master->bits_per_word_mask = SPI_BPW_MASK(bits_per_word);
-+      master->num_chipselect = num_cs;
-+      master->transfer_one = sifive_spi_transfer_one;
-+      master->set_cs = sifive_spi_set_cs;
-+
-+      /* If mmc_spi sees a dma_mask, it starts using dma mapped buffers.
-+       * Probably it should rely on the SPI core auto mapping instead.
-+       */
-+      pdev->dev.dma_mask = 0;
-+
-+      /* Configure the SPI master hardware */
-+      sifive_spi_init(spi);
-+
-+      /* Register for SPI Interrupt */
-+      ret = devm_request_irq(&pdev->dev, spi->irq, sifive_spi_irq, 0,
-+                              dev_name(&pdev->dev), spi);
-+      if (ret) {
-+              dev_err(&pdev->dev, "Unable to bind to interrupt\n");
-+              goto put_master;
-+      }
-+
-+      dev_info(&pdev->dev, "mapped; irq=%d, cs=%d\n",
-+              spi->irq, master->num_chipselect);
-+
-+      ret = devm_spi_register_master(&pdev->dev, master);
-+      if (ret < 0) {
-+              dev_err(&pdev->dev, "spi_register_master failed\n");
-+              goto put_master;
-+      }
-+
-+      return 0;
-+
-+put_master:
-+      spi_master_put(master);
-+
-+      return ret;
-+}
-+
-+static int sifive_spi_remove(struct platform_device *pdev)
-+{
-+      struct spi_master *master = platform_get_drvdata(pdev);
-+      struct sifive_spi *spi = spi_master_get_devdata(master);
-+
-+      /* Disable all the interrupts just in case */
-+      sifive_spi_write(spi, XSPI_IER_OFFSET, 0);
-+      spi_master_put(master);
-+
-+      return 0;
-+}
-+
-+static const struct of_device_id sifive_spi_of_match[] = {
-+      { .compatible = "sifive,spi0", },
-+      {}
-+};
-+MODULE_DEVICE_TABLE(of, sifive_spi_of_match);
-+
-+static struct platform_driver sifive_spi_driver = {
-+      .probe = sifive_spi_probe,
-+      .remove = sifive_spi_remove,
-+      .driver = {
-+              .name = SIFIVE_SPI_NAME,
-+              .of_match_table = sifive_spi_of_match,
-+      },
-+};
-+module_platform_driver(sifive_spi_driver);
-+
-+MODULE_AUTHOR("SiFive, Inc. <[email protected]>");
-+MODULE_DESCRIPTION("SiFive SPI driver");
-+MODULE_LICENSE("GPL");
diff --git a/target/linux/riscv64/patches/005-spi-is25wp256d.patch b/target/linux/riscv64/patches/005-spi-is25wp256d.patch
deleted file mode 100644 (file)
index 68851b4..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-From c6e4a154bd008655dd69a850275d5cb082a7304b Mon Sep 17 00:00:00 2001
-From: "Wesley W. Terpstra" <[email protected]>
-Date: Mon, 5 Feb 2018 17:44:19 -0800
-Subject: [PATCH] spi-nor: add support for is25wp{32,64,128,256}
-
-Signed-off-by: Palmer Dabbelt <[email protected]>
----
- drivers/mtd/spi-nor/spi-nor.c | 47 ++++++++++++++++++++++++++++++++++-
- include/linux/mtd/spi-nor.h   |  2 ++
- 2 files changed, 48 insertions(+), 1 deletion(-)
-
-diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
-index d9c368c441948..e9a3557a3c237 100644
---- a/drivers/mtd/spi-nor/spi-nor.c
-+++ b/drivers/mtd/spi-nor/spi-nor.c
-@@ -1072,6 +1072,9 @@ static const struct flash_info spi_nor_ids[] = {
-                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
-       { "is25wp128",  INFO(0x9d7018, 0, 64 * 1024, 256,
-                       SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
-+      { "is25wp256d", INFO(0x9d7019, 0, 32 * 1024, 1024,
-+                      SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES)
-+      },
-       /* Macronix */
-       { "mx25l512e",   INFO(0xc22010, 0, 64 * 1024,   1, SECT_4K) },
-@@ -1515,6 +1518,45 @@ static int macronix_quad_enable(struct spi_nor *nor)
-       return 0;
- }
-+/**
-+ * issi_unlock() - clear BP[0123] write-protection.
-+ * @nor:      pointer to a 'struct spi_nor'
-+ *
-+ * Bits [2345] of the Status Register are BP[0123].
-+ * ISSI chips use a different block protection scheme than other chips.
-+ * Just disable the write-protect unilaterally.
-+ *
-+ * Return: 0 on success, -errno otherwise.
-+ */
-+static int issi_unlock(struct spi_nor *nor)
-+{
-+      int ret, val;
-+      u8 mask = SR_BP0 | SR_BP1 | SR_BP2 | SR_BP3;
-+
-+      val = read_sr(nor);
-+      if (val < 0)
-+              return val;
-+      if (!(val & mask))
-+              return 0;
-+
-+      write_enable(nor);
-+
-+      write_sr(nor, val & ~mask);
-+
-+      ret = spi_nor_wait_till_ready(nor);
-+      if (ret)
-+              return ret;
-+
-+      ret = read_sr(nor);
-+      if (ret > 0 && !(ret & mask)) {
-+              dev_info(nor->dev, "ISSI Block Protection Bits cleared\n");
-+              return 0;
-+      } else {
-+              dev_err(nor->dev, "ISSI Block Protection Bits not cleared\n");
-+              return -EINVAL;
-+      }
-+}
-+
- /*
-  * Write status Register and configuration register with 2 bytes
-  * The first byte will be written to the status register, while the
-@@ -2747,6 +2789,9 @@ static int spi_nor_init(struct spi_nor *nor)
-               spi_nor_wait_till_ready(nor);
-       }
-+      if (JEDEC_MFR(nor->info) == SNOR_MFR_ISSI)
-+              issi_unlock(nor);
-+
-       if (nor->quad_enable) {
-               err = nor->quad_enable(nor);
-               if (err) {
-@@ -2926,7 +2971,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
-       if (ret)
-               return ret;
--      if (nor->addr_width) {
-+      if (nor->addr_width && JEDEC_MFR(info) != SNOR_MFR_ISSI) {
-               /* already configured from SFDP */
-       } else if (info->addr_width) {
-               nor->addr_width = info->addr_width;
-diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
-index e60da0d34cc14..da422a37d3837 100644
---- a/include/linux/mtd/spi-nor.h
-+++ b/include/linux/mtd/spi-nor.h
-@@ -23,6 +23,7 @@
- #define SNOR_MFR_ATMEL                CFI_MFR_ATMEL
- #define SNOR_MFR_GIGADEVICE   0xc8
- #define SNOR_MFR_INTEL                CFI_MFR_INTEL
-+#define SNOR_MFR_ISSI         0x9d
- #define SNOR_MFR_MICRON               CFI_MFR_ST /* ST Micro <--> Micron */
- #define SNOR_MFR_MACRONIX     CFI_MFR_MACRONIX
- #define SNOR_MFR_SPANSION     CFI_MFR_AMD
-@@ -121,6 +122,7 @@
- #define SR_BP0                        BIT(2)  /* Block protect 0 */
- #define SR_BP1                        BIT(3)  /* Block protect 1 */
- #define SR_BP2                        BIT(4)  /* Block protect 2 */
-+#define SR_BP3                        BIT(5)  /* Block protect 3 (on ISSI chips) */
- #define SR_TB                 BIT(5)  /* Top/Bottom protect */
- #define SR_SRWD                       BIT(7)  /* SR write protect */
- /* Spansion/Cypress specific status bits */
diff --git a/target/linux/riscv64/patches/006-uart-sifive-serial-driver.patch b/target/linux/riscv64/patches/006-uart-sifive-serial-driver.patch
deleted file mode 100644 (file)
index 9779f9f..0000000
+++ /dev/null
@@ -1,1128 +0,0 @@
-From 6f1c41357974f377c4707a9b77125dd9cc9c2873 Mon Sep 17 00:00:00 2001
-From: "Wesley W. Terpstra" <[email protected]>
-Date: Fri, 16 Mar 2018 11:34:26 -0700
-Subject: [PATCH] serial/sifive: initial driver from Paul Walmsley
-
----
- drivers/tty/serial/Kconfig       |   23 +
- drivers/tty/serial/Makefile      |    1 +
- drivers/tty/serial/sifive.c      | 1051 ++++++++++++++++++++++++++++++
- include/uapi/linux/serial_core.h |    3 +
- 4 files changed, 1078 insertions(+)
- create mode 100644 drivers/tty/serial/sifive.c
-
-diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
-index df8bd0c7b97db..94a6de4b7dec4 100644
---- a/drivers/tty/serial/Kconfig
-+++ b/drivers/tty/serial/Kconfig
-@@ -1060,6 +1060,29 @@ config SERIAL_OMAP_CONSOLE
-         your boot loader about how to pass options to the kernel at
-         boot time.)
-+config SERIAL_SIFIVE
-+      tristate "SiFive UART support"
-+      depends on OF
-+      select SERIAL_CORE
-+      help
-+        If you have a SiFive Freedom U500 or similar SoC, enable this to
-+        support the SiFive UART.
-+
-+config SERIAL_SIFIVE_CONSOLE
-+      bool "Console on SiFive UART"
-+      depends on SERIAL_SIFIVE=y
-+      select SERIAL_CORE_CONSOLE
-+      help
-+        Select this option if you would like to use a SiFive UART as the
-+        system console.
-+
-+        Even if you say Y here, the currently visible virtual console
-+        (/dev/tty0) will still be used as the system console by default, but
-+        you can alter that using a kernel command line option such as
-+        "console=ttySIx". (Try "man bootparam" or see the documentation of
-+        your boot loader about how to pass options to the kernel at
-+        boot time.)
-+
- config SERIAL_LANTIQ
-       bool "Lantiq serial driver"
-       depends on LANTIQ
-diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
-index daac675612dff..7e906d3c04558 100644
---- a/drivers/tty/serial/Makefile
-+++ b/drivers/tty/serial/Makefile
-@@ -89,6 +89,7 @@ obj-$(CONFIG_SERIAL_MVEBU_UART)      += mvebu-uart.o
- obj-$(CONFIG_SERIAL_PIC32)    += pic32_uart.o
- obj-$(CONFIG_SERIAL_MPS2_UART)        += mps2-uart.o
- obj-$(CONFIG_SERIAL_OWL)      += owl-uart.o
-+obj-$(CONFIG_SERIAL_SIFIVE)   += sifive.o
- # GPIOLIB helpers for modem control lines
- obj-$(CONFIG_SERIAL_MCTRL_GPIO)       += serial_mctrl_gpio.o
-diff --git a/drivers/tty/serial/sifive.c b/drivers/tty/serial/sifive.c
-new file mode 100644
-index 0000000000000..588fb31cc94d0
---- /dev/null
-+++ b/drivers/tty/serial/sifive.c
-@@ -0,0 +1,1051 @@
-+// SPDX-License-Identifier: GPL-2.0+
-+/*
-+ * SiFive UART driver
-+ * Copyright (C) 2018 Paul Walmsley <[email protected]>
-+ *
-+ * Based partially on drivers/tty/serial/pxa.c, drivers/pwm/pwm-sifive.c,
-+ * and drivers/tty/serial/omap-serial.c
-+ *
-+ * See Chapter 19 "Universal Asynchronous Receiver/Transmitter (UART)" of
-+ * SiFive FE310-G000 v2p3.
-+ *
-+ * The SiFive UART design is not 8250-compatible.  The following common
-+ * features are not supported:
-+ * - Word lengths other than 8 bits
-+ * - Break handling
-+ * - Parity
-+ * - Flow control
-+ * - Modem signals (DSR, RI, etc.)
-+ * On the other hand, the design is free from the baggage of the classical 8250
-+ * programming model.
-+ *
-+ * 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.
-+ */
-+
-+/* XXX Magic SYSRQ support - is it possible to implement? */
-+/* XXX ignore_status_mask */
-+/* XXX Ensure operations are spinlocked that need to be spinlocked */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/console.h>
-+#include <linux/serial_reg.h>
-+#include <linux/delay.h>
-+#include <linux/slab.h>
-+#include <linux/tty.h>
-+#include <linux/tty_flip.h>
-+#include <linux/platform_device.h>
-+#include <linux/io.h>
-+#include <linux/clk.h>
-+#include <linux/serial_core.h>
-+#include <linux/irq.h>
-+#include <linux/of.h>
-+#include <linux/of_irq.h>
-+
-+/*
-+ * Register offsets
-+ */
-+
-+/* TXDATA */
-+#define SIFIVE_SERIAL_TXDATA_OFFS             0x0
-+#define SIFIVE_SERIAL_TXDATA_FULL_SHIFT               31
-+#define SIFIVE_SERIAL_TXDATA_FULL_MASK                (1 << SIFIVE_SERIAL_TXDATA_FULL_SHIFT)
-+#define SIFIVE_SERIAL_TXDATA_DATA_SHIFT               0
-+#define SIFIVE_SERIAL_TXDATA_DATA_MASK                (0xff << SIFIVE_SERIAL_TXDATA_DATA_SHIFT)
-+
-+/* RXDATA */
-+#define SIFIVE_SERIAL_RXDATA_OFFS             0x4
-+#define SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT      31
-+#define SIFIVE_SERIAL_RXDATA_EMPTY_MASK               (1 << SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT)
-+#define SIFIVE_SERIAL_RXDATA_DATA_SHIFT               0
-+#define SIFIVE_SERIAL_RXDATA_DATA_MASK                (0xff << SIFIVE_SERIAL_RXDATA_DATA_SHIFT)
-+
-+/* TXCTRL */
-+#define SIFIVE_SERIAL_TXCTRL_OFFS             0x8
-+#define SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT      16
-+#define SIFIVE_SERIAL_TXCTRL_TXCNT_MASK               (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT)
-+#define SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT      1
-+#define SIFIVE_SERIAL_TXCTRL_NSTOP_MASK               (1 << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT)
-+#define SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT               0
-+#define SIFIVE_SERIAL_TXCTRL_TXEN_MASK                (1 << SIFIVE_SERIAL_TXCTRL_TXEN_SHIFT)
-+
-+/* RXCTRL */
-+#define SIFIVE_SERIAL_RXCTRL_OFFS             0xC
-+#define SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT      16
-+#define SIFIVE_SERIAL_RXCTRL_RXCNT_MASK               (0x7 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT)
-+#define SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT               0
-+#define SIFIVE_SERIAL_RXCTRL_RXEN_MASK                (1 << SIFIVE_SERIAL_RXCTRL_RXEN_SHIFT)
-+
-+/* IE */
-+#define SIFIVE_SERIAL_IE_OFFS                 0x10
-+#define SIFIVE_SERIAL_IE_RXWM_SHIFT           1
-+#define SIFIVE_SERIAL_IE_RXWM_MASK            (1 << SIFIVE_SERIAL_IE_RXWM_SHIFT)
-+#define SIFIVE_SERIAL_IE_TXWM_SHIFT           0
-+#define SIFIVE_SERIAL_IE_TXWM_MASK            (1 << SIFIVE_SERIAL_IE_TXWM_SHIFT)
-+
-+/* IP */
-+#define SIFIVE_SERIAL_IP_OFFS                 0x14
-+#define SIFIVE_SERIAL_IP_RXWM_SHIFT           1
-+#define SIFIVE_SERIAL_IP_RXWM_MASK            (1 << SIFIVE_SERIAL_IP_RXWM_SHIFT)
-+#define SIFIVE_SERIAL_IP_TXWM_SHIFT           0
-+#define SIFIVE_SERIAL_IP_TXWM_MASK            (1 << SIFIVE_SERIAL_IP_TXWM_SHIFT)
-+
-+/* DIV */
-+#define SIFIVE_SERIAL_DIV_OFFS                        0x18
-+#define SIFIVE_SERIAL_DIV_DIV_SHIFT           0
-+#define SIFIVE_SERIAL_DIV_DIV_MASK            (0xffff << SIFIVE_SERIAL_IP_DIV_SHIFT)
-+
-+/*
-+ * Config macros
-+ */
-+
-+/* SIFIVE_SERIAL_MAX_PORTS: maximum number of UARTs possible on a device */
-+/* XXX Move to Kconfig? */
-+#define SIFIVE_SERIAL_MAX_PORTS                       10
-+
-+/* SIFIVE_SERIAL_NAME: our driver's name that we pass to the operating system */
-+#define SIFIVE_SERIAL_NAME                    "sifive-serial"
-+
-+/* SIFIVE_TTY_PREFIX: tty name prefix for SiFive serial ports */
-+#define SIFIVE_TTY_PREFIX                     "ttySI"
-+
-+/*
-+ *
-+ */
-+
-+/**
-+ * sifive_serial_port - driver-specific data extension to struct uart_port
-+ * @port: struct uart_port embedded in this struct
-+ * @dev: struct device *
-+ * @ier: shadowed copy of the interrupt enable register
-+ * @clkin_rate: input clock to the UART IP block.
-+ * @bit_rate: UART serial line rate (e.g., 115200 bps)
-+ * @clk_notifier: clock rate change notifier for upstream clock changes
-+ */
-+struct sifive_serial_port {
-+      struct uart_port        port;
-+      struct device           *dev;
-+      unsigned char           ier;
-+      unsigned long           clkin_rate;
-+      unsigned long           bit_rate;
-+      struct clk              *clk;
-+      struct notifier_block   clk_notifier;
-+};
-+
-+/*
-+ * Structure container-of macros
-+ */
-+
-+#define port_to_sifive_serial_port(p) (container_of((p), \
-+                                                  struct sifive_serial_port, \
-+                                                  port))
-+
-+#define notifier_to_sifive_serial_port(nb) (container_of((nb), \
-+                                                       struct sifive_serial_port, \
-+                                                       clk_notifier))
-+
-+/*
-+ * Forward declarations
-+ */
-+static void sifive_serial_stop_tx(struct uart_port *port);
-+
-+/*
-+ * Internal functions
-+ */
-+
-+/**
-+ * sifive_serial_early_write() - write to a UART register (early)
-+ * @port: pointer to a struct uart_port record
-+ * @offs: register address offset from the IP block base address
-+ * @v: value to write to the register
-+ *
-+ * Given a pointer @port to a struct uart_port record, write the value @v to the
-+ * IP block register address offset @offs.  This function is intended for early
-+ * console use.
-+ */
-+static void sifive_serial_early_write(struct uart_port *port, u16 offs, u32 v)
-+{
-+      writel(v, port->membase + offs);
-+}
-+
-+/**
-+ * sifive_serial_early_read() - read from a UART register (early)
-+ * @port: pointer to a struct uart_port record
-+ * @offs: register address offset from the IP block base address
-+ *
-+ * Given a pointer @port to a struct uart_port record, read the contents of the
-+ * IP block register located at offset @offs from the IP block base and return
-+ * it.  This function is intended for early console use.
-+ *
-+ * Returns: the register value read from the UART.
-+ */
-+static u32 sifive_serial_early_read(struct uart_port *port, u16 offs)
-+{
-+      return readl(port->membase + offs);
-+}
-+
-+/**
-+ * sifive_serial_write() - write to a UART register
-+ * @ssp: pointer to a struct sifive_serial_port record
-+ * @offs: register address offset from the IP block base address
-+ * @v: value to write to the register
-+ *
-+ * Write the value @v to the IP block register located at offset @offs from the
-+ * IP block base, given a pointer @ssp to a struct sifive_serial_port record.
-+ */
-+static void sifive_serial_write(struct sifive_serial_port *ssp, u16 offs, u32 v)
-+{
-+      sifive_serial_early_write(&ssp->port, offs, v);
-+}
-+
-+/**
-+ * sifive_serial_read() - read from a UART register
-+ * @ssp: pointer to a struct sifive_serial_port record
-+ * @offs: register address offset from the IP block base address
-+ *
-+ * Read the contents of the IP block register located at offset @offs from the
-+ * IP block base, given a pointer @ssp to a struct sifive_serial_port record.
-+ *
-+ * Returns: the value of the UART register
-+ */
-+static u32 sifive_serial_read(struct sifive_serial_port *ssp, u16 offs)
-+{
-+      return sifive_serial_early_read(&ssp->port, offs);
-+}
-+
-+/**
-+ * sifive_serial_is_txfifo_full() - is the TXFIFO full?
-+ * @ssp: pointer to a struct sifive_serial_port
-+ *
-+ * Read the transmit FIFO "full" bit, returning a non-zero value if the
-+ * TX FIFO is full, or zero if space remains.  Intended to be used to prevent
-+ * writes to the TX FIFO when it's full.
-+ *
-+ * Returns: SIFIVE_SERIAL_TXDATA_FULL_MASK (non-zero) if the transmit FIFO
-+ * is full, or 0 if space remains.
-+ */
-+static int sifive_serial_is_txfifo_full(struct sifive_serial_port *ssp)
-+{
-+      return sifive_serial_read(ssp, SIFIVE_SERIAL_TXDATA_OFFS) &
-+              SIFIVE_SERIAL_TXDATA_FULL_MASK;
-+}
-+
-+/**
-+ * sifive_serial_transmit_char() - enqueue a byte to transmit onto the TX FIFO
-+ * @ssp: pointer to a struct sifive_serial_port
-+ * @ch: character to transmit
-+ *
-+ * Enqueue a byte @ch onto the transmit FIFO, given a pointer @ssp to the
-+ * struct sifive_serial_port * to transmit on.  Caller should first check to
-+ * ensure that the TXFIFO has space; see sifive_serial_is_txfifo_full().
-+ */
-+static void sifive_serial_transmit_char(struct sifive_serial_port *ssp, int ch)
-+{
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_TXDATA_OFFS, ch);
-+}
-+
-+/**
-+ * sifive_serial_transmit_chars() - enqueue multiple bytes onto the TX FIFO
-+ * @ssp: pointer to a struct sifive_serial_port
-+ *
-+ * Transfer up to a TX FIFO size's worth of characters from the Linux serial
-+ * transmit buffer to the SiFive UART TX FIFO.
-+ */
-+static void sifive_serial_transmit_chars(struct sifive_serial_port *ssp)
-+{
-+      struct circ_buf *xmit = &ssp->port.state->xmit;
-+      int count;
-+
-+      if (ssp->port.x_char) {
-+              sifive_serial_transmit_char(ssp, ssp->port.x_char);
-+              ssp->port.icount.tx++;
-+              ssp->port.x_char = 0;
-+              return;
-+      }
-+      if (uart_circ_empty(xmit) || uart_tx_stopped(&ssp->port)) {
-+              sifive_serial_stop_tx(&ssp->port);
-+              return;
-+      }
-+      count = ssp->port.fifosize;
-+      do {
-+              sifive_serial_transmit_char(ssp, xmit->buf[xmit->tail]);
-+              xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
-+              ssp->port.icount.tx++;
-+              if (uart_circ_empty(xmit))
-+                      break;
-+      } while (--count > 0);
-+
-+      if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-+              uart_write_wakeup(&ssp->port);
-+
-+      if (uart_circ_empty(xmit))
-+              sifive_serial_stop_tx(&ssp->port);
-+}
-+
-+/**
-+ * sifive_serial_enable_txwm() - enable transmit watermark interrupts
-+ * @ssp: pointer to a struct sifive_serial_port
-+ *
-+ * Enable interrupt generation when the transmit FIFO watermark is reached
-+ * on the UART referred to by @ssp.
-+ */
-+static void sifive_serial_enable_txwm(struct sifive_serial_port *ssp)
-+{
-+      if (ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK)
-+              return;
-+
-+      ssp->ier |= SIFIVE_SERIAL_IE_TXWM_MASK;
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ssp->ier);
-+}
-+
-+/**
-+ * sifive_serial_enable_rxwm() - enable receive watermark interrupts
-+ * @ssp: pointer to a struct sifive_serial_port
-+ *
-+ * Enable interrupt generation when the receive FIFO watermark is reached
-+ * on the UART referred to by @ssp.
-+ */
-+static void sifive_serial_enable_rxwm(struct sifive_serial_port *ssp)
-+{
-+      if (ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK)
-+              return;
-+
-+      ssp->ier |= SIFIVE_SERIAL_IE_RXWM_MASK;
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ssp->ier);
-+}
-+
-+/**
-+ * sifive_serial_disable_txwm() - disable transmit watermark interrupts
-+ * @ssp: pointer to a struct sifive_serial_port
-+ *
-+ * Disable interrupt generation when the transmit FIFO watermark is reached
-+ * on the UART referred to by @ssp.
-+ */
-+static void sifive_serial_disable_txwm(struct sifive_serial_port *ssp)
-+{
-+      if (!(ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK))
-+              return;
-+
-+      ssp->ier &= ~SIFIVE_SERIAL_IE_TXWM_MASK;
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ssp->ier);
-+}
-+
-+/**
-+ * sifive_serial_disable_rxwm() - disable receive watermark interrupts
-+ * @ssp: pointer to a struct sifive_serial_port
-+ *
-+ * Disable interrupt generation when the receive FIFO watermark is reached
-+ * on the UART referred to by @ssp.
-+ */
-+static void sifive_serial_disable_rxwm(struct sifive_serial_port *ssp)
-+{
-+      if (!(ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK))
-+              return;
-+
-+      ssp->ier &= ~SIFIVE_SERIAL_IE_RXWM_MASK;
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ssp->ier);
-+}
-+
-+/**
-+ * sifive_serial_receive_char() - receive a byte from the UART
-+ * @ssp: pointer to a struct sifive_serial_port
-+ * @is_empty: char pointer to return whether the RX FIFO is empty
-+ *
-+ * Try to read a byte from the SiFive UART RX FIFO, referenced by
-+ * @ssp, and to return it.  Also returns the RX FIFO empty bit in
-+ * the char pointed to by @ch.  The caller must pass the byte back to the
-+ * Linux serial layer if needed.
-+ *
-+ * Returns: the byte read from the UART RX FIFO.
-+ */
-+static char sifive_serial_receive_char(struct sifive_serial_port *ssp,
-+                                     char *is_empty)
-+{
-+      u32 v;
-+      u8 ch;
-+
-+      v = sifive_serial_read(ssp, SIFIVE_SERIAL_RXDATA_OFFS);
-+
-+      if (!is_empty)
-+              WARN_ON(1);
-+      else
-+              *is_empty = (v & SIFIVE_SERIAL_RXDATA_EMPTY_MASK) >>
-+                      SIFIVE_SERIAL_RXDATA_EMPTY_SHIFT;
-+
-+      ch = (v & SIFIVE_SERIAL_RXDATA_DATA_MASK) >>
-+              SIFIVE_SERIAL_RXDATA_DATA_SHIFT;
-+
-+      return ch;
-+}
-+
-+/**
-+ * sifive_serial_receive_chars() - receive multiple bytes from the UART
-+ * @ssp: pointer to a struct sifive_serial_port
-+ *
-+ * Receive up to an RX FIFO's worth of bytes from the SiFive UART referred
-+ * to by @ssp and pass them up to the Linux serial layer.
-+ */
-+static void sifive_serial_receive_chars(struct sifive_serial_port *ssp)
-+{
-+      unsigned char ch;
-+      char is_empty;
-+      int c;
-+
-+      for (c = ssp->port.fifosize; c > 0; --c) {
-+              ch = sifive_serial_receive_char(ssp, &is_empty);
-+              if (is_empty) break;
-+
-+              ssp->port.icount.rx++;
-+              uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL);
-+      }
-+}
-+
-+/**
-+ * sifive_serial_update_div() - calculate the divisor setting by the line rate
-+ * @ssp: pointer to a struct sifive_serial_port
-+ *
-+ * Calculate the appropriate value of the clock divisor for the UART
-+ * referred to by @ssp and the target line rate referred to by @bps, and
-+ * return it.
-+ */
-+static void sifive_serial_update_div(struct sifive_serial_port *ssp)
-+{
-+      u16 div = DIV_ROUND_UP(ssp->clkin_rate, ssp->bit_rate) - 1;
-+      /* XXX check for div out of spec */
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_DIV_OFFS, div);
-+}
-+
-+/**
-+ * sifive_serial_update_bit_rate() - set the UART "baud rate"
-+ * @ssp: pointer to a struct sifive_serial_port
-+ * @rate: new target bit rate
-+ *
-+ * Calculate the UART divisor value for the target bit rate @rate for the
-+ * SiFive UART described by @ssp and program it into the UART.  There may
-+ * be some error between the target bit rate and the actual bit rate implemented
-+ * by the UART due to clock ratio granularity.
-+ */
-+static void sifive_serial_update_bit_rate(struct sifive_serial_port *ssp,
-+                                        unsigned int rate)
-+{
-+      if (ssp->bit_rate == rate)
-+              return;
-+
-+      ssp->bit_rate = rate;
-+      sifive_serial_update_div(ssp);
-+}
-+
-+/**
-+ * sifive_serial_set_stop_bits() - set the number of stop bits
-+ * @ssp: pointer to a struct sifive_serial_port
-+ * @nstop: 1 or 2 (stop bits)
-+ *
-+ * Program the SiFive UART referred to by @ssp to use @nstop stop bits.
-+ */
-+static void sifive_serial_set_stop_bits(struct sifive_serial_port *ssp,
-+                                      char nstop)
-+{
-+      u32 v;
-+
-+      if (nstop < 1 || nstop > 2) {
-+              WARN_ON(1);
-+              return;
-+      }
-+
-+      v = sifive_serial_read(ssp, SIFIVE_SERIAL_TXCTRL_OFFS);
-+      v &= ~SIFIVE_SERIAL_TXCTRL_NSTOP_MASK;
-+      v |= (nstop-1) << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT;
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_TXCTRL_OFFS, v);
-+}
-+
-+/**
-+ * sifive_serial_wait_for_xmitr() - wait for an empty slot on the TX FIFO
-+ * @ssp: pointer to a struct sifive_serial_port
-+ *
-+ * Delay while the UART TX FIFO referred to by @ssp is marked as full.
-+ *
-+ * XXX Probably should use a timeout/bailout.
-+ */
-+static inline void sifive_serial_wait_for_xmitr(struct sifive_serial_port *ssp)
-+{
-+      while (sifive_serial_is_txfifo_full(ssp))
-+              udelay(1); /* XXX Should vary by bps rate */
-+}
-+
-+/*
-+ * Linux serial API functions
-+ */
-+
-+static void sifive_serial_stop_tx(struct uart_port *port)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+
-+      sifive_serial_disable_txwm(ssp);
-+}
-+
-+static void sifive_serial_stop_rx(struct uart_port *port)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+
-+      sifive_serial_disable_rxwm(ssp);
-+}
-+
-+static void sifive_serial_start_tx(struct uart_port *port)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+
-+      sifive_serial_enable_txwm(ssp);
-+}
-+
-+static irqreturn_t sifive_serial_irq(int irq, void *dev_id)
-+{
-+      struct sifive_serial_port *ssp = dev_id;
-+      irqreturn_t r = IRQ_NONE;
-+      int c = ssp->port.fifosize;
-+      u32 ip;
-+
-+      spin_lock(&ssp->port.lock);
-+
-+      do {
-+              ip = sifive_serial_read(ssp, SIFIVE_SERIAL_IP_OFFS);
-+              if (!ip)
-+                      break;
-+
-+              r = IRQ_HANDLED;
-+
-+              if (ip & SIFIVE_SERIAL_IP_RXWM_MASK)
-+                      sifive_serial_receive_chars(ssp);
-+              if (ip & SIFIVE_SERIAL_IP_TXWM_MASK)
-+                      sifive_serial_transmit_chars(ssp);
-+      } while (c--);
-+
-+      spin_unlock(&ssp->port.lock);
-+
-+      tty_flip_buffer_push(&ssp->port.state->port);
-+
-+      return r;
-+}
-+
-+static unsigned int sifive_serial_tx_empty(struct uart_port *port)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+
-+      return !sifive_serial_is_txfifo_full(ssp);
-+}
-+
-+static unsigned int sifive_serial_get_mctrl(struct uart_port *port)
-+{
-+      return 0; /* XXX -EINVAL? */
-+}
-+
-+static void sifive_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
-+{
-+      // dev_err(port->dev, "set_mctrl not supported\n");
-+}
-+
-+static void sifive_serial_break_ctl(struct uart_port *port, int break_state)
-+{
-+      dev_err(port->dev, "sending break not supported\n");
-+}
-+
-+static int sifive_serial_startup(struct uart_port *port)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+
-+      sifive_serial_enable_rxwm(ssp);
-+
-+      return 0;
-+}
-+
-+static void sifive_serial_shutdown(struct uart_port *port)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+
-+      sifive_serial_disable_rxwm(ssp);
-+      sifive_serial_disable_txwm(ssp);
-+}
-+
-+/**
-+ * sifive_serial_clk_notifier() - clock post-rate-change notifier
-+ * @nb: pointer to the struct notifier_block, from the notifier code
-+ * @event: event mask from the notifier code
-+ * @data: pointer to the struct clk_notifier_data from the notifier code
-+ *
-+ * On the H5U SoC, the UART IP block is derived from the CPU clock source
-+ * after a synchronous divide-by-two divider, so any CPU clock rate change
-+ * requires the UART baud rate to be updated.  This presumably could corrupt any
-+ * serial word currently being transmitted or received.  It would probably
-+ * be better to stop receives and transmits, then complete the baud rate
-+ * change, then re-enable them.
-+ */
-+static int sifive_serial_clk_notifier(struct notifier_block *nb,
-+                                    unsigned long event, void *data)
-+{
-+      struct clk_notifier_data *cnd = data;
-+      struct sifive_serial_port *ssp = notifier_to_sifive_serial_port(nb);
-+
-+      if (event == POST_RATE_CHANGE && ssp->clkin_rate != cnd->new_rate) {
-+              ssp->clkin_rate = cnd->new_rate;
-+              sifive_serial_update_div(ssp);
-+      }
-+
-+      return NOTIFY_OK;
-+}
-+
-+static void sifive_serial_set_termios(struct uart_port *port,
-+                                    struct ktermios *termios,
-+                                    struct ktermios *old)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+      int rate;
-+      char nstop;
-+
-+      if ((termios->c_cflag & CSIZE) != CS8) {
-+              dev_err(ssp->port.dev, "only 8-bit words supported\n");
-+              return;
-+      }
-+
-+      /* Set number of stop bits */
-+      nstop = (termios->c_cflag & CSTOPB) ? 2 : 1;
-+      sifive_serial_set_stop_bits(ssp, nstop);
-+
-+      /* Set line rate */
-+      rate = uart_get_baud_rate(port, termios, old, 0, ssp->clkin_rate / 16);
-+      sifive_serial_update_bit_rate(ssp, rate);
-+
-+      /* XXX Enable FIFOs with watermark 1 */
-+
-+#if 0
-+      spin_lock_irqsave(&ssp->port.lock, flags);
-+#endif
-+
-+      /*
-+       * Update the per-port timeout.
-+       */
-+      uart_update_timeout(port, termios->c_cflag, rate);
-+
-+      /* XXX */
-+      ssp->port.read_status_mask = 0;
-+      if (termios->c_iflag & INPCK) {
-+              dev_err(ssp->port.dev, "INPCK flag not supported\n");
-+              return;
-+      }
-+      if (termios->c_iflag & (BRKINT | PARMRK)) {
-+              dev_err(ssp->port.dev, "BRKINT/PARMRK flag not supported\n");
-+              return;
-+      }
-+
-+#if 0
-+      /*
-+       * ignore all characters if CREAD is not set
-+       */
-+      if ((termios->c_cflag & CREAD) == 0)
-+              ssp->port.ignore_status_mask |= UART_LSR_DR;
-+#endif
-+
-+      /* XXX enable interrupts */
-+}
-+
-+static void sifive_serial_release_port(struct uart_port *port)
-+{
-+}
-+
-+static int sifive_serial_request_port(struct uart_port *port)
-+{
-+      return 0;
-+}
-+
-+static void sifive_serial_config_port(struct uart_port *port, int flags)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+
-+      ssp->port.type = PORT_SIFIVE_H5U;
-+}
-+
-+static int sifive_serial_verify_port(struct uart_port *port,
-+                                   struct serial_struct *ser)
-+{
-+      return -EINVAL;
-+}
-+
-+static const char *sifive_serial_type(struct uart_port *port)
-+{
-+      return port->type == PORT_SIFIVE_H5U ? SIFIVE_SERIAL_NAME : NULL;
-+}
-+
-+/*
-+ * Polling support
-+ */
-+
-+#ifdef CONFIG_CONSOLE_POLL
-+
-+static void sifive_serial_poll_put_char(struct uart_port *port,
-+                                      unsigned char ch)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+
-+      sifive_serial_wait_for_xmitr(ssp);
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_TXDATA_OFFS, ch);
-+}
-+
-+static int sifive_serial_poll_get_char(struct uart_port *port)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+      char is_empty, ch;
-+
-+      ch = sifive_serial_receive_char(ssp, &is_empty);
-+      if (is_empty)
-+              return NO_POLL_CHAR;
-+
-+      return ch;
-+}
-+
-+#endif /* CONFIG_CONSOLE_POLL */
-+
-+/*
-+ * Earlyconsole support
-+ */
-+
-+#ifdef CONFIG_SERIAL_EARLYCON
-+static void early_sifive_serial_putc(struct uart_port *port, int c)
-+{
-+      while (sifive_serial_early_read(port, SIFIVE_SERIAL_TXDATA_OFFS) &
-+             SIFIVE_SERIAL_TXDATA_FULL_MASK)
-+              cpu_relax();
-+
-+      sifive_serial_early_write(port, SIFIVE_SERIAL_TXDATA_OFFS, c);
-+}
-+
-+void early_sifive_serial_write(struct console *console, const char *s,
-+                             unsigned int count)
-+{
-+      struct earlycon_device *device = console->data;
-+      struct uart_port *port = &device->port;
-+
-+      uart_console_write(port, s, count, early_sifive_serial_putc);
-+}
-+
-+static int __init early_sifive_serial_setup(struct earlycon_device *device,
-+                                          const char *options)
-+{
-+      struct uart_port *port = &device->port;
-+
-+      if (!(port->membase || port->iobase))
-+              return -ENODEV;
-+
-+      device->con->write = early_sifive_serial_write;
-+      return 0;
-+}
-+
-+OF_EARLYCON_DECLARE(sifive, "sifive,freedom-uart", early_sifive_serial_setup);
-+#endif /* CONFIG_SERIAL_EARLYCON */
-+
-+/*
-+ * Linux console interface
-+ */
-+
-+#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE
-+
-+static struct sifive_serial_port *sifive_serial_console_ports[SIFIVE_SERIAL_MAX_PORTS];
-+
-+static void sifive_serial_console_putchar(struct uart_port *port, int ch)
-+{
-+      struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
-+
-+      sifive_serial_wait_for_xmitr(ssp);
-+      sifive_serial_transmit_char(ssp, ch);
-+}
-+
-+static void sifive_serial_console_write(struct console *co, const char *s,
-+                                      unsigned int count)
-+{
-+      struct sifive_serial_port *ssp = sifive_serial_console_ports[co->index];
-+      unsigned long flags;
-+      unsigned int ier;
-+      int locked = 1;
-+
-+      if (!ssp) return;
-+
-+      local_irq_save(flags);
-+      if (ssp->port.sysrq)
-+              locked = 0;
-+      else if (oops_in_progress)
-+              locked = spin_trylock(&ssp->port.lock);
-+      else
-+              spin_lock(&ssp->port.lock);
-+
-+      ier = sifive_serial_read(ssp, SIFIVE_SERIAL_IE_OFFS);
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, 0);
-+
-+      uart_console_write(&ssp->port, s, count, sifive_serial_console_putchar);
-+
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_IE_OFFS, ier);
-+
-+      if (locked)
-+              spin_unlock(&ssp->port.lock);
-+      local_irq_restore(flags);
-+}
-+
-+static int __init sifive_serial_console_setup(struct console *co, char *options)
-+{
-+      struct sifive_serial_port *ssp;
-+      int baud = 115200;
-+      int bits = 8;
-+      int parity = 'n';
-+      int flow = 'n';
-+
-+      ssp = sifive_serial_console_ports[co->index];
-+      if (!ssp)
-+              return -ENODEV;
-+
-+      if (options)
-+              uart_parse_options(options, &baud, &parity, &bits, &flow);
-+
-+      return uart_set_options(&ssp->port, co, baud, parity, bits, flow);
-+}
-+
-+static struct uart_driver sifive_serial_uart_driver;
-+
-+static struct console sifive_serial_console = {
-+      .name           = SIFIVE_TTY_PREFIX,
-+      .write          = sifive_serial_console_write,
-+      .device         = uart_console_device,
-+      .setup          = sifive_serial_console_setup,
-+      .flags          = CON_PRINTBUFFER,
-+      .index          = -1,
-+      .data           = &sifive_serial_uart_driver,
-+};
-+
-+static void sifive_serial_add_console_port(struct sifive_serial_port *ssp)
-+{
-+      sifive_serial_console_ports[ssp->port.line] = ssp;
-+}
-+
-+static void sifive_serial_remove_console_port(struct sifive_serial_port *ssp)
-+{
-+      sifive_serial_console_ports[ssp->port.line] = 0;
-+}
-+
-+#define SIFIVE_SERIAL_CONSOLE (&sifive_serial_console)
-+
-+#else
-+
-+#define SIFIVE_SERIAL_CONSOLE NULL
-+
-+static inline void sifive_serial_add_console_port(struct sifive_serial_port *ssp)
-+{}
-+static void sifive_serial_remove_console_port(struct sifive_serial_port *ssp)
-+{}
-+
-+#endif
-+
-+static const struct uart_ops sifive_serial_uops = {
-+      .tx_empty       = sifive_serial_tx_empty,
-+      .set_mctrl      = sifive_serial_set_mctrl,
-+      .get_mctrl      = sifive_serial_get_mctrl,
-+      .stop_tx        = sifive_serial_stop_tx,
-+      .start_tx       = sifive_serial_start_tx,
-+      .stop_rx        = sifive_serial_stop_rx,
-+      .break_ctl      = sifive_serial_break_ctl,
-+      .startup        = sifive_serial_startup,
-+      .shutdown       = sifive_serial_shutdown,
-+      .set_termios    = sifive_serial_set_termios,
-+      .type           = sifive_serial_type,
-+      .release_port   = sifive_serial_release_port,
-+      .request_port   = sifive_serial_request_port,
-+      .config_port    = sifive_serial_config_port,
-+      .verify_port    = sifive_serial_verify_port,
-+#ifdef CONFIG_CONSOLE_POLL
-+      .poll_put_char  = sifive_serial_poll_put_char,
-+      .poll_get_char  = sifive_serial_poll_get_char,
-+#endif
-+};
-+
-+static struct uart_driver sifive_serial_uart_driver = {
-+      .owner          = THIS_MODULE,
-+      .driver_name    = SIFIVE_SERIAL_NAME,
-+      .dev_name       = "ttySI",
-+      .nr             = SIFIVE_SERIAL_MAX_PORTS,
-+      .cons           = SIFIVE_SERIAL_CONSOLE,
-+};
-+
-+static int sifive_serial_probe(struct platform_device *pdev)
-+{
-+      struct sifive_serial_port *ssp;
-+      struct resource *mem;
-+      struct clk *clk;
-+      void __iomem *base;
-+      int irq, id, r;
-+
-+      irq = platform_get_irq(pdev, 0);
-+      if (irq < 0) {
-+              dev_err(&pdev->dev, "could not acquire interrupt\n");
-+              return -EPROBE_DEFER;
-+      }
-+
-+      mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+      base = devm_ioremap_resource(&pdev->dev, mem);
-+      if (IS_ERR(base)) {
-+              dev_err(&pdev->dev, "could not acquire device memory\n");
-+              return PTR_ERR(base);
-+      }
-+
-+      clk = devm_clk_get(&pdev->dev, NULL);
-+      if (IS_ERR(clk)) {
-+              dev_err(&pdev->dev, "unable to find controller clock\n");
-+              return PTR_ERR(clk);
-+      }
-+
-+      id = of_alias_get_id(pdev->dev.of_node, "serial");
-+      if (id < 0) {
-+              dev_err(&pdev->dev, "missing aliases entry\n");
-+              return id;
-+      }
-+
-+#ifdef CONFIG_SERIAL_SIFIVE_CONSOLE
-+      if (id > SIFIVE_SERIAL_MAX_PORTS) {
-+              dev_err(&pdev->dev, "too many UARTs (%d)\n", id);
-+              return -EINVAL;
-+      }
-+#endif
-+
-+      ssp = devm_kzalloc(&pdev->dev, sizeof(*ssp), GFP_KERNEL);
-+      if (!ssp)
-+              return -ENOMEM;
-+
-+      ssp->port.dev = &pdev->dev;
-+      ssp->port.type = PORT_SIFIVE_H5U;
-+      ssp->port.iotype = UPIO_MEM;
-+      ssp->port.irq = irq;
-+      ssp->port.fifosize = 8;
-+      ssp->port.ops = &sifive_serial_uops;
-+      ssp->port.line = id;
-+      ssp->port.mapbase = mem->start;
-+      ssp->port.membase = base;
-+      ssp->dev = &pdev->dev;
-+      ssp->clk = clk;
-+      ssp->clk_notifier.notifier_call = sifive_serial_clk_notifier;
-+
-+      r = clk_notifier_register(ssp->clk, &ssp->clk_notifier);
-+      if (r) {
-+              dev_err(&pdev->dev, "could not register clock notifier: %d\n",
-+                      r);
-+              goto probe_out1;
-+      }
-+
-+      /* Setup clock divider */
-+      ssp->clkin_rate = clk_get_rate(ssp->clk);
-+      ssp->bit_rate = 115200;
-+      sifive_serial_update_div(ssp);
-+
-+      platform_set_drvdata(pdev, ssp);
-+
-+      /* Enable transmits and set the watermark level to 1 */
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_TXCTRL_OFFS,
-+                          (1 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT) |
-+                          SIFIVE_SERIAL_TXCTRL_TXEN_MASK);
-+
-+      /* Enable receives and set the watermark level to 0 */
-+      sifive_serial_write(ssp, SIFIVE_SERIAL_RXCTRL_OFFS,
-+                          (0 << SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT) |
-+                          SIFIVE_SERIAL_RXCTRL_RXEN_MASK);
-+
-+      r = request_irq(ssp->port.irq, sifive_serial_irq, ssp->port.irqflags,
-+                      dev_name(&pdev->dev), ssp);
-+      if (r) {
-+              dev_err(&pdev->dev, "could not attach interrupt: %d\n", r);
-+              goto probe_out2;
-+      }
-+
-+      r = uart_add_one_port(&sifive_serial_uart_driver, &ssp->port);
-+      if (r != 0) {
-+              dev_err(&pdev->dev, "could not add uart: %d\n", r);
-+              goto probe_out3;
-+      }
-+
-+      sifive_serial_add_console_port(ssp);
-+
-+      return 0;
-+
-+probe_out3:
-+      free_irq(ssp->port.irq, ssp);
-+probe_out2:
-+      clk_notifier_unregister(ssp->clk, &ssp->clk_notifier);
-+probe_out1:
-+      return r;
-+}
-+
-+static int sifive_serial_remove(struct platform_device *dev)
-+{
-+      struct sifive_serial_port *ssp = platform_get_drvdata(dev);
-+
-+      sifive_serial_remove_console_port(ssp);
-+      uart_remove_one_port(&sifive_serial_uart_driver, &ssp->port);
-+      free_irq(ssp->port.irq, ssp);
-+      clk_notifier_unregister(ssp->clk, &ssp->clk_notifier);
-+
-+      return 0;
-+}
-+
-+static const struct of_device_id sifive_serial_of_match[] = {
-+      { .compatible = "sifive,uart0" },
-+      {},
-+};
-+MODULE_DEVICE_TABLE(of, sifive_serial_match);
-+
-+static struct platform_driver sifive_serial_platform_driver = {
-+      .probe          = sifive_serial_probe,
-+      .remove         = sifive_serial_remove,
-+      .driver         = {
-+              .name   = SIFIVE_SERIAL_NAME,
-+              .of_match_table = of_match_ptr(sifive_serial_of_match),
-+      },
-+};
-+
-+static int __init sifive_serial_init(void)
-+{
-+      struct tty_driver *tty_drv;
-+      int r;
-+
-+      r = uart_register_driver(&sifive_serial_uart_driver);
-+      if (r) goto init_out1;
-+
-+      tty_drv = sifive_serial_uart_driver.tty_driver;
-+      if (!tty_drv) goto init_out2;
-+
-+      /* change default terminal settings for SiFive uarts */
-+      tty_drv->init_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL;
-+      tty_drv->init_termios.c_ispeed = 115200;
-+      tty_drv->init_termios.c_ospeed = 115200;
-+
-+      r = platform_driver_register(&sifive_serial_platform_driver);
-+      if (r) goto init_out2;
-+
-+      return 0;
-+
-+init_out2:
-+      uart_unregister_driver(&sifive_serial_uart_driver);
-+init_out1:
-+      return r;
-+}
-+
-+static void __exit sifive_serial_exit(void)
-+{
-+      platform_driver_unregister(&sifive_serial_platform_driver);
-+      uart_unregister_driver(&sifive_serial_uart_driver);
-+}
-+
-+module_init(sifive_serial_init);
-+module_exit(sifive_serial_exit);
-+
-+MODULE_DESCRIPTION("SiFive UART serial driver");
-+MODULE_LICENSE("GPL");
-+MODULE_AUTHOR("Paul Walmsley <[email protected]>");
-diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
-index dce5f9dae1210..86973c3854145 100644
---- a/include/uapi/linux/serial_core.h
-+++ b/include/uapi/linux/serial_core.h
-@@ -281,4 +281,7 @@
- /* MediaTek BTIF */
- #define PORT_MTK_BTIF 117
-+/* SiFive UART */
-+#define PORT_SIFIVE_H5U       118
-+
- #endif /* _UAPILINUX_SERIAL_CORE_H */
diff --git a/target/linux/riscv64/patches/007-gpio-sifive-support-GPIO-on-SiFive-SoCs.patch b/target/linux/riscv64/patches/007-gpio-sifive-support-GPIO-on-SiFive-SoCs.patch
deleted file mode 100644 (file)
index ce41ece..0000000
+++ /dev/null
@@ -1,409 +0,0 @@
-From 28447771a2dddf9c083e1eefa5848d03e83496c7 Mon Sep 17 00:00:00 2001
-From: "Wesley W. Terpstra" <[email protected]>
-Date: Wed, 21 Feb 2018 15:43:02 -0800
-Subject: [PATCH 07/11] gpio-sifive: support GPIO on SiFive SoCs
-
----
- .../devicetree/bindings/gpio/gpio-sifive.txt       |  28 ++
- drivers/gpio/Kconfig                               |   7 +
- drivers/gpio/Makefile                              |   1 +
- drivers/gpio/gpio-sifive.c                         | 322 +++++++++++++++++++++
- 4 files changed, 358 insertions(+)
- create mode 100644 Documentation/devicetree/bindings/gpio/gpio-sifive.txt
- create mode 100644 drivers/gpio/gpio-sifive.c
-
-diff --git a/Documentation/devicetree/bindings/gpio/gpio-sifive.txt b/Documentation/devicetree/bindings/gpio/gpio-sifive.txt
-new file mode 100644
-index 00000000..bf41eed8
---- /dev/null
-+++ b/Documentation/devicetree/bindings/gpio/gpio-sifive.txt
-@@ -0,0 +1,28 @@
-+SiFive GPIO controller bindings
-+
-+Required properties:
-+- compatible:
-+  - "sifive,gpio0"
-+- reg: Physical base address and length of the controller's registers.
-+- #gpio-cells : Should be 2
-+  - The first cell is the gpio offset number.
-+  - The second cell indicates the polarity of the GPIO
-+- gpio-controller : Marks the device node as a GPIO controller.
-+- interrupt-controller: Mark the device node as an interrupt controller
-+- #interrupt-cells : Should be 2.
-+  - The first cell is the GPIO offset number within the GPIO controller.
-+  - The second cell is the edge/level to use for interrupt generation.
-+- interrupts: Specify the interrupts, one per GPIO
-+
-+Example:
-+
-+gpio: gpio@10060000 {
-+      compatible = "sifive,gpio0";
-+      interrupt-parent = <&plic>;
-+      interrupts = <7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22>;
-+      reg = <0x0 0x10060000 0x0 0x1000>;
-+      gpio-controller;
-+      #gpio-cells = <2>;
-+      interrupt-controller;
-+      #interrupt-cells = <2>;
-+};
-diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
-index 4f52c3a8..7755f49e 100644
---- a/drivers/gpio/Kconfig
-+++ b/drivers/gpio/Kconfig
-@@ -439,6 +439,13 @@ config GPIO_REG
-         A 32-bit single register GPIO fixed in/out implementation.  This
-         can be used to represent any register as a set of GPIO signals.
-+config GPIO_SIFIVE
-+      bool "SiFive GPIO support"
-+      depends on OF_GPIO
-+      select GPIOLIB_IRQCHIP
-+      help
-+        Say yes here to support the GPIO device on SiFive SoCs.
-+
- config GPIO_SPEAR_SPICS
-       bool "ST SPEAr13xx SPI Chip Select as GPIO support"
-       depends on PLAT_SPEAR
-diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
-index c256aff6..244a3696 100644
---- a/drivers/gpio/Makefile
-+++ b/drivers/gpio/Makefile
-@@ -111,6 +111,7 @@ obj-$(CONFIG_GPIO_REG)             += gpio-reg.o
- obj-$(CONFIG_ARCH_SA1100)     += gpio-sa1100.o
- obj-$(CONFIG_GPIO_SCH)                += gpio-sch.o
- obj-$(CONFIG_GPIO_SCH311X)    += gpio-sch311x.o
-+obj-$(CONFIG_GPIO_SIFIVE)     += gpio-sifive.o
- obj-$(CONFIG_GPIO_SODAVILLE)  += gpio-sodaville.o
- obj-$(CONFIG_GPIO_SPEAR_SPICS)        += gpio-spear-spics.o
- obj-$(CONFIG_GPIO_SPRD)               += gpio-sprd.o
-diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
-new file mode 100644
-index 00000000..6482ebbc
---- /dev/null
-+++ b/drivers/gpio/gpio-sifive.c
-@@ -0,0 +1,322 @@
-+/*
-+ * SiFive GPIO driver
-+ *
-+ * Copyright (C) 2018 SiFive, Inc.
-+ *
-+ * 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 <linux/bitops.h>
-+#include <linux/device.h>
-+#include <linux/errno.h>
-+#include <linux/of_irq.h>
-+#include <linux/gpio/driver.h>
-+#include <linux/irqchip/chained_irq.h>
-+#include <linux/init.h>
-+#include <linux/of.h>
-+#include <linux/pinctrl/consumer.h>
-+#include <linux/platform_device.h>
-+#include <linux/pm.h>
-+#include <linux/slab.h>
-+#include <linux/spinlock.h>
-+
-+#define GPIO_INPUT_VAL        0x00
-+#define GPIO_INPUT_EN 0x04
-+#define GPIO_OUTPUT_EN        0x08
-+#define GPIO_OUTPUT_VAL       0x0C
-+#define GPIO_RISE_IE  0x18
-+#define GPIO_RISE_IP  0x1C
-+#define GPIO_FALL_IE  0x20
-+#define GPIO_FALL_IP  0x24
-+#define GPIO_HIGH_IE  0x28
-+#define GPIO_HIGH_IP  0x2C
-+#define GPIO_LOW_IE   0x30
-+#define GPIO_LOW_IP   0x34
-+#define GPIO_OUTPUT_XOR       0x40
-+
-+#define MAX_GPIO      32
-+
-+struct sifive_gpio {
-+      raw_spinlock_t          lock;
-+      void __iomem            *base;
-+      struct gpio_chip        gc;
-+      unsigned long           enabled;
-+      unsigned                trigger[MAX_GPIO];
-+      unsigned int            irq_parent[MAX_GPIO];
-+      struct sifive_gpio      *self_ptr[MAX_GPIO];
-+};
-+
-+static void sifive_assign_bit(void __iomem *ptr, int offset, int value)
-+{
-+      // It's frustrating that we are not allowed to use the device atomics
-+      // which are GUARANTEED to be supported by this device on RISC-V
-+      u32 bit = BIT(offset), old = ioread32(ptr);
-+      if (value)
-+              iowrite32(old | bit, ptr);
-+      else
-+              iowrite32(old & ~bit, ptr);
-+}
-+
-+static int sifive_direction_input(struct gpio_chip *gc, unsigned offset)
-+{
-+      struct sifive_gpio *chip = gpiochip_get_data(gc);
-+      unsigned long flags;
-+
-+      if (offset >= gc->ngpio)
-+              return -EINVAL;
-+
-+      raw_spin_lock_irqsave(&chip->lock, flags);
-+      sifive_assign_bit(chip->base + GPIO_OUTPUT_EN, offset, 0);
-+      sifive_assign_bit(chip->base + GPIO_INPUT_EN,  offset, 1);
-+      raw_spin_unlock_irqrestore(&chip->lock, flags);
-+
-+      return 0;
-+}
-+
-+static int sifive_direction_output(struct gpio_chip *gc, unsigned offset, int value)
-+{
-+      struct sifive_gpio *chip = gpiochip_get_data(gc);
-+      unsigned long flags;
-+
-+      if (offset >= gc->ngpio)
-+              return -EINVAL;
-+
-+      raw_spin_lock_irqsave(&chip->lock, flags);
-+      sifive_assign_bit(chip->base + GPIO_INPUT_EN,   offset, 0);
-+      sifive_assign_bit(chip->base + GPIO_OUTPUT_VAL, offset, value);
-+      sifive_assign_bit(chip->base + GPIO_OUTPUT_EN,  offset, 1);
-+      raw_spin_unlock_irqrestore(&chip->lock, flags);
-+
-+      return 0;
-+}
-+
-+static int sifive_get_direction(struct gpio_chip *gc, unsigned offset)
-+{
-+      struct sifive_gpio *chip = gpiochip_get_data(gc);
-+
-+      if (offset >= gc->ngpio)
-+              return -EINVAL;
-+
-+      return !(ioread32(chip->base + GPIO_OUTPUT_EN) & BIT(offset));
-+}
-+
-+static int sifive_get_value(struct gpio_chip *gc, unsigned offset)
-+{
-+      struct sifive_gpio *chip = gpiochip_get_data(gc);
-+
-+      if (offset >= gc->ngpio)
-+              return -EINVAL;
-+
-+      return !!(ioread32(chip->base + GPIO_INPUT_VAL) & BIT(offset));
-+}
-+
-+static void sifive_set_value(struct gpio_chip *gc, unsigned offset, int value)
-+{
-+      struct sifive_gpio *chip = gpiochip_get_data(gc);
-+      unsigned long flags;
-+
-+      if (offset >= gc->ngpio)
-+              return;
-+
-+      raw_spin_lock_irqsave(&chip->lock, flags);
-+      sifive_assign_bit(chip->base + GPIO_OUTPUT_VAL, offset, value);
-+      raw_spin_unlock_irqrestore(&chip->lock, flags);
-+}
-+
-+static void sifive_set_ie(struct sifive_gpio *chip, int offset)
-+{
-+      unsigned long flags;
-+      unsigned trigger;
-+
-+      raw_spin_lock_irqsave(&chip->lock, flags);
-+      trigger = (chip->enabled & BIT(offset)) ? chip->trigger[offset] : 0;
-+      sifive_assign_bit(chip->base + GPIO_RISE_IE, offset, trigger & IRQ_TYPE_EDGE_RISING);
-+      sifive_assign_bit(chip->base + GPIO_FALL_IE, offset, trigger & IRQ_TYPE_EDGE_FALLING);
-+      sifive_assign_bit(chip->base + GPIO_HIGH_IE, offset, trigger & IRQ_TYPE_LEVEL_HIGH);
-+      sifive_assign_bit(chip->base + GPIO_LOW_IE,  offset, trigger & IRQ_TYPE_LEVEL_LOW);
-+      raw_spin_unlock_irqrestore(&chip->lock, flags);
-+}
-+
-+static int sifive_irq_set_type(struct irq_data *d, unsigned trigger)
-+{
-+      struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
-+      struct sifive_gpio *chip = gpiochip_get_data(gc);
-+      int offset = irqd_to_hwirq(d);
-+
-+      if (offset < 0 || offset >= gc->ngpio)
-+              return -EINVAL;
-+
-+      chip->trigger[offset] = trigger;
-+      sifive_set_ie(chip, offset);
-+      return 0;
-+}
-+
-+/* chained_irq_{enter,exit} already mask the parent */
-+static void sifive_irq_mask(struct irq_data *d) { }
-+static void sifive_irq_unmask(struct irq_data *d) { }
-+
-+static void sifive_irq_enable(struct irq_data *d)
-+{
-+      struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
-+      struct sifive_gpio *chip = gpiochip_get_data(gc);
-+      int offset = irqd_to_hwirq(d) % MAX_GPIO; // must not fail
-+      u32 bit = BIT(offset);
-+
-+      /* Switch to input */
-+      sifive_direction_input(gc, offset);
-+
-+      /* Clear any sticky pending interrupts */
-+      iowrite32(bit, chip->base + GPIO_RISE_IP);
-+      iowrite32(bit, chip->base + GPIO_FALL_IP);
-+      iowrite32(bit, chip->base + GPIO_HIGH_IP);
-+      iowrite32(bit, chip->base + GPIO_LOW_IP);
-+
-+      /* Enable interrupts */
-+      assign_bit(offset, &chip->enabled, 1);
-+      sifive_set_ie(chip, offset);
-+}
-+
-+static void sifive_irq_disable(struct irq_data *d)
-+{
-+      struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
-+      struct sifive_gpio *chip = gpiochip_get_data(gc);
-+      int offset = irqd_to_hwirq(d) % MAX_GPIO; // must not fail
-+
-+      assign_bit(offset, &chip->enabled, 0);
-+      sifive_set_ie(chip, offset);
-+}
-+
-+static struct irq_chip sifive_irqchip = {
-+      .name           = "sifive-gpio",
-+      .irq_set_type   = sifive_irq_set_type,
-+      .irq_mask       = sifive_irq_mask,
-+      .irq_unmask     = sifive_irq_unmask,
-+      .irq_enable     = sifive_irq_enable,
-+      .irq_disable    = sifive_irq_disable,
-+};
-+
-+static void sifive_irq_handler(struct irq_desc *desc)
-+{
-+      struct irq_chip *irqchip = irq_desc_get_chip(desc);
-+      struct sifive_gpio **self_ptr = irq_desc_get_handler_data(desc);
-+      struct sifive_gpio *chip = *self_ptr;
-+      int offset = self_ptr - &chip->self_ptr[0];
-+      u32 bit = BIT(offset);
-+
-+      chained_irq_enter(irqchip, desc);
-+
-+      /* Re-arm the edge triggers so don't miss the next one */
-+      iowrite32(bit, chip->base + GPIO_RISE_IP);
-+      iowrite32(bit, chip->base + GPIO_FALL_IP);
-+
-+      generic_handle_irq(irq_find_mapping(chip->gc.irq.domain, offset));
-+
-+      /* Re-arm the level triggers after handling to prevent spurious refire */
-+      iowrite32(bit, chip->base + GPIO_HIGH_IP);
-+      iowrite32(bit, chip->base + GPIO_LOW_IP);
-+
-+      chained_irq_exit(irqchip, desc);
-+}
-+
-+static int sifive_gpio_probe(struct platform_device *pdev)
-+{
-+      struct device *dev = &pdev->dev;
-+      struct device_node *node = pdev->dev.of_node;
-+      struct sifive_gpio *chip;
-+      struct resource *res;
-+      int gpio, irq, ret, ngpio;
-+
-+      chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
-+      if (!chip) {
-+              dev_err(dev, "out of memory\n");
-+              return -ENOMEM;
-+      }
-+
-+      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-+      chip->base = devm_ioremap_resource(dev, res);
-+      if (IS_ERR(chip->base)) {
-+              dev_err(dev, "failed to allocate device memory\n");
-+              return PTR_ERR(chip->base);
-+      }
-+
-+      ngpio = of_irq_count(node);
-+      if (ngpio >= MAX_GPIO) {
-+              dev_err(dev, "too many interrupts\n");
-+              return -EINVAL;
-+      }
-+
-+      raw_spin_lock_init(&chip->lock);
-+      chip->gc.direction_input = sifive_direction_input;
-+      chip->gc.direction_output = sifive_direction_output;
-+      chip->gc.get_direction = sifive_get_direction;
-+      chip->gc.get = sifive_get_value;
-+      chip->gc.set = sifive_set_value;
-+      chip->gc.base = -1;
-+      chip->gc.ngpio = ngpio;
-+      chip->gc.label = dev_name(dev);
-+      chip->gc.parent = dev;
-+      chip->gc.owner = THIS_MODULE;
-+
-+      ret = gpiochip_add_data(&chip->gc, chip);
-+      if (ret)
-+              return ret;
-+
-+      /* Disable all GPIO interrupts before enabling parent interrupts */
-+      iowrite32(0, chip->base + GPIO_RISE_IE);
-+      iowrite32(0, chip->base + GPIO_FALL_IE);
-+      iowrite32(0, chip->base + GPIO_HIGH_IE);
-+      iowrite32(0, chip->base + GPIO_LOW_IE);
-+      chip->enabled = 0;
-+
-+      ret = gpiochip_irqchip_add(&chip->gc, &sifive_irqchip, 0, handle_simple_irq, IRQ_TYPE_NONE);
-+      if (ret) {
-+              dev_err(dev, "could not add irqchip\n");
-+              gpiochip_remove(&chip->gc);
-+              return ret;
-+      }
-+
-+      chip->gc.irq.num_parents = ngpio;
-+      chip->gc.irq.parents = &chip->irq_parent[0];
-+      chip->gc.irq.map = &chip->irq_parent[0];
-+
-+      for (gpio = 0; gpio < ngpio; ++gpio) {
-+              irq = platform_get_irq(pdev, gpio);
-+              if (irq < 0) {
-+                      dev_err(dev, "invalid IRQ\n");
-+                      gpiochip_remove(&chip->gc);
-+                      return -ENODEV;
-+              }
-+
-+              chip->irq_parent[gpio] = irq;
-+              chip->self_ptr[gpio] = chip;
-+              chip->trigger[gpio] = IRQ_TYPE_LEVEL_HIGH;
-+      }
-+
-+      for (gpio = 0; gpio < ngpio; ++gpio) {
-+              irq = chip->irq_parent[gpio];
-+              irq_set_chained_handler_and_data(irq, sifive_irq_handler, &chip->self_ptr[gpio]);
-+              irq_set_parent(irq_find_mapping(chip->gc.irq.domain, gpio), irq);
-+      }
-+
-+      platform_set_drvdata(pdev, chip);
-+      dev_info(dev, "SiFive GPIO chip registered %d GPIOs\n", ngpio);
-+
-+      return 0;
-+}
-+
-+static const struct of_device_id sifive_gpio_match[] = {
-+      {
-+              .compatible = "sifive,gpio0",
-+      },
-+      { },
-+};
-+
-+static struct platform_driver sifive_gpio_driver = {
-+      .probe          = sifive_gpio_probe,
-+      .driver = {
-+              .name   = "sifive_gpio",
-+              .of_match_table = of_match_ptr(sifive_gpio_match),
-+      },
-+};
-+builtin_platform_driver(sifive_gpio_driver)
--- 
-2.7.4
-