Add driver for the IHS IO endpoint on IHS FPGAs.

Signed-off-by: Mario Six <mario....@gdsys.cc>
---

 drivers/misc/Kconfig      |   6 +
 drivers/misc/Makefile     |   1 +
 drivers/misc/gdsys_ioep.c | 301 ++++++++++++++++++++++++++++++++++++++++++++++
 include/dm/uclass-id.h    |   1 +
 include/gdsys_ioep.h      | 196 ++++++++++++++++++++++++++++++
 5 files changed, 505 insertions(+)
 create mode 100644 drivers/misc/gdsys_ioep.c
 create mode 100644 include/gdsys_ioep.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index dd768e2a7a..f8885380d0 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -221,4 +221,10 @@ config GDSYS_RXAUI_CTRL
        help
          Support gdsys FPGA's RXAUI control.
 
+config GDSYS_IOEP
+       bool "Enable gdsys IOEP driver"
+       depends on MISC
+       help
+         Support gdsys FPGA's IO endpoint driver.
+
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 3ff9e66da2..40788c9acc 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -56,3 +56,4 @@ obj-$(CONFIG_IHS_FPGA) += ihs_fpga.o gdsys_soc.o
 obj-$(CONFIG_IHS_AXI) += ihs_axi.o
 obj-$(CONFIG_IHS_VIDEO_OUT) += ihs_video_out.o
 obj-$(CONFIG_GDSYS_RXAUI_CTRL) += gdsys_rxaui_ctrl.o
