| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2014-2017 Siemens AG |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include "i210.h" |
| #include <device/device.h> |
| #include <console/console.h> |
| #include <device/pci.h> |
| #include <device/pci_ids.h> |
| #include <device/pci_ops.h> |
| #include <device/pci_def.h> |
| #include <string.h> |
| #include <types.h> |
| #include <delay.h> |
| |
| /* This is a private function to wait for a bit mask in a given register */ |
| /* To avoid endless loops, a time-out is implemented here. */ |
| static int wait_done(uint32_t* reg, uint32_t mask) |
| { |
| uint32_t timeout = I210_POLL_TIMEOUT_US; |
| |
| while (!(*reg & mask)) { |
| udelay(1); |
| if (!--timeout) |
| return I210_NOT_READY; |
| } |
| return I210_SUCCESS; |
| } |
| |
| /** \brief This function can read the configuration space of the MACPHY |
| * For this purpose, the EEPROM interface is used. No direct access |
| * to the flash memory will be done. |
| * @param *dev Pointer to the PCI device of this MACPHY |
| * @param address Address inside the flash where reading will start |
| * @param count Number of words (16 bit values) to read |
| * @param *buffer Pointer to the buffer where to store read data |
| * @return void I210_NO_ERROR or an error code |
| */ |
| static uint32_t read_flash(struct device *dev, uint32_t address, |
| uint32_t count, uint16_t *buffer) |
| { |
| uint32_t bar; |
| uint32_t *eeprd; |
| uint32_t i; |
| |
| /* Get the BAR to memory mapped space*/ |
| bar = pci_read_config32(dev, PCI_BASE_ADDRESS_0); |
| if ((!bar) || ((address + count) > 0x40)) |
| return I210_INVALID_PARAM; |
| eeprd = (uint32_t*)(bar + I210_REG_EEREAD); |
| /* Prior to start ensure flash interface is ready by checking DONE-bit */ |
| if (wait_done(eeprd, I210_DONE)) |
| return I210_NOT_READY; |
| |
| /*OK, interface is ready, we can use it now */ |
| for (i = 0; i < count; i++) { |
| /* To start a read cycle write desired address in bits 12..2 */ |
| *eeprd = ((address + i) << 2) & 0x1FFC; |
| /* Wait until read is done */ |
| if (wait_done(eeprd, I210_DONE)) |
| return I210_READ_ERROR; |
| /* Here, we can read back desired word in bits 31..16 */ |
| buffer[i] = (*eeprd & 0xffff0000) >> 16; |
| } |
| return I210_SUCCESS; |
| } |
| |
| /** \brief This function computes the checksum for the configuration space. |
| * The address range for the checksum is 0x00..0x3e. |
| * @param *dev Pointer to the PCI device of this MACPHY |
| * @param *checksum Pointer to the buffer where to store the checksum |
| * @return void I210_NO_ERROR or an error code |
| */ |
| static uint32_t compute_checksum(struct device *dev, uint16_t *checksum) |
| { |
| uint16_t eep_data[0x40]; |
| uint32_t i; |
| |
| /* First read back data to compute the checksum for */ |
| if (read_flash(dev, 0, 0x3f, eep_data)) |
| return I210_READ_ERROR; |
| /* The checksum is computed in that way that after summarize all the */ |
| /* data from word address 0 to 0x3f the result is 0xBABA. */ |
| *checksum = 0; |
| for (i = 0; i < 0x3f; i++) |
| *checksum += eep_data[i]; |
| *checksum = I210_TARGET_CHECKSUM - *checksum; |
| return I210_SUCCESS; |
| } |
| |
| /** \brief This function can write the configuration space of the MACPHY |
| * For this purpose, the EEPROM interface is used. No direct access |
| * to the flash memory will be done. This function will update |
| * the checksum after a value was changed. |
| * @param *dev Pointer to the PCI device of this MACPHY |
| * @param address Address inside the flash where writing will start |
| * @param count Number of words (16 bit values) to write |
| * @param *buffer Pointer to the buffer where data to write is stored in |
| * @return void I210_NO_ERROR or an error code |
| */ |
| static uint32_t write_flash(struct device *dev, uint32_t address, |
| uint32_t count, uint16_t *buffer) |
| { |
| uint32_t bar; |
| uint32_t *eepwr; |
| uint32_t *eectrl; |
| uint16_t checksum; |
| uint32_t i; |
| |
| /* Get the BAR to memory mapped space */ |
| bar = pci_read_config32(dev, 0x10); |
| if ((!bar) || ((address + count) > 0x40)) |
| return I210_INVALID_PARAM; |
| eepwr = (uint32_t*)(bar + I210_REG_EEWRITE); |
| eectrl = (uint32_t*)(bar + I210_REG_EECTRL); |
| /* Prior to start ensure flash interface is ready by checking DONE-bit */ |
| if (wait_done(eepwr, I210_DONE)) |
| return I210_NOT_READY; |
| |
| /* OK, interface is ready, we can use it now */ |
| for (i = 0; i < count; i++) { |
| /* To start a write cycle write desired address in bits 12..2 */ |
| /* and data to write in bits 31..16 into EEWRITE-register */ |
| *eepwr = ((((address + i) << 2) & 0x1FFC) | (buffer[i] << 16)); |
| /* Wait until write is done */ |
| if (wait_done(eepwr, I210_DONE)) |
| return I210_WRITE_ERROR; |
| } |
| /* Since we have modified data, we need to update the checksum */ |
| if (compute_checksum(dev, &checksum)) |
| return I210_CHECKSUM_ERROR; |
| *eepwr = (0x3f << 2) | checksum << 16; |
| if (wait_done(eepwr, I210_DONE)) |
| return I210_WRITE_ERROR; |
| /* Up to now, desired data was written into shadowed RAM. We now need */ |
| /* to perform a flash cycle to bring the shadowed RAM into flash. */ |
| /* To start a flash cycle we need to set FLUPD and wait for FLDONE. */ |
| *eectrl = *eectrl | I210_FLUPD; |
| if (wait_done(eectrl, I210_FLUDONE)) |
| return I210_FLASH_UPDATE_ERROR; |
| return I210_SUCCESS; |
| } |
| |
| /** \brief This function can read the MAC address out of the MACPHY |
| * @param *dev Pointer to the PCI device of this MACPHY |
| * @param *MACAdr Pointer to the buffer where to store read MAC address |
| * @return void I210_NO_ERROR or an error code |
| */ |
| static uint32_t read_mac_adr(struct device *dev, uint8_t *mac_adr) |
| { |
| uint16_t adr[3]; |
| if (!dev || !mac_adr) |
| return I210_INVALID_PARAM; |
| if (read_flash(dev, 0, 3, adr)) |
| return I210_READ_ERROR; |
| /* Copy the address into destination. This is done because of possible */ |
| /* not matching alignment for destination to uint16_t boundary. */ |
| memcpy(mac_adr, (uint8_t*)adr, 6); |
| return I210_SUCCESS; |
| } |
| |
| /** \brief This function can write the MAC address to the MACPHY |
| * @param *dev Pointer to the PCI device of this MACPHY |
| * @param *MACAdr Pointer to the buffer where the desired MAC address is |
| * @return void I210_NO_ERROR or an error code |
| */ |
| static uint32_t write_mac_adr(struct device *dev, uint8_t *mac_adr) |
| { |
| uint16_t adr[3]; |
| if (!dev || !mac_adr) |
| return I210_INVALID_PARAM; |
| /* Copy desired address into a local buffer to avoid alignment issues */ |
| memcpy((uint8_t*)adr, mac_adr, 6); |
| return write_flash(dev, 0, 3, adr); |
| } |
| |
| /** \brief This function is the driver entry point for the init phase |
| * of the PCI bus allocator. It will program a MAC address |
| * into the MACPHY. |
| * @param *dev Pointer to the used PCI device |
| * @return void Nothing is given back |
| */ |
| static void init(struct device *dev) |
| { |
| uint8_t cur_adr[6]; |
| uint8_t adr_to_set[6]; |
| enum cb_err status; |
| |
| /*Check first whether there is a valid MAC address available */ |
| status = mainboard_get_mac_address(dev, adr_to_set); |
| if (status != CB_SUCCESS) { |
| printk(BIOS_ERR, "I210: No valid MAC address found\n"); |
| return; |
| } |
| /* Before we will write a new address, check the existing one */ |
| if (read_mac_adr(dev, cur_adr)) { |
| printk(BIOS_ERR, "I210: Not able to read MAC address.\n"); |
| return; |
| } |
| if (memcmp(cur_adr, adr_to_set, 6)) { |
| if (write_mac_adr(dev, adr_to_set)) |
| printk(BIOS_ERR, "I210: Error setting MAC address\n"); |
| else |
| printk(BIOS_INFO, "I210: MAC address changed.\n"); |
| } else { |
| printk(BIOS_INFO, "I210: MAC address is up to date.\n"); |
| } |
| return; |
| } |
| |
| static void set_resources(struct device *dev) |
| { |
| pci_dev_set_resources(dev); |
| dev->command |= PCI_COMMAND_MASTER; |
| } |
| |
| static struct device_operations i210_ops = { |
| .read_resources = pci_dev_read_resources, |
| .set_resources = set_resources, |
| .enable_resources = pci_dev_enable_resources, |
| .init = init, |
| .scan_bus = 0, |
| .ops_pci = 0, |
| }; |
| |
| static const unsigned short i210_device_ids[] = { 0x1537, 0x1538, 0x1533, 0 }; |
| |
| static const struct pci_driver i210_driver __pci_driver = { |
| .ops = &i210_ops, |
| .vendor = PCI_VENDOR_ID_INTEL, |
| .devices = i210_device_ids, |
| }; |