/* 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) { // Nothing to do; start/stop is generated by i2c_read/i2c_write return 0; } int i2c_stop(i2c_t *obj) { struct i2c_s *i2c = cy_get_i2c(obj); 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; 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_byte_read"); } return value; } int i2c_byte_write(i2c_t *obj, int data) { struct i2c_s *i2c = cy_get_i2c(obj); // If we have not yet written the address, the first byte being sent is the address. cy_en_scb_i2c_status_t status = i2c->hal_i2c.context.state == CY_SCB_I2C_IDLE ? Cy_SCB_I2C_MasterSendStart(i2c->hal_i2c.base, data >> 1, CY_SCB_I2C_WRITE_XFER, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context)) : Cy_SCB_I2C_MasterWriteByte(i2c->hal_i2c.base, (uint8_t)data, CY_I2C_DEFAULT_TIMEOUT, &(i2c->hal_i2c.context)); 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