summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-09-11 16:48:56 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-09-11 16:48:56 -0600
commit038431ed2be1c38bac7ca3a061075d66c5e4b7a3 (patch)
treecf10d95eebc9b3e9cb33e8ee248949990338d71c
parent9f0053785f164e136356cc2446551f81892aa728 (diff)
downloadnuttx-038431ed2be1c38bac7ca3a061075d66c5e4b7a3.tar.gz
nuttx-038431ed2be1c38bac7ca3a061075d66c5e4b7a3.tar.bz2
nuttx-038431ed2be1c38bac7ca3a061075d66c5e4b7a3.zip
SAMA5: Barebones TWI driver implementation
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_pmc.h13
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_twi.h1
-rw-r--r--nuttx/arch/arm/src/sama5/sam_twi.c491
-rw-r--r--nuttx/configs/sama5d3x-ek/README.txt18
4 files changed, 430 insertions, 93 deletions
diff --git a/nuttx/arch/arm/src/sama5/chip/sam_pmc.h b/nuttx/arch/arm/src/sama5/chip/sam_pmc.h
index 53ae2092c..ba2c75990 100644
--- a/nuttx/arch/arm/src/sama5/chip/sam_pmc.h
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/chip/sam_twi.h b/nuttx/arch/arm/src/sama5/chip/sam_twi.h
index bd0bc3984..bb649f7a7 100644
--- a/nuttx/arch/arm/src/sama5/chip/sam_twi.h
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/sam_twi.c b/nuttx/arch/arm/src/sama5/sam_twi.c
index be2970c59..4103cb77a 100644
--- a/nuttx/arch/arm/src/sama5/sam_twi.c
+++ b/nuttx/arch/arm/src/sama5/sam_twi.c
@@ -4,6 +4,15 @@
* Copyright (C) 2013 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
+ * 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);
- wd_cancel(priv->timeout);
- sam_givesem(&priv->exclsem);
+ 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);
- /* Return the result of the transfer */
+ /* Cancel the timeout and return the result of the transfer */
+ wd_cancel(priv->timeout);
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;
+
+ /* 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);
- /* Process each unmasked bit in the interrupt status */
-#warning Missing logic
+ /* 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;
+
+ /* 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_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);
- ret = twi_start(priv);
+ /* Enable write interrupt */
- return ret > 0 ? OK : -ETIMEDOUT;
+ 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;
+
+ /* Set STOP signal if only one byte is sent*/
+
+ 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));
- ret = twi_start(priv);
+ /* Set internal address bytes */
- return ret > 0 ? OK : -ETIMEDOUT;
+ 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;
@@ -595,6 +797,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;
- /* Enable peripheral clocking */
+ /* Configure PIO pins */
- sam_twi2_enableclk();
+ sam_configpio(PIO_TWI2_CK);
+ sam_configpio(PIO_TWI2_D);
- /* Configure PIO pins */
-#warning Missing logic
+ /* Enable peripheral clocking */
- /* Configure and enable the TWI block */
-#warning Missing logic
+ sam_twi2_enableclk();
- /* Select the interrupt handler */
+ /* Select the interrupt handler, TWI frequency, and peripheral ID */
- 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);
diff --git a/nuttx/configs/sama5d3x-ek/README.txt b/nuttx/configs/sama5d3x-ek/README.txt
index c4f7d1b9a..6b8bb11f1 100644
--- a/nuttx/configs/sama5d3x-ek/README.txt
+++ b/nuttx/configs/sama5d3x-ek/README.txt
@@ -76,6 +76,7 @@ Contents
- Serial FLASH
- HSMCI Card Slots
- USB Ports
+ - AT24 Serial EEPROM
- SAMA5D3x-EK Configuration Options
- Configurations
@@ -618,6 +619,23 @@ USB Ports
---- ----------- -------------------------------------------------------
PD28 OVCUR_USB Combined overrcurrent indication from port A and B
+AT24 Serial EEPROM
+==================
+
+ A AT24C512 Serial EEPPROM was used for tested I2C. There are other I2C/TWI
+ devices on-board, but the serial EEPROM is the simplest test.
+
+ The Serial EEPROM was mounted on an external adaptor board and connected to
+ the SAMA5D3x-EK thusly:
+
+ - VCC -- VCC
+ - GND -- GND
+ - TWCK0(PA31) -- SCL
+ - TWD0(PA30) -- SDA
+
+ By default, PA30 and PA31 are SWJ-DP pins, it can be used as a pin for TWI
+ peripheral in the end application.
+
SAMA5D3x-EK Configuration Options
=================================