riscv64: backport various patches
authorZoltan HERPAI <wigyori@uid0.hu>
Tue, 16 Oct 2018 14:25:29 +0000 (16:25 +0200)
committerZoltan HERPAI <wigyori@uid0.hu>
Tue, 16 Oct 2018 14:25:29 +0000 (16:25 +0200)
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
target/linux/riscv64/config-4.19
target/linux/riscv64/patches/0009-RISC-V-Networking-fix-Hack.patch [new file with mode: 0644]
target/linux/riscv64/patches/0010-pcie-microsemi-added-support-for-the-Vera-board-root.patch [new file with mode: 0644]
target/linux/riscv64/patches/002-clk-sifive-prci.patch [new file with mode: 0644]
target/linux/riscv64/patches/003-clk-gemgxl.patch [new file with mode: 0644]
target/linux/riscv64/patches/004-spi-sifive.patch [new file with mode: 0644]
target/linux/riscv64/patches/005-spi-is25wp256d.patch [new file with mode: 0644]
target/linux/riscv64/patches/006-uart-sifive-serial-driver.patch [new file with mode: 0644]
target/linux/riscv64/patches/007-gpio-sifive-support-GPIO-on-SiFive-SoCs.patch [new file with mode: 0644]

index f6a13e8c3004dd4adc26eabd9e1073d5c64fb958..afb2b850c035a2f99fa3fbe621f28c73fd16c564 100644 (file)
@@ -4,29 +4,29 @@ CONFIG_ARCH_HAS_PTE_SPECIAL=y
 # CONFIG_ARCH_RV32I is not set
 CONFIG_ARCH_RV64I=y
 CONFIG_ARCH_WANT_FRAME_POINTERS=y
-# CONFIG_ASIX_PHY is not set
 CONFIG_ASN1=y
 CONFIG_ASSOCIATIVE_ARRAY=y
 CONFIG_ASYMMETRIC_KEY_TYPE=y
 CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
 CONFIG_ATA=y
 CONFIG_ATA_VERBOSE_ERROR=y
-# CONFIG_ATH5K_PCI is not set
-# CONFIG_AUTOFS_FS is not set
 CONFIG_BLK_DEV_SD=y
 CONFIG_BLK_MQ_PCI=y
 CONFIG_BLK_MQ_VIRTIO=y
 CONFIG_BLK_SCSI_REQUEST=y
-# CONFIG_BPFILTER is not set
 CONFIG_CAVIUM_PTP=y
 CONFIG_CC_HAS_SANCOV_TRACE_PC=y
 CONFIG_CC_HAS_STACKPROTECTOR_NONE=y
 CONFIG_CC_IS_GCC=y
+CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
 CONFIG_CLANG_VERSION=0
 CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLK_GEMGXL_MGMT=y
+CONFIG_CLK_U54_PRCI=y
 CONFIG_CLONE_BACKWARDS=y
 CONFIG_CLZ_TAB=y
-CONFIG_CMDLINE="earlyprintk root=/dev/mmcblk0p2 rootwait"
+CONFIG_CMDLINE="earlyprintk root=/dev/mmcblk0p2 rootwait console=ttySI0"
 CONFIG_CMDLINE_BOOL=y
 CONFIG_CMODEL_MEDANY=y
 # CONFIG_CMODEL_MEDLOW is not set
@@ -42,15 +42,10 @@ CONFIG_CRC16=y
 CONFIG_CRC32_SLICEBY8=y
 CONFIG_CRC7=y
 CONFIG_CRC_ITU_T=y
-CONFIG_CRYPTO_ACOMP2=y
 CONFIG_CRYPTO_AEAD=y
 CONFIG_CRYPTO_AEAD2=y
-# CONFIG_CRYPTO_AEGIS128 is not set
-# CONFIG_CRYPTO_AEGIS128L is not set
-# CONFIG_CRYPTO_AEGIS256 is not set
 CONFIG_CRYPTO_AKCIPHER=y
 CONFIG_CRYPTO_AKCIPHER2=y
