Add I2C and ADC support for ATMega1280 (contributed by Fabio Bizzi)
[bertos.git] / bertos / cpu / avr / drv / i2c_avr.c
index fe3a4eb18aa32358f6096cc4362930e198440915..918d2758cf07a6121f865b498df5b4ffe12f8369 100644 (file)
  *
  * \brief Driver for the AVR ATMega TWI (implementation)
  *
- * \version $Id$
- *
  * \author Stefano Fedrigo <aleph@develer.com>
  * \author Bernie Innocenti <bernie@codewiz.org>
+ * \author Daniele Basile <asterix@develer.com>
  */
 
-#include "hw/hw_cpu.h"  /* CLOCK_FREQ */
 
 #include "cfg/cfg_i2c.h"
+
+#include <hw/hw_cpufreq.h>  /* CPU_FREQ */
+
+#define LOG_LEVEL  I2C_LOG_LEVEL
+#define LOG_FORMAT I2C_LOG_FORMAT
+
+#include <cfg/log.h>
+
 #include <cfg/debug.h>
 #include <cfg/macros.h> // BV()
 #include <cfg/module.h>
 #include <drv/timer.h>
 #include <drv/i2c.h>
 
+#include <cpu/power.h>
+
 #include <compat/twi.h>
 
+#if !CONFIG_I2C_DISABLE_OLD_API
 
 /* Wait for TWINT flag set: bus is ready */
 #define WAIT_TWI_READY  do {} while (!(TWCR & BV(TWINT)))
@@ -61,7 +70,7 @@
  *
  * \return true on success, false otherwise.
  */
-static bool i2c_start(void)
+static bool i2c_builtin_start(void)
 {
        TWCR = BV(TWINT) | BV(TWSTA) | BV(TWEN);
        WAIT_TWI_READY;
@@ -69,7 +78,7 @@ static bool i2c_start(void)
        if (TW_STATUS == TW_START || TW_STATUS == TW_REP_START)
                return true;
 
-       kprintf("!TW_(REP)START: %x\n", TWSR);
+       LOG_ERR("!TW_(REP)START: %x\n", TWSR);
        return false;
 }
 
@@ -81,7 +90,7 @@ static bool i2c_start(void)
  *
  * \return true on success, false otherwise.
  */
-bool i2c_start_w(uint8_t id)
+bool i2c_builtin_start_w(uint8_t id)
 {
        /*
         * Loop on the select write sequence: when the eeprom is busy
@@ -90,9 +99,9 @@ bool i2c_start_w(uint8_t id)
         * keep trying until the eeprom responds with an ACK.
         */
        ticks_t start = timer_clock();
-       while (i2c_start())
+       while (i2c_builtin_start())
        {
-               TWDR = id & ~READ_BIT;
+               TWDR = id & ~I2C_READBIT;
                TWCR = BV(TWINT) | BV(TWEN);
                WAIT_TWI_READY;
 
@@ -100,12 +109,12 @@ bool i2c_start_w(uint8_t id)
                        return true;
                else if (TW_STATUS != TW_MT_SLA_NACK)
                {
-                       kprintf("!TW_MT_SLA_(N)ACK: %x\n", TWSR);
+                       LOG_ERR("!TW_MT_SLA_(N)ACK: %x\n", TWSR);
                        break;
                }
                else if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
                {
-                       kprintf("Timeout on TWI_MT_START\n");
+                       LOG_ERR("Timeout on TWI_MT_START\n");
                        break;
                }
        }
@@ -121,18 +130,18 @@ bool i2c_start_w(uint8_t id)
  *
  * \return true on success, false otherwise.
  */
-bool i2c_start_r(uint8_t id)
+bool i2c_builtin_start_r(uint8_t id)
 {
-       if (i2c_start())
+       if (i2c_builtin_start())
        {
-               TWDR = id | READ_BIT;
+               TWDR = id | I2C_READBIT;
                TWCR = BV(TWINT) | BV(TWEN);
                WAIT_TWI_READY;
 
                if (TW_STATUS == TW_MR_SLA_ACK)
                        return true;
 
-               kprintf("!TW_MR_SLA_ACK: %x\n", TWSR);
+               LOG_ERR("!TW_MR_SLA_ACK: %x\n", TWSR);
        }
 
        return false;
@@ -142,9 +151,9 @@ bool i2c_start_r(uint8_t id)
 /**
  * Send STOP condition.
  */
-void i2c_stop(void)
+void i2c_builtin_stop(void)
 {
-        TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO);
+       TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO);
 }
 
 
