Commit 9fc8706d65fc812f4e1a2da78d3b4411c866db63

Authored by Mario Six
Committed by Anatolij Gustschin
1 parent a63e54ab5f

axi: Add ihs_axi driver

Add a driver for the gdsys IHS AXI bus used on IHS FPGAs.

Reviewed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Mario Six <mario.six@gdsys.cc>

Showing 4 changed files with 328 additions and 0 deletions Side-by-side Diff

Documentation/devicetree/bindings/axi/gdsys,ihs_axi.txt
  1 +gdsys AXI busses of IHS FPGA devices
  2 +
  3 +Certain gdsys IHS FPGAs offer a interface to their built-in AXI bus with which
  4 +the connected devices (usually IP cores) can be controlled via software.
  5 +
  6 +Required properties:
  7 +- compatible: must be "gdsys,ihs_axi"
  8 +- reg: describes the address and length of the AXI bus's register map (within
  9 + the FPGA's register space)
  10 +
  11 +Example:
  12 +
  13 +fpga0_axi_video0 {
  14 + #address-cells = <1>;
  15 + #size-cells = <1>;
  16 + compatible = "gdsys,ihs_axi";
  17 + reg = <0x170 0x10>;
  18 +
  19 + axi_dev_1 {
  20 + ...
  21 + };
  22 +};
... ... @@ -11,4 +11,16 @@
11 11 for now).
12 12  
13 13 Other similar bus architectures may be compatible as well.
  14 +
  15 +if AXI
  16 +
  17 +config IHS_AXI
  18 + bool "Enable IHS AXI driver"
  19 + depends on DM
  20 + help
  21 + Support for gdsys Integrated Hardware Systems Advanced eXtensible
  22 + Interface (IHS AXI) bus on a gdsys IHS FPGA used to communicate with
  23 + IP cores in the FPGA (e.g. video transmitter cores).
  24 +
  25 +endif
drivers/axi/Makefile
... ... @@ -6,4 +6,5 @@
6 6 #
7 7  
8 8 obj-$(CONFIG_AXI) += axi-uclass.o
  9 +obj-$(CONFIG_IHS_AXI) += ihs_axi.o
