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