diff --git a/arch/arm/src/mx8mp/Kconfig b/arch/arm/src/mx8mp/Kconfig index d8b01fa673fd5..d03d100e00429 100644 --- a/arch/arm/src/mx8mp/Kconfig +++ b/arch/arm/src/mx8mp/Kconfig @@ -84,6 +84,45 @@ if MX8MP_I2C in any real driver application. endif # MX8MP_I2C + +##################################################################### +# SPI Configuration (Master) +##################################################################### + +config MX8MP_SPI + bool "SPI Master" + select SPI + ---help--- + Build in support for SPI master mode. + +if MX8MP_SPI + + config MX8MP_SPI1 + bool "SPI1" + default n + select SPI + + config MX8MP_SPI2 + bool "SPI2" + default n + select SPI + + config MX8MP_SPI3 + bool "SPI3" + default n + select SPI + + config MX8MP_SPI_DRIVER + bool "SPI character driver" + default n + select SPI_DRIVER + ---help--- + Build in support for a character driver at /dev/spi[N] that may b + used to perform SPI bus transfers from applications. The intent of + this driver is to support SPI testing. + +endif # MX8MP_SPI + 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 2aa4a10bc016b..dea1edf49d82b 100644 --- a/arch/arm/src/mx8mp/Make.defs +++ b/arch/arm/src/mx8mp/Make.defs @@ -39,3 +39,7 @@ endif ifeq ($(CONFIG_I2C),y) CHIP_CSRCS += mx8mp_i2c.c endif + +ifeq ($(CONFIG_SPI),y) +CHIP_CSRCS += mx8mp_ecspi.c +endif diff --git a/arch/arm/src/mx8mp/hardware/mx8mp_ecspi.h b/arch/arm/src/mx8mp/hardware/mx8mp_ecspi.h new file mode 100644 index 0000000000000..14164cc17fb3c --- /dev/null +++ b/arch/arm/src/mx8mp/hardware/mx8mp_ecspi.h @@ -0,0 +1,102 @@ +/**************************************************************************** + * arch/arm/src/mx8mp/hardware/mx8mp_ecspi.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_ECSPI_H +#define __ARCH_ARM_SRC_MX8MP_HARDWARE_MX8MP_ECSPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* ECSPI Register Offsets ***************************************************/ + +#define RXDATA_OFFSET 0x0000 +#define TXDATA_OFFSET 0x0004 +#define CONREG_OFFSET 0x0008 +#define CONFIGREG_OFFSET 0x000c +#define INTREG_OFFSET 0x0010 +#define DMAREG_OFFSET 0x0014 +#define STATREG_OFFSET 0x0018 +#define PERIODREG_OFFSET 0x001C +#define TESTREG_OFFSET 0x0020 +#define MSGDATA_OFFSET 0x0040 + +/* ECSPI Register Bit Definitions *******************************************/ + +#define CONREG_BURST_LENGTH (20) +#define CONREG_CHANNEL_SELECT (18) +#define CONREG_PRE_DIVIDER (12) +#define CONREG_POST_DIVIDER (8) +#define CONREG_CHANNEL_MODE (4) +#define CONREG_SMC (1 << 3) +#define CONREG_XCH (1 << 2) +#define CONREG_HT (1 << 1) +#define CONREG_EN (1 << 0) + +#define CONFIGREG_HT_LENGTH (24) +#define CONFIGREG_SLCK_CTL (20) +#define CONFIGREG_DATA_CTL (16) +#define CONFIGREG_SS_POL (12) +#define CONFIGREG_SS_CTL (8) +#define CONFIGREG_SCLK_POL (4) +#define CONFIGREG_SCLK_PHA (0) + +#define INTREG_TCEN (1 << 7) +#define INTREG_ROEN (1 << 6) +#define INTREG_RFEN (1 << 5) +#define INTREG_RDREN (1 << 4) +#define INTREG_RREN (1 << 3) +#define INTREG_TFEN (1 << 2) +#define INTREG_TDREN (1 << 1) +#define INTREG_TEEN (1 << 0) + +#define DMAREG_RXTDEN (1 << 31) +#define DMAREG_RX_DMA_LENGTH (24) +#define DMAREG_RXDEN (1 << 23) +#define DMAREG_RX_THRESHOLD (16) +#define DMAREG_TEDEN (1 << 7) +#define DMAREG_TX_THRESHOLD (0) + +#define STATREG_TC (1 << 7) +#define STATREG_RO (1 << 6) +#define STATREG_RF (1 << 5) +#define STATREG_RDR (1 << 4) +#define STATREG_RR (1 << 3) +#define STATREG_TF (1 << 2) +#define STATREG_TDR (1 << 1) +#define STATREG_TE (1 << 0) + +#define PERIODREG_CSD_CTL (16) +#define PERIODREG_CSRC (1 << 15) +#define PERIODREG_SAMPLE_PERIOD (0) + +#define TESTREG_LBC (1 << 31) +#define TESTREG_RXCNT (8) +#define TESTREG_TXCNT (0) + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#endif /* __ARCH_ARM_SRC_MX8MP_HARDWARE_MX8MP_ECSPI_H */ diff --git a/arch/arm/src/mx8mp/hardware/mx8mp_pinmux.h b/arch/arm/src/mx8mp/hardware/mx8mp_pinmux.h index 1f23582d3b905..cc0005889a007 100644 --- a/arch/arm/src/mx8mp/hardware/mx8mp_pinmux.h +++ b/arch/arm/src/mx8mp/hardware/mx8mp_pinmux.h @@ -924,5 +924,6 @@ #define GPIO_PAD_CTRL (PAD_CTL_HYS | PAD_CTL_PUE | PAD_CTL_PE | PAD_CTL_DSE2) #define UART_PAD_CTRL (PAD_CTL_PUE | PAD_CTL_PE) +#define SPI_PAD_CTRL (PAD_CTL_PUE) #endif /* __ARCH_ARM_SRC_MX8MP_HARDWARE_MX8MP_PINMUX_H */ diff --git a/arch/arm/src/mx8mp/mx8mp_ecspi.c b/arch/arm/src/mx8mp/mx8mp_ecspi.c new file mode 100644 index 0000000000000..83dd60e341424 --- /dev/null +++ b/arch/arm/src/mx8mp/mx8mp_ecspi.c @@ -0,0 +1,747 @@ +/****************************************************************************** + * arch/arm/src/mx8mp/mx8mp_ecspi.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_ecspi.h" +#include "mx8mp_ccm.h" +#include "hardware/mx8mp_memorymap.h" +#include "hardware/mx8mp_ecspi.h" + +#include +#include "arm_internal.h" + +/****************************************************************************** + * Pre-processor Definitions + ******************************************************************************/ + +/****************************************************************************** + * Private Function Prototypes + ******************************************************************************/ + +struct mx8mp_spi_s; + +static void mx8mp_spi_enable(struct mx8mp_spi_s *dev); +static int mx8mp_spi_interrupt(int irq, void *context, void *arg); +static int mx8mp_spi_transfer(struct mx8mp_spi_s *priv, + const uint8_t *out, uint8_t *in, int size); + +/* SPI methods */ + +static int spi_lock(struct spi_dev_s *dev, bool lock); +static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency); +static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode); +static void spi_setbits(struct spi_dev_s *dev, int nbits); +#ifdef CONFIG_SPI_HWFEATURES +static int spi_hwfeatures(struct spi_dev_s *dev, spi_hwfeatures_t features); +#endif +static uint32_t spi_send(struct spi_dev_s *dev, uint32_t wd); +static void spi_exchange(struct spi_dev_s *dev, + const void *txbuffer, void *rxbuffer, size_t nwords); +#ifdef CONFIG_SPI_TRIGGER +static int spi_trigger(struct spi_dev_s *dev); +#endif +#ifndef CONFIG_SPI_EXCHANGE +static void spi_sndblock (struct spi_dev_s *dev, + const void *txbuffer, size_t nwords); +static void spi_recvblock(struct spi_dev_s *dev, + void *rxbuffer, size_t nwords); +#endif + +/* Initialization */ + +static void spi_bus_initialize(struct mx8mp_spi_s *priv); + +/****************************************************************************** + * Private Types + ******************************************************************************/ + +struct mx8mp_spi_s +{ + struct spi_dev_s dev; /* Externally visible part of the SPI interface */ + uint32_t base; /* SPI base address */ + int clock; /* Peripheral clock as described in hardware/mx8mp_ccm.h */ + uint16_t irq; /* IRQ number */ + uint32_t frequency; /* Current desired SCLK frequency */ + uint32_t actual; /* TODO: check use - Current actual SCLK frequency */ + uint8_t nbits; /* Width of word in bits (8 to 16) */ + uint8_t mode; /* Mode 0,1,2,3 */ + mutex_t lock; /* Only one thread can access at a time */ + sem_t wait; /* IRQ wait sync */ + uint32_t byte_duration; /* Byte duration - depend on clock */ +}; + +/* SPI device structures */ + +#ifdef CONFIG_MX8MP_SPI1 +static const struct spi_ops_s g_spi1_ops = +{ + .lock = spi_lock, + .select = mx8mp_spi1_select, /* Provided externally by board logic */ + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +#endif + .status = mx8mp_spi1_status, /* Provided externally by board logic */ +#ifdef CONFIG_SPI_CMDDATA + .cmddata = mx8mp_spi1_cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +}; + +static struct mx8mp_spi_s g_spi1_dev = +{ + .dev.ops = &g_spi1_ops, + .base = MX8M_ECSPI1, + .clock = ECSPI1_CLK_ROOT, + .irq = MX8MP_IRQ_ECSPI1, + .lock = NXMUTEX_INITIALIZER, + .wait = SEM_INITIALIZER(0), +}; +#endif + +#ifdef CONFIG_MX8MP_SPI2 +static const struct spi_ops_s g_spi2_ops = +{ + .lock = spi_lock, + .select = mx8mp_spi2_select, /* Provided externally by board logic */ + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +#endif + .status = mx8mp_spi2_status, /* Provided externally by board logic */ +#ifdef CONFIG_SPI_CMDDATA + .cmddata = mx8mp_spi2_cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +}; + +static struct mx8mp_spi_s g_spi1_dev = +{ + .dev.ops = &g_spi2_ops, + .base = MX8M_ECSPI2, + .clock = ECSPI2_CLK_ROOT, + .irq = MX8MP_IRQ_ECSPI2, + .lock = NXMUTEX_INITIALIZER, + .wait = SEM_INITIALIZER(0), +}; +#endif + +#ifdef CONFIG_MX8MP_SPI3 +static const struct spi_ops_s g_spi3_ops = +{ + .lock = spi_lock, + .select = mx8mp_spi3_select, /* Provided externally by board logic */ + .setfrequency = spi_setfrequency, + .setmode = spi_setmode, + .setbits = spi_setbits, +#ifdef CONFIG_SPI_HWFEATURES + .hwfeatures = 0, /* Not supported */ +#endif + .status = mx8mp_spi3_status, /* Provided externally by board logic */ +#ifdef CONFIG_SPI_CMDDATA + .cmddata = mx8mp_spi3_cmddata, +#endif + .send = spi_send, +#ifdef CONFIG_SPI_EXCHANGE + .exchange = spi_exchange, +#else + .sndblock = spi_sndblock, + .recvblock = spi_recvblock, +#endif +}; + +static struct mx8mp_spi_s g_spi3_dev = +{ + .dev.ops = &g_spi3_ops, + .base = MX8M_ECSPI3, + .clock = ECSPI3_CLK_ROOT, + .irq = MX8MP_IRQ_ECSPI3, + .lock = NXMUTEX_INITIALIZER, + .wait = SEM_INITIALIZER(0), +}; +#endif + +/****************************************************************************** + * Private Data + ******************************************************************************/ + +/****************************************************************************** + * Private Functions + ******************************************************************************/ + +static void mx8mp_spi_enable(struct mx8mp_spi_s *priv) +{ + /* enable and set all channels to master mode */ + + modreg32(CONREG_EN, CONREG_EN, priv->base + CONREG_OFFSET); + modreg32(0xf << CONREG_CHANNEL_MODE, + 0xf << CONREG_CHANNEL_MODE, priv->base + CONREG_OFFSET); + + /* select channel 0 (the only one available) */ + + modreg32(0, 0x3 << CONREG_CHANNEL_SELECT, priv->base + CONREG_OFFSET); +} + +/****************************************************************************** + * Name: mx8mp_spi_wait_irq + * + * Description: + * Wait for the IRQ to be triggered. + * + ******************************************************************************/ + +static inline int mx8mp_spi_wait_irq(struct mx8mp_spi_s *priv, int usec) +{ + return nxsem_tickwait(&priv->wait, USEC2TICK(usec)); +} + +/****************************************************************************** + * Name: mx8mp_spi_interrupt + * + * Description: + * The SPI common interrupt handler + * + ******************************************************************************/ + +static int mx8mp_spi_interrupt(int irq, void *context, void *arg) +{ + struct mx8mp_spi_s *priv = (struct mx8mp_spi_s *)arg; + nxsem_post(&priv->wait); + + return 0; +} + +/****************************************************************************** + * Name: mx8mp_spi_transfer + * + * Description: + * Do a transfer on the SPI bus. It may be needed to call it multiple times + * since SPI FIFO is limited to 256 bytes (64 x 32bits) + * + ******************************************************************************/ + +static int mx8mp_spi_transfer(struct mx8mp_spi_s *priv, + const uint8_t *out, uint8_t *in, int size) +{ + /* max 256 bytes per access (64 x 32 bits) */ + + if (size > 256) + { + size = 256; + } + + /* WARNING: SPI FIFO works on 32 bits data only */ + + /* 1. clear IRQs */ + + modreg32(0, STATREG_TC | STATREG_RO, priv->base + STATREG_OFFSET); + + /* 2. compute words to process */ + + uint16_t words = size / 4; + uint16_t remaining_bytes = size % 4; + + /* 3. Configure burst size (in bits) and RX IT threshold (in words) */ + + uint16_t const burst_length = ((words * 4 + remaining_bytes) * 8) & 0xfff; + modreg32((burst_length - 1) << CONREG_BURST_LENGTH, + 0xfff << CONREG_BURST_LENGTH, + priv->base + CONREG_OFFSET); + + uint16_t threshold = words; + if (remaining_bytes) + { + ++threshold; + } + + putreg32((threshold - 1) << DMAREG_RX_THRESHOLD, priv->base + DMAREG_OFFSET); + + /* 4. Load TX FIFO - remaining byte on the first word */ + + if (remaining_bytes) + { + uint32_t data = 0; + for (int32_t i = 0; i < remaining_bytes; ++i) + { + data = (data << 8) | out[i]; + } + + putreg32(data, priv->base + TXDATA_OFFSET); + } + + for (int32_t i = 0; i < words; ++i) + { + uint32_t data = 0; + memcpy(&data, out + i * 4 + remaining_bytes, 4); + data = __builtin_bswap32(data); + putreg32(data, priv->base + TXDATA_OFFSET); + } + + /* 5. start transfer and wait for it */ + + modreg32(CONREG_XCH, CONREG_XCH, priv->base + CONREG_OFFSET); + int ret = mx8mp_spi_wait_irq(priv, priv->byte_duration * (size + 1)); /* add one byte duration of leeway */ + if (ret < 0) + { + return ret; + } + + /* 6. Unload RX FIFO (even if an error occured to empty the queue) */ + + if (remaining_bytes) + { + uint32_t data = getreg32(priv->base + RXDATA_OFFSET); + data = __builtin_bswap32(data); + data >>= (4 - remaining_bytes) * 8; + memcpy(in, &data, remaining_bytes); + } + + for (int32_t i = 0; i < words; ++i) + { + uint32_t data = getreg32(priv->base + RXDATA_OFFSET); + data = __builtin_bswap32(data); + memcpy(in + i * 4 + remaining_bytes, &data, 4); + } + + return size; +} + +/****************************************************************************** + * Name: spi_lock + * + * Description: + * On SPI buses where there are multiple devices, it will be necessary to + * lock SPI to have exclusive access to the buses for a sequence of + * transfers. The bus should be locked before the chip is selected. After + * locking the SPI bus, the caller should then also call the setfrequency, + * setbits, and setmode methods to make sure that the SPI is properly + * configured for the device. If the SPI bus is being shared, then it + * may have been left in an incompatible state. + * + * Input Parameters: + * dev - Device-specific state data + * lock - true: Lock spi bus, false: unlock SPI bus + * + * Returned Value: + * None + * + ******************************************************************************/ + +static int spi_lock(struct spi_dev_s *dev, bool lock) +{ + struct mx8mp_spi_s *priv = (struct mx8mp_spi_s *)dev; + int ret; + + if (lock) + { + ret = nxmutex_lock(&priv->lock); + } + else + { + ret = nxmutex_unlock(&priv->lock); + } + + return ret; +} + +/****************************************************************************** + * Name: spi_setfrequency + * + * Description: + * Set the SPI frequency. + * + * Input Parameters: + * dev - Device-specific state data + * frequency - The SPI frequency requested + * + * Returned Value: + * Returns the actual frequency selected + * + ******************************************************************************/ + +static uint32_t spi_setfrequency(struct spi_dev_s *dev, uint32_t frequency) +{ + struct mx8mp_spi_s *priv = (struct mx8mp_spi_s *)dev; + + if (frequency == priv->frequency) + { + return priv->actual; + } + + uint32_t clock = mx8mp_ccm_get_clock(priv->clock); + DEBUGASSERT(clock > frequency); + + uint8_t pre_div = 0; + uint8_t post_div = 0; + uint32_t old_frequency = 0; + + for (uint8_t i = 0; i < 16; ++i) + { + for (uint8_t j = 0; j < 16; ++j) + { + uint32_t current_frequency = clock / ((1 << i) * (j + 1)); + if ((current_frequency <= frequency) && + (current_frequency > old_frequency)) + { + pre_div = j; + post_div = i; + old_frequency = current_frequency; + } + } + } + + priv->actual = clock / ((1 << post_div) * (pre_div + 1)); + + spiinfo("Source clock: %lu", clock); + spiinfo("Requested freq: %lu", frequency); + spiinfo("pre_divider: %u", pre_div); + spiinfo("post_divider: %u", post_div); + spiinfo("Actual freq: %lu", priv->actual); + + modreg32(pre_div << CONREG_PRE_DIVIDER, 0xf << CONREG_PRE_DIVIDER, + priv->base + CONREG_OFFSET); + modreg32(post_div << CONREG_POST_DIVIDER, 0xf << CONREG_POST_DIVIDER, + priv->base + CONREG_OFFSET); + + /* Store byte duration to adapt irq waiting time */ + + priv->byte_duration = (USEC_PER_SEC / priv->actual) * 8; + + return priv->actual; +} + +/****************************************************************************** + * Name: spi_setmode + * + * Description: + * Set the SPI mode. see enum spi_mode_e for mode definitions + * + * Input Parameters: + * dev - Device-specific state data + * mode - The SPI mode requested + * + * Returned Value: + * Returns the actual frequency selected + * + ******************************************************************************/ + +static void spi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) +{ + struct mx8mp_spi_s *priv = (struct mx8mp_spi_s *)dev; + uint8_t CPOL; + uint8_t CPHA; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode == priv->mode) + { + return; + } + + switch (mode) + { + case SPIDEV_MODE0: + { + CPOL = 0; + CPHA = 0; + } + break; + + case SPIDEV_MODE1: + { + CPOL = 0; + CPHA = 1; + } + break; + + case SPIDEV_MODE2: + { + CPOL = 1; + CPHA = 0; + } + break; + + case SPIDEV_MODE3: + { + CPOL = 1; + CPHA = 1; + } + break; + + default: + return; + } + + uint32_t configreg = 0; + configreg |= (0xf << CONFIGREG_SLCK_CTL); + configreg |= (0xf << CONFIGREG_DATA_CTL); + configreg |= (0x0 << CONFIGREG_SS_POL); + configreg |= (0x0 << CONFIGREG_SS_CTL); + configreg |= (CPOL << CONFIGREG_SCLK_POL); + configreg |= (CPHA << CONFIGREG_SCLK_PHA); + putreg32(configreg, priv->base + CONFIGREG_OFFSET); + + /* Save the mode so that subsequent re-configurations will be + * faster + */ + + priv->mode = mode; +} + +/****************************************************************************** + * Name: spi_setbits + * + * Description: + * Set the number of bits per word. + * + * Input Parameters: + * dev - Device-specific state data + * nbits - The number of bits requested + * + * Returned Value: + * None + * + ******************************************************************************/ + +static void spi_setbits(struct spi_dev_s *dev, int nbits) +{ + struct mx8mp_spi_s *priv = (struct mx8mp_spi_s *)dev; + + spiinfo("nbits=%d\n", nbits); + + /* Has the number of bits changed? */ + + if (nbits == priv->nbits) + { + return; + } + + modreg32(nbits << CONREG_BURST_LENGTH, + 0xfff << CONREG_BURST_LENGTH, + priv->base + CONREG_OFFSET); + + /* Save the selection so that subsequent re-configurations will be faster. */ + + priv->nbits = nbits; +} + +/****************************************************************************** + * Name: spi_send + * + * Description: + * Exchange one word on SPI + * + * Input Parameters: + * dev - Device-specific state data + * wd - The word to send. the size of the data is determined by the + * number of bits selected for the SPI interface. + * + * Returned Value: + * response + * + ******************************************************************************/ + +static uint32_t spi_send(struct spi_dev_s *dev, uint32_t wd) +{ + struct mx8mp_spi_s *priv = (struct mx8mp_spi_s *)dev; + uint32_t ret; + + DEBUGASSERT(priv && priv->base); + + uint32_t dummy; + ret = mx8mp_spi_transfer(priv, (const uint8_t *)&wd, + (uint8_t *)&dummy, + priv->nbits % 8); + + spiinfo("Sent: %04" PRIx32 " Return: %04" PRIx32 + " Status: %02" PRIx32 "\n", wd, ret, dummy); + + return ret; +} + +/****************************************************************************** + * Name: spi_exchange (no DMA) + * + * Description: + * Exchange a block of data on SPI without using DMA + * + * Input Parameters: + * dev - Device-specific state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to a buffer in which to receive data + * nwords - the length of data to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None + * + ******************************************************************************/ + +static void spi_exchange(struct spi_dev_s *dev, const void *txbuffer, + void *rxbuffer, size_t nwords) +{ + struct mx8mp_spi_s *priv = (struct mx8mp_spi_s *)dev; + + int size = nwords; + if (priv->nbits > 8) + { + size = size * 2; + } + + int transfered = 0; + while (transfered < size) + { + int ret = mx8mp_spi_transfer(priv, + (const uint8_t *)txbuffer + transfered, + (uint8_t *)rxbuffer + transfered, + size - transfered); + if (ret < 0) + { + break; + } + + transfered += ret; + } +} + +/****************************************************************************** + * Name: spi_bus_initialize + * + * Description: + * Initialize the selected SPI bus in its default state (Master, 8-bit, + * mode 0, etc.) + * + * Input Parameters: + * priv - private SPI device structure + * + * Returned Value: + * None + * + ******************************************************************************/ + +static void spi_bus_initialize(struct mx8mp_spi_s *priv) +{ + priv->frequency = 0; + priv->nbits = 0; + priv->mode = SPIDEV_MODE1; /* Ensure that mode0 is applied */ + + /* Enable SPI */ + + mx8mp_spi_enable(priv); + + /* Select a default frequency of approx. 400KHz */ + + spi_setfrequency((struct spi_dev_s *)priv, 400000); + + /* Apply a default configuration */ + + spi_setbits((struct spi_dev_s *)priv, 8); + spi_setmode((struct spi_dev_s *)priv, SPIDEV_MODE0); + + /* Attach Interrupt Handler */ + + irq_attach(priv->irq, mx8mp_spi_interrupt, priv); + + /* Enable Interrupt Handler */ + + up_enable_irq(priv->irq); +} + +/****************************************************************************** + * Public Functions + ******************************************************************************/ + +/****************************************************************************** + * Name: mx8mp_spibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * Port number (for hardware that has multiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ******************************************************************************/ + +struct spi_dev_s *mx8mp_spibus_initialize(int bus) +{ + struct mx8mp_spi_s *priv = NULL; + + irqstate_t flags = enter_critical_section(); + + switch (bus) + { +#ifdef CONFIG_MX8MP_SPI1 + case 1: + priv = &g_spi1_dev; + spi_bus_initialize(priv); + break; +#endif + +#ifdef CONFIG_MX8MP_SPI2 + case 2: + priv = &g_spi2_dev; + spi_bus_initialize(priv); + break; +#endif + +#ifdef CONFIG_MX8MP_SPI3 + case 3: + priv = &g_spi3_dev; + spi_bus_initialize(priv); + break; +#endif + + default: + spierr("ERROR: Unsupported SPI bus: %d\n", bus); + } + + leave_critical_section(flags); + return (struct spi_dev_s *)priv; +} diff --git a/arch/arm/src/mx8mp/mx8mp_ecspi.h b/arch/arm/src/mx8mp/mx8mp_ecspi.h new file mode 100644 index 0000000000000..ad119fe77933d --- /dev/null +++ b/arch/arm/src/mx8mp/mx8mp_ecspi.h @@ -0,0 +1,114 @@ +/**************************************************************************** + * arch/arm/src/mx8mp/mx8mp_ecspi.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_ECSPI_H +#define __ARCH_ARM_SRC_MX8MP_MX8MP_ECSPI_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +struct spi_dev_s; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: mx8mp_spibus_initialize + * + * Description: + * Initialize the selected SPI bus + * + * Input Parameters: + * bus number (for hardware that has multiple SPI interfaces) + * + * Returned Value: + * Valid SPI device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct spi_dev_s *mx8mp_spibus_initialize(int bus); + +/**************************************************************************** + * Name: + * mx8mp_spi[n]select, mx8mp_spi[n]status, and mx8mp_spi[n]cmddata + * + * Description: + * These external functions must be provided by board-specific logic. + * They are implementations of the select, status, and cmddata methods of + * the SPI interface defined by struct spi_ops_s + * (see include/nuttx/spi/spi.h). All other methods including + * mx8mp_spibus_initialize()) are provided by common Kinetis logic. + * To use this common SPI logic on your board: + * + * 1. Provide logic in mx8mp_boardinitialize() to configure SPI chip + * select pins. + * 2. Provide mx8mp_spi[n]select() and mx8mp_spi[n]status() functions + * in your board-specific logic. These functions will perform chip + * selection and status operations using GPIOs in the way your board is + * configured. + * 2. If CONFIG_SPI_CMDDATA is defined in the NuttX configuration, provide + * mx8mp_spi[n]cmddata() functions in your board-specific logic. + * These functions will perform cmd/data selection operations using + * GPIOs in the way your board is configured. + * 3. Add a call to mx8mp_spibus_initialize() in your low level + * application initialization logic + * 4. The handle returned by mx8mp_spibus_initialize() may then be used + * to bind the SPI driver to higher level logic (e.g., calling + * mmcsd_spislotinitialize(), for example, will bind the SPI driver to + * the SPI MMC/SD driver). + * + ****************************************************************************/ + +#ifdef CONFIG_MX8MP_SPI1 +void mx8mp_spi1_select(struct spi_dev_s *dev, + uint32_t devid, bool selected); +uint8_t mx8mp_spi1_status(struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +int mx8mp_spi1_cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif +#endif + +#ifdef CONFIG_MX8MP_SPI2 +void mx8mp_spi2_select(struct spi_dev_s *dev, + uint32_t devid, bool selected); +uint8_t mx8mp_spi2_status(struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +int mx8mp_spi2_cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif +#endif + +#ifdef CONFIG_MX8MP_SPI3 +void mx8mp_spi3_select(struct spi_dev_s *dev, + uint32_t devid, bool selected); +uint8_t mx8mp_spi3_status(struct spi_dev_s *dev, uint32_t devid); +#ifdef CONFIG_SPI_CMDDATA +int mx8mp_spi3_cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd); +#endif +#endif + +#endif /* __ARCH_ARM_SRC_MX8MP_MX8MP_ECSPI_H */ diff --git a/arch/arm/src/mx8mp/mx8mp_i2c.c b/arch/arm/src/mx8mp/mx8mp_i2c.c index 47c7b008d70d7..e134f22191407 100644 --- a/arch/arm/src/mx8mp/mx8mp_i2c.c +++ b/arch/arm/src/mx8mp/mx8mp_i2c.c @@ -22,12 +22,13 @@ * Included Files ****************************************************************************/ -#include #include +#include #include "mx8mp_i2c.h" #include "mx8mp_ccm.h" #include "hardware/mx8mp_memorymap.h" +#include "hardware/mx8mp_i2c.h" #include #include "arm_internal.h" diff --git a/arch/arm/src/mx8mp/mx8mp_i2c.h b/arch/arm/src/mx8mp/mx8mp_i2c.h index 1409c01b4f419..5bd9516b4b2b2 100644 --- a/arch/arm/src/mx8mp/mx8mp_i2c.h +++ b/arch/arm/src/mx8mp/mx8mp_i2c.h @@ -26,8 +26,12 @@ ****************************************************************************/ #include -#include -#include "hardware/mx8mp_i2c.h" + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +struct i2c_master_s; /**************************************************************************** * Public Function Prototypes diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/Makefile b/boards/arm/mx8mp/verdin-mx8mp/src/Makefile index 205bb0cfdba38..6e461c22c7675 100644 --- a/boards/arm/mx8mp/verdin-mx8mp/src/Makefile +++ b/boards/arm/mx8mp/verdin-mx8mp/src/Makefile @@ -34,6 +34,14 @@ ifeq ($(CONFIG_MX8MP_I2C_DRIVER),y) CSRCS += mx8mp_i2cdev.c endif +ifeq ($(CONFIG_SPI),y) +CSRCS += mx8mp_spi.c +endif + +ifeq ($(CONFIG_MX8MP_SPI_DRIVER),y) + CSRCS += mx8mp_spidev.c +endif + ifeq ($(CONFIG_ARCH_LEDS),y) CSRCS += mx8mp_autoleds.c else diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_bringup.c b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_bringup.c index 87e0124416295..205d6a8625b1b 100644 --- a/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_bringup.c +++ b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_bringup.c @@ -115,5 +115,15 @@ int mx8mp_bringup(void) } #endif +#ifdef CONFIG_MX8MP_SPI_DRIVER + /* Initialize SPI buses */ + + ret = mx8mp_spidev_initialize(); + if (ret < 0) + { + syslog(LOG_ERR, "ERROR: spidev_initialize() failed: %d\n", ret); + } +#endif + return ret; } diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_spi.c b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_spi.c new file mode 100644 index 0000000000000..4459f1d890c2c --- /dev/null +++ b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_spi.c @@ -0,0 +1,122 @@ +/**************************************************************************** + * boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_spi.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 "arm_internal.h" +#include "chip.h" +#include "mx8mp_gpio.h" + +#include + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mx8mp_spi1/2/3select and mx8mp_spi1/2/3status + * + * Description: + * The external functions, mx8mp_spi1/2/3select and mx8mp_spi1/2/3status + * must be provided by board-specific logic. + * They are implementations of the select and status methods of the SPI + * interface defined by struct spi_ops_s (see include/nuttx/spi/spi.h). + * + ****************************************************************************/ + +#ifdef CONFIG_MX8MP_SPI1 +void mx8mp_spi1_select(struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, + selected ? "assert" : "de-assert"); + + /* nothing to do : CS handled by module */ +} + +uint8_t mx8mp_spi1_status(struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} + +#ifdef CONFIG_SPI_CMDDATA +int mx8mp_spi1_cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif +#endif + +#ifdef CONFIG_MX8MP_SPI2 +void mx8mp_spi2_select(struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, + selected ? "assert" : "de-assert"); + + mx8mp_gpio_write(GPIO_SPI2_CS, !selected); +} + +uint8_t mx8mp_spi2_status(struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} + +#ifdef CONFIG_SPI_CMDDATA +int mx8mp_spi2_cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif +#endif + +#ifdef CONFIG_MX8MP_SPI3 +void mx8mp_spi3_select(struct spi_dev_s *dev, uint32_t devid, + bool selected) +{ + spiinfo("devid: %d CS: %s\n", (int)devid, + selected ? "assert" : "de-assert"); + + mx8mp_gpio_write(GPIO_SPI3_CS, !selected); +} + +uint8_t mx8mp_spi3_status(struct spi_dev_s *dev, uint32_t devid) +{ + return 0; +} + +#ifdef CONFIG_SPI_CMDDATA +int mx8mp_spi3_cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd) +{ + return -ENODEV; +} +#endif +#endif diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_spidev.c b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_spidev.c new file mode 100644 index 0000000000000..2181807408128 --- /dev/null +++ b/boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_spidev.c @@ -0,0 +1,138 @@ +/**************************************************************************** + * boards/arm/mx8mp/verdin-mx8mp/src/mx8mp_spidev.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_ecspi.h" +#include "verdin-mx8mp.h" + +#include + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: board_spidev_initialize + * + * Description: + * Initialize and register spi driver for the specified spi port + * + ****************************************************************************/ + +int board_spidev_initialize(int port) +{ + int ret; + struct spi_dev_s *spi; + + spiinfo("Initializing /dev/spi%d..\n", port); + + /* Initialize spi device */ + + spi = mx8mp_spibus_initialize(port); + if (!spi) + { + i2cerr("ERROR: Failed to initialize spi%d.\n", port); + return -ENODEV; + } + + ret = spi_register(spi, port); + if (ret < 0) + { + i2cerr("ERROR: Failed to register spi%d: %d\n", port, ret); + } + + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mx8mp_spidev_initialize + * + * Description: + * Called to configure all spi drivers + * + ****************************************************************************/ + +int mx8mp_spidev_initialize(void) +{ + int ret = 0; + +#ifdef CONFIG_MX8MP_SPI1 + mx8mp_iomuxc_config(IOMUX_SPI1_MISO); + mx8mp_iomuxc_config(IOMUX_SPI1_MOSI); + mx8mp_iomuxc_config(IOMUX_SPI1_CLK); + mx8mp_iomuxc_config(IOMUX_SPI1_CS); + + ret = board_spidev_initialize(1); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C1.\n"); + return ret; + } +#endif + +#ifdef CONFIG_MX8MP_SPI2 + mx8mp_iomuxc_config(IOMUXC_SPI2_MISO); + mx8mp_iomuxc_config(IOMUXC_SPI2_MOSI); + mx8mp_iomuxc_config(IOMUXC_SPI2_CLK); + mx8mp_iomuxc_config(IOMUXC_SPI2_CS); + mx8mp_gpio_config(GPIO_SPI2_CS); + + ret = board_spidev_initialize(2); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C2.\n"); + return ret; + } +#endif + +#ifdef CONFIG_MX8MP_SPI3 + mx8mp_iomuxc_config(IOMUXC_SPI3_MISO); + mx8mp_iomuxc_config(IOMUXC_SPI3_MOSI); + mx8mp_iomuxc_config(IOMUXC_SPI3_CLK); + mx8mp_iomuxc_config(IOMUXC_SPI3_CS); + mx8mp_gpio_config(GPIO_SPI3_CS); + + ret = board_spidev_initialize(3); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize I2C3.\n"); + return ret; + } +#endif + + return ret; +} diff --git a/boards/arm/mx8mp/verdin-mx8mp/src/verdin-mx8mp.h b/boards/arm/mx8mp/verdin-mx8mp/src/verdin-mx8mp.h index 6fa68195aa63c..c07d5fd9c4f52 100644 --- a/boards/arm/mx8mp/verdin-mx8mp/src/verdin-mx8mp.h +++ b/boards/arm/mx8mp/verdin-mx8mp/src/verdin-mx8mp.h @@ -51,6 +51,13 @@ #define BUTTON_1_IRQ MX8MP_IRQ_SOFT_GPIO1_7 #define BUTTON_1_IOMUX IOMUXC_GPIO1_IO07_GPIO1_IO07, 0, GPIO_PAD_CTRL +/* SPIs */ + +#define IOMUX_SPI1_CLK IOMUXC_ECSPI1_SCLK_ECSPI1_SCLK, 0, SPI_PAD_CTRL +#define IOMUX_SPI1_MOSI IOMUXC_ECSPI1_MISO_ECSPI1_MISO, 0, SPI_PAD_CTRL +#define IOMUX_SPI1_MISO IOMUXC_ECSPI1_MOSI_ECSPI1_MOSI, 0, SPI_PAD_CTRL +#define IOMUX_SPI1_CS IOMUXC_ECSPI1_SS0_ECSPI1_SS0, 0, SPI_PAD_CTRL + /**************************************************************************** * Public Types ****************************************************************************/ @@ -85,5 +92,15 @@ int mx8mp_bringup(void); int mx8mp_i2cdev_initialize(void); +/**************************************************************************** + * Name: mx8mp_spidev_initialize + * + * Description: + * Called to configure all spi + * + ****************************************************************************/ + +int mx8mp_spidev_initialize(void); + #endif /* __ASSEMBLY__ */ #endif /* __BOARDS_ARM_MX8MP_MX8MP_VERDIN_SRC_VERDIN_MX8MP_H */