diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2021-10-07 14:41:56 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2021-10-07 14:41:56 +0200 |
commit | aa54b4be0a467d6180d4113fc5dbba453202ac6b (patch) | |
tree | f820774cec4f011d41604dd22a8ef00d9c7c7c3c | |
parent | 89737fc887e9bbfe20e20555e0778ac84dfb3bd8 (diff) | |
parent | 419b094b7c7466330eb4533d7b722f16d89d6563 (diff) | |
download | barebox-aa54b4be0a467d6180d4113fc5dbba453202ac6b.tar.gz barebox-aa54b4be0a467d6180d4113fc5dbba453202ac6b.tar.xz |
Merge branch 'for-next/riscv'
28 files changed, 1404 insertions, 16 deletions
diff --git a/arch/riscv/Kconfig.socs b/arch/riscv/Kconfig.socs index 221ea133d4..bd4a44a575 100644 --- a/arch/riscv/Kconfig.socs +++ b/arch/riscv/Kconfig.socs @@ -22,6 +22,14 @@ config SOC_VIRT Generates an image tht can be be booted by QEMU. The image is called barebox-dt-2nd.img +config BOARD_RISCVEMU + depends on SOC_VIRT + bool "TinyEMU Virt Machine (riscvemu)" + default y + help + TinyEMU's Virt machine differs from QEMU in poweroff and restart + mechanisms. This adds the necessary support. + config CPU_SIFIVE bool select HAS_CACHE @@ -95,4 +103,18 @@ config SIFIVE_L2 bool "SiFive L2 cache controller" depends on CPU_SIFIVE +config SOC_LITEX + bool "LiteX SoCs" + depends on ARCH_RV32I + select HAS_ASM_DEBUG_LL + select HAS_NMON + select USE_COMPRESSED_DTB + select RISCV_TIMER + +config BOARD_LITEX_LINUX + bool "litex linux board" + depends on SOC_LITEX + select RISCV_M_MODE + def_bool y + endmenu diff --git a/arch/riscv/boards/Makefile b/arch/riscv/boards/Makefile index cb28a25d8b..3b763ff308 100644 --- a/arch/riscv/boards/Makefile +++ b/arch/riscv/boards/Makefile @@ -2,3 +2,5 @@ obj-$(CONFIG_BOARD_ERIZO_GENERIC) += erizo/ obj-$(CONFIG_BOARD_HIFIVE) += hifive/ obj-$(CONFIG_BOARD_BEAGLEV) += beaglev/ +obj-$(CONFIG_BOARD_LITEX_LINUX) += litex-linux/ +obj-$(CONFIG_BOARD_RISCVEMU) += riscvemu/ diff --git a/arch/riscv/boards/litex-linux/Makefile b/arch/riscv/boards/litex-linux/Makefile new file mode 100644 index 0000000000..3d217ffe0b --- /dev/null +++ b/arch/riscv/boards/litex-linux/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +pbl-y += lowlevel.o diff --git a/arch/riscv/boards/litex-linux/lowlevel.c b/arch/riscv/boards/litex-linux/lowlevel.c new file mode 100644 index 0000000000..da23ef5633 --- /dev/null +++ b/arch/riscv/boards/litex-linux/lowlevel.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <common.h> +#include <asm/barebox-riscv.h> +#include <debug_ll.h> +#include <asm/riscv_nmon.h> + +ENTRY_FUNCTION(start_litex_linux, a0, a1, a2) +{ + extern char __dtb_z_litex_linux_start[]; + void *fdt; + + barebox_nmon_entry(); + + putc_ll('>'); + + /* On POR, we are running from read-only memory here. */ + + fdt = __dtb_z_litex_linux_start + get_runtime_offset(); + + barebox_riscv_machine_entry(0x40000000, SZ_256M, fdt); +} diff --git a/arch/riscv/boards/riscvemu/Makefile b/arch/riscv/boards/riscvemu/Makefile new file mode 100644 index 0000000000..dcfc2937d3 --- /dev/null +++ b/arch/riscv/boards/riscvemu/Makefile @@ -0,0 +1 @@ +obj-y += board.o diff --git a/arch/riscv/boards/riscvemu/board.c b/arch/riscv/boards/riscvemu/board.c new file mode 100644 index 0000000000..7dbf9afe4c --- /dev/null +++ b/arch/riscv/boards/riscvemu/board.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Ahmad Fatoum, Pengutronix + */ + +#include <common.h> +#include <driver.h> +#include <poweroff.h> +#include <restart.h> +#include <asm/system.h> +#include <asm/barebox-riscv.h> + +struct riscvemu_priv { + struct restart_handler rst; + void __noreturn (*restart)(unsigned long, void *); + +}; + +#define HTIF_BASE_ADDR IOMEM(0x40008000) +#define HTIF_TOHOST_LOW (HTIF_BASE_ADDR + 0) +#define HTIF_TOHOST_HIGH (HTIF_BASE_ADDR + 4) + +static void __noreturn riscvemu_poweroff(struct poweroff_handler *pwr) +{ + writel(1, HTIF_TOHOST_LOW); + writel(0, HTIF_TOHOST_HIGH); + + __builtin_unreachable(); +} + +static void __noreturn riscvemu_restart(struct restart_handler *rst) +{ + struct riscvemu_priv *priv = container_of(rst, struct riscvemu_priv, rst); + + /* + * barebox PBL relocates itself to end of RAM early on, so unless + * something explicitly scrubbed the initial PBL, we can jump back to + * the reset vector to "reset". + */ + priv->restart(riscv_hartid(), barebox_riscv_boot_dtb()); +} + +static int riscvemu_probe(struct device_d *dev) +{ + struct device_node *of_chosen; + struct riscvemu_priv *priv; + u64 start; + + if (of_find_compatible_node(NULL, NULL, "ucb,htif0")) + poweroff_handler_register_fn(riscvemu_poweroff); + + of_chosen = of_find_node_by_path("/chosen"); + + if (of_property_read_u64(of_chosen, "riscv,kernel-start", &start)) + return 0; + + priv = xzalloc(sizeof(*priv)); + + priv->restart = (void *)(uintptr_t)start; + priv->rst.restart = riscvemu_restart; + priv->rst.name = "vector"; + + return restart_handler_register(&priv->rst); +} + +static const struct of_device_id riscvemu_of_match[] = { + { .compatible = "ucbbar,riscvemu-bar_dev" }, + { /* sentinel */ }, +}; + +static struct driver_d riscvemu_board_driver = { + .name = "board-riscvemu", + .probe = riscvemu_probe, + .of_compatible = riscvemu_of_match, +}; +device_platform_driver(riscvemu_board_driver); diff --git a/arch/riscv/configs/litex_linux_defconfig b/arch/riscv/configs/litex_linux_defconfig new file mode 100644 index 0000000000..8e19964890 --- /dev/null +++ b/arch/riscv/configs/litex_linux_defconfig @@ -0,0 +1,75 @@ +CONFIG_SOC_LITEX=y +CONFIG_STACK_SIZE=0x20000 +CONFIG_MALLOC_SIZE=0xf00000 +CONFIG_MALLOC_TLSF=y +CONFIG_PANIC_HANG=y +CONFIG_BAUDRATE=1000000 +CONFIG_HUSH_FANCY_PROMPT=y +CONFIG_CMDLINE_EDITING=y +CONFIG_AUTO_COMPLETE=y +# CONFIG_TIMESTAMP is not set +CONFIG_BOOTM_SHOW_TYPE=y +CONFIG_BOOTM_VERBOSE=y +CONFIG_BOOTM_INITRD=y +CONFIG_BOOTM_OFTREE=y +CONFIG_BOOTM_OFTREE_UIMAGE=y +CONFIG_DEFAULT_ENVIRONMENT_GENERIC_NEW=y +CONFIG_DEBUG_LL=y +CONFIG_LONGHELP=y +CONFIG_CMD_IOMEM=y +CONFIG_CMD_IMD=y +CONFIG_CMD_MEMINFO=y +CONFIG_CMD_GO=y +CONFIG_CMD_LOADY=y +CONFIG_CMD_EXPORT=y +CONFIG_CMD_CMP=y +CONFIG_CMD_MD5SUM=y +CONFIG_CMD_SHA1SUM=y +CONFIG_CMD_MSLEEP=y +CONFIG_CMD_SLEEP=y +CONFIG_CMD_DHCP=y +CONFIG_CMD_MIITOOL=y +CONFIG_CMD_PING=y +CONFIG_CMD_TFTP=y +CONFIG_CMD_ECHO_E=y +CONFIG_CMD_EDIT=y +CONFIG_CMD_MEMTEST=y +CONFIG_CMD_MM=y +CONFIG_CMD_CLK=y +CONFIG_CMD_GPIO=y +CONFIG_CMD_LED=y +CONFIG_CMD_SPI=y +CONFIG_CMD_LED_TRIGGER=y +CONFIG_CMD_OF_DUMP=y +CONFIG_CMD_TIME=y +CONFIG_CMD_DHRYSTONE=y +CONFIG_NET=y +CONFIG_NET_NFS=y +CONFIG_NET_NETCONSOLE=y +CONFIG_DRIVER_NET_LITEETH=y +CONFIG_AR8327N_PHY=y +CONFIG_AT803X_PHY=y +CONFIG_DAVICOM_PHY=y +CONFIG_DP83867_PHY=y +CONFIG_LXT_PHY=y +CONFIG_MARVELL_PHY=y +CONFIG_MICREL_PHY=y +CONFIG_NATIONAL_PHY=y +CONFIG_REALTEK_PHY=y +CONFIG_SMSC_PHY=y +CONFIG_DRIVER_SPI_LITEX_SPIFLASH=y +CONFIG_MTD=y +# CONFIG_MTD_OOB_DEVICE is not set +CONFIG_MTD_M25P80=y +CONFIG_CLOCKSOURCE_DUMMY_RATE=20000 +CONFIG_SRAM=y +CONFIG_LED=y +CONFIG_LED_GPIO=y +CONFIG_LED_GPIO_OF=y +CONFIG_LED_TRIGGERS=y +CONFIG_GPIO_74XX_MMIO=y +CONFIG_GPIO_GENERIC_PLATFORM=y +# CONFIG_PINCTRL is not set +CONFIG_FS_TFTP=y +CONFIG_FS_NFS=y +CONFIG_DIGEST_CRC32_GENERIC=y diff --git a/arch/riscv/dts/Makefile b/arch/riscv/dts/Makefile index 4a15423b7f..17d7d249e4 100644 --- a/arch/riscv/dts/Makefile +++ b/arch/riscv/dts/Makefile @@ -8,5 +8,6 @@ pbl-$(CONFIG_BOARD_ERIZO_GENERIC) += erizo-generic.dtb.o pbl-$(CONFIG_BOARD_HIFIVE) += hifive-unmatched-a00.dtb.o \ hifive-unleashed-a00.dtb.o pbl-$(CONFIG_BOARD_BEAGLEV) += jh7100-beaglev-starlight.dtb.o +pbl-$(CONFIG_BOARD_LITEX_LINUX) += litex-linux.dtb.o clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts diff --git a/arch/riscv/dts/erizo.dtsi b/arch/riscv/dts/erizo.dtsi index 228711bd69..4eb92ae6f1 100644 --- a/arch/riscv/dts/erizo.dtsi +++ b/arch/riscv/dts/erizo.dtsi @@ -22,6 +22,8 @@ timebase-frequency = <24000000>; + barebox,csr-cycle; + cpu@0 { device_type = "cpu"; compatible = "cliffordwolf,picorv32", "riscv"; diff --git a/arch/riscv/dts/litex-linux.dts b/arch/riscv/dts/litex-linux.dts new file mode 100644 index 0000000000..d21fa57e30 --- /dev/null +++ b/arch/riscv/dts/litex-linux.dts @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "litex_soc_linux.dtsi" + +#include <dt-bindings/gpio/gpio.h> + +/ { + model = "LiteX VexRiscV-SoC-Linux"; + compatible = "litex,vexriscv-soc-linux"; + + aliases { + rom = &rom; + sram = &sram; + }; + + /* ARTY board */ + rom: rom@00000000 { + compatible = "mmio-sram"; + reg = <0x00000000 0x00008000>; + read-only; + }; + + sram: sram@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x00004000>; + }; + + main_ram: memory@40000000 { + device_type = "memory"; + reg = <0x40000000 0x10000000>; + }; +}; + +&uart0 { + status = "okay"; +}; + +&mac0 { + status = "okay"; +}; + +&spi0 { + status = "okay"; + + spiflash: w25q128@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "winbond,w25q128", "jedec,spi-nor"; + spi-max-frequency = <104000000>; + reg = <0>; + }; +}; + +/ { + ledsgpio: gpio@f000a800 { + compatible = "ti,74175"; + reg = <0xf000a800 0x4>; + gpio-controller; + #gpio-cells = <2>; + }; + + leds { + compatible = "gpio-leds"; + + ld0 { + label = "arty:green:ld0"; + gpios = <&ledsgpio 0 GPIO_ACTIVE_HIGH>; + }; + + ld1 { + label = "arty:green:ld1"; + gpios = <&ledsgpio 1 GPIO_ACTIVE_HIGH>; + }; + + ld2 { + label = "arty:green:ld2"; + gpios = <&ledsgpio 2 GPIO_ACTIVE_HIGH>; + }; + + ld3 { + label = "arty:green:ld3"; + gpios = <&ledsgpio 3 GPIO_ACTIVE_HIGH>; + }; + }; + + swgpio: gpio@f0006000 { + compatible = "ti,74125"; + reg = <0xf0006000 0x4>; + gpio-controller; + #gpio-cells = <2>; + }; +}; diff --git a/arch/riscv/dts/litex_soc_linux.dtsi b/arch/riscv/dts/litex_soc_linux.dtsi new file mode 100644 index 0000000000..94a0ba29da --- /dev/null +++ b/arch/riscv/dts/litex_soc_linux.dtsi @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/dts-v1/; + +/ { + compatible = "litex,vexriscv-soc-linux"; + + #address-cells = <1>; + #size-cells = <1>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + timebase-frequency = <100000000>; // 100 MHz + + cpu@0 { + device_type = "cpu"; + compatible = "spinalhdl,vexriscv", "riscv"; + reg = <0>; + }; + }; + + uart0: serial@f0001000 { + compatible = "litex,uart"; + reg = <0xf0001000 0x18>; + status = "disabled"; + }; + + mac0: mac@f0006800 { + compatible = "litex,liteeth"; + reg = <0xf0006800 0x7c /* base */ + 0xf0007000 0x0a /* mdio_base */ + 0xb0000000 0x2000>; /* buf_base */ + tx-fifo-depth = <2>; + rx-fifo-depth = <2>; + status = "disabled"; + }; + + spi0: spi@f000b800 { + compatible = "litex,spiflash"; + + #address-cells = <1>; + #size-cells = <0>; + + reg = <0xf000b800 0x100>; + status = "disabled"; + }; +}; diff --git a/arch/riscv/include/asm/debug_ll.h b/arch/riscv/include/asm/debug_ll.h index b4caa0597a..a3b9c1c4bc 100644 --- a/arch/riscv/include/asm/debug_ll.h +++ b/arch/riscv/include/asm/debug_ll.h @@ -43,6 +43,9 @@ static inline void PUTC_LL(char ch) writel(ch, uart0); } +#elif defined CONFIG_DEBUG_LITEX + +#include <asm/debug_ll_litex.h> #endif diff --git a/arch/riscv/include/asm/debug_ll_litex.h b/arch/riscv/include/asm/debug_ll_litex.h new file mode 100644 index 0000000000..2fcdd9b0ec --- /dev/null +++ b/arch/riscv/include/asm/debug_ll_litex.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2019 Antony Pavlov <antonynpavlov@gmail.com> + * + * This file is part of barebox. + * + * 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. + * + */ + +#ifndef __ASM_DEBUG_LL_LITEX__ +#define __ASM_DEBUG_LL_LITEX__ + +/** @file + * This File contains declaration for early output support + */ + +#include <linux/kconfig.h> + +#define DEBUG_LL_UART_ADDR 0xf0001000 +#define UART_RXTX 0x00 +#define UART_TXFULL 0x04 +#define UART_RXEMPTY 0x08 +#define UART_EV_PENDING 0x10 +#define UART_EV_RX (1 << 1) + + +#ifndef __ASSEMBLY__ + +/* + * C macros + */ + +#include <asm/io.h> + +static inline void PUTC_LL(char ch) +{ +#ifdef CONFIG_DEBUG_LL + /* wait for space */ + while (__raw_readb((u8 *)DEBUG_LL_UART_ADDR + UART_TXFULL)) + ; + + __raw_writeb(ch, (u8 *)DEBUG_LL_UART_ADDR + UART_RXTX); +#endif /* CONFIG_DEBUG_LL */ +} +#else /* __ASSEMBLY__ */ +/* + * Macros for use in assembly language code + */ + +/* + * output a character in a0 + */ +.macro debug_ll_outc_a0 +#ifdef CONFIG_DEBUG_LL + + li t0, DEBUG_LL_UART_ADDR + +201: + lbu t1, UART_TXFULL(t0) /* uart tx full ? */ + andi t1, t1, 0xff + bnez t1, 201b /* try again */ + + sb a0, UART_RXTX(t0) /* write the character */ + +#endif /* CONFIG_DEBUG_LL */ +.endm + +/* + * output a character + */ +.macro debug_ll_outc chr +#ifdef CONFIG_DEBUG_LL + li a0, \chr + debug_ll_outc_a0 +#endif /* CONFIG_DEBUG_LL */ +.endm + +/* + * check character in input buffer + * return value: + * v0 = 0 no character in input buffer + * v0 != 0 character in input buffer + */ +.macro debug_ll_tstc +#ifdef CONFIG_DEBUG_LL + li t0, DEBUG_LL_UART_ADDR + + /* get line status and check for data present */ + lbu s0, UART_RXEMPTY(t0) + bnez s0, 243f + li s0, 1 + j 244f +243: li s0, 0 +244: nop +#endif /* CONFIG_DEBUG_LL */ +.endm + +/* + * get character to v0 + */ +.macro debug_ll_getc +#ifdef CONFIG_DEBUG_LL + +204: + debug_ll_tstc + + /* try again */ + beqz s0, 204b + + /* read a character */ + lb s0, UART_RXTX(t0) + li t1, UART_EV_RX + sb t1, UART_EV_PENDING(t0) + +#endif /* CONFIG_DEBUG_LL */ +.endm +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_DEBUG_LL_LITEX__ */ diff --git a/common/Kconfig b/common/Kconfig index 70debb9cd5..85fb63fb2c 100644 --- a/common/Kconfig +++ b/common/Kconfig @@ -1435,6 +1435,10 @@ config DEBUG_SIFIVE bool "SiFive serial0 port" depends on SOC_SIFIVE +config DEBUG_LITEX + bool "LiteX serial port" + depends on SOC_LITEX + endchoice config DEBUG_LL_NS16550 diff --git a/common/console.c b/common/console.c index 0368c72d0b..8727b187cf 100644 --- a/common/console.c +++ b/common/console.c @@ -165,12 +165,12 @@ int console_set_baudrate(struct console_device *cdev, unsigned baudrate) int ret; unsigned char c; - if (!cdev->setbrg) - return -ENOSYS; - if (cdev->baudrate == baudrate) return 0; + if (!cdev->setbrg) + return -ENOSYS; + /* * If the device is already active, change its baudrate. * The baudrate of an inactive device will be set at activation time. @@ -336,11 +336,13 @@ int console_register(struct console_device *newcdev) ret = newcdev->setbrg(newcdev, baudrate); if (ret) return ret; - newcdev->baudrate_param = newcdev->baudrate = baudrate; + newcdev->baudrate_param = baudrate; dev_add_param_uint32(dev, "baudrate", console_baudrate_set, NULL, &newcdev->baudrate_param, "%u", newcdev); } + newcdev->baudrate = baudrate; + if (newcdev->putc && !newcdev->puts) newcdev->puts = __console_puts; diff --git a/drivers/clocksource/timer-riscv.c b/drivers/clocksource/timer-riscv.c index 5a517fe6b4..96637f988a 100644 --- a/drivers/clocksource/timer-riscv.c +++ b/drivers/clocksource/timer-riscv.c @@ -12,9 +12,8 @@ #include <clock.h> #include <asm/timer.h> #include <asm/csr.h> -#include <asm/system.h> -static u64 notrace riscv_timer_get_count_sbi(void) +static u64 notrace riscv_timer_get_count_time(void) { __maybe_unused u32 hi, lo; @@ -29,7 +28,7 @@ static u64 notrace riscv_timer_get_count_sbi(void) return ((u64)hi << 32) | lo; } -static u64 notrace riscv_timer_get_count_rdcycle(void) +static u64 notrace riscv_timer_get_count_cycle(void) { __maybe_unused u32 hi, lo; @@ -44,24 +43,25 @@ static u64 notrace riscv_timer_get_count_rdcycle(void) return ((u64)hi << 32) | lo; } -static u64 notrace riscv_timer_get_count(void) -{ - if (riscv_mode() == RISCV_S_MODE) - return riscv_timer_get_count_sbi(); - else - return riscv_timer_get_count_rdcycle(); -} - static struct clocksource riscv_clocksource = { - .read = riscv_timer_get_count, .mask = CLOCKSOURCE_MASK(64), .priority = 100, }; static int riscv_timer_init(struct device_d* dev) { + struct device_node *cpu; + dev_dbg(dev, "running at %lu Hz\n", riscv_timebase); + cpu = of_find_node_by_path("/cpus"); + + if (of_property_read_bool(cpu, "barebox,csr-cycle")) { + riscv_clocksource.read = riscv_timer_get_count_cycle; + } else { + riscv_clocksource.read = riscv_timer_get_count_time; + } + riscv_clocksource.mult = clocksource_hz2mult(riscv_timebase, riscv_clocksource.shift); return init_clock(&riscv_clocksource); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 98a44fbbb5..f79c9ea67d 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -21,6 +21,20 @@ config GPIO_74164 shift registers. This driver can be used to provide access to more gpio outputs. +config GPIO_74XX_MMIO + tristate "GPIO driver for 74xx-ICs with MMIO access" + depends on OFDEVICE + select GPIO_GENERIC + help + Say yes here to support GPIO functionality for 74xx-compatible ICs + with MMIO access. Compatible models include: + 1 bit: 741G125 (Input), 741G74 (Output) + 2 bits: 742G125 (Input), 7474 (Output) + 4 bits: 74125 (Input), 74175 (Output) + 6 bits: 74365 (Input), 74174 (Output) + 8 bits: 74244 (Input), 74273 (Output) + 16 bits: 741624 (Input), 7416374 (Output) + config GPIO_CLPS711X bool "GPIO support for CLPS711X" depends on ARCH_CLPS711X || COMPILE_TEST diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 638cbb19a3..023973cb63 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o obj-$(CONFIG_GPIO_74164) += gpio-74164.o +obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o obj-$(CONFIG_MACH_MIPS_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c new file mode 100644 index 0000000000..5b688f4766 --- /dev/null +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 74xx MMIO GPIO driver + * + * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> + * + * Ported to barebox from linux-v5.4-rc6 + * Copyright (C) 2019-2021 Antony Pavlov <antonynpavlov@gmail.com> + * + */ + +#include <common.h> +#include <driver.h> +#include <errno.h> +#include <gpio.h> +#include <init.h> +#include <io.h> +#include <malloc.h> +#include <of_device.h> + +#include <linux/err.h> +#include <linux/basic_mmio_gpio.h> + +#define MMIO_74XX_DIR_IN (0 << 8) +#define MMIO_74XX_DIR_OUT (1 << 8) +#define MMIO_74XX_BIT_CNT(x) ((x) & 0xff) + +struct mmio_74xx_gpio_priv { + struct bgpio_chip bgc; + unsigned int flags; +}; + +static const struct of_device_id mmio_74xx_gpio_ids[] = { + { + .compatible = "ti,741g125", + .data = (const void *)(MMIO_74XX_DIR_IN | 1), + }, + { + .compatible = "ti,742g125", + .data = (const void *)(MMIO_74XX_DIR_IN | 2), + }, + { + .compatible = "ti,74125", + .data = (const void *)(MMIO_74XX_DIR_IN | 4), + }, + { + .compatible = "ti,74365", + .data = (const void *)(MMIO_74XX_DIR_IN | 6), + }, + { + .compatible = "ti,74244", + .data = (const void *)(MMIO_74XX_DIR_IN | 8), + }, + { + .compatible = "ti,741624", + .data = (const void *)(MMIO_74XX_DIR_IN | 16), + }, + { + .compatible = "ti,741g74", + .data = (const void *)(MMIO_74XX_DIR_OUT | 1), + }, + { + .compatible = "ti,7474", + .data = (const void *)(MMIO_74XX_DIR_OUT | 2), + }, + { + .compatible = "ti,74175", + .data = (const void *)(MMIO_74XX_DIR_OUT | 4), + }, + { + .compatible = "ti,74174", + .data = (const void *)(MMIO_74XX_DIR_OUT | 6), + }, + { + .compatible = "ti,74273", + .data = (const void *)(MMIO_74XX_DIR_OUT | 8), + }, + { + .compatible = "ti,7416374", + .data = (const void *)(MMIO_74XX_DIR_OUT | 16), + }, + { } +}; + +static inline +struct mmio_74xx_gpio_priv *to_mmio_74xx_gpio_priv(struct gpio_chip *gc) +{ + struct bgpio_chip *bgc = + container_of(gc, struct bgpio_chip, gc); + + return container_of(bgc, struct mmio_74xx_gpio_priv, bgc); +} + +static int mmio_74xx_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + struct mmio_74xx_gpio_priv *priv = to_mmio_74xx_gpio_priv(gc); + + if (priv->flags & MMIO_74XX_DIR_OUT) + return GPIOF_DIR_OUT; + + return GPIOF_DIR_IN; +} + +static int mmio_74xx_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + struct mmio_74xx_gpio_priv *priv = to_mmio_74xx_gpio_priv(gc); + + return (priv->flags & MMIO_74XX_DIR_OUT) ? -ENOTSUPP : 0; +} + +static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct mmio_74xx_gpio_priv *priv = to_mmio_74xx_gpio_priv(gc); + + if (priv->flags & MMIO_74XX_DIR_OUT) { + gc->ops->set(gc, gpio, val); + return 0; + } + + return -ENOTSUPP; +} + +static int mmio_74xx_gpio_probe(struct device_d *dev) +{ + struct mmio_74xx_gpio_priv *priv; + void __iomem *dat; + int err; + struct gpio_chip *gc; + + priv = xzalloc(sizeof(*priv)); + + priv->flags = (uintptr_t)of_device_get_match_data(dev); + + dat = dev_request_mem_region(dev, 0); + if (IS_ERR(dat)) + return PTR_ERR(dat); + + err = bgpio_init(&priv->bgc, dev, + DIV_ROUND_UP(MMIO_74XX_BIT_CNT(priv->flags), 8), + dat, NULL, NULL, NULL, NULL, 0); + if (err) + return err; + + gc = &priv->bgc.gc; + gc->ops->direction_input = mmio_74xx_dir_in; + gc->ops->direction_output = mmio_74xx_dir_out; + gc->ops->get_direction = mmio_74xx_get_direction; + gc->ngpio = MMIO_74XX_BIT_CNT(priv->flags); + + dev->priv = priv; + + return gpiochip_add(gc); +} + +static struct driver_d mmio_74xx_gpio_driver = { + .name = "74xx-mmio-gpio", + .of_compatible = DRV_OF_COMPAT(mmio_74xx_gpio_ids), + .probe = mmio_74xx_gpio_probe, +}; + +coredevice_platform_driver(mmio_74xx_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); +MODULE_DESCRIPTION("74xx MMIO GPIO driver"); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 4947296f27..48d07aca0e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -288,6 +288,14 @@ config TSE_USE_DEDICATED_DESC_MEM reserved with a malloc but directly mapped to the memory address (defined in config.h) +config DRIVER_NET_LITEETH + bool "LiteX ethernet driver" + select PHYLIB + select MDIO_BITBANG + help + This option enables support for the LiteX LiteEth + ethernet IP core. + source "drivers/net/phy/Kconfig" source "drivers/net/usb/Kconfig" diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 1921d0d9f9..32e81e0047 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -38,3 +38,4 @@ obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o obj-$(CONFIG_DRIVER_NET_EFI_SNP) += efi-snp.o obj-$(CONFIG_DRIVER_NET_VIRTIO) += virtio.o obj-$(CONFIG_DRIVER_NET_AG71XX) += ag71xx.o +obj-$(CONFIG_DRIVER_NET_LITEETH) += liteeth.o diff --git a/drivers/net/liteeth.c b/drivers/net/liteeth.c new file mode 100644 index 0000000000..137cb854c5 --- /dev/null +++ b/drivers/net/liteeth.c @@ -0,0 +1,376 @@ +/* + * LiteX Liteeth Ethernet + * + * Copyright 2017 Joel Stanley <joel@jms.id.au> + * + * Ported to barebox from linux kernel + * Copyright (C) 2019-2021 Antony Pavlov <antonynpavlov@gmail.com> + * Copyright (C) 2021 Marek Czerski <m.czerski@ap-tech.pl> + * + */ + +#include <common.h> +#include <io.h> +#include <linux/iopoll.h> +#include <malloc.h> +#include <net.h> +#include <init.h> +#include <of_net.h> +#include <linux/phy.h> +#include <linux/mdio-bitbang.h> + +#define DRV_NAME "liteeth" + +#define LITEETH_WRITER_SLOT 0x00 +#define LITEETH_WRITER_LENGTH 0x04 +#define LITEETH_WRITER_ERRORS 0x08 +#define LITEETH_WRITER_EV_STATUS 0x0c +#define LITEETH_WRITER_EV_PENDING 0x10 +#define LITEETH_WRITER_EV_ENABLE 0x14 +#define LITEETH_READER_START 0x18 +#define LITEETH_READER_READY 0x1c +#define LITEETH_READER_LEVEL 0x20 +#define LITEETH_READER_SLOT 0x24 +#define LITEETH_READER_LENGTH 0x28 +#define LITEETH_READER_EV_STATUS 0x2c +#define LITEETH_READER_EV_PENDING 0x30 +#define LITEETH_READER_EV_ENABLE 0x34 +#define LITEETH_PREAMBLE_CRC 0x38 +#define LITEETH_PREAMBLE_ERRORS 0x3c +#define LITEETH_CRC_ERRORS 0x40 + +#define LITEETH_PHY_CRG_RESET 0x00 +#define LITEETH_MDIO_W 0x04 +#define MDIO_W_CLK BIT(0) +#define MDIO_W_OE BIT(1) +#define MDIO_W_DO BIT(2) + +#define LITEETH_MDIO_R 0x08 +#define MDIO_R_DI BIT(0) + +#define LITEETH_BUFFER_SIZE 0x800 +#define MAX_PKT_SIZE LITEETH_BUFFER_SIZE + +struct liteeth { + struct device_d *dev; + struct eth_device edev; + void __iomem *base; + void __iomem *mdio_base; + struct mii_bus *mii_bus; + struct mdiobb_ctrl mdiobb; + + /* Link management */ + int cur_duplex; + int cur_speed; + + /* Tx */ + int tx_slot; + int num_tx_slots; + void __iomem *tx_base; + + /* Rx */ + int rx_slot; + int num_rx_slots; + void __iomem *rx_base; +}; + +static inline void litex_write8(void __iomem *addr, u8 val) +{ + writeb(val, addr); +} + +static inline void litex_write16(void __iomem *addr, u16 val) +{ + writew(val, addr); +} + +static inline u8 litex_read8(void __iomem *addr) +{ + return readb(addr); +} + +static inline u32 litex_read32(void __iomem *addr) +{ + return readl(addr); +} + +static void liteeth_mdio_w_modify(struct liteeth *priv, u8 clear, u8 set) +{ + void __iomem *mdio_w = priv->mdio_base + LITEETH_MDIO_W; + + litex_write8(mdio_w, (litex_read8(mdio_w) & ~clear) | set); +} + +static void liteeth_mdio_ctrl(struct mdiobb_ctrl *ctrl, u8 mask, int set) +{ + struct liteeth *priv = container_of(ctrl, struct liteeth, mdiobb); + + liteeth_mdio_w_modify(priv, mask, set ? mask : 0); +} + +/* MDC pin control */ +static void liteeth_set_mdc(struct mdiobb_ctrl *ctrl, int level) +{ + liteeth_mdio_ctrl(ctrl, MDIO_W_CLK, level); +} + +/* Data I/O pin control */ +static void liteeth_set_mdio_dir(struct mdiobb_ctrl *ctrl, int output) +{ + liteeth_mdio_ctrl(ctrl, MDIO_W_OE, output); +} + +/* Set data bit */ +static void liteeth_set_mdio_data(struct mdiobb_ctrl *ctrl, int value) +{ + liteeth_mdio_ctrl(ctrl, MDIO_W_DO, value); +} + +/* Get data bit */ +static int liteeth_get_mdio_data(struct mdiobb_ctrl *ctrl) +{ + struct liteeth *priv = container_of(ctrl, struct liteeth, mdiobb); + + return (litex_read8(priv->mdio_base + LITEETH_MDIO_R) & MDIO_R_DI) != 0; +} + +/* MDIO bus control struct */ +static struct mdiobb_ops bb_ops = { + .set_mdc = liteeth_set_mdc, + .set_mdio_dir = liteeth_set_mdio_dir, + .set_mdio_data = liteeth_set_mdio_data, + .get_mdio_data = liteeth_get_mdio_data, +}; + +static int liteeth_init_dev(struct eth_device *edev) +{ + return 0; +} + +static int liteeth_eth_open(struct eth_device *edev) +{ + struct liteeth *priv = edev->priv; + int ret; + + /* Disable events */ + litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0); + litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0); + + /* Clear pending events? */ + litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, 1); + litex_write8(priv->base + LITEETH_READER_EV_PENDING, 1); + + ret = phy_device_connect(edev, priv->mii_bus, -1, NULL, 0, -1); + if (ret) + return ret; + + return 0; +} + +static int liteeth_eth_send(struct eth_device *edev, void *packet, + int packet_length) +{ + struct liteeth *priv = edev->priv; + void *txbuffer; + int ret; + u8 val; + u8 reg; + + reg = litex_read8(priv->base + LITEETH_READER_EV_PENDING); + if (reg) { + litex_write8(priv->base + LITEETH_READER_EV_PENDING, reg); + } + + /* Reject oversize packets */ + if (unlikely(packet_length > MAX_PKT_SIZE)) { + dev_err(priv->dev, "tx packet too big\n"); + goto drop; + } + + txbuffer = priv->tx_base + priv->tx_slot * LITEETH_BUFFER_SIZE; + memcpy(txbuffer, packet, packet_length); + litex_write8(priv->base + LITEETH_READER_SLOT, priv->tx_slot); + litex_write16(priv->base + LITEETH_READER_LENGTH, packet_length); + + ret = readb_poll_timeout(priv->base + LITEETH_READER_READY, + val, val, 1000); + if (ret == -ETIMEDOUT) { + dev_err(priv->dev, "LITEETH_READER_READY timed out\n"); + goto drop; + } + + litex_write8(priv->base + LITEETH_READER_START, 1); + + priv->tx_slot = (priv->tx_slot + 1) % priv->num_tx_slots; + +drop: + return 0; +} + +static int liteeth_eth_rx(struct eth_device *edev) +{ + struct liteeth *priv = edev->priv; + u8 rx_slot; + int len = 0; + u8 reg; + + reg = litex_read8(priv->base + LITEETH_WRITER_EV_PENDING); + if (!reg) { + goto done; + } + + len = litex_read32(priv->base + LITEETH_WRITER_LENGTH); + if (len == 0 || len > 2048) { + len = 0; + dev_err(priv->dev, "%s: invalid len %d\n", __func__, len); + litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, reg); + goto done; + } + + rx_slot = litex_read8(priv->base + LITEETH_WRITER_SLOT); + + memcpy(NetRxPackets[0], priv->rx_base + rx_slot * LITEETH_BUFFER_SIZE, len); + + net_receive(edev, NetRxPackets[0], len); + + litex_write8(priv->base + LITEETH_WRITER_EV_PENDING, reg); + +done: + return len; +} + +static void liteeth_eth_halt(struct eth_device *edev) +{ + struct liteeth *priv = edev->priv; + + litex_write8(priv->base + LITEETH_WRITER_EV_ENABLE, 0); + litex_write8(priv->base + LITEETH_READER_EV_ENABLE, 0); +} + +static void liteeth_reset_hw(struct liteeth *priv) +{ + /* Reset, twice */ + litex_write8(priv->base + LITEETH_PHY_CRG_RESET, 0); + udelay(10); + litex_write8(priv->base + LITEETH_PHY_CRG_RESET, 1); + udelay(10); + litex_write8(priv->base + LITEETH_PHY_CRG_RESET, 0); + udelay(10); +} + +static int liteeth_get_ethaddr(struct eth_device *edev, unsigned char *m) +{ + return 0; +} + +static int liteeth_set_ethaddr(struct eth_device *edev, + const unsigned char *mac_addr) +{ + return 0; +} + +static int liteeth_probe(struct device_d *dev) +{ + struct device_node *np = dev->device_node; + struct eth_device *edev; + void __iomem *buf_base; + struct liteeth *priv; + int err; + + priv = xzalloc(sizeof(struct liteeth)); + edev = &priv->edev; + edev->priv = priv; + priv->dev = dev; + + priv->base = dev_request_mem_region(dev, 0); + if (IS_ERR(priv->base)) { + err = PTR_ERR(priv->base); + goto err; + } + + priv->mdio_base = dev_request_mem_region(dev, 1); + if (IS_ERR(priv->mdio_base)) { + err = PTR_ERR(priv->mdio_base); + goto err; + } + + buf_base = dev_request_mem_region(dev, 2); + if (IS_ERR(buf_base)) { + err = PTR_ERR(buf_base); + goto err; + } + + err = of_property_read_u32(np, "rx-fifo-depth", + &priv->num_rx_slots); + if (err) { + dev_err(dev, "unable to get rx-fifo-depth\n"); + goto err; + } + + err = of_property_read_u32(np, "tx-fifo-depth", + &priv->num_tx_slots); + if (err) { + dev_err(dev, "unable to get tx-fifo-depth\n"); + goto err; + } + + /* Rx slots */ + priv->rx_base = buf_base; + priv->rx_slot = 0; + + /* Tx slots come after Rx slots */ + priv->tx_base = buf_base + priv->num_rx_slots * LITEETH_BUFFER_SIZE; + priv->tx_slot = 0; + + edev->init = liteeth_init_dev; + edev->open = liteeth_eth_open; + edev->send = liteeth_eth_send; + edev->recv = liteeth_eth_rx; + edev->get_ethaddr = liteeth_get_ethaddr; + edev->set_ethaddr = liteeth_set_ethaddr; + edev->halt = liteeth_eth_halt; + edev->parent = dev; + + priv->mdiobb.ops = &bb_ops; + + priv->mii_bus = alloc_mdio_bitbang(&priv->mdiobb); + priv->mii_bus->parent = dev; + + liteeth_reset_hw(priv); + + err = eth_register(edev); + if (err) { + dev_err(dev, "failed to register edev\n"); + goto err; + } + + err = mdiobus_register(priv->mii_bus); + if (err) { + dev_err(dev, "failed to register mii_bus\n"); + goto err; + } + + dev_info(dev, DRV_NAME " driver registered\n"); + + return 0; + +err: + return err; +} + +static const struct of_device_id liteeth_dt_ids[] = { + { + .compatible = "litex,liteeth" + }, { + } +}; + +static struct driver_d liteeth_driver = { + .name = DRV_NAME, + .probe = liteeth_probe, + .of_compatible = DRV_OF_COMPAT(liteeth_dt_ids), +}; +device_platform_driver(liteeth_driver); + +MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index b1de436ed6..02c5cc08bc 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_DRIVER_SERIAL_LPUART) += serial_lpuart.o obj-$(CONFIG_VIRTIO_CONSOLE) += virtio_console.o obj-$(CONFIG_SERIAL_SIFIVE) += serial_sifive.o obj-$(CONFIG_SERIAL_SBI) += serial_sbi.o +obj-$(CONFIG_SOC_LITEX) += serial_litex.o diff --git a/drivers/serial/serial_litex.c b/drivers/serial/serial_litex.c new file mode 100644 index 0000000000..8562a45ecc --- /dev/null +++ b/drivers/serial/serial_litex.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Antony Pavlov <antonynpavlov@gmail.com> + * + */ + +#include <common.h> +#include <init.h> +#include <io.h> + +#define UART_RXTX 0x00 +#define UART_TXFULL 0x04 +#define UART_RXEMPTY 0x08 +#define UART_EV_PENDING 0x10 +#define UART_EV_RX (1 << 1) + +static inline uint32_t litex_serial_readb(struct console_device *cdev, + uint32_t offset) +{ + void __iomem *base = cdev->dev->priv; + + return readb(base + offset); +} + +static inline void litex_serial_writeb(struct console_device *cdev, + uint32_t value, uint32_t offset) +{ + void __iomem *base = cdev->dev->priv; + + writeb(value, base + offset); +} + +static void litex_serial_putc(struct console_device *cdev, char c) +{ + while (litex_serial_readb(cdev, UART_TXFULL)) + ; + + litex_serial_writeb(cdev, c, UART_RXTX); +} + +static int litex_serial_getc(struct console_device *cdev) +{ + int c; + + while (litex_serial_readb(cdev, UART_RXEMPTY)) + ; + + c = litex_serial_readb(cdev, UART_RXTX); + + /* refresh UART_RXEMPTY by writing UART_EV_RX to UART_EV_PENDING */ + litex_serial_writeb(cdev, UART_EV_RX, UART_EV_PENDING); + + return c; +} + +static int litex_serial_tstc(struct console_device *cdev) +{ + return !litex_serial_readb(cdev, UART_RXEMPTY); +} + +static int litex_serial_probe(struct device_d *dev) +{ + struct resource *iores; + struct console_device *cdev; + + cdev = xzalloc(sizeof(struct console_device)); + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + + dev->priv = IOMEM(iores->start); + cdev->dev = dev; + cdev->tstc = &litex_serial_tstc; + cdev->putc = &litex_serial_putc; + cdev->getc = &litex_serial_getc; + cdev->setbrg = NULL; + + console_register(cdev); + + return 0; +} + +static __maybe_unused struct of_device_id litex_serial_dt_ids[] = { + { + .compatible = "litex,uart", + }, { + /* sentinel */ + } +}; + +static struct driver_d litex_serial_driver = { + .name = "litex-uart", + .probe = litex_serial_probe, + .of_compatible = DRV_OF_COMPAT(litex_serial_dt_ids), +}; +console_platform_driver(litex_serial_driver); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 91083ee709..e92cf675ab 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -74,6 +74,9 @@ config DRIVER_SPI_IMX_2_3 depends on ARCH_IMX50 || ARCH_IMX51 || ARCH_IMX53 || ARCH_IMX6 || ARCH_IMX7 || ARCH_IMX8M default y +config DRIVER_SPI_LITEX_SPIFLASH + bool "Litex SPIFLASH bitbang master driver" + config DRIVER_SPI_MXS bool "i.MX (23,28) SPI Master driver" depends on ARCH_IMX28 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 908d514a01..ac95ffc1db 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DRIVER_SPI_ATH79) += ath79_spi.o obj-$(CONFIG_DRIVER_SPI_GPIO) += gpio_spi.o obj-$(CONFIG_DRIVER_SPI_FSL_QUADSPI) += spi-fsl-qspi.o obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o +obj-$(CONFIG_DRIVER_SPI_LITEX_SPIFLASH) += litex_spiflash.o obj-$(CONFIG_DRIVER_SPI_MVEBU) += mvebu_spi.o obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o obj-$(CONFIG_DRIVER_SPI_ALTERA) += altera_spi.o diff --git a/drivers/spi/litex_spiflash.c b/drivers/spi/litex_spiflash.c new file mode 100644 index 0000000000..e1fc5a4d3f --- /dev/null +++ b/drivers/spi/litex_spiflash.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2020 Antony Pavlov <antonynpavlov@gmail.com> + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <spi/spi.h> +#include <io.h> + +struct litex_spiflash_spi { + struct spi_master master; + void __iomem *regs; + u32 val; +}; + +#define SPIFLASH_BITBANG 0x0 +#define SPIFLASH_BB_MOSI BIT(0) +#define SPIFLASH_BB_CLK BIT(1) +#define SPIFLASH_BB_CSN BIT(2) +#define SPIFLASH_BB_DIR BIT(3) + +#define SPIFLASH_MISO 0x4 +#define SPIFLASH_BITBANG_EN 0x8 + +static inline u32 litex_spiflash_spi_rr(struct litex_spiflash_spi *sp, int reg) +{ + return readl(sp->regs + reg); +} + +static inline void litex_spiflash_spi_wr(struct litex_spiflash_spi *sp, u32 val, int reg) +{ + writel(val, sp->regs + reg); +} + +static inline void litex_setbits(struct litex_spiflash_spi *sp, int bits, int on) +{ + /* + * We are the only user of SCSPTR so no locking is required. + * Reading bit 2 and 0 in SCSPTR gives pin state as input. + * Writing the same bits sets the output value. + * This makes regular read-modify-write difficult so we + * use sp->val to keep track of the latest register value. + */ + + if (on) + sp->val |= bits; + else + sp->val &= ~bits; + + litex_spiflash_spi_wr(sp, sp->val, SPIFLASH_BITBANG); +} + +static inline struct litex_spiflash_spi *litex_spiflash_spidev_to_sp(struct spi_device *spi) +{ + return container_of(spi->master, struct litex_spiflash_spi, master); +} + +static inline void setsck(struct spi_device *spi, int on) +{ + struct litex_spiflash_spi *sc = litex_spiflash_spidev_to_sp(spi); + + litex_setbits(sc, SPIFLASH_BB_CLK, on); +} + +static inline void setmosi(struct spi_device *spi, int on) +{ + struct litex_spiflash_spi *sc = litex_spiflash_spidev_to_sp(spi); + + sc->val &= ~SPIFLASH_BB_DIR; + litex_setbits(sc, SPIFLASH_BB_MOSI, on); +} + +static inline u32 getmiso(struct spi_device *spi) +{ + struct litex_spiflash_spi *sc = litex_spiflash_spidev_to_sp(spi); + + litex_setbits(sc, SPIFLASH_BB_DIR, 1); + return !!((litex_spiflash_spi_rr(sc, SPIFLASH_MISO) & 1)); +} + +#define spidelay(nsecs) udelay(nsecs/1000) + +#include "spi-bitbang-txrx.h" + +static inline void litex_spiflash_spi_chipselect(struct litex_spiflash_spi *sc, int on) +{ + litex_setbits(sc, SPIFLASH_BB_CSN, on); +} + +static int litex_spiflash_spi_setup(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + struct device_d spi_dev = spi->dev; + + if (spi->bits_per_word != 8) { + dev_err(master->dev, "master doesn't support %d bits per word requested by %s\n", + spi->bits_per_word, spi_dev.name); + return -EINVAL; + } + + if ((spi->mode & (SPI_CPHA | SPI_CPOL)) != SPI_MODE_0) { + dev_err(master->dev, "master doesn't support SPI_MODE%d requested by %s\n", + spi->mode & (SPI_CPHA | SPI_CPOL), spi_dev.name); + return -EINVAL; + } + + return 0; +} + +static int litex_spiflash_spi_read(struct spi_device *spi, void *buf, size_t nbyte) +{ + ssize_t cnt = 0; + u8 *rxf_buf = buf; + + while (cnt < nbyte) { + *rxf_buf = bitbang_txrx_be_cpha1(spi, 1000, 1, 0, 8); + rxf_buf++; + cnt++; + } + + return cnt; +} + +static int litex_spiflash_spi_write(struct spi_device *spi, + const void *buf, size_t nbyte) +{ + ssize_t cnt = 0; + const u8 *txf_buf = buf; + + while (cnt < nbyte) { + bitbang_txrx_be_cpha1(spi, 1000, 1, (u32)*txf_buf, 8); + txf_buf++; + cnt++; + } + + return 0; +} + +static int litex_spiflash_spi_transfer(struct spi_device *spi, struct spi_message *mesg) +{ + struct litex_spiflash_spi *sc = litex_spiflash_spidev_to_sp(spi); + struct spi_transfer *t; + + mesg->actual_length = 0; + + /* activate chip select signal */ + litex_spiflash_spi_chipselect(sc, 0); + + list_for_each_entry(t, &mesg->transfers, transfer_list) { + + if (t->tx_buf) + litex_spiflash_spi_write(spi, t->tx_buf, t->len); + + if (t->rx_buf) + litex_spiflash_spi_read(spi, t->rx_buf, t->len); + + mesg->actual_length += t->len; + } + + /* inactivate chip select signal */ + litex_spiflash_spi_chipselect(sc, 1); + + return 0; +} + +static void litex_spiflash_spi_enable(struct litex_spiflash_spi *sp) +{ + u32 val; + + /* set SPIFLASH_BB_DIR = 0 */ + val = SPIFLASH_BB_CSN | SPIFLASH_BB_CLK | SPIFLASH_BB_MOSI; + litex_spiflash_spi_wr(sp, val, SPIFLASH_BITBANG); + + /* enable GPIO mode */ + litex_spiflash_spi_wr(sp, 1, SPIFLASH_BITBANG_EN); +} + +static void litex_spiflash_spi_disable(struct litex_spiflash_spi *sp) +{ + /* disable GPIO mode */ + litex_spiflash_spi_wr(sp, 0, SPIFLASH_BITBANG_EN); +} + +static int litex_spiflash_spi_probe(struct device_d *dev) +{ + struct resource *iores; + struct spi_master *master; + struct litex_spiflash_spi *litex_spiflash_spi; + + litex_spiflash_spi = xzalloc(sizeof(*litex_spiflash_spi)); + dev->priv = litex_spiflash_spi; + + master = &litex_spiflash_spi->master; + master->dev = dev; + + master->bus_num = dev->id; + master->setup = litex_spiflash_spi_setup; + master->transfer = litex_spiflash_spi_transfer; + master->num_chipselect = 1; + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + litex_spiflash_spi->regs = IOMEM(iores->start); + + litex_spiflash_spi_enable(litex_spiflash_spi); + + /* inactivate chip select signal */ + litex_spiflash_spi_chipselect(litex_spiflash_spi, 1); + + spi_register_master(master); + + return 0; +} + +static void litex_spiflash_spi_remove(struct device_d *dev) +{ + struct litex_spiflash_spi *sp = dev->priv; + + litex_spiflash_spi_disable(sp); +} + +static __maybe_unused struct of_device_id litex_spiflash_spi_dt_ids[] = { + { + .compatible = "litex,spiflash", + }, + { + /* sentinel */ + } +}; + +static struct driver_d litex_spiflash_spi_driver = { + .name = "litex-spiflash", + .probe = litex_spiflash_spi_probe, + .remove = litex_spiflash_spi_remove, + .of_compatible = DRV_OF_COMPAT(litex_spiflash_spi_dt_ids), +}; +device_platform_driver(litex_spiflash_spi_driver); diff --git a/images/Makefile.riscv b/images/Makefile.riscv index 4410765cf6..0645238c43 100644 --- a/images/Makefile.riscv +++ b/images/Makefile.riscv @@ -19,3 +19,7 @@ image-$(CONFIG_BOARD_HIFIVE) += barebox-hifive-unmatched.img barebox-hifive-unle pblb-$(CONFIG_BOARD_BEAGLEV) += start_beaglev_starlight FILE_barebox-beaglev-starlight.img = start_beaglev_starlight.pblb image-$(CONFIG_BOARD_BEAGLEV) += barebox-beaglev-starlight.img + +pblb-$(CONFIG_BOARD_LITEX_LINUX) += start_litex_linux +FILE_barebox-litex-linux.img = start_litex_linux.pblb +image-$(CONFIG_BOARD_LITEX_LINUX) += barebox-litex-linux.img |