Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:04

0001 /*
0002  * SPDX-License-Identifier: BSD-2-Clause
0003  *
0004  * Copyright (C) 2014 embedded brains GmbH & Co. KG
0005  *
0006  * Redistribution and use in source and binary forms, with or without
0007  * modification, are permitted provided that the following conditions
0008  * are met:
0009  * 1. Redistributions of source code must retain the above copyright
0010  *    notice, this list of conditions and the following disclaimer.
0011  * 2. Redistributions in binary form must reproduce the above copyright
0012  *    notice, this list of conditions and the following disclaimer in the
0013  *    documentation and/or other materials provided with the distribution.
0014  *
0015  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0016  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0017  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0018  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0019  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0020  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0021  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0022  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0023  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0024  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0025  * POSSIBILITY OF SUCH DAMAGE.
0026  */
0027 
0028 #include <dev/i2c/cadence-i2c.h>
0029 #include <dev/i2c/cadence-i2c-regs.h>
0030 
0031 #include <rtems/score/assert.h>
0032 
0033 #include <dev/i2c/i2c.h>
0034 
0035 #define CADENCE_I2C_DIV_A_MAX 4
0036 
0037 #define CADENCE_I2C_DIV_B_MAX 64
0038 
0039 #define CADENCE_I2C_FIFO_DEPTH 16
0040 
0041 #define CADENCE_I2C_DATA_IRQ_DEPTH (CADENCE_I2C_FIFO_DEPTH - 2)
0042 
0043 #define CADENCE_I2C_TRANSFER_SIZE_MAX 255
0044 
0045 #define CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX (18 * CADENCE_I2C_DATA_IRQ_DEPTH)
0046 
0047 #define CADENCE_I2C_IRQ_ERROR \
0048   (CADENCE_I2C_IXR_ARB_LOST \
0049     | CADENCE_I2C_IXR_RX_UNF \
0050     | CADENCE_I2C_IXR_TX_OVR \
0051     | CADENCE_I2C_IXR_RX_OVR \
0052     | CADENCE_I2C_IXR_NACK)
0053 
0054 #define CADENCE_I2C_IRQ_USED \
0055   (CADENCE_I2C_IRQ_ERROR \
0056     | CADENCE_I2C_IXR_DATA \
0057     | CADENCE_I2C_IXR_COMP)
0058 
0059 typedef struct {
0060   i2c_bus base;
0061   volatile cadence_i2c *regs;
0062   i2c_msg *msgs;
0063   uint32_t msg_todo;
0064   uint32_t current_msg_todo;
0065   uint8_t *current_msg_byte;
0066   uint32_t current_todo;
0067   uint32_t irqstatus;
0068   bool read;
0069   bool hold;
0070   rtems_id task_id;
0071   uint32_t input_clock;
0072   rtems_vector_number irq;
0073 } cadence_i2c_bus;
0074 
0075 static void cadence_i2c_disable_interrupts(volatile cadence_i2c *regs)
0076 {
0077   regs->irqdisable = 0xffff;
0078 }
0079 
0080 static void cadence_i2c_clear_irq_status(volatile cadence_i2c *regs)
0081 {
0082   regs->irqstatus = regs->irqstatus;
0083 }
0084 
0085 static void cadence_i2c_reset(cadence_i2c_bus *bus)
0086 {
0087   volatile cadence_i2c *regs = bus->regs;
0088   uint32_t val;
0089 
0090   cadence_i2c_disable_interrupts(regs);
0091 
0092   val = regs->control;
0093   val &= ~CADENCE_I2C_CONTROL_HOLD;
0094   val |= CADENCE_I2C_CONTROL_ACKEN
0095     | CADENCE_I2C_CONTROL_MS
0096     | CADENCE_I2C_CONTROL_CLR_FIFO;
0097   regs->control = val;
0098 
0099   regs->transfer_size = 0;
0100   regs->status = regs->status;
0101 
0102   cadence_i2c_clear_irq_status(regs);
0103 }
0104 
0105 static uint32_t cadence_i2c_set_address_size(
0106   const i2c_msg *msg,
0107   uint32_t control
0108 )
0109 {
0110   if ((msg->flags & I2C_M_TEN) == 0) {
0111     control |= CADENCE_I2C_CONTROL_NEA;
0112   } else {
0113     control &= ~CADENCE_I2C_CONTROL_NEA;
0114   }
0115 
0116   return control;
0117 }
0118 
0119 static void cadence_i2c_setup_read_transfer(
0120   cadence_i2c_bus *bus,
0121   volatile cadence_i2c *regs,
0122   uint32_t control
0123 )
0124 {
0125   control |= CADENCE_I2C_CONTROL_RW;
0126   regs->control = control;
0127 
0128   if (bus->current_todo <= CADENCE_I2C_TRANSFER_SIZE_MAX) {
0129     regs->transfer_size = bus->current_todo;
0130   } else {
0131     regs->transfer_size = CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX;
0132   }
0133 }
0134 
0135 static void cadence_i2c_next_byte(cadence_i2c_bus *bus)
0136 {
0137   --bus->current_msg_todo;
0138   ++bus->current_msg_byte;
0139 
0140   if (bus->current_msg_todo == 0) {
0141     i2c_msg *msg;
0142 
0143     ++bus->msgs;
0144     --bus->msg_todo;
0145 
0146     msg = &bus->msgs[0];
0147 
0148     bus->current_msg_todo = msg->len;
0149     bus->current_msg_byte = msg->buf;
0150   }
0151 }
0152 
0153 static void cadence_i2c_write_to_fifo(
0154   cadence_i2c_bus *bus,
0155   volatile cadence_i2c *regs
0156 )
0157 {
0158   uint32_t space_available;
0159   uint32_t todo_now;
0160   uint32_t i;
0161 
0162   space_available = CADENCE_I2C_FIFO_DEPTH - regs->transfer_size;
0163 
0164   if (bus->current_todo > space_available) {
0165     todo_now = space_available;
0166   } else {
0167     todo_now = bus->current_todo;
0168   }
0169 
0170   bus->current_todo -= todo_now;
0171 
0172   for (i = 0; i < todo_now; ++i) {
0173     regs->data = *bus->current_msg_byte;
0174 
0175     cadence_i2c_next_byte(bus);
0176   }
0177 }
0178 
0179 static void cadence_i2c_setup_write_transfer(
0180   cadence_i2c_bus *bus,
0181   volatile cadence_i2c *regs,
0182   uint32_t control
0183 )
0184 {
0185   control &= ~CADENCE_I2C_CONTROL_RW;
0186   regs->control = control;
0187 
0188   cadence_i2c_write_to_fifo(bus, regs);
0189 }
0190 
0191 static void cadence_i2c_setup_transfer(
0192   cadence_i2c_bus *bus,
0193   volatile cadence_i2c *regs
0194 )
0195 {
0196   const i2c_msg *msgs = bus->msgs;
0197   uint32_t msg_todo = bus->msg_todo;
0198   uint32_t i;
0199   uint32_t control;
0200 
0201   bus->current_todo = msgs[0].len;
0202   for (i = 1; i < msg_todo && (msgs[i].flags & I2C_M_NOSTART) != 0; ++i) {
0203     bus->current_todo += msgs[i].len;
0204   }
0205 
0206   regs = bus->regs;
0207 
0208   control = regs->control;
0209   control |= CADENCE_I2C_CONTROL_CLR_FIFO;
0210 
0211   bus->hold = i < msg_todo;
0212 
0213   if (bus->hold || bus->current_todo > CADENCE_I2C_FIFO_DEPTH) {
0214     control |= CADENCE_I2C_CONTROL_HOLD;
0215   } else {
0216     control &= ~CADENCE_I2C_CONTROL_HOLD;
0217   }
0218 
0219   control = cadence_i2c_set_address_size(msgs, control);
0220 
0221   bus->read = (msgs->flags & I2C_M_RD) != 0;
0222   if (bus->read) {
0223     cadence_i2c_setup_read_transfer(bus, regs, control);
0224   } else {
0225     cadence_i2c_setup_write_transfer(bus, regs, control);
0226   }
0227 
0228   cadence_i2c_clear_irq_status(regs);
0229 
0230   regs->address = CADENCE_I2C_ADDRESS(msgs->addr);
0231 }
0232 
0233 static void cadence_i2c_continue_read_transfer(
0234   cadence_i2c_bus *bus,
0235   volatile cadence_i2c *regs
0236 )
0237 {
0238   uint32_t i;
0239 
0240   bus->current_todo -= CADENCE_I2C_DATA_IRQ_DEPTH;
0241 
0242   /*
0243    * This works since CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX is an integral
0244    * multiple of CADENCE_I2C_DATA_IRQ_DEPTH.
0245    *
0246    * FIXME: Tests with a 1024 byte EEPROM show that this doesn't work.  Needs
0247    * further investigations with an I2C analyser or an oscilloscope.
0248    */
0249   if (regs->transfer_size == 0) {
0250     if (bus->current_todo <= CADENCE_I2C_TRANSFER_SIZE_MAX) {
0251       regs->transfer_size = bus->current_todo;
0252     } else {
0253       regs->transfer_size = CADENCE_I2C_TRANSFER_SIZE_ONCE_MAX;
0254     }
0255   }
0256 
0257   for (i = 0; i < CADENCE_I2C_DATA_IRQ_DEPTH; ++i) {
0258     *bus->current_msg_byte = (uint8_t) regs->data;
0259 
0260     cadence_i2c_next_byte(bus);
0261   }
0262 
0263   if (!bus->hold && bus->current_todo <= CADENCE_I2C_FIFO_DEPTH) {
0264     regs->control &= ~CADENCE_I2C_CONTROL_HOLD;
0265   }
0266 }
0267 
0268 static void cadence_i2c_interrupt(void *arg)
0269 {
0270   cadence_i2c_bus *bus = arg;
0271   volatile cadence_i2c *regs = bus->regs;
0272   uint32_t irqstatus = regs->irqstatus;
0273   bool done = false;
0274 
0275   /* Clear interrupts */
0276   regs->irqstatus = irqstatus;
0277 
0278   if ((irqstatus & (CADENCE_I2C_IXR_ARB_LOST | CADENCE_I2C_IXR_NACK)) != 0) {
0279     done = true;
0280   }
0281 
0282   if (
0283     (irqstatus & CADENCE_I2C_IXR_DATA) != 0
0284       && bus->read
0285       && bus->current_todo >= CADENCE_I2C_DATA_IRQ_DEPTH
0286   ) {
0287     cadence_i2c_continue_read_transfer(bus, regs);
0288   }
0289 
0290   if ((irqstatus & CADENCE_I2C_IXR_COMP) != 0) {
0291     if (bus->read) {
0292       uint32_t todo_now = bus->current_todo;
0293       uint32_t i;
0294 
0295       for (i = 0; i < todo_now; ++i) {
0296         *bus->current_msg_byte = (uint8_t) regs->data;
0297 
0298         cadence_i2c_next_byte(bus);
0299       }
0300 
0301       bus->current_todo = 0;
0302 
0303       done = true;
0304     } else {
0305       if (bus->current_todo > 0) {
0306         cadence_i2c_write_to_fifo(bus, regs);
0307       } else {
0308         done = true;
0309       }
0310 
0311       if (!bus->hold && bus->current_todo == 0) {
0312         regs->control &= ~CADENCE_I2C_CONTROL_HOLD;
0313       }
0314     }
0315   }
0316 
0317   if (done) {
0318     uint32_t err = irqstatus & CADENCE_I2C_IRQ_ERROR;
0319 
0320     if (bus->msg_todo == 0 || err != 0) {
0321       rtems_status_code sc;
0322 
0323       cadence_i2c_disable_interrupts(regs);
0324 
0325       bus->irqstatus = err;
0326 
0327       sc = rtems_event_transient_send(bus->task_id);
0328       _Assert(sc == RTEMS_SUCCESSFUL);
0329       (void) sc;
0330     } else {
0331       cadence_i2c_setup_transfer(bus, regs);
0332     }
0333   }
0334 }
0335 
0336 static int cadence_i2c_transfer(
0337   i2c_bus *base,
0338   i2c_msg *msgs,
0339   uint32_t msg_count
0340 )
0341 {
0342   cadence_i2c_bus *bus = (cadence_i2c_bus *) base;
0343   volatile cadence_i2c *regs;
0344   rtems_status_code sc;
0345   uint32_t i;
0346 
0347   _Assert(msg_count > 0);
0348 
0349   for (i = 0; i < msg_count; ++i) {
0350     /* FIXME: Not sure if we can support this. */
0351     if ((msgs[i].flags & I2C_M_RECV_LEN) != 0) {
0352       return -EINVAL;
0353     }
0354   }
0355 
0356   bus->msgs = &msgs[0];
0357   bus->msg_todo = msg_count;
0358   bus->current_msg_todo = msgs[0].len;
0359   bus->current_msg_byte = msgs[0].buf;
0360   bus->task_id = rtems_task_self();
0361 
0362   regs = bus->regs;
0363   cadence_i2c_setup_transfer(bus, regs);
0364   regs->irqenable = CADENCE_I2C_IRQ_USED;
0365 
0366   sc = rtems_event_transient_receive(RTEMS_WAIT, bus->base.timeout);
0367   if (sc != RTEMS_SUCCESSFUL) {
0368     cadence_i2c_reset(bus);
0369     rtems_event_transient_clear();
0370 
0371     return -ETIMEDOUT;
0372   }
0373 
0374   return bus->irqstatus == 0 ? 0 : -EIO;
0375 }
0376 
0377 static int cadence_i2c_set_clock(i2c_bus *base, unsigned long clock)
0378 {
0379   cadence_i2c_bus *bus = (cadence_i2c_bus *) base;
0380   volatile cadence_i2c *regs = bus->regs;
0381   uint32_t error = 0xffffffff;
0382   uint32_t best_div_a = CADENCE_I2C_DIV_A_MAX - 1;
0383   uint32_t best_div_b = CADENCE_I2C_DIV_B_MAX - 1;
0384   uint32_t div = bus->input_clock / (22 * clock);
0385   uint32_t div_a;
0386   uint32_t control;
0387 
0388   if (div <= 0 || div > (CADENCE_I2C_DIV_A_MAX * CADENCE_I2C_DIV_B_MAX)) {
0389     return -EIO;
0390   }
0391 
0392   for (div_a = 0; div_a < CADENCE_I2C_DIV_A_MAX; ++div_a) {
0393     uint32_t a = 22 * clock * (div_a + 1);
0394     uint32_t b = (bus->input_clock + a - 1) / a;
0395 
0396     if (b > 0 && b <= CADENCE_I2C_DIV_B_MAX) {
0397       uint32_t actual_clock = bus->input_clock / (22 * (div_a + 1) * b);
0398       uint32_t e = clock < actual_clock ?
0399         actual_clock - clock : clock - actual_clock;
0400 
0401       /*
0402        * Favour greater div_a values according to UG585, Zynq-7000 AP SoC
0403        * Technical Reference Manual, Table 20-1: Calculated Values for Standard
0404        * and High Speed SCL Clock Values".
0405        */
0406       if (e <= error && actual_clock <= clock) {
0407         error = e;
0408         best_div_a = div_a;
0409         best_div_b = b - 1;
0410       }
0411     }
0412   }
0413 
0414   control = regs->control;
0415   control = CADENCE_I2C_CONTROL_DIV_A_SET(control, best_div_a);
0416   control = CADENCE_I2C_CONTROL_DIV_B_SET(control, best_div_b);
0417   regs->control = control;
0418 
0419   return 0;
0420 }
0421 
0422 static void cadence_i2c_destroy(i2c_bus *base)
0423 {
0424   cadence_i2c_bus *bus = (cadence_i2c_bus *) base;
0425   rtems_status_code sc;
0426 
0427   sc = rtems_interrupt_handler_remove(bus->irq, cadence_i2c_interrupt, bus);
0428   _Assert(sc == RTEMS_SUCCESSFUL);
0429   (void) sc;
0430 
0431   i2c_bus_destroy_and_free(&bus->base);
0432 }
0433 
0434 int i2c_bus_register_cadence(
0435   const char *bus_path,
0436   uintptr_t register_base,
0437   uint32_t input_clock,
0438   rtems_vector_number irq
0439 )
0440 {
0441   cadence_i2c_bus *bus;
0442   rtems_status_code sc;
0443   int err;
0444 
0445   bus = (cadence_i2c_bus *) i2c_bus_alloc_and_init(sizeof(*bus));
0446   if (bus == NULL) {
0447     return -1;
0448   }
0449 
0450   bus->regs = (volatile cadence_i2c *) register_base;
0451   bus->input_clock = input_clock;
0452   bus->irq = irq;
0453 
0454   cadence_i2c_reset(bus);
0455 
0456   err = cadence_i2c_set_clock(&bus->base, I2C_BUS_CLOCK_DEFAULT);
0457   if (err != 0) {
0458     (*bus->base.destroy)(&bus->base);
0459 
0460     rtems_set_errno_and_return_minus_one(-err);
0461   }
0462 
0463   sc = rtems_interrupt_handler_install(
0464     irq,
0465     "Cadence I2C",
0466     RTEMS_INTERRUPT_UNIQUE,
0467     cadence_i2c_interrupt,
0468     bus
0469   );
0470   if (sc != RTEMS_SUCCESSFUL) {
0471     (*bus->base.destroy)(&bus->base);
0472 
0473     rtems_set_errno_and_return_minus_one(EIO);
0474   }
0475 
0476   bus->base.transfer = cadence_i2c_transfer;
0477   bus->base.set_clock = cadence_i2c_set_clock;
0478   bus->base.destroy = cadence_i2c_destroy;
0479 
0480   return i2c_bus_register(&bus->base, bus_path);
0481 }