diff options
author | Simon Glass <sjg@chromium.org> | 2019-12-06 21:41:55 -0700 |
---|---|---|
committer | Bin Meng <bmeng.cn@gmail.com> | 2019-12-15 11:44:11 +0800 |
commit | 5bee27aa41eeafb71f1d467f81f7fae6ee8cfa6d (patch) | |
tree | 5db81fff6d52cc7ee4f9f707eaaa82d90e930dfb /drivers | |
parent | 3b65ee34b908ce0c495c25987f5feb37ac163eab (diff) |
pci: Add support for p2sb uclass
The Primary-to-Sideband bus (P2SB) is used to access various peripherals
through memory-mapped I/O in a large chunk of PCI space. The space is
segmented into different channels and peripherals are accessed by
device-specific means within those channels. Devices should be added in
the device tree as subnodes of the p2sb.
This adds a uclass and enables it for sandbox.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/misc/Kconfig | 33 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/p2sb-uclass.c | 216 |
3 files changed, 250 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 82bb093c56..71643af9c2 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -226,6 +226,39 @@ config NUVOTON_NCT6102D disable the legacy UART, the watchdog or other devices in the Nuvoton Super IO chips on X86 platforms. +config P2SB + bool "Intel Primary-to-Sideband Bus" + depends on X86 || SANDBOX + help + This enables support for the Intel Primary-to-Sideband bus, + abbreviated to P2SB. The P2SB is used to access various peripherals + such as eSPI, GPIO, through memory-mapped I/O in a large chunk of PCI + space. The space is segmented into different channels and peripherals + are accessed by device-specific means within those channels. Devices + should be added in the device tree as subnodes of the P2SB. A + Peripheral Channel Register? (PCR) API is provided to access those + devices - see pcr_readl(), etc. + +config SPL_P2SB + bool "Intel Primary-to-Sideband Bus in SPL" + depends on SPL && (X86 || SANDBOX) + help + The Primary-to-Sideband bus is used to access various peripherals + through memory-mapped I/O in a large chunk of PCI space. The space is + segmented into different channels and peripherals are accessed by + device-specific means within those channels. Devices should be added + in the device tree as subnodes of the p2sb. + +config TPL_P2SB + bool "Intel Primary-to-Sideband Bus in TPL" + depends on TPL && (X86 || SANDBOX) + help + The Primary-to-Sideband bus is used to access various peripherals + through memory-mapped I/O in a large chunk of PCI space. The space is + segmented into different channels and peripherals are accessed by + device-specific means within those channels. Devices should be added + in the device tree as subnodes of the p2sb. + config PWRSEQ bool "Enable power-sequencing drivers" depends on DM diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 55976d6be5..78b598b367 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_MXC_OCOTP) += mxc_ocotp.o obj-$(CONFIG_MXS_OCOTP) += mxs_ocotp.o obj-$(CONFIG_NS87308) += ns87308.o obj-$(CONFIG_NUVOTON_NCT6102D) += nuvoton_nct6102d.o +obj-$(CONFIG_P2SB) += p2sb-uclass.o obj-$(CONFIG_PCA9551_LED) += pca9551_led.o obj-$(CONFIG_$(SPL_)PWRSEQ) += pwrseq-uclass.o obj-$(CONFIG_QFW) += qfw.o diff --git a/drivers/misc/p2sb-uclass.c b/drivers/misc/p2sb-uclass.c new file mode 100644 index 0000000000..a198700b5f --- /dev/null +++ b/drivers/misc/p2sb-uclass.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Uclass for Primary-to-sideband bus, used to access various peripherals + * + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <p2sb.h> +#include <spl.h> +#include <asm/io.h> +#include <dm/uclass-internal.h> + +#define PCR_COMMON_IOSF_1_0 1 + +static void *_pcr_reg_address(struct udevice *dev, uint offset) +{ + struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev); + struct udevice *p2sb = dev_get_parent(dev); + struct p2sb_uc_priv *upriv = dev_get_uclass_priv(p2sb); + uintptr_t reg_addr; + + /* Create an address based off of port id and offset */ + reg_addr = upriv->mmio_base; + reg_addr += pplat->pid << PCR_PORTID_SHIFT; + reg_addr += offset; + + return map_sysmem(reg_addr, 4); +} + +/* + * The mapping of addresses via the SBREG_BAR assumes the IOSF-SB + * agents are using 32-bit aligned accesses for their configuration + * registers. For IOSF versions greater than 1_0, IOSF-SB + * agents can use any access (8/16/32 bit aligned) for their + * configuration registers + */ +static inline void check_pcr_offset_align(uint offset, uint size) +{ + const size_t align = PCR_COMMON_IOSF_1_0 ? sizeof(uint32_t) : size; + + assert(IS_ALIGNED(offset, align)); +} + +uint pcr_read32(struct udevice *dev, uint offset) +{ + void *ptr; + uint val; + + /* Ensure the PCR offset is correctly aligned */ + assert(IS_ALIGNED(offset, sizeof(uint32_t))); + + ptr = _pcr_reg_address(dev, offset); + val = readl(ptr); + unmap_sysmem(ptr); + + return val; +} + +uint pcr_read16(struct udevice *dev, uint offset) +{ + /* Ensure the PCR offset is correctly aligned */ + check_pcr_offset_align(offset, sizeof(uint16_t)); + + return readw(_pcr_reg_address(dev, offset)); +} + +uint pcr_read8(struct udevice *dev, uint offset) +{ + /* Ensure the PCR offset is correctly aligned */ + check_pcr_offset_align(offset, sizeof(uint8_t)); + + return readb(_pcr_reg_address(dev, offset)); +} + +/* + * After every write one needs to perform a read an innocuous register to + * ensure the writes are completed for certain ports. This is done for + * all ports so that the callers don't need the per-port knowledge for + * each transaction. + */ +static void write_completion(struct udevice *dev, uint offset) +{ + readl(_pcr_reg_address(dev, ALIGN_DOWN(offset, sizeof(uint32_t)))); +} + +void pcr_write32(struct udevice *dev, uint offset, uint indata) +{ + /* Ensure the PCR offset is correctly aligned */ + assert(IS_ALIGNED(offset, sizeof(indata))); + + writel(indata, _pcr_reg_address(dev, offset)); + /* Ensure the writes complete */ + write_completion(dev, offset); +} + +void pcr_write16(struct udevice *dev, uint offset, uint indata) +{ + /* Ensure the PCR offset is correctly aligned */ + check_pcr_offset_align(offset, sizeof(uint16_t)); + + writew(indata, _pcr_reg_address(dev, offset)); + /* Ensure the writes complete */ + write_completion(dev, offset); +} + +void pcr_write8(struct udevice *dev, uint offset, uint indata) +{ + /* Ensure the PCR offset is correctly aligned */ + check_pcr_offset_align(offset, sizeof(uint8_t)); + + writeb(indata, _pcr_reg_address(dev, offset)); + /* Ensure the writes complete */ + write_completion(dev, offset); +} + +void pcr_clrsetbits32(struct udevice *dev, uint offset, uint clr, uint set) +{ + uint data32; + + data32 = pcr_read32(dev, offset); + data32 &= ~clr; + data32 |= set; + pcr_write32(dev, offset, data32); +} + +void pcr_clrsetbits16(struct udevice *dev, uint offset, uint clr, uint set) +{ + uint data16; + + data16 = pcr_read16(dev, offset); + data16 &= ~clr; + data16 |= set; + pcr_write16(dev, offset, data16); +} + +void pcr_clrsetbits8(struct udevice *dev, uint offset, uint clr, uint set) +{ + uint data8; + + data8 = pcr_read8(dev, offset); + data8 &= ~clr; + data8 |= set; + pcr_write8(dev, offset, data8); +} + +int p2sb_get_port_id(struct udevice *dev) +{ + struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev); + + return pplat->pid; +} + +int p2sb_set_port_id(struct udevice *dev, int portid) +{ + struct udevice *ps2b; + struct p2sb_child_platdata *pplat; + + if (!CONFIG_IS_ENABLED(OF_PLATDATA)) + return -ENOSYS; + + uclass_find_first_device(UCLASS_P2SB, &ps2b); + if (!ps2b) + return -EDEADLK; + dev->parent = ps2b; + + /* + * We must allocate this, since when the device was bound it did not + * have a parent. + * TODO(sjg@chromium.org): Add a parent pointer to child devices in dtoc + */ + dev->parent_platdata = malloc(sizeof(*pplat)); + if (!dev->parent_platdata) + return -ENOMEM; + pplat = dev_get_parent_platdata(dev); + pplat->pid = portid; + + return 0; +} + +static int p2sb_child_post_bind(struct udevice *dev) +{ +#if !CONFIG_IS_ENABLED(OF_PLATDATA) + struct p2sb_child_platdata *pplat = dev_get_parent_platdata(dev); + int ret; + u32 pid; + + ret = dev_read_u32(dev, "intel,p2sb-port-id", &pid); + if (ret) + return ret; + pplat->pid = pid; +#endif + + return 0; +} + +static int p2sb_post_bind(struct udevice *dev) +{ + if (spl_phase() > PHASE_TPL && !CONFIG_IS_ENABLED(OF_PLATDATA)) + return dm_scan_fdt_dev(dev); + + return 0; +} + +UCLASS_DRIVER(p2sb) = { + .id = UCLASS_P2SB, + .name = "p2sb", + .per_device_auto_alloc_size = sizeof(struct p2sb_uc_priv), + .post_bind = p2sb_post_bind, + .child_post_bind = p2sb_child_post_bind, + .per_child_platdata_auto_alloc_size = + sizeof(struct p2sb_child_platdata), +}; |