Commit 282ce645 authored by Tom Rini's avatar Tom Rini
Browse files

Merge branch 'master' of git://git.denx.de/u-boot-video

parents a032e0a6 25a9f974
gdsys AXI busses of IHS FPGA devices
Certain gdsys IHS FPGAs offer a interface to their built-in AXI bus with which
the connected devices (usually IP cores) can be controlled via software.
Required properties:
- compatible: must be "gdsys,ihs_axi"
- reg: describes the address and length of the AXI bus's register map (within
the FPGA's register space)
Example:
fpga0_axi_video0 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "gdsys,ihs_axi";
reg = <0x170 0x10>;
axi_dev_1 {
...
};
};
......@@ -11,6 +11,7 @@
i2c0 = &i2c_0;
pci0 = &pci;
rtc0 = &rtc_0;
axi0 = &axi;
};
chosen {
......@@ -311,6 +312,16 @@
};
};
};
axi: axi@0 {
compatible = "sandbox,axi";
#address-cells = <0x1>;
#size-cells = <0x1>;
store@0 {
compatible = "sandbox,sandbox_store";
reg = <0x0 0x400>;
};
};
};
#include "cros-ec-keyboard.dtsi"
......
......@@ -36,6 +36,7 @@
usb0 = &usb_0;
usb1 = &usb_1;
usb2 = &usb_2;
axi0 = &axi;
};
a-test {
......@@ -552,6 +553,16 @@
compatible = "sandbox,wdt";
};
axi: axi@0 {
compatible = "sandbox,axi";
#address-cells = <0x1>;
#size-cells = <0x1>;
store@0 {
compatible = "sandbox,sandbox_store";
reg = <0x0 0x400>;
};
};
chosen {
#address-cells = <1>;
#size-cells = <1>;
......
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2018
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#ifndef __asm_axi_h
#define __asm_axi_h
#define axi_emul_get_ops(dev) ((struct axi_emul_ops *)(dev)->driver->ops)
/**
* axi_sandbox_get_emul() - Retrieve a pointer to a AXI emulation device
* @bus: The AXI bus from which to retrieve a emulation device
* @address: The address of a transfer that should be handled by a emulation
* device
* @length: The data width of a transfer that should be handled by a emulation
* device
* @emulp: Pointer to a buffer receiving the emulation device that handles
* the transfer specified by the address and length parameters
*
* To test the AXI uclass, we implement a simple AXI emulation device, which is
* a virtual device on a AXI bus that exposes a simple storage interface: When
* reading and writing from the device, the addresses are translated to offsets
* within the device's storage. For write accesses the data is written to the
* specified storage offset, and for read accesses the data is read from the
* specified storage offset.
*
* A DTS entry might look like this:
*
* axi: axi@0 {
* compatible = "sandbox,axi";
* #address-cells = <0x1>;
* #size-cells = <0x1>;
* store@0 {
* compatible = "sandbox,sandbox_store";
* reg = <0x0 0x400>;
* };
* };
*
* This function may then be used to retrieve the pointer to the sandbox_store
* emulation device given the AXI bus device, and the data (address, data
* width) of a AXI transfer which should be handled by a emulation device.
*
* Return: 0 of OK, -ENODEV if no device capable of handling the specified
* transfer exists or the device could not be retrieved
*/
int axi_sandbox_get_emul(struct udevice *bus, ulong address, uint length,
struct udevice **emulp);
/**
* axi_get_store() - Get address of internal storage of a emulated AXI device
* @dev: Emulated AXI device to get the pointer of the internal storage
* for.
* @storep: Pointer to the internal storage of the emulated AXI device.
*
* To preset or read back the contents internal storage of the emulated AXI
* device, this function returns the pointer to the storage. Changes to the
* contents of the storage are reflected when using the AXI read/write API
* methods, and vice versa, so by using this method expected read data can be
* set up in advance, and written data can be checked in unit tests.
*
* Return: 0 if OK, -ve on error.
*/
int axi_get_store(struct udevice *dev, u8 **storep);
#endif /* __asm_axi_h */
......@@ -1027,6 +1027,14 @@ config CMD_USB_MASS_STORAGE
help
USB mass storage support
config CMD_AXI
bool "axi"
depends on AXI
help
Enable the command "axi" for accessing AXI (Advanced eXtensible
Interface) busses, a on-chip interconnect specification for managing
functional blocks in SoC designs, which is also often used in designs
involving FPGAs (e.g. communication with IP cores in Xilinx FPGAs).
endmenu
......
......@@ -149,6 +149,7 @@ obj-$(CONFIG_CMD_ZFS) += zfs.o
obj-$(CONFIG_CMD_DFU) += dfu.o
obj-$(CONFIG_CMD_GPT) += gpt.o
obj-$(CONFIG_CMD_ETHSW) += ethsw.o
obj-$(CONFIG_CMD_AXI) += axi.o
# Power
obj-$(CONFIG_CMD_PMIC) += pmic.o
......
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2016
* Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
*
* (C) Copyright 2017, 2018
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <axi.h>
#include <command.h>
#include <console.h>
#include <dm.h>
/* Currently selected AXI bus device */
static struct udevice *axi_cur_bus;
/* Transmission size from last command */
static uint dp_last_size;
/* Address from last command */
static uint dp_last_addr;
/* Number of bytes to display from last command; default = 64 */
static uint dp_last_length = 0x40;
/**
* show_bus() - Show devices on a single AXI bus
* @bus: The AXI bus device to printt information for
*/
static void show_bus(struct udevice *bus)
{
struct udevice *dev;
printf("Bus %d:\t%s", bus->req_seq, bus->name);
if (device_active(bus))
printf(" (active %d)", bus->seq);
printf("\n");
for (device_find_first_child(bus, &dev);
dev;
device_find_next_child(&dev))
printf(" %s\n", dev->name);
}
/**
* axi_set_cur_bus() - Set the currently active AXI bus
* @busnum: The number of the bus (i.e. its sequence number) that should be
* made active
*
* The operations supplied by this command operate only on the currently active
* bus.
*
* Return: 0 if OK, -ve on error
*/
static int axi_set_cur_bus(unsigned int busnum)
{
struct udevice *bus;
struct udevice *dummy;
int ret;
/* Make sure that all sequence numbers are initialized */
for (uclass_first_device(UCLASS_AXI, &dummy);
dummy;
uclass_next_device(&dummy))
;
ret = uclass_get_device_by_seq(UCLASS_AXI, busnum, &bus);
if (ret) {
debug("%s: No bus %d\n", __func__, busnum);
return ret;
}
axi_cur_bus = bus;
return 0;
}
/**
* axi_get_cur_bus() - Retrieve the currently active AXI bus device
* @busp: Pointer to a struct udevice that receives the currently active bus
* device
*
* Return: 0 if OK, -ve on error
*/
static int axi_get_cur_bus(struct udevice **busp)
{
if (!axi_cur_bus) {
puts("No AXI bus selected\n");
return -ENODEV;
}
*busp = axi_cur_bus;
return 0;
}
/*
* Command handlers
*/
static int do_axi_show_bus(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
struct udevice *dummy;
/* Make sure that all sequence numbers are initialized */
for (uclass_first_device(UCLASS_AXI, &dummy);
dummy;
uclass_next_device(&dummy))
;
if (argc == 1) {
/* show all busses */
struct udevice *bus;
for (uclass_first_device(UCLASS_AXI, &bus);
bus;
uclass_next_device(&bus))
show_bus(bus);
} else {
int i;
/* show specific bus */
i = simple_strtoul(argv[1], NULL, 10);
struct udevice *bus;
int ret;
ret = uclass_get_device_by_seq(UCLASS_AXI, i, &bus);
if (ret) {
printf("Invalid bus %d: err=%d\n", i, ret);
return CMD_RET_FAILURE;
}
show_bus(bus);
}
return 0;
}
static int do_axi_bus_num(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
int ret = 0;
int bus_no;
if (argc == 1) {
/* querying current setting */
struct udevice *bus;
if (!axi_get_cur_bus(&bus))
bus_no = bus->seq;
else
bus_no = -1;
printf("Current bus is %d\n", bus_no);
} else {
bus_no = simple_strtoul(argv[1], NULL, 10);
printf("Setting bus to %d\n", bus_no);
ret = axi_set_cur_bus(bus_no);
if (ret)
printf("Failure changing bus number (%d)\n", ret);
}
return ret ? CMD_RET_FAILURE : 0;
}
static int do_axi_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
/* Print that many bytes per line */
const uint DISP_LINE_LEN = 16;
u8 linebuf[DISP_LINE_LEN];
unsigned int k;
ulong addr, length, size;
ulong nbytes;
enum axi_size_t axisize;
int unitsize;
/*
* We use the last specified parameters, unless new ones are
* entered.
*/
size = dp_last_size;
addr = dp_last_addr;
length = dp_last_length;
if (argc < 3)
return CMD_RET_USAGE;
if (!axi_cur_bus) {
puts("No AXI bus selected\n");
return CMD_RET_FAILURE;
}
if ((flag & CMD_FLAG_REPEAT) == 0) {
size = simple_strtoul(argv[1], NULL, 10);
/*
* Address is specified since argc >= 3
*/
addr = simple_strtoul(argv[2], NULL, 16);
/*
* If there's another parameter, it is the length to display;
* length is the number of objects, not number of bytes
*/
if (argc > 3)
length = simple_strtoul(argv[3], NULL, 16);
}
switch (size) {
case 8:
axisize = AXI_SIZE_8;
unitsize = 1;
break;
case 16:
axisize = AXI_SIZE_16;
unitsize = 2;
break;
case 32:
axisize = AXI_SIZE_32;
unitsize = 4;
break;
default:
printf("Unknown read size '%lu'\n", size);
return CMD_RET_USAGE;
};
nbytes = length * unitsize;
do {
ulong linebytes = (nbytes > DISP_LINE_LEN) ?
DISP_LINE_LEN : nbytes;
for (k = 0; k < linebytes / unitsize; ++k) {
int ret = axi_read(axi_cur_bus, addr + k * unitsize,
linebuf + k * unitsize, axisize);
if (!ret) /* Continue if axi_read was successful */
continue;
if (ret == -ENOSYS)
printf("axi_read failed; read size not supported?\n");
else
printf("axi_read failed: err = %d\n", ret);
return CMD_RET_FAILURE;
}
print_buffer(addr, (void *)linebuf, unitsize,
linebytes / unitsize,
DISP_LINE_LEN / unitsize);
nbytes -= max(linebytes, 1UL);
addr += linebytes;
if (ctrlc())
break;
} while (nbytes > 0);
dp_last_size = size;
dp_last_addr = addr;
dp_last_length = length;
return 0;
}
static int do_axi_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
u32 writeval;
ulong addr, count, size;
enum axi_size_t axisize;
if (argc <= 3 || argc >= 6)
return CMD_RET_USAGE;
size = simple_strtoul(argv[1], NULL, 10);
switch (size) {
case 8:
axisize = AXI_SIZE_8;
break;
case 16:
axisize = AXI_SIZE_16;
break;
case 32:
axisize = AXI_SIZE_32;
break;
default:
printf("Unknown write size '%lu'\n", size);
return CMD_RET_USAGE;
};
/* Address is specified since argc > 4 */
addr = simple_strtoul(argv[2], NULL, 16);
/* Get the value to write */
writeval = simple_strtoul(argv[3], NULL, 16);
/* Count ? */
if (argc == 5)
count = simple_strtoul(argv[4], NULL, 16);
else
count = 1;
while (count-- > 0) {
int ret = axi_write(axi_cur_bus, addr + count * sizeof(u32),
&writeval, axisize);
if (ret) {
printf("axi_write failed: err = %d\n", ret);
return CMD_RET_FAILURE;
}
}
return 0;
}
static cmd_tbl_t cmd_axi_sub[] = {
U_BOOT_CMD_MKENT(bus, 1, 1, do_axi_show_bus, "", ""),
U_BOOT_CMD_MKENT(dev, 1, 1, do_axi_bus_num, "", ""),
U_BOOT_CMD_MKENT(md, 4, 1, do_axi_md, "", ""),
U_BOOT_CMD_MKENT(mw, 5, 1, do_axi_mw, "", ""),
};
static int do_ihs_axi(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
cmd_tbl_t *c;
if (argc < 2)
return CMD_RET_USAGE;
/* Strip off leading 'axi' command argument */
argc--;
argv++;
/* Hand off rest of command line to sub-commands */
c = find_cmd_tbl(argv[0], &cmd_axi_sub[0], ARRAY_SIZE(cmd_axi_sub));
if (c)
return c->cmd(cmdtp, flag, argc, argv);
else
return CMD_RET_USAGE;
}
static char axi_help_text[] =
"bus - show AXI bus info\n"
"axi dev [bus] - show or set current AXI bus to bus number [bus]\n"
"axi md size addr [# of objects] - read from AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n"
"axi mw size addr value [count] - write data [value] to AXI device at address [addr] and data width [size] (one of 8, 16, 32)\n";
U_BOOT_CMD(axi, 7, 1, do_ihs_axi,
"AXI sub-system",
axi_help_text
);
......@@ -45,6 +45,7 @@ CONFIG_CMD_REMOTEPROC=y
CONFIG_CMD_SF=y
CONFIG_CMD_SPI=y
CONFIG_CMD_USB=y
CONFIG_CMD_AXI=y
CONFIG_CMD_TFTPPUT=y
CONFIG_CMD_TFTPSRV=y
CONFIG_CMD_RARP=y
......@@ -80,6 +81,8 @@ CONFIG_DEVRES=y
CONFIG_DEBUG_DEVRES=y
CONFIG_ADC=y
CONFIG_ADC_SANDBOX=y
CONFIG_AXI=y
CONFIG_AXI_SANDBOX=y
CONFIG_CLK=y
CONFIG_CPU=y
CONFIG_DM_DEMO=y
......
......@@ -8,6 +8,8 @@ source "drivers/adc/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig"
source "drivers/block/Kconfig"
source "drivers/bootcount/Kconfig"
......
......@@ -103,6 +103,7 @@ obj-y += smem/
obj-y += soc/
obj-$(CONFIG_REMOTEPROC) += remoteproc/
obj-y += thermal/
obj-y += axi/
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/
endif
menuconfig AXI
bool "AXI bus drivers"
help
Support AXI (Advanced eXtensible Interface) busses, a on-chip
interconnect specification for managing functional blocks in SoC
designs, which is also often used in designs involving FPGAs (e.g.
communication with IP cores in Xilinx FPGAs).
These types of busses expose a virtual address space that can be
accessed using different address widths (8, 16, and 32 are supported
for now).
Other similar bus architectures may be compatible as well.
if AXI
config IHS_AXI
bool "Enable IHS AXI driver"
depends on DM
help
Support for gdsys Integrated Hardware Systems Advanced eXtensible
Interface (IHS AXI) bus on a gdsys IHS FPGA used to communicate with
IP cores in the FPGA (e.g. video transmitter cores).
config AXI_SANDBOX
bool "Enable AXI sandbox driver"
depends on DM
help
Support AXI (Advanced eXtensible Interface) emulation for the sandbox
environment.
endif
#
# (C) Copyright 2017
# Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-$(CONFIG_AXI) += axi-uclass.o
obj-$(CONFIG_IHS_AXI) += ihs_axi.o
obj-$(CONFIG_SANDBOX) += axi-emul-uclass.o
obj-$(CONFIG_SANDBOX) += sandbox_store.o
obj-$(CONFIG_AXI_SANDBOX) += axi_sandbox.o
// SPDX-License-Identifier: GPL-2.0+
/*
* (C) Copyright 2018
* Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
*/
#include <common.h>
#include <axi.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <asm/axi.h>
int axi_sandbox_get_emul(struct udevice *bus, ulong address,
enum axi_size_t size, struct udevice **emulp)
{
struct udevice *dev;
u32 reg[2];
uint offset;
switch (size) {
case AXI_SIZE_8:
offset = 1;
break;
case AXI_SIZE_16:
offset = 2;
break;
case AXI_SIZE_32:
offset = 4;
break;
default:
debug("%s: Unknown AXI transfer size '%d'", bus->name, size);
offset = 0;
}
/*
* Note: device_find_* don't activate the devices; they're activated