Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* ZD1211 USB-WLAN driver for Linux
0003  *
0004  * Copyright (C) 2005-2007 Ulrich Kunitz <kune@deine-taler.de>
0005  * Copyright (C) 2006-2007 Daniel Drake <dsd@gentoo.org>
0006  */
0007 
0008 #include <linux/kernel.h>
0009 #include <linux/slab.h>
0010 
0011 #include "zd_rf.h"
0012 #include "zd_usb.h"
0013 #include "zd_chip.h"
0014 
0015 /* This RF programming code is based upon the code found in v2.16.0.0 of the
0016  * ZyDAS vendor driver. Unlike other RF's, Ubec publish full technical specs
0017  * for this RF on their website, so we're able to understand more than
0018  * usual as to what is going on. Thumbs up for Ubec for doing that. */
0019 
0020 /* The 3-wire serial interface provides access to 8 write-only registers.
0021  * The data format is a 4 bit register address followed by a 20 bit value. */
0022 #define UW2453_REGWRITE(reg, val) ((((reg) & 0xf) << 20) | ((val) & 0xfffff))
0023 
0024 /* For channel tuning, we have to configure registers 1 (synthesizer), 2 (synth
0025  * fractional divide ratio) and 3 (VCO config).
0026  *
0027  * We configure the RF to produce an interrupt when the PLL is locked onto
0028  * the configured frequency. During initialization, we run through a variety
0029  * of different VCO configurations on channel 1 until we detect a PLL lock.
0030  * When this happens, we remember which VCO configuration produced the lock
0031  * and use it later. Actually, we use the configuration *after* the one that
0032  * produced the lock, which seems odd, but it works.
0033  *
0034  * If we do not see a PLL lock on any standard VCO config, we fall back on an
0035  * autocal configuration, which has a fixed (as opposed to per-channel) VCO
0036  * config and different synth values from the standard set (divide ratio
0037  * is still shared with the standard set). */
0038 
0039 /* The per-channel synth values for all standard VCO configurations. These get
0040  * written to register 1. */
0041 static const u8 uw2453_std_synth[] = {
0042     RF_CHANNEL( 1) = 0x47,
0043     RF_CHANNEL( 2) = 0x47,
0044     RF_CHANNEL( 3) = 0x67,
0045     RF_CHANNEL( 4) = 0x67,
0046     RF_CHANNEL( 5) = 0x67,
0047     RF_CHANNEL( 6) = 0x67,
0048     RF_CHANNEL( 7) = 0x57,
0049     RF_CHANNEL( 8) = 0x57,
0050     RF_CHANNEL( 9) = 0x57,
0051     RF_CHANNEL(10) = 0x57,
0052     RF_CHANNEL(11) = 0x77,
0053     RF_CHANNEL(12) = 0x77,
0054     RF_CHANNEL(13) = 0x77,
0055     RF_CHANNEL(14) = 0x4f,
0056 };
0057 
0058 /* This table stores the synthesizer fractional divide ratio for *all* VCO
0059  * configurations (both standard and autocal). These get written to register 2.
0060  */
0061 static const u16 uw2453_synth_divide[] = {
0062     RF_CHANNEL( 1) = 0x999,
0063     RF_CHANNEL( 2) = 0x99b,
0064     RF_CHANNEL( 3) = 0x998,
0065     RF_CHANNEL( 4) = 0x99a,
0066     RF_CHANNEL( 5) = 0x999,
0067     RF_CHANNEL( 6) = 0x99b,
0068     RF_CHANNEL( 7) = 0x998,
0069     RF_CHANNEL( 8) = 0x99a,
0070     RF_CHANNEL( 9) = 0x999,
0071     RF_CHANNEL(10) = 0x99b,
0072     RF_CHANNEL(11) = 0x998,
0073     RF_CHANNEL(12) = 0x99a,
0074     RF_CHANNEL(13) = 0x999,
0075     RF_CHANNEL(14) = 0xccc,
0076 };
0077 
0078 /* Here is the data for all the standard VCO configurations. We shrink our
0079  * table a little by observing that both channels in a consecutive pair share
0080  * the same value. We also observe that the high 4 bits ([0:3] in the specs)
0081  * are all 'Reserved' and are always set to 0x4 - we chop them off in the data
0082  * below. */
0083 #define CHAN_TO_PAIRIDX(a) ((a - 1) / 2)
0084 #define RF_CHANPAIR(a,b) [CHAN_TO_PAIRIDX(a)]
0085 static const u16 uw2453_std_vco_cfg[][7] = {
0086     { /* table 1 */
0087         RF_CHANPAIR( 1,  2) = 0x664d,
0088         RF_CHANPAIR( 3,  4) = 0x604d,
0089         RF_CHANPAIR( 5,  6) = 0x6675,
0090         RF_CHANPAIR( 7,  8) = 0x6475,
0091         RF_CHANPAIR( 9, 10) = 0x6655,
0092         RF_CHANPAIR(11, 12) = 0x6455,
0093         RF_CHANPAIR(13, 14) = 0x6665,
0094     },
0095     { /* table 2 */
0096         RF_CHANPAIR( 1,  2) = 0x666d,
0097         RF_CHANPAIR( 3,  4) = 0x606d,
0098         RF_CHANPAIR( 5,  6) = 0x664d,
0099         RF_CHANPAIR( 7,  8) = 0x644d,
0100         RF_CHANPAIR( 9, 10) = 0x6675,
0101         RF_CHANPAIR(11, 12) = 0x6475,
0102         RF_CHANPAIR(13, 14) = 0x6655,
0103     },
0104     { /* table 3 */
0105         RF_CHANPAIR( 1,  2) = 0x665d,
0106         RF_CHANPAIR( 3,  4) = 0x605d,
0107         RF_CHANPAIR( 5,  6) = 0x666d,
0108         RF_CHANPAIR( 7,  8) = 0x646d,
0109         RF_CHANPAIR( 9, 10) = 0x664d,
0110         RF_CHANPAIR(11, 12) = 0x644d,
0111         RF_CHANPAIR(13, 14) = 0x6675,
0112     },
0113     { /* table 4 */
0114         RF_CHANPAIR( 1,  2) = 0x667d,
0115         RF_CHANPAIR( 3,  4) = 0x607d,
0116         RF_CHANPAIR( 5,  6) = 0x665d,
0117         RF_CHANPAIR( 7,  8) = 0x645d,
0118         RF_CHANPAIR( 9, 10) = 0x666d,
0119         RF_CHANPAIR(11, 12) = 0x646d,
0120         RF_CHANPAIR(13, 14) = 0x664d,
0121     },
0122     { /* table 5 */
0123         RF_CHANPAIR( 1,  2) = 0x6643,
0124         RF_CHANPAIR( 3,  4) = 0x6043,
0125         RF_CHANPAIR( 5,  6) = 0x667d,
0126         RF_CHANPAIR( 7,  8) = 0x647d,
0127         RF_CHANPAIR( 9, 10) = 0x665d,
0128         RF_CHANPAIR(11, 12) = 0x645d,
0129         RF_CHANPAIR(13, 14) = 0x666d,
0130     },
0131     { /* table 6 */
0132         RF_CHANPAIR( 1,  2) = 0x6663,
0133         RF_CHANPAIR( 3,  4) = 0x6063,
0134         RF_CHANPAIR( 5,  6) = 0x6643,
0135         RF_CHANPAIR( 7,  8) = 0x6443,
0136         RF_CHANPAIR( 9, 10) = 0x667d,
0137         RF_CHANPAIR(11, 12) = 0x647d,
0138         RF_CHANPAIR(13, 14) = 0x665d,
0139     },
0140     { /* table 7 */
0141         RF_CHANPAIR( 1,  2) = 0x6653,
0142         RF_CHANPAIR( 3,  4) = 0x6053,
0143         RF_CHANPAIR( 5,  6) = 0x6663,
0144         RF_CHANPAIR( 7,  8) = 0x6463,
0145         RF_CHANPAIR( 9, 10) = 0x6643,
0146         RF_CHANPAIR(11, 12) = 0x6443,
0147         RF_CHANPAIR(13, 14) = 0x667d,
0148     },
0149     { /* table 8 */
0150         RF_CHANPAIR( 1,  2) = 0x6673,
0151         RF_CHANPAIR( 3,  4) = 0x6073,
0152         RF_CHANPAIR( 5,  6) = 0x6653,
0153         RF_CHANPAIR( 7,  8) = 0x6453,
0154         RF_CHANPAIR( 9, 10) = 0x6663,
0155         RF_CHANPAIR(11, 12) = 0x6463,
0156         RF_CHANPAIR(13, 14) = 0x6643,
0157     },
0158     { /* table 9 */
0159         RF_CHANPAIR( 1,  2) = 0x664b,
0160         RF_CHANPAIR( 3,  4) = 0x604b,
0161         RF_CHANPAIR( 5,  6) = 0x6673,
0162         RF_CHANPAIR( 7,  8) = 0x6473,
0163         RF_CHANPAIR( 9, 10) = 0x6653,
0164         RF_CHANPAIR(11, 12) = 0x6453,
0165         RF_CHANPAIR(13, 14) = 0x6663,
0166     },
0167     { /* table 10 */
0168         RF_CHANPAIR( 1,  2) = 0x666b,
0169         RF_CHANPAIR( 3,  4) = 0x606b,
0170         RF_CHANPAIR( 5,  6) = 0x664b,
0171         RF_CHANPAIR( 7,  8) = 0x644b,
0172         RF_CHANPAIR( 9, 10) = 0x6673,
0173         RF_CHANPAIR(11, 12) = 0x6473,
0174         RF_CHANPAIR(13, 14) = 0x6653,
0175     },
0176     { /* table 11 */
0177         RF_CHANPAIR( 1,  2) = 0x665b,
0178         RF_CHANPAIR( 3,  4) = 0x605b,
0179         RF_CHANPAIR( 5,  6) = 0x666b,
0180         RF_CHANPAIR( 7,  8) = 0x646b,
0181         RF_CHANPAIR( 9, 10) = 0x664b,
0182         RF_CHANPAIR(11, 12) = 0x644b,
0183         RF_CHANPAIR(13, 14) = 0x6673,
0184     },
0185 
0186 };
0187 
0188 /* The per-channel synth values for autocal. These get written to register 1. */
0189 static const u16 uw2453_autocal_synth[] = {
0190     RF_CHANNEL( 1) = 0x6847,
0191     RF_CHANNEL( 2) = 0x6847,
0192     RF_CHANNEL( 3) = 0x6867,
0193     RF_CHANNEL( 4) = 0x6867,
0194     RF_CHANNEL( 5) = 0x6867,
0195     RF_CHANNEL( 6) = 0x6867,
0196     RF_CHANNEL( 7) = 0x6857,
0197     RF_CHANNEL( 8) = 0x6857,
0198     RF_CHANNEL( 9) = 0x6857,
0199     RF_CHANNEL(10) = 0x6857,
0200     RF_CHANNEL(11) = 0x6877,
0201     RF_CHANNEL(12) = 0x6877,
0202     RF_CHANNEL(13) = 0x6877,
0203     RF_CHANNEL(14) = 0x684f,
0204 };
0205 
0206 /* The VCO configuration for autocal (all channels) */
0207 static const u16 UW2453_AUTOCAL_VCO_CFG = 0x6662;
0208 
0209 /* TX gain settings. The array index corresponds to the TX power integration
0210  * values found in the EEPROM. The values get written to register 7. */
0211 static u32 uw2453_txgain[] = {
0212     [0x00] = 0x0e313,
0213     [0x01] = 0x0fb13,
0214     [0x02] = 0x0e093,
0215     [0x03] = 0x0f893,
0216     [0x04] = 0x0ea93,
0217     [0x05] = 0x1f093,
0218     [0x06] = 0x1f493,
0219     [0x07] = 0x1f693,
0220     [0x08] = 0x1f393,
0221     [0x09] = 0x1f35b,
0222     [0x0a] = 0x1e6db,
0223     [0x0b] = 0x1ff3f,
0224     [0x0c] = 0x1ffff,
0225     [0x0d] = 0x361d7,
0226     [0x0e] = 0x37fbf,
0227     [0x0f] = 0x3ff8b,
0228     [0x10] = 0x3ff33,
0229     [0x11] = 0x3fb3f,
0230     [0x12] = 0x3ffff,
0231 };
0232 
0233 /* RF-specific structure */
0234 struct uw2453_priv {
0235     /* index into synth/VCO config tables where PLL lock was found
0236      * -1 means autocal */
0237     int config;
0238 };
0239 
0240 #define UW2453_PRIV(rf) ((struct uw2453_priv *) (rf)->priv)
0241 
0242 static int uw2453_synth_set_channel(struct zd_chip *chip, int channel,
0243     bool autocal)
0244 {
0245     int r;
0246     int idx = channel - 1;
0247     u32 val;
0248 
0249     if (autocal)
0250         val = UW2453_REGWRITE(1, uw2453_autocal_synth[idx]);
0251     else
0252         val = UW2453_REGWRITE(1, uw2453_std_synth[idx]);
0253 
0254     r = zd_rfwrite_locked(chip, val, RF_RV_BITS);
0255     if (r)
0256         return r;
0257 
0258     return zd_rfwrite_locked(chip,
0259         UW2453_REGWRITE(2, uw2453_synth_divide[idx]), RF_RV_BITS);
0260 }
0261 
0262 static int uw2453_write_vco_cfg(struct zd_chip *chip, u16 value)
0263 {
0264     /* vendor driver always sets these upper bits even though the specs say
0265      * they are reserved */
0266     u32 val = 0x40000 | value;
0267     return zd_rfwrite_locked(chip, UW2453_REGWRITE(3, val), RF_RV_BITS);
0268 }
0269 
0270 static int uw2453_init_mode(struct zd_chip *chip)
0271 {
0272     static const u32 rv[] = {
0273         UW2453_REGWRITE(0, 0x25f98), /* enter IDLE mode */
0274         UW2453_REGWRITE(0, 0x25f9a), /* enter CAL_VCO mode */
0275         UW2453_REGWRITE(0, 0x25f94), /* enter RX/TX mode */
0276         UW2453_REGWRITE(0, 0x27fd4), /* power down RSSI circuit */
0277     };
0278 
0279     return zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
0280 }
0281 
0282 static int uw2453_set_tx_gain_level(struct zd_chip *chip, int channel)
0283 {
0284     u8 int_value = chip->pwr_int_values[channel - 1];
0285 
0286     if (int_value >= ARRAY_SIZE(uw2453_txgain)) {
0287         dev_dbg_f(zd_chip_dev(chip), "can't configure TX gain for "
0288               "int value %x on channel %d\n", int_value, channel);
0289         return 0;
0290     }
0291 
0292     return zd_rfwrite_locked(chip,
0293         UW2453_REGWRITE(7, uw2453_txgain[int_value]), RF_RV_BITS);
0294 }
0295 
0296 static int uw2453_init_hw(struct zd_rf *rf)
0297 {
0298     int i, r;
0299     int found_config = -1;
0300     u16 intr_status;
0301     struct zd_chip *chip = zd_rf_to_chip(rf);
0302 
0303     static const struct zd_ioreq16 ioreqs[] = {
0304         { ZD_CR10,  0x89 }, { ZD_CR15,  0x20 },
0305         { ZD_CR17,  0x28 }, /* 6112 no change */
0306         { ZD_CR23,  0x38 }, { ZD_CR24,  0x20 }, { ZD_CR26,  0x93 },
0307         { ZD_CR27,  0x15 }, { ZD_CR28,  0x3e }, { ZD_CR29,  0x00 },
0308         { ZD_CR33,  0x28 }, { ZD_CR34,  0x30 },
0309         { ZD_CR35,  0x43 }, /* 6112 3e->43 */
0310         { ZD_CR41,  0x24 }, { ZD_CR44,  0x32 },
0311         { ZD_CR46,  0x92 }, /* 6112 96->92 */
0312         { ZD_CR47,  0x1e },
0313         { ZD_CR48,  0x04 }, /* 5602 Roger */
0314         { ZD_CR49,  0xfa }, { ZD_CR79,  0x58 }, { ZD_CR80,  0x30 },
0315         { ZD_CR81,  0x30 }, { ZD_CR87,  0x0a }, { ZD_CR89,  0x04 },
0316         { ZD_CR91,  0x00 }, { ZD_CR92,  0x0a }, { ZD_CR98,  0x8d },
0317         { ZD_CR99,  0x28 }, { ZD_CR100, 0x02 },
0318         { ZD_CR101, 0x09 }, /* 6112 13->1f 6220 1f->13 6407 13->9 */
0319         { ZD_CR102, 0x27 },
0320         { ZD_CR106, 0x1c }, /* 5d07 5112 1f->1c 6220 1c->1f
0321                      * 6221 1f->1c
0322                      */
0323         { ZD_CR107, 0x1c }, /* 6220 1c->1a 5221 1a->1c */
0324         { ZD_CR109, 0x13 },
0325         { ZD_CR110, 0x1f }, /* 6112 13->1f 6221 1f->13 6407 13->0x09 */
0326         { ZD_CR111, 0x13 }, { ZD_CR112, 0x1f }, { ZD_CR113, 0x27 },
0327         { ZD_CR114, 0x23 }, /* 6221 27->23 */
0328         { ZD_CR115, 0x24 }, /* 6112 24->1c 6220 1c->24 */
0329         { ZD_CR116, 0x24 }, /* 6220 1c->24 */
0330         { ZD_CR117, 0xfa }, /* 6112 fa->f8 6220 f8->f4 6220 f4->fa */
0331         { ZD_CR118, 0xf0 }, /* 5d07 6112 f0->f2 6220 f2->f0 */
0332         { ZD_CR119, 0x1a }, /* 6112 1a->10 6220 10->14 6220 14->1a */
0333         { ZD_CR120, 0x4f },
0334         { ZD_CR121, 0x1f }, /* 6220 4f->1f */
0335         { ZD_CR122, 0xf0 }, { ZD_CR123, 0x57 }, { ZD_CR125, 0xad },
0336         { ZD_CR126, 0x6c }, { ZD_CR127, 0x03 },
0337         { ZD_CR128, 0x14 }, /* 6302 12->11 */
0338         { ZD_CR129, 0x12 }, /* 6301 10->0f */
0339         { ZD_CR130, 0x10 }, { ZD_CR137, 0x50 }, { ZD_CR138, 0xa8 },
0340         { ZD_CR144, 0xac }, { ZD_CR146, 0x20 }, { ZD_CR252, 0xff },
0341         { ZD_CR253, 0xff },
0342     };
0343 
0344     static const u32 rv[] = {
0345         UW2453_REGWRITE(4, 0x2b),    /* configure receiver gain */
0346         UW2453_REGWRITE(5, 0x19e4f), /* configure transmitter gain */
0347         UW2453_REGWRITE(6, 0xf81ad), /* enable RX/TX filter tuning */
0348         UW2453_REGWRITE(7, 0x3fffe), /* disable TX gain in test mode */
0349 
0350         /* enter CAL_FIL mode, TX gain set by registers, RX gain set by pins,
0351          * RSSI circuit powered down, reduced RSSI range */
0352         UW2453_REGWRITE(0, 0x25f9c), /* 5d01 cal_fil */
0353 
0354         /* synthesizer configuration for channel 1 */
0355         UW2453_REGWRITE(1, 0x47),
0356         UW2453_REGWRITE(2, 0x999),
0357 
0358         /* disable manual VCO band selection */
0359         UW2453_REGWRITE(3, 0x7602),
0360 
0361         /* enable manual VCO band selection, configure current level */
0362         UW2453_REGWRITE(3, 0x46063),
0363     };
0364 
0365     r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
0366     if (r)
0367         return r;
0368 
0369     r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS);
0370     if (r)
0371         return r;
0372 
0373     r = uw2453_init_mode(chip);
0374     if (r)
0375         return r;
0376 
0377     /* Try all standard VCO configuration settings on channel 1 */
0378     for (i = 0; i < ARRAY_SIZE(uw2453_std_vco_cfg) - 1; i++) {
0379         /* Configure synthesizer for channel 1 */
0380         r = uw2453_synth_set_channel(chip, 1, false);
0381         if (r)
0382             return r;
0383 
0384         /* Write VCO config */
0385         r = uw2453_write_vco_cfg(chip, uw2453_std_vco_cfg[i][0]);
0386         if (r)
0387             return r;
0388 
0389         /* ack interrupt event */
0390         r = zd_iowrite16_locked(chip, 0x0f, UW2453_INTR_REG);
0391         if (r)
0392             return r;
0393 
0394         /* check interrupt status */
0395         r = zd_ioread16_locked(chip, &intr_status, UW2453_INTR_REG);
0396         if (r)
0397             return r;
0398 
0399         if (!(intr_status & 0xf)) {
0400             dev_dbg_f(zd_chip_dev(chip),
0401                 "PLL locked on configuration %d\n", i);
0402             found_config = i;
0403             break;
0404         }
0405     }
0406 
0407     if (found_config == -1) {
0408         /* autocal */
0409         dev_dbg_f(zd_chip_dev(chip),
0410             "PLL did not lock, using autocal\n");
0411 
0412         r = uw2453_synth_set_channel(chip, 1, true);
0413         if (r)
0414             return r;
0415 
0416         r = uw2453_write_vco_cfg(chip, UW2453_AUTOCAL_VCO_CFG);
0417         if (r)
0418             return r;
0419     }
0420 
0421     /* To match the vendor driver behaviour, we use the configuration after
0422      * the one that produced a lock. */
0423     UW2453_PRIV(rf)->config = found_config + 1;
0424 
0425     return zd_iowrite16_locked(chip, 0x06, ZD_CR203);
0426 }
0427 
0428 static int uw2453_set_channel(struct zd_rf *rf, u8 channel)
0429 {
0430     int r;
0431     u16 vco_cfg;
0432     int config = UW2453_PRIV(rf)->config;
0433     bool autocal = (config == -1);
0434     struct zd_chip *chip = zd_rf_to_chip(rf);
0435 
0436     static const struct zd_ioreq16 ioreqs[] = {
0437         { ZD_CR80,  0x30 }, { ZD_CR81,  0x30 }, { ZD_CR79,  0x58 },
0438         { ZD_CR12,  0xf0 }, { ZD_CR77,  0x1b }, { ZD_CR78,  0x58 },
0439     };
0440 
0441     r = uw2453_synth_set_channel(chip, channel, autocal);
0442     if (r)
0443         return r;
0444 
0445     if (autocal)
0446         vco_cfg = UW2453_AUTOCAL_VCO_CFG;
0447     else
0448         vco_cfg = uw2453_std_vco_cfg[config][CHAN_TO_PAIRIDX(channel)];
0449 
0450     r = uw2453_write_vco_cfg(chip, vco_cfg);
0451     if (r)
0452         return r;
0453 
0454     r = uw2453_init_mode(chip);
0455     if (r)
0456         return r;
0457 
0458     r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
0459     if (r)
0460         return r;
0461 
0462     r = uw2453_set_tx_gain_level(chip, channel);
0463     if (r)
0464         return r;
0465 
0466     return zd_iowrite16_locked(chip, 0x06, ZD_CR203);
0467 }
0468 
0469 static int uw2453_switch_radio_on(struct zd_rf *rf)
0470 {
0471     int r;
0472     struct zd_chip *chip = zd_rf_to_chip(rf);
0473     struct zd_ioreq16 ioreqs[] = {
0474         { ZD_CR11,  0x00 }, { ZD_CR251, 0x3f },
0475     };
0476 
0477     /* enter RXTX mode */
0478     r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f94), RF_RV_BITS);
0479     if (r)
0480         return r;
0481 
0482     if (zd_chip_is_zd1211b(chip))
0483         ioreqs[1].value = 0x7f;
0484 
0485     return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
0486 }
0487 
0488 static int uw2453_switch_radio_off(struct zd_rf *rf)
0489 {
0490     int r;
0491     struct zd_chip *chip = zd_rf_to_chip(rf);
0492     static const struct zd_ioreq16 ioreqs[] = {
0493         { ZD_CR11,  0x04 }, { ZD_CR251, 0x2f },
0494     };
0495 
0496     /* enter IDLE mode */
0497     /* FIXME: shouldn't we go to SLEEP? sent email to zydas */
0498     r = zd_rfwrite_locked(chip, UW2453_REGWRITE(0, 0x25f90), RF_RV_BITS);
0499     if (r)
0500         return r;
0501 
0502     return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
0503 }
0504 
0505 static void uw2453_clear(struct zd_rf *rf)
0506 {
0507     kfree(rf->priv);
0508 }
0509 
0510 int zd_rf_init_uw2453(struct zd_rf *rf)
0511 {
0512     rf->init_hw = uw2453_init_hw;
0513     rf->set_channel = uw2453_set_channel;
0514     rf->switch_radio_on = uw2453_switch_radio_on;
0515     rf->switch_radio_off = uw2453_switch_radio_off;
0516     rf->patch_6m_band_edge = zd_rf_generic_patch_6m;
0517     rf->clear = uw2453_clear;
0518     /* we have our own TX integration code */
0519     rf->update_channel_int = 0;
0520 
0521     rf->priv = kmalloc(sizeof(struct uw2453_priv), GFP_KERNEL);
0522     if (rf->priv == NULL)
0523         return -ENOMEM;
0524 
0525     return 0;
0526 }
0527