diff options
author | Werner Zeh <werner.zeh@siemens.com> | 2015-02-13 12:18:58 +0100 |
---|---|---|
committer | Alexandru Gagniuc <mr.nuke.me@gmail.com> | 2015-02-24 07:08:11 +0100 |
commit | 63693dca06314d44601c9dec713290c694719935 (patch) | |
tree | e59d7a73900f726f004cb2dd634493e15b04dd8e /src/drivers/intel | |
parent | 5b4b024af2159d8a3ea3d6c9cf1eb5cf4cd0ac45 (diff) |
drivers/intel/i210: Add new driver for Intel i210 MACPHY
Add a new driver for Intel i210 MACPHY with the goal to
update the MAC address in i210 if it is found
during PCI scan.
Change-Id: I4d4e797543a9f278fb649596f63ae8e1f285b3c3
Signed-off-by: Werner Zeh <werner.zeh@siemens.com>
Reviewed-on: http://review.coreboot.org/8404
Reviewed-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/drivers/intel')
-rw-r--r-- | src/drivers/intel/Kconfig | 1 | ||||
-rw-r--r-- | src/drivers/intel/Makefile.inc | 1 | ||||
-rw-r--r-- | src/drivers/intel/i210/Kconfig | 3 | ||||
-rw-r--r-- | src/drivers/intel/i210/Makefile.inc | 1 | ||||
-rw-r--r-- | src/drivers/intel/i210/i210.c | 243 | ||||
-rw-r--r-- | src/drivers/intel/i210/i210.h | 45 |
6 files changed, 294 insertions, 0 deletions
diff --git a/src/drivers/intel/Kconfig b/src/drivers/intel/Kconfig index 9954f31e03..511cf5c186 100644 --- a/src/drivers/intel/Kconfig +++ b/src/drivers/intel/Kconfig @@ -18,3 +18,4 @@ ## source src/drivers/intel/gma/Kconfig +source src/drivers/intel/i210/Kconfig diff --git a/src/drivers/intel/Makefile.inc b/src/drivers/intel/Makefile.inc index 7bc6dd5100..dba81b553d 100644 --- a/src/drivers/intel/Makefile.inc +++ b/src/drivers/intel/Makefile.inc @@ -1,3 +1,4 @@ subdirs-y += gma subdirs-y += wifi subdirs-$(CONFIG_PLATFORM_USES_FSP) += fsp +subdirs-$(CONFIG_DRIVER_INTEL_I210) += i210 diff --git a/src/drivers/intel/i210/Kconfig b/src/drivers/intel/i210/Kconfig new file mode 100644 index 0000000000..0191169a0d --- /dev/null +++ b/src/drivers/intel/i210/Kconfig @@ -0,0 +1,3 @@ +config DRIVER_INTEL_I210 + bool + default n diff --git a/src/drivers/intel/i210/Makefile.inc b/src/drivers/intel/i210/Makefile.inc new file mode 100644 index 0000000000..a1f15de1c1 --- /dev/null +++ b/src/drivers/intel/i210/Makefile.inc @@ -0,0 +1 @@ +ramstage-y += i210.c
\ No newline at end of file diff --git a/src/drivers/intel/i210/i210.c b/src/drivers/intel/i210/i210.c new file mode 100644 index 0000000000..cdb76e436d --- /dev/null +++ b/src/drivers/intel/i210/i210.c @@ -0,0 +1,243 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#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 <string.h> +#include <types.h> +#include <delay.h> + +/* We need one function we can call to get a MAC address to use */ +/* This function can be coded somewhere else but must exist. */ +extern enum cb_err mainboard_get_mac_address(u16 bus, u8 devfn, u8 mac[6]); + +/* 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(u32* reg, u32 mask) +{ + u32 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 u32 read_flash(struct device *dev, u32 address, u32 count, u16 *buffer) +{ + u32 bar; + u32 *eeprd; + u32 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 = (u32*)(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 u32 compute_checksum(struct device *dev, u16 *checksum) +{ + u16 eep_data[0x40]; + u32 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 u32 write_flash(struct device *dev, u32 address, u32 count, u16 *buffer) +{ + u32 bar; + u32 *eepwr; + u32 *eectrl; + u16 checksum; + u32 i; + + /* Get the BAR to memory mapped space */ + bar = pci_read_config32(dev, 0x10); + if ((!bar) || ((address + count) > 0x40)) + return I210_INVALID_PARAM; + eepwr = (u32*)(bar + I210_REG_EEWRITE); + eectrl = (u32*)(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 memory. */ + /* To start a flash cycle we need to set FLUPD-bit 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 u32 read_mac_adr(struct device *dev, u8 *mac_adr) +{ + u16 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 u16 boundary. */ + memcpy(mac_adr, (u8*)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 u32 write_mac_adr(struct device *dev, u8 *mac_adr) +{ + u16 adr[3]; + if (!dev || !mac_adr) + return I210_INVALID_PARAM; + /* Copy desired address into a local buffer to avoid alignment issues */ + memcpy((u8*)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) +{ + u8 cur_adr[6]; + u8 adr_to_set[6]; + enum cb_err status; + + /*Check first whether there is a valid MAC address available */ + status = mainboard_get_mac_address(dev->bus->subordinate, + dev->path.pci.devfn, 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 struct device_operations i210_ops = { + .read_resources = pci_dev_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_dev_enable_resources, + .init = init, + .scan_bus = 0, + .ops_pci = 0, +}; + +static const unsigned short i210_device_ids[] = { 0x1538, 0x1533, 0 }; + +static const struct pci_driver i210_driver __pci_driver = { + .ops = &i210_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = i210_device_ids, +}; diff --git a/src/drivers/intel/i210/i210.h b/src/drivers/intel/i210/i210.h new file mode 100644 index 0000000000..b4f18ecd1b --- /dev/null +++ b/src/drivers/intel/i210/i210.h @@ -0,0 +1,45 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _INTEL_I210_H_ +#define _INTEL_I210_H_ + +#define I210_PCI_MEM_BAR_OFFSET 0x10 +#define I210_REG_EECTRL 0x12010 /* Offset for EEPROM control reg */ +#define I210_FLUPD 0x800000 /* Start flash update bit */ +#define I210_FLUDONE 0x4000000 /* Flash update done indicator */ +#define I210_REG_EEREAD 0x12014 /* Offset for EEPROM read reg */ +#define I210_REG_EEWRITE 0x12018 /* Offset for EEPROM write reg */ +#define I210_CMDV 0x01 /* command valid bit */ +#define I210_DONE 0x02 /* command done bit */ +#define I210_TARGET_CHECKSUM 0xBABA /* resulting checksum */ + + +/*define some other useful values here */ +#define I210_POLL_TIMEOUT_US 300000 /* 300 ms */ +/*Define some error states here*/ +#define I210_SUCCESS 0x00000000 +#define I210_INVALID_PARAM 0x00000001 +#define I210_NOT_READY 0x00000002 +#define I210_READ_ERROR 0x00000004 +#define I210_WRITE_ERROR 0x00000008 +#define I210_CHECKSUM_ERROR 0x00000010 +#define I210_FLASH_UPDATE_ERROR 0x00000020 + +#endif /* _INTEL_I210_H_ */ |