diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 27e6fb13b7..48c297abfb 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -604,6 +604,7 @@ config ARCH_CHIP_MX8MP select ARCH_HAVE_MPU select ARCH_HAVE_FETCHADD select ARCH_HAVE_RAMFUNCS + select ARCH_HAVE_I2CRESET select ARM_HAVE_MPU_UNIFIED select ARMV7M_HAVE_ICACHE select ARMV7M_HAVE_DCACHE diff --git a/arch/arm/src/mx8mp/Kconfig b/arch/arm/src/mx8mp/Kconfig index 496057ea94..d8b01fa673 100644 --- a/arch/arm/src/mx8mp/Kconfig +++ b/arch/arm/src/mx8mp/Kconfig @@ -31,6 +31,59 @@ config MX8MP_UART4 select UART4_SERIALDRIVER select ARCH_HAVE_SERIAL_TERMIOS +##################################################################### +# I2C Configuration (Master) +##################################################################### + +config MX8MP_I2C + bool "I2C Master" + select I2C + ---help--- + Build in support for I2C master mode. + +if MX8MP_I2C + + config MX8MP_I2C1 + bool "I2C1" + default n + select I2C + + config MX8MP_I2C2 + bool "I2C2" + default n + select I2C + + config MX8MP_I2C3 + bool "I2C3" + default n + select I2C + + config MX8MP_I2C4 + bool "I2C4" + default n + select I2C + + config MX8MP_I2C5 + bool "I2C5" + default n + select I2C + + config MX8MP_I2C6 + bool "I2C6" + default n + select I2C + + config MX8MP_I2C_DRIVER + bool "I2C character driver" + default n + select I2C_DRIVER + ---help--- + Build in support for a character driver at /dev/i2c[N] that may be + used to perform I2C bus transfers from applications. The intent of + this driver is to support I2C testing. It is not suitable for use + in any real driver application. + +endif # MX8MP_I2C endmenu # These "hidden" settings determine whether a peripheral option is available diff --git a/arch/arm/src/mx8mp/Make.defs b/arch/arm/src/mx8mp/Make.defs index 53a891086a..2338e23715 100644 --- a/arch/arm/src/mx8mp/Make.defs +++ b/arch/arm/src/mx8mp/Make.defs @@ -35,3 +35,7 @@ endif ifneq ($(CONFIG_SCHED_TICKLESS),y) CHIP_CSRCS += mx8mp_timerisr.c endif + +ifeq ($(CONFIG_I2C),y) +CHIP_CSRCS += mx8mp_i2c.c +endif diff --git a/arch/arm/src/mx8mp/hardware/mx8mp_i2c.h b/arch/arm/src/mx8mp/hardware/mx8mp_i2c.h new file mode 100644 index 0000000000..6e4bf3bb7e --- /dev/null +++ b/arch/arm/src/mx8mp/hardware/mx8mp_i2c.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * arch/arm/src/mx8mp/hardware/mx8mp_i2c.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_MX8MP_HARDWARE_MX8MP_I2C_H +#define __ARCH_ARM_SRC_MX8MP_HARDWARE_MX8MP_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* I2C Register Offsets *****************************************************/ + +#define IADR_OFFSET 0x0000 +#define IFDR_OFFSET 0x0004 +#define I2CR_OFFSET 0x0008 +#define I2SR_OFFSET 0x000c +#define I2DR_OFFSET 0x0010 + +/* I2C Register Bit Definitions *********************************************/ + +#define IADR_SHIFT 1 +#define IADR_MASK (0x7f << IADR_SHIFT) + +#define IFDR_SHIFT 0 +#define IFDR_MASK (0x3f << IFDR_SHIFT) + +#define I2CR_IEN (1 << 7) /* enable */ +#define I2CR_IIEN (1 << 6) /* interrupt enable */ +#define I2CR_MSTA (1 << 5) /* master/start */ +#define I2CR_MTX (1 << 4) /* transmit */ +#define I2CR_TXAK (1 << 3) /* ACK/NACK on reception */ +#define I2CR_RSTA (1 << 2) /* repeated start */ + +#define I2SR_ICF (1 << 7) /* transfer in progress */ +#define I2SR_IAAS (1 << 6) /* addressed as a slave */ +#define I2SR_IBB (1 << 5) /* is busy */ +#define I2SR_IAL (1 << 4) /* arbitration lost */ +#define I2SR_SRW (1 << 2) /* slave read/write (if in slave mode) */ +#define I2SR_IIF (1 << 1) /* interrupt is pending */ +#define I2SR_RXAK (1 << 0) /* no ack detected */ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#endif /* __ARCH_ARM_SRC_MX8MP_HARDWARE_MX8MP_I2C_H */ diff --git a/arch/arm/src/mx8mp/mx8mp_i2c.c b/arch/arm/src/mx8mp/mx8mp_i2c.c new file mode 100644 index 0000000000..47c7b008d7 --- /dev/null +++ b/arch/arm/src/mx8mp/mx8mp_i2c.c @@ -0,0 +1,819 @@ +/**************************************************************************** + * arch/arm/src/mx8mp/mx8mp_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 +#include + +#include "mx8mp_i2c.h" +#include "mx8mp_ccm.h" +#include "hardware/mx8mp_memorymap.h" + +#include +#include "arm_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define I2C_STALL_TIMEOUT MSEC2TICK(10) /* 10 ms */ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +struct mx8mp_i2c_s; + +static bool is_busy(struct mx8mp_i2c_s *priv); +static void mx8mp_i2c_enable(struct mx8mp_i2c_s *priv); +static void mx8mp_i2c_disable(struct mx8mp_i2c_s *priv); +static int mx8mp_i2c_interrupt(int irq, void *context, void *arg); +static void mx8mp_i2c_reset_bus(struct mx8mp_i2c_s *dev); + +/* I2C lower half driver methods */ + +static int mx8mp_i2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count); +static int mx8mp_i2c_reset(struct i2c_master_s *dev); + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* I2C Device Private Data */ + +struct mx8mp_i2c_s +{ + /* Generic I2C device */ + + struct i2c_master_s dev; + + /* Port configuration */ + + uint32_t base; /* I2C base address */ + int clock; /* Peripheral clock as described in hardware/mx8mp_ccm.h */ + uint16_t irq; /* IRQ number */ + uint32_t frequency; /* Current I2C frequency */ + int refs; /* Reference count */ + mutex_t lock; /* Only one thread can access at a time */ + sem_t wait; /* IRQ wait sync */ +}; + +/* I2C lower half driver operations */ + +static const struct i2c_ops_s mx8mp_i2c_ops = +{ + .transfer = mx8mp_i2c_transfer +#ifdef CONFIG_I2C_RESET + , .reset = mx8mp_i2c_reset +#endif +}; + +/* I2C device structures */ + +#ifdef CONFIG_MX8MP_I2C1 +static struct mx8mp_i2c_s g_i2c1_dev = +{ + .dev.ops = &mx8mp_i2c_ops, + .base = MX8M_I2C1, + .clock = I2C1_CLK_ROOT, + .irq = MX8MP_IRQ_I2C1, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, + .wait = SEM_INITIALIZER(0), +}; +#endif + +#ifdef CONFIG_MX8MP_I2C2 +static struct mx8mp_i2c_s g_i2c2_dev = +{ + .dev.ops = &mx8mp_i2c_ops, + .base = MX8M_I2C2, + .clock = I2C2_CLK_ROOT, + .irq = MX8MP_IRQ_I2C2, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, + .wait = SEM_INITIALIZER(0), +}; +#endif + +#ifdef CONFIG_MX8MP_I2C3 +static struct mx8mp_i2c_s g_i2c3_dev = +{ + .dev.ops = &mx8mp_i2c_ops, + .base = MX8M_I2C3, + .clock = I2C3_CLK_ROOT, + .irq = MX8MP_IRQ_I2C3, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, + .wait = SEM_INITIALIZER(0), +}; +#endif + +#ifdef CONFIG_MX8MP_I2C4 +static struct mx8mp_i2c_s g_i2c4_dev = +{ + .dev.ops = &mx8mp_i2c_ops, + .base = MX8M_I2C4, + .clock = I2C4_CLK_ROOT, + .irq = MX8MP_IRQ_I2C4, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, + .wait = SEM_INITIALIZER(0), +}; +#endif + +#ifdef CONFIG_MX8MP_I2C5 +static struct mx8mp_i2c_s g_i2c5_dev = +{ + .dev.ops = &mx8mp_i2c_ops, + .base = MX8M_I2C5, + .clock = I2C5_CLK_ROOT, + .irq = MX8MP_IRQ_I2C5, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, + .wait = SEM_INITIALIZER(0), +}; +#endif + +#ifdef CONFIG_MX8MP_I2C6 +static struct mx8mp_i2c_s g_i2c6_dev = +{ + .dev.ops = &mx8mp_i2c_ops, + .base = MX8M_I2C6, + .clock = I2C6_CLK_ROOT, + .irq = MX8MP_IRQ_I2C6, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, + .wait = SEM_INITIALIZER(0), +}; +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint16_t DIVIDERS_MAP[] = +{ + 30, 32, 36, 42, 48, 52, 60, 72, + 80, 88, 104, 128, 144, 160, 192, 240, + 288, 320, 384, 480, 576, 640, 768, 960, + 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840, + 22, 24, 26, 28, 32, 36, 40, 44, + 48, 56, 64, 72, 80, 96, 112, 128, + 160, 192, 224, 256, 320, 384, 448, 512, + 640, 768, 896, 1024, 1280, 1536, 1792, 2048 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mx8mp_i2c_init + * + * Description: + * Setup the I2C hardware, ready for operation with defaults + * + ****************************************************************************/ + +static int mx8mp_i2c_init(struct mx8mp_i2c_s *priv) +{ + /* enable peripheral */ + + mx8mp_i2c_enable(priv); + + /* Attach Interrupt Handler */ + + irq_attach(priv->irq, mx8mp_i2c_interrupt, priv); + + /* Enable Interrupt Handler */ + + up_enable_irq(priv->irq); + + /* Force a frequency update */ + + priv->frequency = 0; + + return 0; +} + +/**************************************************************************** + * Name: mx8mp_i2c_deinit + * + * Description: + * Shutdown the I2C hardware + * + ****************************************************************************/ + +static int mx8mp_i2c_deinit(struct mx8mp_i2c_s *priv) +{ + /* Disable and detach interrupts */ + + up_disable_irq(priv->irq); + irq_detach(priv->irq); + + /* Disable peripheral */ + + mx8mp_i2c_disable(priv); + + return 0; +} + +/**************************************************************************** + * Name: mx8mp_i2c_enable + * + * Description: + * Enable the I2C peripheral + * + ****************************************************************************/ + +static void mx8mp_i2c_enable(struct mx8mp_i2c_s *priv) +{ + /* Set slave address here to support it (IFDR) */ + + putreg16(0, priv->base + I2SR_OFFSET); + modreg16(I2CR_IEN, I2CR_IEN, priv->base + I2CR_OFFSET); + modreg16(I2CR_IIEN, I2CR_IIEN, priv->base + I2CR_OFFSET); +} + +/**************************************************************************** + * Name: mx8mp_i2c_disable + * + * Description: + * Disable the I2C peripheral + * + ****************************************************************************/ + +static void mx8mp_i2c_disable(struct mx8mp_i2c_s *priv) +{ + putreg16(0, priv->base + I2CR_OFFSET); +} + +/**************************************************************************** + * Name: is_busy + * + * Description: + * Check if the bus is busy (true) or not (false) + * + ****************************************************************************/ + +static bool is_busy(struct mx8mp_i2c_s *priv) +{ + return (getreg16(priv->base + I2SR_OFFSET) & I2SR_IBB); +} + +/**************************************************************************** + * Name: mx8mp_i2c_wait + * + * Description: + * Wait for the IRQ to be triggered. + * + ****************************************************************************/ + +static inline int mx8mp_i2c_wait_irq(struct mx8mp_i2c_s *priv, int usec) +{ + return nxsem_tickwait(&priv->wait, USEC2TICK(usec)); +} + +/**************************************************************************** + * Name: mx8mp_i2c_send_start + * + * Description: + * Emit start bit on the bus + * + ****************************************************************************/ + +static int mx8mp_i2c_send_start(struct mx8mp_i2c_s *priv) +{ + if (is_busy(priv)) + { + return -EBUSY; + } + + modreg16(I2CR_MSTA | I2CR_MTX | I2CR_TXAK, + I2CR_MSTA | I2CR_MTX | I2CR_TXAK, + priv->base + I2CR_OFFSET); + + return 0; +} + +/**************************************************************************** + * Name: mx8mp_i2c_send_repeat_start + * + * Description: + * Emit repeat start bit on the bus + * + ****************************************************************************/ + +static int mx8mp_i2c_send_repeat_start(struct mx8mp_i2c_s *priv) +{ + if (is_busy(priv) && !(getreg16(priv->base + I2SR_OFFSET) & I2CR_MSTA)) + { + return -EBUSY; + } + + modreg16(I2CR_RSTA, I2CR_RSTA, priv->base + I2CR_OFFSET); + + return 0; +} + +/**************************************************************************** + * Name: mx8mp_i2c_send_stop + * + * Description: + * Emit stop bit on the bus + * + ****************************************************************************/ + +static void mx8mp_i2c_send_stop(struct mx8mp_i2c_s *priv) +{ + modreg16(0, I2CR_MSTA | I2CR_MTX | I2CR_TXAK, priv->base + I2CR_OFFSET); +} + +/**************************************************************************** + * Name: mx8mp_i2c_write_byte + * + * Description: + * Write a byte on the bus, checking for arbitration lost and RX ACK. + * + ****************************************************************************/ + +static int mx8mp_i2c_write_byte(struct mx8mp_i2c_s *priv, uint8_t byte) +{ + int ret; + uint16_t status; + + modreg16(0, I2SR_IIF, priv->base + I2SR_OFFSET); + putreg16(byte, priv->base + I2DR_OFFSET); + + ret = mx8mp_i2c_wait_irq(priv, 500); + if (ret < 0) + { + return ret; + } + + status = getreg16(priv->base + I2SR_OFFSET); + if (status & I2SR_IAL) + { + /* Arbitration lost */ + + modreg16(0, I2SR_IAL, priv->base + I2SR_OFFSET); + return -EACCES; + } + + if (status & I2SR_RXAK) + { + return -EIO; + } + + return 0; +} + +/**************************************************************************** + * Name: mx8mp_i2c_write_bytes + * + * Description: + * Write multiple byte on the bus, handling the STOP logic. + * + ****************************************************************************/ + +static int mx8mp_i2c_write_bytes(struct mx8mp_i2c_s *priv, + const uint8_t *data, + uint32_t size, + bool is_last) +{ + uint32_t i; + int ret; + + for (i = 0; i < size; ++i) + { + ret = mx8mp_i2c_write_byte(priv, data[i]); + if (ret < 0) + { + return ret; + } + } + + if (is_last) + { + mx8mp_i2c_send_stop(priv); + } + + return 0; +} + +/**************************************************************************** + * Name: mx8mp_i2c_read_bytes + * + * Description: + * Read multiple bytes on the bus, handling the NACK and STOP logic. + * + ****************************************************************************/ + +int mx8mp_i2c_read_bytes(struct mx8mp_i2c_s *priv, + uint8_t *data, + uint32_t size, + bool is_last) +{ + int ret; + uint8_t temp; + uint32_t i; + + temp = getreg16(priv->base + I2CR_OFFSET); + temp &= ~(I2CR_MTX | I2CR_TXAK); + if (size == 1) + { + /* NACK on read if one byte */ + + temp |= I2CR_TXAK; + } + + putreg16(temp, priv->base + I2CR_OFFSET); + + /* dummy read to start reading */ + + modreg16(0, I2SR_IIF, priv->base + I2SR_OFFSET); + data[0] = getreg16(priv->base + I2DR_OFFSET); + + for (i = 0; i < size; ++i) + { + ret = mx8mp_i2c_wait_irq(priv, 500); + if (ret < 0) + { + return ret; + } + + if ((i == (size - 1)) && is_last) + { + mx8mp_i2c_send_stop(priv); + } + else if (i == (size - 2)) + { + /* No ACK on last byte */ + + modreg16(0, I2CR_TXAK, priv->base + I2CR_OFFSET); + } + + modreg16(0, I2SR_IIF, priv->base + I2SR_OFFSET); + data[i] = getreg16(priv->base + I2DR_OFFSET); + } + + return 0; +} + +/**************************************************************************** + * Name: mx8mp_i2c_set_frequency + * + * Description: + * Set the I2C peripheral clock frequency. Note: the real frequency may be + * lower than the targeted one (but never greater). + * + ****************************************************************************/ + +static void mx8mp_i2c_set_frequency(struct mx8mp_i2c_s *priv, + uint32_t frequency) +{ + uint8_t i; + uint8_t divider; + uint32_t old_frequency; + uint32_t clock; + + i2cinfo("frequency=%lu\n", frequency); + + clock = mx8mp_ccm_get_clock(priv->clock); + DEBUGASSERT(clock > frequency); + + if (frequency == priv->frequency) + { + return; + } + + divider = 0; + old_frequency = 0; + + for (i = 0; i < 0x40; ++i) + { + uint32_t current_frequency = clock / DIVIDERS_MAP[i]; + if ((current_frequency <= frequency) && + (current_frequency > old_frequency)) + { + divider = i; + old_frequency = current_frequency; + } + } + + putreg16(divider, priv->base + IFDR_OFFSET); + priv->frequency = frequency; + i2cinfo(" real frequency=%lu (%lu / %d)\n", + old_frequency, clock, DIVIDERS_MAP[divider]); +} + +/**************************************************************************** + * Name: mx8mp_i2c_reset_bus + * + * Description: + * Reset the bus if this one is stall (stuck I2C device(s)) + * + ****************************************************************************/ + +static void mx8mp_i2c_reset_bus(struct mx8mp_i2c_s *priv) +{ + mx8mp_i2c_disable(priv); + nxsig_usleep(50); + mx8mp_i2c_enable(priv); + nxsig_usleep(50); +} + +/**************************************************************************** + * Name: mx8mp_i2c_interrupt + * + * Description: + * The I2C common interrupt handler + * + ****************************************************************************/ + +static int mx8mp_i2c_interrupt(int irq, void *context, void *arg) +{ + struct mx8mp_i2c_s *priv = (struct mx8mp_i2c_s *)arg; + modreg16(0, I2SR_IIF, priv->base + I2SR_OFFSET); + nxsem_post(&priv->wait); + + return 0; +} + +/**************************************************************************** + * Name: mx8mp_i2c_transfer + * + * Description: + * Perform a sequence of I2C transfers + * + ****************************************************************************/ + +static int mx8mp_i2c_transfer(struct i2c_master_s *dev, + struct i2c_msg_s *msgs, int count) +{ + struct mx8mp_i2c_s *priv = (struct mx8mp_i2c_s *)dev; + struct i2c_msg_s *msg; + clock_t deadline; + bool last; + int ret; + int i; + + i2cinfo("msgs=%p count=%d\n", msgs, count); + DEBUGASSERT(dev != NULL && msgs != NULL && (unsigned)count <= UINT16_MAX); + + /* Get exclusive access to the I2C bus */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + /* Configure the I2C frequency. REVISIT: Note that the frequency is set + * only on the first message. This could be extended to support + * different transfer frequencies for each message segment. + */ + + mx8mp_i2c_set_frequency(priv, msgs->frequency); + + /* Process every message */ + + if ((I2C_M_NOSTART & msgs->flags) == 0) + { + ret = mx8mp_i2c_send_start(priv); + if (ret < 0) + { + goto error; + } + } + + for (i = 0; i < count; ++i) + { + msg = msgs + i; + last = (i == (count - 1)); + + if ((I2C_M_NOSTART & msg->flags) == 0) + { + if (I2C_M_READ & msg->flags) + { + ret = mx8mp_i2c_write_byte(priv, I2C_READADDR8(msg->addr)); + } + else + { + ret = mx8mp_i2c_write_byte(priv, I2C_WRITEADDR8(msg->addr)); + } + } + + if (ret == 0) + { + if (I2C_M_READ & msg->flags) + { + ret = mx8mp_i2c_read_bytes(priv, + msg->buffer, msg->length, last); + } + else + { + ret = mx8mp_i2c_write_bytes(priv, + msg->buffer, msg->length, last); + } + } + + if (ret == -EACCES) + { + /* arbitration lost */ + + goto error; + } + else if (ret < 0) + { + mx8mp_i2c_send_stop(priv); + goto error; + } + + if (!last && ((I2C_M_NOSTART & msg->flags) == 0)) + { + ret = mx8mp_i2c_send_repeat_start(priv); + if (ret < 0) + { + goto error; + } + } + } + +error: + + /* Ensure that the bus is free before leaving */ + + deadline = clock_systime_ticks() + I2C_STALL_TIMEOUT; + while (is_busy(priv)) + { + if (clock_systime_ticks() > deadline) + { + i2cwarn("Bus is stall: try to reset it\n"); + mx8mp_i2c_reset_bus(priv); + break; + } + + nxsig_usleep(10); + } + + /* Release access to I2C bus */ + + nxmutex_unlock(&priv->lock); + return ret; +} + +static int mx8mp_i2c_reset(struct i2c_master_s *dev) +{ + struct mx8mp_i2c_s *priv = (struct mx8mp_i2c_s *)dev; + int ret; + + /* Our caller must own a ref */ + + DEBUGASSERT(priv->refs > 0); + + /* Lock out other clients */ + + ret = nxmutex_lock(&priv->lock); + if (ret < 0) + { + return ret; + } + + mx8mp_i2c_reset_bus(priv); + + /* Release the port for re-use by other clients */ + + nxmutex_unlock(&priv->lock); + + return 0; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mx8mp_i2cbus_initialize + * + * Description: + * Initialise an I2C device + * + ****************************************************************************/ + +struct i2c_master_s *mx8mp_i2cbus_initialize(int port) +{ + struct mx8mp_i2c_s *priv; + + i2cinfo("port=%d\n", port); + + switch (port) + { +#ifdef CONFIG_MX8MP_I2C1 + case 1: + priv = &g_i2c1_dev; + break; +#endif + +#ifdef CONFIG_MX8MP_I2C2 + case 2: + priv = &g_i2c2_dev; + break; +#endif + +#ifdef CONFIG_MX8MP_I2C3 + case 3: + priv = &g_i2c3_dev; + break; +#endif + +#ifdef CONFIG_MX8MP_I2C4 + case 4: + priv = &g_i2c4_dev; + break; +#endif + +#ifdef CONFIG_MX8MP_I2C5 + case 5: + priv = &g_i2c5_dev; + break; +#endif + +#ifdef CONFIG_MX8MP_I2C6 + case 6: + priv = &g_i2c6_dev; + break; +#endif + + default: + i2cerr("ERROR: Unsupported I2C port %d\n", port); + return NULL; + } + + nxmutex_lock(&priv->lock); + if (priv->refs++ == 0) + { + mx8mp_i2c_init(priv); + } + + nxmutex_unlock(&priv->lock); + return &priv->dev; +} + +/**************************************************************************** + * Name: mx8mp_i2cbus_uninitialize + * + * Description: + * Uninitialise an I2C device + * + ****************************************************************************/ + +int mx8mp_i2cbus_uninitialize(struct i2c_master_s *dev) +{ + struct mx8mp_i2c_s *priv = (struct mx8mp_i2c_s *)dev; + + DEBUGASSERT(priv != NULL); + + /* Decrement reference count and check for underflow */ + + if (priv->refs == 0) + { + return -1; + } + + nxmutex_lock(&priv->lock); + if (--priv->refs) + { + nxmutex_unlock(&priv->lock); + return 0; + } + + /* Disable power and other HW resource (GPIO's) */ + + mx8mp_i2c_deinit(priv); + + nxmutex_unlock(&priv->lock); + return 0; +} diff --git a/arch/arm/src/mx8mp/mx8mp_i2c.h b/arch/arm/src/mx8mp/mx8mp_i2c.h new file mode 100644 index 0000000000..1409c01b4f --- /dev/null +++ b/arch/arm/src/mx8mp/mx8mp_i2c.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * arch/arm/src/mx8mp/mx8mp_i2c.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_MX8MP_MX8MP_I2C_H +#define __ARCH_ARM_SRC_MX8MP_MX8MP_I2C_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include "hardware/mx8mp_i2c.h" + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: mx8mp_i2cbus_initialize + * + * Description: + * Initialize the selected I2C port. And return a unique instance of struct + * struct i2c_master_s. This function may be called to obtain multiple + * instances of the interface, each of which may be set up with a + * different frequency and slave address. + * + * Input Parameters: + * Port number (for hardware that has multiple I2C interfaces) + * + * Returned Value: + * Valid I2C device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct i2c_master_s *mx8mp_i2cbus_initialize(int port); + +/**************************************************************************** + * Name: mx8mp_i2cbus_uninitialize + * + * Description: + * De-initialize the selected I2C port, and power down the device. + * + * Input Parameters: + * Device structure as returned by the lpc43_i2cbus_initialize() + * + * Returned Value: + * OK on success, ERROR when internal reference count mismatch or dev + * points to invalid hardware device. + * + ****************************************************************************/ + +int mx8mp_i2cbus_uninitialize(struct i2c_master_s *dev); + +#endif /* __ARCH_ARM_SRC_MX8MP_MX8MP_I2C_H */ diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/Makefile b/boards/arm/mx8mp/verdin-mx8mp/src/Makefile index 9d4ba8179a..d36ecb77d1 100644 --- a/boards/arm/mx8mp/verdin-mx8mp/src/Makefile +++ b/boards/arm/mx8mp/verdin-mx8mp/src/Makefile @@ -23,7 +23,16 @@ include $(TOPDIR)/Make.defs CSRCS = mx8mp_boot.c mx8mp_bringup.c ifeq ($(CONFIG_BOARDCTL),y) -CSRCS += mx8mp_appinit.c + CSRCS += mx8mp_appinit.c endif +ifeq ($(CONFIG_SENSORS_INA219),y) + CSRCS += mx8mp_ina219.c +endif + +ifeq ($(CONFIG_MX8MP_I2C_DRIVER),y) + CSRCS += mx8mp_i2cdev.c +endif + + include $(TOPDIR)/boards/Board.mk diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_bringup.c b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_bringup.c index 2d42980dbe..b2e8c135df 100644 --- a/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_bringup.c +++ b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_bringup.c @@ -28,10 +28,16 @@ #include #include +#include "verdin-mx8mp.h" + #ifdef CONFIG_USERLED # include #endif +#ifdef CONFIG_SENSORS_INA219 +# include "mx8mp_ina219.h" +#endif + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -58,5 +64,35 @@ int mx8mp_bringup(void) } #endif +#ifdef CONFIG_FS_PROCFS + /* Mount the procfs file system */ + + ret = nx_mount(NULL, "/proc", "procfs", 0, NULL); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Failed to mount procfs at /proc: %d\n", ret); + } +#endif + +#ifdef CONFIG_MX8MP_I2C_DRIVER + /* Initialize I2C buses */ + + ret = mx8mp_i2cdev_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: mx8mp_i2cdev_initialize() failed: %d\n", ret); + } +#endif + +#ifdef CONFIG_SENSORS_INA219 + /* Configure and initialize the INA219 sensor in I2C4 */ + + ret = board_ina219_initialize(4); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: mx8mp_ina219_initialize() failed: %d\n", ret); + } +#endif + return ret; } diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_i2cdev.c b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_i2cdev.c new file mode 100644 index 0000000000..a22440b6d7 --- /dev/null +++ b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_i2cdev.c @@ -0,0 +1,147 @@ +/**************************************************************************** + * boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_i2cdev.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 + +#include +#include +#include + +#include + +#include "arm_internal.h" +#include "chip.h" +#include "mx8mp_i2c.h" + +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_i2cdev_initialize + * + * Description: + * Initialize and register i2c driver for the specified i2c port + * + ****************************************************************************/ + +int board_i2cdev_initialize(int port) +{ + int ret; + struct i2c_master_s *i2c; + + i2cinfo("Initializing /dev/i2c%d..\n", port); + + /* Initialize i2c device */ + + i2c = mx8mp_i2cbus_initialize(port); + if (!i2c) + { + i2cerr("ERROR: Failed to initialize i2c%d.\n", port); + return -ENODEV; + } + + ret = i2c_register(i2c, port); + if (ret < 0) + { + i2cerr("ERROR: Failed to register i2c%d: %d\n", port, ret); + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mx8mp_i2cdev_initialize + * + * Description: + * Called to configure all i2c drivers + * + ****************************************************************************/ + +int mx8mp_i2cdev_initialize(void) +{ + int ret = 0; + +#ifdef CONFIG_MX8MP_I2C1 + ret = board_i2cdev_initialize(1); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C1.\n"); + return ret; + } +#endif + +#ifdef CONFIG_MX8MP_I2C2 + ret = board_i2cdev_initialize(2); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C2.\n"); + return ret; + } +#endif + +#ifdef CONFIG_MX8MP_I2C3 + ret = board_i2cdev_initialize(3); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C3.\n"); + return ret; + } +#endif + +#ifdef CONFIG_MX8MP_I2C4 + ret = board_i2cdev_initialize(4); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C4.\n"); + return ret; + } +#endif + +#ifdef CONFIG_MX8MP_I2C5 + ret = board_i2cdev_initialize(5); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C5.\n"); + return ret; + } +#endif + +#ifdef CONFIG_MX8MP_I2C6 + ret = board_i2cdev_initialize(6); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C6.\n"); + return ret; + } +#endif + + return ret; +} diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_ina219.c b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_ina219.c new file mode 100644 index 0000000000..a8952976c5 --- /dev/null +++ b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_ina219.c @@ -0,0 +1,85 @@ +/**************************************************************************** + * boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_ina219.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 + +#include +#include +#include +#include + +#include +#include + +#include "mx8mp_i2c.h" +#include "mx8mp_ina219.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_ina219_initialize + * + * Description: + * Initialize and register the INA219 voltage/current sensor. + * + * Input parameters: + * devno - The device number, used to build the device path as /dev/inaN + * busno - The I2C bus number + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int board_ina219_initialize(int busno) +{ + struct i2c_master_s *i2c; + int ret; + + sninfo("Initializing INA219!\n"); + + /* Initialize I2C */ + + i2c = mx8mp_i2cbus_initialize(busno); + if (!i2c) + { + return -ENODEV; + } + + /* Then register the sensor */ + + ret = ina219_register("/dev/ina219", i2c, 0x40, 10000, 0x00); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: Error registering INA219\n"); + } + + return ret; +} diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_ina219.h b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_ina219.h new file mode 100644 index 0000000000..ec1b0ad739 --- /dev/null +++ b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_ina219.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_ina219.h + * + * 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. + * + ****************************************************************************/ + +#ifndef __BOARDS_ARM_MX8MP_MX8MP_VERDIN_INCLUDE_BOARD_H +#define __BOARDS_ARM_MX8MP_MX8MP_VERDIN_INCLUDE_BOARD_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: board_ina219_initialize + * + * Description: + * Initialize and register the INA219 voltage/current sensor. + * + * Input parameters: + * busno - The I2C bus number + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +int board_ina219_initialize(int busno); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* __BOARDS_ARM_MX8MP_MX8MP_VERDIN_INCLUDE_BOARD_H */ diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/verdin-mx8mp.h b/boards/arm/mx8mp/verdin-mx8mp/src/verdin-mx8mp.h index cb6308f164..2f891ddf8b 100644 --- a/boards/arm/mx8mp/verdin-mx8mp/src/verdin-mx8mp.h +++ b/boards/arm/mx8mp/verdin-mx8mp/src/verdin-mx8mp.h @@ -46,7 +46,7 @@ ****************************************************************************/ /**************************************************************************** - * Name: xmc4_bringup + * Name: mx8mp_bringup * * Description: * Bring up board features @@ -55,5 +55,15 @@ int mx8mp_bringup(void); +/**************************************************************************** + * Name: mx8mp_i2cdev_initialize + * + * Description: + * Called to configure all i2c + * + ****************************************************************************/ + +int mx8mp_i2cdev_initialize(void); + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_ARM_MX8MP_MX8MP_VERDIN_SRC_VERDIN_MX8MP_H */