mbed-os/targets/TARGET_Cypress/TARGET_PSOC6/cy_i2c_api.c

332 lines
11 KiB
C

/* mbed Microcontroller Library
* Copyright (c) 2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "i2c_api.h"
#include "cyhal_i2c.h"
#include "cyhal_utils.h"
#include "cyhal_hwmgr.h"
#include "mbed_error.h"
#include "mbed_assert.h"
#include "mbed_critical.h"
#if DEVICE_I2C
#define CY_I2C_DEFAULT_TIMEOUT 200
#define CY_I2C_SLAVE_EVENT_IDLE 0
#define CY_I2C_SLAVE_EVENT_READ_ADDR 1
#define CY_I2C_SLAVE_EVENT_GENERAL_CALL 2
#define CY_I2C_SLAVE_EVENT_WRITE_ADDR 3
static inline struct i2c_s *cy_get_i2c(i2c_t *obj)
{
#if DEVICE_I2C_ASYNCH
return &(obj->i2c);
#else
return obj;
#endif
}
#ifdef __cplusplus
extern "C" {
#endif
static uint32_t cy_i2c_convert_event(i2c_t *obj, cyhal_i2c_event_t event)
{
if (CYHAL_I2C_EVENT_NONE != (event & (CYHAL_I2C_SLAVE_ERR_EVENT | CYHAL_I2C_MASTER_ERR_EVENT))) {
event |= I2C_EVENT_ERROR;
}
if (CYHAL_I2C_EVENT_NONE != (event & (CYHAL_I2C_SLAVE_RD_CMPLT_EVENT | CYHAL_I2C_SLAVE_WR_CMPLT_EVENT | CYHAL_I2C_MASTER_RD_CMPLT_EVENT | CYHAL_I2C_MASTER_WR_CMPLT_EVENT)) && !i2c_active(obj)) {
event |= I2C_EVENT_TRANSFER_COMPLETE;
}
return event;
}
static void cy_i2c_event_handler(void *handler_arg, cyhal_i2c_event_t event)
{
struct i2c_s *i2c = cy_get_i2c((i2c_t *)handler_arg);
switch (event) {
case CYHAL_I2C_SLAVE_READ_EVENT:
i2c->slave_event = CY_I2C_SLAVE_EVENT_READ_ADDR;
break;
case CYHAL_I2C_SLAVE_WRITE_EVENT:
i2c->slave_event = CY_I2C_SLAVE_EVENT_WRITE_ADDR;
break;
case CYHAL_I2C_SLAVE_ERR_EVENT: // fallthrough
case CYHAL_I2C_SLAVE_RD_CMPLT_EVENT: // fallthrough
case CYHAL_I2C_SLAVE_WR_CMPLT_EVENT:
i2c->slave_event = CY_I2C_SLAVE_EVENT_IDLE;
break;
default:
break;
}
#ifdef DEVICE_I2C_ASYNCH
i2c->async_event = cy_i2c_convert_event((i2c_t *)handler_arg, event);
void (*async_handler)(void) = i2c->async_handler;
if (NULL != async_handler && i2c->async_event != 0) {
(*async_handler)();
}
#endif
}
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
{
struct i2c_s *i2c = cy_get_i2c(obj);
cy_rslt_t result = cyhal_i2c_init(&(i2c->hal_i2c), sda, scl, NULL);
if (result == CYHAL_HWMGR_RSLT_ERR_INUSE) {
// MBED I2C driver currently does not support free, so we will allow I2C to be reallocated.
// TODO: once the the I2C driver properly supports free, this need to be fixed so that clocks and pins are no longer leaked.
cyhal_hwmgr_free(&(i2c->hal_i2c.resource));
cyhal_resource_inst_t pin_rsc = cyhal_utils_get_gpio_resource(sda);
cyhal_hwmgr_free(&pin_rsc);
pin_rsc = cyhal_utils_get_gpio_resource(scl);
cyhal_hwmgr_free(&pin_rsc);
result = cyhal_i2c_init(&(i2c->hal_i2c), sda, scl, NULL);
}
if (CY_RSLT_SUCCESS != result) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_init");
}
i2c->cfg.is_slave = false;
i2c->cfg.address = 0;
i2c->cfg.frequencyhal_hz = 400000;
i2c->async_handler = NULL;
cyhal_i2c_register_callback(&(i2c->hal_i2c), &cy_i2c_event_handler, obj);
cyhal_i2c_enable_event(&(i2c->hal_i2c), (cyhal_i2c_event_t)(CYHAL_I2C_SLAVE_READ_EVENT | CYHAL_I2C_SLAVE_WRITE_EVENT | CYHAL_I2C_SLAVE_ERR_EVENT | CYHAL_I2C_SLAVE_RD_CMPLT_EVENT | CYHAL_I2C_SLAVE_WR_CMPLT_EVENT | CYHAL_I2C_MASTER_ERR_EVENT | CYHAL_I2C_MASTER_RD_CMPLT_EVENT | CYHAL_I2C_MASTER_WR_CMPLT_EVENT), CYHAL_ISR_PRIORITY_DEFAULT, true);
}
void i2c_frequency(i2c_t *obj, int hz)
{
struct i2c_s *i2c = cy_get_i2c(obj);
i2c->cfg.frequencyhal_hz = (uint32_t)hz;
if (CY_RSLT_SUCCESS != cyhal_i2c_configure(&(i2c->hal_i2c), &(i2c->cfg))) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_configure");
}
}
int i2c_start(i2c_t *obj)
{
/* start/stop is generated by i2c_read/i2c_write */
struct i2c_s *i2c = cy_get_i2c(obj);
/* Clear state of address */
i2c->address_set = false;
return 0;
}
int i2c_stop(i2c_t *obj)
{
struct i2c_s *i2c = cy_get_i2c(obj);
/* Clear state of address */
i2c->address_set = false;
if (i2c->hal_i2c.context.state != CY_SCB_I2C_IDLE) {
return Cy_SCB_I2C_MasterSendStop(i2c->hal_i2c.base, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context));
}
return 0;
}
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
{
struct i2c_s *i2c = cy_get_i2c(obj);
if (CY_RSLT_SUCCESS != cyhal_i2c_master_read(&(i2c->hal_i2c), address >> 1, (uint8_t *)data, (uint16_t)length, CY_I2C_DEFAULT_TIMEOUT, (bool)stop)) {
return (int)I2C_ERROR_NO_SLAVE;
}
return length;
}
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop)
{
struct i2c_s *i2c = cy_get_i2c(obj);
if (CY_RSLT_SUCCESS != cyhal_i2c_master_write(&(i2c->hal_i2c), address >> 1, (const uint8_t *)data, (uint16_t)length, CY_I2C_DEFAULT_TIMEOUT, (bool)stop)) {
return (int)I2C_ERROR_NO_SLAVE;
}
// NOTE: HAL does not report how many bytes were actually sent in case of early NAK
return length;
}
void i2c_reset(i2c_t *obj)
{
i2c_stop(obj);
}
int i2c_byte_read(i2c_t *obj, int last)
{
struct i2c_s *i2c = cy_get_i2c(obj);
uint8_t value;
/* Slave device address is stored in object by first write operation */
if (i2c->address_set) {
/* Send slave device address */
/* Make sure if I2C transaction direction is 'Read' */
if (CY_SCB_I2C_SUCCESS != Cy_SCB_I2C_MasterSendStart(i2c->hal_i2c.base, i2c->address, CY_SCB_I2C_READ_XFER, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context))) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "i2c_send_start");
}
/* Clear state of address. It is not needed for next operation. */
i2c->address_set = false;
}
/* Read next byte */
if (CY_SCB_I2C_SUCCESS != Cy_SCB_I2C_MasterReadByte(i2c->hal_i2c.base, last != 0 ? CY_SCB_I2C_NAK : CY_SCB_I2C_ACK, &value, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context))) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "i2c_read_byte");
}
return value;
}
int i2c_byte_write(i2c_t *obj, int data)
{
struct i2c_s *i2c = cy_get_i2c(obj);
cy_en_scb_i2c_status_t status = CY_SCB_I2C_SUCCESS;
/* First byte should be address */
if (i2c->address_set) {
/* Verify if Master is ready for send slave address and send first data byte */
/* Make sure that I2C transaction direction is 'Write' */
if (i2c->hal_i2c.context.state == CY_SCB_I2C_IDLE) {
status = Cy_SCB_I2C_MasterSendStart(i2c->hal_i2c.base, i2c->address, CY_SCB_I2C_WRITE_XFER, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context));
}
if (status == CY_SCB_I2C_SUCCESS) {
status = Cy_SCB_I2C_MasterWriteByte(i2c->hal_i2c.base, (uint8_t)data, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context));
}
} else {
/* Store slave address and remember status for next byte read or write operation */
i2c->address = data >> 1;
i2c->address_set = true;
}
switch (status) {
case CY_SCB_I2C_MASTER_MANUAL_TIMEOUT:
return 2;
case CY_SCB_I2C_SUCCESS:
return 1;
default:
return 0;
}
}
const PinMap *i2c_master_sda_pinmap(void)
{
return PinMap_I2C_SDA;
}
const PinMap *i2c_master_scl_pinmap(void)
{
return PinMap_I2C_SCL;
}
const PinMap *i2c_slave_sda_pinmap(void)
{
return PinMap_I2C_SDA;
}
const PinMap *i2c_slave_scl_pinmap(void)
{
return PinMap_I2C_SCL;
}
#if DEVICE_I2CSLAVE
void i2c_slave_mode(i2c_t *obj, int enable_slave)
{
struct i2c_s *i2c = cy_get_i2c(obj);
i2c->cfg.is_slave = (0 != enable_slave);
if (CY_RSLT_SUCCESS != cyhal_i2c_configure(&(i2c->hal_i2c), &(i2c->cfg))) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_configure");
}
}
int i2c_slave_receive(i2c_t *obj)
{
return cy_get_i2c(obj)->slave_event;
}
int i2c_slave_read(i2c_t *obj, char *data, int length)
{
struct i2c_s *i2c = cy_get_i2c(obj);
if (CY_RSLT_SUCCESS != cyhal_i2c_slave_config_read_buff(&(i2c->hal_i2c), (uint8_t *)data, (uint16_t)length)) {
return 0;
}
return 1;
}
int i2c_slave_write(i2c_t *obj, const char *data, int length)
{
struct i2c_s *i2c = cy_get_i2c(obj);
if (CY_RSLT_SUCCESS != cyhal_i2c_slave_config_write_buff(&(i2c->hal_i2c), (const uint8_t *)data, (uint16_t)length)) {
return 0;
}
return 1;
}
void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask)
{
struct i2c_s *i2c = cy_get_i2c(obj);
i2c->cfg.address = address;
if (CY_RSLT_SUCCESS != cyhal_i2c_configure(&(i2c->hal_i2c), &(i2c->cfg))) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_configure");
}
Cy_SCB_I2C_SlaveSetAddressMask(i2c->hal_i2c.base, (uint8_t)mask);
}
#endif
#if DEVICE_I2C_ASYNCH
void i2c_transfer_asynch(i2c_t *obj, const void *tx, size_t tx_length, void *rx, size_t rx_length, uint32_t address, uint32_t stop, uint32_t handler, uint32_t event, DMAUsage hint)
{
struct i2c_s *i2c = cy_get_i2c(obj);
core_util_critical_section_enter();
i2c->async_rx_size = rx_length;
i2c->async_handler = (void (*)(void))handler;
core_util_critical_section_exit();
if (CY_RSLT_SUCCESS != cyhal_i2c_master_transfer_async(&(i2c->hal_i2c), address, tx, tx_length, rx, rx_length)) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_master_transfer_async");
}
}
uint32_t i2c_irq_handler_asynch(i2c_t *obj)
{
return cy_get_i2c(obj)->async_event;
}
uint8_t i2c_active(i2c_t *obj)
{
return cy_get_i2c(obj)->hal_i2c.pending != 0 ? 1 : 0;
}
void i2c_abort_asynch(i2c_t *obj)
{
struct i2c_s *i2c = cy_get_i2c(obj);
if (CY_RSLT_SUCCESS != cyhal_i2c_abort_async(&(i2c->hal_i2c))) {
MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER_I2C, MBED_ERROR_CODE_FAILED_OPERATION), "cyhal_i2c_abort_async");
}
}
void i2c_free(i2c_t *obj)
{
struct i2c_s *i2c = cy_get_i2c(obj);
cyhal_i2c_free(&i2c->hal_i2c);
}
#endif
#ifdef __cplusplus
}
#endif
#endif