From 5448c99ff2b7fdfad20778df7251e502c867f7f4 Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Wed, 12 Jul 2017 06:44:53 -0600 Subject: [PATCH] I was having issues with the bus freezing up .. slaves holding SDL low.. so I rewrote a good portion of the interrupt logic based on the application notes from Atmel. One major improvement is using the RXNACK flag in the STATUS register, which indicates that no device responded to an address packet. Assuming that the chip will always give an interrupt status, I believe it's possible to eliminate the timer as well. --- arch/arm/src/samdl/sam_i2c_master.c | 1132 ++++++++------------------- 1 file changed, 331 insertions(+), 801 deletions(-) diff --git a/arch/arm/src/samdl/sam_i2c_master.c b/arch/arm/src/samdl/sam_i2c_master.c index 3e5a232f27d..38fc1a26b4b 100644 --- a/arch/arm/src/samdl/sam_i2c_master.c +++ b/arch/arm/src/samdl/sam_i2c_master.c @@ -3,6 +3,7 @@ * * Copyright (C) 2013-2014 Gregory Nutt. All rights reserved. * Copyright (C) 2015 Filament - www.filament.com + * Author: Matt Thompson * Author: Alan Carvalho de Assis * Author: Gregory Nutt * @@ -60,7 +61,7 @@ #include #include #include -#include +#include #include @@ -81,6 +82,7 @@ /******************************************************************************* * Pre-processor Definitions *******************************************************************************/ + /* Configuration ***************************************************************/ #ifndef CONFIG_SAM_I2C0_FREQUENCY @@ -88,26 +90,27 @@ #endif #ifndef CONFIG_SAM_I2C1_FREQUENCY - #define CONFIG_SAM_I2C1_FREQUENCY 100000 +# define CONFIG_SAM_I2C1_FREQUENCY 100000 #endif #ifndef CONFIG_SAM_I2C2_FREQUENCY - #define CONFIG_SAM_I2C2_FREQUENCY 100000 +# define CONFIG_SAM_I2C2_FREQUENCY 100000 #endif #ifndef CONFIG_SAM_I2C3_FREQUENCY - #define CONFIG_SAM_I2C3_FREQUENCY 100000 +# define CONFIG_SAM_I2C3_FREQUENCY 100000 #endif #ifndef CONFIG_SAM_I2C4_FREQUENCY - #define CONFIG_SAM_I2C4_FREQUENCY 100000 +# define CONFIG_SAM_I2C4_FREQUENCY 100000 #endif #ifndef CONFIG_SAM_I2C5_FREQUENCY - #define CONFIG_SAM_I2C5_FREQUENCY 100000 +# define CONFIG_SAM_I2C5_FREQUENCY 100000 #endif /* Driver internal definitions *************************************************/ + /* If verbose I2C debug output is enable, then allow more time before we declare * a timeout. The debug output from i2c_interrupt will really slow things down! * @@ -116,69 +119,67 @@ */ #ifdef CONFIG_DEBUG_I2C_INFO -# define I2C_TIMEOUT_MSPB (65000) /* 50 msec/byte */ +# define I2C_TIMEOUT_MSPB (65000) /* 50 msec/byte */ #else -# define I2C_TIMEOUT_MSPB (5000) /* 5 msec/byte */ +# define I2C_TIMEOUT_MSPB (5000) /* 5 msec/byte */ #endif /* Clocking to the I2C module(s) is provided by the main clock, divided down * as necessary. */ -#define I2C_MAX_FREQUENCY 66000000 /* Maximum I2C frequency */ +#define I2C_MAX_FREQUENCY 66000000 /* Maximum I2C frequency */ /******************************************************************************* * Private Types *******************************************************************************/ + /* Invariant attributes of a I2C bus */ struct i2c_attr_s { - uint8_t i2c; /* I2C device number (for debug output) */ - uint8_t sercom; /* Identifies the SERCOM peripheral */ - uint8_t irq; /* SERCOM IRQ number */ - uint8_t gclkgen; /* Source GCLK generator */ - uint8_t slowgen; /* Slow GCLK generator */ - port_pinset_t pad0; /* Pin configuration for PAD0 */ - port_pinset_t pad1; /* Pin configuration for PAD1 */ - uint32_t muxconfig; /* Pad multiplexing configuration */ - uint32_t srcfreq; /* Source clock frequency */ - uintptr_t base; /* Base address of I2C registers */ - bool runinstdby; /* Run in Stand-by ? */ - uint32_t sdaholdtime; /* Hold time after start bit */ - uint32_t speed; /* I2C Speed: Standard; Fast; High */ - bool scllowtout; /* SCL low timeout */ - uint32_t inactout; /* Inactive Bus Timeout */ - bool sclstretch; /* SCL stretch only after ACK */ - bool sclslvextout; /* SCL Slave extend timeout */ - bool sclmstextout; /* SCL Master extend timeout */ + uint8_t i2c; /* I2C device number (for debug output) */ + uint8_t sercom; /* Identifies the SERCOM peripheral */ + uint8_t irq; /* SERCOM IRQ number */ + uint8_t gclkgen; /* Source GCLK generator */ + uint8_t slowgen; /* Slow GCLK generator */ + port_pinset_t pad0; /* Pin configuration for PAD0 */ + port_pinset_t pad1; /* Pin configuration for PAD1 */ + uint32_t muxconfig; /* Pad multiplexing configuration */ + uint32_t srcfreq; /* Source clock frequency */ + uintptr_t base; /* Base address of I2C registers */ + bool runinstdby; /* Run in Stand-by ? */ + uint32_t sdaholdtime; /* Hold time after start bit */ + uint32_t speed; /* I2C Speed: Standard; Fast; High */ + bool scllowtout; /* SCL low timeout */ + uint32_t inactout; /* Inactive Bus Timeout */ + bool sclstretch; /* SCL stretch only after ACK */ + bool sclslvextout; /* SCL Slave extend timeout */ + bool sclmstextout; /* SCL Master extend timeout */ }; /* State of a I2C bus */ struct sam_i2c_dev_s { - struct i2c_dev_s dev; /* Generic I2C device */ + struct i2c_master_s dev; /* I2C master device */ const struct i2c_attr_s *attr; /* Invariant attributes of I2C device */ - struct i2c_msg_s *msg; /* Message list */ - uint32_t frequency; /* I2C transfer clock frequency */ - uint16_t address; /* Slave address */ - uint16_t flags; /* Transfer flags */ - uint8_t msgc; /* Number of message in the message list */ + struct i2c_msg_s *msg; /* Current message being processed */ + uint32_t frequency; /* I2C transfer clock frequency */ + uint16_t flags; /* Transfer flags */ - sem_t exclsem; /* Only one thread can access at a time */ - sem_t waitsem; /* Wait for I2C transfer completion */ - WDOG_ID timeout; /* Watchdog to recover from bus hangs */ - volatile int result; /* The result of the transfer */ - volatile int xfrd; /* Number of bytes transfers */ + sem_t exclsem; /* Only one thread can access at a time */ + sem_t waitsem; /* Wait for I2C transfer completion */ + volatile int result; /* The result of the transfer */ + volatile int xfrd; /* Number of bytes transfers */ /* Debug stuff */ #ifdef CONFIG_SAM_I2C_REGDEBUG - bool wrlast; /* Last was a write */ - uint32_t addrlast; /* Last address */ - uint32_t vallast; /* Last value */ - int ntimes; /* Number of times */ + bool wrlast; /* Last was a write */ + uint32_t addrlast; /* Last address */ + uint32_t vallast; /* Last value */ + int ntimes; /* Number of times */ #endif }; @@ -188,78 +189,54 @@ struct sam_i2c_dev_s /* Low-level helper functions */ -static uint8_t i2c_getreg8(struct sam_i2c_dev_s *priv, - unsigned int offset); +static uint8_t i2c_getreg8(struct sam_i2c_dev_s *priv, unsigned int offset); static void i2c_putreg8(struct sam_i2c_dev_s *priv, uint8_t regval, - unsigned int offset); -static uint16_t i2c_getreg16(struct sam_i2c_dev_s *priv, - unsigned int offset); + unsigned int offset); +static uint16_t i2c_getreg16(struct sam_i2c_dev_s *priv, unsigned int offset); static void i2c_putreg16(struct sam_i2c_dev_s *priv, uint16_t regval, - unsigned int offset); -static uint32_t i2c_getreg32(struct sam_i2c_dev_s *priv, - unsigned int offset); + unsigned int offset); +static uint32_t i2c_getreg32(struct sam_i2c_dev_s *priv, unsigned int offset); static void i2c_putreg32(struct sam_i2c_dev_s *priv, uint32_t regval, - unsigned int offset); + unsigned int offset); -static void i2c_takesem(sem_t *sem); -#define i2c_givesem(sem) (sem_post(sem)) +static void i2c_takesem(sem_t * sem); +#define i2c_givesem(sem) (sem_post(sem)) #ifdef CONFIG_SAM_I2C_REGDEBUG static bool i2c_checkreg(struct sam_i2c_dev_s *priv, bool wr, - uint32_t value, uintptr_t address); + uint32_t value, uintptr_t address); static uint32_t i2c_getabs(struct sam_i2c_dev_s *priv, uintptr_t address); static void i2c_putabs(struct sam_i2c_dev_s *priv, uintptr_t address, - uint32_t value); + uint32_t value); #else -# define i2c_checkreg(priv,wr,value,address) (false) -# define i2c_putabs(p,a,v) putreg32(v,a) -# define i2c_getabs(p,a) getreg32(a) +# define i2c_checkreg(priv,wr,value,address) (false) +# define i2c_putabs(p,a,v) putreg32(v,a) +# define i2c_getabs(p,a) getreg32(a) #endif static inline uint32_t i2c_getrel(struct sam_i2c_dev_s *priv, - unsigned int offset); + unsigned int offset); static inline void i2c_putrel(struct sam_i2c_dev_s *priv, unsigned int offset, - uint32_t value); + uint32_t value); /* I2C transfer helper functions */ static int i2c_wait_for_bus(struct sam_i2c_dev_s *priv, unsigned int size); static void i2c_wakeup(struct sam_i2c_dev_s *priv, int result); -static int i2c_interrupt(int irq, FAR void *context); -static void i2c_timeout(int argc, uint32_t arg, ...); +static int i2c_interrupt(int irq, FAR void *context, void *arg); static void i2c_startread(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg); static void i2c_startwrite(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg); static void i2c_startmessage(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg); -static int i2c_addr_response(struct sam_i2c_dev_s *priv); -/* I2C device operations */ - -static uint32_t i2c_setfrequency(FAR struct i2c_dev_s *dev, - uint32_t frequency); -static int i2c_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits); -static int i2c_write(FAR struct i2c_dev_s *dev, const uint8_t *buffer, - int buflen); -static int i2c_read(FAR struct i2c_dev_s *dev, uint8_t *buffer, int buflen); -#ifdef CONFIG_I2C_WRITEREAD -static int i2c_writeread(FAR struct i2c_dev_s *inst, const uint8_t *wbuffer, - int wbuflen, uint8_t *rbuffer, int rbuflen); -#endif -#ifdef CONFIG_I2C_TRANSFER -static int i2c_transfer(FAR struct i2c_dev_s *dev, - FAR struct i2c_msg_s *msgs, int count); -#endif -#ifdef CONFIG_I2C_SLAVE -static int i2c_setownaddress(FAR struct i2c_dev_s *dev, int addr, int nbits); -static int i2c_registercallback(FAR struct i2c_dev_s *dev, - int (*callback)(FAR void *arg), FAR void *arg); -#endif +static int sam_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count); /* Initialization */ -static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, - uint32_t frequency); +static uint32_t sam_i2c_setfrequency(struct sam_i2c_dev_s *priv, + uint32_t frequency); static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency); static void i2c_wait_synchronization(struct sam_i2c_dev_s *priv); static void i2c_pad_configure(struct sam_i2c_dev_s *priv); @@ -271,24 +248,16 @@ static void i2c_pad_configure(struct sam_i2c_dev_s *priv); #ifdef SAMDL_HAVE_I2C0 static const struct i2c_attr_s g_i2c0attr = { - .i2c = 0, - .sercom = 0, - .irq = SAM_IRQ_SERCOM0, - .gclkgen = BOARD_SERCOM0_GCLKGEN, - .slowgen = BOARD_SERCOM0_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM0_PINMAP_PAD0, - .pad1 = BOARD_SERCOM0_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM0_MUXCONFIG, - .srcfreq = BOARD_SERCOM0_FREQUENCY, - .base = SAM_SERCOM0_BASE, - .runinstdby = BOARD_SERCOM0_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM0_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM0_I2C_SPEED, - .scllowtout = BOARD_SERCOM0_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM0_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM0_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM0_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM0_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 0, + .sercom = 0, + .irq = SAM_IRQ_SERCOM0, + .gclkgen = BOARD_SERCOM0_GCLKGEN, + .slowgen = BOARD_SERCOM0_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM0_PINMAP_PAD0, + .pad1 = BOARD_SERCOM0_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM0_MUXCONFIG, + .srcfreq = BOARD_SERCOM0_FREQUENCY, + .base = SAM_SERCOM0_BASE, }; static struct sam_i2c_dev_s g_i2c0; @@ -296,24 +265,16 @@ static struct sam_i2c_dev_s g_i2c0; #ifdef SAMDL_HAVE_I2C1 static const struct i2c_attr_s g_i2c1attr = { - .i2c = 1, - .sercom = 1, - .irq = SAM_IRQ_SERCOM1, - .gclkgen = BOARD_SERCOM1_GCLKGEN, - .slowgen = BOARD_SERCOM1_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM1_PINMAP_PAD0, - .pad1 = BOARD_SERCOM1_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM1_MUXCONFIG, - .srcfreq = BOARD_SERCOM1_FREQUENCY, - .base = SAM_SERCOM1_BASE, - .runinstdby = BOARD_SERCOM1_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM1_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM1_I2C_SPEED, - .scllowtout = BOARD_SERCOM1_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM1_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM1_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM1_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM1_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 1, + .sercom = 1, + .irq = SAM_IRQ_SERCOM1, + .gclkgen = BOARD_SERCOM1_GCLKGEN, + .slowgen = BOARD_SERCOM1_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM1_PINMAP_PAD0, + .pad1 = BOARD_SERCOM1_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM1_MUXCONFIG, + .srcfreq = BOARD_SERCOM1_FREQUENCY, + .base = SAM_SERCOM1_BASE, }; static struct sam_i2c_dev_s g_i2c1; @@ -322,24 +283,16 @@ static struct sam_i2c_dev_s g_i2c1; #ifdef SAMDL_HAVE_I2C2 static const struct i2c_attr_s g_i2c2attr = { - .i2c = 2, - .sercom = 2, - .irq = SAM_IRQ_SERCOM2, - .gclkgen = BOARD_SERCOM2_GCLKGEN, - .slowgen = BOARD_SERCOM2_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM2_PINMAP_PAD0, - .pad1 = BOARD_SERCOM2_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM2_MUXCONFIG, - .srcfreq = BOARD_SERCOM2_FREQUENCY, - .base = SAM_SERCOM2_BASE, - .runinstdby = BOARD_SERCOM2_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM2_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM2_I2C_SPEED, - .scllowtout = BOARD_SERCOM2_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM2_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM2_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM2_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM2_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 2, + .sercom = 2, + .irq = SAM_IRQ_SERCOM2, + .gclkgen = BOARD_SERCOM2_GCLKGEN, + .slowgen = BOARD_SERCOM2_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM2_PINMAP_PAD0, + .pad1 = BOARD_SERCOM2_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM2_MUXCONFIG, + .srcfreq = BOARD_SERCOM2_FREQUENCY, + .base = SAM_SERCOM2_BASE, }; static struct sam_i2c_dev_s g_i2c2; @@ -348,24 +301,16 @@ static struct sam_i2c_dev_s g_i2c2; #ifdef SAMDL_HAVE_I2C3 static const struct i2c_attr_s g_i2c3attr = { - .i2c = 3, - .sercom = 3, - .irq = SAM_IRQ_SERCOM3, - .gclkgen = BOARD_SERCOM3_GCLKGEN, - .slowgen = BOARD_SERCOM3_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM3_PINMAP_PAD0, - .pad1 = BOARD_SERCOM3_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM3_MUXCONFIG, - .srcfreq = BOARD_SERCOM3_FREQUENCY, - .base = SAM_SERCOM3_BASE, - .runinstdby = BOARD_SERCOM3_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM3_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM3_I2C_SPEED, - .scllowtout = BOARD_SERCOM3_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM3_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM3_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM3_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM3_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 3, + .sercom = 3, + .irq = SAM_IRQ_SERCOM3, + .gclkgen = BOARD_SERCOM3_GCLKGEN, + .slowgen = BOARD_SERCOM3_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM3_PINMAP_PAD0, + .pad1 = BOARD_SERCOM3_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM3_MUXCONFIG, + .srcfreq = BOARD_SERCOM3_FREQUENCY, + .base = SAM_SERCOM3_BASE, }; static struct sam_i2c_dev_s g_i2c3; @@ -374,24 +319,16 @@ static struct sam_i2c_dev_s g_i2c3; #ifdef SAMDL_HAVE_I2C4 static const struct i2c_attr_s g_i2c4attr = { - .i2c = 4, - .sercom = 4, - .irq = SAM_IRQ_SERCOM4, - .gclkgen = BOARD_SERCOM4_GCLKGEN, - .slowgen = BOARD_SERCOM4_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM4_PINMAP_PAD0, - .pad1 = BOARD_SERCOM4_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM4_MUXCONFIG, - .srcfreq = BOARD_SERCOM4_FREQUENCY, - .base = SAM_SERCOM4_BASE, - .runinstdby = BOARD_SERCOM4_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM4_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM4_I2C_SPEED, - .scllowtout = BOARD_SERCOM4_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM4_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM4_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM4_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM4_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 4, + .sercom = 4, + .irq = SAM_IRQ_SERCOM4, + .gclkgen = BOARD_SERCOM4_GCLKGEN, + .slowgen = BOARD_SERCOM4_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM4_PINMAP_PAD0, + .pad1 = BOARD_SERCOM4_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM4_MUXCONFIG, + .srcfreq = BOARD_SERCOM4_FREQUENCY, + .base = SAM_SERCOM4_BASE, }; static struct sam_i2c_dev_s g_i2c4; @@ -400,24 +337,16 @@ static struct sam_i2c_dev_s g_i2c4; #ifdef SAMDL_HAVE_I2C5 static const struct i2c_attr_s g_i2c5attr = { - .i2c = 5, - .sercom = 5, - .irq = SAM_IRQ_SERCOM5, - .gclkgen = BOARD_SERCOM5_GCLKGEN, - .slowgen = BOARD_SERCOM5_SLOW_GCLKGEN, - .pad0 = BOARD_SERCOM5_PINMAP_PAD0, - .pad1 = BOARD_SERCOM5_PINMAP_PAD1, - .muxconfig = BOARD_SERCOM5_MUXCONFIG, - .srcfreq = BOARD_SERCOM5_FREQUENCY, - .base = SAM_SERCOM5_BASE, - .runinstdby = BOARD_SERCOM5_I2C_RUNINSTDBY, - .sdaholdtime = BOARD_SERCOM5_I2C_START_HOLD_TIME, - .speed = BOARD_SERCOM5_I2C_SPEED, - .scllowtout = BOARD_SERCOM5_I2C_SCL_LOW_TIMEOUT, - .inactout = BOARD_SERCOM5_I2C_INACTIVE_TIMEOUT, - .sclstretch = BOARD_SERCOM5_I2C_SCL_STRETCH_ACK, - .sclslvextout = BOARD_SERCOM5_I2C_SCL_SLAVE_EXT_TIMEOUT, - .sclmstextout = BOARD_SERCOM5_I2C_SCL_MASTER_EXT_TIMEOUT, + .i2c = 5, + .sercom = 5, + .irq = SAM_IRQ_SERCOM5, + .gclkgen = BOARD_SERCOM5_GCLKGEN, + .slowgen = BOARD_SERCOM5_SLOW_GCLKGEN, + .pad0 = BOARD_SERCOM5_PINMAP_PAD0, + .pad1 = BOARD_SERCOM5_PINMAP_PAD1, + .muxconfig = BOARD_SERCOM5_MUXCONFIG, + .srcfreq = BOARD_SERCOM5_FREQUENCY, + .base = SAM_SERCOM5_BASE, }; static struct sam_i2c_dev_s g_i2c5; @@ -425,46 +354,36 @@ static struct sam_i2c_dev_s g_i2c5; struct i2c_ops_s g_i2cops = { - .setfrequency = i2c_setfrequency, - .setaddress = i2c_setaddress, - .write = i2c_write, - .read = i2c_read, -#ifdef CONFIG_I2C_WRITEREAD - .writeread = i2c_writeread, -#endif -#ifdef CONFIG_I2C_TRANSFER - .transfer = i2c_transfer -#endif -#ifdef CONFIG_I2C_SLAVE - .setownaddress = i2c_setownaddress - .registercallback = i2c_registercallback + .transfer = sam_i2c_transfer, +#ifdef CONFIG_I2C_RESET + .reset = sam_i2c_reset, #endif }; -/**************************************************************************** +/******************************************************************************* * Low-level Helpers - ****************************************************************************/ + *******************************************************************************/ -/************************************************************************************ +/******************************************************************************* * Name: i2c_getreg8 * * Description: * Get a 8-bit register value by offset * - ************************************************************************************/ + *******************************************************************************/ static uint8_t i2c_getreg8(struct sam_i2c_dev_s *priv, unsigned int offset) { return getreg8(priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_putreg8 * * Description: * Put a 8-bit register value by offset * - ************************************************************************************/ + *******************************************************************************/ static void i2c_putreg8(struct sam_i2c_dev_s *priv, uint8_t regval, unsigned int offset) @@ -472,52 +391,53 @@ static void i2c_putreg8(struct sam_i2c_dev_s *priv, uint8_t regval, putreg8(regval, priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_getreg16 * * Description: * Get a 16-bit register value by offset * - ************************************************************************************/ -static uint16_t i2c_getreg16(struct sam_i2c_dev_s *priv, - unsigned int offset) + *******************************************************************************/ + +static uint16_t i2c_getreg16(struct sam_i2c_dev_s *priv, unsigned int offset) { return getreg16(priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_putreg16 * * Description: * Put a 16-bit register value by offset * - ************************************************************************************/ -static void i2c_putreg16(struct sam_i2c_dev_s *priv, uint16_t regval, - unsigned int offset) + *******************************************************************************/ + +static void i2c_putreg16(struct sam_i2c_dev_s *priv, uint16_t regval, + unsigned int offset) { putreg16(regval, priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_getreg32 * * Description: * Get a 32-bit register value by offset * - ************************************************************************************/ -static uint32_t i2c_getreg32(struct sam_i2c_dev_s *priv, - unsigned int offset) + *******************************************************************************/ + +static uint32_t i2c_getreg32(struct sam_i2c_dev_s *priv, unsigned int offset) { return getreg32(priv->attr->base + offset); } -/************************************************************************************ +/******************************************************************************* * Name: i2c_putreg32 * * Description: * Put a 32-bit register value by offset * - ************************************************************************************/ + *******************************************************************************/ static void i2c_putreg32(struct sam_i2c_dev_s *priv, uint32_t regval, unsigned int offset) @@ -525,7 +445,7 @@ static void i2c_putreg32(struct sam_i2c_dev_s *priv, uint32_t regval, putreg32(regval, priv->attr->base + offset); } -/**************************************************************************** +/******************************************************************************* * Name: i2c_takesem * * Description: @@ -538,9 +458,9 @@ static void i2c_putreg32(struct sam_i2c_dev_s *priv, uint32_t regval, * Returned Value: * None * - ****************************************************************************/ + *******************************************************************************/ -static void i2c_takesem(sem_t *sem) +static void i2c_takesem(sem_t * sem) { /* Take the semaphore (perhaps waiting) */ @@ -554,7 +474,7 @@ static void i2c_takesem(sem_t *sem) } } -/**************************************************************************** +/******************************************************************************* * Name: i2c_checkreg * * Description: @@ -568,15 +488,15 @@ static void i2c_takesem(sem_t *sem) * true: This is the first register access of this type. * false: This is the same as the preceding register access. * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_SAM_I2C_REGDEBUG static bool i2c_checkreg(struct sam_i2c_dev_s *priv, bool wr, uint32_t value, uint32_t address) { - if (wr == priv->wrlast && /* Same kind of access? */ - value == priv->vallast && /* Same value? */ - address == priv->addrlast) /* Same address? */ + if (wr == priv->wrlast && /* Same kind of access? */ + value == priv->vallast && /* Same value? */ + address == priv->addrlast) /* Same address? */ { /* Yes, then just keep a count of the number of times we did this. */ @@ -608,13 +528,13 @@ static bool i2c_checkreg(struct sam_i2c_dev_s *priv, bool wr, uint32_t value, } #endif -/**************************************************************************** +/******************************************************************************* * Name: i2c_getabs * * Description: * Read any 32-bit register using an absolute * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_SAM_I2C_REGDEBUG static uint32_t i2c_getabs(struct sam_i2c_dev_s *priv, uintptr_t address) @@ -630,13 +550,13 @@ static uint32_t i2c_getabs(struct sam_i2c_dev_s *priv, uintptr_t address) } #endif -/**************************************************************************** +/******************************************************************************* * Name: i2c_putabs * * Description: * Write to any 32-bit register using an absolute address * - ****************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_SAM_I2C_REGDEBUG static void i2c_putabs(struct sam_i2c_dev_s *priv, uintptr_t address, @@ -651,27 +571,28 @@ static void i2c_putabs(struct sam_i2c_dev_s *priv, uintptr_t address, } #endif -/**************************************************************************** +/******************************************************************************* * Name: i2c_getrel * * Description: * Read a I2C register using an offset relative to the I2C base address * - ****************************************************************************/ + *******************************************************************************/ -static inline uint32_t i2c_getrel(struct sam_i2c_dev_s *priv, unsigned int offset) +static inline uint32_t i2c_getrel(struct sam_i2c_dev_s *priv, + unsigned int offset) { return i2c_getabs(priv, priv->attr->base + offset); } -/**************************************************************************** +/******************************************************************************* * Name: i2c_putrel * * Description: * Write a value to a I2C register using an offset relative to the I2C base * address. * - ****************************************************************************/ + *******************************************************************************/ static inline void i2c_putrel(struct sam_i2c_dev_s *priv, unsigned int offset, uint32_t value) @@ -679,15 +600,16 @@ static inline void i2c_putrel(struct sam_i2c_dev_s *priv, unsigned int offset, i2c_putabs(priv, priv->attr->base + offset, value); } -/**************************************************************************** +/******************************************************************************* * I2C transfer helper functions - ****************************************************************************/ + *******************************************************************************/ /******************************************************************************* * Name: i2c_wait_for_bus * * Description: - * Perform a I2C transfer start + * Wait for the ISR to post the semaphore, indicating the transaction is + * complete * * Assumptions: * Interrupts are disabled @@ -696,23 +618,18 @@ static inline void i2c_putrel(struct sam_i2c_dev_s *priv, unsigned int offset, static int i2c_wait_for_bus(struct sam_i2c_dev_s *priv, unsigned int size) { - int32_t timeout; - uint32_t regval; + struct timespec ts; - timeout = MSEC2TICK(I2C_TIMEOUT_MSPB); - if (timeout < 1) + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += 200e3; + + if (sem_timedwait(&priv->waitsem, (const struct timespec *)&ts) != OK) { - timeout = 1; + i2cinfo("timedwait error %d\n", errno); + return -errno; } - while (!((regval = i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET)) & I2C_INT_MB) && - !((regval = i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET)) & I2C_INT_SB)) - { - if (--timeout == 0) - return -ETIMEDOUT; - } - - return timeout; + return priv->result; } /******************************************************************************* @@ -725,17 +642,14 @@ static int i2c_wait_for_bus(struct sam_i2c_dev_s *priv, unsigned int size) static void i2c_wakeup(struct sam_i2c_dev_s *priv, int result) { - /* Cancel any pending timeout */ - - wd_cancel(priv->timeout); - /* Disable any further I2C interrupts */ + i2c_putreg8(priv, I2C_INT_MB | I2C_INT_SB, SAM_I2C_INTENCLR_OFFSET); /* Wake up the waiting thread with the result of the transfer */ priv->result = result; - i2c_givesem(&priv->waitsem); + sem_post(&priv->waitsem); } /******************************************************************************* @@ -748,11 +662,8 @@ static void i2c_wakeup(struct sam_i2c_dev_s *priv, int result) static int i2c_interrupt(int irq, FAR void *context, FAR void *arg) { - struct sam_i2c_dev_s *priv = ()arg; + struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)arg; struct i2c_msg_s *msg; - uint32_t sr; - uint32_t imr; - uint32_t pending; uint32_t regval; msg = priv->msg; @@ -765,44 +676,53 @@ static int i2c_interrupt(int irq, FAR void *context, FAR void *arg) /* Clear error INTFLAG */ - i2c_putreg16(priv, I2C_INT_ERR, SAM_I2C_INTFLAG_OFFSET); + i2c_putreg16(priv, I2C_INT_ERROR, SAM_I2C_INTFLAG_OFFSET); /* Cancel timeout */ - i2c_wakeup(priv, OK); + i2c_wakeup(priv, -EBUSY); return -EBUSY; } - /* It is a read or write operation? */ - - if (msg->flags & I2C_M_READ) + if ((i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET) & I2C_INT_SB) == I2C_INT_SB) { - /* Clear read interrupt flag */ - //i2c_putreg8(priv, I2C_INT_SB, SAM_I2C_INTFLAG_OFFSET); - - /* Send STOP after transmit last byte */ + /* Send NACK and STOP after transmission of last byte */ if (priv->xfrd == (msg->length - 1)) { - /* Wait for sync */ + /* NACK */ + regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); + regval |= I2C_CTRLB_ACKACT; + i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); i2c_wait_synchronization(priv); - /* Send STOP condition */ + /* STOP */ regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); regval |= I2C_CTRLB_CMD_ACKSTOP; i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); + i2c_wait_synchronization(priv); + + msg->buffer[priv->xfrd++] = i2c_getreg8(priv, SAM_I2C_DATA_OFFSET); + i2c_wait_synchronization(priv); } + else + { + regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); + regval &= ~I2C_CTRLB_ACKACT; + i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); + i2c_wait_synchronization(priv); - /* Wait for Sync */ + msg->buffer[priv->xfrd++] = i2c_getreg8(priv, SAM_I2C_DATA_OFFSET); + i2c_wait_synchronization(priv); - i2c_wait_synchronization(priv); - - /* Read byte from bus */ - - msg->buffer[priv->xfrd++] = i2c_getreg8(priv, SAM_I2C_DATA_OFFSET); + regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); + regval |= I2C_CTRLB_CMD_ACKREAD; + i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); + i2c_wait_synchronization(priv); + } /* Disable Interrupt after last byte */ @@ -811,109 +731,47 @@ static int i2c_interrupt(int irq, FAR void *context, FAR void *arg) /* Cancel timeout */ i2c_wakeup(priv, OK); - i2cinfo("Got data = 0x%02X\n", msg->buffer[0]); + // i2cinfo("Got data = 0x%02X\n", msg->buffer[0]); } + + i2c_putreg8(priv, I2C_INT_SB, SAM_I2C_INTFLAG_OFFSET); } - else + + if ((i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET) & I2C_INT_MB) == I2C_INT_MB) { - /* Wait for sync */ + /* If no device responded to the address packet, STATUS.RXNACK will be + * set + */ - i2c_wait_synchronization(priv); - - /* Write byte to bus */ - - i2c_putreg8(priv, msg->buffer[priv->xfrd], SAM_I2C_DATA_OFFSET); - priv->xfrd++; - - /* Clear write interrupt flag */ - - i2c_putreg8(priv, I2C_INT_MB, SAM_I2C_INTFLAG_OFFSET); + if ((i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET) & I2C_STATUS_RXNACK) == + I2C_STATUS_RXNACK) + { + i2c_wakeup(priv, -ENODEV); + return OK; + } if (priv->xfrd == msg->length) { - /* Wait for sync */ - - i2c_wait_synchronization(priv); - /* Send STOP condition */ regval = i2c_getreg32(priv, SAM_I2C_CTRLB_OFFSET); regval |= I2C_CTRLB_CMD_ACKSTOP; i2c_putreg32(priv, regval, SAM_I2C_CTRLB_OFFSET); + i2c_wait_synchronization(priv); - /* Disable Interrupts */ - - regval = I2C_INT_MB | I2C_INT_SB; - i2c_putreg8(priv, regval, SAM_I2C_INTENCLR_OFFSET); + i2c_wakeup(priv, OK); } + else + { + i2c_putreg8(priv, msg->buffer[priv->xfrd++], SAM_I2C_DATA_OFFSET); + } + + i2c_putreg8(priv, I2C_INT_MB, SAM_I2C_INTFLAG_OFFSET); } return OK; } -/******************************************************************************* - * Name: i2c_timeout - * - * Description: - * Watchdog timer for timeout of I2C operation - * - * Assumptions: - * Called from the timer interrupt handler with interrupts disabled. - * - *******************************************************************************/ - -static void i2c_timeout(int argc, uint32_t arg, ...) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)arg; - - i2cerr("ERROR: I2C%d Timeout!\n", priv->attr->i2c); - i2c_wakeup(priv, -ETIMEDOUT); -} - -/******************************************************************************* - * Name: i2c_addr_response - * - * Description: - * Detect and clear I2C address bus response or timeout - * - *******************************************************************************/ - -static int i2c_addr_response(struct sam_i2c_dev_s *priv) -{ - uint32_t regval; - - /* Verify if there are errors */ - - regval = i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET); - if (regval & I2C_INT_SB) - { - /* Clear the interrupt flag */ - - i2c_putreg8(priv, I2C_INT_SB, SAM_I2C_INTFLAG_OFFSET); - - /* Check arbitration */ - - regval = i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET); - if (regval & I2C_STATUS_ARBLOST) - { - i2cerr("ERROR: Transfer I2C Bus Collision!\n"); - return -EAGAIN; - } - } - - /* Verify if slave device reply with ACK */ - - else if ((regval = i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET)) & I2C_STATUS_RXNACK) - { - /* Slave is busy, issue an ACK and STOP */ - - i2c_putreg32(priv, I2C_CTRLB_CMD_ACKSTOP, SAM_I2C_CTRLB_OFFSET); - return -EBUSY; - } - - return 0; -} - /******************************************************************************* * Name: i2c_startread * @@ -924,13 +782,12 @@ static int i2c_addr_response(struct sam_i2c_dev_s *priv) static void i2c_startread(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) { - int ret; uint32_t regval; /* Setup for the transfer */ priv->result = -EBUSY; - priv->xfrd = 0; + priv->xfrd = 0; /* Set action to ACK */ @@ -961,6 +818,7 @@ static void i2c_startread(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) /* Set the ADDR register */ i2c_putreg32(priv, regval, SAM_I2C_ADDR_OFFSET); + i2c_wait_synchronization(priv); } /******************************************************************************* @@ -973,13 +831,12 @@ static void i2c_startread(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) static void i2c_startwrite(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) { - int ret; uint32_t regval; /* Setup for the transfer */ priv->result = -EBUSY; - priv->xfrd = 0; + priv->xfrd = 0; /* Wait bus sync */ @@ -1014,6 +871,7 @@ static void i2c_startwrite(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) /* Set the ADDR register */ i2c_putreg32(priv, regval, SAM_I2C_ADDR_OFFSET); + i2c_wait_synchronization(priv); } /******************************************************************************* @@ -1041,287 +899,7 @@ static void i2c_startmessage(struct sam_i2c_dev_s *priv, struct i2c_msg_s *msg) *******************************************************************************/ /******************************************************************************* - * Name: i2c_setfrequency - * - * Description: - * Set the frequency for the next transfer - * - *******************************************************************************/ - -static uint32_t i2c_setfrequency(FAR struct i2c_dev_s *dev, uint32_t frequency) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; - uint32_t actual; - - DEBUGASSERT(dev); - - i2cinfo("sercom=%d frequency=%d\n", priv->attr->sercom, frequency); - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* And setup the clock frequency */ - - actual = i2c_hw_setfrequency(priv, frequency); - i2c_givesem(&priv->exclsem); - return actual; -} - -/******************************************************************************* - * Name: i2c_setaddress - * - * Description: - * Set the I2C slave address for a subsequent read/write - * - *******************************************************************************/ - -static int i2c_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *) dev; - - i2cinfo("I2C%d address: %02x nbits: %d\n", priv->attr->i2c, addr, nbits); - DEBUGASSERT(dev != NULL && nbits == 7); - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* Remember 7- or 10-bit address */ - - priv->address = addr; - priv->flags = (nbits == 10) ? I2C_M_TEN : 0; - - i2c_givesem(&priv->exclsem); - return OK; -} - -/******************************************************************************* - * Name: i2c_write - * - * Description: - * Send a block of data on I2C using the previously selected I2C - * frequency and slave address. - * - *******************************************************************************/ - -static int i2c_write(FAR struct i2c_dev_s *dev, const uint8_t *wbuffer, int wbuflen) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *) dev; - irqstate_t flags; - int ret; - - struct i2c_msg_s msg = - { - .addr = priv->address, - .flags = priv->flags, - .buffer = (uint8_t *)wbuffer, /* Override const */ - .length = wbuflen - }; - - i2cinfo("I2C%d buflen: %d\n", priv->attr->i2c, wbuflen); - DEBUGASSERT(dev != NULL); - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* Initiate the write */ - - priv->msg = &msg; - priv->msgc = 1; - - /* Initiate the write operation. The rest will be handled from interrupt - * logic. Interrupts must be disabled to prevent re-entrance from the - * interrupt level. - */ - - flags = enter_critical_section(); - i2c_startwrite(priv, &msg); - - /* And wait for the write to complete. Interrupts will be re-enabled while - * we are waiting. - */ - - ret = i2c_wait_for_bus(priv, wbuflen); - if (ret < 0) - { - i2cerr("ERROR: Transfer failed: %d\n", ret); - } - - leave_critical_section(flags); - i2c_givesem(&priv->exclsem); - return ret; -} - -/******************************************************************************* - * Name: i2c_read - * - * Description: - * Receive a block of data on I2C using the previously selected I2C - * frequency and slave address. - * - *******************************************************************************/ - -static int i2c_read(FAR struct i2c_dev_s *dev, uint8_t *rbuffer, int rbuflen) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; - irqstate_t flags; - int ret; - - struct i2c_msg_s msg = - { - .addr = priv->address, - .flags = priv->flags | I2C_M_READ, - .buffer = rbuffer, - .length = rbuflen - }; - - DEBUGASSERT(dev != NULL); - i2cinfo("I2C%d rbuflen: %d\n", priv->attr->i2c, rbuflen); - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* Initiate the read */ - - priv->msg = &msg; - priv->msgc = 1; - - /* Initiate the read operation. The rest will be handled from interrupt - * logic. Interrupts must be disabled to prevent re-entrance from the - * interrupt level. - */ - - flags = enter_critical_section(); - i2c_startread(priv, &msg); - - /* And wait for the read to complete. Interrupts will be re-enabled while - * we are waiting. - */ - - ret = i2c_wait_for_bus(priv, rbuflen); - if (ret < 0) - { - i2cerr("ERROR: Transfer failed: %d\n", ret); - } - - leave_critical_section(flags); - i2c_givesem(&priv->exclsem); - return ret; -} - -/******************************************************************************* - * Name: i2c_writeread - * - * Description: - * - *******************************************************************************/ - -#ifdef CONFIG_I2C_WRITEREAD -static int i2c_writeread(FAR struct i2c_dev_s *dev, const uint8_t *wbuffer, - int wbuflen, uint8_t *rbuffer, int rbuflen) -{ - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; - struct i2c_msg_s msgv[2]; - irqstate_t flags; - int ret; - - DEBUGASSERT(dev != NULL); - i2cinfo("I2C%d wbuflen: %d rbuflen: %d\n", priv->attr->i2c, wbuflen, rbuflen); - - /* Format two messages: The first is a write */ - - msgv[0].addr = priv->address; - msgv[0].flags = priv->flags; - msgv[0].buffer = (uint8_t *)wbuffer; /* Override const */ - msgv[0].length = wbuflen; - - /* The second is either a read (rbuflen > 0) or a write (rbuflen < 0) with - * no restart. - */ - - if (rbuflen > 0) - { - msgv[1].flags = (priv->flags | I2C_M_READ); - } - else - { - msgv[1].flags = (priv->flags | I2C_M_NORESTART), - rbuflen = -rbuflen; - } - - msgv[1].addr = priv->address; - msgv[1].buffer = rbuffer; - msgv[1].length = rbuflen; - - /* Get exclusive access to the device */ - - i2c_takesem(&priv->exclsem); - - /* Initiate the read */ - - priv->msg = msgv; - priv->msgc = 2; - - /* Initiate the write operation. The rest will be handled from interrupt - * logic. Interrupts must be disabled to prevent re-entrance from the - * interrupt level. - */ - - flags = enter_critical_section(); - i2c_startwrite(priv, msgv); - - /* And wait for the write/read to complete. Interrupts will be re-enabled - * while we are waiting. - */ - - ret = i2c_wait_for_bus(priv, wbuflen + rbuflen); - if (ret < 0) - { - i2cerr("ERROR: Transfer failed: %d\n", ret); - } - - leave_critical_section(flags); - i2c_givesem(&priv->exclsem); - return ret; -} -#endif - -/******************************************************************************* - * Name: i2c_setownaddress - * - * Description: - * - *******************************************************************************/ - -#ifdef CONFIG_I2C_SLAVE -static int i2c_setownaddress(FAR struct i2c_dev_s *dev, int addr, int nbits) -{ -#error Not implemented - return -ENOSYS; -} -#endif - -/******************************************************************************* - * Name: i2c_registercallback - * - * Description: - * - *******************************************************************************/ - -#ifdef CONFIG_I2C_SLAVE -static int i2c_registercallback((FAR struct i2c_dev_s *dev, - int (*callback)(FAR void *arg), FAR void *arg) -{ -#error Not implemented - return -ENOSYS; -} -#endif - -/******************************************************************************* - * Name: i2c_transfer + * Name: sam_i2c_transfer * * Description: * Receive a block of data on I2C using the previously selected I2C @@ -1332,18 +910,27 @@ static int i2c_registercallback((FAR struct i2c_dev_s *dev, * *******************************************************************************/ -#ifdef CONFIG_I2C_TRANSFER -static int i2c_transfer(FAR struct i2c_dev_s *dev, - FAR struct i2c_msg_s *msgs, int count) +static int sam_i2c_transfer(FAR struct i2c_master_s *dev, + FAR struct i2c_msg_s *msgs, int count) { struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; irqstate_t flags; unsigned int size; int i; - int ret; + int ret = -EBUSY; DEBUGASSERT(dev != NULL && msgs != NULL && count > 0); - i2cinfo("I2C%d count: %d\n", priv->attr->i2c, count); + + /* Set the frequency from the first message in the msgs vector */ + + if (count) + { + if (priv->frequency != msgs->frequency) + { + sam_i2c_setfrequency(priv, msgs->frequency); + priv->frequency = msgs->frequency; + } + } /* Calculate the total transfer size so that we can calculate a reasonable * timeout value. @@ -1363,53 +950,68 @@ static int i2c_transfer(FAR struct i2c_dev_s *dev, /* Initiate the message transfer */ - priv->msg = msgs; - priv->msgc = count; - - /* Initiate the transfer. The rest will be handled from interrupt - * logic. Interrupts must be disabled to prevent re-entrance from the - * interrupt level. + /* Initiate the transfer. The rest will be handled from interrupt logic. + * Interrupts must be disabled to prevent re-entrance from the interrupt + * level. */ - flags = enter_critical_section(); - i2c_startmessage(priv, msgs); - - /* And wait for the transfers to complete. Interrupts will be re-enabled - * while we are waiting. - */ - - ret = i2c_wait_for_bus(priv, size); - if (ret < 0) + while (count--) { - i2cerr("ERROR: Transfer failed: %d\n", ret); + priv->msg = msgs; + flags = enter_critical_section(); + i2c_startmessage(priv, msgs); + + /* And wait for the transfers to complete. Interrupts will be re-enabled + * while we are waiting. + */ + + ret = i2c_wait_for_bus(priv, msgs->length); + if (ret < 0) + { +#if 0 + i2cerr("ERROR: Transfer failed: %d\n", ret); + i2cinfo("STATUS: 0x%08x\n", + i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET)); + i2cinfo("INTFLAG: 0x%02x\n", + i2c_getreg8(priv, SAM_I2C_INTFLAG_OFFSET)); +#endif + leave_critical_section(flags); + i2c_givesem(&priv->exclsem); + return ret; + } + + leave_critical_section(flags); + + /* Move to the next message */ + + msgs++; } - leave_critical_section(flags); i2c_givesem(&priv->exclsem); return ret; } -#endif /******************************************************************************* * Initialization *******************************************************************************/ /******************************************************************************* - * Name: i2c_hw_setfrequency + * Name: sam_i2c_setfrequency * * Description: * Set the frequency for the next transfer * *******************************************************************************/ -static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, uint32_t frequency) +static uint32_t sam_i2c_setfrequency(struct sam_i2c_dev_s *priv, + uint32_t frequency) { uint32_t maxfreq; uint32_t baud = 0; uint32_t baud_hs = 0; uint32_t ctrla; - i2cinfo("sercom=%d frequency=%d\n", priv->attr->sercom, frequency); + // i2cinfo("sercom=%d frequency=%d\n", priv->attr->sercom, frequency); /* Check if the configured BAUD is within the valid range */ @@ -1445,6 +1047,7 @@ static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, uint32_t frequen else { /* Find baudrate for high speed */ + baud_hs = ((priv->attr->srcfreq * 10) / (478 * frequency)) - 1; if (baud_hs > 255) @@ -1455,7 +1058,7 @@ static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, uint32_t frequen } /* Momentarily disable I2C while we apply the new BAUD setting (if it was - * previously enabled) + * previously enabled). */ ctrla = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); @@ -1468,18 +1071,23 @@ static uint32_t i2c_hw_setfrequency(struct sam_i2c_dev_s *priv, uint32_t frequen /* Set the new BAUD value */ - i2c_putreg32(priv, (uint32_t) ((baud_hs << 16) | baud), SAM_I2C_BAUD_OFFSET); + i2c_putreg32(priv, (uint32_t) ((baud_hs << 16) | baud), + SAM_I2C_BAUD_OFFSET); /* Re-enable I2C.. waiting for synchronization */ i2c_putreg32(priv, ctrla, SAM_I2C_CTRLA_OFFSET); i2c_wait_synchronization(priv); + + i2c_putreg16(priv, I2C_STATUS_BUSSTATE_IDLE, SAM_I2C_STATUS_OFFSET); + i2c_wait_synchronization(priv); } else { /* Set the new BAUD when the I2C is already disabled */ - i2c_putreg32(priv, (uint32_t) ((baud_hs << 16) | baud), SAM_I2C_BAUD_OFFSET); + i2c_putreg32(priv, (uint32_t) ((baud_hs << 16) | baud), + SAM_I2C_BAUD_OFFSET); } priv->frequency = frequency; @@ -1511,8 +1119,7 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) /* Configure the GCLKs for the SERCOM module */ - //sercom_coreclk_configure(priv->attr->sercom, priv->attr->gclkgen, false); - sam_gclk_chan_enable(priv->attr->sercom + GCLK_CHAN_SERCOM0_CORE, priv->attr->gclkgen); + sercom_coreclk_configure(priv->attr->sercom, priv->attr->gclkgen, false); sercom_slowclk_configure(priv->attr->sercom, priv->attr->slowgen); /* Check if module is enabled */ @@ -1520,9 +1127,10 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); if (regval & I2C_CTRLA_ENABLE) { - i2cerr("ERROR: Cannot initialize I2C because it is already initialized!\n"); + i2cerr + ("ERROR: Cannot initialize I2C because it is already initialized!\n"); return; - } + } /* Check if reset is in progress */ @@ -1533,67 +1141,15 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) return; } - /* Set the SERCOM in I2C master mode */ - - regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); - regval &= ~I2C_CTRLA_MODE_MASK; - regval |= (I2C_CTRLA_MODE_MASTER); - i2c_putreg32(priv, regval, SAM_I2C_CTRLA_OFFSET); - /* Configure pads */ i2c_pad_configure(priv); - /* Should it run in stand-by mode ?*/ - - if (priv->attr->runinstdby) - { - ctrla = I2C_CTRLA_RUNSTDBY; - } - - /* Setup start data hold timeout */ - - ctrla |= priv->attr->sdaholdtime; - - /* Setup transfer speed */ - - ctrla |= priv->attr->speed; - - /* Setup Inactive Bus Timeout */ - - ctrla |= priv->attr->inactout; - - /* Setup SCL low timeout */ - - if (priv->attr->scllowtout) - { - ctrla |= I2C_CTRLA_LOWTOUT; - } - - /* Setup SCL clock stretch mode */ - - if (priv->attr->sclstretch) - { - ctrla |= I2C_CTRLA_SCLAM; - } - - /* Setup slave SCL low extend timeout */ - - if (priv->attr->sclslvextout) - { - ctrla |= I2C_CTRLA_SEXTTOEN; - } - - /* Setup master SCL low extend timeout */ - - if (priv->attr->sclmstextout) - { - ctrla |= I2C_CTRLA_MEXTTOEN; - } - - regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); - regval |= ctrla; - i2c_putreg32(priv, regval, SAM_I2C_CTRLA_OFFSET); + ctrla = + I2C_CTRLA_MODE_MASTER | I2C_CTRLA_RUNSTDBY | I2C_CTRLA_SPEED_FAST | + I2C_CTRLA_SDAHOLD_450NS | priv->attr->muxconfig; + i2c_putreg32(priv, ctrla, SAM_I2C_CTRLA_OFFSET); + i2c_wait_synchronization(priv); /* Enable Smart Mode */ @@ -1601,23 +1157,22 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) /* Set an initial baud value. */ - (void)i2c_hw_setfrequency(priv, 100000); + sam_i2c_setfrequency(priv, 100000); /* Enable I2C */ - regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); + regval = i2c_getreg32(priv, SAM_I2C_CTRLA_OFFSET); regval |= I2C_CTRLA_ENABLE; i2c_putreg32(priv, regval, SAM_I2C_CTRLA_OFFSET); - i2c_wait_synchronization(priv); /* Force IDLE bus state */ - regval = i2c_getreg16(priv, SAM_I2C_STATUS_OFFSET); - if (!(regval & I2C_STATUS_BUSSTATE_IDLE)) - { - i2c_putreg16(priv, I2C_STATUS_BUSSTATE_IDLE, SAM_I2C_STATUS_OFFSET); - } + i2c_putreg16(priv, I2C_STATUS_BUSSTATE_IDLE, SAM_I2C_STATUS_OFFSET); + i2c_wait_synchronization(priv); + + i2c_putreg8(priv, I2C_INT_ALL, SAM_I2C_INTENCLR_OFFSET); + i2c_putreg8(priv, I2C_INT_ALL, SAM_I2C_INTFLAG_OFFSET); /* Enable SERCOM interrupts at the NVIC */ @@ -1625,26 +1180,26 @@ static void i2c_hw_initialize(struct sam_i2c_dev_s *priv, uint32_t frequency) leave_critical_section(flags); } -/**************************************************************************** +/******************************************************************************* * Name: i2c_wait_synchronization * * Description: * Wait until the SERCOM I2C reports that it is synchronized. * - ****************************************************************************/ + *******************************************************************************/ static void i2c_wait_synchronization(struct sam_i2c_dev_s *priv) { while ((i2c_getreg16(priv, SAM_I2C_SYNCBUSY_OFFSET) & 0x7) != 0); } -/**************************************************************************** +/******************************************************************************* * Name: i2c_pad_configure * * Description: * Configure the SERCOM I2C pads. * - ****************************************************************************/ + *******************************************************************************/ static void i2c_pad_configure(struct sam_i2c_dev_s *priv) { @@ -1666,28 +1221,26 @@ static void i2c_pad_configure(struct sam_i2c_dev_s *priv) *******************************************************************************/ /******************************************************************************* - * Name: up_i2cinitialize + * Name: sam_i2c_master_initialize * * Description: * Initialize a I2C device for I2C operation * *******************************************************************************/ -struct i2c_dev_s *up_i2cinitialize(int bus) +struct i2c_master_s *sam_i2c_master_initialize(int bus) { struct sam_i2c_dev_s *priv; uint32_t frequency; irqstate_t flags; int ret = 0; - i2cinfo("Initializing I2C%d\n", bus); - #ifdef SAMDL_HAVE_I2C0 if (bus == 0) { /* Select up I2C0 and setup invariant attributes */ - priv = &g_i2c0; + priv = &g_i2c0; priv->attr = &g_i2c0attr; /* Select the (initial) I2C frequency */ @@ -1701,7 +1254,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C1 and setup invariant attributes */ - priv = &g_i2c1; + priv = &g_i2c1; priv->attr = &g_i2c1attr; /* Select the (initial) I2C frequency */ @@ -1715,7 +1268,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C2 and setup invariant attributes */ - priv = &g_i2c2; + priv = &g_i2c2; priv->attr = &g_i2c2attr; /* Select the (initial) I2C frequency */ @@ -1729,7 +1282,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C3 and setup invariant attributes */ - priv = &g_i2c3; + priv = &g_i2c3; priv->attr = &g_i2c3attr; /* Select the (initial) I2C frequency */ @@ -1743,7 +1296,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C4 and setup invariant attributes */ - priv = &g_i2c4; + priv = &g_i2c4; priv->attr = &g_i2c4attr; /* Select the (initial) I2C frequency */ @@ -1757,7 +1310,7 @@ struct i2c_dev_s *up_i2cinitialize(int bus) { /* Select up I2C5 and setup invariant attributes */ - priv = &g_i2c5; + priv = &g_i2c5; priv->attr = &g_i2c5attr; /* Select the (initial) I2C frequency */ @@ -1775,29 +1328,20 @@ struct i2c_dev_s *up_i2cinitialize(int bus) flags = enter_critical_section(); - /* Allocate a watchdog timer */ - - priv->timeout = wd_create(); - if (priv->timeout == NULL) - { - idbg("ERROR: Failed to allocate a timer\n"); - goto errout_with_irq; - } - /* Attach Interrupt Handler */ ret = irq_attach(priv->attr->irq, i2c_interrupt, priv); if (ret < 0) { - idbg("ERROR: Failed to attach irq %d\n", priv->attr->irq); - goto errout_with_wdog; + i2cerr("ERROR: Failed to attach irq %d\n", priv->attr->irq); + leave_critical_section(flags); + return NULL; } /* Initialize the I2C driver structure */ priv->dev.ops = &g_i2cops; - priv->address = 0; - priv->flags = 0; + priv->flags = 0; (void)sem_init(&priv->exclsem, 0, 1); (void)sem_init(&priv->waitsem, 0, 0); @@ -1807,28 +1351,19 @@ struct i2c_dev_s *up_i2cinitialize(int bus) i2c_hw_initialize(priv, frequency); leave_critical_section(flags); return &priv->dev; - -errout_with_wdog: - wd_delete(priv->timeout); - priv->timeout = NULL; - -errout_with_irq: - - leave_critical_section(flags); - return NULL; } /******************************************************************************* - * Name: up_i2cuninitalize + * Name: sam_i2c_uninitalize * * Description: * Uninitialize an I2C device * *******************************************************************************/ -int up_i2cuninitialize(FAR struct i2c_dev_s *dev) +int sam_i2c_uninitialize(FAR struct i2c_master_s *dev) { - struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *) dev; + struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; i2cinfo("I2C%d Un-initializing\n", priv->attr->i2c); @@ -1841,27 +1376,22 @@ int up_i2cuninitialize(FAR struct i2c_dev_s *dev) sem_destroy(&priv->exclsem); sem_destroy(&priv->waitsem); - /* Free the watchdog timer */ - - wd_delete(priv->timeout); - priv->timeout = NULL; - /* Detach Interrupt Handler */ (void)irq_detach(priv->attr->irq); return OK; } -/************************************************************************************ - * Name: up_i2creset +/******************************************************************************* + * Name: sam_i2c_reset * * Description: * Reset an I2C bus * - ************************************************************************************/ + *******************************************************************************/ #ifdef CONFIG_I2C_RESET -int up_i2creset(FAR struct i2c_dev_s *dev) +int sam_i2c_reset(FAR struct i2c_master_s *dev) { struct sam_i2c_dev_s *priv = (struct sam_i2c_dev_s *)dev; int ret;