[U-Boot] [PATCH v2 1/7] drivers: Add AXI uclass

Add a uclass for AXI (Advanced eXtensible Interface) busses, and a driver for the gdsys IHS AXI bus on IHS FPGAs.
Signed-off-by: Mario Six mario.six@gdsys.cc
---
v1 -> v2: * Spelled out all abbreviations in the Kconfig help * Split commit into uclass addition and driver addition
--- drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/axi/Kconfig | 13 +++++++++ drivers/axi/Makefile | 8 ++++++ drivers/axi/axi-uclass.c | 40 ++++++++++++++++++++++++++ include/axi.h | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 7 files changed, 140 insertions(+) create mode 100644 drivers/axi/Kconfig create mode 100644 drivers/axi/Makefile create mode 100644 drivers/axi/axi-uclass.c create mode 100644 include/axi.h
diff --git a/drivers/Kconfig b/drivers/Kconfig index c2e813f5adf..eeaaa7575cd 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -8,6 +8,8 @@ source "drivers/adc/Kconfig"
source "drivers/ata/Kconfig"
+source "drivers/axi/Kconfig" + source "drivers/block/Kconfig"
source "drivers/bootcount/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index b3f1b600a55..54b396d6368 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -99,6 +99,7 @@ obj-y += input/ obj-y += soc/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-y += thermal/ +obj-y += axi/
obj-$(CONFIG_MACH_PIC32) += ddr/microchip/ endif diff --git a/drivers/axi/Kconfig b/drivers/axi/Kconfig new file mode 100644 index 00000000000..4e4153b4283 --- /dev/null +++ b/drivers/axi/Kconfig @@ -0,0 +1,13 @@ +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. diff --git a/drivers/axi/Makefile b/drivers/axi/Makefile new file mode 100644 index 00000000000..100a77788a2 --- /dev/null +++ b/drivers/axi/Makefile @@ -0,0 +1,8 @@ +# +# (C) Copyright 2017 +# Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_AXI) += axi-uclass.o diff --git a/drivers/axi/axi-uclass.c b/drivers/axi/axi-uclass.c new file mode 100644 index 00000000000..3a5ddf3a46a --- /dev/null +++ b/drivers/axi/axi-uclass.c @@ -0,0 +1,40 @@ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <axi.h> + +int axi_read(struct udevice *dev, ulong address, void *data, + enum axi_size_t size) +{ + struct axi_ops *ops = axi_get_ops(dev); + + if (!ops->read) + return -ENOSYS; + + return ops->read(dev, address, data, size); +} + +int axi_write(struct udevice *dev, ulong address, void *data, + enum axi_size_t size) +{ + struct axi_ops *ops = axi_get_ops(dev); + + if (!ops->write) + return -ENOSYS; + + return ops->write(dev, address, data, size); +} + +UCLASS_DRIVER(axi) = { + .id = UCLASS_AXI, + .name = "axi", + .post_bind = dm_scan_fdt_dev, + .flags = DM_UC_FLAG_SEQ_ALIAS, +}; + diff --git a/include/axi.h b/include/axi.h new file mode 100644 index 00000000000..317e931a6cf --- /dev/null +++ b/include/axi.h @@ -0,0 +1,75 @@ +/* + * (C) Copyright 2017 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _AXI_H_ +#define _AXI_H_ + +enum axi_size_t { + AXI_SIZE_8, + AXI_SIZE_16, + AXI_SIZE_32, +}; + +/** + * struct axi_ops - driver operations for AXI uclass + * + * Drivers should support these operations unless otherwise noted. These + * operations are intended to be used by uclass code, not directly from + * other code. + */ +struct axi_ops { + /** + * read() - Read a single value from a specified address on a AXI bus + * + * @dev: AXI bus to read from. + * @address: The address to read from. + * @data: Pointer to a variable that takes the data value read + * from the address on the AXI bus. + * @size: The size of the data to be read. + * @return 0 if OK, -ve on error. + */ + int (*read)(struct udevice *dev, ulong address, void *data, enum axi_size_t size); + + /** + * write() - Write a single value to a specified address on a AXI bus + * + * @dev: AXI bus to write to. + * @address: The address to write to. + * @data: Pointer to the data value to be written to the address + * on the AXI bus. + * @size: The size of the data to write. + * @return 0 if OK, -ve on error. + */ + int (*write)(struct udevice *dev, ulong address, void *data, enum axi_size_t size); +}; + +#define axi_get_ops(dev) ((struct axi_ops *)(dev)->driver->ops) + +/** + * axi_read() - Read a single value from a specified address on a AXI bus + * + * @dev: AXI bus to read from. + * @address: The address to read from. + * @data: Pointer to a variable that takes the data value read from the + * address on the AXI bus. + * @size: The size of the data to write. + * @return 0 if OK, -ve on error. + */ +int axi_read(struct udevice *dev, ulong address, void *data, enum axi_size_t size); + +/** + * axi_write() - Write a single value to a specified address on a AXI bus + * + * @dev: AXI bus to write to. + * @address: The address to write to. + * @data: Pointer to the data value to be written to the address on the + * AXI bus. + * @size: The size of the data to write. + * @return 0 if OK, -ve on error. + */ +int axi_write(struct udevice *dev, ulong address, void *data, enum axi_size_t size); +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index d7f9df3583a..0aad4bc14d7 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -43,6 +43,7 @@ enum uclass_id { UCLASS_I2C_GENERIC, /* Generic I2C device */ UCLASS_I2C_MUX, /* I2C multiplexer */ UCLASS_IDE, /* IDE device */ + UCLASS_AXI, /* AXI busses */ UCLASS_IRQ, /* Interrupt controller */ UCLASS_KEYBOARD, /* Keyboard input device */ UCLASS_LED, /* Light-emitting diode (LED) */ -- 2.11.0

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

On 23 May 2018 at 06:10, Mario Six mario.six@gdsys.cc wrote:
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
Reviewed-by: Simon Glass sjg@chromium.org

Add test infrastructure and tests for the AXI uclass.
Signed-off-by: Mario Six mario.six@gdsys.cc
---
v1 -> v2: * Spelled out abbreviations in Kconfig help * Expanded emulation documentation * Renamed storage pointer to storep * Expanded AXI emulator usage documentation * Switched AXI emulator to aligned access
--- drivers/axi/Kconfig | 7 +++ drivers/axi/Makefile | 3 ++ drivers/axi/axi-emul-uclass.c | 68 ++++++++++++++++++++++++++ drivers/axi/axi_sandbox.c | 77 +++++++++++++++++++++++++++++ drivers/axi/sandbox_store.c | 110 ++++++++++++++++++++++++++++++++++++++++++ include/axi.h | 84 ++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 7 files changed, 350 insertions(+) create mode 100644 drivers/axi/axi-emul-uclass.c create mode 100644 drivers/axi/axi_sandbox.c create mode 100644 drivers/axi/sandbox_store.c
diff --git a/drivers/axi/Kconfig b/drivers/axi/Kconfig index ae80c98af8a..f81d843f894 100644 --- a/drivers/axi/Kconfig +++ b/drivers/axi/Kconfig @@ -22,4 +22,11 @@ config IHS_AXI 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 diff --git a/drivers/axi/Makefile b/drivers/axi/Makefile index 18d9380e9ba..66b6c5a28f4 100644 --- a/drivers/axi/Makefile +++ b/drivers/axi/Makefile @@ -7,3 +7,6 @@
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 diff --git a/drivers/axi/axi-emul-uclass.c b/drivers/axi/axi-emul-uclass.c new file mode 100644 index 00000000000..8d37935323f --- /dev/null +++ b/drivers/axi/axi-emul-uclass.c @@ -0,0 +1,68 @@ +/* + * (C) Copyright 2018 + * 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 <dm/device-internal.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: + offset = 0; + } + + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + int ret; + + ret = dev_read_u32_array(dev, "reg", reg, ARRAY_SIZE(reg)); + if (ret) + continue; + + if (address >= reg[0] && address <= reg[0] + reg[1] - offset) { + if (device_probe(dev)) + return -ENODEV; + + *emulp = dev; + return 0; + } + } + + return -ENODEV; +} + +int axi_get_store(struct udevice *dev, u8 **storep) +{ + struct axi_emul_ops *ops = axi_emul_get_ops(dev); + + if (!ops->get_store) + return -ENOSYS; + + return ops->get_store(dev, storep); +} + +UCLASS_DRIVER(axi_emul) = { + .id = UCLASS_AXI_EMUL, + .name = "axi_emul", +}; diff --git a/drivers/axi/axi_sandbox.c b/drivers/axi/axi_sandbox.c new file mode 100644 index 00000000000..7a485b114bf --- /dev/null +++ b/drivers/axi/axi_sandbox.c @@ -0,0 +1,77 @@ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <axi.h> +#include <dm.h> + +/* + * This driver implements a AXI bus for the sandbox architecture for testing + * purposes. + * + * The bus forwards every access to it to a special AXI emulation device (which + * it gets via the axi_emul_get_ops function) that implements a simple + * read/write storage. + * + * The emulator device must still be contained in the device tree in the usual + * way, since configuration data for the storage is read from the DT. + */ + +int axi_sandbox_read(struct udevice *bus, ulong address, void *data, + enum axi_size_t size) +{ + struct axi_emul_ops *ops; + struct udevice *emul; + int ret; + + /* Get emulator device */ + ret = axi_sandbox_get_emul(bus, address, size, &emul); + if (ret) + return ret == -ENODEV ? 0 : ret; + /* Forward all reads to the AXI emulator */ + ops = axi_emul_get_ops(emul); + if (!ops || !ops->read) + return -ENOSYS; + + return ops->read(emul, address, data, size); +} + +int axi_sandbox_write(struct udevice *bus, ulong address, void *data, + enum axi_size_t size) +{ + struct axi_emul_ops *ops; + struct udevice *emul; + int ret; + + /* Get emulator device */ + ret = axi_sandbox_get_emul(bus, address, size, &emul); + if (ret) + return ret == -ENODEV ? 0 : ret; + /* Forward all writes to the AXI emulator */ + ops = axi_emul_get_ops(emul); + if (!ops || !ops->write) + return -ENOSYS; + + return ops->write(emul, address, data, size); +} + +static const struct udevice_id axi_sandbox_ids[] = { + { .compatible = "sandbox,axi" }, + { /* sentinel */ } +}; + +static const struct axi_ops axi_sandbox_ops = { + .read = axi_sandbox_read, + .write = axi_sandbox_write, +}; + +U_BOOT_DRIVER(axi_sandbox_bus) = { + .name = "axi_sandbox_bus", + .id = UCLASS_AXI, + .of_match = axi_sandbox_ids, + .ops = &axi_sandbox_ops, +}; diff --git a/drivers/axi/sandbox_store.c b/drivers/axi/sandbox_store.c new file mode 100644 index 00000000000..61c7aea22c8 --- /dev/null +++ b/drivers/axi/sandbox_store.c @@ -0,0 +1,110 @@ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <axi.h> +#include <dm.h> + +struct sandbox_store_priv { + u8 *store; +}; + +void copy_axi_data(void *src, void *dst, enum axi_size_t size) +{ + u8 *dst8 = dst; + u16 *dst16 = dst; + u32 *dst32 = dst; + + switch (size) { + case AXI_SIZE_8: + *dst8 = *((u8 *)src); + break; + case AXI_SIZE_16: + *dst16 = be16_to_cpu(*((u16 *)src)); + break; + case AXI_SIZE_32: + *dst32 = be32_to_cpu(*((u32 *)src)); + break; + } +} + +int sandbox_store_read(struct udevice *dev, ulong address, void *data, + enum axi_size_t size) +{ + struct sandbox_store_priv *priv = dev_get_priv(dev); + + copy_axi_data(priv->store + address, data, size); + + return 0; +} + +int sandbox_store_write(struct udevice *dev, ulong address, void *data, + enum axi_size_t size) +{ + struct sandbox_store_priv *priv = dev_get_priv(dev); + + copy_axi_data(data, priv->store + address, size); + + return 0; +} + +int sandbox_store_get_store(struct udevice *dev, u8 **store) +{ + struct sandbox_store_priv *priv = dev_get_priv(dev); + + *store = priv->store; + + return 0; +} + +static const struct udevice_id sandbox_store_ids[] = { + { .compatible = "sandbox,sandbox_store" }, + { /* sentinel */ } +}; + +static const struct axi_emul_ops sandbox_store_ops = { + .read = sandbox_store_read, + .write = sandbox_store_write, + .get_store = sandbox_store_get_store, +}; + +int sandbox_store_probe(struct udevice *dev) +{ + struct sandbox_store_priv *priv = dev_get_priv(dev); + u32 reg[2]; + int ret; + + ret = dev_read_u32_array(dev, "reg", reg, ARRAY_SIZE(reg)); + if (ret) + return -EINVAL; + + priv->store = calloc(reg[1], 1); + + if (!priv->store) + return -ENOMEM; + + return 0; +} + +int sandbox_store_remove(struct udevice *dev) +{ + struct sandbox_store_priv *priv = dev_get_priv(dev); + + free(priv->store); + + return 0; +} + +U_BOOT_DRIVER(sandbox_axi_store) = { + .name = "sandbox_axi_store", + .id = UCLASS_AXI_EMUL, + .of_match = sandbox_store_ids, + .ops = &sandbox_store_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_store_priv), + .probe = sandbox_store_probe, + .remove = sandbox_store_remove, +}; diff --git a/include/axi.h b/include/axi.h index 317e931a6cf..5b428127f8c 100644 --- a/include/axi.h +++ b/include/axi.h @@ -72,4 +72,88 @@ int axi_read(struct udevice *dev, ulong address, void *data, enum axi_size_t siz * @return 0 if OK, -ve on error. */ int axi_write(struct udevice *dev, ulong address, void *data, enum axi_size_t size); + +struct axi_emul_ops { + /** + * read() - Read a single value from a specified address on a AXI bus + * + * @dev: AXI bus to read from. + * @address: The address to read from. + * @data: Pointer to a variable that takes the data value read + * from the address on the AXI bus. + * @size: The size of the data to be read. + * @return 0 if OK, -ve on error. + */ + int (*read)(struct udevice *dev, ulong address, void *data, enum axi_size_t size); + + /** + * write() - Write a single value to a specified address on a AXI bus + * + * @dev: AXI bus to write to. + * @address: The address to write to. + * @data: Pointer to the data value to be written to the address + * on the AXI bus. + * @size: The size of the data to write. + * @return 0 if OK, -ve on error. + */ + int (*write)(struct udevice *dev, ulong address, void *data, enum axi_size_t size); + + /** + * 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. + * @return 0 if OK, -ve on error. + */ + int (*get_store)(struct udevice *dev, u8 **storep); +}; + +#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 + * + * 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. + */ +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 + * + * 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. + * + * @dev: Emulated AXI device to get the pointer of the internal storage + * for. + * @storep: Pointer to the internal storage of the emulated AXI device. + * @return 0 if OK, -ve on error. + */ +int axi_get_store(struct udevice *dev, u8 **storep); + #endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 0aad4bc14d7..8ff1e4f1bf5 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -23,6 +23,7 @@ enum uclass_id { UCLASS_I2C_EMUL, /* sandbox I2C device emulator */ UCLASS_PCI_EMUL, /* sandbox PCI device emulator */ UCLASS_USB_EMUL, /* sandbox USB bus device emulator */ + UCLASS_AXI_EMUL, /* sandbox AXI bus device emulator */ UCLASS_SIMPLE_BUS, /* bus with child devices */
/* U-Boot uclasses start here - in alphabetical order */ -- 2.11.0

