[U-Boot] [PATCH 39/51] drivers: Add gdsys_ioep driver
Mario Six
mario.six at gdsys.cc
Fri Jul 14 12:55:25 UTC 2017
Add driver for the IHS IO endpoint on IHS FPGAs.
Signed-off-by: Mario Six <mario.six at 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.six at gdsys.cc
+ *
+ * based on the cmd_ioloop driver/command, which is
+ *
+ * (C) Copyright 2014
+ * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach at 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.six at 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
More information about the U-Boot
mailing list