diff --git a/arch/arm/src/sama5/chip/sam_pmc.h b/arch/arm/src/sama5/chip/sam_pmc.h index 53ae2092c3..ba2c759900 100644 --- a/arch/arm/src/sama5/chip/sam_pmc.h +++ b/arch/arm/src/sama5/chip/sam_pmc.h @@ -368,14 +368,15 @@ /* Peripheral Control Register */ #define PMC_PCR_PID_SHIFT (0) /* Bits 0-5: Peripheral ID */ -#define PMC_PCR_PID_MASK (63 < PMC_PCR_PID_SHIFT) +#define PMC_PCR_PID_MASK (63 << PMC_PCR_PID_SHIFT) +# define PMC_PCR_PID(n) ((n) << PMC_PCR_PID_SHIFT) #define PMC_PCR_CMD (1 << 12) /* Bit 12: Command */ #define PMC_PCR_DIV_SHIFT (16) /* Bits 16-17: Divisor Value */ -#define PMC_PCR_DIV_MASK (3 < PMC_PCR_DIV_SHIFT) -# define PMC_PCR_DIV1 (0 < PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK */ -# define PMC_PCR_DIV2 (1 < PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/2 */ -# define PMC_PCR_DIV4 (2 < PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/4 */ -# define PMC_PCR_DIV8 (3 < PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/8 */ +#define PMC_PCR_DIV_MASK (3 << PMC_PCR_DIV_SHIFT) +# define PMC_PCR_DIV1 (0 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK */ +# define PMC_PCR_DIV2 (1 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/2 */ +# define PMC_PCR_DIV4 (2 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/4 */ +# define PMC_PCR_DIV8 (3 << PMC_PCR_DIV_SHIFT) /* Peripheral clock is MCK/8 */ #define PMC_PCR_EN (1 << 28) /* Bit 28: Enable */ /* Oscillator Calibration Register */ diff --git a/arch/arm/src/sama5/chip/sam_twi.h b/arch/arm/src/sama5/chip/sam_twi.h index bd0bc3984f..bb649f7a75 100644 --- a/arch/arm/src/sama5/chip/sam_twi.h +++ b/arch/arm/src/sama5/chip/sam_twi.h @@ -137,6 +137,7 @@ #define TWI_MMR_MREAD (1 << 12) /* Bit 12: Master Read Direction */ #define TWI_MMR_DADR_SHIFT (16) /* Bits 16-22: Device Address */ #define TWI_MMR_DADR_MASK (0x7f << TWI_MMR_DADR_SHIFT) +# define TWI_MMR_DADR(n) ((uint32_t)(n) << TWI_MMR_DADR_SHIFT) /* TWI Slave Mode Register */ diff --git a/arch/arm/src/sama5/sam_twi.c b/arch/arm/src/sama5/sam_twi.c index be2970c59b..4103cb77a5 100644 --- a/arch/arm/src/sama5/sam_twi.c +++ b/arch/arm/src/sama5/sam_twi.c @@ -4,6 +4,15 @@ * Copyright (C) 2013 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * + * References: + * SAMA5D3 Series Data Sheet + * Atmel NoOS sample code. + * + * The Atmel sample code has a BSD compatibile license that requires this + * copyright notice: + * + * Copyright (c) 2011, Atmel Corporation + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -14,8 +23,8 @@ * 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 + * 3. Neither the name NuttX, Atmel, 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 @@ -54,7 +63,11 @@ #include "up_arch.h" +#include "chip/sam_pmc.h" +#include "chip/sam_pinmap.h" + #include "sam_periphclks.h" +#include "sam_pio.h" #include "sam_twi.h" #if defined(CONFIG_SAMA5_TWI0) || defined(CONFIG_SAMA5_TWI1) || defined(CONFIG_SAMA5_TWI2) @@ -80,15 +93,41 @@ #define TWI_TIMEOUT ((20 * CLK_TCK) / 1000) /* 20 mS */ +/* Clocking to the TWO module(s) is provided by the main clocked, divided down + * as necessary. + */ + +#define TWI_MAX_FREQUENCY 66000000 /* Maximum TWI frequency */ + +#if BOARD_MCK_FREQUENCY <= TWI_MAX_FREQUENCY +# define TWI_FREQUENCY BOARD_MCK_FREQUENCY +# define TWI_PCR_DIV PMC_PCR_DIV1 +#elif (BOARD_MCK_FREQUENCY >> 1) <= TWI_MAX_FREQUENCY +# define TWI_FREQUENCY (BOARD_MCK_FREQUENCY >> 1) +# define TWI_PCR_DIV PMC_PCR_DIV2 +#elif (BOARD_MCK_FREQUENCY >> 2) <= TWI_MAX_FREQUENCY +# define TWI_FREQUENCY (BOARD_MCK_FREQUENCY >> 2) +# define TWI_PCR_DIV PMC_PCR_DIV4 +#elif (BOARD_MCK_FREQUENCY >> 3) <= TWI_MAX_FREQUENCY +# define TWI_FREQUENCY (BOARD_MCK_FREQUENCY >> 3) +# define TWI_PCR_DIV PMC_PCR_DIV8 +#else +# error Cannot realize TWI frequency +#endif + /* Debug ***********************************************************************/ /* CONFIG_DEBUG_I2C + CONFIG_DEBUG enables general I2C debug output. */ #ifdef CONFIG_DEBUG_I2C -# define i2cdbg dbg -# define i2cvdbg vdbg +# define i2cdbg dbg +# define i2cvdbg vdbg +# define i2clldbg lldbg +# define i2cllvdbg llvdbg #else # define i2cdbg(x...) # define i2cvdbg(x...) +# define i2clldbg(x...) +# define i2cllvdbg(x...) #endif /******************************************************************************* @@ -99,13 +138,15 @@ struct twi_dev_s { struct i2c_dev_s dev; /* Generic I2C device */ struct i2c_msg_s msg; /* A single message for legacy read/write */ - unsigned int base; /* Base address of registers */ + uintptr_t base; /* Base address of registers */ uint16_t irq; /* IRQ number for this device */ + uint8_t twi; /* TWI peripheral number (for debug output) */ sem_t exclsem; /* Only one thread can access at a time */ sem_t waitsem; /* Wait for TWI transfer completion */ WDOG_ID timeout; /* Watchdog to recover from bus hangs */ - int result; /* The result of the transfer */ + volatile int result; /* The result of the transfer */ + volatile int xfrd; /* Number of bytes transfers */ /* Debug stuff */ @@ -123,24 +164,24 @@ struct twi_dev_s /* Low-level helper functions */ -static void sam_takesem(sem_t *sem); -#define sam_givesem(sem) (sem_post(sem)) +static void twi_takesem(sem_t *sem); +#define twi_givesem(sem) (sem_post(sem)) #ifdef CONFIG_SAMA5_TWI_REGDEBUG -static bool sam_checkreg(struct twi_dev_s *priv, bool wr, +static bool twi_checkreg(struct twi_dev_s *priv, bool wr, uint32_t value, uintptr_t address); #else -# define sam_checkreg(priv,wr,value,address) (false) +# define twi_checkreg(priv,wr,value,address) (false) #endif -static inline uint32_t sam_getreg(struct twi_dev_s *priv, - unsigned int offset); -static inline void sam_putreg(struct twi_dev_s *priv, uint32_t value, +static inline uint32_t twi_getreg(struct twi_dev_s *priv, unsigned int offset); +static inline void twi_putreg(struct twi_dev_s *priv, unsigned int offset, + uint32_t value); /* I2C transfer helper functions */ -static int twi_start(struct twi_dev_s *priv); +static int twi_wait(struct twi_dev_s *priv); static int twi_interrupt(struct twi_dev_s *priv); #ifdef CONFIG_SAMA5_TWI0 static int twi0_interrupt(int irq, FAR void *context); @@ -162,8 +203,8 @@ static int twi_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, int buflen); static int twi_read(FAR struct i2c_dev_s *dev, uint8_t *buffer, int buflen); #ifdef CONFIG_I2C_WRITEREAD -static int twi_writeread(FAR struct i2c_dev_s *inst, const uint8_t *wbuffer, - int wbuflen, uint8_t *rbuffer, int rbuflen); +static int twi_writeread(FAR struct i2c_dev_s *inst, const uint8_t *buffer, + int buflen, uint8_t *rbuffer, int buflen); #endif #ifdef CONFIG_I2C_TRANSFER static int twi_transfer(FAR struct i2c_dev_s *dev, @@ -175,6 +216,13 @@ static int twi_registercallback(FAR struct i2c_dev_s *dev, int (*callback)(void)); #endif +/* Initialization */ + +static uint32_t twi_hw_setfrequency(struct twi_dev_s *priv, + uint32_t frequency); +static void twi_hw_initialize(struct twi_dev_s *priv, unsigned int pid, + uint32_t frequency); + /******************************************************************************* * Private Data *******************************************************************************/ @@ -213,7 +261,7 @@ struct i2c_ops_s g_twiops = * Low-level Helpers ****************************************************************************/ /**************************************************************************** - * Name: sam_takesem + * Name: twi_takesem * * Description: * Take the wait semaphore (handling false alarm wakeups due to the receipt @@ -227,7 +275,7 @@ struct i2c_ops_s g_twiops = * ****************************************************************************/ -static void sam_takesem(sem_t *sem) +static void twi_takesem(sem_t *sem) { /* Take the semaphore (perhaps waiting) */ @@ -242,7 +290,7 @@ static void sam_takesem(sem_t *sem) } /**************************************************************************** - * Name: sam_checkreg + * Name: twi_checkreg * * Description: * Check if the current register access is a duplicate of the preceding. @@ -258,7 +306,7 @@ static void sam_takesem(sem_t *sem) ****************************************************************************/ #ifdef CONFIG_SAMA5_TWI_REGDEBUG -static bool sam_checkreg(struct twi_dev_s *priv, bool wr, uint32_t value, +static bool twi_checkreg(struct twi_dev_s *priv, bool wr, uint32_t value, uint32_t address) { if (wr == priv->wrlast && /* Same kind of access? */ @@ -296,20 +344,20 @@ static bool sam_checkreg(struct twi_dev_s *priv, bool wr, uint32_t value, #endif /**************************************************************************** - * Name: sam_getreg + * Name: twi_getreg * * Description: * Read an SPI register * ****************************************************************************/ -static inline uint32_t sam_getreg(struct twi_dev_s *priv, unsigned int offset) +static inline uint32_t twi_getreg(struct twi_dev_s *priv, unsigned int offset) { uint32_t address = priv->base + offset; uint32_t value = getreg32(address); #ifdef CONFIG_SAMA5_TWI_REGDEBUG - if (sam_checkreg(priv, false, value, address)) + if (twi_checkreg(priv, false, value, address)) { lldbg("%08x->%08x\n", address, value); } @@ -319,20 +367,20 @@ static inline uint32_t sam_getreg(struct twi_dev_s *priv, unsigned int offset) } /**************************************************************************** - * Name: sam_putreg + * Name: twi_putreg * * Description: * Write a value to an SPI register * ****************************************************************************/ -static inline void sam_putreg(struct twi_dev_s *priv, uint32_t value, - unsigned int offset) +static inline void twi_putreg(struct twi_dev_s *priv, unsigned int offset, + uint32_t value) { uint32_t address = priv->base + offset; #ifdef CONFIG_SAMA5_TWI_REGDEBUG - if (sam_checkreg(priv, true, value, address)) + if (twi_checkreg(priv, true, value, address)) { lldbg("%08x<-%08x\n", address, value); } @@ -346,30 +394,32 @@ static inline void sam_putreg(struct twi_dev_s *priv, uint32_t value, ****************************************************************************/ /******************************************************************************* - * Name: twi_start + * Name: twi_wait * * Description: * Perform a I2C transfer start * *******************************************************************************/ -static int twi_start(struct twi_dev_s *priv) +static int twi_wait(struct twi_dev_s *priv) { - sam_takesem(&priv->exclsem); -#warning Missing logic - /* Start a timeout to avoid hangs */ wd_start(priv->timeout, TWI_TIMEOUT, twi_timeout, 1, (uint32_t)priv); /* Wait for either the TWI transfer or the timeout to complete */ - sam_takesem(&priv->waitsem); + do + { + i2cvdbg("TWI%d Waiting...\n", priv->twi); + twi_takesem(&priv->waitsem); + i2cvdbg("TWI%d Awakened with result: %d\n", priv->twi, priv->result); + } + while (priv->result == -EBUSY); + + /* Cancel the timeout and return the result of the transfer */ + wd_cancel(priv->timeout); - sam_givesem(&priv->exclsem); - - /* Return the result of the transfer */ - return priv->result; } @@ -383,11 +433,90 @@ static int twi_start(struct twi_dev_s *priv) static int twi_interrupt(struct twi_dev_s *priv) { - /* Get the unmasked bits in the interrupt status register */ -#warning Missing logic + uint32_t sr; + uint32_t imr; + uint32_t pending; + uint32_t regval; - /* Process each unmasked bit in the interrupt status */ -#warning Missing logic + /* Retrieve masked interrupt status */ + + sr = twi_getreg(priv, SAM_TWI_SR_OFFSET); + imr = twi_getreg(priv, SAM_TWI_IMR_OFFSET); + pending = sr & imr; + + i2cllvdbg("TWI%d pending: %08x\n", priv->twi, pending); + + /* Byte received */ + + if ((pending & TWI_INT_RXRDY) == TWI_INT_RXRDY) + { + priv->msg.buffer[priv->xfrd] = twi_getreg(priv, SAM_TWI_RHR_OFFSET); + priv->xfrd++; + + /* Check for transfer complete */ + + if (priv->xfrd >= priv->msg.length) + { + /* The transfer is complete. Disable the RXRDY interrupt and + * enable the TXCOMP interrupt + */ + + twi_putreg(priv, SAM_TWI_IDR_OFFSET, TWI_INT_RXRDY); + twi_putreg(priv, SAM_TWI_IER_OFFSET, TWI_INT_TXCOMP); + } + + /* Not yet complete, but will the next be the last byte? */ + + else if (priv->xfrd == (priv->msg.length - 1)) + { + /* Yes, set the stop signal */ + + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_STOP); + } + } + + /* Byte sent*/ + + else if ((pending & TWI_INT_TXRDY) == TWI_INT_TXRDY) + { + /* Transfer finished? */ + + if (priv->xfrd >= priv->msg.length) + { + /* The transfer is complete. Disable the TXRDY interrupt and + * enable the TXCOMP interrupt + */ + + twi_putreg(priv, SAM_TWI_IDR_OFFSET, TWI_INT_TXRDY); + twi_putreg(priv, SAM_TWI_IER_OFFSET, TWI_INT_TXCOMP); + + /* Send the STOP condition */ + + regval = twi_getreg(priv, SAM_TWI_CR_OFFSET); + regval |= TWI_CR_STOP; + twi_putreg(priv, SAM_TWI_CR_OFFSET, regval); + } + + /* No, there are more bytes remaining to be sent */ + + else + { + twi_putreg(priv, SAM_TWI_THR_OFFSET, priv->msg.buffer[priv->xfrd]); + priv->xfrd++; + } + } + + /* Transfer complete */ + + else if ((pending & TWI_INT_TXCOMP) == TWI_INT_TXCOMP) + { + twi_putreg(priv, SAM_TWI_IDR_OFFSET, TWI_INT_TXCOMP); + priv->result = OK; + + /* Wake up the waiting thread */ + + twi_givesem(&priv->waitsem); + } return OK; } @@ -419,17 +548,18 @@ static int twi2_interrupt(int irq, FAR void *context) * Description: * Watchdog timer for timeout of TWI operation * + * Assumptions: + * Called from the timer interrupt handler with interrupts disabled. + * *******************************************************************************/ static void twi_timeout(int argc, uint32_t arg, ...) { - struct twi_dev_s *priv = (struct twi_dev_s *) arg; + struct twi_dev_s *priv = (struct twi_dev_s *)arg; - irqstate_t flags = irqsave(); -#warning Missing logic - priv->result = -ENOSYS; - sam_givesem(&priv->waitsem); - irqrestore(flags); + i2clldbg("TWI%d Timeout!\n", priv->twi); + priv->result = -ETIMEDOUT; + twi_givesem(&priv->waitsem); } /******************************************************************************* @@ -446,14 +576,20 @@ static void twi_timeout(int argc, uint32_t arg, ...) static uint32_t twi_setfrequency(FAR struct i2c_dev_s *dev, uint32_t frequency) { - struct twi_dev_s *priv = (struct twi_dev_s *) dev; + struct twi_dev_s *priv = (struct twi_dev_s *)dev; + uint32_t actual; - /* Setup clocking as close a possible to the selectd freqeuncy */ -#warning Missing Logic + DEBUGASSERT(dev); - /* Return the actual frequency */ + /* Get exclusive access to the device */ - return frequency; + twi_takesem(&priv->exclsem); + + /* And setup the clock frequency */ + + actual = twi_hw_setfrequency(priv, frequency); + twi_givesem(&priv->exclsem); + return actual; } /******************************************************************************* @@ -468,12 +604,19 @@ static int twi_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits) { struct twi_dev_s *priv = (struct twi_dev_s *) dev; - DEBUGASSERT(dev != NULL); - DEBUGASSERT(nbits == 7 ); + i2cvdbg("TWI%d nbits: %d\n", priv->twi, nbits); + DEBUGASSERT(dev != NULL && nbits == 7); + + /* Get exclusive access to the device */ + + twi_takesem(&priv->exclsem); + + /* Set the correctly shifted, 7-bit address */ priv->msg.addr = addr << 1; priv->msg.flags = 0 ; + twi_givesem(&priv->exclsem); return OK; } @@ -489,18 +632,45 @@ static int twi_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits) static int twi_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, int buflen) { struct twi_dev_s *priv = (struct twi_dev_s *) dev; + uint8_t devaddr; int ret; + i2cvdbg("TWI%d buflen: %d\n", priv->twi, buflen); DEBUGASSERT(dev != NULL); + /* Get exclusive access to the device */ + + twi_takesem(&priv->exclsem); + priv->msg.addr &= ~0x01; priv->msg.buffer = (uint8_t*)buffer; priv->msg.length = buflen; priv->result = -EBUSY; + priv->xfrd = 0; - ret = twi_start(priv); + /* Set slave address and number of internal address bytes. */ - return ret > 0 ? OK : -ETIMEDOUT; + twi_putreg(priv, SAM_TWI_MMR_OFFSET, 0); + + devaddr = priv->msg.addr >> 1; + twi_putreg(priv, SAM_TWI_MMR_OFFSET, TWI_MMR_IADRSZ_NONE | TWI_MMR_DADR(devaddr)); + + /* Set internal address bytes. */ + + twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); + twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); + + /* Write first byte to send.*/ + + twi_putreg(priv, SAM_TWI_THR_OFFSET, *buffer); + + /* Enable write interrupt */ + + twi_putreg(priv, SAM_TWI_IER_OFFSET, TWI_INT_TXRDY); + ret = twi_wait(priv); + + twi_givesem(&priv->exclsem); + return ret; } /******************************************************************************* @@ -515,18 +685,50 @@ static int twi_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, int bufle static int twi_read(FAR struct i2c_dev_s *dev, uint8_t *buffer, int buflen) { struct twi_dev_s *priv = (struct twi_dev_s *) dev; + uint8_t devaddr; int ret; + i2cvdbg(TWI%d "buflen: %d\n", priv->twi, buflen); DEBUGASSERT(dev != NULL); + /* Get exclusive access to the device */ + + twi_takesem(&priv->exclsem); + priv->msg.addr |= 0x01; priv->msg.buffer = buffer; priv->msg.length = buflen; priv->result = -EBUSY; + priv->xfrd = 0; - ret = twi_start(priv); + /* Set STOP signal if only one byte is sent*/ - return ret > 0 ? OK : -ETIMEDOUT; + if (buflen == 1) + { + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_STOP); + } + + /* Set slave address and number of internal address bytes. */ + + twi_putreg(priv, SAM_TWI_MMR_OFFSET, 0); + + devaddr = priv->msg.addr >> 1; + twi_putreg(priv, SAM_TWI_MMR_OFFSET, TWI_MMR_IADRSZ_NONE | TWI_MMR_MREAD | TWI_MMR_DADR(devaddr)); + + /* Set internal address bytes */ + + twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); + twi_putreg(priv, SAM_TWI_IADR_OFFSET, 0); + + /* Enable read interrupt and send the START codnition */ + + twi_putreg(priv, SAM_TWI_IER_OFFSET, TWI_INT_RXRDY); + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_START); + + ret = twi_wait(priv); + + twi_givesem(&priv->exclsem); + return ret; } /******************************************************************************* @@ -537,8 +739,8 @@ static int twi_read(FAR struct i2c_dev_s *dev, uint8_t *buffer, int buflen) *******************************************************************************/ #ifdef CONFIG_I2C_WRITEREAD -static int twi_writeread(FAR struct i2c_dev_s *inst, const uint8_t *wbuffer, - int wbuflen, uint8_t *rbuffer, int rbuflen) +static int twi_writeread(FAR struct i2c_dev_s *inst, const uint8_t *buffer, + int buflen, uint8_t *rbuffer, int buflen) { #error Not implemented return -ENOSYS; @@ -594,6 +796,106 @@ static int twi_transfer(FAR struct i2c_dev_s *dev, } #endif +/******************************************************************************* + * Initialization + *******************************************************************************/ + +/******************************************************************************* + * Name: twi_hw_setfrequency + * + * Description: + * Set the frequence for the next transfer + * + *******************************************************************************/ + +static uint32_t twi_hw_setfrequency(struct twi_dev_s *priv, uint32_t frequency) +{ + unsigned int ckdiv; + unsigned int cldiv; + uint32_t actual; + uint32_t regval; + + /* Configure TWI output clocking, trying each value of CKDIV {0..7} */ + + for (ckdiv = 0; ckdiv < 8; ckdiv++) + { + /* Calulate the CLDIV value using the current CKDIV guess */ + + cldiv = ((TWI_FREQUENCY / (frequency << 1)) - 4) / (1 << ckdiv); + + /* Is CLDIV in range? */ + + if (cldiv <= 255) + { + /* Yes, break out and use it */ + + break; + } + } + + /* Then setup the TWI Clock Waveform Generator Register, using the same + * value for CLDIV and CHDIV (for 1:1 duty). + */ + + twi_putreg(priv, SAM_TWI_CWGR_OFFSET, 0); + + regval = ((uint32_t)ckdiv << TWI_CWGR_CKDIV_SHIFT) | + ((uint32_t)cldiv << TWI_CWGR_CHDIV_SHIFT) | + ((uint32_t)cldiv << TWI_CWGR_CLDIV_SHIFT); + twi_putreg(priv, SAM_TWI_CWGR_OFFSET, regval); + + /* Return the actual frequency */ + + actual = (TWI_FREQUENCY / 2) / (((1 << ckdiv) * cldiv) + 2); + i2cvdbg("TWI%d frequency: %d ckdiv: %d cldiv: %d actual: %d\n", + priv->twi, frequency, ckdiv, cldiv, actual); + + return actual; +} + +/******************************************************************************* + * Name: twi_hw_initialize + * + * Description: + * Initialize one TWI peripheral for I2C operation + * + *******************************************************************************/ + +static void twi_hw_initialize(struct twi_dev_s *priv, unsigned int pid, + uint32_t frequency) +{ + uint32_t regval; + + uvdbg("TWI%d Initializing\n", priv->twi); + + /* SVEN: TWI Slave Mode Enabled */ + + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_SVEN); + + /* Reset the TWI */ + + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_SWRST); + (void)twi_getreg(priv, SAM_TWI_RHR_OFFSET); + + /* TWI Slave Mode Disabled, TWI Master Mode Disabled. */ + + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_SVDIS); + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_MSDIS); + + /* Set master mode */ + + twi_putreg(priv, SAM_TWI_CR_OFFSET, TWI_CR_MSEN); + + /* Set the TWI peripheral input clock to the maximum, valid frequency */ + + regval = PMC_PCR_PID(pid) | PMC_PCR_CMD | TWI_PCR_DIV | PMC_PCR_EN; + putreg32(regval, SAM_PMC_PCR); + + /* Set the initial TWI data transfer frequency */ + + (void)twi_hw_setfrequency(priv, frequency); +} + /******************************************************************************* * Public Functions *******************************************************************************/ @@ -611,76 +913,91 @@ struct i2c_dev_s *up_i2cinitialize(int bus) struct twi_dev_s *priv; xcpt_t handler; irqstate_t flags; - uint32_t regval; + uint32_t frequency; + unsigned int pid; + + uvdbg("TWI%d Initializing\n", priv->twi); flags = irqsave(); #ifdef CONFIG_SAMA5_TWI0 if (bus == 0) { + /* Set up TWI2 register base address and IRQ number */ + priv = &g_twi0; priv->base = SAM_TWI0_VBASE; priv->irq = SAM_IRQ_TWI0; + priv->twi = 0; /* Enable peripheral clocking */ sam_twi0_enableclk(); /* Configure PIO pins */ -#warning Missing logic - /* Configure and enable the TWI block */ -#warning Missing logic + sam_configpio(PIO_TWI0_CK); + sam_configpio(PIO_TWI0_D); - /* Select the interrupt handler */ + /* Select the interrupt handler, TWI frequency, and peripheral ID */ - handler = twi0_interrupt; + handler = twi0_interrupt; + frequency = CONFIG_SAMA5_TWI0_FREQUENCY; + pid = SAM_PID_TWI0; } else #endif #ifdef CONFIG_SAMA5_TWI1 if (bus == 1) { + /* Set up TWI1 register base address and IRQ number */ + priv = &g_twi1; priv->base = SAM_TWI1_VBASE; priv->irq = SAM_IRQ_TWI1; + priv->twi = 1; /* Enable peripheral clocking */ sam_twi1_enableclk(); /* Configure PIO pins */ -#warning Missing logic - /* Configure and enable the TWI block */ -#warning Missing logic + sam_configpio(PIO_TWI1_CK); + sam_configpio(PIO_TWI1_D); - /* Select the interrupt handler */ + /* Select the interrupt handler, TWI frequency, and peripheral ID */ - handler = twi1_interrupt; + handler = twi1_interrupt; + frequency = CONFIG_SAMA5_TWI1_FREQUENCY; + pid = SAM_PID_TWI1; } else #endif #ifdef CONFIG_SAMA5_TWI2 if (bus == 2) { + /* Set up TWI2 register base address and IRQ number */ + priv = &g_twi2; priv->base = SAM_TWI2_VBASE; priv->irq = SAM_IRQ_TWI2; + priv->twi = 2; + + /* Configure PIO pins */ + + sam_configpio(PIO_TWI2_CK); + sam_configpio(PIO_TWI2_D); /* Enable peripheral clocking */ sam_twi2_enableclk(); - /* Configure PIO pins */ -#warning Missing logic + /* Select the interrupt handler, TWI frequency, and peripheral ID */ - /* Configure and enable the TWI block */ -#warning Missing logic - - /* Select the interrupt handler */ - - handler = twi2_interrupt; + handler = twi2_interrupt; + frequency = CONFIG_SAMA5_TWI2_FREQUENCY; + pid = SAM_PID_TWI2; } else #endif @@ -698,14 +1015,15 @@ struct i2c_dev_s *up_i2cinitialize(int bus) sem_init(&priv->exclsem, 0, 1); sem_init(&priv->waitsem, 0, 0); - /* Configure and enable the TWI hardware */ -#warning Missing logic - /* Allocate a watchdog timer */ priv->timeout = wd_create(); DEBUGASSERT(priv->timeout != 0); + /* Configure and enable the TWI hardware */ + + twi_hw_initialize(priv, pid, frequency); + /* Attach Interrupt Handler */ irq_attach(priv->irq, handler); @@ -729,8 +1047,11 @@ int up_i2cuninitialize(FAR struct i2c_dev_s * dev) { struct twi_dev_s *priv = (struct twi_dev_s *) dev; - /* Disable TWI */ -#warning Missing logic + uvdbg("TWI%d Un-initializing\n", priv->twi); + + /* Disable interrupts */ + + up_disable_irq(priv->irq); /* Reset data structures */ @@ -742,10 +1063,6 @@ int up_i2cuninitialize(FAR struct i2c_dev_s * dev) wd_delete(priv->timeout); priv->timeout = NULL; - /* Disable interrupts */ - - up_disable_irq(priv->irq); - /* Detach Interrupt Handler */ irq_detach(priv->irq);