[45a63ee] | 1 | /* |
---|
| 2 | * Copyright (c) 2014 embedded brains GmbH. All rights reserved. |
---|
| 3 | * |
---|
| 4 | * embedded brains GmbH |
---|
| 5 | * Dornierstr. 4 |
---|
| 6 | * 82178 Puchheim |
---|
| 7 | * Germany |
---|
| 8 | * <info@embedded-brains.de> |
---|
| 9 | * |
---|
| 10 | * The license and distribution terms for this file may be |
---|
| 11 | * found in the file LICENSE in this distribution or at |
---|
| 12 | * http://www.rtems.org/license/LICENSE. |
---|
| 13 | */ |
---|
| 14 | |
---|
| 15 | #include <bsp/cadence-i2c.h> |
---|
| 16 | #include <bsp/cadence-i2c-regs.h> |
---|
| 17 | |
---|
| 18 | #include <rtems/irq-extension.h> |
---|
| 19 | #include <rtems/score/assert.h> |
---|
| 20 | |
---|
| 21 | #include <dev/i2c/i2c.h> |
---|
| 22 | |
---|
| 23 | #define CADENCE_I2C_DIV_A_MAX 4 |
---|
| 24 | |
---|
| 25 | #define CADENCE_I2C_DIV_B_MAX 64 |
---|
| 26 | |
---|
| 27 | #define CADENCE_I2C_FIFO_DEPTH 16 |
---|
| 28 | |
---|
| 29 | #define CADENCE_I2C_DATA_IRQ_DEPTH (CADENCE_I2C_FIFO_DEPTH - 2) |
---|
| 30 | |
---|
| 31 | #define CADENCE_I2C_TRANSFER_SIZE_MAX 255 |
---|
| 32 | |
---|
| 33 | #define CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX (18 * CADENCE_I2C_DATA_IRQ_DEPTH) |
---|
| 34 | |
---|
| 35 | #define CADENCE_I2C_IRQ_ERROR \ |
---|
| 36 | (CADENCE_I2C_IXR_ARB_LOST \ |
---|
| 37 | | CADENCE_I2C_IXR_RX_UNF \ |
---|
| 38 | | CADENCE_I2C_IXR_TX_OVR \ |
---|
| 39 | | CADENCE_I2C_IXR_RX_OVR \ |
---|
| 40 | | CADENCE_I2C_IXR_NACK) |
---|
| 41 | |
---|
| 42 | #define CADENCE_I2C_IRQ_USED \ |
---|
| 43 | (CADENCE_I2C_IRQ_ERROR \ |
---|
| 44 | | CADENCE_I2C_IXR_DATA \ |
---|
| 45 | | CADENCE_I2C_IXR_COMP) |
---|
| 46 | |
---|
| 47 | typedef struct { |
---|
| 48 | i2c_bus base; |
---|
| 49 | volatile cadence_i2c *regs; |
---|
| 50 | i2c_msg *msgs; |
---|
| 51 | uint32_t msg_todo; |
---|
| 52 | uint32_t current_msg_todo; |
---|
| 53 | uint8_t *current_msg_byte; |
---|
| 54 | uint32_t current_todo; |
---|
| 55 | uint32_t irqstatus; |
---|
| 56 | bool read; |
---|
| 57 | bool hold; |
---|
| 58 | rtems_id task_id; |
---|
| 59 | uint32_t input_clock; |
---|
| 60 | rtems_vector_number irq; |
---|
| 61 | } cadence_i2c_bus; |
---|
| 62 | |
---|
| 63 | static void cadence_i2c_disable_interrupts(volatile cadence_i2c *regs) |
---|
| 64 | { |
---|
| 65 | regs->irqdisable = 0xffff; |
---|
| 66 | } |
---|
| 67 | |
---|
| 68 | static void cadence_i2c_clear_irq_status(volatile cadence_i2c *regs) |
---|
| 69 | { |
---|
| 70 | regs->irqstatus = regs->irqstatus; |
---|
| 71 | } |
---|
| 72 | |
---|
| 73 | static void cadence_i2c_reset(cadence_i2c_bus *bus) |
---|
| 74 | { |
---|
| 75 | volatile cadence_i2c *regs = bus->regs; |
---|
| 76 | uint32_t val; |
---|
| 77 | |
---|
| 78 | cadence_i2c_disable_interrupts(regs); |
---|
| 79 | |
---|
| 80 | val = regs->control; |
---|
| 81 | val &= ~CADENCE_I2C_CONTROL_HOLD; |
---|
| 82 | val |= CADENCE_I2C_CONTROL_ACKEN |
---|
| 83 | | CADENCE_I2C_CONTROL_MS |
---|
| 84 | | CADENCE_I2C_CONTROL_CLR_FIFO; |
---|
| 85 | regs->control = val; |
---|
| 86 | |
---|
| 87 | regs->transfer_size = 0; |
---|
| 88 | regs->status = regs->status; |
---|
| 89 | |
---|
| 90 | cadence_i2c_clear_irq_status(regs); |
---|
| 91 | } |
---|
| 92 | |
---|
| 93 | static uint32_t cadence_i2c_set_address_size( |
---|
| 94 | const i2c_msg *msg, |
---|
| 95 | uint32_t control |
---|
| 96 | ) |
---|
| 97 | { |
---|
| 98 | if ((msg->flags & I2C_M_TEN) == 0) { |
---|
| 99 | control |= CADENCE_I2C_CONTROL_NEA; |
---|
| 100 | } else { |
---|
| 101 | control &= ~CADENCE_I2C_CONTROL_NEA; |
---|
| 102 | } |
---|
| 103 | |
---|
| 104 | return control; |
---|
| 105 | } |
---|
| 106 | |
---|
| 107 | static void cadence_i2c_setup_read_transfer( |
---|
| 108 | cadence_i2c_bus *bus, |
---|
| 109 | volatile cadence_i2c *regs, |
---|
| 110 | uint32_t control |
---|
| 111 | ) |
---|
| 112 | { |
---|
| 113 | control |= CADENCE_I2C_CONTROL_RW; |
---|
| 114 | regs->control = control; |
---|
| 115 | |
---|
| 116 | if (bus->current_todo <= CADENCE_I2C_TRANSFER_SIZE_MAX) { |
---|
| 117 | regs->transfer_size = bus->current_todo; |
---|
| 118 | } else { |
---|
| 119 | regs->transfer_size = CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX; |
---|
| 120 | } |
---|
| 121 | } |
---|
| 122 | |
---|
| 123 | static void cadence_i2c_next_byte(cadence_i2c_bus *bus) |
---|
| 124 | { |
---|
| 125 | --bus->current_msg_todo; |
---|
| 126 | ++bus->current_msg_byte; |
---|
| 127 | |
---|
| 128 | if (bus->current_msg_todo == 0) { |
---|
| 129 | i2c_msg *msg; |
---|
| 130 | |
---|
| 131 | ++bus->msgs; |
---|
| 132 | --bus->msg_todo; |
---|
| 133 | |
---|
| 134 | msg = &bus->msgs[0]; |
---|
| 135 | |
---|
| 136 | bus->current_msg_todo = msg->len; |
---|
| 137 | bus->current_msg_byte = msg->buf; |
---|
| 138 | } |
---|
| 139 | } |
---|
| 140 | |
---|
| 141 | static void cadence_i2c_write_to_fifo( |
---|
| 142 | cadence_i2c_bus *bus, |
---|
| 143 | volatile cadence_i2c *regs |
---|
| 144 | ) |
---|
| 145 | { |
---|
| 146 | uint32_t space_available; |
---|
| 147 | uint32_t todo_now; |
---|
| 148 | uint32_t i; |
---|
| 149 | |
---|
| 150 | space_available = CADENCE_I2C_FIFO_DEPTH - regs->transfer_size; |
---|
| 151 | |
---|
| 152 | if (bus->current_todo > space_available) { |
---|
| 153 | todo_now = space_available; |
---|
| 154 | } else { |
---|
| 155 | todo_now = bus->current_todo; |
---|
| 156 | } |
---|
| 157 | |
---|
| 158 | bus->current_todo -= todo_now; |
---|
| 159 | |
---|
| 160 | for (i = 0; i < todo_now; ++i) { |
---|
| 161 | regs->data = *bus->current_msg_byte; |
---|
| 162 | |
---|
| 163 | cadence_i2c_next_byte(bus); |
---|
| 164 | } |
---|
| 165 | } |
---|
| 166 | |
---|
| 167 | static void cadence_i2c_setup_write_transfer( |
---|
| 168 | cadence_i2c_bus *bus, |
---|
| 169 | volatile cadence_i2c *regs, |
---|
| 170 | uint32_t control |
---|
| 171 | ) |
---|
| 172 | { |
---|
| 173 | control &= ~CADENCE_I2C_CONTROL_RW; |
---|
| 174 | regs->control = control; |
---|
| 175 | |
---|
| 176 | cadence_i2c_write_to_fifo(bus, regs); |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | static void cadence_i2c_setup_transfer( |
---|
| 180 | cadence_i2c_bus *bus, |
---|
| 181 | volatile cadence_i2c *regs |
---|
| 182 | ) |
---|
| 183 | { |
---|
| 184 | const i2c_msg *msgs = bus->msgs; |
---|
| 185 | uint32_t msg_todo = bus->msg_todo; |
---|
| 186 | uint32_t i; |
---|
| 187 | uint32_t control; |
---|
| 188 | |
---|
| 189 | bus->current_todo = msgs[0].len; |
---|
| 190 | for (i = 1; i < msg_todo && (msgs[i].flags & I2C_M_NOSTART) != 0; ++i) { |
---|
| 191 | bus->current_todo += msgs[i].len; |
---|
| 192 | } |
---|
| 193 | |
---|
| 194 | regs = bus->regs; |
---|
| 195 | |
---|
| 196 | control = regs->control; |
---|
| 197 | control |= CADENCE_I2C_CONTROL_CLR_FIFO; |
---|
| 198 | |
---|
| 199 | bus->hold = i < msg_todo; |
---|
| 200 | |
---|
| 201 | if (bus->hold || bus->current_todo > CADENCE_I2C_FIFO_DEPTH) { |
---|
| 202 | control |= CADENCE_I2C_CONTROL_HOLD; |
---|
| 203 | } else { |
---|
| 204 | control &= ~CADENCE_I2C_CONTROL_HOLD; |
---|
| 205 | } |
---|
| 206 | |
---|
| 207 | control = cadence_i2c_set_address_size(msgs, control); |
---|
| 208 | |
---|
| 209 | bus->read = (msgs->flags & I2C_M_RD) != 0; |
---|
| 210 | if (bus->read) { |
---|
| 211 | cadence_i2c_setup_read_transfer(bus, regs, control); |
---|
| 212 | } else { |
---|
| 213 | cadence_i2c_setup_write_transfer(bus, regs, control); |
---|
| 214 | } |
---|
| 215 | |
---|
| 216 | cadence_i2c_clear_irq_status(regs); |
---|
| 217 | |
---|
| 218 | regs->address = CADENCE_I2C_ADDRESS(msgs->addr); |
---|
| 219 | } |
---|
| 220 | |
---|
| 221 | static void cadence_i2c_continue_read_transfer( |
---|
| 222 | cadence_i2c_bus *bus, |
---|
| 223 | volatile cadence_i2c *regs |
---|
| 224 | ) |
---|
| 225 | { |
---|
| 226 | uint32_t i; |
---|
| 227 | |
---|
| 228 | bus->current_todo -= CADENCE_I2C_DATA_IRQ_DEPTH; |
---|
| 229 | |
---|
| 230 | /* |
---|
| 231 | * This works since CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX is an integral |
---|
| 232 | * multiple of CADENCE_I2C_DATA_IRQ_DEPTH. |
---|
| 233 | * |
---|
| 234 | * FIXME: Tests with a 1024 byte EEPROM show that this doesn't work. Needs |
---|
| 235 | * further investigations with an I2C analyser or an oscilloscope. |
---|
| 236 | */ |
---|
| 237 | if (regs->transfer_size == 0) { |
---|
| 238 | if (bus->current_todo <= CADENCE_I2C_TRANSFER_SIZE_MAX) { |
---|
| 239 | regs->transfer_size = bus->current_todo; |
---|
| 240 | } else { |
---|
| 241 | regs->transfer_size = CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX; |
---|
| 242 | } |
---|
| 243 | } |
---|
| 244 | |
---|
| 245 | for (i = 0; i < CADENCE_I2C_DATA_IRQ_DEPTH; ++i) { |
---|
| 246 | *bus->current_msg_byte = (uint8_t) regs->data; |
---|
| 247 | |
---|
| 248 | cadence_i2c_next_byte(bus); |
---|
| 249 | } |
---|
| 250 | |
---|
| 251 | if (!bus->hold && bus->current_todo <= CADENCE_I2C_FIFO_DEPTH) { |
---|
| 252 | regs->control &= ~CADENCE_I2C_CONTROL_HOLD; |
---|
| 253 | } |
---|
| 254 | } |
---|
| 255 | |
---|
| 256 | static void cadence_i2c_interrupt(void *arg) |
---|
| 257 | { |
---|
| 258 | cadence_i2c_bus *bus = arg; |
---|
| 259 | volatile cadence_i2c *regs = bus->regs; |
---|
| 260 | uint32_t irqstatus = regs->irqstatus; |
---|
| 261 | bool done = false; |
---|
| 262 | |
---|
| 263 | /* Clear interrupts */ |
---|
| 264 | regs->irqstatus = irqstatus; |
---|
| 265 | |
---|
| 266 | if ((irqstatus & (CADENCE_I2C_IXR_ARB_LOST | CADENCE_I2C_IXR_NACK)) != 0) { |
---|
| 267 | done = true; |
---|
| 268 | } |
---|
| 269 | |
---|
| 270 | if ( |
---|
| 271 | (irqstatus & CADENCE_I2C_IXR_DATA) != 0 |
---|
| 272 | && bus->read |
---|
| 273 | && bus->current_todo >= CADENCE_I2C_DATA_IRQ_DEPTH |
---|
| 274 | ) { |
---|
| 275 | cadence_i2c_continue_read_transfer(bus, regs); |
---|
| 276 | } |
---|
| 277 | |
---|
| 278 | if ((irqstatus & CADENCE_I2C_IXR_COMP) != 0) { |
---|
| 279 | if (bus->read) { |
---|
| 280 | uint32_t todo_now = bus->current_todo; |
---|
| 281 | uint32_t i; |
---|
| 282 | |
---|
| 283 | for (i = 0; i < todo_now; ++i) { |
---|
| 284 | *bus->current_msg_byte = (uint8_t) regs->data; |
---|
| 285 | |
---|
| 286 | cadence_i2c_next_byte(bus); |
---|
| 287 | } |
---|
| 288 | |
---|
| 289 | bus->current_todo = 0; |
---|
| 290 | |
---|
| 291 | done = true; |
---|
| 292 | } else { |
---|
| 293 | if (bus->current_todo > 0) { |
---|
| 294 | cadence_i2c_write_to_fifo(bus, regs); |
---|
| 295 | } else { |
---|
| 296 | done = true; |
---|
| 297 | } |
---|
| 298 | |
---|
| 299 | if (!bus->hold && bus->current_todo == 0) { |
---|
| 300 | regs->control &= ~CADENCE_I2C_CONTROL_HOLD; |
---|
| 301 | } |
---|
| 302 | } |
---|
| 303 | } |
---|
| 304 | |
---|
| 305 | if (done) { |
---|
| 306 | uint32_t err = irqstatus & CADENCE_I2C_IRQ_ERROR; |
---|
| 307 | |
---|
| 308 | if (bus->msg_todo == 0 || err != 0) { |
---|
| 309 | rtems_status_code sc; |
---|
| 310 | |
---|
| 311 | cadence_i2c_disable_interrupts(regs); |
---|
| 312 | |
---|
| 313 | bus->irqstatus = err; |
---|
| 314 | |
---|
| 315 | sc = rtems_event_transient_send(bus->task_id); |
---|
| 316 | _Assert(sc == RTEMS_SUCCESSFUL); |
---|
| 317 | (void) sc; |
---|
| 318 | } else { |
---|
| 319 | cadence_i2c_setup_transfer(bus, regs); |
---|
| 320 | } |
---|
| 321 | } |
---|
| 322 | } |
---|
| 323 | |
---|
| 324 | static int cadence_i2c_transfer( |
---|
| 325 | i2c_bus *base, |
---|
| 326 | i2c_msg *msgs, |
---|
| 327 | uint32_t msg_count |
---|
| 328 | ) |
---|
| 329 | { |
---|
| 330 | cadence_i2c_bus *bus = (cadence_i2c_bus *) base; |
---|
| 331 | volatile cadence_i2c *regs; |
---|
| 332 | rtems_status_code sc; |
---|
| 333 | uint32_t i; |
---|
| 334 | |
---|
| 335 | _Assert(msg_count > 0); |
---|
| 336 | |
---|
| 337 | for (i = 0; i < msg_count; ++i) { |
---|
| 338 | /* FIXME: Not sure if we can support this. */ |
---|
| 339 | if ((msgs[i].flags & I2C_M_RECV_LEN) != 0) { |
---|
| 340 | return -EINVAL; |
---|
| 341 | } |
---|
| 342 | } |
---|
| 343 | |
---|
| 344 | bus->msgs = &msgs[0]; |
---|
| 345 | bus->msg_todo = msg_count; |
---|
| 346 | bus->current_msg_todo = msgs[0].len; |
---|
| 347 | bus->current_msg_byte = msgs[0].buf; |
---|
| 348 | bus->task_id = rtems_task_self(); |
---|
| 349 | |
---|
| 350 | regs = bus->regs; |
---|
| 351 | cadence_i2c_setup_transfer(bus, regs); |
---|
| 352 | regs->irqenable = CADENCE_I2C_IRQ_USED; |
---|
| 353 | |
---|
| 354 | sc = rtems_event_transient_receive(RTEMS_WAIT, bus->base.timeout); |
---|
| 355 | if (sc != RTEMS_SUCCESSFUL) { |
---|
| 356 | cadence_i2c_reset(bus); |
---|
| 357 | rtems_event_transient_clear(); |
---|
| 358 | |
---|
| 359 | return -ETIMEDOUT; |
---|
| 360 | } |
---|
| 361 | |
---|
| 362 | return bus->irqstatus == 0 ? 0 : -EIO; |
---|
| 363 | } |
---|
| 364 | |
---|
| 365 | static int cadence_i2c_set_clock(i2c_bus *base, unsigned long clock) |
---|
| 366 | { |
---|
| 367 | cadence_i2c_bus *bus = (cadence_i2c_bus *) base; |
---|
| 368 | volatile cadence_i2c *regs = bus->regs; |
---|
| 369 | uint32_t error = 0xffffffff; |
---|
| 370 | uint32_t best_div_a = CADENCE_I2C_DIV_A_MAX - 1; |
---|
| 371 | uint32_t best_div_b = CADENCE_I2C_DIV_B_MAX - 1; |
---|
| 372 | uint32_t div = bus->input_clock / (22 * clock); |
---|
| 373 | uint32_t div_a; |
---|
| 374 | uint32_t control; |
---|
| 375 | |
---|
| 376 | if (div <= 0 || div > (CADENCE_I2C_DIV_A_MAX * CADENCE_I2C_DIV_B_MAX)) { |
---|
| 377 | return -EIO; |
---|
| 378 | } |
---|
| 379 | |
---|
| 380 | for (div_a = 0; div_a < CADENCE_I2C_DIV_A_MAX; ++div_a) { |
---|
| 381 | uint32_t a = 22 * clock * (div_a + 1); |
---|
| 382 | uint32_t b = (bus->input_clock + a - 1) / a; |
---|
| 383 | |
---|
| 384 | if (b > 0 && b <= CADENCE_I2C_DIV_B_MAX) { |
---|
| 385 | uint32_t actual_clock = bus->input_clock / (22 * (div_a + 1) * b); |
---|
| 386 | uint32_t e = clock < actual_clock ? |
---|
| 387 | actual_clock - clock : clock - actual_clock; |
---|
| 388 | |
---|
| 389 | /* |
---|
| 390 | * Favour greater div_a values according to UG585, Zynq-7000 AP SoC |
---|
| 391 | * Technical Reference Manual, Table 20-1: Calculated Values for Standard |
---|
| 392 | * and High Speed SCL Clock Values". |
---|
| 393 | */ |
---|
| 394 | if (e <= error && actual_clock <= clock) { |
---|
| 395 | error = e; |
---|
| 396 | best_div_a = div_a; |
---|
| 397 | best_div_b = b - 1; |
---|
| 398 | } |
---|
| 399 | } |
---|
| 400 | } |
---|
| 401 | |
---|
| 402 | control = regs->control; |
---|
| 403 | control = CADENCE_I2C_CONTROL_DIV_A_SET(control, best_div_a); |
---|
| 404 | control = CADENCE_I2C_CONTROL_DIV_B_SET(control, best_div_b); |
---|
| 405 | regs->control = control; |
---|
| 406 | |
---|
| 407 | return 0; |
---|
| 408 | } |
---|
| 409 | |
---|
| 410 | static void cadence_i2c_destroy(i2c_bus *base) |
---|
| 411 | { |
---|
| 412 | cadence_i2c_bus *bus = (cadence_i2c_bus *) base; |
---|
| 413 | rtems_status_code sc; |
---|
| 414 | |
---|
| 415 | sc = rtems_interrupt_handler_remove(bus->irq, cadence_i2c_interrupt, bus); |
---|
| 416 | _Assert(sc == RTEMS_SUCCESSFUL); |
---|
| 417 | (void) sc; |
---|
| 418 | |
---|
| 419 | i2c_bus_destroy_and_free(&bus->base); |
---|
| 420 | } |
---|
| 421 | |
---|
| 422 | int i2c_bus_register_cadence( |
---|
| 423 | const char *bus_path, |
---|
| 424 | uintptr_t register_base, |
---|
| 425 | uint32_t input_clock, |
---|
| 426 | rtems_vector_number irq |
---|
| 427 | ) |
---|
| 428 | { |
---|
| 429 | cadence_i2c_bus *bus; |
---|
| 430 | rtems_status_code sc; |
---|
| 431 | int err; |
---|
| 432 | |
---|
| 433 | bus = (cadence_i2c_bus *) i2c_bus_alloc_and_init(sizeof(*bus)); |
---|
| 434 | if (bus == NULL) { |
---|
| 435 | return -1; |
---|
| 436 | } |
---|
| 437 | |
---|
| 438 | bus->regs = (volatile cadence_i2c *) register_base; |
---|
| 439 | bus->input_clock = input_clock; |
---|
| 440 | bus->irq = irq; |
---|
| 441 | |
---|
| 442 | cadence_i2c_reset(bus); |
---|
| 443 | |
---|
| 444 | err = cadence_i2c_set_clock(&bus->base, I2C_BUS_CLOCK_DEFAULT); |
---|
| 445 | if (err != 0) { |
---|
| 446 | (*bus->base.destroy)(&bus->base); |
---|
| 447 | |
---|
| 448 | rtems_set_errno_and_return_minus_one(-err); |
---|
| 449 | } |
---|
| 450 | |
---|
| 451 | sc = rtems_interrupt_handler_install( |
---|
| 452 | irq, |
---|
| 453 | "Cadence I2C", |
---|
| 454 | RTEMS_INTERRUPT_UNIQUE, |
---|
| 455 | cadence_i2c_interrupt, |
---|
| 456 | bus |
---|
| 457 | ); |
---|
| 458 | if (sc != RTEMS_SUCCESSFUL) { |
---|
| 459 | (*bus->base.destroy)(&bus->base); |
---|
| 460 | |
---|
| 461 | rtems_set_errno_and_return_minus_one(EIO); |
---|
| 462 | } |
---|
| 463 | |
---|
| 464 | bus->base.transfer = cadence_i2c_transfer; |
---|
| 465 | bus->base.set_clock = cadence_i2c_set_clock; |
---|
| 466 | bus->base.destroy = cadence_i2c_destroy; |
---|
| 467 | |
---|
| 468 | return i2c_bus_register(&bus->base, bus_path); |
---|
| 469 | } |
---|