Updated I2C driver to follow API documented here : http://wiki.techno-innov.fr/index...
authorNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 27 Sep 2016 21:02:37 +0000 (23:02 +0200)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:05 +0000 (17:03 +0100)
drivers/i2c.c
extdrv/eeprom.c
extdrv/tmp101_temp_sensor.c
include/drivers/i2c.h
include/extdrv/tmp101_temp_sensor.h

index f26437c..0d96e21 100644 (file)
@@ -38,6 +38,7 @@
 /*
  * I2C Bus structure
  *
+ * mode : current configuration mode : I2C_MASTER, I2C_SLAVE or I2C_MONITOR.
  * clock : current i2c clock.
  * state : global state of the i2c engine.
  * master_status : status returned by i2c block as found in "status" register.
@@ -52,6 +53,7 @@
  */
 struct i2c_bus {
        volatile struct lpc_i2c* regs;
+       uint8_t mode;
        volatile uint32_t clock;
        volatile uint32_t state;
        volatile uint32_t master_status;
@@ -70,7 +72,12 @@ struct i2c_bus {
        volatile uint32_t read_index;
 };
 
-static struct i2c_bus mod_i2c;
+static struct i2c_bus i2c_buses[NB_I2C_BUSSES] = {
+       {
+               .regs = (struct lpc_i2c*)LPC_I2C0,
+               .state = I2C_OK,
+       },
+};
 
 
 /* FIXME : Should add a field "what to do on addr NACK" to i2c_bus structure, and use
@@ -86,7 +93,7 @@ static struct i2c_bus mod_i2c;
 void I2C_0_Handler(void)
 {
        uint8_t status;
-       struct i2c_bus* i2c = &mod_i2c;
+       struct i2c_bus* i2c = &(i2c_buses[0]);
 
        i2c->timeout = 0;
 
@@ -271,6 +278,55 @@ void I2C_0_Handler(void)
 /***************************************************************************** */
 /*                        I2C access                                           */
 /***************************************************************************** */
+static int i2c_perform_data_transfer(struct i2c_bus* i2c)
+{
+       int ret = 0;
+
+       /* Start the process */
+       i2c->state = I2C_BUSY;
+       i2c->regs->ctrl_set = I2C_START_FLAG;
+       /* Wait for process completion */
+       do {} while (i2c->state == I2C_BUSY);
+
+       /* Handle returned state (errors or OK) */
+       switch (i2c->state) {
+               case I2C_OK:
+               case I2C_NO_DATA:
+                       /* Return 0 : success */
+                       break;
+               case I2C_NACK:
+                       ret = -EREMOTEIO;
+                       break;
+               case I2C_ARBITRATION_LOST:
+                       ret = -EBUSY;
+                       break;
+               case I2C_BUS_ERROR: /* This one is bad ... */
+               case I2C_ERROR_UNKNOWN:
+               default:
+                       ret = -EIO;
+                       break;
+       }
+
+       return ret;
+}
+
+/* Release Bus
+ * Some devices do not release the Bus at the end of a transaction if they don't receive
+ *   a start condition immediately followed by a stop condition.
+ */
+void i2c_release_bus(uint8_t bus_num)
+{
+       struct i2c_bus* i2c = &(i2c_buses[0]);
+       /* Force device to release the bus :
+        *    send a START followed by a STOP (initiate transmission with nul write_length) */
+       i2c->state = I2C_BUSY;
+       i2c->write_length = 0;
+       i2c->regs->ctrl_set = I2C_START_FLAG;
+       do {} while (i2c->state == I2C_BUSY);
+       i2c->state = I2C_OK;
+}
+
+
 /* Read
  * Performs a non-blocking read on the module's i2c bus.
  *   cmd_buf : buffer containing all control byte to be sent on the i2c bus
@@ -283,72 +339,45 @@ void I2C_0_Handler(void)
  *   Upon successfull completion, returns the number of bytes read. On error, returns a negative
  *   integer equivalent to errors from glibc.
  */
-int i2c_read(const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count)
+int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count)
 {
+       struct i2c_bus* i2c = &(i2c_buses[0]);
        int ret = 0;
 
        /* Checks */
-       if (mod_i2c.regs != LPC_I2C0)
+       if (i2c->regs != LPC_I2C0)
                return -EBADFD;
        if (cmd_buf == NULL)
                return -EINVAL;
        if ((inbuff == NULL) && (count > 0))
                return -EINVAL;
 
-       if (mod_i2c.state == I2C_BUSY) {
+       if (i2c->state == I2C_BUSY) {
                return -EBUSY;
        }
-       if (mod_i2c.state != I2C_OK) {
+       if (i2c->state != I2C_OK) {
                /* What should we do ??? someone failed to reset status ? */
        }
 
-       /* Set up mod_i2c for read operation */
+       /* Set up i2c structure for read operation */
        /* command (write) buffer */
-       mod_i2c.out_buff = cmd_buf;
-       mod_i2c.write_length = cmd_size;
+       i2c->out_buff = cmd_buf;
+       i2c->write_length = cmd_size;
        /* control buffer, if any. Note that it's the only way to control
         *   operations on modules i2C bus to simplify the interface */
-       mod_i2c.repeated_start_restart = ctrl_buf;
-       mod_i2c.restart_after_addr = I2C_CONT;
-       mod_i2c.restart_after_data = 0;
+       i2c->repeated_start_restart = ctrl_buf;
+       i2c->restart_after_addr = I2C_CONT;
+       i2c->restart_after_data = 0;
        /* read buffer */
-       mod_i2c.in_buff = inbuff;
-       mod_i2c.read_length = count;
-       mod_i2c.read_index = 0;
+       i2c->in_buff = inbuff;
+       i2c->read_length = count;
+       i2c->read_index = 0;
 
-       /* Start the process */
-       mod_i2c.state = I2C_BUSY;
-       mod_i2c.regs->ctrl_set = I2C_START_FLAG;
-       /* And wait for process completion */
-       do {} while (mod_i2c.state == I2C_BUSY);
-
-       /* Handle returned state (errors or OK) */
-       switch (mod_i2c.state) {
-               case I2C_OK:
-               case I2C_NO_DATA:
-                       ret = mod_i2c.read_index;
-                       break;
-               case I2C_NACK:
-                       ret = -EREMOTEIO;
-                       break;
-               case I2C_ARBITRATION_LOST:
-                       ret = -EBUSY;
-                       break;
-               case I2C_BUS_ERROR: /* This one is bad ... */
-               case I2C_ERROR_UNKNOWN:
-               default:
-                       ret = -EIO;
-                       break;
+       ret = i2c_perform_data_transfer(i2c);
+       if (ret == 0) {
+               return i2c->read_index;
        }
 
-       /* Force device to release the bus :
-        *    send a START followed by a STOP (initiate transmission with nul write_length) */
-       mod_i2c.state = I2C_BUSY;
-       mod_i2c.write_length = 0;
-       mod_i2c.regs->ctrl_set = I2C_START_FLAG;
-       do {} while (mod_i2c.state == I2C_BUSY);
-       mod_i2c.state = I2C_OK;
-
        return ret;
 }
 
@@ -364,69 +393,42 @@ int i2c_read(const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* i
  *   Upon successfull completion, returns the number of bytes written. On error, returns a negative
  *   integer equivalent to errors from glibc.
  */
-int i2c_write(const void *buf, size_t count, const void* ctrl_buf)
+int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf)
 {
+       struct i2c_bus* i2c = &(i2c_buses[0]);
        int ret = 0;
 
        /* Checks */
-       if (mod_i2c.regs != LPC_I2C0)
+       if (i2c->regs != LPC_I2C0)
                return -EBADFD;
        if (buf == NULL)
                return -EINVAL;
 
-       if (mod_i2c.state == I2C_BUSY) {
+       if (i2c->state == I2C_BUSY) {
                return -EBUSY;
        }
-       if (mod_i2c.state != I2C_OK) {
+       if (i2c->state != I2C_OK) {
                /* What should we do ??? someone failed to reset status ? */
        }
 
        /* Clear read information to prevent entering master receiver states */
-       mod_i2c.read_length = 0;
-       mod_i2c.in_buff = NULL;
-       mod_i2c.state = I2C_BUSY;
-       /* Set up mod_i2c for write operation */
-       mod_i2c.out_buff = buf;
-       mod_i2c.write_length = count;
+       i2c->read_length = 0;
+       i2c->in_buff = NULL;
+       i2c->state = I2C_BUSY;
+       /* Set up i2c_bus structure for write operation */
+       i2c->out_buff = buf;
+       i2c->write_length = count;
        /* control buffer, if any. Note that it's the only way to control
         *   operations on modules i2C bus to simplify the interface */
-       mod_i2c.restart_after_addr = I2C_CONT;
-       mod_i2c.repeated_start_restart = ctrl_buf;
-       mod_i2c.restart_after_data = 0;
+       i2c->restart_after_addr = I2C_CONT;
+       i2c->repeated_start_restart = ctrl_buf;
+       i2c->restart_after_data = 0;
 
-       /* Start the process */
-       mod_i2c.state = I2C_BUSY;
-       mod_i2c.regs->ctrl_set = I2C_START_FLAG;
-       /* Wait for process completion */
-       do {} while (mod_i2c.state == I2C_BUSY);
-
-       /* Handle returned state (errors or OK) */
-       switch (mod_i2c.state) {
-               case I2C_OK:
-               case I2C_NO_DATA:
-                       ret = mod_i2c.write_length;
-                       break;
-               case I2C_NACK:
-                       ret = -EREMOTEIO;
-                       break;
-               case I2C_ARBITRATION_LOST:
-                       ret = -EBUSY;
-                       break;
-               case I2C_BUS_ERROR: /* This one is bad ... */
-               case I2C_ERROR_UNKNOWN:
-               default:
-                       ret = -EIO;
-                       break;
+       ret = i2c_perform_data_transfer(i2c);
+       if (ret == 0) {
+               return i2c->write_length;
        }
 
-       /* Force device to release the bus :
-        *    send a START followed by a STOP (initiate transmission with nul write_length) */
-       mod_i2c.state = I2C_BUSY;
-       mod_i2c.write_length = 0;
-       mod_i2c.regs->ctrl_set = I2C_START_FLAG;
-       do {} while (mod_i2c.state == I2C_BUSY);
-       mod_i2c.state = I2C_OK;
-
        return ret;
 }
 
@@ -447,40 +449,47 @@ static void i2c_clock_on(uint32_t i2c_clk_freq)
        i2c->clk_duty_low = (scl_clk - i2c->clk_duty_high);
 }
 
-void i2c_on(uint32_t i2c_clk_freq)
+/* I2C on / off
+ *   bus_num : I2C bus number to use. Ignored on this micro-controller which has only one I2C bus.
+ *   i2c_clk_freq : I2C clock freqeuncy in Hz
+ *   mode is one of I2C_MASTER, I2C_SLAVE or I2C_MONITOR.
+ *   Note that only I2C_MASTER is currently supported.
+ */
+int i2c_on(uint8_t bus_num, uint32_t i2c_clk_freq, uint8_t mode)
 {
-       struct lpc_i2c* i2c = LPC_I2C0;
+       struct i2c_bus* i2c = &(i2c_buses[0]);
 
        NVIC_DisableIRQ(I2C0_IRQ);
        /* Power on I2C 0 block */
        subsystem_power(LPC_SYS_ABH_CLK_CTRL_I2C, 1);
        /* Set clock */
        i2c_clock_on(i2c_clk_freq);
-       mod_i2c.clock = i2c_clk_freq;
-       /* Initialize i2c_bus struct */
-       memset(&mod_i2c, 0, sizeof(struct i2c_bus));
-       mod_i2c.regs = (struct lpc_i2c*)LPC_I2C0;
-       mod_i2c.state = I2C_OK;
+       i2c->clock = i2c_clk_freq;
        /* Enable I2C */
        /* FIXME: if enabling slave functions, add I2C_ASSERT_ACK flag */
-       i2c->ctrl_set = (I2C_ENABLE_FLAG);
+       i2c->regs->ctrl_set = (I2C_ENABLE_FLAG);
        /* And enable interrupts */
        NVIC_EnableIRQ(I2C0_IRQ);
+
+       return 0;
 }
 
-void i2c_off(void)
+int i2c_off(uint8_t bus_num)
 {
+       struct i2c_bus* i2c = &(i2c_buses[0]);
        NVIC_DisableIRQ(I2C0_IRQ);
        subsystem_power(LPC_SYS_ABH_CLK_CTRL_I2C, 0);
-       mod_i2c.clock = 0;
+       i2c->clock = 0;
+       return 0;
 }
 
 /* Allow system to propagate main clock */
 void i2c_clock_update(void)
 {
-       if (mod_i2c.clock) {
+       struct i2c_bus* i2c = &(i2c_buses[0]);
+       if (i2c->clock) {
                /* FIXME : we should stop I2C transfers, disable I2C interrupts and stop I1C clock. */
-               i2c_clock_on(mod_i2c.clock); /* 6 is not a module num, nor system i2c (5) */
+               i2c_clock_on(i2c->clock); /* 6 is not a module num, nor system i2c (5) */
        }
 }
 
index 9306f0b..91e2c7d 100644 (file)
@@ -70,14 +70,14 @@ int eeprom_detect(uint8_t eeprom_addr)
        if (eeprom_addr == EEPROM_ID_SMALL_ADDR_1) {
                cmd_buf[0] = EEPROM_ID_SMALL_ADDR_2;
        }
-       ret = i2c_read(cmd_buf, 1, NULL, NULL, 0);
+       ret = i2c_read(0, cmd_buf, 1, NULL, NULL, 0);
        if (ret == 0) {
                return EEPROM_TYPE_SMALL;
        }
 
        /* No small eeprom ... look for big ones */
        cmd_buf[0] = eeprom_addr;
-       ret = i2c_read(cmd_buf, 1, NULL, NULL, 0);
+       ret = i2c_read(0, cmd_buf, 1, NULL, NULL, 0);
        if (ret == 0) {
                return EEPROM_TYPE_BIG;
        }
@@ -129,14 +129,14 @@ int eeprom_read(uint8_t eeprom_addr, uint32_t offset, void *buf, size_t count)
                        cmd_buf[0] = EEPROM_ID_SMALL_ADDR_1 | ((offset & 0x700) >> 7);
                        cmd_buf[1] = offset & 0xFF;
                        cmd_buf[2] = EEPROM_ID_SMALL_ADDR_1 | 0x01;
-                       ret = i2c_read(cmd_buf, CMD_BUF_SIZE - 1, ctrl_buf + 1, buf, count);
+                       ret = i2c_read(0, cmd_buf, CMD_BUF_SIZE - 1, ctrl_buf + 1, buf, count);
                        break;
                case EEPROM_TYPE_BIG:
                        cmd_buf[0] = eeprom_addr;
                        cmd_buf[1] = ((offset & 0xFF00) >> 8);
                        cmd_buf[2] = offset & 0xFF;
                        cmd_buf[3] = (eeprom_addr | 0x01);
-                       ret = i2c_read(cmd_buf, CMD_BUF_SIZE, ctrl_buf, buf, count);
+                       ret = i2c_read(0, cmd_buf, CMD_BUF_SIZE, ctrl_buf, buf, count);
                        break;
                default:
                        ret = -1;
@@ -207,7 +207,7 @@ int eeprom_write(uint8_t eeprom_addr, uint32_t offset, const void *buf, size_t c
                offset += size;
                memcpy(full_buff, cmd, cmd_size);
                memcpy(full_buff + cmd_size, buf + write_count, size);
-               ret = i2c_write(full_buff, (cmd_size + size), NULL);
+               ret = i2c_write(0, full_buff, (cmd_size + size), NULL);
 
                if (ret != (cmd_size + size)) {
                        break;
@@ -215,7 +215,7 @@ int eeprom_write(uint8_t eeprom_addr, uint32_t offset, const void *buf, size_t c
                /* Wait for page write completion : The device does not acknoledge anything during
                 * page write, perform page writes with no data, until it returns 1 */
                do {
-                       ret = i2c_write(full_buff, 1, NULL);
+                       ret = i2c_write(0, full_buff, 1, NULL);
                } while (ret != 1);
 
                write_count += size;
index e0f9d90..8677947 100644 (file)
@@ -67,7 +67,7 @@ int tmp101_probe_sensor(struct tmp101_sensor_config* conf)
 
        /* Did we already probe the sensor ? */
        if (conf->probe_ok != 1) {
-               conf->probe_ok = i2c_read(&cmd_buf, 1, NULL, NULL, 0);
+               conf->probe_ok = i2c_read(conf->bus_num, &cmd_buf, 1, NULL, NULL, 0);
        }
        return conf->probe_ok;
 }
@@ -104,23 +104,25 @@ int tmp101_sensor_read(struct tmp101_sensor_config* conf, uint16_t* raw, int* de
        /* Read the requested data */
        if (conf->last_accessed_register == TMP_REG_TEMPERATURE) {
                /* No need to switch back to temperature register */
-               ret = i2c_read((cmd_buf + 2), 1, (ctrl_buf + 2), (char*)&temp, 2);
+               ret = i2c_read(conf->bus_num, (cmd_buf + 2), 1, (ctrl_buf + 2), (char*)&temp, 2);
        } else {
                /* Send (write) temperature register address to TMP101 internal pointer */
-               ret = i2c_read(cmd_buf, CMD_BUF_SIZE, ctrl_buf, (char*)&temp, 2);
+               ret = i2c_read(conf->bus_num, cmd_buf, CMD_BUF_SIZE, ctrl_buf, (char*)&temp, 2);
                conf->last_accessed_register = TMP_REG_TEMPERATURE;
        }
 
-       if (ret == 2) {
-               if (raw != NULL) {
-                       *raw = byte_swap_16(temp);
-               }
-               if (deci_degrees != NULL) {
-                       *deci_degrees = tmp101_convert_to_deci_degrees(byte_swap_16(temp));
-               }
-               return 0;
+       if (ret != 2) {
+               conf->probe_ok = 0;
+               return ret;
        }
-       return ret;
+
+       if (raw != NULL) {
+               *raw = byte_swap_16(temp);
+       }
+       if (deci_degrees != NULL) {
+               *deci_degrees = tmp101_convert_to_deci_degrees(byte_swap_16(temp));
+       }
+       return 0;
 }
 
 
@@ -146,12 +148,13 @@ int tmp101_sensor_config(struct tmp101_sensor_config* conf)
        conf->actual_config = (TMP_SHUTDOWN_MODE_ON | TMP_THERMOSTAT_INTERRUPT_MODE | TMP_ALERT_POLARITY_HIGH);
        conf->actual_config |= (conf->resolution & (0x03 << 5));
        cmd[2] = conf->actual_config;
-       ret = i2c_write(cmd, 3, NULL);
+       ret = i2c_write(conf->bus_num, cmd, 3, NULL);
        conf->last_accessed_register = TMP_REG_CONFIG;
-       if (ret == 3) {
-               return 0; /* Config success */
+       if (ret != 3) {
+               conf->probe_ok = 0;
+               return ret;
        }
-       return ret;
+       return 0; /* Config success */
 }
 
 /* Start a conversion when the sensor is in shutdown mode. */
@@ -166,12 +169,13 @@ int tmp101_sensor_start_conversion(struct tmp101_sensor_config* conf)
 
        cmd[2] = conf->actual_config;
        cmd[2] |= TMP_ONE_SHOT_TRIGGER;
-       ret = i2c_write(cmd, 3, NULL);
+       ret = i2c_write(conf->bus_num, cmd, 3, NULL);
        conf->last_accessed_register = TMP_REG_CONFIG;
-       if (ret == 3) {
-               return 0; /* Conversion start success */
+       if (ret != 3) {
+               conf->probe_ok = 0;
+               return ret;
        }
-       return ret;
+       return 0; /* Conversion start success */
 }
 
 
index 520e5a6..ecce01c 100644 (file)
 #include "core/lpc_regs.h"
 
 
+#define NB_I2C_BUSSES   1
+
 #define I2C_CLK_100KHz  (100*1000)
 #define I2C_CLK_400KHz  (400*1000)
 
 #define I2C_READ_BIT 0x01
 #define I2C_WRITE_BIT 0x00
 
+enum i2c_busses {
+       I2C0 = 0,
+       I2C1,
+       I2C2,
+       I2C3,
+};
+
+enum i2c_modes {
+   I2C_MASTER = 0,
+   I2C_SLAVE,
+   I2C_MONITOR,
+};
+
 enum i2c_conditions {
        I2C_CONT = 0,
        I2C_DO_REPEATED_START,
@@ -106,7 +121,7 @@ enum i2c_state_machine_states {
  *   -EREMOTEIO : Device did not acknowledge
  *   -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
  */
-int i2c_read(const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count);
+int i2c_read(uint8_t bus_num, const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count);
 
 /* I2C Write
  * Performs a non-blocking write on the i2c bus.
@@ -127,16 +142,28 @@ int i2c_read(const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* i
  *   -EREMOTEIO : Device did not acknowledge
  *   -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
  */
-int i2c_write(const void *buf, size_t count, const void* ctrl_buf);
+int i2c_write(uint8_t bus_num, const void *buf, size_t count, const void* ctrl_buf);
+
 
+/* Release Bus
+ * Some devices do not release the Bus at the end of a transaction if they don't receive
+ *   a start condition immediately followed by a stop condition.
+ */
+void i2c_release_bus(uint8_t bus_num);
 
 
 
 /***************************************************************************** */
 /*                I2C Init                                                     */
 /***************************************************************************** */
-void i2c_on(uint32_t i2c_clk_freq);
-void i2c_off(void);
+/* I2C on / off
+ *   bus_num : I2C bus number to use. Ignored on this micro-controller which has only one I2C bus.
+ *   i2c_clk_freq : I2C clock freqeuncy in Hz
+ *   mode is one of I2C_MASTER, I2C_SLAVE or I2C_MONITOR.
+ *   Note that only I2C_MASTER is currently supported.
+ */
+int i2c_on(uint8_t bus_num, uint32_t i2c_clk_freq, uint8_t mode);
+int i2c_off(uint8_t bus_num);
 /* Allow system to propagate main clock */
 void i2c_clock_update(void);
 
index 6fd6ec3..a3413f1 100644 (file)
@@ -57,6 +57,7 @@
  */
 struct tmp101_sensor_config {
        uint8_t addr;
+       uint8_t bus_num;
        uint8_t probe_ok;
        uint8_t actual_config;
        int last_accessed_register;