| /****************************************************************************** |
| * * |
| * License Agreement * |
| * * |
| * Copyright (c) 2016 Altera Corporation, San Jose, California, USA. * |
| * All rights reserved. * |
| * * |
| * Permission is hereby granted, free of charge, to any person obtaining a * |
| * copy of this software and associated documentation files (the "Software"), * |
| * to deal in the Software without restriction, including without limitation * |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, * |
| * and/or sell copies of the Software, and to permit persons to whom the * |
| * Software is furnished to do so, subject to the following conditions: * |
| * * |
| * The above copyright notice and this permission notice shall be included in * |
| * all copies or substantial portions of the Software. * |
| * * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * |
| * DEALINGS IN THE SOFTWARE. * |
| * * |
| * * |
| ******************************************************************************/ |
| #include <stddef.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include "io.h" |
| #include "priv/alt_busy_sleep.h" |
| #include "sys/alt_errno.h" |
| #include "sys/alt_irq.h" |
| #include "sys/alt_stdio.h" |
| #include "sys/alt_alarm.h" |
| #include "altera_avalon_i2c.h" |
| |
| |
| |
| /* for all functions in this file, see the altera_avalon_i2c.h file for more complete function descriptions. */ |
| |
| /* optional irq callback */ |
| static void optional_irq_callback(void * context) |
| { |
| int timeout=100000; |
| alt_u32 bytes_read; |
| |
| ALT_AVALON_I2C_DEV_t *i2c_dev = context; |
| IRQ_DATA_t *irq = i2c_dev->callback_context; |
| |
| if (irq->irq_busy==2) /*receive request*/ |
| { |
| alt_avalon_i2c_rx_read_available(i2c_dev, irq->buffer, irq->size, &bytes_read); |
| irq->size-=bytes_read; |
| irq->buffer+=bytes_read; |
| if (irq->size > 0) |
| { |
| /* clear ISR register content */ |
| alt_avalon_i2c_int_clear(i2c_dev,ALT_AVALON_I2C_ISR_ALL_CLEARABLE_INTS_MSK); |
| /* re-enable the RX_READY interrupt */ |
| alt_avalon_i2c_int_enable(i2c_dev,ALT_AVALON_I2C_ISER_RX_READY_EN_MSK); |
| return; |
| } |
| } |
| |
| /*transaction should be done so no or minimal looping should occur*/ |
| /*for a write, this code will only be reached after the cmd fifo is*/ |
| /*empty (sent). For a read this code will only be reached after all*/ |
| /*bytes have been received.*/ |
| while (alt_avalon_i2c_is_busy(i2c_dev)) |
| { |
| if (--timeout == 0) |
| { |
| break; |
| } |
| } |
| |
| /*disable the ip. The ip is disabled and enabled for each transaction.*/ |
| alt_avalon_i2c_disable(i2c_dev); |
| |
| irq->irq_busy=0; |
| } |
| |
| void alt_avalon_i2c_register_optional_irq_handler(ALT_AVALON_I2C_DEV_t *i2c_dev,IRQ_DATA_t * irq_data) |
| { |
| irq_data->irq_busy=0; |
| alt_avalon_i2c_register_callback(i2c_dev,optional_irq_callback,0,irq_data); |
| } |
| |
| /* The list of registered i2c components */ |
| ALT_LLIST_HEAD(alt_avalon_i2c_list); |
| |
| /* Interrupt handler for the AVALON_I2C module. */ |
| /* Interrupts are not re-enabled in this handler */ |
| static void alt_avalon_i2c_irq(void *context) |
| { |
| ALT_AVALON_I2C_DEV_t *dev = (ALT_AVALON_I2C_DEV_t *) context; |
| alt_irq_context cpu_sr; |
| |
| /*disable i2c interrupts*/ |
| alt_avalon_i2c_int_disable(dev,ALT_AVALON_I2C_ISR_ALLINTS_MSK); |
| |
| /* clear irq status */ |
| alt_avalon_i2c_int_clear(dev,ALT_AVALON_I2C_ISR_ALL_CLEARABLE_INTS_MSK); |
| |
| /* |
| * Other interrupts are explicitly disabled if callbacks |
| * are registered because there is no guarantee that they are |
| * pre-emption-safe. This allows the driver to support |
| * interrupt pre-emption. |
| */ |
| if(dev->callback) |
| { |
| cpu_sr = alt_irq_disable_all(); |
| dev->callback(dev); |
| alt_irq_enable_all(cpu_sr); |
| } |
| |
| return; |
| } |
| |
| /* Associate a user-specific routine with the i2c interrupt handler. */ |
| void alt_avalon_i2c_register_callback( |
| ALT_AVALON_I2C_DEV_t *dev, |
| alt_avalon_i2c_callback callback, |
| alt_u32 control, |
| void *context) |
| { |
| dev->callback = callback; |
| dev->callback_context = context; |
| dev->control = control; |
| |
| return ; |
| } |
| |
| /* Initializes the I2C Module. This routine is called |
| * from the ALT_AVALON_I2C_INIT macro and is called automatically |
| * by alt_sys_init.c */ |
| void alt_avalon_i2c_init (ALT_AVALON_I2C_DEV_t *dev) |
| { |
| extern alt_llist alt_avalon_i2c_list; |
| ALT_AVALON_I2C_MASTER_CONFIG_t cfg; |
| int error; |
| |
| /* disable ip */ |
| alt_avalon_i2c_disable(dev); |
| |
| /* Disable interrupts */ |
| alt_avalon_i2c_int_disable(dev,ALT_AVALON_I2C_ISR_ALLINTS_MSK); |
| |
| /* clear ISR register content */ |
| alt_avalon_i2c_int_clear(dev,ALT_AVALON_I2C_ISR_ALL_CLEARABLE_INTS_MSK); |
| |
| /* set the cmd fifo threshold */ |
| alt_avalon_i2c_tfr_cmd_fifo_threshold_set(dev,ALT_AVALON_I2C_TFR_CMD_FIFO_NOT_FULL); |
| |
| /* set the tx fifo threshold */ |
| alt_avalon_i2c_rx_fifo_threshold_set(dev,ALT_AVALON_I2C_RX_DATA_FIFO_FULL); |
| |
| /* set the default bus speed */ |
| cfg.speed_mode = ALT_AVALON_I2C_SPEED_STANDARD; |
| |
| /*set the address mode */ |
| cfg.addr_mode = ALT_AVALON_I2C_ADDR_MODE_7_BIT; |
| |
| /* set the bus speed */ |
| alt_avalon_i2c_master_config_speed_set(dev,&cfg,ALT_AVALON_I2C_SS_MAX_HZ); |
| |
| /* write the cfg information */ |
| alt_avalon_i2c_master_config_set(dev,&cfg); |
| |
| /* Register this instance of the i2c controller with HAL */ |
| alt_dev_llist_insert((alt_dev_llist*) dev, &alt_avalon_i2c_list); |
| |
| /* |
| * Creating semaphores used to protect access to the registers |
| * when running in a multi-threaded environment. |
| */ |
| error = ALT_SEM_CREATE (&dev->regs_lock, 1); |
| |
| if (!error) |
| { |
| /* Install IRQ handler */ |
| alt_ic_isr_register(dev->irq_controller_ID, dev->irq_ID, alt_avalon_i2c_irq, dev, 0x0); |
| } |
| else |
| { |
| alt_printf("failed to create semaphores\n"); |
| } |
| |
| return; |
| |
| } |
| |
| /* Retrieve a pointer to the i2c instance */ |
| ALT_AVALON_I2C_DEV_t* alt_avalon_i2c_open(const char* name) |
| { |
| ALT_AVALON_I2C_DEV_t* dev = NULL; |
| |
| dev = (ALT_AVALON_I2C_DEV_t*) alt_find_dev (name, &alt_avalon_i2c_list); |
| |
| return dev; |
| } |
| |
| /* enable the avalon i2c ip */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_enable(ALT_AVALON_I2C_DEV_t *i2c_dev) |
| { |
| IRQ_DATA_t *irq_data = i2c_dev->callback_context; |
| alt_u32 enable_status; |
| |
| /*if the ip is already enabled, return a busy status*/ |
| enable_status = (IORD_ALT_AVALON_I2C_CTRL(i2c_dev->i2c_base) & ALT_AVALON_I2C_CTRL_EN_MSK) >> ALT_AVALON_I2C_CTRL_EN_OFST; |
| if (enable_status) |
| { |
| return ALT_AVALON_I2C_BUSY; |
| } |
| |
| /*if the optional irq callback is registered ensure irq_busy is 0*/ |
| if (i2c_dev->callback == optional_irq_callback) |
| { |
| irq_data->irq_busy=0; |
| } |
| |
| /* enable ip */ |
| IORMW_ALT_AVALON_I2C_CTRL(i2c_dev->i2c_base,ALT_AVALON_I2C_CTRL_EN_MSK,ALT_AVALON_I2C_CTRL_EN_MSK); |
| |
| return ALT_AVALON_I2C_SUCCESS; |
| } |
| |
| /* disable the avalon i2c ip */ |
| void alt_avalon_i2c_disable(ALT_AVALON_I2C_DEV_t *i2c_dev) |
| { |
| /* disable ip */ |
| IORMW_ALT_AVALON_I2C_CTRL(i2c_dev->i2c_base,0,ALT_AVALON_I2C_CTRL_EN_MSK); |
| |
| } |
| |
| /* populate the the master config structure from the register values */ |
| void alt_avalon_i2c_master_config_get(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| ALT_AVALON_I2C_MASTER_CONFIG_t* cfg) |
| { |
| |
| cfg->addr_mode = i2c_dev->address_mode; |
| cfg->speed_mode = (IORD_ALT_AVALON_I2C_CTRL(i2c_dev->i2c_base) & ALT_AVALON_I2C_CTRL_BUS_SPEED_MSK) >> ALT_AVALON_I2C_CTRL_BUS_SPEED_OFST; |
| |
| cfg->scl_hcnt = (IORD_ALT_AVALON_I2C_SCL_HIGH(i2c_dev->i2c_base) & ALT_AVALON_I2C_SCL_HIGH_COUNT_PERIOD_MSK) >> ALT_AVALON_I2C_SCL_HIGH_COUNT_PERIOD_OFST; |
| cfg->scl_lcnt = (IORD_ALT_AVALON_I2C_SCL_LOW(i2c_dev->i2c_base) & ALT_AVALON_I2C_SCL_LOW_COUNT_PERIOD_MSK) >> ALT_AVALON_I2C_SCL_LOW_COUNT_PERIOD_OFST; |
| cfg->sda_cnt = (IORD_ALT_AVALON_I2C_SDA_HOLD(i2c_dev->i2c_base) & ALT_AVALON_I2C_SDA_HOLD_COUNT_PERIOD_MSK) >> ALT_AVALON_I2C_SDA_HOLD_COUNT_PERIOD_OFST; |
| } |
| |
| /* set the registers from the master config structure */ |
| void alt_avalon_i2c_master_config_set(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| const ALT_AVALON_I2C_MASTER_CONFIG_t* cfg) |
| { |
| i2c_dev->address_mode = cfg->addr_mode; |
| IORMW_ALT_AVALON_I2C_CTRL(i2c_dev->i2c_base,(cfg->speed_mode) << ALT_AVALON_I2C_CTRL_BUS_SPEED_OFST,ALT_AVALON_I2C_CTRL_BUS_SPEED_MSK); |
| |
| IOWR_ALT_AVALON_I2C_SCL_HIGH(i2c_dev->i2c_base,cfg->scl_hcnt); |
| IOWR_ALT_AVALON_I2C_SCL_LOW(i2c_dev->i2c_base,cfg->scl_lcnt); |
| IOWR_ALT_AVALON_I2C_SDA_HOLD(i2c_dev->i2c_base,cfg->sda_cnt); |
| } |
| |
| /* This function returns the speed based on parameters of the |
| * I2C master configuration. |
| */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_master_config_speed_get(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| const ALT_AVALON_I2C_MASTER_CONFIG_t* cfg, |
| alt_u32 * speed_in_hz) |
| { |
| |
| if ((cfg->scl_lcnt == 0) || (cfg->scl_hcnt == 0)) |
| { |
| return ALT_AVALON_I2C_BAD_ARG; |
| } |
| |
| *speed_in_hz = (i2c_dev->ip_freq_in_hz) / (cfg->scl_lcnt + cfg->scl_hcnt); |
| |
| return ALT_AVALON_I2C_SUCCESS; |
| } |
| |
| /*This is a utility function that computes parameters for the I2C master |
| * configuration that best matches the speed requested. */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_master_config_speed_set(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| ALT_AVALON_I2C_MASTER_CONFIG_t * cfg, |
| alt_u32 speed_in_hz) |
| { |
| alt_u32 scl_lcnt,scl_hcnt; |
| |
| /* If speed is not standard or fast return range error */ |
| if ((speed_in_hz > ALT_AVALON_I2C_FS_MAX_HZ) || (speed_in_hz < ALT_AVALON_I2C_SS_MIN_HZ) || (speed_in_hz == 0)) |
| { |
| return ALT_AVALON_I2C_RANGE; |
| } |
| |
| /* <lcount> = <internal clock> / 2 * <speed, Hz> */ |
| scl_lcnt = (i2c_dev->ip_freq_in_hz) / (speed_in_hz << 1); |
| |
| /* adjust h/l by predetermined amount */ |
| scl_hcnt = scl_lcnt + ALT_AVALON_I2C_DIFF_LCNT_HCNT; |
| scl_lcnt = scl_lcnt - ALT_AVALON_I2C_DIFF_LCNT_HCNT; |
| |
| if (speed_in_hz > ALT_AVALON_I2C_FS_MIN_HZ) |
| { |
| cfg->speed_mode = ALT_AVALON_I2C_SPEED_FAST; |
| } |
| else |
| { |
| cfg->speed_mode = ALT_AVALON_I2C_SPEED_STANDARD; |
| } |
| |
| cfg->scl_lcnt = scl_lcnt; |
| cfg->scl_hcnt = scl_hcnt; |
| cfg->sda_cnt = scl_lcnt - (scl_lcnt / 2); |
| |
| return ALT_AVALON_I2C_SUCCESS; |
| |
| } |
| |
| /*Returns ALT_AVALON_I2C_TRUE if the I2C controller is busy. The I2C controller is busy if |
| * not in the IDLE state */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_is_busy(ALT_AVALON_I2C_DEV_t *i2c_dev) |
| { |
| |
| if (IORD_ALT_AVALON_I2C_STATUS(i2c_dev->i2c_base) & ALT_AVALON_I2C_STATUS_CORE_STATUS_MSK) |
| { |
| return ALT_AVALON_I2C_TRUE; |
| } |
| |
| return ALT_AVALON_I2C_FALSE; |
| } |
| |
| /*Read all available bytes from the receive FIFO up to max_bytes_to_read. If max_bytes_to_read = 0 then read all available */ |
| void alt_avalon_i2c_rx_read_available(ALT_AVALON_I2C_DEV_t *i2c_dev, alt_u8 *buffer, alt_u32 max_bytes_to_read, alt_u32 *bytes_read) |
| { |
| *bytes_read = 0; |
| |
| while (IORD_ALT_AVALON_I2C_RX_DATA_FIFO_LVL(i2c_dev->i2c_base)) |
| { |
| buffer[*bytes_read] = (alt_u8)IORD_ALT_AVALON_I2C_RX_DATA(i2c_dev->i2c_base); |
| *bytes_read+=1; |
| if ((*bytes_read == max_bytes_to_read) && (max_bytes_to_read != 0)) break; |
| } |
| } |
| |
| /*when a byte is available, reads a single data byte from the receive FIFO. */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_rx_read(ALT_AVALON_I2C_DEV_t *i2c_dev, alt_u8 *val) |
| { |
| alt_u32 status = ALT_AVALON_I2C_SUCCESS; |
| alt_u32 timeout = 100000; |
| |
| |
| while (IORD_ALT_AVALON_I2C_RX_DATA_FIFO_LVL(i2c_dev->i2c_base) == 0) |
| { |
| if (timeout<10) alt_busy_sleep(10000); |
| if (--timeout == 0) |
| { |
| status = ALT_AVALON_I2C_TIMEOUT; |
| break; |
| } |
| } |
| |
| *val = (alt_u8)IORD_ALT_AVALON_I2C_RX_DATA(i2c_dev->i2c_base); |
| |
| return status; |
| } |
| |
| /* When space is available, writes the Transfer Command FIFO. */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_cmd_write(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| alt_u8 val, |
| alt_u8 issue_restart, |
| alt_u8 issue_stop) |
| { |
| alt_u32 timeout = 10000; |
| ALT_AVALON_I2C_STATUS_CODE status = ALT_AVALON_I2C_SUCCESS; |
| |
| |
| while ((IORD_ALT_AVALON_I2C_ISR(i2c_dev->i2c_base) & ALT_AVALON_I2C_ISR_TX_READY_MSK)==0) |
| { |
| if (timeout<10) alt_busy_sleep(10000); |
| if (--timeout == 0) |
| { |
| return ALT_AVALON_I2C_TIMEOUT; |
| } |
| } |
| |
| IOWR_ALT_AVALON_I2C_TFR_CMD(i2c_dev->i2c_base,val | |
| (issue_restart << ALT_AVALON_I2C_TFR_CMD_STA_OFST) | |
| (issue_stop << ALT_AVALON_I2C_TFR_CMD_STO_OFST)); |
| |
| |
| /*check for nack error*/ |
| alt_avalon_i2c_check_nack(i2c_dev,&status); |
| |
| /*check for arb lost*/ |
| alt_avalon_i2c_check_arblost(i2c_dev,&status); |
| |
| return status; |
| } |
| |
| /*send 7 or 10 bit i2c address to cmd fifo*/ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_send_address(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| const alt_u32 rw_bit, |
| const alt_u8 issue_restart) |
| { |
| alt_u32 status; |
| |
| if (i2c_dev->address_mode == ALT_AVALON_I2C_ADDR_MODE_10_BIT) |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev,(((i2c_dev->master_target_address | TARGET_ADDR_MASK_10BIT) >> 7) & 0xfe) | rw_bit,issue_restart,ALT_AVALON_I2C_NO_STOP); |
| status = alt_avalon_i2c_cmd_write(i2c_dev,i2c_dev->master_target_address & 0xff,ALT_AVALON_I2C_NO_RESTART,ALT_AVALON_I2C_NO_STOP); |
| } |
| else |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev,(i2c_dev->master_target_address << 1) | rw_bit,issue_restart,ALT_AVALON_I2C_NO_STOP); |
| } |
| |
| return status; |
| } |
| |
| /* This function returns the current target address. */ |
| void alt_avalon_i2c_master_target_get(ALT_AVALON_I2C_DEV_t * i2c_dev, alt_u32 * target_addr) |
| { |
| *target_addr=i2c_dev->master_target_address; |
| } |
| |
| /* This function updates the target address for any upcoming I2C bus IO. */ |
| void alt_avalon_i2c_master_target_set(ALT_AVALON_I2C_DEV_t * i2c_dev, alt_u32 target_addr) |
| { |
| i2c_dev->master_target_address=target_addr; |
| } |
| |
| /*if nack detected, status is set to ALT_AVALON_I2C_NACK_ERR*/ |
| void alt_avalon_i2c_check_nack(ALT_AVALON_I2C_DEV_t *i2c_dev,ALT_AVALON_I2C_STATUS_CODE * status) |
| { |
| if (IORD_ALT_AVALON_I2C_ISR(i2c_dev->i2c_base) & ALT_AVALON_I2C_ISR_NACK_DET_MSK) |
| { |
| *status=ALT_AVALON_I2C_NACK_ERR; |
| } |
| } |
| |
| /*if arb lost is detected, status is set to ALT_AVALON_I2C_ARB_LOST_ERR*/ |
| void alt_avalon_i2c_check_arblost(ALT_AVALON_I2C_DEV_t *i2c_dev,ALT_AVALON_I2C_STATUS_CODE * status) |
| { |
| if (IORD_ALT_AVALON_I2C_ISR(i2c_dev->i2c_base) & ALT_AVALON_I2C_ISR_ARBLOST_DET_MSK) |
| { |
| *status=ALT_AVALON_I2C_ARB_LOST_ERR; |
| } |
| } |
| |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_interrupt_transaction_status(ALT_AVALON_I2C_DEV_t *i2c_dev) |
| { |
| ALT_AVALON_I2C_STATUS_CODE status = ALT_AVALON_I2C_SUCCESS; |
| IRQ_DATA_t *irq_data = i2c_dev->callback_context; |
| alt_u32 timeout=10000 * irq_data->size + 10000; |
| alt_u32 saveints,temp_bytes_read; |
| |
| /* save current enabled interrupts */ |
| alt_avalon_i2c_enabled_ints_get(i2c_dev,&saveints); |
| |
| /* disable the enabled interrupts */ |
| alt_avalon_i2c_int_disable(i2c_dev,saveints); |
| |
| alt_avalon_i2c_check_nack(i2c_dev,&status); |
| |
| if (status!=ALT_AVALON_I2C_SUCCESS) |
| { |
| if (irq_data->irq_busy) |
| { |
| while (alt_avalon_i2c_is_busy(i2c_dev)) |
| { |
| if (timeout<10) alt_busy_sleep(10000); |
| if (--timeout == 0) |
| { |
| status = ALT_AVALON_I2C_TIMEOUT; |
| break; |
| } |
| } |
| |
| /*clear any rx entries */ |
| alt_avalon_i2c_rx_read_available(i2c_dev, irq_data->buffer,0,&temp_bytes_read); |
| |
| /*disable the ip. The ip is disabled and enabled for each transaction. */ |
| alt_avalon_i2c_disable(i2c_dev); |
| |
| /*abort the transaction */ |
| irq_data->irq_busy=0; |
| } |
| |
| /*return nack error so transaction can be retried*/ |
| return status; |
| } |
| |
| if (irq_data->irq_busy) |
| { |
| /*re-enable the interrupts*/ |
| alt_avalon_i2c_int_enable(i2c_dev,saveints); |
| |
| /*return transaction still busy*/ |
| return ALT_AVALON_I2C_BUSY; |
| } |
| |
| /*return transaction completed status, ok to do another transaction*/ |
| return ALT_AVALON_I2C_SUCCESS; |
| } |
| |
| /*transmit function with retry and optionally interrupts*/ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_master_tx(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| const alt_u8 * buffer, |
| const alt_u32 size, |
| const alt_u8 use_interrupts) |
| { |
| ALT_AVALON_I2C_STATUS_CODE status; |
| alt_u32 retry=10000; |
| |
| while (retry--) |
| { |
| if (retry<10) alt_busy_sleep(10000); |
| if (use_interrupts) |
| { |
| status = alt_avalon_i2c_master_transmit_using_interrupts(i2c_dev, buffer, size, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_STOP); |
| } |
| else |
| { |
| status = alt_avalon_i2c_master_transmit(i2c_dev, buffer, size, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_STOP); |
| } |
| if ((status==ALT_AVALON_I2C_ARB_LOST_ERR) || (status==ALT_AVALON_I2C_NACK_ERR) || (status==ALT_AVALON_I2C_BUSY)) continue; |
| break; |
| } |
| |
| return status; |
| } |
| |
| /*receive function with retry and optionally interrupts*/ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_master_rx(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| alt_u8 * buffer, |
| const alt_u32 size, |
| const alt_u8 use_interrupts) |
| { |
| ALT_AVALON_I2C_STATUS_CODE status; |
| alt_u32 retry=10000; |
| |
| if (use_interrupts) |
| { |
| while (retry--) |
| { |
| if (retry<10) alt_busy_sleep(10000); |
| status = alt_avalon_i2c_master_receive_using_interrupts(i2c_dev, buffer, size, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_STOP); |
| if ((status==ALT_AVALON_I2C_ARB_LOST_ERR) || (status==ALT_AVALON_I2C_NACK_ERR) || (status==ALT_AVALON_I2C_BUSY)) continue; |
| break; |
| } |
| } |
| else |
| { |
| while (retry--) |
| { |
| if (retry<10) alt_busy_sleep(10000); |
| status = alt_avalon_i2c_master_receive(i2c_dev, buffer, size, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_STOP); |
| if ((status==ALT_AVALON_I2C_ARB_LOST_ERR) || (status==ALT_AVALON_I2C_NACK_ERR) || (status==ALT_AVALON_I2C_BUSY)) continue; |
| break; |
| } |
| } |
| |
| return status; |
| } |
| |
| |
| /*transmit, restart, recieve function using retry and optionally interrupts */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_master_tx_rx(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| const alt_u8 * txbuffer, |
| const alt_u32 txsize, |
| alt_u8 * rxbuffer, |
| const alt_u32 rxsize, |
| const alt_u8 use_interrupts) |
| { |
| ALT_AVALON_I2C_STATUS_CODE status; |
| alt_u32 retry=10000; |
| |
| if (use_interrupts) |
| { |
| while (retry--) |
| { |
| if (retry<10) alt_busy_sleep(10000); |
| status = alt_avalon_i2c_master_transmit_using_interrupts(i2c_dev, txbuffer, txsize, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_NO_STOP); |
| if ((status==ALT_AVALON_I2C_ARB_LOST_ERR) || (status==ALT_AVALON_I2C_NACK_ERR) || (status==ALT_AVALON_I2C_BUSY)) continue; |
| |
| status = alt_avalon_i2c_master_receive_using_interrupts(i2c_dev, rxbuffer, rxsize, ALT_AVALON_I2C_RESTART, ALT_AVALON_I2C_STOP); |
| if ((status==ALT_AVALON_I2C_ARB_LOST_ERR) || (status==ALT_AVALON_I2C_NACK_ERR) || (status==ALT_AVALON_I2C_BUSY)) continue; |
| |
| break; |
| } |
| } |
| else |
| { |
| while (retry--) |
| { |
| if (retry<10) alt_busy_sleep(10000); |
| status = alt_avalon_i2c_master_transmit(i2c_dev, txbuffer, txsize, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_NO_STOP); |
| if ((status==ALT_AVALON_I2C_ARB_LOST_ERR) || (status==ALT_AVALON_I2C_NACK_ERR) || (status==ALT_AVALON_I2C_BUSY)) continue; |
| |
| status = alt_avalon_i2c_master_receive(i2c_dev, rxbuffer, rxsize, ALT_AVALON_I2C_RESTART, ALT_AVALON_I2C_STOP); |
| if ((status==ALT_AVALON_I2C_ARB_LOST_ERR) || (status==ALT_AVALON_I2C_NACK_ERR) || (status==ALT_AVALON_I2C_BUSY)) continue; |
| |
| break; |
| } |
| } |
| |
| return status; |
| } |
| |
| /*This function issues a write command and transmits data to the I2C bus. */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_master_transmit(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| const alt_u8 * buffer, |
| alt_u32 size, |
| const alt_u8 issue_restart, |
| const alt_u8 issue_stop) |
| { |
| ALT_AVALON_I2C_STATUS_CODE status = ALT_AVALON_I2C_SUCCESS; |
| alt_u32 timeout=size * 10000; |
| |
| if (size==0) |
| { |
| return ALT_AVALON_I2C_SUCCESS; |
| } |
| |
| /*if a new transaction, enable ip and clear int status*/ |
| if (!issue_restart) |
| { |
| /*enable the ip. The ip is disabled and enabled for each transaction.*/ |
| status = alt_avalon_i2c_enable(i2c_dev); |
| if (status != ALT_AVALON_I2C_SUCCESS) |
| { |
| return status; |
| } |
| |
| /*Clear the ISR reg*/ |
| alt_avalon_i2c_int_clear(i2c_dev,ALT_AVALON_I2C_ISR_ALL_CLEARABLE_INTS_MSK); |
| } |
| |
| /*Start Write, transmit address. */ |
| status = alt_avalon_i2c_send_address(i2c_dev,ALT_AVALON_I2C_WRITE,issue_restart); |
| |
| if (status == ALT_AVALON_I2C_SUCCESS) |
| { |
| while ((size > 1) && (status == ALT_AVALON_I2C_SUCCESS)) |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev, *buffer, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_NO_STOP); |
| |
| ++buffer; |
| --size; |
| } |
| |
| /* Last byte */ |
| if (status == ALT_AVALON_I2C_SUCCESS) |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev, *buffer, ALT_AVALON_I2C_NO_RESTART, issue_stop); |
| |
| ++buffer; |
| --size; |
| } |
| } |
| |
| /*if end of transaction, wait until the ip is idle then disable the ip*/ |
| if ((issue_stop) || (status != ALT_AVALON_I2C_SUCCESS)) |
| { |
| |
| while (alt_avalon_i2c_is_busy(i2c_dev)) |
| { |
| if (timeout<10) alt_busy_sleep(10000); |
| if (--timeout == 0) |
| { |
| status = ALT_AVALON_I2C_TIMEOUT; |
| break; |
| } |
| } |
| |
| /*check for a nack error*/ |
| alt_avalon_i2c_check_nack(i2c_dev,&status); |
| |
| /*disable the ip. The ip is disabled and enabled for each transaction.*/ |
| alt_avalon_i2c_disable(i2c_dev); |
| } |
| |
| |
| return status; |
| } |
| |
| /*This function issues a write command and transmits data to the I2C bus */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_master_transmit_using_interrupts(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| const alt_u8 * buffer, |
| alt_u32 size, |
| const alt_u8 issue_restart, |
| const alt_u8 issue_stop) |
| { |
| ALT_AVALON_I2C_STATUS_CODE status = ALT_AVALON_I2C_SUCCESS; |
| alt_u32 timeout=size*10000; |
| IRQ_DATA_t *irq_data = i2c_dev->callback_context; |
| |
| if (size==0) |
| { |
| return ALT_AVALON_I2C_SUCCESS; |
| } |
| |
| /*IS the optional interrupt handler registered??*/ |
| if (i2c_dev->callback != optional_irq_callback) |
| { |
| return ALT_AVALON_I2C_BAD_ARG; |
| } |
| |
| /*if a new transaction, enable ip and clear int status*/ |
| if (!issue_restart) |
| { |
| /*enable the ip. The ip is disabled and enabled for each transaction.*/ |
| status = alt_avalon_i2c_enable(i2c_dev); |
| if (status != ALT_AVALON_I2C_SUCCESS) |
| { |
| return status; |
| } |
| |
| /*Clear the ISR reg*/ |
| alt_avalon_i2c_int_clear(i2c_dev,ALT_AVALON_I2C_ISR_ALL_CLEARABLE_INTS_MSK); |
| } |
| |
| /*Start Write, transmit address. */ |
| status = alt_avalon_i2c_send_address(i2c_dev,ALT_AVALON_I2C_WRITE,issue_restart); |
| |
| if (status == ALT_AVALON_I2C_SUCCESS) |
| { |
| while ((size > 1) && (status == ALT_AVALON_I2C_SUCCESS)) |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev, *buffer, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_NO_STOP); |
| |
| ++buffer; |
| --size; |
| } |
| |
| /* Last byte */ |
| if (status == ALT_AVALON_I2C_SUCCESS) |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev, *buffer, ALT_AVALON_I2C_NO_RESTART, issue_stop); |
| |
| ++buffer; |
| --size; |
| } |
| } |
| |
| /*if error, wait until the ip is idle then disable the ip*/ |
| if (status != ALT_AVALON_I2C_SUCCESS) |
| { |
| |
| while (alt_avalon_i2c_is_busy(i2c_dev)) |
| { |
| if (timeout<10) alt_busy_sleep(10000); |
| if (--timeout == 0) |
| { |
| status = ALT_AVALON_I2C_TIMEOUT; |
| break; |
| } |
| } |
| |
| /*disable the ip. The ip is disabled and enabled for each transaction.*/ |
| alt_avalon_i2c_disable(i2c_dev); |
| } |
| else |
| { |
| if (issue_stop) |
| { |
| /* clear ISR register content */ |
| alt_avalon_i2c_int_clear(i2c_dev,ALT_AVALON_I2C_ISR_ALL_CLEARABLE_INTS_MSK); |
| /* set the cmd fifo threshold */ |
| alt_avalon_i2c_tfr_cmd_fifo_threshold_set(i2c_dev,ALT_AVALON_I2C_TFR_CMD_FIFO_EMPTY); |
| /* set the interrupt transaction busy bit */ |
| irq_data->irq_busy=1; |
| /* enable the TX_READY interrupt */ |
| alt_avalon_i2c_int_enable(i2c_dev,ALT_AVALON_I2C_ISER_TX_READY_EN_MSK); |
| } |
| } |
| |
| return status; |
| } |
| |
| /*This function receives one or more data bytes transmitted from a slave in |
| * response to read requests issued from this master. */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_master_receive(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| alt_u8 * buffer, |
| const alt_u32 size, |
| const alt_u8 issue_restart, |
| const alt_u8 issue_stop) |
| { |
| ALT_AVALON_I2C_STATUS_CODE status = ALT_AVALON_I2C_SUCCESS; |
| alt_u32 timeout; |
| alt_u32 bytes_read=0; |
| alt_u32 bytes_written=0; |
| alt_u32 temp_bytes_read; |
| |
| if (size==0) |
| { |
| return ALT_AVALON_I2C_SUCCESS; |
| } |
| |
| /*if a new transaction, enable ip and clear int status*/ |
| if (!issue_restart) |
| { |
| /*enable the ip. The ip is disabled and enabled for each transaction.*/ |
| status = alt_avalon_i2c_enable(i2c_dev); |
| if (status != ALT_AVALON_I2C_SUCCESS) |
| { |
| return status; |
| } |
| |
| /*Clear the ISR reg*/ |
| alt_avalon_i2c_int_clear(i2c_dev,ALT_AVALON_I2C_ISR_ALL_CLEARABLE_INTS_MSK); |
| } |
| |
| /*Start Write, transmit address. */ |
| status = alt_avalon_i2c_send_address(i2c_dev,ALT_AVALON_I2C_READ,issue_restart); |
| |
| if (status == ALT_AVALON_I2C_SUCCESS) |
| { |
| while ((bytes_written < (size-1)) && (status == ALT_AVALON_I2C_SUCCESS)) |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev, 0, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_NO_STOP); |
| bytes_written++; |
| if (status == ALT_AVALON_I2C_SUCCESS) |
| { |
| alt_avalon_i2c_rx_read_available(i2c_dev, buffer,0,&temp_bytes_read); |
| buffer+=temp_bytes_read; |
| bytes_read+=temp_bytes_read; |
| } |
| } |
| |
| /* Last byte */ |
| if (status == ALT_AVALON_I2C_SUCCESS) |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev, 0, ALT_AVALON_I2C_NO_RESTART, issue_stop); |
| } |
| } |
| |
| while ((bytes_read < size) && (status==ALT_AVALON_I2C_SUCCESS)) |
| { |
| status=alt_avalon_i2c_rx_read(i2c_dev, buffer); |
| buffer++; |
| bytes_read++; |
| } |
| |
| /*if end of transaction, wait until the ip is idle then disable the ip*/ |
| if ((issue_stop) || (status != ALT_AVALON_I2C_SUCCESS)) |
| { |
| timeout=10000 * size; |
| while (alt_avalon_i2c_is_busy(i2c_dev)) |
| { |
| if (timeout<10) alt_busy_sleep(10000); |
| if (--timeout == 0) |
| { |
| status = ALT_AVALON_I2C_TIMEOUT; |
| break; |
| } |
| } |
| |
| /*check for nack error*/ |
| alt_avalon_i2c_check_nack(i2c_dev,&status); |
| |
| /*disable the ip. The ip is disabled and enabled for each transaction.*/ |
| alt_avalon_i2c_disable(i2c_dev); |
| } |
| |
| return status; |
| } |
| |
| /*This function receives one or more data bytes transmitted from a slave in |
| * response to read requests issued from this master. Uses the optional interrupt routine. */ |
| ALT_AVALON_I2C_STATUS_CODE alt_avalon_i2c_master_receive_using_interrupts(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| alt_u8 * buffer, |
| const alt_u32 size, |
| const alt_u8 issue_restart, |
| const alt_u8 issue_stop) |
| { |
| ALT_AVALON_I2C_STATUS_CODE status = ALT_AVALON_I2C_SUCCESS; |
| IRQ_DATA_t *irq_data = i2c_dev->callback_context; |
| alt_u32 timeout; |
| alt_u32 bytes_written=0; |
| |
| if (size==0) |
| { |
| return ALT_AVALON_I2C_SUCCESS; |
| } |
| |
| /*Is the optional interrupt handler registered??*/ |
| if (i2c_dev->callback != optional_irq_callback) |
| { |
| return ALT_AVALON_I2C_BAD_ARG; |
| } |
| |
| /*if a new transaction, enable ip and clear int status*/ |
| if (!issue_restart) |
| { |
| /*enable the ip. The ip is disabled and enabled for each transaction.*/ |
| status = alt_avalon_i2c_enable(i2c_dev); |
| if (status != ALT_AVALON_I2C_SUCCESS) |
| { |
| return status; |
| } |
| |
| /*Clear the ISR reg*/ |
| alt_avalon_i2c_int_clear(i2c_dev,ALT_AVALON_I2C_ISR_ALL_CLEARABLE_INTS_MSK); |
| |
| } |
| |
| /*Start Write, transmit address. */ |
| status = alt_avalon_i2c_send_address(i2c_dev,ALT_AVALON_I2C_READ,issue_restart); |
| |
| if (status == ALT_AVALON_I2C_SUCCESS) |
| { |
| while ((bytes_written < (size-1)) && (status == ALT_AVALON_I2C_SUCCESS)) |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev, 0, ALT_AVALON_I2C_NO_RESTART, ALT_AVALON_I2C_NO_STOP); |
| bytes_written++; |
| } |
| |
| /* Last byte */ |
| if (status == ALT_AVALON_I2C_SUCCESS) |
| { |
| status = alt_avalon_i2c_cmd_write(i2c_dev, 0, ALT_AVALON_I2C_NO_RESTART, issue_stop); |
| } |
| } |
| |
| /*if error, wait until the ip is idle then disable the ip*/ |
| if (status != ALT_AVALON_I2C_SUCCESS) |
| { |
| timeout=10000 * size; |
| while (alt_avalon_i2c_is_busy(i2c_dev)) |
| { |
| if (timeout<10) alt_busy_sleep(10000); |
| if (--timeout == 0) |
| { |
| status = ALT_AVALON_I2C_TIMEOUT; |
| break; |
| } |
| } |
| |
| /*disable the ip. The ip is disabled and enabled for each transaction.*/ |
| alt_avalon_i2c_disable(i2c_dev); |
| } |
| else |
| { |
| if (issue_stop) |
| { |
| /* clear ISR register content */ |
| alt_avalon_i2c_int_clear(i2c_dev,ALT_AVALON_I2C_ISR_ALL_CLEARABLE_INTS_MSK); |
| /* set the cmd fifo threshold */ |
| alt_avalon_i2c_rx_fifo_threshold_set(i2c_dev,ALT_AVALON_I2C_RX_DATA_FIFO_1_ENTRY); |
| /* set the interrupt transaction busy bit 2 = receive */ |
| irq_data->irq_busy=2; |
| |
| irq_data->buffer = buffer; |
| irq_data->size = size; |
| |
| /* enable the RX_READY interrupt */ |
| alt_avalon_i2c_int_enable(i2c_dev,ALT_AVALON_I2C_ISER_RX_READY_EN_MSK); |
| } |
| } |
| |
| return status; |
| } |
| |
| /* Returns the current I2C controller interrupt status conditions. */ |
| void alt_avalon_i2c_int_status_get(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| alt_u32 *status) |
| { |
| *status = IORD_ALT_AVALON_I2C_ISR(i2c_dev->i2c_base) & IORD_ALT_AVALON_I2C_ISER(i2c_dev->i2c_base); |
| } |
| |
| /*Returns the I2C controller raw interrupt status conditions irrespective of |
| * the interrupt status condition enablement state. */ |
| void alt_avalon_i2c_int_raw_status_get(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| alt_u32 *status) |
| { |
| *status = IORD_ALT_AVALON_I2C_ISR(i2c_dev->i2c_base); |
| } |
| |
| /*Clears the specified I2C controller interrupt status conditions identified |
| * in the mask. */ |
| void alt_avalon_i2c_int_clear(ALT_AVALON_I2C_DEV_t *i2c_dev, const alt_u32 mask) |
| { |
| IOWR_ALT_AVALON_I2C_ISR(i2c_dev->i2c_base,mask); |
| } |
| |
| /*Disable the specified I2C controller interrupt status conditions identified in |
| * the mask. */ |
| void alt_avalon_i2c_int_disable(ALT_AVALON_I2C_DEV_t *i2c_dev, const alt_u32 mask) |
| { |
| alt_u32 enabled_ints; |
| |
| alt_avalon_i2c_enabled_ints_get(i2c_dev,&enabled_ints); |
| enabled_ints &= (~mask); |
| IOWR_ALT_AVALON_I2C_ISER(i2c_dev->i2c_base,ALT_AVALON_I2C_ISR_ALLINTS_MSK & enabled_ints); |
| } |
| |
| /*Enable the specified I2C controller interrupt status conditions identified in |
| * the mask. */ |
| void alt_avalon_i2c_int_enable(ALT_AVALON_I2C_DEV_t *i2c_dev, const alt_u32 mask) |
| { |
| alt_u32 enabled_ints; |
| |
| alt_avalon_i2c_enabled_ints_get(i2c_dev,&enabled_ints); |
| enabled_ints |= mask; |
| IOWR_ALT_AVALON_I2C_ISER(i2c_dev->i2c_base,ALT_AVALON_I2C_ISR_ALLINTS_MSK & enabled_ints); |
| } |
| |
| /*gets the enabled i2c interrupts. */ |
| void alt_avalon_i2c_enabled_ints_get(ALT_AVALON_I2C_DEV_t *i2c_dev, alt_u32 * enabled_ints) |
| { |
| *enabled_ints=IORD_ALT_AVALON_I2C_ISER(i2c_dev->i2c_base) & ALT_AVALON_I2C_ISR_ALLINTS_MSK; |
| } |
| |
| /*Gets the current receive FIFO threshold level value. */ |
| void alt_avalon_i2c_rx_fifo_threshold_get(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| ALT_AVALON_I2C_RX_DATA_FIFO_THRESHOLD_t *threshold) |
| { |
| *threshold = (IORD_ALT_AVALON_I2C_CTRL(i2c_dev->i2c_base) & ALT_AVALON_I2C_CTRL_RX_DATA_FIFO_THD_MSK) >> ALT_AVALON_I2C_CTRL_RX_DATA_FIFO_THD_OFST; |
| } |
| |
| /*sets the current receive FIFO threshold level value. */ |
| void alt_avalon_i2c_rx_fifo_threshold_set(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| const ALT_AVALON_I2C_RX_DATA_FIFO_THRESHOLD_t threshold) |
| { |
| IORMW_ALT_AVALON_I2C_CTRL(i2c_dev->i2c_base,threshold << ALT_AVALON_I2C_CTRL_RX_DATA_FIFO_THD_OFST,ALT_AVALON_I2C_CTRL_RX_DATA_FIFO_THD_MSK); |
| } |
| |
| /*Gets the current Transfer Command FIFO threshold level value.*/ |
| void alt_avalon_i2c_tfr_cmd_fifo_threshold_get(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| ALT_AVALON_I2C_TFR_CMD_FIFO_THRESHOLD_t *threshold) |
| { |
| *threshold = (IORD_ALT_AVALON_I2C_CTRL(i2c_dev->i2c_base) & ALT_AVALON_I2C_CTRL_TFR_CMD_FIFO_THD_MSK) >> ALT_AVALON_I2C_CTRL_TFR_CMD_FIFO_THD_OFST; |
| } |
| |
| /*Sets the current Transfer Command FIFO threshold level value.*/ |
| void alt_avalon_i2c_tfr_cmd_fifo_threshold_set(ALT_AVALON_I2C_DEV_t *i2c_dev, |
| const ALT_AVALON_I2C_TFR_CMD_FIFO_THRESHOLD_t threshold) |
| { |
| IORMW_ALT_AVALON_I2C_CTRL(i2c_dev->i2c_base,threshold << ALT_AVALON_I2C_CTRL_TFR_CMD_FIFO_THD_OFST,ALT_AVALON_I2C_CTRL_TFR_CMD_FIFO_THD_MSK); |
| } |
| |
| |
| |
| |