Hi Mario,
On 23 May 2018 at 06:10, Mario Six mario.six@gdsys.cc wrote:
Add test infrastructure and tests for the AXI uclass.
Signed-off-by: Mario Six mario.six@gdsys.cc
v1 -> v2:
- Spelled out abbreviations in Kconfig help
- Expanded emulation documentation
- Renamed storage pointer to storep
- Expanded AXI emulator usage documentation
- Switched AXI emulator to aligned access
drivers/axi/Kconfig | 7 +++ drivers/axi/Makefile | 3 ++ drivers/axi/axi-emul-uclass.c | 68 ++++++++++++++++++++++++++ drivers/axi/axi_sandbox.c | 77 +++++++++++++++++++++++++++++ drivers/axi/sandbox_store.c | 110 ++++++++++++++++++++++++++++++++++++++++++ include/axi.h | 84 ++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 7 files changed, 350 insertions(+) create mode 100644 drivers/axi/axi-emul-uclass.c create mode 100644 drivers/axi/axi_sandbox.c create mode 100644 drivers/axi/sandbox_store.c
Reviewed-by: Simon Glass sjg@chromium.org
Some nits below.
diff --git a/drivers/axi/axi_sandbox.c b/drivers/axi/axi_sandbox.c new file mode 100644 index 00000000000..7a485b114bf --- /dev/null +++ b/drivers/axi/axi_sandbox.c @@ -0,0 +1,77 @@ +/*
- (C) Copyright 2018
- Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <axi.h> +#include <dm.h>
+/*
- This driver implements a AXI bus for the sandbox architecture for testing
- purposes.
- The bus forwards every access to it to a special AXI emulation device (which
- it gets via the axi_emul_get_ops function) that implements a simple
- read/write storage.
- The emulator device must still be contained in the device tree in the usual
- way, since configuration data for the storage is read from the DT.
- */
+int axi_sandbox_read(struct udevice *bus, ulong address, void *data,
enum axi_size_t size)
Can this and the write() function below be static?
+{
struct axi_emul_ops *ops;
struct udevice *emul;
int ret;
/* Get emulator device */
ret = axi_sandbox_get_emul(bus, address, size, &emul);
if (ret)
return ret == -ENODEV ? 0 : ret;
/* Forward all reads to the AXI emulator */
ops = axi_emul_get_ops(emul);
if (!ops || !ops->read)
return -ENOSYS;
return ops->read(emul, address, data, size);
+}
+int axi_sandbox_write(struct udevice *bus, ulong address, void *data,
enum axi_size_t size)
+{
struct axi_emul_ops *ops;
struct udevice *emul;
int ret;
/* Get emulator device */
ret = axi_sandbox_get_emul(bus, address, size, &emul);
if (ret)
return ret == -ENODEV ? 0 : ret;
/* Forward all writes to the AXI emulator */
ops = axi_emul_get_ops(emul);
if (!ops || !ops->write)
return -ENOSYS;
return ops->write(emul, address, data, size);
+}
+static const struct udevice_id axi_sandbox_ids[] = {
{ .compatible = "sandbox,axi" },
{ /* sentinel */ }
+};
+static const struct axi_ops axi_sandbox_ops = {
.read = axi_sandbox_read,
.write = axi_sandbox_write,
+};
+U_BOOT_DRIVER(axi_sandbox_bus) = {
.name = "axi_sandbox_bus",
.id = UCLASS_AXI,
.of_match = axi_sandbox_ids,
.ops = &axi_sandbox_ops,
+}; diff --git a/drivers/axi/sandbox_store.c b/drivers/axi/sandbox_store.c new file mode 100644 index 00000000000..61c7aea22c8 --- /dev/null +++ b/drivers/axi/sandbox_store.c @@ -0,0 +1,110 @@ +/*
- (C) Copyright 2018
- Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <axi.h> +#include <dm.h>
+struct sandbox_store_priv {
u8 *store;
+};
+void copy_axi_data(void *src, void *dst, enum axi_size_t size) +{
u8 *dst8 = dst;
u16 *dst16 = dst;
u32 *dst32 = dst;
Can you put these declarations in the case statements instead? I'm not sure what coverity with think of this aliasing.
switch (size) {
case AXI_SIZE_8:
*dst8 = *((u8 *)src);
break;
case AXI_SIZE_16:
*dst16 = be16_to_cpu(*((u16 *)src));
break;
case AXI_SIZE_32:
*dst32 = be32_to_cpu(*((u32 *)src));
break;
}
+}
+int sandbox_store_read(struct udevice *dev, ulong address, void *data,
enum axi_size_t size)
+{
struct sandbox_store_priv *priv = dev_get_priv(dev);
copy_axi_data(priv->store + address, data, size);
return 0;
+}
+int sandbox_store_write(struct udevice *dev, ulong address, void *data,
enum axi_size_t size)
+{
struct sandbox_store_priv *priv = dev_get_priv(dev);
copy_axi_data(data, priv->store + address, size);
return 0;
+}
+int sandbox_store_get_store(struct udevice *dev, u8 **store) +{
struct sandbox_store_priv *priv = dev_get_priv(dev);
*store = priv->store;
return 0;
+}
+static const struct udevice_id sandbox_store_ids[] = {
{ .compatible = "sandbox,sandbox_store" },
{ /* sentinel */ }
+};
+static const struct axi_emul_ops sandbox_store_ops = {
.read = sandbox_store_read,
.write = sandbox_store_write,
.get_store = sandbox_store_get_store,
+};
+int sandbox_store_probe(struct udevice *dev) +{
struct sandbox_store_priv *priv = dev_get_priv(dev);
u32 reg[2];
int ret;
ret = dev_read_u32_array(dev, "reg", reg, ARRAY_SIZE(reg));
if (ret)
return -EINVAL;
priv->store = calloc(reg[1], 1);
drop blank line
if (!priv->store)
return -ENOMEM;
return 0;
+}
+int sandbox_store_remove(struct udevice *dev) +{
struct sandbox_store_priv *priv = dev_get_priv(dev);
free(priv->store);
return 0;
+}
+U_BOOT_DRIVER(sandbox_axi_store) = {
.name = "sandbox_axi_store",
.id = UCLASS_AXI_EMUL,
.of_match = sandbox_store_ids,
.ops = &sandbox_store_ops,
.priv_auto_alloc_size = sizeof(struct sandbox_store_priv),
.probe = sandbox_store_probe,
.remove = sandbox_store_remove,
+}; diff --git a/include/axi.h b/include/axi.h index 317e931a6cf..5b428127f8c 100644 --- a/include/axi.h +++ b/include/axi.h @@ -72,4 +72,88 @@ int axi_read(struct udevice *dev, ulong address, void *data, enum axi_size_t siz
- @return 0 if OK, -ve on error.
*/ int axi_write(struct udevice *dev, ulong address, void *data, enum axi_size_t size);
+struct axi_emul_ops {
/**
* read() - Read a single value from a specified address on a AXI bus
*
* @dev: AXI bus to read from.
* @address: The address to read from.
* @data: Pointer to a variable that takes the data value read
* from the address on the AXI bus.
* @size: The size of the data to be read.
* @return 0 if OK, -ve on error.
*/
int (*read)(struct udevice *dev, ulong address, void *data, enum axi_size_t size);
/**
* write() - Write a single value to a specified address on a AXI bus
*
* @dev: AXI bus to write to.
* @address: The address to write to.
* @data: Pointer to the data value to be written to the address
* on the AXI bus.
* @size: The size of the data to write.
* @return 0 if OK, -ve on error.
*/
int (*write)(struct udevice *dev, ulong address, void *data, enum axi_size_t size);
/**
* 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.
* @return 0 if OK, -ve on error.
*/
int (*get_store)(struct udevice *dev, u8 **storep);
+};
+#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
- 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.
- */
+int axi_sandbox_get_emul(struct udevice *bus, ulong address, uint length,
struct udevice **emulp);
Can these two function declarations go in arch/sandbox/include/asm perhaps? I don't think they are needed except for sandbox?
+/**
- axi_get_store() - Get address of internal storage of a 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.
- @dev: Emulated AXI device to get the pointer of the internal storage
for.
- @storep: Pointer to the internal storage of the emulated AXI device.
- @return 0 if OK, -ve on error.
- */
+int axi_get_store(struct udevice *dev, u8 **storep);
#endif
Regards, Simon

Hi Simon,
On Fri, May 25, 2018 at 4:41 AM, Simon Glass sjg@chromium.org wrote:
Hi Mario,
On 23 May 2018 at 06:10, Mario Six mario.six@gdsys.cc wrote:
Add test infrastructure and tests for the AXI uclass.
Signed-off-by: Mario Six mario.six@gdsys.cc
v1 -> v2:
- Spelled out abbreviations in Kconfig help
- Expanded emulation documentation
- Renamed storage pointer to storep
- Expanded AXI emulator usage documentation
- Switched AXI emulator to aligned access
drivers/axi/Kconfig | 7 +++ drivers/axi/Makefile | 3 ++ drivers/axi/axi-emul-uclass.c | 68 ++++++++++++++++++++++++++ drivers/axi/axi_sandbox.c | 77 +++++++++++++++++++++++++++++ drivers/axi/sandbox_store.c | 110 ++++++++++++++++++++++++++++++++++++++++++ include/axi.h | 84 ++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 7 files changed, 350 insertions(+) create mode 100644 drivers/axi/axi-emul-uclass.c create mode 100644 drivers/axi/axi_sandbox.c create mode 100644 drivers/axi/sandbox_store.c
Reviewed-by: Simon Glass sjg@chromium.org
Some nits below.
diff --git a/drivers/axi/axi_sandbox.c b/drivers/axi/axi_sandbox.c new file mode 100644 index 00000000000..7a485b114bf --- /dev/null +++ b/drivers/axi/axi_sandbox.c @@ -0,0 +1,77 @@ +/*
- (C) Copyright 2018
- Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <axi.h> +#include <dm.h>
+/*
- This driver implements a AXI bus for the sandbox architecture for testing
- purposes.
- The bus forwards every access to it to a special AXI emulation device (which
- it gets via the axi_emul_get_ops function) that implements a simple
- read/write storage.
- The emulator device must still be contained in the device tree in the usual
- way, since configuration data for the storage is read from the DT.
- */
+int axi_sandbox_read(struct udevice *bus, ulong address, void *data,
enum axi_size_t size)
Can this and the write() function below be static?
Yes, sure, will be fixed in v3.
+{
struct axi_emul_ops *ops;
struct udevice *emul;
int ret;
/* Get emulator device */
ret = axi_sandbox_get_emul(bus, address, size, &emul);
if (ret)
return ret == -ENODEV ? 0 : ret;
/* Forward all reads to the AXI emulator */
ops = axi_emul_get_ops(emul);
if (!ops || !ops->read)
return -ENOSYS;
return ops->read(emul, address, data, size);
+}
+int axi_sandbox_write(struct udevice *bus, ulong address, void *data,
enum axi_size_t size)
+{
struct axi_emul_ops *ops;
struct udevice *emul;
int ret;
/* Get emulator device */
ret = axi_sandbox_get_emul(bus, address, size, &emul);
if (ret)
return ret == -ENODEV ? 0 : ret;
/* Forward all writes to the AXI emulator */
ops = axi_emul_get_ops(emul);
if (!ops || !ops->write)
return -ENOSYS;
return ops->write(emul, address, data, size);
+}
+static const struct udevice_id axi_sandbox_ids[] = {
{ .compatible = "sandbox,axi" },
{ /* sentinel */ }
+};
+static const struct axi_ops axi_sandbox_ops = {
.read = axi_sandbox_read,
.write = axi_sandbox_write,
+};
+U_BOOT_DRIVER(axi_sandbox_bus) = {
.name = "axi_sandbox_bus",
.id = UCLASS_AXI,
.of_match = axi_sandbox_ids,
.ops = &axi_sandbox_ops,
+}; diff --git a/drivers/axi/sandbox_store.c b/drivers/axi/sandbox_store.c new file mode 100644 index 00000000000..61c7aea22c8 --- /dev/null +++ b/drivers/axi/sandbox_store.c @@ -0,0 +1,110 @@ +/*
- (C) Copyright 2018
- Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <axi.h> +#include <dm.h>
+struct sandbox_store_priv {
u8 *store;
+};
+void copy_axi_data(void *src, void *dst, enum axi_size_t size) +{
u8 *dst8 = dst;
u16 *dst16 = dst;
u32 *dst32 = dst;
Can you put these declarations in the case statements instead? I'm not sure what coverity with think of this aliasing.
Yeah, coverity probably won't like that. I'll put the casts in the case statements for v3.
switch (size) {
case AXI_SIZE_8:
*dst8 = *((u8 *)src);
break;
case AXI_SIZE_16:
*dst16 = be16_to_cpu(*((u16 *)src));
break;
case AXI_SIZE_32:
*dst32 = be32_to_cpu(*((u32 *)src));
break;
}
+}
+int sandbox_store_read(struct udevice *dev, ulong address, void *data,
enum axi_size_t size)
+{
struct sandbox_store_priv *priv = dev_get_priv(dev);
copy_axi_data(priv->store + address, data, size);
return 0;
+}
+int sandbox_store_write(struct udevice *dev, ulong address, void *data,
enum axi_size_t size)
+{
struct sandbox_store_priv *priv = dev_get_priv(dev);
copy_axi_data(data, priv->store + address, size);
return 0;
+}
+int sandbox_store_get_store(struct udevice *dev, u8 **store) +{
struct sandbox_store_priv *priv = dev_get_priv(dev);
*store = priv->store;
return 0;
+}
+static const struct udevice_id sandbox_store_ids[] = {
{ .compatible = "sandbox,sandbox_store" },
{ /* sentinel */ }
+};
+static const struct axi_emul_ops sandbox_store_ops = {
.read = sandbox_store_read,
.write = sandbox_store_write,
.get_store = sandbox_store_get_store,
+};
+int sandbox_store_probe(struct udevice *dev) +{
struct sandbox_store_priv *priv = dev_get_priv(dev);
u32 reg[2];
int ret;
ret = dev_read_u32_array(dev, "reg", reg, ARRAY_SIZE(reg));
if (ret)
return -EINVAL;
priv->store = calloc(reg[1], 1);
drop blank line
Will be done for v3.
if (!priv->store)
return -ENOMEM;
return 0;
+}
+int sandbox_store_remove(struct udevice *dev) +{
struct sandbox_store_priv *priv = dev_get_priv(dev);
free(priv->store);
return 0;
+}
+U_BOOT_DRIVER(sandbox_axi_store) = {
.name = "sandbox_axi_store",
.id = UCLASS_AXI_EMUL,
.of_match = sandbox_store_ids,
.ops = &sandbox_store_ops,
.priv_auto_alloc_size = sizeof(struct sandbox_store_priv),
.probe = sandbox_store_probe,
.remove = sandbox_store_remove,
+}; diff --git a/include/axi.h b/include/axi.h index 317e931a6cf..5b428127f8c 100644 --- a/include/axi.h +++ b/include/axi.h @@ -72,4 +72,88 @@ int axi_read(struct udevice *dev, ulong address, void *data, enum axi_size_t siz
- @return 0 if OK, -ve on error.
*/ int axi_write(struct udevice *dev, ulong address, void *data, enum axi_size_t size);
+struct axi_emul_ops {
/**
* read() - Read a single value from a specified address on a AXI bus
*
* @dev: AXI bus to read from.
* @address: The address to read from.
* @data: Pointer to a variable that takes the data value read
* from the address on the AXI bus.
* @size: The size of the data to be read.
* @return 0 if OK, -ve on error.
*/
int (*read)(struct udevice *dev, ulong address, void *data, enum axi_size_t size);
/**
* write() - Write a single value to a specified address on a AXI bus
*
* @dev: AXI bus to write to.
* @address: The address to write to.
* @data: Pointer to the data value to be written to the address
* on the AXI bus.
* @size: The size of the data to write.
* @return 0 if OK, -ve on error.
*/
int (*write)(struct udevice *dev, ulong address, void *data, enum axi_size_t size);
/**
* 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.
* @return 0 if OK, -ve on error.
*/
int (*get_store)(struct udevice *dev, u8 **storep);
+};
+#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
- 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.
- */
+int axi_sandbox_get_emul(struct udevice *bus, ulong address, uint length,
struct udevice **emulp);
Can these two function declarations go in arch/sandbox/include/asm perhaps? I don't think they are needed except for sandbox?
Yes, no problem. Will be fixed in v3.
+/**
- axi_get_store() - Get address of internal storage of a 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.
- @dev: Emulated AXI device to get the pointer of the internal storage
for.
- @storep: Pointer to the internal storage of the emulated AXI device.
- @return 0 if OK, -ve on error.
- */
+int axi_get_store(struct udevice *dev, u8 **storep);
#endif
Regards, Simon
Best regards, Mario

Add test AXI drivers to the sandbox.
Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Mario Six mario.six@gdsys.cc
---
v1 -> v2: No changes
--- arch/sandbox/dts/sandbox.dts | 11 +++++++++++ arch/sandbox/dts/test.dts | 11 +++++++++++ configs/sandbox_defconfig | 3 +++ 3 files changed, 25 insertions(+)
diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index 86680a6b11d..8a236ea76b2 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -12,6 +12,7 @@ i2c0 = &i2c_0; pci0 = &pci; rtc0 = &rtc_0; + axi0 = &axi; };
chosen { @@ -315,6 +316,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" diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 5a0f187d8b7..5822c5dd59a 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -34,6 +34,7 @@ usb0 = &usb_0; usb1 = &usb_1; usb2 = &usb_2; + axi0 = &axi; };
a-test { @@ -507,6 +508,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>; diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 2fc84a16c91..83107c7f99a 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -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 @@ -81,6 +82,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 -- 2.11.0

Add tests for the AXI uclass.
Signed-off-by: Mario Six mario.six@gdsys.cc
---
v1 -> v2: * Fixed asserts (moved expected values first)
--- test/dm/Makefile | 1 + test/dm/axi.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 test/dm/axi.c
diff --git a/test/dm/Makefile b/test/dm/Makefile index 5511a85700c..de8b9940888 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -43,4 +43,5 @@ obj-$(CONFIG_DM_VIDEO) += video.o obj-$(CONFIG_ADC) += adc.o obj-$(CONFIG_SPMI) += spmi.o obj-$(CONFIG_WDT) += wdt.o +obj-$(CONFIG_AXI) += axi.o endif diff --git a/test/dm/axi.c b/test/dm/axi.c new file mode 100644 index 00000000000..cd31efd6126 --- /dev/null +++ b/test/dm/axi.c @@ -0,0 +1,74 @@ +/* + * (C) Copyright 2018 + * 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 <dm/test.h> +#include <test/ut.h> + +/* Test that sandbox AXI works correctly */ +static int dm_test_axi_base(struct unit_test_state *uts) +{ + struct udevice *bus; + + ut_assertok(uclass_get_device(UCLASS_AXI, 0, &bus)); + + return 0; +} +DM_TEST(dm_test_axi_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that sandbox PCI bus numbering works correctly */ +static int dm_test_axi_busnum(struct unit_test_state *uts) +{ + struct udevice *bus; + + ut_assertok(uclass_get_device_by_seq(UCLASS_AXI, 0, &bus)); + + return 0; +} +DM_TEST(dm_test_axi_busnum, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test that we can use the store device correctly */ +static int dm_test_axi_store(struct unit_test_state *uts) +{ + struct udevice *store; + u8 tdata1[] = {0x55, 0x66, 0x77, 0x88}; + u8 tdata2[] = {0xAA, 0xBB, 0xCC, 0xDD}; + u32 val; + u8 *data; + + /* Check that asking for the device automatically fires up AXI */ + ut_assertok(uclass_get_device(UCLASS_AXI_EMUL, 0, &store)); + ut_assert(device_active(store)); + + axi_get_store(store, &data); + + /* Test reading */ + memcpy(data, tdata1, ARRAY_SIZE(tdata1)); + axi_read(store, 0, &val, AXI_SIZE_32); + ut_asserteq(0x55667788, val); + + memcpy(data + 3, tdata2, ARRAY_SIZE(tdata2)); + axi_read(store, 3, &val, AXI_SIZE_32); + ut_asserteq(0xAABBCCDD, val); + + /* Reset data store */ + memset(data, 0, 16); + + /* Test writing */ + val = 0x55667788; + axi_write(store, 0, &val, AXI_SIZE_32); + ut_asserteq(0, memcmp(data, tdata1, ARRAY_SIZE(tdata1))); + + val = 0xAABBCCDD; + axi_write(store, 3, &val, AXI_SIZE_32); + ut_asserteq(0, memcmp(data + 3, tdata2, ARRAY_SIZE(tdata1))); + + return 0; +} +DM_TEST(dm_test_axi_store, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); -- 2.11.0

On 23 May 2018 at 06:10, Mario Six mario.six@gdsys.cc wrote:
Add tests for the AXI uclass.
Signed-off-by: Mario Six mario.six@gdsys.cc
v1 -> v2:
- Fixed asserts (moved expected values first)
test/dm/Makefile | 1 + test/dm/axi.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 test/dm/axi.c
Reviewed-by: Simon Glass sjg@chromium.org
Can you please use lower-case hex?

Hi Simon,
On Fri, May 25, 2018 at 4:41 AM, Simon Glass sjg@chromium.org wrote:
On 23 May 2018 at 06:10, Mario Six mario.six@gdsys.cc wrote:
Add tests for the AXI uclass.
Signed-off-by: Mario Six mario.six@gdsys.cc
v1 -> v2:
- Fixed asserts (moved expected values first)
test/dm/Makefile | 1 + test/dm/axi.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 test/dm/axi.c
Reviewed-by: Simon Glass sjg@chromium.org
Can you please use lower-case hex?
OK, will switch to lower-case for v3.
Best regards, Mario

Add a command to debug the AXI bus.
Signed-off-by: Mario Six mario.six@gdsys.cc
---
v1 -> v2: No changes
--- cmd/Kconfig | 8 ++ cmd/Makefile | 1 + cmd/axi.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 cmd/axi.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 38406fcfdac..0f730f7a397 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -995,6 +995,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
diff --git a/cmd/Makefile b/cmd/Makefile index 0d7322ee0a4..cfca11499ec 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -145,6 +145,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 diff --git a/cmd/axi.c b/cmd/axi.c new file mode 100644 index 00000000000..d8001875e2c --- /dev/null +++ b/cmd/axi.c @@ -0,0 +1,310 @@ +/* + * (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 <dm.h> +#include <command.h> +#include <console.h> +#include <axi.h> + +static struct udevice *axi_cur_bus; +static uint dp_last_size; +static uint dp_last_addr; +static uint dp_last_length = 0x40; + +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); +} + +static int do_axi_show_bus(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + 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 cmd_axi_set_bus_num(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; +} + +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; +} + +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 = cmd_axi_set_bus_num(bus_no); + + if (ret) + printf("Failure changing bus number (%d)\n", ret); + } + + return ret ? CMD_RET_FAILURE : 0; +} + +#define DISP_LINE_LEN 16 + +int do_axi_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned int k; + ulong addr, length, size; + int rc = 0; + u32 linebuf[DISP_LINE_LEN / sizeof(u32)]; + 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 > 4 + */ + addr = simple_strtoul(argv[2], NULL, 16); + + /* + * If 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: + 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 res = axi_read(axi_cur_bus, addr + k * unitsize, + &linebuf[k], axisize); + if (res) { + if (res == -ENOSYS) + printf("axi_read failed; read size not supported?\n"); + else + printf("axi_read failed: %d\n", res); + + return CMD_RET_FAILURE; + } + } + print_buffer(addr, (void *)linebuf, unitsize, + linebytes / unitsize, + DISP_LINE_LEN / unitsize); + + nbytes -= linebytes; + addr += linebytes; + if (ctrlc()) { + rc = 1; + break; + } + } while (nbytes > 0); + + dp_last_size = size; + dp_last_addr = addr; + dp_last_length = length; + + return rc; +} + +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 < 4) || (argc > 5)) + return CMD_RET_USAGE; + + size = simple_strtoul(argv[1], NULL, 16); + + switch (size) { + case 8: + axisize = AXI_SIZE_8; + break; + case 16: + axisize = AXI_SIZE_16; + break; + case 32: + axisize = AXI_SIZE_32; + break; + default: + 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 res = axi_write(axi_cur_bus, addr + count * sizeof(u32), + &writeval, axisize); + + if (res) + return res; + } + + 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++; + + 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 [dev] - show or set current AXI bus\n" + "axi md size address [# of objects] - read from AXI device\n" + "axi mw size address value [count] - write to AXI device (fill)\n"; + +U_BOOT_CMD( + axi, 7, 1, do_ihs_axi, + "AXI sub-system", + axi_help_text +); -- 2.11.0

Hi Mario,
On 23 May 2018 at 06:10, Mario Six mario.six@gdsys.cc wrote:
Add a command to debug the AXI bus.
Signed-off-by: Mario Six mario.six@gdsys.cc
v1 -> v2: No changes
cmd/Kconfig | 8 ++ cmd/Makefile | 1 + cmd/axi.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 cmd/axi.c
Reviewed-by: Simon Glass sjg@chromium.org
Comments below.
diff --git a/cmd/Kconfig b/cmd/Kconfig index 38406fcfdac..0f730f7a397 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -995,6 +995,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
diff --git a/cmd/Makefile b/cmd/Makefile index 0d7322ee0a4..cfca11499ec 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -145,6 +145,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 diff --git a/cmd/axi.c b/cmd/axi.c new file mode 100644 index 00000000000..d8001875e2c --- /dev/null +++ b/cmd/axi.c @@ -0,0 +1,310 @@ +/*
- (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 <dm.h> +#include <command.h> +#include <console.h> +#include <axi.h>
+static struct udevice *axi_cur_bus; +static uint dp_last_size; +static uint dp_last_addr; +static uint dp_last_length = 0x40;
+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);
+}
+static int do_axi_show_bus(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
+{
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);
Here you probe the bus, whereas above you don't. Is that intended?
if (ret) {
printf("Invalid bus %d: err=%d\n", i, ret);
return CMD_RET_FAILURE;
}
show_bus(bus);
}
return 0;
+}
+static int cmd_axi_set_bus_num(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);
Here you probe the bus which seems right to me.
if (ret) {
debug("%s: No bus %d\n", __func__, busnum);
return ret;
}
axi_cur_bus = bus;
return 0;
+}
[..]
Regards, Simon

Hi Simon,
On Fri, May 25, 2018 at 4:41 AM, Simon Glass sjg@chromium.org wrote:
Hi Mario,
On 23 May 2018 at 06:10, Mario Six mario.six@gdsys.cc wrote:
Add a command to debug the AXI bus.
Signed-off-by: Mario Six mario.six@gdsys.cc
v1 -> v2: No changes
cmd/Kconfig | 8 ++ cmd/Makefile | 1 + cmd/axi.c | 310 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 319 insertions(+) create mode 100644 cmd/axi.c
Reviewed-by: Simon Glass sjg@chromium.org
Comments below.
diff --git a/cmd/Kconfig b/cmd/Kconfig index 38406fcfdac..0f730f7a397 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -995,6 +995,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
diff --git a/cmd/Makefile b/cmd/Makefile index 0d7322ee0a4..cfca11499ec 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -145,6 +145,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 diff --git a/cmd/axi.c b/cmd/axi.c new file mode 100644 index 00000000000..d8001875e2c --- /dev/null +++ b/cmd/axi.c @@ -0,0 +1,310 @@ +/*
- (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 <dm.h> +#include <command.h> +#include <console.h> +#include <axi.h>
+static struct udevice *axi_cur_bus; +static uint dp_last_size; +static uint dp_last_addr; +static uint dp_last_length = 0x40;
+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);
+}
+static int do_axi_show_bus(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
+{
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);
Here you probe the bus, whereas above you don't. Is that intended?
Right, I should probably make sure that the busses are probed in either case, good point.
Will be fixed in v3.
if (ret) {
printf("Invalid bus %d: err=%d\n", i, ret);
return CMD_RET_FAILURE;
}
show_bus(bus);
}
return 0;
+}
+static int cmd_axi_set_bus_num(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);
Here you probe the bus which seems right to me.
I'll use this same approach in the other function as well.
if (ret) {
debug("%s: No bus %d\n", __func__, busnum);
return ret;
}
axi_cur_bus = bus;
return 0;
+}
[..]
Regards, Simon
Best regards, Mario

Add a driver for the Xilinx LogiCORE DisplayPort IP core, which is a pure DP transmitter core for Xiling FPGA (no display capabilities).
Signed-off-by: Mario Six mario.six@gdsys.cc
---
v1 -> v2: * Switch to display_enable * Mentioned that the LogiCORE has no display capabilities
--- drivers/video/Kconfig | 11 + drivers/video/Makefile | 1 + drivers/video/logicore_dp_dpcd.h | 342 ++++++ drivers/video/logicore_dp_tx.c | 1975 ++++++++++++++++++++++++++++++++++ drivers/video/logicore_dp_tx.h | 40 + drivers/video/logicore_dp_tx_regif.h | 365 +++++++ 6 files changed, 2734 insertions(+) create mode 100644 drivers/video/logicore_dp_dpcd.h create mode 100644 drivers/video/logicore_dp_tx.c create mode 100644 drivers/video/logicore_dp_tx.h create mode 100644 drivers/video/logicore_dp_tx_regif.h
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 4c4d2861fe7..ddec697e359 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -423,6 +423,17 @@ config ATMEL_HLCD help HLCDC supports video output to an attached LCD panel.
+config LOGICORE_DP_TX + bool "Enable Logicore DP TX driver" + depends on DISPLAY + help + Enable the driver for the transmitter part of the Xilinx LogiCORE + DisplayPort, a IP core for Xilinx FPGAs that implements a DisplayPort + video interface as defined by VESA DisplayPort v1.2. + + Note that this is a pure transmitter device, and has no display + capabilities by itself. + config VIDEO_BROADWELL_IGD bool "Enable Intel Broadwell integrated graphics device" depends on X86 diff --git a/drivers/video/Makefile b/drivers/video/Makefile index cf7ad281c3b..fa4ac715fcf 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o
obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o +obj-$(CONFIG_LOGICORE_DP_TX) += logicore_dp_tx.o obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o obj-$(CONFIG_CFB_CONSOLE) += cfb_console.o obj-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o diff --git a/drivers/video/logicore_dp_dpcd.h b/drivers/video/logicore_dp_dpcd.h new file mode 100644 index 00000000000..68582945514 --- /dev/null +++ b/drivers/video/logicore_dp_dpcd.h @@ -0,0 +1,342 @@ +/* + * logicore_dp_dpcd.h + * + * DPCD interface definition for XILINX LogiCore DisplayPort v6.1 + * based on Xilinx dp_v3_1 driver sources + * + * (C) Copyright 2016 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __GDSYS_LOGICORE_DP_DPCD_H__ +#define __GDSYS_LOGICORE_DP_DPCD_H__ + +/* receiver capability field */ +#define DPCD_REV 0x00000 +#define DPCD_MAX_LINK_RATE 0x00001 +#define DPCD_MAX_LANE_COUNT 0x00002 +#define DPCD_MAX_DOWNSPREAD 0x00003 +#define DPCD_NORP_PWR_V_CAP 0x00004 +#define DPCD_DOWNSP_PRESENT 0x00005 +#define DPCD_ML_CH_CODING_CAP 0x00006 +#define DPCD_DOWNSP_COUNT_MSA_OUI 0x00007 +#define DPCD_RX_PORT0_CAP_0 0x00008 +#define DPCD_RX_PORT0_CAP_1 0x00009 +#define DPCD_RX_PORT1_CAP_0 0x0000A +#define DPCD_RX_PORT1_CAP_1 0x0000B +#define DPCD_I2C_SPEED_CTL_CAP 0x0000C +#define DPCD_EDP_CFG_CAP 0x0000D +#define DPCD_TRAIN_AUX_RD_INTERVAL 0x0000E +#define DPCD_ADAPTER_CAP 0x0000F +#define DPCD_FAUX_CAP 0x00020 +#define DPCD_MSTM_CAP 0x00021 +#define DPCD_NUM_AUDIO_EPS 0x00022 +#define DPCD_AV_GRANULARITY 0x00023 +#define DPCD_AUD_DEC_LAT_7_0 0x00024 +#define DPCD_AUD_DEC_LAT_15_8 0x00025 +#define DPCD_AUD_PP_LAT_7_0 0x00026 +#define DPCD_AUD_PP_LAT_15_8 0x00027 +#define DPCD_VID_INTER_LAT 0x00028 +#define DPCD_VID_PROG_LAT 0x00029 +#define DPCD_REP_LAT 0x0002A +#define DPCD_AUD_DEL_INS_7_0 0x0002B +#define DPCD_AUD_DEL_INS_15_8 0x0002C +#define DPCD_AUD_DEL_INS_23_16 0x0002D +#define DPCD_GUID 0x00030 +#define DPCD_RX_GTC_VALUE_7_0 0x00054 +#define DPCD_RX_GTC_VALUE_15_8 0x00055 +#define DPCD_RX_GTC_VALUE_23_16 0x00056 +#define DPCD_RX_GTC_VALUE_31_24 0x00057 +#define DPCD_RX_GTC_MSTR_REQ 0x00058 +#define DPCD_RX_GTC_FREQ_LOCK_DONE 0x00059 +#define DPCD_DOWNSP_0_CAP 0x00080 +#define DPCD_DOWNSP_1_CAP 0x00081 +#define DPCD_DOWNSP_2_CAP 0x00082 +#define DPCD_DOWNSP_3_CAP 0x00083 +#define DPCD_DOWNSP_0_DET_CAP 0x00080 +#define DPCD_DOWNSP_1_DET_CAP 0x00084 +#define DPCD_DOWNSP_2_DET_CAP 0x00088 +#define DPCD_DOWNSP_3_DET_CAP 0x0008C + +/* link configuration field */ +#define DPCD_LINK_BW_SET 0x00100 +#define DPCD_LANE_COUNT_SET 0x00101 +#define DPCD_TP_SET 0x00102 +#define DPCD_TRAINING_LANE0_SET 0x00103 +#define DPCD_TRAINING_LANE1_SET 0x00104 +#define DPCD_TRAINING_LANE2_SET 0x00105 +#define DPCD_TRAINING_LANE3_SET 0x00106 +#define DPCD_DOWNSPREAD_CTRL 0x00107 +#define DPCD_ML_CH_CODING_SET 0x00108 +#define DPCD_I2C_SPEED_CTL_SET 0x00109 +#define DPCD_EDP_CFG_SET 0x0010A +#define DPCD_LINK_QUAL_LANE0_SET 0x0010B +#define DPCD_LINK_QUAL_LANE1_SET 0x0010C +#define DPCD_LINK_QUAL_LANE2_SET 0x0010D +#define DPCD_LINK_QUAL_LANE3_SET 0x0010E +#define DPCD_TRAINING_LANE0_1_SET2 0x0010F +#define DPCD_TRAINING_LANE2_3_SET2 0x00110 +#define DPCD_MSTM_CTRL 0x00111 +#define DPCD_AUDIO_DELAY_7_0 0x00112 +#define DPCD_AUDIO_DELAY_15_8 0x00113 +#define DPCD_AUDIO_DELAY_23_6 0x00114 +#define DPCD_UPSTREAM_DEVICE_DP_PWR_NEED 0x00118 +#define DPCD_FAUX_MODE_CTRL 0x00120 +#define DPCD_FAUX_FORWARD_CH_DRIVE_SET 0x00121 +#define DPCD_BACK_CH_STATUS 0x00122 +#define DPCD_FAUX_BACK_CH_SYMBOL_ERROR_COUNT 0x00123 +#define DPCD_FAUX_BACK_CH_TRAINING_PATTERN_TIME 0x00125 +#define DPCD_TX_GTC_VALUE_7_0 0x00154 +#define DPCD_TX_GTC_VALUE_15_8 0x00155 +#define DPCD_TX_GTC_VALUE_23_16 0x00156 +#define DPCD_TX_GTC_VALUE_31_24 0x00157 +#define DPCD_RX_GTC_VALUE_PHASE_SKEW_EN 0x00158 +#define DPCD_TX_GTC_FREQ_LOCK_DONE 0x00159 +#define DPCD_ADAPTER_CTRL 0x001A0 +#define DPCD_BRANCH_DEVICE_CTRL 0x001A1 +#define DPCD_PAYLOAD_ALLOCATE_SET 0x001C0 +#define DPCD_PAYLOAD_ALLOCATE_START_TIME_SLOT 0x001C1 +#define DPCD_PAYLOAD_ALLOCATE_TIME_SLOT_COUNT 0x001C2 + +/* link/sink status field */ +#define DPCD_SINK_COUNT 0x00200 +#define DPCD_DEVICE_SERVICE_IRQ 0x00201 +#define DPCD_STATUS_LANE_0_1 0x00202 +#define DPCD_STATUS_LANE_2_3 0x00203 +#define DPCD_LANE_ALIGN_STATUS_UPDATED 0x00204 +#define DPCD_SINK_STATUS 0x00205 +#define DPCD_ADJ_REQ_LANE_0_1 0x00206 +#define DPCD_ADJ_REQ_LANE_2_3 0x00207 +#define DPCD_TRAINING_SCORE_LANE_0 0x00208 +#define DPCD_TRAINING_SCORE_LANE_1 0x00209 +#define DPCD_TRAINING_SCORE_LANE_2 0x0020A +#define DPCD_TRAINING_SCORE_LANE_3 0x0020B +#define DPCD_ADJ_REQ_PC2 0x0020C +#define DPCD_FAUX_FORWARD_CH_SYMBOL_ERROR_COUNT 0x0020D +#define DPCD_SYMBOL_ERROR_COUNT_LANE_0 0x00210 +#define DPCD_SYMBOL_ERROR_COUNT_LANE_1 0x00212 +#define DPCD_SYMBOL_ERROR_COUNT_LANE_2 0x00214 +#define DPCD_SYMBOL_ERROR_COUNT_LANE_3 0x00216 + +/* automated testing sub-field */ +#define DPCD_FAUX_FORWARD_CH_STATUS 0x00280 +#define DPCD_FAUX_BACK_CH_DRIVE_SET 0x00281 +#define DPCD_FAUX_BACK_CH_SYM_ERR_COUNT_CTRL 0x00282 +#define DPCD_PAYLOAD_TABLE_UPDATE_STATUS 0x002C0 +#define DPCD_VC_PAYLOAD_ID_SLOT(SlotNum) \ + (DPCD_PAYLOAD_TABLE_UPDATE_STATUS + SlotNum) + +/* sink control field */ +#define DPCD_SET_POWER_DP_PWR_VOLTAGE 0x00600 + +/* sideband message buffers */ +#define DPCD_DOWN_REQ 0x01000 +#define DPCD_UP_REP 0x01200 +#define DPCD_DOWN_REP 0x01400 +#define DPCD_UP_REQ 0x01600 + +/* event status indicator field */ +#define DPCD_SINK_COUNT_ESI 0x02002 +#define DPCD_SINK_DEVICE_SERVICE_IRQ_VECTOR_ESI0 0x02003 +#define DPCD_SINK_DEVICE_SERVICE_IRQ_VECTOR_ESI1 0x02004 +#define DPCD_SINK_LINK_SERVICE_IRQ_VECTOR_ESI0 0x02005 +#define DPCD_SINK_LANE0_1_STATUS 0x0200C +#define DPCD_SINK_LANE2_3_STATUS 0x0200D +#define DPCD_SINK_ALIGN_STATUS_UPDATED_ESI 0x0200E +#define DPCD_SINK_STATUS_ESI 0x0200F + +/* + * field addresses and sizes. + */ +#define DPCD_RECEIVER_CAP_FIELD_START DPCD_REV +#define DPCD_RECEIVER_CAP_FIELD_SIZE 0x100 +#define DPCD_LINK_CFG_FIELD_START DPCD_LINK_BW_SET +#define DPCD_LINK_CFG_FIELD_SIZE 0x100 +#define DPCD_LINK_SINK_STATUS_FIELD_START DPCD_SINK_COUNT +#define DPCD_LINK_SINK_STATUS_FIELD_SIZE 0x17 +/* 0x00000: DPCD_REV */ +#define DPCD_REV_MNR_MASK 0x0F +#define DPCD_REV_MJR_MASK 0xF0 +#define DPCD_REV_MJR_SHIFT 4 +/* 0x00001: MAX_LINK_RATE */ +#define DPCD_MAX_LINK_RATE_162GBPS 0x06 +#define DPCD_MAX_LINK_RATE_270GBPS 0x0A +#define DPCD_MAX_LINK_RATE_540GBPS 0x14 +/* 0x00002: MAX_LANE_COUNT */ +#define DPCD_MAX_LANE_COUNT_MASK 0x1F +#define DPCD_MAX_LANE_COUNT_1 0x01 +#define DPCD_MAX_LANE_COUNT_2 0x02 +#define DPCD_MAX_LANE_COUNT_4 0x04 +#define DPCD_TPS3_SUPPORT_MASK 0x40 +#define DPCD_ENHANCED_FRAME_SUPPORT_MASK 0x80 +/* 0x00003: MAX_DOWNSPREAD */ +#define DPCD_MAX_DOWNSPREAD_MASK 0x01 +#define DPCD_NO_AUX_HANDSHAKE_LINK_TRAIN_MASK 0x40 +/* 0x00005: DOWNSP_PRESENT */ +#define DPCD_DOWNSP_PRESENT_MASK 0x01 +#define DPCD_DOWNSP_TYPE_MASK 0x06 +#define DPCD_DOWNSP_TYPE_SHIFT 1 +#define DPCD_DOWNSP_TYPE_DP 0x0 +#define DPCD_DOWNSP_TYPE_AVGA_ADVII 0x1 +#define DPCD_DOWNSP_TYPE_DVI_HDMI_DPPP 0x2 +#define DPCD_DOWNSP_TYPE_OTHERS 0x3 +#define DPCD_DOWNSP_FORMAT_CONV_MASK 0x08 +#define DPCD_DOWNSP_DCAP_INFO_AVAIL_MASK 0x10 +/* 0x00006, 0x00108: ML_CH_CODING_SUPPORT, ML_CH_CODING_SET */ +#define DPCD_ML_CH_CODING_MASK 0x01 +/* 0x00007: DOWNSP_COUNT_MSA_OUI */ +#define DPCD_DOWNSP_COUNT_MASK 0x0F +#define DPCD_MSA_TIMING_PAR_IGNORED_MASK 0x40 +#define DPCD_OUI_SUPPORT_MASK 0x80 +/* 0x00008, 0x0000A: RX_PORT[0-1]_CAP_0 */ +#define DPCD_RX_PORTX_CAP_0_LOCAL_EDID_PRESENT_MASK 0x02 +#define DPCD_RX_PORTX_CAP_0_ASSOC_TO_PRECEDING_PORT_MASK 0x04 +/* 0x0000C, 0x00109: I2C_SPEED_CTL_CAP, I2C_SPEED_CTL_SET */ +#define DPCD_I2C_SPEED_CTL_NONE 0x00 +#define DPCD_I2C_SPEED_CTL_1KBIPS 0x01 +#define DPCD_I2C_SPEED_CTL_5KBIPS 0x02 +#define DPCD_I2C_SPEED_CTL_10KBIPS 0x04 +#define DPCD_I2C_SPEED_CTL_100KBIPS 0x08 +#define DPCD_I2C_SPEED_CTL_400KBIPS 0x10 +#define DPCD_I2C_SPEED_CTL_1MBIPS 0x20 +/* 0x0000E: TRAIN_AUX_RD_INTERVAL */ +#define DPCD_TRAIN_AUX_RD_INT_100_400US 0x00 +#define DPCD_TRAIN_AUX_RD_INT_4MS 0x01 +#define DPCD_TRAIN_AUX_RD_INT_8MS 0x02 +#define DPCD_TRAIN_AUX_RD_INT_12MS 0x03 +#define DPCD_TRAIN_AUX_RD_INT_16MS 0x04 +/* 0x00020: DPCD_FAUX_CAP */ +#define DPCD_FAUX_CAP_MASK 0x01 +/* 0x00021: MSTM_CAP */ +#define DPCD_MST_CAP_MASK 0x01 +/* 0x00080, 0x00081|4, 0x00082|8, 0x00083|C: DOWNSP_X_(DET_)CAP */ +#define DPCD_DOWNSP_X_CAP_TYPE_MASK 0x07 +#define DPCD_DOWNSP_X_CAP_TYPE_DP 0x0 +#define DPCD_DOWNSP_X_CAP_TYPE_AVGA 0x1 +#define DPCD_DOWNSP_X_CAP_TYPE_DVI 0x2 +#define DPCD_DOWNSP_X_CAP_TYPE_HDMI 0x3 +#define DPCD_DOWNSP_X_CAP_TYPE_OTHERS 0x4 +#define DPCD_DOWNSP_X_CAP_TYPE_DPPP 0x5 +#define DPCD_DOWNSP_X_CAP_HPD_MASK 0x80 +#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_MASK 0xF0 +#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_SHIFT 4 +#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_720_480_I_60 0x1 +#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_720_480_I_50 0x2 +#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1920_1080_I_60 0x3 +#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1920_1080_I_50 0x4 +#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1280_720_P_60 0x5 +#define DPCD_DOWNSP_X_CAP_NON_EDID_ATTR_1280_720_P_50 0x7 +/* 0x00082, 0x00086, 0x0008A, 0x0008E: DOWNSP_X_(DET_)CAP2 */ +#define DPCD_DOWNSP_X_DCAP_MAX_BPC_MASK 0x03 +#define DPCD_DOWNSP_X_DCAP_MAX_BPC_8 0x0 +#define DPCD_DOWNSP_X_DCAP_MAX_BPC_10 0x1 +#define DPCD_DOWNSP_X_DCAP_MAX_BPC_12 0x2 +#define DPCD_DOWNSP_X_DCAP_MAX_BPC_16 0x3 +/* 0x00082, 0x00086, 0x0008A, 0x0008E: DOWNSP_X_(DET_)CAP2 */ +#define DPCD_DOWNSP_X_DCAP_HDMI_DPPP_FS2FP_MASK 0x01 +#define DPCD_DOWNSP_X_DCAP_DVI_DL_MASK 0x02 +#define DPCD_DOWNSP_X_DCAP_DVI_HCD_MASK 0x04 + +/* link configuration field masks, shifts, and register values */ +/* 0x00100: DPCD_LINK_BW_SET */ +#define DPCD_LINK_BW_SET_162GBPS 0x06 +#define DPCD_LINK_BW_SET_270GBPS 0x0A +#define DPCD_LINK_BW_SET_540GBPS 0x14 +/* 0x00101: LANE_COUNT_SET */ +#define DPCD_LANE_COUNT_SET_MASK 0x1F +#define DPCD_LANE_COUNT_SET_1 0x01 +#define DPCD_LANE_COUNT_SET_2 0x02 +#define DPCD_LANE_COUNT_SET_4 0x04 +#define DPCD_ENHANCED_FRAME_EN_MASK 0x80 +/* 0x00102: TP_SET */ +#define DPCD_TP_SEL_MASK 0x03 +#define DPCD_TP_SEL_OFF 0x0 +#define DPCD_TP_SEL_TP1 0x1 +#define DPCD_TP_SEL_TP2 0x2 +#define DPCD_TP_SEL_TP3 0x3 +#define DPCD_TP_SET_LQP_MASK 0x06 +#define DPCD_TP_SET_LQP_SHIFT 2 +#define DPCD_TP_SET_LQP_OFF 0x0 +#define DPCD_TP_SET_LQP_D102_TEST 0x1 +#define DPCD_TP_SET_LQP_SER_MES 0x2 +#define DPCD_TP_SET_LQP_PRBS7 0x3 +#define DPCD_TP_SET_REC_CLK_OUT_EN_MASK 0x10 +#define DPCD_TP_SET_SCRAMB_DIS_MASK 0x20 +#define DPCD_TP_SET_SE_COUNT_SEL_MASK 0xC0 +#define DPCD_TP_SET_SE_COUNT_SEL_SHIFT 6 +#define DPCD_TP_SET_SE_COUNT_SEL_DE_ISE 0x0 +#define DPCD_TP_SET_SE_COUNT_SEL_DE 0x1 +#define DPCD_TP_SET_SE_COUNT_SEL_ISE 0x2 +/* 0x00103-0x00106: TRAINING_LANE[0-3]_SET */ +#define DPCD_TRAINING_LANEX_SET_VS_MASK 0x03 +#define DPCD_TRAINING_LANEX_SET_MAX_VS_MASK 0x04 +#define DPCD_TRAINING_LANEX_SET_PE_MASK 0x18 +#define DPCD_TRAINING_LANEX_SET_PE_SHIFT 3 +#define DPCD_TRAINING_LANEX_SET_MAX_PE_MASK 0x20 +/* 0x00107: DOWNSPREAD_CTRL */ +#define DPCD_SPREAD_AMP_MASK 0x10 +#define DPCD_MSA_TIMING_PAR_IGNORED_EN_MASK 0x80 +/* 0x00108: ML_CH_CODING_SET - Same as 0x00006: ML_CH_CODING_SUPPORT */ +/* 0x00109: I2C_SPEED_CTL_SET - Same as 0x0000C: I2C_SPEED_CTL_CAP */ +/* 0x0010F-0x00110: TRAINING_LANE[0_1-2_3]_SET2 */ +#define DPCD_TRAINING_LANE_0_2_SET_PC2_MASK 0x03 +#define DPCD_TRAINING_LANE_0_2_SET_MAX_PC2_MASK 0x04 +#define DPCD_TRAINING_LANE_1_3_SET_PC2_MASK 0x30 +#define DPCD_TRAINING_LANE_1_3_SET_PC2_SHIFT 4 +#define DPCD_TRAINING_LANE_1_3_SET_MAX_PC2_MASK 0x40 +/* 0x00111: MSTM_CTRL */ +#define DPCD_MST_EN_MASK 0x01 +#define DPCD_UP_REQ_EN_MASK 0x02 +#define DPCD_UP_IS_SRC_MASK 0x03 + +/* link/sink status field masks, shifts, and register values */ +/* 0x00200: SINK_COUNT */ +#define DPCD_SINK_COUNT_LOW_MASK 0x3F +#define DPCD_SINK_CP_READY_MASK 0x40 +#define DPCD_SINK_COUNT_HIGH_MASK 0x80 +#define DPCD_SINK_COUNT_HIGH_LOW_SHIFT 1 +/* 0x00202: STATUS_LANE_0_1 */ +#define DPCD_STATUS_LANE_0_CR_DONE_MASK 0x01 +#define DPCD_STATUS_LANE_0_CE_DONE_MASK 0x02 +#define DPCD_STATUS_LANE_0_SL_DONE_MASK 0x04 +#define DPCD_STATUS_LANE_1_CR_DONE_MASK 0x10 +#define DPCD_STATUS_LANE_1_CE_DONE_MASK 0x20 +#define DPCD_STATUS_LANE_1_SL_DONE_MASK 0x40 +/* 0x00202: STATUS_LANE_2_3 */ +#define DPCD_STATUS_LANE_2_CR_DONE_MASK 0x01 +#define DPCD_STATUS_LANE_2_CE_DONE_MASK 0x02 +#define DPCD_STATUS_LANE_2_SL_DONE_MASK 0x04 +#define DPCD_STATUS_LANE_3_CR_DONE_MASK 0x10 +#define DPCD_STATUS_LANE_3_CE_DONE_MASK 0x20 +#define DPCD_STATUS_LANE_3_SL_DONE_MASK 0x40 +/* 0x00204: LANE_ALIGN_STATUS_UPDATED */ +#define DPCD_LANE_ALIGN_STATUS_UPDATED_IA_DONE_MASK \ + 0x01 +#define DPCD_LANE_ALIGN_STATUS_UPDATED_DOWNSP_STATUS_CHANGED_MASK \ + 0x40 +#define DPCD_LANE_ALIGN_STATUS_UPDATED_LINK_STATUS_UPDATED_MASK \ + 0x80 +/* 0x00205: SINK_STATUS */ +#define DPCD_SINK_STATUS_RX_PORT0_SYNC_STATUS_MASK 0x01 +#define DPCD_SINK_STATUS_RX_PORT1_SYNC_STATUS_MASK 0x02 + +/* 0x00206, 0x00207: ADJ_REQ_LANE_[0,2]_[1,3] */ +#define DPCD_ADJ_REQ_LANE_0_2_VS_MASK 0x03 +#define DPCD_ADJ_REQ_LANE_0_2_PE_MASK 0x0C +#define DPCD_ADJ_REQ_LANE_0_2_PE_SHIFT 2 +#define DPCD_ADJ_REQ_LANE_1_3_VS_MASK 0x30 +#define DPCD_ADJ_REQ_LANE_1_3_VS_SHIFT 4 +#define DPCD_ADJ_REQ_LANE_1_3_PE_MASK 0xC0 +#define DPCD_ADJ_REQ_LANE_1_3_PE_SHIFT 6 +/* 0x0020C: ADJ_REQ_PC2 */ +#define DPCD_ADJ_REQ_PC2_LANE_0_MASK 0x03 +#define DPCD_ADJ_REQ_PC2_LANE_1_MASK 0x0C +#define DPCD_ADJ_REQ_PC2_LANE_1_SHIFT 2 +#define DPCD_ADJ_REQ_PC2_LANE_2_MASK 0x30 +#define DPCD_ADJ_REQ_PC2_LANE_2_SHIFT 4 +#define DPCD_ADJ_REQ_PC2_LANE_3_MASK 0xC0 +#define DPCD_ADJ_REQ_PC2_LANE_3_SHIFT 6 + +#endif /* __GDSYS_LOGICORE_DP_DPCD_H__ */ diff --git a/drivers/video/logicore_dp_tx.c b/drivers/video/logicore_dp_tx.c new file mode 100644 index 00000000000..99d1035d571 --- /dev/null +++ b/drivers/video/logicore_dp_tx.c @@ -0,0 +1,1975 @@ +/* + * logicore_dp_tx.c + * + * Driver for XILINX LogiCore DisplayPort v6.1 TX (Source) + * based on Xilinx dp_v3_1 driver sources, updated to dp_v4_0 + * + * (C) Copyright 2016 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <display.h> + +#include "axi.h" + +#include "logicore_dp_dpcd.h" +#include "logicore_dp_tx_regif.h" +#include "logicore_dp_tx.h" + +#define S_AXI_CLK_DEFAULT 100000000 + +#define PHY_CLOCK_SELECT_DEFAULT PHY_CLOCK_SELECT_540GBPS + +/* The maximum voltage swing level is 3. */ +#define MAXIMUM_VS_LEVEL 3 +/* The maximum pre-emphasis level is 3. */ +#define MAXIMUM_PE_LEVEL 3 + +/* Error out if an AUX request yields a defer reply more than 50 times. */ +#define AUX_MAX_DEFER_COUNT 50 +/* Error out if an AUX request times out more than 50 times awaiting a reply. */ +#define AUX_MAX_TIMEOUT_COUNT 50 +/* Error out if checking for a connected device times out more than 50 times. */ +#define IS_CONNECTED_MAX_TIMEOUT_COUNT 50 + +enum { + TS_CLOCK_RECOVERY, + TS_CHANNEL_EQUALIZATION, + TS_ADJUST_LINK_RATE, + TS_ADJUST_LANE_COUNT, + TS_FAILURE, + TS_SUCCESS +}; + +struct aux_transaction { + u16 cmd_code; + u8 num_bytes; + u32 address; + u8 *data; +}; + +/* + * all porch parameters have been removed because our videodata is hstart/vstart + * based and there is no benefit in keeping the porches + */ + +struct main_stream_attributes { + /* Pixel clock of the stream (in Hz). */ + u32 pixel_clock_hz; + /* Miscellaneous stream attributes 0 as specified by the DisplayPort + * 1.2 specification. + */ + u32 misc_0; + /* Miscellaneous stream attributes 1 as specified by the DisplayPort + * 1.2 specification. + */ + u32 misc_1; + /* N value for the video stream. */ + u32 n_vid; + /* M value used to recover the video clock from the link clock. */ + u32 m_vid; + /* Width of the user data input port. */ + u32 user_pixel_width; + /* Used to translate the number of pixels per line to the native + * internal 16-bit datapath. + */ + u32 data_per_lane; + /* Average number of bytes per transfer unit, scaled up by a factor of + * 1000. + */ + u32 avg_bytes_per_tu; + /* Size of the transfer unit in the framing logic. In MST mode, this is + * also the number of time slots that are alloted in the payload ID + * table. + */ + u32 transfer_unit_size; + /* Number of initial wait cycles at the start of a new line by the + * framing logic. + */ + u32 init_wait; + /* Bits per color component. */ + u32 bits_per_color; + /* The component format currently in use by the video stream. */ + u8 component_format; + /* The dynamic range currently in use by the video stream. */ + u8 dynamic_range; + /* The YCbCr colorimetry currently in use by the video stream. */ + u8 y_cb_cr_colorimetry; + /* Synchronous clock mode is currently in use by the video stream. */ + u8 synchronous_clock_mode; + /* If set to 1, the value stored for user_pixel_width will be used as + * the pixel width. + */ + u8 override_user_pixel_width; + /* Horiz. blank start (pixels). */ + u32 h_start; + /* Horiz. active resolution (pixels) */ + u16 h_active; + /* Horiz. sync width (pixels) */ + u16 h_sync_width; + /* Horiz. total (pixels) */ + u16 h_total; + /* Horiz. sync polarity (0=neg|1=pos) */ + bool h_sync_polarity; + /* Vert. blank start (in lines). */ + u32 v_start; + /* Vert. active resolution (lines) */ + u16 v_active; + /* Vert. sync width (lines) */ + u16 v_sync_width; + /* Vert. total (lines) */ + u16 v_total; + /* Vert. sync polarity (0=neg|1=pos) */ + bool v_sync_polarity; +}; + +struct link_config { + u8 lane_count; + u8 link_rate; + bool scrambler_en; + bool enhanced_framing_mode; + bool downspread_control; + u8 max_lane_count; + u8 max_link_rate; + bool support_enhanced_framing_mode; + bool support_downspread_control; + u8 vs_level; /* voltage swing for each lane */ + u8 pe_level; /* pre-emphasis/cursor level for each lane */ + u8 pattern; +}; + +struct dp_tx { + u32 base; + + u32 s_axi_clk; + + bool train_adaptive; + + u8 max_link_rate; + u8 max_lane_count; + + u8 dpcd_rx_caps[16]; + + /* This is a raw read of the RX device's status registers. The first 4 + * bytes correspond to the lane status associated with clock recovery, + * channel equalization, symbol lock, and interlane alignment. The + * remaining 2 bytes represent the pre-emphasis and voltage swing level + * adjustments requested by the RX device. + */ + u8 lane_status_ajd_reqs[6]; + + struct link_config link_config; + struct main_stream_attributes main_stream_attributes; +}; + +/* + * internal API + */ + +static u32 get_reg(struct udevice *dev, u32 reg) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + u32 value = 0; + int res; + + // TODO error handling + res = axi_read(dev->parent, dp_tx->base + reg, &value, AXI_SIZE_32); + if (res < 0) + printf("%s() failed; res = %d\n", __func__, res); + + return value; +} + +static void set_reg(struct udevice *dev, u32 reg, u32 value) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + + axi_write(dev->parent, dp_tx->base + reg, &value, AXI_SIZE_32); +} + +/* + * Check if there is a connected RX device. + * The Xilinx original calls msleep_interruptible at least once, + * ignoring status. + */ +bool is_connected(struct udevice *dev) +{ + u8 retries = 0; + + do { + int status = get_reg(dev, REG_INTERRUPT_SIG_STATE) & + INTERRUPT_SIG_STATE_HPD_STATE_MASK; + if (status) + return true; + + udelay(1000); + } while (retries++ < IS_CONNECTED_MAX_TIMEOUT_COUNT); + + return false; +} + +/* + * Wait for the DisplayPort PHY to come out of reset. + */ +static int wait_phy_ready(struct udevice *dev, u32 mask) +{ + u16 timeout = 20000; + u32 phy_status; + + /* Wait until the PHY is ready. */ + do { + phy_status = get_reg(dev, REG_PHY_STATUS) & mask; + + /* Protect against an infinite loop. */ + if (!timeout--) + return -ETIMEDOUT; + + udelay(20); + } while (phy_status != mask); + + return 0; +} + +/* AUX channel access */ + +/* + * Wait until another request is no longer in progress. + */ +static int aux_wait_ready(struct udevice *dev) +{ + int status; + u32 timeout = 100; + + /* Wait until the DisplayPort TX core is ready. */ + do { + status = get_reg(dev, REG_INTERRUPT_SIG_STATE); + + /* Protect against an infinite loop. */ + if (!timeout--) + return -ETIMEDOUT; + udelay(20); + } while (status & REPLY_STATUS_REPLY_IN_PROGRESS_MASK); + + return 0; +} + +/* + * Wait for a reply indicating that the most recent AUX request + * has been received by the RX device. + */ +static int aux_wait_reply(struct udevice *dev) +{ + u32 timeout = 100; + + while (timeout > 0) { + int status = get_reg(dev, REG_REPLY_STATUS); + + /* Check for error. */ + if (status & REPLY_STATUS_REPLY_ERROR_MASK) + return -ETIMEDOUT; + + /* Check for a reply. */ + if ((status & REPLY_STATUS_REPLY_RECEIVED_MASK) && + !(status & + REPLY_STATUS_REQUEST_IN_PROGRESS_MASK) && + !(status & + REPLY_STATUS_REPLY_IN_PROGRESS_MASK)) { + return 0; + } + + timeout--; + udelay(20); + } + + return -ETIMEDOUT; +} + +/* + * Submit the supplied AUX request to the RX device over the AUX + * channel by writing the command, the destination address, (the write buffer + * for write commands), and the data size to the DisplayPort TX core. + */ +static int aux_request_send(struct udevice *dev, + struct aux_transaction *request) +{ + u32 timeout_count; + int status; + u8 index; + + /* Ensure that any pending AUX transactions have completed. */ + timeout_count = 0; + do { + status = get_reg(dev, REG_REPLY_STATUS); + + udelay(20); + timeout_count++; + if (timeout_count >= AUX_MAX_TIMEOUT_COUNT) + return -ETIMEDOUT; + } while ((status & REPLY_STATUS_REQUEST_IN_PROGRESS_MASK) || + (status & REPLY_STATUS_REPLY_IN_PROGRESS_MASK)); + + set_reg(dev, REG_AUX_ADDRESS, request->address); + + if ((request->cmd_code == AUX_CMD_WRITE) || + (request->cmd_code == AUX_CMD_I2C_WRITE) || + (request->cmd_code == AUX_CMD_I2C_WRITE_MOT)) { + /* Feed write data into the DisplayPort TX core's write FIFO. */ + for (index = 0; index < request->num_bytes; index++) { + set_reg(dev, + REG_AUX_WRITE_FIFO, request->data[index]); + } + } + + /* Submit the command and the data size. */ + set_reg(dev, REG_AUX_CMD, + ((request->cmd_code << AUX_CMD_SHIFT) | + ((request->num_bytes - 1) & + AUX_CMD_NBYTES_TRANSFER_MASK))); + + /* Check for a reply from the RX device to the submitted request. */ + status = aux_wait_reply(dev); + if (status) + /* Waiting for a reply timed out. */ + return -ETIMEDOUT; + + /* Analyze the reply. */ + status = get_reg(dev, REG_AUX_REPLY_CODE); + if ((status == AUX_REPLY_CODE_DEFER) || + (status == AUX_REPLY_CODE_I2C_DEFER)) { + /* The request was deferred. */ + return -EAGAIN; + } else if ((status == AUX_REPLY_CODE_NACK) || + (status == AUX_REPLY_CODE_I2C_NACK)) { + /* The request was not acknowledged. */ + return -EIO; + } + + /* The request was acknowledged. */ + + if ((request->cmd_code == AUX_CMD_READ) || + (request->cmd_code == AUX_CMD_I2C_READ) || + (request->cmd_code == AUX_CMD_I2C_READ_MOT)) { + /* Wait until all data has been received. */ + timeout_count = 0; + do { + status = get_reg(dev, REG_REPLY_DATA_COUNT); + + udelay(100); + timeout_count++; + if (timeout_count >= AUX_MAX_TIMEOUT_COUNT) + return -ETIMEDOUT; + } while (status != request->num_bytes); + + /* Obtain the read data from the reply FIFO. */ + for (index = 0; index < request->num_bytes; index++) + request->data[index] = get_reg(dev, REG_AUX_REPLY_DATA); + } + + return 0; +} + +/* + * Submit the supplied AUX request to the RX device over the AUX + * channel. If waiting for a reply times out, or if the DisplayPort TX core + * indicates that the request was deferred, the request is sent again (up to a + * maximum specified by AUX_MAX_DEFER_COUNT|AUX_MAX_TIMEOUT_COUNT). + */ +static int aux_request(struct udevice *dev, struct aux_transaction *request) +{ + u32 defer_count = 0; + u32 timeout_count = 0; + + while ((defer_count < AUX_MAX_DEFER_COUNT) && + (timeout_count < AUX_MAX_TIMEOUT_COUNT)) { + int status = aux_wait_ready(dev); + + if (status) { + /* The RX device isn't ready yet. */ + timeout_count++; + continue; + } + + status = aux_request_send(dev, request); + if (status == -EAGAIN) { + /* The request was deferred. */ + defer_count++; + } else if (status == -ETIMEDOUT) { + /* Waiting for a reply timed out. */ + timeout_count++; + } else { + /* -EIO indicates that the request was NACK'ed, + * 0 indicates that the request was ACK'ed. + */ + return status; + } + + udelay(100); + } + + /* The request was not successfully received by the RX device. */ + return -ETIMEDOUT; +} + +/* + * Common sequence of submitting an AUX command for + * AUX read, AUX write, I2C-over-AUX read, and I2C-over-AUX write transactions. + * If required, the reads and writes are split into multiple requests, each + * acting on a maximum of 16 bytes. + */ +static int aux_common(struct udevice *dev, u32 cmd_type, u32 address, + u32 num_bytes, u8 *data) +{ + struct aux_transaction request; + u32 bytes_left; + + /* + * set the start address for AUX transactions. For I2C transactions, + * this is the address of the I2C bus. + */ + request.address = address; + + bytes_left = num_bytes; + while (bytes_left) { + int status; + + request.cmd_code = cmd_type; + + if ((cmd_type == AUX_CMD_READ) || + (cmd_type == AUX_CMD_WRITE)) { + /* Increment address for normal AUX transactions. */ + request.address = address + (num_bytes - bytes_left); + } + + /* Increment the pointer to the supplied data buffer. */ + request.data = &data[num_bytes - bytes_left]; + + request.num_bytes = (bytes_left > 16) ? 16 : bytes_left; + bytes_left -= request.num_bytes; + + if ((cmd_type == AUX_CMD_I2C_READ) && bytes_left) { + /* Middle of a transaction I2C read request. Override + * the command code that was set to cmd_type. + */ + request.cmd_code = AUX_CMD_I2C_READ_MOT; + } else if ((cmd_type == AUX_CMD_I2C_WRITE) && bytes_left) { + /* Middle of a transaction I2C write request. Override + * the command code that was set to cmd_type. + */ + request.cmd_code = AUX_CMD_I2C_WRITE_MOT; + } + + status = aux_request(dev, &request); + if (status) + return status; + } + + return 0; +} + +/* + * Issue a read request over the AUX channel that will read from + * the RX device's DisplayPort Configuration data (DPCD) address space. The read + * message will be divided into multiple transactions which read a maximum of 16 + * bytes each. + */ +static int aux_read(struct udevice *dev, u32 dpcd_address, u32 bytes_to_read, + void *read_data) +{ + int status; + + if (!is_connected(dev)) + return -ENODEV; + + /* Send AUX read transaction. */ + status = aux_common(dev, AUX_CMD_READ, dpcd_address, + bytes_to_read, (u8 *)read_data); + + return status; +} + +/* + * Issue a write request over the AUX channel that will write to + * the RX device's DisplayPort Configuration data (DPCD) address space. The + * write message will be divided into multiple transactions which write a + * maximum of 16 bytes each. + */ +static int aux_write(struct udevice *dev, u32 dpcd_address, u32 bytes_to_write, + void *write_data) +{ + int status; + + if (!is_connected(dev)) + return -ENODEV; + + /* Send AUX write transaction. */ + status = aux_common(dev, AUX_CMD_WRITE, dpcd_address, + bytes_to_write, (u8 *)write_data); + + return status; +} + +/* core initialization */ + +static int initialize(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + u32 val; + u32 phy_config; + unsigned int k; + + /* place the PHY (and GTTXRESET) into reset. */ + phy_config = get_reg(dev, REG_PHY_CONFIG); + set_reg(dev, REG_PHY_CONFIG, phy_config | PHY_CONFIG_GT_ALL_RESET_MASK); + + /* reset the video streams and AUX logic. */ + set_reg(dev, REG_SOFT_RESET, + SOFT_RESET_VIDEO_STREAM_ALL_MASK | + SOFT_RESET_AUX_MASK); + + /* disable the DisplayPort TX core. */ + set_reg(dev, REG_ENABLE, 0); + + /* set the clock divider. */ + val = get_reg(dev, REG_AUX_CLK_DIVIDER); + val &= ~AUX_CLK_DIVIDER_VAL_MASK; + val |= dp_tx->s_axi_clk / 1000000; + set_reg(dev, REG_AUX_CLK_DIVIDER, val); + + /* set the DisplayPort TX core's clock speed. */ + set_reg(dev, REG_PHY_CLOCK_SELECT, PHY_CLOCK_SELECT_DEFAULT); + + /* bring the PHY (and GTTXRESET) out of reset. */ + set_reg(dev, REG_PHY_CONFIG, + phy_config & ~PHY_CONFIG_GT_ALL_RESET_MASK); + + /* enable the DisplayPort TX core. */ + set_reg(dev, REG_ENABLE, 1); + + /* Unmask Hot-Plug-Detect (HPD) interrupts. */ + set_reg(dev, REG_INTERRUPT_MASK, + ~INTERRUPT_MASK_HPD_PULSE_DETECTED_MASK & + ~INTERRUPT_MASK_HPD_EVENT_MASK & + ~INTERRUPT_MASK_HPD_IRQ_MASK); + + for (k = 0; k < 4; k++) { + /* Disable pre-cursor levels. */ + set_reg(dev, REG_PHY_PRECURSOR_LANE_0 + 4 * k, 0); + + /* Write default voltage swing levels to the TX registers. */ + set_reg(dev, REG_PHY_VOLTAGE_DIFF_LANE_0 + 4 * k, 0); + + /* Write default pre-emphasis levels to the TX registers. */ + set_reg(dev, REG_PHY_POSTCURSOR_LANE_0 + 4 * k, 0); + } + + return 0; +} + +bool is_link_rate_valid(struct udevice *dev, u8 link_rate) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + bool valid = true; + + if ((link_rate != LINK_BW_SET_162GBPS) && + (link_rate != LINK_BW_SET_270GBPS) && + (link_rate != LINK_BW_SET_540GBPS)) + valid = false; + else if (link_rate > dp_tx->link_config.max_link_rate) + valid = false; + + return valid; +} + +bool is_lane_count_valid(struct udevice *dev, u8 lane_count) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + bool valid = true; + + if ((lane_count != LANE_COUNT_SET_1) && + (lane_count != LANE_COUNT_SET_2) && + (lane_count != LANE_COUNT_SET_4)) + valid = false; + else if (lane_count > dp_tx->link_config.max_lane_count) + valid = false; + + return valid; +} + +static int get_rx_capabilities(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + u8 rx_max_link_rate; + u8 rx_max_lane_count; + + if (!is_connected(dev)) + return -ENODEV; + + status = aux_read(dev, DPCD_RECEIVER_CAP_FIELD_START, 16, + dp_tx->dpcd_rx_caps); + if (status) + return -EIO; + + rx_max_link_rate = dp_tx->dpcd_rx_caps[DPCD_MAX_LINK_RATE]; + rx_max_lane_count = dp_tx->dpcd_rx_caps[DPCD_MAX_LANE_COUNT] & + DPCD_MAX_LANE_COUNT_MASK; + + dp_tx->link_config.max_link_rate = + (rx_max_link_rate > dp_tx->max_link_rate) ? + dp_tx->max_link_rate : rx_max_link_rate; + if (!is_link_rate_valid(dev, rx_max_link_rate)) + return -EINVAL; + + dp_tx->link_config.max_lane_count = + (rx_max_lane_count > dp_tx->max_lane_count) ? + dp_tx->max_lane_count : rx_max_lane_count; + if (!is_lane_count_valid(dev, rx_max_lane_count)) + return -EINVAL; + + dp_tx->link_config.support_enhanced_framing_mode = + dp_tx->dpcd_rx_caps[DPCD_MAX_LANE_COUNT] & + DPCD_ENHANCED_FRAME_SUPPORT_MASK; + dp_tx->link_config.support_downspread_control = + dp_tx->dpcd_rx_caps[DPCD_MAX_DOWNSPREAD] & + DPCD_MAX_DOWNSPREAD_MASK; + + return 0; +} + +static void enable_main_link(struct udevice *dev) +{ + /* reset the scrambler. */ + set_reg(dev, REG_FORCE_SCRAMBLER_RESET, 0x1); + + /* enable the main stream. */ + set_reg(dev, REG_ENABLE_MAIN_STREAM, 0x1); +} + +static void disable_main_link(struct udevice *dev) +{ + /* reset the scrambler. */ + set_reg(dev, REG_FORCE_SCRAMBLER_RESET, 0x1); + + /* Disable the main stream. */ + set_reg(dev, REG_ENABLE_MAIN_STREAM, 0x0); +} + +static void reset_dp_phy(struct udevice *dev, u32 reset) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + u32 val; + + set_reg(dev, REG_ENABLE, 0x0); + + val = get_reg(dev, REG_PHY_CONFIG); + + /* Apply reset. */ + set_reg(dev, REG_PHY_CONFIG, val | reset); + + /* Remove reset. */ + set_reg(dev, REG_PHY_CONFIG, val); + + /* Wait for the PHY to be ready. */ + wait_phy_ready(dev, PHY_STATUS_LANES_READY_MASK(dp_tx->max_lane_count)); + + set_reg(dev, REG_ENABLE, 0x1); +} + +/* + * enable or disable the enhanced framing symbol sequence for + * both the DisplayPort TX core and the RX device. + */ +static int set_enhanced_frame_mode(struct udevice *dev, u8 enable) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + u8 val; + + if (!is_connected(dev)) + return -ENODEV; + + if (dp_tx->link_config.support_enhanced_framing_mode) + dp_tx->link_config.enhanced_framing_mode = enable; + else + dp_tx->link_config.enhanced_framing_mode = false; + + /* Write enhanced frame mode enable to the DisplayPort TX core. */ + set_reg(dev, REG_ENHANCED_FRAME_EN, + dp_tx->link_config.enhanced_framing_mode); + + /* Write enhanced frame mode enable to the RX device. */ + status = aux_read(dev, DPCD_LANE_COUNT_SET, 0x1, &val); + if (status) + return -EIO; + + if (dp_tx->link_config.enhanced_framing_mode) + val |= DPCD_ENHANCED_FRAME_EN_MASK; + else + val &= ~DPCD_ENHANCED_FRAME_EN_MASK; + + status = aux_write(dev, DPCD_LANE_COUNT_SET, 0x1, &val); + if (status) + return -EIO; + + return 0; +} + +/* + * Set the number of lanes to be used by the main link for both + * the DisplayPort TX core and the RX device. + */ +static int set_lane_count(struct udevice *dev, u8 lane_count) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + u8 val; + + if (!is_connected(dev)) + return -ENODEV; + + printf(" set lane count to %u\n", lane_count); + + dp_tx->link_config.lane_count = lane_count; + + /* Write the new lane count to the DisplayPort TX core. */ + set_reg(dev, REG_LANE_COUNT_SET, dp_tx->link_config.lane_count); + + /* Write the new lane count to the RX device. */ + status = aux_read(dev, DPCD_LANE_COUNT_SET, 0x1, &val); + if (status) + return -EIO; + val &= ~DPCD_LANE_COUNT_SET_MASK; + val |= dp_tx->link_config.lane_count; + + status = aux_write(dev, DPCD_LANE_COUNT_SET, 0x1, &val); + if (status) + return -EIO; + + return 0; +} + +/* + * Set the clock frequency for the DisplayPort PHY corresponding + * to a desired data rate. + */ +static int set_clk_speed(struct udevice *dev, u32 speed) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + u32 val; + + /* Disable the DisplayPort TX core first. */ + val = get_reg(dev, REG_ENABLE); + set_reg(dev, REG_ENABLE, 0x0); + + /* Change speed of the feedback clock. */ + set_reg(dev, REG_PHY_CLOCK_SELECT, speed); + + /* Re-enable the DisplayPort TX core if it was previously enabled. */ + if (val) + set_reg(dev, REG_ENABLE, 0x1); + + /* Wait until the PHY is ready. */ + status = wait_phy_ready(dev, PHY_STATUS_LANES_READY_MASK(dp_tx->max_lane_count)); + if (status) + return -EIO; + + return 0; +} + +/* + * Set the data rate to be used by the main link for both the + * DisplayPort TX core and the RX device. + */ +static int set_link_rate(struct udevice *dev, u8 link_rate) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + + /* Write a corresponding clock frequency to the DisplayPort TX core. */ + switch (link_rate) { + case LINK_BW_SET_162GBPS: + printf(" set link rate to 1.62 Gb/s\n"); + status = set_clk_speed(dev, PHY_CLOCK_SELECT_162GBPS); + break; + case LINK_BW_SET_270GBPS: + printf(" set link rate to 2.70 Gb/s\n"); + status = set_clk_speed(dev, PHY_CLOCK_SELECT_270GBPS); + break; + case LINK_BW_SET_540GBPS: + printf(" set link rate to 5.40 Gb/s\n"); + status = set_clk_speed(dev, PHY_CLOCK_SELECT_540GBPS); + break; + default: + return -EINVAL; + } + if (status) + return -EIO; + + dp_tx->link_config.link_rate = link_rate; + + /* Write new link rate to the DisplayPort TX core. */ + set_reg(dev, REG_LINK_BW_SET, dp_tx->link_config.link_rate); + + /* Write new link rate to the RX device. */ + status = aux_write(dev, DPCD_LINK_BW_SET, 1, + &dp_tx->link_config.link_rate); + if (status) + return -EIO; + + return 0; +} + +/* link training */ + +/* + * Determine what the RX device's required training delay is for + * link training. + */ +static int get_training_delay(struct udevice *dev, int training_state) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + u16 delay; + + switch (dp_tx->dpcd_rx_caps[DPCD_TRAIN_AUX_RD_INTERVAL]) { + case DPCD_TRAIN_AUX_RD_INT_100_400US: + if (training_state == TS_CLOCK_RECOVERY) + /* delay for the clock recovery phase. */ + delay = 100; + else + /* delay for the channel equalization phase. */ + delay = 400; + break; + case DPCD_TRAIN_AUX_RD_INT_4MS: + delay = 4000; + break; + case DPCD_TRAIN_AUX_RD_INT_8MS: + delay = 8000; + break; + case DPCD_TRAIN_AUX_RD_INT_12MS: + delay = 12000; + break; + case DPCD_TRAIN_AUX_RD_INT_16MS: + delay = 16000; + break; + default: + /* Default to 20 ms. */ + delay = 20000; + break; + } + + return delay; +} + +/* + * Set current voltage swing and pre-emphasis level settings from + * the link_config structure to hardware. + */ +static void set_vswing_preemp(struct udevice *dev, u8 *aux_data) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + u8 data; + u8 vs_level_rx = dp_tx->link_config.vs_level; + u8 pe_level_rx = dp_tx->link_config.pe_level; + + /* Set up the data buffer for writing to the RX device. */ + data = (pe_level_rx << DPCD_TRAINING_LANEX_SET_PE_SHIFT) | vs_level_rx; + /* The maximum voltage swing has been reached. */ + if (vs_level_rx == MAXIMUM_VS_LEVEL) + data |= DPCD_TRAINING_LANEX_SET_MAX_VS_MASK; + + /* The maximum pre-emphasis level has been reached. */ + if (pe_level_rx == MAXIMUM_PE_LEVEL) + data |= DPCD_TRAINING_LANEX_SET_MAX_PE_MASK; + memset(aux_data, data, 4); +} + +/* + * Set new voltage swing and pre-emphasis levels using the + * adjustment requests obtained from the RX device. + */ +static int adj_vswing_preemp(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + u8 index; + u8 vs_level_adj_req[4]; + u8 pe_level_adj_req[4]; + u8 aux_data[4]; + u8 *ajd_reqs = &dp_tx->lane_status_ajd_reqs[4]; + + /* Analyze the adjustment requests for changes in voltage swing and + * pre-emphasis levels. + */ + vs_level_adj_req[0] = ajd_reqs[0] & DPCD_ADJ_REQ_LANE_0_2_VS_MASK; + vs_level_adj_req[1] = (ajd_reqs[0] & DPCD_ADJ_REQ_LANE_1_3_VS_MASK) >> + DPCD_ADJ_REQ_LANE_1_3_VS_SHIFT; + vs_level_adj_req[2] = ajd_reqs[1] & DPCD_ADJ_REQ_LANE_0_2_VS_MASK; + vs_level_adj_req[3] = (ajd_reqs[1] & DPCD_ADJ_REQ_LANE_1_3_VS_MASK) >> + DPCD_ADJ_REQ_LANE_1_3_VS_SHIFT; + pe_level_adj_req[0] = (ajd_reqs[0] & DPCD_ADJ_REQ_LANE_0_2_PE_MASK) >> + DPCD_ADJ_REQ_LANE_0_2_PE_SHIFT; + pe_level_adj_req[1] = (ajd_reqs[0] & DPCD_ADJ_REQ_LANE_1_3_PE_MASK) >> + DPCD_ADJ_REQ_LANE_1_3_PE_SHIFT; + pe_level_adj_req[2] = (ajd_reqs[1] & DPCD_ADJ_REQ_LANE_0_2_PE_MASK) >> + DPCD_ADJ_REQ_LANE_0_2_PE_SHIFT; + pe_level_adj_req[3] = (ajd_reqs[1] & DPCD_ADJ_REQ_LANE_1_3_PE_MASK) >> + DPCD_ADJ_REQ_LANE_1_3_PE_SHIFT; + + /* Change the drive settings to match the adjustment requests. Use the + * greatest level requested. + */ + dp_tx->link_config.vs_level = 0; + dp_tx->link_config.pe_level = 0; + for (index = 0; index < dp_tx->link_config.lane_count; index++) { + if (vs_level_adj_req[index] > dp_tx->link_config.vs_level) + dp_tx->link_config.vs_level = vs_level_adj_req[index]; + if (pe_level_adj_req[index] > dp_tx->link_config.pe_level) + dp_tx->link_config.pe_level = pe_level_adj_req[index]; + } + + /* Verify that the voltage swing and pre-emphasis combination is + * allowed. Some combinations will result in a differential peak-to-peak + * voltage that is outside the permissible range. See the VESA + * DisplayPort v1.2 Specification, section 3.1.5.2. + * The valid combinations are: + * PE=0 PE=1 PE=2 PE=3 + * VS=0 valid valid valid valid + * VS=1 valid valid valid + * VS=2 valid valid + * VS=3 valid + * + * NOTE: + * Xilinix dp_v3_1 driver seems to have an off by one error when + * limiting pe_level which is fixed here. + */ + if (dp_tx->link_config.pe_level > (3 - dp_tx->link_config.vs_level)) + dp_tx->link_config.pe_level = 3 - dp_tx->link_config.vs_level; + + /* Make the adjustments to both the DisplayPort TX core and the RX + * device. + */ + set_vswing_preemp(dev, aux_data); + /* Write the voltage swing and pre-emphasis levels for each lane to the + * RX device. + */ + status = aux_write(dev, DPCD_TRAINING_LANE0_SET, 4, aux_data); + if (status) + return -EIO; + + return 0; +} + +/* + * Do a burst AUX read from the RX device over the AUX + * channel. The contents of the status registers will be stored for later use by + * check_clock_recovery, check_channel_equalization, and + * adj_vswing_preemp. + */ +static int get_lane_status_adj_reqs(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + + /* Read and store 4 bytes of lane status and 2 bytes of adjustment + * requests. + */ + status = aux_read(dev, DPCD_STATUS_LANE_0_1, 6, + dp_tx->lane_status_ajd_reqs); + if (status) + return -EIO; + + return 0; +} + +/* + * Check if the RX device's DisplayPort Configuration data (DPCD) + * indicates that the clock recovery sequence during link training was + * successful - the RX device's link clock and data recovery unit has realized + * and maintained the frequency lock for all lanes currently in use. + */ +static int check_clock_recovery(struct udevice *dev, u8 lane_count) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + u8 *lane_status = dp_tx->lane_status_ajd_reqs; + + /* Check that all LANEx_CR_DONE bits are set. */ + switch (lane_count) { + case LANE_COUNT_SET_4: + if (!(lane_status[1] & DPCD_STATUS_LANE_3_CR_DONE_MASK)) + goto out_fail; + if (!(lane_status[1] & DPCD_STATUS_LANE_2_CR_DONE_MASK)) + goto out_fail; + /* Drop through and check lane 1. */ + case LANE_COUNT_SET_2: + if (!(lane_status[0] & DPCD_STATUS_LANE_1_CR_DONE_MASK)) + goto out_fail; + /* Drop through and check lane 0. */ + case LANE_COUNT_SET_1: + if (!(lane_status[0] & DPCD_STATUS_LANE_0_CR_DONE_MASK)) + goto out_fail; + default: + /* All (lane_count) lanes have achieved clock recovery. */ + break; + } + + return 0; + +out_fail: + return -EIO; +} + +/* + * Check if the RX device's DisplayPort Configuration data (DPCD) + * indicates that the channel equalization sequence during link training was + * successful - the RX device has achieved channel equalization, symbol lock, + * and interlane alignment for all lanes currently in use. + */ +static int check_channel_equalization(struct udevice *dev, u8 lane_count) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + u8 *lane_status = dp_tx->lane_status_ajd_reqs; + + /* Check that all LANEx_CHANNEL_EQ_DONE bits are set. */ + switch (lane_count) { + case LANE_COUNT_SET_4: + if (!(lane_status[1] & DPCD_STATUS_LANE_3_CE_DONE_MASK)) + goto out_fail; + if (!(lane_status[1] & DPCD_STATUS_LANE_2_CE_DONE_MASK)) + goto out_fail; + /* Drop through and check lane 1. */ + case LANE_COUNT_SET_2: + if (!(lane_status[0] & DPCD_STATUS_LANE_1_CE_DONE_MASK)) + goto out_fail; + /* Drop through and check lane 0. */ + case LANE_COUNT_SET_1: + if (!(lane_status[0] & DPCD_STATUS_LANE_0_CE_DONE_MASK)) + goto out_fail; + default: + /* All (lane_count) lanes have achieved channel equalization. */ + break; + } + + /* Check that all LANEx_SYMBOL_LOCKED bits are set. */ + switch (lane_count) { + case LANE_COUNT_SET_4: + if (!(lane_status[1] & DPCD_STATUS_LANE_3_SL_DONE_MASK)) + goto out_fail; + if (!(lane_status[1] & DPCD_STATUS_LANE_2_SL_DONE_MASK)) + goto out_fail; + /* Drop through and check lane 1. */ + case LANE_COUNT_SET_2: + if (!(lane_status[0] & DPCD_STATUS_LANE_1_SL_DONE_MASK)) + goto out_fail; + /* Drop through and check lane 0. */ + case LANE_COUNT_SET_1: + if (!(lane_status[0] & DPCD_STATUS_LANE_0_SL_DONE_MASK)) + goto out_fail; + default: + /* All (lane_count) lanes have achieved symbol lock. */ + break; + } + + /* Check that interlane alignment is done. */ + if (!(lane_status[2] & DPCD_LANE_ALIGN_STATUS_UPDATED_IA_DONE_MASK)) + goto out_fail; + + return 0; + +out_fail: + return -EIO; +} + +/* + * Set the training pattern to be used during link training for + * both the DisplayPort TX core and the RX device. + */ +static int set_training_pattern(struct udevice *dev, u32 pattern) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + u8 aux_data[5]; + + /* Write to the DisplayPort TX core. */ + set_reg(dev, REG_TRAINING_PATTERN_SET, pattern); + + aux_data[0] = pattern; + + /* Write scrambler disable to the DisplayPort TX core. */ + switch (pattern) { + case TRAINING_PATTERN_SET_OFF: + set_reg(dev, REG_SCRAMBLING_DISABLE, 0); + dp_tx->link_config.scrambler_en = 1; + break; + case TRAINING_PATTERN_SET_TP1: + case TRAINING_PATTERN_SET_TP2: + case TRAINING_PATTERN_SET_TP3: + aux_data[0] |= DPCD_TP_SET_SCRAMB_DIS_MASK; + set_reg(dev, REG_SCRAMBLING_DISABLE, 1); + dp_tx->link_config.scrambler_en = 0; + break; + default: + break; + } + + /* Make the adjustments to both the DisplayPort TX core and the RX + * device. + */ + set_vswing_preemp(dev, &aux_data[1]); + /* Write the voltage swing and pre-emphasis levels for each lane to the + * RX device. + */ + if (pattern == TRAINING_PATTERN_SET_OFF) + status = aux_write(dev, DPCD_TP_SET, 1, aux_data); + else + status = aux_write(dev, DPCD_TP_SET, 5, aux_data); + if (status) + return -EIO; + + return 0; +} + +/* + * Run the clock recovery sequence as part of link training. The + * sequence is as follows: + * 0) Start signaling at the minimum voltage swing, pre-emphasis, and post- + * cursor levels. + * 1) Transmit training pattern 1 over the main link with symbol scrambling + * disabled. + * 2) The clock recovery loop. If clock recovery is unsuccessful after + * MaxIterations loop iterations, return. + * 2a) Wait for at least the period of time specified in the RX device's + * DisplayPort Configuration data (DPCD) register, + * TRAINING_AUX_RD_INTERVAL. + * 2b) Check if all lanes have achieved clock recovery lock. If so, return. + * 2c) Check if the same voltage swing level has been used 5 consecutive + * times or if the maximum level has been reached. If so, return. + * 2d) Adjust the voltage swing, pre-emphasis, and post-cursor levels as + * requested by the RX device. + * 2e) Loop back to 2a. + * For a more detailed description of the clock recovery sequence, see section + * 3.5.1.2.1 of the DisplayPort 1.2a specification document. + */ +static unsigned int training_state_clock_recovery(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + u32 delay_us; + u8 prev_vs_level = 0; + u8 same_vs_level_count = 0; + + /* Obtain the required delay for clock recovery as specified by the + * RX device. + */ + delay_us = get_training_delay(dev, TS_CLOCK_RECOVERY); + + /* Start CRLock. */ + + /* Transmit training pattern 1. */ + /* Disable the scrambler. */ + /* Start from minimal voltage swing and pre-emphasis levels. */ + dp_tx->link_config.vs_level = 0; + dp_tx->link_config.pe_level = 0; + status = set_training_pattern(dev, TRAINING_PATTERN_SET_TP1); + if (status) + return TS_FAILURE; + + while (1) { + /* Wait delay specified in TRAINING_AUX_RD_INTERVAL. */ + udelay(delay_us); + + /* Get lane and adjustment requests. */ + status = get_lane_status_adj_reqs(dev); + if (status) + return TS_FAILURE; + + /* Check if all lanes have realized and maintained the frequency + * lock and get adjustment requests. + */ + status = check_clock_recovery(dev, + dp_tx->link_config.lane_count); + if (!status) + return TS_CHANNEL_EQUALIZATION; + + /* Check if the same voltage swing for each lane has been used 5 + * consecutive times. + */ + if (prev_vs_level == dp_tx->link_config.vs_level) { + same_vs_level_count++; + } else { + same_vs_level_count = 0; + prev_vs_level = dp_tx->link_config.vs_level; + } + if (same_vs_level_count >= 5) + break; + + /* Only try maximum voltage swing once. */ + if (dp_tx->link_config.vs_level == MAXIMUM_VS_LEVEL) + break; + + /* Adjust the drive settings as requested by the RX device. */ + status = adj_vswing_preemp(dev); + if (status) + /* The AUX write failed. */ + return TS_FAILURE; + } + + return TS_ADJUST_LINK_RATE; +} + +/* + * Run the channel equalization sequence as part of link + * training. The sequence is as follows: + * 0) Start signaling with the same drive settings used at the end of the + * clock recovery sequence. + * 1) Transmit training pattern 2 (or 3) over the main link with symbol + * scrambling disabled. + * 2) The channel equalization loop. If channel equalization is + * unsuccessful after 5 loop iterations, return. + * 2a) Wait for at least the period of time specified in the RX device's + * DisplayPort Configuration data (DPCD) register, + * TRAINING_AUX_RD_INTERVAL. + * 2b) Check if all lanes have achieved channel equalization, symbol lock, + * and interlane alignment. If so, return. + * 2c) Check if the same voltage swing level has been used 5 consecutive + * times or if the maximum level has been reached. If so, return. + * 2d) Adjust the voltage swing, pre-emphasis, and post-cursor levels as + * requested by the RX device. + * 2e) Loop back to 2a. + * For a more detailed description of the channel equalization sequence, see + * section 3.5.1.2.2 of the DisplayPort 1.2a specification document. + */ +static int training_state_channel_equalization(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + u32 delay_us; + u32 iteration_count = 0; + + /* Obtain the required delay for channel equalization as specified by + * the RX device. + */ + delay_us = get_training_delay(dev, TS_CHANNEL_EQUALIZATION); + + /* Start channel equalization. */ + + /* Write the current drive settings. */ + /* Transmit training pattern 2/3. */ + if (dp_tx->dpcd_rx_caps[DPCD_MAX_LANE_COUNT] & DPCD_TPS3_SUPPORT_MASK) + status = set_training_pattern(dev, TRAINING_PATTERN_SET_TP3); + else + status = set_training_pattern(dev, TRAINING_PATTERN_SET_TP2); + + if (status) + return TS_FAILURE; + + while (iteration_count < 5) { + /* Wait delay specified in TRAINING_AUX_RD_INTERVAL. */ + udelay(delay_us); + + /* Get lane and adjustment requests. */ + status = get_lane_status_adj_reqs(dev); + if (status) + /* The AUX read failed. */ + return TS_FAILURE; + + /* Check that all lanes still have their clocks locked. */ + status = check_clock_recovery(dev, + dp_tx->link_config.lane_count); + if (status) + break; + + /* Check if all lanes have accomplished channel equalization, + * symbol lock, and interlane alignment. + */ + status = + check_channel_equalization(dev, + dp_tx->link_config.lane_count); + if (!status) + return TS_SUCCESS; + + /* Adjust the drive settings as requested by the RX device. */ + status = adj_vswing_preemp(dev); + if (status) + /* The AUX write failed. */ + return TS_FAILURE; + + iteration_count++; + } + + /* Tried 5 times with no success. Try a reduced bitrate first, then + * reduce the number of lanes. + */ + return TS_ADJUST_LINK_RATE; +} + +/* + * This function is reached if either the clock recovery or the channel + * equalization process failed during training. As a result, the data rate will + * be downshifted, and training will be re-attempted (starting with clock + * recovery) at the reduced data rate. If the data rate is already at 1.62 Gbps, + * a downshift in lane count will be attempted. + */ +static int training_state_adjust_link_rate(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + + switch (dp_tx->link_config.link_rate) { + case LINK_BW_SET_540GBPS: + status = set_link_rate(dev, LINK_BW_SET_270GBPS); + if (status) { + status = TS_FAILURE; + break; + } + status = TS_CLOCK_RECOVERY; + break; + case LINK_BW_SET_270GBPS: + status = set_link_rate(dev, LINK_BW_SET_162GBPS); + if (status) { + status = TS_FAILURE; + break; + } + status = TS_CLOCK_RECOVERY; + break; + default: + /* Already at the lowest link rate. Try reducing the lane + * count next. + */ + status = TS_ADJUST_LANE_COUNT; + break; + } + + return status; +} + +/* + * This function is reached if either the clock recovery or the channel + * equalization process failed during training, and a minimal data rate of 1.62 + * Gbps was being used. As a result, the number of lanes in use will be reduced, + * and training will be re-attempted (starting with clock recovery) at this + * lower lane count. + */ +static int trainig_state_adjust_lane_count(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + + switch (dp_tx->link_config.lane_count) { + case LANE_COUNT_SET_4: + status = set_lane_count(dev, LANE_COUNT_SET_2); + if (status) { + status = TS_FAILURE; + break; + } + + status = set_link_rate(dev, dp_tx->link_config.max_link_rate); + if (status) { + status = TS_FAILURE; + break; + } + status = TS_CLOCK_RECOVERY; + break; + case LANE_COUNT_SET_2: + status = set_lane_count(dev, LANE_COUNT_SET_1); + if (status) { + status = TS_FAILURE; + break; + } + + status = set_link_rate(dev, dp_tx->link_config.max_link_rate); + if (status) { + status = TS_FAILURE; + break; + } + status = TS_CLOCK_RECOVERY; + break; + default: + /* Already at the lowest lane count. Training has failed at the + * lowest lane count and link rate. + */ + status = TS_FAILURE; + break; + } + + return status; +} + +/* + * Check if the receiver's DisplayPort Configuration data (DPCD) + * indicates the receiver has achieved and maintained clock recovery, channel + * equalization, symbol lock, and interlane alignment for all lanes currently in + * use. + */ +static int check_link_status(struct udevice *dev, u8 lane_count) +{ + u8 retry_count = 0; + + if (!is_connected(dev)) + return -ENODEV; + + /* Retrieve AUX info. */ + do { + int status; + + /* Get lane and adjustment requests. */ + status = get_lane_status_adj_reqs(dev); + if (status) + return -EIO; + + /* Check if the link needs training. */ + if ((check_clock_recovery(dev, lane_count) == 0) && + (check_channel_equalization(dev, lane_count) == 0)) + return 0; + + retry_count++; + } while (retry_count < 5); /* Retry up to 5 times. */ + + return -EIO; +} + +/* + * Run the link training process. It is implemented as a state + * machine, with each state returning the next state. First, the clock recovery + * sequence will be run; if successful, the channel equalization sequence will + * run. If either the clock recovery or channel equalization sequence failed, + * the link rate or the number of lanes used will be reduced and training will + * be re-attempted. If training fails at the minimal data rate, 1.62 Gbps with + * a single lane, training will no longer re-attempt and fail. + * + * ### Here be dragons ### + * There are undocumented timeout constraints in the link training process. + * In DP v1.2a spec, Chapter 3.5.1.2.2 a 10ms limit for the complete training + * process is mentioned. Which individual timeouts are derived and implemented + * by sink manufacturers is unknown. So each step should be as short as possible + * and link training should start as soon as possible after HPD. + */ +static int run_training(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + int training_state = TS_CLOCK_RECOVERY; + + while (1) { + switch (training_state) { + case TS_CLOCK_RECOVERY: + training_state = + training_state_clock_recovery(dev); + break; + case TS_CHANNEL_EQUALIZATION: + training_state = + training_state_channel_equalization(dev); + break; + case TS_ADJUST_LINK_RATE: + training_state = + training_state_adjust_link_rate(dev); + break; + case TS_ADJUST_LANE_COUNT: + training_state = + trainig_state_adjust_lane_count(dev); + break; + default: + break; + } + + if (training_state == TS_SUCCESS) + break; + else if (training_state == TS_FAILURE) + return -EIO; + + if ((training_state == TS_ADJUST_LINK_RATE) || + (training_state == TS_ADJUST_LANE_COUNT)) { + if (!dp_tx->train_adaptive) + return -EIO; + + status = set_training_pattern(dev, + TRAINING_PATTERN_SET_OFF); + if (status) + return -EIO; + } + } + + /* Final status check. */ + status = check_link_status(dev, dp_tx->link_config.lane_count); + if (status) + return -EIO; + + return 0; +} + +/* link policy maker */ + +/** + * Determine the common capabilities between the DisplayPort TX + * core and the RX device. + */ +static int cfg_main_link_max(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + + if (!is_connected(dev)) + return -ENODEV; + + /* Configure the main link to the maximum common link rate between the + * DisplayPort TX core and the RX device. + */ + status = set_link_rate(dev, dp_tx->link_config.max_link_rate); + if (status) + return status; + + /* Configure the main link to the maximum common lane count between the + * DisplayPort TX core and the RX device. + */ + status = set_lane_count(dev, dp_tx->link_config.max_lane_count); + if (status) + return status; + + return 0; +} + +/* + * Check if the link needs training and runs the training + * sequence if training is required. + */ +static int establish_link(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int status; + int status2; + + reset_dp_phy(dev, PHY_CONFIG_PHY_RESET_MASK); + + /* Disable main link during training. */ + disable_main_link(dev); + + /* Wait for the PHY to be ready. */ + status = wait_phy_ready(dev, PHY_STATUS_LANES_READY_MASK(dp_tx->max_lane_count)); + if (status) + return -EIO; + + /* Train main link. */ + status = run_training(dev); + + /* Turn off the training pattern and enable scrambler. */ + status2 = set_training_pattern(dev, TRAINING_PATTERN_SET_OFF); + if (status || status2) + return -EIO; + + return 0; +} + +/* + * Stream policy maker + */ + +/* + * Calculate the following Main Stream Attributes (MSA): + * - Transfer unit size + * - User pixel width + * - Horizontal total clock + * - Vertical total clock + * - misc_0 + * - misc_1 + * - Data per lane + * - Average number of bytes per transfer unit + * - Number of initial wait cycles + * These values are derived from: + * - Bits per color + * - Horizontal resolution + * - Vertical resolution + * - Horizontal blank start + * - Vertical blank start + * - Pixel clock (in KHz) + * - Horizontal sync polarity + * - Vertical sync polarity + * - Horizontal sync pulse width + * - Vertical sync pulse width + */ +static void cfg_msa_recalculate(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + u32 video_bw; + u32 link_bw; + u32 words_per_line; + u8 bits_per_pixel; + struct main_stream_attributes *msa_config; + struct link_config *link_config; + + msa_config = &dp_tx->main_stream_attributes; + link_config = &dp_tx->link_config; + + /* Set the user pixel width to handle clocks that exceed the + * capabilities of the DisplayPort TX core. + */ + if (msa_config->override_user_pixel_width == 0) { + if ((msa_config->pixel_clock_hz > 300000000) && + (link_config->lane_count == LANE_COUNT_SET_4)) { + msa_config->user_pixel_width = 4; + } /* Xilinx driver used 75 MHz as a limit here, 150 MHZ should + * be more sane + */ + else if ((msa_config->pixel_clock_hz > 150000000) && + (link_config->lane_count != LANE_COUNT_SET_1)) { + msa_config->user_pixel_width = 2; + } else { + msa_config->user_pixel_width = 1; + } + } + + /* Compute the rest of the MSA values. */ + msa_config->n_vid = 27 * 1000 * link_config->link_rate; + + /* Miscellaneous attributes. */ + if (msa_config->bits_per_color == 6) + msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_6BPC; + else if (msa_config->bits_per_color == 8) + msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_8BPC; + else if (msa_config->bits_per_color == 10) + msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_10BPC; + else if (msa_config->bits_per_color == 12) + msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_12BPC; + else if (msa_config->bits_per_color == 16) + msa_config->misc_0 = MAIN_STREAMX_MISC0_BDC_16BPC; + + msa_config->misc_0 = (msa_config->misc_0 << + MAIN_STREAMX_MISC0_BDC_SHIFT) | + (msa_config->y_cb_cr_colorimetry << + MAIN_STREAMX_MISC0_YCBCR_COLORIMETRY_SHIFT) | + (msa_config->dynamic_range << + MAIN_STREAMX_MISC0_DYNAMIC_RANGE_SHIFT) | + (msa_config->component_format << + MAIN_STREAMX_MISC0_COMPONENT_FORMAT_SHIFT) | + (msa_config->synchronous_clock_mode); + + msa_config->misc_1 = 0; + + /* Determine the number of bits per pixel for the specified color + * component format. + */ + if (msa_config->component_format == + MAIN_STREAMX_MISC0_COMPONENT_FORMAT_YCBCR422) + /* YCbCr422 color component format. */ + bits_per_pixel = msa_config->bits_per_color * 2; + else + /* RGB or YCbCr 4:4:4 color component format. */ + bits_per_pixel = msa_config->bits_per_color * 3; + + /* Calculate the data per lane. */ + words_per_line = (msa_config->h_active * bits_per_pixel); + if (words_per_line % 16) + words_per_line += 16; + words_per_line /= 16; + + msa_config->data_per_lane = words_per_line - link_config->lane_count; + if (words_per_line % link_config->lane_count) + msa_config->data_per_lane += (words_per_line % + link_config->lane_count); + + /* Allocate a fixed size for single-stream transport (SST) + * operation. + */ + msa_config->transfer_unit_size = 64; + + /* Calculate the average number of bytes per transfer unit. + * Note: Both the integer and the fractional part is stored in + * avg_bytes_per_tu. + */ + video_bw = ((msa_config->pixel_clock_hz / 1000) * bits_per_pixel) / 8; + link_bw = (link_config->lane_count * link_config->link_rate * 27); + msa_config->avg_bytes_per_tu = (video_bw * + msa_config->transfer_unit_size) / + link_bw; + + /* The number of initial wait cycles at the start of a new line + * by the framing logic. This allows enough data to be buffered + * in the input FIFO before video is sent. + */ + if ((msa_config->avg_bytes_per_tu / 1000) <= 4) + msa_config->init_wait = 64; + else + msa_config->init_wait = msa_config->transfer_unit_size - + (msa_config->avg_bytes_per_tu / 1000); +} + +/* + * Disable/enable the end of line reset to the internal video pipe in case of + * reduced blanking as required. + */ +static void set_line_reset(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + u32 reg_val; + u16 h_blank; + u16 h_reduced_blank; + struct main_stream_attributes *msa_config = + &dp_tx->main_stream_attributes; + + h_blank = msa_config->h_total - msa_config->h_active; + /* Reduced blanking starts at ceil(0.2 * HTotal). */ + h_reduced_blank = 2 * msa_config->h_total; + if (h_reduced_blank % 10) + h_reduced_blank += 10; + h_reduced_blank /= 10; + + /* CVT spec. states h_blank is either 80 or 160 for reduced blanking. */ + reg_val = get_reg(dev, REG_LINE_RESET_DISABLE); + if ((h_blank < h_reduced_blank) && + ((h_blank == 80) || (h_blank == 160))) { + reg_val |= LINE_RESET_DISABLE_MASK; + } else { + reg_val &= ~LINE_RESET_DISABLE_MASK; + } + set_reg(dev, REG_LINE_RESET_DISABLE, reg_val); +} + +/* + * Clear the main stream attributes registers of the DisplayPort + * TX core. + */ +static void clear_msa_values(struct udevice *dev) +{ + set_reg(dev, REG_MAIN_STREAM_HTOTAL, 0); + set_reg(dev, REG_MAIN_STREAM_VTOTAL, 0); + set_reg(dev, REG_MAIN_STREAM_POLARITY, 0); + set_reg(dev, REG_MAIN_STREAM_HSWIDTH, 0); + set_reg(dev, REG_MAIN_STREAM_VSWIDTH, 0); + set_reg(dev, REG_MAIN_STREAM_HRES, 0); + set_reg(dev, REG_MAIN_STREAM_VRES, 0); + set_reg(dev, REG_MAIN_STREAM_HSTART, 0); + set_reg(dev, REG_MAIN_STREAM_VSTART, 0); + set_reg(dev, REG_MAIN_STREAM_MISC0, 0); + set_reg(dev, REG_MAIN_STREAM_MISC1, 0); + set_reg(dev, REG_USER_PIXEL_WIDTH, 0); + set_reg(dev, REG_USER_DATA_COUNT_PER_LANE, 0); + set_reg(dev, REG_M_VID, 0); + set_reg(dev, REG_N_VID, 0); + + set_reg(dev, REG_STREAM1, 0); + set_reg(dev, REG_TU_SIZE, 0); + set_reg(dev, REG_MIN_BYTES_PER_TU, 0); + set_reg(dev, REG_FRAC_BYTES_PER_TU, 0); + set_reg(dev, REG_INIT_WAIT, 0); +} + +/* + * Set the main stream attributes registers of the DisplayPort TX + * core with the values specified in the main stream attributes configuration + * structure. + */ +static void set_msa_values(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + struct main_stream_attributes *msa_config = + &dp_tx->main_stream_attributes; + + printf(" set MSA %u x %u\n", msa_config->h_active, + msa_config->v_active); + + set_reg(dev, REG_MAIN_STREAM_HTOTAL, msa_config->h_total); + set_reg(dev, REG_MAIN_STREAM_VTOTAL, msa_config->v_total); + set_reg(dev, REG_MAIN_STREAM_POLARITY, + msa_config->h_sync_polarity | + (msa_config->v_sync_polarity << + MAIN_STREAMX_POLARITY_VSYNC_POL_SHIFT)); + set_reg(dev, REG_MAIN_STREAM_HSWIDTH, msa_config->h_sync_width); + set_reg(dev, REG_MAIN_STREAM_VSWIDTH, msa_config->v_sync_width); + set_reg(dev, REG_MAIN_STREAM_HRES, msa_config->h_active); + set_reg(dev, REG_MAIN_STREAM_VRES, msa_config->v_active); + set_reg(dev, REG_MAIN_STREAM_HSTART, msa_config->h_start); + set_reg(dev, REG_MAIN_STREAM_VSTART, msa_config->v_start); + set_reg(dev, REG_MAIN_STREAM_MISC0, msa_config->misc_0); + set_reg(dev, REG_MAIN_STREAM_MISC1, msa_config->misc_1); + set_reg(dev, REG_USER_PIXEL_WIDTH, msa_config->user_pixel_width); + + set_reg(dev, REG_M_VID, msa_config->pixel_clock_hz / 1000); + set_reg(dev, REG_N_VID, msa_config->n_vid); + set_reg(dev, REG_USER_DATA_COUNT_PER_LANE, msa_config->data_per_lane); + + set_line_reset(dev); + + set_reg(dev, REG_TU_SIZE, msa_config->transfer_unit_size); + set_reg(dev, REG_MIN_BYTES_PER_TU, msa_config->avg_bytes_per_tu / 1000); + set_reg(dev, REG_FRAC_BYTES_PER_TU, + (msa_config->avg_bytes_per_tu % 1000) * 1024 / 1000); + set_reg(dev, REG_INIT_WAIT, msa_config->init_wait); +} + +static void logicore_dp_tx_set_msa(struct udevice *dev, + struct logicore_dp_tx_msa *msa) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + + memset(&dp_tx->main_stream_attributes, 0, + sizeof(struct main_stream_attributes)); + + dp_tx->main_stream_attributes.pixel_clock_hz = msa->pixel_clock_hz; + dp_tx->main_stream_attributes.bits_per_color = msa->bits_per_color; + dp_tx->main_stream_attributes.h_active = msa->h_active; + dp_tx->main_stream_attributes.h_start = msa->h_start; + dp_tx->main_stream_attributes.h_sync_polarity = msa->h_sync_polarity; + dp_tx->main_stream_attributes.h_sync_width = msa->h_sync_width; + dp_tx->main_stream_attributes.h_total = msa->h_total; + dp_tx->main_stream_attributes.v_active = msa->v_active; + dp_tx->main_stream_attributes.v_start = msa->v_start; + dp_tx->main_stream_attributes.v_sync_polarity = msa->v_sync_polarity; + dp_tx->main_stream_attributes.v_sync_width = msa->v_sync_width; + dp_tx->main_stream_attributes.v_total = msa->v_total; + dp_tx->main_stream_attributes.override_user_pixel_width = + msa->override_user_pixel_width; + dp_tx->main_stream_attributes.user_pixel_width = msa->user_pixel_width; + dp_tx->main_stream_attributes.synchronous_clock_mode = 0; +} + +/* + * external API + */ + +int logicore_dp_tx_video_enable(struct udevice *dev, + struct logicore_dp_tx_msa *msa) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + int res; + u8 power = 0x01; + + if (!is_connected(dev)) { + printf(" no DP sink connected\n"); + return -EIO; + } + + initialize(dev); + + disable_main_link(dev); + + logicore_dp_tx_set_msa(dev, msa); + + get_rx_capabilities(dev); + + printf(" DP sink connected\n"); + aux_write(dev, DPCD_SET_POWER_DP_PWR_VOLTAGE, 1, &power); + set_enhanced_frame_mode(dev, true); + cfg_main_link_max(dev); + res = establish_link(dev); + printf(" establish_link: %s, vs: %d, pe: %d\n", + res ? "failed" : "ok", dp_tx->link_config.vs_level, + dp_tx->link_config.pe_level); + + cfg_msa_recalculate(dev); + + clear_msa_values(dev); + set_msa_values(dev); + + enable_main_link(dev); + + return 0; +} + +int logicore_dp_tx_enable(struct udevice *dev, int panel_bpp, + const struct display_timing *timing) +{ + struct clk pixclock; + struct logicore_dp_tx_msa *msa; + struct logicore_dp_tx_msa mode_640_480_60 = { + .pixel_clock_hz = 25175000, + .bits_per_color = 8, + .h_active = 640, + .h_start = 144, + .h_sync_polarity = false, + .h_sync_width = 96, + .h_total = 800, + .v_active = 480, + .v_start = 35, + .v_sync_polarity = false, + .v_sync_width = 2, + .v_total = 525, + .override_user_pixel_width = false, + .user_pixel_width = 0, + }; + + struct logicore_dp_tx_msa mode_720_400_70 = { + .pixel_clock_hz = 28300000, + .bits_per_color = 8, + .h_active = 720, + .h_start = 162, + .h_sync_polarity = false, + .h_sync_width = 108, + .h_total = 900, + .v_active = 400, + .v_start = 37, + .v_sync_polarity = true, + .v_sync_width = 2, + .v_total = 449, + .override_user_pixel_width = false, + .user_pixel_width = 0, + }; + + struct logicore_dp_tx_msa mode_1024_768_60 = { + .pixel_clock_hz = 65000000, + .bits_per_color = 8, + .h_active = 1024, + .h_start = 296, + .h_sync_polarity = false, + .h_sync_width = 136, + .h_total = 1344, + .v_active = 768, + .v_start = 35, + .v_sync_polarity = false, + .v_sync_width = 2, + .v_total = 806, + .override_user_pixel_width = false, + .user_pixel_width = 0, + }; + + if (timing->hactive.typ == 1024 && timing->vactive.typ == 768) + msa = &mode_1024_768_60; + else if (timing->hactive.typ == 720 && timing->vactive.typ == 400) + msa = &mode_720_400_70; + else + msa = &mode_640_480_60; + + if (clk_get_by_index(dev, 0, &pixclock)) { + printf("%s: Could not get pixelclock\n", dev->name); + return -1; + } + clk_set_rate(&pixclock, msa->pixel_clock_hz); + + return logicore_dp_tx_video_enable(dev, msa); +} + +int logicore_dp_tx_probe(struct udevice *dev) +{ + struct dp_tx *dp_tx = dev_get_priv(dev); + + dp_tx->s_axi_clk = S_AXI_CLK_DEFAULT; + dp_tx->train_adaptive = false; + dp_tx->max_link_rate = DPCD_MAX_LINK_RATE_540GBPS; + dp_tx->max_lane_count = DPCD_MAX_LANE_COUNT_4; + + dp_tx->base = dev_read_u32_default(dev, "reg", -1); + + return 0; +} + +struct dm_display_ops logicore_dp_tx_ops = { + .enable = logicore_dp_tx_enable, +}; + +static const struct udevice_id logicore_dp_tx_ids[] = { + { .compatible = "gdsys,logicore_dp_tx" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(logicore_dp_tx) = { + .name = "logicore_dp_tx", + .id = UCLASS_DISPLAY, + .of_match = logicore_dp_tx_ids, + .probe = logicore_dp_tx_probe, + .priv_auto_alloc_size = sizeof(struct dp_tx), + .ops = &logicore_dp_tx_ops, +}; diff --git a/drivers/video/logicore_dp_tx.h b/drivers/video/logicore_dp_tx.h new file mode 100644 index 00000000000..32f9649b9ce --- /dev/null +++ b/drivers/video/logicore_dp_tx.h @@ -0,0 +1,40 @@ +/* + * logicore_dp_tx.h + * + * Driver for XILINX LogiCore DisplayPort v6.1 TX (Source) + * + * (C) Copyright 2016 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __GDSYS_LOGICORE_DP_TX_H__ +#define __GDSYS_LOGICORE_DP_TX_H__ + +/* + * this is a stripped down version of struct main_stream_attributes that contains + * only the parameters that are not set by cfg_msa_recalculate() + */ +struct logicore_dp_tx_msa { + u32 pixel_clock_hz; /* The pixel clock of the stream (in Hz). */ + u32 bits_per_color; /* Number of bits per color component. */ + u16 h_active; /* Horizontal active resolution (pixels) */ + u32 h_start; /* Horizontal blank start (in pixels). */ + bool h_sync_polarity; /* Horizontal sync polarity (0=negative|1=positive) */ + u16 h_sync_width; /* Horizontal sync width (pixels) */ + u16 h_total; /* Horizontal total (pixels) */ + u16 v_active; /* Vertical active resolution (lines) */ + u32 v_start; /* Vertical blank start (in lines). */ + bool v_sync_polarity; /* Vertical sync polarity (0=negative|1=positive) */ + u16 v_sync_width; /* Vertical sync width (lines) */ + u16 v_total; /* Vertical total (lines) */ + bool override_user_pixel_width; /* If true, the value stored for + user_pixel_width will be used as + the pixel width. */ + u32 user_pixel_width; /* The width of the user data input port. */ +}; + +int logicore_dp_tx_video_enable(struct udevice *dev, struct logicore_dp_tx_msa *msa); + +#endif /* __GDSYS_LOGICORE_DP_TX_H__ */ diff --git a/drivers/video/logicore_dp_tx_regif.h b/drivers/video/logicore_dp_tx_regif.h new file mode 100644 index 00000000000..c4105605c9b --- /dev/null +++ b/drivers/video/logicore_dp_tx_regif.h @@ -0,0 +1,365 @@ +/* + * logicore_dp_tx_regif.h + * + * Register interface definition for XILINX LogiCore DisplayPort v6.1 TX (Source) + * based on Xilinx dp_v3_1 driver sources + * + * (C) Copyright 2016 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __GDSYS_LOGICORE_DP_TX_REGIF_H__ +#define __GDSYS_LOGICORE_DP_TX_REGIF_H__ + +enum { + /* link configuration field */ + REG_LINK_BW_SET = 0x000, + REG_LANE_COUNT_SET = 0x004, + REG_ENHANCED_FRAME_EN = 0x008, + REG_TRAINING_PATTERN_SET = 0x00C, + REG_LINK_QUAL_PATTERN_SET = 0x010, + REG_SCRAMBLING_DISABLE = 0x014, + REG_DOWNSPREAD_CTRL = 0x018, + REG_SOFT_RESET = 0x01C, + + /* core enables */ + REG_ENABLE = 0x080, + REG_ENABLE_MAIN_STREAM = 0x084, + REG_ENABLE_SEC_STREAM = 0x088, + REG_FORCE_SCRAMBLER_RESET = 0x0C0, + REG_MST_CONFIG = 0x0D0, + REG_LINE_RESET_DISABLE = 0x0F0, + + /* core ID */ + REG_VERSION = 0x0F8, + REG_CORE_ID = 0x0FC, + + /* AUX channel interface */ + REG_AUX_CMD = 0x100, + REG_AUX_WRITE_FIFO = 0x104, + REG_AUX_ADDRESS = 0x108, + REG_AUX_CLK_DIVIDER = 0x10C, + REG_USER_FIFO_OVERFLOW = 0x110, + REG_INTERRUPT_SIG_STATE = 0x130, + REG_AUX_REPLY_DATA = 0x134, + REG_AUX_REPLY_CODE = 0x138, + REG_AUX_REPLY_COUNT = 0x13C, + REG_INTERRUPT_STATUS = 0x140, + REG_INTERRUPT_MASK = 0x144, + REG_REPLY_DATA_COUNT = 0x148, + REG_REPLY_STATUS = 0x14C, + REG_HPD_DURATION = 0x150, + + /* main stream attributes for SST / MST STREAM1 */ + REG_STREAM1_MSA_START = 0x180, + REG_MAIN_STREAM_HTOTAL = 0x180, + REG_MAIN_STREAM_VTOTAL = 0x184, + REG_MAIN_STREAM_POLARITY = 0x188, + REG_MAIN_STREAM_HSWIDTH = 0x18C, + REG_MAIN_STREAM_VSWIDTH = 0x190, + REG_MAIN_STREAM_HRES = 0x194, + REG_MAIN_STREAM_VRES = 0x198, + REG_MAIN_STREAM_HSTART = 0x19C, + REG_MAIN_STREAM_VSTART = 0x1A0, + REG_MAIN_STREAM_MISC0 = 0x1A4, + REG_MAIN_STREAM_MISC1 = 0x1A8, + REG_M_VID = 0x1AC, + REG_TU_SIZE = 0x1B0, + REG_N_VID = 0x1B4, + REG_USER_PIXEL_WIDTH = 0x1B8, + REG_USER_DATA_COUNT_PER_LANE =0x1BC, + REG_MAIN_STREAM_INTERLACED = 0x1C0, + REG_MIN_BYTES_PER_TU = 0x1C4, + REG_FRAC_BYTES_PER_TU = 0x1C8, + REG_INIT_WAIT = 0x1CC, + REG_STREAM1 = 0x1D0, + REG_STREAM2 = 0x1D4, + REG_STREAM3 = 0x1D8, + REG_STREAM4 = 0x1DC, + + /* PHY configuration status */ + REG_PHY_CONFIG = 0x200, + REG_PHY_VOLTAGE_DIFF_LANE_0 = 0x220, + REG_PHY_VOLTAGE_DIFF_LANE_1 = 0x224, + REG_PHY_VOLTAGE_DIFF_LANE_2 = 0x228, + REG_PHY_VOLTAGE_DIFF_LANE_3 = 0x22C, + REG_PHY_TRANSMIT_PRBS7 = 0x230, + REG_PHY_CLOCK_SELECT = 0x234, + REG_PHY_POWER_DOWN = 0x238, + REG_PHY_PRECURSOR_LANE_0 = 0x23C, + REG_PHY_PRECURSOR_LANE_1 = 0x240, + REG_PHY_PRECURSOR_LANE_2 = 0x244, + REG_PHY_PRECURSOR_LANE_3 = 0x248, + REG_PHY_POSTCURSOR_LANE_0 = 0x24C, + REG_PHY_POSTCURSOR_LANE_1 = 0x250, + REG_PHY_POSTCURSOR_LANE_2 = 0x254, + REG_PHY_POSTCURSOR_LANE_3 = 0x258, + REG_PHY_STATUS = 0x280, + REG_GT_DRP_COMMAND = 0x2A0, + REG_GT_DRP_READ_DATA = 0x2A4, + REG_GT_DRP_CHANNEL_STATUS = 0x2A8, + + /* DisplayPort audio */ + REG_AUDIO_CONTROL = 0x300, + REG_AUDIO_CHANNELS = 0x304, + REG_AUDIO_INFO_DATA = 0x308, + REG_AUDIO_MAUD = 0x328, + REG_AUDIO_NAUD = 0x32C, + REG_AUDIO_EXT_DATA = 0x330, + + /* HDCP */ + REG_HDCP_ENABLE = 0x400, + + /* main stream attributes for MST STREAM2, 3, and 4 */ + REG_STREAM2_MSA_START = 0x500, + REG_STREAM3_MSA_START = 0x550, + REG_STREAM4_MSA_START = 0x5A0, + + REG_VC_PAYLOAD_BUFFER_ADDR = 0x800, +}; + +enum { + LINK_BW_SET_162GBPS = 0x06, + LINK_BW_SET_270GBPS = 0x0A, + LINK_BW_SET_540GBPS = 0x14, +}; + +enum { + LANE_COUNT_SET_1 = 0x1, + LANE_COUNT_SET_2 = 0x2, + LANE_COUNT_SET_4 = 0x4, +}; + +enum { + TRAINING_PATTERN_SET_OFF = 0x0, + /* training pattern 1 used for clock recovery */ + TRAINING_PATTERN_SET_TP1 = 0x1, + /* training pattern 2 used for channel equalization */ + TRAINING_PATTERN_SET_TP2 = 0x2, + /* training pattern 3 used for channel equalization for cores with DP v1.2 */ + TRAINING_PATTERN_SET_TP3 = 0x3, +}; + +enum { + LINK_QUAL_PATTERN_SET_OFF = 0x0, + /* D10.2 unscrambled test pattern transmitted */ + LINK_QUAL_PATTERN_SET_D102_TEST = 0x1, + /* symbol error rate measurement pattern transmitted */ + LINK_QUAL_PATTERN_SET_SER_MES = 0x2, + /* pseudo random bit sequence 7 transmitted */ + LINK_QUAL_PATTERN_SET_PRBS7 = 0x3, +}; + +enum { + SOFT_RESET_VIDEO_STREAM1_MASK = 0x00000001, + SOFT_RESET_VIDEO_STREAM2_MASK = 0x00000002, + SOFT_RESET_VIDEO_STREAM3_MASK = 0x00000004, + SOFT_RESET_VIDEO_STREAM4_MASK = 0x00000008, + SOFT_RESET_AUX_MASK = 0x00000080, + SOFT_RESET_VIDEO_STREAM_ALL_MASK = 0x0000000F, +}; + +enum { + MST_CONFIG_MST_EN_MASK = 0x00000001, +}; + +enum { + LINE_RESET_DISABLE_MASK = 0x1, +}; + +#define AUX_CMD_NBYTES_TRANSFER_MASK 0x0000000F + +#define AUX_CMD_SHIFT 8 +#define AUX_CMD_MASK 0x00000F00 +enum { + AUX_CMD_I2C_WRITE = 0x0, + AUX_CMD_I2C_READ = 0x1, + AUX_CMD_I2C_WRITE_STATUS = 0x2, + AUX_CMD_I2C_WRITE_MOT = 0x4, + AUX_CMD_I2C_READ_MOT = 0x5, + AUX_CMD_I2C_WRITE_STATUS_MOT = 0x6, + AUX_CMD_WRITE = 0x8, + AUX_CMD_READ = 0x9, +}; + +#define AUX_CLK_DIVIDER_VAL_MASK 0x00FF + +#define AUX_CLK_DIVIDER_AUX_SIG_WIDTH_FILT_SHIFT 8 +#define AUX_CLK_DIVIDER_AUX_SIG_WIDTH_FILT_MASK 0xFF00 + +enum { + INTERRUPT_SIG_STATE_HPD_STATE_MASK = 0x00000001, + INTERRUPT_SIG_STATE_REQUEST_STATE_MASK = 0x00000002, + INTERRUPT_SIG_STATE_REPLY_STATE_MASK = 0x00000004, + INTERRUPT_SIG_STATE_REPLY_TIMEOUT_MASK = 0x00000008, +}; + +enum { + AUX_REPLY_CODE_ACK = 0x0, + AUX_REPLY_CODE_I2C_ACK = 0x0, + AUX_REPLY_CODE_NACK = 0x1, + AUX_REPLY_CODE_DEFER = 0x2, + AUX_REPLY_CODE_I2C_NACK = 0x4, + AUX_REPLY_CODE_I2C_DEFER = 0x8, +}; + +enum { + INTERRUPT_STATUS_HPD_IRQ_MASK = 0x00000001, + INTERRUPT_STATUS_HPD_EVENT_MASK = 0x00000002, + INTERRUPT_STATUS_REPLY_RECEIVED_MASK = 0x00000004, + INTERRUPT_STATUS_REPLY_TIMEOUT_MASK = 0x00000008, + INTERRUPT_STATUS_HPD_PULSE_DETECTED_MASK = 0x00000010, + INTERRUPT_STATUS_EXT_PKT_TXD_MASK = 0x00000020, +}; + +enum { + INTERRUPT_MASK_HPD_IRQ_MASK = 0x00000001, + INTERRUPT_MASK_HPD_EVENT_MASK = 0x00000002, + INTERRUPT_MASK_REPLY_RECEIVED_MASK = 0x00000004, + INTERRUPT_MASK_REPLY_TIMEOUT_MASK = 0x00000008, + INTERRUPT_MASK_HPD_PULSE_DETECTED_MASK = 0x00000010, + INTERRUPT_MASK_EXT_PKT_TXD_MASK = 0x00000020, +}; + +#define REPLY_STATUS_REPLY_STATUS_STATE_SHIFT 4 +#define REPLY_STATUS_REPLY_STATUS_STATE_MASK 0x00000FF0 +enum { + REPLY_STATUS_REPLY_RECEIVED_MASK = 0x00000001, + REPLY_STATUS_REPLY_IN_PROGRESS_MASK = 0x00000002, + REPLY_STATUS_REQUEST_IN_PROGRESS_MASK = 0x00000004, + REPLY_STATUS_REPLY_ERROR_MASK = 0x00000008, +}; + +#define MAIN_STREAMX_POLARITY_VSYNC_POL_SHIFT 1 +enum { + MAIN_STREAMX_POLARITY_HSYNC_POL_MASK = 0x00000001, + MAIN_STREAMX_POLARITY_VSYNC_POL_MASK = 0x00000002, +}; + +enum { + MAIN_STREAMX_MISC0_SYNC_CLK_MASK = 0x00000001, +}; + +#define MAIN_STREAMX_MISC0_COMPONENT_FORMAT_SHIFT 1 +#define MAIN_STREAMX_MISC0_COMPONENT_FORMAT_MASK 0x00000006 +enum { + MAIN_STREAMX_MISC0_COMPONENT_FORMAT_RGB = 0x0, + MAIN_STREAMX_MISC0_COMPONENT_FORMAT_YCBCR422 = 0x1, + MAIN_STREAMX_MISC0_COMPONENT_FORMAT_YCBCR444 = 0x2, +}; + +#define MAIN_STREAMX_MISC0_DYNAMIC_RANGE_SHIFT 3 +#define MAIN_STREAMX_MISC0_DYNAMIC_RANGE_MASK 0x00000008 + +#define MAIN_STREAMX_MISC0_YCBCR_COLORIMETRY_SHIFT 4 +#define MAIN_STREAMX_MISC0_YCBCR_COLORIMETRY_MASK 0x00000010 + +#define MAIN_STREAMX_MISC0_BDC_SHIFT 5 +#define MAIN_STREAMX_MISC0_BDC_MASK 0x000000E0 +enum { + MAIN_STREAMX_MISC0_BDC_6BPC = 0x0, + MAIN_STREAMX_MISC0_BDC_8BPC = 0x1, + MAIN_STREAMX_MISC0_BDC_10BPC = 0x2, + MAIN_STREAMX_MISC0_BDC_12BPC = 0x3, + MAIN_STREAMX_MISC0_BDC_16BPC = 0x4, +}; + +enum { + PHY_CONFIG_PHY_RESET_ENABLE_MASK = 0x0000000, + PHY_CONFIG_PHY_RESET_MASK = 0x0000001, + PHY_CONFIG_GTTX_RESET_MASK = 0x0000002, + PHY_CONFIG_GT_ALL_RESET_MASK = 0x0000003, + PHY_CONFIG_TX_PHY_PMA_RESET_MASK = 0x0000100, + PHY_CONFIG_TX_PHY_PCS_RESET_MASK = 0x0000200, + PHY_CONFIG_TX_PHY_POLARITY_MASK = 0x0000800, + PHY_CONFIG_TX_PHY_PRBSFORCEERR_MASK = 0x0001000, + PHY_CONFIG_TX_PHY_POLARITY_IND_LANE_MASK = 0x0010000, + PHY_CONFIG_TX_PHY_POLARITY_LANE0_MASK = 0x0020000, + PHY_CONFIG_TX_PHY_POLARITY_LANE1_MASK = 0x0040000, + PHY_CONFIG_TX_PHY_POLARITY_LANE2_MASK = 0x0080000, + PHY_CONFIG_TX_PHY_POLARITY_LANE3_MASK = 0x0100000, + PHY_CONFIG_TX_PHY_8B10BEN_MASK = 0x0200000, +}; + +#define PHY_CONFIG_TX_PHY_LOOPBACK_SHIFT 13 +#define PHY_CONFIG_TX_PHY_LOOPBACK_MASK 0x000E000 + +enum { + PHY_CLOCK_SELECT_162GBPS = 0x1, + PHY_CLOCK_SELECT_270GBPS = 0x3, + PHY_CLOCK_SELECT_540GBPS = 0x5, +}; + +enum { + VS_LEVEL_0 = 0x2, + VS_LEVEL_1 = 0x5, + VS_LEVEL_2 = 0x8, + VS_LEVEL_3 = 0xF, + VS_LEVEL_OFFSET = 0x4, +}; + +enum { + PE_LEVEL_0 = 0x00, + PE_LEVEL_1 = 0x0E, + PE_LEVEL_2 = 0x14, + PE_LEVEL_3 = 0x1B, +}; + +enum { + PHY_STATUS_RESET_LANE_2_3_DONE_SHIFT = 2, + PHY_STATUS_TX_ERROR_LANE_0_SHIFT = 18, + PHY_STATUS_TX_BUFFER_STATUS_LANE_1_SHIFT = 20, + PHY_STATUS_TX_ERROR_LANE_1_SHIFT = 22, + PHY_STATUS_TX_BUFFER_STATUS_LANE_0_SHIFT = 16, + PHY_STATUS_TX_BUFFER_STATUS_LANE_2_SHIFT = 24, + PHY_STATUS_TX_ERROR_LANE_2_SHIFT = 26, + PHY_STATUS_TX_BUFFER_STATUS_LANE_3_SHIFT = 28, + PHY_STATUS_TX_ERROR_LANE_3_SHIFT = 30, +}; + +enum { + PHY_STATUS_RESET_LANE_0_DONE_MASK = 0x00000001, + PHY_STATUS_RESET_LANE_1_DONE_MASK = 0x00000002, + PHY_STATUS_RESET_LANE_2_3_DONE_MASK = 0x0000000C, + PHY_STATUS_PLL_LANE0_1_LOCK_MASK = 0x00000010, + PHY_STATUS_PLL_LANE2_3_LOCK_MASK = 0x00000020, + PHY_STATUS_PLL_FABRIC_LOCK_MASK = 0x00000040, + PHY_STATUS_TX_BUFFER_STATUS_LANE_0_MASK = 0x00030000, + PHY_STATUS_TX_ERROR_LANE_0_MASK = 0x000C0000, + PHY_STATUS_TX_BUFFER_STATUS_LANE_1_MASK = 0x00300000, + PHY_STATUS_TX_ERROR_LANE_1_MASK = 0x00C00000, + PHY_STATUS_TX_BUFFER_STATUS_LANE_2_MASK = 0x03000000, + PHY_STATUS_TX_ERROR_LANE_2_MASK = 0x0C000000, + PHY_STATUS_TX_BUFFER_STATUS_LANE_3_MASK = 0x30000000, + PHY_STATUS_TX_ERROR_LANE_3_MASK = 0xC0000000, +}; + +#define PHY_STATUS_LANE_0_READY_MASK \ + (PHY_STATUS_RESET_LANE_0_DONE_MASK | \ + PHY_STATUS_PLL_LANE0_1_LOCK_MASK) +#define PHY_STATUS_LANES_0_1_READY_MASK \ + (PHY_STATUS_LANE_0_READY_MASK | \ + PHY_STATUS_RESET_LANE_1_DONE_MASK) +/* + * PHY_STATUS_ALL_LANES_READY_MASK seems zo be missing lanes 0 and 1 in + * Xilinx dp_v3_0 implementation + */ +#define PHY_STATUS_ALL_LANES_READY_MASK \ + (PHY_STATUS_LANES_0_1_READY_MASK | \ + PHY_STATUS_RESET_LANE_2_3_DONE_MASK | \ + PHY_STATUS_PLL_LANE2_3_LOCK_MASK) +#define PHY_STATUS_LANES_READY_MASK(n) \ + (((n) > 2) ? PHY_STATUS_ALL_LANES_READY_MASK : \ + ((n) == 2) ? PHY_STATUS_LANES_0_1_READY_MASK : \ + PHY_STATUS_LANE_0_READY_MASK) + +#define GT_DRP_COMMAND_DRP_ADDR_MASK 0x000F +#define GT_DRP_COMMAND_DRP_RW_CMD_MASK 0x0080 +#define GT_DRP_COMMAND_DRP_W_DATA_SHIFT 16 +#define GT_DRP_COMMAND_DRP_W_DATA_MASK 0xFF00 + +#define HDCP_ENABLE_BYPASS_DISABLE_MASK 0x0001 + +#endif /* __GDSYS_LOGICORE_DP_TX_REGIF_H__ */ -- 2.11.0

Hi Mario,
Please test the patch with ./scripts/checkpatch.pl, there are warnings/ errors reported that need fixing: ... total: 1 errors, 31 warnings, 26 checks, 2746 lines checked
On Wed, 23 May 2018 14:10:47 +0200 Mario Six mario.six@gdsys.cc wrote: ...
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index cf7ad281c3b..fa4ac715fcf 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o
obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o +obj-$(CONFIG_LOGICORE_DP_TX) += logicore_dp_tx.o
Please add new driver entry sorted alphabetically. I know there are already not sorted entries in the list here, but we shouldn't add more.
...
diff --git a/drivers/video/logicore_dp_dpcd.h b/drivers/video/logicore_dp_dpcd.h new file mode 100644 index 00000000000..68582945514 --- /dev/null +++ b/drivers/video/logicore_dp_dpcd.h
...
+struct main_stream_attributes {
- /* Pixel clock of the stream (in Hz). */
- u32 pixel_clock_hz;
- /* Miscellaneous stream attributes 0 as specified by the DisplayPort
* 1.2 specification.
*/
Please fix multi-line comment style throughout this patch, we use this style: /* * multi-line * comment */
...
+static u32 get_reg(struct udevice *dev, u32 reg) +{
- struct dp_tx *dp_tx = dev_get_priv(dev);
- u32 value = 0;
- int res;
- // TODO error handling
please no C++ comments.
...
+bool is_connected(struct udevice *dev) +{
shouldn't it be static?
...
+bool is_link_rate_valid(struct udevice *dev, u8 link_rate) +{
...
+bool is_lane_count_valid(struct udevice *dev, u8 lane_count) +{
Are these functions supposed to be used externally? If not, please add static. Otherwise move them to /* external API */ section.
...
+int logicore_dp_tx_enable(struct udevice *dev, int panel_bpp,
const struct display_timing *timing)
+{
should be static?
...
+int logicore_dp_tx_probe(struct udevice *dev) +{
should be static?
...
+struct dm_display_ops logicore_dp_tx_ops = {
- .enable = logicore_dp_tx_enable,
+};
should be static, too.
...
diff --git a/drivers/video/logicore_dp_tx_regif.h b/drivers/video/logicore_dp_tx_regif.h new file mode 100644 index 00000000000..c4105605c9b --- /dev/null +++ b/drivers/video/logicore_dp_tx_regif.h
...
- /* core ID */
- REG_VERSION = 0x0F8,
- REG_CORE_ID = 0x0FC,
here a space and tabs follow '=', please use tabs only.
...
- REG_USER_PIXEL_WIDTH = 0x1B8,
- REG_USER_DATA_COUNT_PER_LANE =0x1BC,
spaces around '=', checkpatch will report style issues like this.
...
+/*
- PHY_STATUS_ALL_LANES_READY_MASK seems zo be missing lanes 0 and 1 in
s/zo/to ?
Thanks,
-- Anatolij

Hi Anatolij,
On Sat, May 26, 2018 at 12:24 AM, Anatolij Gustschin agust@denx.de wrote:
Hi Mario,
Please test the patch with ./scripts/checkpatch.pl, there are warnings/ errors reported that need fixing: ... total: 1 errors, 31 warnings, 26 checks, 2746 lines checked
Right, sorry, will be fixed in v3.
On Wed, 23 May 2018 14:10:47 +0200 Mario Six mario.six@gdsys.cc wrote: ...
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index cf7ad281c3b..fa4ac715fcf 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_VIDEO_IVYBRIDGE_IGD) += ivybridge_igd.o
obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o +obj-$(CONFIG_LOGICORE_DP_TX) += logicore_dp_tx.o
Please add new driver entry sorted alphabetically. I know there are already not sorted entries in the list here, but we shouldn't add more.
OK, I'll add a patch that sorts the entries, and add the new entry with respect to the new ordering.
...
diff --git a/drivers/video/logicore_dp_dpcd.h b/drivers/video/logicore_dp_dpcd.h new file mode 100644 index 00000000000..68582945514 --- /dev/null +++ b/drivers/video/logicore_dp_dpcd.h
...
+struct main_stream_attributes {
/* Pixel clock of the stream (in Hz). */
u32 pixel_clock_hz;
/* Miscellaneous stream attributes 0 as specified by the DisplayPort
* 1.2 specification.
*/
Please fix multi-line comment style throughout this patch, we use this style: /* * multi-line * comment */
Will be fixed in v3.
...
+static u32 get_reg(struct udevice *dev, u32 reg) +{
struct dp_tx *dp_tx = dev_get_priv(dev);
u32 value = 0;
int res;
// TODO error handling
please no C++ comments.
Overlooked that one. Will be fixed in v3.
...
+bool is_connected(struct udevice *dev) +{
shouldn't it be static?
Yes, good idea, I'll declare it static for v3.
...
+bool is_link_rate_valid(struct udevice *dev, u8 link_rate) +{
...
+bool is_lane_count_valid(struct udevice *dev, u8 lane_count) +{
Are these functions supposed to be used externally? If not, please add static. Otherwise move them to /* external API */ section.
No, the U-Boot driver doesn't use them outside the file. I'll mark them static in v3.
...
+int logicore_dp_tx_enable(struct udevice *dev, int panel_bpp,
const struct display_timing *timing)
+{
should be static?
Dito.
...
+int logicore_dp_tx_probe(struct udevice *dev) +{
should be static?
...
+struct dm_display_ops logicore_dp_tx_ops = {
.enable = logicore_dp_tx_enable,
+};
should be static, too.
Dito.
...
diff --git a/drivers/video/logicore_dp_tx_regif.h b/drivers/video/logicore_dp_tx_regif.h new file mode 100644 index 00000000000..c4105605c9b --- /dev/null +++ b/drivers/video/logicore_dp_tx_regif.h
...
/* core ID */
REG_VERSION = 0x0F8,
REG_CORE_ID = 0x0FC,
here a space and tabs follow '=', please use tabs only.
...
REG_USER_PIXEL_WIDTH = 0x1B8,
REG_USER_DATA_COUNT_PER_LANE =0x1BC,
spaces around '=', checkpatch will report style issues like this.
I'll fix the whitespace issues in v3.
...
+/*
- PHY_STATUS_ALL_LANES_READY_MASK seems zo be missing lanes 0 and 1 in
s/zo/to ?
Yeah, that's a typo. Will fix in v3.
Thanks,
Thanks for reviewing, and best regards, Mario

On 23 May 2018 at 06:10, Mario Six mario.six@gdsys.cc wrote:
Add a uclass for AXI (Advanced eXtensible Interface) busses, and a driver for the gdsys IHS AXI bus on IHS FPGAs.
Signed-off-by: Mario Six mario.six@gdsys.cc
v1 -> v2:
- Spelled out all abbreviations in the Kconfig help
- Split commit into uclass addition and driver addition
drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/axi/Kconfig | 13 +++++++++ drivers/axi/Makefile | 8 ++++++ drivers/axi/axi-uclass.c | 40 ++++++++++++++++++++++++++ include/axi.h | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 7 files changed, 140 insertions(+) create mode 100644 drivers/axi/Kconfig create mode 100644 drivers/axi/Makefile create mode 100644 drivers/axi/axi-uclass.c create mode 100644 include/axi.h
Reviewed-by: Simon Glass sjg@chromium.org
One little nit:
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index d7f9df3583a..0aad4bc14d7 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -43,6 +43,7 @@ enum uclass_id { UCLASS_I2C_GENERIC, /* Generic I2C device */ UCLASS_I2C_MUX, /* I2C multiplexer */ UCLASS_IDE, /* IDE device */
UCLASS_AXI, /* AXI busses */
AXI bus
UCLASS_IRQ, /* Interrupt controller */ UCLASS_KEYBOARD, /* Keyboard input device */ UCLASS_LED, /* Light-emitting diode (LED) */
-- 2.11.0

Hi Simon,
On Fri, May 25, 2018 at 4:41 AM, Simon Glass sjg@chromium.org wrote:
On 23 May 2018 at 06:10, Mario Six mario.six@gdsys.cc wrote:
Add a uclass for AXI (Advanced eXtensible Interface) busses, and a driver for the gdsys IHS AXI bus on IHS FPGAs.
Signed-off-by: Mario Six mario.six@gdsys.cc
v1 -> v2:
- Spelled out all abbreviations in the Kconfig help
- Split commit into uclass addition and driver addition
drivers/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/axi/Kconfig | 13 +++++++++ drivers/axi/Makefile | 8 ++++++ drivers/axi/axi-uclass.c | 40 ++++++++++++++++++++++++++ include/axi.h | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + 7 files changed, 140 insertions(+) create mode 100644 drivers/axi/Kconfig create mode 100644 drivers/axi/Makefile create mode 100644 drivers/axi/axi-uclass.c create mode 100644 include/axi.h
Reviewed-by: Simon Glass sjg@chromium.org
One little nit:
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index d7f9df3583a..0aad4bc14d7 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -43,6 +43,7 @@ enum uclass_id { UCLASS_I2C_GENERIC, /* Generic I2C device */ UCLASS_I2C_MUX, /* I2C multiplexer */ UCLASS_IDE, /* IDE device */
UCLASS_AXI, /* AXI busses */
AXI bus
Sure, will fix for v3.
UCLASS_IRQ, /* Interrupt controller */ UCLASS_KEYBOARD, /* Keyboard input device */ UCLASS_LED, /* Light-emitting diode (LED) */
-- 2.11.0
Best regards, Mario
participants (3)
-
Anatolij Gustschin
-
Mario Six
-
Simon Glass