blob: d1e127d2c83362749ef7ed79466f98fb1909b24e [file] [log] [blame]
/****************************************************************************
* arch/xtensa/src/esp32/esp32_dac.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 "xtensa.h"
#include <nuttx/config.h>
#include <nuttx/irq.h>
#include <nuttx/spinlock.h>
#include <nuttx/analog/dac.h>
#include <debug.h>
#include "esp32_dac.h"
#include "esp32_rtc_gpio.h"
#include "hardware/esp32_rtc_io.h"
#include "hardware/esp32_dport.h"
#include "hardware/esp32_sens.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define ESP32_DAC0_RTC_IO_CHANNEL RTCIO_GPIO25_CHANNEL
#define ESP32_DAC1_RTC_IO_CHANNEL RTCIO_GPIO26_CHANNEL
/****************************************************************************
* Private Types
****************************************************************************/
struct esp32_dac_priv_s
{
spinlock_t slock; /* Device specific lock. */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* DAC methods */
static void dac_reset(struct dac_dev_s *dev);
static int dac_setup(struct dac_dev_s *dev);
static void dac_shutdown(struct dac_dev_s *dev);
static void dac_txint(struct dac_dev_s *dev, bool enable);
static int dac_send(struct dac_dev_s *dev, struct dac_msg_s *msg);
static int dac_ioctl(struct dac_dev_s *dev, int cmd, unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
struct esp32_dac_priv_s esp32_dac_priv;
static const struct dac_ops_s g_dacops =
{
.ao_reset = dac_reset,
.ao_setup = dac_setup,
.ao_shutdown = dac_shutdown,
.ao_txint = dac_txint,
.ao_send = dac_send,
.ao_ioctl = dac_ioctl,
};
static struct dac_dev_s g_dac =
{
.ad_ops = &g_dacops, /* Arch-specific operations */
.ad_nchannel = 2, /* Available number of DAC channels */
.ad_priv = (void *) (&esp32_dac_priv), /* Used by the arch-specific logic */
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: dac_power
*
* Description:
* Power ON or OFF both DAC channels
*
* Input Parameters:
* bool on - true : turn DAC ON; false : turn DAC OFF
*
* Returned Value:
* None
*
****************************************************************************/
static void dac_power(bool on)
{
if (on)
{
modifyreg32(RTC_IO_PAD_DAC1_REG, 0, RTC_IO_PDAC1_DAC_XPD_FORCE |
RTC_IO_PDAC1_XPD_DAC);
modifyreg32(RTC_IO_PAD_DAC2_REG, 0, RTC_IO_PDAC2_DAC_XPD_FORCE |
RTC_IO_PDAC2_XPD_DAC);
}
else
{
modifyreg32(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_DAC_XPD_FORCE |
RTC_IO_PDAC1_XPD_DAC, 0);
modifyreg32(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_DAC_XPD_FORCE |
RTC_IO_PDAC2_XPD_DAC, 0);
}
}
/****************************************************************************
* Name: dac_reset
*
* Description:
* Reset the DAC channel. Called early to initialize the hardware. This
* is called, before dac_setup() and on error conditions.
*
* NOTE: DAC reset will reset both DAC channels!
*
* Input Parameters:
* dev - A pointer to the DAC device structure. This structure contains
* information about the DAC device.
*
* Returned Value:
* None
*
****************************************************************************/
static void dac_reset(struct dac_dev_s *dev)
{
dac_shutdown(dev);
dac_setup(dev);
}
/****************************************************************************
* Name: dac_setup
*
* Description:
* Configure the DAC. This method is called the first time that the DAC
* device is opened. This will occur when the port is first opened.
* This setup includes configuring and attaching DAC interrupts.
* Interrupts are all disabled upon return.
*
* Input Parameters:
* dev - A pointer to the DAC device structure. This structure contains
* information about the DAC device.
*
* Returned Value:
* Zero on success; a negated errno value on failure.
*
****************************************************************************/
static int dac_setup(struct dac_dev_s *dev)
{
irqstate_t flags;
struct esp32_dac_priv_s *priv = (struct esp32_dac_priv_s *) dev->ad_priv;
ainfo("DAC starting setup (for both channels)");
flags = spin_lock_irqsave(&priv->slock);
/* Initialize RTC GPIO set to RTC Disabled and disable both pull resistors
* set RTC_IO_PDACn_MUX_SEL to route the pad to RTC block
* set RTC_IO_PDACn_DRV to 0x2 (which is default anyway)
* Note: Drive strength _DRV (you won't find this in TRM)
* 0: ~5 mA; 1: ~10 mA; 2: ~20 mA; 3: ~40 mA; the default value is 2.
* Keep other bits 0, especially:
* RTC_IO_PDACn_FUN_SEL (2 bits) = mode 0 to choose RTC_GPIO function.
* RTC_IO_PDACn_FUN_IE to disable Input
* RTC_IO_PDACn_RUE to disable pull up resistor
* RTC_IO_PDACn_RDE to disable pull down resistor
*
* Note: the following 2 bits are setup separately in dac_power as a last
* operation.
* set RTC_IO_PDACn_DAC_XPD_FORCE to power up DAC
* set RTC_IO_PDACn_XPD_DAC to power on DAC
*/
uint32_t reg_val = RTC_IO_PDAC1_MUX_SEL |
0x2 << RTC_IO_PDAC1_DRV_S;
/* Write the same value to both registers for DAC 1 and DAC 2 */
putreg32(reg_val, RTC_IO_PAD_DAC1_REG);
putreg32(reg_val, RTC_IO_PAD_DAC2_REG);
/* Disable GPIO output by setting bits in "write 1 to clear" reg */
modifyreg32(RTC_GPIO_ENABLE_W1TC_REG, 0 ,
(UINT32_C(1) << (ESP32_DAC0_RTC_IO_CHANNEL +
RTC_GPIO_ENABLE_W1TC_S)) |
(UINT32_C(1) << (ESP32_DAC1_RTC_IO_CHANNEL +
RTC_GPIO_ENABLE_W1TC_S)));
/* Clear bit of PAD_DRIVER to setup "normal" output mode for the
* corresponding pads
*/
modifyreg32(RTC_GPIO_PIN6_REG, RTC_GPIO_PIN6_PAD_DRIVER, 0);
modifyreg32(RTC_GPIO_PIN7_REG, RTC_GPIO_PIN7_PAD_DRIVER, 0);
dac_power(true);
dev->ad_ocount += 1;
spin_unlock_irqrestore(&priv->slock, flags);
return OK;
}
/****************************************************************************
* Name: dac_shutdown
*
* Description:
* Disable the DAC. This method is called when the DAC device is closed.
* This method reverses the operation the setup method.
*
* Input Parameters:
* dev - A pointer to the DAC device structure. This structure contains
* information about the DAC device.
*
* Returned Value:
* None
*
****************************************************************************/
static void dac_shutdown(struct dac_dev_s *dev)
{
esp32_configrtcio(ESP32_DAC0_RTC_IO_CHANNEL, RTC_FUNCTION_DIGITAL);
esp32_configrtcio(ESP32_DAC1_RTC_IO_CHANNEL, RTC_FUNCTION_DIGITAL);
dac_power(false);
}
/****************************************************************************
* Name: dac_txint
*
* Description:
* Call to enable or disable TX (transmit) interrupts for the DAC device.
* This function is intended to control interrupt-driven data transfers.
* Enabling TX interrupts allows the DAC device to generate
* an interrupt when it is ready to accept new data for transmission.
* Disabling TX interrupts would prevent the DAC from generating these
* interrupts.
*
* Input Parameters:
* dev - A pointer to the DAC device structure. This structure contains
* information about the DAC device.
* enable - Set true to enable TX interrupts. set false to disable
* TX interrupts.
*
* Returned Value:
* None
*
* Note:
* The actual logic for enabling or disabling TX interrupts is not
* implemented in this function!
*
****************************************************************************/
static void dac_txint(struct dac_dev_s *dev, bool enable)
{
}
/****************************************************************************
* Name: dac_send
*
* Description:
* Set the DAC (Digital-to-Analog Converter) output.
*
* Input Parameters:
* dev - A pointer to the DAC device structure. This structure contains
* information about the DAC device.
* msg - A pointer to the DAC message structure. This structure includes
* the data to be sent to the DAC and the target DAC channel.
* The 'am_data' field of this structure is the actual data to be
* written to the DAC, and 'am_channel' determines which DAC channel
* (0 or 1) to use.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure. -EINVAL is
* returned if an invalid channel is specified.
*
* Note: The dac_msg_s.am_data is treated as 8 bit value i.e. in range
* from 0-255 and corresponds to the analog voltage 0~Vref.
* The reference voltage 'Vref' here is input from the pin VDD3P3_RTC
* which ideally equals to the power supply VDD (3.3V).
* The output voltage can be calculated as the following:
* out_voltage = 3.3 * digi_val / 255
*
****************************************************************************/
static int dac_send(struct dac_dev_s *dev, struct dac_msg_s *msg)
{
irqstate_t flags;
uint8_t value = (uint8_t)(msg->am_data & 0xff);
uint32_t reg_val;
struct esp32_dac_priv_s *priv = (struct esp32_dac_priv_s *) dev->ad_priv;
flags = spin_lock_irqsave(&priv->slock);
switch (msg->am_channel)
{
case 0:
modifyreg32(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1, 0);
reg_val = getreg32(RTC_IO_PAD_DAC1_REG);
reg_val &= ~RTC_IO_PDAC1_DAC_M;
reg_val |= value << RTC_IO_PDAC1_DAC_S;
putreg32(reg_val, RTC_IO_PAD_DAC1_REG);
break;
case 1:
modifyreg32(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2, 0);
reg_val = getreg32(RTC_IO_PAD_DAC2_REG);
reg_val &= ~RTC_IO_PDAC2_DAC_M;
reg_val |= value << RTC_IO_PDAC2_DAC_S;
putreg32(reg_val, RTC_IO_PAD_DAC2_REG);
break;
default:
spin_unlock_irqrestore(&priv->slock, flags);
return -EINVAL;
}
spin_unlock_irqrestore(&priv->slock, flags);
/* One shot mode does not support interrupts for DAC. The TX Done is
* signaled to upper half driver directly from this function because the
* value is used right away.
*/
dac_txdone(dev);
return OK;
}
/****************************************************************************
* Name: dac_ioctl
*
* Description:
* All ioctl (input/output control) calls for the DAC device are routed
* through this method. This function handles various control commands
* for the DAC device. Currently, it returns -ENOTTY for all commands,
* indicating that no command is implemented.
*
* Input Parameters:
* dev - A pointer to the DAC device structure. This structure contains
* information about the DAC device, required for handling the ioctl
* commands.
* cmd - An integer value representing the ioctl command. These commands
* are used to perform various control operations on the DAC device.
* arg - An unsigned long value representing additional information or
* arguments that are relevant to the ioctl command.
* The interpretation of this parameter
* depends on the specific command.
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure. Currently, it
* always returns -ENOTTY, indicating that no ioctl commands are supported.
*
****************************************************************************/
static int dac_ioctl(struct dac_dev_s *dev, int cmd, unsigned long arg)
{
return -ENOTTY;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32_dac_initialize
*
* Description:
* Initialize the DAC.
*
* Input Parameters:
* None
*
* Returned Value:
* Valid dac device structure reference on success; a NULL on failure.
*
****************************************************************************/
struct dac_dev_s *esp32_dac_initialize(void)
{
return &g_dac;
}