Skip to content
Permalink
Browse files

uart/ns16550, drivers/pcie: add PCI(e) support

A parallel PCI implementation ("pcie") is added with features for PCIe.
In particular, message-signaled interrupts (MSI) are supported, which
are essential to the use of any non-trivial PCIe device.

The NS16550 UART driver is modified to use pcie.

pcie is a complete replacement for the old PCI support ("pci"). It is
smaller, by an order of magnitude, and cleaner. Both pci and pcie can
(and do) coexist in the same builds, but the intent is to rework any
existing drivers that depend on pci and ultimately remove pci entirely.

This patch is large, but things in mirror are smaller than they appear.
Most of the modified files are configuration-related, and are changed
only slightly to accommodate the modified UART driver.

Deficiencies:

64-bit support is minimal. The code works fine with 64-bit capable
devices, but will not cooperate with MMIO regions (or MSI targets) that
have high bits set. This is not needed on any current boards, and is
unlikely to be needed in the future. Only superficial changes would
be required if we change our minds.

The method specifying PCI endpoints in devicetree is somewhat kludgey.
The "right" way would be to hang PCI devices off a topological tree;
while this would be more aesthetically pleasing, I don't think it's
worth the effort, given our non-standard use of devicetree.

Signed-off-by: Charles E. Youse <charles.youse@intel.com>
  • Loading branch information...
Charles E. Youse authored and andrewboie committed Apr 2, 2019
1 parent 4cf4040 commit e03905354671129de41a415b3b34e35a924a1c5b
@@ -29,6 +29,7 @@
/arch/x86/ @andrewboie @ramakrishnapallala
/arch/x86/core/ @andrewboie
/arch/x86/core/crt0.S @ramakrishnapallala @nashif
/arch/x86/core/pcie.c @gnuless
/soc/x86/ @andrewboie @ramakrishnapallala
/soc/x86/intel_quark/quark_d2000/ @nashif
/soc/x86/intel_quark/quark_se/ @nashif
@@ -119,13 +120,15 @@
/drivers/led_strip/ @mbolivar
/drivers/modem/ @mike-scott
/drivers/pci/ @gnuless
/drivers/pcie/ @gnuless
/drivers/pinmux/stm32/ @rsalveti @idlethread
/drivers/sensor/ @bogdan-davidoaia @MaureenHelm
/drivers/sensor/hts*/ @avisconti
/drivers/sensor/lis*/ @avisconti
/drivers/sensor/lps*/ @avisconti
/drivers/sensor/lsm*/ @avisconti
/drivers/serial/uart_altera_jtag_hal.c @ramakrishnapallala
/drivers/serial/*ns16550* @gnuless
/drivers/net/slip.c @jukkar @tbursztyka
/drivers/spi/ @tbursztyka
/drivers/spi/spi_ll_stm32.* @superna9999
@@ -142,6 +145,7 @@
/dts/arm/nxp/ @MaureenHelm
/dts/bindings/ @galak
/dts/bindings/can/ @alexanderwachter
/dts/bindings/serial/ns16550.yaml @gnuless
/dts/bindings/*/nordic* @anangl
/dts/bindings/*/nxp* @MaureenHelm
/ext/fs/ @nashif @ramakrishnapallala
@@ -181,6 +185,9 @@
/include/drivers/ioapic.h @andrewboie
/include/drivers/loapic.h @andrewboie
/include/drivers/mvic.h @andrewboie
/include/drivers/pcie/ @gnuless
/include/drivers/serial/uart_ns16550.h @gnuless
/include/dt-bindings/pcie/ @gnuless
/include/fs.h @nashif @ramakrishnapallala
/include/fs/ @nashif @ramakrishnapallala
/include/hwinfo.h @alexanderwachter
@@ -32,6 +32,7 @@ zephyr_library_sources(
zephyr_library_sources_if_kconfig( irq_offload.c)
zephyr_library_sources_if_kconfig( x86_mmu.c)
zephyr_library_sources_if_kconfig( reboot_rst_cnt.c)
zephyr_library_sources_if_kconfig( pcie.c)
zephyr_library_sources_ifdef(CONFIG_LAZY_FP_SHARING float.c)
zephyr_library_sources_ifdef(CONFIG_X86_USERSPACE userspace.S)

@@ -0,0 +1,90 @@
/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <kernel.h>
#include <drivers/pcie/pcie.h>
#include <arch/x86/arch.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
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: Apache-2.0

CONFIG_X86=y
CONFIG_PCIE=y
CONFIG_SOC_QUARK_X1000=y
CONFIG_SOC_SERIES_QUARK_X1000=y
CONFIG_BOARD_GALILEO=y
@@ -6,42 +6,32 @@

/* Board level DTS fixup file */

