/* * Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved. * * SPDX-License-Identifier: MIT */ #include #include "FreeRTOS.h" #include #include #include #include #include #include "projdefs.h" #include "gpio_drv.h" #include "portmacro.h" #define DRIVER_NAME "GPIO" struct BankBackup { uint32_t pinmux[4]; uint32_t oen; }; static struct BankBackup stateBackup[BANK_NUM_MAX]; static void prvEnterCritical(UBaseType_t *uxIsr) { if (xPortIsIsrContext()) *uxIsr = taskENTER_CRITICAL_FROM_ISR(); else { taskENTER_CRITICAL(); *uxIsr = 0; } }; static void prvExitCritical(UBaseType_t uxSaveIsr) { if (xPortIsIsrContext()) taskEXIT_CRITICAL_FROM_ISR(uxSaveIsr); else { taskEXIT_CRITICAL(); (void)uxSaveIsr; } }; static inline void prvGpioRegWrite(uint32_t addr, uint32_t mask, uint32_t val) { UBaseType_t uxSavedIsr; prvEnterCritical(&uxSavedIsr); REG32_UPDATE_BITS(addr, mask, val); prvExitCritical(uxSavedIsr); } static inline void prvGpioCalcRegAndBit(const struct GpioBank *bk, uint8_t offset, enum GpioRegType regType, uint16_t *reg, uint8_t *bit) { *reg = bk->regs[regType].reg << 2; *bit = bk->regs[regType].bit + offset; } static inline void prvGpioGetBankAndOffset(uint16_t gpio, const struct GpioBank **bk, uint8_t *off) { const struct GpioBank *gpk; gpk = pGetGpioBank(); *bk = &gpk[gpio >> 5]; *off = gpio % 32; } int xGpioSetDir(uint16_t gpio, enum GpioDirType dir) { uint16_t reg; uint8_t bit; uint8_t offset; const struct GpioBank *bk; if (dir >= GPIO_DIR_INVALID) { printf("%s: INVAL DIR %d\n", DRIVER_NAME, dir); return -pdFREERTOS_ERRNO_EINVAL; } prvGpioGetBankAndOffset(gpio, &bk, &offset); prvGpioCalcRegAndBit(bk, offset, REG_DIR, ®, &bit); prvGpioRegWrite(bk->domain->rGpio + reg, BIT(bit), dir ? BIT(bit) : 0); return 0; } int xGpioSetValue(uint16_t gpio, enum GpioOutLevelType level) { uint16_t reg; uint8_t bit; uint8_t offset; const struct GpioBank *bk; if (level >= GPIO_LEVEL_INVALID) { printf("%s: INVAL OUT %d\n", DRIVER_NAME, level); return -pdFREERTOS_ERRNO_EINVAL; } prvGpioGetBankAndOffset(gpio, &bk, &offset); prvGpioCalcRegAndBit(bk, offset, REG_OUT, ®, &bit); prvGpioRegWrite(bk->domain->rGpio + reg, BIT(bit), level ? BIT(bit) : 0); return 0; } int xGpioGetValue(uint16_t gpio) { uint16_t reg; uint8_t bit; uint8_t offset; const struct GpioBank *bk; prvGpioGetBankAndOffset(gpio, &bk, &offset); prvGpioCalcRegAndBit(bk, offset, REG_IN, ®, &bit); return !!(REG32((unsigned long)bk->domain->rGpio + reg) & BIT(bit)); } int xPinconfSet(uint16_t gpio, uint32_t flags) { uint16_t reg; uint8_t bit; uint8_t offset; uint8_t ds_value = 0; const struct GpioBank *bk; const struct GpioRegDesc *desc; prvGpioGetBankAndOffset(gpio, &bk, &offset); prvGpioCalcRegAndBit(bk, offset, REG_PULLEN, ®, &bit); if (flags & PINF_CONFIG_BIAS_DISABLE) { prvGpioRegWrite(bk->domain->rPullen + reg, BIT(bit), 0); } else if (flags & (PINF_CONFIG_BIAS_PULL_UP | PINF_CONFIG_BIAS_PULL_DOWN)) { prvGpioRegWrite(bk->domain->rPullen + reg, BIT(bit), BIT(bit)); prvGpioCalcRegAndBit(bk, offset, REG_PULL, ®, &bit); if (flags & PINF_CONFIG_BIAS_PULL_UP) prvGpioRegWrite(bk->domain->rPull + reg, BIT(bit), BIT(bit)); else prvGpioRegWrite(bk->domain->rPull + reg, BIT(bit), 0); } if (flags & PINF_CONFIG_DRV_MASK && !bk->regs[REG_DRV].reg) { prvGpioCalcRegAndBit(bk, offset, REG_DRV, ®, &bit); desc = &bk->regs[REG_DRV]; reg = desc->reg + ((desc->bit + (offset << 1)) >> 5); bit = (desc->bit + (offset << 1)) % 32; if (flags & PINF_CONFIG_DRV_STRENGTH_0) ds_value = 0; else if (flags & PINF_CONFIG_DRV_STRENGTH_1) ds_value = 1; else if (flags & PINF_CONFIG_DRV_STRENGTH_2) ds_value = 2; else if (flags & PINF_CONFIG_DRV_STRENGTH_3) ds_value = 3; prvGpioRegWrite(bk->domain->rDrv + (reg << 2), 0x3 << bit, ds_value << bit); } return 0; } int xPinmuxSet(uint16_t gpio, enum PinMuxType func) { uint16_t reg; uint8_t bit; uint8_t offset; const struct GpioBank *bk; const struct GpioRegDesc *desc; if (func >= PIN_FUNC_INVALID) { printf("%s: INVAL FUNC %d\n", DRIVER_NAME, func); return -pdFREERTOS_ERRNO_EINVAL; } prvGpioGetBankAndOffset(gpio, &bk, &offset); desc = &bk->regs[REG_MUX]; reg = desc->reg + ((desc->bit + (offset << 2)) >> 5); bit = (desc->bit + (offset << 2)) % 32; prvGpioRegWrite(bk->domain->rMux + (reg << 2), 0xf << bit, func << bit); return 0; } static void prvOenStateHandle(const struct GpioBank *bk, struct BankBackup *bbk, bool isBackup) { uint8_t reg, bit, end; bit = bk->regs[REG_DIR].bit; end = bit + bk->pin_num; reg = bk->regs[REG_DIR].reg; while (bit < end) { if (isBackup) { bbk->oen &= ~(0x1 << bit); bbk->oen |= REG32(bk->domain->rGpio + (reg << 2)) & (0x1 << bit); prvGpioRegWrite(bk->domain->rGpio + (reg << 2), 0x1 << bit, 0x1 << bit); } else { prvGpioRegWrite(bk->domain->rGpio + (reg << 2), 0x1 << bit, bbk->oen); } bit++; } } static void prvPinmuxStateHandle(const struct GpioBank *bk, struct BankBackup *bbk, bool isBackup) { uint8_t reg, offset, bit, begin, end; begin = bk->regs[REG_MUX].bit; end = begin + bk->pin_num * 4; reg = bk->regs[REG_MUX].reg; while (begin < end) { offset = begin >> 5; bit = begin & 0x1f; if (isBackup) { bbk->pinmux[offset] &= ~(0xf << bit); bbk->pinmux[offset] |= REG32(bk->domain->rMux + ((reg + offset) << 2)) & (0xf << bit); prvGpioRegWrite(bk->domain->rMux + ((reg + offset) << 2), 0xf << bit, 0); } else { prvGpioRegWrite(bk->domain->rMux + ((reg + offset) << 2), 0xf << bit, bbk->pinmux[offset]); } begin += 4; } } static int prvStrCaseCmp(const char *string1, const char *string2) { char value; char *lp = (char *)string1; char *rp = (char *)string2; while (*lp && *rp) { value = ((*lp >= 'A' && *lp <= 'Z') ? (*lp - 'A' + 'a') : *lp) - ((*rp >= 'A' && *rp <= 'Z') ? (*rp - 'A' + 'a') : *rp); if (value) return value; lp++; rp++; } return *lp - *rp; } static int prvBankCheck(const char *name, const struct GpioBank **bk) { int index; const struct GpioBank *gpk = pGetGpioBank(); /* Find bank */ for (index = 0; index < BANK_NUM_MAX; index++) { if (!prvStrCaseCmp(gpk[index].name, name)) { *bk = &gpk[index]; break; } } if (index == BANK_NUM_MAX) return -pdFREERTOS_ERRNO_ENXIO; if (!(*bk)->pin_num) return -pdFREERTOS_ERRNO_EINVAL; return index; } int xBankStateBackup(const char *name) { int index; const struct GpioBank *bk; index = prvBankCheck(name, &bk); if (index < 0) return index; prvOenStateHandle(bk, &stateBackup[index], true); prvPinmuxStateHandle(bk, &stateBackup[index], true); return 0; } int xBankStateRestore(const char *name) { int index; const struct GpioBank *bk; index = prvBankCheck(name, &bk); if (index < 0) return index; prvPinmuxStateHandle(bk, &stateBackup[index], false); prvOenStateHandle(bk, &stateBackup[index], false); return 0; }