| /**************************************************************************** |
| * arch/arm/src/sama5/sam_pmc.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| |
| /* References: |
| * SAMA5D3 Series Data Sheet |
| */ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdint.h> |
| |
| #include <arch/board/board.h> |
| |
| #include "arm_internal.h" |
| #include "chip.h" |
| |
| #ifdef CONFIG_ARCH_HAVE_SDIO |
| # include "hardware/sam_hsmci.h" |
| #endif |
| |
| #include "hardware/sam_pmc.h" |
| #include "sam_pmc.h" |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: sam_pllack_frequency |
| * |
| * Description: |
| * Given the Main Clock frequency that provides the input to PLLA, return |
| * the frequency of the PPA output clock, PLLACK |
| * |
| * Assumptions: |
| * PLLA is enabled. If the PLL is is disabled, either at the input divider |
| * or the output multiplier, the value zero is returned. |
| * |
| ****************************************************************************/ |
| |
| uint32_t sam_pllack_frequency(uint32_t mainclk) |
| { |
| uint32_t regval; |
| #ifdef SAMA5_HAVE_PLLAR_DIV |
| uint32_t diva; |
| #endif |
| uint32_t mula; |
| uint32_t pllack; |
| |
| /* Get the PLLA configuration. We will multiply (and possibly divide) |
| * the Main Clock to get the PLLA output clock (PLLACK). |
| */ |
| |
| regval = getreg32(SAM_PMC_CKGR_PLLAR); |
| pllack = mainclk; |
| |
| #ifdef SAMA5_HAVE_PLLAR_DIV |
| /* Get the PLLA divider (DIVA) |
| * |
| * DIVA = 0: Divider output is 0 |
| * DIVA = 1: Divider is bypassed |
| * DIVA = 2-255: Divider output is the selected clock divided by DIVA |
| */ |
| |
| diva = (regval & PMC_CKGR_PLLAR_DIV_MASK) >> PMC_CKGR_PLLAR_DIV_SHIFT; |
| if (diva > 1) |
| { |
| pllack /= diva; |
| } |
| else if (diva < 1) |
| { |
| return 0; |
| } |
| #endif |
| |
| /* Get the PLLA multiplier (MULA) |
| * |
| * MULA = 0: PLLA is deactivated |
| * MULA > 0: The PLLA Clock frequency is the PLLA input frequency |
| * multiplied by MULA + 1. |
| */ |
| |
| mula = (regval & PMC_CKGR_PLLAR_MUL_MASK) >> PMC_CKGR_PLLAR_MUL_SHIFT; |
| if (mula > 0) |
| { |
| pllack *= (mula + 1); |
| } |
| else |
| { |
| return 0; |
| } |
| |
| return pllack; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_plladiv2_frequency |
| * |
| * Description: |
| * The PLLACK input to most clocking may or may not be divided by two. |
| * This function will return the possibly divided PLLACK clock input |
| * frequency. |
| * |
| * Assumptions: |
| * See sam_pllack_frequency. |
| * |
| ****************************************************************************/ |
| |
| uint32_t sam_plladiv2_frequency(uint32_t mainclk) |
| { |
| uint32_t regval; |
| uint32_t pllack; |
| |
| /* Get the PLLA output clock */ |
| |
| pllack = sam_pllack_frequency(mainclk); |
| if (pllack == 0) |
| { |
| return 0; |
| } |
| |
| /* Check if the PLLACK output is divided by 2 */ |
| |
| regval = getreg32(SAM_PMC_MCKR); |
| if ((regval & PMC_MCKR_PLLADIV2) != 0) |
| { |
| pllack >>= 1; |
| } |
| |
| return pllack; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_pck_frequency |
| * |
| * Description: |
| * Given the Main Clock frequency that provides the input to PLLA, return |
| * the frequency of the processor clock (PCK). |
| * |
| * Assumptions: |
| * PLLA is enabled and the either the main clock or the PLLA output clock |
| * (PLLACK) provides the input to the MCK prescaler. |
| * |
| ****************************************************************************/ |
| |
| uint32_t sam_pck_frequency(uint32_t mainclk) |
| { |
| uint32_t regval; |
| uint32_t pres; |
| uint32_t pck; |
| |
| /* Get the input source selection to the master/processor clock divider */ |
| |
| regval = getreg32(SAM_PMC_MCKR); |
| switch (regval & PMC_MCKR_CSS_MASK) |
| { |
| case PMC_MCKR_CSS_MAIN: /* Main Clock */ |
| |
| /* Use the Main Clock frequency */ |
| |
| pck = mainclk; |
| break; |
| |
| case PMC_MCKR_CSS_PLLA: /* PLLA Clock */ |
| |
| /* Use the PLLA output clock */ |
| |
| pck = sam_plladiv2_frequency(mainclk); |
| if (pck == 0) |
| { |
| return 0; |
| } |
| break; |
| |
| case PMC_MCKR_CSS_SLOW: /* Slow Clock */ |
| case PMC_MCKR_CSS_UPLL: /* UPLL Clock */ |
| default: |
| return 0; |
| } |
| |
| /* Get the PCK frequency which is given by the selected input clock |
| * divided by a power-of-two prescaler. |
| * |
| * PRES = 0: Selected clock |
| * PRES = n > 0: Selected clock divided by 2**n |
| */ |
| |
| pres = (regval & PMC_MCKR_PRES_MASK) >> PMC_MCKR_PRES_SHIFT; |
| return pck >> pres; |
| } |
| |
| /**************************************************************************** |
| * Name: sam_mck_frequency |
| * |
| * Description: |
| * Given the Main Clock frequency that provides the input to PLLA, return |
| * the frequency of the PPA output clock, PLLACK |
| * |
| * Assumptions: |
| * PLLA is enabled and the either the main clock or the PLLA output clock |
| * (PLLACK) provides the input to the MCK prescaler. |
| * |
| ****************************************************************************/ |
| |
| uint32_t sam_mck_frequency(uint32_t mainclk) |
| { |
| uint32_t regval; |
| uint32_t mdiv; |
| uint32_t mck; |
| |
| /* The MCK frequency is equivalent to the PCK clock frequency with an |
| * additional divider. |
| */ |
| |
| mck = sam_pck_frequency(mainclk); |
| if (mck == 0) |
| { |
| return 0; |
| } |
| |
| /* MDIV = n: |
| * Master Clock is Prescaler Output Clock divided by encoded value |
| */ |
| |
| regval = getreg32(SAM_PMC_MCKR); |
| switch (regval & PMC_MCKR_MDIV_MASK) |
| { |
| case PMC_MCKR_MDIV_PCKDIV1: |
| return mck; |
| |
| case PMC_MCKR_MDIV_PCKDIV2: |
| mdiv = 2; |
| break; |
| |
| case PMC_MCKR_MDIV_PCKDIV3: |
| mdiv = 3; |
| break; |
| |
| case PMC_MCKR_MDIV_PCKDIV4: |
| mdiv = 4; |
| break; |
| |
| default: |
| return 0; |
| } |
| |
| return mck / mdiv; |
| } |