1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * (C) Copyright 2017 4 * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc 5 * 6 * based on the cmd_ioloop driver/command, which is 7 * 8 * (C) Copyright 2014 9 * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc 10 * 11 * SPDX-License-Identifier: GPL-2.0+ 12 */ 13 14#include <common.h> 15#include <dm.h> 16#include <log.h> 17#include <misc.h> 18#include <regmap.h> 19 20#include "gdsys_ioep.h" 21 22/** 23 * struct gdsys_ioep_priv - Private data structure for IOEP devices 24 * @map: Register map to be used for the device 25 * @state: Flag to keep the current status of the RX control (enabled/disabled) 26 */ 27struct gdsys_ioep_priv { 28 struct regmap *map; 29 bool state; 30}; 31 32/** 33 * enum last_spec - Convenience enum for read data sanity check 34 * @READ_DATA_IS_LAST: The data to be read should be the final data of the 35 * current packet 36 * @READ_DATA_IS_NOT_LAST: The data to be read should not be the final data of 37 * the current packet 38 */ 39enum last_spec { 40 READ_DATA_IS_LAST, 41 READ_DATA_IS_NOT_LAST, 42}; 43 44static int gdsys_ioep_set_receive(struct udevice *dev, bool val) 45{ 46 struct gdsys_ioep_priv *priv = dev_get_priv(dev); 47 u16 state; 48 49 priv->state = !priv->state; 50 51 if (val) 52 state = CTRL_PROC_RECEIVE_ENABLE; 53 else 54 state = ~CTRL_PROC_RECEIVE_ENABLE; 55 56 gdsys_ioep_set(priv->map, tx_control, state); 57 58 if (val) { 59 /* Set device address to dummy 1 */ 60 gdsys_ioep_set(priv->map, device_address, 1); 61 } 62 63 return !priv->state; 64} 65 66static int gdsys_ioep_send(struct udevice *dev, int offset, 67 const void *buf, int size) 68{ 69 struct gdsys_ioep_priv *priv = dev_get_priv(dev); 70 int k; 71 u16 *p = (u16 *)buf; 72 73 for (k = 0; k < size; ++k) 74 gdsys_ioep_set(priv->map, transmit_data, *(p++)); 75 76 gdsys_ioep_set(priv->map, tx_control, CTRL_PROC_RECEIVE_ENABLE | 77 CTRL_FLUSH_TRANSMIT_BUFFER); 78 79 return 0; 80} 81 82/** 83 * receive_byte_buffer() - Read data from a IOEP device 84 * @dev: The IOEP device to read data from 85 * @len: The length of the data to read 86 * @buffer: The buffer to read the data into 87 * @last_spec: Flag to indicate if the data to be read in this call should be 88 * the final data of the current packet (i.e. it should be empty 89 * after this read) 90 * 91 * Return: 0 if OK, -ve on error 92 */ 93static int receive_byte_buffer(struct udevice *dev, uint len, 94 u16 *buffer, enum last_spec last_spec) 95{ 96 struct gdsys_ioep_priv *priv = dev_get_priv(dev); 97 int k; 98 int ret = -EIO; 99 100 for (k = 0; k < len; ++k) { 101 u16 rx_tx_status; 102 103 gdsys_ioep_get(priv->map, receive_data, buffer++); 104 105 gdsys_ioep_get(priv->map, rx_tx_status, &rx_tx_status); 106 /* 107 * Sanity check: If the data read should have been the last, 108 * but wasn't, something is wrong 109 */ 110 if (k == (len - 1) && (last_spec == READ_DATA_IS_NOT_LAST || 111 rx_tx_status & STATE_RX_DATA_LAST)) 112 ret = 0; 113 } 114 115 if (ret) 116 debug("%s: Error while receiving bufer (err = %d)\n", 117 dev->name, ret); 118 119 return ret; 120} 121 122static int gdsys_ioep_receive(struct udevice *dev, int offset, void *buf, 123 int size) 124{ 125 int ret; 126 struct io_generic_packet header; 127 u16 *p = (u16 *)buf; 128 const int header_words = sizeof(struct io_generic_packet) / sizeof(u16); 129 uint len; 130 131 /* Read the packet header */ 132 ret = receive_byte_buffer(dev, header_words, p, READ_DATA_IS_NOT_LAST); 133 if (ret) { 134 debug("%s: Failed to read header data (err = %d)\n", 135 dev->name, ret); 136 return ret; 137 } 138 139 memcpy(&header, p, header_words * sizeof(u16)); 140 p += header_words; 141 142 /* Get payload data length */ 143 len = (header.packet_length + 1) / sizeof(u16); 144 145 /* Read the packet payload */ 146 ret = receive_byte_buffer(dev, len, p, READ_DATA_IS_LAST); 147 if (ret) { 148 debug("%s: Failed to read payload data (err = %d)\n", 149 dev->name, ret); 150 return ret; 151 } 152 153 return 0; 154} 155 156static int gdsys_ioep_get_and_reset_status(struct udevice *dev, int msgid, 157 void *tx_msg, int tx_size, 158 void *rx_msg, int rx_size) 159{ 160 struct gdsys_ioep_priv *priv = dev_get_priv(dev); 161 const u16 mask = STATE_RX_DIST_ERR | STATE_RX_LENGTH_ERR | 162 STATE_RX_FRAME_CTR_ERR | STATE_RX_FCS_ERR | 163 STATE_RX_PACKET_DROPPED | STATE_TX_ERR; 164 u16 *status = rx_msg; 165 166 gdsys_ioep_get(priv->map, rx_tx_status, status); 167 168 gdsys_ioep_set(priv->map, rx_tx_status, *status); 169 170 return (*status & mask) ? 1 : 0; 171} 172 173static const struct misc_ops gdsys_ioep_ops = { 174 .set_enabled = gdsys_ioep_set_receive, 175 .write = gdsys_ioep_send, 176 .read = gdsys_ioep_receive, 177 .call = gdsys_ioep_get_and_reset_status, 178}; 179 180static int gdsys_ioep_probe(struct udevice *dev) 181{ 182 struct gdsys_ioep_priv *priv = dev_get_priv(dev); 183 int ret; 184 185 ret = regmap_init_mem(dev_ofnode(dev), &priv->map); 186 if (ret) { 187 debug("%s: Could not initialize regmap (err = %d)", 188 dev->name, ret); 189 return ret; 190 } 191 192 priv->state = false; 193 194 return 0; 195} 196 197static const struct udevice_id gdsys_ioep_ids[] = { 198 { .compatible = "gdsys,io-endpoint" }, 199 { } 200}; 201 202U_BOOT_DRIVER(gdsys_ioep) = { 203 .name = "gdsys_ioep", 204 .id = UCLASS_MISC, 205 .ops = &gdsys_ioep_ops, 206 .flags = DM_UC_FLAG_SEQ_ALIAS, 207 .of_match = gdsys_ioep_ids, 208 .probe = gdsys_ioep_probe, 209 .priv_auto = sizeof(struct gdsys_ioep_priv), 210}; 211