| /**************************************************************************** |
| * arch/renesas/src/rx65n/rx65n_rspi.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 <sys/types.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <semaphore.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/arch.h> |
| #include <nuttx/mutex.h> |
| #include <nuttx/spi/spi.h> |
| |
| #include <arch/board/board.h> |
| |
| #include "renesas_internal.h" |
| #include "chip.h" |
| #include "rx65n_definitions.h" |
| #include "rx65n_rspi.h" |
| #include "rx65n_dtc.h" |
| |
| #include <arch/board/board.h> |
| #include <arch/board/rx65n_gpio.h> |
| |
| #if defined(CONFIG_RX65N_RSPI0) || defined(CONFIG_RX65N_RSPI1) || defined(CONFIG_RX65N_RSPI2) |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Default bit rate */ |
| |
| #define RSPI_BRDV_DEFAULT 0 |
| |
| /* The number of words that will fit in the Tx FIFO */ |
| |
| #define RX65N_TXFIFO_WORDS BUFSIZE_4FRAME |
| |
| /* Interrupt priority */ |
| |
| #define RX65N_RSPI_INTRRUPT_PRIO 15 |
| |
| /* RSPI power on and off */ |
| |
| #define RSPI_POWER_ON 0 |
| #define RSPI_POWER_OFF 1 |
| |
| /* RSPI Register protection enable and disable */ |
| |
| #define REG_PROTECTION_ON 1 |
| #define REG_PROTECTION_OFF 0 |
| |
| /* Frequency divisor */ |
| |
| enum FREQ_DIVISOR |
| { |
| FREQ_DIVISOR_2 = 2, |
| FREQ_DIVISOR_4 = 4, |
| FREQ_DIVISOR_6 = 6, |
| FREQ_DIVISOR_8 = 8, |
| FREQ_DIVISOR_10 = 10, |
| FREQ_DIVISOR_12 = 12, |
| FREQ_DIVISOR_24 = 24, |
| FREQ_DIVISOR_48 = 48, |
| FREQ_DIVISOR_96 = 96, |
| }; |
| |
| /* The number of words that will fit in the buffer */ |
| |
| enum BUF_SIZE |
| { |
| BUFSIZE_1FRAME = 1, |
| BUFSIZE_2FRAME = 2, |
| BUFSIZE_3FRAME = 3, |
| BUFSIZE_4FRAME = 4, |
| }; |
| |
| /* RSPI events */ |
| |
| typedef enum |
| { |
| RSPI_EVT_TRANSFER_COMPLETE, /* The data transfer completed */ |
| RSPI_EVT_TRANSFER_ABORTED, /* The data transfer was aborted */ |
| RSPI_EVT_ERR_MODE_FAULT, /* Mode fault error */ |
| RSPI_EVT_ERR_READ_OVF, /* Read overflow */ |
| RSPI_EVT_ERR_PARITY, /* Parity error */ |
| RSPI_EVT_ERR_UNDER_RUN, /* Under run error */ |
| RSPI_EVT_ERR_UNDEF /* Undefined/unknown error event */ |
| } rspi_event_t; |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct rx65n_rspidev_s |
| { |
| struct spi_dev_s rspidev; /* Externally visible part of the RSPI interface */ |
| uint32_t rspibase; /* RSPIn base address */ |
| uint32_t rspiclock; /* Clocking for the RSPI module */ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| uint8_t rspitxirq; /* RSPI tx IRQ number */ |
| uint8_t rspirxirq; /* RSPI rx IRQ number */ |
| uint8_t rspierirq; /* RSPI error IRQ number */ |
| uint8_t rspiidlirq; /* RSPI idle IRQ number */ |
| uint32_t rspigrpbase; |
| uint32_t rspierimask; |
| uint32_t rspiidlimask; |
| sem_t waitsem; /* Wait for transfer to complete */ |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI_SW_DT_MODE |
| /* These following are the source and destination buffers of the transfer. |
| * they are retained in this structure so that they will be accessible |
| * from an interrupt handler. The actual type of the buffer is uint8_t is |
| * nbits <=8 and uint16_t is nbits >8. |
| */ |
| |
| void *txbuffer; /* Source buffer */ |
| void *rxbuffer; /* Destination buffer */ |
| |
| /* These are functions pointers that are configured to perform the |
| * appropriate transfer for the particular kind of exchange that is |
| * occurring. Different functions may be selected depending on (1) |
| * if the tx or txbuffer is NULL and depending on the number of bits |
| * per word. |
| */ |
| |
| void (*txword)(struct rx65n_rspidev_s *priv); |
| void (*rxword)(struct rx65n_rspidev_s *priv); |
| #else |
| DTC_HANDLE dtchandle; /* DTC channel handle for data transfers */ |
| dtc_transfer_data_t *p_txdt; /* Pointer Tx data transfer */ |
| dtc_transfer_data_t *p_rxdt; /* Pointer Rx data transfer */ |
| sem_t txsem; |
| sem_t rxsem; |
| uint8_t txvec; /* DTC activation source of RSPI Tx */ |
| uint8_t rxvec; /* DTC activation source of RSPI Rx */ |
| #endif |
| |
| bool initialized; /* Has RSPI interface been initialized */ |
| mutex_t lock; /* Held while chip is selected for mutual exclusion */ |
| uint32_t frequency; /* Requested clock frequency */ |
| uint32_t actual; /* Actual clock frequency */ |
| |
| int ntxwords; /* Number of words left to transfer on the Tx Buffer */ |
| int nrxwords; /* Number of words received on the Rx Buffer */ |
| int nwords; /* Number of words to be exchanged */ |
| |
| uint8_t nbits; /* Width of word in bits (4 through 16) */ |
| uint8_t mode; /* Mode 0,1,2,3 */ |
| uint8_t bufsize; /* Buf size: 1,2,3 or 4 word */ |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /* Helpers */ |
| |
| static inline uint32_t rspi_getreg32(struct rx65n_rspidev_s *priv, |
| uint8_t offset); |
| static inline uint16_t rspi_getreg16(struct rx65n_rspidev_s *priv, |
| uint8_t offset); |
| static inline uint8_t rspi_getreg8(struct rx65n_rspidev_s *priv, |
| uint8_t offset); |
| static inline void rspi_putreg32(struct rx65n_rspidev_s *priv, |
| uint8_t offset, uint32_t value); |
| static inline void rspi_putreg16(struct rx65n_rspidev_s *priv, |
| uint8_t offset, uint16_t value); |
| static inline void rspi_putreg8(struct rx65n_rspidev_s *priv, |
| uint8_t offset, uint8_t value); |
| |
| /* SPI data transfer */ |
| |
| #if defined(CONFIG_RX65N_RSPI_SW_DT_MODE) |
| static void rspi_txnull(struct rx65n_rspidev_s *priv); |
| static void rspi_txuint32(struct rx65n_rspidev_s *priv); |
| static void rspi_txuint16(struct rx65n_rspidev_s *priv); |
| static void rspi_txuint8(struct rx65n_rspidev_s *priv); |
| static void rspi_rxnull(struct rx65n_rspidev_s *priv); |
| static void rspi_rxuint32(struct rx65n_rspidev_s *priv); |
| static void rspi_rxuint16(struct rx65n_rspidev_s *priv); |
| static void rspi_rxuint8(struct rx65n_rspidev_s *priv); |
| static void rspi_performtx(struct rx65n_rspidev_s *priv); |
| static inline void rspi_performrx(struct rx65n_rspidev_s *priv); |
| #endif |
| static int rspi_transfer(struct rx65n_rspidev_s *priv, const void *txbuffer, |
| void *rxbuffer, unsigned int nwords); |
| |
| /* Interrupt handling */ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| static inline struct rx65n_rspidev_s *rspi_mapirq(int irq); |
| static int rspi_idlinterrupt(int irq, void *context, void *arg); |
| static int rspi_erinterrupt(int irq, void *context, void *arg); |
| static int rspi_txinterrupt(int irq, void *context, void *arg); |
| static int rspi_rxinterrupt(int irq, void *context, void *arg); |
| #endif |
| |
| /* SPI methods */ |
| |
| static int rspi_lock(struct spi_dev_s *dev, bool lock); |
| static uint32_t rspi_setfrequency(struct spi_dev_s *dev, |
| uint32_t frequency); |
| static void rspi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode); |
| static void rspi_setbits(struct spi_dev_s *dev, int nbits); |
| #ifdef CONFIG_SPI_HWFEATURES |
| static int rspi_hwfeatures(struct spi_dev_s *dev, |
| spi_hwfeatures_t features); |
| #endif |
| static uint32_t rspi_send(struct spi_dev_s *dev, uint32_t wd); |
| static void rspi_exchange(struct spi_dev_s *dev, const void *txbuffer, |
| void *rxbuffer, size_t nwords); |
| #ifdef CONFIG_SPI_TRIGGER |
| static int rspi_trigger(struct spi_dev_s *dev); |
| #endif |
| #ifndef CONFIG_SPI_EXCHANGE |
| static void rspi_sndblock(struct spi_dev_s *dev, const void *txbuffer, |
| size_t nwords); |
| static void rspi_recvblock(struct spi_dev_s *dev, void *rxbuffer, |
| size_t nwords); |
| #endif |
| |
| /* Initialization */ |
| |
| static void rspi_bus_initialize(struct rx65n_rspidev_s *priv); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RX65N_RSPI0 |
| #if defined(CONFIG_RX65N_RSPI_DTC_DT_MODE) |
| dtc_transfer_data_t g_tx0dt; |
| dtc_transfer_data_t g_rx0dt; |
| #endif |
| static const struct spi_ops_s g_rspi0ops = |
| { |
| .lock = rspi_lock, |
| .select = rx65n_rspi0select, |
| .setfrequency = rspi_setfrequency, |
| .setmode = rspi_setmode, |
| .setbits = rspi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = rspi_hwfeatures, |
| #endif |
| .status = rx65n_rspi0status, |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = rx65n_rspi0cmddata, |
| #endif |
| .send = rspi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = rspi_exchange, |
| #else |
| .sndblock = rspi_sndblock, |
| .recvblock = rspi_recvblock, |
| #endif |
| |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = rx65n_rspi0register, /* Provided externally */ |
| #else |
| .registercallback = 0, |
| #endif |
| }; |
| |
| static struct rx65n_rspidev_s g_rspi0dev = |
| { |
| .rspidev = |
| { |
| .ops = &g_rspi0ops, |
| }, |
| .rspibase = RX65N_RSPI0_BASE, |
| .rspiclock = RX65N_PCLK_FREQUENCY, |
| #ifndef CONFIG_SPI_POLLWAIT |
| .rspitxirq = RX65N_SPTI0_IRQ, |
| .rspirxirq = RX65N_SPRI0_IRQ, |
| .rspierirq = RX65N_SPEI0_IRQ, |
| .rspiidlirq = RX65N_SPII0_IRQ, |
| .rspigrpbase = RX65N_GRPAL0_ADDR, |
| .rspierimask = RX65N_GRPAL0_SPEI0_MASK, |
| .rspiidlimask = RX65N_GRPAL0_SPII0_MASK, |
| .waitsem = SEM_INITIALIZER(0), |
| #endif |
| #if defined(CONFIG_RX65N_RSPI_DTC_DT_MODE) |
| .p_txdt = &g_tx0dt, |
| .p_rxdt = &g_rx0dt, |
| .txsem = SEM_INITIALIZER(0), |
| .rxsem = SEM_INITIALIZER(0), |
| .txvec = RX65N_RSPI0_TXVECT, |
| .rxvec = RX65N_RSPI0_RXVECT, |
| #endif |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI1 |
| #if defined(CONFIG_RX65N_RSPI_DTC_DT_MODE) |
| dtc_transfer_data_t g_tx1dt; |
| dtc_transfer_data_t g_rx1dt; |
| #endif |
| static const struct spi_ops_s g_rspi1ops = |
| { |
| .lock = rspi_lock, |
| .select = rx65n_rspi1select, |
| .setfrequency = rspi_setfrequency, |
| .setmode = rspi_setmode, |
| .setbits = rspi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = rspi_hwfeatures, |
| #endif |
| .status = rx65n_rspi1status, |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = rx65n_rspi1cmddata, |
| #endif |
| .send = rspi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = rspi_exchange, |
| #else |
| .sndblock = rspi_sndblock, |
| .recvblock = rspi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_TRIGGER |
| .trigger = rspi_trigger, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = rx65n_rspi1register, /* Provided externally */ |
| #else |
| .registercallback = 0, |
| #endif |
| }; |
| |
| static struct rx65n_rspidev_s g_rspi1dev = |
| { |
| .rspidev = |
| { |
| .ops = &g_rspi1ops, |
| }, |
| .rspibase = RX65N_RSPI1_BASE, |
| .rspiclock = RX65N_PCLK_FREQUENCY, |
| #ifndef CONFIG_SPI_POLLWAIT |
| .rspitxirq = RX65N_SPTI1_IRQ, |
| .rspirxirq = RX65N_SPRI1_IRQ, |
| .rspierirq = RX65N_SPEI1_IRQ, |
| .rspiidlirq = RX65N_SPII1_IRQ, |
| .rspigrpbase = RX65N_GRPAL0_ADDR, |
| .rspierimask = RX65N_GRPAL0_SPEI1_MASK, |
| .rspiidlimask = RX65N_GRPAL0_SPII1_MASK, |
| .waitsem = SEM_INITIALIZER(0), |
| #endif |
| #if defined(CONFIG_RX65N_RSPI_DTC_DT_MODE) |
| .p_txdt = &g_tx1dt, |
| .p_rxdt = &g_rx1dt, |
| .txsem = SEM_INITIALIZER(0), |
| .rxsem = SEM_INITIALIZER(0), |
| .txvec = RX65N_RSPI1_TXVECT, |
| .rxvec = RX65N_RSPI1_RXVECT, |
| #endif |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI2 |
| #if defined(CONFIG_RX65N_RSPI_DTC_DT_MODE) |
| dtc_transfer_data_t g_tx2dt; |
| dtc_transfer_data_t g_rx2dt; |
| #endif |
| static const struct spi_ops_s g_rspi2ops = |
| { |
| .lock = rspi_lock, |
| .select = rx65n_rspi2select, |
| .setfrequency = rspi_setfrequency, |
| .setmode = rspi_setmode, |
| .setbits = rspi_setbits, |
| #ifdef CONFIG_SPI_HWFEATURES |
| .hwfeatures = rspi_hwfeatures, |
| #endif |
| .status = rx65n_rspi2status, |
| #ifdef CONFIG_SPI_CMDDATA |
| .cmddata = rx65n_rspi2cmddata, |
| #endif |
| .send = rspi_send, |
| #ifdef CONFIG_SPI_EXCHANGE |
| .exchange = rspi_exchange, |
| #else |
| .sndblock = rspi_sndblock, |
| .recvblock = rspi_recvblock, |
| #endif |
| #ifdef CONFIG_SPI_TRIGGER |
| .trigger = rspi_trigger, |
| #endif |
| #ifdef CONFIG_SPI_CALLBACK |
| .registercallback = rx65n_rspi2register, /* Provided externally */ |
| #else |
| .registercallback = 0, |
| #endif |
| }; |
| |
| static struct rx65n_rspidev_s g_rspi2dev = |
| { |
| .rspidev = |
| { |
| .ops = &g_rspi2ops, |
| }, |
| .rspibase = RX65N_RSPI2_BASE, |
| .rspiclock = RX65N_PCLK_FREQUENCY, |
| #ifndef CONFIG_SPI_POLLWAIT |
| .rspitxirq = RX65N_SPTI2_IRQ, |
| .rspirxirq = RX65N_SPRI2_IRQ, |
| .rspierirq = RX65N_SPEI2_IRQ, |
| .rspiidlirq = RX65N_SPII2_IRQ, |
| .rspigrpbase = RX65N_GRPAL0_ADDR, |
| .rspierimask = RX65N_GRPAL0_SPEI2_MASK, |
| .rspiidlimask = RX65N_GRPAL0_SPII2_MASK, |
| .waitsem = SEM_INITIALIZER(0), |
| #endif |
| #if defined(CONFIG_RX65N_RSPI_DTC_DT_MODE) |
| .p_txdt = &g_tx2dt, |
| .p_rxdt = &g_rx2dt, |
| .txsem = SEM_INITIALIZER(0), |
| .rxsem = SEM_INITIALIZER(0), |
| .txvec = RX65N_RSPI2_TXVECT, |
| .rxvec = RX65N_RSPI2_RXVECT, |
| #endif |
| .lock = NXMUTEX_INITIALIZER, |
| }; |
| #endif |
| |
| #if defined(CONFIG_RX65N_RSPI_DTC_DT_MODE) |
| dtc_static_transfer_data_cfg_t tx_cfg = |
| { |
| #if CONFIG_RX65N_RSPI_BUF_SIZE > 1 |
| .transfer_mode = DTC_TRANSFER_MODE_BLOCK, |
| #else |
| .transfer_mode = DTC_TRANSFER_MODE_NORMAL, |
| #endif |
| .data_size = DTC_DATA_SIZE_BYTE, |
| .src_addr_mode = DTC_SRC_ADDR_INCR, |
| .chain_transfer_enable = DTC_CHAIN_TRANSFER_DISABLE, |
| .chain_transfer_mode = DTC_CHAIN_TRANSFER_CONTINUOUSLY, |
| .response_interrupt = DTC_INTERRUPT_AFTER_ALL_COMPLETE, |
| .repeat_block_side = DTC_REPEAT_BLOCK_DESTINATION, |
| .dest_addr_mode = DTC_DES_ADDR_FIXED, |
| .source_addr = 0, /* This will set dynamically */ |
| .dest_addr = 0, /* Set data register address */ |
| .transfer_count = 0, /* This will set dynamically */ |
| #if CONFIG_RX65N_RSPI_BUF_SIZE > 1 |
| .block_size = CONFIG_RX65N_RSPI_BUF_SIZE, /* Looks like tx fifo size */ |
| #else |
| .block_size = 0, |
| #endif |
| .rsv = 0, |
| .writeback_disable = DTC_WRITEBACK_ENABLE, |
| .sequence_end = DTC_SEQUENCE_TRANSFER_CONTINUE, |
| .refer_index_table_enable = DTC_REFER_INDEX_TABLE_DISABLE, |
| .disp_add_enable = DTC_SRC_ADDR_DISP_ADD_DISABLE, |
| }; |
| |
| dtc_static_transfer_data_cfg_t rx_cfg = |
| { |
| #if CONFIG_RX65N_RSPI_BUF_SIZE > 1 |
| .transfer_mode = DTC_TRANSFER_MODE_BLOCK, |
| #else |
| .transfer_mode = DTC_TRANSFER_MODE_NORMAL, |
| #endif |
| .data_size = DTC_DATA_SIZE_BYTE, |
| .src_addr_mode = DTC_SRC_ADDR_FIXED, |
| .chain_transfer_enable = DTC_CHAIN_TRANSFER_DISABLE, |
| .chain_transfer_mode = DTC_CHAIN_TRANSFER_CONTINUOUSLY, |
| .response_interrupt = DTC_INTERRUPT_AFTER_ALL_COMPLETE, |
| .repeat_block_side = DTC_REPEAT_BLOCK_SOURCE, |
| .dest_addr_mode = DTC_DES_ADDR_INCR, |
| .source_addr = 0, /* Set data register address */ |
| .dest_addr = 0, /* This will set dynamically */ |
| .transfer_count = 0, /* This will set dynamically */ |
| #if CONFIG_RX65N_RSPI_BUF_SIZE > 1 |
| .block_size = CONFIG_RX65N_RSPI_BUF_SIZE, /* Looks like tx fifo size */ |
| #else |
| .block_size = 0, |
| #endif |
| .rsv = 0, |
| .writeback_disable = DTC_WRITEBACK_ENABLE, |
| .sequence_end = DTC_SEQUENCE_TRANSFER_CONTINUE, |
| .refer_index_table_enable = DTC_REFER_INDEX_TABLE_DISABLE, |
| .disp_add_enable = DTC_SRC_ADDR_DISP_ADD_DISABLE, |
| }; |
| #endif |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: rx65n_rspi0/1/2 select and rx65n_rspi0/1/2 status |
| * |
| * Description: |
| * The external functions, rx65n_rspi0/1/2 select, rx65n_rspi0/1/2 status,and |
| * rx65n_rspi0/1/2 cmddata must be provided by board-specific logic. These |
| * 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 rx65n_rspibus_initialize()) are provided by |
| * common R656N logic. To use thiscommon SPI logic on your board: |
| * |
| * 1. Provide logic in stm32_boardinitialize() to configure SPI chip select |
| * pins. |
| * 2. Provide stm32_spi1/2/...select() and stm32_spi1/2/...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. |
| * 3. If CONFIG_SPI_CMDDATA is defined in your NuttX configuration file, |
| * then provide stm32_spi1/2/...cmddata() functions in your |
| * board-specific logic.These functions will perform cmd/data |
| * selection operations using GPIOs in the way your board is configured. |
| * 4. Add a calls to stm32_spibus_initialize() in your low level |
| * application initialization logic |
| * 5. The handle returned by stm32_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_RX65N_RSPI0 |
| void rx65n_rspi0select(struct spi_dev_s *dev, uint32_t devid, |
| bool selected) |
| { |
| spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : |
| "de-assert"); |
| } |
| |
| uint8_t rx65n_rspi0status(struct spi_dev_s *dev, uint32_t devid) |
| { |
| return 0; |
| } |
| |
| int rx65n_rspi0cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd) |
| { |
| return -ENODEV; |
| } |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI1 |
| void rx65n_rspi1select(struct spi_dev_s *dev, uint32_t devid, |
| bool selected) |
| { |
| spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : |
| "de-assert"); |
| } |
| |
| uint8_t rx65n_rspi1status(struct spi_dev_s *dev, uint32_t devid) |
| { |
| return 0; |
| } |
| |
| int rx65n_rspi1cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd) |
| { |
| return -ENODEV; |
| } |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI2 |
| void rx65n_rspi2select(struct spi_dev_s *dev, uint32_t devid, |
| bool selected) |
| { |
| spiinfo("devid: %d CS: %s\n", (int)devid, selected ? "assert" : |
| "de-assert"); |
| } |
| |
| uint8_t rx65n_rspi2status(struct spi_dev_s *dev, uint32_t devid) |
| { |
| return 0; |
| } |
| |
| int rx65n_rspi2cmddata(struct spi_dev_s *dev, uint32_t devid, bool cmd) |
| { |
| return -ENODEV; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rx65n_rspi0/1/2register |
| * |
| * Description: |
| * If the board supports a card detect callback to inform the SPI-based |
| * MMC/SD driver when an SD card is inserted or removed, then |
| * CONFIG_SPI_CALLBACK should be defined and the following function(s) |
| * must be implemented. These functions implements the registercallback |
| * method of the SPI interface (see include/nuttx/spi/spi.h for details) |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * callback - The function to call on the media change |
| * arg - A caller provided value to return with the callback |
| * |
| * Returned Value: |
| * 0 on success; negated errno on failure. |
| * |
| ****************************************************************************/ |
| #ifdef CONFIG_SPI_CALLBACK |
| #ifdef CONFIG_RX65N_RSPI0 |
| int rx65n_rspi0register(struct spi_dev_s *dev, spi_mediachange_t callback, |
| void *arg) |
| { |
| spiinfo("INFO: Registering rspi0 device\n"); |
| return OK; |
| } |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI1 |
| int rx65n_rspi1register(struct spi_dev_s *dev, spi_mediachange_t callback, |
| void *arg) |
| { |
| spiinfo("INFO: Registering rspi1 device\n"); |
| return OK; |
| } |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI2 |
| int rx65n_rspi2register(struct spi_dev_s *dev, spi_mediachange_t callback, |
| void *arg) |
| { |
| spiinfo("INFO: Registering rspi2 device\n"); |
| return OK; |
| } |
| #endif |
| #endif |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: rspi_getreg32 |
| * |
| * Description: |
| * Read the RSPI register at this offeset |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * offset - Offset to the SPI register from the register base address |
| * |
| * Returned Value: |
| * Value of the register at this offset |
| * |
| ****************************************************************************/ |
| |
| static inline uint32_t rspi_getreg32(struct rx65n_rspidev_s *priv, |
| uint8_t offset) |
| { |
| return getreg32(priv->rspibase + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_getreg16 |
| * |
| * Description: |
| * Read the RSPI register at this offeset |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * offset - Offset to the SPI register from the register base address |
| * |
| * Returned Value: |
| * Value of the register at this offset |
| * |
| ****************************************************************************/ |
| |
| static inline uint16_t rspi_getreg16(struct rx65n_rspidev_s *priv, |
| uint8_t offset) |
| { |
| return getreg16(priv->rspibase + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_getreg8 |
| * |
| * Description: |
| * Read the RSPI register at this offeset |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * offset - Offset to the SPI register from the register base address |
| * |
| * Returned Value: |
| * Value of the register at this offset |
| * |
| ****************************************************************************/ |
| |
| static inline uint8_t rspi_getreg8(struct rx65n_rspidev_s *priv, |
| uint8_t offset) |
| { |
| return getreg8(priv->rspibase + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_putreg32 |
| * |
| * Description: |
| * Write the value to the RSPI register at this offeset |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * offset - Offset to the SPI register from the register base address |
| * value - Value to write |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void rspi_putreg32(struct rx65n_rspidev_s *priv, |
| uint8_t offset, uint32_t value) |
| { |
| putreg32(value, priv->rspibase + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_putreg16 |
| * |
| * Description: |
| * Write the value to the RSPI register at this offeset |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * offset - Offset to the SPI register from the register base address |
| * value - Value to write |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void rspi_putreg16(struct rx65n_rspidev_s *priv, |
| uint8_t offset, uint16_t value) |
| { |
| putreg16(value, priv->rspibase + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_putreg8 |
| * |
| * Description: |
| * Write the value to the RSPI register at this offeset |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * offset - Offset to the SPI register from the register base address |
| * value - Value to write |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline void rspi_putreg8(struct rx65n_rspidev_s *priv, |
| uint8_t offset, uint8_t value) |
| { |
| putreg8(value, priv->rspibase + offset); |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_txnull, rspi_txuint16, and rspi_txuint8 |
| * |
| * Description: |
| * Transfer all ones, a uint8_t, or uint16_t to Tx FIFO and update |
| * the txbuffer pointer appropriately. The selected function depends |
| * on (1) if there is a source txbuffer provided, and (2) if the |
| * number of bits per word is <=8 or >8. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_RX65N_RSPI_SW_DT_MODE) |
| static void rspi_txnull(struct rx65n_rspidev_s *priv) |
| { |
| rspi_putreg32(priv, RX65N_RSPI_SPDR_OFFSET, 0xffff); |
| } |
| |
| static void rspi_txuint32(struct rx65n_rspidev_s *priv) |
| { |
| uint32_t *ptr = (uint32_t *)priv->txbuffer; |
| rspi_putreg32(priv, RX65N_RSPI_SPDR_OFFSET, *ptr++); |
| priv->txbuffer = (void *)ptr; |
| } |
| |
| static void rspi_txuint16(struct rx65n_rspidev_s *priv) |
| { |
| uint16_t *ptr = (uint16_t *)priv->txbuffer; |
| rspi_putreg16(priv, RX65N_RSPI_SPDR_OFFSET, *ptr++); |
| priv->txbuffer = (void *)ptr; |
| } |
| |
| static void rspi_txuint8(struct rx65n_rspidev_s *priv) |
| { |
| uint8_t *ptr = (uint8_t *)priv->txbuffer; |
| rspi_putreg8(priv, RX65N_RSPI_SPDR_OFFSET, *ptr++); |
| priv->txbuffer = (void *)ptr; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_rxnull,rspi_rxuint16, and rspi_rxuint8 |
| * |
| * Description: |
| * Discard input, save a uint8_t, or or save a uint16_t from Tx FIFO in the |
| * user rxvbuffer and update the rxbuffer pointer appropriately. The |
| * selected function dependes on (1) if there is a destination rxbuffer |
| * provided, and (2) if the number of bits per word is <=8 or >8. |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_RX65N_RSPI_SW_DT_MODE) |
| static void rspi_rxnull(struct rx65n_rspidev_s *priv) |
| { |
| rspi_getreg32(priv, RX65N_RSPI_SPDR_OFFSET); |
| } |
| |
| static void rspi_rxuint32(struct rx65n_rspidev_s *priv) |
| { |
| uint32_t *ptr = (uint32_t *)priv->rxbuffer; |
| *ptr++ = (uint32_t)rspi_getreg32(priv, RX65N_RSPI_SPDR_OFFSET); |
| priv->rxbuffer = (void *)ptr; |
| } |
| |
| static void rspi_rxuint16(struct rx65n_rspidev_s *priv) |
| { |
| uint16_t *ptr = (uint16_t *)priv->rxbuffer; |
| *ptr++ = (uint16_t)rspi_getreg16(priv, RX65N_RSPI_SPDR_OFFSET); |
| priv->rxbuffer = (void *)ptr; |
| } |
| |
| static void rspi_rxuint8(struct rx65n_rspidev_s *priv) |
| { |
| uint8_t *ptr = (uint8_t *)priv->rxbuffer; |
| *ptr++ = (uint8_t)rspi_getreg8(priv, RX65N_RSPI_SPDR_OFFSET); |
| priv->rxbuffer = (void *)ptr; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_performtx |
| * |
| * Description: |
| * If the Tx FIFO is empty, then transfer as many words as we can to |
| * the FIFO. |
| * |
| * Input Parameters: |
| * |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_RX65N_RSPI_SW_DT_MODE) |
| static void rspi_performtx(struct rx65n_rspidev_s *priv) |
| { |
| uint8_t regval8; |
| int ntxd = 0; /* Number of words written to Tx FIFO */ |
| |
| /* Check if the Tx FIFO is empty */ |
| |
| if ((rspi_getreg8(priv, RX65N_RSPI_SPSR_OFFSET) & RSPI_SPSR_SPTEF) != 0) |
| { |
| /* Check if all of the Tx words have been sent */ |
| |
| if (priv->ntxwords > 0) |
| { |
| /* No.. |
| * Transfer more words until either the TxFIFO is full or |
| * until all of the user provided data has been sent. |
| */ |
| |
| for (; ntxd < priv->ntxwords && ntxd < priv->bufsize; ntxd++) |
| { |
| priv->txword(priv); |
| } |
| |
| /* Update the count of words to be transferred */ |
| |
| priv->ntxwords -= ntxd; |
| |
| /* Add dummy data in FIFO to make it full */ |
| |
| if (ntxd < priv->bufsize) |
| { |
| while (ntxd != priv->bufsize) |
| { |
| rspi_txnull(priv); |
| ntxd++; |
| } |
| } |
| } |
| else |
| { |
| /* Yes.. The transfer is complete, |
| * disable Tx FIFO empty interrupt |
| */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 &= ~RSPI_SPCR_SPTIE; |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| } |
| } |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_performrx |
| * |
| * Description: |
| * Transfer as many bytes as possible from the Rx FIFO to the user Rx |
| * buffer (if one was provided). |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| #if defined(CONFIG_RX65N_RSPI_SW_DT_MODE) |
| static inline void rspi_performrx(struct rx65n_rspidev_s *priv) |
| { |
| /* Loop while data is available in the Rx FIFO (SPRXn) */ |
| |
| while ((rspi_getreg8(priv, RX65N_RSPI_SPSR_OFFSET) & RSPI_SPSR_SPRF) != 0) |
| { |
| /* Have all of the requested words been transferred from the Rx FIFO? */ |
| |
| if (priv->nrxwords < priv->nwords) |
| { |
| /* No.. Read more data from Rx FIFO */ |
| |
| priv->rxword(priv); |
| priv->nrxwords++; |
| } |
| else |
| { |
| rspi_rxnull(priv); /* Reading dummy data send */ |
| } |
| } |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_startxfr |
| * |
| * Description: |
| * If data was added to the Tx FIFO, then start the exchange |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void rspi_startxfr(struct rx65n_rspidev_s *priv) |
| { |
| uint8_t regval8; |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 |= RSPI_SPCR_SPE; |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| } |
| |
| /**************************************************************************** |
| * Name: spi_dtcrxsetup |
| * |
| * Description: |
| * Setup to perform RX DTC |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_RX65N_RSPI_DTC_DT_MODE |
| static dtc_err_t rspi_dtctxsetup(struct rx65n_rspidev_s *priv, |
| const void *txbuffer, |
| const void *txdummy, size_t nwords) |
| { |
| dtc_err_t ret = DTC_SUCCESS; |
| dtc_dynamic_transfer_data_cfg_t dcfg; |
| |
| /* 8- or 16-bit mode? */ |
| |
| if (priv->nbits >= 4 && priv->nbits <= 8) |
| { |
| if (txbuffer) |
| { |
| dcfg.data_size = DTC_DATA_SIZE_BYTE; |
| } |
| else |
| { |
| txbuffer = txdummy; |
| dcfg.data_size = DTC_DATA_SIZE_BYTE; |
| } |
| } |
| else if (priv->nbits > 8 && priv->nbits <= 16) |
| { |
| if (txbuffer) |
| { |
| dcfg.data_size = DTC_DATA_SIZE_WORD; |
| } |
| else |
| { |
| txbuffer = txdummy; |
| dcfg.data_size = DTC_DATA_SIZE_WORD; |
| } |
| } |
| else |
| { |
| spierr("ERROR: RSPI not supporting: %d bit word\n", priv->nbits); |
| } |
| |
| dcfg.source_addr = (uint32_t)txbuffer; |
| dcfg.dest_addr = priv->rspibase + RX65N_RSPI_SPDR_OFFSET; |
| |
| /* Configure block size if buf size is configured as more than 1 */ |
| |
| if (CONFIG_RX65N_RSPI_BUF_SIZE - 1) |
| { |
| /* Configure DTC in Block transfer mode */ |
| |
| dcfg.block_size = CONFIG_RX65N_RSPI_BUF_SIZE; |
| dcfg.transfer_count = nwords / CONFIG_RX65N_RSPI_BUF_SIZE; |
| } |
| else |
| { |
| dcfg.transfer_count = nwords; |
| } |
| |
| /* Configure the RX DTC */ |
| |
| ret = rx65n_dtc_setup_dynamic_transferdata(priv->dtchandle, priv->txvec, |
| (uint32_t)&dcfg, 0); |
| if (ret < 0) |
| { |
| spierr("ERROR:[%d] Fail to configure DTC information for TX\n", ret); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: spi_dtcrxsetup |
| * |
| * Description: |
| * Setup to perform RX DTC |
| * |
| ****************************************************************************/ |
| |
| static dtc_err_t rspi_dtcrxsetup(struct rx65n_rspidev_s *priv, |
| void *rxbuffer, void *rxdummy, |
| size_t nwords) |
| { |
| dtc_err_t ret = DTC_SUCCESS; |
| dtc_dynamic_transfer_data_cfg_t dcfg; |
| |
| /* 8- or 16-bit mode */ |
| |
| if (priv->nbits >= 4 && priv->nbits <= 8) |
| { |
| if (rxbuffer) |
| { |
| dcfg.data_size = DTC_DATA_SIZE_BYTE; |
| } |
| else |
| { |
| rxbuffer = rxdummy; |
| dcfg.data_size = DTC_DATA_SIZE_BYTE; |
| } |
| } |
| else if (priv->nbits > 8 && priv->nbits <= 16) |
| { |
| if (rxbuffer) |
| { |
| dcfg.data_size = DTC_DATA_SIZE_WORD; |
| } |
| else |
| { |
| rxbuffer = rxdummy; |
| dcfg.data_size = DTC_DATA_SIZE_WORD; |
| } |
| } |
| else |
| { |
| spierr("ERROR: RSPI not supporting: %d bit word\n", nbits); |
| } |
| |
| dcfg.source_addr = priv->rspibase + RX65N_RSPI_SPDR_OFFSET; |
| dcfg.dest_addr = (uint32_t)rxbuffer; |
| |
| /* Configure block size if buf size is configured as more than 1 */ |
| |
| if (CONFIG_RX65N_RSPI_BUF_SIZE - 1) |
| { |
| /* Configure DTC in Block transfer mode */ |
| |
| dcfg.block_size = CONFIG_RX65N_RSPI_BUF_SIZE; |
| dcfg.transfer_count = nwords / CONFIG_RX65N_RSPI_BUF_SIZE; |
| } |
| else |
| { |
| dcfg.transfer_count = nwords; |
| } |
| |
| /* Configure the RX DTC */ |
| |
| ret = rx65n_dtc_setup_dynamic_transferdata(priv->dtchandle, |
| priv->rxvec, (uint32_t)&dcfg, 0); |
| if (ret < 0) |
| { |
| spierr("ERROR:[%d] Fail to configure DTC information for RX\n", ret); |
| return ret; |
| } |
| |
| return ret; |
| } |
| |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_transfer |
| * |
| * Description: |
| * Exchange a block data with the SPI device |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * txbuffer - The buffer of data to send to the device (may be NULL). |
| * rxbuffer - The buffer to receive data from the device (may be NULL). |
| * nwords - The total number of words to be exchanged. If the interface |
| * uses <= 8 bits per word, then this is the number of |
| * uint8_t's; if the interface uses >8 bits per word, then this |
| * is the number of uint16_t's |
| * |
| * Returned Value: |
| * 0: success, <0:Negated error number on failure |
| * |
| ****************************************************************************/ |
| #ifdef CONFIG_RX65N_RSPI_SW_DT_MODE |
| static int rspi_transfer(struct rx65n_rspidev_s *priv, const void *txbuffer, |
| void *rxbuffer, unsigned int nwords) |
| { |
| uint8_t regval8; |
| #ifndef CONFIG_SPI_POLLWAIT |
| irqstate_t flags; |
| #endif |
| |
| /* Set up to perform the transfer */ |
| |
| priv->txbuffer = (uint8_t *)txbuffer; /* Source buffer */ |
| priv->rxbuffer = (uint8_t *)rxbuffer; /* Destination buffer */ |
| priv->ntxwords = nwords; /* Number of words left to send */ |
| priv->nrxwords = 0; /* Number of words received */ |
| priv->nwords = nwords; /* Total number of exchanges */ |
| |
| /* Set up the low-level data transfer function pointers */ |
| |
| if (priv->nbits > 16) |
| { |
| priv->txword = rspi_txuint32; |
| priv->rxword = rspi_rxuint32; |
| } |
| |
| if (priv->nbits > 8) |
| { |
| priv->txword = rspi_txuint16; |
| priv->rxword = rspi_rxuint16; |
| } |
| else |
| { |
| priv->txword = rspi_txuint8; |
| priv->rxword = rspi_rxuint8; |
| } |
| |
| if (!txbuffer) |
| { |
| priv->txword = rspi_txnull; |
| } |
| |
| if (!rxbuffer) |
| { |
| priv->rxword = rspi_rxnull; |
| } |
| |
| /* Copied data to start the sequence (saves one interrupt) */ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| flags = enter_critical_section(); |
| |
| /* Enable interrupt */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 |= (RSPI_SPCR_SPEIE | RSPI_SPCR_SPTIE | RSPI_SPCR_SPRIE); |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* Start transmission */ |
| |
| rspi_performtx(priv); |
| rspi_startxfr(priv); |
| |
| /* Enable Idle interrupt */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR2_OFFSET); |
| regval8 |= (RSPI_SPCR2_SPIIE); /* RSPI Idle Interrupt Enable */ |
| rspi_putreg8(priv, RX65N_RSPI_SPCR2_OFFSET, regval8); |
| |
| leave_critical_section(flags); |
| |
| /* Wait for the transfer to complete. Since there is no handshake |
| * with SPI, the following should complete even if there are problems |
| * with the transfer, so it should be safe with no timeout. |
| */ |
| |
| /* Wait to be signaled from the interrupt handler */ |
| |
| return nxsem_wait_uninterruptible(&priv->waitsem); |
| |
| #else |
| /* Perform the transfer using polling logic. This will totally |
| * dominate the CPU until the transfer is complete. Only recommended |
| * if (1) your SPI is very fast, and (2) if you only use very short |
| * transfers. |
| */ |
| |
| do |
| { |
| /* Handle outgoing Tx FIFO transfers */ |
| |
| rspi_performtx(priv); |
| |
| /* Handle incoming Rx FIFO transfers */ |
| |
| rspi_performrx(priv); |
| |
| /* Resume the transfer */ |
| |
| rspi_startxfr(priv); |
| |
| /* If there are other threads at this same priority level, |
| * the following may help: |
| */ |
| |
| sched_yield(); |
| } |
| while (priv->nrxwords < priv->nwords); |
| |
| return OK; |
| #endif |
| } |
| #elif defined(CONFIG_RX65N_RSPI_DTC_DT_MODE) |
| static int rspi_transfer(struct rx65n_rspidev_s *priv, const void *txbuffer, |
| void *rxbuffer, unsigned int nwords) |
| { |
| uint8_t regval8; |
| static uint16_t rxdummy = 0xffff; |
| static const uint16_t txdummy = 0xffff; |
| irqstate_t flags; |
| int ret; |
| |
| /* Disable RSPI */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 &= (~RSPI_SPCR_SPE); |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* Setup DTC */ |
| |
| ret = rspi_dtctxsetup(priv, txbuffer, &txdummy, nwords); |
| if (ret > 1) |
| { |
| spierr("ERROR:Fail to setup DTC for TX transfer\n"); |
| return -EINVAL; |
| } |
| |
| ret = rspi_dtcrxsetup(priv, rxbuffer, &rxdummy, nwords); |
| if (ret > 1) |
| { |
| spierr("ERROR:[%d] Fail to setup DTC for RX transfer\n"); |
| return -EINVAL; |
| } |
| |
| flags = enter_critical_section(); |
| |
| /* Enable RSPI source for DTC trigger */ |
| |
| rx65n_dtc_srcactivation(priv->dtchandle, priv->txvec); |
| rx65n_dtc_srcactivation(priv->dtchandle, priv->rxvec); |
| |
| /* Enable TX, RX and Error interrupt */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 |= (RSPI_SPCR_SPEIE | RSPI_SPCR_SPRIE | RSPI_SPCR_SPTIE); |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* Enable RSPI */ |
| |
| rspi_startxfr(priv); |
| |
| /* Wait for transfer completion */ |
| |
| leave_critical_section(flags); |
| |
| /* Wait to be signaled from the interrupt handler */ |
| |
| return nxsem_wait_uninterruptible(&priv->waitsem); |
| } |
| |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_mapirq |
| * |
| * Description: |
| * Map an IRQ number into the appropriate SPI device |
| * |
| * Input Parameters: |
| * irq - The IRQ number to be mapped |
| * |
| * Returned Value: |
| * On success, a reference to the private data structgure for this IRQ. |
| * NULL on failure. |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| static inline struct rx65n_rspidev_s *rspi_mapirq(int irq) |
| { |
| switch (irq) |
| { |
| #ifdef CONFIG_RX65N_RSPI0 |
| case RX65N_SPRI0_IRQ: |
| case RX65N_SPTI0_IRQ: |
| case RX65N_SPEI0_IRQ: |
| case RX65N_SPII0_IRQ: |
| return &g_rspi0dev; |
| #endif |
| #ifdef CONFIG_RX65N_RSPI1 |
| case RX65N_SPRI1_IRQ: |
| case RX65N_SPTI1_IRQ: |
| case RX65N_SPEI1_IRQ: |
| case RX65N_SPII1_IRQ: |
| return &g_rspi1dev; |
| #endif |
| #ifdef CONFIG_RX65N_RSPI2 |
| case RX65N_SPRI2_IRQ: |
| case RX65N_SPTI2_IRQ: |
| case RX65N_SPEI2_IRQ: |
| case RX65N_SPII2_IRQ: |
| return &g_rspi2dev; |
| #endif |
| default: |
| return NULL; |
| } |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_errhandle |
| * |
| * Description: |
| * RSPI common error handler |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * bus - channel number |
| * |
| * Returned Value: |
| * 0: success, <0:Negated error number on failure |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| static void rspi_errhandle(struct rx65n_rspidev_s *priv, uint8_t bus) |
| { |
| uint8_t regval8; |
| rspi_event_t event = RSPI_EVT_ERR_UNDEF; |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPSR_OFFSET); |
| |
| /* Identify and clear error condition . */ |
| |
| if (regval8 & RSPI_SPSR_OVRF) |
| { |
| /* Overrun error */ |
| |
| if (RSPI_EVT_ERR_UNDEF == event) |
| { |
| event = RSPI_EVT_ERR_READ_OVF; |
| } |
| |
| /* Clear error source: OVRF flag. */ |
| |
| regval8 &= (~RSPI_SPSR_OVRF); |
| rspi_putreg8(priv, RX65N_RSPI_SPSR_OFFSET, regval8); |
| } |
| |
| if (regval8 & RSPI_SPSR_MODF) |
| { |
| if (regval8 & RSPI_SPSR_UDRF) |
| { |
| if (RSPI_EVT_ERR_UNDEF == event) |
| { |
| event = RSPI_EVT_ERR_UNDER_RUN; |
| } |
| |
| /* Clear error source : MODF flag and UDRF */ |
| |
| regval8 &= RSPI_SPSR_MODF_UDRF_MASK; |
| rspi_putreg8(priv, RX65N_RSPI_SPSR_OFFSET, regval8); |
| } |
| else |
| { |
| if (RSPI_EVT_ERR_UNDEF == event) |
| { |
| event = RSPI_EVT_ERR_MODE_FAULT; |
| } |
| |
| /* Clear error source : MODF flag */ |
| |
| regval8 &= (~RSPI_SPSR_MODF); |
| rspi_putreg8(priv, RX65N_RSPI_SPSR_OFFSET, regval8); |
| } |
| } |
| |
| if (regval8 & RSPI_SPSR_PERF) |
| { |
| if (RSPI_EVT_ERR_UNDEF == event) |
| { |
| event = RSPI_EVT_ERR_PARITY; |
| } |
| |
| /* Clear error source: PERF flag */ |
| |
| regval8 &= (~RSPI_SPSR_PERF); |
| rspi_putreg8(priv, RX65N_RSPI_SPSR_OFFSET, regval8); |
| } |
| |
| /* Disable the RSPI operation . */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 &= (~(RSPI_SPCR_SPTIE | RSPI_SPCR_SPRIE | RSPI_SPCR_SPE)); |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* Yes, wake up the waiting thread . */ |
| |
| nxsem_post(&priv->waitsem); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_idlinterrupt |
| * |
| * Description: |
| * RSPI IDLE interrupt handler invoke on transmission/reception completion |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * 0: success, <0:Negated error number on failure |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| static int rspi_idlinterrupt(int irq, void *context, void *arg) |
| { |
| uint8_t regval8; |
| |
| struct rx65n_rspidev_s *priv = rspi_mapirq(irq); |
| |
| DEBUGASSERT(priv != NULL); |
| |
| /* Disable Idle interrupt in SPCR2 */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR2_OFFSET); |
| regval8 &= ~(RSPI_SPCR2_SPIIE); /* RSPI Idle Interrupt Enable */ |
| rspi_putreg8(priv, RX65N_RSPI_SPCR2_OFFSET, regval8); |
| |
| /* Disable ransmit/receive interrupt */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 &= ~(RSPI_SPCR_SPRIE | RSPI_SPCR_SPTIE); |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* Yes, wake up the waiting thread */ |
| |
| nxsem_post(&priv->waitsem); |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_erinterrupt |
| * |
| * Description: |
| * RSPI common error interrupt handler |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * |
| * Returned Value: |
| * 0: success, <0:Negated error number on failure |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| static int rspi_erinterrupt(int irq, void *context, void *arg) |
| { |
| struct rx65n_rspidev_s *priv = rspi_mapirq(irq); |
| |
| DEBUGASSERT(priv != NULL); |
| |
| #ifdef CONFIG_RX65N_RSPI0 |
| rspi_errhandle(priv, RX65N_RSPI_CHANNEL0); |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI1 |
| rspi_errhandle(priv, RX65N_RSPI_CHANNEL1); |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI2 |
| rspi_errhandle(priv, RX65N_RSPI_CHANNEL2); |
| #endif |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_rxinterrupt |
| * |
| * Description: |
| * Receive buffer full interrupt handler to process a block data from SPI |
| * device |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * rxbuffer - The buffer to receive data from the device (may be NULL). |
| * nwords - The total number of words to be exchanged. If the interface |
| * uses <= 8 bits per word, then this is the number of |
| * uint8_t's; if the interface uses >8 bits per word, then this |
| * is the number of uint16_t's |
| * |
| * Returned Value: |
| * 0: success, <0:Negated error number on failure |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| static int rspi_rxinterrupt(int irq, void *context, void *arg) |
| { |
| #ifdef CONFIG_RX65N_RSPI_DTC_DT_MODE |
| uint8_t regval8; |
| #endif |
| |
| struct rx65n_rspidev_s *priv = rspi_mapirq(irq); |
| |
| DEBUGASSERT(priv != NULL); |
| |
| #ifdef CONFIG_RX65N_RSPI_SW_DT_MODE |
| /* Handle incoming Rx FIFO transfers */ |
| |
| rspi_performrx(priv); |
| #else |
| |
| /* Disable ransmit/receive interrupt */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 &= ~(RSPI_SPCR_SPRIE | RSPI_SPCR_SPTIE); |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* Yes, wake up the waiting thread */ |
| |
| nxsem_post(&priv->waitsem); |
| |
| #endif |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_txinterrupt |
| * |
| * Description: |
| * Transmit buffer empty interrupt handler to process a block data for SPI |
| * device |
| * |
| * Input Parameters: |
| * priv - Device-specific state data |
| * txbuffer - The buffer of data to send to the device (may be NULL). |
| * nwords - The total number of words to be exchanged. If the interface |
| * uses <= 8 bits per word, then this is the number of |
| * uint8_t's; if the interface uses >8 bits per word, then this |
| * is the number of uint16_t's |
| * |
| * Returned Value: |
| * 0: success, <0:Negated error number on failure |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| static int rspi_txinterrupt(int irq, void *context, void *arg) |
| { |
| struct rx65n_rspidev_s *priv = rspi_mapirq(irq); |
| |
| #ifdef CONFIG_RX65N_RSPI_DTC_DT_MODE |
| uint8_t regval8; |
| #endif |
| |
| DEBUGASSERT(priv != NULL); |
| |
| #ifdef CONFIG_RX65N_RSPI_SW_DT_MODE |
| /* Handle outgoing Tx FIFO transfers */ |
| |
| rspi_performtx(priv); |
| |
| #else |
| |
| /* DTC transfer completion */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| if ((regval8 & RSPI_SPCR_TXMD) == 1) |
| { |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 &= ~(RSPI_SPCR_SPRIE | RSPI_SPCR_SPTIE); |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* Yes, wake up the waiting thread */ |
| |
| nxsem_post(&priv->waitsem); |
| } |
| |
| #endif |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_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: |
| * 0: success, <0:Negated error number on failure |
| * |
| ****************************************************************************/ |
| |
| static int rspi_lock(struct spi_dev_s *dev, bool lock) |
| { |
| struct rx65n_rspidev_s *priv = (struct rx65n_rspidev_s *)dev; |
| int ret; |
| |
| if (lock) |
| { |
| ret = nxmutex_lock(&priv->lock); |
| } |
| else |
| { |
| ret = nxmutex_unlock(&priv->lock); |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_setfrequency |
| * |
| * Description: |
| * Set the RSPI frequency. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * frequency - The SPI frequency requested |
| * |
| * Returned Value: |
| * Returns the actual frequency selected |
| * |
| ****************************************************************************/ |
| |
| static uint32_t rspi_setfrequency(struct spi_dev_s *dev, |
| uint32_t frequency) |
| { |
| struct rx65n_rspidev_s *priv = (struct rx65n_rspidev_s *)dev; |
| uint32_t actual; |
| |
| DEBUGASSERT(priv); |
| actual = priv->actual; |
| |
| if (frequency != priv->frequency) |
| { |
| /* Below formula used to calculate bit rate |
| * Bit rate f(PCLK)/(2 * (n + 1) * 2^N) . |
| * n denotes SPBR setting (0,1,2..255) . |
| * N denotes a BRDV[1:0] bit setting (0, 1, 2, 3) . |
| */ |
| |
| uint16_t regval16; |
| uint32_t brdv; |
| uint8_t spbr; |
| |
| /* N=0 as per fit code */ |
| |
| brdv = RSPI_BRDV_DEFAULT; |
| |
| DEBUGASSERT(brdv < 4); |
| |
| if (frequency >= priv->rspiclock / FREQ_DIVISOR_2) |
| { |
| actual = priv->rspiclock / FREQ_DIVISOR_2; |
| } |
| else if (frequency >= priv->rspiclock / FREQ_DIVISOR_4) |
| { |
| actual = priv->rspiclock / FREQ_DIVISOR_4; |
| } |
| else if (frequency >= priv->rspiclock / FREQ_DIVISOR_6) |
| { |
| actual = priv->rspiclock / FREQ_DIVISOR_6; |
| } |
| else if (frequency >= priv->rspiclock / FREQ_DIVISOR_8) |
| { |
| actual = priv->rspiclock / FREQ_DIVISOR_8; |
| } |
| else if (frequency >= priv->rspiclock / FREQ_DIVISOR_10) |
| { |
| actual = priv->rspiclock / FREQ_DIVISOR_10; |
| } |
| else if (frequency >= priv->rspiclock / FREQ_DIVISOR_12) |
| { |
| actual = priv->rspiclock / FREQ_DIVISOR_12; |
| } |
| else if (frequency >= priv->rspiclock / FREQ_DIVISOR_24) |
| { |
| actual = priv->rspiclock / FREQ_DIVISOR_24; |
| } |
| else if (frequency >= priv->rspiclock / FREQ_DIVISOR_48) |
| { |
| actual = priv->rspiclock / FREQ_DIVISOR_48; |
| } |
| else /* (frequency >= priv->rspiclock / FREQ_DIVISOR_96) */ |
| { |
| actual = priv->rspiclock / FREQ_DIVISOR_96; |
| } |
| |
| /* Calculate n and set the n in SPBR */ |
| |
| spbr = (priv->rspiclock / (2 * actual * ((1 << brdv)))) - 1; |
| rspi_putreg8(priv, RX65N_RSPI_SPBR_OFFSET, spbr); |
| |
| /* Set the N as BRDV[1:0] in SPCMD0 b2 and b3 */ |
| |
| regval16 = rspi_getreg16(priv, RX65N_RSPI_SPCMD0_OFFSET); |
| regval16 &= ~(RSPI_SPCMD_BRDV_MASK); |
| regval16 |= (brdv << 2); |
| rspi_putreg16(priv, RX65N_RSPI_SPCMD0_OFFSET, regval16); |
| |
| /* Save the frequency and actual frequency in RSPI device structure */ |
| |
| priv->frequency = frequency; |
| priv->actual = actual; |
| } |
| |
| return actual; |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_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 rspi_setmode(struct spi_dev_s *dev, enum spi_mode_e mode) |
| { |
| struct rx65n_rspidev_s *priv = (struct rx65n_rspidev_s *)dev; |
| |
| uint16_t regval16; |
| |
| if (priv && mode != priv->mode) |
| { |
| uint32_t modebits; |
| |
| /* Select the CTL register bits based on the selected mode */ |
| |
| switch (mode) |
| { |
| case SPIDEV_MODE0: /* CPOL=0 CPHA=0 */ |
| modebits = 0; |
| break; |
| |
| case SPIDEV_MODE1: /* CPOL=0 CPHA=1 */ |
| modebits = RSPI_SPCMD_PHA; |
| break; |
| |
| case SPIDEV_MODE2: /* CPOL=1 CPHA=0 */ |
| modebits = RSPI_SPCMD_POL; |
| break; |
| |
| case SPIDEV_MODE3: /* CPOL=1 CPHA=1 */ |
| modebits = RSPI_SPCMD_PHA | RSPI_SPCMD_POL; |
| break; |
| |
| default: |
| spiwarn("Warning: Unsupported RSPI mode: %d\n", mode); |
| return; |
| } |
| |
| /* Then set the selected mode */ |
| |
| regval16 = rspi_getreg16(priv, RX65N_RSPI_SPCMD0_OFFSET); |
| regval16 &= ~(RSPI_SPCMD_PHA | RSPI_SPCMD_POL); |
| regval16 |= modebits; |
| rspi_putreg16(priv, RX65N_RSPI_SPCMD0_OFFSET, regval16); |
| |
| /* Save the mode so that subsequent re-configurations will be faster */ |
| |
| priv->mode = mode; |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_setbits |
| * |
| * Description: |
| * Set the number of bits per word. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * nbits - The number of bits requests |
| * |
| * Returned Value: |
| * none |
| * |
| ****************************************************************************/ |
| |
| static void rspi_setbits(struct spi_dev_s *dev, int nbits) |
| { |
| struct rx65n_rspidev_s *priv = (struct rx65n_rspidev_s *)dev; |
| uint8_t regval8; |
| uint16_t regval16; |
| |
| if (priv && nbits != priv->nbits && nbits > 0 && nbits <= 16) |
| { |
| if (nbits >= 4 && nbits <= 8) |
| { |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPDCR_OFFSET); |
| regval8 |= RSPI_SPDCR_SPBYT; |
| rspi_putreg8(priv, RX65N_RSPI_SPDCR_OFFSET, regval8); |
| } |
| else if (nbits > 8 && nbits <= 16) |
| { |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPDCR_OFFSET); |
| regval8 &= ~(RSPI_SPDCR_SPLW | RSPI_SPDCR_SPBYT); |
| rspi_putreg8(priv, RX65N_RSPI_SPDCR_OFFSET, regval8); |
| } |
| else |
| { |
| spierr("ERROR: RSPI not supporting: %d bit word\n", nbits); |
| } |
| |
| /* Configure command data register for this transfer */ |
| |
| regval16 = rspi_getreg16(priv, RX65N_RSPI_SPCMD0_OFFSET); |
| regval16 |= (RSPI_SPCMD_SPB_MASK & ((nbits - 1) << 8)); |
| rspi_putreg16(priv, RX65N_RSPI_SPCMD0_OFFSET, regval16); |
| |
| priv->nbits = nbits; |
| } |
| else |
| { |
| spiwarn("Warning: Unsupported RSPI Word width: %d\n", nbits); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_send |
| * |
| * Description: |
| * Exchange one word on RSPI |
| * |
| * 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 rspi_send(struct spi_dev_s *dev, uint32_t wd) |
| { |
| struct rx65n_rspidev_s *priv = (struct rx65n_rspidev_s *)dev; |
| uint32_t response = 0; |
| |
| rspi_transfer(priv, &wd, &response, 1); |
| return response; |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_exchange |
| * |
| * Description: |
| * Exahange a block of data from SPI. Required. |
| * |
| * Input Parameters: |
| * dev - Device-specific state data |
| * buffer - A pointer to the buffer of data to be sent |
| * rxbuffer - A pointer to the buffer in which to receive data |
| * nwords - the length of data that 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 rspi_exchange(struct spi_dev_s *dev, const void *txbuffer, |
| void *rxbuffer, size_t nwords) |
| { |
| struct rx65n_rspidev_s *priv = (struct rx65n_rspidev_s *)dev; |
| rspi_transfer(priv, txbuffer, rxbuffer, nwords); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_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 |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_SPI_EXCHANGE |
| static void rspi_sndblock(struct spi_dev_s *dev, const void *buffer, |
| size_t nwords) |
| { |
| struct rx65n_rspidev_s *priv = (struct rx65n_rspidev_s *)dev; |
| rspi_transfer(priv, buffer, NULL, nwords); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: rspi_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 |
| * |
| ****************************************************************************/ |
| |
| #ifndef CONFIG_SPI_EXCHANGE |
| static void rspi_recvblock(struct spi_dev_s *dev, void *buffer, |
| size_t nwords) |
| { |
| struct rx65n_rspidev_s *priv = (struct rx65n_rspidev_s *)dev; |
| rspi_transfer(priv, NULL, buffer, nwords); |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Function Name: rspi_interrupt_init |
| * Description : Configure ICU for RSPI interrupt |
| * Arguments : none |
| * Return Value : none |
| ****************************************************************************/ |
| |
| void rspi_interrupt_init(struct rx65n_rspidev_s *priv, uint8_t bus) |
| { |
| /* Enable error interrupt source bit */ |
| |
| IEN(ICU, GROUPAL0) = 0; /* Disable Group AL0 interrupts */ |
| IR(ICU, GROUPAL0) = 0; /* Clear interrupt flag */ |
| IPR(ICU, GROUPAL0) = RX65N_RSPI_INTRRUPT_PRIO; |
| IEN(ICU, GROUPAL0) = 1; /* Enable Group AL0 interrupt */ |
| |
| #ifdef CONFIG_RX65N_RSPI0 |
| if (bus == RX65N_RSPI_CHANNEL0) |
| { |
| /* Configure Transmit empty buffer interrupt source bit */ |
| |
| IEN(RSPI0, SPTI0) = 0; /* Disable SPTI0 interrupts */ |
| IR(RSPI0, SPTI0) = 0; /* Clear interrupt flag */ |
| IPR(RSPI0, SPTI0) = RX65N_RSPI_INTRRUPT_PRIO; |
| IEN(RSPI0, SPTI0) = 1; /* Enable SPTI0 interrupt */ |
| ICU.IER[0x04].BIT.IEN7 = 1; |
| |
| /* Configure Receive buffer full interrupt source bit */ |
| |
| IEN(RSPI0, SPRI0) = 0; /* Disable SPRI0 interrupts */ |
| IR(RSPI0, SPRI0) = 0; /* Clear interrupt flag */ |
| IPR(RSPI0, SPRI0) = RX65N_RSPI_INTRRUPT_PRIO; |
| IEN(RSPI0, SPRI0) = 1; /* Enable SPRI0 interrupt */ |
| ICU.IER[0x04].BIT.IEN6 = 1; |
| |
| /* Enable error interrupt source bit */ |
| |
| ICU.GENAL0.BIT.EN17 = 1; |
| |
| /* Enable Idle interrupt source bit */ |
| |
| ICU.GENAL0.BIT.EN16 = 1; |
| } |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI1 |
| if (bus == RX65N_RSPI_CHANNEL1) |
| { |
| /* Configure Transmit empty buffer interrupt */ |
| |
| IEN(RSPI1, SPTI1) = 0; /* Disable SPTI1 interrupts */ |
| IR(RSPI1, SPTI1) = 0; /* Clear interrupt flag */ |
| IPR(RSPI1, SPTI1) = RX65N_RSPI_INTRRUPT_PRIO; |
| IEN(RSPI1, SPTI1) = 1; /* Enable SPTI1 interrupt */ |
| ICU.IER[0x05].BIT.IEN1 = 1; |
| |
| /* Configure Receive buffer full interrupt */ |
| |
| IEN(RSPI1, SPRI1) = 0; /* Disable SPRI1 interrupts */ |
| IR(RSPI1, SPRI1) = 0; /* Clear interrupt flag */ |
| IPR(RSPI1, SPRI1) = RX65N_RSPI_INTRRUPT_PRIO; |
| IEN(RSPI1, SPRI1) = 1; /* Enable SPRI1 interrupt */ |
| ICU.IER[0x05].BIT.IEN0 = 1; |
| |
| /* Enable error interrupt source bit */ |
| |
| ICU.GENAL0.BIT.EN19 = 1; |
| |
| /* Enable Idle interrupt source bit */ |
| |
| ICU.GENAL0.BIT.EN18 = 1; |
| } |
| #endif |
| |
| #ifdef CONFIG_RX65N_RSPI2 |
| if (bus == RX65N_RSPI_CHANNEL2) |
| { |
| /* Configure Transmit empty buffer interrupt */ |
| |
| IEN(RSPI2, SPTI2) = 0; /* Disable SPTI2 interrupts */ |
| IR(RSPI2, SPTI2) = 0; /* Clear interrupt flag */ |
| IPR(RSPI2, SPTI2) = RX65N_RSPI_INTRRUPT_PRIO; |
| IEN(RSPI2, SPTI2) = 1; /* Enable SPTI2 interrupt */ |
| ICU.IER[13].BIT.IEN5 = 1; |
| |
| /* Configure Receive buffer full interrupt */ |
| |
| IEN(RSPI2, SPRI2) = 0; /* Disable SPRI2 interrupts */ |
| IR(RSPI2, SPRI2) = 0; /* Clear interrupt flag */ |
| IPR(RSPI2, SPRI2) = RX65N_RSPI_INTRRUPT_PRIO; |
| IEN(RSPI2, SPRI2) = 1; /* Enable SPRI2 interrupt */ |
| ICU.IER[13].BIT.IEN4 = 1; |
| |
| /* Enable error interrupt source bit */ |
| |
| ICU.GENAL0.BIT.EN21 = 1; |
| |
| /* Enable Idle interrupt source bit */ |
| |
| ICU.GENAL0.BIT.EN20 = 1; |
| } |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Function Name: power_on_off |
| * Description : Switches power to an RSPI channel. Required by FIT spec. |
| * Arguments : |
| * channel -Which channel to use. |
| * on_or_off -0 means 1 means off |
| * Return Value : none |
| ****************************************************************************/ |
| |
| static void rspi_power_on_off(uint8_t channel, uint8_t on_or_off) |
| { |
| SYSTEM.PRCR.WORD = 0xa50bu; |
| |
| switch (channel) |
| { |
| case RX65N_RSPI_CHANNEL0: |
| MSTP(RSPI0) = on_or_off; |
| break; |
| case RX65N_RSPI_CHANNEL1: |
| MSTP(RSPI1) = on_or_off; |
| break; |
| case RX65N_RSPI_CHANNEL2: |
| MSTP(RSPI2) = on_or_off; |
| break; |
| } |
| } |
| |
| /**************************************************************************** |
| * Function Name: rspi_reg_protect |
| * Description : Register protection enable/disable. Required by FIT spec. |
| * Arguments : |
| * enable - used to enable and disable protection |
| * Return Value : none |
| ****************************************************************************/ |
| |
| static void rspi_reg_protect(uint8_t enable) |
| { |
| SYSTEM.PRCR.WORD = 0xa50b; |
| MPC.PWPR.BIT.B0WI = 0; |
| MPC.PWPR.BIT.PFSWE = 1; |
| } |
| |
| /**************************************************************************** |
| * Name: rspi_bus_initialize |
| * |
| * Description: |
| * Initialize the selected RSPI bus in its default state (Master, 8-bit, |
| * mode 0, etc.) |
| * |
| * Input Parameters: |
| * priv - private SPI device structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void rspi_bus_initialize(struct rx65n_rspidev_s *priv) |
| { |
| uint8_t regval8; |
| uint16_t regval16; |
| |
| #if defined(CONFIG_RX65N_RSPI_DTC_DT_MODE) |
| int ret; |
| |
| /* Prepare Transmit and receive parameter */ |
| |
| priv->dtchandle = rx65n_dtc_gethandle(0); |
| ret = rx65n_dtc_setup_static_transferdata(priv->dtchandle, |
| priv->txvec, (uint32_t)&tx_cfg, |
| (uint32_t)priv->p_txdt, (uint32_t)0); |
| if (ret < 0) |
| { |
| spierr("ERROR:[%d] Fail to configure DTC information for TX\n", ret); |
| return; |
| } |
| |
| ret = rx65n_dtc_setup_static_transferdata(priv->dtchandle, |
| priv->rxvec, (uint32_t)&rx_cfg, |
| (uint32_t)priv->p_rxdt, (uint32_t)0); |
| if (ret < 0) |
| { |
| spierr("ERROR:[%d] Fail to configure DTC information for RX\n", ret); |
| return; |
| } |
| |
| #endif |
| |
| /* Initialize control register */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 = regval8 & (~RSPI_SPCR_SMPS); /* RSPI operation (4-wire method) */ |
| regval8 = regval8 & (~RSPI_SPCR_TXMD); /* Full-duplex synchronous serial communications */ |
| regval8 = regval8 | (RSPI_SPCR_MSTR); /* Master mode */ |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* RSPI Pin Control Register */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPPCR_OFFSET); |
| regval8 &= (~(RSPI_SPPCR_SPLP /* Loopback mode */ |
| | RSPI_SPPCR_SPLP2 /* Loopback mode */ |
| | RSPI_SPPCR_MOIFV /* MOSI pin idles high */ |
| | RSPI_SPPCR_MOIFE)); /* MOSI pin idles at MOIFV */ |
| rspi_putreg8(priv, RX65N_RSPI_SPPCR_OFFSET, regval8); |
| |
| /* Sets polarity of SSL signal */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SSLP_OFFSET); |
| regval8 &= (~(RSPI_SSLP_SSL0P |
| | RSPI_SSLP_SSL1P |
| | RSPI_SSLP_SSL2P |
| | RSPI_SSLP_SSL3P)); /* RSPCK is low when idle */ |
| rspi_putreg8(priv, RX65N_RSPI_SSLP_OFFSET, regval8); |
| |
| /* Inititalize frequency, frame size and SPI mode */ |
| |
| priv->frequency = 0; |
| priv->mode = SPIDEV_MODE0; |
| |
| /* Select a default frequency of approx. 400KHz */ |
| |
| rspi_setfrequency((struct spi_dev_s *)priv, 400000); |
| |
| /* Configure data control register SPDCR |
| * Four frames can be transmitted or received in one round of transmission |
| * or reception activation. |
| */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPDCR_OFFSET); |
| regval8 &= (~RSPI_SPDCR_MASK); |
| #ifdef CONFIG_RX65N_RSPI_BUF_SIZE |
| regval8 |= (RSPI_SPDCR_SPFC_MASK & (CONFIG_RX65N_RSPI_BUF_SIZE -1)); |
| priv->bufsize = CONFIG_RX65N_RSPI_BUF_SIZE; |
| #else |
| regval8 |= (RSPI_SPDCR_SPFC0 | RSPI_SPDCR_SPFC1); /* 4 frames */ |
| priv->bufsize = BUFSIZE_4FRAME; |
| #endif |
| regval8 |= (RSPI_SPDCR_SPBYT); |
| priv->nbits = 8; |
| rspi_putreg8(priv, RX65N_RSPI_SPDCR_OFFSET, regval8); |
| |
| /* Configure RSPI clock delay registers SPCKD */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCKD_OFFSET); |
| regval8 &= (~RSPI_SPCKD_MASK); |
| #if CONFIG_RX65N_RSPI_SPCKD_DELAY |
| regval8 |= (RSPI_SPCKD_MASK & CONFIG_RX65N_RSPI_SPCKD_DELAY); |
| #else |
| regval8 |= (RSPI_SPCKD_SCKDL1); /* 2RSPCK delay */ |
| #endif |
| rspi_putreg8(priv, RX65N_RSPI_SPCKD_OFFSET, regval8); |
| |
| /* Configure RSPI slave select negation delay register SSLND */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SSLND_OFFSET); |
| regval8 &= (~RSPI_SSLND_MASK); |
| #if CONFIG_RX65N_RSPI_SSLND_DELAY |
| regval8 |= (RSPI_SSLND_MASK & CONFIG_RX65N_RSPI_SSLND_DELAY); |
| #else |
| regval8 |= (RSPI_SSLND_SLNDL1); /* 2RSPCK delay */ |
| #endif |
| rspi_putreg8(priv, RX65N_RSPI_SSLND_OFFSET, regval8); |
| |
| /* Configure RSPI next-access delay register SPND */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPND_OFFSET); |
| regval8 &= (~RSPI_SPND_MASK); |
| #if CONFIG_RX65N_RSPI_SPND_DELAY |
| regval8 |= (RSPI_SPND_MASK & CONFIG_RX65N_RSPI_SPND_DELAY); |
| #else |
| regval8 |= (RSPI_SPND_SPNDL1); /* 2RSPCK delay */ |
| #endif |
| rspi_putreg8(priv, RX65N_RSPI_SPND_OFFSET, regval8); |
| |
| /* Configure RSPI Control Register 2 */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR2_OFFSET); |
| regval8 &= (~RSPI_SPCR2_MASK); /* Clear all bit */ |
| #ifdef CONFIG_RX65N_RSPI_HIGHSPEED |
| regval8 |= (RSPI_SPCR2_SCKASE); |
| #endif |
| rspi_putreg8(priv, RX65N_RSPI_SPCR2_OFFSET, regval8); |
| |
| /* Configure RSPI sequence control register SPSCR */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPSCR_OFFSET); |
| regval8 &= (~RSPI_SPSCR_MASK); |
| rspi_putreg8(priv, RX65N_RSPI_SPSCR_OFFSET, regval8); |
| |
| /* Configure the SPCMD0 command register */ |
| |
| regval16 = rspi_getreg16(priv, RX65N_RSPI_SPCMD0_OFFSET); |
| regval16 &= (~RSPI_SPCMD_MASK); |
| regval16 |= ((RSPI_SPCMD_SPB0 | RSPI_SPCMD_SPB1 | RSPI_SPCMD_SPB2) |
| #if CONFIG_RX65N_RSPI_BITORDER |
| | (RSPI_SPCMD_LSBF) /* RSPI LSB First */ |
| #endif |
| | (RSPI_SPCMD_SPNDEN) /* RSPI Next-Access Delay Enable */ |
| | (RSPI_SPCMD_SLNDEN) /* SSL Negation Delay Setting Enable */ |
| | (RSPI_SPCMD_SCKDEN)); /* RSPCK Delay Setting Enable */ |
| rspi_putreg16(priv, RX65N_RSPI_SPCMD0_OFFSET, regval16); |
| |
| /* Configure Set RSPI data control register 2 (SPDCR2) */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPDCR2_OFFSET); |
| regval8 &= (~RSPI_SPDCR2_BYSW); |
| rspi_putreg8(priv, RX65N_RSPI_SPDCR2_OFFSET, regval8); |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| /* Disable all interrupt in SPCR */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 &= ~(RSPI_SPCR_SPEIE /* RSPI Error Interrupt Enable */ |
| | RSPI_SPCR_SPTIE /* Transmit Buffer Empty Interrupt Enable */ |
| | RSPI_SPCR_SPRIE); /* RSPI Receive Buffer Full Interrupt Enable */ |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* Disable Idle interrupt in SPCR2 */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR2_OFFSET); |
| regval8 &= ~(RSPI_SPCR2_SPIIE); /* RSPI Idle Interrupt Enable */ |
| rspi_putreg8(priv, RX65N_RSPI_SPCR2_OFFSET, regval8); |
| #endif |
| |
| /* Attach the interrupt */ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| irq_attach(priv->rspitxirq, (xcpt_t)rspi_txinterrupt, NULL); |
| irq_attach(priv->rspirxirq, (xcpt_t)rspi_rxinterrupt, NULL); |
| irq_attach(priv->rspierirq, (xcpt_t)rspi_erinterrupt, NULL); |
| irq_attach(priv->rspiidlirq, (xcpt_t)rspi_idlinterrupt, NULL); |
| #endif |
| |
| /* Enable RSPI functionality */ |
| |
| regval8 = rspi_getreg8(priv, RX65N_RSPI_SPCR_OFFSET); |
| regval8 |= (RSPI_SPCR_SPE); /* RSPI Function Enable */ |
| rspi_putreg8(priv, RX65N_RSPI_SPCR_OFFSET, regval8); |
| |
| /* Enable SPI irq */ |
| |
| #ifndef CONFIG_SPI_POLLWAIT |
| up_enable_irq(priv->rspitxirq); |
| up_enable_irq(priv->rspirxirq); |
| up_enable_irq(priv->rspierirq); |
| up_enable_irq(priv->rspiidlirq); |
| #endif |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: rx65n_rspibus_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 *rx65n_rspibus_initialize(int bus) |
| { |
| struct rx65n_rspidev_s *priv = NULL; |
| irqstate_t flags = enter_critical_section(); |
| |
| #ifdef CONFIG_RX65N_RSPI0 |
| if (bus == RX65N_RSPI_CHANNEL0) |
| { |
| /* Select RSPI0 */ |
| |
| priv = &g_rspi0dev; |
| |
| /* Only configure if the bus is not already configured */ |
| |
| if (!priv->initialized) |
| { |
| /* Configure RSPI0 pins for 4WIRE mode |
| * RSPCKA,MOSIA, MISOA and SSLA0 |
| */ |
| |
| rspi_reg_protect(REG_PROTECTION_OFF); /* Disable protection */ |
| rspi_pinconfig(bus); |
| |
| /* Switches power to an RSPI channel */ |
| |
| rspi_power_on_off(bus, RSPI_POWER_ON); |
| |
| /* Configure interrupt controller for RSPI */ |
| |
| rspi_interrupt_init(priv, bus); |
| |
| /* Set up default configuration: Master, 8-bit, etc. */ |
| |
| rspi_bus_initialize(priv); |
| |
| priv->initialized = true; |
| } |
| } |
| else |
| #endif |
| #ifdef CONFIG_RX65N_RSPI1 |
| if (bus == RX65N_RSPI_CHANNEL1) |
| { |
| /* Select SPI1 */ |
| |
| priv = &g_rspi1dev; |
| |
| /* Only configure if the bus is not already configured */ |
| |
| if (!priv->initialized) |
| { |
| /* Configure SPI1 pins 4WIRE mode: RSPCKB, MOSIB, MISOB and SSLB0 */ |
| |
| rspi_reg_protect(REG_PROTECTION_OFF); /* Disable protection */ |
| rspi_pinconfig(bus); |
| |
| /* Switches power to an RSPI channel */ |
| |
| rspi_power_on_off(bus, RSPI_POWER_ON); |
| |
| /* Configure interrupt controller for RSPI */ |
| |
| rspi_interrupt_init(priv, bus); |
| |
| /* Set up default configuration: Master, 8-bit, etc. */ |
| |
| rspi_bus_initialize(priv); |
| priv->initialized = true; |
| } |
| } |
| else |
| #endif |
| #ifdef CONFIG_RX65N_RSPI2 |
| if (bus == RX65N_RSPI_CHANNEL2) |
| { |
| /* Select SPI2 */ |
| |
| priv = &g_rspi2dev; |
| |
| /* Only configure if the bus is not already configured */ |
| |
| if (!priv->initialized) |
| { |
| /* Configure SPI2 pins 4WIRE mode: RSPCKC, MOSIC, MISOC and SSLC0 */ |
| |
| rspi_reg_protect(REG_PROTECTION_OFF); /* Disable protection */ |
| rspi_pinconfig(bus); |
| |
| /* Switches power to an RSPI channel */ |
| |
| rspi_power_on_off(bus, RSPI_POWER_ON); |
| |
| /* Configure interrupt controller for RSPI */ |
| |
| rspi_interrupt_init(priv, bus); |
| |
| /* Set up default configuration: Master, 8-bit, etc. */ |
| |
| rspi_bus_initialize(priv); |
| |
| priv->initialized = true; |
| } |
| } |
| else |
| #endif |
| { |
| spierr("ERROR: Unsupported RSPI bus: %d\n", bus); |
| } |
| |
| leave_critical_section(flags); |
| return (struct spi_dev_s *)priv; |
| } |
| |
| #endif /* CONFIG_RX65N_RSPI0 || CONFIG_RX65N_RSPI1 || CONFIG_RX65N_RSPI2 */ |