Files
simtrace2/firmware/libcommon/source/tc_etu.c
Oliver Smith f721e69bc1 treewide: remove FSF address
Remove the paragraph about writing to the Free Software Foundation's
mailing address. The FSF has changed addresses in the past, and may do
so again. In 2021 this is not useful, let's rather have a bit less
boilerplate at the start of source files.

Change-Id: Ie0a3b2273383adbb3303faffd6ff96be7f4cae99
2021-12-14 11:47:21 +01:00

205 lines
5.4 KiB
C

/* SIMtrace TC (Timer / Clock) code for ETU tracking
*
* (C) 2006-2016 by Harald Welte <laforge@gnumonks.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdint.h>
#include "utils.h"
#include "tc_etu.h"
#include "chip.h"
/* pins for Channel 0 of TC-block 0, we only use TCLK + TIOB */
#define PIN_TCLK0 {PIO_PA4, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT }
#define PIN_TIOA0 {PIO_PA0, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
#define PIN_TIOB0 {PIO_PA1, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
static const Pin pins_tc0[] = { PIN_TCLK0, PIN_TIOB0 };
/* pins for Channel 2 of TC-block 0, we only use TCLK + TIOB */
#define PIN_TCLK2 {PIO_PA29, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
#define PIN_TIOA2 {PIO_PA26, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
#define PIN_TIOB2 {PIO_PA27, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT}
static const Pin pins_tc2[] = { PIN_TCLK2, PIN_TIOB2 };
struct tc_etu_state {
/* total negotiated waiting time (default = 9600) */
uint16_t waiting_time;
/* how many clock cycles per ETU (default = 372) */
uint16_t clocks_per_etu;
/* how many ETUs does waiting time correspond ? */
uint16_t wait_events;
/* how many ETUs have we seen expire so far? */
uint16_t nr_events;
/* channel number */
uint8_t chan_nr;
/* Timer/Counter register pointer */
TcChannel *chan;
/* User reference */
void *handle;
};
#define INIT_TE_STATE(n) { \
.waiting_time = 9600, \
.clocks_per_etu = 372, \
.wait_events = 10, \
.nr_events = 0, \
.chan_nr = n, \
}
static struct tc_etu_state te_state0 = INIT_TE_STATE(0);
static struct tc_etu_state te_state2 = INIT_TE_STATE(2);
static struct tc_etu_state *get_te(uint8_t chan_nr)
{
if (chan_nr == 0)
return &te_state0;
else
return &te_state2;
}
static void tc_etu_irq(struct tc_etu_state *te)
{
uint32_t sr = te->chan->TC_SR;
if (sr & TC_SR_ETRGS) {
/* external trigger, i.e. we have seen a bit on I/O */
te->nr_events = 0;
}
if (sr & TC_SR_CPCS) {
/* Compare C event has occurred, i.e. 1 ETU expired */
te->nr_events++;
if (te->nr_events == te->wait_events/2) {
/* Indicate that half the waiting tim has expired */
tc_etu_wtime_half_expired(te->handle);
}
if (te->nr_events >= te->wait_events) {
TcChannel *chan = te->chan;
chan->TC_CMR |= TC_CMR_ENETRG;
/* disable and re-enable clock to make it stop */
chan->TC_CCR = TC_CCR_CLKDIS;
chan->TC_CCR = TC_CCR_CLKEN;
/* Indicate that the waiting tim has expired */
tc_etu_wtime_expired(te->handle);
}
}
}
void TC0_IrqHandler(void)
{
tc_etu_irq(&te_state0);
}
void TC2_IrqHandler(void)
{
tc_etu_irq(&te_state2);
}
static void recalc_nr_events(struct tc_etu_state *te)
{
te->wait_events = te->waiting_time / 12;
te->chan->TC_RC = te->clocks_per_etu * 12;
}
void tc_etu_set_wtime(uint8_t chan_nr, uint16_t wtime)
{
struct tc_etu_state *te = get_te(chan_nr);
te->waiting_time = wtime;
recalc_nr_events(te);
}
void tc_etu_set_etu(uint8_t chan_nr, uint16_t etu)
{
struct tc_etu_state *te = get_te(chan_nr);
te->clocks_per_etu = etu;
recalc_nr_events(te);
}
void tc_etu_enable(uint8_t chan_nr)
{
struct tc_etu_state *te = get_te(chan_nr);
te->nr_events = 0;
te->chan->TC_CCR = TC_CCR_CLKEN|TC_CCR_SWTRG;
}
void tc_etu_disable(uint8_t chan_nr)
{
struct tc_etu_state *te = get_te(chan_nr);
te->nr_events = 0;
te->chan->TC_CCR = TC_CCR_CLKDIS;
}
void tc_etu_init(uint8_t chan_nr, void *handle)
{
struct tc_etu_state *te = get_te(chan_nr);
uint32_t tc_clks;
te->handle = handle;
switch (chan_nr) {
case 0:
/* Configure PA4(TCLK0), PA1(TIB0) */
PIO_Configure(pins_tc0, ARRAY_SIZE(pins_tc0));
PMC_EnablePeripheral(ID_TC0);
/* route TCLK0 to XC2 */
TC0->TC_BMR &= ~TC_BMR_TC0XC0S_Msk;
TC0->TC_BMR |= TC_BMR_TC0XC0S_TCLK0;
tc_clks = TC_CMR_TCCLKS_XC0;
/* register interrupt handler */
NVIC_EnableIRQ(TC0_IRQn);
te->chan = &TC0->TC_CHANNEL[0];
break;
case 2:
/* Configure PA29(TCLK2), PA27(TIOB2) */
PIO_Configure(pins_tc2, ARRAY_SIZE(pins_tc2));
PMC_EnablePeripheral(ID_TC2);
/* route TCLK2 to XC2. TC0 really means TCA in this case */
TC0->TC_BMR &= ~TC_BMR_TC2XC2S_Msk;
TC0->TC_BMR |= TC_BMR_TC2XC2S_TCLK2;
tc_clks = TC_CMR_TCCLKS_XC2;
/* register interrupt handler */
NVIC_EnableIRQ(TC2_IRQn);
te->chan = &TC0->TC_CHANNEL[2];
break;
default:
return;
}
/* enable interrupts for Compare-C and external trigger */
te->chan->TC_IER = TC_IER_CPCS | TC_IER_ETRGS;
te->chan->TC_CMR = tc_clks | /* XC(TCLK) clock */
TC_CMR_WAVE | /* wave mode */
TC_CMR_ETRGEDG_FALLING | /* ext trig on falling edge */
TC_CMR_EEVT_TIOB | /* ext trig is TIOB0 */
TC_CMR_ENETRG | /* enable ext trig */
TC_CMR_WAVSEL_UP_RC | /* wave mode up */
TC_CMR_ACPA_SET | /* set TIOA on a compare */
TC_CMR_ACPC_CLEAR | /* clear TIOA on C compare */
TC_CMR_ASWTRG_CLEAR; /* Clear TIOA on sw trig */
tc_etu_set_etu(chan_nr, 372);
/* start with a disabled clock */
tc_etu_disable(chan_nr);
/* Reset to start timers */
TC0->TC_BCR = TC_BCR_SYNC;
}