Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * 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