From 9745fb07474f4501eff62130a78a42a8b8c18b05 Mon Sep 17 00:00:00 2001 From: Jonathan Yong Date: Mon, 6 Jun 2022 19:41:27 +0300 Subject: platform/x86/intel: Add Primary to Sideband (P2SB) bridge support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SoC features such as GPIO are accessed via a reserved MMIO area, we don't know its address but can obtain it from the BAR of the P2SB device, that device is normally hidden so we have to temporarily unhide it, read address and hide it back. There are already a few users and at least one more is coming which require an access to Primary to Sideband (P2SB) bridge in order to get IO or MMIO BAR hidden by BIOS. Create a library to access P2SB for x86 devices in a unified way. Background information ====================== Note, the term "bridge" is used in the documentation and it has nothing to do with a PCI (host) bridge as per the PCI specifications. The P2SB is an interesting device by its nature and hardware design. First of all, it has several devices in the hardware behind it. These devices may or may not be represented as ACPI devices by a firmware. It also has a hardwired (to 0s) the least significant bits of the base address register which is represented by the only 64-bit BAR0. It means that OS mustn't reallocate the BAR. On top of that in some cases P2SB is represented by function 0 on PCI slot (in terms of B:D.F) and according to the PCI specification any other function can't be seen until function 0 is present and visible. In the PCI configuration space of P2SB device the full 32-bit register is allocated for the only purpose of hiding the entire P2SB device. As per [3]: 3.1.39 P2SB Control (P2SBC)—Offset E0h Hide Device (HIDE): When this bit is set, the P2SB will return 1s on any PCI Configuration Read on IOSF-P. All other transactions including PCI Configuration Writes on IOSF-P are unaffected by this. This does not affect reads performed on the IOSF-SB interface. This doesn't prevent MMIO accesses, although preventing the OS from assigning these addresses. The firmware on the affected platforms marks the region as unusable (by cutting it off from the PCI host bridge resources) as depicted in the Apollo Lake example below: PCI host bridge to bus 0000:00 pci_bus 0000:00: root bus resource [io 0x0070-0x0077] pci_bus 0000:00: root bus resource [io 0x0000-0x006f window] pci_bus 0000:00: root bus resource [io 0x0078-0x0cf7 window] pci_bus 0000:00: root bus resource [io 0x0d00-0xffff window] pci_bus 0000:00: root bus resource [mem 0x7c000001-0x7fffffff window] pci_bus 0000:00: root bus resource [mem 0x7b800001-0x7bffffff window] pci_bus 0000:00: root bus resource [mem 0x80000000-0xcfffffff window] pci_bus 0000:00: root bus resource [mem 0xe0000000-0xefffffff window] pci_bus 0000:00: root bus resource [bus 00-ff] The P2SB 16MB BAR is located at 0xd0000000-0xd0ffffff memory window. The generic solution ==================== The generic solution for all cases when we need to access to the information behind P2SB device is a library code where users ask for necessary resources by demand and hence those users take care of not being run on the systems where this access is not required. The library provides the p2sb_bar() API to retrieve the MMIO of the BAR0 of the device from P2SB device slot. P2SB unconditional unhiding awareness ===================================== Technically it's possible to unhide the P2SB device and devices on the same PCI slot and access them at any time as needed. But there are several potential issues with that: - the systems were never tested against such configuration and hence nobody knows what kind of bugs it may bring, especially when we talk about SPI NOR case which contains Intel FirmWare Image (IFWI) code (including BIOS) and already known to be problematic in the past for end users - the PCI by its nature is a hotpluggable bus and in case somebody attaches a driver to the functions of a P2SB slot device(s) the end user experience and system behaviour can be unpredictable - the kernel code would need some ugly hacks (or code looking as an ugly hack) under arch/x86/pci in order to enable these devices on only selected platforms (which may include CPU ID table followed by a potentially growing number of DMI strings The future improvements ======================= The future improvements with this code may go in order to gain some kind of cache, if it's possible at all, to prevent unhiding and hiding many times to take static information that may be saved once per boot. Links ===== [1]: https://lab.whitequark.org/notes/2017-11-08/accessing-intel-ich-pch-gpios/ [2]: https://cdrdv2.intel.com/v1/dl/getContent/332690?wapkw=332690 [3]: https://cdrdv2.intel.com/v1/dl/getContent/332691?wapkw=332691 [4]: https://medium.com/@jacksonchen_43335/bios-gpio-p2sb-70e9b829b403 Signed-off-by: Jonathan Yong Co-developed-by: Andy Shevchenko Signed-off-by: Andy Shevchenko Tested-by: Henning Schild Acked-by: Hans de Goede Acked-by: Linus Walleij Signed-off-by: Lee Jones --- include/linux/platform_data/x86/p2sb.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 include/linux/platform_data/x86/p2sb.h (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/x86/p2sb.h b/include/linux/platform_data/x86/p2sb.h new file mode 100644 index 000000000000..a1d5fddc8f13 --- /dev/null +++ b/include/linux/platform_data/x86/p2sb.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Primary to Sideband (P2SB) bridge access support + */ + +#ifndef _PLATFORM_DATA_X86_P2SB_H +#define _PLATFORM_DATA_X86_P2SB_H + +#include +#include + +struct pci_bus; +struct resource; + +#if IS_BUILTIN(CONFIG_P2SB) + +int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem); + +#else /* CONFIG_P2SB */ + +static inline int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) +{ + return -ENODEV; +} + +#endif /* CONFIG_P2SB is not set */ + +#endif /* _PLATFORM_DATA_X86_P2SB_H */ -- cgit v1.2.3 From 446f0cf9e08b483d7dc6f61eee0ee846b22f6386 Mon Sep 17 00:00:00 2001 From: Henning Schild Date: Mon, 6 Jun 2022 19:41:37 +0300 Subject: platform/x86: simatic-ipc: drop custom P2SB bar code The two drivers that used to use this have been switched over to the common P2SB accessor, so this code is not needed any longer. Signed-off-by: Henning Schild Signed-off-by: Andy Shevchenko Reviewed-by: Hans de Goede Signed-off-by: Lee Jones --- drivers/platform/x86/simatic-ipc.c | 38 ---------------------- include/linux/platform_data/x86/simatic-ipc-base.h | 2 -- 2 files changed, 40 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/simatic-ipc.c b/drivers/platform/x86/simatic-ipc.c index b599cda5ba3c..26c35e1660cb 100644 --- a/drivers/platform/x86/simatic-ipc.c +++ b/drivers/platform/x86/simatic-ipc.c @@ -101,44 +101,6 @@ static int register_platform_devices(u32 station_id) return 0; } -/* FIXME: this should eventually be done with generic P2SB discovery code - * the individual drivers for watchdogs and LEDs access memory that implements - * GPIO, but pinctrl will not come up because of missing ACPI entries - * - * While there is no conflict a cleaner solution would be to somehow bring up - * pinctrl even with these ACPI entries missing, and base the drivers on pinctrl. - * After which the following function could be dropped, together with the code - * poking the memory. - */ -/* - * Get membase address from PCI, used in leds and wdt module. Here we read - * the bar0. The final address calculation is done in the appropriate modules - */ -u32 simatic_ipc_get_membase0(unsigned int p2sb) -{ - struct pci_bus *bus; - u32 bar0 = 0; - /* - * The GPIO memory is in bar0 of the hidden P2SB device. - * Unhide the device to have a quick look at it, before we hide it - * again. - * Also grab the pci rescan lock so that device does not get discovered - * and remapped while it is visible. - * This code is inspired by drivers/mfd/lpc_ich.c - */ - bus = pci_find_bus(0, 0); - pci_lock_rescan_remove(); - pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x0); - pci_bus_read_config_dword(bus, p2sb, PCI_BASE_ADDRESS_0, &bar0); - - bar0 &= ~0xf; - pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x1); - pci_unlock_rescan_remove(); - - return bar0; -} -EXPORT_SYMBOL(simatic_ipc_get_membase0); - static int __init simatic_ipc_init_module(void) { const struct dmi_system_id *match; diff --git a/include/linux/platform_data/x86/simatic-ipc-base.h b/include/linux/platform_data/x86/simatic-ipc-base.h index 62d2bc774067..39fefd48cf4d 100644 --- a/include/linux/platform_data/x86/simatic-ipc-base.h +++ b/include/linux/platform_data/x86/simatic-ipc-base.h @@ -24,6 +24,4 @@ struct simatic_ipc_platform { u8 devmode; }; -u32 simatic_ipc_get_membase0(unsigned int p2sb); - #endif /* __PLATFORM_DATA_X86_SIMATIC_IPC_BASE_H */ -- cgit v1.2.3