Changeset View
Changeset View
Standalone View
Standalone View
head/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 | ||||
markmi_dsl-only.net: FYI: sys/arm64/arm64/busdma_machdep.c 's common_bus_dma_tag_create has:
```
common… | |||||
crowston_protonmail.comAuthorUnsubmitted Done Inline ActionsI will try the better bound on the DMA high limit. Thank you for your tenacious investigation! crowston_protonmail.com: I will try the better bound on the DMA high limit.
Thank you for your tenacious investigation! | |||||
markmi_dsl-only.netUnsubmitted Not Done Inline ActionsBy the way: the edk2 toolkit uses the 0x11 figure instead of the below 0x12 figure (0x21-0xf). So my list-posted patch that also reverted to your earlier REG_VALUE_4GB_WINDOW and REG_VALUE_4GB_CONFIG use matches what they did. So far, I've not seen any evidence of problems with what I did. markmi_dsl-only.net: By the way: the edk2 toolkit uses the 0x11 figure instead of the below 0x12 figure (0x21-0xf). | |||||
#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) | |||||
#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 */ | |||||
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); | ||||
FYI: sys/arm64/arm64/busdma_machdep.c 's common_bus_dma_tag_create has:
and so forces reference to the last byte of the page identified by lowaddr (and highaddr). Thus lowaddr needs to identify the last page that can avoid bouncing (or earlier), not the first page that needs bouncing.
arm64, arm, powerpc, riscv, mips, and x86 all use the trunc_page and PAGE_SIZE-1 computation. I do not remember a hint of that from the documentation for bus_dma_tag_create.
Turns out that that basing the code on subtracting 1 from 0xc0000000u for DMA_HIGH_LIMIT works without needing to force dma_bits to 32 in the bcm2838_xhci.c code.
(I'm unsure of the old #define pair vs. the newer material below. 0x21-0xf=0x12 instead of 0x11, for example. But I'm not PCIe literate to know which is better. I used the old pair in my testing the adjusted DMA_HIGH_LIMIT.)