treewide: remove FSF address
[osmocom-bb.git] / src / target / firmware / calypso / tpu.c
blobd84136c999cd06401750f56b0891cb557e907a26
1 /* Calypso DBB internal TPU (Time Processing Unit) Driver */
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
5 * All Rights Reserved
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
19 #include <stdint.h>
20 #include <stdio.h>
22 #include <debug.h>
23 #include <delay.h>
24 #include <memory.h>
25 #include <calypso/tpu.h>
26 #include <calypso/tsp.h>
28 /* Using TPU_DEBUG you will send special HLDC messages to the host PC
29 * containing the full TPU RAM content at the time you call tpu_enable() */
30 //#define TPU_DEBUG
32 #define BASE_ADDR_TPU 0xffff1000
33 #define TPU_REG(x) (BASE_ADDR_TPU+(x))
35 #define BASE_ADDR_TPU_RAM 0xffff9000
36 #define TPU_RAM_END 0xffff97ff
38 enum tpu_reg_arm {
39 TPU_CTRL = 0x0, /* Control & Status Register */
40 INT_CTRL = 0x2, /* Interrupt Control Register */
41 INT_STAT = 0x4, /* Interrupt Status Register */
42 TPU_OFFSET = 0xC, /* Offset operand value register */
43 TPU_SYNCHRO = 0xE, /* synchro operand value register */
44 IT_DSP_PG = 0x20,
47 enum tpu_ctrl_bits {
48 TPU_CTRL_RESET = (1 << 0),
49 TPU_CTRL_PAGE = (1 << 1),
50 TPU_CTRL_EN = (1 << 2),
51 /* unused */
52 TPU_CTRL_DSP_EN = (1 << 4),
53 /* unused */
54 TPU_CTRL_MCU_RAM_ACC = (1 << 6),
55 TPU_CTRL_TSP_RESET = (1 << 7),
56 TPU_CTRL_IDLE = (1 << 8),
57 TPU_CTRL_WAIT = (1 << 9),
58 TPU_CTRL_CK_ENABLE = (1 << 10),
59 TPU_CTRL_FULL_WRITE = (1 << 11),
62 enum tpu_int_ctrl_bits {
63 ICTRL_MCU_FRAME = (1 << 0),
64 ICTRL_MCU_PAGE = (1 << 1),
65 ICTRL_DSP_FRAME = (1 << 2),
66 ICTRL_DSP_FRAME_FORCE = (1 << 3),
69 static uint16_t *tpu_ptr = (uint16_t *)BASE_ADDR_TPU_RAM;
71 #ifdef TPU_DEBUG
72 #include <comm/sercomm.h>
73 #include <layer1/sync.h>
74 static struct msgb *tpu_debug_msg = NULL;
76 static void tpu_debug_alloc(void)
78 tpu_debug_msg = sercomm_alloc_msgb(sizeof(uint32_t) + 64*2);
79 if (!tpu_debug_msg)
80 printf("UNABLE TO ALLOC TPU DBG\n");
82 static void tpu_debug_flush(void)
84 if (tpu_debug_msg) {
85 sercomm_sendmsg(SC_DLCI_DEBUG, tpu_debug_msg);
86 tpu_debug_msg = NULL;
88 tpu_debug_alloc();
90 static void tpu_debug_enqueue(uint16_t instr)
92 uint16_t *u16_out;
94 if (!tpu_debug_msg)
95 return;
96 if (tpu_ptr == (uint16_t *) BASE_ADDR_TPU_RAM) {
97 /* prepend tpu memory dump with frame number */
98 uint32_t *fn = (uint32_t *) msgb_put(tpu_debug_msg, sizeof(fn));
99 *fn = l1s.current_time.fn;
101 if (msgb_tailroom(tpu_debug_msg) >= sizeof(instr)) {
102 /* cannot use msgb_put_u16 as host program expects little endian */
103 u16_out = (uint16_t *) msgb_put(tpu_debug_msg, sizeof(instr));
104 *u16_out = instr;
107 #else
108 static void tpu_debug_alloc(void) { }
109 static void tpu_debug_flush(void) { }
110 static void tpu_debug_enqueue(uint16_t instr) { }
111 #endif
113 #define BIT_SET 1
114 #define BIT_CLEAR 0
116 /* wait for a certain control bit to be set */
117 static int tpu_wait_ctrl_bit(uint16_t bit, int set)
119 int timeout = 10*1000;
121 while (1) {
122 uint16_t reg = readw(TPU_REG(TPU_CTRL));
123 if (set) {
124 if (reg & bit)
125 break;
126 } else {
127 if (!(reg & bit))
128 break;
130 timeout--;
131 if (timeout <= 0) {
132 puts("Timeout while waiting for TPU ctrl bit!\n");
133 return -1;
137 return 0;
140 /* assert or de-assert TPU reset */
141 void tpu_reset(int active)
143 uint16_t reg;
145 printd("tpu_reset(%u)\n", active);
146 reg = readw(TPU_REG(TPU_CTRL));
147 if (active) {
148 reg |= (TPU_CTRL_RESET|TPU_CTRL_TSP_RESET);
149 writew(reg, TPU_REG(TPU_CTRL));
150 tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_SET);
151 } else {
152 reg &= ~(TPU_CTRL_RESET|TPU_CTRL_TSP_RESET);
153 writew(reg, TPU_REG(TPU_CTRL));
154 tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_CLEAR);
158 /* Enable or Disable a new scenario loaded into the TPU */
159 void tpu_enable(int active)
161 uint16_t reg = readw(TPU_REG(TPU_CTRL));
163 printd("tpu_enable(%u)\n", active);
165 if (active)
166 reg |= TPU_CTRL_EN;
167 else
168 reg &= ~TPU_CTRL_EN;
169 writew(reg, TPU_REG(TPU_CTRL));
171 tpu_debug_flush();
173 /* After the new scenario is loaded, TPU switches the MCU-visible memory
174 * page, i.e. we can write without any danger */
175 tpu_rewind();
176 #if 0
178 int i;
179 uint16_t oldreg = 0;
181 for (i = 0; i < 100000; i++) {
182 reg = readw(TPU_REG(TPU_CTRL));
183 if (i == 0 || oldreg != reg) {
184 printd("%d TPU state: 0x%04x\n", i, reg);
186 oldreg = reg;
189 #endif
192 /* Enable or Disable the clock of the TPU Module */
193 void tpu_clk_enable(int active)
195 uint16_t reg = readw(TPU_REG(TPU_CTRL));
197 printd("tpu_clk_enable(%u)\n", active);
198 if (active) {
199 reg |= TPU_CTRL_CK_ENABLE;
200 writew(reg, TPU_REG(TPU_CTRL));
201 tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_SET);
202 } else {
203 reg &= ~TPU_CTRL_CK_ENABLE;
204 writew(reg, TPU_REG(TPU_CTRL));
205 tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_CLEAR);
209 /* Enable Frame Interrupt generation on next frame. DSP will reset it */
210 void tpu_dsp_frameirq_enable(void)
212 uint16_t reg = readw(TPU_REG(TPU_CTRL));
213 reg |= TPU_CTRL_DSP_EN;
214 writew(reg, TPU_REG(TPU_CTRL));
216 tpu_wait_ctrl_bit(TPU_CTRL_DSP_EN, BIT_SET);
219 /* Is a Frame interrupt still pending for the DSP ? */
220 int tpu_dsp_fameirq_pending(void)
222 uint16_t reg = readw(TPU_REG(TPU_CTRL));
224 if (reg & TPU_CTRL_DSP_EN)
225 return 1;
227 return 0;
230 void tpu_rewind(void)
232 dputs("tpu_rewind()\n");
233 tpu_ptr = (uint16_t *) BASE_ADDR_TPU_RAM;
236 void tpu_enqueue(uint16_t instr)
238 printd("tpu_enqueue(tpu_ptr=%p, instr=0x%04x)\n", tpu_ptr, instr);
239 tpu_debug_enqueue(instr);
240 *tpu_ptr++ = instr;
241 if (tpu_ptr > (uint16_t *) TPU_RAM_END)
242 puts("TPU enqueue beyond end of TPU memory\n");
245 void tpu_init(void)
247 uint16_t *ptr;
249 /* Put TPU into Reset and enable clock */
250 tpu_reset(1);
251 tpu_clk_enable(1);
253 /* set all TPU RAM to zero */
254 for (ptr = (uint16_t *) BASE_ADDR_TPU_RAM; ptr < (uint16_t *) TPU_RAM_END; ptr++)
255 *ptr = 0x0000;
257 /* Get TPU out of reset */
258 tpu_reset(0);
259 /* Disable all interrupts */
260 writeb(0x7, TPU_REG(INT_CTRL));
262 tpu_rewind();
263 tpu_enq_offset(0);
264 tpu_enq_sync(0);
267 void tpu_test(void)
269 int i;
271 /* program a sequence of TSPACT events into the TPU */
272 for (i = 0; i < 10; i++) {
273 puts("TSP ACT enable: ");
274 tsp_act_enable(0x0001);
275 tpu_enq_wait(10);
276 puts("TSP ACT disable: ");
277 tsp_act_disable(0x0001);
278 tpu_enq_wait(10);
280 tpu_enq_sleep();
282 /* tell the chip to execute the scenario */
283 tpu_enable(1);
286 void tpu_wait_idle(void)
288 dputs("Waiting for TPU Idle ");
289 /* Wait until TPU is doing something */
290 delay_us(3);
291 /* Wait until TPU is idle */
292 while (readw(TPU_REG(TPU_CTRL)) & TPU_CTRL_IDLE)
293 dputchar('.');
294 dputs("Done!\n");
297 void tpu_frame_irq_en(int mcu, int dsp)
299 uint8_t reg = readb(TPU_REG(INT_CTRL));
300 if (mcu)
301 reg &= ~ICTRL_MCU_FRAME;
302 else
303 reg |= ICTRL_MCU_FRAME;
305 if (dsp)
306 reg &= ~ICTRL_DSP_FRAME;
307 else
308 reg |= ICTRL_DSP_FRAME;
310 writeb(reg, TPU_REG(INT_CTRL));
313 void tpu_force_dsp_frame_irq(void)
315 uint8_t reg = readb(TPU_REG(INT_CTRL));
316 reg |= ICTRL_DSP_FRAME_FORCE;
317 writeb(reg, TPU_REG(INT_CTRL));
320 uint16_t tpu_get_offset(void)
322 return readw(TPU_REG(TPU_OFFSET));
325 uint16_t tpu_get_synchro(void)
327 return readw(TPU_REG(TPU_SYNCHRO));
330 /* add two numbers, modulo 5000, and ensure the result is positive */
331 uint16_t add_mod5000(int16_t a, int16_t b)
333 int32_t sum = (int32_t)a + (int32_t)b;
335 sum %= 5000;
337 /* wrap around zero */
338 if (sum < 0)
339 sum += 5000;
341 return sum;