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

@ -1,12 +1,12 @@
#include <common/debug.h> #include <common/debug.h>
#include <common/i2c.h> #include <ec/i2c.h>
int smbus_read(uint8_t address, uint8_t command, uint16_t * data) { int smbus_read(uint8_t address, uint8_t command, uint16_t * data) {
return i2c_get(address, command, (uint8_t *)data, 2); return i2c_get(&I2C_0, address, command, (uint8_t *)data, 2);
} }
int smbus_write(uint8_t address, uint8_t command, uint16_t data) { int smbus_write(uint8_t address, uint8_t command, uint16_t data) {
return i2c_set(address, command, (uint8_t *)&data, 2); return i2c_set(&I2C_0, address, command, (uint8_t *)&data, 2);
} }
// ChargeOption0 flags // ChargeOption0 flags

View File

@ -23,6 +23,8 @@ extern struct Gpio __code DD_ON;
extern struct Gpio __code EC_EN; extern struct Gpio __code EC_EN;
extern struct Gpio __code EC_RSMRST_N; extern struct Gpio __code EC_RSMRST_N;
extern struct Gpio __code LED_ACIN; extern struct Gpio __code LED_ACIN;
extern struct Gpio __code LED_BAT_CHG;
extern struct Gpio __code LED_BAT_FULL;
extern struct Gpio __code LED_PWR; extern struct Gpio __code LED_PWR;
extern struct Gpio __code LID_SW_N; extern struct Gpio __code LID_SW_N;
extern struct Gpio __code PCH_DPWROK_EC; extern struct Gpio __code PCH_DPWROK_EC;

View File

@ -11,7 +11,7 @@
#define KM_LAY 2 #define KM_LAY 2
// Keymap // Keymap
extern uint16_t __code KEYMAP[KM_LAY][KM_OUT][KM_IN]; extern uint16_t __code KEYMAP[KM_OUT][KM_IN][KM_LAY];
// Get a keycode from the keymap // Get a keycode from the keymap
uint16_t keymap(int output, int input, int layer); uint16_t keymap(int output, int input, int layer);

View File

@ -5,7 +5,7 @@
#endif #endif
#ifdef I2C_DEBUGGER #ifdef I2C_DEBUGGER
#include <common/i2c.h> #include <ec/i2c.h>
#endif #endif
int putchar(int c) { int putchar(int c) {
@ -14,7 +14,7 @@ int putchar(int c) {
SBUF = byte; SBUF = byte;
#endif #endif
#ifdef I2C_DEBUGGER #ifdef I2C_DEBUGGER
i2c_send(I2C_DEBUGGER, &byte, 1); i2c_send(&I2C_0, I2C_DEBUGGER, &byte, 1);
#endif #endif
return (int)byte; return (int)byte;
} }

View File

@ -17,5 +17,5 @@ void smbus_init(void) {
SMB45P3USH = 0x01; SMB45P3USH = 0x01;
// Set up for i2c usage // Set up for i2c usage
i2c_reset(true); i2c_reset(&I2C_0, true);
} }

View File

