1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/atxmega/periph/i2c.c
Gerson Fernando Budke 549e2b4de1
cpu/atxmega: Fix PM states on peripherals
Fix the required PM state on i2c and spi peripherals.

Signed-off-by: Gerson Fernando Budke <nandojve@gmail.com>
2023-12-01 14:12:23 +01:00

289 lines
7.2 KiB
C

/*
* Copyright (C) 2021-2023 Gerson Fernando Budke <nandojve@gmail.com>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atxmega
* @ingroup cpu_atxmega_periph
* @{
*
* @file
* @brief Low-level I2C driver implementation
*
* @author Gerson Fernando Budke <nandojve@gmail.com>
*
* @}
*/
#include <assert.h>
#include <errno.h>
#include "cpu.h"
#include "cpu_pm.h"
#include "irq.h"
#include "periph/i2c.h"
#include "periph/gpio.h"
#include "periph/pm.h"
#define ENABLE_DEBUG 0
#include "debug.h"
/**
* @brief Device Context allocation
*/
static struct {
mutex_t locks;
mutex_t xfer;
uint8_t *buffer;
size_t pos;
size_t len;
uint8_t flags;
int8_t status;
} i2c_ctx[I2C_NUMOF];
/**
* @brief Get the pointer to the base register of the given I2C device
*
* @param[in] dev I2C device identifier
*
* @return base register address
*/
static inline TWI_t* dev(i2c_t dev)
{
return ((TWI_t*) (i2c_config[dev].dev));
}
static inline uint8_t _i2c_calc_baud(i2c_t i2c)
{
uint16_t ftwi = (((CLOCK_CORECLOCK / (2*i2c_config[i2c].speed))-5)+1);
if (ftwi > 255) {
DEBUG("[i2c] init: dev speed is lower than minimal speed supported.\n");
return 0;
}
if (ftwi < 1) {
DEBUG("[i2c] init: dev speed is high than maximum speed supported.\n");
return 0;
}
return ftwi & 0xff;
}
void i2c_init(i2c_t i2c)
{
uint8_t baudrate;
assert((unsigned)i2c < I2C_NUMOF);
baudrate = _i2c_calc_baud(i2c);
if (baudrate == 0) {
DEBUG("[i2c] init: baudrate is invalid.\n");
return;
}
mutex_init(&i2c_ctx[i2c].locks);
mutex_init(&i2c_ctx[i2c].xfer);
mutex_lock(&i2c_ctx[i2c].xfer);
i2c_init_pins(i2c);
pm_periph_enable(i2c_config[i2c].pwr);
dev(i2c)->MASTER.BAUD = baudrate;
pm_periph_disable(i2c_config[i2c].pwr);
}
void i2c_init_pins(i2c_t i2c)
{
assert((unsigned)i2c < I2C_NUMOF);
gpio_init(i2c_config[i2c].sda_pin, GPIO_OPC_WRD_AND_PULL);
gpio_init(i2c_config[i2c].scl_pin, GPIO_OPC_WRD_AND_PULL);
}
void i2c_acquire(i2c_t i2c)
{
assert((unsigned)i2c < I2C_NUMOF);
DEBUG("acquire\n");
pm_block(4); /* Require clkPer */
mutex_lock(&i2c_ctx[i2c].locks);
pm_periph_enable(i2c_config[i2c].pwr);
dev(i2c)->MASTER.CTRLA = (i2c_config[i2c].int_lvl << TWI_MASTER_INTLVL_gp)
| TWI_MASTER_RIEN_bm
| TWI_MASTER_WIEN_bm
| TWI_MASTER_ENABLE_bm;
dev(i2c)->MASTER.STATUS = TWI_MASTER_BUSSTATE_IDLE_gc;
}
void i2c_release(i2c_t i2c)
{
assert((unsigned)i2c < I2C_NUMOF);
dev(i2c)->MASTER.CTRLA = 0;
pm_periph_disable(i2c_config[i2c].pwr);
mutex_unlock(&i2c_ctx[i2c].locks);
pm_unblock(4);
DEBUG("release\n");
}
static int _i2c_transaction(i2c_t i2c, uint16_t addr, const void *data,
size_t len, uint8_t flags, bool is_read)
{
assert((unsigned)i2c < I2C_NUMOF);
if (flags & I2C_ADDR10) {
DEBUG("[i2c] xfer: no 10 bit address support.\n");
return -EOPNOTSUPP;
}
if (data == NULL || len == 0) {
DEBUG("[i2c] xfer: invalid parameter.\n");
return -EINVAL;
}
i2c_ctx[i2c].buffer = (uint8_t *) data;
i2c_ctx[i2c].pos = 0;
i2c_ctx[i2c].len = len;
i2c_ctx[i2c].flags = flags;
dev(i2c)->MASTER.ADDR = (addr << 1) | (is_read ? 0x01 : 0x00);
mutex_lock(&i2c_ctx[i2c].xfer);
DEBUG("[i2c] xfer: status = %d\n", i2c_ctx[i2c].status);
return i2c_ctx[i2c].status;
}
int i2c_write_bytes(i2c_t i2c, uint16_t addr, const void *data, size_t len,
uint8_t flags)
{
return _i2c_transaction(i2c, addr, data, len, flags, false);
}
int i2c_read_bytes(i2c_t i2c, uint16_t addr, void *data, size_t len,
uint8_t flags)
{
return _i2c_transaction(i2c, addr, data, len, flags, true);
}
/**
* @internal
*
* @brief TWI master write interrupt handler.
*
* Handles TWI transactions (master write) and responses to (N)ACK.
*/
static inline void _i2c_write_handler(int i2c)
{
if (i2c_ctx[i2c].pos < i2c_ctx[i2c].len) {
const uint8_t* const data = i2c_ctx[i2c].buffer;
dev(i2c)->MASTER.DATA = data[i2c_ctx[i2c].pos++];
}
else {
/* Send STOP condition to complete the transaction. */
if (!(i2c_ctx[i2c].flags & I2C_NOSTOP)) {
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
}
i2c_ctx[i2c].status = 0;
mutex_unlock(&i2c_ctx[i2c].xfer);
}
}
/**
* @internal
*
* @brief TWI master read interrupt handler.
*
* This is the master read interrupt handler that takes care of reading bytes
* from the TWI slave device.
*/
static inline void _i2c_read_handler(int i2c)
{
if (i2c_ctx[i2c].pos < i2c_ctx[i2c].len) {
uint8_t* const data = i2c_ctx[i2c].buffer;
data[i2c_ctx[i2c].pos++] = dev(i2c)->MASTER.DATA;
/* If there is more to read, issue ACK and start a byte read.
* Otherwise, issue NACK and STOP to complete the transaction.
*/
if (i2c_ctx[i2c].pos < i2c_ctx[i2c].len) {
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_RECVTRANS_gc;
}
else {
uint8_t control_c = TWI_MASTER_ACKACT_bm;
if (!(i2c_ctx[i2c].flags & I2C_NOSTOP)) {
control_c |= TWI_MASTER_CMD_STOP_gc;
}
dev(i2c)->MASTER.CTRLC = control_c;
i2c_ctx[i2c].status = 0;
mutex_unlock(&i2c_ctx[i2c].xfer);
}
} else {
/* Issue STOP and buffer overflow condition. */
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
i2c_ctx[i2c].status = -ENOMEM;
mutex_unlock(&i2c_ctx[i2c].xfer);
}
}
/**
* @internal
*
* @brief Common TWI master interrupt service routine.
*
* Check current status and calls the appropriate handler.
*/
static inline void isr_handler(int i2c)
{
assert((unsigned)i2c < I2C_NUMOF);
int8_t const m_status = dev(i2c)->MASTER.STATUS;
if (m_status & TWI_MASTER_ARBLOST_bm) {
dev(i2c)->MASTER.STATUS = m_status | TWI_MASTER_ARBLOST_bm;
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
i2c_ctx[i2c].status = -EBUSY;
mutex_unlock(&i2c_ctx[i2c].xfer);
}
else if ((m_status & TWI_MASTER_BUSERR_bm)
|| (m_status & TWI_MASTER_RXACK_bm)) {
dev(i2c)->MASTER.CTRLC = TWI_MASTER_CMD_STOP_gc;
i2c_ctx[i2c].status = ((m_status & TWI_MASTER_BUSERR_bm) > 0)
? -EIO
: -ENXIO;
mutex_unlock(&i2c_ctx[i2c].xfer);
}
else if (m_status & TWI_MASTER_WIF_bm) {
_i2c_write_handler(i2c);
}
else if (m_status & TWI_MASTER_RIF_bm) {
_i2c_read_handler(i2c);
}
else {
i2c_ctx[i2c].status = -EPROTO;
mutex_unlock(&i2c_ctx[i2c].xfer);
}
}
#ifdef I2C_0_ISR
AVR8_ISR(I2C_0_ISR, isr_handler, 0);
#endif /* I2C_0_ISR */
#ifdef I2C_1_ISR
AVR8_ISR(I2C_1_ISR, isr_handler, 1);
#endif /* I2C_1_ISR */
#ifdef I2C_2_ISR
AVR8_ISR(I2C_2_ISR, isr_handler, 2);
#endif /* I2C_2_ISR */
#ifdef I2C_3_ISR
AVR8_ISR(I2C_3_ISR, isr_handler, 3);
#endif /* I2C_3_ISR */