Initial import of OsmocomBB into git repository
[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 #define BASE_ADDR_TPU   0xffff1000
33 #define TPU_REG(x)      (BASE_ADDR_TPU+(x))
34
35 #define BASE_ADDR_TPU_RAM       0xffff9000
36 #define TPU_RAM_END             0xffff97ff
37
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,
45 };
46
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),
60 };
61
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),
67 };
68
69 #define BIT_SET 1
70 #define BIT_CLEAR 0
71
72 /* wait for a certain control bit to be set */
73 static int tpu_wait_ctrl_bit(uint16_t bit, int set)
74 {
75         int timeout = 10*1000;
76
77         while (1) {
78                 uint16_t reg = readw(TPU_REG(TPU_CTRL));
79                 if (set) {
80                         if (reg & bit)
81                                 break;
82                 } else {
83                         if (!(reg & bit))
84                                 break;
85                 }
86                 timeout--;
87                 if (timeout <= 0) {
88                         puts("Timeout while waiting for TPU ctrl bit!\n");
89                         return -1;
90                 }
91         }
92
93         return 0;
94 }
95
96 /* assert or de-assert TPU reset */
97 void tpu_reset(int active)
98 {
99         uint16_t reg;
100
101         printd("tpu_reset(%u)\n", active);
102         reg = readw(TPU_REG(TPU_CTRL));
103         if (active) {
104                 reg |= (TPU_CTRL_RESET|TPU_CTRL_TSP_RESET);
105                 writew(reg, TPU_REG(TPU_CTRL));
106                 tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_SET);
107         } else {
108                 reg &= ~(TPU_CTRL_RESET|TPU_CTRL_TSP_RESET);
109                 writew(reg, TPU_REG(TPU_CTRL));
110                 tpu_wait_ctrl_bit(TPU_CTRL_RESET, BIT_CLEAR);
111         }
112 }
113
114 /* Enable or Disable a new scenario loaded into the TPU */
115 void tpu_enable(int active)
116 {
117         uint16_t reg = readw(TPU_REG(TPU_CTRL));
118
119         printd("tpu_enable(%u)\n", active);
120         if (active)
121                 reg |= TPU_CTRL_EN;
122         else
123                 reg &= ~TPU_CTRL_EN;
124         writew(reg, TPU_REG(TPU_CTRL));
125
126         /* After the new scenario is loaded, TPU switches the MCU-visible memory
127          * page, i.e. we can write without any danger */
128         tpu_rewind();
129 #if 0
130         {
131                 int i;
132                 uint16_t oldreg = 0;
133
134                 for (i = 0; i < 100000; i++) {
135                         reg = readw(TPU_REG(TPU_CTRL));
136                         if (i == 0 || oldreg != reg) {
137                                 printd("%d TPU state: 0x%04x\n", i, reg);
138                         }
139                         oldreg = reg;
140                 }
141         }
142 #endif
143 }
144
145 /* Enable or Disable the clock of teh TPU Module */
146 void tpu_clk_enable(int active)
147 {
148         uint16_t reg = readw(TPU_REG(TPU_CTRL));
149
150         printd("tpu_clk_enable(%u)\n", active);
151         if (active) {
152                 reg |= TPU_CTRL_CK_ENABLE;
153                 writew(reg, TPU_REG(TPU_CTRL));
154                 tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_SET);
155         } else {
156                 reg &= ~TPU_CTRL_CK_ENABLE;
157                 writew(reg, TPU_REG(TPU_CTRL));
158                 tpu_wait_ctrl_bit(TPU_CTRL_CK_ENABLE, BIT_CLEAR);
159         }
160 }
161
162 /* Enable Frame Interrupt generation on next frame.  DSP will reset it */
163 void tpu_dsp_frameirq_enable(void)
164 {
165         uint16_t reg = readw(TPU_REG(TPU_CTRL));
166         reg |= TPU_CTRL_DSP_EN;
167         writew(reg, TPU_REG(TPU_CTRL));
168
169         tpu_wait_ctrl_bit(TPU_CTRL_DSP_EN, BIT_SET);
170 }
171
172 /* Is a Frame interrupt still pending for the DSP ? */
173 int tpu_dsp_fameirq_pending(void)
174 {
175         uint16_t reg = readw(TPU_REG(TPU_CTRL));
176
177         if (reg & TPU_CTRL_DSP_EN)
178                 return 1;
179
180         return 0;
181 }
182
183 static uint16_t *tpu_ptr;
184
185 void tpu_rewind(void)
186 {
187         dputs("tpu_rewind()\n");
188         tpu_ptr = (uint16_t *) BASE_ADDR_TPU_RAM;
189 }
190
191 void tpu_enqueue(uint16_t instr)
192 {
193         printd("tpu_enqueue(tpu_ptr=%p, instr=0x%04x\n", tpu_ptr, instr);
194         *tpu_ptr++ = instr;
195         if (tpu_ptr > (uint16_t *) TPU_RAM_END)
196                 puts("TPU enqueue beyond end of TPU memory\n");
197 }
198
199 void tpu_init(void)
200 {
201         /* Get TPU out of reset */
202         tpu_reset(1);
203         tpu_clk_enable(1);
204         tpu_reset(0);
205         /* Disable all interrupts */
206         writeb(0x7, TPU_REG(INT_CTRL));
207
208         tpu_rewind();
209         tpu_enq_offset(0);
210         tpu_enq_sync(0);
211 }
212
213 void tpu_test(void)
214 {
215         int i;
216
217         /* program a sequence of TSPACT events into the TPU */
218         for (i = 0; i < 10; i++) {
219                 puts("TSP ACT enable: ");
220                 tsp_act_enable(0x0001);
221                 tpu_enq_wait(10);
222                 puts("TSP ACT disable: ");
223                 tsp_act_disable(0x0001);
224                 tpu_enq_wait(10);
225         }
226         tpu_enq_sleep();
227
228         /* tell the chip to execute the scenario */
229         tpu_enable(1);
230 }
231
232 void tpu_wait_idle(void)
233 {
234         dputs("Waiting for TPU Idle ");
235         /* Wait until TPU is doing something */
236         delay_us(3);
237         /* Wait until TPU is idle */
238         while (readw(TPU_REG(TPU_CTRL)) & TPU_CTRL_IDLE)
239                 dputchar('.');
240         dputs("Done!\n");
241 }
242
243 void tpu_frame_irq_en(int mcu, int dsp)
244 {
245         uint8_t reg = readb(TPU_REG(INT_CTRL));
246         if (mcu)
247                 reg &= ~ICTRL_MCU_FRAME;
248         else
249                 reg |= ICTRL_MCU_FRAME;
250
251         if (dsp)
252                 reg &= ~ICTRL_DSP_FRAME;
253         else
254                 reg |= ICTRL_DSP_FRAME;
255
256         writeb(reg, TPU_REG(INT_CTRL));
257 }
258
259 void tpu_force_dsp_frame_irq(void)
260 {
261         uint8_t reg = readb(TPU_REG(INT_CTRL));
262         reg |= ICTRL_DSP_FRAME_FORCE;
263         writeb(reg, TPU_REG(INT_CTRL));
264 }
265
266 uint16_t tpu_get_offset(void)
267 {
268         return readw(TPU_REG(TPU_OFFSET));
269 }
270
271 uint16_t tpu_get_synchro(void)
272 {
273         return readw(TPU_REG(TPU_SYNCHRO));
274 }
275
276 /* add two numbers, modulo 5000, and ensure the result is positive */
277 uint16_t add_mod5000(uint16_t a, uint16_t b)
278 {
279         int32_t sum = (uint32_t)a + (uint32_t)b;
280
281         sum %= 5000;
282
283         /* wrap around zero */
284         if (sum < 0)
285                 sum += 5000;
286
287         return sum;
288 }