diff options
| author | Alexander Motin <mav@FreeBSD.org> | 2017-09-11 18:48:09 +0000 |
|---|---|---|
| committer | Alexander Motin <mav@FreeBSD.org> | 2017-09-11 18:48:09 +0000 |
| commit | 4791823d15269c5782bc274c483951207ba1f03b (patch) | |
| tree | 543e0555bc10384cf228c379926774fef20f6f6a /sys/dev/ntb | |
| parent | 3eb8998b7a92aba424ce6e3cd266313c8cc39ebd (diff) | |
Notes
Diffstat (limited to 'sys/dev/ntb')
| -rw-r--r-- | sys/dev/ntb/ntb_hw/ntb_hw_intel.c (renamed from sys/dev/ntb/ntb_hw/ntb_hw.c) | 8 | ||||
| -rw-r--r-- | sys/dev/ntb/ntb_hw/ntb_hw_intel.h (renamed from sys/dev/ntb/ntb_hw/ntb_regs.h) | 0 | ||||
| -rw-r--r-- | sys/dev/ntb/ntb_hw/ntb_hw_plx.c | 950 |
3 files changed, 954 insertions, 4 deletions
diff --git a/sys/dev/ntb/ntb_hw/ntb_hw.c b/sys/dev/ntb/ntb_hw/ntb_hw_intel.c index 9c8314f5a089..2375fb8005e4 100644 --- a/sys/dev/ntb/ntb_hw/ntb_hw.c +++ b/sys/dev/ntb/ntb_hw/ntb_hw_intel.c @@ -61,7 +61,7 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> -#include "ntb_regs.h" +#include "ntb_hw_intel.h" #include "../ntb.h" #define MAX_MSIX_INTERRUPTS MAX(XEON_DB_COUNT, ATOM_DB_COUNT) @@ -3116,6 +3116,6 @@ static device_method_t ntb_intel_methods[] = { static DEFINE_CLASS_0(ntb_hw, ntb_intel_driver, ntb_intel_methods, sizeof(struct ntb_softc)); -DRIVER_MODULE(ntb_intel, pci, ntb_intel_driver, ntb_hw_devclass, NULL, NULL); -MODULE_DEPEND(ntb_intel, ntb, 1, 1, 1); -MODULE_VERSION(ntb_intel, 1); +DRIVER_MODULE(ntb_hw_intel, pci, ntb_intel_driver, ntb_hw_devclass, NULL, NULL); +MODULE_DEPEND(ntb_hw_intel, ntb, 1, 1, 1); +MODULE_VERSION(ntb_hw_intel, 1); diff --git a/sys/dev/ntb/ntb_hw/ntb_regs.h b/sys/dev/ntb/ntb_hw/ntb_hw_intel.h index a03773627aa3..a03773627aa3 100644 --- a/sys/dev/ntb/ntb_hw/ntb_regs.h +++ b/sys/dev/ntb/ntb_hw/ntb_hw_intel.h diff --git a/sys/dev/ntb/ntb_hw/ntb_hw_plx.c b/sys/dev/ntb/ntb_hw/ntb_hw_plx.c new file mode 100644 index 000000000000..bc02e9e4413a --- /dev/null +++ b/sys/dev/ntb/ntb_hw/ntb_hw_plx.c @@ -0,0 +1,950 @@ +/*- + * Copyright (c) 2017 Alexander Motin <mav@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * The Non-Transparent Bridge (NTB) is a device that allows you to connect + * two or more systems using a PCI-e links, providing remote memory access. + * + * This module contains a driver for NTBs in PLX/Avago/Broadcom PCIe bridges. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/interrupt.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/sysctl.h> +#include <vm/vm.h> +#include <vm/pmap.h> +#include <machine/bus.h> +#include <machine/intr_machdep.h> +#include <machine/resource.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include "../ntb.h" + +#define PLX_MAX_BARS 4 /* There are at most 4 data BARs. */ +#define PLX_NUM_SPAD 8 /* There are 8 scratchpads. */ +#define PLX_NUM_SPAD_PATT 4 /* Use test pattern as 4 more. */ +#define PLX_NUM_DB 16 /* There are 16 doorbells. */ + +struct ntb_plx_mw_info { + int mw_bar; + int mw_64bit; + int mw_rid; + struct resource *mw_res; + vm_paddr_t mw_pbase; + caddr_t mw_vbase; + vm_size_t mw_size; + vm_memattr_t mw_map_mode; + bus_addr_t mw_xlat_addr; + size_t mw_xlat_size; +}; + +struct ntb_plx_softc { + /* ntb.c context. Do not move! Must go first! */ + void *ntb_store; + + device_t dev; + struct resource *conf_res; + int conf_rid; + u_int ntx; /* NTx number within chip. */ + u_int link; /* Link v/s Virtual side. */ + u_int port; /* Port number within chip. */ + + int int_rid; + struct resource *int_res; + void *int_tag; + + struct ntb_plx_mw_info mw_info[PLX_MAX_BARS]; + int mw_count; /* Number of memory windows. */ + + int spad_count1; /* Number of standard spads. */ + int spad_count2; /* Number of extra spads. */ + uint32_t spad_off1; /* Offset of our spads. */ + uint32_t spad_off2; /* Offset of our extra spads. */ + uint32_t spad_offp1; /* Offset of peer spads. */ + uint32_t spad_offp2; /* Offset of peer extra spads. */ + + /* Parameters of window shared with peer config access in B2B mode. */ + int b2b_mw; /* Shared window number. */ + uint64_t b2b_off; /* Offset in shared window. */ +}; + +#define PLX_NT0_BASE 0x3E000 +#define PLX_NT1_BASE 0x3C000 +#define PLX_NTX_BASE(sc) ((sc)->ntx ? PLX_NT1_BASE : PLX_NT0_BASE) +#define PLX_NTX_LINK_OFFSET 0x01000 + +/* Bases of NTx our/peer interface registers */ +#define PLX_NTX_OUR_BASE(sc) \ + (PLX_NTX_BASE(sc) + ((sc)->link ? PLX_NTX_LINK_OFFSET : 0)) +#define PLX_NTX_PEER_BASE(sc) \ + (PLX_NTX_BASE(sc) + ((sc)->link ? 0 : PLX_NTX_LINK_OFFSET)) + +/* Read/write NTx our interface registers */ +#define NTX_READ(sc, reg) \ + bus_read_4((sc)->conf_res, PLX_NTX_OUR_BASE(sc) + (reg)) +#define NTX_WRITE(sc, reg, val) \ + bus_write_4((sc)->conf_res, PLX_NTX_OUR_BASE(sc) + (reg), (val)) + +/* Read/write NTx peer interface registers */ +#define PNTX_READ(sc, reg) \ + bus_read_4((sc)->conf_res, PLX_NTX_PEER_BASE(sc) + (reg)) +#define PNTX_WRITE(sc, reg, val) \ + bus_write_4((sc)->conf_res, PLX_NTX_PEER_BASE(sc) + (reg), (val)) + +/* Read/write B2B NTx registers */ +#define BNTX_READ(sc, reg) \ + bus_read_4((sc)->mw_info[(sc)->b2b_mw].mw_res, \ + PLX_NTX_BASE(sc) + (reg)) +#define BNTX_WRITE(sc, reg, val) \ + bus_write_4((sc)->mw_info[(sc)->b2b_mw].mw_res, \ + PLX_NTX_BASE(sc) + (reg), (val)) + +#define PLX_PORT_BASE(p) ((p) << 12) +#define PLX_STATION_PORT_BASE(sc) PLX_PORT_BASE((sc)->port & ~7) + +#define PLX_PORT_CONTROL(sc) (PLX_STATION_PORT_BASE(sc) + 0x208) + +static int ntb_plx_init(device_t dev); +static int ntb_plx_detach(device_t dev); +static int ntb_plx_mw_set_trans_internal(device_t dev, unsigned mw_idx); + +static int +ntb_plx_probe(device_t dev) +{ + + switch (pci_get_devid(dev)) { + case 0x87a010b5: + device_set_desc(dev, "PLX Non-Transparent Bridge NT0 Link"); + return (BUS_PROBE_DEFAULT); + case 0x87a110b5: + device_set_desc(dev, "PLX Non-Transparent Bridge NT1 Link"); + return (BUS_PROBE_DEFAULT); + case 0x87b010b5: + device_set_desc(dev, "PLX Non-Transparent Bridge NT0 Virtual"); + return (BUS_PROBE_DEFAULT); + case 0x87b110b5: + device_set_desc(dev, "PLX Non-Transparent Bridge NT1 Virtual"); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +ntb_plx_init(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + struct ntb_plx_mw_info *mw; + uint64_t val64; + int i; + uint32_t val; + + if (sc->b2b_mw >= 0) { + /* Set peer BAR0/1 size and address for B2B NTx access. */ + mw = &sc->mw_info[sc->b2b_mw]; + if (mw->mw_64bit) { + PNTX_WRITE(sc, 0xe4, 0x3); /* 64-bit */ + val64 = 0x2000000000000000 * mw->mw_bar | 0x4; + PNTX_WRITE(sc, PCIR_BAR(0), val64); + PNTX_WRITE(sc, PCIR_BAR(0) + 4, val64 >> 32); + } else { + PNTX_WRITE(sc, 0xe4, 0x2); /* 32-bit */ + val = 0x20000000 * mw->mw_bar; + PNTX_WRITE(sc, PCIR_BAR(0), val); + } + + /* Set Virtual to Link address translation for B2B. */ + for (i = 0; i < sc->mw_count; i++) { + mw = &sc->mw_info[i]; + if (mw->mw_64bit) { + val64 = 0x2000000000000000 * mw->mw_bar; + NTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4, val64); + NTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4 + 4, val64 >> 32); + } else { + val = 0x20000000 * mw->mw_bar; + NTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4, val); + } + } + + /* Enable Link Interface LUT entry 0 for 0:0.0. */ + PNTX_WRITE(sc, 0xdb4, 1); + } + + /* + * Enable Virtual Interface LUT entry 0 for 0:0.0 and + * entry 1 for our Requester ID reported by chip. + */ + val = (NTX_READ(sc, 0xc90) << 16) | 0x00010001; + NTX_WRITE(sc, sc->link ? 0xdb4 : 0xd94, val); + + /* Set Link to Virtual address translation. */ + for (i = 0; i < sc->mw_count; i++) { + mw = &sc->mw_info[i]; + if (mw->mw_xlat_size != 0) + ntb_plx_mw_set_trans_internal(dev, i); + } + + pci_enable_busmaster(dev); + if (sc->b2b_mw >= 0) + PNTX_WRITE(sc, PCIR_COMMAND, PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); + + return (0); +} + +static void +ntb_plx_isr(void *arg) +{ + device_t dev = arg; + struct ntb_plx_softc *sc = device_get_softc(dev); + uint32_t val; + + ntb_db_event((device_t)arg, 0); + + if (sc->link) /* Link Interface has no Link Error registers. */ + return; + + val = NTX_READ(sc, 0xfe0); + if (val == 0) + return; + NTX_WRITE(sc, 0xfe0, val); + if (val & 1) + device_printf(dev, "Correctable Error\n"); + if (val & 2) + device_printf(dev, "Uncorrectable Error\n"); + if (val & 4) { + /* DL_Down resets link side registers, have to reinit. */ + ntb_plx_init(dev); + ntb_link_event(dev); + } + if (val & 8) + device_printf(dev, "Uncorrectable Error Message Drop\n"); +} + +static int +ntb_plx_setup_intr(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + int error; + + /* + * XXX: This hardware supports MSI, but I found it unusable. + * It generates new MSI only when doorbell register goes from + * zero, but does not generate it when another bit is set or on + * partial clear. It makes operation very racy and unreliable. + * The data book mentions some mask juggling magic to workaround + * that, but I failed to make it work. + */ + sc->int_rid = 0; + sc->int_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->int_rid, RF_SHAREABLE|RF_ACTIVE); + if (sc->int_res == NULL) { + device_printf(dev, "bus_alloc_resource failed\n"); + return (ENOMEM); + } + error = bus_setup_intr(dev, sc->int_res, INTR_MPSAFE | INTR_TYPE_MISC, + NULL, ntb_plx_isr, dev, &sc->int_tag); + if (error != 0) { + device_printf(dev, "bus_setup_intr failed: %d\n", error); + return (error); + } + + if (!sc->link) { /* Link Interface has no Link Error registers. */ + NTX_WRITE(sc, 0xfe0, 0xf); /* Clear link interrupts. */ + NTX_WRITE(sc, 0xfe4, 0x0); /* Unmask link interrupts. */ + } + return (0); +} + +static void +ntb_plx_teardown_intr(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + + if (!sc->link) /* Link Interface has no Link Error registers. */ + NTX_WRITE(sc, 0xfe4, 0xf); /* Mask link interrupts. */ + + if (sc->int_res) { + bus_teardown_intr(dev, sc->int_res, sc->int_tag); + bus_release_resource(dev, SYS_RES_IRQ, sc->int_rid, + sc->int_res); + } +} + +static int +ntb_plx_attach(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + struct ntb_plx_mw_info *mw; + int error = 0, i; + uint32_t val; + char buf[32]; + + /* Identify what we are (what side of what NTx). */ + sc->dev = dev; + val = pci_read_config(dev, 0xc8c, 4); + sc->ntx = (val & 1) != 0; + sc->link = (val & 0x80000000) != 0; + + /* Get access to whole 256KB of chip configuration space via BAR0/1. */ + sc->conf_rid = PCIR_BAR(0); + sc->conf_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->conf_rid, RF_ACTIVE); + if (sc->conf_res == NULL) { + device_printf(dev, "Can't allocate configuration BAR.\n"); + return (ENXIO); + } + + /* Identify chip port we are connected to. */ + val = bus_read_4(sc->conf_res, 0x360); + sc->port = (val >> ((sc->ntx == 0) ? 8 : 16)) & 0x1f; + + /* Find configured memory windows at BAR2-5. */ + sc->mw_count = 0; + for (i = 2; i <= 5; i++) { + mw = &sc->mw_info[sc->mw_count]; + mw->mw_bar = i; + mw->mw_rid = PCIR_BAR(mw->mw_bar); + mw->mw_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &mw->mw_rid, RF_ACTIVE); + if (mw->mw_res == NULL) + continue; + mw->mw_pbase = rman_get_start(mw->mw_res); + mw->mw_size = rman_get_size(mw->mw_res); + mw->mw_vbase = rman_get_virtual(mw->mw_res); + mw->mw_map_mode = VM_MEMATTR_UNCACHEABLE; + sc->mw_count++; + + /* Skip over adjacent BAR for 64-bit BARs. */ + val = pci_read_config(dev, PCIR_BAR(mw->mw_bar), 4); + if ((val & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) { + mw->mw_64bit = 1; + i++; + } + } + + /* Try to identify B2B mode. */ + i = 1; + snprintf(buf, sizeof(buf), "hint.%s.%d.b2b", device_get_name(dev), + device_get_unit(dev)); + TUNABLE_INT_FETCH(buf, &i); + if (sc->link) { + device_printf(dev, "NTB-to-Root Port mode (Link Interface)\n"); + sc->b2b_mw = -1; + } else if (i == 0) { + device_printf(dev, "NTB-to-Root Port mode (Virtual Interface)\n"); + sc->b2b_mw = -1; + } else { + device_printf(dev, "NTB-to-NTB (back-to-back) mode\n"); + + /* We need at least one memory window for B2B peer access. */ + if (sc->mw_count == 0) { + device_printf(dev, "No memory window BARs enabled.\n"); + error = ENXIO; + goto out; + } + sc->b2b_mw = sc->mw_count - 1; + + /* Use half of the window for B2B, but no less then 1MB. */ + mw = &sc->mw_info[sc->b2b_mw]; + if (mw->mw_size >= 2 * 1024 * 1024) + sc->b2b_off = mw->mw_size / 2; + else + sc->b2b_off = 0; + } + + /* + * Use Physical Layer User Test Pattern as additional scratchpad. + * Make sure they are present and enabled by writing to them. + * XXX: Its a hack, but standard 8 registers are not enough. + */ + sc->spad_offp1 = sc->spad_off1 = PLX_NTX_OUR_BASE(sc) + 0xc6c; + sc->spad_offp2 = sc->spad_off2 = PLX_PORT_BASE(sc->ntx * 8) + 0x20c; + if (sc->b2b_mw >= 0) { + /* In NTB-to-NTB mode each side has own scratchpads. */ + sc->spad_count1 = PLX_NUM_SPAD; + bus_write_4(sc->conf_res, sc->spad_off2, 0x12345678); + if (bus_read_4(sc->conf_res, sc->spad_off2) == 0x12345678) + sc->spad_count2 = PLX_NUM_SPAD_PATT; + } else { + /* Otherwise we have share scratchpads with the peer. */ + if (sc->link) { + sc->spad_off1 += PLX_NUM_SPAD / 2 * 4; + sc->spad_off2 += PLX_NUM_SPAD_PATT / 2 * 4; + } else { + sc->spad_offp1 += PLX_NUM_SPAD / 2 * 4; + sc->spad_offp2 += PLX_NUM_SPAD_PATT / 2 * 4; + } + sc->spad_count1 = PLX_NUM_SPAD / 2; + bus_write_4(sc->conf_res, sc->spad_off2, 0x12345678); + if (bus_read_4(sc->conf_res, sc->spad_off2) == 0x12345678) + sc->spad_count2 = PLX_NUM_SPAD_PATT / 2; + } + + /* Apply static part of NTB configuration. */ + ntb_plx_init(dev); + + /* Allocate and setup interrupts. */ + error = ntb_plx_setup_intr(dev); + if (error) + goto out; + + /* Attach children to this controller */ + error = ntb_register_device(dev); + +out: + if (error != 0) + ntb_plx_detach(dev); + return (error); +} + +static int +ntb_plx_detach(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + struct ntb_plx_mw_info *mw; + int i; + + /* Detach & delete all children */ + ntb_unregister_device(dev); + + /* Disable and free interrupts. */ + ntb_plx_teardown_intr(dev); + + /* Free memory resources. */ + for (i = 0; i < sc->mw_count; i++) { + mw = &sc->mw_info[i]; + bus_release_resource(dev, SYS_RES_MEMORY, mw->mw_rid, + mw->mw_res); + } + bus_release_resource(dev, SYS_RES_MEMORY, sc->conf_rid, sc->conf_res); + return (0); +} + + +static bool +ntb_plx_link_is_up(device_t dev, enum ntb_speed *speed, enum ntb_width *width) +{ + uint16_t link; + + link = pcie_read_config(dev, PCIER_LINK_STA, 2); + if (speed != NULL) + *speed = (link & PCIEM_LINK_STA_SPEED); + if (width != NULL) + *width = (link & PCIEM_LINK_STA_WIDTH) >> 4; + return ((link & PCIEM_LINK_STA_WIDTH) != 0); +} + +static int +ntb_plx_link_enable(device_t dev, enum ntb_speed speed __unused, + enum ntb_width width __unused) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + uint32_t reg, val; + + /* The fact that we see the Link Interface means link is enabled. */ + if (sc->link) { + ntb_link_event(dev); + return (0); + } + + reg = PLX_PORT_CONTROL(sc); + val = bus_read_4(sc->conf_res, reg); + if ((val & (1 << (sc->port & 7))) == 0) { + /* If already enabled, generate fake link event and exit. */ + ntb_link_event(dev); + return (0); + } + val &= ~(1 << (sc->port & 7)); + bus_write_4(sc->conf_res, reg, val); + return (0); +} + +static int +ntb_plx_link_disable(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + uint32_t reg, val; + + /* Link disable for Link Interface would be suicidal. */ + if (sc->link) + return (0); + + reg = PLX_PORT_CONTROL(sc); + val = bus_read_4(sc->conf_res, reg); + val |= (1 << (sc->port & 7)); + bus_write_4(sc->conf_res, reg, val); + return (0); +} + +static bool +ntb_plx_link_enabled(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + uint32_t reg, val; + + /* The fact that we see the Link Interface means link is enabled. */ + if (sc->link) + return (TRUE); + + reg = PLX_PORT_CONTROL(sc); + val = bus_read_4(sc->conf_res, reg); + return ((val & (1 << (sc->port & 7))) == 0); +} + +static uint8_t +ntb_plx_mw_count(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + + if (sc->b2b_mw >= 0 && sc->b2b_off == 0) + return (sc->mw_count - 1); /* B2B consumed whole window. */ + return (sc->mw_count); +} + +static int +ntb_plx_mw_get_range(device_t dev, unsigned mw_idx, vm_paddr_t *base, + caddr_t *vbase, size_t *size, size_t *align, size_t *align_size, + bus_addr_t *plimit) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + struct ntb_plx_mw_info *mw; + size_t off; + + if (mw_idx >= sc->mw_count) + return (EINVAL); + off = 0; + if (mw_idx == sc->b2b_mw) { + KASSERT(sc->b2b_off != 0, + ("user shouldn't get non-shared b2b mw")); + off = sc->b2b_off; + } + mw = &sc->mw_info[mw_idx]; + + /* Local to remote memory window parameters. */ + if (base != NULL) + *base = mw->mw_pbase + off; + if (vbase != NULL) + *vbase = mw->mw_vbase + off; + if (size != NULL) + *size = mw->mw_size - off; + + /* + * Remote to local memory window translation address alignment. + * XXX: In B2B mode we can change window size (and so alignmet) + * live, but there is no way to report it, so report safe value. + */ + if (align != NULL) + *align = mw->mw_size - off; + + /* + * Remote to local memory window size alignment. + * XXX: The chip has no limit registers. In B2B case size must be + * power of 2 (since we can reprogram BAR size), but there is no way + * to report it, so report 1MB -- minimal BAR size. In non-B2B case + * there is no control at all, so report the precofigured BAR size. + */ + if (align_size != NULL) { + if (sc->b2b_mw >= 0) + *align_size = 1024 * 1024; + else + *align_size = mw->mw_size - off; + } + + /* Remote to local memory window translation address upper limit. */ + if (plimit != NULL) + *plimit = mw->mw_64bit ? BUS_SPACE_MAXADDR : + BUS_SPACE_MAXADDR_32BIT; + return (0); +} + +static int +ntb_plx_mw_set_trans_internal(device_t dev, unsigned mw_idx) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + struct ntb_plx_mw_info *mw; + uint64_t addr, off, size, val64; + uint32_t val; + + mw = &sc->mw_info[mw_idx]; + addr = mw->mw_xlat_addr; + size = mw->mw_xlat_size; + off = 0; + if (mw_idx == sc->b2b_mw) { + off = sc->b2b_off; + KASSERT(off != 0, ("user shouldn't get non-shared b2b mw")); + + /* + * While generally we can set any BAR size on link side, + * for B2B shared window we can't go above preconfigured + * size due to BAR address alignment requirements. + */ + if (size > mw->mw_size - off) + return (EINVAL); + } + + if (size > 0) { + /* Round BAR size to next power of 2 or at least 1MB. */ + if (!powerof2(size)) + size = 1LL << flsll(size); + if (size < 1024 * 1024) + size = 1024 * 1024; + + /* Hardware requires addr aligned to BAR size. */ + if ((addr & (size - 1)) != 0) + return (EINVAL); + } + + if (mw->mw_64bit) { + if (sc->b2b_mw >= 0) { + /* Set Link Interface BAR size and enable/disable it. */ + val64 = 0; + if (size > 0) + val64 = (~(size - 1) & ~0xfffff); + val64 |= 0x4; + PNTX_WRITE(sc, 0xe8 + (mw->mw_bar - 2) * 4, val64); + PNTX_WRITE(sc, 0xe8 + (mw->mw_bar - 2) * 4 + 4, val64 >> 32); + + /* Set Link Interface BAR address. */ + val64 = 0x2000000000000000 * mw->mw_bar + off; + val64 |= 0x4; + PNTX_WRITE(sc, PCIR_BAR(mw->mw_bar), val64); + PNTX_WRITE(sc, PCIR_BAR(mw->mw_bar) + 4, val64 >> 32); + } + + /* Set Virtual Interface BARs address translation */ + PNTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4, addr); + PNTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4 + 4, addr >> 32); + } else { + /* Make sure we fit into 32-bit address space. */ + if ((addr & UINT32_MAX) != addr) + return (ERANGE); + if (((addr + size) & UINT32_MAX) != (addr + size)) + return (ERANGE); + + if (sc->b2b_mw >= 0) { + /* Set Link Interface BAR size and enable/disable it. */ + val = 0; + if (size > 0) + val = (~(size - 1) & ~0xfffff); + PNTX_WRITE(sc, 0xe8 + (mw->mw_bar - 2) * 4, val); + + /* Set Link Interface BAR address. */ + val64 = 0x20000000 * mw->mw_bar + off; + PNTX_WRITE(sc, PCIR_BAR(mw->mw_bar), val64); + } + + /* Set Virtual Interface BARs address translation */ + PNTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4, addr); + } + return (0); +} + +static int +ntb_plx_mw_set_trans(device_t dev, unsigned mw_idx, bus_addr_t addr, size_t size) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + struct ntb_plx_mw_info *mw; + + if (mw_idx >= sc->mw_count) + return (EINVAL); + mw = &sc->mw_info[mw_idx]; + mw->mw_xlat_addr = addr; + mw->mw_xlat_size = size; + return (ntb_plx_mw_set_trans_internal(dev, mw_idx)); +} + +static int +ntb_plx_mw_clear_trans(device_t dev, unsigned mw_idx) +{ + + return (ntb_plx_mw_set_trans(dev, mw_idx, 0, 0)); +} + +static int +ntb_plx_mw_get_wc(device_t dev, unsigned idx, vm_memattr_t *mode) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + struct ntb_plx_mw_info *mw; + + if (idx >= sc->mw_count) + return (EINVAL); + mw = &sc->mw_info[idx]; + *mode = mw->mw_map_mode; + return (0); +} + +static int +ntb_plx_mw_set_wc(device_t dev, unsigned idx, vm_memattr_t mode) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + struct ntb_plx_mw_info *mw; + uint64_t off; + int rc; + + if (idx >= sc->mw_count) + return (EINVAL); + mw = &sc->mw_info[idx]; + if (mw->mw_map_mode == mode) + return (0); + + off = 0; + if (idx == sc->b2b_mw) { + KASSERT(sc->b2b_off != 0, + ("user shouldn't get non-shared b2b mw")); + off = sc->b2b_off; + } + + rc = pmap_change_attr((vm_offset_t)mw->mw_vbase + off, + mw->mw_size - off, mode); + if (rc == 0) + mw->mw_map_mode = mode; + return (rc); +} + +static uint8_t +ntb_plx_spad_count(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + + return (sc->spad_count1 + sc->spad_count2); +} + +static int +ntb_plx_spad_write(device_t dev, unsigned int idx, uint32_t val) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + u_int off; + + if (idx >= sc->spad_count1 + sc->spad_count2) + return (EINVAL); + + if (idx < sc->spad_count1) + off = sc->spad_off1 + idx * 4; + else + off = sc->spad_off2 + (idx - sc->spad_count1) * 4; + bus_write_4(sc->conf_res, off, val); + return (0); +} + +static void +ntb_plx_spad_clear(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + int i; + + for (i = 0; i < sc->spad_count1 + sc->spad_count2; i++) + ntb_plx_spad_write(dev, i, 0); +} + +static int +ntb_plx_spad_read(device_t dev, unsigned int idx, uint32_t *val) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + u_int off; + + if (idx >= sc->spad_count1 + sc->spad_count2) + return (EINVAL); + + if (idx < sc->spad_count1) + off = sc->spad_off1 + idx * 4; + else + off = sc->spad_off2 + (idx - sc->spad_count1) * 4; + *val = bus_read_4(sc->conf_res, off); + return (0); +} + +static int +ntb_plx_peer_spad_write(device_t dev, unsigned int idx, uint32_t val) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + u_int off; + + if (idx >= sc->spad_count1 + sc->spad_count2) + return (EINVAL); + + if (idx < sc->spad_count1) + off = sc->spad_offp1 + idx * 4; + else + off = sc->spad_offp2 + (idx - sc->spad_count1) * 4; + if (sc->b2b_mw >= 0) + bus_write_4(sc->mw_info[sc->b2b_mw].mw_res, off, val); + else + bus_write_4(sc->conf_res, off, val); + return (0); +} + +static int +ntb_plx_peer_spad_read(device_t dev, unsigned int idx, uint32_t *val) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + u_int off; + + if (idx >= sc->spad_count1 + sc->spad_count2) + return (EINVAL); + + if (idx < sc->spad_count1) + off = sc->spad_offp1 + idx * 4; + else + off = sc->spad_offp2 + (idx - sc->spad_count1) * 4; + if (sc->b2b_mw >= 0) + *val = bus_read_4(sc->mw_info[sc->b2b_mw].mw_res, off); + else + *val = bus_read_4(sc->conf_res, off); + return (0); +} + +static uint64_t +ntb_plx_db_valid_mask(device_t dev) +{ + + return ((1LL << PLX_NUM_DB) - 1); +} + +static int +ntb_plx_db_vector_count(device_t dev) +{ + + return (1); +} + +static uint64_t +ntb_plx_db_vector_mask(device_t dev, uint32_t vector) +{ + + if (vector > 0) + return (0); + return ((1LL << PLX_NUM_DB) - 1); +} + +static void +ntb_plx_db_clear(device_t dev, uint64_t bits) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + + NTX_WRITE(sc, sc->link ? 0xc60 : 0xc50, bits); +} + +static void +ntb_plx_db_clear_mask(device_t dev, uint64_t bits) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + + NTX_WRITE(sc, sc->link ? 0xc68 : 0xc58, bits); +} + +static uint64_t +ntb_plx_db_read(device_t dev) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + + return (NTX_READ(sc, sc->link ? 0xc5c : 0xc4c)); +} + +static void +ntb_plx_db_set_mask(device_t dev, uint64_t bits) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + + NTX_WRITE(sc, sc->link ? 0xc64 : 0xc54, bits); +} + +static int +ntb_plx_peer_db_addr(device_t dev, bus_addr_t *db_addr, vm_size_t *db_size) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + struct ntb_plx_mw_info *mw; + + KASSERT((db_addr != NULL && db_size != NULL), ("must be non-NULL")); + + if (sc->b2b_mw >= 0) { + mw = &sc->mw_info[sc->b2b_mw]; + *db_addr = (uint64_t)mw->mw_pbase + PLX_NTX_BASE(sc) + 0xc4c; + } else { + *db_addr = rman_get_start(sc->conf_res) + PLX_NTX_BASE(sc); + *db_addr += sc->link ? 0xc4c : 0xc5c; + } + *db_size = 4; + return (0); +} + +static void +ntb_plx_peer_db_set(device_t dev, uint64_t bit) +{ + struct ntb_plx_softc *sc = device_get_softc(dev); + + if (sc->b2b_mw >= 0) + BNTX_WRITE(sc, 0xc4c, bit); + else + NTX_WRITE(sc, sc->link ? 0xc4c : 0xc5c, bit); +} + +static device_method_t ntb_plx_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ntb_plx_probe), + DEVMETHOD(device_attach, ntb_plx_attach), + DEVMETHOD(device_detach, ntb_plx_detach), + /* NTB interface */ + DEVMETHOD(ntb_link_is_up, ntb_plx_link_is_up), + DEVMETHOD(ntb_link_enable, ntb_plx_link_enable), + DEVMETHOD(ntb_link_disable, ntb_plx_link_disable), + DEVMETHOD(ntb_link_enabled, ntb_plx_link_enabled), + DEVMETHOD(ntb_mw_count, ntb_plx_mw_count), + DEVMETHOD(ntb_mw_get_range, ntb_plx_mw_get_range), + DEVMETHOD(ntb_mw_set_trans, ntb_plx_mw_set_trans), + DEVMETHOD(ntb_mw_clear_trans, ntb_plx_mw_clear_trans), + DEVMETHOD(ntb_mw_get_wc, ntb_plx_mw_get_wc), + DEVMETHOD(ntb_mw_set_wc, ntb_plx_mw_set_wc), + DEVMETHOD(ntb_spad_count, ntb_plx_spad_count), + DEVMETHOD(ntb_spad_clear, ntb_plx_spad_clear), + DEVMETHOD(ntb_spad_write, ntb_plx_spad_write), + DEVMETHOD(ntb_spad_read, ntb_plx_spad_read), + DEVMETHOD(ntb_peer_spad_write, ntb_plx_peer_spad_write), + DEVMETHOD(ntb_peer_spad_read, ntb_plx_peer_spad_read), + DEVMETHOD(ntb_db_valid_mask, ntb_plx_db_valid_mask), + DEVMETHOD(ntb_db_vector_count, ntb_plx_db_vector_count), + DEVMETHOD(ntb_db_vector_mask, ntb_plx_db_vector_mask), + DEVMETHOD(ntb_db_clear, ntb_plx_db_clear), + DEVMETHOD(ntb_db_clear_mask, ntb_plx_db_clear_mask), + DEVMETHOD(ntb_db_read, ntb_plx_db_read), + DEVMETHOD(ntb_db_set_mask, ntb_plx_db_set_mask), + DEVMETHOD(ntb_peer_db_addr, ntb_plx_peer_db_addr), + DEVMETHOD(ntb_peer_db_set, ntb_plx_peer_db_set), + DEVMETHOD_END +}; + +static DEFINE_CLASS_0(ntb_hw, ntb_plx_driver, ntb_plx_methods, + sizeof(struct ntb_plx_softc)); +DRIVER_MODULE(ntb_hw_plx, pci, ntb_plx_driver, ntb_hw_devclass, NULL, NULL); +MODULE_DEPEND(ntb_hw_plx, ntb, 1, 1, 1); +MODULE_VERSION(ntb_hw_plx, 1); |
