Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 1 | /* SIMtrace TC (Timer / Clock) code for ETU tracking |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 2 | * |
Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 3 | * (C) 2006-2016 by Harald Welte <laforge@gnumonks.org> |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 4 | * |
Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License as published by |
| 7 | * the Free Software Foundation; either version 2 of the License, or |
| 8 | * (at your option) any later version. |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 9 | * |
Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 14 | * |
Kévin Redon | 9a12d68 | 2018-07-08 13:21:16 +0200 | [diff] [blame] | 15 | * You should have received a copy of the GNU General Public License |
| 16 | * along with this program; if not, write to the Free Software |
| 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 18 | */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 19 | #include <stdint.h> |
| 20 | |
| 21 | #include "utils.h" |
| 22 | #include "tc_etu.h" |
| 23 | |
| 24 | #include "chip.h" |
| 25 | |
Harald Welte | 2bff7cd | 2017-03-05 22:14:04 +0100 | [diff] [blame] | 26 | /* pins for Channel 0 of TC-block 0, we only use TCLK + TIOB */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 27 | #define PIN_TCLK0 {PIO_PA4, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT } |
| 28 | #define PIN_TIOA0 {PIO_PA0, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} |
| 29 | #define PIN_TIOB0 {PIO_PA1, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} |
Harald Welte | 2bff7cd | 2017-03-05 22:14:04 +0100 | [diff] [blame] | 30 | static const Pin pins_tc0[] = { PIN_TCLK0, PIN_TIOB0 }; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 31 | |
Harald Welte | 2bff7cd | 2017-03-05 22:14:04 +0100 | [diff] [blame] | 32 | /* pins for Channel 2 of TC-block 0, we only use TCLK + TIOB */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 33 | #define PIN_TCLK2 {PIO_PA29, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} |
| 34 | #define PIN_TIOA2 {PIO_PA26, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} |
| 35 | #define PIN_TIOB2 {PIO_PA27, PIOA, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT} |
Harald Welte | 2bff7cd | 2017-03-05 22:14:04 +0100 | [diff] [blame] | 36 | static const Pin pins_tc2[] = { PIN_TCLK2, PIN_TIOB2 }; |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 37 | |
| 38 | struct tc_etu_state { |
| 39 | /* total negotiated waiting time (default = 9600) */ |
| 40 | uint16_t waiting_time; |
| 41 | /* how many clock cycles per ETU (default = 372) */ |
| 42 | uint16_t clocks_per_etu; |
| 43 | /* how many ETUs does waiting time correspond ? */ |
| 44 | uint16_t wait_events; |
| 45 | /* how many ETUs have we seen expire so far? */ |
| 46 | uint16_t nr_events; |
| 47 | /* channel number */ |
| 48 | uint8_t chan_nr; |
| 49 | /* Timer/Counter register pointer */ |
| 50 | TcChannel *chan; |
| 51 | /* User reference */ |
| 52 | void *handle; |
| 53 | }; |
| 54 | |
| 55 | #define INIT_TE_STATE(n) { \ |
| 56 | .waiting_time = 9600, \ |
| 57 | .clocks_per_etu = 372, \ |
| 58 | .wait_events = 10, \ |
| 59 | .nr_events = 0, \ |
| 60 | .chan_nr = n, \ |
| 61 | } |
| 62 | |
| 63 | static struct tc_etu_state te_state0 = INIT_TE_STATE(0); |
| 64 | static struct tc_etu_state te_state2 = INIT_TE_STATE(2); |
| 65 | |
| 66 | static struct tc_etu_state *get_te(uint8_t chan_nr) |
| 67 | { |
| 68 | if (chan_nr == 0) |
| 69 | return &te_state0; |
| 70 | else |
| 71 | return &te_state2; |
| 72 | } |
| 73 | |
| 74 | static void tc_etu_irq(struct tc_etu_state *te) |
| 75 | { |
| 76 | uint32_t sr = te->chan->TC_SR; |
| 77 | |
| 78 | if (sr & TC_SR_ETRGS) { |
| 79 | /* external trigger, i.e. we have seen a bit on I/O */ |
| 80 | te->nr_events = 0; |
| 81 | } |
| 82 | |
| 83 | if (sr & TC_SR_CPCS) { |
| 84 | /* Compare C event has occurred, i.e. 1 ETU expired */ |
| 85 | te->nr_events++; |
| 86 | if (te->nr_events == te->wait_events/2) { |
| 87 | /* Indicate that half the waiting tim has expired */ |
| 88 | tc_etu_wtime_half_expired(te->handle); |
| 89 | } |
| 90 | if (te->nr_events >= te->wait_events) { |
| 91 | TcChannel *chan = te->chan; |
| 92 | chan->TC_CMR |= TC_CMR_ENETRG; |
| 93 | |
| 94 | /* disable and re-enable clock to make it stop */ |
| 95 | chan->TC_CCR = TC_CCR_CLKDIS; |
| 96 | chan->TC_CCR = TC_CCR_CLKEN; |
| 97 | |
| 98 | /* Indicate that the waiting tim has expired */ |
| 99 | tc_etu_wtime_expired(te->handle); |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | void TC0_IrqHandler(void) |
| 105 | { |
| 106 | tc_etu_irq(&te_state0); |
| 107 | } |
| 108 | |
| 109 | void TC2_IrqHandler(void) |
| 110 | { |
| 111 | tc_etu_irq(&te_state2); |
| 112 | } |
| 113 | |
| 114 | static void recalc_nr_events(struct tc_etu_state *te) |
| 115 | { |
| 116 | te->wait_events = te->waiting_time / 12; |
| 117 | te->chan->TC_RC = te->clocks_per_etu * 12; |
| 118 | } |
| 119 | |
| 120 | void tc_etu_set_wtime(uint8_t chan_nr, uint16_t wtime) |
| 121 | { |
| 122 | struct tc_etu_state *te = get_te(chan_nr); |
| 123 | te->waiting_time = wtime; |
| 124 | recalc_nr_events(te); |
| 125 | } |
| 126 | |
| 127 | void tc_etu_set_etu(uint8_t chan_nr, uint16_t etu) |
| 128 | { |
| 129 | struct tc_etu_state *te = get_te(chan_nr); |
| 130 | te->clocks_per_etu = etu; |
| 131 | recalc_nr_events(te); |
| 132 | } |
| 133 | |
Harald Welte | 042f0d3 | 2016-02-24 21:01:50 +0100 | [diff] [blame] | 134 | void tc_etu_enable(uint8_t chan_nr) |
| 135 | { |
| 136 | struct tc_etu_state *te = get_te(chan_nr); |
| 137 | |
| 138 | te->nr_events = 0; |
| 139 | te->chan->TC_CCR = TC_CCR_CLKEN|TC_CCR_SWTRG; |
| 140 | } |
| 141 | |
| 142 | void tc_etu_disable(uint8_t chan_nr) |
| 143 | { |
| 144 | struct tc_etu_state *te = get_te(chan_nr); |
| 145 | |
| 146 | te->nr_events = 0; |
| 147 | te->chan->TC_CCR = TC_CCR_CLKDIS; |
| 148 | } |
| 149 | |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 150 | void tc_etu_init(uint8_t chan_nr, void *handle) |
| 151 | { |
| 152 | struct tc_etu_state *te = get_te(chan_nr); |
| 153 | uint32_t tc_clks; |
| 154 | |
| 155 | te->handle = handle; |
| 156 | |
| 157 | switch (chan_nr) { |
| 158 | case 0: |
Harald Welte | 2bff7cd | 2017-03-05 22:14:04 +0100 | [diff] [blame] | 159 | /* Configure PA4(TCLK0), PA1(TIB0) */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 160 | PIO_Configure(pins_tc0, ARRAY_SIZE(pins_tc0)); |
| 161 | PMC_EnablePeripheral(ID_TC0); |
| 162 | /* route TCLK0 to XC2 */ |
| 163 | TC0->TC_BMR &= ~TC_BMR_TC0XC0S_Msk; |
| 164 | TC0->TC_BMR |= TC_BMR_TC0XC0S_TCLK0; |
| 165 | tc_clks = TC_CMR_TCCLKS_XC0; |
| 166 | /* register interrupt handler */ |
| 167 | NVIC_EnableIRQ(TC0_IRQn); |
| 168 | |
| 169 | te->chan = &TC0->TC_CHANNEL[0]; |
| 170 | break; |
| 171 | case 2: |
Harald Welte | 2bff7cd | 2017-03-05 22:14:04 +0100 | [diff] [blame] | 172 | /* Configure PA29(TCLK2), PA27(TIOB2) */ |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 173 | PIO_Configure(pins_tc2, ARRAY_SIZE(pins_tc2)); |
| 174 | PMC_EnablePeripheral(ID_TC2); |
| 175 | /* route TCLK2 to XC2. TC0 really means TCA in this case */ |
| 176 | TC0->TC_BMR &= ~TC_BMR_TC2XC2S_Msk; |
| 177 | TC0->TC_BMR |= TC_BMR_TC2XC2S_TCLK2; |
| 178 | tc_clks = TC_CMR_TCCLKS_XC2; |
| 179 | /* register interrupt handler */ |
| 180 | NVIC_EnableIRQ(TC2_IRQn); |
| 181 | |
| 182 | te->chan = &TC0->TC_CHANNEL[2]; |
| 183 | break; |
| 184 | default: |
| 185 | return; |
| 186 | } |
| 187 | |
| 188 | /* enable interrupts for Compare-C and external trigger */ |
| 189 | te->chan->TC_IER = TC_IER_CPCS | TC_IER_ETRGS; |
| 190 | |
| 191 | te->chan->TC_CMR = tc_clks | /* XC(TCLK) clock */ |
| 192 | TC_CMR_WAVE | /* wave mode */ |
| 193 | TC_CMR_ETRGEDG_FALLING | /* ext trig on falling edge */ |
| 194 | TC_CMR_EEVT_TIOB | /* ext trig is TIOB0 */ |
| 195 | TC_CMR_ENETRG | /* enable ext trig */ |
| 196 | TC_CMR_WAVSEL_UP_RC | /* wave mode up */ |
| 197 | TC_CMR_ACPA_SET | /* set TIOA on a compare */ |
| 198 | TC_CMR_ACPC_CLEAR | /* clear TIOA on C compare */ |
| 199 | TC_CMR_ASWTRG_CLEAR; /* Clear TIOA on sw trig */ |
| 200 | |
| 201 | tc_etu_set_etu(chan_nr, 372); |
| 202 | |
Harald Welte | 042f0d3 | 2016-02-24 21:01:50 +0100 | [diff] [blame] | 203 | /* start with a disabled clock */ |
| 204 | tc_etu_disable(chan_nr); |
Harald Welte | 9d3e382 | 2015-11-09 00:50:54 +0100 | [diff] [blame] | 205 | |
| 206 | /* Reset to start timers */ |
| 207 | TC0->TC_BCR = TC_BCR_SYNC; |
| 208 | } |