Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 4 | * Copyright (C) 2012 Google Inc. |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 5 | * Copyright (C) 2016 Damien Zammit <damien@zamaudio.com> |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; version 2 of the License. |
| 10 | * |
| 11 | * This program is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | * GNU General Public License for more details. |
| 15 | */ |
| 16 | |
| 17 | /* |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 18 | * This driver resets the 10ec:8168 NIC then tries to read |
| 19 | * "macaddress" string XX:XX:XX:XX:XX:XX from CBFS. |
| 20 | * If no MAC is found, it programs a default MAC address in the device |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 21 | * so that if the EEPROM/efuse is unconfigured it still has a default MAC. |
| 22 | */ |
| 23 | |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 24 | #include <cbfs.h> |
| 25 | #include <string.h> |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 26 | #include <arch/io.h> |
| 27 | #include <device/device.h> |
| 28 | #include <device/pci.h> |
| 29 | #include <device/pci_ops.h> |
| 30 | #include <device/pci_def.h> |
| 31 | #include <delay.h> |
| 32 | #include <console/console.h> |
| 33 | |
| 34 | #define NIC_TIMEOUT 1000 |
| 35 | |
| 36 | #define CMD_REG 0x37 |
| 37 | #define CMD_REG_RESET 0x10 |
| 38 | |
| 39 | #define CFG_9346 0x50 |
| 40 | #define CFG_9346_LOCK 0x00 |
| 41 | #define CFG_9346_UNLOCK 0xc0 |
| 42 | |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 43 | static u8 get_hex_digit(const u8 c) |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 44 | { |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 45 | u8 ret = 0; |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 46 | |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 47 | ret = c - '0'; |
| 48 | if (ret > 0x09) { |
| 49 | ret = c - 'A' + 0x0a; |
| 50 | if (ret > 0x0f) |
| 51 | ret = c - 'a' + 0x0a; |
| 52 | } |
| 53 | if (ret > 0x0f) { |
| 54 | printk(BIOS_DEBUG, "Error: Invalid hex digit found: " |
| 55 | "%c - 0x%02x\n", (char)c, c); |
| 56 | ret = 0; |
| 57 | } |
| 58 | return ret; |
| 59 | } |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 60 | |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 61 | static void get_mac_address(u8 *macaddr, const u8 *strbuf) |
| 62 | { |
| 63 | size_t offset = 0; |
| 64 | int i; |
| 65 | |
| 66 | if ( (strbuf[2] != ':') || (strbuf[5] != ':') || |
| 67 | (strbuf[8] != ':') || (strbuf[11] != ':') || |
| 68 | (strbuf[14] != ':') ) { |
| 69 | printk(BIOS_ERR, "r8168: ignore invalid MAC address in cbfs\n"); |
| 70 | return; |
| 71 | } |
| 72 | |
| 73 | for (i = 0; i < 6; i++) { |
| 74 | macaddr[i] = 0; |
| 75 | macaddr[i] |= get_hex_digit(strbuf[offset]) << 4; |
| 76 | macaddr[i] |= get_hex_digit(strbuf[offset + 1]); |
| 77 | offset += 3; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | #define MACLEN 17 |
| 82 | |
| 83 | static void program_mac_address(struct device *dev, u16 io_base) |
| 84 | { |
| 85 | struct cbfsf fh; |
| 86 | uint32_t matchraw = CBFS_TYPE_RAW; |
| 87 | u8 macstrbuf[MACLEN] = { 0 }; |
| 88 | int i = 0; |
| 89 | u8 mac[6] = { 0x00, 0xe0, 0x4c, 0x00, 0xc0, 0xb0 }; |
| 90 | |
Arthur Heymans | ec74f45 | 2017-01-10 15:44:47 +0100 | [diff] [blame] | 91 | if (!cbfs_boot_locate(&fh, "rt8168-macaddress", &matchraw)) { |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 92 | if (rdev_readat(&fh.data, macstrbuf, 0, MACLEN) == MACLEN) |
| 93 | get_mac_address(mac, macstrbuf); |
| 94 | else |
| 95 | printk(BIOS_ERR, "r8168: Error reading MAC from CBFS\n"); |
| 96 | } else { |
Arthur Heymans | ec74f45 | 2017-01-10 15:44:47 +0100 | [diff] [blame] | 97 | printk(BIOS_ERR, "r8168: 'rt8168-macaddress' not found in CBFS," |
| 98 | " using default 00:e0:4c:00:c0:b0\n"); |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 99 | } |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 100 | |
| 101 | /* Reset NIC */ |
| 102 | printk(BIOS_DEBUG, "r8168: Resetting NIC..."); |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 103 | outb(CMD_REG_RESET, io_base + CMD_REG); |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 104 | |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 105 | /* Poll for reset, with 1sec timeout */ |
| 106 | while (i < NIC_TIMEOUT && (inb(io_base + CMD_REG) & CMD_REG_RESET)) { |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 107 | udelay(1000); |
| 108 | if (++i >= NIC_TIMEOUT) |
| 109 | printk(BIOS_DEBUG, "timeout waiting for nic to reset\n"); |
| 110 | } |
| 111 | if (i < NIC_TIMEOUT) |
| 112 | printk(BIOS_DEBUG, "done\n"); |
| 113 | |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 114 | printk(BIOS_DEBUG, "r8168: Programming MAC Address..."); |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 115 | |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 116 | /* Disable register protection */ |
| 117 | outb(CFG_9346_UNLOCK, io_base + CFG_9346); |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 118 | |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 119 | /* Set MAC address: only 4-byte write accesses allowed */ |
| 120 | outl(mac[4] | mac[5] << 8, io_base + 4); |
| 121 | inl(io_base + 4); |
| 122 | outl(mac[0] | mac[1] << 8 | mac[2] << 16 | mac[3] << 24, |
| 123 | io_base); |
| 124 | inl(io_base); |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 125 | /* Lock config regs */ |
Damien Zammit | f5dd23f | 2016-12-01 23:29:34 +1100 | [diff] [blame] | 126 | outb(CFG_9346_LOCK, io_base + CFG_9346); |
| 127 | |
| 128 | printk(BIOS_DEBUG, "done\n"); |
| 129 | } |
| 130 | |
| 131 | static void r8168_init(struct device *dev) |
| 132 | { |
| 133 | /* Get the resource of the NIC mmio */ |
| 134 | struct resource *nic_res = find_resource(dev, PCI_BASE_ADDRESS_0); |
| 135 | u16 io_base = (u16)nic_res->base; |
| 136 | |
| 137 | /* Enable but do not set bus master */ |
| 138 | pci_write_config16(dev, PCI_COMMAND, |
| 139 | PCI_COMMAND_MEMORY | PCI_COMMAND_IO); |
| 140 | |
| 141 | /* Program MAC address based on CBFS "macaddress" containing |
| 142 | * a string AA:BB:CC:DD:EE:FF */ |
| 143 | if (io_base) |
| 144 | program_mac_address(dev, io_base); |
| 145 | else |
| 146 | printk(BIOS_DEBUG, "r8168: Error cant find MMIO resource\n"); |
Damien Zammit | e983f0c | 2016-05-21 02:24:19 +1000 | [diff] [blame] | 147 | } |
| 148 | |
| 149 | static struct device_operations r8168_ops = { |
| 150 | .read_resources = pci_dev_read_resources, |
| 151 | .set_resources = pci_dev_set_resources, |
| 152 | .enable_resources = pci_dev_enable_resources, |
| 153 | .init = r8168_init, |
| 154 | .scan_bus = 0, |
| 155 | }; |
| 156 | |
| 157 | static const struct pci_driver r8168_driver __pci_driver = { |
| 158 | .ops = &r8168_ops, |
| 159 | .vendor = 0x10ec, |
| 160 | .device = 0x8168, |
| 161 | }; |