Fix the i2c implemtation for lm3s. Add some defines to use also the second i2c device.
[bertos.git] / bertos / cpu / cortex-m3 / drv / i2c_lm3s.c
index 27fed7c69e06985e5a51a29661332baa07b4a186..642b5c0f4206faccb64ff1a522b9ea79c72650f7 100644 (file)
 
 #include <cpu/detect.h>
 #include <cpu/irq.h>
+
+#include <io/cm3_types.h>
+#include <io/lm3s.h>
+
 #include <drv/timer.h>
 #include <drv/i2c.h>
+#include <drv/gpio_lm3s.h>
+#include <drv/clock_lm3s.h>
 
-
-/**
- * Send START condition on the bus.
+/*
  *
- * \return true on success, false otherwise.
  */
-static bool i2c_builtin_start(void)
-{
-
-       return false;
-}
+#if 0
+       /* I2C 0 */
+       #define I2C                        I2C0_MASTER_BASE
+       #define SYSCTL_RCGC1_I2C           SYSCTL_RCGC1_I2C0
+       #define SYSCTL_RCGC2_GPIO          SYSCTL_RCGC2_GPIOB
+       #define GPIO_I2C_SCL_PIN           GPIO_I2C0_SCL_PIN
+       #define GPIO_I2C_SDA_PIN           GPIO_I2C0_SDA_PIN
+       #define GPIO_PORT_BASE             GPIO_PORTB_BASE
+#else
+       /* I2C 1 */
+       #define I2C                        I2C1_MASTER_BASE
+       #define SYSCTL_RCGC1_I2C           SYSCTL_RCGC1_I2C1
+       #define SYSCTL_RCGC2_GPIO          SYSCTL_RCGC2_GPIOA
+       #define GPIO_I2C_SCL_PIN           GPIO_I2C1_SCL_PIN
+       #define GPIO_I2C_SDA_PIN           GPIO_I2C1_SDA_PIN
+       #define GPIO_PORT_BASE             GPIO_PORTA_BASE
+#endif
 
 
 /**
@@ -72,23 +87,8 @@ static bool i2c_builtin_start(void)
  */
 bool i2c_builtin_start_w(uint8_t id)
 {
-       /*
-        * 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 eeprom responds with an ACK.
-        */
-       ticks_t start = timer_clock();
-       while (i2c_builtin_start())
-       {
-               else if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
-               {
-                       LOG_ERR("Timeout on TWI_MT_START\n");
-                       break;
-               }
-       }
-
-       return false;
+       HWREG(I2C + I2C_O_MSA) = id & ~BV(0);
+       return true;
 }
 
 
@@ -101,57 +101,150 @@ bool i2c_builtin_start_w(uint8_t id)
  */
 bool i2c_builtin_start_r(uint8_t id)
 {
-       if (i2c_builtin_start())
-       {
+       HWREG(I2C + I2C_O_MSA) = id | 1;
+       return true;
+}
 
-       }
 
-       return false;
+void i2c_builtin_stop(void)
+{
 }
 
 
-/**
- * Send STOP condition.
- */
-void i2c_builtin_stop(void)
+bool i2c_builtin_put(const uint8_t data)
 {
+       (void)data;
+       return true;
+}
+
 
+int i2c_builtin_get(bool ack)
+{
+       (void)ack;
+       return 0;
 }
 
+INLINE bool check_ack(uint32_t mode_mask)
+{
+       ticks_t start = timer_clock();
+       while ( HWREG(I2C + I2C_O_MCS) &  I2C_MCS_ADRACK )
+       {
+               if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT))
+               {
+                       LOG_ERR("Timeout on I2C_START\n");
+                       return false;
+               }
+
+               HWREG(I2C + I2C_O_MCS) = I2C_MASTER_CMD_BURST_SEND_ERROR_STOP;
 
-/**
- * Put a single byte in master transmitter mode
- * to the selected slave device through the TWI bus.
- *
- * \return true on success, false on error.
+               HWREG(I2C + I2C_O_MCS) = mode_mask;
+               while( HWREG(I2C + I2C_O_MCS) & I2C_MCS_BUSY );
+
+       }
+
+       return true;
+}
+
+/*
+ * With this function is allowed only the atomic write.
  */
