Sfoglia il codice sorgente

recipes-kernel: linux-mainline: Initial commit of mainline 5.1 kernel

Add a linux-mainline kernel from 5.1-rc1. The idea is that this kernel
will autorev the 5.1 development cycle and then be updated to autorev
the 5.1 stable branch after release.

Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
Alistair Francis 5 anni fa
parent
commit
a14f6bbc93

+ 1174 - 0
recipes-kernel/linux/files/freedom-u540/0001-tty-serial-add-driver-for-the-SiFive-UART.patch

@@ -0,0 +1,1174 @@
+From 322175de5296868fa55aa911ea7ba936a985a29a Mon Sep 17 00:00:00 2001
+From: Paul Walmsley <paul.walmsley@sifive.com>
+Date: Sat, 20 Oct 2018 03:10:47 -0700
+Subject: [PATCH 01/10] tty: serial: add driver for the SiFive UART
+
+Add a serial driver for the SiFive UART, found on SiFive FU540 devices
+(among others).
+
+The underlying serial IP block is relatively basic, and currently does
+not support serial break detection.  Further information on the IP
+block can be found in the documentation and Chisel sources:
+
+https://static.dev.sifive.com/FU540-C000-v1.0.pdf
+
+https://github.com/sifive/sifive-blocks/tree/master/src/main/scala/devices/uart
+
+This driver was written in collaboration with Wesley Terpstra
+<wesley@sifive.com>.
+
+Boot-tested on a SiFive HiFive Unleashed A00 board.
+
+Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Cc: Jiri Slaby <jslaby@suse.com>
+Cc: Palmer Dabbelt <palmer@sifive.com>
+Cc: Wesley Terpstra <wesley@sifive.com>
+Cc: linux-serial@vger.kernel.org
+Cc: linux-riscv@lists.infradead.org
+Cc: linux-kernel@vger.kernel.org
+Cc: Julia Lawall <julia.lawall@lip6.fr>
+Signed-off-by: Paul Walmsley <paul.walmsley@sifive.com>
+Signed-off-by: Paul Walmsley <paul@pwsan.com>
+---
+ drivers/tty/serial/Kconfig       |   24 +
+ drivers/tty/serial/Makefile      |    1 +
+ drivers/tty/serial/sifive.c      | 1067 ++++++++++++++++++++++++++++++
+ include/uapi/linux/serial_core.h |    3 +
+ 4 files changed, 1095 insertions(+)
+ create mode 100644 drivers/tty/serial/sifive.c
+
+diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
+index 72966bc0ac76..561e053b690a 100644
+--- a/drivers/tty/serial/Kconfig
++++ b/drivers/tty/serial/Kconfig
+@@ -1095,6 +1095,30 @@ 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
++	  Select this option if you are building a kernel for a device that
++	  contains a SiFive UART IP block.  This type of UART is present on
++	  SiFive FU540 SoCs, among others.
++
++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=ttySIFx". (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 40b702aaa85e..2aff1d07d08b 100644
+--- a/drivers/tty/serial/Makefile
++++ b/drivers/tty/serial/Makefile
+@@ -92,6 +92,7 @@ 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_RDA)	+= rda-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 000000000000..80c06039fb57
+--- /dev/null
++++ b/drivers/tty/serial/sifive.c
+@@ -0,0 +1,1067 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * SiFive UART driver
++ * Copyright (C) 2018 Paul Walmsley <paul@pwsan.com>
++ * Copyright (C) 2018 SiFive
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; 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.
++ *
++ * Based partially on:
++ * - drivers/tty/serial/pxa.c
++ * - drivers/tty/serial/amba-pl011.c
++ * - drivers/tty/serial/uartlite.c
++ * - drivers/tty/serial/omap-serial.c
++ * - drivers/pwm/pwm-sifive.c
++ *
++ * See the following sources for further documentation:
++ * - Chapter 19 "Universal Asynchronous Receiver/Transmitter (UART)" of
++ *   SiFive FE310-G000 v2p3
++ * - The tree/master/src/main/scala/devices/uart directory of
++ *   https://github.com/sifive/sifive-blocks/
++ *
++ * 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 8250
++ * programming model.
++ */
++
++#include <linux/clk.h>
++#include <linux/console.h>
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++#include <linux/platform_device.h>
++#include <linux/serial_core.h>
++#include <linux/serial_reg.h>
++#include <linux/slab.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.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 on a device that can
++ *                          host a serial console
++ */
++#define SIFIVE_SERIAL_MAX_PORTS			8
++
++/*
++ * SIFIVE_DEFAULT_BAUD_RATE: default baud rate that the driver should
++ *                           configure itself to use
++ */
++#define SIFIVE_DEFAULT_BAUD_RATE		115200
++
++/* 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			"ttySIF"
++
++/* SIFIVE_TX_FIFO_DEPTH: depth of the TX FIFO (in bytes) */
++#define SIFIVE_TX_FIFO_DEPTH			8
++
++/* SIFIVE_RX_FIFO_DEPTH: depth of the TX FIFO (in bytes) */
++#define SIFIVE_RX_FIFO_DEPTH			8
++
++#if (SIFIVE_TX_FIFO_DEPTH != SIFIVE_RX_FIFO_DEPTH)
++#error Driver does not support configurations with different TX, RX FIFO sizes
++#endif
++
++/*
++ *
++ */
++
++/**
++ * 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.
++ * @baud_rate: UART serial line rate (e.g., 115200 baud)
++ * @clk_notifier: clock rate change notifier for upstream clock changes
++ *
++ * Configuration data specific to this SiFive UART.
++ */
++struct sifive_serial_port {
++	struct uart_port	port;
++	struct device		*dev;
++	unsigned char		ier;
++	unsigned long		clkin_rate;
++	unsigned long		baud_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
++ */
++
++/**
++ * __ssp_early_writel() - write to a SiFive serial port 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.
++ *
++ * Context: Intended to be used only by the earlyconsole code.
++ */
++static void __ssp_early_writel(u32 v, u16 offs, struct uart_port *port)
++{
++	writel_relaxed(v, port->membase + offs);
++}
++
++/**
++ * __ssp_early_readl() - read from a SiFive serial port 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.
++ *
++ * Context: Intended to be called only by the earlyconsole code or by
++ *          __ssp_readl() or __ssp_writel() (in this driver)
++ *
++ * Returns: the register value read from the UART.
++ */
++static u32 __ssp_early_readl(struct uart_port *port, u16 offs)
++{
++	return readl_relaxed(port->membase + offs);
++}
++
++/**
++ * __ssp_writel() - write to a SiFive serial port register
++ * @v: value to write to the register
++ * @offs: register address offset from the IP block base address
++ * @ssp: pointer to a struct sifive_serial_port record
++ *
++ * 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.
++ *
++ * Context: Any context.
++ */
++static void __ssp_writel(u32 v, u16 offs, struct sifive_serial_port *ssp)
++{
++	__ssp_early_writel(v, offs, &ssp->port);
++}
++
++/**
++ * __ssp_readl() - read from a SiFive serial port 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.
++ *
++ * Context: Any context.
++ *
++ * Returns: the value of the UART register
++ */
++static u32 __ssp_readl(struct sifive_serial_port *ssp, u16 offs)
++{
++	return __ssp_early_readl(&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 __ssp_readl(ssp, SIFIVE_SERIAL_TXDATA_OFFS) &
++		SIFIVE_SERIAL_TXDATA_FULL_MASK;
++}
++
++/**
++ * __ssp_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().
++ *
++ * Context: Any context.
++ */
++static void __ssp_transmit_char(struct sifive_serial_port *ssp, int ch)
++{
++	__ssp_writel(ch, SIFIVE_SERIAL_TXDATA_OFFS, ssp);
++}
++
++/**
++ * __ssp_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.
++ *
++ * Context: Any context.  Expects @ssp->port.lock to be held by caller.
++ */
++static void __ssp_transmit_chars(struct sifive_serial_port *ssp)
++{
++	struct circ_buf *xmit = &ssp->port.state->xmit;
++	int count;
++
++	if (ssp->port.x_char) {
++		__ssp_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 = SIFIVE_TX_FIFO_DEPTH;
++	do {
++		__ssp_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);
++}
++
++/**
++ * __ssp_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 SiFive UART referred to by @ssp.
++ */
++static void __ssp_enable_txwm(struct sifive_serial_port *ssp)
++{
++	if (ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK)
++		return;
++
++	ssp->ier |= SIFIVE_SERIAL_IE_TXWM_MASK;
++	__ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
++}
++
++/**
++ * __ssp_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 SiFive UART referred to by @ssp.
++ */
++static void __ssp_enable_rxwm(struct sifive_serial_port *ssp)
++{
++	if (ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK)
++		return;
++
++	ssp->ier |= SIFIVE_SERIAL_IE_RXWM_MASK;
++	__ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
++}
++
++/**
++ * __ssp_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 __ssp_disable_txwm(struct sifive_serial_port *ssp)
++{
++	if (!(ssp->ier & SIFIVE_SERIAL_IE_TXWM_MASK))
++		return;
++
++	ssp->ier &= ~SIFIVE_SERIAL_IE_TXWM_MASK;
++	__ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
++}
++
++/**
++ * __ssp_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 __ssp_disable_rxwm(struct sifive_serial_port *ssp)
++{
++	if (!(ssp->ier & SIFIVE_SERIAL_IE_RXWM_MASK))
++		return;
++
++	ssp->ier &= ~SIFIVE_SERIAL_IE_RXWM_MASK;
++	__ssp_writel(ssp->ier, SIFIVE_SERIAL_IE_OFFS, ssp);
++}
++
++/**
++ * __ssp_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 __ssp_receive_char(struct sifive_serial_port *ssp, char *is_empty)
++{
++	u32 v;
++	u8 ch;
++
++	v = __ssp_readl(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;
++}
++
++/**
++ * __ssp_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.
++ *
++ * Context: Expects ssp->port.lock to be held by caller.
++ */
++static void __ssp_receive_chars(struct sifive_serial_port *ssp)
++{
++	unsigned char ch;
++	char is_empty;
++	int c;
++
++	for (c = SIFIVE_RX_FIFO_DEPTH; c > 0; --c) {
++		ch = __ssp_receive_char(ssp, &is_empty);
++		if (is_empty)
++			break;
++
++		ssp->port.icount.rx++;
++		uart_insert_char(&ssp->port, 0, 0, ch, TTY_NORMAL);
++	}
++
++	spin_unlock(&ssp->port.lock);
++	tty_flip_buffer_push(&ssp->port.state->port);
++	spin_lock(&ssp->port.lock);
++}
++
++/**
++ * __ssp_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
++ * and target line rate referred to by @ssp and write it into the
++ * hardware.
++ */
++static void __ssp_update_div(struct sifive_serial_port *ssp)
++{
++	u16 div;
++
++	div = DIV_ROUND_UP(ssp->clkin_rate, ssp->baud_rate) - 1;
++
++	__ssp_writel(div, SIFIVE_SERIAL_DIV_OFFS, ssp);
++}
++
++/**
++ * __ssp_update_baud_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 __ssp_update_baud_rate(struct sifive_serial_port *ssp,
++				   unsigned int rate)
++{
++	if (ssp->baud_rate == rate)
++		return;
++
++	ssp->baud_rate = rate;
++	__ssp_update_div(ssp);
++}
++
++/**
++ * __ssp_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 __ssp_set_stop_bits(struct sifive_serial_port *ssp, char nstop)
++{
++	u32 v;
++
++	if (nstop < 1 || nstop > 2) {
++		WARN_ON(1);
++		return;
++	}
++
++	v = __ssp_readl(ssp, SIFIVE_SERIAL_TXCTRL_OFFS);
++	v &= ~SIFIVE_SERIAL_TXCTRL_NSTOP_MASK;
++	v |= (nstop - 1) << SIFIVE_SERIAL_TXCTRL_NSTOP_SHIFT;
++	__ssp_writel(v, SIFIVE_SERIAL_TXCTRL_OFFS, ssp);
++}
++
++/**
++ * __ssp_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.
++ *
++ * Context: Any context.
++ */
++static void __maybe_unused __ssp_wait_for_xmitr(struct sifive_serial_port *ssp)
++{
++	while (sifive_serial_is_txfifo_full(ssp))
++		udelay(1); /* XXX Could probably be more intelligent here */
++}
++
++/*
++ * 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);
++
++	__ssp_disable_txwm(ssp);
++}
++
++static void sifive_serial_stop_rx(struct uart_port *port)
++{
++	struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++	__ssp_disable_rxwm(ssp);
++}
++
++static void sifive_serial_start_tx(struct uart_port *port)
++{
++	struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++	__ssp_enable_txwm(ssp);
++}
++
++static irqreturn_t sifive_serial_irq(int irq, void *dev_id)
++{
++	struct sifive_serial_port *ssp = dev_id;
++	u32 ip;
++
++	spin_lock(&ssp->port.lock);
++
++	ip = __ssp_readl(ssp, SIFIVE_SERIAL_IP_OFFS);
++	if (!ip) {
++		spin_unlock(&ssp->port.lock);
++		return IRQ_NONE;
++	}
++
++	if (ip & SIFIVE_SERIAL_IP_RXWM_MASK)
++		__ssp_receive_chars(ssp);
++	if (ip & SIFIVE_SERIAL_IP_TXWM_MASK)
++		__ssp_transmit_chars(ssp);
++
++	spin_unlock(&ssp->port.lock);
++
++	return IRQ_HANDLED;
++}
++
++static unsigned int sifive_serial_tx_empty(struct uart_port *port)
++{
++	return TIOCSER_TEMT;
++}
++
++static unsigned int sifive_serial_get_mctrl(struct uart_port *port)
++{
++	return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR;
++}
++
++static void sifive_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
++{
++	/* IP block does not support these signals */
++}
++
++static void sifive_serial_break_ctl(struct uart_port *port, int break_state)
++{
++	/* IP block does not support sending a break */
++}
++
++static int sifive_serial_startup(struct uart_port *port)
++{
++	struct sifive_serial_port *ssp = port_to_sifive_serial_port(port);
++
++	__ssp_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);
++
++	__ssp_disable_rxwm(ssp);
++	__ssp_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 V0 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;
++		__ssp_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);
++	unsigned long flags;
++	u32 v, old_v;
++	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;
++	__ssp_set_stop_bits(ssp, nstop);
++
++	/* Set line rate */
++	rate = uart_get_baud_rate(port, termios, old, 0, ssp->clkin_rate / 16);
++	__ssp_update_baud_rate(ssp, rate);
++
++	spin_lock_irqsave(&ssp->port.lock, flags);
++
++	/*
++	 * Update the per-port timeout.
++	 */
++	uart_update_timeout(port, termios->c_cflag, rate);
++
++	ssp->port.read_status_mask = 0;
++	if (termios->c_iflag & INPCK) {
++		dev_err(ssp->port.dev, "INPCK flag not supported\n");
++		goto ssst_out;
++	}
++	if (termios->c_iflag & (BRKINT | PARMRK)) {
++		dev_err(ssp->port.dev, "BRKINT/PARMRK flag not supported\n");
++		goto ssst_out;
++	}
++
++	/*
++	 * ignore all characters if CREAD is not set
++	 */
++	v = __ssp_readl(ssp, SIFIVE_SERIAL_RXCTRL_OFFS);
++	old_v = v;
++	if ((termios->c_cflag & CREAD) == 0)
++		v &= SIFIVE_SERIAL_RXCTRL_RXEN_MASK;
++	else
++		v |= SIFIVE_SERIAL_RXCTRL_RXEN_MASK;
++	if (v != old_v)
++		__ssp_writel(v, SIFIVE_SERIAL_RXCTRL_OFFS, ssp);
++
++ssst_out:
++	spin_unlock_irqrestore(&ssp->port.lock, flags);
++}
++
++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_V0;
++}
++
++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_V0 ? "SiFive UART v0" : NULL;
++}
++
++/*
++ * Early console support
++ */
++
++#ifdef CONFIG_SERIAL_EARLYCON
++static void early_sifive_serial_putc(struct uart_port *port, int c)
++{
++	while (__ssp_early_readl(port, SIFIVE_SERIAL_TXDATA_OFFS) &
++	       SIFIVE_SERIAL_TXDATA_FULL_MASK)
++		cpu_relax();
++
++	__ssp_early_writel(c, SIFIVE_SERIAL_TXDATA_OFFS, port);
++}
++
++static void early_sifive_serial_write(struct console *con, const char *s,
++				      unsigned int n)
++{
++	struct earlycon_device *dev = con->data;
++	struct uart_port *port = &dev->port;
++
++	uart_console_write(port, s, n, early_sifive_serial_putc);
++}
++
++static int __init early_sifive_serial_setup(struct earlycon_device *dev,
++					    const char *options)
++{
++	struct uart_port *port = &dev->port;
++
++	if (!port->membase)
++		return -ENODEV;
++
++	dev->con->write = early_sifive_serial_write;
++
++	return 0;
++}
++
++OF_EARLYCON_DECLARE(sifive, "sifive,uart0", early_sifive_serial_setup);
++OF_EARLYCON_DECLARE(sifive, "sifive,fu540-c000-uart0",
++		    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);
++
++	__ssp_wait_for_xmitr(ssp);
++	__ssp_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 = __ssp_readl(ssp, SIFIVE_SERIAL_IE_OFFS);
++	__ssp_writel(0, SIFIVE_SERIAL_IE_OFFS, ssp);
++
++	uart_console_write(&ssp->port, s, count, sifive_serial_console_putchar);
++
++	__ssp_writel(ier, SIFIVE_SERIAL_IE_OFFS, ssp);
++
++	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 = SIFIVE_DEFAULT_BAUD_RATE;
++	int bits = 8;
++	int parity = 'n';
++	int flow = 'n';
++
++	if (co->index < 0 || co->index >= SIFIVE_SERIAL_MAX_PORTS)
++		return -ENODEV;
++
++	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 int __init sifive_console_init(void)
++{
++	register_console(&sifive_serial_console);
++	return 0;
++}
++
++console_initcall(sifive_console_init);
++
++static void __ssp_add_console_port(struct sifive_serial_port *ssp)
++{
++	sifive_serial_console_ports[ssp->port.line] = ssp;
++}
++
++static void __ssp_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 __ssp_add_console_port(struct sifive_serial_port *ssp)
++{}
++static void __ssp_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,
++};
++
++static struct uart_driver sifive_serial_uart_driver = {
++	.owner		= THIS_MODULE,
++	.driver_name	= SIFIVE_SERIAL_NAME,
++	.dev_name	= SIFIVE_TTY_PREFIX,
++	.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_V0;
++	ssp->port.iotype = UPIO_MEM;
++	ssp->port.irq = irq;
++	ssp->port.fifosize = SIFIVE_TX_FIFO_DEPTH;
++	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;
++	}
++
++	/* Set up clock divider */
++	ssp->clkin_rate = clk_get_rate(ssp->clk);
++	ssp->baud_rate = SIFIVE_DEFAULT_BAUD_RATE;
++	__ssp_update_div(ssp);
++
++	platform_set_drvdata(pdev, ssp);
++
++	/* Enable transmits and set the watermark level to 1 */
++	__ssp_writel((1 << SIFIVE_SERIAL_TXCTRL_TXCNT_SHIFT) |
++		     SIFIVE_SERIAL_TXCTRL_TXEN_MASK,
++		     SIFIVE_SERIAL_TXCTRL_OFFS, ssp);
++
++	/* Enable receives and set the watermark level to 0 */
++	__ssp_writel((0 << SIFIVE_SERIAL_RXCTRL_RXCNT_SHIFT) |
++		     SIFIVE_SERIAL_RXCTRL_RXEN_MASK,
++		     SIFIVE_SERIAL_RXCTRL_OFFS, ssp);
++
++	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;
++	}
++
++	__ssp_add_console_port(ssp);
++
++	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;
++	}
++
++	return 0;
++
++probe_out3:
++	__ssp_remove_console_port(ssp);
++	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);
++
++	__ssp_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,fu540-c000-uart0" },
++	{ .compatible = "sifive,uart0" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, sifive_serial_of_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)
++{
++	int r;
++
++	r = uart_register_driver(&sifive_serial_uart_driver);
++	if (r)
++		goto init_out1;
++
++	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 6009ee2c2e99..d3bb0d447c0b 100644
+--- a/include/uapi/linux/serial_core.h
++++ b/include/uapi/linux/serial_core.h
+@@ -287,4 +287,7 @@
+ /* RDA UART */
+ #define PORT_RDA	118
+ 
++/* SiFive UART */
++#define PORT_SIFIVE_V0	118
++
+ #endif /* _UAPILINUX_SERIAL_CORE_H */
+-- 
+2.21.0
+

+ 236 - 0
recipes-kernel/linux/files/freedom-u540/0002-gemgxl-mgmt-implement-clock-switch-for-GEM-tx_clk.patch

@@ -0,0 +1,236 @@
+From 3d9d2c51f1c4780deafb2fcaf4c5ad14b8ae9e68 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 02/10] gemgxl-mgmt: implement clock switch for GEM tx_clk
+
+Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
+---
+ .../bindings/clock/sifive,gemgxl-mgmt.txt     |  26 ++++
+ drivers/clk/Kconfig                           |   1 +
+ drivers/clk/Makefile                          |   1 +
+ drivers/clk/sifive/Kconfig                    |   9 ++
+ drivers/clk/sifive/Makefile                   |   2 +
+ drivers/clk/sifive/gemgxl-mgmt.c              | 129 ++++++++++++++++++
+ 6 files changed, 168 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt
+ create mode 100644 drivers/clk/sifive/Kconfig
+ create mode 100644 drivers/clk/sifive/Makefile
+ create mode 100644 drivers/clk/sifive/gemgxl-mgmt.c
+
+diff --git a/Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt b/Documentation/devicetree/bindings/clock/sifive,gemgxl-mgmt.txt
+new file mode 100644
+index 000000000000..349489e33eda
+--- /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/Kconfig b/drivers/clk/Kconfig
+index e705aab9e38b..49d72e084eac 100644
+--- a/drivers/clk/Kconfig
++++ b/drivers/clk/Kconfig
+@@ -309,6 +309,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"
+diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
+index 1db133652f0c..62ba714e34b1 100644
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -93,6 +93,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/drivers/clk/sifive/Kconfig b/drivers/clk/sifive/Kconfig
+new file mode 100644
+index 000000000000..284bffb121eb
+--- /dev/null
++++ 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
+new file mode 100644
+index 000000000000..7784d2ee0f44
+--- /dev/null
++++ 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 000000000000..00b07580fe3c
+--- /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);
+-- 
+2.21.0
+

