/*
* 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.
*/
struct i2c_bus {
volatile struct lpc_i2c* regs;
+ uint8_t mode;
volatile uint32_t clock;
volatile uint32_t state;
volatile uint32_t master_status;
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
void I2C_0_Handler(void)
{
uint8_t status;
- struct i2c_bus* i2c = &mod_i2c;
+ struct i2c_bus* i2c = &(i2c_buses[0]);
i2c->timeout = 0;
/***************************************************************************** */
/* 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
* 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;
}
* 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;
}
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) */
}
}
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;
}
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;
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;
/* 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;
/* 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;
}
/* 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;
}
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. */
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 */
}
#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,
* -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.
* -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);