summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/include/asm/pci.h7
-rw-r--r--arch/mips/include/asm/pci.h7
-rw-r--r--arch/x86/Kconfig1
-rw-r--r--arch/x86/configs/efi_defconfig2
-rw-r--r--arch/x86/include/asm/dma.h36
-rw-r--r--arch/x86/include/asm/pci.h7
-rw-r--r--drivers/efi/Kconfig1
-rw-r--r--drivers/pci/Kconfig5
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/pci-efi.c342
-rw-r--r--drivers/pci/pci-efi.h343
-rw-r--r--drivers/pci/pci.c106
-rw-r--r--drivers/watchdog/Kconfig17
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/itco_wdt.c346
-rw-r--r--include/linux/pci.h3
16 files changed, 1187 insertions, 38 deletions
diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h
new file mode 100644
index 0000000000..d7419cabe7
--- /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 0000000000..d7419cabe7
--- /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/arch/x86/Kconfig b/arch/x86/Kconfig
index 311c3d1a8e..bcb44b23f0 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/configs/efi_defconfig b/arch/x86/configs/efi_defconfig
index 761ffbe261..73614dd4b4 100644
--- a/arch/x86/configs/efi_defconfig
+++ b/arch/x86/configs/efi_defconfig
@@ -87,7 +87,9 @@ 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
CONFIG_FS_TFTP=y
CONFIG_FS_NFS=y
diff --git a/arch/x86/include/asm/dma.h b/arch/x86/include/asm/dma.h
index 3dab2b688d..8a3b044f3a 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 */
diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h
new file mode 100644
index 0000000000..ca1c0f912d
--- /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 cca1a2e1d6..80d9e6f0c5 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 0585460976..71d05055d4 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 d227619ed4..6fc4eaf6b2 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 0000000000..e1fe11d070
--- /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 0000000000..1943461cdf
--- /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
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 945a983387..950c509447 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/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index df85a227ac..b785181c59 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -124,4 +124,21 @@ config GPIO_WATCHDOG
If you say yes here you get support for watchdog device
controlled through GPIO-line.
+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 e88da0adaf..0b598af402 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_STM32_IWDG_WATCHDOG) += stm32_iwdg.o
obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
obj-$(CONFIG_GPIO_WATCHDOG) += gpio_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 0000000000..e7bd0fc99b
--- /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);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 0c8fed7c8e..486d4251d4 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