+ 63 - 0
recipes-kernel/linux/files/freedom-u540/0003-u54-prci-driver-for-core-U54-clocks.patch

@@ -0,0 +1,63 @@
+From aebd1722fcf2f9e7071ceeca5995a308ca099959 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 03/10] 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 000000000000..88682c5eaebc
+--- /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 */
++	};
+-- 
+2.21.0
+

+ 333 - 0
recipes-kernel/linux/files/freedom-u540/0004-u54-prci-driver-for-core-U54-clocks.patch

@@ -0,0 +1,333 @@
+From 883228d88d9ad6edc7ed9a4540926e80a22c560f 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 04/10] u54-prci: driver for core U54 clocks
+
+---
+ drivers/clk/sifive/u54-prci.c | 314 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 314 insertions(+)
+ create mode 100644 drivers/clk/sifive/u54-prci.c
+
+diff --git a/drivers/clk/sifive/u54-prci.c b/drivers/clk/sifive/u54-prci.c
+new file mode 100644
+index 000000000000..edc4b7818e71
+--- /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);
+-- 
+2.21.0
+

+ 409 - 0
recipes-kernel/linux/files/freedom-u540/0005-gpio-sifive-support-GPIO-on-SiFive-SoCs.patch

@@ -0,0 +1,409 @@
+From 9b081d5eb7cd900b8ed96c1ee01f515de11fb174 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 05/10] 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 000000000000..bf41eed81dbf
+--- /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 3f50526a771f..2a0115b0df97 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -451,6 +451,13 @@ config GPIO_SAMA5D2_PIOBU
+ 	  The difference from regular GPIOs is that they
+ 	  maintain their value during backup/self-refresh.
+ 
++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_SIOX
+ 	tristate "SIOX GPIO support"
+ 	depends on SIOX
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
+index 54d55274b93a..660e2e70f05c 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -114,6 +114,7 @@ obj-$(CONFIG_ARCH_SA1100)	+= gpio-sa1100.o
+ obj-$(CONFIG_GPIO_SAMA5D2_PIOBU)	+= gpio-sama5d2-piobu.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_SNPS_CREG)	+= gpio-creg-snps.o
+ obj-$(CONFIG_GPIO_SODAVILLE)	+= gpio-sodaville.o
+ obj-$(CONFIG_GPIO_SPEAR_SPICS)	+= gpio-spear-spics.o
+diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
+new file mode 100644
+index 000000000000..6482ebbc00ce
+--- /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.21.0
+

