Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion boards/hifive1b/Makefile.features
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ CPU = fe310
CPU_MODEL = fe310_g002

# Put defined MCU peripherals here (in alphabetical order)
#FEATURES_PROVIDED += periph_i2c
FEATURES_PROVIDED += periph_i2c
#FEATURES_PROVIDED += periph_pwm
FEATURES_PROVIDED += periph_rtc
FEATURES_PROVIDED += periph_rtt
Expand Down
16 changes: 16 additions & 0 deletions boards/hifive1b/include/periph_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ static const uart_conf_t uart_config[] = {
#define PWM_NUMOF (3)
/** @} */

/**
* @name I2C configuration
* @{
*/
static const i2c_conf_t i2c_config[] = {
{
.addr = I2C0_CTRL_ADDR,
.scl = GPIO_PIN(0, 13),
.sda = GPIO_PIN(0, 12),
.speed = I2C_SPEED_NORMAL,
},
};

#define I2C_NUMOF ARRAY_SIZE(i2c_config)
/** @} */

#ifdef __cplusplus
}
#endif
Expand Down
37 changes: 37 additions & 0 deletions cpu/fe310/include/periph_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,43 @@ typedef struct {
*/
#define PERIPH_TIMER_PROVIDES_SET

/**
* @name Use the shared I2C functions
* @{
*/
/** Use read reg function from periph common */
#define PERIPH_I2C_NEED_READ_REG
/** Use write reg function from periph common */
#define PERIPH_I2C_NEED_WRITE_REG
/** Use read regs function from periph common */
#define PERIPH_I2C_NEED_READ_REGS
/** Use write regs function from periph common */
#define PERIPH_I2C_NEED_WRITE_REGS
/** @} */

#ifndef DOXYGEN
/**
* @brief Default mapping of I2C bus speed values
* @{
*/
#define HAVE_I2C_SPEED_T
typedef enum {
I2C_SPEED_NORMAL, /**< normal mode: ~100kbit/s */
I2C_SPEED_FAST, /**< fast mode: ~400kbit/s */
} i2c_speed_t;
/** @} */
#endif /* ndef DOXYGEN */

/**
* @brief I2C configuration options
*/
typedef struct {
uint32_t addr; /**< device base address */
gpio_t scl; /**< SCL pin */
gpio_t sda; /**< SDA pin */
i2c_speed_t speed; /**< I2C speed */
} i2c_conf_t;

#ifdef __cplusplus
}
#endif
Expand Down
280 changes: 280 additions & 0 deletions cpu/fe310/periph/i2c.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
/*
* Copyright (C) 2019 Inria
*
* 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_fe310
* @ingroup drivers_periph_i2c
* @{
*
* @file
* @brief Low-level I2C driver implementation
*
* @author Alexandre Abadie <alexandre.abadie@inria.fr>
*
* @}
*/

#include <inttypes.h>
#include <assert.h>
#include <errno.h>

#include "cpu.h"
#include "mutex.h"

#include "periph/i2c.h"
#include "periph_conf.h"

#include "vendor/i2c.h"
#include "vendor/platform.h"
#include "vendor/prci_driver.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

#define I2C_BUSY_TIMEOUT (0xffff)
static const uint16_t _fe310_i2c_speed[2] = { 100U, 400U };

static inline int _wait_busy(i2c_t dev, uint32_t max_timeout_counter);
static inline int _start(i2c_t dev, uint16_t address);
static inline int _read(i2c_t dev, uint8_t *data, int length, uint8_t stop);
static inline int _write(i2c_t dev, const uint8_t *data, int length, uint8_t stop);

/**
* @brief Initialized bus locks
*/
static mutex_t locks[I2C_NUMOF];

void i2c_init(i2c_t dev)
{
assert(dev < I2C_NUMOF);

/* Initialize mutex */
mutex_init(&locks[dev]);

/* Select IOF0 */
GPIO_REG(GPIO_IOF_SEL) &= ~((1 << i2c_config[dev].scl) | (1 << i2c_config[dev].sda));
/* Enable IOF */
GPIO_REG(GPIO_IOF_EN) |= ((1 << i2c_config[dev].scl) | (1 << i2c_config[dev].sda));

_REG32(i2c_config[dev].addr, I2C_CONTROL) &= ~(I2C_CONTROL_IE | I2C_CONTROL_EN);

/* Compute prescale: presc = (CORE_CLOCK / (5 * I2C_SPEED)) - 1 */
uint16_t presc = ((uint16_t)(cpu_freq() / 1000) / (5 * _fe310_i2c_speed[i2c_config[dev].speed])) - 1;

DEBUG("[i2c] init: computed prescale: %i (0x%02X|0x%02X)\n", presc, (presc >> 8), (presc & 0xFF));

_REG32(i2c_config[dev].addr, I2C_PRESCALE_LO) = (presc & 0xFF);
_REG32(i2c_config[dev].addr, I2C_PRESCALE_HI) = (presc >> 8);

DEBUG("[i2c] init: stored prescale: (0x%02X|0x%02X)\n",
(uint8_t)(_REG32(i2c_config[dev].addr, I2C_PRESCALE_HI) & 0xFF),
(uint8_t)(_REG32(i2c_config[dev].addr, I2C_PRESCALE_LO) & 0xFF));

_REG32(i2c_config[dev].addr, I2C_CONTROL) = I2C_CONTROL_EN;

DEBUG("[i2c] init: control reg 0x%08X)\n",
(unsigned)_REG32(i2c_config[dev].addr, I2C_CONTROL));

DEBUG("[i2c] initialization done\n");
}

int i2c_acquire(i2c_t dev)
{
assert(dev < I2C_NUMOF);
mutex_lock(&locks[dev]);
return 0;
}

