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