+obj-$(CONFIG_GDSYS_IOEP) += gdsys_ioep.o
diff --git a/drivers/misc/gdsys_ioep.c b/drivers/misc/gdsys_ioep.c
new file mode 100644
index 0000000000..5bf1796a40
--- /dev/null
+++ b/drivers/misc/gdsys_ioep.c
@@ -0,0 +1,301 @@
+/*
+ * (C) Copyright 2017
+ * Mario Six,  Guntermann & Drunck GmbH, mario....@gdsys.cc
+ *
+ * based on the cmd_ioloop driver/command, which is
+ *
+ * (C) Copyright 2014
+ * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eib...@gdsys.cc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <gdsys_soc.h>
+#include <ihs_fpga.h>
+#include <gdsys_ioep.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum {
+       REG_TRANSMIT_DATA = 0x0,
+       REG_TX_CONTROL = 0x2,
+       REG_RECEIVE_DATA = 0x4,
+       REG_RX_TX_STATUS = 0x6,
+       REG_DEVICE_ADDRESS = 0xA,
+       REG_TARGET_ADDRESS = 0xC,
+       REG_INT_ENABLE = 0xE,
+};
+
+enum {
+       STATE_TX_PACKET_BUILDING = BIT(0),
+       STATE_TX_TRANSMITTING = BIT(1),
+       STATE_TX_BUFFER_FULL = BIT(2),
+       STATE_TX_ERR = BIT(3),
+       STATE_RECEIVE_TIMEOUT = BIT(4),
+       STATE_PROC_RX_STORE_TIMEOUT = BIT(5),
+       STATE_PROC_RX_RECEIVE_TIMEOUT = BIT(6),
+       STATE_RX_DIST_ERR = BIT(7),
+       STATE_RX_LENGTH_ERR = BIT(8),
+       STATE_RX_FRAME_CTR_ERR = BIT(9),
+       STATE_RX_FCS_ERR = BIT(10),
+       STATE_RX_PACKET_DROPPED = BIT(11),
+       STATE_RX_DATA_LAST = BIT(12),
+       STATE_RX_DATA_FIRST = BIT(13),
+       STATE_RX_DATA_AVAILABLE = BIT(15),
+};
+
+enum {
+       CTRL_PROC_RECEIVE_ENABLE = BIT(12),
+       CTRL_FLUSH_TRANSMIT_BUFFER = BIT(15),
+};
+
+enum {
+       IRQ_CPU_TRANSMITBUFFER_FREE_STATUS = BIT(5),
+       IRQ_CPU_PACKET_TRANSMITTED_EVENT = BIT(6),
+       IRQ_NEW_CPU_PACKET_RECEIVED_EVENT = BIT(7),
+       IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS = BIT(8),
+};
+
+struct gdsys_ioep_priv {
+       int addr;
+};
+
+int ioep_enable_receive(struct udevice *dev)
+{
+       struct ioep_ops *ops = ioep_get_ops(dev);
+
+       return ops->enable_receive(dev);
+}
+
+int ioep_disable_receive(struct udevice *dev)
+{
+       struct ioep_ops *ops = ioep_get_ops(dev);
+
+       return ops->disable_receive(dev);
+}
+
+int ioep_send(struct udevice *dev, struct io_generic_packet *header, u16 *data)
+{
+       struct ioep_ops *ops = ioep_get_ops(dev);
+
+       return ops->send(dev, header, data);
+}
+
+int ioep_receive(struct udevice *dev, struct io_generic_packet *header,
+                u16 *data)
+{
+       struct ioep_ops *ops = ioep_get_ops(dev);
+
+       return ops->receive(dev, header, data);
+}
+
+int ioep_set_address(struct udevice *dev, u16 addr)
+{
+       struct ioep_ops *ops = ioep_get_ops(dev);
+
+       return ops->set_address(dev, addr);
+}
+
+bool ioep_data_available(struct udevice *dev)
+{
+       struct ioep_ops *ops = ioep_get_ops(dev);
+
+       return ops->data_available(dev);
+}
+
+int ioep_reset_status(struct udevice *dev, bool print_status)
+{
+       struct ioep_ops *ops = ioep_get_ops(dev);
+
+       return ops->reset_status(dev, print_status);
+}
+
+UCLASS_DRIVER(ioep) = {
+       .id             = UCLASS_IOEP,
+       .name           = "ioep",
+       .flags          = DM_UC_FLAG_SEQ_ALIAS,
+};
+
+int gdsys_ioep_enable_receive(struct udevice *dev)
+{
+       struct gdsys_ioep_priv *priv = dev_get_priv(dev);
+       struct gdsys_soc_child_platdata *pplat = dev_get_parent_platdata(dev);
+
+       fpga_out_le16(pplat->fpga, priv->addr + REG_TX_CONTROL,
+                     CTRL_PROC_RECEIVE_ENABLE);
+
+       return 0;
+}
+
+int gdsys_ioep_disable_receive(struct udevice *dev)
+{
+       struct gdsys_ioep_priv *priv = dev_get_priv(dev);
+       struct gdsys_soc_child_platdata *pplat = dev_get_parent_platdata(dev);
+
+       fpga_out_le16(pplat->fpga, priv->addr + REG_TX_CONTROL,
+                     ~CTRL_PROC_RECEIVE_ENABLE);
+
+       return 0;
+}
+
+int gdsys_ioep_send(struct udevice *dev, struct io_generic_packet *header,
+                   u16 *data)
+{
+       struct gdsys_ioep_priv *priv = dev_get_priv(dev);
+       struct gdsys_soc_child_platdata *pplat = dev_get_parent_platdata(dev);
+       int k;
+       u16 *p;
+
+       p = (u16 *)header;
+
+       for (k = 0; k < sizeof(struct io_generic_packet) / 2; ++k)
+               fpga_out_le16(pplat->fpga,
+                             priv->addr + REG_TRANSMIT_DATA, *p++);
+
+       p = (u16 *)data;
+
+       for (k = 0; k < (header->packet_length + 1) / 2; ++k)
+               fpga_out_le16(pplat->fpga,
+                             priv->addr + REG_TRANSMIT_DATA, *p++);
+
+       fpga_out_le16(pplat->fpga, priv->addr + REG_TX_CONTROL,
+                     CTRL_PROC_RECEIVE_ENABLE | CTRL_FLUSH_TRANSMIT_BUFFER);
+
+       return 0;
+}
+
+int receive_byte_buffer(struct udevice *dev, uint len, u16 *buffer)
+{
+       struct gdsys_ioep_priv *priv = dev_get_priv(dev);
+       struct gdsys_soc_child_platdata *pplat = dev_get_parent_platdata(dev);
+       int k;
+       int res = -EIO;
+
+       for (k = 0; k < len; ++k) {
+               u16 rx_tx_status;
+
+               *buffer++ = fpga_in_le16(pplat->fpga,
+                                   priv->addr + REG_RECEIVE_DATA);
+
+               rx_tx_status = fpga_in_le16(pplat->fpga,
+                                           priv->addr + REG_RX_TX_STATUS);
+               if (k == len && rx_tx_status & STATE_RX_DATA_LAST)
+                       res = 0;
+       }
+
+       return res;
+}
+
+int gdsys_ioep_receive(struct udevice *dev, struct io_generic_packet *header,
+                      u16 *data)
+{
+       int res1, res2;
+       u16 *p = (u16 *)header;
+
+       res1 = receive_byte_buffer(dev,
+                                  sizeof(struct io_generic_packet) / 2, p);
+
+       if (!res1)
+               res2 = receive_byte_buffer(dev, header->packet_length + 1,
+                                          data);
+
+       return res1 ? res1 : res2;
+}
+
+int gdsys_ioep_set_address(struct udevice *dev, u16 addr)
+{
+       struct gdsys_ioep_priv *priv = dev_get_priv(dev);
+       struct gdsys_soc_child_platdata *pplat = dev_get_parent_platdata(dev);
+
+       /* Set device address */
+       fpga_out_le16(pplat->fpga, priv->addr + REG_DEVICE_ADDRESS, addr);
+
+       return 0;
+}
+
+bool gdsys_ioep_data_available(struct udevice *dev)
+{
+       struct gdsys_ioep_priv *priv = dev_get_priv(dev);
+       struct gdsys_soc_child_platdata *pplat = dev_get_parent_platdata(dev);
+       u16 rx_tx_status;
+
+       rx_tx_status = fpga_in_le16(pplat->fpga, priv->addr + REG_RX_TX_STATUS);
+
+       return rx_tx_status & STATE_RX_DATA_AVAILABLE;
+}
+
+int gdsys_ioep_reset_status(struct udevice *dev, bool print_status)
+{
+       struct gdsys_ioep_priv *priv = dev_get_priv(dev);
+       struct gdsys_soc_child_platdata *pplat = dev_get_parent_platdata(dev);
+       u16 mask = STATE_RX_DIST_ERR | STATE_RX_LENGTH_ERR |
+                  STATE_RX_FRAME_CTR_ERR | STATE_RX_FCS_ERR |
+                  STATE_RX_PACKET_DROPPED | STATE_TX_ERR;
+
+       u16 status = fpga_in_le16(pplat->fpga, priv->addr + REG_RX_TX_STATUS);
+
+       if (!(status & mask)) {
+               fpga_out_le16(pplat->fpga, priv->addr + REG_RX_TX_STATUS,
+                             status);
+               return 0;
+       }
+
+       fpga_out_le16(pplat->fpga, priv->addr + REG_RX_TX_STATUS, status);
+
+       if (!print_status)
+               return 1;
+
+       if (status & STATE_RX_PACKET_DROPPED)
+               printf("RX_PACKET_DROPPED, status %04x\n", status);
+
+       if (status & STATE_RX_DIST_ERR)
+               printf("RX_DIST_ERR\n");
+       if (status & STATE_RX_LENGTH_ERR)
+               printf("RX_LENGTH_ERR\n");
+       if (status & STATE_RX_FRAME_CTR_ERR)
+               printf("RX_FRAME_CTR_ERR\n");
+       if (status & STATE_RX_FCS_ERR)
+               printf("RX_FCS_ERR\n");
+
+       if (status & STATE_TX_ERR)
+               printf("TX_ERR\n");
+
+       return 1;
+}
+
+static const struct ioep_ops gdsys_ioep_ops = {
+       .enable_receive = gdsys_ioep_enable_receive,
+       .disable_receive = gdsys_ioep_disable_receive,
+       .send = gdsys_ioep_send,
+       .receive = gdsys_ioep_receive,
+       .set_address = gdsys_ioep_set_address,
+       .data_available = gdsys_ioep_data_available,
+       .reset_status = gdsys_ioep_reset_status,
+};
+
+int gdsys_ioep_probe(struct udevice *dev)
+{
+       struct gdsys_ioep_priv *priv = dev_get_priv(dev);
+
+       priv->addr = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+                                   "reg", -1);
+
+       return 0;
+}
+
+static const struct udevice_id gdsys_ioep_ids[] = {
+       { .compatible = "gdsys,io-endpoint" },
+       { }
+};
+
+U_BOOT_DRIVER(gdsys_ioep) = {
+       .name           = "gdsys_ioep",
+       .id             = UCLASS_IOEP,
+       .ops            = &gdsys_ioep_ops,
+       .flags          = DM_UC_FLAG_SEQ_ALIAS,
+       .of_match       = gdsys_ioep_ids,
+       .probe          = gdsys_ioep_probe,
+       .priv_auto_alloc_size = sizeof(struct gdsys_ioep_priv),
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index 35e478cc74..7bf8857799 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -45,6 +45,7 @@ enum uclass_id {
        UCLASS_IHS_AXI,         /* gdsys IHS AXI bus */
        UCLASS_IHS_FPGA,        /* gdsys IHS FPGAs */
        UCLASS_IHS_VIDEO_OUT,   /* gdsys IHS video output */
+       UCLASS_IOEP,            /* gdsys IHS IO endpoint */
        UCLASS_IRQ,             /* Interrupt controller */
        UCLASS_KEYBOARD,        /* Keyboard input device */
        UCLASS_LED,             /* Light-emitting diode (LED) */
diff --git a/include/gdsys_ioep.h b/include/gdsys_ioep.h
new file mode 100644
index 0000000000..6edf44d528
--- /dev/null
+++ b/include/gdsys_ioep.h
@@ -0,0 +1,196 @@
+/*
+ * (C) Copyright 2017
+ * Mario Six,  Guntermann & Drunck GmbH, mario....@gdsys.cc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _GDSYS_IOEP_CTRL_H_
+#define _GDSYS_IOEP_CTRL_H_
+
+/**
+ * The GDSYS IO endpoint is a IHS FPGA interface used by gdsys devices to send
+ * and receive special data packets via a proprietary protocol.
+ */
+
+/**
+ * struct io_generic_packet - header structure for GDSYS IOEP packets
+ *
+ * @target_address:    Target protocol address of the packet.
+ * @source_address:    Source protocol address of the packet.
+ * @packet_type:       Packet type.
+ * @bc:                        Block counter (filled in by FPGA).
+ * @packet_length:     Length of the packet's payload in 16 bit words.
+ */
+struct io_generic_packet {
+       u16 target_address;
+       u16 source_address;
+       u8 packet_type;
+       u8 bc;
+       u16 packet_length;
+} __attribute__((__packed__));
+
+/**
+ * struct ioep_ops - driver operations for GDSYS IOEP 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 ioep_ops {
+       /**
+        * enable_receive() - Enable the receive path of a given GDSYS IOEP
+        *                    instance
+        *
+        * @dev:        GDSYS IOEP instance to use.
+        * @return 0 if OK, -ve on error.
+        */
+       int (*enable_receive)(struct udevice *dev);
+
+       /**
+        * disable_receive() - Disable the receive path of a given GDSYS IOEP
+        *                     instance
+        *
+        * @dev:        GDSYS IOEP instance to use.
+        * @return 0 if OK, -ve on error.
+        */
+       int (*disable_receive)(struct udevice *dev);
+
+       /**
+        * send() - Send a data packet through the GDSYS IOEP instance
+        *
+        * @dev:        GDSYS IOEP instance to use.
+        * @header:     Header data for the packet to send (have to be filled
+        *              before calling this method).
+        * @data:       Payload data for the packet as an array of 16 bit
+        *              values.
+        * @return 0 if OK, -ve on error.
+        */
+       int (*send)(struct udevice *dev, struct io_generic_packet *header,
+                   u16 *data);
+
+       /**
+        * receive() - Receive a data packet through the GDSYS IOEP instance
+        *
+        * @dev:        GDSYS IOEP instance to use.
+        * @header:     A pointer to a header data structure, which is filled
+        *              with the data from the received packet.
+        * @data:       A pointer to a 16 bit array, which receives the payload
+        *              data from the received packet (must be large enough for
+        *              the data).
+        * @return 0 if OK, -ve on error.
+        */
+       int (*receive)(struct udevice *dev, struct io_generic_packet *header,
+                      u16 *data);
+
+       /**
+        * set_address() - Set the protocol address for this GDSYS IOEP
+        *                 instance
+        *
+        * @dev:        GDSYS IOEP instance to use.
+        * @addr:       The address to set (a 16 bit value)
+        * @return 0 if OK, -ve on error.
+        */
+       int (*set_address)(struct udevice *dev, u16 addr);
+
+       /**
+        * data_available() - Check if a packet can be read from thie GDSYS
+        *                    IOEP instance.
+        *
+        * The packet can be read via the receive() method.
+        *
+        * @dev:        GDSYS IOEP instance to use.
+        * @return true is a package can be read, false if not.
+        */
+       bool (*data_available)(struct udevice *dev);
+
+       /**
+        * reset_status() - Reset the error status of a GDSYS IOEP instance
+        *
+        * The error status *prior* to the status reset may optionally be
+        * printed.
+        *
+        * @dev:                GDSYS IOEP instance to use.
+        * @print_status:       Flag that, if set, makes the function print the
+        *                      error status prior to resetting.
+        * @return 0 if the instance was signalling an error prior to reset, 1
+        *  if no error was signaled.
+        */
+       int (*reset_status)(struct udevice *dev, bool print_status);
+};
+
+#define ioep_get_ops(dev)      ((struct ioep_ops *)(dev)->driver->ops)
+
+/**
+ * ioep_enable_receive() - Enable the receive path of a given GDSYS IOEP
+ *                        instance
+ *
+ * @dev:       GDSYS IOEP instance to use.
+ * @return 0 if OK, -ve on error.
+ */
+int ioep_enable_receive(struct udevice *dev);
+
+/**
+ * ioep_disable_receive() - Disable the receive path of a given GDSYS IOEP
+ *                         instance
+ *
+ * @dev:       GDSYS IOEP instance to use.
+ * @return 0 if OK, -ve on error.
+ */
+int ioep_disable_receive(struct udevice *dev);
+
+/**
+ * ioep_send() - Send a data packet through the GDSYS IOEP instance
+ *
+ * @dev:       GDSYS IOEP instance to use.
+ * @header:    Header data for the packet to send (have to be filled before
+ *             calling this method).
+ * @data:      Payload data for the packet as an array of 16 bit values.
+ * @return 0 if OK, -ve on error.
+ */
+int ioep_send(struct udevice *dev, struct io_generic_packet *header,
+             u16 *data);
+
+/**
+ * ioep_receive() - Receive a data packet through the GDSYS IOEP instance
+ *
+ * @dev:       GDSYS IOEP instance to use.
+ * @header:    A pointer to a header data structure, which is filled with the
+ *             data from the received packet.
+ * @data:      A pointer to a 16 bit array, which receives the payload data
+ *             from the received packet (must be large enough for the data).
+ * @return 0 if OK, -ve on error.
+ */
+int ioep_receive(struct udevice *dev, struct io_generic_packet *header,
+                u16 *data);
+
+/**
+ * ioep_set_address() - Set the protocol address for this GDSYS IOEP instance
+ *
+ * @dev:       GDSYS IOEP instance to use.
+ * @addr:      The address to set (a 16 bit value)
+ * @return 0 if OK, -ve on error.
+ */
+int ioep_set_address(struct udevice *dev, u16 addr);
+
+/**
+ * ioep_data_available() - Check if a packet can be read from thie GDSYS IOEP
+ *                        instance.
+ *
+ * @dev:       GDSYS IOEP instance to use.
+ * @return true is a package can be read, false if not.
+ */
+bool ioep_data_available(struct udevice *dev);
+
+/**
+ * ioep_reset_status() - Reset the error status of a GDSYS IOEP instance
+ *
+ * @dev:               GDSYS IOEP instance to use.
+ * @print_status:      Flag that, if set, makes the function print the
+ *                     error status prior to resetting.
+ * @return 0 if the instance was signalling an error prior to reset, 1
+ *  if no error was signaled.
+ */
+int ioep_reset_status(struct udevice *dev, bool print_status);
+
+#endif /* _GDSYS_IOEP_CTRL_H_ */
-- 
2.11.0

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to