From mboxrd@z Thu Jan 1 00:00:00 1970 From: Bin Meng Date: Mon, 4 Nov 2019 16:23:00 +0800 Subject: [U-Boot] [PATCH v3 036/108] pci: Add support for p2sb uclass In-Reply-To: <20191021033913.220758-36-sjg@chromium.org> References: <20191021033913.220758-22-sjg@chromium.org> <20191021033913.220758-36-sjg@chromium.org> Message-ID: List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de Hi Simon, On Mon, Oct 21, 2019 at 11:40 AM Simon Glass 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 > --- > > 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 | 209 ++++++++++++++++++++++++++++++++++ > include/dm/uclass-id.h | 1 + > include/p2sb.h | 127 +++++++++++++++++++++ > 7 files changed, 373 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 20ebc68997b..f8b78b8d7e1 100644 > --- a/configs/sandbox_defconfig > +++ b/configs/sandbox_defconfig > @@ -150,6 +150,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 ba50893b432..1ed2e5d4aa2 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 What does the ? mean? We are not sure what PCR stands for? > + 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..c4e663b5492 > --- /dev/null > +++ b/drivers/misc/p2sb-uclass.c > @@ -0,0 +1,209 @@ > +// 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 > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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_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; > +} > + > +static int p2sb_post_bind(struct udevice *dev) > +{ > + if (spl_phase() >= PHASE_SPL && !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 d19ad1a12e6..6ae9138ca3d 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 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 > + */ > + > +#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 Read data > + * > + * @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 > -- I still are not sure P2SB is generic enough to merit a uclass driver. Regards, Bin