| /**************************************************************************** |
| * arch/arm/src/sama5/sama5d2x_pio.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * 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 <time.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/irq.h> |
| #include <nuttx/arch.h> |
| #include <arch/board/board.h> |
| |
| #include "arm_internal.h" |
| #include "hardware/_sama5d2x_pio.h" |
| |
| #include "chip.h" |
| #include "sam_periphclks.h" |
| #include "sam_pio.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Macros to convert a pin to a vanilla input */ |
| |
| #define PIO_INPUT_BITS (PIO_INPUT | PIO_CFG_DEFAULT) |
| #define MK_INPUT(p) (((p) & (PIO_PORT_MASK | PIO_PIN_MASK)) | PIO_INPUT_BITS) |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /* Lookup for non-secure PIOs */ |
| |
| const uintptr_t g_piobase[SAM_NPIO] = |
| { |
| SAM_PIO_IOGROUPA_VBASE |
| #if SAM_NPIO > 1 |
| , SAM_PIO_IOGROUPB_VBASE |
| #endif |
| #if SAM_NPIO > 2 |
| , SAM_PIO_IOGROUPC_VBASE |
| #endif |
| #if SAM_NPIO > 3 |
| , SAM_PIO_IOGROUPD_VBASE |
| #endif |
| }; |
| |
| /* Lookup for non-secure PIOs */ |
| |
| const uintptr_t g_spiobase[SAM_NPIO] = |
| { |
| SAM_SPIO_IOGROUPA_VBASE |
| #if SAM_NPIO > 1 |
| , SAM_SPIO_IOGROUPB_VBASE |
| #endif |
| #if SAM_NPIO > 2 |
| , SAM_SPIO_IOGROUPC_VBASE |
| #endif |
| #if SAM_NPIO > 3 |
| , SAM_SPIO_IOGROUPD_VBASE |
| #endif |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* Maps a port number to the standard port character */ |
| |
| #if defined(CONFIG_DEBUG_GPIO_INFO) && SAM_NPIO > 0 |
| static const char g_portchar[SAM_NPIO] = |
| { |
| 'A' |
| #if SAM_NPIO > 1 |
| , 'B' |
| #endif |
| #if SAM_NPIO > 2 |
| , 'C' |
| #endif |
| #if SAM_NPIO > 3 |
| , 'D' |
| #endif |
| }; |
| #endif |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: sam_issecure |
| * |
| * Description: |
| * Return true if the configuration selects a secure port. |
| * |
| ****************************************************************************/ |
| |
| static inline bool sam_issecure(pio_pinset_t cfgset) |
| { |
| return ((cfgset & PIO_INT_SECURE) != 0); |
| } |
| |
| /**************************************************************************** |
| * Name: sam_piobase |
| * |
| * Description: |
| * Return the base address of the PIO register set |
| * |
| ****************************************************************************/ |
| |
| static uintptr_t sam_piobase(pio_pinset_t cfgset) |
| { |
| int port = (cfgset & PIO_PORT_MASK) >> PIO_PORT_SHIFT; |
| |
| /* Verify that the port number is within range */ |
| |
| if (port < SAM_NPIO) |
| { |
| /* Is this a secure or an un-secured PIO? */ |
| |
| if (sam_issecure(cfgset)) |
| { |
| /* Return the base address of the secure PIO registers */ |
| |
| return sam_spion_vbase(port); |
| } |
| else |
| { |
| /* Return the base address of the un-secured PIO registers */ |
| |
| return sam_pion_vbase(port); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_piopin |
| * |
| * Description: |
| * Return a bitmask corresponding to the bit position in a PIO register |
| * |
| ****************************************************************************/ |
| |
| static inline uint32_t sam_piopin(pio_pinset_t cfgset) |
| { |
| return 1 << ((cfgset & PIO_PIN_MASK) >> PIO_PIN_SHIFT); |
| } |
| |
| /**************************************************************************** |
| * Name: sam_configcommon |
| * |
| * Description: |
| * Configure common PIO pin settings based on bit-encoded description of |
| * the pin. |
| * |
| ****************************************************************************/ |
| |
| static uint32_t sam_configcommon(pio_pinset_t cfgset) |
| { |
| uint32_t regval = 0; |
| |
| /* Enable/disable the pull-up as requested |
| * NOTE: Control of the pull-up resistor is possible regardless of the |
| * configuration of the I/O line (Input, Output, Open-drain). |
| */ |
| |
| if ((cfgset & PIO_CFG_PULLUP) != 0) |
| { |
| regval |= PIO_CFGR_PUEN; |
| } |
| |
| /* Enable/disable the pull-down as requested */ |
| |
| if ((cfgset & PIO_CFG_PULLDOWN) != 0) |
| { |
| regval |= PIO_CFGR_PDEN; |
| } |
| |
| /* Check if filtering should be enabled. |
| * NOTE: Input filtering and Schmitt triggering apply only to inputs. |
| */ |
| |
| if ((cfgset & PIO_CFG_DEGLITCH) != 0) |
| { |
| if ((cfgset & PIO_CFG_SLOWCLK) != 0) |
| { |
| regval |= (PIO_CFGR_IFEN | PIO_CFG_SLOWCLK); |
| } |
| else |
| { |
| regval |= PIO_CFGR_IFEN; |
| } |
| } |
| |
| /* Enable/disable the Schmitt trigger inputs. |
| * NOTE: Input filtering and Schmitt triggering apply only to inputs. |
| */ |
| |
| if ((cfgset & PIO_CFG_SCHMITT) != 0) |
| { |
| regval |= PIO_CFGR_SCHMITT; |
| } |
| |
| /* Enable the open drain driver if requested. |
| * NOTE: The open drain apply option applies only to output and |
| * peripheral pins. |
| */ |
| |
| if ((cfgset & PIO_CFG_OPENDRAIN) != 0) |
| { |
| regval |= PIO_CFGR_OPD; |
| } |
| |
| /* Select I/O drive. |
| * REVISIT: Doesn't rive strength apply only to output and peripheral |
| * pins as well? |
| */ |
| |
| switch (cfgset & PIO_DRIVE_MASK) |
| { |
| default: |
| case PIO_DRIVE_LOW: |
| regval |= PIO_CFGR_DRVSTR_LOW; |
| break; |
| |
| case PIO_DRIVE_MEDIUM: |
| regval |= PIO_CFGR_DRVSTR_MED; |
| break; |
| |
| case PIO_DRIVE_HIGH: |
| regval |= PIO_CFGR_DRVSTR_HIGH; |
| break; |
| } |
| |
| /* Select Input Event selection. |
| * NOTE: Only applies to input pins |
| */ |
| |
| switch (cfgset & PIO_INT_MASK) |
| { |
| default: |
| case PIO_INT_NONE: |
| break; |
| case PIO_INT_FALLING: |
| regval |= PIO_CFGR_EVTSEL_FALLING; |
| break; |
| case PIO_INT_RISING: |
| regval |= PIO_CFGR_EVTSEL_RISING; |
| break; |
| case PIO_INT_BOTHEDGES: |
| regval |= PIO_CFGR_EVTSEL_BOTH; |
| break; |
| case PIO_INT_LOWLEVEL: |
| regval |= PIO_CFGR_EVTSEL_LOW; |
| break; |
| case PIO_INT_HIGHLEVEL: |
| regval |= PIO_CFGR_EVTSEL_HIGH; |
| break; |
| } |
| |
| return regval; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_configinput |
| * |
| * Description: |
| * Configure a PIO input pin based on bit-encoded description of the pin. |
| * |
| ****************************************************************************/ |
| |
| static inline int sam_configinput(uintptr_t base, uint32_t pin, |
| pio_pinset_t cfgset) |
| { |
| uint32_t regval; |
| |
| /* Disable interrupts on the pin */ |
| |
| putreg32(pin, base + SAM_PIO_IDR_OFFSET); |
| |
| /* Select GPIO input */ |
| |
| regval = sam_configcommon(cfgset); |
| regval |= (PIO_CFGR_FUNC_GPIO | PIO_CFGR_DIR_INPUT); |
| |
| /* Clear some output only bits. Mostly this just simplifies debug. */ |
| |
| putreg32(pin, base + SAM_PIO_CODR_OFFSET); |
| |
| /* Configure the pin as an input and enable the PIO function */ |
| |
| putreg32(regval, base + SAM_PIO_CFGR_OFFSET); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_configoutput |
| * |
| * Description: |
| * Configure a PIO output pin based on bit-encoded description of the pin. |
| * |
| ****************************************************************************/ |
| |
| static inline int sam_configoutput(uintptr_t base, uint32_t pin, |
| pio_pinset_t cfgset) |
| { |
| uint32_t regval; |
| |
| /* Disable interrupts on the pin */ |
| |
| putreg32(pin, base + SAM_PIO_IDR_OFFSET); |
| |
| /* Select GPIO output */ |
| |
| regval = sam_configcommon(cfgset); |
| regval = (PIO_CFGR_FUNC_GPIO | PIO_CFGR_DIR_OUTPUT); |
| |
| /* Set default value. This is to be done before the pin is configured as |
| * an output in order to avoid any glitches at the time of the |
| * configuration. |
| */ |
| |
| if ((cfgset & PIO_OUTPUT_SET) != 0) |
| { |
| putreg32(pin, base + SAM_PIO_SODR_OFFSET); |
| } |
| else |
| { |
| putreg32(pin, base + SAM_PIO_CODR_OFFSET); |
| } |
| |
| /* Configure the pin as an output and enable the PIO function */ |
| |
| putreg32(regval, base + SAM_PIO_CFGR_OFFSET); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_configperiph |
| * |
| * Description: |
| * Configure a PIO pin driven by a peripheral A or B signal based on |
| * bit-encoded description of the pin. |
| * |
| ****************************************************************************/ |
| |
| static inline int sam_configperiph(uintptr_t base, uint32_t pin, |
| pio_pinset_t cfgset) |
| { |
| uint32_t regval; |
| unsigned int periph; |
| |
| /* Disable interrupts on the pin */ |
| |
| putreg32(pin, base + SAM_PIO_IDR_OFFSET); |
| |
| /* Select the peripheral function. The Direction bit does not apply to |
| * peripherals. |
| */ |
| |
| regval = sam_configcommon(cfgset); |
| periph = ((cfgset & PIO_MODE_MASK) - PIO_ANALOG) >> PIO_MODE_SHIFT; |
| regval |= PIO_CFGR_FUNC_PERIPH(periph); |
| |
| /* Clear some output only bits. Mostly this just simplifies debug. */ |
| |
| putreg32(pin, base + SAM_PIO_CODR_OFFSET); |
| |
| /* Configure the pin as a peripheral */ |
| |
| putreg32(regval, base + SAM_PIO_CFGR_OFFSET); |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: sam_configpio |
| * |
| * Description: |
| * Configure a PIO pin based on bit-encoded description of the pin. |
| * |
| ****************************************************************************/ |
| |
| int sam_configpio(pio_pinset_t cfgset) |
| { |
| uintptr_t base; |
| uint32_t pin; |
| irqstate_t flags; |
| int ret; |
| |
| /* Get the base address and pin mask associated with this pin |
| * configuration |
| */ |
| |
| base = sam_piobase(cfgset); |
| if (base == 0) |
| { |
| return -EINVAL; |
| } |
| |
| pin = sam_piopin(cfgset); |
| |
| /* Disable interrupts to prohibit re-entrance. */ |
| |
| flags = enter_critical_section(); |
| |
| /* Enable writing to PIO registers. |
| * |
| * The following registers are write-protected when WPEN is set in |
| * PIO_WPMR: |
| * - PIO Mask Register |
| * - PIO Configuration Register |
| * The following registers are write-protected when WPEN is set in |
| * S_PIO_WPMR: |
| * - Secure PIO Mask Register |
| * - Secure PIO Configuration Register |
| * - Secure PIO Slow Clock Divider Debouncing Register |
| * The following registers are write-protected when WPITEN is set in |
| * PIO_WPMR: |
| * - PIO Interrupt Enable Register |
| * - PIO Interrupt Disable Register |
| * The following registers are write-protected when WPITEN is set in |
| * S_PIO_WPMR: |
| * - Secure PIO Interrupt Enable Register |
| * - Secure PIO Interrupt Disable Register |
| * |
| * I suspect that the default state is the WPMR is unprotected, so these |
| * operations could probably all be avoided. |
| */ |
| |
| putreg32(PIO_WPMR_WPKEY, SAM_PIO_WPMR); |
| putreg32(PIO_WPMR_WPKEY, SAM_SPIO_WPMR); |
| |
| /* Select the secure or un-secured PIO operation */ |
| |
| #if 0 |
| if (sam_issecure(cfgset)) |
| { |
| putreg32(pin, base + SAM_SPIO_SIOSR_OFFSET); |
| } |
| else |
| #endif |
| { |
| putreg32(pin, base + SAM_SPIO_SIONR_OFFSET); |
| } |
| |
| /* Set the mask register to modify only the specific pin being |
| * configured. |
| */ |
| |
| putreg32(pin, base + SAM_PIO_MSKR_OFFSET); |
| |
| /* Put the pin in an initial state -- a vanilla input pin */ |
| |
| sam_configinput(base, pin, MK_INPUT(cfgset)); |
| |
| /* Then handle the real pin configuration according to pin type */ |
| |
| switch (cfgset & PIO_MODE_MASK) |
| { |
| case PIO_INPUT: |
| ret = sam_configinput(base, pin, cfgset); |
| break; |
| |
| case PIO_OUTPUT: |
| ret = sam_configoutput(base, pin, cfgset); |
| break; |
| |
| case PIO_ANALOG: |
| |
| /* REVISIT */ |
| |
| ret = OK; |
| break; |
| |
| case PIO_PERIPHA: |
| case PIO_PERIPHB: |
| case PIO_PERIPHC: |
| case PIO_PERIPHD: |
| case PIO_PERIPHE: |
| case PIO_PERIPHF: |
| case PIO_PERIPHG: |
| ret = sam_configperiph(base, pin, cfgset); |
| break; |
| |
| default: |
| ret = -EINVAL; |
| break; |
| } |
| |
| /* Disable writing to PIO registers */ |
| |
| putreg32(PIO_WPMR_WPEN | PIO_WPMR_WPITEN | PIO_WPMR_WPKEY, SAM_PIO_WPMR); |
| putreg32(PIO_WPMR_WPEN | PIO_WPMR_WPITEN | PIO_WPMR_WPKEY, SAM_SPIO_WPMR); |
| leave_critical_section(flags); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_piowrite |
| * |
| * Description: |
| * Write one or zero to the selected PIO pin |
| * |
| ****************************************************************************/ |
| |
| void sam_piowrite(pio_pinset_t pinset, bool value) |
| { |
| uintptr_t base = sam_piobase(pinset); |
| uint32_t pin = sam_piopin(pinset); |
| |
| if (base != 0) |
| { |
| /* Set or clear the output as requested. NOTE: that there is no |
| * check if the pin is actually configured as an output so this could, |
| * potentially, do nothing. |
| */ |
| |
| if (value) |
| { |
| putreg32(pin, base + SAM_PIO_SODR_OFFSET); |
| } |
| else |
| { |
| putreg32(pin, base + SAM_PIO_CODR_OFFSET); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: sam_pioread |
| * |
| * Description: |
| * Read one or zero from the selected PIO pin |
| * |
| ****************************************************************************/ |
| |
| bool sam_pioread(pio_pinset_t pinset) |
| { |
| uintptr_t base = sam_piobase(pinset); |
| uint32_t pin; |
| uint32_t regval; |
| |
| if (base != 0) |
| { |
| pin = sam_piopin(pinset); |
| |
| /* For output PIOs, the ODSR register provides the output value to |
| * drive the pin. The PDSR register, on the other hand, provides |
| * the current sensed value on a pin, whether the pin is configured |
| * as an input, an output or as a peripheral. |
| * |
| * There is small delay between the setting in ODSR and PDSR but |
| * otherwise they should be the same unless something external is |
| * driving the pin. |
| * |
| * Let's assume that PDSR is what the caller wants. |
| */ |
| |
| regval = getreg32(base + SAM_PIO_PDSR_OFFSET); |
| return (regval & pin) != 0; |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_pio_forceclk |
| * |
| * Description: |
| * Enable PIO clocking. Needed only for SAMA5D3/D4 compatibility. For the |
| * SAMA5D2, there is a common clock for all PIO ports and that clock is |
| * always enabled. |
| * |
| ****************************************************************************/ |
| |
| void sam_pio_forceclk(pio_pinset_t pinset, bool enable) |
| { |
| } |
| |
| /**************************************************************************** |
| * Function: sam_dumppio |
| * |
| * Description: |
| * Dump all PIO registers associated with the base address of the provided |
| * pinset. |
| * |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_DEBUG_GPIO_INFO |
| int sam_dumppio(uint32_t pinset, const char *msg) |
| { |
| irqstate_t flags; |
| uintptr_t base; |
| unsigned int port; |
| bool secure; |
| |
| /* Get the base address associated with the PIO port */ |
| |
| port = (pinset & PIO_PORT_MASK) >> PIO_PORT_SHIFT; |
| base = sam_piobase(pinset); |
| secure = sam_issecure(pinset); |
| |
| /* The following requires exclusive access to the PIO registers */ |
| |
| flags = enter_critical_section(); |
| |
| if (secure) |
| { |
| gpioinfo("SPIO%c pinset: %08x base: %08x -- %s\n", |
| g_portchar[port], pinset, base, msg); |
| } |
| else |
| { |
| gpioinfo("PIO%c pinset: %08x base: %08x -- %s\n", |
| g_portchar[port], pinset, base, msg); |
| } |
| |
| gpioinfo(" MSKR: %08x CFGR: %08x PDSR: %08x LOCKSR: %08x\n", |
| getreg32(base + SAM_PIO_MSKR_OFFSET), |
| getreg32(base + SAM_PIO_CFGR_OFFSET), |
| getreg32(base + SAM_PIO_PDSR_OFFSET), |
| getreg32(base + SAM_PIO_LOCKSR_OFFSET)); |
| gpioinfo(" ODSR: %08x IMR: %08x ISR: %08x\n", |
| getreg32(base + SAM_PIO_ODSR_OFFSET), |
| getreg32(base + SAM_PIO_IMR_OFFSET), |
| getreg32(base + SAM_PIO_ISR_OFFSET)); |
| |
| if (secure) |
| { |
| gpioinfo(" SCDR: %08x WPMR: %08x WPSR: %08x IOSSR: %08x\n", |
| getreg32(SAM_SPIO_SCDR), |
| getreg32(SAM_SPIO_WPMR), |
| getreg32(SAM_SPIO_WPSR), |
| getreg32(base + SAM_SPIO_IOSSR_OFFSET)); |
| } |
| else |
| { |
| gpioinfo(" WPMR: %08x WPSR: %08x\n", |
| getreg32(SAM_PIO_WPMR), getreg32(SAM_PIO_WPSR)); |
| } |
| |
| leave_critical_section(flags); |
| return OK; |
| } |
| #endif |