Add support for multiple I2C buses

This commit is contained in:
Jeremy Soller
2020-01-15 16:25:30 -07:00
parent efa9fb7fb4
commit 93545fb83c
16 changed files with 152 additions and 123 deletions

View File

@ -6,101 +6,111 @@
//TODO: find best value
#define I2C_TIMEOUT 10000
#define HOSTA HOSTAE
#define HOCTL HOCTLE
#define HOCTL2 HOCTL2E
#define HOBDB HOBDBE
#define TRASLA TRASLAE
struct I2C {
volatile uint8_t * hosta;
volatile uint8_t * hoctl;
volatile uint8_t * hoctl2;
volatile uint8_t * hobdb;
volatile uint8_t * trasla;
};
void i2c_reset(bool kill) {
if (HOSTA & HOSTA_BUSY) {
struct I2C __code I2C_4 = {
.hosta = HOSTAE,
.hoctl = HOCTLE,
.hoctl2 = HOCTL2E,
.hobdb = HOBDBE,
.trasla = TRASLAE,
};
void i2c_reset(struct I2C * i2c, bool kill) {
if (*(i2c->hosta) & HOSTA_BUSY) {
// Set kill bit
if (kill) HOCTL |= (1 << 1);
if (kill) *(i2c->hoctl) |= (1 << 1);
// Wait for host to finish
while (HOSTA & HOSTA_BUSY) {}
while (*(i2c->hosta) & HOSTA_BUSY) {}
}
// Clear status register
HOSTA = HOSTA;
*(i2c->hosta) = *(i2c->hosta);
// Clear current command
HOCTL = 0;
*(i2c->hoctl) = 0;
// Disable host interface
HOCTL2 = 0;
*(i2c->hoctl2) = 0;
}
int i2c_start(uint8_t addr, bool read) {
int i2c_start(struct I2C * i2c, uint8_t addr, bool read) {
// If we are already in a transaction
if (HOSTA & HOSTA_BYTE_DONE) {
if (*(i2c->hosta) & HOSTA_BYTE_DONE) {
// If we are switching direction
if ((TRASLA & 1) != read) {
if ((*(i2c->trasla) & 1) != read) {
// If we are switching to read mode
if (read) {
// Enable direction switch
HOCTL2 |= (1 << 3) | (1 << 2);
*(i2c->hoctl2) |= (1 << 3) | (1 << 2);
} else {
// Unsupported!
i2c_reset(true);
i2c_reset(i2c, true);
return -1;
}
}
} else {
i2c_reset(true);
i2c_reset(i2c, true);
// Enable host controller with i2c compatibility
HOCTL2 = (1 << 1) | 1;
*(i2c->hoctl2) = (1 << 1) | 1;
// Set address
TRASLA = (addr << 1) | read;
*(i2c->trasla) = (addr << 1) | read;
}
return 0;
}
void i2c_stop(void) {
void i2c_stop(struct I2C * i2c) {
// Disable i2c compatibility
HOCTL2 &= ~(1 << 1);
*(i2c->hoctl2) &= ~(1 << 1);
// Clear status
HOSTA = HOSTA;
*(i2c->hosta) = *(i2c->hosta);
i2c_reset(false);
i2c_reset(i2c, false);
}
static int i2c_transaction(uint8_t * data, int length, bool read) {
static int i2c_transaction(struct I2C * i2c, 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
HOCTL |= (1 << 5);
*(i2c->hoctl) |= (1 << 5);
}
} else {
// Write byte
HOBDB = data[i];
*(i2c->hobdb) = data[i];
}
// If we are already in a transaction
if (HOSTA & HOSTA_BYTE_DONE) {
if (*(i2c->hosta) & HOSTA_BYTE_DONE) {
// Clear status to process next byte
HOSTA = HOSTA;
*(i2c->hosta) = *(i2c->hosta);
} else {
// Start new transaction
HOCTL = (1 << 6) | (0b111 << 2);
*(i2c->hoctl) = (1 << 6) | (0b111 << 2);
}
// If we are waiting on direction switch
if (HOCTL2 & (1 << 2)) {
if (*(i2c->hoctl2) & (1 << 2)) {
// Complete direction switch
HOCTL2 &= ~(1 << 2);
*(i2c->hoctl2) &= ~(1 << 2);
}
// Wait for byte done, timeout, or error
uint8_t status;
uint32_t timeout = I2C_TIMEOUT;
for(timeout = I2C_TIMEOUT; timeout > 0; timeout--) {
status = HOSTA;
status = *(i2c->hosta);
// If error occured, kill transaction and return error
if (status & HOSTA_ERR) {
i2c_reset(true);
i2c_reset(i2c, true);
return -(int)(status);
} else
// If byte done, break
@ -110,23 +120,23 @@ static int i2c_transaction(uint8_t * data, int length, bool read) {
}
// If timeout occured, kill transaction and return error
if (timeout == 0) {
i2c_reset(true);
i2c_reset(i2c, true);
return -(0x1000 | (int)status);
}
if (read) {
// Read byte
data[i] = HOBDB;
data[i] = *(i2c->hobdb);
}
}
return i;
}
int i2c_read(uint8_t * data, int length) {
return i2c_transaction(data, length, true);
int i2c_read(struct I2C * i2c, uint8_t * data, int length) {
return i2c_transaction(i2c, data, length, true);
}
int i2c_write(uint8_t * data, int length) {
return i2c_transaction(data, length, false);
int i2c_write(struct I2C * i2c, uint8_t * data, int length) {
return i2c_transaction(i2c, data, length, false);
}

View File

@ -3,6 +3,8 @@
#include <common/i2c.h>
void i2c_reset(bool kill);
extern struct I2C __code I2C_4;
void i2c_reset(struct I2C * i2c, bool kill);
#endif // _EC_I2C_H

View File

@ -6,101 +6,111 @@
//TODO: find best value
#define I2C_TIMEOUT 10000
#define HOSTA HOSTAA
#define HOCTL HOCTLA
#define HOCTL2 HOCTL2A
#define HOBDB HOBDBA
#define TRASLA TRASLAA
struct I2C {
volatile uint8_t * hosta;
volatile uint8_t * hoctl;
volatile uint8_t * hoctl2;
volatile uint8_t * hobdb;
volatile uint8_t * trasla;
};
void i2c_reset(bool kill) {
if (HOSTA & HOSTA_BUSY) {
struct I2C __code I2C_0 = {
.hosta = HOSTAA,
.hoctl = HOCTLA,
.hoctl2 = HOCTL2A,
.hobdb = HOBDBA,
.trasla = TRASLAA,
};
void i2c_reset(struct I2C * i2c, bool kill) {
if (*(i2c->hosta) & HOSTA_BUSY) {
// Set kill bit
if (kill) HOCTL |= (1 << 1);
if (kill) *(i2c->hoctl) |= (1 << 1);
// Wait for host to finish
while (HOSTA & HOSTA_BUSY) {}
while (*(i2c->hosta) & HOSTA_BUSY) {}
}
// Clear status register
HOSTA = HOSTA;
*(i2c->hosta) = *(i2c->hosta);
// Clear current command
HOCTL = 0;
*(i2c->hoctl) = 0;
// Disable host interface
HOCTL2 = 0;
*(i2c->hoctl2) = 0;
}
int i2c_start(uint8_t addr, bool read) {
int i2c_start(struct I2C * i2c, uint8_t addr, bool read) {
// If we are already in a transaction
if (HOSTA & HOSTA_BYTE_DONE) {
if (*(i2c->hosta) & HOSTA_BYTE_DONE) {
// If we are switching direction
if ((TRASLA & 1) != read) {
if ((*(i2c->trasla) & 1) != read) {
// If we are switching to read mode
if (read) {
// Enable direction switch
HOCTL2 |= (1 << 3) | (1 << 2);
*(i2c->hoctl2) |= (1 << 3) | (1 << 2);
} else {
// Unsupported!
i2c_reset(true);
i2c_reset(i2c, true);
return -1;
}
}
} else {
i2c_reset(true);
i2c_reset(i2c, true);
// Enable host controller with i2c compatibility
HOCTL2 = (1 << 1) | 1;
*(i2c->hoctl2) = (1 << 1) | 1;
// Set address
TRASLA = (addr << 1) | read;
*(i2c->trasla) = (addr << 1) | read;
}
return 0;
}
void i2c_stop(void) {
void i2c_stop(struct I2C * i2c) {
// Disable i2c compatibility
HOCTL2 &= ~(1 << 1);
*(i2c->hoctl2) &= ~(1 << 1);
// Clear status
HOSTA = HOSTA;
*(i2c->hosta) = *(i2c->hosta);
i2c_reset(false);
i2c_reset(i2c, false);
}
static int i2c_transaction(uint8_t * data, int length, bool read) {
static int i2c_transaction(struct I2C * i2c, 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
HOCTL |= (1 << 5);
*(i2c->hoctl) |= (1 << 5);
}
} else {
// Write byte
HOBDB = data[i];
*(i2c->hobdb) = data[i];
}
// If we are already in a transaction
if (HOSTA & HOSTA_BYTE_DONE) {
if (*(i2c->hosta) & HOSTA_BYTE_DONE) {
// Clear status to process next byte
HOSTA = HOSTA;
*(i2c->hosta) = *(i2c->hosta);
} else {
// Start new transaction
HOCTL = (1 << 6) | (0b111 << 2);
*(i2c->hoctl) = (1 << 6) | (0b111 << 2);
}
// If we are waiting on direction switch
if (HOCTL2 & (1 << 2)) {
if (*(i2c->hoctl2) & (1 << 2)) {
// Complete direction switch
HOCTL2 &= ~(1 << 2);
*(i2c->hoctl2) &= ~(1 << 2);
}
// Wait for byte done, timeout, or error
uint8_t status;
uint32_t timeout = I2C_TIMEOUT;
for(timeout = I2C_TIMEOUT; timeout > 0; timeout--) {
status = HOSTA;
status = *(i2c->hosta);
// If error occured, kill transaction and return error
if (status & HOSTA_ERR) {
i2c_reset(true);
i2c_reset(i2c, true);
return -(int)(status);
} else
// If byte done, break
@ -110,23 +120,23 @@ static int i2c_transaction(uint8_t * data, int length, bool read) {
}
// If timeout occured, kill transaction and return error
if (timeout == 0) {
i2c_reset(true);
i2c_reset(i2c, true);
return -(0x1000 | (int)status);
}
if (read) {
// Read byte
data[i] = HOBDB;
data[i] = *(i2c->hobdb);
}
}
return i;
}
int i2c_read(uint8_t * data, int length) {
return i2c_transaction(data, length, true);
int i2c_read(struct I2C * i2c, uint8_t * data, int length) {
return i2c_transaction(i2c, data, length, true);
}
int i2c_write(uint8_t * data, int length) {
return i2c_transaction(data, length, false);
int i2c_write(struct I2C * i2c, uint8_t * data, int length) {
return i2c_transaction(i2c, data, length, false);
}

View File

@ -3,6 +3,8 @@
#include <common/i2c.h>
void i2c_reset(bool kill);
extern struct I2C __code I2C_0;
void i2c_reset(struct I2C * i2c, bool kill);
#endif // _EC_I2C_H