From 84056291e8f121154c2fe5bcccfe47bf8168885b Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Wed, 9 Jul 2014 11:31:21 -0600 Subject: [PATCH] Use sam_pio_forceclk() so that we can read the current state of an open-drain output in the TWI reset logic. --- arch/arm/src/sama5/sam_twi.c | 108 +++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 37 deletions(-) diff --git a/arch/arm/src/sama5/sam_twi.c b/arch/arm/src/sama5/sam_twi.c index 7f07e530ad..5a8c317cd0 100644 --- a/arch/arm/src/sama5/sam_twi.c +++ b/arch/arm/src/sama5/sam_twi.c @@ -69,6 +69,7 @@ #include "sam_periphclks.h" #include "sam_pio.h" #include "sam_twi.h" +#include "sam_pmc.h" // REMOVE ME #if defined(CONFIG_SAMA5_TWI0) || defined(CONFIG_SAMA5_TWI1) || \ defined(CONFIG_SAMA5_TWI2) || defined(CONFIG_SAMA5_TWI3) @@ -1146,11 +1147,38 @@ static int twi_transfer(FAR struct i2c_dev_s *dev, * Initialization *******************************************************************************/ +/**************************************************************************** + * Name: twi_enableclk + * + * Description: + * Enable clocking on the selected TWI + * + ****************************************************************************/ + +static void twi_enableclk(struct twi_dev_s *priv) +{ + int pid; + + /* Get the peripheral ID associated with the TWI device port and enable + * clocking to the TWI block. + */ + + pid = priv->attr->pid; + if (pid < 32) + { + sam_enableperiph0(pid); + } + else + { + sam_enableperiph1(pid); + } +} + /******************************************************************************* * Name: twi_hw_setfrequency * * Description: - * Set the frequence for the next transfer + * Set the frequency for the next transfer * *******************************************************************************/ @@ -1214,6 +1242,10 @@ static void twi_hw_initialize(struct twi_dev_s *priv, uint32_t frequency) i2cvdbg("TWI%d Initializing\n", priv->attr->twi); + /* Enable peripheral clocking */ + + twi_enableclk(priv); + /* SVEN: TWI Slave Mode Enabled */ twi_putrel(priv, SAM_TWI_CR_OFFSET, TWI_CR_SVEN); @@ -1283,7 +1315,9 @@ static void twi_hw_initialize(struct twi_dev_s *priv, uint32_t frequency) * Name: twi_sw_initialize * * Description: - * Initialize/Re-initialize one TWI state structure + * Initialize/Re-initialize one TWI state structure. This logic performs + * only repeatable initialization afte (1) the one-time initialization, and + * (2) after each bus reset. * *******************************************************************************/ @@ -1296,28 +1330,10 @@ static void twi_initialize(struct twi_dev_s *priv, uint32_t frequency) sam_configpio(priv->attr->sclcfg); sam_configpio(priv->attr->sdacfg); - /* Initialize the device structure */ - - priv->dev.ops = &g_twiops; - priv->address = 0; - priv->flags = 0; - - sem_init(&priv->exclsem, 0, 1); - sem_init(&priv->waitsem, 0, 0); - - /* Allocate a watchdog timer */ - - priv->timeout = wd_create(); - DEBUGASSERT(priv->timeout != 0); - /* Configure and enable the TWI hardware */ twi_hw_initialize(priv, frequency); - /* Attach Interrupt Handler */ - - irq_attach(priv->attr->irq, priv->attr->handler); - /* Enable Interrupts */ up_enable_irq(priv->attr->irq); @@ -1351,10 +1367,6 @@ struct i2c_dev_s *up_i2cinitialize(int bus) priv = &g_twi0; priv->attr = &g_twi0attr; - /* Enable peripheral clocking */ - - sam_twi0_enableclk(); - /* Select the (initial) TWI frequency */ frequency = CONFIG_SAMA5_TWI0_FREQUENCY; @@ -1369,10 +1381,6 @@ struct i2c_dev_s *up_i2cinitialize(int bus) priv = &g_twi1; priv->attr = &g_twi1attr; - /* Enable peripheral clocking */ - - sam_twi1_enableclk(); - /* Select the (initial) TWI frequency */ frequency = CONFIG_SAMA5_TWI1_FREQUENCY; @@ -1387,10 +1395,6 @@ struct i2c_dev_s *up_i2cinitialize(int bus) priv = &g_twi2; priv->attr = &g_twi2attr; - /* Enable peripheral clocking */ - - sam_twi2_enableclk(); - /* Select the (initial) TWI frequency */ frequency = CONFIG_SAMA5_TWI2_FREQUENCY; @@ -1405,10 +1409,6 @@ struct i2c_dev_s *up_i2cinitialize(int bus) priv = &g_twi3; priv->attr = &g_twi2attr; - /* Enable peripheral clocking */ - - sam_twi3_enableclk(); - /* Select the (initial) TWI frequency */ frequency = CONFIG_SAMA5_TWI3_FREQUENCY; @@ -1420,9 +1420,30 @@ struct i2c_dev_s *up_i2cinitialize(int bus) return NULL; } - /* Initialize the TWI peripheral */ + /* Perform all one time TWI initialization */ + + irqstate_t flags = irqsave(); + + priv->dev.ops = &g_twiops; + priv->address = 0; + priv->flags = 0; + + sem_init(&priv->exclsem, 0, 1); + sem_init(&priv->waitsem, 0, 0); + + /* Allocate a watchdog timer */ + + priv->timeout = wd_create(); + DEBUGASSERT(priv->timeout != 0); + + /* Attach Interrupt Handler */ + + irq_attach(priv->attr->irq, priv->attr->handler); + + /* Now perform repeatable initialization */ twi_initialize(priv, frequency); + irqrestore(flags); return &priv->dev; } @@ -1501,6 +1522,14 @@ int up_i2creset(FAR struct i2c_dev_s *dev) sam_configpio(sclpin); sam_configpio(sdapin); + /* Peripheral clocking must be enabled in order to read valid data from + * the output pin (clocking is enabled automatically for pins configured + * as inputs). + */ + + sam_pio_forceclk(sclpin, true); + sam_pio_forceclk(sdapin, true); + /* Let SDA go high (i.e., floating since this is an open-drain output). */ sam_piowrite(sdapin, true); @@ -1566,6 +1595,11 @@ int up_i2creset(FAR struct i2c_dev_s *dev) sam_piowrite(sdapin, true); up_udelay(10); + /* Clocking is no longer forced */ + + sam_pio_forceclk(sclpin, false); + sam_pio_forceclk(sdapin, false); + /* Re-initialize the port (using the saved frequency) */ twi_initialize(priv, frequency);