/****************************************************************************** * * * 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 #include #include #include #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; } /* = / 2 * */ 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); }