/* * Copyright (c) 2015-2019, Texas Instruments Incorporated * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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. * * * Neither the name of Texas Instruments Incorporated 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. */ #include #include #include #include #include #include #include #include DeviceFamily_constructPath(inc/hw_memmap.h) #include DeviceFamily_constructPath(inc/hw_aon_event.h) #include DeviceFamily_constructPath(inc/hw_aon_ioc.h) #include DeviceFamily_constructPath(inc/hw_gpio.h) #include DeviceFamily_constructPath(inc/hw_ioc.h) #include DeviceFamily_constructPath(inc/hw_ints.h) #include DeviceFamily_constructPath(inc/hw_ccfg.h) #include DeviceFamily_constructPath(driverlib/driverlib_release.h) #include DeviceFamily_constructPath(driverlib/chipinfo.h) #if (DeviceFamily_PARENT == DeviceFamily_PARENT_CC13X0_CC26X0) #include DeviceFamily_constructPath(inc/hw_aon_sysctl.h) #elif (DeviceFamily_PARENT == DeviceFamily_PARENT_CC13X2_CC26X2) #include DeviceFamily_constructPath(inc/hw_aon_pmctl.h) #endif /*!***************************************************************************** * @file PINCC26XX.c * @brief Device-specific PIN & GPIO driver for CC26xx family [impl] * * # Overview # * This is the device-specific implementation of the generic PIN driver for the * CC26xx family of devices. * ******************************************************************************* */ // Maximum number of pins (# available depends on package configuration) #define MAX_NUM_PINS 31 /// Last DIO number available on package + device combination uint32_t pinUpperBound = 0; /// First DIO number available on package + device combination uint32_t pinLowerBound = 0; /// Array of handles, one per pin (pin id is index) PIN_Handle pinHandleTable[MAX_NUM_PINS]; /// Pointer to GPIO configuration set by PIN_init(...), save state in order to revert when PIN_close(...) static const PIN_Config *defaultPinConfig; /// Array of indexes into GPIO configuration defaultPinConfig, one per pin (pin id is index) static uint8_t pinGpioConfigTable[MAX_NUM_PINS]; /// HW interrupt structure for I/O interrupt handler static HwiP_Struct pinHwi; /// SWI structure for the followup of the I/O interrupt handler static SwiP_Struct pinSwi; /// PIN driver semaphore used to implement synchronicity for PIN_open() static SemaphoreP_Struct pinSemaphore; /// Hardware attribute structure populated in board.c to set HWI and SWI priorities extern const PINCC26XX_HWAttrs PINCC26XX_hwAttrs; // I/O SWI service routine that posts the callback in a SWI context static void PIN_swi(uintptr_t arg0, uintptr_t arg1){ uint32_t eventMask; unsigned char eventCounter; PIN_Handle handle; PIN_IntCb callbackFxn; // Get the OR'd trigger value representing all events values prior to running the SWI eventMask = SwiP_getTrigger(); // eventCounter cycles through all pins on the device up to the max number of pins for(eventCounter = 0; eventCounter <= pinUpperBound; eventCounter++){ // Check if current eventCounter bit is set in eventMask if(eventMask & (1 << eventCounter)){ // Get pin handle and registered callback function // Double paranthesis to supress GCC warning // Intentional assignment is intended, not the equality comparison if((handle = pinHandleTable[eventCounter])) { if((callbackFxn = handle->callbackFxn)) { // Event from existing pin, with an associated handle and a // registered callback -> call callback // Run the callback function in a SWI context. callbackFxn(handle, eventCounter); } } } } } // I/O HWI service routine static void PIN_hwi(uintptr_t arg) { uint32_t eventMask; // Get event flag with lowest index (also pin ID) eventMask = HWREG(GPIO_BASE + GPIO_O_EVFLAGS31_0); // Clear this event flag HWREG(GPIO_NONBUF_BASE + GPIO_O_EVFLAGS31_0) = eventMask; // Include all GPIO's currently triggered in the SWI SwiP_or(&(pinSwi), eventMask); } // Internal utility function for setting IOCFG register for pin static void PINCC26XX_setIoCfg(PIN_Config updateMask, PIN_Config pinCfg) { uint32_t tmpConfig; PIN_Id pinId = PIN_ID(pinCfg); bool invertChanges; if (pinCfg & PIN_GEN) { // Translate from device-independent to device-specific PIN_Config values pinCfg ^= PIN_GEN | PIN_BM_INPUT_EN | PIN_BM_PULLING; } // Get existing IOCFG, determine whether inversion changes, mask away what will be updated tmpConfig = HWREG(IOC_BASE + IOC_O_IOCFG0 + 4 * pinId); invertChanges = (tmpConfig ^ pinCfg) & updateMask & PINCC26XX_INV_INOUT; tmpConfig &= ~updateMask; // Insert what we want to update, possibly revert IRQ edges, write back to IOCFG tmpConfig |= (pinCfg & updateMask & PINCC26XX_BM_IOCFG); if ((updateMask & PINCC26XX_BM_IRQ) == PINCC26XX_BM_IRQ && (tmpConfig & PINCC26XX_INV_INOUT) == 0) { // We're changing IRQ options but inversion will not be enabled -> keep IRQ options } else if ((updateMask & PINCC26XX_BM_IRQ) == 0 && !invertChanges) { // We're not changing IRQ options and inversion remains unchanged -> keep IRQ options } else { // We're updating IRQ options and inversion will be enabled, OR // we're not updating IRQ options but inversion settings change // -> reverse polarity of edge detection when positive-only or negative-only switch (tmpConfig & PINCC26XX_BM_IRQ) { case PINCC26XX_IRQ_POSEDGE: tmpConfig &= ~PINCC26XX_BM_IRQ; tmpConfig |= PINCC26XX_IRQ_NEGEDGE; break; case PINCC26XX_IRQ_NEGEDGE: tmpConfig &= ~PINCC26XX_BM_IRQ; tmpConfig |= PINCC26XX_IRQ_POSEDGE; break; default: break; } } /* Clear any pending events from the previous pin configuration before we write the new interrupt settings */ PINCC26XX_clrPendInterrupt(pinId); HWREG(IOC_BASE + IOC_O_IOCFG0 + 4 * pinId) = tmpConfig; // Update GPIO output value and enable depending on previous output mode (enabled or disabled) { bool outputEnabled = (HWREG(GPIO_BASE + GPIO_O_DOE31_0) & (1 << pinId)) ? true : false; if(!outputEnabled) { if (updateMask & PINCC26XX_BM_GPIO_OUTPUT_VAL) { // Set GPIO output value HWREGB(GPIO_BASE + GPIO_O_DOUT3_0 + pinId) = (pinCfg & PINCC26XX_BM_GPIO_OUTPUT_VAL) ? 1 : 0; } } if (updateMask & PINCC26XX_BM_GPIO_OUTPUT_EN) { // Set GPIO output enable uint32_t key = HwiP_disable(); HWREG(GPIO_BASE + GPIO_O_DOE31_0) = (HWREG(GPIO_BASE + GPIO_O_DOE31_0) & ~(1 << pinId)) | ((pinCfg&PINCC26XX_BM_GPIO_OUTPUT_EN) ? (1 << pinId) : 0); HwiP_restore(key); } if(outputEnabled) { if (updateMask & PINCC26XX_BM_GPIO_OUTPUT_VAL) { // Set GPIO output value HWREGB(GPIO_BASE + GPIO_O_DOUT3_0 + pinId) = (pinCfg & PINCC26XX_BM_GPIO_OUTPUT_VAL) ? 1 : 0; } } } /* Clear any events from pin value changes as a result of the new configuration */ PINCC26XX_clrPendInterrupt(pinId); } // Internal utility function for setting mux setting in IOCFG register for pin static void PINCC26XX_setIoCfgMux(PIN_Id pinId, int32_t mux) { // Read in existing value in IOCFG register and update with supplied mux value if (mux < 0) { mux = PINCC26XX_MUX_GPIO; } uint32_t tmpConfig; tmpConfig = HWREG(IOC_BASE + IOC_O_IOCFG0 + 4 * pinId); tmpConfig &= ~IOC_IOCFG0_PORT_ID_M; tmpConfig |= mux & IOC_IOCFG0_PORT_ID_M; HWREG(IOC_BASE + IOC_O_IOCFG0 + 4 * pinId) = tmpConfig; } uint32_t PINCC26XX_getPinCount(){ // Get number of pins available on device (from HW register) uint32_t pinCount = (( HWREG( FCFG1_BASE + FCFG1_O_IOCONF ) & FCFG1_IOCONF_GPIO_CNT_M ) >> FCFG1_IOCONF_GPIO_CNT_S ) ; return pinCount; } PIN_Status PIN_init(const PIN_Config pinConfig[]) { uint32_t i; uint32_t pinConfigMask = 0; // Works as long as # pins <=32 HwiP_Params hwiParams; SwiP_Params swiParams; uint32_t reservedPinMask = 0; // Its ok if Power init has already been called. Power_init(); // Make sure we are using correct version of Driverlib DRIVERLIB_ASSERT_CURR_RELEASE(); /* pinLowerBound is initialized to 0 by default. * All cases where this is not the case are handled here. */ switch (ChipInfo_GetChipType()) { case CHIP_TYPE_CC1310: case CHIP_TYPE_CC1350: case CHIP_TYPE_CC1312: if (ChipInfo_GetPackageType() == PACKAGE_7x7) { pinLowerBound = 1; reservedPinMask |= 0x01; } break; case CHIP_TYPE_CC1352: pinLowerBound = 3; reservedPinMask |= 0x07; break; case CHIP_TYPE_CC1352P: pinLowerBound = 5; reservedPinMask |= 0x1F; break; } if ((HWREG(CCFG_BASE + CCFG_O_MODE_CONF) & CCFG_MODE_CONF_SCLK_LF_OPTION_M) >> CCFG_MODE_CONF_SCLK_LF_OPTION_S == 0x1) { // An IO is used for LF clock input reservedPinMask |= (1 << ((HWREG(CCFG_BASE+CCFG_O_EXT_LF_CLK) & CCFG_EXT_LF_CLK_DIO_M) >> CCFG_EXT_LF_CLK_DIO_S)); } // Get the last addressable DIO number pinUpperBound = pinLowerBound + PINCC26XX_getPinCount() - 1; // Initialize table of pins that have default GPIO configuration for (i = 0; i <= pinUpperBound; i++) { pinGpioConfigTable[i] = PIN_UNASSIGNED; } // Read in pinConfig list and create bitmask of which IOs to initialize to // default values and which to initialize from pinConfig for (i = 0, pinConfigMask = 0; PIN_ID(pinConfig[i]) != PIN_TERMINATE; i++) { // Ignore unassigned pins if (PIN_ID(pinConfig[i]) == PIN_UNASSIGNED) { continue; } // Check that pin exists and is available if (PIN_ID(pinConfig[i]) > pinUpperBound || PIN_ID(pinConfig[i]) < pinLowerBound || reservedPinMask & (1 << PIN_ID(pinConfig[i]))) { return PIN_NO_ACCESS; } // Mark pin as being in pinConfig pinConfigMask |= (1 << PIN_ID(pinConfig[i])); // For quick reference, store index i in table pinGpioConfigTable[PIN_ID(pinConfig[i])] = i; } // Set Power dependecies & constraints Power_setDependency(PowerCC26XX_PERIPH_GPIO); // Save GPIO default setup defaultPinConfig = pinConfig; // Setup semaphore for sequencing accesses to PIN_open() SemaphoreP_constructBinary(&pinSemaphore, 1); // Loop thru all pins and configure for (i = 0; i <= pinUpperBound; i++) { if (reservedPinMask & (1 << i)) { // Pin is reserved for other purposes -> setup dummy handle pinHandleTable[i] = (PIN_State*)0x00000004; } else { if (pinConfigMask & (1 << i)) { // Setup all pins in pinConfig as instructed PINCC26XX_setIoCfg(PIN_BM_ALL, pinConfig[pinGpioConfigTable[i]]); } else { // Setup all pins not in pinConfig to default configuration: // GPIO, input buffer disable, GPIO output disable, low GPIO output, no pull, no IRQ, no wakeup PINCC26XX_setIoCfg(PIN_BM_ALL, PIN_ID(i) | PINCC26XX_NOPULL); } // Set pin as GPIO and clear pin handle PINCC26XX_setIoCfgMux(PIN_ID(i), -1); pinHandleTable[i] = NULL; } } // Setup HWI handler HwiP_Params_init(&hwiParams); hwiParams.priority = PINCC26XX_hwAttrs.intPriority; HwiP_construct(&pinHwi, INT_AON_GPIO_EDGE, PIN_hwi, &hwiParams); // Setup SWI handler SwiP_Params_init(&(swiParams)); swiParams.priority = PINCC26XX_hwAttrs.swiPriority; swiParams.trigger = 0; SwiP_construct(&pinSwi, PIN_swi, &(swiParams)); // Setup interrupts so that they wake up from standby (use MCU_WU1) HWREG(AON_EVENT_BASE + AON_EVENT_O_MCUWUSEL) = (HWREG(AON_EVENT_BASE + AON_EVENT_O_MCUWUSEL) & (~AON_EVENT_MCUWUSEL_WU1_EV_M)) | AON_EVENT_MCUWUSEL_WU1_EV_PAD; // Open latches out to I/Os // This might be unnecessary, but isn't when you start from debugger HWREG(AON_IOC_BASE + AON_IOC_O_IOCLATCH) = AON_IOC_IOCLATCH_EN; // If we boot from shutdown, the IOs are latched, this opens the latches again #if (DeviceFamily_PARENT == DeviceFamily_PARENT_CC13X0_CC26X0) HWREG(AON_SYSCTL_BASE + AON_SYSCTL_O_SLEEPCTL) = AON_SYSCTL_SLEEPCTL_IO_PAD_SLEEP_DIS; #elif (DeviceFamily_PARENT == DeviceFamily_PARENT_CC13X2_CC26X2) HWREG(AON_PMCTL_BASE + AON_PMCTL_O_SLEEPCTL) = AON_PMCTL_SLEEPCTL_IO_PAD_SLEEP_DIS; #endif return PIN_SUCCESS; } PIN_Handle PIN_open(PIN_State* state, const PIN_Config pinList[]) { uint32_t i; bool pinsAllocated = true; uint32_t portMask = 0; PIN_Id pinId; if ((state == NULL) || (pinList == NULL)) { return (NULL); } // Ensure that only one client at a time can call PIN_open() or PIN_add() SemaphoreP_pend(&pinSemaphore, SemaphoreP_WAIT_FOREVER); // Check whether all pins in pinList are valid and available first for (i = 0; (pinId = PIN_ID(pinList[i])) != PIN_TERMINATE; i++) { /* Unassigned pins is allowed, but cannot generate a bitmask. */ if (pinId != PIN_UNASSIGNED) { if ((pinId > pinUpperBound) || (pinId >= MAX_NUM_PINS) || // For Klocwork pinHandleTable[pinId]) { pinsAllocated = false; break; } else { // Generate bitmask for port operations (always one port on CC26xx) portMask |= (1 << pinId); } } } if (!pinsAllocated) { // Indicate that the pins were not allocatable state = NULL; } else { // Setup state object state->callbackFxn = NULL; state->portMask = 0; state->userArg = 0; // Configure I/O pins according to pinList for (i = 0; (pinId = PIN_ID(pinList[i])) != PIN_TERMINATE; i++) { // Check pinId < MAX_NUM_PINS for Klocwork if ((pinId != PIN_UNASSIGNED) && (pinId < MAX_NUM_PINS)) { pinHandleTable[pinId] = state; state->portMask |= (1 << pinId); PIN_setConfig(state, PIN_BM_ALL, pinList[i]); } } } SemaphoreP_post(&pinSemaphore); return state; } PIN_Status PIN_add(PIN_Handle handle, PIN_Config pinCfg) { PIN_Status returnStatus; PIN_Id pinId = PIN_ID(pinCfg); // Check that handle and pinId is valid if (!handle || (pinId > pinUpperBound) || (pinId >= MAX_NUM_PINS)) { return PIN_NO_ACCESS; } // Ensure that only one client at a time can call PIN_open() or PIN_add() SemaphoreP_pend(&pinSemaphore, SemaphoreP_WAIT_FOREVER); // Check whether pin is available if (pinHandleTable[pinId]) { // Pin already allocated -> do nothing returnStatus = PIN_ALREADY_ALLOCATED; } else { // Allocate pin pinHandleTable[pinId] = handle; handle->portMask |= (1 << pinId); PIN_setConfig(handle, PIN_BM_ALL, pinCfg); returnStatus = PIN_SUCCESS; } SemaphoreP_post(&pinSemaphore); return returnStatus; } PIN_Status PIN_remove(PIN_Handle handle, PIN_Id pinId) { if (handle && (handle->portMask & (1 << pinId))) { // Deallocate pin handle->portMask &= ~(1 << pinId); pinHandleTable[pinId] = NULL; // Find GPIO default value and revert to it if (pinGpioConfigTable[pinId] == PIN_UNASSIGNED) { // Revert pin to default configuration: // GPIO, input buffer disable, GPIO output disable, low GPIO output, no pull, no IRQ, no wakeup PINCC26XX_setIoCfg(PIN_BM_ALL, PIN_ID(pinId) | PIN_INPUT_DIS); } else { // Revert pin to previous GPIO configuration PINCC26XX_setIoCfg(PIN_BM_ALL, defaultPinConfig[pinGpioConfigTable[pinId]]); } // Revert to GPIO PINCC26XX_setIoCfgMux(PIN_ID(pinId), -1); return PIN_SUCCESS; } else { return PIN_NO_ACCESS; } } void PIN_close(PIN_Handle handle) { uint32_t i; // No need for sequencing accesses to PIN_close() // For each pin in port bitmask while (handle->portMask) { // Find lowest index pin i = PIN_ctz(handle->portMask); // Deallocate pin PIN_remove(handle, i); } } uint32_t PIN_getInputValue(PIN_Id pinId) { return PINCC26XX_getInputValue(pinId); } PIN_Status PIN_setOutputEnable(PIN_Handle handle, PIN_Id pinId, bool outputEnable) { if (PIN_CHKEN && (pinId > pinUpperBound || pinHandleTable[pinId] != handle)) { // Non-existing pin or pin is not allocated to this client return PIN_NO_ACCESS; } PINCC26XX_setOutputEnable(pinId, outputEnable); return PIN_SUCCESS; } PIN_Status PIN_setOutputValue(PIN_Handle handle, PIN_Id pinId, uint32_t val) { if (PIN_CHKEN && (pinId > pinUpperBound || pinHandleTable[pinId] != handle)) { // Non-existing pin or pin is not allocated to this client return PIN_NO_ACCESS; } PINCC26XX_setOutputValue(pinId, val); return PIN_SUCCESS; } uint32_t PIN_getOutputValue(PIN_Id pinId) { return PINCC26XX_getOutputValue(pinId); } PIN_Status PIN_setInterrupt(PIN_Handle handle, PIN_Config pinCfg) { if (PIN_CHKEN && ((PIN_ID(pinCfg) > pinUpperBound) || (PIN_ID(pinCfg) >= MAX_NUM_PINS) || (pinHandleTable[PIN_ID(pinCfg)] != handle))) { // Non-existing pin or pin is not allocated to this client return PIN_NO_ACCESS; } PIN_setConfig(handle, PIN_BM_IRQ, pinCfg); return PIN_SUCCESS; } PIN_Status PIN_clrPendInterrupt(PIN_Handle handle, PIN_Id pinId) { if (PIN_CHKEN && ((pinId > pinUpperBound) || (pinId >= MAX_NUM_PINS) || (pinHandleTable[pinId] != handle))) { // Non-existing pin or pin is not allocated to this client return PIN_NO_ACCESS; } PINCC26XX_clrPendInterrupt(pinId); return PIN_SUCCESS; } PIN_Status PIN_registerIntCb(PIN_Handle handle, PIN_IntCb pCb) { if (handle) { handle->callbackFxn = pCb; return PIN_SUCCESS; } else { return PIN_NO_ACCESS; } } PIN_Config PIN_getConfig(PIN_Id pinId) { // Translate from device-specific to device independent PIN_Config values return PIN_GEN | (PINCC26XX_getConfig(pinId) ^ (PIN_BM_INPUT_EN | PIN_BM_PULLING)); } PIN_Status PIN_setConfig(PIN_Handle handle, PIN_Config updateMask, PIN_Config pinCfg) { if (PIN_CHKEN && (PIN_ID(pinCfg) > pinUpperBound || (PIN_ID(pinCfg) >= MAX_NUM_PINS) || (pinHandleTable[PIN_ID(pinCfg)] != handle))) { // Non-existing pin or pin is not allocated to this client return PIN_NO_ACCESS; } PINCC26XX_setIoCfg(updateMask, pinCfg); return PIN_SUCCESS; } PIN_Config PINCC26XX_getConfig(PIN_Id pinId) { // Get IOCFG register value and add in some extras: // * pinId // * pin GPIO output enable // * pin GPIO output value uint32_t tmpConfig; tmpConfig = HWREG(IOC_BASE + IOC_O_IOCFG0 + 4 * pinId); tmpConfig &= PINCC26XX_BM_IOCFG; tmpConfig |= PIN_ID(pinId); tmpConfig |= (PINCC26XX_getOutputValue(pinId)) ? PINCC26XX_GPIO_HIGH : PINCC26XX_GPIO_LOW; tmpConfig |= (HWREG(GPIO_BASE + GPIO_O_DOE31_0) & (1 << pinId)) ? PINCC26XX_GPIO_OUTPUT_EN : 0; return tmpConfig; } int32_t PINCC26XX_getMux(PIN_Id pinId) { if (PIN_CHKEN && pinId > pinUpperBound) { // Non-existing pin return PIN_NO_ACCESS; } int32_t tmpConfig; tmpConfig = HWREG(IOC_BASE + IOC_O_IOCFG0 + 4 * pinId); tmpConfig &= IOC_IOCFG0_PORT_ID_M; if (tmpConfig == PINCC26XX_MUX_GPIO) { tmpConfig = -1; } return tmpConfig; } PIN_Status PINCC26XX_setMux(PIN_Handle handle, PIN_Id pinId, int32_t mux) { // Add check for pinId >= MAX_NUM_PINS for Klocwork if (PIN_CHKEN && ((pinId > pinUpperBound) || (pinId >= MAX_NUM_PINS) || (pinHandleTable[pinId] != handle))) { // Non-existing pin or pin is not allocated to this client return PIN_NO_ACCESS; } PINCC26XX_setIoCfgMux(pinId, mux); return PIN_SUCCESS; } PIN_Status PINCC26XX_setWakeup(const PIN_Config pinConfig[]) { uint32_t i; // TODO: is this enough? for (i = 0; PIN_ID(pinConfig[i]) != PIN_TERMINATE; i++) { PINCC26XX_setIoCfg(PINCC26XX_BM_ALL, pinConfig[i]); } return PIN_SUCCESS; } uint32_t PIN_getPortMask(PIN_Handle handle) { // On CC26xx there is only one port encompassing all pins if (handle) { return handle->portMask; } else { return 0; } } uint32_t PIN_getPortInputValue(PIN_Handle handle) { return PINCC26XX_getPortInputValue(handle); } uint32_t PIN_getPortOutputValue(PIN_Handle handle) { return PINCC26XX_getPortOutputValue(handle); } PIN_Status PIN_setPortOutputValue(PIN_Handle handle, uint32_t bmOutVal) { if (PIN_CHKEN && (handle == NULL || pinHandleTable[PIN_ctz(handle->portMask)] != handle)) { return PIN_NO_ACCESS; } PINCC26XX_setPortOutputValue(handle, bmOutVal); return PIN_SUCCESS; } PIN_Status PIN_setPortOutputEnable(PIN_Handle handle, uint32_t bmOutEn) { if (PIN_CHKEN && (handle == NULL || pinHandleTable[PIN_ctz(handle->portMask)] != handle)) { return PIN_NO_ACCESS; } PINCC26XX_setPortOutputEnable(handle, bmOutEn); return PIN_SUCCESS; }