http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/a280628a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c ---------------------------------------------------------------------- diff --git a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c b/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c deleted file mode 100755 index 10dfcdf..0000000 --- a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_master_interrupt.c +++ /dev/null @@ -1,755 +0,0 @@ -/** - * \file - * - * \brief SAM I2C Master Interrupt Driver - * - * Copyright (C) 2012-2015 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_interrupt.h" - -extern enum status_code _i2c_master_wait_for_bus( - struct i2c_master_module *const module); - -extern enum status_code _i2c_master_address_response( - struct i2c_master_module *const module); - -extern enum status_code _i2c_master_send_hs_master_code( - struct i2c_master_module *const module, - uint8_t hs_master_code);; - -/** - * \internal - * Read next data. Used by interrupt handler to get next data byte from slave. - * - * \param[in,out] module Pointer to software module structure - */ -static void _i2c_master_read( - struct i2c_master_module *const module) -{ - /* Sanity check arguments. */ - Assert(module); - Assert(module->hw); - - SercomI2cm *const i2c_module = &(module->hw->I2CM); - bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; - - /* Find index to save next value in buffer */ - uint16_t buffer_index = module->buffer_length; - buffer_index -= module->buffer_remaining; - - module->buffer_remaining--; - - if (sclsm_flag) { - if (module->send_nack && module->buffer_remaining == 1) { - /* Set action to NACK. */ - i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; - } - } else { - if (module->send_nack && module->buffer_remaining == 0) { - /* Set action to NACK. */ - i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT; - } - } - - if (module->buffer_remaining == 0) { - if (module->send_stop) { - /* Send stop condition */ - _i2c_master_wait_for_sync(module); - i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); - } - } - - /* Read byte from slave and put in buffer */ - _i2c_master_wait_for_sync(module); - module->buffer[buffer_index] = i2c_module->DATA.reg; -} - -/** - * \internal - * - * Write next data. Used by interrupt handler to send next data byte to slave. - * - * \param[in,out] module Pointer to software module structure - */ -static void _i2c_master_write(struct i2c_master_module *const module) -{ - /* Sanity check arguments. */ - Assert(module); - Assert(module->hw); - - SercomI2cm *const i2c_module = &(module->hw->I2CM); - - /* Check for ack from slave */ - if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) - { - /* Set status */ - module->status = STATUS_ERR_OVERFLOW; - /* Do not write more data */ - return; - } - - /* Find index to get next byte in buffer */ - uint16_t buffer_index = module->buffer_length; - buffer_index -= module->buffer_remaining; - - module->buffer_remaining--; - - /* Write byte from buffer to slave */ - _i2c_master_wait_for_sync(module); - i2c_module->DATA.reg = module->buffer[buffer_index]; -} - -/** - * \internal - * Acts on slave address response. Checks for errors concerning master->slave - * handshake. - * - * \param[in,out] module Pointer to software module structure - */ -static void _i2c_master_async_address_response( - struct i2c_master_module *const module) -{ - /* Sanity check arguments. */ - Assert(module); - Assert(module->hw); - - SercomI2cm *const i2c_module = &(module->hw->I2CM); - - /* Check for error. Ignore bus-error; workaround for bus state stuck in - * BUSY. - */ - if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) - { - /* Clear write interrupt flag */ - i2c_module->INTFLAG.reg = SERCOM_I2CM_INTENCLR_MB; - - /* Check arbitration */ - if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_ARBLOST) { - /* Return busy */ - module->status = STATUS_ERR_PACKET_COLLISION; - } - } else if (i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) { - /* Return bad address value */ - module->status = STATUS_ERR_BAD_ADDRESS; - module->buffer_remaining = 0; - - if (module->send_stop) { - /* Send stop condition */ - _i2c_master_wait_for_sync(module); - i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); - } - } - - module->buffer_length = module->buffer_remaining; - - /* Check for status OK. */ - if (module->status == STATUS_BUSY) { - /* Call function based on transfer direction. */ - if (module->transfer_direction == I2C_TRANSFER_WRITE) { - _i2c_master_write(module); - } else { - _i2c_master_read(module); - } - } -} - -/** - * \brief Registers callback for the specified callback type - * - * Associates the given callback function with the - * specified callback type. - * - * To enable the callback, the \ref i2c_master_enable_callback function - * must be used. - * - * \param[in,out] module Pointer to the software module struct - * \param[in] callback Pointer to the function desired for the - * specified callback - * \param[in] callback_type Callback type to register - */ -void i2c_master_register_callback( - struct i2c_master_module *const module, - const i2c_master_callback_t callback, - enum i2c_master_callback callback_type) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - Assert(callback); - - /* Register callback */ - module->callbacks[callback_type] = callback; - - /* Set corresponding bit to set callback as registered */ - module->registered_callback |= (1 << callback_type); -} - -/** - * \brief Unregisters callback for the specified callback type - * - * When called, the currently registered callback for the given callback type - * will be removed. - * - * \param[in,out] module Pointer to the software module struct - * \param[in] callback_type Specifies the callback type to unregister - */ -void i2c_master_unregister_callback( - struct i2c_master_module *const module, - enum i2c_master_callback callback_type) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - - /* Register callback */ - module->callbacks[callback_type] = NULL; - - /* Clear corresponding bit to set callback as unregistered */ - module->registered_callback &= ~(1 << callback_type); -} - -/** - * \internal - * Starts a read bytes operation. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting reading I<SUP>2</SUP>C packet. - * \retval STATUS_OK If reading was started successfully - * \retval STATUS_BUSY If module is currently busy with another transfer - */ -enum status_code i2c_master_read_bytes( - struct i2c_master_module *const module, - struct i2c_master_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - - SercomI2cm *const i2c_module = &(module->hw->I2CM); - - /* Save packet to software module */ - module->buffer = packet->data; - module->buffer_remaining = packet->data_length; - module->transfer_direction = I2C_TRANSFER_READ; - module->status = STATUS_BUSY; - module->send_stop = false; - module->send_nack = false; - - /* Enable interrupts */ - i2c_module->INTENSET.reg = - SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; - - return STATUS_OK; -} - -/** - * \internal - * Starts a read packet operation. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting reading I<SUP>2</SUP>C packet. - * \retval STATUS_OK If reading was started successfully - * \retval STATUS_BUSY If module is currently busy with another transfer - */ -static enum status_code _i2c_master_read_packet( - struct i2c_master_module *const module, - struct i2c_master_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - - SercomI2cm *const i2c_module = &(module->hw->I2CM); - enum status_code tmp_status; - - /* Save packet to software module */ - module->buffer = packet->data; - module->buffer_remaining = packet->data_length; - module->transfer_direction = I2C_TRANSFER_READ; - module->status = STATUS_BUSY; - - /* Switch to high speed mode */ - if (packet->high_speed) { - _i2c_master_send_hs_master_code(module, packet->hs_master_code); - } - - /* Set action to ACK. */ - i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; - - if (packet->ten_bit_address) { - /* - * Write ADDR.ADDR[10:1] with the 10-bit address. ADDR.TENBITEN must - * be set and read/write bit (ADDR.ADDR[0]) equal to 0. - */ - i2c_module->ADDR.reg = (packet->address << 1) | - (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | - SERCOM_I2CM_ADDR_TENBITEN; - - /* Wait for response on bus. */ - tmp_status = _i2c_master_wait_for_bus(module); - - /* Set action to ack. */ - i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; - - /* Check for address response error unless previous error is - * detected. */ - if (tmp_status == STATUS_OK) { - tmp_status = _i2c_master_address_response(module); - } - - if (tmp_status == STATUS_OK) { - /* Enable interrupts */ - i2c_module->INTENSET.reg = - SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; - - /* - * Write ADDR[7:0] register to "11110 address[9:8] 1" - * ADDR.TENBITEN must be cleared - */ - i2c_module->ADDR.reg = (((packet->address >> 8) | 0x78) << 1) | - (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | - I2C_TRANSFER_READ; - } else { - return tmp_status; - } - } else { - /* Enable interrupts */ - i2c_module->INTENSET.reg = - SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; - - /* Set address and direction bit. Will send start command on bus */ - i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_READ | - (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); - } - - return STATUS_OK; -} - -/** - * \brief Initiates a read packet operation - * - * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C - * bus. This is the non-blocking equivalent of \ref i2c_master_read_packet_wait. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting reading I<SUP>2</SUP>C packet. - * \retval STATUS_OK If reading was started successfully - * \retval STATUS_BUSY If module is currently busy with another transfer - */ -enum status_code i2c_master_read_packet_job( - struct i2c_master_module *const module, - struct i2c_master_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - Assert(packet); - - /* Check if the I2C module is busy with a job */ - if (module->buffer_remaining > 0) { - return STATUS_BUSY; - } - - /* Make sure we send STOP */ - module->send_stop = true; - module->send_nack = true; - /* Start reading */ - return _i2c_master_read_packet(module, packet); -} - -/** - * \brief Initiates a read packet operation without sending a STOP condition when done - * - * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C bus without - * sending a stop condition, thus retaining ownership of the bus when done. - * To end the transaction, a \ref i2c_master_read_packet_wait "read" or - * \ref i2c_master_write_packet_wait "write" with stop condition must be - * performed. - * - * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting reading I<SUP>2</SUP>C packet. - * \retval STATUS_OK If reading was started successfully - * \retval STATUS_BUSY If module is currently busy with another operation - */ -enum status_code i2c_master_read_packet_job_no_stop( - struct i2c_master_module *const module, - struct i2c_master_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - Assert(packet); - - /* Check if the I2C module is busy with a job */ - if (module->buffer_remaining > 0) { - return STATUS_BUSY; - } - - /* Make sure we don't send STOP */ - module->send_stop = false; - module->send_nack = true; - /* Start reading */ - return _i2c_master_read_packet(module, packet); -} - -/** - * \brief Initiates a read packet operation without sending a NACK signal and a - * STOP condition when done - * - * Reads a data packet from the specified slave address on the I<SUP>2</SUP>C bus without - * sending a nack and a stop condition, thus retaining ownership of the bus when done. - * To end the transaction, a \ref i2c_master_read_packet_wait "read" or - * \ref i2c_master_write_packet_wait "write" with stop condition must be - * performed. - * - * This is the non-blocking equivalent of \ref i2c_master_read_packet_wait_no_stop. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting reading I<SUP>2</SUP>C packet. - * \retval STATUS_OK If reading was started successfully - * \retval STATUS_BUSY If module is currently busy with another operation - */ -enum status_code i2c_master_read_packet_job_no_nack( - struct i2c_master_module *const module, - struct i2c_master_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - Assert(packet); - - /* Check if the I2C module is busy with a job */ - if (module->buffer_remaining > 0) { - return STATUS_BUSY; - } - - /* Make sure we don't send STOP */ - module->send_stop = false; - module->send_nack = false; - /* Start reading */ - return _i2c_master_read_packet(module, packet); -} - -/** - * \internal - * Starts a write bytes operation. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting write I<SUP>2</SUP>C bytes. - * \retval STATUS_OK If writing was started successfully - * \retval STATUS_BUSY If module is currently busy with another transfer - */ -enum status_code i2c_master_write_bytes( - struct i2c_master_module *const module, - struct i2c_master_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - - SercomI2cm *const i2c_module = &(module->hw->I2CM); - - /* Save packet to software module */ - module->buffer = packet->data; - module->buffer_remaining = packet->data_length; - module->transfer_direction = I2C_TRANSFER_WRITE; - module->status = STATUS_BUSY; - module->send_stop = false; - module->send_nack = false; - - /* Enable interrupts */ - i2c_module->INTENSET.reg = - SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; - - return STATUS_OK; -} - -/** - * \internal Initiates a write packet operation - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting writing I<SUP>2</SUP>C packet job. - * \retval STATUS_OK If writing was started successfully - * \retval STATUS_BUSY If module is currently busy with another transfer - */ -static enum status_code _i2c_master_write_packet( - struct i2c_master_module *const module, - struct i2c_master_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - - SercomI2cm *const i2c_module = &(module->hw->I2CM); - - /* Switch to high speed mode */ - if (packet->high_speed) { - _i2c_master_send_hs_master_code(module, packet->hs_master_code); - } - - /* Set action to ACK. */ - i2c_module->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; - - /* Save packet to software module */ - module->buffer = packet->data; - module->buffer_remaining = packet->data_length; - module->transfer_direction = I2C_TRANSFER_WRITE; - module->status = STATUS_BUSY; - - /* Enable interrupts */ - i2c_module->INTENSET.reg = - SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB; - - /* Set address and direction bit, will send start command on bus */ - if (packet->ten_bit_address) { - i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | - (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos) | - SERCOM_I2CM_ADDR_TENBITEN; - } else { - i2c_module->ADDR.reg = (packet->address << 1) | I2C_TRANSFER_WRITE | - (packet->high_speed << SERCOM_I2CM_ADDR_HS_Pos); - } - - return STATUS_OK; -} - -/** - * \brief Initiates a write packet operation - * - * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C - * bus. This is the non-blocking equivalent of \ref i2c_master_write_packet_wait. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting writing I<SUP>2</SUP>C packet job. - * \retval STATUS_OK If writing was started successfully - * \retval STATUS_BUSY If module is currently busy with another transfer - */ -enum status_code i2c_master_write_packet_job( - struct i2c_master_module *const module, - struct i2c_master_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - Assert(packet); - - /* Check if the I2C module is busy with another job. */ - if (module->buffer_remaining > 0) { - return STATUS_BUSY; - } - - /* Make sure we send STOP at end*/ - module->send_stop = true; - module->send_nack = true; - /* Start write operation */ - return _i2c_master_write_packet(module, packet); -} - -/** - * \brief Initiates a write packet operation without sending a STOP condition when done - * - * Writes a data packet to the specified slave address on the I<SUP>2</SUP>C bus - * without sending a stop condition, thus retaining ownership of the bus when - * done. To end the transaction, a \ref i2c_master_read_packet_wait "read" or - * \ref i2c_master_write_packet_wait "write" with stop condition or sending - * a stop with the \ref i2c_master_send_stop function must be performed. - * - * This is the non-blocking equivalent of \ref i2c_master_write_packet_wait_no_stop. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting writing I<SUP>2</SUP>C packet job. - * \retval STATUS_OK If writing was started successfully - * \retval STATUS_BUSY If module is currently busy with another - */ -enum status_code i2c_master_write_packet_job_no_stop( - struct i2c_master_module *const module, - struct i2c_master_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - Assert(packet); - - /* Check if the I2C module is busy with another job. */ - if (module->buffer_remaining > 0) { - return STATUS_BUSY; - } - - /* Do not send stop condition when done */ - module->send_stop = false; - module->send_nack = true; - /* Start write operation */ - return _i2c_master_write_packet(module, packet); -} - -/** - * \internal - * Interrupt handler for I<SUP>2</SUP>C master. - * - * \param[in] instance SERCOM instance that triggered the interrupt - */ -void _i2c_master_interrupt_handler( - uint8_t instance) -{ - /* Get software module for callback handling */ - struct i2c_master_module *module = - (struct i2c_master_module*)_sercom_instances[instance]; - - Assert(module); - - SercomI2cm *const i2c_module = &(module->hw->I2CM); - bool sclsm_flag = i2c_module->CTRLA.bit.SCLSM; - - /* Combine callback registered and enabled masks */ - uint8_t callback_mask = module->enabled_callback; - callback_mask &= module->registered_callback; - - /* Check if the module should respond to address ack */ - if ((module->buffer_length <= 0) && (module->buffer_remaining > 0)) { - /* Call function for address response */ - _i2c_master_async_address_response(module); - - /* Check if buffer write is done */ - } else if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && - (module->status == STATUS_BUSY) && - (module->transfer_direction == I2C_TRANSFER_WRITE)) { - /* Stop packet operation */ - i2c_module->INTENCLR.reg = - SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; - - module->buffer_length = 0; - module->status = STATUS_OK; - - if (module->send_stop) { - /* Send stop condition */ - _i2c_master_wait_for_sync(module); - i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); - } else { - /* Clear write interrupt flag */ - i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_MB; - } - - if (callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) { - module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); - } - - /* Continue buffer write/read */ - } else if ((module->buffer_length > 0) && (module->buffer_remaining > 0)){ - /* Check that bus ownership is not lost */ - if ((!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(2))) && - (!(sclsm_flag && (module->buffer_remaining == 1)))) { - module->status = STATUS_ERR_PACKET_COLLISION; - } else if (module->transfer_direction == I2C_TRANSFER_WRITE) { - _i2c_master_write(module); - } else { - _i2c_master_read(module); - } - } - - /* Check if read buffer transfer is complete */ - if ((module->buffer_length > 0) && (module->buffer_remaining <= 0) && - (module->status == STATUS_BUSY) && - (module->transfer_direction == I2C_TRANSFER_READ)) { - - /* Clear read interrupt flag */ - if (i2c_module->INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB) { - i2c_module->INTFLAG.reg = SERCOM_I2CM_INTFLAG_SB; - } - /* Stop packet operation */ - i2c_module->INTENCLR.reg = - SERCOM_I2CM_INTENCLR_MB | SERCOM_I2CM_INTENCLR_SB; - module->buffer_length = 0; - module->status = STATUS_OK; - - /* Call appropriate callback if enabled and registered */ - if ((callback_mask & (1 << I2C_MASTER_CALLBACK_READ_COMPLETE)) - && (module->transfer_direction == I2C_TRANSFER_READ)) { - module->callbacks[I2C_MASTER_CALLBACK_READ_COMPLETE](module); - } else if ((callback_mask & (1 << I2C_MASTER_CALLBACK_WRITE_COMPLETE)) - && (module->transfer_direction == I2C_TRANSFER_WRITE)) { - module->callbacks[I2C_MASTER_CALLBACK_WRITE_COMPLETE](module); - } - } - - /* Check for error */ - if ((module->status != STATUS_BUSY) && (module->status != STATUS_OK)) { - /* Stop packet operation */ - i2c_module->INTENCLR.reg = SERCOM_I2CM_INTENCLR_MB | - SERCOM_I2CM_INTENCLR_SB; - - module->buffer_length = 0; - module->buffer_remaining = 0; - - /* Send nack and stop command unless arbitration is lost */ - if ((module->status != STATUS_ERR_PACKET_COLLISION) && - module->send_stop) { - _i2c_master_wait_for_sync(module); - i2c_module->CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | - SERCOM_I2CM_CTRLB_CMD(3); - } - - /* Call error callback if enabled and registered */ - if (callback_mask & (1 << I2C_MASTER_CALLBACK_ERROR)) { - module->callbacks[I2C_MASTER_CALLBACK_ERROR](module); - } - } -}
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/a280628a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c ---------------------------------------------------------------------- diff --git a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c b/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c deleted file mode 100755 index 4bb968d..0000000 --- a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave.c +++ /dev/null @@ -1,745 +0,0 @@ -/** - * \file - * - * \brief SAM I2C Slave Driver - * - * Copyright (C) 2013-2015 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_slave.h" -#if I2C_SLAVE_CALLBACK_MODE == true -# include "i2c_slave_interrupt.h" -#endif - -/** - * \internal Sets configuration 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 Module was configured correctly - * \retval STATUS_ERR_ALREADY_INITIALIZED If setting other GCLK generator than - * previously set - */ -static enum status_code _i2c_slave_set_config( - struct i2c_slave_module *const module, - const struct i2c_slave_config *const config) -{ - uint32_t tmp_ctrla; - - /* Sanity check arguments */ - Assert(module); - Assert(module->hw); - Assert(config); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - Sercom *const sercom_hw = module->hw; - - module->buffer_timeout = config->buffer_timeout; - module->ten_bit_address = config->ten_bit_address; - - 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); - - /* Prepare config to write to register CTRLA */ - if (config->run_in_standby || system_is_debugger_present()) { - tmp_ctrla = SERCOM_I2CS_CTRLA_RUNSTDBY; - } else { - tmp_ctrla = 0; - } - - /* Check and set SCL clock stretch mode. */ - if (config->scl_stretch_only_after_ack_bit || (config->transfer_speed == I2C_SLAVE_SPEED_HIGH_SPEED)) { - tmp_ctrla |= SERCOM_I2CM_CTRLA_SCLSM; - } - - tmp_ctrla |= ((uint32_t)config->sda_hold_time | - config->transfer_speed | - (config->scl_low_timeout << SERCOM_I2CS_CTRLA_LOWTOUTEN_Pos) | - (config->slave_scl_low_extend_timeout << SERCOM_I2CS_CTRLA_SEXTTOEN_Pos)); - - i2c_hw->CTRLA.reg |= tmp_ctrla; - - /* Set CTRLB configuration */ - i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_SMEN | config->address_mode; - - i2c_hw->ADDR.reg = config->address << SERCOM_I2CS_ADDR_ADDR_Pos | - config->address_mask << SERCOM_I2CS_ADDR_ADDRMASK_Pos | - config->ten_bit_address << SERCOM_I2CS_ADDR_TENBITEN_Pos | - config->enable_general_call_address << SERCOM_I2CS_ADDR_GENCEN_Pos; - - return STATUS_OK; -} - -/** - * \brief Initializes the requested I<SUP>2</SUP>C hardware module - * - * Initializes the SERCOM I<SUP>2</SUP>C slave 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 If setting other GCLK generator than - * previously set - */ -enum status_code i2c_slave_init( - struct i2c_slave_module *const module, - Sercom *const hw, - const struct i2c_slave_config *const config) -{ - /* Sanity check arguments */ - Assert(module); - Assert(hw); - Assert(config); - - /* Initialize software module */ - module->hw = hw; - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - /* Check if module is enabled */ - if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_ENABLE) { - return STATUS_ERR_DENIED; - } - - /* Check if reset is in progress */ - if (i2c_hw->CTRLA.reg & SERCOM_I2CS_CTRLA_SWRST) { - return STATUS_BUSY; - } - - uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); - uint32_t pm_index, gclk_index; -#if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) -#if (SAML21) - if (sercom_index == 5) { - pm_index = MCLK_APBDMASK_SERCOM5_Pos; - gclk_index = SERCOM5_GCLK_ID_CORE; - } else { - pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; - gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; - } -#else - pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; - gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; -#endif -#else - pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; - gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; -#endif - - /* Turn on module in PM */ -#if (SAML21) - if (sercom_index == 5) { - system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); - } else { - system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); - } -#else - system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); -#endif - - /* Set up the GCLK for the module */ - struct system_gclk_chan_config gclk_chan_conf; - system_gclk_chan_get_config_defaults(&gclk_chan_conf); - gclk_chan_conf.source_generator = config->generator_source; - system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); - system_gclk_chan_enable(gclk_index); - sercom_set_gclk_generator(config->generator_source, false); - -#if I2C_SLAVE_CALLBACK_MODE == true - /* Get sercom instance index */ - uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); - - /* Save software module in interrupt handler */ - _sercom_set_handler(instance_index, _i2c_slave_interrupt_handler); - - /* Save software module */ - _sercom_instances[instance_index] = module; - - /* Initialize values in module */ - module->registered_callback = 0; - module->enabled_callback = 0; - module->buffer_length = 0; - module->nack_on_address = config->enable_nack_on_address; -#endif - - /* Set SERCOM module to operate in I2C slave mode */ - i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_MODE(0x4); - - /* Set config and return status */ - return _i2c_slave_set_config(module, config); -} - -/** - * \brief Resets the hardware module - * - * This will reset the module to hardware defaults. - * - * \param[in,out] module Pointer to software module structure - */ -void i2c_slave_reset( - struct i2c_slave_module *const module) -{ - /* Sanity check arguments */ - Assert(module); - Assert(module->hw); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - -#if I2C_SLAVE_CALLBACK_MODE == true - /* Reset module instance */ - module->registered_callback = 0; - module->enabled_callback = 0; - module->buffer_length = 0; - module->buffer_remaining = 0; - module->buffer = NULL; -#endif - - /* Disable module */ - i2c_slave_disable(module); - -#if I2C_SLAVE_CALLBACK_MODE == true - /* Clear all pending interrupts */ - system_interrupt_enter_critical_section(); - system_interrupt_clear_pending(_sercom_get_interrupt_vector(module->hw)); - system_interrupt_leave_critical_section(); -#endif - - /* Wait for sync */ - _i2c_slave_wait_for_sync(module); - - /* Reset module */ - i2c_hw->CTRLA.reg = SERCOM_I2CS_CTRLA_SWRST; -} - -/** - * \internal Waits for answer on bus - * - * \param[in] module Pointer to software module structure - * - * \return Status of bus. - * \retval STATUS_OK If given response from slave device - * \retval STATUS_ERR_TIMEOUT If no response was given within specified timeout - * period - */ -static enum status_code _i2c_slave_wait_for_bus( - struct i2c_slave_module *const module) -{ - /* Sanity check arguments */ - Assert(module); - Assert(module->hw); - - SercomI2cm *const i2c_module = &(module->hw->I2CM); - - /* Wait for reply */ - uint16_t timeout_counter = 0; - while ((!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY)) && - (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC)) && - (!(i2c_module->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH))) { - - /* Check timeout condition */ - if (++timeout_counter >= module->buffer_timeout) { - return STATUS_ERR_TIMEOUT; - } - } - return STATUS_OK; -} - -/** - * \brief Writes a packet to the master - * - * Writes a packet to the master. This will wait for the master to issue - * a request. - * - * \param[in] module Pointer to software module structure - * \param[in] packet Packet to write to master - * - * \return Status of packet write. - * \retval STATUS_OK Packet was written successfully - * \retval STATUS_ERR_DENIED Start condition not received, another - * interrupt flag is set - * \retval STATUS_ERR_IO There was an error in the previous transfer - * \retval STATUS_ERR_BAD_FORMAT Master wants to write data - * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided - * \retval STATUS_ERR_BUSY The I<SUP>2</SUP>C module is busy with a job - * \retval STATUS_ERR_ERR_OVERFLOW Master NACKed before entire packet was - * transferred - * \retval STATUS_ERR_TIMEOUT No response was given within the timeout - * period - */ -enum status_code i2c_slave_write_packet_wait( - struct i2c_slave_module *const module, - struct i2c_slave_packet *const packet) -{ - /* Sanity check arguments */ - Assert(module); - Assert(module->hw); - Assert(packet); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - uint16_t length = packet->data_length; - - if (length == 0) { - return STATUS_ERR_INVALID_ARG; - } - -#if I2C_SLAVE_CALLBACK_MODE == true - /* Check if the module is busy with a job or AMATCH is enabled */ - if (module->buffer_remaining > 0 || - (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { - return STATUS_BUSY; - } -#endif - - enum status_code status; - /* Wait for master to send address packet */ - status = _i2c_slave_wait_for_bus(module); - - if (status != STATUS_OK) { - /* Timeout, return */ - return status; - } - if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { - /* Not address interrupt, something is wrong */ - return STATUS_ERR_DENIED; - } - - if (module->ten_bit_address) { - /* ACK the first address */ - i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); - - /* Wait for address interrupt */ - status = _i2c_slave_wait_for_bus(module); - - if (status != STATUS_OK) { - /* Timeout, return */ - return STATUS_ERR_TIMEOUT; - } - - if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { - /* Not address interrupt, something is wrong */ - return STATUS_ERR_DENIED; - } - } - - /* Check if there was an error in last transfer */ - if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | - SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { - return STATUS_ERR_IO; - } - - /* Check direction */ - if (!(i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { - /* Write request from master, send NACK and return */ - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); - return STATUS_ERR_BAD_FORMAT; - } - - /* Read request from master, ACK address */ - i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); - - uint16_t i = 0; - - /* Wait for data interrupt */ - status = _i2c_slave_wait_for_bus(module); - if (status != STATUS_OK) { - /* Timeout, return */ - return status; - } - - while (length--) { - /* Write data */ - _i2c_slave_wait_for_sync(module); - i2c_hw->DATA.reg = packet->data[i++]; - - /* Wait for response from master */ - status = _i2c_slave_wait_for_bus(module); - - if (status != STATUS_OK) { - /* Timeout, return */ - return status; - } - - if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK && - length !=0) { - /* NACK from master, abort */ - /* Release line */ - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); - - return STATUS_ERR_OVERFLOW; - } - /* ACK from master, continue writing */ - } - - /* Release line */ - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x02); - - return STATUS_OK; -} - -/** - * \brief Reads a packet from the master - * - * Reads a packet from the master. This will wait for the master to issue a - * request. - * - * \param[in] module Pointer to software module structure - * \param[out] packet Packet to read from master - * - * \return Status of packet read. - * \retval STATUS_OK Packet was read successfully - * \retval STATUS_ABORTED Master sent stop condition or repeated - * start before specified length of bytes - * was received - * \retval STATUS_ERR_IO There was an error in the previous transfer - * \retval STATUS_ERR_DENIED Start condition not received, another - * interrupt flag is set - * \retval STATUS_ERR_INVALID_ARG Invalid argument(s) was provided - * \retval STATUS_ERR_BUSY The I<SUP>2</SUP>C module is busy with a job - * \retval STATUS_ERR_BAD_FORMAT Master wants to read data - * \retval STATUS_ERR_ERR_OVERFLOW Last byte received overflows buffer - */ -enum status_code i2c_slave_read_packet_wait( - struct i2c_slave_module *const module, - struct i2c_slave_packet *const packet) -{ - /* Sanity check arguments */ - Assert(module); - Assert(module->hw); - Assert(packet); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - uint16_t length = packet->data_length; - - if (length == 0) { - return STATUS_ERR_INVALID_ARG; - } - -#if I2C_SLAVE_CALLBACK_MODE == true - /* Check if the module is busy with a job or AMATCH is enabled */ - if (module->buffer_remaining > 0 || - (i2c_hw->INTENSET.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { - return STATUS_BUSY; - } -#endif - - enum status_code status; - - /* Wait for master to send address packet */ - status = _i2c_slave_wait_for_bus(module); - if (status != STATUS_OK) { - /* Timeout, return */ - return status; - } - - if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { - /* Not address interrupt, something is wrong */ - return STATUS_ERR_DENIED; - } - - /* Check if there was an error in the last transfer */ - if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | - SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { - return STATUS_ERR_IO; - } - /* Check direction */ - if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { - /* Read request from master, send NACK and return */ - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); - return STATUS_ERR_BAD_FORMAT; - } - - /* Write request from master, ACK address */ - i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x3); - - uint16_t i = 0; - while (length--) { - - /* Wait for next byte or stop condition */ - status = _i2c_slave_wait_for_bus(module); - if (status != STATUS_OK) { - /* Timeout, return */ - return status; - } - - if ((i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) || - i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { - /* Master sent stop condition, or repeated start, read done */ - /* Clear stop flag */ - i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; - return STATUS_ABORTED; - } - - /* Read data */ - _i2c_slave_wait_for_sync(module); - packet->data[i++] = i2c_hw->DATA.reg; - - } - - /* Packet read done, wait for packet to NACK, Stop or repeated start */ - status = _i2c_slave_wait_for_bus(module); - - if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { - /* Buffer is full, send NACK */ - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); - } - if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { - /* Clear stop flag */ - i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; - } - return STATUS_OK; -} - -/** - * \brief Waits for a start condition on the bus - * - * \note This function is only available for 7-bit slave addressing. - * - * Waits for the master to issue a start condition on the bus. - * - * \note This function does not check for errors in the last transfer, - * this will be discovered when reading or writing. - * - * \param[in] module Pointer to software module structure - * - * \return Direction of the current transfer, when in slave mode. - * \retval I2C_SLAVE_DIRECTION_NONE No request from master within timeout - * period - * \retval I2C_SLAVE_DIRECTION_READ Write request from master - * \retval I2C_SLAVE_DIRECTION_WRITE Read request from master - */ -enum i2c_slave_direction i2c_slave_get_direction_wait( - struct i2c_slave_module *const module) -{ - /* Sanity check arguments */ - Assert(module); - Assert(module->hw); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - enum status_code status; - - /* Wait for address interrupt */ - status = _i2c_slave_wait_for_bus(module); - - if (status != STATUS_OK) { - /* Timeout, return */ - return I2C_SLAVE_DIRECTION_NONE; - } - - if (!(i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH)) { - /* Not address interrupt, something is wrong */ - return I2C_SLAVE_DIRECTION_NONE; - } - - /* Check direction */ - if ((i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR)) { - /* Read request from master */ - return I2C_SLAVE_DIRECTION_WRITE; - } else { - /* Write request from master */ - return I2C_SLAVE_DIRECTION_READ; - } -} - -/** - * \brief Retrieves the current module status - * - * Checks the status of the module and returns it as a bitmask of status - * flags. - * - * \param[in] module Pointer to the I<SUP>2</SUP>C slave software device struct - * - * \return Bitmask of status flags. - * - * \retval I2C_SLAVE_STATUS_ADDRESS_MATCH A valid address has been received - * \retval I2C_SLAVE_STATUS_DATA_READY A I<SUP>2</SUP>C slave byte transmission is - * successfully completed - * \retval I2C_SLAVE_STATUS_STOP_RECEIVED A stop condition is detected for a - * transaction being processed - * \retval I2C_SLAVE_STATUS_CLOCK_HOLD The slave is holding the SCL line - * low - * \retval I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT An SCL low time-out has occurred - * \retval I2C_SLAVE_STATUS_REPEATED_START Indicates a repeated start, only - * valid if \ref - * I2C_SLAVE_STATUS_ADDRESS_MATCH is - * set - * \retval I2C_SLAVE_STATUS_RECEIVED_NACK The last data packet sent was not - * acknowledged - * \retval I2C_SLAVE_STATUS_COLLISION The I<SUP>2</SUP>C slave was not able to - * transmit a high data or NACK bit - * \retval I2C_SLAVE_STATUS_BUS_ERROR An illegal bus condition has - * occurred on the bus - */ -uint32_t i2c_slave_get_status( - struct i2c_slave_module *const module) -{ - /* Sanity check arguments */ - Assert(module); - Assert(module->hw); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - uint8_t intflags = i2c_hw->INTFLAG.reg; - uint8_t status = i2c_hw->STATUS.reg; - uint32_t status_flags = 0; - - /* Check Address Match flag */ - if (intflags & SERCOM_I2CS_INTFLAG_AMATCH) { - status_flags |= I2C_SLAVE_STATUS_ADDRESS_MATCH; - } - /* Check Data Ready flag */ - if (intflags & SERCOM_I2CS_INTFLAG_DRDY) { - status_flags |= I2C_SLAVE_STATUS_DATA_READY; - } - /* Check Stop flag */ - if (intflags & SERCOM_I2CS_INTFLAG_PREC) { - status_flags |= I2C_SLAVE_STATUS_STOP_RECEIVED; - } - /* Check Clock Hold */ - if (status & SERCOM_I2CS_STATUS_CLKHOLD) { - status_flags |= I2C_SLAVE_STATUS_CLOCK_HOLD; - } - /* Check SCL Low Timeout */ - if (status & SERCOM_I2CS_STATUS_LOWTOUT) { - status_flags |= I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT; - } - /* Check Repeated Start */ - if (status & SERCOM_I2CS_STATUS_SR) { - status_flags |= I2C_SLAVE_STATUS_REPEATED_START; - } - /* Check Received Not Acknowledge */ - if (status & SERCOM_I2CS_STATUS_RXNACK) { - status_flags |= I2C_SLAVE_STATUS_RECEIVED_NACK; - } - /* Check Transmit Collision */ - if (status & SERCOM_I2CS_STATUS_COLL) { - status_flags |= I2C_SLAVE_STATUS_COLLISION; - } - /* Check Bus Error */ - if (status & SERCOM_I2CS_STATUS_BUSERR) { - status_flags |= I2C_SLAVE_STATUS_BUS_ERROR; - } - - return status_flags; -} - -/** - * \brief Clears a module status flag - * - * Clears the given status flag of the module. - * - * \note Not all status flags can be cleared. - * - * \param[in] module Pointer to the I<SUP>2</SUP>C software device struct - * \param[in] status_flags Bit mask of status flags to clear - * - */ -void i2c_slave_clear_status( - struct i2c_slave_module *const module, - uint32_t status_flags) -{ - /* Sanity check arguments */ - Assert(module); - Assert(module->hw); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - /* Clear Address Match flag */ - if (status_flags & I2C_SLAVE_STATUS_ADDRESS_MATCH) { - i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; - } - /* Clear Data Ready flag */ - if (status_flags & I2C_SLAVE_STATUS_DATA_READY) { - i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_DRDY; - } - /* Clear Stop flag */ - if (status_flags & I2C_SLAVE_STATUS_STOP_RECEIVED) { - i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; - } - /* Clear SCL Low Timeout */ - if (status_flags & I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT) { - i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_LOWTOUT; - } - /* Clear Transmit Collision */ - if (status_flags & I2C_SLAVE_STATUS_COLLISION) { - i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_COLL; - } - /* Clear Bus Error */ - if (status_flags & I2C_SLAVE_STATUS_BUS_ERROR) { - i2c_hw->STATUS.reg = SERCOM_I2CS_STATUS_BUSERR; - } -} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/a280628a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c ---------------------------------------------------------------------- diff --git a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c b/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c deleted file mode 100755 index 0a6f234..0000000 --- a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_sam0/i2c_slave_interrupt.c +++ /dev/null @@ -1,434 +0,0 @@ -/** - * \file - * - * \brief SAM I2C Slave Interrupt Driver - * - * Copyright (c) 2013-2015 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_slave_interrupt.h" - -/** - * \brief Enables sending of NACK on address match - * - * Enables sending of NACK on address match, thus discarding - * any incoming transaction. - * - * \param[in,out] module Pointer to software module structure - */ -void i2c_slave_enable_nack_on_address( - struct i2c_slave_module *const module) -{ - /* Sanity check arguments. */ - Assert(module); - - module->nack_on_address = true; -} - -/** - * \brief Disables sending NACK on address match - * - * Disables sending of NACK on address match, thus - * acknowledging incoming transactions. - * - * \param[in,out] module Pointer to software module structure - */ -void i2c_slave_disable_nack_on_address( - struct i2c_slave_module *const module) -{ - /* Sanity check arguments. */ - Assert(module); - - module->nack_on_address = false; -} - -/** - * \internal - * Reads next data. Used by interrupt handler to get next data byte from master. - * - * \param[in,out] module Pointer to software module structure - */ -static void _i2c_slave_read( - struct i2c_slave_module *const module) -{ - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - /* Read byte from master and put in buffer. */ - *(module->buffer++) = i2c_hw->DATA.reg; - - /*Decrement remaining buffer length */ - module->buffer_remaining--; -} - -/** - * \internal - * Writes next data. Used by interrupt handler to send next data byte to master. - * - * \param[in,out] module Pointer to software module structure - */ -static void _i2c_slave_write( - struct i2c_slave_module *const module) -{ - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - /* Write byte from buffer to master */ - i2c_hw->DATA.reg = *(module->buffer++); - - /*Decrement remaining buffer length */ - module->buffer_remaining--; -} - -/** - * \brief Registers callback for the specified callback type - * - * Associates the given callback function with the - * specified callback type. To enable the callback, the - * \ref i2c_slave_enable_callback function must be used. - * - * \param[in,out] module Pointer to the software module struct - * \param[in] callback Pointer to the function desired for the - * specified callback - * \param[in] callback_type Callback type to register - */ -void i2c_slave_register_callback( - struct i2c_slave_module *const module, - i2c_slave_callback_t callback, - enum i2c_slave_callback callback_type) -{ - /* Sanity check. */ - Assert(module); - Assert(module->hw); - Assert(callback); - - /* Register callback. */ - module->callbacks[callback_type] = callback; - - /* Set corresponding bit to set callback as initiated. */ - module->registered_callback |= (1 << callback_type); -} - -/** - * \brief Unregisters callback for the specified callback type - * - * Removes the currently registered callback for the given callback - * type. - * - * \param[in,out] module Pointer to the software module struct - * \param[in] callback_type Callback type to unregister - */ -void i2c_slave_unregister_callback( - struct i2c_slave_module *const module, - enum i2c_slave_callback callback_type) -{ - /* Sanity check. */ - Assert(module); - Assert(module->hw); - - /* Register callback. */ - module->callbacks[callback_type] = NULL; - - /* Set corresponding bit to set callback as initiated. */ - module->registered_callback &= ~(1 << callback_type); -} - -/** - * \brief Initiates a reads packet operation - * - * Reads a data packet from the master. A write request must be initiated by - * the master before the packet can be read. - * - * The \ref I2C_SLAVE_CALLBACK_WRITE_REQUEST callback can be used to call this - * function. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting asynchronously reading I<SUP>2</SUP>C packet. - * \retval STATUS_OK If reading was started successfully - * \retval STATUS_BUSY If module is currently busy with another transfer - */ -enum status_code i2c_slave_read_packet_job( - struct i2c_slave_module *const module, - struct i2c_slave_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - Assert(packet); - - /* Check if the I2C module is busy doing async operation. */ - if (module->buffer_remaining > 0) { - return STATUS_BUSY; - } - - /* Save packet to software module. */ - module->buffer = packet->data; - module->buffer_remaining = packet->data_length; - module->buffer_length = packet->data_length; - module->status = STATUS_BUSY; - - /* Enable interrupts */ - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | - SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; - - /* Read will begin when master initiates the transfer */ - return STATUS_OK; -} - -/** - * \brief Initiates a write packet operation - * - * Writes a data packet to the master. A read request must be initiated by - * the master before the packet can be written. - * - * The \ref I2C_SLAVE_CALLBACK_READ_REQUEST callback can be used to call this - * function. - * - * \param[in,out] module Pointer to software module struct - * \param[in,out] packet Pointer to I<SUP>2</SUP>C packet to transfer - * - * \return Status of starting writing I<SUP>2</SUP>C packet. - * \retval STATUS_OK If writing was started successfully - * \retval STATUS_BUSY If module is currently busy with another transfer - */ -enum status_code i2c_slave_write_packet_job( - struct i2c_slave_module *const module, - struct i2c_slave_packet *const packet) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - Assert(packet); - - /* Check if the I2C module is busy doing async operation. */ - if (module->buffer_remaining > 0) { - return STATUS_BUSY; - } - - /* Save packet to software module. */ - module->buffer = packet->data; - module->buffer_remaining = packet->data_length; - module->buffer_length = packet->data_length; - module->status = STATUS_BUSY; - - /* Enable interrupts */ - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - i2c_hw->INTENSET.reg = SERCOM_I2CS_INTFLAG_AMATCH | - SERCOM_I2CS_INTFLAG_DRDY | SERCOM_I2CS_INTFLAG_PREC; - - return STATUS_OK; -} - -/** - * \internal Interrupt handler for I<SUP>2</SUP>C slave - * - * \param[in] instance Sercom instance that triggered the interrupt - */ -void _i2c_slave_interrupt_handler( - uint8_t instance) -{ - /* Get software module for callback handling. */ - struct i2c_slave_module *module = - (struct i2c_slave_module*)_sercom_instances[instance]; - - Assert(module); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - /* Combine callback registered and enabled masks. */ - uint8_t callback_mask = module->enabled_callback; - callback_mask &= module->registered_callback; - - - if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_AMATCH) { - /* Address match */ - /* Check if last transfer is done - repeated start */ - if (module->buffer_length != module->buffer_remaining && - module->transfer_direction == I2C_TRANSFER_WRITE) { - - module->status = STATUS_OK; - module->buffer_length = 0; - module->buffer_remaining = 0; - - if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE))) { - module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); - } - } else if (module->buffer_length != module->buffer_remaining && - module->transfer_direction == I2C_TRANSFER_READ) { - module->status = STATUS_OK; - module->buffer_length = 0; - module->buffer_remaining = 0; - - if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE))) { - module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); - } - } - - if (i2c_hw->STATUS.reg & (SERCOM_I2CS_STATUS_BUSERR | - SERCOM_I2CS_STATUS_COLL | SERCOM_I2CS_STATUS_LOWTOUT)) { - /* An error occurred in last packet transfer */ - module->status = STATUS_ERR_IO; - - if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER))) { - module->callbacks[I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER](module); - } - } - if (module->nack_on_address) { - /* NACK address, workaround 13574 */ - _i2c_slave_set_ctrlb_ackact(module, false); - } else if (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_DIR) { - /* Set transfer direction in module instance */ - module->transfer_direction = I2C_TRANSFER_READ; - - /* Read request from master */ - if (callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) { - module->callbacks[I2C_SLAVE_CALLBACK_READ_REQUEST](module); - } - - if (module->buffer_length == 0) { - /* Data buffer not set up, NACK address, workaround 13574*/ - _i2c_slave_set_ctrlb_ackact(module, false); - } else { - /* ACK address, workaround 13574 */ - _i2c_slave_set_ctrlb_ackact(module, true); - } - } else { - /* Set transfer direction in dev inst */ - module->transfer_direction = I2C_TRANSFER_WRITE; - - /* Write request from master */ - if (callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)) { - module->callbacks[I2C_SLAVE_CALLBACK_WRITE_REQUEST](module); - } - - if (module->buffer_length == 0) { - /* Data buffer not set up, NACK address, workaround 13574 */ - _i2c_slave_set_ctrlb_ackact(module, false); - } else { - /* ACK address, workaround 13574 */ - _i2c_slave_set_ctrlb_ackact(module, true); - } - } - - /* ACK or NACK address, Workaround 13574 */ - _i2c_slave_set_ctrlb_cmd3(module); - - /* ACK next incoming packet, workaround 13574 */ - _i2c_slave_set_ctrlb_ackact(module, true); - - } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_PREC) { - /* Stop condition on bus - current transfer done */ - - /* Clear Stop interrupt */ - i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; - - /* Disable interrupts */ - i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_PREC | SERCOM_I2CS_INTFLAG_DRDY; - - if (!((module->enabled_callback & (1 << I2C_SLAVE_CALLBACK_READ_REQUEST)) - || (module->enabled_callback == (1 << I2C_SLAVE_CALLBACK_WRITE_REQUEST)))) { - /* Disable address match if read/write request is not enabled */ - i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_AMATCH; - } - - if (!(module->status == STATUS_ERR_OVERFLOW || module->status == STATUS_ERR_IO)) { - module->status = STATUS_OK; - module->buffer_length = 0; - module->buffer_remaining = 0; - - /* Call appropriate callback if enabled and registered */ - if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_READ_COMPLETE)) - && (module->transfer_direction == I2C_TRANSFER_WRITE)) { - /* Read from master complete */ - module->callbacks[I2C_SLAVE_CALLBACK_READ_COMPLETE](module); - } else if ((callback_mask & (1 << I2C_SLAVE_CALLBACK_WRITE_COMPLETE)) - && (module->transfer_direction == I2C_TRANSFER_READ)) { - /* Write to master complete */ - module->callbacks[I2C_SLAVE_CALLBACK_WRITE_COMPLETE](module); - } - } - } else if (i2c_hw->INTFLAG.reg & SERCOM_I2CS_INTFLAG_DRDY) { - /* Check if buffer is full, or NACK from master */ - if (module->buffer_remaining <= 0 || - (module->transfer_direction == I2C_TRANSFER_READ && - (module->buffer_length > module->buffer_remaining) && - (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_RXNACK))) { - - module->buffer_remaining = 0; - module->buffer_length = 0; - - if (module->transfer_direction == I2C_TRANSFER_WRITE) { - /* Buffer is full, send NACK, workaround 13574 */ - _i2c_slave_set_ctrlb_ackact(module, false); - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); - - /* Set status, new character in DATA register will overflow - * buffer */ - module->status = STATUS_ERR_OVERFLOW; - - if (callback_mask & (1 << I2C_SLAVE_CALLBACK_ERROR)) { - /* Read complete */ - module->callbacks[I2C_SLAVE_CALLBACK_ERROR](module); - } - } else { - /* Release SCL and wait for new start condition */ - _i2c_slave_set_ctrlb_ackact(module, false); - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_CMD(0x2); - - /* Transfer successful */ - module->status = STATUS_OK; - - /* Disable interrupts */ - i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTFLAG_DRDY; - } - - /* Continue buffer write/read */ - } else if (module->buffer_length > 0 && module->buffer_remaining > 0) { - /* Call function based on transfer direction */ - if (module->transfer_direction == I2C_TRANSFER_WRITE) { - _i2c_slave_read(module); - } else { - _i2c_slave_write(module); - } - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-mynewt-larva/blob/a280628a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_slave.h ---------------------------------------------------------------------- diff --git a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_slave.h b/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_slave.h deleted file mode 100755 index 239fb9d..0000000 --- a/hw/mcu/atmel/samd21xx/src/sam0/drivers/sercom/i2c/i2c_slave.h +++ /dev/null @@ -1,753 +0,0 @@ -/** - * \file - * - * \brief SAM SERCOM I2C Slave Driver - * - * Copyright (c) 2013-2015 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> - */ - -#ifndef I2C_SLAVE_H_INCLUDED -#define I2C_SLAVE_H_INCLUDED - -#include "i2c_common.h" -#include <sercom.h> -#include <pinmux.h> - -#if I2C_SLAVE_CALLBACK_MODE == true -# include <sercom_interrupt.h> -#endif - -#ifndef PINMUX_DEFAULT -# define PINMUX_DEFAULT 0 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * \addtogroup asfdoc_sam0_sercom_i2c_group - * - * @{ - * - */ - -/** - * \name I2C Slave Status Flags - * - * I<SUP>2</SUP>C slave status flags, returned by \ref i2c_slave_get_status() and cleared - * by \ref i2c_slave_clear_status(). - * @{ - */ - -/** Address Match. - * \note Should only be cleared internally by driver. - */ -#define I2C_SLAVE_STATUS_ADDRESS_MATCH (1UL << 0) -/** Data Ready. */ -#define I2C_SLAVE_STATUS_DATA_READY (1UL << 1) -/** Stop Received. */ -#define I2C_SLAVE_STATUS_STOP_RECEIVED (1UL << 2) -/** Clock Hold. - * \note Cannot be cleared, only valid when I2C_SLAVE_STATUS_ADDRESS_MATCH is - * set. - */ -#define I2C_SLAVE_STATUS_CLOCK_HOLD (1UL << 3) -/** SCL Low Timeout. */ -#define I2C_SLAVE_STATUS_SCL_LOW_TIMEOUT (1UL << 4) -/** Repeated Start. - * \note Cannot be cleared, only valid when I2C_SLAVE_STATUS_ADDRESS_MATCH is - * set. - */ -#define I2C_SLAVE_STATUS_REPEATED_START (1UL << 5) -/** Received not acknowledge. - * \note Cannot be cleared. - */ -#define I2C_SLAVE_STATUS_RECEIVED_NACK (1UL << 6) -/** Transmit Collision. */ -#define I2C_SLAVE_STATUS_COLLISION (1UL << 7) -/** Bus error. */ -#define I2C_SLAVE_STATUS_BUS_ERROR (1UL << 8) - -/** @} */ - -/** - * \brief I<SUP>2</SUP>C slave packet for read/write - * - * Structure to be used when transferring I<SUP>2</SUP>C slave packets. - */ -struct i2c_slave_packet { - /** Length of data array */ - uint16_t data_length; - /** Data array containing all data to be transferred */ - uint8_t *data; -}; - -#if I2C_SLAVE_CALLBACK_MODE == true - /** - * \brief Callback types - * - * The available callback types for the I<SUP>2</SUP>C slave. - */ -enum i2c_slave_callback { - /** Callback for packet write complete */ - I2C_SLAVE_CALLBACK_WRITE_COMPLETE, - /** Callback for packet read complete */ - I2C_SLAVE_CALLBACK_READ_COMPLETE, - /** - * Callback for read request from master - can be used to - * issue a write - */ - I2C_SLAVE_CALLBACK_READ_REQUEST, - /** - * Callback for write request from master - can be used to issue a read - */ - I2C_SLAVE_CALLBACK_WRITE_REQUEST, - /** Callback for error */ - I2C_SLAVE_CALLBACK_ERROR, - /** - * Callback for error in last transfer. Discovered on a new address - * interrupt. - */ - I2C_SLAVE_CALLBACK_ERROR_LAST_TRANSFER, -# if !defined(__DOXYGEN__) - /** Total number of callbacks */ - _I2C_SLAVE_CALLBACK_N, -# endif -}; - -# if !defined(__DOXYGEN__) -/** Software module prototype */ -struct i2c_slave_module; - -/** Callback type */ -typedef void (*i2c_slave_callback_t)( - struct i2c_slave_module *const module); -# endif -#endif - -/** - * \brief Enum for the possible SDA hold times with respect to the negative - * edge of SCL - * - * Enum for the possible SDA hold times with respect to the negative edge - * of SCL. - */ -enum i2c_slave_sda_hold_time { - /** SDA hold time disabled */ - I2C_SLAVE_SDA_HOLD_TIME_DISABLED = - ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((0) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), - /** SDA hold time 50ns - 100ns */ - I2C_SLAVE_SDA_HOLD_TIME_50NS_100NS = - ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((1) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), - /** SDA hold time 300ns - 600ns */ - I2C_SLAVE_SDA_HOLD_TIME_300NS_600NS = - ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((2) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), - /** SDA hold time 400ns - 800ns */ - I2C_SLAVE_SDA_HOLD_TIME_400NS_800NS = - ((SERCOM_I2CS_CTRLA_SDAHOLD_Msk & ((3) << SERCOM_I2CS_CTRLA_SDAHOLD_Pos))), -}; - -/** - * \brief Enum for the possible address modes - * - * Enum for the possible address modes. - */ -enum i2c_slave_address_mode { - /** Address match on address_mask used as a mask to address */ - I2C_SLAVE_ADDRESS_MODE_MASK = SERCOM_I2CS_CTRLB_AMODE(0), - /** Address math on both address and address_mask */ - I2C_SLAVE_ADDRESS_MODE_TWO_ADDRESSES = SERCOM_I2CS_CTRLB_AMODE(1), - /** - * Address match on range of addresses between and including address and - * address_mask - */ - I2C_SLAVE_ADDRESS_MODE_RANGE = SERCOM_I2CS_CTRLB_AMODE(2), -}; - -/** - * \brief Enum for the direction of a request - * - * Enum for the direction of a request. - */ -enum i2c_slave_direction { - /** Read */ - I2C_SLAVE_DIRECTION_READ, - /** Write */ - I2C_SLAVE_DIRECTION_WRITE, - /** No direction */ - I2C_SLAVE_DIRECTION_NONE, -}; - -#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED -/** - * \brief Enum for the transfer speed - * - * Enum for the transfer speed. - */ -enum i2c_slave_transfer_speed { - /** Standard-mode (Sm) up to 100KHz and Fast-mode (Fm) up to 400KHz */ - I2C_SLAVE_SPEED_STANDARD_AND_FAST = SERCOM_I2CS_CTRLA_SPEED(0), - /** Fast-mode Plus (Fm+) up to 1MHz */ - I2C_SLAVE_SPEED_FAST_MODE_PLUS = SERCOM_I2CS_CTRLA_SPEED(1), - /** High-speed mode (Hs-mode) up to 3.4MHz */ - I2C_SLAVE_SPEED_HIGH_SPEED = SERCOM_I2CS_CTRLA_SPEED(2), -}; -#endif - -/** - * \brief SERCOM I<SUP>2</SUP>C slave driver software device instance structure. - * - * SERCOM I<SUP>2</SUP>C slave driver software instance structure, used to - * retain software state information of an associated hardware module instance. - * - * \note The fields of this structure should not be altered by the user - * application; they are reserved for module-internal use only. - */ -struct i2c_slave_module { -#if !defined(__DOXYGEN__) - /** Hardware instance initialized for the struct */ - Sercom *hw; - /** Module lock */ - volatile bool locked; - /** Timeout value for polled functions */ - uint16_t buffer_timeout; -# ifdef FEATURE_I2C_10_BIT_ADDRESS - /** Using 10-bit addressing for the slave */ - bool ten_bit_address; -# endif -# if I2C_SLAVE_CALLBACK_MODE == true - /** Nack on address match */ - bool nack_on_address; - /** Pointers to callback functions */ - volatile i2c_slave_callback_t callbacks[_I2C_SLAVE_CALLBACK_N]; - /** Mask for registered callbacks */ - volatile uint8_t registered_callback; - /** Mask for enabled callbacks */ - volatile uint8_t enabled_callback; - /** The total number of bytes to transfer */ - volatile uint16_t buffer_length; - /** - * Counter used for bytes left to send in write and to count number of - * obtained bytes in read - */ - uint16_t buffer_remaining; - /** Data buffer for packet write and read */ - volatile uint8_t *buffer; - /** Save direction of request from master. 1 = read, 0 = write. */ - volatile enum i2c_transfer_direction transfer_direction; - /** Status for status read back in error callback */ - volatile enum status_code status; -# endif -#endif -}; - -/** - * \brief Configuration structure for the I<SUP>2</SUP>C slave device - * - * This is the configuration structure for the I<SUP>2</SUP>C slave device. It is used - * as an argument for \ref i2c_slave_init to provide the desired - * configurations for the module. The structure should be initialized using the - * \ref i2c_slave_get_config_defaults. - */ -struct i2c_slave_config { - /** Set to enable the SCL low timeout */ - bool enable_scl_low_timeout; - /** SDA hold time with respect to the negative edge of SCL */ - enum i2c_slave_sda_hold_time sda_hold_time; - /** Timeout to wait for master in polled functions */ - uint16_t buffer_timeout; - /** Addressing mode */ - enum i2c_slave_address_mode address_mode; - /** Address or upper limit of address range */ - uint16_t address; - /** Address mask, second address, or lower limit of address range */ - uint16_t address_mask; -#ifdef FEATURE_I2C_10_BIT_ADDRESS - /** Enable 10-bit addressing */ - bool ten_bit_address; -#endif - /** - * Enable general call address recognition (general call address - * is defined as 0000000 with direction bit 0). - */ - bool enable_general_call_address; - -#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED - /** Transfer speed mode */ - enum i2c_slave_transfer_speed transfer_speed; -#endif - -#if I2C_SLAVE_CALLBACK_MODE == true - /** - * Enable NACK on address match (this can be changed after initialization - * via the \ref i2c_slave_enable_nack_on_address and - * \ref i2c_slave_disable_nack_on_address functions). - */ - bool enable_nack_on_address; -#endif - /** GCLK generator to use as clock source */ - enum gclk_generator generator_source; - /** Set to keep module active in sleep modes */ - bool run_in_standby; - /** PAD0 (SDA) pinmux */ - uint32_t pinmux_pad0; - /** PAD1 (SCL) pinmux */ - uint32_t pinmux_pad1; - /** Set to enable SCL low time-out */ - bool scl_low_timeout; -#ifdef FEATURE_I2C_SCL_STRETCH_MODE - /** Set to enable SCL stretch only after ACK bit (required for high speed) */ - bool scl_stretch_only_after_ack_bit; -#endif -#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT - /** Set to enable slave SCL low extend time-out */ - bool slave_scl_low_extend_timeout; -#endif -}; - - -/** - * \name Lock/Unlock - * @{ - */ - -/** - * \brief Attempt to get lock on driver instance - * - * This function checks the instance's lock, which indicates whether or not it - * is currently in use, and sets the lock if it was not already set. - * - * The purpose of this is to enable exclusive access to driver instances, so - * that, e.g., transactions by different services will not interfere with each - * other. - * - * \param[in,out] module Pointer to the driver instance to lock - * - * \retval STATUS_OK If the module was locked - * \retval STATUS_BUSY If the module was already locked - */ -static inline enum status_code i2c_slave_lock( - struct i2c_slave_module *const module) -{ - enum status_code status; - - system_interrupt_enter_critical_section(); - - if (module->locked) { - status = STATUS_BUSY; - } else { - module->locked = true; - status = STATUS_OK; - } - - system_interrupt_leave_critical_section(); - - return status; -} - -/** - * \brief Unlock driver instance - * - * This function clears the instance lock, indicating that it is available for - * use. - * - * \param[in,out] module Pointer to the driver instance to lock - * - * \retval STATUS_OK If the module was locked - * \retval STATUS_BUSY If the module was already locked - */ -static inline void i2c_slave_unlock(struct i2c_slave_module *const module) -{ - module->locked = false; -} - -/** @} */ - -/** - * \name Configuration and Initialization - * @{ - */ - -/** - * \brief Returns the synchronization status of the module - * - * Returns the synchronization status of the module. - * - * \param[out] module Pointer to software module structure - * - * \return Status of the synchronization. - * \retval true Module is busy synchronizing - * \retval false Module is not synchronizing - */ -static inline bool i2c_slave_is_syncing( - const struct i2c_slave_module *const module) -{ - /* Sanity check */ - Assert(module); - Assert(module->hw); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - - /* Return sync status */ -#if defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_1) - return (i2c_hw->STATUS.reg & SERCOM_I2CS_STATUS_SYNCBUSY); -#elif defined(FEATURE_SERCOM_SYNCBUSY_SCHEME_VERSION_2) - return (i2c_hw->SYNCBUSY.reg & SERCOM_I2CS_SYNCBUSY_MASK); -#else -# error Unknown SERCOM SYNCBUSY scheme! -#endif -} - -#if !defined(__DOXYGEN__) -/** - * \internal Wait for hardware module to sync - * - * \param[in] module Pointer to software module structure - */ -static void _i2c_slave_wait_for_sync( - const struct i2c_slave_module *const module) -{ - /* Sanity check */ - Assert(module); - - while (i2c_slave_is_syncing(module)) { - /* Wait for I<SUP>2</SUP>C module to sync */ - } -} -#endif - -///@cond INTERNAL -/** - * \internal Workaround for errata 13574 - * Instead set ACK/NACK of CTRLB - * - * This errata exist in part revisions of SAM D20/D21/D09 - * D10/D11/L21/L22/DA1/C20/C21, but workaround can be works in all - * revision of those device. As this function operation - * should be use less cpu time as possible, so caller - * function can ignore to check revision number, and use - * this workaround in all revision of those device. - * - * \param[in,out] module Pointer to software module structure - * \param[in] send_ack true send ACK, false send NACK - */ -static inline void _i2c_slave_set_ctrlb_ackact( - struct i2c_slave_module *const module, - bool send_ack) -{ - Assert(module); - Assert(module->hw); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - -#if (SAMD20 || SAMD21 || SAMD09 || SAMD10 || SAMD11 || SAML21 || SAMDA1 || \ - SAML22 || SAMC20 || SAMC21) - /* Workaround, Following two write are atomic */ - system_interrupt_enter_critical_section(); - i2c_hw->STATUS.reg = 0; - - if (send_ack == true) { - i2c_hw->CTRLB.reg = 0; - } - else { - i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_ACKACT; - } - system_interrupt_leave_critical_section(); -#else - /* Normal operation */ - if (send_ack == true) { - i2c_hw->CTRLB.reg &= ~SERCOM_I2CS_CTRLB_ACKACT; - } - else { - i2c_hw->CTRLB.reg |= SERCOM_I2CS_CTRLB_ACKACT; - } -#endif - return; -} - -/** - * \internal Workaround for SAM0 errata 13574, - * instead Set CMD3 of CTRLB - * - * This errata exist in part revisions of SAM D20/D21/D09 - * D10/D11/L21/DA1/C20/C21, but workaround can be works in all - * revision of those device. As this function operation - * should be use less cpu time as possible, so caller - * function can ignore to check revision number, and use - * this workaround in all revision of those device. - * - * \param[in,out] module Pointer to software module structure - */ -static inline void _i2c_slave_set_ctrlb_cmd3( - struct i2c_slave_module *const module) -{ - Assert(module); - Assert(module->hw); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - -#if (SAMD20 || SAMD21 || SAMD09 || SAMD10 || SAMD11 || SAML21 || SAML22 || \ - SAMDA1 || SAMC20 || SAMC21) - /* Workaround */ - /* - * Below code instead i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_CMD(0x3); - * CMD=0x3 clears all interrupts, so to keep the result similar - * PREC is cleared if it was set - */ - if (i2c_hw->INTFLAG.bit.PREC) { - i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC; - } - i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_AMATCH; -#else - /* Normal operation */ - i2c_hw->CTRLB.reg = SERCOM_I2CS_CTRLB_CMD(0x3); -#endif - return; -} -///@endcond - -/** - * \brief Gets the I<SUP>2</SUP>C slave default configurations - * - * This will initialize the configuration structure to known default values. - * - * The default configuration is as follows: - * - Disable SCL low timeout - * - 300ns - 600ns SDA hold time - * - Buffer timeout = 65535 - * - Address with mask - * - Address = 0 - * - Address mask = 0 (one single address) - * - General call address disabled - * - Address nack disabled if the interrupt driver is used - * - GCLK generator 0 - * - Do not run in standby - * - PINMUX_DEFAULT for SERCOM pads - * - * Those default configuration only available if the device supports it: - * - Not using 10-bit addressing - * - Standard-mode and Fast-mode transfer speed - * - SCL stretch disabled - * - Slave SCL low extend time-out disabled - * - * \param[out] config Pointer to configuration structure to be initialized - */ -static inline void i2c_slave_get_config_defaults( - struct i2c_slave_config *const config) -{ - /*Sanity check argument */ - Assert(config); - config->enable_scl_low_timeout = false; - config->sda_hold_time = I2C_SLAVE_SDA_HOLD_TIME_300NS_600NS; - config->buffer_timeout = 65535; - config->address_mode = I2C_SLAVE_ADDRESS_MODE_MASK; - config->address = 0; - config->address_mask = 0; -#ifdef FEATURE_I2C_10_BIT_ADDRESS - config->ten_bit_address = false; -#endif - config->enable_general_call_address = false; -#ifdef FEATURE_I2C_FAST_MODE_PLUS_AND_HIGH_SPEED - config->transfer_speed = I2C_SLAVE_SPEED_STANDARD_AND_FAST; -#endif -#if I2C_SLAVE_CALLBACK_MODE == true - config->enable_nack_on_address = false; -#endif - config->generator_source = GCLK_GENERATOR_0; - config->run_in_standby = false; - config->pinmux_pad0 = PINMUX_DEFAULT; - config->pinmux_pad1 = PINMUX_DEFAULT; - config->scl_low_timeout = false; -#ifdef FEATURE_I2C_SCL_STRETCH_MODE - config->scl_stretch_only_after_ack_bit = false; -#endif -#ifdef FEATURE_I2C_SCL_EXTEND_TIMEOUT - config->slave_scl_low_extend_timeout = false; -#endif -} - -enum status_code i2c_slave_init(struct i2c_slave_module *const module, - Sercom *const hw, - const struct i2c_slave_config *const config); - -/** - * \brief Enables the I<SUP>2</SUP>C module - * - * This will enable the requested I<SUP>2</SUP>C module. - * - * \param[in] module Pointer to the software module struct - */ -static inline void i2c_slave_enable( - const struct i2c_slave_module *const module) -{ - /* Sanity check of arguments */ - Assert(module); - Assert(module->hw); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - -#if I2C_SLAVE_CALLBACK_MODE == true - /* Enable global interrupt for module */ - system_interrupt_enable(_sercom_get_interrupt_vector(module->hw)); -#endif - - /* Wait for module to sync */ - _i2c_slave_wait_for_sync(module); - - /* Enable module */ - i2c_hw->CTRLA.reg |= SERCOM_I2CS_CTRLA_ENABLE; -} - - -/** - * \brief Disables the I<SUP>2</SUP>C module - * - * This will disable the I<SUP>2</SUP>C module specified in the provided software module - * structure. - * - * \param[in] module Pointer to the software module struct - */ -static inline void i2c_slave_disable( - const struct i2c_slave_module *const module) -{ - /* Sanity check of arguments */ - Assert(module); - Assert(module->hw); - - SercomI2cs *const i2c_hw = &(module->hw->I2CS); - -#if I2C_SLAVE_CALLBACK_MODE == true - /* Disable interrupts */ - i2c_hw->INTENCLR.reg = SERCOM_I2CS_INTENSET_PREC | - SERCOM_I2CS_INTENSET_AMATCH | SERCOM_I2CS_INTENSET_DRDY; - - /* Clear interrupt flags */ - i2c_hw->INTFLAG.reg = SERCOM_I2CS_INTFLAG_PREC | SERCOM_I2CS_INTFLAG_AMATCH | - SERCOM_I2CS_INTFLAG_DRDY; - - /* Disable global interrupt for module */ - system_interrupt_disable(_sercom_get_interrupt_vector(module->hw)); -#endif - - /* Wait for module to sync */ - _i2c_slave_wait_for_sync(module); - - /* Disable module */ - i2c_hw->CTRLA.reg &= ~SERCOM_I2CS_CTRLA_ENABLE; -} - -void i2c_slave_reset( - struct i2c_slave_module *const module); - -/** @} */ - -/** - * \name Read and Write - * @{ - */ - -enum status_code i2c_slave_write_packet_wait( - struct i2c_slave_module *const module, - struct i2c_slave_packet *const packet); -enum status_code i2c_slave_read_packet_wait( - struct i2c_slave_module *const module, - struct i2c_slave_packet *const packet); -enum i2c_slave_direction i2c_slave_get_direction_wait( - struct i2c_slave_module *const module); - -/** @} */ - -/** - * \name Status Management - * @{ - */ -uint32_t i2c_slave_get_status( - struct i2c_slave_module *const module); -void i2c_slave_clear_status( - struct i2c_slave_module *const module, - uint32_t status_flags); -/** @} */ - -#ifdef FEATURE_I2C_DMA_SUPPORT -/** - * \name SERCOM I2C slave with DMA Interfaces - * @{ - */ - -/** - * \brief Read SERCOM I<SUP>2</SUP>C interrupt status. - * - * Read I<SUP>2</SUP>C interrupt status for DMA transfer. - * - * \param[in,out] module Pointer to the driver instance to lock - * - */ -static inline uint8_t i2c_slave_dma_read_interrupt_status(struct i2c_slave_module *const module) -{ - return (uint8_t)module->hw->I2CS.INTFLAG.reg; -} - -/** - * \brief Write SERCOM I<SUP>2</SUP>C interrupt status. - * - * Write I<SUP>2</SUP>C interrupt status for DMA transfer. - * - * \param[in,out] module Pointer to the driver instance to lock - * \param[in] flag Interrupt flag status - * - */ -static inline void i2c_slave_dma_write_interrupt_status(struct i2c_slave_module *const module, - uint8_t flag) -{ - module->hw->I2CS.INTFLAG.reg = flag; -} - -/** @} */ -#endif - -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif /* I2C_SLAVE_H_INCLUDED */