[U-Boot] [PATCH 074/126] pci: Add support for p2sb uclass

Bin Meng bmeng.cn at gmail.com
Thu Oct 10 04:57:09 UTC 2019


Hi Simon,

On Wed, Sep 25, 2019 at 10:59 PM Simon Glass <sjg at chromium.org> wrote:
>
> 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.
>

Again, I see no value of bringing P2SB to the uclass driver level,
given as of today it is only seen in ApolloLake. Similar mechanism was
seen in old x86 SoCs, and at the old time, it was called "Message
Port", that existed in old Atom SoCs.

> This adds a uclass and enables it for sandbox.
>
> Signed-off-by: Simon Glass <sjg at chromium.org>
> ---
>
>  configs/sandbox_defconfig     |   1 +
>  configs/sandbox_spl_defconfig |   1 +
>  drivers/misc/Kconfig          |  33 ++++++
>  drivers/misc/Makefile         |   1 +
>  drivers/misc/p2sb-uclass.c    | 199 ++++++++++++++++++++++++++++++++++
>  include/dm/uclass-id.h        |   1 +
>  include/p2sb.h                | 127 ++++++++++++++++++++++
>  7 files changed, 363 insertions(+)
>  create mode 100644 drivers/misc/p2sb-uclass.c
>  create mode 100644 include/p2sb.h
>
> diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
> index f77b9e8a7d1..6f4d8449290 100644
> --- a/configs/sandbox_defconfig
> +++ b/configs/sandbox_defconfig
> @@ -149,6 +149,7 @@ CONFIG_PCI=y
>  CONFIG_DM_PCI=y
>  CONFIG_DM_PCI_COMPAT=y
>  CONFIG_PCI_SANDBOX=y
> +CONFIG_P2SB=y
>  CONFIG_PHY=y
>  CONFIG_PHY_SANDBOX=y
>  CONFIG_PINCTRL=y
> diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig
> index 409b8a38d5e..c49e05ec319 100644
> --- a/configs/sandbox_spl_defconfig
> +++ b/configs/sandbox_spl_defconfig
> @@ -135,6 +135,7 @@ CONFIG_PCI=y
>  CONFIG_DM_PCI=y
>  CONFIG_DM_PCI_COMPAT=y
>  CONFIG_PCI_SANDBOX=y
> +CONFIG_P2SB=y
>  CONFIG_PHY=y
>  CONFIG_PHY_SANDBOX=y
>  CONFIG_PINCTRL=y
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index 8037b6ee2d7..74055a35516 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -217,6 +217,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 0001d105bae..840d9fa5e39 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 00000000000..7915eb5c3ed
> --- /dev/null
> +++ b/drivers/misc/p2sb-uclass.c
> @@ -0,0 +1,199 @@
> +// 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 at chromium.org>
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <mapmem.h>
> +#include <p2sb.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;

Having PCR_PORTID_SHIFT defined in the p2sb.h is not appropriate. A
different SoC may have a different value, or even choose a completely
different register layout, that makes the function here useless.