#ifdef CONFIG_SBL_FIXUP

#define DT_UART_NS16550_PORT_0_BASE_ADDR DT_NS16550_81434000_BASE_ADDRESS
#define DT_UART_NS16550_PORT_0_BAUD_RATE DT_NS16550_81434000_CURRENT_SPEED
#define DT_UART_NS16550_PORT_0_NAME DT_NS16550_81434000_LABEL
#define DT_UART_NS16550_PORT_0_IRQ DT_NS16550_81434000_IRQ_0
#define DT_UART_NS16550_PORT_0_IRQ_PRI DT_NS16550_81434000_IRQ_0_PRIORITY
#define DT_UART_NS16550_PORT_0_IRQ_FLAGS DT_NS16550_81434000_IRQ_0_SENSE
#define DT_UART_NS16550_PORT_0_CLK_FREQ DT_NS16550_81434000_CLOCK_FREQUENCY

#define DT_UART_NS16550_PORT_1_BASE_ADDR DT_NS16550_81432000_BASE_ADDRESS
#define DT_UART_NS16550_PORT_1_BAUD_RATE DT_NS16550_81432000_CURRENT_SPEED
#define DT_UART_NS16550_PORT_1_NAME DT_NS16550_81432000_LABEL
#define DT_UART_NS16550_PORT_1_IRQ DT_NS16550_81432000_IRQ_0
#define DT_UART_NS16550_PORT_1_IRQ_PRI DT_NS16550_81432000_IRQ_0_PRIORITY
#define DT_UART_NS16550_PORT_1_IRQ_FLAGS DT_NS16550_81432000_IRQ_0_SENSE
#define DT_UART_NS16550_PORT_1_CLK_FREQ DT_NS16550_81432000_CLOCK_FREQUENCY

#else

#define DT_UART_NS16550_PORT_0_BASE_ADDR DT_NS16550_91524000_BASE_ADDRESS
#define DT_UART_NS16550_PORT_0_BAUD_RATE DT_NS16550_91524000_CURRENT_SPEED
#define DT_UART_NS16550_PORT_0_NAME DT_NS16550_91524000_LABEL
#define DT_UART_NS16550_PORT_0_IRQ DT_NS16550_91524000_IRQ_0
#define DT_UART_NS16550_PORT_0_IRQ_PRI DT_NS16550_91524000_IRQ_0_PRIORITY
#define DT_UART_NS16550_PORT_0_IRQ_FLAGS DT_NS16550_91524000_IRQ_0_SENSE
#define DT_UART_NS16550_PORT_0_CLK_FREQ DT_NS16550_91524000_CLOCK_FREQUENCY

