blob: 656e4f3c89ce246a03e4924274d403f98845474e [file] [log] [blame]
Werner Zeh63693dc2015-02-13 12:18:58 +01001/*
2 * This file is part of the coreboot project.
3 *
Werner Zeh608d9912016-04-26 09:26:14 +02004 * Copyright (C) 2014-2016 Siemens AG.
Werner Zeh63693dc2015-02-13 12:18:58 +01005 *
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 Zeh63693dc2015-02-13 12:18:58 +010014 */
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 Zeh63693dc2015-02-13 12:18:58 +010026/* 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 Zeh608d9912016-04-26 09:26:14 +020028static int wait_done(uint32_t* reg, uint32_t mask)
Werner Zeh63693dc2015-02-13 12:18:58 +010029{
Werner Zeh608d9912016-04-26 09:26:14 +020030 uint32_t timeout = I210_POLL_TIMEOUT_US;
Werner Zeh63693dc2015-02-13 12:18:58 +010031
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 Zeh608d9912016-04-26 09:26:14 +020049static uint32_t read_flash(struct device *dev, uint32_t address,
50 uint32_t count, uint16_t *buffer)
Werner Zeh63693dc2015-02-13 12:18:58 +010051{
Werner Zeh608d9912016-04-26 09:26:14 +020052 uint32_t bar;
53 uint32_t *eeprd;
54 uint32_t i;
Werner Zeh63693dc2015-02-13 12:18:58 +010055
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 Zeh608d9912016-04-26 09:26:14 +020060 eeprd = (uint32_t*)(bar + I210_REG_EEREAD);
Werner Zeh63693dc2015-02-13 12:18:58 +010061 /* 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 Zeh608d9912016-04-26 09:26:14 +020084static uint32_t compute_checksum(struct device *dev, uint16_t *checksum)
Werner Zeh63693dc2015-02-13 12:18:58 +010085{
Werner Zeh608d9912016-04-26 09:26:14 +020086 uint16_t eep_data[0x40];
87 uint32_t i;
Werner Zeh63693dc2015-02-13 12:18:58 +010088
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 Zeh608d9912016-04-26 09:26:14 +0200111static uint32_t write_flash(struct device *dev, uint32_t address,
112 uint32_t count, uint16_t *buffer)
Werner Zeh63693dc2015-02-13 12:18:58 +0100113{
Werner Zeh608d9912016-04-26 09:26:14 +0200114 uint32_t bar;
115 uint32_t *eepwr;
116 uint32_t *eectrl;
117 uint16_t checksum;
118 uint32_t i;
Werner Zeh63693dc2015-02-13 12:18:58 +0100119
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 Zeh608d9912016-04-26 09:26:14 +0200124 eepwr = (uint32_t*)(bar + I210_REG_EEWRITE);
125 eectrl = (uint32_t*)(bar + I210_REG_EECTRL);
Werner Zeh63693dc2015-02-13 12:18:58 +0100126 /* 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 Zeh608d9912016-04-26 09:26:14 +0200146 /* 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 Zeh63693dc2015-02-13 12:18:58 +0100148 *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 Zeh608d9912016-04-26 09:26:14 +0200159static uint32_t read_mac_adr(struct device *dev, uint8_t *mac_adr)
Werner Zeh63693dc2015-02-13 12:18:58 +0100160{
Werner Zeh608d9912016-04-26 09:26:14 +0200161 uint16_t adr[3];
Werner Zeh63693dc2015-02-13 12:18:58 +0100162 if (!dev || !mac_adr)
163 return I210_INVALID_PARAM;
164 if (read_flash(dev, 0, 3, adr))
165 return I210_READ_ERROR;
Werner Zeh608d9912016-04-26 09:26:14 +0200166 /* 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 Zeh63693dc2015-02-13 12:18:58 +0100169 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 Zeh608d9912016-04-26 09:26:14 +0200177static uint32_t write_mac_adr(struct device *dev, uint8_t *mac_adr)
Werner Zeh63693dc2015-02-13 12:18:58 +0100178{
Werner Zeh608d9912016-04-26 09:26:14 +0200179 uint16_t adr[3];
Werner Zeh63693dc2015-02-13 12:18:58 +0100180 if (!dev || !mac_adr)
181 return I210_INVALID_PARAM;
182 /* Copy desired address into a local buffer to avoid alignment issues */
Werner Zeh608d9912016-04-26 09:26:14 +0200183 memcpy((uint8_t*)adr, mac_adr, 6);
Werner Zeh63693dc2015-02-13 12:18:58 +0100184 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 */
193static void init(struct device *dev)
194{
Werner Zeh608d9912016-04-26 09:26:14 +0200195 uint8_t cur_adr[6];
196 uint8_t adr_to_set[6];
Werner Zeh63693dc2015-02-13 12:18:58 +0100197 enum cb_err status;
198
199 /*Check first whether there is a valid MAC address available */
Werner Zehe22d96c2016-06-29 07:53:47 +0200200 status = mainboard_get_mac_address(dev, adr_to_set);
Werner Zeh63693dc2015-02-13 12:18:58 +0100201 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
221static 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
230static const unsigned short i210_device_ids[] = { 0x1538, 0x1533, 0 };
231
232static const struct pci_driver i210_driver __pci_driver = {
233 .ops = &i210_ops,
234 .vendor = PCI_VENDOR_ID_INTEL,
235 .devices = i210_device_ids,
236};