-# CONFIG_CRYPTO_CFB is not set
 CONFIG_CRYPTO_CRC32C=y
 CONFIG_CRYPTO_DRBG=y
 CONFIG_CRYPTO_DRBG_HMAC=y
@@ -61,11 +56,8 @@ CONFIG_CRYPTO_HASH2=y
 CONFIG_CRYPTO_HASH_INFO=y
 CONFIG_CRYPTO_HMAC=y
 CONFIG_CRYPTO_JITTERENTROPY=y
-CONFIG_CRYPTO_KPP2=y
 CONFIG_CRYPTO_MANAGER=y
 CONFIG_CRYPTO_MANAGER2=y
-# CONFIG_CRYPTO_MORUS1280 is not set
-# CONFIG_CRYPTO_MORUS640 is not set
 CONFIG_CRYPTO_NULL=y
 CONFIG_CRYPTO_NULL2=y
 CONFIG_CRYPTO_RNG=y
@@ -73,11 +65,7 @@ CONFIG_CRYPTO_RNG2=y
 CONFIG_CRYPTO_RNG_DEFAULT=y
 CONFIG_CRYPTO_RSA=y
 CONFIG_CRYPTO_SHA256=y
-# CONFIG_CRYPTO_SM3 is not set
-# CONFIG_CRYPTO_SM4 is not set
-# CONFIG_CRYPTO_SPECK is not set
 CONFIG_CRYPTO_WORKQUEUE=y
-# CONFIG_CRYPTO_ZSTD is not set
 CONFIG_DEBUG_BUGVERBOSE=y
 # CONFIG_DEBUG_FS is not set
 CONFIG_DEBUG_SECTION_MISMATCH=y
@@ -88,14 +76,11 @@ CONFIG_DEVTMPFS=y
 CONFIG_DEVTMPFS_MOUNT=y
 CONFIG_DMA_DIRECT_OPS=y
 CONFIG_DNOTIFY=y
-# CONFIG_DP83822_PHY is not set
-# CONFIG_DP83TC811_PHY is not set
 CONFIG_DTC=y
 CONFIG_E1000E=y
 CONFIG_EARLY_PRINTK=y
 CONFIG_ELF_CORE=y
 CONFIG_ENABLE_MUST_CHECK=y
-CONFIG_EXPORTFS=y
 CONFIG_EXT3_FS=y
 # CONFIG_EXT3_FS_POSIX_ACL is not set
 # CONFIG_EXT3_FS_SECURITY is not set
@@ -107,8 +92,7 @@ CONFIG_FRAME_POINTER=y
 CONFIG_FRAME_WARN=2048
 CONFIG_FS_IOMAP=y
 CONFIG_FS_MBCACHE=y
-CONFIG_FUTEX_PI=y
-CONFIG_GCC_VERSION=80100
+CONFIG_GCC_VERSION=80200
 # CONFIG_GEMINI_ETHERNET is not set
 CONFIG_GENERIC_BUG=y
 CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
@@ -127,9 +111,7 @@ CONFIG_GPIOLIB=y
 CONFIG_GPIOLIB_FASTPATH_LIMIT=512
 # CONFIG_GPIO_HLWD is not set
 # CONFIG_GPIO_MAX3191X is not set
-# CONFIG_GPIO_MB86S7X is not set
-# CONFIG_GPIO_PCIE_IDIO_24 is not set
-# CONFIG_GUP_BENCHMARK is not set
+CONFIG_GPIO_SIFIVE=y
 CONFIG_HAS_DMA=y
 CONFIG_HAS_IOMEM=y
 CONFIG_HAS_IOPORT_MAP=y
@@ -178,8 +160,6 @@ CONFIG_KEYS=y
 # CONFIG_LAN743X is not set
 # CONFIG_LEDS_CR0014114 is not set
 # CONFIG_LEDS_LM3692X is not set
