diff options
author | Paul Mackerras <paulus@samba.org> | 2008-01-30 19:25:51 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2008-01-30 19:25:51 -0500 |
commit | bd45ac0c5daae35e7c71138172e63df5cf644cf6 (patch) | |
tree | 5eb5a599bf6a9d7a8a34e802db932aa9e9555de4 /drivers/ssb | |
parent | 4eece4ccf997c0e6d8fdad3d842e37b16b8d705f (diff) | |
parent | 5bdeae46be6dfe9efa44a548bd622af325f4bdb4 (diff) |
Merge branch 'linux-2.6'
Diffstat (limited to 'drivers/ssb')
-rw-r--r-- | drivers/ssb/b43_pci_bridge.c | 2 | ||||
-rw-r--r-- | drivers/ssb/main.c | 10 | ||||
-rw-r--r-- | drivers/ssb/pci.c | 276 | ||||
-rw-r--r-- | drivers/ssb/pcmcia.c | 71 |
4 files changed, 217 insertions, 142 deletions
diff --git a/drivers/ssb/b43_pci_bridge.c b/drivers/ssb/b43_pci_bridge.c index f145d8a4cfde..1a31f7a72848 100644 --- a/drivers/ssb/b43_pci_bridge.c +++ b/drivers/ssb/b43_pci_bridge.c | |||
@@ -27,6 +27,8 @@ static const struct pci_device_id b43_pci_bridge_tbl[] = { | |||
27 | { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4321) }, | 27 | { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4321) }, |
28 | { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4324) }, | 28 | { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4324) }, |
29 | { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4325) }, | 29 | { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4325) }, |
30 | { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4328) }, | ||
31 | { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4329) }, | ||
30 | { 0, }, | 32 | { 0, }, |
31 | }; | 33 | }; |
32 | MODULE_DEVICE_TABLE(pci, b43_pci_bridge_tbl); | 34 | MODULE_DEVICE_TABLE(pci, b43_pci_bridge_tbl); |
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 85a20546e827..9028ed5715a1 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c | |||
@@ -872,14 +872,22 @@ EXPORT_SYMBOL(ssb_clockspeed); | |||
872 | 872 | ||
873 | static u32 ssb_tmslow_reject_bitmask(struct ssb_device *dev) | 873 | static u32 ssb_tmslow_reject_bitmask(struct ssb_device *dev) |
874 | { | 874 | { |
875 | u32 rev = ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_SSBREV; | ||
876 | |||
875 | /* The REJECT bit changed position in TMSLOW between | 877 | /* The REJECT bit changed position in TMSLOW between |
876 | * Backplane revisions. */ | 878 | * Backplane revisions. */ |
877 | switch (ssb_read32(dev, SSB_IDLOW) & SSB_IDLOW_SSBREV) { | 879 | switch (rev) { |
878 | case SSB_IDLOW_SSBREV_22: | 880 | case SSB_IDLOW_SSBREV_22: |
879 | return SSB_TMSLOW_REJECT_22; | 881 | return SSB_TMSLOW_REJECT_22; |
880 | case SSB_IDLOW_SSBREV_23: | 882 | case SSB_IDLOW_SSBREV_23: |
881 | return SSB_TMSLOW_REJECT_23; | 883 | return SSB_TMSLOW_REJECT_23; |
884 | case SSB_IDLOW_SSBREV_24: /* TODO - find the proper REJECT bits */ | ||
885 | case SSB_IDLOW_SSBREV_25: /* same here */ | ||
886 | case SSB_IDLOW_SSBREV_26: /* same here */ | ||
887 | case SSB_IDLOW_SSBREV_27: /* same here */ | ||
888 | return SSB_TMSLOW_REJECT_23; /* this is a guess */ | ||
882 | default: | 889 | default: |
890 | printk(KERN_INFO "ssb: Backplane Revision 0x%.8X\n", rev); | ||
883 | WARN_ON(1); | 891 | WARN_ON(1); |
884 | } | 892 | } |
885 | return (SSB_TMSLOW_REJECT_22 | SSB_TMSLOW_REJECT_23); | 893 | return (SSB_TMSLOW_REJECT_22 | SSB_TMSLOW_REJECT_23); |
diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 0ab095c6581a..b434df75047f 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c | |||
@@ -212,29 +212,29 @@ static inline u8 ssb_crc8(u8 crc, u8 data) | |||
212 | return t[crc ^ data]; | 212 | return t[crc ^ data]; |
213 | } | 213 | } |
214 | 214 | ||
215 | static u8 ssb_sprom_crc(const u16 *sprom) | 215 | static u8 ssb_sprom_crc(const u16 *sprom, u16 size) |
216 | { | 216 | { |
217 | int word; | 217 | int word; |
218 | u8 crc = 0xFF; | 218 | u8 crc = 0xFF; |
219 | 219 | ||
220 | for (word = 0; word < SSB_SPROMSIZE_WORDS - 1; word++) { | 220 | for (word = 0; word < size - 1; word++) { |
221 | crc = ssb_crc8(crc, sprom[word] & 0x00FF); | 221 | crc = ssb_crc8(crc, sprom[word] & 0x00FF); |
222 | crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8); | 222 | crc = ssb_crc8(crc, (sprom[word] & 0xFF00) >> 8); |
223 | } | 223 | } |
224 | crc = ssb_crc8(crc, sprom[SPOFF(SSB_SPROM_REVISION)] & 0x00FF); | 224 | crc = ssb_crc8(crc, sprom[size - 1] & 0x00FF); |
225 | crc ^= 0xFF; | 225 | crc ^= 0xFF; |
226 | 226 | ||
227 | return crc; | 227 | return crc; |
228 | } | 228 | } |
229 | 229 | ||
230 | static int sprom_check_crc(const u16 *sprom) | 230 | static int sprom_check_crc(const u16 *sprom, u16 size) |
231 | { | 231 | { |
232 | u8 crc; | 232 | u8 crc; |
233 | u8 expected_crc; | 233 | u8 expected_crc; |
234 | u16 tmp; | 234 | u16 tmp; |
235 | 235 | ||
236 | crc = ssb_sprom_crc(sprom); | 236 | crc = ssb_sprom_crc(sprom, size); |
237 | tmp = sprom[SPOFF(SSB_SPROM_REVISION)] & SSB_SPROM_REVISION_CRC; | 237 | tmp = sprom[size - 1] & SSB_SPROM_REVISION_CRC; |
238 | expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; | 238 | expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; |
239 | if (crc != expected_crc) | 239 | if (crc != expected_crc) |
240 | return -EPROTO; | 240 | return -EPROTO; |
@@ -246,8 +246,8 @@ static void sprom_do_read(struct ssb_bus *bus, u16 *sprom) | |||
246 | { | 246 | { |
247 | int i; | 247 | int i; |
248 | 248 | ||
249 | for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) | 249 | for (i = 0; i < bus->sprom_size; i++) |
250 | sprom[i] = readw(bus->mmio + SSB_SPROM_BASE + (i * 2)); | 250 | sprom[i] = ioread16(bus->mmio + SSB_SPROM_BASE + (i * 2)); |
251 | } | 251 | } |
252 | 252 | ||
253 | static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) | 253 | static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) |
@@ -255,6 +255,7 @@ static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) | |||
255 | struct pci_dev *pdev = bus->host_pci; | 255 | struct pci_dev *pdev = bus->host_pci; |
256 | int i, err; | 256 | int i, err; |
257 | u32 spromctl; | 257 | u32 spromctl; |
258 | u16 size = bus->sprom_size; | ||
258 | 259 | ||
259 | ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n"); | 260 | ssb_printk(KERN_NOTICE PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n"); |
260 | err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); | 261 | err = pci_read_config_dword(pdev, SSB_SPROMCTL, &spromctl); |
@@ -266,12 +267,12 @@ static int sprom_do_write(struct ssb_bus *bus, const u16 *sprom) | |||
266 | goto err_ctlreg; | 267 | goto err_ctlreg; |
267 | ssb_printk(KERN_NOTICE PFX "[ 0%%"); | 268 | ssb_printk(KERN_NOTICE PFX "[ 0%%"); |
268 | msleep(500); | 269 | msleep(500); |
269 | for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) { | 270 | for (i = 0; i < size; i++) { |
270 | if (i == SSB_SPROMSIZE_WORDS / 4) | 271 | if (i == size / 4) |
271 | ssb_printk("25%%"); | 272 | ssb_printk("25%%"); |
272 | else if (i == SSB_SPROMSIZE_WORDS / 2) | 273 | else if (i == size / 2) |
273 | ssb_printk("50%%"); | 274 | ssb_printk("50%%"); |
274 | else if (i == (SSB_SPROMSIZE_WORDS / 4) * 3) | 275 | else if (i == (size * 3) / 4) |
275 | ssb_printk("75%%"); | 276 | ssb_printk("75%%"); |
276 | else if (i % 2) | 277 | else if (i % 2) |
277 | ssb_printk("."); | 278 | ssb_printk("."); |
@@ -296,24 +297,53 @@ err_ctlreg: | |||
296 | return err; | 297 | return err; |
297 | } | 298 | } |
298 | 299 | ||
299 | static void sprom_extract_r1(struct ssb_sprom_r1 *out, const u16 *in) | 300 | static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in, |
301 | u16 mask, u16 shift) | ||
302 | { | ||
303 | u16 v; | ||
304 | u8 gain; | ||
305 | |||
306 | v = in[SPOFF(SSB_SPROM1_AGAIN)]; | ||
307 | gain = (v & mask) >> shift; | ||
308 | if (gain == 0xFF) | ||
309 | gain = 2; /* If unset use 2dBm */ | ||
310 | if (sprom_revision == 1) { | ||
311 | /* Convert to Q5.2 */ | ||
312 | gain <<= 2; | ||
313 | } else { | ||
314 | /* Q5.2 Fractional part is stored in 0xC0 */ | ||
315 | gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2); | ||
316 | } | ||
317 | |||
318 | return (s8)gain; | ||
319 | } | ||
320 | |||
321 | static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) | ||
300 | { | 322 | { |
301 | int i; | 323 | int i; |
302 | u16 v; | 324 | u16 v; |
325 | s8 gain; | ||
326 | u16 loc[3]; | ||
303 | 327 | ||
304 | SPEX(pci_spid, SSB_SPROM1_SPID, 0xFFFF, 0); | 328 | if (out->revision == 3) { /* rev 3 moved MAC */ |
305 | SPEX(pci_svid, SSB_SPROM1_SVID, 0xFFFF, 0); | 329 | loc[0] = SSB_SPROM3_IL0MAC; |
306 | SPEX(pci_pid, SSB_SPROM1_PID, 0xFFFF, 0); | 330 | loc[1] = SSB_SPROM3_ET0MAC; |
331 | loc[2] = SSB_SPROM3_ET1MAC; | ||
332 | } else { | ||
333 | loc[0] = SSB_SPROM1_IL0MAC; | ||
334 | loc[1] = SSB_SPROM1_ET0MAC; | ||
335 | loc[2] = SSB_SPROM1_ET1MAC; | ||
336 | } | ||
307 | for (i = 0; i < 3; i++) { | 337 | for (i = 0; i < 3; i++) { |
308 | v = in[SPOFF(SSB_SPROM1_IL0MAC) + i]; | 338 | v = in[SPOFF(loc[0]) + i]; |
309 | *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); | 339 | *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); |
310 | } | 340 | } |
311 | for (i = 0; i < 3; i++) { | 341 | for (i = 0; i < 3; i++) { |
312 | v = in[SPOFF(SSB_SPROM1_ET0MAC) + i]; | 342 | v = in[SPOFF(loc[1]) + i]; |
313 | *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); | 343 | *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); |
314 | } | 344 | } |
315 | for (i = 0; i < 3; i++) { | 345 | for (i = 0; i < 3; i++) { |
316 | v = in[SPOFF(SSB_SPROM1_ET1MAC) + i]; | 346 | v = in[SPOFF(loc[2]) + i]; |
317 | *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); | 347 | *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); |
318 | } | 348 | } |
319 | SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0); | 349 | SPEX(et0phyaddr, SSB_SPROM1_ETHPHY, SSB_SPROM1_ETHPHY_ET0A, 0); |
@@ -324,9 +354,9 @@ static void sprom_extract_r1(struct ssb_sprom_r1 *out, const u16 *in) | |||
324 | SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0); | 354 | SPEX(board_rev, SSB_SPROM1_BINF, SSB_SPROM1_BINF_BREV, 0); |
325 | SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE, | 355 | SPEX(country_code, SSB_SPROM1_BINF, SSB_SPROM1_BINF_CCODE, |
326 | SSB_SPROM1_BINF_CCODE_SHIFT); | 356 | SSB_SPROM1_BINF_CCODE_SHIFT); |
327 | SPEX(antenna_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA, | 357 | SPEX(ant_available_a, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTA, |
328 | SSB_SPROM1_BINF_ANTA_SHIFT); | 358 | SSB_SPROM1_BINF_ANTA_SHIFT); |
329 | SPEX(antenna_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG, | 359 | SPEX(ant_available_bg, SSB_SPROM1_BINF, SSB_SPROM1_BINF_ANTBG, |
330 | SSB_SPROM1_BINF_ANTBG_SHIFT); | 360 | SSB_SPROM1_BINF_ANTBG_SHIFT); |
331 | SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0); | 361 | SPEX(pa0b0, SSB_SPROM1_PA0B0, 0xFFFF, 0); |
332 | SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0); | 362 | SPEX(pa0b1, SSB_SPROM1_PA0B1, 0xFFFF, 0); |
@@ -347,100 +377,108 @@ static void sprom_extract_r1(struct ssb_sprom_r1 *out, const u16 *in) | |||
347 | SSB_SPROM1_ITSSI_A_SHIFT); | 377 | SSB_SPROM1_ITSSI_A_SHIFT); |
348 | SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0); | 378 | SPEX(itssi_bg, SSB_SPROM1_ITSSI, SSB_SPROM1_ITSSI_BG, 0); |
349 | SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0); | 379 | SPEX(boardflags_lo, SSB_SPROM1_BFLLO, 0xFFFF, 0); |
350 | SPEX(antenna_gain_a, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_A, 0); | 380 | if (out->revision >= 2) |
351 | SPEX(antenna_gain_bg, SSB_SPROM1_AGAIN, SSB_SPROM1_AGAIN_BG, | 381 | SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0); |
352 | SSB_SPROM1_AGAIN_BG_SHIFT); | 382 | |
353 | for (i = 0; i < 4; i++) { | 383 | /* Extract the antenna gain values. */ |
354 | v = in[SPOFF(SSB_SPROM1_OEM) + i]; | 384 | gain = r123_extract_antgain(out->revision, in, |
355 | *(((__le16 *)out->oem) + i) = cpu_to_le16(v); | 385 | SSB_SPROM1_AGAIN_BG, |
356 | } | 386 | SSB_SPROM1_AGAIN_BG_SHIFT); |
387 | out->antenna_gain.ghz24.a0 = gain; | ||
388 | out->antenna_gain.ghz24.a1 = gain; | ||
389 | out->antenna_gain.ghz24.a2 = gain; | ||
390 | out->antenna_gain.ghz24.a3 = gain; | ||
391 | gain = r123_extract_antgain(out->revision, in, | ||
392 | SSB_SPROM1_AGAIN_A, | ||
393 | SSB_SPROM1_AGAIN_A_SHIFT); | ||
394 | out->antenna_gain.ghz5.a0 = gain; | ||
395 | out->antenna_gain.ghz5.a1 = gain; | ||
396 | out->antenna_gain.ghz5.a2 = gain; | ||
397 | out->antenna_gain.ghz5.a3 = gain; | ||
357 | } | 398 | } |
358 | 399 | ||
359 | static void sprom_extract_r2(struct ssb_sprom_r2 *out, const u16 *in) | 400 | static void sprom_extract_r4(struct ssb_sprom *out, const u16 *in) |
360 | { | 401 | { |
361 | int i; | 402 | int i; |
362 | u16 v; | 403 | u16 v; |
363 | 404 | ||
364 | SPEX(boardflags_hi, SSB_SPROM2_BFLHI, 0xFFFF, 0); | 405 | /* extract the equivalent of the r1 variables */ |
365 | SPEX(maxpwr_a_hi, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_HI, 0); | 406 | for (i = 0; i < 3; i++) { |
366 | SPEX(maxpwr_a_lo, SSB_SPROM2_MAXP_A, SSB_SPROM2_MAXP_A_LO, | 407 | v = in[SPOFF(SSB_SPROM4_IL0MAC) + i]; |
367 | SSB_SPROM2_MAXP_A_LO_SHIFT); | 408 | *(((__be16 *)out->il0mac) + i) = cpu_to_be16(v); |
368 | SPEX(pa1lob0, SSB_SPROM2_PA1LOB0, 0xFFFF, 0); | ||
369 | SPEX(pa1lob1, SSB_SPROM2_PA1LOB1, 0xFFFF, 0); | ||
370 | SPEX(pa1lob2, SSB_SPROM2_PA1LOB2, 0xFFFF, 0); | ||
371 | SPEX(pa1hib0, SSB_SPROM2_PA1HIB0, 0xFFFF, 0); | ||
372 | SPEX(pa1hib1, SSB_SPROM2_PA1HIB1, 0xFFFF, 0); | ||
373 | SPEX(pa1hib2, SSB_SPROM2_PA1HIB2, 0xFFFF, 0); | ||
374 | SPEX(ofdm_pwr_off, SSB_SPROM2_OPO, SSB_SPROM2_OPO_VALUE, 0); | ||
375 | for (i = 0; i < 4; i++) { | ||
376 | v = in[SPOFF(SSB_SPROM2_CCODE) + i]; | ||
377 | *(((__le16 *)out->country_str) + i) = cpu_to_le16(v); | ||
378 | } | 409 | } |
410 | for (i = 0; i < 3; i++) { | ||
411 | v = in[SPOFF(SSB_SPROM4_ET0MAC) + i]; | ||
412 | *(((__be16 *)out->et0mac) + i) = cpu_to_be16(v); | ||
413 | } | ||
414 | for (i = 0; i < 3; i++) { | ||
415 | v = in[SPOFF(SSB_SPROM4_ET1MAC) + i]; | ||
416 | *(((__be16 *)out->et1mac) + i) = cpu_to_be16(v); | ||
417 | } | ||
418 | SPEX(et0phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET0A, 0); | ||
419 | SPEX(et1phyaddr, SSB_SPROM4_ETHPHY, SSB_SPROM4_ETHPHY_ET1A, | ||
420 | SSB_SPROM4_ETHPHY_ET1A_SHIFT); | ||
421 | SPEX(country_code, SSB_SPROM4_CCODE, 0xFFFF, 0); | ||
422 | SPEX(boardflags_lo, SSB_SPROM4_BFLLO, 0xFFFF, 0); | ||
423 | SPEX(boardflags_hi, SSB_SPROM4_BFLHI, 0xFFFF, 0); | ||
424 | SPEX(ant_available_a, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_A, | ||
425 | SSB_SPROM4_ANTAVAIL_A_SHIFT); | ||
426 | SPEX(ant_available_bg, SSB_SPROM4_ANTAVAIL, SSB_SPROM4_ANTAVAIL_BG, | ||
427 | SSB_SPROM4_ANTAVAIL_BG_SHIFT); | ||
428 | SPEX(maxpwr_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_MAXP_BG_MASK, 0); | ||
429 | SPEX(itssi_bg, SSB_SPROM4_MAXP_BG, SSB_SPROM4_ITSSI_BG, | ||
430 | SSB_SPROM4_ITSSI_BG_SHIFT); | ||
431 | SPEX(maxpwr_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_MAXP_A_MASK, 0); | ||
432 | SPEX(itssi_a, SSB_SPROM4_MAXP_A, SSB_SPROM4_ITSSI_A, | ||
433 | SSB_SPROM4_ITSSI_A_SHIFT); | ||
434 | SPEX(gpio0, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P0, 0); | ||
435 | SPEX(gpio1, SSB_SPROM4_GPIOA, SSB_SPROM4_GPIOA_P1, | ||
436 | SSB_SPROM4_GPIOA_P1_SHIFT); | ||
437 | SPEX(gpio2, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P2, 0); | ||
438 | SPEX(gpio3, SSB_SPROM4_GPIOB, SSB_SPROM4_GPIOB_P3, | ||
439 | SSB_SPROM4_GPIOB_P3_SHIFT); | ||
440 | |||
441 | /* Extract the antenna gain values. */ | ||
442 | SPEX(antenna_gain.ghz24.a0, SSB_SPROM4_AGAIN01, | ||
443 | SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT); | ||
444 | SPEX(antenna_gain.ghz24.a1, SSB_SPROM4_AGAIN01, | ||
445 | SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT); | ||
446 | SPEX(antenna_gain.ghz24.a2, SSB_SPROM4_AGAIN23, | ||
447 | SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT); | ||
448 | SPEX(antenna_gain.ghz24.a3, SSB_SPROM4_AGAIN23, | ||
449 | SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT); | ||
450 | memcpy(&out->antenna_gain.ghz5, &out->antenna_gain.ghz24, | ||
451 | sizeof(out->antenna_gain.ghz5)); | ||
452 | |||
453 | /* TODO - get remaining rev 4 stuff needed */ | ||
379 | } | 454 | } |
380 | 455 | ||
381 | static void sprom_extract_r3(struct ssb_sprom_r3 *out, const u16 *in) | 456 | static int sprom_extract(struct ssb_bus *bus, struct ssb_sprom *out, |
382 | { | 457 | const u16 *in, u16 size) |
383 | out->ofdmapo = (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0xFF00) >> 8; | ||
384 | out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 0] & 0x00FF) << 8; | ||
385 | out->ofdmapo <<= 16; | ||
386 | out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0xFF00) >> 8; | ||
387 | out->ofdmapo |= (in[SPOFF(SSB_SPROM3_OFDMAPO) + 1] & 0x00FF) << 8; | ||
388 | |||
389 | out->ofdmalpo = (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0xFF00) >> 8; | ||
390 | out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 0] & 0x00FF) << 8; | ||
391 | out->ofdmalpo <<= 16; | ||
392 | out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0xFF00) >> 8; | ||
393 | out->ofdmalpo |= (in[SPOFF(SSB_SPROM3_OFDMALPO) + 1] & 0x00FF) << 8; | ||
394 | |||
395 | out->ofdmahpo = (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0xFF00) >> 8; | ||
396 | out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 0] & 0x00FF) << 8; | ||
397 | out->ofdmahpo <<= 16; | ||
398 | out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0xFF00) >> 8; | ||
399 | out->ofdmahpo |= (in[SPOFF(SSB_SPROM3_OFDMAHPO) + 1] & 0x00FF) << 8; | ||
400 | |||
401 | SPEX(gpioldc_on_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_ON, | ||
402 | SSB_SPROM3_GPIOLDC_ON_SHIFT); | ||
403 | SPEX(gpioldc_off_cnt, SSB_SPROM3_GPIOLDC, SSB_SPROM3_GPIOLDC_OFF, | ||
404 | SSB_SPROM3_GPIOLDC_OFF_SHIFT); | ||
405 | SPEX(cckpo_1M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_1M, 0); | ||
406 | SPEX(cckpo_2M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_2M, | ||
407 | SSB_SPROM3_CCKPO_2M_SHIFT); | ||
408 | SPEX(cckpo_55M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_55M, | ||
409 | SSB_SPROM3_CCKPO_55M_SHIFT); | ||
410 | SPEX(cckpo_11M, SSB_SPROM3_CCKPO, SSB_SPROM3_CCKPO_11M, | ||
411 | SSB_SPROM3_CCKPO_11M_SHIFT); | ||
412 | |||
413 | out->ofdmgpo = (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0xFF00) >> 8; | ||
414 | out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 0] & 0x00FF) << 8; | ||
415 | out->ofdmgpo <<= 16; | ||
416 | out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0xFF00) >> 8; | ||
417 | out->ofdmgpo |= (in[SPOFF(SSB_SPROM3_OFDMGPO) + 1] & 0x00FF) << 8; | ||
418 | } | ||
419 | |||
420 | static int sprom_extract(struct ssb_bus *bus, | ||
421 | struct ssb_sprom *out, const u16 *in) | ||
422 | { | 458 | { |
423 | memset(out, 0, sizeof(*out)); | 459 | memset(out, 0, sizeof(*out)); |
424 | 460 | ||
425 | SPEX(revision, SSB_SPROM_REVISION, SSB_SPROM_REVISION_REV, 0); | 461 | out->revision = in[size - 1] & 0x00FF; |
426 | SPEX(crc, SSB_SPROM_REVISION, SSB_SPROM_REVISION_CRC, | 462 | ssb_dprintk(KERN_DEBUG PFX "SPROM revision %d detected.\n", out->revision); |
427 | SSB_SPROM_REVISION_CRC_SHIFT); | ||
428 | |||
429 | if ((bus->chip_id & 0xFF00) == 0x4400) { | 463 | if ((bus->chip_id & 0xFF00) == 0x4400) { |
430 | /* Workaround: The BCM44XX chip has a stupid revision | 464 | /* Workaround: The BCM44XX chip has a stupid revision |
431 | * number stored in the SPROM. | 465 | * number stored in the SPROM. |
432 | * Always extract r1. */ | 466 | * Always extract r1. */ |
433 | sprom_extract_r1(&out->r1, in); | 467 | out->revision = 1; |
468 | sprom_extract_r123(out, in); | ||
469 | } else if (bus->chip_id == 0x4321) { | ||
470 | /* the BCM4328 has a chipid == 0x4321 and a rev 4 SPROM */ | ||
471 | out->revision = 4; | ||
472 | sprom_extract_r4(out, in); | ||
434 | } else { | 473 | } else { |
435 | if (out->revision == 0) | 474 | if (out->revision == 0) |
436 | goto unsupported; | 475 | goto unsupported; |
437 | if (out->revision >= 1 && out->revision <= 3) | 476 | if (out->revision >= 1 && out->revision <= 3) { |
438 | sprom_extract_r1(&out->r1, in); | 477 | sprom_extract_r123(out, in); |
439 | if (out->revision >= 2 && out->revision <= 3) | 478 | } |
440 | sprom_extract_r2(&out->r2, in); | 479 | if (out->revision == 4) |
441 | if (out->revision == 3) | 480 | sprom_extract_r4(out, in); |
442 | sprom_extract_r3(&out->r3, in); | 481 | if (out->revision >= 5) |
443 | if (out->revision >= 4) | ||
444 | goto unsupported; | 482 | goto unsupported; |
445 | } | 483 | } |
446 | 484 | ||
@@ -448,7 +486,7 @@ static int sprom_extract(struct ssb_bus *bus, | |||
448 | unsupported: | 486 | unsupported: |
449 | ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d " | 487 | ssb_printk(KERN_WARNING PFX "Unsupported SPROM revision %d " |
450 | "detected. Will extract v1\n", out->revision); | 488 | "detected. Will extract v1\n", out->revision); |
451 | sprom_extract_r1(&out->r1, in); | 489 | sprom_extract_r123(out, in); |
452 | return 0; | 490 | return 0; |
453 | } | 491 | } |
454 | 492 | ||
@@ -458,16 +496,29 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus, | |||
458 | int err = -ENOMEM; | 496 | int err = -ENOMEM; |
459 | u16 *buf; | 497 | u16 *buf; |
460 | 498 | ||
461 | buf = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL); | 499 | buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL); |
462 | if (!buf) | 500 | if (!buf) |
463 | goto out; | 501 | goto out; |
502 | bus->sprom_size = SSB_SPROMSIZE_WORDS_R123; | ||
464 | sprom_do_read(bus, buf); | 503 | sprom_do_read(bus, buf); |
465 | err = sprom_check_crc(buf); | 504 | err = sprom_check_crc(buf, bus->sprom_size); |
466 | if (err) { | 505 | if (err) { |
467 | ssb_printk(KERN_WARNING PFX | 506 | /* check for rev 4 sprom - has special signature */ |
468 | "WARNING: Invalid SPROM CRC (corrupt SPROM)\n"); | 507 | if (buf[32] == 0x5372) { |
508 | kfree(buf); | ||
509 | buf = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), | ||
510 | GFP_KERNEL); | ||
511 | if (!buf) | ||
512 | goto out; | ||
513 | bus->sprom_size = SSB_SPROMSIZE_WORDS_R4; | ||
514 | sprom_do_read(bus, buf); | ||
515 | err = sprom_check_crc(buf, bus->sprom_size); | ||
516 | } | ||
517 | if (err) | ||
518 | ssb_printk(KERN_WARNING PFX "WARNING: Invalid" | ||
519 | " SPROM CRC (corrupt SPROM)\n"); | ||
469 | } | 520 | } |
470 | err = sprom_extract(bus, sprom, buf); | 521 | err = sprom_extract(bus, sprom, buf, bus->sprom_size); |
471 | 522 | ||
472 | kfree(buf); | 523 | kfree(buf); |
473 | out: | 524 | out: |
@@ -581,29 +632,28 @@ const struct ssb_bus_ops ssb_pci_ops = { | |||
581 | .write32 = ssb_pci_write32, | 632 | .write32 = ssb_pci_write32, |
582 | }; | 633 | }; |
583 | 634 | ||
584 | static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len) | 635 | static int sprom2hex(const u16 *sprom, char *buf, size_t buf_len, u16 size) |
585 | { | 636 | { |
586 | int i, pos = 0; | 637 | int i, pos = 0; |
587 | 638 | ||
588 | for (i = 0; i < SSB_SPROMSIZE_WORDS; i++) { | 639 | for (i = 0; i < size; i++) |
589 | pos += snprintf(buf + pos, buf_len - pos - 1, | 640 | pos += snprintf(buf + pos, buf_len - pos - 1, |
590 | "%04X", swab16(sprom[i]) & 0xFFFF); | 641 | "%04X", swab16(sprom[i]) & 0xFFFF); |
591 | } | ||
592 | pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); | 642 | pos += snprintf(buf + pos, buf_len - pos - 1, "\n"); |
593 | 643 | ||
594 | return pos + 1; | 644 | return pos + 1; |
595 | } | 645 | } |
596 | 646 | ||
597 | static int hex2sprom(u16 *sprom, const char *dump, size_t len) | 647 | static int hex2sprom(u16 *sprom, const char *dump, size_t len, u16 size) |
598 | { | 648 | { |
599 | char tmp[5] = { 0 }; | 649 | char tmp[5] = { 0 }; |
600 | int cnt = 0; | 650 | int cnt = 0; |
601 | unsigned long parsed; | 651 | unsigned long parsed; |
602 | 652 | ||
603 | if (len < SSB_SPROMSIZE_BYTES * 2) | 653 | if (len < size * 2) |
604 | return -EINVAL; | 654 | return -EINVAL; |
605 | 655 | ||
606 | while (cnt < SSB_SPROMSIZE_WORDS) { | 656 | while (cnt < size) { |
607 | memcpy(tmp, dump, 4); | 657 | memcpy(tmp, dump, 4); |
608 | dump += 4; | 658 | dump += 4; |
609 | parsed = simple_strtoul(tmp, NULL, 16); | 659 | parsed = simple_strtoul(tmp, NULL, 16); |
@@ -627,7 +677,7 @@ static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, | |||
627 | if (!bus) | 677 | if (!bus) |
628 | goto out; | 678 | goto out; |
629 | err = -ENOMEM; | 679 | err = -ENOMEM; |
630 | sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL); | 680 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); |
631 | if (!sprom) | 681 | if (!sprom) |
632 | goto out; | 682 | goto out; |
633 | 683 | ||
@@ -640,7 +690,7 @@ static ssize_t ssb_pci_attr_sprom_show(struct device *pcidev, | |||
640 | sprom_do_read(bus, sprom); | 690 | sprom_do_read(bus, sprom); |
641 | mutex_unlock(&bus->pci_sprom_mutex); | 691 | mutex_unlock(&bus->pci_sprom_mutex); |
642 | 692 | ||
643 | count = sprom2hex(sprom, buf, PAGE_SIZE); | 693 | count = sprom2hex(sprom, buf, PAGE_SIZE, bus->sprom_size); |
644 | err = 0; | 694 | err = 0; |
645 | 695 | ||
646 | out_kfree: | 696 | out_kfree: |
@@ -662,15 +712,15 @@ static ssize_t ssb_pci_attr_sprom_store(struct device *pcidev, | |||
662 | if (!bus) | 712 | if (!bus) |
663 | goto out; | 713 | goto out; |
664 | err = -ENOMEM; | 714 | err = -ENOMEM; |
665 | sprom = kcalloc(SSB_SPROMSIZE_WORDS, sizeof(u16), GFP_KERNEL); | 715 | sprom = kcalloc(bus->sprom_size, sizeof(u16), GFP_KERNEL); |
666 | if (!sprom) | 716 | if (!sprom) |
667 | goto out; | 717 | goto out; |
668 | err = hex2sprom(sprom, buf, count); | 718 | err = hex2sprom(sprom, buf, count, bus->sprom_size); |
669 | if (err) { | 719 | if (err) { |
670 | err = -EINVAL; | 720 | err = -EINVAL; |
671 | goto out_kfree; | 721 | goto out_kfree; |
672 | } | 722 | } |
673 | err = sprom_check_crc(sprom); | 723 | err = sprom_check_crc(sprom, bus->sprom_size); |
674 | if (err) { | 724 | if (err) { |
675 | err = -EINVAL; | 725 | err = -EINVAL; |
676 | goto out_kfree; | 726 | goto out_kfree; |
diff --git a/drivers/ssb/pcmcia.c b/drivers/ssb/pcmcia.c index bb44a76b3eb5..46816cda8b98 100644 --- a/drivers/ssb/pcmcia.c +++ b/drivers/ssb/pcmcia.c | |||
@@ -94,7 +94,6 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus, | |||
94 | struct ssb_device *dev) | 94 | struct ssb_device *dev) |
95 | { | 95 | { |
96 | int err; | 96 | int err; |
97 | unsigned long flags; | ||
98 | 97 | ||
99 | #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG | 98 | #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG |
100 | ssb_printk(KERN_INFO PFX | 99 | ssb_printk(KERN_INFO PFX |
@@ -103,11 +102,9 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus, | |||
103 | dev->core_index); | 102 | dev->core_index); |
104 | #endif | 103 | #endif |
105 | 104 | ||
106 | spin_lock_irqsave(&bus->bar_lock, flags); | ||
107 | err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); | 105 | err = ssb_pcmcia_switch_coreidx(bus, dev->core_index); |
108 | if (!err) | 106 | if (!err) |
109 | bus->mapped_device = dev; | 107 | bus->mapped_device = dev; |
110 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
111 | 108 | ||
112 | return err; | 109 | return err; |
113 | } | 110 | } |
@@ -115,14 +112,12 @@ int ssb_pcmcia_switch_core(struct ssb_bus *bus, | |||
115 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) | 112 | int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) |
116 | { | 113 | { |
117 | int attempts = 0; | 114 | int attempts = 0; |
118 | unsigned long flags; | ||
119 | conf_reg_t reg; | 115 | conf_reg_t reg; |
120 | int res, err = 0; | 116 | int res; |
121 | 117 | ||
122 | SSB_WARN_ON((seg != 0) && (seg != 1)); | 118 | SSB_WARN_ON((seg != 0) && (seg != 1)); |
123 | reg.Offset = 0x34; | 119 | reg.Offset = 0x34; |
124 | reg.Function = 0; | 120 | reg.Function = 0; |
125 | spin_lock_irqsave(&bus->bar_lock, flags); | ||
126 | while (1) { | 121 | while (1) { |
127 | reg.Action = CS_WRITE; | 122 | reg.Action = CS_WRITE; |
128 | reg.Value = seg; | 123 | reg.Value = seg; |
@@ -143,13 +138,11 @@ int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg) | |||
143 | udelay(10); | 138 | udelay(10); |
144 | } | 139 | } |
145 | bus->mapped_pcmcia_seg = seg; | 140 | bus->mapped_pcmcia_seg = seg; |
146 | out_unlock: | 141 | |
147 | spin_unlock_irqrestore(&bus->bar_lock, flags); | 142 | return 0; |
148 | return err; | ||
149 | error: | 143 | error: |
150 | ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); | 144 | ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n"); |
151 | err = -ENODEV; | 145 | return -ENODEV; |
152 | goto out_unlock; | ||
153 | } | 146 | } |
154 | 147 | ||
155 | static int select_core_and_segment(struct ssb_device *dev, | 148 | static int select_core_and_segment(struct ssb_device *dev, |
@@ -182,22 +175,33 @@ static int select_core_and_segment(struct ssb_device *dev, | |||
182 | static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) | 175 | static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset) |
183 | { | 176 | { |
184 | struct ssb_bus *bus = dev->bus; | 177 | struct ssb_bus *bus = dev->bus; |
178 | unsigned long flags; | ||
179 | int err; | ||
180 | u16 value = 0xFFFF; | ||
185 | 181 | ||
186 | if (unlikely(select_core_and_segment(dev, &offset))) | 182 | spin_lock_irqsave(&bus->bar_lock, flags); |
187 | return 0xFFFF; | 183 | err = select_core_and_segment(dev, &offset); |
184 | if (likely(!err)) | ||
185 | value = readw(bus->mmio + offset); | ||
186 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
188 | 187 | ||
189 | return readw(bus->mmio + offset); | 188 | return value; |
190 | } | 189 | } |
191 | 190 | ||
192 | static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) | 191 | static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) |
193 | { | 192 | { |
194 | struct ssb_bus *bus = dev->bus; | 193 | struct ssb_bus *bus = dev->bus; |
195 | u32 lo, hi; | 194 | unsigned long flags; |
195 | int err; | ||
196 | u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF; | ||
196 | 197 | ||
197 | if (unlikely(select_core_and_segment(dev, &offset))) | 198 | spin_lock_irqsave(&bus->bar_lock, flags); |
198 | return 0xFFFFFFFF; | 199 | err = select_core_and_segment(dev, &offset); |
199 | lo = readw(bus->mmio + offset); | 200 | if (likely(!err)) { |
200 | hi = readw(bus->mmio + offset + 2); | 201 | lo = readw(bus->mmio + offset); |
202 | hi = readw(bus->mmio + offset + 2); | ||
203 | } | ||
204 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
201 | 205 | ||
202 | return (lo | (hi << 16)); | 206 | return (lo | (hi << 16)); |
203 | } | 207 | } |
@@ -205,22 +209,31 @@ static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset) | |||
205 | static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) | 209 | static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value) |
206 | { | 210 | { |
207 | struct ssb_bus *bus = dev->bus; | 211 | struct ssb_bus *bus = dev->bus; |
212 | unsigned long flags; | ||
213 | int err; | ||
208 | 214 | ||
209 | if (unlikely(select_core_and_segment(dev, &offset))) | 215 | spin_lock_irqsave(&bus->bar_lock, flags); |
210 | return; | 216 | err = select_core_and_segment(dev, &offset); |
211 | writew(value, bus->mmio + offset); | 217 | if (likely(!err)) |
218 | writew(value, bus->mmio + offset); | ||
219 | mmiowb(); | ||
220 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
212 | } | 221 | } |
213 | 222 | ||
214 | static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) | 223 | static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value) |
215 | { | 224 | { |
216 | struct ssb_bus *bus = dev->bus; | 225 | struct ssb_bus *bus = dev->bus; |
226 | unsigned long flags; | ||
227 | int err; | ||
217 | 228 | ||
218 | if (unlikely(select_core_and_segment(dev, &offset))) | 229 | spin_lock_irqsave(&bus->bar_lock, flags); |
219 | return; | 230 | err = select_core_and_segment(dev, &offset); |
220 | writeb((value & 0xFF000000) >> 24, bus->mmio + offset + 3); | 231 | if (likely(!err)) { |
221 | writeb((value & 0x00FF0000) >> 16, bus->mmio + offset + 2); | 232 | writew((value & 0x0000FFFF), bus->mmio + offset); |
222 | writeb((value & 0x0000FF00) >> 8, bus->mmio + offset + 1); | 233 | writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2); |
223 | writeb((value & 0x000000FF) >> 0, bus->mmio + offset + 0); | 234 | } |
235 | mmiowb(); | ||
236 | spin_unlock_irqrestore(&bus->bar_lock, flags); | ||
224 | } | 237 | } |
225 | 238 | ||
226 | /* Not "static", as it's used in main.c */ | 239 | /* Not "static", as it's used in main.c */ |
@@ -231,10 +244,12 @@ const struct ssb_bus_ops ssb_pcmcia_ops = { | |||
231 | .write32 = ssb_pcmcia_write32, | 244 | .write32 = ssb_pcmcia_write32, |
232 | }; | 245 | }; |
233 | 246 | ||
247 | #include <linux/etherdevice.h> | ||
234 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, | 248 | int ssb_pcmcia_get_invariants(struct ssb_bus *bus, |
235 | struct ssb_init_invariants *iv) | 249 | struct ssb_init_invariants *iv) |
236 | { | 250 | { |
237 | //TODO | 251 | //TODO |
252 | random_ether_addr(iv->sprom.il0mac); | ||
238 | return 0; | 253 | return 0; |
239 | } | 254 | } |
240 | 255 | ||