void i2c_release(i2c_t dev)
{
assert(dev < I2C_NUMOF);
mutex_unlock(&locks[dev]);
}

int i2c_read_bytes(i2c_t dev, uint16_t address, void *data, size_t length,
uint8_t flags)
{
assert(length > 0);
assert(dev < I2C_NUMOF);

if (flags & I2C_ADDR10) {
return -EOPNOTSUPP;
}

/* Check for wrong arguments given */
if (data == NULL || length == 0) {
return -EINVAL;
}

DEBUG("[i2c] read bytes\n");

int ret = 0;
if (!(flags & I2C_NOSTART)) {
ret = _start(dev, ((address << 1) | I2C_READ));
if (ret < 0) {
DEBUG("[i2c] Error: start command failed\n");
return ret;
}
}

/* read data and issue stop if needed */
ret = _read(dev, data, length, (flags & I2C_NOSTOP) ? 0 : 1);
if (ret < 0) {
DEBUG("[i2c] Error: read command failed\n");
return ret;
}

_wait_busy(dev, I2C_BUSY_TIMEOUT);

return length;
}

int i2c_write_bytes(i2c_t dev, uint16_t address, const void *data, size_t length,
uint8_t flags)
{
assert(dev < I2C_NUMOF);

int ret = 0;

/* Check for unsupported operations */
if (flags & I2C_ADDR10) {
return -EOPNOTSUPP;
}
/* Check for wrong arguments given */
if (data == NULL || length == 0) {
return -EINVAL;
}

DEBUG("[i2c] write bytes\n");

if (!(flags & I2C_NOSTART)) {
ret = _start(dev, (address << 1));
if (ret < 0) {
DEBUG("[i2c] error: start command failed\n");
return ret;
}
}

ret = _write(dev, data, length, (flags & I2C_NOSTOP) ? 0 : 1);
if (ret < 0) {
DEBUG("[i2c] error: write command failed\n");
return ret;
}

return 0;
}

static inline int _wait_busy(i2c_t dev, uint32_t max_timeout_counter)
{
uint32_t timeout_counter = 0;
DEBUG("[i2c] wait for transfert\n");
while (_REG32(i2c_config[dev].addr, I2C_STATUS) & I2C_STATUS_TIP) {
if (++timeout_counter >= max_timeout_counter) {
DEBUG("[i2c] transfert timeout\n");
return -ETIMEDOUT;
}
else if ((_REG32(i2c_config[dev].addr, I2C_STATUS) & I2C_STATUS_ALOST) == I2C_STATUS_ALOST) {
/* Arbitration lost */
DEBUG("[i2c] error: Arbitration lost\n");
return -EAGAIN;
}
}
return 0;
}

static inline int _start(i2c_t dev, uint16_t address)
{
_wait_busy(dev, I2C_BUSY_TIMEOUT);

/* start transmission */
DEBUG("[i2c] write slave address, 0x%02X\n", address);
_REG32(i2c_config[dev].addr, I2C_DATA) = address;

DEBUG("[i2c] send start condition\n");
_REG32(i2c_config[dev].addr, I2C_CMD) = (I2C_CMD_STA | I2C_CMD_WR);

/* Ensure all bytes has been read */
int ret = _wait_busy(dev, I2C_BUSY_TIMEOUT);
if (ret < 0) {
return ret;
}

return 0;
}

static inline int _read(i2c_t dev, uint8_t *data, int length, uint8_t stop)
{
uint8_t count = 0;

/* Read data buffer. */
while (length--) {
uint8_t command = I2C_CMD_RD;

/* Wait for hardware module to sync */
int ret = _wait_busy(dev, I2C_BUSY_TIMEOUT);
if (ret < 0) {
return ret;
}

if (length == 0) {
/* Send NACK before STOP */
command |= I2C_CMD_ACK;
/* Prepare stop command */
if (stop) {
command |= I2C_CMD_STO;
}
}

_REG32(i2c_config[dev].addr, I2C_CMD) = command;

/* Wait for response on bus. */
ret = _wait_busy(dev, I2C_BUSY_TIMEOUT);
if (ret < 0) {
return ret;
}

data[count] = (uint32_t)(_REG32(i2c_config[dev].addr, I2C_DATA));
DEBUG("[i2c] read byte #%i, 0x%02X\n", count, data[count]);

count++;
}
return 0;
}

static inline int _write(i2c_t dev, const uint8_t *data, int length, uint8_t stop)
{
uint8_t count = 0;

/* Write data buffer until the end. */
while (length--) {
/* Wait for hardware module to sync */
int ret = _wait_busy(dev, I2C_BUSY_TIMEOUT);
if (ret < 0) {
return ret;
}

uint8_t command = I2C_CMD_WR;

DEBUG("[i2c] write byte #%i, 0x%02X\n", count, data[count]);
_REG32(i2c_config[dev].addr, I2C_DATA) = data[count++];

/* Check if this is the last byte to read */
if ((length == 0) && stop) {
command |= I2C_CMD_STO;
}

_REG32(i2c_config[dev].addr, I2C_CMD) = command;

ret = _wait_busy(dev, I2C_BUSY_TIMEOUT);
if (ret < 0) {
return ret;
}
}

return 0;
}
1 change: 1 addition & 0 deletions tests/driver_ina3221/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* @}
*/

#include <stdio.h>
#include <string.h>
#include "periph/gpio.h"
#include "xtimer.h"
Expand Down
2 changes: 2 additions & 0 deletions tests/driver_ph_oem/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
* @}
*/

#include <stdio.h>

#include "xtimer.h"
#include "event/callback.h"

Expand Down