mirror of
https://github.com/apache/nuttx.git
synced 2025-05-09 06:42:10 +08:00

Fix the minor style issue and remove unnecessary cast Signed-off-by: chao an <anchao@xiaomi.com>
434 lines
12 KiB
C
434 lines
12 KiB
C
/****************************************************************************
|
|
* arch/xtensa/src/esp32/esp32_rmt.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 <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <inttypes.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <debug.h>
|
|
|
|
#include <nuttx/kmalloc.h>
|
|
#include <arch/board/board.h>
|
|
#include <nuttx/irq.h>
|
|
#include <nuttx/arch.h>
|
|
#include <nuttx/spinlock.h>
|
|
|
|
#include "xtensa.h"
|
|
|
|
#include "esp32_gpio.h"
|
|
#include "esp32_rmt.h"
|
|
#include "esp32_irq.h"
|
|
#include "esp32_clockconfig.h"
|
|
|
|
#include "hardware/esp32_dport.h"
|
|
#include "hardware/esp32_gpio_sigmap.h"
|
|
|
|
#ifdef CONFIG_ESP32_RMT
|
|
|
|
/****************************************************************************
|
|
* Pre-processor Definitions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Types
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Private Function Prototypes
|
|
****************************************************************************/
|
|
|
|
/* RMT methods */
|
|
|
|
static void rmt_reset(struct rmt_dev_s *dev);
|
|
static int rmt_setup(struct rmt_dev_s *dev);
|
|
IRAM_ATTR static int rmt_interrupt(int irq, void *context, void *arg);
|
|
|
|
/****************************************************************************
|
|
* Private Data
|
|
****************************************************************************/
|
|
|
|
struct rmt_dev_s g_rmt_dev =
|
|
{
|
|
.periph = ESP32_PERIPH_RMT,
|
|
.irq = ESP32_IRQ_RMT,
|
|
.cpu = 0,
|
|
.cpuint = -ENOMEM,
|
|
.lock = 0
|
|
};
|
|
|
|
/****************************************************************************
|
|
* Private Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: rmt_reset
|
|
*
|
|
* Description:
|
|
* Reset the RMT device. Called early to initialize the hardware. This
|
|
* function is called, before esp32_rmt_setup().
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" RMT driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
static void rmt_reset(struct rmt_dev_s *dev)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
flags = spin_lock_irqsave(&dev->lock);
|
|
|
|
modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST, 1);
|
|
modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST, 0);
|
|
|
|
/* Clear any spurious IRQ Flag */
|
|
|
|
putreg32(0xffffffff, RMT_INT_CLR_REG);
|
|
|
|
/* Enable memory wrap-around */
|
|
|
|
modifyreg32(RMT_APB_CONF_REG, 0 , BIT(1));
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rmt_setup
|
|
*
|
|
* Description:
|
|
* Configure the RMT. This method is called the first time that the RMT
|
|
* device is opened. This will occur when the port is first opened.
|
|
* This setup includes configuring and attaching RMT interrupts.
|
|
*
|
|
* Input Parameters:
|
|
* dev - An instance of the "upper half" RMT driver state structure.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; a negated errno on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
static int rmt_setup(struct rmt_dev_s *dev)
|
|
{
|
|
irqstate_t flags;
|
|
int ret = OK;
|
|
|
|
flags = spin_lock_irqsave(&dev->lock);
|
|
|
|
if (dev->cpuint != -ENOMEM)
|
|
{
|
|
/* Disable the provided CPU Interrupt to configure it. */
|
|
|
|
up_disable_irq(dev->cpuint);
|
|
}
|
|
|
|
dev->cpu = up_cpu_index();
|
|
dev->cpuint = esp32_setup_irq(dev->cpu, dev->periph,
|
|
1, ESP32_CPUINT_LEVEL);
|
|
if (dev->cpuint < 0)
|
|
{
|
|
/* Failed to allocate a CPU interrupt of this type. */
|
|
|
|
ret = dev->cpuint;
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
ret = irq_attach(dev->irq, rmt_interrupt, dev);
|
|
|
|
if (ret != OK)
|
|
{
|
|
/* Failed to attach IRQ, so CPU interrupt must be freed. */
|
|
|
|
esp32_teardown_irq(dev->cpu, dev->periph, dev->cpuint);
|
|
dev->cpuint = -ENOMEM;
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Enable the CPU interrupt that is linked to the RMT device. */
|
|
|
|
up_enable_irq(dev->irq);
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rmt_load_tx_buffer
|
|
*
|
|
* Description:
|
|
* Copies chunks of data from the buffer to the RMT device memory
|
|
* This function can also be called on the first transmition data chunk
|
|
*
|
|
* Input Parameters:
|
|
* channel - Pointer to the channel to be reloaded
|
|
*
|
|
* Returned Value:
|
|
* None
|
|
*
|
|
****************************************************************************/
|
|
|
|
IRAM_ATTR void rmt_load_tx_buffer(struct rmt_dev_channel_s *channel)
|
|
{
|
|
uint32_t *src = channel->src;
|
|
uint32_t dst_mem;
|
|
uint32_t buffer_size;
|
|
|
|
if (channel->src_offset == 0)
|
|
{
|
|
buffer_size = channel->available_words;
|
|
dst_mem = channel->start_address;
|
|
channel->next_buffer = 0;
|
|
}
|
|
else
|
|
{
|
|
buffer_size = channel->reload_thresh;
|
|
dst_mem = channel->start_address +
|
|
4*channel->next_buffer*channel->reload_thresh;
|
|
|
|
/* only swap buffers after the first call */
|
|
|
|
if (channel->next_buffer == 0)
|
|
{
|
|
channel->next_buffer = 1;
|
|
}
|
|
else
|
|
{
|
|
channel->next_buffer = 0;
|
|
}
|
|
}
|
|
|
|
while (channel->src_offset < channel->words_to_send && buffer_size > 0)
|
|
{
|
|
uint32_t word_to_send = *(src + channel->src_offset);
|
|
putreg32(word_to_send, dst_mem);
|
|
|
|
channel->src_offset++;
|
|
dst_mem += 4;
|
|
buffer_size--;
|
|
}
|
|
|
|
/* Adding 0x00 on RMT's buffer marks the EOT */
|
|
|
|
if (channel->src_offset == channel->words_to_send && buffer_size > 0)
|
|
{
|
|
putreg32(0x00, dst_mem);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rmt_interrupt
|
|
*
|
|
* Description:
|
|
* RMT TX interrupt handler
|
|
*
|
|
* Input Parameters:
|
|
* irq - The IRQ number of the interrupt.
|
|
* context - The register state save array at the time of the interrupt.
|
|
* arg - The pointer to driver structure.
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; a negated errno on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
IRAM_ATTR static int rmt_interrupt(int irq, void *context, void *arg)
|
|
{
|
|
struct rmt_dev_s *dev = (struct rmt_dev_s *)arg;
|
|
uint32_t regval = getreg32(RMT_INT_ST_REG);
|
|
|
|
uint8_t error_flag = 0;
|
|
|
|
int flags = spin_lock_irqsave(&dev->lock);
|
|
|
|
for (int ch_idx = 0; ch_idx < RMT_NUMBER_OF_CHANNELS; ch_idx++)
|
|
{
|
|
struct rmt_dev_channel_s *channel_data =
|
|
(struct rmt_dev_channel_s *) &(dev->channels[ch_idx]);
|
|
|
|
/* IRQs from channels with no pins, should be ignored */
|
|
|
|
if (channel_data->output_pin < 0)
|
|
{
|
|
putreg32(RMT_CHN_TX_THR_EVENT_INT_CLR(ch_idx), RMT_INT_CLR_REG);
|
|
putreg32(RMT_CHN_TX_END_INT_CLR(ch_idx), RMT_INT_CLR_REG);
|
|
continue;
|
|
}
|
|
|
|
if (regval & RMT_CHN_TX_THR_EVENT_INT_ST(ch_idx))
|
|
{
|
|
putreg32(RMT_CHN_TX_THR_EVENT_INT_CLR(ch_idx), RMT_INT_CLR_REG);
|
|
|
|
/* buffer refill */
|
|
|
|
rmt_load_tx_buffer(channel_data);
|
|
}
|
|
else if (regval & RMT_CHN_TX_END_INT_ST(ch_idx))
|
|
{
|
|
/* end of transmition */
|
|
|
|
modifyreg32(RMT_INT_ENA_REG,
|
|
RMT_CHN_TX_END_INT_ENA(ch_idx) |
|
|
RMT_CHN_TX_THR_EVENT_INT_ENA(ch_idx),
|
|
0
|
|
);
|
|
|
|
putreg32(RMT_CHN_TX_END_INT_CLR(ch_idx), RMT_INT_CLR_REG);
|
|
putreg32(RMT_CHN_TX_THR_EVENT_INT_CLR(ch_idx), RMT_INT_CLR_REG);
|
|
|
|
/* release the lock so the write function can return */
|
|
|
|
nxsem_post(&channel_data->tx_sem);
|
|
}
|
|
}
|
|
|
|
if (error_flag)
|
|
{
|
|
/* clear any spurious IRQ flag */
|
|
|
|
putreg32(0xffffffff, RMT_INT_CLR_REG);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&dev->lock, flags);
|
|
return OK;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Public Functions
|
|
****************************************************************************/
|
|
|
|
/****************************************************************************
|
|
* Name: esp32_rmtinitialize
|
|
*
|
|
* Description:
|
|
* Initialize the selected RMT device
|
|
*
|
|
* Input Parameters:
|
|
*
|
|
* Returned Value:
|
|
* Valid RMT device structure reference on success; a NULL on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
struct rmt_dev_s *esp32_rmtinitialize(void)
|
|
{
|
|
struct rmt_dev_s *rmtdev = &g_rmt_dev;
|
|
irqstate_t flags;
|
|
|
|
flags = spin_lock_irqsave(&rmtdev->lock);
|
|
|
|
modifyreg32(DPORT_PERIP_CLK_EN_REG, 0, DPORT_RMT_CLK_EN);
|
|
modifyreg32(DPORT_PERIP_RST_EN_REG, DPORT_RMT_RST, 0);
|
|
|
|
spin_unlock_irqrestore(&rmtdev->lock, flags);
|
|
|
|
rmt_reset(rmtdev);
|
|
rmt_setup(rmtdev);
|
|
|
|
rmtdev->channels = kmm_zalloc(sizeof(struct rmt_dev_channel_s) *
|
|
RMT_NUMBER_OF_CHANNELS);
|
|
if (!rmtdev->channels)
|
|
{
|
|
rmterr("Failed to allocate memory for RMT Channels");
|
|
return NULL;
|
|
}
|
|
|
|
for (int ch_idx = 0; ch_idx < RMT_NUMBER_OF_CHANNELS; ch_idx++)
|
|
{
|
|
struct rmt_dev_channel_s *channel_data =
|
|
(struct rmt_dev_channel_s *) &(rmtdev->channels[ch_idx]);
|
|
|
|
channel_data->open_count = 0;
|
|
channel_data->ch_idx = ch_idx;
|
|
channel_data->output_pin = -1;
|
|
|
|
channel_data->available_words = 64;
|
|
uint32_t start_addr_chn = RMT_DATA_BASE_ADDR +
|
|
RMT_DATA_MEMORY_BLOCK_WORDS * 4 * ch_idx;
|
|
|
|
channel_data->start_address = start_addr_chn;
|
|
channel_data->reload_thresh = channel_data->available_words / 2;
|
|
channel_data->parent_dev = rmtdev;
|
|
}
|
|
|
|
return rmtdev;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: rmt_attach_pin_to_channel
|
|
*
|
|
* Description:
|
|
* Binds a gpio pin to a RMT channel
|
|
*
|
|
* Input Parameters:
|
|
* rmtdev - pointer the rmt device, needed for the locks
|
|
* output_pin - the pin used for output
|
|
* channel - the RMT's channel that will be used
|
|
*
|
|
* Returned Value:
|
|
* Zero on success; a negated errno on failure
|
|
*
|
|
****************************************************************************/
|
|
|
|
int rmt_attach_pin_to_channel(struct rmt_dev_s *rmtdev, int ch_idx, int pin)
|
|
{
|
|
irqstate_t flags;
|
|
|
|
if (ch_idx >= RMT_NUMBER_OF_CHANNELS || pin < 0)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
flags = spin_lock_irqsave(&rmtdev->lock);
|
|
|
|
struct rmt_dev_channel_s *channel_data =
|
|
(struct rmt_dev_channel_s *) &(rmtdev->channels[ch_idx]);
|
|
|
|
channel_data->output_pin = pin;
|
|
nxsem_init(&channel_data->tx_sem, 0, 1);
|
|
|
|
/* Configure RMT GPIO pin */
|
|
|
|
esp32_gpio_matrix_out(pin, RMT_SIG_OUT0_IDX + ch_idx, 0, 0);
|
|
esp32_configgpio(pin, OUTPUT_FUNCTION_1);
|
|
|
|
spin_unlock_irqrestore(&rmtdev->lock, flags);
|
|
|
|
return OK;
|
|
}
|
|
|
|
#endif
|