On Mon, Nov 25, 2019 at 12:11 PM Simon Glass <s...@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. > > This adds a uclass and enables it for sandbox. > > Signed-off-by: Simon Glass <s...@chromium.org> > > --- > > Changes in v5: > - Add a way to obtain the port ID for a device > > Changes in v4: > - Adjust condition for binding children > > Changes in v3: None > Changes in v2: None > > configs/sandbox_defconfig | 1 + > configs/sandbox_spl_defconfig | 1 + > drivers/misc/Kconfig | 33 ++++++ > drivers/misc/Makefile | 1 + > drivers/misc/p2sb-uclass.c | 216 ++++++++++++++++++++++++++++++++++ > include/dm/uclass-id.h | 1 + > include/p2sb.h | 135 +++++++++++++++++++++ > 7 files changed, 388 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 a8144436eb..5a3c4f151d 100644 > --- a/configs/sandbox_defconfig > +++ b/configs/sandbox_defconfig > @@ -151,6 +151,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 02702fa7a5..478e4bd9d4 100644 > --- a/configs/sandbox_spl_defconfig > +++ b/configs/sandbox_spl_defconfig > @@ -136,6 +136,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 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 <s...@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(s...@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), > +}; > diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h > index 8431ad9c44..c1bab17ad1 100644 > --- a/include/dm/uclass-id.h > +++ b/include/dm/uclass-id.h > @@ -70,6 +70,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 0000000000..60c7f70773 > --- /dev/null > +++ b/include/p2sb.h > @@ -0,0 +1,135 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * Copyright 2019 Google LLC > + * Written by Simon Glass <s...@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
typo: data > + * > + * 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); > + Reviewed-by: Bin Meng <bmeng...@gmail.com> _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot