diff --git a/arch/arm/src/samdl/sam_i2c_master.c b/arch/arm/src/samdl/sam_i2c_master.c index 3e5a232f27..38fc1a26b4 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;