+ 342 - 0
recipes-kernel/linux/files/freedom-u540/0006-pwm-sifive-add-a-driver-for-SiFive-SoC-PWM.patch

@@ -0,0 +1,342 @@
+From f263b4b12a93490dbb1af78a51a6f06129448226 Mon Sep 17 00:00:00 2001
+From: "Wesley W. Terpstra" <wesley@sifive.com>
+Date: Fri, 2 Mar 2018 14:53:51 -0800
+Subject: [PATCH 06/10] pwm-sifive: add a driver for SiFive SoC PWM
+
+---
+ .../devicetree/bindings/pwm/pwm-sifive.txt    |  28 ++
+ drivers/pwm/Kconfig                           |  10 +
+ drivers/pwm/Makefile                          |   1 +
+ drivers/pwm/pwm-sifive.c                      | 252 ++++++++++++++++++
+ 4 files changed, 291 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pwm/pwm-sifive.txt
+ create mode 100644 drivers/pwm/pwm-sifive.c
+
+diff --git a/Documentation/devicetree/bindings/pwm/pwm-sifive.txt b/Documentation/devicetree/bindings/pwm/pwm-sifive.txt
+new file mode 100644
+index 000000000000..7cea20db58e5
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pwm/pwm-sifive.txt
+@@ -0,0 +1,28 @@
++SiFive PWM controller
++
++Unlike most other PWM controllers, the SiFive PWM controller currently only
++supports one period for all channels in the PWM. This is set globally in DTS.
++The period also has significant restrictions on the values it can achieve,
++which the driver rounds to the nearest achievable frequency.
++
++Required properties:
++- compatible: should be "sifive,pwm0"
++- reg: physical base address and length of the controller's registers
++- clocks: The frequency the controller runs at
++- #pwm-cells: Should be 2.
++  The first cell is the PWM channel number
++  The second cell is the PWM polarity
++- sifive,approx-period: the driver will get as close to this period as it can
++- interrupts: one interrupt per PWM channel (currently unused in the driver)
++
++Examples:
++
++pwm:  pwm@10020000 {
++	compatible = "sifive,pwm0";
++	reg = <0x0 0x10020000 0x0 0x1000>;
++	clocks = <&tlclk>;
++	interrupt-parent = <&plic>;
++	interrupts = <42 43 44 45>;
++	#pwm-cells = <2>;
++	sifive,approx-period = <1000000>;
++};
+diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
+index 54f8238aac0d..995b9f654d46 100644
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -389,6 +389,16 @@ config PWM_SAMSUNG
+ 	  To compile this driver as a module, choose M here: the module
+ 	  will be called pwm-samsung.
+ 
++config PWM_SIFIVE
++	tristate "SiFive PWM support"
++	depends on OF
++	depends on COMMON_CLK
++	help
++	  Generic PWM framework driver for SiFive SoCs.
++
++	  To compile this driver as a module, choose M here: the module
++	  will be called pwm-sifive.
++
+ config PWM_SPEAR
+ 	tristate "STMicroelectronics SPEAr PWM support"
+ 	depends on PLAT_SPEAR
+diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
+index 448825e892bc..0da3e99b0186 100644
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -38,6 +38,7 @@ obj-$(CONFIG_PWM_RCAR)		+= pwm-rcar.o
+ obj-$(CONFIG_PWM_RENESAS_TPU)	+= pwm-renesas-tpu.o
+ obj-$(CONFIG_PWM_ROCKCHIP)	+= pwm-rockchip.o
+ obj-$(CONFIG_PWM_SAMSUNG)	+= pwm-samsung.o
++obj-$(CONFIG_PWM_SIFIVE)	+= pwm-sifive.o
+ obj-$(CONFIG_PWM_SPEAR)		+= pwm-spear.o
+ obj-$(CONFIG_PWM_STI)		+= pwm-sti.o
+ obj-$(CONFIG_PWM_STM32)		+= pwm-stm32.o
+diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c
+new file mode 100644
+index 000000000000..93bc0844d23a
+--- /dev/null
++++ b/drivers/pwm/pwm-sifive.c
+@@ -0,0 +1,252 @@
++/*
++ * Copyright (C) 2018 SiFive, Inc
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2, as published by
++ * the Free Software Foundation.
++ */
++
++#include <dt-bindings/pwm/pwm.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++#include <linux/slab.h>
++#include <linux/clk.h>
++#include <linux/io.h>
++
++#define MAX_PWM			4
++
++/* Register offsets */
++#define REG_PWMCFG		0x0
++#define REG_PWMCOUNT		0x8
++#define REG_PWMS		0x10
++#define	REG_PWMCMP0		0x20
++
++/* PWMCFG fields */
++#define BIT_PWM_SCALE		0
++#define BIT_PWM_STICKY		8
++#define BIT_PWM_ZERO_ZMP	9
++#define BIT_PWM_DEGLITCH	10
++#define BIT_PWM_EN_ALWAYS	12
++#define BIT_PWM_EN_ONCE		13
++#define BIT_PWM0_CENTER		16
++#define BIT_PWM0_GANG		24
++#define BIT_PWM0_IP		28
++
++#define SIZE_PWMCMP		4
++#define MASK_PWM_SCALE		0xf
++
++struct sifive_pwm_device {
++	struct pwm_chip		chip;
++	struct notifier_block	notifier;
++	struct clk		*clk;
++	void __iomem		*regs;
++	int 			irq;
++	unsigned int		approx_period;
++	unsigned int		real_period;
++};
++
++static inline struct sifive_pwm_device *chip_to_sifive(struct pwm_chip *c)
++{
++	return container_of(c, struct sifive_pwm_device, chip);
++}
++
++static inline struct sifive_pwm_device *notifier_to_sifive(struct notifier_block *nb)
++{
++	return container_of(nb, struct sifive_pwm_device, notifier);
++}
++
++static int sifive_pwm_apply(struct pwm_chip *chip, struct pwm_device *dev, struct pwm_state *state)
++{
++	struct sifive_pwm_device *pwm = chip_to_sifive(chip);
++	unsigned int duty_cycle;
++	u32 frac;
++
++	duty_cycle = state->duty_cycle;
++	if (!state->enabled) duty_cycle = 0;
++	if (state->polarity == PWM_POLARITY_NORMAL) duty_cycle = state->period - duty_cycle;
++
++	frac = ((u64)duty_cycle << 16) / state->period;
++	frac = min(frac, 0xFFFFU);
++
++	iowrite32(frac, pwm->regs + REG_PWMCMP0 + (dev->hwpwm * SIZE_PWMCMP));
++
++	if (state->enabled) {
++		state->period = pwm->real_period;
++		state->duty_cycle = ((u64)frac * pwm->real_period) >> 16;
++		if (state->polarity == PWM_POLARITY_NORMAL)
++			state->duty_cycle = state->period - state->duty_cycle;
++	}
++
++	return 0;
++}
++
++static void sifive_pwm_get_state(struct pwm_chip *chip, struct pwm_device *dev, struct pwm_state *state)
++{
++	struct sifive_pwm_device *pwm = chip_to_sifive(chip);
++	unsigned long duty;
++
++	duty = ioread32(pwm->regs + REG_PWMCMP0 + (dev->hwpwm * SIZE_PWMCMP));
++
++	state->period     = pwm->real_period;
++	state->duty_cycle = ((u64)duty * pwm->real_period) >> 16;
++	state->polarity   = PWM_POLARITY_INVERSED;
++	state->enabled    = duty > 0;
++}
++
++static const struct pwm_ops sifive_pwm_ops = {
++	.get_state	= sifive_pwm_get_state,
++	.apply		= sifive_pwm_apply,
++	.owner		= THIS_MODULE,
++};
++
++static struct pwm_device *sifive_pwm_xlate(struct pwm_chip *chip, const struct of_phandle_args *args)
++{
++	struct sifive_pwm_device *pwm = chip_to_sifive(chip);
++	struct pwm_device *dev;
++
++	if (args->args[0] >= chip->npwm)
++		return ERR_PTR(-EINVAL);
++
++	dev = pwm_request_from_chip(chip, args->args[0], NULL);
++	if (IS_ERR(dev))
++		return dev;
++
++	/* The period cannot be changed on a per-PWM basis */
++	dev->args.period   = pwm->real_period;
++	dev->args.polarity = PWM_POLARITY_NORMAL;
++	if (args->args[1] & PWM_POLARITY_INVERTED)
++		dev->args.polarity = PWM_POLARITY_INVERSED;
++
++	return dev;
++}
++
++static void sifive_pwm_update_clock(struct sifive_pwm_device *pwm, unsigned long rate)
++{
++	/* (1 << (16+scale)) * 10^9/rate = real_period */
++	unsigned long scalePow = (pwm->approx_period * (u64)rate) / 1000000000;
++	int scale = ilog2(scalePow) - 16;
++
++	scale = clamp(scale, 0, 0xf);
++	iowrite32((1 << BIT_PWM_EN_ALWAYS) | (scale << BIT_PWM_SCALE), pwm->regs + REG_PWMCFG);
++
++	pwm->real_period = (1000000000ULL << (16 + scale)) / rate;
++}
++
++static int sifive_pwm_clock_notifier(struct notifier_block *nb, unsigned long event, void *data)
++{
++	struct clk_notifier_data *ndata = data;
++	struct sifive_pwm_device *pwm = notifier_to_sifive(nb);
++
++	if (event == POST_RATE_CHANGE)
++		sifive_pwm_update_clock(pwm, ndata->new_rate);
++
++	return NOTIFY_OK;
++}
++
++static int sifive_pwm_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *node = pdev->dev.of_node;
++	struct sifive_pwm_device *pwm;
++	struct pwm_chip *chip;
++	struct resource *res;
++	int ret;
++
++	pwm = devm_kzalloc(dev, sizeof(*pwm), GFP_KERNEL);
++	if (!pwm) {
++		dev_err(dev, "Out of memory\n");
++		return -ENOMEM;
++	}
++
++	chip = &pwm->chip;
++	chip->dev = dev;
++	chip->ops = &sifive_pwm_ops;
++	chip->of_xlate = sifive_pwm_xlate;
++	chip->of_pwm_n_cells = 2;
++	chip->base = -1;
++
++	ret = of_property_read_u32(node, "sifive,npwm", &chip->npwm);
++	if (ret < 0 || chip->npwm > MAX_PWM) chip->npwm = MAX_PWM;
++
++	ret = of_property_read_u32(node, "sifive,approx-period", &pwm->approx_period);
++	if (ret < 0) {
++		dev_err(dev, "Unable to read sifive,approx-period from DTS\n");
++		return -ENOENT;
++	}
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	pwm->regs = devm_ioremap_resource(dev, res);
++	if (IS_ERR(pwm->regs)) {
++		dev_err(dev, "Unable to map IO resources\n");
++		return PTR_ERR(pwm->regs);
++	}
++
++	pwm->clk = devm_clk_get(dev, NULL);
++	if (IS_ERR(pwm->clk)) {
++		dev_err(dev, "Unable to find controller clock\n");
++		return PTR_ERR(pwm->clk);
++	}
++
++	pwm->irq = platform_get_irq(pdev, 0);
++	if (pwm->irq < 0) {
++		dev_err(dev, "Unable to find interrupt\n");
++		return pwm->irq;
++	}
++
++	/* Watch for changes to underlying clock frequency */
++	pwm->notifier.notifier_call = sifive_pwm_clock_notifier;
++	clk_notifier_register(pwm->clk, &pwm->notifier);
++
++	/* Initialize PWM config */
++	sifive_pwm_update_clock(pwm, clk_get_rate(pwm->clk));
++
++	/* No interrupt handler needed yet */
++/*
++	ret = devm_request_irq(dev, pwm->irq, sifive_pwm_irq, 0, dev_name(dev), pwm);
++	if (ret) {
++		dev_err(dev, "Unable to bind interrupt\n");
++		return ret;
++	}
++*/
++
++	ret = pwmchip_add(chip);
++	if (ret < 0) {
++		dev_err(dev, "cannot register PWM: %d\n", ret);
++		clk_notifier_unregister(pwm->clk, &pwm->notifier);
++		return ret;
++	}
++
++	platform_set_drvdata(pdev, pwm);
++	dev_info(dev, "SiFive PWM chip registered %d PWMs\n", chip->npwm);
++
++	return 0;
++}
++
++static int sifive_pwm_remove(struct platform_device *dev)
++{
++	struct sifive_pwm_device *pwm = platform_get_drvdata(dev);
++	struct pwm_chip *chip = &pwm->chip;
++
++	clk_notifier_unregister(pwm->clk, &pwm->notifier);
++	return pwmchip_remove(chip);
++}
++
++static const struct of_device_id sifive_pwm_of_match[] = {
++	{ .compatible = "sifive,pwm0" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, sifive_pwm_of_match);
++
++static struct platform_driver sifive_pwm_driver = {
++	.probe = sifive_pwm_probe,
++	.remove = sifive_pwm_remove,
++	.driver = {
++		.name = "pwm-sifivem",
++		.of_match_table = of_match_ptr(sifive_pwm_of_match),
++	},
++};
++module_platform_driver(sifive_pwm_driver);
++
++MODULE_DESCRIPTION("SiFive PWM driver");
++MODULE_LICENSE("GPL v2");
+-- 
+2.21.0
+

+ 39 - 0
recipes-kernel/linux/files/freedom-u540/0007-RISC-V-Networking-fix-Hack.patch

@@ -0,0 +1,39 @@
+From b720ad246b285e678cc16f0c35ef1921ace80043 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 07/10] 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 4be4cc09eb90..a69a95304cb3 100644
+--- a/drivers/net/phy/mdio_bus.c
++++ b/drivers/net/phy/mdio_bus.c
+@@ -57,9 +57,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.21.0
+

