1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/cc26xx_cc13xx/periph/i2c.c
Gunar Schorcht 007e29ebb5 cpu/periph/i2c: update implementations to new I2C API
Make all `spi_acquire` implementations return `void` and add assertions to check for valid device identifier where missing.
2021-11-29 06:35:25 +01:00

278 lines
7.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2018 Kaspar Schleiser <kaspar@schleiser.de>
*
* 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_cc26xx_cc13xx
* @ingroup drivers_periph_i2c
* @{
*
* @file
* @brief Low-level I2C driver implementation
* @note This CPU has weak pullups, external pullup resistors may be
* required.
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include "mutex.h"
#include "cpu.h"
#include "periph/i2c.h"
#include "cc26xx_cc13xx_power.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#define PREG(x) DEBUG("%s=0x%08x\n", #x, (unsigned)x);
/**
* @brief Mutex lock for the only available I2C periph
* @note If multiple I2C devices are added locks must be an array for each one.
*/
static mutex_t _lock;
static int _check_errors(void)
{
int ret = 0;
/* The reference manual (SWCU117H) is ambiguous on how to wait:
*
* 1. 21.4 8. says "wait until BUSBUSY is cleared"
* 2. command flow diagrams (e.g., 21.3.5.1) indicate to wait while
* BUSY is set
*
* (3. 21.5.1.10 says BUSY is only valid after 4 SYSBUS clock cycles)
*
* Waiting first for cleared IDLE and then for cleared BUSY works fine.
*/
/* wait for transfer to be complete, this also could be a few nops... */
while (I2C->MSTAT & MSTAT_IDLE) {}
while (I2C->MSTAT & MSTAT_BUSY) {}
/* check if there was an error */
if (I2C->MSTAT & MSTAT_ERR) {
DEBUG("%s\n", __FUNCTION__);
PREG(I2C->MSTAT);
ret = -ETIMEDOUT;
if (I2C->MSTAT & MSTAT_ADRACK_N) {
DEBUG("ADDRESS NACK\n");
return -ENXIO;
}
else if (I2C->MSTAT & MSTAT_DATACK_N) {
DEBUG("DATA NACK\n");
ret = -EIO;
}
else if (I2C->MSTAT & MSTAT_ARBLST) {
DEBUG("ARBITRATION LOSS\n");
ret = -EAGAIN;
}
/*
* If a non-NACK error occurs we must reinit or lock up.
* dev = 0 since it is the only one, if more are added it should be
* the dev num, this is done to avoid passing in arguments and
* increasing code size.
*/
i2c_init(0);
return ret;
}
return ret;
}
void i2c_init(i2c_t devnum)
{
(void)devnum;
assert(devnum < I2C_NUMOF);
/* Make sure everything is shut off in case of reinit */
I2C->MCR = 0;
/* Enable serial power domain */
if (!power_is_domain_enabled(POWER_DOMAIN_SERIAL)) {
power_enable_domain(POWER_DOMAIN_SERIAL);
}
/* enable I2C clock in run mode */
power_clock_enable_i2c();
/* configure pins */
IOC->CFG[I2C_SDA_PIN] = (IOCFG_PORTID_I2C_MSSDA
| IOCFG_INPUT_ENABLE
| IOCFG_IOMODE_OPEN_DRAIN
| IOCFG_PULLCTL_UP);
IOC->CFG[I2C_SCL_PIN] = (IOCFG_PORTID_I2C_MSSCL
| IOCFG_INPUT_ENABLE
| IOCFG_IOMODE_OPEN_DRAIN
| IOCFG_PULLCTL_UP);
/* initialize I2C master */
I2C->MCR = MCR_MFE;
/* configure clock speed
* {PERDMACLK / [2 × (SCL_LP + SCL_HP) × SCL_CLK]} 1
* with SCL_LP==6 && SCL_HP==4 use 0x17 for 100kHZ with 48MHZ CPU clock */
I2C->MTPR = MTPR_TPR_100KHZ;
}
void i2c_acquire(i2c_t dev)
{
(void)dev;
assert(dev < I2C_NUMOF);
mutex_lock(&_lock);
}
void i2c_release(i2c_t dev)
{
(void)dev;
assert(dev < I2C_NUMOF);
mutex_unlock(&_lock);
}
int i2c_read_bytes(i2c_t dev, uint16_t addr,
void *data, size_t len, uint8_t flags)
{
(void)dev;
int ret = 0;
char *bufpos = data;
DEBUG("%s %u\n", __FUNCTION__, len);
PREG(I2C->MSTAT);
assert(dev < I2C_NUMOF);
assert(data != NULL);
/* Check for unsupported operations */
if (flags & I2C_ADDR10) {
return -EOPNOTSUPP;
}
/* Check for wrong arguments given */
if (len == 0) {
return -EINVAL;
}
if (!(I2C->MSTAT & MSTAT_BUSBSY) && (flags & I2C_NOSTART)) {
return -EINVAL;
}
/* Sequence may be omitted in a single master system */
while (I2C->MSTAT & MSTAT_BUSY) {}
I2C->MSA = ((uint32_t)addr << 1) | MSA_RS;
while (len--) {
DEBUG("LOOP %u\n", len);
/* setup transfer */
uint32_t mctrl = MCTRL_RUN;
if (!(flags & I2C_NOSTART)) {
DEBUG("START\n");
mctrl |= MCTRL_START;
/* make note not to generate START from second byte onwards */
flags |= I2C_NOSTART;
}
/* after last byte, generate STOP unless told not to */
if (!len && !(flags & I2C_NOSTOP)) {
DEBUG("STOP\n");
mctrl |= MCTRL_STOP;
}
else {
DEBUG("ACK\n");
mctrl |= MCTRL_ACK;
}
while (I2C->MSTAT & MSTAT_BUSY) {}
/* initiate transfer */
I2C->MCTRL = mctrl;
/* check if there was an error */
ret = _check_errors();
if (ret != 0) {
return ret;
}
/* copy next byte from I2C data register */
DEBUG("IN=0x%02x\n", (unsigned)I2C->MDR);
*bufpos++ = I2C->MDR;
}
return ret;
}
int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len,
uint8_t flags)
{
(void)dev;
int ret = 0;
const unsigned char *bufpos = data;
DEBUG("%s %u\n", __FUNCTION__, len);
PREG(I2C->MSTAT);
assert(dev < I2C_NUMOF);
assert(data != NULL);
/* Check for unsupported operations */
if (flags & I2C_ADDR10) {
return -EOPNOTSUPP;
}
/* Check for wrong arguments given */
if (len == 0) {
return -EINVAL;
}
if (!(I2C->MSTAT & MSTAT_BUSBSY) && (flags & I2C_NOSTART)) {
return -EINVAL;
}
/* Since write is 0 we just need shift the address in */
I2C->MSA = (uint32_t)addr << 1;
/* Sequence may be omitted in a single master system. */
while (I2C->MSTAT & MSTAT_BUSY) {}
while (len--) {
DEBUG("LOOP %u 0x%2x\n", len, (unsigned)*bufpos);
/* copy next byte into I2C data register */
I2C->MDR = *bufpos++;
/* setup transfer */
uint32_t mctrl = MCTRL_RUN;
if (!(flags & I2C_NOSTART)) {
DEBUG("START\n");
mctrl |= MCTRL_START;
/* make note not to generate START from second byte onwards */
flags |= I2C_NOSTART;
}
/* after last byte, generate STOP unless told not to */
if (!len && !(flags & I2C_NOSTOP)) {
DEBUG("STOP\n");
mctrl |= MCTRL_STOP;
}
/* initiate transfer */
I2C->MCTRL = mctrl;
/* check if there was an error */
ret = _check_errors();
if (ret != 0) {
return ret;
}
}
return ret;
}