/**
* \file
*
* \brief SAM I2C Master Driver
*
* Copyright (C) 2012-2016 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* 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. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
*
* \asf_license_stop
*
*/
/*
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#include "i2c_master.h"
#if I2C_MASTER_CALLBACK_MODE == true
# include "i2c_master_interrupt.h"
#endif
/* Forward declaration */
enum status_code _i2c_master_wait_for_bus(
struct i2c_master_module *const module);
enum status_code _i2c_master_address_response(
struct i2c_master_module *const module);
enum status_code _i2c_master_send_hs_master_code(
struct i2c_master_module *const module,
uint8_t hs_master_code);
#if !defined(__DOXYGEN__)
/**
* \internal Sets configurations to module
*
* \param[out] module Pointer to software module structure
* \param[in] config Configuration structure with configurations to set
*
* \return Status of setting configuration.
* \retval STATUS_OK If module was configured correctly
* \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than
* previously set
* \retval STATUS_ERR_BAUDRATE_UNAVAILABLE If given baudrate is not compatible
* with set GCLK frequency
*/
static enum status_code _i2c_master_set_config(
struct i2c_master_module *const module,
const struct i2c_master_config *const config)
{
/* Sanity check arguments. */
Assert(module);
Assert(module->hw);
Assert(config);
/* Temporary variables. */
uint32_t tmp_ctrla;
int32_t tmp_baud = 0;
int32_t tmp_baud_hs = 0;
int32_t tmp_baudlow_hs = 0;
enum status_code tmp_status_code = STATUS_OK;
SercomI2cm *const i2c_module = &(module->hw->I2CM);
Sercom *const sercom_hw = module->hw;
uint8_t sercom_index = _sercom_get_sercom_inst_index(sercom_hw);
/* Pin configuration */
struct system_pinmux_config pin_conf;
system_pinmux_get_config_defaults(&pin_conf);
uint32_t pad0 = config->pinmux_pad0;
uint32_t pad1 = config->pinmux_pad1;
/* SERCOM PAD0 - SDA */
if (pad0 == PINMUX_DEFAULT) {
pad0 = _sercom_get_default_pad(sercom_hw, 0);
}
pin_conf.mux_position = pad0 & 0xFFFF;
pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK;
system_pinmux_pin_set_config(pad0 >> 16, &pin_conf);
/* SERCOM PAD1 - SCL */
if (pad1 == PINMUX_DEFAULT) {
pad1 = _sercom_get_default_pad(sercom_hw, 1);
}
pin_conf.mux_position = pad1 & 0xFFFF;
pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_OUTPUT_WITH_READBACK;
system_pinmux_pin_set_config(pad1 >> 16, &pin_conf);
/* Save timeout on unknown bus state in software module. */
module->unknown_bus_state_timeout = config->unknown_bus_state_timeout;
/* Save timeout on buffer write. */
module->buffer_timeout = config->buffer_timeout;
/* Set whether module should run in standby. */
if (config->run_in_standby || system_is_debugger_present()) {
tmp_ctrla = SERCOM_I2CM_CTRLA_RUNSTDBY;
} else {
tmp_ctrla = 0;
}
/* Check and set start data hold timeout. */
if (config->start_hold_time != I2C_MASTER_START_HOLD_TIME_DISABLED) {
tmp_ctrla |= config->start_hold_time;
}
/* Check and set transfer speed */
tmp_ctrla |= config->transfer_speed;
/* Check and set SCL low timeout. */
if (config->scl_low_timeout) {
tmp_ctrla |= SERCOM_I2CM_CTRLA_LOWTOUTEN;
}
/* Check and set inactive bus timeout. */
if (config->inactive_timeout != I2C_MASTER_INACTIVE_TIMEOUT_DISABLED) {
tmp_ctrla |= config->inactive_timeout;
}
/* Check and set SCL clock stretch mode. */
if (config->scl_stretch_only_after_ack_bit || (config->transfer_speed == I2C_MASTER_SPEED_HIGH_SPEED)) {
tmp_ctrla |= SERCOM_I2CM_CTRLA_SCLSM;
}
/* Check and set slave SCL low extend timeout. */
if (config->slave_scl_low_extend_timeout) {
tmp_ctrla |= SERCOM_I2CM_CTRLA_SEXTTOEN;
}
/* Check and set master SCL low extend timeout. */
if (config->master_scl_low_extend_timeout) {
tmp_ctrla |= SERCOM_I2CM_CTRLA_MEXTTOEN;
}
/* Write config to register CTRLA. */
i2c_module->CTRLA.reg |= tmp_ctrla;
/* Set configurations in CTRLB. */
i2c_module->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;
/* Find and set baudrate, considering sda/scl rise time */
uint32_t fgclk = system_gclk_chan_get_hz(SERCOM0_GCLK_ID_CORE + sercom_index);
uint32_t fscl = 1000 * config->baud_rate;
uint32_t fscl_hs = 1000 * config->baud_rate_high_speed;
uint32_t trise = config->sda_scl_rise_time_ns;
tmp_baud = (int32_t)(div_ceil(
fgclk - fscl * (10 + (fgclk * 0.000000001)* trise), 2 * fscl));
/* For High speed mode, set the SCL ratio of high:low to 1:2. */
if (config->transfer_speed == I2C_MASTER_SPEED_HIGH_SPEED) {
tmp_baudlow_hs = (int32_t)((fgclk * 2.0) / (3.0 * fscl_hs) - 1);
if (tmp_baudlow_hs) {
tmp_baud_hs = (int32_t)(fgclk / fscl_hs) - 2 - tmp_baudlow_hs;
} else {
tmp_baud_hs = (int32_t)(div_ceil(fgclk, 2 * fscl_hs)) - 1;
}
}
/* Check that baudrate is supported at current speed. */
if (tmp_baud > 255 || tmp_baud < 0 || tmp_baud_hs > 255 || tmp_baud_hs < 0) {
/* Baud rate not supported. */
tmp_status_code = STATUS_ERR_BAUDRATE_UNAVAILABLE;
}
if (tmp_status_code != STATUS_ERR_BAUDRATE_UNAVAILABLE) {
/* Baud rate acceptable. */
i2c_module->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud) |
SERCOM_I2CM_BAUD_HSBAUD(tmp_baud_hs) | SERCOM_I2CM_BAUD_HSBAUDLOW(tmp_baudlow_hs);
}
return tmp_status_code;
}
#endif /* __DOXYGEN__ */
/**
* \brief Initializes the requested I<SUP>2</SUP>C hardware module
*
* Initializes the SERCOM I<SUP>2</SUP>C master device requested and sets the provided
* software module struct. Run this function before any further use of
* the driver.
*
* \param[out] module Pointer to software module struct
* \param[in] hw Pointer to the hardware instance
* \param[in] config Pointer to the configuration struct
*
* \return Status of initialization.
* \retval STATUS_OK Module initiated correctly
* \retval STATUS_ERR_DENIED If module is enabled
* \retval STATUS_BUSY If module is busy resetting
* \retval STATUS_ERR_ALREADY_INITIALIZED