/** * Uninitialise an I2C device */ int up_i2cuninitialize(struct i2c_dev_s *dev) { irqstate_t flags; i2cvdbg("Deinit I2C port\n"); flags = irqsave(); if (!refcount) goto out; if (--refcount) goto out; tsb_release_pinshare(TSB_PIN_GPIO21 | TSB_PIN_GPIO22); /* Detach Interrupt Handler */ irq_detach(TSB_IRQ_I2C); wd_delete(g_timeout); out: irqrestore(flags); return 0; }
static int tsb_i2c_handle_tx_abort(void) { unsigned long abort_source = g_abort_source; i2cvdbg("%s: 0x%x\n", __func__, abort_source); if (abort_source & TSB_I2C_TX_ABRT_NOACK) { i2cdbg("%s: TSB_I2C_TX_ABRT_NOACK 0x%x\n", __func__, abort_source); return -EREMOTEIO; } if (abort_source & TSB_I2C_TX_ARB_LOST) return -EAGAIN; else if (abort_source & TSB_I2C_TX_ABRT_GCALL_READ) return -EINVAL; /* wrong g_msgs[] data */ else return -EIO; }
static void tsb_i2c_read(void) { int rx_valid; i2cvdbg("rx_index %d\n", g_rx_index); for (; g_rx_index < g_msgs_count; g_rx_index++) { uint32_t len; uint8_t *buffer; if (!(g_msgs[g_rx_index].flags & I2C_M_READ)) continue; if (!(g_status & TSB_I2C_STATUS_READ_IN_PROGRESS)) { len = g_msgs[g_rx_index].length; buffer = g_msgs[g_rx_index].buffer; } else { len = g_rx_length; buffer = g_rx_buffer; } rx_valid = i2c_read(TSB_I2C_RXFLR); for (; len > 0 && rx_valid > 0; len--, rx_valid--) { *buffer++ = i2c_read(TSB_I2C_DATA_CMD); g_rx_outstanding--; } if (len > 0) { /* start the read process */ g_status |= TSB_I2C_STATUS_READ_IN_PROGRESS; g_rx_length = len; g_rx_buffer = buffer; return; } else { g_status &= ~TSB_I2C_STATUS_READ_IN_PROGRESS; } } }
static void tsb_i2c_start_transfer(void) { i2cvdbg("\n"); /* Disable the adapter */ tsb_i2c_disable(); /* write target address */ i2c_write(TSB_I2C_TAR, g_msgs[g_tx_index].addr); /* Disable the interrupts */ tsb_i2c_disable_int(); /* Enable the adapter */ tsb_i2c_enable(); /* Clear interrupts */ tsb_i2c_clear_int(); /* Enable interrupts */ i2c_write(TSB_I2C_INTR_MASK, TSB_I2C_INTR_DEFAULT_MASK); }
/** * Set the I2C bus frequency (not implemented yet) */ static uint32_t up_i2c_setfrequency(struct i2c_dev_s *dev, uint32_t frequency) { i2cvdbg("%d\n", frequency); return frequency; }
/** * Initialise an I2C device */ struct i2c_dev_s *up_i2cinitialize(int port) { irqstate_t flags; int retval; i2cvdbg("Init I2C port %d\n", port); /* Only one I2C port on TSB */ if (port > 0) return NULL; flags = irqsave(); if (refcount++) goto out; retval = tsb_request_pinshare(TSB_PIN_GPIO21 | TSB_PIN_GPIO22); if (retval) { lowsyslog("I2C: cannot get ownership of I2C pins\n"); goto err_req_pinshare; } sem_init(&g_mutex, 0, 1); sem_init(&g_wait, 0, 0); /* enable I2C pins */ tsb_clr_pinshare(TSB_PIN_GPIO21); tsb_clr_pinshare(TSB_PIN_GPIO22); /* enable I2C clocks */ tsb_clk_enable(TSB_CLK_I2CP); tsb_clk_enable(TSB_CLK_I2CS); /* reset I2C module */ tsb_reset(TSB_RST_I2CP); tsb_reset(TSB_RST_I2CS); /* Initialize the I2C controller */ tsb_i2c_init(); /* Allocate a watchdog timer */ g_timeout = wd_create(); DEBUGASSERT(g_timeout != 0); /* Attach Interrupt Handler */ irq_attach(TSB_IRQ_I2C, i2c_interrupt); /* Enable Interrupt Handler */ up_enable_irq(TSB_IRQ_I2C); /* Install our operations */ g_dev.ops = &dev_i2c_ops; out: irqrestore(flags); return &g_dev; err_req_pinshare: refcount--; irqrestore(flags); return NULL; }
/* Perform a sequence of I2C transfers */ static int up_i2c_transfer(struct i2c_dev_s *idev, struct i2c_msg_s *msgs, int num) { int ret; i2cvdbg("msgs: %d\n", num); sem_wait(&g_mutex); g_msgs = msgs; g_msgs_count = num; g_tx_index = 0; g_rx_index = 0; g_rx_outstanding = 0; g_cmd_err = 0; g_msg_err = 0; g_status = TSB_I2C_STATUS_IDLE; g_abort_source = 0; ret = tsb_i2c_wait_bus_ready(); if (ret < 0) goto done; /* * start a watchdog to timeout the transfer if * the bus is locked up... */ wd_start(g_timeout, TSB_I2C_TIMEOUT, i2c_timeout, 1, 0); /* start the transfers */ tsb_i2c_start_transfer(); sem_wait(&g_wait); wd_cancel(g_timeout); if (g_status == TSB_I2C_STATUS_TIMEOUT) { i2cdbg("controller timed out\n"); /* Re-init the adapter */ tsb_i2c_init(); ret = -ETIMEDOUT; goto done; } tsb_i2c_disable(); if (g_msg_err) { ret = g_msg_err; i2cdbg("error msg_err %x\n", g_msg_err); goto done; } if (!g_cmd_err) { ret = 0; i2cvdbg("no error %d\n", num); goto done; } /* Handle abort errors */ if (g_cmd_err == TSB_I2C_ERR_TX_ABRT) { ret = tsb_i2c_handle_tx_abort(); goto done; } /* default error code */ ret = -EIO; i2cdbg("unknown error %x\n", ret); done: sem_post(&g_mutex); return ret; }
/** * Internal function that handles the read or write transfer * It is called from the IRQ handler. */ static void tsb_i2c_transfer_msg(void) { uint32_t intr_mask; uint32_t addr = g_msgs[g_tx_index].addr; uint8_t *buffer = g_tx_buffer; uint32_t length = g_tx_length; bool need_restart = false; int tx_avail; int rx_avail; i2cvdbg("tx_index %d\n", g_tx_index); /* loop over the i2c message array */ for (; g_tx_index < g_msgs_count; g_tx_index++) { if (g_msgs[g_tx_index].addr != addr) { i2cdbg("invalid target address\n"); g_msg_err = -EINVAL; break; } if (g_msgs[g_tx_index].length == 0) { i2cdbg("invalid message length\n"); g_msg_err = -EINVAL; break; } if (!(g_status & TSB_I2C_STATUS_WRITE_IN_PROGRESS)) { /* init a new msg transfer */ buffer = g_msgs[g_tx_index].buffer; length = g_msgs[g_tx_index].length; /* force a restart between messages */ if (g_tx_index > 0) need_restart = true; } /* Get the amount of free space in the internal buffer */ tx_avail = TSB_I2C_TX_FIFO_DEPTH - i2c_read(TSB_I2C_TXFLR); rx_avail = TSB_I2C_RX_FIFO_DEPTH - i2c_read(TSB_I2C_RXFLR); /* loop until one of the fifo is full or buffer is consumed */ while (length > 0 && tx_avail > 0 && rx_avail > 0) { uint32_t cmd = 0; if (g_tx_index == g_msgs_count - 1 && length == 1) { /* Last msg, issue a STOP */ cmd |= (1 << 9); i2cvdbg("STOP\n"); } if (need_restart) { cmd |= (1 << 10); /* RESTART */ need_restart = false; i2cvdbg("RESTART\n"); } if (g_msgs[g_tx_index].flags & I2C_M_READ) { if (rx_avail - g_rx_outstanding <= 0) break; i2c_write(TSB_I2C_DATA_CMD, cmd | 1 << 8); /* READ */ i2cvdbg("READ\n"); rx_avail--; g_rx_outstanding++; } else { i2c_write(TSB_I2C_DATA_CMD, cmd | *buffer++); i2cvdbg("WRITE\n"); } tx_avail--; length--; } g_tx_buffer = buffer; g_tx_length = length; if (length > 0) { g_status |= TSB_I2C_STATUS_WRITE_IN_PROGRESS; break; } else { g_status &= ~TSB_I2C_STATUS_WRITE_IN_PROGRESS; } } intr_mask = TSB_I2C_INTR_DEFAULT_MASK; /* No more data to write. Stop the TX IRQ */ if (g_tx_index == g_msgs_count) intr_mask &= ~TSB_I2C_INTR_TX_EMPTY; /* In case of error, mask all the IRQs */ if (g_msg_err) intr_mask = 0; i2c_write(TSB_I2C_INTR_MASK, intr_mask); }
static int i2cdrvr_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct inode *inode = filep->f_inode; FAR struct i2c_driver_s *priv; FAR struct i2c_transfer_s *transfer; int ret; i2cvdbg("cmd=%d arg=%lu\n", cmd, arg); /* Get our private data structure */ DEBUGASSERT(filep != NULL && filep->f_inode != NULL); inode = filep->f_inode; priv = (FAR struct i2c_driver_s *)inode->i_private; DEBUGASSERT(priv); /* Get exclusive access to the I2C driver state structure */ ret = sem_wait(&priv->exclsem); if (ret < 0) { int errcode = errno; DEBUGASSERT(errcode < 0); return -errcode; } /* Process the IOCTL command */ switch (cmd) { /* Command: I2CIOC_TRANSFER * Description: Perform an I2C transfer * Argument: A reference to an instance of struct i2c_transfer_s. * Dependencies: CONFIG_I2C_DRIVER */ case I2CIOC_TRANSFER: { /* Get the reference to the i2c_transfer_s structure */ transfer = (FAR struct i2c_transfer_s *)((uintptr_t)arg); DEBUGASSERT(transfer != NULL); /* Perform the transfer */ ret = I2C_TRANSFER(priv->i2c, transfer->msgv, transfer->msgc); } break; #ifdef CONFIG_I2C_RESET /* Command: I2CIOC_RESET * Description: Perform an I2C bus reset in an attempt to break loose * stuck I2C devices. * Argument: None * Dependencies: CONFIG_I2C_DRIVER && CONFIG_I2C_RESET */ case I2CIOC_RESET: { ret = I2C_RESET(priv->i2c); } break; #endif default: ret = -ENOTTY; break; } sem_post(&priv->exclsem); return ret; }