[DM/Feature] Basic PCI/PCIe (Peripheral Component Interconnect Express) bus

PCI/PCIe have better performance and more devices support, such as
NVMe, GPU, Powerful NIC (Like RDMA). PCI/PCIe can access control by
IOMMU that the virtualiztion and userspace driver will more safety.
PCI/PCIe device could hot plugging, no design modifications SoC required,
PCI/PCIe on Embedded SoC is popular now.
We make a simple framework to support them.

Feature Lists:
1.PCI INTx: the INT[A-D] pin IRQ for legacy PCI, work with platform PIC.
2.MSI/MSI-X: the message write IRQ for PCIe, work with platform's PIC.
3.PME: we only support the D0, D1, D2, D3HOT, D3COLD init by framework.
4.Endpoint: a simple EP framework for PCI FPGA or NTB function.
5.OFW: we only support work on OFW SoC, ACPI support in the future maybe.

Host controller:
1. Common PCI host controller on ECAM.
2. Generic PCI host controller on ECAM.

Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
GuEe-GUI
2024-09-06 17:45:03 -04:00
committed by Meco Man
parent 802a6ed2ca
commit 2168ed8e7d
27 changed files with 7430 additions and 0 deletions
+1
View File
@@ -23,6 +23,7 @@ rsource "hwcrypto/Kconfig"
rsource "wlan/Kconfig"
rsource "virtio/Kconfig"
rsource "ofw/Kconfig"
rsource "pci/Kconfig"
rsource "pic/Kconfig"
rsource "pin/Kconfig"
rsource "pinctrl/Kconfig"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,179 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-08-25 GuEe-GUI first version
*/
#ifndef __PCI_ENDPOINT_H__
#define __PCI_ENDPOINT_H__
#include <drivers/pci.h>
enum rt_pci_ep_pin
{
RT_PCI_EP_PIN_UNKNOWN,
RT_PCI_EP_PIN_INTA,
RT_PCI_EP_PIN_INTB,
RT_PCI_EP_PIN_INTC,
RT_PCI_EP_PIN_INTD,
};
enum rt_pci_ep_irq
{
RT_PCI_EP_IRQ_UNKNOWN,
RT_PCI_EP_IRQ_LEGACY,
RT_PCI_EP_IRQ_MSI,
RT_PCI_EP_IRQ_MSIX,
};
struct rt_pci_ep_header
{
rt_uint16_t vendor;
rt_uint16_t device;
rt_uint8_t revision;
rt_uint8_t progif;
rt_uint8_t subclass;
rt_uint8_t class_code;
rt_uint8_t cache_line_size;
rt_uint16_t subsystem_vendor;
rt_uint16_t subsystem_device;
enum rt_pci_ep_pin intx;
};
struct rt_pci_ep_bar
{
/* To PCI Bus */
struct rt_pci_bus_resource bus;
/* To CPU */
rt_ubase_t cpu_addr;
};
/*
* Type of MSI-X table, For more format detail,
* please read `components/drivers/include/drivers/pci_msi.h`
*/
struct rt_pci_ep_msix_tbl
{
union
{
rt_uint64_t msg_addr;
struct
{
rt_uint32_t msg_addr_upper;
rt_uint32_t msg_addr_lower;
};
};
rt_uint32_t msg_data;
rt_uint32_t vector_ctrl;
};
struct rt_pci_ep_ops;
struct rt_pci_ep
{
rt_list_t list;
const char *name;
struct rt_ref ref;
const struct rt_device *rc_dev;
const struct rt_pci_ep_ops *ops;
rt_uint8_t max_functions;
RT_BITMAP_DECLARE(functions_map, 8);
rt_list_t epf_nodes;
struct rt_mutex lock;
void *priv;
};
struct rt_pci_epf
{
rt_list_t list;
const char *name;
struct rt_pci_ep_header *header;
struct rt_pci_ep_bar bar[PCI_STD_NUM_BARS];
rt_uint8_t msi_interrupts;
rt_uint16_t msix_interrupts;
rt_uint8_t func_no;
struct rt_pci_ep *ep;
};
struct rt_pci_ep_ops
{
rt_err_t (*write_header)(struct rt_pci_ep *ep, rt_uint8_t func_no,
struct rt_pci_ep_header *hdr);
rt_err_t (*set_bar)(struct rt_pci_ep *ep, rt_uint8_t func_no,
struct rt_pci_ep_bar *bar, int bar_idx);
rt_err_t (*clear_bar)(struct rt_pci_ep *ep, rt_uint8_t func_no,
struct rt_pci_ep_bar *bar, int bar_idx);
rt_err_t (*map_addr)(struct rt_pci_ep *ep, rt_uint8_t func_no,
rt_ubase_t addr, rt_uint64_t pci_addr, rt_size_t size);
rt_err_t (*unmap_addr)(struct rt_pci_ep *ep, rt_uint8_t func_no, rt_ubase_t addr);
rt_err_t (*set_msi)(struct rt_pci_ep *ep, rt_uint8_t func_no,
unsigned irq_nr);
rt_err_t (*get_msi)(struct rt_pci_ep *ep, rt_uint8_t func_no,
unsigned *out_irq_nr);
rt_err_t (*set_msix)(struct rt_pci_ep *ep, rt_uint8_t func_no,
unsigned irq_nr, int bar_idx, rt_off_t offset);
rt_err_t (*get_msix)(struct rt_pci_ep *ep, rt_uint8_t func_no,
unsigned *out_irq_nr);
rt_err_t (*raise_irq)(struct rt_pci_ep *ep, rt_uint8_t func_no,
enum rt_pci_ep_irq type, unsigned irq);
rt_err_t (*start)(struct rt_pci_ep *ep);
rt_err_t (*stop)(struct rt_pci_ep *ep);
};
rt_err_t rt_pci_ep_write_header(struct rt_pci_ep *ep, rt_uint8_t func_no,
struct rt_pci_ep_header *hdr);
rt_err_t rt_pci_ep_set_bar(struct rt_pci_ep *ep, rt_uint8_t func_no,
struct rt_pci_ep_bar *bar, int bar_idx);
rt_err_t rt_pci_ep_clear_bar(struct rt_pci_ep *ep, rt_uint8_t func_no,
struct rt_pci_ep_bar *bar, int bar_idx);
rt_err_t rt_pci_ep_map_addr(struct rt_pci_ep *ep, rt_uint8_t func_no,
rt_ubase_t addr, rt_uint64_t pci_addr, rt_size_t size);
rt_err_t rt_pci_ep_unmap_addr(struct rt_pci_ep *ep, rt_uint8_t func_no,
rt_ubase_t addr);
rt_err_t rt_pci_ep_set_msi(struct rt_pci_ep *ep, rt_uint8_t func_no,
unsigned irq_nr);
rt_err_t rt_pci_ep_get_msi(struct rt_pci_ep *ep, rt_uint8_t func_no,
unsigned *out_irq_nr);
rt_err_t rt_pci_ep_set_msix(struct rt_pci_ep *ep, rt_uint8_t func_no,
unsigned irq_nr, int bar_idx, rt_off_t offset);
rt_err_t rt_pci_ep_get_msix(struct rt_pci_ep *ep, rt_uint8_t func_no,
unsigned *out_irq_nr);
rt_err_t rt_pci_ep_raise_irq(struct rt_pci_ep *ep, rt_uint8_t func_no,
enum rt_pci_ep_irq type, unsigned irq);
rt_err_t rt_pci_ep_start(struct rt_pci_ep *ep);
rt_err_t rt_pci_ep_stop(struct rt_pci_ep *ep);
rt_err_t rt_pci_ep_register(struct rt_pci_ep *ep);
rt_err_t rt_pci_ep_unregister(struct rt_pci_ep *ep);
rt_err_t rt_pci_ep_add_epf(struct rt_pci_ep *ep, struct rt_pci_epf *epf);
rt_err_t rt_pci_ep_remove_epf(struct rt_pci_ep *ep, struct rt_pci_epf *epf);
struct rt_pci_ep *rt_pci_ep_get(const char *name);
void rt_pci_ep_put(struct rt_pci_ep *ep);
#endif /* __PCI_ENDPOINT_H__ */
@@ -0,0 +1,189 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-08-25 GuEe-GUI first version
*/
#ifndef __PCI_MSI_H__
#define __PCI_MSI_H__
#include <drivers/pci.h>
/*
* MSI Format:
* T0: 32-bit Address
* T1: 64-bit Address
* T2: 32-bit Address with Per-Vector Masking
* T3: 64-bit Address with Per-Vector Masking
*
* 31 16 15 8 7 0
* +---------------------------+-----------------+---------------+
* | Message Control | Next Capability | Capability ID | DW0
* | | Pointer | (05h) |
* +---------------------------+-----------------+---------------+
* | Message Address [31:0] | DW1
* +-------------------------------------------------------------+
* | Message Address [63:32] | DW2 (T1: only 64-bit)
* +---------------------------+---------------------------------+
* | Reserved | Message Data | DW3
* +---------------------------+---------------------------------+
* | Mask Bits | DW4 (T2/T3: only with Per-Vector Masking)
* +-------------------------------------------------------------+
* | Pending Bits | DW5 (T2/T3: only with Per-Vector Masking)
* +-------------------------------------------------------------+
*
* MSI Message Control:
*
* 15 9 8 7 6 4 3 1 0
* +----------------------+---+---+---------------+----------+---+
* | Reserved | | | | | |
* +----------------------+---+---+---------------+----------+---+
* ^ ^ ^ ^ ^
* | | | | |
* | | | | +---- MSI Enable (RW)
* | | | +----------- Multiple Message Capable (RO, log2n, [n <= 5])
* | | +------------------------- Multiple Message Enable (RW, log2n, [n <= 5])
* | +----------------------------------- 64-bit Address Capable
* +--------------------------------------- Per-Vector Masking Capable
*/
struct rt_pci_msi_conf
{
rt_uint32_t mask;
rt_uint8_t mask_pos;
int default_irq;
struct
{
rt_uint8_t is_masking:1;
rt_uint8_t is_64bit:1;
rt_uint8_t multi_msg_max:3; /* log2 num of messages allocated */
rt_uint8_t multi_msg_use:3; /* log2 num of messages supported */
} cap;
};
/*
* MSI-X Format:
*
* 31 16 15 8 7 0
* +---------------------------+-----------------+---------------+
* | Message Control | Next Capability | Capability ID | DW0
* | | Pointer | (11h) |
* +---------------------------+-----------------+---+-----------+
* | MSI-X Table Offset | Table BIR | DW1 (BIR: BAR Index Register)
* +-------------------------------------------------+-----------+ |
* | Pending Bit Array (PBA) Offset | PBA BIR | DW2 --------+ |
* +-------------------------------------------------+-----------+ | |
* | |
* MSI-X Message Control: | |
* | |
* 15 14 13 11 10 0 | |
* +---+---+----------+------------------------------------------+ | |
* | | | Reserved | Table Size in N-1 (RO) | | |
* +---+---+----------+------------------------------------------+ | |
* ^ ^ | |
* | | | |
* | +---- Function Mask (RW) | |
* +-------- MSI-X Enable (RW) | |
* | |
* MSI-X Table (BAR[Table BIR] + MSI-X Table Offset): | |
* | |
* DW3 DW2 DW1 DW0 | |
* +----------------+--------------+---------------+---------------+ <---------|-+
* | Vector Control | Message Data | Upper Address | Lower Address | Entry 0 |
* +----------------+--------------+---------------+---------------+ |
* | Vector Control | Message Data | Upper Address | Lower Address | Entry 1 |
* +----------------+--------------+---------------+---------------+ |
* | ...... | ...... | ...... | ...... | |
* +----------------+--------------+---------------+---------------+ |
* | Vector Control | Message Data | Upper Address | Lower Address | Entry N-1 |
* +----------------+--------------+---------------+---------------+ |
* ^ |
* | |
* +---- Bit 0 is vector Mask Bit (R/W) |
* |
* MSI-X Pending Bit Array (BAR[PBA BIR] + Pending Bit Array Offset): |
* |
* DW1 DW0 |
* +-------------------------------+ <-----------------------------------------+
* | Pending Bits 0 - 63 | QW 0
* +-------------------------------+
* | Pending Bits 64 - 127 | QW 1
* +-------------------------------+
* | ...... |
* +-------------------------------+
* | Pending Bits | QW (N-1)/64
* +-------------------------------+
*/
struct rt_pci_msix_conf
{
int index;
rt_uint32_t msg_ctrl;
void *table_base;
};
struct rt_pci_msi_msg
{
rt_uint32_t address_lo;
rt_uint32_t address_hi;
rt_uint32_t data;
};
struct rt_pci_msi_desc
{
rt_list_t list;
int irq;
rt_size_t vector_used;
rt_size_t vector_count;
union
{
/* For MSI-X */
rt_bitmap_t *affinity;
/* For MSI */
rt_bitmap_t **affinities;
};
struct rt_pci_device *pdev;
struct rt_pci_msi_msg msg;
void *write_msi_msg_data;
void (*write_msi_msg)(struct rt_pci_msi_desc *, void *);
rt_bool_t is_msix;
union
{
struct rt_pci_msi_conf msi;
struct rt_pci_msix_conf msix;
};
void *priv;
};
#define rt_pci_msi_first_desc(pdev) \
(rt_list_isempty(&(pdev)->msi_desc_nodes) ? RT_NULL : \
rt_list_first_entry(&(pdev)->msi_desc_nodes, struct rt_pci_msi_desc, list))
#define rt_pci_msi_for_each_desc(pdev, desc) \
rt_list_for_each_entry(desc, &(pdev)->msi_desc_nodes, list)
#define rt_pci_msix_table_size(flags) ((flags & PCIM_MSIXCTRL_TABLE_SIZE) + 1)
rt_err_t rt_pci_msi_setup_irqs(struct rt_pci_device *pdev, int nvec, int type);
void rt_pci_msi_shutdown(struct rt_pci_device *pdev);
void rt_pci_msix_shutdown(struct rt_pci_device *pdev);
void rt_pci_msi_free_irqs(struct rt_pci_device *pdev);
void rt_pci_msi_write_msg(struct rt_pci_msi_desc *desc, struct rt_pci_msi_msg *msg);
void rt_pci_msi_mask_irq(struct rt_pic_irq *pirq);
void rt_pci_msi_unmask_irq(struct rt_pic_irq *pirq);
#endif /* __PCI_MSI_H__ */
+49
View File
@@ -0,0 +1,49 @@
menuconfig RT_USING_PCI
bool "Using Peripheral Component Interconnect Express (PCIe/PCI)"
depends on RT_USING_DM
depends on RT_USING_PIC
select RT_USING_ADT
select RT_USING_ADT_BITMAP
default n
config RT_PCI_MSI
bool "PCI MSI/MSI-X"
depends on RT_USING_PCI
default y
config RT_PCI_ENDPOINT
bool "PCI Endpoint"
depends on RT_USING_PCI
select RT_USING_ADT_REF
default n
config RT_PCI_SYS_64BIT
bool "PCI System 64bit"
depends on RT_USING_PCI
depends on ARCH_CPU_64BIT
default y
config RT_PCI_CACHE_LINE_SIZE
int "PCI Cache line size"
depends on RT_USING_PCI
default 8 if ARCH_CPU_64BIT
default 4
config RT_PCI_LOCKLESS
bool "PCI Lock less in options"
depends on RT_USING_PCI
default n
if RT_USING_PCI
comment "PCI Device Drivers"
config RT_PCI_ECAM
bool "PCIe ECAM"
depends on RT_USING_PCI
default y
help
PCIe Express Enhanced Configuration Access Mechanism
rsource "host/Kconfig"
endif
+28
View File
@@ -0,0 +1,28 @@
from building import *
objs = []
if not GetDepend(['RT_USING_PCI']):
Return('objs')
cwd = GetCurrentDir()
list = os.listdir(cwd)
CPPPATH = [cwd + '/../include']
src = ['access.c', 'host-bridge.c', 'irq.c', 'pci.c', 'pme.c', 'probe.c']
if GetDepend(['RT_USING_OFW']):
src += ['ofw.c']
if GetDepend(['RT_PCI_ECAM']):
src += ['ecam.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
objs = objs + group
Return('objs')
+159
View File
@@ -0,0 +1,159 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-24 GuEe-GUI first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <drivers/pci.h>
struct rt_spinlock rt_pci_lock = { 0 };
#ifdef RT_PCI_LOCKLESS
#define pci_lock_config(l) do { (void)(l); } while (0)
#define pci_unlock_config(l) do { (void)(l); } while (0)
#else
#define pci_lock_config(l) l = rt_spin_lock_irqsave(&rt_pci_lock)
#define pci_unlock_config(l) rt_spin_unlock_irqrestore(&rt_pci_lock, l)
#endif
#define PCI_OPS_READ(name, type) \
rt_err_t rt_pci_bus_read_config_##name(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, type *value) \
{ \
rt_err_t err; \
rt_ubase_t level; \
rt_uint32_t data = 0; \
pci_lock_config(level); \
err = bus->ops->read(bus, devfn, reg, sizeof(type), &data); \
*value = err ? (type)(~0) : (type)data; \
pci_unlock_config(level); \
return err; \
}
#define PCI_OPS_WRITE(name, type) \
rt_err_t rt_pci_bus_write_config_##name(struct rt_pci_bus *bus, rt_uint32_t devfn, int reg, type value) \
{ \
rt_err_t err; \
rt_ubase_t level; \
pci_lock_config(level); \
err = bus->ops->write(bus, devfn, reg, sizeof(type), value); \
pci_unlock_config(level); \
return err; \
}
#define PCI_OPS(name, type) \
PCI_OPS_READ(name, type) \
PCI_OPS_WRITE(name, type)
PCI_OPS(u8, rt_uint8_t)
PCI_OPS(u16, rt_uint16_t)
PCI_OPS(u32, rt_uint32_t)
#undef PCI_OP_WRITE
#undef PCI_OP_READ
#undef PCI_OPS
rt_err_t rt_pci_bus_read_config_uxx(struct rt_pci_bus *bus,
rt_uint32_t devfn, int reg, int width, rt_uint32_t *value)
{
void *base;
if ((base = bus->ops->map(bus, devfn, reg)))
{
if (width == 1)
{
*value = HWREG8(base);
}
else if (width == 2)
{
*value = HWREG16(base);
}
else
{
*value = HWREG32(base);
}
return RT_EOK;
}
return -RT_ERROR;
}
rt_err_t rt_pci_bus_write_config_uxx(struct rt_pci_bus *bus,
rt_uint32_t devfn, int reg, int width, rt_uint32_t value)
{
void *base;
if ((base = bus->ops->map(bus, devfn, reg)))
{
if (width == 1)
{
HWREG8(base) = value;
}
else if (width == 2)
{
HWREG16(base) = value;
}
else
{
HWREG32(base) = value;
}
return RT_EOK;
}
return -RT_ERROR;
}
rt_err_t rt_pci_bus_read_config_generic_u32(struct rt_pci_bus *bus,
rt_uint32_t devfn, int reg, int width, rt_uint32_t *value)
{
void *base;
if ((base = bus->ops->map(bus, devfn, reg)))
{
*value = HWREG32(base);
if (width <= 2)
{
*value = (*value >> (8 * (reg & 3))) & ((1 << (width * 8)) - 1);
}
return RT_EOK;
}
return -RT_ERROR;
}
rt_err_t rt_pci_bus_write_config_generic_u32(struct rt_pci_bus *bus,
rt_uint32_t devfn, int reg, int width, rt_uint32_t value)
{
void *base;
if ((base = bus->ops->map(bus, devfn, reg & ~0x3)))
{
if (width == 4)
{
HWREG32(base) = value;
}
else
{
rt_uint32_t mask, tmp;
mask = ~(((1 << (width * 8)) - 1) << ((reg & 0x3) * 8));
tmp = HWREG32(base) & mask;
tmp |= value << ((reg & 0x3) * 8);
HWREG32(base) = tmp;
}
return RT_EOK;
}
return -RT_ERROR;
}
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-24 GuEe-GUI first version
*/
#include <rthw.h>
#include <rtthread.h>
#define DBG_TAG "pci.ecam"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include "ecam.h"
struct pci_ecam_config_window *pci_ecam_create(struct rt_pci_host_bridge *host_bridge,
const struct pci_ecam_ops *ops)
{
struct pci_ecam_config_window *conf_win = rt_calloc(1, sizeof(*conf_win));
if (!conf_win)
{
return RT_NULL;
}
conf_win->bus_range = host_bridge->bus_range;
conf_win->bus_shift = ops->bus_shift;
conf_win->ops = ops;
host_bridge->ops = (const struct rt_pci_ops *)&ops->pci_ops;
return conf_win;
}
void *pci_ecam_map(struct rt_pci_bus *bus, rt_uint32_t devfn, int where)
{
struct pci_ecam_config_window *conf_win = bus->sysdata;
const struct pci_ecam_ops *eops = conf_win->ops;
void *win = conf_win->win, *map;
rt_uint32_t busn = bus->number, bus_shift = eops->bus_shift, devfn_shift = bus_shift - 8;
busn -= conf_win->bus_range[0];
if (bus_shift)
{
rt_uint32_t bus_offset = (busn & PCIE_ECAM_BUS_MASK) << bus_shift;
rt_uint32_t devfn_offset = (devfn & PCIE_ECAM_DEVFN_MASK) << devfn_shift;
where &= PCIE_ECAM_REG_MASK;
map = win + (bus_offset | devfn_offset | where);
}
else
{
map = win + PCIE_ECAM_OFFSET(busn, devfn, where);
}
return map;
}
const struct pci_ecam_ops pci_generic_ecam_ops =
{
.pci_ops =
{
.map = pci_ecam_map,
.read = rt_pci_bus_read_config_uxx,
.write = rt_pci_bus_write_config_uxx,
}
};
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-24 GuEe-GUI first version
*/
#ifndef __RT_PCI_ECAM_H__
#define __RT_PCI_ECAM_H__
#include <drivers/pci.h>
#include <drivers/ofw.h>
#include <drivers/ofw_io.h>
#include <drivers/platform.h>
/*
* Memory address shift values for the byte-level address that
* can be used when accessing the PCI Express Configuration Space.
*/
/*
* Enhanced Configuration Access Mechanism (ECAM)
*
* See PCI Express Base Specification, Revision 5.0, Version 1.0,
* Section 7.2.2, Table 7-1, p. 677.
*/
#define PCIE_ECAM_BUS_SHIFT 20 /* Bus number */
#define PCIE_ECAM_DEVFN_SHIFT 12 /* Device and Function number */
#define PCIE_ECAM_BUS_MASK 0xff
#define PCIE_ECAM_DEVFN_MASK 0xff
#define PCIE_ECAM_REG_MASK 0xfff /* Limit offset to a maximum of 4K */
#define PCIE_ECAM_BUS(x) (((x) & PCIE_ECAM_BUS_MASK) << PCIE_ECAM_BUS_SHIFT)
#define PCIE_ECAM_DEVFN(x) (((x) & PCIE_ECAM_DEVFN_MASK) << PCIE_ECAM_DEVFN_SHIFT)
#define PCIE_ECAM_REG(x) ((x) & PCIE_ECAM_REG_MASK)
#define PCIE_ECAM_OFFSET(bus, devfn, where) \
(PCIE_ECAM_BUS(bus) | PCIE_ECAM_DEVFN(devfn) | PCIE_ECAM_REG(where))
struct pci_ecam_ops
{
rt_uint32_t bus_shift;
const struct rt_pci_ops pci_ops;
};
struct pci_ecam_config_window
{
rt_uint32_t *bus_range;
rt_uint32_t bus_shift;
void *win;
void *priv;
const struct pci_ecam_ops *ops;
};
/* Default ECAM ops */
extern const struct pci_ecam_ops pci_generic_ecam_ops;
void *pci_ecam_map(struct rt_pci_bus *bus, rt_uint32_t devfn, int where);
struct pci_ecam_config_window *pci_ecam_create(struct rt_pci_host_bridge *host_bridge,
const struct pci_ecam_ops *ops);
rt_err_t pci_host_common_probe(struct rt_platform_device *pdev);
rt_err_t pci_host_common_remove(struct rt_platform_device *pdev);
#endif /* __RT_PCI_ECAM_H__ */
@@ -0,0 +1,15 @@
from building import *
group = []
if not GetDepend(['RT_PCI_ENDPOINT']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../../include']
src = ['endpoint.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
File diff suppressed because it is too large Load Diff
+129
View File
@@ -0,0 +1,129 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-24 GuEe-GUI first version
*/
#include <rtthread.h>
#include <drivers/pm.h>
#include <drivers/pci.h>
#ifdef RT_USING_PM
struct host_bridge_pm_status
{
rt_uint8_t mode;
rt_bool_t enable;
};
static const enum rt_pci_power system_pci_pm_mode[] =
{
[PM_SLEEP_MODE_NONE] = RT_PCI_D0,
[PM_SLEEP_MODE_IDLE] = RT_PCI_D3HOT,
[PM_SLEEP_MODE_LIGHT] = RT_PCI_D1,
[PM_SLEEP_MODE_DEEP] = RT_PCI_D1,
[PM_SLEEP_MODE_STANDBY] = RT_PCI_D2,
[PM_SLEEP_MODE_SHUTDOWN] = RT_PCI_D3COLD,
};
static rt_bool_t pci_device_pm_ops(struct rt_pci_device *pdev, void *data)
{
struct host_bridge_pm_status *status = data;
rt_pci_enable_wake(pdev, system_pci_pm_mode[status->mode], status->enable);
/* To find all devices, always return false */
return RT_FALSE;
}
static rt_err_t host_bridge_pm_suspend(const struct rt_device *device, rt_uint8_t mode)
{
struct host_bridge_pm_status status;
struct rt_pci_device *pdev = rt_container_of(device, struct rt_pci_device, parent);
status.mode = mode;
status.enable = RT_FALSE;
rt_pci_enum_device(pdev->bus, pci_device_pm_ops, &status);
return RT_EOK;
}
static void host_bridge_pm_resume(const struct rt_device *device, rt_uint8_t mode)
{
struct host_bridge_pm_status status;
struct rt_pci_device *pdev = rt_container_of(device, struct rt_pci_device, parent);
status.mode = mode;
status.enable = RT_TRUE;
rt_pci_enum_device(pdev->bus, pci_device_pm_ops, &status);
}
static const struct rt_device_pm_ops host_bridge_pm_ops =
{
.suspend = host_bridge_pm_suspend,
.resume = host_bridge_pm_resume,
};
#endif /* RT_USING_PM */
static void host_bridge_free(struct rt_pci_device *pdev)
{
#ifdef RT_USING_PM
rt_pm_device_unregister(&pdev->parent);
#endif
}
static rt_err_t host_bridge_probe(struct rt_pci_device *pdev)
{
rt_err_t err = RT_EOK;
rt_pci_set_master(pdev);
#ifdef RT_USING_PM
rt_pm_device_register(&pdev->parent, &host_bridge_pm_ops);
#endif
return err;
}
static rt_err_t host_bridge_remove(struct rt_pci_device *pdev)
{
host_bridge_free(pdev);
rt_pci_clear_master(pdev);
return RT_EOK;
}
static rt_err_t host_bridge_shutdown(struct rt_pci_device *pdev)
{
host_bridge_free(pdev);
return RT_EOK;
}
static const struct rt_pci_device_id host_bridge_pci_ids[] =
{
/* PCI host bridges */
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0008) },
/* Any PCI-Express port */
{ RT_PCI_DEVICE_CLASS(PCIS_BRIDGE_PCI_NORMAL, ~0) },
/* PCI-to-PCI bridge */
{ RT_PCI_DEVICE_CLASS(PCIS_BRIDGE_PCI_SUBTRACTIVE, ~0) },
/* Any Root Complex Event Collector */
{ RT_PCI_DEVICE_CLASS(((PCIS_SYSTEM_RCEC << 8) | 0x00), ~0) },
{ /* sentinel */ }
};
static struct rt_pci_driver host_bridge_driver =
{
.name = "host-bridge",
.ids = host_bridge_pci_ids,
.probe = host_bridge_probe,
.remove = host_bridge_remove,
.shutdown = host_bridge_shutdown,
};
RT_PCI_DRIVER_EXPORT(host_bridge_driver);
+10
View File
@@ -0,0 +1,10 @@
config RT_PCI_HOST_COMMON
bool "Common PCI host controller"
depends on RT_PCI_ECAM
default y
config RT_PCI_HOST_GENERIC
bool "Generic PCI host controller"
depends on RT_PCI_ECAM
select RT_PCI_HOST_COMMON
default y
+25
View File
@@ -0,0 +1,25 @@
from building import *
objs = []
cwd = GetCurrentDir()
list = os.listdir(cwd)
CPPPATH = [cwd + '/../../include']
src = []
if GetDepend(['RT_PCI_HOST_COMMON']):
src += ['pci-host-common.c']
if GetDepend(['RT_PCI_HOST_GENERIC']):
src += ['pci-host-generic.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
for d in list:
path = os.path.join(cwd, d)
if os.path.isfile(os.path.join(path, 'SConscript')):
objs = objs + SConscript(os.path.join(d, 'SConscript'))
objs = objs + group
Return('objs')
@@ -0,0 +1,85 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-24 GuEe-GUI first version
*/
#include <rtthread.h>
#include "../ecam.h"
rt_err_t pci_host_common_probe(struct rt_platform_device *pdev)
{
void *base;
rt_err_t err;
struct rt_device *dev = &pdev->parent;
struct pci_ecam_config_window *conf_win;
struct rt_pci_host_bridge *host_bridge = rt_pci_host_bridge_alloc(0);
if (!host_bridge)
{
return -RT_ENOMEM;
}
if (!(base = rt_dm_dev_iomap(dev, 0)))
{
err = -RT_EIO;
goto _fail;
}
host_bridge->parent.ofw_node = dev->ofw_node;
if ((err = rt_pci_host_bridge_init(host_bridge)))
{
goto _fail;
}
host_bridge->sysdata = conf_win = pci_ecam_create(host_bridge,
(const struct pci_ecam_ops *)pdev->id->data);
if (!conf_win)
{
err = -RT_ENOMEM;
goto _fail;
}
conf_win->win = base;
conf_win->priv = host_bridge;
if ((err = rt_pci_host_bridge_probe(host_bridge)))
{
goto _fail;
}
dev->user_data = host_bridge;
return RT_EOK;
_fail:
if (base)
{
rt_iounmap(base);
}
rt_pci_host_bridge_free(host_bridge);
return err;
}
rt_err_t pci_host_common_remove(struct rt_platform_device *pdev)
{
struct pci_ecam_config_window *conf_win;
struct rt_pci_host_bridge *host_bridge = pdev->parent.user_data;
rt_pci_host_bridge_remove(host_bridge);
conf_win = host_bridge->sysdata;
rt_iounmap(conf_win->win);
rt_pci_host_bridge_free(host_bridge);
return RT_EOK;
}
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-24 GuEe-GUI first version
*/
#include <rtthread.h>
#include "../ecam.h"
static const struct pci_ecam_ops gen_pci_cfg_cam_bus_ops =
{
.bus_shift = 16,
.pci_ops =
{
.map = pci_ecam_map,
.read = rt_pci_bus_read_config_uxx,
.write = rt_pci_bus_write_config_uxx,
}
};
static void *pci_dw_ecam_map_bus(struct rt_pci_bus *bus, rt_uint32_t devfn, int where)
{
struct pci_ecam_config_window *conf_win = bus->sysdata;
if (bus->number == conf_win->bus_range[0] && RT_PCI_SLOT(devfn) > 0)
{
return RT_NULL;
}
return pci_ecam_map(bus, devfn, where);
}
static const struct pci_ecam_ops pci_dw_ecam_bus_ops =
{
.pci_ops =
{
.map = pci_dw_ecam_map_bus,
.read = rt_pci_bus_read_config_uxx,
.write = rt_pci_bus_write_config_uxx,
}
};
static const struct rt_ofw_node_id gen_pci_ofw_ids[] =
{
{ .compatible = "pci-host-cam-generic", .data = &gen_pci_cfg_cam_bus_ops },
{ .compatible = "pci-host-ecam-generic", .data = &pci_generic_ecam_ops },
{ .compatible = "marvell,armada8k-pcie-ecam", .data = &pci_dw_ecam_bus_ops },
{ .compatible = "socionext,synquacer-pcie-ecam", .data = &pci_dw_ecam_bus_ops },
{ .compatible = "snps,dw-pcie-ecam", .data = &pci_dw_ecam_bus_ops },
{ /* sentinel */ }
};
static struct rt_platform_driver gen_pci_driver =
{
.name = "pci-host-generic",
.ids = gen_pci_ofw_ids,
.probe = pci_host_common_probe,
.remove = pci_host_common_remove,
};
RT_PLATFORM_DRIVER_EXPORT(gen_pci_driver);
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-11-07 GuEe-GUI first version
*/
#include <rtthread.h>
#define DBG_TAG "pci.irq"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <drivers/pci.h>
void rt_pci_assign_irq(struct rt_pci_device *pdev)
{
int irq = 0;
rt_uint8_t pin, slot = -1;
struct rt_pci_host_bridge *host_bridge = rt_pci_find_host_bridge(pdev->bus);
if (!host_bridge->irq_map)
{
LOG_D("PCI-Device<%s> runtime IRQ mapping not provided by platform",
rt_dm_dev_get_name(&pdev->parent));
return;
}
/* Must try the swizzle when interrupt line passes through a P2P bridge */
rt_pci_read_config_u8(pdev, PCIR_INTPIN, &pin);
if (pin > RT_PCI_INTX_PIN_MAX)
{
pin = 1;
}
if (pin)
{
if (host_bridge->irq_slot)
{
slot = host_bridge->irq_slot(pdev, &pin);
}
/* Map IRQ */
if ((irq = host_bridge->irq_map(pdev, slot, pin)) == -1)
{
irq = 0;
}
}
pdev->irq = irq;
LOG_D("PCI-Device<%s> assign IRQ: got %d", rt_dm_dev_get_name(&pdev->parent), pdev->irq);
/* Save IRQ */
rt_pci_write_config_u8(pdev, PCIR_INTLINE, irq);
}
+15
View File
@@ -0,0 +1,15 @@
from building import *
group = []
if not GetDepend(['RT_PCI_MSI']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../../include']
src = ['device.c', 'irq.c', 'msi.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
+46
View File
@@ -0,0 +1,46 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-24 GuEe-GUI first version
*/
#include <drivers/pci.h>
void rt_pci_msi_init(struct rt_pci_device *pdev)
{
if (pdev && (pdev->msi_cap = rt_pci_find_capability(pdev, PCIY_MSI)))
{
rt_uint16_t ctrl;
rt_pci_read_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, &ctrl);
if (ctrl & PCIM_MSICTRL_MSI_ENABLE)
{
rt_pci_write_config_u16(pdev, pdev->msi_cap + PCIR_MSI_CTRL, ctrl & ~PCIM_MSICTRL_MSI_ENABLE);
}
if (!(ctrl & PCIM_MSICTRL_64BIT))
{
pdev->no_64bit_msi = RT_TRUE;
}
}
}
void rt_pci_msix_init(struct rt_pci_device *pdev)
{
if (pdev && (pdev->msix_cap = rt_pci_find_capability(pdev, PCIY_MSIX)))
{
rt_uint16_t ctrl;
rt_pci_read_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, &ctrl);
if (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE)
{
rt_pci_write_config_u16(pdev, pdev->msix_cap + PCIR_MSIX_CTRL, ctrl & ~PCIM_MSIXCTRL_MSIX_ENABLE);
}
}
}
+146
View File
@@ -0,0 +1,146 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-24 GuEe-GUI first version
*/
#include <drivers/pci_msi.h>
#define DBG_TAG "pci.msi.irq"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static struct rt_spinlock msi_irq_map_lock = {};
static RT_BITMAP_DECLARE(msi_irq_map, MAX_HANDLERS) = {};
rt_err_t rt_pci_msi_setup_irqs(struct rt_pci_device *pdev, int nvec, int type)
{
int irq, index = 0, irq_nr = 0;
rt_err_t err = RT_EOK;
struct rt_pic_irq *pirq;
struct rt_pic *msi_pic;
struct rt_pci_msi_desc *desc;
if (!pdev)
{
return -RT_EINVAL;
}
msi_pic = pdev->msi_pic;
if (type == PCIY_MSI)
{
int last_irq = -1;
rt_size_t irq_nr;
desc = rt_pci_msi_first_desc(pdev);
irq_nr = 1 << desc->msi.cap.multi_msg_use;
rt_hw_spin_lock(&msi_irq_map_lock.lock);
_retry:
for (int i = 0; i < irq_nr; ++i)
{
if ((irq = msi_pic->ops->irq_alloc_msi(msi_pic, desc)) < 0)
{
err = irq;
LOG_E("Setup %s[%d] IRQ error = %s", "MSI", i, rt_strerror(err));
break;
}
if (last_irq >= 0 && last_irq + 1 != irq)
{
for (int idx = 0; idx < i; ++i, --last_irq)
{
rt_bitmap_set_bit(msi_irq_map, last_irq);
}
last_irq = irq;
goto _retry;
}
last_irq = irq;
}
if (!err)
{
/* Get the first irq */
desc->irq = irq - irq_nr;
}
rt_bitmap_for_each_set_bit(msi_irq_map, irq, MAX_HANDLERS)
{
msi_pic->ops->irq_free_msi(msi_pic, irq);
/* Free bit so the next user doesn't need to bzero */
rt_bitmap_clear_bit(msi_irq_map, irq);
}
rt_hw_spin_unlock(&msi_irq_map_lock.lock);
if (!err)
{
for (int idx = 0; idx < nvec; ++idx)
{
pirq = rt_pic_find_pirq(msi_pic, irq + idx);
pirq->msi_desc = desc;
msi_pic->ops->irq_compose_msi_msg(pirq, &desc->msg);
rt_pci_msi_write_msg(desc, &desc->msg);
}
}
}
else if (type == PCIY_MSIX)
{
rt_pci_msi_for_each_desc(pdev, desc)
{
if ((irq = msi_pic->ops->irq_alloc_msi(msi_pic, desc)) < 0)
{
err = irq;
LOG_E("Setup %s[%d] IRQ error = %s", "MSI-X",
desc->msix.index, rt_strerror(err));
break;
}
desc->irq = irq;
pirq = rt_pic_find_pirq(msi_pic, irq);
pirq->msi_desc = desc;
msi_pic->ops->irq_compose_msi_msg(pirq, &desc->msg);
rt_pci_msi_write_msg(desc, &desc->msg);
++irq_nr;
}
if (err)
{
rt_pci_msi_for_each_desc(pdev, desc)
{
if (index >= irq_nr)
{
break;
}
msi_pic->ops->irq_free_msi(msi_pic, desc->irq);
++index;
}
}
}
else
{
err = -RT_EINVAL;
}
return err;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+121
View File
@@ -0,0 +1,121 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2022-10-24 GuEe-GUI first version
*/
#include <drivers/pci.h>
#include <drivers/core/power_domain.h>
#define DBG_TAG "pci.pme"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
/*
* Power Management Capability Register:
*
* 31 27 26 25 24 22 21 20 19 18 16 15 8 7 0
* +---------+---+---+--------+---+---+---+------+-----------+----------------+
* | | | | | | | | | | Capabilitiy ID |
* +---------+---+---+--------+---+---+---+------+-----------+----------------+
* ^ ^ ^ ^ ^ ^ ^ ^ ^
* | | | | | | | | |
* | | | | | | | | +---- Next Capabilitiy Pointer
* | | | | | | | +------------- Version
* | | | | | | +------------------- PME Clock
* | | | | | +----------------------- Immediate Readiness on Return to D0
* | | | | +--------------------------- Device Specifiic Initializtion
* | | | +--------------------------------- Aux Current
* | | +---------------------------------------- D1 Support
* | +-------------------------------------------- D2 Support
* +--------------------------------------------------- PME Support
*/
void rt_pci_pme_init(struct rt_pci_device *pdev)
{
rt_uint16_t pmc;
if (!pdev || !(pdev->pme_cap = rt_pci_find_capability(pdev, PCIY_PMG)))
{
return;
}
rt_pci_read_config_u16(pdev, pdev->pme_cap + PCIR_POWER_CAP, &pmc);
if ((pmc & PCIM_PCAP_SPEC) > 3)
{
LOG_E("%s: Unsupported PME CAP regs spec %u",
rt_dm_dev_get_name(&pdev->parent), pmc & PCIM_PCAP_SPEC);
return;
}
pmc &= PCIM_PCAP_PMEMASK;
if (pmc)
{
pdev->pme_support = RT_FIELD_GET(PCIM_PCAP_PMEMASK, pmc);
rt_pci_pme_active(pdev, RT_FALSE);
}
}
rt_err_t rt_pci_enable_wake(struct rt_pci_device *pdev,
enum rt_pci_power state, rt_bool_t enable)
{
if (!pdev || state >= RT_PCI_PME_MAX)
{
return -RT_EINVAL;
}
if (enable)
{
if (rt_pci_pme_capable(pdev, state) ||
rt_pci_pme_capable(pdev, RT_PCI_D3COLD))
{
rt_pci_pme_active(pdev, RT_EOK);
}
}
else
{
rt_pci_pme_active(pdev, RT_FALSE);
}
return RT_EOK;
}
static void pci_pme_active(struct rt_pci_device *pdev, rt_bool_t enable)
{
rt_uint16_t pmcsr;
if (!pdev->pme_support)
{
return;
}
rt_pci_read_config_u16(pdev, pdev->pme_cap + PCIR_POWER_STATUS, &pmcsr);
/* Clear PME_Status by writing 1 to it and enable PME# */
pmcsr |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
if (!enable)
{
pmcsr &= ~PCIM_PSTAT_PMEENABLE;
}
rt_pci_write_config_u16(pdev, pdev->pme_cap + PCIR_POWER_STATUS, pmcsr);
}
void rt_pci_pme_active(struct rt_pci_device *pdev, rt_bool_t enable)
{
if (!pdev)
{
return;
}
pci_pme_active(pdev, enable);
rt_dm_power_domain_attach(&pdev->parent, enable);
}
File diff suppressed because it is too large Load Diff