/*
 * 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 <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/dpl/SemaphoreP.h>
#include <ti/drivers/dpl/SwiP.h>

#include <ti/drivers/pin/PINCC26XX.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>

#include <ti/devices/DeviceFamily.h>
#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;
}
