blob: 66c25466229e0500a26aa1ab0f0e6c82a9cf70ba [file] [log] [blame]
/*
* Copyright (c) 2015-2016, 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 <stdint.h>
#include <stdbool.h>
#if defined(__IAR_SYSTEMS_ICC__)
#include <intrinsics.h>
#endif
/*
* By default disable both asserts and log for this module.
* This must be done before DebugP.h is included.
*/
#ifndef DebugP_ASSERT_ENABLED
#define DebugP_ASSERT_ENABLED 0
#endif
#ifndef DebugP_LOG_ENABLED
#define DebugP_LOG_ENABLED 0
#endif
#include <ti/drivers/GPIO.h>
#include <ti/drivers/gpio/GPIOCC32XX.h>
#include <ti/drivers/dpl/DebugP.h>
#include <ti/drivers/dpl/HwiP.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC32XX.h>
/* driverlib header files */
#include <ti/devices/cc32xx/inc/hw_types.h>
#include <ti/devices/cc32xx/inc/hw_memmap.h>
#include <ti/devices/cc32xx/inc/hw_gpio.h>
#include <ti/devices/cc32xx/inc/hw_ints.h>
#include <ti/devices/cc32xx/driverlib/rom.h>
#include <ti/devices/cc32xx/driverlib/rom_map.h>
#include <ti/devices/cc32xx/driverlib/gpio.h>
#include <ti/devices/cc32xx/driverlib/pin.h>
#include <ti/devices/cc32xx/driverlib/prcm.h>
/*
* Map GPIO_INT types to corresponding CC32XX interrupt options
*/
static const uint8_t interruptType[] = {
0, /* Undefined interrupt type */
GPIO_FALLING_EDGE, /* 1 = Interrupt on falling edge */
GPIO_RISING_EDGE, /* 2 = Interrupt on rising edge */
GPIO_BOTH_EDGES, /* 3 = Interrupt on both edges */
GPIO_LOW_LEVEL, /* 4 = Interrupt on low level */
GPIO_HIGH_LEVEL /* 5 = Interrupt on high level */
};
/*
* Table of port interrupt vector numbers
* Used by setCallback() to create Hwis.
* Up to 4 port interrupts must be supported
*/
static const uint8_t portInterruptIds[] = {
INT_GPIOA0, INT_GPIOA1,
INT_GPIOA2, INT_GPIOA3
};
/* Table of GPIO input types */
const uint16_t inPinTypes [] = {
PIN_TYPE_STD, /* GPIO_CFG_IN_NOPULL */
PIN_TYPE_STD_PU, /* GPIO_CFG_IN_PU */
PIN_TYPE_STD_PD /* GPIO_CFG_IN_PD */
};
/* Table of GPIO output types */
const uint16_t outPinTypes [] = {
PIN_TYPE_STD, /* GPIO_CFG_OUT_STD */
PIN_TYPE_OD, /* GPIO_CFG_OUT_OD_NOPULL */
PIN_TYPE_OD_PU, /* GPIO_CFG_OUT_OD_PU */
PIN_TYPE_OD_PD /* GPIO_CFG_OUT_OD_PD */
};
/* Table of GPIO drive strengths */
const uint16_t outPinStrengths [] = {
PIN_STRENGTH_2MA, /* GPIO_CFG_OUT_STR_LOW */
PIN_STRENGTH_4MA, /* GPIO_CFG_OUT_STR_MED */
PIN_STRENGTH_6MA /* GPIO_CFG_OUT_STR_HIGH */
};
/*
* Table of port bases address. For use with most driverlib calls.
* Indexed by GPIO port number (0-3).
*/
static const uint32_t gpioBaseAddresses[] = {
GPIOA0_BASE, GPIOA1_BASE,
GPIOA2_BASE, GPIOA3_BASE
};
static const uint32_t powerResources[] = {
PowerCC32XX_PERIPH_GPIOA0,
PowerCC32XX_PERIPH_GPIOA1,
PowerCC32XX_PERIPH_GPIOA2,
PowerCC32XX_PERIPH_GPIOA3,
};
#define NUM_PORTS 4
#define NUM_PINS_PER_PORT 8
#define PORT_MASK 0x3
/*
* Extracts the GPIO interrupt type from the pinConfig. Value to index into the
* interruptType table.
*/
#define getIntTypeNumber(pinConfig) \
((pinConfig & GPIO_CFG_INT_MASK) >> GPIO_CFG_INT_LSB)
/* Returns the GPIO port base address */
#define getPortBase(port) (gpioBaseAddresses[(port) & PORT_MASK])
/* Returns the GPIO port number */
#define getPort(port) (port & PORT_MASK)
/* Returns the GPIO power resource ID */
#define getPowerResource(port) (powerResources[port & PORT_MASK])
/* Returns GPIO number from the pinConfig */
#define getGpioNumber(pinConfig) \
(((pinConfig->port & PORT_MASK) * 8) + getPinNumber(pinConfig->pin))
/* Uninitialized callbackInfo pinIndex */
#define CALLBACK_INDEX_NOT_CONFIGURED 0xFF
/*
* Device specific interpretation of the GPIO_PinConfig content
*/
typedef struct PinConfig {
uint8_t pin;
uint8_t port;
uint16_t config;
} PinConfig;
/*
* User defined pin indexes assigned to a port's 8 pins.
* Used by port interrupt function to locate callback assigned
* to a pin.
*/
typedef struct PortCallbackInfo {
/*
* the port's 8 corresponding
* user defined pinId indices
*/
uint8_t pinIndex[NUM_PINS_PER_PORT];
} PortCallbackInfo;
/*
* Table of portCallbackInfos.
* One for each port.
*/
static PortCallbackInfo gpioCallbackInfo[NUM_PORTS];
/*
* bit mask used to determine if a Hwi has been created/constructed
* for a port already.
* up to NUM_PORTS port interrupts must be supported
*/
static uint8_t portHwiCreatedBitMask = 0;
/*
* Bit mask used to keep track of which of the GPIO objects in the config
* structure have interrupts enabled. This will be used to restore the
* interrupts after coming out of LPDS.
*/
static uint32_t configIntsEnabledMask = 0;
#if DebugP_ASSERT_ENABLED
/*
* Internal boolean to confirm that GPIO_init() has been called.
*/
static bool initCalled = false;
#endif
/* Notification for going into and waking up from LPDS */
static Power_NotifyObj powerNotifyObj;
extern const GPIOCC32XX_Config GPIOCC32XX_config;
static int powerNotifyFxn(unsigned int eventType, uintptr_t eventArg,
uintptr_t clientArg);
/*
* ======== getPinNumber ========
*
* Internal function to efficiently find the index of the right most set bit.
*/
static inline uint32_t getPinNumber(uint32_t x) {
#if defined(__TI_COMPILER_VERSION__)
return (uint32_t)(__clz(__rbit(x)) & 0x7);
#elif defined(codered) || defined(__GNUC__) || defined(sourcerygxx)
return (uint32_t)(__builtin_ctz(x) & 0x7);
#elif defined(__IAR_SYSTEMS_ICC__)
return (uint32_t)(__CLZ(__RBIT(x)) & 0x7);
#elif defined(rvmdk) || defined(__ARMCC_VERSION)
return (uint32_t)(__clz(__rbit(x)) & 0x7);
#else
#error "Unsupported compiler used"
#endif
}
/*
* ======== GPIO_clearInt ========
*/
void GPIO_clearInt(uint_least8_t index)
{
PinConfig *config = (PinConfig *) &GPIOCC32XX_config.pinConfigs[index];
DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfPinConfigs);
/* Clear GPIO interrupt flag */
MAP_GPIOIntClear(getPortBase(config->port), config->pin);
DebugP_log2("GPIO: port 0x%x, pin 0x%x interrupt flag cleared",
getPort(config->port), config->pin);
}
/*
* ======== GPIO_disableInt ========
*/
void GPIO_disableInt(uint_least8_t index)
{
uintptr_t key;
PinConfig *config = (PinConfig *) &GPIOCC32XX_config.pinConfigs[index];
DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfPinConfigs);
/* Make atomic update */
key = HwiP_disable();
/* Disable GPIO interrupt */
MAP_GPIOIntDisable(getPortBase(config->port), config->pin);
configIntsEnabledMask &= ~(1 << index);
HwiP_restore(key);
DebugP_log2("GPIO: port 0x%x, pin 0x%x interrupts disabled",
getPort(config->port), config->pin);
}
/*
* ======== GPIO_enableInt ========
*/
void GPIO_enableInt(uint_least8_t index)
{
uintptr_t key;
PinConfig *config = (PinConfig *) &GPIOCC32XX_config.pinConfigs[index];
DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfPinConfigs);
/* Make atomic update */
key = HwiP_disable();
/* Enable GPIO interrupt */
MAP_GPIOIntEnable(getPortBase(config->port), config->pin);
configIntsEnabledMask |= (1 << index);
HwiP_restore(key);
DebugP_log2("GPIO: port 0x%x, pin 0x%x interrupts enabled",
getPort(config->port), config->pin);
}
/*
* ======== GPIO_getConfig ========
*/
void GPIO_getConfig(uint_least8_t index, GPIO_PinConfig *pinConfig)
{
DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfPinConfigs);
*pinConfig = GPIOCC32XX_config.pinConfigs[index];
}
/*
* ======== GPIO_hwiIntFxn ========
* Hwi function that processes GPIO interrupts.
*/
void GPIO_hwiIntFxn(uintptr_t portIndex)
{
unsigned int bitNum;
unsigned int pinIndex;
uint32_t pins;
uint32_t portBase;
PortCallbackInfo *portCallbackInfo;
portCallbackInfo = &gpioCallbackInfo[portIndex];
portBase = getPortBase(portIndex);
/* Find out which pins have their interrupt flags set */
pins = MAP_GPIOIntStatus(portBase, 0xFF) & 0xFF;
/* clear all the set bits at once */
MAP_GPIOIntClear(portBase, pins);
/* Match the interrupt to its corresponding callback function */
while (pins) {
/* Gets the lowest order set bit number */
bitNum = getPinNumber(pins);
pinIndex = portCallbackInfo->pinIndex[bitNum & 0x7];
/* only call plugged callbacks */
if (pinIndex != CALLBACK_INDEX_NOT_CONFIGURED) {
GPIOCC32XX_config.callbacks[pinIndex](pinIndex);
}
pins &= ~(1 << bitNum);
}
}
/*
* ======== GPIO_init ========
*/
void GPIO_init()
{
unsigned int i, j;
#if DebugP_ASSERT_ENABLED
initCalled = true;
#endif
for (i = 0; i < NUM_PORTS; i++) {
for (j = 0; j < NUM_PINS_PER_PORT; j++) {
gpioCallbackInfo[i].pinIndex[j] = CALLBACK_INDEX_NOT_CONFIGURED;
}
}
/*
* Configure pins and create Hwis per static array content
*/
for (i = 0; i < GPIOCC32XX_config.numberOfPinConfigs; i++) {
if (!(GPIOCC32XX_config.pinConfigs[i] & GPIO_DO_NOT_CONFIG)) {
GPIO_setConfig(i, GPIOCC32XX_config.pinConfigs[i]);
}
if (i < GPIOCC32XX_config.numberOfCallbacks) {
if (GPIOCC32XX_config.callbacks[i] != NULL) {
/* create Hwi as necessary */
GPIO_setCallback(i, GPIOCC32XX_config.callbacks[i]);
}
}
}
Power_registerNotify(&powerNotifyObj,
PowerCC32XX_ENTERING_LPDS | PowerCC32XX_AWAKE_LPDS,
powerNotifyFxn, (uintptr_t) NULL);
}
/*
* ======== GPIO_read ========
*/
uint_fast8_t GPIO_read(uint_least8_t index)
{
unsigned int value;
PinConfig *config = (PinConfig *) &GPIOCC32XX_config.pinConfigs[index];
DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfPinConfigs);
value = MAP_GPIOPinRead(getPortBase(config->port), config->pin);
DebugP_log3("GPIO: port 0x%x, pin 0x%x read 0x%x",
getPort(config->port), config->pin, value);
value = (value & config->pin) ? 1 : 0;
return (value);
}
/*
* ======== GPIO_setCallback ========
*/
void GPIO_setCallback(uint_least8_t index, GPIO_CallbackFxn callback)
{
uint32_t pinNum;
uint32_t portIndex;
PinConfig *config = (PinConfig *) &GPIOCC32XX_config.pinConfigs[index];
DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfCallbacks);
/*
* plug the pin index into the corresponding
* port's callbackInfo pinIndex entry
*/
pinNum = getPinNumber(config->pin);
portIndex = config->port & PORT_MASK;
if (callback == NULL) {
gpioCallbackInfo[portIndex].pinIndex[pinNum] =
CALLBACK_INDEX_NOT_CONFIGURED;
}
else {
gpioCallbackInfo[portIndex].pinIndex[pinNum] = index;
}
/*
* Only update callBackFunctions entry if different.
* This allows the callBackFunctions array to be in flash for static systems.
*/
if (GPIOCC32XX_config.callbacks[index] != callback) {
GPIOCC32XX_config.callbacks[index] = callback;
}
}
/*
* ======== GPIO_setConfig ========
*/
int_fast16_t GPIO_setConfig(uint_least8_t index, GPIO_PinConfig pinConfig)
{
uintptr_t key;
uint32_t pin;
uint32_t pad;
uint32_t portBase;
uint32_t portIndex;
uint32_t portBitMask;
uint16_t direction;
uint16_t strength;
uint16_t pinType;
HwiP_Handle hwiHandle;
HwiP_Params hwiParams;
GPIO_PinConfig gpioPinConfig;
PinConfig *config = (PinConfig *) &GPIOCC32XX_config.pinConfigs[index];
DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfPinConfigs);
if (pinConfig & GPIO_DO_NOT_CONFIG) {
return (GPIO_STATUS_SUCCESS);
}
portBase = getPortBase(config->port);
pin = config->pin;
pad = PinFromPadGet((unsigned long)getGpioNumber(config));
/* Make atomic update */
key = HwiP_disable();
/* set the pad's pinType to GPIO */
MAP_PinModeSet(pad, PIN_MODE_0);
/* enable clocks for the GPIO port */
Power_setDependency(getPowerResource(config->port));
HwiP_restore(key);
if ((pinConfig & GPIO_CFG_IN_INT_ONLY) == 0) {
if (pinConfig & GPIO_CFG_INPUT) {
/* configure input */
direction = GPIO_DIR_MODE_IN;
strength = PIN_STRENGTH_2MA;
pinType = inPinTypes[(pinConfig & GPIO_CFG_IN_TYPE_MASK) >>
GPIO_CFG_IN_TYPE_LSB];
}
else {
/* configure output */
direction = GPIO_DIR_MODE_OUT;
strength =
outPinStrengths[(pinConfig & GPIO_CFG_OUT_STRENGTH_MASK) >>
GPIO_CFG_OUT_STRENGTH_LSB];
pinType = outPinTypes[(pinConfig & GPIO_CFG_OUT_TYPE_MASK) >>
GPIO_CFG_OUT_TYPE_LSB];
}
key = HwiP_disable();
/* Configure the GPIO pin */
MAP_GPIODirModeSet(portBase, pin, direction);
MAP_PinConfigSet(pad, strength, pinType);
/* Set output value */
if (direction == GPIO_DIR_MODE_OUT) {
MAP_GPIOPinWrite(portBase, pin,
((pinConfig & GPIO_CFG_OUT_HIGH) ? 0xFF : 0));
}
/*
* Update pinConfig with the latest GPIO configuration and
* clear the GPIO_DO_NOT_CONFIG bit if it was set.
*/
gpioPinConfig = GPIOCC32XX_config.pinConfigs[index];
gpioPinConfig &= ~(GPIO_CFG_IO_MASK | GPIO_DO_NOT_CONFIG);
gpioPinConfig |= (pinConfig & GPIO_CFG_IO_MASK);
GPIOCC32XX_config.pinConfigs[index] = gpioPinConfig;
HwiP_restore(key);
}
/* Set type of interrupt and then clear it */
if (pinConfig & GPIO_CFG_INT_MASK) {
portIndex = config->port & PORT_MASK;
portBitMask = 1 << portIndex;
/* if Hwi has not already been created, do so */
if ((portHwiCreatedBitMask & portBitMask) == 0) {
HwiP_Params_init(&hwiParams);
hwiParams.arg = (uintptr_t) portIndex;
hwiParams.priority = GPIOCC32XX_config.intPriority;
hwiHandle = HwiP_create(portInterruptIds[portIndex], GPIO_hwiIntFxn,
&hwiParams);
if (hwiHandle == NULL) {
/* Error creating Hwi */
DebugP_log1("GPIO: Error constructing Hwi for GPIO Port %d",
getPort(config->port));
return (GPIO_STATUS_ERROR);
}
}
key = HwiP_disable();
/* Mark the Hwi as created */
portHwiCreatedBitMask |= portBitMask;
MAP_GPIOIntTypeSet(portBase, pin,
interruptType[getIntTypeNumber(pinConfig)]);
MAP_GPIOIntClear(portBase, pin);
/*
* Update pinConfig with the latest interrupt configuration and
* clear the GPIO_DO_NOT_CONFIG bit if it was set.
*/
gpioPinConfig = GPIOCC32XX_config.pinConfigs[index];
gpioPinConfig &= ~(GPIO_CFG_INT_MASK | GPIO_DO_NOT_CONFIG);
gpioPinConfig |= (pinConfig & GPIO_CFG_INT_MASK);
GPIOCC32XX_config.pinConfigs[index] = gpioPinConfig;
HwiP_restore(key);
}
return (GPIO_STATUS_SUCCESS);
}
/*
* ======== GPIO_toggle ========
*/
void GPIO_toggle(uint_least8_t index)
{
uintptr_t key;
uint32_t value;
PinConfig *config = (PinConfig *) &GPIOCC32XX_config.pinConfigs[index];
DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfPinConfigs);
DebugP_assert((GPIOCC32XX_config.pinConfigs[index] & GPIO_CFG_INPUT) ==
GPIO_CFG_OUTPUT);
/* Make atomic update */
key = HwiP_disable();
value = MAP_GPIOPinRead(getPortBase(config->port), config->pin);
value ^= config->pin;
MAP_GPIOPinWrite(getPortBase(config->port), config->pin, value);
/* Update config table entry with value written */
GPIOCC32XX_config.pinConfigs[index] ^= GPIO_CFG_OUT_HIGH;
HwiP_restore(key);
DebugP_log2("GPIO: port 0x%x, pin 0x%x toggled",
getPort(config->port), config->pin);
}
/*
* ======== GPIO_write ========
*/
void GPIO_write(uint_least8_t index, unsigned int value)
{
uintptr_t key;
uint32_t output;
PinConfig *config = (PinConfig *) &GPIOCC32XX_config.pinConfigs[index];
DebugP_assert(initCalled && index < GPIOCC32XX_config.numberOfPinConfigs);
DebugP_assert((GPIOCC32XX_config.pinConfigs[index] & GPIO_CFG_INPUT) ==
GPIO_CFG_OUTPUT);
key = HwiP_disable();
/* Clear output from pinConfig */
GPIOCC32XX_config.pinConfigs[index] &= ~GPIO_CFG_OUT_HIGH;
if (value) {
output = config->pin;
/* Set the pinConfig output bit to high */
GPIOCC32XX_config.pinConfigs[index] |= GPIO_CFG_OUT_HIGH;
}
else {
output = value;
}
MAP_GPIOPinWrite(getPortBase(config->port), config->pin, output);
HwiP_restore(key);
DebugP_log3("GPIO: port 0x%x, pin 0x%x wrote 0x%x",
getPort(config->port), config->pin, value);
}
/*
* ======== powerNotifyFxn ========
*/
static int powerNotifyFxn(unsigned int eventType, uintptr_t eventArg,
uintptr_t clientArg)
{
unsigned int i;
uint32_t ulRegVal;
GPIO_PinConfig config;
uint32_t output;
uint32_t pad;
PinConfig *pinConfig;
PowerCC32XX_ParkState state;
if (eventType == PowerCC32XX_AWAKE_LPDS) {
/* Take GPIO semaphore */
ulRegVal = HWREG(0x400F703C);
ulRegVal = (ulRegVal & ~0x3FF) | 0x155;
HWREG(0x400F703C) = ulRegVal;
for (i = 0; i < GPIOCC32XX_config.numberOfPinConfigs; i++) {
if (!(GPIOCC32XX_config.pinConfigs[i] & GPIO_DO_NOT_CONFIG)) {
config = GPIOCC32XX_config.pinConfigs[i];
GPIO_setConfig(i, config);
if (configIntsEnabledMask & (1 << i)) {
GPIO_enableInt(i);
}
}
}
}
else {
/* Entering LPDS */
/*
* For pins configured as GPIO output, if the GPIOCC32XX_USE_STATIC
* configuration flag is *not* set, get the current pin state, and
* then call to the Power manager to define the state to be held
* during LPDS.
* If GPIOCC32XX_USE_STATIC *is* defined, do nothing, and the pin
* will be parked in the state statically defined in
* PowerCC32XX_config.pinParkDefs[] in the board file.
*/
for (i = 0; i < GPIOCC32XX_config.numberOfPinConfigs; i++) {
if (GPIOCC32XX_config.pinConfigs[i] & GPIO_DO_NOT_CONFIG) {
continue;
}
config = GPIOCC32XX_config.pinConfigs[i];
/* if OUTPUT, and GPIOCC32XX_USE_STATIC flag is not set */
if ((!(config & GPIO_CFG_INPUT)) &&
(!(config & GPIOCC32XX_USE_STATIC))) {
pinConfig = (PinConfig *) &GPIOCC32XX_config.pinConfigs[i];
/* determine state to be held */
pad = PinFromPadGet((unsigned long)getGpioNumber(pinConfig));
output = config & GPIO_CFG_OUT_HIGH;
state = (PowerCC32XX_ParkState) ((output) ? 1 : 0);
/* set the new park state */
PowerCC32XX_setParkState((PowerCC32XX_Pin)pad, state);
}
}
}
return (Power_NOTIFYDONE);
}