- doc: fix qemu-mips build instructions

- MIPS: add GPIO, CLK and SPI drivers for Octeon MIPS64
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEiQkHUH+J02LLC9InKPlOlyTyXBgFAl8oYscACgkQKPlOlyTy
 XBhMSxAAtCdGKSU1cBLvLhoHi9zHfJ9fXKwrSTikSWJ5SotinY8ICdR95PDDChnv
 BL5FH1TYMi6cwLxoT7XuOzSjUvYFx+y7drT5Cj78Udzfw3J0OF2mVhJTLkV0adnK
 beT6srAgM/Sli19ewXquWqzE/s9fSwjjNfZH43ySxDb3S35llYcBaoOIZ+o1EUO5
 So8Vvi31W5H6MpxioTGGH9CJLHU7Xa6hCeLg/h6P1AUAyyQICFMraFfG6nt90k5k
 2z0Lc7D5I5JQi074nAChSo/D91p75Z5pLYx+h3MFWjwafr5QddqjZ/hL+gixubWg
 yW0QX4TA92X1p/wXioYleTldLbbhz23OvjYJyfvmEsNIvTuAj/opi+Im5Lg0T0TT
 QJPNenxKwnURfYwI2woSk6xFBZqMgZ5eo1FacTUebRCkkW/YTKpcOn7K2pNzQfJn
 c/rKo9/rJVyb+1X/EglUEJ07ARUWRTq8dgOYbGe25qqTjYlwES4xd4AQrqQaYpkF
 OGzMGezzw5HejtqMa1x01VsPQZgcKTTS3gmnLYkfs8j7WD0+f5V2Ba9gnrblSXeR
 Vwfc8yWU0fIYW7I4kX9hsugqEqUOlXeUJ1yylNdeenBn6Hvs4Ros5GgNco1n8lhi
 lcEy/DzAcVw5cAZafeR0ZIMj2r3hTs4jW+TUFKEuuNW9W+KV2ZI=
 =Kv/C
 -----END PGP SIGNATURE-----

Merge tag 'mips-pull-2020-08-03' of https://gitlab.denx.de/u-boot/custodians/u-boot-mips

- doc: fix qemu-mips build instructions
- MIPS: add GPIO, CLK and SPI drivers for Octeon MIPS64
This commit is contained in:
Tom Rini 2020-08-04 11:07:16 -04:00
commit bb3694d5b1
15 changed files with 1072 additions and 8 deletions

View file

@ -112,9 +112,11 @@ config ARCH_OCTEON
select DISPLAY_CPUINFO select DISPLAY_CPUINFO
select DMA_ADDR_T_64BIT select DMA_ADDR_T_64BIT
select DM select DM
select DM_SERIAL
select DM_GPIO
select DM_ETH select DM_ETH
select DM_GPIO
select DM_I2C
select DM_SERIAL
select DM_SPI
select MIPS_L2_CACHE select MIPS_L2_CACHE
select MIPS_MACH_EARLY_INIT select MIPS_MACH_EARLY_INIT
select MIPS_TUNE_OCTEON3 select MIPS_TUNE_OCTEON3

View file

@ -5,6 +5,8 @@
/dts-v1/; /dts-v1/;
#include <dt-bindings/clock/octeon-clock.h>
/ { / {
#address-cells = <2>; #address-cells = <2>;
#size-cells = <2>; #size-cells = <2>;
@ -38,6 +40,38 @@
#size-cells = <1>; #size-cells = <1>;
}; };
clk: clock {
compatible = "mrvl,octeon-clk";
#clock-cells = <1>;
u-boot,dm-pre-reloc;
};
gpio: gpio-controller@1070000000800 {
#gpio-cells = <2>;
compatible = "cavium,octeon-7890-gpio";
reg = <0x10700 0x00000800 0x0 0x100>;
gpio-controller;
nr-gpios = <32>;
/* Interrupts are specified by two parts:
* 1) GPIO pin number (0..15)
* 2) Triggering (1 - edge rising
* 2 - edge falling
* 4 - level active high
* 8 - level active low)
*/
interrupt-controller;
#interrupt-cells = <2>;
/* The GPIO pins connect to 16 consecutive CUI bits */
interrupts = <0x03000 4>, <0x03001 4>,
<0x03002 4>, <0x03003 4>,
<0x03004 4>, <0x03005 4>,
<0x03006 4>, <0x03007 4>,
<0x03008 4>, <0x03009 4>,
<0x0300a 4>, <0x0300b 4>,
<0x0300c 4>, <0x0300d 4>,
<0x0300e 4>, <0x0300f 4>;
};
reset: reset@1180006001600 { reset: reset@1180006001600 {
compatible = "mrvl,cn7xxx-rst"; compatible = "mrvl,cn7xxx-rst";
reg = <0x11800 0x06001600 0x0 0x200>; reg = <0x11800 0x06001600 0x0 0x200>;
@ -60,5 +94,37 @@
reg-shift = <3>; reg-shift = <3>;
interrupts = <0x08040 4>; interrupts = <0x08040 4>;
}; };
i2c0: i2c@1180000001000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cavium,octeon-7890-twsi";
reg = <0x11800 0x00001000 0x0 0x200>;
/* INT_ST, INT_TS, INT_CORE */
interrupts = <0x0b000 1>, <0x0b001 1>, <0x0b002 1>;
clock-frequency = <100000>;
clocks = <&clk OCTEON_CLK_IO>;
};
i2c1: i2c@1180000001200 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "cavium,octeon-7890-twsi";
reg = <0x11800 0x00001200 0x0 0x200>;
/* INT_ST, INT_TS, INT_CORE */
interrupts = <0x0b100 1>, <0x0b101 1>, <0x0b102 1>;
clock-frequency = <100000>;
clocks = <&clk OCTEON_CLK_IO>;
};
spi: spi@1070000001000 {
compatible = "cavium,octeon-3010-spi";
reg = <0x10700 0x00001000 0x0 0x100>;
interrupts = <0x05001 1>;
#address-cells = <1>;
#size-cells = <0>;
spi-max-frequency = <25000000>;
clocks = <&clk OCTEON_CLK_IO>;
};
}; };
}; };

