0b60292adb1f1d4886c7d57dc9971746de02aa8e
[osmocom-bb.git] / src / target / firmware / calypso / tpu.c
1 /* Calypso DBB internal TPU (Time Processing Unit) Driver */
2
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4  *
5  * All Rights Reserved
6  *
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.
11  *
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.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  */
22
23 #include <stdint.h>
24 #include <stdio.h>
25
26 #include <debug.h>
27 #include <delay.h>
28 #include <memory.h>
29 #include <calypso/tpu.h>
30 #include <calypso/tsp.h>
31
32 /* Using TPU_DEBUG you will send special HLDC messages to the host PC
33  * containing the full TPU RAM content at the time you call tpu_enable() */
34 //#define TPU_DEBUG
35
36 #define BASE_ADDR_TPU   0xffff1000
37 #define TPU_REG(x)      (BASE_ADDR_TPU+(x))
38
39 #define BASE_ADDR_TPU_RAM       0xffff9000
40 #define TPU_RAM_END             0xffff97ff
41
42 enum tpu_reg_arm {
43         TPU_CTRL        = 0x0,  /* Control & Status Register */
44         INT_CTRL        = 0x2,  /* Interrupt Control Register */
45         INT_STAT        = 0x4,  /* Interrupt Status Register */
46         TPU_OFFSET      = 0xC,  /* Offset operand value register */
47         TPU_SYNCHRO     = 0xE,  /* synchro operand value register */
48         IT_DSP_PG       = 0x20,
49 };
50
51 enum tpu_ctrl_bits {
52         TPU_CTRL_RESET          = (1 << 0),
53         TPU_CTRL_PAGE           = (1 << 1),
54         TPU_CTRL_EN             = (1 << 2),
55         /* unused */
56         TPU_CTRL_DSP_EN         = (1 << 4),
57         /* unused */
58         TPU_CTRL_MCU_RAM_ACC    = (1 << 6),
59         TPU_CTRL_TSP_RESET      = (1 << 7),
60         TPU_CTRL_IDLE           = (1 << 8),
61         TPU_CTRL_WAIT           = (1 << 9),
62         TPU_CTRL_CK_ENABLE      = (1 << 10),
63         TPU_CTRL_FULL_WRITE     = (1 << 11),
64 };
65
66 enum tpu_int_ctrl_bits {
67         ICTRL_MCU_FRAME         = (1 << 0),
68         ICTRL_MCU_PAGE          = (1 << 1),
69         ICTRL_DSP_FRAME         = (1 << 2),
70         ICTRL_DSP_FRAME_FORCE   = (1 << 3),
71 };
72
73 static uint16_t *tpu_ptr = (uint16_t *)BASE_ADDR_TPU_RAM;
74
75 #ifdef TPU_DEBUG
76 #include <comm/sercomm.h>
77 #include <layer1/sync.h>
78 static void tpu_ram_read_en(int enable)
79 {
80         uint16_t reg;
81
82         reg = readw(TPU_REG(TPU_CTRL));
83         if (enable)
84                 reg |= TPU_CTRL_MCU_RAM_ACC;
85         else
86                 reg &= ~TPU_CTRL_MCU_RAM_ACC;
87         writew(reg, TPU_REG(TPU_CTRL));
88 }
89
90 static void tpu_debug(void)
91 {
92         uint16_t *tpu_base = (uint16_t *)BASE_ADDR_TPU_RAM;
93         unsigned int tpu_size = tpu_ptr - tpu_base;
94         struct msgb *msg = sercomm_alloc_msgb(tpu_size*2);
95         uint16_t *data;
96         uint32_t *fn;
97         uint16_t reg;
98         int i;
99
100         /* prepend tpu memory dump with frame number */
101         fn = (uint32_t *) msgb_put(msg, sizeof(fn));
102         *fn = l1s.current_time.fn;
103
104         tpu_ram_read_en(1);
105
106         data = (uint16_t *) msgb_put(msg, tpu_size*2);
107         for (i = 0; i < tpu_size; i ++)
108                 data[i] = tpu_base[i];
109
110         tpu_ram_read_en(0);
111
112         sercomm_sendmsg(SC_DLCI_DEBUG, msg);
113 }
114 #else
115 static void tpu_debug(void) { }
116 #endif
117
118 #define BIT_SET 1
119 #define BIT_CLEAR 0
120
121 /* wait for a certain control bit to be set */
122 static int tpu_wait_ctrl_bit(uint16_t bit, int set)
123 {
124         int timeout = 10*1000;
125
126         while (1) {
127                 uint16_t reg = readw(TPU_REG(TPU_CTRL));
128                 if (set) {
129                         if (reg & bit)
130                                 break;
131                 } else {
132                         if (!(reg & bit))
133                                 break;
134                 }
135                 timeout--;
136                 if (timeout <= 0) {
137                         puts("Timeout while waiting for TPU ctrl bit!\n");
138                         return -1;
139                 }
140         }
141
142         return 0;
143 }
144
145 /* assert or de-assert TPU reset */
146 void tpu_reset(int active)
147 {
148         uint16_t reg;
149
150         printd("tpu_reset(%u)\n", active);
151         reg = readw(TPU_REG(TPU_CTRL));
152         if (active) {
153                 reg |= (TPU_CTRL_RESET|TPU_CTRL_TSP_RESET);
154                 writew(reg, TPU_REG(TPU_CTRL));
155                 tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_SET);
156         } else {
157                 reg &= ~(TPU_CTRL_RESET|TPU_CTRL_TSP_RESET);
158                 writew(reg, TPU_REG(TPU_CTRL));
159                 tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_CLEAR);
160         }
161 }
162
163 /* Enable or Disable a new scenario loaded into the TPU */
164 void tpu_enable(int active)
165 {
166         uint16_t reg = readw(TPU_REG(TPU_CTRL));
167
168         printd("tpu_enable(%u)\n", active);
169
170         tpu_debug();
171
172         if (active)
173                 reg |= TPU_CTRL_EN;
174         else
175                 reg &= ~TPU_CTRL_EN;
176         writew(reg, TPU_REG(TPU_CTRL));
177
178         /* After the new scenario is loaded, TPU switches the MCU-visible memory
179          * page, i.e. we can write without any danger */
180         tpu_rewind();
181 #if 0
182         {
183                 int i;
184                 uint16_t oldreg = 0;
185
186                 for (i = 0; i < 100000; i++) {
187                         reg = readw(TPU_REG(TPU_CTRL));
188                         if (i == 0 || oldreg != reg) {
189                                 printd("%d TPU state: 0x%04x\n", i, reg);
190                         }
191                         oldreg = reg;
192                 }
193         }
194 #endif
195 }
196
197 /* Enable or Disable the clock of the TPU Module */
198 void tpu_clk_enable(int active)
199 {
200         uint16_t reg = readw(TPU_REG(TPU_CTRL));
201
202         printd("tpu_clk_enable(%u)\n", active);
203         if (active) {
204                 reg |= TPU_CTRL_CK_ENABLE;
205                 writew(reg, TPU_REG(TPU_CTRL));
206                 tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_SET);
207         } else {
208                 reg &= ~TPU_CTRL_CK_ENABLE;
209                 writew(reg, TPU_REG(TPU_CTRL));
210                 tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_CLEAR);
211         }
212 }
213
214 /* Enable Frame Interrupt generation on next frame.  DSP will reset it */
215 void tpu_dsp_frameirq_enable(void)
216 {
217         uint16_t reg = readw(TPU_REG(TPU_CTRL));
218         reg |= TPU_CTRL_DSP_EN;
219         writew(reg, TPU_REG(TPU_CTRL));
220
221         tpu_wait_ctrl_bit(TPU_CTRL_DSP_EN, BIT_SET);
222 }
223
224 /* Is a Frame interrupt still pending for the DSP ? */
225 int tpu_dsp_fameirq_pending(void)
226 {
227         uint16_t reg = readw(TPU_REG(TPU_CTRL));
228
229         if (reg & TPU_CTRL_DSP_EN)
230                 return 1;
231
232         return 0;
233 }
234
235 void tpu_rewind(void)
236 {
237         dputs("tpu_rewind()\n");
238         tpu_ptr = (uint16_t *) BASE_ADDR_TPU_RAM;
239 }
240
241 void tpu_enqueue(uint16_t instr)
242 {
243         printd("tpu_enqueue(tpu_ptr=%p, instr=0x%04x)\n", tpu_ptr, instr);
244         *tpu_ptr++ = instr;
245         if (tpu_ptr > (uint16_t *) TPU_RAM_END)
246                 puts("TPU enqueue beyond end of TPU memory\n");
247 }
248
249 void tpu_init(void)
250 {
251         uint16_t *ptr;
252
253         /* Put TPU into Reset and enable clock */
254         tpu_reset(1);
255         tpu_clk_enable(1);
256
257         /* set all TPU RAM to zero */
258         for (ptr = (uint16_t *) BASE_ADDR_TPU_RAM; ptr < (uint16_t *) TPU_RAM_END; ptr++)
259                 *ptr = 0x0000;
260
261         /* Get TPU out of reset */
262         tpu_reset(0);
263         /* Disable all interrupts */
264         writeb(0x7, TPU_REG(INT_CTRL));
265
266         tpu_rewind();
267         tpu_enq_offset(0);
268         tpu_enq_sync(0);
269 }
270
271 void tpu_test(void)
272 {
273         int i;
274
275         /* program a sequence of TSPACT events into the TPU */
276         for (i = 0; i < 10; i++) {
277                 puts("TSP ACT enable: ");
278                 tsp_act_enable(0x0001);
279                 tpu_enq_wait(10);
280                 puts("TSP ACT disable: ");
281                 tsp_act_disable(0x0001);
282                 tpu_enq_wait(10);
283         }
284         tpu_enq_sleep();
285
286         /* tell the chip to execute the scenario */
287         tpu_enable(1);
288 }
289
290 void tpu_wait_idle(void)
291 {
292         dputs("Waiting for TPU Idle ");
293         /* Wait until TPU is doing something */
294         delay_us(3);
295         /* Wait until TPU is idle */
296         while (readw(TPU_REG(TPU_CTRL)) & TPU_CTRL_IDLE)
297                 dputchar('.');
298         dputs("Done!\n");
299 }
300
301 void tpu_frame_irq_en(int mcu, int dsp)
302 {
303         uint8_t reg = readb(TPU_REG(INT_CTRL));
304         if (mcu)
305                 reg &= ~ICTRL_MCU_FRAME;
306         else
307                 reg |= ICTRL_MCU_FRAME;
308
309         if (dsp)
310                 reg &= ~ICTRL_DSP_FRAME;
311         else
312                 reg |= ICTRL_DSP_FRAME;
313
314         writeb(reg, TPU_REG(INT_CTRL));
315 }
316
317 void tpu_force_dsp_frame_irq(void)
318 {
319         uint8_t reg = readb(TPU_REG(INT_CTRL));
320         reg |= ICTRL_DSP_FRAME_FORCE;
321         writeb(reg, TPU_REG(INT_CTRL));
322 }
323
324 uint16_t tpu_get_offset(void)
325 {
326         return readw(TPU_REG(TPU_OFFSET));
327 }
328
329 uint16_t tpu_get_synchro(void)
330 {
331         return readw(TPU_REG(TPU_SYNCHRO));
332 }
333
334 /* add two numbers, modulo 5000, and ensure the result is positive */
335 uint16_t add_mod5000(int16_t a, int16_t b)
336 {
337         int32_t sum = (int32_t)a + (int32_t)b;
338
339         sum %= 5000;
340
341         /* wrap around zero */
342         if (sum < 0)
343                 sum += 5000;
344
345         return sum;
346 }