Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/broadcom/bcm2835/bcm2838_pci.c
Show First 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
#include <dev/pci/pcib_private.h> | #include <dev/pci/pcib_private.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <machine/intr.h> | #include <machine/intr.h> | ||||
#include "pcib_if.h" | #include "pcib_if.h" | ||||
#include "msi_if.h" | #include "msi_if.h" | ||||
extern struct bus_space memmap_bus; | |||||
#define BUS_SPACE_3G_MAXADDR 0xc0000000 | |||||
#define PCI_ID_VAL3 0x43c | #define PCI_ID_VAL3 0x43c | ||||
#define CLASS_SHIFT 0x10 | #define CLASS_SHIFT 0x10 | ||||
#define SUBCLASS_SHIFT 0x8 | #define SUBCLASS_SHIFT 0x8 | ||||
#define REG_CONTROLLER_HW_REV 0x406c | #define REG_CONTROLLER_HW_REV 0x406c | ||||
#define REG_BRIDGE_CTRL 0x9210 | #define REG_BRIDGE_CTRL 0x9210 | ||||
#define BRIDGE_DISABLE_FLAG 0x1 | #define BRIDGE_DISABLE_FLAG 0x1 | ||||
#define BRIDGE_RESET_FLAG 0x2 | #define BRIDGE_RESET_FLAG 0x2 | ||||
#define REG_BRIDGE_SERDES_MODE 0x4204 | #define REG_BRIDGE_SERDES_MODE 0x4204 | ||||
#define REG_BRIDGE_CONFIG 0x4008 | #define REG_DMA_CONFIG 0x4008 | ||||
#define REG_BRIDGE_MEM_WINDOW_LOW 0x4034 | #define REG_DMA_WINDOW_LOW 0x4034 | ||||
#define REG_BRIDGE_MEM_WINDOW_HIGH 0x4038 | #define REG_DMA_WINDOW_HIGH 0x4038 | ||||
#define REG_BRIDGE_MEM_WINDOW_1 0x403c | #define REG_DMA_WINDOW_1 0x403c | ||||
#define REG_BRIDGE_GISB_WINDOW 0x402c | #define REG_BRIDGE_GISB_WINDOW 0x402c | ||||
#define REG_BRIDGE_STATE 0x4068 | #define REG_BRIDGE_STATE 0x4068 | ||||
#define REG_BRIDGE_LINK_STATE 0x00bc | #define REG_BRIDGE_LINK_STATE 0x00bc | ||||
#define REG_BRIDGE_BUS_WINDOW_LOW 0x400c | #define REG_BUS_WINDOW_LOW 0x400c | ||||
#define REG_BRIDGE_BUS_WINDOW_HIGH 0x4010 | #define REG_BUS_WINDOW_HIGH 0x4010 | ||||
#define REG_BRIDGE_CPU_WINDOW_LOW 0x4070 | #define REG_CPU_WINDOW_LOW 0x4070 | ||||
#define REG_BRIDGE_CPU_WINDOW_START_HIGH 0x4080 | #define REG_CPU_WINDOW_START_HIGH 0x4080 | ||||
#define REG_BRIDGE_CPU_WINDOW_END_HIGH 0x4084 | #define REG_CPU_WINDOW_END_HIGH 0x4084 | ||||
#define REG_MSI_ADDR_LOW 0x4044 | #define REG_MSI_ADDR_LOW 0x4044 | ||||
#define REG_MSI_ADDR_HIGH 0x4048 | #define REG_MSI_ADDR_HIGH 0x4048 | ||||
#define REG_MSI_CONFIG 0x404c | #define REG_MSI_CONFIG 0x404c | ||||
#define REG_MSI_CLR 0x4508 | #define REG_MSI_CLR 0x4508 | ||||
#define REG_MSI_MASK_CLR 0x4514 | #define REG_MSI_MASK_CLR 0x4514 | ||||
#define REG_MSI_RAISED 0x4500 | #define REG_MSI_RAISED 0x4500 | ||||
#define REG_MSI_EOI 0x4060 | #define REG_MSI_EOI 0x4060 | ||||
#define NUM_MSI 32 | #define NUM_MSI 32 | ||||
#define REG_EP_CONFIG_CHOICE 0x9000 | #define REG_EP_CONFIG_CHOICE 0x9000 | ||||
#define REG_EP_CONFIG_DATA 0x8000 | #define REG_EP_CONFIG_DATA 0x8000 | ||||
/* | /* | ||||
* These values were obtained from runtime inspection of a Linux system using a | * The system memory controller can address up to 16 GiB of physical memory | ||||
* JTAG. The very limited documentation I have obtained from Broadcom does not | * (although at time of writing the largest memory size available for purchase | ||||
* explain how to compute them. | * is 8 GiB). However, the system DMA controller is capable of accessing only a | ||||
* limited portion of the address space. Worse, the PCI-e controller has further | |||||
* constraints for DMA, and those limitations are not wholly clear to the | |||||
* author. NetBSD and Linux allow DMA on the lower 3 GiB of the physical memory, | |||||
* but experimentation shows DMA performed above 960 MiB results in data | |||||
* corruption with this driver. The limit of 960 MiB is taken from OpenBSD, but | |||||
* apparently that value was chosen for satisfying a constraint of an unrelated | |||||
* peripheral. | |||||
* | |||||
* Whatever the true maximum address, 960 MiB works. | |||||
*/ | */ | ||||
#define REG_VALUE_4GB_WINDOW 0x11 | #define DMA_HIGH_LIMIT 0x3c000000 | ||||
#define REG_VALUE_4GB_CONFIG 0x88003000 | #define MAX_MEMORY_LOG2 0x21 | ||||
#define REG_VALUE_DMA_WINDOW_LOW (MAX_MEMORY_LOG2 - 0xf) | |||||
#define REG_VALUE_DMA_WINDOW_HIGH 0x0 | |||||
#define DMA_WINDOW_ENABLE 0x3000 | |||||
#define REG_VALUE_DMA_WINDOW_CONFIG \ | |||||
(((MAX_MEMORY_LOG2 - 0xf) << 0x1b) | DMA_WINDOW_ENABLE) | |||||
andrew: Can you give this a bcm2838 specific name so we don't confuse it with the existing… | |||||
#define REG_VALUE_MSI_CONFIG 0xffe06540 | #define REG_VALUE_MSI_CONFIG 0xffe06540 | ||||
struct bcm_pcib_irqsrc { | struct bcm_pcib_irqsrc { | ||||
struct intr_irqsrc isrc; | struct intr_irqsrc isrc; | ||||
u_int irq; | u_int irq; | ||||
bool allocated; | bool allocated; | ||||
}; | }; | ||||
struct bcm_pcib_softc { | struct bcm_pcib_softc { | ||||
struct generic_pcie_fdt_softc base; | struct generic_pcie_fdt_softc base; | ||||
device_t dev; | device_t dev; | ||||
bus_dma_tag_t dmat; | |||||
struct mtx config_mtx; | struct mtx config_mtx; | ||||
struct mtx msi_mtx; | struct mtx msi_mtx; | ||||
struct resource *msi_irq_res; | struct resource *msi_irq_res; | ||||
void *msi_intr_cookie; | void *msi_intr_cookie; | ||||
struct bcm_pcib_irqsrc *msi_isrcs; | struct bcm_pcib_irqsrc *msi_isrcs; | ||||
pci_addr_t msi_addr; | pci_addr_t msi_addr; | ||||
}; | }; | ||||
Show All 14 Lines | bcm_pcib_probe(device_t dev) | ||||
if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) | if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) | ||||
return (ENXIO); | return (ENXIO); | ||||
device_set_desc(dev, | device_set_desc(dev, | ||||
"BCM2838-compatible PCI-express controller"); | "BCM2838-compatible PCI-express controller"); | ||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
} | } | ||||
static bus_dma_tag_t | |||||
bcm_pcib_get_dma_tag(device_t dev, device_t child) | |||||
{ | |||||
struct bcm_pcib_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
return (sc->dmat); | |||||
} | |||||
static void | static void | ||||
bcm_pcib_set_reg(struct bcm_pcib_softc *sc, uint32_t reg, uint32_t val) | bcm_pcib_set_reg(struct bcm_pcib_softc *sc, uint32_t reg, uint32_t val) | ||||
{ | { | ||||
bus_space_write_4(sc->base.base.bst, sc->base.base.bsh, reg, | bus_space_write_4(sc->base.base.bst, sc->base.base.bsh, reg, | ||||
htole32(val)); | htole32(val)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 456 Lines • ▼ Show 20 Lines | bcm_pcib_attach(device_t dev) | ||||
pci_addr_t phys_base, pci_base; | pci_addr_t phys_base, pci_base; | ||||
bus_size_t size; | bus_size_t size; | ||||
uint32_t hardware_rev, bridge_state, link_state; | uint32_t hardware_rev, bridge_state, link_state; | ||||
int error, tries; | int error, tries; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sc->dev = dev; | sc->dev = dev; | ||||
/* | |||||
* This tag will be used in preference to the one created in | |||||
* pci_host_generic.c. | |||||
*/ | |||||
error = bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ | |||||
1, 0, /* alignment, bounds */ | |||||
DMA_HIGH_LIMIT, /* lowaddr */ | |||||
BUS_SPACE_MAXADDR, /* highaddr */ | |||||
NULL, NULL, /* filter, filterarg */ | |||||
DMA_HIGH_LIMIT, /* maxsize */ | |||||
BUS_SPACE_UNRESTRICTED, /* nsegments */ | |||||
DMA_HIGH_LIMIT, /* maxsegsize */ | |||||
0, /* flags */ | |||||
Done Inline ActionsNote: from a RPi4B: /usr/include/machine/bus.h:#define BUS_SPACE_MAXSIZE 0xFFFFFFFFFFFFFFFFUL bus_dma(9) reports for boundary and maxsegsz: The boundary must be a power of 2 and must be no smaller than the maximum segment size. `0' indicates that there are no boundary re- strictions. Maximum size, in bytes, of a segment in any DMA mapped region associated with dmat. Unless bus_get_dma_tag(dev) returns something that already has sufficiently restrictive values for the device that will constrain what is specified here, the above reads to me like the bus_dma_tag_create call allows for segments too large to fit the context. If there are such implicit constraints via bus_get_dma_tag(dev), should it be commented on in the code? markmi_dsl-only.net: Note: from a RPi4B:
/usr/include/machine/bus.h:#define BUS_SPACE_MAXSIZE… | |||||
Done Inline ActionsNice catch, thank you. crowston_protonmail.com: Nice catch, thank you. | |||||
NULL, NULL, /* lockfunc, lockarg */ | |||||
&sc->dmat); | |||||
if (error) | |||||
return (error); | |||||
error = pci_host_generic_setup_fdt(dev); | error = pci_host_generic_setup_fdt(dev); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
error = bcm_pcib_check_ranges(dev); | error = bcm_pcib_check_ranges(dev); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
mtx_init(&sc->config_mtx, "bcm_pcib: config_mtx", NULL, MTX_DEF); | mtx_init(&sc->config_mtx, "bcm_pcib: config_mtx", NULL, MTX_DEF); | ||||
bcm_pcib_reset_controller(sc); | bcm_pcib_reset_controller(sc); | ||||
hardware_rev = bcm_pcib_read_reg(sc, REG_CONTROLLER_HW_REV) & 0xffff; | hardware_rev = bcm_pcib_read_reg(sc, REG_CONTROLLER_HW_REV) & 0xffff; | ||||
device_printf(dev, "hardware identifies as revision 0x%x.\n", | device_printf(dev, "hardware identifies as revision 0x%x.\n", | ||||
hardware_rev); | hardware_rev); | ||||
/* | /* | ||||
* Set PCI->CPU memory window. This encodes the inbound window showing | * Set PCI->CPU memory window. This encodes the inbound window showing | ||||
* up to 4 GiB of system memory to the controller, with zero offset. | * the system memory to the controller. | ||||
* Thus, from the perspective of a device on the PCI-E bus, there is a | |||||
* 1:1 map from PCI-E bus addresses to system memory addresses. However, | |||||
* a hardware limitation means that the controller can only perform DMA | |||||
* on the lower 3 GiB of system memory. | |||||
*/ | */ | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_MEM_WINDOW_LOW, REG_VALUE_4GB_WINDOW); | bcm_pcib_set_reg(sc, REG_DMA_WINDOW_LOW, REG_VALUE_DMA_WINDOW_LOW); | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_MEM_WINDOW_HIGH, 0); | bcm_pcib_set_reg(sc, REG_DMA_WINDOW_HIGH, REG_VALUE_DMA_WINDOW_HIGH); | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_CONFIG, REG_VALUE_4GB_CONFIG); | bcm_pcib_set_reg(sc, REG_DMA_CONFIG, REG_VALUE_DMA_WINDOW_CONFIG); | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_GISB_WINDOW, 0); | bcm_pcib_set_reg(sc, REG_BRIDGE_GISB_WINDOW, 0); | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_MEM_WINDOW_1, 0); | bcm_pcib_set_reg(sc, REG_DMA_WINDOW_1, 0); | ||||
bcm_pcib_enable_controller(sc); | bcm_pcib_enable_controller(sc); | ||||
/* Wait for controller to start. */ | /* Wait for controller to start. */ | ||||
for(tries = 0; ; ++tries) { | for(tries = 0; ; ++tries) { | ||||
bridge_state = bcm_pcib_read_reg(sc, REG_BRIDGE_STATE); | bridge_state = bcm_pcib_read_reg(sc, REG_BRIDGE_STATE); | ||||
if ((bridge_state & 0x30) == 0x30) | if ((bridge_state & 0x30) == 0x30) | ||||
Show All 23 Lines | bcm_pcib_attach(device_t dev) | ||||
* Set the CPU->PCI memory window. The map in this direction is not 1:1. | * Set the CPU->PCI memory window. The map in this direction is not 1:1. | ||||
* Addresses seen by the CPU need to be adjusted to make sense to the | * Addresses seen by the CPU need to be adjusted to make sense to the | ||||
* controller as they pass through the window. | * controller as they pass through the window. | ||||
*/ | */ | ||||
pci_base = sc->base.base.ranges[0].pci_base; | pci_base = sc->base.base.ranges[0].pci_base; | ||||
phys_base = sc->base.base.ranges[0].phys_base; | phys_base = sc->base.base.ranges[0].phys_base; | ||||
size = sc->base.base.ranges[0].size; | size = sc->base.base.ranges[0].size; | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_BUS_WINDOW_LOW, pci_base & 0xffffffff); | bcm_pcib_set_reg(sc, REG_BUS_WINDOW_LOW, pci_base & 0xffffffff); | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_BUS_WINDOW_HIGH, pci_base >> 32); | bcm_pcib_set_reg(sc, REG_BUS_WINDOW_HIGH, pci_base >> 32); | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_CPU_WINDOW_LOW, | bcm_pcib_set_reg(sc, REG_CPU_WINDOW_LOW, | ||||
encode_cpu_window_low(phys_base, size)); | encode_cpu_window_low(phys_base, size)); | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_CPU_WINDOW_START_HIGH, | bcm_pcib_set_reg(sc, REG_CPU_WINDOW_START_HIGH, | ||||
encode_cpu_window_start_high(phys_base)); | encode_cpu_window_start_high(phys_base)); | ||||
bcm_pcib_set_reg(sc, REG_BRIDGE_CPU_WINDOW_END_HIGH, | bcm_pcib_set_reg(sc, REG_CPU_WINDOW_END_HIGH, | ||||
encode_cpu_window_end_high(phys_base, size)); | encode_cpu_window_end_high(phys_base, size)); | ||||
/* | /* | ||||
* The controller starts up declaring itself an endpoint; readvertise it | * The controller starts up declaring itself an endpoint; readvertise it | ||||
* as a bridge. | * as a bridge. | ||||
*/ | */ | ||||
bcm_pcib_set_reg(sc, PCI_ID_VAL3, | bcm_pcib_set_reg(sc, PCI_ID_VAL3, | ||||
PCIC_BRIDGE << CLASS_SHIFT | PCIS_BRIDGE_PCI << SUBCLASS_SHIFT); | PCIC_BRIDGE << CLASS_SHIFT | PCIS_BRIDGE_PCI << SUBCLASS_SHIFT); | ||||
Show All 12 Lines | bcm_pcib_attach(device_t dev) | ||||
device_add_child(dev, "pci", -1); | device_add_child(dev, "pci", -1); | ||||
return (bus_generic_attach(dev)); | return (bus_generic_attach(dev)); | ||||
} | } | ||||
/* | /* | ||||
* Device method table. | * Device method table. | ||||
*/ | */ | ||||
static device_method_t bcm_pcib_methods[] = { | static device_method_t bcm_pcib_methods[] = { | ||||
/* Bus interface. */ | |||||
DEVMETHOD(bus_get_dma_tag, bcm_pcib_get_dma_tag), | |||||
/* Device interface. */ | /* Device interface. */ | ||||
DEVMETHOD(device_probe, bcm_pcib_probe), | DEVMETHOD(device_probe, bcm_pcib_probe), | ||||
DEVMETHOD(device_attach, bcm_pcib_attach), | DEVMETHOD(device_attach, bcm_pcib_attach), | ||||
/* PCIB interface. */ | /* PCIB interface. */ | ||||
DEVMETHOD(pcib_read_config, bcm_pcib_read_config), | DEVMETHOD(pcib_read_config, bcm_pcib_read_config), | ||||
DEVMETHOD(pcib_write_config, bcm_pcib_write_config), | DEVMETHOD(pcib_write_config, bcm_pcib_write_config), | ||||
/* MSI interface. */ | /* MSI interface. */ | ||||
DEVMETHOD(msi_alloc_msi, bcm_pcib_alloc_msi), | DEVMETHOD(msi_alloc_msi, bcm_pcib_alloc_msi), | ||||
DEVMETHOD(msi_release_msi, bcm_pcib_release_msi), | DEVMETHOD(msi_release_msi, bcm_pcib_release_msi), | ||||
DEVMETHOD(msi_map_msi, bcm_pcib_map_msi), | DEVMETHOD(msi_map_msi, bcm_pcib_map_msi), | ||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
DEFINE_CLASS_1(pcib, bcm_pcib_driver, bcm_pcib_methods, | DEFINE_CLASS_1(pcib, bcm_pcib_driver, bcm_pcib_methods, | ||||
sizeof(struct bcm_pcib_softc), generic_pcie_fdt_driver); | sizeof(struct bcm_pcib_softc), generic_pcie_fdt_driver); | ||||
static devclass_t bcm_pcib_devclass; | static devclass_t bcm_pcib_devclass; | ||||
DRIVER_MODULE(bcm_pcib, simplebus, bcm_pcib_driver, bcm_pcib_devclass, 0, 0); | DRIVER_MODULE(bcm_pcib, simplebus, bcm_pcib_driver, bcm_pcib_devclass, 0, 0); | ||||
Can you give this a bcm2838 specific name so we don't confuse it with the existing BUS_SPACE_* macros.