nes-proj/arch/platform/cc26x0-cc13x0/sensortag/board-i2c.c

344 lines
11 KiB
C

/*
* Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/
* Copyright (c) 2017, University of Bristol - http://www.bris.ac.uk/
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*---------------------------------------------------------------------------*/
/**
* \addtogroup sensortag-cc26xx-i2c
* @{
*
* \file
* Board-specific I2C driver for the Sensortags
*/
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "ti-lib.h"
#include "board-i2c.h"
#include "lpm.h"
#include "rtimer.h"
#include <string.h>
#include <stdbool.h>
/*---------------------------------------------------------------------------*/
#define I2C_MAX_WAIT_TIME (RTIMER_SECOND / 10)
#define LIMITED_BUSYWAIT(cond) do { \
rtimer_clock_t end_time = RTIMER_NOW() + I2C_MAX_WAIT_TIME; \
while(cond) { \
if(!RTIMER_CLOCK_LT(RTIMER_NOW(), end_time)) { \
return false; \
} \
} \
} while(0)
/*---------------------------------------------------------------------------*/
#define NO_INTERFACE 0xFF
/*---------------------------------------------------------------------------*/
static uint8_t slave_addr = 0x00;
static uint8_t interface = NO_INTERFACE;
/*---------------------------------------------------------------------------*/
static bool
accessible(void)
{
/* First, check the PD */
if(ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON) {
return false;
}
/* Then check the 'run mode' clock gate */
if(!(HWREG(PRCM_BASE + PRCM_O_I2CCLKGR) & PRCM_I2CCLKGR_CLK_EN)) {
return false;
}
return true;
}
/*---------------------------------------------------------------------------*/
void
board_i2c_wakeup()
{
/* First, make sure the SERIAL PD is on */
ti_lib_prcm_power_domain_on(PRCM_DOMAIN_SERIAL);
while((ti_lib_prcm_power_domain_status(PRCM_DOMAIN_SERIAL)
!= PRCM_DOMAIN_POWER_ON));
/* Enable the clock to I2C */
ti_lib_prcm_peripheral_run_enable(PRCM_PERIPH_I2C0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
/* Enable and initialize the I2C master module */
ti_lib_i2c_master_init_exp_clk(I2C0_BASE, ti_lib_sys_ctrl_clock_get(),
true);
}
/*---------------------------------------------------------------------------*/
static bool
i2c_status()
{
uint32_t status;
status = ti_lib_i2c_master_err(I2C0_BASE);
if(status & (I2C_MSTAT_DATACK_N_M | I2C_MSTAT_ADRACK_N_M)) {
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_ERROR_STOP);
}
return status == I2C_MASTER_ERR_NONE;
}
/*---------------------------------------------------------------------------*/
void
board_i2c_shutdown()
{
interface = NO_INTERFACE;
if(accessible()) {
ti_lib_i2c_master_disable(I2C0_BASE);
}
ti_lib_prcm_peripheral_run_disable(PRCM_PERIPH_I2C0);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
/*
* Set all pins to GPIO Input and disable the output driver. Set internal
* pull to match external pull
*
* SDA and SCL: external PU resistor
* SDA HP and SCL HP: MPU PWR low
*/
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA_HP);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA_HP, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL_HP);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL_HP, IOC_IOPULL_DOWN);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_IOPULL_UP);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_IOPULL_UP);
}
/*---------------------------------------------------------------------------*/
bool
board_i2c_write(uint8_t *data, uint8_t len)
{
uint32_t i;
bool success;
/* Write slave address */
ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, false);
/* Write first byte */
ti_lib_i2c_master_data_put(I2C0_BASE, data[0]);
/* Check if another master has access */
LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE));
/* Assert RUN + START */
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
success = i2c_status();
for(i = 1; i < len && success; i++) {
/* Write next byte */
ti_lib_i2c_master_data_put(I2C0_BASE, data[i]);
if(i < len - 1) {
/* Clear START */
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
success = i2c_status();
}
}
/* Assert stop */
if(success) {
/* Assert STOP */
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
success = i2c_status();
LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE));
}
return success;
}
/*---------------------------------------------------------------------------*/
bool
board_i2c_write_single(uint8_t data)
{
/* Write slave address */
ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, false);
/* Write first byte */
ti_lib_i2c_master_data_put(I2C0_BASE, data);
/* Check if another master has access */
LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE));
/* Assert RUN + START + STOP */
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_SINGLE_SEND);
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
return i2c_status();
}
/*---------------------------------------------------------------------------*/
bool
board_i2c_read(uint8_t *data, uint8_t len)
{
uint8_t i;
bool success;
/* Set slave address */
ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, true);
/* Check if another master has access */
LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE));
/* Assert RUN + START + ACK */
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
i = 0;
success = true;
while(i < (len - 1) && success) {
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
success = i2c_status();
if(success) {
data[i] = ti_lib_i2c_master_data_get(I2C0_BASE);
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
i++;
}
}
if(success) {
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
success = i2c_status();
if(success) {
data[len - 1] = ti_lib_i2c_master_data_get(I2C0_BASE);
LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE));
}
}
return success;
}
/*---------------------------------------------------------------------------*/
bool
board_i2c_write_read(uint8_t *wdata, uint8_t wlen, uint8_t *rdata, uint8_t rlen)
{
uint32_t i;
bool success;
/* Set slave address for write */
ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, false);
/* Write first byte */
ti_lib_i2c_master_data_put(I2C0_BASE, wdata[0]);
/* Check if another master has access */
LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE));
/* Assert RUN + START */
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
success = i2c_status();
for(i = 1; i < wlen && success; i++) {
/* Write next byte */
ti_lib_i2c_master_data_put(I2C0_BASE, wdata[i]);
/* Clear START */
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
success = i2c_status();
}
if(!success) {
return false;
}
/* Set slave address for read */
ti_lib_i2c_master_slave_addr_set(I2C0_BASE, slave_addr, true);
/* Assert ACK */
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);
i = 0;
while(i < (rlen - 1) && success) {
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
success = i2c_status();
if(success) {
rdata[i] = ti_lib_i2c_master_data_get(I2C0_BASE);
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_CONT);
i++;
}
}
if(success) {
ti_lib_i2c_master_control(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
LIMITED_BUSYWAIT(ti_lib_i2c_master_busy(I2C0_BASE));
success = i2c_status();
if(success) {
rdata[rlen - 1] = ti_lib_i2c_master_data_get(I2C0_BASE);
LIMITED_BUSYWAIT(ti_lib_i2c_master_bus_busy(I2C0_BASE));
}
}
return success;
}
/*---------------------------------------------------------------------------*/
void
board_i2c_select(uint8_t new_interface, uint8_t address)
{
slave_addr = address;
if(accessible() == false) {
board_i2c_wakeup();
}
if(new_interface != interface) {
interface = new_interface;
ti_lib_i2c_master_disable(I2C0_BASE);
if(interface == BOARD_I2C_INTERFACE_0) {
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA, IOC_NO_IOPULL);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL, IOC_NO_IOPULL);
ti_lib_ioc_pin_type_i2c(I2C0_BASE, BOARD_IOID_SDA, BOARD_IOID_SCL);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA_HP);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL_HP);
} else if(interface == BOARD_I2C_INTERFACE_1) {
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SDA_HP, IOC_NO_IOPULL);
ti_lib_ioc_io_port_pull_set(BOARD_IOID_SCL_HP, IOC_NO_IOPULL);
ti_lib_ioc_pin_type_i2c(I2C0_BASE, BOARD_IOID_SDA_HP, BOARD_IOID_SCL_HP);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SDA);
ti_lib_ioc_pin_type_gpio_input(BOARD_IOID_SCL);
}
/* Enable and initialize the I2C master module */
ti_lib_i2c_master_init_exp_clk(I2C0_BASE, ti_lib_sys_ctrl_clock_get(),
true);
}
}
/*---------------------------------------------------------------------------*/
/** @} */