
Add driver for the IHS IO endpoint on IHS FPGAs.
Signed-off-by: Mario Six mario.six@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@gdsys.cc + * + * based on the cmd_ioloop driver/command, which is + * + * (C) Copyright 2014 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@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@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_ */