Add support for SPI through i.MX8MP ecspi module.

Configure SPI for Verdin evaluation board
This commit is contained in:
Philippe Leduc 2023-09-21 08:26:32 +02:00 committed by Alan Carvalho de Assis
parent 21beb284bc
commit f38cdb09b4
13 changed files with 1310 additions and 3 deletions

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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 */

View file

@ -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 <nuttx/signal.h>
#include <nuttx/spi/spi.h>
#include "mx8mp_ecspi.h"
#include "mx8mp_ccm.h"
#include "hardware/mx8mp_memorymap.h"
#include "hardware/mx8mp_ecspi.h"
#include <debug.h>
#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;
}

View file

@ -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 <nuttx/config.h>
/****************************************************************************
* 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 */

View file

@ -22,12 +22,13 @@
* Included Files
****************************************************************************/
#include <nuttx/i2c/i2c_master.h>
#include <nuttx/signal.h>
#include <nuttx/i2c/i2c_master.h>
#include "mx8mp_i2c.h"
#include "mx8mp_ccm.h"
#include "hardware/mx8mp_memorymap.h"
#include "hardware/mx8mp_i2c.h"
#include <debug.h>
#include "arm_internal.h"

View file

@ -26,8 +26,12 @@
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/i2c/i2c_master.h>
#include "hardware/mx8mp_i2c.h"
/****************************************************************************
* Public Data
****************************************************************************/
struct i2c_master_s;
/****************************************************************************
* Public Function Prototypes

View file

@ -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

View file

@ -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;
}

View file

@ -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 <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/spi/spi.h>
#include "arm_internal.h"
#include "chip.h"
#include "mx8mp_gpio.h"
#include <arch/board/board.h>
/****************************************************************************
* 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

View file

@ -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 <nuttx/config.h>
#include <stdint.h>
#include <stdbool.h>
#include <debug.h>
#include <nuttx/spi/spi_transfer.h>
#include "arm_internal.h"
#include "chip.h"
#include "mx8mp_ecspi.h"
#include "verdin-mx8mp.h"
#include <arch/board/board.h>
/****************************************************************************
* 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;
}

View file

@ -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 */