1 /* 2 * Broadcom specific AMBA 3 * SPROM reading 4 * 5 * Copyright 2011, 2012, Hauke Mehrtens <hauke@hauke-m.de> 6 * 7 * Licensed under the GNU/GPL. See COPYING for details. 8 */ 9 10 #include "bcma_private.h" 11 12 #include <linux/bcma/bcma.h> 13 #include <linux/bcma/bcma_regs.h> 14 #include <linux/pci.h> 15 #include <linux/io.h> 16 #include <linux/dma-mapping.h> 17 #include <linux/slab.h> 18 19 static int(*get_fallback_sprom)(struct bcma_bus *dev, struct ssb_sprom *out); 20 21 /** 22 * bcma_arch_register_fallback_sprom - Registers a method providing a 23 * fallback SPROM if no SPROM is found. 24 * 25 * @sprom_callback: The callback function. 26 * 27 * With this function the architecture implementation may register a 28 * callback handler which fills the SPROM data structure. The fallback is 29 * used for PCI based BCMA devices, where no valid SPROM can be found 30 * in the shadow registers and to provide the SPROM for SoCs where BCMA is 31 * to controll the system bus. 32 * 33 * This function is useful for weird architectures that have a half-assed 34 * BCMA device hardwired to their PCI bus. 35 * 36 * This function is available for architecture code, only. So it is not 37 * exported. 38 */ 39 int bcma_arch_register_fallback_sprom(int (*sprom_callback)(struct bcma_bus *bus, 40 struct ssb_sprom *out)) 41 { 42 if (get_fallback_sprom) 43 return -EEXIST; 44 get_fallback_sprom = sprom_callback; 45 46 return 0; 47 } 48 49 static int bcma_fill_sprom_with_fallback(struct bcma_bus *bus, 50 struct ssb_sprom *out) 51 { 52 int err; 53 54 if (!get_fallback_sprom) { 55 err = -ENOENT; 56 goto fail; 57 } 58 59 err = get_fallback_sprom(bus, out); 60 if (err) 61 goto fail; 62 63 pr_debug("Using SPROM revision %d provided by" 64 " platform.\n", bus->sprom.revision); 65 return 0; 66 fail: 67 pr_warn("Using fallback SPROM failed (err %d)\n", err); 68 return err; 69 } 70 71 /************************************************** 72 * R/W ops. 73 **************************************************/ 74 75 static void bcma_sprom_read(struct bcma_bus *bus, u16 offset, u16 *sprom) 76 { 77 int i; 78 for (i = 0; i < SSB_SPROMSIZE_WORDS_R4; i++) 79 sprom[i] = bcma_read16(bus->drv_cc.core, 80 offset + (i * 2)); 81 } 82 83 /************************************************** 84 * Validation. 85 **************************************************/ 86 87 static inline u8 bcma_crc8(u8 crc, u8 data) 88 { 89 /* Polynomial: x^8 + x^7 + x^6 + x^4 + x^2 + 1 */ 90 static const u8 t[] = { 91 0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B, 92 0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21, 93 0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF, 94 0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5, 95 0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14, 96 0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E, 97 0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80, 98 0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA, 99 0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95, 100 0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF, 101 0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01, 102 0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B, 103 0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA, 104 0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0, 105 0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E, 106 0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34, 107 0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0, 108 0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A, 109 0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54, 110 0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E, 111 0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF, 112 0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5, 113 0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B, 114 0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61, 115 0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E, 116 0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74, 117 0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA, 118 0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0, 119 0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41, 120 0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B, 121 0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5, 122 0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F, 123 }; 124 return t[crc ^ data]; 125 } 126 127 static u8 bcma_sprom_crc(const u16 *sprom) 128 { 129 int word; 130 u8 crc = 0xFF; 131 132 for (word = 0; word < SSB_SPROMSIZE_WORDS_R4 - 1; word++) { 133 crc = bcma_crc8(crc, sprom[word] & 0x00FF); 134 crc = bcma_crc8(crc, (sprom[word] & 0xFF00) >> 8); 135 } 136 crc = bcma_crc8(crc, sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 0x00FF); 137 crc ^= 0xFF; 138 139 return crc; 140 } 141 142 static int bcma_sprom_check_crc(const u16 *sprom) 143 { 144 u8 crc; 145 u8 expected_crc; 146 u16 tmp; 147 148 crc = bcma_sprom_crc(sprom); 149 tmp = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_CRC; 150 expected_crc = tmp >> SSB_SPROM_REVISION_CRC_SHIFT; 151 if (crc != expected_crc) 152 return -EPROTO; 153 154 return 0; 155 } 156 157 static int bcma_sprom_valid(const u16 *sprom) 158 { 159 u16 revision; 160 int err; 161 162 err = bcma_sprom_check_crc(sprom); 163 if (err) 164 return err; 165 166 revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & SSB_SPROM_REVISION_REV; 167 if (revision != 8 && revision != 9) { 168 pr_err("Unsupported SPROM revision: %d\n", revision); 169 return -ENOENT; 170 } 171 172 return 0; 173 } 174 175 /************************************************** 176 * SPROM extraction. 177 **************************************************/ 178 179 #define SPOFF(offset) ((offset) / sizeof(u16)) 180 181 #define SPEX(_field, _offset, _mask, _shift) \ 182 bus->sprom._field = ((sprom[SPOFF(_offset)] & (_mask)) >> (_shift)) 183 184 static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) 185 { 186 u16 v, o; 187 int i; 188 u16 pwr_info_offset[] = { 189 SSB_SROM8_PWR_INFO_CORE0, SSB_SROM8_PWR_INFO_CORE1, 190 SSB_SROM8_PWR_INFO_CORE2, SSB_SROM8_PWR_INFO_CORE3 191 }; 192 BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) != 193 ARRAY_SIZE(bus->sprom.core_pwr_info)); 194 195 bus->sprom.revision = sprom[SSB_SPROMSIZE_WORDS_R4 - 1] & 196 SSB_SPROM_REVISION_REV; 197 198 for (i = 0; i < 3; i++) { 199 v = sprom[SPOFF(SSB_SPROM8_IL0MAC) + i]; 200 *(((__be16 *)bus->sprom.il0mac) + i) = cpu_to_be16(v); 201 } 202 203 SPEX(board_rev, SSB_SPROM8_BOARDREV, ~0, 0); 204 205 SPEX(txpid2g[0], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G0, 206 SSB_SPROM4_TXPID2G0_SHIFT); 207 SPEX(txpid2g[1], SSB_SPROM4_TXPID2G01, SSB_SPROM4_TXPID2G1, 208 SSB_SPROM4_TXPID2G1_SHIFT); 209 SPEX(txpid2g[2], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G2, 210 SSB_SPROM4_TXPID2G2_SHIFT); 211 SPEX(txpid2g[3], SSB_SPROM4_TXPID2G23, SSB_SPROM4_TXPID2G3, 212 SSB_SPROM4_TXPID2G3_SHIFT); 213 214 SPEX(txpid5gl[0], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL0, 215 SSB_SPROM4_TXPID5GL0_SHIFT); 216 SPEX(txpid5gl[1], SSB_SPROM4_TXPID5GL01, SSB_SPROM4_TXPID5GL1, 217 SSB_SPROM4_TXPID5GL1_SHIFT); 218 SPEX(txpid5gl[2], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL2, 219 SSB_SPROM4_TXPID5GL2_SHIFT); 220 SPEX(txpid5gl[3], SSB_SPROM4_TXPID5GL23, SSB_SPROM4_TXPID5GL3, 221 SSB_SPROM4_TXPID5GL3_SHIFT); 222 223 SPEX(txpid5g[0], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G0, 224 SSB_SPROM4_TXPID5G0_SHIFT); 225 SPEX(txpid5g[1], SSB_SPROM4_TXPID5G01, SSB_SPROM4_TXPID5G1, 226 SSB_SPROM4_TXPID5G1_SHIFT); 227 SPEX(txpid5g[2], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G2, 228 SSB_SPROM4_TXPID5G2_SHIFT); 229 SPEX(txpid5g[3], SSB_SPROM4_TXPID5G23, SSB_SPROM4_TXPID5G3, 230 SSB_SPROM4_TXPID5G3_SHIFT); 231 232 SPEX(txpid5gh[0], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH0, 233 SSB_SPROM4_TXPID5GH0_SHIFT); 234 SPEX(txpid5gh[1], SSB_SPROM4_TXPID5GH01, SSB_SPROM4_TXPID5GH1, 235 SSB_SPROM4_TXPID5GH1_SHIFT); 236 SPEX(txpid5gh[2], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH2, 237 SSB_SPROM4_TXPID5GH2_SHIFT); 238 SPEX(txpid5gh[3], SSB_SPROM4_TXPID5GH23, SSB_SPROM4_TXPID5GH3, 239 SSB_SPROM4_TXPID5GH3_SHIFT); 240 241 SPEX(boardflags_lo, SSB_SPROM8_BFLLO, ~0, 0); 242 SPEX(boardflags_hi, SSB_SPROM8_BFLHI, ~0, 0); 243 SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0); 244 SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0); 245 246 SPEX(country_code, SSB_SPROM8_CCODE, ~0, 0); 247 248 /* Extract cores power info info */ 249 for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) { 250 o = pwr_info_offset[i]; 251 SPEX(core_pwr_info[i].itssi_2g, o + SSB_SROM8_2G_MAXP_ITSSI, 252 SSB_SPROM8_2G_ITSSI, SSB_SPROM8_2G_ITSSI_SHIFT); 253 SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SROM8_2G_MAXP_ITSSI, 254 SSB_SPROM8_2G_MAXP, 0); 255 256 SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SROM8_2G_PA_0, ~0, 0); 257 SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SROM8_2G_PA_1, ~0, 0); 258 SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SROM8_2G_PA_2, ~0, 0); 259 260 SPEX(core_pwr_info[i].itssi_5g, o + SSB_SROM8_5G_MAXP_ITSSI, 261 SSB_SPROM8_5G_ITSSI, SSB_SPROM8_5G_ITSSI_SHIFT); 262 SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SROM8_5G_MAXP_ITSSI, 263 SSB_SPROM8_5G_MAXP, 0); 264 SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM8_5GHL_MAXP, 265 SSB_SPROM8_5GH_MAXP, 0); 266 SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM8_5GHL_MAXP, 267 SSB_SPROM8_5GL_MAXP, SSB_SPROM8_5GL_MAXP_SHIFT); 268 269 SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SROM8_5GL_PA_0, ~0, 0); 270 SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SROM8_5GL_PA_1, ~0, 0); 271 SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SROM8_5GL_PA_2, ~0, 0); 272 SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SROM8_5G_PA_0, ~0, 0); 273 SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SROM8_5G_PA_1, ~0, 0); 274 SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SROM8_5G_PA_2, ~0, 0); 275 SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SROM8_5GH_PA_0, ~0, 0); 276 SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SROM8_5GH_PA_1, ~0, 0); 277 SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SROM8_5GH_PA_2, ~0, 0); 278 } 279 280 SPEX(fem.ghz2.tssipos, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TSSIPOS, 281 SSB_SROM8_FEM_TSSIPOS_SHIFT); 282 SPEX(fem.ghz2.extpa_gain, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_EXTPA_GAIN, 283 SSB_SROM8_FEM_EXTPA_GAIN_SHIFT); 284 SPEX(fem.ghz2.pdet_range, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_PDET_RANGE, 285 SSB_SROM8_FEM_PDET_RANGE_SHIFT); 286 SPEX(fem.ghz2.tr_iso, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_TR_ISO, 287 SSB_SROM8_FEM_TR_ISO_SHIFT); 288 SPEX(fem.ghz2.antswlut, SSB_SPROM8_FEM2G, SSB_SROM8_FEM_ANTSWLUT, 289 SSB_SROM8_FEM_ANTSWLUT_SHIFT); 290 291 SPEX(fem.ghz5.tssipos, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TSSIPOS, 292 SSB_SROM8_FEM_TSSIPOS_SHIFT); 293 SPEX(fem.ghz5.extpa_gain, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_EXTPA_GAIN, 294 SSB_SROM8_FEM_EXTPA_GAIN_SHIFT); 295 SPEX(fem.ghz5.pdet_range, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_PDET_RANGE, 296 SSB_SROM8_FEM_PDET_RANGE_SHIFT); 297 SPEX(fem.ghz5.tr_iso, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_TR_ISO, 298 SSB_SROM8_FEM_TR_ISO_SHIFT); 299 SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT, 300 SSB_SROM8_FEM_ANTSWLUT_SHIFT); 301 } 302 303 /* 304 * Indicates the presence of external SPROM. 305 */ 306 static bool bcma_sprom_ext_available(struct bcma_bus *bus) 307 { 308 u32 chip_status; 309 u32 srom_control; 310 u32 present_mask; 311 312 if (bus->drv_cc.core->id.rev >= 31) { 313 if (!(bus->drv_cc.capabilities & BCMA_CC_CAP_SPROM)) 314 return false; 315 316 srom_control = bcma_read32(bus->drv_cc.core, 317 BCMA_CC_SROM_CONTROL); 318 return srom_control & BCMA_CC_SROM_CONTROL_PRESENT; 319 } 320 321 /* older chipcommon revisions use chip status register */ 322 chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); 323 switch (bus->chipinfo.id) { 324 case 0x4313: 325 present_mask = BCMA_CC_CHIPST_4313_SPROM_PRESENT; 326 break; 327 328 case 0x4331: 329 present_mask = BCMA_CC_CHIPST_4331_SPROM_PRESENT; 330 break; 331 332 default: 333 return true; 334 } 335 336 return chip_status & present_mask; 337 } 338 339 /* 340 * Indicates that on-chip OTP memory is present and enabled. 341 */ 342 static bool bcma_sprom_onchip_available(struct bcma_bus *bus) 343 { 344 u32 chip_status; 345 u32 otpsize = 0; 346 bool present; 347 348 chip_status = bcma_read32(bus->drv_cc.core, BCMA_CC_CHIPSTAT); 349 switch (bus->chipinfo.id) { 350 case 0x4313: 351 present = chip_status & BCMA_CC_CHIPST_4313_OTP_PRESENT; 352 break; 353 354 case 0x4331: 355 present = chip_status & BCMA_CC_CHIPST_4331_OTP_PRESENT; 356 break; 357 358 case 43224: 359 case 43225: 360 /* for these chips OTP is always available */ 361 present = true; 362 break; 363 364 default: 365 present = false; 366 break; 367 } 368 369 if (present) { 370 otpsize = bus->drv_cc.capabilities & BCMA_CC_CAP_OTPS; 371 otpsize >>= BCMA_CC_CAP_OTPS_SHIFT; 372 } 373 374 return otpsize != 0; 375 } 376 377 /* 378 * Verify OTP is filled and determine the byte 379 * offset where SPROM data is located. 380 * 381 * On error, returns 0; byte offset otherwise. 382 */ 383 static int bcma_sprom_onchip_offset(struct bcma_bus *bus) 384 { 385 struct bcma_device *cc = bus->drv_cc.core; 386 u32 offset; 387 388 /* verify OTP status */ 389 if ((bcma_read32(cc, BCMA_CC_OTPS) & BCMA_CC_OTPS_GU_PROG_HW) == 0) 390 return 0; 391 392 /* obtain bit offset from otplayout register */ 393 offset = (bcma_read32(cc, BCMA_CC_OTPL) & BCMA_CC_OTPL_GURGN_OFFSET); 394 return BCMA_CC_SPROM + (offset >> 3); 395 } 396 397 int bcma_sprom_get(struct bcma_bus *bus) 398 { 399 u16 offset = BCMA_CC_SPROM; 400 u16 *sprom; 401 int err = 0; 402 403 if (!bus->drv_cc.core) 404 return -EOPNOTSUPP; 405 406 if (!bcma_sprom_ext_available(bus)) { 407 bool sprom_onchip; 408 409 /* 410 * External SPROM takes precedence so check 411 * on-chip OTP only when no external SPROM 412 * is present. 413 */ 414 sprom_onchip = bcma_sprom_onchip_available(bus); 415 if (sprom_onchip) { 416 /* determine offset */ 417 offset = bcma_sprom_onchip_offset(bus); 418 } 419 if (!offset || !sprom_onchip) { 420 /* 421 * Maybe there is no SPROM on the device? 422 * Now we ask the arch code if there is some sprom 423 * available for this device in some other storage. 424 */ 425 err = bcma_fill_sprom_with_fallback(bus, &bus->sprom); 426 return err; 427 } 428 } 429 430 sprom = kcalloc(SSB_SPROMSIZE_WORDS_R4, sizeof(u16), 431 GFP_KERNEL); 432 if (!sprom) 433 return -ENOMEM; 434 435 if (bus->chipinfo.id == 0x4331) 436 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, false); 437 438 pr_debug("SPROM offset 0x%x\n", offset); 439 bcma_sprom_read(bus, offset, sprom); 440 441 if (bus->chipinfo.id == 0x4331) 442 bcma_chipco_bcm4331_ext_pa_lines_ctl(&bus->drv_cc, true); 443 444 err = bcma_sprom_valid(sprom); 445 if (err) 446 goto out; 447 448 bcma_sprom_extract_r8(bus, sprom); 449 450 out: 451 kfree(sprom); 452 return err; 453 } 454