+ 829 - 0
recipes-kernel/linux/files/freedom-u540/0008-pcie-microsemi-Add-support-for-the-Vera-board-root-c.patch

@@ -0,0 +1,829 @@
+From 9ecb60c4fd1cd7e85973427527167eaac137bfe9 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 08/10] pcie-microsemi: Add support for the Vera-board root
+ complex
+
+Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
+---
+ drivers/pci/controller/Kconfig          |   6 +
+ drivers/pci/controller/Makefile         |   1 +
+ drivers/pci/controller/pcie-microsemi.c | 778 ++++++++++++++++++++++++
+ 3 files changed, 785 insertions(+)
+ create mode 100644 drivers/pci/controller/pcie-microsemi.c
+
+diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
+index 6012f3059acd..284cea1b6ba1 100644
+--- a/drivers/pci/controller/Kconfig
++++ b/drivers/pci/controller/Kconfig
+@@ -280,5 +280,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 d56a507495c5..c3b76ff221be 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 000000000000..8e65059e4cf0
+--- /dev/null
++++ b/drivers/pci/controller/pcie-microsemi.c
+@@ -0,0 +1,778 @@
++/*
++ * PCIe host controller driver for Microsemi AXI PCIe Bridge
++ *
++ * Copyright (c) 2018 - Microsemi.
++ *
++ * Based on:
++ *  pcie-rcar.c
++ *  pcie-xilinx.c
++ *  pcie-altera.c
++ *
++ * 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/bitmap.h>
++#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
++#define MICROSEMI_NUM_MSI_IRQS_CODED         5
++
++/* PCIe Bridge Phy and Controller Phy offsets */
++#define MICROSEMI_PCIE0_BRIDGE_ADDR                   0x00004000u
++#define MICROSEMI_PCIE0_CTRL_ADDR                     0x00006000u
++
++#define MICROSEMI_PCIE1_BRIDGE_ADDR                   0x00008000u
++#define MICROSEMI_PCIE1_CTRL_ADDR                     0x0000A000u
++
++/* PCIe Controller Phy Regs */
++#define MICROSEMI_SEC_ERROR_INT                       0x28
++#define  SEC_ERROR_INT_TX_RAM_SEC_ERR_INT              GENMASK(3,0)
++#define  SEC_ERROR_INT_RX_RAM_SEC_ERR_INT              GENMASK(7,4)
++#define  SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT        GENMASK(11,8)
++#define  SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT        GENMASK(15,12)
++#define MICROSEMI_SEC_ERROR_INT_MASK                  0x2c
++#define MICROSEMI_DED_ERROR_INT                       0x30
++#define  DED_ERROR_INT_TX_RAM_DED_ERR_INT              GENMASK(3,0)
++#define  DED_ERROR_INT_RX_RAM_DED_ERR_INT              GENMASK(7,4)
++#define  DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT        GENMASK(11,8)
++#define  DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT        GENMASK(15,12)
++#define MICROSEMI_DED_ERROR_INT_MASK                  0x34
++#define MICROSEMI_ECC_CONTROL                         0x38
++#define  ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS           BIT(27)
++#define  ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS           BIT(26)
++#define  ECC_CONTROL_RX_RAM_ECC_BYPASS                 BIT(25)
++#define  ECC_CONTROL_TX_RAM_ECC_BYPASS                 BIT(24)
++#define MICROSEMI_LTSSM_STATE                         0x5c
++#define  MICROSEMI_LTSSM_L0_STATE                      0x10
++#define MICROSEMI_PCIE_EVENT_INT                      0x14c
++#define  PCIE_EVENT_INT_L2_EXIT_INT                    BIT(0)
++#define  PCIE_EVENT_INT_HOTRST_EXIT_INT                BIT(1)
++#define  PCIE_EVENT_INT_DLUP_EXIT_INT                  BIT(2)
++#define  PCIE_EVENT_INT_L2_EXIT_INT_MASK               BIT(16)
++#define  PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK           BIT(17)
++#define  PCIE_EVENT_INT_DLUP_EXIT_INT_MASK             BIT(18)
++
++/* PCIe Bridge Phy Regs */
++#define MICROSEMI_PCIE_PCI_IDS_DW1                    0x9c
++#define MICROSEMI_IMASK_LOCAL                         0x180
++#define  MICROSEMI_PCIE_LOCAL_INT_ENABLE               0x0F000000u
++#define  MICROSEMI_PCI_INTS                            0x0F000000u
++#define  MICROSEMI_PM_MSI_INT_SHIFT                    24
++#define  MICROSEMI_PCIE_ENABLE_MSI                     0x10000000u
++#define  MICROSEMI_MSI_INT                             0x10000000u
++#define  MICROSEMI_MSI_INT_SHIFT                       28
++#define MICROSEMI_ISTATUS_LOCAL                       0x184
++#define MICROSEMI_IMASK_HOST                          0x188
++#define MICROSEMI_ISTATUS_HOST                        0x18c
++#define MICROSEMI_ISTATUS_MSI                         0x194
++
++/* PCIe AXI slave table init defines */
++#define MICROSEMI_ATR0_AXI4_SLV0_SRCADDR_PARAM        0x800u
++#define  ATR_SIZE_SHIFT                                (1)
++#define  ATR_IMPL_ENABLE                               (1)
++#define MICROSEMI_ATR0_AXI4_SLV0_SRC_ADDR             0x804u
++#define MICROSEMI_ATR0_AXI4_SLV0_TRSL_ADDR_LSB        0x808u
++#define MICROSEMI_ATR0_AXI4_SLV0_TRSL_ADDR_UDW        0x80cu
++#define MICROSEMI_ATR0_AXI4_SLV0_TRSL_PARAM           0x810u
++#define  MICROSEMI_PCIE_TX_RX_INTERFACE                0x00000000u
++#define  MICROSEMI_PCIE_CONFIG_INTERFACE               0x00000001u
++
++
++#define MICROSEMI_ATR0_AXI4_SLV_SIZE                  (32)
++
++/* PCIe Master table init defines */
++#define MICROSEMI_ATR0_PCIE_WIN0_SRCADDR_31_12        0x600u
++#define  MICROSEMI_ATR0_PCIE_WIN0_SIZE                 (0x1f)
++#define  MICROSEMI_ATR0_PCIE_WIN0_SIZE_SHIFT           (1)
++#define MICROSEMI_ATR0_PCIE_WIN0_SRCADDR_63_32        0x604u
++
++#define MICROSEMI_MSI_MSG_ADDR                        0x190u
++
++/* PCIe Config space MSI capability structure */
++#define MSI_CAP_CTRL_OFFSET                 0xE0u
++#define  MSI_ENABLE                          (0x01u << 16)
++#define  MSI_ENABLE_MULTI                    (MICROSEMI_NUM_MSI_IRQS_CODED << 20)
++#define MSI_MSG_ADDR_OFFSET                 0xE4u
++#define MSI_MSG_UPPER_ADDR_OFFSET           0xE8u
++#define MSI_MSG_DATA_OFFSET                 0xF0u
++
++
++/**
++ * struct microsemi_pcie_port - PCIe port information
++ * @pcie_base_addr: IO Mapped Register Base
++ * @axi_base_addr: AMBA 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 *pcie_base_addr;
++        void __iomem *axi_base_addr;
++        int bridge;
++        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->pcie_base_addr + reg);
++}
++
++static inline void pcie_write(struct microsemi_pcie_port *port,
++                                u32 val, u32 reg)
++{
++        writel(val, port->pcie_base_addr + reg);
++}
++
++static inline bool microsemi_pcie_link_up(struct microsemi_pcie_port *port)
++{
++        void *base_addr;
++
++        if(port->bridge == 0)
++            base_addr = port->axi_base_addr + MICROSEMI_PCIE0_CTRL_ADDR;
++        else
++            base_addr = port->axi_base_addr + MICROSEMI_PCIE1_CTRL_ADDR;
++
++        return (readl(base_addr + MICROSEMI_LTSSM_STATE)
++                & MICROSEMI_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->pcie_base_addr + 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 = upper_32_bits(MICROSEMI_MSI_MSG_ADDR);
++        msg.address_lo = lower_32_bits(MICROSEMI_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_OFFSET);
++
++        pcie_write(port, cap_ctrl | MSI_ENABLE_MULTI | 
++                       MSI_ENABLE, MSI_CAP_CTRL_OFFSET);
++        pcie_write(port, MICROSEMI_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;
++        void *base_addr;
++
++        if(port->bridge == 0)
++            base_addr = port->axi_base_addr + MICROSEMI_PCIE0_BRIDGE_ADDR;
++        else
++            base_addr = port->axi_base_addr + MICROSEMI_PCIE1_BRIDGE_ADDR;
++
++        status = readl(base_addr + MICROSEMI_ISTATUS_LOCAL);
++
++        status = (status & MICROSEMI_PCI_INTS) >> MICROSEMI_PM_MSI_INT_SHIFT;
++        for_each_set_bit(bit, &status, PCI_NUM_INTX) {
++                /* clear that interrupt bit */
++                writel(1 << (bit+MICROSEMI_PM_MSI_INT_SHIFT),
++                         base_addr + MICROSEMI_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(base_addr + MICROSEMI_ISTATUS_LOCAL);
++        if (status & MICROSEMI_MSI_INT) {
++                // Clear the ISTATUS MSI bit
++                writel((1 << MICROSEMI_MSI_INT_SHIFT), base_addr + MICROSEMI_ISTATUS_LOCAL);
++                msi = readl(base_addr + MICROSEMI_ISTATUS_MSI);
++                for_each_set_bit(bit, &msi, MICROSEMI_NUM_MSI_IRQS) {
++                        /* clear that MSI interrupt bit */
++                        writel((1 << bit),
++                                base_addr + MICROSEMI_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;
++        void *bridge_base_addr;
++
++        if(port->bridge == 0) {
++                bridge_base_addr =  port->axi_base_addr + MICROSEMI_PCIE0_BRIDGE_ADDR;
++        } else {
++                bridge_base_addr =  port->axi_base_addr + MICROSEMI_PCIE1_BRIDGE_ADDR;
++        }
++
++        /* 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);
++        }
++
++        /* Enable interrupts */
++        writel(MICROSEMI_PCIE_ENABLE_MSI | MICROSEMI_PCIE_LOCAL_INT_ENABLE,
++                bridge_base_addr + MICROSEMI_IMASK_LOCAL);
++
++        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 atr_sz;
++        const char *type;
++        int err;
++        void *bridge_base_addr;
++        void *ctrl_base_addr;
++        u32 val;
++        int index = 1;
++
++        type = of_get_property(node, "device_type", NULL);
++        if (!type || strcmp(type, "pci")) {
++                dev_err(dev, "invalid \"device_type\" %s\n", type);
++                return -EINVAL;
++        }
++
++        /* Only supporting bridge 1 */
++        port->bridge = 1;
++
++        err = of_address_to_resource(node, 0, &regs);
++        if (err) {
++                dev_err(dev, "missing \"reg\" property\n");
++                return err;
++        }
++
++        port->pcie_base_addr = devm_pci_remap_cfg_resource(dev, &regs);
++        if (IS_ERR(port->pcie_base_addr))
++                return PTR_ERR(port->pcie_base_addr);
++
++        err = of_address_to_resource(node, 1, &regs1);
++        if (err) {
++                dev_err(dev, "missing \"reg\" property\n");
++                return err;
++        }
++
++        port->axi_base_addr = devm_ioremap_resource(dev, &regs1);
++        if (IS_ERR(port->axi_base_addr))
++                return PTR_ERR(port->axi_base_addr);
++
++        if(port->bridge == 0) {
++                bridge_base_addr =  port->axi_base_addr + MICROSEMI_PCIE0_BRIDGE_ADDR;
++                ctrl_base_addr = port->axi_base_addr + MICROSEMI_PCIE0_CTRL_ADDR;
++        } else {
++                bridge_base_addr =  port->axi_base_addr + MICROSEMI_PCIE1_BRIDGE_ADDR;
++                ctrl_base_addr = port->axi_base_addr + MICROSEMI_PCIE1_CTRL_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 */
++        val = ECC_CONTROL_AXI2PCIE_RAM_ECC_BYPASS
++            | ECC_CONTROL_PCIE2AXI_RAM_ECC_BYPASS
++            | ECC_CONTROL_RX_RAM_ECC_BYPASS
++            | ECC_CONTROL_TX_RAM_ECC_BYPASS;
++        writel(val, ctrl_base_addr + MICROSEMI_ECC_CONTROL);
++
++        val = PCIE_EVENT_INT_L2_EXIT_INT
++            | PCIE_EVENT_INT_HOTRST_EXIT_INT
++            | PCIE_EVENT_INT_DLUP_EXIT_INT
++            | PCIE_EVENT_INT_L2_EXIT_INT_MASK
++            | PCIE_EVENT_INT_HOTRST_EXIT_INT_MASK
++            | PCIE_EVENT_INT_DLUP_EXIT_INT_MASK;
++        writel(val, ctrl_base_addr + MICROSEMI_PCIE_EVENT_INT);
++
++        val = SEC_ERROR_INT_TX_RAM_SEC_ERR_INT
++            | SEC_ERROR_INT_RX_RAM_SEC_ERR_INT
++            | SEC_ERROR_INT_PCIE2AXI_RAM_SEC_ERR_INT
++            | SEC_ERROR_INT_AXI2PCIE_RAM_SEC_ERR_INT;
++        writel(val, ctrl_base_addr + MICROSEMI_SEC_ERROR_INT);
++        writel(val, ctrl_base_addr + MICROSEMI_SEC_ERROR_INT_MASK);
++
++        val = DED_ERROR_INT_TX_RAM_DED_ERR_INT
++            | DED_ERROR_INT_RX_RAM_DED_ERR_INT
++            | DED_ERROR_INT_PCIE2AXI_RAM_DED_ERR_INT
++            | DED_ERROR_INT_AXI2PCIE_RAM_DED_ERR_INT;
++        writel(val, ctrl_base_addr + MICROSEMI_DED_ERROR_INT);
++        writel(val, ctrl_base_addr + MICROSEMI_DED_ERROR_INT_MASK);
++
++        writel(0x00000000, bridge_base_addr + MICROSEMI_IMASK_LOCAL);
++        writel(GENMASK(31,0), bridge_base_addr + MICROSEMI_ISTATUS_LOCAL);
++        writel(0x00000000, bridge_base_addr + MICROSEMI_IMASK_HOST);
++        writel(GENMASK(31,0), bridge_base_addr + MICROSEMI_ISTATUS_HOST);
++
++        dev_info(dev, "interrupt disabled\n");
++
++        /* Configure Address Translation Table 0 for pcie config space */
++        writel(MICROSEMI_PCIE_CONFIG_INTERFACE,
++                bridge_base_addr + MICROSEMI_ATR0_AXI4_SLV0_TRSL_PARAM);
++
++        size = resource_size(&regs);
++
++        atr_sz = find_first_bit((const unsigned long *)&size, 64) - 1;
++
++        writel(lower_32_bits(regs.start) | atr_sz << ATR_SIZE_SHIFT | ATR_IMPL_ENABLE,
++                bridge_base_addr + MICROSEMI_ATR0_AXI4_SLV0_SRCADDR_PARAM);
++
++        //writel(upper_32_bits(regs.start),
++        //    bridge_base_addr + MICROSEMI_ATR0_AXI4_SLV0_SRC_ADDR);
++
++        writel(lower_32_bits(regs.start),
++                bridge_base_addr + MICROSEMI_ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
++
++        //writel(upper_32_bits(regs.start),
++        //        bridge_base_addr + MICROSEMI_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;
++                    atr_sz = find_first_bit((const unsigned long *)&size, 64) - 1;
++
++                    /* Configure Address Translation Table index for pcie mem space */
++                    writel(MICROSEMI_PCIE_TX_RX_INTERFACE,
++                        bridge_base_addr + (index * MICROSEMI_ATR0_AXI4_SLV_SIZE ) + 
++                        MICROSEMI_ATR0_AXI4_SLV0_TRSL_PARAM);
++
++                    writel(lower_32_bits(range.cpu_addr) | atr_sz << ATR_SIZE_SHIFT | ATR_IMPL_ENABLE,
++                        bridge_base_addr + (index * MICROSEMI_ATR0_AXI4_SLV_SIZE ) + 
++                        MICROSEMI_ATR0_AXI4_SLV0_SRCADDR_PARAM);
++
++                    //writel(upper_32_bits(range.cpu_addr),
++                    //    bridge_base_addr + (index * MICROSEMI_ATR0_AXI4_SLV_SIZE ) + 
++                    //    MICROSEMI_ATR0_AXI4_SLV0_SRC_ADDR);
++
++                    writel(lower_32_bits(range.pci_addr),
++                        bridge_base_addr + (index * MICROSEMI_ATR0_AXI4_SLV_SIZE ) + 
++                        MICROSEMI_ATR0_AXI4_SLV0_TRSL_ADDR_LSB);
++
++                    //writel(upper_32_bits(range.pci_addr),
++                    //    bridge_base_addr + (index * MICROSEMI_ATR0_AXI4_SLV_SIZE ) + 
++                    //    MICROSEMI_ATR0_AXI4_SLV0_TRSL_ADDR_UDW);
++
++                    break;
++                }
++                index++;
++        }
++
++        writel(readl(bridge_base_addr + MICROSEMI_ATR0_PCIE_WIN0_SRCADDR_31_12)
++                | MICROSEMI_ATR0_PCIE_WIN0_SIZE << MICROSEMI_ATR0_PCIE_WIN0_SIZE_SHIFT,
++                bridge_base_addr + MICROSEMI_ATR0_PCIE_WIN0_SRCADDR_31_12);
++
++        writel(0x0, bridge_base_addr + MICROSEMI_ATR0_PCIE_WIN0_SRCADDR_63_32);
++
++        writel((readl(bridge_base_addr + MICROSEMI_PCIE_PCI_IDS_DW1) & 0xffff)
++                | (PCI_CLASS_BRIDGE_PCI << 16),
++                bridge_base_addr + MICROSEMI_PCIE_PCI_IDS_DW1);
++
++        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);
++
++        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.21.0
+

+ 41 - 0
recipes-kernel/linux/files/freedom-u540/0009-HACK-Revert-of-device-Really-only-set-bus-DMA-mask-w.patch

@@ -0,0 +1,41 @@
+From 9b3b6bc3456ac35aaf99a3182f8a40485abef67b Mon Sep 17 00:00:00 2001
+From: Alistair Francis <alistair.francis@wdc.com>
+Date: Thu, 21 Feb 2019 16:29:37 -0800
+Subject: [PATCH 09/10] HACK: Revert "of/device: Really only set bus DMA mask
+ when appropriate"
+
+This reverts commit 6778be4e520959659b27a441c06a84c9cb009085.
+
+Reverting the commit fixes these error messages and an non-functioning
+USB bus when attaching a USB to PCIe card to a RISC-V board:
+    xhci_hcd 0000:03:00.0: Host took too long to start, waited 16000 microseconds.
+    xhci_hcd 0000:03:00.0: startup error -19
+    xhci_hcd 0000:03:00.0: USB bus 2 deregistered
+    xhci_hcd 0000:03:00.0: WARNING: Host System Error
+    xhci_hcd 0000:03:00.0: remove, state 1
+
+Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
+---
+ drivers/of/device.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/drivers/of/device.c b/drivers/of/device.c
+index 3717f2a20d0d..8299f8055da7 100644
+--- a/drivers/of/device.c
++++ b/drivers/of/device.c
+@@ -149,11 +149,9 @@ int of_dma_configure(struct device *dev, struct device_node *np, bool force_dma)
+ 	 * set by the driver.
+ 	 */
+ 	mask = DMA_BIT_MASK(ilog2(dma_addr + size - 1) + 1);
++	dev->bus_dma_mask = mask;
+ 	dev->coherent_dma_mask &= mask;
+ 	*dev->dma_mask &= mask;
+-	/* ...but only set bus mask if we found valid dma-ranges earlier */
+-	if (!ret)
+-		dev->bus_dma_mask = mask;
+ 
+ 	coherent = of_dma_is_coherent(np);
+ 	dev_dbg(dev, "device is%sdma coherent\n",
+-- 
+2.21.0
+

+ 44 - 0
recipes-kernel/linux/files/freedom-u540/0010-HACK-radeon-Don-t-set-PCI-DMA-mask.patch

@@ -0,0 +1,44 @@
+From 820e6a80587c0191c630641583e8d98687942146 Mon Sep 17 00:00:00 2001
+From: Alistair Francis <alistair.francis@wdc.com>
+Date: Wed, 27 Feb 2019 15:31:29 -0800
+Subject: [PATCH 10/10] HACK: radeon: Don't set PCI DMA mask
+
+Setting a PCI DMA mask when running on RISC-V results in these messages
+beeing seen during boot:
+
+    [drm:r600_ring_test] *ERROR* radeon: ring 0 test failed (scratch(0x8504)=0xCAFEDEAD)
+    radeon 0000:04:00.0: disabling GPU acceleration
+
+and results with a partially working graphcis card. By not setting a DMA
+mask the messages aren't seen and the GPU works.
+
+Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
+---
+ drivers/gpu/drm/radeon/radeon_device.c | 11 +++++------
+ 1 file changed, 5 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
+index 53f29a115104..add186346544 100644
+--- a/drivers/gpu/drm/radeon/radeon_device.c
++++ b/drivers/gpu/drm/radeon/radeon_device.c
+@@ -1377,12 +1377,11 @@ int radeon_device_init(struct radeon_device *rdev,
+ #endif
+ 
+ 	dma_bits = rdev->need_dma32 ? 32 : 40;
+-	r = pci_set_dma_mask(rdev->pdev, DMA_BIT_MASK(dma_bits));
+-	if (r) {
+-		rdev->need_dma32 = true;
+-		dma_bits = 32;
+-		pr_warn("radeon: No suitable DMA available\n");
+-	}
++
++	rdev->need_dma32 = true;
++	dma_bits = 32;
++	pr_warn("radeon: No suitable DMA available\n");
++
+ 	r = pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(dma_bits));
+ 	if (r) {
+ 		pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(32));
+-- 
+2.21.0
+

+ 35 - 0
recipes-kernel/linux/linux-mainline_5.1.bb

@@ -0,0 +1,35 @@
+require recipes-kernel/linux/linux-riscv-common.inc
+
+DESCRIPTION = "Mainline Linux Kernel"
+
+LINUX_VERSION ?= "5.1"
+LINUX_VERSION_EXTENSION = ""
+
+# Update this to point to the stable release branches after the official release
+BRANCH = "master"
+SRCREV = "${AUTOREV}"
+SRC_URI = " \
+    git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git;branch=${BRANCH} \
+"
+
+# Out of tree patches required for HiFive Unleashed
+SRC_URI_append_freedom-u540 = " \
+    file://0001-tty-serial-add-driver-for-the-SiFive-UART.patch \
+    file://0002-gemgxl-mgmt-implement-clock-switch-for-GEM-tx_clk.patch \
+    file://0003-u54-prci-driver-for-core-U54-clocks.patch \
+    file://0004-u54-prci-driver-for-core-U54-clocks.patch \
+    file://0005-gpio-sifive-support-GPIO-on-SiFive-SoCs.patch \
+    file://0006-pwm-sifive-add-a-driver-for-SiFive-SoC-PWM.patch \
+    file://0007-RISC-V-Networking-fix-Hack.patch \
+    file://0008-pcie-microsemi-Add-support-for-the-Vera-board-root-c.patch \
+    file://0009-HACK-Revert-of-device-Really-only-set-bus-DMA-mask-w.patch \
+    file://0010-HACK-radeon-Don-t-set-PCI-DMA-mask.patch \
+"
+
+# qemu uses in-tree defconfig
+# freedom-u540 uses out-of-tree defconfig
+SRC_URI_append_freedom-u540 = " file://defconfig"
+
+SRC_URI_append_qemuriscv32 = " file://32bit.cfg"
+
+KERNEL_VERSION_SANITY_SKIP = "1"