[coreboot-gerrit] New patch to review for coreboot: 59168cb drivers/realtek: Add driver stub to correct buggy firmware with r8168
Damien Zammit (damien@zamaudio.com)
gerrit at coreboot.org
Mon Apr 20 08:56:30 CEST 2015
Damien Zammit (damien at zamaudio.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9806
-gerrit
commit 59168cb9b0d3ed14f4980b2b65ed5033789eb9f5
Author: Damien Zammit <damien at zamaudio.com>
Date: Mon Apr 20 16:49:00 2015 +1000
drivers/realtek: Add driver stub to correct buggy firmware with r8168
Some r8168 network cards return prefetchable memory on their internel addresses.
Add infrastructure and option to force non-prefetchable memory on allocation.
Change-Id: I07512494791de1ad8659521617b7a1661e4da0d3
Signed-off-by: Damien Zammit <damien at zamaudio.com>
---
src/device/device.c | 8 +--
src/device/pci_device.c | 141 +++++++++++++++++++++++++++++++++++++++++++
src/drivers/net/Kconfig | 5 ++
src/drivers/net/Makefile.inc | 1 +
src/drivers/net/realtek.c | 29 +++++++++
src/include/device/pci.h | 2 +
6 files changed, 182 insertions(+), 4 deletions(-)
diff --git a/src/device/device.c b/src/device/device.c
index b3b8d24..0c2498b 100644
--- a/src/device/device.c
+++ b/src/device/device.c
@@ -704,8 +704,8 @@ static void avoid_fixed_resources(struct device *dev)
for (res = dev->resource_list; res; res = res->next) {
if ((res->flags & IORESOURCE_FIXED))
continue;
- printk(BIOS_SPEW, "%s:@%s %02lx limit %08llx\n", __func__,
- dev_path(dev), res->index, res->limit);
+ printk(BIOS_SPEW, "%s: %s@%08llx limit %08llx\n", __func__,
+ dev_path(dev), res->base, res->limit);
if ((res->flags & MEM_MASK) == PREF_TYPE &&
(res->limit < limits.pref.limit))
limits.pref.limit = res->limit;
@@ -737,8 +737,8 @@ static void avoid_fixed_resources(struct device *dev)
else
continue;
- printk(BIOS_SPEW, "%s2: %s@%02lx limit %08llx\n", __func__,
- dev_path(dev), res->index, res->limit);
+ printk(BIOS_SPEW, "%s2: %s@%08llx limit %08llx\n", __func__,
+ dev_path(dev), res->base, res->limit);
printk(BIOS_SPEW, "\tlim->base %08llx lim->limit %08llx\n",
lim->base, lim->limit);
diff --git a/src/device/pci_device.c b/src/device/pci_device.c
index 4651258..74cc63f 100644
--- a/src/device/pci_device.c
+++ b/src/device/pci_device.c
@@ -163,6 +163,120 @@ unsigned pci_find_capability(device_t dev, unsigned cap)
/**
* Given a device and register, read the size of the BAR for that register.
+ * Hardcode any prefetch mem flags to mem (call only for buggy devices).
+ *
+ * @param dev Pointer to the device structure.
+ * @param index Address of the PCI configuration register.
+ * @return TODO
+ */
+struct resource *pci_get_resource_no_prefetch_mem(struct device *dev, unsigned long index)
+{
+ struct resource *resource;
+ unsigned long value, attr;
+ resource_t moving, limit;
+
+ /* Initialize the resources to nothing. */
+ resource = new_resource(dev, index);
+
+ /* Get the initial value. */
+ value = pci_read_config32(dev, index);
+
+ /* See which bits move. */
+ moving = pci_moving_config32(dev, index);
+
+ /* Initialize attr to the bits that do not move. */
+ attr = value & ~moving;
+
+ /* If it is a 64bit resource look at the high half as well. */
+ if (((attr & PCI_BASE_ADDRESS_SPACE_IO) == 0) &&
+ ((attr & PCI_BASE_ADDRESS_MEM_LIMIT_MASK) ==
+ PCI_BASE_ADDRESS_MEM_LIMIT_64)) {
+ /* Find the high bits that move. */
+ moving |=
+ ((resource_t) pci_moving_config32(dev, index + 4)) << 32;
+ }
+
+ /* Find the resource constraints.
+ * Start by finding the bits that move. From there:
+ * - Size is the least significant bit of the bits that move.
+ * - Limit is all of the bits that move plus all of the lower bits.
+ * See PCI Spec 6.2.5.1.
+ */
+ limit = 0;
+ if (moving) {
+ resource->size = 1;
+ resource->align = resource->gran = 0;
+ while (!(moving & resource->size)) {
+ resource->size <<= 1;
+ resource->align += 1;
+ resource->gran += 1;
+ }
+ resource->limit = limit = moving | (resource->size - 1);
+ }
+
+ /*
+ * Some broken hardware has read-only registers that do not
+ * really size correctly.
+ *
+ * Example: the Acer M7229 has BARs 1-4 normally read-only,
+ * so BAR1 at offset 0x10 reads 0x1f1. If you size that register
+ * by writing 0xffffffff to it, it will read back as 0x1f1 -- which
+ * is a violation of the spec.
+ *
+ * We catch this case and ignore it by observing which bits move.
+ *
+ * This also catches the common case of unimplemented registers
+ * that always read back as 0.
+ */
+ if (moving == 0) {
+ if (value != 0) {
+ printk(BIOS_DEBUG, "%s register %02lx(%08lx), "
+ "read-only ignoring it\n",
+ dev_path(dev), index, value);
+ }
+ resource->flags = 0;
+ } else if (attr & PCI_BASE_ADDRESS_SPACE_IO) {
+ /* An I/O mapped base address. */
+ attr &= PCI_BASE_ADDRESS_IO_ATTR_MASK;
+ resource->flags |= IORESOURCE_IO;
+ /* I don't want to deal with 32bit I/O resources. */
+ resource->limit = 0xffff;
+ } else {
+ /* A Memory mapped base address. */
+ attr &= PCI_BASE_ADDRESS_MEM_ATTR_MASK;
+ resource->flags |= IORESOURCE_MEM;
+ // Ignore prefetch
+ //if (attr & PCI_BASE_ADDRESS_MEM_PREFETCH)
+ // resource->flags |= IORESOURCE_PREFETCH;
+ attr &= PCI_BASE_ADDRESS_MEM_LIMIT_MASK;
+ if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_32) {
+ /* 32bit limit. */
+ resource->limit = 0xffffffffUL;
+ } else if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_1M) {
+ /* 1MB limit. */
+ resource->limit = 0x000fffffUL;
+ } else if (attr == PCI_BASE_ADDRESS_MEM_LIMIT_64) {
+ /* 64bit limit. */
+ resource->limit = 0xffffffffffffffffULL;
+ resource->flags |= IORESOURCE_PCI64;
+ } else {
+ /* Invalid value. */
+ printk(BIOS_ERR, "Broken BAR with value %lx\n", attr);
+ printk(BIOS_ERR, " on dev %s at index %02lx\n",
+ dev_path(dev), index);
+ resource->flags = 0;
+ }
+ }
+
+ /* Don't let the limit exceed which bits can move. */
+ if (resource->limit > limit)
+ resource->limit = limit;
+
+ return resource;
+}
+
+/**
+ * Given a device and register, read the size of the BAR for that register.
*
* @param dev Pointer to the device structure.
* @param index Address of the PCI configuration register.
@@ -325,6 +439,27 @@ static void pci_get_rom_resource(struct device *dev, unsigned long index)
}
/**
+ * Read the base address registers for a given device,
+ * but choose mem over prefetch mem for buggy devices.
+ *
+ * @param dev Pointer to the dev structure.
+ * @param howmany How many registers to read (6 for device, 2 for bridge).
+ */
+static void pci_read_bases_no_prefetch_mem(struct device *dev, unsigned int howmany)
+{
+ unsigned long index;
+
+ for (index = PCI_BASE_ADDRESS_0;
+ (index < PCI_BASE_ADDRESS_0 + (howmany << 2));) {
+ struct resource *resource;
+ resource = pci_get_resource_no_prefetch_mem(dev, index);
+ index += (resource->flags & IORESOURCE_PCI64) ? 8 : 4;
+ }
+
+ compact_resources(dev);
+}
+
+/**
* Read the base address registers for a given device.
*
* @param dev Pointer to the dev structure.
@@ -425,6 +560,12 @@ void pci_dev_read_resources(struct device *dev)
pci_get_rom_resource(dev, PCI_ROM_ADDRESS);
}
+void pci_dev_read_resources_no_prefetch(struct device *dev)
+{
+ pci_read_bases_no_prefetch_mem(dev, 6);
+ pci_get_rom_resource(dev, PCI_ROM_ADDRESS);
+}
+
void pci_bus_read_resources(struct device *dev)
{
pci_bridge_read_bases(dev);
diff --git a/src/drivers/net/Kconfig b/src/drivers/net/Kconfig
new file mode 100644
index 0000000..f1631fd
--- /dev/null
+++ b/src/drivers/net/Kconfig
@@ -0,0 +1,5 @@
+config REALTEK_8168_FIX
+ bool "Realtek 8168 fixup"
+ help
+ Select this when you have a buggy realtek 8168 card
+ that thinks its memory is prefetchable.
diff --git a/src/drivers/net/Makefile.inc b/src/drivers/net/Makefile.inc
index 9b3008d..f6fd803 100644
--- a/src/drivers/net/Makefile.inc
+++ b/src/drivers/net/Makefile.inc
@@ -1,2 +1,3 @@
romstage-$(CONFIG_CONSOLE_NE2K) += ne2k.c
ramstage-$(CONFIG_CONSOLE_NE2K) += ne2k.c
+ramstage-$(CONFIG_REALTEK_8168_FIX) += realtek.c
diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c
new file mode 100644
index 0000000..78d55a7
--- /dev/null
+++ b/src/drivers/net/realtek.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 Damien Zammit <damien at zamaudio.com>
+ *
+ * This driver simply forces the 10ec:8168 device not to use prefetchable
+ * memory addresses on buggy hardware, which fixes an issue with the pci
+ * allocator
+ */
+
+#include <arch/io.h>
+#include <device/device.h>
+#include <device/pci.h>
+#include <device/pci_ids.h>
+#include <device/pci_ops.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct device_operations r8168bug_ops = {
+ .read_resources = pci_dev_read_resources_no_prefetch,
+ .set_resources = pci_dev_set_resources,
+ .enable_resources = pci_dev_enable_resources,
+ .init = 0,
+ .scan_bus = 0,
+};
+
+static const struct pci_driver r8168bug_driver __pci_driver = {
+ .ops = &r8168bug_ops,
+ .vendor = 0x10ec,
+ .device = 0x8168,
+};
diff --git a/src/include/device/pci.h b/src/include/device/pci.h
index 4e712f9..27b3327 100644
--- a/src/include/device/pci.h
+++ b/src/include/device/pci.h
@@ -63,6 +63,7 @@ extern struct pci_driver epci_drivers[];
extern struct device_operations default_pci_ops_dev;
extern struct device_operations default_pci_ops_bus;
+void pci_dev_read_resources_no_prefetch(device_t dev);
void pci_dev_read_resources(device_t dev);
void pci_bus_read_resources(device_t dev);
void pci_dev_set_resources(device_t dev);
@@ -79,6 +80,7 @@ uint8_t pci_moving_config8(struct device *dev, unsigned reg);
uint16_t pci_moving_config16(struct device *dev, unsigned reg);
uint32_t pci_moving_config32(struct device *dev, unsigned reg);
struct resource *pci_get_resource(struct device *dev, unsigned long index);
+struct resource *pci_get_resource_no_prefetch_mem(struct device *dev, unsigned long index);
void pci_dev_set_subsystem(device_t dev, unsigned vendor, unsigned device);
void pci_dev_init(struct device *dev);
unsigned int pci_match_simple_dev(device_t dev, pci_devfn_t sdev);
More information about the coreboot-gerrit
mailing list