@ -5,7 +5,7 @@
#endif #endif
#ifdef I2C_DEBUGGER #ifdef I2C_DEBUGGER
#include <common/i2c.h> #include <ec/i2c.h>
#endif #endif
int putchar(int c) { int putchar(int c) {
@ -14,7 +14,7 @@ int putchar(int c) {
SBUF = byte; SBUF = byte;
#endif #endif
#ifdef I2C_DEBUGGER #ifdef I2C_DEBUGGER
i2c_send(I2C_DEBUGGER, &byte, 1); i2c_send(&I2C_0, I2C_DEBUGGER, &byte, 1);
#endif #endif
return (int)byte; return (int)byte;
} }

View File

@ -1,12 +1,12 @@
#include <common/debug.h> #include <common/debug.h>
#include <common/i2c.h> #include <ec/i2c.h>
int smbus_read(uint8_t address, uint8_t command, uint16_t * data) { int smbus_read(uint8_t address, uint8_t command, uint16_t * data) {
return i2c_get(address, command, (uint8_t *)data, 2); return i2c_get(&I2C_4, address, command, (uint8_t *)data, 2);
} }
int smbus_write(uint8_t address, uint8_t command, uint16_t data) { int smbus_write(uint8_t address, uint8_t command, uint16_t data) {
return i2c_set(address, command, (uint8_t *)&data, 2); return i2c_set(&I2C_4, address, command, (uint8_t *)&data, 2);
} }
// ChargeOption0 flags // ChargeOption0 flags

View File

@ -5,7 +5,7 @@
#endif #endif
#ifdef I2C_DEBUGGER #ifdef I2C_DEBUGGER
#include <common/i2c.h> #include <ec/i2c.h>
#endif #endif
int putchar(int c) { int putchar(int c) {
@ -14,7 +14,7 @@ int putchar(int c) {
SBUF = byte; SBUF = byte;
#endif #endif
#ifdef I2C_DEBUGGER #ifdef I2C_DEBUGGER
i2c_send(I2C_DEBUGGER, &byte, 1); i2c_send(&I2C_4, I2C_DEBUGGER, &byte, 1);
#endif #endif
return (int)byte; return (int)byte;
} }

View File

@ -17,5 +17,5 @@ void smbus_init(void) {
SMB45P3USH = 0x01; SMB45P3USH = 0x01;
// Set up for i2c usage // Set up for i2c usage
i2c_reset(true); i2c_reset(&I2C_4, true);
} }

View File

@ -5,7 +5,7 @@
#endif #endif
#ifdef I2C_DEBUGGER #ifdef I2C_DEBUGGER
#include <common/i2c.h> #include <ec/i2c.h>
#endif #endif
int putchar(int c) { int putchar(int c) {
@ -14,7 +14,7 @@ int putchar(int c) {
SBUF = byte; SBUF = byte;
#endif #endif
#ifdef I2C_DEBUGGER #ifdef I2C_DEBUGGER
i2c_send(I2C_DEBUGGER, &byte, 1); i2c_send(&I2C_4, I2C_DEBUGGER, &byte, 1);
#endif #endif
return (int)byte; return (int)byte;
} }

View File

@ -1,53 +1,53 @@
#include <common/i2c.h> #include <common/i2c.h>
int i2c_recv(uint8_t addr, uint8_t* data, int length) { int i2c_recv(struct I2C * i2c, uint8_t addr, uint8_t* data, int length) {
int res = 0; int res = 0;
res = i2c_start(addr, true); res = i2c_start(i2c, addr, true);
if (res < 0) return res; if (res < 0) return res;
res = i2c_read(data, length); res = i2c_read(i2c, data, length);
if (res < 0) return res; if (res < 0) return res;
i2c_stop(); i2c_stop(i2c);
return res; return res;
} }
int i2c_send(uint8_t addr, uint8_t* data, int length) { int i2c_send(struct I2C * i2c, uint8_t addr, uint8_t* data, int length) {
int res = 0; int res = 0;
res = i2c_start(addr, false); res = i2c_start(i2c, addr, false);
if (res < 0) return res; if (res < 0) return res;
res = i2c_write(data, length); res = i2c_write(i2c, data, length);
if (res < 0) return res; if (res < 0) return res;
i2c_stop(); i2c_stop(i2c);
return res; return res;
} }
int i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, int length) { int i2c_get(struct I2C * i2c, uint8_t addr, uint8_t reg, uint8_t* data, int length) {
int res = 0; int res = 0;
res = i2c_start(addr, false); res = i2c_start(i2c, addr, false);
if (res < 0) return res; if (res < 0) return res;
res = i2c_write(&reg, 1); res = i2c_write(i2c, &reg, 1);
if (res < 0) return res; if (res < 0) return res;
return i2c_recv(addr, data, length); return i2c_recv(i2c, addr, data, length);
} }
int i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, int length) { int i2c_set(struct I2C * i2c, uint8_t addr, uint8_t reg, uint8_t* data, int length) {
int res = 0; int res = 0;
res = i2c_start(addr, false); res = i2c_start(i2c, addr, false);
if (res < 0) return res; if (res < 0) return res;
res = i2c_write(&reg, 1); res = i2c_write(i2c, &reg, 1);
if (res < 0) return res; if (res < 0) return res;
return i2c_send(addr, data, length); return i2c_send(i2c, addr, data, length);
} }

View File

@ -4,32 +4,35 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
// I2C bus, should be defined elsewhere
struct I2C;
// Start i2c transaction // Start i2c transaction
// Must be defined by arch, board, or ec // Must be defined by arch, board, or ec
int i2c_start(uint8_t addr, bool read); int i2c_start(struct I2C * i2c, uint8_t addr, bool read);
// Stop i2c transaction // Stop i2c transaction
// Must be defined by arch, board, or ec // Must be defined by arch, board, or ec
void i2c_stop(void); void i2c_stop(struct I2C * i2c);
// Send a byte on i2c bus // Send a byte on i2c bus
// Must be defined by arch, board, or ec // Must be defined by arch, board, or ec
int i2c_write(uint8_t * data, int length); int i2c_write(struct I2C * i2c, uint8_t * data, int length);
// Read bytes from bus // Read bytes from bus
// Must be defined by arch, board, or ec // Must be defined by arch, board, or ec
int i2c_read(uint8_t * data, int length); int i2c_read(struct I2C * i2c, uint8_t * data, int length);
// Read multiple bytes from address in one transaction // Read multiple bytes from address in one transaction
int i2c_recv(uint8_t addr, uint8_t* data, int length); int i2c_recv(struct I2C * i2c, uint8_t addr, uint8_t* data, int length);
// Write multiple bytes to address in one transaction // Write multiple bytes to address in one transaction
int i2c_send(uint8_t addr, uint8_t* data, int length); int i2c_send(struct I2C * i2c, uint8_t addr, uint8_t* data, int length);
// Read multiple bytes from a register in one transaction // Read multiple bytes from a register in one transaction
int i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, int length); int i2c_get(struct I2C * i2c, uint8_t addr, uint8_t reg, uint8_t* data, int length);
// Write multiple bytes to a register in one transaction // Write multiple bytes to a register in one transaction
int i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, int length); int i2c_set(struct I2C * i2c, uint8_t addr, uint8_t reg, uint8_t* data, int length);
#endif // _COMMON_I2C_H #endif // _COMMON_I2C_H

View File

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

View File

@ -3,6 +3,8 @@
#include <common/i2c.h> #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 #endif // _EC_I2C_H

View File

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

View File

@ -3,6 +3,8 @@
#include <common/i2c.h> #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 #endif // _EC_I2C_H