@@ -154,14 +163,14 @@ void i2c_stop(void)
  *
  * \return true on success, false on error.
  */
-bool i2c_put(const uint8_t data)
+bool i2c_builtin_put(const uint8_t data)
 {
        TWDR = data;
        TWCR = BV(TWINT) | BV(TWEN);
        WAIT_TWI_READY;
        if (TW_STATUS != TW_MT_DATA_ACK)
        {
-               kprintf("!TW_MT_DATA_ACK: %x\n", TWSR);
+               LOG_ERR("!TW_MT_DATA_ACK: %x\n", TWSR);
                return false;
        }
        return true;
@@ -175,7 +184,7 @@ bool i2c_put(const uint8_t data)
  *
  * \return the byte read if ok, EOF on errors.
  */
-int i2c_get(bool ack)
+int i2c_builtin_get(bool ack)
 {
        TWCR = BV(TWINT) | BV(TWEN) | (ack ? BV(TWEA) : 0);
        WAIT_TWI_READY;
@@ -184,7 +193,7 @@ int i2c_get(bool ack)
        {
                if (TW_STATUS != TW_MR_DATA_ACK)
                {
-                       kprintf("!TW_MR_DATA_ACK: %x\n", TWSR);
+                       LOG_ERR("!TW_MR_DATA_ACK: %x\n", TWSR);
                        return EOF;
                }
        }
@@ -192,7 +201,7 @@ int i2c_get(bool ack)
        {
                if (TW_STATUS != TW_MR_DATA_NACK)
                {
-                       kprintf("!TW_MR_DATA_NACK: %x\n", TWSR);
+                       LOG_ERR("!TW_MR_DATA_NACK: %x\n", TWSR);
                        return EOF;
                }
        }
@@ -201,13 +210,172 @@ int i2c_get(bool ack)
        return (int)(uint8_t)TWDR;
 }
 
-MOD_DEFINE(i2c);
+#endif /* !CONFIG_I2C_DISABLE_OLD_API */
+
+/*
+ * New Api
+ */
+struct I2cHardware
+{
+};
+
+
+/* Wait for TWINT flag set: bus is ready */
+#define WAIT_READY() \
+       do { \
+               while (!(TWCR & BV(TWINT))) \
+                       cpu_relax(); \
+       } while (0)
+
+/**
+ * Send START condition on the bus.
+ */
+INLINE bool i2c_hw_start(void)
+{
+       TWCR = BV(TWINT) | BV(TWSTA) | BV(TWEN);
+       WAIT_READY();
+
+       if (TW_STATUS == TW_START || TW_STATUS == TW_REP_START)
+               return true;
+
+       return false;
+}
+
+/**
+ * Send STOP condition.
+ */
+INLINE void i2c_hw_stop(void)
+{
+       TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO);
+}
+
+static void i2c_avr_start(I2c *i2c, uint16_t slave_addr)
+{
+       /*
+        * Loop on the select write sequence: when the eeprom is busy
+        * writing previously sent data it will reply to the SLA_W
+        * control byte with a NACK.  In this case, we must
+        * keep trying until the slave responds with an ACK.
+        */
+       ticks_t start = timer_clock();
+       while (i2c_hw_start())
+       {
+               uint8_t sla_ack = 0;
+               uint8_t sla_nack = 0;
+               if (I2C_TEST_START(i2c->flags) == I2C_START_W)
+               {
+                       TWDR = slave_addr & ~I2C_READBIT;
+                       sla_ack = TW_MT_SLA_ACK;
+                       sla_nack = TW_MT_SLA_NACK;
+               }
+               else
+               {
+                       TWDR = slave_addr | I2C_READBIT;
+                       sla_ack = TW_MR_SLA_ACK;
+                       sla_nack = TW_MR_SLA_NACK;
+               }
+
+               TWCR = BV(TWINT) | BV(TWEN);
+               WAIT_READY();
+
+               if (TW_STATUS == sla_ack)
+                       return;
+               else if (TW_STATUS != sla_nack)
+               {
+                       LOG_ERR("Start addr NACK[%x]\n", TWSR);
+                       i2c->errors |= I2C_NO_ACK;
+                       i2c_hw_stop();
+                       break;
+               }
+               else if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
+               {
+                       LOG_ERR("Start timeout\n");
+                       i2c->errors |= I2C_START_TIMEOUT;
+                       i2c_hw_stop();
+                       break;
+               }
+       }
+
+       LOG_ERR("I2c error\n");
+       i2c->errors |= I2C_ERR;
+       i2c_hw_stop();
+}
+
+static void i2c_avr_putc(I2c *i2c, const uint8_t data)
+{
+
+       TWDR = data;
+       TWCR = BV(TWINT) | BV(TWEN);
+       WAIT_READY();
+
+       if (TW_STATUS != TW_MT_DATA_ACK)
+       {
+               LOG_ERR("Data nack[%x]\n", TWSR);
+               i2c->errors |= I2C_DATA_NACK;
+               i2c_hw_stop();
+       }
+
+       if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
+               i2c_hw_stop();
+}
+
+static uint8_t i2c_avr_getc(I2c *i2c)
+{
+       uint8_t data_flag = 0;
+       if (i2c->xfer_size == 1)
+       {
+               TWCR = BV(TWINT) | BV(TWEN);
+               data_flag = TW_MR_DATA_NACK;
+       }
+       else
+       {
+               TWCR = BV(TWINT) | BV(TWEN) | BV(TWEA);
+               data_flag = TW_MR_DATA_ACK;
+       }
+
+       WAIT_READY();
+
+       if (TW_STATUS != data_flag)
+       {
+               LOG_ERR("Data nack[%x]\n", TWSR);
+               i2c->errors |= I2C_DATA_NACK;
+               i2c_hw_stop();
+
+               return 0xFF;
+       }
+
+       uint8_t data = TWDR;
+
+       if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP))
+               i2c_hw_stop();
+
+       return data;
+}
+
+
+static const I2cVT i2c_avr_vt =
+{
+       .start = i2c_avr_start,
+       .getc = i2c_avr_getc,
+       .putc = i2c_avr_putc,
+       .write = i2c_genericWrite,
+       .read = i2c_genericRead,
+};
+
+struct I2cHardware i2c_avr_hw[] =
+{
+       { /* I2C0 */
+       },
+};
 
 /**
- * Initialize TWI module.
+ * Initialize I2C module.
  */
