mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support
@ 2021-04-16  6:24 Ahmad Fatoum
  2021-04-16  6:24 ` [PATCH 1/4] x86: add DMA support Ahmad Fatoum
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2021-04-16  6:24 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

v1 is here:
https://lore.barebox.org/barebox/20191204125659.22506-1-a.fatoum@pengutronix.de/

Series tested with:
  * QEMU q35: ns16550 serial, iTCO and virtio-pci
  * Dell Latitude 7490: Bus enumeration works correctly

v1 -> v2:
  - add DMA helpers, so the new virtio-pci support is usable on x86 as
    well
  - add preassigned bus support, so already running PCI UEFI drivers can
    continue to function
  - handle zero size resources correctly
  - add iTCO watchdog support in new commit

Cheers,
Ahmad Fatoum (4):
  x86: add DMA support
  PCI: support PCI BIOS preassigned buses
  pci: add EFI PCI root bridge IO protocol driver
  watchdog: add support for Intel TCO watchdog timer

 arch/arm/include/asm/pci.h     |   7 +
 arch/mips/include/asm/pci.h    |   7 +
 arch/x86/Kconfig               |   1 +
 arch/x86/configs/efi_defconfig |   2 +
 arch/x86/include/asm/dma.h     |  36 +++-
 arch/x86/include/asm/pci.h     |   7 +
 drivers/efi/Kconfig            |   1 +
 drivers/pci/Kconfig            |   5 +
 drivers/pci/Makefile           |   1 +
 drivers/pci/pci-efi.c          | 342 ++++++++++++++++++++++++++++++++
 drivers/pci/pci-efi.h          | 343 ++++++++++++++++++++++++++++++++
 drivers/pci/pci.c              | 106 ++++++----
 drivers/watchdog/Kconfig       |  17 ++
 drivers/watchdog/Makefile      |   1 +
 drivers/watchdog/itco_wdt.c    | 346 +++++++++++++++++++++++++++++++++
 include/linux/pci.h            |   3 +
 16 files changed, 1187 insertions(+), 38 deletions(-)
 create mode 100644 arch/arm/include/asm/pci.h
 create mode 100644 arch/mips/include/asm/pci.h
 create mode 100644 arch/x86/include/asm/pci.h
 create mode 100644 drivers/pci/pci-efi.c
 create mode 100644 drivers/pci/pci-efi.h
 create mode 100644 drivers/watchdog/itco_wdt.c

-- 
2.31.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] x86: add DMA support
  2021-04-16  6:24 [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support Ahmad Fatoum
@ 2021-04-16  6:24 ` Ahmad Fatoum
  2021-04-16  6:24 ` [PATCH 2/4] PCI: support PCI BIOS preassigned buses Ahmad Fatoum
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2021-04-16  6:24 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Both interconnect and PCI are cache coherent on x86, so we shouldn't
need any special CPU barriers for DMA. Indeed, Linux defined neither
ARCH_HAS_SYNC_DMA_FOR_CPU nor ARCH_HAS_SYNC_DMA_FOR_DEVICE on x86.

It thus seems that the only reordering we need to take care of is
compiler-induced reordering. The Linux memory model that barebox adheres
to as well demands that all accesses to shared data are volatile.

volatile accesses are already guarnateed to not be reordered against
each other, so we don't even need an explicit barrier(), which is
already the case on other architectures that have a disabled MMU.

Cc: Lucas Stach <l.stach@pengutronix.de>
Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 arch/x86/Kconfig           |  1 +
 arch/x86/include/asm/dma.h | 36 +++++++++++++++++++++++++++++++++++-
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 311c3d1a8ec5..bcb44b23f05a 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -4,6 +4,7 @@
 config X86
 	bool
 	select HAS_KALLSYMS
+	select HAS_DMA
 	select GENERIC_FIND_NEXT_BIT
 	default y
 
diff --git a/arch/x86/include/asm/dma.h b/arch/x86/include/asm/dma.h
index 3dab2b688d8e..8a3b044f3a9c 100644
--- a/arch/x86/include/asm/dma.h
+++ b/arch/x86/include/asm/dma.h
@@ -4,6 +4,40 @@
 #ifndef __ASM_DMA_H
 #define __ASM_DMA_H
 
-/* empty */
+#include <linux/string.h>
+#include <linux/compiler.h>
+#include <xfuncs.h>
+#include <malloc.h>
+
+/*
+ * x86 is cache coherent, so we need not do anything special here
+ */
+
+static inline void *dma_alloc_coherent(size_t size, dma_addr_t *dma_handle)
+{
+	void *ret = xmemalign(4096, size);
+	if (dma_handle)
+		*dma_handle = (dma_addr_t)ret;
+
+	memset(ret, 0, size);
+
+	return ret;
+}
+
+static inline void dma_free_coherent(void *mem, dma_addr_t dma_handle,
+				     size_t size)
+{
+	free(mem);
+}
+
+static inline void dma_sync_single_for_cpu(dma_addr_t address, size_t size,
+					   enum dma_data_direction dir)
+{
+}
+
+static inline void dma_sync_single_for_device(dma_addr_t address, size_t size,
+					      enum dma_data_direction dir)
+{
+}
 
 #endif /* __ASM_DMA_H */
-- 
2.31.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/4] PCI: support PCI BIOS preassigned buses
  2021-04-16  6:24 [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support Ahmad Fatoum
  2021-04-16  6:24 ` [PATCH 1/4] x86: add DMA support Ahmad Fatoum
@ 2021-04-16  6:24 ` Ahmad Fatoum
  2021-04-16  6:24 ` [PATCH 3/4] pci: add EFI PCI root bridge IO protocol driver Ahmad Fatoum
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2021-04-16  6:24 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

When running under UEFI, barebox should no redo PCI enumeration,
because the UEFI implementation will likely already have drivers
that won't cope with e.g. BAR addresses changing.

The user-visible effect of this is that likely the framebuffer will
stop working because the UEFI driver won't be able to access it
any longer.

Support this configuration by changing the PCI code to consult the
new pcibios_assign_all_busses().

When it's true, there is no change to previous behavior.
When it's false, reconfiguration is omitted and instead current
configuration is read back from the bus.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 arch/arm/include/asm/pci.h  |   7 +++
 arch/mips/include/asm/pci.h |   7 +++
 drivers/pci/pci.c           | 106 +++++++++++++++++++++++-------------
 include/linux/pci.h         |   3 +
 4 files changed, 86 insertions(+), 37 deletions(-)
 create mode 100644 arch/arm/include/asm/pci.h
 create mode 100644 arch/mips/include/asm/pci.h

diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h
new file mode 100644
index 000000000000..d7419cabe7ef
--- /dev/null
+++ b/arch/arm/include/asm/pci.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_PCI_H
+#define __ASM_PCI_H
+
+#define pcibios_assign_all_busses()	1
+
+#endif
diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h
new file mode 100644
index 000000000000..d7419cabe7ef
--- /dev/null
+++ b/arch/mips/include/asm/pci.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_PCI_H
+#define __ASM_PCI_H
+
+#define pcibios_assign_all_busses()	1
+
+#endif
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 945a983387ed..950c5094476c 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -51,25 +51,27 @@ void register_pci_controller(struct pci_controller *hose)
 	bus->resource[PCI_BUS_RESOURCE_MEM] = hose->mem_resource;
 	bus->resource[PCI_BUS_RESOURCE_MEM_PREF] = hose->mem_pref_resource;
 	bus->resource[PCI_BUS_RESOURCE_IO] = hose->io_resource;
-	bus->number = bus_index++;
 