#define DT_UART_NS16550_PORT_1_BASE_ADDR DT_NS16550_91522000_BASE_ADDRESS
#define DT_UART_NS16550_PORT_1_BAUD_RATE DT_NS16550_91522000_CURRENT_SPEED
#define DT_UART_NS16550_PORT_1_NAME DT_NS16550_91522000_LABEL
#define DT_UART_NS16550_PORT_1_IRQ DT_NS16550_91522000_IRQ_0
#define DT_UART_NS16550_PORT_1_IRQ_PRI DT_NS16550_91522000_IRQ_0_PRIORITY
#define DT_UART_NS16550_PORT_1_IRQ_FLAGS DT_NS16550_91522000_IRQ_0_SENSE
#define DT_UART_NS16550_PORT_1_CLK_FREQ DT_NS16550_91522000_CLOCK_FREQUENCY
#define DT_UART_NS16550_PORT_0_BASE_ADDR DT_NS16550_0_BASE_ADDRESS
#define DT_UART_NS16550_PORT_0_SIZE DT_NS16550_0_SIZE
#define DT_UART_NS16550_PORT_0_BAUD_RATE DT_NS16550_0_CURRENT_SPEED
#define DT_UART_NS16550_PORT_0_NAME DT_NS16550_0_LABEL
#define DT_UART_NS16550_PORT_0_IRQ DT_NS16550_0_IRQ_0
#define DT_UART_NS16550_PORT_0_IRQ_PRI DT_NS16550_0_IRQ_0_PRIORITY
#define DT_UART_NS16550_PORT_0_IRQ_FLAGS DT_NS16550_0_IRQ_0_SENSE
#define DT_UART_NS16550_PORT_0_CLK_FREQ DT_NS16550_0_CLOCK_FREQUENCY
#define DT_UART_NS16550_PORT_0_PCIE DT_NS16550_0_PCIE

#ifdef DT_NS16550_0_PCP
#define DT_UART_NS16550_PORT_0_PCP DT_NS16550_0_PCP
#endif

#define DT_UART_NS16550_PORT_1_BASE_ADDR DT_NS16550_1_BASE_ADDRESS
#define DT_UART_NS16550_PORT_1_SIZE DT_NS16550_1_SIZE
#define DT_UART_NS16550_PORT_1_BAUD_RATE DT_NS16550_1_CURRENT_SPEED
#define DT_UART_NS16550_PORT_1_NAME DT_NS16550_1_LABEL
#define DT_UART_NS16550_PORT_1_IRQ DT_NS16550_1_IRQ_0
#define DT_UART_NS16550_PORT_1_IRQ_PRI DT_NS16550_1_IRQ_0_PRIORITY
#define DT_UART_NS16550_PORT_1_IRQ_FLAGS DT_NS16550_1_IRQ_0_SENSE
#define DT_UART_NS16550_PORT_1_CLK_FREQ DT_NS16550_1_CLOCK_FREQUENCY
#define DT_UART_NS16550_PORT_1_PCIE DT_NS16550_1_PCIE

#ifdef DT_NS16550_1_PCP
#define DT_UART_NS16550_PORT_1_PCP DT_NS16550_1_PCP
#endif

/* End of Board Level DTS fixup file */
@@ -13,6 +13,7 @@

#include <apollo_lake.dtsi>
#include <dt-bindings/i2c/i2c.h>
#include <dt-bindings/pcie/pcie.h>

/ {
model = "up_squared";
@@ -29,24 +30,29 @@
};

