Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 4 | * Copyright (C) 2014-2016 Siemens AG. |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; version 2 of the License. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 14 | */ |
| 15 | |
| 16 | #include "i210.h" |
| 17 | #include <device/device.h> |
| 18 | #include <console/console.h> |
| 19 | #include <device/pci.h> |
| 20 | #include <device/pci_ids.h> |
| 21 | #include <device/pci_ops.h> |
| 22 | #include <string.h> |
| 23 | #include <types.h> |
| 24 | #include <delay.h> |
| 25 | |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 26 | /* This is a private function to wait for a bit mask in a given register */ |
| 27 | /* To avoid endless loops, a time-out is implemented here. */ |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 28 | static int wait_done(uint32_t* reg, uint32_t mask) |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 29 | { |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 30 | uint32_t timeout = I210_POLL_TIMEOUT_US; |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 31 | |
| 32 | while (!(*reg & mask)) { |
| 33 | udelay(1); |
| 34 | if (!--timeout) |
| 35 | return I210_NOT_READY; |
| 36 | } |
| 37 | return I210_SUCCESS; |
| 38 | } |
| 39 | |
| 40 | /** \brief This function can read the configuration space of the MACPHY |
| 41 | * For this purpose, the EEPROM interface is used. No direct access |
| 42 | * to the flash memory will be done. |
| 43 | * @param *dev Pointer to the PCI device of this MACPHY |
| 44 | * @param address Address inside the flash where reading will start |
| 45 | * @param count Number of words (16 bit values) to read |
| 46 | * @param *buffer Pointer to the buffer where to store read data |
| 47 | * @return void I210_NO_ERROR or an error code |
| 48 | */ |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 49 | static uint32_t read_flash(struct device *dev, uint32_t address, |
| 50 | uint32_t count, uint16_t *buffer) |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 51 | { |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 52 | uint32_t bar; |
| 53 | uint32_t *eeprd; |
| 54 | uint32_t i; |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 55 | |
| 56 | /* Get the BAR to memory mapped space*/ |
| 57 | bar = pci_read_config32(dev, PCI_BASE_ADDRESS_0); |
| 58 | if ((!bar) || ((address + count) > 0x40)) |
| 59 | return I210_INVALID_PARAM; |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 60 | eeprd = (uint32_t*)(bar + I210_REG_EEREAD); |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 61 | /* Prior to start ensure flash interface is ready by checking DONE-bit */ |
| 62 | if (wait_done(eeprd, I210_DONE)) |
| 63 | return I210_NOT_READY; |
| 64 | |
| 65 | /*OK, interface is ready, we can use it now */ |
| 66 | for (i = 0; i < count; i++) { |
| 67 | /* To start a read cycle write desired address in bits 12..2 */ |
| 68 | *eeprd = ((address + i) << 2) & 0x1FFC; |
| 69 | /* Wait until read is done */ |
| 70 | if (wait_done(eeprd, I210_DONE)) |
| 71 | return I210_READ_ERROR; |
| 72 | /* Here, we can read back desired word in bits 31..16 */ |
| 73 | buffer[i] = (*eeprd & 0xffff0000) >> 16; |
| 74 | } |
| 75 | return I210_SUCCESS; |
| 76 | } |
| 77 | |
| 78 | /** \brief This function computes the checksum for the configuration space. |
| 79 | * The address range for the checksum is 0x00..0x3e. |
| 80 | * @param *dev Pointer to the PCI device of this MACPHY |
| 81 | * @param *checksum Pointer to the buffer where to store the checksum |
| 82 | * @return void I210_NO_ERROR or an error code |
| 83 | */ |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 84 | static uint32_t compute_checksum(struct device *dev, uint16_t *checksum) |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 85 | { |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 86 | uint16_t eep_data[0x40]; |
| 87 | uint32_t i; |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 88 | |
| 89 | /* First read back data to compute the checksum for */ |
| 90 | if (read_flash(dev, 0, 0x3f, eep_data)) |
| 91 | return I210_READ_ERROR; |
| 92 | /* The checksum is computed in that way that after summarize all the */ |
| 93 | /* data from word address 0 to 0x3f the result is 0xBABA. */ |
| 94 | *checksum = 0; |
| 95 | for (i = 0; i < 0x3f; i++) |
| 96 | *checksum += eep_data[i]; |
| 97 | *checksum = I210_TARGET_CHECKSUM - *checksum; |
| 98 | return I210_SUCCESS; |
| 99 | } |
| 100 | |
| 101 | /** \brief This function can write the configuration space of the MACPHY |
| 102 | * For this purpose, the EEPROM interface is used. No direct access |
| 103 | * to the flash memory will be done. This function will update |
| 104 | * the checksum after a value was changed. |
| 105 | * @param *dev Pointer to the PCI device of this MACPHY |
| 106 | * @param address Address inside the flash where writing will start |
| 107 | * @param count Number of words (16 bit values) to write |
| 108 | * @param *buffer Pointer to the buffer where data to write is stored in |
| 109 | * @return void I210_NO_ERROR or an error code |
| 110 | */ |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 111 | static uint32_t write_flash(struct device *dev, uint32_t address, |
| 112 | uint32_t count, uint16_t *buffer) |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 113 | { |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 114 | uint32_t bar; |
| 115 | uint32_t *eepwr; |
| 116 | uint32_t *eectrl; |
| 117 | uint16_t checksum; |
| 118 | uint32_t i; |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 119 | |
| 120 | /* Get the BAR to memory mapped space */ |
| 121 | bar = pci_read_config32(dev, 0x10); |
| 122 | if ((!bar) || ((address + count) > 0x40)) |
| 123 | return I210_INVALID_PARAM; |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 124 | eepwr = (uint32_t*)(bar + I210_REG_EEWRITE); |
| 125 | eectrl = (uint32_t*)(bar + I210_REG_EECTRL); |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 126 | /* Prior to start ensure flash interface is ready by checking DONE-bit */ |
| 127 | if (wait_done(eepwr, I210_DONE)) |
| 128 | return I210_NOT_READY; |
| 129 | |
| 130 | /* OK, interface is ready, we can use it now */ |
| 131 | for (i = 0; i < count; i++) { |
| 132 | /* To start a write cycle write desired address in bits 12..2 */ |
| 133 | /* and data to write in bits 31..16 into EEWRITE-register */ |
| 134 | *eepwr = ((((address + i) << 2) & 0x1FFC) | (buffer[i] << 16)); |
| 135 | /* Wait until write is done */ |
| 136 | if (wait_done(eepwr, I210_DONE)) |
| 137 | return I210_WRITE_ERROR; |
| 138 | } |
| 139 | /* Since we have modified data, we need to update the checksum */ |
| 140 | if (compute_checksum(dev, &checksum)) |
| 141 | return I210_CHECKSUM_ERROR; |
| 142 | *eepwr = (0x3f << 2) | checksum << 16; |
| 143 | if (wait_done(eepwr, I210_DONE)) |
| 144 | return I210_WRITE_ERROR; |
| 145 | /* Up to now, desired data was written into shadowed RAM. We now need */ |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 146 | /* to perform a flash cycle to bring the shadowed RAM into flash. */ |
| 147 | /* To start a flash cycle we need to set FLUPD and wait for FLDONE. */ |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 148 | *eectrl = *eectrl | I210_FLUPD; |
| 149 | if (wait_done(eectrl, I210_FLUDONE)) |
| 150 | return I210_FLASH_UPDATE_ERROR; |
| 151 | return I210_SUCCESS; |
| 152 | } |
| 153 | |
| 154 | /** \brief This function can read the MAC address out of the MACPHY |
| 155 | * @param *dev Pointer to the PCI device of this MACPHY |
| 156 | * @param *MACAdr Pointer to the buffer where to store read MAC address |
| 157 | * @return void I210_NO_ERROR or an error code |
| 158 | */ |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 159 | static uint32_t read_mac_adr(struct device *dev, uint8_t *mac_adr) |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 160 | { |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 161 | uint16_t adr[3]; |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 162 | if (!dev || !mac_adr) |
| 163 | return I210_INVALID_PARAM; |
| 164 | if (read_flash(dev, 0, 3, adr)) |
| 165 | return I210_READ_ERROR; |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 166 | /* Copy the address into destination. This is done because of possible */ |
| 167 | /* not matching alignment for destination to uint16_t boundary. */ |
| 168 | memcpy(mac_adr, (uint8_t*)adr, 6); |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 169 | return I210_SUCCESS; |
| 170 | } |
| 171 | |
| 172 | /** \brief This function can write the MAC address to the MACPHY |
| 173 | * @param *dev Pointer to the PCI device of this MACPHY |
| 174 | * @param *MACAdr Pointer to the buffer where the desired MAC address is |
| 175 | * @return void I210_NO_ERROR or an error code |
| 176 | */ |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 177 | static uint32_t write_mac_adr(struct device *dev, uint8_t *mac_adr) |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 178 | { |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 179 | uint16_t adr[3]; |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 180 | if (!dev || !mac_adr) |
| 181 | return I210_INVALID_PARAM; |
| 182 | /* Copy desired address into a local buffer to avoid alignment issues */ |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 183 | memcpy((uint8_t*)adr, mac_adr, 6); |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 184 | return write_flash(dev, 0, 3, adr); |
| 185 | } |
| 186 | |
| 187 | /** \brief This function is the driver entry point for the init phase |
| 188 | * of the PCI bus allocator. It will program a MAC address |
| 189 | * into the MACPHY. |
| 190 | * @param *dev Pointer to the used PCI device |
| 191 | * @return void Nothing is given back |
| 192 | */ |
| 193 | static void init(struct device *dev) |
| 194 | { |
Werner Zeh | 608d991 | 2016-04-26 09:26:14 +0200 | [diff] [blame] | 195 | uint8_t cur_adr[6]; |
| 196 | uint8_t adr_to_set[6]; |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 197 | enum cb_err status; |
| 198 | |
| 199 | /*Check first whether there is a valid MAC address available */ |
Werner Zeh | e22d96c | 2016-06-29 07:53:47 +0200 | [diff] [blame] | 200 | status = mainboard_get_mac_address(dev, adr_to_set); |
Werner Zeh | 63693dc | 2015-02-13 12:18:58 +0100 | [diff] [blame] | 201 | if (status != CB_SUCCESS) { |
| 202 | printk(BIOS_ERR, "I210: No valid MAC address found\n"); |
| 203 | return; |
| 204 | } |
| 205 | /* Before we will write a new address, check the existing one */ |
| 206 | if (read_mac_adr(dev, cur_adr)) { |
| 207 | printk(BIOS_ERR, "I210: Not able to read MAC address.\n"); |
| 208 | return; |
| 209 | } |
| 210 | if (memcmp(cur_adr, adr_to_set, 6)) { |
| 211 | if (write_mac_adr(dev, adr_to_set)) |
| 212 | printk(BIOS_ERR, "I210: Error setting MAC address\n"); |
| 213 | else |
| 214 | printk(BIOS_INFO, "I210: MAC address changed.\n"); |
| 215 | } else { |
| 216 | printk(BIOS_INFO, "I210: MAC address is up to date.\n"); |
| 217 | } |
| 218 | return; |
| 219 | } |
| 220 | |
| 221 | static struct device_operations i210_ops = { |
| 222 | .read_resources = pci_dev_read_resources, |
| 223 | .set_resources = pci_dev_set_resources, |
| 224 | .enable_resources = pci_dev_enable_resources, |
| 225 | .init = init, |
| 226 | .scan_bus = 0, |
| 227 | .ops_pci = 0, |
| 228 | }; |
| 229 | |
| 230 | static const unsigned short i210_device_ids[] = { 0x1538, 0x1533, 0 }; |
| 231 | |
| 232 | static const struct pci_driver i210_driver __pci_driver = { |
| 233 | .ops = &i210_ops, |
| 234 | .vendor = PCI_VENDOR_ID_INTEL, |
| 235 | .devices = i210_device_ids, |
| 236 | }; |