forked from Firmware/Firmwares
		
	rework structure
This commit is contained in:
		| @@ -0,0 +1,252 @@ | ||||
| // See LICENSE file for license details | ||||
|  | ||||
| #include "platform.h" | ||||
|  | ||||
| #ifdef PRCI_CTRL_ADDR | ||||
| #include "fe300prci/fe300prci_driver.h" | ||||
| #include <unistd.h> | ||||
|  | ||||
| #define rdmcycle(x)  {				       \ | ||||
|     uint32_t lo, hi, hi2;			       \ | ||||
|     __asm__ __volatile__ ("1:\n\t"		       \ | ||||
| 			  "csrr %0, mcycleh\n\t"       \ | ||||
| 			  "csrr %1, mcycle\n\t"	       \ | ||||
| 			  "csrr %2, mcycleh\n\t"		\ | ||||
| 			  "bne  %0, %2, 1b\n\t"			\ | ||||
| 			  : "=r" (hi), "=r" (lo), "=r" (hi2)) ;	\ | ||||
|     *(x) = lo | ((uint64_t) hi << 32); 				\ | ||||
|   } | ||||
|  | ||||
| uint32_t PRCI_measure_mcycle_freq(uint32_t mtime_ticks, uint32_t mtime_freq) | ||||
| { | ||||
|  | ||||
|   uint32_t start_mtime = CLINT_REG(CLINT_MTIME); | ||||
|   uint32_t end_mtime = start_mtime + mtime_ticks + 1; | ||||
|  | ||||
|   // Make sure we won't get rollover. | ||||
|   while (end_mtime < start_mtime){ | ||||
|     start_mtime = CLINT_REG(CLINT_MTIME); | ||||
|     end_mtime = start_mtime + mtime_ticks + 1; | ||||
|   } | ||||
|  | ||||
|   // Don't start measuring until mtime edge. | ||||
|   uint32_t tmp = start_mtime; | ||||
|   do { | ||||
|     start_mtime = CLINT_REG(CLINT_MTIME); | ||||
|   } while (start_mtime == tmp); | ||||
|    | ||||
|   uint64_t start_mcycle; | ||||
|   rdmcycle(&start_mcycle); | ||||
|    | ||||
|   while (CLINT_REG(CLINT_MTIME) < end_mtime) ; | ||||
|    | ||||
|   uint64_t end_mcycle; | ||||
|   rdmcycle(&end_mcycle); | ||||
|   uint32_t difference = (uint32_t) (end_mcycle - start_mcycle); | ||||
|  | ||||
|   uint64_t freq = ((uint64_t) difference * mtime_freq) / mtime_ticks; | ||||
|   return (uint32_t) freq & 0xFFFFFFFF; | ||||
|    | ||||
| } | ||||
|   | ||||
|  | ||||
| void PRCI_use_hfrosc(int div, int trim) | ||||
| { | ||||
|   // Make sure the HFROSC is running at its default setting | ||||
|   // It is OK to change this even if we are running off of it. | ||||
|    | ||||
|   PRCI_REG(PRCI_HFROSCCFG) = (ROSC_DIV(div) | ROSC_TRIM(trim) | ROSC_EN(1)); | ||||
|  | ||||
|   while ((PRCI_REG(PRCI_HFROSCCFG) & ROSC_RDY(1)) == 0); | ||||
|    | ||||
|   PRCI_REG(PRCI_PLLCFG) &= ~PLL_SEL(1); | ||||
| } | ||||
|  | ||||
| void PRCI_use_pll(int refsel, int bypass, | ||||
| 			 int r, int f, int q, int finaldiv, | ||||
| 			 int hfroscdiv, int hfrosctrim) | ||||
| { | ||||
|   // Ensure that we aren't running off the PLL before we mess with it. | ||||
|   if (PRCI_REG(PRCI_PLLCFG) & PLL_SEL(1)) { | ||||
|     // Make sure the HFROSC is running at its default setting | ||||
|     PRCI_use_hfrosc(4, 16); | ||||
|   } | ||||
|    | ||||
|   // Set PLL Source to be HFXOSC if desired. | ||||
|   uint32_t config_value = 0; | ||||
|  | ||||
|   config_value |= PLL_REFSEL(refsel); | ||||
|    | ||||
|   if (bypass) { | ||||
|     // Bypass | ||||
|     config_value |= PLL_BYPASS(1); | ||||
|  | ||||
|     PRCI_REG(PRCI_PLLCFG) = config_value; | ||||
|  | ||||
|     // If we don't have an HFXTAL, this doesn't really matter. | ||||
|     // Set our Final output divide to divide-by-1: | ||||
|     PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0)); | ||||
|   } else { | ||||
|    | ||||
|     // To overclock, use the hfrosc | ||||
|     if (hfrosctrim >= 0 && hfroscdiv >= 0) { | ||||
|       PRCI_use_hfrosc(hfroscdiv, hfrosctrim); | ||||
|     } | ||||
|      | ||||
|     // Set DIV Settings for PLL | ||||
|      | ||||
|     // (Legal values of f_REF are 6-48MHz) | ||||
|  | ||||
|     // Set DIVR to divide-by-2 to get 8MHz frequency | ||||
|     // (legal values of f_R are 6-12 MHz) | ||||
|  | ||||
|     config_value |= PLL_BYPASS(1); | ||||
|     config_value |= PLL_R(r); | ||||
|  | ||||
|     // Set DIVF to get 512Mhz frequncy | ||||
|     // There is an implied multiply-by-2, 16Mhz. | ||||
|     // So need to write 32-1 | ||||
|     // (legal values of f_F are 384-768 MHz) | ||||
|     config_value |= PLL_F(f); | ||||
|  | ||||
|     // Set DIVQ to divide-by-2 to get 256 MHz frequency | ||||
|     // (legal values of f_Q are 50-400Mhz) | ||||
|     config_value |= PLL_Q(q); | ||||
|  | ||||
|     // Set our Final output divide to divide-by-1: | ||||
|     if (finaldiv == 1){ | ||||
|       PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0)); | ||||
|     } else { | ||||
|       PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV(finaldiv-1)); | ||||
|     } | ||||
|  | ||||
|     PRCI_REG(PRCI_PLLCFG) = config_value; | ||||
|  | ||||
|     // Un-Bypass the PLL. | ||||
|     PRCI_REG(PRCI_PLLCFG) &= ~PLL_BYPASS(1); | ||||
|  | ||||
|     // Wait for PLL Lock | ||||
|     // Note that the Lock signal can be glitchy. | ||||
|     // Need to wait 100 us | ||||
|     // RTC is running at 32kHz. | ||||
|     // So wait 4 ticks of RTC. | ||||
|     uint32_t now = CLINT_REG(CLINT_MTIME); | ||||
|     while (CLINT_REG(CLINT_MTIME) - now < 4) ; | ||||
|      | ||||
|     // Now it is safe to check for PLL Lock | ||||
|     while ((PRCI_REG(PRCI_PLLCFG) & PLL_LOCK(1)) == 0); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   // Switch over to PLL Clock source | ||||
|   PRCI_REG(PRCI_PLLCFG) |= PLL_SEL(1); | ||||
|  | ||||
|   // If we're running off HFXOSC, turn off the HFROSC to | ||||
|   // save power. | ||||
|   if (refsel) { | ||||
|     PRCI_REG(PRCI_HFROSCCFG) &= ~ROSC_EN(1); | ||||
|   } | ||||
|    | ||||
| } | ||||
|  | ||||
| void PRCI_use_default_clocks() | ||||
| { | ||||
|   // Turn off the LFROSC | ||||
|   AON_REG(AON_LFROSC) &= ~ROSC_EN(1); | ||||
|  | ||||
|   // Use HFROSC | ||||
|   PRCI_use_hfrosc(4, 16); | ||||
| } | ||||
|  | ||||
| void PRCI_use_hfxosc(uint32_t finaldiv) | ||||
| { | ||||
|    | ||||
|   PRCI_use_pll(1, // Use HFXTAL | ||||
| 	       1, // Bypass = 1 | ||||
| 	       0, // PLL settings don't matter | ||||
| 	       0, // PLL settings don't matter | ||||
| 	       0, // PLL settings don't matter | ||||
| 	       finaldiv, | ||||
| 	       -1, | ||||
| 	       -1); | ||||
| } | ||||
|  | ||||
| // This is a generic function, which | ||||
| // doesn't span the entire range of HFROSC settings. | ||||
| // It only adjusts the trim, which can span a hundred MHz or so. | ||||
| // This function does not check the legality of the PLL settings | ||||
| // at all, and it is quite possible to configure invalid PLL settings | ||||
| // this way. | ||||
| // It returns the actual measured CPU frequency. | ||||
|  | ||||
| uint32_t PRCI_set_hfrosctrim_for_f_cpu(uint32_t f_cpu, PRCI_freq_target target ) | ||||
| { | ||||
|  | ||||
|   uint32_t hfrosctrim = 0; | ||||
|   uint32_t hfroscdiv = 4; | ||||
|   uint32_t prev_trim = 0; | ||||
|  | ||||
|   // In this function we use PLL settings which | ||||
|   // will give us a 32x multiplier from the output | ||||
|   // of the HFROSC source to the output of the | ||||
|   // PLL. We first measure our HFROSC to get the | ||||
|   // right trim, then finally use it as the PLL source. | ||||
|   // We should really check here that the f_cpu | ||||
|   // requested is something in the limit of the PLL. For | ||||
|   // now that is up to the user. | ||||
|  | ||||
|   // This will undershoot for frequencies not divisible by 16. | ||||
|   uint32_t desired_hfrosc_freq = (f_cpu/ 16); | ||||
|  | ||||
|   PRCI_use_hfrosc(hfroscdiv, hfrosctrim); | ||||
|    | ||||
|   // Ignore the first run (for icache reasons) | ||||
|   uint32_t cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ); | ||||
|  | ||||
|   cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ); | ||||
|   uint32_t prev_freq = cpu_freq; | ||||
|    | ||||
|   while ((cpu_freq < desired_hfrosc_freq) && (hfrosctrim < 0x1F)){ | ||||
|     prev_trim = hfrosctrim; | ||||
|     prev_freq = cpu_freq; | ||||
|     hfrosctrim ++; | ||||
|     PRCI_use_hfrosc(hfroscdiv, hfrosctrim); | ||||
|     cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ); | ||||
|   }  | ||||
|  | ||||
|   // We couldn't go low enough | ||||
|   if (prev_freq > desired_hfrosc_freq){ | ||||
|     PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim); | ||||
|     cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ); | ||||
|     return cpu_freq; | ||||
|   } | ||||
|    | ||||
|   // We couldn't go high enough | ||||
|   if (cpu_freq < desired_hfrosc_freq){ | ||||
|     PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim); | ||||
|     cpu_freq = PRCI_measure_mcycle_freq(1000, RTC_FREQ); | ||||
|     return cpu_freq; | ||||
|   } | ||||
|  | ||||
|   // Check for over/undershoot | ||||
|   switch(target) { | ||||
|   case(PRCI_FREQ_CLOSEST): | ||||
|     if ((desired_hfrosc_freq - prev_freq) < (cpu_freq - desired_hfrosc_freq)) { | ||||
|       PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim); | ||||
|     } else { | ||||
|       PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, hfrosctrim); | ||||
|     } | ||||
|     break; | ||||
|   case(PRCI_FREQ_UNDERSHOOT): | ||||
|     PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, prev_trim); | ||||
|     break; | ||||
|   default: | ||||
|     PRCI_use_pll(0, 0, 1, 31, 1, 1, hfroscdiv, hfrosctrim); | ||||
|   } | ||||
|  | ||||
|   cpu_freq =  PRCI_measure_mcycle_freq(1000, RTC_FREQ); | ||||
|   return cpu_freq; | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif | ||||
| @@ -0,0 +1,79 @@ | ||||
| // See LICENSE file for license details | ||||
|  | ||||
| #ifndef _FE300PRCI_DRIVER_H_ | ||||
| #define _FE300PRCI_DRIVER_H_ | ||||
|  | ||||
| __BEGIN_DECLS | ||||
|  | ||||
| #include <unistd.h> | ||||
|  | ||||
| typedef enum prci_freq_target { | ||||
|    | ||||
|   PRCI_FREQ_OVERSHOOT, | ||||
|   PRCI_FREQ_CLOSEST, | ||||
|   PRCI_FREQ_UNDERSHOOT | ||||
|  | ||||
| } PRCI_freq_target; | ||||
|  | ||||
| /* Measure and return the approximate frequency of the  | ||||
|  * CPU, as given by measuring the mcycle counter against  | ||||
|  * the mtime ticks. | ||||
|  */ | ||||
| uint32_t PRCI_measure_mcycle_freq(uint32_t mtime_ticks, uint32_t mtime_freq); | ||||
|  | ||||
| /* Safely switch over to the HFROSC using the given div | ||||
|  * and trim settings. | ||||
|  */ | ||||
| void PRCI_use_hfrosc(int div, int trim); | ||||
|  | ||||
| /* Safely switch over to the 16MHz HFXOSC, | ||||
|  * applying the finaldiv clock divider (1 is the lowest | ||||
|  * legal value). | ||||
|  */ | ||||
| void PRCI_use_hfxosc(uint32_t finaldiv); | ||||
|  | ||||
| /* Safely switch over to the PLL using the given | ||||
|  * settings. | ||||
|  *  | ||||
|  * Note that not all combinations of the inputs are actually | ||||
|  * legal, and this function does not check for their | ||||
|  * legality ("safely" means that this function won't turn off | ||||
|  * or glitch the clock the CPU is actually running off, but | ||||
|  * doesn't protect against you making it too fast or slow.) | ||||
|  */ | ||||
|  | ||||
| void PRCI_use_pll(int refsel, int bypass, | ||||
| 			 int r, int f, int q, int finaldiv, | ||||
| 			 int hfroscdiv, int hfrosctrim); | ||||
|  | ||||
| /* Use the default clocks configured at reset. | ||||
|  * This is ~16Mhz HFROSC and turns off the LFROSC | ||||
|  * (on the current FE310 Dev Platforms, an external LFROSC is  | ||||
|  * used as it is more power efficient). | ||||
|  */ | ||||
| void PRCI_use_default_clocks(); | ||||
|  | ||||
| /* This routine will adjust the HFROSC trim | ||||
|  * while using HFROSC as the clock source,  | ||||
|  * measure the resulting frequency, then | ||||
|  * use it as the PLL clock source,  | ||||
|  * in an attempt to get over, under, or close to the  | ||||
|  * requested frequency. It returns the actual measured  | ||||
|  * frequency.  | ||||
|  * | ||||
|  * Note that the requested frequency must be within the  | ||||
|  * range supported by the PLL so not all values are  | ||||
|  * achievable with this function, and not all  | ||||
|  * are guaranteed to actually work. The PLL | ||||
|  * is rated higher than the hardware. | ||||
|  *  | ||||
|  * There is no check on the desired f_cpu frequency, it | ||||
|  * is up to the user to specify something reasonable. | ||||
|  */ | ||||
|  | ||||
| uint32_t PRCI_set_hfrosctrim_for_f_cpu(uint32_t f_cpu, PRCI_freq_target target); | ||||
|  | ||||
| __END_DECLS | ||||
|  | ||||
| #endif | ||||
|    | ||||
		Reference in New Issue
	
	Block a user