treewide: remove FSF address
[osmocom-bb.git] / src / target / firmware / layer1 / prim_pm.c
blob11bee99b2c5c79b33716e5fcdc625369ba9b5405
1 /* Layer 1 Power Measurement */
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>
21 #include <string.h>
22 #include <stdlib.h>
24 #include <defines.h>
25 #include <debug.h>
26 #include <memory.h>
27 #include <byteorder.h>
28 #include <osmocom/gsm/gsm_utils.h>
29 #include <osmocom/core/msgb.h>
30 #include <calypso/dsp_api.h>
31 #include <calypso/irq.h>
32 #include <calypso/tpu.h>
33 #include <calypso/tsp.h>
34 #include <calypso/dsp.h>
35 #include <calypso/timer.h>
36 #include <comm/sercomm.h>
37 #include <asm/system.h>
39 #include <layer1/sync.h>
40 #include <layer1/agc.h>
41 #include <layer1/tdma_sched.h>
42 #include <layer1/tpu_window.h>
43 #include <layer1/l23_api.h>
44 #include <layer1/prim.h>
45 #include <rffe.h>
47 #include <l1ctl_proto.h>
49 static void l1ddsp_meas_read(uint8_t nbmeas, uint16_t *pm)
51 uint8_t i;
53 for (i = 0; i < nbmeas; i++)
54 pm[i] = (uint16_t) ((dsp_api.db_r->a_pm[i] & 0xffff) >> 3);
55 dsp_api.r_page_used = 1;
58 /* scheduler callback to issue a power measurement task to the DSP */
59 static int l1s_pm_cmd(uint8_t num_meas,
60 __unused uint8_t p2, uint16_t arfcn)
62 putchart('P');
64 dsp_api.db_w->d_task_md = num_meas; /* number of measurements */
65 dsp_api.ndb->d_fb_mode = 0; /* wideband search */
67 /* Tell the RF frontend to set the gain appropriately */
68 rffe_compute_gain(-85, CAL_DSP_TGT_BB_LVL);
70 /* Program TPU */
71 /* FIXME: RXWIN_PW needs to set up multiple times in case
72 * num_meas > 1 */
73 l1s_rx_win_ctrl(arfcn, L1_RXWIN_PW, 0);
74 //l1s_rx_win_ctrl(arfcn, L1_RXWIN_NB);
76 return 0;
79 /* scheduler callback to read power measurement resposnse from the DSP */
80 static int l1s_pm_resp(uint8_t num_meas, __unused uint8_t p2,
81 uint16_t arfcn)
83 struct l1ctl_pm_conf *pmr;
84 uint16_t pm_level[2];
86 putchart('p');
88 l1ddsp_meas_read(num_meas, pm_level);
90 printf("PM MEAS: ARFCN=%u, %-4d dBm at baseband, %-4d dBm at RF\n",
91 arfcn, pm_level[0]/8, agc_inp_dbm8_by_pm(pm_level[0])/8);
93 printd("PM MEAS: %-4d dBm, %-4d dBm ARFCN=%u\n",
94 agc_inp_dbm8_by_pm(pm_level[0])/8,
95 agc_inp_dbm8_by_pm(pm_level[1])/8, arfcn);
97 if (!l1s.pm.msg)
98 l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF);
100 if (msgb_tailroom(l1s.pm.msg) < sizeof(*pmr)) {
101 /* flush current msgb */
102 l1_queue_for_l2(l1s.pm.msg);
103 /* allocate a new msgb and initialize header */
104 l1s.pm.msg = l1ctl_msgb_alloc(L1CTL_PM_CONF);
107 pmr = (struct l1ctl_pm_conf *) msgb_put(l1s.pm.msg, sizeof(*pmr));
108 pmr->band_arfcn = htons(arfcn);
109 /* FIXME: do this as RxLev rather than DBM8 ? */
110 pmr->pm[0] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[0])/8);
111 if (num_meas > 1)
112 pmr->pm[1] = dbm2rxlev(agc_inp_dbm8_by_pm(pm_level[1])/8);
113 else
114 pmr->pm[1] = 0;
116 if (l1s.pm.mode == 1) {
117 if (l1s.pm.range.arfcn_next != l1s.pm.range.arfcn_end) {
118 /* schedule PM for next ARFCN in range */
119 l1s.pm.range.arfcn_next =
120 (l1s.pm.range.arfcn_next+1) & 0xfbff;
121 l1s_pm_test(1, l1s.pm.range.arfcn_next);
122 } else {
123 /* we have finished, flush the msgb to L2 */
124 struct l1ctl_hdr *l1h;
125 l1h = (struct l1ctl_hdr *) l1s.pm.msg->l1h;
126 l1h->flags |= L1CTL_F_DONE;
127 l1_queue_for_l2(l1s.pm.msg);
128 l1s.pm.msg = NULL;
132 return 0;
135 static const struct tdma_sched_item pm_sched_set[] = {
136 SCHED_ITEM_DT(l1s_pm_cmd, 0, 1, 0), SCHED_END_FRAME(),
137 SCHED_END_FRAME(),
138 SCHED_ITEM(l1s_pm_resp, -4, 1, 0), SCHED_END_FRAME(),
139 SCHED_END_SET()
142 /* Schedule a power measurement test */
143 void l1s_pm_test(uint8_t base_fn, uint16_t arfcn)
145 unsigned long flags;
147 printd("l1s_pm_test(%u, %u)\n", base_fn, arfcn);
149 local_firq_save(flags);
150 tdma_schedule_set(base_fn, pm_sched_set, arfcn);
151 local_irq_restore(flags);
155 * perform measurements of neighbour cells
158 /* scheduler callback to issue a power measurement task to the DSP */
159 static int l1s_neigh_pm_cmd(uint8_t num_meas,
160 __unused uint8_t p2, __unused uint16_t p3)
162 uint8_t last_gain = rffe_get_gain();
164 dsp_api.db_w->d_task_md = num_meas; /* number of measurements */
165 // dsp_api.ndb->d_fb_mode = 0; /* wideband search */
167 /* Tell the RF frontend to set the gain appropriately (keep last) */
168 rffe_compute_gain(-85, CAL_DSP_TGT_BB_LVL);
170 /* Program TPU */
171 /* FIXME: RXWIN_PW needs to set up multiple times in case
172 * num_meas > 1 */
173 /* do measurement dummy, in case l1s.neigh_pm.n == 0 */
174 l1s_rx_win_ctrl((l1s.neigh_pm.n) ?
175 l1s.neigh_pm.band_arfcn[l1s.neigh_pm.pos] : 0,
176 L1_RXWIN_PW, l1s.neigh_pm.tn[l1s.neigh_pm.pos]);
178 /* restore last gain */
179 rffe_set_gain(last_gain);
181 l1s.neigh_pm.running = 1;
183 return 0;
186 /* scheduler callback to read power measurement resposnse from the DSP */
187 static int l1s_neigh_pm_resp(__unused uint8_t p1, __unused uint8_t p2,
188 __unused uint16_t p3)
190 uint16_t dbm;
191 uint8_t level;
193 dsp_api.r_page_used = 1;
195 if (l1s.neigh_pm.n == 0 || !l1s.neigh_pm.running)
196 goto out;
198 dbm = (uint16_t) ((dsp_api.db_r->a_pm[0] & 0xffff) >> 3);
199 level = dbm2rxlev(agc_inp_dbm8_by_pm(dbm)/8);
201 l1s.neigh_pm.level[l1s.neigh_pm.pos] = level;
203 if (++l1s.neigh_pm.pos >= l1s.neigh_pm.n) {
204 struct msgb *msg;
205 struct l1ctl_neigh_pm_ind *mi;
206 int i;
208 l1s.neigh_pm.pos = 0;
209 /* return result */
210 msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND);
211 for (i = 0; i < l1s.neigh_pm.n; i++) {
212 if (msgb_tailroom(msg) < (int) sizeof(*mi)) {
213 l1_queue_for_l2(msg);
214 msg = l1ctl_msgb_alloc(L1CTL_NEIGH_PM_IND);
216 mi = (struct l1ctl_neigh_pm_ind *)
217 msgb_put(msg, sizeof(*mi));
218 mi->band_arfcn = htons(l1s.neigh_pm.band_arfcn[i]);
219 mi->tn = l1s.neigh_pm.tn[i];
220 mi->pm[0] = l1s.neigh_pm.level[i];
221 mi->pm[1] = 0;
223 l1_queue_for_l2(msg);
226 out:
227 l1s.neigh_pm.running = 0;
229 return 0;
232 const struct tdma_sched_item neigh_pm_sched_set[] = {
233 SCHED_ITEM_DT(l1s_neigh_pm_cmd, 0, 1, 0), SCHED_END_FRAME(),
234 SCHED_END_FRAME(),
235 SCHED_ITEM(l1s_neigh_pm_resp, -4, 1, 0), SCHED_END_FRAME(),
236 SCHED_END_SET()