-	if (hose->set_busno)
-		hose->set_busno(hose, bus->number);
-
-	if (bus->resource[PCI_BUS_RESOURCE_MEM])
-		last_mem = bus->resource[PCI_BUS_RESOURCE_MEM]->start;
-	else
-		last_mem = 0;
-
-	if (bus->resource[PCI_BUS_RESOURCE_MEM_PREF])
-		last_mem_pref = bus->resource[PCI_BUS_RESOURCE_MEM_PREF]->start;
-	else
-		last_mem_pref = 0;
-
-	if (bus->resource[PCI_BUS_RESOURCE_IO])
-		last_io = bus->resource[PCI_BUS_RESOURCE_IO]->start;
-	else
-		last_io = 0;
+	if (pcibios_assign_all_busses()) {
+		bus->number = bus_index++;
+		if (hose->set_busno)
+			hose->set_busno(hose, bus->number);
+
+		if (bus->resource[PCI_BUS_RESOURCE_MEM])
+			last_mem = bus->resource[PCI_BUS_RESOURCE_MEM]->start;
+		else
+			last_mem = 0;
+
+		if (bus->resource[PCI_BUS_RESOURCE_MEM_PREF])
+			last_mem_pref = bus->resource[PCI_BUS_RESOURCE_MEM_PREF]->start;
+		else
+			last_mem_pref = 0;
+
+		if (bus->resource[PCI_BUS_RESOURCE_IO])
+			last_io = bus->resource[PCI_BUS_RESOURCE_IO]->start;
+		else
+			last_io = 0;
+	}
 
 	pci_scan_bus(bus);
 	pci_bus_register_devices(bus);
@@ -156,13 +158,16 @@ static void setup_device(struct pci_dev *dev, int max_bar)
 	u8 cmd;
 
 	pci_read_config_byte(dev, PCI_COMMAND, &cmd);
-	pci_write_config_byte(dev, PCI_COMMAND,
-			      cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
+
+	if (pcibios_assign_all_busses())
+		pci_write_config_byte(dev, PCI_COMMAND,
+				      cmd & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY));
+
 
 	for (bar = 0; bar < max_bar; bar++) {
 		const int pci_base_address_0 = PCI_BASE_ADDRESS_0 + bar * 4;
 		const int pci_base_address_1 = PCI_BASE_ADDRESS_1 + bar * 4;
-		resource_size_t *last_addr;
+		resource_size_t *last_addr, start;
 		u32 orig, mask, size;
 		unsigned long flags;
 		const char *kind;
@@ -207,32 +212,47 @@ static void setup_device(struct pci_dev *dev, int max_bar)
 		pr_debug("pbar%d: mask=%08x %s %d bytes\n", bar, mask, kind,
 			 size);
 
-		if (ALIGN(*last_addr, size) + size >
-		    dev->bus->resource[busres]->end) {
-			pr_debug("BAR does not fit within bus %s res\n", kind);
-			return;
+		if (pcibios_assign_all_busses()) {
+			if (ALIGN(*last_addr, size) + size >
+			    dev->bus->resource[busres]->end) {
+				pr_debug("BAR does not fit within bus %s res\n", kind);
+				return;
+			}
+
+			*last_addr = ALIGN(*last_addr, size);
+			pci_write_config_dword(dev, pci_base_address_0, *last_addr);
+			if (mask & PCI_BASE_ADDRESS_MEM_TYPE_64)
+				pci_write_config_dword(dev, pci_base_address_1, 0);
+			start = *last_addr;
+			*last_addr += size;
+		} else {
+			u32 tmp;
+			pci_read_config_dword(dev, pci_base_address_0, &tmp);
+			tmp &= mask & PCI_BASE_ADDRESS_SPACE_IO ? PCI_BASE_ADDRESS_IO_MASK
+				                                : PCI_BASE_ADDRESS_MEM_MASK;
+			start = tmp;
+
+			if (mask & PCI_BASE_ADDRESS_MEM_TYPE_64) {
+				pci_read_config_dword(dev, pci_base_address_1, &tmp);
+				start |= (u64)tmp << 32;
+			}
 		}
 
-		*last_addr = ALIGN(*last_addr, size);
-		pci_write_config_dword(dev, pci_base_address_0, *last_addr);
 		dev->resource[bar].flags = flags;
-		dev->resource[bar].start = *last_addr;
-		dev->resource[bar].end = dev->resource[bar].start + size - 1;
-
-		pr_debug("pbar%d: allocated at %pa\n", bar, last_addr);
-
-		*last_addr += size;
+		dev->resource[bar].start = start;
+		dev->resource[bar].end = start + size - 1;
 
 		if (mask & PCI_BASE_ADDRESS_MEM_TYPE_64) {
 			dev->resource[bar].flags |= IORESOURCE_MEM_64;
-			pci_write_config_dword(dev, pci_base_address_1, 0);
 			bar++;
 		}
 	}
 
 	pci_fixup_device(pci_fixup_header, dev);
 
-	pci_write_config_byte(dev, PCI_COMMAND, cmd);
+	if (pcibios_assign_all_busses())
+		pci_write_config_byte(dev, PCI_COMMAND, cmd);
+
 	list_add_tail(&dev->bus_list, &dev->bus->devices);
 }
 
