nuttx/arch/arm/src/stm32/stm32_opamp.c
Xiang Xiao 972a260391 arch/arm: Remove FAR and CODE from chip folder(3)
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2022-05-03 16:50:52 +03:00

1408 lines
35 KiB
C

/****************************************************************************
* arch/arm/src/stm32/stm32_opamp.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <debug.h>
#include <arch/board/board.h>
#include <nuttx/analog/opamp.h>
#include <nuttx/analog/ioctl.h>
#include "chip.h"
#include "stm32_gpio.h"
#include "stm32_opamp.h"
/* OPAMP "upper half" support must be enabled */
#ifdef CONFIG_STM32_OPAMP
/* Some OPAMP peripheral must be enabled */
/* Up to 4 OPAMPs in STM32F3 Series */
#if defined(CONFIG_STM32_OPAMP1) || defined(CONFIG_STM32_OPAMP2) || \
defined(CONFIG_STM32_OPAMP3) || defined(CONFIG_STM32_OPAMP4)
#ifndef CONFIG_STM32_SYSCFG
# error "SYSCFG clock enable must be set"
#endif
/* @TODO: support for STM32F30XX opamps */
#if defined(CONFIG_STM32_STM32F30XX) || defined(CONFIG_STM32_STM32F33XX)
/* Currently only STM32F33XX supported */
#if defined(CONFIG_STM32_STM32F30XX)
# error "Not supported yet"
#endif
#if defined(CONFIG_STM32_STM32F33XX)
# if defined(CONFIG_STM32_OPAMP1) || defined(CONFIG_STM32_OPAMP3) || \
defined(CONFIG_STM32_OPAMP4)
# error "STM32F33 supports only OPAMP2"
# endif
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* OPAMPs default configuration *********************************************/
#ifdef CONFIG_STM32_OPAMP1
# ifndef OPAMP1_MODE
# define OPAMP1_MODE OPAMP_MODE_DEFAULT
# endif
# ifndef OPAMP1_MUX
# define OPAMP1_MUX OPAMP_MUX_DEFAULT
# endif
# ifndef OPAMP1_USERCAL
# define OPAMP1_USERCAL OPAMP_USERCAL_DEFAULT
# endif
# ifndef OPAMP1_LOCK
# define OPAMP1_LOCK OPAMP_LOCK_DEFAULT
# endif
# ifndef OPAMP1_GAIN
# define OPAMP1_GAIN OPAMP_GAIN_DEFAULT
# endif
#endif
#ifdef CONFIG_STM32_OPAMP2
# ifndef OPAMP2_MODE
# define OPAMP2_MODE OPAMP_MODE_DEFAULT
# endif
# ifndef OPAMP2_MUX
# define OPAMP2_MUX OPAMP_MUX_DEFAULT
# endif
# ifndef OPAMP2_USERCAL
# define OPAMP2_USERCAL OPAMP_USERCAL_DEFAULT
# endif
# ifndef OPAMP2_LOCK
# define OPAMP2_LOCK OPAMP_LOCK_DEFAULT
# endif
# ifndef OPAMP2_GAIN
# define OPAMP2_GAIN OPAMP_GAIN_DEFAULT
# endif
#endif
#ifdef CONFIG_STM32_OPAMP3
# ifndef OPAMP3_MODE
# define OPAMP3_MODE OPAMP_MODE_DEFAULT
# endif
# ifndef OPAMP3_MUX
# define OPAMP3_MUX OPAMP_MUX_DEFAULT
# endif
# ifndef OPAMP3_USERCAL
# define OPAMP3_USERCAL OPAMP_USERCAL_DEFAULT
# endif
# ifndef OPAMP3_LOCK
# define OPAMP3_LOCK OPAMP_LOCK_DEFAULT
# endif
# ifndef OPAMP3_GAIN
# define OPAMP3_GAIN OPAMP_GAIN_DEFAULT
# endif
#endif
#ifdef CONFIG_STM32_OPAMP4
# ifndef OPAMP4_MODE
# define OPAMP4_MODE OPAMP_MODE_DEFAULT
# endif
# ifndef OPAMP4_MUX
# define OPAMP4_MUX OPAMP_MUX_DEFAULT
# endif
# ifndef OPAMP4_USERCAL
# define OPAMP4_USERCAL OPAMP_USERCAL_DEFAULT
# endif
# ifndef OPAMP4_LOCK
# define OPAMP4_LOCK OPAMP_LOCK_DEFAULT
# endif
# ifndef OPAMP4_GAIN
# define OPAMP4_GAIN OPAMP_GAIN_DEFAULT
# endif
#endif
/* Some assertions *********************************************************/
/* Check OPAMPs inputs selection */
#ifdef CONFIG_STM32_OPAMP1
# if (OPAMP1_MODE == OPAMP_MODE_FOLLOWER)
# define OPAMP1_VMSEL OPAMP1_VMSEL_FOLLOWER
# endif
# if (OPAMP1_MODE == OPAMP_MODE_PGA)
# define OPAMP1_VMSEL OPAMP1_VMSEL_PGA
# endif
# if (OPAMP1_MODE == OPAMP_MODE_STANDALONE)
# ifndef OPAMP1_VMSEL
# error "OPAMP1_VMSEL must be selected in standalone mode!"
# endif
# endif
# ifndef OPAMP1_VPSEL
# error "OPAMP1_VPSEL must be selected in standalone mode!"
# endif
#endif
#ifdef CONFIG_STM32_OPAMP2
# if (OPAMP2_MODE == OPAMP_MODE_FOLLOWER)
# define OPAMP2_VMSEL OPAMP2_VMSEL_FOLLOWER
# endif
# if (OPAMP2_MODE == OPAMP_MODE_PGA)
# define OPAMP2_VMSEL OPAMP2_VMSEL_PGA
# endif
# if (OPAMP2_MODE == OPAMP_MODE_STANDALONE)
# ifndef OPAMP2_VMSEL
# error "OPAMP2_VMSEL must be selected in standalone mode!"
# endif
# endif
# ifndef OPAMP2_VPSEL
# error "OPAMP2_VPSEL must be selected in standalone mode!"
# endif
#endif
#ifdef CONFIG_STM32_OPAMP3
# if (OPAMP3_MODE == OPAMP_MODE_FOLLOWER)
# define OPAMP3_VMSEL OPAMP3_VMSEL_FOLLOWER
# endif
# if (OPAMP3_MODE == OPAMP_MODE_PGA)
# define OPAMP3_VMSEL OPAMP3_VMSEL_PGA
# endif
# if (OPAMP3_MODE == OPAMP_MODE_STANDALONE)
# ifndef OPAMP3_VMSEL
# error "OPAMP3_VMSEL must be selected in standalone mode!"
# endif
# endif
# ifndef OPAMP3_VPSEL
# error "OPAMP3_VPSEL must be selected in standalone mode!"
# endif
#endif
#ifdef CONFIG_STM32_OPAMP4
# if (OPAMP4_MODE == OPAMP_MODE_FOLLOWER)
# define OPAMP4_VMSEL OPAMP4_VMSEL_FOLLOWER
# endif
# if (OPAMP4_MODE == OPAMP_MODE_PGA)
# define OPAMP4_VMSEL OPAMP4_VMSEL_PGA
# endif
# if (OPAMP4_MODE == OPAMP_MODE_STANDALONE)
# ifndef OPAMP4_VMSEL
# error "OPAMP4_VMSEL must be selected in standalone mode!"
# endif
# endif
# ifndef OPAMP4_VPSEL
# error "OPAMP4_VPSEL must be selected in standalone mode!"
# endif
#endif
/* When OPAMP MUX enabled, make sure that secondary selection inputs are
* configured
*/
#ifdef CONFIG_STM32_OPAMP1
# if (OPAMP1_MUX == OPAMP_MUX_ENABLE)
# if !defined(OPAMP1_VMSSEL) || !defined(OPAMP1_VPSSEL)
# error "OPAMP1_VMSSEL and OPAMP1_VPSSEL must be selected when OPAMP1 MUX enabled!"
# endif
# endif
#endif
#ifdef CONFIG_STM32_OPAMP2
# if (OPAMP2_MUX == OPAMP_MUX_ENABLE)
# if !defined(OPAMP2_VMSSEL) || !defined(OPAMP2_VPSSEL)
# error "OPAMP2_VMSSEL and OPAMP2_VPSSEL must be selected when OPAMP2 MUX enabled!"
# endif
# endif
#endif
#ifdef CONFIG_STM32_OPAMP3
# if (OPAMP3_MUX == OPAMP_MUX_ENABLE)
# if !defined(OPAMP3_VMSSEL) || !defined(OPAMP3_VPSSEL)
# error "OPAMP3_VMSSEL and OPAMP3_VPSSEL must be selected when OPAMP3 MUX enabled!"
# endif
# endif
#endif
#ifdef CONFIG_STM32_OPAMP4
# if (OPAMP4_MUX == OPAMP_MUX_ENABLE)
# if !defined(OPAMP4_VMSSEL) || !defined(OPAMP4_VPSSEL)
# error "OPAMP4_VMSSEL and OPAMP4_VPSSEL must be selected when OPAMP4 MUX enabled!"
# endif
# endif
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/* This structure describes the configuration of one OPAMP device */
struct stm32_opamp_s
{
uint32_t csr; /* Control and status register */
uint8_t lock:1; /* OPAMP lock */
uint8_t mux:1; /* Timer controlled MUX mode */
uint8_t mode:2; /* OPAMP mode */
uint8_t gain:4; /* OPAMP gain in PGA mode */
uint8_t vm_sel:2; /* Inverting input selection */
uint8_t vp_sel:2; /* Non inverting input selection */
uint8_t vms_sel:2; /* Inverting input secondary selection (MUX mode) */
uint8_t vps_sel:2; /* Non inverting input secondary selection (Mux mode) */
uint16_t trim_n:5; /* Offset trimming value (NMOS) */
uint16_t trim_p:5; /* Offset trimming value (PMOS) */
uint16_t _reserved:6; /* reserved for calibration */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/* OPAMP Register access */
static inline void opamp_modify_csr(struct stm32_opamp_s *priv,
uint32_t clearbits, uint32_t setbits);
static inline uint32_t opamp_getreg_csr(struct stm32_opamp_s *priv);
static inline void opamp_putreg_csr(struct stm32_opamp_s *priv,
uint32_t value);
static bool stm32_opamplock_get(struct stm32_opamp_s *priv);
static int stm32_opamplock(struct stm32_opamp_s *priv, bool lock);
/* Initialization */
static int stm32_opampconfig(struct stm32_opamp_s *priv);
static int stm32_opampenable(struct stm32_opamp_s *priv, bool enable);
static int stm32_opampgain_set(struct stm32_opamp_s *priv, uint8_t gain);
#if 0
static int stm32_opampcalibrate(struct stm32_opamp_s *priv);
#endif
/* OPAMP Driver Methods */
static void opamp_shutdown(struct opamp_dev_s *dev);
static int opamp_setup(struct opamp_dev_s *dev);
static int opamp_ioctl(struct opamp_dev_s *dev, int cmd,
unsigned long arg);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct opamp_ops_s g_opampops =
{
.ao_shutdown = opamp_shutdown,
.ao_setup = opamp_setup,
.ao_ioctl = opamp_ioctl
};
#ifdef CONFIG_STM32_OPAMP1
static struct stm32_opamp_s g_opamp1priv =
{
.csr = STM32_OPAMP1_CSR,
.lock = OPAMP1_LOCK,
.mux = OPAMP1_MUX,
.mode = OPAMP1_MODE,
.vm_sel = OPAMP1_VMSEL,
.vp_sel = OPAMP1_VPSEL,
#if OPAMP1_MUX == OPAMP_MUX_ENABLE
.vms_sel = OPAMP1_VMSSEL,
.vps_sel = OPAMP1_VPSSEL,
#endif
.gain = OPAMP1_GAIN
};
static struct opamp_dev_s g_opamp1dev =
{
.ad_ops = &g_opampops,
.ad_priv = &g_opamp1priv
};
#endif
#ifdef CONFIG_STM32_OPAMP2
static struct stm32_opamp_s g_opamp2priv =
{
.csr = STM32_OPAMP2_CSR,
.lock = OPAMP2_LOCK,
.mux = OPAMP2_MUX,
.mode = OPAMP2_MODE,
.vm_sel = OPAMP2_VMSEL,
.vp_sel = OPAMP2_VPSEL,
#if OPAMP2_MUX == OPAMP_MUX_ENABLE
.vms_sel = OPAMP2_VMSSEL,
.vps_sel = OPAMP2_VPSSEL,
#endif
.gain = OPAMP2_GAIN
};
static struct opamp_dev_s g_opamp2dev =
{
.ad_ops = &g_opampops,
.ad_priv = &g_opamp2priv
};
#endif
#ifdef CONFIG_STM32_OPAMP3
static struct stm32_opamp_s g_opamp3priv =
{
.csr = STM32_OPAMP3_CSR,
.lock = OPAMP3_LOCK,
.mux = OPAMP3_MUX,
.mode = OPAMP3_MODE,
.vm_sel = OPAMP3_VMSEL,
.vp_sel = OPAMP3_VPSEL,
#if OPAMP3_MUX == OPAMP_MUX_ENABLE
.vms_sel = OPAMP3_VMSSEL,
.vps_sel = OPAMP3_VPSSEL,
#endif
.gain = OPAMP3_GAIN
};
static struct opamp_dev_s g_opamp3dev =
{
.ad_ops = &g_opampops,
.ad_priv = &g_opamp3priv
};
#endif
#ifdef CONFIG_STM32_OPAMP4
static struct stm32_opamp_s g_opamp4priv =
{
.csr = STM32_OPAMP4_CSR,
.lock = OPAMP4_LOCK,
.mux = OPAMP4_MUX,
.mode = OPAMP4_MODE,
.vm_sel = OPAMP4_VMSEL,
.vp_sel = OPAMP4_VPSEL,
#if OPAMP4_MUX == OPAMP_MUX_ENABLE
.vms_sel = OPAMP4_VMSSEL,
.vps_sel = OPAMP4_VPSSEL,
#endif
.gain = OPAMP4_GAIN
};
static struct opamp_dev_s g_opamp4dev =
{
.ad_ops = &g_opampops,
.ad_priv = &g_opamp4priv
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: opamp_modify_csr
*
* Description:
* Modify the value of a 32-bit OPAMP CSR register (not atomic).
*
* Input Parameters:
* priv - A reference to the OPAMP structure
* clrbits - The bits to clear
* setbits - The bits to set
*
* Returned Value:
* None
*
****************************************************************************/
static inline void opamp_modify_csr(struct stm32_opamp_s *priv,
uint32_t clearbits, uint32_t setbits)
{
uint32_t csr = priv->csr;
modifyreg32(csr, clearbits, setbits);
}
/****************************************************************************
* Name: opamp_getreg_csr
*
* Description:
* Read the value of an OPAMP CSR register
*
* Input Parameters:
* priv - A reference to the OPAMP structure
*
* Returned Value:
* The current contents of the OPAMP CSR register
*
****************************************************************************/
static inline uint32_t opamp_getreg_csr(struct stm32_opamp_s *priv)
{
uint32_t csr = priv->csr;
return getreg32(csr);
}
/****************************************************************************
* Name: opamp_putreg_csr
*
* Description:
* Write a value to an OPAMP register.
*
* Input Parameters:
* priv - A reference to the OPAMP structure
* value - The value to write to the OPAMP CSR register
*
* Returned Value:
* None
*
****************************************************************************/
static inline void opamp_putreg_csr(struct stm32_opamp_s *priv,
uint32_t value)
{
uint32_t csr = priv->csr;
putreg32(value, csr);
}
/****************************************************************************
* Name: stm32_opamp_opamplock_get
*
* Description:
* Get OPAMP lock bit state
*
* Input Parameters:
* priv - A reference to the OPAMP structure
*
* Returned Value:
* True if OPAMP locked, false if not locked
*
****************************************************************************/
static bool stm32_opamplock_get(struct stm32_opamp_s *priv)
{
uint32_t regval;
regval = opamp_getreg_csr(priv);
return (((regval & OPAMP_CSR_LOCK) == 0) ? false : true);
}
/****************************************************************************
* Name: stm32_opamplock
*
* Description:
* Lock OPAMP CSR register
*
* Input Parameters:
* priv - A reference to the OPAMP structure
* enable - lock flag
*
* Returned Value:
* 0 on success, a negated errno value on failure
*
****************************************************************************/
static int stm32_opamplock(struct stm32_opamp_s *priv, bool lock)
{
bool current;
current = stm32_opamplock_get(priv);
if (current)
{
if (lock == false)
{
aerr("ERROR: OPAMP LOCK can be cleared only by a system reset\n");
return -EPERM;
}
}
else
{
if (lock == true)
{
opamp_modify_csr(priv, 0, OPAMP_CSR_LOCK);
priv->lock = OPAMP_LOCK_RO;
}
}
return OK;
}
/****************************************************************************
* Name: stm32_opampconfig
*
* Description:
* Configure OPAMP and used I/Os
*
* Input Parameters:
* priv - A reference to the OPAMP structure
*
* Returned Value:
* 0 on success, a negated errno value on failure
*
****************************************************************************/
static int stm32_opampconfig(struct stm32_opamp_s *priv)
{
uint32_t regval = 0;
int index;
/* Get OPAMP index */
switch (priv->csr)
{
#ifdef CONFIG_STM32_OPAMP1
case STM32_OPAMP1_CSR:
index = 1;
break;
#endif
#ifdef CONFIG_STM32_OPAMP2
case STM32_OPAMP2_CSR:
index = 2;
break;
#endif
#ifdef CONFIG_STM32_OPAMP3
case STM32_OPAMP3_CSR:
index = 3;
break;
#endif
#ifdef CONFIG_STM32_OPAMP4
case STM32_OPAMP4_CSR:
index = 4;
break;
#endif
default:
return -EINVAL;
}
/* Configure non inverting input */
switch (index)
{
#ifdef CONFIG_STM32_OPAMP1
case 1:
{
switch (priv->vp_sel)
{
case OPAMP1_VPSEL_PA7:
stm32_configgpio(GPIO_OPAMP1_VINP_1);
regval |= OPAMP_CSR_VPSEL_PA7;
break;
case OPAMP1_VPSEL_PA5:
stm32_configgpio(GPIO_OPAMP1_VINP_2);
regval |= OPAMP_CSR_VPSEL_PA5;
break;
case OPAMP1_VPSEL_PA3:
stm32_configgpio(GPIO_OPAMP1_VINP_3);
regval |= OPAMP_CSR_VPSEL_PA3;
break;
case OPAMP1_VPSEL_PA1:
stm32_configgpio(GPIO_OPAMP1_VINP_4);
regval |= OPAMP_CSR_VPSEL_PA1;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP2
case 2:
{
switch (priv->vp_sel)
{
#ifndef CONFIG_STM32_STM32F33XX
case OPAMP2_VPSEL_PD14:
stm32_configgpio(GPIO_OPAMP2_VINP_1);
regval |= OPAMP_CSR_VPSEL_PD14;
break;
#endif
case OPAMP2_VPSEL_PB14:
stm32_configgpio(GPIO_OPAMP2_VINP_2);
regval |= OPAMP_CSR_VPSEL_PB14;
break;
case OPAMP2_VPSEL_PB0:
stm32_configgpio(GPIO_OPAMP2_VINP_3);
regval |= OPAMP_CSR_VPSEL_PB0;
break;
case OPAMP2_VPSEL_PA7:
stm32_configgpio(GPIO_OPAMP2_VINP_4);
regval |= OPAMP_CSR_VPSEL_PA7;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP3
case 3:
{
switch (priv->vp_sel)
{
case OPAMP3_VPSEL_PB13:
stm32_configgpio(GPIO_OPAMP3_VINP_1);
regval |= OPAMP_CSR_VPSEL_PB13;
break;
case OPAMP3_VPSEL_PA5:
stm32_configgpio(GPIO_OPAMP3_VINP_2);
regval |= OPAMP_CSR_VPSEL_PA5;
break;
case OPAMP3_VPSEL_PA1:
stm32_configgpio(GPIO_OPAMP3_VINP_3);
regval |= OPAMP_CSR_VPSEL_PA1;
break;
case OPAMP3_VPSEL_PB0:
stm32_configgpio(GPIO_OPAMP3_VINP_4);
regval |= OPAMP_CSR_VPSEL_PB0;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP4
case 4:
{
switch (priv->vp_sel)
{
case OPAMP4_VPSEL_PD11:
stm32_configgpio(GPIO_OPAMP4_VINP_1);
regval |= OPAMP_CSR_VPSEL_PD11;
break;
case OPAMP4_VPSEL_PB11:
stm32_configgpio(GPIO_OPAMP4_VINP_2);
regval |= OPAMP_CSR_VPSEL_PB11;
break;
case OPAMP4_VPSEL_PA4:
stm32_configgpio(GPIO_OPAMP4_VINP_3);
regval |= OPAMP_CSR_VPSEL_PA4;
break;
case OPAMP4_VPSEL_PB13:
stm32_configgpio(GPIO_OPAMP4_VINP_4;
regval |= OPAMP_CSR_VPSEL_PB13;
break;
default:
return -EINVAL;
}
break;
}
#endif
default:
return -EINVAL;
}
/* Configure inverting input */
switch (index)
{
#ifdef CONFIG_STM32_OPAMP1
case 1:
{
switch (priv->vm_sel)
{
case OPAMP1_VSEL_PC5:
stm32_configgpio(GPIO_OPAMP1_VINM_1);
regval |= OPAMP_CSR_VMSEL_PC5;
break;
case OPAMP1_VMSEL_PA3:
stm32_configgpio(GPIO_OPAMP1_VINM_2);
regval |= OPAMP_CSR_VMSEL_PA3;
break;
case OPAMP1_VMSEL_PGAMODE:
regval |= OPAMP_CSR_VMSEL_PGA;
break;
case OPAMP1_VMSEL_FOLLOWER:
regval |= OPAMP_CSR_VMSEL_FOLLOWER;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP2
case 2:
{
switch (priv->vm_sel)
{
case OPAMP2_VMSEL_PC5:
stm32_configgpio(GPIO_OPAMP2_VINM_1);
regval |= OPAMP_CSR_VMSEL_PC5;
break;
case OPAMP2_VMSEL_PA5:
stm32_configgpio(GPIO_OPAMP2_VINM_2);
regval |= OPAMP_CSR_VMSEL_PA5;
break;
case OPAMP2_VMSEL_PGAMODE:
regval |= OPAMP_CSR_VMSEL_PGA;
break;
case OPAMP2_VMSEL_FOLLOWER:
regval |= OPAMP_CSR_VMSEL_FOLLOWER;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP3
case 3:
{
switch (priv->vm_sel)
{
case OPAMP3_VMSEL_PB10:
stm32_configgpio(GPIO_OPAMP3_VINM_1);
regval |= OPAMP_CSR_VMSEL_PB10;
break;
case OPAMP3_VMSEL_PB2:
stm32_configgpio(GPIO_OPAMP3_VINM_2);
regval |= OPAMP_CSR_VMSEL_PB2;
break;
case OPAMP3_VMSEL_PGAMODE:
regval |= OPAMP_CSR_VMSEL_PGA;
break;
case OPAMP3_VMSEL_FOLLOWER:
regval |= OPAMP_CSR_VMSEL_FOLLOWER;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP4
case 4:
{
switch (priv->vm_sel)
{
case OPAMP4_VMSEL_PB10:
stm32_configgpio(GPIO_OPAMP4_VINM_1);
regval |= OPAMP_CSR_VMSEL_PB10;
break;
case OPAMP4_VMSEL_PD8:
stm32_configgpio(GPIO_OPAMP4_VINM_2);
regval |= OPAMP_CSR_VMSEL_PD8;
break;
case OPAMP4_VMSEL_PGAMODE:
regval |= OPAMP_CSR_VMSEL_PGA;
break;
case OPAMP4_VMSEL_FOLLOWER:
regval |= OPAMP_CSR_VMSEL_FOLLOWER;
break;
default:
return -EINVAL;
}
break;
}
#endif
default:
return -EINVAL;
}
if (priv->mux == 1)
{
/* Enable Timer controlled Mux mode */
regval |= OPAMP_CSR_TCMEN;
/* Configure non inverting secondary input */
switch (index)
{
#ifdef CONFIG_STM32_OPAMP1
case 1:
{
switch (priv->vps_sel)
{
case OPAMP1_VPSEL_PA7:
stm32_configgpio(GPIO_OPAMP1_VINP_1);
regval |= OPAMP_CSR_VPSSEL_PA7;
break;
case OPAMP1_VPSEL_PA5:
stm32_configgpio(GPIO_OPAMP1_VINP_2);
regval |= OPAMP_CSR_VPSSEL_PA5;
break;
case OPAMP1_VPSEL_PA3:
stm32_configgpio(GPIO_OPAMP1_VINP_3);
regval |= OPAMP_CSR_VPSSEL_PA3;
break;
case OPAMP1_VPSEL_PA1:
stm32_configgpio(GPIO_OPAMP1_VINP_4);
regval |= OPAMP_CSR_VPSSEL_PA1;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP2
case 2:
{
switch (priv->vps_sel)
{
#ifndef CONFIG_STM32_STM32F33XX
case OPAMP2_VPSEL_PD14:
stm32_configgpio(GPIO_OPAMP2_VINP_1);
regval |= OPAMP_CSR_VPSSEL_PD14;
break;
#endif
case OPAMP2_VPSEL_PB14:
stm32_configgpio(GPIO_OPAMP2_VINP_2);
regval |= OPAMP_CSR_VPSSEL_PB14;
break;
case OPAMP2_VPSEL_PB0:
stm32_configgpio(GPIO_OPAMP2_VINP_3);
regval |= OPAMP_CSR_VPSSEL_PB0;
break;
case OPAMP2_VPSEL_PA7:
stm32_configgpio(GPIO_OPAMP2_VINP_4);
regval |= OPAMP_CSR_VPSSEL_PA7;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP3
case 3:
{
switch (priv->vps_sel)
{
case OPAMP3_VPSEL_PB13:
stm32_configgpio(GPIO_OPAMP3_VINP_1);
regval |= OPAMP_CSR_VPSSEL_PB13;
break;
case OPAMP3_VPSEL_PA5:
stm32_configgpio(GPIO_OPAMP3_VINP_2);
regval |= OPAMP_CSR_VPSSEL_PA5;
break;
case OPAMP3_VPSEL_PA1:
stm32_configgpio(GPIO_OPAMP3_VINP_3);
regval |= OPAMP_CSR_VPSSEL_PA1;
break;
case OPAMP3_VPSEL_PB0:
stm32_configgpio(GPIO_OPAMP3_VINP_4);
regval |= OPAMP_CSR_VPSSEL_PB0;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP4
case 4:
{
switch (priv->vps_sel)
{
case OPAMP4_VPSEL_PD11:
stm32_configgpio(GPIO_OPAMP4_VINP_1);
regval |= OPAMP_CSR_VPSSEL_PD11;
break;
case OPAMP4_VPSEL_PB11:
stm32_configgpio(GPIO_OPAMP4_VINP_2);
regval |= OPAMP_CSR_VPSSEL_PB11;
break;
case OPAMP4_VPSEL_PA4:
stm32_configgpio(GPIO_OPAMP4_VINP_3);
regval |= OPAMP_CSR_VPSSEL_PA4;
break;
case OPAMP4_VPSEL_PB13:
stm32_configgpio(GPIO_OPAMP4_VINP_4);
regval |= OPAMP_CSR_VPSSEL_PB13;
break;
default:
return -EINVAL;
}
break;
}
#endif
default:
return -EINVAL;
}
/* Configure inverting secondary input */
switch (index)
{
#ifdef CONFIG_STM32_OPAMP1
case 1:
{
switch (priv->vms_sel)
{
case OPAMP1_VSEL_PC5:
stm32_configgpio(GPIO_OPAMP1_VINM_1);
regval &= ~OPAMP_CSR_VMSSEL;
break;
case OPAMP1_VMSEL_PA3:
stm32_configgpio(GPIO_OPAMP1_VINM_2);
regval |= OPAMP_CSR_VMSSEL;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP2
case 2:
{
switch (priv->vms_sel)
{
case OPAMP2_VMSEL_PC5:
stm32_configgpio(GPIO_OPAMP2_VINM_1);
regval &= ~OPAMP_CSR_VMSSEL;
break;
case OPAMP2_VMSEL_PA5:
stm32_configgpio(GPIO_OPAMP2_VINM_2);
regval |= OPAMP_CSR_VMSSEL;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP3
case 3:
{
switch (priv->vms_sel)
{
case OPAMP3_VMSEL_PB10:
stm32_configgpio(GPIO_OPAMP3_VINM_1);
regval &= ~OPAMP_CSR_VMSSEL;
break;
case OPAMP3_VMSEL_PB2:
stm32_configgpio(GPIO_OPAMP3_VINM_2);
regval |= OPAMP_CSR_VMSSEL;
break;
default:
return -EINVAL;
}
break;
}
#endif
#ifdef CONFIG_STM32_OPAMP4
case 4:
{
switch (priv->vms_sel)
{
case OPAMP4_VMSEL_PB10:
stm32_configgpio(GPIO_OPAMP4_VINM_1);
regval &= ~OPAMP_CSR_VMSSEL;
break;
case OPAMP4_VMSEL_PD8:
stm32_configgpio(GPIO_OPAMP4_VINM_2);
regval |= OPAMP_CSR_VMSSEL;
break;
default:
return -EINVAL;
}
break;
}
#endif
default:
return -EINVAL;
}
}
/* Save CSR register */
opamp_putreg_csr(priv, regval);
/* Configure default gain in PGA mode */
stm32_opampgain_set(priv, priv->gain);
/* Enable OPAMP */
stm32_opampenable(priv, true);
/* TODO: OPAMP user calibration */
/* stm32_opampcalibrate(priv); */
/* Lock OPAMP if needed */
if (priv->lock == OPAMP_LOCK_RO)
{
stm32_opamplock(priv, true);
}
return OK;
}
/****************************************************************************
* Name: stm32_opampenable
*
* Description:
* Enable/disable OPAMP
*
* Input Parameters:
* priv - A reference to the OPAMP structure
* enable - enable/disable flag
*
* Returned Value:
* 0 on success, a negated errno value on failure
*
****************************************************************************/
static int stm32_opampenable(struct stm32_opamp_s *priv, bool enable)
{
bool lock;
ainfo("enable: %d\n", enable ? 1 : 0);
lock = stm32_opamplock_get(priv);
if (lock)
{
aerr("ERROR: OPAMP locked!\n");
return -EPERM;
}
else
{
if (enable)
{
/* Enable the OPAMP */
opamp_modify_csr(priv, 0, OPAMP_CSR_OPAMPEN);
}
else
{
/* Disable the OPAMP */
opamp_modify_csr(priv, OPAMP_CSR_OPAMPEN, 0);
}
}
return OK;
}
/****************************************************************************
* Name: stm32_opampgain_set
*
* Description:
* Set OPAMP gain
*
* Input Parameters:
* priv - A reference to the OPAMP structure
* gain - OPAMP gain
*
* Returned Value:
* 0 on success, a negated errno value on failure
*
****************************************************************************/
static int stm32_opampgain_set(struct stm32_opamp_s *priv, uint8_t gain)
{
bool lock;
uint32_t regval = 0;
lock = stm32_opamplock_get(priv);
if (lock)
{
aerr("ERROR: OPAMP locked!\n");
return -EPERM;
}
regval = opamp_getreg_csr(priv);
switch (gain)
{
case OPAMP_GAIN_2:
regval |= OPAMP_CSR_PGAGAIN_2;
break;
case OPAMP_GAIN_4:
regval |= OPAMP_CSR_PGAGAIN_4;
break;
case OPAMP_GAIN_8:
regval |= OPAMP_CSR_PGAGAIN_8;
break;
case OPAMP_GAIN_2_VM0:
regval |= OPAMP_CSR_PGAGAIN_2VM0;
break;
case OPAMP_GAIN_4_VM0:
regval |= OPAMP_CSR_PGAGAIN_4VM0;
break;
case OPAMP_GAIN_8_VM0:
regval |= OPAMP_CSR_PGAGAIN_8VM0;
break;
case OPAMP_GAIN_16_VM0:
regval |= OPAMP_CSR_PGAGAIN_16VM0;
break;
case OPAMP_GAIN_2_VM1:
regval |= OPAMP_CSR_PGAGAIN_2VM1;
break;
case OPAMP_GAIN_4_VM1:
regval |= OPAMP_CSR_PGAGAIN_4VM1;
break;
case OPAMP_GAIN_8_VM1:
regval |= OPAMP_CSR_PGAGAIN_8VM1;
break;
case OPAMP_GAIN_16_VM1:
regval |= OPAMP_CSR_PGAGAIN_16VM1;
break;
default:
aerr("ERROR: Unsupported OPAMP gain\n");
return -EINVAL;
}
/* Update gain in OPAMP device structure */
priv->gain = gain;
return OK;
}
#if 0
static int stm32_opampcalibrate(struct stm32_opamp_s *priv)
{
#warning "Missing logic"
return OK;
}
#endif
/****************************************************************************
* Name: opamp_shutdown
*
* Description:
* Disable the OPAMP. This method is called when the OPAMP device is
* closed. This method reverses the operation the setup method.
* Works only if OPAMP device is not locked.
*
* Input Parameters:
*
* Returned Value:
* None
*
****************************************************************************/
static void opamp_shutdown(struct opamp_dev_s *dev)
{
#warning "Missing logic"
}
/****************************************************************************
* Name: opamp_setup
*
* Description:
* Configure the OPAMP. This method is called the first time that the OPAMP
* device is opened. This will occur when the port is first opened.
* This setup includes configuring and attaching OPAMP interrupts.
* Interrupts are all disabled upon return.
*
* Input Parameters:
*
* Returned Value:
*
****************************************************************************/
static int opamp_setup(struct opamp_dev_s *dev)
{
#warning "Missing logic"
return OK;
}
/****************************************************************************
* Name: opamp_ioctl
*
* Description:
* All ioctl calls will be routed through this method.
*
* Input Parameters:
* dev - pointer to device structure used by the driver
* cmd - command
* arg - arguments passed with command
*
* Returned Value:
*
****************************************************************************/
static int opamp_ioctl(struct opamp_dev_s *dev, int cmd,
unsigned long arg)
{
#warning "Missing logic"
return -ENOTTY;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: stm32_opampinitialize
*
* Description:
* Initialize the OPAMP.
*
* Input Parameters:
* intf - The OPAMP interface number.
*
* Returned Value:
* Valid OPAMP device structure reference on success; a NULL on failure.
*
* Assumptions:
* 1. Clock to the OPAMP block has enabled,
* 2. Board-specific logic has already configured
*
****************************************************************************/
struct opamp_dev_s *stm32_opampinitialize(int intf)
{
struct opamp_dev_s *dev;
struct stm32_opamp_s *opamp;
int ret;
switch (intf)
{
#ifdef CONFIG_STM32_OPAMP1
case 1:
ainfo("OPAMP1 selected\n");
dev = &g_opamp1dev;
break;
#endif
#ifdef CONFIG_STM32_OPAMP2
case 2:
ainfo("OPAMP2 selected\n");
dev = &g_opamp2dev;
break;
#endif
#ifdef CONFIG_STM32_OPAMP3
case 3:
ainfo("OPAMP3 selected\n");
dev = &g_opamp3dev;
break;
#endif
#ifdef CONFIG_STM32_OPAMP4
case 4:
ainfo("OPAMP4 selected\n");
dev = &g_opamp4dev;
break;
#endif
default:
aerr("ERROR: No OPAMP interface defined\n");
return NULL;
}
/* Configure selected OPAMP */
opamp = dev->ad_priv;
ret = stm32_opampconfig(opamp);
if (ret < 0)
{
aerr("ERROR: Failed to initialize OPAMP%d: %d\n", intf, ret);
return NULL;
}
return dev;
}
#endif /* CONFIG_STM32_STM32F30XX || CONFIG_STM32_STM32F33XX*/
#endif /* CONFIG_STM32_OPAMP1 || CONFIG_STM32_OPAMP2 ||
* CONFIG_STM32_OPAMP3 || CONFIG_STM32_OPAMP4
*/
#endif /* CONFIG_STM32_OPAMP */