--- /dev/null
+/****************************************************************************
+ * drivers/eeprom.c
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include <stdint.h>
+
+#include "core/lpc_regs_12xx.h"
+#include "core/lpc_core_cm0.h"
+#include "core/system.h"
+#include "lib/string.h"
+#include "drivers/i2c.h"
+
+
+/***************************************************************************** */
+/* Read and Write for module eeprom */
+/***************************************************************************** */
+/* Config */
+/* Small eeprom : up to 2k bytes. These use a segment address on the lower three bits
+ * of the address byte, and thus reply on 8 consecutive addresses */
+#define EEPROM_ID_SMALL_ADDR 0xA0
+#define EEPROM_ID_SMALL_I2C_SIZE 1024
+#define EEPROM_ID_SMALL_PAGE_SIZE 16
+/* Big eeprom : from 4k bytes and above : These use two address bytes, and the three
+ * physical address pins are used to set the chip address. On DTPlug modules they should
+ * have address 0xA2. */
+#define EEPROM_ID_BIG_ADDR 0xA2
+#define EEPROM_ID_BIG_I2C_SIZE 16*1024
+#define EEPROM_ID_BIG_PAGE_SIZE 64
+
+#define EEPROM_TYPE_SMALL EEPROM_ID_SMALL_ADDR
+#define EEPROM_TYPE_BIG EEPROM_ID_BIG_ADDR
+static uint8_t eeprom_type = 0; /* Will in fact store the eeprom address */
+
+/* Detect the eeprom size */
+int eeprom_detect(void)
+{
+ int ret = 0;
+ char cmd_buf[1] = { EEPROM_ID_SMALL_ADDR, };
+
+ /* Look for small eeproms first, only these would answer on EEPROM_ID_SMALL_ADDR */
+ ret = i2c_read(cmd_buf, 1, NULL, NULL, 0);
+ if (ret == 0) {
+ return EEPROM_TYPE_SMALL;
+ }
+ /* No small eeprom ... look for big ones */
+ cmd_buf[0] = EEPROM_ID_BIG_ADDR;
+ ret = i2c_read(cmd_buf, 1, NULL, NULL, 0);
+ if (ret == 0) {
+ return EEPROM_TYPE_BIG;
+ }
+
+ if (ret > 0) {
+ return = -1;
+ } else if (ret == -EREMOTEIO) {
+ return EEPROM_TYPE_NONE; /* No module */
+ }
+ return ret; /* Error or module size */
+}
+
+/* EEPROM Read
+ * Performs a non-blocking read on the eeprom.
+ * address : data offset in eeprom.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes read. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EFAULT : address above eeprom size
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -EREMOTEIO : Device did not acknowledge
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+#define CMD_BUF_SIZE 4
+int eeprom_read(uint32_t offset, void *buf, size_t count)
+{
+ int ret = 0;
+ char cmd_buf[CMD_BUF_SIZE] = { EEPROM_ID_BIG_ADDR, 0, 0, (EEPROM_ID_BIG_ADDR | 0x01), };
+ char ctrl_buf[CMD_BUF_SIZE] = { I2C_CONT, I2C_CONT, I2C_DO_REPEATED_START, I2C_CONT, };
+
+ if (eeprom_type <= 0) {
+ eeprom_type = eeprom_detect();
+ if (eeprom_type <= 0) {
+ return -EBADFD;
+ }
+ }
+
+ /* Read the requested data */
+ switch (eeprom_type) {
+ case EEPROM_TYPE_SMALL:
+ cmd_buf[0] = EEPROM_ID_SMALL_ADDR | ((offset & 0x700) >> 7);
+ cmd_buf[1] = offset & 0xFF;
+ cmd_buf[2] = EEPROM_ID_SMALL_ADDR | 0x01;
+ ret = i2c_read(cmd_buf, CMD_BUF_SIZE - 1, ctrl_buf + 1, buf, count);
+ break;
+ case EEPROM_TYPE_BIG:
+ cmd_buf[1] = ((offset & 0xFF00) >> 8);
+ cmd_buf[2] = offset & 0xFF;
+ ret = i2c_read(cmd_buf, CMD_BUF_SIZE, ctrl_buf, buf, count);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+
+/* EEPROM Write
+ * Performs a non-blocking write on the eeprom.
+ * address : data offset in eeprom.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes written. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EFAULT : address above eeprom size
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -EREMOTEIO : Device did not acknowledge
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+#define CMD_SIZE_SMALL 2
+#define CMD_SIZE_BIG 3
+#define MAX_CMD_SIZE CMD_SIZE_BIG
+#define EEPROM_ID_MAX_PAGE_SIZE EEPROM_ID_BIG_PAGE_SIZE
+int eeprom_write(uint32_t offset, const void *buf, size_t count)
+{
+}
+
+
--- /dev/null
+/****************************************************************************
+ * drivers/i2c.c
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#include <stdint.h>
+
+#include "core/lpc_regs_12xx.h"
+#include "core/lpc_core_cm0.h"
+#include "core/system.h"
+#include "lib/string.h"
+#include "drivers/i2c.h"
+
+
+/*
+ * I2C Bus structure
+ *
+ * clock : current i2c clock.
+ * bus_lock : lock to take hold of the bus.
+ * state : global state of the i2c engine.
+ * master_status : status returned by i2c block as found in "status" register.
+ *
+ * repeated_start_restart : This buffer, if used, must have the SAME size as out_buff.
+ * Then, instead of simply moving to the next byte, the corresponding condition
+ * will be executed : I2C_CONT, I2C_DO_REPEATED_START, I2C_DO_STOP_START or I2C_STOP.
+ * restart_after_addr : Can be used instead of repeated_start_restart buffer to perform a
+ * single repeated start or stop/start sequence after first adress got sent.
+ * restart_after_data : Can be used instead of repeated_start_restart buffer to perform a
+ * single repeated start or stop/start sequence after given data byte.
+ */
+struct i2c_bus {
+ volatile struct lpc_i2c* regs;
+ volatile uint32_t clock;
+ volatile uint32_t bus_lock;
+ volatile uint32_t state;
+ volatile uint32_t master_status;
+ volatile uint32_t slave_status;
+ volatile uint32_t timeout;
+
+ volatile const char* out_buff;
+ volatile const char* repeated_start_restart;
+ volatile uint32_t write_length;
+ volatile uint32_t write_index;
+ volatile uint32_t restart_after_addr;
+ volatile uint32_t restart_after_data;
+
+ volatile char* in_buff;
+ volatile uint32_t read_length;
+ volatile uint32_t read_index;
+};
+
+static struct i2c_bus mod_i2c;
+
+
+/* FIXME : Should add a field "what to do on addr NACK" to i2c_bus structure, and use
+ it with the timeout to create a retry mechanism */
+/* FIXME : For case 58 ... What would be the use of a restart ?? perform periodic reads ? */
+/* FIXME : Implement Slave when arbitration lost ? */
+
+
+
+/* I2C Interrupt handler */
+/* Actual version will stop on NACKs */
+/* See LPC1764 user's manual UM10360 on page 457 (19.9.5) for details on I2C State machine */
+void I2C_Handler(void)
+{
+ uint8_t status;
+ struct i2c_bus* i2c = &mod_i2c;
+
+ i2c->timeout = 0;
+
+ /* this handler deals with master read and master write only */
+ status = (i2c->regs->status & 0xFF);
+ i2c->master_status = status; /* Store current status */
+
+ /* I2C State Machine ! */
+ switch (status) {
+
+ /* All modes */
+ case 0x00: /* Bus Error. Enter not addressed Slave mode and release bus. */
+ i2c->regs->ctrl_set = (I2C_ASSERT_ACK | I2C_STOP_FLAG);
+ i2c->state = I2C_BUS_ERROR;
+ break;
+ case 0x08: /* A START condition has been transmitted. */
+ i2c->write_index = 0;
+ if (i2c->write_length != 0) {
+ /* Send Slave Address (SLA)
+ * Depending on R/W bit, Master Receive or master Transmit mode will be enterred. */
+ i2c->regs->data = i2c->out_buff[i2c->write_index++];
+ i2c->regs->ctrl_clear = I2C_START_FLAG;
+ } else {
+ i2c->regs->ctrl_clear = I2C_START_FLAG;
+ i2c->regs->ctrl_set = I2C_STOP_FLAG;
+ i2c->state = I2C_NO_DATA;
+ }
+ break;
+ case 0x10: /* A repeated START has been transmitted. */
+ /* Setting read_index to 0 is usefull only if next data byte is
+ * Slave Address + Read (SLA + R), but it's OK if we will write too. */
+ i2c->read_index = 0;
+ /* Send Slave Address and Read/Write bit (SLA + R/W)
+ * Depending on R/W bit, Master Receive or master Transmit mode will be enterred. */
+ i2c->regs->data = i2c->out_buff[i2c->write_index++];
+ /* FIXME : Shouldn't this be done only in receiver mode (SLA + R) ? */
+ i2c->regs->ctrl_clear = I2C_START_FLAG;
+ break;
+ case 0x38: /* Arbitration lost. We don't deal with multiple master situation */
+ /* FIXME : We have two option :
+ * - do nothing, which will release the bus. Will lead to "Not Addressed Slave" state.
+ * This options allows handling of multiple master topologies.
+ * - Set start flag, and a start condition will be transmitted when bus becomes free.
+ */
+ /* We choose to do nothing, we ARE the true master after all ! */
+ i2c->state = I2C_ARBITRATION_LOST;
+ break;
+
+ /* Master Transmitter Mode Only */
+ case 0x18: /* Address + Write transmitted and ACK'ed */
+ if (i2c->write_length == 1) { /* Nothing more to send, we must stop. */
+ /* This one is used to probe devices, or wait for completion */
+ i2c->regs->ctrl_set = I2C_STOP_FLAG;
+ i2c->state = I2C_NO_DATA;
+ } else {
+ uint32_t condition = i2c->restart_after_addr;
+ if (i2c->repeated_start_restart)
+ condition = i2c->repeated_start_restart[i2c->write_index - 1];
+ switch (condition) {
+ case I2C_CONT: /* Send next data byte */
+ i2c->regs->data = i2c->out_buff[i2c->write_index++];
+ break;
+ case I2C_DO_REPEATED_START: /* Send repeated start condition */
+ i2c->regs->ctrl_set = I2C_START_FLAG;
+ break;
+ case I2C_DO_STOP_START: /* Send STOP / START condition */
+ i2c->regs->ctrl_set = I2C_STOP_FLAG | I2C_START_FLAG;
+ break;
+ case I2C_STOP: /* Send STOP condition */
+ i2c->regs->ctrl_set = I2C_STOP_FLAG;
+ i2c->state = I2C_NO_DATA;
+ break;
+ }
+ if (i2c->restart_after_addr)
+ i2c->restart_after_addr = 0; /* This one must be defused once used */
+ }
+ break;
+ case 0x20: /* NACK on Address + Write (SLA + W) */
+ case 0x30: /* NACK on Data byte */
+ /* FIXME : We have three other options : Resend / Repeated START / STOP + START
+ * If I'm right, Resend would require moving write_index back and using same data
+ * Repeated START ? ignore the NACK and move to reading data ?
+ * STOP + START ? Start over ? write_index will be set to 0 in I2C_STARTED case (0x08)
+ * Sending a STOP condition will end transactions and let the driver take the decision.
+ */
+ i2c->regs->ctrl_set = I2C_STOP_FLAG;
+ i2c->state = I2C_NACK;
+ break;
+ case 0x28: /* Data byte has been transmitted and ACK received */
+ if (i2c->write_index < i2c->write_length) {
+ /* More to write, but what do we do ? */
+ uint32_t condition = I2C_CONT;
+ if (i2c->restart_after_data == i2c->write_index)
+ condition = I2C_DO_REPEATED_START;
+ else if (i2c->repeated_start_restart)
+ condition = i2c->repeated_start_restart[i2c->write_index - 1];
+ switch (condition) {
+ case I2C_CONT: /* Send next data byte */
+ i2c->regs->data = i2c->out_buff[i2c->write_index++];
+ break;
+ case I2C_DO_REPEATED_START: /* Send repeated start condition */
+ i2c->regs->ctrl_set = I2C_START_FLAG;
+ break;
+ case I2C_DO_STOP_START: /* Send STOP / START condition */
+ i2c->regs->ctrl_set = I2C_STOP_FLAG | I2C_START_FLAG;
+ break;
+ case I2C_STOP: /* Send STOP condition */
+ /* Hey, why sending a STOP and terminate communication if we are not at
+ * the end of the write buffer ?? */
+ i2c->regs->ctrl_set = I2C_STOP_FLAG;
+ i2c->state = I2C_OK;
+ break;
+ }
+ } else {
+ if (i2c->read_length != 0) { /* Anything to read ? */
+ i2c->regs->ctrl_set = I2C_START_FLAG;
+ } else {
+ i2c->regs->ctrl_set = I2C_STOP_FLAG;
+ i2c->state = I2C_OK;
+ }
+ }
+ break;
+
+ /* Master Receiver Mode Only */
+ case 0x40: /* Address + Read transmitted and ACK'ed */
+ if ((i2c->read_index + 1) < i2c->read_length) {
+ /* Will go to State 0x50 (assert ACK after data is received) */
+ i2c->regs->ctrl_set = I2C_ASSERT_ACK;
+ } else {
+ /* Will go to State 0x58 (NACK after data is received) */
+ i2c->regs->ctrl_clear = I2C_ASSERT_ACK;
+ }
+ break;
+
+ case 0x48: /* NACK on Address + Read (SLA + R) */
+ /* FIXME : We have two other options : Repeated START / STOP + START */
+ i2c->regs->ctrl_set = I2C_STOP_FLAG;
+ i2c->state = I2C_NACK;
+ break;
+
+ case 0x50: /* Data byte has been received and ACK sent */
+ i2c->in_buff[i2c->read_index++] = i2c->regs->data;
+ if ((i2c->read_index + 1) < i2c->read_length) {
+ /* assert ACK after data is received, requesting next Data from slave */
+ i2c->regs->ctrl_set = I2C_ASSERT_ACK;
+ } else {
+ /* Assert NACK on last byte, telling the slave to stop transmitting data
+ and release the I2C Bus */
+ i2c->regs->ctrl_clear = I2C_ASSERT_ACK;
+ }
+ break;
+
+ case 0x58: /* Data byte has been received and NACK "sent" */
+ /* This tells the slave it was the last byte. We should be done. */
+ i2c->in_buff[i2c->read_index++] = i2c->regs->data;
+ /* FIXME : We have two other options : Repeated START or STOP + START,
+ * but what for ? periodic reads ? */
+ i2c->regs->ctrl_set = I2C_STOP_FLAG;
+ i2c->state = I2C_OK;
+ break;
+
+ default:
+ i2c->state = I2C_ERROR_UNKNOWN;
+ break;
+ }
+
+ /* Clear interrupt flag. This has to be done last. */
+ i2c->regs->ctrl_clear = I2C_INTR_FLAG;
+ return;
+}
+
+
+
+
+/***************************************************************************** */
+/* I2C access */
+/***************************************************************************** */
+/* 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
+ * cmd_size : size of the cmd_buf command buffer
+ * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions
+ * if not NULL, ctrl_buf has the same size as cmd_buf
+ * inbuff : the buffer where read data will be put. May be NULL if count is 0.
+ * count : the number of bytes to be read.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes read. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EBADFD : Device not initialized (-77)
+ * -EBUSY : Device or ressource Busy or Arbitration lost (-16)
+ * -EAGAIN : Device already in use (-11)
+ * -EINVAL : Invalid argument (no cmd_buf, or no inbuff when count > 0) (-22)
+ * -EREMOTEIO : Device did not acknowledge (-121)
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine (-5)
+ */
+int i2c_read(const void *cmd_buf, size_t cmd_size, const void* ctrl_buf, void* inbuff, size_t count)
+{
+ int ret = 0;
+
+ /* Checks */
+ if (mod_i2c.regs != LPC_I2C0)
+ return -EBADFD;
+ if (cmd_buf == NULL)
+ return -EINVAL;
+ if ((inbuff == NULL) && (count > 0))
+ return -EINVAL;
+
+ /* Lock acquire */
+ if (sync_lock_test_and_set(&mod_i2c.bus_lock, 1) == 1)
+ return -EAGAIN;
+
+ if (mod_i2c.state == I2C_BUSY) {
+ /* Someone uses the bus without hold of the bus lock ?? */
+ sync_lock_release(&mod_i2c.bus_lock);
+ return -EBUSY;
+ }
+ if (mod_i2c.state != I2C_OK) {
+ /* What should we do ??? someone failed to reset status ? */
+ }
+
+ /* Set up mod_i2c for read operation */
+ /* command (write) buffer */
+ mod_i2c.out_buff = cmd_buf;
+ mod_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;
+ /* read buffer */
+ mod_i2c.in_buff = inbuff;
+ mod_i2c.read_length = count;
+
+ /* 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;
+ }
+
+ /* 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;
+
+ /* Release the lock */
+ sync_lock_release(&mod_i2c.bus_lock);
+
+ return ret;
+}
+
+/* Write
+ * Performs a non-blocking write on the module's i2c bus.
+ * buf : buffer containing all byte to be sent on the i2c bus,
+ * including conrtol bytes (address, offsets, ...)
+ * count : the number of bytes to be sent, including address bytes and so on.
+ * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions
+ * FIXME : note that STOP + START conditions are not allowed, the STOP would lead to sending
+ * the first bytes of buf, creating an infinite loop.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes written. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -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 ret = 0;
+
+ /* Checks */
+ if (mod_i2c.regs != LPC_I2C0)
+ return -EBADFD;
+ if (buf == NULL)
+ return -EINVAL;
+
+ /* Lock acquire */
+ if (sync_lock_test_and_set(&mod_i2c.bus_lock, 1) == 1)
+ return -EAGAIN;
+
+ if (mod_i2c.state == I2C_BUSY) {
+ /* Someone uses the bus without hold of the bus lock ?? */
+ sync_lock_release(&mod_i2c.bus_lock);
+ return -EBUSY;
+ }
+ if (mod_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;
+ /* 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;
+
+ /* 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;
+ }
+
+ /* 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;
+
+ /* Release the lock */
+ sync_lock_release(&mod_i2c.bus_lock);
+
+ return ret;
+}
+
+
+
+/***************************************************************************** */
+/* I2C Init */
+/***************************************************************************** */
+static void i2c_clock_on(uint32_t i2c_clk_freq)
+{
+ struct lpc_i2c* i2c = LPC_I2C0;
+ uint32_t main_clock = get_main_clock();
+ uint32_t scl_clk = 0;
+
+ /* Setup I2C clock */
+ scl_clk = (main_clock / i2c_clk_freq);
+ i2c->clk_duty_high = (scl_clk / 2);
+ i2c->clk_duty_low = (scl_clk - i2c->clk_duty_high);
+}
+
+void set_i2c_pins(void)
+{
+ struct lpc_sys_control* sys_ctrl = LPC_SYS_CONTROL;
+ struct lpc_io_control* ioctrl = LPC_IO_CONTROL;
+
+ /* Make sure IO_Config is clocked */
+ sys_ctrl->sys_AHB_clk_ctrl |= LPC_SYS_ABH_CLK_CTRL_IO_CONFIG;
+ ioctrl->pio0_10 = LPC_IO_FUNC_ALT(2) | LPC_IO_OPEN_DRAIN_ENABLE; /* True open drai */
+ ioctrl->pio0_11 = LPC_IO_FUNC_ALT(2) | LPC_IO_OPEN_DRAIN_ENABLE;
+ /* Config done, power off IO_CONFIG block */
+ sys_ctrl->sys_AHB_clk_ctrl &= ~LPC_SYS_ABH_CLK_CTRL_IO_CONFIG;
+}
+
+void i2c_on(uint32_t i2c_clk_freq)
+{
+ struct lpc_sys_control* sys_ctrl = LPC_SYS_CONTROL;
+ struct lpc_i2c* i2c = LPC_I2C0;
+
+ NVIC_DisableIRQ(I2C0_IRQ);
+ /* Power on I2C 0 block */
+ sys_ctrl->sys_AHB_clk_ctrl |= LPC_SYS_ABH_CLK_CTRL_I2C;
+ /* Set clock */
+ i2c_clock_on(i2c_clk_freq);
+ mod_i2c.clock = i2c_clk_freq;
+ /* Set pins function for I2C 0 */
+ set_i2c_pins();
+ /* 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;
+ /* Enable I2C */
+ i2c->ctrl_set = 0x40; /* Fixme, if enabling slave functions, write 0x44 instead */
+ /* And enable interrupts */
+ NVIC_EnableIRQ(I2C0_IRQ);
+}
+
+void i2c_off(void)
+{
+ struct lpc_sys_control* sys_ctrl = LPC_SYS_CONTROL;
+ NVIC_DisableIRQ(I2C0_IRQ);
+ sys_ctrl->sys_AHB_clk_ctrl &= ~(LPC_SYS_ABH_CLK_CTRL_I2C);
+ mod_i2c.clock = 0;
+}
+
+/* Allow system to propagate main clock */
+void i2c_clock_update(void)
+{
+ if (mod_i2c.clock)
+ i2c_clock_on(mod_i2c.clock); /* 6 is not a module num, nor system i2c (5) */
+}
+
+
--- /dev/null
+/****************************************************************************
+ * drivers/i2c.h
+ *
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef DRIVERS_I2C_H
+#define DRIVERS_I2C_H
+
+#include <stdint.h>
+
+
+#define I2C_CLK_100KHz (100*1000)
+#define I2C_CLK_400KHz (400*1000)
+
+
+enum i2c_conditions {
+ I2C_CONT = 0,
+ I2C_DO_REPEATED_START,
+ I2C_DO_STOP_START,
+ I2C_STOP,
+};
+
+enum i2c_states {
+ /* Must be set before starting the state machine to be able to wait for completion. */
+ I2C_BUSY = 0,
+ I2C_OK, /* All right, default state after init has been done. */
+ /* Sent slave address and it got ACK'ed, but no data.
+ * Used to probe or wait for completion. */
+ I2C_NO_DATA,
+ I2C_NACK, /* NACK received */
+ I2C_TIME_OUT,
+ I2C_ARBITRATION_LOST, /* Another master took the I2C Bus from us ... */
+ I2C_BUS_ERROR, /* Illegal start or stop (status of 0x00) */
+ I2C_ERROR_UNKNOWN,
+};
+
+enum i2c_state_machine_states {
+ /* Error states */
+ I2C_ILLEGAL = 0x00, /* Illegal start or stop */
+ I2C_ARBIT_LOST = 0x38,
+ /* Start condition states */
+ I2C_STARTED = 0x08,
+ I2C_RESTARTED, /* Unused, should be set when restarting (STOP+START) after a NACK */
+ I2C_REPEATED_START = 0x10,
+ /* Transmitter states */
+ I2C_ACK_ON_ADDRESS_W = 0x18,
+ I2C_NACK_ON_ADDRESS_W = 0x20,
+ I2C_ACK_ON_DATA_W = 0x28,
+ I2C_NACK_ON_DATA_W = 0x30,
+ /* Receiver states */
+ I2C_ACK_ON_ADDRESS_R = 0x40,
+ I2C_NACK_ON_ADDRESS_R = 0x48,
+ I2C_DATA_ACK = 0x50,
+ I2C_DATA_NACK = 0x58,
+};
+
+
+/***************************************************************************** */
+/* I2C Init */
+/***************************************************************************** */
+void set_i2c_pins(void);
+void i2c_on(uint32_t i2c_clk_freq);
+void i2c_off(void);
+/* Allow system to propagate main clock */
+void i2c_clock_update(void);
+
+/***************************************************************************** */
+/* Read and Write for system eeprom */
+/***************************************************************************** */
+/* EEPROM Read
+ * Performs a non-blocking read on the eeprom.
+ * address : data offset in eeprom.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes read. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EFAULT : address above eeprom size
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -EREMOTEIO : Device did not acknowledge
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+int eeprom_read(uint32_t offset, void *buf, size_t count);
+
+/* EEPROM Write
+ * Performs a non-blocking write on the eeprom.
+ * address : data offset in eeprom.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes written. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EFAULT : address above eeprom size
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -EREMOTEIO : Device did not acknowledge
+ * -EIO : Bad one: Illegal start or stop, or illegal state in i2c state machine
+ */
+int eeprom_write(uint32_t offset, const void *buf, size_t count);
+
+
+
+/***************************************************************************** */
+/* Modules I2C access */
+/***************************************************************************** */
+/* I2C Read
+ * Performs a non-blocking read on the i2c bus.
+ * cmd_buf : buffer containing all control byte to be sent on the i2c bus
+ * cmd_size : size of the cmd_buf command buffer
+ * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions
+ * ctrl_buf has the same size as cmd_buf
+ * inbuff : the buffer where read data will be put.
+ * count : the number of bytes to be read.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes read. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -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);
+
+/* I2C Write
+ * Performs a non-blocking write on the i2c bus.
+ * buf : buffer containing all byte to be sent on the i2c bus,
+ * including conrtol bytes (address, offsets, ...)
+ * count : the number of bytes to be sent, including address bytes and so on.
+ * ctrl_buf : buffer containing action to be done after sending, like repeated START conditions
+ * ctrl_buf has the same size as cmd_buf
+ * FIXME : note that STOP + START conditions are not allowed, the STOP would lead to sending
+ * the first bytes of buf, creating an infinite loop.
+ * RETURN VALUE
+ * Upon successfull completion, returns the number of bytes written. On error, returns a negative
+ * integer equivalent to errors from glibc.
+ * -EBADFD : Device not initialized
+ * -EBUSY : Device or ressource Busy or Arbitration lost
+ * -EAGAIN : Device already in use
+ * -EINVAL : Invalid argument (buf)
+ * -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);
+
+
+#endif /* DRIVERS_I2C_H */