3397dc03107d5e345ed309e06798f4e10a1528a9
[osmocom-bb.git] / src / target / firmware / layer1 / sync.c
1 /* Synchronous part of GSM Layer 1 */
2
3 /* (C) 2010 by Harald Welte <laforge@gnumonks.org>
4  * (C) 2010 by Dieter Spaar <spaar@mirider.augusta.de>
5  * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
6  *
7  * All Rights Reserved
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  *
23  */
24
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #include <defines.h>
31 #include <debug.h>
32 #include <memory.h>
33 #include <byteorder.h>
34 #include <asm/system.h>
35
36 #include <osmocore/gsm_utils.h>
37 #include <osmocore/msgb.h>
38 #include <calypso/dsp_api.h>
39 #include <calypso/irq.h>
40 #include <calypso/tpu.h>
41 #include <calypso/tsp.h>
42 #include <calypso/dsp.h>
43 #include <calypso/timer.h>
44 #include <comm/sercomm.h>
45
46 #include <abb/twl3025.h>
47
48 //#define DEBUG_EVERY_TDMA
49
50 #include <layer1/sync.h>
51 #include <layer1/afc.h>
52 #include <layer1/agc.h>
53 #include <layer1/apc.h>
54 #include <layer1/tdma_sched.h>
55 #include <layer1/mframe_sched.h>
56 #include <layer1/sched_gsmtime.h>
57 #include <layer1/tpu_window.h>
58 #include <layer1/l23_api.h>
59
60 #include <l1ctl_proto.h>
61
62 struct l1s_state l1s;
63
64 void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn)
65 {
66         ADD_MODULO(time->fn, delta_fn, GSM_MAX_FN);
67
68         if (delta_fn == 1) {
69                 ADD_MODULO(time->t2, 1, 26);
70                 ADD_MODULO(time->t3, 1, 51);
71
72                 /* if the new frame number is a multiple of 51 */
73                 if (time->t3 == 0) {
74                         ADD_MODULO(time->tc, 1, 8);
75
76                         /* if new FN is multiple of 51 and 26 */
77                         if (time->t2 == 0)
78                                 ADD_MODULO(time->t1, 1, 2048);
79                 }
80         } else
81                 gsm_fn2gsmtime(time, time->fn);
82 }
83
84 void l1s_time_dump(const struct gsm_time *time)
85 {
86         printf("fn=%lu(%u/%2u/%2u)", time->fn, time->t1, time->t2, time->t3);
87 }
88
89 /* clip a signed 16bit value at a certain limit */
90 int16_t clip_int16(int16_t angle, int16_t clip_at)
91 {
92         if (angle > clip_at)
93                 angle = clip_at;
94         else if (angle < -clip_at)
95                 angle = -clip_at;
96
97         return angle;
98 }
99
100 int16_t l1s_snr_int(uint16_t snr)
101 {
102         return snr >> 10;
103 }
104
105 uint16_t l1s_snr_fract(uint16_t snr)
106 {
107         uint32_t fract = snr & 0x3ff;
108         fract = fract * 1000 / (2 << 10);
109
110         return fract & 0xffff;
111 }
112
113 #define AFC_MAX_ANGLE           328     /* 0.01 radian in fx1.15 */
114
115 /* synchronize the L1S to a new timebase (typically a new cell */
116 void synchronize_tdma(struct l1_cell_info *cinfo)
117 {
118         int32_t fn_offset;
119         uint32_t tpu_shift = cinfo->time_alignment;
120
121         /* NB detection only works if the TOA of the SB
122          * is within 0...8. We have to add 75 to get an SB TOA of 4. */
123         tpu_shift += 75;
124
125         tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA;
126
127         fn_offset = cinfo->fn_offset - 1;
128
129         /* if we're already very close to the end of the TPU frame, the
130          * next interrupt will basically occur now and we need to
131          * compensate */
132         if (tpu_shift < SWITCH_TIME)
133                 fn_offset++;
134
135 #if 0 /* probably wrong as we already added "offset" and "shift" above */
136         /* increment the TPU quarter-bit offset */
137         l1s.tpu_offset = (l1s.tpu_offset + tpu_shift) % TPU_RANGE;
138 #else
139         l1s.tpu_offset = tpu_shift;
140 #endif
141
142         puts("Synchronize_TDMA\n");
143         /* request the TPU to adjust the SYNCHRO and OFFSET registers */
144         tpu_enq_at(SWITCH_TIME);
145         tpu_enq_sync(l1s.tpu_offset);
146 #if 0
147         /* FIXME: properly end the TPU window at the emd of l1_sync() */
148         tpu_end_scenario();
149 #endif
150
151         /* Change the current time to reflect the new value */
152         l1s_time_inc(&l1s.current_time, fn_offset);
153         l1s.next_time = l1s.current_time;
154         l1s_time_inc(&l1s.next_time, 1);
155
156         /* The serving cell now no longer has a frame or bit offset */
157         cinfo->fn_offset = 0;
158         cinfo->time_alignment = 0;
159 }
160
161 void l1s_reset_hw(void)
162 {
163         dsp_api.w_page = 0;
164         dsp_api.r_page = 0;
165         dsp_api.r_page_used = 0;
166         dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0;
167         dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0;
168         dsp_api.ndb->d_dsp_page = 0;
169
170         /* we have to really reset the TPU, otherwise FB detection
171          * somtimes returns wrong TOA values. */
172         tpu_reset(1);
173         tpu_reset(0);
174         tpu_rewind();
175         tpu_enq_wait(5); /* really needed ? */
176         tpu_enq_sync(l1s.tpu_offset);
177         tpu_end_scenario();
178 }
179
180 /* Lost TDMA interrupt detection.  This works by starting a hardware timer
181  * that is clocked by the same master clock source (VCTCXO).  We expect
182  * 1875 timer ticks in the duration of a TDMA frame (5000 qbits / 1250 bits) */
183
184 /* Timer for detecting lost IRQ */
185 #define TIMER_TICKS_PER_TDMA    1875
186 #define TIMER_TICK_JITTER       1
187
188 static int last_timestamp;
189
190 static inline void check_lost_frame(void)
191 {
192         int diff, timestamp = hwtimer_read(1);
193
194         if (last_timestamp < timestamp)
195                 last_timestamp += (4*TIMER_TICKS_PER_TDMA);
196
197         diff = last_timestamp - timestamp;
198
199         /* allow for a bit of jitter */
200         if (diff < TIMER_TICKS_PER_TDMA - TIMER_TICK_JITTER ||
201             diff > TIMER_TICKS_PER_TDMA + TIMER_TICK_JITTER)
202                 printf("LOST %d!\n", diff);
203
204         last_timestamp = timestamp;
205 }
206
207 /* schedule a completion */
208 void l1s_compl_sched(enum l1_compl c)
209 {
210         unsigned long flags;
211
212         local_firq_save(flags);
213         l1s.scheduled_compl |= (1 << c);
214         local_irq_restore(flags);
215 }
216
217 /* main routine for synchronous part of layer 1, called by frame interrupt
218  * generated by TPU once every TDMA frame */
219 static void l1_sync(void)
220 {
221         uint16_t sched_flags;
222
223         putchart('+');
224
225         check_lost_frame();
226
227         /* Increment Time */
228         l1s.current_time = l1s.next_time;
229         l1s_time_inc(&l1s.next_time, 1);
230         //l1s_time_dump(&l1s.current_time); putchar(' ');
231
232         dsp_api.frame_ctr++;
233         dsp_api.r_page_used = 0;
234
235         /* Update pointers */
236         if (dsp_api.w_page == 0)
237                 dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0;
238         else
239                 dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_1;
240
241         if (dsp_api.r_page == 0)
242                 dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0;
243         else
244                 dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_1;
245
246         /* Reset MCU->DSP page */
247         dsp_api_memset((uint16_t *) dsp_api.db_w, sizeof(*dsp_api.db_w));
248
249         /* Update AFC */
250         afc_load_dsp();
251
252         if (dsp_api.ndb->d_error_status) {
253                 printf("DSP Error Status: %u\n", dsp_api.ndb->d_error_status);
254                 dsp_api.ndb->d_error_status = 0;
255         }
256
257         /* execute the sched_items that have been scheduled for this
258          * TDMA frame (including setup/cleanup steps) */
259         sched_flags = tdma_sched_flag_scan();
260
261         if (sched_flags & TDMA_IFLG_TPU)
262                 l1s_win_init();
263
264         tdma_sched_execute();
265
266         if (dsp_api.r_page_used) {
267                 /* clear and switch the read page */
268                 dsp_api_memset((uint16_t *) dsp_api.db_r,
269                                 sizeof(*dsp_api.db_r));
270
271                 /* TSM30 does it (really needed ?):
272                  * Set crc result as "SB not found". */
273                 dsp_api.db_r->a_sch[0] = (1<<B_SCH_CRC);   /* B_SCH_CRC =1, BLUD =0 */
274
275                 dsp_api.r_page ^= 1;
276         }
277
278         if (sched_flags & TDMA_IFLG_DSP)
279                 dsp_end_scenario();
280
281         if (sched_flags & TDMA_IFLG_TPU)
282                 tpu_end_scenario();
283
284         /* schedule new / upcoming TDMA items */
285         mframe_schedule();
286         /* schedule new / upcoming one-shot events */
287         sched_gsmtime_execute(l1s.current_time.fn);
288
289         tdma_sched_advance();
290 }
291
292 /* ABORT command ********************************************************/
293
294 static int l1s_abort_cmd(__unused uint8_t p1, __unused uint8_t p2,
295                          __unused uint16_t p3)
296 {
297         putchart('A');
298
299         /* similar to l1s_reset_hw() without touching the TPU */
300
301         dsp_api.w_page = 0;
302         dsp_api.r_page = 0;
303         dsp_api.r_page_used = 0;
304         dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0;
305         dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0;
306
307         /* Reset task commands. */
308         dsp_api.db_w->d_task_d  = NO_DSP_TASK; /* Init. RX task to NO TASK */
309         dsp_api.db_w->d_task_u  = NO_DSP_TASK; /* Init. TX task to NO TASK */
310         dsp_api.db_w->d_task_ra = NO_DSP_TASK; /* Init. RA task to NO TASK */
311         dsp_api.db_w->d_task_md = NO_DSP_TASK; /* Init. MONITORING task to NO TASK */
312         dsp_api.ndb->d_dsp_page = 0;
313
314         /* Set "b_abort" to TRUE, dsp will reset current and pending tasks */
315         dsp_api.db_w->d_ctrl_system |= (1 << B_TASK_ABORT);
316         return 0;
317 }
318
319 void l1s_dsp_abort(void)
320 {
321         /* abort right now */
322         tdma_schedule(0, &l1s_abort_cmd, 0, 0, 0, 10);
323 }
324
325 void l1s_tx_apc_helper(uint16_t arfcn)
326 {
327         int16_t auxapc;
328         enum gsm_band band;
329         int i;
330
331         /* Get DAC setting */
332         band = gsm_arfcn2band(arfcn);
333         auxapc = apc_tx_pwrlvl2auxapc(band, l1s.tx_power);
334
335         /* Load the ApcOffset into the DSP */
336         #define  MY_OFFSET      4
337         dsp_api.ndb->d_apcoff = ABB_VAL(APCOFF, (1 << 6) | MY_OFFSET) | 1; /* 2x slope for the GTA-02 ramp */
338
339         /* Load the TX Power into the DSP */
340         /*
341            If the power is too low (below 0 dBm) the ramp is not OK,
342            especially for GSM-1800. However an MS does not send below
343            0dBm anyway.
344         */
345         dsp_api.db_w->d_power_ctl = ABB_VAL(AUXAPC, auxapc);
346
347         /* Update the ramp according to the PCL */
348         for (i = 0; i < 16; i++)
349                 dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, twl3025_default_ramp[i]);
350
351         /* The Ramp Table is sent to ABB only once after RF init routine called */
352         dsp_api.db_w->d_ctrl_abb |= (1 << B_RAMP) | (1 << B_BULRAMPDEL);
353 }
354
355 /* Interrupt handler */
356 static void frame_irq(__unused enum irq_nr nr)
357 {
358         l1_sync();
359 }
360
361 /* reset the layer1 as part of synchronizing to a new cell */
362 void l1s_reset(void)
363 {
364         /* Reset state */
365         l1s.fb.mode = 0;
366         l1s.tx_power = 7; /* initial power reset */
367
368         /* Leave dedicated mode */
369         l1s.dedicated.type = GSM_DCHAN_NONE;
370
371         /* reset scheduler and hardware */
372         sched_gsmtime_reset();
373         mframe_reset();
374         tdma_sched_reset();
375         l1s_dsp_abort();
376
377         /* Cipher off */
378         dsp_load_ciph_param(0, NULL);
379 }
380
381 void l1s_init(void)
382 {
383         unsigned int i;
384
385         for (i = 0; i < ARRAY_SIZE(l1s.tx_queue); i++)
386                 INIT_LLIST_HEAD(&l1s.tx_queue[i]);
387         l1s.tx_meas = NULL;
388
389         sched_gsmtime_init();
390
391         /* register FRAME interrupt as FIQ so it can interrupt normal IRQs */
392         irq_register_handler(IRQ_TPU_FRAME, &frame_irq);
393         irq_config(IRQ_TPU_FRAME, 1, 1, 0);
394         irq_enable(IRQ_TPU_FRAME);
395
396         /* configure timer 1 to be auto-reload and have a prescale of 12 (13MHz/12 == qbit clock) */
397         hwtimer_enable(1, 1);
398         hwtimer_load(1, (1875*4)-1);
399         hwtimer_config(1, 0, 1);
400         hwtimer_enable(1, 1);
401 }
402