samv7:twihs driver add reference counting

This commit is contained in:
David Sidrane 2017-06-15 11:16:28 -10:00
parent d8da813562
commit d9d32ac808

View File

@ -151,7 +151,7 @@ struct twi_dev_s
struct i2c_msg_s *msg; /* Message list */ struct i2c_msg_s *msg; /* Message list */
uint32_t twiclk; /* TWIHS input clock frequency */ uint32_t twiclk; /* TWIHS input clock frequency */
uint32_t frequency; /* TWIHS transfer clock frequency */ uint32_t frequency; /* TWIHS transfer clock frequency */
bool initd; /* True :device has been initialized */ int refs; /* Reference count */
uint8_t msgc; /* Number of message in the message list */ uint8_t msgc; /* Number of message in the message list */
sem_t exclsem; /* Only one thread can access at a time */ sem_t exclsem; /* Only one thread can access at a time */
@ -1118,6 +1118,10 @@ static int twi_reset(FAR struct i2c_master_s *dev)
DEBUGASSERT(priv != NULL); DEBUGASSERT(priv != NULL);
/* Our caller must own a ref */
DEBUGASSERT(priv->refs > 0);
/* Get exclusive access to the TWIHS device */ /* Get exclusive access to the TWIHS device */
twi_takesem(&priv->exclsem); twi_takesem(&priv->exclsem);
@ -1342,6 +1346,7 @@ struct i2c_master_s *sam_i2cbus_initialize(int bus)
{ {
struct twi_dev_s *priv; struct twi_dev_s *priv;
uint32_t frequency; uint32_t frequency;
const struct twi_attr_s *attr = 0;
irqstate_t flags; irqstate_t flags;
int ret; int ret;
@ -1352,8 +1357,8 @@ struct i2c_master_s *sam_i2cbus_initialize(int bus)
{ {
/* Select up TWIHS0 and setup invariant attributes */ /* Select up TWIHS0 and setup invariant attributes */
priv = &g_twi0; priv = &g_twi0;
priv->attr = &g_twi0attr; attr = &g_twi0attr;
/* Select the (initial) TWIHS frequency */ /* Select the (initial) TWIHS frequency */
@ -1366,8 +1371,8 @@ struct i2c_master_s *sam_i2cbus_initialize(int bus)
{ {
/* Select up TWIHS1 and setup invariant attributes */ /* Select up TWIHS1 and setup invariant attributes */
priv = &g_twi1; priv = &g_twi1;
priv->attr = &g_twi1attr; attr = &g_twi1attr;
/* Select the (initial) TWIHS frequency */ /* Select the (initial) TWIHS frequency */
@ -1380,8 +1385,8 @@ struct i2c_master_s *sam_i2cbus_initialize(int bus)
{ {
/* Select up TWIHS2 and setup invariant attributes */ /* Select up TWIHS2 and setup invariant attributes */
priv = &g_twi2; priv = &g_twi2;
priv->attr = &g_twi2attr; attr = &g_twi2attr;
/* Select the (initial) TWIHS frequency */ /* Select the (initial) TWIHS frequency */
@ -1394,14 +1399,16 @@ struct i2c_master_s *sam_i2cbus_initialize(int bus)
return NULL; return NULL;
} }
/* Perform one-time TWIHS initialization */
flags = enter_critical_section(); flags = enter_critical_section();
/* Has the device already been initialized? */ /* Has the device already been initialized? */
if (!priv->initd) if ((volatile int)priv->refs++ == 0)
{ {
/* Perform one-time TWIHS initialization */
priv->attr = attr;
/* Allocate a watchdog timer */ /* Allocate a watchdog timer */
priv->timeout = wd_create(); priv->timeout = wd_create();
@ -1438,10 +1445,6 @@ struct i2c_master_s *sam_i2cbus_initialize(int bus)
/* Perform repeatable TWIHS hardware initialization */ /* Perform repeatable TWIHS hardware initialization */
twi_hw_initialize(priv, frequency); twi_hw_initialize(priv, frequency);
/* Now it has been initialized */
priv->initd = true;
} }
leave_critical_section(flags); leave_critical_section(flags);
@ -1452,6 +1455,7 @@ errout_with_wdog:
priv->timeout = NULL; priv->timeout = NULL;
errout_with_irq: errout_with_irq:
priv->refs--;
leave_critical_section(flags); leave_critical_section(flags);
return NULL; return NULL;
} }
@ -1469,28 +1473,40 @@ int sam_i2cbus_uninitialize(FAR struct i2c_master_s *dev)
struct twi_dev_s *priv = (struct twi_dev_s *) dev; struct twi_dev_s *priv = (struct twi_dev_s *) dev;
irqstate_t flags; irqstate_t flags;
i2cinfo("TWIHS%d Un-initializing\n", priv->attr->twi); DEBUGASSERT(priv);
/* Decrement reference count and check for underflow */
if (priv->refs == 0)
{
return ERROR;
}
i2cinfo("TWIHS%d Un-initializing refs:%d\n", priv->attr->twi, priv->refs);
/* Disable TWIHS interrupts */ /* Disable TWIHS interrupts */
flags = enter_critical_section(); flags = enter_critical_section();
up_disable_irq(priv->attr->irq);
/* Reset data structures */ if (--priv->refs == 0)
{
up_disable_irq(priv->attr->irq);
sem_destroy(&priv->exclsem); /* Reset data structures */
sem_destroy(&priv->waitsem);
/* Free the watchdog timer */ sem_destroy(&priv->exclsem);
sem_destroy(&priv->waitsem);
wd_delete(priv->timeout); /* Free the watchdog timer */
priv->timeout = NULL;
/* Detach Interrupt Handler */ wd_delete(priv->timeout);
priv->timeout = NULL;
(void)irq_detach(priv->attr->irq); /* Detach Interrupt Handler */
(void)irq_detach(priv->attr->irq);
}
priv->initd = false;
leave_critical_section(flags); leave_critical_section(flags);
return OK; return OK;
} }