aboutsummaryrefslogtreecommitdiff
path: root/system-clock/src/clock.c
blob: dd376844f8e9a39b3dfa09be1ddea27d859a6911 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/*
 * This file sets the system clock to its full glory of 80Mhz
 */

#include "clock.h"
#include "gpio.h"
#include "spin.h"
#include "flash.h"
#include <stdint.h>

#define TIMEOUT 10000

int pll_off()
{
  uint32_t c;

  RCC.c_r &= ~BIT(24); /* Turn off pll. */
  for(c = 0; c < TIMEOUT && RCC.c_r & BIT(25); ++ c); /* Wait for OFF. */
  
  if (c == TIMEOUT) {
    return E_TIMEOUT;
  }

  return 0;
}

int pll_on()
{
  uint32_t c;

  RCC.c_r |= BIT(24); /* Turn on PLL. */
  for(c = 0; c < TIMEOUT && !(RCC.c_r & BIT(25)); ++ c); /* Wait for RDY. */

  if (c == TIMEOUT) {
    return E_TIMEOUT;
  }

  return 0;
}

int configure_pll(
    uint8_t pllp_div_factor,
    pll_divisor_t  pllr,  /* System clock divisor. */
    pll_divisor_t  pllq,  /* Divison factor for PLL48M1CLK. */
    pllp_divisor_t pllp,  /* Divison factor for PLLSAI2CLK. */
    uint8_t        plln,  /* PLL numerator. */
    pllm_divisor_t pllm,  /* PLL denominator. */
    pll_src_t      pllsrc /* PLL source */ )
{
  if (RCC.c_r & BIT(25)) {
    /* PLL must be off to configure it. */
    return E_NOT_OFF;
  }

  /* Make sure inputs are valid. */
  if (pllp_div_factor == 1 || pllp_div_factor > 31) {
    return E_BADPLLP_DIV;
  }
  if (plln < 8 || plln > 86) {
    return E_BADPLLN;
  }

  RCC.pllcfg_r =
    (pllp_div_factor << 27) |
    (pllr << 24) |
    (pllq << 20) |
    (pllp << 16) |
    (plln << 8)  |
    (pllm << 4)  |
    (pllsrc << 0);

  return 0;
}
  
int set_system_clock_MHz(uint8_t mhz)
{
  /* Set the source of the system colck to MSI temporarily. */
  set_system_clock_src(SYSTEM_CLOCK_SRC_MSI);

  if (mhz <= 8 || mhz > 80) {
    return E_BAD_ARG;
  }

  pll_off();

  configure_pll(
    0 /* pllp_div_factor */,
    PLL_DIVISOR_4  /* pllr: VCO / 4 = mhz MHz. */,
    PLL_DIVISOR_4  /* pllq: VCO / 4 = mhz MHz */,
    PLLP_DIVISOR_7 /* pllp */,

    /* The following set the frequency of VCO to (mhz*4)MHz: mhz * 1 * 4MHz. */
    mhz            /* plln    | mhz */,
    PLLM_DIVISOR_1 /* pllm    | 01 */,
    PLL_SRC_MSI    /* pll src | 04 Mhz */);

  pll_on();

  /* Configure the flash to have 4 wait states. This is required at
   * 80 MHz. */
  FLASH.ac_r &= ~0x07;
  FLASH.ac_r |= 0x04;

  /* Set the source of the system colck to PLL. */
  set_system_clock_src(SYSTEM_CLOCK_SRC_PLL);
  return 0;
}

int set_system_clock_src(system_clock_src_t src)
{
  uint8_t value = RCC.cfg_r & ~0x03;
  RCC.cfg_r = value | src;
}