aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/rtl8180_rtl8225.c
diff options
context:
space:
mode:
authorMichael Wu <flamingice@sourmilk.net>2007-10-14 14:43:16 -0400
committerDavid S. Miller <davem@davemloft.net>2008-01-28 18:09:35 -0500
commitf653211197f3841f383fa9757ef8ce182c6cf627 (patch)
treeba56e1fa09924d1ffab4f825044175291786b58e /drivers/net/wireless/rtl8180_rtl8225.c
parentfa1c114fdaa605496045e56c42d0c8aa4c139e57 (diff)
Add rtl8180 wireless driver
This patch adds a mac80211 based wireless driver for the rtl8180 and rtl8185 PCI wireless cards. Also included are some rtl8187 changes required due to the relationship between that driver and this one. Michael Wu is primarily responsible for the initial driver and rtl8185 support. Andreas Merello provided the additional rtl8180 support. Thanks to Jukka Ruohonen for the donating a rtl8185 card! It was very helpful for the rtl8225z2 code. The Signed-off-by information below is collected from the individual patches submitted to wireless-2.6 before merging this driver upstream. Signed-off-by: Andrea Merello <andreamrl@tiscali.it> Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: Pavel Roskin <proski@gnu.org> Signed-off-by: Michael Wu <flamingice@sourmilk.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/rtl8180_rtl8225.c')
-rw-r--r--drivers/net/wireless/rtl8180_rtl8225.c779
1 files changed, 779 insertions, 0 deletions
diff --git a/drivers/net/wireless/rtl8180_rtl8225.c b/drivers/net/wireless/rtl8180_rtl8225.c
new file mode 100644
index 000000000000..ef3832bee85c
--- /dev/null
+++ b/drivers/net/wireless/rtl8180_rtl8225.c
@@ -0,0 +1,779 @@
1
2/*
3 * Radio tuning for RTL8225 on RTL8180
4 *
5 * Copyright 2007 Michael Wu <flamingice@sourmilk.net>
6 * Copyright 2007 Andrea Merello <andreamrl@tiscali.it>
7 *
8 * Based on the r8180 driver, which is:
9 * Copyright 2005 Andrea Merello <andreamrl@tiscali.it>, et al.
10 *
11 * Thanks to Realtek for their support!
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License version 2 as
15 * published by the Free Software Foundation.
16 */
17
18#include <linux/init.h>
19#include <linux/pci.h>
20#include <linux/delay.h>
21#include <net/mac80211.h>
22
23#include "rtl8180.h"
24#include "rtl8180_rtl8225.h"
25
26static void rtl8225_write(struct ieee80211_hw *dev, u8 addr, u16 data)
27{
28 struct rtl8180_priv *priv = dev->priv;
29 u16 reg80, reg84, reg82;
30 u32 bangdata;
31 int i;
32
33 bangdata = (data << 4) | (addr & 0xf);
34
35 reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput) & 0xfff3;
36 reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
37
38 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x7);
39
40 reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect);
41 rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x7 | 0x400);
42 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
43 udelay(10);
44
45 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
46 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
47 udelay(2);
48 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
49 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
50 udelay(10);
51
52 for (i = 15; i >= 0; i--) {
53 u16 reg = reg80 | !!(bangdata & (1 << i));
54
55 if (i & 1)
56 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
57
58 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
59 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg | (1 << 1));
60
61 if (!(i & 1))
62 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
63 }
64
65 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
66 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
67 udelay(10);
68
69 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
70 rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x400);
71 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
72}
73
74static u16 rtl8225_read(struct ieee80211_hw *dev, u8 addr)
75{
76 struct rtl8180_priv *priv = dev->priv;
77 u16 reg80, reg82, reg84, out;
78 int i;
79
80 reg80 = rtl818x_ioread16(priv, &priv->map->RFPinsOutput);
81 reg82 = rtl818x_ioread16(priv, &priv->map->RFPinsEnable);
82 reg84 = rtl818x_ioread16(priv, &priv->map->RFPinsSelect) | 0x400;
83
84 reg80 &= ~0xF;
85
86 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82 | 0x000F);
87 rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84 | 0x000F);
88
89 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80 | (1 << 2));
90 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
91 udelay(4);
92 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg80);
93 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
94 udelay(5);
95
96 for (i = 4; i >= 0; i--) {
97 u16 reg = reg80 | ((addr >> i) & 1);
98
99 if (!(i & 1)) {
100 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
101 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
102 udelay(1);
103 }
104
105 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
106 reg | (1 << 1));
107 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
108 udelay(2);
109 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
110 reg | (1 << 1));
111 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
112 udelay(2);
113
114 if (i & 1) {
115 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, reg);
116 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
117 udelay(1);
118 }
119 }
120
121 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x000E);
122 rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x040E);
123 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
124 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
125 reg80 | (1 << 3) | (1 << 1));
126 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
127 udelay(2);
128 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
129 reg80 | (1 << 3));
130 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
131 udelay(2);
132 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
133 reg80 | (1 << 3));
134 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
135 udelay(2);
136
137 out = 0;
138 for (i = 11; i >= 0; i--) {
139 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
140 reg80 | (1 << 3));
141 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
142 udelay(1);
143 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
144 reg80 | (1 << 3) | (1 << 1));
145 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
146 udelay(2);
147 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
148 reg80 | (1 << 3) | (1 << 1));
149 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
150 udelay(2);
151 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
152 reg80 | (1 << 3) | (1 << 1));
153 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
154 udelay(2);
155
156 if (rtl818x_ioread16(priv, &priv->map->RFPinsInput) & (1 << 1))
157 out |= 1 << i;
158
159 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
160 reg80 | (1 << 3));
161 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
162 udelay(2);
163 }
164
165 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput,
166 reg80 | (1 << 3) | (1 << 2));
167 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
168 udelay(2);
169
170 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, reg82);
171 rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, reg84);
172 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x03A0);
173
174 return out;
175}
176
177static const u16 rtl8225bcd_rxgain[] = {
178 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
179 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
180 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
181 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
182 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
183 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
184 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
185 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
186 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
187 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
188 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
189 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
190};
191
192static const u8 rtl8225_agc[] = {
193 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e,
194 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96,
195 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e,
196 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86,
197 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e,
198 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36,
199 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e,
200 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26,
201 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e,
202 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16,
203 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
204 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06,
205 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01,
206 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
207 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
208 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
209};
210
211static const u8 rtl8225_gain[] = {
212 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */
213 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */
214 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */
215 0x33, 0x80, 0x79, 0xc5, /* -78dbm */
216 0x43, 0x78, 0x76, 0xc5, /* -74dbm */
217 0x53, 0x60, 0x73, 0xc5, /* -70dbm */
218 0x63, 0x58, 0x70, 0xc5, /* -66dbm */
219};
220
221static const u8 rtl8225_threshold[] = {
222 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd
223};
224
225static const u8 rtl8225_tx_gain_cck_ofdm[] = {
226 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
227};
228
229static const u8 rtl8225_tx_power_cck[] = {
230 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
231 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
232 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
233 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
234 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
235 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
236};
237
238static const u8 rtl8225_tx_power_cck_ch14[] = {
239 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
240 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
241 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
242 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
243 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
244 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
245};
246
247static const u8 rtl8225_tx_power_ofdm[] = {
248 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
249};
250
251static const u32 rtl8225_chan[] = {
252 0x085c, 0x08dc, 0x095c, 0x09dc, 0x0a5c, 0x0adc, 0x0b5c,
253 0x0bdc, 0x0c5c, 0x0cdc, 0x0d5c, 0x0ddc, 0x0e5c, 0x0f72
254};
255
256static void rtl8225_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
257{
258 struct rtl8180_priv *priv = dev->priv;
259 u8 cck_power, ofdm_power;
260 const u8 *tmp;
261 u32 reg;
262 int i;
263
264 cck_power = priv->channels[channel - 1].val & 0xFF;
265 ofdm_power = priv->channels[channel - 1].val >> 8;
266
267 cck_power = min(cck_power, (u8)35);
268 ofdm_power = min(ofdm_power, (u8)35);
269
270 rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK,
271 rtl8225_tx_gain_cck_ofdm[cck_power / 6] >> 1);
272
273 if (channel == 14)
274 tmp = &rtl8225_tx_power_cck_ch14[(cck_power % 6) * 8];
275 else
276 tmp = &rtl8225_tx_power_cck[(cck_power % 6) * 8];
277
278 for (i = 0; i < 8; i++)
279 rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
280
281 msleep(1); /* FIXME: optional? */
282
283 /* anaparam2 on */
284 rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
285 reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
286 rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
287 rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_ON);
288 rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
289 rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
290
291 rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM,
292 rtl8225_tx_gain_cck_ofdm[ofdm_power/6] >> 1);
293
294 tmp = &rtl8225_tx_power_ofdm[ofdm_power % 6];
295
296 rtl8225_write_phy_ofdm(dev, 5, *tmp);
297 rtl8225_write_phy_ofdm(dev, 7, *tmp);
298
299 msleep(1);
300}
301
302static void rtl8225_rf_init(struct ieee80211_hw *dev)
303{
304 struct rtl8180_priv *priv = dev->priv;
305 int i;
306
307 rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON);
308
309 /* host_pci_init */
310 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
311 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
312 rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
313 rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
314 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
315 msleep(200); /* FIXME: ehh?? */
316 rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6));
317
318 rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x000a8008);
319
320 /* TODO: check if we need really to change BRSR to do RF config */
321 rtl818x_ioread16(priv, &priv->map->BRSR);
322 rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
323 rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
324 rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
325 rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
326 rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
327
328 rtl8225_write(dev, 0x0, 0x067);
329 rtl8225_write(dev, 0x1, 0xFE0);
330 rtl8225_write(dev, 0x2, 0x44D);
331 rtl8225_write(dev, 0x3, 0x441);
332 rtl8225_write(dev, 0x4, 0x8BE);
333 rtl8225_write(dev, 0x5, 0xBF0); /* TODO: minipci */
334 rtl8225_write(dev, 0x6, 0xAE6);
335 rtl8225_write(dev, 0x7, rtl8225_chan[0]);
336 rtl8225_write(dev, 0x8, 0x01F);
337 rtl8225_write(dev, 0x9, 0x334);
338 rtl8225_write(dev, 0xA, 0xFD4);
339 rtl8225_write(dev, 0xB, 0x391);
340 rtl8225_write(dev, 0xC, 0x050);
341 rtl8225_write(dev, 0xD, 0x6DB);
342 rtl8225_write(dev, 0xE, 0x029);
343 rtl8225_write(dev, 0xF, 0x914); msleep(1);
344
345 rtl8225_write(dev, 0x2, 0xC4D); msleep(100);
346
347 rtl8225_write(dev, 0x0, 0x127);
348
349 for (i = 0; i < ARRAY_SIZE(rtl8225bcd_rxgain); i++) {
350 rtl8225_write(dev, 0x1, i + 1);
351 rtl8225_write(dev, 0x2, rtl8225bcd_rxgain[i]);
352 }
353
354 rtl8225_write(dev, 0x0, 0x027);
355 rtl8225_write(dev, 0x0, 0x22F);
356 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
357
358 for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
359 rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
360 msleep(1);
361 rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
362 msleep(1);
363 }
364
365 msleep(1);
366
367 rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1);
368 rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1);
369 rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1);
370 rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1);
371 rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1);
372 rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1);
373 rtl8225_write_phy_ofdm(dev, 0x06, 0x00); msleep(1);
374 rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1);
375 rtl8225_write_phy_ofdm(dev, 0x08, 0x00); msleep(1);
376 rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1);
377 rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1);
378 rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1);
379 rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1);
380 rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1);
381 rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1);
382 rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1);
383 rtl8225_write_phy_ofdm(dev, 0x11, 0x03); msleep(1);
384 rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1);
385 rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1);
386 rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1);
387 rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1);
388 rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1);
389 rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1);
390 rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
391 rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1);
392 rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1);
393 rtl8225_write_phy_ofdm(dev, 0x1b, 0x76); msleep(1);
394 rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1);
395 rtl8225_write_phy_ofdm(dev, 0x1e, 0x95); msleep(1);
396 rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1);
397 rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1);
398 rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1);
399 rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1);
400 rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1);
401 rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1);
402 rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1);
403 rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1);
404
405 rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1);
406 rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1);
407 rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1);
408 rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1);
409 rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1);
410 rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1);
411 rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1);
412 rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1);
413 rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1);
414 rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1);
415 rtl8225_write_phy_cck(dev, 0x13, 0xd0);
416 rtl8225_write_phy_cck(dev, 0x19, 0x00);
417 rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
418 rtl8225_write_phy_cck(dev, 0x1b, 0x08);
419 rtl8225_write_phy_cck(dev, 0x40, 0x86);
420 rtl8225_write_phy_cck(dev, 0x41, 0x8d); msleep(1);
421 rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1);
422 rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1);
423 rtl8225_write_phy_cck(dev, 0x44, 0x1f); msleep(1);
424 rtl8225_write_phy_cck(dev, 0x45, 0x1e); msleep(1);
425 rtl8225_write_phy_cck(dev, 0x46, 0x1a); msleep(1);
426 rtl8225_write_phy_cck(dev, 0x47, 0x15); msleep(1);
427 rtl8225_write_phy_cck(dev, 0x48, 0x10); msleep(1);
428 rtl8225_write_phy_cck(dev, 0x49, 0x0a); msleep(1);
429 rtl8225_write_phy_cck(dev, 0x4a, 0x05); msleep(1);
430 rtl8225_write_phy_cck(dev, 0x4b, 0x02); msleep(1);
431 rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1);
432
433 rtl818x_iowrite8(priv, &priv->map->TESTR, 0x0D); msleep(1);
434
435 rtl8225_rf_set_tx_power(dev, 1);
436
437 /* RX antenna default to A */
438 rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */
439 rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */
440
441 rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
442 msleep(1);
443 rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002);
444 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
445
446 rtl8225_write(dev, 0x0c, 0x50);
447 /* set OFDM initial gain */
448 rtl8225_write_phy_ofdm(dev, 0x0d, rtl8225_gain[4 * 4]);
449 rtl8225_write_phy_ofdm(dev, 0x23, rtl8225_gain[4 * 4 + 1]);
450 rtl8225_write_phy_ofdm(dev, 0x1b, rtl8225_gain[4 * 4 + 2]);
451 rtl8225_write_phy_ofdm(dev, 0x1d, rtl8225_gain[4 * 4 + 3]);
452 /* set CCK threshold */
453 rtl8225_write_phy_cck(dev, 0x41, rtl8225_threshold[0]);
454}
455
456static const u8 rtl8225z2_tx_power_cck_ch14[] = {
457 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
458};
459
460static const u8 rtl8225z2_tx_power_cck_B[] = {
461 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x04
462};
463
464static const u8 rtl8225z2_tx_power_cck_A[] = {
465 0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04
466};
467
468static const u8 rtl8225z2_tx_power_cck[] = {
469 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
470};
471
472static void rtl8225z2_rf_set_tx_power(struct ieee80211_hw *dev, int channel)
473{
474 struct rtl8180_priv *priv = dev->priv;
475 u8 cck_power, ofdm_power;
476 const u8 *tmp;
477 int i;
478
479 cck_power = priv->channels[channel - 1].val & 0xFF;
480 ofdm_power = priv->channels[channel - 1].val >> 8;
481
482 if (channel == 14)
483 tmp = rtl8225z2_tx_power_cck_ch14;
484 else if (cck_power == 12)
485 tmp = rtl8225z2_tx_power_cck_B;
486 else if (cck_power == 13)
487 tmp = rtl8225z2_tx_power_cck_A;
488 else
489 tmp = rtl8225z2_tx_power_cck;
490
491 for (i = 0; i < 8; i++)
492 rtl8225_write_phy_cck(dev, 0x44 + i, *tmp++);
493
494 cck_power = min(cck_power, (u8)35);
495 if (cck_power == 13 || cck_power == 14)
496 cck_power = 12;
497 if (cck_power >= 15)
498 cck_power -= 2;
499
500 rtl818x_iowrite8(priv, &priv->map->TX_GAIN_CCK, cck_power);
501 rtl818x_ioread8(priv, &priv->map->TX_GAIN_CCK);
502 msleep(1);
503
504 ofdm_power = min(ofdm_power, (u8)35);
505 rtl818x_iowrite8(priv, &priv->map->TX_GAIN_OFDM, ofdm_power);
506
507 rtl8225_write_phy_ofdm(dev, 2, 0x62);
508 rtl8225_write_phy_ofdm(dev, 5, 0x00);
509 rtl8225_write_phy_ofdm(dev, 6, 0x40);
510 rtl8225_write_phy_ofdm(dev, 7, 0x00);
511 rtl8225_write_phy_ofdm(dev, 8, 0x40);
512
513 msleep(1);
514}
515
516static const u16 rtl8225z2_rxgain[] = {
517 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009,
518 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141,
519 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183,
520 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244,
521 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288,
522 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345,
523 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389,
524 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393,
525 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d,
526 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9,
527 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
528 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
529};
530
531static void rtl8225z2_rf_init(struct ieee80211_hw *dev)
532{
533 struct rtl8180_priv *priv = dev->priv;
534 int i;
535
536 rtl8180_set_anaparam(priv, RTL8225_ANAPARAM_ON);
537
538 /* host_pci_init */
539 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
540 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
541 rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
542 rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0);
543 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
544 msleep(200); /* FIXME: ehh?? */
545 rtl818x_iowrite8(priv, &priv->map->GP_ENABLE, 0xFF & ~(1 << 6));
546
547 rtl818x_iowrite32(priv, &priv->map->RF_TIMING, 0x00088008);
548
549 /* TODO: check if we need really to change BRSR to do RF config */
550 rtl818x_ioread16(priv, &priv->map->BRSR);
551 rtl818x_iowrite16(priv, &priv->map->BRSR, 0xFFFF);
552 rtl818x_iowrite32(priv, &priv->map->RF_PARA, 0x00100044);
553 rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
554 rtl818x_iowrite8(priv, &priv->map->CONFIG3, 0x44);
555 rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
556
557 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
558
559 rtl8225_write(dev, 0x0, 0x0B7); msleep(1);
560 rtl8225_write(dev, 0x1, 0xEE0); msleep(1);
561 rtl8225_write(dev, 0x2, 0x44D); msleep(1);
562 rtl8225_write(dev, 0x3, 0x441); msleep(1);
563 rtl8225_write(dev, 0x4, 0x8C3); msleep(1);
564 rtl8225_write(dev, 0x5, 0xC72); msleep(1);
565 rtl8225_write(dev, 0x6, 0x0E6); msleep(1);
566 rtl8225_write(dev, 0x7, 0x82A); msleep(1);
567 rtl8225_write(dev, 0x8, 0x03F); msleep(1);
568 rtl8225_write(dev, 0x9, 0x335); msleep(1);
569 rtl8225_write(dev, 0xa, 0x9D4); msleep(1);
570 rtl8225_write(dev, 0xb, 0x7BB); msleep(1);
571 rtl8225_write(dev, 0xc, 0x850); msleep(1);
572 rtl8225_write(dev, 0xd, 0xCDF); msleep(1);
573 rtl8225_write(dev, 0xe, 0x02B); msleep(1);
574 rtl8225_write(dev, 0xf, 0x114); msleep(100);
575
576 if (!(rtl8225_read(dev, 6) & (1 << 7))) {
577 rtl8225_write(dev, 0x02, 0x0C4D);
578 msleep(200);
579 rtl8225_write(dev, 0x02, 0x044D);
580 msleep(100);
581 /* TODO: readd calibration failure message when the calibration
582 check works */
583 }
584
585 rtl8225_write(dev, 0x0, 0x1B7);
586 rtl8225_write(dev, 0x3, 0x002);
587 rtl8225_write(dev, 0x5, 0x004);
588
589 for (i = 0; i < ARRAY_SIZE(rtl8225z2_rxgain); i++) {
590 rtl8225_write(dev, 0x1, i + 1);
591 rtl8225_write(dev, 0x2, rtl8225z2_rxgain[i]);
592 }
593
594 rtl8225_write(dev, 0x0, 0x0B7); msleep(100);
595 rtl8225_write(dev, 0x2, 0xC4D);
596
597 msleep(200);
598 rtl8225_write(dev, 0x2, 0x44D);
599 msleep(100);
600
601 rtl8225_write(dev, 0x00, 0x2BF);
602 rtl8225_write(dev, 0xFF, 0xFFFF);
603
604 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
605
606 for (i = 0; i < ARRAY_SIZE(rtl8225_agc); i++) {
607 rtl8225_write_phy_ofdm(dev, 0xB, rtl8225_agc[i]);
608 msleep(1);
609 rtl8225_write_phy_ofdm(dev, 0xA, 0x80 + i);
610 msleep(1);
611 }
612
613 msleep(1);
614
615 rtl8225_write_phy_ofdm(dev, 0x00, 0x01); msleep(1);
616 rtl8225_write_phy_ofdm(dev, 0x01, 0x02); msleep(1);
617 rtl8225_write_phy_ofdm(dev, 0x02, 0x62); msleep(1);
618 rtl8225_write_phy_ofdm(dev, 0x03, 0x00); msleep(1);
619 rtl8225_write_phy_ofdm(dev, 0x04, 0x00); msleep(1);
620 rtl8225_write_phy_ofdm(dev, 0x05, 0x00); msleep(1);
621 rtl8225_write_phy_ofdm(dev, 0x06, 0x40); msleep(1);
622 rtl8225_write_phy_ofdm(dev, 0x07, 0x00); msleep(1);
623 rtl8225_write_phy_ofdm(dev, 0x08, 0x40); msleep(1);
624 rtl8225_write_phy_ofdm(dev, 0x09, 0xfe); msleep(1);
625 rtl8225_write_phy_ofdm(dev, 0x0a, 0x09); msleep(1);
626 rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
627 rtl8225_write_phy_ofdm(dev, 0x0b, 0x80); msleep(1);
628 rtl8225_write_phy_ofdm(dev, 0x0c, 0x01); msleep(1);
629 rtl8225_write_phy_ofdm(dev, 0x0d, 0x43);
630 rtl8225_write_phy_ofdm(dev, 0x0e, 0xd3); msleep(1);
631 rtl8225_write_phy_ofdm(dev, 0x0f, 0x38); msleep(1);
632 rtl8225_write_phy_ofdm(dev, 0x10, 0x84); msleep(1);
633 rtl8225_write_phy_ofdm(dev, 0x11, 0x06); msleep(1);
634 rtl8225_write_phy_ofdm(dev, 0x12, 0x20); msleep(1);
635 rtl8225_write_phy_ofdm(dev, 0x13, 0x20); msleep(1);
636 rtl8225_write_phy_ofdm(dev, 0x14, 0x00); msleep(1);
637 rtl8225_write_phy_ofdm(dev, 0x15, 0x40); msleep(1);
638 rtl8225_write_phy_ofdm(dev, 0x16, 0x00); msleep(1);
639 rtl8225_write_phy_ofdm(dev, 0x17, 0x40); msleep(1);
640 rtl8225_write_phy_ofdm(dev, 0x18, 0xef); msleep(1);
641 rtl8225_write_phy_ofdm(dev, 0x19, 0x19); msleep(1);
642 rtl8225_write_phy_ofdm(dev, 0x1a, 0x20); msleep(1);
643 rtl8225_write_phy_ofdm(dev, 0x1b, 0x11); msleep(1);
644 rtl8225_write_phy_ofdm(dev, 0x1c, 0x04); msleep(1);
645 rtl8225_write_phy_ofdm(dev, 0x1d, 0xc5); msleep(1);
646 rtl8225_write_phy_ofdm(dev, 0x1e, 0xb3); msleep(1);
647 rtl8225_write_phy_ofdm(dev, 0x1f, 0x75); msleep(1);
648 rtl8225_write_phy_ofdm(dev, 0x20, 0x1f); msleep(1);
649 rtl8225_write_phy_ofdm(dev, 0x21, 0x27); msleep(1);
650 rtl8225_write_phy_ofdm(dev, 0x22, 0x16); msleep(1);
651 rtl8225_write_phy_ofdm(dev, 0x23, 0x80); msleep(1); /* FIXME: not needed? */
652 rtl8225_write_phy_ofdm(dev, 0x24, 0x46); msleep(1);
653 rtl8225_write_phy_ofdm(dev, 0x25, 0x20); msleep(1);
654 rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1);
655 rtl8225_write_phy_ofdm(dev, 0x27, 0x88); msleep(1);
656
657 rtl8225_write_phy_cck(dev, 0x00, 0x98); msleep(1);
658 rtl8225_write_phy_cck(dev, 0x03, 0x20); msleep(1);
659 rtl8225_write_phy_cck(dev, 0x04, 0x7e); msleep(1);
660 rtl8225_write_phy_cck(dev, 0x05, 0x12); msleep(1);
661 rtl8225_write_phy_cck(dev, 0x06, 0xfc); msleep(1);
662 rtl8225_write_phy_cck(dev, 0x07, 0x78); msleep(1);
663 rtl8225_write_phy_cck(dev, 0x08, 0x2e); msleep(1);
664 rtl8225_write_phy_cck(dev, 0x10, 0x93); msleep(1);
665 rtl8225_write_phy_cck(dev, 0x11, 0x88); msleep(1);
666 rtl8225_write_phy_cck(dev, 0x12, 0x47); msleep(1);
667 rtl8225_write_phy_cck(dev, 0x13, 0xd0);
668 rtl8225_write_phy_cck(dev, 0x19, 0x00);
669 rtl8225_write_phy_cck(dev, 0x1a, 0xa0);
670 rtl8225_write_phy_cck(dev, 0x1b, 0x08);
671 rtl8225_write_phy_cck(dev, 0x40, 0x86);
672 rtl8225_write_phy_cck(dev, 0x41, 0x8a); msleep(1);
673 rtl8225_write_phy_cck(dev, 0x42, 0x15); msleep(1);
674 rtl8225_write_phy_cck(dev, 0x43, 0x18); msleep(1);
675 rtl8225_write_phy_cck(dev, 0x44, 0x36); msleep(1);
676 rtl8225_write_phy_cck(dev, 0x45, 0x35); msleep(1);
677 rtl8225_write_phy_cck(dev, 0x46, 0x2e); msleep(1);
678 rtl8225_write_phy_cck(dev, 0x47, 0x25); msleep(1);
679 rtl8225_write_phy_cck(dev, 0x48, 0x1c); msleep(1);
680 rtl8225_write_phy_cck(dev, 0x49, 0x12); msleep(1);
681 rtl8225_write_phy_cck(dev, 0x4a, 0x09); msleep(1);
682 rtl8225_write_phy_cck(dev, 0x4b, 0x04); msleep(1);
683 rtl8225_write_phy_cck(dev, 0x4c, 0x05); msleep(1);
684
685 rtl818x_iowrite8(priv, (u8 __iomem *)((void __iomem *)priv->map + 0x5B), 0x0D); msleep(1);
686
687 rtl8225z2_rf_set_tx_power(dev, 1);
688
689 /* RX antenna default to A */
690 rtl8225_write_phy_cck(dev, 0x10, 0x9b); msleep(1); /* B: 0xDB */
691 rtl8225_write_phy_ofdm(dev, 0x26, 0x90); msleep(1); /* B: 0x10 */
692
693 rtl818x_iowrite8(priv, &priv->map->TX_ANTENNA, 0x03); /* B: 0x00 */
694 msleep(1);
695 rtl818x_iowrite32(priv, (__le32 __iomem *)((void __iomem *)priv->map + 0x94), 0x15c00002);
696 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
697}
698
699static void rtl8225_rf_stop(struct ieee80211_hw *dev)
700{
701 struct rtl8180_priv *priv = dev->priv;
702 u8 reg;
703
704 rtl8225_write(dev, 0x4, 0x1f); msleep(1);
705
706 rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_CONFIG);
707 reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
708 rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | RTL818X_CONFIG3_ANAPARAM_WRITE);
709 rtl818x_iowrite32(priv, &priv->map->ANAPARAM2, RTL8225_ANAPARAM2_OFF);
710 rtl818x_iowrite32(priv, &priv->map->ANAPARAM, RTL8225_ANAPARAM_OFF);
711 rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg & ~RTL818X_CONFIG3_ANAPARAM_WRITE);
712 rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
713}
714
715static void rtl8225_rf_set_channel(struct ieee80211_hw *dev,
716 struct ieee80211_conf *conf)
717{
718 struct rtl8180_priv *priv = dev->priv;
719
720 if (priv->rf->init == rtl8225_rf_init)
721 rtl8225_rf_set_tx_power(dev, conf->channel);
722 else
723 rtl8225z2_rf_set_tx_power(dev, conf->channel);
724
725 rtl8225_write(dev, 0x7, rtl8225_chan[conf->channel - 1]);
726 msleep(10);
727
728 if (conf->flags & IEEE80211_CONF_SHORT_SLOT_TIME) {
729 rtl818x_iowrite8(priv, &priv->map->SLOT, 0x9);
730 rtl818x_iowrite8(priv, &priv->map->SIFS, 0x22);
731 rtl818x_iowrite8(priv, &priv->map->DIFS, 0x14);
732 rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
733 rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0x73);
734 } else {
735 rtl818x_iowrite8(priv, &priv->map->SLOT, 0x14);
736 rtl818x_iowrite8(priv, &priv->map->SIFS, 0x44);
737 rtl818x_iowrite8(priv, &priv->map->DIFS, 0x24);
738 rtl818x_iowrite8(priv, &priv->map->EIFS, 81);
739 rtl818x_iowrite8(priv, &priv->map->CW_VAL, 0xa5);
740 }
741}
742
743static const struct rtl818x_rf_ops rtl8225_ops = {
744 .name = "rtl8225",
745 .init = rtl8225_rf_init,
746 .stop = rtl8225_rf_stop,
747 .set_chan = rtl8225_rf_set_channel
748};
749
750static const struct rtl818x_rf_ops rtl8225z2_ops = {
751 .name = "rtl8225z2",
752 .init = rtl8225z2_rf_init,
753 .stop = rtl8225_rf_stop,
754 .set_chan = rtl8225_rf_set_channel
755};
756
757const struct rtl818x_rf_ops * rtl8180_detect_rf(struct ieee80211_hw *dev)
758{
759 struct rtl8180_priv *priv = dev->priv;
760 u16 reg8, reg9;
761
762 rtl818x_iowrite16(priv, &priv->map->RFPinsOutput, 0x0480);
763 rtl818x_iowrite16(priv, &priv->map->RFPinsSelect, 0x0488);
764 rtl818x_iowrite16(priv, &priv->map->RFPinsEnable, 0x1FFF);
765 rtl818x_ioread8(priv, &priv->map->EEPROM_CMD);
766 msleep(100);
767
768 rtl8225_write(dev, 0, 0x1B7);
769
770 reg8 = rtl8225_read(dev, 8);
771 reg9 = rtl8225_read(dev, 9);
772
773 rtl8225_write(dev, 0, 0x0B7);
774
775 if (reg8 != 0x588 || reg9 != 0x700)
776 return &rtl8225_ops;
777
778 return &rtl8225z2_ops;
779}