From 6efc56241956344823d4ad6866dd5baf66ea1331 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Thu, 13 Feb 2014 12:44:22 -0600 Subject: [PATCH] SAMD20: Add port configuration logic --- arch/arm/src/samd/Make.defs | 2 +- arch/arm/src/samd/chip/sam_port.h | 5 + arch/arm/src/samd/sam_port.c | 549 ++++++++++++++++++++++++++++++ arch/arm/src/samd/sam_port.h | 47 +-- 4 files changed, 568 insertions(+), 35 deletions(-) create mode 100644 arch/arm/src/samd/sam_port.c diff --git a/arch/arm/src/samd/Make.defs b/arch/arm/src/samd/Make.defs index 05430923b2..acb5638fc4 100644 --- a/arch/arm/src/samd/Make.defs +++ b/arch/arm/src/samd/Make.defs @@ -68,7 +68,7 @@ CMN_CSRCS += up_dumpnvic.c endif CHIP_ASRCS = -CHIP_CSRCS = sam_idle.c sam_irq.c sam_start.c sam_timerisr.c +CHIP_CSRCS = sam_idle.c sam_irq.c sam_port.c sam_start.c sam_timerisr.c ifeq ($(CONFIG_NUTTX_KERNEL),y) CHIP_CSRCS += sam_userspace.c diff --git a/arch/arm/src/samd/chip/sam_port.h b/arch/arm/src/samd/chip/sam_port.h index 84778cdea1..082e845e40 100644 --- a/arch/arm/src/samd/chip/sam_port.h +++ b/arch/arm/src/samd/chip/sam_port.h @@ -49,6 +49,10 @@ ********************************************************************************************/ /* PORT register offsets ********************************************************************/ +#define SAM_PORTA (0) +#define SAM_PORTB (1) + +#define SAM_PORTN_OFFSET(n) (0x0000 + ((n) << 7)) #define SAM_PORTA_OFFSET 0x0000 /* Port A register offset */ #define SAM_PORTB_OFFSET 0x0080 /* Port B register offset */ @@ -118,6 +122,7 @@ /* PORT register addresses ******************************************************************/ +#define SAM_PORTN_BASE(n) (SAM_PORT_BASE+SAM_PORTN_OFFSET(n)) #define SAM_PORTA_BASE (SAM_PORT_BASE+SAM_PORTA_OFFSET) #define SAM_PORTB_BASE (SAM_PORT_BASE+SAM_PORTB_OFFSET) diff --git a/arch/arm/src/samd/sam_port.c b/arch/arm/src/samd/sam_port.c new file mode 100644 index 0000000000..d936fd7f1f --- /dev/null +++ b/arch/arm/src/samd/sam_port.c @@ -0,0 +1,549 @@ +/**************************************************************************** + * arch/arm/src/samd/sam_port.c + * + * Copyright (C) 2014 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 + +#include +#include +#include +#include + +#include +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" +#include "sam_port.h" +#include "chip/sam_port.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_PORT +static const char g_portchar[2] = { 'A', 'B' }; +#endif + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ +/**************************************************************************** + * Name: sam_portbase + * + * Description: + * Return the base address of the PORT register set + * + ****************************************************************************/ + +static inline uintptr_t sam_portbase(port_pinset_t pinset) +{ + int port = (pinset & PORT_MASK) >> PORT_SHIFT; + return SAM_PORTN_BASE(port); +} + +/**************************************************************************** + * Name: sam_portpin + * + * Description: + * Returun the base address of the PORT register set + * + ****************************************************************************/ + +static inline int sam_portpin(port_pinset_t pinset) +{ + return 1 << ((pinset & PORT_PIN_MASK) >> PORT_PIN_SHIFT); +} + +/**************************************************************************** + * Name: sam_configinput + * + * Description: + * Configure a PORT input pin based on bit-encoded description of the pin. + * This function serves the dual role of putting all pins into a known, + * initial state. Hence, it is overkill for what really needs to be done. + * + * Assumption: + * sam_configreset has been called to put the pin into the default reset + * state. + * + ****************************************************************************/ + +static inline void sam_configinput(uintptr_t base, port_pinset_t pinset) +{ + uint32_t regval; + uint32_t bit; + int pin; + + /* Decode pin information */ + + pin = (pinset & PORT_PIN_MASK) >> PORT_PIN_SHIFT; + bit = (1 << pin); + + /* Direction bit is already zero (input) */ + /* Enable the I/O synchronizer? */ + + if ((pinset & PORT_SYNCHRONIZER_MASK) == PORT_SYNCHRONIZER_ON) + { + regval = getreg32(base + SAM_PORT_CTRL_OFFSET); + regval |= bit; + putreg32(regval, base + SAM_PORT_CTRL_OFFSET); + } + + /* Set the pin configuration */ + + regval = (PORT_WRCONFIG_WRPINCFG | PORT_WRCONFIG_WRPMUX | PORT_WRCONFIG_INEN); + if (pin > 16) + { + /* Select the upper half word and adjust the bit setting */ + + regval = PORT_WRCONFIG_HWSEL; + pin -= 16; + } + + regval |= PORT_WRCONFIG_PINMASK(pin); + + /* Check for pull-up/down selection */ + + switch (pinset & PORT_PULL_MASK) + { + case PORT_PULL_UP: + { + /* Select pull-up by setting the corresponding bit in OUT + * register. + */ + + putreg32(bit, base + SAM_PORT_OUTSET_OFFSET); + } + /* Fall through */ + + case PORT_PULL_DOWN: + { + regval |= PORT_WRCONFIG_PULLEN; + } + break; + + default: + case PORT_PULL_NONE: + break; + } + + /* Configure the pin as an input */ + + putreg32(regval, base + SAM_PORT_WRCONFIG_OFFSET); +} + +/**************************************************************************** + * Name: sam_configinterrupt + * + * Description: + * Configure a PORT interrupt pin based on bit-encoded description of the + * pin. + * + * Assumption: + * sam_configreset has been called to put the pin into the default reset + * state: + * + ****************************************************************************/ + +static inline void sam_configinterrupt(uintptr_t base, port_pinset_t pinset) +{ +#warning Missing logic +} + +/**************************************************************************** + * Name: sam_configoutput + * + * Description: + * Configure a PORT output pin based on bit-encoded description of the pin. + * + * Assumption: + * sam_configreset has been called to put the pin into the default reset + * state. + * + ****************************************************************************/ + +static inline void sam_configoutput(uintptr_t base, port_pinset_t pinset) +{ + uint32_t regval; + uint32_t bit; + int pin; + + /* Decode pin information */ + + pin = (pinset & PORT_PIN_MASK) >> PORT_PIN_SHIFT; + bit = (1 << pin); + + /* Set the direction bit to configure the pin as an input */ + + putreg32(bit, base + SAM_PORT_DIRSET_OFFSET); + + /* Set the initial output value to high? */ + + if ((pinset & PORT_OUTVALUE_MASK) == PORT_OUTPUT_SET) + { + putreg32(bit, base + SAM_PORT_OUTSET_OFFSET); + } + + /* Set the pin configuration. This will be an output with the input + * buffer enabled. + */ + + regval = (PORT_WRCONFIG_WRPINCFG | PORT_WRCONFIG_WRPMUX | PORT_WRCONFIG_INEN); + if (pin > 16) + { + /* Select the upper half word and adjust the bit setting */ + + regval = PORT_WRCONFIG_HWSEL; + pin -= 16; + } + + regval |= PORT_WRCONFIG_PINMASK(pin); + + /* Check for pull-up/down selection */ + + switch (pinset & PORT_PULL_MASK) + { + case PORT_PULL_UP: + { + /* Select pull-up by setting the corresponding bit in OUT + * register. + */ + + putreg32(bit, base + SAM_PORT_OUTSET_OFFSET); + } + /* Fall through */ + + case PORT_PULL_DOWN: + { + regval |= PORT_WRCONFIG_PULLEN; + } + break; + + default: + case PORT_PULL_NONE: + break; + } + + /* Select higher strength drive? */ + + if ((pinset & PORT_DRIVE_MASK) == PORT_DRIVE_HIGH) + { + regval |= PORT_WRCONFIG_DRVSTR; + } + + /* Configure the pin as an output */ + + putreg32(regval, base + SAM_PORT_WRCONFIG_OFFSET); +} + +/**************************************************************************** + * Name: sam_configperiph + * + * Description: + * Configure a PORT pin driven by a peripheral based on bit-encoded + * description of the pin. + * + * Assumption: + * sam_configreset has been called to put the pin into the default reset + * state. + * + ****************************************************************************/ + +static inline void sam_configperiph(uintptr_t base, port_pinset_t pinset) +{ + uint32_t regval; + uint32_t bit; + uint32_t func; + int pin; + + /* Decode pin information */ + + pin = (pinset & PORT_PIN_MASK) >> PORT_PIN_SHIFT; + bit = (1 << pin); + + /* Set the pin configuration. This will be an peripheral with the + * selected function. + */ + + regval = (PORT_WRCONFIG_WRPINCFG | PORT_WRCONFIG_WRPMUX | PORT_WRCONFIG_PMUXEN); + if (pin > 16) + { + /* Select the upper half word and adjust the bit setting */ + + regval = PORT_WRCONFIG_HWSEL; + pin -= 16; + } + + regval |= PORT_WRCONFIG_PINMASK(pin); + + /* Set the pin function */ + + func = (pinset & PORT_FUNC_MASK) >> PORT_FUNC_SHIFT; + regval = (func << PORT_WRCONFIG_PMUX_SHIFT); + + /* Check for pull-up/down selection */ + + switch (pinset & PORT_PULL_MASK) + { + case PORT_PULL_UP: + { + /* Select pull-up by setting the corresponding bit in OUT + * register. + */ + + putreg32(bit, base + SAM_PORT_OUTSET_OFFSET); + } + /* Fall through */ + + case PORT_PULL_DOWN: + { + regval |= PORT_WRCONFIG_PULLEN; + } + break; + + default: + case PORT_PULL_NONE: + break; + } + + /* Configure the pin for the peripheral function */ + + putreg32(regval, base + SAM_PORT_WRCONFIG_OFFSET); +} + +/**************************************************************************** + * Name: sam_configreset + * + * Description: + * Configure a PORT pin in the default, reset state. + * + ****************************************************************************/ + +static inline void sam_configreset(uintptr_t base, port_pinset_t pinset) +{ + uint32_t regval; + uint32_t bit; + int pin; + + /* Decode pin information */ + + pin = (pinset & PORT_PIN_MASK) >> PORT_PIN_SHIFT; + bit = (1 << pin); + + /* Set the direction bit to zero (input) */ + + putreg32(bit, base + SAM_PORT_DIRCLR_OFFSET); + + /* Disable the I/O synchronizer */ + + regval = getreg32(base + SAM_PORT_CTRL_OFFSET); + regval &= ~bit; + putreg32(regval, base + SAM_PORT_CTRL_OFFSET); + + /* Assume input pull-down or output value low */ + + putreg32(bit, base + SAM_PORT_OUTCLR_OFFSET); + + /* Set the pin configuration */ + + regval = (PORT_WRCONFIG_WRPINCFG | PORT_WRCONFIG_WRPMUX); + if (pin > 16) + { + /* Select the upper half word and adjust the bit setting */ + + regval = PORT_WRCONFIG_HWSEL; + pin -= 16; + } + + regval |= PORT_WRCONFIG_PINMASK(pin); + + /* Disable the peripheral multiplexor, disable the input, disable + * pull-up/down, reset driver strength, etc. + */ + + putreg32(regval, base + SAM_PORT_WRCONFIG_OFFSET); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sam_configport + * + * Description: + * Configure a PORT pin based on bit-encoded description of the pin. + * + ****************************************************************************/ + +int sam_configport(port_pinset_t pinset) +{ + uintptr_t base = sam_portbase(pinset); + irqstate_t flags; + + /* Make sure that all operations on the port are atomic */ + + flags = irqsave(); + + /* Put the PORT in the known, reset state. */ + + sam_configreset(base, pinset); + + /* Then put the PORT into the requested state */ + + switch (pinset & PORT_MODE_MASK) + { + case PORT_INPUT: + sam_configinput(base, pinset); + break; + + case PORT_OUTPUT: + sam_configoutput(base, pinset); + break; + + case PORT_PERIPHERAL: + sam_configperiph(base, pinset); + break; + + case PORT_INTERRUPT: + sam_configinterrupt(base, pinset); + break; + + default: + break; + } + + irqrestore(flags); + return OK; +} + +/**************************************************************************** + * Name: sam_portwrite + * + * Description: + * Write one or zero to the selected PORT pin + * + ****************************************************************************/ + +void sam_portwrite(port_pinset_t pinset, bool value) +{ + uintptr_t base = sam_portbase(pinset); + uint32_t pin = sam_portpin(pinset); + + if (value) + { + putreg32(pin, base + SAM_PORT_OUTSET_OFFSET); + } + else + { + putreg32(pin, base + SAM_PORT_OUTCLR_OFFSET); + } +} + +/**************************************************************************** + * Name: sam_portread + * + * Description: + * Read one or zero from the selected PORT pin + * + ****************************************************************************/ + +bool sam_portread(port_pinset_t pinset) +{ + uintptr_t base = sam_portbase(pinset); + uint32_t pin = sam_portpin(pinset); + + return (getreg32(base + SAM_PORT_IN_OFFSET) & pin) != 0; +} + +/************************************************************************************ + * Function: sam_dumpport + * + * Description: + * Dump all PORT registers associated with the base address of the provided pinset. + * + ************************************************************************************/ + +#ifdef CONFIG_DEBUG_PORT +int sam_dumpport(uint32_t pinset, const char *msg) +{ + irqstate_t flags; + uintptr_t base; + unsigned int pin; + unsigned int port; + + /* Get the base address associated with the PIO port */ + + pin = sam_portpin(pinset); + port = (pinset & PORT_MASK) >> PORT_SHIFT; + base = SAM_PORTN_BASE(port); + + /* The following requires exclusive access to the PORT registers */ + + flags = irqsave(); + lldbg("PORT%c pinset: %08x base: %08x -- %s\n", + g_portchar[port], pinset, base, msg); + lldbg(" DIR: %08x OUT: %08x IN: %08x\n", + getreg32(base + SAM_PORT_DIR_OFFSET), + getreg32(base + SAM_PORT_OUT_OFFSET), + getreg32(base + SAM_PORT_IN_OFFSET)); + lldbg(" CTRL: %08x WRCONFIG: %08x\n", + getreg32(base + SAM_PORT_CTRL_OFFSET), + getreg32(base + SAM_PORT_WRCONFIG_OFFSET)); + lldbg(" PMUX[%08x]: %02x PINCFG[%08x]: %02x\n", + base + SAM_PORT_PMUX_OFFSET(pin), + getreg8(base + SAM_PORT_PMUX_OFFSET(pin)), + base + SAM_PORT_PINCFG_OFFSET(pin), + getreg8(base + SAM_PORT_PINCFG_OFFSET(pin))); + + irqrestore(flags); + return OK; +} +#endif + diff --git a/arch/arm/src/samd/sam_port.h b/arch/arm/src/samd/sam_port.h index a9223b05a4..dce1d52c13 100644 --- a/arch/arm/src/samd/sam_port.h +++ b/arch/arm/src/samd/sam_port.h @@ -67,11 +67,11 @@ * 2222 1111 1111 1100 0000 0000 * 3210 9876 5432 1098 7654 3210 * ------------ ----------------------------- - * PORT Input: MMRR .... .SB. II.. ..PB BBBB + * PORT Input: MMRR .... .S.. .... ..PB BBBB * PORT Output: MM.. .... D..V .... ..PB BBBB * Peripheral: MM.. FFF. .... II.. ..PB BBBB * ------------ ----------------------------- - * MMRR FFF. DSBV II.. ..PB BBBB + * MMRR FFF. DS.V II.. ..PB BBBB */ /* Input/output/peripheral mode: @@ -101,7 +101,7 @@ * 3210 9876 5432 1098 7654 3210 * ------------ ----------------------------- * PORT Input: ..RR .... .... .... .... .... - * PORT Output: .... .... .... .... .... .... + * PORT Output: ..RR .... .... .... .... .... * Peripheral: .... .... .... .... .... .... */ @@ -189,23 +189,6 @@ # define PORT_SYNCHRONIZER_OFF (0 << PORT_SYNCHRONIZER_SHIFT) # define PORT_SYNCHRONIZER_ON (1 << PORT_SYNCHRONIZER_SHIFT) -/* Input buffering - * - * MODE BITFIELDS - * ------------ ----------------------------- - * 2222 1111 1111 1100 0000 0000 - * 3210 9876 5432 1098 7654 3210 - * ------------ ----------------------------- - * PORT Input: .... .... ..B. .... .... .... - * PORT Output: .... .... .... .... .... .... - * Peripheral: .... .... .... .... .... .... - */ - -#define PORT_INBUFFER_SHIFT (13) /* Bit 13: Input buffer enable */ -#define PORT_INBUFFER_MASK (1 << PORT_SYNCHRONIZER_SHIFT) -# define PORT_INBUFFER_OFF (0 << PORT_SYNCHRONIZER_SHIFT) -# define PORT_INBUFFER_ON (1 << PORT_SYNCHRONIZER_SHIFT) - /* If the pin is an PORT output, then this identifies the initial output value: * * MODE BITFIELDS @@ -223,14 +206,14 @@ # define PORT_OUTPUT_CLEAR (0 << PORT_SYNCHRONIZER_SHIFT) # define PORT_OUTPUT_SET (1 << PORT_SYNCHRONIZER_SHIFT) -/* Selections for an interrupting input and peripheral events: +/* Selections for external interrupts: * * MODE BITFIELDS * ------------ ----------------------------- * 2222 1111 1111 1100 0000 0000 * 3210 9876 5432 1098 7654 3210 * ------------ ----------------------------- - * PORT Input: .... .... .... II.. .... .... + * PORT Input: .... .... .... .... .... .... * PORT Output: .... .... .... .... .... .... * Peripheral: .... .... .... II.. .... .... */ @@ -255,8 +238,8 @@ #define PORT_SHIFT (5) /* Bit 5: Port number */ #define PORT_MASK (1 << PORT_SHIFT) -# define PORTA (0 << PORT_SHIFT) -# define PORTB (1 << PORT_SHIFT) +# define PORTA (SAM_PORTA << PORT_SHIFT) +# define PORTB (SAM_PORTB << PORT_SHIFT) /* This identifies the bit in the port: * @@ -309,7 +292,7 @@ * Public Types ****************************************************************************/ -typedef uint32_t port_cfgset_t; +typedef uint32_t port_pinset_t; /**************************************************************************** * Public Data @@ -335,17 +318,13 @@ extern "C" * * Description: * Configure a PORT pin based on bit-encoded description of the pin. - * Once it is configured as Alternative (PORT_ALT|PORT_CNF_AFPP|...) - * function, it must be unconfigured with sam_unconfigport() with - * the same cfgset first before it can be set to non-alternative function. * * Returns: - * OK on success - * ERROR on invalid port, or when pin is locked as ALT function. + * OK (always) * ****************************************************************************/ -int sam_configport(port_cfgset_t cfgset); +int sam_configport(port_pinset_t pinset); /**************************************************************************** * Name: sam_portwrite @@ -355,7 +334,7 @@ int sam_configport(port_cfgset_t cfgset); * ****************************************************************************/ -void sam_portwrite(port_cfgset_t pinset, bool value); +void sam_portwrite(port_pinset_t pinset, bool value); /**************************************************************************** * Name: sam_portread @@ -365,7 +344,7 @@ void sam_portwrite(port_cfgset_t pinset, bool value); * ****************************************************************************/ -bool sam_portread(port_cfgset_t pinset); +bool sam_portread(port_pinset_t pinset); /**************************************************************************** * Function: sam_dumpport @@ -377,7 +356,7 @@ bool sam_portread(port_cfgset_t pinset); ****************************************************************************/ #ifdef CONFIG_DEBUG -void sam_dumpport(port_cfgset_t pinset, const char *msg); +void sam_dumpport(port_pinset_t pinset, const char *msg); #else # define sam_dumpport(p,m) #endif