> +       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_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 at 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;
> +}
> +
> +UCLASS_DRIVER(p2sb) = {
> +       .id             = UCLASS_P2SB,
> +       .name           = "p2sb",
> +       .per_device_auto_alloc_size = sizeof(struct p2sb_uc_priv),
> +       .child_post_bind = p2sb_child_post_bind,
> +       .per_child_platdata_auto_alloc_size =
> +               sizeof(struct p2sb_child_platdata),
> +};
> diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
> index 907fe257ad1..496570ff47d 100644
> --- a/include/dm/uclass-id.h
> +++ b/include/dm/uclass-id.h
> @@ -68,6 +68,7 @@ enum uclass_id {
>         UCLASS_NOP,             /* No-op devices */
>         UCLASS_NORTHBRIDGE,     /* Intel Northbridge / SDRAM controller */
>         UCLASS_NVME,            /* NVM Express device */
> +       UCLASS_P2SB,            /* (x86) Primary-to-Sideband Bus */
>         UCLASS_PANEL,           /* Display panel, such as an LCD */
>         UCLASS_PANEL_BACKLIGHT, /* Backlight controller for panel */
>         UCLASS_PCH,             /* x86 platform controller hub */
> diff --git a/include/p2sb.h b/include/p2sb.h
> new file mode 100644
> index 00000000000..370f127058c
> --- /dev/null
> +++ b/include/p2sb.h
> @@ -0,0 +1,127 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright 2019 Google LLC
> + * Written by Simon Glass <sjg at chromium.org>
> + */
> +
> +#ifndef __p2sb_h
> +#define __p2sb_h
> +
> +/* Port Id lives in bits 23:16 and register offset lives in 15:0 of address */
> +#define PCR_PORTID_SHIFT       16
> +
> +/**
> + * struct p2sb_child_platdata - Information about each child of a p2sb device
> + *
> + * @pid: Port ID for this child
> + */
> +struct p2sb_child_platdata {
> +       uint pid;
> +};
> +
> +/**
> + * struct p2sb_uc_priv - information for the uclass about each device
> + *
> + * This must be set up by the driver when it is probed
> + *
> + * @mmio_base: Base address of P2SB region
> + */
> +struct p2sb_uc_priv {
> +       uint mmio_base;
> +};
> +
> +/**
> + * struct p2sb_ops - Operations for the P2SB (none at present)
> + */
> +struct p2sb_ops {
> +};
> +
> +#define p2sb_get_ops(dev)        ((struct p2sb_ops *)(dev)->driver->ops)
> +
> +/**
> + * pcr_read32/16/8() - Read from a PCR device
> + *
> + * Reads data from a PCR device within the P2SB
> + *
> + * @dev: Device to read from
> + * @offset: Offset within device to read
> + * @return value read
> + */
> +uint pcr_read32(struct udevice *dev, uint offset);
> +uint pcr_read16(struct udevice *dev, uint offset);
> +uint pcr_read8(struct udevice *dev, uint offset);
> +
> +/**
> + * pcr_read32/16/8() - Write to a PCR device
> + *
> + * Writes data to a PCR device within the P2SB
> + *
> + * @dev: Device to write to
> + * @offset: Offset within device to write
> + * @data: Data to write
> + */
> +void pcr_write32(struct udevice *dev, uint offset, uint data);
> +void pcr_write16(struct udevice *dev, uint offset, uint data);
> +void pcr_write8(struct udevice *dev, uint offset, uint data);
> +
> +/**
> + * pcr_clrsetbits32/16/8() - Update a PCR device
> + *
> + * Updates dat in a PCR device within the P2SB
> + *
> + * This reads from the device, clears and set bits, then writes back.
> + *
> + * new_data = (old_data & ~clr) | set
> + *
> + * @dev: Device to update
> + * @offset: Offset within device to update
> + * @clr: Bits to clear after reading
> + * @set: Bits to set before writing
> + */
> +void pcr_clrsetbits32(struct udevice *dev, uint offset, uint clr, uint set);
> +void pcr_clrsetbits16(struct udevice *dev, uint offset, uint clr, uint set);
> +void pcr_clrsetbits8(struct udevice *dev, uint offset, uint clr, uint set);
> +
> +static inline void pcr_setbits32(struct udevice *dev, uint offset, uint set)
> +{
> +       return pcr_clrsetbits32(dev, offset, 0, set);
> +}
> +
> +static inline void pcr_setbits16(struct udevice *dev, uint offset, uint set)
> +{
> +       return pcr_clrsetbits16(dev, offset, 0, set);
> +}
> +
> +static inline void pcr_setbits8(struct udevice *dev, uint offset, uint set)
> +{
> +       return pcr_clrsetbits8(dev, offset, 0, set);
> +}
> +
> +static inline void pcr_clrbits32(struct udevice *dev, uint offset, uint clr)
> +{
> +       return pcr_clrsetbits32(dev, offset, clr, 0);
> +}
> +
> +static inline void pcr_clrbits16(struct udevice *dev, uint offset, uint clr)
> +{
> +       return pcr_clrsetbits16(dev, offset, clr, 0);
> +}
> +
> +static inline void pcr_clrbits8(struct udevice *dev, uint offset, uint clr)
> +{
> +       return pcr_clrsetbits8(dev, offset, clr, 0);
> +}
> +
> +/**
> + * p2sb_set_port_id() - Set the port ID for a p2sb child device
> + *
> + * This must be called in a device's bind() method when OF_PLATDATA is used
> + * since the uclass cannot access the device's of-platdata.
> + *
> + * @dev: Child device (whose parent is UCLASS_P2SB)
> + * @portid: Port ID of child device
> + * @return 0 if OK, -ENODEV is the p2sb device could not be found
> + */
> +int p2sb_set_port_id(struct udevice *dev, int portid);
> +
> +#endif
> --

Regards,
Bin


More information about the U-Boot mailing list