-bool i2c_builtin_put(const uint8_t data)
+bool i2c_send(const void *_buf, size_t count)
 {
+       const uint8_t *buf = (const uint8_t *)_buf;
+
+       if (count == 1)
+       {
+               HWREGB(I2C + I2C_O_MDR) = *buf;
+
+               HWREG(I2C + I2C_O_MCS) = I2C_MASTER_CMD_SINGLE_SEND;
+
+               while( HWREG(I2C + I2C_O_MCS) & I2C_MCS_BUSY );
+
+               if ( !check_ack(I2C_MASTER_CMD_SINGLE_SEND) )
+                       return false;
+
+               count = 0;
+               buf++;
+       }
+
+       if (count > 1)
+       {
+               HWREGB(I2C + I2C_O_MDR) = *buf++;
+               count--;
+
+               HWREG(I2C + I2C_O_MCS) = I2C_MASTER_CMD_BURST_SEND_START;
+
+               while( HWREG(I2C + I2C_O_MCS) & I2C_MCS_BUSY );
+
+               if ( !check_ack(I2C_MASTER_CMD_BURST_SEND_START) )
+                       return false;
+
+               while(count - 1)
+               {
+                       HWREGB(I2C + I2C_O_MDR) = *buf++;
+                       count--;
+                       HWREG(I2C + I2C_O_MCS) = I2C_MASTER_CMD_BURST_SEND_CONT;
+                       while( HWREG(I2C + I2C_O_MCS) & I2C_MCS_BUSY );
+
+               }
+
+               HWREGB(I2C + I2C_O_MDR) = *buf++;
+
+               HWREG(I2C + I2C_O_MCS) = I2C_MASTER_CMD_BURST_SEND_FINISH;
+               while( HWREG(I2C + I2C_O_MCS) & I2C_MCS_BUSY );
+       }
+
        return true;
 }
 
 /**
- * Get 1 byte from slave in master transmitter mode
- * to the selected slave device through the I2C bus.
- * If \a ack is true issue a ACK after getting the byte,
- * otherwise a NACK is issued.
- *
- * \return the byte read if ok, EOF on errors.
+ * In order to read bytes from the i2c we should make some tricks.
  */
-int i2c_builtin_get(bool ack)
+bool i2c_recv(void *_buf, size_t count)
 {
+       uint8_t *buf = (uint8_t *)_buf;
 
-       if (ack)
+       if (count == 1)
        {
+               HWREG(I2C + I2C_O_MCS) = I2C_MASTER_CMD_SINGLE_RECEIVE;
+               while( HWREG(I2C + I2C_O_MCS) & I2C_MCS_BUSY );
+
+               if ( !check_ack(I2C_MASTER_CMD_SINGLE_RECEIVE) )
+                       return false;
 
+               *buf++ = HWREG(I2C + I2C_O_MDR);
+               count = 0;
        }
-       else
+
+       if (count > 1)
        {
 
+               HWREG(I2C + I2C_O_MCS) = I2C_MASTER_CMD_BURST_RECEIVE_START;
+               while( HWREG(I2C + I2C_O_MCS) & I2C_MCS_BUSY );
+
+               if ( !check_ack(I2C_MASTER_CMD_BURST_RECEIVE_START) )
+                       return false;
+
+               *buf++ = (uint8_t)HWREG(I2C + I2C_O_MDR);
+               count--;
+
+               while(count - 1)
+               {
+
+                       HWREG(I2C + I2C_O_MCS) = I2C_MASTER_CMD_BURST_RECEIVE_CONT;
+                       while( HWREG(I2C + I2C_O_MCS) & I2C_MCS_BUSY );
+                       *buf++ = (uint8_t)HWREG(I2C + I2C_O_MDR);
+                       count--;
+               }
+
+               HWREG(I2C + I2C_O_MCS) = I2C_MASTER_CMD_BURST_RECEIVE_FINISH;
+               while( HWREG(I2C + I2C_O_MCS) & I2C_MCS_BUSY );
+
+               *buf++ = (uint8_t)HWREG(I2C + I2C_O_MDR);
+               count--;
        }
 
-       /* avoid sign extension */
-       return 0;
+       return true;
 }
 
 MOD_DEFINE(i2c);
@@ -161,5 +254,29 @@ MOD_DEFINE(i2c);
  */
 void i2c_builtin_init(void)
 {
+
+       /* Enable the peripheral clock */
+       SYSCTL_RCGC1_R |= SYSCTL_RCGC1_I2C;
+       SYSCTL_RCGC2_R |= SYSCTL_RCGC2_GPIO;
+
+       /* Configure GPIO pins to work as I2C pins */
+       lm3s_gpioPinConfig(GPIO_PORT_BASE, GPIO_I2C_SCL_PIN,
+               GPIO_DIR_MODE_HW, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_OD_WPU);
+
+       lm3s_gpioPinConfig(GPIO_PORT_BASE, GPIO_I2C_SDA_PIN,
+               GPIO_DIR_MODE_HW, GPIO_STRENGTH_2MA, GPIO_PIN_TYPE_OD_WPU);
+
+    /*
+        * Compute the clock divider that achieves the fastest speed less than or
+     * equal to the desired speed.  The numerator is biased to favor a larger
+     * clock divider so that the resulting clock is always less than or equal
+     * to the desired clock, never greater.
+        */
+    HWREG(I2C + I2C_O_MTPR) = ((CPU_FREQ + (2 * 10 * CONFIG_I2C_FREQ) - 1) / (2 * 10 * CONFIG_I2C_FREQ)) - 1;
+
+
+       //Enable I2C in master mode
+       HWREG(I2C + I2C_O_MCR) |= I2C_MCR_MFE;
+
        MOD_INIT(i2c);
 }