| /**************************************************************************** |
| * arch/arm/src/lpc54xx/lpc54_spi_master.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| |
| /* TODO: |
| * |
| * - There are no interrupt driven transfers, only polled. I don't |
| * consider this a significant problem because of the higher rate that |
| * would be necessary for interrupt driven transfers. |
| * - Integrate DMA transfers. This is fairly important because it can |
| * a) improve the data transfer rates and b) free the CPU when the |
| * SPI driver would otherwise be stuck in a tight polling loop. |
| */ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <sys/types.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <arch/board/board.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/spi/spi.h> |
| |
| #include "arm_internal.h" |
| #include "hardware/lpc54_pinmux.h" |
| #include "hardware/lpc54_syscon.h" |
| #include "hardware/lpc54_flexcomm.h" |
| #include "hardware/lpc54_spi.h" |
| #include "lpc54_config.h" |
| #include "lpc54_enableclk.h" |
| #include "lpc54_gpio.h" |
| #include "lpc54_spi_master.h" |
| |
| #ifdef HAVE_SPI_MASTER_DEVICE |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| #define SPI_DUMMYDATA8 0xff |
| #define SPI_DUMMYDATA16 0xffff |
| |
| #define SPI_MINWIDTH 4 |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| # define SPI_MAXWIDTH 16 |
| #else |
| # define SPI_MAXWIDTH 8 |
| #endif |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /* This structure describes the state of the SSP driver */ |
| |
| struct lpc54_spidev_s |
| { |
| struct spi_dev_s dev; /* Externally visible part of the SPI interface */ |
| uintptr_t base; /* Base address of Flexcomm registers */ |
| mutex_t lock; /* Held while chip is selected for mutual exclusion */ |
| uint32_t fclock; /* Flexcomm function clock frequency */ |
| uint32_t frequency; /* Requested clock frequency */ |
| uint32_t actual; /* Actual clock frequency */ |
| uint16_t irq; /* Flexcomm IRQ number */ |
| uint8_t nbits; /* Width of word in bits (SPI_MINWIDTH to SPI_MAXWIDTH) */ |
| uint8_t mode; /* Mode 0,1,2,3 */ |
| }; |
| |
| /* These structures describes the Rx side of an 8- or 16-bit SPI data |
| * exchange. |
| */ |
| |
| struct lpc54_rxtransfer8_s |
| { |
| uint8_t *rxptr; /* Pointer into receive buffer */ |
| unsigned int remaining; /* Bytes remaining in the receive buffer */ |
| unsigned int expected; /* Bytes expected to be received */ |
| }; |
| |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| struct lpc54_rxtransfer16_s |
| { |
| uint16_t *rxptr; /* Pointer into receive buffer */ |
| unsigned int remaining; /* Hwords remaining in the receive buffer */ |
| unsigned int expected; /* Hwords expected to be received */ |
| }; |
| #endif |
| |
| /* These structures describes the Tx side of an 8- or 16-bit SPI data |
| * exchange. |
| */ |
| |
| struct lpc54_txtransfer8_s |
| { |
| uint32_t txctrl; /* Tx control bits */ |
| const uint8_t *txptr; /* Pointer into transmit buffer */ |
| unsigned int remaining; /* Bytes remaining in the transmit buffer */ |
| }; |
| |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| struct lpc54_txtransfer16_s |
| { |
| uint32_t txctrl; /* Tx control bits */ |
| const uint16_t *txptr; /* Pointer into transmit buffer */ |
| unsigned int remaining; /* Hwords remaining in the transmit buffer */ |
| }; |
| #endif |
| |
| struct lpc54_txdummy_s |
| { |
| uint32_t txctrl; /* Tx control bits */ |
| unsigned int remaining; /* Bytes remaining in the transmit buffer */ |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /* Transfer helpers */ |
| |
| static inline size_t lpc54_spi_fifodepth(struct lpc54_spidev_s *priv); |
| static inline bool lpc54_spi_txavailable(struct lpc54_spidev_s *priv); |
| static inline bool lpc54_spi_rxavailable(struct lpc54_spidev_s *priv); |
| |
| static void lpc54_spi_resetfifos(struct lpc54_spidev_s *priv); |
| static void lpc54_spi_rxtransfer8(struct lpc54_spidev_s *priv, |
| struct lpc54_rxtransfer8_s *xfr); |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| static void lpc54_spi_rxtransfer16(struct lpc54_spidev_s *priv, |
| struct lpc54_rxtransfer16_s *xfr); |
| #endif |
| static bool lpc54_spi_txtransfer8(struct lpc54_spidev_s *priv, |
| struct lpc54_txtransfer8_s *xfr); |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| static bool lpc54_spi_txtransfer16(struct lpc54_spidev_s *priv, |
| struct lpc54_txtransfer16_s *xfr); |
| #endif |
| static bool lpc54_spi_txdummy(struct lpc54_spidev_s *priv, |
| struct lpc54_txdummy_s *xfr); |
| #ifdef CONFIG_SPI_EXCHANGE |
| static void lpc54_spi_exchange8(struct lpc54_spidev_s *priv, |
| const void *txbuffer, void *rxbuffer, |
| size_t nwords); |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| static void lpc54_spi_exchange16(struct lpc54_spidev_s *priv, |
| const void *txbuffer, void *rxbuffer, |
| size_t nwords); |
| #endif |
| #endif |
| static void lpc54_spi_sndblock8(struct lpc54_spidev_s *priv, |
| const void *buffer, size_t nwords); |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| static void lpc54_spi_sndblock16(struct lpc54_spidev_s *priv, |
| const void *buffer, size_t nwords); |
| #endif |
| static void lpc54_spi_recvblock8(struct lpc54_spidev_s *priv, |
| void *buffer, size_t nwords); |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| static void lpc54_spi_recvblock16(struct lpc54_spidev_s *priv, |
| void *buffer, size_t nwords); |
| #endif |
| |
| /* SPI methods */ |
| |
| static int lpc54_spi_lock(struct spi_dev_s *dev, bool lock); |
| static uint32_t lpc54_spi_setfrequency(struct spi_dev_s *dev, |
| uint32_t frequency); |
| static void lpc54_spi_setmode(struct spi_dev_s *dev, |
| enum spi_mode_e mode); |
| static void lpc54_spi_setbits(struct spi_dev_s *dev, int nbits); |
| static uint32_t lpc54_spi_send(struct spi_dev_s *dev, uint32_t wd); |
| #ifdef CONFIG_SPI_EXCHANGE |
| static void lpc54_spi_exchange(struct spi_dev_s *dev, |
| const void *txbuffer, void *rxbuffer, |
| size_t nwords); |
| #endif |
| static void lpc54_spi_sndblock(struct spi_dev_s *dev, |
| const void *buffer, size_t nwords); |
| static void lpc54_spi_recvblock(struct spi_dev_s *dev, |
| void *buffer, size_t nwords); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_LPC54_SPI0_MASTER |
| static const struct spi_ops_s g_spi0_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi0_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi0_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi0_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi0_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi0_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_LPC54_SPI1_MASTER |
| static const struct spi_ops_s g_spi1_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi1_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi1_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi1_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi1_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi1_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_LPC54_SPI2_MASTER |
| static const struct spi_ops_s g_spi2_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi2_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi2_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi2_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi2_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi2_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_LPC54_SPI3_MASTER |
| static const struct spi_ops_s g_spi3_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi3_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi3_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi3_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi3_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi3_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_LPC54_SPI4_MASTER |
| static const struct spi_ops_s g_spi4_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi4_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi4_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi4_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi4_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi4_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_LPC54_SPI5_MASTER |
| static const struct spi_ops_s g_spi5_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi5_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi5_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi5_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi5_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi5_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_LPC54_SPI6_MASTER |
| static const struct spi_ops_s g_spi6_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi6_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi6_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi6_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi6_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi6_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_LPC54_SPI7_MASTER |
| static const struct spi_ops_s g_spi7_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi7_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi7_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi7_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi7_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi7_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_LPC54_SPI8_MASTER |
| static const struct spi_ops_s g_spi8_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi8_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi8_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi8_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi8_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi8_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_LPC54_SPI9_MASTER |
| static const struct spi_ops_s g_spi9_ops = |
| { |
| .lock = lpc54_spi_lock, |
| .select = lpc54_spi9_select, /* Provided externally */ |
| .setfrequency = lpc54_spi_setfrequency, |
| .setmode = lpc54_spi_setmode, |
| .setbits = lpc54_spi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = NULL, /* Not supported */ |
| #endif |
| .status = lpc54_spi9_status, /* Provided externally */ |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = lpc54_spi9_cmddata, /* Provided externally */ |
| #endif |
| .send = lpc54_spi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = lpc54_spi_exchange, |
| #else |
| .sndblock = lpc54_spi_sndblock, |
| .recvblock = lpc54_spi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = lpc54_spi9_register, /* Provided externally */ |
| #else |
| .registercallback = NULL, /* Not implemented */ |
| #endif |
| }; |
| |
| static struct lpc54_spidev_s g_spi9_dev = |
| { |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_putreg |
| * |
| * Description: |
| * Write a value to a register at the offset from the Flexcomm base. |
| * |
| ****************************************************************************/ |
| |
| static inline void lpc54_spi_putreg(struct lpc54_spidev_s *priv, |
| unsigned int regoffset, uint32_t regval) |
| { |
| putreg32(regval, priv->base + regoffset); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_getreg |
| * |
| * Description: |
| * Read the content of a register at the offset from the Flexcomm base. |
| * |
| ****************************************************************************/ |
| |
| static inline uint32_t lpc54_spi_getreg(struct lpc54_spidev_s *priv, |
| unsigned int regoffset) |
| { |
| return getreg32(priv->base + regoffset); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_fifodepth |
| * |
| * Description: |
| * Return the depth of the SPI FIFOs. This is a constant value and could |
| * be hard coded. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * The FIFO depth in words of the configured bit width. |
| * |
| ****************************************************************************/ |
| |
| static inline size_t lpc54_spi_fifodepth(struct lpc54_spidev_s *priv) |
| { |
| uint32_t regval = lpc54_spi_getreg(priv, LPC54_SPI_FIFOCFG_OFFSET); |
| return ((regval & SPI_FIFOCFG_SIZE_MASK) >> SPI_FIFOCFG_SIZE_SHIFT) << 3; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_txavailable |
| * |
| * Description: |
| * Return true if the Tx FIFO is not full. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * true: Tx FIFO is not full. |
| * |
| ****************************************************************************/ |
| |
| static inline bool lpc54_spi_txavailable(struct lpc54_spidev_s *priv) |
| { |
| uint32_t regval = lpc54_spi_getreg(priv, LPC54_SPI_FIFOSTAT_OFFSET); |
| return ((regval & SPI_FIFOSTAT_TXNOTFULL) != 0); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_rxavailable |
| * |
| * Description: |
| * Return true if the Rx FIFO is not empty. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * true: Rx FIFO is not empty. |
| * |
| ****************************************************************************/ |
| |
| static inline bool lpc54_spi_rxavailable(struct lpc54_spidev_s *priv) |
| { |
| uint32_t regval = lpc54_spi_getreg(priv, LPC54_SPI_FIFOSTAT_OFFSET); |
| return ((regval & SPI_FIFOSTAT_RXNOTEMPTY) != 0); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_rxdiscard |
| * |
| * Description: |
| * Read and discard the data until the Rx FIFO is empty. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void lpc54_spi_rxdiscard(struct lpc54_spidev_s *priv) |
| { |
| while (lpc54_spi_rxavailable(priv)) |
| { |
| lpc54_spi_getreg(priv, LPC54_SPI_FIFORD_OFFSET); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_resetfifos |
| * |
| * Description: |
| * Clear Tx/Rx errors and empty FIFOs. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void lpc54_spi_resetfifos(struct lpc54_spidev_s *priv) |
| { |
| uint32_t regval; |
| |
| /* Clear Tx/Rx errors and empty FIFOs */ |
| |
| regval = lpc54_spi_getreg(priv, LPC54_SPI_FIFOCFG_OFFSET); |
| regval |= (SPI_FIFOCFG_EMPTYTX | SPI_FIFOCFG_EMPTYRX); |
| lpc54_spi_putreg(priv, LPC54_SPI_FIFOCFG_OFFSET, regval); |
| |
| regval = lpc54_spi_getreg(priv, LPC54_SPI_FIFOSTAT_OFFSET); |
| regval |= (SPI_FIFOSTAT_TXERR | SPI_FIFOSTAT_RXERR); |
| lpc54_spi_putreg(priv, LPC54_SPI_FIFOSTAT_OFFSET, regval); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_rxtransfer8 and lpc54_spi_rxtransfer16 |
| * |
| * Description: |
| * Receive one 8-bit or 16-bit value from the selected SPI device. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * xfr - Describes the Rx transfer |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void lpc54_spi_rxtransfer8(struct lpc54_spidev_s *priv, |
| struct lpc54_rxtransfer8_s *xfr) |
| { |
| /* Read one byte if available and expected */ |
| |
| if (lpc54_spi_rxavailable(priv)) |
| { |
| /* There is something in the Rx FIFO to be read. Are we expecting |
| * data in the Rx FIFO? Is there space available in the Rx buffer? |
| */ |
| |
| if (xfr->expected == 0 || xfr->remaining == 0) |
| { |
| /* No.. |
| * then just read and discard the data until the Rx FIFO is empty |
| */ |
| |
| lpc54_spi_rxdiscard(priv); |
| xfr->expected = 0; |
| } |
| else |
| { |
| /* Read and transfer one byte */ |
| |
| *xfr->rxptr = lpc54_spi_getreg(priv, LPC54_SPI_FIFORD_OFFSET); |
| |
| /* Update pointers and counts */ |
| |
| xfr->rxptr++; |
| xfr->remaining--; |
| xfr->expected--; |
| } |
| } |
| } |
| |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| static void lpc54_spi_rxtransfer16(struct lpc54_spidev_s *priv, |
| struct lpc54_rxtransfer16_s *xfr) |
| { |
| /* Read one HWord if available and expected */ |
| |
| if (lpc54_spi_rxavailable(priv)) |
| { |
| /* There is something in the Rx FIFO to be read. Are we expecting |
| * data in the Rx FIFO? Is there space available in the Rx buffer? |
| */ |
| |
| if (xfr->expected == 0 || xfr->remaining == 0) |
| { |
| /* No.. then just read and discard the data until the Rx FIFO |
| * is empty. |
| */ |
| |
| lpc54_spi_rxdiscard(priv); |
| xfr->expected = 0; |
| } |
| else |
| { |
| /* Read and transfer HWord */ |
| |
| *xfr->rxptr = lpc54_spi_getreg(priv, LPC54_SPI_FIFORD_OFFSET); |
| |
| /* Update pointers and counts */ |
| |
| xfr->rxptr++; |
| xfr->remaining--; |
| xfr->expected--; |
| } |
| } |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_txtransfer8 and lpc54_spi_txtransfer16 |
| * |
| * Description: |
| * Send one 8- or 16-bit value to the selected SPI device. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * xfr - Describes the Tx transfer |
| * |
| * Returned Value: |
| * true: The value was added to the TxFIFO |
| * |
| ****************************************************************************/ |
| |
| static bool lpc54_spi_txtransfer8(struct lpc54_spidev_s *priv, |
| struct lpc54_txtransfer8_s *xfr) |
| { |
| uint32_t regval; |
| |
| /* Transmit if txFIFO is not full and there is more Tx data to be sent */ |
| |
| if (lpc54_spi_txavailable(priv) && xfr->remaining > 0) |
| { |
| /* Get the next byte to be sent */ |
| |
| regval = *xfr->txptr; |
| |
| /* And send it */ |
| |
| regval |= xfr->txctrl; |
| lpc54_spi_putreg(priv, LPC54_SPI_FIFOWR_OFFSET, regval); |
| |
| /* Update pointers and counts */ |
| |
| xfr->txptr++; |
| xfr->remaining--; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| static bool lpc54_spi_txtransfer16(struct lpc54_spidev_s *priv, |
| struct lpc54_txtransfer16_s *xfr) |
| { |
| uint32_t regval; |
| |
| /* Transmit if txFIFO is not full and there is more Tx data to be sent */ |
| |
| if (lpc54_spi_txavailable(priv) && xfr->remaining > 0) |
| { |
| /* Get the next byte to be sent */ |
| |
| regval = *xfr->txptr; |
| |
| /* And send it */ |
| |
| regval |= xfr->txctrl; |
| lpc54_spi_putreg(priv, LPC54_SPI_FIFOWR_OFFSET, regval); |
| |
| /* Update pointers and counts */ |
| |
| xfr->txptr++; |
| xfr->remaining--; |
| |
| return true; |
| } |
| |
| return false; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_txdummy |
| * |
| * Description: |
| * Send dummy Tx data when we really only care about the Rx data. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * xfr - Describes the Tx transfer |
| * |
| * Returned Value: |
| * true: The dummy value was added to the TxFIFO |
| * |
| ****************************************************************************/ |
| |
| static bool lpc54_spi_txdummy(struct lpc54_spidev_s *priv, |
| struct lpc54_txdummy_s *xfr) |
| { |
| /* Transmit if txFIFO is not full and there is more Tx data to be sent */ |
| |
| if (lpc54_spi_txavailable(priv) && xfr->remaining > 0) |
| { |
| /* Send the dummy data */ |
| |
| lpc54_spi_putreg(priv, LPC54_SPI_FIFOWR_OFFSET, xfr->txctrl); |
| |
| /* Update counts */ |
| |
| xfr->remaining--; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_exchange8 and lpc54_spi_exchange16 |
| * |
| * Description: |
| * Implements the SPI exchange method for the case of 8-bit and 16-bit |
| * transfers. |
| * |
| * Input Parameters: |
| * priv - 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 |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_SPI_EXCHANGE |
| static void lpc54_spi_exchange8(struct lpc54_spidev_s *priv, |
| const void *txbuffer, void *rxbuffer, |
| size_t nwords) |
| { |
| struct lpc54_rxtransfer8_s rxtransfer; |
| struct lpc54_txtransfer8_s txtransfer; |
| size_t depth; |
| |
| DEBUGASSERT(rxbuffer != NULL && txbuffer != NULL); |
| |
| /* Get the FIFO depth */ |
| |
| depth = lpc54_spi_fifodepth(priv); |
| |
| /* Set up the transfer data */ |
| |
| txtransfer.txctrl = SPI_FIFOWR_LEN(priv->nbits) | SPI_FIFOWR_TXSSELN_ALL; |
| txtransfer.txptr = (uint8_t *)txbuffer; |
| txtransfer.remaining = nwords; |
| rxtransfer.rxptr = (uint8_t *)rxbuffer; |
| rxtransfer.remaining = nwords; |
| rxtransfer.expected = 0; |
| |
| /* Clear Tx/Rx errors and empty FIFOs */ |
| |
| lpc54_spi_resetfifos(priv); |
| |
| /* Loop until all Tx data has been sent and until all Rx data has been |
| * received. |
| */ |
| |
| while (txtransfer.remaining != 0 || rxtransfer.remaining != 0) |
| { |
| /* Transfer one byte from the Rx FIFO to the caller's Rx buffer */ |
| |
| lpc54_spi_rxtransfer8(priv, &rxtransfer); |
| |
| /* If sending another byte would exceed the capacity of the Rx FIFO |
| * then read-only until there space freed. |
| */ |
| |
| if (rxtransfer.expected < depth) |
| { |
| /* Attempt to transfer one byte from the caller's Tx buffer to |
| * the Tx FIFO. |
| */ |
| |
| if (lpc54_spi_txtransfer8(priv, &txtransfer)) |
| { |
| /* Increment the Rx expected count if successful */ |
| |
| rxtransfer.expected++; |
| } |
| } |
| } |
| } |
| #endif /* CONFIG_SPI_EXCHANGE */ |
| |
| #if defined(CONFIG_SPI_EXCHANGE) && defined(CONFIG_LPC54_SPI_WIDEDATA) |
| static void lpc54_spi_exchange16(struct lpc54_spidev_s *priv, |
| const void *txbuffer, |
| void *rxbuffer, |
| size_t nwords) |
| { |
| struct lpc54_rxtransfer16_s rxtransfer; |
| struct lpc54_txtransfer16_s txtransfer; |
| uint32_t regval; |
| size_t depth; |
| |
| DEBUGASSERT(rxbuffer != NULL && ((uintptr_t)rxbuffer & 1) == 0); |
| DEBUGASSERT(txbuffer != NULL && ((uintptr_t)txbuffer & 1) == 0); |
| |
| /* Get the FIFO depth */ |
| |
| depth = lpc54_spi_fifodepth(priv); |
| |
| /* Set up the transfer data */ |
| |
| txtransfer.txctrl = SPI_FIFOWR_LEN(priv->nbits) | SPI_FIFOWR_TXSSELN_ALL; |
| txtransfer.txptr = (uint16_t *)txbuffer; |
| txtransfer.remaining = nwords; |
| rxtransfer.rxptr = (uint16_t *)rxbuffer; |
| rxtransfer.remaining = nwords; |
| rxtransfer.expected = 0; |
| |
| /* Clear Tx/Rx errors and empty FIFOs */ |
| |
| lpc54_spi_resetfifos(priv); |
| |
| /* Loop until all Tx data has been sent and until all Rx data has been |
| * received. |
| */ |
| |
| while (txtransfer.remaining || rxtransfer.remaining || rxtransfer.expected) |
| { |
| /* Transfer one HWord from the Rx FIFO to the caller's Rx buffer */ |
| |
| lpc54_spi_rxtransfer16(priv, &rxtransfer); |
| |
| /* If sending another byte would exceed the capacity of the Rx FIFO |
| * then read-only until there space freed. |
| */ |
| |
| if (rxtransfer.expected < depth) |
| { |
| /* Attempt to send one more byte */ |
| |
| if (lpc54_spi_txtransfer16(priv, &txtransfer)) |
| { |
| /* Increment the Rx expected count if successful */ |
| |
| rxtransfer.expected++; |
| } |
| } |
| } |
| } |
| #endif /* CONFIG_SPI_EXCHANGE && CONFIG_LPC54_SPI_WIDEDATA */ |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_sndblock8 and lpc54_spi_sndblock16 |
| * |
| * Description: |
| * Implements the SPI sndblock method for the case of 8- and 16-bit |
| * transfers. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * buffer - A pointer to the buffer of data to be sent |
| * nwords - the length of data to send from the buffer in number 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 lpc54_spi_sndblock8(struct lpc54_spidev_s *priv, |
| const void *buffer, size_t nwords) |
| { |
| struct lpc54_txtransfer8_s txtransfer; |
| |
| DEBUGASSERT(buffer != NULL); |
| |
| /* Set up the transfer data. NOTE that we are ignoring returned Rx data */ |
| |
| txtransfer.txctrl = SPI_FIFOWR_RXIGNORE | SPI_FIFOWR_LEN(priv->nbits) | |
| SPI_FIFOWR_TXSSELN_ALL; |
| txtransfer.txptr = (uint8_t *)buffer; |
| txtransfer.remaining = nwords; |
| |
| /* Clear Tx/Rx errors and empty FIFOs */ |
| |
| lpc54_spi_resetfifos(priv); |
| |
| /* Loop until all Tx data has been sent */ |
| |
| while (txtransfer.remaining != 0) |
| { |
| /* Attempt to transfer one byte from the caller's Tx buffer to the |
| * Tx FIFO. |
| */ |
| |
| lpc54_spi_txtransfer8(priv, &txtransfer); |
| } |
| } |
| |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| static void lpc54_spi_sndblock16(struct lpc54_spidev_s *priv, |
| const void *buffer, size_t nwords) |
| { |
| struct lpc54_txtransfer16_s txtransfer; |
| |
| DEBUGASSERT(buffer != NULL); |
| |
| /* Set up the transfer data. NOTE that we are ignoring returned Rx data */ |
| |
| txtransfer.txctrl = SPI_FIFOWR_RXIGNORE | SPI_FIFOWR_LEN(priv->nbits) | |
| SPI_FIFOWR_TXSSELN_ALL; |
| txtransfer.txptr = (uint16_t *)buffer; |
| txtransfer.remaining = nwords; |
| |
| /* Clear Tx/Rx errors and empty FIFOs */ |
| |
| lpc54_spi_resetfifos(priv); |
| |
| /* Loop until all Tx data has been sent and until all Rx data has been |
| * received. |
| */ |
| |
| while (txtransfer.remaining != 0) |
| { |
| /* Attempt to transfer one byte from the caller's Tx buffer to the |
| * Tx FIFO. |
| */ |
| |
| lpc54_spi_txtransfer16(priv, &txtransfer); |
| } |
| } |
| #endif /* CONFIG_LPC54_SPI_WIDEDATA */ |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_recvblock8 and lpc54_spi_recvblock16 |
| * |
| * Description: |
| * Implements the SPI recvblock method for the case of 8- and 16-bit |
| * transfers. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * buffer - A pointer to the buffer in which to receive data |
| * nwords - the length of data that can be received in the buffer in |
| * number 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 lpc54_spi_recvblock8(struct lpc54_spidev_s *priv, |
| void *buffer, size_t nwords) |
| { |
| struct lpc54_rxtransfer8_s rxtransfer; |
| struct lpc54_txdummy_s txtransfer; |
| size_t depth; |
| |
| DEBUGASSERT(buffer != NULL); |
| |
| /* Get the FIFO depth */ |
| |
| depth = lpc54_spi_fifodepth(priv); |
| |
| /* Set up the transfer data */ |
| |
| txtransfer.txctrl = SPI_DUMMYDATA8 | SPI_FIFOWR_LEN(priv->nbits) | |
| SPI_FIFOWR_TXSSELN_ALL; |
| txtransfer.remaining = nwords; |
| rxtransfer.rxptr = (uint8_t *)buffer; |
| rxtransfer.remaining = nwords; |
| rxtransfer.expected = 0; |
| |
| /* Clear Tx/Rx errors and empty FIFOs */ |
| |
| lpc54_spi_resetfifos(priv); |
| |
| /* Loop until all Tx data has been sent and until all Rx data has been |
| * received. |
| */ |
| |
| while (txtransfer.remaining != 0 || rxtransfer.remaining != 0) |
| { |
| /* Transfer one byte from the Rx FIFO to the caller's Rx buffer */ |
| |
| lpc54_spi_rxtransfer8(priv, &rxtransfer); |
| |
| /* If sending another byte would exceed the capacity of the Rx FIFO |
| * then read-only until there space freed. |
| */ |
| |
| if (rxtransfer.expected < depth) |
| { |
| /* Attempt to transfer one dummy byte to the Tx FIFO. */ |
| |
| if (lpc54_spi_txdummy(priv, &txtransfer)) |
| { |
| /* Increment the Rx expected count if successful */ |
| |
| rxtransfer.expected++; |
| } |
| } |
| } |
| } |
| |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| static void lpc54_spi_recvblock16(struct lpc54_spidev_s *priv, |
| void *buffer, size_t nwords) |
| { |
| struct lpc54_rxtransfer16_s rxtransfer; |
| struct lpc54_txdummy_s txtransfer; |
| size_t depth; |
| |
| DEBUGASSERT(buffer != NULL); |
| |
| /* Get the FIFO depth */ |
| |
| depth = lpc54_spi_fifodepth(priv); |
| |
| /* Set up the transfer data */ |
| |
| txtransfer.txctrl = SPI_DUMMYDATA16 | SPI_FIFOWR_LEN(priv->nbits) | |
| SPI_FIFOWR_TXSSELN_ALL; |
| txtransfer.remaining = nwords; |
| rxtransfer.rxptr = (uint16_t *)rxbuffer; |
| rxtransfer.remaining = nwords; |
| rxtransfer.expected = 0; |
| |
| /* Clear Tx/Rx errors and empty FIFOs */ |
| |
| lpc54_spi_resetfifos(priv); |
| |
| /* Loop until all Tx data has been sent and until all Rx data has been |
| * received. |
| */ |
| |
| while (txtransfer.remaining || rxtransfer.remaining || rxtransfer.expected) |
| { |
| /* Transfer one HWord from the Rx FIFO to the caller's Rx buffer */ |
| |
| lpc54_spi_rxtransfer16(priv, &rxtransfer); |
| |
| /* If sending another byte would exceed the capacity of the Rx FIFO |
| * then read-only until there space freed. |
| */ |
| |
| if (rxtransfer.expected < depth) |
| { |
| /* Attempt to transfer one dummy HWord to the Tx FIFO. */ |
| |
| if (lpc54_spi_txdummy(priv, &txtransfer)) |
| { |
| /* Increment the Rx expected count if successful */ |
| |
| rxtransfer.expected++; |
| } |
| } |
| } |
| } |
| #endif /* CONFIG_LPC54_SPI_WIDEDATA */ |
| |
| /**************************************************************************** |
| * Name: lpc54_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 lpc54_spi_lock(struct spi_dev_s *dev, bool lock) |
| { |
| struct lpc54_spidev_s *priv = (struct lpc54_spidev_s *)dev; |
| int ret; |
| |
| if (lock) |
| { |
| ret = nxmutex_lock(&priv->lock); |
| } |
| else |
| { |
| ret = nxmutex_unlock(&priv->lock); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_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 lpc54_spi_setfrequency(struct spi_dev_s *dev, |
| uint32_t frequency) |
| { |
| struct lpc54_spidev_s *priv = (struct lpc54_spidev_s *)dev; |
| uint32_t divider; |
| uint32_t actual; |
| uint32_t regval; |
| |
| /* Check if the requested frequency is the same as the current frequency |
| * selection. |
| */ |
| |
| DEBUGASSERT(priv != NULL && frequency <= priv->fclock / 2); |
| |
| if (priv->frequency == frequency) |
| { |
| /* We are already at this frequency. Return the actual. */ |
| |
| return priv->actual; |
| } |
| |
| /* Set the new SPI frequency */ |
| |
| divider = priv->fclock / frequency; |
| if (divider > 0x10000) |
| { |
| divider = 0x10000; |
| } |
| |
| regval = lpc54_spi_getreg(priv, LPC54_SPI_DIV_OFFSET); |
| regval &= ~SPI_DIV_MASK; |
| regval |= SPI_DIV(divider); |
| lpc54_spi_putreg(priv, LPC54_SPI_DIV_OFFSET, regval); |
| |
| /* Calculate the actual frequency */ |
| |
| actual = priv->fclock / divider; |
| |
| /* Save the frequency setting */ |
| |
| priv->frequency = frequency; |
| priv->actual = actual; |
| |
| spiinfo("Frequency %d->%d\n", frequency, actual); |
| return actual; |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_setmode |
| * |
| * Description: |
| * Set the SPI mode. Optional. See enum spi_mode_e for mode definitions |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * mode - The SPI mode requested |
| * |
| * Returned Value: |
| * none |
| * |
| ****************************************************************************/ |
| |
| static void lpc54_spi_setmode(struct spi_dev_s *dev, |
| enum spi_mode_e mode) |
| { |
| struct lpc54_spidev_s *priv = (struct lpc54_spidev_s *)dev; |
| uint32_t regval; |
| |
| /* Has the mode changed? */ |
| |
| if (mode != priv->mode) |
| { |
| /* Yes... Set the new mode */ |
| |
| regval = lpc54_spi_getreg(priv, LPC54_SPI_CFG_OFFSET); |
| regval &= ~(SPI_CFG_CPHA | SPI_CFG_CPOL); |
| |
| switch (mode) |
| { |
| case SPIDEV_MODE0: /* CPOL=0; CPHA=0 */ |
| break; |
| |
| case SPIDEV_MODE1: /* CPOL=0; CPHA=1 */ |
| regval |= SPI_CFG_CPHA; |
| break; |
| |
| case SPIDEV_MODE2: /* CPOL=1; CPHA=0 */ |
| regval |= SPI_CFG_CPOL; |
| break; |
| |
| case SPIDEV_MODE3: /* CPOL=1; CPHA=1 */ |
| regval |= (SPI_CFG_CPHA | SPI_CFG_CPOL); |
| break; |
| |
| default: |
| DEBUGPANIC(); |
| return; |
| } |
| |
| lpc54_spi_putreg(priv, LPC54_SPI_CFG_OFFSET, regval); |
| |
| /* Save the mode so that subsequent re-configurations will be faster */ |
| |
| priv->mode = mode; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_setbits |
| * |
| * Description: |
| * Set the number if bits per word. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * nbits - The number of bits requests |
| * |
| * Returned Value: |
| * none |
| * |
| ****************************************************************************/ |
| |
| static void lpc54_spi_setbits(struct spi_dev_s *dev, int nbits) |
| { |
| struct lpc54_spidev_s *priv = (struct lpc54_spidev_s *)dev; |
| |
| /* The valid range of bit selections is SPI_MINWIDTH through SPI_MAXWIDTH */ |
| |
| DEBUGASSERT(priv != NULL && nbits >= SPI_MINWIDTH && |
| nbits <= SPI_MAXWIDTH); |
| |
| if (nbits >= SPI_MINWIDTH && nbits <= SPI_MAXWIDTH) |
| { |
| /* Save the selection. It will be applied when data is transferred. */ |
| |
| priv->nbits = nbits; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_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 lpc54_spi_send(struct spi_dev_s *dev, uint32_t wd) |
| { |
| struct lpc54_spidev_s *priv = (struct lpc54_spidev_s *)dev; |
| uint32_t regval; |
| |
| DEBUGASSERT(priv != NULL); |
| |
| /* Clear Tx/Rx errors and empty FIFOs */ |
| |
| lpc54_spi_resetfifos(priv); |
| |
| /* Send the word. Since we just reset the FIFOs, we assume that the Tx |
| * FIFO is not full and that the Rx FIFO is empty. |
| */ |
| |
| DEBUGASSERT(lpc54_spi_txavailable(priv) || !lpc54_spi_rxavailable(priv)); |
| |
| regval = wd | SPI_FIFOWR_LEN(priv->nbits) | SPI_FIFOWR_TXSSELN_ALL; |
| lpc54_spi_putreg(priv, LPC54_SPI_FIFOWR_OFFSET, regval); |
| |
| /* Wait for the Rx FIFO to become non-empty. */ |
| |
| while (!lpc54_spi_rxavailable(priv)) |
| { |
| } |
| |
| /* Then read and return the value from the Rx FIFO */ |
| |
| return lpc54_spi_getreg(priv, LPC54_SPI_FIFORD_OFFSET); |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_exchange |
| * |
| * Description: |
| * Exchange a block of data on SPI |
| * |
| * 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 |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_SPI_EXCHANGE |
| static void lpc54_spi_exchange(struct lpc54_spidev_s *priv, |
| const void *txbuffer, void *rxbuffer, |
| size_t nwords) |
| { |
| struct lpc54_spidev_s *priv = (struct lpc54_spidev_s *)dev; |
| |
| DEBUGASSERT(priv != NULL); |
| |
| /* If there is no data sink, then handle this transfer with |
| * lpc54_spi_sndblock(). |
| */ |
| |
| if (rxbuffer == NULL) |
| { |
| lpc54_spi_sndblock(priv, txbuffer, nwords) |
| } |
| |
| /* If there is no data source, then handle this transfer with |
| * lpc54_spi_recvblock(). |
| */ |
| |
| else if (txbuffer == NULL) |
| { |
| lpc54_spi_recvblock(priv, rxbuffer, nwords) |
| } |
| |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| /* If the data with is > 8-bits, then handle this transfer with |
| * lpc54_spi_exchange16(). |
| */ |
| |
| else if (priv->nbits > 8) |
| { |
| lpc54_spi_exchange16(priv, txbuffer, rxbuffer, nwords); |
| } |
| #endif |
| |
| /* Otherwise, let lpc54_spi_exchange8() do the job */ |
| |
| else if (priv->nbits > 8) |
| { |
| lpc54_spi_exchange8(priv, txbuffer, rxbuffer, nwords); |
| } |
| } |
| #endif /* CONFIG_SPI_EXCHANGE */ |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_sndblock |
| * |
| * Description: |
| * Send a block of data on SPI |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * buffer - A pointer to the buffer of data to be sent |
| * nwords - the length of data to send from the buffer in number 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 lpc54_spi_sndblock(struct spi_dev_s *dev, |
| const void *buffer, size_t nwords) |
| { |
| struct lpc54_spidev_s *priv = (struct lpc54_spidev_s *)dev; |
| |
| spiinfo("buffer=%p nwords=%d\n", buffer, nwords); |
| DEBUGASSERT(priv != NULL && buffer != NULL); |
| |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| /* If the data with is > 8-bits, then handle this transfer with |
| * lpc54_spi_sndblock16(). |
| */ |
| |
| if (priv->nbits > 8) |
| { |
| lpc54_spi_sndblock16(priv, buffer, nwords); |
| } |
| |
| /* Otherwise, let lpc54_spi_sndblock8() do the job */ |
| |
| else if (priv->nbits > 8) |
| #endif |
| { |
| lpc54_spi_sndblock8(priv, buffer, nwords); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: lpc54_spi_recvblock |
| * |
| * Description: |
| * Revice a block of data from SPI |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * buffer - A pointer to the buffer in which to receive data |
| * nwords - the length of data that can be received in the buffer in |
| * number 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 lpc54_spi_recvblock(struct spi_dev_s *dev, void *buffer, |
| size_t nwords) |
| { |
| struct lpc54_spidev_s *priv = (struct lpc54_spidev_s *)dev; |
| |
| spiinfo("buffer=%p nwords=%d\n", buffer, nwords); |
| DEBUGASSERT(priv != NULL && buffer != NULL); |
| |
| #ifdef CONFIG_LPC54_SPI_WIDEDATA |
| /* If the data with is > 8-bits, then handle this transfer with |
| * lpc54_spi_recvblock16(). |
| */ |
| |
| if (priv->nbits > 8) |
| { |
| lpc54_spi_recvblock16(priv, buffer, nwords); |
| } |
| |
| /* Otherwise, let lpc54_spi_recvblock8() do the job */ |
| |
| else if (priv->nbits > 8) |
| #endif |
| { |
| lpc54_spi_recvblock8(priv, buffer, nwords); |
| } |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: lpc54_spibus_initialize |
| * |
| * Description: |
| * Initialize the selected SPI port |
| * 0 - SPI0 |
| * 1 - SPI1 |
| * ... |
| * 9 - SSP9 |
| * |
| * Input Parameters: |
| * port - SPI peripheral number. 0..9 |
| * |
| * Returned Value: |
| * Valid SPI device structure reference on success; a NULL on failure |
| * |
| ****************************************************************************/ |
| |
| struct spi_dev_s *lpc54_spibus_initialize(int port) |
| { |
| struct lpc54_spidev_s *priv; |
| irqstate_t flags; |
| uint32_t regval; |
| |
| flags = enter_critical_section(); |
| |
| /* Configure the requested SPI peripheral */ |
| |
| /* NOTE: The basic FLEXCOMM initialization was performed in |
| * lpc54_lowputc.c. |
| */ |
| |
| #ifdef CONFIG_LPC54_SPI0_MASTER |
| if (port == 0) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM0 */ |
| |
| lpc54_flexcomm0_enableclk(); |
| |
| /* Set FLEXCOMM0 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM0_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi0_dev; |
| priv->base = LPC54_FLEXCOMM0_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM0; |
| priv->fclock = BOARD_FLEXCOMM0_FCLK; |
| priv->dev.ops = &g_spi0_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI0_SCK); |
| lpc54_gpio_config(GPIO_SPI0_MOSI); |
| lpc54_gpio_config(GPIO_SPI0_MISO); |
| |
| /* Set up the FLEXCOMM0 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM0_CLKSEL, LPC54_SYSCON_FCLKSEL0); |
| } |
| else |
| #endif |
| #ifdef CONFIG_LPC54_SPI1_MASTER |
| if (port == 1) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM1 */ |
| |
| lpc54_flexcomm1_enableclk(); |
| |
| /* Set FLEXCOMM1 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM1_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi1_dev; |
| priv->base = LPC54_FLEXCOMM1_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM1; |
| priv->fclock = BOARD_FLEXCOMM1_FCLK; |
| priv->dev.ops = &g_spi1_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI1_SCK); |
| lpc54_gpio_config(GPIO_SPI1_MOSI); |
| lpc54_gpio_config(GPIO_SPI1_MISO); |
| |
| /* Set up the FLEXCOMM1 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM1_CLKSEL, LPC54_SYSCON_FCLKSEL1); |
| } |
| else |
| #endif |
| #ifdef CONFIG_LPC54_SPI2_MASTER |
| if (port == 2) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM2 */ |
| |
| lpc54_flexcomm2_enableclk(); |
| |
| /* Set FLEXCOMM2 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM2_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi2_dev; |
| priv->base = LPC54_FLEXCOMM2_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM2; |
| priv->fclock = BOARD_FLEXCOMM2_FCLK; |
| priv->dev.ops = &g_spi2_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI2_SCK); |
| lpc54_gpio_config(GPIO_SPI2_MOSI); |
| lpc54_gpio_config(GPIO_SPI2MISO); |
| |
| /* Set up the FLEXCOMM2 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM2_CLKSEL, LPC54_SYSCON_FCLKSEL2); |
| } |
| else |
| #endif |
| #ifdef CONFIG_LPC54_SPI3_MASTER |
| if (port == 3) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM3 */ |
| |
| lpc54_flexcomm3_enableclk(); |
| |
| /* Set FLEXCOMM3 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM3_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi3_dev; |
| priv->base = LPC54_FLEXCOMM3_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM3; |
| priv->fclock = BOARD_FLEXCOMM3_FCLK; |
| priv->dev.ops = &g_spi3_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI3_SCK); |
| lpc54_gpio_config(GPIO_SPI3_MOSI); |
| lpc54_gpio_config(GPIO_SPI3_MISO); |
| |
| /* Set up the FLEXCOMM3 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM3_CLKSEL, LPC54_SYSCON_FCLKSEL3); |
| } |
| else |
| #endif |
| #ifdef CONFIG_LPC54_SPI4_MASTER |
| if (port == 4) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM4 */ |
| |
| lpc54_flexcomm4_enableclk(); |
| |
| /* Set FLEXCOMM4 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM4_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi4_dev; |
| priv->base = LPC54_FLEXCOMM4_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM4; |
| priv->fclock = BOARD_FLEXCOMM4_FCLK; |
| priv->dev.ops = &g_spi4_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI4_SCK); |
| lpc54_gpio_config(GPIO_SPI4_MOSI); |
| lpc54_gpio_config(GPIO_SPI4_MISO); |
| |
| /* Set up the FLEXCOMM4 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM4_CLKSEL, LPC54_SYSCON_FCLKSEL4); |
| } |
| else |
| #endif |
| #ifdef CONFIG_LPC54_SPI5_MASTER |
| if (port == 5) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM5 */ |
| |
| lpc54_flexcomm5_enableclk(); |
| |
| /* Set FLEXCOMM5 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM5_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi5_dev; |
| priv->base = LPC54_FLEXCOMM5_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM5; |
| priv->fclock = BOARD_FLEXCOMM5_FCLK; |
| priv->dev.ops = &g_spi5_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI5_SCK); |
| lpc54_gpio_config(GPIO_SPI5_MOSI); |
| lpc54_gpio_config(GPIO_SPI5_MISO); |
| |
| /* Set up the FLEXCOMM5 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM5_CLKSEL, LPC54_SYSCON_FCLKSEL5); |
| } |
| else |
| #endif |
| #ifdef CONFIG_LPC54_SPI6_MASTER |
| if (port == 6) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM6 */ |
| |
| lpc54_flexcomm6_enableclk(); |
| |
| /* Set FLEXCOMM6 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM6_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi6_dev; |
| priv->base = LPC54_FLEXCOMM6_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM6; |
| priv->fclock = BOARD_FLEXCOMM6_FCLK; |
| priv->dev.ops = &g_spi6_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI6_SCK); |
| lpc54_gpio_config(GPIO_SPI6_MOSI); |
| lpc54_gpio_config(GPIO_SPI6_MISO); |
| |
| /* Set up the FLEXCOMM6 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM6_CLKSEL, LPC54_SYSCON_FCLKSEL6); |
| } |
| else |
| #endif |
| #ifdef CONFIG_LPC54_SPI7_MASTER |
| if (port == 7) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM7 */ |
| |
| lpc54_flexcomm7_enableclk(); |
| |
| /* Set FLEXCOMM7 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM7_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi7_dev; |
| priv->base = LPC54_FLEXCOMM7_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM7; |
| priv->fclock = BOARD_FLEXCOMM7_FCLK; |
| priv->dev.ops = &g_spi7_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI7_SCK); |
| lpc54_gpio_config(GPIO_SPI7_MOSI); |
| lpc54_gpio_config(GPIO_SPI7_MISO); |
| |
| /* Set up the FLEXCOMM7 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM7_CLKSEL, LPC54_SYSCON_FCLKSEL7); |
| } |
| else |
| #endif |
| #ifdef CONFIG_LPC54_SPI8_MASTER |
| if (port == 8) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM8 */ |
| |
| lpc54_flexcomm8_enableclk(); |
| |
| /* Set FLEXCOMM8 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM8_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi8_dev; |
| priv->base = LPC54_FLEXCOMM8_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM8; |
| priv->fclock = BOARD_FLEXCOMM8_FCLK; |
| priv->dev.ops = &g_spi8_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI8_SCK); |
| lpc54_gpio_config(GPIO_SPI8_MOSI); |
| lpc54_gpio_config(GPIO_SPI8_MISO); |
| |
| /* Set up the FLEXCOMM8 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM8_CLKSEL, LPC54_SYSCON_FCLKSEL8); |
| } |
| else |
| #endif |
| #ifdef CONFIG_LPC54_SPI9_MASTER |
| if (port == 9) |
| { |
| /* Attach 12 MHz clock to FLEXCOMM9 */ |
| |
| lpc54_flexcomm9_enableclk(); |
| |
| /* Set FLEXCOMM9 to the SPI peripheral, locking that configuration |
| * in place. |
| */ |
| |
| putreg32(FLEXCOMM_PSELID_PERSEL_SPI | FLEXCOMM_PSELID_LOCK, |
| LPC54_FLEXCOMM9_PSELID); |
| |
| /* Initialize the state structure */ |
| |
| priv = &g_spi9_dev; |
| priv->base = LPC54_FLEXCOMM9_BASE; |
| priv->irq = LPC54_IRQ_FLEXCOMM9; |
| priv->fclock = BOARD_FLEXCOMM9_FCLK; |
| priv->dev.ops = &g_spi9_ops; |
| |
| /* Configure SPI pins (defined in board.h) */ |
| |
| lpc54_gpio_config(GPIO_SPI9_SCK); |
| lpc54_gpio_config(GPIO_SPI9_MOSI); |
| lpc54_gpio_config(GPIO_SPI9_MISO); |
| |
| /* Set up the FLEXCOMM9 function clock */ |
| |
| putreg32(BOARD_FLEXCOMM9_CLKSEL, LPC54_SYSCON_FCLKSEL9); |
| } |
| else |
| #endif |
| { |
| leave_critical_section(flags); |
| return NULL; |
| } |
| |
| leave_critical_section(flags); |
| |
| /* Set the initial SPI configuration */ |
| |
| priv->frequency = 0; |
| priv->nbits = 8; |
| priv->mode = SPIDEV_MODE0; |
| |
| /* Configure master mode in mode 0: |
| * |
| * ENABLE - Disabled for now (0) |
| * MASTER - Master mode (1) |
| * LSBF - MSB first (0) |
| * CPHA/CPOL - Mode 0 (0,0) |
| * LOOP - Disable loopback mode (0) |
| * SPOLn - Active low (0,0,0) |
| */ |
| |
| regval = lpc54_spi_getreg(priv, LPC54_SPI_CFG_OFFSET); |
| regval &= ~(SPI_CFG_ENABLE | SPI_CFG_LSBF | SPI_CFG_CPHA | SPI_CFG_CPOL | |
| SPI_CFG_LOOP | SPI_CFG_SPOL0 | SPI_CFG_SPOL1 | SPI_CFG_SPOL2 | |
| SPI_CFG_SPOL3); |
| regval |= SPI_CFG_MASTER; |
| lpc54_spi_putreg(priv, LPC54_SPI_CFG_OFFSET, regval); |
| |
| /* Enable FIFOs */ |
| |
| regval = lpc54_spi_getreg(priv, LPC54_SPI_FIFOCFG_OFFSET); |
| regval |= (SPI_FIFOCFG_EMPTYTX | SPI_FIFOCFG_EMPTYRX); |
| lpc54_spi_putreg(priv, LPC54_SPI_FIFOCFG_OFFSET, regval); |
| |
| regval |= (SPI_FIFOCFG_ENABLETX | SPI_FIFOCFG_ENABLERX); |
| lpc54_spi_putreg(priv, LPC54_SPI_FIFOCFG_OFFSET, regval); |
| |
| /* Set FIFO trigger levels: Empty for Tx FIFO; 1 word for RxFIFO */ |
| |
| regval = lpc54_spi_getreg(priv, LPC54_SPI_FIFOCFG_OFFSET); |
| regval &= ~(SPI_FIFOTRIG_TXLVL_MASK | SPI_FIFOTRIG_RXLVL_MASK); |
| regval |= (SPI_FIFOTRIG_TXLVL_EMPTY | SPI_FIFOTRIG_RXLVL_NOTEMPTY); |
| |
| /* Enable generation of interrupts for selected FIFO trigger levels */ |
| |
| regval |= (SPI_FIFOTRIG_TXLVLENA | SPI_FIFOTRIG_RXLVLENA); |
| lpc54_spi_putreg(priv, LPC54_SPI_FIFOCFG_OFFSET, regval); |
| |
| /* Set the delay configuration (not used) */ |
| |
| regval = (SPI_DLY_PRE_DELAY(0) | SPI_DLY_POST_DELAY(0) | |
| SPI_DLY_FRAME_DELAY(0) | SPI_DLY_TRANSFER_DELAY(0)); |
| lpc54_spi_putreg(priv, LPC54_SPI_DLY_OFFSET, regval); |
| |
| /* Select a default frequency of approx. 400KHz */ |
| |
| lpc54_spi_setfrequency((struct spi_dev_s *)priv, 400000); |
| |
| /* Enable the SPI peripheral */ |
| |
| regval = lpc54_spi_getreg(priv, LPC54_SPI_CFG_OFFSET); |
| regval |= SPI_CFG_ENABLE; |
| lpc54_spi_putreg(priv, LPC54_SPI_CFG_OFFSET, regval); |
| |
| return &priv->dev; |
| } |
| |
| #endif /* HAVE_SPI_MASTER_DEVICE */ |