diff --git a/src/arch/avr/i2c.c b/src/arch/avr/i2c.c index 2591336..e3255be 100644 --- a/src/arch/avr/i2c.c +++ b/src/arch/avr/i2c.c @@ -2,15 +2,12 @@ #include #include -#include #include +#include #define TIMEOUT (F_CPU/1000) -#define I2C_READ 0x01 -#define I2C_WRITE 0x00 - -uint8_t i2c_start(uint8_t addr, uint8_t rw) { +uint8_t i2c_start(uint8_t addr, bool read) { uint32_t count; // reset TWI control register @@ -32,7 +29,7 @@ uint8_t i2c_start(uint8_t addr, uint8_t rw) { } // load slave addr into data register - TWDR = ((addr << 1) | rw); + TWDR = ((addr << 1) | read); // start transmission of addr TWCR = (1< - void i2c_init(unsigned long baud); #endif // _BOARD_I2C_H diff --git a/src/board/system76/galp3-c/smbus.c b/src/board/system76/galp3-c/smbus.c index fff4ca8..8c23765 100644 --- a/src/board/system76/galp3-c/smbus.c +++ b/src/board/system76/galp3-c/smbus.c @@ -1,4 +1,5 @@ #include +#include void smbus_init(void) { // 9.2 MHz * 4.7 us = 43.24 @@ -17,4 +18,7 @@ void smbus_init(void) { // Clock set to 50 Hz SCLKTSA = 1; + + // Set up for i2c usage + i2c_reset(true); } diff --git a/src/board/system76/galp3-c/stdio.c b/src/board/system76/galp3-c/stdio.c index 8dbeec7..3a57dbf 100644 --- a/src/board/system76/galp3-c/stdio.c +++ b/src/board/system76/galp3-c/stdio.c @@ -1,9 +1,9 @@ #include -#include +#include int putchar(int c) { unsigned char byte = (unsigned char)c; - i2c_write(0x76, &byte, 1); + i2c_send(0x76, &byte, 1); return (int)byte; } diff --git a/src/common/i2c.c b/src/common/i2c.c new file mode 100644 index 0000000..b3c4d9f --- /dev/null +++ b/src/common/i2c.c @@ -0,0 +1,53 @@ +#include + +int i2c_recv(uint8_t addr, uint8_t* data, int length) { + int res = 0; + + res = i2c_start(addr, true); + if (res < 0) return res; + + res = i2c_read(data, length); + if (res < 0) return res; + + i2c_stop(); + + return res; +} + +int i2c_send(uint8_t addr, uint8_t* data, int length) { + int res = 0; + + res = i2c_start(addr, false); + if (res < 0) return res; + + res = i2c_write(data, length); + if (res < 0) return res; + + i2c_stop(); + + return res; +} + +int i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, int length) { + int res = 0; + + res = i2c_start(addr, false); + if (res < 0) return res; + + res = i2c_write(®, 1); + if (res < 0) return res; + + return i2c_recv(addr, data, length); +} + +int i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, int length) { + int res = 0; + + res = i2c_start(addr, false); + if (res < 0) return res; + + res = i2c_write(®, 1); + if (res < 0) return res; + + return i2c_send(addr, data, length); +} diff --git a/src/common/include/common/i2c.h b/src/common/include/common/i2c.h new file mode 100644 index 0000000..8f79820 --- /dev/null +++ b/src/common/include/common/i2c.h @@ -0,0 +1,35 @@ +#ifndef _COMMON_I2C_H +#define _COMMON_I2C_H + +#include +#include + +// Start i2c transaction +// Must be defined by arch, board, or ec +int i2c_start(uint8_t addr, bool read); + +// Stop i2c transaction +// Must be defined by arch, board, or ec +void i2c_stop(void); + +// Send a byte on i2c bus +// Must be defined by arch, board, or ec +int i2c_write(uint8_t * data, int length); + +// Read bytes from bus +// Must be defined by arch, board, or ec +int i2c_read(uint8_t * data, int length); + +// Read multiple bytes from address in one transaction +int i2c_recv(uint8_t addr, uint8_t* data, int length); + +// Write multiple bytes to address in one transaction +int i2c_send(uint8_t addr, uint8_t* data, int length); + +// Read multiple bytes from a register in one transaction +int i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, int length); + +// Write multiple bytes to a register in one transaction +int i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, int length); + +#endif // _COMMON_I2C_H diff --git a/src/ec/it8587e/i2c.c b/src/ec/it8587e/i2c.c index 9eb7edd..943cd70 100644 --- a/src/ec/it8587e/i2c.c +++ b/src/ec/it8587e/i2c.c @@ -1,7 +1,10 @@ +#include + #include -#include +#include #include +//TODO: find best value #define TIMEOUT (F_CPU/1000) #define HOSTA_BYTE_DONE (1 << 7) @@ -14,66 +17,118 @@ #define HOSTA_BUSY (1 << 0) #define HOSTA_ERR (HOSTA_TIMEOUT | HOSTA_NACK | HOSTA_FAIL | HOSTA_BUS_ERR | HOSTA_DEV_ERR) -static void i2c_kill(void) { - if (HOSTAA & HOSTA_BUSY) { - HOCTLA = (1 << 1); - // TODO: is timeout needed? - while (HOSTAA & HOSTA_BUSY) {} - } +void i2c_reset(bool kill) { + // Set kill bit + if (kill) HOCTLA |= (1 << 1); + // Wait for host to finish + while (HOSTAA & HOSTA_BUSY) {} + // Clear status register HOSTAA = HOSTAA; + // Clear current command HOCTLA = 0; + // Disable host interface HOCTL2A = 0; } -uint8_t i2c_transaction(uint8_t addr_rw, uint8_t *data, uint16_t length) { - // End any current transaction - i2c_kill(); +int i2c_start(uint8_t addr, bool read) { + // If we are already in a transaction + if (HOSTAA & HOSTA_BYTE_DONE) { + // If we are switching direction + if ((TRASLAA & 1) != read) { + // If we are switching to read mode + if (read) { + // Enable direction switch + HOCTL2A |= (1 << 3) | (1 << 2); - // Enable host controller with i2c compatibility - HOCTL2A = (1 << 1) | 1; - // Set slave address - TRASLAA = addr_rw; - - static uint8_t __pdata status; - static uint32_t __pdata timeout; - static uint16_t __pdata i; - for (i = 0; i < length; i++) { - if ((addr_rw & 1) && ((i + 1) == length)) { - // Set last byte - HOCTLA |= (1 << 5); - } - // Clear status - HOSTAA = HOSTAA; - if (!(addr_rw & 1)) { - // Set data byte - HOBDBA = data[i]; - } - if (i == 0) { - // Start transaction - HOCTLA = (1 << 6) | (0b111 << 2); - } - for(timeout = TIMEOUT; timeout > 0; timeout++) { - status = HOSTAA; - HOSTAA = status; - if (status & HOSTA_ERR) { - timeout = 0; - break; - } - if (status & HOSTA_BYTE_DONE) { - break; + // Clear status to start switch process + HOSTAA = HOSTAA; + } else { + // Unsupported! + i2c_reset(true); + return -1; } } - if (timeout == 0) { - i2c_kill(); - return 1; - } - if (addr_rw & 1) { - // Get data byte - data[i] = HOBDBA; - } - } + } else { + // Enable host controller with i2c compatibility + HOCTL2A = (1 << 1) | 1; - HOCTL2A = 0; + // Set address + TRASLAA = (addr << 1) | read; + } return 0; } + +void i2c_stop(void) { + // Disable i2c compatibility + HOCTL2A &= ~(1 << 1); + // Clear status + HOSTAA = HOSTAA; + + i2c_reset(false); +} + +static int i2c_transaction(uint8_t * data, int length, bool read) { + int i; + for (i = 0; i < length; i++) { + if (read) { + // If last byte + if ((i + 1) == length) { + // Set last byte bit + HOCTLA |= (1 << 5); + } + } else { + // Write byte + HOBDBA = data[i]; + } + + // If we are already in a transaction + if (HOSTAA & HOSTA_BYTE_DONE) { + // Clear status to process next byte + HOSTAA = HOSTAA; + } else + // If we are waiting on direction switch + if (HOCTLA & (1 << 2)) { + // Complete direction switch + HOCTLA &= ~(1 << 2); + } else { + // Start new transaction + HOCTLA = (1 << 6) | (0b111 << 2); + } + + // Wait for byte done, timeout, or error + // uint32_t timeout; + // for(timeout = TIMEOUT; timeout > 0; timeout--) { + for(;;) { + uint8_t status = HOSTAA; + if (status & HOSTA_BYTE_DONE) { + // If byte is finished, break + break; + } else if (status & HOSTA_ERR) { + // If error occured, kill transaction and return error + i2c_reset(true); + return -(int)(status & HOSTA_ERR); + } + } + // if (timeout == 0) { + // // If timeout occured, kill transaction and return error + // i2c_reset(true); + // return -1; + // } + + if (read) { + // Read byte + data[i] = HOBDBA; + } + } + + return i; +} + +int i2c_read(uint8_t * data, int length) { + return i2c_transaction(data, length, true); +} + +int i2c_write(uint8_t * data, int length) { + return i2c_transaction(data, length, false); +} diff --git a/src/ec/it8587e/include/ec/i2c.h b/src/ec/it8587e/include/ec/i2c.h index 60afcbd..7b1af45 100644 --- a/src/ec/it8587e/include/ec/i2c.h +++ b/src/ec/it8587e/include/ec/i2c.h @@ -1,10 +1,8 @@ #ifndef _EC_I2C_H #define _EC_I2C_H -#include +#include -uint8_t i2c_transaction(uint8_t addr_rw, uint8_t * data, uint16_t length); -#define i2c_read(addr, data, length) i2c_transaction(((uint8_t)addr << 1) | 1, data, length) -#define i2c_write(addr, data, length) i2c_transaction(((uint8_t)addr << 1), data, length) +void i2c_reset(bool kill); #endif // _EC_I2C_H