@@ -240,6 +260,12 @@ static void prescan_setup_bridge(struct pci_dev *dev)
 {
 	u16 cmdstat;
 
+	if (!pcibios_assign_all_busses()) {
+		pci_read_config_byte(dev, PCI_PRIMARY_BUS, &dev->bus->number);
+		pci_read_config_byte(dev, PCI_SECONDARY_BUS, &dev->subordinate->number);
+		return;
+	}
+
 	pci_read_config_word(dev, PCI_COMMAND, &cmdstat);
 
 	/* Configure bus number registers */
@@ -283,6 +309,9 @@ static void prescan_setup_bridge(struct pci_dev *dev)
 
 static void postscan_setup_bridge(struct pci_dev *dev)
 {
+	if (!pcibios_assign_all_busses())
+		return;
+
 	/* limit subordinate to last used bus number */
 	pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, bus_index - 1);
 
@@ -416,8 +445,11 @@ static unsigned int pci_scan_bus(struct pci_bus *bus)
 				bus->resource[PCI_BUS_RESOURCE_IO];
 
 			child_bus->parent = &dev->dev;
-			child_bus->number = bus_index++;
-			child_bus->primary = bus->number;
+
+			if (pcibios_assign_all_busses()) {
+				child_bus->number = bus_index++;
+				child_bus->primary = bus->number;
+			}
 			list_add_tail(&child_bus->node, &bus->children);
 			dev->subordinate = child_bus;
 
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 0c8fed7c8e0d..486d4251d42a 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -29,6 +29,9 @@
 
 #include <linux/pci_ids.h>
 
+/* Include architecture-dependent settings and functions */
+
+#include <asm/pci.h>
 #define PCI_ANY_ID (~0)
 
 #define PCI_FIND_CAP_TTL	48
-- 
2.31.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 3/4] pci: add EFI PCI root bridge IO protocol driver
  2021-04-16  6:24 [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support Ahmad Fatoum
  2021-04-16  6:24 ` [PATCH 1/4] x86: add DMA support Ahmad Fatoum
  2021-04-16  6:24 ` [PATCH 2/4] PCI: support PCI BIOS preassigned buses Ahmad Fatoum
@ 2021-04-16  6:24 ` Ahmad Fatoum
  2021-04-16  6:24 ` [PATCH 4/4] watchdog: add support for Intel TCO watchdog timer Ahmad Fatoum
  2021-05-03 12:07 ` [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support Sascha Hauer
  4 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2021-04-16  6:24 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

UEFI specifies two protocols for abstracting both the PCI host bus
controller and for PCI devices. The protocol for PCI devices provides
function pointers for accessing IO Port, Memory and PCI configuration
space, among others. The protocol for bus controllers provides the
ability to read the root bridge's PCI configuration space and to query
resources.

In barebox, we would want to reuse existing PCI drivers unmodified, so
we utilize the root bridge protocol, unlike most other EFI payloads.

We still utilize the PCI (device) IO protocol, but not for core
functionality: EFI has already enumerated the bus for us and allocated
the EFI handles. It thus makes sense to have the new pci device have the
EFI handle as parent and the controller as grand parent instead of
being sibling with the EFI handles. This is done with an early PCI fixup
that patches the device's parent pointer after consulting the PCI IO
GetLocation.

Driver is written from scratch and hasn't seen heavy usage yet, so it
should be used with care. It was written while consulting the UEFI
2.1D specification.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 arch/x86/configs/efi_defconfig |   1 +
 arch/x86/include/asm/pci.h     |   7 +
 drivers/efi/Kconfig            |   1 +
 drivers/pci/Kconfig            |   5 +
 drivers/pci/Makefile           |   1 +
 drivers/pci/pci-efi.c          | 342 ++++++++++++++++++++++++++++++++
 drivers/pci/pci-efi.h          | 343 +++++++++++++++++++++++++++++++++
 7 files changed, 700 insertions(+)
 create mode 100644 arch/x86/include/asm/pci.h
 create mode 100644 drivers/pci/pci-efi.c
 create mode 100644 drivers/pci/pci-efi.h

diff --git a/arch/x86/configs/efi_defconfig b/arch/x86/configs/efi_defconfig
index 761ffbe261ba..e007bf8a0150 100644
--- a/arch/x86/configs/efi_defconfig
+++ b/arch/x86/configs/efi_defconfig
@@ -88,6 +88,7 @@ CONFIG_WATCHDOG=y
 CONFIG_WATCHDOG_EFI=y
 CONFIG_F71808E_WDT=y
 # CONFIG_PINCTRL is not set
+CONFIG_PCI_EFI=y
 CONFIG_FS_EXT4=y
 CONFIG_FS_TFTP=y
 CONFIG_FS_NFS=y
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h
new file mode 100644
index 000000000000..ca1c0f912dca
--- /dev/null
+++ b/arch/x86/include/asm/pci.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_PCI_H
+#define __ASM_PCI_H
+
+#define pcibios_assign_all_busses()	0
+
+#endif
diff --git a/drivers/efi/Kconfig b/drivers/efi/Kconfig
index cca1a2e1d632..80d9e6f0c5eb 100644
--- a/drivers/efi/Kconfig
+++ b/drivers/efi/Kconfig
@@ -2,3 +2,4 @@ config EFI_BOOTUP
 	bool
 	select BLOCK
 	select PARTITION_DISK
+	select HW_HAS_PCI
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 058546097621..71d05055d470 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -54,6 +54,11 @@ config PCI_LAYERSCAPE
 	select OF_PCI
 	select PCI
 
+config PCI_EFI
+	bool "EFI PCI protocol"
+	depends on EFI_BOOTUP
+	select PCI
+
 endmenu
 
 endif
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index d227619ed458..6fc4eaf6b2c2 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
 obj-$(CONFIG_PCIE_DW) += pcie-designware.o pcie-designware-host.o
 obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
 obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
+obj-$(CONFIG_PCI_EFI) += pci-efi.o
diff --git a/drivers/pci/pci-efi.c b/drivers/pci/pci-efi.c
new file mode 100644
index 000000000000..e1fe11d07016
--- /dev/null
+++ b/drivers/pci/pci-efi.c
@@ -0,0 +1,342 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ */
+#define pr_fmt(fmt) "pci-efi: " fmt
+
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <xfuncs.h>
+#include <efi.h>
+#include <efi/efi.h>
+#include <efi/efi-device.h>
+#include <linux/pci.h>
+
+#include "pci-efi.h"
+
+struct efi_pci_priv {
+	struct efi_pci_root_bridge_io_protocol *protocol;
+	struct device_d *dev;
+	struct pci_controller pci;
+	struct resource mem;
+	struct resource mem_pref;
+	struct resource io;
+	struct list_head children;
+};
+
+struct pci_child_id {
+	size_t segmentno;
+	size_t busno;
+	size_t devno;
+	size_t funcno;
+};
+
+struct pci_child {
+	struct efi_pci_io_protocol *protocol;
+	struct device_d *dev;
+	struct list_head list;
+	struct pci_child_id id;
+};
+
+static inline bool pci_child_id_equal(struct pci_child_id *a, struct pci_child_id *b)
+{
+	return a->segmentno == b->segmentno
+		&& a->busno == b->busno
+		&& a->devno == b->devno
+		&& a->funcno == b->funcno;
+}
+
+#define host_to_efi_pci(host) container_of(host, struct efi_pci_priv, pci)
+
+static inline u64 efi_pci_addr(struct pci_bus *bus, u32 devfn, int where)
+{
+	return EFI_PCI_ADDRESS(bus->number,
+			       PCI_SLOT(devfn), PCI_FUNC(devfn),
+			       where);
+}
+
+static int efi_pci_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+			   int size, u32 *val)
+{
+	struct efi_pci_priv *priv = host_to_efi_pci(bus->host);
+	efi_status_t efiret;
+	u32 value;
+	enum efi_pci_protocol_width width;
+
+	switch (size) {
+	case 4:
+		width = EFI_PCI_WIDTH_U32;
+		break;
+	case 2:
+		width = EFI_PCI_WIDTH_U16;
+		break;
+	case 1:
+		width = EFI_PCI_WIDTH_U8;
+		break;
+	default:
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	efiret = priv->protocol->pci.read(priv->protocol, width,
+		efi_pci_addr(bus, devfn, where),
+		1, &value);
+
+	*val = 0xFFFFFFFF;
+
+	if (EFI_ERROR(efiret))
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	*val = value;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int efi_pci_wr_conf(struct pci_bus *bus, u32 devfn, int where,
+			   int size, u32 val)
+{
+	struct efi_pci_priv *priv = host_to_efi_pci(bus->host);
+	efi_status_t efiret;
+	enum efi_pci_protocol_width width;
+
+	switch (size) {
+	case 4:
+		width = EFI_PCI_WIDTH_U32;
+		break;
+	case 2:
+		width = EFI_PCI_WIDTH_U16;
+		break;
+	case 1:
+		width = EFI_PCI_WIDTH_U8;
+		break;
+	default:
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	}
+
+	efiret = priv->protocol->pci.write(priv->protocol, width,
+		efi_pci_addr(bus, devfn, where),
+		1, &val);
+	if (EFI_ERROR(efiret))
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static inline struct resource build_resource(const char *name,
+					     resource_size_t start,
+					     resource_size_t len,
+					     unsigned long flags)
+{
+	struct resource res;
+
+	res.name = name;
+	res.start = start;
+	res.end = start + len - 1;
+	res.flags = flags;
+	res.parent = NULL;
+	INIT_LIST_HEAD(&res.children);
+	INIT_LIST_HEAD(&res.sibling);
+
+	return res;
+}
+
+static const struct pci_ops efi_pci_ops = {
+	.read = efi_pci_rd_conf,
+	.write = efi_pci_wr_conf,
+};
+
+static u8 *acpi_parse_resource(u8 *next, struct resource *out)
+{
+	struct efi_acpi_resource *res;
+	const char *name = NULL;
+	unsigned long flags = 0;
+
+	do {
+		if (*next == ACPI_RESOURCE_END_TAG)
+			return NULL;
+
+		if (*next != ACPI_RESOURCE_DESC_TAG)
+			return ERR_PTR(-EIO);
+
+		res = container_of(next, struct efi_acpi_resource, asd);
+
+		next = (u8 *)&res[1];
+	} while (res->addr_len == 0);
+
+	switch (res->restype) {
+	case ACPI_RESOURCE_TYPE_MEM:
+		if ((res->typflags & ACPI_RESOURCE_TYPFLAG_MTP_MASK)
+		    != ACPI_RESOURCE_TYPFLAG_MTP_MEM)
+			break;
+
+		name = "NP-MEM";
+		flags = IORESOURCE_MEM;
+
+		switch (res->typflags & ACPI_RESOURCE_TYPFLAG_MEM_MASK) {
+		case ACPI_RESOURCE_TYPFLAG_MEM_PREF:
+			name = "P-MEM";
+			flags |= IORESOURCE_PREFETCH;
+			/* fallthrough */
+		case ACPI_RESOURCE_TYPFLAG_MEM_WC:
+		case ACPI_RESOURCE_TYPFLAG_MEM_CACHEABLE:
+			flags |= IORESOURCE_CACHEABLE;
+		}
+
+		if (res->typflags & ACPI_RESOURCE_TYPFLAG_RW_MASK)
+			flags |= IORESOURCE_MEM_WRITEABLE;
+
+		break;
+	case ACPI_RESOURCE_TYPE_IO:
+		name = "IO";
+		flags = IORESOURCE_IO;
+		break;
+	case ACPI_RESOURCE_TYPE_BUSNO:
+		name = "BUS";
+		flags = IORESOURCE_BUS;
+		break;
+	default:
+		return ERR_PTR(-ENXIO);
+	}
+
+	*out = build_resource(name, res->addr_min, res->addr_len, flags);
+
+	pr_debug("%s: %llx-%llx (len=%llx, gr=%lld, xlate_off=%llx, resflags=%08lx)\n",
+		 out->name,
+		 res->addr_min, res->addr_max, res->addr_len,
+		 res->addr_granularity, res->addr_xlate_off,
+		 flags);
+
+	return next;
+}
+
+/* EFI already enumerated the bus for us, match our new pci devices with the efi
+ * handles
+ */
+static void efi_pci_fixup_dev_parent(struct pci_dev *dev)
+{
+	struct efi_pci_priv *priv = host_to_efi_pci(dev->bus->host);
+	struct pci_child *child;
+	struct pci_child_id id;
+
+	id.segmentno = priv->protocol->segmentno;
+	id.busno = dev->bus->number;
+	id.devno = PCI_SLOT(dev->devfn);
+	id.funcno = PCI_FUNC(dev->devfn);
+
+	list_for_each_entry(child, &priv->children, list) {
+		if (IS_ERR(child->protocol))
+			continue;
+
+		if (!child->protocol) {
+			struct efi_device *efichild = to_efi_device(child->dev);
+			efi_status_t efiret;
+
+			BS->handle_protocol(efichild->handle, &EFI_PCI_IO_PROTOCOL_GUID,
+					    (void **)&child->protocol);
+			if (!child->protocol) {
+				child->protocol = ERR_PTR(-ENODEV);
+				continue;
+			}
+
+			efiret = child->protocol->get_location(child->protocol,
+						       &child->id.segmentno,
+						       &child->id.busno,
+						       &child->id.devno,
+						       &child->id.funcno);
+
+			if (EFI_ERROR(efiret)) {
+				child->protocol = ERR_PTR(-efi_errno(efiret));
+				continue;
+			}
+		}
+
+		if (pci_child_id_equal(&child->id, &id)) {
+			dev->dev.priv = child->protocol;
+			dev->dev.parent = child->dev;
+			return;
+		}
+	}
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, efi_pci_fixup_dev_parent);
+
+static int efi_pci_probe(struct efi_device *efidev)
+{
+	struct device_d *child;
+	struct efi_pci_priv *priv;
+	efi_status_t efiret;
+	void *resources;
+	struct resource resource;
+	u8 *res;
+
+	priv = xzalloc(sizeof(*priv));
+
+	BS->handle_protocol(efidev->handle, &EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID,
+			(void **)&priv->protocol);
+	if (!priv->protocol)
+		return -ENODEV;
+
+	efiret = priv->protocol->configuration(priv->protocol, &resources);
+	if (EFI_ERROR(efiret))
+		return -efi_errno(efiret);
+
+	res = resources;
+
+	while (1) {
+		res = acpi_parse_resource(res, &resource);
+
+		if (IS_ERR(res))
+			return PTR_ERR(res);
+
+		if (!res)
+			break;
+
+		if ((resource.flags & (IORESOURCE_MEM | IORESOURCE_PREFETCH))
+		    == (IORESOURCE_MEM | IORESOURCE_PREFETCH)) {
+			priv->pci.mem_pref_resource = &priv->mem_pref;
+			priv->mem_pref = resource;
+		} else if (resource.flags & IORESOURCE_MEM) {
+			priv->pci.mem_resource = &priv->mem;
+			priv->mem = resource;
+		} else if (resource.flags & IORESOURCE_IO) {
+			priv->pci.io_resource = &priv->io;
+			priv->io = resource;
+		}
+	}
+
+	priv->pci.parent = &efidev->dev;
+	priv->pci.pci_ops = &efi_pci_ops;
+
+	INIT_LIST_HEAD(&priv->children);
+
+	device_for_each_child(&efidev->dev, child) {
+		struct pci_child *pci_child;
+		struct efi_device *efichild = to_efi_device(child);
+
+		if (!efi_device_has_guid(efichild, EFI_PCI_IO_PROTOCOL_GUID))
+			continue;
+
+		pci_child = xzalloc(sizeof(*pci_child));
+
+		pci_child->dev = &efichild->dev;
+
+		/*
+		 * regiser_pci_controller can reconfigure bridge bus numbers,
+		 * thus we only collect the child node handles here, but
+		 * don't yet call GetLocation on them
+		 */
+		list_add_tail(&pci_child->list, &priv->children);
+	};
+
+	register_pci_controller(&priv->pci);
+
+	return 0;
+}
+
+static struct efi_driver efi_pci_driver = {
+        .driver = {
+		.name  = "efi-pci",
+	},
+        .probe = efi_pci_probe,
+	.guid = EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID,
+};
+device_efi_driver(efi_pci_driver);
diff --git a/drivers/pci/pci-efi.h b/drivers/pci/pci-efi.h
new file mode 100644
index 000000000000..1943461cdf23
--- /dev/null
+++ b/drivers/pci/pci-efi.h
@@ -0,0 +1,343 @@
+// SPDX-License-Identifier: GPL-2.0
+#ifndef __PCI_EFI_H_
+#define __PCI_EFI_H_
+
+#include <efi.h>
+
+struct efi_pci_root_bridge_io_protocol;
+struct efi_pci_io_protocol;
+
+enum efi_pci_protocol_width {
+	EFI_PCI_WIDTH_U8,
+	EFI_PCI_WIDTH_U16,
+	EFI_PCI_WIDTH_U32,
+	EFI_PCI_WIDTH_U64,
+	EFI_PCI_WIDTH_FIFO_U8,
+	EFI_PCI_WIDTH_FIFO_U16,
+	EFI_PCI_WIDTH_FIFO_U32,
+	EFI_PCI_WIDTH_FIFO_U64,
+	EFI_PCI_WIDTH_FILL_U8,
+	EFI_PCI_WIDTH_FILL_U16,
+	EFI_PCI_WIDTH_FILL_U32,
+	EFI_PCI_WIDTH_FILL_U64,
+	EFI_PCI_WIDTH_MAX
+};
+
+#define EFI_PCI_IO_PASS_THROUGH_BAR	0xff
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_io_mem) (
+	struct efi_pci_root_bridge_io_protocol *this,
+	enum efi_pci_protocol_width width,
+	u64 addr,
+	size_t count,
+	void *buf
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_io_mem) (
+	struct efi_pci_io_protocol *this,
+	enum efi_pci_protocol_width width,
+	u8 bar,
+	u64 offset,
+	size_t count,
+	void *buf
+);
+
+struct efi_pci_root_bridge_io_protocol_access {
+	efi_pci_root_bridge_io_protocol_io_mem read;
+	efi_pci_root_bridge_io_protocol_io_mem write;
+};
+
+struct efi_pci_io_protocol_access {
+	efi_pci_io_protocol_io_mem read;
+	efi_pci_io_protocol_io_mem write;
+};
+
+#define EFI_PCI_ATTRIBUTE_ISA_MOTHERBOARD_IO   0x0001
+#define EFI_PCI_ATTRIBUTE_ISA_IO               0x0002
+#define EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO       0x0004
+#define EFI_PCI_ATTRIBUTE_VGA_MEMORY           0x0008
+#define EFI_PCI_ATTRIBUTE_VGA_IO               0x0010
+#define EFI_PCI_ATTRIBUTE_IDE_PRIMARY_IO       0x0020
+#define EFI_PCI_ATTRIBUTE_IDE_SECONDARY_IO     0x0040
+#define EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080
+#define EFI_PCI_ATTRIBUTE_IO                   0x0100
+#define EFI_PCI_ATTRIBUTE_MEMORY               0x0200
+#define EFI_PCI_ATTRIBUTE_BUS_MASTER           0x0400
+#define EFI_PCI_ATTRIBUTE_MEMORY_CACHED        0x0800
+#define EFI_PCI_ATTRIBUTE_MEMORY_DISABLE       0x1000
+#define EFI_PCI_ATTRIBUTE_EMBEDDED_DEVICE      0x2000
+#define EFI_PCI_ATTRIBUTE_EMBEDDED_ROM         0x4000
+#define EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE   0x8000
+#define EFI_PCI_ATTRIBUTE_ISA_IO_16            0x10000
+#define EFI_PCI_ATTRIBUTE_VGA_PALETTE_IO_16    0x20000
+#define EFI_PCI_ATTRIBUTE_VGA_IO_16            0x40000
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_poll_io_mem) (
+	struct efi_pci_root_bridge_io_protocol	*this,
+	enum efi_pci_protocol_width width,
+	u64 address,
+	u64 mask,
+	u64 value,
+	u64 delay,
+	u64 *result
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_poll_io_mem) (
+	struct efi_pci_io_protocol	*this,
+	enum efi_pci_protocol_width width,
+	u8 bar,
+	u64 offset,
+	u64 mask,
+	u64 value,
+	u64 delay,
+	u64 *result
+);
+
+enum efi_pci_root_bridge_io_protocol_operation {
+	EFI_PCI_ROOT_BRIGE_IO_OP_BUS_MASTER_READ,
+	EFI_PCI_ROOT_BRIGE_IO_OP_BUS_MASTER_WRITE,
+	EFI_PCI_ROOT_BRIGE_IO_OP_BUS_MASTER_COMMON_BUFFER,
+	EFI_PCI_ROOT_BRIGE_IO_OP_BUS_MASTER_READ64,
+	EFI_PCI_ROOT_BRIGE_IO_OP_BUS_MASTER_WRITE64,
+	EFI_PCI_ROOT_BRIGE_IO_OP_BUS_MASTER_COMMON_BUFFER64,
+	EFI_PCI_ROOT_BRIGE_IO_OP_MAX
+};
+
+enum efi_pci_io_protocol_operation {
+	EFI_PCI_IO_OP_BUS_MASTER_READ,
+	EFI_PCI_IO_OP_BUS_MASTER_WRITE,
+	EFI_PCI_IO_OP_BUS_MASTER_COMMON_BUFFER,
+	EFI_PCI_IO_OP_MAX
+};
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_copy_mem) (
+	struct efi_pci_root_bridge_io_protocol *this,
+	enum efi_pci_protocol_width width,
+	u64 dst,
+	u64 src,
+	size_t count
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_copy_mem) (
+	struct efi_pci_io_protocol *this,
+	enum efi_pci_protocol_width width,
+	u8 dst_bar,
+	u64 dst_offset,
+	u8 src_bar,
+	u64 src_offset,
+	size_t count
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_map) (
+	struct efi_pci_root_bridge_io_protocol *this,
+	enum efi_pci_root_bridge_io_protocol_operation operation,
+	void *hostaddr,
+	size_t *nbytes,
+	efi_physical_addr_t *devaddr,
+	void **mapping
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_map) (
+	struct efi_pci_io_protocol *this,
+	enum efi_pci_io_protocol_operation operation,
+	void *hostaddr,
+	size_t *nbytes,
+	efi_physical_addr_t *devaddr,
+	void **mapping
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_unmap) (
+	struct efi_pci_root_bridge_io_protocol *this,
+	void *mapping
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_unmap) (
+	struct efi_pci_io_protocol *this,
+	void *mapping
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_allocate_buffer) (
+	struct efi_pci_root_bridge_io_protocol *this,
+	enum efi_allocate_type alloctype,
+	enum efi_memory_type memtype,
+	size_t npages,
+	void **hostaddr,
+	u64 attrs
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_allocate_buffer) (
+	struct efi_pci_io_protocol *this,
+	enum efi_allocate_type alloctype,
+	enum efi_memory_type memtype,
+	size_t npages,
+	void **hostaddr,
+	u64 attrs
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_free_buffer) (
+	struct efi_pci_root_bridge_io_protocol *this,
+	size_t npages,
+	void *hostaddr
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_free_buffer) (
+	struct efi_pci_io_protocol *this,
+	size_t npages,
+	void *hostaddr
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_flush) (
+	struct efi_pci_root_bridge_io_protocol *this
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_flush) (
+	struct efi_pci_io_protocol *this
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_get_location) (
+	struct efi_pci_io_protocol *this,
+	size_t *segmentno,
+	size_t *busno,
+	size_t *deveno,
+	size_t *funcno
+);
+
+enum efi_io_protocol_attribute_operation {
+	PCI_IO_ATTR_OP_GET,
+	PCI_IO_ATTR_OP_SET,
+	PCI_IO_ATTR_OP_ENABLE,
+	PCI_IO_ATTR_OP_DISABLE,
+	PCI_IO_ATTR_OP_SUPPORTED,
+	PCI_IO_ATTR_OP_MAX,
+};
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_attributes) (
+	struct efi_pci_io_protocol *this,
+	enum efi_io_protocol_attribute_operation operation,
+	u64 attrs,
+	u64 *result
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_get_attributes) (
+	struct efi_pci_root_bridge_io_protocol *this,
+	u64 *supports,
+	u64 *attrs
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_get_bar_attributes) (
+	struct efi_pci_io_protocol *this,
+	u8 bar,
+	u64 *supports,
+	void **resources
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_set_attributes) (
+	struct efi_pci_root_bridge_io_protocol *this,
+	u64 attrs,
+	u64 *resource_base,
+	u64 *resource_len
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_set_bar_attributes) (
+	struct efi_pci_io_protocol *this,
+	u64 attrs,
+	u8 bar,
+	u64 *offset,
+	u64 *len
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_root_bridge_io_protocol_configuration) (
+	struct efi_pci_root_bridge_io_protocol *this,
+	void **resources
+);
+
+typedef efi_status_t (EFIAPI *efi_pci_io_protocol_config) (
+	struct efi_pci_io_protocol *this,
+	enum efi_pci_protocol_width width,
+	u32 offset,
+	size_t count,
+	void *buffer
+);
+
+struct efi_pci_io_protocol_config_access {
+	efi_pci_io_protocol_config read;
+	efi_pci_io_protocol_config write;
+};
+
+struct __packed efi_acpi_resource {
+#define	ACPI_RESOURCE_DESC_TAG	0x8A
+#define	ACPI_RESOURCE_END_TAG	0x79
+	u8 asd; /* 0x8A */
+	u16 len; /* 0x2B */
+#define	ACPI_RESOURCE_TYPE_MEM		0
+#define	ACPI_RESOURCE_TYPE_IO		1
+#define	ACPI_RESOURCE_TYPE_BUSNO	2
+	u8 restype;
+	u8 genflags;
+#define	ACPI_RESOURCE_TYPFLAG_TTP_MASK	0b00100000
+#define	ACPI_RESOURCE_TYPFLAG_MTP_MASK	0b00011000
+#define		ACPI_RESOURCE_TYPFLAG_MTP_MEM		0
+#define		ACPI_RESOURCE_TYPFLAG_MTP_RESERVED	1
+#define		ACPI_RESOURCE_TYPFLAG_MTP_ACPI		2
+#define		ACPI_RESOURCE_TYPFLAG_MTP_NVS		3
+#define	ACPI_RESOURCE_TYPFLAG_MEM_MASK	0b00000110
+#define		ACPI_RESOURCE_TYPFLAG_MEM_NONCACHEABLE	0
+#define		ACPI_RESOURCE_TYPFLAG_MEM_CACHEABLE	1
+#define		ACPI_RESOURCE_TYPFLAG_MEM_WC		2
+#define		ACPI_RESOURCE_TYPFLAG_MEM_PREF		3
+#define	ACPI_RESOURCE_TYPFLAG_RW_MASK	0b00000001
+	u8 typflags;
+	u64 addr_granularity;
+	u64 addr_min;
+	u64 addr_max;
+	u64 addr_xlate_off;
+	u64 addr_len;
+};
+
+struct efi_pci_root_bridge_io_protocol {
+	efi_handle_t					parent_handle;
+	efi_pci_root_bridge_io_protocol_poll_io_mem	poll_mem;
+	efi_pci_root_bridge_io_protocol_poll_io_mem	poll_io;
+	struct efi_pci_root_bridge_io_protocol_access	mem;
+	struct efi_pci_root_bridge_io_protocol_access	io;
+	struct efi_pci_root_bridge_io_protocol_access	pci;
+	efi_pci_root_bridge_io_protocol_copy_mem	copy_mem;
+	efi_pci_root_bridge_io_protocol_map		map;
+	efi_pci_root_bridge_io_protocol_unmap		unmap;
+	efi_pci_root_bridge_io_protocol_allocate_buffer	allocate_buffer;
+	efi_pci_root_bridge_io_protocol_free_buffer	free_buffer;
+	efi_pci_root_bridge_io_protocol_flush		flush;
+	efi_pci_root_bridge_io_protocol_get_attributes	get_attributes;
+	efi_pci_root_bridge_io_protocol_set_attributes	set_attributes;
+	efi_pci_root_bridge_io_protocol_configuration	configuration;
+	u32						segmentno;
+};
+
+struct efi_pci_io_protocol {
+	efi_pci_io_protocol_poll_io_mem		poll_mem;
+	efi_pci_io_protocol_poll_io_mem		poll_io;
+	struct efi_pci_io_protocol_access	mem;
+	struct efi_pci_io_protocol_access	io;
+	struct efi_pci_io_protocol_access	pci;
+	efi_pci_io_protocol_copy_mem		copy_mem;
+	efi_pci_io_protocol_map			map;
+	efi_pci_io_protocol_unmap		unmap;
+	efi_pci_io_protocol_allocate_buffer	allocate_buffer;
+	efi_pci_io_protocol_free_buffer		free_buffer;
+	efi_pci_io_protocol_flush		flush;
+	efi_pci_io_protocol_get_location	get_location;
+	efi_pci_io_protocol_attributes		attributes;
+	efi_pci_io_protocol_get_bar_attributes	get_bar_attributes;
+	efi_pci_io_protocol_set_bar_attributes	set_bar_attributes;
+	u64					rom_size;
+	void					*rom_image;
+};
+
+#define EFI_PCI_ADDRESS(bus, dev, func, reg) \
+  (u64) ( \
+  (((size_t) bus) << 24) | \
+  (((size_t) dev) << 16) | \
+  (((size_t) func) << 8) | \
+  (((size_t) (reg)) < 256 ? ((size_t) (reg)) : ((u64)(reg)) << 32))
+
+#endif
-- 
2.31.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 4/4] watchdog: add support for Intel TCO watchdog timer
  2021-04-16  6:24 [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2021-04-16  6:24 ` [PATCH 3/4] pci: add EFI PCI root bridge IO protocol driver Ahmad Fatoum
@ 2021-04-16  6:24 ` Ahmad Fatoum
  2021-05-03 12:07 ` [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support Sascha Hauer
  4 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2021-04-16  6:24 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

Variants of the iTCO are integrated into many Intel southbridges.
They are most often accessed via PCI. Add a driver for the variant
found in the q35 QEMU machine.

It should be straight forward to extend the itco_chipset_info array
to support more variants in future as the need arises. To test, use:

  qemu-system-x86_64 -M q35 -global ICH9-LPC.noreboot=false

The last option corresponds to a pin strap option, which can't be
influenced from within the VM.

Signed-off-by: Ahmad Fatoum <ahmad@a3f.at>
---
 arch/x86/configs/efi_defconfig |   1 +
 drivers/watchdog/Kconfig       |  17 ++
 drivers/watchdog/Makefile      |   1 +
 drivers/watchdog/itco_wdt.c    | 346 +++++++++++++++++++++++++++++++++
 4 files changed, 365 insertions(+)
 create mode 100644 drivers/watchdog/itco_wdt.c

diff --git a/arch/x86/configs/efi_defconfig b/arch/x86/configs/efi_defconfig
index e007bf8a0150..73614dd4b466 100644
--- a/arch/x86/configs/efi_defconfig
+++ b/arch/x86/configs/efi_defconfig
@@ -87,6 +87,7 @@ CONFIG_STATE_DRV=y
 CONFIG_WATCHDOG=y
 CONFIG_WATCHDOG_EFI=y
 CONFIG_F71808E_WDT=y
+CONFIG_ITCO_WDT=y
 # CONFIG_PINCTRL is not set
 CONFIG_PCI_EFI=y
 CONFIG_FS_EXT4=y
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index cf83b6a15bd4..76c1a89edc19 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -117,4 +117,21 @@ config F71808E_WDT
 	  F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866
 	  Super I/O controllers.
 
+config ITCO_WDT
+	bool "Intel TCO Timer/Watchdog"
+	depends on X86
+	depends on PCI
+	help
+	  Hardware driver for the intel TCO timer based watchdog devices.
+	  These drivers are included in the Intel 82801 I/O Controller
+	  Hub family (from ICH0 up to ICH10) and in the Intel 63xxESB
+	  controller hub.
+
+	  The TCO (Total Cost of Ownership) timer is a watchdog timer
+	  that will reboot the machine after its second expiration.
+
+	  On some motherboards the driver may fail to reset the chipset's
+	  NO_REBOOT flag which prevents the watchdog from rebooting the
+	  machine.
+
 endif
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index dc9842770a62..b55c58cf3cb3 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
 obj-$(CONFIG_STM32_IWDG_WATCHDOG) += stm32_iwdg.o
 obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
 obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
+obj-$(CONFIG_ITCO_WDT) += itco_wdt.o
diff --git a/drivers/watchdog/itco_wdt.c b/drivers/watchdog/itco_wdt.c
new file mode 100644
index 000000000000..e7bd0fc99bbb
--- /dev/null
+++ b/drivers/watchdog/itco_wdt.c
@@ -0,0 +1,346 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * EFI Boot Guard, iTCO support (Version 3 and later)
+ *
+ * Copyright (c) 2006-2011 Wim Van Sebroeck <wim@iguana.be>.
+ * Copyright (c) 2019 Siemens AG
+ * Copyright (c) 2019 Ahmad Fatoum, Pengutronix
+ *
+ * Authors:
+ *  Jan Kiszka <jan.kiszka@siemens.com>
+ *  Christian Storm <christian.storm@siemens.com>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <efi.h>
+#include <linux/pci.h>
+#include <watchdog.h>
+
+#define ACPIBASE		0x40
+#define ACPICTRL_PMCBASE	0x44
+
+#define PMBASE_ADDRMASK		0x0000ff80
+#define PMCBASE_ADDRMASK	0xfffffe00
+
+#define ACPIBASE_GCS_OFF	0x3410
+
+#define ACPIBASE_SMI_OFF	0x30
+#define ACPIBASE_SMI_END	0x33
+#define ACPIBASE_PMC_OFF	0x08
+#define ACPIBASE_PMC_END	0x0c
+#define ACPIBASE_TCO_OFF	0x60
+#define ACPIBASE_TCO_END	0x7f
+
+#define SMI_TCO_MASK		(1 << 13)
+
+#define TCO_TMR_HLT_MASK	(1 << 11)
+
+/* SMI Control and Enable Register */
+#define SMI_EN(itco)	((itco)->smibase)
+/* TCO base address */
+#define TCOBASE(itco)	((itco)->tcobase)
+
+#define TCO_RLD(p)	(TCOBASE(p) + 0x00) /* TCO Timer Reload/Curr. Value */
+#define TCOv1_TMR(p)	(TCOBASE(p) + 0x01) /* TCOv1 Timer Initial Value*/
+#define TCO_DAT_IN(p)	(TCOBASE(p) + 0x02) /* TCO Data In Register	*/
+#define TCO_DAT_OUT(p)	(TCOBASE(p) + 0x03) /* TCO Data Out Register	*/
+#define TCO1_STS(p)	(TCOBASE(p) + 0x04) /* TCO1 Status Register	*/
+#define TCO2_STS(p)	(TCOBASE(p) + 0x06) /* TCO2 Status Register	*/
+#define TCO1_CNT(p)	(TCOBASE(p) + 0x08) /* TCO1 Control Register	*/
+#define TCO2_CNT(p)	(TCOBASE(p) + 0x0a) /* TCO2 Control Register	*/
+#define TCOv2_TMR(p)	(TCOBASE(p) + 0x12) /* TCOv2 Timer Initial Value*/
+
+#define PMC_NO_REBOOT_MASK	(1 << 4)
+
+#define RCBABASE		0xf0
+
+#define PCI_ID_ITCO_INTEL_ICH9		0x2918
+
+struct itco_priv;
+
+struct itco_info {
+	u32 pci_id;
+	const char *name;
+	u32 pmbase;
+	u32 smireg;
+	int (*update_no_reboot_bit)(struct itco_priv *itco, bool set);
+	unsigned version;
+};
+
+struct itco_priv {
+	struct pci_dev *pdev;
+	struct watchdog wdd;
+	void __iomem *io;
+	u32 smibase;
+	u32 tcobase;
+	void __iomem *gcs_pmc;
+	struct itco_info *info;
+	unsigned timeout;
+};
+
+static u32 itco_get_pmbase(struct itco_priv *itco)
+{
+	u32 pmbase = itco->info->pmbase;
+
+	if (!pmbase)
+		pci_read_config_dword(itco->pdev, ACPIBASE, &pmbase);
+
+	return pmbase & PMBASE_ADDRMASK;
+}
+
+static inline struct itco_priv *to_itco_priv(struct watchdog *wdd)
+{
+	return container_of(wdd, struct itco_priv, wdd);
+}
+
+static void itco_wdt_ping(struct itco_priv *itco)
+{
+	/* Reload the timer by writing to the TCO Timer Counter register */
+	outw(0x0001, TCO_RLD(itco));
+}
+
+static inline unsigned int seconds_to_ticks(struct itco_priv *itco, int secs)
+{
+	return itco->info->version == 3 ? secs : (secs * 10) / 6;
+}
+
+static inline unsigned int ticks_to_seconds(struct itco_priv *itco, int ticks)
+{
+	return itco->info->version == 3 ? ticks : (ticks * 6) / 10;
+}
+
+
+static int itco_wdt_start(struct itco_priv *itco, unsigned int timeout)
+{
+	unsigned tmrval;
+	u32 value;
+	int ret;
+
+	tmrval = seconds_to_ticks(itco, timeout);
+
+	/* Enable TCO SMIs */
+	value = inl(SMI_EN(itco)) | SMI_TCO_MASK;
+	outl(value, SMI_EN(itco));
+
+	/* Set timer value */
+	value = inw(TCOv2_TMR(itco));
+
+	value &= 0xfc00;
+	value |= tmrval & 0x3ff;
+
+	outw(value, TCOv2_TMR(itco));
+	value = inw(TCOv2_TMR(itco));
+
+	if ((value & 0x3ff) != tmrval)
+		return -EINVAL;
+
+	/* Force reloading of timer value */
+	outw(1, TCO_RLD(itco));
+
+	/* Clear NO_REBOOT flag */
+	ret = itco->info->update_no_reboot_bit(itco, false);
+	if (ret)
+		return ret;
+
+	/* Clear HLT flag to start timer */
+	value = inw(TCO1_CNT(itco)) & ~TCO_TMR_HLT_MASK;
+	outw(value, TCO1_CNT(itco));
+	value = inw(TCO1_CNT(itco));
+
+	if (value & 0x0800)
+		return -EIO;
+
+	return 0;
+}
+
+static int itco_wdt_stop(struct itco_priv *itco)
+{
+	u32 val;
+
+	/* Bit 11: TCO Timer Halt -> 1 = The TCO timer is disabled */
+	val = inw(TCO1_CNT(itco)) | 0x0800;
+	outw(val, TCO1_CNT(itco));
+	val = inb(TCO1_CNT(itco));
+
+	/* Set the NO_REBOOT bit to prevent later reboots, just for sure */
+	itco->info->update_no_reboot_bit(itco, true);
+
+	if ((val & 0x0800) == 0)
+		return -EIO;
+	return 0;
+}
+
+static int itco_wdt_set_timeout(struct watchdog *wdd, unsigned int timeout)
+{
+	struct itco_priv *itco = to_itco_priv(wdd);
+	int ret;
+
+	if (!timeout)
+		return itco_wdt_stop(itco);
+
+	/* from the specs: */
+	/* "Values of 0h-3h are ignored and should not be attempted" */
+	if (timeout < 0x04)
+		return -EINVAL;
+
+	if (itco->timeout != timeout) {
+		ret = itco_wdt_start(itco, timeout);
+		if (ret) {
+			dev_err(wdd->hwdev, "Fail to (re)start watchdog\n");
+			return ret;
+		}
+	}
+
+	itco_wdt_ping(itco);
+	return 0;
+}
+
+static inline u32 no_reboot_bit(unsigned version)
+{
+	u32 enable_bit;
+
+	switch (version) {
+	case 5:
+	case 3:
+		enable_bit = 0x00000010;
+		break;
+	case 2:
+		enable_bit = 0x00000020;
+		break;
+	case 4:
+	case 1:
+	default:
+		enable_bit = 0x00000002;
+		break;
+	}
+
+	return enable_bit;
+}
+
+
+static int update_no_reboot_bit(struct itco_priv *itco, bool set)
+{
+	u32 val32 = 0, newval32 = 0;
+
+	val32 = readl(itco->gcs_pmc);
+	if (set)
+		val32 |= no_reboot_bit(itco->info->version);
+	else
+		val32 &= ~no_reboot_bit(itco->info->version);
+	writel(val32, itco->gcs_pmc);
+	newval32 = readl(itco->gcs_pmc);
+
+	/* make sure the update is successful */
+	if (val32 != newval32)
+		return -EPERM;
+
+	return 0;
+}
+
+static void lpc_ich_enable_acpi_space(struct itco_priv *itco)
+{
+	u8 reg_save;
+
+	pci_read_config_byte(itco->pdev, ACPICTRL_PMCBASE, &reg_save);
+	pci_write_config_byte(itco->pdev, ACPICTRL_PMCBASE, reg_save | 0x80);
+}
+
+enum itco_chipsets {
+	ITCO_INTEL_ICH9,
+};
+
+/* version 1 not supported! */
+static struct itco_info itco_chipset_info[] = {
+	[ITCO_INTEL_ICH9] = {
+		.pci_id = PCI_ID_ITCO_INTEL_ICH9,
+		.name = "ICH9", /* QEmu machine q35 */
+		.smireg = 0x30,
+		.update_no_reboot_bit = update_no_reboot_bit,
+		.version = 2,
+	},
+};
+
+static int itco_wdt_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct itco_priv *itco;
+	struct watchdog *wdd;
+	u32 rcba_base_cfg;
+	u32 pmbase;
+	int ret;
+	int i;
+
+	pci_enable_device(pdev);
+	pci_set_master(pdev);
+
+	itco = xzalloc(sizeof(*itco));
+
+	itco->pdev = pdev;
+
+	for (i = 0; i < ARRAY_SIZE(itco_chipset_info); i++) {
+		if (id->device == itco_chipset_info[i].pci_id) {
+			itco->info = &itco_chipset_info[i];
+			break;
+		}
+	}
+
+	if (!itco->info)
+		return -ENODEV;
+
+
+	pci_read_config_dword(itco->pdev, RCBABASE, &rcba_base_cfg);
+	if (!(rcba_base_cfg & 1)) {
+		dev_notice(&pdev->dev, "RCBA is disabled by hardware/BIOS, device disabled\n");
+		return -ENODEV;
+	}
+
+	pmbase = itco_get_pmbase(itco);
+	if (!pmbase) {
+		dev_notice(&itco->pdev->dev, "I/O space for ACPI uninitialized\n");
+		return -ENODEV;
+	}
+
+	itco->smibase = pmbase + ACPIBASE_SMI_OFF;
+	itco->tcobase = pmbase + ACPIBASE_TCO_OFF;
+
+	lpc_ich_enable_acpi_space(itco);
+
+	itco->gcs_pmc = IOMEM(rcba_base_cfg & 0xffffc000UL) + ACPIBASE_GCS_OFF;
+
+
+	dev_notice(&pdev->dev, "gcs_pmc = 0x%p, smibase = 0x%x, tcobase = 0x%x\n",
+		   itco->gcs_pmc, itco->smibase, itco->tcobase);
+
+	wdd = &itco->wdd;
+	wdd->hwdev = &pdev->dev;
+	wdd->set_timeout = itco_wdt_set_timeout;
+
+	wdd->timeout_max = ticks_to_seconds(itco, 0x3ff);
+
+	outw(0x0008, TCO1_STS(itco)); /* Clear the Time Out Status bit */
+	outw(0x0002, TCO2_STS(itco)); /* Clear SECOND_TO_STS bit */
+	outw(0x0004, TCO2_STS(itco)); /* Clear BOOT_STS bit */
+
+	ret = watchdog_register(wdd);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register watchdog device\n");
+		return ret;
+	}
+
+	dev_info(&pdev->dev, "probed Intel TCO %s watchdog\n", itco->info->name);
+
+	return 0;
+}
+
+
+static DEFINE_PCI_DEVICE_TABLE(itco_wdt_pci_tbl) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ID_ITCO_INTEL_ICH9) },
+	{ /* sentinel */ },
+};
+
+static struct pci_driver itco_wdt_driver = {
+	.name = "itco_wdt",
+	.id_table = itco_wdt_pci_tbl,
+	.probe = itco_wdt_probe,
+};
+device_pci_driver(itco_wdt_driver);
-- 
2.31.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support
  2021-04-16  6:24 [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support Ahmad Fatoum
                   ` (3 preceding siblings ...)
  2021-04-16  6:24 ` [PATCH 4/4] watchdog: add support for Intel TCO watchdog timer Ahmad Fatoum
@ 2021-05-03 12:07 ` Sascha Hauer
  4 siblings, 0 replies; 6+ messages in thread
From: Sascha Hauer @ 2021-05-03 12:07 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox

On Fri, Apr 16, 2021 at 08:24:32AM +0200, Ahmad Fatoum wrote:
> v1 is here:
> https://lore.barebox.org/barebox/20191204125659.22506-1-a.fatoum@pengutronix.de/
> 
> Series tested with:
>   * QEMU q35: ns16550 serial, iTCO and virtio-pci
>   * Dell Latitude 7490: Bus enumeration works correctly
> 
> v1 -> v2:
>   - add DMA helpers, so the new virtio-pci support is usable on x86 as
>     well
>   - add preassigned bus support, so already running PCI UEFI drivers can
>     continue to function
>   - handle zero size resources correctly
>   - add iTCO watchdog support in new commit
> 
> Cheers,
> Ahmad Fatoum (4):
>   x86: add DMA support
>   PCI: support PCI BIOS preassigned buses
>   pci: add EFI PCI root bridge IO protocol driver
>   watchdog: add support for Intel TCO watchdog timer

Applied, thanks

Sascha


-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-05-03 12:10 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-16  6:24 [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support Ahmad Fatoum
2021-04-16  6:24 ` [PATCH 1/4] x86: add DMA support Ahmad Fatoum
2021-04-16  6:24 ` [PATCH 2/4] PCI: support PCI BIOS preassigned buses Ahmad Fatoum
2021-04-16  6:24 ` [PATCH 3/4] pci: add EFI PCI root bridge IO protocol driver Ahmad Fatoum
2021-04-16  6:24 ` [PATCH 4/4] watchdog: add support for Intel TCO watchdog timer Ahmad Fatoum
2021-05-03 12:07 ` [PATCH 0/4] x86: add EFI PCI host and iTCO watchdog support Sascha Hauer

mail archive of the barebox mailing list

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://lore.barebox.org/barebox/0 barebox/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 barebox barebox/ https://lore.barebox.org/barebox \
		barebox@lists.infradead.org barebox@lists.infradead.org
	public-inbox-index barebox

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git