Changeset View
Changeset View
Standalone View
Standalone View
sys/arm64/broadcom/iproc_pcie.c
Context not available. | |||||
* endpoint devices. | * endpoint devices. | ||||
*/ | */ | ||||
enum iproc_pcie_type { | enum iproc_pcie_type { | ||||
IPROC_PCIE_PAXB_BCMA = 0, | IPROC_PCIE_PAXB = 0, | ||||
IPROC_PCIE_PAXB, | |||||
IPROC_PCIE_PAXB_V2, | |||||
IPROC_PCIE_PAXC, | IPROC_PCIE_PAXC, | ||||
IPROC_PCIE_PAXC_V2, | |||||
}; | }; | ||||
/* | /* | ||||
Context not available. | |||||
uint16_t offset; | uint16_t offset; | ||||
}; | }; | ||||
/* iProc PCIe PAXB BCMA registers */ | /* iProc PCIe PAXB v1 registers */ | ||||
static const struct iproc_pcie_regs iproc_pcie_reg_paxb_bcma[] = { | |||||
{IPROC_PCIE_CLK_CTRL, 0x000}, | |||||
{IPROC_PCIE_CFG_IND_ADDR, 0x120}, | |||||
{IPROC_PCIE_CFG_IND_DATA, 0x124}, | |||||
{IPROC_PCIE_CFG_ADDR, 0x1f8}, | |||||
{IPROC_PCIE_CFG_DATA, 0x1fc}, | |||||
{IPROC_PCIE_INTX_EN, 0x330}, | |||||
{IPROC_PCIE_LINK_STATUS, 0xf0c}, | |||||
}; | |||||
/* iProc PCIe PAXB registers */ | |||||
static const struct iproc_pcie_regs iproc_pcie_reg_paxb[] = { | static const struct iproc_pcie_regs iproc_pcie_reg_paxb[] = { | ||||
{IPROC_PCIE_CLK_CTRL, 0x000}, | {IPROC_PCIE_CLK_CTRL, 0x000}, | ||||
{IPROC_PCIE_CFG_IND_ADDR, 0x120}, | {IPROC_PCIE_CFG_IND_ADDR, 0x120}, | ||||
Context not available. | |||||
{IPROC_PCIE_APB_ERR_EN, 0xf40}, | {IPROC_PCIE_APB_ERR_EN, 0xf40}, | ||||
}; | }; | ||||
/* iProc PCIe PAXB v2 registers */ | |||||
static const struct iproc_pcie_regs iproc_pcie_reg_paxb_v2[] = { | |||||
{IPROC_PCIE_CLK_CTRL, 0x000}, | |||||
{IPROC_PCIE_CFG_IND_ADDR, 0x120}, | |||||
{IPROC_PCIE_CFG_IND_DATA, 0x124}, | |||||
{IPROC_PCIE_CFG_ADDR, 0x1f8}, | |||||
{IPROC_PCIE_CFG_DATA, 0x1fc}, | |||||
{IPROC_PCIE_INTX_EN, 0x330}, | |||||
{IPROC_PCIE_OARR0, 0xd20}, | |||||
{IPROC_PCIE_OMAP0, 0xd40}, | |||||
{IPROC_PCIE_OARR1, 0xd28}, | |||||
{IPROC_PCIE_OMAP1, 0xd48}, | |||||
{IPROC_PCIE_OARR2, 0xd60}, | |||||
{IPROC_PCIE_OMAP2, 0xd68}, | |||||
{IPROC_PCIE_OARR3, 0xdf0}, | |||||
{IPROC_PCIE_OMAP3, 0xdf8}, | |||||
{IPROC_PCIE_IARR0, 0xd00}, | |||||
{IPROC_PCIE_IMAP0, 0xc00}, | |||||
{IPROC_PCIE_IARR2, 0xd10}, | |||||
{IPROC_PCIE_IMAP2, 0xcc0}, | |||||
{IPROC_PCIE_IARR3, 0xe00}, | |||||
{IPROC_PCIE_IMAP3, 0xe08}, | |||||
{IPROC_PCIE_IARR4, 0xe68}, | |||||
{IPROC_PCIE_IMAP4, 0xe70}, | |||||
{IPROC_PCIE_LINK_STATUS, 0xf0c}, | |||||
{IPROC_PCIE_APB_ERR_EN, 0xf40}, | |||||
}; | |||||
/* iProc PCIe PAXC v1 registers */ | /* iProc PCIe PAXC v1 registers */ | ||||
static const struct iproc_pcie_regs iproc_pcie_reg_paxc[] = { | static const struct iproc_pcie_regs iproc_pcie_reg_paxc[] = { | ||||
{IPROC_PCIE_CLK_CTRL, 0x000}, | {IPROC_PCIE_CLK_CTRL, 0x000}, | ||||
Context not available. | |||||
{IPROC_PCIE_CFG_DATA, 0x1fc}, | {IPROC_PCIE_CFG_DATA, 0x1fc}, | ||||
}; | }; | ||||
/* iProc PCIe PAXC v2 registers */ | |||||
static const struct iproc_pcie_regs iproc_pcie_reg_paxc_v2[] = { | |||||
{IPROC_PCIE_MSI_GIC_MODE, 0x050}, | |||||
{IPROC_PCIE_MSI_BASE_ADDR, 0x074}, | |||||
{IPROC_PCIE_MSI_WINDOW_SIZE, 0x078}, | |||||
{IPROC_PCIE_MSI_ADDR_LO, 0x07c}, | |||||
{IPROC_PCIE_MSI_ADDR_HI, 0x080}, | |||||
{IPROC_PCIE_MSI_EN_CFG, 0x09c}, | |||||
{IPROC_PCIE_CFG_IND_ADDR, 0x1f0}, | |||||
{IPROC_PCIE_CFG_IND_DATA, 0x1f4}, | |||||
{IPROC_PCIE_CFG_ADDR, 0x1f8}, | |||||
{IPROC_PCIE_CFG_DATA, 0x1fc}, | |||||
}; | |||||
/* iProc PCIe outbound mapping info */ | /* iProc PCIe outbound mapping info */ | ||||
struct iproc_pcie_ob { | struct iproc_pcie_ob { | ||||
/* offset from AXI address to the internal PCIe core address */ | /* offset from AXI address to the internal PCIe core address */ | ||||
Context not available. | |||||
u_int func, u_int reg, uint32_t val, int bytes); | u_int func, u_int reg, uint32_t val, int bytes); | ||||
static uint32_t iproc_pcie_read_config_internal(device_t dev, u_int bus, | static uint32_t iproc_pcie_read_config_internal(device_t dev, u_int bus, | ||||
u_int slot, u_int func, u_int reg, int bytes); | u_int slot, u_int func, u_int reg, int bytes); | ||||
static void iproc_pcie_write_config_internal(device_t dev, u_int bus, | |||||
u_int slot, u_int func, u_int reg, uint32_t val, int bytes); | |||||
static uint32_t iproc_pcie_generic_config_read(device_t dev, u_int bus, | static uint32_t iproc_pcie_generic_config_read(device_t dev, u_int bus, | ||||
u_int slot, u_int func, u_int reg, int bytes); | u_int slot, u_int func, u_int reg, int bytes); | ||||
Context not available. | |||||
static int iproc_pcie_rev_init(struct iproc_pcie_softc *sc); | static int iproc_pcie_rev_init(struct iproc_pcie_softc *sc); | ||||
static uint32_t iproc_pcie_cfg_retry(struct iproc_pcie_softc *sc, uint16_t | static uint32_t iproc_pcie_cfg_retry(struct iproc_pcie_softc *sc, uint16_t | ||||
offset); | offset); | ||||
static void iproc_pcie_apb_err_disable(struct iproc_pcie_softc *sc, u_int bus, | static void iproc_pcie_apb_err_enable(struct iproc_pcie_softc *sc, u_int bus); | ||||
int disable); | static void iproc_pcie_apb_err_disable(struct iproc_pcie_softc *sc, u_int bus); | ||||
static void iproc_pcie_intx_enable(struct iproc_pcie_softc *sc); | static void iproc_pcie_intx_enable(struct iproc_pcie_softc *sc); | ||||
static int iproc_pcie_ob_write(struct iproc_pcie_softc *sc, int window, | static int iproc_pcie_ob_write(struct iproc_pcie_softc *sc, int window, | ||||
int size_idx, uint64_t axi_addr, uint64_t pci_addr); | int size_idx, uint64_t axi_addr, uint64_t pci_addr); | ||||
Context not available. | |||||
/* Bus interface */ | /* Bus interface */ | ||||
DEVMETHOD(bus_get_dma_tag, iproc_pcie_get_dma_tag), | DEVMETHOD(bus_get_dma_tag, iproc_pcie_get_dma_tag), | ||||
DEVMETHOD(bus_read_ivar, ofw_pci_read_ivar), | |||||
DEVMETHOD(bus_write_ivar, ofw_pci_write_ivar), | |||||
DEVMETHOD(bus_alloc_resource, ofw_pci_alloc_resource), | |||||
DEVMETHOD(bus_release_resource, ofw_pci_release_resource), | |||||
DEVMETHOD(bus_activate_resource, ofw_pci_activate_resource), | |||||
DEVMETHOD(bus_deactivate_resource, ofw_pci_deactivate_resource), | |||||
DEVMETHOD(bus_adjust_resource, ofw_pci_adjust_resource), | |||||
DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), | |||||
DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), | |||||
/* pcib interface */ | /* pcib interface */ | ||||
DEVMETHOD(pcib_route_interrupt, iproc_pcie_route_interrupt), | DEVMETHOD(pcib_route_interrupt, iproc_pcie_route_interrupt), | ||||
Context not available. | |||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
static driver_t iproc_pcie_driver = { | |||||
"pcib", | |||||
iproc_pcie_methods, | |||||
sizeof(struct iproc_pcie_softc), | |||||
}; | |||||
static devclass_t iproc_pcie_devclass; | static devclass_t iproc_pcie_devclass; | ||||
DEFINE_CLASS_1(pcib, iproc_pcie_driver, iproc_pcie_methods, | |||||
sizeof(struct iproc_pcie_softc), ofw_pci_driver); | |||||
DRIVER_MODULE(iproc_pcie, simplebus, iproc_pcie_driver, iproc_pcie_devclass, 0, 0); | DRIVER_MODULE(iproc_pcie, simplebus, iproc_pcie_driver, iproc_pcie_devclass, 0, 0); | ||||
DRIVER_MODULE(iproc_pcie, ofwbus, iproc_pcie_driver, iproc_pcie_devclass, 0, 0); | DRIVER_MODULE(iproc_pcie, ofwbus, iproc_pcie_driver, iproc_pcie_devclass, 0, 0); | ||||
MODULE_DEPEND(iproc_pcie, pci, 1, 1, 1); | MODULE_DEPEND(iproc_pcie, pci, 1, 1, 1); | ||||
static struct ofw_compat_data compat_data[] = { | static struct ofw_compat_data compat_data[] = { | ||||
{"brcm,iproc-pcie", (uintptr_t)IPROC_PCIE_PAXB}, | {"brcm,iproc-pcie", (uintptr_t)IPROC_PCIE_PAXB}, | ||||
{"brcm,iproc-pcie-paxb-v2", (uintptr_t)IPROC_PCIE_PAXB_V2}, | |||||
{"brcm,iproc-pcie-paxc", (uintptr_t)IPROC_PCIE_PAXC}, | {"brcm,iproc-pcie-paxc", (uintptr_t)IPROC_PCIE_PAXC}, | ||||
{"brcm,iproc-pcie-nitro", (uintptr_t)IPROC_PCIE_PAXC}, | {"brcm,iproc-pcie-nitro", (uintptr_t)IPROC_PCIE_PAXC}, | ||||
{"brcm,iproc-pcie-paxc-v2", (uintptr_t)IPROC_PCIE_PAXC_V2}, | |||||
{NULL, 0} | {NULL, 0} | ||||
}; | }; | ||||
Context not available. | |||||
rv = iproc_pcie_check_link(sc); | rv = iproc_pcie_check_link(sc); | ||||
if (rv != 0) { | if (rv != 0) { | ||||
device_printf(dev, "no PCIe EP device detected\n"); | if (bootverbose) | ||||
device_printf(dev, "no PCIe EP device detected\n"); | |||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
Context not available. | |||||
sc = device_get_softc(bus); | sc = device_get_softc(bus); | ||||
/* PAXC doesn't support legacy IRQs, skip mapping */ | /* PAXC doesn't support legacy IRQs, skip mapping */ | ||||
if (sc->type == IPROC_PCIE_PAXC || | if (sc->type == IPROC_PCIE_PAXC) | ||||
sc->type == IPROC_PCIE_PAXC_V2) | |||||
return (PCI_INVALID_IRQ); | return (PCI_INVALID_IRQ); | ||||
return (ofw_pci_route_interrupt(bus, dev, pin)); | return (ofw_pci_route_interrupt(bus, dev, pin)); | ||||
Context not available. | |||||
iproc_pcie_reg_invalid(uint16_t reg_offset) | iproc_pcie_reg_invalid(uint16_t reg_offset) | ||||
{ | { | ||||
return (!!(reg_offset == IPROC_PCIE_REG_INVALID)); | return (reg_offset == IPROC_PCIE_REG_INVALID); | ||||
} | } | ||||
static uint16_t | static uint16_t | ||||
Context not available. | |||||
if (slot > 0) | if (slot > 0) | ||||
return (~0U); | return (~0U); | ||||
iproc_pcie_apb_err_disable(sc, bus, 1); | iproc_pcie_apb_err_disable(sc, bus); | ||||
if (sc->iproc_cfg_read) | if (sc->iproc_cfg_read) | ||||
val = iproc_pcie_read_config_internal(dev, bus, slot, | val = iproc_pcie_read_config_internal(dev, bus, slot, | ||||
Context not available. | |||||
val = iproc_pcie_generic_config_read(dev, bus, slot, func, reg, | val = iproc_pcie_generic_config_read(dev, bus, slot, func, reg, | ||||
bytes); | bytes); | ||||
iproc_pcie_apb_err_disable(sc, bus, 0); | iproc_pcie_apb_err_enable(sc, bus); | ||||
/* quirks */ | /* quirks */ | ||||
if (sc->ep_is_internal) { | if (sc->ep_is_internal) { | ||||
Context not available. | |||||
u_int func, u_int reg, uint32_t val, int bytes) | u_int func, u_int reg, uint32_t val, int bytes) | ||||
{ | { | ||||
struct iproc_pcie_softc *sc; | struct iproc_pcie_softc *sc; | ||||
sc = device_get_softc(dev); | |||||
iproc_pcie_apb_err_disable(sc, bus, 1); | |||||
iproc_pcie_write_config_internal(dev, bus, slot, | |||||
func, reg, val, bytes); | |||||
iproc_pcie_apb_err_disable(sc, bus, 0); | |||||
} | |||||
static void | |||||
iproc_pcie_write_config_internal(device_t dev, u_int bus, u_int slot, | |||||
u_int func, u_int reg, uint32_t val, int bytes) | |||||
{ | |||||
struct iproc_pcie_softc *sc; | |||||
uint32_t mask, tmp; | uint32_t mask, tmp; | ||||
uint16_t offset; | uint16_t offset; | ||||
int rv; | int rv; | ||||
sc = device_get_softc(dev); | |||||
if ((bus > PCI_BUSMAX) || (slot > PCI_SLOTMAX) || | if ((bus > PCI_BUSMAX) || (slot > PCI_SLOTMAX) || | ||||
(func > PCI_FUNCMAX) || (reg > PCIE_REGMAX)) | (func > PCI_FUNCMAX) || (reg > PCIE_REGMAX)) | ||||
return; | return; | ||||
sc = device_get_softc(dev); | iproc_pcie_apb_err_disable(sc, bus); | ||||
rv = iproc_pcie_map_cfg_bus(sc, bus, slot, func, reg & ~0x3, &offset); | rv = iproc_pcie_map_cfg_bus(sc, bus, slot, func, reg & ~0x3, &offset); | ||||
if (rv != 0) | if (rv != 0) | ||||
return; | goto out; | ||||
if (bytes == 4) { | if (bytes == 4) { | ||||
bus_write_4(sc->reg, offset, val); | bus_write_4(sc->reg, offset, val); | ||||
bus_barrier(sc->reg, offset, 4, BUS_SPACE_BARRIER_WRITE); | bus_barrier(sc->reg, offset, 4, BUS_SPACE_BARRIER_WRITE); | ||||
return; | goto out; | ||||
} | } | ||||
mask = ~(((1 << (bytes * 8)) - 1) << ((reg & 0x3) * 8)); | mask = ~(((1 << (bytes * 8)) - 1) << ((reg & 0x3) * 8)); | ||||
Context not available. | |||||
bus_write_4(sc->reg, offset, tmp); | bus_write_4(sc->reg, offset, tmp); | ||||
bus_barrier(sc->reg, offset, 4, BUS_SPACE_BARRIER_WRITE); | bus_barrier(sc->reg, offset, 4, BUS_SPACE_BARRIER_WRITE); | ||||
out: | |||||
iproc_pcie_apb_err_enable(sc, bus); | |||||
} | } | ||||
static uint32_t | static uint32_t | ||||
Context not available. | |||||
* triggering a system exception. | * triggering a system exception. | ||||
*/ | */ | ||||
static void | static void | ||||
iproc_pcie_apb_err_disable(struct iproc_pcie_softc *sc, u_int bus, int disable) | iproc_pcie_apb_err_disable(struct iproc_pcie_softc *sc, u_int bus) | ||||
{ | { | ||||
uint32_t val; | uint32_t val; | ||||
int rv; | int rv; | ||||
Context not available. | |||||
if (rv != 0) | if (rv != 0) | ||||
return; | return; | ||||
if (disable) | val &= ~APB_ERR_EN; | ||||
val &= ~APB_ERR_EN; | rv = iproc_pcie_write_reg(sc, IPROC_PCIE_APB_ERR_EN, val); | ||||
else | } | ||||
val |= APB_ERR_EN; | } | ||||
static void | |||||
iproc_pcie_apb_err_enable(struct iproc_pcie_softc *sc, u_int bus) | |||||
{ | |||||
uint32_t val; | |||||
int rv; | |||||
if (bus && sc->has_apb_err_disable) { | |||||
rv = iproc_pcie_read_reg(sc, IPROC_PCIE_APB_ERR_EN, &val); | |||||
if (rv != 0) | |||||
return; | |||||
val |= APB_ERR_EN; | |||||
rv = iproc_pcie_write_reg(sc, IPROC_PCIE_APB_ERR_EN, val); | rv = iproc_pcie_write_reg(sc, IPROC_PCIE_APB_ERR_EN, val); | ||||
} | } | ||||
} | } | ||||
Context not available. | |||||
{ | { | ||||
switch (sc->type) { | switch (sc->type) { | ||||
case IPROC_PCIE_PAXB_BCMA: | |||||
sc->reg_offsets = iproc_pcie_reg_paxb_bcma; | |||||
sc->num_regs = nitems(iproc_pcie_reg_paxb_bcma); | |||||
device_printf(sc->dev, "unsupported iProc PCIe interface\n"); | |||||
return (ENXIO); | |||||
case IPROC_PCIE_PAXB: | case IPROC_PCIE_PAXB: | ||||
sc->reg_offsets = iproc_pcie_reg_paxb; | sc->reg_offsets = iproc_pcie_reg_paxb; | ||||
sc->num_regs = nitems(iproc_pcie_reg_paxb); | sc->num_regs = nitems(iproc_pcie_reg_paxb); | ||||
Context not available. | |||||
sc->ob.nr_windows = nitems(paxb_ob_map); | sc->ob.nr_windows = nitems(paxb_ob_map); | ||||
} | } | ||||
break; | break; | ||||
case IPROC_PCIE_PAXB_V2: | |||||
sc->reg_offsets = iproc_pcie_reg_paxb_v2; | |||||
sc->num_regs = nitems(iproc_pcie_reg_paxb_v2); | |||||
device_printf(sc->dev, "unsupported iProc PCIe interface\n"); | |||||
return (ENXIO); | |||||
case IPROC_PCIE_PAXC: | case IPROC_PCIE_PAXC: | ||||
sc->reg_offsets = iproc_pcie_reg_paxc; | sc->reg_offsets = iproc_pcie_reg_paxc; | ||||
sc->num_regs = nitems(iproc_pcie_reg_paxc); | sc->num_regs = nitems(iproc_pcie_reg_paxc); | ||||
sc->ep_is_internal = 1; | sc->ep_is_internal = 1; | ||||
sc->iproc_cfg_read = 1; | sc->iproc_cfg_read = 1; | ||||
break; | break; | ||||
case IPROC_PCIE_PAXC_V2: | |||||
sc->reg_offsets = iproc_pcie_reg_paxc_v2; | |||||
sc->num_regs = nitems(iproc_pcie_reg_paxc_v2); | |||||
device_printf(sc->dev, "unsupported iProc PCIe interface\n"); | |||||
return (ENXIO); | |||||
default: | default: | ||||
device_printf(sc->dev, "incompatible iProc PCIe interface\n"); | device_printf(sc->dev, "unsupported iProc PCIe interface\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
Context not available. |