-# CONFIG_LEDS_MLXREG is not set
-# CONFIG_LEDS_TRIGGER_ACTIVITY is not set
 CONFIG_LEGACY_PTYS=y
 CONFIG_LEGACY_PTY_COUNT=256
 CONFIG_LIBFDT=y
@@ -192,11 +172,8 @@ CONFIG_MAXPHYSMEM_128GB=y
 # CONFIG_MAXPHYSMEM_2GB is not set
 CONFIG_MDIO_BUS=y
 CONFIG_MDIO_DEVICE=y
-# CONFIG_MDIO_MSCC_MIIM is not set
 CONFIG_MEMFD_CREATE=y
-# CONFIG_MICROCHIP_T1_PHY is not set
 CONFIG_MICROSEMI_PHY=y
-# CONFIG_MISC_RTSX_PCI is not set
 # CONFIG_MISC_RTSX_USB is not set
 CONFIG_MMC=y
 CONFIG_MMC_BLOCK=y
@@ -217,9 +194,8 @@ CONFIG_NEED_DMA_MAP_STATE=y
 CONFIG_NET_FAILOVER=y
 CONFIG_NET_FLOW_LIMIT=y
 CONFIG_NET_NS=y
-CONFIG_NET_PACKET_ENGINE=y
 CONFIG_NET_PTP_CLASSIFY=y
-# CONFIG_NET_SCH_CBS is not set
+CONFIG_NET_VENDOR_CADENCE=y
 CONFIG_NET_VENDOR_CORTINA=y
 CONFIG_NET_VENDOR_MICROSEMI=y
 CONFIG_NET_VENDOR_NI=y
@@ -239,12 +215,12 @@ CONFIG_OF_MDIO=y
 CONFIG_OF_NET=y
 CONFIG_OID_REGISTRY=y
 CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW=y
-# CONFIG_OVERLAY_FS_XINO_AUTO is not set
 CONFIG_PADATA=y
 CONFIG_PAGE_OFFSET=0xffffffe000000000
 CONFIG_PANIC_TIMEOUT=0
 CONFIG_PCI=y
 # CONFIG_PCIE_CADENCE_HOST is not set
+CONFIG_PCIE_MICROSEMI=y
 CONFIG_PCI_DEBUG=y
 CONFIG_PCI_DOMAINS=y
 CONFIG_PCI_DOMAINS_GENERIC=y
@@ -267,58 +243,49 @@ CONFIG_RCU_NEED_SEGCBLIST=y
 CONFIG_RCU_STALL_COMMON=y
 CONFIG_RCU_TRACE=y
 CONFIG_RD_GZIP=y
+CONFIG_REALTEK_PHY=y
 CONFIG_REGMAP=y
 CONFIG_REGMAP_I2C=y
 CONFIG_REGMAP_SPI=y
-# CONFIG_RENESAS_PHY is not set
-CONFIG_RFKILL_LEDS=y
 CONFIG_RFS_ACCEL=y
 CONFIG_RISCV=y
-CONFIG_RISCV_INTC=y
 CONFIG_RISCV_ISA_A=y
 CONFIG_RISCV_ISA_C=y
-CONFIG_RISCV_PLIC=y
 CONFIG_RISCV_TIMER=y
-# CONFIG_RPMSG_VIRTIO is not set
 CONFIG_RPS=y
 CONFIG_RTC_CLASS=y
 # CONFIG_RTC_DRV_ISL12026 is not set
-# CONFIG_RTC_DRV_PCF85363 is not set
 CONFIG_RTC_I2C_AND_SPI=y
-# CONFIG_RUNTIME_TESTING_MENU is not set
 CONFIG_SATA_AHCI=y
 CONFIG_SATA_MOBILE_LPM_POLICY=0
 CONFIG_SATA_PMP=y
 CONFIG_SATA_SIL24=y
 CONFIG_SCHED_DEBUG=y
 CONFIG_SCSI=y
