text: /*
   * Copyright (c) 2019 Intel Corporation
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <kernel.h>
  #include <drivers/pcie/pcie.h>
  
  #ifdef CONFIG_PCIE_MSI
  #include <drivers/pcie/msi.h>
  #endif
  
  /*
   * The Configuration Mechanism (previously, Configuration Mechanism #1)
   * uses two 32-bit ports in the I/O space, here called CAP and CDP.
   *
   * N.B.: this code relies on the fact that the PCIE_BDF() format (as
   * defined in dt-bindings/pcie/pcie.h) and the CAP agree on the bus/dev/func
   * bitfield positions and sizes.
   */
  
  #define PCIE_X86_CAP	0xCF8U	/* Configuration Address Port */
  #define PCIE_X86_CAP_BDF_MASK	0x00FFFF00U  /* b/d/f bits */
  #define PCIE_X86_CAP_EN		0x80000000U  /* enable bit */
  #define PCIE_X86_CAP_WORD_MASK	0x3FU  /*  6-bit word index .. */
  #define PCIE_X86_CAP_WORD_SHIFT	2U  /* .. is in CAP[7:2] */
  
  #define PCIE_X86_CDP	0xCFCU	/* Configuration Data Port */
  
  /*
   * Helper function for exported configuration functions. Configuration access
   * ain't atomic, so spinlock to keep drivers from clobbering each other.
   */
  static void pcie_conf(pcie_bdf_t bdf, unsigned int reg, bool write, u32_t *data)
  {
  	static struct k_spinlock lock;
  	k_spinlock_key_t k;
  
  	bdf &= PCIE_X86_CAP_BDF_MASK;
  	bdf |= PCIE_X86_CAP_EN;
  	bdf |= (reg & PCIE_X86_CAP_WORD_MASK) << PCIE_X86_CAP_WORD_SHIFT;
  
  	k = k_spin_lock(&lock);
  	sys_out32(bdf, PCIE_X86_CAP);
  
  	if (write) {
  		sys_out32(*data, PCIE_X86_CDP);
  	} else {
  		*data = sys_in32(PCIE_X86_CDP);
  	}
  
  	sys_out32(0U, PCIE_X86_CAP);
  	k_spin_unlock(&lock, k);
  }
  
  /* these functions are explained in include/drivers/pcie/pcie.h */
  
  u32_t pcie_conf_read(pcie_bdf_t bdf, unsigned int reg)
  {
  	u32_t data;
  
  	pcie_conf(bdf, reg, false, &data);
  	return data;
  }
  
  void pcie_conf_write(pcie_bdf_t bdf, unsigned int reg, u32_t data)
  {
  	pcie_conf(bdf, reg, true, &data);
  }
  
  #ifdef CONFIG_PCIE_MSI
  
  /* these functions are explained in include/drivers/pcie/msi.h */
  
  u32_t pcie_msi_map(unsigned int irq)
  {
  	ARG_UNUSED(irq);
  	return 0xFEE00000U;  /* standard delivery to BSP local APIC */
  }
  
  u16_t pcie_msi_mdr(unsigned int irq)
  {
  	unsigned char vector = _irq_to_interrupt_vector[irq];
  
  	return 0x4000U | vector;  /* edge triggered */
  }
  
  #endif
  