| /**************************************************************************** |
| * 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 */ |