Move i2c support to common

This commit is contained in:
Jeremy Soller 2019-11-13 14:33:04 -07:00
parent 894e448dfc
commit 890d8e9968
No known key found for this signature in database
GPG Key ID: E988B49EE78A7FB1
9 changed files with 215 additions and 152 deletions

View File

@ -2,15 +2,12 @@
#include <avr/io.h>
#include <util/twi.h>
#include <arch/i2c.h>
#include <board/cpu.h>
#include <common/i2c.h>
#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<<TWINT) | (1<<TWEN);
// wait for end of transmission
@ -53,7 +50,7 @@ uint8_t i2c_start(uint8_t addr, uint8_t rw) {
return 0;
}
void i2c_stop() {
void i2c_stop(void) {
// transmit STOP condition
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWSTO);
}
@ -81,83 +78,16 @@ uint8_t i2c_write(uint8_t data) {
return 0;
}
uint8_t i2c_read_ack() {
// start TWI module and acknowledge data after reception
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
uint8_t i2c_read(bool ack) {
if (ack) {
// start TWI module and acknowledge data after reception
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
} else {
// start receiving without acknowledging reception
TWCR = (1<<TWINT) | (1<<TWEN);
}
// wait for end of transmission
while( !(TWCR & (1<<TWINT)) );
// return received data from TWDR
return TWDR;
}
uint8_t i2c_read_nack() {
// start receiving without acknowledging reception
TWCR = (1<<TWINT) | (1<<TWEN);
// wait for end of transmission
while( !(TWCR & (1<<TWINT)) );
// return received data from TWDR
return TWDR;
}
uint8_t i2c_recv(uint8_t addr, uint8_t* data, uint16_t length) {
if (i2c_start(addr, I2C_READ)) return 1;
uint16_t i;
for (i = 0; i < (length-1); i++)
{
data[i] = i2c_read_ack();
}
data[(length-1)] = i2c_read_nack();
i2c_stop();
return 0;
}
uint8_t i2c_send(uint8_t addr, uint8_t* data, uint16_t length) {
if (i2c_start(addr, I2C_WRITE)) return 1;
uint16_t i;
for (i = 0; i < length; i++) {
if (i2c_write(data[i])) return 1;
}
i2c_stop();
return 0;
}
uint8_t i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, uint16_t length) {
if (i2c_start(addr, I2C_WRITE)) return 1;
if (i2c_write(reg)) return 1;
if (i2c_start(addr, I2C_READ)) return 1;
uint16_t i;
for (i = 0; i < (length-1); i++)
{
data[i] = i2c_read_ack();
}
data[(length-1)] = i2c_read_nack();
i2c_stop();
return 0;
}
uint8_t i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, uint16_t length) {
if (i2c_start(addr, I2C_WRITE)) return 1;
if(i2c_write(reg)) return 1;
uint16_t i;
for (i = 0; i < length; i++)
{
if (i2c_write(data[i])) return 1;
}
i2c_stop();
return 0;
}

View File

@ -1,10 +0,0 @@
#ifndef _ARCH_I2C_H
#define _ARCH_I2C_H
uint8_t i2c_recv(uint8_t addr, uint8_t* data, uint16_t length);
uint8_t i2c_send(uint8_t addr, uint8_t* data, uint16_t length);
uint8_t i2c_get(uint8_t addr, uint8_t reg, uint8_t* data, uint16_t length);
uint8_t i2c_set(uint8_t addr, uint8_t reg, uint8_t* data, uint16_t length);
#endif // _ARCH_I2C_H

View File

@ -1,8 +1,6 @@
#ifndef _BOARD_I2C_H
#define _BOARD_I2C_H
#include <arch/i2c.h>
void i2c_init(unsigned long baud);
#endif // _BOARD_I2C_H

View File

@ -1,4 +1,5 @@
#include <board/smbus.h>
#include <ec/i2c.h>
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);
}

View File

@ -1,9 +1,9 @@
#include <stdio.h>
#include <ec/i2c.h>
#include <common/i2c.h>
int putchar(int c) {
unsigned char byte = (unsigned char)c;
i2c_write(0x76, &byte, 1);
i2c_send(0x76, &byte, 1);
return (int)byte;
}

53
src/common/i2c.c Normal file
View File

@ -0,0 +1,53 @@
#include <common/i2c.h>
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(&reg, 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(&reg, 1);
if (res < 0) return res;
return i2c_send(addr, data, length);
}

View File

@ -0,0 +1,35 @@
#ifndef _COMMON_I2C_H
#define _COMMON_I2C_H
#include <stdbool.h>
#include <stdint.h>
// 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

View File

@ -1,7 +1,10 @@
#include <stdbool.h>
#include <board/cpu.h>
#include <ec/i2c.h>
#include <common/i2c.h>
#include <ec/smbus.h>
//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);
}

View File

@ -1,10 +1,8 @@
#ifndef _EC_I2C_H
#define _EC_I2C_H
#include <stdint.h>
#include <common/i2c.h>
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