View file

@ -5,7 +5,7 @@
/dts-v1/; /dts-v1/;
/include/ "mrvl,cn73xx.dtsi" #include "mrvl,cn73xx.dtsi"
/ { / {
model = "cavium,ebb7304"; model = "cavium,ebb7304";
@ -13,6 +13,7 @@
aliases { aliases {
serial0 = &uart0; serial0 = &uart0;
spi0 = &spi;
}; };
chosen { chosen {
@ -94,3 +95,21 @@
&uart0 { &uart0 {
clock-frequency = <1200000000>; clock-frequency = <1200000000>;
}; };
&i2c0 {
u-boot,dm-pre-reloc; /* Needed early for DDR SPD EEPROM */
clock-frequency = <100000>;
};
&i2c1 {
u-boot,dm-pre-reloc; /* Needed early for DDR SPD EEPROM */
clock-frequency = <100000>;
};
&spi {
flash@0 {
compatible = "micron,n25q128a11", "jedec,spi-nor";
spi-max-frequency = <2000000>;
reg = <0>;
};
};

View file

@ -1,9 +1,9 @@
CONFIG_MIPS=y CONFIG_MIPS=y
CONFIG_SYS_TEXT_BASE=0xffffffff80000000 CONFIG_SYS_TEXT_BASE=0xffffffff80000000
CONFIG_SYS_MALLOC_F_LEN=0x4000 CONFIG_SYS_MALLOC_F_LEN=0x4000
CONFIG_NR_DRAM_BANKS=2
CONFIG_ENV_SIZE=0x2000 CONFIG_ENV_SIZE=0x2000
CONFIG_ENV_SECT_SIZE=0x10000 CONFIG_ENV_SECT_SIZE=0x10000
CONFIG_NR_DRAM_BANKS=2
CONFIG_DEBUG_UART_BASE=0x8001180000000800 CONFIG_DEBUG_UART_BASE=0x8001180000000800
CONFIG_DEBUG_UART_CLOCK=1200000000 CONFIG_DEBUG_UART_CLOCK=1200000000
CONFIG_ARCH_OCTEON=y CONFIG_ARCH_OCTEON=y
@ -12,6 +12,8 @@ CONFIG_ARCH_OCTEON=y
CONFIG_DEBUG_UART=y CONFIG_DEBUG_UART=y
CONFIG_SYS_CONSOLE_INFO_QUIET=y CONFIG_SYS_CONSOLE_INFO_QUIET=y
CONFIG_HUSH_PARSER=y CONFIG_HUSH_PARSER=y
CONFIG_CMD_GPIO=y
CONFIG_CMD_I2C=y
CONFIG_CMD_MTD=y CONFIG_CMD_MTD=y
CONFIG_CMD_PCI=y CONFIG_CMD_PCI=y
CONFIG_CMD_DHCP=y CONFIG_CMD_DHCP=y
@ -29,10 +31,18 @@ CONFIG_CFI_FLASH=y
CONFIG_SYS_FLASH_USE_BUFFER_WRITE=y CONFIG_SYS_FLASH_USE_BUFFER_WRITE=y
CONFIG_FLASH_CFI_MTD=y CONFIG_FLASH_CFI_MTD=y
CONFIG_SYS_FLASH_CFI=y CONFIG_SYS_FLASH_CFI=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SPI_FLASH_ATMEL=y
CONFIG_SPI_FLASH_SPANSION=y
CONFIG_SPI_FLASH_STMICRO=y
# CONFIG_NETDEVICES is not set # CONFIG_NETDEVICES is not set
CONFIG_PCI=y
CONFIG_DM_PCI=y
CONFIG_DEBUG_UART_SHIFT=3 CONFIG_DEBUG_UART_SHIFT=3
CONFIG_DEBUG_UART_ANNOUNCE=y CONFIG_DEBUG_UART_ANNOUNCE=y
CONFIG_SYS_NS16550=y CONFIG_SYS_NS16550=y
CONFIG_SPI=y
CONFIG_OCTEON_SPI=y
CONFIG_SYSRESET=y CONFIG_SYSRESET=y
CONFIG_SYSRESET_OCTEON=y CONFIG_SYSRESET_OCTEON=y
CONFIG_HEXDUMP=y CONFIG_HEXDUMP=y

View file

@ -29,28 +29,28 @@ Using u-boot.bin as ROM (replaces Qemu monitor):
.. code-block:: bash .. code-block:: bash
make qemu_mips make qemu_mips_defconfig
qemu-system-mips -M mips -bios u-boot.bin -nographic qemu-system-mips -M mips -bios u-boot.bin -nographic
32 bit, little endian 32 bit, little endian
.. code-block:: bash .. code-block:: bash
make qemu_mipsel make qemu_mipsel_defconfig
qemu-system-mipsel -M mips -bios u-boot.bin -nographic qemu-system-mipsel -M mips -bios u-boot.bin -nographic
64 bit, big endian 64 bit, big endian
.. code-block:: bash .. code-block:: bash
make qemu_mips64 make qemu_mips64_defconfig
qemu-system-mips64 -cpu MIPS64R2-generic -M mips -bios u-boot.bin -nographic qemu-system-mips64 -cpu MIPS64R2-generic -M mips -bios u-boot.bin -nographic
64 bit, little endian 64 bit, little endian
.. code-block:: bash .. code-block:: bash
make qemu_mips64el make qemu_mips64el_defconfig
qemu-system-mips64el -cpu MIPS64R2-generic -M mips -bios u-boot.bin -nographic qemu-system-mips64el -cpu MIPS64R2-generic -M mips -bios u-boot.bin -nographic
or using u-boot.bin from emulated flash: or using u-boot.bin from emulated flash:

View file

@ -83,6 +83,13 @@ config CLK_INTEL
set up by U-Boot itself but only statically. Thus the driver does not set up by U-Boot itself but only statically. Thus the driver does not
support changing clock rates, only querying them. support changing clock rates, only querying them.
config CLK_OCTEON
bool "Clock controller driver for Marvell MIPS Octeon"
depends on CLK && ARCH_OCTEON
default y
help
Enable this to support the clocks on Octeon MIPS platforms.
config CLK_STM32F config CLK_STM32F
bool "Enable clock driver support for STM32F family" bool "Enable clock driver support for STM32F family"
depends on CLK && (STM32F7 || STM32F4) depends on CLK && (STM32F7 || STM32F4)

View file

@ -29,6 +29,7 @@ obj-$(CONFIG_$(SPL_TPL_)CLK_INTEL) += intel/
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
obj-$(CONFIG_CLK_K210) += kendryte/ obj-$(CONFIG_CLK_K210) += kendryte/
obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o
obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o
obj-$(CONFIG_CLK_OWL) += owl/ obj-$(CONFIG_CLK_OWL) += owl/
obj-$(CONFIG_CLK_RENESAS) += renesas/ obj-$(CONFIG_CLK_RENESAS) += renesas/
obj-$(CONFIG_CLK_SIFIVE) += sifive/ obj-$(CONFIG_CLK_SIFIVE) += sifive/

72
drivers/clk/clk_octeon.c Normal file
View file

@ -0,0 +1,72 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2020 Stefan Roese <sr@denx.de>
*/
#include <clk-uclass.h>
#include <dm.h>
#include <dt-bindings/clock/octeon-clock.h>
DECLARE_GLOBAL_DATA_PTR;
struct octeon_clk_priv {
u64 core_clk;
u64 io_clk;
};
static int octeon_clk_enable(struct clk *clk)
{
/* Nothing to do on Octeon */
return 0;
}
static ulong octeon_clk_get_rate(struct clk *clk)
{
struct octeon_clk_priv *priv = dev_get_priv(clk->dev);
switch (clk->id) {
case OCTEON_CLK_CORE:
return priv->core_clk;
case OCTEON_CLK_IO:
return priv->io_clk;
default:
return 0;
}
return 0;
}
static struct clk_ops octeon_clk_ops = {
.enable = octeon_clk_enable,
.get_rate = octeon_clk_get_rate,
};
static const struct udevice_id octeon_clk_ids[] = {
{ .compatible = "mrvl,octeon-clk" },
{ /* sentinel */ }
};
static int octeon_clk_probe(struct udevice *dev)
{
struct octeon_clk_priv *priv = dev_get_priv(dev);
/*
* The clock values are already read into GD, lets just store them
* in priv data
*/
priv->core_clk = gd->cpu_clk;
priv->io_clk = gd->bus_clk;
return 0;
}
U_BOOT_DRIVER(clk_octeon) = {
.name = "clk_octeon",
.id = UCLASS_CLK,
.of_match = octeon_clk_ids,
.ops = &octeon_clk_ops,
.probe = octeon_clk_probe,
.priv_auto_alloc_size = sizeof(struct octeon_clk_priv),
};

View file

@ -346,6 +346,16 @@ config PIC32_GPIO
help help
Say yes here to support Microchip PIC32 GPIOs. Say yes here to support Microchip PIC32 GPIOs.
config OCTEON_GPIO
bool "Octeon II/III/TX/TX2 GPIO driver"
depends on DM_GPIO && DM_PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
default y
help
Add support for the Marvell Octeon GPIO driver. This is used with
various Octeon parts such as Octeon II/III and OcteonTX/TX2.
Octeon II/III has 32 GPIOs (count defined via DT) and OcteonTX/TX2
has 64 GPIOs (count defined via internal register).
config STM32_GPIO config STM32_GPIO
bool "ST STM32 GPIO driver" bool "ST STM32 GPIO driver"
depends on DM_GPIO && (ARCH_STM32 || ARCH_STM32MP) depends on DM_GPIO && (ARCH_STM32 || ARCH_STM32MP)

View file

@ -59,6 +59,7 @@ obj-$(CONFIG_HIKEY_GPIO) += hi6220_gpio.o
obj-$(CONFIG_HSDK_CREG_GPIO) += hsdk-creg-gpio.o obj-$(CONFIG_HSDK_CREG_GPIO) += hsdk-creg-gpio.o
obj-$(CONFIG_IMX_RGPIO2P) += imx_rgpio2p.o obj-$(CONFIG_IMX_RGPIO2P) += imx_rgpio2p.o
obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o obj-$(CONFIG_PIC32_GPIO) += pic32_gpio.o
obj-$(CONFIG_OCTEON_GPIO) += octeon_gpio.o
obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o obj-$(CONFIG_MVEBU_GPIO) += mvebu_gpio.o
obj-$(CONFIG_MSM_GPIO) += msm_gpio.o obj-$(CONFIG_MSM_GPIO) += msm_gpio.o
obj-$(CONFIG_$(SPL_)PCF8575_GPIO) += pcf8575_gpio.o obj-$(CONFIG_$(SPL_)PCF8575_GPIO) += pcf8575_gpio.o

242
drivers/gpio/octeon_gpio.c Normal file
View file

@ -0,0 +1,242 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Marvell International Ltd.
*
* (C) Copyright 2011
* eInfochips Ltd. <www.einfochips.com>
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
*
* (C) Copyright 2010
* Marvell Semiconductor <www.marvell.com>
*/
#include <dm.h>
#include <pci.h>
#include <pci_ids.h>
#include <asm/gpio.h>
#include <asm/io.h>
#include <linux/bitfield.h>
#include <linux/compat.h>
#include <dt-bindings/gpio/gpio.h>
/* Returns the bit value to write or read based on the offset */
#define GPIO_BIT(x) BIT_ULL((x) & 0x3f)
#define GPIO_RX_DAT 0x00
#define GPIO_TX_SET 0x08
#define GPIO_TX_CLR 0x10
#define GPIO_CONST 0x90 /* OcteonTX only */
/* Offset to register-set for 2nd GPIOs (> 63), OcteonTX only */
#define GPIO1_OFFSET 0x1400
/* GPIO_CONST register bits */
#define GPIO_CONST_GPIOS_MASK GENMASK_ULL(7, 0)
/* GPIO_BIT_CFG register bits */
#define GPIO_BIT_CFG_TX_OE BIT_ULL(0)
#define GPIO_BIT_CFG_PIN_XOR BIT_ULL(1)
#define GPIO_BIT_CFG_INT_EN BIT_ULL(2)
#define GPIO_BIT_CFG_PIN_SEL_MASK GENMASK_ULL(26, 16)
enum {
PROBE_PCI = 0, /* PCI based probing */
PROBE_DT, /* DT based probing */
};
struct octeon_gpio_data {
int probe;
u32 reg_offs;
u32 gpio_bit_cfg_offs;
};
struct octeon_gpio {
void __iomem *base;
const struct octeon_gpio_data *data;
};
/* Returns the offset to the output register based on the offset and value */
static u32 gpio_tx_reg(int offset, int value)
{
u32 ret;
ret = value ? GPIO_TX_SET : GPIO_TX_CLR;
if (offset > 63)
ret += GPIO1_OFFSET;
return ret;
}
/* Returns the offset to the input data register based on the offset */
static u32 gpio_rx_dat_reg(int offset)
{
u32 ret;
ret = GPIO_RX_DAT;
if (offset > 63)
ret += GPIO1_OFFSET;
return ret;
}
static int octeon_gpio_dir_input(struct udevice *dev, unsigned int offset)
{
struct octeon_gpio *gpio = dev_get_priv(dev);
debug("%s(%s, %u)\n", __func__, dev->name, offset);
clrbits_64(gpio->base + gpio->data->gpio_bit_cfg_offs + 8 * offset,
GPIO_BIT_CFG_TX_OE | GPIO_BIT_CFG_PIN_XOR |
GPIO_BIT_CFG_INT_EN | GPIO_BIT_CFG_PIN_SEL_MASK);
return 0;
}
static int octeon_gpio_dir_output(struct udevice *dev, unsigned int offset,
int value)
{
struct octeon_gpio *gpio = dev_get_priv(dev);
debug("%s(%s, %u, %d)\n", __func__, dev->name, offset, value);
writeq(GPIO_BIT(offset), gpio->base + gpio->data->reg_offs +
gpio_tx_reg(offset, value));
clrsetbits_64(gpio->base + gpio->data->gpio_bit_cfg_offs + 8 * offset,
GPIO_BIT_CFG_PIN_SEL_MASK | GPIO_BIT_CFG_INT_EN,
GPIO_BIT_CFG_TX_OE);
return 0;
}
static int octeon_gpio_get_value(struct udevice *dev, unsigned int offset)
{
struct octeon_gpio *gpio = dev_get_priv(dev);
u64 reg = readq(gpio->base + gpio->data->reg_offs +
gpio_rx_dat_reg(offset));
debug("%s(%s, %u): value: %d\n", __func__, dev->name, offset,
!!(reg & GPIO_BIT(offset)));
return !!(reg & GPIO_BIT(offset));
}
static int octeon_gpio_set_value(struct udevice *dev,
unsigned int offset, int value)
{
struct octeon_gpio *gpio = dev_get_priv(dev);
debug("%s(%s, %u, %d)\n", __func__, dev->name, offset, value);
writeq(GPIO_BIT(offset), gpio->base + gpio->data->reg_offs +
gpio_tx_reg(offset, value));
return 0;
}
static int octeon_gpio_get_function(struct udevice *dev, unsigned int offset)
{
struct octeon_gpio *gpio = dev_get_priv(dev);
u64 val = readq(gpio->base + gpio->data->gpio_bit_cfg_offs +
8 * offset);
int pin_sel;
debug("%s(%s, %u): GPIO_BIT_CFG: 0x%llx\n", __func__, dev->name,
offset, val);
pin_sel = FIELD_GET(GPIO_BIT_CFG_PIN_SEL_MASK, val);
if (pin_sel)
return GPIOF_FUNC;
else if (val & GPIO_BIT_CFG_TX_OE)
return GPIOF_OUTPUT;
else
return GPIOF_INPUT;
}
static int octeon_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
struct ofnode_phandle_args *args)
{
if (args->args_count < 1)
return -EINVAL;
desc->offset = args->args[0];
desc->flags = 0;
if (args->args_count > 1) {
if (args->args[1] & GPIO_ACTIVE_LOW)
desc->flags |= GPIOD_ACTIVE_LOW;
/* In the future add tri-state flag support */
}
return 0;
}
static const struct dm_gpio_ops octeon_gpio_ops = {
.direction_input = octeon_gpio_dir_input,
.direction_output = octeon_gpio_dir_output,
.get_value = octeon_gpio_get_value,
.set_value = octeon_gpio_set_value,
.get_function = octeon_gpio_get_function,
.xlate = octeon_gpio_xlate,
};
static int octeon_gpio_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct octeon_gpio *priv = dev_get_priv(dev);
char *end;
priv->data = (const struct octeon_gpio_data *)dev_get_driver_data(dev);
if (priv->data->probe == PROBE_PCI) {
priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
PCI_REGION_MEM);
uc_priv->gpio_count = readq(priv->base +
priv->data->reg_offs + GPIO_CONST) &
GPIO_CONST_GPIOS_MASK;
} else {
priv->base = dev_remap_addr(dev);
uc_priv->gpio_count = ofnode_read_u32_default(dev->node,
"nr-gpios", 32);
}
if (!priv->base) {
debug("%s(%s): Could not get base address\n",
__func__, dev->name);
return -ENODEV;
}
uc_priv->bank_name = strdup(dev->name);
end = strchr(uc_priv->bank_name, '@');
end[0] = 'A' + dev->seq;
end[1] = '\0';
debug("%s(%s): base address: %p, pin count: %d\n",
__func__, dev->name, priv->base, uc_priv->gpio_count);
return 0;
}
static const struct octeon_gpio_data gpio_octeon_data = {
.probe = PROBE_DT,
.reg_offs = 0x80,
.gpio_bit_cfg_offs = 0x100,
};
static const struct octeon_gpio_data gpio_octeontx_data = {
.probe = PROBE_PCI,
.reg_offs = 0x00,
.gpio_bit_cfg_offs = 0x400,
};
static const struct udevice_id octeon_gpio_ids[] = {
{ .compatible = "cavium,thunder-8890-gpio",
.data = (ulong)&gpio_octeontx_data },
{ .compatible = "cavium,octeon-7890-gpio",
.data = (ulong)&gpio_octeon_data },
{ }
};
U_BOOT_DRIVER(octeon_gpio) = {
.name = "octeon_gpio",
.id = UCLASS_GPIO,
.of_match = of_match_ptr(octeon_gpio_ids),
.probe = octeon_gpio_probe,
.priv_auto_alloc_size = sizeof(struct octeon_gpio),
.ops = &octeon_gpio_ops,
.flags = DM_FLAG_PRE_RELOC,
};

View file

@ -240,6 +240,14 @@ config NXP_FSPI
Enable the NXP FlexSPI (FSPI) driver. This driver can be used to Enable the NXP FlexSPI (FSPI) driver. This driver can be used to
access the SPI NOR flash on platforms embedding this NXP IP core. access the SPI NOR flash on platforms embedding this NXP IP core.
config OCTEON_SPI
bool "Octeon SPI driver"
depends on DM_PCI && (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2)
help
Enable the Octeon SPI driver. This driver can be used to
access the SPI NOR flash on Octeon II/III and OcteonTX/TX2
SoC platforms.
config OMAP3_SPI config OMAP3_SPI
bool "McSPI driver for OMAP" bool "McSPI driver for OMAP"
help help

View file

@ -43,6 +43,7 @@ obj-$(CONFIG_MXC_SPI) += mxc_spi.o
obj-$(CONFIG_MXS_SPI) += mxs_spi.o obj-$(CONFIG_MXS_SPI) += mxs_spi.o
obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o
obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o
obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o
obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
obj-$(CONFIG_PIC32_SPI) += pic32_spi.o obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
obj-$(CONFIG_PL022_SPI) += pl022_spi.o obj-$(CONFIG_PL022_SPI) += pl022_spi.o

613
drivers/spi/octeon_spi.c Normal file
View file

@ -0,0 +1,613 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Marvell International Ltd.
*/
#include <clk.h>
#include <dm.h>
#include <malloc.h>
#include <spi.h>
#include <spi-mem.h>
#include <watchdog.h>
#include <asm/io.h>
#include <asm/unaligned.h>
#include <linux/bitfield.h>
#include <linux/compat.h>
#include <linux/delay.h>
#define OCTEON_SPI_MAX_BYTES 9
#define OCTEON_SPI_MAX_CLOCK_HZ 50000000
#define OCTEON_SPI_NUM_CS 4
#define OCTEON_SPI_CS_VALID(cs) ((cs) < OCTEON_SPI_NUM_CS)
#define MPI_CFG 0x0000
#define MPI_STS 0x0008
#define MPI_TX 0x0010
#define MPI_XMIT 0x0018
#define MPI_WIDE_DAT 0x0040
#define MPI_IO_CTL 0x0048
#define MPI_DAT(X) (0x0080 + ((X) << 3))
#define MPI_WIDE_BUF(X) (0x0800 + ((X) << 3))
#define MPI_CYA_CFG 0x1000
#define MPI_CLKEN 0x1080
#define MPI_CFG_ENABLE BIT_ULL(0)
#define MPI_CFG_IDLELO BIT_ULL(1)
#define MPI_CFG_CLK_CONT BIT_ULL(2)
#define MPI_CFG_WIREOR BIT_ULL(3)
#define MPI_CFG_LSBFIRST BIT_ULL(4)
#define MPI_CFG_CS_STICKY BIT_ULL(5)
#define MPI_CFG_CSHI BIT_ULL(7)
#define MPI_CFG_IDLECLKS GENMASK_ULL(9, 8)
#define MPI_CFG_TRITX BIT_ULL(10)
#define MPI_CFG_CSLATE BIT_ULL(11)
#define MPI_CFG_CSENA0 BIT_ULL(12)
#define MPI_CFG_CSENA1 BIT_ULL(13)
#define MPI_CFG_CSENA2 BIT_ULL(14)
#define MPI_CFG_CSENA3 BIT_ULL(15)
#define MPI_CFG_CLKDIV GENMASK_ULL(28, 16)
#define MPI_CFG_LEGACY_DIS BIT_ULL(31)
#define MPI_CFG_IOMODE GENMASK_ULL(35, 34)
#define MPI_CFG_TB100_EN BIT_ULL(49)
#define MPI_DAT_DATA GENMASK_ULL(7, 0)
#define MPI_STS_BUSY BIT_ULL(0)
#define MPI_STS_MPI_INTR BIT_ULL(1)
#define MPI_STS_RXNUM GENMASK_ULL(12, 8)
#define MPI_TX_TOTNUM GENMASK_ULL(4, 0)
#define MPI_TX_TXNUM GENMASK_ULL(12, 8)
#define MPI_TX_LEAVECS BIT_ULL(16)
#define MPI_TX_CSID GENMASK_ULL(21, 20)
#define MPI_XMIT_TOTNUM GENMASK_ULL(10, 0)
#define MPI_XMIT_TXNUM GENMASK_ULL(30, 20)
#define MPI_XMIT_BUF_SEL BIT_ULL(59)
#define MPI_XMIT_LEAVECS BIT_ULL(60)
#define MPI_XMIT_CSID GENMASK_ULL(62, 61)
/* Used on Octeon TX2 */
void board_acquire_flash_arb(bool acquire);
/* Local driver data structure */
struct octeon_spi {
void __iomem *base; /* Register base address */
struct clk clk;
u32 clkdiv; /* Clock divisor for device speed */
};
static u64 octeon_spi_set_mpicfg(struct udevice *dev)
{
struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
struct udevice *bus = dev_get_parent(dev);
struct octeon_spi *priv = dev_get_priv(bus);
u64 mpi_cfg;
uint max_speed = slave->max_hz;
bool cpha, cpol;
if (!max_speed)
max_speed = 12500000;
if (max_speed > OCTEON_SPI_MAX_CLOCK_HZ)
max_speed = OCTEON_SPI_MAX_CLOCK_HZ;
debug("\n slave params %d %d %d\n", slave->cs,
slave->max_hz, slave->mode);
cpha = !!(slave->mode & SPI_CPHA);
cpol = !!(slave->mode & SPI_CPOL);
mpi_cfg = FIELD_PREP(MPI_CFG_CLKDIV, priv->clkdiv & 0x1fff) |
FIELD_PREP(MPI_CFG_CSHI, !!(slave->mode & SPI_CS_HIGH)) |
FIELD_PREP(MPI_CFG_LSBFIRST, !!(slave->mode & SPI_LSB_FIRST)) |
FIELD_PREP(MPI_CFG_WIREOR, !!(slave->mode & SPI_3WIRE)) |
FIELD_PREP(MPI_CFG_IDLELO, cpha != cpol) |
FIELD_PREP(MPI_CFG_CSLATE, cpha) |
MPI_CFG_CSENA0 | MPI_CFG_CSENA1 |
MPI_CFG_CSENA2 | MPI_CFG_CSENA1 |
MPI_CFG_ENABLE;
debug("\n mpi_cfg %llx\n", mpi_cfg);
return mpi_cfg;
}
/**
* Wait until the SPI bus is ready
*
* @param dev SPI device to wait for
*/
static void octeon_spi_wait_ready(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct octeon_spi *priv = dev_get_priv(bus);
void *base = priv->base;
u64 mpi_sts;
do {
mpi_sts = readq(base + MPI_STS);
WATCHDOG_RESET();
} while (mpi_sts & MPI_STS_BUSY);
debug("%s(%s)\n", __func__, dev->name);
}
/**
* Claim the bus for a slave device
*
* @param dev SPI bus
*
* @return 0 for success, -EINVAL if chip select is invalid
*/
static int octeon_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct octeon_spi *priv = dev_get_priv(bus);
void *base = priv->base;
u64 mpi_cfg;
debug("\n\n%s(%s)\n", __func__, dev->name);
if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
return -EINVAL;
if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
board_acquire_flash_arb(true);
mpi_cfg = readq(base + MPI_CFG);
mpi_cfg &= ~MPI_CFG_TRITX;
mpi_cfg |= MPI_CFG_ENABLE;
writeq(mpi_cfg, base + MPI_CFG);
mpi_cfg = readq(base + MPI_CFG);
udelay(5); /** Wait for bus to settle */
return 0;
}
/**
* Release the bus to a slave device
*
* @param dev SPI bus
*
* @return 0 for success, -EINVAL if chip select is invalid
*/
static int octeon_spi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct octeon_spi *priv = dev_get_priv(bus);
void *base = priv->base;
u64 mpi_cfg;
debug("%s(%s)\n\n", __func__, dev->name);
if (!OCTEON_SPI_CS_VALID(spi_chip_select(dev)))
return -EINVAL;
if (IS_ENABLED(CONFIG_ARCH_OCTEONTX2))
board_acquire_flash_arb(false);
mpi_cfg = readq(base + MPI_CFG);
mpi_cfg &= ~MPI_CFG_ENABLE;
writeq(mpi_cfg, base + MPI_CFG);
mpi_cfg = readq(base + MPI_CFG);
udelay(1);
return 0;
}
static int octeon_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev_get_parent(dev);
struct octeon_spi *priv = dev_get_priv(bus);
void *base = priv->base;
u64 mpi_tx;
u64 mpi_cfg;
u64 wide_dat = 0;
int len = bitlen / 8;
int i;
const u8 *tx_data = dout;
u8 *rx_data = din;
int cs = spi_chip_select(dev);
if (!OCTEON_SPI_CS_VALID(cs))
return -EINVAL;
debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
__func__, dev->name, bitlen, dout, din, flags, cs);
mpi_cfg = octeon_spi_set_mpicfg(dev);
if (mpi_cfg != readq(base + MPI_CFG)) {
writeq(mpi_cfg, base + MPI_CFG);
mpi_cfg = readq(base + MPI_CFG);
udelay(10);
}
debug("\n mpi_cfg upd %llx\n", mpi_cfg);
/*
* Start by writing and reading 8 bytes at a time. While we can support
* up to 10, it's easier to just use 8 with the MPI_WIDE_DAT register.
*/
while (len > 8) {
if (tx_data) {
wide_dat = get_unaligned((u64 *)tx_data);
debug(" tx: %016llx \t", (unsigned long long)wide_dat);
tx_data += 8;
writeq(wide_dat, base + MPI_WIDE_DAT);
}
mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
FIELD_PREP(MPI_TX_LEAVECS, 1) |
FIELD_PREP(MPI_TX_TXNUM, tx_data ? 8 : 0) |
FIELD_PREP(MPI_TX_TOTNUM, 8);
writeq(mpi_tx, base + MPI_TX);
octeon_spi_wait_ready(dev);
debug("\n ");
if (rx_data) {
wide_dat = readq(base + MPI_WIDE_DAT);
debug(" rx: %016llx\t", (unsigned long long)wide_dat);
*(u64 *)rx_data = wide_dat;
rx_data += 8;
}
len -= 8;
}
debug("\n ");
/* Write and read the rest of the data */
if (tx_data) {
for (i = 0; i < len; i++) {
debug(" tx: %02x\n", *tx_data);
writeq(*tx_data++, base + MPI_DAT(i));
}
}
mpi_tx = FIELD_PREP(MPI_TX_CSID, cs) |
FIELD_PREP(MPI_TX_LEAVECS, !(flags & SPI_XFER_END)) |
FIELD_PREP(MPI_TX_TXNUM, tx_data ? len : 0) |
FIELD_PREP(MPI_TX_TOTNUM, len);
writeq(mpi_tx, base + MPI_TX);
octeon_spi_wait_ready(dev);
debug("\n ");
if (rx_data) {
for (i = 0; i < len; i++) {
*rx_data = readq(base + MPI_DAT(i)) & 0xff;
debug(" rx: %02x\n", *rx_data);
rx_data++;
}
}
return 0;
}
static int octeontx2_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev_get_parent(dev);
struct octeon_spi *priv = dev_get_priv(bus);
void *base = priv->base;
u64 mpi_xmit;
u64 mpi_cfg;
u64 wide_dat = 0;
int len = bitlen / 8;
int rem;
int i;
const u8 *tx_data = dout;
u8 *rx_data = din;
int cs = spi_chip_select(dev);
if (!OCTEON_SPI_CS_VALID(cs))
return -EINVAL;
debug("\n %s(%s, %u, %p, %p, 0x%lx), cs: %d\n",
__func__, dev->name, bitlen, dout, din, flags, cs);
mpi_cfg = octeon_spi_set_mpicfg(dev);
mpi_cfg |= MPI_CFG_TRITX | MPI_CFG_LEGACY_DIS | MPI_CFG_CS_STICKY |
MPI_CFG_TB100_EN;
mpi_cfg &= ~MPI_CFG_IOMODE;
if (flags & (SPI_TX_DUAL | SPI_RX_DUAL))
mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 2);
if (flags & (SPI_TX_QUAD | SPI_RX_QUAD))
mpi_cfg |= FIELD_PREP(MPI_CFG_IOMODE, 3);
if (mpi_cfg != readq(base + MPI_CFG)) {
writeq(mpi_cfg, base + MPI_CFG);
mpi_cfg = readq(base + MPI_CFG);
udelay(10);
}
debug("\n mpi_cfg upd %llx\n\n", mpi_cfg);
/* Start by writing or reading 1024 bytes at a time. */
while (len > 1024) {
if (tx_data) {
/* 8 bytes per iteration */
for (i = 0; i < 128; i++) {
wide_dat = get_unaligned((u64 *)tx_data);
debug(" tx: %016llx \t",
(unsigned long long)wide_dat);
if ((i % 4) == 3)
debug("\n");
tx_data += 8;
writeq(wide_dat, base + MPI_WIDE_BUF(i));
}
}
mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) | MPI_XMIT_LEAVECS |
FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? 1024 : 0) |
FIELD_PREP(MPI_XMIT_TOTNUM, 1024);
writeq(mpi_xmit, base + MPI_XMIT);
octeon_spi_wait_ready(dev);
debug("\n ");
if (rx_data) {
/* 8 bytes per iteration */
for (i = 0; i < 128; i++) {
wide_dat = readq(base + MPI_WIDE_BUF(i));
debug(" rx: %016llx\t",
(unsigned long long)wide_dat);
if ((i % 4) == 3)
debug("\n");
*(u64 *)rx_data = wide_dat;
rx_data += 8;
}
}
len -= 1024;
}
if (tx_data) {
rem = len % 8;
/* 8 bytes per iteration */
for (i = 0; i < len / 8; i++) {
wide_dat = get_unaligned((u64 *)tx_data);
debug(" tx: %016llx \t",
(unsigned long long)wide_dat);
if ((i % 4) == 3)
debug("\n");
tx_data += 8;
writeq(wide_dat, base + MPI_WIDE_BUF(i));
}
if (rem) {
memcpy(&wide_dat, tx_data, rem);
debug(" rtx: %016llx\t", wide_dat);
writeq(wide_dat, base + MPI_WIDE_BUF(i));
}
}
mpi_xmit = FIELD_PREP(MPI_XMIT_CSID, cs) |
FIELD_PREP(MPI_XMIT_LEAVECS, !(flags & SPI_XFER_END)) |
FIELD_PREP(MPI_XMIT_TXNUM, tx_data ? len : 0) |
FIELD_PREP(MPI_XMIT_TOTNUM, len);
writeq(mpi_xmit, base + MPI_XMIT);
octeon_spi_wait_ready(dev);
debug("\n ");
if (rx_data) {
rem = len % 8;
/* 8 bytes per iteration */
for (i = 0; i < len / 8; i++) {
wide_dat = readq(base + MPI_WIDE_BUF(i));
debug(" rx: %016llx\t",
(unsigned long long)wide_dat);
if ((i % 4) == 3)
debug("\n");
*(u64 *)rx_data = wide_dat;
rx_data += 8;
}
if (rem) {
wide_dat = readq(base + MPI_WIDE_BUF(i));
debug(" rrx: %016llx\t",
(unsigned long long)wide_dat);
memcpy(rx_data, &wide_dat, rem);
rx_data += rem;
}
}
return 0;
}
static bool octeon_spi_supports_op(struct spi_slave *slave,
const struct spi_mem_op *op)
{
/* For now, support only below combinations
* 1-1-1
* 1-1-2 1-2-2
* 1-1-4 1-4-4
*/
if (op->cmd.buswidth != 1)
return false;
return true;
}
static int octeon_spi_exec_op(struct spi_slave *slave,
const struct spi_mem_op *op)
{
unsigned long flags = SPI_XFER_BEGIN;
const void *tx;
void *rx;
u8 opcode, *buf;
u8 *addr;
int i, temp, ret;
if (op->cmd.buswidth != 1)
return -ENOTSUPP;
/* Send CMD */
i = 0;
opcode = op->cmd.opcode;
if (!op->data.nbytes && !op->addr.nbytes && !op->dummy.nbytes)
flags |= SPI_XFER_END;
ret = octeontx2_spi_xfer(slave->dev, 8, (void *)&opcode, NULL, flags);
if (ret < 0)
return ret;
/* Send Address and dummy */
if (op->addr.nbytes) {
/* Alloc buffer for address+dummy */
buf = (u8 *)calloc(1, op->addr.nbytes + op->dummy.nbytes);
if (!buf) {
printf("%s Out of memory\n", __func__);
return -ENOMEM;
}
addr = (u8 *)&op->addr.val;
for (temp = 0; temp < op->addr.nbytes; temp++)
buf[i++] = *(u8 *)(addr + op->addr.nbytes - 1 - temp);
for (temp = 0; temp < op->dummy.nbytes; temp++)
buf[i++] = 0xff;
if (op->addr.buswidth == 2)
flags |= SPI_RX_DUAL;
if (op->addr.buswidth == 4)
flags |= SPI_RX_QUAD;
if (!op->data.nbytes)
flags |= SPI_XFER_END;
ret = octeontx2_spi_xfer(slave->dev, i * 8, (void *)buf, NULL,
flags);
free(buf);
if (ret < 0)
return ret;
}
if (!op->data.nbytes)
return 0;
/* Send/Receive Data */
flags |= SPI_XFER_END;
if (op->data.buswidth == 2)
flags |= SPI_RX_DUAL;
if (op->data.buswidth == 4)
flags |= SPI_RX_QUAD;
rx = (op->data.dir == SPI_MEM_DATA_IN) ? op->data.buf.in : NULL;
tx = (op->data.dir == SPI_MEM_DATA_OUT) ? op->data.buf.out : NULL;
ret = octeontx2_spi_xfer(slave->dev, (op->data.nbytes * 8), tx, rx,
flags);
return ret;
}
static const struct spi_controller_mem_ops octeontx2_spi_mem_ops = {
.supports_op = octeon_spi_supports_op,
.exec_op = octeon_spi_exec_op,
};
/**
* Set the speed of the SPI bus
*
* @param bus bus to set
* @param max_hz maximum speed supported
*/
static int octeon_spi_set_speed(struct udevice *bus, uint max_hz)
{
struct octeon_spi *priv = dev_get_priv(bus);
ulong clk_rate;
u32 calc_hz;
if (max_hz > OCTEON_SPI_MAX_CLOCK_HZ)
max_hz = OCTEON_SPI_MAX_CLOCK_HZ;
clk_rate = clk_get_rate(&priv->clk);
if (IS_ERR_VALUE(clk_rate))
return -EINVAL;
debug("%s(%s, %u, %lu)\n", __func__, bus->name, max_hz, clk_rate);
priv->clkdiv = clk_rate / (2 * max_hz);
while (1) {
calc_hz = clk_rate / (2 * priv->clkdiv);
if (calc_hz <= max_hz)
break;
priv->clkdiv += 1;
}
if (priv->clkdiv > 8191)
return -EINVAL;
debug("%s: clkdiv=%d\n", __func__, priv->clkdiv);
return 0;
}
static int octeon_spi_set_mode(struct udevice *bus, uint mode)
{
/* We don't set it here */
return 0;
}
static struct dm_spi_ops octeon_spi_ops = {
.claim_bus = octeon_spi_claim_bus,
.release_bus = octeon_spi_release_bus,
.set_speed = octeon_spi_set_speed,
.set_mode = octeon_spi_set_mode,
.xfer = octeon_spi_xfer,
};
static int octeon_spi_probe(struct udevice *dev)
{
struct octeon_spi *priv = dev_get_priv(dev);
int ret;
/* Octeon TX & TX2 use PCI based probing */
if (device_is_compatible(dev, "cavium,thunder-8190-spi")) {
pci_dev_t bdf = dm_pci_get_bdf(dev);
debug("SPI PCI device: %x\n", bdf);
priv->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
PCI_REGION_MEM);
/* Add base offset */
priv->base += 0x1000;
/*
* Octeon TX2 needs a different xfer function and supports
* mem_ops
*/
if (device_is_compatible(dev, "cavium,thunderx-spi")) {
octeon_spi_ops.xfer = octeontx2_spi_xfer;
octeon_spi_ops.mem_ops = &octeontx2_spi_mem_ops;
}
} else {
priv->base = dev_remap_addr(dev);
}
ret = clk_get_by_index(dev, 0, &priv->clk);
if (ret < 0)
return ret;
ret = clk_enable(&priv->clk);
if (ret)
return ret;
debug("SPI bus %s %d at %p\n", dev->name, dev->seq, priv->base);
return 0;
}
static const struct udevice_id octeon_spi_ids[] = {
/* MIPS Octeon */
{ .compatible = "cavium,octeon-3010-spi" },
/* ARM Octeon TX / TX2 */
{ .compatible = "cavium,thunder-8190-spi" },
{ }
};
U_BOOT_DRIVER(octeon_spi) = {
.name = "spi_octeon",
.id = UCLASS_SPI,
.of_match = octeon_spi_ids,
.probe = octeon_spi_probe,
.priv_auto_alloc_size = sizeof(struct octeon_spi),
.ops = &octeon_spi_ops,
};

View file

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2020 Stefan Roese <sr@denx.de>
*/
#ifndef __DT_BINDINGS_CLOCK_OCTEON_CLOCK_H
#define __DT_BINDINGS_CLOCK_OCTEON_CLOCK_H
#define OCTEON_CLK_CORE 0
#define OCTEON_CLK_IO 1
#endif /* __DT_BINDINGS_CLOCK_OCTEON_CLOCK_H */