-# CONFIG_SECONDARY_TRUSTED_KEYRING is not set
 CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
 CONFIG_SERIAL_8250_NR_UARTS=4
 CONFIG_SERIAL_8250_RUNTIME_UARTS=4
 CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_SIFIVE=y
+CONFIG_SERIAL_SIFIVE_CONSOLE=y
 CONFIG_SERIO=y
 CONFIG_SERIO_SERPORT=y
-CONFIG_SGL_ALLOC=y
 CONFIG_SG_POOL=y
 CONFIG_SIFIVE_PLIC=y
-# CONFIG_SIOX is not set
-# CONFIG_SLIMBUS is not set
 CONFIG_SLUB_DEBUG=y
 CONFIG_SMP=y
-# CONFIG_SOUNDWIRE is not set
 CONFIG_SPARSE_IRQ=y
 CONFIG_SPI=y
 CONFIG_SPI_BITBANG=y
 CONFIG_SPI_MASTER=y
 CONFIG_SPI_MEM=y
+CONFIG_SPI_SIFIVE=y
 CONFIG_SRCU=y
 CONFIG_STACKTRACE=y
 CONFIG_SWIOTLB=y
 CONFIG_SWPHY=y
 CONFIG_SYSCTL_EXCEPTION_TRACE=y
 CONFIG_SYSFS_SYSCALL=y
-# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set
 CONFIG_THREAD_INFO_IN_TASK=y
 CONFIG_TICK_CPU_ACCOUNTING=y
 CONFIG_TIMER_OF=y
@@ -327,9 +294,7 @@ CONFIG_TRACE_CLOCK=y
 CONFIG_TREE_RCU=y
 CONFIG_TREE_SRCU=y
 CONFIG_TUNE_GENERIC=y
-# CONFIG_TYPEC is not set
 CONFIG_UEVENT_HELPER_PATH=""
-# CONFIG_UNISYSSPAR is not set
 CONFIG_USB=y
 CONFIG_USB_COMMON=y
 CONFIG_USB_EHCI_HCD=y
@@ -359,8 +324,6 @@ CONFIG_VIRTIO_MMIO=y
 CONFIG_VIRTIO_NET=y
 CONFIG_VM_EVENT_COUNTERS=y
 CONFIG_X509_CERTIFICATE_PARSER=y
-# CONFIG_XDP_SOCKETS is not set
-# CONFIG_XILINX_VCU is not set
 CONFIG_XPS=y
 CONFIG_ZLIB_INFLATE=y
 CONFIG_ZONE_DMA32=y
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
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 <atish.patra@wdc.com>
+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 <atish.patra@wdc.com>
+---
+ 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
new file mode 100644 (file)
index 0000000..47c75ea
--- /dev/null
@@ -0,0 +1,804 @@
+From 1fd6c8fbc07bec3c18771738ce4b2aad3ed228f2 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <wesley@sifive.com>
+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 <badal.nilawar@microsemi.com>
++ *
++ * 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
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" <wesley@sifive.com>
+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 <palmer@sifive.com>
+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 <palmer@sifive.com>
+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 <palmer@sifive.com>
+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 <palmer@sifive.com>
+---
+ 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" <wesley@sifive.com>
+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
new file mode 100644 (file)
index 0000000..c44befc
--- /dev/null
@@ -0,0 +1,196 @@
+From 13317fd60728d24988fb8f5682bfaafe401b3a15 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <wesley@sifive.com>
+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 <palmer@sifive.com>
+---
+
+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
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" <wesley@sifive.com>
+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 <palmer@sifive.com>
+---
+ .../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.
++ *    sifive@sifive.com
++ *
++ * 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. <sifive@sifive.com>");
++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
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" <wesley@sifive.com>
+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 <palmer@sifive.com>
+---
+ 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
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" <wesley@sifive.com>
+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 <paul@pwsan.com>
++ *
++ * 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 <paul@pwsan.com>");
+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
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" <wesley@sifive.com>
+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
+