diff options
Diffstat (limited to 'code/fe310/bsp/drivers')
-rw-r--r-- | code/fe310/bsp/drivers/Makefile | 10 | ||||
-rw-r--r-- | code/fe310/bsp/drivers/plic_driver.c | 127 | ||||
-rw-r--r-- | code/fe310/bsp/drivers/plic_driver.h | 51 | ||||
-rw-r--r-- | code/fe310/bsp/drivers/prci_driver.c | 265 | ||||
-rw-r--r-- | code/fe310/bsp/drivers/prci_driver.h | 80 |
5 files changed, 533 insertions, 0 deletions
diff --git a/code/fe310/bsp/drivers/Makefile b/code/fe310/bsp/drivers/Makefile new file mode 100644 index 0000000..14fed05 --- /dev/null +++ b/code/fe310/bsp/drivers/Makefile @@ -0,0 +1,10 @@ +include ../../common.mk +CFLAGS += -I../include -include sys/cdefs.h + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +all: plic_driver.o prci_driver.o + +clean: + rm -f *.o diff --git a/code/fe310/bsp/drivers/plic_driver.c b/code/fe310/bsp/drivers/plic_driver.c new file mode 100644 index 0000000..27b9d2c --- /dev/null +++ b/code/fe310/bsp/drivers/plic_driver.c @@ -0,0 +1,127 @@ +// See LICENSE for license details. + +#include "sifive/devices/plic.h" +#include "plic_driver.h" +#include "platform.h" +#include "encoding.h" +#include <string.h> + + +// Note that there are no assertions or bounds checking on these +// parameter values. + +void volatile_memzero(uint8_t * base, unsigned int size) +{ + volatile uint8_t * ptr; + for (ptr = base; ptr < (base + size); ptr++){ + *ptr = 0; + } +} + +void PLIC_init ( + plic_instance_t * this_plic, + uintptr_t base_addr, + uint32_t num_sources, + uint32_t num_priorities + ) +{ + + this_plic->base_addr = base_addr; + this_plic->num_sources = num_sources; + this_plic->num_priorities = num_priorities; + + // Disable all interrupts (don't assume that these registers are reset). + unsigned long hart_id = read_csr(mhartid); + volatile_memzero((uint8_t*) (this_plic->base_addr + + PLIC_ENABLE_OFFSET + + (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET)), + (num_sources + 8) / 8); + + // Set all priorities to 0 (equal priority -- don't assume that these are reset). + volatile_memzero ((uint8_t *)(this_plic->base_addr + + PLIC_PRIORITY_OFFSET), + (num_sources + 1) << PLIC_PRIORITY_SHIFT_PER_SOURCE); + + // Set the threshold to 0. + volatile plic_threshold* threshold = (plic_threshold*) + (this_plic->base_addr + + PLIC_THRESHOLD_OFFSET + + (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET)); + + *threshold = 0; + +} + +void PLIC_set_threshold (plic_instance_t * this_plic, + plic_threshold threshold){ + + unsigned long hart_id = read_csr(mhartid); + volatile plic_threshold* threshold_ptr = (plic_threshold*) (this_plic->base_addr + + PLIC_THRESHOLD_OFFSET + + (hart_id << PLIC_THRESHOLD_SHIFT_PER_TARGET)); + + *threshold_ptr = threshold; + +} + + +void PLIC_enable_interrupt (plic_instance_t * this_plic, plic_source source){ + + unsigned long hart_id = read_csr(mhartid); + volatile uint8_t * current_ptr = (volatile uint8_t *)(this_plic->base_addr + + PLIC_ENABLE_OFFSET + + (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) + + (source >> 3)); + uint8_t current = *current_ptr; + current = current | ( 1 << (source & 0x7)); + *current_ptr = current; + +} + +void PLIC_disable_interrupt (plic_instance_t * this_plic, plic_source source){ + + unsigned long hart_id = read_csr(mhartid); + volatile uint8_t * current_ptr = (volatile uint8_t *) (this_plic->base_addr + + PLIC_ENABLE_OFFSET + + (hart_id << PLIC_ENABLE_SHIFT_PER_TARGET) + + (source >> 3)); + uint8_t current = *current_ptr; + current = current & ~(( 1 << (source & 0x7))); + *current_ptr = current; + +} + +void PLIC_set_priority (plic_instance_t * this_plic, plic_source source, plic_priority priority){ + + if (this_plic->num_priorities > 0) { + volatile plic_priority * priority_ptr = (volatile plic_priority *) + (this_plic->base_addr + + PLIC_PRIORITY_OFFSET + + (source << PLIC_PRIORITY_SHIFT_PER_SOURCE)); + *priority_ptr = priority; + } +} + +plic_source PLIC_claim_interrupt(plic_instance_t * this_plic){ + + unsigned long hart_id = read_csr(mhartid); + + volatile plic_source * claim_addr = (volatile plic_source * ) + (this_plic->base_addr + + PLIC_CLAIM_OFFSET + + (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET)); + + return *claim_addr; + +} + +void PLIC_complete_interrupt(plic_instance_t * this_plic, plic_source source){ + + unsigned long hart_id = read_csr(mhartid); + volatile plic_source * claim_addr = (volatile plic_source *) (this_plic->base_addr + + PLIC_CLAIM_OFFSET + + (hart_id << PLIC_CLAIM_SHIFT_PER_TARGET)); + *claim_addr = source; + +} + diff --git a/code/fe310/bsp/drivers/plic_driver.h b/code/fe310/bsp/drivers/plic_driver.h new file mode 100644 index 0000000..98b2bdd --- /dev/null +++ b/code/fe310/bsp/drivers/plic_driver.h @@ -0,0 +1,51 @@ +// See LICENSE file for licence details + +#ifndef _PLIC_DRIVER_H_ +#define _PLIC_DRIVER_H_ + + +__BEGIN_DECLS + +#include "platform.h" + +typedef struct __plic_instance_t +{ + uintptr_t base_addr; + + uint32_t num_sources; + uint32_t num_priorities; + +} plic_instance_t; + +typedef uint32_t plic_source; +typedef uint32_t plic_priority; +typedef uint32_t plic_threshold; + +void PLIC_init ( + plic_instance_t * this_plic, + uintptr_t base_addr, + uint32_t num_sources, + uint32_t num_priorities + ); + +void PLIC_set_threshold (plic_instance_t * this_plic, + plic_threshold threshold); + +void PLIC_enable_interrupt (plic_instance_t * this_plic, + plic_source source); + +void PLIC_disable_interrupt (plic_instance_t * this_plic, + plic_source source); + +void PLIC_set_priority (plic_instance_t * this_plic, + plic_source source, + plic_priority priority); + +plic_source PLIC_claim_interrupt(plic_instance_t * this_plic); + +void PLIC_complete_interrupt(plic_instance_t * this_plic, + plic_source source); + +__END_DECLS + +#endif diff --git a/code/fe310/bsp/drivers/prci_driver.c b/code/fe310/bsp/drivers/prci_driver.c new file mode 100644 index 0000000..c2df2c9 --- /dev/null +++ b/code/fe310/bsp/drivers/prci_driver.c @@ -0,0 +1,265 @@ +// See LICENSE file for license details + +#include "platform.h" + +#ifdef PRCI_CTRL_ADDR +#include "prci_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; + +} + +unsigned long PRCI_get_cpu_freq() +{ + static uint32_t cpu_freq; + + if (!cpu_freq) { + // warm up I$ + PRCI_measure_mcycle_freq(3000, RTC_FREQ); + // measure for real + cpu_freq = PRCI_measure_mcycle_freq(3000, RTC_FREQ); + } + + return cpu_freq; +} + +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: + if (finaldiv < 0) { + PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV_BY_1(1) | PLL_FINAL_DIV(0)); + } else { + PRCI_REG(PRCI_PLLDIV) = (PLL_FINAL_DIV(finaldiv)); + } + + 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 diff --git a/code/fe310/bsp/drivers/prci_driver.h b/code/fe310/bsp/drivers/prci_driver.h new file mode 100644 index 0000000..14e1370 --- /dev/null +++ b/code/fe310/bsp/drivers/prci_driver.h @@ -0,0 +1,80 @@ +// See LICENSE file for license details + +#ifndef _PRCI_DRIVER_H_ +#define _PRCI_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); +unsigned long PRCI_get_cpu_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 + |