IndustrialDeviceController/Software/MT3620_IDC_RTApp/lib/GPT.c (776 lines of code) (raw):

/* Copyright (c) Codethink Ltd. All rights reserved. Licensed under the MIT License. */ #include "GPT.h" #include "CPUFreq.h" #include "NVIC.h" #include "mt3620/gpt.h" #include <stddef.h> // Define this to enable setting of speed in GPT3 #define GPT3_ENABLE_NON_STANDARD_SPEED #define MAX_TIMER_INDEX (MT3620_UNIT_GPT_COUNT + MT3620_UNIT_GPT0 - 1) #define GPT_PRIORITY 2 #define GPT01_IRQ 1 #define GPT3_IRQ 2 struct GPT { bool open; uint32_t id; GPT_Mode mode; void (*callback)(GPT *); uint32_t numCycles; volatile uint32_t initCnt; volatile bool paused; }; static GPT context[MT3620_UNIT_GPT_COUNT] = {0}; static inline int32_t GPT__GetCtrlValue( GPT *handle, bool *enabled, GPT_Mode *mode, bool *speed, bool *restart) { if (!handle) { return ERROR_PARAMETER; } int32_t error = ERROR_NONE; switch (handle->id) { case MT3620_UNIT_GPT0: if (enabled) { *enabled = MT3620_GPT_FIELD_READ(gpt0_ctrl, en); } if (mode) { *mode = MT3620_GPT_FIELD_READ(gpt0_ctrl, mode); } if (speed) { *speed = MT3620_GPT_FIELD_READ(gpt0_ctrl, speed); } if (restart) { *restart = MT3620_GPT_FIELD_READ(gpt0_ctrl, restart); } break; case MT3620_UNIT_GPT1: if (enabled) { *enabled = MT3620_GPT_FIELD_READ(gpt1_ctrl, en); } if (mode) { *mode = MT3620_GPT_FIELD_READ(gpt1_ctrl, mode); } if (speed) { *speed = MT3620_GPT_FIELD_READ(gpt1_ctrl, speed); } if (restart) { *restart = MT3620_GPT_FIELD_READ(gpt1_ctrl, restart); } break; case MT3620_UNIT_GPT2: if (enabled) { *enabled = MT3620_GPT_FIELD_READ(gpt2_ctrl, en); } if (mode) { *mode = GPT_MODE_NONE; } if (speed) { *speed = MT3620_GPT_FIELD_READ(gpt2_ctrl, speed); } if (restart) { error = ERROR_UNSUPPORTED; } break; case MT3620_UNIT_GPT3: if (enabled) { *enabled = MT3620_GPT_FIELD_READ(gpt3_ctrl, en); } if (mode) { *mode = handle->mode; } if (speed) { error = ERROR_UNSUPPORTED; } if (restart) { error = ERROR_UNSUPPORTED; } break; case MT3620_UNIT_GPT4: if (enabled) { *enabled = MT3620_GPT_FIELD_READ(gpt4_ctrl, en); } if (mode) { *mode = GPT_MODE_NONE; } if (speed) { *speed = MT3620_GPT_FIELD_READ(gpt4_ctrl, speed); } if (restart) { error = ERROR_UNSUPPORTED; } break; default: error = ERROR_UNSUPPORTED; break; } return error; } static inline int32_t GPT__SetCtrlValue( GPT *handle, bool *enabled, GPT_Mode *mode, bool *speed, bool *restart) { if (!handle) { return ERROR_PARAMETER; } int32_t error = ERROR_NONE; switch (handle->id) { case MT3620_UNIT_GPT0: if (enabled) { MT3620_GPT_FIELD_WRITE(gpt0_ctrl, en, *enabled); } if (mode) { MT3620_GPT_FIELD_WRITE(gpt0_ctrl, mode, *mode); } if (speed) { MT3620_GPT_FIELD_WRITE(gpt0_ctrl, speed, *speed); } if (restart) { MT3620_GPT_FIELD_WRITE(gpt0_ctrl, restart, *restart); } break; case MT3620_UNIT_GPT1: if (enabled) { MT3620_GPT_FIELD_WRITE(gpt1_ctrl, en, *enabled); } if (mode) { MT3620_GPT_FIELD_WRITE(gpt1_ctrl, mode, *mode); } if (speed) { MT3620_GPT_FIELD_WRITE(gpt1_ctrl, speed, *speed); } if (restart) { MT3620_GPT_FIELD_WRITE(gpt1_ctrl, restart, *restart); } break; case MT3620_UNIT_GPT2: if (enabled) { MT3620_GPT_FIELD_WRITE(gpt2_ctrl, en, *enabled); } if (mode && (*mode != GPT_MODE_NONE)) { error = ERROR_UNSUPPORTED; } if (speed) { MT3620_GPT_FIELD_WRITE(gpt2_ctrl, speed, *speed); } if (restart) { error = ERROR_UNSUPPORTED; } break; case MT3620_UNIT_GPT3: if (enabled) { MT3620_GPT_FIELD_WRITE(gpt3_ctrl, en, *enabled); } if (mode) { // GPT3 doesn't support repeat in HW, but we can emulate it in HW handle->mode = *mode; } if (speed || restart) { error = ERROR_UNSUPPORTED; } break; case MT3620_UNIT_GPT4: if (enabled) { MT3620_GPT_FIELD_WRITE(gpt4_ctrl, en, *enabled); } if (mode && (*mode != GPT_MODE_NONE)) { error = ERROR_UNSUPPORTED; } if (speed) { MT3620_GPT_FIELD_WRITE(gpt4_ctrl, speed, *speed); } if (restart) { error = ERROR_UNSUPPORTED; } break; default: error = ERROR_UNSUPPORTED; break; } return error; } bool GPT_IsEnabled(GPT *handle) { if (!handle) { return false; } bool enabled; if (GPT__GetCtrlValue( handle, &enabled, NULL, NULL, NULL) != ERROR_NONE) { return false; } return enabled; } static inline int32_t GPT__SetEnabled( GPT *handle, bool enabled) { if (!handle) { return ERROR_PARAMETER; } int32_t error = GPT__SetCtrlValue(handle, &enabled, NULL, NULL, NULL); return error; } static inline int32_t GPT__ToggleRestart( GPT *handle, bool restart) { if (!handle) { return ERROR_PARAMETER; } int32_t error = GPT__SetCtrlValue(handle, NULL, NULL, NULL, &restart); return error; } static inline int32_t getAvailableSpeeds( const uint32_t id, float *lowSpeed, float *highSpeed) { switch (id) { case MT3620_UNIT_GPT0: case MT3620_UNIT_GPT1: case MT3620_UNIT_GPT2: *lowSpeed = MT3620_GPT_012_LOW_SPEED; *highSpeed = MT3620_GPT_012_HIGH_SPEED; break; case MT3620_UNIT_GPT4: *highSpeed = (float)(CPUFreq_Get()); *lowSpeed = (*highSpeed) / 2; break; default: return ERROR_UNSUPPORTED; } return ERROR_NONE; } static inline int32_t GPT__GetSpeed3(GPT *handle, float *speedHz) { if (!handle || !speedHz) { return ERROR_PARAMETER; } // Calculate speed (26MHz / osc_cnt_1us) uint32_t osc_count_value, speedTemp; osc_count_value = MT3620_GPT_FIELD_READ(gpt3_ctrl, osc_cnt_1us) + 1; speedTemp = MT3620_GPT_3_SRC_CLK_HZ / osc_count_value; if (speedTemp == 0) { return ERROR_GPT_SPEED_INVALID; } else { *speedHz = speedTemp; } return ERROR_NONE; } int32_t GPT_GetSpeed(GPT* handle, float *speedHz) { if (!handle || !speedHz) { return ERROR_PARAMETER; } if (handle->id == MT3620_UNIT_GPT3) { return GPT__GetSpeed3(handle, speedHz); } // Determine available speeds float lowSpeed, highSpeed; getAvailableSpeeds(handle->id, &lowSpeed, &highSpeed); bool speed_reg; int32_t error; if ((error = GPT__GetCtrlValue( handle, NULL, NULL, &speed_reg, NULL)) == ERROR_NONE) { *speedHz = speed_reg ? highSpeed : lowSpeed; } return error; } static inline int32_t GPT__SetSpeed3( GPT *handle, float speedHz) { if (!handle) { return ERROR_PARAMETER; } #ifdef GPT3_ENABLE_NON_STANDARD_SPEED uint32_t osc_count_value = MT3620_GPT_3_SRC_CLK_HZ / speedHz; if (osc_count_value == 0) { return ERROR_GPT_SPEED_INVALID; } MT3620_GPT_FIELD_WRITE(gpt3_ctrl, osc_cnt_1us, osc_count_value - 1); #endif // #ifdef GPT3_ENABLE_NON_STANDARD_SPEED return ERROR_NONE; } int32_t GPT_SetSpeed(GPT *handle, float speedHz) { if (!handle) { return ERROR_PARAMETER; } if (GPT_IsEnabled(handle)) { return ERROR_BUSY; } if (handle->id == MT3620_UNIT_GPT3) { return GPT__SetSpeed3(handle, speedHz); } float lowSpeed, highSpeed; getAvailableSpeeds(handle->id, &lowSpeed, &highSpeed); bool registerValue; if ((speedHz <= lowSpeed) || ((speedHz - lowSpeed) < (highSpeed - speedHz))) { registerValue = false; } else { registerValue = true; } int32_t error = GPT__SetCtrlValue( handle, NULL, NULL, &registerValue, NULL); return error; } int32_t GPT_GetMode( GPT *handle, GPT_Mode *mode) { if (!handle || !mode) { return ERROR_PARAMETER; } GPT_Mode m; int32_t error; if ((error = GPT__GetCtrlValue( handle, NULL, &m, NULL, NULL)) == ERROR_NONE) { *mode = m; } return error; } int32_t GPT_SetMode(GPT *handle, GPT_Mode mode) { if (!handle) { return ERROR_PARAMETER; } if (mode == GPT_MODE_NONE) { return ERROR_NONE; } return GPT__SetCtrlValue(handle, NULL, &mode, NULL, NULL); } uint32_t GPT_GetCount(GPT *handle) { if (!handle || (handle->id > MAX_TIMER_INDEX)) { return 0; } uint32_t count = 0; switch (handle->id) { case MT3620_UNIT_GPT0: count = mt3620_gpt->gpt0_cnt; break; case MT3620_UNIT_GPT1: count = mt3620_gpt->gpt1_cnt; break; case MT3620_UNIT_GPT2: count = mt3620_gpt->gpt2_cnt; break; case MT3620_UNIT_GPT3: count = mt3620_gpt->gpt3_cnt; break; case MT3620_UNIT_GPT4: count = mt3620_gpt->gpt4_cnt; break; default: break; } return count; } uint32_t GPT_GetRunningTime(GPT *handle, GPT_Units units) { if (!handle || (handle->id > MAX_TIMER_INDEX)) { return 0; } float speed; if ((GPT_GetSpeed(handle, &speed) != ERROR_NONE) || (speed == 0)) { return 0; } // Get relative count (some timers count backwards, others from non-zero start) uint32_t count = GPT_GetCount(handle); uint32_t init_count; switch (handle->id) { case MT3620_UNIT_GPT0: init_count = mt3620_gpt->gpt0_icnt; count = init_count - count; break; case MT3620_UNIT_GPT1: init_count = mt3620_gpt->gpt1_icnt; count = init_count - count; break; case MT3620_UNIT_GPT2: // for GPT2, we have to rely on cached initcnt init_count = handle->initCnt; count = count - init_count; break; case MT3620_UNIT_GPT3: init_count = mt3620_gpt->gpt3_init; count = count - init_count; break; case MT3620_UNIT_GPT4: init_count = mt3620_gpt->gpt4_init; count = count - init_count; break; default: return 0; } return ((uint64_t)count * (uint64_t)units) / speed; } int32_t GPT_GetNumCycles(GPT* handle, uint32_t* numCycles) { if (!handle || !numCycles) { return ERROR_PARAMETER; } if (handle->id > MAX_TIMER_INDEX) { return ERROR_UNSUPPORTED; } *numCycles = handle->numCycles; return ERROR_NONE; } static int32_t getGPTIndex(int32_t id) { return id - MT3620_UNIT_GPT0; } static void GPT_ToggleInterrupts(GPT *handle, bool enable) { // Just enables the NVIC control, so timer can interrupt processor int irq; bool ableToDisable = true; switch (handle->id) { case MT3620_UNIT_GPT0: case MT3620_UNIT_GPT1: if (context[getGPTIndex(MT3620_UNIT_GPT0)].open || context[getGPTIndex(MT3620_UNIT_GPT1)].open) { ableToDisable = false; } irq = GPT01_IRQ; break; case MT3620_UNIT_GPT3: irq = GPT3_IRQ; break; default: return; } if (enable) { NVIC_EnableIRQ(irq, GPT_PRIORITY); } else if (ableToDisable) { NVIC_DisableIRQ(irq); } } GPT* GPT_Open(int32_t id, float speedHz, GPT_Mode mode) { int32_t index = getGPTIndex(id); if ((id > MAX_TIMER_INDEX) || (index < 0)) { return NULL; } if (context[index].open) { // user already has this handle return NULL; } if (GPT_IsEnabled(&(context[index]))) { return NULL; } context[index].open = true; context[index].id = id; // Setup Timer control registers if (GPT_SetSpeed(&context[index], speedHz) != ERROR_NONE) { context[index].open = false; return NULL; } if (GPT_SetMode(&context[index], mode) != ERROR_NONE) { context[index].open = false; return NULL; } context[index].callback = NULL; context[index].numCycles = 0; context[index].initCnt = 0; context[index].paused = false; GPT_ToggleInterrupts(&context[index], true); return &context[index]; } void GPT_Close(GPT *handle) { if (!handle || !handle->open) { return; } if (GPT_IsEnabled(handle)) { GPT_Stop(handle); } handle->open = false; // Disable interrupts GPT_ToggleInterrupts(handle, false); } int32_t GPT_GetId(GPT *handle) { if (!handle) { return -1; } return (int32_t)handle->id; } int32_t GPT_Stop(GPT *handle) { if (!handle || !handle->open) { return ERROR_PARAMETER; } if (!GPT_IsEnabled(handle)) { return ERROR_GPT_NOT_RUNNING; } // Disable timer GPT__SetEnabled(handle, false); // Handle different timer types switch (handle->id) { case MT3620_UNIT_GPT0: // Disable interrupt register MT3620_GPT_FIELD_WRITE(gpt_ier, gpt0_int_en, false); break; case MT3620_UNIT_GPT1: // Disable interrupt register MT3620_GPT_FIELD_WRITE(gpt_ier, gpt1_int_en, false); break; case MT3620_UNIT_GPT2: case MT3620_UNIT_GPT3: case MT3620_UNIT_GPT4: break; default: return ERROR_UNSUPPORTED; } handle->numCycles = 0; return ERROR_NONE; } int32_t GPT_Pause(GPT *handle) { if (!handle) { return ERROR_PARAMETER; } if (!GPT_IsEnabled(handle)) { return ERROR_GPT_NOT_RUNNING; } if (handle->paused) { return ERROR_GPT_ALREADY_PAUSED; } handle->initCnt = GPT_GetCount(handle); int32_t error; if ((error = GPT__SetEnabled(handle, false)) != ERROR_NONE) { return error; } handle->paused = true; return ERROR_NONE; } int32_t GPT_Resume(GPT *handle) { if (!handle) { return ERROR_PARAMETER; } if (GPT_IsEnabled(handle)) { return ERROR_GPT_ALREADY_RUNNING; } if (!handle->paused) { return ERROR_GPT_NOT_PAUSED; } // Set initial count to cached value switch (handle->id) { case MT3620_UNIT_GPT0: mt3620_gpt->gpt0_icnt = handle->initCnt; break; case MT3620_UNIT_GPT1: mt3620_gpt->gpt1_icnt = handle->initCnt; break; case MT3620_UNIT_GPT2: // GPT2 is different; you have to write to cnt to set init count mt3620_gpt->gpt2_cnt = handle->initCnt; break; case MT3620_UNIT_GPT3: mt3620_gpt->gpt3_init = handle->initCnt; break; case MT3620_UNIT_GPT4: mt3620_gpt->gpt4_init = handle->initCnt; break; default: break; } int32_t error; if ((error = GPT__SetEnabled(handle, true)) != ERROR_NONE) { return error; } handle->paused = false; return ERROR_NONE; } static inline uint32_t calculateNumCounts( uint32_t timeout, float speed, GPT_Units units) { // Calculate nCount required for timeout [units] // TODO: add overflow detection here? return units > speed ? ((float)timeout * speed) / units : (speed / (float)units) * timeout; } int32_t GPT_StartTimeout( GPT *handle, uint32_t timeout, GPT_Units units, void (*callback)(GPT *)) { if (!handle || !handle->open) { return ERROR_PARAMETER; } if (GPT_IsEnabled(handle)) { return ERROR_GPT_ALREADY_RUNNING; } // Fail silently if not supported GPT__ToggleRestart(handle, true); int32_t error; float actualSpeed; if ((error = GPT_GetSpeed(handle, &actualSpeed)) != ERROR_NONE) { return error; } uint32_t nCount = calculateNumCounts(timeout, actualSpeed, units); if (nCount == 0) { // the timeout is smaller than the resolution of the timer return ERROR_GPT_TIMEOUT_INVALID; } // Handle different timer types switch (handle->id) { case MT3620_UNIT_GPT0: // Since 0 & 1 share INT1; we need to enable interrupt register. MT3620_GPT_FIELD_WRITE(gpt_isr, gpt0_int, false); MT3620_GPT_FIELD_WRITE(gpt_ier, gpt0_int_en, true); // Set initial count (0 & 1 count down) mt3620_gpt->gpt0_icnt = nCount - 1; break; case MT3620_UNIT_GPT1: // Enable interrupt register MT3620_GPT_FIELD_WRITE(gpt_isr, gpt1_int, false); MT3620_GPT_FIELD_WRITE(gpt_ier, gpt1_int_en, true); // Set initial count mt3620_gpt->gpt1_icnt = nCount - 1; break; case MT3620_UNIT_GPT3: mt3620_gpt->gpt3_init = 0; mt3620_gpt->gpt3_expire = nCount - 1; MT3620_GPT_FIELD_WRITE(gpt3_ctrl, gpt3_iclr, true); break; case MT3620_UNIT_GPT2: case MT3620_UNIT_GPT4: return ERROR_UNSUPPORTED; default: return ERROR_UNSUPPORTED; } // Save callback and enable timer handle->callback = callback; handle->paused = false; GPT__SetEnabled(handle, true); return ERROR_NONE; } int32_t GPT_Start_Freerun(GPT *handle) { if (!handle || !handle->open) { return ERROR_PARAMETER; } if (GPT_IsEnabled(handle)) { return ERROR_GPT_ALREADY_RUNNING; } // Set timer ctrl to run maximum length (if not free running timer) // Handle different timer types switch (handle->id) { case MT3620_UNIT_GPT0: // Disable interrupts MT3620_GPT_FIELD_WRITE(gpt_isr, gpt0_int, false); MT3620_GPT_FIELD_WRITE(gpt_ier, gpt0_int_en, false); // Set initial count (0 & 1 count down) mt3620_gpt->gpt0_icnt = MT3620_GPT_MAX_COUNT; break; case MT3620_UNIT_GPT1: // Disable interrupts MT3620_GPT_FIELD_WRITE(gpt_isr, gpt1_int, false); MT3620_GPT_FIELD_WRITE(gpt_ier, gpt1_int_en, false); // Set initial count mt3620_gpt->gpt1_icnt = MT3620_GPT_MAX_COUNT; break; case MT3620_UNIT_GPT2: // GPT2 is different; you have to write to the cnt reg mt3620_gpt->gpt2_cnt = 0; // Cache initCnt since there is no dedicated register. handle->initCnt = 0; // NB: You shouldn't read the GPT2 cnt register for 3T 32Hz cycles // after setting it; else the change doesn't stick. break; case MT3620_UNIT_GPT3: // GPT3 counts from init to expire mt3620_gpt->gpt3_init = 0; mt3620_gpt->gpt3_expire = MT3620_GPT_MAX_COUNT; MT3620_GPT_FIELD_WRITE(gpt3_ctrl, gpt3_iclr, true); break; case MT3620_UNIT_GPT4: mt3620_gpt->gpt4_init = 0; break; default: break; } // Enable timer handle->paused = false; GPT__SetEnabled(handle, true); return ERROR_NONE; } int32_t GPT_WaitTimer_Blocking( GPT *handle, uint32_t timeout, GPT_Units units) { if (!handle || !handle->open) { return ERROR_PARAMETER; } if (GPT_IsEnabled(handle)) { return ERROR_GPT_ALREADY_RUNNING; } int32_t error; float actualSpeed; if ((error = GPT_GetSpeed(handle, &actualSpeed)) != ERROR_NONE) { return error; } uint32_t nCount = calculateNumCounts(timeout, actualSpeed, units); if (nCount == 0) { // The timeout is smaller than the resolution of the timer. return ERROR_GPT_TIMEOUT_INVALID; } // This is required, as the initcnt isn't propogated immediately to cnt on enable. uint32_t startCount = GPT_GetCount(handle); // Start timeout and wait for it, don't re-set the speed if ((error = GPT_Start_Freerun(handle)) != ERROR_NONE) { GPT_Stop(handle); return error; } uint32_t count; switch (handle->id) { case MT3620_UNIT_GPT0: case MT3620_UNIT_GPT1: // Timer decrements to 0. while (((count = GPT_GetCount(handle)) > (MT3620_GPT_MAX_COUNT - nCount)) || (count == startCount)) { } break; case MT3620_UNIT_GPT2: case MT3620_UNIT_GPT3: case MT3620_UNIT_GPT4: // Timer increments from 0 (we set init to 0 for GPT3). while (GPT_GetCount(handle) < nCount) {} break; default: return ERROR_UNSUPPORTED; } return GPT_Stop(handle); } static void GPT_IRQ(GPT *handle) { if (!handle || !handle->open) { return; } if (handle->callback) { handle->callback(handle); } handle->numCycles++; } /// <summary> /// Interrupt function for INT1 (used by GPT0 and GPT1) /// </summary> void gpt_int_b(void) { // Get the timer that's triggered the interrupt bool gpt0_int = MT3620_GPT_FIELD_READ(gpt_isr, gpt0_int); bool gpt1_int = MT3620_GPT_FIELD_READ(gpt_isr, gpt1_int); // Reset status flags MT3620_GPT_FIELD_WRITE(gpt_isr, gpt0_int, false); MT3620_GPT_FIELD_WRITE(gpt_isr, gpt1_int, false); // Trigger user callbacks if (gpt0_int) { GPT_IRQ(&context[0]); } if (gpt1_int) { GPT_IRQ(&context[1]); } } void gpt3_int_b(void) { GPT *handle = &context[3]; GPT_IRQ(handle); if (handle->mode == GPT_MODE_REPEAT) { GPT__SetEnabled(handle, false); GPT__SetEnabled(handle, true); } } ///------------------------Test helpers-------------------------- void GPT_GetTestSpeeds( GPT *handle, GPT_TestSpeeds *testSpeeds) { if (!handle) { return; } switch (handle->id) { case MT3620_UNIT_GPT0: case MT3620_UNIT_GPT1: case MT3620_UNIT_GPT2: // 2 speed timer testSpeeds->speeds[0] = MT3620_GPT_012_LOW_SPEED; testSpeeds->speeds[1] = MT3620_GPT_012_HIGH_SPEED; testSpeeds->count = 2; break; case MT3620_UNIT_GPT3: // multi speed timer testSpeeds->speeds[0] = MT3620_GPT_3_LOW_SPEED; testSpeeds->speeds[1] = MT3620_GPT_3_HIGH_SPEED / 8; testSpeeds->speeds[2] = MT3620_GPT_3_HIGH_SPEED / 4; testSpeeds->speeds[3] = MT3620_GPT_3_HIGH_SPEED / 2; testSpeeds->speeds[4] = MT3620_GPT_3_HIGH_SPEED; testSpeeds->count = 5; break; case MT3620_UNIT_GPT4: // 2 speed timer testSpeeds->speeds[1] = CPUFreq_Get(); testSpeeds->speeds[0] = testSpeeds->speeds[1] / 2; testSpeeds->count = 2; break; default: break; } }