| /* SIMtrace TC (Timer / Clock) code for ETU tracking */ |
| |
| /* (C) 2006-2016 by Harald Welte <hwelte@hmw-consulting.de> |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| */ |
| |
| #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; |
| } |