aboutsummaryrefslogtreecommitdiff
path: root/system-clock/src/clock.c
blob: ec12240c83bccc3de84d7bb0cbc04f6c0bcb3d9b (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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
 * 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_sys_clock_to_hsi()
{
  /* Turn on the HSI, and wait for it to come up. */
  RCC.c_r |= BIT(8);
  while(RCC.c_r & BIT(9));

  /* Use the HSI. */
  RCC.cfg_r |= BIT(0);
  return 0;
}

int set_sys_clock_to_pll()
{
  RCC.c_r &= ~BIT(24); /* set PLL to OFF. */

  RCC.pllcfg_r =
    BIT(24) | /* Enable PLLR. This is for system clock output. */
    /* Set PLLM to 2 and PLLN to 10. This gives us a 10/2 multiplier. */
    (10 << 8) | /* Set PLLN to 10. */
    (0 << 4)  | /* Set PLLM to 2. */
    /* Set the input to be HSI16. */
    BIT(1);


  RCC.c_r |= BIT(24); /* Turn PLL on. */
  while(!(RCC.c_r & BIT(25))); /* Wait for PLL to be ready. */

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

  /* Set the PLL as the system clock. */
  RCC.cfg_r = 0x3;
}

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;
}