
Add a driver for the gdsys IHS AXI bus used on IHS FPGAs.
Signed-off-by: Mario Six mario.six@gdsys.cc
---
v1 -> v2: New in v2
--- drivers/axi/Kconfig | 12 ++++ drivers/axi/Makefile | 1 + drivers/axi/ihs_axi.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 drivers/axi/ihs_axi.c
diff --git a/drivers/axi/Kconfig b/drivers/axi/Kconfig index 4e4153b4283..ae80c98af8a 100644 --- a/drivers/axi/Kconfig +++ b/drivers/axi/Kconfig @@ -11,3 +11,15 @@ menuconfig AXI 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). + +endif diff --git a/drivers/axi/Makefile b/drivers/axi/Makefile index 100a77788a2..18d9380e9ba 100644 --- a/drivers/axi/Makefile +++ b/drivers/axi/Makefile @@ -6,3 +6,4 @@ #
obj-$(CONFIG_AXI) += axi-uclass.o +obj-$(CONFIG_IHS_AXI) += ihs_axi.o diff --git a/drivers/axi/ihs_axi.c b/drivers/axi/ihs_axi.c new file mode 100644 index 00000000000..21227638f78 --- /dev/null +++ b/drivers/axi/ihs_axi.c @@ -0,0 +1,179 @@ +/* + * (C) Copyright 2016 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <axi.h> +#include <dm.h> +#include <regmap.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct ihs_axi_regs { + u16 interrupt_status; + u16 interrupt_enable_control; + u16 address_lsb; + u16 address_msb; + u16 write_data_lsb; + u16 write_data_msb; + u16 read_data_lsb; + u16 read_data_msb; +}; + +#define ihs_axi_set(map, member, val) \ + regmap_set(map, struct ihs_axi_regs, member, val) + +#define ihs_axi_get(map, member, valp) \ + regmap_get(map, struct ihs_axi_regs, member, valp) + +struct ihs_axi_priv { + struct regmap *map; +}; + +enum { + STATUS_EVENT_MASK = GENMASK(15, 12), + + STATUS_READ_COMPLETE_EVENT = BIT(15), + STATUS_WRITE_COMPLETE_EVENT = BIT(14), + STATUS_TIMEOUT_EVENT = BIT(13), + STATUS_ERROR_EVENT = BIT(12), + STATUS_AXI_INT = BIT(11), + STATUS_READ_DATA_AVAILABLE = BIT(7), + STATUS_BUSY = BIT(6), + STATUS_INIT_DONE = BIT(5), +}; + +enum { + CONTROL_EVENT_ENABLE_MASK = GENMASK(15, 11), + CONTROL_CMD_MASK = GENMASK(3, 0), + + CONTROL_READ_COMPLETE_EVENT_ENABLE = BIT(15), + CONTROL_WRITE_COMPLETE_EVENT_ENABLE = BIT(14), + CONTROL_TIMEOUT_EVENT_ENABLE = BIT(13), + CONTROL_ERROR_EVENT_ENABLE = BIT(12), + CONTROL_AXI_INT_ENABLE = BIT(11), + + CONTROL_CMD_NOP = 0x0, + CONTROL_CMD_WRITE = 0x8, + CONTROL_CMD_WRITE_POST_INC = 0x9, + CONTROL_CMD_READ = 0xa, + CONTROL_CMD_READ_POST_INC = 0xb, + +}; + +static int ihs_axi_transfer(struct udevice *bus, ulong address, u16 cmd, + u16 complete_flag) +{ + struct ihs_axi_priv *priv = dev_get_priv(bus); + u16 wait_mask = complete_flag | STATUS_TIMEOUT_EVENT | + STATUS_ERROR_EVENT; + u16 status; + uint k; + + cmd &= CONTROL_CMD_MASK; + + ihs_axi_set(priv->map, address_lsb, address & 0xffff); + ihs_axi_set(priv->map, address_msb, (address >> 16) & 0xffff); + + ihs_axi_set(priv->map, interrupt_status, wait_mask); + ihs_axi_set(priv->map, interrupt_enable_control, cmd); + + for (k = 10; k > 0; --k) { + ihs_axi_get(priv->map, interrupt_status, &status); + if (status & wait_mask) + break; + udelay(1); + } + + if (!k) + ihs_axi_get(priv->map, interrupt_status, &status); + + if (status & complete_flag) + return 0; + + if (status & STATUS_ERROR_EVENT) + return -EIO; + + return -ETIMEDOUT; +} + +/* + * API + */ + +int ihs_axi_read(struct udevice *dev, ulong address, void *data, + enum axi_size_t size) +{ + struct ihs_axi_priv *priv = dev_get_priv(dev); + int res = 0; + u16 data_lsb, data_msb; + u32 *p = data; + + if (size != AXI_SIZE_32) + return -ENOSYS; + + res = ihs_axi_transfer(dev, address, CONTROL_CMD_READ, + STATUS_READ_COMPLETE_EVENT); + if (res < 0) + return res; + + ihs_axi_get(priv->map, read_data_lsb, &data_lsb); + ihs_axi_get(priv->map, read_data_msb, &data_msb); + + *p = (data_msb << 16) | data_lsb; + + return res; +} + +int ihs_axi_write(struct udevice *dev, ulong address, void *data, + enum axi_size_t size) +{ + struct ihs_axi_priv *priv = dev_get_priv(dev); + int res = 0; + u32 *p = data; + + if (size != AXI_SIZE_32) + return -ENOSYS; + + ihs_axi_set(priv->map, write_data_lsb, *p & 0xffff); + ihs_axi_set(priv->map, write_data_msb, (*p >> 16) & 0xffff); + + res = ihs_axi_transfer(dev, address, CONTROL_CMD_WRITE, + STATUS_WRITE_COMPLETE_EVENT); + + return res; +} + +static const struct udevice_id ihs_axi_ids[] = { + { .compatible = "gdsys,ihs_axi" }, + { /* sentinel */ } +}; + +static const struct axi_ops ihs_axi_ops = { + .read = ihs_axi_read, + .write = ihs_axi_write, +}; + +int ihs_axi_probe(struct udevice *dev) +{ + struct ihs_axi_priv *priv = dev_get_priv(dev); + + regmap_init_mem(dev_ofnode(dev), &priv->map); + + return 0; +} + +U_BOOT_DRIVER(ihs_axi_bus) = { + .name = "ihs_axi_bus", + .id = UCLASS_AXI, + .of_match = ihs_axi_ids, + .ops = &ihs_axi_ops, + .priv_auto_alloc_size = sizeof(struct ihs_axi_priv), + .probe = ihs_axi_probe, +}; -- 2.11.0