Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:22:49

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (c) 2017 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 <bsp.h>
0029 #include <bsp/fdt.h>
0030 #include <libfdt.h>
0031 #include <arm/freescale/imx/imx_ccmvar.h>
0032 #include <arm/freescale/imx/imx_i2creg.h>
0033 #include <dev/i2c/i2c.h>
0034 #include <rtems/irq-extension.h>
0035 
0036 #define IMX_I2C_TRANSMIT (IMX_I2C_I2CR_IEN | IMX_I2C_I2CR_IIEN \
0037   | IMX_I2C_I2CR_MSTA | IMX_I2C_I2CR_MTX)
0038 
0039 #define IMX_I2C_RECEIVE (IMX_I2C_I2CR_IEN | IMX_I2C_I2CR_IIEN \
0040   | IMX_I2C_I2CR_MSTA)
0041 
0042 typedef struct {
0043   i2c_bus base;
0044   volatile imx_i2c *regs;
0045   uint32_t msg_todo;
0046   const i2c_msg *msg;
0047   bool read;
0048   bool start;
0049   uint16_t restart;
0050   uint32_t chunk_total;
0051   uint32_t chunk_done;
0052   uint16_t buf_todo;
0053   uint8_t *buf;
0054   rtems_id task_id;
0055   int eno;
0056   rtems_vector_number irq;
0057 } imx_i2c_bus;
0058 
0059 typedef struct {
0060   uint16_t divisor;
0061   uint8_t ifdr;
0062 } imx_i2c_clock_divisor;
0063 
0064 static const imx_i2c_clock_divisor imx_i2c_clock_divisor_table[] = {
0065   {    0, 0x20 }, {   22, 0x20 }, {   24, 0x21 }, {     26, 0x22 },
0066   {   28, 0x23 }, {   30, 0x00 }, {   32, 0x24 }, {     36, 0x25 },
0067   {   40, 0x26 }, {   42, 0x03 }, {   44, 0x27 }, {     48, 0x28 },
0068   {   52, 0x05 }, {   56, 0x29 }, {   60, 0x06 }, {     64, 0x2a },
0069   {   72, 0x2b }, {   80, 0x2c }, {   88, 0x09 }, {     96, 0x2d },
0070   {  104, 0x0a }, {  112, 0x2e }, {  128, 0x2f }, {    144, 0x0c },
0071   {  160, 0x30 }, {  192, 0x31 }, {  224, 0x32 }, {    240, 0x0f },
0072   {  256, 0x33 }, {  288, 0x10 }, {  320, 0x34 }, {    384, 0x35 },
0073   {  448, 0x36 }, {  480, 0x13 }, {  512, 0x37 }, {    576, 0x14 },
0074   {  640, 0x38 }, {  768, 0x39 }, {  896, 0x3a }, {    960, 0x17 },
0075   { 1024, 0x3b }, { 1152, 0x18 }, { 1280, 0x3c }, {   1536, 0x3d },
0076   { 1792, 0x3e }, { 1920, 0x1b }, { 2048, 0x3f }, {   2304, 0x1c },
0077   { 2560, 0x1d }, { 3072, 0x1e }, { 3840, 0x1f }, { 0xffff, 0x1f }
0078 };
0079 
0080 static void imx_i2c_stop(volatile imx_i2c *regs)
0081 {
0082   regs->i2cr = IMX_I2C_I2CR_IEN;
0083   regs->i2sr = 0;
0084   regs->i2sr;
0085 }
0086 
0087 static void imx_i2c_trigger_receive(imx_i2c_bus *bus, volatile imx_i2c *regs)
0088 {
0089   uint16_t i2cr;
0090 
0091   i2cr = IMX_I2C_RECEIVE;
0092 
0093   if (bus->chunk_total == 1) {
0094     i2cr |= IMX_I2C_I2CR_TXAK;
0095   }
0096 
0097   regs->i2cr = i2cr;
0098   regs->i2dr;
0099 }
0100 
0101 static void imx_i2c_done(imx_i2c_bus *bus, int eno)
0102 {
0103   /*
0104    * Generates a stop in case of transmit, otherwise, only disables interrupts
0105    * (IMX_I2C_I2CR_MSTA is already cleared).
0106    */
0107   imx_i2c_stop(bus->regs);
0108 
0109   bus->eno = eno;
0110   rtems_event_transient_send(bus->task_id);
0111 }
0112 
0113 static const i2c_msg *imx_i2c_msg_inc(imx_i2c_bus *bus)
0114 {
0115   const i2c_msg *next;
0116 
0117   next = bus->msg + 1;
0118   bus->msg = next;
0119   --bus->msg_todo;
0120   return next;
0121 }
0122 
0123 static void imx_i2c_msg_inc_and_set_buf(imx_i2c_bus *bus)
0124 {
0125   const i2c_msg *next;
0126 
0127   next = imx_i2c_msg_inc(bus);
0128   bus->buf_todo = next->len;
0129   bus->buf = next->buf;
0130 }
0131 
0132 static void imx_i2c_buf_inc(imx_i2c_bus *bus)
0133 {
0134   ++bus->buf;
0135   --bus->buf_todo;
0136   ++bus->chunk_done;
0137 }
0138 
0139 static void imx_i2c_buf_push(imx_i2c_bus *bus, uint8_t c)
0140 {
0141   while (true) {
0142     if (bus->buf_todo > 0) {
0143       bus->buf[0] = c;
0144       imx_i2c_buf_inc(bus);
0145       break;
0146     }
0147 
0148     imx_i2c_msg_inc_and_set_buf(bus);
0149   }
0150 }
0151 
0152 static uint8_t imx_i2c_buf_pop(imx_i2c_bus *bus)
0153 {
0154   while (true) {
0155     if (bus->buf_todo > 0) {
0156       uint8_t c;
0157 
0158       c = bus->buf[0];
0159       imx_i2c_buf_inc(bus);
0160       return c;
0161     }
0162 
0163     imx_i2c_msg_inc_and_set_buf(bus);
0164   }
0165 }
0166 
0167 RTEMS_STATIC_ASSERT(I2C_M_RD == 1, imx_i2c_read_flag);
0168 
0169 static void imx_i2c_setup_chunk(imx_i2c_bus *bus, volatile imx_i2c *regs)
0170 {
0171   while (true) {
0172     const i2c_msg *msg;
0173     int flags;
0174     int can_continue;
0175     uint32_t i;
0176 
0177     if (bus->msg_todo == 0) {
0178       imx_i2c_done(bus, 0);
0179       break;
0180     }
0181 
0182     msg = bus->msg;
0183     flags = msg->flags;
0184 
0185     bus->read = (flags & I2C_M_RD) != 0;
0186     bus->start = (flags & I2C_M_NOSTART) == 0;
0187     bus->chunk_total = msg->len;
0188     bus->chunk_done = 0;
0189     bus->buf_todo = msg->len;
0190     bus->buf = msg->buf;
0191 
0192     can_continue = (flags & I2C_M_RD) | I2C_M_NOSTART;
0193 
0194     for (i = 1; i < bus->msg_todo; ++i) {
0195       if ((msg[i].flags & (I2C_M_RD | I2C_M_NOSTART)) != can_continue) {
0196         break;
0197       }
0198 
0199       bus->chunk_total += msg[i].len;
0200     }
0201 
0202     if (bus->start) {
0203       regs->i2cr = IMX_I2C_TRANSMIT | bus->restart;
0204       regs->i2dr = (uint8_t) ((msg->addr << 1) | (flags & I2C_M_RD));
0205       bus->restart = IMX_I2C_I2CR_RSTA;
0206       break;
0207     } else if (bus->chunk_total > 0) {
0208       if (bus->read) {
0209         imx_i2c_trigger_receive(bus, regs);
0210       } else {
0211         regs->i2cr = IMX_I2C_TRANSMIT;
0212         regs->i2dr = imx_i2c_buf_pop(bus);
0213       }
0214 
0215       break;
0216     } else {
0217       ++bus->msg;
0218       --bus->msg_todo;
0219     }
0220   }
0221 }
0222 
0223 static void imx_i2c_transfer_complete(
0224   imx_i2c_bus *bus,
0225   volatile imx_i2c *regs,
0226   uint16_t i2sr
0227 )
0228 {
0229   if (bus->start) {
0230     bus->start = false;
0231 
0232     if ((i2sr & IMX_I2C_I2SR_RXAK) != 0) {
0233       imx_i2c_done(bus, EIO);
0234       return;
0235     }
0236 
0237     if (bus->read) {
0238       imx_i2c_trigger_receive(bus, regs);
0239       return;
0240     }
0241   }
0242 
0243   if (bus->chunk_done < bus->chunk_total) {
0244     if (bus->read) {
0245       if (bus->chunk_done + 2 == bus->chunk_total) {
0246         /* Receive second last byte with NACK */
0247         regs->i2cr = IMX_I2C_RECEIVE | IMX_I2C_I2CR_TXAK;
0248       } else if (bus->chunk_done + 1 == bus->chunk_total) {
0249         /* Receive last byte with STOP */
0250         bus->restart = 0;
0251         regs->i2cr = (IMX_I2C_RECEIVE | IMX_I2C_I2CR_TXAK)
0252           & ~IMX_I2C_I2CR_MSTA;
0253       }
0254 
0255       imx_i2c_buf_push(bus, (uint8_t) regs->i2dr);
0256 
0257       if (bus->chunk_done == bus->chunk_total) {
0258         imx_i2c_msg_inc(bus);
0259         imx_i2c_setup_chunk(bus, regs);
0260       }
0261     } else {
0262       if (bus->chunk_done > 0 && (i2sr & IMX_I2C_I2SR_RXAK) != 0) {
0263         imx_i2c_done(bus, EIO);
0264         return;
0265       }
0266 
0267       regs->i2dr = imx_i2c_buf_pop(bus);
0268     }
0269   } else {
0270     imx_i2c_msg_inc(bus);
0271     imx_i2c_setup_chunk(bus, regs);
0272   }
0273 }
0274 
0275 static void imx_i2c_interrupt(void *arg)
0276 {
0277   imx_i2c_bus *bus;
0278   volatile imx_i2c *regs;
0279   uint16_t i2sr;
0280 
0281   bus = arg;
0282   regs = bus->regs;
0283 
0284   i2sr = regs->i2sr;
0285   regs->i2sr = 0;
0286 
0287   if ((i2sr & (IMX_I2C_I2SR_IAL | IMX_I2C_I2SR_ICF)) == IMX_I2C_I2SR_ICF) {
0288     imx_i2c_transfer_complete(bus, regs, i2sr);
0289   } else {
0290     imx_i2c_done(bus, EIO);
0291   }
0292 }
0293 
0294 static int imx_i2c_wait_for_not_busy(volatile imx_i2c *regs)
0295 {
0296   rtems_interval timeout;
0297   bool before;
0298 
0299   if ((regs->i2sr & IMX_I2C_I2SR_IBB) == 0) {
0300     return 0;
0301   }
0302 
0303   timeout = rtems_clock_tick_later(10);
0304 
0305   do {
0306     before = rtems_clock_tick_before(timeout);
0307 
0308     if ((regs->i2sr & IMX_I2C_I2SR_IBB) == 0) {
0309       return 0;
0310     }
0311   } while (before);
0312 
0313   return ETIMEDOUT;
0314 }
0315 
0316 static int imx_i2c_transfer(i2c_bus *base, i2c_msg *msgs, uint32_t n)
0317 {
0318   imx_i2c_bus *bus;
0319   int supported_flags;
0320   uint32_t i;
0321   volatile imx_i2c *regs;
0322   int eno;
0323   rtems_status_code sc;
0324 
0325   supported_flags = I2C_M_RD;
0326 
0327   for (i = 0; i < n; ++i) {
0328     if ((msgs[i].flags & ~supported_flags) != 0) {
0329       return -EINVAL;
0330     }
0331 
0332     supported_flags |= I2C_M_NOSTART;
0333   }
0334 
0335   bus = (imx_i2c_bus *) base;
0336   regs = bus->regs;
0337 
0338   eno = imx_i2c_wait_for_not_busy(regs);
0339   if (eno != 0) {
0340     return -eno;
0341   }
0342 
0343   bus->msg_todo = n;
0344   bus->msg = &msgs[0];
0345   bus->restart = 0;
0346   bus->task_id = rtems_task_self();
0347   bus->eno = 0;
0348 
0349   regs->i2sr = 0;
0350   imx_i2c_setup_chunk(bus, regs);
0351 
0352   sc = rtems_event_transient_receive(RTEMS_WAIT, bus->base.timeout);
0353   if (sc != RTEMS_SUCCESSFUL) {
0354     imx_i2c_stop(bus->regs);
0355     rtems_event_transient_clear();
0356     return -ETIMEDOUT;
0357   }
0358 
0359   return -bus->eno;
0360 }
0361 
0362 static int imx_i2c_set_clock(i2c_bus *base, unsigned long clock)
0363 {
0364   imx_i2c_bus *bus;
0365   uint32_t ipg_clock;
0366   uint16_t div;
0367   size_t i;
0368   const imx_i2c_clock_divisor *clock_divisor;
0369 
0370   bus = (imx_i2c_bus *) base;
0371   ipg_clock = imx_ccm_ipg_hz();
0372   div = (uint16_t) ((ipg_clock + clock - 1) / clock);
0373 
0374   for (i = 0; i < RTEMS_ARRAY_SIZE(imx_i2c_clock_divisor_table); ++i) {
0375     clock_divisor = &imx_i2c_clock_divisor_table[i];
0376 
0377     if (clock_divisor->divisor >= div) {
0378       break;
0379     }
0380   }
0381 
0382   bus->regs->ifdr = clock_divisor->ifdr;
0383   return 0;
0384 }
0385 
0386 static void imx_i2c_destroy(i2c_bus *base)
0387 {
0388   imx_i2c_bus *bus;
0389 
0390   bus = (imx_i2c_bus *) base;
0391   rtems_interrupt_handler_remove(bus->irq, imx_i2c_interrupt, bus);
0392   i2c_bus_destroy_and_free(&bus->base);
0393 }
0394 
0395 static int imx_i2c_init(imx_i2c_bus *bus)
0396 {
0397   rtems_status_code sc;
0398 
0399   imx_i2c_set_clock(&bus->base, I2C_BUS_CLOCK_DEFAULT);
0400   bus->regs->i2cr = IMX_I2C_I2CR_IEN;
0401 
0402   sc = rtems_interrupt_handler_install(
0403     bus->irq,
0404     "I2C",
0405     RTEMS_INTERRUPT_UNIQUE,
0406     imx_i2c_interrupt,
0407     bus
0408   );
0409   if (sc != RTEMS_SUCCESSFUL) {
0410     return EAGAIN;
0411   }
0412 
0413   return 0;
0414 }
0415 
0416 int i2c_bus_register_imx(const char *bus_path, const char *alias_or_path)
0417 {
0418   const void *fdt;
0419   const char *path;
0420   int node;
0421   imx_i2c_bus *bus;
0422   int eno;
0423 
0424   fdt = bsp_fdt_get();
0425   path = fdt_get_alias(fdt, alias_or_path);
0426 
0427   if (path == NULL) {
0428     path = alias_or_path;
0429   }
0430 
0431   node = fdt_path_offset(fdt, path);
0432   if (node < 0) {
0433     rtems_set_errno_and_return_minus_one(ENXIO);
0434   }
0435 
0436   bus = (imx_i2c_bus *) i2c_bus_alloc_and_init(sizeof(*bus));
0437   if (bus == NULL){
0438     return -1;
0439   }
0440 
0441   bus->regs = imx_get_reg_of_node(fdt, node);
0442   bus->irq = imx_get_irq_of_node(fdt, node, 0);
0443 
0444   eno = imx_i2c_init(bus);
0445   if (eno != 0) {
0446     (*bus->base.destroy)(&bus->base);
0447     rtems_set_errno_and_return_minus_one(eno);
0448   }
0449 
0450   bus->base.transfer = imx_i2c_transfer;
0451   bus->base.set_clock = imx_i2c_set_clock;
0452   bus->base.destroy = imx_i2c_destroy;
0453 
0454   return i2c_bus_register(&bus->base, bus_path);
0455 }