soc {
uart0: uart@91524000 {
uart0: uart@0 {
compatible = "ns16550";
reg = <0x91524000 0x1000>;

pcie;
reg = <PCIE_BDF(0,0x18,0) PCIE_ID(0x8086,0x5abc)>;

label = "UART_0";
clock-frequency = <1843200>;
interrupts = <4 IRQ_TYPE_LEVEL_LOW 3>;
interrupt-parent = <&intc>;

status = "ok";
current-speed = <115200>;
};

uart1: uart@91522000 {
uart1: uart@1 {
compatible = "ns16550";
reg = <0x91522000 0x1000>;

pcie;
reg = <PCIE_BDF(0,0x18,1) PCIE_ID(0x8086,0x5abe)>;

label = "UART_1";
clock-frequency = <1843200>;
interrupts = <5 IRQ_TYPE_LEVEL_LOW 3>;
interrupts = <3 IRQ_TYPE_LEVEL_LOW 3>;
interrupt-parent = <&intc>;

status = "ok";
@@ -13,4 +13,5 @@ CONFIG_UART_NS16550=y
CONFIG_UART_CONSOLE=y
CONFIG_I2C=y
CONFIG_PCI=y
CONFIG_PCIE=y
CONFIG_PCI_ENUMERATION=y
@@ -21,6 +21,7 @@ add_subdirectory_if_kconfig(led)
add_subdirectory_if_kconfig(led_strip)
add_subdirectory_if_kconfig(modem)
add_subdirectory_if_kconfig(pci)
add_subdirectory_if_kconfig(pcie)
add_subdirectory_if_kconfig(pinmux)
add_subdirectory_if_kconfig(pwm)
add_subdirectory_if_kconfig(rtc)
@@ -29,6 +29,8 @@ source "drivers/entropy/Kconfig"

source "drivers/pci/Kconfig"

source "drivers/pcie/Kconfig"

source "drivers/gpio/Kconfig"

source "drivers/interrupt_controller/Kconfig.shared_irq"
@@ -0,0 +1,3 @@
zephyr_sources(pcie.c)
zephyr_sources_ifdef(CONFIG_PCIE_MSI msi.c)
zephyr_sources_ifdef(CONFIG_PCIE_SHELL shell.c)
@@ -0,0 +1,34 @@
# Kconfig - PCIe/new PCI configuration options

#
# Copyright (c) 2019 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
#

menuconfig PCIE
bool "Enable new PCI/PCIe support"
depends on X86
help
This option enables support for new PCI(e) drivers.

if PCIE

config PCIE_MSI
bool "Enable support for PCI(e) MSI"
default n
help
Use Message-Signaled Interrupts where possible. With this option
enabled, PCI(e) devices which support MSI will be configured (at
runtime) to use them. This is typically required for PCIe devices
to generate interrupts at all.

config PCIE_SHELL
bool "Enable PCIe/new PCI Shell"
default n
depends on SHELL
help
Enable commands for debugging PCI(e) using the built-in shell.

endif # PCIE

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdbool.h>
#include <drivers/pcie/pcie.h>
#include <drivers/pcie/msi.h>

/* functions documented in include/drivers/pcie/msi.h */

u32_t pcie_get_cap(pcie_bdf_t bdf, u32_t cap_id)
{
u32_t reg = 0U;
u32_t data;

data = pcie_conf_read(bdf, PCIE_CONF_CMDSTAT);
if (data & PCIE_CONF_CMDSTAT_CAPS) {
data = pcie_conf_read(bdf, PCIE_CONF_CAPPTR);
reg = PCIE_CONF_CAPPTR_FIRST(data);
}

while (reg) {
data = pcie_conf_read(bdf, reg);

if (PCIE_CONF_CAP_ID(data) == cap_id)
break;

reg = PCIE_CONF_CAP_NEXT(data);
}

return reg;
}

bool pcie_set_msi(pcie_bdf_t bdf, unsigned int irq)
{
bool success = false; /* keepin' the MISRA peeps employed */
u32_t base;
u32_t mcr;
u32_t map;
u32_t mdr;

map = pcie_msi_map(irq);
mdr = pcie_msi_mdr(irq);
base = pcie_get_cap(bdf, PCIE_MSI_CAP_ID);

if (base != 0U) {
mcr = pcie_conf_read(bdf, base + PCIE_MSI_MCR);
pcie_conf_write(bdf, base + PCIE_MSI_MAP0, map);

if (mcr & PCIE_MSI_MCR_64) {
pcie_conf_write(bdf, base + PCIE_MSI_MAP1_64, 0U);
pcie_conf_write(bdf, base + PCIE_MSI_MDR_64, mdr);
} else {
pcie_conf_write(bdf, base + PCIE_MSI_MDR_32, mdr);
}

mcr |= PCIE_MSI_MCR_EN;
mcr &= ~PCIE_MSI_MCR_MME; /* only 1 IRQ please */
pcie_conf_write(bdf, base + PCIE_MSI_MCR, mcr);
pcie_set_cmd(bdf, PCIE_CONF_CMDSTAT_MASTER, true);
success = true;
}

return success;
}

0 comments on commit e039053

Please sign in to comment.
You can’t perform that action at this time.