1323032Smav/*-
2355041Smav * Copyright (c) 2017-2019 Alexander Motin <mav@FreeBSD.org>
3323032Smav * All rights reserved.
4323032Smav *
5323032Smav * Redistribution and use in source and binary forms, with or without
6323032Smav * modification, are permitted provided that the following conditions
7323032Smav * are met:
8323032Smav * 1. Redistributions of source code must retain the above copyright
9323032Smav *    notice, this list of conditions and the following disclaimer.
10323032Smav * 2. Redistributions in binary form must reproduce the above copyright
11323032Smav *    notice, this list of conditions and the following disclaimer in the
12323032Smav *    documentation and/or other materials provided with the distribution.
13323032Smav *
14323032Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15323032Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16323032Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17323032Smav * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18323032Smav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19323032Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20323032Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21323032Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22323032Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23323032Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24323032Smav * SUCH DAMAGE.
25323032Smav */
26323032Smav
27323032Smav/*
28323032Smav * The Non-Transparent Bridge (NTB) is a device that allows you to connect
29323032Smav * two or more systems using a PCI-e links, providing remote memory access.
30323032Smav *
31323032Smav * This module contains a driver for NTBs in PLX/Avago/Broadcom PCIe bridges.
32323032Smav */
33323032Smav
34323032Smav#include <sys/cdefs.h>
35323032Smav__FBSDID("$FreeBSD: stable/11/sys/dev/ntb/ntb_hw/ntb_hw_plx.c 355152 2019-11-28 00:41:42Z mav $");
36323032Smav
37323032Smav#include <sys/param.h>
38323032Smav#include <sys/kernel.h>
39323032Smav#include <sys/systm.h>
40323032Smav#include <sys/bus.h>
41323032Smav#include <sys/interrupt.h>
42323032Smav#include <sys/module.h>
43323032Smav#include <sys/rman.h>
44323032Smav#include <sys/sysctl.h>
45323032Smav#include <vm/vm.h>
46323032Smav#include <vm/pmap.h>
47323032Smav#include <machine/bus.h>
48323032Smav#include <machine/intr_machdep.h>
49323032Smav#include <machine/resource.h>
50323032Smav#include <dev/pci/pcireg.h>
51323032Smav#include <dev/pci/pcivar.h>
52323032Smav
53323032Smav#include "../ntb.h"
54323032Smav
55323032Smav#define PLX_MAX_BARS		4	/* There are at most 4 data BARs. */
56323032Smav#define PLX_NUM_SPAD		8	/* There are 8 scratchpads. */
57323032Smav#define PLX_NUM_SPAD_PATT	4	/* Use test pattern as 4 more. */
58323032Smav#define PLX_NUM_DB		16	/* There are 16 doorbells. */
59355041Smav#define PLX_MAX_SPLIT		128	/* Allow are at most 128 splits. */
60323032Smav
61323032Smavstruct ntb_plx_mw_info {
62323032Smav	int			 mw_bar;
63323032Smav	int			 mw_64bit;
64323032Smav	int			 mw_rid;
65323032Smav	struct resource		*mw_res;
66323032Smav	vm_paddr_t		 mw_pbase;
67323032Smav	caddr_t			 mw_vbase;
68323032Smav	vm_size_t		 mw_size;
69355041Smav	struct {
70355041Smav		vm_memattr_t	 mw_map_mode;
71355041Smav		bus_addr_t	 mw_xlat_addr;
72355041Smav		bus_size_t	 mw_xlat_size;
73355041Smav	} splits[PLX_MAX_SPLIT];
74323032Smav};
75323032Smav
76323032Smavstruct ntb_plx_softc {
77323032Smav	/* ntb.c context. Do not move! Must go first! */
78323032Smav	void			*ntb_store;
79323032Smav
80323032Smav	device_t		 dev;
81323032Smav	struct resource		*conf_res;
82323032Smav	int			 conf_rid;
83323032Smav	u_int			 ntx;		/* NTx number within chip. */
84323032Smav	u_int			 link;		/* Link v/s Virtual side. */
85323032Smav	u_int			 port;		/* Port number within chip. */
86324407Smav	u_int			 alut;		/* A-LUT is enabled for NTx */
87355041Smav	u_int			 split;		/* split BAR2 into 2^x parts */
88323032Smav
89323032Smav	int			 int_rid;
90323032Smav	struct resource		*int_res;
91323032Smav	void			*int_tag;
92323032Smav
93323032Smav	struct ntb_plx_mw_info	 mw_info[PLX_MAX_BARS];
94323032Smav	int			 mw_count;	/* Number of memory windows. */
95323032Smav
96323032Smav	int			 spad_count1;	/* Number of standard spads. */
97323032Smav	int			 spad_count2;	/* Number of extra spads. */
98323032Smav	uint32_t		 spad_off1;	/* Offset of our spads. */
99323032Smav	uint32_t		 spad_off2;	/* Offset of our extra spads. */
100323032Smav	uint32_t		 spad_offp1;	/* Offset of peer spads. */
101323032Smav	uint32_t		 spad_offp2;	/* Offset of peer extra spads. */
102323032Smav
103323032Smav	/* Parameters of window shared with peer config access in B2B mode. */
104323032Smav	int			 b2b_mw;	/* Shared window number. */
105323032Smav	uint64_t		 b2b_off;	/* Offset in shared window. */
106323032Smav};
107323032Smav
108323032Smav#define	PLX_NT0_BASE		0x3E000
109323032Smav#define	PLX_NT1_BASE		0x3C000
110323032Smav#define	PLX_NTX_BASE(sc)	((sc)->ntx ? PLX_NT1_BASE : PLX_NT0_BASE)
111323032Smav#define	PLX_NTX_LINK_OFFSET	0x01000
112323032Smav
113323032Smav/* Bases of NTx our/peer interface registers */
114323453Smav#define	PLX_NTX_OUR_BASE(sc)				\
115323032Smav    (PLX_NTX_BASE(sc) + ((sc)->link ? PLX_NTX_LINK_OFFSET : 0))
116323453Smav#define	PLX_NTX_PEER_BASE(sc)				\
117323032Smav    (PLX_NTX_BASE(sc) + ((sc)->link ? 0 : PLX_NTX_LINK_OFFSET))
118323032Smav
119323032Smav/* Read/write NTx our interface registers */
120323453Smav#define	NTX_READ(sc, reg)				\
121323453Smav    bus_read_4((sc)->conf_res, PLX_NTX_OUR_BASE(sc) + (reg))
122323453Smav#define	NTX_WRITE(sc, reg, val)				\
123323453Smav    bus_write_4((sc)->conf_res, PLX_NTX_OUR_BASE(sc) + (reg), (val))
124323032Smav
125323032Smav/* Read/write NTx peer interface registers */
126323453Smav#define	PNTX_READ(sc, reg)				\
127323453Smav    bus_read_4((sc)->conf_res, PLX_NTX_PEER_BASE(sc) + (reg))
128323453Smav#define	PNTX_WRITE(sc, reg, val)			\
129323453Smav    bus_write_4((sc)->conf_res, PLX_NTX_PEER_BASE(sc) + (reg), (val))
130323032Smav
131323032Smav/* Read/write B2B NTx registers */
132323453Smav#define	BNTX_READ(sc, reg)				\
133323032Smav    bus_read_4((sc)->mw_info[(sc)->b2b_mw].mw_res,	\
134323032Smav    PLX_NTX_BASE(sc) + (reg))
135323453Smav#define	BNTX_WRITE(sc, reg, val)			\
136323032Smav    bus_write_4((sc)->mw_info[(sc)->b2b_mw].mw_res,	\
137323032Smav    PLX_NTX_BASE(sc) + (reg), (val))
138323032Smav
139323453Smav#define	PLX_PORT_BASE(p)		((p) << 12)
140323453Smav#define	PLX_STATION_PORT_BASE(sc)	PLX_PORT_BASE((sc)->port & ~7)
141323453Smav
142323453Smav#define	PLX_PORT_CONTROL(sc)		(PLX_STATION_PORT_BASE(sc) + 0x208)
143323453Smav
144323032Smavstatic int ntb_plx_init(device_t dev);
145323032Smavstatic int ntb_plx_detach(device_t dev);
146323032Smavstatic int ntb_plx_mw_set_trans_internal(device_t dev, unsigned mw_idx);
147323032Smav
148323032Smavstatic int
149323032Smavntb_plx_probe(device_t dev)
150323032Smav{
151323032Smav
152323032Smav	switch (pci_get_devid(dev)) {
153323032Smav	case 0x87a010b5:
154323032Smav		device_set_desc(dev, "PLX Non-Transparent Bridge NT0 Link");
155323032Smav		return (BUS_PROBE_DEFAULT);
156323032Smav	case 0x87a110b5:
157323032Smav		device_set_desc(dev, "PLX Non-Transparent Bridge NT1 Link");
158323032Smav		return (BUS_PROBE_DEFAULT);
159323032Smav	case 0x87b010b5:
160323032Smav		device_set_desc(dev, "PLX Non-Transparent Bridge NT0 Virtual");
161323032Smav		return (BUS_PROBE_DEFAULT);
162323032Smav	case 0x87b110b5:
163323032Smav		device_set_desc(dev, "PLX Non-Transparent Bridge NT1 Virtual");
164323032Smav		return (BUS_PROBE_DEFAULT);
165323032Smav	}
166323032Smav	return (ENXIO);
167323032Smav}
168323032Smav
169323032Smavstatic int
170323032Smavntb_plx_init(device_t dev)
171323032Smav{
172323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
173323032Smav	struct ntb_plx_mw_info *mw;
174323032Smav	uint64_t val64;
175323032Smav	int i;
176323032Smav	uint32_t val;
177323032Smav
178323032Smav	if (sc->b2b_mw >= 0) {
179323032Smav		/* Set peer BAR0/1 size and address for B2B NTx access. */
180323032Smav		mw = &sc->mw_info[sc->b2b_mw];
181323032Smav		if (mw->mw_64bit) {
182323032Smav			PNTX_WRITE(sc, 0xe4, 0x3);	/* 64-bit */
183323032Smav			val64 = 0x2000000000000000 * mw->mw_bar | 0x4;
184323032Smav			PNTX_WRITE(sc, PCIR_BAR(0), val64);
185323032Smav			PNTX_WRITE(sc, PCIR_BAR(0) + 4, val64 >> 32);
186323032Smav		} else {
187323032Smav			PNTX_WRITE(sc, 0xe4, 0x2);	/* 32-bit */
188323032Smav			val = 0x20000000 * mw->mw_bar;
189323032Smav			PNTX_WRITE(sc, PCIR_BAR(0), val);
190323032Smav		}
191323032Smav
192323032Smav		/* Set Virtual to Link address translation for B2B. */
193323032Smav		for (i = 0; i < sc->mw_count; i++) {
194323032Smav			mw = &sc->mw_info[i];
195323032Smav			if (mw->mw_64bit) {
196323032Smav				val64 = 0x2000000000000000 * mw->mw_bar;
197323032Smav				NTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4, val64);
198323032Smav				NTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4 + 4, val64 >> 32);
199323032Smav			} else {
200323032Smav				val = 0x20000000 * mw->mw_bar;
201323032Smav				NTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4, val);
202323032Smav			}
203323032Smav		}
204323032Smav
205324407Smav		/* Make sure Virtual to Link A-LUT is disabled. */
206324407Smav		if (sc->alut)
207324407Smav			PNTX_WRITE(sc, 0xc94, 0);
208324407Smav
209344652Smav		/* Enable all Link Interface LUT entries for peer. */
210344652Smav		for (i = 0; i < 32; i += 2) {
211344652Smav			PNTX_WRITE(sc, 0xdb4 + i * 2,
212344652Smav			    0x00010001 | ((i + 1) << 19) | (i << 3));
213344652Smav		}
214323032Smav	}
215323032Smav
216323032Smav	/*
217344652Smav	 * Enable Virtual Interface LUT entry 0 for 0:0.*.
218344652Smav	 * entry 1 for our Requester ID reported by the chip,
219344652Smav	 * entries 2-5 for 0/64/128/192:4.* of I/OAT DMA engines.
220344652Smav	 * XXX: Its a hack, we can't know all DMA engines, but this covers all
221344652Smav	 * I/OAT of Xeon E5/E7 at least from Sandy Bridge till Skylake I saw.
222323032Smav	 */
223323032Smav	val = (NTX_READ(sc, 0xc90) << 16) | 0x00010001;
224323032Smav	NTX_WRITE(sc, sc->link ? 0xdb4 : 0xd94, val);
225344652Smav	NTX_WRITE(sc, sc->link ? 0xdb8 : 0xd98, 0x40210021);
226344652Smav	NTX_WRITE(sc, sc->link ? 0xdbc : 0xd9c, 0xc0218021);
227323032Smav
228323032Smav	/* Set Link to Virtual address translation. */
229355041Smav	for (i = 0; i < sc->mw_count; i++)
230355041Smav		ntb_plx_mw_set_trans_internal(dev, i);
231323032Smav
232323032Smav	pci_enable_busmaster(dev);
233323032Smav	if (sc->b2b_mw >= 0)
234323032Smav		PNTX_WRITE(sc, PCIR_COMMAND, PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
235323032Smav
236323032Smav	return (0);
237323032Smav}
238323032Smav
239323032Smavstatic void
240323032Smavntb_plx_isr(void *arg)
241323032Smav{
242323032Smav	device_t dev = arg;
243323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
244323032Smav	uint32_t val;
245323032Smav
246323032Smav	ntb_db_event((device_t)arg, 0);
247323032Smav
248323453Smav	if (sc->link)	/* Link Interface has no Link Error registers. */
249323453Smav		return;
250323453Smav
251323032Smav	val = NTX_READ(sc, 0xfe0);
252323032Smav	if (val == 0)
253323032Smav		return;
254323032Smav	NTX_WRITE(sc, 0xfe0, val);
255323032Smav	if (val & 1)
256323032Smav		device_printf(dev, "Correctable Error\n");
257323032Smav	if (val & 2)
258323032Smav		device_printf(dev, "Uncorrectable Error\n");
259323032Smav	if (val & 4) {
260323032Smav		/* DL_Down resets link side registers, have to reinit. */
261323032Smav		ntb_plx_init(dev);
262323032Smav		ntb_link_event(dev);
263323032Smav	}
264323032Smav	if (val & 8)
265323032Smav		device_printf(dev, "Uncorrectable Error Message Drop\n");
266323032Smav}
267323032Smav
268323032Smavstatic int
269323032Smavntb_plx_setup_intr(device_t dev)
270323032Smav{
271323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
272323032Smav	int error;
273323032Smav
274323032Smav	/*
275323032Smav	 * XXX: This hardware supports MSI, but I found it unusable.
276323032Smav	 * It generates new MSI only when doorbell register goes from
277323032Smav	 * zero, but does not generate it when another bit is set or on
278323032Smav	 * partial clear.  It makes operation very racy and unreliable.
279323032Smav	 * The data book mentions some mask juggling magic to workaround
280323032Smav	 * that, but I failed to make it work.
281323032Smav	 */
282323032Smav	sc->int_rid = 0;
283323032Smav	sc->int_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
284323032Smav	    &sc->int_rid, RF_SHAREABLE|RF_ACTIVE);
285323032Smav	if (sc->int_res == NULL) {
286323032Smav		device_printf(dev, "bus_alloc_resource failed\n");
287323032Smav		return (ENOMEM);
288323032Smav	}
289323032Smav	error = bus_setup_intr(dev, sc->int_res, INTR_MPSAFE | INTR_TYPE_MISC,
290323032Smav	    NULL, ntb_plx_isr, dev, &sc->int_tag);
291323032Smav	if (error != 0) {
292323032Smav		device_printf(dev, "bus_setup_intr failed: %d\n", error);
293323032Smav		return (error);
294323032Smav	}
295323453Smav
296323453Smav	if (!sc->link) { /* Link Interface has no Link Error registers. */
297323453Smav		NTX_WRITE(sc, 0xfe0, 0xf);	/* Clear link interrupts. */
298323453Smav		NTX_WRITE(sc, 0xfe4, 0x0);	/* Unmask link interrupts. */
299323453Smav	}
300323032Smav	return (0);
301323032Smav}
302323032Smav
303323032Smavstatic void
304323032Smavntb_plx_teardown_intr(device_t dev)
305323032Smav{
306323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
307323032Smav
308323453Smav	if (!sc->link)	/* Link Interface has no Link Error registers. */
309323453Smav		NTX_WRITE(sc, 0xfe4, 0xf);	/* Mask link interrupts. */
310323453Smav
311323032Smav	if (sc->int_res) {
312323032Smav		bus_teardown_intr(dev, sc->int_res, sc->int_tag);
313323032Smav		bus_release_resource(dev, SYS_RES_IRQ, sc->int_rid,
314323032Smav		    sc->int_res);
315323032Smav	}
316323032Smav}
317323032Smav
318323032Smavstatic int
319323032Smavntb_plx_attach(device_t dev)
320323032Smav{
321323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
322323032Smav	struct ntb_plx_mw_info *mw;
323355041Smav	int error = 0, i, j;
324323032Smav	uint32_t val;
325323032Smav	char buf[32];
326323032Smav
327323032Smav	/* Identify what we are (what side of what NTx). */
328323032Smav	sc->dev = dev;
329323032Smav	val = pci_read_config(dev, 0xc8c, 4);
330323032Smav	sc->ntx = (val & 1) != 0;
331323032Smav	sc->link = (val & 0x80000000) != 0;
332323032Smav
333323032Smav	/* Get access to whole 256KB of chip configuration space via BAR0/1. */
334323032Smav	sc->conf_rid = PCIR_BAR(0);
335323032Smav	sc->conf_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
336323032Smav	    &sc->conf_rid, RF_ACTIVE);
337323032Smav	if (sc->conf_res == NULL) {
338323032Smav		device_printf(dev, "Can't allocate configuration BAR.\n");
339323032Smav		return (ENXIO);
340323032Smav	}
341323032Smav
342323032Smav	/* Identify chip port we are connected to. */
343323032Smav	val = bus_read_4(sc->conf_res, 0x360);
344323032Smav	sc->port = (val >> ((sc->ntx == 0) ? 8 : 16)) & 0x1f;
345323032Smav
346324407Smav	/* Detect A-LUT enable and size. */
347324407Smav	val >>= 30;
348324407Smav	sc->alut = (val == 0x3) ? 1 : ((val & (1 << sc->ntx)) ? 2 : 0);
349324407Smav	if (sc->alut)
350324407Smav		device_printf(dev, "%u A-LUT entries\n", 128 * sc->alut);
351324407Smav
352323032Smav	/* Find configured memory windows at BAR2-5. */
353323032Smav	sc->mw_count = 0;
354323032Smav	for (i = 2; i <= 5; i++) {
355323032Smav		mw = &sc->mw_info[sc->mw_count];
356323032Smav		mw->mw_bar = i;
357323032Smav		mw->mw_rid = PCIR_BAR(mw->mw_bar);
358323032Smav		mw->mw_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
359323032Smav		    &mw->mw_rid, RF_ACTIVE);
360323032Smav		if (mw->mw_res == NULL)
361323032Smav			continue;
362323032Smav		mw->mw_pbase = rman_get_start(mw->mw_res);
363323032Smav		mw->mw_size = rman_get_size(mw->mw_res);
364323032Smav		mw->mw_vbase = rman_get_virtual(mw->mw_res);
365355041Smav		for (j = 0; j < PLX_MAX_SPLIT; j++)
366355041Smav			mw->splits[j].mw_map_mode = VM_MEMATTR_UNCACHEABLE;
367323032Smav		sc->mw_count++;
368323032Smav
369323032Smav		/* Skip over adjacent BAR for 64-bit BARs. */
370323032Smav		val = pci_read_config(dev, PCIR_BAR(mw->mw_bar), 4);
371323032Smav		if ((val & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) {
372323032Smav			mw->mw_64bit = 1;
373323032Smav			i++;
374323032Smav		}
375323032Smav	}
376323032Smav
377323032Smav	/* Try to identify B2B mode. */
378323032Smav	i = 1;
379323032Smav	snprintf(buf, sizeof(buf), "hint.%s.%d.b2b", device_get_name(dev),
380323032Smav	    device_get_unit(dev));
381323032Smav	TUNABLE_INT_FETCH(buf, &i);
382323032Smav	if (sc->link) {
383323032Smav		device_printf(dev, "NTB-to-Root Port mode (Link Interface)\n");
384323032Smav		sc->b2b_mw = -1;
385323032Smav	} else if (i == 0) {
386323032Smav		device_printf(dev, "NTB-to-Root Port mode (Virtual Interface)\n");
387323032Smav		sc->b2b_mw = -1;
388323032Smav	} else {
389323032Smav		device_printf(dev, "NTB-to-NTB (back-to-back) mode\n");
390323032Smav
391323032Smav		/* We need at least one memory window for B2B peer access. */
392323032Smav		if (sc->mw_count == 0) {
393323032Smav			device_printf(dev, "No memory window BARs enabled.\n");
394323032Smav			error = ENXIO;
395323032Smav			goto out;
396323032Smav		}
397323032Smav		sc->b2b_mw = sc->mw_count - 1;
398323032Smav
399323032Smav		/* Use half of the window for B2B, but no less then 1MB. */
400323032Smav		mw = &sc->mw_info[sc->b2b_mw];
401323032Smav		if (mw->mw_size >= 2 * 1024 * 1024)
402323032Smav			sc->b2b_off = mw->mw_size / 2;
403323032Smav		else
404323032Smav			sc->b2b_off = 0;
405323032Smav	}
406323032Smav
407355041Smav	snprintf(buf, sizeof(buf), "hint.%s.%d.split", device_get_name(dev),
408355041Smav	    device_get_unit(dev));
409355041Smav	TUNABLE_INT_FETCH(buf, &sc->split);
410355041Smav	if (sc->split > 7) {
411355041Smav		device_printf(dev, "Split value is too high (%u)\n", sc->split);
412355041Smav		sc->split = 0;
413355041Smav	} else if (sc->split > 0 && sc->alut == 0) {
414355041Smav		device_printf(dev, "Can't split with disabled A-LUT\n");
415355041Smav		sc->split = 0;
416355041Smav	} else if (sc->split > 0 && (sc->mw_count == 0 || sc->mw_info[0].mw_bar != 2)) {
417355041Smav		device_printf(dev, "Can't split disabled BAR2\n");
418355041Smav		sc->split = 0;
419355041Smav	} else if (sc->split > 0 && (sc->b2b_mw == 0 && sc->b2b_off == 0)) {
420355041Smav		device_printf(dev, "Can't split BAR2 consumed by B2B\n");
421355041Smav		sc->split = 0;
422355041Smav	} else if (sc->split > 0) {
423355041Smav		device_printf(dev, "Splitting BAR2 into %d memory windows\n",
424355041Smav		    1 << sc->split);
425355041Smav	}
426355041Smav
427323032Smav	/*
428323032Smav	 * Use Physical Layer User Test Pattern as additional scratchpad.
429323032Smav	 * Make sure they are present and enabled by writing to them.
430323032Smav	 * XXX: Its a hack, but standard 8 registers are not enough.
431323032Smav	 */
432323453Smav	sc->spad_offp1 = sc->spad_off1 = PLX_NTX_OUR_BASE(sc) + 0xc6c;
433323453Smav	sc->spad_offp2 = sc->spad_off2 = PLX_PORT_BASE(sc->ntx * 8) + 0x20c;
434323032Smav	if (sc->b2b_mw >= 0) {
435323453Smav		/* In NTB-to-NTB mode each side has own scratchpads. */
436323032Smav		sc->spad_count1 = PLX_NUM_SPAD;
437323032Smav		bus_write_4(sc->conf_res, sc->spad_off2, 0x12345678);
438323032Smav		if (bus_read_4(sc->conf_res, sc->spad_off2) == 0x12345678)
439323032Smav			sc->spad_count2 = PLX_NUM_SPAD_PATT;
440323032Smav	} else {
441323453Smav		/* Otherwise we have share scratchpads with the peer. */
442323032Smav		if (sc->link) {
443323032Smav			sc->spad_off1 += PLX_NUM_SPAD / 2 * 4;
444323032Smav			sc->spad_off2 += PLX_NUM_SPAD_PATT / 2 * 4;
445323032Smav		} else {
446323032Smav			sc->spad_offp1 += PLX_NUM_SPAD / 2 * 4;
447323032Smav			sc->spad_offp2 += PLX_NUM_SPAD_PATT / 2 * 4;
448323032Smav		}
449323453Smav		sc->spad_count1 = PLX_NUM_SPAD / 2;
450323032Smav		bus_write_4(sc->conf_res, sc->spad_off2, 0x12345678);
451323032Smav		if (bus_read_4(sc->conf_res, sc->spad_off2) == 0x12345678)
452323032Smav			sc->spad_count2 = PLX_NUM_SPAD_PATT / 2;
453323032Smav	}
454323032Smav
455323032Smav	/* Apply static part of NTB configuration. */
456323032Smav	ntb_plx_init(dev);
457323032Smav
458323032Smav	/* Allocate and setup interrupts. */
459323032Smav	error = ntb_plx_setup_intr(dev);
460323032Smav	if (error)
461323032Smav		goto out;
462323032Smav
463323032Smav	/* Attach children to this controller */
464323032Smav	error = ntb_register_device(dev);
465323032Smav
466323032Smavout:
467323032Smav	if (error != 0)
468323032Smav		ntb_plx_detach(dev);
469323032Smav	return (error);
470323032Smav}
471323032Smav
472323032Smavstatic int
473323032Smavntb_plx_detach(device_t dev)
474323032Smav{
475323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
476323032Smav	struct ntb_plx_mw_info *mw;
477323032Smav	int i;
478323032Smav
479323032Smav	/* Detach & delete all children */
480323032Smav	ntb_unregister_device(dev);
481323032Smav
482323032Smav	/* Disable and free interrupts. */
483323032Smav	ntb_plx_teardown_intr(dev);
484323032Smav
485323032Smav	/* Free memory resources. */
486323032Smav	for (i = 0; i < sc->mw_count; i++) {
487323032Smav		mw = &sc->mw_info[i];
488323032Smav		bus_release_resource(dev, SYS_RES_MEMORY, mw->mw_rid,
489323032Smav		    mw->mw_res);
490323032Smav	}
491323032Smav	bus_release_resource(dev, SYS_RES_MEMORY, sc->conf_rid, sc->conf_res);
492323032Smav	return (0);
493323032Smav}
494323032Smav
495323032Smav
496323032Smavstatic bool
497323032Smavntb_plx_link_is_up(device_t dev, enum ntb_speed *speed, enum ntb_width *width)
498323032Smav{
499323032Smav	uint16_t link;
500323032Smav
501323032Smav	link = pcie_read_config(dev, PCIER_LINK_STA, 2);
502323032Smav	if (speed != NULL)
503323032Smav		*speed = (link & PCIEM_LINK_STA_SPEED);
504323032Smav	if (width != NULL)
505323032Smav		*width = (link & PCIEM_LINK_STA_WIDTH) >> 4;
506323032Smav	return ((link & PCIEM_LINK_STA_WIDTH) != 0);
507323032Smav}
508323032Smav
509323032Smavstatic int
510323032Smavntb_plx_link_enable(device_t dev, enum ntb_speed speed __unused,
511323032Smav    enum ntb_width width __unused)
512323032Smav{
513323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
514323032Smav	uint32_t reg, val;
515323032Smav
516323032Smav	/* The fact that we see the Link Interface means link is enabled. */
517323032Smav	if (sc->link) {
518323032Smav		ntb_link_event(dev);
519323032Smav		return (0);
520323032Smav	}
521323032Smav
522323453Smav	reg = PLX_PORT_CONTROL(sc);
523323032Smav	val = bus_read_4(sc->conf_res, reg);
524323032Smav	if ((val & (1 << (sc->port & 7))) == 0) {
525323032Smav		/* If already enabled, generate fake link event and exit. */
526323032Smav		ntb_link_event(dev);
527323032Smav		return (0);
528323032Smav	}
529323032Smav	val &= ~(1 << (sc->port & 7));
530323032Smav	bus_write_4(sc->conf_res, reg, val);
531323032Smav	return (0);
532323032Smav}
533323032Smav
534323032Smavstatic int
535323032Smavntb_plx_link_disable(device_t dev)
536323032Smav{
537323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
538323032Smav	uint32_t reg, val;
539323032Smav
540323032Smav	/* Link disable for Link Interface would be suicidal. */
541323032Smav	if (sc->link)
542323032Smav		return (0);
543323032Smav
544323453Smav	reg = PLX_PORT_CONTROL(sc);
545323032Smav	val = bus_read_4(sc->conf_res, reg);
546323032Smav	val |= (1 << (sc->port & 7));
547323032Smav	bus_write_4(sc->conf_res, reg, val);
548323032Smav	return (0);
549323032Smav}
550323032Smav
551323032Smavstatic bool
552323032Smavntb_plx_link_enabled(device_t dev)
553323032Smav{
554323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
555323032Smav	uint32_t reg, val;
556323032Smav
557323032Smav	/* The fact that we see the Link Interface means link is enabled. */
558323032Smav	if (sc->link)
559323032Smav		return (TRUE);
560323032Smav
561323453Smav	reg = PLX_PORT_CONTROL(sc);
562323032Smav	val = bus_read_4(sc->conf_res, reg);
563323032Smav	return ((val & (1 << (sc->port & 7))) == 0);
564323032Smav}
565323032Smav
566323032Smavstatic uint8_t
567323032Smavntb_plx_mw_count(device_t dev)
568323032Smav{
569323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
570355041Smav	uint8_t res;
571323032Smav
572355041Smav	res = sc->mw_count;
573355041Smav	res += (1 << sc->split) - 1;
574323032Smav	if (sc->b2b_mw >= 0 && sc->b2b_off == 0)
575355041Smav		res--; /* B2B consumed whole window. */
576355041Smav	return (res);
577323032Smav}
578323032Smav
579355041Smavstatic unsigned
580355041Smavntb_plx_user_mw_to_idx(struct ntb_plx_softc *sc, unsigned uidx, unsigned *sp)
581355041Smav{
582355041Smav	unsigned t;
583355041Smav
584355041Smav	t = 1 << sc->split;
585355041Smav	if (uidx < t) {
586355041Smav		*sp = uidx;
587355041Smav		return (0);
588355041Smav	}
589355041Smav	*sp = 0;
590355041Smav	return (uidx - (t - 1));
591355041Smav}
592355041Smav
593323032Smavstatic int
594323032Smavntb_plx_mw_get_range(device_t dev, unsigned mw_idx, vm_paddr_t *base,
595323032Smav    caddr_t *vbase, size_t *size, size_t *align, size_t *align_size,
596323032Smav    bus_addr_t *plimit)
597323032Smav{
598323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
599323032Smav	struct ntb_plx_mw_info *mw;
600355041Smav	size_t off, ss;
601355041Smav	unsigned sp, split;
602323032Smav
603355041Smav	mw_idx = ntb_plx_user_mw_to_idx(sc, mw_idx, &sp);
604323032Smav	if (mw_idx >= sc->mw_count)
605323032Smav		return (EINVAL);
606323032Smav	off = 0;
607323032Smav	if (mw_idx == sc->b2b_mw) {
608323032Smav		KASSERT(sc->b2b_off != 0,
609323032Smav		    ("user shouldn't get non-shared b2b mw"));
610323032Smav		off = sc->b2b_off;
611323032Smav	}
612323032Smav	mw = &sc->mw_info[mw_idx];
613355041Smav	split = (mw->mw_bar == 2) ? sc->split : 0;
614355041Smav	ss = (mw->mw_size - off) >> split;
615323032Smav
616323032Smav	/* Local to remote memory window parameters. */
617323032Smav	if (base != NULL)
618355041Smav		*base = mw->mw_pbase + off + ss * sp;
619323032Smav	if (vbase != NULL)
620355041Smav		*vbase = mw->mw_vbase + off + ss * sp;
621323032Smav	if (size != NULL)
622355041Smav		*size = ss;
623323032Smav
624323032Smav	/*
625323032Smav	 * Remote to local memory window translation address alignment.
626324407Smav	 * Translation address has to be aligned to the BAR size, but A-LUT
627324407Smav	 * entries re-map addresses can be aligned to 1/128 or 1/256 of it.
628324407Smav	 * XXX: In B2B mode we can change BAR size (and so alignmet) live,
629324407Smav	 * but there is no way to report it here, so report safe value.
630323032Smav	 */
631324407Smav	if (align != NULL) {
632324407Smav		if (sc->alut && mw->mw_bar == 2)
633324407Smav			*align = (mw->mw_size - off) / 128 / sc->alut;
634324407Smav		else
635324407Smav			*align = mw->mw_size - off;
636324407Smav	}
637323032Smav
638323032Smav	/*
639323032Smav	 * Remote to local memory window size alignment.
640324407Smav	 * The chip has no limit registers, but A-LUT, when available, allows
641324407Smav	 * access control with granularity of 1/128 or 1/256 of the BAR size.
642324407Smav	 * XXX: In B2B case we can change BAR size live, but there is no way
643324407Smav	 * to report it, so report half of the BAR size, that should be safe.
644324407Smav	 * In non-B2B case there is no control at all, so report the BAR size.
645323032Smav	 */
646323032Smav	if (align_size != NULL) {
647324407Smav		if (sc->alut && mw->mw_bar == 2)
648324407Smav			*align_size = (mw->mw_size - off) / 128 / sc->alut;
649324407Smav		else if (sc->b2b_mw >= 0)
650324407Smav			*align_size = (mw->mw_size - off) / 2;
651323032Smav		else
652323032Smav			*align_size = mw->mw_size - off;
653323032Smav	}
654323032Smav
655323032Smav	/* Remote to local memory window translation address upper limit. */
656323032Smav	if (plimit != NULL)
657323032Smav		*plimit = mw->mw_64bit ? BUS_SPACE_MAXADDR :
658323032Smav		    BUS_SPACE_MAXADDR_32BIT;
659323032Smav	return (0);
660323032Smav}
661323032Smav
662323032Smavstatic int
663323032Smavntb_plx_mw_set_trans_internal(device_t dev, unsigned mw_idx)
664323032Smav{
665323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
666323032Smav	struct ntb_plx_mw_info *mw;
667324407Smav	uint64_t addr, eaddr, off, size, bsize, esize, val64;
668323032Smav	uint32_t val;
669355041Smav	unsigned i, sp, split;
670323032Smav
671323032Smav	mw = &sc->mw_info[mw_idx];
672355041Smav	off = (mw_idx == sc->b2b_mw) ? sc->b2b_off : 0;
673355041Smav	split = (mw->mw_bar == 2) ? sc->split : 0;
674323032Smav
675355041Smav	/* Get BAR size.  In case of split or B2RP we can't change it. */
676355041Smav	if (split || sc->b2b_mw < 0) {
677355041Smav		bsize = mw->mw_size - off;
678355041Smav	} else {
679355041Smav		bsize = mw->splits[0].mw_xlat_size;
680324407Smav		if (!powerof2(bsize))
681324407Smav			bsize = 1LL << flsll(bsize);
682355041Smav		if (bsize > 0 && bsize < 1024 * 1024)
683324407Smav			bsize = 1024 * 1024;
684355041Smav	}
685323032Smav
686355041Smav	/*
687355041Smav	 * While for B2B we can set any BAR size on a link side, for shared
688355041Smav	 * window we can't go above preconfigured size due to BAR address
689355041Smav	 * alignment requirements.
690355041Smav	 */
691355041Smav	if ((off & (bsize - 1)) != 0)
692355041Smav		return (EINVAL);
693324407Smav
694355041Smav	/* In B2B mode set Link Interface BAR size/address. */
695355041Smav	if (sc->b2b_mw >= 0 && mw->mw_64bit) {
696355041Smav		val64 = 0;
697355041Smav		if (bsize > 0)
698355041Smav			val64 = (~(bsize - 1) & ~0xfffff);
699355041Smav		val64 |= 0xc;
700355041Smav		PNTX_WRITE(sc, 0xe8 + (mw->mw_bar - 2) * 4, val64);
701355041Smav		PNTX_WRITE(sc, 0xe8 + (mw->mw_bar - 2) * 4 + 4, val64 >> 32);
702323032Smav
703355041Smav		val64 = 0x2000000000000000 * mw->mw_bar + off;
704355041Smav		PNTX_WRITE(sc, PCIR_BAR(mw->mw_bar), val64);
705355041Smav		PNTX_WRITE(sc, PCIR_BAR(mw->mw_bar) + 4, val64 >> 32);
706355041Smav	} else if (sc->b2b_mw >= 0) {
707355041Smav		val = 0;
708355041Smav		if (bsize > 0)
709355041Smav			val = (~(bsize - 1) & ~0xfffff);
710355041Smav		PNTX_WRITE(sc, 0xe8 + (mw->mw_bar - 2) * 4, val);
711323032Smav
712355041Smav		val64 = 0x20000000 * mw->mw_bar + off;
713355041Smav		PNTX_WRITE(sc, PCIR_BAR(mw->mw_bar), val64);
714355041Smav	}
715323032Smav
716355041Smav	/* Set BARs address translation */
717355041Smav	addr = split ? UINT64_MAX : mw->splits[0].mw_xlat_addr;
718355041Smav	if (mw->mw_64bit) {
719323032Smav		PNTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4, addr);
720323032Smav		PNTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4 + 4, addr >> 32);
721323032Smav	} else {
722323032Smav		PNTX_WRITE(sc, 0xc3c + (mw->mw_bar - 2) * 4, addr);
723323032Smav	}
724324407Smav
725355041Smav	/* Configure and enable A-LUT if we need it. */
726355041Smav	size = split ? 0 : mw->splits[0].mw_xlat_size;
727355041Smav	if (sc->alut && mw->mw_bar == 2 && (sc->split > 0 ||
728355041Smav	    ((addr & (bsize - 1)) != 0 || size != bsize))) {
729355041Smav		esize = bsize / (128 * sc->alut);
730355041Smav		for (i = sp = 0; i < 128 * sc->alut; i++) {
731355041Smav			if (i % (128 * sc->alut >> sc->split) == 0) {
732355041Smav				eaddr = addr = mw->splits[sp].mw_xlat_addr;
733355041Smav				size = mw->splits[sp++].mw_xlat_size;
734355041Smav			}
735324407Smav			val = sc->link ? 0 : 1;
736324407Smav			if (sc->alut == 1)
737324407Smav				val += 2 * sc->ntx;
738324407Smav			val *= 0x1000 * sc->alut;
739324407Smav			val += 0x38000 + i * 4 + (i >= 128 ? 0x0e00 : 0);
740324407Smav			bus_write_4(sc->conf_res, val, eaddr);
741324407Smav			bus_write_4(sc->conf_res, val + 0x400, eaddr >> 32);
742324407Smav			bus_write_4(sc->conf_res, val + 0x800,
743324407Smav			    (eaddr < addr + size) ? 0x3 : 0);
744324407Smav			eaddr += esize;
745324407Smav		}
746324407Smav		NTX_WRITE(sc, 0xc94, 0x10000000);
747324407Smav	} else if (sc->alut && mw->mw_bar == 2)
748324407Smav		NTX_WRITE(sc, 0xc94, 0);
749324407Smav
750323032Smav	return (0);
751323032Smav}
752323032Smav
753323032Smavstatic int
754323032Smavntb_plx_mw_set_trans(device_t dev, unsigned mw_idx, bus_addr_t addr, size_t size)
755323032Smav{
756323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
757323032Smav	struct ntb_plx_mw_info *mw;
758355041Smav	unsigned sp;
759323032Smav
760355041Smav	mw_idx = ntb_plx_user_mw_to_idx(sc, mw_idx, &sp);
761323032Smav	if (mw_idx >= sc->mw_count)
762323032Smav		return (EINVAL);
763323032Smav	mw = &sc->mw_info[mw_idx];
764355041Smav	if (!mw->mw_64bit &&
765355041Smav	    ((addr & UINT32_MAX) != addr ||
766355041Smav	     ((addr + size) & UINT32_MAX) != (addr + size)))
767355041Smav		return (ERANGE);
768355041Smav	mw->splits[sp].mw_xlat_addr = addr;
769355041Smav	mw->splits[sp].mw_xlat_size = size;
770323032Smav	return (ntb_plx_mw_set_trans_internal(dev, mw_idx));
771323032Smav}
772323032Smav
773323032Smavstatic int
774323032Smavntb_plx_mw_clear_trans(device_t dev, unsigned mw_idx)
775323032Smav{
776323032Smav
777323032Smav	return (ntb_plx_mw_set_trans(dev, mw_idx, 0, 0));
778323032Smav}
779323032Smav
780323032Smavstatic int
781355041Smavntb_plx_mw_get_wc(device_t dev, unsigned mw_idx, vm_memattr_t *mode)
782323032Smav{
783323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
784323032Smav	struct ntb_plx_mw_info *mw;
785355041Smav	unsigned sp;
786323032Smav
787355041Smav	mw_idx = ntb_plx_user_mw_to_idx(sc, mw_idx, &sp);
788355041Smav	if (mw_idx >= sc->mw_count)
789323032Smav		return (EINVAL);
790355041Smav	mw = &sc->mw_info[mw_idx];
791355041Smav	*mode = mw->splits[sp].mw_map_mode;
792323032Smav	return (0);
793323032Smav}
794323032Smav
795323032Smavstatic int
796355041Smavntb_plx_mw_set_wc(device_t dev, unsigned mw_idx, vm_memattr_t mode)
797323032Smav{
798323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
799323032Smav	struct ntb_plx_mw_info *mw;
800355041Smav	uint64_t off, ss;
801323032Smav	int rc;
802355041Smav	unsigned sp, split;
803323032Smav
804355041Smav	mw_idx = ntb_plx_user_mw_to_idx(sc, mw_idx, &sp);
805355041Smav	if (mw_idx >= sc->mw_count)
806323032Smav		return (EINVAL);
807355041Smav	mw = &sc->mw_info[mw_idx];
808355041Smav	if (mw->splits[sp].mw_map_mode == mode)
809323032Smav		return (0);
810323032Smav
811323032Smav	off = 0;
812355041Smav	if (mw_idx == sc->b2b_mw) {
813323032Smav		KASSERT(sc->b2b_off != 0,
814323032Smav		    ("user shouldn't get non-shared b2b mw"));
815323032Smav		off = sc->b2b_off;
816323032Smav	}
817323032Smav
818355041Smav	split = (mw->mw_bar == 2) ? sc->split : 0;
819355041Smav	ss = (mw->mw_size - off) >> split;
820355041Smav	rc = pmap_change_attr((vm_offset_t)mw->mw_vbase + off + ss * sp,
821355041Smav	    ss, mode);
822323032Smav	if (rc == 0)
823355041Smav		mw->splits[sp].mw_map_mode = mode;
824323032Smav	return (rc);
825323032Smav}
826323032Smav
827323032Smavstatic uint8_t
828323032Smavntb_plx_spad_count(device_t dev)
829323032Smav{
830323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
831323032Smav
832323032Smav	return (sc->spad_count1 + sc->spad_count2);
833323032Smav}
834323032Smav
835323032Smavstatic int
836323032Smavntb_plx_spad_write(device_t dev, unsigned int idx, uint32_t val)
837323032Smav{
838323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
839323032Smav	u_int off;
840323032Smav
841323032Smav	if (idx >= sc->spad_count1 + sc->spad_count2)
842323032Smav		return (EINVAL);
843323032Smav
844323032Smav	if (idx < sc->spad_count1)
845323032Smav		off = sc->spad_off1 + idx * 4;
846323032Smav	else
847323032Smav		off = sc->spad_off2 + (idx - sc->spad_count1) * 4;
848323032Smav	bus_write_4(sc->conf_res, off, val);
849323032Smav	return (0);
850323032Smav}
851323032Smav
852323032Smavstatic void
853323032Smavntb_plx_spad_clear(device_t dev)
854323032Smav{
855323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
856323032Smav	int i;
857323032Smav
858323032Smav	for (i = 0; i < sc->spad_count1 + sc->spad_count2; i++)
859323032Smav		ntb_plx_spad_write(dev, i, 0);
860323032Smav}
861323032Smav
862323032Smavstatic int
863323032Smavntb_plx_spad_read(device_t dev, unsigned int idx, uint32_t *val)
864323032Smav{
865323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
866323032Smav	u_int off;
867323032Smav
868323032Smav	if (idx >= sc->spad_count1 + sc->spad_count2)
869323032Smav		return (EINVAL);
870323032Smav
871323032Smav	if (idx < sc->spad_count1)
872323032Smav		off = sc->spad_off1 + idx * 4;
873323032Smav	else
874323032Smav		off = sc->spad_off2 + (idx - sc->spad_count1) * 4;
875323032Smav	*val = bus_read_4(sc->conf_res, off);
876323032Smav	return (0);
877323032Smav}
878323032Smav
879323032Smavstatic int
880323032Smavntb_plx_peer_spad_write(device_t dev, unsigned int idx, uint32_t val)
881323032Smav{
882323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
883323032Smav	u_int off;
884323032Smav
885323032Smav	if (idx >= sc->spad_count1 + sc->spad_count2)
886323032Smav		return (EINVAL);
887323032Smav
888323032Smav	if (idx < sc->spad_count1)
889323032Smav		off = sc->spad_offp1 + idx * 4;
890323032Smav	else
891323032Smav		off = sc->spad_offp2 + (idx - sc->spad_count1) * 4;
892323032Smav	if (sc->b2b_mw >= 0)
893323032Smav		bus_write_4(sc->mw_info[sc->b2b_mw].mw_res, off, val);
894323032Smav	else
895323032Smav		bus_write_4(sc->conf_res, off, val);
896323032Smav	return (0);
897323032Smav}
898323032Smav
899323032Smavstatic int
900323032Smavntb_plx_peer_spad_read(device_t dev, unsigned int idx, uint32_t *val)
901323032Smav{
902323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
903323032Smav	u_int off;
904323032Smav
905323032Smav	if (idx >= sc->spad_count1 + sc->spad_count2)
906323032Smav		return (EINVAL);
907323032Smav
908323032Smav	if (idx < sc->spad_count1)
909323032Smav		off = sc->spad_offp1 + idx * 4;
910323032Smav	else
911323032Smav		off = sc->spad_offp2 + (idx - sc->spad_count1) * 4;
912323032Smav	if (sc->b2b_mw >= 0)
913323032Smav		*val = bus_read_4(sc->mw_info[sc->b2b_mw].mw_res, off);
914323032Smav	else
915323032Smav		*val = bus_read_4(sc->conf_res, off);
916323032Smav	return (0);
917323032Smav}
918323032Smav
919323032Smavstatic uint64_t
920323032Smavntb_plx_db_valid_mask(device_t dev)
921323032Smav{
922323032Smav
923323032Smav	return ((1LL << PLX_NUM_DB) - 1);
924323032Smav}
925323032Smav
926323032Smavstatic int
927323032Smavntb_plx_db_vector_count(device_t dev)
928323032Smav{
929323032Smav
930323032Smav	return (1);
931323032Smav}
932323032Smav
933323032Smavstatic uint64_t
934323032Smavntb_plx_db_vector_mask(device_t dev, uint32_t vector)
935323032Smav{
936323032Smav
937323032Smav	if (vector > 0)
938323032Smav		return (0);
939323032Smav	return ((1LL << PLX_NUM_DB) - 1);
940323032Smav}
941323032Smav
942323032Smavstatic void
943323032Smavntb_plx_db_clear(device_t dev, uint64_t bits)
944323032Smav{
945323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
946323032Smav
947323032Smav	NTX_WRITE(sc, sc->link ? 0xc60 : 0xc50, bits);
948323032Smav}
949323032Smav
950323032Smavstatic void
951323032Smavntb_plx_db_clear_mask(device_t dev, uint64_t bits)
952323032Smav{
953323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
954323032Smav
955323032Smav	NTX_WRITE(sc, sc->link ? 0xc68 : 0xc58, bits);
956323032Smav}
957323032Smav
958323032Smavstatic uint64_t
959323032Smavntb_plx_db_read(device_t dev)
960323032Smav{
961323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
962323032Smav
963323032Smav	return (NTX_READ(sc, sc->link ? 0xc5c : 0xc4c));
964323032Smav}
965323032Smav
966323032Smavstatic void
967323032Smavntb_plx_db_set_mask(device_t dev, uint64_t bits)
968323032Smav{
969323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
970323032Smav
971323032Smav	NTX_WRITE(sc, sc->link ? 0xc64 : 0xc54, bits);
972323032Smav}
973323032Smav
974323032Smavstatic int
975323032Smavntb_plx_peer_db_addr(device_t dev, bus_addr_t *db_addr, vm_size_t *db_size)
976323032Smav{
977323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
978323032Smav	struct ntb_plx_mw_info *mw;
979323032Smav
980323032Smav	KASSERT((db_addr != NULL && db_size != NULL), ("must be non-NULL"));
981323032Smav
982323032Smav	if (sc->b2b_mw >= 0) {
983323032Smav		mw = &sc->mw_info[sc->b2b_mw];
984323032Smav		*db_addr = (uint64_t)mw->mw_pbase + PLX_NTX_BASE(sc) + 0xc4c;
985323032Smav	} else {
986323032Smav		*db_addr = rman_get_start(sc->conf_res) + PLX_NTX_BASE(sc);
987323032Smav		*db_addr += sc->link ? 0xc4c : 0xc5c;
988323032Smav	}
989323032Smav	*db_size = 4;
990323032Smav	return (0);
991323032Smav}
992323032Smav
993323032Smavstatic void
994323032Smavntb_plx_peer_db_set(device_t dev, uint64_t bit)
995323032Smav{
996323032Smav	struct ntb_plx_softc *sc = device_get_softc(dev);
997323032Smav
998323032Smav	if (sc->b2b_mw >= 0)
999323032Smav		BNTX_WRITE(sc, 0xc4c, bit);
1000323032Smav	else
1001323032Smav		NTX_WRITE(sc, sc->link ? 0xc4c : 0xc5c, bit);
1002323032Smav}
1003323032Smav
1004323032Smavstatic device_method_t ntb_plx_methods[] = {
1005323032Smav	/* Device interface */
1006323032Smav	DEVMETHOD(device_probe,		ntb_plx_probe),
1007323032Smav	DEVMETHOD(device_attach,	ntb_plx_attach),
1008323032Smav	DEVMETHOD(device_detach,	ntb_plx_detach),
1009323455Smav	/* Bus interface */
1010323455Smav	DEVMETHOD(bus_child_location_str, ntb_child_location_str),
1011323455Smav	DEVMETHOD(bus_print_child,	ntb_print_child),
1012355152Smav	DEVMETHOD(bus_get_dma_tag,	ntb_get_dma_tag),
1013323032Smav	/* NTB interface */
1014323032Smav	DEVMETHOD(ntb_link_is_up,	ntb_plx_link_is_up),
1015323032Smav	DEVMETHOD(ntb_link_enable,	ntb_plx_link_enable),
1016323032Smav	DEVMETHOD(ntb_link_disable,	ntb_plx_link_disable),
1017323032Smav	DEVMETHOD(ntb_link_enabled,	ntb_plx_link_enabled),
1018323032Smav	DEVMETHOD(ntb_mw_count,		ntb_plx_mw_count),
1019323032Smav	DEVMETHOD(ntb_mw_get_range,	ntb_plx_mw_get_range),
1020323032Smav	DEVMETHOD(ntb_mw_set_trans,	ntb_plx_mw_set_trans),
1021323032Smav	DEVMETHOD(ntb_mw_clear_trans,	ntb_plx_mw_clear_trans),
1022323032Smav	DEVMETHOD(ntb_mw_get_wc,	ntb_plx_mw_get_wc),
1023323032Smav	DEVMETHOD(ntb_mw_set_wc,	ntb_plx_mw_set_wc),
1024323032Smav	DEVMETHOD(ntb_spad_count,	ntb_plx_spad_count),
1025323032Smav	DEVMETHOD(ntb_spad_clear,	ntb_plx_spad_clear),
1026323032Smav	DEVMETHOD(ntb_spad_write,	ntb_plx_spad_write),
1027323032Smav	DEVMETHOD(ntb_spad_read,	ntb_plx_spad_read),
1028323032Smav	DEVMETHOD(ntb_peer_spad_write,	ntb_plx_peer_spad_write),
1029323032Smav	DEVMETHOD(ntb_peer_spad_read,	ntb_plx_peer_spad_read),
1030323032Smav	DEVMETHOD(ntb_db_valid_mask,	ntb_plx_db_valid_mask),
1031323032Smav	DEVMETHOD(ntb_db_vector_count,	ntb_plx_db_vector_count),
1032323032Smav	DEVMETHOD(ntb_db_vector_mask,	ntb_plx_db_vector_mask),
1033323032Smav	DEVMETHOD(ntb_db_clear,		ntb_plx_db_clear),
1034323032Smav	DEVMETHOD(ntb_db_clear_mask,	ntb_plx_db_clear_mask),
1035323032Smav	DEVMETHOD(ntb_db_read,		ntb_plx_db_read),
1036323032Smav	DEVMETHOD(ntb_db_set_mask,	ntb_plx_db_set_mask),
1037323032Smav	DEVMETHOD(ntb_peer_db_addr,	ntb_plx_peer_db_addr),
1038323032Smav	DEVMETHOD(ntb_peer_db_set,	ntb_plx_peer_db_set),
1039323032Smav	DEVMETHOD_END
1040323032Smav};
1041323032Smav
1042323032Smavstatic DEFINE_CLASS_0(ntb_hw, ntb_plx_driver, ntb_plx_methods,
1043323032Smav    sizeof(struct ntb_plx_softc));
1044323032SmavDRIVER_MODULE(ntb_hw_plx, pci, ntb_plx_driver, ntb_hw_devclass, NULL, NULL);
1045323032SmavMODULE_DEPEND(ntb_hw_plx, ntb, 1, 1, 1);
1046323032SmavMODULE_VERSION(ntb_hw_plx, 1);
1047