/***************************************************************************** * * \file * * \brief Power Manager driver. * * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * * \page License * * Subject to your compliance with these terms, you may use Microchip * software and any derivatives exclusively with Microchip products. * It is your responsibility to comply with third party license terms applicable * to your use of third party software (including open source software) that * may accompany Microchip software. * * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. * * \asf_license_stop * *****************************************************************************/ /* * Support and FAQ: visit Microchip Support */ #include "compiler.h" #include "pm.h" /*! \name PM Writable Bit-Field Registers */ //! @{ typedef union { unsigned long mcctrl; avr32_pm_mcctrl_t MCCTRL; } u_avr32_pm_mcctrl_t; typedef union { unsigned long cksel; avr32_pm_cksel_t CKSEL; } u_avr32_pm_cksel_t; typedef union { unsigned long pll; avr32_pm_pll_t PLL; } u_avr32_pm_pll_t; typedef union { unsigned long oscctrl0; avr32_pm_oscctrl0_t OSCCTRL0; } u_avr32_pm_oscctrl0_t; typedef union { unsigned long oscctrl1; avr32_pm_oscctrl1_t OSCCTRL1; } u_avr32_pm_oscctrl1_t; typedef union { unsigned long oscctrl32; avr32_pm_oscctrl32_t OSCCTRL32; } u_avr32_pm_oscctrl32_t; typedef union { unsigned long ier; avr32_pm_ier_t IER; } u_avr32_pm_ier_t; typedef union { unsigned long idr; avr32_pm_idr_t IDR; } u_avr32_pm_idr_t; typedef union { unsigned long icr; avr32_pm_icr_t ICR; } u_avr32_pm_icr_t; typedef union { unsigned long gcctrl; avr32_pm_gcctrl_t GCCTRL; } u_avr32_pm_gcctrl_t; typedef union { unsigned long rccr; avr32_pm_rccr_t RCCR; } u_avr32_pm_rccr_t; typedef union { unsigned long bgcr; avr32_pm_bgcr_t BGCR; } u_avr32_pm_bgcr_t; typedef union { unsigned long vregcr; avr32_pm_vregcr_t VREGCR; } u_avr32_pm_vregcr_t; typedef union { unsigned long bod; avr32_pm_bod_t BOD; } u_avr32_pm_bod_t; //! @} /*! \brief Sets the mode of the oscillator 0. * * \param pm Base address of the Power Manager (i.e. &AVR32_PM). * \param mode Oscillator 0 mode (i.e. AVR32_PM_OSCCTRL0_MODE_x). */ static void pm_set_osc0_mode(volatile avr32_pm_t *pm, unsigned int mode) { // Read u_avr32_pm_oscctrl0_t u_avr32_pm_oscctrl0 = {pm->oscctrl0}; // Modify u_avr32_pm_oscctrl0.OSCCTRL0.mode = mode; // Write pm->oscctrl0 = u_avr32_pm_oscctrl0.oscctrl0; } void pm_enable_osc0_ext_clock(volatile avr32_pm_t *pm) { pm_set_osc0_mode(pm, AVR32_PM_OSCCTRL0_MODE_EXT_CLOCK); } void pm_enable_osc0_crystal(volatile avr32_pm_t *pm, unsigned int fosc0) { pm_set_osc0_mode(pm, (fosc0 < 900000) ? AVR32_PM_OSCCTRL0_MODE_CRYSTAL_G0 : (fosc0 < 3000000) ? AVR32_PM_OSCCTRL0_MODE_CRYSTAL_G1 : (fosc0 < 8000000) ? AVR32_PM_OSCCTRL0_MODE_CRYSTAL_G2 : AVR32_PM_OSCCTRL0_MODE_CRYSTAL_G3); } void pm_enable_clk0(volatile avr32_pm_t *pm, unsigned int startup) { pm_enable_clk0_no_wait(pm, startup); pm_wait_for_clk0_ready(pm); } void pm_disable_clk0(volatile avr32_pm_t *pm) { pm->mcctrl &= ~AVR32_PM_MCCTRL_OSC0EN_MASK; } void pm_enable_clk0_no_wait(volatile avr32_pm_t *pm, unsigned int startup) { // Read register u_avr32_pm_oscctrl0_t u_avr32_pm_oscctrl0 = {pm->oscctrl0}; // Modify u_avr32_pm_oscctrl0.OSCCTRL0.startup = startup; // Write back pm->oscctrl0 = u_avr32_pm_oscctrl0.oscctrl0; pm->mcctrl |= AVR32_PM_MCCTRL_OSC0EN_MASK; } void pm_wait_for_clk0_ready(volatile avr32_pm_t *pm) { while (!(pm->poscsr & AVR32_PM_POSCSR_OSC0RDY_MASK)); } /*! \brief Sets the mode of the oscillator 1. * * \param pm Base address of the Power Manager (i.e. &AVR32_PM). * \param mode Oscillator 1 mode (i.e. AVR32_PM_OSCCTRL1_MODE_x). */ static void pm_set_osc1_mode(volatile avr32_pm_t *pm, unsigned int mode) { // Read u_avr32_pm_oscctrl1_t u_avr32_pm_oscctrl1 = {pm->oscctrl1}; // Modify u_avr32_pm_oscctrl1.OSCCTRL1.mode = mode; // Write pm->oscctrl1 = u_avr32_pm_oscctrl1.oscctrl1; } void pm_enable_osc1_ext_clock(volatile avr32_pm_t *pm) { pm_set_osc1_mode(pm, AVR32_PM_OSCCTRL1_MODE_EXT_CLOCK); } void pm_enable_osc1_crystal(volatile avr32_pm_t *pm, unsigned int fosc1) { pm_set_osc1_mode(pm, (fosc1 < 900000) ? AVR32_PM_OSCCTRL1_MODE_CRYSTAL_G0 : (fosc1 < 3000000) ? AVR32_PM_OSCCTRL1_MODE_CRYSTAL_G1 : (fosc1 < 8000000) ? AVR32_PM_OSCCTRL1_MODE_CRYSTAL_G2 : AVR32_PM_OSCCTRL1_MODE_CRYSTAL_G3); } void pm_enable_clk1(volatile avr32_pm_t *pm, unsigned int startup) { pm_enable_clk1_no_wait(pm, startup); pm_wait_for_clk1_ready(pm); } void pm_disable_clk1(volatile avr32_pm_t *pm) { pm->mcctrl &= ~AVR32_PM_MCCTRL_OSC1EN_MASK; } void pm_enable_clk1_no_wait(volatile avr32_pm_t *pm, unsigned int startup) { // Read register u_avr32_pm_oscctrl1_t u_avr32_pm_oscctrl1 = {pm->oscctrl1}; // Modify u_avr32_pm_oscctrl1.OSCCTRL1.startup = startup; // Write back pm->oscctrl1 = u_avr32_pm_oscctrl1.oscctrl1; pm->mcctrl |= AVR32_PM_MCCTRL_OSC1EN_MASK; } void pm_wait_for_clk1_ready(volatile avr32_pm_t *pm) { while (!(pm->poscsr & AVR32_PM_POSCSR_OSC1RDY_MASK)); } /*! \brief Sets the mode of the 32-kHz oscillator. * * \param pm Base address of the Power Manager (i.e. &AVR32_PM). * \param mode 32-kHz oscillator mode (i.e. AVR32_PM_OSCCTRL32_MODE_x). */ static void pm_set_osc32_mode(volatile avr32_pm_t *pm, unsigned int mode) { // Read u_avr32_pm_oscctrl32_t u_avr32_pm_oscctrl32 = {pm->oscctrl32}; // Modify u_avr32_pm_oscctrl32.OSCCTRL32.mode = mode; // Write pm->oscctrl32 = u_avr32_pm_oscctrl32.oscctrl32; } void pm_enable_osc32_ext_clock(volatile avr32_pm_t *pm) { pm_set_osc32_mode(pm, AVR32_PM_OSCCTRL32_MODE_EXT_CLOCK); } void pm_enable_osc32_crystal(volatile avr32_pm_t *pm) { pm_set_osc32_mode(pm, AVR32_PM_OSCCTRL32_MODE_CRYSTAL); } void pm_enable_clk32(volatile avr32_pm_t *pm, unsigned int startup) { pm_enable_clk32_no_wait(pm, startup); pm_wait_for_clk32_ready(pm); } void pm_disable_clk32(volatile avr32_pm_t *pm) { pm->oscctrl32 &= ~AVR32_PM_OSCCTRL32_OSC32EN_MASK; } void pm_enable_clk32_no_wait(volatile avr32_pm_t *pm, unsigned int startup) { // Read register u_avr32_pm_oscctrl32_t u_avr32_pm_oscctrl32 = {pm->oscctrl32}; // Modify u_avr32_pm_oscctrl32.OSCCTRL32.osc32en = 1; u_avr32_pm_oscctrl32.OSCCTRL32.startup = startup; // Write back pm->oscctrl32 = u_avr32_pm_oscctrl32.oscctrl32; } void pm_wait_for_clk32_ready(volatile avr32_pm_t *pm) { while (!(pm->poscsr & AVR32_PM_POSCSR_OSC32RDY_MASK)); } void pm_cksel_get(volatile avr32_pm_t *pm, unsigned long *p_cksel) { *p_cksel = pm->cksel; } void pm_cksel_set(volatile avr32_pm_t *pm, unsigned long cksel) { pm->cksel = cksel; // Wait for ckrdy bit and then clear it while (!(pm->poscsr & AVR32_PM_POSCSR_CKRDY_MASK)); } void pm_cksel(volatile avr32_pm_t *pm, unsigned int pbadiv, unsigned int pbasel, unsigned int pbbdiv, unsigned int pbbsel, unsigned int hsbdiv, unsigned int hsbsel) { u_avr32_pm_cksel_t u_avr32_pm_cksel = {0}; u_avr32_pm_cksel.CKSEL.cpusel = hsbsel; u_avr32_pm_cksel.CKSEL.cpudiv = hsbdiv; u_avr32_pm_cksel.CKSEL.hsbsel = hsbsel; u_avr32_pm_cksel.CKSEL.hsbdiv = hsbdiv; u_avr32_pm_cksel.CKSEL.pbasel = pbasel; u_avr32_pm_cksel.CKSEL.pbadiv = pbadiv; u_avr32_pm_cksel.CKSEL.pbbsel = pbbsel; u_avr32_pm_cksel.CKSEL.pbbdiv = pbbdiv; pm->cksel = u_avr32_pm_cksel.cksel; // Wait for ckrdy bit and then clear it while (!(pm->poscsr & AVR32_PM_POSCSR_CKRDY_MASK)); } void pm_gc_setup(volatile avr32_pm_t *pm, unsigned int gc, unsigned int osc_or_pll, // Use Osc (=0) or PLL (=1) unsigned int pll_osc, // Sel Osc0/PLL0 or Osc1/PLL1 unsigned int diven, unsigned int div) { u_avr32_pm_gcctrl_t u_avr32_pm_gcctrl = {0}; u_avr32_pm_gcctrl.GCCTRL.oscsel = pll_osc; u_avr32_pm_gcctrl.GCCTRL.pllsel = osc_or_pll; u_avr32_pm_gcctrl.GCCTRL.diven = diven; u_avr32_pm_gcctrl.GCCTRL.div = div; pm->gcctrl[gc] = u_avr32_pm_gcctrl.gcctrl; } void pm_gc_enable(volatile avr32_pm_t *pm, unsigned int gc) { pm->gcctrl[gc] |= AVR32_PM_GCCTRL_CEN_MASK; } void pm_gc_disable(volatile avr32_pm_t *pm, unsigned int gc) { pm->gcctrl[gc] &= ~AVR32_PM_GCCTRL_CEN_MASK; } void pm_pll_setup(volatile avr32_pm_t *pm, unsigned int pll, unsigned int mul, unsigned int div, unsigned int osc, unsigned int lockcount) { u_avr32_pm_pll_t u_avr32_pm_pll = {0}; u_avr32_pm_pll.PLL.pllosc = osc; u_avr32_pm_pll.PLL.plldiv = div; u_avr32_pm_pll.PLL.pllmul = mul; u_avr32_pm_pll.PLL.pllcount = lockcount; pm->pll[pll] = u_avr32_pm_pll.pll; } void pm_pll_set_option(volatile avr32_pm_t *pm, unsigned int pll, unsigned int pll_freq, unsigned int pll_div2, unsigned int pll_wbwdisable) { u_avr32_pm_pll_t u_avr32_pm_pll = {pm->pll[pll]}; u_avr32_pm_pll.PLL.pllopt = pll_freq | (pll_div2 << 1) | (pll_wbwdisable << 2); pm->pll[pll] = u_avr32_pm_pll.pll; } unsigned int pm_pll_get_option(volatile avr32_pm_t *pm, unsigned int pll) { return (pm->pll[pll] & AVR32_PM_PLLOPT_MASK) >> AVR32_PM_PLLOPT_OFFSET; } void pm_pll_enable(volatile avr32_pm_t *pm, unsigned int pll) { pm->pll[pll] |= AVR32_PM_PLLEN_MASK; } void pm_pll_disable(volatile avr32_pm_t *pm, unsigned int pll) { pm->pll[pll] &= ~AVR32_PM_PLLEN_MASK; } void pm_wait_for_pll0_locked(volatile avr32_pm_t *pm) { while (!(pm->poscsr & AVR32_PM_POSCSR_LOCK0_MASK)); } void pm_wait_for_pll1_locked(volatile avr32_pm_t *pm) { while (!(pm->poscsr & AVR32_PM_POSCSR_LOCK1_MASK)); } unsigned long pm_get_clock(volatile avr32_pm_t *pm) { u_avr32_pm_mcctrl_t u_avr32_pm_mcctrl = {pm->mcctrl}; return u_avr32_pm_mcctrl.MCCTRL.mcsel; } void pm_switch_to_clock(volatile avr32_pm_t *pm, unsigned long clock) { // Read u_avr32_pm_mcctrl_t u_avr32_pm_mcctrl = {pm->mcctrl}; // Modify u_avr32_pm_mcctrl.MCCTRL.mcsel = clock; // Write back pm->mcctrl = u_avr32_pm_mcctrl.mcctrl; } void pm_switch_to_osc0(volatile avr32_pm_t *pm, unsigned int fosc0, unsigned int startup) { pm_enable_osc0_crystal(pm, fosc0); // Enable the Osc0 in crystal mode pm_enable_clk0(pm, startup); // Crystal startup time - This parameter is critical and depends on the characteristics of the crystal pm_switch_to_clock(pm, AVR32_PM_MCSEL_OSC0); // Then switch main clock to Osc0 } void pm_bod_enable_irq(volatile avr32_pm_t *pm) { pm->ier = AVR32_PM_IER_BODDET_MASK; } void pm_bod_disable_irq(volatile avr32_pm_t *pm) { bool global_interrupt_enabled = cpu_irq_is_enabled(); if (global_interrupt_enabled) cpu_irq_disable(); pm->idr = AVR32_PM_IDR_BODDET_MASK; pm->isr; if (global_interrupt_enabled) cpu_irq_enable(); } void pm_bod_clear_irq(volatile avr32_pm_t *pm) { pm->icr = AVR32_PM_ICR_BODDET_MASK; } unsigned long pm_bod_get_irq_status(volatile avr32_pm_t *pm) { return ((pm->isr & AVR32_PM_ISR_BODDET_MASK) != 0); } unsigned long pm_bod_get_irq_enable_bit(volatile avr32_pm_t *pm) { return ((pm->imr & AVR32_PM_IMR_BODDET_MASK) != 0); } unsigned long pm_bod_get_level(volatile avr32_pm_t *pm) { return (pm->bod & AVR32_PM_BOD_LEVEL_MASK) >> AVR32_PM_BOD_LEVEL_OFFSET; } unsigned long pm_read_gplp(volatile avr32_pm_t *pm, unsigned long gplp) { return pm->gplp[gplp]; } void pm_write_gplp(volatile avr32_pm_t *pm, unsigned long gplp, unsigned long value) { pm->gplp[gplp] = value; } long pm_enable_module(volatile avr32_pm_t *pm, unsigned long module) { unsigned long domain = module>>5; unsigned long *regptr = (unsigned long*)(&(pm->cpumask) + domain); // Implementation-specific shortcut: the ckMASK registers are contiguous and // memory-mapped in that order: CPUMASK, HSBMASK, PBAMASK, PBBMASK. *regptr |= (1<<(module%32)); return PASS; } long pm_disable_module(volatile avr32_pm_t *pm, unsigned long module) { unsigned long domain = module>>5; unsigned long *regptr = (unsigned long*)(&(pm->cpumask) + domain); // Implementation-specific shortcut: the ckMASK registers are contiguous and // memory-mapped in that order: CPUMASK, HSBMASK, PBAMASK, PBBMASK. *regptr &= ~(1<<(module%32)); return PASS; }