/**************************************************************************** * arch/arm/src/lpc54xx/lpc54_i2c_master.c * * Copyright (C) 2017 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Parts of this file were adapted from sample code provided for the LPC54xx * family from NXP which has a compatible BSD license. * * Copyright (c) 2016, Freescale Semiconductor, Inc. * Copyright 2016-2017 NXP * * 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 NuttX 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 #include #include "chip.h" #include "arm_arch.h" #include "arm_internal.h" #include "hardware/lpc54_pinmux.h" #include "hardware/lpc54_syscon.h" #include "hardware/lpc54_flexcomm.h" #include "hardware/lpc54_i2c.h" #include "lpc54_config.h" #include "lpc54_clockconfig.h" #include "lpc54_enableclk.h" #include "lpc54_gpio.h" #include "lpc54_i2c_master.h" #include #ifdef HAVE_I2C_MASTER_DEVICE /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* 20 Millisecond timeout in system clock ticks. */ #ifdef CONFIG_DEBUG_I2C_INFO # define I2C_WDOG_TIMEOUT MSEC2TICK(50) #else # define I2C_WDOG_TIMEOUT MSEC2TICK(20) #endif /* Default I2C frequency */ #if defined(CONFIG_LPC54_I2C_FAST) || defined(CONFIG_LPC54_I2C_HIGH) # define I2C_DEFAULT_FREQUENCY 1000000 #else # define I2C_DEFAULT_FREQUENCY 400000 #endif /* I2C Master Interrupts */ #define I2C_MASTER_INTS \ (I2C_INT_MSTPENDING | I2C_INT_MSTARBLOSS | I2C_INT_MSTSTSTPERR) /**************************************************************************** * Private Types ****************************************************************************/ /* I2C state */ enum lpc54_i2cstate_e { I2CSTATE_IDLE = 0, I2CSTATE_TRANSMIT, I2CSTATE_RECEIVE, I2CSTATE_START, I2CSTATE_STOP, I2CSTATE_WAITSTOP }; /* This structure provides the overall state of the I2C driver */ struct lpc54_i2cdev_s { struct i2c_master_s dev; /* Generic I2C device */ uintptr_t base; /* Base address of Flexcomm registers */ struct wdog_s timeout; /* Watchdog to timeout when bus hung */ uint32_t frequency; /* Current I2C frequency */ uint32_t fclock; /* Flexcomm function clock frequency */ struct i2c_msg_s *msgs; /* Remaining transfers (first is in progress) */ int16_t nmsgs; /* Number of transfer remaining */ int16_t result; /* The result of the transfer */ sem_t exclsem; /* Only one thread can access at a time */ #ifndef CONFIG_I2C_POLLED sem_t waitsem; /* Supports wait for state machine completion */ uint16_t irq; /* Flexcomm IRQ number */ #endif uint16_t xfrd; /* Number of bytes transferred */ volatile uint8_t state; /* State of state machine */ }; /**************************************************************************** * Private Functions ****************************************************************************/ static inline void lpc54_i2c_putreg(struct lpc54_i2cdev_s *priv, unsigned int regoffset, uint32_t regval); static inline uint32_t lpc54_i2c_getreg(struct lpc54_i2cdev_s *priv, unsigned int regoffset); static void lpc54_i2c_setfrequency(struct lpc54_i2cdev_s *priv, uint32_t frequency); static void lpc54_i2c_timeout(wdparm_t arg); static void lpc54_i2c_xfrsetup(struct lpc54_i2cdev_s *priv); static bool lpc54_i2c_nextmsg(struct lpc54_i2cdev_s *priv); static bool lpc54_i2c_statemachine(struct lpc54_i2cdev_s *priv); #ifndef CONFIG_I2C_POLLED static int lpc54_i2c_interrupt(int irq, FAR void *context, FAR void *arg); #else static int lpc54_i2c_poll(struct lpc54_i2cdev_s *priv); #endif static int lpc54_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count); #ifdef CONFIG_I2C_RESET static int lpc54_i2c_reset(FAR struct i2c_master_s * dev); #endif /**************************************************************************** * Private Data ****************************************************************************/ struct i2c_ops_s lpc54_i2c_ops = { .transfer = lpc54_i2c_transfer #ifdef CONFIG_I2C_RESET , .reset = lpc54_i2c_reset #endif }; #ifdef CONFIG_LPC54_I2C0_MASTER static struct lpc54_i2cdev_s g_i2c0_dev; #endif #ifdef CONFIG_LPC54_I2C1_MASTER static struct lpc54_i2cdev_s g_i2c1_dev; #endif #ifdef CONFIG_LPC54_I2C2_MASTER static struct lpc54_i2cdev_s g_i2c2_dev; #endif #ifdef CONFIG_LPC54_I2C3_MASTER static struct lpc54_i2cdev_s g_i2c3_dev; #endif #ifdef CONFIG_LPC54_I2C4_MASTER static struct lpc54_i2cdev_s g_i2c4_dev; #endif #ifdef CONFIG_LPC54_I2C5_MASTER static struct lpc54_i2cdev_s g_i2c5_dev; #endif #ifdef CONFIG_LPC54_I2C6_MASTER static struct lpc54_i2cdev_s g_i2c6_dev; #endif #ifdef CONFIG_LPC54_I2C7_MASTER static struct lpc54_i2cdev_s g_i2c7_dev; #endif #ifdef CONFIG_LPC54_I2C8_MASTER static struct lpc54_i2cdev_s g_i2c8_dev; #endif #ifdef CONFIG_LPC54_I2C9_MASTER static struct lpc54_i2cdev_s g_i2c9_dev; #endif /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: lpc54_i2c_putreg * * Description: * Write a value to a register at the offset from the Flexcomm base. * ****************************************************************************/ static inline void lpc54_i2c_putreg(struct lpc54_i2cdev_s *priv, unsigned int regoffset, uint32_t regval) { putreg32(regval, priv->base + regoffset); } /**************************************************************************** * Name: lpc54_i2c_getreg * * Description: * Read the content of a register at the offset from the Flexcomm base. * ****************************************************************************/ static inline uint32_t lpc54_i2c_getreg(struct lpc54_i2cdev_s *priv, unsigned int regoffset) { return getreg32(priv->base + regoffset); } /**************************************************************************** * Name: lpc54_i2c_setfrequency * * Description: * Set the frequency for the next transfer * ****************************************************************************/ static void lpc54_i2c_setfrequency(struct lpc54_i2cdev_s *priv, uint32_t frequency) { uint32_t scl; uint32_t divider; uint32_t best_scl; uint32_t best_div; uint32_t err; uint32_t best_err; uint32_t regval; i2cinfo("frequency %ld (%ld)\n", (long)frequency, (long)priv->frequency); /* Has the I2C frequency changed? */ if (frequency != priv->frequency) { /* Yes.. instantiate the new I2C frequency */ best_err = 0; for (scl = 9; scl >= 2; scl--) { /* Calculate ideal divider value for the current scl candidate. * * SCL High Time: Thi = divider * SCLhi * SCL High Time: Tlo = divider * SCLlo * Fscl = Finput / (Thi + Tlo) * * If Thi == TloL: Fscl = Finput / (divider * SCL * 2) * Or: divider = Finput / (Fscl * SCL * 2) */ divider = priv->fclock / (frequency * scl * 2); /* Adjust it if it is out of range */ if (divider > 0x00010000) { divider = 0x00010000; } /* Calculate the frequency error */ err = priv->fclock - (frequency * scl * 2 * divider); if (err < best_err || best_err == 0) { best_div = divider; best_scl = scl; best_err = err; } if (err == 0 || divider >= 0x10000) { /* Break out of the loop early ifeither exact value was found * or the divider is at its maximum value. */ break; } } /* Instantiate the new I2C frequency */ regval = I2C_CLKDIV(best_div); lpc54_i2c_putreg(priv, LPC54_I2C_CLKDIV_OFFSET, regval); regval = I2C_MSTTIME_SCLLOW(best_scl) | I2C_MSTTIME_SCLHIGH(best_scl); lpc54_i2c_putreg(priv, LPC54_I2C_MSTTIME_OFFSET, regval); priv->frequency = frequency; } } /**************************************************************************** * Name: lpc54_i2c_timeout * * Description: * Watchdog timer for timeout of I2C operation * ****************************************************************************/ static void lpc54_i2c_timeout(wdparm_t arg) { struct lpc54_i2cdev_s *priv = (struct lpc54_i2cdev_s *)arg; #ifndef CONFIG_I2C_POLLED irqstate_t flags = enter_critical_section(); #endif i2cerr("ERROR: Timeout! state=%u\n", priv->state); /* Disable further I2C interrupts and return to the IDLE state with the * timeout result. */ lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS); priv->state = I2CSTATE_IDLE; if (priv->result == OK) { priv->result = -ETIMEDOUT; } #ifndef CONFIG_I2C_POLLED /* Wake up any waiters */ nxsem_post(&priv->waitsem); leave_critical_section(flags); #endif } /**************************************************************************** * Name: lpc54_i2c_xfrsetup * * Description: * Setup to initiate a transfer. * ****************************************************************************/ static void lpc54_i2c_xfrsetup(struct lpc54_i2cdev_s *priv) { struct i2c_msg_s *msg; DEBUGASSERT(priv != NULL && priv->msgs != NULL); msg = priv->msgs; /* Disable I2C interrupts while configuring for the transfer */ lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS); /* Set up for the transfer */ priv->xfrd = 0; /* Select the initial state */ if ((msg->flags & I2C_M_NOSTART) != 0) { /* Start condition will be omitted. Begin the transfer in the data * phase. */ if (msg->length == 0) { priv->state = I2CSTATE_STOP; } else if ((I2C_M_READ & msg->flags) == I2C_M_READ) { priv->state = I2CSTATE_RECEIVE; } else { priv->state = I2CSTATE_TRANSMIT; } } else { priv->state = I2CSTATE_START; } /* Set the I2C frequency if provided in this message. Otherwise, use the * current I2C frequency setting. */ if (msg->frequency > 0) { lpc54_i2c_setfrequency(priv, msg->frequency); } /* Clear error status bits */ lpc54_i2c_putreg(priv, LPC54_I2C_STAT_OFFSET, I2C_INT_MSTARBLOSS | I2C_INT_MSTSTSTPERR); #ifndef CONFIG_I2C_POLLED /* Enable I2C master interrupts */ lpc54_i2c_putreg(priv, LPC54_I2C_INTENSET_OFFSET, I2C_MASTER_INTS); #endif } /**************************************************************************** * Name: lpc54_i2c_nextmsg * * Description: * Called at the completion of each message. If there are more messages, * this function will perform the setup for the next message. * ****************************************************************************/ static bool lpc54_i2c_nextmsg(struct lpc54_i2cdev_s *priv) { irqstate_t flags; /* Disable interrupts to prevent the timeout while we make the decision * here. */ flags = enter_critical_section(); i2cinfo("nmsgs=%u\n", priv->nmsgs - 1); /* Decrement the number of messages remaining. */ if (--priv->nmsgs > 0 && priv->result == OK) { /* There are more messages, set up for the next message */ priv->msgs++; lpc54_i2c_xfrsetup(priv); i2cinfo("state=%u\n", priv->state); leave_critical_section(flags); return false; } else { /* That was the last message... we are done. * Cancel any timeout */ wd_cancel(&priv->timeout); /* Disable further I2C interrupts and return to the IDLE state */ lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS); priv->state = I2CSTATE_IDLE; i2cinfo("state=%u\n", priv->state); leave_critical_section(flags); return true; } } /**************************************************************************** * Name: lpc54_i2c_statemachine * * Description: * This is the I2C transfer state machine that implements the actual * transfer. It may be called from the interrupt level or is may be used * without interrupts in a polled mode. * ****************************************************************************/ static bool lpc54_i2c_statemachine(struct lpc54_i2cdev_s *priv) { struct i2c_msg_s *msg; uint32_t status; uint32_t mstate; DEBUGASSERT(priv != NULL && priv->msgs != NULL); msg = priv->msgs; i2cinfo("state=%u\n", priv->state); status = lpc54_i2c_getreg(priv, LPC54_I2C_STAT_OFFSET); if (status & I2C_INT_MSTARBLOSS) { i2cerr("ERROR: Arbitation loss\n"); lpc54_i2c_putreg(priv, LPC54_I2C_STAT_OFFSET, I2C_INT_MSTARBLOSS); priv->result = -EIO; return true; } if (status & I2C_INT_MSTSTSTPERR) { i2cerr("ERROR: Start/stop error\n"); lpc54_i2c_putreg(priv, LPC54_I2C_STAT_OFFSET, I2C_INT_MSTSTSTPERR); priv->result = -EIO; return true; } if ((status & I2C_INT_MSTPENDING) == 0) { i2cerr("ERROR: Busy\n"); priv->result = -EBUSY; return true; } /* Get the state of the I2C module */ mstate = (status & I2C_STAT_MSTSTATE_MASK) >> I2C_STAT_MSTSTATE_SHIFT; i2cinfo("mstate=%u\n", mstate); if ((mstate == I2C_MASTER_STATE_ADDRNAK) || (mstate == I2C_MASTER_STATE_DATANAK)) { /* Slave NACKed last byte, issue stop and return error */ lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET, I2C_MSTCTL_MSTSTOP); priv->result = -EPERM; priv->state = I2CSTATE_WAITSTOP; i2cerr("ERROR: NAKed, state=%u\n", priv->state); return false; } switch (priv->state) { case I2CSTATE_START: { enum lpc54_i2cstate_e newstate; if ((msg->flags & I2C_M_READ) == I2C_M_READ) { lpc54_i2c_putreg(priv, LPC54_I2C_MSTDAT_OFFSET, I2C_READADDR8(msg->addr)); newstate = I2CSTATE_RECEIVE; } else { lpc54_i2c_putreg(priv, LPC54_I2C_MSTDAT_OFFSET, I2C_WRITEADDR8(msg->addr)); newstate = I2CSTATE_TRANSMIT; } if (priv->xfrd >= msg->length) { /* No more data, setup for STOP */ newstate = I2CSTATE_STOP; } priv->state = newstate; /* Send START condition */ lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET, I2C_MSTCTL_MSTSTART); } break; case I2CSTATE_TRANSMIT: { if (mstate != I2C_MASTER_STATE_TXOK) { i2cerr("ERROR bad state=%u, expected %u\n", mstate, I2C_MASTER_STATE_TXOK); priv->result = -EINVAL; return true; } lpc54_i2c_putreg(priv, LPC54_I2C_MSTDAT_OFFSET, msg->buffer[priv->xfrd]); lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET, I2C_MSTCTL_MSTCONTINUE); priv->xfrd++; if (priv->xfrd >= msg->length) { /* No more data, schedule stop condition */ priv->state = I2CSTATE_STOP; } } break; case I2CSTATE_RECEIVE: { if (mstate != I2C_MASTER_STATE_RXAVAIL) { i2cerr("ERROR bad state=%u, expected %u\n", mstate, I2C_MASTER_STATE_RXAVAIL); priv->result = -EINVAL; return true; } msg->buffer[priv->xfrd] = lpc54_i2c_getreg(priv, LPC54_I2C_MSTDAT_OFFSET); priv->xfrd++; if (priv->xfrd < msg->length) { lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET, I2C_MSTCTL_MSTCONTINUE); } else { /* No more data expected, issue NACK and STOP right away */ lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET, I2C_MSTCTL_MSTSTOP); priv->state = I2CSTATE_WAITSTOP; } } break; case I2CSTATE_STOP: { bool dostop = true; /* Is this the last message? */ if (priv->nmsgs > 1) { struct i2c_msg_s *nextmsg; /* No.. Is there a start on the next message? If so, it * should be preceded by a STOP. */ nextmsg = msg + 1; dostop = ((nextmsg->flags & I2C_M_NOSTART) != 0); } if (dostop) { /* Stop condition is omitted, we are done. Start the next * message (or return to the IDLE state if none). */ return lpc54_i2c_nextmsg(priv); } else { /* Send stop condition */ lpc54_i2c_putreg(priv, LPC54_I2C_MSTCTL_OFFSET, I2C_MSTCTL_MSTSTOP); priv->state = I2CSTATE_WAITSTOP; } } break; case I2CSTATE_WAITSTOP: { /* Start the next message (or return to the IDLE state if none). */ return lpc54_i2c_nextmsg(priv); } break; case I2CSTATE_IDLE: default: priv->result = -EINVAL; return true; } i2cinfo("state=%u\n", priv->state); return false; } /**************************************************************************** * Name: lpc54_i2c_interrupt * * Description: * The I2C Interrupt Handler * ****************************************************************************/ #ifndef CONFIG_I2C_POLLED static int lpc54_i2c_interrupt(int irq, FAR void *context, FAR void *arg) { struct lpc54_i2cdev_s *priv = (struct lpc54_i2cdev_s *)arg; bool done; DEBUGASSERT(priv != NULL); /* Run the I2C state machine */ done = lpc54_i2c_statemachine(priv); if (done) { /* Disable further I2C interrupts. */ lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS); /* Wake up wake up any waiters */ nxsem_post(&priv->waitsem); } return OK; } #endif /* CONFIG_I2C_POLLED */ /**************************************************************************** * Name: lpc54_i2c_transfer * * Description: * Perform a sequence of I2C transfers * ****************************************************************************/ static int lpc54_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s *msgs, int count) { struct lpc54_i2cdev_s *priv = (struct lpc54_i2cdev_s *)dev; int ret; i2cinfo("count=%d\n", count); DEBUGASSERT(dev != NULL && msgs != NULL); /* Get exclusive access to the I2C bus */ nxsem_wait(&priv->exclsem); /* Set up for the transfer */ priv->xfrd = 0; priv->msgs = msgs; priv->nmsgs = count; priv->result = OK; /* Set up the transfer timeout */ wd_start(&priv->timeout, priv->nmsgs * I2C_WDOG_TIMEOUT, lpc54_i2c_timeout, (wdparm_t)priv); /* Initiate the transfer */ lpc54_i2c_xfrsetup(priv); /* Loop until the transfer is complete or until a timeout occurs */ do { #ifndef CONFIG_I2C_POLLED nxsem_wait(&priv->waitsem); #else lpc54_i2c_statemachine(priv); #endif } while (priv->state != I2CSTATE_IDLE); ret = priv->result; i2cinfo("Done, result=%d\n", ret); nxsem_post(&priv->exclsem); return ret; } /**************************************************************************** * Name: lpc54_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 lpc54_i2c_reset(FAR struct i2c_master_s * dev) { #warning Missing logic return OK; } #endif /* CONFIG_I2C_RESET */ /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: lpc54_i2cbus_initialize * * Description: * Initialise an I2C device * ****************************************************************************/ struct i2c_master_s *lpc54_i2cbus_initialize(int port) { struct lpc54_i2cdev_s *priv; irqstate_t flags; uint32_t regval; i2cinfo("port=%d\n", port); flags = enter_critical_section(); /* Configure the requestin I2C peripheral */ /* NOTE: The basic FLEXCOMM initialization was performed in * lpc54_lowputc.c. */ #ifdef CONFIG_LPC54_I2C0_MASTER if (port == 0) { /* Attach 12 MHz clock to FLEXCOMM0 */ lpc54_flexcomm0_enableclk(); /* Set FLEXCOMM0 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM0_PSELID); /* Initialize the state structure */ priv = &g_i2c0_dev; priv->base = LPC54_FLEXCOMM0_BASE; priv->fclock = BOARD_FLEXCOMM0_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM0; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C0_SCL); lpc54_gpio_config(GPIO_I2C0_SDA); /* Set up the FLEXCOMM0 function clock */ putreg32(BOARD_FLEXCOMM0_CLKSEL, LPC54_SYSCON_FCLKSEL0); } else #endif #ifdef CONFIG_LPC54_I2C1_MASTER if (port == 1) { /* Attach 12 MHz clock to FLEXCOMM1 */ lpc54_flexcomm1_enableclk(); /* Set FLEXCOMM1 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM1_PSELID); /* Initialize the state structure */ priv = &g_i2c1_dev; priv->base = LPC54_FLEXCOMM1_BASE; priv->fclock = BOARD_FLEXCOMM1_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM1; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C1_SCL); lpc54_gpio_config(GPIO_I2C1_SDA); /* Set up the FLEXCOMM1 function clock */ putreg32(BOARD_FLEXCOMM1_CLKSEL, LPC54_SYSCON_FCLKSEL1); } else #endif #ifdef CONFIG_LPC54_I2C2_MASTER if (port == 2) { /* Attach 12 MHz clock to FLEXCOMM2 */ lpc54_flexcomm2_enableclk(); /* Set FLEXCOMM2 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM2_PSELID); /* Initialize the state structure */ priv = &g_i2c2_dev; priv->base = LPC54_FLEXCOMM2_BASE; priv->fclock = BOARD_FLEXCOMM2_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM2; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C2_SCL); lpc54_gpio_config(GPIO_I2C2_SDA); /* Set up the FLEXCOMM2 function clock */ putreg32(BOARD_FLEXCOMM2_CLKSEL, LPC54_SYSCON_FCLKSEL2); } else #endif #ifdef CONFIG_LPC54_I2C3_MASTER if (port == 3) { /* Attach 12 MHz clock to FLEXCOMM3 */ lpc54_flexcomm3_enableclk(); /* Set FLEXCOMM3 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM3_PSELID); /* Initialize the state structure */ priv = &g_i2c3_dev; priv->base = LPC54_FLEXCOMM3_BASE; priv->fclock = BOARD_FLEXCOMM3_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM3; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C3_SCL); lpc54_gpio_config(GPIO_I2C3_SDA); /* Set up the FLEXCOMM3 function clock */ putreg32(BOARD_FLEXCOMM3_CLKSEL, LPC54_SYSCON_FCLKSEL3); } else #endif #ifdef CONFIG_LPC54_I2C4_MASTER if (port == 4) { /* Attach 12 MHz clock to FLEXCOMM4 */ lpc54_flexcomm4_enableclk(); /* Set FLEXCOMM4 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM4_PSELID); /* Initialize the state structure */ priv = &g_i2c4_dev; priv->base = LPC54_FLEXCOMM4_BASE; priv->fclock = BOARD_FLEXCOMM4_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM4; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C4_SCL); lpc54_gpio_config(GPIO_I2C4_SDA); /* Set up the FLEXCOMM4 function clock */ putreg32(BOARD_FLEXCOMM4_CLKSEL, LPC54_SYSCON_FCLKSEL4); } else #endif #ifdef CONFIG_LPC54_I2C5_MASTER if (port == 5) { /* Attach 12 MHz clock to FLEXCOMM5 */ lpc54_flexcomm5_enableclk(); /* Set FLEXCOMM5 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM5_PSELID); /* Initialize the state structure */ priv = &g_i2c5_dev; priv->base = LPC54_FLEXCOMM5_BASE; priv->fclock = BOARD_FLEXCOMM5_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM5; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C5_SCL); lpc54_gpio_config(GPIO_I2C5_SDA); /* Set up the FLEXCOMM5 function clock */ putreg32(BOARD_FLEXCOMM5_CLKSEL, LPC54_SYSCON_FCLKSEL5); } else #endif #ifdef CONFIG_LPC54_I2C6_MASTER if (port == 6) { /* Attach 12 MHz clock to FLEXCOMM6 */ lpc54_flexcomm6_enableclk(); /* Set FLEXCOMM6 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM6_PSELID); /* Initialize the state structure */ priv = &g_i2c6_dev; priv->base = LPC54_FLEXCOMM6_BASE; priv->fclock = BOARD_FLEXCOMM6_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM6; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C6_SCL); lpc54_gpio_config(GPIO_I2C6_SDA); /* Set up the FLEXCOMM6 function clock */ putreg32(BOARD_FLEXCOMM6_CLKSEL, LPC54_SYSCON_FCLKSEL6); } else #endif #ifdef CONFIG_LPC54_I2C7_MASTER if (port == 7) { /* Attach 12 MHz clock to FLEXCOMM7 */ lpc54_flexcomm7_enableclk(); /* Set FLEXCOMM7 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM7_PSELID); /* Initialize the state structure */ priv = &g_i2c7_dev; priv->base = LPC54_FLEXCOMM7_BASE; priv->fclock = BOARD_FLEXCOMM7_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM7; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C7_SCL); lpc54_gpio_config(GPIO_I2C7_SDA); /* Set up the FLEXCOMM7 function clock */ putreg32(BOARD_FLEXCOMM7_CLKSEL, LPC54_SYSCON_FCLKSEL7); } else #endif #ifdef CONFIG_LPC54_I2C8_MASTER if (port == 8) { /* Attach 12 MHz clock to FLEXCOMM8 */ lpc54_flexcomm8_enableclk(); /* Set FLEXCOMM8 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM8_PSELID); /* Initialize the state structure */ priv = &g_i2c8_dev; priv->base = LPC54_FLEXCOMM8_BASE; priv->fclock = BOARD_FLEXCOMM8_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM8; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C8_SCL); lpc54_gpio_config(GPIO_I2C8_SDA); /* Set up the FLEXCOMM8 function clock */ putreg32(BOARD_FLEXCOMM8_CLKSEL, LPC54_SYSCON_FCLKSEL8); } else #endif #ifdef CONFIG_LPC54_I2C9_MASTER if (port == 9) { /* Attach 12 MHz clock to FLEXCOMM9 */ lpc54_flexcomm9_enableclk(); /* Set FLEXCOMM9 to the I2C peripheral, locking that configuration * in place. */ putreg32(FLEXCOMM_PSELID_PERSEL_I2C | FLEXCOMM_PSELID_LOCK, LPC54_FLEXCOMM9_PSELID); /* Initialize the state structure */ priv = &g_i2c9_dev; priv->base = LPC54_FLEXCOMM9_BASE; priv->fclock = BOARD_FLEXCOMM9_FCLK; #ifndef CONFIG_I2C_POLLED priv->irq = LPC54_IRQ_FLEXCOMM9; #endif /* Configure I2C pins (defined in board.h) */ lpc54_gpio_config(GPIO_I2C9_SCL); lpc54_gpio_config(GPIO_I2C9_SDA); /* Set up the FLEXCOMM9 function clock */ putreg32(BOARD_FLEXCOMM9_CLKSEL, LPC54_SYSCON_FCLKSEL9); } else #endif { i2cerr("ERROR: Unsupported port=%d\n", port); return NULL; } leave_critical_section(flags); /* Install our operations */ priv->dev.ops = &lpc54_i2c_ops; /* Enable the I2C peripheral in the master mode */ regval = lpc54_i2c_getreg(priv, LPC54_I2C_CFG_OFFSET); regval &= I2C_CFG_ALLENABLES; regval |= I2C_CFG_MSTEN; lpc54_i2c_putreg(priv, LPC54_I2C_CFG_OFFSET, regval); /* Set the default I2C frequency */ lpc54_i2c_setfrequency(priv, I2C_DEFAULT_FREQUENCY); /* Initialize semaphores */ nxsem_init(&priv->exclsem, 0, 1); #ifndef CONFIG_I2C_POLLED nxsem_init(&priv->waitsem, 0, 0); /* The waitsem semaphore is used for signaling and, hence, should not have * priority inheritance enabled. */ nxsem_set_protocol(&priv->waitsem, SEM_PRIO_NONE); #endif #ifndef CONFIG_I2C_POLLED /* Attach Interrupt Handler */ irq_attach(priv->irq, lpc54_i2c_interrupt, priv); #endif /* Disable interrupts at the I2C peripheral */ lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_INT_ALL); #ifndef CONFIG_I2C_POLLED /* Enable interrupts at the NVIC */ up_enable_irq(priv->irq); #endif return &priv->dev; } /**************************************************************************** * Name: lpc54_i2cbus_uninitialize * * Description: * Uninitialise an I2C device * ****************************************************************************/ int lpc54_i2cbus_uninitialize(FAR struct i2c_master_s * dev) { struct lpc54_i2cdev_s *priv = (struct lpc54_i2cdev_s *) dev; uint32_t regval; /* Disable I2C interrupts */ lpc54_i2c_putreg(priv, LPC54_I2C_INTENCLR_OFFSET, I2C_MASTER_INTS); /* Disable the I2C peripheral */ regval = lpc54_i2c_getreg(priv, LPC54_I2C_CFG_OFFSET); regval &= I2C_CFG_ALLENABLES; regval &= ~I2C_CFG_MSTEN; lpc54_i2c_putreg(priv, LPC54_I2C_CFG_OFFSET, regval); #ifndef CONFIG_I2C_POLLED /* Disable the Flexcomm interface at the NVIC and detach the interrupt. */ up_disable_irq(priv->irq); irq_detach(priv->irq); #endif return OK; } #endif /* HAVE_I2C_MASTER_DEVICE */