/**************************************************************************** * arch/arm/src/cxd56xx/cxd56_i2c.c * * Copyright 2018 Sony Semiconductor Solutions Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name of Sony Semiconductor Solutions Corporation nor * the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chip.h" #include "up_arch.h" #include "up_internal.h" #include "cxd56_clock.h" #include "cxd56_i2c.h" #include "hardware/cxd56_i2c.h" #include "cxd56_pinconfig.h" #if defined(CONFIG_CXD56_I2C0_SCUSEQ) || defined(CONFIG_CXD56_I2C1_SCUSEQ) #include #endif #ifdef CONFIG_CXD56_I2C /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define I2C_TIMEOUT (20*1000/CONFIG_USEC_PER_TICK) /* 20 mS */ #define I2C_DEFAULT_FREQUENCY 400000 #define I2C_FIFO_MAX_SIZE 32 #define I2C_INTR_ENABLE ((INTR_STOP_DET) | \ (INTR_TX_ABRT) | \ (INTR_TX_OVER) | \ (INTR_RX_OVER) | \ (INTR_RX_UNDER)) /**************************************************************************** * Private Data ****************************************************************************/ struct cxd56_i2cdev_s { struct i2c_master_s dev; /* Generic I2C device */ unsigned int base; /* Base address of registers */ uint16_t irqid; /* IRQ for this device */ int8_t port; /* Port number */ uint32_t baseFreq; /* branch frequency */ sem_t mutex; /* Only one thread can access at a time */ sem_t wait; /* Place to wait for transfer completion */ WDOG_ID timeout; /* watchdog to timeout when bus hung */ uint32_t frequency; /* Current I2C frequency */ ssize_t reg_buff_offset; ssize_t rw_size; struct i2c_msg_s *msgs; int error; /* Error status of each transfers */ int refs; /* Reference count */ }; /* Channel 0 as SCU_I2C0 * Channel 1 as SCU_I2C1 * Channel 2 as I2CM */ #ifdef CONFIG_CXD56_I2C0 static struct cxd56_i2cdev_s g_i2c0dev = { .port = 0, .base = CXD56_SCU_I2C0_BASE, .irqid = CXD56_IRQ_SCU_I2C0, .refs = 0, }; #endif #ifdef CONFIG_CXD56_I2C1 static struct cxd56_i2cdev_s g_i2c1dev = { .port = 1, .base = CXD56_SCU_I2C1_BASE, .irqid = CXD56_IRQ_SCU_I2C1, .refs = 0, }; #endif #ifdef CONFIG_CXD56_I2C2 static struct cxd56_i2cdev_s g_i2c2dev = { .port = 2, .base = CXD56_I2CM_BASE, .irqid = CXD56_IRQ_I2CM, .refs = 0, }; #endif /**************************************************************************** * Private Functions ****************************************************************************/ static inline uint32_t i2c_reg_read(struct cxd56_i2cdev_s *priv, uint32_t offset); static inline void i2c_reg_write(struct cxd56_i2cdev_s *priv, uint32_t offset, uint32_t val); static inline void i2c_reg_rmw(struct cxd56_i2cdev_s *dev, uint32_t offset, uint32_t val, uint32_t mask); static int cxd56_i2c_disable(struct cxd56_i2cdev_s *priv); static void cxd56_i2c_enable(struct cxd56_i2cdev_s *priv); static int cxd56_i2c_interrupt(int irq, FAR void *context, FAR void *arg); static void cxd56_i2c_timeout(int argc, uint32_t arg, ...); static void cxd56_i2c_setfrequency(struct cxd56_i2cdev_s *priv, uint32_t frequency); static int cxd56_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count); #ifdef CONFIG_I2C_RESET static int cxd56_i2c_reset(FAR struct i2c_master_s * dev); #endif #if defined(CONFIG_CXD56_I2C0_SCUSEQ) || defined(CONFIG_CXD56_I2C1_SCUSEQ) static int cxd56_i2c_transfer_scu(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count); #endif /**************************************************************************** * Name: cxd56_i2c_pincontrol * * Description: * Configure the I2C pin * * Input Parameter: * on - true: enable pin, false: disable pin * ****************************************************************************/ static void cxd56_i2c_pincontrol(int ch, bool on) { switch (ch) { #ifdef CONFIG_CXD56_I2C0 case 0: if (on) { CXD56_PIN_CONFIGS(PINCONFS_I2C0); } else { CXD56_PIN_CONFIGS(PINCONFS_I2C0_GPIO); } break; #endif /* CONFIG_CXD56_I2C0 */ #ifdef CONFIG_CXD56_I2C1 case 1: if (on) { CXD56_PIN_CONFIGS(PINCONFS_PWMB_I2C1); } else { CXD56_PIN_CONFIGS(PINCONFS_PWMB_GPIO); } break; #endif /* CONFIG_CXD56_I2C1 */ #ifdef CONFIG_CXD56_I2C2 case 2: if (on) { CXD56_PIN_CONFIGS(PINCONFS_SPI0B_I2C2); } else { CXD56_PIN_CONFIGS(PINCONFS_SPI0B_GPIO); } break; #endif /* CONFIG_CXD56_I2C2 */ default: break; } } /**************************************************************************** * I2C device operations ****************************************************************************/ struct i2c_ops_s cxd56_i2c_ops = { .transfer = cxd56_i2c_transfer, #ifdef CONFIG_I2C_RESET .reset = cxd56_i2c_reset, #endif }; #if defined(CONFIG_CXD56_I2C0_SCUSEQ) || defined(CONFIG_CXD56_I2C1_SCUSEQ) struct i2c_ops_s cxd56_i2c_scu_ops = { .transfer = cxd56_i2c_transfer_scu, #ifdef CONFIG_I2C_RESET .reset = cxd56_i2c_reset, #endif }; #endif /**************************************************************************** * Name: cxd56_i2c_setfrequency * * Description: * Set the frequency for the next transfer * ****************************************************************************/ static void cxd56_i2c_setfrequency(struct cxd56_i2cdev_s *priv, uint32_t frequency) { int32_t lcnt; int32_t hcnt; uint64_t lcnt64; uint64_t hcnt64; uint64_t speed; uint64_t tLow; uint64_t tHigh; uint32_t base = cxd56_get_i2c_baseclock(priv->port); uint32_t spklen; ASSERT(base); if ((priv->frequency == frequency) && (priv->baseFreq == base)) { return; } priv->frequency = frequency; priv->baseFreq = base; base /= 1000; if (frequency <= 100000) { tLow = 4700000; tHigh = 4000000; } else if (frequency <= 400000) { tLow = 1300000; tHigh = 600000; } else { tLow = 500000; tHigh = 260000; } if (frequency > 100000) { if (base < 20032) { spklen = 1; } else if (base < 40064) { spklen = 2; } else { spklen = 3; } } else { spklen = 1; } lcnt64 = (tLow + 6500ull / 20000ull) * base; lcnt = ((lcnt64 + 999999999ull) / 1000000000ull) - 1; /* ceil */ lcnt = lcnt < 8 ? 8 : lcnt; hcnt64 = (tHigh - 6500ull) * base; hcnt = ((hcnt64 + 999999999ull) / 1000000000ull) - 6 - spklen; /* ceil */ hcnt = hcnt < 6 ? 6 : hcnt; speed = 1000000000000000000ull / (((lcnt + 1) * 1000000000000ull + (hcnt + 6 + spklen) * 1000000000000ull) / base + 20000ull / 1000ull * 1000000ull); if (speed > (frequency * 1000ull)) { uint64_t adj; adj = ((1000000000000000000ull / (frequency * 1000ull)) - (1000000000000000000ull / speed)) * base; hcnt += (adj + 999999999999ull) / 1000000000000ull; } /* use FS register in SS and FS mode */ i2c_reg_write(priv, CXD56_IC_FS_SCL_HCNT, hcnt); i2c_reg_write(priv, CXD56_IC_FS_SCL_LCNT, lcnt); i2c_reg_rmw(priv, CXD56_IC_CON, IC_SPEED_FS, IC_MAX_SPEED_MODE); i2c_reg_write(priv, CXD56_IC_FS_SPKLEN, spklen); } /**************************************************************************** * Name: cxd56_i2c_timeout * * Description: * Watchdog timer for timeout of I2C operation * ****************************************************************************/ static void cxd56_i2c_timeout(int argc, uint32_t arg, ...) { struct cxd56_i2cdev_s *priv = (struct cxd56_i2cdev_s *)arg; irqstate_t flags = enter_critical_section(); priv->error = -ENODEV; nxsem_post(&priv->wait); leave_critical_section(flags); } /**************************************************************************** * Name: cxd56_i2c_drainrxfifo * * Description: * Receive I2C data * ****************************************************************************/ static void cxd56_i2c_drainrxfifo(struct cxd56_i2cdev_s *priv) { struct i2c_msg_s *msg = priv->msgs; uint32_t status; uint32_t dat; ssize_t i; DEBUGASSERT(msg != NULL); status = i2c_reg_read(priv, CXD56_IC_STATUS); for (i = 0; i < priv->rw_size && status & STATUS_RFNE; i++) { dat = i2c_reg_read(priv, CXD56_IC_DATA_CMD); msg->buffer[priv->reg_buff_offset + i] = dat & 0xff; status = i2c_reg_read(priv, CXD56_IC_STATUS); } priv->reg_buff_offset += priv->rw_size; } /**************************************************************************** * Name: cxd56_i2c_interrupt * * Description: * The I2C Interrupt Handler * ****************************************************************************/ static int cxd56_i2c_interrupt(int irq, FAR void *context, FAR void *arg) { FAR struct cxd56_i2cdev_s *priv = (FAR struct cxd56_i2cdev_s *)arg; uint32_t state; int ret; state = i2c_reg_read(priv, CXD56_IC_INTR_STAT); if (state & INTR_TX_ABRT) { i2c_reg_read(priv, CXD56_IC_CLR_TX_ABRT); priv->error = -ENODEV; } if (state & INTR_TX_OVER) { i2c_reg_read(priv, CXD56_IC_CLR_TX_OVER); priv->error = -EIO; } if (state & INTR_RX_OVER) { i2c_reg_read(priv, CXD56_IC_CLR_RX_OVER); priv->error = -EIO; } if (state & INTR_RX_UNDER) { i2c_reg_read(priv, CXD56_IC_CLR_RX_UNDER); priv->error = -EIO; } if (state & INTR_TX_EMPTY) { /* TX_EMPTY is automatically cleared by hardware * when the buffer level goes above the threshold. */ i2c_reg_rmw(priv, CXD56_IC_INTR_MASK, 0, INTR_TX_EMPTY); } if (state & INTR_RX_FULL) { /* RX_FULL is automatically cleared by hardware * when the buffer level goes below the threshold. */ i2c_reg_rmw(priv, CXD56_IC_INTR_MASK, 0, INTR_RX_FULL); cxd56_i2c_drainrxfifo(priv); } if (state & INTR_STOP_DET) { i2c_reg_read(priv, CXD56_IC_CLR_STOP_DET); } if ((priv->error) || (state & INTR_TX_EMPTY) || (state & INTR_RX_FULL)) { /* Failure of wd_cancel() means that the timer expired. * In this case, nxsem_post() has already been called. * Therefore, call nxsem_post() only when wd_cancel() succeeds. */ ret = wd_cancel(priv->timeout); if (ret == OK) { nxsem_post(&priv->wait); } } return OK; } /**************************************************************************** * Name: cxd56_i2c_receive * * Description: * Receive data from I2C bus. * Prohibit all interrupt because the STOP condition might happen * if the interrupt occurs when the writing request. * Actual receiving data is in RX_FULL interrupt handler. * * TODO : The argument "last" is not used. ****************************************************************************/ static int cxd56_i2c_receive(struct cxd56_i2cdev_s *priv, int last) { struct i2c_msg_s *msg = priv->msgs; int i; int en; ssize_t msg_length; irqstate_t flags; priv->reg_buff_offset = 0; DEBUGASSERT(msg != NULL); for (msg_length = msg->length; msg_length > 0; msg_length -= priv->rw_size) { if (msg_length <= I2C_FIFO_MAX_SIZE) { priv->rw_size = msg_length; en = 1; } else { priv->rw_size = I2C_FIFO_MAX_SIZE; en = 0; } /* update threshold value of the receive buffer */ i2c_reg_write(priv, CXD56_IC_RX_TL, priv->rw_size - 1); for (i = 0; i < priv->rw_size - 1; i++) { i2c_reg_write(priv, CXD56_IC_DATA_CMD, CMD_READ); } flags = enter_critical_section(); wd_start(priv->timeout, I2C_TIMEOUT, cxd56_i2c_timeout, 1, (uint32_t)priv); /* Set stop flag for indicate the last data */ i2c_reg_write(priv, CXD56_IC_DATA_CMD, CMD_READ | (en ? CMD_STOP : 0)); i2c_reg_rmw(priv, CXD56_IC_INTR_MASK, INTR_RX_FULL, INTR_RX_FULL); leave_critical_section(flags); nxsem_wait(&priv->wait); if (priv->error != OK) { break; } } return 0; } /**************************************************************************** * Name: cxd56_i2c_send * * Description: * Send data to I2C bus. * ****************************************************************************/ static int cxd56_i2c_send(struct cxd56_i2cdev_s *priv, int last) { struct i2c_msg_s *msg = priv->msgs; ssize_t i; irqstate_t flags; DEBUGASSERT(msg != NULL); for (i = 0; i < msg->length - 1; i++) { while (!(i2c_reg_read(priv, CXD56_IC_STATUS) & STATUS_TFNF)); i2c_reg_write(priv, CXD56_IC_DATA_CMD, (uint32_t)msg->buffer[i]); } while (!(i2c_reg_read(priv, CXD56_IC_STATUS) & STATUS_TFNF)); flags = enter_critical_section(); wd_start(priv->timeout, I2C_TIMEOUT, cxd56_i2c_timeout, 1, (uint32_t)priv); i2c_reg_write(priv, CXD56_IC_DATA_CMD, (uint32_t)msg->buffer[i] | (last ? CMD_STOP : 0)); /* Enable TX_EMPTY interrupt for determine transfer done. */ i2c_reg_rmw(priv, CXD56_IC_INTR_MASK, INTR_TX_EMPTY, INTR_TX_EMPTY); leave_critical_section(flags); nxsem_wait(&priv->wait); return 0; } /**************************************************************************** * Name: cxd56_i2c_transfer * * Description: * Perform a sequence of I2C transfers * * TODO: Multiple i2c_msg_s read operations with the same address are not * currently guaranteed. ****************************************************************************/ static int cxd56_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count) { struct cxd56_i2cdev_s *priv = (struct cxd56_i2cdev_s *)dev; int i; int ret = 0; int semval = 0; int addr = -1; static int wostop = 0; DEBUGASSERT(dev != NULL); /* Get exclusive access to the I2C bus */ nxsem_wait(&priv->mutex); /* Check wait semaphore value. If the value is not 0, the transfer can not * be performed normally. */ ret = nxsem_getvalue(&priv->wait, &semval); DEBUGASSERT(ret == OK && semval == 0); /* Disable clock gating (clock enable) */ cxd56_i2c_clock_gate_disable(priv->port); for (i = 0; i < count; i++, msgs++) { /* Pass msg descriptor via device context */ priv->msgs = msgs; priv->error = OK; if ((addr != msgs->addr) && !wostop) { cxd56_i2c_disable(priv); cxd56_i2c_setfrequency(priv, msgs->frequency); i2c_reg_rmw(priv, CXD56_IC_CON, IC_RESTART_EN, IC_RESTART_EN); i2c_reg_write(priv, CXD56_IC_TAR, msgs->addr & 0x7f); cxd56_i2c_enable(priv); addr = msgs->addr; } if (msgs->flags & I2C_M_NOSTOP) { /* Don't send stop condition even if the last data */ wostop = 1; } else { wostop = 0; } if (msgs->flags & I2C_M_READ) { ret = cxd56_i2c_receive(priv, (wostop) ? 0 : (i + 1 == count)); } else { ret = cxd56_i2c_send(priv, (wostop) ? 0 : (i + 1 == count)); } if (ret < 0) { break; } if (priv->error != OK) { ret = priv->error; break; } /* Clear msg descriptor for prevent illegal access in interrupt */ priv->msgs = NULL; } if (!wostop) { cxd56_i2c_disable(priv); } /* Enable clock gating (clock disable) */ cxd56_i2c_clock_gate_enable(priv->port); nxsem_post(&priv->mutex); return ret; } /**************************************************************************** * Name: cxd56_i2c_reset * * Description: * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. * * Input Parameters: * dev - Device-specific state data * * Returned Value: * Zero (OK) on success; a negated errno value on failure. * ****************************************************************************/ #ifdef CONFIG_I2C_RESET static int cxd56_i2c_reset(FAR struct i2c_master_s *dev) { return OK; } #endif /* CONFIG_I2C_RESET */ /**************************************************************************** * Name: cxd56_i2c_transfer_scu * * Description: * Perform a sequence of I2C transfers with scu oneshot sequencer. * ****************************************************************************/ #if defined(CONFIG_CXD56_I2C0_SCUSEQ) || defined(CONFIG_CXD56_I2C1_SCUSEQ) static int cxd56_i2c_scurecv(int port, int addr, uint8_t *buf, ssize_t buflen) { uint16_t inst[2]; int instn; int len0; int len1; ssize_t rem; int ret = OK; /* Ignore buffer is NULL */ if (buf == NULL) { return OK; } if (buflen > 16) { return -EINVAL; } rem = buflen; len0 = rem > 8 ? 8 : rem; rem -= len0; len1 = rem > 8 ? 8 : rem; rem -= len1; inst[0] = SCU_INST_RECV(len0); if (len1) { inst[1] = SCU_INST_RECV(len1); instn = 2; } else { instn = 1; } inst[instn - 1] |= SCU_INST_LAST; ret = scu_i2ctransfer(port, addr, inst, instn, buf, buflen); if (ret < 0) { syslog(LOG_ERR, "I2C receive failed. port %d addr %d\n", port, addr); } return ret; } static int cxd56_i2c_scusend(int port, int addr, uint8_t *buf, ssize_t buflen) { uint16_t inst[12]; ssize_t rem; int i; int ret = OK; rem = buflen; while (rem) { for (i = 0; i < 12 && rem > 0; i++) { inst[i] = SCU_INST_SEND(*buf++); rem--; } if (rem == 0) { inst[i - 1] |= SCU_INST_LAST; } if (i > 0) { ret = scu_i2ctransfer(port, addr, inst, i, NULL, i); if (ret < 0) { syslog(LOG_ERR, "I2C send failed. port %d addr %d\n", port, addr); break; } } } return ret; } static int cxd56_i2c_transfer_scu(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count) { FAR struct cxd56_i2cdev_s *priv = (FAR struct cxd56_i2cdev_s *)dev; ssize_t len = 0; uint8_t *buf = NULL; uint8_t addr = msgs->addr; int i; int ret = 0; DEBUGASSERT(dev != NULL); /* Get exclusive access to the I2C bus */ nxsem_wait(&priv->mutex); /* Apply frequency for request msgs */ if (priv->frequency != msgs->frequency) { cxd56_i2c_clock_gate_disable(priv->port); cxd56_i2c_disable(priv); cxd56_i2c_setfrequency(priv, msgs->frequency); i2c_reg_rmw(priv, CXD56_IC_CON, IC_RESTART_EN, IC_RESTART_EN); i2c_reg_write(priv, CXD56_IC_TAR, msgs->addr & 0x7f); cxd56_i2c_enable(priv); cxd56_i2c_clock_gate_enable(priv->port); priv->frequency = msgs->frequency; } for (i = 0; i < count; i++, msgs++) { len = msgs->length; buf = msgs->buffer; if (msgs->flags & I2C_M_READ) { ret = cxd56_i2c_scurecv(priv->port, addr, buf, len); } else { ret = cxd56_i2c_scusend(priv->port, addr, buf, len); } if (ret < 0) { break; } } nxsem_post(&priv->mutex); return ret; } #endif static inline uint32_t i2c_reg_read(struct cxd56_i2cdev_s *priv, uint32_t offset) { return getreg32(priv->base + offset); } static inline void i2c_reg_write(struct cxd56_i2cdev_s *priv, uint32_t offset, uint32_t val) { putreg32(val, priv->base + offset); } static inline void i2c_reg_rmw(struct cxd56_i2cdev_s *priv, uint32_t offset, uint32_t val, uint32_t mask) { uint32_t regval; regval = getreg32(priv->base + offset); putreg32((regval & ~mask) | val, priv->base + offset); } static int cxd56_i2c_disable(struct cxd56_i2cdev_s *priv) { int retry = 25000; uint32_t stat; /* disable all interrupt */ i2c_reg_write(priv, CXD56_IC_INTR_MASK, 0x0); /* clear all interrupt status */ i2c_reg_read(priv, CXD56_IC_CLR_INTR); i2c_reg_write(priv, CXD56_IC_ENABLE, 0); do { stat = i2c_reg_read(priv, CXD56_IC_ENABLE_STATUS); } while (--retry && (stat & ESTATUS_IC_EN)); if (!retry) { i2cerr("i2c wait timeout.\n"); return -EBUSY; } /* clear all interrupt status again */ i2c_reg_read(priv, CXD56_IC_CLR_INTR); return 0; } static void cxd56_i2c_enable(struct cxd56_i2cdev_s *priv) { i2c_reg_write(priv, CXD56_IC_INTR_MASK, I2C_INTR_ENABLE); i2c_reg_write(priv, CXD56_IC_ENABLE, 1); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: cxd56_i2cbus_initialize * * Description: * Initialise an I2C device * ****************************************************************************/ struct i2c_master_s *cxd56_i2cbus_initialize(int port) { struct cxd56_i2cdev_s *priv; irqstate_t flags; flags = enter_critical_section(); #ifdef CONFIG_CXD56_I2C0 if (port == 0) { priv = &g_i2c0dev; # ifndef CONFIG_CXD56_I2C0_SCUSEQ priv->dev.ops = &cxd56_i2c_ops; # else priv->dev.ops = &cxd56_i2c_scu_ops; # endif } else #endif #ifdef CONFIG_CXD56_I2C1 if (port == 1) { priv = &g_i2c1dev; # ifndef CONFIG_CXD56_I2C1_SCUSEQ priv->dev.ops = &cxd56_i2c_ops; # else priv->dev.ops = &cxd56_i2c_scu_ops; # endif } else #endif #ifdef CONFIG_CXD56_I2C2 if (port == 2) { priv = &g_i2c2dev; priv->dev.ops = &cxd56_i2c_ops; } else #endif { leave_critical_section(flags); i2cerr("I2C Only support 0,1,2\n"); return NULL; } priv->refs++; /* Test if already initialized or not */ if (1 < priv->refs) { leave_critical_section(flags); return &priv->dev; } priv->port = port; priv->frequency = 0; cxd56_i2c_clock_enable(priv->port); priv->baseFreq = cxd56_get_i2c_baseclock(priv->port); cxd56_i2c_disable(priv); i2c_reg_write(priv, CXD56_IC_INTR_MASK, 0x00); i2c_reg_read(priv, CXD56_IC_CLR_INTR); /* set threshold level of the Rx/Tx FIFO */ i2c_reg_write(priv, CXD56_IC_RX_TL, 0xff); i2c_reg_write(priv, CXD56_IC_TX_TL, 0); /* set hold time for margin */ i2c_reg_write(priv, CXD56_IC_SDA_HOLD, 1); i2c_reg_write(priv, CXD56_IC_CON, (IC_SLAVE_DISABLE | IC_MASTER_MODE | IC_TX_EMPTY_CTRL)); cxd56_i2c_setfrequency(priv, I2C_DEFAULT_FREQUENCY); leave_critical_section(flags); /* Configure pin */ cxd56_i2c_pincontrol(port, true); nxsem_init(&priv->mutex, 0, 1); nxsem_init(&priv->wait, 0, 0); priv->timeout = wd_create(); /* Attach Interrupt Handler */ irq_attach(priv->irqid, cxd56_i2c_interrupt, priv); /* Enable Interrupt Handler */ up_enable_irq(priv->irqid); /* Enable Interrupt in SCU */ if (port == 0 || port == 1) { putreg32(getreg32(CXD56_SCU_BASE + 0x400) | (1u << (port + 1)), CXD56_SCU_BASE + 0x400); } /* Enable clock gating (clock disable) */ cxd56_i2c_clock_gate_enable(port); return &priv->dev; } /**************************************************************************** * Name: cxd56_i2cbus_uninitialize * * Description: * Uninitialise an I2C device * ****************************************************************************/ int cxd56_i2cbus_uninitialize(FAR struct i2c_master_s *dev) { struct cxd56_i2cdev_s *priv = (struct cxd56_i2cdev_s *)dev; /* Decrement reference count and check for underflow */ if (priv->refs == 0) { return ERROR; } if (--priv->refs) { return OK; } /* Configure pin */ cxd56_i2c_pincontrol(priv->port, false); /* Disable clock gating (clock enable) */ cxd56_i2c_clock_gate_disable(priv->port); cxd56_i2c_disable(priv); cxd56_i2c_clock_disable(priv->port); up_disable_irq(priv->irqid); irq_detach(priv->irqid); wd_delete(priv->timeout); priv->timeout = NULL; nxsem_destroy(&priv->mutex); nxsem_destroy(&priv->wait); return OK; } #endif