drivers/axi/ihs_axi.c
  1 +// SPDX-License-Identifier: GPL-2.0+
  2 +/*
  3 + * (C) Copyright 2016
  4 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc
  5 + *
  6 + * (C) Copyright 2017, 2018
  7 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
  8 + */
  9 +
  10 +#include <common.h>
  11 +#include <axi.h>
  12 +#include <dm.h>
  13 +#include <regmap.h>
  14 +
  15 +/**
  16 + * struct ihs_axi_regs - Structure for the register map of a IHS AXI device
  17 + * @interrupt_status: Status register to indicate certain events (e.g.
  18 + * error during transfer, transfer complete, etc.)
  19 + * @interrupt_enable_control: Register to both control which statuses will be
  20 + * indicated in the interrupt_status register, and
  21 + * to change bus settings
  22 + * @address_lsb: Least significant 16-bit word of the address of a
  23 + * device to transfer data from/to
  24 + * @address_msb: Most significant 16-bit word of the address of a
  25 + * device to transfer data from/to
  26 + * @write_data_lsb: Least significant 16-bit word of the data to be
  27 + * written to a device
  28 + * @write_data_msb: Most significant 16-bit word of the data to be
  29 + * written to a device
  30 + * @read_data_lsb: Least significant 16-bit word of the data read
  31 + * from a device
  32 + * @read_data_msb: Most significant 16-bit word of the data read
  33 + * from a device
  34 + */
  35 +struct ihs_axi_regs {
  36 + u16 interrupt_status;
  37 + u16 interrupt_enable_control;
  38 + u16 address_lsb;
  39 + u16 address_msb;
  40 + u16 write_data_lsb;
  41 + u16 write_data_msb;
  42 + u16 read_data_lsb;
  43 + u16 read_data_msb;
  44 +};
  45 +
  46 +/**
  47 + * ihs_axi_set() - Convenience macro to set values in register map
  48 + * @map: The register map to write to
  49 + * @member: The member of the ihs_axi_regs structure to write
  50 + * @val: The value to write to the register map
  51 + */
  52 +#define ihs_axi_set(map, member, val) \
  53 + regmap_set(map, struct ihs_axi_regs, member, val)
  54 +
  55 +/**
  56 + * ihs_axi_get() - Convenience macro to read values from register map
  57 + * @map: The register map to read from
  58 + * @member: The member of the ihs_axi_regs structure to read
  59 + * @valp: Pointer to a buffer to receive the value read
  60 + */
  61 +#define ihs_axi_get(map, member, valp) \
  62 + regmap_get(map, struct ihs_axi_regs, member, valp)
  63 +
  64 +/**
  65 + * struct ihs_axi_priv - Private data structure of IHS AXI devices
  66 + * @map: Register map for the IHS AXI device
  67 + */
  68 +struct ihs_axi_priv {
  69 + struct regmap *map;
  70 +};
  71 +
  72 +/**
  73 + * enum status_reg - Description of bits in the interrupt_status register
  74 + * @STATUS_READ_COMPLETE_EVENT: A read transfer was completed
  75 + * @STATUS_WRITE_COMPLETE_EVENT: A write transfer was completed
  76 + * @STATUS_TIMEOUT_EVENT: A timeout has occurred during the transfer
  77 + * @STATUS_ERROR_EVENT: A error has occurred during the transfer
  78 + * @STATUS_AXI_INT: A AXI interrupt has occurred
  79 + * @STATUS_READ_DATA_AVAILABLE: Data is available to be read
  80 + * @STATUS_BUSY: The bus is busy
  81 + * @STATUS_INIT_DONE: The bus has finished initializing
  82 + */
  83 +enum status_reg {
  84 + STATUS_READ_COMPLETE_EVENT = BIT(15),
  85 + STATUS_WRITE_COMPLETE_EVENT = BIT(14),
  86 + STATUS_TIMEOUT_EVENT = BIT(13),
  87 + STATUS_ERROR_EVENT = BIT(12),
  88 + STATUS_AXI_INT = BIT(11),
  89 + STATUS_READ_DATA_AVAILABLE = BIT(7),
  90 + STATUS_BUSY = BIT(6),
  91 + STATUS_INIT_DONE = BIT(5),
  92 +};
  93 +
  94 +/**
  95 + * enum control_reg - Description of bit fields in the interrupt_enable_control
  96 + * register
  97 + * @CONTROL_READ_COMPLETE_EVENT_ENABLE: STATUS_READ_COMPLETE_EVENT will be
  98 + * raised in the interrupt_status register
  99 + * @CONTROL_WRITE_COMPLETE_EVENT_ENABLE: STATUS_WRITE_COMPLETE_EVENT will be
  100 + * raised in the interrupt_status register
  101 + * @CONTROL_TIMEOUT_EVENT_ENABLE: STATUS_TIMEOUT_EVENT will be raised in
  102 + * the interrupt_status register
  103 + * @CONTROL_ERROR_EVENT_ENABLE: STATUS_ERROR_EVENT will be raised in
  104 + * the interrupt_status register
  105 + * @CONTROL_AXI_INT_ENABLE: STATUS_AXI_INT will be raised in the
  106 + * interrupt_status register
  107 + * @CONTROL_CMD_NOP: Configure bus to send a NOP command
  108 + * for the next transfer
  109 + * @CONTROL_CMD_WRITE: Configure bus to do a write transfer
  110 + * @CONTROL_CMD_WRITE_POST_INC: Auto-increment address after write
  111 + * transfer
  112 + * @CONTROL_CMD_READ: Configure bus to do a read transfer
  113 + * @CONTROL_CMD_READ_POST_INC: Auto-increment address after read
  114 + * transfer
  115 + */
  116 +enum control_reg {
  117 + CONTROL_READ_COMPLETE_EVENT_ENABLE = BIT(15),
  118 + CONTROL_WRITE_COMPLETE_EVENT_ENABLE = BIT(14),
  119 + CONTROL_TIMEOUT_EVENT_ENABLE = BIT(13),
  120 + CONTROL_ERROR_EVENT_ENABLE = BIT(12),
  121 + CONTROL_AXI_INT_ENABLE = BIT(11),
  122 +
  123 + CONTROL_CMD_NOP = 0x0,
  124 + CONTROL_CMD_WRITE = 0x8,
  125 + CONTROL_CMD_WRITE_POST_INC = 0x9,
  126 + CONTROL_CMD_READ = 0xa,
  127 + CONTROL_CMD_READ_POST_INC = 0xb,
  128 +};
  129 +
  130 +/**
  131 + * enum axi_cmd - Determine if transfer is read or write transfer
  132 + * @AXI_CMD_READ: The transfer should be a read transfer
  133 + * @AXI_CMD_WRITE: The transfer should be a write transfer
  134 + */
  135 +enum axi_cmd {
  136 + AXI_CMD_READ,
  137 + AXI_CMD_WRITE,
  138 +};
  139 +
  140 +/**
  141 + * ihs_axi_transfer() - Run transfer on the AXI bus
  142 + * @bus: The AXI bus device on which to run the transfer on
  143 + * @address: The address to use in the transfer (i.e. which address to
  144 + * read/write from/to)
  145 + * @cmd: Should the transfer be a read or write transfer?
  146 + *
  147 + * Return: 0 if OK, -ve on error
  148 + */
  149 +static int ihs_axi_transfer(struct udevice *bus, ulong address,
  150 + enum axi_cmd cmd)
  151 +{
  152 + struct ihs_axi_priv *priv = dev_get_priv(bus);
  153 + /* Try waiting for events up to 10 times */
  154 + const uint WAIT_TRIES = 10;
  155 + u16 wait_mask = STATUS_TIMEOUT_EVENT |
  156 + STATUS_ERROR_EVENT;
  157 + u16 complete_flag;
  158 + u16 status;
  159 + uint k;
  160 +
  161 + if (cmd == AXI_CMD_READ) {
  162 + complete_flag = STATUS_READ_COMPLETE_EVENT;
  163 + cmd = CONTROL_CMD_READ;
  164 + } else {
  165 + complete_flag = STATUS_WRITE_COMPLETE_EVENT;
  166 + cmd = CONTROL_CMD_WRITE;
  167 + }
  168 +
  169 + wait_mask |= complete_flag;
  170 +
  171 + /* Lower 16 bit */
  172 + ihs_axi_set(priv->map, address_lsb, address & 0xffff);
  173 + /* Upper 16 bit */
  174 + ihs_axi_set(priv->map, address_msb, (address >> 16) & 0xffff);
  175 +
  176 + ihs_axi_set(priv->map, interrupt_status, wait_mask);
  177 + ihs_axi_set(priv->map, interrupt_enable_control, cmd);
  178 +
  179 + for (k = WAIT_TRIES; k > 0; --k) {
  180 + ihs_axi_get(priv->map, interrupt_status, &status);
  181 + if (status & wait_mask)
  182 + break;
  183 + udelay(1);
  184 + }
  185 +
  186 + /*
  187 + * k == 0 -> Tries ran out with no event we were waiting for actually
  188 + * occurring.
  189 + */
  190 + if (!k)
  191 + ihs_axi_get(priv->map, interrupt_status, &status);
  192 +
  193 + if (status & complete_flag)
  194 + return 0;
  195 +
  196 + if (status & STATUS_ERROR_EVENT) {
  197 + debug("%s: Error occurred during transfer\n", bus->name);
  198 + return -EIO;
  199 + }
  200 +
  201 + debug("%s: Transfer timed out\n", bus->name);
  202 + return -ETIMEDOUT;
  203 +}
  204 +
  205 +/*
  206 + * API
  207 + */
  208 +
  209 +static int ihs_axi_read(struct udevice *dev, ulong address, void *data,
  210 + enum axi_size_t size)
  211 +{
  212 + struct ihs_axi_priv *priv = dev_get_priv(dev);
  213 + int ret;
  214 + u16 data_lsb, data_msb;
  215 + u32 *p = data;
  216 +
  217 + if (size != AXI_SIZE_32) {
  218 + debug("%s: transfer size '%d' not supported\n",
  219 + dev->name, size);
  220 + return -ENOSYS;
  221 + }
  222 +
  223 + ret = ihs_axi_transfer(dev, address, AXI_CMD_READ);
  224 + if (ret < 0) {
  225 + debug("%s: Error during AXI transfer (err = %d)\n",
  226 + dev->name, ret);
  227 + return ret;
  228 + }
  229 +
  230 + ihs_axi_get(priv->map, read_data_lsb, &data_lsb);
  231 + ihs_axi_get(priv->map, read_data_msb, &data_msb);
  232 +
  233 + /* Assemble data from two 16-bit words */
  234 + *p = (data_msb << 16) | data_lsb;
  235 +
  236 + return 0;
  237 +}
  238 +
  239 +static int ihs_axi_write(struct udevice *dev, ulong address, void *data,
  240 + enum axi_size_t size)
  241 +{
  242 + struct ihs_axi_priv *priv = dev_get_priv(dev);
  243 + int ret;
  244 + u32 *p = data;
  245 +
  246 + if (size != AXI_SIZE_32) {
  247 + debug("%s: transfer size '%d' not supported\n",
  248 + dev->name, size);
  249 + return -ENOSYS;
  250 + }
  251 +
  252 + /* Lower 16 bit */
  253 + ihs_axi_set(priv->map, write_data_lsb, *p & 0xffff);
  254 + /* Upper 16 bit */
  255 + ihs_axi_set(priv->map, write_data_msb, (*p >> 16) & 0xffff);
  256 +
  257 + ret = ihs_axi_transfer(dev, address, AXI_CMD_WRITE);
  258 + if (ret < 0) {
  259 + debug("%s: Error during AXI transfer (err = %d)\n",
  260 + dev->name, ret);
  261 + return ret;
  262 + }
  263 +
  264 + return 0;
  265 +}
  266 +
  267 +static const struct udevice_id ihs_axi_ids[] = {
  268 + { .compatible = "gdsys,ihs_axi" },
  269 + { /* sentinel */ }
  270 +};
  271 +
  272 +static const struct axi_ops ihs_axi_ops = {
  273 + .read = ihs_axi_read,
  274 + .write = ihs_axi_write,
  275 +};
  276 +
  277 +static int ihs_axi_probe(struct udevice *dev)
  278 +{
  279 + struct ihs_axi_priv *priv = dev_get_priv(dev);
  280 +
  281 + regmap_init_mem(dev_ofnode(dev), &priv->map);
  282 +
  283 + return 0;
  284 +}
  285 +
  286 +U_BOOT_DRIVER(ihs_axi_bus) = {
  287 + .name = "ihs_axi_bus",
  288 + .id = UCLASS_AXI,
  289 + .of_match = ihs_axi_ids,
  290 + .ops = &ihs_axi_ops,
  291 + .priv_auto_alloc_size = sizeof(struct ihs_axi_priv),
  292 + .probe = ihs_axi_probe,
  293 +};