| /**************************************************************************** |
| * arch/arm/src/samv7/sam_pck.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. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <arch/board/board.h> |
| |
| #include "hardware/sam_pinmap.h" |
| #include "arm_internal.h" |
| #include "sam_gpio.h" |
| |
| #include "sam_pck.h" |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Function: sam_pck_configure |
| * |
| * Description: |
| * Configure a programmable clock output. The selected PCK is programmed |
| * to the selected frequency using either PLLA or the MCK as the source |
| * clock (depending on the value of the selected frequency). The clock |
| * is initially disabled. You must call sam_pck_enable() to enable the |
| * clock after it has been configured. |
| * |
| * Input Parameters: |
| * pckid - Identifies the programmable clock output (0, 1, or 2) |
| * clocksrc - MCK or SCK. If MCK is selected, the logic will automatically |
| * select the PLLACK clock if it seems like a better choice. |
| * frequency - Defines the desired frequency. The exact frequency may |
| * not be attainable. In this case, frequency is interpreted to be |
| * a not-to-exceed frequency. |
| * |
| * Returned Value: |
| * The actual frequency of the clock output. |
| * |
| ****************************************************************************/ |
| |
| uint32_t sam_pck_configure(enum pckid_e pckid, enum pckid_clksrc_e clksrc, |
| uint32_t frequency) |
| { |
| uint32_t regval; |
| uint32_t clkin; |
| uint32_t actual; |
| uint32_t pres; |
| |
| /* Pick a clock source. Several are possible but only MCK, PLLA, the |
| * MAINCK,or SCK are supported here. |
| */ |
| |
| switch (clksrc) |
| { |
| case PCKSRC_MCK: /* Source clock = MCK or PLLACK */ |
| { |
| /* Pick either the MCK or the PLLACK, whichever will best realize |
| * the target frequency. |
| */ |
| |
| DEBUGASSERT(BOARD_MCK_FREQUENCY < BOARD_PLLA_FREQUENCY); |
| |
| /* Pick the PLLACK if it seems like a better choice */ |
| |
| if (frequency <= BOARD_MCK_FREQUENCY || |
| frequency < BOARD_PLLA_FREQUENCY / 64) |
| { |
| regval = PMC_PCK_CSS_MCK; |
| clkin = BOARD_MCK_FREQUENCY; |
| } |
| else |
| { |
| regval = PMC_PCK_CSS_PLLA; |
| clkin = BOARD_PLLA_FREQUENCY; |
| } |
| } |
| break; |
| |
| case PCKSRC_MAINCK: /* Source clock = MAIN clock */ |
| regval = PMC_PCK_CSS_MAIN; |
| clkin = BOARD_MAINOSC_FREQUENCY; |
| break; |
| |
| case PCKSRC_SCK: /* Source clock = SCK */ |
| regval = PMC_PCK_CSS_SLOW; |
| clkin = BOARD_SLOWCLK_FREQUENCY; |
| break; |
| |
| default: |
| _err("ERROR: Unknown clock source\n"); |
| return 0; |
| } |
| |
| /* Programmable Clock frequency is selected clock frequency divided by |
| * PRES + 1 |
| */ |
| |
| pres = clkin / frequency; |
| if (pres < 1) |
| { |
| pres = 1; |
| } |
| else if (pres > 256) |
| { |
| pres = 256; |
| } |
| |
| regval |= PMC_PCK_PRES(pres - 1); |
| actual = clkin / pres; |
| |
| /* Disable the programmable clock, configure the PCK output pin, then set |
| * the selected configuration. |
| */ |
| |
| switch (pckid) |
| { |
| case PCK0: |
| putreg32(PMC_PCK0, SAM_PMC_SCDR); |
| #ifdef GPIO_PMC_PCK0 |
| cam_configgpio(GPIO_PMC_PCK0); |
| #endif |
| putreg32(regval, SAM_PMC_PCK0); |
| break; |
| |
| case PCK1: |
| putreg32(PMC_PCK1, SAM_PMC_SCDR); |
| #ifdef GPIO_PMC_PCK1 |
| cam_configgpio(GPIO_PMC_PCK1); |
| #endif |
| putreg32(regval, SAM_PMC_PCK1); |
| break; |
| |
| case PCK2: |
| putreg32(PMC_PCK2, SAM_PMC_SCDR); |
| #ifdef GPIO_PMC_PCK2 |
| cam_configgpio(GPIO_PMC_PCK2); |
| #endif |
| putreg32(regval, SAM_PMC_PCK2); |
| break; |
| |
| case PCK3: |
| putreg32(PMC_PCK3, SAM_PMC_SCDR); |
| #ifdef GPIO_PMC_PCK3 |
| cam_configgpio(GPIO_PMC_PCK3); |
| #endif |
| putreg32(regval, SAM_PMC_PCK3); |
| break; |
| |
| case PCK4: |
| putreg32(PMC_PCK4, SAM_PMC_SCDR); |
| #ifdef GPIO_PMC_PCK4 |
| cam_configgpio(GPIO_PMC_PCK4); |
| #endif |
| putreg32(regval, SAM_PMC_PCK4); |
| break; |
| |
| case PCK5: |
| putreg32(PMC_PCK5, SAM_PMC_SCDR); |
| #ifdef GPIO_PMC_PCK5 |
| cam_configgpio(GPIO_PMC_PCK5); |
| #endif |
| putreg32(regval, SAM_PMC_PCK5); |
| break; |
| |
| case PCK6: |
| putreg32(PMC_PCK6, SAM_PMC_SCDR); |
| #ifdef GPIO_PMC_PCK6 |
| cam_configgpio(GPIO_PMC_PCK6); |
| #endif |
| putreg32(regval, SAM_PMC_PCK6); |
| break; |
| |
| default: |
| return -EINVAL; |
| } |
| |
| /* And return the actual frequency */ |
| |
| return actual; |
| } |
| |
| /**************************************************************************** |
| * Function: sam_pck_frequency |
| * |
| * Description: |
| * Return the frequency if the programmable clock |
| * |
| * Input Parameters: |
| * pckid - Identifies the programmable clock output (0, 1, .., 6) |
| * |
| * Returned Value: |
| * The frequency of the programmable clock (which may or may not be |
| * enabled). |
| * |
| ****************************************************************************/ |
| |
| uint32_t sam_pck_frequency(enum pckid_e pckid) |
| { |
| uintptr_t regaddr; |
| uint32_t regval; |
| uint32_t clkin; |
| uint32_t presc; |
| |
| /* Get the programmable clock configuration */ |
| |
| regaddr = SAM_PMC_PCK((int)pckid); |
| regval = getreg32(regaddr); |
| |
| /* Get the frequency of the clock source */ |
| |
| switch (regval & PMC_PCK_CSS_MASK) |
| { |
| case PMC_PCK_CSS_SLOW: /* Slow Clock */ |
| clkin = BOARD_SLOWCLK_FREQUENCY; |
| break; |
| |
| case PMC_PCK_CSS_MAIN: /* Main Clock */ |
| clkin = BOARD_MAINOSC_FREQUENCY; |
| break; |
| |
| case PMC_PCK_CSS_PLLA: /* PLLA Clock */ |
| clkin = BOARD_PLLA_FREQUENCY; |
| break; |
| |
| #ifdef BOARD_UPLL_FREQUENCY |
| case PMC_PCK_CSS_UPLL: /* Divided UPLL Clock */ |
| clkin = BOARD_UPLL_FREQUENCY; |
| break; |
| #endif |
| |
| case PMC_PCK_CSS_MCK: /* Master Clock */ |
| clkin = BOARD_MCK_FREQUENCY; |
| break; |
| |
| default: |
| _err("ERROR: Unknown clock source\n"); |
| return 0; |
| } |
| |
| /* Get the prescaler value */ |
| |
| presc = (regval & PMC_PCK_PRES_MASK) >> PMC_PCK_PRES_SHIFT; |
| return clkin / (presc + 1); |
| } |
| |
| /**************************************************************************** |
| * Function: sam_pck_enable |
| * |
| * Description: |
| * Enable or disable a programmable clock output. |
| * |
| * Input Parameters: |
| * pckid - Identifies the programmable clock output (0, 1, .., 6) |
| * enable - True: enable the clock output, False: disable the clock output |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| void sam_pck_enable(enum pckid_e pckid, bool enable) |
| { |
| uintptr_t regaddr; |
| uint32_t regval; |
| |
| /* Select the bit in the PMC_SDER or PMC_SCER corresponding to the |
| * programmable clock. |
| */ |
| |
| regval = PMC_PCK(pckid); |
| |
| /* Select the SDER or SCER */ |
| |
| regaddr = enable ? SAM_PMC_SCER : SAM_PMC_SCDR; |
| |
| /* And do the deed */ |
| |
| putreg32(regval, regaddr); |
| } |
| |
| /**************************************************************************** |
| * Function: sam_pck_isenabled |
| * |
| * Description: |
| * Return true if the programmable clock is enabled. |
| * |
| * Input Parameters: |
| * pckid - Identifies the programmable clock output (0, 1, .., 6) |
| * |
| * Returned Value: |
| * True if the specified programmable clock is enabled |
| * |
| ****************************************************************************/ |
| |
| bool sam_pck_isenabled(enum pckid_e pckid) |
| { |
| uint32_t mask; |
| |
| /* Select the bit in the PMC_SCSR corresponding to the programmable |
| * clock. |
| */ |
| |
| mask = PMC_PCK(pckid); |
| |
| /* Return true if the bit is set */ |
| |
| return (getreg32(SAM_PMC_SCSR) & mask) != 0; |
| } |