2 * Copyright © 2019 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
22 volatile AO_TICK_TYPE ao_tick_count;
33 AO_TICK_TYPE before, after;
37 before = ao_tick_count;
38 cvr = samd21_systick.cvr;
39 after = ao_tick_count;
40 } while (before != after);
42 return (uint64_t) after * (1000000000ULL / AO_HERTZ) +
43 (uint64_t) cvr * (1000000000ULL / AO_SYSTICK);
47 volatile uint8_t ao_data_interval = 1;
48 volatile uint8_t ao_data_count;
51 void samd21_systick_isr(void)
53 ao_arch_release_interrupts();
54 if (samd21_systick.csr & (1 << SAMD21_SYSTICK_CSR_COUNTFLAG)) {
56 ao_task_check_alarm();
58 if (++ao_data_count == ao_data_interval && ao_data_interval) {
61 if (ao_fake_flight_active)
62 ao_fake_flight_poll();
66 #if (AO_DATA_ALL & ~(AO_DATA_ADC))
67 ao_wakeup((void *) &ao_data_count);
79 ao_timer_set_adc_interval(uint8_t interval)
82 ao_data_interval = interval;
88 #define SYSTICK_RELOAD (AO_SYSTICK / AO_HERTZ - 1)
93 samd21_systick.csr = 0;
94 samd21_systick.rvr = SYSTICK_RELOAD;
95 samd21_systick.cvr = 0;
96 samd21_systick.csr = ((1 << SAMD21_SYSTICK_CSR_ENABLE) |
97 (1 << SAMD21_SYSTICK_CSR_TICKINT) |
98 (SAMD21_SYSTICK_CSR_CLKSOURCE_HCLK_8 << SAMD21_SYSTICK_CSR_CLKSOURCE));
99 /* Set clock to lowest priority */
100 samd21_scb.shpr3 |= 3UL << 30;
109 /* Set flash wait state to tolerate 48MHz */
110 samd21_nvmctrl.ctrlb |= (1 << SAMD21_NVMCTRL_CTRLB_RWS);
112 samd21_pm.apbamask |= ((1 << SAMD21_PM_APBAMASK_GCLK) |
113 (1 << SAMD21_PM_APBAMASK_SYSCTRL));
116 samd21_gclk.ctrl = (1 << SAMD21_GCLK_CTRL_SWRST)
119 /* Wait for reset to complete */
120 while ((samd21_gclk.ctrl & (1 << SAMD21_GCLK_CTRL_SWRST)) &&
121 (samd21_gclk.status & (1 << SAMD21_GCLK_STATUS_SYNCBUSY)))
125 ao_enable_output(&samd21_port_b, 10, 1);
126 /* Enable xosc (external xtal oscillator) */
127 samd21_sysctrl.xosc = ((SAMD21_SYSCTRL_XOSC_STARTUP_16384 << SAMD21_SYSCTRL_XOSC_STARTUP) |
128 (1 << SAMD21_SYSCTRL_XOSC_AMPGC) |
129 (SAMD21_SYSCTRL_XOSC_GAIN_16MHz << SAMD21_SYSCTRL_XOSC_GAIN) |
130 (0 << SAMD21_SYSCTRL_XOSC_ONDEMAND) |
131 (1 << SAMD21_SYSCTRL_XOSC_RUNSTDBY) |
132 (1 << SAMD21_SYSCTRL_XOSC_XTALEN));
133 samd21_sysctrl.xosc |= ((1 << SAMD21_SYSCTRL_XOSC_ENABLE));
136 while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_XOSCRDY)) == 0)
139 ao_enable_output(&samd21_port_b, 11, 1);
144 samd21_sysctrl.dpllctrlb = (((AO_XOSC_DIV/2 - 1) << SAMD21_SYSCTRL_DPLLCTRLB_DIV) |
145 (0 << SAMD21_SYSCTRL_DPLLCTRLB_LBYPASS) |
146 (SAMD21_SYSCTRL_DPLLCTRLB_LTIME_DEFAULT << SAMD21_SYSCTRL_DPLLCTRLB_LTIME) |
147 (SAMD21_SYSCTRL_DPLLCTRLB_REFCLK_XOSC << SAMD21_SYSCTRL_DPLLCTRLB_REFCLK) |
148 (0 << SAMD21_SYSCTRL_DPLLCTRLB_WUF) |
149 (1 << SAMD21_SYSCTRL_DPLLCTRLB_LPEN) |
150 (SAMD21_SYSCTRL_DPLLCTRLB_FILTER_DEFAULT << SAMD21_SYSCTRL_DPLLCTRLB_FILTER));
153 samd21_sysctrl.dpllratio = ((AO_XOSC_MUL - 1) << SAMD21_SYSCTRL_DPLLRATIO_LDR);
155 /* Always on in run mode, off in standby mode */
156 samd21_sysctrl.dpllctrla = ((0 << SAMD21_SYSCTRL_DPLLCTRLA_ONDEMAND) |
157 (0 << SAMD21_SYSCTRL_DPLLCTRLA_RUNSTDBY));
160 samd21_sysctrl.dpllctrla |= (1 << SAMD21_SYSCTRL_DPLLCTRLA_ENABLE);
162 /* Wait for the DPLL to be enabled */
163 while ((samd21_sysctrl.dpllstatus & (1 << SAMD21_SYSCTRL_DPLLSTATUS_ENABLE)) == 0)
166 /* Wait for the DPLL to be ready */
167 while ((samd21_sysctrl.dpllstatus & (1 << SAMD21_SYSCTRL_DPLLSTATUS_CLKRDY)) == 0)
170 samd21_gclk_wait_sync();
173 * Switch generator 0 (CPU clock) to DPLL
177 samd21_gclk_gendiv(AO_GCLK_SYSCLK, AO_XOSC_GCLK_DIV);
179 /* select DPLL as source */
180 samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_FDPLL96M, AO_GCLK_FDPLL96M);
186 * Enable DFLL48M clock
189 samd21_sysctrl.dfllctrl = (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE);
190 samd21_dfll_wait_sync();
193 #define AO_GCLK_XOSC32K 1
195 /* Enable xosc32k (external 32.768kHz oscillator) */
196 samd21_sysctrl.xosc32k = ((6 << SAMD21_SYSCTRL_XOSC32K_STARTUP) |
197 (1 << SAMD21_SYSCTRL_XOSC32K_XTALEN) |
198 (1 << SAMD21_SYSCTRL_XOSC32K_EN32K));
200 /* requires separate store */
201 samd21_sysctrl.xosc32k |= (1 << SAMD21_SYSCTRL_XOSC32K_ENABLE);
204 while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_XOSC32KRDY)) == 0)
208 * Use xosc32k as source of gclk generator AO_GCLK_XOSC32K
211 samd21_gclk_gendiv(AO_GCLK_XOSC32K, 1);
212 samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_XOSC32K, AO_GCLK_XOSC32K);
215 * Use generator as source for dfm48m reference
218 samd21_gclk_clkctrl(AO_GCLK_XOSC32K, SAMD21_GCLK_CLKCTRL_ID_DFLL48M_REF);
220 /* Set multiplier to get as close to 48MHz as we can without going over */
221 samd21_sysctrl.dfllmul = (((31/4) << SAMD21_SYSCTRL_DFLLMUL_CSTEP) |
222 ((255/4) << SAMD21_SYSCTRL_DFLLMUL_FSTEP) |
223 ((AO_DFLL48M / AO_XOSC32K) << SAMD21_SYSCTRL_DFLLMUL_MUL));
225 /* pull out coarse calibration value from rom */
226 uint32_t coarse = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL) &
227 SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK);
229 samd21_sysctrl.dfllval = ((coarse << SAMD21_SYSCTRL_DFLLVAL_COARSE) |
230 (512 << SAMD21_SYSCTRL_DFLLVAL_FINE));
232 samd21_sysctrl.dfllctrl = 0;
233 samd21_dfll_wait_sync();
235 samd21_sysctrl.dfllctrl = ((1 << SAMD21_SYSCTRL_DFLLCTRL_MODE) |
236 (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE));
238 samd21_dfll_wait_sync();
239 samd21_gclk_wait_sync();
241 /* wait for fine lock */
242 while ((samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLLCKC)) == 0 ||
243 (samd21_sysctrl.pclksr & (1 << SAMD21_SYSCTRL_PCLKSR_DFLLLCKF)) == 0)
246 samd21_sysctrl.dfllmul = (((31/4) << SAMD21_SYSCTRL_DFLLMUL_CSTEP) |
247 ((255/4) << SAMD21_SYSCTRL_DFLLMUL_FSTEP) |
248 ((AO_DFLL48M / 1000) << SAMD21_SYSCTRL_DFLLMUL_MUL));
250 /* pull out coarse calibration value from rom */
251 uint32_t coarse = ((samd21_aux1.calibration >> SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL) &
252 SAMD21_AUX1_CALIBRATION_DFLL48M_COARSE_CAL_MASK);
254 samd21_sysctrl.dfllval = ((coarse << SAMD21_SYSCTRL_DFLLVAL_COARSE) |
255 (512 << SAMD21_SYSCTRL_DFLLVAL_FINE));
257 samd21_sysctrl.dfllctrl = 0;
258 samd21_dfll_wait_sync();
260 samd21_sysctrl.dfllctrl = ((1 << SAMD21_SYSCTRL_DFLLCTRL_MODE) |
261 (1 << SAMD21_SYSCTRL_DFLLCTRL_BPLCKC) |
262 (1 << SAMD21_SYSCTRL_DFLLCTRL_CCDIS) |
263 (1 << SAMD21_SYSCTRL_DFLLCTRL_USBCRM) |
264 (1 << SAMD21_SYSCTRL_DFLLCTRL_ENABLE));
266 samd21_dfll_wait_sync();
267 samd21_gclk_wait_sync();
271 * Switch generator to DFLL48M
275 samd21_gclk_gendiv(AO_GCLK_SYSCLK, 1);
277 /* select DFLL48M as source */
278 samd21_gclk_genctrl(SAMD21_GCLK_GENCTRL_SRC_DFLL48M, AO_GCLK_DFLL48M);
281 /* Set up all of the clocks to be /1 */
283 samd21_pm.cpusel = ((0 << SAMD21_PM_CPUSEL_CPUDIV));
284 samd21_pm.apbasel = ((0 << SAMD21_PM_APBASEL_APBADIV));
285 samd21_pm.apbbsel = ((0 << SAMD21_PM_APBBSEL_APBBDIV));
286 samd21_pm.apbcsel = ((0 << SAMD21_PM_APBCSEL_APBCDIV));
289 samd21_sysctrl.osc8m &= ~(1UL << SAMD21_SYSCTRL_OSC8M_ENABLE);
291 /* Additional misc configuration stuff */
293 /* Disable automatic NVM write operations */
294 samd21_nvmctrl.ctrlb |= (1UL << SAMD21_NVMCTRL_CTRLB_MANW);
296 ao_gpio_set(&samd21_port_b, 10, 0);