2017-09-01 15:33:10 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2017 HAW Hamburg
|
2018-01-19 22:09:41 +01:00
|
|
|
* 2018 Freie Universität Berlin
|
2018-06-05 21:52:18 +02:00
|
|
|
* 2018 Mesotic SAS
|
2017-09-01 15:33:10 +02:00
|
|
|
*
|
2018-01-19 22:09:41 +01:00
|
|
|
* 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.
|
2017-09-01 15:33:10 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2021-09-05 21:47:38 +02:00
|
|
|
* @ingroup cpu_nrf5x_common
|
2017-09-01 15:33:10 +02:00
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file
|
2018-01-19 22:09:41 +01:00
|
|
|
* @brief Low-level I2C (TWI) peripheral driver implementation
|
2017-09-01 15:33:10 +02:00
|
|
|
*
|
|
|
|
* @author Dimitri Nahm <dimitri.nahm@haw-hamburg.de>
|
2018-01-19 22:09:41 +01:00
|
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
2018-06-05 21:52:18 +02:00
|
|
|
* @author Dylan Laduranty <dylan.laduranty@mesotic.com>
|
2017-09-01 15:33:10 +02:00
|
|
|
*
|
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
2019-08-22 11:55:14 +02:00
|
|
|
#include <assert.h>
|
2018-01-19 22:09:41 +01:00
|
|
|
#include <string.h>
|
2018-06-05 21:52:18 +02:00
|
|
|
#include <errno.h>
|
2018-01-19 22:09:41 +01:00
|
|
|
|
2017-09-01 15:33:10 +02:00
|
|
|
#include "cpu.h"
|
|
|
|
#include "mutex.h"
|
|
|
|
#include "assert.h"
|
|
|
|
#include "periph/i2c.h"
|
2018-01-19 22:09:41 +01:00
|
|
|
#include "periph/gpio.h"
|
2021-08-06 11:06:59 +02:00
|
|
|
#include "byteorder.h"
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2020-10-22 11:34:00 +02:00
|
|
|
#define ENABLE_DEBUG 0
|
2017-09-01 15:33:10 +02:00
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
/**
|
2018-01-19 22:09:41 +01:00
|
|
|
* @brief If any of the 8 lower bits are set, the speed value is invalid
|
2017-09-01 15:33:10 +02:00
|
|
|
*/
|
2018-01-19 22:09:41 +01:00
|
|
|
#define INVALID_SPEED_MASK (0xff)
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2021-08-06 11:06:59 +02:00
|
|
|
/**
|
|
|
|
* @brief Allocate a tx buffer
|
|
|
|
*/
|
|
|
|
static uint8_t tx_buf[256];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Mutex for locking the TX buffer
|
|
|
|
*/
|
|
|
|
static mutex_t buffer_lock;
|
|
|
|
|
2018-01-30 19:07:26 +01:00
|
|
|
/**
|
2018-06-05 21:52:18 +02:00
|
|
|
* @brief Initialized dev locks (we have a maximum of two devices...)
|
2018-01-30 19:07:26 +01:00
|
|
|
*/
|
2018-06-05 21:52:18 +02:00
|
|
|
static mutex_t locks[I2C_NUMOF];
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2020-05-29 09:37:42 +02:00
|
|
|
/**
|
|
|
|
* @brief array with a busy mutex for each I2C device, used to block the
|
|
|
|
* thread until the transfer is done
|
|
|
|
*/
|
|
|
|
static mutex_t busy[I2C_NUMOF];
|
|
|
|
|
|
|
|
void i2c_isr_handler(void *arg);
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
static inline NRF_TWIM_Type *bus(i2c_t dev)
|
2017-09-01 15:33:10 +02:00
|
|
|
{
|
2019-10-04 08:42:35 +02:00
|
|
|
return i2c_config[dev].dev;
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
static int finish(i2c_t dev)
|
2017-09-01 15:33:10 +02:00
|
|
|
{
|
2018-01-19 22:09:41 +01:00
|
|
|
DEBUG("[i2c] waiting for STOPPED or ERROR event\n");
|
2020-05-29 09:37:42 +02:00
|
|
|
/* Unmask interrupts */
|
|
|
|
bus(dev)->INTENSET = TWIM_INTEN_STOPPED_Msk | TWIM_INTEN_ERROR_Msk;
|
|
|
|
mutex_lock(&busy[dev]);
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
if ((bus(dev)->EVENTS_STOPPED)) {
|
|
|
|
bus(dev)->EVENTS_STOPPED = 0;
|
2018-01-19 22:09:41 +01:00
|
|
|
DEBUG("[i2c] finish: stop event occurred\n");
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
if (bus(dev)->EVENTS_ERROR) {
|
|
|
|
bus(dev)->EVENTS_ERROR = 0;
|
|
|
|
if (bus(dev)->ERRORSRC & TWIM_ERRORSRC_ANACK_Msk) {
|
|
|
|
bus(dev)->ERRORSRC = TWIM_ERRORSRC_ANACK_Msk;
|
2018-01-19 22:09:41 +01:00
|
|
|
DEBUG("[i2c] check_error: NACK on address byte\n");
|
2018-06-05 21:52:18 +02:00
|
|
|
return -ENXIO;
|
2018-01-19 22:09:41 +01:00
|
|
|
}
|
2018-06-05 21:52:18 +02:00
|
|
|
if (bus(dev)->ERRORSRC & TWIM_ERRORSRC_DNACK_Msk) {
|
|
|
|
bus(dev)->ERRORSRC = TWIM_ERRORSRC_DNACK_Msk;
|
2018-01-19 22:09:41 +01:00
|
|
|
DEBUG("[i2c] check_error: NACK on data byte\n");
|
2018-06-05 21:52:18 +02:00
|
|
|
return -EIO;
|
2018-01-19 22:09:41 +01:00
|
|
|
}
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
return 0;
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
|
|
|
|
2020-11-30 16:35:50 +01:00
|
|
|
static void _init_pins(i2c_t dev)
|
|
|
|
{
|
|
|
|
gpio_init(i2c_config[dev].scl, GPIO_IN_OD_PU);
|
|
|
|
gpio_init(i2c_config[dev].sda, GPIO_IN_OD_PU);
|
|
|
|
bus(dev)->PSEL.SCL = i2c_config[dev].scl;
|
|
|
|
bus(dev)->PSEL.SDA = i2c_config[dev].sda;
|
|
|
|
}
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
void i2c_init(i2c_t dev)
|
2017-09-01 15:33:10 +02:00
|
|
|
{
|
2018-06-05 21:52:18 +02:00
|
|
|
assert(dev < I2C_NUMOF);
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
/* Initialize mutex */
|
|
|
|
mutex_init(&locks[dev]);
|
2020-05-29 09:37:42 +02:00
|
|
|
mutex_init(&busy[dev]);
|
|
|
|
mutex_lock(&busy[dev]);
|
|
|
|
|
2018-01-19 22:09:41 +01:00
|
|
|
/* disable device during initialization, will be enabled when acquire is
|
|
|
|
* called */
|
2018-06-05 21:52:18 +02:00
|
|
|
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
|
2020-11-30 16:35:50 +01:00
|
|
|
|
2018-01-19 22:09:41 +01:00
|
|
|
/* configure pins */
|
2020-11-30 16:35:50 +01:00
|
|
|
_init_pins(dev);
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
/* configure dev clock speed */
|
|
|
|
bus(dev)->FREQUENCY = i2c_config[dev].speed;
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2021-11-12 20:44:32 +01:00
|
|
|
spi_twi_irq_register_i2c(bus(dev), i2c_isr_handler, (void *)(uintptr_t)dev);
|
2020-05-29 09:37:42 +02:00
|
|
|
|
2020-11-30 16:35:50 +01:00
|
|
|
/* We expect that the device was being acquired before
|
2018-01-19 22:09:41 +01:00
|
|
|
* the i2c_init_master() function is called, so it should be enabled when
|
|
|
|
* exiting this function. */
|
2018-06-05 21:52:18 +02:00
|
|
|
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
|
|
|
|
2020-11-30 16:35:50 +01:00
|
|
|
#ifdef MODULE_PERIPH_I2C_RECONFIGURE
|
|
|
|
void i2c_init_pins(i2c_t dev)
|
|
|
|
{
|
|
|
|
assert(dev < I2C_NUMOF);
|
|
|
|
|
|
|
|
_init_pins(dev);
|
|
|
|
|
|
|
|
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
|
|
|
|
|
|
|
|
mutex_unlock(&locks[dev]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void i2c_deinit_pins(i2c_t dev)
|
|
|
|
{
|
|
|
|
assert(dev < I2C_NUMOF);
|
|
|
|
|
|
|
|
mutex_lock(&locks[dev]);
|
|
|
|
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
|
|
|
|
}
|
|
|
|
#endif /* MODULE_PERIPH_I2C_RECONFIGURE */
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
int i2c_acquire(i2c_t dev)
|
2017-09-01 15:33:10 +02:00
|
|
|
{
|
2018-06-05 21:52:18 +02:00
|
|
|
assert(dev < I2C_NUMOF);
|
2018-01-19 22:09:41 +01:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
mutex_lock(&locks[dev]);
|
|
|
|
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Enabled;
|
2018-01-19 22:09:41 +01:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
DEBUG("[i2c] acquired dev %i\n", (int)dev);
|
2017-09-01 15:33:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-08-22 11:55:14 +02:00
|
|
|
void i2c_release(i2c_t dev)
|
2017-09-01 15:33:10 +02:00
|
|
|
{
|
2018-06-05 21:52:18 +02:00
|
|
|
assert(dev < I2C_NUMOF);
|
2018-01-19 22:09:41 +01:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
bus(dev)->ENABLE = TWIM_ENABLE_ENABLE_Disabled;
|
|
|
|
mutex_unlock(&locks[dev]);
|
2018-01-19 22:09:41 +01:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
DEBUG("[i2c] released dev %i\n", (int)dev);
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
int i2c_write_regs(i2c_t dev, uint16_t addr, uint16_t reg,
|
|
|
|
const void *data, size_t len, uint8_t flags)
|
2017-09-01 15:33:10 +02:00
|
|
|
{
|
2021-08-06 11:06:59 +02:00
|
|
|
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 253));
|
2018-06-05 21:52:18 +02:00
|
|
|
|
2021-08-06 11:06:59 +02:00
|
|
|
if (flags & (I2C_NOSTART | I2C_ADDR10)) {
|
2018-06-05 21:52:18 +02:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2021-08-06 11:06:59 +02:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
/* the nrf52's TWI device does not support to do two consecutive transfers
|
|
|
|
* without a repeated start condition in between. So we have to put all data
|
2021-08-06 11:06:59 +02:00
|
|
|
* to be transferred into a buffer (tx_buf).
|
|
|
|
* */
|
|
|
|
|
|
|
|
uint8_t reg_addr_len; /* Length in bytes of the register address */
|
|
|
|
|
|
|
|
/* Lock tx_buf */
|
|
|
|
mutex_lock(&buffer_lock);
|
|
|
|
|
|
|
|
if (flags & (I2C_REG16)) {
|
|
|
|
reg_addr_len = 2;
|
|
|
|
/* Prepare the 16-bit register transfer */
|
|
|
|
tx_buf[0] = reg >> 8; /* AddrH in the first byte */
|
|
|
|
tx_buf[1] = reg & 0xFF; /* AddrL in the second byte */
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
reg_addr_len = 1;
|
|
|
|
tx_buf[0] = reg;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(&tx_buf[reg_addr_len], data, len);
|
|
|
|
int ret = i2c_write_bytes(dev, addr, tx_buf, reg_addr_len + len, flags);
|
|
|
|
|
|
|
|
/* Release tx_buf */
|
|
|
|
mutex_unlock(&buffer_lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len,
|
|
|
|
uint8_t flags)
|
2017-09-01 15:33:10 +02:00
|
|
|
{
|
2018-06-05 21:52:18 +02:00
|
|
|
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 256));
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2021-08-06 11:06:59 +02:00
|
|
|
if (flags & (I2C_NOSTART | I2C_ADDR10)) {
|
2018-06-05 21:52:18 +02:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2018-01-19 22:09:41 +01:00
|
|
|
DEBUG("[i2c] read_bytes: %i bytes from addr 0x%02x\n", (int)len, (int)addr);
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
bus(dev)->ADDRESS = addr;
|
|
|
|
bus(dev)->RXD.PTR = (uint32_t)data;
|
|
|
|
bus(dev)->RXD.MAXCNT = (uint8_t)len;
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
if (!(flags & I2C_NOSTOP)) {
|
|
|
|
bus(dev)->SHORTS = TWIM_SHORTS_LASTRX_STOP_Msk;
|
|
|
|
}
|
|
|
|
/* Start transmission */
|
|
|
|
bus(dev)->TASKS_STARTRX = 1;
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
return finish(dev);
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
int i2c_read_regs(i2c_t dev, uint16_t addr, uint16_t reg,
|
|
|
|
void *data, size_t len, uint8_t flags)
|
2017-09-01 15:33:10 +02:00
|
|
|
{
|
2018-06-05 21:52:18 +02:00
|
|
|
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 256));
|
2018-01-19 22:09:41 +01:00
|
|
|
|
2021-08-06 11:06:59 +02:00
|
|
|
if (flags & (I2C_NOSTART | I2C_ADDR10)) {
|
2018-06-05 21:52:18 +02:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2021-08-06 11:06:59 +02:00
|
|
|
|
2018-01-19 22:09:41 +01:00
|
|
|
DEBUG("[i2c] read_regs: %i byte(s) from reg 0x%02x at addr 0x%02x\n",
|
|
|
|
(int)len, (int)reg, (int)addr);
|
|
|
|
|
2021-08-06 11:06:59 +02:00
|
|
|
/* Prepare transfer */
|
2018-06-05 21:52:18 +02:00
|
|
|
bus(dev)->ADDRESS = addr;
|
2021-08-06 11:06:59 +02:00
|
|
|
if (flags & (I2C_REG16)) {
|
|
|
|
/* Register endianness for 16 bit */
|
|
|
|
reg = htons(reg);
|
|
|
|
bus(dev)->TXD.MAXCNT = 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
bus(dev)->TXD.MAXCNT = 1;
|
|
|
|
}
|
2018-06-05 21:52:18 +02:00
|
|
|
bus(dev)->TXD.PTR = (uint32_t)®
|
|
|
|
bus(dev)->RXD.PTR = (uint32_t)data;
|
|
|
|
bus(dev)->RXD.MAXCNT = (uint8_t)len;
|
|
|
|
bus(dev)->SHORTS = (TWIM_SHORTS_LASTTX_STARTRX_Msk);
|
|
|
|
if (!(flags & I2C_NOSTOP)) {
|
|
|
|
bus(dev)->SHORTS |= TWIM_SHORTS_LASTRX_STOP_Msk;
|
|
|
|
}
|
2021-08-06 11:06:59 +02:00
|
|
|
|
|
|
|
/* Start transfer */
|
2018-06-05 21:52:18 +02:00
|
|
|
bus(dev)->TASKS_STARTTX = 1;
|
2017-09-01 15:33:10 +02:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
return finish(dev);
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len,
|
|
|
|
uint8_t flags)
|
2017-09-01 15:33:10 +02:00
|
|
|
{
|
2018-06-05 21:52:18 +02:00
|
|
|
assert((dev < I2C_NUMOF) && data && (len > 0) && (len < 256));
|
2018-01-19 22:09:41 +01:00
|
|
|
|
2021-08-06 11:06:59 +02:00
|
|
|
if (flags & (I2C_NOSTART | I2C_ADDR10)) {
|
2018-06-05 21:52:18 +02:00
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
2018-01-19 22:09:41 +01:00
|
|
|
DEBUG("[i2c] write_bytes: %i byte(s) to addr 0x%02x\n", (int)len, (int)addr);
|
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
bus(dev)->ADDRESS = addr;
|
|
|
|
bus(dev)->TXD.PTR = (uint32_t)data;
|
|
|
|
bus(dev)->TXD.MAXCNT = (uint8_t)len;
|
|
|
|
if (!(flags & I2C_NOSTOP)) {
|
|
|
|
bus(dev)->SHORTS = TWIM_SHORTS_LASTTX_STOP_Msk;
|
|
|
|
}
|
|
|
|
bus(dev)->TASKS_STARTTX = 1;
|
2018-01-19 22:09:41 +01:00
|
|
|
|
2018-06-05 21:52:18 +02:00
|
|
|
return finish(dev);
|
2017-09-01 15:33:10 +02:00
|
|
|
}
|
2020-05-29 09:37:42 +02:00
|
|
|
|
|
|
|
void i2c_isr_handler(void *arg)
|
|
|
|
{
|
2021-11-12 20:44:32 +01:00
|
|
|
i2c_t dev = (i2c_t)(uintptr_t)arg;
|
2020-05-29 09:37:42 +02:00
|
|
|
|
|
|
|
/* Mask interrupts to ensure that they only trigger once */
|
|
|
|
bus(dev)->INTENCLR = TWIM_INTEN_STOPPED_Msk | TWIM_INTEN_ERROR_Msk;
|
|
|
|
|
|
|
|
mutex_unlock(&busy[dev]);
|
|
|
|
}
|