| /**************************************************************************** |
| * arch/arm/src/nrf91/nrf91_i2c.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/kmalloc.h> |
| #include <arch/board/board.h> |
| |
| #include "arm_internal.h" |
| #include "nrf91_gpio.h" |
| #include "nrf91_i2c.h" |
| |
| #include "hardware/nrf91_twi.h" |
| #include "hardware/nrf91_utils.h" |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* I2C Device Private Data */ |
| |
| struct nrf91_i2c_priv_s |
| { |
| const struct i2c_ops_s *ops; /* Standard I2C operations */ |
| uint32_t base; /* TWI base address */ |
| uint32_t scl_pin; /* SCL pin configuration */ |
| uint32_t sda_pin; /* SDA pin configuration */ |
| int refs; /* Reference count */ |
| int status; /* I2C transfer status */ |
| #ifndef CONFIG_I2C_POLLED |
| uint32_t irq; /* TWI interrupt */ |
| #endif |
| uint8_t msgc; /* Message count */ |
| struct i2c_msg_s *msgv; /* Message list */ |
| uint8_t *ptr; /* Current message buffer */ |
| #ifdef CONFIG_NRF91_I2C_MASTER_COPY_BUF_SIZE |
| /* Static buffer used for continued messages */ |
| |
| uint8_t copy_buf[CONFIG_NRF91_I2C_MASTER_COPY_BUF_SIZE]; |
| #endif |
| uint32_t freq; /* Current I2C frequency */ |
| int dcnt; /* Current message length */ |
| uint16_t flags; /* Current message flags */ |
| uint16_t addr; /* Current I2C address */ |
| mutex_t lock; /* Mutual exclusion mutex */ |
| #ifndef CONFIG_I2C_POLLED |
| sem_t sem_isr; /* Interrupt wait semaphore */ |
| #endif |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| static inline void nrf91_i2c_putreg(struct nrf91_i2c_priv_s *priv, |
| uint32_t offset, |
| uint32_t value); |
| static inline uint32_t nrf91_i2c_getreg(struct nrf91_i2c_priv_s *priv, |
| uint32_t offset); |
| static int nrf91_i2c_transfer(struct i2c_master_s *dev, |
| struct i2c_msg_s *msgs, |
| int count); |
| #ifdef CONFIG_I2C_RESET |
| static int nrf91_i2c_reset(struct i2c_master_s *dev); |
| #endif |
| #ifndef CONFIG_I2C_POLLED |
| static int nrf91_i2c_isr(int irq, void *context, void *arg); |
| #endif |
| static int nrf91_i2c_deinit(struct nrf91_i2c_priv_s *priv); |
| static int nrf91_i2c_init(struct nrf91_i2c_priv_s *priv); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* I2C operations */ |
| |
| static const struct i2c_ops_s g_nrf91_i2c_ops = |
| { |
| .transfer = nrf91_i2c_transfer |
| #ifdef CONFIG_I2C_RESET |
| , .reset = nrf91_i2c_reset |
| #endif |
| }; |
| |
| /* I2C0 (TWI0) device */ |
| |
| #ifdef CONFIG_NRF91_I2C0_MASTER |
| |
| static struct nrf91_i2c_priv_s g_nrf91_i2c0_priv = |
| { |
| .ops = &g_nrf91_i2c_ops, |
| .base = NRF91_TWIM0_BASE, |
| .scl_pin = BOARD_I2C0_SCL_PIN, |
| .sda_pin = BOARD_I2C0_SDA_PIN, |
| .refs = 0, |
| .lock = NXMUTEX_INITIALIZER, |
| #ifndef CONFIG_I2C_POLLED |
| .sem_isr = SEM_INITIALIZER(0), |
| .irq = NRF91_IRQ_SERIAL0, |
| #endif |
| .msgc = 0, |
| .msgv = NULL, |
| .ptr = NULL, |
| .freq = 0, |
| .dcnt = 0, |
| .flags = 0, |
| .addr = 0, |
| }; |
| #endif |
| |
| /* I2C1 (TWI1) device */ |
| |
| #ifdef CONFIG_NRF91_I2C1_MASTER |
| static struct nrf91_i2c_priv_s g_nrf91_i2c1_priv = |
| { |
| .ops = &g_nrf91_i2c_ops, |
| .base = NRF91_TWIM1_BASE, |
| .scl_pin = BOARD_I2C1_SCL_PIN, |
| .sda_pin = BOARD_I2C1_SDA_PIN, |
| .refs = 0, |
| .lock = NXMUTEX_INITIALIZER, |
| #ifndef CONFIG_I2C_POLLED |
| .sem_isr = SEM_INITIALIZER(0), |
| .irq = NRF91_IRQ_SERIAL1, |
| #endif |
| .msgc = 0, |
| .msgv = NULL, |
| .ptr = NULL, |
| .freq = 0, |
| .dcnt = 0, |
| .flags = 0, |
| .addr = 0, |
| }; |
| #endif |
| |
| /* I2C2 (TWI3) device */ |
| |
| #ifdef CONFIG_NRF91_I2C2_MASTER |
| static struct nrf91_i2c_priv_s g_nrf91_i2c2_priv = |
| { |
| .ops = &g_nrf91_i2c_ops, |
| .base = NRF91_TWIM2_BASE, |
| .scl_pin = BOARD_I2C2_SCL_PIN, |
| .sda_pin = BOARD_I2C2_SDA_PIN, |
| .refs = 0, |
| .lock = NXMUTEX_INITIALIZER, |
| #ifndef CONFIG_I2C_POLLED |
| .sem_isr = SEM_INITIALIZER(0), |
| .irq = NRF91_IRQ_SERIAL2, |
| #endif |
| .msgc = 0, |
| .msgv = NULL, |
| .ptr = NULL, |
| .freq = 0, |
| .dcnt = 0, |
| .flags = 0, |
| .addr = 0, |
| }; |
| #endif |
| |
| /* I2C3 (TWI3) device */ |
| |
| #ifdef CONFIG_NRF91_I2C3_MASTER |
| static struct nrf91_i2c_priv_s g_nrf91_i2c3_priv = |
| { |
| .ops = &g_nrf91_i2c_ops, |
| .base = NRF91_TWIM2_BASE, |
| .scl_pin = BOARD_I2C3_SCL_PIN, |
| .sda_pin = BOARD_I2C3_SDA_PIN, |
| .refs = 0, |
| .lock = NXMUTEX_INITIALIZER, |
| #ifndef CONFIG_I2C_POLLED |
| .sem_isr = SEM_INITIALIZER(0), |
| .irq = NRF91_IRQ_SERIAL3, |
| #endif |
| .msgc = 0, |
| .msgv = NULL, |
| .ptr = NULL, |
| .freq = 0, |
| .dcnt = 0, |
| .flags = 0, |
| .addr = 0, |
| }; |
| #endif |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nrf91_i2c_putreg |
| * |
| * Description: |
| * Put a 32-bit register value by offset |
| * |
| ****************************************************************************/ |
| |
| static inline void nrf91_i2c_putreg(struct nrf91_i2c_priv_s *priv, |
| uint32_t offset, |
| uint32_t value) |
| { |
| putreg32(value, priv->base + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf91_i2c_getreg |
| * |
| * Description: |
| * Get a 32-bit register value by offset |
| * |
| ****************************************************************************/ |
| |
| static inline uint32_t nrf91_i2c_getreg(struct nrf91_i2c_priv_s *priv, |
| uint32_t offset) |
| { |
| return getreg32(priv->base + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: nrf91_i2c_transfer |
| * |
| * Description: |
| * Generic I2C transfer function |
| * |
| ****************************************************************************/ |
| |
| static int nrf91_i2c_transfer(struct i2c_master_s *dev, |
| struct i2c_msg_s *msgs, |
| int count) |
| { |
| struct nrf91_i2c_priv_s *priv = (struct nrf91_i2c_priv_s *)dev; |
| uint32_t regval = 0; |
| int ret = OK; |
| #ifndef CONFIG_NRF91_I2C_MASTER_DISABLE_NOSTART |
| uint8_t *pack_buf = NULL; |
| #endif |
| |
| ret = nxmutex_lock(&priv->lock); |
| if (ret < 0) |
| { |
| return ret; |
| } |
| |
| /* Reset ptr and dcnt */ |
| |
| priv->dcnt = 0; |
| priv->ptr = NULL; |
| |
| priv->msgv = msgs; |
| priv->msgc = count; |
| |
| /* Reset I2C transfer status */ |
| |
| priv->status = OK; |
| |
| i2cinfo("I2C TRANSFER count=%d\n", count); |
| |
| /* Do we need change I2C bus frequency ? */ |
| |
| if (priv->msgv->frequency != priv->freq) |
| { |
| /* Get TWI frequency */ |
| |
| switch (priv->msgv->frequency) |
| { |
| case 100000: |
| { |
| regval = TWIM_FREQUENCY_100KBPS; |
| break; |
| } |
| |
| case 250000: |
| { |
| regval = TWIM_FREQUENCY_250KBPS; |
| break; |
| } |
| |
| case 400000: |
| { |
| regval = TWIM_FREQUENCY_400KBPS; |
| break; |
| } |
| |
| default: |
| { |
| ret = -EINVAL; |
| goto errout; |
| } |
| } |
| |
| /* Write TWI frequency */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_FREQUENCY_OFFSET, regval); |
| |
| /* Save the new I2C frequency */ |
| |
| priv->freq = priv->msgv->frequency; |
| } |
| |
| /* I2C transfer */ |
| |
| do |
| { |
| /* Get current message data */ |
| |
| priv->ptr = priv->msgv->buffer; |
| priv->dcnt = priv->msgv->length; |
| priv->flags = priv->msgv->flags; |
| priv->addr = priv->msgv->addr; |
| |
| i2cinfo("ptr=%p dcnt=%d flags=%d addr=%d\n", |
| priv->ptr, priv->dcnt, priv->flags, priv->addr); |
| |
| /* Write TWI address */ |
| |
| regval = priv->addr; |
| nrf91_i2c_putreg(priv, NRF91_TWIM_ADDRESS_OFFSET, regval); |
| |
| if ((priv->flags & I2C_M_READ) == 0) |
| { |
| #ifndef CONFIG_NRF91_I2C_MASTER_DISABLE_NOSTART |
| /* Check if we need to combine messages */ |
| |
| if (priv->msgc > 1) |
| { |
| if (priv->msgv[1].flags & I2C_M_NOSTART) |
| { |
| /* More than 2 messages not supported */ |
| |
| DEBUGASSERT(priv->msgc < 3); |
| |
| /* Combine buffers */ |
| |
| if ((priv->msgv[0].length + |
| priv->msgv[1].length) <= |
| CONFIG_NRF91_I2C_MASTER_COPY_BUF_SIZE) |
| { |
| pack_buf = priv->copy_buf; |
| } |
| else |
| { |
| pack_buf = kmm_malloc(priv->msgv[0].length + |
| priv->msgv[1].length); |
| if (pack_buf == NULL) |
| { |
| return -ENOMEM; |
| } |
| } |
| |
| /* Combine messages */ |
| |
| memcpy(pack_buf, priv->msgv[0].buffer, |
| priv->msgv[0].length); |
| memcpy(pack_buf + priv->msgv[0].length, |
| priv->msgv[1].buffer, priv->msgv[1].length); |
| |
| /* Use new buffer to transmit data */ |
| |
| priv->ptr = pack_buf; |
| priv->dcnt = priv->msgv[0].length + priv->msgv[1].length; |
| |
| /* Next message */ |
| |
| priv->msgc -= 1; |
| priv->msgv += 1; |
| } |
| } |
| #else |
| if (priv->msgc > 1) |
| { |
| if (priv->msgv[1].flags & I2C_M_NOSTART) |
| { |
| /* Not supported */ |
| |
| DEBUGPANIC(); |
| } |
| } |
| #endif |
| |
| /* Write TXD data pointer */ |
| |
| regval = (uint32_t)priv->ptr; |
| DEBUGASSERT(nrf91_easydma_valid(regval)); |
| nrf91_i2c_putreg(priv, NRF91_TWIM_TXDPTR_OFFSET, regval); |
| |
| /* Write number of bytes in TXD buffer */ |
| |
| regval = priv->dcnt; |
| nrf91_i2c_putreg(priv, NRF91_TWIM_TXDMAXCNT_OFFSET, regval); |
| |
| /* Start TX sequence */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_TASKS_STARTTX_OFFSET, 1); |
| |
| /* Wait for last TX event */ |
| |
| #ifdef CONFIG_I2C_POLLED |
| while (nrf91_i2c_getreg(priv, |
| NRF91_TWIM_EVENTS_LASTTX_OFFSET) != 1); |
| while (1) |
| { |
| regval = nrf91_i2c_getreg(priv, |
| NRF91_TWIM_ERRORSRC_OFFSET) & 0x7; |
| if (regval != 0) |
| { |
| i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval); |
| ret = -1; |
| nrf91_i2c_putreg(priv, |
| NRF91_TWIM_ERRORSRC_OFFSET, 0x7); |
| goto errout; |
| } |
| |
| if (nrf91_i2c_getreg(priv, |
| NRF91_TWIM_EVENTS_LASTTX_OFFSET) == 1) |
| { |
| break; |
| } |
| } |
| |
| /* Clear event */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_LASTTX_OFFSET, 0); |
| #else |
| nxsem_wait(&priv->sem_isr); |
| |
| if (priv->status < 0) |
| { |
| ret = priv->status; |
| goto errout; |
| } |
| #endif |
| } |
| else |
| { |
| /* Write RXD data pointer */ |
| |
| regval = (uint32_t)priv->ptr; |
| DEBUGASSERT(nrf91_easydma_valid(regval)); |
| nrf91_i2c_putreg(priv, NRF91_TWIM_RXDPTR_OFFSET, regval); |
| |
| /* Write number of bytes in RXD buffer */ |
| |
| regval = priv->dcnt; |
| nrf91_i2c_putreg(priv, NRF91_TWIM_RXDMAXCNT_OFFSET, regval); |
| |
| /* Start RX sequence */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_TASKS_STARTRX_OFFSET, 1); |
| |
| /* Wait for last RX done */ |
| |
| #ifdef CONFIG_I2C_POLLED |
| while (1) |
| { |
| regval = nrf91_i2c_getreg(priv, |
| NRF91_TWIM_ERRORSRC_OFFSET) & 0x7; |
| if (regval != 0) |
| { |
| i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval); |
| ret = -1; |
| nrf91_i2c_putreg(priv, |
| NRF91_TWIM_ERRORSRC_OFFSET, 0x7); |
| goto errout; |
| } |
| |
| if (nrf91_i2c_getreg(priv, |
| NRF91_TWIM_EVENTS_LASTRX_OFFSET) == 1) |
| { |
| break; |
| } |
| } |
| |
| /* Clear event */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_LASTRX_OFFSET, 0); |
| #else |
| nxsem_wait(&priv->sem_isr); |
| |
| if (priv->status < 0) |
| { |
| ret = priv->status; |
| goto errout; |
| } |
| #endif |
| } |
| |
| /* Next message */ |
| |
| priv->msgc -= 1; |
| priv->msgv += 1; |
| } |
| while (priv->msgc > 0); |
| |
| /* TWIM stop */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_TASKS_STOP_OFFSET, 1); |
| |
| /* Wait for stop event */ |
| |
| #ifdef CONFIG_I2C_POLLED |
| while (1) |
| { |
| regval = nrf91_i2c_getreg(priv, |
| NRF91_TWIM_ERRORSRC_OFFSET) & 0x7; |
| if (regval != 0) |
| { |
| i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval); |
| ret = -1; |
| nrf91_i2c_putreg(priv, |
| NRF91_TWIM_ERRORSRC_OFFSET, 0x7); |
| goto errout; |
| } |
| |
| if (nrf91_i2c_getreg(priv, |
| NRF91_TWIM_EVENTS_STOPPED_OFFSET) == 1) |
| { |
| break; |
| } |
| } |
| |
| /* Clear event */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_STOPPED_OFFSET, 0); |
| #else |
| nxsem_wait(&priv->sem_isr); |
| |
| if (priv->status < 0) |
| { |
| ret = priv->status; |
| goto errout; |
| } |
| #endif |
| |
| errout: |
| #ifndef CONFIG_NRF91_I2C_MASTER_DISABLE_NOSTART |
| if (pack_buf != NULL && pack_buf != priv->copy_buf) |
| { |
| kmm_free(pack_buf); |
| } |
| #endif |
| |
| nxmutex_unlock(&priv->lock); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: nrf91_i2c_reset |
| * |
| * Description: |
| * Perform an I2C bus reset in an attempt to break loose stuck I2C devices. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * |
| * Returned Value: |
| * Zero (OK) on success; a negated errno value on failure. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_I2C_RESET |
| static int nrf91_i2c_reset(struct i2c_master_s *dev) |
| { |
| #error not implemented |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nrf91_i2c_isr |
| * |
| * Description: |
| * Common I2C interrupt service routine |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_I2C_POLLED |
| static int nrf91_i2c_isr(int irq, void *context, void *arg) |
| { |
| struct nrf91_i2c_priv_s *priv = (struct nrf91_i2c_priv_s *)arg; |
| uint32_t regval = 0; |
| |
| /* Reset I2C status */ |
| |
| priv->status = OK; |
| |
| if ((priv->flags & I2C_M_READ) == 0) |
| { |
| if (nrf91_i2c_getreg(priv, NRF91_TWIM_EVENTS_LASTTX_OFFSET) == 1) |
| { |
| i2cinfo("I2C LASTTX\n"); |
| |
| /* TX done */ |
| |
| nxsem_post(&priv->sem_isr); |
| |
| /* Clear event */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_LASTTX_OFFSET, 0); |
| |
| return OK; |
| } |
| } |
| else |
| { |
| if (nrf91_i2c_getreg(priv, NRF91_TWIM_EVENTS_LASTRX_OFFSET) == 1) |
| { |
| i2cinfo("I2C LASTRX\n"); |
| |
| /* RX done */ |
| |
| nxsem_post(&priv->sem_isr); |
| |
| /* Clear event */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_LASTRX_OFFSET, 0); |
| |
| return OK; |
| } |
| } |
| |
| if (nrf91_i2c_getreg(priv, NRF91_TWIM_EVENTS_STOPPED_OFFSET) == 1) |
| { |
| i2cinfo("I2C STOPPED\n"); |
| |
| /* STOPPED event */ |
| |
| nxsem_post(&priv->sem_isr); |
| |
| /* Clear event */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_STOPPED_OFFSET, 0); |
| } |
| |
| if (nrf91_i2c_getreg(priv, NRF91_TWIM_EVENTS_ERROR_OFFSET) == 1) |
| { |
| regval = nrf91_i2c_getreg(priv, NRF91_TWIM_ERRORSRC_OFFSET) & 0x7; |
| |
| i2cerr("Error SRC: 0x%08" PRIx32 "\n", regval); |
| |
| /* Set ERROR status */ |
| |
| if (regval & TWIM_ERRORSRC_OVERRUN) |
| { |
| /* Overrun error */ |
| |
| priv->status = -EIO; |
| } |
| else |
| { |
| /* NACK */ |
| |
| priv->status = -ENXIO; |
| } |
| |
| /* ERROR event */ |
| |
| nxsem_post(&priv->sem_isr); |
| |
| /* Clear event */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_EVENTS_ERROR_OFFSET, 0); |
| nrf91_i2c_putreg(priv, NRF91_TWIM_ERRORSRC_OFFSET, 0x7); |
| } |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: nrf91_i2c_init |
| * |
| * Description: |
| * Setup the I2C hardware, ready for operation with defaults |
| * |
| ****************************************************************************/ |
| |
| static int nrf91_i2c_init(struct nrf91_i2c_priv_s *priv) |
| { |
| uint32_t regval = 0; |
| int pin = 0; |
| int port = 0; |
| |
| /* Disable TWI interface */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_ENABLE_OFFSET, TWIM_ENABLE_DIS); |
| |
| /* Configure SCL and SDA pins */ |
| |
| nrf91_gpio_config(priv->scl_pin); |
| nrf91_gpio_config(priv->sda_pin); |
| |
| /* Select SCL pin */ |
| |
| pin = GPIO_PIN_DECODE(priv->scl_pin); |
| port = GPIO_PORT_DECODE(priv->scl_pin); |
| |
| regval = (pin << TWIM_PSELSCL_PIN_SHIFT); |
| regval |= (port << TWIM_PSELSCL_PORT_SHIFT); |
| nrf91_i2c_putreg(priv, NRF91_TWIM_PSELSCL_OFFSET, regval); |
| |
| /* Select SDA pin */ |
| |
| pin = GPIO_PIN_DECODE(priv->sda_pin); |
| port = GPIO_PORT_DECODE(priv->sda_pin); |
| |
| regval = (pin << TWIM_PSELSDA_PIN_SHIFT); |
| regval |= (port << TWIM_PSELSDA_PORT_SHIFT); |
| nrf91_i2c_putreg(priv, NRF91_TWIM_PSELSDA_OFFSET, regval); |
| |
| /* Enable TWI interface */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_ENABLE_OFFSET, TWIM_ENABLE_EN); |
| |
| #ifndef CONFIG_I2C_POLLED |
| /* Enable I2C interrupts */ |
| |
| regval = (TWIM_INT_LASTRX | TWIM_INT_LASTTX | TWIM_INT_STOPPED | |
| TWIM_INT_ERROR); |
| nrf91_i2c_putreg(priv, NRF91_TWIM_INTEN_OFFSET, regval); |
| |
| /* Attach error and event interrupts to the ISRs */ |
| |
| irq_attach(priv->irq, nrf91_i2c_isr, priv); |
| up_enable_irq(priv->irq); |
| #endif |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: nrf91_i2c_deinit |
| * |
| * Description: |
| * Shutdown the I2C hardware |
| * |
| ****************************************************************************/ |
| |
| static int nrf91_i2c_deinit(struct nrf91_i2c_priv_s *priv) |
| { |
| /* Disable TWI interface */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_ENABLE_OFFSET, TWIM_ENABLE_DIS); |
| |
| /* Unconfigure GPIO pins */ |
| |
| nrf91_gpio_unconfig(priv->scl_pin); |
| nrf91_gpio_unconfig(priv->sda_pin); |
| |
| /* Deatach TWI from GPIO */ |
| |
| nrf91_i2c_putreg(priv, NRF91_TWIM_PSELSCL_OFFSET, TWIM_PSELSCL_RESET); |
| nrf91_i2c_putreg(priv, NRF91_TWIM_PSELSDA_OFFSET, TWIM_PSELSDA_RESET); |
| |
| /* Disable and detach interrupts */ |
| |
| #ifndef CONFIG_I2C_POLLED |
| up_disable_irq(priv->irq); |
| irq_detach(priv->irq); |
| #endif |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: nrf91_i2cbus_initialize |
| * |
| * Description: |
| * Initialize one I2C bus |
| * |
| ****************************************************************************/ |
| |
| struct i2c_master_s *nrf91_i2cbus_initialize(int port) |
| { |
| struct nrf91_i2c_priv_s *priv = NULL; |
| |
| i2cinfo("I2C INITIALIZE port=%d\n", port); |
| |
| /* Get interface */ |
| |
| switch (port) |
| { |
| #ifdef CONFIG_NRF91_I2C0_MASTER |
| case 0: |
| { |
| priv = (struct nrf91_i2c_priv_s *)&g_nrf91_i2c0_priv; |
| break; |
| } |
| #endif |
| |
| #ifdef CONFIG_NRF91_I2C1_MASTER |
| case 1: |
| { |
| priv = (struct nrf91_i2c_priv_s *)&g_nrf91_i2c1_priv; |
| break; |
| } |
| #endif |
| |
| #ifdef CONFIG_NRF91_I2C2_MASTER |
| case 2: |
| { |
| priv = (struct nrf91_i2c_priv_s *)&g_nrf91_i2c2_priv; |
| break; |
| } |
| #endif |
| |
| #ifdef CONFIG_NRF91_I2C3_MASTER |
| case 3: |
| { |
| priv = (struct nrf91_i2c_priv_s *)&g_nrf91_i2c3_priv; |
| break; |
| } |
| #endif |
| |
| default: |
| { |
| return NULL; |
| } |
| } |
| |
| /* Initialize private data for the first time, increment reference count, |
| * power-up hardware and configure GPIOs. |
| */ |
| |
| nxmutex_lock(&priv->lock); |
| if (priv->refs++ == 0) |
| { |
| /* Initialize I2C */ |
| |
| nrf91_i2c_init(priv); |
| } |
| |
| nxmutex_unlock(&priv->lock); |
| return (struct i2c_master_s *)priv; |
| } |
| |
| /**************************************************************************** |
| * Name: nrf91_i2cbus_uninitialize |
| * |
| * Description: |
| * Uninitialize an I2C bus |
| * |
| ****************************************************************************/ |
| |
| int nrf91_i2cbus_uninitialize(struct i2c_master_s *dev) |
| { |
| struct nrf91_i2c_priv_s *priv = (struct nrf91_i2c_priv_s *)dev; |
| |
| DEBUGASSERT(dev); |
| |
| /* Decrement reference count and check for underflow */ |
| |
| if (priv->refs == 0) |
| { |
| return ERROR; |
| } |
| |
| nxmutex_lock(&priv->lock); |
| if (--priv->refs) |
| { |
| nxmutex_unlock(&priv->lock); |
| return OK; |
| } |
| |
| /* Disable power and other HW resource (GPIO's) */ |
| |
| nrf91_i2c_deinit(priv); |
| nxmutex_unlock(&priv->lock); |
| |
| return OK; |
| } |