1 /* Synchronous part of GSM Layer 1 */
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>
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.
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.
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.
33 #include <byteorder.h>
34 #include <asm/system.h>
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>
46 #include <abb/twl3025.h>
48 //#define DEBUG_EVERY_TDMA
50 #include <layer1/sync.h>
51 #include <layer1/afc.h>
52 #include <layer1/agc.h>
53 #include <layer1/tdma_sched.h>
54 #include <layer1/mframe_sched.h>
55 #include <layer1/sched_gsmtime.h>
56 #include <layer1/tpu_window.h>
57 #include <layer1/l23_api.h>
59 #include <l1a_l23_interface.h>
63 void l1s_time_inc(struct gsm_time *time, uint32_t delta_fn)
65 ADD_MODULO(time->fn, delta_fn, GSM_MAX_FN);
68 ADD_MODULO(time->t2, 1, 26);
69 ADD_MODULO(time->t3, 1, 51);
71 /* if the new frame number is a multiple of 51 */
73 ADD_MODULO(time->tc, 1, 8);
75 /* if new FN is multiple of 51 and 26 */
77 ADD_MODULO(time->t1, 1, 2048);
80 gsm_fn2gsmtime(time, time->fn);
83 void l1s_time_dump(const struct gsm_time *time)
85 printf("fn=%lu(%u/%2u/%2u)", time->fn, time->t1, time->t2, time->t3);
88 /* clip a signed 16bit value at a certain limit */
89 int16_t clip_int16(int16_t angle, int16_t clip_at)
93 else if (angle < -clip_at)
99 int16_t l1s_snr_int(uint16_t snr)
104 uint16_t l1s_snr_fract(uint16_t snr)
106 uint32_t fract = snr & 0x3ff;
107 fract = fract * 1000 / (2 << 10);
109 return fract & 0xffff;
112 #define AFC_MAX_ANGLE 328 /* 0.01 radian in fx1.15 */
114 /* synchronize the L1S to a new timebase (typically a new cell */
115 void synchronize_tdma(struct l1_cell_info *cinfo)
118 uint32_t tpu_shift = cinfo->time_alignment;
120 /* NB detection only works if the TOA of the SB
121 * is within 0...8. We have to add 75 to get an SB TOA of 4. */
124 tpu_shift = (l1s.tpu_offset + tpu_shift) % QBITS_PER_TDMA;
126 fn_offset = cinfo->fn_offset - 1;
128 /* if we're already very close to the end of the TPU frame, the
129 * next interrupt will basically occur now and we need to
131 if (tpu_shift < SWITCH_TIME)
134 #if 0 /* probably wrong as we already added "offset" and "shift" above */
135 /* increment the TPU quarter-bit offset */
136 l1s.tpu_offset = (l1s.tpu_offset + tpu_shift) % TPU_RANGE;
138 l1s.tpu_offset = tpu_shift;
141 puts("Synchronize_TDMA\n");
142 /* request the TPU to adjust the SYNCHRO and OFFSET registers */
143 tpu_enq_at(SWITCH_TIME);
144 tpu_enq_sync(l1s.tpu_offset);
146 /* FIXME: properly end the TPU window at the emd of l1_sync() */
150 /* Change the current time to reflect the new value */
151 l1s_time_inc(&l1s.current_time, fn_offset);
152 l1s.next_time = l1s.current_time;
153 l1s_time_inc(&l1s.next_time, 1);
155 /* The serving cell now no longer has a frame or bit offset */
156 cinfo->fn_offset = 0;
157 cinfo->time_alignment = 0;
160 void l1s_reset_hw(void)
164 dsp_api.r_page_used = 0;
165 dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0;
166 dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0;
167 dsp_api.ndb->d_dsp_page = 0;
169 /* we have to really reset the TPU, otherwise FB detection
170 * somtimes returns wrong TOA values. */
174 tpu_enq_wait(5); /* really needed ? */
175 tpu_enq_offset(l1s.tpu_offset);
179 /* Timer for detecting lost IRQ */
180 #define TIMER_TICKS_PER_TDMA 1875
182 static int last_timestamp;
184 static inline void check_lost_frame(void)
186 int diff, timestamp = hwtimer_read(1);
188 if (last_timestamp < timestamp)
189 last_timestamp += (4*TIMER_TICKS_PER_TDMA);
191 diff = last_timestamp - timestamp;
195 last_timestamp = timestamp;
198 /* schedule a completion */
199 void l1s_compl_sched(enum l1_compl c)
203 local_firq_save(flags);
204 l1s.scheduled_compl |= (1 << c);
205 local_irq_restore(flags);
208 /* main routine for synchronous part of layer 1, called by frame interrupt
209 * generated by TPU once every TDMA frame */
210 static void l1_sync(void)
217 l1s.current_time = l1s.next_time;
218 l1s_time_inc(&l1s.next_time, 1);
219 //l1s_time_dump(&l1s.current_time); putchar(' ');
222 dsp_api.r_page_used = 0;
224 /* Update pointers */
225 if (dsp_api.w_page == 0)
226 dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0;
228 dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_1;
230 if (dsp_api.r_page == 0)
231 dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0;
233 dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_1;
235 /* Reset MCU->DSP page */
236 dsp_api_memset((uint16_t *) dsp_api.db_w, sizeof(*dsp_api.db_w));
241 if (dsp_api.ndb->d_error_status) {
242 printf("DSP Error Status: %u\n", dsp_api.ndb->d_error_status);
243 dsp_api.ndb->d_error_status = 0;
246 /* execute the sched_items that have been scheduled for this
248 tdma_sched_execute();
250 if (dsp_api.r_page_used) {
251 /* clear and switch the read page */
252 dsp_api_memset((uint16_t *) dsp_api.db_r,
253 sizeof(*dsp_api.db_r));
255 /* TSM30 does it (really needed ?):
256 * Set crc result as "SB not found". */
257 dsp_api.db_r->a_sch[0] = (1<<B_SCH_CRC); /* B_SCH_CRC =1, BLUD =0 */
262 //dsp_end_scenario();
264 /* schedule new / upcoming TDMA items */
266 /* schedule new / upcoming one-shot events */
267 sched_gsmtime_execute(l1s.current_time.fn);
269 tdma_sched_advance();
272 /* ABORT command ********************************************************/
274 static int l1s_abort_cmd(__unused uint8_t p1, __unused uint8_t p2,
275 __unused uint16_t p3)
279 /* similar to l1s_reset_hw() without touching the TPU */
283 dsp_api.r_page_used = 0;
284 dsp_api.db_w = (T_DB_MCU_TO_DSP *) BASE_API_W_PAGE_0;
285 dsp_api.db_r = (T_DB_DSP_TO_MCU *) BASE_API_R_PAGE_0;
287 /* Reset task commands. */
288 dsp_api.db_w->d_task_d = NO_DSP_TASK; /* Init. RX task to NO TASK */
289 dsp_api.db_w->d_task_u = NO_DSP_TASK; /* Init. TX task to NO TASK */
290 dsp_api.db_w->d_task_ra = NO_DSP_TASK; /* Init. RA task to NO TASK */
291 dsp_api.db_w->d_task_md = NO_DSP_TASK; /* Init. MONITORING task to NO TASK */
292 dsp_api.ndb->d_dsp_page = 0;
294 /* Set "b_abort" to TRUE, dsp will reset current and pending tasks */
295 dsp_api.db_w->d_ctrl_system |= (1 << B_TASK_ABORT);
299 void l1s_dsp_abort(void)
301 /* abort right now */
302 tdma_schedule(0, &l1s_abort_cmd, 0, 0, 0);
305 void l1s_tx_apc_helper(void)
309 /* Load the ApcOffset into the DSP */
311 dsp_api.ndb->d_apcoff = ABB_VAL(APCOFF, (1 << 6) | MY_OFFSET) | 1; /* 2x slope for the GTA-02 ramp */
313 /* Load the TX Power into the DSP */
315 If the power is too low (below 0 dBm) the ramp is not OK,
316 especially for GSM-1800. However an MS does not send below
319 dsp_api.db_w->d_power_ctl = ABB_VAL(AUXAPC, 0xC0); /* 2 dBm pulse with offset 4 (GSM-1800) */
321 /* Update the ramp according to the PCL */
322 for (i = 0; i < 16; i++)
323 dsp_api.ndb->a_ramp[i] = ABB_VAL(APCRAM, twl3025_default_ramp[i]);
325 /* The Ramp Table is sent to ABB only once after RF init routine called */
326 dsp_api.db_w->d_ctrl_abb |= (1 << B_RAMP) | (1 << B_BULRAMPDEL);
329 /* Interrupt handler */
330 static void frame_irq(__unused enum irq_nr nr)
335 /* reset the layer1 as part of synchronizing to a new cell */
342 /* reset scheduler and hardware */
352 for (i = 0; i < ARRAY_SIZE(l1s.tx_queue); i++)
353 INIT_LLIST_HEAD(&l1s.tx_queue[i]);
355 sched_gsmtime_init();
357 /* register FRAME interrupt as FIQ so it can interrupt normal IRQs */
358 irq_register_handler(IRQ_TPU_FRAME, &frame_irq);
359 irq_config(IRQ_TPU_FRAME, 1, 1, 0);
360 irq_enable(IRQ_TPU_FRAME);
362 /* configure timer 1 to be auto-reload and have a prescale of 12 (13MHz/12 == qbit clock) */
363 hwtimer_enable(1, 1);
364 hwtimer_load(1, (1875*4)-1);
365 hwtimer_config(1, 0, 1);
366 hwtimer_enable(1, 1);