nuttx/arch/arm/src/sama5/sam_pio.c

864 lines
24 KiB
C

/****************************************************************************
* arch/arm/src/sama5/sam_pio.c
* General Purpose Input/Output (PIO) logic for the SAMA5
*
* Copyright (C) 2013-2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdint.h>
#include <time.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/arch.h>
#include <arch/board/board.h>
#include "up_internal.h"
#include "up_arch.h"
#include "chip/sam_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
****************************************************************************/
/* SAM_PION_VBASE will only be defined if the PIO register blocks are
* contiguous. If not defined, then we need to do a table lookup.
*/
#ifndef SAM_PION_VBASE
const uintptr_t g_piobase[SAM_NPIO] =
{
SAM_PIOA_VBASE, SAM_PIOB_VBASE, SAM_PIOC_VBASE, SAM_PIOD_VBASE,
SAM_PIOE_VBASE
};
#endif
/****************************************************************************
* Private Data
****************************************************************************/
/* Maps a port number to the standard port character */
#ifdef CONFIG_DEBUG_GPIO
static const char g_portchar[SAM_NPIO] =
{
'A', 'B', 'C', 'D', 'E'
};
#endif
/* Map a PIO number to the PIO peripheral identifier (PID) */
static const uint8_t g_piopid[SAM_NPIO] =
{
SAM_PID_PIOA, SAM_PID_PIOB, SAM_PID_PIOC, SAM_PID_PIOD, SAM_PID_PIOE
};
/* Used to determine if a PIO port is configured to support interrupts */
static const bool g_piointerrupt[SAM_NPIO] =
{
#ifdef CONFIG_SAMA5_PIOA_IRQ
true,
#else
false,
#endif
#ifdef CONFIG_SAMA5_PIOB_IRQ
true,
#else
false,
#endif
#ifdef CONFIG_SAMA5_PIOC_IRQ
true,
#else
false,
#endif
#ifdef CONFIG_SAMA5_PIOD_IRQ
true,
#else
false,
#endif
#ifdef CONFIG_SAMA5_PIOE_IRQ
true
#else
false
#endif
};
/* This is an array of ports that PIO enable forced on */
static uint32_t g_forced[SAM_NPIO];
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: sam_piobase
*
* Description:
* Return the base address of the PIO register set
*
****************************************************************************/
static inline uintptr_t sam_piobase(pio_pinset_t cfgset)
{
int port = (cfgset & PIO_PORT_MASK) >> PIO_PORT_SHIFT;
if (port < SAM_NPIO)
{
return sam_pion_vbase(port);
}
else
{
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_pio_enableclk
*
* Description:
* Enable clocking on the selected PIO
*
****************************************************************************/
static void sam_pio_enableclk(pio_pinset_t cfgset)
{
int port = (cfgset & PIO_PORT_MASK) >> PIO_PORT_SHIFT;
int pid;
if (port < SAM_NPIO)
{
/* Get the peripheral ID associated with the PIO port and enable
* clocking to the PIO block.
*/
pid = g_piopid[port];
if (pid < 32)
{
sam_enableperiph0(pid);
}
else
{
sam_enableperiph1(pid);
}
}
}
/****************************************************************************
* Name: sam_pio_disableclk
*
* Description:
* Disable clocking on the selected PIO if we can. We can that if:
*
* 1) No pins are configured as PIO inputs (peripheral inputs don't need
* clocking, and
* 2) Glitch and debounce filtering are not enabled. Currently, this can
* only happen if the the pin is a PIO input, but we may need to
* implement glitch filtering on peripheral inputs as well in the
* future???
* 3) The port is not configured for PIO interrupts. At present, the logic
* always keeps clocking on to ports that are configured for interrupts,
* but that could be dynamically controlled as well be keeping track
* of which PIOs have interrupts enabled.
*
* My! Wouldn't is be much easier to just keep all of the PIO clocks
* enabled? Is there a power management downside?
*
****************************************************************************/
static void sam_pio_disableclk(pio_pinset_t cfgset)
{
int port = (cfgset & PIO_PORT_MASK) >> PIO_PORT_SHIFT;
uintptr_t base;
int pid;
/* Leave clocking enabled for configured interrupt ports or for ports that
* have forced enabling of PIO clocking.
*/
if (port < SAM_NPIO && !g_piointerrupt[port] && g_forced[port] == 0)
{
/* Get the base address of the PIO port */
base = sam_pion_vbase(port);
/* Are any pins configured as PIO inputs?
*
* PSR - A bit set to "1" means that the corresponding pin is a PIO
* OSR - A bit set to "1" means that the corresponding pin is an output
*/
if ((getreg32(base + SAM_PIO_PSR_OFFSET) &
~getreg32(base + SAM_PIO_PSR_OFFSET)) == 0)
{
/* Any remaining configured pins are either not PIOs or all not
* PIO inputs. Disable clocking to this PIO block.
*
* Get the peripheral ID associated with the PIO port and disable
* clocking to the PIO block.
*/
pid = g_piopid[port];
if (pid < 32)
{
sam_disableperiph0(pid);
}
else
{
sam_disableperiph1(pid);
}
}
}
}
/****************************************************************************
* 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)
{
#if defined(PIO_HAVE_SCHMITT) || defined(PIO_HAVE_DRIVE)
uint32_t regval;
#endif
#if defined(PIO_HAVE_DRIVE)
uint32_t offset;
uint32_t mask;
uint32_t drive;
int shift;
#endif
/* Disable interrupts on the pin */
putreg32(pin, base + SAM_PIO_IDR_OFFSET);
/* Enable/disable the pull-up as requested */
if ((cfgset & PIO_CFG_PULLUP) != 0)
{
putreg32(pin, base + SAM_PIO_PUER_OFFSET);
}
else
{
putreg32(pin, base + SAM_PIO_PUDR_OFFSET);
}
#ifdef PIO_HAVE_PULLDOWN
/* Enable/disable the pull-down as requested */
if ((cfgset & PIO_CFG_PULLDOWN) != 0)
{
putreg32(pin, base + SAM_PIO_PPDER_OFFSET);
}
else
{
putreg32(pin, base + SAM_PIO_PPDDR_OFFSET);
}
#endif
/* Check if filtering should be enabled */
if ((cfgset & PIO_CFG_DEGLITCH) != 0)
{
putreg32(pin, base + SAM_PIO_IFER_OFFSET);
}
else
{
putreg32(pin, base + SAM_PIO_IFDR_OFFSET);
}
#ifdef PIO_HAVE_SCHMITT
/* Enable/disable the Schmitt trigger: Zero enables. Schmitt triggered
* inputs are enabled by default.
*/
regval = getreg32(base + SAM_PIO_SCHMITT_OFFSET);
if ((cfgset & PIO_CFG_SCHMITT) != 0)
{
regval &= ~pin;
}
else
{
regval |= pin;
}
putreg32(regval, base + SAM_PIO_SCHMITT_OFFSET);
#endif
#ifdef PIO_HAVE_DRIVE
/* Configure drive strength */
drive = (cfgset & PIO_DRIVE_MASK) >> PIO_DRIVE_SHIFT;
if (pin < 32)
{
offset = SAM_PIO_DRIVER1_OFFSET;
mask = PIO_DRIVER1_LINE_MASK(pin);
shift = PIO_DRIVER1_LINE_SHIFT(pin);
}
else
{
offset = SAM_PIO_DRIVER2_OFFSET;
mask = PIO_DRIVER2_LINE_MASK(pin);
shift = PIO_DRIVER2_LINE_SHIFT(pin);
}
regval = getreg32(base + offset);
regval &= ~mask;
regval |= drive << shift;
putreg32(regval, base + offset);
#endif
/* Clear some output only bits. Mostly this just simplifies debug. */
putreg32(pin, base + SAM_PIO_MDDR_OFFSET);
putreg32(pin, base + SAM_PIO_CODR_OFFSET);
/* Configure the pin as an input and enable the PIO function */
putreg32(pin, base + SAM_PIO_ODR_OFFSET);
putreg32(pin, base + SAM_PIO_PER_OFFSET);
/* To-Do: If DEGLITCH is selected, need to configure DIFSR, SCIFSR, and
* IFDGSR registers. This would probably best be done with
* another, new API... perhaps sam_configfilter()
*/
/* "Reading the I/O line levels requires the clock of the PIO Controller
* to be enabled, otherwise PIO_PDSR reads the levels present on the I/O
* line at the time the clock was disabled."
*/
sam_pio_enableclk(cfgset);
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)
{
/* Disable interrupts on the pin */
putreg32(pin, base + SAM_PIO_IDR_OFFSET);
/* Enable/disable the pull-up as requested */
if ((cfgset & PIO_CFG_PULLUP) != 0)
{
putreg32(pin, base + SAM_PIO_PUER_OFFSET);
}
else
{
putreg32(pin, base + SAM_PIO_PUDR_OFFSET);
}
#ifdef PIO_HAVE_PULLDOWN
/* Enable/disable the pull-down as requested */
if ((cfgset & PIO_CFG_PULLDOWN) != 0)
{
putreg32(pin, base + SAM_PIO_PPDER_OFFSET);
}
else
{
putreg32(pin, base + SAM_PIO_PPDDR_OFFSET);
}
#endif
/* Disable glitch filtering */
putreg32(pin, base + SAM_PIO_IFDR_OFFSET);
/* Enable the open drain driver if requested */
if ((cfgset & PIO_CFG_OPENDRAIN) != 0)
{
putreg32(pin, base + SAM_PIO_MDER_OFFSET);
}
else
{
putreg32(pin, base + SAM_PIO_MDDR_OFFSET);
}
/* 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(pin, base + SAM_PIO_OER_OFFSET);
putreg32(pin, base + SAM_PIO_PER_OFFSET);
/* Clocking to the PIO block may no longer be necessary. */
sam_pio_disableclk(cfgset);
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;
/* Disable interrupts on the pin */
putreg32(pin, base + SAM_PIO_IDR_OFFSET);
/* Enable/disable the pull-up as requested */
if ((cfgset & PIO_CFG_PULLUP) != 0)
{
putreg32(pin, base + SAM_PIO_PUER_OFFSET);
}
else
{
putreg32(pin, base + SAM_PIO_PUDR_OFFSET);
}
#ifdef PIO_HAVE_PULLDOWN
/* Enable/disable the pull-down as requested */
if ((cfgset & PIO_CFG_PULLDOWN) != 0)
{
putreg32(pin, base + SAM_PIO_PPDER_OFFSET);
}
else
{
putreg32(pin, base + SAM_PIO_PPDDR_OFFSET);
}
#endif
/* Disable glitch filtering */
putreg32(pin, base + SAM_PIO_IFDR_OFFSET);
#ifdef PIO_HAVE_PERIPHCD
/* Configure pin, depending upon the peripheral A, B, C or D
*
* PERIPHA: ABCDSR1[n] = 0 ABCDSR2[n] = 0
* PERIPHB: ABCDSR1[n] = 1 ABCDSR2[n] = 0
* PERIPHC: ABCDSR1[n] = 0 ABCDSR2[n] = 1
* PERIPHD: ABCDSR1[n] = 1 ABCDSR2[n] = 1
*/
regval = getreg32(base + SAM_PIO_ABCDSR1_OFFSET);
if ((cfgset & PIO_MODE_MASK) == PIO_PERIPHA ||
(cfgset & PIO_MODE_MASK) == PIO_PERIPHC)
{
regval &= ~pin;
}
else
{
regval |= pin;
}
putreg32(regval, base + SAM_PIO_ABCDSR1_OFFSET);
regval = getreg32(base + SAM_PIO_ABCDSR2_OFFSET);
if ((cfgset & PIO_MODE_MASK) == PIO_PERIPHA ||
(cfgset & PIO_MODE_MASK) == PIO_PERIPHB)
{
regval &= ~pin;
}
else
{
regval |= pin;
}
putreg32(regval, base + SAM_PIO_ABCDSR2_OFFSET);
#else
/* Configure pin, depending upon the peripheral A or B:
*
* PERIPHA: ABSR[n] = 0
* PERIPHB: ABSR[n] = 1
*/
regval = getreg32(base + SAM_PIO_ABSR_OFFSET);
if ((cfgset & PIO_MODE_MASK) == PIO_PERIPHA)
{
regval &= ~pin;
}
else
{
regval |= pin;
}
putreg32(regval, base + SAM_PIO_ABSR_OFFSET);
#endif
/* Disable PIO functionality */
putreg32(pin, base + SAM_PIO_PDR_OFFSET);
/* Clocking to the PIO block may no longer be necessary. */
sam_pio_disableclk(cfgset);
return OK;
}
/****************************************************************************
* Global 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;
/* Sanity check */
base = sam_piobase(cfgset);
if (base == 0)
{
return -EINVAL;
}
pin = sam_piopin(cfgset);
/* Disable interrupts to prohibit re-entrance. */
flags = irqsave();
/* Enable writing to PIO registers. The following registers are protected:
*
* - PIO Enable/Disable Registers (PER/PDR)
* - PIO Output Enable/Disable Registers (OER/ODR)
* - PIO Interrupt Security Level Register (ISLR)
* - PIO Input Filter Enable/Disable Registers (IFER/IFDR)
* - PIO Multi-driver Enable/Disable Registers (MDER/MDDR)
* - PIO Pull-Up Enable/Disable Registers (PUER/PUDR)
* - PIO Peripheral ABCD Select Register 1/2 (ABCDSR1/2)
* - PIO Output Write Enable/Disable Registers
* - PIO Pad Pull-Down Enable/Disable Registers (PPER/PPDR)
*
* I suspect that the default state is the WPMR is unprotected, so these
* operations could probably all be avoided.
*/
putreg32(PIO_WPMR_WPKEY, base + SAM_PIO_WPMR_OFFSET);
/* Put the pin in an intial state -- a vanilla input pint */
(void)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_PERIPHA:
case PIO_PERIPHB:
#ifdef PIO_HAVE_PERIPHCD
case PIO_PERIPHC:
case PIO_PERIPHD:
#endif
ret = sam_configperiph(base, pin, cfgset);
break;
default:
ret = -EINVAL;
break;
}
/* Disable writing to PIO registers */
putreg32(PIO_WPMR_WPEN | PIO_WPMR_WPKEY, base + SAM_PIO_WPMR_OFFSET);
irqrestore(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 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 the 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. This logic is overly conservative and does not enable PIO
* clocking unless necessary (PIO input selected, glitch/filtering enable, or PIO
* interrupts enabled). There are, however, certain conditions were we may want
* for force the PIO clock to be enabled. An example is reading the input value
* from an open drain output.
*
* The PIO automatic enable/disable logic is not smart enough enough to know about
* these cases. For those cases, sam_pio_forceclk() is provided.
*
************************************************************************************/
void sam_pio_forceclk(pio_pinset_t pinset, bool enable)
{
unsigned int port;
uint32_t pin;
irqstate_t flags;
/* Extract the port number */
port = (pinset & PIO_PORT_MASK) >> PIO_PORT_SHIFT;
pin = sam_piopin(pinset);
/* The remainder of this operation must be atomic */
flags = irqsave();
/* Are we enabling or disabling clocking */
if (enable)
{
/* Indicate that clocking is forced and enable the clock */
g_forced[port] |= pin;
sam_pio_enableclk(pinset);
}
else
{
/* Clocking is no longer forced for this pin */
g_forced[port] &= ~pin;
sam_pio_disableclk(pinset);
}
irqrestore(flags);
}
/************************************************************************************
* Function: sam_dumppio
*
* Description:
* Dump all PIO registers associated with the base address of the provided pinset.
*
************************************************************************************/
#ifdef CONFIG_DEBUG_GPIO
int sam_dumppio(uint32_t pinset, const char *msg)
{
irqstate_t flags;
uintptr_t base;
unsigned int port;
/* Get the base address associated with the PIO port */
port = (pinset & PIO_PORT_MASK) >> PIO_PORT_SHIFT;
base = sam_pion_vbase(port);
/* The following requires exclusive access to the PIO registers */
flags = irqsave();
lldbg("PIO%c pinset: %08x base: %08x -- %s\n",
g_portchar[port], pinset, base, msg);
#ifdef SAM_PIO_ISLR_OFFSET
lldbg(" PSR: %08x ISLR: %08x OSR: %08x IFSR: %08x\n",
getreg32(base + SAM_PIO_PSR_OFFSET), getreg32(base + SAM_PIO_ISLR_OFFSET),
getreg32(base + SAM_PIO_OSR_OFFSET), getreg32(base + SAM_PIO_IFSR_OFFSET));
#else
lldbg(" PSR: %08x OSR: %08x IFSR: %08x\n",
getreg32(base + SAM_PIO_PSR_OFFSET), getreg32(base + SAM_PIO_OSR_OFFSET),
getreg32(base + SAM_PIO_IFSR_OFFSET));
#endif
lldbg(" ODSR: %08x PDSR: %08x IMR: %08x ISR: %08x\n",
getreg32(base + SAM_PIO_ODSR_OFFSET), getreg32(base + SAM_PIO_PDSR_OFFSET),
getreg32(base + SAM_PIO_IMR_OFFSET), getreg32(base + SAM_PIO_ISR_OFFSET));
lldbg(" MDSR: %08x PUSR: %08x ABDCSR: %08x %08x\n",
getreg32(base + SAM_PIO_MDSR_OFFSET), getreg32(base + SAM_PIO_PUSR_OFFSET),
getreg32(base + SAM_PIO_ABCDSR1_OFFSET), getreg32(base + SAM_PIO_ABCDSR2_OFFSET));
lldbg(" IFSCSR: %08x SCDR: %08x PPDSR: %08x OWSR: %08x\n",
getreg32(base + SAM_PIO_IFSCSR_OFFSET), getreg32(base + SAM_PIO_SCDR_OFFSET),
getreg32(base + SAM_PIO_PPDSR_OFFSET), getreg32(base + SAM_PIO_OWSR_OFFSET));
#ifdef SAM_PIO_LOCKSR_OFFSET
lldbg(" AIMMR: %08x ELSR: %08x FRLHSR: %08x LOCKSR: %08x\n",
getreg32(base + SAM_PIO_AIMMR_OFFSET), getreg32(base + SAM_PIO_ELSR_OFFSET),
getreg32(base + SAM_PIO_FRLHSR_OFFSET), getreg32(base + SAM_PIO_LOCKSR_OFFSET));
#else
lldbg(" AIMMR: %08x ELSR: %08x FRLHSR: %08x\n",
getreg32(base + SAM_PIO_AIMMR_OFFSET), getreg32(base + SAM_PIO_ELSR_OFFSET),
getreg32(base + SAM_PIO_FRLHSR_OFFSET));
#endif
lldbg("SCHMITT: %08x DRIVER: %08x %08x\n",
getreg32(base + SAM_PIO_SCHMITT_OFFSET), getreg32(base + SAM_PIO_DRIVER1_OFFSET),
getreg32(base + SAM_PIO_DRIVER2_OFFSET));
lldbg(" WPMR: %08x WPSR: %08x\n",
getreg32(base + SAM_PIO_WPMR_OFFSET), getreg32(base + SAM_PIO_WPSR_OFFSET));
irqrestore(flags);
return OK;
}
#endif