1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* ZD1211 USB-WLAN driver for Linux |
3 | * |
4 | * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de> |
5 | * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org> |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/slab.h> |
10 | |
11 | #include "zd_rf.h" |
12 | #include "zd_usb.h" |
13 | #include "zd_chip.h" |
14 | |
15 | /* This RF programming code is based upon the code found in v2.16.0.0 of the |
16 | * ZyDAS vendor driver. Unlike other RF's, Ubec publish full technical specs |
17 | * for this RF on their website, so we're able to understand more than |
18 | * usual as to what is going on. Thumbs up for Ubec for doing that. */ |
19 | |
20 | /* The 3-wire serial interface provides access to 8 write-only registers. |
21 | * The data format is a 4 bit register address followed by a 20 bit value. */ |
22 | #define UW2453_REGWRITE(reg, val) ((((reg) & 0xf) << 20) | ((val) & 0xfffff)) |
23 | |
24 | /* For channel tuning, we have to configure registers 1 (synthesizer), 2 (synth |
25 | * fractional divide ratio) and 3 (VCO config). |
26 | * |
27 | * We configure the RF to produce an interrupt when the PLL is locked onto |
28 | * the configured frequency. During initialization, we run through a variety |
29 | * of different VCO configurations on channel 1 until we detect a PLL lock. |
30 | * When this happens, we remember which VCO configuration produced the lock |
31 | * and use it later. Actually, we use the configuration *after* the one that |
32 | * produced the lock, which seems odd, but it works. |
33 | * |
34 | * If we do not see a PLL lock on any standard VCO config, we fall back on an |
35 | * autocal configuration, which has a fixed (as opposed to per-channel) VCO |
36 | * config and different synth values from the standard set (divide ratio |
37 | * is still shared with the standard set). */ |
38 | |
39 | /* The per-channel synth values for all standard VCO configurations. These get |
40 | * written to register 1. */ |
41 | static const u8 uw2453_std_synth[] = { |
42 | RF_CHANNEL( 1) = 0x47, |
43 | RF_CHANNEL( 2) = 0x47, |
44 | RF_CHANNEL( 3) = 0x67, |
45 | RF_CHANNEL( 4) = 0x67, |
46 | RF_CHANNEL( 5) = 0x67, |
47 | RF_CHANNEL( 6) = 0x67, |
48 | RF_CHANNEL( 7) = 0x57, |
49 | RF_CHANNEL( 8) = 0x57, |
50 | RF_CHANNEL( 9) = 0x57, |
51 | RF_CHANNEL(10) = 0x57, |
52 | RF_CHANNEL(11) = 0x77, |
53 | RF_CHANNEL(12) = 0x77, |
54 | RF_CHANNEL(13) = 0x77, |
55 | RF_CHANNEL(14) = 0x4f, |
56 | }; |
57 | |
58 | /* This table stores the synthesizer fractional divide ratio for *all* VCO |
59 | * configurations (both standard and autocal). These get written to register 2. |
60 | */ |
61 | static const u16 uw2453_synth_divide[] = { |
62 | RF_CHANNEL( 1) = 0x999, |
63 | RF_CHANNEL( 2) = 0x99b, |
64 | RF_CHANNEL( 3) = 0x998, |
65 | RF_CHANNEL( 4) = 0x99a, |
66 | RF_CHANNEL( 5) = 0x999, |
67 | RF_CHANNEL( 6) = 0x99b, |
68 | RF_CHANNEL( 7) = 0x998, |
69 | RF_CHANNEL( 8) = 0x99a, |
70 | RF_CHANNEL( 9) = 0x999, |
71 | RF_CHANNEL(10) = 0x99b, |
72 | RF_CHANNEL(11) = 0x998, |
73 | RF_CHANNEL(12) = 0x99a, |
74 | RF_CHANNEL(13) = 0x999, |
75 | RF_CHANNEL(14) = 0xccc, |
76 | }; |
77 | |
78 | /* Here is the data for all the standard VCO configurations. We shrink our |
79 | * table a little by observing that both channels in a consecutive pair share |
80 | * the same value. We also observe that the high 4 bits ([0:3] in the specs) |
81 | * are all 'Reserved' and are always set to 0x4 - we chop them off in the data |
82 | * below. */ |
83 | #define CHAN_TO_PAIRIDX(a) ((a - 1) / 2) |
84 | #define RF_CHANPAIR(a,b) [CHAN_TO_PAIRIDX(a)] |
85 | static const u16 uw2453_std_vco_cfg[][7] = { |
86 | { /* table 1 */ |
87 | RF_CHANPAIR( 1, 2) = 0x664d, |
88 | RF_CHANPAIR( 3, 4) = 0x604d, |
89 | RF_CHANPAIR( 5, 6) = 0x6675, |
90 | RF_CHANPAIR( 7, 8) = 0x6475, |
91 | RF_CHANPAIR( 9, 10) = 0x6655, |
92 | RF_CHANPAIR(11, 12) = 0x6455, |
93 | RF_CHANPAIR(13, 14) = 0x6665, |
94 | }, |
95 | { /* table 2 */ |
96 | RF_CHANPAIR( 1, 2) = 0x666d, |
97 | RF_CHANPAIR( 3, 4) = 0x606d, |
98 | RF_CHANPAIR( 5, 6) = 0x664d, |
99 | RF_CHANPAIR( 7, 8) = 0x644d, |
100 | RF_CHANPAIR( 9, 10) = 0x6675, |
101 | RF_CHANPAIR(11, 12) = 0x6475, |
102 | RF_CHANPAIR(13, 14) = 0x6655, |
103 | }, |
104 | { /* table 3 */ |
105 | RF_CHANPAIR( 1, 2) = 0x665d, |
106 | RF_CHANPAIR( 3, 4) = 0x605d, |
107 | RF_CHANPAIR( 5, 6) = 0x666d, |
108 | RF_CHANPAIR( 7, 8) = 0x646d, |
109 | RF_CHANPAIR( 9, 10) = 0x664d, |
110 | RF_CHANPAIR(11, 12) = 0x644d, |
111 | RF_CHANPAIR(13, 14) = 0x6675, |
112 | }, |
113 | { /* table 4 */ |
114 | RF_CHANPAIR( 1, 2) = 0x667d, |
115 | RF_CHANPAIR( 3, 4) = 0x607d, |
116 | RF_CHANPAIR( 5, 6) = 0x665d, |
117 | RF_CHANPAIR( 7, 8) = 0x645d, |
118 | RF_CHANPAIR( 9, 10) = 0x666d, |
119 | RF_CHANPAIR(11, 12) = 0x646d, |
120 | RF_CHANPAIR(13, 14) = 0x664d, |
121 | }, |
122 | { /* table 5 */ |
123 | RF_CHANPAIR( 1, 2) = 0x6643, |
124 | RF_CHANPAIR( 3, 4) = 0x6043, |
125 | RF_CHANPAIR( 5, 6) = 0x667d, |
126 | RF_CHANPAIR( 7, 8) = 0x647d, |
127 | RF_CHANPAIR( 9, 10) = 0x665d, |
128 | RF_CHANPAIR(11, 12) = 0x645d, |
129 | RF_CHANPAIR(13, 14) = 0x666d, |
130 | }, |
131 | { /* table 6 */ |
132 | RF_CHANPAIR( 1, 2) = 0x6663, |
133 | RF_CHANPAIR( 3, 4) = 0x6063, |
134 | RF_CHANPAIR( 5, 6) = 0x6643, |
135 | RF_CHANPAIR( 7, 8) = 0x6443, |
136 | RF_CHANPAIR( 9, 10) = 0x667d, |
137 | RF_CHANPAIR(11, 12) = 0x647d, |
138 | RF_CHANPAIR(13, 14) = 0x665d, |
139 | }, |
140 | { /* table 7 */ |
141 | RF_CHANPAIR( 1, 2) = 0x6653, |
142 | RF_CHANPAIR( 3, 4) = 0x6053, |
143 | RF_CHANPAIR( 5, 6) = 0x6663, |
144 | RF_CHANPAIR( 7, 8) = 0x6463, |
145 | RF_CHANPAIR( 9, 10) = 0x6643, |
146 | RF_CHANPAIR(11, 12) = 0x6443, |
147 | RF_CHANPAIR(13, 14) = 0x667d, |
148 | }, |
149 | { /* table 8 */ |
150 | RF_CHANPAIR( 1, 2) = 0x6673, |
151 | RF_CHANPAIR( 3, 4) = 0x6073, |
152 | RF_CHANPAIR( 5, 6) = 0x6653, |
153 | RF_CHANPAIR( 7, 8) = 0x6453, |
154 | RF_CHANPAIR( 9, 10) = 0x6663, |
155 | RF_CHANPAIR(11, 12) = 0x6463, |
156 | RF_CHANPAIR(13, 14) = 0x6643, |
157 | }, |
158 | { /* table 9 */ |
159 | RF_CHANPAIR( 1, 2) = 0x664b, |
160 | RF_CHANPAIR( 3, 4) = 0x604b, |
161 | RF_CHANPAIR( 5, 6) = 0x6673, |
162 | RF_CHANPAIR( 7, 8) = 0x6473, |
163 | RF_CHANPAIR( 9, 10) = 0x6653, |
164 | RF_CHANPAIR(11, 12) = 0x6453, |
165 | RF_CHANPAIR(13, 14) = 0x6663, |
166 | }, |
167 | { /* table 10 */ |
168 | RF_CHANPAIR( 1, 2) = 0x666b, |
169 | RF_CHANPAIR( 3, 4) = 0x606b, |
170 | RF_CHANPAIR( 5, 6) = 0x664b, |
171 | RF_CHANPAIR( 7, 8) = 0x644b, |
172 | RF_CHANPAIR( 9, 10) = 0x6673, |
173 | RF_CHANPAIR(11, 12) = 0x6473, |
174 | RF_CHANPAIR(13, 14) = 0x6653, |
175 | }, |
176 | { /* table 11 */ |
177 | RF_CHANPAIR( 1, 2) = 0x665b, |
178 | RF_CHANPAIR( 3, 4) = 0x605b, |
179 | RF_CHANPAIR( 5, 6) = 0x666b, |
180 | RF_CHANPAIR( 7, 8) = 0x646b, |
181 | RF_CHANPAIR( 9, 10) = 0x664b, |
182 | RF_CHANPAIR(11, 12) = 0x644b, |
183 | RF_CHANPAIR(13, 14) = 0x6673, |
184 | }, |
185 | |
186 | }; |
187 | |
188 | /* The per-channel synth values for autocal. These get written to register 1. */ |
189 | static const u16 uw2453_autocal_synth[] = { |
190 | RF_CHANNEL( 1) = 0x6847, |
191 | RF_CHANNEL( 2) = 0x6847, |
192 | RF_CHANNEL( 3) = 0x6867, |
193 | RF_CHANNEL( 4) = 0x6867, |
194 | RF_CHANNEL( 5) = 0x6867, |
195 | RF_CHANNEL( 6) = 0x6867, |
196 | RF_CHANNEL( 7) = 0x6857, |
197 | RF_CHANNEL( 8) = 0x6857, |
198 | RF_CHANNEL( 9) = 0x6857, |
199 | RF_CHANNEL(10) = 0x6857, |
200 | RF_CHANNEL(11) = 0x6877, |
201 | RF_CHANNEL(12) = 0x6877, |
202 | RF_CHANNEL(13) = 0x6877, |
203 | RF_CHANNEL(14) = 0x684f, |
204 | }; |
205 | |
206 | /* The VCO configuration for autocal (all channels) */ |
207 | static const u16 UW2453_AUTOCAL_VCO_CFG = 0x6662; |
208 | |
209 | /* TX gain settings. The array index corresponds to the TX power integration |
210 | * values found in the EEPROM. The values get written to register 7. */ |
211 | static u32 uw2453_txgain[] = { |
212 | [0x00] = 0x0e313, |
213 | [0x01] = 0x0fb13, |
214 | [0x02] = 0x0e093, |
215 | [0x03] = 0x0f893, |
216 | [0x04] = 0x0ea93, |
217 | [0x05] = 0x1f093, |
218 | [0x06] = 0x1f493, |
219 | [0x07] = 0x1f693, |
220 | [0x08] = 0x1f393, |
221 | [0x09] = 0x1f35b, |
222 | [0x0a] = 0x1e6db, |
223 | [0x0b] = 0x1ff3f, |
224 | [0x0c] = 0x1ffff, |
225 | [0x0d] = 0x361d7, |
226 | [0x0e] = 0x37fbf, |
227 | [0x0f] = 0x3ff8b, |
228 | [0x10] = 0x3ff33, |
229 | [0x11] = 0x3fb3f, |
230 | [0x12] = 0x3ffff, |
231 | }; |
232 | |
233 | /* RF-specific structure */ |
234 | struct uw2453_priv { |
235 | /* index into synth/VCO config tables where PLL lock was found |
236 | * -1 means autocal */ |
237 | int config; |
238 | }; |
239 | |
240 | #define UW2453_PRIV(rf) ((struct uw2453_priv *) (rf)->priv) |
241 | |
242 | static int uw2453_synth_set_channel(struct zd_chip *chip, int channel, |
243 | bool autocal) |
244 | { |
245 | int r; |
246 | int idx = channel - 1; |
247 | u32 val; |
248 | |
249 | if (autocal) |
250 | val = UW2453_REGWRITE(1, uw2453_autocal_synth[idx]); |
251 | else |
252 | val = UW2453_REGWRITE(1, uw2453_std_synth[idx]); |
253 | |
254 | r = zd_rfwrite_locked(chip, value: val, bits: RF_RV_BITS); |
255 | if (r) |
256 | return r; |
257 | |
258 | return zd_rfwrite_locked(chip, |
259 | UW2453_REGWRITE(2, uw2453_synth_divide[idx]), bits: RF_RV_BITS); |
260 | } |
261 | |
262 | static int uw2453_write_vco_cfg(struct zd_chip *chip, u16 value) |
263 | { |
264 | /* vendor driver always sets these upper bits even though the specs say |
265 | * they are reserved */ |
266 | u32 val = 0x40000 | value; |
267 | return zd_rfwrite_locked(chip, UW2453_REGWRITE(3, val), bits: RF_RV_BITS); |
268 | } |
269 | |
270 | static int uw2453_init_mode(struct zd_chip *chip) |
271 | { |
272 | static const u32 rv[] = { |
273 | UW2453_REGWRITE(0, 0x25f98), /* enter IDLE mode */ |
274 | UW2453_REGWRITE(0, 0x25f9a), /* enter CAL_VCO mode */ |
275 | UW2453_REGWRITE(0, 0x25f94), /* enter RX/TX mode */ |
276 | UW2453_REGWRITE(0, 0x27fd4), /* power down RSSI circuit */ |
277 | }; |
278 | |
279 | return zd_rfwritev_locked(chip, values: rv, ARRAY_SIZE(rv), bits: RF_RV_BITS); |
280 | } |
281 | |
282 | static int uw2453_set_tx_gain_level(struct zd_chip *chip, int channel) |
283 | { |
284 | u8 int_value = chip->pwr_int_values[channel - 1]; |
285 | |
286 | if (int_value >= ARRAY_SIZE(uw2453_txgain)) { |
287 | dev_dbg_f(zd_chip_dev(chip), "can't configure TX gain for " |
288 | "int value %x on channel %d\n" , int_value, channel); |
289 | return 0; |
290 | } |
291 | |
292 | return zd_rfwrite_locked(chip, |
293 | UW2453_REGWRITE(7, uw2453_txgain[int_value]), bits: RF_RV_BITS); |
294 | } |
295 | |
296 | static int uw2453_init_hw(struct zd_rf *rf) |
297 | { |
298 | int i, r; |
299 | int found_config = -1; |
300 | u16 intr_status; |
301 | struct zd_chip *chip = zd_rf_to_chip(rf); |
302 | |
303 | static const struct zd_ioreq16 ioreqs[] = { |
304 | { ZD_CR10, 0x89 }, { ZD_CR15, 0x20 }, |
305 | { ZD_CR17, 0x28 }, /* 6112 no change */ |
306 | { ZD_CR23, 0x38 }, { ZD_CR24, 0x20 }, { ZD_CR26, 0x93 }, |
307 | { ZD_CR27, 0x15 }, { ZD_CR28, 0x3e }, { ZD_CR29, 0x00 }, |
308 | { ZD_CR33, 0x28 }, { ZD_CR34, 0x30 }, |
309 | { ZD_CR35, 0x43 }, /* 6112 3e->43 */ |
310 | { ZD_CR41, 0x24 }, { ZD_CR44, 0x32 }, |
311 | { ZD_CR46, 0x92 }, /* 6112 96->92 */ |
312 | { ZD_CR47, 0x1e }, |
313 | { ZD_CR48, 0x04 }, /* 5602 Roger */ |
314 | { ZD_CR49, 0xfa }, { ZD_CR79, 0x58 }, { ZD_CR80, 0x30 }, |
315 | { ZD_CR81, 0x30 }, { ZD_CR87, 0x0a }, { ZD_CR89, 0x04 }, |
316 | { ZD_CR91, 0x00 }, { ZD_CR92, 0x0a }, { ZD_CR98, 0x8d }, |
317 | { ZD_CR99, 0x28 }, { ZD_CR100, 0x02 }, |
318 | { ZD_CR101, 0x09 }, /* 6112 13->1f 6220 1f->13 6407 13->9 */ |
319 | { ZD_CR102, 0x27 }, |
320 | { ZD_CR106, 0x1c }, /* 5d07 5112 1f->1c 6220 1c->1f |
321 | * 6221 1f->1c |
322 | */ |
323 | { ZD_CR107, 0x1c }, /* 6220 1c->1a 5221 1a->1c */ |
324 | { ZD_CR109, 0x13 }, |
325 | { ZD_CR110, 0x1f }, /* 6112 13->1f 6221 1f->13 6407 13->0x09 */ |
326 | { ZD_CR111, 0x13 }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 }, |
327 | { ZD_CR114, 0x23 }, /* 6221 27->23 */ |
328 | { ZD_CR115, 0x24 }, /* 6112 24->1c 6220 1c->24 */ |
329 | { ZD_CR116, 0x24 }, /* 6220 1c->24 */ |
330 | { ZD_CR117, 0xfa }, /* 6112 fa->f8 6220 f8->f4 6220 f4->fa */ |
331 | { ZD_CR118, 0xf0 }, /* 5d07 6112 f0->f2 6220 f2->f0 */ |
332 | { ZD_CR119, 0x1a }, /* 6112 1a->10 6220 10->14 6220 14->1a */ |
333 | { ZD_CR120, 0x4f }, |
334 | { ZD_CR121, 0x1f }, /* 6220 4f->1f */ |
335 | { ZD_CR122, 0xf0 }, { ZD_CR123, 0x57 }, { ZD_CR125, 0xad }, |
336 | { ZD_CR126, 0x6c }, { ZD_CR127, 0x03 }, |
337 | { ZD_CR128, 0x14 }, /* 6302 12->11 */ |
338 | { ZD_CR129, 0x12 }, /* 6301 10->0f */ |
339 | { ZD_CR130, 0x10 }, { ZD_CR137, 0x50 }, { ZD_CR138, 0xa8 }, |
340 | { ZD_CR144, 0xac }, { ZD_CR146, 0x20 }, { ZD_CR252, 0xff }, |
341 | { ZD_CR253, 0xff }, |
342 | }; |
343 | |
344 | static const u32 rv[] = { |
345 | UW2453_REGWRITE(4, 0x2b), /* configure receiver gain */ |
346 | UW2453_REGWRITE(5, 0x19e4f), /* configure transmitter gain */ |
347 | UW2453_REGWRITE(6, 0xf81ad), /* enable RX/TX filter tuning */ |
348 | UW2453_REGWRITE(7, 0x3fffe), /* disable TX gain in test mode */ |
349 | |
350 | /* enter CAL_FIL mode, TX gain set by registers, RX gain set by pins, |
351 | * RSSI circuit powered down, reduced RSSI range */ |
352 | UW2453_REGWRITE(0, 0x25f9c), /* 5d01 cal_fil */ |
353 | |
354 | /* synthesizer configuration for channel 1 */ |
355 | UW2453_REGWRITE(1, 0x47), |
356 | UW2453_REGWRITE(2, 0x999), |
357 | |
358 | /* disable manual VCO band selection */ |
359 | UW2453_REGWRITE(3, 0x7602), |
360 | |
361 | /* enable manual VCO band selection, configure current level */ |
362 | UW2453_REGWRITE(3, 0x46063), |
363 | }; |
364 | |
365 | r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); |
366 | if (r) |
367 | return r; |
368 | |
369 | r = zd_rfwritev_locked(chip, values: rv, ARRAY_SIZE(rv), bits: RF_RV_BITS); |
370 | if (r) |
371 | return r; |
372 | |
373 | r = uw2453_init_mode(chip); |
374 | if (r) |
375 | return r; |
376 | |
377 | /* Try all standard VCO configuration settings on channel 1 */ |
378 | for (i = 0; i < ARRAY_SIZE(uw2453_std_vco_cfg) - 1; i++) { |
379 | /* Configure synthesizer for channel 1 */ |
380 | r = uw2453_synth_set_channel(chip, channel: 1, autocal: false); |
381 | if (r) |
382 | return r; |
383 | |
384 | /* Write VCO config */ |
385 | r = uw2453_write_vco_cfg(chip, value: uw2453_std_vco_cfg[i][0]); |
386 | if (r) |
387 | return r; |
388 | |
389 | /* ack interrupt event */ |
390 | r = zd_iowrite16_locked(chip, value: 0x0f, UW2453_INTR_REG); |
391 | if (r) |
392 | return r; |
393 | |
394 | /* check interrupt status */ |
395 | r = zd_ioread16_locked(chip, value: &intr_status, UW2453_INTR_REG); |
396 | if (r) |
397 | return r; |
398 | |
399 | if (!(intr_status & 0xf)) { |
400 | dev_dbg_f(zd_chip_dev(chip), |
401 | "PLL locked on configuration %d\n" , i); |
402 | found_config = i; |
403 | break; |
404 | } |
405 | } |
406 | |
407 | if (found_config == -1) { |
408 | /* autocal */ |
409 | dev_dbg_f(zd_chip_dev(chip), |
410 | "PLL did not lock, using autocal\n" ); |
411 | |
412 | r = uw2453_synth_set_channel(chip, channel: 1, autocal: true); |
413 | if (r) |
414 | return r; |
415 | |
416 | r = uw2453_write_vco_cfg(chip, value: UW2453_AUTOCAL_VCO_CFG); |
417 | if (r) |
418 | return r; |
419 | } |
420 | |
421 | /* To match the vendor driver behaviour, we use the configuration after |
422 | * the one that produced a lock. */ |
423 | UW2453_PRIV(rf)->config = found_config + 1; |
424 | |
425 | return zd_iowrite16_locked(chip, value: 0x06, ZD_CR203); |
426 | } |
427 | |
428 | static int uw2453_set_channel(struct zd_rf *rf, u8 channel) |
429 | { |
430 | int r; |
431 | u16 vco_cfg; |
432 | int config = UW2453_PRIV(rf)->config; |
433 | bool autocal = (config == -1); |
434 | struct zd_chip *chip = zd_rf_to_chip(rf); |
435 | |
436 | static const struct zd_ioreq16 ioreqs[] = { |
437 | { ZD_CR80, 0x30 }, { ZD_CR81, 0x30 }, { ZD_CR79, 0x58 }, |
438 | { ZD_CR12, 0xf0 }, { ZD_CR77, 0x1b }, { ZD_CR78, 0x58 }, |
439 | }; |
440 | |
441 | r = uw2453_synth_set_channel(chip, channel, autocal); |
442 | if (r) |
443 | return r; |
444 | |
445 | if (autocal) |
446 | vco_cfg = UW2453_AUTOCAL_VCO_CFG; |
447 | else |
448 | vco_cfg = uw2453_std_vco_cfg[config][CHAN_TO_PAIRIDX(channel)]; |
449 | |
450 | r = uw2453_write_vco_cfg(chip, value: vco_cfg); |
451 | if (r) |
452 | return r; |
453 | |
454 | r = uw2453_init_mode(chip); |
455 | if (r) |
456 | return r; |
457 | |
458 | r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); |
459 | if (r) |
460 | return r; |
461 | |
462 | r = uw2453_set_tx_gain_level(chip, channel); |
463 | if (r) |
464 | return r; |
465 | |
466 | return zd_iowrite16_locked(chip, value: 0x06, ZD_CR203); |
467 | } |
468 | |
469 | static int uw2453_switch_radio_on(struct zd_rf *rf) |
470 | { |
471 | int r; |
472 | struct zd_chip *chip = zd_rf_to_chip(rf); |
473 | struct zd_ioreq16 ioreqs[] = { |
474 | { ZD_CR11, 0x00 }, { ZD_CR251, 0x3f }, |
475 | }; |
476 | |
477 | /* enter RXTX mode */ |
478 | r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f94), bits: RF_RV_BITS); |
479 | if (r) |
480 | return r; |
481 | |
482 | if (zd_chip_is_zd1211b(chip)) |
483 | ioreqs[1].value = 0x7f; |
484 | |
485 | return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); |
486 | } |
487 | |
488 | static int uw2453_switch_radio_off(struct zd_rf *rf) |
489 | { |
490 | int r; |
491 | struct zd_chip *chip = zd_rf_to_chip(rf); |
492 | static const struct zd_ioreq16 ioreqs[] = { |
493 | { ZD_CR11, 0x04 }, { ZD_CR251, 0x2f }, |
494 | }; |
495 | |
496 | /* enter IDLE mode */ |
497 | /* FIXME: shouldn't we go to SLEEP? sent email to zydas */ |
498 | r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f90), bits: RF_RV_BITS); |
499 | if (r) |
500 | return r; |
501 | |
502 | return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); |
503 | } |
504 | |
505 | static void uw2453_clear(struct zd_rf *rf) |
506 | { |
507 | kfree(objp: rf->priv); |
508 | } |
509 | |
510 | int zd_rf_init_uw2453(struct zd_rf *rf) |
511 | { |
512 | rf->init_hw = uw2453_init_hw; |
513 | rf->set_channel = uw2453_set_channel; |
514 | rf->switch_radio_on = uw2453_switch_radio_on; |
515 | rf->switch_radio_off = uw2453_switch_radio_off; |
516 | rf->patch_6m_band_edge = zd_rf_generic_patch_6m; |
517 | rf->clear = uw2453_clear; |
518 | /* we have our own TX integration code */ |
519 | rf->update_channel_int = 0; |
520 | |
521 | rf->priv = kmalloc(size: sizeof(struct uw2453_priv), GFP_KERNEL); |
522 | if (rf->priv == NULL) |
523 | return -ENOMEM; |
524 | |
525 | return 0; |
526 | } |
527 | |
528 | |