/************************************************************************************ * arch/arm/src/tiva/tiva_i2c.c * * Copyright (C) 2014 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * The basic structure of this driver derives in spirit (if nothing more) from the * NuttX STM32 I2C driver which has: * * Copyright (C) 2011 Uros Platise. All rights reserved. * Author: Uros Platise * * 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 #include "up_arch.h" #include "tiva_enablepwr.h" #include "tiva_enableclks.h" #include "tiva_gpio.h" #include "chip/tiva_pinmap.h" #include "chip/tiva_syscontrol.h" #include "tiva_i2c.h" /* At least one I2C peripheral must be enabled */ #if defined(CONFIG_TIVA_I2C0) || defined(CONFIG_TIVA_I2C1) || \ defined(CONFIG_TIVA_I2C2) || defined(CONFIG_TIVA_I2C3) || \ defined(CONFIG_TIVA_I2C4) || defined(CONFIG_TIVA_I2C5) || \ defined(CONFIG_TIVA_I2C6) || defined(CONFIG_TIVA_I2C7) || \ defined(CONFIG_TIVA_I2C8) || defined(CONFIG_TIVA_I2C9) /************************************************************************************ * Pre-processor Definitions ************************************************************************************/ /* Configuration ********************************************************************/ /* CONFIG_I2C_POLLED may be set so that I2C interrupts will not be used. Instead, * CPU-intensive polling will be used. */ /* Interrupt wait timeout in seconds and milliseconds */ #if !defined(CONFIG_TIVA_I2C_TIMEOSEC) && !defined(CONFIG_TIVA_I2C_TIMEOMS) # define CONFIG_TIVA_I2C_TIMEOSEC 0 # define CONFIG_TIVA_I2C_TIMEOMS 500 /* Default is 500 milliseconds */ #elif !defined(CONFIG_TIVA_I2C_TIMEOSEC) # define CONFIG_TIVA_I2C_TIMEOSEC 0 /* User provided milliseconds */ #elif !defined(CONFIG_TIVA_I2C_TIMEOMS) # define CONFIG_TIVA_I2C_TIMEOMS 0 /* User provided seconds */ #endif /* Interrupt wait time timeout in system timer ticks */ #ifndef CONFIG_TIVA_I2C_TIMEOTICKS # define CONFIG_TIVA_I2C_TIMEOTICKS \ (SEC2TICK(CONFIG_TIVA_I2C_TIMEOSEC) + MSEC2TICK(CONFIG_TIVA_I2C_TIMEOMS)) #endif #ifndef CONFIG_TIVA_I2C_DYNTIMEO_STARTSTOP # define CONFIG_TIVA_I2C_DYNTIMEO_STARTSTOP TICK2USEC(CONFIG_TIVA_I2C_TIMEOTICKS) #endif /* GPIO pins ************************************************************************/ /* Macros to convert a I2C pin to a GPIO output */ #define I2C_INPUT (GPIO_FUNC_INPUT) #define I2C_OUTPUT (GPIO_FUNC_ODOUTPUT | GPIO_PADTYPE_OD | GPIO_VALUE_ONE) #define MKI2C_INPUT(p) (((p) & (GPIO_PORT_MASK | GPIO_PIN_MASK)) | I2C_INPUT) #define MKI2C_OUTPUT(p) (((p) & (GPIO_PORT_MASK | GPIO_PIN_MASK)) | I2C_OUTPUT) /* Debug ****************************************************************************/ /* CONFIG_DEBUG_I2C + CONFIG_DEBUG enables general I2C debug output. */ #ifdef CONFIG_DEBUG_I2C # define i2cdbg dbg # define i2cvdbg vdbg #else # define i2cdbg(x...) # define i2cvdbg(x...) #endif #ifndef CONFIG_DEBUG # undef CONFIG_TIVA_I2C_REGDEBUG #endif /* I2C event trace logic. NOTE: trace uses the internal, non-standard, low-level * debug interface syslog() but does not require that any other debug * is enabled. */ #ifndef CONFIG_I2C_TRACE # define tiva_i2c_tracereset(p) # define tiva_i2c_tracenew(p,s) # define tiva_i2c_traceevent(p,e,a) # define tiva_i2c_tracedump(p) #endif #ifndef CONFIG_I2C_NTRACE # define CONFIG_I2C_NTRACE 32 #endif /************************************************************************************ * Private Types ************************************************************************************/ /* Interrupt state */ enum tiva_intstate_e { INTSTATE_IDLE = 0, /* No I2C activity */ INTSTATE_WAITING, /* Waiting for data transfer to complete */ INTSTATE_DONE /* Interrupt activity complete */ }; /* Trace events */ enum tiva_trace_e { I2CEVENT_NONE = 0, /* No events have occurred with this status */ I2CEVENT_SENDADDRESS, /* Address sent, param = address */ I2CEVENT_ERROR, /* Error occurred, param = MCS */ I2CEVENT_BUSY, /* Still busy, param = MCS */ I2CEVENT_XFRDONE, /* Transfer completed without error, param = mcnt */ I2CEVENT_RECVSETUP, /* Setup to receive the next byte, param = mcnt */ I2CEVENT_SENDBYTE, /* Send byte, param = mcnt */ I2CEVENT_SPURIOUS, /* Spurious interrupt received, param = msgc */ I2CEVENT_NEXTMSG, /* Starting next message, param = msgc */ I2CEVENT_TIMEOUT, /* Software detected timeout, param = RIS */ I2CEVENT_DONE /* All messages transferred, param = intstate */ }; /* Trace data */ struct tiva_trace_s { uint32_t status; /* I2C 32-bit SR2|SR1 status */ uint32_t count; /* Interrupt count when status change */ enum tiva_trace_e event; /* Last event that occurred with this status */ uint32_t parm; /* Parameter associated with the event */ uint32_t time; /* First of event or first status */ }; /* I2C Device hardware configuration */ struct tiva_i2c_config_s { uintptr_t base; /* I2C base address */ #ifndef TIVA_SYSCON_RCGCI2C uint32_t rcgbit; /* Bit in the RCG1 register to enable clocking */ #endif #ifndef TIVA_SYSCON_SRI2C uint32_t rstbit; /* Bit in the SRCR1 register to reset I2C */ #endif uint32_t scl_pin; /* GPIO configuration for SCL as SCL */ uint32_t sda_pin; /* GPIO configuration for SDA as SDA */ #ifndef CONFIG_I2C_POLLED int (*isr)(int, void *); /* Interrupt handler */ uint8_t irq; /* IRQ number */ #endif uint8_t devno; /* I2Cn where n = devno */ }; /* I2C Device Private Data */ struct tiva_i2c_priv_s { const struct tiva_i2c_config_s *config; /* Port configuration */ sem_t exclsem; /* Mutual exclusion semaphore */ #ifndef CONFIG_I2C_POLLED sem_t waitsem; /* Interrupt wait semaphore */ #endif uint8_t refs; /* Reference count */ volatile uint8_t intstate; /* Interrupt handshake (see enum tiva_intstate_e) */ uint8_t msgc; /* Message count */ struct i2c_msg_s *msgv; /* Message list */ uint8_t *mptr; /* Current message buffer */ int mcnt; /* Current message length */ uint16_t mflags; /* Current message flags */ uint32_t mstatus; /* MCS register at the end of the transfer */ #ifdef CONFIG_TIVA_I2C_REGDEBUG /* Register level debug */ bool wrlast; /* Last was a write */ uintptr_t addrlast; /* Last address */ uint32_t vallast; /* Last value */ int ntimes; /* Number of times */ #endif #ifdef CONFIG_I2C_TRACE /* I2C trace support */ int tndx; /* Trace array index */ int tcount; /* Number of events with this status */ uint32_t ttime; /* Time when the trace was started */ uint32_t tstatus; /* Last status read */ /* The actual trace data */ struct tiva_trace_s trace[CONFIG_I2C_NTRACE]; #endif }; /* I2C Device, Instance */ struct tiva_i2c_inst_s { const struct i2c_ops_s *ops; /* Standard I2C operations */ struct tiva_i2c_priv_s *priv; /* Common driver private data structure */ uint32_t frequency; /* Frequency used in this instantiation */ uint16_t address; /* Address used in this instantiation */ uint16_t flags; /* Flags used in this instantiation */ }; /************************************************************************************ * Private Function Prototypes ************************************************************************************/ #ifdef CONFIG_TIVA_I2C_REGDEBUG static bool tiva_i2c_checkreg(struct tiva_i2c_priv_s *priv, bool wr, uint32_t regval, uintptr_t regaddr); static uint32_t tiva_i2c_getreg(struct tiva_i2c_priv_s *priv, unsigned int offset); static void tiva_i2c_putreg(struct tiva_i2c_priv_s *priv, unsigned int offset, uint32_t value); #else static inline uint32_t tiva_i2c_getreg(struct tiva_i2c_priv_s *priv, unsigned int offset); static inline void tiva_i2c_putreg(struct tiva_i2c_priv_s *priv, unsigned int offset, uint32_t value); #endif static inline void tiva_i2c_sem_wait(struct i2c_dev_s *dev); #ifdef CONFIG_TIVA_I2C_DYNTIMEO static useconds_t tiva_i2c_tousecs(int msgc, struct i2c_msg_s *msgv); #endif /* CONFIG_TIVA_I2C_DYNTIMEO */ static inline int tiva_i2c_sem_waitdone(struct tiva_i2c_priv_s *priv); static inline void tiva_i2c_sem_post(struct i2c_dev_s *dev); static inline void tiva_i2c_sem_init(struct i2c_dev_s *dev); static inline void tiva_i2c_sem_destroy(struct i2c_dev_s *dev); #ifdef CONFIG_I2C_TRACE static void tiva_i2c_tracereset(struct tiva_i2c_priv_s *priv); static void tiva_i2c_tracenew(struct tiva_i2c_priv_s *priv, uint32_t status); static void tiva_i2c_traceevent(struct tiva_i2c_priv_s *priv, enum tiva_trace_e event, uint32_t parm); static void tiva_i2c_tracedump(struct tiva_i2c_priv_s *priv); #endif /* CONFIG_I2C_TRACE */ static void tiva_i2c_startxfr(struct tiva_i2c_priv_s *priv); static void tiva_i2c_nextxfr(struct tiva_i2c_priv_s *priv, uint32_t cmd); static int tiva_i2c_interrupt(struct tiva_i2c_priv_s * priv, uint32_t status); #ifndef CONFIG_I2C_POLLED #ifdef CONFIG_TIVA_I2C0 static int tiva_i2c0_interrupt(int irq, void *context); #endif #ifdef CONFIG_TIVA_I2C1 static int tiva_i2c1_interrupt(int irq, void *context); #endif #ifdef CONFIG_TIVA_I2C2 static int tiva_i2c2_interrupt(int irq, void *context); #endif #ifdef CONFIG_TIVA_I2C3 static int tiva_i2c3_interrupt(int irq, void *context); #endif #ifdef CONFIG_TIVA_I2C4 static int tiva_i2c4_interrupt(int irq, void *context); #endif #ifdef CONFIG_TIVA_I2C5 static int tiva_i2c5_interrupt(int irq, void *context); #endif #ifdef CONFIG_TIVA_I2C6 static int tiva_i2c6_interrupt(int irq, void *context); #endif #ifdef CONFIG_TIVA_I2C7 static int tiva_i2c7_interrupt(int irq, void *context); #endif #ifdef CONFIG_TIVA_I2C8 static int tiva_i2c8_interrupt(int irq, void *context); #endif #ifdef CONFIG_TIVA_I2C9 static int tiva_i2c9_interrupt(int irq, void *context); #endif #endif /* !CONFIG_I2C_POLLED */ static int tiva_i2c_initialize(struct tiva_i2c_priv_s *priv, uint32_t frequency); static int tiva_i2c_uninitialize(struct tiva_i2c_priv_s *priv); static uint32_t tiva_i2c_setclock(struct tiva_i2c_priv_s *priv, uint32_t frequency); static uint32_t tiva_i2c_setfrequency(struct i2c_dev_s *dev, uint32_t frequency); static int tiva_i2c_setaddress(struct i2c_dev_s *dev, int addr, int nbits); static int tiva_i2c_process(struct i2c_dev_s *dev, struct i2c_msg_s *msgv, int msgc); static int tiva_i2c_write(struct i2c_dev_s *dev, const uint8_t *buffer, int buflen); static int tiva_i2c_read(struct i2c_dev_s *dev, uint8_t *buffer, int buflen); #ifdef CONFIG_I2C_WRITEREAD static int tiva_i2c_writeread(struct i2c_dev_s *dev, const uint8_t *wbuffer, int wbuflen, uint8_t *buffer, int buflen); #endif /* CONFIG_I2C_WRITEREAD */ #ifdef CONFIG_I2C_TRANSFER static int tiva_i2c_transfer(struct i2c_dev_s *dev, struct i2c_msg_s *msgv, int msgc); #endif /* CONFIG_I2C_TRANSFER */ /************************************************************************************ * Private Data ************************************************************************************/ #ifdef CONFIG_TIVA_I2C0 static const struct tiva_i2c_config_s tiva_i2c0_config = { .base = TIVA_I2C0_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C0, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C0, #endif .scl_pin = GPIO_I2C0_SCL, .sda_pin = GPIO_I2C0_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c0_interrupt, .irq = TIVA_IRQ_I2C0, #endif .devno = 0, }; static struct tiva_i2c_priv_s tiva_i2c0_priv; #endif #ifdef CONFIG_TIVA_I2C1 static const struct tiva_i2c_config_s tiva_i2c1_config = { .base = TIVA_I2C1_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C1, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C1, #endif .scl_pin = GPIO_I2C1_SCL, .sda_pin = GPIO_I2C1_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c1_interrupt, .irq = TIVA_IRQ_I2C1, #endif .devno = 1, }; static struct tiva_i2c_priv_s tiva_i2c1_priv; #endif #ifdef CONFIG_TIVA_I2C2 static const struct tiva_i2c_config_s tiva_i2c2_config = { .base = TIVA_I2C2_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C2, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C2, #endif .scl_pin = GPIO_I2C2_SCL, .sda_pin = GPIO_I2C2_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c2_interrupt, .irq = TIVA_IRQ_I2C2, #endif .devno = 2, }; static struct tiva_i2c_priv_s tiva_i2c2_priv; #endif #ifdef CONFIG_TIVA_I2C3 static const struct tiva_i2c_config_s tiva_i2c3_config = { .base = TIVA_I2C3_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C3, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C3, #endif .scl_pin = GPIO_I2C3_SCL, .sda_pin = GPIO_I2C3_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c3_interrupt, .irq = TIVA_IRQ_I2C3, #endif .devno = 3, }; static struct tiva_i2c_priv_s tiva_i2c3_priv; #endif #ifdef CONFIG_TIVA_I2C4 static const struct tiva_i2c_config_s tiva_i2c4_config = { .base = TIVA_I2C4_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C4, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C4, #endif .scl_pin = GPIO_I2C4_SCL, .sda_pin = GPIO_I2C4_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c4_interrupt, .irq = TIVA_IRQ_I2C4, #endif .devno = 4, }; static struct tiva_i2c_priv_s tiva_i2c4_priv; #endif #ifdef CONFIG_TIVA_I2C5 static const struct tiva_i2c_config_s tiva_i2c5_config = { .base = TIVA_I2C5_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C5, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C5, #endif .scl_pin = GPIO_I2C5_SCL, .sda_pin = GPIO_I2C5_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c5_interrupt, .irq = TIVA_IRQ_I2C5, #endif .devno = 5, }; static struct tiva_i2c_priv_s tiva_i2c5_priv; #endif #ifdef CONFIG_TIVA_I2C6 static const struct tiva_i2c_config_s tiva_i2c6_config = { .base = TIVA_I2C6_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C6, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C6, #endif .scl_pin = GPIO_I2C6_SCL, .sda_pin = GPIO_I2C6_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c6_interrupt, .irq = TIVA_IRQ_I2C6, #endif .devno = 6, }; static struct tiva_i2c_priv_s tiva_i2c6_priv; #endif #ifdef CONFIG_TIVA_I2C7 static const struct tiva_i2c_config_s tiva_i2c7_config = { .base = TIVA_I2C7_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C7, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C7, #endif .scl_pin = GPIO_I2C7_SCL, .sda_pin = GPIO_I2C7_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c7_interrupt, .irq = TIVA_IRQ_I2C7, #endif .devno = 7, }; static struct tiva_i2c_priv_s tiva_i2c7_priv; #endif #ifdef CONFIG_TIVA_I2C8 static const struct tiva_i2c_config_s tiva_i2c8_config = { .base = TIVA_I2C8_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C8, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C8, #endif .scl_pin = GPIO_I2C8_SCL, .sda_pin = GPIO_I2C8_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c8_interrupt, .irq = TIVA_IRQ_I2C8, #endif .devno = 8, }; static struct tiva_i2c_priv_s tiva_i2c8_priv; #endif #ifdef CONFIG_TIVA_I2C9 static const struct tiva_i2c_config_s tiva_i2c9_config = { .base = TIVA_I2C9_BASE, #ifndef TIVA_SYSCON_RCGCI2C .rcgbit = SYSCON_RCGC1_I2C9, #endif #ifndef TIVA_SYSCON_SRI2C .rstbit = SYSCON_SRCR1_I2C9, #endif .scl_pin = GPIO_I2C9_SCL, .sda_pin = GPIO_I2C9_SDA, #ifndef CONFIG_I2C_POLLED .isr = tiva_i2c9_interrupt, .irq = TIVA_IRQ_I2C9, #endif .devno = 9, }; static struct tiva_i2c_priv_s tiva_i2c9_priv; #endif /* Device Structures, Instantiation */ static const struct i2c_ops_s tiva_i2c_ops = { .setfrequency = tiva_i2c_setfrequency, .setaddress = tiva_i2c_setaddress, .write = tiva_i2c_write, .read = tiva_i2c_read #ifdef CONFIG_I2C_WRITEREAD , .writeread = tiva_i2c_writeread #endif #ifdef CONFIG_I2C_TRANSFER , .transfer = tiva_i2c_transfer #endif #ifdef CONFIG_I2C_SLAVE , .setownaddress = tiva_i2c_setownaddress, .registercallback = tiva_i2c_registercallback #endif }; /************************************************************************************ * Private Functions ************************************************************************************/ /************************************************************************************ * Name: sam_checkreg * * Description: * Check if the current register access is a duplicate of the preceding. * * Input Parameters: * regval - The value to be written * regaddr - The address of the register to write to * * Returned Value: * true: This is the first register access of this type. * flase: This is the same as the preceding register access. * ************************************************************************************/ #ifdef CONFIG_TIVA_I2C_REGDEBUG static bool tiva_i2c_checkreg(struct tiva_i2c_priv_s *priv, bool wr, uint32_t regval, uintptr_t regaddr) { if (wr == priv->wrlast && /* Same kind of access? */ regval == priv->vallast && /* Same value? */ regaddr == priv->addrlast) /* Same address? */ { /* Yes, then just keep a count of the number of times we did this. */ priv->ntimes++; return false; } else { /* Did we do the previous operation more than once? */ if (priv->ntimes > 0) { /* Yes... show how many times we did it */ lldbg("...[Repeats %d times]...\n", priv->ntimes); } /* Save information about the new access */ priv->wrlast = wr; priv->vallast = regval; priv->addrlast = regaddr; priv->ntimes = 0; } /* Return true if this is the first time that we have done this operation */ return true; } #endif /************************************************************************************ * Name: tiva_i2c_getreg * * Description: * Get a 16-bit register value by offset * ************************************************************************************/ #ifdef CONFIG_TIVA_I2C_REGDEBUG static uint32_t tiva_i2c_getreg(struct tiva_i2c_priv_s *priv, unsigned int offset) { uintptr_t regaddr = priv->config->base + offset; uint32_t regval = getreg32(regaddr); if (tiva_i2c_checkreg(priv, false, regval, regaddr)) { lldbg("%08x->%08x\n", regaddr, regval); } return regval; } #else static inline uint32_t tiva_i2c_getreg(struct tiva_i2c_priv_s *priv, unsigned int offset) { return getreg32(priv->config->base + offset); } #endif /************************************************************************************ * Name: tiva_i2c_putreg * * Description: * Put a 16-bit register value by offset * ************************************************************************************/ #ifdef CONFIG_TIVA_I2C_REGDEBUG static void tiva_i2c_putreg(struct tiva_i2c_priv_s *priv, unsigned int offset, uint32_t regval) { uintptr_t regaddr = priv->config->base + offset; if (tiva_i2c_checkreg(priv, true, regval, regaddr)) { lldbg("%08x<-%08x\n", regaddr, regval); } putreg32(regval, regaddr); } #else static inline void tiva_i2c_putreg(struct tiva_i2c_priv_s *priv, unsigned int offset, uint32_t regval) { putreg32(regval, priv->config->base + offset); } #endif /************************************************************************************ * Name: tiva_i2c_sem_wait * * Description: * Take the exclusive access, waiting as necessary * ************************************************************************************/ static inline void tiva_i2c_sem_wait(struct i2c_dev_s *dev) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; while (sem_wait(&inst->priv->exclsem) != 0) { ASSERT(errno == EINTR); } } /************************************************************************************ * Name: tiva_i2c_tousecs * * Description: * Return a micro-second delay based on the number of bytes left to be processed. * ************************************************************************************/ #ifdef CONFIG_TIVA_I2C_DYNTIMEO static useconds_t tiva_i2c_tousecs(int msgc, struct i2c_msg_s *msgv) { size_t bytecount = 0; int i; /* Count the number of bytes left to process */ for (i = 0; i < msgc; i++) { bytecount += msgv[i].length; } /* Then return a number of microseconds based on a user provided scaling * factor. */ return (useconds_t)(CONFIG_TIVA_I2C_DYNTIMEO_USECPERBYTE * bytecount); } #endif /************************************************************************************ * Name: tiva_i2c_sem_waitdone * * Description: * Wait for a transfer to complete * ************************************************************************************/ #ifndef CONFIG_I2C_POLLED static inline int tiva_i2c_sem_waitdone(struct tiva_i2c_priv_s *priv) { struct timespec abstime; irqstate_t flags; int ret; flags = irqsave(); /* Enable the master interrupt. The I2C master module generates an interrupt when * a transaction completes (either transmit or receive), when arbitration is lost, * or when an error occurs during a transaction. */ tiva_i2c_putreg(priv, TIVA_I2CM_IMR_OFFSET, I2CM_IMR_MIM); /* Signal the interrupt handler that we are waiting. NOTE: Interrupts * are currently disabled but will be temporarily re-enabled below when * sem_timedwait() sleeps. */ do { /* Get the current time */ (void)clock_gettime(CLOCK_REALTIME, &abstime); /* Calculate a time in the future */ #if CONFIG_TIVA_I2C_TIMEOSEC > 0 abstime.tv_sec += CONFIG_TIVA_I2C_TIMEOSEC; #endif /* Add a value proportional to the number of bytes in the transfer */ #ifdef CONFIG_TIVA_I2C_DYNTIMEO abstime.tv_nsec += 1000 * tiva_i2c_tousecs(priv->msgc, priv->msgv); if (abstime.tv_nsec > 1000 * 1000 * 1000) { abstime.tv_sec++; abstime.tv_nsec -= 1000 * 1000 * 1000; } #elif CONFIG_TIVA_I2C_TIMEOMS > 0 abstime.tv_nsec += CONFIG_TIVA_I2C_TIMEOMS * 1000 * 1000; if (abstime.tv_nsec > 1000 * 1000 * 1000) { abstime.tv_sec++; abstime.tv_nsec -= 1000 * 1000 * 1000; } #endif /* Wait until either the transfer is complete or the timeout expires */ ret = sem_timedwait(&priv->waitsem, &abstime); if (ret != OK && errno != EINTR) { /* Break out of the loop on irrecoverable errors. This would * include timeouts and mystery errors reported by sem_timedwait. * NOTE that we try again if we are awakened by a signal (EINTR). */ tiva_i2c_traceevent(priv, I2CEVENT_TIMEOUT, tiva_i2c_getreg(priv, TIVA_I2CM_RIS_OFFSET)); break; } } /* Loop until the interrupt level transfer is complete. */ while (priv->intstate != INTSTATE_DONE); /* Set the interrupt state back to IDLE */ priv->intstate = INTSTATE_IDLE; /* Disable I2C interrupts */ tiva_i2c_putreg(priv, TIVA_I2CM_IMR_OFFSET, 0); irqrestore(flags); return ret; } #else static inline int tiva_i2c_sem_waitdone(struct tiva_i2c_priv_s *priv) { uint32_t timeout; uint32_t start; uint32_t elapsed; uint32_t status; int ret; /* Get the timeout value */ #ifdef CONFIG_TIVA_I2C_DYNTIMEO timeout = USEC2TICK(tiva_i2c_tousecs(priv->msgc, priv->msgv)); #else timeout = CONFIG_TIVA_I2C_TIMEOTICKS; #endif /* Signal the interrupt handler that we are waiting. NOTE: Interrupts * are currently disabled but will be temporarily re-enabled below when * sem_timedwait() sleeps. */ start = clock_systimer(); do { /* Read the raw interrupt status */ status = tiva_i2c_getreg(priv, TIVA_I2CM_RIS_OFFSET); /* Poll by simply calling the timer interrupt handler with the raw * interrupt status until it reports that it is done. */ tiva_i2c_interrupt(priv, status); /* Calculate the elapsed time */ elapsed = clock_systimer() - start; } /* Loop until the transfer is complete. */ while (priv->intstate != INTSTATE_DONE && elapsed < timeout); i2cvdbg("intstate: %d elapsed: %d threshold: %d status: %08x\n", priv->intstate, elapsed, timeout, status); /* Set the interrupt state back to IDLE */ ret = priv->intstate == INTSTATE_DONE ? OK : -ETIMEDOUT; priv->intstate = INTSTATE_IDLE; return ret; } #endif /************************************************************************************ * Name: tiva_i2c_sem_post * * Description: * Release the mutual exclusion semaphore * ************************************************************************************/ static inline void tiva_i2c_sem_post(struct i2c_dev_s *dev) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; sem_post(&inst->priv->exclsem); } /************************************************************************************ * Name: tiva_i2c_sem_init * * Description: * Initialize semaphores * ************************************************************************************/ static inline void tiva_i2c_sem_init(struct i2c_dev_s *dev) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; sem_init(&inst->priv->exclsem, 0, 1); #ifndef CONFIG_I2C_POLLED sem_init(&inst->priv->waitsem, 0, 0); #endif } /************************************************************************************ * Name: tiva_i2c_sem_destroy * * Description: * Destroy semaphores. * ************************************************************************************/ static inline void tiva_i2c_sem_destroy(struct i2c_dev_s *dev) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; sem_destroy(&inst->priv->exclsem); #ifndef CONFIG_I2C_POLLED sem_destroy(&inst->priv->waitsem); #endif } /************************************************************************************ * Name: tiva_i2c_trace * * Description: * I2C trace instrumentation * ************************************************************************************/ #ifdef CONFIG_I2C_TRACE static void tiva_i2c_traceclear(struct tiva_i2c_priv_s *priv) { struct tiva_trace_s *trace = &priv->trace[priv->tndx]; trace->status = 0; /* I2C 32-bit SR2|SR1 status */ trace->count = 0; /* Interrupt count when status change */ trace->event = I2CEVENT_NONE; /* Last event that occurred with this status */ trace->parm = 0; /* Parameter associated with the event */ trace->time = 0; /* Time of first status or event */ } static void tiva_i2c_tracereset(struct tiva_i2c_priv_s *priv) { /* Reset the trace info for a new data collection */ priv->tndx = 0; priv->tcount = 0; priv->ttime = clock_systimer(); priv->tstatus = 0; tiva_i2c_traceclear(priv); } static void tiva_i2c_tracenew(struct tiva_i2c_priv_s *priv, uint32_t status) { struct tiva_trace_s *trace = &priv->trace[priv->tndx]; /* Is the current entry uninitialized? Has the status changed? */ if (trace->count == 0 || status != trace->status) { /* Yes.. Was it the status changed? */ if (trace->count != 0) { /* Yes.. bump up the trace index (unless we are out of trace entries) */ if (priv->tndx >= (CONFIG_I2C_NTRACE-1)) { i2cdbg("I2C%d: ERROR: Trace table overflow\n", priv->config->devno); return; } priv->tndx++; trace = &priv->trace[priv->tndx]; } /* Initialize the new trace entry */ tiva_i2c_traceclear(priv); trace->status = status; trace->count = 1; trace->time = clock_systimer(); /* Save the status and reset the count */ priv->tstatus = status; priv->tcount = 1; } else { /* Just increment the count of times that we have seen this status */ trace->count++; } } static void tiva_i2c_traceevent(struct tiva_i2c_priv_s *priv, enum tiva_trace_e event, uint32_t parm) { struct tiva_trace_s *trace; if (event != I2CEVENT_NONE) { trace = &priv->trace[priv->tndx]; if (trace->event != event) { /* Initialize the new trace entry */ trace->event = event; trace->parm = parm; trace->count = priv->tcount; trace->time = clock_systimer(); /* Bump up the trace index (unless we are out of trace entries) */ if (priv->tndx >= (CONFIG_I2C_NTRACE-1)) { i2cdbg("I2C%d: ERROR: Trace table overflow\n", priv->config->devno); return; } priv->tndx++; priv->tcount++; tiva_i2c_traceclear(priv); trace = &priv->trace[priv->tndx]; trace->status = priv->tstatus; trace->count = priv->tcount; trace->time = clock_systimer(); } else { priv->tcount++; } } } static void tiva_i2c_tracedump(struct tiva_i2c_priv_s *priv) { struct tiva_trace_s *trace; int i; syslog(LOG_DEBUG, "Elapsed time: %d\n", clock_systimer() - priv->ttime); for (i = 0; i <= priv->tndx; i++) { trace = &priv->trace[i]; syslog(LOG_DEBUG, "%2d. STATUS: %08x COUNT: %3d EVENT: %2d PARM: %08x TIME: %d\n", i+1, trace->status, trace->count, trace->event, trace->parm, trace->time - priv->ttime); } } #endif /* CONFIG_I2C_TRACE */ /************************************************************************************ * Name: tiva_i2c_startxfr * * Description: * Send the START conditions/force Master mode * ************************************************************************************/ static void tiva_i2c_startxfr(struct tiva_i2c_priv_s *priv) { struct i2c_msg_s *msg; uint32_t regval; DEBUGASSERT(priv && priv->msgc > 0); /* Get run-time data for the next message */ msg = priv->msgv; priv->mptr = msg->buffer; priv->mcnt = msg->length; priv->mflags = msg->flags; /* Set the Master Slave Address */ regval = (uint32_t)msg->addr << I2CM_SA_SA_SHIFT; if ((msg->flags & I2C_M_READ) != 0) { regval |= I2CM_SA_RS; } tiva_i2c_putreg(priv, TIVA_I2CM_SA_OFFSET, regval); tiva_i2c_traceevent(priv, I2CEVENT_SENDADDRESS, msg->addr); /* Then initiate the transfer */ tiva_i2c_nextxfr(priv, I2CM_CS_START); } /************************************************************************************ * Name: tiva_i2c_nextxfr * * Description: * Common Interrupt Service Routine * ************************************************************************************/ static void tiva_i2c_nextxfr(struct tiva_i2c_priv_s *priv, uint32_t cmd) { /* Set up the basic command. The STOP bit should be set on the last byte transfer. * * - CASE 1: If this is the last message in the sequence, then the stop bit should * always be set. * - CASE 2.1.1: The next message may be another read or write of the SAME * direction (read or write) and to the SAME address WITHOUT repeated start, in * which case this is really just a continuation of the message. No STOP is * needed. * - CASE 2.x.2: The next message may be to the SAME address WITH repeated start. * Because the repeated start, a direction change is possible. This is still * a continuation of the same message sequence and so no STOP is needed. * - CASE 2.2.x: The next message may be a DIFFERENT address WITHOUT repeated * start. This would be an error; The STOP will be sent, the next message will * fail. */ cmd |= I2CM_CS_RUN; if (priv->mcnt < 2) { /* Are there more messages in this sequence? */ if (priv->msgc < 2) { /* No.. send the STOP */ cmd |= I2CM_CS_STOP; } else { /* Yes.. peek at the next message */ struct i2c_msg_s *curr = priv->msgv; struct i2c_msg_s *next = curr + 1; /* Same address as the current message? */ if (curr->addr != next->addr) { /* No.. send the STOP */ cmd |= I2CM_CS_STOP; } } } /* Set up to transfer the next byte. Are we sending or receiving? */ if ((priv->mflags & I2C_M_READ) != 0) { /* We are receiving data. We need to ACK UNLESS we are going to send * STOP. */ if ((cmd & I2CM_CS_STOP) == 0) { cmd |= I2CM_CS_ACK; } /* Write the command to the control register to receive the next byte. */ tiva_i2c_putreg(priv, TIVA_I2CM_CS_OFFSET, cmd); tiva_i2c_traceevent(priv, I2CEVENT_RECVSETUP, priv->mcnt); } else { uint32_t dr; /* We are sending data. Write the data to be sent to the DR register. */ dr = (uint32_t)*priv->mptr++; tiva_i2c_putreg(priv, TIVA_I2CM_DR_OFFSET, dr << I2CM_DR_SHIFT); /* Write the command to the control register to send the byte in the DR * register. */ tiva_i2c_putreg(priv, TIVA_I2CM_CS_OFFSET, cmd); tiva_i2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->mcnt); } priv->intstate = INTSTATE_WAITING; } /************************************************************************************ * Name: tiva_i2c_interrupt * * Description: * Common Interrupt Service Routine * ************************************************************************************/ static int tiva_i2c_interrupt(struct tiva_i2c_priv_s *priv, uint32_t status) { /* Check for new trace setup */ tiva_i2c_tracenew(priv, status); /* Check for a master interrupt? The I2C master module generates an interrupt when * a transaction completes (either transmit or receive), when arbitration is lost, * or when an error occurs during a transaction. */ if ((status & I2CM_RIS_MRIS) != 0) { uint32_t mcs; #ifndef CONFIG_I2C_POLLED /* Clear the pending master interrupt */ tiva_i2c_putreg(priv, TIVA_I2CM_ICR_OFFSET, I2CM_ICR_MIC); status &= ~I2CM_RIS_MRIS; /* Workaround for I2C master interrupt clear errata for rev B Tiva * devices. For later devices, this write is ignored and therefore * harmless (other than the slight performance hit). */ (void)tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); #endif /* We need look at the Master Control/Status register to determine the cause * of the master interrupt. */ mcs = tiva_i2c_getreg(priv, TIVA_I2CM_CS_OFFSET); /* If the busy bit is set, then the other bits are not valid */ if ((mcs & I2CM_CS_BUSY) != 0) { tiva_i2c_traceevent(priv, I2CEVENT_BUSY, mcs); } /* Check for errors, in which case, stop the transfer and return. */ #if 0 /* I2CM_CS_CLKTO */ else if ((mcs & (I2CM_CS_ERROR | I2CM_CS_ARBLST | I2CM_CS_CLKTO)) != 0) #else else if ((mcs & (I2CM_CS_ERROR | I2CM_CS_ARBLST)) != 0) #endif { tiva_i2c_traceevent(priv, I2CEVENT_ERROR, mcs); /* Disable further interrupts */ tiva_i2c_putreg(priv, TIVA_I2CM_IMR_OFFSET, 0); #ifndef CONFIG_I2C_POLLED /* Is there a thread waiting for this event (there should be) */ if (priv->intstate != INTSTATE_IDLE && priv->intstate != INTSTATE_DONE) { /* Yes.. inform the thread that the transfer is complete * and wake it up. */ sem_post(&priv->waitsem); priv->mstatus = mcs; priv->intstate = INTSTATE_DONE; } #else priv->mstatus = mcs; priv->intstate = INTSTATE_DONE; #endif } /* Otherwise, the last transfer must have completed successfully */ else { int dr; tiva_i2c_traceevent(priv, I2CEVENT_XFRDONE, priv->mcnt); /* Read from the DR register */ dr = tiva_i2c_getreg(priv, TIVA_I2CM_DR_OFFSET); /* We check for msgc > 0 here as an unexpected interrupt with * due to noise on the I2C cable can otherwise cause msgc to * wrap causing memory overwrite */ if (priv->msgc > 0 && priv->msgv != NULL) { /* Was this the completion of an address or of the data portion * of the transfer? */ DEBUGASSERT(priv->mcnt > 0); if (priv->intstate == INTSTATE_WAITING) { /* Data transfer completed. Are we sending or receiving data? */ if ((priv->mflags & I2C_M_READ) != 0) { /* We are receiving data. Copy the received data to * the user buffer */ *priv->mptr++ = (uint8_t)dr; } /* Decrement the count of bytes remaining to be sent */ priv->mcnt--; } /* Was that the last byte of this message? */ if (priv->mcnt > 0) { /* Send the next byte */ tiva_i2c_nextxfr(priv, 0); } else { /* Increment to next pointer and decrement message count */ priv->msgv++; priv->msgc--; /* Is there another message to be sent? */ if (priv->msgc > 0) { /* Do we need to terminate or restart after this byte? * If there are more messages to send, then we may * continue with or without the (repeated) start bit. */ tiva_i2c_traceevent(priv, I2CEVENT_NEXTMSG, priv->msgc); if ((priv->msgv->flags & I2C_M_NORESTART) != 0) { /* Just continue transferring data. In this case, * no STOP was sent at the end of the last message * and the there is no new address. * * REVISIT: In this case, the address or the * direction of the transfer cannot be permitted to * change */ tiva_i2c_nextxfr(priv, 0); } else { /* Set the repeated start. No STOP was sent at the * end of the previous message. */ tiva_i2c_startxfr(priv); } } else { /* No.. then we are finished */ tiva_i2c_traceevent(priv, I2CEVENT_DONE, priv->intstate); /* Disable further interrupts */ tiva_i2c_putreg(priv, TIVA_I2CM_IMR_OFFSET, 0); #ifndef CONFIG_I2C_POLLED /* Is there a thread waiting for this event (there * should be) */ if (priv->intstate != INTSTATE_IDLE && priv->intstate != INTSTATE_DONE) { /* Yes.. inform the thread that the transfer is * complete and wake it up. */ sem_post(&priv->waitsem); priv->mstatus = 0; priv->intstate = INTSTATE_DONE; } #else priv->mstatus = 0; priv->intstate = INTSTATE_DONE; #endif } } } /* There is no pending message? Must be a spurious interrupt */ else { tiva_i2c_traceevent(priv, I2CEVENT_SPURIOUS, priv->msgc); } } } /* Make sure that all pending interrupts were handled */ #ifndef CONFIG_I2C_POLLED DEBUGASSERT(status == 0); #endif return OK; } /************************************************************************************ * Name: tiva_i2c0_interrupt * * Description: * I2C0 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C0) static int tiva_i2c0_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c0_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c1_interrupt * * Description: * I2C1 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C1) static int tiva_i2c1_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c1_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c2_interrupt * * Description: * I2C2 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C2) static int tiva_i2c2_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c2_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c3_interrupt * * Description: * I2C2 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C3) static int tiva_i2c3_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c3_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c4_interrupt * * Description: * I2C4 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C4) static int tiva_i2c4_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c4_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c5_interrupt * * Description: * I2C5 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C5) static int tiva_i2c5_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c5_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c6_interrupt * * Description: * I2C6 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C6) static int tiva_i2c6_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c6_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c7_interrupt * * Description: * I2C7 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C7) static int tiva_i2c7_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c7_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c8_interrupt * * Description: * I2C8 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C8) static int tiva_i2c8_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c8_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c9_interrupt * * Description: * I2C9 interrupt service routine * ************************************************************************************/ #if !defined(CONFIG_I2C_POLLED) && defined(CONFIG_TIVA_I2C9) static int tiva_i2c9_interrupt(int irq, void *context) { struct tiva_i2c_priv_s *priv; uint32_t status; /* Read the masked interrupt status */ priv = &tiva_i2c9_priv; status = tiva_i2c_getreg(priv, TIVA_I2CM_MIS_OFFSET); /* Let the common interrupt handler do the rest of the work */ return tiva_i2c_interrupt(priv, status); } #endif /************************************************************************************ * Name: tiva_i2c_initialize * * Description: * Setup the I2C hardware, ready for operation with defaults * ************************************************************************************/ static int tiva_i2c_initialize(struct tiva_i2c_priv_s *priv, uint32_t frequency) { const struct tiva_i2c_config_s *config = priv->config; uint32_t regval; int ret; i2cvdbg("I2C%d: refs=%d\n", config->devno, priv->refs); /* Enable power and clocking to the I2C peripheral. * * - Enable Power (TM4C129 family only): Applies power (only) to the I2C * peripheral. This is not an essential step since enabling clocking * will also apply power. The only significance is that the I2C state * will be retained if the I2C clocking is subsequently disabled. * - Enable Clocking (All families): Applies both power and clocking to * the I2C peripheral, bringing it a fully functional state. */ #ifdef TIVA_SYSCON_RCGCI2C tiva_i2c_enablepwr(config->devno); tiva_i2c_enableclk(config->devno); i2cvdbg("I2C%d: RCGI2C[%08x]=%08x\n", config->devno, TIVA_SYSCON_RCGCI2C, getreg32(TIVA_SYSCON_RCGCI2C)); #else modifyreg32(TIVA_SYSCON_RCGC1, 0, priv->rcgbit); i2cvdbg("I2C%d: RCGC1[%08x]=%08x\n", config->devno, TIVA_SYSCON_RCGC1, getreg32(TIVA_SYSCON_RCGC1)); #endif /* Reset the I2C block */ #ifdef TIVA_SYSCON_SRI2C modifyreg32(TIVA_SYSCON_SRI2C, 0, SYSCON_SRI2C(config->devno)); modifyreg32(TIVA_SYSCON_SRI2C, SYSCON_SRI2C(config->devno), 0); #else modifyreg32(TIVA_SYSCON_SRCR1, 0, priv->rstbit); modifyreg32(TIVA_SYSCON_SRCR1, priv->rstbit, 0); #endif /* Configure pins */ i2cvdbg("I2C%d: SCL=%08x SDA=%08x\n", config->devno, config->scl_pin, config->sda_pin); ret = tiva_configgpio(config->scl_pin); if (ret < 0) { i2cvdbg("I2C%d: tiva_configgpio(%08x) failed: %d\n", config->scl_pin, ret); return ret; } ret = tiva_configgpio(config->sda_pin); if (ret < 0) { i2cvdbg("I2C%d: tiva_configgpio(%08x) failed: %d\n", config->sda_pin, ret); tiva_configgpio(MKI2C_INPUT(config->scl_pin)); return ret; } /* Enable the I2C master block */ regval = tiva_i2c_getreg(priv, TIVA_I2CM_CR_OFFSET); regval |= I2CM_CR_MFE; tiva_i2c_putreg(priv, TIVA_I2CM_CR_OFFSET, regval); #ifdef TIVA_I2CSC_PC_OFFSET #ifdef CONFIG_TIVA_I2C_HIGHSPEED /* Enable high-speed mode */ tiva_i2c_putreg(priv, TIVA_I2CSC_PC_OFFSET, I2CSC_PC_HS); #else /* Disable high-speed mode */ tiva_i2c_putreg(priv, TIVA_I2CSC_PC_OFFSET, 0); #endif #endif /* Configure the the initial I2C clock frequency. */ (void)tiva_i2c_setclock(priv, frequency); /* Attach interrupt handlers and enable interrupts at the NVIC (still * disabled at the source). */ #ifndef CONFIG_I2C_POLLED (void)irq_attach(config->irq, config->isr); up_enable_irq(config->irq); #endif return OK; } /************************************************************************************ * Name: tiva_i2c_uninitialize * * Description: * Shutdown the I2C hardware * ************************************************************************************/ static int tiva_i2c_uninitialize(struct tiva_i2c_priv_s *priv) { uint32_t regval; i2cvdbg("I2C%d: refs=%d\n", priv->config->devno, priv->refs); /* Disable I2C */ regval = tiva_i2c_getreg(priv, TIVA_I2CM_CR_OFFSET); regval &= ~I2CM_CR_MFE; tiva_i2c_putreg(priv, TIVA_I2CM_CR_OFFSET, regval); /* Unconfigure GPIO pins */ tiva_configgpio(MKI2C_INPUT(priv->config->scl_pin)); tiva_configgpio(MKI2C_INPUT(priv->config->sda_pin)); /* Disable and detach interrupts */ #ifndef CONFIG_I2C_POLLED up_disable_irq(priv->config->irq); irq_detach(priv->config->irq); #endif /* Disable clocking */ #ifdef TIVA_SYSCON_RCGCI2C modifyreg32(TIVA_SYSCON_RCGCI2C, SYSCON_RCGCI2C(priv->config->devno), 0); #else modifyreg32(TIVA_SYSCON_RCGC1, priv->rcgbit, 0); #endif return OK; } /************************************************************************************ * Name: tiva_i2c_setclock * * Description: * Set the I2C frequency * ************************************************************************************/ static uint32_t tiva_i2c_setclock(struct tiva_i2c_priv_s *priv, uint32_t frequency) { uint32_t regval; uint32_t tmp; i2cvdbg("I2C%d: frequency: %u\n", priv->config->devno, frequency); /* Calculate the clock divider that results in the highest frequency that * is than or equal to the desired speed. */ tmp = 2 * 10 * frequency; regval = (((SYSCLK_FREQUENCY + tmp - 1) / tmp) - 1) << I2CM_TPR_SHIFT; DEBUGASSERT((regval & I2CM_TPR_MASK) == regval); tiva_i2c_putreg(priv, TIVA_I2CM_TPR_OFFSET, regval); #if defined(CONFIG_TIVA_I2C_HIGHSPEED) && defined(TIVA_I2CSC_PC_OFFSET) /* If the I2C peripheral is High-Speed enabled then choose the highest * speed that is less than or equal to 3.4 Mbps. */ regval = tiva_i2c_getreg(priv, TIVA_I2CSC_PC_OFFSET); if ((regval & I2CSC_PC_HS) != 0) { tmp = (2 * 3 * 3400000); regval = (((SYSCLK_FREQUENCY + tmp - 1) / tmp) - 1) << I2CM_TPR_SHIFT; tiva_i2c_putreg(priv, TIVA_I2CM_TPR_OFFSET, I2CM_TPR_HS | regval); } #endif return frequency; } /************************************************************************************ * Name: tiva_i2c_setfrequency * * Description: * Set the I2C frequency * ************************************************************************************/ static uint32_t tiva_i2c_setfrequency(struct i2c_dev_s *dev, uint32_t frequency) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; struct tiva_i2c_priv_s *priv; DEBUGASSERT(inst && inst->priv); priv = inst->priv; i2cvdbg("I2C%d: frequency: %u\n", inst->priv->config->devno, frequency); /* Get exclusive access to the I2C device */ tiva_i2c_sem_wait(dev); /* Set the clocking for the selected frequency */ inst->frequency = tiva_i2c_setclock(priv, frequency); tiva_i2c_sem_post(dev); return frequency; } /************************************************************************************ * Name: tiva_i2c_setaddress * * Description: * Set the I2C slave address * ************************************************************************************/ static int tiva_i2c_setaddress(struct i2c_dev_s *dev, int addr, int nbits) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; DEBUGASSERT(inst && inst->priv && inst->priv->config); i2cvdbg("I2C%d: addr: %02x nbits=%d\n", inst->priv->config->devno, addr, nbits); tiva_i2c_sem_wait(dev); inst->address = addr; inst->flags = (nbits == 10) ? I2C_M_TEN : 0; tiva_i2c_sem_post(dev); return OK; } /************************************************************************************ * Name: tiva_i2c_process * * Description: * Common I2C transfer logic * ************************************************************************************/ static int tiva_i2c_process(struct i2c_dev_s *dev, struct i2c_msg_s *msgv, int msgc) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; struct tiva_i2c_priv_s *priv = inst->priv; uint32_t regval; int errcode = OK; ASSERT(msgc > 0); i2cvdbg("I2C%d: msgc=%d\n", priv->config->devno, msgc); /* Reset mptr and mcnt to ensure an unexpected data interrupt doesn't * overwrite stale data. */ priv->mcnt = 0; priv->mptr = NULL; priv->msgv = msgv; priv->msgc = msgc; priv->mstatus = 0; /* Reset I2C trace logic */ tiva_i2c_tracereset(priv); tiva_i2c_tracenew(priv, 0); /* Set I2C clock frequency */ tiva_i2c_setclock(priv, inst->frequency); /* Send the address, then the process moves into the ISR. I2C * interrupts will be enabled within tiva_i2c_waitdone(). */ tiva_i2c_startxfr(priv); /* Wait for an ISR, if there was a timeout, fetch latest status to get * the BUSY flag. */ if (tiva_i2c_sem_waitdone(priv) < 0) { i2cdbg("I2C%d: ERROR: Timed out\n", priv->config->devno); errcode = ETIMEDOUT; } #if 0 /* I2CM_CS_CLKTO */ else if ((priv->mstatus & (I2CM_CS_ERROR | I2CM_CS_ARBLST | I2CM_CS_CLKTO)) != 0) #else else if ((priv->mstatus & (I2CM_CS_ERROR | I2CM_CS_ARBLST)) != 0) #endif { i2cdbg("I2C%d: ERROR: I2C error status: %08x\n", priv->config->devno, priv->mstatus); if ((priv->mstatus & I2CM_CS_ARBLST) != 0) { /* Arbitration Lost */ errcode = EAGAIN; } else if ((priv->mstatus & (I2CM_CS_ADRACK | I2CM_CS_DATACK)) != 0) { /* Acknowledge Failure */ errcode = ENXIO; } #if 0 /* I2CM_CS_CLKTO */ else if ((priv->mstatus & I2CM_CS_CLKTO) != 0) { /* Timeout */ errcode = ETIME; } #endif else { /* Something else? */ errcode = EIO; } } /* This is not an error, but should not happen. The I2CM_CS_BUSBSY signal * can hang, however. This normally indicates the STOP was never sent, * possibly because some other error occurred. * * The status bits are not valid if BUSY is set. But in this context I * assume that busy bit stuck on would be a very bad situation, worthy * of a reset. */ regval = tiva_i2c_getreg(priv, TIVA_I2CM_CS_OFFSET); if ((regval & (I2CM_CS_BUSY | I2CM_CS_BUSBSY)) != 0) { /* I2C Bus is for some reason busy. If I2CM_CS_BUSY then none of the * other bits are valid. */ i2cdbg("I2C%d: ERROR: I2C still busy: %08x\n", priv->config->devno, regval); /* Reset and reinitialize the I2C hardware */ tiva_i2c_initialize(priv, inst->frequency); /* Is the busy condition a consequence of some other error? */ if (errcode == OK) { /* No... then just being busy is the cause of the error */ errcode = EBUSY; } } /* Dump the trace result */ tiva_i2c_tracedump(priv); /* Ensure that no ISR happening after we finish can overwrite any user data */ priv->mcnt = 0; priv->mptr = NULL; tiva_i2c_sem_post(dev); return -errcode; } /************************************************************************************ * Name: tiva_i2c_write * * Description: * Write I2C data * ************************************************************************************/ static int tiva_i2c_write(struct i2c_dev_s *dev, const uint8_t *buffer, int buflen) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; struct i2c_msg_s msgv = { .addr = inst->address, .flags = inst->flags, .buffer = (uint8_t *)buffer, .length = buflen }; DEBUGASSERT(inst && inst->priv && inst->priv->config); i2cvdbg("I2C%d: buflen=%d\n", inst->priv->config->devno, buflen); tiva_i2c_sem_wait(dev); /* ensure that address or flags don't change meanwhile */ return tiva_i2c_process(dev, &msgv, 1); } /************************************************************************************ * Name: tiva_i2c_read * * Description: * Read I2C data * ************************************************************************************/ int tiva_i2c_read(struct i2c_dev_s *dev, uint8_t *buffer, int buflen) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; struct i2c_msg_s msgv = { .addr = inst->address, .flags = inst->flags | I2C_M_READ, .buffer = buffer, .length = buflen }; DEBUGASSERT(inst && inst->priv && inst->priv->config); i2cvdbg("I2C%d: buflen=%d\n", inst->priv->config->devno, buflen); tiva_i2c_sem_wait(dev); /* ensure that address or flags don't change meanwhile */ return tiva_i2c_process(dev, &msgv, 1); } /************************************************************************************ * Name: tiva_i2c_writeread * * Description: * Read then write I2C data * ************************************************************************************/ #ifdef CONFIG_I2C_WRITEREAD static int tiva_i2c_writeread(struct i2c_dev_s *dev, const uint8_t *wbuffer, int wbuflen, uint8_t *buffer, int buflen) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; struct i2c_msg_s msgv[2] = { { .addr = inst->address, .flags = inst->flags, .buffer = (uint8_t *)wbuffer, /* This is really ugly, sorry const ... */ .length = wbuflen }, { .addr = inst->address, .flags = inst->flags | ((buflen > 0) ? I2C_M_READ : I2C_M_NORESTART), .buffer = buffer, .length = (buflen > 0) ? buflen : -buflen } }; DEBUGASSERT(inst && inst->priv && inst->priv->config); i2cvdbg("I2C%d: wbuflen=%d buflen=%d\n", inst->priv->config->devno, wbuflen, buflen); tiva_i2c_sem_wait(dev); /* Ensure that address or flags don't change meanwhile */ return tiva_i2c_process(dev, msgv, 2); } #endif /************************************************************************************ * Name: tiva_i2c_transfer * * Description: * Generic I2C transfer function * ************************************************************************************/ #ifdef CONFIG_I2C_TRANSFER static int tiva_i2c_transfer(struct i2c_dev_s *dev, struct i2c_msg_s *msgv, int msgc) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; DEBUGASSERT(inst && inst->priv && inst->priv->config); i2cvdbg("I2C%d: msgc=%d\n", inst->priv->config->devno, msgc); UNUSED(inst); tiva_i2c_sem_wait(dev); /* Ensure that address or flags don't change meanwhile */ return tiva_i2c_process(dev, msgv, msgc); } #endif /************************************************************************************ * Public Functions ************************************************************************************/ /************************************************************************************ * Name: up_i2cinitialize * * Description: * Initialize one I2C bus * ************************************************************************************/ struct i2c_dev_s *up_i2cinitialize(int port) { struct tiva_i2c_priv_s *priv = NULL; /* Private data of device with multiple instances */ struct tiva_i2c_inst_s *inst = NULL; /* Device, single instance */ const struct tiva_i2c_config_s *config; /* Constant configuration */ int irqs; i2cvdbg("I2C%d: Initialize\n", port); /* Get I2C private structure */ switch (port) { #ifdef CONFIG_TIVA_I2C0 case 0: priv = &tiva_i2c0_priv; config = &tiva_i2c0_config; break; #endif #ifdef CONFIG_TIVA_I2C1 case 1: priv = &tiva_i2c1_priv; config = &tiva_i2c1_config; break; #endif #ifdef CONFIG_TIVA_I2C2 case 2: priv = &tiva_i2c2_priv; config = &tiva_i2c2_config; break; #endif #ifdef CONFIG_TIVA_I2C3 case 3: priv = &tiva_i2c3_priv; config = &tiva_i2c3_config; break; #endif #ifdef CONFIG_TIVA_I2C4 case 4: priv = &tiva_i2c4_priv; config = &tiva_i2c4_config; break; #endif #ifdef CONFIG_TIVA_I2C5 case 5: priv = &tiva_i2c5_priv; config = &tiva_i2c5_config; break; #endif #ifdef CONFIG_TIVA_I2C6 case 6: priv = &tiva_i2c6_priv; config = &tiva_i2c6_config; break; #endif #ifdef CONFIG_TIVA_I2C7 case 7: priv = &tiva_i2c7_priv; config = &tiva_i2c7_config; break; #endif #ifdef CONFIG_TIVA_I2C8 case 8: priv = &tiva_i2c8_priv; config = &tiva_i2c8_config; break; #endif #ifdef CONFIG_TIVA_I2C9 case 9: priv = &tiva_i2c9_priv; config = &tiva_i2c9_config; break; #endif default: i2cdbg("I2C%d: ERROR: Not supported\n", port); return NULL; } /* Allocate instance */ if (!(inst = kmm_malloc(sizeof(struct tiva_i2c_inst_s)))) { return NULL; } /* Initialize instance */ inst->ops = &tiva_i2c_ops; inst->priv = priv; inst->frequency = 100000; inst->address = 0; inst->flags = 0; /* Initialize private data for the first time, increment reference count, * power-up hardware and configure GPIOs. */ irqs = irqsave(); priv->refs++; if (priv->refs == 1) { /* Initialize the device structure */ priv->config = config; tiva_i2c_sem_init((struct i2c_dev_s *)inst); /* Initialize the I2C hardware */ tiva_i2c_initialize(priv, 100000); } irqrestore(irqs); return (struct i2c_dev_s *)inst; } /************************************************************************************ * Name: up_i2cuninitialize * * Description: * Uninitialize an I2C bus * ************************************************************************************/ int up_i2cuninitialize(struct i2c_dev_s *dev) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; struct tiva_i2c_priv_s *priv ; int irqs; DEBUGASSERT(inst && inst->priv); priv = inst->priv; DEBUGASSERT(priv->config && priv->refs > 0); i2cvdbg("I2C%d: Uninitialize\n", priv->config->devno); /* Decrement reference count and check for underflow */ irqs = irqsave(); /* Free this instance */ kmm_free(inst); /* Check if the reference count will decrement to zero */ if (priv->refs < 2) { /* Yes.. Disable power and other HW resource (GPIO's) */ tiva_i2c_uninitialize(priv); priv->refs = 0; /* Release unused resources */ tiva_i2c_sem_destroy(dev); } else { /* No.. just decrement the number of references to the device */ priv->refs--; } irqrestore(irqs); return OK; } /************************************************************************************ * Name: up_i2creset * * Description: * Reset an I2C bus * ************************************************************************************/ #ifdef CONFIG_I2C_RESET int up_i2creset(struct i2c_dev_s *dev) { struct tiva_i2c_inst_s *inst = (struct tiva_i2c_inst_s *)dev; struct tiva_i2c_priv_s *priv; unsigned int clock_count; unsigned int stretch_count; uint32_t scl_gpio; uint32_t sda_gpio; int ret = ERROR; DEBUGASSERT(inst && inst->priv && inst->priv->config); i2cvdbg("I2C%d:\n", inst->priv->config->devno); /* Get I2C private structure */ priv = inst->priv; /* Our caller must own a ref */ ASSERT(priv->refs > 0); /* Lock out other clients */ tiva_i2c_sem_wait(dev); /* Un-initialize the port */ tiva_i2c_uninitialize(priv); /* Use GPIO configuration to un-wedge the bus */ scl_gpio = MKI2C_OUTPUT(priv->config->scl_pin); sda_gpio = MKI2C_OUTPUT(priv->config->sda_pin); tiva_configgpio(scl_gpio); tiva_configgpio(sda_gpio); /* Let SDA go high */ tiva_gpiowrite(sda_gpio, 1); /* Clock the bus until any slaves currently driving it let it go. */ clock_count = 0; while (!tiva_gpioread(sda_gpio)) { /* Give up if we have tried too hard */ if (clock_count++ > 10) { goto out; } /* Sniff to make sure that clock stretching has finished. * * If the bus never relaxes, the reset has failed. */ stretch_count = 0; while (!tiva_gpioread(scl_gpio)) { /* Give up if we have tried too hard */ if (stretch_count++ > 10) { goto out; } up_udelay(10); } /* Drive SCL low */ tiva_gpiowrite(scl_gpio, 0); up_udelay(10); /* Drive SCL high again */ tiva_gpiowrite(scl_gpio, 1); up_udelay(10); } /* Generate a start followed by a stop to reset slave * state machines. */ tiva_gpiowrite(sda_gpio, 0); up_udelay(10); tiva_gpiowrite(scl_gpio, 0); up_udelay(10); tiva_gpiowrite(scl_gpio, 1); up_udelay(10); tiva_gpiowrite(sda_gpio, 1); up_udelay(10); /* Revert the GPIO configuration. */ tiva_configgpio(MKI2C_INPUT(sda_gpio)); tiva_configgpio(MKI2C_INPUT(scl_gpio)); /* Re-init the port */ tiva_i2c_initialize(priv, inst->frequency); ret = OK; out: /* Release the port for re-use by other clients */ tiva_i2c_sem_post(dev); return ret; } #endif /* CONFIG_I2C_RESET */ #endif /* CONFIG_TIVA_I2C0 ... CONFIG_TIVA_I2C9 */