| /**************************************************************************** |
| * arch/arm/src/xmc4/xmc4_ccu4.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * XMC CCU Driver |
| * |
| * For now, this file contains only helper methods mandatory for xmc tickless |
| * feature. Contibutions are welcomed. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| #include <arch/board/board.h> |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include "arm_internal.h" |
| #include "hardware/xmc4_ccu4.h" |
| #include "xmc4_ccu4.h" |
| |
| #define CCU4_NDIVIDERS 15 |
| |
| static const uint8_t g_log2divider[CCU4_NDIVIDERS] = |
| { |
| 1, /* TIMER_CLOCK1 -> div2 */ |
| 2, /* TIMER_CLOCK1 -> div4 */ |
| 3, /* TIMER_CLOCK2 -> div8 */ |
| 4, /* TIMER_CLOCK2 -> div16 */ |
| 5, /* TIMER_CLOCK3 -> div32 */ |
| 6, /* TIMER_CLOCK3 -> div64 */ |
| 7, /* TIMER_CLOCK4 -> div128 */ |
| 8, /* TIMER_CLOCK4 -> div256 */ |
| 9, /* TIMER_CLOCK4 -> div512 */ |
| 10, /* TIMER_CLOCK4 -> div1024 */ |
| 11, /* TIMER_CLOCK4 -> div2048 */ |
| 12, /* TIMER_CLOCK4 -> div4096 */ |
| 13, /* TIMER_CLOCK4 -> div8192 */ |
| 14, /* TIMER_CLOCK4 -> div16384 */ |
| 15 /* TIMER_CLOCK4 -> div32769 */ |
| }; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: xmc4_ccu4_divfreq_lookup |
| * |
| * Description: |
| * Given the TC input frequency (Ftcin) and a divider index, return the |
| * value of the divided frequency |
| * |
| * Input Parameters: |
| * ftcin - TC input frequency |
| * ndx - Divider index |
| * |
| * Returned Value: |
| * The divided frequency value |
| * |
| ****************************************************************************/ |
| |
| static uint32_t xmc4_ccu4_divfreq_lookup(uint32_t ftcin, int ndx) |
| { |
| return ftcin >> g_log2divider[ndx]; |
| } |
| |
| /**************************************************************************** |
| * Name: xmc4_ccu4_freqdiv_lookup |
| * |
| * Description: |
| * Given the TC input frequency (Ftcin) and a divider index, return the |
| * value of the Ftcin divider. |
| * |
| * Input Parameters: |
| * ftcin - TC input frequency |
| * ndx - Divider index |
| * |
| * Returned Value: |
| * The Ftcin input divider value |
| * |
| ****************************************************************************/ |
| |
| static int xmc4_ccu4_freqdiv_lookup(uint32_t ftcin, int ndx) |
| { |
| return 1 << g_log2divider[ndx]; |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: xmc4_ccu4_divisor |
| * |
| * Description: |
| * Finds the best MCK divisor given the timer frequency and MCK. The |
| * result is guaranteed to satisfy the following equation: |
| * |
| * (Ftcin / (div * 65536)) <= freq <= (Ftcin / dev) |
| * |
| * where: |
| * freq - the desired frequency |
| * Ftcin - The timer/counter input frequency |
| * div - With DIV being the highest possible value. |
| * |
| * Input Parameters: |
| * frequency Desired timer frequency. |
| * div Divisor value. |
| * pssiv PSSIV field value for divisor. |
| * |
| * Returned Value: |
| * Zero (OK) if a proper divisor has been found, otherwise a negated errno |
| * value indicating the nature of the failure. |
| * |
| ****************************************************************************/ |
| |
| int xmc4_ccu4_divisor(uint32_t frequency, uint32_t *div, uint32_t *pssiv) |
| { |
| uint32_t ftcin = BOARD_CCU_FREQUENCY; |
| int ndx = 0; |
| |
| tmrinfo("frequency=%" PRIu32 "\n", frequency); |
| |
| /* Satisfy lower bound. That is, the value of the divider such that: |
| * |
| * frequency >= (tc_input_frequency * 65536) / divider. |
| */ |
| |
| while (frequency < (xmc4_ccu4_divfreq_lookup(ftcin, ndx) >> 16)) |
| { |
| if (++ndx > CCU4_NDIVIDERS) |
| { |
| /* If no divisor can be found, return -ERANGE */ |
| |
| tmrerr("ERROR: Lower bound search failed\n"); |
| return -ERANGE; |
| } |
| } |
| |
| /* Try to maximize DIV while still satisfying upper bound. That the |
| * value of the divider such that: |
| * |
| * frequency < tc_input_frequency / divider. |
| */ |
| |
| for (; ndx < (CCU4_NDIVIDERS - 1); ndx++) |
| { |
| if (frequency > xmc4_ccu4_divfreq_lookup(ftcin, ndx + 1)) |
| { |
| break; |
| } |
| } |
| |
| /* Return the divider value */ |
| |
| if (div) |
| { |
| uint32_t value = xmc4_ccu4_freqdiv_lookup(ftcin, ndx); |
| tmrinfo("return div=%lu\n", (unsigned long)value); |
| *div = value; |
| } |
| |
| /* Return the PSSIV selection */ |
| |
| if (pssiv) |
| { |
| tmrinfo("return pssiv=%d\n", ndx + 1); |
| *pssiv = ndx + 1; |
| } |
| |
| return OK; |
| } |