// 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 */ #define LOG_CATEGORY UCLASS_P2SB #include #include #include #include #include #include #include #include #include #define PCR_COMMON_IOSF_1_0 1 int p2sb_set_hide(struct udevice *dev, bool hide) { struct p2sb_ops *ops = p2sb_get_ops(dev); if (!ops->set_hide) return -ENOSYS; return ops->set_hide(dev, hide); } void *pcr_reg_address(struct udevice *dev, uint offset) { struct p2sb_child_plat *pplat = dev_get_parent_plat(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_plat *pplat = dev_get_parent_plat(dev); return pplat->pid; } int p2sb_set_port_id(struct udevice *dev, int portid) { struct p2sb_child_plat *pplat; if (!CONFIG_IS_ENABLED(OF_PLATDATA)) return -ENOSYS; pplat = dev_get_parent_plat(dev); pplat->pid = portid; return 0; } static int p2sb_child_post_bind(struct udevice *dev) { if (CONFIG_IS_ENABLED(OF_REAL)) { struct p2sb_child_plat *pplat = dev_get_parent_plat(dev); int ret; u32 pid; ret = dev_read_u32(dev, "intel,p2sb-port-id", &pid); if (ret) return ret; pplat->pid = pid; } 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 = sizeof(struct p2sb_uc_priv), .post_bind = p2sb_post_bind, .child_post_bind = p2sb_child_post_bind, .per_child_plat_auto = sizeof(struct p2sb_child_plat), };