-void i2c_init(void)
+void i2c_hw_init(I2c *i2c, int dev, uint32_t clock)
 {
+       i2c->hw = &i2c_avr_hw[dev];
+       i2c->vt = &i2c_avr_vt;
+
        ATOMIC(
                /*
                 * This is pretty useless according to AVR's datasheet,
@@ -216,32 +384,28 @@ void i2c_init(void)
                 * probably due to some unwanted interaction between the
                 * port pin and the TWI lines.
                 */
-#if CPU_AVR_ATMEGA64 || CPU_AVR_ATMEGA128 || CPU_AVR_ATMEGA1281
+       #if CPU_AVR_ATMEGA64 || CPU_AVR_ATMEGA128 || CPU_AVR_ATMEGA1281 || CPU_AVR_ATMEGA1280
                PORTD |= BV(PD0) | BV(PD1);
                DDRD  |= BV(PD0) | BV(PD1);
-#elif CPU_AVR_ATMEGA8
+       #elif CPU_AVR_ATMEGA8
                PORTC |= BV(PC4) | BV(PC5);
                DDRC  |= BV(PC4) | BV(PC5);
-#elif CPU_AVR_ATMEGA32
+       #elif CPU_AVR_ATMEGA32
                PORTC |= BV(PC1) | BV(PC0);
                DDRC  |= BV(PC1) | BV(PC0);
-#else
+       #else
                #error Unsupported architecture
-#endif
+       #endif
 
                /*
                 * Set speed:
-                * F = CLOCK_FREQ / (16 + 2*TWBR * 4^TWPS)
+                * F = CPU_FREQ / (16 + 2*TWBR * 4^TWPS)
                 */
-               #ifndef CONFIG_I2C_FREQ
-                       #warning Using default value of 300000L for CONFIG_I2C_FREQ
-                       #define CONFIG_I2C_FREQ  300000L /* ~300 kHz */
-               #endif
-               #define TWI_PRESC 1       /* 4 ^ TWPS */
+               ASSERT(clock);
+               #define TWI_PRESC    1       /* 4 ^ TWPS */
 
-               TWBR = (CLOCK_FREQ / (2 * CONFIG_I2C_FREQ * TWI_PRESC)) - (8 / TWI_PRESC);
+               TWBR = (CPU_FREQ / (2 * clock * TWI_PRESC)) - (8 / TWI_PRESC);
                TWSR = 0;
                TWCR = BV(TWEN);
        );
-       MOD_INIT(i2c);
 }