From 31578e285a21a749a49e3ac146feb8b02fcc7b52 Mon Sep 17 00:00:00 2001
From: Uros Majstorovic <majstor@majstor.org>
Date: Wed, 4 Dec 2019 06:11:35 +0100
Subject: added new metal sdk

---
 code/fe310/drivers/Makefile      |  10 ++
 code/fe310/drivers/plic_driver.c | 127 +++++++++++++++++++
 code/fe310/drivers/plic_driver.h |  51 ++++++++
 code/fe310/drivers/prci_driver.c | 265 +++++++++++++++++++++++++++++++++++++++
 code/fe310/drivers/prci_driver.h |  80 ++++++++++++
 5 files changed, 533 insertions(+)
 create mode 100644 code/fe310/drivers/Makefile
 create mode 100644 code/fe310/drivers/plic_driver.c
 create mode 100644 code/fe310/drivers/plic_driver.h
 create mode 100644 code/fe310/drivers/prci_driver.c
 create mode 100644 code/fe310/drivers/prci_driver.h

(limited to 'code/fe310/drivers')

diff --git a/code/fe310/drivers/Makefile b/code/fe310/drivers/Makefile
new file mode 100644
index 0000000..fc5366f
--- /dev/null
+++ b/code/fe310/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/drivers/plic_driver.c b/code/fe310/drivers/plic_driver.c
new file mode 100644
index 0000000..27b9d2c
--- /dev/null
+++ b/code/fe310/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/drivers/plic_driver.h b/code/fe310/drivers/plic_driver.h
new file mode 100644
index 0000000..98b2bdd
--- /dev/null
+++ b/code/fe310/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/drivers/prci_driver.c b/code/fe310/drivers/prci_driver.c
new file mode 100644
index 0000000..c2df2c9
--- /dev/null
+++ b/code/fe310/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/drivers/prci_driver.h b/code/fe310/drivers/prci_driver.h
new file mode 100644
index 0000000..14e1370
--- /dev/null
+++ b/code/fe310/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
+
-- 
cgit v1.2.3