Ejemplo n.º 1
0
/**
 * 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;
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
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;
        }
    }
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
/**
 * 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;
}
Ejemplo n.º 6
0
/**
 * 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;
}
Ejemplo n.º 7
0
/* 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;
}
Ejemplo n.º 8
0
/**
 * 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);
}
Ejemplo n.º 9
0
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;
}