Intel 8254x: Difference between revisions

Jump to navigation Jump to search
Initializing, EEPROM and obtaining the MAC address in the i8254x
[unchecked revision][unchecked revision]
m (Correct url)
(Initializing, EEPROM and obtaining the MAC address in the i8254x)
Line 30:
 
<source lang="c">
*(uint32_t *)(ioaddr + reg) = val; // writes "val" to an MMIO address
val = *(uint32_t *)(ioaddr + reg); // reads "val" from an MMIO address
</source>
 
Line 39:
 
<source lang="c">
out32outl(ioaddr + 0x00, reg); // set the IOADDR window
out32outl(ioaddr + 0x04, val); // write the value to the IOADDR window which will end up in the register in IOADDR
in32inl(ioaddr + 0x04); // read back the value
</source>
 
== Initialization ==
The 8254x will be on an undefined state and as such it needs to be reset. The first thing that should be done is enabling bus mastering, memory and IO accesses from the [[PCI]] command register.
 
Then the NIC should be reset by setting CTRL.RST (0x4000000) bit in the CTRL (0x00000) register of the card.
 
== EEPROM Reading ==
There are a few variants of the card with many differences, most notably the method to access the EEPROM and the Flash memory of the card. Here we will only describe methods applicable to cards that use the EEPROM method.
 
After that the EEPROM must be enabled in order to be able to read the MAC address of the NIC, this is done by setting the EECD.SK (0x01), EECD.CS (0x02) and EECD.DI (0x03) bits of the EECD (0x00010) register. This will allow software to perform reads to the EEPROM.
 
Before reading the EEPROM has a "lock-unlock" mechanism to prevent software-hardware collisions when reading from the EEPROM.
 
To lock the EEPROM the EECD.REQ (0x40) bit must be set in the EECD register. Then wait until the EECD.GNT (0x80) bit becomes set.
Unlocking only requires to clear EECD.REQ.
 
To finally read the EEPROM first the kernel should AND the address to 12 (Applicable only to 82541x or 82547GI/EI cards) or 8 bits; then bit shift the desired address to 2 (Applicable only to 82541x or 82547GI/EI cards) or by 4. The kernel must OR it with the EECD.START (0x01) bit. Then finally write it to the EERD (0x00014) register.
 
The kernel should wait until the EEPROM read operation is finished by checking until EECD.DONE becomes clear. Then the kernel must read the EERD register, shift it to the right by 16 bits and truncate it to 16-bits.
 
After that the EERD.START bit must be cleared.
 
<source lang="c">
static uint16_t i8254x_read_eeprom(uint8_t addr) {
uint32_t tmp;
uint16_t data;
 
if((le32_to_cpu(mmio_read_dword(dev_info.mmio.addr, I8254X_EECD)) & I8254X_EECD_EE_PRES) == 0) {
kpanic("EEPROM present bit is not set for i8254x\n");
}
 
/* Tell the EEPROM to start reading */
if(dev_info.version == I82547GI_EI
|| dev_info.version == I82541EI_A0
|| dev_info.version == I82541EI_B0
|| dev_info.version == I82541ER_C0
|| dev_info.version == I82541GI_B1
|| dev_info.version == I82541PI_C0) {
/* Specification says that only 82541x devices and the
* 82547GI/EI do 2-bit shift */
tmp = ((uint32_t)addr & 0xfff) << 2;
} else {
tmp = ((uint32_t)addr & 0xff) << 8;
}
tmp |= I8254X_EERD_START;
mmio_write_dword(dev_info.mmio.addr, I8254X_EERD, cpu_to_le32(tmp));
 
/* Wait until the read is finished - then the DONE bit is cleared */
timeout((le32_to_cpu(mmio_read_dword(dev_info.mmio.addr, I8254X_EERD)) & I8254X_EERD_DONE) == 0, 100);
 
/* Obtain the data */
data = (uint16_t)(le32_to_cpu(mmio_read_dword(dev_info.mmio.addr, I8254X_EERD)) >> 16);
 
/* Tell EEPROM to stop reading */
tmp = le32_to_cpu(mmio_read_dword(dev_info.mmio.addr, I8254X_EERD));
tmp &= ~(uint32_t)I8254X_EERD_START;
mmio_write_dword(dev_info.mmio.addr, I8254X_EERD, cpu_to_le32(tmp));
return data;
}
</source>
 
When all data is finally read the kernel should unlock the EEPROM to let hardware access it.
 
== Obtaining the MAC address ==
Obtaining the MAC is quite trivial and only requires reading the first 3-words of the EEPROM.
 
<source lang="c">
uint8_t dev_info.mac_addr[6];
 
/* Assumes a little-endian architecture */
i8254x_lock_eeprom();
*((uint16_t *)&dev_info.mac_addr[0]) = i8254x_read_eeprom(0x00);
*((uint16_t *)&dev_info.mac_addr[2]) = i8254x_read_eeprom(0x01);
*((uint16_t *)&dev_info.mac_addr[4]) = i8254x_read_eeprom(0x02);
i8254x_unlock_eeprom();
</source>
 
170

edits

Cookies help us deliver our services. By using our services, you agree to our use of cookies.

Navigation menu