123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- // 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 <log.h>
- #include <malloc.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
- 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_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),
- };
|