/** * Initialise the I2C driver * * @param handle Which I2C bus (if more than one exists) * @param mode I2C device mode. Must be either I2C_MASTER or I2C_SLAVE * @param addr Own slave address * @param speed Bus speed in kbps * */ int i2c_init(int handle, int mode, uint8_t addr, uint16_t speed, int queue_len_tx, int queue_len_rx, i2c_callback_t callback) { /* Error checking */ if (handle >= pca9665_device_count) return E_NO_DEVICE; if (device[handle].is_initialised) return E_NO_ERR; if (queue_len_tx <= 0) return E_INVALID_PARAM; if (mode != I2C_MASTER) { driver_debug(DEBUG_I2C, "PCA9665 driver does not support slave mode operation\r\n"); return E_INVALID_PARAM; } if (i2c_lock == NULL) i2c_lock = xSemaphoreCreateMutex(); if (i2c_lock == NULL) return E_NO_BUFFER; /* TX Queue */ if (device[handle].tx.queue == NULL) { device[handle].tx.queue = xQueueCreate(queue_len_tx, sizeof(i2c_frame_t *)); } /* RX Queue */ if ((device[handle].rx.queue == NULL) && (queue_len_rx > 0)) { device[handle].rx.queue = xQueueCreate(queue_len_rx, sizeof(i2c_frame_t *)); } /* Callback */ if (callback != NULL) { device[handle].callback = callback; } /* Call board specific ISR initialization */ pca9665_isr_init(); /* Initialise device */ int result = pca9665_init(handle, device[handle].base, addr, speed); if (result == E_NO_ERR) { device[handle].is_initialised = 1; } else { driver_debug(DEBUG_I2C, "Failed to initialise PCA9665 driver\r\n"); } command_register(pca9554_commands); return result; }
static int v4l2_case_debug_capability(void* user_ptr, int test_num) { v4l2_data* data = (v4l2_data*)user_ptr; int ret = 0; if (!open_device(data->device)) { BLTS_DEBUG("Can't open device %s\n", data->device->dev_name); return -1; } if(blts_log_start_syslog_capture()) { BLTS_ERROR("Can't initiate syslog capture\n"); goto err; } ret = driver_debug(data->device); if(errno == EINVAL) { BLTS_DEBUG("Driver returned EINVAL, and does not support driver status logging\n"); ret = 0; } if(blts_log_end_syslog_capture()) { BLTS_ERROR("Common library can't stop syslog capture\n"); } close_device(data->device); return ret; err: close_device(data->device); return -1; }
/** * Send a soft reset sepcial command * @param handler Device handle */ static inline void pca9665_soft_reset(int handler) { driver_debug(DEBUG_I2C, "RESET\r\n"); *(uint8_t *) (device[handler].base + INDPTR) = I2CPRESET - 4; *(uint8_t *) (device[handler].base + INDIRECT) = 0xA5; *(uint8_t *) (device[handler].base + INDIRECT) = 0x5A; }
/** * Initialises the remote registers in the PCA9665 * This function is called both from first init, and * when an error occurs inside the ISR. * @param handle integer handle (starting from 0) */ static int pca9665_init_registers(int handle) { /* Soft reset the device */ pca9665_soft_reset(handle); int retry = 10; while (retry--) { if ((pca9665_read_reg(handle, I2CCON) & 0x40) == 0) break; pca9665_soft_reset(handle); delay_us(1000); } /* Setup: I2C Addr */ pca9665_write_reg(handle, I2CADR, device[handle].slave_addr << 1); if (pca9665_read_reg(handle, I2CADR) != (device[handle].slave_addr << 1)) return -1; /* Setup: I2C mode */ pca9665_write_reg(handle, I2CMODE, 0x01); /* Setup: I2C Timeout */ pca9665_write_reg(handle, I2CTO, 0xFF); /* Setup: Speed * 1 * f SCL = ----------------------------------------- * Tosc (I2CSCLL + I2CSCLH) + Tr + Tf + Td * */ double Td = 300e-9; // Tdelay in s double Tr = 300e-9; // Estimated Trise in s double Tf = 300e-9; // Estimated Tfall in s double Tosc = 28e-9; // Tosc in s double period = 1 / (Tosc * device[handle].speed * 1000) - (Tr + Tf + Td) / Tosc; driver_debug(DEBUG_I2C, "Period is %f\r\n", period); pca9665_write_reg(handle, I2CSCLL, (uint8_t) (period * 0.66)); pca9665_write_reg(handle, I2CSCLH, (uint8_t) (period * 0.33)); return 0; }
static inline void pca9665_read_data_to_buffer(int handle) { if (device[handle].rx.frame == NULL) return; /* Check number of bytes */ int count = pca9665_read_reg(handle, I2CCOUNT) & 0x7f; if (count == 0) return; /* Check buffer allocation */ if (count + device[handle].rx.next_byte > I2C_MTU) return; /* Copy data and increment next_byte counter */ pca9665_read_data(handle, &device[handle].rx.frame->data[device[handle].rx.next_byte], count); device[handle].rx.next_byte += count; driver_debug(DEBUG_I2C, "RX: count %u, next_byte %u\r\n", count, device[handle].rx.next_byte); }
/** * Interrupt service routine * Basically handles the entire protocol */ void __attribute__((noinline)) pca9665_dsr(portBASE_TYPE * task_woken) { static int handle, len; static uint8_t state; static uint8_t dest; /* Loop through number of devices */ for (handle = 0; handle < pca9665_device_count; handle++) { /* Check for interrupt flag in device status register */ if (!(pca9665_read_reg(handle, I2CCON) & CON_SI)) continue; /* We have an interrupt, read the status register */ state = pca9665_read_reg(handle, I2CSTA); /* The I2C driver is one _big_ state-machine */ driver_debug(DEBUG_I2C, "I2C ISR %u %x\n\r", handle, state); switch (state) { /** * MASTER IRQ's */ /* START: is the first ISR that appears for outgoing frames */ case STA_M_REPEATED_START_SENDT: case STA_M_START_SENDT: /* Mark as busy, so start flag is not sent from task context while transmission is active */ device[handle].is_busy = 1; /* If this is the beginning of a new frame, dequeue */ if (device[handle].tx.frame == NULL && device[handle].rx.frame == NULL) { /* Try do dequeue element, if it fails, stop transmission */ xQueueReceiveFromISR(device[handle].tx.queue, &device[handle].tx.frame, task_woken); if (device[handle].tx.frame == NULL) { pca9665_try_tx_from_isr(handle, task_woken); break; } /* If TX len > 0, go for master transmit */ if (device[handle].tx.frame->len) { device[handle].mode = DEVICE_MODE_M_T; device[handle].tx.next_byte = 0; /* If TX len == 0 and RX len > 0, go for master receive */ } else if (device[handle].tx.frame->len_rx) { device[handle].mode = DEVICE_MODE_M_R; device[handle].rx.frame = device[handle].tx.frame; device[handle].tx.frame = NULL; device[handle].rx.frame->len = device[handle].rx.frame->len_rx; device[handle].rx.next_byte = 0; /* Well, this should not happen */ } else { csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; pca9665_try_tx_from_isr(handle, task_woken); break; } } /* If mode is master receiver then set the read-bit in the address field */ if (device[handle].mode == DEVICE_MODE_M_R) { dest = (device[handle].rx.frame->dest << 1) | 0x01; device[handle].rx.next_byte = 0; /* Do first part of frame here */ if (device[handle].rx.frame->len > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); } else { pca9665_write_reg(handle, I2CCOUNT, device[handle].rx.frame->len | 0x80); } pca9665_write_data(handle, &dest, 1); } else { dest = device[handle].tx.frame->dest << 1; device[handle].tx.next_byte = 0; /* Do first part of frame here */ if (device[handle].tx.frame->len + 1 > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); pca9665_write_data(handle, &dest, 1); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], PCA9665_MAX_BUF - 1); device[handle].tx.next_byte += PCA9665_MAX_BUF - 1; } else { pca9665_write_reg(handle, I2CCOUNT, device[handle].tx.frame->len + 1); pca9665_write_data(handle, &dest, 1); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], device[handle].tx.frame->len); device[handle].tx.next_byte += device[handle].tx.frame->len; } } /* Let the hardware continue */ pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* WRITE ACK: A node is ready to be written to */ case STA_M_SLAW_SENDT_ACKED: case STA_M_DATA_SENDT_ACKED: /* Safety first */ if (device[handle].tx.frame == NULL) goto isr_error; /* Calculate remaining length */ len = device[handle].tx.frame->len - device[handle].tx.next_byte; /* Transmit next chunk */ if (len > 0) { if (len > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], PCA9665_MAX_BUF); device[handle].tx.next_byte += PCA9665_MAX_BUF; } else { pca9665_write_reg(handle, I2CCOUNT, len); pca9665_write_data(handle, &device[handle].tx.frame->data[device[handle].tx.next_byte], len); device[handle].tx.next_byte += len; } pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* Or, Change from master transmit, to master read if wanted */ } else if (device[handle].tx.frame->len_rx) { device[handle].mode = DEVICE_MODE_M_R; device[handle].rx.frame = device[handle].tx.frame; device[handle].tx.frame = NULL; device[handle].rx.frame->len = device[handle].rx.frame->len_rx; /* We need to send a repeated start now! */ pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA | CON_STA); break; /* Or, We are done */ } else { csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; pca9665_try_tx_from_isr(handle, task_woken); } break; /* WRITE ERROR: A write has failed */ case STA_M_SLAW_SENDT_NACKED: case STA_M_DATA_SENDT_LAST_NACKED: if (device[handle].tx.frame != NULL) { driver_debug(DEBUG_I2C, "I2C SLA+W NACK: 0x%02"PRIx8"\n\r", device[handle].tx.frame->dest); csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; } pca9665_try_tx_from_isr(handle, task_woken); break; /* ARBITRATION LOST: Start condition failed */ case STA_M_ARBITRATION_LOST: /* Restart transmission by resetting next_byte and preserving tx_frame pointer */ device[handle].tx.next_byte = 0; pca9665_try_tx_from_isr(handle, task_woken); break; /* READ ACK: A node is ready to be read from */ case STA_M_SLAR_SENT_ACKED: case STA_M_DATA_RECEIVED_ACKED: case STA_M_DATA_RECEIVED_NACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; if (device[handle].rx.queue == NULL) goto isr_error; pca9665_read_data_to_buffer(handle); int remaining = device[handle].rx.frame->len - device[handle].rx.next_byte; driver_debug(DEBUG_I2C, "RX: Remaining %u\r\n", remaining); /* If no more to receive */ if (remaining == 0) { if (xQueueSendToBackFromISR(device[handle].rx.queue, &device[handle].rx.frame, task_woken) == pdFALSE) { driver_debug(DEBUG_I2C, "I2C rx queue full - freeing\n\r"); csp_buffer_free_isr(device[handle].rx.frame); } device[handle].rx.frame = NULL; pca9665_try_tx_from_isr(handle, task_woken); break; } /* If more than a full PCA9665 buffer remains */ if (remaining > PCA9665_MAX_BUF) { pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); /* Otherwise, this is the last bit, set NACK on final slave read */ } else { pca9665_write_reg(handle, I2CCOUNT, remaining | 0x80); } pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* READ ERROR: A read has failed */ case STA_M_SLAR_SENT_NACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; driver_debug(DEBUG_I2C, "I2C SLA+R nacked\n\r"); csp_buffer_free_isr(device[handle].rx.frame); device[handle].rx.frame = NULL; /* Start up again */ pca9665_try_tx_from_isr(handle, task_woken); break; /** * SLAVE RECEIVER BUFFERED MODE */ /* START: Lost the arbitration and is addressed as a slave receiver */ case STA_S_ARB_LOST_SLAW_RECEIVED: case STA_S_ARB_LOST_GC_RECEIVED: /* Preserve TX frame active, so the START flag will be re-set when the * reception is completed */ /* Deliberate Fallthrough */ /* START: Addressed as a slave receiver */ case STA_S_SLAW_RECEIVED_ACKED: case STA_S_GC_RECEIVED: /* Check if RX frame was started */ if (device[handle].rx.frame != NULL) goto isr_error; /* Allocate new frame */ device[handle].rx.frame = csp_buffer_get_isr(I2C_MTU); if (device[handle].rx.frame == NULL) goto isr_error; device[handle].is_busy = 1; device[handle].rx.next_byte = 0; device[handle].rx.frame->len = 0; device[handle].rx.frame->dest = device[handle].slave_addr; pca9665_write_reg(handle, I2CCOUNT, PCA9665_MAX_BUF); pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* READ: Data received. */ case STA_S_DATA_RECEIVED_SLA_ACKED: case STA_S_DATA_RECEIVED_GC_ACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; /* Receive data, if any */ pca9665_read_data_to_buffer(handle); /* Limit incoming bytes */ pca9665_write_reg(handle, I2CCOUNT, (device[handle].rx.next_byte + PCA9665_MAX_BUF > I2C_MTU) ? (I2C_MTU - device[handle].rx.next_byte) | 0x80 : PCA9665_MAX_BUF); pca9665_write_reg(handle, I2CCON, CON_ENSIO | CON_MODE | CON_AA); break; /* STOP or NACK: No more data to receive */ case STA_S_STOP_REP_RECEIVED: case STA_S_DATA_RECEIVED_SLA_NACKED: case STA_S_DATA_RECEIVED_GC_NACKED: /* Safety first */ if (device[handle].rx.frame == NULL) goto isr_error; /* Receive data, if any */ pca9665_read_data_to_buffer(handle); /* Queue up frame */ device[handle].rx.frame->len = device[handle].rx.next_byte; if (device[handle].rx.queue != NULL) { if (xQueueSendToBackFromISR(device[handle].rx.queue, &device[handle].rx.frame, task_woken) == pdFALSE) { driver_debug(DEBUG_I2C, "I2C RX queue full\n\r"); csp_buffer_free_isr(device[handle].rx.frame); } } else if (device[handle].callback != NULL) { device[handle].callback(device[handle].rx.frame, task_woken); } else { csp_buffer_free_isr(device[handle].rx.frame); } /* The frame has been freed now */ device[handle].rx.frame = NULL; /* Set back to master mode */ pca9665_try_tx_from_isr(handle, task_woken); break; /** * Other IRQ's, typically indicates a hardware or protcol error * The IDLE status is considered an error if asserted at the same time as the Serial Interrupt flag */ case STA_IDLE: default: isr_error: /* Soft reset the device */ driver_debug(DEBUG_I2C, "I2C ERR 0x%02X\n\r", state); pca9665_init_registers(handle); /* Clean up RX */ if (device[handle].rx.frame != NULL) { csp_buffer_free_isr(device[handle].rx.frame); device[handle].rx.frame = NULL; } /* Clean up TX */ if (device[handle].tx.frame != NULL) { csp_buffer_free_isr(device[handle].tx.frame); device[handle].tx.frame = NULL; } /* Start up again */ pca9665_try_tx_from_isr(handle, task_woken); break; } } /**< END Switch/Case */ }