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 }; 294